summaryrefslogtreecommitdiff
path: root/chromium/components
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-18 14:34:04 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-10-04 11:15:27 +0000
commite6430e577f105ad8813c92e75c54660c4985026e (patch)
tree88115e5d1fb471fea807111924dcccbeadbf9e4f /chromium/components
parent53d399fe6415a96ea6986ec0d402a9c07da72453 (diff)
downloadqtwebengine-chromium-e6430e577f105ad8813c92e75c54660c4985026e.tar.gz
BASELINE: Update Chromium to 61.0.3163.99
Change-Id: I8452f34574d88ca2b27af9bd56fc9ff3f16b1367 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components')
-rw-r--r--chromium/components/BUILD.gn40
-rw-r--r--chromium/components/about_ui/BUILD.gn21
-rw-r--r--chromium/components/about_ui/DEPS6
-rw-r--r--chromium/components/about_ui/android/BUILD.gn18
-rw-r--r--chromium/components/about_ui/android/OWNERS2
-rw-r--r--chromium/components/about_ui/credit_utils.cc71
-rw-r--r--chromium/components/about_ui/credit_utils.h17
-rw-r--r--chromium/components/about_ui/resources/about_credits.js50
-rw-r--r--chromium/components/about_ui/resources/about_credits.tmpl28
-rw-r--r--chromium/components/about_ui/resources/about_credits_entry.tmpl4
-rw-r--r--chromium/components/app_modal/BUILD.gn7
-rw-r--r--chromium/components/app_modal/app_modal_dialog.cc80
-rw-r--r--chromium/components/app_modal/app_modal_dialog.h102
-rw-r--r--chromium/components/app_modal/app_modal_dialog_queue.cc20
-rw-r--r--chromium/components/app_modal/app_modal_dialog_queue.h28
-rw-r--r--chromium/components/app_modal/javascript_app_modal_dialog.cc69
-rw-r--r--chromium/components/app_modal/javascript_app_modal_dialog.h67
-rw-r--r--chromium/components/app_modal/javascript_dialog_manager.cc13
-rw-r--r--chromium/components/app_modal/views/DEPS1
-rw-r--r--chromium/components/app_modal/views/javascript_app_modal_dialog_views.cc5
-rw-r--r--chromium/components/app_modal/views/javascript_app_modal_dialog_views.h1
-rw-r--r--chromium/components/arc/BUILD.gn32
-rw-r--r--chromium/components/arc/DEPS7
-rw-r--r--chromium/components/arc/arc_bridge_host_impl.cc22
-rw-r--r--chromium/components/arc/arc_bridge_host_impl.h6
-rw-r--r--chromium/components/arc/arc_bridge_service.h10
-rw-r--r--chromium/components/arc/arc_browser_context_keyed_service_factory_base.h122
-rw-r--r--chromium/components/arc/arc_features.cc10
-rw-r--r--chromium/components/arc/arc_features.h2
-rw-r--r--chromium/components/arc/arc_service_manager.cc22
-rw-r--r--chromium/components/arc/arc_service_manager.h63
-rw-r--r--chromium/components/arc/arc_service_manager_unittest.cc10
-rw-r--r--chromium/components/arc/arc_session.cc267
-rw-r--r--chromium/components/arc/arc_session.h33
-rw-r--r--chromium/components/arc/arc_session_runner.cc96
-rw-r--r--chromium/components/arc/arc_session_runner.h16
-rw-r--r--chromium/components/arc/arc_session_runner_unittest.cc68
-rw-r--r--chromium/components/arc/arc_util.cc49
-rw-r--r--chromium/components/arc/arc_util.h11
-rw-r--r--chromium/components/arc/arc_util_unittest.cc20
-rw-r--r--chromium/components/arc/audio/arc_audio_bridge.cc79
-rw-r--r--chromium/components/arc/audio/arc_audio_bridge.h36
-rw-r--r--chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc2173
-rw-r--r--chromium/components/arc/bluetooth/arc_bluetooth_bridge.h491
-rw-r--r--chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc331
-rw-r--r--chromium/components/arc/clipboard/arc_clipboard_bridge.cc66
-rw-r--r--chromium/components/arc/clipboard/arc_clipboard_bridge.h23
-rw-r--r--chromium/components/arc/common/accessibility_helper.mojom42
-rw-r--r--chromium/components/arc/common/app.mojom37
-rw-r--r--chromium/components/arc/common/arc_bridge.mojom12
-rw-r--r--chromium/components/arc/common/audio.mojom8
-rw-r--r--chromium/components/arc/common/file_system.mojom25
-rw-r--r--chromium/components/arc/common/intent_helper.mojom7
-rw-r--r--chromium/components/arc/common/lock_screen.mojom13
-rw-r--r--chromium/components/arc/common/net.mojom19
-rw-r--r--chromium/components/arc/common/typemaps.gni5
-rw-r--r--chromium/components/arc/common/video.mojom12
-rw-r--r--chromium/components/arc/common/video_accelerator.mojom102
-rw-r--r--chromium/components/arc/common/video_accelerator.typemap12
-rw-r--r--chromium/components/arc/common/video_common.mojom21
-rw-r--r--chromium/components/arc/common/video_common.typemap18
-rw-r--r--chromium/components/arc/common/video_decode_accelerator.mojom101
-rw-r--r--chromium/components/arc/common/video_encode_accelerator.mojom196
-rw-r--r--chromium/components/arc/common/video_encode_accelerator.typemap19
-rw-r--r--chromium/components/arc/common/voice_interaction_arc_home.mojom6
-rw-r--r--chromium/components/arc/common/voice_interaction_framework.mojom52
-rw-r--r--chromium/components/arc/common/voice_interaction_framework.typemap13
-rw-r--r--chromium/components/arc/common/volume_mounter.mojom45
-rw-r--r--chromium/components/arc/common/volume_mounter.typemap17
-rw-r--r--chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc60
-rw-r--r--chromium/components/arc/crash_collector/arc_crash_collector_bridge.h23
-rw-r--r--chromium/components/arc/ime/arc_ime_bridge_impl.cc13
-rw-r--r--chromium/components/arc/ime/arc_ime_service.cc38
-rw-r--r--chromium/components/arc/ime/arc_ime_service.h19
-rw-r--r--chromium/components/arc/ime/arc_ime_service_unittest.cc3
-rw-r--r--chromium/components/arc/instance_holder.h11
-rw-r--r--chromium/components/arc/intent_helper/activity_icon_loader.cc14
-rw-r--r--chromium/components/arc/intent_helper/activity_icon_loader.h2
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc121
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge.h50
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc101
-rw-r--r--chromium/components/arc/intent_helper/link_handler_model_impl.cc5
-rw-r--r--chromium/components/arc/intent_helper/link_handler_model_impl.h8
-rw-r--r--chromium/components/arc/intent_helper/local_activity_resolver.cc37
-rw-r--r--chromium/components/arc/intent_helper/local_activity_resolver.h47
-rw-r--r--chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc120
-rw-r--r--chromium/components/arc/kiosk/arc_kiosk_bridge.cc44
-rw-r--r--chromium/components/arc/kiosk/arc_kiosk_bridge.h55
-rw-r--r--chromium/components/arc/kiosk/arc_kiosk_bridge_unittest.cc59
-rw-r--r--chromium/components/arc/lock_screen/arc_lock_screen_bridge.cc77
-rw-r--r--chromium/components/arc/lock_screen/arc_lock_screen_bridge.h57
-rw-r--r--chromium/components/arc/metrics/arc_metrics_service.cc80
-rw-r--r--chromium/components/arc/metrics/arc_metrics_service.h35
-rw-r--r--chromium/components/arc/net/arc_net_host_impl.cc116
-rw-r--r--chromium/components/arc/net/arc_net_host_impl.h24
-rw-r--r--chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc49
-rw-r--r--chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.h17
-rw-r--r--chromium/components/arc/power/arc_power_bridge.cc67
-rw-r--r--chromium/components/arc/power/arc_power_bridge.h16
-rw-r--r--chromium/components/arc/storage_manager/arc_storage_manager.cc51
-rw-r--r--chromium/components/arc/storage_manager/arc_storage_manager.h23
-rw-r--r--chromium/components/arc/video_accelerator/DEPS3
-rw-r--r--chromium/components/arc/video_accelerator/video_accelerator.h2
-rw-r--r--chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc19
-rw-r--r--chromium/components/arc/video_accelerator/video_accelerator_struct_traits.h28
-rw-r--r--chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.cc163
-rw-r--r--chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.h68
-rw-r--r--chromium/components/arc/voice_interaction/OWNERS2
-rw-r--r--chromium/components/arc/voice_interaction/voice_interaction_struct_traits.h53
-rw-r--r--chromium/components/arc/volume_mounter/DEPS3
-rw-r--r--chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.cc141
-rw-r--r--chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.h66
-rw-r--r--chromium/components/arc/volume_mounter/volume_mounter_traits.cc77
-rw-r--r--chromium/components/arc/volume_mounter/volume_mounter_traits.h31
-rw-r--r--chromium/components/autofill/android/BUILD.gn39
-rw-r--r--chromium/components/autofill/android/DEPS4
-rw-r--r--chromium/components/autofill/android/autofill_provider_android.cc240
-rw-r--r--chromium/components/autofill/android/autofill_provider_android.h81
-rw-r--r--chromium/components/autofill/android/form_data_android.cc102
-rw-r--r--chromium/components/autofill/android/form_data_android.h63
-rw-r--r--chromium/components/autofill/android/form_field_data_android.cc91
-rw-r--r--chromium/components/autofill/android/form_field_data_android.h35
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.cc64
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.h23
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory.cc30
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory.h19
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc10
-rw-r--r--chromium/components/autofill/content/browser/payments/OWNERS2
-rw-r--r--chromium/components/autofill/content/common/autofill_agent.mojom15
-rw-r--r--chromium/components/autofill/content/common/autofill_driver.mojom12
-rw-r--r--chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc4
-rw-r--r--chromium/components/autofill/content/renderer/OWNERS2
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.cc51
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.h14
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.cc14
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.h6
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.cc9
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.h6
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.cc132
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.h4
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.cc88
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.h18
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc35
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.cc102
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.h4
-rw-r--r--chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc6
-rw-r--r--chromium/components/autofill/content/renderer/test_password_generation_agent.h1
-rw-r--r--chromium/components/autofill/core/browser/BUILD.gn6
-rw-r--r--chromium/components/autofill/core/browser/DEPS14
-rw-r--r--chromium/components/autofill/core/browser/address.cc4
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_util.cc4
-rw-r--r--chromium/components/autofill/core/browser/autofill_client.h4
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_util.cc6
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments.cc19
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments.h15
-rw-r--r--chromium/components/autofill/core/browser/autofill_field.h10
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler.cc71
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler.h137
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler_proxy.cc79
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler_proxy.h71
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.cc106
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.h79
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_unittest.cc21
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.cc220
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.h139
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics_unittest.cc410
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.cc16
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.h4
-rw-r--r--chromium/components/autofill/core/browser/autofill_provider.cc22
-rw-r--r--chromium/components/autofill/core/browser/autofill_provider.h62
-rw-r--r--chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc18
-rw-r--r--chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.h4
-rw-r--r--chromium/components/autofill/core/browser/autofill_test_utils.cc24
-rw-r--r--chromium/components/autofill/core/browser/autofill_test_utils.h22
-rw-r--r--chromium/components/autofill/core/browser/credit_card.cc16
-rw-r--r--chromium/components/autofill/core/browser/credit_card.h25
-rw-r--r--chromium/components/autofill/core/browser/credit_card_unittest.cc32
-rw-r--r--chromium/components/autofill/core/browser/form_structure.cc58
-rw-r--r--chromium/components/autofill/core/browser/form_structure.h4
-rw-r--r--chromium/components/autofill/core/browser/form_structure_unittest.cc2
-rw-r--r--chromium/components/autofill/core/browser/legal_message_line.cc19
-rw-r--r--chromium/components/autofill/core/browser/legal_message_line.h9
-rw-r--r--chromium/components/autofill/core/browser/legal_message_line_unittest.cc93
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.cc93
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.h22
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_unittest.cc485
-rw-r--r--chromium/components/autofill/core/browser/proto/server.proto6
-rw-r--r--chromium/components/autofill/core/browser/region_combobox_model.cc2
-rw-r--r--chromium/components/autofill/core/browser/region_combobox_model_unittest.cc14
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.cc4
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.h1
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc21
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h3
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc18
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc12
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.h7
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc12
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h7
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.cc93
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.h17
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc23
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc1
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc35
-rw-r--r--chromium/components/autofill/core/common/OWNERS2
-rw-r--r--chromium/components/autofill/core/common/autofill_util.cc6
-rw-r--r--chromium/components/autofill/core/common/autofill_util.h6
-rw-r--r--chromium/components/autofill/core/common/autofill_util_unittest.cc42
-rw-r--r--chromium/components/autofill/core/common/form_data.cc13
-rw-r--r--chromium/components/autofill/core/common/form_data.h4
-rw-r--r--chromium/components/autofill/core/common/form_field_data.cc6
-rw-r--r--chromium/components/autofill/core/common/form_field_data.h7
-rw-r--r--chromium/components/autofill/core/common/password_form.cc4
-rw-r--r--chromium/components/autofill/core/common/password_form.h22
-rw-r--r--chromium/components/autofill/ios/OWNERS2
-rw-r--r--chromium/components/autofill/ios/browser/resources/autofill_controller.js16
-rw-r--r--chromium/components/autofill_strings.grdp8
-rw-r--r--chromium/components/background_task_scheduler/BUILD.gn2
-rw-r--r--chromium/components/background_task_scheduler/OWNERS1
-rw-r--r--chromium/components/bookmarks/browser/bookmark_codec.cc29
-rw-r--r--chromium/components/bookmarks/browser/bookmark_codec.h22
-rw-r--r--chromium/components/bookmarks/browser/bookmark_model.cc2
-rw-r--r--chromium/components/bookmarks/browser/bookmark_node.cc2
-rw-r--r--chromium/components/bookmarks/browser/bookmark_node.h7
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage.cc6
-rw-r--r--chromium/components/bookmarks/browser/startup_task_runner_service.cc3
-rw-r--r--chromium/components/bookmarks/browser/startup_task_runner_service.h7
-rw-r--r--chromium/components/bookmarks/browser/titled_url_index.cc10
-rw-r--r--chromium/components/bookmarks/browser/titled_url_index.h4
-rw-r--r--chromium/components/bookmarks/browser/titled_url_node_sorter.h5
-rw-r--r--chromium/components/browser_sync/BUILD.gn2
-rw-r--r--chromium/components/browser_sync/DEPS2
-rw-r--r--chromium/components/browser_sync/profile_sync_components_factory_impl.cc168
-rw-r--r--chromium/components/browser_sync/profile_sync_service.cc73
-rw-r--r--chromium/components/browser_sync/profile_sync_service.h18
-rw-r--r--chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc12
-rw-r--r--chromium/components/browser_sync/profile_sync_service_mock.h1
-rw-r--r--chromium/components/browser_sync/profile_sync_service_startup_unittest.cc7
-rw-r--r--chromium/components/browser_sync/profile_sync_service_unittest.cc8
-rw-r--r--chromium/components/browser_sync/profile_sync_test_util.cc7
-rw-r--r--chromium/components/browser_watcher/BUILD.gn4
-rw-r--r--chromium/components/browser_watcher/features.cc2
-rw-r--r--chromium/components/browser_watcher/features.h4
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector.cc148
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector.h43
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector_unittest.cc583
-rw-r--r--chromium/components/browser_watcher/stability_debugging.cc50
-rw-r--r--chromium/components/browser_watcher/stability_debugging.h4
-rw-r--r--chromium/components/browser_watcher/stability_debugging_win_unittest.cc126
-rw-r--r--chromium/components/browser_watcher/stability_metrics.cc21
-rw-r--r--chromium/components/browser_watcher/stability_metrics.h41
-rw-r--r--chromium/components/browser_watcher/stability_paths.cc101
-rw-r--r--chromium/components/browser_watcher/stability_paths.h21
-rw-r--r--chromium/components/browser_watcher/stability_paths_unittest.cc125
-rw-r--r--chromium/components/browser_watcher/stability_report.proto15
-rw-r--r--chromium/components/browser_watcher/stability_report_extractor.cc15
-rw-r--r--chromium/components/browser_watcher/stability_report_extractor.h8
-rw-r--r--chromium/components/browser_watcher/stability_report_extractor_unittest.cc457
-rw-r--r--chromium/components/browser_watcher/watcher_metrics_provider_win.cc75
-rw-r--r--chromium/components/browser_watcher/watcher_metrics_provider_win.h16
-rw-r--r--chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc38
-rw-r--r--chromium/components/browsing_data/core/BUILD.gn13
-rw-r--r--chromium/components/browsing_data/core/DEPS1
-rw-r--r--chromium/components/browsing_data/core/browsing_data_utils.cc26
-rw-r--r--chromium/components/browsing_data/core/browsing_data_utils.h3
-rw-r--r--chromium/components/browsing_data/core/clear_browsing_data_tab.h4
-rw-r--r--chromium/components/browsing_data/core/counters/autofill_counter.cc21
-rw-r--r--chromium/components/browsing_data/core/counters/autofill_counter.h11
-rw-r--r--chromium/components/browsing_data/core/counters/bookmark_counter.cc102
-rw-r--r--chromium/components/browsing_data/core/counters/bookmark_counter.h41
-rw-r--r--chromium/components/browsing_data/core/counters/browsing_data_counter.cc27
-rw-r--r--chromium/components/browsing_data/core/counters/browsing_data_counter.h11
-rw-r--r--chromium/components/browsing_data/core/counters/history_counter.cc66
-rw-r--r--chromium/components/browsing_data/core/counters/history_counter.h14
-rw-r--r--chromium/components/browsing_data/core/counters/passwords_counter.cc19
-rw-r--r--chromium/components/browsing_data/core/counters/passwords_counter.h11
-rw-r--r--chromium/components/browsing_data/core/counters/sync_tracker.cc44
-rw-r--r--chromium/components/browsing_data/core/counters/sync_tracker.h40
-rw-r--r--chromium/components/browsing_data/core/history_notice_utils.cc61
-rw-r--r--chromium/components/browsing_data_strings.grdp19
-rw-r--r--chromium/components/captive_portal/captive_portal_detector.cc5
-rw-r--r--chromium/components/captive_portal/captive_portal_detector.h7
-rw-r--r--chromium/components/cast_certificate/cast_cert_validator.cc70
-rw-r--r--chromium/components/cast_certificate/cast_cert_validator_unittest.cc132
-rw-r--r--chromium/components/cast_certificate/cast_crl.cc8
-rw-r--r--chromium/components/cast_channel/BUILD.gn78
-rw-r--r--chromium/components/cast_channel/DEPS8
-rw-r--r--chromium/components/cast_channel/OWNERS5
-rw-r--r--chromium/components/cast_channel/cast_auth_util.cc394
-rw-r--r--chromium/components/cast_channel/cast_auth_util.h123
-rw-r--r--chromium/components/cast_channel/cast_auth_util_unittest.cc335
-rw-r--r--chromium/components/cast_channel/cast_channel_enum.cc46
-rw-r--r--chromium/components/cast_channel/cast_channel_enum.h144
-rw-r--r--chromium/components/cast_channel/cast_framer.cc174
-rw-r--r--chromium/components/cast_channel/cast_framer.h101
-rw-r--r--chromium/components/cast_channel/cast_framer_unittest.cc138
-rw-r--r--chromium/components/cast_channel/cast_message_util.cc87
-rw-r--r--chromium/components/cast_channel/cast_message_util.h35
-rw-r--r--chromium/components/cast_channel/cast_socket.cc659
-rw-r--r--chromium/components/cast_channel/cast_socket.h420
-rw-r--r--chromium/components/cast_channel/cast_socket_service.cc143
-rw-r--r--chromium/components/cast_channel/cast_socket_service.h112
-rw-r--r--chromium/components/cast_channel/cast_socket_service_unittest.cc96
-rw-r--r--chromium/components/cast_channel/cast_socket_unittest.cc1124
-rw-r--r--chromium/components/cast_channel/cast_test_util.cc47
-rw-r--r--chromium/components/cast_channel/cast_test_util.h170
-rw-r--r--chromium/components/cast_channel/cast_transport.cc376
-rw-r--r--chromium/components/cast_channel/cast_transport.h194
-rw-r--r--chromium/components/cast_channel/cast_transport_unittest.cc682
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate.cc197
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate.h113
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate_unittest.cc215
-rw-r--r--chromium/components/cast_channel/logger.cc133
-rw-r--r--chromium/components/cast_channel/logger.h81
-rw-r--r--chromium/components/cast_channel/logger_unittest.cc62
-rw-r--r--chromium/components/cast_channel/proto/BUILD.gn12
-rw-r--r--chromium/components/cast_channel/proto/authority_keys.proto17
-rw-r--r--chromium/components/cast_channel/proto/cast_channel.proto99
-rw-r--r--chromium/components/cdm/browser/cdm_message_filter_android.cc28
-rw-r--r--chromium/components/cdm/browser/cdm_message_filter_android.h16
-rw-r--r--chromium/components/cdm/browser/media_drm_storage_impl.cc37
-rw-r--r--chromium/components/cdm/browser/media_drm_storage_impl.h19
-rw-r--r--chromium/components/cdm/common/cdm_messages_android.h6
-rw-r--r--chromium/components/cdm/renderer/android_key_systems.cc17
-rw-r--r--chromium/components/certificate_reporting/BUILD.gn2
-rw-r--r--chromium/components/certificate_reporting/DEPS3
-rw-r--r--chromium/components/certificate_reporting/cert_logger.proto25
-rw-r--r--chromium/components/certificate_reporting/error_report.cc44
-rw-r--r--chromium/components/certificate_reporting/error_report.h16
-rw-r--r--chromium/components/certificate_reporting/error_report_unittest.cc70
-rw-r--r--chromium/components/certificate_reporting/error_reporter.cc4
-rw-r--r--chromium/components/certificate_transparency/ct_policy_manager.cc4
-rw-r--r--chromium/components/chrome_cleaner/public/constants/constants.cc2
-rw-r--r--chromium/components/chrome_cleaner/public/constants/constants.h26
-rw-r--r--chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom78
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/DEPS4
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/OWNERS7
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap12
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc41
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h22
-rw-r--r--chromium/components/cloud_devices/common/cloud_device_description.cc8
-rw-r--r--chromium/components/cloud_devices/common/description_items.h7
-rw-r--r--chromium/components/cloud_devices/common/description_items_inl.h11
-rw-r--r--chromium/components/cloud_devices/common/printer_description.cc8
-rw-r--r--chromium/components/component_updater/component_updater_service_unittest.cc12
-rw-r--r--chromium/components/component_updater/configurator_impl.cc28
-rw-r--r--chromium/components/component_updater/configurator_impl.h5
-rw-r--r--chromium/components/component_updater/default_component_installer.cc100
-rw-r--r--chromium/components/component_updater/default_component_installer.h34
-rw-r--r--chromium/components/component_updater/default_component_installer_unittest.cc116
-rw-r--r--chromium/components/components_strings.grd1
-rw-r--r--chromium/components/constrained_window/BUILD.gn1
-rw-r--r--chromium/components/constrained_window/DEPS1
-rw-r--r--chromium/components/constrained_window/constrained_window_views.cc18
-rw-r--r--chromium/components/content_settings/core/browser/BUILD.gn4
-rw-r--r--chromium/components/content_settings/core/browser/DEPS1
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_default_provider.cc5
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref.cc1
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref_provider.cc29
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_registry.cc10
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_utils.cc41
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_utils.h18
-rw-r--r--chromium/components/content_settings/core/browser/cookie_settings.cc1
-rw-r--r--chromium/components/content_settings/core/browser/host_content_settings_map.cc18
-rw-r--r--chromium/components/content_settings/core/browser/host_content_settings_map.h1
-rw-r--r--chromium/components/content_settings/core/browser/website_settings_registry.cc24
-rw-r--r--chromium/components/content_settings/core/common/BUILD.gn8
-rw-r--r--chromium/components/content_settings/core/common/DEPS1
-rw-r--r--chromium/components/content_settings/core/common/OWNERS3
-rw-r--r--chromium/components/content_settings/core/common/content_settings.cc92
-rw-r--r--chromium/components/content_settings/core/common/content_settings.h12
-rw-r--r--chromium/components/content_settings/core/common/content_settings.mojom4
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern.cc121
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern.h3
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc4
-rw-r--r--chromium/components/content_settings/core/common/content_settings_struct_traits.cc3
-rw-r--r--chromium/components/content_settings/core/common/content_settings_struct_traits.h8
-rw-r--r--chromium/components/content_settings/core/common/content_settings_types.h17
-rw-r--r--chromium/components/content_settings/core/common/content_settings_utils.cc47
-rw-r--r--chromium/components/content_settings/core/common/content_settings_utils.h27
-rw-r--r--chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.cc1
-rw-r--r--chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.h5
-rw-r--r--chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.cc1
-rw-r--r--chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.h2
-rw-r--r--chromium/components/crash/content/app/breakpad_linux.cc11
-rw-r--r--chromium/components/crash/content/app/breakpad_linux.h4
-rw-r--r--chromium/components/crash/content/app/breakpad_win.cc34
-rw-r--r--chromium/components/crash/content/app/crashpad.cc8
-rw-r--r--chromium/components/crash/content/app/crashpad.h2
-rw-r--r--chromium/components/crash/content/app/crashpad_win.cc51
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android.cc11
-rw-r--r--chromium/components/crash/content/browser/crash_dump_observer_android.cc1
-rw-r--r--chromium/components/crash/core/browser/resources/crashes.html17
-rw-r--r--chromium/components/cronet/android/BUILD.gn28
-rw-r--r--chromium/components/cronet/ios/BUILD.gn244
-rw-r--r--chromium/components/cronet/ios/cronet_consumer/BUILD.gn67
-rw-r--r--chromium/components/cronet/ios/test/BUILD.gn3
-rw-r--r--chromium/components/cryptauth/BUILD.gn13
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc96
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h9
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc739
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h186
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc509
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.cc35
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h21
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator_unittest.cc17
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.cc26
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h21
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver_unittest.cc71
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler.h38
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler_impl.cc76
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler_impl.h76
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler_impl_unittest.cc101
-rw-r--r--chromium/components/cryptauth/connection.cc21
-rw-r--r--chromium/components/cryptauth/connection.h3
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow.cc7
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow.h13
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc5
-rw-r--r--chromium/components/cryptauth/cryptauth_client.h19
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl.cc160
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl.h20
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl_unittest.cc56
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager.cc117
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager.h9
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager_unittest.cc29
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_manager.cc23
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_manager.h3
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc11
-rw-r--r--chromium/components/cryptauth/device_to_device_authenticator.cc18
-rw-r--r--chromium/components/cryptauth/device_to_device_authenticator.h4
-rw-r--r--chromium/components/cryptauth/device_to_device_authenticator_unittest.cc30
-rw-r--r--chromium/components/cryptauth/device_to_device_initiator_helper.cc332
-rw-r--r--chromium/components/cryptauth/device_to_device_initiator_helper.h187
-rw-r--r--chromium/components/cryptauth/device_to_device_initiator_operations.cc336
-rw-r--r--chromium/components/cryptauth/device_to_device_initiator_operations.h116
-rw-r--r--chromium/components/cryptauth/device_to_device_operations_unittest.cc49
-rw-r--r--chromium/components/cryptauth/fake_secure_channel.cc14
-rw-r--r--chromium/components/cryptauth/fake_secure_channel.h6
-rw-r--r--chromium/components/cryptauth/foreground_eid_generator.cc32
-rw-r--r--chromium/components/cryptauth/foreground_eid_generator.h3
-rw-r--r--chromium/components/cryptauth/foreground_eid_generator_unittest.cc168
-rw-r--r--chromium/components/cryptauth/local_device_data_provider.cc54
-rw-r--r--chromium/components/cryptauth/local_device_data_provider.h44
-rw-r--r--chromium/components/cryptauth/local_device_data_provider_unittest.cc236
-rw-r--r--chromium/components/cryptauth/mock_cryptauth_client.h16
-rw-r--r--chromium/components/cryptauth/mock_local_device_data_provider.cc54
-rw-r--r--chromium/components/cryptauth/mock_local_device_data_provider.h43
-rw-r--r--chromium/components/cryptauth/proto/cryptauth_api.proto6
-rw-r--r--chromium/components/cryptauth/remote_device.cc3
-rw-r--r--chromium/components/cryptauth/remote_device.h2
-rw-r--r--chromium/components/cryptauth/secure_channel.cc109
-rw-r--r--chromium/components/cryptauth/secure_channel.h30
-rw-r--r--chromium/components/cryptauth/secure_channel_unittest.cc140
-rw-r--r--chromium/components/cryptauth/wire_message.cc11
-rw-r--r--chromium/components/cryptauth/wire_message.h15
-rw-r--r--chromium/components/data_reduction_proxy/OWNERS3
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.cc100
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc243
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/BUILD.gn3
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc19
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc13
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc35
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc31
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h39
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc13
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc129
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h36
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc1
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc18
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h20
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc306
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h4
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc70
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc70
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc5
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc22
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h23
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc8
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc43
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h1
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics_unittest.cc35
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc20
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h6
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc238
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h8
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc261
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc1
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc26
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc23
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc46
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc50
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h9
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc10
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h18
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc17
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h14
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc4
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc561
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h185
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc829
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc19
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h8
-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_use_group.h35
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_use_group_provider.h33
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h15
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc8
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc7
-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.cc96
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h39
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc247
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h80
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc109
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h41
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc198
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc8
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h2
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc41
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h9
-rw-r--r--chromium/components/data_reduction_proxy/proto/pageload_metrics.proto4
-rw-r--r--chromium/components/data_usage/OWNERS4
-rw-r--r--chromium/components/data_usage/core/data_use.cc10
-rw-r--r--chromium/components/data_usage/core/data_use.h17
-rw-r--r--chromium/components/data_use_measurement/OWNERS2
-rw-r--r--chromium/components/data_use_measurement/core/data_use.cc12
-rw-r--r--chromium/components/data_use_measurement/core/data_use.h13
-rw-r--r--chromium/components/data_use_measurement/core/data_use_recorder.cc70
-rw-r--r--chromium/components/data_use_measurement/core/data_use_recorder.h70
-rw-r--r--chromium/components/data_use_measurement/core/data_use_user_data.cc2
-rw-r--r--chromium/components/data_use_measurement/core/data_use_user_data.h1
-rw-r--r--chromium/components/device_event_log/device_event_log_impl.cc6
-rw-r--r--chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc2
-rw-r--r--chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc6
-rw-r--r--chromium/components/discardable_memory/service/discardable_shared_memory_manager.h4
-rw-r--r--chromium/components/dom_distiller/content/browser/distillability_driver.cc1
-rw-r--r--chromium/components/dom_distiller/content/browser/distillability_driver.h2
-rw-r--r--chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc10
-rw-r--r--chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.h5
-rw-r--r--chromium/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc5
-rw-r--r--chromium/components/dom_distiller/content/browser/distiller_ui_handle.h3
-rw-r--r--chromium/components/dom_distiller/content/common/distiller_javascript_service.mojom4
-rw-r--r--chromium/components/dom_distiller/content/renderer/distillability_agent.cc22
-rw-r--r--chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.cc1
-rw-r--r--chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.h2
-rw-r--r--chromium/components/dom_distiller/content/renderer/distiller_native_javascript.cc6
-rw-r--r--chromium/components/dom_distiller/core/BUILD.gn3
-rw-r--r--chromium/components/dom_distiller/core/distilled_page_prefs_android.cc4
-rw-r--r--chromium/components/dom_distiller/core/distilled_page_prefs_android.h1
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_service_android.cc4
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_service_android.h1
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_switches.cc1
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_switches.h1
-rw-r--r--chromium/components/dom_distiller/core/experiments.cc7
-rw-r--r--chromium/components/dom_distiller/core/experiments.h15
-rw-r--r--chromium/components/dom_distiller/core/javascript/domdistiller.js3
-rw-r--r--chromium/components/dom_distiller/core/url_utils_android.cc4
-rw-r--r--chromium/components/dom_distiller/core/url_utils_android.h27
-rw-r--r--chromium/components/dom_distiller/ios/BUILD.gn1
-rw-r--r--chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm4
-rw-r--r--chromium/components/dom_distiller/ios/distiller_page_ios.mm6
-rw-r--r--chromium/components/dom_distiller/webui/resources/about_dom_distiller.html18
-rw-r--r--chromium/components/domain_reliability/beacon.cc7
-rw-r--r--chromium/components/domain_reliability/context.cc2
-rw-r--r--chromium/components/domain_reliability/monitor.cc2
-rw-r--r--chromium/components/doodle/doodle_fetcher_impl_unittest.cc33
-rw-r--r--chromium/components/doodle/doodle_service_unittest.cc2
-rw-r--r--chromium/components/doodle/doodle_types.cc38
-rw-r--r--chromium/components/doodle/doodle_types.h8
-rw-r--r--chromium/components/doodle/doodle_types_unittest.cc58
-rw-r--r--chromium/components/download/BUILD.gn23
-rw-r--r--chromium/components/download/components_unittests.filter14
-rw-r--r--chromium/components/download/content/BUILD.gn38
-rw-r--r--chromium/components/download/content/DEPS1
-rw-r--r--chromium/components/download/content/download_driver_impl.cc188
-rw-r--r--chromium/components/download/content/download_driver_impl.h67
-rw-r--r--chromium/components/download/content/download_driver_impl_unittest.cc129
-rw-r--r--chromium/components/download/content/factory/BUILD.gn25
-rw-r--r--chromium/components/download/content/factory/download_service_factory.cc58
-rw-r--r--chromium/components/download/content/factory/download_service_factory.h43
-rw-r--r--chromium/components/download/content/internal/BUILD.gn45
-rw-r--r--chromium/components/download/content/internal/download_driver_impl.cc279
-rw-r--r--chromium/components/download/content/internal/download_driver_impl.h88
-rw-r--r--chromium/components/download/content/internal/download_driver_impl_unittest.cc189
-rw-r--r--chromium/components/download/content/public/BUILD.gn38
-rw-r--r--chromium/components/download/content/public/all_download_item_notifier.cc75
-rw-r--r--chromium/components/download/content/public/all_download_item_notifier.h94
-rw-r--r--chromium/components/download/content/public/all_download_item_notifier_unittest.cc114
-rw-r--r--chromium/components/download/internal/BUILD.gn55
-rw-r--r--chromium/components/download/internal/DEPS1
-rw-r--r--chromium/components/download/internal/client_set.cc30
-rw-r--r--chromium/components/download/internal/client_set.h35
-rw-r--r--chromium/components/download/internal/client_set_unittest.cc43
-rw-r--r--chromium/components/download/internal/config.cc77
-rw-r--r--chromium/components/download/internal/config.h47
-rw-r--r--chromium/components/download/internal/controller.h110
-rw-r--r--chromium/components/download/internal/controller_impl.cc1034
-rw-r--r--chromium/components/download/internal/controller_impl.h234
-rw-r--r--chromium/components/download/internal/controller_impl_unittest.cc1405
-rw-r--r--chromium/components/download/internal/download_driver.h49
-rw-r--r--chromium/components/download/internal/download_service_impl.cc155
-rw-r--r--chromium/components/download/internal/download_service_impl.h23
-rw-r--r--chromium/components/download/internal/download_service_impl_unittest.cc145
-rw-r--r--chromium/components/download/internal/download_store.cc113
-rw-r--r--chromium/components/download/internal/download_store.h60
-rw-r--r--chromium/components/download/internal/download_store_unittest.cc324
-rw-r--r--chromium/components/download/internal/driver_entry.h17
-rw-r--r--chromium/components/download/internal/entry.cc29
-rw-r--r--chromium/components/download/internal/entry.h26
-rw-r--r--chromium/components/download/internal/entry_utils.cc67
-rw-r--r--chromium/components/download/internal/entry_utils.h49
-rw-r--r--chromium/components/download/internal/entry_utils_unittest.cc119
-rw-r--r--chromium/components/download/internal/file_monitor.h58
-rw-r--r--chromium/components/download/internal/file_monitor_impl.cc219
-rw-r--r--chromium/components/download/internal/file_monitor_impl.h63
-rw-r--r--chromium/components/download/internal/file_monitor_unittest.cc173
-rw-r--r--chromium/components/download/internal/model.h19
-rw-r--r--chromium/components/download/internal/model_impl.cc78
-rw-r--r--chromium/components/download/internal/model_impl.h10
-rw-r--r--chromium/components/download/internal/model_impl_unittest.cc203
-rw-r--r--chromium/components/download/internal/noop_store.cc53
-rw-r--r--chromium/components/download/internal/noop_store.h46
-rw-r--r--chromium/components/download/internal/proto/BUILD.gn18
-rw-r--r--chromium/components/download/internal/proto/entry.proto55
-rw-r--r--chromium/components/download/internal/proto/request.proto21
-rw-r--r--chromium/components/download/internal/proto/scheduling.proto43
-rw-r--r--chromium/components/download/internal/proto_conversions.cc307
-rw-r--r--chromium/components/download/internal/proto_conversions.h64
-rw-r--r--chromium/components/download/internal/proto_conversions_unittest.cc153
-rw-r--r--chromium/components/download/internal/scheduler/battery_listener.cc58
-rw-r--r--chromium/components/download/internal/scheduler/battery_listener.h59
-rw-r--r--chromium/components/download/internal/scheduler/battery_listener_unittest.cc74
-rw-r--r--chromium/components/download/internal/scheduler/device_status.cc72
-rw-r--r--chromium/components/download/internal/scheduler/device_status.h60
-rw-r--r--chromium/components/download/internal/scheduler/device_status_listener.cc122
-rw-r--r--chromium/components/download/internal/scheduler/device_status_listener.h72
-rw-r--r--chromium/components/download/internal/scheduler/device_status_listener_unittest.cc146
-rw-r--r--chromium/components/download/internal/scheduler/network_listener.cc84
-rw-r--r--chromium/components/download/internal/scheduler/network_listener.h66
-rw-r--r--chromium/components/download/internal/scheduler/network_listener_unittest.cc100
-rw-r--r--chromium/components/download/internal/scheduler/scheduler.h36
-rw-r--r--chromium/components/download/internal/scheduler/scheduler_impl.cc131
-rw-r--r--chromium/components/download/internal/scheduler/scheduler_impl.h72
-rw-r--r--chromium/components/download/internal/scheduler/scheduler_impl_unittest.cc429
-rw-r--r--chromium/components/download/internal/service_config_impl.cc24
-rw-r--r--chromium/components/download/internal/service_config_impl.h32
-rw-r--r--chromium/components/download/internal/service_config_impl_unittest.cc23
-rw-r--r--chromium/components/download/internal/startup_status.cc28
-rw-r--r--chromium/components/download/internal/startup_status.h40
-rw-r--r--chromium/components/download/internal/stats.cc310
-rw-r--r--chromium/components/download/internal/stats.h191
-rw-r--r--chromium/components/download/internal/store.h7
-rw-r--r--chromium/components/download/internal/test/BUILD.gn12
-rw-r--r--chromium/components/download/public/BUILD.gn22
-rw-r--r--chromium/components/download/public/client.h57
-rw-r--r--chromium/components/download/public/clients.h21
-rw-r--r--chromium/components/download/public/download_params.cc9
-rw-r--r--chromium/components/download/public/download_params.h35
-rw-r--r--chromium/components/download/public/download_service.h56
-rw-r--r--chromium/components/download/public/download_task_types.h22
-rw-r--r--chromium/components/download/public/service_config.h37
-rw-r--r--chromium/components/download/public/task_scheduler.h36
-rw-r--r--chromium/components/drive/BUILD.gn2
-rw-r--r--chromium/components/error_page/common/localized_error.cc36
-rw-r--r--chromium/components/error_page/renderer/net_error_helper_core.cc2
-rw-r--r--chromium/components/error_page/renderer/net_error_helper_core_unittest.cc6
-rw-r--r--chromium/components/error_page_strings.grdp6
-rw-r--r--chromium/components/exo/BUILD.gn49
-rw-r--r--chromium/components/exo/DEPS2
-rw-r--r--chromium/components/exo/buffer.cc59
-rw-r--r--chromium/components/exo/buffer.h4
-rw-r--r--chromium/components/exo/buffer_unittest.cc59
-rw-r--r--chromium/components/exo/compositor_frame_sink_holder.cc82
-rw-r--r--chromium/components/exo/compositor_frame_sink_holder.h79
-rw-r--r--chromium/components/exo/data_device.cc128
-rw-r--r--chromium/components/exo/data_device.h72
-rw-r--r--chromium/components/exo/data_device_delegate.h68
-rw-r--r--chromium/components/exo/data_offer.cc130
-rw-r--r--chromium/components/exo/data_offer.h79
-rw-r--r--chromium/components/exo/data_offer_delegate.h38
-rw-r--r--chromium/components/exo/data_offer_observer.h27
-rw-r--r--chromium/components/exo/data_offer_unittest.cc92
-rw-r--r--chromium/components/exo/data_source.cc25
-rw-r--r--chromium/components/exo/data_source.h38
-rw-r--r--chromium/components/exo/data_source_delegate.h47
-rw-r--r--chromium/components/exo/display.cc25
-rw-r--r--chromium/components/exo/display.h12
-rw-r--r--chromium/components/exo/display_unittest.cc42
-rw-r--r--chromium/components/exo/file_helper.h37
-rw-r--r--chromium/components/exo/gaming_seat.cc27
-rw-r--r--chromium/components/exo/gaming_seat.h51
-rw-r--r--chromium/components/exo/gaming_seat_ozone.cc108
-rw-r--r--chromium/components/exo/gaming_seat_unittest.cc291
-rw-r--r--chromium/components/exo/keyboard.cc159
-rw-r--r--chromium/components/exo/keyboard.h47
-rw-r--r--chromium/components/exo/keyboard_delegate.h15
-rw-r--r--chromium/components/exo/keyboard_device_configuration_delegate.h5
-rw-r--r--chromium/components/exo/keyboard_observer.h23
-rw-r--r--chromium/components/exo/keyboard_unittest.cc370
-rw-r--r--chromium/components/exo/layer_tree_frame_sink_holder.cc90
-rw-r--r--chromium/components/exo/layer_tree_frame_sink_holder.h77
-rw-r--r--chromium/components/exo/notification_surface.cc72
-rw-r--r--chromium/components/exo/notification_surface.h21
-rw-r--r--chromium/components/exo/notification_surface_manager.h2
-rw-r--r--chromium/components/exo/pointer.cc161
-rw-r--r--chromium/components/exo/pointer.h40
-rw-r--r--chromium/components/exo/pointer_unittest.cc29
-rw-r--r--chromium/components/exo/shell_surface.cc256
-rw-r--r--chromium/components/exo/shell_surface.h53
-rw-r--r--chromium/components/exo/shell_surface_unittest.cc285
-rw-r--r--chromium/components/exo/sub_surface.cc1
-rw-r--r--chromium/components/exo/surface.cc123
-rw-r--r--chromium/components/exo/surface.h31
-rw-r--r--chromium/components/exo/surface_tree_host.cc144
-rw-r--r--chromium/components/exo/surface_tree_host.h70
-rw-r--r--chromium/components/exo/surface_unittest.cc55
-rw-r--r--chromium/components/exo/touch_unittest.cc26
-rw-r--r--chromium/components/exo/wayland/BUILD.gn5
-rw-r--r--chromium/components/exo/wayland/clients/client_base.cc42
-rw-r--r--chromium/components/exo/wayland/clients/client_base.h2
-rw-r--r--chromium/components/exo/wayland/server.cc692
-rw-r--r--chromium/components/exo/wayland/server_unittest.cc2
-rw-r--r--chromium/components/exo/wm_helper.cc77
-rw-r--r--chromium/components/exo/wm_helper.h61
-rw-r--r--chromium/components/exo/wm_helper_ash.cc49
-rw-r--r--chromium/components/exo/wm_helper_ash.h35
-rw-r--r--chromium/components/exo/wm_helper_mus.cc10
-rw-r--r--chromium/components/exo/wm_helper_mus.h10
-rw-r--r--chromium/components/favicon/content/content_favicon_driver.cc4
-rw-r--r--chromium/components/favicon/content/content_favicon_driver_unittest.cc29
-rw-r--r--chromium/components/favicon/content/favicon_url_util.cc8
-rw-r--r--chromium/components/favicon/core/BUILD.gn9
-rw-r--r--chromium/components/favicon/core/DEPS1
-rw-r--r--chromium/components/favicon/core/fallback_icon_client.h28
-rw-r--r--chromium/components/favicon/core/fallback_icon_service.cc90
-rw-r--r--chromium/components/favicon/core/fallback_icon_service.h55
-rw-r--r--chromium/components/favicon/core/favicon_handler.cc33
-rw-r--r--chromium/components/favicon/core/favicon_handler.h5
-rw-r--r--chromium/components/favicon/core/favicon_handler_unittest.cc258
-rw-r--r--chromium/components/favicon/core/favicon_service.h33
-rw-r--r--chromium/components/favicon/core/favicon_service_impl.cc12
-rw-r--r--chromium/components/favicon/core/favicon_service_impl.h11
-rw-r--r--chromium/components/favicon/core/features.cc2
-rw-r--r--chromium/components/favicon/core/large_icon_service.cc100
-rw-r--r--chromium/components/favicon/core/large_icon_service.h24
-rw-r--r--chromium/components/favicon/core/large_icon_service_unittest.cc73
-rw-r--r--chromium/components/favicon_base/BUILD.gn6
-rw-r--r--chromium/components/favicon_base/fallback_icon_style.cc30
-rw-r--r--chromium/components/favicon_base/fallback_icon_style.h14
-rw-r--r--chromium/components/favicon_base/fallback_icon_url_parser.cc142
-rw-r--r--chromium/components/favicon_base/fallback_icon_url_parser.h74
-rw-r--r--chromium/components/favicon_base/fallback_icon_url_parser_unittest.cc294
-rw-r--r--chromium/components/favicon_base/favicon_callback.h4
-rw-r--r--chromium/components/favicon_base/favicon_types.cc5
-rw-r--r--chromium/components/favicon_base/favicon_types.h28
-rw-r--r--chromium/components/favicon_base/large_icon_url_parser.cc43
-rw-r--r--chromium/components/favicon_base/large_icon_url_parser.h48
-rw-r--r--chromium/components/favicon_base/large_icon_url_parser_unittest.cc63
-rw-r--r--chromium/components/feature_engagement_tracker/OWNERS3
-rw-r--r--chromium/components/feature_engagement_tracker/README.md442
-rw-r--r--chromium/components/feature_engagement_tracker/internal/BUILD.gn1
-rw-r--r--chromium/components/feature_engagement_tracker/internal/availability_model_impl.cc5
-rw-r--r--chromium/components/feature_engagement_tracker/internal/availability_model_impl.h9
-rw-r--r--chromium/components/feature_engagement_tracker/internal/availability_model_impl_unittest.cc28
-rw-r--r--chromium/components/feature_engagement_tracker/internal/availability_store.cc24
-rw-r--r--chromium/components/feature_engagement_tracker/internal/availability_store.h6
-rw-r--r--chromium/components/feature_engagement_tracker/internal/availability_store_unittest.cc31
-rw-r--r--chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.cc27
-rw-r--r--chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.h2
-rw-r--r--chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration_unittest.cc25
-rw-r--r--chromium/components/feature_engagement_tracker/internal/condition_validator.cc17
-rw-r--r--chromium/components/feature_engagement_tracker/internal/condition_validator.h4
-rw-r--r--chromium/components/feature_engagement_tracker/internal/configuration.cc47
-rw-r--r--chromium/components/feature_engagement_tracker/internal/configuration.h12
-rw-r--r--chromium/components/feature_engagement_tracker/internal/editable_configuration.cc12
-rw-r--r--chromium/components/feature_engagement_tracker/internal/editable_configuration.h2
-rw-r--r--chromium/components/feature_engagement_tracker/internal/feature_config_condition_validator_unittest.cc6
-rw-r--r--chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc20
-rw-r--r--chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl_unittest.cc54
-rw-r--r--chromium/components/feature_engagement_tracker/internal/model_impl.cc7
-rw-r--r--chromium/components/feature_engagement_tracker/internal/once_condition_validator.cc19
-rw-r--r--chromium/components/feature_engagement_tracker/internal/once_condition_validator.h4
-rw-r--r--chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.cc7
-rw-r--r--chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.h3
-rw-r--r--chromium/components/feature_engagement_tracker/internal/stats.cc21
-rw-r--r--chromium/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc1
-rw-r--r--chromium/components/feature_engagement_tracker/public/BUILD.gn3
-rw-r--r--chromium/components/feature_engagement_tracker/public/event_constants.cc33
-rw-r--r--chromium/components/feature_engagement_tracker/public/event_constants.h67
-rw-r--r--chromium/components/feature_engagement_tracker/public/feature_constants.cc44
-rw-r--r--chromium/components/feature_engagement_tracker/public/feature_constants.h44
-rw-r--r--chromium/components/feature_engagement_tracker/public/feature_list.cc58
-rw-r--r--chromium/components/feature_engagement_tracker/public/feature_list.h77
-rw-r--r--chromium/components/feature_engagement_tracker/test/BUILD.gn18
-rw-r--r--chromium/components/feedback/BUILD.gn4
-rw-r--r--chromium/components/feedback/feedback_data_unittest.cc22
-rw-r--r--chromium/components/feedback/feedback_report.cc6
-rw-r--r--chromium/components/feedback/feedback_report.h1
-rw-r--r--chromium/components/feedback/feedback_uploader.cc108
-rw-r--r--chromium/components/feedback/feedback_uploader.h64
-rw-r--r--chromium/components/feedback/feedback_uploader_chrome.cc34
-rw-r--r--chromium/components/feedback/feedback_uploader_chrome.h9
-rw-r--r--chromium/components/feedback/feedback_uploader_chrome_unittest.cc56
-rw-r--r--chromium/components/feedback/feedback_uploader_delegate.cc36
-rw-r--r--chromium/components/feedback/feedback_uploader_delegate.h14
-rw-r--r--chromium/components/feedback/feedback_uploader_factory.cc18
-rw-r--r--chromium/components/feedback/feedback_uploader_factory.h12
-rw-r--r--chromium/components/feedback/feedback_uploader_unittest.cc194
-rw-r--r--chromium/components/feedback/system_logs/system_logs_fetcher.cc99
-rw-r--r--chromium/components/feedback/system_logs/system_logs_fetcher.h83
-rw-r--r--chromium/components/feedback/system_logs/system_logs_source.cc14
-rw-r--r--chromium/components/feedback/system_logs/system_logs_source.h39
-rw-r--r--chromium/components/filesystem/BUILD.gn99
-rw-r--r--chromium/components/filesystem/directory_impl.cc32
-rw-r--r--chromium/components/filesystem/directory_impl.h3
-rw-r--r--chromium/components/filesystem/file_impl.cc2
-rw-r--r--chromium/components/filesystem/file_impl.h5
-rw-r--r--chromium/components/filesystem/file_system_app.cc8
-rw-r--r--chromium/components/filesystem/file_system_app.h8
-rw-r--r--chromium/components/filesystem/lock_table.cc12
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/BUILD.gn24
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc428
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h179
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc47
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/pref_service_factory.h29
-rw-r--r--chromium/components/filesystem/public/interfaces/directory.mojom7
-rw-r--r--chromium/components/find_in_page_strings.grdp2
-rw-r--r--chromium/components/flags_ui/flags_state.cc22
-rw-r--r--chromium/components/flags_ui/flags_state.h6
-rw-r--r--chromium/components/flags_ui/resources/apple_flags.css175
-rw-r--r--chromium/components/flags_ui/resources/apple_flags.html110
-rw-r--r--chromium/components/flags_ui/resources/flags.html49
-rw-r--r--chromium/components/flags_ui_strings.grdp8
-rw-r--r--chromium/components/font_service/font_service_app.cc6
-rw-r--r--chromium/components/font_service/font_service_app.h3
-rw-r--r--chromium/components/gcm_driver/BUILD.gn66
-rw-r--r--chromium/components/gcm_driver/config.gni9
-rw-r--r--chromium/components/gcm_driver/crypto/BUILD.gn2
-rw-r--r--chromium/components/gcm_driver/instance_id/BUILD.gn7
-rw-r--r--chromium/components/google/core/browser/google_util.cc26
-rw-r--r--chromium/components/google/core/browser/google_util.h9
-rw-r--r--chromium/components/guest_view/browser/BUILD.gn8
-rw-r--r--chromium/components/guest_view/browser/guest_view_base.cc28
-rw-r--r--chromium/components/guest_view/browser/guest_view_base.h13
-rw-r--r--chromium/components/guest_view/browser/guest_view_manager.h8
-rw-r--r--chromium/components/guest_view/browser/guest_view_manager_unittest.cc8
-rw-r--r--chromium/components/guest_view/browser/test_guest_view_manager.cc33
-rw-r--r--chromium/components/guest_view/browser/test_guest_view_manager.h8
-rw-r--r--chromium/components/guest_view/renderer/BUILD.gn8
-rw-r--r--chromium/components/guest_view/renderer/guest_view_request.cc9
-rw-r--r--chromium/components/handoff/handoff_manager.h5
-rw-r--r--chromium/components/handoff/handoff_manager.mm17
-rw-r--r--chromium/components/history/core/browser/BUILD.gn3
-rw-r--r--chromium/components/history/core/browser/android/favicon_sql_handler.cc6
-rw-r--r--chromium/components/history/core/browser/expire_history_backend_unittest.cc13
-rw-r--r--chromium/components/history/core/browser/history_backend.cc82
-rw-r--r--chromium/components/history/core/browser/history_backend.h34
-rw-r--r--chromium/components/history/core/browser/history_backend_unittest.cc81
-rw-r--r--chromium/components/history/core/browser/history_match.cc39
-rw-r--r--chromium/components/history/core/browser/history_match.h59
-rw-r--r--chromium/components/history/core/browser/history_model_worker.cc2
-rw-r--r--chromium/components/history/core/browser/history_model_worker.h2
-rw-r--r--chromium/components/history/core/browser/history_service.cc58
-rw-r--r--chromium/components/history/core/browser/history_service.h38
-rw-r--r--chromium/components/history/core/browser/history_types.cc14
-rw-r--r--chromium/components/history/core/browser/history_types.h56
-rw-r--r--chromium/components/history/core/browser/history_types_unittest.cc12
-rw-r--r--chromium/components/history/core/browser/thumbnail_database.cc102
-rw-r--r--chromium/components/history/core/browser/thumbnail_database.h42
-rw-r--r--chromium/components/history/core/browser/thumbnail_database_unittest.cc236
-rw-r--r--chromium/components/history/core/browser/top_sites_backend.cc17
-rw-r--r--chromium/components/history/core/browser/top_sites_backend.h7
-rw-r--r--chromium/components/history/core/browser/top_sites_impl.cc112
-rw-r--r--chromium/components/history/core/browser/top_sites_impl.h41
-rw-r--r--chromium/components/history/core/browser/top_sites_impl_unittest.cc34
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_bridge.cc839
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_bridge.h142
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc409
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_metadata_database.cc22
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc18
-rw-r--r--chromium/components/history/core/browser/typed_url_syncable_service_unittest.cc16
-rw-r--r--chromium/components/history/core/browser/web_history_service.cc79
-rw-r--r--chromium/components/history/core/browser/web_history_service.h37
-rw-r--r--chromium/components/history/core/browser/web_history_service_unittest.cc34
-rw-r--r--chromium/components/history_strings.grdp2
-rw-r--r--chromium/components/image_fetcher/ios/DEPS2
-rw-r--r--chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm18
-rw-r--r--chromium/components/image_fetcher/ios/webp_decoder.h2
-rw-r--r--chromium/components/infobars/core/infobar_delegate.h8
-rw-r--r--chromium/components/infobars/core/infobar_manager.h8
-rw-r--r--chromium/components/invalidation/impl/BUILD.gn2
-rw-r--r--chromium/components/json_schema/json_schema_validator_unittest_base.cc8
-rw-r--r--chromium/components/keyed_service/content/browser_context_keyed_base_factory.cc2
-rw-r--r--chromium/components/keyed_service/content/browser_context_keyed_service_factory.cc2
-rw-r--r--chromium/components/keyed_service/content/refcounted_browser_context_keyed_service_factory.cc2
-rw-r--r--chromium/components/keyed_service/core/keyed_service_base_factory.cc5
-rw-r--r--chromium/components/keyed_service/core/keyed_service_base_factory.h7
-rw-r--r--chromium/components/keyed_service/core/refcounted_keyed_service.cc2
-rw-r--r--chromium/components/keyed_service/ios/browser_state_keyed_service_factory.cc2
-rw-r--r--chromium/components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.cc2
-rw-r--r--chromium/components/language/DEPS8
-rw-r--r--chromium/components/language/OWNERS4
-rw-r--r--chromium/components/language/README15
-rw-r--r--chromium/components/language/content/DEPS4
-rw-r--r--chromium/components/language/content/browser/DEPS3
-rw-r--r--chromium/components/language/content/renderer/DEPS3
-rw-r--r--chromium/components/language/core/browser/BUILD.gn33
-rw-r--r--chromium/components/language/core/browser/DEPS3
-rw-r--r--chromium/components/language/core/browser/url_language_histogram.cc146
-rw-r--r--chromium/components/language/core/browser/url_language_histogram.h71
-rw-r--r--chromium/components/language/core/browser/url_language_histogram_unittest.cc147
-rw-r--r--chromium/components/language/ios/DEPS3
-rw-r--r--chromium/components/leveldb/env_mojo.cc134
-rw-r--r--chromium/components/leveldb/env_mojo.h18
-rw-r--r--chromium/components/leveldb/leveldb_app.cc19
-rw-r--r--chromium/components/leveldb/leveldb_app.h10
-rw-r--r--chromium/components/leveldb/leveldb_database_impl.cc63
-rw-r--r--chromium/components/leveldb/leveldb_database_impl.h16
-rw-r--r--chromium/components/leveldb/leveldb_mojo_proxy.cc9
-rw-r--r--chromium/components/leveldb/leveldb_mojo_proxy.h12
-rw-r--r--chromium/components/leveldb/leveldb_service_impl.cc38
-rw-r--r--chromium/components/leveldb/leveldb_service_impl.h19
-rw-r--r--chromium/components/leveldb/leveldb_service_unittest.cc12
-rw-r--r--chromium/components/leveldb/public/interfaces/BUILD.gn2
-rw-r--r--chromium/components/leveldb/public/interfaces/leveldb.mojom9
-rw-r--r--chromium/components/leveldb/remote_iterator_unittest.cc2
-rw-r--r--chromium/components/leveldb_proto/leveldb_database.cc36
-rw-r--r--chromium/components/leveldb_proto/options.h3
-rw-r--r--chromium/components/leveldb_proto/proto_database_impl_unittest.cc15
-rw-r--r--chromium/components/leveldb_proto/testing/fake_db.h13
-rw-r--r--chromium/components/login/OWNERS1
-rw-r--r--chromium/components/login/screens/screen_context.cc17
-rw-r--r--chromium/components/login/screens/screen_context.h10
-rw-r--r--chromium/components/login_dialog_strings.grdp29
-rw-r--r--chromium/components/machine_intelligence/BUILD.gn39
-rw-r--r--chromium/components/machine_intelligence/DEPS5
-rw-r--r--chromium/components/machine_intelligence/OWNERS2
-rw-r--r--chromium/components/machine_intelligence/proto/BUILD.gn (renamed from chromium/components/translate/core/browser/proto/BUILD.gn)0
-rw-r--r--chromium/components/machine_intelligence/proto/ranker_model.proto49
-rw-r--r--chromium/components/machine_intelligence/proto/translate_ranker_model.proto46
-rw-r--r--chromium/components/machine_intelligence/ranker_model.cc54
-rw-r--r--chromium/components/machine_intelligence/ranker_model.h45
-rw-r--r--chromium/components/machine_intelligence/ranker_model_loader.cc306
-rw-r--r--chromium/components/machine_intelligence/ranker_model_loader.h196
-rw-r--r--chromium/components/machine_intelligence/ranker_model_loader_unittest.cc359
-rw-r--r--chromium/components/machine_intelligence/ranker_model_unittest.cc76
-rw-r--r--chromium/components/machine_intelligence/ranker_url_fetcher.cc109
-rw-r--r--chromium/components/machine_intelligence/ranker_url_fetcher.h77
-rw-r--r--chromium/components/metrics/BUILD.gn17
-rw-r--r--chromium/components/metrics/call_stack_profile_collector.cc6
-rw-r--r--chromium/components/metrics/call_stack_profile_collector.h5
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider.cc243
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider.h44
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc238
-rw-r--r--chromium/components/metrics/call_stack_profile_params.cc22
-rw-r--r--chromium/components/metrics/call_stack_profile_params.h29
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector.cc26
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector.h13
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector_unittest.cc3
-rw-r--r--chromium/components/metrics/cloned_install_detector.cc19
-rw-r--r--chromium/components/metrics/cloned_install_detector.h14
-rw-r--r--chromium/components/metrics/cloned_install_detector_unittest.cc12
-rw-r--r--chromium/components/metrics/drive_metrics_provider.cc27
-rw-r--r--chromium/components/metrics/drive_metrics_provider.h22
-rw-r--r--chromium/components/metrics/drive_metrics_provider_win.cc6
-rw-r--r--chromium/components/metrics/field_trials_provider.cc65
-rw-r--r--chromium/components/metrics/field_trials_provider.h55
-rw-r--r--chromium/components/metrics/field_trials_provider_unittest.cc103
-rw-r--r--chromium/components/metrics/file_metrics_provider.cc200
-rw-r--r--chromium/components/metrics/file_metrics_provider.h39
-rw-r--r--chromium/components/metrics/file_metrics_provider_unittest.cc223
-rw-r--r--chromium/components/metrics/leak_detector/leak_detector.cc2
-rw-r--r--chromium/components/metrics/machine_id_provider.h31
-rw-r--r--chromium/components/metrics/machine_id_provider_stub.cc14
-rw-r--r--chromium/components/metrics/machine_id_provider_win.cc14
-rw-r--r--chromium/components/metrics/machine_id_provider_win_unittest.cc13
-rw-r--r--chromium/components/metrics/metrics_log.cc102
-rw-r--r--chromium/components/metrics/metrics_log.h30
-rw-r--r--chromium/components/metrics/metrics_log_store.cc1
-rw-r--r--chromium/components/metrics/metrics_log_unittest.cc86
-rw-r--r--chromium/components/metrics/metrics_provider.cc6
-rw-r--r--chromium/components/metrics/metrics_provider.h8
-rw-r--r--chromium/components/metrics/metrics_service.cc174
-rw-r--r--chromium/components/metrics/metrics_service.h80
-rw-r--r--chromium/components/metrics/metrics_service_accessor.cc8
-rw-r--r--chromium/components/metrics/metrics_service_client.cc21
-rw-r--r--chromium/components/metrics/metrics_service_client.h5
-rw-r--r--chromium/components/metrics/metrics_service_unittest.cc175
-rw-r--r--chromium/components/metrics/metrics_state_manager.cc16
-rw-r--r--chromium/components/metrics/metrics_state_manager.h21
-rw-r--r--chromium/components/metrics/metrics_state_manager_unittest.cc3
-rw-r--r--chromium/components/metrics/metrics_switches.cc7
-rw-r--r--chromium/components/metrics/metrics_switches.h2
-rw-r--r--chromium/components/metrics/net/network_metrics_provider.cc125
-rw-r--r--chromium/components/metrics/net/network_metrics_provider.h30
-rw-r--r--chromium/components/metrics/net/network_metrics_provider_unittest.cc100
-rw-r--r--chromium/components/metrics/net/version_utils.cc41
-rw-r--r--chromium/components/metrics/net/version_utils.h29
-rw-r--r--chromium/components/metrics/persistent_system_profile.cc437
-rw-r--r--chromium/components/metrics/persistent_system_profile.h158
-rw-r--r--chromium/components/metrics/persistent_system_profile_unittest.cc171
-rw-r--r--chromium/components/metrics/proto/BUILD.gn1
-rw-r--r--chromium/components/metrics/proto/cast_logs.proto10
-rw-r--r--chromium/components/metrics/proto/chrome_user_metrics_extension.proto4
-rw-r--r--chromium/components/metrics/proto/printer_event.proto64
-rw-r--r--chromium/components/metrics/proto/system_profile.proto26
-rw-r--r--chromium/components/metrics/proto/translate_event.proto9
-rw-r--r--chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h16
-rw-r--r--chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom1
-rw-r--r--chromium/components/metrics/single_sample_metrics.cc1
-rw-r--r--chromium/components/metrics/single_sample_metrics.h2
-rw-r--r--chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc4
-rw-r--r--chromium/components/metrics/stability_metrics_helper.cc2
-rw-r--r--chromium/components/metrics/version_utils.cc40
-rw-r--r--chromium/components/metrics/version_utils.h28
-rw-r--r--chromium/components/metrics_services_manager/metrics_services_manager.cc5
-rw-r--r--chromium/components/metrics_services_manager/metrics_services_manager_client.h3
-rw-r--r--chromium/components/nacl/loader/BUILD.gn2
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.cc2
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_throttle.cc95
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_throttle.h17
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc6
-rw-r--r--chromium/components/navigation_metrics/BUILD.gn1
-rw-r--r--chromium/components/navigation_metrics/DEPS2
-rw-r--r--chromium/components/navigation_metrics/navigation_metrics.cc47
-rw-r--r--chromium/components/net_log/BUILD.gn6
-rw-r--r--chromium/components/net_log/chrome_net_log.cc98
-rw-r--r--chromium/components/net_log/chrome_net_log.h60
-rw-r--r--chromium/components/net_log/net_export_file_writer.cc370
-rw-r--r--chromium/components/net_log/net_export_file_writer.h249
-rw-r--r--chromium/components/net_log/net_export_file_writer_unittest.cc795
-rw-r--r--chromium/components/net_log/net_log_file_writer.cc380
-rw-r--r--chromium/components/net_log/net_log_file_writer.h245
-rw-r--r--chromium/components/net_log/net_log_file_writer_unittest.cc798
-rw-r--r--chromium/components/net_log/resources/net_export.css2
-rw-r--r--chromium/components/net_log/resources/net_export.html30
-rw-r--r--chromium/components/net_log/resources/net_export.js38
-rw-r--r--chromium/components/neterror/resources/neterror.css35
-rw-r--r--chromium/components/neterror/resources/neterror.html13
-rw-r--r--chromium/components/neterror/resources/neterror.js8
-rw-r--r--chromium/components/neterror/resources/offline.js54
-rw-r--r--chromium/components/network_session_configurator/BUILD.gn34
-rw-r--r--chromium/components/network_session_configurator/DEPS7
-rw-r--r--chromium/components/network_session_configurator/browser/BUILD.gn33
-rw-r--r--chromium/components/network_session_configurator/browser/DEPS6
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator.cc431
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator.h33
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc514
-rw-r--r--chromium/components/network_session_configurator/common/BUILD.gn15
-rw-r--r--chromium/components/network_session_configurator/common/DEPS3
-rw-r--r--chromium/components/network_session_configurator/common/network_switch_list.h39
-rw-r--r--chromium/components/network_session_configurator/common/network_switches.cc31
-rw-r--r--chromium/components/network_session_configurator/common/network_switches.h29
-rw-r--r--chromium/components/network_session_configurator/network_session_configurator.cc356
-rw-r--r--chromium/components/network_session_configurator/network_session_configurator.h30
-rw-r--r--chromium/components/network_session_configurator/network_session_configurator_unittest.cc357
-rw-r--r--chromium/components/network_time/network_time_test_utils.cc9
-rw-r--r--chromium/components/network_time/network_time_tracker.cc15
-rw-r--r--chromium/components/new_or_sad_tab_strings.grdp16
-rw-r--r--chromium/components/ntp_snippets/BUILD.gn34
-rw-r--r--chromium/components/ntp_snippets/DEPS4
-rw-r--r--chromium/components/ntp_snippets/OWNERS1
-rw-r--r--chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.cc174
-rw-r--r--chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.h106
-rw-r--r--chromium/components/ntp_snippets/breaking_news/breaking_news_listener.h32
-rw-r--r--chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.cc155
-rw-r--r--chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.h83
-rw-r--r--chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider_unittest.cc152
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc182
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_json_request.h93
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_json_request_unittest.cc190
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager.cc65
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager.h42
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc270
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h105
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager_impl_unittest.cc334
-rw-r--r--chromium/components/ntp_snippets/category.h4
-rw-r--r--chromium/components/ntp_snippets/content_suggestion.cc7
-rw-r--r--chromium/components/ntp_snippets/content_suggestion.h7
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_metrics.cc34
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_metrics.h9
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc82
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_service.cc49
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_service.h7
-rw-r--r--chromium/components/ntp_snippets/features.cc17
-rw-r--r--chromium/components/ntp_snippets/features.h14
-rw-r--r--chromium/components/ntp_snippets/ntp_snippets_constants.cc26
-rw-r--r--chromium/components/ntp_snippets/ntp_snippets_constants.h17
-rw-r--r--chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc4
-rw-r--r--chromium/components/ntp_snippets/pref_names.cc9
-rw-r--r--chromium/components/ntp_snippets/pref_names.h20
-rw-r--r--chromium/components/ntp_snippets/remote/DEPS3
-rw-r--r--chromium/components/ntp_snippets/remote/cached_image_fetcher.cc142
-rw-r--r--chromium/components/ntp_snippets/remote/cached_image_fetcher.h86
-rw-r--r--chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc153
-rw-r--r--chromium/components/ntp_snippets/remote/contextual_json_request.cc251
-rw-r--r--chromium/components/ntp_snippets/remote/contextual_json_request.h115
-rw-r--r--chromium/components/ntp_snippets/remote/contextual_json_request_unittest.cc90
-rwxr-xr-xchromium/components/ntp_snippets/remote/fetch.py16
-rw-r--r--chromium/components/ntp_snippets/remote/json_request.cc40
-rw-r--r--chromium/components/ntp_snippets/remote/json_request.h15
-rw-r--r--chromium/components/ntp_snippets/remote/json_request_unittest.cc25
-rw-r--r--chromium/components/ntp_snippets/remote/json_to_categories.cc135
-rw-r--r--chromium/components/ntp_snippets/remote/json_to_categories.h45
-rw-r--r--chromium/components/ntp_snippets/remote/persistent_scheduler.h9
-rw-r--r--chromium/components/ntp_snippets/remote/prefetched_pages_tracker.h35
-rw-r--r--chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc121
-rw-r--r--chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h68
-rw-r--r--chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl_unittest.cc238
-rw-r--r--chromium/components/ntp_snippets/remote/proto/ntp_snippets.proto6
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestion.cc48
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestion.h14
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestion_unittest.cc36
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_database.h1
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc27
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc454
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h161
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc344
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h147
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc1168
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc1004
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider.h3
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc256
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h74
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc2828
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_scheduler.h12
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc147
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h10
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc122
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_status_service_unittest.cc11
-rw-r--r--chromium/components/ntp_snippets/remote/test_utils.cc30
-rw-r--r--chromium/components/ntp_snippets/remote/test_utils.h27
-rw-r--r--chromium/components/ntp_tiles/BUILD.gn3
-rw-r--r--chromium/components/ntp_tiles/constants.cc3
-rw-r--r--chromium/components/ntp_tiles/constants.h3
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl.cc76
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl.h7
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc24
-rw-r--r--chromium/components/ntp_tiles/metrics.cc67
-rw-r--r--chromium/components/ntp_tiles/metrics.h21
-rw-r--r--chromium/components/ntp_tiles/metrics_unittest.cc73
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites.cc124
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites.h41
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites_unittest.cc212
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl.cc31
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl.h6
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl_unittest.cc34
-rw-r--r--chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc89
-rw-r--r--chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h26
-rw-r--r--chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.cc164
-rw-r--r--chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.h64
-rw-r--r--chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler_client.h61
-rw-r--r--chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html8
-rw-r--r--chromium/components/ntp_tiles/webui/resources/popular_sites_internals.css48
-rw-r--r--chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html86
-rw-r--r--chromium/components/ntp_tiles/webui/resources/popular_sites_internals.js63
-rw-r--r--chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.cc5
-rw-r--r--chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.h3
-rw-r--r--chromium/components/offline_items_collection/core/offline_content_aggregator.cc2
-rw-r--r--chromium/components/offline_pages/content/BUILD.gn45
-rw-r--r--chromium/components/offline_pages/content/DEPS6
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents.cc20
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents.h4
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc36
-rw-r--r--chromium/components/offline_pages/content/prefetch_service_factory.cc36
-rw-r--r--chromium/components/offline_pages/content/prefetch_service_factory.h43
-rw-r--r--chromium/components/offline_pages/content/suggested_articles_observer.cc170
-rw-r--r--chromium/components/offline_pages/content/suggested_articles_observer.h80
-rw-r--r--chromium/components/offline_pages/content/suggested_articles_observer_unittest.cc215
-rw-r--r--chromium/components/offline_pages/core/BUILD.gn2
-rw-r--r--chromium/components/offline_pages/core/DEPS2
-rw-r--r--chromium/components/offline_pages/core/archive_manager.cc15
-rw-r--r--chromium/components/offline_pages/core/archive_manager_unittest.cc15
-rw-r--r--chromium/components/offline_pages/core/background/DEPS1
-rw-r--r--chromium/components/offline_pages/core/background/network_quality_provider_stub.cc14
-rw-r--r--chromium/components/offline_pages/core/background/network_quality_provider_stub.h23
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator.cc4
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator.h4
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc2
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator_unittest.cc5
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store_sql.cc43
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store_unittest.cc74
-rw-r--r--chromium/components/offline_pages/core/background/save_page_request.cc6
-rw-r--r--chromium/components/offline_pages/core/background/save_page_request.h10
-rw-r--r--chromium/components/offline_pages/core/background/save_page_request_unittest.cc6
-rw-r--r--chromium/components/offline_pages/core/client_id.cc25
-rw-r--r--chromium/components/offline_pages/core/client_id.h36
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter.cc8
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter.h4
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc7
-rw-r--r--chromium/components/offline_pages/core/offline_page_feature.cc18
-rw-r--r--chromium/components/offline_pages/core/offline_page_feature.h9
-rw-r--r--chromium/components/offline_pages/core/offline_page_item.cc41
-rw-r--r--chromium/components/offline_pages/core/offline_page_item.h27
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc32
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_sql.h2
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_unittest.cc82
-rw-r--r--chromium/components/offline_pages/core/offline_page_model.cc23
-rw-r--r--chromium/components/offline_pages/core/offline_page_model.h24
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_impl.cc119
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_impl.h25
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc247
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_store.cc10
-rw-r--r--chromium/components/offline_pages/core/offline_page_types.h12
-rw-r--r--chromium/components/offline_pages/core/prefetch/BUILD.gn90
-rw-r--r--chromium/components/offline_pages/core/prefetch/DEPS10
-rw-r--r--chromium/components/offline_pages/core/prefetch/README.md40
-rw-r--r--chromium/components/offline_pages/core/prefetch/add_unique_urls_task.cc145
-rw-r--r--chromium/components/offline_pages/core/prefetch/add_unique_urls_task.h58
-rw-r--r--chromium/components/offline_pages/core/prefetch/add_unique_urls_task_unittest.cc129
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.cc18
-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.cc26
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.cc75
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.h52
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc69
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_request.cc19
-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.cc34
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_task.cc54
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_task.h43
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_task_unittest.cc53
-rw-r--r--chromium/components/offline_pages/core/prefetch/offline_metrics_collector.h36
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_dispatcher.h43
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc125
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h35
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc137
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_downloader.cc113
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_downloader.h96
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc293
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc87
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h65
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc114
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_gcm_handler.h46
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_importer.h42
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_item.cc2
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_item.h14
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_item_unittest.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory.h47
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc84
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h68
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl_unittest.cc104
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.cc15
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.h8
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc25
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h7
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc25
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc77
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h29
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_server_urls_unittest.cc67
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_service.h27
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_service_impl.cc96
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_service_impl.h36
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_service_impl_unittest.cc21
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc119
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.h86
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_types.cc15
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_types.h39
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/any.proto6
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto44
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/operation.proto9
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/status.proto6
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/timestamp.proto21
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc173
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store.h122
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc90
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h54
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc66
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.cc29
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.h25
-rw-r--r--chromium/components/offline_pages/core/prefetch/suggested_articles_observer.cc132
-rw-r--r--chromium/components/offline_pages/core/prefetch/suggested_articles_observer.h73
-rw-r--r--chromium/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc138
-rw-r--r--chromium/components/offline_pages/core/prefetch/task_test_base.cc33
-rw-r--r--chromium/components/offline_pages/core/prefetch/task_test_base.h52
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_offline_metrics_collector.h28
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc48
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h48
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc29
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h30
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_importer.h30
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.cc35
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h35
-rw-r--r--chromium/components/offline_pages/core/stub_offline_page_model.cc2
-rw-r--r--chromium/components/offline_pages/core/stub_offline_page_model.h2
-rw-r--r--chromium/components/omnibox/browser/BUILD.gn6
-rw-r--r--chromium/components/omnibox_strings.grdp3
-rw-r--r--chromium/components/os_crypt/BUILD.gn7
-rw-r--r--chromium/components/os_crypt/key_storage_config_linux.cc12
-rw-r--r--chromium/components/os_crypt/key_storage_config_linux.h43
-rw-r--r--chromium/components/os_crypt/key_storage_keyring.h1
-rw-r--r--chromium/components/os_crypt/key_storage_linux.cc50
-rw-r--r--chromium/components/os_crypt/key_storage_linux.h22
-rw-r--r--chromium/components/os_crypt/key_storage_util_linux.cc45
-rw-r--r--chromium/components/os_crypt/key_storage_util_linux.h15
-rw-r--r--chromium/components/os_crypt/key_storage_util_linux_unittest.cc112
-rw-r--r--chromium/components/os_crypt/kwallet_dbus_unittest.cc249
-rw-r--r--chromium/components/os_crypt/os_crypt.h27
-rw-r--r--chromium/components/os_crypt/os_crypt_linux.cc31
-rw-r--r--chromium/components/os_crypt/os_crypt_mocker_linux.cc5
-rw-r--r--chromium/components/packed_ct_ev_whitelist/BUILD.gn34
-rw-r--r--chromium/components/packed_ct_ev_whitelist/DEPS5
-rw-r--r--chromium/components/packed_ct_ev_whitelist/OWNERS5
-rw-r--r--chromium/components/packed_ct_ev_whitelist/bit_stream_reader.cc78
-rw-r--r--chromium/components/packed_ct_ev_whitelist/bit_stream_reader.h67
-rw-r--r--chromium/components/packed_ct_ev_whitelist/bit_stream_reader_unittest.cc100
-rw-r--r--chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.cc152
-rw-r--r--chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h86
-rw-r--r--chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist_unittest.cc157
-rw-r--r--chromium/components/page_info_strings.grdp58
-rw-r--r--chromium/components/pairing/bluetooth_host_pairing_controller.cc82
-rw-r--r--chromium/components/pairing/bluetooth_host_pairing_controller.h32
-rw-r--r--chromium/components/pairing/fake_host_pairing_controller.cc4
-rw-r--r--chromium/components/pairing/fake_host_pairing_controller.h2
-rw-r--r--chromium/components/pairing/host_pairing_controller.h11
-rw-r--r--chromium/components/pairing/shark_connection_listener.cc6
-rw-r--r--chromium/components/pairing/shark_connection_listener.h4
-rw-r--r--chromium/components/password_manager/DEPS1
-rw-r--r--chromium/components/password_manager/OWNERS1
-rw-r--r--chromium/components/password_manager/content/browser/BUILD.gn3
-rw-r--r--chromium/components/password_manager/content/browser/DEPS1
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.cc33
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.h14
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_factory.cc30
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_factory.h11
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc197
-rw-r--r--chromium/components/password_manager/content/browser/credential_manager_impl.cc3
-rw-r--r--chromium/components/password_manager/content/browser/credential_manager_impl.h2
-rw-r--r--chromium/components/password_manager/content/browser/credential_manager_impl_unittest.cc13
-rw-r--r--chromium/components/password_manager/content/browser/visible_password_observer.cc55
-rw-r--r--chromium/components/password_manager/content/browser/visible_password_observer.h74
-rw-r--r--chromium/components/password_manager/content/common/BUILD.gn16
-rw-r--r--chromium/components/password_manager/content/common/OWNERS2
-rw-r--r--chromium/components/password_manager/content/common/credential_manager.mojom53
-rw-r--r--chromium/components/password_manager/content/common/credential_manager.typemap2
-rw-r--r--chromium/components/password_manager/content/common/credential_manager_struct_traits.h2
-rw-r--r--chromium/components/password_manager/content/renderer/BUILD.gn1
-rw-r--r--chromium/components/password_manager/content/renderer/credential_manager_client.cc12
-rw-r--r--chromium/components/password_manager/content/renderer/credential_manager_client.h2
-rw-r--r--chromium/components/password_manager/content/renderer/credential_manager_client_browsertest.cc8
-rw-r--r--chromium/components/password_manager/core/browser/BUILD.gn113
-rw-r--r--chromium/components/password_manager/core/browser/DEPS14
-rw-r--r--chromium/components/password_manager/core/browser/affiliated_match_helper.cc223
-rw-r--r--chromium/components/password_manager/core/browser/affiliated_match_helper.h160
-rw-r--r--chromium/components/password_manager/core/browser/affiliated_match_helper_unittest.cc630
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_backend.cc294
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_backend.h167
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_backend_unittest.cc925
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_database.cc266
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_database.h93
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_database_unittest.cc309
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetch_throttler.cc143
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetch_throttler.h135
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h29
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetch_throttler_unittest.cc424
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetcher.cc251
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetcher.h99
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetcher_delegate.h46
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_fetcher_unittest.cc337
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_service.cc109
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_service.h172
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_service_unittest.cc159
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_utils.cc314
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_utils.h197
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_utils_unittest.cc247
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc242
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h160
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc670
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_api.proto (renamed from chromium/components/password_manager/core/browser/affiliation_api.proto)0
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.cc286
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.h167
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend_unittest.cc874
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.cc346
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.h120
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_database_unittest.cc409
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.cc144
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h135
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h29
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc420
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc264
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h103
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_delegate.h46
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_unittest.cc383
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.cc94
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.h168
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_service_unittest.cc167
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc319
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.h222
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils_unittest.cc242
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/facet_manager.cc251
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/facet_manager.h137
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/facet_manager_host.h37
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/facet_manager_unittest.cc1539
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.cc80
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.h53
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.cc66
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h83
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc75
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h78
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.cc37
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.h39
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h39
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_password_form_manager.cc5
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_password_form_manager.h2
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_password_form_manager_unittest.cc15
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc2
-rw-r--r--chromium/components/password_manager/core/browser/export/password_exporter.cc11
-rw-r--r--chromium/components/password_manager/core/browser/export/password_exporter.h12
-rw-r--r--chromium/components/password_manager/core/browser/export/password_exporter_unittest.cc11
-rw-r--r--chromium/components/password_manager/core/browser/facet_manager.cc249
-rw-r--r--chromium/components/password_manager/core/browser/facet_manager.h137
-rw-r--r--chromium/components/password_manager/core/browser/facet_manager_host.h36
-rw-r--r--chromium/components/password_manager/core/browser/facet_manager_unittest.cc1522
-rw-r--r--chromium/components/password_manager/core/browser/fake_affiliation_api.cc78
-rw-r--r--chromium/components/password_manager/core/browser/fake_affiliation_api.h53
-rw-r--r--chromium/components/password_manager/core/browser/fake_affiliation_fetcher.cc66
-rw-r--r--chromium/components/password_manager/core/browser/fake_affiliation_fetcher.h83
-rw-r--r--chromium/components/password_manager/core/browser/fake_form_fetcher.cc3
-rw-r--r--chromium/components/password_manager/core/browser/fake_form_fetcher.h2
-rw-r--r--chromium/components/password_manager/core/browser/form_fetcher.h3
-rw-r--r--chromium/components/password_manager/core/browser/form_fetcher_impl.cc9
-rw-r--r--chromium/components/password_manager/core/browser/form_saver.h3
-rw-r--r--chromium/components/password_manager/core/browser/form_saver_impl.cc44
-rw-r--r--chromium/components/password_manager/core/browser/form_saver_impl.h22
-rw-r--r--chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc25
-rw-r--r--chromium/components/password_manager/core/browser/hash_password_manager.cc128
-rw-r--r--chromium/components/password_manager/core/browser/hash_password_manager.h67
-rw-r--r--chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc73
-rw-r--r--chromium/components/password_manager/core/browser/http_data_cleaner.cc8
-rw-r--r--chromium/components/password_manager/core/browser/http_password_store_migrator.cc4
-rw-r--r--chromium/components/password_manager/core/browser/http_password_store_migrator.h4
-rw-r--r--chromium/components/password_manager/core/browser/import/password_csv_reader.cc9
-rw-r--r--chromium/components/password_manager/core/browser/import/password_csv_reader_unittest.cc20
-rw-r--r--chromium/components/password_manager/core/browser/import/password_importer.cc18
-rw-r--r--chromium/components/password_manager/core/browser/import/password_importer.h11
-rw-r--r--chromium/components/password_manager/core/browser/import/password_importer_unittest.cc13
-rw-r--r--chromium/components/password_manager/core/browser/login_database.cc18
-rw-r--r--chromium/components/password_manager/core/browser/login_database.h9
-rw-r--r--chromium/components/password_manager/core/browser/login_database_unittest.cc31
-rw-r--r--chromium/components/password_manager/core/browser/mock_affiliated_match_helper.cc68
-rw-r--r--chromium/components/password_manager/core/browser/mock_affiliated_match_helper.h64
-rw-r--r--chromium/components/password_manager/core/browser/mock_affiliation_consumer.cc37
-rw-r--r--chromium/components/password_manager/core/browser/mock_affiliation_consumer.h39
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_store.cc7
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_store.h4
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager.cc2
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.cc474
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.h200
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager_unittest.cc395
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc434
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder.h395
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc586
-rw-r--r--chromium/components/password_manager/core/browser/password_form_user_action.h30
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.cc124
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.h30
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client.h19
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc105
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h122
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc75
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.cc15
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.h58
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.cc20
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.h15
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment_unittest.cc51
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_test_utils.cc1
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_unittest.cc169
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.cc31
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.h6
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util_unittest.cc32
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_defines.h14
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc50
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager.h11
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc64
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector.cc47
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector.h18
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector_consumer.h4
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc71
-rw-r--r--chromium/components/password_manager/core/browser/password_store.cc127
-rw-r--r--chromium/components/password_manager/core/browser/password_store.h87
-rw-r--r--chromium/components/password_manager/core/browser/password_store_default.cc28
-rw-r--r--chromium/components/password_manager/core/browser/password_store_default.h12
-rw-r--r--chromium/components/password_manager/core/browser/password_store_default_unittest.cc6
-rw-r--r--chromium/components/password_manager/core/browser/password_store_factory_util.cc35
-rw-r--r--chromium/components/password_manager/core/browser/password_store_factory_util.h20
-rw-r--r--chromium/components/password_manager/core/browser/password_store_signin_notifier.cc30
-rw-r--r--chromium/components/password_manager/core/browser/password_store_signin_notifier.h47
-rw-r--r--chromium/components/password_manager/core/browser/password_store_unittest.cc237
-rw-r--r--chromium/components/password_manager/core/browser/password_syncable_service.cc462
-rw-r--r--chromium/components/password_manager/core/browser/password_syncable_service.h36
-rw-r--r--chromium/components/password_manager/core/browser/password_syncable_service_unittest.cc655
-rw-r--r--chromium/components/password_manager/core/browser/password_ui_utils.cc55
-rw-r--r--chromium/components/password_manager/core/browser/password_ui_utils.h32
-rw-r--r--chromium/components/password_manager/core/browser/password_ui_utils_unittest.cc70
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.cc88
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.h44
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc226
-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/sql_table_builder.cc342
-rw-r--r--chromium/components/password_manager/core/browser/sql_table_builder.h126
-rw-r--r--chromium/components/password_manager/core/browser/sql_table_builder_unittest.cc219
-rw-r--r--chromium/components/password_manager/core/browser/stub_form_saver.cc13
-rw-r--r--chromium/components/password_manager/core/browser/stub_form_saver.h1
-rw-r--r--chromium/components/password_manager/core/browser/stub_password_manager_client.cc25
-rw-r--r--chromium/components/password_manager/core/browser/stub_password_manager_client.h11
-rw-r--r--chromium/components/password_manager/core/browser/test_affiliation_fetcher_factory.h39
-rw-r--r--chromium/components/password_manager/core/browser/test_password_store.cc7
-rw-r--r--chromium/components/password_manager/core/common/password_manager_features.cc6
-rw-r--r--chromium/components/password_manager/core/common/password_manager_features.h1
-rw-r--r--chromium/components/password_manager/core/common/password_manager_pref_names.cc5
-rw-r--r--chromium/components/password_manager/core/common/password_manager_pref_names.h20
-rw-r--r--chromium/components/password_manager/sync/browser/BUILD.gn3
-rw-r--r--chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.cc238
-rw-r--r--chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.h165
-rw-r--r--chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service_unittest.cc463
-rw-r--r--chromium/components/password_manager/sync/browser/password_model_worker.cc2
-rw-r--r--chromium/components/password_manager/sync/browser/password_model_worker.h2
-rw-r--r--chromium/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc1
-rw-r--r--chromium/components/payments/OWNERS2
-rw-r--r--chromium/components/payments/android/OWNERS2
-rw-r--r--chromium/components/payments/android/web_app_manifest_section_table.cc10
-rw-r--r--chromium/components/payments/android/web_app_manifest_section_table_unittest.cc55
-rw-r--r--chromium/components/payments/content/BUILD.gn6
-rw-r--r--chromium/components/payments/content/DEPS1
-rw-r--r--chromium/components/payments/content/OWNERS2
-rw-r--r--chromium/components/payments/content/android/BUILD.gn8
-rw-r--r--chromium/components/payments/content/android/OWNERS2
-rw-r--r--chromium/components/payments/content/android/currency_formatter_android.cc5
-rw-r--r--chromium/components/payments/content/android/currency_formatter_android.h3
-rw-r--r--chromium/components/payments/content/android/origin_security_checker_android.cc6
-rw-r--r--chromium/components/payments/content/android/origin_security_checker_android.h16
-rw-r--r--chromium/components/payments/content/android/payment_details_validation_android.cc8
-rw-r--r--chromium/components/payments/content/android/payment_details_validation_android.h16
-rw-r--r--chromium/components/payments/content/android/payment_manifest_downloader_android.cc153
-rw-r--r--chromium/components/payments/content/android/payment_manifest_downloader_android.h42
-rw-r--r--chromium/components/payments/content/android/payment_manifest_parser_android.cc11
-rw-r--r--chromium/components/payments/content/android/payment_manifest_parser_android.h6
-rw-r--r--chromium/components/payments/content/origin_security_checker.cc2
-rw-r--r--chromium/components/payments/content/payment_details_validation.cc2
-rw-r--r--chromium/components/payments/content/payment_details_validation.h2
-rw-r--r--chromium/components/payments/content/payment_manifest_downloader.cc196
-rw-r--r--chromium/components/payments/content/payment_manifest_downloader.h87
-rw-r--r--chromium/components/payments/content/payment_manifest_downloader_unittest.cc75
-rw-r--r--chromium/components/payments/content/payment_manifest_parser_host.cc49
-rw-r--r--chromium/components/payments/content/payment_manifest_parser_host.h24
-rw-r--r--chromium/components/payments/content/payment_request.cc36
-rw-r--r--chromium/components/payments/content/payment_request.h5
-rw-r--r--chromium/components/payments/content/payment_request_spec.cc167
-rw-r--r--chromium/components/payments/content/payment_request_spec.h45
-rw-r--r--chromium/components/payments/content/payment_request_spec_unittest.cc2
-rw-r--r--chromium/components/payments/content/payment_request_state.cc69
-rw-r--r--chromium/components/payments/content/payment_request_state.h11
-rw-r--r--chromium/components/payments/content/payment_request_state_unittest.cc9
-rw-r--r--chromium/components/payments/content/payment_request_web_contents_manager.h2
-rw-r--r--chromium/components/payments/content/payment_response_helper.h2
-rw-r--r--chromium/components/payments/content/payment_response_helper_unittest.cc16
-rw-r--r--chromium/components/payments/content/payments_validators.h2
-rw-r--r--chromium/components/payments/content/utility/fingerprint_parser.cc10
-rw-r--r--chromium/components/payments/content/utility/payment_manifest_parser.cc220
-rw-r--r--chromium/components/payments/content/utility/payment_manifest_parser.h26
-rw-r--r--chromium/components/payments/content/utility/payment_manifest_parser_unittest.cc366
-rw-r--r--chromium/components/payments/core/BUILD.gn105
-rw-r--r--chromium/components/payments/core/DEPS9
-rw-r--r--chromium/components/payments/core/OWNERS2
-rw-r--r--chromium/components/payments/core/address_normalization_manager.cc15
-rw-r--r--chromium/components/payments/core/address_normalization_manager.h22
-rw-r--r--chromium/components/payments/core/address_normalization_manager_unittest.cc10
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument.cc55
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument.h25
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument_unittest.cc76
-rw-r--r--chromium/components/payments/core/basic_card_response.cc15
-rw-r--r--chromium/components/payments/core/features.cc19
-rw-r--r--chromium/components/payments/core/features.h25
-rw-r--r--chromium/components/payments/core/journey_logger.cc146
-rw-r--r--chromium/components/payments/core/journey_logger.h25
-rw-r--r--chromium/components/payments/core/journey_logger_unittest.cc430
-rw-r--r--chromium/components/payments/core/payment_address.cc46
-rw-r--r--chromium/components/payments/core/payment_instrument.h19
-rw-r--r--chromium/components/payments/core/payment_method_data.cc26
-rw-r--r--chromium/components/payments/core/payment_method_data.h5
-rw-r--r--chromium/components/payments/core/payment_method_data_unittest.cc24
-rw-r--r--chromium/components/payments/core/payment_request_base_delegate.h80
-rw-r--r--chromium/components/payments/core/payment_request_data_util.cc78
-rw-r--r--chromium/components/payments/core/payment_request_data_util.h28
-rw-r--r--chromium/components/payments/core/payment_request_data_util_unittest.cc10
-rw-r--r--chromium/components/payments/core/payment_request_delegate.h65
-rw-r--r--chromium/components/payments/core/payments_profile_comparator.cc24
-rw-r--r--chromium/components/payments/core/payments_profile_comparator.h11
-rw-r--r--chromium/components/payments/core/payments_test_util.cc24
-rw-r--r--chromium/components/payments/core/payments_test_util.h27
-rw-r--r--chromium/components/payments/core/strings_util.cc64
-rw-r--r--chromium/components/payments/core/strings_util.h15
-rw-r--r--chromium/components/payments/core/strings_util_unittest.cc71
-rw-r--r--chromium/components/payments/core/subkey_requester.cc27
-rw-r--r--chromium/components/payments/core/subkey_requester.h7
-rw-r--r--chromium/components/payments/core/subkey_requester_unittest.cc20
-rw-r--r--chromium/components/payments/mojom/BUILD.gn19
-rw-r--r--chromium/components/payments/mojom/OWNERS2
-rw-r--r--chromium/components/payments/mojom/payment_app.mojom61
-rw-r--r--chromium/components/payments/mojom/payment_manifest_parser.mojom5
-rw-r--r--chromium/components/payments/mojom/payment_request.mojom234
-rw-r--r--chromium/components/payments_strings.grdp73
-rw-r--r--chromium/components/pdf/DEPS3
-rw-r--r--chromium/components/pdf/browser/BUILD.gn1
-rw-r--r--chromium/components/pdf/browser/pdf_web_contents_helper.cc109
-rw-r--r--chromium/components/pdf/browser/pdf_web_contents_helper.h38
-rw-r--r--chromium/components/pdf/renderer/pdf_accessibility_tree.cc2
-rw-r--r--chromium/components/physical_web/webui/physical_web_base_message_handler.cc4
-rw-r--r--chromium/components/physical_web/webui/resources/physical_web.html8
-rw-r--r--chromium/components/plugins/renderer/loadable_plugin_placeholder.cc8
-rw-r--r--chromium/components/plugins/renderer/webview_plugin.cc33
-rw-r--r--chromium/components/plugins/renderer/webview_plugin.h10
-rw-r--r--chromium/components/policy/core/browser/BUILD.gn2
-rw-r--r--chromium/components/policy/core/common/BUILD.gn8
-rw-r--r--chromium/components/policy_strings.grdp9
-rw-r--r--chromium/components/precache/DEPS11
-rw-r--r--chromium/components/precache/OWNERS8
-rw-r--r--chromium/components/precache/README11
-rw-r--r--chromium/components/precache/android/BUILD.gn29
-rw-r--r--chromium/components/precache/content/BUILD.gn50
-rw-r--r--chromium/components/precache/content/DEPS18
-rw-r--r--chromium/components/precache/content/precache_manager.cc514
-rw-r--r--chromium/components/precache/content/precache_manager.h269
-rw-r--r--chromium/components/precache/content/precache_manager_unittest.cc692
-rw-r--r--chromium/components/precache/core/BUILD.gn90
-rw-r--r--chromium/components/precache/core/DEPS7
-rw-r--r--chromium/components/precache/core/fetcher_pool.h87
-rw-r--r--chromium/components/precache/core/fetcher_pool_unittest.cc223
-rw-r--r--chromium/components/precache/core/precache_database.cc467
-rw-r--r--chromium/components/precache/core/precache_database.h201
-rw-r--r--chromium/components/precache/core/precache_database_unittest.cc685
-rw-r--r--chromium/components/precache/core/precache_fetcher.cc972
-rw-r--r--chromium/components/precache/core/precache_fetcher.h369
-rw-r--r--chromium/components/precache/core/precache_fetcher_unittest.cc2125
-rw-r--r--chromium/components/precache/core/precache_manifest_util.cc67
-rw-r--r--chromium/components/precache/core/precache_manifest_util.h30
-rw-r--r--chromium/components/precache/core/precache_referrer_host_table.cc123
-rw-r--r--chromium/components/precache/core/precache_referrer_host_table.h82
-rw-r--r--chromium/components/precache/core/precache_referrer_host_table_unittest.cc154
-rw-r--r--chromium/components/precache/core/precache_session_table.cc121
-rw-r--r--chromium/components/precache/core/precache_session_table.h83
-rw-r--r--chromium/components/precache/core/precache_session_table_unittest.cc177
-rw-r--r--chromium/components/precache/core/precache_switches.cc21
-rw-r--r--chromium/components/precache/core/precache_switches.h20
-rw-r--r--chromium/components/precache/core/precache_url_table.cc205
-rw-r--r--chromium/components/precache/core/precache_url_table.h111
-rw-r--r--chromium/components/precache/core/precache_url_table_unittest.cc202
-rw-r--r--chromium/components/precache/core/proto/precache.proto160
-rw-r--r--chromium/components/precache/core/proto/quota.proto25
-rw-r--r--chromium/components/precache/core/proto/timestamp.proto17
-rw-r--r--chromium/components/precache/core/proto/unfinished_work.proto61
-rw-r--r--chromium/components/prefs/BUILD.gn3
-rw-r--r--chromium/components/prefs/in_memory_pref_store.h1
-rw-r--r--chromium/components/prefs/in_memory_pref_store_unittest.cc8
-rw-r--r--chromium/components/prefs/json_pref_store.cc68
-rw-r--r--chromium/components/prefs/json_pref_store.h15
-rw-r--r--chromium/components/prefs/json_pref_store_unittest.cc172
-rw-r--r--chromium/components/prefs/overlay_user_pref_store.cc112
-rw-r--r--chromium/components/prefs/overlay_user_pref_store.h22
-rw-r--r--chromium/components/prefs/overlay_user_pref_store_unittest.cc52
-rw-r--r--chromium/components/prefs/persistent_pref_store.cc18
-rw-r--r--chromium/components/prefs/persistent_pref_store.h7
-rw-r--r--chromium/components/prefs/persistent_pref_store_unittest.cc22
-rw-r--r--chromium/components/prefs/persistent_pref_store_unittest.h15
-rw-r--r--chromium/components/prefs/pref_registry.cc14
-rw-r--r--chromium/components/prefs/pref_registry.h20
-rw-r--r--chromium/components/prefs/pref_service.cc54
-rw-r--r--chromium/components/prefs/pref_service.h11
-rw-r--r--chromium/components/prefs/pref_service_factory.cc6
-rw-r--r--chromium/components/prefs/pref_service_factory.h5
-rw-r--r--chromium/components/prefs/pref_value_store.cc23
-rw-r--r--chromium/components/prefs/pref_value_store.h54
-rw-r--r--chromium/components/prefs/scoped_user_pref_update.cc5
-rw-r--r--chromium/components/prefs/scoped_user_pref_update.h7
-rw-r--r--chromium/components/prefs/testing_pref_store.cc6
-rw-r--r--chromium/components/prefs/testing_pref_store.h2
-rw-r--r--chromium/components/previews/OWNERS2
-rw-r--r--chromium/components/previews/core/previews_black_list.h3
-rw-r--r--chromium/components/previews/core/previews_decider.h7
-rw-r--r--chromium/components/previews/core/previews_experiments.cc8
-rw-r--r--chromium/components/previews/core/previews_experiments.h9
-rw-r--r--chromium/components/previews/core/previews_experiments_unittest.cc8
-rw-r--r--chromium/components/previews/core/previews_features.cc4
-rw-r--r--chromium/components/previews/core/previews_features.h1
-rw-r--r--chromium/components/previews/core/previews_io_data.cc17
-rw-r--r--chromium/components/previews/core/previews_io_data.h4
-rw-r--r--chromium/components/previews/core/previews_io_data_unittest.cc63
-rw-r--r--chromium/components/printing/browser/print_manager.cc8
-rw-r--r--chromium/components/printing/browser/print_manager.h4
-rw-r--r--chromium/components/printing/common/print_messages.h5
-rw-r--r--chromium/components/printing/renderer/print_web_view_helper.cc69
-rw-r--r--chromium/components/printing/renderer/print_web_view_helper.h6
-rw-r--r--chromium/components/printing/renderer/print_web_view_helper_linux.cc4
-rw-r--r--chromium/components/printing/service/pdf_compositor_service.cc4
-rw-r--r--chromium/components/printing/service/pdf_compositor_service.h1
-rw-r--r--chromium/components/proximity_auth/BUILD.gn18
-rw-r--r--chromium/components/proximity_auth/public/interfaces/BUILD.gn11
-rw-r--r--chromium/components/proximity_auth/public/interfaces/auth_type.mojom28
-rw-r--r--chromium/components/proxy_config/ios/proxy_service_factory.cc3
-rw-r--r--chromium/components/proxy_config/pref_proxy_config_tracker.h2
-rw-r--r--chromium/components/proxy_config/pref_proxy_config_tracker_impl.cc73
-rw-r--r--chromium/components/proxy_config/pref_proxy_config_tracker_impl.h30
-rw-r--r--chromium/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc51
-rw-r--r--chromium/components/quirks/quirks_client.cc3
-rw-r--r--chromium/components/quirks/quirks_manager.cc12
-rw-r--r--chromium/components/quirks/quirks_manager.h10
-rw-r--r--chromium/components/rappor/rappor_recorder_impl.cc1
-rw-r--r--chromium/components/rappor/rappor_recorder_impl.h5
-rw-r--r--chromium/components/reading_list/core/BUILD.gn24
-rw-r--r--chromium/components/reading_list/core/reading_list_model.cc15
-rw-r--r--chromium/components/reading_list/core/reading_list_model.h6
-rw-r--r--chromium/components/reading_list/core/reading_list_model_impl.cc54
-rw-r--r--chromium/components/reading_list/core/reading_list_model_unittest.cc2
-rw-r--r--chromium/components/reading_list/core/reading_list_store.cc58
-rw-r--r--chromium/components/reading_list/core/reading_list_store.h29
-rw-r--r--chromium/components/reading_list/core/reading_list_store_unittest.cc8
-rw-r--r--chromium/components/reading_list/core/reading_list_switches.cc17
-rw-r--r--chromium/components/reading_list/core/reading_list_switches.h15
-rw-r--r--chromium/components/reading_list/features/BUILD.gn21
-rw-r--r--chromium/components/reading_list/features/reading_list.gni (renamed from chromium/components/reading_list/core/reading_list.gni)0
-rw-r--r--chromium/components/reading_list/features/reading_list_switches.cc17
-rw-r--r--chromium/components/reading_list/features/reading_list_switches.h15
-rw-r--r--chromium/components/reading_list/ios/BUILD.gn1
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_bridge_observer.h2
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_bridge_observer.mm4
-rw-r--r--chromium/components/renderer_context_menu/context_menu_content_type.cc4
-rw-r--r--chromium/components/renderer_context_menu/render_view_context_menu_base.cc10
-rw-r--r--chromium/components/resources/OWNERS11
-rw-r--r--chromium/components/resources/components_resources.grd1
-rw-r--r--chromium/components/resources/flags_ui_resources.grdp3
-rw-r--r--chromium/components/resources/ntp_tiles_resources.grdp3
-rw-r--r--chromium/components/resources/safe_browsing_resources.grdp6
-rw-r--r--chromium/components/resources/security_interstitials_resources.grdp6
-rw-r--r--chromium/components/safe_browsing/BUILD.gn18
-rw-r--r--chromium/components/safe_browsing/DEPS1
-rw-r--r--chromium/components/safe_browsing/OWNERS2
-rw-r--r--chromium/components/safe_browsing/base_blocking_page.cc26
-rw-r--r--chromium/components/safe_browsing/base_blocking_page.h7
-rw-r--r--chromium/components/safe_browsing/base_ping_manager.cc20
-rw-r--r--chromium/components/safe_browsing/base_ping_manager_unittest.cc10
-rw-r--r--chromium/components/safe_browsing/base_resource_throttle.cc46
-rw-r--r--chromium/components/safe_browsing/base_resource_throttle.h52
-rw-r--r--chromium/components/safe_browsing/browser/BUILD.gn8
-rw-r--r--chromium/components/safe_browsing/browser/DEPS3
-rw-r--r--chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc98
-rw-r--r--chromium/components/safe_browsing/browser/browser_url_loader_throttle.h70
-rw-r--r--chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.cc99
-rw-r--r--chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.h47
-rw-r--r--chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc163
-rw-r--r--chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.h102
-rw-r--r--chromium/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc13
-rw-r--r--chromium/components/safe_browsing/browser/threat_details.cc42
-rw-r--r--chromium/components/safe_browsing/browser/threat_details.h4
-rw-r--r--chromium/components/safe_browsing/browser/url_checker_delegate.h52
-rw-r--r--chromium/components/safe_browsing/common/BUILD.gn21
-rw-r--r--chromium/components/safe_browsing/common/DEPS2
-rw-r--r--chromium/components/safe_browsing/common/OWNERS2
-rw-r--r--chromium/components/safe_browsing/common/safe_browsing.mojom31
-rw-r--r--chromium/components/safe_browsing/common/safe_browsing_prefs.cc62
-rw-r--r--chromium/components/safe_browsing/common/safe_browsing_prefs.h43
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_constants.cc19
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_constants.h15
-rw-r--r--chromium/components/safe_browsing/common/utils.cc25
-rw-r--r--chromium/components/safe_browsing/common/utils.h20
-rw-r--r--chromium/components/safe_browsing/csd.proto63
-rw-r--r--chromium/components/safe_browsing/features.cc79
-rw-r--r--chromium/components/safe_browsing/features.h30
-rw-r--r--chromium/components/safe_browsing/password_protection/BUILD.gn84
-rw-r--r--chromium/components/safe_browsing/password_protection/DEPS5
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_request.cc102
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_request.h32
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_service.cc366
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_service.h73
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc420
-rw-r--r--chromium/components/safe_browsing/renderer/BUILD.gn37
-rw-r--r--chromium/components/safe_browsing/renderer/DEPS5
-rw-r--r--chromium/components/safe_browsing/renderer/threat_dom_details.cc36
-rw-r--r--chromium/components/safe_browsing/renderer/threat_dom_details.h3
-rw-r--r--chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.cc85
-rw-r--r--chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.h58
-rw-r--r--chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle_unittest.cc135
-rw-r--r--chromium/components/safe_browsing/triggers/BUILD.gn68
-rw-r--r--chromium/components/safe_browsing/triggers/DEPS4
-rw-r--r--chromium/components/safe_browsing/triggers/OWNERS1
-rw-r--r--chromium/components/safe_browsing/triggers/ad_sampler_trigger.cc96
-rw-r--r--chromium/components/safe_browsing/triggers/ad_sampler_trigger.h49
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_manager.cc119
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_manager.h81
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc245
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_throttler.cc101
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_throttler.h58
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc22
-rw-r--r--chromium/components/safe_browsing/web_ui/BUILD.gn31
-rw-r--r--chromium/components/safe_browsing/web_ui/DEPS6
-rw-r--r--chromium/components/safe_browsing/web_ui/constants.cc20
-rw-r--r--chromium/components/safe_browsing/web_ui/constants.h19
-rw-r--r--chromium/components/safe_browsing/web_ui/resources/safe_browsing.css26
-rw-r--r--chromium/components/safe_browsing/web_ui/resources/safe_browsing.html29
-rw-r--r--chromium/components/safe_browsing/web_ui/resources/safe_browsing.js48
-rw-r--r--chromium/components/safe_browsing/web_ui/safe_browsing_ui.cc77
-rw-r--r--chromium/components/safe_browsing/web_ui/safe_browsing_ui.h43
-rw-r--r--chromium/components/safe_browsing_db/BUILD.gn87
-rw-r--r--chromium/components/safe_browsing_db/DEPS1
-rw-r--r--chromium/components/safe_browsing_db/OWNERS5
-rw-r--r--chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.cc16
-rw-r--r--chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.h3
-rw-r--r--chromium/components/safe_browsing_db/database_manager.cc6
-rw-r--r--chromium/components/safe_browsing_db/database_manager.h56
-rw-r--r--chromium/components/safe_browsing_db/database_manager_unittest.cc4
-rw-r--r--chromium/components/safe_browsing_db/hit_report.h1
-rw-r--r--chromium/components/safe_browsing_db/remote_database_manager.cc32
-rw-r--r--chromium/components/safe_browsing_db/remote_database_manager.h6
-rw-r--r--chromium/components/safe_browsing_db/remote_database_manager_unittest.cc10
-rw-r--r--chromium/components/safe_browsing_db/safe_browsing_api_handler.h3
-rw-r--r--chromium/components/safe_browsing_db/test_database_manager.cc17
-rw-r--r--chromium/components/safe_browsing_db/test_database_manager.h9
-rw-r--r--chromium/components/safe_browsing_db/util.h2
-rw-r--r--chromium/components/safe_browsing_db/v4_database.cc49
-rw-r--r--chromium/components/safe_browsing_db/v4_database.h6
-rw-r--r--chromium/components/safe_browsing_db/v4_database_unittest.cc6
-rw-r--r--chromium/components/safe_browsing_db/v4_feature_list.cc15
-rw-r--r--chromium/components/safe_browsing_db/v4_get_hash_protocol_manager.cc12
-rw-r--r--chromium/components/safe_browsing_db/v4_get_hash_protocol_manager.h7
-rw-r--r--chromium/components/safe_browsing_db/v4_local_database_manager.cc178
-rw-r--r--chromium/components/safe_browsing_db/v4_local_database_manager.h35
-rw-r--r--chromium/components/safe_browsing_db/v4_local_database_manager_unittest.cc257
-rw-r--r--chromium/components/safe_browsing_db/v4_protocol_manager_util.cc26
-rw-r--r--chromium/components/safe_browsing_db/v4_protocol_manager_util.h33
-rw-r--r--chromium/components/safe_browsing_db/v4_store.cc13
-rw-r--r--chromium/components/safe_browsing_db/v4_update_protocol_manager.cc14
-rw-r--r--chromium/components/safe_browsing_db/v4_update_protocol_manager.h7
-rw-r--r--chromium/components/safe_browsing_db/whitelist_checker_client.cc74
-rw-r--r--chromium/components/safe_browsing_db/whitelist_checker_client.h55
-rw-r--r--chromium/components/safe_browsing_db/whitelist_checker_client_unittest.cc132
-rw-r--r--chromium/components/safe_browsing_strings.grdp6
-rw-r--r--chromium/components/safe_json/BUILD.gn2
-rw-r--r--chromium/components/safe_json/json_sanitizer.h4
-rw-r--r--chromium/components/safe_json/json_sanitizer_android.cc21
-rw-r--r--chromium/components/safe_json/safe_json_parser_impl.cc32
-rw-r--r--chromium/components/safe_json/safe_json_parser_impl.h8
-rw-r--r--chromium/components/safe_json/utility/safe_json_parser_mojo_impl.cc1
-rw-r--r--chromium/components/safe_json/utility/safe_json_parser_mojo_impl.h7
-rw-r--r--chromium/components/search_engines/default_search_manager.cc1
-rw-r--r--chromium/components/search_engines/default_search_manager.h1
-rw-r--r--chromium/components/search_engines/default_search_manager_unittest.cc14
-rw-r--r--chromium/components/search_engines/default_search_policy_handler.cc3
-rw-r--r--chromium/components/search_engines/prepopulated_engines.json8
-rw-r--r--chromium/components/search_engines/prepopulated_engines_schema.json1
-rw-r--r--chromium/components/search_engines/template_url.h2
-rw-r--r--chromium/components/search_engines/template_url_data.cc4
-rw-r--r--chromium/components/search_engines/template_url_data.h6
-rw-r--r--chromium/components/search_engines/template_url_data_util.cc16
-rw-r--r--chromium/components/search_engines/template_url_fetcher.h2
-rw-r--r--chromium/components/search_engines/template_url_parser.cc13
-rw-r--r--chromium/components/search_engines/template_url_prepopulate_data_unittest.cc62
-rw-r--r--chromium/components/search_provider_logos/BUILD.gn5
-rw-r--r--chromium/components/search_provider_logos/OWNERS4
-rw-r--r--chromium/components/search_provider_logos/features.cc14
-rw-r--r--chromium/components/search_provider_logos/features.h18
-rw-r--r--chromium/components/search_provider_logos/fixed_logo_api.cc35
-rw-r--r--chromium/components/search_provider_logos/fixed_logo_api.h31
-rw-r--r--chromium/components/search_provider_logos/google_logo_api.cc236
-rw-r--r--chromium/components/search_provider_logos/google_logo_api.h45
-rw-r--r--chromium/components/search_provider_logos/google_logo_api_unittest.cc100
-rw-r--r--chromium/components/search_provider_logos/logo_cache.cc18
-rw-r--r--chromium/components/search_provider_logos/logo_cache.h18
-rw-r--r--chromium/components/search_provider_logos/logo_cache_unittest.cc90
-rw-r--r--chromium/components/search_provider_logos/logo_common.h16
-rw-r--r--chromium/components/search_provider_logos/logo_tracker.cc60
-rw-r--r--chromium/components/search_provider_logos/logo_tracker.h51
-rw-r--r--chromium/components/search_provider_logos/logo_tracker_unittest.cc140
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_controller_client.cc8
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_controller_client.h1
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_page.cc3
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_page.h2
-rw-r--r--chromium/components/security_interstitials/content/unsafe_resource.cc11
-rw-r--r--chromium/components/security_interstitials/content/unsafe_resource.h3
-rw-r--r--chromium/components/security_interstitials/core/BUILD.gn4
-rw-r--r--chromium/components/security_interstitials/core/bad_clock_ui.cc4
-rw-r--r--chromium/components/security_interstitials/core/base_safe_browsing_error_ui.cc3
-rw-r--r--chromium/components/security_interstitials/core/base_safe_browsing_error_ui.h12
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_badclock.css9
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_captiveportal.css9
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_common.js46
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_large.html56
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_large.js172
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css45
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_ssl.css17
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_ui.html97
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css505
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_v2.html50
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js172
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js49
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css30
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html7
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js28
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/list_of_interstitials.html103
-rw-r--r--chromium/components/security_interstitials/core/common/resources/interstitial_common.css450
-rw-r--r--chromium/components/security_interstitials/core/common/resources/interstitial_common.js62
-rw-r--r--chromium/components/security_interstitials/core/common/resources/interstitial_core.css (renamed from chromium/components/security_interstitials/core/browser/resources/interstitial_common.css)0
-rw-r--r--chromium/components/security_interstitials/core/common/resources/interstitial_mobile_nav.js49
-rw-r--r--chromium/components/security_interstitials/core/controller_client.cc39
-rw-r--r--chromium/components/security_interstitials/core/controller_client.h19
-rw-r--r--chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc24
-rw-r--r--chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc29
-rw-r--r--chromium/components/security_interstitials/core/ssl_error_ui.cc27
-rw-r--r--chromium/components/security_interstitials/core/ssl_error_ui.h12
-rw-r--r--chromium/components/security_interstitials/core/superfish_error_ui.cc74
-rw-r--r--chromium/components/security_interstitials/core/superfish_error_ui.h36
-rw-r--r--chromium/components/security_interstitials/core/urls.cc15
-rw-r--r--chromium/components/security_interstitials/core/urls.h16
-rw-r--r--chromium/components/security_interstitials_strings.grdp34
-rw-r--r--chromium/components/security_state/DEPS1
-rw-r--r--chromium/components/security_state/content/content_utils.cc286
-rw-r--r--chromium/components/security_state/content/content_utils_unittest.cc71
-rw-r--r--chromium/components/security_state/core/security_state.cc101
-rw-r--r--chromium/components/security_state/core/security_state.h15
-rw-r--r--chromium/components/security_state/core/security_state_unittest.cc95
-rw-r--r--chromium/components/security_state/core/switches.cc5
-rw-r--r--chromium/components/security_state/core/switches.h3
-rw-r--r--chromium/components/security_state_strings.grdp12
-rw-r--r--chromium/components/sessions/BUILD.gn3
-rw-r--r--chromium/components/sessions/content/content_serialized_navigation_builder.cc6
-rw-r--r--chromium/components/sessions/content/content_serialized_navigation_builder.h16
-rw-r--r--chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc20
-rw-r--r--chromium/components/sessions/content/content_serialized_navigation_driver.cc44
-rw-r--r--chromium/components/sessions/content/content_serialized_navigation_driver.h4
-rw-r--r--chromium/components/sessions/core/base_session_service.cc2
-rw-r--r--chromium/components/sessions/core/serialized_navigation_driver.h12
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry.cc57
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm4
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_driver.cc72
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_driver.h4
-rw-r--r--chromium/components/signin/core/account_id/account_id.cc10
-rw-r--r--chromium/components/signin/core/account_id/account_id.h7
-rw-r--r--chromium/components/signin/core/browser/BUILD.gn48
-rw-r--r--chromium/components/signin/core/browser/about_signin_internals.cc58
-rw-r--r--chromium/components/signin/core/browser/about_signin_internals.h3
-rw-r--r--chromium/components/signin/core/browser/access_token_fetcher.cc3
-rw-r--r--chromium/components/signin/core/browser/access_token_fetcher.h3
-rw-r--r--chromium/components/signin/core/browser/account_fetcher_service.cc9
-rw-r--r--chromium/components/signin/core/browser/account_fetcher_service.h7
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor.cc7
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor.h3
-rw-r--r--chromium/components/signin/core/browser/account_tracker_service.cc1
-rw-r--r--chromium/components/signin/core/browser/account_tracker_service.h8
-rw-r--r--chromium/components/signin/core/browser/android/BUILD.gn6
-rw-r--r--chromium/components/signin/core/browser/child_account_info_fetcher_android.cc5
-rw-r--r--chromium/components/signin/core/browser/child_account_info_fetcher_android.h3
-rw-r--r--chromium/components/signin/core/browser/chrome_connected_header_helper.cc171
-rw-r--r--chromium/components/signin/core/browser/chrome_connected_header_helper.h55
-rw-r--r--chromium/components/signin/core/browser/dice_header_helper.cc147
-rw-r--r--chromium/components/signin/core/browser/dice_header_helper.h44
-rw-r--r--chromium/components/signin/core/browser/fake_signin_manager.cc10
-rw-r--r--chromium/components/signin/core/browser/fake_signin_manager.h8
-rw-r--r--chromium/components/signin/core/browser/profile_identity_provider.cc3
-rw-r--r--chromium/components/signin/core/browser/profile_identity_provider.h3
-rw-r--r--chromium/components/signin/core/browser/profile_oauth2_token_service.cc10
-rw-r--r--chromium/components/signin/core/browser/profile_oauth2_token_service.h18
-rw-r--r--chromium/components/signin/core/browser/refresh_token_annotation_request.cc44
-rw-r--r--chromium/components/signin/core/browser/refresh_token_annotation_request.h9
-rw-r--r--chromium/components/signin/core/browser/resources/signin_index.html2
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper.cc307
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper.h131
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper_unittest.cc296
-rw-r--r--chromium/components/signin/core/browser/signin_manager.cc35
-rw-r--r--chromium/components/signin/core/browser/signin_manager.h10
-rw-r--r--chromium/components/signin/core/browser/signin_manager_base.cc4
-rw-r--r--chromium/components/signin/core/browser/signin_manager_base.h44
-rw-r--r--chromium/components/signin/core/browser/signin_metrics.h1
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider.cc3
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider.h3
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc6
-rw-r--r--chromium/components/signin/core/browser/signin_tracker.cc6
-rw-r--r--chromium/components/signin/core/browser/signin_tracker.h2
-rw-r--r--chromium/components/signin/core/common/BUILD.gn15
-rw-r--r--chromium/components/signin/core/common/profile_management_switches.cc47
-rw-r--r--chromium/components/signin/core/common/profile_management_switches.h28
-rw-r--r--chromium/components/signin/core/common/signin_switches.cc14
-rw-r--r--chromium/components/signin/core/common/signin_switches.h12
-rw-r--r--chromium/components/signin/features.gni9
-rw-r--r--chromium/components/signin/ios/browser/BUILD.gn5
-rw-r--r--chromium/components/signin/ios/browser/account_consistency_service.h9
-rw-r--r--chromium/components/signin/ios/browser/account_consistency_service.mm22
-rw-r--r--chromium/components/signin/ios/browser/account_consistency_service_unittest.mm34
-rw-r--r--chromium/components/signin/ios/browser/merge_session_observer_bridge.h2
-rw-r--r--chromium/components/signin/ios/browser/merge_session_observer_bridge.mm4
-rw-r--r--chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.h3
-rw-r--r--chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm4
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm4
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm4
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm4
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_message_filter_platform.h2
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_message_filter_platform_android.cc9
-rw-r--r--chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc13
-rw-r--r--chromium/components/spellcheck/browser/spellchecker_session_bridge_android.h3
-rw-r--r--chromium/components/spellcheck/common/BUILD.gn6
-rw-r--r--chromium/components/spellcheck/common/OWNERS2
-rw-r--r--chromium/components/spellcheck/common/spellcheck.mojom28
-rw-r--r--chromium/components/spellcheck/common/spellcheck.typemap15
-rw-r--r--chromium/components/spellcheck/common/spellcheck_messages.h54
-rw-r--r--chromium/components/spellcheck/common/spellcheck_panel.mojom35
-rw-r--r--chromium/components/spellcheck/common/spellcheck_result.cc25
-rw-r--r--chromium/components/spellcheck/common/spellcheck_result.h22
-rw-r--r--chromium/components/spellcheck/renderer/hunspell_engine.cc12
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck.cc44
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck.h9
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_panel.cc87
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_panel.h41
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider.cc54
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider.h55
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc18
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider_test.cc89
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider_test.h34
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider_unittest.cc5
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_unittest.cc78
-rw-r--r--chromium/components/ssl_config/BUILD.gn1
-rw-r--r--chromium/components/ssl_config/DEPS1
-rw-r--r--chromium/components/ssl_config/ssl_config_prefs.cc1
-rw-r--r--chromium/components/ssl_config/ssl_config_prefs.h1
-rw-r--r--chromium/components/ssl_config/ssl_config_service_manager_pref.cc58
-rw-r--r--chromium/components/ssl_config/ssl_config_service_manager_pref_unittest.cc168
-rw-r--r--chromium/components/ssl_config/ssl_config_switches.cc9
-rw-r--r--chromium/components/ssl_config/ssl_config_switches.h6
-rw-r--r--chromium/components/startup_metric_utils/browser/startup_metric_host_impl.cc1
-rw-r--r--chromium/components/startup_metric_utils/browser/startup_metric_host_impl.h7
-rw-r--r--chromium/components/startup_metric_utils/browser/startup_metric_utils.cc15
-rw-r--r--chromium/components/startup_metric_utils/browser/startup_metric_utils.h6
-rw-r--r--chromium/components/storage_monitor/BUILD.gn1
-rw-r--r--chromium/components/storage_monitor/OWNERS3
-rw-r--r--chromium/components/storage_monitor/image_capture_device.h1
-rw-r--r--chromium/components/storage_monitor/mtab_watcher_linux.cc24
-rw-r--r--chromium/components/storage_monitor/mtab_watcher_linux.h27
-rw-r--r--chromium/components/storage_monitor/portable_device_watcher_win.cc25
-rw-r--r--chromium/components/storage_monitor/storage_monitor.cc2
-rw-r--r--chromium/components/storage_monitor/storage_monitor.h11
-rw-r--r--chromium/components/storage_monitor/storage_monitor_linux.cc99
-rw-r--r--chromium/components/storage_monitor/storage_monitor_linux.h36
-rw-r--r--chromium/components/storage_monitor/storage_monitor_linux_unittest.cc23
-rw-r--r--chromium/components/strings/components_chromium_strings_mr.xtb2
-rw-r--r--chromium/components/strings/components_google_chrome_strings_mr.xtb2
-rw-r--r--chromium/components/strings/components_google_chrome_strings_sk.xtb2
-rw-r--r--chromium/components/strings/components_strings_am.xtb97
-rw-r--r--chromium/components/strings/components_strings_ar.xtb103
-rw-r--r--chromium/components/strings/components_strings_bg.xtb95
-rw-r--r--chromium/components/strings/components_strings_bn.xtb161
-rw-r--r--chromium/components/strings/components_strings_ca.xtb105
-rw-r--r--chromium/components/strings/components_strings_cs.xtb101
-rw-r--r--chromium/components/strings/components_strings_da.xtb97
-rw-r--r--chromium/components/strings/components_strings_de.xtb106
-rw-r--r--chromium/components/strings/components_strings_el.xtb99
-rw-r--r--chromium/components/strings/components_strings_en-GB.xtb95
-rw-r--r--chromium/components/strings/components_strings_es-419.xtb101
-rw-r--r--chromium/components/strings/components_strings_es.xtb95
-rw-r--r--chromium/components/strings/components_strings_et.xtb95
-rw-r--r--chromium/components/strings/components_strings_fa.xtb97
-rw-r--r--chromium/components/strings/components_strings_fi.xtb95
-rw-r--r--chromium/components/strings/components_strings_fil.xtb99
-rw-r--r--chromium/components/strings/components_strings_fr.xtb95
-rw-r--r--chromium/components/strings/components_strings_gu.xtb95
-rw-r--r--chromium/components/strings/components_strings_hi.xtb95
-rw-r--r--chromium/components/strings/components_strings_hr.xtb95
-rw-r--r--chromium/components/strings/components_strings_hu.xtb97
-rw-r--r--chromium/components/strings/components_strings_id.xtb95
-rw-r--r--chromium/components/strings/components_strings_it.xtb95
-rw-r--r--chromium/components/strings/components_strings_iw.xtb99
-rw-r--r--chromium/components/strings/components_strings_ja.xtb95
-rw-r--r--chromium/components/strings/components_strings_kn.xtb96
-rw-r--r--chromium/components/strings/components_strings_ko.xtb97
-rw-r--r--chromium/components/strings/components_strings_lt.xtb97
-rw-r--r--chromium/components/strings/components_strings_lv.xtb95
-rw-r--r--chromium/components/strings/components_strings_ml.xtb97
-rw-r--r--chromium/components/strings/components_strings_mr.xtb131
-rw-r--r--chromium/components/strings/components_strings_ms.xtb99
-rw-r--r--chromium/components/strings/components_strings_nl.xtb97
-rw-r--r--chromium/components/strings/components_strings_no.xtb95
-rw-r--r--chromium/components/strings/components_strings_pl.xtb97
-rw-r--r--chromium/components/strings/components_strings_pt-BR.xtb95
-rw-r--r--chromium/components/strings/components_strings_pt-PT.xtb95
-rw-r--r--chromium/components/strings/components_strings_ro.xtb97
-rw-r--r--chromium/components/strings/components_strings_ru.xtb97
-rw-r--r--chromium/components/strings/components_strings_sk.xtb119
-rw-r--r--chromium/components/strings/components_strings_sl.xtb95
-rw-r--r--chromium/components/strings/components_strings_sr.xtb97
-rw-r--r--chromium/components/strings/components_strings_sv.xtb101
-rw-r--r--chromium/components/strings/components_strings_sw.xtb95
-rw-r--r--chromium/components/strings/components_strings_ta.xtb97
-rw-r--r--chromium/components/strings/components_strings_te.xtb95
-rw-r--r--chromium/components/strings/components_strings_th.xtb95
-rw-r--r--chromium/components/strings/components_strings_tr.xtb95
-rw-r--r--chromium/components/strings/components_strings_uk.xtb95
-rw-r--r--chromium/components/strings/components_strings_vi.xtb101
-rw-r--r--chromium/components/strings/components_strings_zh-CN.xtb95
-rw-r--r--chromium/components/strings/components_strings_zh-TW.xtb95
-rw-r--r--chromium/components/subresource_filter/DEPS1
-rw-r--r--chromium/components/subresource_filter/content/browser/BUILD.gn3
-rw-r--r--chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc25
-rw-r--r--chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h26
-rw-r--r--chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc6
-rw-r--r--chromium/components/subresource_filter/content/browser/async_document_subresource_filter.cc49
-rw-r--r--chromium/components/subresource_filter/content/browser/async_document_subresource_filter.h19
-rw-r--r--chromium/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc119
-rw-r--r--chromium/components/subresource_filter/content/browser/content_ruleset_service.cc6
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc16
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h2
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc104
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h15
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc39
-rw-r--r--chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.cc28
-rw-r--r--chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h8
-rw-r--r--chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc73
-rw-r--r--chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h7
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer.h11
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc7
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.h5
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc53
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h57
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc40
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h3
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc39
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.cc20
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h10
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.cc1
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h1
-rw-r--r--chromium/components/subresource_filter/content/common/ruleset_dealer.cc4
-rw-r--r--chromium/components/subresource_filter/content/common/ruleset_dealer.h4
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc65
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h13
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc4
-rw-r--r--chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc41
-rw-r--r--chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h37
-rw-r--r--chromium/components/subresource_filter/core/browser/BUILD.gn4
-rw-r--r--chromium/components/subresource_filter/core/browser/ruleset_service.cc15
-rw-r--r--chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc4
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_constants.cc4
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_constants.h18
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features.cc31
-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_unittest.cc34
-rw-r--r--chromium/components/subresource_filter/core/common/BUILD.gn31
-rw-r--r--chromium/components/subresource_filter/core/common/PRESUBMIT.py19
-rw-r--r--chromium/components/subresource_filter/core/common/activation_state.cc2
-rw-r--r--chromium/components/subresource_filter/core/common/closed_hash_map.h293
-rw-r--r--chromium/components/subresource_filter/core/common/closed_hash_map_unittest.cc166
-rw-r--r--chromium/components/subresource_filter/core/common/copying_file_stream.cc31
-rw-r--r--chromium/components/subresource_filter/core/common/copying_file_stream.h51
-rw-r--r--chromium/components/subresource_filter/core/common/document_subresource_filter.cc32
-rw-r--r--chromium/components/subresource_filter/core/common/document_subresource_filter.h26
-rw-r--r--chromium/components/subresource_filter/core/common/document_subresource_filter_unittest.cc115
-rw-r--r--chromium/components/subresource_filter/core/common/flat/BUILD.gn6
-rw-r--r--chromium/components/subresource_filter/core/common/flat/indexed_ruleset.fbs9
-rw-r--r--chromium/components/subresource_filter/core/common/flat/url_pattern_index.fbs96
-rw-r--r--chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.cc59
-rw-r--r--chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.h68
-rw-r--r--chromium/components/subresource_filter/core/common/fuzzy_pattern_matching_unittest.cc108
-rw-r--r--chromium/components/subresource_filter/core/common/indexed_ruleset.cc6
-rw-r--r--chromium/components/subresource_filter/core/common/indexed_ruleset.h37
-rw-r--r--chromium/components/subresource_filter/core/common/indexed_ruleset_unittest.cc37
-rw-r--r--chromium/components/subresource_filter/core/common/load_policy.h20
-rw-r--r--chromium/components/subresource_filter/core/common/ngram_extractor.h133
-rw-r--r--chromium/components/subresource_filter/core/common/ngram_extractor_unittest.cc111
-rw-r--r--chromium/components/subresource_filter/core/common/proto/BUILD.gn11
-rw-r--r--chromium/components/subresource_filter/core/common/proto/rules.proto195
-rw-r--r--chromium/components/subresource_filter/core/common/string_splitter.h101
-rw-r--r--chromium/components/subresource_filter/core/common/string_splitter_unittest.cc82
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_creator.cc8
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_creator.h12
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_utils.cc2
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_utils.h9
-rw-r--r--chromium/components/subresource_filter/core/common/uint64_hasher.h52
-rw-r--r--chromium/components/subresource_filter/core/common/unindexed_ruleset.cc71
-rw-r--r--chromium/components/subresource_filter/core/common/unindexed_ruleset.h100
-rw-r--r--chromium/components/subresource_filter/core/common/unindexed_ruleset_unittest.cc182
-rw-r--r--chromium/components/subresource_filter/core/common/url_pattern.cc256
-rw-r--r--chromium/components/subresource_filter/core/common/url_pattern.h79
-rw-r--r--chromium/components/subresource_filter/core/common/url_pattern_index.cc567
-rw-r--r--chromium/components/subresource_filter/core/common/url_pattern_index.h135
-rw-r--r--chromium/components/subresource_filter/core/common/url_pattern_index_unittest.cc707
-rw-r--r--chromium/components/subresource_filter/core/common/url_pattern_unittest.cc124
-rw-r--r--chromium/components/subresource_filter/core/common/url_rule_test_support.cc56
-rw-r--r--chromium/components/subresource_filter/core/common/url_rule_test_support.h74
-rw-r--r--chromium/components/suggestions/BUILD.gn3
-rw-r--r--chromium/components/suggestions/image_encoder.cc6
-rw-r--r--chromium/components/suggestions/image_manager.cc9
-rw-r--r--chromium/components/suggestions/image_manager.h3
-rw-r--r--chromium/components/suggestions/image_manager_unittest.cc5
-rw-r--r--chromium/components/suggestions/proto/suggestions.proto12
-rw-r--r--chromium/components/suggestions/suggestions_service_impl.cc2
-rw-r--r--chromium/components/suggestions/suggestions_service_impl_unittest.cc2
-rw-r--r--chromium/components/suggestions/webui/suggestions_source.cc197
-rw-r--r--chromium/components/suggestions/webui/suggestions_source.h75
-rw-r--r--chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html34
-rw-r--r--chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js47
-rw-r--r--chromium/components/supervised_user_error_page/supervised_user_error_page.cc8
-rw-r--r--chromium/components/supervised_user_error_page/supervised_user_error_page.h1
-rw-r--r--chromium/components/supervised_user_error_page/supervised_user_error_page_android.cc1
-rw-r--r--chromium/components/supervised_user_error_page/supervised_user_error_page_unittest.cc27
-rw-r--r--chromium/components/supervised_user_error_page_strings.grdp7
-rw-r--r--chromium/components/sync/BUILD.gn27
-rw-r--r--chromium/components/sync_bookmarks/bookmark_change_processor.cc4
-rw-r--r--chromium/components/sync_preferences/BUILD.gn1
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.cc6
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.h7
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable.cc30
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable.h4
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable_factory.cc50
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable_factory.h11
-rw-r--r--chromium/components/sync_sessions/favicon_cache.cc5
-rw-r--r--chromium/components/sync_sessions/revisit/page_revisit_broadcaster.cc2
-rw-r--r--chromium/components/sync_sessions/revisit/sessions_page_revisit_observer_unittest.cc2
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager.cc59
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager.h9
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager_unittest.cc12
-rw-r--r--chromium/components/sync_sessions/task_tracker.cc221
-rw-r--r--chromium/components/sync_sessions/task_tracker.h100
-rw-r--r--chromium/components/sync_sessions/task_tracker_unittest.cc148
-rw-r--r--chromium/components/sync_wifi/wifi_credential.cc19
-rw-r--r--chromium/components/task_scheduler_util/browser/initialization.cc20
-rw-r--r--chromium/components/task_scheduler_util/browser/initialization.h4
-rw-r--r--chromium/components/test/BUILD.gn13
-rw-r--r--chromium/components/timers/alarm_timer_chromeos.cc10
-rw-r--r--chromium/components/toolbar/BUILD.gn16
-rw-r--r--chromium/components/toolbar/DEPS2
-rw-r--r--chromium/components/toolbar/test_toolbar_model.cc4
-rw-r--r--chromium/components/toolbar/test_toolbar_model.h7
-rw-r--r--chromium/components/toolbar/toolbar_model.h4
-rw-r--r--chromium/components/toolbar/toolbar_model_delegate.h12
-rw-r--r--chromium/components/toolbar/toolbar_model_impl.cc19
-rw-r--r--chromium/components/toolbar/toolbar_model_impl.h1
-rw-r--r--chromium/components/toolbar/vector_icons/offline_pin.icon26
-rw-r--r--chromium/components/tracing/BUILD.gn4
-rw-r--r--chromium/components/tracing/common/DEPS7
-rw-r--r--chromium/components/tracing/common/graphics_memory_dump_provider_android_unittest.cc16
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider.cc752
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider.h70
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc414
-rw-r--r--chromium/components/tracing/common/trace_config_file_unittest.cc11
-rw-r--r--chromium/components/tracing/common/tracing_messages.h36
-rw-r--r--chromium/components/tracing/core/proto_zero_message.h2
-rw-r--r--chromium/components/translate/content/renderer/BUILD.gn12
-rw-r--r--chromium/components/translate/core/browser/BUILD.gn20
-rw-r--r--chromium/components/translate/core/common/BUILD.gn10
-rw-r--r--chromium/components/translate/ios/browser/BUILD.gn4
-rw-r--r--chromium/components/typemaps.gni2
-rw-r--r--chromium/components/ui_devtools/BUILD.gn2
-rw-r--r--chromium/components/ui_devtools/README.md3
-rw-r--r--chromium/components/ui_devtools/devtools_base_agent.h14
-rw-r--r--chromium/components/ui_devtools/devtools_client.cc6
-rw-r--r--chromium/components/ui_devtools/devtools_client.h9
-rw-r--r--chromium/components/ui_devtools/devtools_server.cc11
-rw-r--r--chromium/components/ui_devtools/devtools_server.h10
-rw-r--r--chromium/components/ui_devtools/inspector_protocol_config.json2
-rw-r--r--chromium/components/ui_devtools/string_util.cc6
-rw-r--r--chromium/components/ui_devtools/string_util.h10
-rw-r--r--chromium/components/ui_devtools/switches.cc6
-rw-r--r--chromium/components/ui_devtools/switches.h10
-rw-r--r--chromium/components/ui_devtools/views/BUILD.gn62
-rw-r--r--chromium/components/ui_devtools/views/DEPS4
-rw-r--r--chromium/components/ui_devtools/views/OWNERS1
-rw-r--r--chromium/components/ui_devtools/views/ui_devtools_css_agent.cc208
-rw-r--r--chromium/components/ui_devtools/views/ui_devtools_css_agent.h53
-rw-r--r--chromium/components/ui_devtools/views/ui_devtools_dom_agent.cc385
-rw-r--r--chromium/components/ui_devtools/views/ui_devtools_dom_agent.h102
-rw-r--r--chromium/components/ui_devtools/views/ui_devtools_unittest.cc876
-rw-r--r--chromium/components/ui_devtools/views/ui_element.cc78
-rw-r--r--chromium/components/ui_devtools/views/ui_element.h75
-rw-r--r--chromium/components/ui_devtools/views/ui_element_delegate.h38
-rw-r--r--chromium/components/ui_devtools/views/view_element.cc87
-rw-r--r--chromium/components/ui_devtools/views/view_element.h49
-rw-r--r--chromium/components/ui_devtools/views/widget_element.cc71
-rw-r--r--chromium/components/ui_devtools/views/widget_element.h54
-rw-r--r--chromium/components/ui_devtools/views/window_element.cc96
-rw-r--r--chromium/components/ui_devtools/views/window_element.h51
-rw-r--r--chromium/components/ui_metrics/BUILD.gn9
-rw-r--r--chromium/components/ui_metrics/sadtab_metrics_types.h28
-rw-r--r--chromium/components/ukm/BUILD.gn23
-rw-r--r--chromium/components/ukm/DEPS1
-rw-r--r--chromium/components/ukm/debug_page/debug_page.cc3
-rw-r--r--chromium/components/ukm/observers/sync_disable_observer_unittest.cc2
-rw-r--r--chromium/components/ukm/public/BUILD.gn27
-rw-r--r--chromium/components/ukm/public/interfaces/BUILD.gn15
-rw-r--r--chromium/components/ukm/public/interfaces/OWNERS2
-rw-r--r--chromium/components/ukm/public/interfaces/ukm_interface.mojom22
-rw-r--r--chromium/components/ukm/public/ukm_entry_builder.cc32
-rw-r--r--chromium/components/ukm/public/ukm_entry_builder.h52
-rw-r--r--chromium/components/ukm/public/ukm_export.h29
-rw-r--r--chromium/components/ukm/public/ukm_recorder.cc47
-rw-r--r--chromium/components/ukm/public/ukm_recorder.h107
-rw-r--r--chromium/components/ukm/test_ukm_recorder.cc194
-rw-r--r--chromium/components/ukm/test_ukm_recorder.h89
-rw-r--r--chromium/components/ukm/ukm_interface.cc54
-rw-r--r--chromium/components/ukm/ukm_interface.h35
-rw-r--r--chromium/components/ukm/ukm_recorder_impl.h4
-rw-r--r--chromium/components/ukm/ukm_service.cc3
-rw-r--r--chromium/components/ukm/ukm_service_unittest.cc14
-rw-r--r--chromium/components/update_client/BUILD.gn5
-rw-r--r--chromium/components/update_client/action_runner.cc95
-rw-r--r--chromium/components/update_client/action_runner.h70
-rw-r--r--chromium/components/update_client/action_runner_win.cc57
-rw-r--r--chromium/components/update_client/background_downloader_win.cc380
-rw-r--r--chromium/components/update_client/background_downloader_win.h47
-rw-r--r--chromium/components/update_client/component.cc138
-rw-r--r--chromium/components/update_client/component.h27
-rw-r--r--chromium/components/update_client/component_unpacker.cc1
-rw-r--r--chromium/components/update_client/component_unpacker_unittest.cc8
-rw-r--r--chromium/components/update_client/configurator.h6
-rw-r--r--chromium/components/update_client/crx_downloader.cc26
-rw-r--r--chromium/components/update_client/crx_downloader.h19
-rw-r--r--chromium/components/update_client/crx_downloader_unittest.cc27
-rw-r--r--chromium/components/update_client/ping_manager_unittest.cc21
-rw-r--r--chromium/components/update_client/protocol_builder.cc50
-rw-r--r--chromium/components/update_client/protocol_builder.h6
-rw-r--r--chromium/components/update_client/test_configurator.cc6
-rw-r--r--chromium/components/update_client/test_configurator.h7
-rw-r--r--chromium/components/update_client/test_installer.cc6
-rw-r--r--chromium/components/update_client/test_installer.h5
-rw-r--r--chromium/components/update_client/update_client.h3
-rw-r--r--chromium/components/update_client/update_client_errors.h3
-rw-r--r--chromium/components/update_client/update_client_unittest.cc428
-rw-r--r--chromium/components/update_client/url_fetcher_downloader.cc90
-rw-r--r--chromium/components/update_client/url_fetcher_downloader.h28
-rw-r--r--chromium/components/update_client/url_request_post_interceptor.cc10
-rw-r--r--chromium/components/update_client/utils.h2
-rw-r--r--chromium/components/upload_list/BUILD.gn4
-rw-r--r--chromium/components/upload_list/crash_upload_list.cc12
-rw-r--r--chromium/components/upload_list/crash_upload_list.h24
-rw-r--r--chromium/components/upload_list/text_log_upload_list.cc76
-rw-r--r--chromium/components/upload_list/text_log_upload_list.h44
-rw-r--r--chromium/components/upload_list/text_log_upload_list_unittest.cc317
-rw-r--r--chromium/components/upload_list/upload_list.cc120
-rw-r--r--chromium/components/upload_list/upload_list.h85
-rw-r--r--chromium/components/upload_list/upload_list_unittest.cc334
-rw-r--r--chromium/components/url_formatter/BUILD.gn3
-rw-r--r--chromium/components/url_formatter/elide_url.cc10
-rw-r--r--chromium/components/url_formatter/elide_url_unittest.cc27
-rw-r--r--chromium/components/url_formatter/idn_spoof_checker.cc16
-rw-r--r--chromium/components/url_formatter/top_domains/BUILD.gn1
-rw-r--r--chromium/components/url_formatter/url_formatter.cc203
-rw-r--r--chromium/components/url_formatter/url_formatter.h14
-rw-r--r--chromium/components/url_formatter/url_formatter_android.cc6
-rw-r--r--chromium/components/url_formatter/url_formatter_android.h20
-rw-r--r--chromium/components/url_formatter/url_formatter_unittest.cc1287
-rw-r--r--chromium/components/url_matcher/url_matcher_factory_unittest.cc17
-rw-r--r--chromium/components/url_pattern_index/BUILD.gn70
-rw-r--r--chromium/components/url_pattern_index/DEPS5
-rw-r--r--chromium/components/url_pattern_index/OWNERS3
-rw-r--r--chromium/components/url_pattern_index/README12
-rw-r--r--chromium/components/url_pattern_index/closed_hash_map.h294
-rw-r--r--chromium/components/url_pattern_index/closed_hash_map_unittest.cc166
-rw-r--r--chromium/components/url_pattern_index/copying_file_stream.cc31
-rw-r--r--chromium/components/url_pattern_index/copying_file_stream.h51
-rw-r--r--chromium/components/url_pattern_index/flat/BUILD.gn11
-rw-r--r--chromium/components/url_pattern_index/flat/url_pattern_index.fbs125
-rw-r--r--chromium/components/url_pattern_index/fuzzy_pattern_matching.cc59
-rw-r--r--chromium/components/url_pattern_index/fuzzy_pattern_matching.h68
-rw-r--r--chromium/components/url_pattern_index/fuzzy_pattern_matching_unittest.cc108
-rw-r--r--chromium/components/url_pattern_index/ngram_extractor.h133
-rw-r--r--chromium/components/url_pattern_index/ngram_extractor_unittest.cc111
-rw-r--r--chromium/components/url_pattern_index/proto/BUILD.gn11
-rw-r--r--chromium/components/url_pattern_index/proto/rules.proto195
-rw-r--r--chromium/components/url_pattern_index/string_splitter.h101
-rw-r--r--chromium/components/url_pattern_index/string_splitter_unittest.cc82
-rw-r--r--chromium/components/url_pattern_index/uint64_hasher.h52
-rw-r--r--chromium/components/url_pattern_index/unindexed_ruleset.cc71
-rw-r--r--chromium/components/url_pattern_index/unindexed_ruleset.h100
-rw-r--r--chromium/components/url_pattern_index/unindexed_ruleset_unittest.cc181
-rw-r--r--chromium/components/url_pattern_index/url_pattern.cc256
-rw-r--r--chromium/components/url_pattern_index/url_pattern.h79
-rw-r--r--chromium/components/url_pattern_index/url_pattern_index.cc667
-rw-r--r--chromium/components/url_pattern_index/url_pattern_index.h145
-rw-r--r--chromium/components/url_pattern_index/url_pattern_index_unittest.cc707
-rw-r--r--chromium/components/url_pattern_index/url_pattern_unittest.cc124
-rw-r--r--chromium/components/url_pattern_index/url_rule_test_support.cc56
-rw-r--r--chromium/components/url_pattern_index/url_rule_test_support.h74
-rw-r--r--chromium/components/user_manager/fake_user_manager.cc2
-rw-r--r--chromium/components/user_manager/known_user.cc32
-rw-r--r--chromium/components/user_manager/known_user.h9
-rw-r--r--chromium/components/user_manager/user.h22
-rw-r--r--chromium/components/user_manager/user_image/user_image.cc11
-rw-r--r--chromium/components/user_manager/user_manager.h3
-rw-r--r--chromium/components/user_manager/user_manager_base.cc114
-rw-r--r--chromium/components/user_manager/user_manager_base.h5
-rw-r--r--chromium/components/user_manager/user_names.cc22
-rw-r--r--chromium/components/user_manager/user_names.h8
-rw-r--r--chromium/components/variations/BUILD.gn11
-rw-r--r--chromium/components/variations/active_field_trials.cc52
-rw-r--r--chromium/components/variations/active_field_trials.h26
-rw-r--r--chromium/components/variations/active_field_trials_unittest.cc26
-rw-r--r--chromium/components/variations/android/variations_associated_data_android.cc2
-rw-r--r--chromium/components/variations/child_process_field_trial_syncer.cc8
-rw-r--r--chromium/components/variations/child_process_field_trial_syncer.h7
-rw-r--r--chromium/components/variations/child_process_field_trial_syncer_unittest.cc3
-rw-r--r--chromium/components/variations/client_filterable_state.cc35
-rw-r--r--chromium/components/variations/client_filterable_state.h60
-rw-r--r--chromium/components/variations/experiment_labels.cc5
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_util.cc21
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_util.h4
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_util_unittest.cc19
-rw-r--r--chromium/components/variations/metrics.cc28
-rw-r--r--chromium/components/variations/metrics.h95
-rw-r--r--chromium/components/variations/metrics_util.h1
-rw-r--r--chromium/components/variations/platform_field_trials.h38
-rw-r--r--chromium/components/variations/pref_names.cc20
-rw-r--r--chromium/components/variations/pref_names.h5
-rw-r--r--chromium/components/variations/proto/study.proto19
-rw-r--r--chromium/components/variations/proto/variations_seed.proto4
-rw-r--r--chromium/components/variations/service/BUILD.gn5
-rw-r--r--chromium/components/variations/service/ui_string_overrider_unittest.cc7
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.cc328
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.h123
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator_unittest.cc276
-rw-r--r--chromium/components/variations/service/variations_service.cc451
-rw-r--r--chromium/components/variations/service/variations_service.h43
-rw-r--r--chromium/components/variations/service/variations_service_client.h14
-rw-r--r--chromium/components/variations/service/variations_service_unittest.cc498
-rw-r--r--chromium/components/variations/study_filtering.cc144
-rw-r--r--chromium/components/variations/study_filtering.h61
-rw-r--r--chromium/components/variations/study_filtering_unittest.cc149
-rw-r--r--chromium/components/variations/synthetic_trial_registry.cc85
-rw-r--r--chromium/components/variations/synthetic_trial_registry.h79
-rw-r--r--chromium/components/variations/synthetic_trial_registry_unittest.cc177
-rw-r--r--chromium/components/variations/synthetic_trials_active_group_id_provider.cc42
-rw-r--r--chromium/components/variations/synthetic_trials_active_group_id_provider.h53
-rw-r--r--chromium/components/variations/variations_params_manager.cc40
-rw-r--r--chromium/components/variations/variations_params_manager.h14
-rw-r--r--chromium/components/variations/variations_request_scheduler.cc3
-rw-r--r--chromium/components/variations/variations_seed_processor.cc46
-rw-r--r--chromium/components/variations/variations_seed_processor.h18
-rw-r--r--chromium/components/variations/variations_seed_processor_unittest.cc97
-rw-r--r--chromium/components/variations/variations_seed_simulator.cc15
-rw-r--r--chromium/components/variations/variations_seed_simulator.h10
-rw-r--r--chromium/components/variations/variations_seed_store.cc290
-rw-r--r--chromium/components/variations/variations_seed_store.h63
-rw-r--r--chromium/components/variations/variations_seed_store_unittest.cc224
-rw-r--r--chromium/components/variations/variations_switches.cc9
-rw-r--r--chromium/components/variations/variations_switches.h2
-rw-r--r--chromium/components/variations/variations_util.cc5
-rw-r--r--chromium/components/vector_icons/BUILD.gn29
-rw-r--r--chromium/components/vector_icons/DEPS5
-rw-r--r--chromium/components/vector_icons/aggregate_vector_icons.py157
-rw-r--r--chromium/components/vector_icons/back_arrow.1x.icon26
-rw-r--r--chromium/components/vector_icons/back_arrow.icon27
-rw-r--r--chromium/components/vector_icons/bluetooth_connected.icon43
-rw-r--r--chromium/components/vector_icons/business.icon87
-rw-r--r--chromium/components/vector_icons/check_circle.icon13
-rw-r--r--chromium/components/vector_icons/close.1x.icon13
-rw-r--r--chromium/components/vector_icons/close.icon12
-rw-r--r--chromium/components/vector_icons/edit.icon21
-rw-r--r--chromium/components/vector_icons/error_circle.icon18
-rw-r--r--chromium/components/vector_icons/forward_arrow.1x.icon27
-rw-r--r--chromium/components/vector_icons/forward_arrow.icon27
-rw-r--r--chromium/components/vector_icons/info_outline.icon30
-rw-r--r--chromium/components/vector_icons/location_on.icon12
-rw-r--r--chromium/components/vector_icons/lock.icon33
-rw-r--r--chromium/components/vector_icons/media_router_active.icon57
-rw-r--r--chromium/components/vector_icons/media_router_error.icon63
-rw-r--r--chromium/components/vector_icons/media_router_idle.icon50
-rw-r--r--chromium/components/vector_icons/media_router_warning.icon60
-rw-r--r--chromium/components/vector_icons/microphone.icon24
-rw-r--r--chromium/components/vector_icons/midi.icon41
-rw-r--r--chromium/components/vector_icons/notifications.icon24
-rw-r--r--chromium/components/vector_icons/notifications_off.icon41
-rw-r--r--chromium/components/vector_icons/protocol_handler.icon48
-rw-r--r--chromium/components/vector_icons/screen_share.1x.icon30
-rw-r--r--chromium/components/vector_icons/search.icon27
-rw-r--r--chromium/components/vector_icons/vector_icons.gni61
-rw-r--r--chromium/components/vector_icons/videocam.icon19
-rw-r--r--chromium/components/vector_icons/warning.icon25
-rw-r--r--chromium/components/version_info/BUILD.gn16
-rw-r--r--chromium/components/version_info/channel_android.cc25
-rw-r--r--chromium/components/version_info/channel_android.h17
-rw-r--r--chromium/components/version_info/version_info.cc18
-rw-r--r--chromium/components/version_info/version_info.h6
-rw-r--r--chromium/components/version_info/version_string.cc33
-rw-r--r--chromium/components/version_info/version_string.h20
-rw-r--r--chromium/components/version_ui/resources/about_version.html70
-rw-r--r--chromium/components/version_ui/version_handler_helper.cc4
-rw-r--r--chromium/components/version_ui_strings.grdp2
-rw-r--r--chromium/components/visitedlink/renderer/visitedlink_slave.cc6
-rw-r--r--chromium/components/visitedlink/renderer/visitedlink_slave.h7
-rw-r--r--chromium/components/viz/BUILD.gn56
-rw-r--r--chromium/components/viz/DEPS4
-rw-r--r--chromium/components/viz/client/BUILD.gn25
-rw-r--r--chromium/components/viz/client/DEPS10
-rw-r--r--chromium/components/viz/client/client_layer_tree_frame_sink.cc167
-rw-r--r--chromium/components/viz/client/client_layer_tree_frame_sink.h93
-rw-r--r--chromium/components/viz/client/client_shared_bitmap_manager.cc184
-rw-r--r--chromium/components/viz/client/client_shared_bitmap_manager.h60
-rw-r--r--chromium/components/viz/client/local_surface_id_provider.cc28
-rw-r--r--chromium/components/viz/client/local_surface_id_provider.h48
-rw-r--r--chromium/components/viz/common/BUILD.gn134
-rw-r--r--chromium/components/viz/common/DEPS24
-rw-r--r--chromium/components/viz/common/README.md4
-rw-r--r--chromium/components/viz/common/display/renderer_settings.cc15
-rw-r--r--chromium/components/viz/common/display/renderer_settings.h42
-rw-r--r--chromium/components/viz/common/gl_helper.cc1237
-rw-r--r--chromium/components/viz/common/gl_helper.h383
-rw-r--r--chromium/components/viz/common/gl_helper_benchmark.cc252
-rw-r--r--chromium/components/viz/common/gl_helper_readback_support.cc172
-rw-r--r--chromium/components/viz/common/gl_helper_readback_support.h75
-rw-r--r--chromium/components/viz/common/gl_helper_scaling.cc882
-rw-r--r--chromium/components/viz/common/gl_helper_scaling.h208
-rw-r--r--chromium/components/viz/common/gl_helper_unittest.cc1425
-rw-r--r--chromium/components/viz/common/gpu/DEPS12
-rw-r--r--chromium/components/viz/common/gpu/README.md9
-rw-r--r--chromium/components/viz/common/gpu/context_cache_controller.cc175
-rw-r--r--chromium/components/viz/common/gpu/context_cache_controller.h110
-rw-r--r--chromium/components/viz/common/gpu/context_provider.cc26
-rw-r--r--chromium/components/viz/common/gpu/context_provider.h104
-rw-r--r--chromium/components/viz/common/gpu/in_process_context_provider.cc154
-rw-r--r--chromium/components/viz/common/gpu/in_process_context_provider.h83
-rw-r--r--chromium/components/viz/common/hit_test/DEPS4
-rw-r--r--chromium/components/viz/common/hit_test/aggregated_hit_test_region.h49
-rw-r--r--chromium/components/viz/common/quads/DEPS5
-rw-r--r--chromium/components/viz/common/quads/README.md8
-rw-r--r--chromium/components/viz/common/quads/resource_format.h35
-rw-r--r--chromium/components/viz/common/quads/shared_bitmap.cc82
-rw-r--r--chromium/components/viz/common/quads/shared_bitmap.h77
-rw-r--r--chromium/components/viz/common/quads/texture_mailbox.cc101
-rw-r--r--chromium/components/viz/common/quads/texture_mailbox.h123
-rw-r--r--chromium/components/viz/common/resources/DEPS6
-rw-r--r--chromium/components/viz/common/resources/README.md7
-rw-r--r--chromium/components/viz/common/resources/buffer_to_texture_target_map.cc72
-rw-r--r--chromium/components/viz/common/resources/buffer_to_texture_target_map.h37
-rw-r--r--chromium/components/viz/common/resources/buffer_to_texture_target_map_unittest.cc37
-rw-r--r--chromium/components/viz/common/resources/platform_color.h77
-rw-r--r--chromium/components/viz/common/resources/platform_color_unittest.cc42
-rw-r--r--chromium/components/viz/common/resources/resource_format_utils.cc177
-rw-r--r--chromium/components/viz/common/resources/resource_format_utils.h27
-rw-r--r--chromium/components/viz/common/resources/resource_settings.cc18
-rw-r--r--chromium/components/viz/common/resources/resource_settings.h29
-rw-r--r--chromium/components/viz/common/resources/shared_bitmap_manager.h33
-rw-r--r--chromium/components/viz/common/surfaces/DEPS4
-rw-r--r--chromium/components/viz/common/surfaces/frame_sink_id.cc19
-rw-r--r--chromium/components/viz/common/surfaces/frame_sink_id.h64
-rw-r--r--chromium/components/viz/common/surfaces/frame_sink_id_allocator.h32
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id.cc21
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id.h85
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id_allocator.cc24
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id_allocator.h34
-rw-r--r--chromium/components/viz/common/surfaces/sequence_surface_reference_factory.cc23
-rw-r--r--chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h37
-rw-r--r--chromium/components/viz/common/surfaces/surface_id.cc21
-rw-r--r--chromium/components/viz/common/surfaces/surface_id.h88
-rw-r--r--chromium/components/viz/common/surfaces/surface_info.h63
-rw-r--r--chromium/components/viz/common/surfaces/surface_reference_factory.h39
-rw-r--r--chromium/components/viz/common/surfaces/surface_reference_owner.h23
-rw-r--r--chromium/components/viz/common/surfaces/surface_sequence.h53
-rw-r--r--chromium/components/viz/common/surfaces/surface_sequence_generator.cc19
-rw-r--r--chromium/components/viz/common/surfaces/surface_sequence_generator.h41
-rw-r--r--chromium/components/viz/common/surfaces/surface_sequence_generator_unittest.cc24
-rw-r--r--chromium/components/viz/common/viz_common_export.h29
-rw-r--r--chromium/components/viz/common/yuv_readback_unittest.cc558
-rw-r--r--chromium/components/viz/display_compositor/BUILD.gn175
-rw-r--r--chromium/components/viz/display_compositor/DEPS27
-rw-r--r--chromium/components/viz/display_compositor/buffer_queue.cc303
-rw-r--r--chromium/components/viz/display_compositor/buffer_queue.h136
-rw-r--r--chromium/components/viz/display_compositor/buffer_queue_unittest.cc680
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator.h28
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.cc68
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.h41
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.h40
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.mm37
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.cc128
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.h55
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.cc39
-rw-r--r--chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.h35
-rw-r--r--chromium/components/viz/display_compositor/display_compositor_test_suite.cc38
-rw-r--r--chromium/components/viz/display_compositor/display_compositor_test_suite.h39
-rw-r--r--chromium/components/viz/display_compositor/gl_helper.cc1237
-rw-r--r--chromium/components/viz/display_compositor/gl_helper.h383
-rw-r--r--chromium/components/viz/display_compositor/gl_helper_benchmark.cc251
-rw-r--r--chromium/components/viz/display_compositor/gl_helper_readback_support.cc172
-rw-r--r--chromium/components/viz/display_compositor/gl_helper_readback_support.h76
-rw-r--r--chromium/components/viz/display_compositor/gl_helper_scaling.cc881
-rw-r--r--chromium/components/viz/display_compositor/gl_helper_scaling.h208
-rw-r--r--chromium/components/viz/display_compositor/gl_helper_unittest.cc1430
-rw-r--r--chromium/components/viz/display_compositor/host_shared_bitmap_manager.cc252
-rw-r--r--chromium/components/viz/display_compositor/host_shared_bitmap_manager.h125
-rw-r--r--chromium/components/viz/display_compositor/host_shared_bitmap_manager_unittest.cc137
-rw-r--r--chromium/components/viz/display_compositor/run_all_unittests.cc15
-rw-r--r--chromium/components/viz/display_compositor/yuv_readback_unittest.cc558
-rw-r--r--chromium/components/viz/frame_sinks/BUILD.gn35
-rw-r--r--chromium/components/viz/frame_sinks/DEPS7
-rw-r--r--chromium/components/viz/frame_sinks/display_provider.h36
-rw-r--r--chromium/components/viz/frame_sinks/frame_eviction_manager.cc179
-rw-r--r--chromium/components/viz/frame_sinks/frame_eviction_manager.h82
-rw-r--r--chromium/components/viz/frame_sinks/frame_evictor.cc56
-rw-r--r--chromium/components/viz/frame_sinks/frame_evictor.h46
-rw-r--r--chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.cc106
-rw-r--r--chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.h80
-rw-r--r--chromium/components/viz/frame_sinks/gpu_compositor_frame_sink_delegate.h30
-rw-r--r--chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc156
-rw-r--r--chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.h110
-rw-r--r--chromium/components/viz/frame_sinks/mojo_frame_sink_manager.cc149
-rw-r--r--chromium/components/viz/frame_sinks/mojo_frame_sink_manager.h118
-rw-r--r--chromium/components/viz/host/BUILD.gn62
-rw-r--r--chromium/components/viz/host/DEPS23
-rw-r--r--chromium/components/viz/host/OWNERS1
-rw-r--r--chromium/components/viz/host/frame_sink_observer.h26
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.cc150
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.h157
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager_unittests.cc142
-rw-r--r--chromium/components/viz/host/server_gpu_memory_buffer_manager.cc232
-rw-r--r--chromium/components/viz/host/server_gpu_memory_buffer_manager.h103
-rw-r--r--chromium/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc288
-rw-r--r--chromium/components/viz/host/viz_host_export.h29
-rw-r--r--chromium/components/viz/service/BUILD.gn184
-rw-r--r--chromium/components/viz/service/DEPS8
-rw-r--r--chromium/components/viz/service/display/DEPS24
-rw-r--r--chromium/components/viz/service/display/README.md12
-rw-r--r--chromium/components/viz/service/display/display.cc434
-rw-r--r--chromium/components/viz/service/display/display.h135
-rw-r--r--chromium/components/viz/service/display/display_client.h24
-rw-r--r--chromium/components/viz/service/display/display_scheduler.cc492
-rw-r--r--chromium/components/viz/service/display/display_scheduler.h136
-rw-r--r--chromium/components/viz/service/display/display_scheduler_unittest.cc734
-rw-r--r--chromium/components/viz/service/display/display_unittest.cc658
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.cc992
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.h233
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_perftest.cc187
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_pixeltest.cc319
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_unittest.cc3095
-rw-r--r--chromium/components/viz/service/display_embedder/DEPS32
-rw-r--r--chromium/components/viz/service/display_embedder/OWNERS (renamed from chromium/components/viz/display_compositor/OWNERS)0
-rw-r--r--chromium/components/viz/service/display_embedder/README.md6
-rw-r--r--chromium/components/viz/service/display_embedder/buffer_queue.cc310
-rw-r--r--chromium/components/viz/service/display_embedder/buffer_queue.h137
-rw-r--r--chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc704
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator.h28
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.cc68
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h41
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.h40
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.mm37
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.cc126
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.h55
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.cc39
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.h35
-rw-r--r--chromium/components/viz/service/display_embedder/display_output_surface.cc152
-rw-r--r--chromium/components/viz/service/display_embedder/display_output_surface.h79
-rw-r--r--chromium/components/viz/service/display_embedder/display_output_surface_ozone.cc127
-rw-r--r--chromium/components/viz/service/display_embedder/display_output_surface_ozone.h79
-rw-r--r--chromium/components/viz/service/display_embedder/display_provider.h35
-rw-r--r--chromium/components/viz/service/display_embedder/gpu_display_provider.cc109
-rw-r--r--chromium/components/viz/service/display_embedder/gpu_display_provider.h55
-rw-r--r--chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc51
-rw-r--r--chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h47
-rw-r--r--chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.cc193
-rw-r--r--chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.h72
-rw-r--r--chromium/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc169
-rw-r--r--chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc85
-rw-r--r--chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h64
-rw-r--r--chromium/components/viz/service/frame_sinks/DEPS8
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc385
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h173
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_client.h53
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h35
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc894
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc173
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h102
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc177
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_eviction_manager.cc179
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_eviction_manager.h83
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_evictor.cc56
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_evictor.h46
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_client.h27
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc353
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h196
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc529
-rw-r--r--chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc106
-rw-r--r--chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h74
-rw-r--r--chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc163
-rw-r--r--chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h108
-rw-r--r--chromium/components/viz/service/frame_sinks/primary_begin_frame_source.cc89
-rw-r--r--chromium/components/viz/service/frame_sinks/primary_begin_frame_source.h58
-rw-r--r--chromium/components/viz/service/frame_sinks/referenced_surface_tracker.cc38
-rw-r--r--chromium/components/viz/service/frame_sinks/referenced_surface_tracker.h30
-rw-r--r--chromium/components/viz/service/frame_sinks/referenced_surface_tracker_unittest.cc147
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_references_unittest.cc527
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_resource_holder.cc70
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_resource_holder.h55
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_resource_holder_client.h25
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc1605
-rw-r--r--chromium/components/viz/service/hit_test/DEPS5
-rw-r--r--chromium/components/viz/service/hit_test/OWNERS1
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator.cc213
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator.h107
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc1017
-rw-r--r--chromium/components/viz/service/viz_service_export.h29
-rw-r--r--chromium/components/viz/test/BUILD.gn36
-rw-r--r--chromium/components/viz/viz.gni57
-rw-r--r--chromium/components/viz/viz_export.h29
-rw-r--r--chromium/components/wallpaper/BUILD.gn5
-rw-r--r--chromium/components/wallpaper/wallpaper_color_calculator.cc95
-rw-r--r--chromium/components/wallpaper/wallpaper_color_calculator.h37
-rw-r--r--chromium/components/wallpaper/wallpaper_color_calculator_unittest.cc60
-rw-r--r--chromium/components/wallpaper/wallpaper_color_extraction_result.h45
-rw-r--r--chromium/components/wallpaper/wallpaper_color_profile.h25
-rw-r--r--chromium/components/wallpaper/wallpaper_info.h81
-rw-r--r--chromium/components/wallpaper/wallpaper_layout.h28
-rw-r--r--chromium/components/wallpaper/wallpaper_manager_base.cc150
-rw-r--r--chromium/components/wallpaper/wallpaper_manager_base.h69
-rw-r--r--chromium/components/wallpaper/wallpaper_resizer.cc2
-rw-r--r--chromium/components/wallpaper/wallpaper_resizer.h2
-rw-r--r--chromium/components/web_cache/renderer/web_cache_impl.cc4
-rw-r--r--chromium/components/web_cache/renderer/web_cache_impl.h7
-rw-r--r--chromium/components/web_contents_delegate_android/BUILD.gn2
-rw-r--r--chromium/components/web_contents_delegate_android/DEPS2
-rw-r--r--chromium/components/web_contents_delegate_android/color_chooser_android.cc26
-rw-r--r--chromium/components/web_contents_delegate_android/color_chooser_android.h3
-rw-r--r--chromium/components/web_contents_delegate_android/validation_message_bubble_android.cc65
-rw-r--r--chromium/components/web_contents_delegate_android/validation_message_bubble_android.h12
-rw-r--r--chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc32
-rw-r--r--chromium/components/web_contents_delegate_android/web_contents_delegate_android.h19
-rw-r--r--chromium/components/web_resource/OWNERS1
-rw-r--r--chromium/components/web_resource/web_resource_service_unittest.cc2
-rw-r--r--chromium/components/web_restrictions/BUILD.gn14
-rw-r--r--chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc1
-rw-r--r--chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.h5
-rw-r--r--chromium/components/webauth/BUILD.gn15
-rw-r--r--chromium/components/webauth/OWNERS2
-rw-r--r--chromium/components/webauth/authenticator.mojom89
-rw-r--r--chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc19
-rw-r--r--chromium/components/webcrypto/algorithms/aes_kw_unittest.cc5
-rw-r--r--chromium/components/webcrypto/algorithms/hmac_unittest.cc13
-rw-r--r--chromium/components/webcrypto/jwk.cc2
-rw-r--r--chromium/components/webdata/common/BUILD.gn3
-rw-r--r--chromium/components/webdata/common/web_data_request_manager.cc22
-rw-r--r--chromium/components/webdata/common/web_data_request_manager.h12
-rw-r--r--chromium/components/webdata/common/web_database.cc27
-rw-r--r--chromium/components/webdata/common/web_database_migration_unittest.cc283
-rw-r--r--chromium/components/wifi/network_properties.cc6
-rw-r--r--chromium/components/zoom/OWNERS1
2735 files changed, 135720 insertions, 71936 deletions
diff --git a/chromium/components/BUILD.gn b/chromium/components/BUILD.gn
index 0c176a8e273..93afa70f243 100644
--- a/chromium/components/BUILD.gn
+++ b/chromium/components/BUILD.gn
@@ -5,7 +5,6 @@
import("//build/config/chrome_build.gni")
import("//build/config/features.gni")
import("//build/config/ui.gni")
-import("//extensions/features/features.gni")
import("//printing/features/features.gni")
import("//rlz/features/features.gni")
import("//testing/test.gni")
@@ -40,10 +39,6 @@ if (is_ios) {
# no tests will run) and add a reference here. You can add more than one unit
# test target if convenient.
test("components_unittests") {
- sources = [
- "test/run_all_unittests.cc",
- ]
-
if (is_android || is_linux || is_mac || is_win) {
data = [
"test/data/",
@@ -86,6 +81,7 @@ test("components_unittests") {
"//components/download:unit_tests",
"//components/favicon/core:unit_tests",
"//components/favicon_base:unit_tests",
+ "//components/feature_engagement_tracker:unit_tests",
"//components/flags_ui:unit_tests",
"//components/gcm_driver:unit_tests",
"//components/gcm_driver/crypto:unit_tests",
@@ -96,14 +92,16 @@ test("components_unittests") {
"//components/image_fetcher/core:unit_tests",
"//components/json_schema:unit_tests",
"//components/keyed_service/core:unit_tests",
+ "//components/language/core/browser:unit_tests",
"//components/language_usage_metrics:unit_tests",
"//components/leveldb_proto:unit_tests",
"//components/login:unit_tests",
+ "//components/machine_intelligence:unit_tests",
"//components/metrics:unit_tests",
"//components/mime_util:unit_tests",
"//components/navigation_metrics:unit_tests",
"//components/net_log:unit_tests",
- "//components/network_session_configurator:unit_tests",
+ "//components/network_session_configurator/browser:unit_tests",
"//components/network_time:unit_tests",
"//components/ntp_snippets:unit_tests",
"//components/ntp_tiles:unit_tests",
@@ -117,7 +115,6 @@ test("components_unittests") {
"//components/payments/core:unit_tests",
"//components/physical_web/data_source:unit_tests",
"//components/physical_web/eddystone:unit_tests",
- "//components/precache/core:unit_tests",
"//components/prefs:unit_tests",
"//components/previews/core:unit_tests",
"//components/proxy_config:unit_tests",
@@ -141,7 +138,7 @@ test("components_unittests") {
"//components/sync_preferences:unit_tests",
"//components/sync_sessions:unit_tests",
"//components/task_scheduler_util/common:unit_tests",
- "//components/test:test_support",
+ "//components/test:run_all_unittests",
"//components/translate/core/browser:unit_tests",
"//components/translate/core/common:unit_tests",
"//components/translate/core/language_detection:unit_tests",
@@ -151,18 +148,19 @@ test("components_unittests") {
"//components/upload_list:unit_tests",
"//components/url_formatter:unit_tests",
"//components/url_matcher:unit_tests",
+ "//components/url_pattern_index:unit_tests",
"//components/variations:unit_tests",
"//components/variations/service:unit_tests",
"//components/web_resource:unit_tests",
"//components/webdata/common:unit_tests",
]
- if (enable_nacl) {
- deps += [ "//components/nacl/browser:unit_tests" ]
+ if (toolkit_views && use_aura) {
+ deps += [ "//components/ui_devtools/views:unit_tests" ]
}
- if (enable_extensions) {
- deps += [ "//components/guest_view/browser:unit_tests" ]
+ if (enable_nacl) {
+ deps += [ "//components/nacl/browser:unit_tests" ]
}
if (is_ios) {
@@ -177,6 +175,7 @@ test("components_unittests") {
"//components/autofill/content/common:unit_tests",
"//components/autofill/content/renderer:unit_tests",
"//components/cast_certificate:unit_tests",
+ "//components/cast_channel:unit_tests",
"//components/certificate_reporting:unit_tests",
"//components/certificate_transparency:unit_tests",
"//components/contextual_search:unit_tests",
@@ -200,22 +199,20 @@ test("components_unittests") {
"//components/link_header_util:unit_tests",
"//components/navigation_interception:unit_tests",
"//components/network_hints/renderer:unit_tests",
- "//components/offline_pages/content:unit_tests",
"//components/offline_pages/content/background_loader:unit_tests",
"//components/offline_pages/core:unit_tests",
"//components/offline_pages/core/background:unit_tests",
"//components/offline_pages/core/downloads:unit_tests",
+ "//components/offline_pages/core/prefetch:unit_tests",
"//components/offline_pages/core/request_header:unit_tests",
- "//components/packed_ct_ev_whitelist:unit_tests",
"//components/password_manager/content/browser:unit_tests",
"//components/payments/content:unit_tests",
"//components/payments/content/utility:unit_tests",
"//components/policy/core/browser:unit_tests",
"//components/policy/core/common:unit_tests",
- "//components/precache/content:unit_tests",
"//components/safe_browsing/common:unit_tests",
"//components/safe_browsing/password_protection:password_protection_unittest",
- "//components/safe_browsing_db:unit_tests",
+ "//components/safe_browsing/triggers:unit_tests",
"//components/safe_json:unit_tests",
"//components/security_state/content:unit_tests",
"//components/spellcheck/browser:unit_tests",
@@ -224,8 +221,8 @@ test("components_unittests") {
"//components/subresource_filter/content/common:unit_tests",
"//components/subresource_filter/content/renderer:unit_tests",
"//components/tracing:unit_tests",
+ "//components/translate/content/renderer:unit_tests",
"//components/visitedlink/test:unit_tests",
- "//components/viz/display_compositor:unit_tests",
"//components/wallpaper:unit_tests",
"//components/web_cache/browser:unit_tests",
"//components/webcrypto:unit_tests",
@@ -249,7 +246,6 @@ test("components_unittests") {
if (is_android) {
deps += [
"//components/cdm/browser:unit_tests",
- "//components/feature_engagement_tracker:unit_tests",
"//components/gcm_driver/instance_id:test_support",
"//components/gcm_driver/instance_id/android:instance_id_driver_java",
"//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
@@ -281,6 +277,10 @@ test("components_unittests") {
deps += [
"//components/cryptauth:unit_tests",
"//components/feedback:unit_tests",
+
+ # See comment in components/guest_view/browser/BUILD.gn for why
+ # guest_view is currently non-mobile.
+ "//components/guest_view/browser:unit_tests",
"//components/proximity_auth:unit_tests",
"//components/storage_monitor:unit_tests",
"//components/web_modal:unit_tests",
@@ -319,7 +319,9 @@ test("components_unittests") {
]
}
- if (safe_browsing_mode == 2) {
+ if (safe_browsing_mode == 1) {
+ deps += [ "//components/safe_browsing_db:unit_tests_desktop" ]
+ } else if (safe_browsing_mode == 2) {
deps += [ "//components/safe_browsing_db:unit_tests_mobile" ]
}
diff --git a/chromium/components/about_ui/BUILD.gn b/chromium/components/about_ui/BUILD.gn
new file mode 100644
index 00000000000..b1b59494a38
--- /dev/null
+++ b/chromium/components/about_ui/BUILD.gn
@@ -0,0 +1,21 @@
+# 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.
+
+static_library("about_ui") {
+ sources = [
+ "credit_utils.cc",
+ "credit_utils.h",
+ ]
+ deps = [
+ "//base",
+ "//components/resources:components_resources",
+ "//third_party/brotli:dec",
+ "//ui/base",
+ "//ui/resources",
+ ]
+
+ if (is_android) {
+ deps += [ "//components/about_ui/android:about_ui_jni_headers" ]
+ }
+}
diff --git a/chromium/components/about_ui/DEPS b/chromium/components/about_ui/DEPS
new file mode 100644
index 00000000000..72b42ddc984
--- /dev/null
+++ b/chromium/components/about_ui/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+components/grit/components_resources.h",
+ "+jni",
+ "+third_party/brotli",
+ "+ui/base",
+]
diff --git a/chromium/components/about_ui/android/BUILD.gn b/chromium/components/about_ui/android/BUILD.gn
new file mode 100644
index 00000000000..7e82547127e
--- /dev/null
+++ b/chromium/components/about_ui/android/BUILD.gn
@@ -0,0 +1,18 @@
+# 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.
+import("//build/config/android/rules.gni")
+
+generate_jni("about_ui_jni_headers") {
+ sources = [
+ "java/src/org/chromium/components/aboutui/CreditUtils.java",
+ ]
+ jni_package = "components/about_ui"
+}
+
+android_library("aboutui_java") {
+ java_files = [ "java/src/org/chromium/components/aboutui/CreditUtils.java" ]
+ deps = [
+ "//base:base_java",
+ ]
+}
diff --git a/chromium/components/about_ui/android/OWNERS b/chromium/components/about_ui/android/OWNERS
new file mode 100644
index 00000000000..d99c934847c
--- /dev/null
+++ b/chromium/components/about_ui/android/OWNERS
@@ -0,0 +1,2 @@
+agrieve@chromium.org
+torne@chromium.org
diff --git a/chromium/components/about_ui/credit_utils.cc b/chromium/components/about_ui/credit_utils.cc
new file mode 100644
index 00000000000..238512d62ef
--- /dev/null
+++ b/chromium/components/about_ui/credit_utils.cc
@@ -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.
+
+#include "components/about_ui/credit_utils.h"
+
+#include <stdint.h>
+
+#include "base/strings/string_piece.h"
+#include "components/grit/components_resources.h"
+#include "third_party/brotli/include/brotli/decode.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/jni_array.h"
+#include "jni/CreditUtils_jni.h"
+#endif
+
+namespace about_ui {
+
+std::string GetCredits(bool include_scripts) {
+ std::string response;
+ base::StringPiece raw_response =
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_ABOUT_UI_CREDITS_HTML);
+ const uint8_t* next_encoded_byte =
+ reinterpret_cast<const uint8_t*>(raw_response.data());
+ size_t input_size_remaining = raw_response.size();
+ BrotliDecoderState* decoder = BrotliDecoderCreateInstance(
+ nullptr /* no custom allocator */, nullptr /* no custom deallocator */,
+ nullptr /* no custom memory handle */);
+ CHECK(!!decoder);
+ while (!BrotliDecoderIsFinished(decoder)) {
+ size_t output_size_remaining = 0;
+ CHECK(BrotliDecoderDecompressStream(
+ decoder, &input_size_remaining, &next_encoded_byte,
+ &output_size_remaining, nullptr,
+ nullptr) != BROTLI_DECODER_RESULT_ERROR);
+ const uint8_t* output_buffer =
+ BrotliDecoderTakeOutput(decoder, &output_size_remaining);
+ response.insert(response.end(), output_buffer,
+ output_buffer + output_size_remaining);
+ }
+ BrotliDecoderDestroyInstance(decoder);
+ if (include_scripts) {
+ response +=
+ "\n<script src=\"chrome://resources/js/cr.js\"></script>\n"
+ "<script src=\"chrome://credits/credits.js\"></script>\n";
+ }
+ response += "</body>\n</html>";
+ return response;
+}
+
+#if defined(OS_ANDROID)
+static base::android::ScopedJavaLocalRef<jbyteArray> GetJavaWrapperCredits(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jclass>& clazz) {
+ std::string html_content = GetCredits(false);
+ const char* html_content_arr = html_content.c_str();
+ return base::android::ToJavaByteArray(
+ env, reinterpret_cast<const uint8_t*>(html_content_arr),
+ html_content.size());
+}
+
+// The RegisterNativesImpl is a static function, so has to be called somewhere.
+bool RegisterAboutUIUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+#endif
+
+} // namespace about_ui
diff --git a/chromium/components/about_ui/credit_utils.h b/chromium/components/about_ui/credit_utils.h
new file mode 100644
index 00000000000..2bc934547a8
--- /dev/null
+++ b/chromium/components/about_ui/credit_utils.h
@@ -0,0 +1,17 @@
+// 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_ABOUT_UI_CREDIT_UTILS_H_
+#define COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_
+
+#include <string>
+
+namespace about_ui {
+
+// Decode a Brotli compressed HTML license file and attach .js files.
+std::string GetCredits(bool include_scripts);
+
+} // namespace about_ui
+
+#endif // COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_
diff --git a/chromium/components/about_ui/resources/about_credits.js b/chromium/components/about_ui/resources/about_credits.js
index d01ffb68514..6d7ffc5d41f 100644
--- a/chromium/components/about_ui/resources/about_credits.js
+++ b/chromium/components/about_ui/resources/about_credits.js
@@ -2,58 +2,26 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+/* eslint-disable no-restricted-properties */
function $(id) { return document.getElementById(id); }
-
-function toggle(o) {
- var licence = o.nextSibling;
-
- while (licence.className != 'licence') {
- if (!licence) return false;
- licence = licence.nextSibling;
- }
-
- if (licence.style && licence.style.display == 'block') {
- licence.style.display = 'none';
- o.textContent = 'show license';
- } else {
- licence.style.display = 'block';
- o.textContent = 'hide license';
- }
- return false;
-}
+/* eslint-enable no-restricted-properties */
document.addEventListener('DOMContentLoaded', function() {
- var licenseEls = [].slice.call(document.getElementsByClassName('product'));
-
- licenseEls.sort(function(a, b) {
- var nameA = a.getElementsByClassName('title')[0].textContent;
- var nameB = b.getElementsByClassName('title')[0].textContent;
- if (nameA < nameB) return -1;
- if (nameA > nameB) return 1;
- return 0;
- });
-
- var parentEl = licenseEls[0].parentNode;
- parentEl.innerHTML = '';
- for (var i = 0; i < licenseEls.length; i++) {
- parentEl.appendChild(licenseEls[i]);
- }
-
- document.body.hidden = false;
-
if (cr.isChromeOS) {
var keyboardUtils = document.createElement('script');
keyboardUtils.src = 'chrome://credits/keyboard_utils.js';
document.body.appendChild(keyboardUtils);
}
- var links = document.querySelectorAll('a.show');
- for (var i = 0; i < links.length; ++i) {
- links[i].onclick = function() { return toggle(this); };
- }
-
+ $('print-link').hidden = false;
$('print-link').onclick = function() {
window.print();
return false;
};
+
+ document.addEventListener('keypress', function(e) {
+ // Make the license show/hide toggle when the Enter is pressed.
+ if (e.keyCode == 0x0d && e.target.tagName == 'LABEL')
+ e.target.previousElementSibling.click();
+ });
});
diff --git a/chromium/components/about_ui/resources/about_credits.tmpl b/chromium/components/about_ui/resources/about_credits.tmpl
index fc4fc965ac9..9cf3f9aad01 100644
--- a/chromium/components/about_ui/resources/about_credits.tmpl
+++ b/chromium/components/about_ui/resources/about_credits.tmpl
@@ -2,6 +2,7 @@
<html>
<head>
<meta charset="utf-8">
+<meta name="viewport" content="width=device-width">
<title>Credits</title>
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<style>
@@ -28,17 +29,20 @@ body {
margin: 3px;
}
.product .homepage {
+ color: blue;
float: right;
margin: 3px;
text-align: right;
}
-.product .homepage::after {
+.product .homepage::before {
content: " - ";
}
.product .show {
+ color: blue;
float: right;
margin: 3px;
text-align: right;
+ text-decoration: underline;
}
.licence {
background-color: #e8eef7;
@@ -57,15 +61,25 @@ body {
.dialog .homepage {
display: none;
}
+input + label + div {
+ display: none;
+}
+input + label::after {
+ content: "show license";
+ cursor: pointer;
+}
+input:checked + label + div {
+ display: block;
+}
+input:checked + label::after {
+ content: "hide license";
+ cursor: pointer;
+}
</style>
</head>
-<body hidden>
+<body>
<span class="page-title" style="float:left;">Credits</span>
-<a id="print-link" href="#" style="float:right;">Print</a>
+<a id="print-link" href="#" style="float:right;" hidden>Print</a>
<div style="clear:both; overflow:auto;"><!-- Chromium <3s the following projects -->
{{entries}}
</div>
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://credits/credits.js"></script>
-</body>
-</html> \ No newline at end of file
diff --git a/chromium/components/about_ui/resources/about_credits_entry.tmpl b/chromium/components/about_ui/resources/about_credits_entry.tmpl
index d1810cd9402..2db0c1bb784 100644
--- a/chromium/components/about_ui/resources/about_credits_entry.tmpl
+++ b/chromium/components/about_ui/resources/about_credits_entry.tmpl
@@ -1,9 +1,9 @@
<div class="product">
<span class="title">{{name}}</span>
-<a class="show" href="#">show license</a>
<span class="homepage"><a href="{{url}}">homepage</a></span>
+<input type="checkbox" hidden id="{{id}}">
+<label class="show" for="{{id}}" tabindex="0"></label>
<div class="licence">
<pre>{{license}}</pre>
</div>
</div>
-
diff --git a/chromium/components/app_modal/BUILD.gn b/chromium/components/app_modal/BUILD.gn
index 9e5bdb5cd85..dfb72d1c410 100644
--- a/chromium/components/app_modal/BUILD.gn
+++ b/chromium/components/app_modal/BUILD.gn
@@ -6,8 +6,6 @@ import("//build/config/ui.gni")
static_library("app_modal") {
sources = [
- "app_modal_dialog.cc",
- "app_modal_dialog.h",
"app_modal_dialog_queue.cc",
"app_modal_dialog_queue.h",
"javascript_app_modal_dialog.cc",
@@ -41,9 +39,6 @@ static_library("app_modal") {
"views/javascript_app_modal_dialog_views.h",
]
- deps += [
- "//components/constrained_window",
- "//ui/views",
- ]
+ deps += [ "//ui/views" ]
}
}
diff --git a/chromium/components/app_modal/app_modal_dialog.cc b/chromium/components/app_modal/app_modal_dialog.cc
deleted file mode 100644
index 8689b7fc03a..00000000000
--- a/chromium/components/app_modal/app_modal_dialog.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/app_modal/app_modal_dialog.h"
-
-#include "base/logging.h"
-#include "base/run_loop.h"
-#include "components/app_modal/app_modal_dialog_queue.h"
-#include "components/app_modal/native_app_modal_dialog.h"
-
-using content::WebContents;
-
-namespace app_modal {
-namespace {
-
-AppModalDialogObserver* app_modal_dialog_observer = NULL;
-
-} // namespace
-
-AppModalDialogObserver::AppModalDialogObserver() {
- DCHECK(!app_modal_dialog_observer);
- app_modal_dialog_observer = this;
-}
-
-AppModalDialogObserver::~AppModalDialogObserver() {
- DCHECK(app_modal_dialog_observer);
- app_modal_dialog_observer = NULL;
-}
-
-AppModalDialog::AppModalDialog(WebContents* web_contents,
- const base::string16& title)
- : title_(title),
- completed_(false),
- valid_(true),
- native_dialog_(NULL),
- web_contents_(web_contents) {
-}
-
-AppModalDialog::~AppModalDialog() {
- CompleteDialog();
-}
-
-void AppModalDialog::ShowModalDialog() {
- native_dialog_ = CreateNativeDialog();
- native_dialog_->ShowAppModalDialog();
- if (app_modal_dialog_observer)
- app_modal_dialog_observer->Notify(this);
-}
-
-bool AppModalDialog::IsValid() {
- return valid_;
-}
-
-void AppModalDialog::Invalidate() {
- valid_ = false;
-}
-
-bool AppModalDialog::IsJavaScriptModalDialog() {
- return false;
-}
-
-void AppModalDialog::ActivateModalDialog() {
- DCHECK(native_dialog_);
- native_dialog_->ActivateAppModalDialog();
-}
-
-void AppModalDialog::CloseModalDialog() {
- DCHECK(native_dialog_);
- native_dialog_->CloseAppModalDialog();
-}
-
-void AppModalDialog::CompleteDialog() {
- if (!completed_) {
- completed_ = true;
- AppModalDialogQueue::GetInstance()->ShowNextDialog();
- }
-}
-
-} // namespace app_modal
diff --git a/chromium/components/app_modal/app_modal_dialog.h b/chromium/components/app_modal/app_modal_dialog.h
deleted file mode 100644
index c4923eaf88a..00000000000
--- a/chromium/components/app_modal/app_modal_dialog.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_APP_MODAL_APP_MODAL_DIALOG_H_
-#define COMPONENTS_APP_MODAL_APP_MODAL_DIALOG_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "build/build_config.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace app_modal {
-
-class NativeAppModalDialog;
-
-// A controller+model base class for modal dialogs.
-class AppModalDialog {
- public:
- // A union of data necessary to determine the type of message box to
- // show.
- AppModalDialog(content::WebContents* web_contents,
- const base::string16& title);
- virtual ~AppModalDialog();
-
- // Called by the AppModalDialogQueue to show this dialog.
- void ShowModalDialog();
-
- // Called by the AppModalDialogQueue to activate the dialog.
- void ActivateModalDialog();
-
- // Closes the dialog if it is showing.
- void CloseModalDialog();
-
- // Completes dialog handling, shows next modal dialog from the queue.
- // TODO(beng): Get rid of this method.
- void CompleteDialog();
-
- base::string16 title() const { return title_; }
- NativeAppModalDialog* native_dialog() const { return native_dialog_; }
- content::WebContents* web_contents() const { return web_contents_; }
-
- // Returns true if the dialog is still valid. As dialogs are created they are
- // added to the AppModalDialogQueue. When the current modal dialog finishes
- // and it's time to show the next dialog in the queue IsValid is invoked.
- // If IsValid returns false the dialog is deleted and not shown.
- bool IsValid();
-
- // Methods overridable by AppModalDialog subclasses:
-
- // Invalidates the dialog, therefore causing it to not be shown when its turn
- // to be shown comes around.
- virtual void Invalidate();
-
- // Used only for testing. Returns whether the dialog is a JavaScript modal
- // dialog.
- virtual bool IsJavaScriptModalDialog();
-
- protected:
- // Overridden by subclasses to create the feature-specific native dialog box.
- virtual NativeAppModalDialog* CreateNativeDialog() = 0;
-
- private:
- // Information about the message box is held in the following variables.
- base::string16 title_;
-
- // True if CompleteDialog was called.
- bool completed_;
-
- // False if the dialog should no longer be shown, e.g. because the underlying
- // tab navigated away while the dialog was queued.
- bool valid_;
-
- // The toolkit-specific implementation of the app modal dialog box.
- NativeAppModalDialog* native_dialog_;
-
- content::WebContents* web_contents_;
-
- DISALLOW_COPY_AND_ASSIGN(AppModalDialog);
-};
-
-// An interface to observe that a modal dialog is shown.
-class AppModalDialogObserver {
- public:
- AppModalDialogObserver();
- virtual ~AppModalDialogObserver();
-
- // Called when the modal dialog is shown.
- virtual void Notify(AppModalDialog* dialog) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AppModalDialogObserver);
-};
-
-} // namespace app_modal
-
-#endif // COMPONENTS_APP_MODAL_APP_MODAL_DIALOG_H_
diff --git a/chromium/components/app_modal/app_modal_dialog_queue.cc b/chromium/components/app_modal/app_modal_dialog_queue.cc
index ab041eb84d1..ce1fc2bc943 100644
--- a/chromium/components/app_modal/app_modal_dialog_queue.cc
+++ b/chromium/components/app_modal/app_modal_dialog_queue.cc
@@ -5,7 +5,7 @@
#include "components/app_modal/app_modal_dialog_queue.h"
#include "base/memory/singleton.h"
-#include "components/app_modal/app_modal_dialog.h"
+#include "components/app_modal/javascript_app_modal_dialog.h"
namespace app_modal {
@@ -14,7 +14,7 @@ AppModalDialogQueue* AppModalDialogQueue::GetInstance() {
return base::Singleton<AppModalDialogQueue>::get();
}
-void AppModalDialogQueue::AddDialog(AppModalDialog* dialog) {
+void AppModalDialogQueue::AddDialog(JavaScriptAppModalDialog* dialog) {
if (!active_dialog_) {
ShowModalDialog(dialog);
return;
@@ -23,11 +23,11 @@ void AppModalDialogQueue::AddDialog(AppModalDialog* dialog) {
}
void AppModalDialogQueue::ShowNextDialog() {
- AppModalDialog* dialog = GetNextDialog();
+ JavaScriptAppModalDialog* dialog = GetNextDialog();
if (dialog)
ShowModalDialog(dialog);
else
- active_dialog_ = NULL;
+ active_dialog_ = nullptr;
}
void AppModalDialogQueue::ActivateModalDialog() {
@@ -43,7 +43,7 @@ void AppModalDialogQueue::ActivateModalDialog() {
}
bool AppModalDialogQueue::HasActiveDialog() const {
- return active_dialog_ != NULL;
+ return active_dialog_ != nullptr;
}
AppModalDialogQueue::AppModalDialogQueue()
@@ -54,11 +54,11 @@ AppModalDialogQueue::AppModalDialogQueue()
AppModalDialogQueue::~AppModalDialogQueue() {
}
-void AppModalDialogQueue::ShowModalDialog(AppModalDialog* dialog) {
+void AppModalDialogQueue::ShowModalDialog(JavaScriptAppModalDialog* dialog) {
// Be sure and set the active_dialog_ field first, otherwise if
// ShowModalDialog triggers a call back to the queue they'll get the old
// dialog. Also, if the dialog calls |ShowNextDialog()| before returning, that
- // would write NULL into |active_dialog_| and this function would then undo
+ // would write nullptr into |active_dialog_| and this function would then undo
// that.
active_dialog_ = dialog;
showing_modal_dialog_ = true;
@@ -66,15 +66,15 @@ void AppModalDialogQueue::ShowModalDialog(AppModalDialog* dialog) {
showing_modal_dialog_ = false;
}
-AppModalDialog* AppModalDialogQueue::GetNextDialog() {
+JavaScriptAppModalDialog* AppModalDialogQueue::GetNextDialog() {
while (!app_modal_dialog_queue_.empty()) {
- AppModalDialog* dialog = app_modal_dialog_queue_.front();
+ JavaScriptAppModalDialog* dialog = app_modal_dialog_queue_.front();
app_modal_dialog_queue_.pop_front();
if (dialog->IsValid())
return dialog;
delete dialog;
}
- return NULL;
+ return nullptr;
}
} // namespace app_modal
diff --git a/chromium/components/app_modal/app_modal_dialog_queue.h b/chromium/components/app_modal/app_modal_dialog_queue.h
index 96a582e12e0..3b945b6ba85 100644
--- a/chromium/components/app_modal/app_modal_dialog_queue.h
+++ b/chromium/components/app_modal/app_modal_dialog_queue.h
@@ -15,14 +15,14 @@ template <typename T> struct DefaultSingletonTraits;
namespace app_modal {
-class AppModalDialog;
+class JavaScriptAppModalDialog;
-// Keeps a queue of AppModalDialogs, making sure only one app modal
+// Keeps a queue of JavaScriptAppModalDialogs, making sure only one app modal
// dialog is shown at a time.
// This class is a singleton.
class AppModalDialogQueue {
public:
- typedef std::deque<AppModalDialog*>::iterator iterator;
+ typedef std::deque<JavaScriptAppModalDialog*>::iterator iterator;
// Returns the singleton instance.
static AppModalDialogQueue* GetInstance();
@@ -32,9 +32,9 @@ class AppModalDialogQueue {
// most recently active browser window (or whichever is currently active)
// will be app modal, meaning it will be activated if the user tries to
// activate any other browser windows.
- // Note: The AppModalDialog |dialog| must be window modal before it
+ // Note: The JavaScriptAppModalDialog |dialog| must be window modal before it
// can be added as app modal.
- void AddDialog(AppModalDialog* dialog);
+ void AddDialog(JavaScriptAppModalDialog* dialog);
// Removes the current dialog in the queue (the one that is being shown).
// Shows the next dialog in the queue, if any is present. This does not
@@ -53,7 +53,7 @@ class AppModalDialogQueue {
// Returns true if there is currently an active app modal dialog box.
bool HasActiveDialog() const;
- AppModalDialog* active_dialog() { return active_dialog_; }
+ JavaScriptAppModalDialog* active_dialog() { return active_dialog_; }
// Iterators to walk the queue. The queue does not include the currently
// active app modal dialog box.
@@ -67,21 +67,21 @@ class AppModalDialogQueue {
~AppModalDialogQueue();
// Shows |dialog| and notifies the BrowserList that a modal dialog is showing.
- void ShowModalDialog(AppModalDialog* dialog);
+ void ShowModalDialog(JavaScriptAppModalDialog* dialog);
// Returns the next dialog to show. This removes entries from
// app_modal_dialog_queue_ until one is valid or the queue is empty. This
- // returns NULL if there are no more dialogs, or all the dialogs in the queue
- // are not valid.
- AppModalDialog* GetNextDialog();
+ // returns nullptr if there are no more dialogs, or all the dialogs in the
+ // queue are not valid.
+ JavaScriptAppModalDialog* GetNextDialog();
// Contains all app modal dialogs which are waiting to be shown. The currently
// active modal dialog is not included.
- std::deque<AppModalDialog*> app_modal_dialog_queue_;
+ std::deque<JavaScriptAppModalDialog*> app_modal_dialog_queue_;
- // The currently active app-modal dialog box's delegate. NULL if there is no
- // active app-modal dialog box.
- AppModalDialog* active_dialog_;
+ // The currently active app-modal dialog box. nullptr if there is no active
+ // app-modal dialog box.
+ JavaScriptAppModalDialog* active_dialog_;
// Stores if |ShowModalDialog()| is currently being called on an app-modal
// dialog.
diff --git a/chromium/components/app_modal/javascript_app_modal_dialog.cc b/chromium/components/app_modal/javascript_app_modal_dialog.cc
index dd5808078b0..2e2ec520717 100644
--- a/chromium/components/app_modal/javascript_app_modal_dialog.cc
+++ b/chromium/components/app_modal/javascript_app_modal_dialog.cc
@@ -7,13 +7,17 @@
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "components/app_modal/app_modal_dialog_queue.h"
#include "components/app_modal/javascript_dialog_manager.h"
#include "components/app_modal/javascript_native_dialog_factory.h"
+#include "components/app_modal/native_app_modal_dialog.h"
#include "ui/gfx/text_elider.h"
namespace app_modal {
namespace {
+AppModalDialogObserver* app_modal_dialog_observer = nullptr;
+
// Control maximum sizes of various texts passed to us from javascript.
#if defined(OS_POSIX) && !defined(OS_MACOSX)
// Two-dimensional eliding. Reformat the text of the message dialog
@@ -66,7 +70,11 @@ JavaScriptAppModalDialog::JavaScriptAppModalDialog(
bool is_before_unload_dialog,
bool is_reload,
const content::JavaScriptDialogManager::DialogClosedCallback& callback)
- : AppModalDialog(web_contents, title),
+ : title_(title),
+ completed_(false),
+ valid_(true),
+ native_dialog_(nullptr),
+ web_contents_(web_contents),
extra_data_map_(extra_data_map),
javascript_dialog_type_(javascript_dialog_type),
display_suppress_checkbox_(display_suppress_checkbox),
@@ -80,25 +88,46 @@ JavaScriptAppModalDialog::JavaScriptAppModalDialog(
}
JavaScriptAppModalDialog::~JavaScriptAppModalDialog() {
+ CompleteDialog();
+}
+
+void JavaScriptAppModalDialog::ShowModalDialog() {
+ native_dialog_ = JavaScriptDialogManager::GetInstance()
+ ->native_dialog_factory()
+ ->CreateNativeJavaScriptDialog(this);
+ native_dialog_->ShowAppModalDialog();
+ if (app_modal_dialog_observer)
+ app_modal_dialog_observer->Notify(this);
}
-NativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() {
- return JavaScriptDialogManager::GetInstance()
- ->native_dialog_factory()
- ->CreateNativeJavaScriptDialog(this);
+void JavaScriptAppModalDialog::ActivateModalDialog() {
+ DCHECK(native_dialog_);
+ native_dialog_->ActivateAppModalDialog();
}
-bool JavaScriptAppModalDialog::IsJavaScriptModalDialog() {
- return true;
+void JavaScriptAppModalDialog::CloseModalDialog() {
+ DCHECK(native_dialog_);
+ native_dialog_->CloseAppModalDialog();
+}
+
+void JavaScriptAppModalDialog::CompleteDialog() {
+ if (!completed_) {
+ completed_ = true;
+ AppModalDialogQueue::GetInstance()->ShowNextDialog();
+ }
+}
+
+bool JavaScriptAppModalDialog::IsValid() {
+ return valid_;
}
void JavaScriptAppModalDialog::Invalidate() {
- if (!IsValid())
+ if (!valid_)
return;
- AppModalDialog::Invalidate();
+ valid_ = false;
CallDialogClosedCallback(false, base::string16());
- if (native_dialog())
+ if (native_dialog_)
CloseModalDialog();
}
@@ -138,7 +167,7 @@ void JavaScriptAppModalDialog::SetOverridePromptText(
void JavaScriptAppModalDialog::NotifyDelegate(bool success,
const base::string16& user_input,
bool suppress_js_messages) {
- if (!IsValid())
+ if (!valid_)
return;
CallDialogClosedCallback(success, user_input);
@@ -146,7 +175,7 @@ void JavaScriptAppModalDialog::NotifyDelegate(bool success,
// The close callback above may delete web_contents_, thus removing the extra
// data from the map owned by ::JavaScriptDialogManager. Make sure
// to only use the data if still present. http://crbug.com/236476
- ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents());
+ ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents_);
if (extra_data != extra_data_map_->end()) {
extra_data->second.has_already_shown_a_dialog_ = true;
extra_data->second.suppress_javascript_messages_ = suppress_js_messages;
@@ -154,14 +183,14 @@ void JavaScriptAppModalDialog::NotifyDelegate(bool success,
// On Views, we can end up coming through this code path twice :(.
// See crbug.com/63732.
- AppModalDialog::Invalidate();
+ valid_ = false;
}
void JavaScriptAppModalDialog::CallDialogClosedCallback(bool success,
const base::string16& user_input) {
// TODO(joenotcharles): Both the callers of this function also check IsValid
- // and call AppModalDialog::Invalidate, but in different orders. If the
- // difference is not significant, more common code could be moved here.
+ // and call Invalidate, but in different orders. If the difference is not
+ // significant, more common code could be moved here.
UMA_HISTOGRAM_MEDIUM_TIMES(
"JSDialogs.FineTiming.TimeBetweenDialogCreatedAndSameDialogClosed",
base::TimeTicks::Now() - creation_time_);
@@ -171,4 +200,14 @@ void JavaScriptAppModalDialog::CallDialogClosedCallback(bool success,
}
}
+AppModalDialogObserver::AppModalDialogObserver() {
+ DCHECK(!app_modal_dialog_observer);
+ app_modal_dialog_observer = this;
+}
+
+AppModalDialogObserver::~AppModalDialogObserver() {
+ DCHECK(app_modal_dialog_observer);
+ app_modal_dialog_observer = nullptr;
+}
+
} // namespace app_modal
diff --git a/chromium/components/app_modal/javascript_app_modal_dialog.h b/chromium/components/app_modal/javascript_app_modal_dialog.h
index a87febde9fe..06269e142c3 100644
--- a/chromium/components/app_modal/javascript_app_modal_dialog.h
+++ b/chromium/components/app_modal/javascript_app_modal_dialog.h
@@ -10,11 +10,12 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/time/time.h"
-#include "components/app_modal/app_modal_dialog.h"
#include "content/public/browser/javascript_dialog_manager.h"
namespace app_modal {
+class NativeAppModalDialog;
+
// Extra data for JavaScript dialogs to add Chrome-only features.
class ChromeJavaScriptDialogExtraData {
public:
@@ -32,7 +33,7 @@ class ChromeJavaScriptDialogExtraData {
// A controller + model class for JavaScript alert, confirm, prompt, and
// onbeforeunload dialog boxes.
-class JavaScriptAppModalDialog : public AppModalDialog {
+class JavaScriptAppModalDialog {
public:
typedef std::map<void*, ChromeJavaScriptDialogExtraData> ExtraDataMap;
@@ -47,12 +48,26 @@ class JavaScriptAppModalDialog : public AppModalDialog {
bool is_before_unload_dialog,
bool is_reload,
const content::JavaScriptDialogManager::DialogClosedCallback& callback);
- ~JavaScriptAppModalDialog() override;
+ ~JavaScriptAppModalDialog();
+
+ // Called by the AppModalDialogQueue to show this dialog.
+ void ShowModalDialog();
+
+ // Called by the AppModalDialogQueue to activate the dialog.
+ void ActivateModalDialog();
+
+ // Closes the dialog if it is showing.
+ void CloseModalDialog();
+
+ // Returns true if the dialog is still valid. As dialogs are created they are
+ // added to the AppModalDialogQueue. When the current modal dialog finishes
+ // and it's time to show the next dialog in the queue IsValid is invoked.
+ // If IsValid returns false the dialog is deleted and not shown.
+ bool IsValid();
- // Overridden from AppModalDialog:
- NativeAppModalDialog* CreateNativeDialog() override;
- bool IsJavaScriptModalDialog() override;
- void Invalidate() override;
+ // Invalidates the dialog, therefore causing it to not be shown when its turn
+ // to be shown comes around.
+ void Invalidate();
// Callbacks from NativeDialog when the user accepts or cancels the dialog.
void OnCancel(bool suppress_js_messages);
@@ -66,7 +81,10 @@ class JavaScriptAppModalDialog : public AppModalDialog {
// its delegate instead of whatever the UI reports.
void SetOverridePromptText(const base::string16& prompt_text);
- // Accessors
+ // Accessors.
+ base::string16 title() const { return title_; }
+ NativeAppModalDialog* native_dialog() const { return native_dialog_; }
+ content::WebContents* web_contents() const { return web_contents_; }
content::JavaScriptDialogType javascript_dialog_type() const {
return javascript_dialog_type_;
}
@@ -84,6 +102,26 @@ class JavaScriptAppModalDialog : public AppModalDialog {
void CallDialogClosedCallback(bool success,
const base::string16& prompt_text);
+ // Completes dialog handling, shows next modal dialog from the queue.
+ // TODO(beng): Get rid of this method.
+ void CompleteDialog();
+
+ // The title of the dialog.
+ base::string16 title_;
+
+ // // True if CompleteDialog was called.
+ bool completed_;
+
+ // False if the dialog should no longer be shown, e.g. because the underlying
+ // tab navigated away while the dialog was queued.
+ bool valid_;
+
+ // // The toolkit-specific implementation of the app modal dialog box.
+ NativeAppModalDialog* native_dialog_;
+
+ // The WebContents that opened this dialog.
+ content::WebContents* web_contents_;
+
// A map of extra Chrome-only data associated with the delegate_. Can be
// inspected via |extra_data_map_[web_contents_]|.
ExtraDataMap* extra_data_map_;
@@ -108,6 +146,19 @@ class JavaScriptAppModalDialog : public AppModalDialog {
DISALLOW_COPY_AND_ASSIGN(JavaScriptAppModalDialog);
};
+// An interface to observe that a modal dialog is shown.
+class AppModalDialogObserver {
+ public:
+ AppModalDialogObserver();
+ virtual ~AppModalDialogObserver();
+
+ // Called when the modal dialog is shown.
+ virtual void Notify(JavaScriptAppModalDialog* dialog) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AppModalDialogObserver);
+};
+
} // namespace app_modal
#endif // COMPONENTS_APP_MODAL_JAVASCRIPT_APP_MODAL_DIALOG_H_
diff --git a/chromium/components/app_modal/javascript_dialog_manager.cc b/chromium/components/app_modal/javascript_dialog_manager.cc
index c5a368384b5..de3674c309b 100644
--- a/chromium/components/app_modal/javascript_dialog_manager.cc
+++ b/chromium/components/app_modal/javascript_dialog_manager.cc
@@ -12,7 +12,6 @@
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
-#include "components/app_modal/app_modal_dialog.h"
#include "components/app_modal/app_modal_dialog_queue.h"
#include "components/app_modal/javascript_dialog_extensions_client.h"
#include "components/app_modal/javascript_native_dialog_factory.h"
@@ -244,7 +243,6 @@ bool JavaScriptDialogManager::HandleJavaScriptDialog(
const base::string16* prompt_override) {
AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
if (!dialog_queue->HasActiveDialog() ||
- !dialog_queue->active_dialog()->IsJavaScriptModalDialog() ||
dialog_queue->active_dialog()->web_contents() != web_contents) {
return false;
}
@@ -272,15 +270,14 @@ bool JavaScriptDialogManager::HandleJavaScriptDialog(
void JavaScriptDialogManager::CancelDialogs(content::WebContents* web_contents,
bool reset_state) {
AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance();
- AppModalDialog* active_dialog = queue->active_dialog();
- for (AppModalDialogQueue::iterator i = queue->begin();
- i != queue->end(); ++i) {
+ JavaScriptAppModalDialog* active_dialog = queue->active_dialog();
+ for (auto* dialog : *queue) {
// Invalidating the active dialog might trigger showing a not-yet
// invalidated dialog, so invalidate the active dialog last.
- if ((*i) == active_dialog)
+ if (dialog == active_dialog)
continue;
- if ((*i)->web_contents() == web_contents)
- (*i)->Invalidate();
+ if (dialog->web_contents() == web_contents)
+ dialog->Invalidate();
}
if (active_dialog && active_dialog->web_contents() == web_contents)
active_dialog->Invalidate();
diff --git a/chromium/components/app_modal/views/DEPS b/chromium/components/app_modal/views/DEPS
index 9783b361145..df19c5c0f7c 100644
--- a/chromium/components/app_modal/views/DEPS
+++ b/chromium/components/app_modal/views/DEPS
@@ -1,5 +1,4 @@
include_rules = [
- "+components/constrained_window",
"+ui/events/keycodes",
"+ui/views",
]
diff --git a/chromium/components/app_modal/views/javascript_app_modal_dialog_views.cc b/chromium/components/app_modal/views/javascript_app_modal_dialog_views.cc
index 3ba54fe0b6d..96e0e33a653 100644
--- a/chromium/components/app_modal/views/javascript_app_modal_dialog_views.cc
+++ b/chromium/components/app_modal/views/javascript_app_modal_dialog_views.cc
@@ -6,7 +6,6 @@
#include "base/strings/utf_string_conversions.h"
#include "components/app_modal/javascript_app_modal_dialog.h"
-#include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/keycodes/keyboard_codes.h"
@@ -146,6 +145,10 @@ views::View* JavaScriptAppModalDialogViews::GetInitiallyFocusedView() {
return views::DialogDelegate::GetInitiallyFocusedView();
}
+bool JavaScriptAppModalDialogViews::ShouldShowCloseButton() const {
+ return false;
+}
+
void JavaScriptAppModalDialogViews::WindowClosing() {
parent_->OnClose();
}
diff --git a/chromium/components/app_modal/views/javascript_app_modal_dialog_views.h b/chromium/components/app_modal/views/javascript_app_modal_dialog_views.h
index d1195fd4547..9fd9c152e71 100644
--- a/chromium/components/app_modal/views/javascript_app_modal_dialog_views.h
+++ b/chromium/components/app_modal/views/javascript_app_modal_dialog_views.h
@@ -47,6 +47,7 @@ class JavaScriptAppModalDialogViews : public NativeAppModalDialog,
ui::ModalType GetModalType() const override;
views::View* GetContentsView() override;
views::View* GetInitiallyFocusedView() override;
+ bool ShouldShowCloseButton() const override;
void WindowClosing() override;
views::Widget* GetWidget() override;
const views::Widget* GetWidget() const override;
diff --git a/chromium/components/arc/BUILD.gn b/chromium/components/arc/BUILD.gn
index c9443f48a14..d5454b56e53 100644
--- a/chromium/components/arc/BUILD.gn
+++ b/chromium/components/arc/BUILD.gn
@@ -7,14 +7,10 @@ import("//testing/test.gni")
static_library("arc") {
sources = [
- "arc_service_manager.cc",
- "arc_service_manager.h",
"arc_util.cc",
"arc_util.h",
"audio/arc_audio_bridge.cc",
"audio/arc_audio_bridge.h",
- "bluetooth/arc_bluetooth_bridge.cc",
- "bluetooth/arc_bluetooth_bridge.h",
"bluetooth/bluetooth_struct_traits.cc",
"bluetooth/bluetooth_struct_traits.h",
"bluetooth/bluetooth_type_converters.cc",
@@ -43,12 +39,10 @@ static_library("arc") {
"intent_helper/intent_filter.h",
"intent_helper/link_handler_model_impl.cc",
"intent_helper/link_handler_model_impl.h",
- "intent_helper/local_activity_resolver.cc",
- "intent_helper/local_activity_resolver.h",
"intent_helper/page_transition_util.cc",
"intent_helper/page_transition_util.h",
- "kiosk/arc_kiosk_bridge.cc",
- "kiosk/arc_kiosk_bridge.h",
+ "lock_screen/arc_lock_screen_bridge.cc",
+ "lock_screen/arc_lock_screen_bridge.h",
"metrics/arc_metrics_service.cc",
"metrics/arc_metrics_service.h",
"net/arc_net_host_impl.cc",
@@ -61,6 +55,8 @@ static_library("arc") {
"storage_manager/arc_storage_manager.h",
"video_accelerator/video_accelerator_struct_traits.cc",
"video_accelerator/video_accelerator_struct_traits.h",
+ "volume_mounter/arc_volume_mounter_bridge.cc",
+ "volume_mounter/arc_volume_mounter_bridge.h",
]
public_deps = [
@@ -70,14 +66,15 @@ static_library("arc") {
deps = [
"//ash:ash",
"//ash/public/cpp:ash_public_cpp",
- "//ash/shared:app_types",
"//base",
"//chromeos",
+ "//chromeos:login_manager_proto",
"//chromeos:power_manager_proto",
"//components/exo",
"//components/google/core/browser",
"//components/onc",
"//components/prefs",
+ "//components/session_manager/core",
"//components/signin/core/account_id",
"//components/user_manager",
"//device/bluetooth",
@@ -104,10 +101,13 @@ static_library("arc_base") {
"arc_bridge_host_impl.h",
"arc_bridge_service.cc",
"arc_bridge_service.h",
+ "arc_browser_context_keyed_service_factory_base.h",
"arc_features.cc",
"arc_features.h",
"arc_service.cc",
"arc_service.h",
+ "arc_service_manager.cc",
+ "arc_service_manager.h",
"arc_session.cc",
"arc_session.h",
"arc_session_runner.cc",
@@ -120,6 +120,8 @@ static_library("arc_base") {
deps = [
"//base",
"//chromeos",
+ "//components/keyed_service/content",
+ "//components/signin/core/account_id",
"//components/user_manager",
"//mojo/edk/system",
]
@@ -146,6 +148,7 @@ mojom("arc_bindings") {
"common/ime.mojom",
"common/intent_helper.mojom",
"common/kiosk.mojom",
+ "common/lock_screen.mojom",
"common/metrics.mojom",
"common/net.mojom",
"common/notifications.mojom",
@@ -160,9 +163,12 @@ mojom("arc_bindings") {
"common/tracing.mojom",
"common/tts.mojom",
"common/video.mojom",
- "common/video_accelerator.mojom",
+ "common/video_common.mojom",
+ "common/video_decode_accelerator.mojom",
+ "common/video_encode_accelerator.mojom",
"common/voice_interaction_arc_home.mojom",
"common/voice_interaction_framework.mojom",
+ "common/volume_mounter.mojom",
"common/wallpaper.mojom",
]
@@ -211,7 +217,6 @@ source_set("unit_tests") {
"arc_service_manager_unittest.cc",
"arc_session_runner_unittest.cc",
"arc_util_unittest.cc",
- "bluetooth/arc_bluetooth_bridge_unittest.cc",
"bluetooth/bluetooth_struct_traits_unittest.cc",
"bluetooth/bluetooth_type_converters_unittest.cc",
"ime/arc_ime_service_unittest.cc",
@@ -220,17 +225,16 @@ source_set("unit_tests") {
"intent_helper/font_size_util_unittest.cc",
"intent_helper/intent_filter_unittest.cc",
"intent_helper/link_handler_model_impl_unittest.cc",
- "intent_helper/local_activity_resolver_unittest.cc",
"intent_helper/page_transition_util_unittest.cc",
- "kiosk/arc_kiosk_bridge_unittest.cc",
]
deps = [
":arc_test_support",
- "//ash/shared:app_types",
+ "//ash/public/cpp:ash_public_cpp",
"//base",
"//base/test:test_support",
"//chromeos",
+ "//chromeos:test_support_without_gmock",
"//components/signin/core/account_id",
"//components/user_manager",
"//components/user_manager:test_support",
diff --git a/chromium/components/arc/DEPS b/chromium/components/arc/DEPS
index 8c82be2ef4c..9e1e36806a6 100644
--- a/chromium/components/arc/DEPS
+++ b/chromium/components/arc/DEPS
@@ -1,26 +1,27 @@
include_rules = [
+ "+ash/public/cpp",
"+chromeos/chromeos_switches.h",
"+chromeos/cryptohome",
"+chromeos/dbus",
"+components/exo",
+ "+components/keyed_service",
"+components/prefs",
+ "+components/session_manager/core",
"+components/signin/core/account_id",
"+components/user_manager",
"+mojo",
"+storage/browser/fileapi",
"+third_party/re2",
"+third_party/skia",
- "+ui/gfx/geometry/rect.h",
+ "+ui/gfx/geometry",
"+ui/gfx/range/range.h",
]
specific_include_rules = {
"arc_util.cc": [
- "+ash/shared",
"+ui/aura",
],
"arc_util_unittest.cc": [
- "+ash/shared",
"+ui/aura",
],
}
diff --git a/chromium/components/arc/arc_bridge_host_impl.cc b/chromium/components/arc/arc_bridge_host_impl.cc
index 3f8638728fb..02c5720605c 100644
--- a/chromium/components/arc/arc_bridge_host_impl.cc
+++ b/chromium/components/arc/arc_bridge_host_impl.cc
@@ -76,7 +76,9 @@ ArcBridgeHostImpl::ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service,
DCHECK(instance_.is_bound());
instance_.set_connection_error_handler(
base::Bind(&ArcBridgeHostImpl::OnClosed, base::Unretained(this)));
- instance_->Init(binding_.CreateInterfacePtrAndBind());
+ mojom::ArcBridgeHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ instance_->Init(std::move(host_proxy));
}
ArcBridgeHostImpl::~ArcBridgeHostImpl() {
@@ -151,6 +153,12 @@ void ArcBridgeHostImpl::OnKioskInstanceReady(
OnInstanceReady(arc_bridge_service_->kiosk(), std::move(kiosk_ptr));
}
+void ArcBridgeHostImpl::OnLockScreenInstanceReady(
+ mojom::LockScreenInstancePtr lock_screen_ptr) {
+ OnInstanceReady(arc_bridge_service_->lock_screen(),
+ std::move(lock_screen_ptr));
+}
+
void ArcBridgeHostImpl::OnMetricsInstanceReady(
mojom::MetricsInstancePtr metrics_ptr) {
OnInstanceReady(arc_bridge_service_->metrics(), std::move(metrics_ptr));
@@ -224,13 +232,19 @@ void ArcBridgeHostImpl::OnVoiceInteractionFrameworkInstanceReady(
std::move(framework_ptr));
}
+void ArcBridgeHostImpl::OnVolumeMounterInstanceReady(
+ mojom::VolumeMounterInstancePtr volume_mounter_ptr) {
+ OnInstanceReady(arc_bridge_service_->volume_mounter(),
+ std::move(volume_mounter_ptr));
+}
+
void ArcBridgeHostImpl::OnWallpaperInstanceReady(
mojom::WallpaperInstancePtr wallpaper_ptr) {
OnInstanceReady(arc_bridge_service_->wallpaper(), std::move(wallpaper_ptr));
}
void ArcBridgeHostImpl::OnClosed() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
VLOG(1) << "Mojo connection lost";
// Close all mojo channels.
@@ -243,7 +257,7 @@ void ArcBridgeHostImpl::OnClosed() {
template <typename T>
void ArcBridgeHostImpl::OnInstanceReady(InstanceHolder<T>* holder,
mojo::InterfacePtr<T> ptr) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(binding_.is_bound());
DCHECK(ptr.is_bound());
@@ -265,7 +279,7 @@ void ArcBridgeHostImpl::OnInstanceReady(InstanceHolder<T>* holder,
}
void ArcBridgeHostImpl::OnChannelClosed(MojoChannel* channel) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
mojo_channels_.erase(
std::find_if(mojo_channels_.begin(), mojo_channels_.end(),
[channel](std::unique_ptr<MojoChannel>& ptr) {
diff --git a/chromium/components/arc/arc_bridge_host_impl.h b/chromium/components/arc/arc_bridge_host_impl.h
index 935a7ef9199..d1d6b4f346a 100644
--- a/chromium/components/arc/arc_bridge_host_impl.h
+++ b/chromium/components/arc/arc_bridge_host_impl.h
@@ -59,6 +59,8 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
void OnIntentHelperInstanceReady(
mojom::IntentHelperInstancePtr intent_helper_ptr) override;
void OnKioskInstanceReady(mojom::KioskInstancePtr kiosk_ptr) override;
+ void OnLockScreenInstanceReady(
+ mojom::LockScreenInstancePtr lock_screen_ptr) override;
void OnMetricsInstanceReady(mojom::MetricsInstancePtr metrics_ptr) override;
void OnNetInstanceReady(mojom::NetInstancePtr net_ptr) override;
void OnNotificationsInstanceReady(
@@ -78,6 +80,8 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
mojom::VoiceInteractionArcHomeInstancePtr home_ptr) override;
void OnVoiceInteractionFrameworkInstanceReady(
mojom::VoiceInteractionFrameworkInstancePtr framework_ptr) override;
+ void OnVolumeMounterInstanceReady(
+ mojom::VolumeMounterInstancePtr volume_mounter_ptr) override;
void OnWallpaperInstanceReady(
mojom::WallpaperInstancePtr wallpaper_ptr) override;
@@ -94,7 +98,7 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
// Called if one of the established channels is closed.
void OnChannelClosed(MojoChannel* channel);
- base::ThreadChecker thread_checker_;
+ THREAD_CHECKER(thread_checker_);
// Owned by ArcServiceManager.
ArcBridgeService* const arc_bridge_service_;
diff --git a/chromium/components/arc/arc_bridge_service.h b/chromium/components/arc/arc_bridge_service.h
index 7d540135ad7..e69c0de19e1 100644
--- a/chromium/components/arc/arc_bridge_service.h
+++ b/chromium/components/arc/arc_bridge_service.h
@@ -27,6 +27,7 @@ class FileSystemInstance;
class ImeInstance;
class IntentHelperInstance;
class KioskInstance;
+class LockScreenInstance;
class MetricsInstance;
class NetInstance;
class NotificationsInstance;
@@ -41,6 +42,7 @@ class TtsInstance;
class VideoInstance;
class VoiceInteractionArcHomeInstance;
class VoiceInteractionFrameworkInstance;
+class VolumeMounterInstance;
class WallpaperInstance;
} // namespace mojom
@@ -77,6 +79,9 @@ class ArcBridgeService {
return &intent_helper_;
}
InstanceHolder<mojom::KioskInstance>* kiosk() { return &kiosk_; }
+ InstanceHolder<mojom::LockScreenInstance>* lock_screen() {
+ return &lock_screen_;
+ }
InstanceHolder<mojom::MetricsInstance>* metrics() { return &metrics_; }
InstanceHolder<mojom::NetInstance>* net() { return &net_; }
InstanceHolder<mojom::NotificationsInstance>* notifications() {
@@ -103,6 +108,9 @@ class ArcBridgeService {
voice_interaction_framework() {
return &voice_interaction_framework_;
}
+ InstanceHolder<mojom::VolumeMounterInstance>* volume_mounter() {
+ return &volume_mounter_;
+ }
InstanceHolder<mojom::WallpaperInstance>* wallpaper() { return &wallpaper_; }
private:
@@ -119,6 +127,7 @@ class ArcBridgeService {
InstanceHolder<mojom::ImeInstance> ime_;
InstanceHolder<mojom::IntentHelperInstance> intent_helper_;
InstanceHolder<mojom::KioskInstance> kiosk_;
+ InstanceHolder<mojom::LockScreenInstance> lock_screen_;
InstanceHolder<mojom::MetricsInstance> metrics_;
InstanceHolder<mojom::NetInstance> net_;
InstanceHolder<mojom::NotificationsInstance> notifications_;
@@ -135,6 +144,7 @@ class ArcBridgeService {
voice_interaction_arc_home_;
InstanceHolder<mojom::VoiceInteractionFrameworkInstance>
voice_interaction_framework_;
+ InstanceHolder<mojom::VolumeMounterInstance> volume_mounter_;
InstanceHolder<mojom::WallpaperInstance> wallpaper_;
DISALLOW_COPY_AND_ASSIGN(ArcBridgeService);
diff --git a/chromium/components/arc/arc_browser_context_keyed_service_factory_base.h b/chromium/components/arc/arc_browser_context_keyed_service_factory_base.h
new file mode 100644
index 00000000000..3ebf5fb4c67
--- /dev/null
+++ b/chromium/components/arc/arc_browser_context_keyed_service_factory_base.h
@@ -0,0 +1,122 @@
+// 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_ARC_ARC_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_BASE_H_
+#define COMPONENTS_ARC_ARC_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_BASE_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "components/arc/arc_service_manager.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace arc {
+namespace internal {
+
+// Implementation of BrowserContextKeyedServiceFactory for ARC related
+// services. The ARC related BrowserContextKeyedService can make its factory
+// class derived from this class.
+//
+// How to use:
+// In .h:
+// #include "components/keyed_service/core/keyed_service.h"
+//
+// namespace content {
+// class BrowserContext;
+// } // namespace content
+//
+// class ArcFooService : public KeyedService, ... {
+// public:
+// static ArcFooService* GetForBrowserContext(
+// content::BrowserContext* context);
+//
+// ArcFooService(content::BrowserContext* context,
+// ArcBridgeService* arc_bridge_service);
+// };
+//
+// In .cc:
+//
+// namespace {
+// class ArcFooServiceFactory
+// : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+// ArcFooService,
+// ArcFooServiceFactory> {
+// public:
+// static constexpr const char* kName = "ArcFooServiceFactory";
+//
+// static ArcFooServiceFactory* GetInstance() {
+// return base::Singleton<ArcFooServiceFactory>::get();
+// }
+//
+// private:
+// friend struct base::DefaultSingletonTraits<ArcFooServiceFactory>;
+// ArcFooServiceFactory() = default;
+// ~ArcFooServiceFactory() override = default;
+// };
+// } // namespace
+//
+// ArcFooService* ArcFooService::GetForBrowserContext(
+// content::BrowserContext* context) {
+// return ArcFooServiceFactory::GetForBrowserContext(context);
+// }
+//
+// If the service depends on other KeyedService, DependsOn() can be called in
+// the factory's ctor similar to other BrowserContextKeyedServiceFactory
+// subclasses.
+//
+// This header is intended to be included only from the .cc file directly.
+//
+// TODO(hidehiko): Make ArcFooService constructor (and maybe destructor)
+// private with declaring appropriate friend.
+template <typename Service, typename Factory>
+class ArcBrowserContextKeyedServiceFactoryBase
+ : public BrowserContextKeyedServiceFactory {
+ public:
+ // Returns the instance of the service for the given |context|,
+ // or nullptr if |context| is not allowed to use ARC.
+ // If the instance is not yet created, this creates a new service instance.
+ static Service* GetForBrowserContext(content::BrowserContext* context) {
+ return static_cast<Service*>(
+ Factory::GetInstance()->GetServiceForBrowserContext(context,
+ true /* create */));
+ }
+
+ protected:
+ ArcBrowserContextKeyedServiceFactoryBase()
+ : BrowserContextKeyedServiceFactory(
+ Factory::kName,
+ BrowserContextDependencyManager::GetInstance()) {}
+ ~ArcBrowserContextKeyedServiceFactoryBase() override = default;
+
+ // BrowserContextKeyedServiceFactory override:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override {
+ auto* arc_service_manager = arc::ArcServiceManager::Get();
+
+ // Practically, this is in testing case.
+ if (!arc_service_manager) {
+ VLOG(2) << "ArcServiceManager is not available.";
+ return nullptr;
+ }
+
+ if (arc_service_manager->browser_context() != context) {
+ VLOG(2) << "Non ARC allowed browser context.";
+ return nullptr;
+ }
+
+ return new Service(context, arc_service_manager->arc_bridge_service());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArcBrowserContextKeyedServiceFactoryBase);
+};
+
+} // namespace internal
+} // namespace arc
+
+#endif // COMPONENTS_ARC_ARC_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_BASE_H_
diff --git a/chromium/components/arc/arc_features.cc b/chromium/components/arc/arc_features.cc
index 66bf95a6d7d..f0ab3c4b751 100644
--- a/chromium/components/arc/arc_features.cc
+++ b/chromium/components/arc/arc_features.cc
@@ -6,20 +6,10 @@
namespace arc {
-// Controls if ARC should use silent auth code request API.
-const base::Feature kArcUseAuthEndpointFeature {
- "ArcUseAuthEndpoint", base::FEATURE_ENABLED_BY_DEFAULT
-};
-
// Controls ACTION_BOOT_COMPLETED broadcast for third party applications on ARC.
// When disabled, third party apps will not receive this broadcast.
const base::Feature kBootCompletedBroadcastFeature {
"ArcBootCompletedBroadcast", base::FEATURE_ENABLED_BY_DEFAULT
};
-// Controls whether we show ARC Files app in Chrome launcher.
-const base::Feature kShowArcFilesAppFeature {
- "ShowArcFilesApp", base::FEATURE_DISABLED_BY_DEFAULT
-};
-
} // namespace arc
diff --git a/chromium/components/arc/arc_features.h b/chromium/components/arc/arc_features.h
index 6366960f623..30ac5a47c3c 100644
--- a/chromium/components/arc/arc_features.h
+++ b/chromium/components/arc/arc_features.h
@@ -12,9 +12,7 @@
namespace arc {
// Please keep alphabetized.
-extern const base::Feature kArcUseAuthEndpointFeature;
extern const base::Feature kBootCompletedBroadcastFeature;
-extern const base::Feature kShowArcFilesAppFeature;
} // namespace arc
diff --git a/chromium/components/arc/arc_service_manager.cc b/chromium/components/arc/arc_service_manager.cc
index a9585bd3f84..3ecd33bdf89 100644
--- a/chromium/components/arc/arc_service_manager.cc
+++ b/chromium/components/arc/arc_service_manager.cc
@@ -6,11 +6,9 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/task_runner.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_session.h"
#include "components/arc/arc_session_runner.h"
-#include "components/arc/intent_helper/arc_intent_helper_observer.h"
namespace arc {
namespace {
@@ -20,17 +18,14 @@ ArcServiceManager* g_arc_service_manager = nullptr;
} // namespace
-ArcServiceManager::ArcServiceManager(
- scoped_refptr<base::TaskRunner> blocking_task_runner)
- : blocking_task_runner_(blocking_task_runner),
- arc_bridge_service_(base::MakeUnique<ArcBridgeService>()),
- activity_resolver_(new LocalActivityResolver()) {
+ArcServiceManager::ArcServiceManager()
+ : arc_bridge_service_(base::MakeUnique<ArcBridgeService>()) {
DCHECK(!g_arc_service_manager);
g_arc_service_manager = this;
}
ArcServiceManager::~ArcServiceManager() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(g_arc_service_manager, this);
g_arc_service_manager = nullptr;
}
@@ -39,19 +34,19 @@ ArcServiceManager::~ArcServiceManager() {
ArcServiceManager* ArcServiceManager::Get() {
if (!g_arc_service_manager)
return nullptr;
- DCHECK(g_arc_service_manager->thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(g_arc_service_manager->thread_checker_);
return g_arc_service_manager;
}
ArcBridgeService* ArcServiceManager::arc_bridge_service() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return arc_bridge_service_.get();
}
bool ArcServiceManager::AddServiceInternal(
const std::string& name,
std::unique_ptr<ArcService> service) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!name.empty() && services_.count(name) != 0) {
LOG(ERROR) << "Ignoring registration of service with duplicate name: "
<< name;
@@ -63,7 +58,7 @@ bool ArcServiceManager::AddServiceInternal(
ArcService* ArcServiceManager::GetNamedServiceInternal(
const std::string& name) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (name.empty()) {
LOG(ERROR) << "kArcServiceName[] should be a fully-qualified class name.";
return nullptr;
@@ -77,8 +72,7 @@ ArcService* ArcServiceManager::GetNamedServiceInternal(
}
void ArcServiceManager::Shutdown() {
- DCHECK(thread_checker_.CalledOnValidThread());
- activity_resolver_ = nullptr;
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
services_.clear();
}
diff --git a/chromium/components/arc/arc_service_manager.h b/chromium/components/arc/arc_service_manager.h
index 7d540a0c781..f918bb2d93c 100644
--- a/chromium/components/arc/arc_service_manager.h
+++ b/chromium/components/arc/arc_service_manager.h
@@ -14,10 +14,13 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/task_runner.h"
#include "base/threading/thread_checker.h"
#include "components/arc/arc_service.h"
-#include "components/arc/intent_helper/local_activity_resolver.h"
+#include "components/signin/core/account_id/account_id.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
namespace arc {
@@ -61,10 +64,31 @@ std::string GetArcServiceName(...) {
// instance via the ArcBridgeService.
class ArcServiceManager {
public:
- explicit ArcServiceManager(
- scoped_refptr<base::TaskRunner> blocking_task_runner);
+ ArcServiceManager();
~ArcServiceManager();
+ // Returns the current BrowserContext which ARC is allowed.
+ // This is workaround to split the dependency from chrome/.
+ // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+ // components/arc.
+ content::BrowserContext* browser_context() { return browser_context_; }
+
+ // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+ // components/arc. See browser_context() for details.
+ void set_browser_context(content::BrowserContext* browser_context) {
+ browser_context_ = browser_context;
+ }
+
+ // Returns the current AccountID which ARC is allowed.
+ // This is workaround to split the dependency from chrome/.
+ // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+ // components/arc.
+ const AccountId& account_id() const { return account_id_; }
+
+ // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+ // components/arc.
+ void set_account_id(const AccountId& account_id) { account_id_ = account_id; }
+
// |arc_bridge_service| can only be accessed on the thread that this
// class was created on.
ArcBridgeService* arc_bridge_service();
@@ -102,29 +126,32 @@ class ArcServiceManager {
// Called to shut down all ARC services.
void Shutdown();
- scoped_refptr<base::TaskRunner> blocking_task_runner() const {
- return blocking_task_runner_;
- }
-
- // Returns the activity resolver owned by ArcServiceManager.
- scoped_refptr<LocalActivityResolver> activity_resolver() {
- return activity_resolver_;
- }
-
private:
- class IntentHelperObserverImpl; // implemented in arc_service_manager.cc.
-
// Helper methods for AddService and GetService.
bool AddServiceInternal(const std::string& name,
std::unique_ptr<ArcService> service);
ArcService* GetNamedServiceInternal(const std::string& name);
- base::ThreadChecker thread_checker_;
- scoped_refptr<base::TaskRunner> blocking_task_runner_;
+ THREAD_CHECKER(thread_checker_);
std::unique_ptr<ArcBridgeService> arc_bridge_service_;
std::unordered_multimap<std::string, std::unique_ptr<ArcService>> services_;
- scoped_refptr<LocalActivityResolver> activity_resolver_;
+
+ // This holds the pointer to the BrowserContext (practically Profile)
+ // which is allowed to use ARC.
+ // This is set just before BrowserContextKeyedService classes are
+ // instantiated.
+ // So, in BrowserContextKeyedServiceFactory::BuildServiceInstanceFor(),
+ // given BrowserContext pointer can be compared to this to check if it is
+ // allowed to use ARC.
+ // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+ // components/arc. See browser_context() for details.
+ content::BrowserContext* browser_context_ = nullptr;
+
+ // This holds the AccountId corresponding to the |browser_context_|.
+ // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+ // components/arc. See browser_context() for details.
+ AccountId account_id_;
DISALLOW_COPY_AND_ASSIGN(ArcServiceManager);
};
diff --git a/chromium/components/arc/arc_service_manager_unittest.cc b/chromium/components/arc/arc_service_manager_unittest.cc
index 120d5ac343c..cd9f9dc7cc8 100644
--- a/chromium/components/arc/arc_service_manager_unittest.cc
+++ b/chromium/components/arc/arc_service_manager_unittest.cc
@@ -91,7 +91,7 @@ TEST_F(ArcServiceManagerTest, BasicGetter) {
bool named_service_alive = false;
// ArcServiceManager is empty, GetService() should return nullptr.
- auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+ auto manager = base::MakeUnique<ArcServiceManager>();
EXPECT_EQ(nullptr, manager->GetService<NamedService>());
EXPECT_TRUE(manager->AddService(base::MakeUnique<NamedService>(
@@ -110,7 +110,7 @@ TEST_F(ArcServiceManagerTest, MultipleAnonymousServices) {
bool anonymous_service_alive = false;
bool second_anonymous_service_alive = false;
- auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+ auto manager = base::MakeUnique<ArcServiceManager>();
EXPECT_TRUE(manager->AddService(base::MakeUnique<AnonymousService>(
arc_bridge_service(), &anonymous_service_alive)));
@@ -131,7 +131,7 @@ TEST_F(ArcServiceManagerTest, MultipleNamedServices) {
bool second_named_service_alive = false;
bool different_named_service_alive = false;
- auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+ auto manager = base::MakeUnique<ArcServiceManager>();
auto named_service = base::MakeUnique<NamedService>(arc_bridge_service(),
&named_service_alive);
@@ -168,7 +168,7 @@ TEST_F(ArcServiceManagerTest, MultipleNamedServices) {
TEST_F(ArcServiceManagerTest, EmptyNamedServices) {
bool empty_named_service_alive = false;
- auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+ auto manager = base::MakeUnique<ArcServiceManager>();
EXPECT_TRUE(manager->AddService(base::MakeUnique<EmptyNamedService>(
arc_bridge_service(), &empty_named_service_alive)));
@@ -187,7 +187,7 @@ TEST_F(ArcServiceManagerTest, TestGetGlobalService) {
// Create a manager. This will automatically be registered as a global
// instance.
- auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+ auto manager = base::MakeUnique<ArcServiceManager>();
// The getter should return nullptr when the manager doesn't know about the
// NamedService instance.
diff --git a/chromium/components/arc/arc_session.cc b/chromium/components/arc/arc_session.cc
index 62c9e3325d9..bfe9c8a4b91 100644
--- a/chromium/components/arc/arc_session.cc
+++ b/chromium/components/arc/arc_session.cc
@@ -19,7 +19,8 @@
#include "base/memory/ptr_util.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sys_info.h"
-#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/chromeos_switches.h"
@@ -125,9 +126,26 @@ class ArcSessionImpl : public ArcSession,
// ConnectMojo() -> OnMojoConnected() ->
// RUNNING
//
- // At any state, Stop() can be called. It does not immediately stop the
- // instance, but will eventually stop it.
- // The actual stop will be notified via ArcSession::Observer::OnStopped().
+ // Also, StartForLoginScreen() may start ARC instance with
+ // |login_screen_instance_requested_| set to |true|. In that case, the state
+ // changes like the following:
+ //
+ // NOT_STARTED
+ // StartForLoginScreen() ->
+ // CREATING_SOCKET
+ // CreateSocket() -> OnSocketCreated() ->
+ // STARTING_INSTANCE
+ // -> OnInstanceStarted() ->
+ // RUNNING_FOR_LOGIN_SCREEN
+ //
+ // Start() can also be used at any of these 3 state (from CREATING_SOCKET to
+ // RUNNING_FOR_LOGIN_SCREEN) to turn the instance for login screen into a
+ // fully functional one.
+ //
+ // Regardless of whether the instance is for login screen or not, at any
+ // state, Stop() can be called. It may not immediately stop the instance,
+ // but will eventually stop it. The actual stop will be notified via
+ // ArcSession::Observer::OnSessionStopped().
//
// When Stop() is called, it makes various behavior based on the current
// phase.
@@ -153,6 +171,7 @@ class ArcSessionImpl : public ArcSession,
// whose read side is also polled. Then, in its callback, similar to
// STARTING_INSTANCE, a request to stop the ARC instance is sent to
// SessionManager, and ArcInstanceStopped handles remaining procedure.
+ // RUNNING_FOR_LOGIN_SCREEN:
// RUNNING:
// There is no more callback which runs on normal flow, so Stop() requests
// to stop the ARC instance via SessionManager.
@@ -161,19 +180,6 @@ class ArcSessionImpl : public ArcSession,
// is an event ArcInstanceStopped() sent from SessionManager, when ARC
// instace unexpectedly terminates. ArcInstanceStopped() turns the state into
// STOPPED immediately.
- // This happens only when STARTING_INSTANCE, CONNECTING_MOJO or RUNNING
- // state.
- //
- // STARTING_INSTANCE:
- // In OnInstanceStarted(), |state_| is checked at the beginning. If it is
- // STOPPED, then ArcInstanceStopped() is called. Do nothing in that case.
- // CONNECTING_MOJO:
- // Similar to Stop() case above, ArcInstanceStopped() also notifies to
- // BlockingPool thread to cancel it to unblock the thread. In
- // OnMojoConnected(), similar to OnInstanceStarted(), check if |state_| is
- // STOPPED, then do nothing.
- // RUNNING:
- // It is not necessary to do anything special here.
//
// In NOT_STARTED or STOPPED state, the instance can be safely destructed.
// Specifically, in STOPPED state, there may be inflight operations or
@@ -189,9 +195,13 @@ class ArcSessionImpl : public ArcSession,
// An UNIX socket is being created.
CREATING_SOCKET,
- // The request to start the instance has been sent.
+ // The request to start or resume the instance has been sent.
STARTING_INSTANCE,
+ // The instance is set up, but only a handful of processes NOT including
+ // arcbridgeservice (i.e. mojo endpoint) are running.
+ RUNNING_FOR_LOGIN_SCREEN,
+
// The instance has started. Waiting for it to connect to the IPC bridge.
CONNECTING_MOJO,
@@ -202,11 +212,12 @@ class ArcSessionImpl : public ArcSession,
STOPPED,
};
- ArcSessionImpl(ArcBridgeService* arc_bridge_service,
- const scoped_refptr<base::TaskRunner>& blocking_task_runner);
+ explicit ArcSessionImpl(ArcBridgeService* arc_bridge_service);
~ArcSessionImpl() override;
// ArcSession overrides:
+ void StartForLoginScreen() override;
+ bool IsForLoginScreen() override;
void Start() override;
void Stop() override;
void OnShutdown() override;
@@ -215,10 +226,12 @@ class ArcSessionImpl : public ArcSession,
// Creates the UNIX socket on a worker pool and then processes its file
// descriptor.
static mojo::edk::ScopedPlatformHandle CreateSocket();
- void OnSocketCreated(mojo::edk::ScopedPlatformHandle fd);
+ void OnSocketCreated(bool instance_is_for_login_screen,
+ mojo::edk::ScopedPlatformHandle fd);
// DBus callback for StartArcInstance().
- void OnInstanceStarted(mojo::edk::ScopedPlatformHandle socket_fd,
+ void OnInstanceStarted(bool instance_is_for_login_screen,
+ mojo::edk::ScopedPlatformHandle socket_fd,
StartArcInstanceResult result,
const std::string& container_instance_id);
@@ -236,25 +249,36 @@ class ArcSessionImpl : public ArcSession,
void ArcInstanceStopped(bool clean,
const std::string& container_instance_id) override;
- // Completes the termination procedure.
+ // Completes the termination procedure. Note that calling this may end up with
+ // deleting |this| because the function calls observers' OnSessionStopped().
void OnStopped(ArcStopReason reason);
+ // Sends a StartArcInstance D-Bus request to session_manager.
+ static void SendStartArcInstanceDBusMessage(
+ bool instance_is_for_login_screen,
+ const chromeos::SessionManagerClient::StartArcInstanceCallback& cb);
+
// Checks whether a function runs on the thread where the instance is
// created.
- base::ThreadChecker thread_checker_;
+ THREAD_CHECKER(thread_checker_);
// Owned by ArcServiceManager.
ArcBridgeService* const arc_bridge_service_;
- // Task runner to run a blocking tasks.
- scoped_refptr<base::TaskRunner> blocking_task_runner_;
-
// The state of the session.
State state_ = State::NOT_STARTED;
// When Stop() is called, this flag is set.
bool stop_requested_ = false;
+ // When StartForLoginScreen() is called, this flag is set. After
+ // that, when Start() is called to resume the boot, the flag is unset.
+ bool login_screen_instance_requested_ = false;
+
+ // The handle StartForLoginScreen() has created. The variable has a
+ // valid handle only when |state_| is RUNNING_FOR_LOGIN_SCREEN.
+ mojo::edk::ScopedPlatformHandle socket_fd_;
+
// Container instance id passed from session_manager.
// Should be available only after OnInstanceStarted().
std::string container_instance_id_;
@@ -273,12 +297,8 @@ class ArcSessionImpl : public ArcSession,
DISALLOW_COPY_AND_ASSIGN(ArcSessionImpl);
};
-ArcSessionImpl::ArcSessionImpl(
- ArcBridgeService* arc_bridge_service,
- const scoped_refptr<base::TaskRunner>& blocking_task_runner)
- : arc_bridge_service_(arc_bridge_service),
- blocking_task_runner_(blocking_task_runner),
- weak_factory_(this) {
+ArcSessionImpl::ArcSessionImpl(ArcBridgeService* arc_bridge_service)
+ : arc_bridge_service_(arc_bridge_service), weak_factory_(this) {
chromeos::SessionManagerClient* client = GetSessionManagerClient();
if (client == nullptr)
return;
@@ -286,7 +306,7 @@ ArcSessionImpl::ArcSessionImpl(
}
ArcSessionImpl::~ArcSessionImpl() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(state_ == State::NOT_STARTED || state_ == State::STOPPED);
chromeos::SessionManagerClient* client = GetSessionManagerClient();
if (client == nullptr)
@@ -295,16 +315,43 @@ ArcSessionImpl::~ArcSessionImpl() {
}
void ArcSessionImpl::Start() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_EQ(state_, State::NOT_STARTED);
- VLOG(2) << "Starting ARC session.";
- VLOG(2) << "Creating socket...";
-
- state_ = State::CREATING_SOCKET;
- base::PostTaskAndReplyWithResult(
- blocking_task_runner_.get(), FROM_HERE,
- base::Bind(&ArcSessionImpl::CreateSocket),
- base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr()));
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // Start() can be called either for starting ARC from scratch or for
+ // resuming an existing one. Start() must be able to start a fully
+ // functional instance from all of |state_| up to and including
+ // RUNNING_FOR_LOGIN_SCREEN.
+ DCHECK_GE(State::RUNNING_FOR_LOGIN_SCREEN, state_);
+
+ // Flip the flag now so that callback functions like OnSocketCreated()
+ // can do the right thing.
+ login_screen_instance_requested_ = false;
+
+ if (state_ == State::NOT_STARTED) {
+ // An instance for login screen does not exist. Start a new one from
+ // scratch.
+ VLOG(2) << "Starting ARC session";
+ VLOG(2) << "Creating socket...";
+ state_ = State::CREATING_SOCKET;
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock()},
+ base::Bind(&ArcSessionImpl::CreateSocket),
+ base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr(),
+ false /* not for login screen */));
+ } else if (state_ == State::CREATING_SOCKET) {
+ VLOG(2) << "Requested to start ARC instance with an existing socket";
+ // OnSocketCreated() will start a fully featured instance.
+ } else if (state_ == State::STARTING_INSTANCE) {
+ VLOG(2) << "Requested to resume an existing ARC instance";
+ // OnInstanceStarted() will start a fully featured instance.
+ } else if (state_ == State::RUNNING_FOR_LOGIN_SCREEN) {
+ VLOG(2) << "Resuming an existing ARC instance";
+ state_ = State::STARTING_INSTANCE;
+ SendStartArcInstanceDBusMessage(
+ false /* not for login screen */,
+ base::Bind(&ArcSessionImpl::OnInstanceStarted,
+ weak_factory_.GetWeakPtr(), false /* the same */,
+ base::Passed(&socket_fd_)));
+ }
}
// static
@@ -346,8 +393,9 @@ mojo::edk::ScopedPlatformHandle ArcSessionImpl::CreateSocket() {
}
void ArcSessionImpl::OnSocketCreated(
+ bool instance_is_for_login_screen,
mojo::edk::ScopedPlatformHandle socket_fd) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(state_, State::CREATING_SOCKET);
if (stop_requested_) {
@@ -362,36 +410,65 @@ void ArcSessionImpl::OnSocketCreated(
return;
}
- VLOG(2) << "Socket is created. Starting ARC instance...";
+ VLOG(2) << "Socket is created. Starting ARC instance"
+ << (instance_is_for_login_screen ? " for login screen" : "");
state_ = State::STARTING_INSTANCE;
+ SendStartArcInstanceDBusMessage(
+ instance_is_for_login_screen,
+ base::Bind(&ArcSessionImpl::OnInstanceStarted, weak_factory_.GetWeakPtr(),
+ instance_is_for_login_screen, base::Passed(&socket_fd)));
+}
+
+// static
+void ArcSessionImpl::SendStartArcInstanceDBusMessage(
+ bool instance_is_for_login_screen,
+ const chromeos::SessionManagerClient::StartArcInstanceCallback& cb) {
+ chromeos::SessionManagerClient* session_manager_client =
+ chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
+ if (instance_is_for_login_screen) {
+ session_manager_client->StartArcInstance(
+ chromeos::SessionManagerClient::ArcStartupMode::LOGIN_SCREEN,
+ // All variables below except |cb| will be ignored.
+ cryptohome::Identification(), false, false, cb);
+ return;
+ }
+
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
DCHECK(user_manager->GetPrimaryUser());
const cryptohome::Identification cryptohome_id(
user_manager->GetPrimaryUser()->GetAccountId());
- bool disable_boot_completed_broadcast =
+ const bool skip_boot_completed_broadcast =
!base::FeatureList::IsEnabled(arc::kBootCompletedBroadcastFeature);
// We only enable /vendor/priv-app when voice interaction is enabled because
// voice interaction service apk would be bundled in this location.
- bool enable_vendor_privileged =
+ const bool scan_vendor_priv_app =
chromeos::switches::IsVoiceInteractionEnabled();
- chromeos::SessionManagerClient* session_manager_client =
- chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
session_manager_client->StartArcInstance(
- cryptohome_id, disable_boot_completed_broadcast, enable_vendor_privileged,
- base::Bind(&ArcSessionImpl::OnInstanceStarted, weak_factory_.GetWeakPtr(),
- base::Passed(&socket_fd)));
+ chromeos::SessionManagerClient::ArcStartupMode::FULL, cryptohome_id,
+ skip_boot_completed_broadcast, scan_vendor_priv_app, cb);
}
void ArcSessionImpl::OnInstanceStarted(
+ bool instance_is_for_login_screen,
mojo::edk::ScopedPlatformHandle socket_fd,
StartArcInstanceResult result,
const std::string& container_instance_id) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(state_, State::STARTING_INSTANCE);
- container_instance_id_ = container_instance_id;
+
+ bool resumed = false;
+ if (!container_instance_id_.empty()) {
+ // |container_instance_id_| has already been initialized when the instance
+ // for login screen was started.
+ DCHECK(container_instance_id.empty());
+ DCHECK(!instance_is_for_login_screen);
+ resumed = true;
+ } else {
+ container_instance_id_ = container_instance_id;
+ }
if (stop_requested_) {
if (result == StartArcInstanceResult::SUCCESS) {
@@ -411,19 +488,38 @@ void ArcSessionImpl::OnInstanceStarted(
return;
}
- VLOG(2) << "ARC instance is successfully started. Connecting Mojo...";
+ if (instance_is_for_login_screen) {
+ VLOG(2) << "ARC instance for login screen is successfully started.";
+ if (login_screen_instance_requested_) {
+ state_ = State::RUNNING_FOR_LOGIN_SCREEN;
+ socket_fd_ = std::move(socket_fd);
+ } else {
+ // Start() has been called.
+ VLOG(2) << "Resuming an existing ARC instance";
+ state_ = State::STARTING_INSTANCE;
+ SendStartArcInstanceDBusMessage(
+ false /* not for login screen */,
+ base::Bind(&ArcSessionImpl::OnInstanceStarted,
+ weak_factory_.GetWeakPtr(), false /* the same */,
+ base::Passed(&socket_fd_)));
+ }
+ return;
+ }
+
+ VLOG(2) << "ARC instance is successfully "
+ << (resumed ? "resumed" : "started") << ". Connecting Mojo...";
state_ = State::CONNECTING_MOJO;
// Prepare a pipe so that AcceptInstanceConnection can be interrupted on
// Stop().
base::ScopedFD cancel_fd;
if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) {
- OnStopped(ArcStopReason::GENERIC_BOOT_FAILURE);
+ StopArcInstance();
return;
}
- base::PostTaskAndReplyWithResult(
- blocking_task_runner_.get(), FROM_HERE,
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock()},
base::Bind(&ArcSessionImpl::ConnectMojo, base::Passed(&socket_fd),
base::Passed(&cancel_fd)),
base::Bind(&ArcSessionImpl::OnMojoConnected, weak_factory_.GetWeakPtr()));
@@ -481,7 +577,7 @@ mojo::ScopedMessagePipeHandle ArcSessionImpl::ConnectMojo(
void ArcSessionImpl::OnMojoConnected(
mojo::ScopedMessagePipeHandle server_pipe) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(state_, State::CONNECTING_MOJO);
accept_cancel_pipe_.reset();
@@ -509,7 +605,7 @@ void ArcSessionImpl::OnMojoConnected(
}
void ArcSessionImpl::Stop() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
VLOG(2) << "Stopping ARC session is requested.";
// For second time or later, just do nothing.
@@ -538,6 +634,11 @@ void ArcSessionImpl::Stop() {
// clean it up.
return;
+ case State::RUNNING_FOR_LOGIN_SCREEN:
+ // An ARC instance for login screen is running. Request to stop it.
+ StopArcInstance();
+ return;
+
case State::CONNECTING_MOJO:
// Mojo connection is being waited on a BlockingPool thread.
// Request to cancel it. Following stopping procedure will run
@@ -557,12 +658,15 @@ void ArcSessionImpl::Stop() {
}
void ArcSessionImpl::StopArcInstance() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(state_ == State::STARTING_INSTANCE ||
+ state_ == State::RUNNING_FOR_LOGIN_SCREEN ||
state_ == State::CONNECTING_MOJO || state_ == State::RUNNING);
- // Notification will arrive through ArcInstanceStopped().
- VLOG(2) << "Requesting to stop ARC instance";
+ VLOG(2) << "Requesting session_manager to stop ARC instance";
+
+ // When the instance is not for login screen, change the |state_| in
+ // ArcInstanceStopped().
chromeos::SessionManagerClient* session_manager_client =
chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
session_manager_client->StopArcInstance(
@@ -572,7 +676,7 @@ void ArcSessionImpl::StopArcInstance() {
void ArcSessionImpl::ArcInstanceStopped(
bool clean,
const std::string& container_instance_id) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
VLOG(1) << "Notified that ARC instance is stopped "
<< (clean ? "cleanly" : "uncleanly");
@@ -606,8 +710,25 @@ void ArcSessionImpl::ArcInstanceStopped(
OnStopped(reason);
}
+void ArcSessionImpl::StartForLoginScreen() {
+ DCHECK_EQ(State::NOT_STARTED, state_);
+
+ VLOG(2) << "Starting ARC session for login screen";
+ VLOG(2) << "Creating socket...";
+ login_screen_instance_requested_ = true;
+ state_ = State::CREATING_SOCKET;
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock()}, base::Bind(&ArcSessionImpl::CreateSocket),
+ base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr(),
+ true /* for login screen */));
+}
+
+bool ArcSessionImpl::IsForLoginScreen() {
+ return login_screen_instance_requested_;
+}
+
void ArcSessionImpl::OnStopped(ArcStopReason reason) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// OnStopped() should be called once per instance.
DCHECK_NE(state_, State::STOPPED);
VLOG(2) << "ARC session is stopped.";
@@ -618,7 +739,7 @@ void ArcSessionImpl::OnStopped(ArcStopReason reason) {
}
void ArcSessionImpl::OnShutdown() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
stop_requested_ = true;
if (state_ == State::STOPPED)
return;
@@ -633,10 +754,12 @@ void ArcSessionImpl::OnShutdown() {
// Note that this may fail if ARC container is not actually running, but
// ignore an error as described below.
if (state_ == State::STARTING_INSTANCE ||
- state_ == State::CONNECTING_MOJO || state_ == State::RUNNING)
+ state_ == State::RUNNING_FOR_LOGIN_SCREEN ||
+ state_ == State::CONNECTING_MOJO || state_ == State::RUNNING) {
StopArcInstance();
+ }
- // Directly set to the STOPPED stateby OnStopped(). Note that calling
+ // Directly set to the STOPPED state by OnStopped(). Note that calling
// StopArcInstance() may not work well. At least, because the UI thread is
// already stopped here, ArcInstanceStopped() callback cannot be invoked.
OnStopped(ArcStopReason::SHUTDOWN);
@@ -657,10 +780,8 @@ void ArcSession::RemoveObserver(Observer* observer) {
// static
std::unique_ptr<ArcSession> ArcSession::Create(
- ArcBridgeService* arc_bridge_service,
- const scoped_refptr<base::TaskRunner>& blocking_task_runner) {
- return base::MakeUnique<ArcSessionImpl>(arc_bridge_service,
- blocking_task_runner);
+ ArcBridgeService* arc_bridge_service) {
+ return base::MakeUnique<ArcSessionImpl>(arc_bridge_service);
}
} // namespace arc
diff --git a/chromium/components/arc/arc_session.h b/chromium/components/arc/arc_session.h
index 4f9f38747ab..01c8c9394ff 100644
--- a/chromium/components/arc/arc_session.h
+++ b/chromium/components/arc/arc_session.h
@@ -8,11 +8,7 @@
#include <memory>
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
-#include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task_runner.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_stop_reason.h"
@@ -21,8 +17,8 @@ namespace arc {
// Starts the ARC instance and bootstraps the bridge connection.
// Clients should implement the Delegate to be notified upon communications
// being available.
-// The instance can be safely removed 1) before Start() is called, or 2) after
-// OnStopped() is called.
+// The instance can be safely removed 1) before Start*() is called, or 2) after
+// OnSessionStopped() is called.
// The number of instances must be at most one. Otherwise, ARC instances will
// conflict.
class ArcSession {
@@ -43,21 +39,32 @@ class ArcSession {
// Creates a default instance of ArcSession.
static std::unique_ptr<ArcSession> Create(
- ArcBridgeService* arc_bridge_service,
- const scoped_refptr<base::TaskRunner>& blocking_task_runner);
+ ArcBridgeService* arc_bridge_service);
virtual ~ArcSession();
+ // Starts an instance for login screen. The instance is not a fully functional
+ // one, and Observer::OnSessionReady() will *never* be called.
+ virtual void StartForLoginScreen() = 0;
+
+ // Returns true if StartForLoginScreen() has been called but Start() hasn't.
+ virtual bool IsForLoginScreen() = 0;
+
// Starts and bootstraps a connection with the instance. The Observer's
- // OnReady() will be called if the bootstrapping is successful, or
- // OnStopped() if it is not. Start() should not be called twice or more.
+ // OnSessionReady() will be called if the bootstrapping is successful, or
+ // OnSessionStopped() if it is not. Start() should not be called twice or
+ // more. When StartForLoginScreen() has already been called, Start() turns
+ // the mini instance to a fully functional one.
virtual void Start() = 0;
- // Requests to stop the currently-running instance.
- // The completion is notified via OnStopped() of the Delegate.
+ // Requests to stop the currently-running instance whether or not it is for
+ // login screen.
+ // The completion is notified via OnSessionStopped() of the Observer.
virtual void Stop() = 0;
// Called when Chrome is in shutdown state. This is called when the message
- // loop is already stopped, and the instance will soon be deleted.
+ // loop is already stopped, and the instance will soon be deleted. Caller
+ // may expect that OnSessionStopped() is synchronously called back except
+ // when it has already been called before.
virtual void OnShutdown() = 0;
void AddObserver(Observer* observer);
diff --git a/chromium/components/arc/arc_session_runner.cc b/chromium/components/arc/arc_session_runner.cc
index 005539e557b..c9af9939e28 100644
--- a/chromium/components/arc/arc_session_runner.cc
+++ b/chromium/components/arc/arc_session_runner.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/task_runner.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
namespace arc {
@@ -15,31 +16,48 @@ namespace {
constexpr base::TimeDelta kDefaultRestartDelay =
base::TimeDelta::FromSeconds(5);
+chromeos::SessionManagerClient* GetSessionManagerClient() {
+ // If the DBusThreadManager or the SessionManagerClient aren't available,
+ // there isn't much we can do. This should only happen when running tests.
+ if (!chromeos::DBusThreadManager::IsInitialized() ||
+ !chromeos::DBusThreadManager::Get() ||
+ !chromeos::DBusThreadManager::Get()->GetSessionManagerClient())
+ return nullptr;
+ return chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
+}
+
} // namespace
ArcSessionRunner::ArcSessionRunner(const ArcSessionFactory& factory)
: restart_delay_(kDefaultRestartDelay),
factory_(factory),
- weak_ptr_factory_(this) {}
+ weak_ptr_factory_(this) {
+ chromeos::SessionManagerClient* client = GetSessionManagerClient();
+ if (client)
+ client->AddObserver(this);
+}
ArcSessionRunner::~ArcSessionRunner() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (arc_session_)
arc_session_->RemoveObserver(this);
+ chromeos::SessionManagerClient* client = GetSessionManagerClient();
+ if (client)
+ client->RemoveObserver(this);
}
void ArcSessionRunner::AddObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
observer_list_.AddObserver(observer);
}
void ArcSessionRunner::RemoveObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
observer_list_.RemoveObserver(observer);
}
void ArcSessionRunner::RequestStart() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Consecutive RequestStart() call. Do nothing.
if (run_requested_)
@@ -52,30 +70,33 @@ void ArcSessionRunner::RequestStart() {
// previous RequestStop() call).
DCHECK(!restart_timer_.IsRunning());
- if (arc_session_) {
+ if (arc_session_ && state_ >= State::STARTING) {
// In this case, RequestStop() was called, and before |arc_session_| had
// finished stopping, RequestStart() was called. Do nothing in that case,
// since when |arc_session_| does actually stop, OnSessionStopped() will
// be called, where it should automatically restart.
DCHECK_EQ(state_, State::STOPPING);
} else {
- DCHECK_EQ(state_, State::STOPPED);
+ DCHECK_LE(state_, State::STARTING_FOR_LOGIN_SCREEN);
StartArcSession();
}
}
-void ArcSessionRunner::RequestStop() {
- DCHECK(thread_checker_.CalledOnValidThread());
+void ArcSessionRunner::RequestStop(bool always_stop_session) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // Consecutive RequestStop() call. Do nothing.
- if (!run_requested_)
- return;
+ if (!run_requested_) {
+ // Call Stop() to stop an instance for login screen (if any.) If this is
+ // just a consecutive RequestStop() call, Stop() does nothing.
+ if (!always_stop_session || !arc_session_)
+ return;
+ }
VLOG(1) << "Session ended";
run_requested_ = false;
if (arc_session_) {
- // The |state_| could be either STARTING, RUNNING or STOPPING.
+ // The |state_| could be either STARTING*, RUNNING or STOPPING.
DCHECK_NE(state_, State::STOPPED);
if (state_ == State::STOPPING) {
@@ -96,7 +117,7 @@ void ArcSessionRunner::RequestStop() {
}
void ArcSessionRunner::OnShutdown() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
VLOG(1) << "OnShutdown";
run_requested_ = false;
@@ -112,38 +133,40 @@ void ArcSessionRunner::OnShutdown() {
}
bool ArcSessionRunner::IsRunning() const {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return state_ == State::RUNNING;
}
bool ArcSessionRunner::IsStopped() const {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return state_ == State::STOPPED;
}
void ArcSessionRunner::SetRestartDelayForTesting(
const base::TimeDelta& restart_delay) {
DCHECK_EQ(state_, State::STOPPED);
- DCHECK(!arc_session_);
DCHECK(!restart_timer_.IsRunning());
restart_delay_ = restart_delay;
}
void ArcSessionRunner::StartArcSession() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_EQ(state_, State::STOPPED);
- DCHECK(!arc_session_);
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!restart_timer_.IsRunning());
VLOG(1) << "Starting ARC instance";
- arc_session_ = factory_.Run();
- arc_session_->AddObserver(this);
+ if (!arc_session_) {
+ DCHECK_EQ(state_, State::STOPPED);
+ arc_session_ = factory_.Run();
+ arc_session_->AddObserver(this);
+ } else {
+ DCHECK_EQ(state_, State::STARTING_FOR_LOGIN_SCREEN);
+ }
state_ = State::STARTING;
arc_session_->Start();
}
void ArcSessionRunner::OnSessionReady() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(state_, State::STARTING);
DCHECK(arc_session_);
DCHECK(!restart_timer_.IsRunning());
@@ -153,12 +176,17 @@ void ArcSessionRunner::OnSessionReady() {
}
void ArcSessionRunner::OnSessionStopped(ArcStopReason stop_reason) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_NE(state_, State::STOPPED);
DCHECK(arc_session_);
DCHECK(!restart_timer_.IsRunning());
VLOG(0) << "ARC stopped: " << stop_reason;
+
+ // The observers should be agnostic to the existence of the limited-purpose
+ // instance.
+ const bool notify_observers = !arc_session_->IsForLoginScreen();
+
arc_session_->RemoveObserver(this);
arc_session_.reset();
@@ -188,8 +216,24 @@ void ArcSessionRunner::OnSessionStopped(ArcStopReason stop_reason) {
}
state_ = State::STOPPED;
- for (auto& observer : observer_list_)
- observer.OnSessionStopped(stop_reason, restarting);
+ if (notify_observers) {
+ for (auto& observer : observer_list_)
+ observer.OnSessionStopped(stop_reason, restarting);
+ }
+}
+
+void ArcSessionRunner::EmitLoginPromptVisibleCalled() {
+ // Since 'login-prompt-visible' Upstart signal starts all Upstart jobs the
+ // container may depend on such as cras, EmitLoginPromptVisibleCalled() is the
+ // safe place to start the container for login screen.
+ DCHECK(!arc_session_);
+ DCHECK_EQ(state_, State::STOPPED);
+
+ // TODO(yusukes): Once Chrome OS side is ready, uncomment the following:
+ // arc_session_ = factory_.Run();
+ // arc_session_->AddObserver(this);
+ // state_ = State::STARTING_FOR_LOGIN_SCREEN;
+ // arc_session_->StartForLoginScreen();
}
} // namespace arc
diff --git a/chromium/components/arc/arc_session_runner.h b/chromium/components/arc/arc_session_runner.h
index 5de7585de26..13bf3ea575a 100644
--- a/chromium/components/arc/arc_session_runner.h
+++ b/chromium/components/arc/arc_session_runner.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "chromeos/dbus/session_manager_client.h"
#include "components/arc/arc_session.h"
#include "components/arc/arc_stop_reason.h"
@@ -18,7 +19,8 @@ namespace arc {
// Accept requests to start/stop ARC instance. Also supports automatic
// restarting on unexpected ARC instance crash.
-class ArcSessionRunner : public ArcSession::Observer {
+class ArcSessionRunner : public ArcSession::Observer,
+ public chromeos::SessionManagerClient::Observer {
public:
// Observer to notify events across multiple ARC session runs.
class Observer {
@@ -50,7 +52,8 @@ class ArcSessionRunner : public ArcSession::Observer {
void RequestStart();
// Stops the ARC service.
- void RequestStop();
+ // TODO(yusukes): Remove the parameter.
+ void RequestStop(bool always_stop_session);
// OnShutdown() should be called when the browser is shutting down. This can
// only be called on the thread that this class was created on. We assume that
@@ -94,6 +97,10 @@ class ArcSessionRunner : public ArcSession::Observer {
// ARC instance is not currently running.
STOPPED,
+ // Request to start ARC instance for login screen is received. Starting an
+ // ARC instance.
+ STARTING_FOR_LOGIN_SCREEN,
+
// Request to start ARC instance is received. Starting an ARC instance.
STARTING,
@@ -112,7 +119,10 @@ class ArcSessionRunner : public ArcSession::Observer {
void OnSessionReady() override;
void OnSessionStopped(ArcStopReason reason) override;
- base::ThreadChecker thread_checker_;
+ // chromeos::SessionManagerClient::Observer:
+ void EmitLoginPromptVisibleCalled() override;
+
+ THREAD_CHECKER(thread_checker_);
// Observers for the ARC instance state change events.
base::ObserverList<Observer> observer_list_;
diff --git a/chromium/components/arc/arc_session_runner_unittest.cc b/chromium/components/arc/arc_session_runner_unittest.cc
index c407a097661..144d0b0bb6f 100644
--- a/chromium/components/arc/arc_session_runner_unittest.cc
+++ b/chromium/components/arc/arc_session_runner_unittest.cc
@@ -14,6 +14,7 @@
#include "base/single_thread_task_runner.h"
#include "base/test/scoped_task_environment.h"
#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_session_manager_client.h"
#include "components/arc/arc_session_runner.h"
#include "components/arc/test/fake_arc_session.h"
#include "mojo/public/cpp/system/message_pipe.h"
@@ -40,10 +41,13 @@ class ArcSessionRunnerTest : public testing::Test,
base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
void SetUp() override {
+ chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
+ base::MakeUnique<chromeos::FakeSessionManagerClient>());
chromeos::DBusThreadManager::Initialize();
stop_reason_ = ArcStopReason::SHUTDOWN;
restarting_ = false;
+ stopped_called_ = false;
// We inject FakeArcSession here so we do not need task_runner.
arc_session_runner_ =
@@ -65,8 +69,17 @@ class ArcSessionRunnerTest : public testing::Test,
arc_session_runner_->GetArcSessionForTesting());
}
- ArcStopReason stop_reason() { return stop_reason_; }
- bool restarting() { return restarting_; }
+ ArcStopReason stop_reason() {
+ EXPECT_TRUE(stopped_called());
+ return stop_reason_;
+ }
+
+ bool restarting() {
+ EXPECT_TRUE(stopped_called());
+ return restarting_;
+ }
+
+ bool stopped_called() { return stopped_called_; }
void ResetArcSessionFactory(
const ArcSessionRunner::ArcSessionFactory& factory) {
@@ -95,10 +108,12 @@ class ArcSessionRunnerTest : public testing::Test,
// ArcSessionRunner::OnSessionStopped().
stop_reason_ = stop_reason;
restarting_ = restarting;
+ stopped_called_ = true;
}
ArcStopReason stop_reason_;
bool restarting_;
+ bool stopped_called_;
std::unique_ptr<ArcSessionRunner> arc_session_runner_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -138,7 +153,7 @@ TEST_F(ArcSessionRunnerTest, Basic) {
arc_session_runner()->RequestStart();
EXPECT_TRUE(arc_session_runner()->IsRunning());
- arc_session_runner()->RequestStop();
+ arc_session_runner()->RequestStop(false);
EXPECT_TRUE(arc_session_runner()->IsStopped());
EXPECT_TRUE(observer.stopped_called());
}
@@ -154,7 +169,7 @@ TEST_F(ArcSessionRunnerTest, StopMidStartup) {
EXPECT_FALSE(arc_session_runner()->IsStopped());
EXPECT_FALSE(arc_session_runner()->IsRunning());
- arc_session_runner()->RequestStop();
+ arc_session_runner()->RequestStop(false);
EXPECT_TRUE(arc_session_runner()->IsStopped());
}
@@ -171,6 +186,47 @@ TEST_F(ArcSessionRunnerTest, BootFailure) {
EXPECT_TRUE(arc_session_runner()->IsStopped());
}
+// Does the same with the mini instance for login screen.
+// TODO(yusukes): Enable the test once EmitLoginPromptVisibleCalled() is fully
+// enabled.
+TEST_F(ArcSessionRunnerTest, DISABLED_BootFailureForLoginScreen) {
+ ResetArcSessionFactory(
+ base::Bind(&ArcSessionRunnerTest::CreateBootFailureArcSession,
+ ArcStopReason::CRASH));
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ chromeos::DBusThreadManager::Get()
+ ->GetSessionManagerClient()
+ ->EmitLoginPromptVisible();
+ // If starting the mini instance fails, arc_session_runner()'s state goes back
+ // to STOPPED, but its observers won't be notified.
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+ EXPECT_FALSE(stopped_called());
+
+ // Also make sure that RequestStart() works just fine after the boot
+ // failure.
+ ResetArcSessionFactory(base::Bind(FakeArcSession::Create));
+ arc_session_runner()->RequestStart();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+}
+
+// Tests that RequestStart() works even after EmitLoginPromptVisibleCalled()
+// is called.
+// TODO(yusukes): Enable the test once EmitLoginPromptVisibleCalled() is fully
+// enabled.
+TEST_F(ArcSessionRunnerTest, DISABLED_StartWithLoginScreenInstance) {
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ chromeos::DBusThreadManager::Get()
+ ->GetSessionManagerClient()
+ ->EmitLoginPromptVisible();
+ EXPECT_FALSE(arc_session_runner()->IsStopped());
+ EXPECT_FALSE(arc_session_runner()->IsRunning());
+
+ arc_session_runner()->RequestStart();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+}
+
// If the instance is stopped, it should be re-started.
TEST_F(ArcSessionRunnerTest, Restart) {
arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
@@ -186,7 +242,7 @@ TEST_F(ArcSessionRunnerTest, Restart) {
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(arc_session_runner()->IsRunning());
- arc_session_runner()->RequestStop();
+ arc_session_runner()->RequestStop(false);
EXPECT_TRUE(arc_session_runner()->IsStopped());
}
@@ -217,7 +273,7 @@ TEST_F(ArcSessionRunnerTest, OnSessionStopped) {
EXPECT_TRUE(arc_session_runner()->IsRunning());
// Graceful stop.
- arc_session_runner()->RequestStop();
+ arc_session_runner()->RequestStop(false);
EXPECT_EQ(ArcStopReason::SHUTDOWN, stop_reason());
EXPECT_FALSE(restarting());
EXPECT_TRUE(arc_session_runner()->IsStopped());
diff --git a/chromium/components/arc/arc_util.cc b/chromium/components/arc/arc_util.cc
index 58133783c34..0ebc98c1e2d 100644
--- a/chromium/components/arc/arc_util.cc
+++ b/chromium/components/arc/arc_util.cc
@@ -6,7 +6,7 @@
#include <string>
-#include "ash/shared/app_types.h"
+#include "ash/public/cpp/app_types.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "chromeos/chromeos_switches.h"
@@ -30,6 +30,9 @@ const base::Feature kEnableArcFeature{"EnableARC",
constexpr char kAvailabilityNone[] = "none";
constexpr char kAvailabilityInstalled[] = "installed";
constexpr char kAvailabilityOfficiallySupported[] = "officially-supported";
+constexpr char kAlwaysStart[] = "always-start";
+constexpr char kAlwaysStartWithNoPlayStore[] =
+ "always-start-with-no-play-store";
void SetArcCpuRestrictionCallback(
login_manager::ContainerCpuRestrictionState state,
@@ -49,8 +52,8 @@ bool IsArcAvailable() {
const auto* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(chromeos::switches::kArcAvailability)) {
- std::string value = command_line->GetSwitchValueASCII(
- chromeos::switches::kArcAvailability);
+ const std::string value =
+ command_line->GetSwitchValueASCII(chromeos::switches::kArcAvailability);
DCHECK(value == kAvailabilityNone || value == kAvailabilityInstalled ||
value == kAvailabilityOfficiallySupported)
<< "Unknown flag value: " << value;
@@ -63,18 +66,44 @@ bool IsArcAvailable() {
// TODO(hidehiko): Remove this and clean up whole this function, when
// session_manager supports a new flag.
return command_line->HasSwitch(chromeos::switches::kEnableArc) ||
- (command_line->HasSwitch(chromeos::switches::kArcAvailable) &&
- base::FeatureList::IsEnabled(kEnableArcFeature));
+ (command_line->HasSwitch(chromeos::switches::kArcAvailable) &&
+ base::FeatureList::IsEnabled(kEnableArcFeature));
+}
+
+bool IsWebstoreSearchEnabled() {
+ const auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(chromeos::switches::kArcAvailability)) {
+ const std::string value =
+ command_line->GetSwitchValueASCII(chromeos::switches::kArcAvailability);
+
+ return value == kAvailabilityNone;
+ }
+ return true;
+}
+
+bool IsPlayStoreAvailable() {
+ const auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
+ return true;
+
+ const std::string value =
+ command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode);
+ return value != kAlwaysStartWithNoPlayStore;
}
bool ShouldArcAlwaysStart() {
- return base::CommandLine::ForCurrentProcess()->HasSwitch(
- chromeos::switches::kArcAlwaysStart);
+ const auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
+ return false;
+ const std::string value =
+ command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode);
+ return value == kAlwaysStartWithNoPlayStore || value == kAlwaysStart;
}
-void SetArcAlwaysStartForTesting() {
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- chromeos::switches::kArcAlwaysStart);
+void SetArcAlwaysStartForTesting(bool play_store_available) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ chromeos::switches::kArcStartMode,
+ play_store_available ? kAlwaysStart : kAlwaysStartWithNoPlayStore);
}
bool IsArcKioskAvailable() {
diff --git a/chromium/components/arc/arc_util.h b/chromium/components/arc/arc_util.h
index d653b5ac43d..6b2f6acc945 100644
--- a/chromium/components/arc/arc_util.h
+++ b/chromium/components/arc/arc_util.h
@@ -34,13 +34,22 @@ namespace arc {
// check, so it is ok to access them directly.
bool IsArcAvailable();
+// Returns true if ARC is not installed and the current device is not supported
+// to run ARC.
+bool IsWebstoreSearchEnabled();
+
+// Returns true if ARC image has Play Store package.
+bool IsPlayStoreAvailable();
+
// Returns true if ARC should always start within the primary user session
// (opted in user or not), and other supported mode such as guest and Kiosk
// mode.
bool ShouldArcAlwaysStart();
// Enables to always start ARC for testing, by appending the command line flag.
-void SetArcAlwaysStartForTesting();
+// If |bool play_store_available| is not set then flag that disables ARC Play
+// Store UI is added.
+void SetArcAlwaysStartForTesting(bool play_store_available);
// Returns true if ARC is installed and running ARC kiosk apps on the current
// device is officially supported.
diff --git a/chromium/components/arc/arc_util_unittest.cc b/chromium/components/arc/arc_util_unittest.cc
index 0a52662a614..51b8a41b6da 100644
--- a/chromium/components/arc/arc_util_unittest.cc
+++ b/chromium/components/arc/arc_util_unittest.cc
@@ -7,7 +7,7 @@
#include <memory>
#include <string>
-#include "ash/shared/app_types.h"
+#include "ash/public/cpp/app_types.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
@@ -23,7 +23,7 @@
namespace arc {
namespace {
-// If an instance is created, based on the value passed to the consturctor,
+// If an instance is created, based on the value passed to the constructor,
// EnableARC feature is enabled/disabled in the scope.
class ScopedArcFeature {
public:
@@ -239,5 +239,21 @@ TEST_F(ArcUtilTest, IsArcAllowedForUser) {
EXPECT_FALSE(IsArcAllowedForUser(ephemeral_user));
}
+TEST_F(ArcUtilTest, ArcStartModeDefault) {
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ command_line->InitFromArgv({"", "--arc-availability=installed"});
+ EXPECT_FALSE(ShouldArcAlwaysStart());
+ EXPECT_TRUE(IsPlayStoreAvailable());
+}
+
+TEST_F(ArcUtilTest, ArcStartModeWithoutPlayStore) {
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ command_line->InitFromArgv(
+ {"", "--arc-availability=installed",
+ "--arc-start-mode=always-start-with-no-play-store"});
+ EXPECT_TRUE(ShouldArcAlwaysStart());
+ EXPECT_FALSE(IsPlayStoreAvailable());
+}
+
} // namespace
} // namespace arc
diff --git a/chromium/components/arc/audio/arc_audio_bridge.cc b/chromium/components/arc/audio/arc_audio_bridge.cc
index 9a0da08829a..432dad4b6b8 100644
--- a/chromium/components/arc/audio/arc_audio_bridge.cc
+++ b/chromium/components/arc/audio/arc_audio_bridge.cc
@@ -4,16 +4,49 @@
#include "components/arc/audio/arc_audio_bridge.h"
+#include <utility>
+
#include "ash/system/audio/tray_audio.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"
namespace arc {
+namespace {
+
+// Singleton factory for ArcAccessibilityHelperBridge.
+class ArcAudioBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcAudioBridge,
+ ArcAudioBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcAudioBridgeFactory";
+
+ static ArcAudioBridgeFactory* GetInstance() {
+ return base::Singleton<ArcAudioBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcAudioBridgeFactory>;
+ ArcAudioBridgeFactory() = default;
+ ~ArcAudioBridgeFactory() override = default;
+};
+
+} // namespace
+
+// static
+ArcAudioBridge* ArcAudioBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcAudioBridgeFactory::GetForBrowserContext(context);
+}
-ArcAudioBridge::ArcAudioBridge(ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this) {
- arc_bridge_service()->audio()->AddObserver(this);
+ArcAudioBridge::ArcAudioBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service), binding_(this) {
+ arc_bridge_service_->audio()->AddObserver(this);
if (chromeos::CrasAudioHandler::IsInitialized()) {
cras_audio_handler_ = chromeos::CrasAudioHandler::Get();
cras_audio_handler_->AddAudioObserver(this);
@@ -21,17 +54,29 @@ ArcAudioBridge::ArcAudioBridge(ArcBridgeService* bridge_service)
}
ArcAudioBridge::~ArcAudioBridge() {
- if (cras_audio_handler_ && chromeos::CrasAudioHandler::IsInitialized()) {
+ if (cras_audio_handler_)
cras_audio_handler_->RemoveAudioObserver(this);
- }
- arc_bridge_service()->audio()->RemoveObserver(this);
+
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->audio()->RemoveObserver(this);
}
void ArcAudioBridge::OnInstanceReady() {
mojom::AudioInstance* audio_instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->audio(), Init);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->audio(), Init);
DCHECK(audio_instance); // the instance on ARC side is too old.
- audio_instance->Init(binding_.CreateInterfacePtrAndBind());
+ mojom::AudioHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ audio_instance->Init(std::move(host_proxy));
+ available_ = true;
+}
+
+void ArcAudioBridge::OnInstanceClosed() {
+ available_ = false;
}
void ArcAudioBridge::ShowVolumeControls() {
@@ -39,6 +84,16 @@ void ArcAudioBridge::ShowVolumeControls() {
ash::TrayAudio::ShowPopUpVolumeView();
}
+void ArcAudioBridge::OnSystemVolumeUpdateRequest(int32_t percent) {
+ if (percent < 0 || percent > 100)
+ return;
+ cras_audio_handler_->SetOutputVolumePercent(percent);
+ bool is_muted =
+ percent <= cras_audio_handler_->GetOutputDefaultVolumeMuteThreshold();
+ if (cras_audio_handler_->IsOutputMuted() != is_muted)
+ cras_audio_handler_->SetOutputMute(is_muted);
+}
+
void ArcAudioBridge::OnAudioNodesChanged() {
uint64_t output_id = cras_audio_handler_->GetPrimaryActiveOutputNode();
const chromeos::AudioDevice* output_device =
@@ -84,16 +139,20 @@ void ArcAudioBridge::SendSwitchState(bool headphone_inserted,
}
DVLOG(1) << "Send switch state " << switch_state;
+ if (!available_)
+ return;
mojom::AudioInstance* audio_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->audio(), NotifySwitchState);
+ arc_bridge_service_->audio(), NotifySwitchState);
if (audio_instance)
audio_instance->NotifySwitchState(switch_state);
}
void ArcAudioBridge::SendVolumeState() {
DVLOG(1) << "Send volume " << volume_ << " muted " << muted_;
+ if (!available_)
+ return;
mojom::AudioInstance* audio_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->audio(), NotifyVolumeState);
+ arc_bridge_service_->audio(), NotifyVolumeState);
if (audio_instance)
audio_instance->NotifyVolumeState(volume_, muted_);
}
diff --git a/chromium/components/arc/audio/arc_audio_bridge.h b/chromium/components/arc/audio/arc_audio_bridge.h
index 45c0f97a4d9..c49cd8829b7 100644
--- a/chromium/components/arc/audio/arc_audio_bridge.h
+++ b/chromium/components/arc/audio/arc_audio_bridge.h
@@ -12,34 +12,39 @@
#include "components/arc/arc_service.h"
#include "components/arc/common/audio.mojom.h"
#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
class ArcBridgeService;
-class ArcAudioBridge : public ArcService,
+class ArcAudioBridge : public KeyedService,
public InstanceHolder<mojom::AudioInstance>::Observer,
public mojom::AudioHost,
public chromeos::CrasAudioHandler::AudioObserver {
public:
- explicit ArcAudioBridge(ArcBridgeService* bridge_service);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcAudioBridge* GetForBrowserContext(content::BrowserContext* context);
+
+ ArcAudioBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
~ArcAudioBridge() override;
// InstanceHolder<mojom::AudioInstance>::Observer overrides.
void OnInstanceReady() override;
+ void OnInstanceClosed() override;
// mojom::AudioHost overrides.
void ShowVolumeControls() override;
+ void OnSystemVolumeUpdateRequest(int32_t percent) override;
private:
- mojo::Binding<mojom::AudioHost> binding_;
-
- chromeos::CrasAudioHandler* cras_audio_handler_ = nullptr;
-
- int volume_ = 0; // Volume range: 0-100.
- bool muted_ = false;
-
// chromeos::CrasAudioHandler::AudioObserver overrides.
void OnAudioNodesChanged() override;
void OnOutputNodeVolumeChanged(uint64_t node_id, int volume) override;
@@ -48,6 +53,19 @@ class ArcAudioBridge : public ArcService,
void SendSwitchState(bool headphone_inserted, bool microphone_inserted);
void SendVolumeState();
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
+ mojo::Binding<mojom::AudioHost> binding_;
+
+ chromeos::CrasAudioHandler* cras_audio_handler_ = nullptr;
+
+ int volume_ = 0; // Volume range: 0-100.
+ bool muted_ = false;
+
+ // Avoids sending requests when the instance is unavailable.
+ // TODO(crbug.com/549195): Remove once the root cause is fixed.
+ bool available_ = false;
+
DISALLOW_COPY_AND_ASSIGN(ArcAudioBridge);
};
diff --git a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc b/chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc
deleted file mode 100644
index b63042df3eb..00000000000
--- a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc
+++ /dev/null
@@ -1,2173 +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/bluetooth/arc_bluetooth_bridge.h"
-
-#include <bluetooth/bluetooth.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <sys/socket.h>
-
-#include <iomanip>
-#include <string>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/arc/arc_bridge_service.h"
-#include "components/arc/bluetooth/bluetooth_type_converters.h"
-#include "device/bluetooth/bluetooth_common.h"
-#include "device/bluetooth/bluetooth_device.h"
-#include "device/bluetooth/bluetooth_gatt_connection.h"
-#include "device/bluetooth/bluetooth_gatt_notify_session.h"
-#include "device/bluetooth/bluetooth_local_gatt_characteristic.h"
-#include "device/bluetooth/bluetooth_local_gatt_descriptor.h"
-#include "device/bluetooth/bluez/bluetooth_device_bluez.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-
-using device::BluetoothAdapter;
-using device::BluetoothAdapterFactory;
-using device::BluetoothAdvertisement;
-using device::BluetoothDevice;
-using device::BluetoothDiscoveryFilter;
-using device::BluetoothDiscoverySession;
-using device::BluetoothGattConnection;
-using device::BluetoothGattNotifySession;
-using device::BluetoothGattCharacteristic;
-using device::BluetoothGattDescriptor;
-using device::BluetoothGattService;
-using device::BluetoothLocalGattCharacteristic;
-using device::BluetoothLocalGattDescriptor;
-using device::BluetoothLocalGattService;
-using device::BluetoothRemoteGattCharacteristic;
-using device::BluetoothRemoteGattDescriptor;
-using device::BluetoothRemoteGattService;
-using device::BluetoothTransport;
-using device::BluetoothUUID;
-
-namespace {
-constexpr uint32_t kGattReadPermission =
- BluetoothGattCharacteristic::Permission::PERMISSION_READ |
- BluetoothGattCharacteristic::Permission::PERMISSION_READ_ENCRYPTED |
- BluetoothGattCharacteristic::Permission::
- PERMISSION_READ_ENCRYPTED_AUTHENTICATED;
-constexpr uint32_t kGattWritePermission =
- BluetoothGattCharacteristic::Permission::PERMISSION_WRITE |
- BluetoothGattCharacteristic::Permission::PERMISSION_WRITE_ENCRYPTED |
- BluetoothGattCharacteristic::Permission::
- PERMISSION_WRITE_ENCRYPTED_AUTHENTICATED;
-constexpr int32_t kInvalidGattAttributeHandle = -1;
-constexpr int32_t kInvalidAdvertisementHandle = -1;
-// Bluetooth Specification Version 4.2 Vol 3 Part F Section 3.2.2
-// An attribute handle of value 0xFFFF is known as the maximum attribute handle.
-constexpr int32_t kMaxGattAttributeHandle = 0xFFFF;
-// Bluetooth Specification Version 4.2 Vol 3 Part F Section 3.2.9
-// The maximum length of an attribute value shall be 512 octets.
-constexpr int kMaxGattAttributeLength = 512;
-// Copied from Android at system/bt/stack/btm/btm_ble_int.h
-// https://goo.gl/k7PM6u
-constexpr uint16_t kAndroidMBluetoothVersionNumber = 95;
-// Bluetooth SDP Service Class ID List Attribute identifier
-constexpr uint16_t kServiceClassIDListAttributeID = 0x0001;
-// Timeout for Bluetooth Discovery (scan)
-// 120 seconds is used here as the upper bound of the time need to do device
-// discovery once, 20 seconds for inquiry scan and 100 seconds for page scan
-// for 100 new devices.
-constexpr base::TimeDelta kDiscoveryTimeout = base::TimeDelta::FromSeconds(120);
-// From https://www.bluetooth.com/specifications/assigned-numbers/baseband
-// The Class of Device for generic computer.
-constexpr uint32_t kBluetoothComputerClass = 0x100;
-
-using GattStatusCallback =
- base::Callback<void(arc::mojom::BluetoothGattStatus)>;
-using GattReadCallback =
- base::Callback<void(arc::mojom::BluetoothGattValuePtr)>;
-using CreateSdpRecordCallback =
- base::Callback<void(arc::mojom::BluetoothCreateSdpRecordResultPtr)>;
-using RemoveSdpRecordCallback =
- base::Callback<void(arc::mojom::BluetoothStatus)>;
-
-// Example of identifier: /org/bluez/hci0/dev_E0_CF_65_8C_86_1A/service001a
-// Convert the last 4 characters of |identifier| to an
-// int, by interpreting them as hexadecimal digits.
-int ConvertGattIdentifierToId(const std::string identifier) {
- return std::stoi(identifier.substr(identifier.size() - 4), nullptr, 16);
-}
-
-// Create GattDBElement and fill in common data for
-// Gatt Service/Characteristic/Descriptor.
-template <class RemoteGattAttribute>
-arc::mojom::BluetoothGattDBElementPtr CreateGattDBElement(
- const arc::mojom::BluetoothGattDBAttributeType type,
- const RemoteGattAttribute* attribute) {
- arc::mojom::BluetoothGattDBElementPtr element =
- arc::mojom::BluetoothGattDBElement::New();
- element->type = type;
- element->uuid = attribute->GetUUID();
- element->id = element->attribute_handle = element->start_handle =
- element->end_handle =
- ConvertGattIdentifierToId(attribute->GetIdentifier());
- element->properties = 0;
- return element;
-}
-
-template <class RemoteGattAttribute>
-RemoteGattAttribute* FindGattAttributeByUuid(
- const std::vector<RemoteGattAttribute*>& attributes,
- const BluetoothUUID& uuid) {
- auto it = std::find_if(
- attributes.begin(), attributes.end(),
- [uuid](RemoteGattAttribute* attr) { return attr->GetUUID() == uuid; });
- return it != attributes.end() ? *it : nullptr;
-}
-
-// Common success callback for GATT operations that only need to report
-// GattStatus back to Android.
-void OnGattOperationDone(const GattStatusCallback& callback) {
- callback.Run(arc::mojom::BluetoothGattStatus::GATT_SUCCESS);
-}
-
-// Common error callback for GATT operations that only need to report
-// GattStatus back to Android.
-void OnGattOperationError(const GattStatusCallback& callback,
- BluetoothGattService::GattErrorCode error_code) {
- callback.Run(mojo::ConvertTo<arc::mojom::BluetoothGattStatus>(error_code));
-}
-
-// Common success callback for ReadGattCharacteristic and ReadGattDescriptor
-void OnGattReadDone(const GattReadCallback& callback,
- const std::vector<uint8_t>& result) {
- arc::mojom::BluetoothGattValuePtr gattValue =
- arc::mojom::BluetoothGattValue::New();
- gattValue->status = arc::mojom::BluetoothGattStatus::GATT_SUCCESS;
- gattValue->value = result;
- callback.Run(std::move(gattValue));
-}
-
-// Common error callback for ReadGattCharacteristic and ReadGattDescriptor
-void OnGattReadError(const GattReadCallback& callback,
- BluetoothGattService::GattErrorCode error_code) {
- arc::mojom::BluetoothGattValuePtr gattValue =
- arc::mojom::BluetoothGattValue::New();
- gattValue->status =
- mojo::ConvertTo<arc::mojom::BluetoothGattStatus>(error_code);
- callback.Run(std::move(gattValue));
-}
-
-// Callback function for mojom::BluetoothInstance::RequestGattRead
-void OnGattServerRead(
- const BluetoothLocalGattService::Delegate::ValueCallback& success_callback,
- const BluetoothLocalGattService::Delegate::ErrorCallback& error_callback,
- arc::mojom::BluetoothGattStatus status,
- const std::vector<uint8_t>& value) {
- if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS)
- success_callback.Run(value);
- else
- error_callback.Run();
-}
-
-// Callback function for mojom::BluetoothInstance::RequestGattWrite
-void OnGattServerWrite(
- const base::Closure& success_callback,
- const BluetoothLocalGattService::Delegate::ErrorCallback& error_callback,
- arc::mojom::BluetoothGattStatus status) {
- if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS)
- success_callback.Run();
- else
- error_callback.Run();
-}
-
-bool IsGattOffsetValid(int offset) {
- return 0 <= offset && offset < kMaxGattAttributeLength;
-}
-
-// This is needed because Android only support UUID 16 bits in service data
-// section in advertising data
-uint16_t GetUUID16(const BluetoothUUID& uuid) {
- // Convert xxxxyyyy-xxxx-xxxx-xxxx-xxxxxxxxxxxx to int16 yyyy
- return std::stoi(uuid.canonical_value().substr(4, 4), nullptr, 16);
-}
-
-arc::mojom::BluetoothPropertyPtr GetDiscoveryTimeoutProperty(uint32_t timeout) {
- arc::mojom::BluetoothPropertyPtr property =
- arc::mojom::BluetoothProperty::New();
- property->set_discovery_timeout(timeout);
- return property;
-}
-
-void OnCreateServiceRecordDone(const CreateSdpRecordCallback& callback,
- uint32_t service_handle) {
- arc::mojom::BluetoothCreateSdpRecordResultPtr result =
- arc::mojom::BluetoothCreateSdpRecordResult::New();
- result->status = arc::mojom::BluetoothStatus::SUCCESS;
- result->service_handle = service_handle;
-
- callback.Run(std::move(result));
-}
-
-void OnCreateServiceRecordError(
- const CreateSdpRecordCallback& callback,
- bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) {
- arc::mojom::BluetoothCreateSdpRecordResultPtr result =
- arc::mojom::BluetoothCreateSdpRecordResult::New();
- if (error_code ==
- bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY) {
- result->status = arc::mojom::BluetoothStatus::NOT_READY;
- } else {
- result->status = arc::mojom::BluetoothStatus::FAIL;
- }
-
- callback.Run(std::move(result));
-}
-
-void OnRemoveServiceRecordDone(const RemoveSdpRecordCallback& callback) {
- callback.Run(arc::mojom::BluetoothStatus::SUCCESS);
-}
-
-void OnRemoveServiceRecordError(
- const RemoveSdpRecordCallback& callback,
- bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) {
- arc::mojom::BluetoothStatus status;
- if (error_code ==
- bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY)
- status = arc::mojom::BluetoothStatus::NOT_READY;
- else
- status = arc::mojom::BluetoothStatus::FAIL;
-
- callback.Run(status);
-}
-
-} // namespace
-
-namespace arc {
-
-ArcBluetoothBridge::ArcBluetoothBridge(ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this), weak_factory_(this) {
- if (BluetoothAdapterFactory::IsBluetoothSupported()) {
- VLOG(1) << "Registering bluetooth adapter.";
- BluetoothAdapterFactory::GetAdapter(base::Bind(
- &ArcBluetoothBridge::OnAdapterInitialized, weak_factory_.GetWeakPtr()));
- } else {
- VLOG(1) << "Bluetooth not supported.";
- }
- arc_bridge_service()->bluetooth()->AddObserver(this);
-}
-
-ArcBluetoothBridge::~ArcBluetoothBridge() {
- DCHECK(CalledOnValidThread());
-
- arc_bridge_service()->bluetooth()->RemoveObserver(this);
-
- if (bluetooth_adapter_)
- bluetooth_adapter_->RemoveObserver(this);
-}
-
-void ArcBluetoothBridge::OnAdapterInitialized(
- scoped_refptr<BluetoothAdapter> adapter) {
- DCHECK(CalledOnValidThread());
-
- // We can downcast here because we are always running on Chrome OS, and
- // so our adapter uses BlueZ.
- bluetooth_adapter_ =
- static_cast<bluez::BluetoothAdapterBlueZ*>(adapter.get());
-
- // The ARC instance was ready before the Bluetooth adapter, hence we didn't
- // register ourselves as an observer with it then. Since our adapter is
- // ready, we should register it now.
- if (!bluetooth_adapter_->HasObserver(this) &&
- arc_bridge_service()->bluetooth()->has_instance()) {
- bluetooth_adapter_->AddObserver(this);
- }
-}
-
-void ArcBluetoothBridge::OnInstanceReady() {
- mojom::BluetoothInstance* bluetooth_instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->bluetooth(), Init);
- DCHECK(bluetooth_instance);
-
- bluetooth_instance->Init(binding_.CreateInterfacePtrAndBind());
-
- // The Bluetooth adapter was ready before the ARC instance, hence we didn't
- // register ourselves as an observer with it then. Since our instance is
- // ready, we should register it now.
- if (bluetooth_adapter_ && !bluetooth_adapter_->HasObserver(this))
- bluetooth_adapter_->AddObserver(this);
-}
-
-void ArcBluetoothBridge::OnInstanceClosed() {
- if (bluetooth_adapter_)
- bluetooth_adapter_->RemoveObserver(this);
-}
-
-void ArcBluetoothBridge::SendDevice(const BluetoothDevice* device) const {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnDeviceFound);
- if (!bluetooth_instance)
- return;
-
- std::vector<mojom::BluetoothPropertyPtr> properties =
- GetDeviceProperties(mojom::BluetoothPropertyType::ALL, device);
-
- bluetooth_instance->OnDeviceFound(std::move(properties));
-
-
- if (!(device->GetType() & device::BLUETOOTH_TRANSPORT_LE))
- return;
-
- base::Optional<int8_t> rssi = device->GetInquiryRSSI();
- mojom::BluetoothAddressPtr addr;
-
- // We only want to send updated advertise data to Android only when we are
- // scanning which is checked by the validity of rssi. Here are the 2 cases
- // that we don't want to send updated advertise data to Android.
- // 1) Cached found device and 2) rssi became invalid when we stop scanning.
- if (rssi.has_value()) {
- auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnLEDeviceFound);
- if (!btle_instance)
- return;
- std::vector<mojom::BluetoothAdvertisingDataPtr> adv_data =
- GetAdvertisingData(device);
- addr = mojom::BluetoothAddress::From(device->GetAddress());
- btle_instance->OnLEDeviceFound(std::move(addr), rssi.value(),
- std::move(adv_data));
- }
-
- if (!device->IsConnected())
- return;
-
- addr = mojom::BluetoothAddress::From(device->GetAddress());
- OnGattConnectStateChanged(std::move(addr), true);
-}
-
-void ArcBluetoothBridge::DeviceAdded(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- SendDevice(device);
-}
-
-void ArcBluetoothBridge::DeviceChanged(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- SendDevice(device);
-
- if (!(device->GetType() & device::BLUETOOTH_TRANSPORT_LE))
- return;
-
- auto it = gatt_connection_cache_.find(device->GetAddress());
- bool was_connected = it != gatt_connection_cache_.end();
- bool is_connected = device->IsConnected();
-
- if (is_connected == was_connected)
- return;
-
- if (is_connected)
- gatt_connection_cache_.insert(device->GetAddress());
- else // was_connected
- gatt_connection_cache_.erase(it);
-
- mojom::BluetoothAddressPtr addr =
- mojom::BluetoothAddress::From(device->GetAddress());
- OnGattConnectStateChanged(std::move(addr), is_connected);
-}
-
-void ArcBluetoothBridge::DeviceAddressChanged(BluetoothAdapter* adapter,
- BluetoothDevice* device,
- const std::string& old_address) {
- if (old_address == device->GetAddress())
- return;
-
- if (!(device->GetType() & device::BLUETOOTH_TRANSPORT_LE))
- return;
-
- auto it = gatt_connection_cache_.find(old_address);
- if (it == gatt_connection_cache_.end())
- return;
-
- gatt_connection_cache_.erase(it);
- gatt_connection_cache_.insert(device->GetAddress());
-
- auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnLEDeviceAddressChange);
- if (!btle_instance)
- return;
-
- mojom::BluetoothAddressPtr old_addr =
- mojom::BluetoothAddress::From(old_address);
- mojom::BluetoothAddressPtr new_addr =
- mojom::BluetoothAddress::From(device->GetAddress());
- btle_instance->OnLEDeviceAddressChange(std::move(old_addr),
- std::move(new_addr));
-}
-
-void ArcBluetoothBridge::DevicePairedChanged(BluetoothAdapter* adapter,
- BluetoothDevice* device,
- bool new_paired_status) {
- DCHECK(adapter);
- DCHECK(device);
-
- mojom::BluetoothAddressPtr addr =
- mojom::BluetoothAddress::From(device->GetAddress());
-
- if (new_paired_status) {
- // OnBondStateChanged must be called with BluetoothBondState::BONDING to
- // make sure the bond state machine on Android is ready to take the
- // pair-done event. Otherwise the pair-done event will be dropped as an
- // invalid change of paired status.
- OnPairing(addr->Clone());
- OnPairedDone(std::move(addr));
- } else {
- OnForgetDone(std::move(addr));
- }
-}
-
-void ArcBluetoothBridge::DeviceRemoved(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- DCHECK(adapter);
- DCHECK(device);
-
- mojom::BluetoothAddressPtr addr =
- mojom::BluetoothAddress::From(device->GetAddress());
- OnForgetDone(std::move(addr));
-
- auto it = gatt_connection_cache_.find(device->GetAddress());
- if (it == gatt_connection_cache_.end())
- return;
-
- addr = mojom::BluetoothAddress::From(device->GetAddress());
- gatt_connection_cache_.erase(it);
- OnGattConnectStateChanged(std::move(addr), false);
-}
-
-void ArcBluetoothBridge::GattServiceAdded(BluetoothAdapter* adapter,
- BluetoothDevice* device,
- BluetoothRemoteGattService* service) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattServiceRemoved(
- BluetoothAdapter* adapter,
- BluetoothDevice* device,
- BluetoothRemoteGattService* service) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattServicesDiscovered(BluetoothAdapter* adapter,
- BluetoothDevice* device) {
- auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnSearchComplete);
- if (!btle_instance)
- return;
-
- mojom::BluetoothAddressPtr addr =
- mojom::BluetoothAddress::From(device->GetAddress());
-
- btle_instance->OnSearchComplete(std::move(addr),
- mojom::BluetoothGattStatus::GATT_SUCCESS);
-}
-
-void ArcBluetoothBridge::GattDiscoveryCompleteForService(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattService* service) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattServiceChanged(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattService* service) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattCharacteristicAdded(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattCharacteristic* characteristic) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattCharacteristicRemoved(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattCharacteristic* characteristic) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattDescriptorAdded(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattDescriptor* descriptor) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattDescriptorRemoved(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattDescriptor* descriptor) {
- // Placeholder for GATT client functionality
-}
-
-void ArcBluetoothBridge::GattCharacteristicValueChanged(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattCharacteristic* characteristic,
- const std::vector<uint8_t>& value) {
- auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnGattNotify);
- if (!btle_instance)
- return;
-
- BluetoothRemoteGattService* service = characteristic->GetService();
- BluetoothDevice* device = service->GetDevice();
- mojom::BluetoothAddressPtr address =
- mojom::BluetoothAddress::From(device->GetAddress());
- mojom::BluetoothGattServiceIDPtr service_id =
- mojom::BluetoothGattServiceID::New();
- service_id->is_primary = service->IsPrimary();
- service_id->id = mojom::BluetoothGattID::New();
- service_id->id->inst_id = ConvertGattIdentifierToId(service->GetIdentifier());
- service_id->id->uuid = service->GetUUID();
-
- mojom::BluetoothGattIDPtr char_id = mojom::BluetoothGattID::New();
- char_id->inst_id = ConvertGattIdentifierToId(characteristic->GetIdentifier());
- char_id->uuid = characteristic->GetUUID();
-
- btle_instance->OnGattNotify(std::move(address), std::move(service_id),
- std::move(char_id), true /* is_notify */, value);
-}
-
-void ArcBluetoothBridge::GattDescriptorValueChanged(
- BluetoothAdapter* adapter,
- BluetoothRemoteGattDescriptor* descriptor,
- const std::vector<uint8_t>& value) {
- // Placeholder for GATT client functionality
-}
-
-template <class LocalGattAttribute>
-void ArcBluetoothBridge::OnGattAttributeReadRequest(
- const BluetoothDevice* device,
- const LocalGattAttribute* attribute,
- int offset,
- const ValueCallback& success_callback,
- const ErrorCallback& error_callback) {
- DCHECK(CalledOnValidThread());
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), RequestGattRead);
- if (!bluetooth_instance || !IsGattOffsetValid(offset)) {
- error_callback.Run();
- return;
- }
-
- DCHECK(gatt_handle_.find(attribute->GetIdentifier()) != gatt_handle_.end());
-
- bluetooth_instance->RequestGattRead(
- mojom::BluetoothAddress::From(device->GetAddress()),
- gatt_handle_[attribute->GetIdentifier()], offset, false /* is_long */,
- base::Bind(&OnGattServerRead, success_callback, error_callback));
-}
-
-template <class LocalGattAttribute>
-void ArcBluetoothBridge::OnGattAttributeWriteRequest(
- const BluetoothDevice* device,
- const LocalGattAttribute* attribute,
- const std::vector<uint8_t>& value,
- int offset,
- const base::Closure& success_callback,
- const ErrorCallback& error_callback) {
- DCHECK(CalledOnValidThread());
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), RequestGattWrite);
- if (!bluetooth_instance || !IsGattOffsetValid(offset)) {
- error_callback.Run();
- return;
- }
-
- DCHECK(gatt_handle_.find(attribute->GetIdentifier()) != gatt_handle_.end());
-
- bluetooth_instance->RequestGattWrite(
- mojom::BluetoothAddress::From(device->GetAddress()),
- gatt_handle_[attribute->GetIdentifier()], offset, value,
- base::Bind(&OnGattServerWrite, success_callback, error_callback));
-}
-
-void ArcBluetoothBridge::OnCharacteristicReadRequest(
- const BluetoothDevice* device,
- const BluetoothLocalGattCharacteristic* characteristic,
- int offset,
- const ValueCallback& callback,
- const ErrorCallback& error_callback) {
- OnGattAttributeReadRequest(device, characteristic, offset, callback,
- error_callback);
-}
-
-void ArcBluetoothBridge::OnCharacteristicWriteRequest(
- const BluetoothDevice* device,
- const BluetoothLocalGattCharacteristic* characteristic,
- const std::vector<uint8_t>& value,
- int offset,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- OnGattAttributeWriteRequest(device, characteristic, value, offset, callback,
- error_callback);
-}
-
-void ArcBluetoothBridge::OnDescriptorReadRequest(
- const BluetoothDevice* device,
- const BluetoothLocalGattDescriptor* descriptor,
- int offset,
- const ValueCallback& callback,
- const ErrorCallback& error_callback) {
- OnGattAttributeReadRequest(device, descriptor, offset, callback,
- error_callback);
-}
-
-void ArcBluetoothBridge::OnDescriptorWriteRequest(
- const BluetoothDevice* device,
- const BluetoothLocalGattDescriptor* descriptor,
- const std::vector<uint8_t>& value,
- int offset,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- OnGattAttributeWriteRequest(device, descriptor, value, offset, callback,
- error_callback);
-}
-
-void ArcBluetoothBridge::OnNotificationsStart(
- const BluetoothDevice* device,
- const BluetoothLocalGattCharacteristic* characteristic) {}
-
-void ArcBluetoothBridge::OnNotificationsStop(
- const BluetoothDevice* device,
- const BluetoothLocalGattCharacteristic* characteristic) {}
-
-void ArcBluetoothBridge::EnableAdapter(const EnableAdapterCallback& callback) {
- DCHECK(bluetooth_adapter_);
- if (!bluetooth_adapter_->IsPowered()) {
- bluetooth_adapter_->SetPowered(
- true, base::Bind(&ArcBluetoothBridge::OnPoweredOn,
- weak_factory_.GetWeakPtr(), callback),
- base::Bind(&ArcBluetoothBridge::OnPoweredError,
- weak_factory_.GetWeakPtr(), callback));
- return;
- }
-
- OnPoweredOn(callback);
-}
-
-void ArcBluetoothBridge::DisableAdapter(
- const DisableAdapterCallback& callback) {
- DCHECK(bluetooth_adapter_);
- bluetooth_adapter_->SetPowered(
- false, base::Bind(&ArcBluetoothBridge::OnPoweredOff,
- weak_factory_.GetWeakPtr(), callback),
- base::Bind(&ArcBluetoothBridge::OnPoweredError,
- weak_factory_.GetWeakPtr(), callback));
-}
-
-void ArcBluetoothBridge::GetAdapterProperty(mojom::BluetoothPropertyType type) {
- DCHECK(bluetooth_adapter_);
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnAdapterProperties);
- if (!bluetooth_instance)
- return;
-
- std::vector<mojom::BluetoothPropertyPtr> properties =
- GetAdapterProperties(type);
-
- bluetooth_instance->OnAdapterProperties(mojom::BluetoothStatus::SUCCESS,
- std::move(properties));
-}
-
-void ArcBluetoothBridge::OnSetDiscoverable(bool discoverable,
- bool success,
- uint32_t timeout) {
- DCHECK(CalledOnValidThread());
-
- if (success && discoverable && timeout > 0) {
- discoverable_off_timer_.Start(
- FROM_HERE, base::TimeDelta::FromSeconds(timeout),
- base::Bind(&ArcBluetoothBridge::SetDiscoverable,
- weak_factory_.GetWeakPtr(), false, 0));
- }
-
- auto status =
- success ? mojom::BluetoothStatus::SUCCESS : mojom::BluetoothStatus::FAIL;
- OnSetAdapterProperty(status, GetDiscoveryTimeoutProperty(timeout));
-}
-
-// Set discoverable state to on / off.
-// In case of turning on, start timer to turn it back off in |timeout| seconds.
-void ArcBluetoothBridge::SetDiscoverable(bool discoverable, uint32_t timeout) {
- DCHECK(bluetooth_adapter_);
- DCHECK(CalledOnValidThread());
- DCHECK(!discoverable || timeout == 0);
-
- bool currently_discoverable = bluetooth_adapter_->IsDiscoverable();
-
- if (!discoverable && !currently_discoverable)
- return;
-
- if (discoverable && currently_discoverable) {
- if (base::TimeDelta::FromSeconds(timeout) >
- discoverable_off_timer_.GetCurrentDelay()) {
- // Restart discoverable_off_timer_ if new timeout is greater
- OnSetDiscoverable(true, true, timeout);
- } else {
- // Just send message to Android if new timeout is lower.
- OnSetAdapterProperty(mojom::BluetoothStatus::SUCCESS,
- GetDiscoveryTimeoutProperty(timeout));
- }
- return;
- }
-
- bluetooth_adapter_->SetDiscoverable(
- discoverable,
- base::Bind(&ArcBluetoothBridge::OnSetDiscoverable,
- weak_factory_.GetWeakPtr(), discoverable, true, timeout),
- base::Bind(&ArcBluetoothBridge::OnSetDiscoverable,
- weak_factory_.GetWeakPtr(), discoverable, false, timeout));
-}
-
-void ArcBluetoothBridge::OnSetAdapterProperty(
- mojom::BluetoothStatus status,
- mojom::BluetoothPropertyPtr property) {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnAdapterProperties);
- DCHECK(bluetooth_instance);
-
- std::vector<arc::mojom::BluetoothPropertyPtr> properties;
- properties.push_back(std::move(property));
-
- bluetooth_instance->OnAdapterProperties(status, std::move(properties));
-}
-
-void ArcBluetoothBridge::SetAdapterProperty(
- mojom::BluetoothPropertyPtr property) {
- DCHECK(bluetooth_adapter_);
-
- if (property->is_discovery_timeout()) {
- uint32_t discovery_timeout = property->get_discovery_timeout();
- if (discovery_timeout > 0) {
- SetDiscoverable(true, discovery_timeout);
- } else {
- OnSetAdapterProperty(mojom::BluetoothStatus::PARM_INVALID,
- std::move(property));
- }
- } else if (property->is_bdname()) {
- auto property_clone = property.Clone();
- bluetooth_adapter_->SetName(
- property->get_bdname(),
- base::Bind(&ArcBluetoothBridge::OnSetAdapterProperty,
- weak_factory_.GetWeakPtr(), mojom::BluetoothStatus::SUCCESS,
- base::Passed(&property)),
- base::Bind(&ArcBluetoothBridge::OnSetAdapterProperty,
- weak_factory_.GetWeakPtr(), mojom::BluetoothStatus::FAIL,
- base::Passed(&property_clone)));
- } else if (property->is_adapter_scan_mode()) {
- // Android will set adapter scan mode in these 3 situations.
- // 1) Set to BT_SCAN_MODE_NONE just before turning BT off.
- // 2) Set to BT_SCAN_MODE_CONNECTABLE just after turning on.
- // 3) Set to BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE just before set the
- // discoverable timeout.
- // Since turning BT off/on implied scan mode none/connectable and setting
- // discovery timeout implied scan mode discoverable, we don't need to
- // do anything here. We will just call success callback in this case.
- OnSetAdapterProperty(mojom::BluetoothStatus::SUCCESS, std::move(property));
- } else {
- // Android does not set any other property type.
- OnSetAdapterProperty(mojom::BluetoothStatus::UNSUPPORTED,
- std::move(property));
- }
-}
-
-void ArcBluetoothBridge::GetRemoteDeviceProperty(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothPropertyType type) {
- DCHECK(bluetooth_adapter_);
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnRemoteDeviceProperties);
- if (!bluetooth_instance)
- return;
-
- std::string addr_str = remote_addr->To<std::string>();
- BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str);
-
- std::vector<mojom::BluetoothPropertyPtr> properties =
- GetDeviceProperties(type, device);
- mojom::BluetoothStatus status = mojom::BluetoothStatus::SUCCESS;
-
- if (!device) {
- VLOG(1) << __func__ << ": device " << addr_str << " not available";
- status = mojom::BluetoothStatus::FAIL;
- }
-
- bluetooth_instance->OnRemoteDeviceProperties(status, std::move(remote_addr),
- std::move(properties));
-}
-
-void ArcBluetoothBridge::SetRemoteDeviceProperty(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothPropertyPtr property) {
- DCHECK(bluetooth_adapter_);
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnRemoteDeviceProperties);
- if (!bluetooth_instance)
- return;
-
- // Unsupported. Only used by Android hidden API, BluetoothDevice.SetAlias().
- // And only Android Settings App / Android TV / NFC used that.
- bluetooth_instance->OnRemoteDeviceProperties(
- mojom::BluetoothStatus::UNSUPPORTED, std::move(remote_addr),
- std::vector<mojom::BluetoothPropertyPtr>());
-}
-
-void ArcBluetoothBridge::GetRemoteServiceRecord(
- mojom::BluetoothAddressPtr remote_addr,
- const BluetoothUUID& uuid) {
- // TODO(smbarber): Implement GetRemoteServiceRecord
-}
-
-void ArcBluetoothBridge::GetRemoteServices(
- mojom::BluetoothAddressPtr remote_addr) {
- // TODO(smbarber): Implement GetRemoteServices
-}
-
-void ArcBluetoothBridge::StartDiscovery() {
- DCHECK(bluetooth_adapter_);
- DCHECK(CalledOnValidThread());
-
- if (discovery_session_) {
- LOG(ERROR) << "Discovery session already running; Reset timeout.";
- discovery_off_timer_.Start(FROM_HERE, kDiscoveryTimeout,
- base::Bind(&ArcBluetoothBridge::CancelDiscovery,
- weak_factory_.GetWeakPtr()));
- SendCachedDevicesFound();
- return;
- }
-
- bluetooth_adapter_->StartDiscoverySession(
- base::Bind(&ArcBluetoothBridge::OnDiscoveryStarted,
- weak_factory_.GetWeakPtr()),
- base::Bind(&ArcBluetoothBridge::OnDiscoveryError,
- weak_factory_.GetWeakPtr()));
-}
-
-void ArcBluetoothBridge::CancelDiscovery() {
- if (!discovery_session_) {
- return;
- }
-
- discovery_session_->Stop(base::Bind(&ArcBluetoothBridge::OnDiscoveryStopped,
- weak_factory_.GetWeakPtr()),
- base::Bind(&ArcBluetoothBridge::OnDiscoveryError,
- weak_factory_.GetWeakPtr()));
-}
-
-void ArcBluetoothBridge::OnPoweredOn(
- const base::Callback<void(mojom::BluetoothAdapterState)>& callback) const {
- callback.Run(mojom::BluetoothAdapterState::ON);
- SendCachedPairedDevices();
-}
-
-void ArcBluetoothBridge::OnPoweredOff(
- const base::Callback<void(mojom::BluetoothAdapterState)>& callback) const {
- callback.Run(mojom::BluetoothAdapterState::OFF);
-}
-
-void ArcBluetoothBridge::OnPoweredError(
- const base::Callback<void(mojom::BluetoothAdapterState)>& callback) const {
- LOG(WARNING) << "failed to change power state";
-
- callback.Run(bluetooth_adapter_->IsPowered()
- ? mojom::BluetoothAdapterState::ON
- : mojom::BluetoothAdapterState::OFF);
-}
-
-void ArcBluetoothBridge::OnDiscoveryStarted(
- std::unique_ptr<BluetoothDiscoverySession> session) {
- DCHECK(CalledOnValidThread());
-
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnDiscoveryStateChanged);
- if (!bluetooth_instance)
- return;
-
- discovery_session_ = std::move(session);
-
- // We need to set timer to turn device discovery off because of the difference
- // between Android API (do device discovery once) and Chrome API (do device
- // discovery until user turns it off).
- discovery_off_timer_.Start(FROM_HERE, kDiscoveryTimeout,
- base::Bind(&ArcBluetoothBridge::CancelDiscovery,
- weak_factory_.GetWeakPtr()));
-
- bluetooth_instance->OnDiscoveryStateChanged(
- mojom::BluetoothDiscoveryState::STARTED);
-
- SendCachedDevicesFound();
-}
-
-void ArcBluetoothBridge::OnDiscoveryStopped() {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnDiscoveryStateChanged);
- if (!bluetooth_instance)
- return;
-
- discovery_session_.reset();
- discovery_off_timer_.Stop();
-
- bluetooth_instance->OnDiscoveryStateChanged(
- mojom::BluetoothDiscoveryState::STOPPED);
-}
-
-void ArcBluetoothBridge::CreateBond(mojom::BluetoothAddressPtr addr,
- int32_t transport) {
- std::string addr_str = addr->To<std::string>();
- BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str);
- if (!device || !device->IsPairable()) {
- VLOG(1) << __func__ << ": device " << addr_str
- << " is no longer valid or pairable";
- OnPairedError(std::move(addr), BluetoothDevice::ERROR_FAILED);
- return;
- }
-
- if (device->IsPaired()) {
- OnPairedDone(std::move(addr));
- return;
- }
-
- // Use the default pairing delegate which is the delegate registered and owned
- // by ash.
- BluetoothDevice::PairingDelegate* delegate =
- bluetooth_adapter_->DefaultPairingDelegate();
-
- if (!delegate) {
- OnPairedError(std::move(addr), BluetoothDevice::ERROR_FAILED);
- return;
- }
-
- // If pairing finished successfully, DevicePairedChanged will notify Android
- // on paired state change event, so DoNothing is passed as a success callback.
- device->Pair(delegate, base::Bind(&base::DoNothing),
- base::Bind(&ArcBluetoothBridge::OnPairedError,
- weak_factory_.GetWeakPtr(), base::Passed(&addr)));
-}
-
-void ArcBluetoothBridge::RemoveBond(mojom::BluetoothAddressPtr addr) {
- // Forget the device if it is no longer valid or not even paired.
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(addr->To<std::string>());
- if (!device || !device->IsPaired()) {
- OnForgetDone(std::move(addr));
- return;
- }
-
- // If unpairing finished successfully, DevicePairedChanged will notify Android
- // on paired state change event, so DoNothing is passed as a success callback.
- device->Forget(base::Bind(&base::DoNothing),
- base::Bind(&ArcBluetoothBridge::OnForgetError,
- weak_factory_.GetWeakPtr(), base::Passed(&addr)));
-}
-
-void ArcBluetoothBridge::CancelBond(mojom::BluetoothAddressPtr addr) {
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(addr->To<std::string>());
- if (!device) {
- OnForgetDone(std::move(addr));
- return;
- }
-
- device->CancelPairing();
- OnForgetDone(std::move(addr));
-}
-
-void ArcBluetoothBridge::GetConnectionState(
- mojom::BluetoothAddressPtr addr,
- const GetConnectionStateCallback& callback) {
- if (!bluetooth_adapter_) {
- callback.Run(false);
- return;
- }
-
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(addr->To<std::string>());
- if (!device) {
- callback.Run(false);
- return;
- }
-
- callback.Run(device->IsConnected());
-}
-
-void ArcBluetoothBridge::StartLEScan() {
- DCHECK(bluetooth_adapter_);
- if (discovery_session_) {
- LOG(WARNING) << "Discovery session already running; leaving alone";
- SendCachedDevicesFound();
- return;
- }
- bluetooth_adapter_->StartDiscoverySessionWithFilter(
- base::MakeUnique<BluetoothDiscoveryFilter>(
- device::BLUETOOTH_TRANSPORT_LE),
- base::Bind(&ArcBluetoothBridge::OnDiscoveryStarted,
- weak_factory_.GetWeakPtr()),
- base::Bind(&ArcBluetoothBridge::OnDiscoveryError,
- weak_factory_.GetWeakPtr()));
-}
-
-void ArcBluetoothBridge::StopLEScan() {
- CancelDiscovery();
-}
-
-void ArcBluetoothBridge::OnGattConnectStateChanged(
- mojom::BluetoothAddressPtr addr,
- bool connected) const {
- auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnLEConnectionStateChange);
- if (!btle_instance)
- return;
-
- DCHECK(addr);
-
- btle_instance->OnLEConnectionStateChange(std::move(addr), connected);
-}
-
-void ArcBluetoothBridge::OnGattConnected(
- mojom::BluetoothAddressPtr addr,
- std::unique_ptr<BluetoothGattConnection> connection) {
- DCHECK(CalledOnValidThread());
- gatt_connections_[addr->To<std::string>()] = std::move(connection);
- OnGattConnectStateChanged(std::move(addr), true);
-}
-
-void ArcBluetoothBridge::OnGattConnectError(
- mojom::BluetoothAddressPtr addr,
- BluetoothDevice::ConnectErrorCode error_code) const {
- OnGattConnectStateChanged(std::move(addr), false);
-}
-
-void ArcBluetoothBridge::OnGattDisconnected(
- mojom::BluetoothAddressPtr addr) {
- DCHECK(CalledOnValidThread());
- auto it = gatt_connections_.find(addr->To<std::string>());
- if (it == gatt_connections_.end()) {
- LOG(WARNING) << "OnGattDisconnected called, "
- << "but no gatt connection was found";
- } else {
- gatt_connections_.erase(it);
- }
-
- OnGattConnectStateChanged(std::move(addr), false);
-}
-
-void ArcBluetoothBridge::ConnectLEDevice(
- mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnLEConnectionStateChange);
- if (!bluetooth_instance)
- return;
-
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
-
- if (!device) {
- LOG(ERROR) << "Unknown device " << remote_addr->To<std::string>();
- OnGattConnectError(std::move(remote_addr),
- BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
- return;
- }
-
- if (device->IsConnected()) {
- bluetooth_instance->OnLEConnectionStateChange(std::move(remote_addr), true);
- return;
- }
-
- // Also pass disconnect callback in error case since it would be disconnected
- // anyway.
- mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone();
- device->CreateGattConnection(
- base::Bind(&ArcBluetoothBridge::OnGattConnected,
- weak_factory_.GetWeakPtr(), base::Passed(&remote_addr)),
- base::Bind(&ArcBluetoothBridge::OnGattConnectError,
- weak_factory_.GetWeakPtr(), base::Passed(&remote_addr_clone)));
-}
-
-void ArcBluetoothBridge::DisconnectLEDevice(
- mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnLEConnectionStateChange);
- if (!bluetooth_instance)
- return;
-
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
-
- if (!device || !device->IsConnected()) {
- bluetooth_instance->OnLEConnectionStateChange(std::move(remote_addr),
- false);
- return;
- }
-
- mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone();
- device->Disconnect(
- base::Bind(&ArcBluetoothBridge::OnGattDisconnected,
- weak_factory_.GetWeakPtr(), base::Passed(&remote_addr)),
- base::Bind(&ArcBluetoothBridge::OnGattDisconnected,
- weak_factory_.GetWeakPtr(), base::Passed(&remote_addr_clone)));
-}
-
-void ArcBluetoothBridge::SearchService(mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnSearchComplete);
- if (!bluetooth_instance)
- return;
-
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
- if (!device) {
- LOG(ERROR) << "Unknown device " << remote_addr->To<std::string>();
- bluetooth_instance->OnSearchComplete(
- std::move(remote_addr), mojom::BluetoothGattStatus::GATT_FAILURE);
- return;
- }
-
- // Call the callback if discovery is completed
- if (device->IsGattServicesDiscoveryComplete()) {
- bluetooth_instance->OnSearchComplete(
- std::move(remote_addr), mojom::BluetoothGattStatus::GATT_SUCCESS);
- return;
- }
-
- // Discard result. Will call the callback when discovery is completed.
- device->GetGattServices();
-}
-
-void ArcBluetoothBridge::OnStartLEListenDone(
- const StartLEListenCallback& callback,
- scoped_refptr<BluetoothAdvertisement> advertisement) {
- DCHECK(CalledOnValidThread());
- advertisment_ = advertisement;
- callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
-}
-
-void ArcBluetoothBridge::OnStartLEListenError(
- const StartLEListenCallback& callback,
- BluetoothAdvertisement::ErrorCode error_code) {
- DCHECK(CalledOnValidThread());
- advertisment_ = nullptr;
- callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
-}
-
-void ArcBluetoothBridge::StartLEListen(const StartLEListenCallback& callback) {
- DCHECK(CalledOnValidThread());
- std::unique_ptr<BluetoothAdvertisement::Data> adv_data =
- base::MakeUnique<BluetoothAdvertisement::Data>(
- BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
- bluetooth_adapter_->RegisterAdvertisement(
- std::move(adv_data), base::Bind(&ArcBluetoothBridge::OnStartLEListenDone,
- weak_factory_.GetWeakPtr(), callback),
- base::Bind(&ArcBluetoothBridge::OnStartLEListenError,
- weak_factory_.GetWeakPtr(), callback));
-}
-
-void ArcBluetoothBridge::OnStopLEListenDone(
- const StopLEListenCallback& callback) {
- DCHECK(CalledOnValidThread());
- advertisment_ = nullptr;
- callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
-}
-
-void ArcBluetoothBridge::OnStopLEListenError(
- const StopLEListenCallback& callback,
- BluetoothAdvertisement::ErrorCode error_code) {
- DCHECK(CalledOnValidThread());
- advertisment_ = nullptr;
- callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
-}
-
-void ArcBluetoothBridge::StopLEListen(const StopLEListenCallback& callback) {
- if (!advertisment_) {
- OnStopLEListenError(
- callback,
- BluetoothAdvertisement::ErrorCode::ERROR_ADVERTISEMENT_DOES_NOT_EXIST);
- return;
- }
- advertisment_->Unregister(base::Bind(&ArcBluetoothBridge::OnStopLEListenDone,
- weak_factory_.GetWeakPtr(), callback),
- base::Bind(&ArcBluetoothBridge::OnStopLEListenError,
- weak_factory_.GetWeakPtr(), callback));
-}
-
-void ArcBluetoothBridge::GetGattDB(mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnGetGattDB);
- if (!bluetooth_instance)
- return;
-
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
- std::vector<mojom::BluetoothGattDBElementPtr> db;
-
- if (!device) {
- LOG(ERROR) << "Unknown device " << remote_addr->To<std::string>();
- bluetooth_instance->OnGetGattDB(std::move(remote_addr), std::move(db));
- return;
- }
-
- for (auto* service : device->GetGattServices()) {
- mojom::BluetoothGattDBElementPtr service_element = CreateGattDBElement(
- service->IsPrimary()
- ? mojom::BluetoothGattDBAttributeType::BTGATT_DB_PRIMARY_SERVICE
- : mojom::BluetoothGattDBAttributeType::BTGATT_DB_SECONDARY_SERVICE,
- service);
-
- const auto& characteristics = service->GetCharacteristics();
- if (characteristics.size() > 0) {
- const auto& descriptors = characteristics.back()->GetDescriptors();
- service_element->start_handle =
- ConvertGattIdentifierToId(characteristics.front()->GetIdentifier());
- service_element->end_handle = ConvertGattIdentifierToId(
- descriptors.size() > 0 ? descriptors.back()->GetIdentifier()
- : characteristics.back()->GetIdentifier());
- }
- db.push_back(std::move(service_element));
-
- for (auto* characteristic : characteristics) {
- mojom::BluetoothGattDBElementPtr characteristic_element =
- CreateGattDBElement(
- mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
- characteristic);
- characteristic_element->properties = characteristic->GetProperties();
- db.push_back(std::move(characteristic_element));
-
- for (auto* descriptor : characteristic->GetDescriptors()) {
- db.push_back(CreateGattDBElement(
- mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR,
- descriptor));
- }
- }
- }
-
- bluetooth_instance->OnGetGattDB(std::move(remote_addr), std::move(db));
-}
-
-BluetoothRemoteGattCharacteristic* ArcBluetoothBridge::FindGattCharacteristic(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id) const {
- DCHECK(remote_addr);
- DCHECK(service_id);
- DCHECK(char_id);
-
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
- if (!device)
- return nullptr;
-
- BluetoothRemoteGattService* service =
- FindGattAttributeByUuid(device->GetGattServices(), service_id->id->uuid);
- if (!service)
- return nullptr;
-
- return FindGattAttributeByUuid(service->GetCharacteristics(), char_id->uuid);
-}
-
-BluetoothRemoteGattDescriptor* ArcBluetoothBridge::FindGattDescriptor(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattIDPtr desc_id) const {
- BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
- std::move(remote_addr), std::move(service_id), std::move(char_id));
- if (!characteristic)
- return nullptr;
-
- return FindGattAttributeByUuid(characteristic->GetDescriptors(),
- desc_id->uuid);
-}
-
-void ArcBluetoothBridge::ReadGattCharacteristic(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- const ReadGattCharacteristicCallback& callback) {
- BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
- std::move(remote_addr), std::move(service_id), std::move(char_id));
- DCHECK(characteristic);
- DCHECK(characteristic->GetPermissions() & kGattReadPermission);
-
- characteristic->ReadRemoteCharacteristic(
- base::Bind(&OnGattReadDone, callback),
- base::Bind(&OnGattReadError, callback));
-}
-
-void ArcBluetoothBridge::WriteGattCharacteristic(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattValuePtr value,
- const WriteGattCharacteristicCallback& callback) {
- BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
- std::move(remote_addr), std::move(service_id), std::move(char_id));
- DCHECK(characteristic);
- DCHECK(characteristic->GetPermissions() & kGattWritePermission);
-
- characteristic->WriteRemoteCharacteristic(
- value->value, base::Bind(&OnGattOperationDone, callback),
- base::Bind(&OnGattOperationError, callback));
-}
-
-void ArcBluetoothBridge::ReadGattDescriptor(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattIDPtr desc_id,
- const ReadGattDescriptorCallback& callback) {
- BluetoothRemoteGattDescriptor* descriptor =
- FindGattDescriptor(std::move(remote_addr), std::move(service_id),
- std::move(char_id), std::move(desc_id));
- DCHECK(descriptor);
- DCHECK(descriptor->GetPermissions() & kGattReadPermission);
-
- descriptor->ReadRemoteDescriptor(base::Bind(&OnGattReadDone, callback),
- base::Bind(&OnGattReadError, callback));
-}
-
-void ArcBluetoothBridge::WriteGattDescriptor(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattIDPtr desc_id,
- mojom::BluetoothGattValuePtr value,
- const WriteGattDescriptorCallback& callback) {
- BluetoothRemoteGattDescriptor* descriptor =
- FindGattDescriptor(std::move(remote_addr), std::move(service_id),
- std::move(char_id), std::move(desc_id));
- DCHECK(descriptor);
- DCHECK(descriptor->GetPermissions() & kGattWritePermission);
-
- // To register / deregister GATT notification, we need to
- // 1) Write to CCC Descriptor to enable/disable the notification
- // 2) Ask BT hw to register / deregister the notification
- // The Chrome API groups both steps into one API, and does not support writing
- // directly to the CCC Descriptor. Therefore, until we fix
- // https://crbug.com/622832, we return successfully when we encounter this.
- // TODO(http://crbug.com/622832)
- if (descriptor->GetUUID() ==
- BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) {
- OnGattOperationDone(callback);
- return;
- }
-
- descriptor->WriteRemoteDescriptor(
- value->value, base::Bind(&OnGattOperationDone, callback),
- base::Bind(&OnGattOperationError, callback));
-}
-
-void ArcBluetoothBridge::OnGattNotifyStartDone(
- const RegisterForGattNotificationCallback& callback,
- const std::string char_string_id,
- std::unique_ptr<BluetoothGattNotifySession> notify_session) {
- DCHECK(CalledOnValidThread());
- notification_session_[char_string_id] = std::move(notify_session);
- callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
-}
-
-void ArcBluetoothBridge::RegisterForGattNotification(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- const RegisterForGattNotificationCallback& callback) {
- BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
- std::move(remote_addr), std::move(service_id), std::move(char_id));
-
- if (!characteristic) {
- LOG(WARNING) << __func__ << " Characteristic is not existed.";
- return;
- }
-
- characteristic->StartNotifySession(
- base::Bind(&ArcBluetoothBridge::OnGattNotifyStartDone,
- weak_factory_.GetWeakPtr(), callback,
- characteristic->GetIdentifier()),
- base::Bind(&OnGattOperationError, callback));
-}
-
-void ArcBluetoothBridge::DeregisterForGattNotification(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- const DeregisterForGattNotificationCallback& callback) {
- DCHECK(CalledOnValidThread());
-
- BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
- std::move(remote_addr), std::move(service_id), std::move(char_id));
-
- if (!characteristic) {
- LOG(WARNING) << __func__ << " Characteristic is not existed.";
- return;
- }
-
- std::string char_id_str = characteristic->GetIdentifier();
- std::unique_ptr<BluetoothGattNotifySession> notify =
- std::move(notification_session_[char_id_str]);
- notification_session_.erase(char_id_str);
- notify->Stop(base::Bind(&OnGattOperationDone, callback));
-}
-
-void ArcBluetoothBridge::ReadRemoteRssi(
- mojom::BluetoothAddressPtr remote_addr,
- const ReadRemoteRssiCallback& callback) {
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
- if (!device) {
- callback.Run(mojom::kUnknownPower);
- return;
- }
- callback.Run(device->GetInquiryRSSI().value_or(mojom::kUnknownPower));
-}
-
-void ArcBluetoothBridge::OpenBluetoothSocket(
- const OpenBluetoothSocketCallback& callback) {
- base::ScopedFD sock(socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM));
- if (!sock.is_valid()) {
- LOG(ERROR) << "Failed to open socket.";
- callback.Run(mojo::ScopedHandle());
- return;
- }
- mojo::edk::ScopedPlatformHandle platform_handle{
- mojo::edk::PlatformHandle(sock.release())};
- MojoHandle wrapped_handle;
- MojoResult wrap_result = mojo::edk::CreatePlatformHandleWrapper(
- std::move(platform_handle), &wrapped_handle);
- if (wrap_result != MOJO_RESULT_OK) {
- LOG(ERROR) << "Failed to wrap handles. Closing: " << wrap_result;
- callback.Run(mojo::ScopedHandle());
- return;
- }
- mojo::ScopedHandle scoped_handle{mojo::Handle(wrapped_handle)};
-
- callback.Run(std::move(scoped_handle));
-}
-
-bool ArcBluetoothBridge::IsGattServerAttributeHandleAvailable(int need) {
- return gatt_server_attribute_next_handle_ + need <= kMaxGattAttributeHandle;
-}
-
-int32_t ArcBluetoothBridge::GetNextGattServerAttributeHandle() {
- return IsGattServerAttributeHandleAvailable(1)
- ? ++gatt_server_attribute_next_handle_
- : kInvalidGattAttributeHandle;
-}
-
-template <class LocalGattAttribute>
-int32_t ArcBluetoothBridge::CreateGattAttributeHandle(
- LocalGattAttribute* attribute) {
- DCHECK(CalledOnValidThread());
- if (!attribute)
- return kInvalidGattAttributeHandle;
- int32_t handle = GetNextGattServerAttributeHandle();
- if (handle == kInvalidGattAttributeHandle)
- return kInvalidGattAttributeHandle;
- const std::string& identifier = attribute->GetIdentifier();
- gatt_identifier_[handle] = identifier;
- gatt_handle_[identifier] = handle;
- return handle;
-}
-
-void ArcBluetoothBridge::AddService(mojom::BluetoothGattServiceIDPtr service_id,
- int32_t num_handles,
- const AddServiceCallback& callback) {
- if (!IsGattServerAttributeHandleAvailable(num_handles)) {
- callback.Run(kInvalidGattAttributeHandle);
- return;
- }
- base::WeakPtr<BluetoothLocalGattService> service =
- BluetoothLocalGattService::Create(
- bluetooth_adapter_.get(), service_id->id->uuid,
- service_id->is_primary, nullptr /* included_service */,
- this /* delegate */);
- callback.Run(CreateGattAttributeHandle(service.get()));
-}
-
-void ArcBluetoothBridge::AddCharacteristic(
- int32_t service_handle,
- const BluetoothUUID& uuid,
- int32_t properties,
- int32_t permissions,
- const AddCharacteristicCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
- if (!IsGattServerAttributeHandleAvailable(1)) {
- callback.Run(kInvalidGattAttributeHandle);
- return;
- }
- base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic =
- BluetoothLocalGattCharacteristic::Create(
- uuid, properties, permissions,
- bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]));
- int32_t characteristic_handle =
- CreateGattAttributeHandle(characteristic.get());
- last_characteristic_[service_handle] = characteristic_handle;
- callback.Run(characteristic_handle);
-}
-
-void ArcBluetoothBridge::AddDescriptor(int32_t service_handle,
- const BluetoothUUID& uuid,
- int32_t permissions,
- const AddDescriptorCallback& callback) {
- DCHECK(CalledOnValidThread());
- if (!IsGattServerAttributeHandleAvailable(1)) {
- callback.Run(kInvalidGattAttributeHandle);
- return;
- }
- // Chrome automatically adds a CCC Descriptor to a characteristic when needed.
- // We will generate a bogus handle for Android.
- if (uuid ==
- BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) {
- int32_t handle = GetNextGattServerAttributeHandle();
- callback.Run(handle);
- return;
- }
-
- DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
- BluetoothLocalGattService* service =
- bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]);
- DCHECK(service);
- // Since the Android API does not give information about which characteristic
- // is the parent of the new descriptor, we assume that it would be the last
- // characteristic that was added to the given service. This matches the
- // Android framework code at android/bluetooth/BluetoothGattServer.java#594.
- // Link: https://goo.gl/cJZl1u
- DCHECK(last_characteristic_.find(service_handle) !=
- last_characteristic_.end());
- int32_t last_characteristic_handle = last_characteristic_[service_handle];
-
- DCHECK(gatt_identifier_.find(last_characteristic_handle) !=
- gatt_identifier_.end());
- BluetoothLocalGattCharacteristic* characteristic =
- service->GetCharacteristic(gatt_identifier_[last_characteristic_handle]);
- DCHECK(characteristic);
-
- base::WeakPtr<BluetoothLocalGattDescriptor> descriptor =
- BluetoothLocalGattDescriptor::Create(uuid, permissions, characteristic);
- callback.Run(CreateGattAttributeHandle(descriptor.get()));
-}
-
-void ArcBluetoothBridge::StartService(int32_t service_handle,
- const StartServiceCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
- BluetoothLocalGattService* service =
- bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]);
- DCHECK(service);
- service->Register(base::Bind(&OnGattOperationDone, callback),
- base::Bind(&OnGattOperationError, callback));
-}
-
-void ArcBluetoothBridge::StopService(int32_t service_handle,
- const StopServiceCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
- BluetoothLocalGattService* service =
- bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]);
- DCHECK(service);
- service->Unregister(base::Bind(&OnGattOperationDone, callback),
- base::Bind(&OnGattOperationError, callback));
-}
-
-void ArcBluetoothBridge::DeleteService(int32_t service_handle,
- const DeleteServiceCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
- BluetoothLocalGattService* service =
- bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]);
- DCHECK(service);
- gatt_identifier_.erase(service_handle);
- gatt_handle_.erase(service->GetIdentifier());
- service->Delete();
- OnGattOperationDone(callback);
-}
-
-void ArcBluetoothBridge::SendIndication(
- int32_t attribute_handle,
- mojom::BluetoothAddressPtr address,
- bool confirm,
- const std::vector<uint8_t>& value,
- const SendIndicationCallback& callback) {}
-
-void ArcBluetoothBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr,
- const BluetoothUUID& target_uuid) {
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
- if (!device) {
- OnGetServiceRecordsError(std::move(remote_addr), target_uuid,
- bluez::BluetoothServiceRecordBlueZ::ErrorCode::
- ERROR_DEVICE_DISCONNECTED);
- return;
- }
-
- bluez::BluetoothDeviceBlueZ* device_bluez =
- static_cast<bluez::BluetoothDeviceBlueZ*>(device);
-
- mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone();
-
- device_bluez->GetServiceRecords(
- base::Bind(&ArcBluetoothBridge::OnGetServiceRecordsDone,
- weak_factory_.GetWeakPtr(), base::Passed(&remote_addr),
- target_uuid),
- base::Bind(&ArcBluetoothBridge::OnGetServiceRecordsError,
- weak_factory_.GetWeakPtr(), base::Passed(&remote_addr_clone),
- target_uuid));
-}
-
-void ArcBluetoothBridge::CreateSdpRecord(
- mojom::BluetoothSdpRecordPtr record_mojo,
- const CreateSdpRecordCallback& callback) {
- auto record = record_mojo.To<bluez::BluetoothServiceRecordBlueZ>();
-
- // Check if ServiceClassIDList attribute (attribute ID 0x0001) is included
- // after type conversion, since it is mandatory for creating a service record.
- if (!record.IsAttributePresented(kServiceClassIDListAttributeID)) {
- mojom::BluetoothCreateSdpRecordResultPtr result =
- mojom::BluetoothCreateSdpRecordResult::New();
- result->status = mojom::BluetoothStatus::FAIL;
- callback.Run(std::move(result));
- return;
- }
-
- bluetooth_adapter_->CreateServiceRecord(
- record, base::Bind(&OnCreateServiceRecordDone, callback),
- base::Bind(&OnCreateServiceRecordError, callback));
-}
-
-void ArcBluetoothBridge::RemoveSdpRecord(
- uint32_t service_handle,
- const RemoveSdpRecordCallback& callback) {
- bluetooth_adapter_->RemoveServiceRecord(
- service_handle, base::Bind(&OnRemoveServiceRecordDone, callback),
- base::Bind(&OnRemoveServiceRecordError, callback));
-}
-
-bool ArcBluetoothBridge::GetAdvertisementHandle(int32_t* adv_handle) {
- for (int i = 0; i < kMaxAdvertisements; i++) {
- if (advertisements_.find(i) == advertisements_.end()) {
- *adv_handle = i;
- return true;
- }
- }
- return false;
-}
-
-void ArcBluetoothBridge::ReserveAdvertisementHandle(
- const ReserveAdvertisementHandleCallback& callback) {
- DCHECK(CalledOnValidThread());
- // Find an empty advertisement slot.
- int32_t adv_handle;
- if (!GetAdvertisementHandle(&adv_handle)) {
- LOG(WARNING) << "Out of space for advertisement data";
- callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE,
- kInvalidAdvertisementHandle);
- return;
- }
-
- // We have a handle. Put an entry in the map to reserve it.
- advertisements_[adv_handle] = nullptr;
-
- // The advertisement will be registered when we get the call
- // to SetAdvertisingData. For now, just return the adv_handle.
- callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS, adv_handle);
-}
-
-void ArcBluetoothBridge::BroadcastAdvertisement(
- int32_t adv_handle,
- std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
- const BroadcastAdvertisementCallback& callback) {
- DCHECK(CalledOnValidThread());
- if (advertisements_.find(adv_handle) == advertisements_.end()) {
- callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
- return;
- }
-
- if (!advertisements_[adv_handle]) {
- OnReadyToRegisterAdvertisement(callback, adv_handle,
- std::move(advertisement));
- return;
- }
-
- advertisements_[adv_handle]->Unregister(
- base::Bind(&ArcBluetoothBridge::OnReadyToRegisterAdvertisement,
- weak_factory_.GetWeakPtr(), callback, adv_handle,
- base::Passed(std::move(advertisement))),
- base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementError,
- weak_factory_.GetWeakPtr(), callback, adv_handle));
-}
-
-void ArcBluetoothBridge::ReleaseAdvertisementHandle(
- int32_t adv_handle,
- const ReleaseAdvertisementHandleCallback& callback) {
- DCHECK(CalledOnValidThread());
- if (advertisements_.find(adv_handle) == advertisements_.end()) {
- callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
- return;
- }
-
- if (!advertisements_[adv_handle]) {
- advertisements_.erase(adv_handle);
- callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
- return;
- }
-
- advertisements_[adv_handle]->Unregister(
- base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementDone,
- weak_factory_.GetWeakPtr(), callback, adv_handle),
- base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementError,
- weak_factory_.GetWeakPtr(), callback, adv_handle));
-}
-
-void ArcBluetoothBridge::OnReadyToRegisterAdvertisement(
- const BroadcastAdvertisementCallback& callback,
- int32_t adv_handle,
- std::unique_ptr<device::BluetoothAdvertisement::Data> data) {
- DCHECK(CalledOnValidThread());
- bluetooth_adapter_->RegisterAdvertisement(
- std::move(data),
- base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementDone,
- weak_factory_.GetWeakPtr(), callback, adv_handle),
- base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementError,
- weak_factory_.GetWeakPtr(), callback, adv_handle));
-}
-
-void ArcBluetoothBridge::OnRegisterAdvertisementDone(
- const BroadcastAdvertisementCallback& callback,
- int32_t adv_handle,
- scoped_refptr<BluetoothAdvertisement> advertisement) {
- DCHECK(CalledOnValidThread());
- advertisements_[adv_handle] = std::move(advertisement);
- callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
-}
-
-void ArcBluetoothBridge::OnRegisterAdvertisementError(
- const BroadcastAdvertisementCallback& callback,
- int32_t adv_handle,
- BluetoothAdvertisement::ErrorCode error_code) {
- DCHECK(CalledOnValidThread());
- LOG(WARNING) << "Failed to register advertisement for handle " << adv_handle
- << ", error code = " << error_code;
- advertisements_[adv_handle] = nullptr;
- callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
-}
-
-void ArcBluetoothBridge::OnUnregisterAdvertisementDone(
- const ReleaseAdvertisementHandleCallback& callback,
- int32_t adv_handle) {
- DCHECK(CalledOnValidThread());
- advertisements_.erase(adv_handle);
- callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
-}
-
-void ArcBluetoothBridge::OnUnregisterAdvertisementError(
- const ReleaseAdvertisementHandleCallback& callback,
- int32_t adv_handle,
- BluetoothAdvertisement::ErrorCode error_code) {
- DCHECK(CalledOnValidThread());
- LOG(WARNING) << "Failed to unregister advertisement for handle " << adv_handle
- << ", error code = " << error_code;
- advertisements_.erase(adv_handle);
- callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
-}
-
-void ArcBluetoothBridge::OnDiscoveryError() {
- LOG(WARNING) << "failed to change discovery state";
-}
-
-void ArcBluetoothBridge::OnPairing(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnBondStateChanged);
- if (!bluetooth_instance)
- return;
-
- bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::SUCCESS,
- std::move(addr),
- mojom::BluetoothBondState::BONDING);
-}
-
-void ArcBluetoothBridge::OnPairedDone(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnBondStateChanged);
- if (!bluetooth_instance)
- return;
-
- bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::SUCCESS,
- std::move(addr),
- mojom::BluetoothBondState::BONDED);
-}
-
-void ArcBluetoothBridge::OnPairedError(
- mojom::BluetoothAddressPtr addr,
- BluetoothDevice::ConnectErrorCode error_code) const {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnBondStateChanged);
- if (!bluetooth_instance)
- return;
-
- bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::FAIL,
- std::move(addr),
- mojom::BluetoothBondState::NONE);
-}
-
-void ArcBluetoothBridge::OnForgetDone(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnBondStateChanged);
- if (!bluetooth_instance)
- return;
-
- bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::SUCCESS,
- std::move(addr),
- mojom::BluetoothBondState::NONE);
-}
-
-void ArcBluetoothBridge::OnForgetError(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnBondStateChanged);
- if (!bluetooth_instance)
- return;
-
- BluetoothDevice* device =
- bluetooth_adapter_->GetDevice(addr->To<std::string>());
- mojom::BluetoothBondState bond_state = mojom::BluetoothBondState::NONE;
- if (device && device->IsPaired()) {
- bond_state = mojom::BluetoothBondState::BONDED;
- }
- bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::FAIL,
- std::move(addr), bond_state);
-}
-
-std::vector<mojom::BluetoothPropertyPtr>
-ArcBluetoothBridge::GetDeviceProperties(mojom::BluetoothPropertyType type,
- const BluetoothDevice* device) const {
- std::vector<mojom::BluetoothPropertyPtr> properties;
-
- if (!device) {
- return properties;
- }
-
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::BDNAME) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_bdname(device->GetName() ? device->GetName().value() : "");
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::BDADDR) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_bdaddr(mojom::BluetoothAddress::From(device->GetAddress()));
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::UUIDS) {
- BluetoothDevice::UUIDSet uuids = device->GetUUIDs();
- if (uuids.size() > 0) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_uuids(std::vector<BluetoothUUID>(uuids.begin(), uuids.end()));
- properties.push_back(std::move(btp));
- }
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::CLASS_OF_DEVICE) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_device_class(device->GetBluetoothClass());
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::TYPE_OF_DEVICE) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_device_type(device->GetType());
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::REMOTE_FRIENDLY_NAME) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_remote_friendly_name(
- base::UTF16ToUTF8(device->GetNameForDisplay()));
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::REMOTE_RSSI) {
- base::Optional<int8_t> rssi = device->GetInquiryRSSI();
- if (rssi.has_value()) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_remote_rssi(rssi.value());
- properties.push_back(std::move(btp));
- }
- }
- // TODO(smbarber): Add remote version info
-
- return properties;
-}
-
-std::vector<mojom::BluetoothPropertyPtr>
-ArcBluetoothBridge::GetAdapterProperties(
- mojom::BluetoothPropertyType type) const {
- std::vector<mojom::BluetoothPropertyPtr> properties;
-
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::BDNAME) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- std::string name = bluetooth_adapter_->GetName();
- btp->set_bdname(name);
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::BDADDR) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_bdaddr(
- mojom::BluetoothAddress::From(bluetooth_adapter_->GetAddress()));
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::UUIDS) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_uuids(bluetooth_adapter_->GetUUIDs());
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::CLASS_OF_DEVICE) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_device_class(kBluetoothComputerClass);
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::TYPE_OF_DEVICE) {
- // Assume that all ChromeOS devices are dual mode Bluetooth device.
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_device_type(device::BLUETOOTH_TRANSPORT_DUAL);
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::ADAPTER_SCAN_MODE) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- mojom::BluetoothScanMode scan_mode = mojom::BluetoothScanMode::CONNECTABLE;
-
- if (bluetooth_adapter_->IsDiscoverable())
- scan_mode = mojom::BluetoothScanMode::CONNECTABLE_DISCOVERABLE;
-
- btp->set_adapter_scan_mode(scan_mode);
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::ADAPTER_BONDED_DEVICES) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- BluetoothAdapter::DeviceList devices = bluetooth_adapter_->GetDevices();
-
- std::vector<mojom::BluetoothAddressPtr> bonded_devices;
-
- for (auto* device : devices) {
- if (device->IsPaired())
- continue;
-
- mojom::BluetoothAddressPtr addr =
- mojom::BluetoothAddress::From(device->GetAddress());
- bonded_devices.push_back(std::move(addr));
- }
-
- btp->set_bonded_devices(std::move(bonded_devices));
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::ADAPTER_DISCOVERY_TIMEOUT) {
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_discovery_timeout(bluetooth_adapter_->GetDiscoverableTimeout());
- properties.push_back(std::move(btp));
- }
- if (type == mojom::BluetoothPropertyType::ALL ||
- type == mojom::BluetoothPropertyType::LOCAL_LE_FEATURES) {
- // TODO(crbug.com/637171) Investigate all the le_features.
- mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- mojom::BluetoothLocalLEFeaturesPtr le_features =
- mojom::BluetoothLocalLEFeatures::New();
- le_features->version_supported = kAndroidMBluetoothVersionNumber;
- le_features->local_privacy_enabled = 0;
- le_features->max_adv_instance = kMaxAdvertisements;
- le_features->rpa_offload_supported = 0;
- le_features->max_irk_list_size = 0;
- le_features->max_adv_filter_supported = 0;
- le_features->activity_energy_info_supported = 0;
- le_features->scan_result_storage_size = 0;
- le_features->total_trackable_advertisers = 0;
- le_features->extended_scan_support = false;
- le_features->debug_logging_supported = false;
- btp->set_local_le_features(std::move(le_features));
- properties.push_back(std::move(btp));
- }
-
- return properties;
-}
-
-// Android support 6 types of Advertising Data which are Advertising Data Flags,
-// Local Name, Service UUIDs, Tx Power Level, Service Data, and Manufacturer
-// Data. Note that we need to use 16-bit UUID in Service Data section because
-// Android does not support 128-bit UUID there.
-std::vector<mojom::BluetoothAdvertisingDataPtr>
-ArcBluetoothBridge::GetAdvertisingData(const BluetoothDevice* device) const {
- std::vector<mojom::BluetoothAdvertisingDataPtr> advertising_data;
-
- // Advertising Data Flags
- if (device->GetAdvertisingDataFlags().has_value()) {
- mojom::BluetoothAdvertisingDataPtr flags =
- mojom::BluetoothAdvertisingData::New();
- flags->set_flags(device->GetAdvertisingDataFlags().value());
- advertising_data.push_back(std::move(flags));
- }
-
- // Local Name
- mojom::BluetoothAdvertisingDataPtr local_name =
- mojom::BluetoothAdvertisingData::New();
- local_name->set_local_name(device->GetName() ? device->GetName().value()
- : "");
- advertising_data.push_back(std::move(local_name));
-
- // Service UUIDs
- const BluetoothDevice::UUIDSet& uuid_set = device->GetUUIDs();
- if (uuid_set.size() > 0) {
- mojom::BluetoothAdvertisingDataPtr service_uuids =
- mojom::BluetoothAdvertisingData::New();
- service_uuids->set_service_uuids(
- std::vector<BluetoothUUID>(uuid_set.begin(), uuid_set.end()));
- advertising_data.push_back(std::move(service_uuids));
- }
-
- // Tx Power Level
- if (device->GetInquiryTxPower().has_value()) {
- mojom::BluetoothAdvertisingDataPtr tx_power_level_element =
- mojom::BluetoothAdvertisingData::New();
- tx_power_level_element->set_tx_power_level(
- device->GetInquiryTxPower().value());
- advertising_data.push_back(std::move(tx_power_level_element));
- }
-
- // Service Data
- for (const BluetoothUUID& uuid : device->GetServiceDataUUIDs()) {
- mojom::BluetoothAdvertisingDataPtr service_data_element =
- mojom::BluetoothAdvertisingData::New();
- mojom::BluetoothServiceDataPtr service_data =
- mojom::BluetoothServiceData::New();
-
- // Android only supports UUID 16 bit here.
- service_data->uuid_16bit = GetUUID16(uuid);
-
- const std::vector<uint8_t>* data = device->GetServiceDataForUUID(uuid);
- DCHECK(data != nullptr);
-
- service_data->data = *data;
-
- service_data_element->set_service_data(std::move(service_data));
- advertising_data.push_back(std::move(service_data_element));
- }
-
- // Manufacturer Data
- if (!device->GetManufacturerData().empty()) {
- std::vector<uint8_t> manufacturer_data;
- for (const auto& pair : device->GetManufacturerData()) {
- uint16_t id = pair.first;
- // Use little endian here.
- manufacturer_data.push_back(id & 0xff);
- manufacturer_data.push_back(id >> 8);
- manufacturer_data.insert(manufacturer_data.end(), pair.second.begin(),
- pair.second.end());
- }
- mojom::BluetoothAdvertisingDataPtr manufacturer_data_element =
- mojom::BluetoothAdvertisingData::New();
- manufacturer_data_element->set_manufacturer_data(manufacturer_data);
- advertising_data.push_back(std::move(manufacturer_data_element));
- }
-
- return advertising_data;
-}
-
-void ArcBluetoothBridge::SendCachedDevicesFound() const {
- DCHECK(bluetooth_adapter_);
-
- // Send devices that have already been discovered, but aren't connected.
- BluetoothAdapter::DeviceList devices = bluetooth_adapter_->GetDevices();
- for (auto* device : devices) {
- if (device->IsPaired())
- continue;
-
- SendDevice(device);
- }
-}
-
-void ArcBluetoothBridge::SendCachedPairedDevices() const {
- DCHECK(bluetooth_adapter_);
-
- BluetoothAdapter::DeviceList devices = bluetooth_adapter_->GetDevices();
- for (auto* device : devices) {
- if (!device->IsPaired())
- continue;
-
- SendDevice(device);
-
- // OnBondStateChanged must be called with mojom::BluetoothBondState::BONDING
- // to make sure the bond state machine on Android is ready to take the
- // pair-done event. Otherwise the pair-done event will be dropped as an
- // invalid change of paired status.
- mojom::BluetoothAddressPtr addr =
- mojom::BluetoothAddress::From(device->GetAddress());
- OnPairing(addr->Clone());
- OnPairedDone(std::move(addr));
- }
-}
-
-void ArcBluetoothBridge::OnGetServiceRecordsDone(
- mojom::BluetoothAddressPtr remote_addr,
- const BluetoothUUID& target_uuid,
- const std::vector<bluez::BluetoothServiceRecordBlueZ>& records_bluez) {
- auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnGetSdpRecords);
- if (!sdp_bluetooth_instance)
- return;
-
- std::vector<mojom::BluetoothSdpRecordPtr> records;
- for (const auto& r : records_bluez)
- records.push_back(mojom::BluetoothSdpRecord::From(r));
-
- sdp_bluetooth_instance->OnGetSdpRecords(mojom::BluetoothStatus::SUCCESS,
- std::move(remote_addr), target_uuid,
- std::move(records));
-}
-
-void ArcBluetoothBridge::OnGetServiceRecordsError(
- mojom::BluetoothAddressPtr remote_addr,
- const BluetoothUUID& target_uuid,
- bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) {
- auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->bluetooth(), OnGetSdpRecords);
- if (!sdp_bluetooth_instance)
- return;
-
- mojom::BluetoothStatus status;
-
- switch (error_code) {
- case bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY:
- status = mojom::BluetoothStatus::NOT_READY;
- break;
- case bluez::BluetoothServiceRecordBlueZ::ErrorCode::
- ERROR_DEVICE_DISCONNECTED:
- status = mojom::BluetoothStatus::RMT_DEV_DOWN;
- break;
- default:
- status = mojom::BluetoothStatus::FAIL;
- break;
- }
-
- sdp_bluetooth_instance->OnGetSdpRecords(
- status, std::move(remote_addr), target_uuid,
- std::vector<mojom::BluetoothSdpRecordPtr>());
-}
-
-bool ArcBluetoothBridge::CalledOnValidThread() {
- return thread_checker_.CalledOnValidThread();
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.h b/chromium/components/arc/bluetooth/arc_bluetooth_bridge.h
deleted file mode 100644
index a98fa52ee37..00000000000
--- a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.h
+++ /dev/null
@@ -1,491 +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_BLUETOOTH_ARC_BLUETOOTH_BRIDGE_H_
-#define COMPONENTS_ARC_BLUETOOTH_ARC_BLUETOOTH_BRIDGE_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/timer/timer.h"
-#include "components/arc/arc_service.h"
-#include "components/arc/common/bluetooth.mojom.h"
-#include "components/arc/instance_holder.h"
-#include "device/bluetooth/bluetooth_adapter.h"
-#include "device/bluetooth/bluetooth_adapter_factory.h"
-#include "device/bluetooth/bluetooth_advertisement.h"
-#include "device/bluetooth/bluetooth_device.h"
-#include "device/bluetooth/bluetooth_discovery_session.h"
-#include "device/bluetooth/bluetooth_local_gatt_service.h"
-#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
-#include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
-#include "device/bluetooth/bluetooth_remote_gatt_service.h"
-#include "device/bluetooth/bluez/bluetooth_adapter_bluez.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace arc {
-
-class ArcBridgeService;
-
-class ArcBluetoothBridge
- : public ArcService,
- public InstanceHolder<mojom::BluetoothInstance>::Observer,
- public device::BluetoothAdapter::Observer,
- public device::BluetoothAdapterFactory::AdapterCallback,
- public device::BluetoothLocalGattService::Delegate,
- public mojom::BluetoothHost {
- public:
- explicit ArcBluetoothBridge(ArcBridgeService* bridge_service);
- ~ArcBluetoothBridge() override;
-
- // Overridden from InstanceHolder<mojom::BluetoothInstance>::Observer:
- void OnInstanceReady() override;
- void OnInstanceClosed() override;
-
- void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter);
-
- // Overridden from device::BluetoothAdadpter::Observer
- void DeviceAdded(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device) override;
-
- void DeviceChanged(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device) override;
-
- void DeviceAddressChanged(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device,
- const std::string& old_address) override;
-
- void DevicePairedChanged(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device,
- bool new_paired_status) override;
-
- void DeviceRemoved(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device) override;
-
- void GattServiceAdded(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device,
- device::BluetoothRemoteGattService* service) override;
-
- void GattServiceRemoved(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device,
- device::BluetoothRemoteGattService* service) override;
-
- void GattServicesDiscovered(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device) override;
-
- void GattDiscoveryCompleteForService(
- device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattService* service) override;
-
- void GattServiceChanged(device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattService* service) override;
-
- void GattCharacteristicAdded(
- device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattCharacteristic* characteristic) override;
-
- void GattCharacteristicRemoved(
- device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattCharacteristic* characteristic) override;
-
- void GattDescriptorAdded(
- device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattDescriptor* descriptor) override;
-
- void GattDescriptorRemoved(
- device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattDescriptor* descriptor) override;
-
- void GattCharacteristicValueChanged(
- device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattCharacteristic* characteristic,
- const std::vector<uint8_t>& value) override;
-
- void GattDescriptorValueChanged(
- device::BluetoothAdapter* adapter,
- device::BluetoothRemoteGattDescriptor* descriptor,
- const std::vector<uint8_t>& value) override;
-
- // Overridden from device::BluetoothLocalGattService::Delegate
- void OnCharacteristicReadRequest(
- const device::BluetoothDevice* device,
- const device::BluetoothLocalGattCharacteristic* characteristic,
- int offset,
- const ValueCallback& callback,
- const ErrorCallback& error_callback) override;
-
- void OnCharacteristicWriteRequest(
- const device::BluetoothDevice* device,
- const device::BluetoothLocalGattCharacteristic* characteristic,
- const std::vector<uint8_t>& value,
- int offset,
- const base::Closure& callback,
- const ErrorCallback& error_callback) override;
-
- void OnDescriptorReadRequest(
- const device::BluetoothDevice* device,
- const device::BluetoothLocalGattDescriptor* descriptor,
- int offset,
- const ValueCallback& callback,
- const ErrorCallback& error_callback) override;
-
- void OnDescriptorWriteRequest(
- const device::BluetoothDevice* device,
- const device::BluetoothLocalGattDescriptor* descriptor,
- const std::vector<uint8_t>& value,
- int offset,
- const base::Closure& callback,
- const ErrorCallback& error_callback) override;
-
- void OnNotificationsStart(
- const device::BluetoothDevice* device,
- const device::BluetoothLocalGattCharacteristic* characteristic) override;
-
- void OnNotificationsStop(
- const device::BluetoothDevice* device,
- const device::BluetoothLocalGattCharacteristic* characteristic) override;
-
- // Bluetooth Mojo host interface
- void EnableAdapter(const EnableAdapterCallback& callback) override;
- void DisableAdapter(const DisableAdapterCallback& callback) override;
-
- void GetAdapterProperty(mojom::BluetoothPropertyType type) override;
- void SetAdapterProperty(mojom::BluetoothPropertyPtr property) override;
-
- void GetRemoteDeviceProperty(mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothPropertyType type) override;
- void SetRemoteDeviceProperty(mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothPropertyPtr property) override;
- void GetRemoteServiceRecord(mojom::BluetoothAddressPtr remote_addr,
- const device::BluetoothUUID& uuid) override;
-
- void GetRemoteServices(mojom::BluetoothAddressPtr remote_addr) override;
-
- void StartDiscovery() override;
- void CancelDiscovery() override;
-
- void CreateBond(mojom::BluetoothAddressPtr addr, int32_t transport) override;
- void RemoveBond(mojom::BluetoothAddressPtr addr) override;
- void CancelBond(mojom::BluetoothAddressPtr addr) override;
-
- void GetConnectionState(mojom::BluetoothAddressPtr addr,
- const GetConnectionStateCallback& callback) override;
-
- // Bluetooth Mojo host interface - Bluetooth Gatt Client functions
- void StartLEScan() override;
- void StopLEScan() override;
- void ConnectLEDevice(mojom::BluetoothAddressPtr remote_addr) override;
- void DisconnectLEDevice(mojom::BluetoothAddressPtr remote_addr) override;
- void StartLEListen(const StartLEListenCallback& callback) override;
- void StopLEListen(const StopLEListenCallback& callback) override;
- void SearchService(mojom::BluetoothAddressPtr remote_addr) override;
-
- void GetGattDB(mojom::BluetoothAddressPtr remote_addr) override;
- void ReadGattCharacteristic(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- const ReadGattCharacteristicCallback& callback) override;
- void WriteGattCharacteristic(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattValuePtr value,
- const WriteGattCharacteristicCallback& callback) override;
- void ReadGattDescriptor(mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattIDPtr desc_id,
- const ReadGattDescriptorCallback& callback) override;
- void WriteGattDescriptor(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattIDPtr desc_id,
- mojom::BluetoothGattValuePtr value,
- const WriteGattDescriptorCallback& callback) override;
- void RegisterForGattNotification(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- const RegisterForGattNotificationCallback& callback) override;
- void DeregisterForGattNotification(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- const DeregisterForGattNotificationCallback& callback) override;
- void ReadRemoteRssi(mojom::BluetoothAddressPtr remote_addr,
- const ReadRemoteRssiCallback& callback) override;
-
- void OpenBluetoothSocket(
- const OpenBluetoothSocketCallback& callback) override;
-
- // Bluetooth Mojo host interface - Bluetooth Gatt Server functions
- // Android counterpart link:
- // https://source.android.com/devices/halref/bt__gatt__server_8h.html
- // Create a new service. Chrome will create an integer service handle based on
- // that BlueZ identifier that will pass back to Android in the callback.
- // num_handles: number of handle for characteristic / descriptor that will be
- // created in this service
- void AddService(mojom::BluetoothGattServiceIDPtr service_id,
- int32_t num_handles,
- const AddServiceCallback& callback) override;
- // Add a characteristic to a service and pass the characteristic handle back.
- void AddCharacteristic(int32_t service_handle,
- const device::BluetoothUUID& uuid,
- int32_t properties,
- int32_t permissions,
- const AddCharacteristicCallback& callback) override;
- // Add a descriptor to the last characteristic added to the given service
- // and pass the descriptor handle back.
- void AddDescriptor(int32_t service_handle,
- const device::BluetoothUUID& uuid,
- int32_t permissions,
- const AddDescriptorCallback& callback) override;
- // Start a local service.
- void StartService(int32_t service_handle,
- const StartServiceCallback& callback) override;
- // Stop a local service.
- void StopService(int32_t service_handle,
- const StopServiceCallback& callback) override;
- // Delete a local service.
- void DeleteService(int32_t service_handle,
- const DeleteServiceCallback& callback) override;
- // Send value indication to a remote device.
- void SendIndication(int32_t attribute_handle,
- mojom::BluetoothAddressPtr address,
- bool confirm,
- const std::vector<uint8_t>& value,
- const SendIndicationCallback& callback) override;
-
- // Bluetooth Mojo host interface - Bluetooth SDP functions
- void GetSdpRecords(mojom::BluetoothAddressPtr remote_addr,
- const device::BluetoothUUID& target_uuid) override;
- void CreateSdpRecord(mojom::BluetoothSdpRecordPtr record_mojo,
- const CreateSdpRecordCallback& callback) override;
- void RemoveSdpRecord(uint32_t service_handle,
- const RemoveSdpRecordCallback& callback) override;
-
- // Set up or disable multiple advertising.
- void ReserveAdvertisementHandle(
- const ReserveAdvertisementHandleCallback& callback) override;
- void BroadcastAdvertisement(
- int32_t adv_handle,
- std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
- const BroadcastAdvertisementCallback& callback) override;
- void ReleaseAdvertisementHandle(
- int32_t adv_handle,
- const ReleaseAdvertisementHandleCallback& callback) override;
-
- // Chrome observer callbacks
- void OnPoweredOn(
- const base::Callback<void(mojom::BluetoothAdapterState)>& callback) const;
- void OnPoweredOff(
- const base::Callback<void(mojom::BluetoothAdapterState)>& callback) const;
- void OnPoweredError(
- const base::Callback<void(mojom::BluetoothAdapterState)>& callback) const;
- void OnDiscoveryStarted(
- std::unique_ptr<device::BluetoothDiscoverySession> session);
- void OnDiscoveryStopped();
- void OnDiscoveryError();
- void OnPairing(mojom::BluetoothAddressPtr addr) const;
- void OnPairedDone(mojom::BluetoothAddressPtr addr) const;
- void OnPairedError(
- mojom::BluetoothAddressPtr addr,
- device::BluetoothDevice::ConnectErrorCode error_code) const;
- void OnForgetDone(mojom::BluetoothAddressPtr addr) const;
- void OnForgetError(mojom::BluetoothAddressPtr addr) const;
-
- void OnGattConnectStateChanged(mojom::BluetoothAddressPtr addr,
- bool connected) const;
- void OnGattConnected(
- mojom::BluetoothAddressPtr addr,
- std::unique_ptr<device::BluetoothGattConnection> connection);
- void OnGattConnectError(
- mojom::BluetoothAddressPtr addr,
- device::BluetoothDevice::ConnectErrorCode error_code) const;
- void OnGattDisconnected(mojom::BluetoothAddressPtr addr);
-
- void OnStartLEListenDone(const StartLEListenCallback& callback,
- scoped_refptr<device::BluetoothAdvertisement> adv);
- void OnStartLEListenError(
- const StartLEListenCallback& callback,
- device::BluetoothAdvertisement::ErrorCode error_code);
-
- void OnStopLEListenDone(const StopLEListenCallback& callback);
- void OnStopLEListenError(
- const StopLEListenCallback& callback,
- device::BluetoothAdvertisement::ErrorCode error_code);
-
- void OnGattNotifyStartDone(
- const RegisterForGattNotificationCallback& callback,
- const std::string char_string_id,
- std::unique_ptr<device::BluetoothGattNotifySession> notify_session);
-
- private:
- std::vector<mojom::BluetoothPropertyPtr> GetDeviceProperties(
- mojom::BluetoothPropertyType type,
- const device::BluetoothDevice* device) const;
- std::vector<mojom::BluetoothPropertyPtr> GetAdapterProperties(
- mojom::BluetoothPropertyType type) const;
- std::vector<mojom::BluetoothAdvertisingDataPtr> GetAdvertisingData(
- const device::BluetoothDevice* device) const;
-
- void SendCachedDevicesFound() const;
-
- device::BluetoothRemoteGattCharacteristic* FindGattCharacteristic(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id) const;
-
- device::BluetoothRemoteGattDescriptor* FindGattDescriptor(
- mojom::BluetoothAddressPtr remote_addr,
- mojom::BluetoothGattServiceIDPtr service_id,
- mojom::BluetoothGattIDPtr char_id,
- mojom::BluetoothGattIDPtr desc_id) const;
-
- // Propagates the list of paired device to Android.
- void SendCachedPairedDevices() const;
-
- bool IsGattServerAttributeHandleAvailable(int need);
- int32_t GetNextGattServerAttributeHandle();
- template <class LocalGattAttribute>
- int32_t CreateGattAttributeHandle(LocalGattAttribute* attribute);
-
- // Common code for OnCharacteristicReadRequest and OnDescriptorReadRequest
- template <class LocalGattAttribute>
- void OnGattAttributeReadRequest(const device::BluetoothDevice* device,
- const LocalGattAttribute* attribute,
- int offset,
- const ValueCallback& success_callback,
- const ErrorCallback& error_callback);
-
- // Common code for OnCharacteristicWriteRequest and OnDescriptorWriteRequest
- template <class LocalGattAttribute>
- void OnGattAttributeWriteRequest(const device::BluetoothDevice* device,
- const LocalGattAttribute* attribute,
- const std::vector<uint8_t>& value,
- int offset,
- const base::Closure& success_callback,
- const ErrorCallback& error_callback);
-
- void OnSetDiscoverable(bool discoverable, bool success, uint32_t timeout);
- void SetDiscoverable(bool discoverable, uint32_t timeout);
-
- void OnGetServiceRecordsDone(
- mojom::BluetoothAddressPtr remote_addr,
- const device::BluetoothUUID& target_uuid,
- const std::vector<bluez::BluetoothServiceRecordBlueZ>& records_bluez);
- void OnGetServiceRecordsError(
- mojom::BluetoothAddressPtr remote_addr,
- const device::BluetoothUUID& target_uuid,
- bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code);
-
- void OnSetAdapterProperty(mojom::BluetoothStatus success,
- mojom::BluetoothPropertyPtr property);
-
- // Callbacks for managing advertisements registered from the instance.
-
- // Called when we have an open slot in the advertisement map and want to
- // register the advertisement given by |data| for handle |adv_handle|.
- void OnReadyToRegisterAdvertisement(
- const BroadcastAdvertisementCallback& callback,
- int32_t adv_handle,
- std::unique_ptr<device::BluetoothAdvertisement::Data> data);
- // Called when we've successfully registered a new advertisement for
- // handle |adv_handle|.
- void OnRegisterAdvertisementDone(
- const BroadcastAdvertisementCallback& callback,
- int32_t adv_handle,
- scoped_refptr<device::BluetoothAdvertisement> advertisement);
- // Called when the attempt to register an advertisement for handle
- // |adv_handle| has failed. |adv_handle| remains reserved, but no
- // advertisement is associated with it.
- void OnRegisterAdvertisementError(
- const BroadcastAdvertisementCallback& callback,
- int32_t adv_handle,
- device::BluetoothAdvertisement::ErrorCode error_code);
- // Both of the following are called after we've tried to unregister
- // the advertisement for |adv_handle|. Either way, we will no
- // longer be broadcasting this advertisement, so in either case, the
- // handle can be released.
- void OnUnregisterAdvertisementDone(
- const ReleaseAdvertisementHandleCallback& callback,
- int32_t adv_handle);
- void OnUnregisterAdvertisementError(
- const ReleaseAdvertisementHandleCallback& callback,
- int32_t adv_handle,
- device::BluetoothAdvertisement::ErrorCode error_code);
- // Find the next free advertisement handle and put it in *adv_handle,
- // or return false if the advertisement map is full.
- bool GetAdvertisementHandle(int32_t* adv_handle);
-
- void SendDevice(const device::BluetoothDevice* device) const;
-
- bool CalledOnValidThread();
-
- mojo::Binding<mojom::BluetoothHost> binding_;
-
- scoped_refptr<bluez::BluetoothAdapterBlueZ> bluetooth_adapter_;
- scoped_refptr<device::BluetoothAdvertisement> advertisment_;
- std::unique_ptr<device::BluetoothDiscoverySession> discovery_session_;
- std::unordered_map<std::string,
- std::unique_ptr<device::BluetoothGattNotifySession>>
- notification_session_;
- // Map from Android int handle to Chrome (BlueZ) string identifier.
- std::unordered_map<int32_t, std::string> gatt_identifier_;
- // Map from Chrome (BlueZ) string identifier to android int handle.
- std::unordered_map<std::string, int32_t> gatt_handle_;
- // Store last GattCharacteristic added to each GattService for GattServer.
- std::unordered_map<int32_t, int32_t> last_characteristic_;
- // Monotonically increasing value to use as handle to give to Android side.
- int32_t gatt_server_attribute_next_handle_ = 0;
- // Keeps track of all devices which initiated a GATT connection to us.
- std::unordered_set<std::string> gatt_connection_cache_;
- // Map of device address to GATT connection objects for connections we
- // have made. We need to hang on to these as long as the connection is
- // active since their destructors will drop the connections otherwise.
- std::unordered_map<std::string,
- std::unique_ptr<device::BluetoothGattConnection>>
- gatt_connections_;
- // Timer to turn discovery off.
- base::OneShotTimer discovery_off_timer_;
- // Timer to turn adapter discoverable off.
- base::OneShotTimer discoverable_off_timer_;
-
- // Holds advertising data registered by the instance.
- //
- // When a handle is reserved, an entry is placed into the advertisements_
- // map. This entry is not yet associated with a device::BluetoothAdvertisement
- // because the instance hasn't sent us any advertising data yet, so its
- // mapped value is nullptr until that happens. Thus we have three states for a
- // handle:
- // * unmapped -> free
- // * mapped to nullptr -> reserved, awaiting data
- // * mapped to a device::BluetoothAdvertisement -> in use, and the mapped
- // BluetoothAdvertisement is currently registered with the adapter.
- // TODO(crbug.com/658385) Change back to 5 when we support setting signal
- // strength per each advertisement slot.
- enum { kMaxAdvertisements = 1 };
- std::map<int32_t, scoped_refptr<device::BluetoothAdvertisement>>
- advertisements_;
-
- base::ThreadChecker thread_checker_;
-
- // WeakPtrFactory to use for callbacks.
- base::WeakPtrFactory<ArcBluetoothBridge> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ArcBluetoothBridge);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_BLUETOOTH_ARC_BLUETOOTH_BRIDGE_H_
diff --git a/chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc b/chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
deleted file mode 100644
index 400eaa860b2..00000000000
--- a/chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
+++ /dev/null
@@ -1,331 +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/bluetooth/arc_bluetooth_bridge.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "components/arc/arc_bridge_service.h"
-#include "components/arc/bluetooth/bluetooth_type_converters.h"
-#include "components/arc/common/bluetooth.mojom.h"
-#include "components/arc/test/fake_bluetooth_instance.h"
-#include "device/bluetooth/dbus/bluez_dbus_manager.h"
-#include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
-#include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
-#include "device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h"
-#include "device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h"
-#include "device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h"
-#include "device/bluetooth/dbus/fake_bluetooth_le_advertising_manager_client.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-constexpr int16_t kTestRssi = -50;
-constexpr int16_t kTestRssi2 = -70;
-}
-
-namespace arc {
-
-constexpr int kFailureAdvHandle = -1;
-
-class ArcBluetoothBridgeTest : public testing::Test {
- protected:
- void AddTestDevice() {
- bluez::BluezDBusManager* dbus_manager = bluez::BluezDBusManager::Get();
- auto* fake_bluetooth_device_client =
- static_cast<bluez::FakeBluetoothDeviceClient*>(
- dbus_manager->GetBluetoothDeviceClient());
- auto* fake_bluetooth_gatt_service_client =
- static_cast<bluez::FakeBluetoothGattServiceClient*>(
- dbus_manager->GetBluetoothGattServiceClient());
- auto* fake_bluetooth_gatt_characteristic_client =
- static_cast<bluez::FakeBluetoothGattCharacteristicClient*>(
- dbus_manager->GetBluetoothGattCharacteristicClient());
-
- fake_bluetooth_device_client->CreateDevice(
- dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
- dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
- fake_bluetooth_gatt_service_client->ExposeHeartRateService(
- dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
- fake_bluetooth_gatt_characteristic_client->ExposeHeartRateCharacteristics(
- fake_bluetooth_gatt_service_client->GetHeartRateServicePath());
-
- ChangeTestDeviceRssi(kTestRssi);
- }
-
- void ChangeTestDeviceRssi(uint16_t rssi) {
- bluez::BluezDBusManager* dbus_manager = bluez::BluezDBusManager::Get();
- auto* fake_bluetooth_device_client =
- static_cast<bluez::FakeBluetoothDeviceClient*>(
- dbus_manager->GetBluetoothDeviceClient());
- fake_bluetooth_device_client->UpdateDeviceRSSI(
- dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath),
- rssi);
- }
-
- void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter) {
- adapter_ = adapter;
- get_adapter_run_loop_.Quit();
- }
-
- void SetUp() override {
- std::unique_ptr<bluez::BluezDBusManagerSetter> dbus_setter =
- bluez::BluezDBusManager::GetSetterForTesting();
- auto fake_bluetooth_device_client =
- base::MakeUnique<bluez::FakeBluetoothDeviceClient>();
- fake_bluetooth_device_client->RemoveAllDevices();
- dbus_setter->SetBluetoothDeviceClient(
- std::move(fake_bluetooth_device_client));
- dbus_setter->SetBluetoothGattServiceClient(
- base::MakeUnique<bluez::FakeBluetoothGattServiceClient>());
- dbus_setter->SetBluetoothGattCharacteristicClient(
- base::MakeUnique<bluez::FakeBluetoothGattCharacteristicClient>());
- dbus_setter->SetBluetoothGattDescriptorClient(
- base::MakeUnique<bluez::FakeBluetoothGattDescriptorClient>());
-
- arc_bridge_service_ = base::MakeUnique<ArcBridgeService>();
- fake_bluetooth_instance_ = base::MakeUnique<FakeBluetoothInstance>();
- arc_bridge_service_->bluetooth()->SetInstance(
- fake_bluetooth_instance_.get(), 2);
- arc_bluetooth_bridge_ =
- base::MakeUnique<ArcBluetoothBridge>(arc_bridge_service_.get());
-
- device::BluetoothAdapterFactory::GetAdapter(base::Bind(
- &ArcBluetoothBridgeTest::OnAdapterInitialized, base::Unretained(this)));
- // We will quit the loop once we get the adapter.
- get_adapter_run_loop_.Run();
- }
-
- // Helper methods for multi advertisement tests.
- int32_t ReserveAdvertisementHandle() {
- constexpr int kSentinelHandle = -2;
- last_adv_handle_ = kSentinelHandle;
- arc_bluetooth_bridge_->ReserveAdvertisementHandle(
- base::Bind(&ArcBluetoothBridgeTest::ReserveAdvertisementHandleCallback,
- base::Unretained(this)));
-
- base::RunLoop().RunUntilIdle();
- // Make sure the callback was called.
- EXPECT_NE(kSentinelHandle, last_adv_handle_);
- return last_adv_handle_;
- }
-
- mojom::BluetoothGattStatus BroadcastAdvertisement(
- int adv_handle,
- std::unique_ptr<device::BluetoothAdvertisement::Data> data) {
- last_status_ = mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
- arc_bluetooth_bridge_->BroadcastAdvertisement(
- adv_handle, std::move(data),
- base::Bind(&ArcBluetoothBridgeTest::StatusSetterCallback,
- base::Unretained(this)));
-
- base::RunLoop().RunUntilIdle();
- EXPECT_NE(mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED,
- last_status_);
- return last_status_;
- }
-
- mojom::BluetoothGattStatus ReleaseAdvertisementHandle(int adv_handle) {
- last_status_ = mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
- arc_bluetooth_bridge_->ReleaseAdvertisementHandle(
- adv_handle, base::Bind(&ArcBluetoothBridgeTest::StatusSetterCallback,
- base::Unretained(this)));
-
- base::RunLoop().RunUntilIdle();
- EXPECT_NE(mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED,
- last_status_);
- return last_status_;
- }
-
- void ReserveAdvertisementHandleCallback(mojom::BluetoothGattStatus status,
- int32_t adv_handle) {
- if (status == mojom::BluetoothGattStatus::GATT_FAILURE)
- last_adv_handle_ = kFailureAdvHandle;
- else
- last_adv_handle_ = adv_handle;
- }
-
- void StatusSetterCallback(mojom::BluetoothGattStatus status) {
- last_status_ = status;
- }
-
- int NumActiveAdvertisements() {
- bluez::FakeBluetoothLEAdvertisingManagerClient* adv_client =
- static_cast<bluez::FakeBluetoothLEAdvertisingManagerClient*>(
- bluez::BluezDBusManager::Get()
- ->GetBluetoothLEAdvertisingManagerClient());
- return adv_client->currently_registered();
- }
-
- int last_adv_handle_;
- mojom::BluetoothGattStatus last_status_;
-
- std::unique_ptr<ArcBridgeService> arc_bridge_service_;
- std::unique_ptr<FakeBluetoothInstance> fake_bluetooth_instance_;
- std::unique_ptr<ArcBluetoothBridge> arc_bluetooth_bridge_;
- scoped_refptr<device::BluetoothAdapter> adapter_;
- base::MessageLoop message_loop_;
- base::RunLoop get_adapter_run_loop_;
-};
-
-// When we add device to bluez::FakeBluetoothDeviceClient, ArcBluetoothBridge
-// should send new device data to Android. This test will then check
-// the correctness of the device properties sent via arc bridge.
-TEST_F(ArcBluetoothBridgeTest, DeviceFound) {
- EXPECT_EQ(0u, fake_bluetooth_instance_->device_found_data().size());
- AddTestDevice();
- EXPECT_EQ(2u, fake_bluetooth_instance_->device_found_data().size());
- const std::vector<mojom::BluetoothPropertyPtr>& prop =
- fake_bluetooth_instance_->device_found_data().back();
-
- EXPECT_EQ(7u, prop.size());
- EXPECT_TRUE(prop[0]->is_bdname());
- EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyName),
- prop[0]->get_bdname());
- EXPECT_TRUE(prop[1]->is_bdaddr());
- EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress),
- prop[1]->get_bdaddr()->To<std::string>());
- EXPECT_TRUE(prop[2]->is_uuids());
- EXPECT_EQ(1u, prop[2]->get_uuids().size());
- EXPECT_EQ(bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID,
- prop[2]->get_uuids()[0].value());
- EXPECT_TRUE(prop[3]->is_device_class());
- EXPECT_EQ(bluez::FakeBluetoothDeviceClient::kLowEnergyClass,
- prop[3]->get_device_class());
- EXPECT_TRUE(prop[4]->is_device_type());
- // bluez::FakeBluetoothDeviceClient does not return proper device type.
- EXPECT_TRUE(prop[5]->is_remote_friendly_name());
- EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyName),
- prop[5]->get_remote_friendly_name());
- EXPECT_TRUE(prop[6]->is_remote_rssi());
- EXPECT_EQ(kTestRssi, prop[6]->get_remote_rssi());
-
- ChangeTestDeviceRssi(kTestRssi2);
- EXPECT_EQ(3u, fake_bluetooth_instance_->device_found_data().size());
- const std::vector<mojom::BluetoothPropertyPtr>& prop2 =
- fake_bluetooth_instance_->device_found_data().back();
- EXPECT_EQ(7u, prop2.size());
- EXPECT_TRUE(prop2[6]->is_remote_rssi());
- EXPECT_EQ(kTestRssi2, prop2[6]->get_remote_rssi());
-}
-
-// Invoke OnDiscoveryStarted to send cached device to BT instance,
-// and check correctness of the Advertising data sent via arc bridge.
-TEST_F(ArcBluetoothBridgeTest, LEDeviceFound) {
- EXPECT_EQ(0u, fake_bluetooth_instance_->le_device_found_data().size());
- AddTestDevice();
- EXPECT_EQ(1u, fake_bluetooth_instance_->le_device_found_data().size());
-
- const auto& le_device_found_data =
- fake_bluetooth_instance_->le_device_found_data().back();
- const mojom::BluetoothAddressPtr& addr = le_device_found_data->addr();
- const std::vector<mojom::BluetoothAdvertisingDataPtr>& adv_data =
- le_device_found_data->adv_data();
-
- EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress),
- addr->To<std::string>());
- EXPECT_EQ(2u, adv_data.size());
-
- EXPECT_TRUE(adv_data[0]->is_local_name());
- EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyName),
- adv_data[0]->get_local_name());
-
- EXPECT_TRUE(adv_data[1]->is_service_uuids());
- EXPECT_EQ(1u, adv_data[1]->get_service_uuids().size());
- EXPECT_EQ(bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID,
- adv_data[1]->get_service_uuids()[0].canonical_value());
-
- EXPECT_EQ(kTestRssi, le_device_found_data->rssi());
-
- ChangeTestDeviceRssi(kTestRssi2);
- EXPECT_EQ(2u, fake_bluetooth_instance_->le_device_found_data().size());
- EXPECT_EQ(kTestRssi2,
- fake_bluetooth_instance_->le_device_found_data().back()->rssi());
-}
-
-// Invoke GetGattDB and check correctness of the GattDB sent via arc bridge.
-TEST_F(ArcBluetoothBridgeTest, GetGattDB) {
- AddTestDevice();
-
- arc_bluetooth_bridge_->GetGattDB(mojom::BluetoothAddress::From(
- std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress)));
- EXPECT_EQ(1u, fake_bluetooth_instance_->gatt_db_result().size());
-
- const mojom::BluetoothAddressPtr& addr =
- fake_bluetooth_instance_->gatt_db_result().back()->remote_addr();
- EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress),
- addr->To<std::string>());
-
- // HeartRateService in bluez::FakeBluetoothDeviceClient consists of
- // Service: HeartRateService
- // Characteristic: HeartRateMeasurement
- // Descriptor: ClientCharacteristicConfiguration
- // Characteristic: BodySensorLocation
- // Characteristic: HeartRateControlPoint
- const std::vector<mojom::BluetoothGattDBElementPtr>& db =
- fake_bluetooth_instance_->gatt_db_result().back()->db();
- EXPECT_EQ(5u, db.size());
-
- EXPECT_EQ(device::BluetoothUUID(
- bluez::FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
- db[0]->uuid);
- EXPECT_EQ(mojom::BluetoothGattDBAttributeType::BTGATT_DB_PRIMARY_SERVICE,
- db[0]->type);
-
- EXPECT_EQ(device::BluetoothUUID(bluez::FakeBluetoothGattCharacteristicClient::
- kHeartRateMeasurementUUID),
- db[1]->uuid);
- EXPECT_EQ(mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
- db[1]->type);
- EXPECT_EQ(device::BluetoothGattCharacteristic::PROPERTY_NOTIFY,
- db[1]->properties);
-
- EXPECT_EQ(device::BluetoothUUID(bluez::FakeBluetoothGattDescriptorClient::
- kClientCharacteristicConfigurationUUID),
- db[2]->uuid);
- EXPECT_EQ(mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR,
- db[2]->type);
-
- EXPECT_EQ(device::BluetoothUUID(bluez::FakeBluetoothGattCharacteristicClient::
- kBodySensorLocationUUID),
- db[3]->uuid);
- EXPECT_EQ(mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
- db[3]->type);
- EXPECT_EQ(device::BluetoothGattCharacteristic::PROPERTY_READ,
- db[3]->properties);
-
- EXPECT_EQ(device::BluetoothUUID(bluez::FakeBluetoothGattCharacteristicClient::
- kHeartRateControlPointUUID),
- db[4]->uuid);
- EXPECT_EQ(mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
- db[4]->type);
- EXPECT_EQ(device::BluetoothGattCharacteristic::PROPERTY_WRITE,
- db[4]->properties);
-}
-
-// Invoke multi advertisement methods and make sure they are going down to the
-// D-Bus clients.
-TEST_F(ArcBluetoothBridgeTest, SingleAdvertisement) {
- int32_t handle = ReserveAdvertisementHandle();
- EXPECT_NE(kFailureAdvHandle, handle);
- EXPECT_EQ(0, NumActiveAdvertisements());
-
- auto adv_data = base::MakeUnique<device::BluetoothAdvertisement::Data>(
- device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
- mojom::BluetoothGattStatus status =
- BroadcastAdvertisement(handle, std::move(adv_data));
- EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
- EXPECT_EQ(1, NumActiveAdvertisements());
-
- status = ReleaseAdvertisementHandle(handle);
- EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
- EXPECT_EQ(0, NumActiveAdvertisements());
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/clipboard/arc_clipboard_bridge.cc b/chromium/components/arc/clipboard/arc_clipboard_bridge.cc
index 0e6f1e686c9..40f5932aa18 100644
--- a/chromium/components/arc/clipboard/arc_clipboard_bridge.cc
+++ b/chromium/components/arc/clipboard/arc_clipboard_bridge.cc
@@ -4,56 +4,92 @@
#include "components/arc/clipboard/arc_clipboard_bridge.h"
+#include <utility>
+
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_checker.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/clipboard_types.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
namespace arc {
+namespace {
+
+// Singleton factory for ArcClipboardBridge.
+class ArcClipboardBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcClipboardBridge,
+ ArcClipboardBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcClipboardBridgeFactory";
+
+ static ArcClipboardBridgeFactory* GetInstance() {
+ return base::Singleton<ArcClipboardBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcClipboardBridgeFactory>;
+ ArcClipboardBridgeFactory() = default;
+ ~ArcClipboardBridgeFactory() override = default;
+};
+
+} // namespace
-ArcClipboardBridge::ArcClipboardBridge(ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this) {
- arc_bridge_service()->clipboard()->AddObserver(this);
+// static
+ArcClipboardBridge* ArcClipboardBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcClipboardBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcClipboardBridge::ArcClipboardBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service), binding_(this) {
+ arc_bridge_service_->clipboard()->AddObserver(this);
}
ArcClipboardBridge::~ArcClipboardBridge() {
- DCHECK(CalledOnValidThread());
- arc_bridge_service()->clipboard()->RemoveObserver(this);
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->clipboard()->RemoveObserver(this);
}
void ArcClipboardBridge::OnInstanceReady() {
mojom::ClipboardInstance* clipboard_instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->clipboard(), Init);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->clipboard(), Init);
DCHECK(clipboard_instance);
- clipboard_instance->Init(binding_.CreateInterfacePtrAndBind());
+ mojom::ClipboardHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ clipboard_instance->Init(std::move(host_proxy));
}
void ArcClipboardBridge::SetTextContent(const std::string& text) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ui::ScopedClipboardWriter writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
writer.WriteText(base::UTF8ToUTF16(text));
}
void ArcClipboardBridge::GetTextContent() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::string16 text;
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &text);
mojom::ClipboardInstance* clipboard_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->clipboard(), OnGetTextContent);
+ arc_bridge_service_->clipboard(), OnGetTextContent);
if (!clipboard_instance)
return;
clipboard_instance->OnGetTextContent(base::UTF16ToUTF8(text));
}
-bool ArcClipboardBridge::CalledOnValidThread() {
- // Make sure access to the Chrome clipboard is happening in the UI thread.
- return thread_checker_.CalledOnValidThread();
-}
-
} // namespace arc
diff --git a/chromium/components/arc/clipboard/arc_clipboard_bridge.h b/chromium/components/arc/clipboard/arc_clipboard_bridge.h
index 5b07d73d80f..4ac2e4dd1e0 100644
--- a/chromium/components/arc/clipboard/arc_clipboard_bridge.h
+++ b/chromium/components/arc/clipboard/arc_clipboard_bridge.h
@@ -8,19 +8,31 @@
#include <string>
#include "base/macros.h"
-#include "components/arc/arc_service.h"
#include "components/arc/common/clipboard.mojom.h"
#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
+class ArcBridgeService;
+
class ArcClipboardBridge
- : public ArcService,
+ : public KeyedService,
public InstanceHolder<mojom::ClipboardInstance>::Observer,
public mojom::ClipboardHost {
public:
- explicit ArcClipboardBridge(ArcBridgeService* bridge_service);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcClipboardBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcClipboardBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
~ArcClipboardBridge() override;
// InstanceHolder<mojom::ClipboardInstance>::Observer overrides.
@@ -31,12 +43,11 @@ class ArcClipboardBridge
void GetTextContent() override;
private:
- bool CalledOnValidThread();
+ THREAD_CHECKER(thread_checker_);
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
mojo::Binding<mojom::ClipboardHost> binding_;
- base::ThreadChecker thread_checker_;
-
DISALLOW_COPY_AND_ASSIGN(ArcClipboardBridge);
};
diff --git a/chromium/components/arc/common/accessibility_helper.mojom b/chromium/components/arc/common/accessibility_helper.mojom
index f3651b2356a..4fdba278e07 100644
--- a/chromium/components/arc/common/accessibility_helper.mojom
+++ b/chromium/components/arc/common/accessibility_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: 3
+// Next MinVersion: 4
module arc.mojom;
@@ -69,7 +69,16 @@ enum AccessibilityActionType {
EXPAND,
COLLAPSE,
DISMISS,
- SET_TEXT
+ SET_TEXT,
+ CONTEXT_CLICK,
+ SCROLL_DOWN,
+ SCROLL_LEFT,
+ SCROLL_RIGHT,
+ SCROLL_TO_POSITION,
+ SCROLL_UP,
+ SET_PROGRESS,
+ SHOW_ON_SCREEN,
+ CUSTOM_ACTION // Not a standard action.
};
// Possible boolean properties set on an AccessibilityNodeInfo.
@@ -130,7 +139,12 @@ enum AccessibilityIntProperty {
[Extensible]
enum AccessibilityIntListProperty {
CHILD_NODE_IDS,
- ACTIONS
+ CUSTOM_ACTION_IDS
+};
+
+[Extensible]
+enum AccessibilityStringListProperty {
+ CUSTOM_ACTION_DESCRIPTIONS
};
// AccessibilityNodeInfoData is a struct to contain info of
@@ -144,6 +158,8 @@ struct AccessibilityNodeInfoData {
[MinVersion=1]map<AccessibilityIntProperty, int32>? int_properties;
[MinVersion=1]
map<AccessibilityIntListProperty, array<int32>>? int_list_properties;
+ [MinVersion=3]map<AccessibilityStringListProperty, array<string>>?
+ string_list_properties;
};
// Filters the event type (and implicitly the data) sent by the ARC
@@ -172,6 +188,18 @@ struct AccessibilityEventData {
array<AccessibilityNodeInfoData> node_data;
};
+// AccessibilityActionData is a struct to contain info of AccessibilityAction in
+// Android.
+// https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction.html
+struct AccessibilityActionData {
+ int32 node_id;
+
+ AccessibilityActionType action_type;
+
+ // custom_action_id must be set if action_type is CUSTOM_ACTION.
+ int32 custom_action_id;
+};
+
// Next method ID: 2
interface AccessibilityHelperHost {
OnAccessibilityEventDeprecated@0(AccessibilityEventType event_type,
@@ -182,13 +210,15 @@ interface AccessibilityHelperHost {
OnAccessibilityEvent@1(AccessibilityEventData event_data);
};
-// Next method ID: 3
+// Next method ID: 4
interface AccessibilityHelperInstance {
Init@0(AccessibilityHelperHost host);
- // Perform the specified action on a node requested by a Chrome client.
- PerformAction@1(int32 id, AccessibilityActionType action);
+ PerformActionDeprecated@1(int32 id, AccessibilityActionType action);
// Set a filter on the event types received.
SetFilter@2(AccessibilityFilterType filter_type);
+
+ // Perform an action on a node requested by a Chrome client.
+ [MinVersion=3]PerformAction@3(AccessibilityActionData action_data);
};
diff --git a/chromium/components/arc/common/app.mojom b/chromium/components/arc/common/app.mojom
index 6348d52517e..bfdf4ecf507 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: 20
+// Next MinVersion: 23
module arc.mojom;
@@ -75,6 +75,31 @@ enum ShowPackageInfoPage {
MANAGE_LINKS = 1,
};
+// Describes a Play Store app discovery result.
+struct AppDiscoveryResult {
+ string? launch_intent_uri;
+ string? install_intent_uri;
+ string? label;
+ bool is_instant_app;
+ bool is_recent;
+ string? publisher_name;
+ string? formatted_price;
+ float review_score;
+ array<uint8> icon_png_data;
+ [MinVersion=22] string? package_name;
+};
+
+// Describes the status of an app discovery request.
+[Extensible]
+enum AppDiscoveryRequestState {
+ // Request handled successfully.
+ SUCCESS = 0,
+ // Request canceled when a newer request is sent.
+ CANCELED = 1,
+ // Request failed due to any communication error or Play Store internal error.
+ ERROR = 2,
+};
+
// Next method ID: 18
interface AppHost {
// Sends newly added ARC app to Chrome. This message is sent when ARC receives
@@ -161,7 +186,7 @@ interface AppHost {
};
// TODO(lhchavez): Migrate all request/response messages to Mojo.
-// Next method ID: 16
+// Next method ID: 18
// Deprecated method ID: 9
interface AppInstance {
Init@0(AppHost host_ptr);
@@ -230,7 +255,15 @@ interface AppInstance {
// Sets notification setting for the package.
[MinVersion=6] SetNotificationsEnabled@10(string package_name, bool enabled);
+ // Sends a request to ARC to start PAI flow.
+ [MinVersion=21] StartPaiFlow@17();
+
// 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);
+
+ // Starts a query for Play Store apps.
+ [MinVersion=20] GetRecentAndSuggestedAppsFromPlayStore@16(
+ string query, int32 max_results) =>
+ (AppDiscoveryRequestState state@1, array<AppDiscoveryResult> results@0);
};
diff --git a/chromium/components/arc/common/arc_bridge.mojom b/chromium/components/arc/common/arc_bridge.mojom
index 64e1a1f6fe0..c30c3ae2749 100644
--- a/chromium/components/arc/common/arc_bridge.mojom
+++ b/chromium/components/arc/common/arc_bridge.mojom
@@ -17,6 +17,7 @@ import "file_system.mojom";
import "ime.mojom";
import "intent_helper.mojom";
import "kiosk.mojom";
+import "lock_screen.mojom";
import "metrics.mojom";
import "net.mojom";
import "notifications.mojom";
@@ -31,11 +32,12 @@ import "tts.mojom";
import "video.mojom";
import "voice_interaction_arc_home.mojom";
import "voice_interaction_framework.mojom";
+import "volume_mounter.mojom";
import "wallpaper.mojom";
-// Next MinVersion: 25
+// Next MinVersion: 30
// Deprecated method IDs: 101, 105
-// Next method ID: 131
+// Next method ID: 135
interface ArcBridgeHost {
// Keep the entries alphabetical. In order to do so without breaking
// compatibility with the ARC instance, explicitly assign each interface a
@@ -86,6 +88,9 @@ interface ArcBridgeHost {
// Notifies Chrome that the KioskInstance interface is ready.
[MinVersion=20] OnKioskInstanceReady@126(KioskInstance instance_ptr);
+ // Notifies Chrome that the LockScreenInstance interface is ready.
+ [MinVersion=29] OnLockScreenInstanceReady@134(LockScreenInstance instance_ptr);
+
// Notifies Chrome that the MetricsInstance interface is ready.
[MinVersion=10] OnMetricsInstanceReady@116(MetricsInstance instance_ptr);
@@ -130,6 +135,9 @@ interface ArcBridgeHost {
[MinVersion=23] OnVoiceInteractionFrameworkInstanceReady@129(
VoiceInteractionFrameworkInstance instance_ptr);
+ // Notifies Chrome that the VolumeMounter interface is ready.
+ [MinVersion=25] OnVolumeMounterInstanceReady@131(VolumeMounterInstance instance_ptr);
+
// Notifies Chrome that the WallpaperInstance interface is ready.
[MinVersion=18] OnWallpaperInstanceReady@124(WallpaperInstance instance_ptr);
};
diff --git a/chromium/components/arc/common/audio.mojom b/chromium/components/arc/common/audio.mojom
index 39e02ec514a..07e2b2eace3 100644
--- a/chromium/components/arc/common/audio.mojom
+++ b/chromium/components/arc/common/audio.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: 3
+// Next MinVersion: 4
module arc.mojom;
@@ -13,9 +13,15 @@ enum AudioSwitch {
SW_MICROPHONE_INSERT = 0x04
};
+// Next method ID:2
interface AudioHost {
// Tells the host to show the volume controls.
ShowVolumeControls@0();
+
+ // Request that the volume be changed to |volume|.
+ // This is a privileged API and should only be used on whitelisted cases.
+ // |percent| is of the range [0, 100].
+ [MinVersion=3] OnSystemVolumeUpdateRequest@1(int32 percent);
};
// Next method ID: 3
diff --git a/chromium/components/arc/common/file_system.mojom b/chromium/components/arc/common/file_system.mojom
index 24e4041ff8d..698cf6f5832 100644
--- a/chromium/components/arc/common/file_system.mojom
+++ b/chromium/components/arc/common/file_system.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: 4
+// Next MinVersion: 6
module arc.mojom;
@@ -27,6 +27,12 @@ struct Document {
// TODO(crbug.com/672737): Use mojo.common.mojom.Time once the type is
// converted to a non-native type so that it can be used from Java.
uint64 last_modified;
+
+ // Path to a real file on the Android VFS corresponding to this document,
+ // e.g. "/storage/emulated/0/DCIM/kitten.jpg".
+ // This value is available in limited DocumentsProviders only. If the
+ // provider does not expose real VFS paths, this field is always set to null.
+ [MinVersion=5] string? android_file_system_path;
};
// Describes the type of a change made to a document.
@@ -51,7 +57,7 @@ interface FileSystemHost {
[MinVersion=3] OnDocumentChanged@0(int64 watcher_id, ChangeType type);
};
-// Next method ID: 8
+// Next method ID: 10
interface FileSystemInstance {
// Notes about Android Documents Provider:
//
@@ -62,8 +68,11 @@ interface FileSystemInstance {
// It is the origin part of a content:// URI used to access the Documents
// Provider via Content Resolver protocol.
// Example: "com.android.providers.media.documents"
+ // - A documents provider may provide one or more roots. Each root is identified
+ // by a root ID.
// - A document ID is an opaque string that specifies a particular document
- // in a documents provider. Its format varies by providers.
+ // in a documents provider. Its format varies by providers. Roots also have
+ // associated document IDs.
//
// See the following documents for details about Documents Provider:
// https://developer.android.com/guide/topics/providers/document-provider.html
@@ -105,6 +114,16 @@ interface FileSystemInstance {
// streams), -1 is returned.
[MinVersion=1] GetFileSize@1(string url) => (int64 size);
+ // Asks the ContentResolver to get the MIME type of the file specified by the
+ // URL. When an error occurs, returns null value.
+ [MinVersion=4] GetMimeType@8(string url) => (string? mime_type);
+
+ // Queries recent documents of a root specified by |authority| and |root_id|.
+ // If the root exists and it supports recent document queries, a (possibly
+ // empty) list of documents is returned. Otherwise, null is returned.
+ [MinVersion=5] GetRecentDocuments@9(string authority, string root_id) =>
+ (array<Document>? documents);
+
// Establishes full-duplex communication with the host.
[MinVersion=3] Init@5(FileSystemHost host_ptr);
diff --git a/chromium/components/arc/common/intent_helper.mojom b/chromium/components/arc/common/intent_helper.mojom
index 179d5ea2b64..1f68d5186a7 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: 16
+// Next MinVersion: 17
module arc.mojom;
@@ -84,7 +84,7 @@ struct UrlWithMimeType {
// Handles intents from ARC in Chrome.
// Deprecated method ID: 4
-// Next method ID: 6
+// Next method ID: 7
interface IntentHelperHost {
// Called when icons associated with the package are no longer up to date.
[MinVersion=3] OnIconInvalidated@1(string package_name);
@@ -105,6 +105,9 @@ interface IntentHelperHost {
// Sets an image as the wallpaper.
// |jpeg_data| is a JPEG encoded wallpaper image.
[MinVersion=8] SetWallpaperDeprecated@4(array<uint8> jpeg_data);
+
+ // Opens the volume control.
+ [MinVersion=16] OpenVolumeControl@6();
};
// Sends intents to ARC on behalf of Chrome.
diff --git a/chromium/components/arc/common/lock_screen.mojom b/chromium/components/arc/common/lock_screen.mojom
new file mode 100644
index 00000000000..a19bc32dbbd
--- /dev/null
+++ b/chromium/components/arc/common/lock_screen.mojom
@@ -0,0 +1,13 @@
+// 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.
+//
+// Next MinVersion: 1
+
+module arc.mojom;
+
+// Next method ID: 2
+interface LockScreenInstance {
+ // Notifies the container whether the Chrome OS side lock screen is on/off.
+ SetDeviceLocked@1(bool is_locked);
+};
diff --git a/chromium/components/arc/common/net.mojom b/chromium/components/arc/common/net.mojom
index 4954932d084..c70181e284f 100644
--- a/chromium/components/arc/common/net.mojom
+++ b/chromium/components/arc/common/net.mojom
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// Next MinVersion: 7
+
module arc.mojom;
[Extensible]
@@ -122,11 +124,13 @@ struct NetworkData {
array<WifiConfiguration> networks;
};
-interface NetHost {
- // Sends a request to get configured or visible WiFi networks based on the
- // |configured_only| and |visible_only| flags.
- GetNetworksDeprecated@0(bool configured_only, bool visible_only) => (NetworkData data);
+struct GetNetworksResponseType {
+ NetworkResult status;
+ array<NetworkConfiguration> networks;
+};
+
+interface NetHost {
// Sends a request to get enabled / disabled status of WiFi.
GetWifiEnabledState@1() => (bool is_enabled);
@@ -135,7 +139,7 @@ interface NetHost {
// Sends a request to get configured or visible WiFi networks based on the
// request type.
- [MinVersion=2] GetNetworks@3(GetNetworksRequestType type) => (NetworkData data);
+ [MinVersion=2] GetNetworksDeprecated@3(GetNetworksRequestType type) => (NetworkData data);
// Sends a request to enable or disable WiFi. The |result| is true when the
// the state has been successfully set or WiFi is already in the desired
@@ -161,6 +165,11 @@ interface NetHost {
[MinVersion=5] GetDefaultNetwork@9() => (
NetworkConfiguration? logical_default,
NetworkConfiguration? physical_default);
+
+ // Sends a request to get configured or visible WiFi networks based on the
+ // request type.
+ [MinVersion=6] GetNetworks@10(GetNetworksRequestType type) =>
+ (GetNetworksResponseType response);
};
interface NetInstance {
diff --git a/chromium/components/arc/common/typemaps.gni b/chromium/components/arc/common/typemaps.gni
index 09a41ef8f28..8902a9b60cb 100644
--- a/chromium/components/arc/common/typemaps.gni
+++ b/chromium/components/arc/common/typemaps.gni
@@ -9,5 +9,8 @@ typemaps = [
"//components/arc/common/file_system.typemap",
"//components/arc/common/ime.typemap",
"//components/arc/common/intent_helper.typemap",
- "//components/arc/common/video_accelerator.typemap",
+ "//components/arc/common/video_common.typemap",
+ "//components/arc/common/video_encode_accelerator.typemap",
+ "//components/arc/common/voice_interaction_framework.typemap",
+ "//components/arc/common/volume_mounter.typemap",
]
diff --git a/chromium/components/arc/common/video.mojom b/chromium/components/arc/common/video.mojom
index f8c95829911..42755ada236 100644
--- a/chromium/components/arc/common/video.mojom
+++ b/chromium/components/arc/common/video.mojom
@@ -4,7 +4,8 @@
module arc.mojom;
-import "video_accelerator.mojom";
+import "video_decode_accelerator.mojom";
+import "video_encode_accelerator.mojom";
// Next MinVersion: 5
// Deprecated method IDs: 0
@@ -18,11 +19,18 @@ interface VideoHost {
string token);
};
+// Next MinVersion: 1
+// Next method ID: 1
interface VideoInstance {
// Establishes full-duplex communication with the host.
Init@0(VideoHost host_ptr);
};
+// Next MinVersion: 2
+// Next method ID: 3
interface VideoAcceleratorFactory {
- Create@0(VideoAcceleratorService& accelerator);
+ CreateDecodeAccelerator@0(VideoDecodeAccelerator& decoder);
+
+ [MinVersion=1]
+ CreateEncodeAccelerator@1(VideoEncodeAccelerator& encoder);
};
diff --git a/chromium/components/arc/common/video_accelerator.mojom b/chromium/components/arc/common/video_accelerator.mojom
deleted file mode 100644
index a329c335490..00000000000
--- a/chromium/components/arc/common/video_accelerator.mojom
+++ /dev/null
@@ -1,102 +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.
-
-// This file defined the mojo interface between Android and Chromium for video
-// decoding and encoding. See comments of ArcVideoAccelerator for more info.
-
-module arc.mojom;
-
-[Extensible]
-enum HalPixelFormatExtension {
- HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
- HAL_PIXEL_FORMAT_H264 = 0x34363248,
- HAL_PIXEL_FORMAT_VP8 = 0x00385056,
-};
-
-enum PortType {
- PORT_INPUT = 0,
- PORT_OUTPUT = 1,
-};
-
-struct BufferMetadata {
- int64 timestamp; // in microseconds
- uint32 bytes_used;
-};
-
-struct VideoFormat {
- uint32 pixel_format;
- uint32 buffer_size;
-
- // minimal number of buffers required to process the video.
- uint32 min_num_buffers;
- uint32 coded_width;
- uint32 coded_height;
- uint32 crop_left;
- uint32 crop_width;
- uint32 crop_top;
- uint32 crop_height;
-};
-
-struct ArcVideoAcceleratorConfig {
- enum DeviceType {
- DEVICE_ENCODER = 0,
- DEVICE_DECODER = 1,
- };
-
- DeviceType device_type;
- uint32 num_input_buffers;
- uint32 input_pixel_format;
-};
-
-struct ArcVideoAcceleratorDmabufPlane {
- int32 offset;
- int32 stride;
-};
-
-// Next MinVersion: 4
-// Deprecated method IDs: 2, 7
-// Next method ID: 10
-interface VideoAcceleratorService {
- enum Result {
- SUCCESS = 0,
- ILLEGAL_STATE = 1,
- INVALID_ARGUMENT = 2,
- UNREADABLE_INPUT = 3,
- PLATFORM_FAILURE = 4,
- INSUFFICIENT_RESOURCES = 5,
- };
-
- [MinVersion=2]
- Initialize@8(ArcVideoAcceleratorConfig config,
- VideoAcceleratorServiceClient client) => (Result result);
-
- BindSharedMemory@1(PortType port, uint32 index, handle ashmem_fd,
- uint32 offset, uint32 length);
-
- [MinVersion=3]
- BindDmabuf@9(PortType port, uint32 index, handle dmabuf_fd,
- array<ArcVideoAcceleratorDmabufPlane> dmabuf_planes);
-
- UseBuffer@3(PortType port, uint32 index, BufferMetadata metadata);
-
- SetNumberOfOutputBuffers@4(uint32 number);
-
- Reset@5();
-
- Flush@6();
-};
-
-// Deprecated method IDs: 0
-// Next method ID: 6
-interface VideoAcceleratorServiceClient {
- OnError@1(VideoAcceleratorService.Result error);
-
- OnBufferDone@2(PortType port, uint32 index, BufferMetadata metadata);
-
- OnResetDone@3();
-
- OnOutputFormatChanged@4(VideoFormat format);
-
- OnFlushDone@5();
-};
diff --git a/chromium/components/arc/common/video_accelerator.typemap b/chromium/components/arc/common/video_accelerator.typemap
deleted file mode 100644
index 3461d8658d1..00000000000
--- a/chromium/components/arc/common/video_accelerator.typemap
+++ /dev/null
@@ -1,12 +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.
-
-mojom = "//components/arc/common/video_accelerator.mojom"
-public_headers = [ "//components/arc/video_accelerator/video_accelerator.h" ]
-traits_headers =
- [ "//components/arc/video_accelerator/video_accelerator_struct_traits.h" ]
-sources = [
- "//components/arc/video_accelerator/video_accelerator_struct_traits.cc",
-]
-type_mappings = [ "arc.mojom.ArcVideoAcceleratorDmabufPlane=arc::ArcVideoAcceleratorDmabufPlane[move_only]" ]
diff --git a/chromium/components/arc/common/video_common.mojom b/chromium/components/arc/common/video_common.mojom
new file mode 100644
index 00000000000..237e1466ea3
--- /dev/null
+++ b/chromium/components/arc/common/video_common.mojom
@@ -0,0 +1,21 @@
+// 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 defined the mojo interface between Android and Chromium for video
+// decoding and encoding. See comments of ArcVideoAccelerator for more info.
+
+module arc.mojom;
+
+// The offset and stride of a video frame plane. Both offset and stride must
+// be non negative.
+struct VideoFramePlane {
+ int32 offset;
+ int32 stride;
+};
+
+// The graphics dimension. Both width and height should be non-negative.
+struct Size {
+ int32 width;
+ int32 height;
+};
diff --git a/chromium/components/arc/common/video_common.typemap b/chromium/components/arc/common/video_common.typemap
new file mode 100644
index 00000000000..04e082d329e
--- /dev/null
+++ b/chromium/components/arc/common/video_common.typemap
@@ -0,0 +1,18 @@
+# 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.
+
+mojom = "//components/arc/common/video_common.mojom"
+public_headers = [
+ "//components/arc/video_accelerator/video_accelerator.h",
+ "//ui/gfx/geometry/size.h",
+]
+traits_headers =
+ [ "//components/arc/video_accelerator/video_accelerator_struct_traits.h" ]
+sources = [
+ "//components/arc/video_accelerator/video_accelerator_struct_traits.cc",
+]
+type_mappings = [
+ "arc.mojom.VideoFramePlane=arc::VideoFramePlane[move_only]",
+ "arc.mojom.Size=gfx::Size",
+]
diff --git a/chromium/components/arc/common/video_decode_accelerator.mojom b/chromium/components/arc/common/video_decode_accelerator.mojom
new file mode 100644
index 00000000000..bb732f96caa
--- /dev/null
+++ b/chromium/components/arc/common/video_decode_accelerator.mojom
@@ -0,0 +1,101 @@
+// 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.
+
+// This file defined the mojo interface between Android and Chromium for video
+// decoding. See comments of VideoDecodeAccelerator for more info.
+
+module arc.mojom;
+
+import "video_common.mojom";
+
+[Extensible]
+enum HalPixelFormatExtension {
+ HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
+ HAL_PIXEL_FORMAT_H264 = 0x34363248,
+ HAL_PIXEL_FORMAT_VP8 = 0x00385056,
+};
+
+enum PortType {
+ PORT_INPUT = 0,
+ PORT_OUTPUT = 1,
+};
+
+struct BufferMetadata {
+ int64 timestamp; // in microseconds
+ uint32 bytes_used;
+};
+
+struct VideoFormat {
+ uint32 pixel_format;
+ uint32 buffer_size;
+
+ // minimal number of buffers required to process the video.
+ uint32 min_num_buffers;
+ uint32 coded_width;
+ uint32 coded_height;
+ uint32 crop_left;
+ uint32 crop_width;
+ uint32 crop_top;
+ uint32 crop_height;
+};
+
+struct VideoDecodeAcceleratorConfig {
+ // Deprecated. This config struct is used for decoder only.
+ enum DeviceTypeDeprecated {
+ DEVICE_ENCODER = 0,
+ DEVICE_DECODER = 1,
+ };
+
+ // Deprecated. Only decoder will be supported.
+ DeviceTypeDeprecated device_type_deprecated;
+ uint32 num_input_buffers;
+ uint32 input_pixel_format;
+};
+
+// Next MinVersion: 4
+// Deprecated method IDs: 2, 7
+// Next method ID: 10
+interface VideoDecodeAccelerator {
+ enum Result {
+ SUCCESS = 0,
+ ILLEGAL_STATE = 1,
+ INVALID_ARGUMENT = 2,
+ UNREADABLE_INPUT = 3,
+ PLATFORM_FAILURE = 4,
+ INSUFFICIENT_RESOURCES = 5,
+ };
+
+ [MinVersion=2]
+ Initialize@8(VideoDecodeAcceleratorConfig config,
+ VideoDecodeClient client) => (Result result);
+
+ BindSharedMemory@1(PortType port, uint32 index, handle ashmem_fd,
+ uint32 offset, uint32 length);
+
+ [MinVersion=3]
+ BindDmabuf@9(PortType port, uint32 index, handle dmabuf_fd,
+ array<VideoFramePlane> planes);
+
+ UseBuffer@3(PortType port, uint32 index, BufferMetadata metadata);
+
+ SetNumberOfOutputBuffers@4(uint32 number);
+
+ Reset@5();
+
+ Flush@6();
+};
+
+// Deprecated method IDs: 0
+// Next method ID: 6
+interface VideoDecodeClient {
+ OnError@1(VideoDecodeAccelerator.Result error);
+
+ OnBufferDone@2(PortType port, uint32 index, BufferMetadata metadata);
+
+ OnResetDone@3();
+
+ OnOutputFormatChanged@4(VideoFormat format);
+
+ OnFlushDone@5();
+};
diff --git a/chromium/components/arc/common/video_encode_accelerator.mojom b/chromium/components/arc/common/video_encode_accelerator.mojom
new file mode 100644
index 00000000000..5f819a04d21
--- /dev/null
+++ b/chromium/components/arc/common/video_encode_accelerator.mojom
@@ -0,0 +1,196 @@
+// 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 defined the mojo interface between Android and Chromium for video
+// encoding.
+
+module arc.mojom;
+
+import "video_common.mojom";
+
+[Extensible]
+enum VideoPixelFormat {
+ // The values must match to the values in media::VideoPixelFormat
+ PIXEL_FORMAT_UNKNOWN = 0,
+ PIXEL_FORMAT_I420 = 1,
+};
+
+[Extensible]
+enum VideoCodecProfile {
+ // The values must match to the values in media::VideoCodecProfile.
+ VIDEO_CODEC_PROFILE_UNKNOWN = -1,
+ VIDEO_CODEC_PROFILE_MIN = VIDEO_CODEC_PROFILE_UNKNOWN,
+ H264PROFILE_MIN = 0,
+
+ // Including profile Constrained Baseline (CBP).
+ H264PROFILE_BASELINE = H264PROFILE_MIN,
+ H264PROFILE_MAIN = 1,
+ H264PROFILE_EXTENDED = 2,
+ H264PROFILE_HIGH = 3,
+ H264PROFILE_HIGH10PROFILE = 4,
+ H264PROFILE_HIGH422PROFILE = 5,
+ H264PROFILE_HIGH444PREDICTIVEPROFILE = 6,
+ H264PROFILE_SCALABLEBASELINE = 7,
+ H264PROFILE_SCALABLEHIGH = 8,
+ H264PROFILE_STEREOHIGH = 9,
+ H264PROFILE_MULTIVIEWHIGH = 10,
+ H264PROFILE_MAX = H264PROFILE_MULTIVIEWHIGH,
+ VP8PROFILE_MIN = 11,
+ VP8PROFILE_ANY = VP8PROFILE_MIN,
+ VP8PROFILE_MAX = VP8PROFILE_ANY,
+ VP9PROFILE_MIN = 12,
+ VP9PROFILE_PROFILE0 = VP9PROFILE_MIN,
+ VP9PROFILE_PROFILE1 = 13,
+ VP9PROFILE_PROFILE2 = 14,
+ VP9PROFILE_PROFILE3 = 15,
+ VP9PROFILE_MAX = VP9PROFILE_PROFILE3,
+ HEVCPROFILE_MIN = 16,
+ HEVCPROFILE_MAIN = HEVCPROFILE_MIN,
+ HEVCPROFILE_MAIN10 = 17,
+ HEVCPROFILE_MAIN_STILL_PICTURE = 18,
+ HEVCPROFILE_MAX = HEVCPROFILE_MAIN_STILL_PICTURE,
+ DOLBYVISION_MIN = 19,
+ DOLBYVISION_PROFILE0 = DOLBYVISION_MIN,
+ DOLBYVISION_PROFILE4 = 20,
+ DOLBYVISION_PROFILE5 = 21,
+ DOLBYVISION_PROFILE7 = 22,
+ DOLBYVISION_MAX = DOLBYVISION_PROFILE7,
+ VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_MAX,
+};
+
+// Specification of an encoding profile supported by an encoder.
+struct VideoEncodeProfile {
+ VideoCodecProfile profile;
+ Size max_resolution;
+ uint32 max_framerate_numerator;
+ uint32 max_framerate_denominator;
+};
+
+// Video encoder IPC interface.
+// Next MinVersion: 1
+// Next Method ID: 5
+interface VideoEncodeAccelerator {
+
+ // The input buffer storage type.
+ [Extensible]
+ enum StorageType {
+ SHARED_MEMORY,
+ DMABUF,
+ };
+
+ // Enumeration of potential errors generated by the API.
+ [Extensible]
+ enum Error {
+ // An operation was attempted during an incompatible encoder state.
+ kIllegalStateError = 0,
+ // Invalid argument was passed to an API method.
+ kInvalidArgumentError = 1,
+ // A failure occurred at the GPU process or one of its dependencies.
+ // Examples of such failures include GPU hardware failures, GPU driver
+ // failures, GPU library failures, GPU process programming errors, and so
+ // on.
+ kPlatformFailureError = 2,
+ kErrorMax = kPlatformFailureError,
+ };
+
+ // Returns an array of the supported profiles of the video encoder. This can
+ // be called before Initialize().
+ GetSupportedProfiles@0() => (array<VideoEncodeProfile> profiles);
+
+ // Initializes the video encoder with specific configuration. Called once per
+ // encoder construction. This call returns true iff initialization is
+ // successful.
+ // Parameters:
+ // |input_format| is the pixel format of the input frames.
+ // |visible_size| is the resolution of the input frames.
+ // |input_storage| the type of the input buffer.
+ // |output_profile| is the codec profile of the encoded output stream.
+ // |initial_bitrate| is the initial bitrate of the encoded output stream,
+ // in bits per second.
+ // |client| is the client of this video encoder. The client must be valid
+ // during the lifetime of this accelerator.
+ Initialize@1(VideoPixelFormat input_format,
+ Size visible_size,
+ StorageType input_storage,
+ VideoCodecProfile output_profile,
+ uint32 initial_bitrate,
+ VideoEncodeClient client) => (bool success);
+
+ // Encodes the given frame.
+ // Parameters:
+ // |frame_fd| is the handle of the video frame buffer. This could be the
+ // file descriptor of the shared memory or the dmabuf, depends on the
+ // storage type assigned in Initialize().
+ // |planes| is arrays of offset and stride of planes in the video frame.
+ // |timestamp| the timestamp of the video frame(in microseconds).
+ // |force_keyframe| forces the encoding of a keyframe for this frame.
+ Encode@2(handle frame_fd,
+ array<VideoFramePlane> planes,
+ int64 timestamp,
+ bool force_keyframe);
+
+ // Sends a bitstream buffer to the encoder for storing future encoded output.
+ // Each call here will cause the shared memory buffer to be filled once, then
+ // returned with VideoEncodeClient::BitstreamBufferReady().
+ // Parameters:
+ // |bitstream_buffer_id| is the id of the bitstream buffer. It is used to
+ // identify the bitstream in VideoEncodeClient::BitstreamBufferReady().
+ // |shmem_fd| is the file descriptor of the shared memory.
+ // |offset| and |size| define the region in the shared memory to be used
+ // as the bitstream buffer.
+ UseOutputBitstreamBuffer@3(int32 bitstream_buffer_id,
+ handle shmem_fd,
+ uint32 offset,
+ uint32 size);
+
+ // Requests a change to the encoding parameters. This is only a request,
+ // fulfilled on a best-effort basis.
+ // Parameters:
+ // |bitrate| is the requested new bitrate, in bits per second.
+ // |framerate| is the requested new framerate, in frames per second.
+ RequestEncodingParametersChange@4(uint32 bitrate,
+ uint32 framerate);
+};
+
+// Interface for clients that use VideoEncodeAccelerator. These callbacks will
+// not be made unless VideoEncodeAccelerator::Initialize() has returned
+// successfully.
+// Next MinVersion: 1
+// Next Method ID: 3
+interface VideoEncodeClient {
+ // Callback to tell the client what size of frames and buffers to provide
+ // for input and output. The VEA disclaims use or ownership of all previously
+ // provided buffers once this callback is made.
+ // Parameters:
+ // |input_count| is the number of input buffers required for encoding.
+ // The client should be prepared to feed at least this many frames into the
+ // encoder before being returned any input frames, since the encoder may
+ // need to hold onto some subset of inputs as reference pictures.
+ // |input_coded_size| is the logical size of the input frames, in pixels.
+ // The encoder may have hardware alignment requirements that make this
+ // different from |visible_size|, as requested in Initialize(), in which
+ // case the input video frame to Encode() should be padded appropriately.
+ // |output_buffer_size| is the required size of output buffers for this
+ // encoder in bytes.
+ RequireBitstreamBuffers@0(uint32 input_count,
+ Size input_coded_size,
+ uint32 output_buffer_size);
+
+ // Callback to deliver encoded bitstream buffers. Ownership of the buffer is
+ // transferred back to the VideoEncodeClient once this callback is made.
+ // Parameters:
+ // |bitstream_buffer_id| is the id of the buffer that is ready.
+ // |payload_size| is the byte size of the used portion of the buffer.
+ // |key_frame| is true if this delivered frame is a keyframe.
+ // |timestamp| is the same timestamp as the one passed to Encode().
+ BitstreamBufferReady@1(int32 bitstream_buffer_id,
+ uint32 payload_size,
+ bool key_frame,
+ int64 timestamp);
+
+ // Error notification callback. Note that errors in
+ // VideoEncodeAccelerator::Initialize() will not be reported here, but will
+ // instead be indicated by a false return value there.
+ NotifyError@2(VideoEncodeAccelerator.Error error);
+};
diff --git a/chromium/components/arc/common/video_encode_accelerator.typemap b/chromium/components/arc/common/video_encode_accelerator.typemap
new file mode 100644
index 00000000000..61b8db22915
--- /dev/null
+++ b/chromium/components/arc/common/video_encode_accelerator.typemap
@@ -0,0 +1,19 @@
+# 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.
+
+mojom = "//components/arc/common/video_encode_accelerator.mojom"
+public_headers = [ "//media/video/video_encode_accelerator.h" ]
+public_deps = [
+ "//media",
+]
+traits_headers = [ "//components/arc/video_accelerator/video_encode_accelerator_struct_traits.h" ]
+sources = [
+ "//components/arc/video_accelerator/video_encode_accelerator_struct_traits.cc",
+]
+type_mappings = [
+ "arc.mojom.VideoEncodeAccelerator.Error=media::VideoEncodeAccelerator::Error",
+ "arc.mojom.VideoPixelFormat=media::VideoPixelFormat",
+ "arc.mojom.VideoCodecProfile=media::VideoCodecProfile",
+ "arc.mojom.VideoEncodeProfile=media::VideoEncodeAccelerator::SupportedProfile",
+]
diff --git a/chromium/components/arc/common/voice_interaction_arc_home.mojom b/chromium/components/arc/common/voice_interaction_arc_home.mojom
index 37e7b372df8..4db7ffffbd9 100644
--- a/chromium/components/arc/common/voice_interaction_arc_home.mojom
+++ b/chromium/components/arc/common/voice_interaction_arc_home.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: 2
+// Next MinVersion: 3
module arc.mojom;
@@ -47,6 +47,10 @@ struct VoiceInteractionStructure {
// Children of current node
array<VoiceInteractionStructure> children;
+
+ // Accessibility functionality of the node inferred from DOM or based on HTML
+ // role attribute.
+ [MinVersion=2] string? role;
};
// Handles voice interaction queries from Android.
diff --git a/chromium/components/arc/common/voice_interaction_framework.mojom b/chromium/components/arc/common/voice_interaction_framework.mojom
index 88ac74cfe4a..aaa0a4791de 100644
--- a/chromium/components/arc/common/voice_interaction_framework.mojom
+++ b/chromium/components/arc/common/voice_interaction_framework.mojom
@@ -2,14 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Next MinVersion: 4
+// Next MinVersion: 11
module arc.mojom;
import "screen_rect.mojom";
+[Extensible]
+enum VoiceInteractionState {
+ NOT_READY = 0,
+ STOPPED,
+ RUNNING
+};
+
// Handles voice interaction queries from Android.
-// Next method ID: 4
+// Next method ID: 6
interface VoiceInteractionFrameworkHost {
// Returns a screenshot of currently focused window or empty array if
// no window is focused. |data| represents the image encoded in JPEG
@@ -25,20 +32,59 @@ interface VoiceInteractionFrameworkHost {
// Enables/disables screenshot taking.
[MinVersion=3]SetMetalayerEnabled@3(bool enabled);
+
+ // Notifies Chrome whether voice interaction session is running.
+ [MinVersion=6]SetVoiceInteractionRunning@4(bool running);
+
+ // Notifies Chrome the state of voice interaction session.
+ [MinVersion=8]SetVoiceInteractionState@5(VoiceInteractionState state);
+};
+
+// Indicates voice interaction configuration status.
+struct VoiceInteractionStatus {
+ // Whether voice interaction is configured during OOBE flow. |false| if
+ // OOBE flow has been skipped.
+ bool configured;
+ // Whether voice interaction service is enabled.
+ bool voice_interaction_enabled;
+ // Whether allow voice interaction service to request screenshot
+ // and screen context.
+ bool context_enabled;
};
// Connects with Android system server.
-// Next method ID: 4
+// Next method ID: 10
interface VoiceInteractionFrameworkInstance {
Init@0(VoiceInteractionFrameworkHost host_ptr);
// Starts the voice interaction session in container.
StartVoiceInteractionSession@1();
+ // Starts or stops voice interaction session based on current state.
+ [MinVersion=9] ToggleVoiceInteractionSession@8();
+
// Starts the voice interaction session in container, with a screen region
// selected.
[MinVersion=1] StartVoiceInteractionSessionForRegion@2(ScreenRect region);
// Shows/hides the metalayer in the container.
[MinVersion=1] SetMetalayerVisibility@3([MinVersion=2] bool visible);
+
+ // Turns on / off voice interaction in container.
+ [MinVersion=4] SetVoiceInteractionEnabled@4(bool enable);
+
+ // Turns on / off context for voice interaction in container. This function
+ // controls whether screenshot and view hierarchy information should be sent
+ // to container.
+ [MinVersion=4] SetVoiceInteractionContextEnabled@5(bool enable);
+
+ // Starts the voice interaction setup wizard in container.
+ [MinVersion=5] StartVoiceInteractionSetupWizard@6();
+
+ // Starts the voice interaction setup wizard in container.
+ [MinVersion=10] ShowVoiceInteractionSettings@9();
+
+ // Queries voice interaction settings status.
+ [MinVersion=7] GetVoiceInteractionSettings@7() =>
+ (VoiceInteractionStatus status);
};
diff --git a/chromium/components/arc/common/voice_interaction_framework.typemap b/chromium/components/arc/common/voice_interaction_framework.typemap
new file mode 100644
index 00000000000..825471951f7
--- /dev/null
+++ b/chromium/components/arc/common/voice_interaction_framework.typemap
@@ -0,0 +1,13 @@
+# 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.
+
+mojom = "//components/arc/common/voice_interaction_framework.mojom"
+public_headers = [ "//ash/public/cpp/voice_interaction_state.h" ]
+public_deps = [
+ "//ash/public/cpp:ash_public_cpp",
+]
+traits_headers =
+ [ "//components/arc/voice_interaction/voice_interaction_struct_traits.h" ]
+sources = []
+type_mappings = [ "arc.mojom.VoiceInteractionState=ash::VoiceInteractionState" ]
diff --git a/chromium/components/arc/common/volume_mounter.mojom b/chromium/components/arc/common/volume_mounter.mojom
new file mode 100644
index 00000000000..64718211725
--- /dev/null
+++ b/chromium/components/arc/common/volume_mounter.mojom
@@ -0,0 +1,45 @@
+// 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.
+
+module arc.mojom;
+
+[Extensible]
+enum MountEvent {
+ // Mounts a volume in Android.
+ MOUNTING,
+ // Unmounts a volume in Android.
+ UNMOUNTING,
+};
+
+[Extensible]
+enum DeviceType {
+ // Neither USB device nor SD card is inserted.
+ DEVICE_TYPE_UNKNOWN,
+ // USB device is inserted.
+ DEVICE_TYPE_USB,
+ // SD card is inserted.
+ DEVICE_TYPE_SD,
+};
+
+struct MountPointInfo {
+ // The type of mount event to be triggered in Android.
+ MountEvent mount_event;
+ // The device source path of this mount point.
+ string source_path;
+ // The path to the mounted volume.
+ string mount_path;
+ // The fs_uuid of the disk. If empty, the event should be ignored.
+ string fs_uuid;
+ // The file system label.
+ string label;
+ // The device type that contains this mount point. If unknown, the event should be ignored.
+ DeviceType device_type;
+};
+
+// Notifies Android about Mounting events.
+interface VolumeMounterInstance {
+ // @0 is reserved for Init().
+ // Triggers a mount event in Android.
+ OnMountEvent@1(MountPointInfo mount_point_info);
+};
diff --git a/chromium/components/arc/common/volume_mounter.typemap b/chromium/components/arc/common/volume_mounter.typemap
new file mode 100644
index 00000000000..6198166d87e
--- /dev/null
+++ b/chromium/components/arc/common/volume_mounter.typemap
@@ -0,0 +1,17 @@
+# 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.
+
+mojom = "//components/arc/common/volume_mounter.mojom"
+public_headers = [ "//chromeos/disks/disk_mount_manager.h" ]
+traits_headers = [ "//components/arc/volume_mounter/volume_mounter_traits.h" ]
+sources = [
+ "//components/arc/volume_mounter/volume_mounter_traits.cc",
+]
+deps = [
+ "//chromeos",
+]
+type_mappings = [
+ "arc.mojom.DeviceType=chromeos::DeviceType",
+ "arc.mojom.MountEvent=chromeos::disks::DiskMountManager::MountEvent",
+]
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 2ca3da4f26a..109832b532d 100644
--- a/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc
+++ b/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc
@@ -10,8 +10,12 @@
#include <utility>
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "base/process/launch.h"
+#include "base/task_scheduler/post_task.h"
+#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"
namespace {
@@ -47,25 +51,56 @@ void RunCrashReporter(const std::string& crash_type,
} // namespace
namespace arc {
+namespace {
+
+// Singleton factory for ArcCrashCollectorBridge.
+class ArcCrashCollectorBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcCrashCollectorBridge,
+ ArcCrashCollectorBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcCrashCollectorBridgeFactory";
+
+ static ArcCrashCollectorBridgeFactory* GetInstance() {
+ return base::Singleton<ArcCrashCollectorBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcCrashCollectorBridgeFactory>;
+ ArcCrashCollectorBridgeFactory() = default;
+ ~ArcCrashCollectorBridgeFactory() override = default;
+};
+
+} // namespace
+
+// static
+ArcCrashCollectorBridge* ArcCrashCollectorBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcCrashCollectorBridgeFactory::GetForBrowserContext(context);
+}
ArcCrashCollectorBridge::ArcCrashCollectorBridge(
- ArcBridgeService* bridge,
- scoped_refptr<base::TaskRunner> blocking_task_runner)
- : ArcService(bridge),
- blocking_task_runner_(blocking_task_runner),
- binding_(this) {
- arc_bridge_service()->crash_collector()->AddObserver(this);
+ content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service), binding_(this) {
+ arc_bridge_service_->crash_collector()->AddObserver(this);
}
ArcCrashCollectorBridge::~ArcCrashCollectorBridge() {
- arc_bridge_service()->crash_collector()->RemoveObserver(this);
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->crash_collector()->RemoveObserver(this);
}
void ArcCrashCollectorBridge::OnInstanceReady() {
mojom::CrashCollectorHostPtr host_ptr;
binding_.Bind(mojo::MakeRequest(&host_ptr));
- auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->crash_collector(), Init);
+ auto* instance =
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->crash_collector(), Init);
DCHECK(instance);
instance->Init(std::move(host_ptr));
}
@@ -75,9 +110,10 @@ void ArcCrashCollectorBridge::DumpCrash(const std::string& type,
mojo::edk::ScopedPlatformHandle pipe_handle;
mojo::edk::PassWrappedPlatformHandle(pipe.release().value(), &pipe_handle);
- blocking_task_runner_->PostTask(
- FROM_HERE, base::Bind(&RunCrashReporter, type, device_, board_, cpu_abi_,
- base::Passed(std::move(pipe_handle))));
+ base::PostTaskWithTraits(
+ FROM_HERE, {base::WithBaseSyncPrimitives()},
+ base::BindOnce(&RunCrashReporter, type, device_, board_, cpu_abi_,
+ base::Passed(std::move(pipe_handle))));
}
void ArcCrashCollectorBridge::SetBuildProperties(const std::string& device,
diff --git a/chromium/components/arc/crash_collector/arc_crash_collector_bridge.h b/chromium/components/arc/crash_collector/arc_crash_collector_bridge.h
index ec4fcdffb92..17cf7850c8b 100644
--- a/chromium/components/arc/crash_collector/arc_crash_collector_bridge.h
+++ b/chromium/components/arc/crash_collector/arc_crash_collector_bridge.h
@@ -8,15 +8,14 @@
#include <string>
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/arc/arc_service.h"
#include "components/arc/common/crash_collector.mojom.h"
#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
-namespace base {
-class TaskRunner;
-} // namespace base
+namespace content {
+class BrowserContext;
+} // namespace content
namespace arc {
@@ -24,12 +23,18 @@ class ArcBridgeService;
// Relays dumps for non-native ARC crashes to the crash reporter in Chrome OS.
class ArcCrashCollectorBridge
- : public ArcService,
+ : public KeyedService,
public InstanceHolder<mojom::CrashCollectorInstance>::Observer,
public mojom::CrashCollectorHost {
public:
- ArcCrashCollectorBridge(ArcBridgeService* bridge,
- scoped_refptr<base::TaskRunner> blocking_task_runner);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcCrashCollectorBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcCrashCollectorBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge);
+
~ArcCrashCollectorBridge() override;
// InstanceHolder<mojom::CrashCollectorInstance>::Observer overrides.
@@ -43,7 +48,7 @@ class ArcCrashCollectorBridge
const std::string& cpu_abi) override;
private:
- scoped_refptr<base::TaskRunner> blocking_task_runner_;
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
mojo::Binding<mojom::CrashCollectorHost> binding_;
diff --git a/chromium/components/arc/ime/arc_ime_bridge_impl.cc b/chromium/components/arc/ime/arc_ime_bridge_impl.cc
index 99abcd0a14a..4ede0f47ed4 100644
--- a/chromium/components/arc/ime/arc_ime_bridge_impl.cc
+++ b/chromium/components/arc/ime/arc_ime_bridge_impl.cc
@@ -4,12 +4,14 @@
#include "components/arc/ime/arc_ime_bridge_impl.h"
+#include <string>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
#include "ui/base/ime/composition_text.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/geometry/rect.h"
@@ -79,13 +81,20 @@ ArcImeBridgeImpl::ArcImeBridgeImpl(Delegate* delegate,
}
ArcImeBridgeImpl::~ArcImeBridgeImpl() {
- bridge_service_->ime()->RemoveObserver(this);
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ bridge_service_->ime()->RemoveObserver(this);
}
void ArcImeBridgeImpl::OnInstanceReady() {
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(bridge_service_->ime(), Init);
DCHECK(instance);
- instance->Init(binding_.CreateInterfacePtrAndBind());
+ mojom::ImeHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ instance->Init(std::move(host_proxy));
}
void ArcImeBridgeImpl::SendSetCompositionText(
diff --git a/chromium/components/arc/ime/arc_ime_service.cc b/chromium/components/arc/ime/arc_ime_service.cc
index 3ce001364a9..a3297c4e315 100644
--- a/chromium/components/arc/ime/arc_ime_service.cc
+++ b/chromium/components/arc/ime/arc_ime_service.cc
@@ -7,7 +7,9 @@
#include <utility>
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "base/strings/utf_string_conversions.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/ime/arc_ime_bridge_impl.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
@@ -38,10 +40,15 @@ class ArcWindowDelegateImpl : public ArcImeService::ArcWindowDelegate {
}
void RegisterFocusObserver() override {
+ DCHECK(exo::WMHelper::HasInstance());
exo::WMHelper::GetInstance()->AddFocusObserver(ime_service_);
}
void UnregisterFocusObserver() override {
+ // If WMHelper is already destroyed, do nothing.
+ // TODO(crbug.com/748380): Fix shutdown order.
+ if (!exo::WMHelper::HasInstance())
+ return;
exo::WMHelper::GetInstance()->RemoveFocusObserver(ime_service_);
}
@@ -51,14 +58,39 @@ class ArcWindowDelegateImpl : public ArcImeService::ArcWindowDelegate {
DISALLOW_COPY_AND_ASSIGN(ArcWindowDelegateImpl);
};
+// Singleton factory for ArcImeService.
+class ArcImeServiceFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcImeService,
+ ArcImeServiceFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcImeServiceFactory";
+
+ static ArcImeServiceFactory* GetInstance() {
+ return base::Singleton<ArcImeServiceFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcImeServiceFactory>;
+ ArcImeServiceFactory() = default;
+ ~ArcImeServiceFactory() override = default;
+};
+
} // anonymous namespace
////////////////////////////////////////////////////////////////////////////////
// ArcImeService main implementation:
-ArcImeService::ArcImeService(ArcBridgeService* bridge_service)
- : ArcService(bridge_service),
- ime_bridge_(new ArcImeBridgeImpl(this, bridge_service)),
+// static
+ArcImeService* ArcImeService::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcImeServiceFactory::GetForBrowserContext(context);
+}
+
+ArcImeService::ArcImeService(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : ime_bridge_(new ArcImeBridgeImpl(this, bridge_service)),
arc_window_delegate_(new ArcWindowDelegateImpl(this)),
ime_type_(ui::TEXT_INPUT_TYPE_NONE),
has_composition_text_(false),
diff --git a/chromium/components/arc/ime/arc_ime_service.h b/chromium/components/arc/ime/arc_ime_service.h
index 8ffae1f0205..5826c24d19b 100644
--- a/chromium/components/arc/ime/arc_ime_service.h
+++ b/chromium/components/arc/ime/arc_ime_service.h
@@ -8,9 +8,9 @@
#include <memory>
#include "base/macros.h"
-#include "components/arc/arc_service.h"
#include "components/arc/ime/arc_ime_bridge.h"
#include "components/exo/wm_helper.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "ui/aura/env_observer.h"
#include "ui/aura/window_observer.h"
#include "ui/base/ime/text_input_client.h"
@@ -22,11 +22,15 @@
namespace aura {
class Window;
-}
+} // namespace aura
+
+namespace content {
+class BrowserContext;
+} // namespace content
namespace ui {
class InputMethod;
-}
+} // namespace ui
namespace arc {
@@ -34,7 +38,7 @@ class ArcBridgeService;
// This class implements ui::TextInputClient and makes ARC windows behave
// as a text input target in Chrome OS environment.
-class ArcImeService : public ArcService,
+class ArcImeService : public KeyedService,
public ArcImeBridge::Delegate,
public aura::EnvObserver,
public aura::WindowObserver,
@@ -42,7 +46,12 @@ class ArcImeService : public ArcService,
public keyboard::KeyboardControllerObserver,
public ui::TextInputClient {
public:
- explicit ArcImeService(ArcBridgeService* bridge_service);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcImeService* GetForBrowserContext(content::BrowserContext* context);
+
+ ArcImeService(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
~ArcImeService() override;
class ArcWindowDelegate {
diff --git a/chromium/components/arc/ime/arc_ime_service_unittest.cc b/chromium/components/arc/ime/arc_ime_service_unittest.cc
index b141a3c3a4b..5a35fe7933f 100644
--- a/chromium/components/arc/ime/arc_ime_service_unittest.cc
+++ b/chromium/components/arc/ime/arc_ime_service_unittest.cc
@@ -146,7 +146,8 @@ class ArcImeServiceTest : public testing::Test {
private:
void SetUp() override {
arc_bridge_service_ = base::MakeUnique<ArcBridgeService>();
- instance_ = base::MakeUnique<ArcImeService>(arc_bridge_service_.get());
+ instance_ =
+ base::MakeUnique<ArcImeService>(nullptr, arc_bridge_service_.get());
fake_arc_ime_bridge_ = new FakeArcImeBridge();
instance_->SetImeBridgeForTesting(base::WrapUnique(fake_arc_ime_bridge_));
diff --git a/chromium/components/arc/instance_holder.h b/chromium/components/arc/instance_holder.h
index fda056e6461..48f833f4fcc 100644
--- a/chromium/components/arc/instance_holder.h
+++ b/chromium/components/arc/instance_holder.h
@@ -64,8 +64,7 @@ class InstanceHolder {
uint32_t min_version,
const char method_name_for_logging[]) {
if (!instance_) {
- VLOG(1) << "Instance for " << T::Name_ << "::" << method_name_for_logging
- << " not available.";
+ VLOG(1) << "Instance " << T::Name_ << " not available.";
return nullptr;
}
if (version_ < min_version) {
@@ -82,7 +81,7 @@ class InstanceHolder {
// class was created on. RemoveObserver does nothing if |observer| is not in
// the list.
void AddObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
observer_list_.AddObserver(observer);
if (instance_)
@@ -90,7 +89,7 @@ class InstanceHolder {
}
void RemoveObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
observer_list_.RemoveObserver(observer);
}
@@ -98,7 +97,7 @@ class InstanceHolder {
// This can be called in both case; on ready, and on closed.
// Passing nullptr to |instance| means closing.
void SetInstance(T* instance, uint32_t version = T::Version_) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(instance == nullptr || instance_ == nullptr);
// Note: This can be called with nullptr even if |instance_| is still
@@ -123,7 +122,7 @@ class InstanceHolder {
T* instance_ = nullptr;
uint32_t version_ = 0;
- base::ThreadChecker thread_checker_;
+ THREAD_CHECKER(thread_checker_);
base::ObserverList<Observer> observer_list_;
DISALLOW_COPY_AND_ASSIGN(InstanceHolder<T>);
diff --git a/chromium/components/arc/intent_helper/activity_icon_loader.cc b/chromium/components/arc/intent_helper/activity_icon_loader.cc
index 042487231cd..54c43186430 100644
--- a/chromium/components/arc/intent_helper/activity_icon_loader.cc
+++ b/chromium/components/arc/intent_helper/activity_icon_loader.cc
@@ -13,7 +13,7 @@
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
-#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/arc_util.h"
@@ -160,7 +160,7 @@ ActivityIconLoader::ActivityIconLoader()
ActivityIconLoader::~ActivityIconLoader() = default;
void ActivityIconLoader::InvalidateIcons(const std::string& package_name) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto it = cached_icons_.begin(); it != cached_icons_.end();) {
if (it->first.package_name == package_name)
it = cached_icons_.erase(it);
@@ -172,7 +172,7 @@ void ActivityIconLoader::InvalidateIcons(const std::string& package_name) {
ActivityIconLoader::GetResult ActivityIconLoader::GetActivityIcons(
const std::vector<ActivityName>& activities,
const OnIconsReadyCallback& cb) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::unique_ptr<ActivityToIconsMap> result(new ActivityToIconsMap);
std::vector<mojom::ActivityNamePtr> activities_to_fetch;
@@ -239,11 +239,9 @@ void ActivityIconLoader::OnIconsReady(
std::unique_ptr<ActivityToIconsMap> cached_result,
const OnIconsReadyCallback& cb,
std::vector<mojom::ActivityIconPtr> icons) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ArcServiceManager* manager = ArcServiceManager::Get();
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::PostTaskAndReplyWithResult(
- manager->blocking_task_runner().get(), FROM_HERE,
- base::Bind(&ResizeAndEncodeIcons, base::Passed(&icons)),
+ FROM_HERE, base::Bind(&ResizeAndEncodeIcons, base::Passed(&icons)),
base::Bind(&ActivityIconLoader::OnIconsResized,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&cached_result),
cb));
@@ -253,7 +251,7 @@ void ActivityIconLoader::OnIconsResized(
std::unique_ptr<ActivityToIconsMap> cached_result,
const OnIconsReadyCallback& cb,
std::unique_ptr<ActivityToIconsMap> result) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Update |cached_icons_|.
for (const auto& kv : *result) {
cached_icons_.erase(kv.first);
diff --git a/chromium/components/arc/intent_helper/activity_icon_loader.h b/chromium/components/arc/intent_helper/activity_icon_loader.h
index b182fac0745..2b3c705db54 100644
--- a/chromium/components/arc/intent_helper/activity_icon_loader.h
+++ b/chromium/components/arc/intent_helper/activity_icon_loader.h
@@ -110,7 +110,7 @@ class ActivityIconLoader {
// A map which holds icons in a scale-factor independent form (gfx::Image).
ActivityToIconsMap cached_icons_;
- base::ThreadChecker thread_checker_;
+ THREAD_CHECKER(thread_checker_);
// This must come last to make sure weak pointers are invalidated first.
base::WeakPtrFactory<ActivityIconLoader> weak_ptr_factory_;
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 b9e2f350cfd..5fa4f105980 100644
--- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -10,60 +10,96 @@
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/wallpaper/wallpaper_controller.h"
+#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.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/audio/arc_audio_bridge.h"
#include "components/arc/intent_helper/link_handler_model_impl.h"
-#include "components/arc/intent_helper/local_activity_resolver.h"
#include "ui/base/layout.h"
#include "url/gurl.h"
namespace arc {
+namespace {
+
+// Singleton factory for ArcIntentHelperBridge.
+class ArcIntentHelperBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcIntentHelperBridge,
+ ArcIntentHelperBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcIntentHelperBridgeFactory";
+
+ static ArcIntentHelperBridgeFactory* GetInstance() {
+ return base::Singleton<ArcIntentHelperBridgeFactory>::get();
+ }
-// static
-const char ArcIntentHelperBridge::kArcServiceName[] =
- "arc::ArcIntentHelperBridge";
+ private:
+ friend struct base::DefaultSingletonTraits<ArcIntentHelperBridgeFactory>;
+
+ ArcIntentHelperBridgeFactory() = default;
+ ~ArcIntentHelperBridgeFactory() override = default;
+};
+
+} // namespace
// static
const char ArcIntentHelperBridge::kArcIntentHelperPackageName[] =
"org.chromium.arc.intent_helper";
-ArcIntentHelperBridge::ArcIntentHelperBridge(
- ArcBridgeService* bridge_service,
- const scoped_refptr<LocalActivityResolver>& activity_resolver)
- : ArcService(bridge_service),
- binding_(this),
- activity_resolver_(activity_resolver) {
- DCHECK(thread_checker_.CalledOnValidThread());
- arc_bridge_service()->intent_helper()->AddObserver(this);
+// static
+ArcIntentHelperBridge* ArcIntentHelperBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcIntentHelperBridgeFactory::GetForBrowserContext(context);
+}
+
+// static
+KeyedServiceBaseFactory* ArcIntentHelperBridge::GetFactory() {
+ return ArcIntentHelperBridgeFactory::GetInstance();
+}
+
+ArcIntentHelperBridge::ArcIntentHelperBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : context_(context), arc_bridge_service_(bridge_service), binding_(this) {
+ arc_bridge_service_->intent_helper()->AddObserver(this);
}
ArcIntentHelperBridge::~ArcIntentHelperBridge() {
- DCHECK(thread_checker_.CalledOnValidThread());
- arc_bridge_service()->intent_helper()->RemoveObserver(this);
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->intent_helper()->RemoveObserver(this);
}
void ArcIntentHelperBridge::OnInstanceReady() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ash::Shell::Get()->set_link_handler_model_factory(this);
auto* instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->intent_helper(), Init);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->intent_helper(), Init);
DCHECK(instance);
- instance->Init(binding_.CreateInterfacePtrAndBind());
+ mojom::IntentHelperHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ instance->Init(std::move(host_proxy));
}
void ArcIntentHelperBridge::OnInstanceClosed() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ash::Shell::Get()->set_link_handler_model_factory(nullptr);
}
void ArcIntentHelperBridge::OnIconInvalidated(const std::string& package_name) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
icon_loader_.InvalidateIcons(package_name);
}
void ArcIntentHelperBridge::OnOpenDownloads() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// TODO(607411): If the FileManager is not yet open this will open to
// downloads by default, which is what we want. However if it is open it will
// simply be brought to the forgeground without forcibly being navigated to
@@ -72,28 +108,52 @@ void ArcIntentHelperBridge::OnOpenDownloads() {
}
void ArcIntentHelperBridge::OnOpenUrl(const std::string& url) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ash::Shell::Get()->shell_delegate()->OpenUrlFromArc(GURL(url));
}
void ArcIntentHelperBridge::OpenWallpaperPicker() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ash::Shell::Get()->wallpaper_controller()->OpenSetWallpaperPage();
}
void ArcIntentHelperBridge::SetWallpaperDeprecated(
const std::vector<uint8_t>& jpeg_data) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
LOG(ERROR) << "IntentHelper.SetWallpaper is deprecated";
}
+void ArcIntentHelperBridge::OpenVolumeControl() {
+ // TODO(hidehiko): Use browser_context passed to this class's ctor, after
+ // we migrate this into BrowserContextKeyedService.
+ auto* audio = ArcAudioBridge::GetForBrowserContext(
+ ArcServiceManager::Get()->browser_context());
+ DCHECK(audio);
+ audio->ShowVolumeControls();
+}
+
ArcIntentHelperBridge::GetResult ArcIntentHelperBridge::GetActivityIcons(
const std::vector<ActivityName>& activities,
const OnIconsReadyCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return icon_loader_.GetActivityIcons(activities, callback);
}
+bool ArcIntentHelperBridge::ShouldChromeHandleUrl(const GURL& url) {
+ if (!url.SchemeIsHTTPOrHTTPS()) {
+ // Chrome will handle everything that is not http and https.
+ return true;
+ }
+
+ for (const IntentFilter& filter : intent_filters_) {
+ if (filter.Match(url))
+ return false;
+ }
+
+ // Didn't find any matches for Android so let Chrome handle it.
+ return true;
+}
+
void ArcIntentHelperBridge::AddObserver(ArcIntentHelperObserver* observer) {
observer_list_.AddObserver(observer);
}
@@ -102,10 +162,15 @@ void ArcIntentHelperBridge::RemoveObserver(ArcIntentHelperObserver* observer) {
observer_list_.RemoveObserver(observer);
}
+bool ArcIntentHelperBridge::HasObserver(
+ ArcIntentHelperObserver* observer) const {
+ return observer_list_.HasObserver(observer);
+}
+
std::unique_ptr<ash::LinkHandlerModel> ArcIntentHelperBridge::CreateModel(
const GURL& url) {
- DCHECK(thread_checker_.CalledOnValidThread());
- auto impl = base::MakeUnique<LinkHandlerModelImpl>();
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ auto impl = base::MakeUnique<LinkHandlerModelImpl>(context_);
if (!impl->Init(url))
return nullptr;
return std::move(impl);
@@ -132,8 +197,8 @@ ArcIntentHelperBridge::FilterOutIntentHelper(
void ArcIntentHelperBridge::OnIntentFiltersUpdated(
std::vector<IntentFilter> filters) {
- DCHECK(thread_checker_.CalledOnValidThread());
- activity_resolver_->UpdateIntentFilters(std::move(filters));
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ intent_filters_ = std::move(filters);
for (auto& observer : observer_list_)
observer.OnIntentFiltersUpdated();
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 06e58aaec0a..ac5dd705426 100644
--- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -11,42 +11,52 @@
#include "ash/link_handler_model_factory.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "base/threading/thread_checker.h"
-#include "components/arc/arc_service.h"
#include "components/arc/common/intent_helper.mojom.h"
#include "components/arc/instance_holder.h"
#include "components/arc/intent_helper/activity_icon_loader.h"
#include "components/arc/intent_helper/arc_intent_helper_observer.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
-namespace ash {
+class KeyedServiceBaseFactory;
+namespace ash {
class LinkHandlerModel;
-
} // namespace ash
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
class ArcBridgeService;
class IntentFilter;
-class LocalActivityResolver;
// Receives intents from ARC.
class ArcIntentHelperBridge
- : public ArcService,
+ : public KeyedService,
public InstanceHolder<mojom::IntentHelperInstance>::Observer,
public mojom::IntentHelperHost,
public ash::LinkHandlerModelFactory {
public:
- ArcIntentHelperBridge(
- ArcBridgeService* bridge_service,
- const scoped_refptr<LocalActivityResolver>& activity_resolver);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcIntentHelperBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ // Returns factory for the ArcIntentHelperBridge.
+ static KeyedServiceBaseFactory* GetFactory();
+
+ ArcIntentHelperBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
~ArcIntentHelperBridge() override;
void AddObserver(ArcIntentHelperObserver* observer);
void RemoveObserver(ArcIntentHelperObserver* observer);
+ bool HasObserver(ArcIntentHelperObserver* observer) const;
// InstanceHolder<mojom::IntentHelperInstance>::Observer
void OnInstanceReady() override;
@@ -60,6 +70,7 @@ class ArcIntentHelperBridge
void OnOpenUrl(const std::string& url) override;
void OpenWallpaperPicker() override;
void SetWallpaperDeprecated(const std::vector<uint8_t>& jpeg_data) override;
+ void OpenVolumeControl() override;
// Retrieves icons for the |activities| and calls |callback|.
// See ActivityIconLoader::GetActivityIcons() for more details.
@@ -72,6 +83,14 @@ class ArcIntentHelperBridge
GetResult GetActivityIcons(const std::vector<ActivityName>& activities,
const OnIconsReadyCallback& callback);
+ // Returns true when |url| can only be handled by Chrome. Otherwise, which is
+ // when there might be one or more ARC apps that can handle |url|, returns
+ // false. This function synchronously checks the |url| without making any IPC
+ // to ARC side. Note that this function only supports http and https. If url's
+ // scheme is neither http nor https, the function immediately returns true
+ // without checking the filters.
+ bool ShouldChromeHandleUrl(const GURL& url);
+
// ash::LinkHandlerModelFactory
std::unique_ptr<ash::LinkHandlerModel> CreateModel(const GURL& url) override;
@@ -83,17 +102,20 @@ class ArcIntentHelperBridge
static std::vector<mojom::IntentHandlerInfoPtr> FilterOutIntentHelper(
std::vector<mojom::IntentHandlerInfoPtr> handlers);
- // For supporting ArcServiceManager::GetService<T>().
- static const char kArcServiceName[];
-
static const char kArcIntentHelperPackageName[];
private:
+ THREAD_CHECKER(thread_checker_);
+
+ content::BrowserContext* const context_;
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
mojo::Binding<mojom::IntentHelperHost> binding_;
internal::ActivityIconLoader icon_loader_;
- scoped_refptr<LocalActivityResolver> activity_resolver_;
- base::ThreadChecker thread_checker_;
+ // List of intent filters from Android. Used to determine if Chrome should
+ // handle a URL without handing off to Android.
+ std::vector<IntentFilter> intent_filters_;
base::ObserverList<ArcIntentHelperObserver> observer_list_;
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 fd7042291d7..5fdf49ace14 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
@@ -9,33 +9,36 @@
#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/local_activity_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace arc {
namespace {
+IntentFilter GetIntentFilter(const std::string& host) {
+ std::vector<IntentFilter::AuthorityEntry> authorities;
+ authorities.emplace_back(host, -1);
+ return IntentFilter(std::move(authorities),
+ std::vector<IntentFilter::PatternMatcher>());
+}
+
class ArcIntentHelperTest : public testing::Test {
public:
ArcIntentHelperTest() = default;
protected:
std::unique_ptr<ArcBridgeService> arc_bridge_service_;
- scoped_refptr<LocalActivityResolver> activity_resolver_;
std::unique_ptr<ArcIntentHelperBridge> instance_;
private:
void SetUp() override {
arc_bridge_service_ = base::MakeUnique<ArcBridgeService>();
- activity_resolver_ = new LocalActivityResolver();
instance_ = base::MakeUnique<ArcIntentHelperBridge>(
- arc_bridge_service_.get(), activity_resolver_);
+ nullptr /* context */, arc_bridge_service_.get());
}
void TearDown() override {
instance_.reset();
- activity_resolver_ = nullptr;
arc_bridge_service_.reset();
}
@@ -161,4 +164,92 @@ TEST_F(ArcIntentHelperTest, TestObserver) {
EXPECT_FALSE(observer->IsUpdated());
}
+// Tests that ShouldChromeHandleUrl returns true by default.
+TEST_F(ArcIntentHelperTest, TestDefault) {
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("file:///etc/password")));
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("chrome://help")));
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("about://chrome")));
+}
+
+// Tests that ShouldChromeHandleUrl returns false when there's a match.
+TEST_F(ArcIntentHelperTest, TestSingleFilter) {
+ std::vector<IntentFilter> array;
+ array.emplace_back(GetIntentFilter("www.google.com"));
+ instance_->OnIntentFiltersUpdated(std::move(array));
+
+ EXPECT_FALSE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
+
+ EXPECT_TRUE(
+ instance_->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
+}
+
+// Tests the same with multiple filters.
+TEST_F(ArcIntentHelperTest, TestMultipleFilters) {
+ std::vector<IntentFilter> array;
+ array.emplace_back(GetIntentFilter("www.google.com"));
+ array.emplace_back(GetIntentFilter("www.google.co.uk"));
+ array.emplace_back(GetIntentFilter("dev.chromium.org"));
+ instance_->OnIntentFiltersUpdated(std::move(array));
+
+ EXPECT_FALSE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("http://www.google.co.uk")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("http://dev.chromium.org")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("https://dev.chromium.org")));
+
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.android.com")));
+}
+
+// Tests that ShouldChromeHandleUrl returns true for non http(s) URLs.
+TEST_F(ArcIntentHelperTest, TestNonHttp) {
+ std::vector<IntentFilter> array;
+ array.emplace_back(GetIntentFilter("www.google.com"));
+ instance_->OnIntentFiltersUpdated(std::move(array));
+
+ EXPECT_TRUE(
+ instance_->ShouldChromeHandleUrl(GURL("chrome://www.google.com")));
+ EXPECT_TRUE(
+ instance_->ShouldChromeHandleUrl(GURL("custom://www.google.com")));
+}
+
+// Tests that ShouldChromeHandleUrl discards the previous filters when
+// UpdateIntentFilters is called with new ones.
+TEST_F(ArcIntentHelperTest, TestMultipleUpdate) {
+ std::vector<IntentFilter> array;
+ array.emplace_back(GetIntentFilter("www.google.com"));
+ array.emplace_back(GetIntentFilter("dev.chromium.org"));
+ instance_->OnIntentFiltersUpdated(std::move(array));
+
+ std::vector<IntentFilter> array2;
+ array2.emplace_back(GetIntentFilter("www.google.co.uk"));
+ array2.emplace_back(GetIntentFilter("dev.chromium.org"));
+ array2.emplace_back(GetIntentFilter("www.android.com"));
+ instance_->OnIntentFiltersUpdated(std::move(array2));
+
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
+ EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("http://www.google.co.uk")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("http://dev.chromium.org")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("https://dev.chromium.org")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("http://www.android.com")));
+ EXPECT_FALSE(
+ instance_->ShouldChromeHandleUrl(GURL("https://www.android.com")));
+}
+
} // namespace arc
diff --git a/chromium/components/arc/intent_helper/link_handler_model_impl.cc b/chromium/components/arc/intent_helper/link_handler_model_impl.cc
index b4c7c7d6bcb..ef863592e92 100644
--- a/chromium/components/arc/intent_helper/link_handler_model_impl.cc
+++ b/chromium/components/arc/intent_helper/link_handler_model_impl.cc
@@ -47,7 +47,8 @@ bool GetQueryValue(const GURL& url,
} // namespace
-LinkHandlerModelImpl::LinkHandlerModelImpl() : weak_ptr_factory_(this) {}
+LinkHandlerModelImpl::LinkHandlerModelImpl(content::BrowserContext* context)
+ : context_(context), weak_ptr_factory_(this) {}
LinkHandlerModelImpl::~LinkHandlerModelImpl() = default;
@@ -97,7 +98,7 @@ void LinkHandlerModelImpl::OnUrlHandlerList(
bool icon_info_notified = false;
auto* intent_helper_bridge =
- ArcServiceManager::GetGlobalService<ArcIntentHelperBridge>();
+ ArcIntentHelperBridge::GetForBrowserContext(context_);
if (intent_helper_bridge) {
std::vector<ArcIntentHelperBridge::ActivityName> activities;
for (size_t i = 0; i < handlers_.size(); ++i) {
diff --git a/chromium/components/arc/intent_helper/link_handler_model_impl.h b/chromium/components/arc/intent_helper/link_handler_model_impl.h
index c6613b4df6b..2c080f75a39 100644
--- a/chromium/components/arc/intent_helper/link_handler_model_impl.h
+++ b/chromium/components/arc/intent_helper/link_handler_model_impl.h
@@ -15,11 +15,15 @@
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "url/gurl.h"
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
class LinkHandlerModelImpl : public ash::LinkHandlerModel {
public:
- LinkHandlerModelImpl();
+ explicit LinkHandlerModelImpl(content::BrowserContext* context);
~LinkHandlerModelImpl() override;
// ash::LinkHandlerModel overrides:
@@ -45,6 +49,8 @@ class LinkHandlerModelImpl : public ash::LinkHandlerModel {
// Otherwise, returns the original |url| as-us.
static GURL RewriteUrlFromQueryIfAvailable(const GURL& url);
+ content::BrowserContext* const context_;
+
base::ObserverList<Observer> observer_list_;
// Url handler info passed from ARC.
diff --git a/chromium/components/arc/intent_helper/local_activity_resolver.cc b/chromium/components/arc/intent_helper/local_activity_resolver.cc
deleted file mode 100644
index d667bff1c51..00000000000
--- a/chromium/components/arc/intent_helper/local_activity_resolver.cc
+++ /dev/null
@@ -1,37 +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/local_activity_resolver.h"
-
-#include <utility>
-
-#include "url/gurl.h"
-
-namespace arc {
-
-LocalActivityResolver::LocalActivityResolver() = default;
-
-LocalActivityResolver::~LocalActivityResolver() = default;
-
-bool LocalActivityResolver::ShouldChromeHandleUrl(const GURL& url) {
- if (!url.SchemeIsHTTPOrHTTPS()) {
- // Chrome will handle everything that is not http and https.
- return true;
- }
-
- for (const IntentFilter& filter : intent_filters_) {
- if (filter.Match(url))
- return false;
- }
-
- // Didn't find any matches for Android so let Chrome handle it.
- return true;
-}
-
-void LocalActivityResolver::UpdateIntentFilters(
- std::vector<IntentFilter> intent_filters) {
- intent_filters_ = std::move(intent_filters);
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/intent_helper/local_activity_resolver.h b/chromium/components/arc/intent_helper/local_activity_resolver.h
deleted file mode 100644
index 353d31c9ed2..00000000000
--- a/chromium/components/arc/intent_helper/local_activity_resolver.h
+++ /dev/null
@@ -1,47 +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_LOCAL_ACTIVITY_RESOLVER_H_
-#define COMPONENTS_ARC_INTENT_HELPER_LOCAL_ACTIVITY_RESOLVER_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/arc/common/intent_helper.mojom.h"
-#include "components/arc/intent_helper/intent_filter.h"
-
-class GURL;
-
-namespace arc {
-
-class LocalActivityResolver : public base::RefCounted<LocalActivityResolver> {
- public:
- LocalActivityResolver();
-
- // Returns true when |url| can only be handled by Chrome. Otherwise, which is
- // when there might be one or more ARC apps that can handle |url|, returns
- // false. This function synchronously checks the |url| without making any IPC
- // to ARC side. Note that this function only supports http and https. If url's
- // scheme is neither http nor https, the function immediately returns true
- // without checking the filters.
- bool ShouldChromeHandleUrl(const GURL& url);
-
- // Called when the list of intent filters on ARC side is updated.
- void UpdateIntentFilters(std::vector<IntentFilter> intent_filters);
-
- private:
- friend class base::RefCounted<LocalActivityResolver>;
- ~LocalActivityResolver();
-
- // List of intent filters from Android. Used to determine if Chrome should
- // handle a URL without handing off to Android.
- std::vector<IntentFilter> intent_filters_;
-
- DISALLOW_COPY_AND_ASSIGN(LocalActivityResolver);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_INTENT_HELPER_LOCAL_ACTIVITY_RESOLVER_H_
diff --git a/chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc b/chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc
deleted file mode 100644
index 09bf44588a9..00000000000
--- a/chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc
+++ /dev/null
@@ -1,120 +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/local_activity_resolver.h"
-
-#include <string>
-#include <utility>
-
-#include "components/arc/intent_helper/intent_filter.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace arc {
-
-namespace {
-
-IntentFilter GetIntentFilter(const std::string& host) {
- std::vector<IntentFilter::AuthorityEntry> authorities;
- authorities.emplace_back(host, -1);
-
- return IntentFilter(std::move(authorities),
- std::vector<IntentFilter::PatternMatcher>());
-}
-
-} // namespace
-
-// Tests that ShouldChromeHandleUrl returns true by default.
-TEST(LocalActivityResolverTest, TestDefault) {
- scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("http://www.google.com")));
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("https://www.google.com")));
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("file:///etc/password")));
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("chrome://help")));
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("about://chrome")));
-}
-
-// Tests that ShouldChromeHandleUrl returns false when there's a match.
-TEST(LocalActivityResolverTest, TestSingleFilter) {
- scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
-
- std::vector<IntentFilter> array;
- array.emplace_back(GetIntentFilter("www.google.com"));
- resolver->UpdateIntentFilters(std::move(array));
-
- EXPECT_FALSE(resolver->ShouldChromeHandleUrl(GURL("http://www.google.com")));
- EXPECT_FALSE(resolver->ShouldChromeHandleUrl(GURL("https://www.google.com")));
-
- EXPECT_TRUE(
- resolver->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
-}
-
-// Tests the same with multiple filters.
-TEST(LocalActivityResolverTest, TestMultipleFilters) {
- scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
-
- std::vector<IntentFilter> array;
- array.emplace_back(GetIntentFilter("www.google.com"));
- array.emplace_back(GetIntentFilter("www.google.co.uk"));
- array.emplace_back(GetIntentFilter("dev.chromium.org"));
- resolver->UpdateIntentFilters(std::move(array));
-
- EXPECT_FALSE(resolver->ShouldChromeHandleUrl(GURL("http://www.google.com")));
- EXPECT_FALSE(resolver->ShouldChromeHandleUrl(GURL("https://www.google.com")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("http://www.google.co.uk")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("http://dev.chromium.org")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("https://dev.chromium.org")));
-
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("http://www.android.com")));
-}
-
-// Tests that ShouldChromeHandleUrl returns true for non http(s) URLs.
-TEST(LocalActivityResolverTest, TestNonHttp) {
- scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
-
- std::vector<IntentFilter> array;
- array.emplace_back(GetIntentFilter("www.google.com"));
- resolver->UpdateIntentFilters(std::move(array));
-
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("chrome://www.google.com")));
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("custom://www.google.com")));
-}
-
-// Tests that ShouldChromeHandleUrl discards the previous filters when
-// UpdateIntentFilters is called with new ones.
-TEST(LocalActivityResolverTest, TestMultipleUpdate) {
- scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
-
- std::vector<IntentFilter> array;
- array.emplace_back(GetIntentFilter("www.google.com"));
- array.emplace_back(GetIntentFilter("dev.chromium.org"));
- resolver->UpdateIntentFilters(std::move(array));
-
- std::vector<IntentFilter> array2;
- array2.emplace_back(GetIntentFilter("www.google.co.uk"));
- array2.emplace_back(GetIntentFilter("dev.chromium.org"));
- array2.emplace_back(GetIntentFilter("www.android.com"));
- resolver->UpdateIntentFilters(std::move(array2));
-
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("http://www.google.com")));
- EXPECT_TRUE(resolver->ShouldChromeHandleUrl(GURL("https://www.google.com")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("http://www.google.co.uk")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("http://dev.chromium.org")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("https://dev.chromium.org")));
- EXPECT_FALSE(resolver->ShouldChromeHandleUrl(GURL("http://www.android.com")));
- EXPECT_FALSE(
- resolver->ShouldChromeHandleUrl(GURL("https://www.android.com")));
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/kiosk/arc_kiosk_bridge.cc b/chromium/components/arc/kiosk/arc_kiosk_bridge.cc
deleted file mode 100644
index 7837d98aec9..00000000000
--- a/chromium/components/arc/kiosk/arc_kiosk_bridge.cc
+++ /dev/null
@@ -1,44 +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/kiosk/arc_kiosk_bridge.h"
-
-#include "components/arc/arc_bridge_service.h"
-
-namespace arc {
-
-ArcKioskBridge::ArcKioskBridge(ArcBridgeService* bridge_service,
- Delegate* delegate)
- : ArcService(bridge_service), binding_(this), delegate_(delegate) {
- DCHECK(delegate_);
- arc_bridge_service()->kiosk()->AddObserver(this);
-}
-
-ArcKioskBridge::~ArcKioskBridge() {
- arc_bridge_service()->kiosk()->RemoveObserver(this);
-}
-
-void ArcKioskBridge::OnInstanceReady() {
- mojom::KioskInstance* kiosk_instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->kiosk(), Init);
- DCHECK(kiosk_instance);
- kiosk_instance->Init(binding_.CreateInterfacePtrAndBind());
-}
-
-void ArcKioskBridge::OnMaintenanceSessionCreated(int32_t session_id) {
- session_id_ = session_id;
- delegate_->OnMaintenanceSessionCreated();
- // TODO(poromov@) Show appropriate splash screen.
-}
-
-void ArcKioskBridge::OnMaintenanceSessionFinished(int32_t session_id,
- bool success) {
- // Filter only callbacks for the started kiosk session.
- if (session_id != session_id_)
- return;
- session_id_ = -1;
- delegate_->OnMaintenanceSessionFinished();
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/kiosk/arc_kiosk_bridge.h b/chromium/components/arc/kiosk/arc_kiosk_bridge.h
deleted file mode 100644
index 2c46e051910..00000000000
--- a/chromium/components/arc/kiosk/arc_kiosk_bridge.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_ARC_KIOSK_ARC_KIOSK_BRIDGE_H_
-#define COMPONENTS_ARC_KIOSK_ARC_KIOSK_BRIDGE_H_
-
-#include <map>
-
-#include "base/macros.h"
-#include "components/arc/arc_service.h"
-#include "components/arc/common/kiosk.mojom.h"
-#include "components/arc/instance_holder.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace arc {
-
-class ArcBridgeService;
-
-class ArcKioskBridge : public ArcService,
- public InstanceHolder<mojom::KioskInstance>::Observer,
- public mojom::KioskHost {
- public:
- // Received IPCs are passed to this delegate.
- class Delegate {
- public:
- virtual ~Delegate() = default;
- virtual void OnMaintenanceSessionCreated() = 0;
- virtual void OnMaintenanceSessionFinished() = 0;
- };
-
- // |delegate| should be alive while the ArcKioskBridge instance is alive.
- ArcKioskBridge(ArcBridgeService* bridge_service, Delegate* delegate);
- ~ArcKioskBridge() override;
-
- // InstanceHolder<mojom::KioskInstance>::Observer overrides.
- void OnInstanceReady() override;
-
- // mojom::KioskHost overrides.
- void OnMaintenanceSessionCreated(int32_t session_id) override;
- void OnMaintenanceSessionFinished(int32_t session_id, bool success) override;
-
- private:
- mojo::Binding<mojom::KioskHost> binding_;
- Delegate* const delegate_;
-
- // Tracks current maintenance session id.
- int32_t session_id_ = -1;
-
- DISALLOW_COPY_AND_ASSIGN(ArcKioskBridge);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_KIOSK_ARC_KIOSK_BRIDGE_H_
diff --git a/chromium/components/arc/kiosk/arc_kiosk_bridge_unittest.cc b/chromium/components/arc/kiosk/arc_kiosk_bridge_unittest.cc
deleted file mode 100644
index b2243f7ca37..00000000000
--- a/chromium/components/arc/kiosk/arc_kiosk_bridge_unittest.cc
+++ /dev/null
@@ -1,59 +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 <memory>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "components/arc/arc_bridge_service.h"
-#include "components/arc/kiosk/arc_kiosk_bridge.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-class MockArcKioskBridgeDelegate : public arc::ArcKioskBridge::Delegate {
- public:
- MockArcKioskBridgeDelegate() = default;
-
- MOCK_METHOD0(OnMaintenanceSessionCreated, void());
- MOCK_METHOD0(OnMaintenanceSessionFinished, void());
-};
-
-} // namespace
-
-namespace arc {
-
-class ArcKioskBridgeTest : public testing::Test {
- public:
- ArcKioskBridgeTest()
- : bridge_service_(base::MakeUnique<ArcBridgeService>()),
- delegate_(base::MakeUnique<MockArcKioskBridgeDelegate>()),
- kiosk_bridge_(base::MakeUnique<ArcKioskBridge>(bridge_service_.get(),
- delegate_.get())) {}
-
- protected:
- std::unique_ptr<ArcBridgeService> bridge_service_;
- std::unique_ptr<MockArcKioskBridgeDelegate> delegate_;
- std::unique_ptr<ArcKioskBridge> kiosk_bridge_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ArcKioskBridgeTest);
-};
-
-TEST_F(ArcKioskBridgeTest, MaintenanceSessionFinished) {
- EXPECT_CALL(*delegate_, OnMaintenanceSessionCreated()).Times(1);
- kiosk_bridge_->OnMaintenanceSessionCreated(1);
- EXPECT_CALL(*delegate_, OnMaintenanceSessionFinished()).Times(1);
- kiosk_bridge_->OnMaintenanceSessionFinished(1, true);
-}
-
-TEST_F(ArcKioskBridgeTest, MaintenanceSessionNotFinished) {
- EXPECT_CALL(*delegate_, OnMaintenanceSessionCreated()).Times(1);
- kiosk_bridge_->OnMaintenanceSessionCreated(1);
- EXPECT_CALL(*delegate_, OnMaintenanceSessionFinished()).Times(0);
- kiosk_bridge_->OnMaintenanceSessionFinished(2, true);
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/lock_screen/arc_lock_screen_bridge.cc b/chromium/components/arc/lock_screen/arc_lock_screen_bridge.cc
new file mode 100644
index 00000000000..1be29eb72ed
--- /dev/null
+++ b/chromium/components/arc/lock_screen/arc_lock_screen_bridge.cc
@@ -0,0 +1,77 @@
+// 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/arc/lock_screen/arc_lock_screen_bridge.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/threading/thread_task_runner_handle.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/session_manager/core/session_manager.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace arc {
+
+namespace {
+
+// Singleton factory for ArcLockScreenBridge.
+class ArcLockScreenBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcLockScreenBridge,
+ ArcLockScreenBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcLockScreenBridgeFactory";
+
+ static ArcLockScreenBridgeFactory* GetInstance() {
+ return base::Singleton<ArcLockScreenBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcLockScreenBridgeFactory>;
+ ArcLockScreenBridgeFactory() = default;
+ ~ArcLockScreenBridgeFactory() override = default;
+};
+
+} // namespace
+
+// static
+ArcLockScreenBridge* ArcLockScreenBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcLockScreenBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcLockScreenBridge::ArcLockScreenBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service) {
+ arc_bridge_service_->lock_screen()->AddObserver(this);
+ session_manager::SessionManager::Get()->AddObserver(this);
+}
+
+ArcLockScreenBridge::~ArcLockScreenBridge() {
+ arc_bridge_service_->lock_screen()->RemoveObserver(this);
+ session_manager::SessionManager::Get()->RemoveObserver(this);
+}
+
+void ArcLockScreenBridge::OnInstanceReady() {
+ SendDeviceLockedState();
+}
+
+void ArcLockScreenBridge::OnSessionStateChanged() {
+ SendDeviceLockedState();
+}
+
+void ArcLockScreenBridge::SendDeviceLockedState() {
+ mojom::LockScreenInstance* lock_screen_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service_->lock_screen(), SetDeviceLocked);
+ if (!lock_screen_instance)
+ return;
+ lock_screen_instance->SetDeviceLocked(
+ session_manager::SessionManager::Get()->IsUserSessionBlocked());
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/lock_screen/arc_lock_screen_bridge.h b/chromium/components/arc/lock_screen/arc_lock_screen_bridge.h
new file mode 100644
index 00000000000..7c1aabe2de2
--- /dev/null
+++ b/chromium/components/arc/lock_screen/arc_lock_screen_bridge.h
@@ -0,0 +1,57 @@
+// 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_ARC_LOCK_SCREEN_ARC_LOCK_SCREEN_BRIDGE_H_
+#define COMPONENTS_ARC_LOCK_SCREEN_ARC_LOCK_SCREEN_BRIDGE_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "components/arc/common/lock_screen.mojom.h"
+#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/session_manager/core/session_manager_observer.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class notifies the Chrome OS side lock screen state to the container.
+class ArcLockScreenBridge
+ : public KeyedService,
+ public InstanceHolder<mojom::LockScreenInstance>::Observer,
+ public session_manager::SessionManagerObserver {
+ public:
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcLockScreenBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcLockScreenBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
+ ~ArcLockScreenBridge() override;
+
+ // InstanceHolder<mojom::LockScreenInstance>::Observer overrides:
+ void OnInstanceReady() override;
+
+ // session_manager::SessionManagerObserver overrides.
+ void OnSessionStateChanged() override;
+
+ private:
+ // Sends the device locked state to container.
+ void SendDeviceLockedState();
+
+ THREAD_CHECKER(thread_checker_);
+
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
+ DISALLOW_COPY_AND_ASSIGN(ArcLockScreenBridge);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_LOCK_SCREEN_ARC_LOCK_SCREEN_BRIDGE_H_
diff --git a/chromium/components/arc/metrics/arc_metrics_service.cc b/chromium/components/arc/metrics/arc_metrics_service.cc
index 52867ee63f3..ee6d25cc317 100644
--- a/chromium/components/arc/metrics/arc_metrics_service.cc
+++ b/chromium/components/arc/metrics/arc_metrics_service.cc
@@ -8,18 +8,21 @@
#include <utility>
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
namespace arc {
namespace {
constexpr base::TimeDelta kUmaMinTime = base::TimeDelta::FromMilliseconds(1);
+constexpr base::TimeDelta kUmaMaxTime = base::TimeDelta::FromSeconds(60);
constexpr int kUmaNumBuckets = 50;
constexpr base::TimeDelta kRequestProcessListPeriod =
@@ -31,7 +34,7 @@ constexpr char kBootProgressEnableScreen[] = "boot_progress_enable_screen";
std::string BootTypeToString(mojom::BootType boot_type) {
switch (boot_type) {
case mojom::BootType::UNKNOWN:
- return ""; // for backward compatibility.
+ break;
case mojom::BootType::FIRST_BOOT:
return ".FirstBoot";
case mojom::BootType::FIRST_BOOT_AFTER_UPDATE:
@@ -43,26 +46,54 @@ std::string BootTypeToString(mojom::BootType boot_type) {
return "";
}
+// Singleton factory for ArcMetricsService.
+class ArcMetricsServiceFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcMetricsService,
+ ArcMetricsServiceFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcMetricsServiceFactory";
+
+ static ArcMetricsServiceFactory* GetInstance() {
+ return base::Singleton<ArcMetricsServiceFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcMetricsServiceFactory>;
+ ArcMetricsServiceFactory() = default;
+ ~ArcMetricsServiceFactory() override = default;
+};
+
} // namespace
-ArcMetricsService::ArcMetricsService(ArcBridgeService* bridge_service)
- : ArcService(bridge_service),
+// static
+ArcMetricsService* ArcMetricsService::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcMetricsServiceFactory::GetForBrowserContext(context);
+}
+
+ArcMetricsService::ArcMetricsService(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service),
binding_(this),
process_observer_(this),
weak_ptr_factory_(this) {
- arc_bridge_service()->metrics()->AddObserver(this);
- arc_bridge_service()->process()->AddObserver(&process_observer_);
+ arc_bridge_service_->metrics()->AddObserver(this);
+ arc_bridge_service_->process()->AddObserver(&process_observer_);
}
ArcMetricsService::~ArcMetricsService() {
- DCHECK(CalledOnValidThread());
- arc_bridge_service()->process()->RemoveObserver(&process_observer_);
- arc_bridge_service()->metrics()->RemoveObserver(this);
-}
-
-bool ArcMetricsService::CalledOnValidThread() {
- // Make sure access to the Chrome clipboard is happening in the UI thread.
- return thread_checker_.CalledOnValidThread();
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get()) {
+ arc_bridge_service_->process()->RemoveObserver(&process_observer_);
+ arc_bridge_service_->metrics()->RemoveObserver(this);
+ }
}
void ArcMetricsService::OnInstanceReady() {
@@ -76,8 +107,8 @@ void ArcMetricsService::OnInstanceReady() {
}
void ArcMetricsService::OnInstanceClosed() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
VLOG(2) << "Close metrics service.";
- DCHECK(CalledOnValidThread());
if (binding_.is_bound())
binding_.Unbind();
}
@@ -95,7 +126,7 @@ void ArcMetricsService::OnProcessInstanceClosed() {
void ArcMetricsService::RequestProcessList() {
mojom::ProcessInstance* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->process(), RequestProcessList);
+ arc_bridge_service_->process(), RequestProcessList);
if (!process_instance)
return;
VLOG(2) << "RequestProcessList";
@@ -132,13 +163,13 @@ void ArcMetricsService::ParseProcessList(
void ArcMetricsService::OnArcStartTimeRetrieved(
bool success,
base::TimeTicks arc_start_time) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!success) {
LOG(ERROR) << "Failed to retrieve ARC start timeticks.";
return;
}
auto* instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->metrics(), Init);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->metrics(), Init);
if (!instance)
return;
@@ -157,26 +188,25 @@ void ArcMetricsService::OnArcStartTimeRetrieved(
void ArcMetricsService::ReportBootProgress(
std::vector<mojom::BootProgressEventPtr> events,
mojom::BootType boot_type) {
- DCHECK(CalledOnValidThread());
- // TODO(yusukes): Return immediately with with LOG(ERROR) when |boot_type| is
- // UNKNOWN. Once the container is updated, we'll never see the boot type.
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (boot_type == mojom::BootType::UNKNOWN) {
+ LOG(WARNING) << "boot_type is unknown. Skip recording UMA.";
+ return;
+ }
int64_t arc_start_time_in_ms =
(arc_start_time_ - base::TimeTicks()).InMilliseconds();
const std::string suffix = BootTypeToString(boot_type);
- // TODO(yusukes): Define kUmaMaxTime and always use 60s.
- const base::TimeDelta max_time = base::TimeDelta::FromSeconds(
- boot_type == mojom::BootType::UNKNOWN ? 30 : 60);
for (const auto& event : events) {
VLOG(2) << "Report boot progress event:" << event->event << "@"
<< event->uptimeMillis;
const std::string name = "Arc." + event->event + suffix;
const base::TimeDelta elapsed_time = base::TimeDelta::FromMilliseconds(
event->uptimeMillis - arc_start_time_in_ms);
- base::UmaHistogramCustomTimes(name, elapsed_time, kUmaMinTime, max_time,
+ base::UmaHistogramCustomTimes(name, elapsed_time, kUmaMinTime, kUmaMaxTime,
kUmaNumBuckets);
if (event->event.compare(kBootProgressEnableScreen) == 0) {
base::UmaHistogramCustomTimes("Arc.AndroidBootTime" + suffix,
- elapsed_time, kUmaMinTime, max_time,
+ elapsed_time, kUmaMinTime, kUmaMaxTime,
kUmaNumBuckets);
}
}
diff --git a/chromium/components/arc/metrics/arc_metrics_service.h b/chromium/components/arc/metrics/arc_metrics_service.h
index 5395dddef87..53a22bda01c 100644
--- a/chromium/components/arc/metrics/arc_metrics_service.h
+++ b/chromium/components/arc/metrics/arc_metrics_service.h
@@ -11,23 +11,33 @@
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
-#include "components/arc/arc_service.h"
#include "components/arc/common/metrics.mojom.h"
#include "components/arc/common/process.mojom.h"
#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
class ArcBridgeService;
// Collects information from other ArcServices and send UMA metrics.
class ArcMetricsService
- : public ArcService,
+ : public KeyedService,
public InstanceHolder<mojom::MetricsInstance>::Observer,
public mojom::MetricsHost {
public:
- explicit ArcMetricsService(ArcBridgeService* bridge_service);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcMetricsService* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcMetricsService(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
~ArcMetricsService() override;
// InstanceHolder<mojom::MetricsInstance>::Observer overrides.
@@ -43,14 +53,6 @@ class ArcMetricsService
mojom::BootType boot_type) override;
private:
- bool CalledOnValidThread();
- void RequestProcessList();
- void ParseProcessList(std::vector<mojom::RunningAppProcessInfoPtr> processes);
-
- // DBus callbacks.
- void OnArcStartTimeRetrieved(bool success, base::TimeTicks arc_start_time);
-
- private:
// Adapter to be able to also observe ProcessInstance events.
class ProcessObserver
: public InstanceHolder<mojom::ProcessInstance>::Observer {
@@ -68,10 +70,19 @@ class ArcMetricsService
DISALLOW_COPY_AND_ASSIGN(ProcessObserver);
};
+ void RequestProcessList();
+ void ParseProcessList(std::vector<mojom::RunningAppProcessInfoPtr> processes);
+
+ // DBus callbacks.
+ void OnArcStartTimeRetrieved(bool success, base::TimeTicks arc_start_time);
+
+ THREAD_CHECKER(thread_checker_);
+
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
mojo::Binding<mojom::MetricsHost> binding_;
ProcessObserver process_observer_;
- base::ThreadChecker thread_checker_;
base::RepeatingTimer timer_;
base::TimeTicks arc_start_time_;
diff --git a/chromium/components/arc/net/arc_net_host_impl.cc b/chromium/components/arc/net/arc_net_host_impl.cc
index 2531cdf881f..705cb90673f 100644
--- a/chromium/components/arc/net/arc_net_host_impl.cc
+++ b/chromium/components/arc/net/arc_net_host_impl.cc
@@ -11,6 +11,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
@@ -24,6 +25,7 @@
#include "chromeos/network/network_util.h"
#include "chromeos/network/onc/onc_utils.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/user_manager/user_manager.h"
namespace {
@@ -288,27 +290,61 @@ void DefaultNetworkFailureCallback(
} // namespace
namespace arc {
+namespace {
+
+// Singleton factory for ArcNetHostImpl.
+class ArcNetHostImplFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcNetHostImpl,
+ ArcNetHostImplFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcNetHostImplFactory";
+
+ static ArcNetHostImplFactory* GetInstance() {
+ return base::Singleton<ArcNetHostImplFactory>::get();
+ }
-ArcNetHostImpl::ArcNetHostImpl(ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this), weak_factory_(this) {
- arc_bridge_service()->net()->AddObserver(this);
+ private:
+ friend base::DefaultSingletonTraits<ArcNetHostImplFactory>;
+ ArcNetHostImplFactory() = default;
+ ~ArcNetHostImplFactory() override = default;
+};
+
+} // namespace
+
+// static
+ArcNetHostImpl* ArcNetHostImpl::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcNetHostImplFactory::GetForBrowserContext(context);
+}
+
+ArcNetHostImpl::ArcNetHostImpl(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service), binding_(this), weak_factory_(this) {
+ arc_bridge_service_->net()->AddObserver(this);
}
ArcNetHostImpl::~ArcNetHostImpl() {
- DCHECK(thread_checker_.CalledOnValidThread());
- arc_bridge_service()->net()->RemoveObserver(this);
- if (observing_network_state_) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (observing_network_state_)
GetStateHandler()->RemoveObserver(this, FROM_HERE);
- }
+
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->net()->RemoveObserver(this);
}
void ArcNetHostImpl::OnInstanceReady() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
mojom::NetHostPtr host;
binding_.Bind(MakeRequest(&host));
auto* instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->net(), Init);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->net(), Init);
DCHECK(instance);
instance->Init(std::move(host));
@@ -327,28 +363,9 @@ void ArcNetHostImpl::OnInstanceClosed() {
}
void ArcNetHostImpl::GetNetworksDeprecated(
- bool configured_only,
- bool visible_only,
+ mojom::GetNetworksRequestType type,
const GetNetworksDeprecatedCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (configured_only && visible_only) {
- VLOG(1) << "Illegal arguments - both configured and visible networks "
- "requested.";
- return;
- }
-
- mojom::GetNetworksRequestType type =
- mojom::GetNetworksRequestType::CONFIGURED_ONLY;
- if (visible_only) {
- type = mojom::GetNetworksRequestType::VISIBLE_ONLY;
- }
-
- GetNetworks(type, callback);
-}
-
-void ArcNetHostImpl::GetNetworks(mojom::GetNetworksRequestType type,
- const GetNetworksCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
mojom::NetworkDataPtr data = mojom::NetworkData::New();
bool configured_only = true;
@@ -358,7 +375,7 @@ void ArcNetHostImpl::GetNetworks(mojom::GetNetworksRequestType type,
visible_only = true;
}
- // Retrieve list of nearby wifi networks
+ // Retrieve list of nearby WiFi networks.
chromeos::NetworkTypePattern network_pattern =
chromeos::onc::NetworkTypePatternFromOncType(onc::network_type::kWiFi);
std::unique_ptr<base::ListValue> network_properties_list =
@@ -423,6 +440,31 @@ void ArcNetHostImpl::GetNetworks(mojom::GetNetworksRequestType type,
callback.Run(std::move(data));
}
+void ArcNetHostImpl::GetNetworks(mojom::GetNetworksRequestType type,
+ const GetNetworksCallback& callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // Retrieve list of configured or visible WiFi networks.
+ bool configured_only = type == mojom::GetNetworksRequestType::CONFIGURED_ONLY;
+ chromeos::NetworkTypePattern network_pattern =
+ chromeos::onc::NetworkTypePatternFromOncType(onc::network_type::kWiFi);
+ std::unique_ptr<base::ListValue> network_properties_list =
+ chromeos::network_util::TranslateNetworkListToONC(
+ network_pattern, configured_only, !configured_only /* visible_only */,
+ kGetNetworksListLimit);
+
+ std::vector<mojom::NetworkConfigurationPtr> networks;
+ for (const auto& value : *network_properties_list) {
+ const base::DictionaryValue* network_dict = nullptr;
+ value.GetAsDictionary(&network_dict);
+ DCHECK(network_dict);
+ networks.push_back(TranslateONCConfiguration(network_dict));
+ }
+
+ callback.Run(mojom::GetNetworksResponseType::New(
+ arc::mojom::NetworkResult::SUCCESS, std::move(networks)));
+}
+
void ArcNetHostImpl::CreateNetworkSuccessCallback(
const mojom::NetHost::CreateNetworkCallback& mojo_callback,
const std::string& service_path,
@@ -565,7 +607,7 @@ void ArcNetHostImpl::GetWifiEnabledState(
void ArcNetHostImpl::SetWifiEnabledState(
bool is_enabled,
const SetWifiEnabledStateCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
chromeos::NetworkStateHandler::TechnologyState state =
GetStateHandler()->GetTechnologyState(
chromeos::NetworkTypePattern::WiFi());
@@ -589,7 +631,7 @@ void ArcNetHostImpl::StartScan() {
void ArcNetHostImpl::ScanCompleted(const chromeos::DeviceState* /*unused*/) {
auto* net_instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->net(), ScanCompleted);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->net(), ScanCompleted);
if (!net_instance)
return;
@@ -617,7 +659,7 @@ void ArcNetHostImpl::GetDefaultNetwork(
void ArcNetHostImpl::DefaultNetworkSuccessCallback(
const std::string& service_path,
const base::DictionaryValue& dictionary) {
- auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->net(),
+ auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->net(),
DefaultNetworkChanged);
if (!net_instance)
return;
@@ -630,8 +672,8 @@ void ArcNetHostImpl::DefaultNetworkChanged(
const chromeos::NetworkState* network) {
if (!network) {
VLOG(1) << "No default network";
- auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->net(), DefaultNetworkChanged);
+ auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->net(),
+ DefaultNetworkChanged);
if (net_instance)
net_instance->DefaultNetworkChanged(nullptr, nullptr);
return;
@@ -647,7 +689,7 @@ void ArcNetHostImpl::DefaultNetworkChanged(
}
void ArcNetHostImpl::DeviceListChanged() {
- auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->net(),
+ auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->net(),
WifiEnabledStateChanged);
if (!net_instance)
return;
diff --git a/chromium/components/arc/net/arc_net_host_impl.h b/chromium/components/arc/net/arc_net_host_impl.h
index 3c56bbf74d0..56ddffb5937 100644
--- a/chromium/components/arc/net/arc_net_host_impl.h
+++ b/chromium/components/arc/net/arc_net_host_impl.h
@@ -15,36 +15,42 @@
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "chromeos/network/network_state_handler_observer.h"
-#include "components/arc/arc_service.h"
#include "components/arc/common/net.mojom.h"
#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace base {
-
class DictionaryValue;
-
} // namespace base
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
class ArcBridgeService;
// Private implementation of ArcNetHost.
-class ArcNetHostImpl : public ArcService,
+class ArcNetHostImpl : public KeyedService,
public InstanceHolder<mojom::NetInstance>::Observer,
public chromeos::NetworkStateHandlerObserver,
public mojom::NetHost {
public:
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcNetHostImpl* GetForBrowserContext(content::BrowserContext* context);
+
// The constructor will register an Observer with ArcBridgeService.
- explicit ArcNetHostImpl(ArcBridgeService* arc_bridge_service);
+ ArcNetHostImpl(content::BrowserContext* context,
+ ArcBridgeService* arc_bridge_service);
~ArcNetHostImpl() override;
// ARC -> Chrome calls:
void GetNetworksDeprecated(
- bool configured_only,
- bool visible_only,
+ mojom::GetNetworksRequestType type,
const GetNetworksDeprecatedCallback& callback) override;
void GetNetworks(mojom::GetNetworksRequestType type,
@@ -104,6 +110,8 @@ class ArcNetHostImpl : public ArcService,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data);
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
// True if the chrome::NetworkStateHandler is currently being observed for
// state changes.
bool observing_network_state_ = false;
@@ -111,7 +119,7 @@ class ArcNetHostImpl : public ArcService,
std::string cached_service_path_;
std::string cached_guid_;
- base::ThreadChecker thread_checker_;
+ THREAD_CHECKER(thread_checker_);
mojo::Binding<mojom::NetHost> binding_;
base::WeakPtrFactory<ArcNetHostImpl> weak_factory_;
diff --git a/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc b/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
index 54fc0af9d07..dd6f7c1aa0b 100644
--- a/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
+++ b/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
@@ -4,10 +4,14 @@
#include "components/arc/obb_mounter/arc_obb_mounter_bridge.h"
+#include <utility>
+
#include "base/bind.h"
+#include "base/memory/singleton.h"
#include "chromeos/dbus/arc_obb_mounter_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 {
@@ -19,22 +23,55 @@ void RunObbCallback(const base::Callback<void(bool)>& callback,
callback.Run(result == chromeos::DBUS_METHOD_CALL_SUCCESS);
}
+// Singleton factory for ArcObbMounterBridge.
+class ArcObbMounterBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcObbMounterBridge,
+ ArcObbMounterBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcObbMounterBridgeFactory";
+
+ static ArcObbMounterBridgeFactory* GetInstance() {
+ return base::Singleton<ArcObbMounterBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcObbMounterBridgeFactory>;
+ ArcObbMounterBridgeFactory() = default;
+ ~ArcObbMounterBridgeFactory() override = default;
+};
+
} // namespace
-ArcObbMounterBridge::ArcObbMounterBridge(ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this) {
- arc_bridge_service()->obb_mounter()->AddObserver(this);
+// static
+ArcObbMounterBridge* ArcObbMounterBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcObbMounterBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcObbMounterBridge::ArcObbMounterBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service), binding_(this) {
+ arc_bridge_service_->obb_mounter()->AddObserver(this);
}
ArcObbMounterBridge::~ArcObbMounterBridge() {
- arc_bridge_service()->obb_mounter()->RemoveObserver(this);
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->obb_mounter()->RemoveObserver(this);
}
void ArcObbMounterBridge::OnInstanceReady() {
mojom::ObbMounterInstance* obb_mounter_instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->obb_mounter(), Init);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->obb_mounter(), Init);
DCHECK(obb_mounter_instance);
- obb_mounter_instance->Init(binding_.CreateInterfacePtrAndBind());
+ mojom::ObbMounterHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ obb_mounter_instance->Init(std::move(host_proxy));
}
void ArcObbMounterBridge::MountObb(const std::string& obb_file,
diff --git a/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.h b/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.h
index 46f44be5995..860d3da700c 100644
--- a/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.h
+++ b/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.h
@@ -11,19 +11,30 @@
#include "components/arc/arc_service.h"
#include "components/arc/common/obb_mounter.mojom.h"
#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
class ArcBridgeService;
// This class handles OBB mount/unmount requests from Android.
class ArcObbMounterBridge
- : public ArcService,
+ : public KeyedService,
public InstanceHolder<mojom::ObbMounterInstance>::Observer,
public mojom::ObbMounterHost {
public:
- explicit ArcObbMounterBridge(ArcBridgeService* bridge_service);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcObbMounterBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcObbMounterBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
~ArcObbMounterBridge() override;
// InstanceHolder<mojom::ObbMounterInstance>::Observer overrides:
@@ -38,6 +49,8 @@ class ArcObbMounterBridge
const UnmountObbCallback& callback) override;
private:
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
mojo::Binding<mojom::ObbMounterHost> binding_;
DISALLOW_COPY_AND_ASSIGN(ArcObbMounterBridge);
diff --git a/chromium/components/arc/power/arc_power_bridge.cc b/chromium/components/arc/power/arc_power_bridge.cc
index d63f49c4fdb..72e99db62fe 100644
--- a/chromium/components/arc/power/arc_power_bridge.cc
+++ b/chromium/components/arc/power/arc_power_bridge.cc
@@ -9,33 +9,74 @@
#include "ash/shell.h"
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_policy_controller.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"
namespace arc {
+namespace {
// Delay for notifying Android about screen brightness changes, added in
// order to prevent spammy brightness updates.
constexpr base::TimeDelta kNotifyBrightnessDelay =
base::TimeDelta::FromMilliseconds(200);
-ArcPowerBridge::ArcPowerBridge(ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this), weak_ptr_factory_(this) {
- arc_bridge_service()->power()->AddObserver(this);
+// Singleton factory for ArcPowerBridge.
+class ArcPowerBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcPowerBridge,
+ ArcPowerBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcPowerBridgeFactory";
+
+ static ArcPowerBridgeFactory* GetInstance() {
+ return base::Singleton<ArcPowerBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcPowerBridgeFactory>;
+ ArcPowerBridgeFactory() = default;
+ ~ArcPowerBridgeFactory() override = default;
+};
+
+} // namespace
+
+// static
+ArcPowerBridge* ArcPowerBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcPowerBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcPowerBridge::ArcPowerBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service),
+ binding_(this),
+ weak_ptr_factory_(this) {
+ arc_bridge_service_->power()->AddObserver(this);
}
ArcPowerBridge::~ArcPowerBridge() {
- arc_bridge_service()->power()->RemoveObserver(this);
ReleaseAllDisplayWakeLocks();
+
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->power()->RemoveObserver(this);
}
void ArcPowerBridge::OnInstanceReady() {
mojom::PowerInstance* power_instance =
- ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->power(), Init);
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->power(), Init);
DCHECK(power_instance);
- power_instance->Init(binding_.CreateInterfacePtrAndBind());
+ mojom::PowerHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ power_instance->Init(std::move(host_proxy));
ash::Shell::Get()->display_configurator()->AddObserver(this);
chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
AddObserver(this);
@@ -54,8 +95,8 @@ void ArcPowerBridge::OnInstanceClosed() {
}
void ArcPowerBridge::SuspendImminent() {
- mojom::PowerInstance* power_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->power(), Suspend);
+ mojom::PowerInstance* power_instance =
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->power(), Suspend);
if (!power_instance)
return;
@@ -65,8 +106,8 @@ void ArcPowerBridge::SuspendImminent() {
}
void ArcPowerBridge::SuspendDone(const base::TimeDelta& sleep_duration) {
- mojom::PowerInstance* power_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->power(), Resume);
+ mojom::PowerInstance* power_instance =
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->power(), Resume);
if (!power_instance)
return;
@@ -91,8 +132,8 @@ void ArcPowerBridge::BrightnessChanged(int level, bool user_initiated) {
void ArcPowerBridge::OnPowerStateChanged(
chromeos::DisplayPowerState power_state) {
- mojom::PowerInstance* power_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->power(), SetInteractive);
+ mojom::PowerInstance* power_instance =
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->power(), SetInteractive);
if (!power_instance)
return;
@@ -173,7 +214,7 @@ void ArcPowerBridge::ReleaseAllDisplayWakeLocks() {
void ArcPowerBridge::UpdateAndroidScreenBrightness(double percent) {
mojom::PowerInstance* power_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->power(), UpdateScreenBrightnessSettings);
+ arc_bridge_service_->power(), UpdateScreenBrightnessSettings);
if (!power_instance)
return;
power_instance->UpdateScreenBrightnessSettings(percent);
diff --git a/chromium/components/arc/power/arc_power_bridge.h b/chromium/components/arc/power/arc_power_bridge.h
index e6a5c7a51a3..7d2c0738334 100644
--- a/chromium/components/arc/power/arc_power_bridge.h
+++ b/chromium/components/arc/power/arc_power_bridge.h
@@ -9,25 +9,34 @@
#include "base/macros.h"
#include "chromeos/dbus/power_manager_client.h"
-#include "components/arc/arc_service.h"
#include "components/arc/common/power.mojom.h"
#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "ui/display/manager/chromeos/display_configurator.h"
+namespace content {
+class BrowserContext;
+} // namespace content
+
namespace arc {
class ArcBridgeService;
// ARC Power Client sets power management policy based on requests from
// ARC instances.
-class ArcPowerBridge : public ArcService,
+class ArcPowerBridge : public KeyedService,
public InstanceHolder<mojom::PowerInstance>::Observer,
public chromeos::PowerManagerClient::Observer,
public display::DisplayConfigurator::Observer,
public mojom::PowerHost {
public:
- explicit ArcPowerBridge(ArcBridgeService* bridge_service);
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcPowerBridge* GetForBrowserContext(content::BrowserContext* context);
+
+ ArcPowerBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
~ArcPowerBridge() override;
// InstanceHolder<mojom::PowerInstance>::Observer overrides.
@@ -52,6 +61,7 @@ class ArcPowerBridge : public ArcService,
void ReleaseAllDisplayWakeLocks();
void UpdateAndroidScreenBrightness(double percent);
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
mojo::Binding<mojom::PowerHost> binding_;
// Stores a mapping of type -> wake lock ID for all wake locks
diff --git a/chromium/components/arc/storage_manager/arc_storage_manager.cc b/chromium/components/arc/storage_manager/arc_storage_manager.cc
index c87a065f91b..b35cd7a422b 100644
--- a/chromium/components/arc/storage_manager/arc_storage_manager.cc
+++ b/chromium/components/arc/storage_manager/arc_storage_manager.cc
@@ -8,38 +8,49 @@
#include "base/bind.h"
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
namespace arc {
-
namespace {
-// This class is owned by ArcServiceManager so that it is safe to use this raw
-// pointer as the singleton reference.
-ArcStorageManager* g_arc_storage_manager = nullptr;
+// Singleton factory for ArcStorageManager.
+class ArcStorageManagerFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcStorageManager,
+ ArcStorageManagerFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcStorageManagerFactory";
-} // namespace
+ static ArcStorageManagerFactory* GetInstance() {
+ return base::Singleton<ArcStorageManagerFactory>::get();
+ }
-ArcStorageManager::ArcStorageManager(ArcBridgeService* bridge_service)
- : ArcService(bridge_service) {
- DCHECK(!g_arc_storage_manager);
- g_arc_storage_manager = this;
-}
+ private:
+ friend base::DefaultSingletonTraits<ArcStorageManagerFactory>;
+ ArcStorageManagerFactory() = default;
+ ~ArcStorageManagerFactory() override = default;
+};
-ArcStorageManager::~ArcStorageManager() {
- DCHECK_EQ(this, g_arc_storage_manager);
- g_arc_storage_manager = nullptr;
-}
+} // namespace
// static
-ArcStorageManager* ArcStorageManager::Get() {
- DCHECK(g_arc_storage_manager);
- return g_arc_storage_manager;
+ArcStorageManager* ArcStorageManager::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcStorageManagerFactory::GetForBrowserContext(context);
}
+ArcStorageManager::ArcStorageManager(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service) {}
+
+ArcStorageManager::~ArcStorageManager() = default;
+
bool ArcStorageManager::OpenPrivateVolumeSettings() {
auto* storage_manager_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->storage_manager(), OpenPrivateVolumeSettings);
+ arc_bridge_service_->storage_manager(), OpenPrivateVolumeSettings);
if (!storage_manager_instance)
return false;
storage_manager_instance->OpenPrivateVolumeSettings();
@@ -49,7 +60,7 @@ bool ArcStorageManager::OpenPrivateVolumeSettings() {
bool ArcStorageManager::GetApplicationsSize(
const GetApplicationsSizeCallback& callback) {
auto* storage_manager_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->storage_manager(), GetApplicationsSize);
+ arc_bridge_service_->storage_manager(), GetApplicationsSize);
if (!storage_manager_instance)
return false;
storage_manager_instance->GetApplicationsSize(callback);
@@ -59,7 +70,7 @@ bool ArcStorageManager::GetApplicationsSize(
bool ArcStorageManager::DeleteApplicationsCache(
const base::Callback<void()>& callback) {
auto* storage_manager_instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_bridge_service()->storage_manager(), DeleteApplicationsCache);
+ arc_bridge_service_->storage_manager(), DeleteApplicationsCache);
if (!storage_manager_instance)
return false;
storage_manager_instance->DeleteApplicationsCache(callback);
diff --git a/chromium/components/arc/storage_manager/arc_storage_manager.h b/chromium/components/arc/storage_manager/arc_storage_manager.h
index e544b9b410c..8ed50426690 100644
--- a/chromium/components/arc/storage_manager/arc_storage_manager.h
+++ b/chromium/components/arc/storage_manager/arc_storage_manager.h
@@ -9,23 +9,28 @@
#include "base/callback.h"
#include "base/macros.h"
-#include "components/arc/arc_service.h"
#include "components/arc/common/storage_manager.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
namespace arc {
class ArcBridgeService;
// This class represents as a simple proxy of StorageManager to Chrome OS.
-class ArcStorageManager : public ArcService {
+class ArcStorageManager : public KeyedService {
public:
- explicit ArcStorageManager(ArcBridgeService* bridge_service);
- ~ArcStorageManager() override;
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcStorageManager* GetForBrowserContext(
+ content::BrowserContext* context);
- // Gets the singleton instance of the ARC Storage Manager.
- // MUST be called while ArcStorageManager is alive, otherwise it returns
- // nullptr (or aborts on Debug build).
- static ArcStorageManager* Get();
+ ArcStorageManager(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
+ ~ArcStorageManager() override;
// Opens detailed preference screen of private volume on ARC.
// Returns false when an instance of ARC-side isn't ready yet.
@@ -40,6 +45,8 @@ class ArcStorageManager : public ArcService {
bool DeleteApplicationsCache(const base::Callback<void()>& callback);
private:
+ ArcBridgeService* const arc_bridge_service_;
+
DISALLOW_COPY_AND_ASSIGN(ArcStorageManager);
};
diff --git a/chromium/components/arc/video_accelerator/DEPS b/chromium/components/arc/video_accelerator/DEPS
new file mode 100644
index 00000000000..04dd5ab2b11
--- /dev/null
+++ b/chromium/components/arc/video_accelerator/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+media/video/video_encode_accelerator.h",
+]
diff --git a/chromium/components/arc/video_accelerator/video_accelerator.h b/chromium/components/arc/video_accelerator/video_accelerator.h
index bdbf6d5466f..6da58930af8 100644
--- a/chromium/components/arc/video_accelerator/video_accelerator.h
+++ b/chromium/components/arc/video_accelerator/video_accelerator.h
@@ -7,7 +7,7 @@
namespace arc {
-struct ArcVideoAcceleratorDmabufPlane {
+struct VideoFramePlane {
int32_t offset; // in bytes
int32_t stride; // in bytes
};
diff --git a/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc b/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc
index 3feb966d8af..91028274dba 100644
--- a/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc
+++ b/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc
@@ -7,17 +7,24 @@
namespace mojo {
// static
-bool StructTraits<arc::mojom::ArcVideoAcceleratorDmabufPlaneDataView,
- arc::ArcVideoAcceleratorDmabufPlane>::
- Read(arc::mojom::ArcVideoAcceleratorDmabufPlaneDataView data,
- arc::ArcVideoAcceleratorDmabufPlane* out) {
- if (data.offset() < 0 || data.stride() < 0) {
+bool StructTraits<arc::mojom::VideoFramePlaneDataView, arc::VideoFramePlane>::
+ Read(arc::mojom::VideoFramePlaneDataView data, arc::VideoFramePlane* out) {
+ if (data.offset() < 0 || data.stride() < 0)
return false;
- }
out->offset = data.offset();
out->stride = data.stride();
return true;
}
+// static
+bool StructTraits<arc::mojom::SizeDataView, gfx::Size>::Read(
+ arc::mojom::SizeDataView data,
+ gfx::Size* out) {
+ if (data.width() < 0 || data.height() < 0)
+ return false;
+
+ out->SetSize(data.width(), data.height());
+ return true;
+}
} // namespace mojo
diff --git a/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.h b/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.h
index 6c76c90ec31..c7e8d3b174a 100644
--- a/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.h
+++ b/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.h
@@ -5,28 +5,42 @@
#ifndef COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ACCELERATOR_STRUCT_TRAITS_H_
#define COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ACCELERATOR_STRUCT_TRAITS_H_
-#include "components/arc/common/video_accelerator.mojom.h"
+#include "components/arc/common/video_decode_accelerator.mojom.h"
#include "components/arc/video_accelerator/video_accelerator.h"
+#include "ui/gfx/geometry/size.h"
namespace mojo {
template <>
-struct StructTraits<arc::mojom::ArcVideoAcceleratorDmabufPlaneDataView,
- arc::ArcVideoAcceleratorDmabufPlane> {
- static uint32_t offset(const arc::ArcVideoAcceleratorDmabufPlane& r) {
+struct StructTraits<arc::mojom::VideoFramePlaneDataView, arc::VideoFramePlane> {
+ static int32_t offset(const arc::VideoFramePlane& r) {
DCHECK_GE(r.offset, 0);
return r.offset;
}
- static uint32_t stride(const arc::ArcVideoAcceleratorDmabufPlane& r) {
+ static int32_t stride(const arc::VideoFramePlane& r) {
DCHECK_GE(r.stride, 0);
return r.stride;
}
- static bool Read(arc::mojom::ArcVideoAcceleratorDmabufPlaneDataView data,
- arc::ArcVideoAcceleratorDmabufPlane* out);
+ static bool Read(arc::mojom::VideoFramePlaneDataView data,
+ arc::VideoFramePlane* out);
};
+template <>
+struct StructTraits<arc::mojom::SizeDataView, gfx::Size> {
+ static int width(const gfx::Size& r) {
+ DCHECK_GE(r.width(), 0);
+ return r.width();
+ }
+
+ static int height(const gfx::Size& r) {
+ DCHECK_GE(r.height(), 0);
+ return r.height();
+ }
+
+ static bool Read(arc::mojom::SizeDataView data, gfx::Size* out);
+};
} // namespace mojo
#endif // COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ACCELERATOR_STRUCT_TRAITS_H_
diff --git a/chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.cc b/chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.cc
new file mode 100644
index 00000000000..32a573739fa
--- /dev/null
+++ b/chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.cc
@@ -0,0 +1,163 @@
+// 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/arc/video_accelerator/video_encode_accelerator_struct_traits.h"
+
+namespace mojo {
+
+// Make sure values in arc::mojom::VideoEncodeAccelerator::Error and
+// media::VideoEncodeAccelerator::Error match.
+#define CHECK_ERROR_ENUM(value) \
+ static_assert( \
+ static_cast<int>(arc::mojom::VideoEncodeAccelerator::Error::value) == \
+ media::VideoEncodeAccelerator::Error::value, \
+ "enum ##value mismatch")
+
+CHECK_ERROR_ENUM(kIllegalStateError);
+CHECK_ERROR_ENUM(kInvalidArgumentError);
+CHECK_ERROR_ENUM(kPlatformFailureError);
+CHECK_ERROR_ENUM(kErrorMax);
+
+#undef CHECK_ERROR_ENUM
+
+// static
+arc::mojom::VideoEncodeAccelerator::Error
+EnumTraits<arc::mojom::VideoEncodeAccelerator::Error,
+ media::VideoEncodeAccelerator::Error>::
+ ToMojom(media::VideoEncodeAccelerator::Error input) {
+ return static_cast<arc::mojom::VideoEncodeAccelerator::Error>(input);
+}
+
+// static
+bool EnumTraits<arc::mojom::VideoEncodeAccelerator::Error,
+ media::VideoEncodeAccelerator::Error>::
+ FromMojom(arc::mojom::VideoEncodeAccelerator::Error input,
+ media::VideoEncodeAccelerator::Error* output) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+// Make sure values in arc::mojom::VideoPixelFormat match to the values in
+// media::VideoPixelFormat. The former is a subset of the later.
+#define CHECK_PIXEL_FORMAT_ENUM(value) \
+ static_assert( \
+ static_cast<int>(arc::mojom::VideoPixelFormat::value) == media::value, \
+ "enum ##value mismatch")
+
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_I420);
+
+#undef CHECK_PXIEL_FORMAT_ENUM
+
+// static
+arc::mojom::VideoPixelFormat
+EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat>::ToMojom(
+ media::VideoPixelFormat input) {
+ NOTIMPLEMENTED();
+ return arc::mojom::VideoPixelFormat::PIXEL_FORMAT_I420;
+}
+
+// static
+bool EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat>::
+ FromMojom(arc::mojom::VideoPixelFormat input,
+ media::VideoPixelFormat* output) {
+ switch (input) {
+ case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_I420:
+ *output = static_cast<media::VideoPixelFormat>(input);
+ return true;
+ default:
+ DLOG(ERROR) << "Unknown VideoPixelFormat: " << input;
+ return false;
+ }
+}
+
+// Make sure values in arc::mojom::VideoCodecProfile match to the values in
+// media::VideoCodecProfile.
+#define CHECK_PROFILE_ENUM(value) \
+ static_assert( \
+ static_cast<int>(arc::mojom::VideoCodecProfile::value) == media::value, \
+ "enum ##value mismatch")
+
+CHECK_PROFILE_ENUM(VIDEO_CODEC_PROFILE_UNKNOWN);
+CHECK_PROFILE_ENUM(VIDEO_CODEC_PROFILE_MIN);
+CHECK_PROFILE_ENUM(H264PROFILE_MIN);
+CHECK_PROFILE_ENUM(H264PROFILE_BASELINE);
+CHECK_PROFILE_ENUM(H264PROFILE_MAIN);
+CHECK_PROFILE_ENUM(H264PROFILE_EXTENDED);
+CHECK_PROFILE_ENUM(H264PROFILE_HIGH);
+CHECK_PROFILE_ENUM(H264PROFILE_HIGH10PROFILE);
+CHECK_PROFILE_ENUM(H264PROFILE_HIGH422PROFILE);
+CHECK_PROFILE_ENUM(H264PROFILE_HIGH444PREDICTIVEPROFILE);
+CHECK_PROFILE_ENUM(H264PROFILE_SCALABLEBASELINE);
+CHECK_PROFILE_ENUM(H264PROFILE_SCALABLEHIGH);
+CHECK_PROFILE_ENUM(H264PROFILE_STEREOHIGH);
+CHECK_PROFILE_ENUM(H264PROFILE_MULTIVIEWHIGH);
+CHECK_PROFILE_ENUM(H264PROFILE_MAX);
+CHECK_PROFILE_ENUM(VP8PROFILE_MIN);
+CHECK_PROFILE_ENUM(VP8PROFILE_ANY);
+CHECK_PROFILE_ENUM(VP8PROFILE_MAX);
+CHECK_PROFILE_ENUM(VP9PROFILE_MIN);
+CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE0);
+CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE1);
+CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE2);
+CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE3);
+CHECK_PROFILE_ENUM(VP9PROFILE_MAX);
+CHECK_PROFILE_ENUM(HEVCPROFILE_MIN);
+CHECK_PROFILE_ENUM(HEVCPROFILE_MAIN);
+CHECK_PROFILE_ENUM(HEVCPROFILE_MAIN10);
+CHECK_PROFILE_ENUM(HEVCPROFILE_MAIN_STILL_PICTURE);
+CHECK_PROFILE_ENUM(HEVCPROFILE_MAX);
+CHECK_PROFILE_ENUM(DOLBYVISION_MIN);
+CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE0);
+CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE4);
+CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE5);
+CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE7);
+CHECK_PROFILE_ENUM(DOLBYVISION_MAX);
+CHECK_PROFILE_ENUM(VIDEO_CODEC_PROFILE_MAX);
+
+#undef CHECK_PROFILE_ENUM
+
+// static
+arc::mojom::VideoCodecProfile
+EnumTraits<arc::mojom::VideoCodecProfile, media::VideoCodecProfile>::ToMojom(
+ media::VideoCodecProfile input) {
+ return static_cast<arc::mojom::VideoCodecProfile>(input);
+}
+
+// static
+bool EnumTraits<arc::mojom::VideoCodecProfile, media::VideoCodecProfile>::
+ FromMojom(arc::mojom::VideoCodecProfile input,
+ media::VideoCodecProfile* output) {
+ switch (input) {
+ case arc::mojom::VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_BASELINE:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_MAIN:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_EXTENDED:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_HIGH:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_HIGH10PROFILE:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_HIGH422PROFILE:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_HIGH444PREDICTIVEPROFILE:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_SCALABLEBASELINE:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_SCALABLEHIGH:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_STEREOHIGH:
+ case arc::mojom::VideoCodecProfile::H264PROFILE_MULTIVIEWHIGH:
+ case arc::mojom::VideoCodecProfile::VP8PROFILE_ANY:
+ case arc::mojom::VideoCodecProfile::VP9PROFILE_PROFILE0:
+ case arc::mojom::VideoCodecProfile::VP9PROFILE_PROFILE1:
+ case arc::mojom::VideoCodecProfile::VP9PROFILE_PROFILE2:
+ case arc::mojom::VideoCodecProfile::VP9PROFILE_PROFILE3:
+ case arc::mojom::VideoCodecProfile::HEVCPROFILE_MAIN:
+ case arc::mojom::VideoCodecProfile::HEVCPROFILE_MAIN10:
+ case arc::mojom::VideoCodecProfile::HEVCPROFILE_MAIN_STILL_PICTURE:
+ case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE0:
+ case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE4:
+ case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE5:
+ case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7:
+ *output = static_cast<media::VideoCodecProfile>(input);
+ return true;
+ }
+ DLOG(ERROR) << "unknown profile: " << input;
+ return false;
+}
+
+} // namespace mojo
diff --git a/chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.h b/chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.h
new file mode 100644
index 00000000000..c3b39e1f371
--- /dev/null
+++ b/chromium/components/arc/video_accelerator/video_encode_accelerator_struct_traits.h
@@ -0,0 +1,68 @@
+// 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_ARC_VIDEO_ACCELERATOR_VIDEO_ENCODE_ACCELERATOR_STRUCT_TRAITS_H_
+#define COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ENCODE_ACCELERATOR_STRUCT_TRAITS_H_
+
+#include "components/arc/common/video_encode_accelerator.mojom.h"
+#include "media/video/video_encode_accelerator.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<arc::mojom::VideoEncodeAccelerator::Error,
+ media::VideoEncodeAccelerator::Error> {
+ static arc::mojom::VideoEncodeAccelerator::Error ToMojom(
+ media::VideoEncodeAccelerator::Error input);
+
+ static bool FromMojom(arc::mojom::VideoEncodeAccelerator::Error input,
+ media::VideoEncodeAccelerator::Error* output);
+};
+
+template <>
+struct EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat> {
+ static arc::mojom::VideoPixelFormat ToMojom(media::VideoPixelFormat input);
+
+ static bool FromMojom(arc::mojom::VideoPixelFormat input,
+ media::VideoPixelFormat* output);
+};
+
+template <>
+struct EnumTraits<arc::mojom::VideoCodecProfile, media::VideoCodecProfile> {
+ static arc::mojom::VideoCodecProfile ToMojom(media::VideoCodecProfile input);
+
+ static bool FromMojom(arc::mojom::VideoCodecProfile input,
+ media::VideoCodecProfile* output);
+};
+
+template <>
+struct StructTraits<arc::mojom::VideoEncodeProfileDataView,
+ media::VideoEncodeAccelerator::SupportedProfile> {
+ static media::VideoCodecProfile profile(
+ const media::VideoEncodeAccelerator::SupportedProfile& r) {
+ return r.profile;
+ }
+ static const gfx::Size& max_resolution(
+ const media::VideoEncodeAccelerator::SupportedProfile& r) {
+ return r.max_resolution;
+ }
+ static uint32_t max_framerate_numerator(
+ const media::VideoEncodeAccelerator::SupportedProfile& r) {
+ return r.max_framerate_numerator;
+ }
+ static uint32_t max_framerate_denominator(
+ const media::VideoEncodeAccelerator::SupportedProfile& r) {
+ return r.max_framerate_denominator;
+ }
+
+ static bool Read(arc::mojom::VideoEncodeProfileDataView data,
+ media::VideoEncodeAccelerator::SupportedProfile* out) {
+ NOTIMPLEMENTED();
+ return false;
+ }
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ENCODE_ACCELERATOR_STRUCT_TRAITS_H_
diff --git a/chromium/components/arc/voice_interaction/OWNERS b/chromium/components/arc/voice_interaction/OWNERS
new file mode 100644
index 00000000000..bb6511619b7
--- /dev/null
+++ b/chromium/components/arc/voice_interaction/OWNERS
@@ -0,0 +1,2 @@
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/arc/voice_interaction/voice_interaction_struct_traits.h b/chromium/components/arc/voice_interaction/voice_interaction_struct_traits.h
new file mode 100644
index 00000000000..c519f967b29
--- /dev/null
+++ b/chromium/components/arc/voice_interaction/voice_interaction_struct_traits.h
@@ -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.
+
+#ifndef COMPONENTS_ARC_VOICE_INTERACTION_VOICE_INTERACTION_STRUCT_TRAITS_H_
+#define COMPONENTS_ARC_VOICE_INTERACTION_VOICE_INTERACTION_STRUCT_TRAITS_H_
+
+#include "ash/public/cpp/voice_interaction_state.h"
+#include "components/arc/common/voice_interaction_framework.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<arc::mojom::VoiceInteractionState,
+ ash::VoiceInteractionState> {
+ static arc::mojom::VoiceInteractionState ToMojom(
+ ash::VoiceInteractionState state) {
+ switch (state) {
+ case ash::VoiceInteractionState::NOT_READY:
+ return arc::mojom::VoiceInteractionState::NOT_READY;
+ case ash::VoiceInteractionState::STOPPED:
+ return arc::mojom::VoiceInteractionState::STOPPED;
+ case ash::VoiceInteractionState::RUNNING:
+ return arc::mojom::VoiceInteractionState::RUNNING;
+ }
+
+ NOTREACHED() << "Invalid state: " << static_cast<int>(state);
+ return arc::mojom::VoiceInteractionState::NOT_READY;
+ }
+
+ static bool FromMojom(arc::mojom::VoiceInteractionState mojom_state,
+ ash::VoiceInteractionState* state) {
+ switch (mojom_state) {
+ case arc::mojom::VoiceInteractionState::NOT_READY:
+ *state = ash::VoiceInteractionState::NOT_READY;
+ return true;
+ case arc::mojom::VoiceInteractionState::STOPPED:
+ *state = ash::VoiceInteractionState::STOPPED;
+ return true;
+ case arc::mojom::VoiceInteractionState::RUNNING:
+ *state = ash::VoiceInteractionState::RUNNING;
+ return true;
+ }
+
+ NOTREACHED() << "Invalid state: " << static_cast<int>(mojom_state);
+ *state = ash::VoiceInteractionState::NOT_READY;
+ return false;
+ }
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_ARC_VOICE_INTERACTION_VOICE_INTERACTION_STRUCT_TRAITS_H_
diff --git a/chromium/components/arc/volume_mounter/DEPS b/chromium/components/arc/volume_mounter/DEPS
new file mode 100644
index 00000000000..8d9ee79271c
--- /dev/null
+++ b/chromium/components/arc/volume_mounter/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+chromeos/disks",
+]
diff --git a/chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.cc b/chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
new file mode 100644
index 00000000000..d34a0c2bf8c
--- /dev/null
+++ b/chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
@@ -0,0 +1,141 @@
+// 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/volume_mounter/arc_volume_mounter_bridge.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/task_scheduler/post_task.h"
+#include "chromeos/disks/disk_mount_manager.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+
+using chromeos::disks::DiskMountManager;
+
+namespace arc {
+
+namespace {
+
+// Sends MountEvents of all existing MountPoints in cros-disks.
+void SendAllMountEvents(ArcVolumeMounterBridge* bridge) {
+ for (const auto& keyValue : DiskMountManager::GetInstance()->mount_points()) {
+ bridge->OnMountEvent(DiskMountManager::MountEvent::MOUNTING,
+ chromeos::MountError::MOUNT_ERROR_NONE,
+ keyValue.second);
+ }
+}
+
+// Singleton factory for ArcVolumeMounterBridge.
+class ArcVolumeMounterBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcVolumeMounterBridge,
+ ArcVolumeMounterBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcVolumeMounterBridgeFactory";
+
+ static ArcVolumeMounterBridgeFactory* GetInstance() {
+ return base::Singleton<ArcVolumeMounterBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcVolumeMounterBridgeFactory>;
+ ArcVolumeMounterBridgeFactory() = default;
+ ~ArcVolumeMounterBridgeFactory() override = default;
+};
+
+} // namespace
+
+// static
+ArcVolumeMounterBridge* ArcVolumeMounterBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcVolumeMounterBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcVolumeMounterBridge::ArcVolumeMounterBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service) {
+ arc_bridge_service_->volume_mounter()->AddObserver(this);
+ DCHECK(DiskMountManager::GetInstance());
+ DiskMountManager::GetInstance()->AddObserver(this);
+}
+
+ArcVolumeMounterBridge::~ArcVolumeMounterBridge() {
+ DiskMountManager::GetInstance()->RemoveObserver(this);
+ // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+ // BrowserContextKeyedService is not nested.
+ // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+ // so do not touch it.
+ if (ArcServiceManager::Get())
+ arc_bridge_service_->volume_mounter()->RemoveObserver(this);
+}
+
+void ArcVolumeMounterBridge::OnInstanceReady() {
+ base::PostTaskWithTraits(FROM_HERE,
+ {base::TaskPriority::USER_BLOCKING,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::BindOnce(&SendAllMountEvents, this));
+}
+
+void ArcVolumeMounterBridge::OnDiskEvent(
+ chromeos::disks::DiskMountManager::DiskEvent event,
+ const chromeos::disks::DiskMountManager::Disk* disk) {
+ // Ignored. DiskEvents will be maintained in Vold during MountEvents.
+}
+
+void ArcVolumeMounterBridge::OnDeviceEvent(
+ chromeos::disks::DiskMountManager::DeviceEvent event,
+ const std::string& device_path) {
+ // Ignored. ARC doesn't care about events other than Disk and Mount events.
+}
+
+void ArcVolumeMounterBridge::OnFormatEvent(
+ chromeos::disks::DiskMountManager::FormatEvent event,
+ chromeos::FormatError error_code,
+ const std::string& device_path) {
+ // Ignored. ARC doesn't care about events other than Disk and Mount events.
+}
+
+void ArcVolumeMounterBridge::OnMountEvent(
+ DiskMountManager::MountEvent event,
+ chromeos::MountError error_code,
+ const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
+ if (error_code != chromeos::MountError::MOUNT_ERROR_NONE) {
+ DVLOG(1) << "Error " << error_code << "occurs during MountEvent " << event;
+ return;
+ }
+
+ // Get disks informations that are needed by Android MountService.
+ const chromeos::disks::DiskMountManager::Disk* disk =
+ DiskMountManager::GetInstance()->FindDiskBySourcePath(
+ mount_info.source_path);
+ std::string fs_uuid, device_label;
+ chromeos::DeviceType device_type = chromeos::DeviceType::DEVICE_TYPE_UNKNOWN;
+ // There are several cases where disk can be null:
+ // 1. The disk is removed physically before being ejected/unmounted.
+ // 2. The disk is inserted, but then immediately removed physically. The
+ // disk removal will race with mount event in this case.
+ if (disk) {
+ fs_uuid = disk->fs_uuid();
+ device_label = disk->device_label();
+ device_type = disk->device_type();
+ } else {
+ DVLOG(1) << "Disk at " << mount_info.source_path
+ << " is null during MountEvent " << event;
+ }
+
+ mojom::VolumeMounterInstance* volume_mounter_instance =
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->volume_mounter(),
+ OnMountEvent);
+
+ if (!volume_mounter_instance)
+ return;
+
+ volume_mounter_instance->OnMountEvent(mojom::MountPointInfo::New(
+ event, mount_info.source_path, mount_info.mount_path, fs_uuid,
+ device_label, device_type));
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.h b/chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.h
new file mode 100644
index 00000000000..ae57e7a1a4c
--- /dev/null
+++ b/chromium/components/arc/volume_mounter/arc_volume_mounter_bridge.h
@@ -0,0 +1,66 @@
+// 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_ARC_VOLUME_MOUNTER_ARC_VOLUME_MOUNTER_BRIDGE_H_
+#define COMPONENTS_ARC_VOLUME_MOUNTER_ARC_VOLUME_MOUNTER_BRIDGE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chromeos/disks/disk_mount_manager.h"
+#include "components/arc/common/volume_mounter.mojom.h"
+#include "components/arc/instance_holder.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class handles Volume mount/unmount requests from cros-disks and
+// send them to Android.
+class ArcVolumeMounterBridge
+ : public KeyedService,
+ public chromeos::disks::DiskMountManager::Observer,
+ public InstanceHolder<mojom::VolumeMounterInstance>::Observer {
+ public:
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcVolumeMounterBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcVolumeMounterBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
+ ~ArcVolumeMounterBridge() override;
+
+ // InstanceHolder<mojom::VolumeMounterInstance>::Observer overrides:
+ void OnInstanceReady() override;
+
+ // chromeos::disks::DiskMountManager::Observer overrides:
+ void OnDiskEvent(
+ chromeos::disks::DiskMountManager::DiskEvent event,
+ const chromeos::disks::DiskMountManager::Disk* disk) override;
+ void OnDeviceEvent(chromeos::disks::DiskMountManager::DeviceEvent event,
+ const std::string& device_path) override;
+ void OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,
+ chromeos::MountError error_code,
+ const chromeos::disks::DiskMountManager::MountPointInfo&
+ mount_info) override;
+ void OnFormatEvent(chromeos::disks::DiskMountManager::FormatEvent event,
+ chromeos::FormatError error_code,
+ const std::string& device_path) override;
+
+ private:
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
+ DISALLOW_COPY_AND_ASSIGN(ArcVolumeMounterBridge);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_VOLUME_MOUNTER_ARC_VOLUME_MOUNTER_BRIDGE_H_
diff --git a/chromium/components/arc/volume_mounter/volume_mounter_traits.cc b/chromium/components/arc/volume_mounter/volume_mounter_traits.cc
new file mode 100644
index 00000000000..c98edf382fb
--- /dev/null
+++ b/chromium/components/arc/volume_mounter/volume_mounter_traits.cc
@@ -0,0 +1,77 @@
+// 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/arc/volume_mounter/volume_mounter_traits.h"
+
+namespace mojo {
+
+arc::mojom::DeviceType
+EnumTraits<arc::mojom::DeviceType, chromeos::DeviceType>::ToMojom(
+ chromeos::DeviceType device_type) {
+ switch (device_type) {
+ case chromeos::DeviceType::DEVICE_TYPE_USB:
+ return arc::mojom::DeviceType::DEVICE_TYPE_USB;
+ case chromeos::DeviceType::DEVICE_TYPE_SD:
+ return arc::mojom::DeviceType::DEVICE_TYPE_SD;
+ case chromeos::DeviceType::DEVICE_TYPE_UNKNOWN:
+ case chromeos::DeviceType::DEVICE_TYPE_OPTICAL_DISC:
+ case chromeos::DeviceType::DEVICE_TYPE_MOBILE:
+ case chromeos::DeviceType::DEVICE_TYPE_DVD:
+ // Android doesn't recognize this device natively. So, propagating
+ // UNKNOWN and let Android decides how to handle this.
+ return arc::mojom::DeviceType::DEVICE_TYPE_UNKNOWN;
+ }
+ NOTREACHED();
+ return arc::mojom::DeviceType::DEVICE_TYPE_UNKNOWN;
+}
+
+bool EnumTraits<arc::mojom::DeviceType, chromeos::DeviceType>::FromMojom(
+ arc::mojom::DeviceType input,
+ chromeos::DeviceType* out) {
+ switch (input) {
+ case arc::mojom::DeviceType::DEVICE_TYPE_USB:
+ *out = chromeos::DeviceType::DEVICE_TYPE_USB;
+ return true;
+ case arc::mojom::DeviceType::DEVICE_TYPE_SD:
+ *out = chromeos::DeviceType::DEVICE_TYPE_SD;
+ return true;
+ case arc::mojom::DeviceType::DEVICE_TYPE_UNKNOWN:
+ *out = chromeos::DeviceType::DEVICE_TYPE_UNKNOWN;
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+arc::mojom::MountEvent
+EnumTraits<arc::mojom::MountEvent,
+ chromeos::disks::DiskMountManager::MountEvent>::
+ ToMojom(chromeos::disks::DiskMountManager::MountEvent mount_event) {
+ switch (mount_event) {
+ case chromeos::disks::DiskMountManager::MountEvent::MOUNTING:
+ return arc::mojom::MountEvent::MOUNTING;
+ case chromeos::disks::DiskMountManager::MountEvent::UNMOUNTING:
+ return arc::mojom::MountEvent::UNMOUNTING;
+ }
+ NOTREACHED();
+ return arc::mojom::MountEvent::MOUNTING;
+}
+
+bool EnumTraits<arc::mojom::MountEvent,
+ chromeos::disks::DiskMountManager::MountEvent>::
+ FromMojom(arc::mojom::MountEvent input,
+ chromeos::disks::DiskMountManager::MountEvent* out) {
+ switch (input) {
+ case arc::mojom::MountEvent::MOUNTING:
+ *out = chromeos::disks::DiskMountManager::MountEvent::MOUNTING;
+ return true;
+ case arc::mojom::MountEvent::UNMOUNTING:
+ *out = chromeos::disks::DiskMountManager::MountEvent::UNMOUNTING;
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace mojo
diff --git a/chromium/components/arc/volume_mounter/volume_mounter_traits.h b/chromium/components/arc/volume_mounter/volume_mounter_traits.h
new file mode 100644
index 00000000000..974d66bcdbc
--- /dev/null
+++ b/chromium/components/arc/volume_mounter/volume_mounter_traits.h
@@ -0,0 +1,31 @@
+// 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_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_TRAITS_H_
+#define COMPONENTS_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_TRAITS_H_
+
+#include "chromeos/disks/disk_mount_manager.h"
+#include "components/arc/common/volume_mounter.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<arc::mojom::DeviceType, chromeos::DeviceType> {
+ static arc::mojom::DeviceType ToMojom(chromeos::DeviceType device_type);
+ static bool FromMojom(arc::mojom::DeviceType input,
+ chromeos::DeviceType* out);
+};
+
+template <>
+struct EnumTraits<arc::mojom::MountEvent,
+ chromeos::disks::DiskMountManager::MountEvent> {
+ static arc::mojom::MountEvent ToMojom(
+ chromeos::disks::DiskMountManager::MountEvent mount_event);
+ static bool FromMojom(arc::mojom::MountEvent input,
+ chromeos::disks::DiskMountManager::MountEvent* out);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_TRAITS_H_
diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn
index dab9ec22618..7515cb4c750 100644
--- a/chromium/components/autofill/android/BUILD.gn
+++ b/chromium/components/autofill/android/BUILD.gn
@@ -13,6 +13,7 @@ android_library("autofill_java") {
deps = [
":autofill_java_resources",
"//base:base_java",
+ "//content/public/android:content_java",
"//third_party/android_tools:android_support_v7_appcompat_java",
"//ui/android:ui_java",
]
@@ -23,3 +24,41 @@ android_library("autofill_java") {
"java/src/org/chromium/components/autofill/AutofillSuggestion.java",
]
}
+
+android_library("provider_java") {
+ deps = [
+ "//base:base_java",
+ "//content/public/android:content_java",
+ "//third_party/android_tools:android_support_annotations_java",
+ ]
+ java_files = [
+ "java/src/org/chromium/components/autofill/AutofillProvider.java",
+ "java/src/org/chromium/components/autofill/FormData.java",
+ "java/src/org/chromium/components/autofill/FormFieldData.java",
+ ]
+}
+
+generate_jni("jni_headers") {
+ sources = [
+ "java/src/org/chromium/components/autofill/AutofillProvider.java",
+ "java/src/org/chromium/components/autofill/FormData.java",
+ "java/src/org/chromium/components/autofill/FormFieldData.java",
+ ]
+ jni_package = "autofill"
+}
+
+static_library("provider") {
+ sources = [
+ "autofill_provider_android.cc",
+ "autofill_provider_android.h",
+ "form_data_android.cc",
+ "form_data_android.h",
+ "form_field_data_android.cc",
+ "form_field_data_android.h",
+ ]
+ deps = [
+ ":jni_headers",
+ "//components/autofill/core/browser:browser",
+ "//content/public/browser",
+ ]
+}
diff --git a/chromium/components/autofill/android/DEPS b/chromium/components/autofill/android/DEPS
new file mode 100644
index 00000000000..042235bf20b
--- /dev/null
+++ b/chromium/components/autofill/android/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/android/java",
+ "+content/public/browser",
+]
diff --git a/chromium/components/autofill/android/autofill_provider_android.cc b/chromium/components/autofill/android/autofill_provider_android.cc
new file mode 100644
index 00000000000..c2d1fb3fe84
--- /dev/null
+++ b/chromium/components/autofill/android/autofill_provider_android.cc
@@ -0,0 +1,240 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/android/autofill_provider_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/memory/ptr_util.h"
+#include "components/autofill/android/form_data_android.h"
+#include "components/autofill/core/browser/autofill_handler_proxy.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/AutofillProvider_jni.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertUTF16ToJavaString;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
+using content::BrowserThread;
+using content::WebContents;
+using gfx::RectF;
+
+namespace autofill {
+
+AutofillProviderAndroid::AutofillProviderAndroid(
+ const JavaRef<jobject>& jcaller,
+ content::WebContents* web_contents)
+ : id_(kNoQueryId), web_contents_(web_contents) {
+ JNIEnv* env = AttachCurrentThread();
+ java_ref_ = JavaObjectWeakGlobalRef(env, jcaller);
+ Java_AutofillProvider_setNativeAutofillProvider(
+ env, jcaller, reinterpret_cast<jlong>(this));
+}
+
+AutofillProviderAndroid::~AutofillProviderAndroid() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ // Remove the reference to this object on the Java side.
+ Java_AutofillProvider_setNativeAutofillProvider(env, obj, 0);
+}
+
+void AutofillProviderAndroid::OnQueryFormFieldAutofill(
+ AutofillHandlerProxy* handler,
+ int32_t id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) {
+ // The id isn't passed to Java side because Android API guarantees the
+ // response is always for current session, so we just use the current id
+ // in response, see OnAutofillAvailable.
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ id_ = id;
+
+ // Only start a new session when form is changed, the focus or feild value
+ // change will also trigger the query, so it is safe to ignore the query
+ // for the same form.
+ if (IsCurrentlyLinkedForm(form)) {
+ return;
+ }
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ form_ = base::MakeUnique<FormDataAndroid>(form);
+
+ size_t index;
+ if (!form_->GetFieldIndex(field, &index))
+ return;
+
+ gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box);
+
+ ScopedJavaLocalRef<jobject> form_obj = form_->GetJavaPeer();
+ handler_ = handler->GetWeakPtr();
+ Java_AutofillProvider_startAutofillSession(
+ env, obj, form_obj, index, transformed_bounding.x(),
+ transformed_bounding.y(), transformed_bounding.width(),
+ transformed_bounding.height());
+}
+
+void AutofillProviderAndroid::OnAutofillAvailable(JNIEnv* env,
+ jobject jcaller,
+ jobject formData) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (handler_) {
+ const FormData& form = form_->GetAutofillValues();
+ SendFormDataToRenderer(handler_.get(), id_, form);
+ }
+}
+
+void AutofillProviderAndroid::OnTextFieldDidChange(
+ AutofillHandlerProxy* handler,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const base::TimeTicks timestamp) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ size_t index;
+ if (!ValidateHandler(handler) || !IsCurrentlyLinkedForm(form) ||
+ !form_->GetSimilarFieldIndex(field, &index))
+ return;
+
+ form_->OnTextFieldDidChange(index, field.value);
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box);
+ Java_AutofillProvider_onTextFieldDidChange(
+ env, obj, index, transformed_bounding.x(), transformed_bounding.y(),
+ transformed_bounding.width(), transformed_bounding.height());
+}
+
+bool AutofillProviderAndroid::OnWillSubmitForm(
+ AutofillHandlerProxy* handler,
+ const FormData& form,
+ const base::TimeTicks timestamp) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!ValidateHandler(handler) || !IsCurrentlyLinkedForm(form))
+ return false;
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return false;
+ Java_AutofillProvider_onWillSubmitForm(env, obj);
+ Reset();
+ return true;
+}
+
+void AutofillProviderAndroid::OnFocusNoLongerOnForm(
+ AutofillHandlerProxy* handler) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!ValidateHandler(handler))
+ return;
+
+ OnFocusChanged(false, 0, RectF());
+}
+
+void AutofillProviderAndroid::OnFocusOnFormField(
+ AutofillHandlerProxy* handler,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ size_t index;
+ if (!ValidateHandler(handler) || !IsCurrentlyLinkedForm(form) ||
+ !form_->GetSimilarFieldIndex(field, &index))
+ return;
+
+ // Because this will trigger a suggestion query, set request id to browser
+ // initiated request.
+ id_ = kNoQueryId;
+
+ OnFocusChanged(true, index, ToClientAreaBound(bounding_box));
+}
+
+void AutofillProviderAndroid::OnFocusChanged(bool focus_on_form,
+ size_t index,
+ const gfx::RectF& bounding_box) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ Java_AutofillProvider_onFocusChanged(
+ env, obj, focus_on_form, index, bounding_box.x(), bounding_box.y(),
+ bounding_box.width(), bounding_box.height());
+}
+
+void AutofillProviderAndroid::OnDidFillAutofillFormData(
+ AutofillHandlerProxy* handler,
+ const FormData& form,
+ base::TimeTicks timestamp) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (handler != handler_.get() || !IsCurrentlyLinkedForm(form))
+ return;
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ Java_AutofillProvider_onDidFillAutofillFormData(env, obj);
+}
+
+void AutofillProviderAndroid::Reset(AutofillHandlerProxy* handler) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (handler == handler_.get()) {
+ handler_.reset();
+ Reset();
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ Java_AutofillProvider_reset(env, obj);
+ }
+}
+
+bool AutofillProviderAndroid::ValidateHandler(AutofillHandlerProxy* handler) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ bool ret = handler == handler_.get();
+ if (!ret)
+ handler_.reset();
+ return ret;
+}
+
+bool AutofillProviderAndroid::IsCurrentlyLinkedForm(const FormData& form) {
+ return form_ && form_->SimilarFormAs(form);
+}
+
+gfx::RectF AutofillProviderAndroid::ToClientAreaBound(
+ const gfx::RectF& bounding_box) {
+ gfx::Rect client_area = web_contents_->GetContainerBounds();
+ return bounding_box + client_area.OffsetFromOrigin();
+}
+
+void AutofillProviderAndroid::Reset() {
+ form_.reset(nullptr);
+ id_ = kNoQueryId;
+}
+
+bool RegisterAutofillProvider(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/android/autofill_provider_android.h b/chromium/components/autofill/android/autofill_provider_android.h
new file mode 100644
index 00000000000..f1af8ff7ee1
--- /dev/null
+++ b/chromium/components/autofill/android/autofill_provider_android.h
@@ -0,0 +1,81 @@
+// 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_AUTOFILL_ANDROID_AUTOFILL_PROVIDER_ANDROID_H_
+#define COMPONENTS_AUTOFILL_ANDROID_AUTOFILL_PROVIDER_ANDROID_H_
+
+#include "base/android/jni_weak_ref.h"
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/core/browser/autofill_provider.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace autofill {
+
+class FormDataAndroid;
+
+// Android implementation of AutofillProvider, it has one instance per
+// WebContents, this class is native peer of AutofillProvider.java.
+class AutofillProviderAndroid : public AutofillProvider {
+ public:
+ AutofillProviderAndroid(const base::android::JavaRef<jobject>& jcaller,
+ content::WebContents* web_contents);
+ ~AutofillProviderAndroid() override;
+
+ // AutofillProvider:
+ void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler,
+ int32_t id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) override;
+ void OnTextFieldDidChange(AutofillHandlerProxy* handler,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const base::TimeTicks timestamp) override;
+ bool OnWillSubmitForm(AutofillHandlerProxy* handler,
+ const FormData& form,
+ const base::TimeTicks timestamp) override;
+ void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) override;
+ void OnFocusOnFormField(AutofillHandlerProxy* handler,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) override;
+ void OnDidFillAutofillFormData(AutofillHandlerProxy* handler,
+ const FormData& form,
+ base::TimeTicks timestamp) override;
+ void Reset(AutofillHandlerProxy* handler) override;
+
+ // Methods called by Java.
+ void OnAutofillAvailable(JNIEnv* env, jobject jcaller, jobject form_data);
+
+ private:
+ void OnFocusChanged(bool focus_on_form,
+ size_t index,
+ const gfx::RectF& bounding_box);
+
+ bool ValidateHandler(AutofillHandlerProxy* handler);
+
+ bool IsCurrentlyLinkedForm(const FormData& form);
+
+ gfx::RectF ToClientAreaBound(const gfx::RectF& bounding_box);
+
+ void Reset();
+
+ int32_t id_;
+ std::unique_ptr<FormDataAndroid> form_;
+ base::WeakPtr<AutofillHandlerProxy> handler_;
+ JavaObjectWeakGlobalRef java_ref_;
+ content::WebContents* web_contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProviderAndroid);
+};
+
+bool RegisterAutofillProvider(JNIEnv* env);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_ANDROID_AUTOFILL_PROVIDER_ANDROID_H_
diff --git a/chromium/components/autofill/android/form_data_android.cc b/chromium/components/autofill/android/form_data_android.cc
new file mode 100644
index 00000000000..6cfb4f98058
--- /dev/null
+++ b/chromium/components/autofill/android/form_data_android.cc
@@ -0,0 +1,102 @@
+// 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/autofill/android/form_data_android.h"
+
+#include "base/android/jni_string.h"
+#include "components/autofill/android/form_field_data_android.h"
+#include "jni/FormData_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF16;
+using base::android::ConvertUTF16ToJavaString;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace autofill {
+
+FormDataAndroid::FormDataAndroid(const FormData& form)
+ : form_(form), index_(0) {}
+
+FormDataAndroid::~FormDataAndroid() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ Java_FormData_onNativeDestroyed(env, obj);
+}
+
+ScopedJavaLocalRef<jobject> FormDataAndroid::GetJavaPeer() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null()) {
+ for (size_t i = 0; i < form_.fields.size(); ++i) {
+ fields_.push_back(std::unique_ptr<FormFieldDataAndroid>(
+ new FormFieldDataAndroid(&form_.fields[i])));
+ }
+ ScopedJavaLocalRef<jstring> jname =
+ ConvertUTF16ToJavaString(env, form_.name);
+ ScopedJavaLocalRef<jstring> jhost =
+ ConvertUTF8ToJavaString(env, form_.origin.GetOrigin().spec());
+ obj = Java_FormData_createFormData(env, reinterpret_cast<intptr_t>(this),
+ jname, jhost, form_.fields.size());
+ java_ref_ = JavaObjectWeakGlobalRef(env, obj);
+ }
+ return obj;
+}
+
+const FormData& FormDataAndroid::GetAutofillValues() {
+ for (std::unique_ptr<FormFieldDataAndroid>& field : fields_)
+ field->GetValue();
+ return form_;
+}
+
+ScopedJavaLocalRef<jobject> FormDataAndroid::GetNextFormFieldData(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& jcaller) {
+ DCHECK(index_ <= fields_.size());
+ if (index_ == fields_.size())
+ return ScopedJavaLocalRef<jobject>();
+ return fields_[index_++]->GetJavaPeer();
+}
+
+void FormDataAndroid::OnTextFieldDidChange(size_t index,
+ const base::string16& value) {
+ form_.fields[index].value = value;
+ fields_[index]->OnTextFieldDidChange(value);
+}
+
+bool FormDataAndroid::GetFieldIndex(const FormFieldData& field, size_t* index) {
+ for (size_t i = 0; i < form_.fields.size(); ++i) {
+ if (form_.fields[i].SameFieldAs(field)) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FormDataAndroid::GetSimilarFieldIndex(const FormFieldData& field,
+ size_t* index) {
+ for (size_t i = 0; i < form_.fields.size(); ++i) {
+ if (form_.fields[i].SimilarFieldAs(field)) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FormDataAndroid::SimilarFormAs(const FormData& form) {
+ return form_.SimilarFormAs(form);
+}
+
+bool RegisterFormDataAndroid(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/android/form_data_android.h b/chromium/components/autofill/android/form_data_android.h
new file mode 100644
index 00000000000..130995aad8c
--- /dev/null
+++ b/chromium/components/autofill/android/form_data_android.h
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ANDROID_FORM_DATA_ANDROID_H_
+#define COMPONENTS_AUTOFILL_ANDROID_FORM_DATA_ANDROID_H_
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "components/autofill/core/common/form_data.h"
+
+namespace autofill {
+
+class FormFieldDataAndroid;
+
+// This class is native peer of FormData.java, to make autofill::FormData
+// available in Java.
+class FormDataAndroid {
+ public:
+ FormDataAndroid(const FormData& form);
+ virtual ~FormDataAndroid();
+
+ base::android::ScopedJavaLocalRef<jobject> GetJavaPeer();
+
+ // Get autofill values from Java side and return FormData.
+ const FormData& GetAutofillValues();
+
+ base::android::ScopedJavaLocalRef<jobject> GetNextFormFieldData(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller);
+
+ // Get index of given field, return True and index of focus field if found.
+ bool GetFieldIndex(const FormFieldData& field, size_t* index);
+
+ // Get index of given field, return True and index of focus field if
+ // similar field is found. This method compares less attributes than
+ // GetFieldIndex() does, and should be used when field could be changed
+ // dynamically, but the changed has no impact on autofill purpose, e.g. css
+ // style change, see FormFieldData::SimilarFieldAs() for details.
+ bool GetSimilarFieldIndex(const FormFieldData& field, size_t* index);
+
+ // Return true if this form is similar to the given form.
+ bool SimilarFormAs(const FormData& form);
+
+ // Invoked when form field which specified by |index| is charged to new
+ // |value|.
+ void OnTextFieldDidChange(size_t index, const base::string16& value);
+
+ private:
+ FormData form_;
+ std::vector<std::unique_ptr<FormFieldDataAndroid>> fields_;
+ JavaObjectWeakGlobalRef java_ref_;
+ // keep track of index when popping up fields to Java.
+ size_t index_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormDataAndroid);
+};
+
+bool RegisterFormDataAndroid(JNIEnv* env);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_ANDROID_FORM_DATA_ANDROID_H_
diff --git a/chromium/components/autofill/android/form_field_data_android.cc b/chromium/components/autofill/android/form_field_data_android.cc
new file mode 100644
index 00000000000..2df0b75678d
--- /dev/null
+++ b/chromium/components/autofill/android/form_field_data_android.cc
@@ -0,0 +1,91 @@
+// 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/autofill/android/form_field_data_android.h"
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "components/autofill/core/common/autofill_util.h"
+#include "jni/FormFieldData_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF16;
+using base::android::ConvertUTF16ToJavaString;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+using base::android::ToJavaArrayOfStrings;
+
+namespace autofill {
+
+FormFieldDataAndroid::FormFieldDataAndroid(FormFieldData* field)
+ : field_ptr_(field) {}
+
+ScopedJavaLocalRef<jobject> FormFieldDataAndroid::GetJavaPeer() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null()) {
+ ScopedJavaLocalRef<jstring> jname =
+ ConvertUTF16ToJavaString(env, field_ptr_->name);
+ ScopedJavaLocalRef<jstring> jlabel =
+ ConvertUTF16ToJavaString(env, field_ptr_->label);
+ ScopedJavaLocalRef<jstring> jvalue =
+ ConvertUTF16ToJavaString(env, field_ptr_->value);
+ ScopedJavaLocalRef<jstring> jautocomplete_attr =
+ ConvertUTF8ToJavaString(env, field_ptr_->autocomplete_attribute);
+ ScopedJavaLocalRef<jstring> jplaceholder =
+ ConvertUTF16ToJavaString(env, field_ptr_->placeholder);
+ ScopedJavaLocalRef<jstring> jid =
+ ConvertUTF16ToJavaString(env, field_ptr_->id);
+ ScopedJavaLocalRef<jstring> jtype =
+ ConvertUTF8ToJavaString(env, field_ptr_->form_control_type);
+ ScopedJavaLocalRef<jobjectArray> joption_values =
+ ToJavaArrayOfStrings(env, field_ptr_->option_values);
+ ScopedJavaLocalRef<jobjectArray> joption_contents =
+ ToJavaArrayOfStrings(env, field_ptr_->option_contents);
+
+ obj = Java_FormFieldData_createFormFieldData(
+ env, jname, jlabel, jvalue, jautocomplete_attr,
+ field_ptr_->should_autocomplete, jplaceholder, jtype, jid,
+ joption_values, joption_contents, IsCheckable(field_ptr_->check_status),
+ IsChecked(field_ptr_->check_status));
+ java_ref_ = JavaObjectWeakGlobalRef(env, obj);
+ }
+ return obj;
+}
+
+void FormFieldDataAndroid::GetValue() {
+ JNIEnv* env = AttachCurrentThread();
+
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ if (IsCheckable(field_ptr_->check_status)) {
+ bool checked = Java_FormFieldData_isChecked(env, obj);
+ SetCheckStatus(field_ptr_, true, checked);
+ } else {
+ ScopedJavaLocalRef<jstring> jvalue = Java_FormFieldData_getValue(env, obj);
+ if (jvalue.is_null())
+ return;
+ field_ptr_->value = ConvertJavaStringToUTF16(env, jvalue);
+ }
+ field_ptr_->is_autofilled = true;
+}
+
+void FormFieldDataAndroid::OnTextFieldDidChange(const base::string16& value) {
+ field_ptr_->value = value;
+ field_ptr_->is_autofilled = false;
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+
+ Java_FormFieldData_updateValue(env, obj,
+ ConvertUTF16ToJavaString(env, value));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/android/form_field_data_android.h b/chromium/components/autofill/android/form_field_data_android.h
new file mode 100644
index 00000000000..c4fd6a11998
--- /dev/null
+++ b/chromium/components/autofill/android/form_field_data_android.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_AUTOFILL_ANDROID_FORM_FIELD_DATA_ANDROID_H_
+#define COMPONENTS_AUTOFILL_ANDROID_FORM_FIELD_DATA_ANDROID_H_
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "components/autofill/core/common/form_field_data.h"
+
+namespace autofill {
+
+// This class is native peer of FormFieldData.java, makes
+// autofill::FormFieldData available in Java.
+class FormFieldDataAndroid {
+ public:
+ FormFieldDataAndroid(FormFieldData* field);
+ virtual ~FormFieldDataAndroid() {}
+
+ base::android::ScopedJavaLocalRef<jobject> GetJavaPeer();
+ void GetValue();
+ void OnTextFieldDidChange(const base::string16& value);
+
+ private:
+ // Not owned.
+ FormFieldData* field_ptr_;
+ JavaObjectWeakGlobalRef java_ref_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormFieldDataAndroid);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_ANDROID_FORM_FIELD_DATA_ANDROID_H_
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc
index d897ff3cb7f..0d37b7cac28 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc
@@ -10,6 +10,7 @@
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_handler_proxy.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/autofill_switches.h"
@@ -32,16 +33,26 @@ ContentAutofillDriver::ContentAutofillDriver(
content::RenderFrameHost* render_frame_host,
AutofillClient* client,
const std::string& app_locale,
- AutofillManager::AutofillDownloadManagerState enable_download_manager)
+ AutofillManager::AutofillDownloadManagerState enable_download_manager,
+ AutofillProvider* provider)
: render_frame_host_(render_frame_host),
- autofill_manager_(new AutofillManager(this,
- client,
- app_locale,
- enable_download_manager)),
- autofill_external_delegate_(autofill_manager_.get(), this),
+ autofill_manager_(nullptr),
key_press_handler_manager_(this),
binding_(this) {
- autofill_manager_->SetExternalDelegate(&autofill_external_delegate_);
+ // AutofillManager isn't used if provider is valid, Autofill provider is
+ // currently used by Android WebView only.
+ if (provider) {
+ autofill_handler_ = base::MakeUnique<AutofillHandlerProxy>(this, provider);
+ GetAutofillAgent()->SetUserGestureRequired(false);
+ GetAutofillAgent()->SetSecureContextRequired(true);
+ } else {
+ autofill_handler_ = base::MakeUnique<AutofillManager>(
+ this, client, app_locale, enable_download_manager);
+ autofill_manager_ = static_cast<AutofillManager*>(autofill_handler_.get());
+ autofill_external_delegate_ =
+ base::MakeUnique<AutofillExternalDelegate>(autofill_manager_, this);
+ autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get());
+ }
}
ContentAutofillDriver::~ContentAutofillDriver() {}
@@ -148,7 +159,7 @@ void ContentAutofillDriver::RendererShouldPreviewFieldWithValue(
void ContentAutofillDriver::PopupHidden() {
// If the unmask prompt is showing, keep showing the preview. The preview
// will be cleared when the prompt closes.
- if (!autofill_manager_->IsShowingUnmaskPrompt())
+ if (autofill_manager_ && !autofill_manager_->IsShowingUnmaskPrompt())
RendererShouldClearPreviewedForm();
}
@@ -176,22 +187,23 @@ void ContentAutofillDriver::DidInteractWithCreditCardForm() {
void ContentAutofillDriver::FormsSeen(const std::vector<FormData>& forms,
base::TimeTicks timestamp) {
- autofill_manager_->OnFormsSeen(forms, timestamp);
+ autofill_handler_->OnFormsSeen(forms, timestamp);
}
void ContentAutofillDriver::WillSubmitForm(const FormData& form,
base::TimeTicks timestamp) {
- autofill_manager_->OnWillSubmitForm(form, timestamp);
+ autofill_handler_->OnWillSubmitForm(form, timestamp);
}
void ContentAutofillDriver::FormSubmitted(const FormData& form) {
- autofill_manager_->OnFormSubmitted(form);
+ autofill_handler_->OnFormSubmitted(form);
}
void ContentAutofillDriver::TextFieldDidChange(const FormData& form,
const FormFieldData& field,
+ const gfx::RectF& bounding_box,
base::TimeTicks timestamp) {
- autofill_manager_->OnTextFieldDidChange(form, field, timestamp);
+ autofill_handler_->OnTextFieldDidChange(form, field, bounding_box, timestamp);
}
void ContentAutofillDriver::QueryFormFieldAutofill(
@@ -199,48 +211,56 @@ void ContentAutofillDriver::QueryFormFieldAutofill(
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {
- autofill_manager_->OnQueryFormFieldAutofill(id, form, field, bounding_box);
+ autofill_handler_->OnQueryFormFieldAutofill(id, form, field, bounding_box);
}
void ContentAutofillDriver::HidePopup() {
- autofill_manager_->OnHidePopup();
+ autofill_handler_->OnHidePopup();
}
void ContentAutofillDriver::FocusNoLongerOnForm() {
- autofill_manager_->OnFocusNoLongerOnForm();
+ autofill_handler_->OnFocusNoLongerOnForm();
+}
+
+void ContentAutofillDriver::FocusOnFormField(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) {
+ autofill_handler_->OnFocusOnFormField(form, field, bounding_box);
}
void ContentAutofillDriver::DidFillAutofillFormData(const FormData& form,
base::TimeTicks timestamp) {
- autofill_manager_->OnDidFillAutofillFormData(form, timestamp);
+ autofill_handler_->OnDidFillAutofillFormData(form, timestamp);
}
void ContentAutofillDriver::DidPreviewAutofillFormData() {
- autofill_manager_->OnDidPreviewAutofillFormData();
+ autofill_handler_->OnDidPreviewAutofillFormData();
}
void ContentAutofillDriver::DidEndTextFieldEditing() {
- autofill_manager_->OnDidEndTextFieldEditing();
+ autofill_handler_->OnDidEndTextFieldEditing();
}
void ContentAutofillDriver::SetDataList(
const std::vector<base::string16>& values,
const std::vector<base::string16>& labels) {
- autofill_manager_->OnSetDataList(values, labels);
+ autofill_handler_->OnSetDataList(values, labels);
}
void ContentAutofillDriver::DidNavigateFrame(
content::NavigationHandle* navigation_handle) {
if (navigation_handle->IsInMainFrame() &&
!navigation_handle->IsSameDocument()) {
- autofill_manager_->Reset();
+ autofill_handler_->Reset();
}
}
void ContentAutofillDriver::SetAutofillManager(
std::unique_ptr<AutofillManager> manager) {
- autofill_manager_ = std::move(manager);
- autofill_manager_->SetExternalDelegate(&autofill_external_delegate_);
+ CHECK(autofill_manager_);
+ autofill_handler_ = std::move(manager);
+ autofill_manager_ = static_cast<AutofillManager*>(autofill_handler_.get());
+ autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get());
}
const mojom::AutofillAgentPtr& ContentAutofillDriver::GetAutofillAgent() {
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h
index f0ddceb63d9..f35923673f8 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.h
@@ -25,6 +25,7 @@ class RenderFrameHost;
namespace autofill {
class AutofillClient;
+class AutofillProvider;
// Class that drives autofill flow in the browser process based on
// communication from the renderer and from the external world. There is one
@@ -37,7 +38,8 @@ class ContentAutofillDriver : public AutofillDriver,
content::RenderFrameHost* render_frame_host,
AutofillClient* client,
const std::string& app_locale,
- AutofillManager::AutofillDownloadManagerState enable_download_manager);
+ AutofillManager::AutofillDownloadManagerState enable_download_manager,
+ AutofillProvider* provider);
~ContentAutofillDriver() override;
// Gets the driver for |render_frame_host|.
@@ -76,6 +78,7 @@ class ContentAutofillDriver : public AutofillDriver,
void FormSubmitted(const FormData& form) override;
void TextFieldDidChange(const FormData& form,
const FormFieldData& field,
+ const gfx::RectF& bounding_box,
base::TimeTicks timestamp) override;
void QueryFormFieldAutofill(int32_t id,
const FormData& form,
@@ -83,6 +86,9 @@ class ContentAutofillDriver : public AutofillDriver,
const gfx::RectF& bounding_box) override;
void HidePopup() override;
void FocusNoLongerOnForm() override;
+ void FocusOnFormField(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) override;
void DidFillAutofillFormData(const FormData& form,
base::TimeTicks timestamp) override;
void DidPreviewAutofillFormData() override;
@@ -94,10 +100,10 @@ class ContentAutofillDriver : public AutofillDriver,
void DidNavigateFrame(content::NavigationHandle* navigation_handle);
AutofillExternalDelegate* autofill_external_delegate() {
- return &autofill_external_delegate_;
+ return autofill_external_delegate_.get();
}
- AutofillManager* autofill_manager() { return autofill_manager_.get(); }
+ AutofillManager* autofill_manager() { return autofill_manager_; }
content::RenderFrameHost* render_frame_host() { return render_frame_host_; }
const mojom::AutofillAgentPtr& GetAutofillAgent();
@@ -123,13 +129,18 @@ class ContentAutofillDriver : public AutofillDriver,
// always be non-NULL and valid for lifetime of |this|.
content::RenderFrameHost* const render_frame_host_;
- // AutofillManager instance via which this object drives the shared Autofill
+ // AutofillHandler instance via which this object drives the shared Autofill
// code.
- std::unique_ptr<AutofillManager> autofill_manager_;
+ std::unique_ptr<AutofillHandler> autofill_handler_;
+
+ // The pointer to autofill_handler_ if it is AutofillManager instance.
+ // TODO: unify autofill_handler_ and autofill_manager_ to a single pointer to
+ // a common root.
+ AutofillManager* autofill_manager_;
// AutofillExternalDelegate instance that this object instantiates in the
// case where the Autofill native UI is enabled.
- AutofillExternalDelegate autofill_external_delegate_;
+ std::unique_ptr<AutofillExternalDelegate> autofill_external_delegate_;
KeyPressHandlerManager key_press_handler_manager_;
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc
index eee03de0c2e..e3d822a1a8f 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc
@@ -23,9 +23,10 @@ std::unique_ptr<AutofillDriver> CreateDriver(
content::RenderFrameHost* render_frame_host,
AutofillClient* client,
const std::string& app_locale,
- AutofillManager::AutofillDownloadManagerState enable_download_manager) {
+ AutofillManager::AutofillDownloadManagerState enable_download_manager,
+ AutofillProvider* provider) {
return base::MakeUnique<ContentAutofillDriver>(
- render_frame_host, client, app_locale, enable_download_manager);
+ render_frame_host, client, app_locale, enable_download_manager, provider);
}
} // namespace
@@ -42,11 +43,21 @@ void ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
AutofillClient* client,
const std::string& app_locale,
AutofillManager::AutofillDownloadManagerState enable_download_manager) {
+ CreateForWebContentsAndDelegate(contents, client, app_locale,
+ enable_download_manager, nullptr);
+}
+
+void ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
+ content::WebContents* contents,
+ AutofillClient* client,
+ const std::string& app_locale,
+ AutofillManager::AutofillDownloadManagerState enable_download_manager,
+ AutofillProvider* provider) {
if (FromWebContents(contents))
return;
auto new_factory = base::WrapUnique(new ContentAutofillDriverFactory(
- contents, client, app_locale, enable_download_manager));
+ contents, client, app_locale, enable_download_manager, provider));
const std::vector<content::RenderFrameHost*> frames =
contents->GetAllFrames();
for (content::RenderFrameHost* frame : frames) {
@@ -67,9 +78,8 @@ ContentAutofillDriverFactory* ContentAutofillDriverFactory::FromWebContents(
// static
void ContentAutofillDriverFactory::BindAutofillDriver(
- content::RenderFrameHost* render_frame_host,
- const service_manager::BindSourceInfo& source_info,
- mojom::AutofillDriverRequest request) {
+ mojom::AutofillDriverRequest request,
+ content::RenderFrameHost* render_frame_host) {
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
// We try to bind to the driver of this render frame host,
@@ -94,11 +104,13 @@ ContentAutofillDriverFactory::ContentAutofillDriverFactory(
content::WebContents* web_contents,
AutofillClient* client,
const std::string& app_locale,
- AutofillManager::AutofillDownloadManagerState enable_download_manager)
+ AutofillManager::AutofillDownloadManagerState enable_download_manager,
+ AutofillProvider* provider)
: AutofillDriverFactory(client),
content::WebContentsObserver(web_contents),
app_locale_(app_locale),
- enable_download_manager_(enable_download_manager) {}
+ enable_download_manager_(enable_download_manager),
+ provider_(provider) {}
ContentAutofillDriver* ContentAutofillDriverFactory::DriverForFrame(
content::RenderFrameHost* render_frame_host) {
@@ -112,7 +124,7 @@ void ContentAutofillDriverFactory::RenderFrameCreated(
content::RenderFrameHost* render_frame_host) {
AddForKey(render_frame_host,
base::Bind(CreateDriver, render_frame_host, client(), app_locale_,
- enable_download_manager_));
+ enable_download_manager_, provider_));
}
void ContentAutofillDriverFactory::RenderFrameDeleted(
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
index 4a1156bb764..adc12b53ffc 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
@@ -12,7 +12,6 @@
#include "components/autofill/core/browser/autofill_driver_factory.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "content/public/browser/web_contents_observer.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
namespace content {
class RenderFrameHost;
@@ -21,6 +20,7 @@ class RenderFrameHost;
namespace autofill {
class ContentAutofillDriver;
+class AutofillProvider;
// Manages lifetime of ContentAutofillDriver. One Factory per WebContents
// creates one Driver per RenderFrame.
@@ -35,12 +35,19 @@ class ContentAutofillDriverFactory : public AutofillDriverFactory,
AutofillClient* client,
const std::string& app_locale,
AutofillManager::AutofillDownloadManagerState enable_download_manager);
+
+ static void CreateForWebContentsAndDelegate(
+ content::WebContents* contents,
+ AutofillClient* client,
+ const std::string& app_locale,
+ AutofillManager::AutofillDownloadManagerState enable_download_manager,
+ AutofillProvider* provider);
+
static ContentAutofillDriverFactory* FromWebContents(
content::WebContents* contents);
static void BindAutofillDriver(
- content::RenderFrameHost* render_frame_host,
- const service_manager::BindSourceInfo& source_info,
- mojom::AutofillDriverRequest request);
+ mojom::AutofillDriverRequest request,
+ content::RenderFrameHost* render_frame_host);
// Gets the |ContentAutofillDriver| associated with |render_frame_host|.
// |render_frame_host| must be owned by |web_contents()|.
@@ -61,10 +68,12 @@ class ContentAutofillDriverFactory : public AutofillDriverFactory,
content::WebContents* web_contents,
AutofillClient* client,
const std::string& app_locale,
- AutofillManager::AutofillDownloadManagerState enable_download_manager);
+ AutofillManager::AutofillDownloadManagerState enable_download_manager,
+ AutofillProvider* provider);
std::string app_locale_;
AutofillManager::AutofillDownloadManagerState enable_download_manager_;
+ AutofillProvider* provider_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
index c18bcbe9b7e..3ae21b4f658 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -201,6 +201,10 @@ class FakeAutofillAgent : public mojom::AutofillAgent {
int32_t key,
const PasswordFormFillData& form_data) override {}
+ void SetUserGestureRequired(bool required) override {}
+
+ void SetSecureContextRequired(bool required) override {}
+
mojo::BindingSet<mojom::AutofillAgent> bindings_;
base::Closure quit_closure_;
@@ -245,7 +249,11 @@ class TestContentAutofillDriver : public ContentAutofillDriver {
public:
TestContentAutofillDriver(content::RenderFrameHost* rfh,
AutofillClient* client)
- : ContentAutofillDriver(rfh, client, kAppLocale, kDownloadState) {
+ : ContentAutofillDriver(rfh,
+ client,
+ kAppLocale,
+ kDownloadState,
+ nullptr) {
std::unique_ptr<AutofillManager> autofill_manager(
new MockAutofillManager(this, client));
SetAutofillManager(std::move(autofill_manager));
diff --git a/chromium/components/autofill/content/browser/payments/OWNERS b/chromium/components/autofill/content/browser/payments/OWNERS
index ca512d20e83..6a806bd5dad 100644
--- a/chromium/components/autofill/content/browser/payments/OWNERS
+++ b/chromium/components/autofill/content/browser/payments/OWNERS
@@ -1 +1 @@
-jdonnelly@chromium.org
+mahmadi@chromium.org
diff --git a/chromium/components/autofill/content/common/autofill_agent.mojom b/chromium/components/autofill/content/common/autofill_agent.mojom
index f75445c7d58..9dd9fa99bd8 100644
--- a/chromium/components/autofill/content/common/autofill_agent.mojom
+++ b/chromium/components/autofill/content/common/autofill_agent.mojom
@@ -8,7 +8,7 @@ import "components/autofill/content/common/autofill_types.mojom";
import "mojo/common/string16.mojom";
// There is one instance of this interface per render frame in the render
-// process.
+// process. All methods are called by browser on renderer.
interface AutofillAgent {
// Instructs the renderer to fill the active form with the given form data.
// Please refer AutofillDriver.QueryFormFieldAutofill comments about the |id|.
@@ -51,6 +51,19 @@ interface AutofillAgent {
// |key| is the unique id associated with the password form fill data.
ShowInitialPasswordAccountSuggestions(int32 key,
PasswordFormFillData form_data);
+
+ // Configures the render to require, or not, a user gesture before notifying
+ // the autofill agent of a field change. The default is true. Bypassing the
+ // user gesture check should only used for Android Webview, which needs to
+ // be notified of every change to the field.
+ // Note: The Android platform autofill framework only sends values to the
+ // autofill service with the user's consent, so the gesture check is
+ // redundant there anyway.
+ SetUserGestureRequired(bool required);
+
+ // Configures the render to require, or not, the secure context to query
+ // autofill suggestion, the default is false.
+ SetSecureContextRequired(bool required);
};
// There is one instance of this interface per render frame in the render
diff --git a/chromium/components/autofill/content/common/autofill_driver.mojom b/chromium/components/autofill/content/common/autofill_driver.mojom
index 01e878a246e..46e79777730 100644
--- a/chromium/components/autofill/content/common/autofill_driver.mojom
+++ b/chromium/components/autofill/content/common/autofill_driver.mojom
@@ -12,7 +12,7 @@ import "ui/gfx/geometry/mojo/geometry.mojom";
import "url/mojo/url.mojom";
// There is one instance of this interface per render frame host in the browser
-// process.
+// process. All methods are called by renderer on browser.
interface AutofillDriver {
// Notification that forms have been seen that are candidates for
// filling/submitting by the AutofillManager.
@@ -27,6 +27,7 @@ interface AutofillDriver {
// Notification that a form field's value has changed.
TextFieldDidChange(FormData form,
FormFieldData field,
+ gfx.mojom.RectF bounding_box,
mojo.common.mojom.TimeTicks timestamp);
// Queries the browser for Autofill suggestions for a form input field.
@@ -42,6 +43,11 @@ interface AutofillDriver {
// Sent when the current form is no longer focused.
FocusNoLongerOnForm();
+ // Notification that a form field is focused.
+ FocusOnFormField(FormData form,
+ FormFieldData field,
+ gfx.mojom.RectF bounding_box);
+
// Sent when a form is filled with Autofill suggestions.
DidFillAutofillFormData(FormData form, mojo.common.mojom.TimeTicks timestamp);
@@ -82,6 +88,10 @@ interface PasswordManagerDriver {
// Never pass a free-form string as |log|.
RecordSavePasswordProgress(string log);
+ // Notification that the user (not JavaScript) modified the value of a
+ // password field.
+ UserModifiedPasswordField();
+
// Instructs the browser to show a popup with suggestions filled from data
// associated with |key|. The popup will use |text_direction| for displaying
// text.
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 2fa52408cf1..d4ba27181a3 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
@@ -206,7 +206,9 @@ class AutofillTypeTraitsTestImpl : public testing::Test,
AutofillTypeTraitsTestImpl() {}
mojom::TypeTraitsTestPtr GetTypeTraitsTestProxy() {
- return bindings_.CreateInterfacePtrAndBind(this);
+ mojom::TypeTraitsTestPtr proxy;
+ bindings_.AddBinding(this, mojo::MakeRequest(&proxy));
+ return proxy;
}
// mojom::TypeTraitsTest:
diff --git a/chromium/components/autofill/content/renderer/OWNERS b/chromium/components/autofill/content/renderer/OWNERS
index bc6e10076c4..c5e2808f24c 100644
--- a/chromium/components/autofill/content/renderer/OWNERS
+++ b/chromium/components/autofill/content/renderer/OWNERS
@@ -1,2 +1,4 @@
per-file *password*=dvadym@chromium.org
+per-file *password*=kolos@chromium.org
per-file *password*=vabr@chromium.org
+per-file *password*=vasilii@chromium.org
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc
index 4da8a7a0149..98a96eacef1 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/autofill_agent.cc
@@ -37,6 +37,7 @@
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/core/common/save_password_progress_logger.h"
#include "content/public/common/content_switches.h"
+#include "content/public/common/origin_util.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
@@ -152,6 +153,8 @@ AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
ignore_text_changes_(false),
is_popup_possibly_visible_(false),
is_generation_popup_possibly_visible_(false),
+ is_user_gesture_required_(true),
+ is_secure_context_required_(false),
page_click_tracker_(new PageClickTracker(render_frame, this)),
binding_(this),
weak_ptr_factory_(this) {
@@ -165,9 +168,7 @@ AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
AutofillAgent::~AutofillAgent() {}
-void AutofillAgent::BindRequest(
- const service_manager::BindSourceInfo& source_info,
- mojom::AutofillAgentRequest request) {
+void AutofillAgent::BindRequest(mojom::AutofillAgentRequest request) {
binding_.Bind(std::move(request));
}
@@ -245,6 +246,15 @@ void AutofillAgent::FocusedNodeChanged(const WebNode& node) {
return;
element_ = *element;
+
+ FormData form;
+ FormFieldData field;
+ if (form_util::FindFormAndFieldForFormControlElement(element_, &form,
+ &field)) {
+ GetAutofillDriver()->FocusOnFormField(
+ form, field,
+ render_frame()->GetRenderView()->ElementBoundsInWindow(element_));
+ }
}
void AutofillAgent::OnDestruct() {
@@ -307,6 +317,10 @@ void AutofillAgent::TextFieldDidEndEditing(const WebInputElement& element) {
GetAutofillDriver()->DidEndTextFieldEditing();
}
+void AutofillAgent::SetUserGestureRequired(bool required) {
+ is_user_gesture_required_ = required;
+}
+
void AutofillAgent::TextFieldDidChange(const WebFormControlElement& element) {
DCHECK(ToWebInputElement(&element) || form_util::IsTextAreaElement(element));
@@ -316,7 +330,8 @@ void AutofillAgent::TextFieldDidChange(const WebFormControlElement& element) {
// Disregard text changes that aren't caused by user gestures or pastes. Note
// that pastes aren't necessarily user gestures because Blink's conception of
// user gestures is centered around creating new windows/tabs.
- if (!IsUserGesture() && !render_frame()->IsPasting())
+ if (is_user_gesture_required_ && !IsUserGesture() &&
+ !render_frame()->IsPasting())
return;
// We post a task for doing the Autofill as the caret position is not set
@@ -369,8 +384,10 @@ void AutofillAgent::TextFieldDidChangeImpl(
FormFieldData field;
if (form_util::FindFormAndFieldForFormControlElement(element, &form,
&field)) {
- GetAutofillDriver()->TextFieldDidChange(form, field,
- base::TimeTicks::Now());
+ GetAutofillDriver()->TextFieldDidChange(
+ form, field,
+ render_frame()->GetRenderView()->ElementBoundsInWindow(element),
+ base::TimeTicks::Now());
}
}
@@ -410,9 +427,10 @@ void AutofillAgent::DoAcceptDataListSuggestion(
// If this element takes multiple values then replace the last part with
// the suggestion.
if (input_element->IsMultiple() && input_element->IsEmailField()) {
- std::vector<base::StringPiece16> parts = base::SplitStringPiece(
- input_element->EditingValue().Utf16(), base::ASCIIToUTF16(","),
- base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+ base::string16 value = input_element->EditingValue().Utf16();
+ std::vector<base::StringPiece16> parts =
+ base::SplitStringPiece(value, base::ASCIIToUTF16(","),
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (parts.size() == 0)
parts.push_back(base::StringPiece16());
@@ -654,6 +672,10 @@ void AutofillAgent::ShowSuggestions(const WebFormControlElement& element,
QueryAutofillSuggestions(element);
}
+void AutofillAgent::SetSecureContextRequired(bool required) {
+ is_secure_context_required_ = required;
+}
+
void AutofillAgent::QueryAutofillSuggestions(
const WebFormControlElement& element) {
if (!element.GetDocument().GetFrame())
@@ -674,6 +696,17 @@ void AutofillAgent::QueryAutofillSuggestions(
&field);
}
+ // Check the form action attribute only if it is not empty, see
+ // crbug.com/757895.
+ if (is_secure_context_required_ &&
+ !(element.GetDocument().IsSecureContext() &&
+ (form.action.is_empty() || content::IsOriginSecure(form.action)))) {
+ LOG(WARNING) << "Autofill suggestions are disabled because the document "
+ "isn't a secure context or the form's action attribute "
+ "isn't secure.";
+ return;
+ }
+
std::vector<base::string16> data_list_values;
std::vector<base::string16> data_list_labels;
const WebInputElement* input_element = ToWebInputElement(&element);
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h
index e11e13e0469..549ec8facf8 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/autofill_agent.h
@@ -20,7 +20,6 @@
#include "components/autofill/content/renderer/page_click_tracker.h"
#include "content/public/renderer/render_frame_observer.h"
#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "third_party/WebKit/public/web/WebAutofillClient.h"
#include "third_party/WebKit/public/web/WebFormControlElement.h"
#include "third_party/WebKit/public/web/WebFormElement.h"
@@ -59,8 +58,7 @@ class AutofillAgent : public content::RenderFrameObserver,
PasswordGenerationAgent* password_generation_agent);
~AutofillAgent() override;
- void BindRequest(const service_manager::BindSourceInfo& source_info,
- mojom::AutofillAgentRequest request);
+ void BindRequest(mojom::AutofillAgentRequest request);
const mojom::AutofillDriverPtr& GetAutofillDriver();
@@ -83,6 +81,8 @@ class AutofillAgent : public content::RenderFrameObserver,
void ShowInitialPasswordAccountSuggestions(
int32_t key,
const PasswordFormFillData& form_data) override;
+ void SetUserGestureRequired(bool required) override;
+ void SetSecureContextRequired(bool required) override;
void ShowNotSecureWarning(const blink::WebInputElement& element);
@@ -278,6 +278,14 @@ class AutofillAgent : public content::RenderFrameObserver,
// for the password manager. TODO(gcasto): Have both UIs show on focus.
bool is_generation_popup_possibly_visible_;
+ // Whether or not a user gesture is required before notification of a text
+ // field change. Default to true.
+ bool is_user_gesture_required_;
+
+ // Whether or not the secure context is required to query autofill suggestion.
+ // Default to false.
+ bool is_secure_context_required_;
+
std::unique_ptr<PageClickTracker> page_click_tracker_;
mojo::Binding<mojom::AutofillAgent> binding_;
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc
index 71d53fc1416..bf662aa0260 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc
@@ -4,6 +4,8 @@
#include "components/autofill/content/renderer/form_autofill_util.h"
+#include <algorithm>
+#include <limits>
#include <map>
#include <memory>
#include <set>
@@ -43,9 +45,9 @@ using blink::WebElement;
using blink::WebElementCollection;
using blink::WebFormControlElement;
using blink::WebFormElement;
-using blink::WebFrame;
using blink::WebInputElement;
using blink::WebLabelElement;
+using blink::WebLocalFrame;
using blink::WebNode;
using blink::WebOptionElement;
using blink::WebSelectElement;
@@ -109,9 +111,7 @@ bool IsElementInControlElementSet(
return false;
const WebFormControlElement form_control_element =
element.ToConst<WebFormControlElement>();
- return std::find(control_elements.begin(),
- control_elements.end(),
- form_control_element) != control_elements.end();
+ return base::ContainsValue(control_elements, form_control_element);
}
bool IsElementInsideFormOrFieldSet(const WebElement& element) {
@@ -1194,7 +1194,7 @@ bool ExtractFormData(const WebFormElement& form_element, FormData* data) {
data, NULL);
}
-bool IsFormVisible(blink::WebFrame* frame,
+bool IsFormVisible(blink::WebLocalFrame* frame,
const blink::WebFormElement& form_element,
const GURL& canonical_action,
const GURL& canonical_origin,
@@ -1473,7 +1473,7 @@ bool WebFormElementToFormData(
ExtractMask extract_mask,
FormData* form,
FormFieldData* field) {
- const WebFrame* frame = form_element.GetDocument().GetFrame();
+ const WebLocalFrame* frame = form_element.GetDocument().GetFrame();
if (!frame)
return false;
@@ -1762,7 +1762,7 @@ bool ClearPreviewedFormWithElement(const WebFormControlElement& element,
return true;
}
-bool IsWebpageEmpty(const blink::WebFrame* frame) {
+bool IsWebpageEmpty(const blink::WebLocalFrame* frame) {
blink::WebDocument document = frame->GetDocument();
return IsWebElementEmpty(document.Head()) &&
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h
index 998d5b0459a..eac0abc26e7 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.h
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.h
@@ -25,8 +25,8 @@ class WebDocument;
class WebElement;
class WebFormControlElement;
class WebFormElement;
-class WebFrame;
class WebInputElement;
+class WebLocalFrame;
class WebNode;
}
@@ -87,7 +87,7 @@ bool ExtractFormData(const blink::WebFormElement& form_element, FormData* data);
// equals |form_element|. If |form_element| is null, checks if forms action
// equals |action|. Returns true if so. For forms with empty or unspecified
// actions, all form data are used for comparison.
-bool IsFormVisible(blink::WebFrame* frame,
+bool IsFormVisible(blink::WebLocalFrame* frame,
const blink::WebFormElement& form_element,
const GURL& action,
const GURL& origin,
@@ -255,7 +255,7 @@ bool ClearPreviewedFormWithElement(const blink::WebFormControlElement& element,
// <body/>
// <html/>
// Meta, script and title tags don't influence the emptiness of a webpage.
-bool IsWebpageEmpty(const blink::WebFrame* frame);
+bool IsWebpageEmpty(const blink::WebLocalFrame* frame);
// This function checks whether the children of |element|
// are of the type <script>, <meta>, or <title>.
diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc
index 2f360bc0fcb..18a102240eb 100644
--- a/chromium/components/autofill/content/renderer/form_cache.cc
+++ b/chromium/components/autofill/content/renderer/form_cache.cc
@@ -4,6 +4,10 @@
#include "components/autofill/content/renderer/form_cache.h"
+#include <algorithm>
+#include <string>
+#include <utility>
+
#include "base/logging.h"
#include "base/macros.h"
#include "base/stl_util.h"
@@ -28,7 +32,7 @@ using blink::WebDocument;
using blink::WebElement;
using blink::WebFormControlElement;
using blink::WebFormElement;
-using blink::WebFrame;
+using blink::WebLocalFrame;
using blink::WebInputElement;
using blink::WebNode;
using blink::WebSelectElement;
@@ -82,8 +86,7 @@ bool IsFormInteresting(const FormData& form, size_t num_editable_elements) {
} // namespace
-FormCache::FormCache(const WebFrame& frame) : frame_(frame) {
-}
+FormCache::FormCache(const WebLocalFrame& frame) : frame_(frame) {}
FormCache::~FormCache() {
}
diff --git a/chromium/components/autofill/content/renderer/form_cache.h b/chromium/components/autofill/content/renderer/form_cache.h
index 4afb3a66f9e..f2f7775c712 100644
--- a/chromium/components/autofill/content/renderer/form_cache.h
+++ b/chromium/components/autofill/content/renderer/form_cache.h
@@ -17,8 +17,8 @@
namespace blink {
class WebFormControlElement;
-class WebFrame;
class WebInputElement;
+class WebLocalFrame;
class WebSelectElement;
}
@@ -29,7 +29,7 @@ struct FormDataPredictions;
// Manages the forms in a single RenderFrame.
class FormCache {
public:
- explicit FormCache(const blink::WebFrame& frame);
+ explicit FormCache(const blink::WebLocalFrame& frame);
~FormCache();
// Scans the DOM in |frame_| extracting and storing forms that have not been
@@ -62,7 +62,7 @@ class FormCache {
const std::vector<blink::WebFormControlElement>& control_elements);
// The frame this FormCache is associated with.
- const blink::WebFrame& frame_;
+ const blink::WebLocalFrame& frame_;
// The cached forms. Used to prevent re-extraction of forms.
std::set<FormData> parsed_forms_;
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
index 893d84f30b0..ca91a857579 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
@@ -268,13 +268,13 @@ bool IsElementEditable(const blink::WebInputElement& element) {
return element.IsEnabled() && !element.IsReadOnly();
}
-bool DoUsernamesMatch(const base::string16& username1,
- const base::string16& username2,
+bool DoUsernamesMatch(const base::string16& potential_suggestion,
+ const base::string16& current_username,
bool exact_match) {
- if (exact_match)
- return username1 == username2;
- return FieldIsSuggestionSubstringStartingOnTokenBoundary(username1, username2,
- true);
+ if (potential_suggestion == current_username)
+ return true;
+ return !exact_match && IsPrefixOfEmailEndingWithAtSign(current_username,
+ potential_suggestion);
}
// Returns whether the given |element| is editable.
@@ -373,6 +373,54 @@ void UpdateFieldValueAndPropertiesMaskMap(
}
}
+// This function attempts to find the matching credentials for the
+// |current_username| by scanning |fill_data|. The result is written in
+// |username| and |password| parameters.
+void FindMatchesByUsername(const PasswordFormFillData& fill_data,
+ const base::string16& current_username,
+ bool exact_username_match,
+ RendererSavePasswordProgressLogger* logger,
+ base::string16* username,
+ base::string16* password) {
+ // Look for any suitable matches to current field text.
+ if (DoUsernamesMatch(fill_data.username_field.value, current_username,
+ exact_username_match)) {
+ *username = fill_data.username_field.value;
+ *password = fill_data.password_field.value;
+ if (logger)
+ logger->LogMessage(Logger::STRING_USERNAMES_MATCH);
+ } else {
+ // Scan additional logins for a match.
+ for (const auto& it : fill_data.additional_logins) {
+ if (DoUsernamesMatch(it.first, current_username, exact_username_match)) {
+ *username = it.first;
+ *password = it.second.password;
+ break;
+ }
+ }
+ if (logger) {
+ logger->LogBoolean(Logger::STRING_MATCH_IN_ADDITIONAL,
+ !(username->empty() && password->empty()));
+ }
+
+ // Check possible usernames.
+ if (username->empty() && password->empty()) {
+ for (const auto& it : fill_data.other_possible_usernames) {
+ for (size_t i = 0; i < it.second.size(); ++i) {
+ if (DoUsernamesMatch(it.second[i], current_username,
+ exact_username_match)) {
+ *username = it.second[i];
+ *password = it.first.password;
+ break;
+ }
+ }
+ if (!password->empty())
+ break;
+ }
+ }
+ }
+}
+
// This function attempts to fill |username_element| and |password_element|
// with values from |fill_data|. The |password_element| will only have the
// suggestedValue set, and will be registered for copying that to the real
@@ -404,43 +452,9 @@ bool FillUserNameAndPassword(
base::string16 username;
base::string16 password;
- // Look for any suitable matches to current field text.
- if (DoUsernamesMatch(fill_data.username_field.value, current_username,
- exact_username_match)) {
- username = fill_data.username_field.value;
- password = fill_data.password_field.value;
- if (logger)
- logger->LogMessage(Logger::STRING_USERNAMES_MATCH);
- } else {
- // Scan additional logins for a match.
- for (const auto& it : fill_data.additional_logins) {
- if (DoUsernamesMatch(it.first, current_username, exact_username_match)) {
- username = it.first;
- password = it.second.password;
- break;
- }
- }
- if (logger) {
- logger->LogBoolean(Logger::STRING_MATCH_IN_ADDITIONAL,
- !(username.empty() && password.empty()));
- }
+ FindMatchesByUsername(fill_data, current_username, exact_username_match,
+ logger, &username, &password);
- // Check possible usernames.
- if (username.empty() && password.empty()) {
- for (const auto& it : fill_data.other_possible_usernames) {
- for (size_t i = 0; i < it.second.size(); ++i) {
- if (DoUsernamesMatch(
- it.second[i], current_username, exact_username_match)) {
- username = it.second[i];
- password = it.first.password;
- break;
- }
- }
- if (!username.empty() && !password.empty())
- break;
- }
- }
- }
if (password.empty())
return false;
@@ -462,10 +476,6 @@ bool FillUserNameAndPassword(
form_util::PreviewSuggestion(username, current_username,
username_element);
}
- } else if (current_username != username) {
- // If the username can't be filled and it doesn't match a saved password
- // as is, don't autofill a password.
- return false;
}
// Wait to fill in the password until a user gesture occurs. This is to make
@@ -550,12 +560,14 @@ bool FillFormOnPasswordReceived(
blink::WebString::FromUTF16(fill_data.username_field.value));
}
- // Fill if we have an exact match for the username. Note that this sets
- // username to autofilled.
+ bool exact_username_match =
+ username_element.IsNull() || IsElementEditable(username_element);
+ // Use the exact match for the editable username fields and allow prefix
+ // match for read-only username fields.
return FillUserNameAndPassword(
- &username_element, &password_element, fill_data,
- true /* exact_username_match */, false /* set_selection */,
- field_value_and_properties_map, registration_callback, logger);
+ &username_element, &password_element, fill_data, exact_username_match,
+ false /* set_selection */, field_value_and_properties_map,
+ registration_callback, logger);
}
// Annotate |forms| with form and field signatures as HTML attributes.
@@ -612,7 +624,7 @@ bool HasPasswordField(const blink::WebLocalFrame& frame) {
// preceding the |password_element| either in a form, if it belongs to one, or
// in the |frame|.
blink::WebInputElement FindUsernameElementPrecedingPasswordElement(
- blink::WebFrame* frame,
+ blink::WebLocalFrame* frame,
const blink::WebInputElement& password_element) {
DCHECK(!password_element.IsNull());
@@ -688,7 +700,6 @@ PasswordAutofillAgent::~PasswordAutofillAgent() {
}
void PasswordAutofillAgent::BindRequest(
- const service_manager::BindSourceInfo& source_info,
mojom::PasswordAutofillAgentRequest request) {
binding_.Bind(std::move(request));
}
@@ -763,7 +774,7 @@ void PasswordAutofillAgent::UpdateStateForTextChange(
&field_value_and_properties_map_);
}
- blink::WebFrame* const element_frame = element.GetDocument().GetFrame();
+ blink::WebLocalFrame* const element_frame = element.GetDocument().GetFrame();
// The element's frame might have been detached in the meantime (see
// http://crbug.com/585363, comments 5 and 6), in which case frame() will
// return null. This was hardly caused by form submission (unless the user
@@ -798,6 +809,9 @@ void PasswordAutofillAgent::UpdateStateForTextChange(
mutable_element.SetAutofilled(false);
}
}
+
+ if (element.IsPasswordField())
+ GetPasswordManagerDriver()->UserModifiedPasswordField();
}
bool PasswordAutofillAgent::FillSuggestion(
@@ -986,7 +1000,8 @@ bool PasswordAutofillAgent::IsUsernameOrPasswordField(
// to be the username field.
std::unique_ptr<PasswordForm> password_form;
if (element.Form().IsNull()) {
- blink::WebFrame* const element_frame = element.GetDocument().GetFrame();
+ blink::WebLocalFrame* const element_frame =
+ element.GetDocument().GetFrame();
if (!element_frame)
return false;
@@ -1105,7 +1120,7 @@ void PasswordAutofillAgent::OnSameDocumentNavigationCompleted(
// Prompt to save only if the form is now gone, either invisible or
// removed from the DOM.
- blink::WebFrame* frame = render_frame()->GetWebFrame();
+ blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
const auto& password_form = provisionally_saved_form_.password_form();
// TODO(crbug.com/720347): This method could be called often and checking form
// visibility could be expesive. Add performance metrics for this.
@@ -1187,6 +1202,11 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
std::vector<PasswordForm> password_forms;
for (const blink::WebFormElement& form : forms) {
+ if (IsGaiaReauthenticationForm(form)) {
+ // Bail if this is a GAIA passwords site reauthentication form, so that
+ // page will be ignored.
+ return;
+ }
if (only_visible) {
bool is_form_visible = form_util::AreFormContentsVisible(form);
if (logger) {
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h
index b1ea39258e9..a39e3d854fc 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h
@@ -22,7 +22,6 @@
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_view_observer.h"
#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
namespace blink {
@@ -46,8 +45,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
explicit PasswordAutofillAgent(content::RenderFrame* render_frame);
~PasswordAutofillAgent() override;
- void BindRequest(const service_manager::BindSourceInfo& source_info,
- mojom::PasswordAutofillAgentRequest request);
+ void BindRequest(mojom::PasswordAutofillAgentRequest request);
void SetAutofillAgent(AutofillAgent* autofill_agent);
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 825f44621b9..109439563f7 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -29,15 +29,15 @@
#include "third_party/WebKit/public/platform/WebVector.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFormControlElement.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/re2/src/re2/re2.h"
using blink::WebDocument;
using blink::WebFormControlElement;
using blink::WebFormElement;
-using blink::WebFrame;
using blink::WebInputElement;
+using blink::WebLocalFrame;
using blink::WebString;
namespace autofill {
@@ -306,8 +306,6 @@ void FindPredictedElements(
}
}
-// TODO(crbug.com/543085): Move the reauthentication recognition code to the
-// browser.
const char kPasswordSiteUrlRegex[] =
"passwords(?:-[a-z-]+\\.corp)?\\.google\\.com";
@@ -339,10 +337,10 @@ bool FieldHasNonscriptModifiedValue(
// Helper function that checks the presence of visible password and username
// fields in |form.control_elements|.
-// Iff a visible password found, then |*found_visible_password| is set to true.
-// Iff a visible password found AND there is a visible username before it, then
-// |*found_visible_username_before_visible_password| is set to true.
-void FoundVisiblePasswordAndVisibleUsernameBeforePassword(
+// Iff a visible password is found, then |*found_visible_password| is set to
+// true. Iff a visible password is found AND there is a visible username before
+// it, then |*found_visible_username_before_visible_password| is set to true.
+void FindVisiblePasswordAndVisibleUsernameBeforePassword(
const SyntheticForm& form,
bool* found_visible_password,
bool* found_visible_username_before_visible_password) {
@@ -394,15 +392,6 @@ bool GetPasswordForm(
last_text_input_before_password;
autofill::PossibleUsernamesVector other_possible_usernames;
- // Bail if this is a GAIA passwords site reauthentication form, so that
- // the form will be ignored.
- // TODO(msramek): Move this logic to the browser, and disable filling only
- // for the sync credential and if passwords are being synced.
- if (IsGaiaReauthenticationForm(GURL(form.document.Url()).GetOrigin(),
- form.control_elements)) {
- return false;
- }
-
std::map<WebInputElement, PasswordFormFieldPredictionType> predicted_elements;
if (form_predictions) {
FindPredictedElements(form, password_form->form_data, *form_predictions,
@@ -417,7 +406,7 @@ bool GetPasswordForm(
// the latest username field just before selected password field).
bool ignore_invisible_passwords = false;
bool ignore_invisible_usernames = false;
- FoundVisiblePasswordAndVisibleUsernameBeforePassword(
+ FindVisiblePasswordAndVisibleUsernameBeforePassword(
form, &ignore_invisible_passwords, &ignore_invisible_usernames);
std::string layout_sequence;
layout_sequence.reserve(form.control_elements.size());
@@ -649,16 +638,19 @@ bool GetPasswordForm(
} // namespace
-bool IsGaiaReauthenticationForm(
- const GURL& origin,
- const std::vector<blink::WebFormControlElement>& control_elements) {
- if (origin != GaiaUrls::GetInstance()->gaia_url().GetOrigin())
+bool IsGaiaReauthenticationForm(const blink::WebFormElement& form) {
+ if (GURL(form.GetDocument().Url()).GetOrigin() !=
+ GaiaUrls::GetInstance()->gaia_url().GetOrigin()) {
return false;
+ }
bool has_rart_field = false;
bool has_continue_field = false;
- for (const blink::WebFormControlElement& element : control_elements) {
+ blink::WebVector<blink::WebFormControlElement> web_control_elements;
+ form.GetFormControlElements(web_control_elements);
+
+ for (const blink::WebFormControlElement& element : web_control_elements) {
// We're only interested in the presence
// of <input type="hidden" /> elements.
CR_DEFINE_STATIC_LOCAL(WebString, kHidden, ("hidden"));
@@ -711,7 +703,7 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm(
}
std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
- const WebFrame& frame,
+ const WebLocalFrame& frame,
const FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
const FormsPredictionsMap* form_predictions) {
SyntheticForm synthetic_form;
@@ -740,31 +732,37 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
}
bool HasAutocompleteAttributeValue(const blink::WebInputElement& element,
- const char* value_in_lowercase) {
- std::string autocomplete_value_lowercase = base::ToLowerASCII(
- base::UTF16ToUTF8(element.GetAttribute("autocomplete").Utf16()));
-
- std::vector<base::StringPiece> tokens = base::SplitStringPiece(
- autocomplete_value_lowercase, base::kWhitespaceASCII,
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-
- return base::ContainsValue(tokens, value_in_lowercase);
+ base::StringPiece value_in_lowercase) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kAutocomplete, ("autocomplete"));
+ const std::string autocomplete_value =
+ element.GetAttribute(kAutocomplete)
+ .Utf8(WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
+
+ std::vector<base::StringPiece> tokens =
+ base::SplitStringPiece(autocomplete_value, base::kWhitespaceASCII,
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ return std::find_if(tokens.begin(), tokens.end(),
+ [value_in_lowercase](base::StringPiece token) {
+ return base::LowerCaseEqualsASCII(token,
+ value_in_lowercase);
+ }) != tokens.end();
}
bool HasCreditCardAutocompleteAttributes(
const blink::WebInputElement& element) {
- std::string autocomplete_value_lowercase = base::ToLowerASCII(
- base::UTF16ToUTF8(element.GetAttribute("autocomplete").Utf16()));
-
- for (const auto& token : base::SplitStringPiece(
- autocomplete_value_lowercase, base::kWhitespaceASCII,
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
- if (base::StartsWith(token, kAutocompleteCreditCardPrefix,
- base::CompareCase::SENSITIVE)) {
- return true;
- }
- }
- return false;
+ CR_DEFINE_STATIC_LOCAL(WebString, kAutocomplete, ("autocomplete"));
+ const std::string autocomplete_value =
+ element.GetAttribute(kAutocomplete)
+ .Utf8(WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
+
+ std::vector<base::StringPiece> tokens =
+ base::SplitStringPiece(autocomplete_value, base::kWhitespaceASCII,
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ return std::find_if(
+ tokens.begin(), tokens.end(), [](base::StringPiece token) {
+ return base::StartsWith(token, kAutocompleteCreditCardPrefix,
+ base::CompareCase::INSENSITIVE_ASCII);
+ }) != tokens.end();
}
bool IsCreditCardVerificationPasswordField(
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 0cebba2f265..a039eaf546c 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -7,8 +7,11 @@
#include <map>
#include <memory>
+#include <string>
+#include <utility>
#include <vector>
+#include "base/strings/string_piece.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_field_prediction_map.h"
#include "third_party/WebKit/public/platform/WebString.h"
@@ -17,21 +20,16 @@
namespace blink {
class WebFormElement;
class WebFormControlElement;
-class WebFrame;
class WebInputElement;
+class WebLocalFrame;
}
namespace autofill {
struct PasswordForm;
-// Tests whether the given form is a GAIA reauthentication form. The form is
-// not passed directly as WebFormElement, but by specifying its |origin| and
-// |control_elements|. This is for better performance and easier testing.
-// TODO(msramek): Move this logic to the browser.
-bool IsGaiaReauthenticationForm(
- const GURL& origin,
- const std::vector<blink::WebFormControlElement>& control_elements);
+// Tests whether the given form is a GAIA reauthentication form.
+bool IsGaiaReauthenticationForm(const blink::WebFormElement& form);
typedef std::map<
const blink::WebFormControlElement,
@@ -55,14 +53,14 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm(
// Same as CreatePasswordFormFromWebForm() but for input elements that are not
// enclosed in <form> element.
std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
- const blink::WebFrame& frame,
+ const blink::WebLocalFrame& frame,
const FieldValueAndPropertiesMaskMap* nonscript_modified_values,
const FormsPredictionsMap* form_predictions);
// Checks in a case-insensitive way if the autocomplete attribute for the given
// |element| is present and has the specified |value_in_lowercase|.
bool HasAutocompleteAttributeValue(const blink::WebInputElement& element,
- const char* value_in_lowercase);
+ base::StringPiece value_in_lowercase);
// Checks in a case-insensitive way if credit card autocomplete attributes for
// the given |element| are present.
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 699caa9cb77..3f0a4ac9dd6 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
@@ -27,7 +27,7 @@
using blink::WebFormControlElement;
using blink::WebFormElement;
-using blink::WebFrame;
+using blink::WebLocalFrame;
using blink::WebInputElement;
using blink::WebVector;
@@ -191,7 +191,7 @@ class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
FormsPredictionsMap* predictions,
bool with_user_input) {
WebFormElement form;
- LoadWebFormFromHTML(html, &form);
+ LoadWebFormFromHTML(html, &form, nullptr);
WebVector<WebFormControlElement> control_elements;
form.GetFormControlElements(control_elements);
@@ -218,7 +218,7 @@ class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
const std::map<int, PasswordFormFieldPredictionType>&
predictions_positions) {
WebFormElement form;
- LoadWebFormFromHTML(html, &form);
+ LoadWebFormFromHTML(html, &form, nullptr);
FormData form_data;
ASSERT_TRUE(form_util::WebFormElementToFormData(
@@ -239,10 +239,15 @@ class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
}
// Loads the given |html| and retrieves the sole WebFormElement from it.
- void LoadWebFormFromHTML(const std::string& html, WebFormElement* form) {
- LoadHTML(html.c_str());
-
- WebFrame* frame = GetMainFrame();
+ void LoadWebFormFromHTML(const std::string& html,
+ WebFormElement* form,
+ const char* origin) {
+ if (origin)
+ LoadHTMLWithUrlOverride(html.c_str(), origin);
+ else
+ LoadHTML(html.c_str());
+
+ WebLocalFrame* frame = GetMainFrame();
ASSERT_NE(nullptr, frame);
WebVector<WebFormElement> forms;
@@ -1354,12 +1359,6 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IsGaiaReauthFormIgnored) {
{TestCase::KeyValue("continue", "passwords.google.com:443"),
TestCase::KeyValue("rart", "")},
true},
- // Encoded URL is considered the same.
- {"https://accounts.google.com",
- {TestCase::KeyValue("continue",
- "https://passwords.%67oogle.com/settings"),
- TestCase::KeyValue("rart", "")},
- true},
// Fully qualified domain should work as well.
{"https://accounts.google.com",
{TestCase::KeyValue("continue",
@@ -1391,15 +1390,9 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IsGaiaReauthFormIgnored) {
}
std::string html = builder->ProduceHTML();
WebFormElement form;
- LoadWebFormFromHTML(html, &form);
- std::vector<WebFormControlElement> control_elements;
- WebVector<blink::WebFormControlElement> web_control_elements;
- form.GetFormControlElements(web_control_elements);
- control_elements.assign(web_control_elements.begin(),
- web_control_elements.end());
+ LoadWebFormFromHTML(html, &form, test_case.origin);
EXPECT_EQ(test_case.expected_form_is_reauth,
- IsGaiaReauthenticationForm(GURL(test_case.origin).GetOrigin(),
- control_elements));
+ IsGaiaReauthenticationForm(form));
}
}
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc
index bf50f220493..210d4f514f7 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc
@@ -4,10 +4,13 @@
#include "components/autofill/content/renderer/password_generation_agent.h"
+#include <algorithm>
#include <memory>
+#include <utility>
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/form_classifier.h"
@@ -39,24 +42,24 @@ namespace {
using Logger = autofill::SavePasswordProgressLogger;
-// Returns true if we think that this form is for account creation. |passwords|
-// is filled with the password field(s) in the form.
+// Returns true if we think that this form is for account creation. Password
+// field(s) of the form are pushed back to |passwords|.
bool GetAccountCreationPasswordFields(
const std::vector<blink::WebFormControlElement>& control_elements,
std::vector<blink::WebInputElement>* passwords) {
- for (size_t i = 0; i < control_elements.size(); i++) {
+ for (const auto& control_element : control_elements) {
const blink::WebInputElement* input_element =
- ToWebInputElement(&control_elements[i]);
- if (input_element && input_element->IsTextField()) {
- if (input_element->IsPasswordField())
- passwords->push_back(*input_element);
+ ToWebInputElement(&control_element);
+ if (input_element && input_element->IsTextField() &&
+ input_element->IsPasswordField()) {
+ passwords->push_back(*input_element);
}
}
return !passwords->empty();
}
bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
- return std::find(urls.begin(), urls.end(), url) != urls.end();
+ return base::ContainsValue(urls, url);
}
// Calculates the signature of |form| and searches it in |forms|.
@@ -71,10 +74,33 @@ const PasswordFormGenerationData* FindFormGenerationData(
return nullptr;
}
-// This function returns a vector of password fields into which Chrome should
-// fill the generated password. It assumes that |field_signature| describes the
-// field where Chrome shows the password generation prompt. It returns no more
-// than 2 elements.
+// Returns a vector of up to 2 password fields with autocomplete attribute set
+// to "new-password". These will be filled with the generated password.
+std::vector<blink::WebInputElement> FindNewPasswordElementsMarkedBySite(
+ const std::vector<blink::WebInputElement>& all_password_elements) {
+ std::vector<blink::WebInputElement> passwords;
+
+ auto is_new_password_field = [](const blink::WebInputElement& element) {
+ return HasAutocompleteAttributeValue(element, "new-password");
+ };
+
+ auto field_iter =
+ std::find_if(all_password_elements.begin(), all_password_elements.end(),
+ is_new_password_field);
+ if (field_iter != all_password_elements.end()) {
+ passwords.push_back(*field_iter++);
+ field_iter = std::find_if(field_iter, all_password_elements.end(),
+ is_new_password_field);
+ if (field_iter != all_password_elements.end())
+ passwords.push_back(*field_iter);
+ }
+
+ return passwords;
+}
+
+// Returns a vector of up to 2 password fields into which Chrome should fill the
+// generated password. It assumes that |field_signature| describes the field
+// where Chrome shows the password generation prompt.
std::vector<blink::WebInputElement> FindPasswordElementsForGeneration(
const std::vector<blink::WebInputElement>& all_password_elements,
const PasswordFormGenerationData& generation_data) {
@@ -85,11 +111,12 @@ std::vector<blink::WebInputElement> FindPasswordElementsForGeneration(
const blink::WebInputElement& input = *iter;
FieldSignature signature = CalculateFieldSignatureByNameAndType(
input.NameForAutofill().Utf16(), input.FormControlType().Utf8());
- if (signature == generation_data.field_signature)
+ if (signature == generation_data.field_signature) {
generation_field_iter = iter;
- else if (generation_data.confirmation_field_signature &&
- signature == *generation_data.confirmation_field_signature)
+ } else if (generation_data.confirmation_field_signature &&
+ signature == *generation_data.confirmation_field_signature) {
confirmation_field_iter = iter;
+ }
}
std::vector<blink::WebInputElement> passwords;
@@ -114,17 +141,12 @@ void CopyElementValueToOtherInputElements(
}
}
-bool AutocompleteAttributesSetForGeneration(const PasswordForm& form) {
- return form.username_marked_by_site && form.new_password_marked_by_site;
-}
-
} // namespace
PasswordGenerationAgent::AccountCreationFormData::AccountCreationFormData(
linked_ptr<PasswordForm> password_form,
std::vector<blink::WebInputElement> passwords)
- : form(password_form),
- password_elements(passwords) {}
+ : form(password_form), password_elements(std::move(passwords)) {}
PasswordGenerationAgent::AccountCreationFormData::AccountCreationFormData(
const AccountCreationFormData& other) = default;
@@ -152,7 +174,6 @@ PasswordGenerationAgent::PasswordGenerationAgent(
PasswordGenerationAgent::~PasswordGenerationAgent() {}
void PasswordGenerationAgent::BindRequest(
- const service_manager::BindSourceInfo& source_info,
mojom::PasswordGenerationAgentRequest request) {
binding_.Bind(std::move(request));
}
@@ -277,9 +298,8 @@ void PasswordGenerationAgent::FindPossibleGenerationForm() {
&passwords)) {
if (form_classifier_enabled_)
RunFormClassifierAndSaveVote(forms[i], *password_form);
- AccountCreationFormData ac_form_data(
- make_linked_ptr(password_form.release()), passwords);
- possible_account_creation_forms_.push_back(ac_form_data);
+ possible_account_creation_forms_.emplace_back(
+ make_linked_ptr(password_form.release()), std::move(passwords));
}
}
@@ -314,6 +334,7 @@ void PasswordGenerationAgent::FormNotBlacklisted(const PasswordForm& form) {
void PasswordGenerationAgent::GeneratedPasswordAccepted(
const base::string16& password) {
password_is_generated_ = true;
+ password_edited_ = false;
password_generation::LogPasswordGenerationEvent(
password_generation::PASSWORD_ACCEPTED);
LogMessage(Logger::STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED);
@@ -389,8 +410,11 @@ void PasswordGenerationAgent::DetermineGenerationElement() {
for (auto& possible_form_data : possible_account_creation_forms_) {
PasswordForm* possible_password_form = possible_form_data.form.get();
const PasswordFormGenerationData* generation_data = nullptr;
+
+ std::vector<blink::WebInputElement> password_elements;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kLocalHeuristicsOnlyForPasswordGeneration)) {
+ password_elements = possible_form_data.password_elements;
VLOG(2) << "Bypassing additional checks.";
} else if (!ContainsURL(not_blacklisted_password_form_origins_,
possible_password_form->origin)) {
@@ -399,28 +423,31 @@ void PasswordGenerationAgent::DetermineGenerationElement() {
} else {
generation_data = FindFormGenerationData(generation_enabled_forms_,
*possible_password_form);
- if (!generation_data) {
- if (AutocompleteAttributesSetForGeneration(*possible_password_form)) {
- LogMessage(Logger::STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE);
- password_generation::LogPasswordGenerationEvent(
- password_generation::AUTOCOMPLETE_ATTRIBUTES_ENABLED_GENERATION);
- } else {
+ if (generation_data) {
+ password_elements = FindPasswordElementsForGeneration(
+ possible_form_data.password_elements, *generation_data);
+ } else {
+ if (!possible_password_form->new_password_marked_by_site) {
LogMessage(Logger::STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL);
continue;
}
+
+ LogMessage(Logger::STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE);
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::AUTOCOMPLETE_ATTRIBUTES_ENABLED_GENERATION);
+
+ password_elements = FindNewPasswordElementsMarkedBySite(
+ possible_form_data.password_elements);
}
}
+
LogMessage(Logger::STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND);
- std::vector<blink::WebInputElement> password_elements =
- generation_data
- ? FindPasswordElementsForGeneration(
- possible_form_data.password_elements, *generation_data)
- : possible_form_data.password_elements;
if (password_elements.empty()) {
// It might be if JavaScript changes field names.
LogMessage(Logger::STRING_GENERATION_RENDERER_NO_FIELD_FOUND);
return;
}
+
generation_form_data_.reset(new AccountCreationFormData(
possible_form_data.form, std::move(password_elements)));
generation_element_ = generation_form_data_->password_elements[0];
@@ -539,6 +566,7 @@ void PasswordGenerationAgent::HidePopup() {
void PasswordGenerationAgent::PasswordNoLongerGenerated() {
// Do not treat the password as generated, either here or in the browser.
password_is_generated_ = false;
+ password_edited_ = false;
generation_element_.SetShouldRevealPassword(false);
for (blink::WebInputElement& password :
generation_form_data_->password_elements)
@@ -563,7 +591,7 @@ void PasswordGenerationAgent::UserTriggeredGeneratePassword() {
password_form = CreatePasswordFormFromWebForm(form, nullptr, nullptr);
control_elements = form_util::ExtractAutofillableElementsInForm(form);
} else {
- const blink::WebFrame& frame = *render_frame()->GetWebFrame();
+ const blink::WebLocalFrame& frame = *render_frame()->GetWebFrame();
blink::WebDocument doc = frame.GetDocument();
if (doc.IsNull())
return;
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h
index 035f98010ef..2af62823a76 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.h
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.h
@@ -19,7 +19,6 @@
#include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
#include "content/public/renderer/render_frame_observer.h"
#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
#include "url/gurl.h"
@@ -39,8 +38,7 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
PasswordAutofillAgent* password_agent);
~PasswordGenerationAgent() override;
- void BindRequest(const service_manager::BindSourceInfo& source_info,
- mojom::PasswordGenerationAgentRequest request);
+ void BindRequest(mojom::PasswordGenerationAgentRequest request);
// mojom::PasswordGenerationAgent:
void FormNotBlacklisted(const PasswordForm& form) override;
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 558b7fcff18..b80e1c6cb7e 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
@@ -24,7 +24,9 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver {
~FakeContentPasswordManagerDriver() override {}
mojom::PasswordManagerDriverPtr CreateInterfacePtrAndBind() {
- return binding_.CreateInterfacePtrAndBind();
+ mojom::PasswordManagerDriverPtr ptr;
+ binding_.Bind(mojo::MakeRequest(&ptr));
+ return ptr;
}
bool GetLogMessage(std::string* log) {
@@ -70,6 +72,8 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver {
log_ = log;
}
+ void UserModifiedPasswordField() override {}
+
void SaveGenerationFieldDetectedByClassifier(
const autofill::PasswordForm& password_form,
const base::string16& generation_field) override {}
diff --git a/chromium/components/autofill/content/renderer/test_password_generation_agent.h b/chromium/components/autofill/content/renderer/test_password_generation_agent.h
index 11aef9d06a0..6ab5509db2b 100644
--- a/chromium/components/autofill/content/renderer/test_password_generation_agent.h
+++ b/chromium/components/autofill/content/renderer/test_password_generation_agent.h
@@ -8,7 +8,6 @@
#include <vector>
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "components/autofill/content/renderer/password_generation_agent.h"
namespace autofill {
diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn
index 945549f3993..8df55338028 100644
--- a/chromium/components/autofill/core/browser/BUILD.gn
+++ b/chromium/components/autofill/core/browser/BUILD.gn
@@ -40,6 +40,10 @@ static_library("browser") {
"autofill_external_delegate.h",
"autofill_field.cc",
"autofill_field.h",
+ "autofill_handler.cc",
+ "autofill_handler.h",
+ "autofill_handler_proxy.cc",
+ "autofill_handler_proxy.h",
"autofill_ie_toolbar_import_win.cc",
"autofill_ie_toolbar_import_win.h",
"autofill_manager.cc",
@@ -52,6 +56,8 @@ static_library("browser") {
"autofill_profile.h",
"autofill_profile_comparator.cc",
"autofill_profile_comparator.h",
+ "autofill_provider.cc",
+ "autofill_provider.h",
"autofill_scanner.cc",
"autofill_scanner.h",
"autofill_type.cc",
diff --git a/chromium/components/autofill/core/browser/DEPS b/chromium/components/autofill/core/browser/DEPS
index 54761f260e5..9bde9e87d58 100644
--- a/chromium/components/autofill/core/browser/DEPS
+++ b/chromium/components/autofill/core/browser/DEPS
@@ -8,7 +8,6 @@ include_rules = [
"+components/signin/core/browser",
"+components/signin/core/common",
"+components/sync",
- "+components/ukm",
"+components/variations",
"+components/version_info",
"+components/webdata/common",
@@ -17,6 +16,7 @@ include_rules = [
"+google_apis/gaia",
"+google_apis/google_api_keys.h",
"+net",
+ "+services/metrics/public",
"+sql",
"+third_party/fips181",
"+third_party/libaddressinput", # For address i18n.
@@ -26,3 +26,15 @@ include_rules = [
"+ui/base",
"+ui/gfx",
]
+
+specific_include_rules = {
+ "autofill_manager_unittest\.cc": [
+ "+components/ukm",
+ ],
+ "autofill_metrics_unittest\.cc": [
+ "+components/ukm",
+ ],
+ "test_autofill_client\.h": [
+ "+components/ukm",
+ ],
+}
diff --git a/chromium/components/autofill/core/browser/address.cc b/chromium/components/autofill/core/browser/address.cc
index 3a44121606d..c950f69c277 100644
--- a/chromium/components/autofill/core/browser/address.cc
+++ b/chromium/components/autofill/core/browser/address.cc
@@ -9,6 +9,7 @@
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -199,8 +200,7 @@ bool Address::SetInfo(const AutofillType& type,
// There's a good chance that this formatting is not intentional, but it's
// also not obviously safe to just strip the newlines.
if (storable_type == ADDRESS_HOME_STREET_ADDRESS &&
- std::find(street_address_.begin(), street_address_.end(),
- base::string16()) != street_address_.end()) {
+ base::ContainsValue(street_address_, base::string16())) {
street_address_.clear();
return false;
}
diff --git a/chromium/components/autofill/core/browser/autofill_address_util.cc b/chromium/components/autofill/core/browser/autofill_address_util.cc
index bdeea813bf5..e5d9872d18d 100644
--- a/chromium/components/autofill/core/browser/autofill_address_util.cc
+++ b/chromium/components/autofill/core/browser/autofill_address_util.cc
@@ -171,7 +171,7 @@ void SetCountryData(const PersonalDataManager& manager,
"value", countries[i] ? countries[i]->country_code() : "separator");
country_list->Append(std::move(option_details));
}
- localized_strings->Set("autofillCountrySelectList", country_list.release());
+ localized_strings->Set("autofillCountrySelectList", std::move(country_list));
std::unique_ptr<base::ListValue> default_country_components(
new base::ListValue);
@@ -180,7 +180,7 @@ void SetCountryData(const PersonalDataManager& manager,
default_country_components.get(),
&default_country_language_code);
localized_strings->Set("autofillDefaultCountryComponents",
- default_country_components.release());
+ std::move(default_country_components));
localized_strings->SetString("autofillDefaultCountryLanguageCode",
default_country_language_code);
}
diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h
index 9084f772daa..149b6685d0f 100644
--- a/chromium/components/autofill/core/browser/autofill_client.h
+++ b/chromium/components/autofill/core/browser/autofill_client.h
@@ -199,6 +199,10 @@ class AutofillClient : public RiskDataLoader {
// Shows the explanation of http not secure warning message.
virtual void ShowHttpNotSecureExplanation() = 0;
+
+ // Whether Autofill is currently supported by the client. If false, all
+ // features of Autofill are disabled, including Autocomplete.
+ virtual bool IsAutofillSupported() = 0;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc
index 0c06eed93bf..784409dd086 100644
--- a/chromium/components/autofill/core/browser/autofill_data_util.cc
+++ b/chromium/components/autofill/core/browser/autofill_data_util.cc
@@ -58,9 +58,9 @@ const char* const name_suffixes[] = {"b.a", "ba", "d.d.s", "dds", "i", "ii",
"ma", "md", "ms", "ph.d", "phd", "sr",
"v", "vi", "vii", "viii", "x"};
-const char* const family_name_prefixes[] = {"d'", "de", "del", "der", "di",
- "la", "le", "mc", "san", "st",
- "ter", "van", "von"};
+const char* const family_name_prefixes[] = {"d'", "de", "del", "den", "der",
+ "di", "la", "le", "mc", "san",
+ "st", "ter", "van", "von"};
// The common and non-ambiguous CJK surnames (last names) that have more than
// one character.
diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc
index e667d896369..6466e071eee 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments.cc
+++ b/chromium/components/autofill/core/browser/autofill_experiments.cc
@@ -29,6 +29,8 @@ 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 kAutofillCreditCardPopupLayout{
"AutofillCreditCardPopupLayout", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillCreditCardLastUsedDateDisplay{
@@ -36,6 +38,8 @@ const base::Feature kAutofillCreditCardLastUsedDateDisplay{
const base::Feature kAutofillOfferLocalSaveIfServerCardManuallyEntered{
"AutofillOfferLocalSaveIfServerCardManuallyEntered",
base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillSuppressDisusedAddresses{
+ "AutofillSuppressDisusedAddresses", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillUpstreamRequestCvcIfMissing{
"AutofillUpstreamRequestCvcIfMissing", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillUpstreamUseAutofillProfileComparator{
@@ -59,6 +63,11 @@ const char kAutofillCreditCardLastUsedDateShowExpirationDateKey[] =
const char kAutofillUpstreamMaxMinutesSinceAutofillProfileUseKey[] =
"max_minutes_since_autofill_profile_use";
+#if defined(OS_MACOSX)
+const base::Feature kCreditCardAutofillTouchBar{
+ "CreditCardAutofillTouchBar", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // defined(OS_MACOSX)
+
namespace {
// Returns parameter value in |kAutofillCreditCardPopupLayout| feature, or 0 if
@@ -101,6 +110,10 @@ 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() {
@@ -260,4 +273,10 @@ base::TimeDelta GetMaxTimeSinceAutofillProfileUseForCardUpload() {
return base::TimeDelta();
}
+#if defined(OS_MACOSX)
+bool IsCreditCardAutofillTouchBarExperimentEnabled() {
+ return base::FeatureList::IsEnabled(kCreditCardAutofillTouchBar);
+}
+#endif // defined(OS_MACOSX)
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_experiments.h b/chromium/components/autofill/core/browser/autofill_experiments.h
index 7ed13140bbf..0ae7639e9a4 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments.h
+++ b/chromium/components/autofill/core/browser/autofill_experiments.h
@@ -9,6 +9,7 @@
#include "base/strings/string16.h"
#include "base/time/time.h"
+#include "build/build_config.h"
#include "third_party/skia/include/core/SkColor.h"
class PrefService;
@@ -27,9 +28,11 @@ struct Suggestion;
extern const base::Feature kAutofillCreditCardAssist;
extern const base::Feature kAutofillScanCardholderName;
+extern const base::Feature kAutofillCreditCardBankNameDisplay;
extern const base::Feature kAutofillCreditCardPopupLayout;
extern const base::Feature kAutofillCreditCardLastUsedDateDisplay;
extern const base::Feature kAutofillOfferLocalSaveIfServerCardManuallyEntered;
+extern const base::Feature kAutofillSuppressDisusedAddresses;
extern const base::Feature kAutofillUpstreamRequestCvcIfMissing;
extern const base::Feature kAutofillUpstreamUseAutofillProfileComparator;
extern const base::Feature kAutofillUpstreamUseNotRecentlyUsedAutofillProfile;
@@ -37,6 +40,10 @@ extern const char kCreditCardSigninPromoImpressionLimitParamKey[];
extern const char kAutofillCreditCardLastUsedDateShowExpirationDateKey[];
extern const char kAutofillUpstreamMaxMinutesSinceAutofillProfileUseKey[];
+#if defined(OS_MACOSX)
+extern const base::Feature kCreditCardAutofillTouchBar;
+#endif // defined(OS_MACOSX)
+
// Returns true if autofill should be enabled. See also
// IsInAutofillSuggestionsDisabledExperiment below.
bool IsAutofillEnabled(const PrefService* pref_service);
@@ -73,6 +80,9 @@ 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.
@@ -120,6 +130,11 @@ bool IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled();
// for card upload. Returns 0 if the experiment is not enabled.
base::TimeDelta GetMaxTimeSinceAutofillProfileUseForCardUpload();
+#if defined(OS_MACOSX)
+// Returns whether the Credit Card Autofill Touch Bar experiment is enabled.
+bool IsCreditCardAutofillTouchBarExperimentEnabled();
+#endif // defined(OS_MACOSX)
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_
diff --git a/chromium/components/autofill/core/browser/autofill_field.h b/chromium/components/autofill/core/browser/autofill_field.h
index ed74fdfe239..ebe5d0b8c80 100644
--- a/chromium/components/autofill/core/browser/autofill_field.h
+++ b/chromium/components/autofill/core/browser/autofill_field.h
@@ -96,6 +96,13 @@ class AutofillField : public FormFieldData {
return generation_type_;
}
+ void set_generated_password_changed(bool generated_password_changed) {
+ generated_password_changed_ = generated_password_changed;
+ }
+ bool generated_password_changed() const {
+ return generated_password_changed_;
+ }
+
void set_form_classifier_outcome(
AutofillUploadContents::Field::FormClassifierOutcome outcome) {
form_classifier_outcome_ = outcome;
@@ -176,6 +183,9 @@ class AutofillField : public FormFieldData {
// The type of password generation event, if it happened.
AutofillUploadContents::Field::PasswordGenerationType generation_type_;
+ // Whether the generated password was changed by user.
+ bool generated_password_changed_;
+
// The outcome of HTML parsing based form classifier.
AutofillUploadContents::Field::FormClassifierOutcome form_classifier_outcome_;
diff --git a/chromium/components/autofill/core/browser/autofill_handler.cc b/chromium/components/autofill/core/browser/autofill_handler.cc
new file mode 100644
index 00000000000..7c1424b1906
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_handler.cc
@@ -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.
+
+#include "components/autofill/core/browser/autofill_handler.h"
+
+#include "components/autofill/core/common/autofill_data_validation.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace autofill {
+
+using base::TimeTicks;
+
+AutofillHandler::AutofillHandler(AutofillDriver* driver) : driver_(driver) {}
+
+AutofillHandler::~AutofillHandler() {}
+
+bool AutofillHandler::OnWillSubmitForm(const FormData& form,
+ const TimeTicks timestamp) {
+ if (!IsValidFormData(form))
+ return false;
+
+ return OnWillSubmitFormImpl(form, timestamp);
+}
+
+void AutofillHandler::OnTextFieldDidChange(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const TimeTicks timestamp) {
+ if (!IsValidFormData(form) || !IsValidFormFieldData(field))
+ return;
+
+ gfx::RectF transformed_box =
+ driver_->TransformBoundingBoxToViewportCoordinates(bounding_box);
+
+ OnTextFieldDidChangeImpl(form, field, transformed_box, timestamp);
+}
+
+void AutofillHandler::OnQueryFormFieldAutofill(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) {
+ if (!IsValidFormData(form) || !IsValidFormFieldData(field))
+ return;
+
+ gfx::RectF transformed_box =
+ driver_->TransformBoundingBoxToViewportCoordinates(bounding_box);
+
+ OnQueryFormFieldAutofillImpl(query_id, form, field, transformed_box);
+}
+
+void AutofillHandler::OnFocusOnFormField(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) {
+ if (!IsValidFormData(form) || !IsValidFormFieldData(field))
+ return;
+
+ gfx::RectF transformed_box =
+ driver_->TransformBoundingBoxToViewportCoordinates(bounding_box);
+
+ OnFocusOnFormFieldImpl(form, field, transformed_box);
+}
+
+void AutofillHandler::SendFormDataToRenderer(
+ int query_id,
+ AutofillDriver::RendererFormDataAction action,
+ const FormData& data) {
+ driver_->SendFormDataToRenderer(query_id, action, data);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_handler.h b/chromium/components/autofill/core/browser/autofill_handler.h
new file mode 100644
index 00000000000..d8f7b678281
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_handler.h
@@ -0,0 +1,137 @@
+// 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_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/common/form_data.h"
+
+namespace gfx {
+class RectF;
+}
+
+namespace autofill {
+
+struct FormData;
+struct FormFieldData;
+
+// This class defines the interface should be implemented by autofill
+// implementation in browser side to interact with AutofillDriver.
+class AutofillHandler {
+ public:
+ enum AutofillDownloadManagerState {
+ ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
+ DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
+ };
+
+ virtual ~AutofillHandler();
+
+ // Invoked when the value of textfield is changed.
+ void OnTextFieldDidChange(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const base::TimeTicks timestamp);
+
+ // Invoked when the |form| needs to be autofilled, the |bounding_box| is
+ // a window relative value of |field|.
+ void OnQueryFormFieldAutofill(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box);
+
+ // Invoked when the specified form will be submitted, returns false if this
+ // form is not relevant for Autofill.
+ //
+ // IMPORTANT: On iOS, this method is called when the form is submitted,
+ // immediately before OnFormSubmitted() is called. Do not assume that
+ // OnWillSubmitForm() will run before the form submits.
+ // TODO(mathp): Revisit this and use a single method to track form submission.
+ //
+ // Processes the about-to-be-submitted |form|, uploading the possible field
+ // types for the submitted fields to the crowdsourcing server.
+ bool OnWillSubmitForm(const FormData& form, const base::TimeTicks timestamp);
+
+ // Invoked when |form|'s |field| has focus.
+ void OnFocusOnFormField(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box);
+
+ // Invoked when focus is no longer on form.
+ virtual void OnFocusNoLongerOnForm() = 0;
+
+ // Invoked when |form| has been filled with the value given by
+ // SendFormDataToRenderer.
+ virtual void OnDidFillAutofillFormData(const FormData& form,
+ const base::TimeTicks timestamp) = 0;
+
+ // 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 |form| has been submitted.
+ // Processes the submitted |form|, saving any new Autofill data to the user's
+ // personal profile. Returns false if this form is not relevant for Autofill.
+ virtual bool OnFormSubmitted(const FormData& form) = 0;
+
+ // Invoked when textfeild editing ended
+ virtual void OnDidEndTextFieldEditing() = 0;
+
+ // Invoked when popup window should be hidden.
+ virtual void OnHidePopup() = 0;
+
+ // Invoked when data list need to be set.
+ virtual void OnSetDataList(const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels) = 0;
+
+ // Resets cache.
+ virtual void Reset() = 0;
+
+ // Send the form |data| to renderer for the specified |action|.
+ void SendFormDataToRenderer(int query_id,
+ AutofillDriver::RendererFormDataAction action,
+ const FormData& data);
+
+ protected:
+ AutofillHandler(AutofillDriver* driver);
+
+ virtual bool OnWillSubmitFormImpl(const FormData& form,
+ const base::TimeTicks timestamp) = 0;
+
+ virtual void OnTextFieldDidChangeImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const base::TimeTicks timestamp) = 0;
+
+ virtual void OnQueryFormFieldAutofillImpl(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) = 0;
+
+ virtual void OnFocusOnFormFieldImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) = 0;
+
+ AutofillDriver* driver() { return driver_; }
+
+ private:
+ // Provides driver-level context to the shared code of the component. Must
+ // outlive this object.
+ AutofillDriver* driver_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillHandler);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_H_
diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc
new file mode 100644
index 00000000000..c36d8394247
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc
@@ -0,0 +1,79 @@
+// 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/autofill/core/browser/autofill_handler_proxy.h"
+
+#include "components/autofill/core/browser/autofill_provider.h"
+
+namespace autofill {
+
+using base::TimeTicks;
+
+AutofillHandlerProxy::AutofillHandlerProxy(AutofillDriver* driver,
+ AutofillProvider* provider)
+ : AutofillHandler(driver), provider_(provider), weak_ptr_factory_(this) {}
+
+AutofillHandlerProxy::~AutofillHandlerProxy() {}
+
+bool AutofillHandlerProxy::OnWillSubmitFormImpl(const FormData& form,
+ const TimeTicks timestamp) {
+ return provider_->OnWillSubmitForm(this, form, timestamp);
+}
+
+void AutofillHandlerProxy::OnTextFieldDidChangeImpl(
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const TimeTicks timestamp) {
+ provider_->OnTextFieldDidChange(this, form, field, bounding_box, timestamp);
+}
+
+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);
+}
+
+void AutofillHandlerProxy::OnFocusOnFormFieldImpl(
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) {
+ provider_->OnFocusOnFormField(this, form, field, bounding_box);
+}
+
+void AutofillHandlerProxy::OnFocusNoLongerOnForm() {
+ provider_->OnFocusNoLongerOnForm(this);
+}
+
+void AutofillHandlerProxy::OnDidFillAutofillFormData(
+ const FormData& form,
+ const base::TimeTicks timestamp) {
+ provider_->OnDidFillAutofillFormData(this, form, timestamp);
+}
+
+void AutofillHandlerProxy::OnDidPreviewAutofillFormData() {}
+
+void AutofillHandlerProxy::OnFormsSeen(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp) {}
+
+bool AutofillHandlerProxy::OnFormSubmitted(const FormData& form) {
+ return false;
+}
+
+void AutofillHandlerProxy::OnDidEndTextFieldEditing() {}
+
+void AutofillHandlerProxy::OnHidePopup() {}
+
+void AutofillHandlerProxy::OnSetDataList(
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels) {}
+
+void AutofillHandlerProxy::Reset() {
+ provider_->Reset(this);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.h b/chromium/components/autofill/core/browser/autofill_handler_proxy.h
new file mode 100644
index 00000000000..00ea955d6b9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.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_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_PROXY_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_PROXY_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/core/browser/autofill_handler.h"
+
+namespace autofill {
+
+class AutofillProvider;
+
+// This class forwards AutofillHandler calls to AutofillProvider.
+class AutofillHandlerProxy : public AutofillHandler {
+ public:
+ AutofillHandlerProxy(AutofillDriver* driver, AutofillProvider* provider);
+ ~AutofillHandlerProxy() override;
+
+ void OnFocusNoLongerOnForm() 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;
+
+ bool OnFormSubmitted(const FormData& form) override;
+
+ void OnDidEndTextFieldEditing() override;
+ void OnHidePopup() override;
+ void OnSetDataList(const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels) override;
+
+ void Reset() override;
+
+ base::WeakPtr<AutofillHandlerProxy> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ protected:
+ bool OnWillSubmitFormImpl(const FormData& form,
+ const base::TimeTicks timestamp) override;
+
+ void OnTextFieldDidChangeImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const base::TimeTicks timestamp) override;
+
+ void OnQueryFormFieldAutofillImpl(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) override;
+
+ void OnFocusOnFormFieldImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) override;
+
+ private:
+ AutofillProvider* provider_;
+ base::WeakPtrFactory<AutofillHandlerProxy> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillHandlerProxy);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_PROXY_H_
diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc
index ceed0aca37b..1c46141d75f 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager.cc
@@ -219,7 +219,7 @@ AutofillManager::AutofillManager(
AutofillClient* client,
const std::string& app_locale,
AutofillDownloadManagerState enable_download_manager)
- : driver_(driver),
+ : AutofillHandler(driver),
client_(client),
payments_client_(base::MakeUnique<payments::PaymentsClient>(
driver->GetURLRequestContext(),
@@ -263,8 +263,8 @@ AutofillManager::AutofillManager(
if (personal_data_ && client_)
personal_data_->OnSyncServiceInitialized(client_->GetSyncService());
- if (personal_data_ && driver_)
- personal_data_->SetURLRequestContextGetter(driver_->GetURLRequestContext());
+ if (personal_data_ && driver)
+ personal_data_->SetURLRequestContextGetter(driver->GetURLRequestContext());
}
AutofillManager::~AutofillManager() {}
@@ -369,11 +369,11 @@ bool AutofillManager::ShouldShowCreditCardSigninPromo(
}
void AutofillManager::OnFormsSeen(const std::vector<FormData>& forms,
- const TimeTicks& timestamp) {
+ const TimeTicks timestamp) {
if (!IsValidFormDataVector(forms))
return;
- if (!driver_->RendererIsAvailable())
+ if (!driver()->RendererIsAvailable())
return;
bool enabled = IsAutofillEnabled();
@@ -392,11 +392,8 @@ void AutofillManager::OnFormsSeen(const std::vector<FormData>& forms,
ParseForms(forms);
}
-bool AutofillManager::OnWillSubmitForm(const FormData& form,
- const TimeTicks& timestamp) {
- if (!IsValidFormData(form))
- return false;
-
+bool AutofillManager::OnWillSubmitFormImpl(const FormData& form,
+ const TimeTicks timestamp) {
// We will always give Autocomplete a chance to save the data.
std::unique_ptr<FormStructure> submitted_form = ValidateSubmittedForm(form);
if (!submitted_form) {
@@ -516,12 +513,10 @@ void AutofillManager::ProcessPendingFormForUpload() {
StartUploadProcess(std::move(upload_form), TimeTicks::Now(), false);
}
-void AutofillManager::OnTextFieldDidChange(const FormData& form,
- const FormFieldData& field,
- const TimeTicks& timestamp) {
- if (!IsValidFormData(form) || !IsValidFormFieldData(field))
- return;
-
+void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const TimeTicks timestamp) {
if (test_delegate_)
test_delegate_->OnTextFieldChanged();
@@ -561,16 +556,11 @@ bool AutofillManager::IsFormNonSecure(const FormData& form) const {
(form.action.is_valid() && form.action.SchemeIs("http"));
}
-void AutofillManager::OnQueryFormFieldAutofill(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box) {
- if (!IsValidFormData(form) || !IsValidFormFieldData(field))
- return;
-
- gfx::RectF transformed_box =
- driver_->TransformBoundingBoxToViewportCoordinates(bounding_box);
-
+void AutofillManager::OnQueryFormFieldAutofillImpl(
+ int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& transformed_box) {
external_delegate_->OnQuery(query_id, form, field, transformed_box);
// Need to refresh models before using the form_event_loggers.
@@ -589,7 +579,7 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
if (got_autofillable_form) {
if (autofill_field->Type().group() == CREDIT_CARD) {
is_filling_credit_card = true;
- driver_->DidInteractWithCreditCardForm();
+ driver()->DidInteractWithCreditCardForm();
credit_card_form_event_logger_->OnDidInteractWithAutofillableForm();
} else {
address_form_event_logger_->OnDidInteractWithAutofillableForm();
@@ -601,12 +591,11 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
const bool is_http_warning_enabled =
security_state::IsHttpWarningInFormEnabled();
- // TODO(rogerm): Early exit here on !driver_->RendererIsAvailable()?
+ // 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() &&
+ 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.
@@ -794,7 +783,7 @@ void AutofillManager::FillOrPreviewForm(
// NOTE: RefreshDataModels may invalidate |data_model| because it causes the
// PersonalDataManager to reload Mac address book entries. Thus it must come
// before GetProfile or GetCreditCard.
- if (!RefreshDataModels() || !driver_->RendererIsAvailable())
+ if (!RefreshDataModels() || !driver()->RendererIsAvailable())
return;
const CreditCard* credit_card = nullptr;
@@ -811,7 +800,7 @@ void AutofillManager::FillCreditCardForm(int query_id,
const CreditCard& credit_card,
const base::string16& cvc) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field) ||
- !driver_->RendererIsAvailable()) {
+ !driver()->RendererIsAvailable()) {
return;
}
@@ -823,13 +812,17 @@ void AutofillManager::OnFocusNoLongerOnForm() {
ProcessPendingFormForUpload();
}
+void AutofillManager::OnFocusOnFormFieldImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) {}
+
void AutofillManager::OnDidPreviewAutofillFormData() {
if (test_delegate_)
test_delegate_->DidPreviewFormData();
}
void AutofillManager::OnDidFillAutofillFormData(const FormData& form,
- const TimeTicks& timestamp) {
+ const TimeTicks timestamp) {
if (test_delegate_)
test_delegate_->DidFillFormData();
@@ -1023,15 +1016,17 @@ void AutofillManager::OnLoadedServerPredictions(
// Will log quality metrics for each FormStructure based on the presence of
// autocomplete attributes, if available.
- for (FormStructure* cur_form : queried_forms)
- cur_form->LogQualityMetricsBasedOnAutocomplete();
+ for (FormStructure* cur_form : queried_forms) {
+ cur_form->LogQualityMetricsBasedOnAutocomplete(
+ form_interactions_ukm_logger_.get());
+ }
// Forward form structures to the password generation manager to detect
// account creation forms.
- driver_->PropagateAutofillPredictions(queried_forms);
+ driver()->PropagateAutofillPredictions(queried_forms);
// If the corresponding flag is set, annotate forms with the predicted types.
- driver_->SendAutofillTypePredictionsToRenderer(queried_forms);
+ driver()->SendAutofillTypePredictionsToRenderer(queried_forms);
}
IdentityProvider* AutofillManager::GetIdentityProvider() {
@@ -1120,7 +1115,7 @@ void AutofillManager::OnFullCardRequestSucceeded(const CreditCard& card,
}
void AutofillManager::OnFullCardRequestFailed() {
- driver_->RendererShouldClearPreviewedForm();
+ driver()->RendererShouldClearPreviewedForm();
}
void AutofillManager::ShowUnmaskPrompt(
@@ -1170,7 +1165,8 @@ void AutofillManager::OnDidEndTextFieldEditing() {
}
bool AutofillManager::IsAutofillEnabled() const {
- return ::autofill::IsAutofillEnabled(client_->GetPrefs());
+ return ::autofill::IsAutofillEnabled(client_->GetPrefs()) &&
+ client_->IsAutofillSupported();
}
bool AutofillManager::IsCreditCardUploadEnabled() {
@@ -1180,7 +1176,7 @@ bool AutofillManager::IsCreditCardUploadEnabled() {
}
bool AutofillManager::ShouldUploadForm(const FormStructure& form) {
- return IsAutofillEnabled() && !driver_->IsIncognito() &&
+ return IsAutofillEnabled() && !driver()->IsIncognito() &&
form.ShouldBeParsed() &&
(form.active_field_count() >= kRequiredFieldsForUpload ||
(form.all_fields_are_passwords() &&
@@ -1596,7 +1592,7 @@ void AutofillManager::Reset() {
AutofillManager::AutofillManager(AutofillDriver* driver,
AutofillClient* client,
PersonalDataManager* personal_data)
- : driver_(driver),
+ : AutofillHandler(driver),
client_(client),
payments_client_(base::MakeUnique<payments::PaymentsClient>(
driver->GetURLRequestContext(),
@@ -1629,7 +1625,7 @@ AutofillManager::AutofillManager(AutofillDriver* driver,
autofill_assistant_(this),
#endif
weak_ptr_factory_(this) {
- DCHECK(driver_);
+ DCHECK(driver);
DCHECK(client_);
CountryNames::SetLocaleString(app_locale_);
}
@@ -1766,7 +1762,7 @@ void AutofillManager::FillOrPreviewDataModelForm(
if (action == AutofillDriver::FORM_DATA_ACTION_FILL)
personal_data_->RecordUseOf(data_model);
- driver_->SendFormDataToRenderer(query_id, action, result);
+ driver()->SendFormDataToRenderer(query_id, action, result);
return;
}
@@ -1837,7 +1833,7 @@ void AutofillManager::FillOrPreviewDataModelForm(
if (action == AutofillDriver::FORM_DATA_ACTION_FILL)
personal_data_->RecordUseOf(data_model);
- driver_->SendFormDataToRenderer(query_id, action, result);
+ driver()->SendFormDataToRenderer(query_id, action, result);
}
std::unique_ptr<FormStructure> AutofillManager::ValidateSubmittedForm(
@@ -1960,7 +1956,7 @@ bool AutofillManager::UpdateCachedForm(const FormData& live_form,
(*updated_form)->UpdateFromCache(*cached_form, true);
// Annotate the updated form with its predicted types.
- driver_->SendAutofillTypePredictionsToRenderer({*updated_form});
+ driver()->SendAutofillTypePredictionsToRenderer({*updated_form});
return true;
}
@@ -2004,6 +2000,14 @@ std::vector<Suggestion> AutofillManager::GetCreditCardSuggestions(
std::vector<Suggestion> suggestions =
personal_data_->GetCreditCardSuggestions(
type, SanitizeCreditCardFieldValue(field.value));
+ const std::vector<CreditCard*> cards_to_suggest =
+ personal_data_->GetCreditCardsToSuggest();
+ for (const CreditCard* credit_card : cards_to_suggest) {
+ if (!credit_card->bank_name().empty()) {
+ credit_card_form_event_logger_->SetBankNameAvailable();
+ break;
+ }
+ }
for (size_t i = 0; i < suggestions.size(); i++) {
suggestions[i].frontend_id =
MakeFrontendID(suggestions[i].backend_id, std::string());
@@ -2074,7 +2078,7 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
// For the |non_queryable_forms|, we have all the field type info we're ever
// going to get about them. For the other forms, we'll wait until we get a
// response from the server.
- driver_->SendAutofillTypePredictionsToRenderer(non_queryable_forms);
+ driver()->SendAutofillTypePredictionsToRenderer(non_queryable_forms);
}
bool AutofillManager::ParseForm(const FormData& form,
@@ -2175,8 +2179,16 @@ void AutofillManager::DeterminePossibleFieldTypesForUpload(
// profile or credit card, identify any stored types that match the value.
for (size_t i = 0; i < submitted_form->field_count(); ++i) {
AutofillField* field = submitted_form->field(i);
- ServerFieldTypeSet matching_types = field->possible_types();
+ if (!field->possible_types().empty() && field->IsEmpty()) {
+ // This is a password field in a sign-in form. Skip checking its type
+ // since |field->value| is not set.
+ DCHECK_EQ(1u, field->possible_types().size());
+ DCHECK_EQ(PASSWORD, *field->possible_types().begin());
+ continue;
+ }
+
+ ServerFieldTypeSet matching_types;
base::string16 value;
base::TrimWhitespace(field->value, base::TRIM_ALL, &value);
diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h
index a0d7514073a..f24f88559bc 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.h
+++ b/chromium/components/autofill/core/browser/autofill_manager.h
@@ -23,6 +23,7 @@
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_download_manager.h"
#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/browser/autofill_handler.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/card_unmask_delegate.h"
#include "components/autofill/core/browser/form_structure.h"
@@ -72,16 +73,12 @@ extern const int kCreditCardSigninPromoImpressionLimit;
// Manages saving and restoring the user's personal information entered into web
// forms. One per frame; owned by the AutofillDriver.
-class AutofillManager : public AutofillDownloadManager::Observer,
+class AutofillManager : public AutofillHandler,
+ public AutofillDownloadManager::Observer,
public payments::PaymentsClientDelegate,
public payments::FullCardRequest::ResultDelegate,
public payments::FullCardRequest::UIDelegate {
public:
- enum AutofillDownloadManagerState {
- ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
- DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
- };
-
// Registers our Enable/Disable Autofill pref.
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
@@ -123,10 +120,6 @@ class AutofillManager : public AutofillDownloadManager::Observer,
void DidShowSuggestions(bool is_new_popup,
const FormData& form,
const FormFieldData& field);
- void OnFocusNoLongerOnForm();
- void OnDidFillAutofillFormData(const FormData& form,
- const base::TimeTicks& timestamp);
- void OnDidPreviewAutofillFormData();
// Returns true if the value/identifier is deletable. Fills out
// |title| and |body| with relevant user-facing text.
@@ -167,25 +160,8 @@ class AutofillManager : public AutofillDownloadManager::Observer,
// Only for testing.
void SetTestDelegate(AutofillManagerTestDelegate* delegate);
- void OnFormsSeen(const std::vector<FormData>& forms,
- const base::TimeTicks& timestamp);
-
void set_app_locale(std::string app_locale) { app_locale_ = app_locale; }
- // IMPORTANT: On iOS, this method is called when the form is submitted,
- // immediately before OnFormSubmitted() is called. Do not assume that
- // OnWillSubmitForm() will run before the form submits.
- // TODO(mathp): Revisit this and use a single method to track form submission.
- //
- // Processes the about-to-be-submitted |form|, uploading the possible field
- // types for the submitted fields to the crowdsourcing server. Returns false
- // if this form is not relevant for Autofill.
- bool OnWillSubmitForm(const FormData& form, const base::TimeTicks& timestamp);
-
- // Processes the submitted |form|, saving any new Autofill data to the user's
- // personal profile. Returns false if this form is not relevant for Autofill.
- bool OnFormSubmitted(const FormData& form);
-
// Will send an upload based on the |form_structure| data and the local
// Autofill profile data. |observed_submission| is specified if the upload
// follows an observed submission event.
@@ -200,24 +176,22 @@ class AutofillManager : public AutofillDownloadManager::Observer,
// Upload the current pending form.
void ProcessPendingFormForUpload();
- void OnTextFieldDidChange(const FormData& form,
- const FormFieldData& field,
- const base::TimeTicks& timestamp);
-
- // The |bounding_box| is a window relative value.
- void OnQueryFormFieldAutofill(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box);
- void OnDidEndTextFieldEditing();
- void OnHidePopup();
+ // AutofillHandler:
+ void OnFocusNoLongerOnForm() 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;
+ bool OnFormSubmitted(const FormData& form) override;
+ void OnDidEndTextFieldEditing() override;
+ void OnHidePopup() override;
void OnSetDataList(const std::vector<base::string16>& values,
- const std::vector<base::string16>& labels);
-
- // Resets cache.
- virtual void Reset();
+ const std::vector<base::string16>& labels) override;
+ void Reset() override;
- // Returns the value of the AutofillEnabled pref.
+ // Returns true if the value of the AutofillEnabled pref is true and the
+ // client supports Autofill.
virtual bool IsAutofillEnabled() const;
// Returns true if all the conditions for enabling the upload of credit card
@@ -264,6 +238,21 @@ class AutofillManager : public AutofillDownloadManager::Observer,
std::string* cc_backend_id,
std::string* profile_backend_id) const;
+ // AutofillHandler:
+ bool OnWillSubmitFormImpl(const FormData& form,
+ const base::TimeTicks timestamp) override;
+ void OnTextFieldDidChangeImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const base::TimeTicks timestamp) override;
+ 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;
+
std::vector<std::unique_ptr<FormStructure>>* form_structures() {
return &form_structures_;
}
@@ -497,10 +486,6 @@ class AutofillManager : public AutofillDownloadManager::Observer,
// |AutofillMetrics::CardUploadDecisionMetric|.
void LogCardUploadDecisions(int upload_decision_metrics);
- // Provides driver-level context to the shared code of the component. Must
- // outlive this object.
- AutofillDriver* driver_;
-
AutofillClient* const client_;
// Handles Payments service requests.
diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
index ecf3ded51d9..10e6ba75084 100644
--- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -17,7 +17,6 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/metrics_hashes.h"
#include "base/run_loop.h"
@@ -160,7 +159,7 @@ class TestPersonalDataManager : public PersonalDataManager {
std::string SaveImportedProfile(const AutofillProfile& profile) override {
num_times_save_imported_profile_called_++;
- AddProfile(base::MakeUnique<AutofillProfile>(profile));
+ AddProfile(profile);
return profile.guid();
}
@@ -180,9 +179,11 @@ class TestPersonalDataManager : public PersonalDataManager {
return NULL;
}
- void AddProfile(std::unique_ptr<AutofillProfile> profile) {
- profile->set_modification_date(AutofillClock::Now());
- web_profiles_.push_back(std::move(profile));
+ void AddProfile(const AutofillProfile& profile) override {
+ std::unique_ptr<AutofillProfile> profile_ptr =
+ base::MakeUnique<AutofillProfile>(profile);
+ profile_ptr->set_modification_date(AutofillClock::Now());
+ web_profiles_.push_back(std::move(profile_ptr));
}
void AddCreditCard(const CreditCard& credit_card) override {
@@ -673,7 +674,7 @@ class TestAutofillManager : public AutofillManager {
}
void AddProfile(std::unique_ptr<AutofillProfile> profile) {
- personal_data_->AddProfile(std::move(profile));
+ personal_data_->AddProfile(*profile);
}
void AddCreditCard(const CreditCard& credit_card) {
@@ -1163,7 +1164,7 @@ class AutofillManagerTest : public testing::Test {
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
- ukm::TestUkmRecorder test_ukm_recorder_;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
MockAutofillClient autofill_client_;
std::unique_ptr<MockAutofillDriver> autofill_driver_;
std::unique_ptr<TestAutofillManager> autofill_manager_;
@@ -4339,7 +4340,7 @@ TEST_F(AutofillManagerTest, OnTextFieldDidChangeAndUnfocus_Upload) {
form.fields[2].value = ASCIIToUTF16("theking@gmail.com");
// Simulate editing a field.
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- base::TimeTicks::Now());
+ gfx::RectF(), base::TimeTicks::Now());
autofill_manager_->ResetRunLoop();
// Simulate lost of focus on the form.
@@ -4392,7 +4393,7 @@ TEST_F(AutofillManagerTest, OnTextFieldDidChangeAndNavigation_Upload) {
form.fields[2].value = ASCIIToUTF16("theking@gmail.com");
// Simulate editing a field.
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- base::TimeTicks::Now());
+ gfx::RectF(), base::TimeTicks::Now());
autofill_manager_->ResetRunLoop();
// Simulate a navigation so that the pending form is uploaded.
@@ -7060,7 +7061,7 @@ TEST_F(AutofillManagerTest, SignInFormSubmission_Upload) {
types.insert(EMAIL_ADDRESS);
expected_types.push_back(types);
- test::CreateTestFormField("Password", "pw", "secret", "password", &field);
+ test::CreateTestFormField("Password", "pw", "", "password", &field);
form.fields.push_back(field);
FormsSeen(std::vector<FormData>(1, form));
types.clear();
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc
index 3039a0832fd..792546cc593 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics.cc
@@ -19,8 +19,9 @@
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/form_data.h"
-#include "components/ukm/public/ukm_entry_builder.h"
+#include "services/metrics/public/cpp/ukm_entry_builder.h"
namespace internal {
const char kUKMCardUploadDecisionEntryName[] = "Autofill.CardUploadDecision";
@@ -49,6 +50,18 @@ const char kUKMIsEmptyMetricName[] = "IsEmpty";
const char kUKMFormSubmittedEntryName[] = "Autofill.AutofillFormSubmitted";
const char kUKMAutofillFormSubmittedStateMetricName[] =
"AutofillFormSubmittedState";
+// |UkmEntry| for capturing field type prediction quality.
+const char kUKMFieldTypeEntryName[] = "Autofill.FieldTypeValidation";
+const char kUKMFieldFillStatusEntryName[] = "Autofill.FieldFillStatus";
+const char kUKMFormSignatureMetricName[] = "FormSignature";
+const char kUKMFieldSignatureMetricName[] = "FieldSignature";
+const char kUKMValidationEventMetricName[] = "ValidationEvent";
+const char kUKMPredictionSourceMetricName[] = "PredictionSource";
+const char kUKMPredictedTypeMetricName[] = "PredictedType";
+const char kUKMActualTypeMetricName[] = "ActualType";
+const char kUKMWasSuggestionShownMetricName[] = "WasSuggestionShown";
+const char kUKMWasPreviouslyAutofilledMetricName[] = "WasPreviouslyAutofilled";
+
} // namespace internal
namespace autofill {
@@ -277,6 +290,23 @@ void LogUMAHistogramLongTimes(const std::string& name,
histogram->AddTime(duration);
}
+const char* GetQualityMetricPredictionSource(
+ AutofillMetrics::QualityMetricPredictionSource source) {
+ switch (source) {
+ default:
+ case AutofillMetrics::PREDICTION_SOURCE_UNKNOWN:
+ NOTREACHED();
+ return "Unknown";
+
+ case AutofillMetrics::PREDICTION_SOURCE_HEURISTIC:
+ return "Heuristic";
+ case AutofillMetrics::PREDICTION_SOURCE_SERVER:
+ return "Server";
+ case AutofillMetrics::PREDICTION_SOURCE_OVERALL:
+ return "Overall";
+ }
+}
+
const char* GetQualityMetricTypeSuffix(
AutofillMetrics::QualityMetricType metric_type) {
switch (metric_type) {
@@ -339,17 +369,19 @@ ServerFieldType GetActualFieldType(const ServerFieldTypeSet& possible_types,
}
// Logs field type prediction quality metrics. The primary histogram name is
-// constructed based on |source| The field-specific histogram name also factors
-// possible and predicted field types (|possible_types| and |predicted_type|,
-// respectively). May log a suffixed version of the metric depending on
-// |metric_type|.
+// constructed based on |prediction_source| The field-specific histogram names
+// also incorporates the possible and predicted types for |field|. A suffix may
+// be appended to the metric name, depending on |metric_type|.
void LogPredictionQualityMetrics(
- const base::StringPiece& source,
- const ServerFieldTypeSet& possible_types,
+ AutofillMetrics::QualityMetricPredictionSource prediction_source,
ServerFieldType predicted_type,
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormStructure& form,
+ const AutofillField& field,
AutofillMetrics::QualityMetricType metric_type) {
// Generate histogram names.
- const char* const suffix = GetQualityMetricTypeSuffix(metric_type);
+ const char* source = GetQualityMetricPredictionSource(prediction_source);
+ const char* suffix = GetQualityMetricTypeSuffix(metric_type);
std::string raw_data_histogram =
base::JoinString({"Autofill.FieldPrediction.", source, suffix}, "");
std::string aggregate_histogram = base::JoinString(
@@ -357,6 +389,13 @@ void LogPredictionQualityMetrics(
std::string type_specific_histogram = base::JoinString(
{"Autofill.FieldPredictionQuality.ByFieldType.", source, suffix}, "");
+ const ServerFieldTypeSet& possible_types =
+ metric_type == AutofillMetrics::TYPE_AUTOCOMPLETE_BASED
+ ? ServerFieldTypeSet{AutofillType(field.html_type(),
+ field.html_mode())
+ .GetStorableType()}
+ : field.possible_types();
+
// Get the best type classification we can for the field.
ServerFieldType actual_type =
GetActualFieldType(possible_types, predicted_type);
@@ -369,6 +408,10 @@ void LogPredictionQualityMetrics(
UMA_HISTOGRAM_SPARSE_SLOWLY(raw_data_histogram,
(predicted_type << 16) | actual_type);
+ form_interactions_ukm_logger->LogFieldType(
+ form.form_signature(), field.GetFieldSignature(), prediction_source,
+ metric_type, predicted_type, actual_type);
+
// NO_SERVER_DATA is the equivalent of predicting UNKNOWN.
if (predicted_type == NO_SERVER_DATA)
predicted_type = UNKNOWN_TYPE;
@@ -470,6 +513,15 @@ void LogPredictionQualityMetrics(
} // namespace
// static
+void AutofillMetrics::LogSubmittedServerCardExpirationStatusMetric(
+ SubmittedServerCardExpirationStatusMetric metric) {
+ DCHECK_LT(metric, NUM_SUBMITTED_SERVER_CARD_EXPIRATION_STATUS_METRICS);
+ UMA_HISTOGRAM_ENUMERATION(
+ "Autofill.SubmittedServerCardExpirationStatus", metric,
+ NUM_SUBMITTED_SERVER_CARD_EXPIRATION_STATUS_METRICS);
+}
+
+// static
void AutofillMetrics::LogCardUploadDecisionMetrics(
int upload_decision_metrics) {
DCHECK(upload_decision_metrics);
@@ -664,27 +716,35 @@ void AutofillMetrics::LogDeveloperEngagementMetric(
}
void AutofillMetrics::LogHeuristicPredictionQualityMetrics(
- const ServerFieldTypeSet& possible_types,
- ServerFieldType predicted_type,
- AutofillMetrics::QualityMetricType metric_type) {
- LogPredictionQualityMetrics("Heuristic", possible_types, predicted_type,
- metric_type);
+ FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormStructure& form,
+ const AutofillField& field,
+ QualityMetricType metric_type) {
+ LogPredictionQualityMetrics(
+ PREDICTION_SOURCE_HEURISTIC,
+ AutofillType(field.heuristic_type()).GetStorableType(),
+ form_interactions_ukm_logger, form, field, metric_type);
}
void AutofillMetrics::LogServerPredictionQualityMetrics(
- const ServerFieldTypeSet& possible_types,
- ServerFieldType predicted_type,
- AutofillMetrics::QualityMetricType metric_type) {
- LogPredictionQualityMetrics("Server", possible_types, predicted_type,
- metric_type);
+ FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormStructure& form,
+ const AutofillField& field,
+ QualityMetricType metric_type) {
+ LogPredictionQualityMetrics(
+ PREDICTION_SOURCE_SERVER,
+ AutofillType(field.server_type()).GetStorableType(),
+ form_interactions_ukm_logger, form, field, metric_type);
}
void AutofillMetrics::LogOverallPredictionQualityMetrics(
- const ServerFieldTypeSet& possible_types,
- ServerFieldType predicted_type,
- AutofillMetrics::QualityMetricType metric_type) {
- LogPredictionQualityMetrics("Overall", possible_types, predicted_type,
- metric_type);
+ FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormStructure& form,
+ const AutofillField& field,
+ QualityMetricType metric_type) {
+ LogPredictionQualityMetrics(
+ PREDICTION_SOURCE_OVERALL, field.Type().GetStorableType(),
+ form_interactions_ukm_logger, form, field, metric_type);
}
// static
@@ -759,6 +819,16 @@ void AutofillMetrics::LogStoredProfileCount(size_t num_profiles) {
}
// static
+void AutofillMetrics::LogStoredProfileDisusedCount(size_t num_profiles) {
+ UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredProfileDisusedCount", num_profiles);
+}
+
+// static
+void AutofillMetrics::LogStoredProfileDaysSinceLastUse(size_t days) {
+ UMA_HISTOGRAM_COUNTS_1000("Autofill.DaysSinceLastUse.StoredProfile", days);
+}
+
+// static
void AutofillMetrics::LogStoredLocalCreditCardCount(size_t num_local_cards) {
UMA_HISTOGRAM_COUNTS("Autofill.StoredLocalCreditCardCount", num_local_cards);
}
@@ -788,6 +858,13 @@ void AutofillMetrics::LogHasModifiedProfileOnCreditCardFormSubmission(
}
// static
+void AutofillMetrics::LogNumberOfAddressesSuppressedForDisuse(
+ size_t num_profiles) {
+ UMA_HISTOGRAM_COUNTS_1000("Autofill.AddressesSuppressedForDisuse",
+ num_profiles);
+}
+
+// static
void AutofillMetrics::LogAddressSuggestionsCount(size_t num_suggestions) {
UMA_HISTOGRAM_COUNTS("Autofill.AddressSuggestionsCount", num_suggestions);
}
@@ -985,6 +1062,7 @@ AutofillMetrics::FormEventLogger::FormEventLogger(
has_logged_suggestion_filled_(false),
has_logged_will_submit_(false),
has_logged_submitted_(false),
+ has_logged_bank_name_available_(false),
logged_suggestion_filled_was_server_data_(false),
logged_suggestion_filled_was_masked_server_card_(false),
form_interactions_ukm_logger_(form_interactions_ukm_logger) {}
@@ -1026,6 +1104,10 @@ void AutofillMetrics::FormEventLogger::OnDidShowSuggestions(
if (!has_logged_suggestions_shown_) {
has_logged_suggestions_shown_ = true;
Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE);
+ if (has_logged_bank_name_available_) {
+ Log(AutofillMetrics::
+ FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE);
+ }
}
if (is_for_credit_card_) {
@@ -1074,6 +1156,10 @@ void AutofillMetrics::FormEventLogger::OnDidFillSuggestion(
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE);
} else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) {
Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE);
+ if (has_logged_bank_name_available_) {
+ Log(AutofillMetrics::
+ FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE);
+ }
} else {
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE);
}
@@ -1161,6 +1247,10 @@ void AutofillMetrics::FormEventLogger::OnFormSubmitted() {
}
}
+void AutofillMetrics::FormEventLogger::SetBankNameAvailable() {
+ has_logged_bank_name_available_ = true;
+}
+
void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
DCHECK_LT(event, NUM_FORM_EVENTS);
std::string name("Autofill.FormEvents.");
@@ -1192,6 +1282,13 @@ void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
LogUMAHistogramEnumeration(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);
+}
+
AutofillMetrics::FormInteractionsUkmLogger::FormInteractionsUkmLogger(
ukm::UkmRecorder* ukm_recorder)
: ukm_recorder_(ukm_recorder) {}
@@ -1304,6 +1401,65 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogTextFieldDidChange(
MillisecondsSinceFormParsed());
}
+void AutofillMetrics::FormInteractionsUkmLogger::LogFieldFillStatus(
+ const FormStructure& form,
+ const AutofillField& field,
+ QualityMetricType metric_type) {
+ if (!CanLog())
+ return;
+
+ if (source_id_ == -1)
+ GetNewSourceID();
+
+ std::unique_ptr<ukm::UkmEntryBuilder> builder =
+ ukm_recorder_->GetEntryBuilder(source_id_,
+ internal::kUKMFieldFillStatusEntryName);
+ builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
+ MillisecondsSinceFormParsed());
+ builder->AddMetric(internal::kUKMFormSignatureMetricName,
+ static_cast<int64_t>(form.form_signature()));
+ builder->AddMetric(internal::kUKMFieldSignatureMetricName,
+ static_cast<int64_t>(field.GetFieldSignature()));
+ builder->AddMetric(internal::kUKMValidationEventMetricName,
+ static_cast<int64_t>(metric_type));
+ builder->AddMetric(internal::kUKMIsAutofilledMetricName,
+ static_cast<int64_t>(field.is_autofilled));
+ builder->AddMetric(internal::kUKMWasPreviouslyAutofilledMetricName,
+ static_cast<int64_t>(field.previously_autofilled()));
+}
+
+void AutofillMetrics::FormInteractionsUkmLogger::LogFieldType(
+ FormSignature form_signature,
+ FieldSignature field_signature,
+ QualityMetricPredictionSource prediction_source,
+ QualityMetricType metric_type,
+ ServerFieldType predicted_type,
+ ServerFieldType actual_type) {
+ if (!CanLog())
+ return;
+
+ if (source_id_ == -1)
+ GetNewSourceID();
+
+ std::unique_ptr<ukm::UkmEntryBuilder> builder =
+ ukm_recorder_->GetEntryBuilder(source_id_,
+ internal::kUKMFieldTypeEntryName);
+ builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
+ MillisecondsSinceFormParsed());
+ builder->AddMetric(internal::kUKMFormSignatureMetricName,
+ static_cast<int64_t>(form_signature));
+ builder->AddMetric(internal::kUKMFieldSignatureMetricName,
+ static_cast<int64_t>(field_signature));
+ builder->AddMetric(internal::kUKMValidationEventMetricName,
+ static_cast<int64_t>(metric_type));
+ builder->AddMetric(internal::kUKMPredictionSourceMetricName,
+ static_cast<int64_t>(prediction_source));
+ builder->AddMetric(internal::kUKMPredictedTypeMetricName,
+ static_cast<int64_t>(predicted_type));
+ builder->AddMetric(internal::kUKMActualTypeMetricName,
+ static_cast<int64_t>(actual_type));
+}
+
void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted(
AutofillFormSubmittedState state) {
if (!CanLog())
@@ -1341,7 +1497,10 @@ int64_t
AutofillMetrics::FormInteractionsUkmLogger::MillisecondsSinceFormParsed()
const {
DCHECK(!form_parsed_timestamp_.is_null());
- return (base::TimeTicks::Now() - form_parsed_timestamp_).InMilliseconds();
+ // Use the pinned timestamp as the current time if it's set.
+ base::TimeTicks now =
+ pinned_timestamp_.is_null() ? base::TimeTicks::Now() : pinned_timestamp_;
+ return (now - form_parsed_timestamp_).InMilliseconds();
}
void AutofillMetrics::FormInteractionsUkmLogger::GetNewSourceID() {
@@ -1349,4 +1508,17 @@ void AutofillMetrics::FormInteractionsUkmLogger::GetNewSourceID() {
ukm_recorder_->UpdateSourceURL(source_id_, url_);
}
+AutofillMetrics::UkmTimestampPin::UkmTimestampPin(
+ FormInteractionsUkmLogger* logger)
+ : logger_(logger) {
+ DCHECK(logger_);
+ DCHECK(!logger_->has_pinned_timestamp());
+ logger_->set_pinned_timestamp(base::TimeTicks::Now());
+}
+
+AutofillMetrics::UkmTimestampPin::~UkmTimestampPin() {
+ DCHECK(logger_->has_pinned_timestamp());
+ logger_->set_pinned_timestamp(base::TimeTicks());
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h
index d849ce143f8..ee4526fbfff 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.h
+++ b/chromium/components/autofill/core/browser/autofill_metrics.h
@@ -11,7 +11,6 @@
#include <vector>
#include "base/macros.h"
-#include "base/strings/string_piece_forward.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_profile.h"
@@ -19,7 +18,8 @@
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/autofill/core/common/form_field_data.h"
-#include "components/ukm/public/ukm_recorder.h"
+#include "components/autofill/core/common/signatures_util.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
namespace internal {
// Name constants are exposed here so they can be referenced from tests.
@@ -69,6 +69,19 @@ extern const char kUKMIsEmptyMetricName[];
// |UkmEntry| for |AutofillFormSubmittedState|.
extern const char kUKMFormSubmittedEntryName[];
extern const char kUKMAutofillFormSubmittedStateMetricName[];
+
+// |UkmEntry| for capturing field fill status and type prediction quality.
+extern const char kUKMFieldTypeEntryName[];
+extern const char kUKMFieldFillStatusEntryName[];
+extern const char kUKMFormSignatureMetricName[];
+extern const char kUKMFieldSignatureMetricName[];
+extern const char kUKMValidationEventMetricName[];
+extern const char kUKMPredictionSourceMetricName[];
+extern const char kUKMPredictedTypeMetricName[];
+extern const char kUKMActualTypeMetricName[];
+extern const char kUKMWasSuggestionShownMetricName[];
+extern const char kUKMWasPreviouslyAutofilledMetricName[];
+
} // namespace internal
namespace autofill {
@@ -283,9 +296,32 @@ class AutofillMetrics {
INFOBAR_DENIED, // The user explicitly denied the infobar.
INFOBAR_IGNORED, // The user completely ignored the infobar (logged on
// tab close).
+ INFOBAR_NOT_SHOWN_INVALID_LEGAL_MESSAGE, // We didn't show the infobar
+ // because the provided legal
+ // message was invalid.
NUM_INFO_BAR_METRICS,
};
+ // Metric to measure if a submitted card's expiration date matches the same
+ // server card's expiration date (unmasked or not). Cards are considered to
+ // be the same if they have the same card number (if unmasked) or if they have
+ // the same network and last four digits (if masked).
+ enum SubmittedServerCardExpirationStatusMetric {
+ // The submitted card and the unmasked server card had the same expiration
+ // date.
+ FULL_SERVER_CARD_EXPIRATION_DATE_MATCHED,
+ // The submitted card and the unmasked server card had different expiration
+ // dates.
+ FULL_SERVER_CARD_EXPIRATION_DATE_DID_NOT_MATCH,
+ // The submitted card and the masked server card had the same expiration
+ // date.
+ MASKED_SERVER_CARD_EXPIRATION_DATE_MATCHED,
+ // The submitted card and the masked server card had different expiration
+ // dates.
+ MASKED_SERVER_CARD_EXPIRATION_DATE_DID_NOT_MATCH,
+ NUM_SUBMITTED_SERVER_CARD_EXPIRATION_STATUS_METRICS,
+ };
+
// Metrics to measure user interaction with the save credit card prompt.
//
// SAVE_CARD_PROMPT_DISMISS_FOCUS is not stored explicitly, but can be
@@ -408,6 +444,14 @@ class AutofillMetrics {
NUM_FIELD_TYPE_QUALITY_METRICS
};
+ enum QualityMetricPredictionSource {
+ PREDICTION_SOURCE_UNKNOWN, // Not used. The prediction source is unknown.
+ PREDICTION_SOURCE_HEURISTIC, // Local heuristic field-type prediction.
+ PREDICTION_SOURCE_SERVER, // Crowd-sourced server field type prediction.
+ PREDICTION_SOURCE_OVERALL, // Overall field-type prediction seen by user.
+ NUM_QUALITY_METRIC_SOURCES
+ };
+
enum QualityMetricType {
TYPE_SUBMISSION = 0, // Logged based on user's submitted data.
TYPE_NO_SUBMISSION, // Logged based on user's entered data.
@@ -542,10 +586,20 @@ class AutofillMetrics {
// submitted. If the submission is not interrupted by JavaScript, the "form
// submitted" event above will also be logged.
FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE,
-
NUM_FORM_EVENTS,
};
+ // Form Events for autofill with bank name available for display.
+ enum BankNameDisplayedFormEvent {
+ // A dropdown with suggestions was shown and at least one suggestion has a
+ // bank name. Logged at most once per page load.
+ FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE = 0,
+ // A server suggestion was used to fill the form and at least one suggestion
+ // has a bank name. Logged at most once per page load.
+ FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE,
+ BANK_NAME_NUM_FORM_EVENTS,
+ };
+
// Events related to the Unmask Credit Card Prompt.
enum UnmaskPromptEvent {
// The prompt was shown.
@@ -684,6 +738,9 @@ class AutofillMetrics {
public:
explicit FormInteractionsUkmLogger(ukm::UkmRecorder* ukm_recorder);
+ bool has_pinned_timestamp() const { return !pinned_timestamp_.is_null(); }
+ void set_pinned_timestamp(base::TimeTicks t) { pinned_timestamp_ = t; }
+
const GURL& url() const { return url_; }
void OnFormsParsed(const GURL& url);
@@ -694,6 +751,15 @@ class AutofillMetrics {
void LogSelectedMaskedServerCard();
void LogDidFillSuggestion(int record_type);
void LogTextFieldDidChange(const AutofillField& field);
+ void LogFieldFillStatus(const FormStructure& form,
+ const AutofillField& field,
+ QualityMetricType metric_type);
+ void LogFieldType(FormSignature form_signature,
+ FieldSignature field_signature,
+ QualityMetricPredictionSource prediction_source,
+ QualityMetricType metric_type,
+ ServerFieldType predicted_type,
+ ServerFieldType actual_type);
void LogFormSubmitted(AutofillFormSubmittedState state);
// We initialize |url_| with the form's URL when we log the first form
@@ -710,8 +776,28 @@ class AutofillMetrics {
ukm::SourceId source_id_ = -1;
GURL url_;
base::TimeTicks form_parsed_timestamp_;
+ base::TimeTicks pinned_timestamp_;
};
+ // Utility class to pin the timestamp used by the FormInteractionsUkmLogger
+ // while an instance of this class is in scope. Pinned timestamps cannot be
+ // nested.
+ class UkmTimestampPin {
+ public:
+ UkmTimestampPin(FormInteractionsUkmLogger* logger);
+ ~UkmTimestampPin();
+
+ private:
+ FormInteractionsUkmLogger* const logger_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UkmTimestampPin);
+ };
+
+ // If a credit card that matches a server card (unmasked or not) was submitted
+ // on a form, logs whether the submitted card's expiration date matched the
+ // server card's known expiration date.
+ static void LogSubmittedServerCardExpirationStatusMetric(
+ SubmittedServerCardExpirationStatusMetric metric);
+
// |upload_decision_metrics| is a bitmask of |CardUploadDecisionMetric|.
static void LogCardUploadDecisionMetrics(int upload_decision_metrics);
static void LogCreditCardInfoBarMetric(
@@ -736,16 +822,19 @@ class AutofillMetrics {
static void LogDeveloperEngagementMetric(DeveloperEngagementMetric metric);
static void LogHeuristicPredictionQualityMetrics(
- const ServerFieldTypeSet& possible_types,
- ServerFieldType predicted_type,
+ FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormStructure& form,
+ const AutofillField& field,
QualityMetricType metric_type);
static void LogServerPredictionQualityMetrics(
- const ServerFieldTypeSet& possible_types,
- ServerFieldType predicted_type,
+ FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormStructure& form,
+ const AutofillField& field,
QualityMetricType metric_type);
static void LogOverallPredictionQualityMetrics(
- const ServerFieldTypeSet& possible_types,
- ServerFieldType predicted_type,
+ FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormStructure& form,
+ const AutofillField& field,
QualityMetricType metric_type);
static void LogServerQueryMetric(ServerQueryMetric metric);
@@ -802,16 +891,25 @@ class AutofillMetrics {
// This should be called each time a page containing forms is loaded.
static void LogIsAutofillEnabledAtPageLoad(bool enabled);
- // This should be called each time a new profile is launched.
+ // This should be called each time a new chrome profile is launched.
static void LogIsAutofillEnabledAtStartup(bool enabled);
- // This should be called each time a new profile is launched.
+ // Records the number of stored address profiles. This is be called each time
+ // a new chrome profile is launched.
static void LogStoredProfileCount(size_t num_profiles);
- // This should be called each time a new profile is launched.
+ // Records the number of stored address profiles which have not been used in
+ // a long time. This is be called each time a new chrome profile is launched.
+ static void LogStoredProfileDisusedCount(size_t num_profiles);
+
+ // Records the number of days since an address profile was last used. This is
+ // called once per address profile each time a new chrome profile is launched.
+ static void LogStoredProfileDaysSinceLastUse(size_t days);
+
+ // This should be called each time a new chrome profile is launched.
static void LogStoredLocalCreditCardCount(size_t num_local_cards);
- // This should be called each time a new profile is launched.
+ // This should be called each time a new chrome profile is launched.
static void LogStoredServerCreditCardCounts(size_t num_masked_cards,
size_t num_unmasked_cards);
@@ -825,8 +923,15 @@ class AutofillMetrics {
static void LogHasModifiedProfileOnCreditCardFormSubmission(
bool has_modified_profile);
- // Log the number of Autofill suggestions presented to the user when filling a
- // form.
+ // Log the number of autofill address suggestions suppressed because they have
+ // not been used for a long time. Note that these addresses are only
+ // suppressed when the user has not typed any data into the field from which
+ // autofill is triggered. Addresses matching something the user has types are
+ // always offered, regardless of how recently they have been used.
+ static void LogNumberOfAddressesSuppressedForDisuse(size_t num_profiles);
+
+ // Log the number of Autofill address suggestions presented to the user when
+ // filling a form.
static void LogAddressSuggestionsCount(size_t num_suggestions);
// Log the index of the selected Autofill suggestion in the popup.
@@ -937,8 +1042,11 @@ class AutofillMetrics {
void OnFormSubmitted();
+ void SetBankNameAvailable();
+
private:
void Log(FormEvent event) const;
+ void Log(BankNameDisplayedFormEvent event) const;
bool is_for_credit_card_;
size_t server_record_type_count_;
@@ -950,6 +1058,7 @@ class AutofillMetrics {
bool has_logged_suggestion_filled_;
bool has_logged_will_submit_;
bool has_logged_submitted_;
+ bool has_logged_bank_name_available_;
bool logged_suggestion_filled_was_server_data_;
bool logged_suggestion_filled_was_masked_server_card_;
diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
index 65c3b283f59..7fe99a29c6e 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -59,6 +59,9 @@ using ::testing::UnorderedPointwise;
namespace autofill {
namespace {
+using ExpectedUkmMetrics =
+ std::vector<std::vector<std::pair<const char*, int64_t>>>;
+
class TestPersonalDataManager : public PersonalDataManager {
public:
TestPersonalDataManager()
@@ -187,6 +190,20 @@ class TestPersonalDataManager : public PersonalDataManager {
Refresh();
}
+ // Removes all existing credit cards and creates 1 server card with a bank
+ // name.
+ void RecreateServerCreditCardsWithBankName() {
+ server_credit_cards_.clear();
+ std::unique_ptr<CreditCard> credit_card =
+ base::MakeUnique<CreditCard>(CreditCard::FULL_SERVER_CARD, "server_id");
+ test::SetCreditCardInfo(credit_card.get(), "name", "4111111111111111", "12",
+ "24", "1");
+ credit_card->set_guid("10000000-0000-0000-0000-000000000003");
+ credit_card->set_bank_name("Chase");
+ server_credit_cards_.push_back(std::move(credit_card));
+ Refresh();
+ }
+
bool IsAutofillEnabled() const override { return autofill_enabled_; }
void CreateAmbiguousProfiles() {
@@ -329,14 +346,14 @@ MATCHER(CompareMetrics, "") {
}
void VerifyDeveloperEngagementUkm(
+ const ukm::TestAutoSetUkmRecorder& ukm_recorder,
const FormData& form,
- const ukm::TestUkmRecorder* ukm_recorder,
const std::vector<int64_t>& expected_metric_values) {
- const ukm::mojom::UkmEntry* entry = ukm_recorder->GetEntryForEntryName(
+ const ukm::mojom::UkmEntry* entry = ukm_recorder.GetEntryForEntryName(
internal::kUKMDeveloperEngagementEntryName);
ASSERT_NE(nullptr, entry);
const ukm::UkmSource* source =
- ukm_recorder->GetSourceForSourceId(entry->source_id);
+ ukm_recorder.GetSourceForSourceId(entry->source_id);
ASSERT_NE(nullptr, source);
EXPECT_EQ(form.origin, source->url());
@@ -360,20 +377,18 @@ MATCHER(CompareMetricsIgnoringMillisecondsSinceFormParsed, "") {
rhs.first == internal::kUKMMillisecondsSinceFormParsedMetricName));
}
-void VerifyFormInteractionUkm(
- const FormData& form,
- const ukm::TestUkmRecorder* ukm_recorder,
- const char* event_name,
- const std::vector<std::vector<std::pair<const char*, int64_t>>>&
- expected_metrics) {
+void VerifyFormInteractionUkm(const ukm::TestAutoSetUkmRecorder& ukm_recorder,
+ const FormData& form,
+ const char* event_name,
+ const ExpectedUkmMetrics& expected_metrics) {
size_t expected_metrics_index = 0;
- for (size_t i = 0; i < ukm_recorder->entries_count(); ++i) {
- const ukm::mojom::UkmEntry* entry = ukm_recorder->GetEntry(i);
+ for (size_t i = 0; i < ukm_recorder.entries_count(); ++i) {
+ const ukm::mojom::UkmEntry* entry = ukm_recorder.GetEntry(i);
if (entry->event_hash != base::HashMetricName(event_name))
continue;
const ukm::UkmSource* source =
- ukm_recorder->GetSourceForSourceId(entry->source_id);
+ ukm_recorder.GetSourceForSourceId(entry->source_id);
ASSERT_NE(nullptr, source);
EXPECT_EQ(form.origin, source->url());
@@ -385,15 +400,68 @@ void VerifyFormInteractionUkm(
}
}
-void VerifySubmitFormUkm(const FormData& form,
- const ukm::TestUkmRecorder* ukm_recorder,
+void VerifySubmitFormUkm(const ukm::TestAutoSetUkmRecorder& ukm_recorder,
+ const FormData& form,
AutofillMetrics::AutofillFormSubmittedState state) {
VerifyFormInteractionUkm(
- form, ukm_recorder, internal::kUKMFormSubmittedEntryName,
+ ukm_recorder, form, internal::kUKMFormSubmittedEntryName,
{{{internal::kUKMAutofillFormSubmittedStateMetricName, state},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
}
+void AppendFieldFillStatusUkm(const FormData& form,
+ ExpectedUkmMetrics* expected_metrics) {
+ int64_t form_signature = static_cast<int64_t>(CalculateFormSignature(form));
+ int64_t metric_type = static_cast<int64_t>(AutofillMetrics::TYPE_SUBMISSION);
+ for (const FormFieldData& field : form.fields) {
+ int64_t field_signature =
+ static_cast<int64_t>(CalculateFieldSignatureForField(field));
+ expected_metrics->push_back(
+ {{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
+ {internal::kUKMFormSignatureMetricName, form_signature},
+ {internal::kUKMFieldSignatureMetricName, field_signature},
+ {internal::kUKMValidationEventMetricName, metric_type},
+ {internal::kUKMIsAutofilledMetricName, field.is_autofilled ? 1 : 0},
+ {internal::kUKMWasPreviouslyAutofilledMetricName, 0}});
+ }
+}
+
+void AppendFieldTypeUkm(const FormData& form,
+ const std::vector<ServerFieldType>& heuristic_types,
+ const std::vector<ServerFieldType>& server_types,
+ const std::vector<ServerFieldType>& actual_types,
+ ExpectedUkmMetrics* expected_metrics) {
+ ASSERT_EQ(heuristic_types.size(), form.fields.size());
+ ASSERT_EQ(server_types.size(), form.fields.size());
+ ASSERT_EQ(actual_types.size(), form.fields.size());
+ int64_t form_signature = static_cast<int64_t>(CalculateFormSignature(form));
+ int64_t metric_type = static_cast<int64_t>(AutofillMetrics::TYPE_SUBMISSION);
+ std::vector<int64_t> prediction_sources{
+ AutofillMetrics::PREDICTION_SOURCE_HEURISTIC,
+ AutofillMetrics::PREDICTION_SOURCE_SERVER,
+ AutofillMetrics::PREDICTION_SOURCE_OVERALL};
+ for (size_t i = 0; i < form.fields.size(); ++i) {
+ const FormFieldData& field = form.fields[i];
+ int64_t field_signature =
+ static_cast<int64_t>(CalculateFieldSignatureForField(field));
+ for (int64_t source : prediction_sources) {
+ int64_t predicted_type = static_cast<int64_t>(
+ (source == AutofillMetrics::PREDICTION_SOURCE_SERVER
+ ? server_types
+ : heuristic_types)[i]);
+ int64_t actual_type = static_cast<int64_t>(actual_types[i]);
+ expected_metrics->push_back(
+ {{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
+ {internal::kUKMFormSignatureMetricName, form_signature},
+ {internal::kUKMFieldSignatureMetricName, field_signature},
+ {internal::kUKMValidationEventMetricName, metric_type},
+ {internal::kUKMPredictionSourceMetricName, source},
+ {internal::kUKMPredictedTypeMetricName, predicted_type},
+ {internal::kUKMActualTypeMetricName, actual_type}});
+ }
+ }
+}
+
} // namespace
// This is defined in the autofill_metrics.cc implementation file.
@@ -411,7 +479,7 @@ class AutofillMetricsTest : public testing::Test {
void EnableWalletSync();
base::test::ScopedTaskEnvironment scoped_task_environment_;
- ukm::TestUkmRecorder test_ukm_recorder_;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
TestAutofillClient autofill_client_;
std::unique_ptr<AccountTrackerService> account_tracker_;
std::unique_ptr<FakeSigninManagerBase> signin_manager_;
@@ -795,7 +863,7 @@ TEST_P(QualityMetricsTest, Classification) {
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- std::vector<ServerFieldType> heuristic_types, server_types;
+ std::vector<ServerFieldType> heuristic_types, server_types, actual_types;
AutofillField field;
// Add a first name field, that is predicted correctly.
@@ -804,6 +872,7 @@ TEST_P(QualityMetricsTest, Classification) {
form.fields.push_back(field);
heuristic_types.push_back(NAME_FIRST);
server_types.push_back(NAME_FIRST);
+ actual_types.push_back(NAME_FIRST);
// Add a last name field, that is predicted correctly.
test::CreateTestFormField("last", "last", ValueForType(NAME_LAST), "test",
@@ -811,6 +880,7 @@ TEST_P(QualityMetricsTest, Classification) {
form.fields.push_back(field);
heuristic_types.push_back(NAME_LAST);
server_types.push_back(NAME_LAST);
+ actual_types.push_back(NAME_LAST);
// Add an empty or unknown field, that is predicted as per the test params.
test::CreateTestFormField("Unknown", "Unknown",
@@ -819,6 +889,12 @@ TEST_P(QualityMetricsTest, Classification) {
heuristic_types.push_back(predicted_type);
server_types.push_back(predicted_type == UNKNOWN_TYPE ? NO_SERVER_DATA
: predicted_type);
+ // Resolve any field type ambiguity.
+ if (actual_field_type == AMBIGUOUS_TYPE) {
+ if (predicted_type == COMPANY_NAME || predicted_type == NAME_MIDDLE)
+ actual_field_type = predicted_type;
+ }
+ actual_types.push_back(actual_field_type);
// Simulate having seen this form on page load.
autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
@@ -827,11 +903,12 @@ TEST_P(QualityMetricsTest, Classification) {
base::HistogramTester histogram_tester;
autofill_manager_->SubmitForm(form, TimeTicks::Now());
- // Resolve any field type ambiguity.
- if (actual_field_type == AMBIGUOUS_TYPE) {
- if (predicted_type == COMPANY_NAME || predicted_type == NAME_MIDDLE)
- actual_field_type = predicted_type;
- }
+ ExpectedUkmMetrics expected_ukm_metrics;
+ AppendFieldTypeUkm(form, heuristic_types, server_types, actual_types,
+ &expected_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldTypeEntryName,
+ expected_ukm_metrics);
// Validate the total samples and the crossed (predicted-to-actual) samples.
for (const auto& source : prediction_sources) {
@@ -1038,7 +1115,8 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) {
autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
// Simulate text input on one of the fields.
- autofill_manager_->OnTextFieldDidChange(form, form.fields[0], TimeTicks());
+ autofill_manager_->OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
// Trigger a form upload and metrics by Resetting the manager.
base::HistogramTester histogram_tester;
@@ -1583,8 +1661,10 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields) {
base::HistogramTester histogram_tester;
// Simulate text input in the first and second fields.
- autofill_manager_->OnTextFieldDidChange(form, form.fields[0], TimeTicks());
- autofill_manager_->OnTextFieldDidChange(form, form.fields[1], TimeTicks());
+ autofill_manager_->OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
+ autofill_manager_->OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
autofill_manager_->SubmitForm(form, TimeTicks::Now());
@@ -1632,7 +1712,8 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) {
base::HistogramTester histogram_tester;
// Simulate text input in the first field.
- autofill_manager_->OnTextFieldDidChange(form, form.fields[0], TimeTicks());
+ autofill_manager_->OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
// We expect metrics to be logged when the manager is reset.
autofill_manager_->ResetRunLoop();
@@ -1772,7 +1853,7 @@ TEST_F(AutofillMetricsTest,
ASSERT_EQ(1U, test_ukm_recorder_.entries_count());
ASSERT_EQ(1U, test_ukm_recorder_.sources_count());
VerifyDeveloperEngagementUkm(
- form, &test_ukm_recorder_,
+ test_ukm_recorder_, form,
{AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS});
}
}
@@ -1822,7 +1903,7 @@ TEST_F(AutofillMetricsTest,
ASSERT_EQ(1U, test_ukm_recorder_.entries_count());
ASSERT_EQ(1U, test_ukm_recorder_.sources_count());
VerifyDeveloperEngagementUkm(
- form, &test_ukm_recorder_,
+ test_ukm_recorder_, form,
{AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS});
}
}
@@ -1854,7 +1935,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
ASSERT_EQ(1U, test_ukm_recorder_.entries_count());
ASSERT_EQ(1U, test_ukm_recorder_.sources_count());
- VerifyDeveloperEngagementUkm(form, &test_ukm_recorder_,
+ VerifyDeveloperEngagementUkm(test_ukm_recorder_, form,
{AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT});
test_ukm_recorder_.Purge();
}
@@ -1869,7 +1950,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
autofill_manager_->Reset();
VerifyDeveloperEngagementUkm(
- form, &test_ukm_recorder_,
+ test_ukm_recorder_, form,
{AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS,
AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT});
}
@@ -2142,7 +2223,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
}
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionsShownEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionsShownEntryName,
{{{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
{internal::kUKMHeuristicTypeMetricName, CREDIT_CARD_NAME_FULL},
{internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED},
@@ -2155,14 +2236,14 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// call to |external_delegate_->DidAcceptSuggestion|. Second, from call to
// |autofill_manager_->FillOrPreviewForm|.
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionFilledEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionFilledEntryName,
{{{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}},
{{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
// Expect |NON_FILLABLE_FORM_OR_NEW_DATA| in |AutofillFormSubmittedState|
// because |field.value| is empty in |DeterminePossibleFieldTypesForUpload|.
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -2253,7 +2334,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
}
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionsShownEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionsShownEntryName,
{{{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
{internal::kUKMHeuristicTypeMetricName, ADDRESS_HOME_STATE},
{internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED},
@@ -2266,14 +2347,14 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
// call to |external_delegate_->DidAcceptSuggestion|. Second, from call to
// |autofill_manager_->FillOrPreviewForm|.
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionFilledEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionFilledEntryName,
{{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}},
{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
// Expect |NON_FILLABLE_FORM_OR_NEW_DATA| in |AutofillFormSubmittedState|
// because |field.value| is empty in |DeterminePossibleFieldTypesForUpload|.
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -2529,6 +2610,11 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+ // Check that the bank name histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+ "Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
}
// Reset the autofill manager state.
@@ -2546,6 +2632,11 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+ // Check that the bank name histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+ "Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
}
// Reset the autofill manager state.
@@ -2563,6 +2654,59 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
+ // Check that the bank name histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+ "Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
+ }
+
+ // Recreate server cards with bank names.
+ personal_data_->RecreateServerCreditCardsWithBankName();
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
+ // Simulating new popup being shown.
+ base::HistogramTester histogram_tester;
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+ AutofillMetrics::
+ FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE,
+ 1);
+ }
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
+ // Simulating two popups in the same page load.
+ base::HistogramTester histogram_tester;
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+ AutofillMetrics::
+ FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE,
+ 1);
}
}
@@ -2731,6 +2875,11 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1);
+ // Check that the bank name histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+ "Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
}
// Reset the autofill manager state.
@@ -2754,6 +2903,52 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1);
}
+
+ // Recreate server cards with bank names.
+ personal_data_->RecreateServerCreditCardsWithBankName();
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
+ // Simulating filling a full card server suggestion.
+ base::HistogramTester histogram_tester;
+ std::string guid(
+ "10000000-0000-0000-0000-000000000003"); // full server card
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->FillOrPreviewForm(
+ AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
+ autofill_manager_->MakeFrontendID(guid, std::string()));
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+ AutofillMetrics::
+ FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE,
+ 1);
+ }
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
+ // Simulating filling multiple times.
+ base::HistogramTester histogram_tester;
+ std::string guid(
+ "10000000-0000-0000-0000-000000000003"); // full server card
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->FillOrPreviewForm(
+ AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
+ autofill_manager_->MakeFrontendID(guid, std::string()));
+ autofill_manager_->FillOrPreviewForm(
+ AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
+ autofill_manager_->MakeFrontendID(guid, std::string()));
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+ AutofillMetrics::
+ FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE,
+ 1);
+ }
}
// Test that we log submitted form events for credit cards.
@@ -2870,7 +3065,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -2894,12 +3089,12 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionsShownEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionsShownEntryName,
{{{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
{internal::kUKMHeuristicTypeMetricName, CREDIT_CARD_NUMBER},
{internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED},
{internal::kUKMServerTypeMetricName, CREDIT_CARD_NUMBER}}});
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -2926,10 +3121,10 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1);
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionFilledEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionFilledEntryName,
{{{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -2957,10 +3152,10 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1);
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionFilledEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionFilledEntryName,
{{{internal::kUKMRecordTypeMetricName, CreditCard::FULL_SERVER_CARD},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -2990,14 +3185,14 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
1);
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionFilledEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionFilledEntryName,
{{{internal::kUKMRecordTypeMetricName, CreditCard::MASKED_SERVER_CARD},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_,
+ test_ukm_recorder_, form,
internal::kUKMSelectedMaskedServerCardEntryName,
{{{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -3023,7 +3218,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
autofill_manager_->SubmitForm(form, TimeTicks::Now());
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMFormSubmittedEntryName,
+ test_ukm_recorder_, form, internal::kUKMFormSubmittedEntryName,
{{{internal::kUKMAutofillFormSubmittedStateMetricName,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
@@ -3031,7 +3226,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
autofill_manager_->SubmitForm(form, TimeTicks::Now());
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMFormSubmittedEntryName,
+ test_ukm_recorder_, form, internal::kUKMFormSubmittedEntryName,
{{{internal::kUKMAutofillFormSubmittedStateMetricName,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}},
@@ -3123,12 +3318,12 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
0);
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionsShownEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionsShownEntryName,
{{{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
{internal::kUKMHeuristicTypeMetricName, CREDIT_CARD_NUMBER},
{internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED},
{internal::kUKMServerTypeMetricName, CREDIT_CARD_NUMBER}}});
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
}
@@ -3447,6 +3642,11 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+ // Check that the bank name histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+ "Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
}
// Reset the autofill manager state.
@@ -3464,6 +3664,11 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+ // Check that the bank name histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+ "Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
}
// Reset the autofill manager state.
@@ -3481,6 +3686,11 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
+ // Check that the bank name histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+ "Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
}
}
@@ -3588,7 +3798,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
- VerifySubmitFormUkm(form, &test_ukm_recorder_,
+ VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA);
}
@@ -4111,13 +4321,17 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
base::HistogramTester histogram_tester;
autofill_manager_->OnFormsSeen(forms, TimeTicks::Now());
histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0);
+
+ EXPECT_EQ(1U, test_ukm_recorder_.entries_count());
+ EXPECT_EQ(1U, test_ukm_recorder_.sources_count());
+
+ VerifyDeveloperEngagementUkm(
+ test_ukm_recorder_, form,
+ {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS});
}
- std::vector<std::vector<std::pair<const char*, int64_t>>>
- expected_form_submission_ukm_metrics = {
- {{internal::kUKMAutofillFormSubmittedStateMetricName,
- AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA},
- {internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}};
+ ExpectedUkmMetrics expected_form_submission_ukm_metrics;
+ ExpectedUkmMetrics expected_field_fill_status_ukm_metrics;
// No data entered in the form.
{
@@ -4130,16 +4344,18 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_FormSubmitted_NonFillable"));
- // Expect an entry for |DeveloperEngagement| and an entry for form
- // interactions. Both entries are for the same URL.
- ASSERT_EQ(2U, test_ukm_recorder_.entries_count());
- ASSERT_EQ(2U, test_ukm_recorder_.sources_count());
- VerifyDeveloperEngagementUkm(
- form, &test_ukm_recorder_,
- {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS});
- VerifyFormInteractionUkm(form, &test_ukm_recorder_,
+ expected_form_submission_ukm_metrics.push_back(
+ {{internal::kUKMAutofillFormSubmittedStateMetricName,
+ AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA},
+ {internal::kUKMMillisecondsSinceFormParsedMetricName, 0}});
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
internal::kUKMFormSubmittedEntryName,
expected_form_submission_ukm_metrics);
+
+ AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldFillStatusEntryName,
+ expected_field_fill_status_ukm_metrics);
}
// Non fillable form.
@@ -4161,9 +4377,14 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{{internal::kUKMAutofillFormSubmittedStateMetricName,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}});
- VerifyFormInteractionUkm(form, &test_ukm_recorder_,
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
internal::kUKMFormSubmittedEntryName,
expected_form_submission_ukm_metrics);
+
+ AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldFillStatusEntryName,
+ expected_field_fill_status_ukm_metrics);
}
// Fill in the third field.
@@ -4187,9 +4408,15 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
AutofillMetrics::
FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}});
- VerifyFormInteractionUkm(form, &test_ukm_recorder_,
+
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
internal::kUKMFormSubmittedEntryName,
expected_form_submission_ukm_metrics);
+
+ AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldFillStatusEntryName,
+ expected_field_fill_status_ukm_metrics);
}
// Autofilled none with suggestions shown.
@@ -4205,18 +4432,24 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
"Autofill_FormSubmitted_FilledNone_SuggestionsShown"));
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionsShownEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionsShownEntryName,
{{{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
{internal::kUKMHeuristicTypeMetricName, PHONE_HOME_WHOLE_NUMBER},
{internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED},
{internal::kUKMServerTypeMetricName, NO_SERVER_DATA}}});
+
expected_form_submission_ukm_metrics.push_back(
{{internal::kUKMAutofillFormSubmittedStateMetricName,
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}});
- VerifyFormInteractionUkm(form, &test_ukm_recorder_,
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
internal::kUKMFormSubmittedEntryName,
expected_form_submission_ukm_metrics);
+
+ AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldFillStatusEntryName,
+ expected_field_fill_status_ukm_metrics);
}
// Mark one of the fields as autofilled.
@@ -4238,9 +4471,14 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{{internal::kUKMAutofillFormSubmittedStateMetricName,
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}});
- VerifyFormInteractionUkm(form, &test_ukm_recorder_,
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
internal::kUKMFormSubmittedEntryName,
expected_form_submission_ukm_metrics);
+
+ AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldFillStatusEntryName,
+ expected_field_fill_status_ukm_metrics);
}
// Mark all of the fillable fields as autofilled.
@@ -4263,9 +4501,14 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{{internal::kUKMAutofillFormSubmittedStateMetricName,
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}});
- VerifyFormInteractionUkm(form, &test_ukm_recorder_,
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
internal::kUKMFormSubmittedEntryName,
expected_form_submission_ukm_metrics);
+
+ AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldFillStatusEntryName,
+ expected_field_fill_status_ukm_metrics);
}
// Clear out the third field's value.
@@ -4287,9 +4530,14 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{{internal::kUKMAutofillFormSubmittedStateMetricName,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}});
- VerifyFormInteractionUkm(form, &test_ukm_recorder_,
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
internal::kUKMFormSubmittedEntryName,
expected_form_submission_ukm_metrics);
+
+ AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics);
+ VerifyFormInteractionUkm(test_ukm_recorder_, form,
+ internal::kUKMFieldFillStatusEntryName,
+ expected_field_fill_status_ukm_metrics);
}
}
@@ -4324,7 +4572,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) {
{
base::HistogramTester histogram_tester;
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- TimeTicks());
+ gfx::RectF(), TimeTicks());
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::USER_DID_TYPE, 1);
}
@@ -4367,10 +4615,10 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) {
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
autofill_manager_->MakeFrontendID(std::string(), guid));
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- TimeTicks());
+ gfx::RectF(), TimeTicks());
// Simulate a second keystroke; make sure we don't log the metric twice.
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- TimeTicks());
+ gfx::RectF(), TimeTicks());
histogram_tester.ExpectBucketCount(
"Autofill.UserHappiness",
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1);
@@ -4390,7 +4638,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) {
// Simulate editing another autofilled field.
{
base::HistogramTester histogram_tester;
- autofill_manager_->OnTextFieldDidChange(form, form.fields[1], TimeTicks());
+ autofill_manager_->OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
histogram_tester.ExpectUniqueSample(
"Autofill.UserHappiness",
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1);
@@ -4399,12 +4648,12 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) {
autofill_manager_->Reset();
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMInteractedWithFormEntryName,
+ test_ukm_recorder_, form, internal::kUKMInteractedWithFormEntryName,
{{{internal::kUKMIsForCreditCardMetricName, false},
{internal::kUKMLocalRecordTypeCountMetricName, 0},
{internal::kUKMServerRecordTypeCountMetricName, 0}}});
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionsShownEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionsShownEntryName,
{{{internal::kUKMMillisecondsSinceFormParsedMetricName, 0},
{internal::kUKMHeuristicTypeMetricName, PHONE_HOME_WHOLE_NUMBER},
{internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED},
@@ -4414,13 +4663,13 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) {
{internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED},
{internal::kUKMServerTypeMetricName, NO_SERVER_DATA}}});
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMSuggestionFilledEntryName,
+ test_ukm_recorder_, form, internal::kUKMSuggestionFilledEntryName,
{{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}},
{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE},
{internal::kUKMMillisecondsSinceFormParsedMetricName, 0}}});
VerifyFormInteractionUkm(
- form, &test_ukm_recorder_, internal::kUKMTextFieldDidChangeEntryName,
+ test_ukm_recorder_, form, internal::kUKMTextFieldDidChangeEntryName,
{{{internal::kUKMFieldTypeGroupMetricName, NAME},
{internal::kUKMHeuristicTypeMetricName, NAME_FULL},
{internal::kUKMServerTypeMetricName, NO_SERVER_DATA},
@@ -4507,6 +4756,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
base::HistogramTester histogram_tester;
autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(),
TimeTicks::FromInternalValue(3));
autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
@@ -4559,6 +4809,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
autofill_manager_->OnDidFillAutofillFormData(
form, TimeTicks::FromInternalValue(5));
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(),
TimeTicks::FromInternalValue(3));
autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
@@ -4587,6 +4838,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
autofill_manager_->OnDidFillAutofillFormData(
form, TimeTicks::FromInternalValue(5));
autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(),
TimeTicks::FromInternalValue(3));
autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc
index e54c9fdab7d..35bc5904680 100644
--- a/chromium/components/autofill/core/browser/autofill_profile.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile.cc
@@ -309,6 +309,14 @@ bool AutofillProfile::SetInfo(const AutofillType& type,
return form_group->SetInfo(type, trimmed_value, app_locale);
}
+void AutofillProfile::GetSupportedTypes(
+ ServerFieldTypeSet* supported_types) const {
+ FormGroupList info = FormGroups();
+ for (const auto* form_group : info) {
+ form_group->GetSupportedTypes(supported_types);
+ }
+}
+
bool AutofillProfile::IsEmpty(const std::string& app_locale) const {
ServerFieldTypeSet types;
GetNonEmptyTypes(app_locale, &types);
@@ -709,14 +717,6 @@ void AutofillProfile::RecordAndLogUse() {
RecordUse();
}
-void AutofillProfile::GetSupportedTypes(
- ServerFieldTypeSet* supported_types) const {
- FormGroupList info = FormGroups();
- for (const auto* form_group : info) {
- form_group->GetSupportedTypes(supported_types);
- }
-}
-
// static
void AutofillProfile::CreateInferredLabelsHelper(
const std::vector<AutofillProfile*>& profiles,
diff --git a/chromium/components/autofill/core/browser/autofill_profile.h b/chromium/components/autofill/core/browser/autofill_profile.h
index a420dbeac1b..de3311312f3 100644
--- a/chromium/components/autofill/core/browser/autofill_profile.h
+++ b/chromium/components/autofill/core/browser/autofill_profile.h
@@ -65,6 +65,7 @@ class AutofillProfile : public AutofillDataModel {
bool SetInfo(const AutofillType& type,
const base::string16& value,
const std::string& app_locale) override;
+ void GetSupportedTypes(ServerFieldTypeSet* supported_types) const override;
// How this card is stored.
RecordType record_type() const { return record_type_; }
@@ -191,9 +192,6 @@ class AutofillProfile : public AutofillDataModel {
private:
typedef std::vector<const FormGroup*> FormGroupList;
- // FormGroup:
- void GetSupportedTypes(ServerFieldTypeSet* supported_types) const override;
-
// Creates inferred labels for |profiles| at indices corresponding to
// |indices|, and stores the results to the corresponding elements of
// |labels|. These labels include enough fields to differentiate among the
diff --git a/chromium/components/autofill/core/browser/autofill_provider.cc b/chromium/components/autofill/core/browser/autofill_provider.cc
new file mode 100644
index 00000000000..9fc3b8c0f43
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_provider.cc
@@ -0,0 +1,22 @@
+// 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/autofill/core/browser/autofill_provider.h"
+
+#include "components/autofill/core/browser/autofill_handler_proxy.h"
+
+namespace autofill {
+
+AutofillProvider::AutofillProvider() {}
+
+AutofillProvider::~AutofillProvider() {}
+
+void AutofillProvider::SendFormDataToRenderer(AutofillHandlerProxy* handler,
+ int requestId,
+ const FormData& formData) {
+ handler->SendFormDataToRenderer(
+ requestId, AutofillDriver::FORM_DATA_ACTION_FILL, formData);
+}
+
+} // namespace autofil
diff --git a/chromium/components/autofill/core/browser/autofill_provider.h b/chromium/components/autofill/core/browser/autofill_provider.h
new file mode 100644
index 00000000000..d0776bc6bd3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_provider.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_AUTOFILL_CORE_BROWSER_AUTOFILL_PROVIDER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROVIDER_H_
+
+#include "base/time/time.h"
+#include "components/autofill/core/common/form_data.h"
+
+namespace gfx {
+class RectF;
+}
+
+namespace autofill {
+
+class AutofillHandlerProxy;
+
+// This class defines the interface for the autofill implementation other than
+// default AutofillManager.
+class AutofillProvider {
+ public:
+ AutofillProvider();
+ virtual ~AutofillProvider();
+
+ virtual void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler,
+ int32_t id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) = 0;
+
+ virtual void OnTextFieldDidChange(AutofillHandlerProxy* handler,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ const base::TimeTicks timestamp) = 0;
+
+ virtual bool OnWillSubmitForm(AutofillHandlerProxy* handler,
+ const FormData& form,
+ const base::TimeTicks timestamp) = 0;
+
+ virtual void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) = 0;
+
+ virtual void OnFocusOnFormField(AutofillHandlerProxy* handler,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) = 0;
+
+ virtual void OnDidFillAutofillFormData(AutofillHandlerProxy* handler,
+ const FormData& form,
+ base::TimeTicks timestamp) = 0;
+
+ virtual void Reset(AutofillHandlerProxy* handler) = 0;
+
+ void SendFormDataToRenderer(AutofillHandlerProxy* handler,
+ int requestId,
+ const FormData& formData);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROVIDER_H_
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 626743179ee..0857cc6d637 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
@@ -43,8 +43,16 @@ AutofillSaveCardInfoBarDelegateMobile::AutofillSaveCardInfoBarDelegateMobile(
#endif
card_label_(base::string16(kMidlineEllipsis) + card.LastFourDigits()),
card_sub_label_(card.AbbreviatedExpirationDateForDisplay()) {
- if (legal_message)
- LegalMessageLine::Parse(*legal_message, &legal_messages_);
+ if (legal_message) {
+ if (!LegalMessageLine::Parse(*legal_message, &legal_messages_,
+ /*escape_apostrophes=*/true)) {
+ AutofillMetrics::LogCreditCardInfoBarMetric(
+ AutofillMetrics::INFOBAR_NOT_SHOWN_INVALID_LEGAL_MESSAGE, upload_,
+ pref_service_->GetInteger(
+ prefs::kAutofillAcceptSaveCreditCardPromptState));
+ return;
+ }
+ }
AutofillMetrics::LogCreditCardInfoBarMetric(
AutofillMetrics::INFOBAR_SHOWN, upload_,
@@ -63,6 +71,12 @@ void AutofillSaveCardInfoBarDelegateMobile::OnLegalMessageLinkClicked(
infobar()->owner()->OpenURL(url, WindowOpenDisposition::NEW_FOREGROUND_TAB);
}
+bool AutofillSaveCardInfoBarDelegateMobile::LegalMessagesParsedSuccessfully() {
+ // If we are uploading to the server, verify that legal lines have been parsed
+ // into |legal_messages_|.
+ return !upload_ || !legal_messages_.empty();
+}
+
int AutofillSaveCardInfoBarDelegateMobile::GetIconId() const {
return IDR_INFOBAR_AUTOFILL_CC;
}
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 d1a0d2297b8..8b933018107 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
@@ -45,6 +45,10 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate {
// Called when a link in the legal message text was clicked.
void OnLegalMessageLinkClicked(GURL url);
+ // Ensures the InfoBar is not shown if legal messages failed to parse.
+ // Legal messages are only specified for the upload case, not for local save.
+ bool LegalMessagesParsedSuccessfully();
+
// ConfirmInfoBarDelegate:
int GetIconId() const override;
base::string16 GetMessageText() const override;
diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc
index a851aebfcf2..6ca9deb0ead 100644
--- a/chromium/components/autofill/core/browser/autofill_test_utils.cc
+++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc
@@ -35,7 +35,12 @@ namespace test {
std::unique_ptr<PrefService> PrefServiceForTesting() {
scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
new user_prefs::PrefRegistrySyncable());
- AutofillManager::RegisterProfilePrefs(registry.get());
+ return PrefServiceForTesting(registry.get());
+}
+
+std::unique_ptr<PrefService> PrefServiceForTesting(
+ user_prefs::PrefRegistrySyncable* registry) {
+ AutofillManager::RegisterProfilePrefs(registry);
// PDM depends on these prefs, which are normally registered in
// SigninManagerFactory.
@@ -59,7 +64,7 @@ std::unique_ptr<PrefService> PrefServiceForTesting() {
PrefServiceFactory factory;
factory.set_user_prefs(make_scoped_refptr(new TestingPrefStore()));
- return factory.Create(registry.get());
+ return factory.Create(registry);
}
void CreateTestFormField(const char* label,
@@ -212,6 +217,21 @@ AutofillProfile GetFullProfile2() {
return profile;
}
+AutofillProfile GetIncompleteProfile1() {
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/");
+ SetProfileInfo(&profile, "John", "H.", "Doe", "jsmith@example.com", "ACME",
+ "123 Main Street", "Unit 1", "Greensdale", "MI", "48838", "US",
+ "");
+ return profile;
+}
+
+AutofillProfile GetIncompleteProfile2() {
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/");
+ SetProfileInfo(&profile, "", "", "", "jsmith@example.com", "", "", "", "", "",
+ "", "", "");
+ return profile;
+}
+
AutofillProfile GetVerifiedProfile() {
AutofillProfile profile(GetFullProfile());
profile.set_origin(kSettingsOrigin);
diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h
index 6bd447e6ded..65817d8d9dc 100644
--- a/chromium/components/autofill/core/browser/autofill_test_utils.h
+++ b/chromium/components/autofill/core/browser/autofill_test_utils.h
@@ -14,6 +14,10 @@
class PrefService;
+namespace user_prefs {
+class PrefRegistrySyncable;
+} // namespace user_prefs
+
namespace autofill {
class AutofillProfile;
@@ -25,12 +29,14 @@ struct FormFieldData;
// Common utilities shared amongst Autofill tests.
namespace test {
-// Returns a PrefService that can be used for Autofill-related testing in
-// contexts where the PrefService would otherwise have to be constructed
-// manually (e.g., in unit tests within Autofill core code). The returned
-// PrefService has had Autofill preferences registered on its associated
-// registry.
+// The following methods return a PrefService that can be used for
+// Autofill-related testing in contexts where the PrefService would otherwise
+// have to be constructed manually (e.g., in unit tests within Autofill core
+// code). The returned PrefService has had Autofill preferences registered on
+// its associated registry.
std::unique_ptr<PrefService> PrefServiceForTesting();
+std::unique_ptr<PrefService> PrefServiceForTesting(
+ user_prefs::PrefRegistrySyncable* registry);
// Provides a quick way to populate a FormField with c-strings.
void CreateTestFormField(const char* label,
@@ -65,6 +71,12 @@ AutofillProfile GetFullProfile();
// Returns a profile full of dummy info, different to the above.
AutofillProfile GetFullProfile2();
+// Returns an incomplete profile of dummy info.
+AutofillProfile GetIncompleteProfile1();
+
+// Returns an incomplete profile of dummy info, different to the above.
+AutofillProfile GetIncompleteProfile2();
+
// Returns a verified profile full of dummy info.
AutofillProfile GetVerifiedProfile();
diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc
index 33dc123d3b6..aeda42b6da3 100644
--- a/chromium/components/autofill/core/browser/credit_card.cc
+++ b/chromium/components/autofill/core/browser/credit_card.cc
@@ -103,6 +103,7 @@ base::string16 NetworkForFill(const std::string& network) {
CreditCard::CreditCard(const std::string& guid, const std::string& origin)
: AutofillDataModel(guid, origin),
record_type_(LOCAL_CARD),
+ card_type_(CARD_TYPE_UNKNOWN),
network_(kGenericCard),
expiration_month_(0),
expiration_year_(0),
@@ -490,6 +491,7 @@ void CreditCard::operator=(const CreditCard& credit_card) {
return;
record_type_ = credit_card.record_type_;
+ card_type_ = credit_card.card_type_;
number_ = credit_card.number_;
name_on_card_ = credit_card.name_on_card_;
network_ = credit_card.network_;
@@ -498,6 +500,7 @@ void CreditCard::operator=(const CreditCard& credit_card) {
server_id_ = credit_card.server_id_;
server_status_ = credit_card.server_status_;
billing_address_id_ = credit_card.billing_address_id_;
+ bank_name_ = credit_card.bank_name_;
set_guid(credit_card.guid());
set_origin(credit_card.origin());
@@ -564,6 +567,10 @@ int CreditCard::Compare(const CreditCard& credit_card) const {
if (comparison != 0)
return comparison;
+ comparison = bank_name_.compare(credit_card.bank_name_);
+ if (comparison != 0)
+ return comparison;
+
if (static_cast<int>(server_status_) <
static_cast<int>(credit_card.server_status_))
return -1;
@@ -739,6 +746,7 @@ base::string16 CreditCard::NetworkForDisplay() const {
base::string16 CreditCard::NetworkAndLastFourDigits() const {
base::string16 network = NetworkForDisplay();
+ // TODO(crbug.com/734197): truncate network.
base::string16 digits = LastFourDigits();
if (digits.empty())
@@ -748,6 +756,14 @@ base::string16 CreditCard::NetworkAndLastFourDigits() const {
return network + base::string16(kMidlineEllipsis) + digits;
}
+base::string16 CreditCard::BankNameAndLastFourDigits() const {
+ base::string16 digits = LastFourDigits();
+ // TODO(crbug.com/734197): truncate bank name.
+ if (digits.empty())
+ return ASCIIToUTF16(bank_name_);
+ return ASCIIToUTF16(bank_name_) + base::string16(kMidlineEllipsis) + digits;
+}
+
base::string16 CreditCard::AbbreviatedExpirationDateForDisplay() const {
base::string16 month = ExpirationMonthAsString();
base::string16 year = Expiration2DigitYearAsString();
diff --git a/chromium/components/autofill/core/browser/credit_card.h b/chromium/components/autofill/core/browser/credit_card.h
index 4dc8bb9bc93..4a58dab6b49 100644
--- a/chromium/components/autofill/core/browser/credit_card.h
+++ b/chromium/components/autofill/core/browser/credit_card.h
@@ -46,6 +46,17 @@ class CreditCard : public AutofillDataModel {
OK,
};
+ // The type of the card. Local cards are all CARD_TYPE_UNKNOWN. Server cards
+ // may have a more specific type.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.autofill
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: CardType
+ enum CardType : int {
+ CARD_TYPE_UNKNOWN,
+ CARD_TYPE_CREDIT,
+ CARD_TYPE_DEBIT,
+ CARD_TYPE_PREPAID,
+ };
+
CreditCard(const std::string& guid, const std::string& origin);
// Creates a server card. The type must be MASKED_SERVER_CARD or
@@ -101,6 +112,9 @@ class CreditCard : public AutofillDataModel {
const std::string& network() const { return network_; }
+ const std::string& bank_name() const { return bank_name_; }
+ void set_bank_name(const std::string& bank_name) { bank_name_ = bank_name; }
+
int expiration_month() const { return expiration_month_; }
int expiration_year() const { return expiration_year_; }
@@ -147,6 +161,11 @@ class CreditCard : public AutofillDataModel {
RecordType record_type() const { return record_type_; }
void set_record_type(RecordType rt) { record_type_ = rt; }
+ // Whether this is a credit, debit, or prepaid card. Known only for server
+ // cards. All local cards are CARD_TYPE_UNKNOWN.
+ CardType card_type() const { return card_type_; }
+ void set_card_type(CardType card_type) { card_type_ = card_type; }
+
// Returns true if there are no values (field types) set.
bool IsEmpty(const std::string& app_locale) const;
@@ -209,6 +228,8 @@ class CreditCard : public AutofillDataModel {
base::string16 NetworkForDisplay() const;
// A label for this card formatted as 'IssuerNetwork - 2345'.
base::string16 NetworkAndLastFourDigits() const;
+ // A label for this card formatted as 'BankName - 2345'.
+ base::string16 BankNameAndLastFourDigits() 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.
@@ -237,6 +258,7 @@ class CreditCard : public AutofillDataModel {
// See enum definition above.
RecordType record_type_;
+ CardType card_type_;
// The card number. For MASKED_SERVER_CARDs, this number will just contain the
// last four digits of the card number.
@@ -249,6 +271,9 @@ class CreditCard : public AutofillDataModel {
// below.
std::string network_;
+ // The issuer bank name of the card.
+ std::string bank_name_;
+
// These members are zero if not present.
int expiration_month_;
int expiration_year_;
diff --git a/chromium/components/autofill/core/browser/credit_card_unittest.cc b/chromium/components/autofill/core/browser/credit_card_unittest.cc
index 3857613b21a..91a195d433f 100644
--- a/chromium/components/autofill/core/browser/credit_card_unittest.cc
+++ b/chromium/components/autofill/core/browser/credit_card_unittest.cc
@@ -8,6 +8,7 @@
#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"
@@ -155,6 +156,37 @@ TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) {
obfuscated5);
}
+// Tests credit card bank name and last four digits string generation.
+TEST(CreditCardTest, BankNameAndLastFourDigitsStrings) {
+ // Case 1: Have everything and show bank name.
+ CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(&credit_card1, "John Dillinger",
+ "5105 1051 0510 5100", "01", "2010", "1");
+ credit_card1.set_bank_name("Chase");
+ base::string16 obfuscated1 = credit_card1.BankNameAndLastFourDigits();
+ EXPECT_FALSE(credit_card1.bank_name().empty());
+ EXPECT_EQ(UTF8ToUTF16(std::string("Chase") + kUTF8MidlineEllipsis + "5100"),
+ obfuscated1);
+
+ // Case 2: Have no bank name and not show bank name.
+ CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(&credit_card2, "John Dillinger",
+ "5105 1051 0510 5100", "01", "2010", "1");
+ base::string16 obfuscated2 = credit_card2.BankNameAndLastFourDigits();
+ EXPECT_TRUE(credit_card2.bank_name().empty());
+ EXPECT_EQ(UTF8ToUTF16(std::string(kUTF8MidlineEllipsis) + "5100"),
+ obfuscated2);
+
+ // Case 3: Have bank name but no last four digits, only show bank name.
+ CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(&credit_card3, "John Dillinger", "", "01", "2010",
+ "1");
+ credit_card3.set_bank_name("Chase");
+ base::string16 obfuscated3 = credit_card3.BankNameAndLastFourDigits();
+ EXPECT_FALSE(credit_card3.bank_name().empty());
+ EXPECT_EQ(UTF8ToUTF16(std::string("Chase")), obfuscated3);
+}
+
TEST(CreditCardTest, AssignmentOperator) {
CreditCard a(base::GenerateGUID(), "some origin");
test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010",
diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc
index 10a8380908c..a18ae1bd40d 100644
--- a/chromium/components/autofill/core/browser/form_structure.cc
+++ b/chromium/components/autofill/core/browser/form_structure.cc
@@ -38,7 +38,7 @@
#include "components/autofill/core/common/signatures_util.h"
#include "components/rappor/public/rappor_utils.h"
#include "components/rappor/rappor_service_impl.h"
-#include "components/ukm/public/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
namespace autofill {
namespace {
@@ -696,6 +696,9 @@ void FormStructure::LogQualityMetrics(
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
bool did_show_suggestions,
bool observed_submission) const {
+ // Use the same timestamp on UKM Metrics generated within this method's scope.
+ AutofillMetrics::UkmTimestampPin timestamp_pin(form_interactions_ukm_logger);
+
size_t num_detected_field_types = 0;
size_t num_edited_autofilled_fields = 0;
bool did_autofill_all_possible_fields = true;
@@ -710,40 +713,31 @@ void FormStructure::LogQualityMetrics(
for (size_t i = 0; i < field_count(); ++i) {
auto* const field = this->field(i);
- // No further logging for password fields. Those are primarily related to a
- // different feature code path, and so make more sense to track outside of
- // this metric.
- if (field->form_control_type == "password")
- continue;
-
if (IsUPIVirtualPaymentAddress(field->value)) {
AutofillMetrics::LogUserHappinessMetric(
AutofillMetrics::USER_DID_ENTER_UPI_VPA);
}
+
+ form_interactions_ukm_logger->LogFieldFillStatus(*this, *field,
+ metric_type);
+
+ AutofillMetrics::LogHeuristicPredictionQualityMetrics(
+ form_interactions_ukm_logger, *this, *field, metric_type);
+ AutofillMetrics::LogServerPredictionQualityMetrics(
+ form_interactions_ukm_logger, *this, *field, metric_type);
+ AutofillMetrics::LogOverallPredictionQualityMetrics(
+ form_interactions_ukm_logger, *this, *field, metric_type);
// We count fields that were autofilled but later modified, regardless of
// whether the data now in the field is recognized.
if (field->previously_autofilled())
num_edited_autofilled_fields++;
- // Aliases for the field types predicted by heuristics, server and overall.
- ServerFieldType heuristic_type =
- AutofillType(field->heuristic_type()).GetStorableType();
- ServerFieldType server_type =
- AutofillType(field->server_type()).GetStorableType();
- ServerFieldType predicted_type = field->Type().GetStorableType();
-
const ServerFieldTypeSet& field_types = field->possible_types();
DCHECK(!field_types.empty());
-
- AutofillMetrics::LogHeuristicPredictionQualityMetrics(
- field_types, heuristic_type, metric_type);
- AutofillMetrics::LogServerPredictionQualityMetrics(field_types, server_type,
- metric_type);
- AutofillMetrics::LogOverallPredictionQualityMetrics(
- field_types, predicted_type, metric_type);
-
- if (field_types.count(EMPTY_TYPE) || field_types.count(UNKNOWN_TYPE))
+ if (field_types.count(EMPTY_TYPE) || field_types.count(UNKNOWN_TYPE)) {
+ DCHECK_EQ(field_types.size(), 1u);
continue;
+ }
++num_detected_field_types;
if (field->is_autofilled)
@@ -813,21 +807,18 @@ void FormStructure::LogQualityMetrics(
}
}
-void FormStructure::LogQualityMetricsBasedOnAutocomplete() const {
+void FormStructure::LogQualityMetricsBasedOnAutocomplete(
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger)
+ const {
const AutofillMetrics::QualityMetricType metric_type =
AutofillMetrics::TYPE_AUTOCOMPLETE_BASED;
for (const auto& field : fields_) {
if (field->html_type() != HTML_TYPE_UNSPECIFIED &&
field->html_type() != HTML_TYPE_UNRECOGNIZED) {
- // The type inferred by the autocomplete attribute.
- ServerFieldTypeSet actual_field_type_set{
- AutofillType(field->html_type(), field->html_mode())
- .GetStorableType()};
-
AutofillMetrics::LogHeuristicPredictionQualityMetrics(
- actual_field_type_set, field->heuristic_type(), metric_type);
+ form_interactions_ukm_logger, *this, *field, metric_type);
AutofillMetrics::LogServerPredictionQualityMetrics(
- actual_field_type_set, field->server_type(), metric_type);
+ form_interactions_ukm_logger, *this, *field, metric_type);
}
}
}
@@ -1098,8 +1089,11 @@ void FormStructure::EncodeFormForUpload(AutofillUploadContents* upload) const {
AutofillUploadContents::Field* added_field = upload->add_field();
added_field->set_autofill_type(field_type);
- if (field->generation_type())
+ if (field->generation_type()) {
added_field->set_generation_type(field->generation_type());
+ added_field->set_generated_password_changed(
+ field->generated_password_changed());
+ }
if (field->form_classifier_outcome()) {
added_field->set_form_classifier_outcome(
diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h
index 52fd9fbfaf4..c70a23a8c73 100644
--- a/chromium/components/autofill/core/browser/form_structure.h
+++ b/chromium/components/autofill/core/browser/form_structure.h
@@ -147,7 +147,9 @@ class FormStructure {
// Log the quality of the heuristics and server predictions for this form
// structure, if autocomplete attributes are present on the fields (they are
// used as golden truths).
- void LogQualityMetricsBasedOnAutocomplete() const;
+ void LogQualityMetricsBasedOnAutocomplete(
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger)
+ const;
// Classifies each field in |fields_| based upon its |autocomplete| attribute,
// if the attribute is available. The association is stored into the field's
diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc
index ac809717668..3a161b5f1cd 100644
--- a/chromium/components/autofill/core/browser/form_structure_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc
@@ -2363,6 +2363,7 @@ TEST_F(FormStructureTest,
form_structure->field(i)->set_generation_type(
AutofillUploadContents::Field::
MANUALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM);
+ form_structure->field(i)->set_generated_password_changed(true);
form_structure->field(i)->set_form_classifier_outcome(
AutofillUploadContents::Field::GENERATION_ELEMENT);
} else {
@@ -2436,6 +2437,7 @@ TEST_F(FormStructureTest,
MANUALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM);
upload_password_field->set_properties_mask(FieldPropertiesFlags::HAD_FOCUS |
FieldPropertiesFlags::USER_TYPED);
+ upload_password_field->set_generated_password_changed(true);
std::string expected_upload_string;
ASSERT_TRUE(upload.SerializeToString(&expected_upload_string));
diff --git a/chromium/components/autofill/core/browser/legal_message_line.cc b/chromium/components/autofill/core/browser/legal_message_line.cc
index 71bf0aa8c51..dc28aebb703 100644
--- a/chromium/components/autofill/core/browser/legal_message_line.cc
+++ b/chromium/components/autofill/core/browser/legal_message_line.cc
@@ -79,7 +79,8 @@ LegalMessageLine::~LegalMessageLine() {}
// static
bool LegalMessageLine::Parse(const base::DictionaryValue& legal_message,
- LegalMessageLines* out) {
+ LegalMessageLines* out,
+ bool escape_apostrophes) {
const base::ListValue* lines_list = nullptr;
if (legal_message.GetList("line", &lines_list)) {
LegalMessageLines lines;
@@ -88,7 +89,7 @@ bool LegalMessageLine::Parse(const base::DictionaryValue& legal_message,
lines.emplace_back(LegalMessageLine());
const base::DictionaryValue* single_line;
if (!lines_list->GetDictionary(i, &single_line) ||
- !lines.back().ParseLine(*single_line))
+ !lines.back().ParseLine(*single_line, escape_apostrophes))
return false;
}
@@ -98,7 +99,8 @@ bool LegalMessageLine::Parse(const base::DictionaryValue& legal_message,
return true;
}
-bool LegalMessageLine::ParseLine(const base::DictionaryValue& line) {
+bool LegalMessageLine::ParseLine(const base::DictionaryValue& line,
+ bool escape_apostrophes) {
DCHECK(text_.empty());
DCHECK(links_.empty());
@@ -133,6 +135,17 @@ bool LegalMessageLine::ParseLine(const base::DictionaryValue& line) {
if (!line.GetString("template", &template_icu))
return false;
+ if (escape_apostrophes) {
+ // The ICU standard counts "'{" as beginning an escaped string literal, even
+ // if there's no closing apostrophe. This fails legal message templates
+ // where an apostrophe precedes the template parameter, like "l'{1}" in
+ // Italian. Therefore, when |escape_apostrophes| is true, escape all
+ // apostrophes in the string by doubling them up.
+ // http://www.icu-project.org/apiref/icu4c/messagepattern_8h.html#af6e0757e0eb81c980b01ee5d68a9978b
+ base::ReplaceChars(template_icu, base::ASCIIToUTF16("'"),
+ base::ASCIIToUTF16("''"), &template_icu);
+ }
+
// Replace the placeholders in |template_icu| with strings from
// |display_texts|, and store the start position of each replacement in
// |offsets|.
diff --git a/chromium/components/autofill/core/browser/legal_message_line.h b/chromium/components/autofill/core/browser/legal_message_line.h
index 50955f9569c..141ea92dd70 100644
--- a/chromium/components/autofill/core/browser/legal_message_line.h
+++ b/chromium/components/autofill/core/browser/legal_message_line.h
@@ -64,8 +64,13 @@ class LegalMessageLine {
// 3. "${" anywhere in the template string is invalid.
// 4. "\n" embedded anywhere in the template string, or an empty template
// string, can be used to separate paragraphs.
+ // 5. Because a single apostrophe before a curly brace starts quoted literal
+ // text in MessageFormat, "'{0}" gets treated as a literal. To avoid
+ // situations like these, setting |escape_apostrophes| to true will escape
+ // all ASCII apostrophes by doubling them up.
static bool Parse(const base::DictionaryValue& legal_message,
- LegalMessageLines* out);
+ LegalMessageLines* out,
+ bool escape_apostrophes = false);
const base::string16& text() const { return text_; }
const std::vector<Link>& links() const { return links_; }
@@ -73,7 +78,7 @@ class LegalMessageLine {
private:
friend class TestLegalMessageLine;
- bool ParseLine(const base::DictionaryValue& line);
+ bool ParseLine(const base::DictionaryValue& line, bool escape_apostrophes);
base::string16 text_;
std::vector<Link> links_;
diff --git a/chromium/components/autofill/core/browser/legal_message_line_unittest.cc b/chromium/components/autofill/core/browser/legal_message_line_unittest.cc
index a65bc3dced8..e3147f1ee7d 100644
--- a/chromium/components/autofill/core/browser/legal_message_line_unittest.cc
+++ b/chromium/components/autofill/core/browser/legal_message_line_unittest.cc
@@ -47,6 +47,7 @@ class TestLegalMessageLine : public LegalMessageLine {
struct TestCase {
std::string message_json;
LegalMessageLines expected_lines;
+ bool escape_apostrophes;
};
// Prints out a legal message |line| to |os|.
@@ -109,7 +110,8 @@ TEST_P(LegalMessageLineTest, Parsing) {
EXPECT_TRUE(value->GetAsDictionary(&dictionary));
ASSERT_TRUE(dictionary);
LegalMessageLines actual_lines;
- LegalMessageLine::Parse(*dictionary, &actual_lines);
+ LegalMessageLine::Parse(*dictionary, &actual_lines,
+ GetParam().escape_apostrophes);
EXPECT_EQ(GetParam().expected_lines, actual_lines);
};
@@ -272,41 +274,58 @@ INSTANTIATE_TEST_CASE_P(
Link(24, 30, "http://www.example.com/1"),
Link(17, 19, "http://www.example.com/2"),
Link(36, 39, "http://www.example.com/3")})}},
- TestCase{"{"
- " \"line\" : [ {"
- " \"template\": \"a{0} b{1} c{2} d{3} e{4} f{5} g{6}\","
- " \"template_parameter\": [ {"
- " \"display_text\": \"A\","
- " \"url\": \"http://www.example.com/0\""
- " }, {"
- " \"display_text\": \"B\","
- " \"url\": \"http://www.example.com/1\""
- " }, {"
- " \"display_text\": \"C\","
- " \"url\": \"http://www.example.com/2\""
- " }, {"
- " \"display_text\": \"D\","
- " \"url\": \"http://www.example.com/3\""
- " }, {"
- " \"display_text\": \"E\","
- " \"url\": \"http://www.example.com/4\""
- " }, {"
- " \"display_text\": \"F\","
- " \"url\": \"http://www.example.com/5\""
- " }, {"
- " \"display_text\": \"G\","
- " \"url\": \"http://www.example.com/6\""
- " } ]"
- " } ]"
- "}",
- {TestLegalMessageLine(
- "aA bB cC dD eE fF gG",
- {Link(1, 2, "http://www.example.com/0"),
- Link(4, 5, "http://www.example.com/1"),
- Link(7, 8, "http://www.example.com/2"),
- Link(10, 11, "http://www.example.com/3"),
- Link(13, 14, "http://www.example.com/4"),
- Link(16, 17, "http://www.example.com/5"),
- Link(19, 20, "http://www.example.com/6")})}}));
+ TestCase{
+ "{"
+ " \"line\" : [ {"
+ " \"template\": \"a{0} b{1} c{2} d{3} e{4} f{5} g{6}\","
+ " \"template_parameter\": [ {"
+ " \"display_text\": \"A\","
+ " \"url\": \"http://www.example.com/0\""
+ " }, {"
+ " \"display_text\": \"B\","
+ " \"url\": \"http://www.example.com/1\""
+ " }, {"
+ " \"display_text\": \"C\","
+ " \"url\": \"http://www.example.com/2\""
+ " }, {"
+ " \"display_text\": \"D\","
+ " \"url\": \"http://www.example.com/3\""
+ " }, {"
+ " \"display_text\": \"E\","
+ " \"url\": \"http://www.example.com/4\""
+ " }, {"
+ " \"display_text\": \"F\","
+ " \"url\": \"http://www.example.com/5\""
+ " }, {"
+ " \"display_text\": \"G\","
+ " \"url\": \"http://www.example.com/6\""
+ " } ]"
+ " } ]"
+ "}",
+ {TestLegalMessageLine("aA bB cC dD eE fF gG",
+ {Link(1, 2, "http://www.example.com/0"),
+ Link(4, 5, "http://www.example.com/1"),
+ Link(7, 8, "http://www.example.com/2"),
+ Link(10, 11, "http://www.example.com/3"),
+ Link(13, 14, "http://www.example.com/4"),
+ Link(16, 17, "http://www.example.com/5"),
+ Link(19, 20, "http://www.example.com/6")})}},
+ // When |escape_apostrophes| is true, all ASCII apostrophes should be
+ // escaped for ICU's MessageFormat by doubling them up. This allows the
+ // template parameters to work correctly.
+ // http://www.icu-project.org/apiref/icu4c/messagepattern_8h.html#af6e0757e0eb81c980b01ee5d68a9978b
+ TestCase{
+ "{"
+ " \"line\" : [ {"
+ " \"template\": \"The panda bear's bamboo was '{0}.\","
+ " \"template_parameter\": [ {"
+ " \"display_text\": \"delicious\","
+ " \"url\": \"http://www.example.com/0\""
+ " } ]"
+ " } ]"
+ "}",
+ {TestLegalMessageLine("The panda bear's bamboo was 'delicious.",
+ {Link(29, 38, "http://www.example.com/0")})},
+ true}));
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc
index 2704d9276b5..f8566fa5773 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager.cc
@@ -12,10 +12,12 @@
#include <string>
#include <utility>
+#include "base/feature_list.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/timezone.h"
#include "base/memory/ptr_util.h"
#include "base/profiler/scoped_tracker.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -49,6 +51,7 @@
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h"
namespace autofill {
+
namespace {
using ::i18n::addressinput::AddressField;
@@ -58,6 +61,9 @@ using ::i18n::addressinput::STREET_ADDRESS;
// The length of a local profile GUID.
const int LOCAL_GUID_LENGTH = 36;
+constexpr base::TimeDelta kDisusedProfileTimeDelta =
+ base::TimeDelta::FromDays(180);
+
template<typename T>
class FormGroupMatchesByGUIDFunctor {
public:
@@ -268,7 +274,7 @@ PersonalDataManager::PersonalDataManager(const std::string& app_locale)
pref_service_(nullptr),
account_tracker_(nullptr),
is_off_the_record_(false),
- has_logged_profile_count_(false),
+ has_logged_stored_profile_metrics_(false),
has_logged_local_credit_card_count_(false),
has_logged_server_credit_card_counts_(false) {}
@@ -373,7 +379,7 @@ void PersonalDataManager::OnWebDataServiceRequestDone(
if (h == pending_profiles_query_) {
ReceiveLoadedDbValues(h, result.get(), &pending_profiles_query_,
&web_profiles_);
- LogProfileCount(); // This only logs local profiles.
+ LogStoredProfileMetrics(); // This only logs local profiles.
} else {
ReceiveLoadedDbValues(h, result.get(), &pending_server_profiles_query_,
&server_profiles_);
@@ -827,12 +833,12 @@ void PersonalDataManager::Refresh() {
LoadCreditCards();
}
-const std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest()
+std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest()
const {
std::vector<AutofillProfile*> profiles = GetProfiles(true);
// Rank the suggestions by frecency (see AutofillDataModel for details).
- base::Time comparison_time = AutofillClock::Now();
+ const base::Time comparison_time = AutofillClock::Now();
std::sort(profiles.begin(), profiles.end(),
[comparison_time](const AutofillDataModel* a,
const AutofillDataModel* b) {
@@ -842,6 +848,22 @@ const std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest()
return profiles;
}
+// static
+void PersonalDataManager::RemoveProfilesNotUsedSinceTimestamp(
+ base::Time min_last_used,
+ std::vector<AutofillProfile*>* profiles) {
+ const size_t original_size = profiles->size();
+ profiles->erase(
+ std::stable_partition(profiles->begin(), profiles->end(),
+ [min_last_used](const AutofillDataModel* m) {
+ return m->use_date() > min_last_used;
+ }),
+ profiles->end());
+ const size_t num_profiles_supressed = original_size - profiles->size();
+ AutofillMetrics::LogNumberOfAddressesSuppressedForDisuse(
+ num_profiles_supressed);
+}
+
std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions(
const AutofillType& type,
const base::string16& field_contents,
@@ -857,6 +879,15 @@ std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions(
// Get the profiles to suggest, which are already sorted.
std::vector<AutofillProfile*> profiles = GetProfilesToSuggest();
+ // If enabled, suppress disused address profiles when triggered from an empty
+ // field.
+ if (field_contents.empty() &&
+ base::FeatureList::IsEnabled(kAutofillSuppressDisusedAddresses)) {
+ const base::Time min_last_used =
+ AutofillClock::Now() - kDisusedProfileTimeDelta;
+ RemoveProfilesNotUsedSinceTimestamp(min_last_used, &profiles);
+ }
+
std::vector<Suggestion> suggestions;
// Match based on a prefix search.
std::vector<AutofillProfile*> matched_profiles;
@@ -1115,8 +1146,7 @@ bool PersonalDataManager::IsCountryOfInterest(const std::string& country_code)
AutofillCountry::CountryCodeForLocale(app_locale())));
}
- return std::find(country_codes.begin(), country_codes.end(),
- base::ToLowerASCII(country_code)) != country_codes.end();
+ return base::ContainsValue(country_codes, base::ToLowerASCII(country_code));
}
const std::string& PersonalDataManager::GetDefaultCountryCodeForNewAddress()
@@ -1339,10 +1369,28 @@ std::string PersonalDataManager::SaveImportedCreditCard(
return guid;
}
-void PersonalDataManager::LogProfileCount() const {
- if (!has_logged_profile_count_) {
+void PersonalDataManager::LogStoredProfileMetrics() const {
+ if (!has_logged_stored_profile_metrics_) {
+ // Update the histogram of how many addresses the user has stored.
AutofillMetrics::LogStoredProfileCount(web_profiles_.size());
- has_logged_profile_count_ = true;
+
+ // If the user has stored addresses, log the distribution of days since
+ // their last use and how many would be considered disused.
+ if (!web_profiles_.empty()) {
+ size_t num_disused_profiles = 0;
+ const base::Time now = AutofillClock::Now();
+ for (const std::unique_ptr<AutofillProfile>& profile : web_profiles_) {
+ const base::TimeDelta time_since_last_use = now - profile->use_date();
+ AutofillMetrics::LogStoredProfileDaysSinceLastUse(
+ time_since_last_use.InDays());
+ if (time_since_last_use > kDisusedProfileTimeDelta)
+ ++num_disused_profiles;
+ }
+ AutofillMetrics::LogStoredProfileDisusedCount(num_disused_profiles);
+ }
+
+ // Only log this info once per chrome user profile load.
+ has_logged_stored_profile_metrics_ = true;
}
}
@@ -1384,8 +1432,7 @@ std::string PersonalDataManager::MostCommonCountryCodeFromProfiles() const {
std::string country_code = base::ToUpperASCII(base::UTF16ToASCII(
profiles[i]->GetRawInfo(ADDRESS_HOME_COUNTRY)));
- if (std::find(country_codes.begin(), country_codes.end(), country_code) !=
- country_codes.end()) {
+ if (base::ContainsValue(country_codes, country_code)) {
// Verified profiles count 100x more than unverified ones.
votes[country_code] += profiles[i]->IsVerified() ? 100 : 1;
}
@@ -1616,6 +1663,23 @@ bool PersonalDataManager::ImportCreditCard(
// there is for local cards.
for (const auto& card : server_credit_cards_) {
if (candidate_credit_card.HasSameNumberAs(*card)) {
+ // Record metric on whether expiration dates matched.
+ if (candidate_credit_card.expiration_month() ==
+ card->expiration_month() &&
+ candidate_credit_card.expiration_year() == card->expiration_year()) {
+ AutofillMetrics::LogSubmittedServerCardExpirationStatusMetric(
+ card->record_type() == CreditCard::FULL_SERVER_CARD
+ ? AutofillMetrics::FULL_SERVER_CARD_EXPIRATION_DATE_MATCHED
+ : AutofillMetrics::MASKED_SERVER_CARD_EXPIRATION_DATE_MATCHED);
+ } else {
+ AutofillMetrics::LogSubmittedServerCardExpirationStatusMetric(
+ card->record_type() == CreditCard::FULL_SERVER_CARD
+ ? AutofillMetrics::
+ FULL_SERVER_CARD_EXPIRATION_DATE_DID_NOT_MATCH
+ : AutofillMetrics::
+ MASKED_SERVER_CARD_EXPIRATION_DATE_DID_NOT_MATCH);
+ }
+
// We can offer to save locally even if we already have this stored as
// another masked server card with the same |TypeAndLastFourDigits| as
// long as the AutofillOfferLocalSaveIfServerCardManuallyEntered flag is
@@ -1678,7 +1742,12 @@ std::vector<Suggestion> PersonalDataManager::GetSuggestionsForCards(
// Otherwise the label is the card number, or if that is empty the
// cardholder name. The label should never repeat the value.
if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
- suggestion->value = credit_card->NetworkAndLastFourDigits();
+ if (IsAutofillCreditCardBankNameDisplayExperimentEnabled() &&
+ !credit_card->bank_name().empty()) {
+ suggestion->value = credit_card->BankNameAndLastFourDigits();
+ } else {
+ suggestion->value = credit_card->NetworkAndLastFourDigits();
+ }
if (IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled()) {
suggestion->label =
credit_card->GetLastUsedDateForDisplay(app_locale_);
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h
index ac29f283ac5..444ba13f4c4 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.h
+++ b/chromium/components/autofill/core/browser/personal_data_manager.h
@@ -131,10 +131,10 @@ class PersonalDataManager : public KeyedService,
const CreditCard& imported_credit_card);
// Adds |profile| to the web database.
- void AddProfile(const AutofillProfile& profile);
+ virtual void AddProfile(const AutofillProfile& profile);
// Updates |profile| which already exists in the web database.
- void UpdateProfile(const AutofillProfile& profile);
+ virtual void UpdateProfile(const AutofillProfile& profile);
// Removes the profile or credit card represented by |guid|.
virtual void RemoveByGUID(const std::string& guid);
@@ -212,7 +212,13 @@ class PersonalDataManager : public KeyedService,
virtual const std::vector<CreditCard*>& GetCreditCards() const;
// Returns the profiles to suggest to the user, ordered by frecency.
- const std::vector<AutofillProfile*> GetProfilesToSuggest() const;
+ std::vector<AutofillProfile*> GetProfilesToSuggest() const;
+
+ // Remove profiles that haven't been used after |min_last_used| from
+ // |profiles|. The relative ordering of |profiles| is maintained.
+ static void RemoveProfilesNotUsedSinceTimestamp(
+ base::Time min_last_used,
+ std::vector<AutofillProfile*>* profiles);
// Loads profiles that can suggest data for |type|. |field_contents| is the
// part the user has already typed. |field_is_autofilled| is true if the field
@@ -390,9 +396,9 @@ class PersonalDataManager : public KeyedService,
// Notifies observers that personal data has changed.
void NotifyPersonalDataChanged();
- // The first time this is called, logs an UMA metric for the number of
- // profiles the user has. On subsequent calls, does nothing.
- void LogProfileCount() const;
+ // The first time this is called, logs a UMA metrics about the user's profile.
+ // On subsequent calls, does nothing.
+ void LogStoredProfileMetrics() const;
// The first time this is called, logs an UMA metric for the number of local
// credit cards the user has. On subsequent calls, does nothing.
@@ -592,8 +598,8 @@ class PersonalDataManager : public KeyedService,
// Default value is false.
bool is_off_the_record_;
- // Whether we have already logged the number of profiles this session.
- mutable bool has_logged_profile_count_;
+ // Whether we have already logged the stored profile metrics this session.
+ mutable bool has_logged_stored_profile_metrics_;
// Whether we have already logged the number of local credit cards this
// session.
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 fea84994c33..990fe2a6b65 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -250,6 +250,7 @@ class PersonalDataManagerTestBase {
bool ImportAddressProfiles(const FormStructure& form) {
return personal_data_->ImportAddressProfiles(form);
}
+
bool ImportCreditCard(
const FormStructure& form,
bool should_return_local_card,
@@ -836,6 +837,27 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCards) {
EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i]));
}
+TEST_F(PersonalDataManagerTest, SavesServerCardType) {
+ EnableWalletCardImport();
+ std::vector<CreditCard> server_cards;
+ server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
+ test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
+ "9012" /* Visa */, "01", "2999", "1");
+ server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+
+ server_cards.back().set_card_type(CreditCard::CARD_TYPE_DEBIT);
+
+ test::SetServerCreditCards(autofill_table_, server_cards);
+ personal_data_->Refresh();
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+ auto cards = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, cards.size());
+
+ EXPECT_EQ(CreditCard::CARD_TYPE_DEBIT, cards.front()->card_type());
+}
+
TEST_F(PersonalDataManagerTest, AddProfilesAndCreditCards) {
AutofillProfile profile0(base::GenerateGUID(), "https://www.example.com");
test::SetProfileInfo(&profile0,
@@ -3616,6 +3638,63 @@ TEST_F(PersonalDataManagerTest,
EXPECT_EQ(2U, suggestions.size());
}
+// Tests that disused profiles are suppressed when supression is enabled and
+// the input field is empty.
+TEST_F(PersonalDataManagerTest,
+ GetProfileSuggestions_SuppressDisusedProfilesOnEmptyField) {
+ base::test::ScopedFeatureList scoped_features;
+ scoped_features.InitAndEnableFeature(kAutofillSuppressDisusedAddresses);
+
+ // 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");
+ profile1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(200));
+ personal_data_->AddProfile(profile1);
+
+ AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile2, "Marion2", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox",
+ "456 Zoo St.\nSecond Line\nThird line", "unit 5",
+ "Hollywood", "CA", "91601", "US", "12345678910");
+ profile2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(20));
+ personal_data_->AddProfile(profile2);
+
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+
+ // Query with empty string only returns profile2.
+ {
+ std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
+ AutofillType(ADDRESS_HOME_STREET_ADDRESS), base::string16(), false,
+ std::vector<ServerFieldType>());
+ EXPECT_EQ(1U, suggestions.size());
+ }
+
+ // Query with prefix for profile1 returns profile1.
+ {
+ std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
+ AutofillType(ADDRESS_HOME_STREET_ADDRESS), base::ASCIIToUTF16("123"),
+ false, std::vector<ServerFieldType>());
+ ASSERT_EQ(1U, suggestions.size());
+ EXPECT_EQ(
+ base::ASCIIToUTF16("123 Zoo St., Second Line, Third line, unit 5"),
+ suggestions[0].value);
+ }
+
+ // Query with prefix for profile2 returns profile2.
+ {
+ std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
+ AutofillType(ADDRESS_HOME_STREET_ADDRESS), base::ASCIIToUTF16("456"),
+ false, std::vector<ServerFieldType>());
+ EXPECT_EQ(1U, suggestions.size());
+ EXPECT_EQ(
+ base::ASCIIToUTF16("456 Zoo St., Second Line, Third line, unit 5"),
+ suggestions[0].value);
+ }
+}
+
// Test that a masked server card is not suggested if more that six numbers have
// been typed in the field.
TEST_F(PersonalDataManagerTest,
@@ -3924,6 +4003,74 @@ TEST_F(PersonalDataManagerTest,
ASSERT_EQ(3U, suggestions.size());
}
+// Tests that server cards will shown bank name when bank name available and
+// feature flag on.
+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.
+ CreditCard credit_card0("287151C8-6AB1-487C-9095-28E80BE5DA15",
+ "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card0, "Clyde Barrow",
+ "347666888555" /* American Express */, "04", "2999",
+ "1");
+ credit_card0.set_use_count(3);
+ credit_card0.set_use_date(AutofillClock::Now() -
+ base::TimeDelta::FromDays(1));
+ personal_data_->AddCreditCard(credit_card0);
+
+ std::vector<CreditCard> server_cards;
+
+ // Add a server card without bank name.
+ server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b459"));
+ test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "2110", "12",
+ "2999", "1");
+ server_cards.back().set_use_count(2);
+ server_cards.back().set_use_date(AutofillClock::Now() -
+ base::TimeDelta::FromDays(1));
+ server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+
+ // Add a server card with bank name.
+ server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b460"));
+ test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "2111", "12",
+ "2999", "1");
+ server_cards.back().set_use_count(1);
+ server_cards.back().set_use_date(AutofillClock::Now() -
+ base::TimeDelta::FromDays(1));
+ server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+ server_cards.back().set_bank_name("Chase");
+
+ test::SetServerCreditCards(autofill_table_, server_cards);
+
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+
+ std::vector<Suggestion> suggestions =
+ personal_data_->GetCreditCardSuggestions(
+ AutofillType(CREDIT_CARD_NUMBER),
+ /* field_contents= */ base::string16());
+ ASSERT_EQ(3U, suggestions.size());
+
+ // Local cards will show network.
+ EXPECT_EQ(
+ base::UTF8ToUTF16(std::string("Amex") + kUTF8MidlineEllipsis + "8555"),
+ suggestions[0].value);
+ // Server card without bank name will show network.
+ EXPECT_EQ(
+ base::UTF8ToUTF16(std::string("Visa") + kUTF8MidlineEllipsis + "2110"),
+ suggestions[1].value);
+ // Server card with bank name will show bank name.
+ EXPECT_EQ(
+ base::UTF8ToUTF16(std::string("Chase") + kUTF8MidlineEllipsis + "2111"),
+ suggestions[2].value);
+}
+
// Tests that only the full server card is kept when deduping with a local
// duplicate of it.
TEST_F(PersonalDataManagerTest,
@@ -4422,6 +4569,190 @@ TEST_F(PersonalDataManagerTest, DontDuplicateFullServerCard) {
EXPECT_FALSE(imported_credit_card_matches_masked_server_credit_card);
}
+TEST_F(PersonalDataManagerTest,
+ Metrics_SubmittedServerCardExpirationStatus_FullServerCardMatch) {
+ EnableWalletCardImport();
+
+ std::vector<CreditCard> server_cards;
+ server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
+ test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
+ "4444333322221111" /* Visa */, "04", "2111", "1");
+
+ test::SetServerCreditCards(autofill_table_, server_cards);
+ personal_data_->Refresh();
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+
+ // A user fills/enters the card's information on a checkout form. Ensure that
+ // an expiration date match is recorded.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Card Number:", "card_number", "4444333322221111",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "04", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Year:", "exp_year", "2111", "text", &field);
+ form.fields.push_back(field);
+
+ base::HistogramTester histogram_tester;
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ bool imported_credit_card_matches_masked_server_credit_card;
+ EXPECT_FALSE(personal_data_->ImportFormData(
+ form_structure, false, &imported_credit_card,
+ &imported_credit_card_matches_masked_server_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+ EXPECT_FALSE(imported_credit_card_matches_masked_server_credit_card);
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.SubmittedServerCardExpirationStatus",
+ AutofillMetrics::FULL_SERVER_CARD_EXPIRATION_DATE_MATCHED, 1);
+}
+
+TEST_F(PersonalDataManagerTest,
+ Metrics_SubmittedServerCardExpirationStatus_FullServerCardMismatch) {
+ EnableWalletCardImport();
+
+ std::vector<CreditCard> server_cards;
+ server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
+ test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
+ "4444333322221111" /* Visa */, "04", "2111", "1");
+
+ test::SetServerCreditCards(autofill_table_, server_cards);
+ personal_data_->Refresh();
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+
+ // A user fills/enters the card's information on a checkout form but changes
+ // the expiration date of the card. Ensure that an expiration date mismatch
+ // is recorded.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Card Number:", "card_number", "4444333322221111",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "04", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Year:", "exp_year", "2345", "text", &field);
+ form.fields.push_back(field);
+
+ base::HistogramTester histogram_tester;
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ bool imported_credit_card_matches_masked_server_credit_card;
+ EXPECT_FALSE(personal_data_->ImportFormData(
+ form_structure, false, &imported_credit_card,
+ &imported_credit_card_matches_masked_server_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+ EXPECT_FALSE(imported_credit_card_matches_masked_server_credit_card);
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.SubmittedServerCardExpirationStatus",
+ AutofillMetrics::FULL_SERVER_CARD_EXPIRATION_DATE_DID_NOT_MATCH, 1);
+}
+
+TEST_F(PersonalDataManagerTest,
+ Metrics_SubmittedServerCardExpirationStatus_MaskedServerCardMatch) {
+ EnableWalletCardImport();
+
+ std::vector<CreditCard> server_cards;
+ server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
+ test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
+ "1111" /* Visa */, "01", "2111", "");
+ server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+
+ test::SetServerCreditCards(autofill_table_, server_cards);
+ personal_data_->Refresh();
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+
+ // A user fills/enters the card's information on a checkout form. Ensure that
+ // an expiration date match is recorded.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Card Number:", "card_number", "4444333322221111",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Year:", "exp_year", "2111", "text", &field);
+ form.fields.push_back(field);
+
+ base::HistogramTester histogram_tester;
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ bool imported_credit_card_matches_masked_server_credit_card;
+ EXPECT_FALSE(personal_data_->ImportFormData(
+ form_structure, false, &imported_credit_card,
+ &imported_credit_card_matches_masked_server_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+ EXPECT_FALSE(imported_credit_card_matches_masked_server_credit_card);
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.SubmittedServerCardExpirationStatus",
+ AutofillMetrics::MASKED_SERVER_CARD_EXPIRATION_DATE_MATCHED, 1);
+}
+
+TEST_F(PersonalDataManagerTest,
+ Metrics_SubmittedServerCardExpirationStatus_MaskedServerCardMismatch) {
+ EnableWalletCardImport();
+
+ std::vector<CreditCard> server_cards;
+ server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
+ test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
+ "1111" /* Visa */, "01", "2111", "");
+ server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+
+ test::SetServerCreditCards(autofill_table_, server_cards);
+ personal_data_->Refresh();
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+
+ // A user fills/enters the card's information on a checkout form but changes
+ // the expiration date of the card. Ensure that an expiration date mismatch
+ // is recorded.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Card Number:", "card_number", "4444333322221111",
+ "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "04", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Year:", "exp_year", "2345", "text", &field);
+ form.fields.push_back(field);
+
+ base::HistogramTester histogram_tester;
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ bool imported_credit_card_matches_masked_server_credit_card;
+ EXPECT_FALSE(personal_data_->ImportFormData(
+ form_structure, false, &imported_credit_card,
+ &imported_credit_card_matches_masked_server_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+ EXPECT_FALSE(imported_credit_card_matches_masked_server_credit_card);
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.SubmittedServerCardExpirationStatus",
+ AutofillMetrics::MASKED_SERVER_CARD_EXPIRATION_DATE_DID_NOT_MATCH, 1);
+}
+
// Tests the SaveImportedProfile method with different profiles to make sure the
// merge logic works correctly.
typedef struct {
@@ -6560,4 +6891,158 @@ TEST_F(PersonalDataManagerTest, RemoveByGUID_ResetsBillingAddress) {
}
}
+TEST_F(PersonalDataManagerTest, LogStoredProfileMetrics_NoStoredProfiles) {
+ base::HistogramTester histogram_tester;
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+ EXPECT_TRUE(personal_data_->GetProfiles().empty());
+ histogram_tester.ExpectTotalCount("Autofill.StoredProfileCount", 1);
+ histogram_tester.ExpectBucketCount("Autofill.StoredProfileCount", 0, 1);
+ histogram_tester.ExpectTotalCount("Autofill.StoredProfileDisusedCount", 0);
+ histogram_tester.ExpectTotalCount("Autofill.DaysSinceLastUse.StoredProfile",
+ 0);
+}
+
+TEST_F(PersonalDataManagerTest, LogStoredProfileMetrics) {
+ // Add a recently used (3 days ago) profile.
+ AutofillProfile profile0(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile0, "Bob", "", "Doe", "", "Fox", "1212 Center.",
+ "Bld. 5", "Orlando", "FL", "32801", "US", "19482937549");
+ profile0.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(3));
+ personal_data_->AddProfile(profile0);
+
+ // Add a profile used a long time (200 days) ago.
+ AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile1, "Seb", "", "Doe", "", "ACME",
+ "1234 Evergreen Terrace", "Bld. 5", "Springfield", "IL",
+ "32801", "US", "15151231234");
+ profile1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(200));
+ personal_data_->AddProfile(profile1);
+
+ // Reload the database, which will log the stored profile counts.
+ base::HistogramTester histogram_tester;
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+
+ EXPECT_EQ(2u, personal_data_->GetProfiles().size());
+ histogram_tester.ExpectTotalCount("Autofill.StoredProfileCount", 1);
+ histogram_tester.ExpectBucketCount("Autofill.StoredProfileCount", 2, 1);
+
+ histogram_tester.ExpectTotalCount("Autofill.StoredProfileDisusedCount", 1);
+ histogram_tester.ExpectBucketCount("Autofill.StoredProfileDisusedCount", 1,
+ 1);
+
+ histogram_tester.ExpectTotalCount("Autofill.DaysSinceLastUse.StoredProfile",
+ 2);
+ histogram_tester.ExpectBucketCount("Autofill.DaysSinceLastUse.StoredProfile",
+ 3, 1);
+ histogram_tester.ExpectBucketCount("Autofill.DaysSinceLastUse.StoredProfile",
+ 200, 1);
+}
+
+TEST_F(PersonalDataManagerTest, RemoveProfilesNotUsedSinceTimestamp) {
+ const char kHistogramName[] = "Autofill.AddressesSuppressedForDisuse";
+ const base::Time kNow = AutofillClock::Now();
+ constexpr size_t kNumProfiles = 10;
+
+ // Setup the profile vectors with last use dates ranging from |now| to 270
+ // days ago, in 30 day increments. Note that the profiles are sorted by
+ // decreasing last use date.
+ std::vector<AutofillProfile> all_profile_data;
+ std::vector<AutofillProfile*> all_profile_ptrs;
+ all_profile_data.reserve(kNumProfiles);
+ all_profile_ptrs.reserve(kNumProfiles);
+ for (size_t i = 0; i < kNumProfiles; ++i) {
+ constexpr base::TimeDelta k30Days = base::TimeDelta::FromDays(30);
+ all_profile_data.emplace_back(base::GenerateGUID(), "https://example.com");
+ all_profile_data.back().set_use_date(kNow - (i * k30Days));
+ all_profile_ptrs.push_back(&all_profile_data.back());
+ }
+
+ // Verify that disused profiles get removed from the end. Note that the last
+ // four profiles have use dates more than 175 days ago.
+ {
+ // Create a working copy of the profile pointers.
+ std::vector<AutofillProfile*> profiles(all_profile_ptrs);
+
+ // The first 6 have use dates more recent than 175 days ago.
+ std::vector<AutofillProfile*> expected_profiles(profiles.begin(),
+ profiles.begin() + 6);
+
+ // Filter the profiles while capturing histograms.
+ base::HistogramTester histogram_tester;
+ PersonalDataManager::RemoveProfilesNotUsedSinceTimestamp(
+ kNow - base::TimeDelta::FromDays(175), &profiles);
+
+ // Validate that we get the expected filtered profiles and histograms.
+ EXPECT_EQ(expected_profiles, profiles);
+ histogram_tester.ExpectTotalCount(kHistogramName, 1);
+ histogram_tester.ExpectBucketCount(kHistogramName, 4, 1);
+ }
+
+ // Reverse the profile order and verify that disused profiles get removed
+ // from the beginning. Note that the first five profiles, post reversal, have
+ // use dates more then 145 days ago.
+ {
+ // Create a reversed working copy of the profile pointers.
+ std::vector<AutofillProfile*> profiles(all_profile_ptrs.rbegin(),
+ all_profile_ptrs.rend());
+
+ // The last 5 profiles have use dates more recent than 145 days ago.
+ std::vector<AutofillProfile*> expected_profiles(profiles.begin() + 5,
+ profiles.end());
+
+ // Filter the profiles while capturing histograms.
+ base::HistogramTester histogram_tester;
+ PersonalDataManager::RemoveProfilesNotUsedSinceTimestamp(
+ kNow - base::TimeDelta::FromDays(145), &profiles);
+
+ // Validate that we get the expected filtered profiles and histograms.
+ EXPECT_EQ(expected_profiles, profiles);
+ histogram_tester.ExpectTotalCount(kHistogramName, 1);
+ histogram_tester.ExpectBucketCount(kHistogramName, 5, 1);
+ }
+
+ // Randomize the profile order and validate that the filtered list retains
+ // that order. Note that the six profiles have use dates more then 115 days
+ // ago.
+ {
+ // A handy constant.
+ const base::Time k115DaysAgo = kNow - base::TimeDelta::FromDays(115);
+
+ // Created a shuffled master copy of the profile pointers.
+ std::vector<AutofillProfile*> shuffled_profiles(all_profile_ptrs);
+ std::random_shuffle(shuffled_profiles.begin(), shuffled_profiles.end());
+
+ // Copy the shuffled profile pointer collections to use as the working set.
+ std::vector<AutofillProfile*> profiles(shuffled_profiles);
+
+ // Filter the profiles while capturing histograms.
+ base::HistogramTester histogram_tester;
+ PersonalDataManager::RemoveProfilesNotUsedSinceTimestamp(k115DaysAgo,
+ &profiles);
+
+ // Validate that we have the right profiles. Iterate of the the shuffled
+ // master copy and the filtered copy at the same time. making sure that the
+ // elements in the filtered copy occur in the same order as the shuffled
+ // master. Along the way, validate that the elements in and out of the
+ // filtered copy have appropriate use dates.
+ EXPECT_EQ(4u, profiles.size());
+ auto it = shuffled_profiles.begin();
+ for (const AutofillProfile* profile : profiles) {
+ for (; it != shuffled_profiles.end() && (*it) != profile; ++it) {
+ EXPECT_LT((*it)->use_date(), k115DaysAgo);
+ }
+ ASSERT_TRUE(it != shuffled_profiles.end());
+ EXPECT_GT(profile->use_date(), k115DaysAgo);
+ ++it;
+ }
+ for (; it != shuffled_profiles.end(); ++it) {
+ EXPECT_LT((*it)->use_date(), k115DaysAgo);
+ }
+
+ // Validate the histograms.
+ histogram_tester.ExpectTotalCount(kHistogramName, 1);
+ histogram_tester.ExpectBucketCount(kHistogramName, 6, 1);
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto
index e84e2025a79..ee556e5b174 100644
--- a/chromium/components/autofill/core/browser/proto/server.proto
+++ b/chromium/components/autofill/core/browser/proto/server.proto
@@ -33,7 +33,7 @@ message AutofillQueryResponseContents {
// 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: 22
+// Next available id: 23
message AutofillUploadContents {
required string client_version = 1;
required fixed64 form_signature = 2;
@@ -98,6 +98,10 @@ message AutofillUploadContents {
// The value of the id attribute, if it differs from the name attribute.
// Otherwise, this field is absent.
optional string id = 21;
+
+ // True iff the user changed generated password. If there was no generation,
+ // the field is absent.
+ optional bool generated_password_changed = 22;
}
// Signature of the form action host (e.g. Hash64Bit("example.com")).
optional fixed64 action_signature = 13;
diff --git a/chromium/components/autofill/core/browser/region_combobox_model.cc b/chromium/components/autofill/core/browser/region_combobox_model.cc
index 977a692de5d..48a7d7a4ac7 100644
--- a/chromium/components/autofill/core/browser/region_combobox_model.cc
+++ b/chromium/components/autofill/core/browser/region_combobox_model.cc
@@ -53,7 +53,7 @@ base::string16 RegionComboboxModel::GetItemAt(int index) {
if (static_cast<size_t>(index) >= regions_.size())
return l10n_util::GetStringUTF16(IDS_AUTOFILL_LOADING_REGIONS);
- if (!regions_[index].first.empty())
+ if (!regions_[index].second.empty())
return base::UTF8ToUTF16(regions_[index].second);
// The separator item. Implemented for platforms that don't yet support
diff --git a/chromium/components/autofill/core/browser/region_combobox_model_unittest.cc b/chromium/components/autofill/core/browser/region_combobox_model_unittest.cc
index a38ca400499..3c0aeb24495 100644
--- a/chromium/components/autofill/core/browser/region_combobox_model_unittest.cc
+++ b/chromium/components/autofill/core/browser/region_combobox_model_unittest.cc
@@ -19,8 +19,10 @@
namespace autofill {
// Strings used in more than one place and must be the same everywhere.
-const char kQuebec[] = "Quebec";
-const char kOntario[] = "Ontario";
+const char kQuebecCode[] = "QC";
+const char kQuebecName[] = "Quebec";
+const char kOntarioCode[] = "ON";
+const char kOntarioName[] = "Ontario";
// Make sure the two regions returned by the source are properly set in the
// model.
@@ -30,15 +32,15 @@ TEST(RegionComboboxModelTest, QuebecOntarioRegions) {
model.LoadRegionData("", &test_region_data_loader, 0);
std::vector<std::pair<std::string, std::string>> regions;
- regions.push_back(std::make_pair("QC", kQuebec));
- regions.push_back(std::make_pair("ON", kOntario));
+ regions.push_back(std::make_pair(kQuebecCode, kQuebecName));
+ regions.push_back(std::make_pair(kOntarioCode, kOntarioName));
test_region_data_loader.SendAsynchronousData(regions);
EXPECT_EQ(3, model.GetItemCount());
EXPECT_EQ(base::ASCIIToUTF16("---"), model.GetItemAt(0));
- EXPECT_EQ(base::ASCIIToUTF16(kQuebec), model.GetItemAt(1));
- EXPECT_EQ(base::ASCIIToUTF16(kOntario), model.GetItemAt(2));
+ EXPECT_EQ(base::ASCIIToUTF16(kQuebecName), model.GetItemAt(1));
+ EXPECT_EQ(base::ASCIIToUTF16(kOntarioName), model.GetItemAt(2));
EXPECT_FALSE(model.failed_to_load_data());
}
diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc
index 61d05df444a..8dde20e85cc 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_client.cc
@@ -145,4 +145,8 @@ void TestAutofillClient::StartSigninFlow() {}
void TestAutofillClient::ShowHttpNotSecureExplanation() {}
+bool TestAutofillClient::IsAutofillSupported() {
+ return true;
+}
+
} // 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 965023d23f9..a0bd9cbdeb4 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.h
+++ b/chromium/components/autofill/core/browser/test_autofill_client.h
@@ -77,6 +77,7 @@ class TestAutofillClient : public AutofillClient {
bool ShouldShowSigninPromo() override;
void StartSigninFlow() override;
void ShowHttpNotSecureExplanation() override;
+ bool IsAutofillSupported() override;
void SetPrefs(std::unique_ptr<PrefService> prefs) {
prefs_ = std::move(prefs);
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 7049f31ff17..48e362f18ee 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
@@ -11,7 +11,6 @@
#include <vector>
#include "base/bind.h"
-#include "base/debug/dump_without_crashing.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/proto/autofill_sync.pb.h"
@@ -26,12 +25,10 @@
using base::Optional;
using base::Time;
-using base::debug::DumpWithoutCrashing;
using sync_pb::AutofillSpecifics;
using syncer::EntityChange;
using syncer::EntityChangeList;
using syncer::EntityData;
-using syncer::EntityDataMap;
using syncer::MetadataChangeList;
using syncer::ModelError;
using syncer::ModelTypeChangeProcessor;
@@ -284,12 +281,10 @@ void AutocompleteSyncBridge::CreateForWebDataServiceAndBackend(
AutofillWebDataService* web_data_service,
AutofillWebDataBackend* web_data_backend) {
web_data_service->GetDBUserData()->SetUserData(
- UserDataKey(),
- base::MakeUnique<AutocompleteSyncBridge>(
- web_data_backend,
- base::BindRepeating(
- &ModelTypeChangeProcessor::Create,
- base::BindRepeating(base::IgnoreResult(&DumpWithoutCrashing)))));
+ UserDataKey(), base::MakeUnique<AutocompleteSyncBridge>(
+ web_data_backend,
+ base::BindRepeating(&ModelTypeChangeProcessor::Create,
+ base::RepeatingClosure())));
}
// static
@@ -326,14 +321,14 @@ AutocompleteSyncBridge::CreateMetadataChangeList() {
Optional<syncer::ModelError> AutocompleteSyncBridge::MergeSyncData(
std::unique_ptr<MetadataChangeList> metadata_change_list,
- EntityDataMap entity_data_map) {
+ EntityChangeList entity_data) {
DCHECK(thread_checker_.CalledOnValidThread());
SyncDifferenceTracker tracker(GetAutofillTable());
- for (auto kv : entity_data_map) {
- DCHECK(kv.second->specifics.has_autofill());
+ for (const auto& change : entity_data) {
+ DCHECK(change.data().specifics.has_autofill());
RETURN_IF_ERROR(tracker.IncorporateRemoteSpecifics(
- kv.first, kv.second->specifics.autofill()));
+ change.storage_key(), change.data().specifics.autofill()));
}
RETURN_IF_ERROR(tracker.FlushToLocal(web_data_backend_));
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 3d3053c6110..75b978d9f59 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
@@ -13,7 +13,6 @@
#include "base/optional.h"
#include "base/scoped_observer.h"
#include "base/supports_user_data.h"
-#include "base/threading/non_thread_safe.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/metadata_change_list.h"
@@ -48,7 +47,7 @@ class AutocompleteSyncBridge : public base::SupportsUserData::Data,
override;
base::Optional<syncer::ModelError> MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityDataMap entity_data_map) override;
+ syncer::EntityChangeList entity_data) override;
base::Optional<syncer::ModelError> ApplySyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) 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 59506b1c911..18a81d558d1 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
@@ -43,7 +43,6 @@ using syncer::EntityChange;
using syncer::EntityChangeList;
using syncer::EntityData;
using syncer::EntityDataPtr;
-using syncer::EntityDataMap;
using syncer::FakeModelTypeChangeProcessor;
using syncer::KeyAndData;
using syncer::ModelError;
@@ -194,7 +193,7 @@ class AutocompleteSyncBridgeTest : public testing::Test {
return key;
}
- EntityChangeList EntityAddList(
+ EntityChangeList CreateEntityAddList(
const std::vector<AutofillSpecifics>& specifics_vector) {
EntityChangeList changes;
for (const auto& specifics : specifics_vector) {
@@ -204,28 +203,19 @@ class AutocompleteSyncBridgeTest : public testing::Test {
return changes;
}
- EntityDataMap CreateEntityDataMap(
- const std::vector<AutofillSpecifics>& specifics_vector) {
- EntityDataMap map;
- for (const auto& specifics : specifics_vector) {
- map[GetStorageKey(specifics)] = SpecificsToEntity(specifics);
- }
- return map;
- }
-
- void VerifyApplyChanges(const std::vector<EntityChange>& changes) {
+ void VerifyApplyChanges(const EntityChangeList& changes) {
const auto error = bridge()->ApplySyncChanges(
bridge()->CreateMetadataChangeList(), changes);
EXPECT_FALSE(error);
}
void VerifyApplyAdds(const std::vector<AutofillSpecifics>& specifics) {
- VerifyApplyChanges(EntityAddList(specifics));
+ VerifyApplyChanges(CreateEntityAddList(specifics));
}
void VerifyMerge(const std::vector<AutofillSpecifics>& specifics) {
const auto error = bridge()->MergeSyncData(
- bridge()->CreateMetadataChangeList(), CreateEntityDataMap(specifics));
+ bridge()->CreateMetadataChangeList(), CreateEntityAddList(specifics));
EXPECT_FALSE(error);
}
diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc
index 816addcbf15..c0e86025a21 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc
@@ -73,7 +73,7 @@ AutocompleteSyncableService::AutocompleteSyncableService(
}
AutocompleteSyncableService::~AutocompleteSyncableService() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// static
@@ -106,7 +106,7 @@ syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
const syncer::SyncDataList& initial_sync_data,
std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
std::unique_ptr<syncer::SyncErrorFactory> error_handler) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!sync_processor_);
DCHECK(sync_processor);
DCHECK(error_handler);
@@ -167,7 +167,7 @@ syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
}
void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(syncer::AUTOFILL, type);
sync_processor_.reset();
@@ -176,7 +176,7 @@ void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
syncer::ModelType type) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sync_processor_);
DCHECK_EQ(type, syncer::AUTOFILL);
@@ -195,7 +195,7 @@ syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sync_processor_);
if (!sync_processor_) {
@@ -290,7 +290,7 @@ bool AutocompleteSyncableService::LoadAutofillData(
bool AutocompleteSyncableService::SaveChangesToWebData(
const std::vector<AutofillEntry>& new_entries) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!GetAutofillTable()->UpdateAutofillEntries(new_entries))
return false;
diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.h b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.h
index 52932be7fe1..c94383b7286 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.h
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.h
@@ -13,8 +13,8 @@
#include "base/macros.h"
#include "base/scoped_observer.h"
+#include "base/sequence_checker.h"
#include "base/supports_user_data.h"
-#include "base/threading/non_thread_safe.h"
#include "components/autofill/core/browser/webdata/autofill_change.h"
#include "components/autofill/core/browser/webdata/autofill_entry.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
@@ -49,8 +49,7 @@ class AutofillTable;
class AutocompleteSyncableService
: public base::SupportsUserData::Data,
public syncer::SyncableService,
- public AutofillWebDataServiceObserverOnDBThread,
- public base::NonThreadSafe {
+ public AutofillWebDataServiceObserverOnDBThread {
public:
~AutocompleteSyncableService() override;
@@ -162,6 +161,8 @@ class AutocompleteSyncableService
syncer::SyncableService::StartSyncFlare flare_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncableService);
};
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
index 1fa55f74027..e67793de69c 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
@@ -63,7 +63,7 @@ AutofillProfileSyncableService::AutofillProfileSyncableService(
}
AutofillProfileSyncableService::~AutofillProfileSyncableService() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// static
@@ -95,7 +95,7 @@ AutofillProfileSyncableService::MergeDataAndStartSyncing(
const syncer::SyncDataList& initial_sync_data,
std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!sync_processor_.get());
DCHECK(sync_processor.get());
DCHECK(sync_error_factory.get());
@@ -205,7 +205,7 @@ AutofillProfileSyncableService::MergeDataAndStartSyncing(
}
void AutofillProfileSyncableService::StopSyncing(syncer::ModelType type) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(type, syncer::AUTOFILL_PROFILE);
sync_processor_.reset();
@@ -216,7 +216,7 @@ void AutofillProfileSyncableService::StopSyncing(syncer::ModelType type) {
syncer::SyncDataList AutofillProfileSyncableService::GetAllSyncData(
syncer::ModelType type) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sync_processor_.get());
DCHECK_EQ(type, syncer::AUTOFILL_PROFILE);
@@ -229,7 +229,7 @@ syncer::SyncDataList AutofillProfileSyncableService::GetAllSyncData(
syncer::SyncError AutofillProfileSyncableService::ProcessSyncChanges(
const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!sync_processor_.get()) {
syncer::SyncError error(FROM_HERE,
syncer::SyncError::DATATYPE_ERROR,
@@ -295,7 +295,7 @@ bool AutofillProfileSyncableService::LoadAutofillData(
bool AutofillProfileSyncableService::SaveChangesToWebData(
const DataBundle& bundle) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AutofillTable* autofill_table = GetAutofillTable();
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h
index bed681d98f9..897204829cd 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service.h
@@ -12,9 +12,9 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/scoped_observer.h"
+#include "base/sequence_checker.h"
#include "base/supports_user_data.h"
#include "base/synchronization/lock.h"
-#include "base/threading/non_thread_safe.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/webdata/autofill_change.h"
#include "components/autofill/core/browser/webdata/autofill_entry.h"
@@ -48,8 +48,7 @@ extern const char kAutofillProfileTag[];
class AutofillProfileSyncableService
: public base::SupportsUserData::Data,
public syncer::SyncableService,
- public AutofillWebDataServiceObserverOnDBThread,
- public base::NonThreadSafe {
+ public AutofillWebDataServiceObserverOnDBThread {
public:
~AutofillProfileSyncableService() override;
@@ -194,6 +193,8 @@ class AutofillProfileSyncableService
syncer::SyncableService::StartSyncFlare flare_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncableService);
};
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
index d98db894172..cba036d2383 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
@@ -193,6 +193,7 @@ std::unique_ptr<CreditCard> CreditCardFromStatement(
base::Time::FromTimeT(s.ColumnInt64(index++)));
credit_card->set_origin(s.ColumnString(index++));
credit_card->set_billing_address_id(s.ColumnString(index++));
+ credit_card->set_bank_name(s.ColumnString(index++));
return credit_card;
}
@@ -472,6 +473,12 @@ bool AutofillTable::MigrateToVersion(int version,
case 72:
*update_compatible_version = true;
return MigrateToVersion72RenameCardTypeToIssuerNetwork();
+ case 73:
+ *update_compatible_version = false;
+ return MigrateToVersion73AddMaskedCardBankName();
+ case 74:
+ *update_compatible_version = false;
+ return MigrateToVersion74AddServerCardTypeColumn();
}
return true;
}
@@ -1215,11 +1222,13 @@ bool AutofillTable::GetServerCreditCards(
"metadata.use_count," // 3
"metadata.use_date," // 4
"network," // 5
- "status," // 6
- "name_on_card," // 7
- "exp_month," // 8
- "exp_year," // 9
- "metadata.billing_address_id " // 10
+ "type," // 6
+ "status," // 7
+ "name_on_card," // 8
+ "exp_month," // 9
+ "exp_year," // 10
+ "metadata.billing_address_id," // 11
+ "bank_name " // 12
"FROM masked_credit_cards masked "
"LEFT OUTER JOIN unmasked_credit_cards USING (id) "
"LEFT OUTER JOIN server_card_metadata metadata USING (id)"));
@@ -1256,11 +1265,18 @@ bool AutofillTable::GetServerCreditCards(
DCHECK_EQ(CreditCard::GetCardNetwork(full_card_number), card_network);
}
+ int card_type = s.ColumnInt(index++);
+ if (card_type >= CreditCard::CARD_TYPE_UNKNOWN &&
+ card_type <= CreditCard::CARD_TYPE_PREPAID) {
+ card->set_card_type(static_cast<CreditCard::CardType>(card_type));
+ }
+
card->SetServerStatus(ServerStatusStringToEnum(s.ColumnString(index++)));
card->SetRawInfo(CREDIT_CARD_NAME_FULL, s.ColumnString16(index++));
card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++));
card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(index++));
card->set_billing_address_id(s.ColumnString(index++));
+ card->set_bank_name(s.ColumnString(index++));
credit_cards->push_back(std::move(card));
}
return s.Succeeded();
@@ -1273,24 +1289,27 @@ void AutofillTable::AddMaskedCreditCards(
db_->GetUniqueStatement("INSERT INTO masked_credit_cards("
"id," // 0
"network," // 1
- "status," // 2
- "name_on_card," // 3
- "last_four," // 4
- "exp_month," // 5
- "exp_year)" // 6
- "VALUES (?,?,?,?,?,?,?)"));
+ "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.BindString(2,
+ masked_insert.BindInt(2, card.card_type());
+ masked_insert.BindString(3,
ServerStatusEnumToString(card.GetServerStatus()));
- masked_insert.BindString16(3, card.GetRawInfo(CREDIT_CARD_NAME_FULL));
- masked_insert.BindString16(4, card.LastFourDigits());
- masked_insert.BindString16(5, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
- masked_insert.BindString16(6,
+ 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);
@@ -1355,12 +1374,12 @@ bool AutofillTable::AddFullServerCreditCard(const CreditCard& credit_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 (?,?,?)"));
+ 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;
@@ -1956,7 +1975,9 @@ bool AutofillTable::InitMaskedCreditCardsTable() {
"network VARCHAR,"
"last_four VARCHAR,"
"exp_month INTEGER DEFAULT 0,"
- "exp_year INTEGER DEFAULT 0)")) {
+ "exp_year INTEGER DEFAULT 0, "
+ "bank_name VARCHAR, "
+ "type INTEGER DEFAULT 0)")) {
NOTREACHED();
return false;
}
@@ -2599,4 +2620,28 @@ bool AutofillTable::MigrateToVersion72RenameCardTypeToIssuerNetwork() {
transaction.Commit();
}
+bool AutofillTable::MigrateToVersion73AddMaskedCardBankName() {
+ // Add the new bank_name column to the masked_credit_cards table.
+ return db_->DoesColumnExist("masked_credit_cards", "bank_name") ||
+ db_->Execute(
+ "ALTER TABLE masked_credit_cards ADD COLUMN bank_name VARCHAR");
+}
+
+bool AutofillTable::MigrateToVersion74AddServerCardTypeColumn() {
+ // Version 73 was actually used by two different schemas; an attempt to add
+ // the "type" column (as in this version 74) was landed and reverted, and then
+ // the "bank_name" column was added (and stuck). Some clients may have been
+ // upgraded to one and some the other. Figure out which is the case.
+ const bool added_type_column_in_v73 =
+ db_->DoesColumnExist("masked_credit_cards", "type");
+
+ // If we previously added the "type" column, then it's already present with
+ // the correct semantics, but we now need to run the "bank_name" migration.
+ // Otherwise, we need to add "type" now.
+ return added_type_column_in_v73 ? MigrateToVersion73AddMaskedCardBankName()
+ : db_->Execute(
+ "ALTER TABLE masked_credit_cards ADD "
+ "COLUMN type INTEGER DEFAULT 0");
+}
+
} // 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 161b033fe69..95634417a01 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h
@@ -162,10 +162,13 @@ struct FormFieldData;
// name_on_card
// network Issuer network of the card. For example, "VISA". Renamed
// from "type" in version 72.
+// type Card type. One of CreditCard::CardType enum values.
+// Added in version 74.
// last_four Last four digits of the card number. For de-duping
// with locally stored cards and generating descriptions.
// exp_month Expiration month: 1-12
// exp_year Four-digit year: 2017
+// bank_name Issuer bank name of the credit card.
//
// unmasked_credit_cards
// When a masked credit credit card is unmasked and the
@@ -464,6 +467,8 @@ class AutofillTable : public WebDatabaseTable,
bool MigrateToVersion70AddSyncMetadata();
bool MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata();
bool MigrateToVersion72RenameCardTypeToIssuerNetwork();
+ bool MigrateToVersion73AddMaskedCardBankName();
+ bool MigrateToVersion74AddServerCardTypeColumn();
// Max data length saved in the table, AKA the maximum length allowed for
// form data.
@@ -473,9 +478,8 @@ class AutofillTable : public WebDatabaseTable,
private:
FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill);
FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddChanges);
- FRIEND_TEST_ALL_PREFIXES(
- AutofillTableTest,
- Autofill_GetCountOfValuesContainedBetween);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ Autofill_GetCountOfValuesContainedBetween);
FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_RemoveBetweenChanges);
FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_UpdateDontReplace);
FRIEND_TEST_ALL_PREFIXES(
@@ -511,10 +515,9 @@ class AutofillTable : public WebDatabaseTable,
// Methods for adding autofill entries at a specified time. For
// testing only.
- bool AddFormFieldValuesTime(
- const std::vector<FormFieldData>& elements,
- std::vector<AutofillChange>* changes,
- base::Time time);
+ bool AddFormFieldValuesTime(const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes,
+ base::Time time);
bool AddFormFieldValueTime(const FormFieldData& element,
std::vector<AutofillChange>* changes,
base::Time time);
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 193021652dd..931f8b48243 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -14,7 +14,6 @@
#include "base/files/scoped_temp_dir.h"
#include "base/guid.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -1762,6 +1761,28 @@ TEST_F(AutofillTableTest, SetServerCardModify) {
outputs.clear();
}
+TEST_F(AutofillTableTest, ServerCardBankName) {
+ // Add a masked card.
+ CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
+ masked_card.SetRawInfo(CREDIT_CARD_NAME_FULL,
+ ASCIIToUTF16("Paul F. Tompkins"));
+ masked_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1"));
+ masked_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020"));
+ masked_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1111"));
+ masked_card.SetNetworkForMaskedCard(kVisaCard);
+ masked_card.set_bank_name("Chase");
+
+ // Set server credit cards
+ std::vector<CreditCard> inputs = {masked_card};
+ test::SetServerCreditCards(table_.get(), inputs);
+
+ // Get server credit cards and check bank names equal
+ std::vector<std::unique_ptr<CreditCard>> outputs;
+ table_->GetServerCreditCards(&outputs);
+ ASSERT_EQ(1u, outputs.size());
+ EXPECT_EQ("Chase", outputs[0]->bank_name());
+}
+
TEST_F(AutofillTableTest, SetServerCardUpdateUsageStatsAndBillingAddress) {
// Add a masked card.
CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
index 261f63bd6ca..7d47212f84f 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
@@ -13,7 +13,6 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_data_model.h"
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 54e8be78009..bbbdba8db9f 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
@@ -35,7 +35,7 @@ void* UserDataKey() {
return reinterpret_cast<void*>(&user_data_key);
}
-const char* CardTypeFromWalletCardType(
+const char* CardNetworkFromWalletCardType(
sync_pb::WalletMaskedCreditCard::WalletCardType type) {
switch (type) {
case sync_pb::WalletMaskedCreditCard::AMEX:
@@ -60,6 +60,20 @@ const char* CardTypeFromWalletCardType(
}
}
+CreditCard::CardType CardTypeFromWalletCardClass(
+ sync_pb::WalletMaskedCreditCard::WalletCardClass card_class) {
+ switch (card_class) {
+ case sync_pb::WalletMaskedCreditCard::CREDIT:
+ return CreditCard::CARD_TYPE_CREDIT;
+ case sync_pb::WalletMaskedCreditCard::DEBIT:
+ return CreditCard::CARD_TYPE_DEBIT;
+ case sync_pb::WalletMaskedCreditCard::PREPAID:
+ return CreditCard::CARD_TYPE_PREPAID;
+ default:
+ return CreditCard::CARD_TYPE_UNKNOWN;
+ }
+}
+
CreditCard::ServerStatus ServerToLocalStatus(
sync_pb::WalletMaskedCreditCard::WalletCardStatus status) {
switch (status) {
@@ -76,12 +90,14 @@ CreditCard CardFromSpecifics(const sync_pb::WalletMaskedCreditCard& card) {
CreditCard result(CreditCard::MASKED_SERVER_CARD, card.id());
result.SetNumber(base::UTF8ToUTF16(card.last_four()));
result.SetServerStatus(ServerToLocalStatus(card.status()));
- result.SetNetworkForMaskedCard(CardTypeFromWalletCardType(card.type()));
+ result.SetNetworkForMaskedCard(CardNetworkFromWalletCardType(card.type()));
+ result.set_card_type(CardTypeFromWalletCardClass(card.card_class()));
result.SetRawInfo(CREDIT_CARD_NAME_FULL,
base::UTF8ToUTF16(card.name_on_card()));
result.SetExpirationMonth(card.exp_month());
result.SetExpirationYear(card.exp_year());
result.set_billing_address_id(card.billing_address_id());
+ result.set_bank_name(card.bank_name());
return result;
}
@@ -98,8 +114,7 @@ AutofillProfile ProfileFromSpecifics(
profile.SetRawInfo(COMPANY_NAME, base::UTF8ToUTF16(address.company_name()));
profile.SetRawInfo(ADDRESS_HOME_STATE,
base::UTF8ToUTF16(address.address_1()));
- profile.SetRawInfo(ADDRESS_HOME_CITY,
- base::UTF8ToUTF16(address.address_2()));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, base::UTF8ToUTF16(address.address_2()));
profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
base::UTF8ToUTF16(address.address_3()));
// AutofillProfile doesn't support address_4 ("sub dependent locality").
@@ -192,11 +207,9 @@ bool SetDataIfChanged(
AutofillWalletSyncableService::AutofillWalletSyncableService(
AutofillWebDataBackend* webdata_backend,
const std::string& app_locale)
- : webdata_backend_(webdata_backend) {
-}
+ : webdata_backend_(webdata_backend) {}
-AutofillWalletSyncableService::~AutofillWalletSyncableService() {
-}
+AutofillWalletSyncableService::~AutofillWalletSyncableService() {}
syncer::SyncMergeResult AutofillWalletSyncableService::MergeDataAndStartSyncing(
syncer::ModelType type,
@@ -342,10 +355,8 @@ syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData(
size_t prev_card_count = 0;
size_t prev_address_count = 0;
bool changed_cards = SetDataIfChanged(
- table, wallet_cards,
- &AutofillTable::GetServerCreditCards,
- &AutofillTable::SetServerCreditCards,
- &prev_card_count);
+ table, wallet_cards, &AutofillTable::GetServerCreditCards,
+ &AutofillTable::SetServerCreditCards, &prev_card_count);
bool changed_addresses = SetDataIfChanged(
table, wallet_addresses, &AutofillTable::GetServerProfiles,
&AutofillTable::SetServerProfiles, &prev_address_count);
diff --git a/chromium/components/autofill/core/common/OWNERS b/chromium/components/autofill/core/common/OWNERS
index bc6e10076c4..c5e2808f24c 100644
--- a/chromium/components/autofill/core/common/OWNERS
+++ b/chromium/components/autofill/core/common/OWNERS
@@ -1,2 +1,4 @@
per-file *password*=dvadym@chromium.org
+per-file *password*=kolos@chromium.org
per-file *password*=vabr@chromium.org
+per-file *password*=vasilii@chromium.org
diff --git a/chromium/components/autofill/core/common/autofill_util.cc b/chromium/components/autofill/core/common/autofill_util.cc
index 50e50176d8c..718f9221275 100644
--- a/chromium/components/autofill/core/common/autofill_util.cc
+++ b/chromium/components/autofill/core/common/autofill_util.cc
@@ -115,6 +115,12 @@ bool FieldIsSuggestionSubstringStartingOnTokenBoundary(
base::string16::npos;
}
+bool IsPrefixOfEmailEndingWithAtSign(const base::string16& full_string,
+ const base::string16& prefix) {
+ return base::StartsWith(full_string, prefix + base::UTF8ToUTF16("@"),
+ base::CompareCase::SENSITIVE);
+}
+
size_t GetTextSelectionStart(const base::string16& suggestion,
const base::string16& field_contents,
bool case_sensitive) {
diff --git a/chromium/components/autofill/core/common/autofill_util.h b/chromium/components/autofill/core/common/autofill_util.h
index 6a67f3ef870..e51d7bd3d4f 100644
--- a/chromium/components/autofill/core/common/autofill_util.h
+++ b/chromium/components/autofill/core/common/autofill_util.h
@@ -56,6 +56,12 @@ bool FieldIsSuggestionSubstringStartingOnTokenBoundary(
const base::string16& field_contents,
bool case_sensitive);
+// Currently, a token for the purposes of this method is defined as {'@'}.
+// Returns true if the |full_string| has a |prefix| as a prefix and the prefix
+// ends on a token.
+bool IsPrefixOfEmailEndingWithAtSign(const base::string16& full_string,
+ const base::string16& prefix);
+
// Finds the first occurrence of a searched substring |field_contents| within
// the string |suggestion| starting at token boundaries and returns the index to
// the end of the located substring, or base::string16::npos if the substring is
diff --git a/chromium/components/autofill/core/common/autofill_util_unittest.cc b/chromium/components/autofill/core/common/autofill_util_unittest.cc
index 6e04dc76031..6a59b0d618f 100644
--- a/chromium/components/autofill/core/common/autofill_util_unittest.cc
+++ b/chromium/components/autofill/core/common/autofill_util_unittest.cc
@@ -70,6 +70,48 @@ INSTANTIATE_TEST_CASE_P(
FieldIsTokenBoundarySubstringCase{"ab", "", false, true},
FieldIsTokenBoundarySubstringCase{"ab", "", true, true}));
+struct AtSignPrefixCase {
+ const char* const field_suggestion;
+ const char* const field_contents;
+ const bool expected_result;
+};
+
+class PrefixEndingOnTokenBoundaryTest
+ : public testing::TestWithParam<AtSignPrefixCase> {};
+
+TEST_P(PrefixEndingOnTokenBoundaryTest, IsPrefixOfEmailEndingWithAtSign) {
+ auto test_case = GetParam();
+ SCOPED_TRACE(testing::Message()
+ << "suggestion = " << test_case.field_suggestion
+ << ", contents = " << test_case.field_contents);
+
+ EXPECT_EQ(test_case.expected_result,
+ IsPrefixOfEmailEndingWithAtSign(
+ base::ASCIIToUTF16(test_case.field_suggestion),
+ base::ASCIIToUTF16(test_case.field_contents)));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ AutofillUtilTest,
+ PrefixEndingOnTokenBoundaryTest,
+ testing::Values(AtSignPrefixCase{"ab@cd.b", "a", false},
+ AtSignPrefixCase{"ab@cd.b", "b", false},
+ AtSignPrefixCase{"ab@cd.b", "Ab", false},
+ AtSignPrefixCase{"ab@cd.b", "cd", false},
+ AtSignPrefixCase{"ab@cd.b", "d", false},
+ AtSignPrefixCase{"ab@cd.b", "b@", false},
+ AtSignPrefixCase{"ab@cd.b", "cd.b", false},
+ AtSignPrefixCase{"ab@cd.b", "b@cd", false},
+ AtSignPrefixCase{"ab@cd.b", "ab@c", false},
+ AtSignPrefixCase{"ba.a.ab", "a.a", false},
+ AtSignPrefixCase{"", "ab", false},
+ AtSignPrefixCase{"ab@c", "ab@", false},
+ AtSignPrefixCase{"ab@cd@g", "ab", true},
+ AtSignPrefixCase{"ab@cd@g", "ab@cd", true},
+ AtSignPrefixCase{"abc", "abc", false},
+ AtSignPrefixCase{"ab", "", false},
+ AtSignPrefixCase{"ab@cd.b", "ab", true}));
+
// Tests for GetTextSelectionStart().
struct GetTextSelectionStartCase {
const char* const field_suggestion;
diff --git a/chromium/components/autofill/core/common/form_data.cc b/chromium/components/autofill/core/common/form_data.cc
index 47137bb7f3b..81de550a22b 100644
--- a/chromium/components/autofill/core/common/form_data.cc
+++ b/chromium/components/autofill/core/common/form_data.cc
@@ -86,6 +86,19 @@ bool FormData::SameFormAs(const FormData& form) const {
return true;
}
+bool FormData::SimilarFormAs(const FormData& form) const {
+ if (name != form.name || origin != form.origin || action != form.action ||
+ is_form_tag != form.is_form_tag ||
+ is_formless_checkout != form.is_formless_checkout ||
+ fields.size() != form.fields.size())
+ return false;
+ for (size_t i = 0; i < fields.size(); ++i) {
+ if (!fields[i].SimilarFieldAs(form.fields[i]))
+ return false;
+ }
+ return true;
+}
+
bool FormData::operator==(const FormData& form) const {
return name == form.name && origin == form.origin && action == form.action &&
is_form_tag == form.is_form_tag &&
diff --git a/chromium/components/autofill/core/common/form_data.h b/chromium/components/autofill/core/common/form_data.h
index a4397fc2a23..5e5a42936f1 100644
--- a/chromium/components/autofill/core/common/form_data.h
+++ b/chromium/components/autofill/core/common/form_data.h
@@ -23,6 +23,10 @@ struct FormData {
// form elements.
bool SameFormAs(const FormData& other) const;
+ // Same as SameFormAs() except calling FormFieldData.SimilarFieldAs() to
+ // compare fields.
+ bool SimilarFormAs(const FormData& other) 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.
diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc
index 48796627fa9..89c35362ef7 100644
--- a/chromium/components/autofill/core/common/form_field_data.cc
+++ b/chromium/components/autofill/core/common/form_field_data.cc
@@ -159,6 +159,12 @@ bool FormFieldData::SameFieldAs(const FormFieldData& field) const {
// should not be considered changes in the structure of the form.
}
+bool FormFieldData::SimilarFieldAs(const FormFieldData& field) const {
+ return label == field.label && name == field.name && id == field.id &&
+ form_control_type == field.form_control_type &&
+ IsCheckable(check_status) == IsCheckable(field.check_status);
+}
+
bool FormFieldData::operator==(const FormFieldData& field) const {
return SameFieldAs(field) && is_autofilled == field.is_autofilled &&
check_status == field.check_status &&
diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h
index a4de0a2236c..1466bedc0cb 100644
--- a/chromium/components/autofill/core/common/form_field_data.h
+++ b/chromium/components/autofill/core/common/form_field_data.h
@@ -56,6 +56,13 @@ struct FormFieldData {
// Returns true if two form fields are the same, not counting the value.
bool SameFieldAs(const FormFieldData& field) const;
+ // SameFieldAs() is a little restricted when field's style changed
+ // dynamically, like css.
+ // This method only compares critical attributes of field to check whether
+ // they are similar enough to be considered as same field if form's
+ // other information isn't changed.
+ bool SimilarFieldAs(const FormFieldData& field) const;
+
// 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 3a7ffa5d321..cc44dd7f494 100644
--- a/chromium/components/autofill/core/common/password_form.cc
+++ b/chromium/components/autofill/core/common/password_form.cc
@@ -64,6 +64,8 @@ void PasswordFormToJSON(const PasswordForm& form,
target->SetBoolean("was_parsed_using_autofill_predictions",
form.was_parsed_using_autofill_predictions);
target->SetString("affiliated_web_realm", form.affiliated_web_realm);
+ target->SetString("app_display_name", form.app_display_name);
+ target->SetString("app_icon_url", form.app_icon_url.possibly_invalid_spec());
target->SetBoolean("does_look_like_signup_form",
form.does_look_like_signup_form);
std::ostringstream submission_event_string_stream;
@@ -134,6 +136,8 @@ bool PasswordForm::operator==(const PasswordForm& form) const {
is_public_suffix_match == form.is_public_suffix_match &&
is_affiliation_based_match == form.is_affiliation_based_match &&
affiliated_web_realm == form.affiliated_web_realm &&
+ app_display_name == form.app_display_name &&
+ app_icon_url == form.app_icon_url &&
does_look_like_signup_form == form.does_look_like_signup_form &&
submission_event == form.submission_event;
}
diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h
index 67c0090d1b2..825fdabcda6 100644
--- a/chromium/components/autofill/core/common/password_form.h
+++ b/chromium/components/autofill/core/common/password_form.h
@@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "base/time/time.h"
@@ -133,11 +134,26 @@ struct PasswordForm {
// The web realm affiliated with the Android application, if the form is an
// Android credential. Otherwise, the string is empty. If there are several
- // realms affiliated with the application, an arbitrary realm is chosen.
- // The field is filled out in PasswordStore's InjectAffiliatedWebRealms.
- // If there was no call of InjectAffiliatedWebRealms, the string is empty.
+ // realms affiliated with the application, an arbitrary realm is chosen. The
+ // field is filled out when the PasswordStore injects affiliation and branding
+ // information, i.e. in InjectAffiliationAndBrandingInformation. If there was
+ // no prior call to this method, the string is empty.
std::string affiliated_web_realm;
+ // The display name (e.g. Play Store name) of the Android application if the
+ // form is an Android credential. Otherwise, the string is empty. The field is
+ // filled out when the PasswordStore injects affiliation and branding
+ // information, i.e. in InjectAffiliationAndBrandingInformation. If there was
+ // no prior call to this method, the string is empty.
+ std::string app_display_name;
+
+ // The icon URL (e.g. Play Store icon URL) of the Android application if the
+ // form is an Android credential. Otherwise, the URL is empty. The field is
+ // filled out when the PasswordStore injects affiliation and branding
+ // information, i.e. in InjectAffiliationAndBrandingInformation. If there was
+ // no prior call to this method, the URL is empty.
+ GURL app_icon_url;
+
// The name of the submit button used. Optional; only used in scoring
// of PasswordForm results from the database to make matches as tight as
// possible.
diff --git a/chromium/components/autofill/ios/OWNERS b/chromium/components/autofill/ios/OWNERS
index bf70425aac7..0e4fd0bc5ad 100644
--- a/chromium/components/autofill/ios/OWNERS
+++ b/chromium/components/autofill/ios/OWNERS
@@ -1,2 +1,2 @@
-jdonnelly@chromium.org
+mahmadi@chromium.org
thestig@chromium.org
diff --git a/chromium/components/autofill/ios/browser/resources/autofill_controller.js b/chromium/components/autofill/ios/browser/resources/autofill_controller.js
index 060db199984..8acb6ff2114 100644
--- a/chromium/components/autofill/ios/browser/resources/autofill_controller.js
+++ b/chromium/components/autofill/ios/browser/resources/autofill_controller.js
@@ -286,8 +286,9 @@ function getUnownedAutofillableFormFieldElements_(elements, fieldsets) {
* It is based on the logic in
* bool ExtractFieldsFromControlElements(
* const WebVector<WebFormControlElement>& control_elements,
+ * const FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
* ExtractMask extract_mask,
- * ScopedVector<FormFieldData>* form_fields,
+ * std::vector<std::unique_ptr<FormFieldData>>* form_fields,
* std::vector<bool>* fields_extracted,
* std::map<WebFormControlElement, FormFieldData*>* element_map)
* in chromium/src/components/autofill/content/renderer/form_autofill_util.cc
@@ -804,10 +805,17 @@ function extractFormsAndFormElements_(frame, minimumRequiredFields, forms) {
}
}
- // Recursively invoke for all frames/iframes.
- var frames = frame.frames;
+ // Recursively invoke for all iframes.
+ var frames = doc.getElementsByTagName("iframe");
for (var i = 0; i < frames.length; i++) {
- extractFormsAndFormElements_(frames[i], minimumRequiredFields, forms);
+ // Check frame origin using only information available to the current frame
+ // (i.e. frames[i].src) to avoid triggering a SecurityError. Skip frames
+ // from different origin since we can't access the DOM elements to autofill.
+ if (!frames[i].src ||
+ __gCrWeb.common.isSameOrigin(frame.location.href, frames[i].src)) {
+ extractFormsAndFormElements_(
+ frames[i].contentWindow, minimumRequiredFields, forms);
+ }
}
};
diff --git a/chromium/components/autofill_strings.grdp b/chromium/components/autofill_strings.grdp
index bea84fef451..16fd55452af 100644
--- a/chromium/components/autofill_strings.grdp
+++ b/chromium/components/autofill_strings.grdp
@@ -371,4 +371,12 @@
<message name="IDS_AUTOFILL_NO_SAVED_ADDRESS" desc="The string to display in comboboxes when no addresses are available.">
No saved addresses
</message>
+
+ <!-- Autofill/Wallet integration preferences -->
+ <message name="IDS_AUTOFILL_WALLET_MANAGEMENT_LINK_TEXT" desc="Text for link that allows users to see and edit their Wallet information." formatter_data="android_java">
+ Edit
+ </message>
+ <message name="IDS_AUTOFILL_FROM_GOOGLE_ACCOUNT_LONG" desc="Text that indicates an address or credit card came from Google servers." formatter_data="android_java">
+ From Google Payments
+ </message>
</grit-part>
diff --git a/chromium/components/background_task_scheduler/BUILD.gn b/chromium/components/background_task_scheduler/BUILD.gn
index e23f4837d71..f77a15cc244 100644
--- a/chromium/components/background_task_scheduler/BUILD.gn
+++ b/chromium/components/background_task_scheduler/BUILD.gn
@@ -31,6 +31,7 @@ if (is_android) {
"android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerGcmNetworkManager.java",
"android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerJobService.java",
"android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefs.java",
+ "android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java",
"android/java/src/org/chromium/components/background_task_scheduler/BundleToPersistableBundleConverter.java",
"android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java",
"android/java/src/org/chromium/components/background_task_scheduler/TaskInfo.java",
@@ -68,6 +69,7 @@ if (is_android) {
"android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskServiceTest.java",
"android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefsTest.java",
"android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerTest.java",
+ "android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java",
"android/junit/src/org/chromium/components/background_task_scheduler/ShadowGcmNetworkManager.java",
"android/junit/src/org/chromium/components/background_task_scheduler/TestBackgroundTask.java",
]
diff --git a/chromium/components/background_task_scheduler/OWNERS b/chromium/components/background_task_scheduler/OWNERS
index 4d5ae7f35ae..5c8a6c0e1d9 100644
--- a/chromium/components/background_task_scheduler/OWNERS
+++ b/chromium/components/background_task_scheduler/OWNERS
@@ -1,4 +1,5 @@
awdf@chromium.org
dtrainor@chromium.org
+fgorski@chromium.org
nyquist@chromium.org
diff --git a/chromium/components/bookmarks/browser/bookmark_codec.cc b/chromium/components/bookmarks/browser/bookmark_codec.cc
index abbb1cd7b64..97ede4fda55 100644
--- a/chromium/components/bookmarks/browser/bookmark_codec.cc
+++ b/chromium/components/bookmarks/browser/bookmark_codec.cc
@@ -56,7 +56,7 @@ BookmarkCodec::BookmarkCodec()
BookmarkCodec::~BookmarkCodec() {}
-base::Value* BookmarkCodec::Encode(BookmarkModel* model) {
+std::unique_ptr<base::Value> BookmarkCodec::Encode(BookmarkModel* model) {
return Encode(model->bookmark_bar_node(),
model->other_node(),
model->mobile_node(),
@@ -64,7 +64,7 @@ base::Value* BookmarkCodec::Encode(BookmarkModel* model) {
model->root_node()->sync_transaction_version());
}
-base::Value* BookmarkCodec::Encode(
+std::unique_ptr<base::Value> BookmarkCodec::Encode(
const BookmarkNode* bookmark_bar_node,
const BookmarkNode* other_folder_node,
const BookmarkNode* mobile_folder_node,
@@ -72,7 +72,7 @@ base::Value* BookmarkCodec::Encode(
int64_t sync_transaction_version) {
ids_reassigned_ = false;
InitializeChecksum();
- base::DictionaryValue* roots = new base::DictionaryValue();
+ auto roots = base::MakeUnique<base::DictionaryValue>();
roots->Set(kRootFolderNameKey, EncodeNode(bookmark_bar_node));
roots->Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node));
roots->Set(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node));
@@ -83,15 +83,15 @@ base::Value* BookmarkCodec::Encode(
roots->SetString(kSyncTransactionVersion,
base::Int64ToString(sync_transaction_version));
}
- base::DictionaryValue* main = new base::DictionaryValue();
+ auto main = base::MakeUnique<base::DictionaryValue>();
main->SetInteger(kVersionKey, kCurrentVersion);
FinalizeChecksum();
// We are going to store the computed checksum. So set stored checksum to be
// the same as computed checksum.
stored_checksum_ = computed_checksum_;
- main->Set(kChecksumKey, new base::Value(computed_checksum_));
- main->Set(kRootsKey, roots);
- return main;
+ main->SetString(kChecksumKey, computed_checksum_);
+ main->Set(kRootsKey, std::move(roots));
+ return std::move(main);
}
bool BookmarkCodec::Decode(BookmarkNode* bb_node,
@@ -137,10 +137,10 @@ std::unique_ptr<base::Value> BookmarkCodec::EncodeNode(
base::Int64ToString(node->date_folder_modified().ToInternalValue()));
UpdateChecksumWithFolderNode(id, title);
- base::ListValue* child_values = new base::ListValue();
- value->Set(kChildrenKey, child_values);
+ auto child_values = base::MakeUnique<base::ListValue>();
for (int i = 0; i < node->child_count(); ++i)
child_values->Append(EncodeNode(node->GetChild(i)));
+ value->Set(kChildrenKey, std::move(child_values));
}
const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
if (meta_info_map)
@@ -153,14 +153,14 @@ std::unique_ptr<base::Value> BookmarkCodec::EncodeNode(
return std::move(value);
}
-base::Value* BookmarkCodec::EncodeMetaInfo(
+std::unique_ptr<base::Value> BookmarkCodec::EncodeMetaInfo(
const BookmarkNode::MetaInfoMap& meta_info_map) {
- base::DictionaryValue* meta_info = new base::DictionaryValue;
+ auto meta_info = base::MakeUnique<base::DictionaryValue>();
for (BookmarkNode::MetaInfoMap::const_iterator it = meta_info_map.begin();
it != meta_info_map.end(); ++it) {
meta_info->SetStringWithoutPathExpansion(it->first, it->second);
}
- return meta_info;
+ return std::move(meta_info);
}
bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
@@ -433,6 +433,11 @@ void BookmarkCodec::DecodeMetaInfoHelper(
const std::string& prefix,
BookmarkNode::MetaInfoMap* meta_info_map) {
for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+ // Deprecated keys should be excluded after removing enhanced bookmarks
+ // feature crrev.com/1638413003.
+ if (base::StartsWith(it.key(), "stars.", base::CompareCase::SENSITIVE))
+ continue;
+
if (it.value().IsType(base::Value::Type::DICTIONARY)) {
const base::DictionaryValue* subdict;
it.value().GetAsDictionary(&subdict);
diff --git a/chromium/components/bookmarks/browser/bookmark_codec.h b/chromium/components/bookmarks/browser/bookmark_codec.h
index 7760826e4f6..313729ef96d 100644
--- a/chromium/components/bookmarks/browser/bookmark_codec.h
+++ b/chromium/components/bookmarks/browser/bookmark_codec.h
@@ -42,15 +42,15 @@ 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.
- base::Value* Encode(BookmarkModel* model);
+ std::unique_ptr<base::Value> Encode(BookmarkModel* model);
- // Encodes the bookmark bar and other folders returning the JSON value. It's
- // up to the caller to delete the returned object.
- base::Value* Encode(const BookmarkNode* bookmark_bar_node,
- const BookmarkNode* other_folder_node,
- const BookmarkNode* mobile_folder_node,
- const BookmarkNode::MetaInfoMap* model_meta_info_map,
- int64_t sync_transaction_version);
+ // Encodes the bookmark bar and other folders returning the JSON value.
+ std::unique_ptr<base::Value> Encode(
+ const BookmarkNode* bookmark_bar_node,
+ const BookmarkNode* other_folder_node,
+ const BookmarkNode* mobile_folder_node,
+ const BookmarkNode::MetaInfoMap* model_meta_info_map,
+ int64_t sync_transaction_version);
// 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,
@@ -112,9 +112,9 @@ class BookmarkCodec {
// Encodes node and all its children into a Value object and returns it.
std::unique_ptr<base::Value> EncodeNode(const BookmarkNode* node);
- // Encodes the given meta info into a Value object and returns it. The caller
- // takes ownership of the returned object.
- base::Value* EncodeMetaInfo(const BookmarkNode::MetaInfoMap& meta_info_map);
+ // Encodes the given meta info into a Value object and returns it.
+ std::unique_ptr<base::Value> EncodeMetaInfo(
+ const BookmarkNode::MetaInfoMap& meta_info_map);
// Helper to perform decoding.
bool DecodeHelper(BookmarkNode* bb_node,
diff --git a/chromium/components/bookmarks/browser/bookmark_model.cc b/chromium/components/bookmarks/browser/bookmark_model.cc
index f9d58ceeafb..689ef752d25 100644
--- a/chromium/components/bookmarks/browser/bookmark_model.cc
+++ b/chromium/components/bookmarks/browser/bookmark_model.cc
@@ -482,7 +482,7 @@ void BookmarkModel::OnFaviconsChanged(const std::set<GURL>& page_urls,
base::AutoLock url_lock(url_lock_);
for (const BookmarkNode* node : nodes_ordered_by_url_set_) {
- if (icon_url == node->icon_url())
+ if (node->icon_url() && icon_url == *node->icon_url())
to_update.insert(node);
}
}
diff --git a/chromium/components/bookmarks/browser/bookmark_node.cc b/chromium/components/bookmarks/browser/bookmark_node.cc
index 8bc1c292318..1977fcb5efd 100644
--- a/chromium/components/bookmarks/browser/bookmark_node.cc
+++ b/chromium/components/bookmarks/browser/bookmark_node.cc
@@ -123,7 +123,7 @@ void BookmarkNode::Initialize(int64_t id) {
}
void BookmarkNode::InvalidateFavicon() {
- icon_url_ = GURL();
+ icon_url_.reset();
favicon_ = gfx::Image();
favicon_type_ = favicon_base::INVALID_ICON;
favicon_state_ = INVALID_FAVICON;
diff --git a/chromium/components/bookmarks/browser/bookmark_node.h b/chromium/components/bookmarks/browser/bookmark_node.h
index d125c44e52c..c3d26e50e15 100644
--- a/chromium/components/bookmarks/browser/bookmark_node.h
+++ b/chromium/components/bookmarks/browser/bookmark_node.h
@@ -10,6 +10,7 @@
#include <memory>
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/time/time.h"
#include "components/bookmarks/browser/titled_url_node.h"
@@ -69,7 +70,7 @@ class BookmarkNode : public ui::TreeNode<BookmarkNode>, public TitledUrlNode {
// Returns the favicon's URL. Returns an empty URL if there is no favicon
// associated with this bookmark.
- const GURL& icon_url() const { return icon_url_; }
+ const GURL* icon_url() const { return icon_url_ ? icon_url_.get() : nullptr; }
Type type() const { return type_; }
void set_type(Type type) { type_ = type; }
@@ -134,7 +135,7 @@ class BookmarkNode : public ui::TreeNode<BookmarkNode>, public TitledUrlNode {
// Sets the favicon's URL.
void set_icon_url(const GURL& icon_url) {
- icon_url_ = icon_url;
+ icon_url_ = base::MakeUnique<GURL>(icon_url);
}
// Returns the favicon. In nearly all cases you should use the method
@@ -179,7 +180,7 @@ class BookmarkNode : public ui::TreeNode<BookmarkNode>, public TitledUrlNode {
favicon_base::IconType favicon_type_;
// The URL of the node's favicon.
- GURL icon_url_;
+ std::unique_ptr<GURL> icon_url_;
// The loading state of the favicon.
FaviconState favicon_state_;
diff --git a/chromium/components/bookmarks/browser/bookmark_storage.cc b/chromium/components/bookmarks/browser/bookmark_storage.cc
index d32a90c8806..ed52b77c2fb 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage.cc
+++ b/chromium/components/bookmarks/browser/bookmark_storage.cc
@@ -153,10 +153,10 @@ BookmarkStorage::BookmarkStorage(
: model_(model),
writer_(profile_path.Append(kBookmarksFileName),
sequenced_task_runner,
- base::TimeDelta::FromMilliseconds(kSaveDelayMS)),
+ base::TimeDelta::FromMilliseconds(kSaveDelayMS),
+ "BookmarkStorage"),
sequenced_task_runner_(sequenced_task_runner),
- weak_factory_(this) {
-}
+ weak_factory_(this) {}
BookmarkStorage::~BookmarkStorage() {
if (writer_.HasPendingWrite())
diff --git a/chromium/components/bookmarks/browser/startup_task_runner_service.cc b/chromium/components/bookmarks/browser/startup_task_runner_service.cc
index 5b9b74139a6..117bb1bca32 100644
--- a/chromium/components/bookmarks/browser/startup_task_runner_service.cc
+++ b/chromium/components/bookmarks/browser/startup_task_runner_service.cc
@@ -17,11 +17,12 @@ StartupTaskRunnerService::StartupTaskRunnerService(
}
StartupTaskRunnerService::~StartupTaskRunnerService() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
scoped_refptr<base::DeferredSequencedTaskRunner>
StartupTaskRunnerService::GetBookmarkTaskRunner() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!bookmark_task_runner_) {
bookmark_task_runner_ =
new base::DeferredSequencedTaskRunner(io_task_runner_);
diff --git a/chromium/components/bookmarks/browser/startup_task_runner_service.h b/chromium/components/bookmarks/browser/startup_task_runner_service.h
index b39819af135..aed0e88f5fd 100644
--- a/chromium/components/bookmarks/browser/startup_task_runner_service.h
+++ b/chromium/components/bookmarks/browser/startup_task_runner_service.h
@@ -7,7 +7,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "components/keyed_service/core/keyed_service.h"
namespace base {
@@ -18,8 +18,7 @@ class SequencedTaskRunner;
namespace bookmarks {
// This service manages the startup task runners.
-class StartupTaskRunnerService : public base::NonThreadSafe,
- public KeyedService {
+class StartupTaskRunnerService : public KeyedService {
public:
explicit StartupTaskRunnerService(
const scoped_refptr<base::SequencedTaskRunner>& io_task_runner);
@@ -41,6 +40,8 @@ class StartupTaskRunnerService : public base::NonThreadSafe,
scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
scoped_refptr<base::DeferredSequencedTaskRunner> bookmark_task_runner_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(StartupTaskRunnerService);
};
diff --git a/chromium/components/bookmarks/browser/titled_url_index.cc b/chromium/components/bookmarks/browser/titled_url_index.cc
index fa9ed7647eb..eb95ed329bd 100644
--- a/chromium/components/bookmarks/browser/titled_url_index.cc
+++ b/chromium/components/bookmarks/browser/titled_url_index.cc
@@ -207,16 +207,10 @@ bool TitledUrlIndex::GetResultsMatchingTerm(
while (i != index_.end() &&
i->first.size() >= term.size() &&
term.compare(0, term.size(), i->first, 0, term.size()) == 0) {
-#if !defined(OS_ANDROID)
- prefix_matches->insert(i->second.begin(), i->second.end());
-#else
- // Work around a bug in the implementation of std::set::insert in the STL
- // used on android (http://crbug.com/367050).
for (TitledUrlNodeSet::const_iterator n = i->second.begin();
- n != i->second.end();
- ++n)
+ n != i->second.end(); ++n) {
prefix_matches->insert(prefix_matches->end(), *n);
-#endif
+ }
++i;
}
if (!first_term) {
diff --git a/chromium/components/bookmarks/browser/titled_url_index.h b/chromium/components/bookmarks/browser/titled_url_index.h
index d6a0fbae8d3..f6df5980d2e 100644
--- a/chromium/components/bookmarks/browser/titled_url_index.h
+++ b/chromium/components/bookmarks/browser/titled_url_index.h
@@ -8,10 +8,10 @@
#include <stddef.h>
#include <map>
-#include <set>
#include <string>
#include <vector>
+#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "components/query_parser/query_parser.h"
@@ -50,7 +50,7 @@ class TitledUrlIndex {
private:
using TitledUrlNodes = std::vector<const TitledUrlNode*>;
- using TitledUrlNodeSet = std::set<const TitledUrlNode*>;
+ using TitledUrlNodeSet = base::flat_set<const TitledUrlNode*>;
using Index = std::map<base::string16, TitledUrlNodeSet>;
// Constructs |sorted_nodes| by copying the matches in |matches| and sorting
diff --git a/chromium/components/bookmarks/browser/titled_url_node_sorter.h b/chromium/components/bookmarks/browser/titled_url_node_sorter.h
index 3327b5980c6..7913ef057fa 100644
--- a/chromium/components/bookmarks/browser/titled_url_node_sorter.h
+++ b/chromium/components/bookmarks/browser/titled_url_node_sorter.h
@@ -5,9 +5,10 @@
#ifndef COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_NODE_SORTER_H_
#define COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_NODE_SORTER_H_
-#include <set>
#include <vector>
+#include "base/containers/flat_set.h"
+
namespace bookmarks {
class TitledUrlNode;
@@ -15,7 +16,7 @@ class TitledUrlNode;
class TitledUrlNodeSorter {
public:
using TitledUrlNodes = std::vector<const TitledUrlNode*>;
- using TitledUrlNodeSet = std::set<const TitledUrlNode*>;
+ using TitledUrlNodeSet = base::flat_set<const TitledUrlNode*>;
virtual ~TitledUrlNodeSorter() {};
diff --git a/chromium/components/browser_sync/BUILD.gn b/chromium/components/browser_sync/BUILD.gn
index 4c2e28742e6..761ca07d9ec 100644
--- a/chromium/components/browser_sync/BUILD.gn
+++ b/chromium/components/browser_sync/BUILD.gn
@@ -33,7 +33,7 @@ static_library("browser_sync") {
"//components/password_manager/sync/browser",
"//components/pref_registry",
"//components/prefs",
- "//components/reading_list/core:flags",
+ "//components/reading_list/features:flags",
"//components/signin/core/browser",
"//components/strings",
"//components/sync_bookmarks",
diff --git a/chromium/components/browser_sync/DEPS b/chromium/components/browser_sync/DEPS
index 83dd8c54333..b81123b156f 100644
--- a/chromium/components/browser_sync/DEPS
+++ b/chromium/components/browser_sync/DEPS
@@ -11,7 +11,7 @@ include_rules = [
"+components/password_manager/sync/browser",
"+components/pref_registry",
"+components/prefs",
- "+components/reading_list/core",
+ "+components/reading_list/features",
"+components/signin/core/browser",
"+components/signin/core/common",
"+components/sync",
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 5f47d6185b4..03f74739635 100644
--- a/chromium/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/chromium/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -27,7 +27,7 @@
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/sync/browser/password_data_type_controller.h"
#include "components/prefs/pref_service.h"
-#include "components/reading_list/core/reading_list_switches.h"
+#include "components/reading_list/features/reading_list_switches.h"
#include "components/sync/base/report_unrecoverable_error.h"
#include "components/sync/device_info/device_info_data_type_controller.h"
#include "components/sync/device_info/local_device_info_provider_impl.h"
@@ -154,48 +154,51 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
sync_service->GetLocalDeviceInfoProvider()));
}
- // Autocomplete sync is enabled by default. Register unless explicitly
- // disabled.
- if (!disabled_types.Has(syncer::AUTOFILL)) {
- if (FeatureList::IsEnabled(switches::kSyncUSSAutocomplete)) {
- sync_service->RegisterDataTypeController(
- base::MakeUnique<autofill::WebDataModelTypeController>(
- syncer::AUTOFILL, sync_client_, db_thread_, web_data_service_,
- base::Bind(
- &autofill::AutocompleteSyncBridge::FromWebDataService)));
- } else {
+ // 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)) {
+ if (FeatureList::IsEnabled(switches::kSyncUSSAutocomplete)) {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<autofill::WebDataModelTypeController>(
+ syncer::AUTOFILL, sync_client_, db_thread_, web_data_service_,
+ base::Bind(
+ &autofill::AutocompleteSyncBridge::FromWebDataService)));
+ } else {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<AutofillDataTypeController>(
+ db_thread_, error_callback, sync_client_, web_data_service_));
+ }
+ }
+
+ // Autofill sync is enabled by default. Register unless explicitly
+ // disabled.
+ if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) {
sync_service->RegisterDataTypeController(
- base::MakeUnique<AutofillDataTypeController>(
+ base::MakeUnique<AutofillProfileDataTypeController>(
db_thread_, error_callback, sync_client_, web_data_service_));
}
- }
- // Autofill sync is enabled by default. Register unless explicitly
- // disabled.
- if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) {
- sync_service->RegisterDataTypeController(
- base::MakeUnique<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(
- base::MakeUnique<AutofillWalletDataTypeController>(
- syncer::AUTOFILL_WALLET_DATA, 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(
+ base::MakeUnique<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(
- base::MakeUnique<AutofillWalletDataTypeController>(
- syncer::AUTOFILL_WALLET_METADATA, 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(
+ base::MakeUnique<AutofillWalletDataTypeController>(
+ syncer::AUTOFILL_WALLET_METADATA, db_thread_, error_callback,
+ sync_client_, web_data_service_));
+ }
}
// Bookmark sync is enabled by default. Register unless explicitly
@@ -206,55 +209,54 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
sync_client_));
}
- const bool history_disabled =
- sync_client_->GetPrefService()->GetBoolean(history_disabled_pref_);
- // TypedUrl sync is enabled by default. Register unless explicitly disabled,
- // or if saving history is disabled.
- if (!disabled_types.Has(syncer::TYPED_URLS) && !history_disabled) {
- if (base::FeatureList::IsEnabled(switches::kSyncUSSTypedURL)) {
- // TODO(gangwu): Register controller here once typed url controller
- // implemented.
- } else {
- sync_service->RegisterDataTypeController(
- base::MakeUnique<TypedUrlDataTypeController>(
- error_callback, sync_client_, history_disabled_pref_));
+ // These features are enabled only if history is not disabled.
+ if (!sync_client_->GetPrefService()->GetBoolean(history_disabled_pref_)) {
+ // TypedUrl sync is enabled by default. Register unless explicitly
+ // disabled.
+ if (!disabled_types.Has(syncer::TYPED_URLS)) {
+ if (base::FeatureList::IsEnabled(switches::kSyncUSSTypedURL)) {
+ // TODO(gangwu): Register controller here once typed url controller
+ // implemented.
+ } else {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<TypedUrlDataTypeController>(
+ error_callback, sync_client_, history_disabled_pref_));
+ }
}
- }
- // Delete directive sync is enabled by default. Register unless full history
- // sync is disabled.
- if (!disabled_types.Has(syncer::HISTORY_DELETE_DIRECTIVES) &&
- !history_disabled) {
- sync_service->RegisterDataTypeController(
- base::MakeUnique<HistoryDeleteDirectivesDataTypeController>(
- error_callback, sync_client_));
- }
+ // Delete directive sync is enabled by default.
+ if (!disabled_types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<HistoryDeleteDirectivesDataTypeController>(
+ error_callback, sync_client_));
+ }
- // Session sync is enabled by default. Register unless explicitly disabled.
- // This is also disabled if the browser history is disabled, because the
- // tab sync data is added to the web history on the server.
- if (!disabled_types.Has(syncer::PROXY_TABS) && !history_disabled) {
- sync_service->RegisterDataTypeController(
- base::MakeUnique<ProxyDataTypeController>(syncer::PROXY_TABS));
- sync_service->RegisterDataTypeController(
- base::MakeUnique<SessionDataTypeController>(
- error_callback, sync_client_,
- sync_service->GetLocalDeviceInfoProvider(),
- history_disabled_pref_));
- }
+ // Session sync is enabled by default. This is disabled if history is
+ // disabled because the tab sync data is added to the web history on the
+ // server.
+ if (!disabled_types.Has(syncer::PROXY_TABS)) {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<ProxyDataTypeController>(syncer::PROXY_TABS));
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<SessionDataTypeController>(
+ error_callback, sync_client_,
+ sync_service->GetLocalDeviceInfoProvider(),
+ history_disabled_pref_));
+ }
- // Favicon sync is enabled by default. Register unless explicitly disabled.
- if (!disabled_types.Has(syncer::FAVICON_IMAGES) &&
- !disabled_types.Has(syncer::FAVICON_TRACKING) && !history_disabled) {
- // crbug/384552. We disable error uploading for this data types for now.
- sync_service->RegisterDataTypeController(
- base::MakeUnique<AsyncDirectoryTypeController>(
- syncer::FAVICON_IMAGES, base::Closure(), sync_client_,
- syncer::GROUP_UI, ui_thread_));
- sync_service->RegisterDataTypeController(
- base::MakeUnique<AsyncDirectoryTypeController>(
- syncer::FAVICON_TRACKING, base::Closure(), sync_client_,
- syncer::GROUP_UI, ui_thread_));
+ // Favicon sync is enabled by default. Register unless explicitly disabled.
+ 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(
+ base::MakeUnique<AsyncDirectoryTypeController>(
+ syncer::FAVICON_IMAGES, base::Closure(), sync_client_,
+ syncer::GROUP_UI, ui_thread_));
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<AsyncDirectoryTypeController>(
+ syncer::FAVICON_TRACKING, base::Closure(), sync_client_,
+ syncer::GROUP_UI, ui_thread_));
+ }
}
// Password sync is enabled by default. Register unless explicitly
diff --git a/chromium/components/browser_sync/profile_sync_service.cc b/chromium/components/browser_sync/profile_sync_service.cc
index cf5f2af870f..513e27529ac 100644
--- a/chromium/components/browser_sync/profile_sync_service.cc
+++ b/chromium/components/browser_sync/profile_sync_service.cc
@@ -32,11 +32,12 @@
#include "components/invalidation/public/invalidation_service.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/json_pref_store.h"
-#include "components/reading_list/core/reading_list_enable_flags.h"
+#include "components/reading_list/features/reading_list_enable_flags.h"
#include "components/signin/core/browser/about_signin_internals.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_metrics.h"
+#include "components/signin/core/common/profile_management_switches.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/base/bind_to_task_runner.h"
#include "components/sync/base/cryptographer.h"
@@ -234,7 +235,7 @@ void ProfileSyncService::Initialize() {
url_request_context_, syncer::SyncStoppedReporter::ResultCallback());
sessions_sync_manager_ = base::MakeUnique<SessionsSyncManager>(
sync_client_->GetSyncSessionsClient(), &sync_prefs_, local_device_.get(),
- std::move(router),
+ router,
base::Bind(&ProfileSyncService::NotifyForeignSessionUpdated,
sync_enabled_weak_factory_.GetWeakPtr()),
base::Bind(&ProfileSyncService::TriggerRefresh,
@@ -786,6 +787,12 @@ void ProfileSyncService::SetFirstSetupComplete() {
}
}
+bool ProfileSyncService::IsSyncConfirmationNeeded() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return (!IsLocalSyncEnabled() && IsSignedIn()) && !IsFirstSetupInProgress() &&
+ !IsFirstSetupComplete() && IsSyncRequested();
+}
+
void ProfileSyncService::UpdateLastSyncedTime() {
sync_prefs_.SetLastSyncedTime(base::Time::Now());
}
@@ -924,8 +931,7 @@ void ProfileSyncService::OnEngineInitialized(
}
// Initialize local device info.
- local_device_->Initialize(cache_guid, signin_scoped_device_id,
- sync_client_->GetBlockingPool());
+ local_device_->Initialize(cache_guid, signin_scoped_device_id);
if (protocol_event_observers_.might_have_observers()) {
engine_->RequestBufferedProtocolEventsAndEnableForwarding();
@@ -950,11 +956,6 @@ void ProfileSyncService::OnEngineInitialized(
crypto_->SetSyncEngine(engine_.get());
crypto_->SetDataTypeManager(data_type_manager_.get());
- // If we have a cached passphrase use it to decrypt/encrypt data now that the
- // backend is initialized. We want to call this before notifying observers in
- // case this operation affects the "passphrase required" status.
- crypto_->ConsumeCachedPassphraseIfPossible();
-
// Auto-start means IsFirstSetupComplete gets set automatically.
if (start_behavior_ == AUTO_START && !IsFirstSetupComplete()) {
// This will trigger a configure if it completes setup.
@@ -1207,10 +1208,6 @@ void ProfileSyncService::OnConfigureDone(
configure_status_ = result.status;
data_type_status_table_ = result.data_type_status_table;
- // We should have cleared our cached passphrase before we get here (in
- // OnEngineInitialized()).
- DCHECK(crypto_->cached_passphrase().empty());
-
if (!sync_configure_start_time_.is_null()) {
if (configure_status_ == DataTypeManager::OK) {
base::Time sync_configure_stop_time = base::Time::Now();
@@ -1295,6 +1292,8 @@ void ProfileSyncService::OnConfigureDone(
return;
}
+ RecordMemoryUsageHistograms();
+
StartSyncingWithServer();
}
@@ -1547,23 +1546,6 @@ void ProfileSyncService::UpdateSelectedTypesHistogram(
}
}
-#if defined(OS_CHROMEOS)
-void ProfileSyncService::RefreshSpareBootstrapToken(
- const std::string& passphrase) {
- syncer::SystemEncryptor encryptor;
- syncer::Cryptographer temp_cryptographer(&encryptor);
- // The first 2 params (hostname and username) doesn't have any effect here.
- syncer::KeyParams key_params = {"localhost", "dummy", passphrase};
-
- std::string bootstrap_token;
- if (!temp_cryptographer.AddKey(key_params)) {
- NOTREACHED() << "Failed to add key to cryptographer.";
- }
- temp_cryptographer.GetBootstrapToken(&bootstrap_token);
- sync_prefs_.SetSpareBootstrapToken(bootstrap_token);
-}
-#endif
-
void ProfileSyncService::OnUserChoseDatatypes(
bool sync_everything,
syncer::ModelTypeSet chosen_types) {
@@ -1937,23 +1919,20 @@ void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
}
void ProfileSyncService::GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (IsSyncRequested() && !password.empty()) {
- crypto_->CachePassphrase(password);
- // Try to consume the passphrase we just cached. If the sync engine
- // is not running yet, the passphrase will remain cached until the
- // engine starts up.
- crypto_->ConsumeCachedPassphraseIfPossible();
- }
-#if defined(OS_CHROMEOS)
- RefreshSpareBootstrapToken(password);
-#endif
if (!IsEngineInitialized() || GetAuthError().state() != AuthError::NONE) {
// Track the fact that we're still waiting for auth to complete.
is_auth_in_progress_ = true;
}
+
+ if (switches::IsAccountConsistencyDiceEnabled() &&
+ oauth2_token_service_->RefreshTokenIsAvailable(account_id)) {
+ // When Dice is enabled, the refresh token may be available before the user
+ // enables sync. Start sync if the refresh token is already available in the
+ // token service when the authenticated account is set.
+ OnRefreshTokenAvailable(account_id);
+ }
}
void ProfileSyncService::GoogleSignedOut(const std::string& account_id,
@@ -2387,6 +2366,16 @@ void ProfileSyncService::ReportPreviousSessionMemoryWarningCount() {
sync_prefs_.SetCleanShutdown(false);
}
+void ProfileSyncService::RecordMemoryUsageHistograms() {
+ ModelTypeSet active_types = GetActiveDataTypes();
+ for (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();
+ }
+}
+
const GURL& ProfileSyncService::sync_service_url() const {
DCHECK(thread_checker_.CalledOnValidThread());
return sync_service_url_;
diff --git a/chromium/components/browser_sync/profile_sync_service.h b/chromium/components/browser_sync/profile_sync_service.h
index a713ca3c734..d9c8da9b83b 100644
--- a/chromium/components/browser_sync/profile_sync_service.h
+++ b/chromium/components/browser_sync/profile_sync_service.h
@@ -25,7 +25,6 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
-#include "components/keyed_service/core/keyed_service.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
#include "components/signin/core/browser/signin_manager_base.h"
#include "components/sync/base/experiments.h"
@@ -170,7 +169,6 @@ class ProfileSyncService : public syncer::SyncServiceBase,
public syncer::SyncPrefObserver,
public syncer::DataTypeManagerObserver,
public syncer::UnrecoverableErrorHandler,
- public KeyedService,
public OAuth2TokenService::Consumer,
public OAuth2TokenService::Observer,
public SigninManagerBase::Observer,
@@ -384,8 +382,7 @@ class ProfileSyncService : public syncer::SyncServiceBase,
// SigninManagerBase::Observer implementation.
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSignedOut(const std::string& account_id,
const std::string& username) override;
@@ -437,6 +434,10 @@ class ProfileSyncService : public syncer::SyncServiceBase,
// 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.
+ 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;
@@ -681,12 +682,6 @@ class ProfileSyncService : public syncer::SyncServiceBase,
bool sync_everything,
const syncer::ModelTypeSet chosen_types) const;
-#if defined(OS_CHROMEOS)
- // Refresh spare sync bootstrap token for re-enabling the sync service.
- // Called on successful sign-in notifications.
- void RefreshSpareBootstrapToken(const std::string& passphrase);
-#endif
-
// Internal unrecoverable error handler. Used to track error reason via
// Sync.UnrecoverableErrors histogram.
void OnInternalUnrecoverableError(const tracked_objects::Location& from_here,
@@ -723,6 +718,9 @@ class ProfileSyncService : public syncer::SyncServiceBase,
// Check if previous shutdown is shutdown cleanly.
void ReportPreviousSessionMemoryWarningCount();
+ // Estimates and records memory usage histograms per type.
+ void RecordMemoryUsageHistograms();
+
// After user switches to custom passphrase encryption a set of steps needs to
// be performed:
//
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 469864650b5..54135dd7702 100644
--- a/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -196,11 +196,11 @@ class MockAutofillBackend : public autofill::AutofillWebDataBackend {
autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {}
void RemoveExpiredFormElements() override {}
void NotifyOfMultipleAutofillChanges() override {
- DCHECK(!ui_thread_->RunsTasksOnCurrentThread());
+ DCHECK(!ui_thread_->RunsTasksInCurrentSequence());
ui_thread_->PostTask(FROM_HERE, on_changed_);
}
void NotifyThatSyncHasStarted(syncer::ModelType model_type) override {
- DCHECK(!ui_thread_->RunsTasksOnCurrentThread());
+ DCHECK(!ui_thread_->RunsTasksInCurrentSequence());
ui_thread_->PostTask(FROM_HERE, base::Bind(on_sync_started_, model_type));
}
@@ -332,7 +332,7 @@ class WebDataServiceFake : public AutofillWebDataService {
void CreateSyncableService(
const base::Closure& on_changed_callback,
const base::Callback<void(syncer::ModelType)>& on_sync_started) {
- ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread());
+ ASSERT_TRUE(db_thread_->RunsTasksInCurrentSequence());
// These services are deleted in DestroySyncableService().
backend_ = base::MakeUnique<MockAutofillBackend>(
GetDatabase(), on_changed_callback, on_sync_started, ui_thread_.get());
@@ -350,7 +350,7 @@ class WebDataServiceFake : public AutofillWebDataService {
}
void DestroySyncableService() {
- ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread());
+ ASSERT_TRUE(db_thread_->RunsTasksInCurrentSequence());
autocomplete_syncable_service_ = nullptr;
autofill_profile_syncable_service_ = nullptr;
backend_.reset();
@@ -740,7 +740,7 @@ class FakeServerUpdater : public base::RefCountedThreadSafe<FakeServerUpdater> {
void Update() {
// This gets called in a modelsafeworker thread.
- ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread());
+ ASSERT_TRUE(db_thread_->RunsTasksInCurrentSequence());
syncer::UserShare* user_share = service_->GetUserShare();
syncer::syncable::Directory* directory = user_share->directory.get();
@@ -789,7 +789,7 @@ class FakeServerUpdater : public base::RefCountedThreadSafe<FakeServerUpdater> {
void CreateNewEntry(const AutofillEntry& entry) {
entry_ = entry;
- ASSERT_FALSE(db_thread_->RunsTasksOnCurrentThread());
+ ASSERT_FALSE(db_thread_->RunsTasksInCurrentSequence());
if (!db_thread_->PostTask(FROM_HERE,
base::Bind(&FakeServerUpdater::Update, this))) {
NOTREACHED() << "Failed to post task to the db thread.";
diff --git a/chromium/components/browser_sync/profile_sync_service_mock.h b/chromium/components/browser_sync/profile_sync_service_mock.h
index 35191d35e42..1df3ffb80c6 100644
--- a/chromium/components/browser_sync/profile_sync_service_mock.h
+++ b/chromium/components/browser_sync/profile_sync_service_mock.h
@@ -76,6 +76,7 @@ class ProfileSyncServiceMock : public ProfileSyncService {
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));
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 9d3e951e6d1..2b603cc3ab5 100644
--- a/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc
@@ -44,7 +44,6 @@ namespace {
const char kGaiaId[] = "12345";
const char kEmail[] = "test_user@gmail.com";
-const char kDummyPassword[] = "foobar";
class SyncServiceObserverMock : public syncer::SyncServiceObserver {
public:
@@ -119,13 +118,14 @@ class ProfileSyncServiceStartupTest : public testing::Test {
kEmail);
pref_service()->SetString(prefs::kGoogleServicesAccountId, account_id);
#if !defined(OS_CHROMEOS)
+ const char kDummyPassword[] = "foobar";
profile_sync_service_bundle_.signin_manager()->SignIn(kGaiaId, kEmail,
kDummyPassword);
#else
profile_sync_service_bundle_.signin_manager()->SetAuthenticatedAccountInfo(
kGaiaId, kEmail);
if (sync_service)
- sync_service->GoogleSigninSucceeded(account_id, kEmail, kDummyPassword);
+ sync_service->GoogleSigninSucceeded(account_id, kEmail);
#endif
return account_id;
}
@@ -195,6 +195,9 @@ TEST_F(ProfileSyncServiceStartupTest, StartFirstTime) {
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.
std::string account_id = SimulateTestUserSignin(sync_service_.get());
ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
diff --git a/chromium/components/browser_sync/profile_sync_service_unittest.cc b/chromium/components/browser_sync/profile_sync_service_unittest.cc
index 4dd3cb85a24..d5aa12bb29d 100644
--- a/chromium/components/browser_sync/profile_sync_service_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_unittest.cc
@@ -60,7 +60,7 @@ class FakeDataTypeManager : public syncer::DataTypeManager {
explicit FakeDataTypeManager(const ConfigureCalled& configure_called)
: configure_called_(configure_called) {}
- ~FakeDataTypeManager() override{};
+ ~FakeDataTypeManager() override {}
void Configure(syncer::ModelTypeSet desired_types,
syncer::ConfigureReason reason) override {
@@ -72,7 +72,7 @@ class FakeDataTypeManager : public syncer::DataTypeManager {
void ResetDataTypeErrors() override {}
void PurgeForMigration(syncer::ModelTypeSet undesired_types,
syncer::ConfigureReason reason) override {}
- void Stop() override{};
+ void Stop() override {}
ModelTypeSet GetActiveDataTypes() const override { return ModelTypeSet(); }
bool IsNigoriEnabled() const override { return true; }
State state() const override { return syncer::DataTypeManager::CONFIGURED; }
@@ -222,6 +222,7 @@ class ProfileSyncServiceTest : public ::testing::Test {
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_ = base::MakeUnique<ProfileSyncService>(std::move(init_params));
service_->RegisterDataTypeController(
@@ -392,6 +393,7 @@ TEST_F(ProfileSyncServiceTest, SuccessfulLocalBackendInitialization) {
InitializeForNthSync();
EXPECT_FALSE(service()->IsManaged());
EXPECT_TRUE(service()->IsSyncActive());
+ EXPECT_FALSE(service()->IsSyncConfirmationNeeded());
}
// Verify that an initialization where first setup is not complete does not
@@ -401,12 +403,14 @@ TEST_F(ProfileSyncServiceTest, NeedsConfirmation) {
base::MakeUnique<base::Value>(false));
IssueTestTokens();
CreateService(ProfileSyncService::MANUAL_START);
+
syncer::SyncPrefs sync_prefs(prefs());
base::Time now = base::Time::Now();
sync_prefs.SetLastSyncedTime(now);
sync_prefs.SetKeepEverythingSynced(true);
service()->Initialize();
EXPECT_FALSE(service()->IsSyncActive());
+ EXPECT_TRUE(service()->IsSyncConfirmationNeeded());
// The last sync time shouldn't be cleared.
// TODO(zea): figure out a way to check that the directory itself wasn't
diff --git a/chromium/components/browser_sync/profile_sync_test_util.cc b/chromium/components/browser_sync/profile_sync_test_util.cc
index 575271243a2..c482bfbeb64 100644
--- a/chromium/components/browser_sync/profile_sync_test_util.cc
+++ b/chromium/components/browser_sync/profile_sync_test_util.cc
@@ -16,8 +16,8 @@
#include "components/signin/core/browser/signin_manager_base.h"
#include "components/sync/base/sync_prefs.h"
#include "components/sync/driver/signin_manager_wrapper.h"
-#include "components/sync/engine/browser_thread_model_worker.h"
#include "components/sync/engine/passive_model_worker.h"
+#include "components/sync/engine/sequenced_model_worker.h"
#include "components/sync/engine/ui_model_worker.h"
#include "net/url_request/url_request_test_util.h"
@@ -130,10 +130,9 @@ BundleSyncClient::CreateModelWorkerForGroup(syncer::ModelSafeGroup group) {
DCHECK(file_thread_) << "DB thread was specified but FILE thread was not.";
switch (group) {
case syncer::GROUP_DB:
- return new syncer::BrowserThreadModelWorker(db_thread_, syncer::GROUP_DB);
+ return new syncer::SequencedModelWorker(db_thread_, syncer::GROUP_DB);
case syncer::GROUP_FILE:
- return new syncer::BrowserThreadModelWorker(file_thread_,
- syncer::GROUP_FILE);
+ return new syncer::SequencedModelWorker(file_thread_, syncer::GROUP_FILE);
case syncer::GROUP_UI:
return new syncer::UIModelWorker(base::ThreadTaskRunnerHandle::Get());
case syncer::GROUP_PASSIVE:
diff --git a/chromium/components/browser_watcher/BUILD.gn b/chromium/components/browser_watcher/BUILD.gn
index 483a3bfce6e..9b16d7f3413 100644
--- a/chromium/components/browser_watcher/BUILD.gn
+++ b/chromium/components/browser_watcher/BUILD.gn
@@ -88,6 +88,8 @@ if (is_win) {
"stability_data_names.h",
"stability_debugging.cc",
"stability_debugging.h",
+ "stability_metrics.cc",
+ "stability_metrics.h",
"stability_paths.cc",
"stability_paths.h",
]
@@ -105,6 +107,8 @@ if (is_win) {
"postmortem_minidump_writer_win_unittest.cc",
"postmortem_report_collector_unittest.cc",
"stability_debugging_win_unittest.cc",
+ "stability_paths_unittest.cc",
+ "stability_report_extractor_unittest.cc",
"system_session_analyzer_win_unittest.cc",
"watcher_client_win_unittest.cc",
"watcher_metrics_provider_win_unittest.cc",
diff --git a/chromium/components/browser_watcher/features.cc b/chromium/components/browser_watcher/features.cc
index 399e49b9c17..76ecd82a54c 100644
--- a/chromium/components/browser_watcher/features.cc
+++ b/chromium/components/browser_watcher/features.cc
@@ -11,4 +11,6 @@ const base::Feature kStabilityDebuggingFeature{
const char kInitFlushParam[] = "init_flush";
+const char kCollectPostmortemParam[] = "collect_postmortem";
+
} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/features.h b/chromium/components/browser_watcher/features.h
index 8f3503a6c3f..5f067f67ae0 100644
--- a/chromium/components/browser_watcher/features.h
+++ b/chromium/components/browser_watcher/features.h
@@ -17,6 +17,10 @@ extern const base::Feature kStabilityDebuggingFeature;
// flush.
extern const char kInitFlushParam[];
+// Name of an experiment parameter that controls whether to collect postmortem
+// reports.
+extern const char kCollectPostmortemParam[];
+
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_FEATURES_H_
diff --git a/chromium/components/browser_watcher/postmortem_report_collector.cc b/chromium/components/browser_watcher/postmortem_report_collector.cc
index 8929fe46fc1..80ce8c75d98 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector.cc
+++ b/chromium/components/browser_watcher/postmortem_report_collector.cc
@@ -6,7 +6,6 @@
#include <utility>
-#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
@@ -60,102 +59,58 @@ void LogCollectionStatus(CollectionStatus status) {
} // namespace
+void PostmortemDeleter::Process(
+ const std::vector<base::FilePath>& stability_files) {
+ for (const FilePath& file : stability_files) {
+ if (base::DeleteFile(file, false))
+ LogCollectionStatus(UNCLEAN_SHUTDOWN);
+ else
+ LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
+ }
+}
+
PostmortemReportCollector::PostmortemReportCollector(
const std::string& product_name,
const std::string& version_number,
const std::string& channel_name,
+ crashpad::CrashReportDatabase* report_database,
SystemSessionAnalyzer* analyzer)
: product_name_(product_name),
version_number_(version_number),
channel_name_(channel_name),
- system_session_analyzer_(analyzer) {}
-
-PostmortemReportCollector::~PostmortemReportCollector() {}
-
-int PostmortemReportCollector::CollectAndSubmitAllPendingReports(
- const base::FilePath& debug_info_dir,
- const base::FilePath::StringType& debug_file_pattern,
- const std::set<base::FilePath>& excluded_debug_files,
- crashpad::CrashReportDatabase* report_database) {
- DCHECK_NE(true, debug_info_dir.empty());
- DCHECK_NE(true, debug_file_pattern.empty());
+ report_database_(report_database),
+ system_session_analyzer_(analyzer) {
DCHECK_NE(nullptr, report_database);
+}
- // Collect the list of files to harvest.
- std::vector<FilePath> debug_files = GetDebugStateFilePaths(
- debug_info_dir, debug_file_pattern, excluded_debug_files);
- UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount",
- debug_files.size());
+PostmortemReportCollector::~PostmortemReportCollector() {}
+void PostmortemReportCollector::Process(
+ const std::vector<base::FilePath>& stability_files) {
// Determine the crashpad client id.
crashpad::UUID client_id;
- crashpad::Settings* settings = report_database->GetSettings();
+ crashpad::Settings* settings = report_database_->GetSettings();
if (settings) {
// If GetSettings() or GetClientID() fails client_id will be left at its
// default value, all zeroes, which is appropriate.
settings->GetClientID(&client_id);
}
- // Number of unclean shutdowns based on successful collection of stability
- // reports.
- int unclean_cnt = 0;
- // Number of unclean shutdowns that may be attributable to system instability
- // based on successful collection of stability reports. This number should be
- // smaller or equal to |unclean_cnt|.
- int unclean_system_cnt = 0;
-
- // Process each stability file.
- for (const FilePath& file : debug_files) {
- bool system_unclean = false;
- if (CollectAndSubmitOneReport(client_id, file, report_database,
- &system_unclean)) {
- ++unclean_cnt;
- if (system_unclean)
- ++unclean_system_cnt;
- }
- }
-
- UMA_STABILITY_HISTOGRAM_COUNTS_100(
- "ActivityTracker.Collect.UncleanShutdownCount", unclean_cnt);
- UMA_STABILITY_HISTOGRAM_COUNTS_100(
- "ActivityTracker.Collect.UncleanSystemCount", unclean_system_cnt);
-
- return unclean_cnt;
-}
-
-std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths(
- const FilePath& debug_info_dir,
- const FilePath::StringType& debug_file_pattern,
- const std::set<FilePath>& excluded_debug_files) {
- DCHECK_NE(true, debug_info_dir.empty());
- DCHECK_NE(true, debug_file_pattern.empty());
-
- std::vector<FilePath> paths;
- base::FileEnumerator enumerator(debug_info_dir, false /* recursive */,
- base::FileEnumerator::FILES,
- debug_file_pattern);
- FilePath path;
- for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
- if (excluded_debug_files.find(path) == excluded_debug_files.end())
- paths.push_back(path);
+ for (const FilePath& file : stability_files) {
+ CollectAndSubmitOneReport(client_id, file);
}
- return paths;
}
-bool PostmortemReportCollector::CollectAndSubmitOneReport(
+void PostmortemReportCollector::CollectAndSubmitOneReport(
const crashpad::UUID& client_id,
- const FilePath& file,
- crashpad::CrashReportDatabase* report_database,
- bool* system_unclean) {
- DCHECK_NE(nullptr, report_database);
- DCHECK_NE(nullptr, system_unclean);
- *system_unclean = false;
+ const FilePath& file) {
+ DCHECK_NE(nullptr, report_database_);
+ LogCollectionStatus(COLLECTION_ATTEMPT);
// Note: the code below involves two notions of report: chrome internal state
// reports and the crashpad reports they get wrapped into.
- // Collect the data from the debug file to a proto. Note: a non-empty report
- // is interpreted here as an unclean exit.
+ // Collect the data from the debug file to a proto.
StabilityReport report_proto;
CollectionStatus status = CollectOneReport(file, &report_proto);
if (status != SUCCESS) {
@@ -164,41 +119,41 @@ bool PostmortemReportCollector::CollectAndSubmitOneReport(
if (!base::DeleteFile(file, false))
DLOG(ERROR) << "Failed to delete " << file.value();
LogCollectionStatus(status);
- return false;
+ return;
}
+ // Delete the stability file. If the file cannot be deleted, do not report its
+ // contents - it will be retried in a future processing run. Note that this
+ // approach can lead to under reporting and retries. However, under reporting
+ // is preferable to the over reporting that would happen with a file that
+ // cannot be deleted. Also note that the crash registration may still fail at
+ // this point: losing the report in such a case is deemed acceptable.
+ if (!base::DeleteFile(file, false)) {
+ DLOG(ERROR) << "Failed to delete " << file.value();
+ LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
+ return;
+ }
+
+ LogCollectionStatus(UNCLEAN_SHUTDOWN);
+ if (report_proto.system_state().session_state() == SystemState::UNCLEAN)
+ LogCollectionStatus(UNCLEAN_SESSION);
+
// Prepare a crashpad report.
CrashReportDatabase::NewReport* new_report = nullptr;
CrashReportDatabase::OperationStatus database_status =
- report_database->PrepareNewCrashReport(&new_report);
+ report_database_->PrepareNewCrashReport(&new_report);
if (database_status != CrashReportDatabase::kNoError) {
- // Assume this is recoverable: not deleting the file.
- DLOG(ERROR) << "PrepareNewCrashReport failed";
LogCollectionStatus(PREPARE_NEW_CRASH_REPORT_FAILED);
- return false;
+ return;
}
CrashReportDatabase::CallErrorWritingCrashReport
- call_error_writing_crash_report(report_database, new_report);
+ call_error_writing_crash_report(report_database_, new_report);
// Write the report to a minidump.
if (!WriteReportToMinidump(&report_proto, client_id, new_report->uuid,
reinterpret_cast<FILE*>(new_report->handle))) {
- // Assume this is not recoverable and delete the file.
- if (!base::DeleteFile(file, false))
- DLOG(ERROR) << "Failed to delete " << file.value();
LogCollectionStatus(WRITE_TO_MINIDUMP_FAILED);
- return false;
- }
-
- // If the file cannot be deleted, do not report its contents. Note this can
- // lead to under reporting and retries. However, under reporting is
- // preferable to the over reporting that would happen with a file that
- // cannot be deleted.
- // TODO(manzagop): metrics for the number of non-deletable files.
- if (!base::DeleteFile(file, false)) {
- DLOG(ERROR) << "Failed to delete " << file.value();
- LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
- return false;
+ return;
}
// Finalize the report wrt the report database. Note that this doesn't trigger
@@ -206,19 +161,14 @@ bool PostmortemReportCollector::CollectAndSubmitOneReport(
// writing, the delay is on the order of up to 15 minutes).
call_error_writing_crash_report.Disarm();
crashpad::UUID unused_report_id;
- database_status = report_database->FinishedWritingCrashReport(
+ database_status = report_database_->FinishedWritingCrashReport(
new_report, &unused_report_id);
if (database_status != CrashReportDatabase::kNoError) {
- DLOG(ERROR) << "FinishedWritingCrashReport failed";
LogCollectionStatus(FINISHED_WRITING_CRASH_REPORT_FAILED);
- return false;
+ return;
}
- // Report collection is successful. We may increment |unclean_system_cnt|.
- if (report_proto.system_state().session_state() == SystemState::UNCLEAN)
- *system_unclean = true;
-
- return true;
+ LogCollectionStatus(SUCCESS);
}
CollectionStatus PostmortemReportCollector::CollectOneReport(
diff --git a/chromium/components/browser_watcher/postmortem_report_collector.h b/chromium/components/browser_watcher/postmortem_report_collector.h
index 01871812ede..436c16d49f0 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector.h
+++ b/chromium/components/browser_watcher/postmortem_report_collector.h
@@ -28,6 +28,15 @@
namespace browser_watcher {
+// Deletes stability files.
+class PostmortemDeleter {
+ public:
+ PostmortemDeleter() = default;
+ ~PostmortemDeleter() = default;
+
+ void Process(const std::vector<base::FilePath>& stability_files);
+};
+
// Handles postmortem report collection by establishing the set of stability
// files to collect, then for each file:
// - extracting a report protocol buffer
@@ -39,20 +48,13 @@ class PostmortemReportCollector {
PostmortemReportCollector(const std::string& product_name,
const std::string& version_number,
const std::string& channel_name,
+ crashpad::CrashReportDatabase* report_database,
SystemSessionAnalyzer* analyzer);
- virtual ~PostmortemReportCollector();
-
- // Collects postmortem stability reports from files found in |debug_info_dir|,
- // relying on |debug_file_pattern| and |excluded_debug_files|. Reports are
- // then wrapped in Crashpad reports, manufactured via |report_database|.
- // Returns the number crash reports successfully registered with the reporter.
- // TODO(manzagop): consider mechanisms for partial collection if this is to be
- // used on a critical path.
- int CollectAndSubmitAllPendingReports(
- const base::FilePath& debug_info_dir,
- const base::FilePath::StringType& debug_file_pattern,
- const std::set<base::FilePath>& excluded_debug_files,
- crashpad::CrashReportDatabase* report_database);
+ ~PostmortemReportCollector();
+
+ // Collects postmortem stability reports from |stability_files|. Reports are
+ // then wrapped in Crashpad reports and registered with the crash database.
+ void Process(const std::vector<base::FilePath>& stability_files);
const std::string& product_name() const { return product_name_; }
const std::string& version_number() const { return version_number_; }
@@ -81,18 +83,10 @@ class PostmortemReportCollector {
PostmortemReportCollectorCollectionFromGlobalTrackerTest,
SystemStateTest);
- // Virtual for unittesting.
- virtual std::vector<base::FilePath> GetDebugStateFilePaths(
- const base::FilePath& debug_info_dir,
- const base::FilePath::StringType& debug_file_pattern,
- const std::set<base::FilePath>& excluded_debug_files);
-
// Collects a stability file, generates a report and registers it with the
- // database. Returns true on success. False otherwise.
- bool CollectAndSubmitOneReport(const crashpad::UUID& client_id,
- const base::FilePath& file,
- crashpad::CrashReportDatabase* report_database,
- bool* system_unclean);
+ // database.
+ void CollectAndSubmitOneReport(const crashpad::UUID& client_id,
+ const base::FilePath& file);
virtual CollectionStatus CollectOneReport(
const base::FilePath& stability_file,
@@ -111,6 +105,7 @@ class PostmortemReportCollector {
std::string version_number_;
std::string channel_name_;
+ crashpad::CrashReportDatabase* report_database_; // Not owned.
SystemSessionAnalyzer* system_session_analyzer_; // Not owned.
DISALLOW_COPY_AND_ASSIGN(PostmortemReportCollector);
diff --git a/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc b/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
index 791a6cb070b..9214b6f8801 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
+++ b/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
@@ -40,6 +40,7 @@ using base::debug::ActivityUserData;
using base::debug::GlobalActivityTracker;
using base::debug::ThreadActivityTracker;
using base::File;
+using base::FilePath;
using base::FilePersistentMemoryAllocator;
using base::MemoryMappedFile;
using base::PersistentMemoryAllocator;
@@ -54,23 +55,62 @@ using testing::SetArgPointee;
namespace {
+TEST(PostmortemDeleterTest, BasicTest) {
+ base::HistogramTester histogram_tester;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ // Create three files.
+ FilePath path_one = temp_dir.GetPath().AppendASCII("a.pma");
+ FilePath path_two = temp_dir.GetPath().AppendASCII("b.pma");
+ FilePath path_three = temp_dir.GetPath().AppendASCII("c.pma");
+
+ std::vector<FilePath> stability_files = {path_one, path_two, path_three};
+
+ for (const FilePath& path : stability_files) {
+ {
+ base::ScopedFILE file(base::OpenFile(path, "w"));
+ ASSERT_NE(file.get(), nullptr);
+ }
+ ASSERT_TRUE(base::PathExists(path));
+ }
+
+ // Open one stability file to prevent its deletion.
+ base::ScopedFILE file(base::OpenFile(path_two, "w"));
+ ASSERT_NE(file.get(), nullptr);
+
+ // Validate deletion and metrics.
+ PostmortemDeleter deleter;
+ deleter.Process(stability_files);
+
+ ASSERT_FALSE(base::PathExists(path_one));
+ ASSERT_FALSE(base::PathExists(path_three));
+ histogram_tester.ExpectBucketCount("ActivityTracker.Collect.Status",
+ UNCLEAN_SHUTDOWN, 2);
+
+ ASSERT_TRUE(base::PathExists(path_two));
+ histogram_tester.ExpectBucketCount("ActivityTracker.Collect.Status",
+ DEBUG_FILE_DELETION_FAILED, 1);
+
+ std::vector<CollectionStatus> unexpected_statuses = {
+ NONE,
+ SUCCESS,
+ ANALYZER_CREATION_FAILED,
+ DEBUG_FILE_NO_DATA,
+ PREPARE_NEW_CRASH_REPORT_FAILED,
+ WRITE_TO_MINIDUMP_FAILED,
+ FINISHED_WRITING_CRASH_REPORT_FAILED,
+ COLLECTION_ATTEMPT};
+ for (CollectionStatus status : unexpected_statuses) {
+ histogram_tester.ExpectBucketCount("ActivityTracker.Collect.Status", status,
+ 0);
+ }
+}
+
const char kProductName[] = "TestProduct";
const char kVersionNumber[] = "TestVersionNumber";
const char kChannelName[] = "TestChannel";
-// The tracker creates some data entries internally.
-const size_t kInternalProcessDatums = 1;
-
-void ContainsKeyValue(
- const google::protobuf::Map<std::string, TypedValue>& data,
- const std::string& key,
- const std::string& value) {
- auto it = data.find(key);
- ASSERT_TRUE(it != data.end());
- EXPECT_EQ(TypedValue::kStringValue, it->second.value_case());
- EXPECT_EQ(value, it->second.string_value());
-}
-
// Exposes a public constructor in order to create a dummy database.
class MockCrashReportDatabase : public CrashReportDatabase {
public:
@@ -113,23 +153,17 @@ class MockCrashReportDatabase : public CrashReportDatabase {
CrashReportDatabase::OperationStatus(const UUID& uuid));
};
-// Used for testing CollectAndSubmitAllPendingReports.
class MockPostmortemReportCollector : public PostmortemReportCollector {
public:
- MockPostmortemReportCollector()
+ explicit MockPostmortemReportCollector(CrashReportDatabase* crash_database)
: PostmortemReportCollector(kProductName,
kVersionNumber,
kChannelName,
+ crash_database,
nullptr) {}
- MOCK_METHOD3(GetDebugStateFilePaths,
- std::vector<base::FilePath>(
- const base::FilePath& debug_info_dir,
- const base::FilePath::StringType& debug_file_pattern,
- const std::set<base::FilePath>&));
MOCK_METHOD2(CollectOneReport,
- CollectionStatus(const base::FilePath&,
- StabilityReport* report));
+ CollectionStatus(const FilePath&, StabilityReport* report));
MOCK_METHOD4(WriteReportToMinidump,
bool(StabilityReport* report,
const crashpad::UUID& client_id,
@@ -164,10 +198,11 @@ MATCHER_P(EqualsProto, message, "") {
} // namespace
-class PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest
- : public testing::Test {
+class PostmortemReportCollectorProcessTest : public testing::Test {
public:
- void SetUpTest(bool system_session_clean) {
+ void SetUpTest(bool system_session_clean, bool expect_write_dump) {
+ collector_.reset(new MockPostmortemReportCollector(&database_));
+
// Create a dummy debug file.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
debug_file_ = temp_dir_.GetPath().AppendASCII("foo-1.pma");
@@ -177,29 +212,23 @@ class PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest
}
ASSERT_TRUE(base::PathExists(debug_file_));
- // Expect collection of the debug file paths.
- debug_file_pattern_ = FILE_PATH_LITERAL("foo-*.pma");
- std::vector<base::FilePath> debug_files{debug_file_};
- EXPECT_CALL(collector_,
- GetDebugStateFilePaths(debug_file_.DirName(),
- debug_file_pattern_, no_excluded_files_))
- .Times(1)
- .WillOnce(Return(debug_files));
-
EXPECT_CALL(database_, GetSettings()).Times(1).WillOnce(Return(nullptr));
// Expect a single collection call.
StabilityReport report;
report.mutable_system_state()->set_session_state(
system_session_clean ? SystemState::CLEAN : SystemState::UNCLEAN);
- EXPECT_CALL(collector_, CollectOneReport(debug_file_, _))
+ EXPECT_CALL(*collector_, CollectOneReport(debug_file_, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<1>(report), Return(SUCCESS)));
+ if (!expect_write_dump)
+ return;
+
// Expect the call to write the proto to a minidump. This involves
// requesting a report from the crashpad database, writing the report, then
// finalizing it with the database.
- base::FilePath minidump_path = temp_dir_.GetPath().AppendASCII("foo-1.dmp");
+ FilePath minidump_path = temp_dir_.GetPath().AppendASCII("foo-1.dmp");
base::File minidump_file(
minidump_path, base::File::FLAG_CREATE | base::File::File::FLAG_WRITE);
crashpad::UUID new_report_uuid;
@@ -211,137 +240,79 @@ class PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest
.WillOnce(DoAll(SetArgPointee<0>(&crashpad_report_),
Return(CrashReportDatabase::kNoError)));
- EXPECT_CALL(collector_,
+ EXPECT_CALL(*collector_,
WriteReportToMinidump(_, _, _, minidump_file.GetPlatformFile()))
.Times(1)
.WillOnce(Return(true));
}
void ValidateHistograms(int unclean_cnt, int unclean_system_cnt) {
- histogram_tester_.ExpectTotalCount(
- "ActivityTracker.Collect.StabilityFileCount", 1);
- histogram_tester_.ExpectBucketCount(
- "ActivityTracker.Collect.StabilityFileCount", 1, 1);
- histogram_tester_.ExpectTotalCount(
- "ActivityTracker.Collect.UncleanShutdownCount", 1);
- histogram_tester_.ExpectBucketCount(
- "ActivityTracker.Collect.UncleanShutdownCount", unclean_cnt, 1);
- histogram_tester_.ExpectTotalCount(
- "ActivityTracker.Collect.UncleanSystemCount", 1);
- histogram_tester_.ExpectBucketCount(
- "ActivityTracker.Collect.UncleanSystemCount", unclean_system_cnt, 1);
+ histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
+ UNCLEAN_SHUTDOWN, unclean_cnt);
+ histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
+ UNCLEAN_SESSION, unclean_system_cnt);
}
void CollectReports(bool is_session_clean) {
- SetUpTest(is_session_clean);
+ SetUpTest(is_session_clean, true);
EXPECT_CALL(database_, FinishedWritingCrashReport(&crashpad_report_, _))
.Times(1)
.WillOnce(Return(CrashReportDatabase::kNoError));
// Run the test.
- int success_cnt = collector_.CollectAndSubmitAllPendingReports(
- debug_file_.DirName(), debug_file_pattern_, no_excluded_files_,
- &database_);
- ASSERT_EQ(1, success_cnt);
+ std::vector<FilePath> debug_files{debug_file_};
+ collector_->Process(debug_files);
ASSERT_FALSE(base::PathExists(debug_file_));
}
protected:
base::HistogramTester histogram_tester_;
base::ScopedTempDir temp_dir_;
- base::FilePath debug_file_;
+ FilePath debug_file_;
MockCrashReportDatabase database_;
- MockPostmortemReportCollector collector_;
- base::FilePath::StringType debug_file_pattern_;
- std::set<base::FilePath> no_excluded_files_;
+ std::unique_ptr<MockPostmortemReportCollector> collector_;
CrashReportDatabase::NewReport crashpad_report_;
};
-TEST_F(PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest,
- CollectAndSubmitAllPendingReportsCleanSession) {
+TEST_F(PostmortemReportCollectorProcessTest, ProcessCleanSession) {
CollectReports(true);
int expected_unclean = 1;
int expected_system_unclean = 0;
ValidateHistograms(expected_unclean, expected_system_unclean);
}
-TEST_F(PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest,
- CollectAndSubmitAllPendingReportsUncleanSession) {
+TEST_F(PostmortemReportCollectorProcessTest, ProcessUncleanSession) {
CollectReports(false);
int expected_unclean = 1;
int expected_system_unclean = 1;
ValidateHistograms(expected_unclean, expected_system_unclean);
}
-TEST_F(PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest,
- CollectAndSubmitAllPendingReportsStuckFile) {
- SetUpTest(true);
+TEST_F(PostmortemReportCollectorProcessTest, ProcessStuckFile) {
+ bool system_session_clean = true;
+ bool expect_write_dump = false;
+ SetUpTest(system_session_clean, expect_write_dump);
// Open the stability debug file to prevent its deletion.
base::ScopedFILE file(base::OpenFile(debug_file_, "w"));
ASSERT_NE(file.get(), nullptr);
- // Expect Crashpad is notified of an error writing the crash report.
- EXPECT_CALL(database_, ErrorWritingCrashReport(&crashpad_report_))
- .Times(1)
- .WillOnce(Return(CrashReportDatabase::kNoError));
-
// Run the test.
- int success_cnt = collector_.CollectAndSubmitAllPendingReports(
- debug_file_.DirName(), debug_file_pattern_, no_excluded_files_,
- &database_);
- ASSERT_EQ(0, success_cnt);
+ std::vector<FilePath> debug_files{debug_file_};
+ collector_->Process(debug_files);
ASSERT_TRUE(base::PathExists(debug_file_));
+ histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
+ DEBUG_FILE_DELETION_FAILED, 1);
int expected_unclean = 0;
int expected_system_unclean = 0;
ValidateHistograms(expected_unclean, expected_system_unclean);
}
-TEST(PostmortemReportCollectorTest, GetDebugStateFilePaths) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
- // Create files.
- std::vector<base::FilePath> expected_paths;
- std::set<base::FilePath> excluded_paths;
- {
- // Matches the pattern.
- base::FilePath path = temp_dir.GetPath().AppendASCII("foo1.pma");
- base::ScopedFILE file(base::OpenFile(path, "w"));
- ASSERT_NE(file.get(), nullptr);
- expected_paths.push_back(path);
-
- // Matches the pattern, but is excluded.
- path = temp_dir.GetPath().AppendASCII("foo2.pma");
- file.reset(base::OpenFile(path, "w"));
- ASSERT_NE(file.get(), nullptr);
- ASSERT_TRUE(excluded_paths.insert(path).second);
-
- // Matches the pattern.
- path = temp_dir.GetPath().AppendASCII("foo3.pma");
- file.reset(base::OpenFile(path, "w"));
- ASSERT_NE(file.get(), nullptr);
- expected_paths.push_back(path);
-
- // Does not match the pattern.
- path = temp_dir.GetPath().AppendASCII("bar.baz");
- file.reset(base::OpenFile(path, "w"));
- ASSERT_NE(file.get(), nullptr);
- }
-
- PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
- EXPECT_THAT(
- collector.GetDebugStateFilePaths(
- temp_dir.GetPath(), FILE_PATH_LITERAL("foo*.pma"), excluded_paths),
- testing::UnorderedElementsAreArray(expected_paths));
-}
-
TEST(PostmortemReportCollectorTest, CollectEmptyFile) {
// Create an empty file.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- base::FilePath file_path = temp_dir.GetPath().AppendASCII("empty.pma");
+ FilePath file_path = temp_dir.GetPath().AppendASCII("empty.pma");
{
base::ScopedFILE file(base::OpenFile(file_path, "w"));
ASSERT_NE(file.get(), nullptr);
@@ -349,8 +320,9 @@ TEST(PostmortemReportCollectorTest, CollectEmptyFile) {
ASSERT_TRUE(PathExists(file_path));
// Validate collection: an empty file cannot suppport an analyzer.
+ MockCrashReportDatabase crash_db;
PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
+ kChannelName, &crash_db, nullptr);
StabilityReport report;
ASSERT_EQ(ANALYZER_CREATION_FAILED,
collector.CollectOneReport(file_path, &report));
@@ -360,8 +332,7 @@ TEST(PostmortemReportCollectorTest, CollectRandomFile) {
// Create a file with content we don't expect to be valid for a debug file.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- base::FilePath file_path =
- temp_dir.GetPath().AppendASCII("invalid_content.pma");
+ FilePath file_path = temp_dir.GetPath().AppendASCII("invalid_content.pma");
{
base::ScopedFILE file(base::OpenFile(file_path, "w"));
ASSERT_NE(file.get(), nullptr);
@@ -376,202 +347,13 @@ TEST(PostmortemReportCollectorTest, CollectRandomFile) {
// Validate collection: random content appears as though there is not
// stability data.
+ MockCrashReportDatabase crash_db;
PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
+ kChannelName, &crash_db, nullptr);
StabilityReport report;
ASSERT_NE(SUCCESS, collector.CollectOneReport(file_path, &report));
}
-namespace {
-
-// Parameters for the activity tracking.
-const size_t kFileSize = 64 << 10; // 64 KiB
-const int kStackDepth = 6;
-const uint64_t kAllocatorId = 0;
-const char kAllocatorName[] = "PostmortemReportCollectorCollectionTest";
-const uint64_t kTaskSequenceNum = 42;
-const uintptr_t kTaskOrigin = 1000U;
-const uintptr_t kLockAddress = 1001U;
-const uintptr_t kEventAddress = 1002U;
-const int kThreadId = 43;
-const int kProcessId = 44;
-const int kAnotherThreadId = 45;
-const uint32_t kGenericId = 46U;
-const int32_t kGenericData = 47;
-
-} // namespace
-
-// Sets up a file backed thread tracker for direct access. A
-// GlobalActivityTracker is not created, meaning there is no risk of
-// the instrumentation interfering with the file's content.
-class PostmortemReportCollectorCollectionTest : public testing::Test {
- public:
- // Create a proper debug file.
- void SetUp() override {
- testing::Test::SetUp();
-
- // Create a file backed allocator.
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug_file.pma");
- allocator_ = CreateAllocator();
- ASSERT_NE(nullptr, allocator_);
-
- size_t tracker_mem_size =
- ThreadActivityTracker::SizeForStackDepth(kStackDepth);
- ASSERT_GT(kFileSize, tracker_mem_size);
-
- // Create a tracker.
- tracker_ = CreateTracker(allocator_.get(), tracker_mem_size);
- ASSERT_NE(nullptr, tracker_);
- ASSERT_TRUE(tracker_->IsValid());
- }
-
- std::unique_ptr<PersistentMemoryAllocator> CreateAllocator() {
- // Create the memory mapped file.
- std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
- bool success = mmfile->Initialize(
- File(debug_file_path_, File::FLAG_CREATE | File::FLAG_READ |
- File::FLAG_WRITE | File::FLAG_SHARE_DELETE),
- {0, static_cast<int64_t>(kFileSize)},
- MemoryMappedFile::READ_WRITE_EXTEND);
- if (!success || !mmfile->IsValid())
- return nullptr;
-
- // Create a persistent memory allocator.
- if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true))
- return nullptr;
- return WrapUnique(new FilePersistentMemoryAllocator(
- std::move(mmfile), kFileSize, kAllocatorId, kAllocatorName, false));
- }
-
- std::unique_ptr<ThreadActivityTracker> CreateTracker(
- PersistentMemoryAllocator* allocator,
- size_t tracker_mem_size) {
- // Allocate a block of memory for the tracker to use.
- PersistentMemoryAllocator::Reference mem_reference = allocator->Allocate(
- tracker_mem_size, GlobalActivityTracker::kTypeIdActivityTracker);
- if (mem_reference == 0U)
- return nullptr;
-
- // Get the memory's base address.
- void* mem_base = allocator->GetAsArray<char>(
- mem_reference, GlobalActivityTracker::kTypeIdActivityTracker,
- PersistentMemoryAllocator::kSizeAny);
- if (mem_base == nullptr)
- return nullptr;
-
- // Make the allocation iterable so it can be found by other processes.
- allocator->MakeIterable(mem_reference);
-
- return WrapUnique(new ThreadActivityTracker(mem_base, tracker_mem_size));
- }
-
- const base::FilePath& debug_file_path() const { return debug_file_path_; }
-
- protected:
- base::ScopedTempDir temp_dir_;
- base::FilePath debug_file_path_;
-
- std::unique_ptr<PersistentMemoryAllocator> allocator_;
- std::unique_ptr<ThreadActivityTracker> tracker_;
-};
-
-TEST_F(PostmortemReportCollectorCollectionTest, CollectSuccess) {
- // Create some activity data.
- tracker_->PushActivity(reinterpret_cast<void*>(kTaskOrigin),
- base::debug::Activity::ACT_TASK_RUN,
- ActivityData::ForTask(kTaskSequenceNum));
- tracker_->PushActivity(
- nullptr, base::debug::Activity::ACT_LOCK_ACQUIRE,
- ActivityData::ForLock(reinterpret_cast<void*>(kLockAddress)));
- ThreadActivityTracker::ActivityId activity_id = tracker_->PushActivity(
- nullptr, base::debug::Activity::ACT_EVENT_WAIT,
- ActivityData::ForEvent(reinterpret_cast<void*>(kEventAddress)));
- tracker_->PushActivity(nullptr, base::debug::Activity::ACT_THREAD_JOIN,
- ActivityData::ForThread(kThreadId));
- tracker_->PushActivity(nullptr, base::debug::Activity::ACT_PROCESS_WAIT,
- ActivityData::ForProcess(kProcessId));
- tracker_->PushActivity(nullptr, base::debug::Activity::ACT_GENERIC,
- ActivityData::ForGeneric(kGenericId, kGenericData));
- // Note: this exceeds the activity stack's capacity.
- tracker_->PushActivity(nullptr, base::debug::Activity::ACT_THREAD_JOIN,
- ActivityData::ForThread(kAnotherThreadId));
-
- // Add some user data.
- ActivityTrackerMemoryAllocator user_data_allocator(
- allocator_.get(), GlobalActivityTracker::kTypeIdUserDataRecord,
- GlobalActivityTracker::kTypeIdUserDataRecordFree, 1024U, 10U, false);
- std::unique_ptr<ActivityUserData> user_data =
- tracker_->GetUserData(activity_id, &user_data_allocator);
- user_data->SetInt("some_int", 42);
-
- // Validate collection returns the expected report.
- PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
- StabilityReport report;
- ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
-
- // Validate the report.
- ASSERT_EQ(1, report.process_states_size());
- const ProcessState& process_state = report.process_states(0);
- EXPECT_EQ(base::GetCurrentProcId(), process_state.process_id());
- ASSERT_EQ(1, process_state.threads_size());
-
- const ThreadState& thread_state = process_state.threads(0);
- EXPECT_EQ(base::PlatformThread::GetName(), thread_state.thread_name());
-#if defined(OS_WIN)
- EXPECT_EQ(base::PlatformThread::CurrentId(), thread_state.thread_id());
-#elif defined(OS_POSIX)
- EXPECT_EQ(base::PlatformThread::CurrentHandle().platform_handle(),
- thread_state.thread_id());
-#endif
-
- EXPECT_EQ(7, thread_state.activity_count());
- ASSERT_EQ(6, thread_state.activities_size());
- {
- const Activity& activity = thread_state.activities(0);
- EXPECT_EQ(Activity::ACT_TASK_RUN, activity.type());
- EXPECT_EQ(kTaskOrigin, activity.origin_address());
- EXPECT_EQ(kTaskSequenceNum, activity.task_sequence_id());
- EXPECT_EQ(0U, activity.user_data().size());
- }
- {
- const Activity& activity = thread_state.activities(1);
- EXPECT_EQ(Activity::ACT_LOCK_ACQUIRE, activity.type());
- EXPECT_EQ(kLockAddress, activity.lock_address());
- EXPECT_EQ(0U, activity.user_data().size());
- }
- {
- const Activity& activity = thread_state.activities(2);
- EXPECT_EQ(Activity::ACT_EVENT_WAIT, activity.type());
- EXPECT_EQ(kEventAddress, activity.event_address());
- ASSERT_EQ(1U, activity.user_data().size());
- ASSERT_TRUE(base::ContainsKey(activity.user_data(), "some_int"));
- EXPECT_EQ(TypedValue::kSignedValue,
- activity.user_data().at("some_int").value_case());
- EXPECT_EQ(42, activity.user_data().at("some_int").signed_value());
- }
- {
- const Activity& activity = thread_state.activities(3);
- EXPECT_EQ(Activity::ACT_THREAD_JOIN, activity.type());
- EXPECT_EQ(kThreadId, activity.thread_id());
- EXPECT_EQ(0U, activity.user_data().size());
- }
- {
- const Activity& activity = thread_state.activities(4);
- EXPECT_EQ(Activity::ACT_PROCESS_WAIT, activity.type());
- EXPECT_EQ(kProcessId, activity.process_id());
- EXPECT_EQ(0U, activity.user_data().size());
- }
- {
- const Activity& activity = thread_state.activities(5);
- EXPECT_EQ(Activity::ACT_GENERIC, activity.type());
- EXPECT_EQ(kGenericId, activity.generic_id());
- EXPECT_EQ(kGenericData, activity.generic_data());
- EXPECT_EQ(0U, activity.user_data().size());
- }
-}
-
class PostmortemReportCollectorCollectionFromGlobalTrackerTest
: public testing::Test {
public:
@@ -594,190 +376,14 @@ class PostmortemReportCollectorCollectionFromGlobalTrackerTest
debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug.pma");
}
- const base::FilePath& debug_file_path() { return debug_file_path_; }
+ const FilePath& debug_file_path() { return debug_file_path_; }
protected:
base::ScopedTempDir temp_dir_;
- base::FilePath debug_file_path_;
+ FilePath debug_file_path_;
};
TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
- LogCollection) {
- // Record some log messages.
- GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
- "", 3);
- GlobalActivityTracker::Get()->RecordLogMessage("hello world");
- GlobalActivityTracker::Get()->RecordLogMessage("foo bar");
-
- // Collect the stability report.
- PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
- StabilityReport report;
- ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
-
- // Validate the report's log content.
- ASSERT_EQ(2, report.log_messages_size());
- ASSERT_EQ("hello world", report.log_messages(0));
- ASSERT_EQ("foo bar", report.log_messages(1));
-}
-
-TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
- ProcessUserDataCollection) {
- const char string1[] = "foo";
- const char string2[] = "bar";
-
- // Record some process user data.
- GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
- "", 3);
- ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
- ActivityUserData::Snapshot snapshot;
- ASSERT_TRUE(process_data.CreateSnapshot(&snapshot));
- ASSERT_EQ(kInternalProcessDatums, snapshot.size());
- process_data.Set("raw", "foo", 3);
- process_data.SetString("string", "bar");
- process_data.SetChar("char", '9');
- process_data.SetInt("int", -9999);
- process_data.SetUint("uint", 9999);
- process_data.SetBool("bool", true);
- process_data.SetReference("ref", string1, strlen(string1));
- process_data.SetStringReference("sref", string2);
-
- // Collect the stability report.
- PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
- StabilityReport report;
- ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
-
- // Validate the report's user data.
- const auto& collected_data = report.global_data();
- ASSERT_EQ(kInternalProcessDatums + 12U, collected_data.size());
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "raw"));
- EXPECT_EQ(TypedValue::kBytesValue, collected_data.at("raw").value_case());
- EXPECT_EQ("foo", collected_data.at("raw").bytes_value());
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "string"));
- EXPECT_EQ(TypedValue::kStringValue, collected_data.at("string").value_case());
- EXPECT_EQ("bar", collected_data.at("string").string_value());
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "char"));
- EXPECT_EQ(TypedValue::kCharValue, collected_data.at("char").value_case());
- EXPECT_EQ("9", collected_data.at("char").char_value());
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "int"));
- EXPECT_EQ(TypedValue::kSignedValue, collected_data.at("int").value_case());
- EXPECT_EQ(-9999, collected_data.at("int").signed_value());
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "uint"));
- EXPECT_EQ(TypedValue::kUnsignedValue, collected_data.at("uint").value_case());
- EXPECT_EQ(9999U, collected_data.at("uint").unsigned_value());
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "bool"));
- EXPECT_EQ(TypedValue::kBoolValue, collected_data.at("bool").value_case());
- EXPECT_TRUE(collected_data.at("bool").bool_value());
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "ref"));
- EXPECT_EQ(TypedValue::kBytesReference, collected_data.at("ref").value_case());
- const TypedValue::Reference& ref = collected_data.at("ref").bytes_reference();
- EXPECT_EQ(reinterpret_cast<uintptr_t>(string1), ref.address());
- EXPECT_EQ(strlen(string1), static_cast<uint64_t>(ref.size()));
-
- ASSERT_TRUE(base::ContainsKey(collected_data, "sref"));
- EXPECT_EQ(TypedValue::kStringReference,
- collected_data.at("sref").value_case());
- const TypedValue::Reference& sref =
- collected_data.at("sref").string_reference();
- EXPECT_EQ(reinterpret_cast<uintptr_t>(string2), sref.address());
- EXPECT_EQ(strlen(string2), static_cast<uint64_t>(sref.size()));
-
- // Reporter's version details.
- ContainsKeyValue(collected_data, kStabilityReporterProduct, kProductName);
- ContainsKeyValue(collected_data, kStabilityReporterChannel, kChannelName);
-#if defined(ARCH_CPU_X86)
- ContainsKeyValue(collected_data, kStabilityReporterPlatform, "Win32");
-#elif defined(ARCH_CPU_X86_64)
- ContainsKeyValue(collected_data, kStabilityReporterPlatform, "Win64");
-#endif
- ContainsKeyValue(collected_data, kStabilityReporterVersion, kVersionNumber);
-}
-
-TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
- FieldTrialCollection) {
- // Record some data.
- GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
- "", 3);
- ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
- process_data.SetString("string", "bar");
- process_data.SetString("FieldTrial.string", "bar");
- process_data.SetString("FieldTrial.foo", "bar");
-
- // Collect the stability report.
- PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
- StabilityReport report;
- ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
-
- // Validate the report's experiment and global data.
- ASSERT_EQ(2, report.field_trials_size());
- EXPECT_NE(0U, report.field_trials(0).name_id());
- EXPECT_NE(0U, report.field_trials(0).group_id());
- EXPECT_NE(0U, report.field_trials(1).name_id());
- EXPECT_EQ(report.field_trials(0).group_id(),
- report.field_trials(1).group_id());
-
- // Expect 5 key/value pairs (including product details).
- const auto& collected_data = report.global_data();
- EXPECT_EQ(kInternalProcessDatums + 5U, collected_data.size());
- EXPECT_TRUE(base::ContainsKey(collected_data, "string"));
-}
-
-TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
- ModuleCollection) {
- // Record some module information.
- GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
- "", 3);
-
- base::debug::GlobalActivityTracker::ModuleInfo module_info = {};
- module_info.is_loaded = true;
- module_info.address = 0x123456;
- module_info.load_time = 1111LL;
- module_info.size = 0x2d000;
- module_info.timestamp = 0xCAFECAFE;
- module_info.age = 1;
- crashpad::UUID debug_uuid;
- debug_uuid.InitializeFromString("11223344-5566-7788-abcd-0123456789ab");
- memcpy(module_info.identifier, &debug_uuid, sizeof(module_info.identifier));
- module_info.file = "foo";
- module_info.debug_file = "bar";
-
- GlobalActivityTracker::Get()->RecordModuleInfo(module_info);
-
- // Collect the stability report.
- PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, nullptr);
- StabilityReport report;
- ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
-
- // Validate the report's modules content.
- ASSERT_EQ(1, report.process_states_size());
- const ProcessState& process_state = report.process_states(0);
- ASSERT_EQ(1, process_state.modules_size());
-
- const CodeModule collected_module = process_state.modules(0);
- EXPECT_EQ(module_info.address,
- static_cast<uintptr_t>(collected_module.base_address()));
- EXPECT_EQ(module_info.size, static_cast<size_t>(collected_module.size()));
- EXPECT_EQ(module_info.file, collected_module.code_file());
- EXPECT_EQ("CAFECAFE2d000", collected_module.code_identifier());
- EXPECT_EQ(module_info.debug_file, collected_module.debug_file());
- EXPECT_EQ("1122334455667788ABCD0123456789AB1",
- collected_module.debug_identifier());
- EXPECT_EQ("", collected_module.version());
- EXPECT_EQ(0LL, collected_module.shrink_down_delta());
- EXPECT_EQ(!module_info.is_loaded, collected_module.is_unloaded());
-}
-
-TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
SystemStateTest) {
// Setup.
GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
@@ -791,8 +397,9 @@ TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
IsSessionUnclean(base::Time::FromInternalValue(12345LL)))
.Times(1)
.WillOnce(Return(SystemSessionAnalyzer::CLEAN));
+ MockCrashReportDatabase crash_db;
PostmortemReportCollector collector(kProductName, kVersionNumber,
- kChannelName, &analyzer);
+ kChannelName, &crash_db, &analyzer);
StabilityReport report;
ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
diff --git a/chromium/components/browser_watcher/stability_debugging.cc b/chromium/components/browser_watcher/stability_debugging.cc
index 3c8cc649292..aee484c8506 100644
--- a/chromium/components/browser_watcher/stability_debugging.cc
+++ b/chromium/components/browser_watcher/stability_debugging.cc
@@ -4,10 +4,48 @@
#include "components/browser_watcher/stability_debugging.h"
+#include <windows.h>
+
+#include <memory>
+
#include "base/debug/activity_tracker.h"
+#include "build/build_config.h"
namespace browser_watcher {
+namespace {
+
+struct VehUnregisterer {
+ void operator()(void* handle) const {
+ ::RemoveVectoredExceptionHandler(handle);
+ }
+};
+
+using VehHandle = std::unique_ptr<void, VehUnregisterer>;
+
+uintptr_t GetProgramCounter(const CONTEXT& context) {
+#if defined(ARCH_CPU_X86)
+ return context.Eip;
+#elif defined(ARCH_CPU_X86_64)
+ return context.Rip;
+#endif
+}
+
+LONG CALLBACK VectoredExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
+ base::debug::GlobalActivityTracker* tracker =
+ base::debug::GlobalActivityTracker::Get();
+ if (tracker) {
+ EXCEPTION_RECORD* record = exception_pointers->ExceptionRecord;
+ uintptr_t pc = GetProgramCounter(*exception_pointers->ContextRecord);
+ tracker->RecordException(reinterpret_cast<void*>(pc),
+ record->ExceptionAddress, record->ExceptionCode);
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH; // Continue to the next handler.
+}
+
+} // namespace
+
void SetStabilityDataBool(base::StringPiece name, bool value) {
base::debug::GlobalActivityTracker* global_tracker =
base::debug::GlobalActivityTracker::Get();
@@ -26,4 +64,16 @@ void SetStabilityDataInt(base::StringPiece name, int64_t value) {
global_tracker->process_data().SetInt(name, value);
}
+void RegisterStabilityVEH() {
+ // Register a vectored exception handler and request it be first. Note that
+ // subsequent registrations may also request to be first, in which case this
+ // one will be bumped.
+ // TODO(manzagop): Depending on observations, it may be necessary to
+ // consider refreshing the registration, either periodically or at opportune
+ // (e.g. risky) times.
+ static VehHandle veh_handler(
+ ::AddVectoredExceptionHandler(1, &VectoredExceptionHandler));
+ DCHECK(veh_handler);
+}
+
} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_debugging.h b/chromium/components/browser_watcher/stability_debugging.h
index cc23f69f477..145979d0451 100644
--- a/chromium/components/browser_watcher/stability_debugging.h
+++ b/chromium/components/browser_watcher/stability_debugging.h
@@ -15,6 +15,10 @@ namespace browser_watcher {
void SetStabilityDataBool(base::StringPiece name, bool value);
void SetStabilityDataInt(base::StringPiece name, int64_t value);
+// Registers a vectored exception handler that stores exception details to the
+// stability file.
+void RegisterStabilityVEH();
+
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_STABILITY_DEBUGGING_H_
diff --git a/chromium/components/browser_watcher/stability_debugging_win_unittest.cc b/chromium/components/browser_watcher/stability_debugging_win_unittest.cc
index 785ab22be78..d4bd107ba6c 100644
--- a/chromium/components/browser_watcher/stability_debugging_win_unittest.cc
+++ b/chromium/components/browser_watcher/stability_debugging_win_unittest.cc
@@ -4,84 +4,88 @@
#include "components/browser_watcher/stability_debugging.h"
-#include "base/files/file_enumerator.h"
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/debug/activity_tracker.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/process/process.h"
#include "base/test/multiprocess_test.h"
-#include "components/browser_watcher/stability_paths.h"
+#include "base/test/test_timeouts.h"
+#include "components/browser_watcher/stability_report.pb.h"
+#include "components/browser_watcher/stability_report_extractor.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace browser_watcher {
-class StabilityDebuggingWinMultiProcTest : public base::MultiProcessTest {};
+using base::debug::GlobalActivityTracker;
+
+const int kMemorySize = 1 << 20; // 1MiB
+const uint32_t exception_code = 42U;
+const uint32_t exception_flag_continuable = 0U;
+
+class StabilityDebuggingTest : public testing::Test {
+ public:
+ StabilityDebuggingTest() {}
+ ~StabilityDebuggingTest() override {
+ GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
+ if (global_tracker) {
+ global_tracker->ReleaseTrackerForCurrentThreadForTesting();
+ delete global_tracker;
+ }
+ }
-MULTIPROCESS_TEST_MAIN(DummyProcess) {
- return 0;
-}
+ void SetUp() override {
+ testing::Test::SetUp();
-TEST_F(StabilityDebuggingWinMultiProcTest, GetStabilityFileForProcessTest) {
- const base::FilePath empty_path;
-
- // Get the path for the current process.
- base::FilePath stability_path;
- ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
- &stability_path));
-
- // Ensure requesting a second time produces the same.
- base::FilePath stability_path_two;
- ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
- &stability_path_two));
- EXPECT_EQ(stability_path, stability_path_two);
-
- // Ensure a different process has a different stability path.
- base::SpawnChildResult spawn_result = SpawnChild("DummyProcess");
- base::FilePath stability_path_other;
- ASSERT_TRUE(GetStabilityFileForProcess(spawn_result.process, empty_path,
- &stability_path_other));
- EXPECT_NE(stability_path, stability_path_other);
-}
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ debug_path_ = temp_dir_.GetPath().AppendASCII("debug.pma");
-TEST(StabilityDebuggingWinTest,
- GetStabilityFilePatternMatchesGetStabilityFileForProcessResult) {
- // GetStabilityFileForProcess file names must match GetStabilityFilePattern
- // according to
- // FileEnumerator's algorithm. We test this by writing out some files and
- // validating what is matched.
-
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- base::FilePath user_data_dir = temp_dir.GetPath();
-
- // Create the stability directory.
- base::FilePath stability_dir = GetStabilityDir(user_data_dir);
- ASSERT_TRUE(base::CreateDirectory(stability_dir));
-
- // Write a stability file.
- base::FilePath stability_file;
- ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(),
- user_data_dir, &stability_file));
- {
- base::ScopedFILE file(base::OpenFile(stability_file, "w"));
- ASSERT_TRUE(file.get());
+ GlobalActivityTracker::CreateWithFile(debug_path_, kMemorySize, 0ULL, "",
+ 3);
}
- // Write a file that shouldn't match.
- base::FilePath non_matching_file =
- stability_dir.AppendASCII("non_matching.foo");
- {
- base::ScopedFILE file(base::OpenFile(non_matching_file, "w"));
- ASSERT_TRUE(file.get());
+ const base::FilePath& debug_path() { return debug_path_; }
+
+ private:
+ base::ScopedTempDir temp_dir_;
+ base::FilePath debug_path_;
+};
+
+TEST_F(StabilityDebuggingTest, CrashingTest) {
+ RegisterStabilityVEH();
+
+ // Raise an exception, then continue.
+ __try {
+ ::RaiseException(exception_code, exception_flag_continuable, 0U, nullptr);
+ } __except (EXCEPTION_CONTINUE_EXECUTION) {
}
- // Validate only the stability file matches.
- base::FileEnumerator enumerator(stability_dir, false /* recursive */,
- base::FileEnumerator::FILES,
- GetStabilityFilePattern());
- ASSERT_EQ(stability_file, enumerator.Next());
- ASSERT_TRUE(enumerator.Next().empty());
+ // Collect the report.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_path(), &report));
+
+ // Validate expectations.
+ ASSERT_EQ(1, report.process_states_size());
+ const ProcessState& process_state = report.process_states(0);
+ ASSERT_EQ(1, process_state.threads_size());
+
+ bool thread_found = false;
+ for (const ThreadState& thread : process_state.threads()) {
+ if (thread.thread_id() == ::GetCurrentThreadId()) {
+ thread_found = true;
+ ASSERT_TRUE(thread.has_exception());
+ const Exception& exception = thread.exception();
+ EXPECT_EQ(exception_code, exception.code());
+ EXPECT_NE(0ULL, exception.program_counter());
+ EXPECT_NE(0ULL, exception.exception_address());
+ EXPECT_NE(0LL, exception.time());
+ }
+ }
+ ASSERT_TRUE(thread_found);
}
} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_metrics.cc b/chromium/components/browser_watcher/stability_metrics.cc
new file mode 100644
index 00000000000..0ec6675db98
--- /dev/null
+++ b/chromium/components/browser_watcher/stability_metrics.cc
@@ -0,0 +1,21 @@
+// 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/browser_watcher/stability_metrics.h"
+
+#include "base/metrics/histogram_macros.h"
+
+namespace browser_watcher {
+
+void LogCollectOnCrashEvent(CollectOnCrashEvent event) {
+ UMA_HISTOGRAM_ENUMERATION("ActivityTracker.CollectCrash.Event", event,
+ CollectOnCrashEvent::kCollectOnCrashEventMax);
+}
+
+void LogStabilityRecordEvent(StabilityRecordEvent event) {
+ UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Record.Event", event,
+ StabilityRecordEvent::kStabilityRecordEventMax);
+}
+
+} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_metrics.h b/chromium/components/browser_watcher/stability_metrics.h
new file mode 100644
index 00000000000..1daf70ac12f
--- /dev/null
+++ b/chromium/components/browser_watcher/stability_metrics.h
@@ -0,0 +1,41 @@
+// 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_BROWSER_WATCHER_STABILITY_METRICS_H_
+#define COMPONENTS_BROWSER_WATCHER_STABILITY_METRICS_H_
+
+namespace browser_watcher {
+
+// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
+enum class CollectOnCrashEvent {
+ kCollectAttempt,
+ kUserDataDirNotEmpty,
+ kPathExists,
+ kReportExtractionSuccess,
+ kPmaSetDeletedFailed,
+ kOpenForDeleteFailed,
+ kSuccess,
+ // New values go here.
+ kCollectOnCrashEventMax
+};
+
+// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
+enum class StabilityRecordEvent {
+ kRecordAttempt,
+ kStabilityDirectoryExists,
+ kGotStabilityPath,
+ kGotTracker,
+ kMarkDeleted,
+ kMarkDeletedGotFile,
+ kOpenForDeleteFailed,
+ // New values go here.
+ kStabilityRecordEventMax
+};
+
+void LogCollectOnCrashEvent(CollectOnCrashEvent event);
+void LogStabilityRecordEvent(StabilityRecordEvent event);
+
+} // namespace browser_watcher
+
+#endif // COMPONENTS_BROWSER_WATCHER_STABILITY_METRICS_H_
diff --git a/chromium/components/browser_watcher/stability_paths.cc b/chromium/components/browser_watcher/stability_paths.cc
index 66805fa67d1..a8164dffbf2 100644
--- a/chromium/components/browser_watcher/stability_paths.cc
+++ b/chromium/components/browser_watcher/stability_paths.cc
@@ -8,20 +8,32 @@
#include <windows.h>
#endif // defined(OS_WIN)
+#include <memory>
#include <string>
+#include <utility>
+#include "base/debug/activity_tracker.h"
#include "base/feature_list.h"
#include "base/files/file.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/memory_mapped_file.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "components/browser_watcher/features.h"
+#include "components/browser_watcher/stability_metrics.h"
#if defined(OS_WIN)
#include "third_party/crashpad/crashpad/util/win/time.h"
namespace browser_watcher {
+
+using base::FilePath;
+using base::FilePersistentMemoryAllocator;
+using base::MemoryMappedFile;
+using base::PersistentMemoryAllocator;
+
namespace {
bool GetCreationTime(const base::Process& process, FILETIME* creation_time) {
@@ -30,16 +42,31 @@ bool GetCreationTime(const base::Process& process, FILETIME* creation_time) {
&ignore) != 0;
}
+bool SetPmaFileDeleted(const base::FilePath& file_path) {
+ // Map the file read-write so it can guarantee consistency between
+ // the analyzer and any trackers that may still be active.
+ std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
+ mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE);
+ if (!mmfile->IsValid())
+ return false;
+ if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true))
+ return false;
+ FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0,
+ base::StringPiece(), true);
+ allocator.SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
+ return true;
+}
+
} // namespace
-base::FilePath GetStabilityDir(const base::FilePath& user_data_dir) {
+FilePath GetStabilityDir(const FilePath& user_data_dir) {
return user_data_dir.AppendASCII("Stability");
}
-base::FilePath GetStabilityFileForProcess(base::ProcessId pid,
- timeval creation_time,
- const base::FilePath& user_data_dir) {
- base::FilePath stability_dir = GetStabilityDir(user_data_dir);
+FilePath GetStabilityFileForProcess(base::ProcessId pid,
+ timeval creation_time,
+ const FilePath& user_data_dir) {
+ FilePath stability_dir = GetStabilityDir(user_data_dir);
constexpr uint64_t kMicrosecondsPerSecond = static_cast<uint64_t>(1E6);
int64_t creation_time_us =
@@ -51,8 +78,8 @@ base::FilePath GetStabilityFileForProcess(base::ProcessId pid,
}
bool GetStabilityFileForProcess(const base::Process& process,
- const base::FilePath& user_data_dir,
- base::FilePath* file_path) {
+ const FilePath& user_data_dir,
+ FilePath* file_path) {
DCHECK(file_path);
FILETIME creation_time;
@@ -67,30 +94,64 @@ bool GetStabilityFileForProcess(const base::Process& process,
return true;
}
-base::FilePath::StringType GetStabilityFilePattern() {
- return base::FilePath::StringType(FILE_PATH_LITERAL("*-*")) +
+FilePath::StringType GetStabilityFilePattern() {
+ return FilePath::StringType(FILE_PATH_LITERAL("*-*")) +
base::PersistentMemoryAllocator::kFileExtension;
}
-void MarkStabilityFileForDeletion(const base::FilePath& user_data_dir) {
- if (!base::FeatureList::IsEnabled(
- browser_watcher::kStabilityDebuggingFeature)) {
- return;
+std::vector<FilePath> GetStabilityFiles(
+ const FilePath& stability_dir,
+ const FilePath::StringType& stability_file_pattern,
+ const std::set<FilePath>& excluded_stability_files) {
+ DCHECK_NE(true, stability_dir.empty());
+ DCHECK_NE(true, stability_file_pattern.empty());
+
+ std::vector<FilePath> paths;
+ base::FileEnumerator enumerator(stability_dir, false /* recursive */,
+ base::FileEnumerator::FILES,
+ stability_file_pattern);
+ FilePath path;
+ for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
+ if (excluded_stability_files.find(path) == excluded_stability_files.end())
+ paths.push_back(path);
}
+ return paths;
+}
+
+void MarkOwnStabilityFileDeleted(const base::FilePath& user_data_dir) {
+ base::debug::GlobalActivityTracker* global_tracker =
+ base::debug::GlobalActivityTracker::Get();
+ if (!global_tracker)
+ return; // No stability instrumentation.
+ global_tracker->MarkDeleted();
+ LogStabilityRecordEvent(StabilityRecordEvent::kMarkDeleted);
+
+ // Open (with delete) and then immediately close the file by going out of
+ // scope. This should cause the stability debugging file to be deleted prior
+ // to the next execution.
base::FilePath stability_file;
if (!GetStabilityFileForProcess(base::Process::Current(), user_data_dir,
&stability_file)) {
- // TODO(manzagop): add a metric for this.
return;
}
+ LogStabilityRecordEvent(StabilityRecordEvent::kMarkDeletedGotFile);
- // Open (with delete) and then immediately close the file by going out of
- // scope. This should cause the stability debugging file to be deleted prior
- // to the next execution.
- base::File file(stability_file, base::File::FLAG_OPEN |
- base::File::FLAG_READ |
- base::File::FLAG_DELETE_ON_CLOSE);
+ base::File deleter(stability_file, base::File::FLAG_OPEN |
+ base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ if (!deleter.IsValid())
+ LogStabilityRecordEvent(StabilityRecordEvent::kOpenForDeleteFailed);
+}
+
+void MarkStabilityFileDeletedOnCrash(const base::FilePath& file_path) {
+ if (!SetPmaFileDeleted(file_path))
+ LogCollectOnCrashEvent(CollectOnCrashEvent::kPmaSetDeletedFailed);
+
+ base::File deleter(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ if (!deleter.IsValid())
+ LogCollectOnCrashEvent(CollectOnCrashEvent::kOpenForDeleteFailed);
}
} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_paths.h b/chromium/components/browser_watcher/stability_paths.h
index 07216f1f75c..0b2078fbf96 100644
--- a/chromium/components/browser_watcher/stability_paths.h
+++ b/chromium/components/browser_watcher/stability_paths.h
@@ -5,6 +5,9 @@
#ifndef COMPONENTS_BROWSER_WATCHER_STABILITY_PATHS_H_
#define COMPONENTS_BROWSER_WATCHER_STABILITY_PATHS_H_
+#include <set>
+#include <vector>
+
#include "base/files/file_path.h"
#include "base/process/process.h"
#include "build/build_config.h"
@@ -35,8 +38,22 @@ bool GetStabilityFileForProcess(const base::Process& process,
// Returns a pattern that matches file names returned by GetFileForProcess.
base::FilePath::StringType GetStabilityFilePattern();
-// Marks the stability file for deletion.
-void MarkStabilityFileForDeletion(const base::FilePath& user_data_dir);
+// Returns files in |stability_dir| that match |stability_file_pattern|,
+// excluding those in |excluded_stability_files|.
+std::vector<base::FilePath> GetStabilityFiles(
+ const base::FilePath& stability_dir,
+ const base::FilePath::StringType& stability_file_pattern,
+ const std::set<base::FilePath>& excluded_stability_files);
+
+// Sets the current process's stability file's state to deleted (via the
+// GlobalActivityTracker) and opens the file for deletion. Metrics pertaining to
+// stability recording are logged.
+void MarkOwnStabilityFileDeleted(const base::FilePath& user_data_dir);
+
+// Sets another process's stability file's state to deleted, then opens it for
+// deletion. This function is meant for use by the crashpad handler; it logs
+// metrics labelled as in the context of crash collection.
+void MarkStabilityFileDeletedOnCrash(const base::FilePath& file_path);
#endif // defined(OS_WIN)
diff --git a/chromium/components/browser_watcher/stability_paths_unittest.cc b/chromium/components/browser_watcher/stability_paths_unittest.cc
new file mode 100644
index 00000000000..7a8f6ceb281
--- /dev/null
+++ b/chromium/components/browser_watcher/stability_paths_unittest.cc
@@ -0,0 +1,125 @@
+// 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/browser_watcher/stability_paths.h"
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/process/process.h"
+#include "base/test/multiprocess_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace browser_watcher {
+
+class StabilityPathsMultiProcTest : public base::MultiProcessTest {};
+
+MULTIPROCESS_TEST_MAIN(DummyProcess) {
+ return 0;
+}
+
+TEST_F(StabilityPathsMultiProcTest, GetStabilityFileForProcessTest) {
+ const base::FilePath empty_path;
+
+ // Get the path for the current process.
+ base::FilePath stability_path;
+ ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
+ &stability_path));
+
+ // Ensure requesting a second time produces the same.
+ base::FilePath stability_path_two;
+ ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
+ &stability_path_two));
+ EXPECT_EQ(stability_path, stability_path_two);
+
+ // Ensure a different process has a different stability path.
+ base::SpawnChildResult spawn_result = SpawnChild("DummyProcess");
+ base::FilePath stability_path_other;
+ ASSERT_TRUE(GetStabilityFileForProcess(spawn_result.process, empty_path,
+ &stability_path_other));
+ EXPECT_NE(stability_path, stability_path_other);
+}
+
+TEST(StabilityPathsTest,
+ GetStabilityFilePatternMatchesGetStabilityFileForProcessResult) {
+ // GetStabilityFileForProcess file names must match GetStabilityFilePattern
+ // according to
+ // FileEnumerator's algorithm. We test this by writing out some files and
+ // validating what is matched.
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath user_data_dir = temp_dir.GetPath();
+
+ // Create the stability directory.
+ base::FilePath stability_dir = GetStabilityDir(user_data_dir);
+ ASSERT_TRUE(base::CreateDirectory(stability_dir));
+
+ // Write a stability file.
+ base::FilePath stability_file;
+ ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(),
+ user_data_dir, &stability_file));
+ {
+ base::ScopedFILE file(base::OpenFile(stability_file, "w"));
+ ASSERT_TRUE(file.get());
+ }
+
+ // Write a file that shouldn't match.
+ base::FilePath non_matching_file =
+ stability_dir.AppendASCII("non_matching.foo");
+ {
+ base::ScopedFILE file(base::OpenFile(non_matching_file, "w"));
+ ASSERT_TRUE(file.get());
+ }
+
+ // Validate only the stability file matches.
+ base::FileEnumerator enumerator(stability_dir, false /* recursive */,
+ base::FileEnumerator::FILES,
+ GetStabilityFilePattern());
+ ASSERT_EQ(stability_file, enumerator.Next());
+ ASSERT_TRUE(enumerator.Next().empty());
+}
+
+TEST(StabilityPathsTest, GetStabilityFiles) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ // Create files.
+ std::vector<base::FilePath> expected_paths;
+ std::set<base::FilePath> excluded_paths;
+ {
+ // Matches the pattern.
+ base::FilePath path = temp_dir.GetPath().AppendASCII("foo1.pma");
+ base::ScopedFILE file(base::OpenFile(path, "w"));
+ ASSERT_NE(file.get(), nullptr);
+ expected_paths.push_back(path);
+
+ // Matches the pattern, but is excluded.
+ path = temp_dir.GetPath().AppendASCII("foo2.pma");
+ file.reset(base::OpenFile(path, "w"));
+ ASSERT_NE(file.get(), nullptr);
+ ASSERT_TRUE(excluded_paths.insert(path).second);
+
+ // Matches the pattern.
+ path = temp_dir.GetPath().AppendASCII("foo3.pma");
+ file.reset(base::OpenFile(path, "w"));
+ ASSERT_NE(file.get(), nullptr);
+ expected_paths.push_back(path);
+
+ // Does not match the pattern.
+ path = temp_dir.GetPath().AppendASCII("bar.baz");
+ file.reset(base::OpenFile(path, "w"));
+ ASSERT_NE(file.get(), nullptr);
+ }
+
+ EXPECT_THAT(GetStabilityFiles(temp_dir.GetPath(),
+ FILE_PATH_LITERAL("foo*.pma"), excluded_paths),
+ testing::UnorderedElementsAreArray(expected_paths));
+}
+
+} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_report.proto b/chromium/components/browser_watcher/stability_report.proto
index b1ca52e785c..af0dae3c950 100644
--- a/chromium/components/browser_watcher/stability_report.proto
+++ b/chromium/components/browser_watcher/stability_report.proto
@@ -142,8 +142,17 @@ message Activity {
map<string, TypedValue> user_data = 9;
}
-// The state of a thread.
+// Details about an exception.
// Next id: 5
+message Exception {
+ optional uint32 code = 1;
+ optional uint64 program_counter = 2;
+ optional uint64 exception_address = 3;
+ optional int64 time = 4;
+}
+
+// The state of a thread.
+// Next id: 6
message ThreadState {
// The name of the thread, up to a maxiumum length.
optional string thread_name = 1;
@@ -155,6 +164,10 @@ message ThreadState {
// stack and |activities| holds the base of the stack (up to a maximum size).
optional int32 activity_count = 3;
repeated Activity activities = 4;
+
+ // The last exception to be successfully captured. Note this exception may
+ // have been recovered from.
+ optional Exception exception = 5;
}
// The state of a process.
diff --git a/chromium/components/browser_watcher/stability_report_extractor.cc b/chromium/components/browser_watcher/stability_report_extractor.cc
index 805cc108e68..156c68f6598 100644
--- a/chromium/components/browser_watcher/stability_report_extractor.cc
+++ b/chromium/components/browser_watcher/stability_report_extractor.cc
@@ -182,6 +182,15 @@ void CollectActivity(const base::debug::Activity& recorded,
}
}
+void CollectException(const base::debug::Activity& recorded,
+ Exception* collected) {
+ DCHECK(collected);
+ collected->set_code(recorded.data.exception.code);
+ collected->set_program_counter(recorded.calling_address);
+ collected->set_exception_address(recorded.origin_address);
+ collected->set_time(recorded.time_internal);
+}
+
void CollectThread(
const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
ThreadState* thread_state) {
@@ -191,6 +200,12 @@ void CollectThread(
thread_state->set_thread_id(snapshot.thread_id);
thread_state->set_activity_count(snapshot.activity_stack_depth);
+ if (snapshot.last_exception.activity_type ==
+ base::debug::Activity::ACT_EXCEPTION) {
+ CollectException(snapshot.last_exception,
+ thread_state->mutable_exception());
+ }
+
for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
Activity* collected = thread_state->add_activities();
diff --git a/chromium/components/browser_watcher/stability_report_extractor.h b/chromium/components/browser_watcher/stability_report_extractor.h
index 5ff90339e34..80491464086 100644
--- a/chromium/components/browser_watcher/stability_report_extractor.h
+++ b/chromium/components/browser_watcher/stability_report_extractor.h
@@ -12,7 +12,7 @@
namespace browser_watcher {
-// DO NOT CHANGE VALUES. This is logged persistently in a histogram.
+// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
enum CollectionStatus {
NONE = 0,
SUCCESS = 1, // Successfully registered a report with Crashpad.
@@ -22,7 +22,11 @@ enum CollectionStatus {
WRITE_TO_MINIDUMP_FAILED = 5,
DEBUG_FILE_DELETION_FAILED = 6,
FINISHED_WRITING_CRASH_REPORT_FAILED = 7,
- COLLECTION_STATUS_MAX = 8
+ UNCLEAN_SHUTDOWN = 8,
+ UNCLEAN_SESSION = 9,
+ COLLECTION_ATTEMPT = 10,
+ // New values go here.
+ COLLECTION_STATUS_MAX = 11
};
// Extracts a stability report from a stability file.
diff --git a/chromium/components/browser_watcher/stability_report_extractor_unittest.cc b/chromium/components/browser_watcher/stability_report_extractor_unittest.cc
new file mode 100644
index 00000000000..d5f50b631b8
--- /dev/null
+++ b/chromium/components/browser_watcher/stability_report_extractor_unittest.cc
@@ -0,0 +1,457 @@
+// 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/browser_watcher/stability_report_extractor.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/debug/activity_tracker.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/persistent_memory_allocator.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+
+namespace browser_watcher {
+
+using base::debug::ActivityData;
+using base::debug::ActivityTrackerMemoryAllocator;
+using base::debug::ActivityUserData;
+using base::debug::GlobalActivityTracker;
+using base::debug::ThreadActivityTracker;
+using base::File;
+using base::FilePath;
+using base::FilePersistentMemoryAllocator;
+using base::MemoryMappedFile;
+using base::PersistentMemoryAllocator;
+using base::WrapUnique;
+
+namespace {
+
+// The tracker creates some data entries internally.
+const size_t kInternalProcessDatums = 1;
+
+// Parameters for the activity tracking.
+const size_t kFileSize = 64 << 10; // 64 KiB
+const int kStackDepth = 6;
+const uint64_t kAllocatorId = 0;
+const char kAllocatorName[] = "PostmortemReportCollectorCollectionTest";
+const uint64_t kTaskSequenceNum = 42;
+const uintptr_t kTaskOrigin = 1000U;
+const uintptr_t kLockAddress = 1001U;
+const uintptr_t kEventAddress = 1002U;
+const int kThreadId = 43;
+const int kProcessId = 44;
+const int kAnotherThreadId = 45;
+const uint32_t kGenericId = 46U;
+const int32_t kGenericData = 47;
+
+} // namespace
+
+// Sets up a file backed thread tracker for direct access. A
+// GlobalActivityTracker is not created, meaning there is no risk of
+// the instrumentation interfering with the file's content.
+class StabilityReportExtractorThreadTrackerTest : public testing::Test {
+ public:
+ // Create a proper debug file.
+ void SetUp() override {
+ testing::Test::SetUp();
+
+ // Create a file backed allocator.
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug_file.pma");
+ allocator_ = CreateAllocator();
+ ASSERT_NE(nullptr, allocator_);
+
+ size_t tracker_mem_size =
+ ThreadActivityTracker::SizeForStackDepth(kStackDepth);
+ ASSERT_GT(kFileSize, tracker_mem_size);
+
+ // Create a tracker.
+ tracker_ = CreateTracker(allocator_.get(), tracker_mem_size);
+ ASSERT_NE(nullptr, tracker_);
+ ASSERT_TRUE(tracker_->IsValid());
+ }
+
+ std::unique_ptr<PersistentMemoryAllocator> CreateAllocator() {
+ // Create the memory mapped file.
+ std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
+ bool success = mmfile->Initialize(
+ File(debug_file_path_, File::FLAG_CREATE | File::FLAG_READ |
+ File::FLAG_WRITE | File::FLAG_SHARE_DELETE),
+ {0, static_cast<int64_t>(kFileSize)},
+ MemoryMappedFile::READ_WRITE_EXTEND);
+ if (!success || !mmfile->IsValid())
+ return nullptr;
+
+ // Create a persistent memory allocator.
+ if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true))
+ return nullptr;
+ return WrapUnique(new FilePersistentMemoryAllocator(
+ std::move(mmfile), kFileSize, kAllocatorId, kAllocatorName, false));
+ }
+
+ std::unique_ptr<ThreadActivityTracker> CreateTracker(
+ PersistentMemoryAllocator* allocator,
+ size_t tracker_mem_size) {
+ // Allocate a block of memory for the tracker to use.
+ PersistentMemoryAllocator::Reference mem_reference = allocator->Allocate(
+ tracker_mem_size, GlobalActivityTracker::kTypeIdActivityTracker);
+ if (mem_reference == 0U)
+ return nullptr;
+
+ // Get the memory's base address.
+ void* mem_base = allocator->GetAsArray<char>(
+ mem_reference, GlobalActivityTracker::kTypeIdActivityTracker,
+ PersistentMemoryAllocator::kSizeAny);
+ if (mem_base == nullptr)
+ return nullptr;
+
+ // Make the allocation iterable so it can be found by other processes.
+ allocator->MakeIterable(mem_reference);
+
+ return WrapUnique(new ThreadActivityTracker(mem_base, tracker_mem_size));
+ }
+
+ void PerformBasicReportValidation(const StabilityReport& report) {
+ // One report with one thread that has the expected name and id.
+ ASSERT_EQ(1, report.process_states_size());
+ const ProcessState& process_state = report.process_states(0);
+ EXPECT_EQ(base::GetCurrentProcId(), process_state.process_id());
+ ASSERT_EQ(1, process_state.threads_size());
+ const ThreadState& thread_state = process_state.threads(0);
+ EXPECT_EQ(base::PlatformThread::GetName(), thread_state.thread_name());
+#if defined(OS_WIN)
+ EXPECT_EQ(base::PlatformThread::CurrentId(), thread_state.thread_id());
+#elif defined(OS_POSIX)
+ EXPECT_EQ(base::PlatformThread::CurrentHandle().platform_handle(),
+ thread_state.thread_id());
+#endif
+ }
+
+ const FilePath& debug_file_path() const { return debug_file_path_; }
+
+ protected:
+ base::ScopedTempDir temp_dir_;
+ FilePath debug_file_path_;
+
+ std::unique_ptr<PersistentMemoryAllocator> allocator_;
+ std::unique_ptr<ThreadActivityTracker> tracker_;
+};
+
+TEST_F(StabilityReportExtractorThreadTrackerTest, CollectSuccess) {
+ // Create some activity data.
+ tracker_->PushActivity(reinterpret_cast<void*>(kTaskOrigin),
+ base::debug::Activity::ACT_TASK_RUN,
+ ActivityData::ForTask(kTaskSequenceNum));
+ tracker_->PushActivity(
+ nullptr, base::debug::Activity::ACT_LOCK_ACQUIRE,
+ ActivityData::ForLock(reinterpret_cast<void*>(kLockAddress)));
+ ThreadActivityTracker::ActivityId activity_id = tracker_->PushActivity(
+ nullptr, base::debug::Activity::ACT_EVENT_WAIT,
+ ActivityData::ForEvent(reinterpret_cast<void*>(kEventAddress)));
+ tracker_->PushActivity(nullptr, base::debug::Activity::ACT_THREAD_JOIN,
+ ActivityData::ForThread(kThreadId));
+ tracker_->PushActivity(nullptr, base::debug::Activity::ACT_PROCESS_WAIT,
+ ActivityData::ForProcess(kProcessId));
+ tracker_->PushActivity(nullptr, base::debug::Activity::ACT_GENERIC,
+ ActivityData::ForGeneric(kGenericId, kGenericData));
+ // Note: this exceeds the activity stack's capacity.
+ tracker_->PushActivity(nullptr, base::debug::Activity::ACT_THREAD_JOIN,
+ ActivityData::ForThread(kAnotherThreadId));
+
+ // Add some user data.
+ ActivityTrackerMemoryAllocator user_data_allocator(
+ allocator_.get(), GlobalActivityTracker::kTypeIdUserDataRecord,
+ GlobalActivityTracker::kTypeIdUserDataRecordFree, 1024U, 10U, false);
+ std::unique_ptr<ActivityUserData> user_data =
+ tracker_->GetUserData(activity_id, &user_data_allocator);
+ user_data->SetInt("some_int", 42);
+
+ // Validate collection returns the expected report.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+
+ // Validate the report.
+ ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
+ const ThreadState& thread_state = report.process_states(0).threads(0);
+
+ EXPECT_EQ(7, thread_state.activity_count());
+ ASSERT_EQ(6, thread_state.activities_size());
+ {
+ const Activity& activity = thread_state.activities(0);
+ EXPECT_EQ(Activity::ACT_TASK_RUN, activity.type());
+ EXPECT_EQ(kTaskOrigin, activity.origin_address());
+ EXPECT_EQ(kTaskSequenceNum, activity.task_sequence_id());
+ EXPECT_EQ(0U, activity.user_data().size());
+ }
+ {
+ const Activity& activity = thread_state.activities(1);
+ EXPECT_EQ(Activity::ACT_LOCK_ACQUIRE, activity.type());
+ EXPECT_EQ(kLockAddress, activity.lock_address());
+ EXPECT_EQ(0U, activity.user_data().size());
+ }
+ {
+ const Activity& activity = thread_state.activities(2);
+ EXPECT_EQ(Activity::ACT_EVENT_WAIT, activity.type());
+ EXPECT_EQ(kEventAddress, activity.event_address());
+ ASSERT_EQ(1U, activity.user_data().size());
+ ASSERT_TRUE(base::ContainsKey(activity.user_data(), "some_int"));
+ EXPECT_EQ(TypedValue::kSignedValue,
+ activity.user_data().at("some_int").value_case());
+ EXPECT_EQ(42, activity.user_data().at("some_int").signed_value());
+ }
+ {
+ const Activity& activity = thread_state.activities(3);
+ EXPECT_EQ(Activity::ACT_THREAD_JOIN, activity.type());
+ EXPECT_EQ(kThreadId, activity.thread_id());
+ EXPECT_EQ(0U, activity.user_data().size());
+ }
+ {
+ const Activity& activity = thread_state.activities(4);
+ EXPECT_EQ(Activity::ACT_PROCESS_WAIT, activity.type());
+ EXPECT_EQ(kProcessId, activity.process_id());
+ EXPECT_EQ(0U, activity.user_data().size());
+ }
+ {
+ const Activity& activity = thread_state.activities(5);
+ EXPECT_EQ(Activity::ACT_GENERIC, activity.type());
+ EXPECT_EQ(kGenericId, activity.generic_id());
+ EXPECT_EQ(kGenericData, activity.generic_data());
+ EXPECT_EQ(0U, activity.user_data().size());
+ }
+}
+
+TEST_F(StabilityReportExtractorThreadTrackerTest, CollectException) {
+ const void* expected_pc = reinterpret_cast<void*>(0xCAFE);
+ const void* expected_address = nullptr;
+ const uint32_t expected_code = 42U;
+
+ // Record an exception.
+ const int64_t timestamp = base::Time::Now().ToInternalValue();
+ tracker_->RecordExceptionActivity(expected_pc, expected_address,
+ base::debug::Activity::ACT_EXCEPTION,
+ ActivityData::ForException(expected_code));
+
+ // Collect report and validate.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+
+ // Validate the presence of the exception.
+ ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
+ const ThreadState& thread_state = report.process_states(0).threads(0);
+ ASSERT_TRUE(thread_state.has_exception());
+ const Exception& exception = thread_state.exception();
+ EXPECT_EQ(expected_code, exception.code());
+ EXPECT_EQ(expected_pc, reinterpret_cast<void*>(exception.program_counter()));
+ EXPECT_EQ(expected_address,
+ reinterpret_cast<void*>(exception.exception_address()));
+ const int64_t tolerance_us = 1000ULL;
+ EXPECT_LE(std::abs(timestamp - exception.time()), tolerance_us);
+}
+
+TEST_F(StabilityReportExtractorThreadTrackerTest, CollectNoException) {
+ // Record something.
+ tracker_->PushActivity(reinterpret_cast<void*>(kTaskOrigin),
+ base::debug::Activity::ACT_TASK_RUN,
+ ActivityData::ForTask(kTaskSequenceNum));
+
+ // Collect report and validate there is no exception.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+ ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
+ const ThreadState& thread_state = report.process_states(0).threads(0);
+ ASSERT_FALSE(thread_state.has_exception());
+}
+
+// Tests stability report extraction.
+class StabilityReportExtractorTest : public testing::Test {
+ public:
+ const int kMemorySize = 1 << 20; // 1MiB
+
+ StabilityReportExtractorTest() {}
+ ~StabilityReportExtractorTest() override {
+ GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
+ if (global_tracker) {
+ global_tracker->ReleaseTrackerForCurrentThreadForTesting();
+ delete global_tracker;
+ }
+ }
+
+ void SetUp() override {
+ testing::Test::SetUp();
+
+ // Set up a debug file path.
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug.pma");
+ }
+
+ const FilePath& debug_file_path() { return debug_file_path_; }
+
+ protected:
+ base::ScopedTempDir temp_dir_;
+ FilePath debug_file_path_;
+};
+
+TEST_F(StabilityReportExtractorTest, LogCollection) {
+ // Record some log messages.
+ GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
+ "", 3);
+ GlobalActivityTracker::Get()->RecordLogMessage("hello world");
+ GlobalActivityTracker::Get()->RecordLogMessage("foo bar");
+
+ // Collect the stability report.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+
+ // Validate the report's log content.
+ ASSERT_EQ(2, report.log_messages_size());
+ ASSERT_EQ("hello world", report.log_messages(0));
+ ASSERT_EQ("foo bar", report.log_messages(1));
+}
+
+TEST_F(StabilityReportExtractorTest, ProcessUserDataCollection) {
+ const char string1[] = "foo";
+ const char string2[] = "bar";
+
+ // Record some process user data.
+ GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
+ "", 3);
+ ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
+ ActivityUserData::Snapshot snapshot;
+ ASSERT_TRUE(process_data.CreateSnapshot(&snapshot));
+ ASSERT_EQ(kInternalProcessDatums, snapshot.size());
+ process_data.Set("raw", "foo", 3);
+ process_data.SetString("string", "bar");
+ process_data.SetChar("char", '9');
+ process_data.SetInt("int", -9999);
+ process_data.SetUint("uint", 9999);
+ process_data.SetBool("bool", true);
+ process_data.SetReference("ref", string1, strlen(string1));
+ process_data.SetStringReference("sref", string2);
+
+ // Collect the stability report.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+
+ // Validate the report's user data.
+ const auto& collected_data = report.global_data();
+ ASSERT_EQ(kInternalProcessDatums + 8U, collected_data.size());
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "raw"));
+ EXPECT_EQ(TypedValue::kBytesValue, collected_data.at("raw").value_case());
+ EXPECT_EQ("foo", collected_data.at("raw").bytes_value());
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "string"));
+ EXPECT_EQ(TypedValue::kStringValue, collected_data.at("string").value_case());
+ EXPECT_EQ("bar", collected_data.at("string").string_value());
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "char"));
+ EXPECT_EQ(TypedValue::kCharValue, collected_data.at("char").value_case());
+ EXPECT_EQ("9", collected_data.at("char").char_value());
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "int"));
+ EXPECT_EQ(TypedValue::kSignedValue, collected_data.at("int").value_case());
+ EXPECT_EQ(-9999, collected_data.at("int").signed_value());
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "uint"));
+ EXPECT_EQ(TypedValue::kUnsignedValue, collected_data.at("uint").value_case());
+ EXPECT_EQ(9999U, collected_data.at("uint").unsigned_value());
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "bool"));
+ EXPECT_EQ(TypedValue::kBoolValue, collected_data.at("bool").value_case());
+ EXPECT_TRUE(collected_data.at("bool").bool_value());
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "ref"));
+ EXPECT_EQ(TypedValue::kBytesReference, collected_data.at("ref").value_case());
+ const TypedValue::Reference& ref = collected_data.at("ref").bytes_reference();
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(string1), ref.address());
+ EXPECT_EQ(strlen(string1), static_cast<uint64_t>(ref.size()));
+
+ ASSERT_TRUE(base::ContainsKey(collected_data, "sref"));
+ EXPECT_EQ(TypedValue::kStringReference,
+ collected_data.at("sref").value_case());
+ const TypedValue::Reference& sref =
+ collected_data.at("sref").string_reference();
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(string2), sref.address());
+ EXPECT_EQ(strlen(string2), static_cast<uint64_t>(sref.size()));
+}
+
+TEST_F(StabilityReportExtractorTest, FieldTrialCollection) {
+ // Record some data.
+ GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
+ "", 3);
+ ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
+ process_data.SetString("string", "bar");
+ process_data.SetString("FieldTrial.string", "bar");
+ process_data.SetString("FieldTrial.foo", "bar");
+
+ // Collect the stability report.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+
+ // Validate the report's experiment and global data.
+ ASSERT_EQ(2, report.field_trials_size());
+ EXPECT_NE(0U, report.field_trials(0).name_id());
+ EXPECT_NE(0U, report.field_trials(0).group_id());
+ EXPECT_NE(0U, report.field_trials(1).name_id());
+ EXPECT_EQ(report.field_trials(0).group_id(),
+ report.field_trials(1).group_id());
+
+ // Expect 1 key/value pair.
+ const auto& collected_data = report.global_data();
+ EXPECT_EQ(kInternalProcessDatums + 1U, collected_data.size());
+ EXPECT_TRUE(base::ContainsKey(collected_data, "string"));
+}
+
+TEST_F(StabilityReportExtractorTest, ModuleCollection) {
+ // Record some module information.
+ GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
+ "", 3);
+
+ base::debug::GlobalActivityTracker::ModuleInfo module_info = {};
+ module_info.is_loaded = true;
+ module_info.address = 0x123456;
+ module_info.load_time = 1111LL;
+ module_info.size = 0x2d000;
+ module_info.timestamp = 0xCAFECAFE;
+ module_info.age = 1;
+ crashpad::UUID debug_uuid;
+ debug_uuid.InitializeFromString("11223344-5566-7788-abcd-0123456789ab");
+ memcpy(module_info.identifier, &debug_uuid, sizeof(module_info.identifier));
+ module_info.file = "foo";
+ module_info.debug_file = "bar";
+
+ GlobalActivityTracker::Get()->RecordModuleInfo(module_info);
+
+ // Collect the stability report.
+ StabilityReport report;
+ ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+
+ // Validate the report's modules content.
+ ASSERT_EQ(1, report.process_states_size());
+ const ProcessState& process_state = report.process_states(0);
+ ASSERT_EQ(1, process_state.modules_size());
+
+ const CodeModule collected_module = process_state.modules(0);
+ EXPECT_EQ(module_info.address,
+ static_cast<uintptr_t>(collected_module.base_address()));
+ EXPECT_EQ(module_info.size, static_cast<size_t>(collected_module.size()));
+ EXPECT_EQ(module_info.file, collected_module.code_file());
+ EXPECT_EQ("CAFECAFE2d000", collected_module.code_identifier());
+ EXPECT_EQ(module_info.debug_file, collected_module.debug_file());
+ EXPECT_EQ("1122334455667788ABCD0123456789AB1",
+ collected_module.debug_identifier());
+ EXPECT_EQ("", collected_module.version());
+ EXPECT_EQ(0LL, collected_module.shrink_down_delta());
+ EXPECT_EQ(!module_info.is_loaded, collected_module.is_unloaded());
+}
+
+} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/watcher_metrics_provider_win.cc b/chromium/components/browser_watcher/watcher_metrics_provider_win.cc
index 6376079ba23..a95e63a8bc1 100644
--- a/chromium/components/browser_watcher/watcher_metrics_provider_win.cc
+++ b/chromium/components/browser_watcher/watcher_metrics_provider_win.cc
@@ -14,6 +14,7 @@
#include "base/bind.h"
#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_macros.h"
@@ -22,6 +23,8 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "base/win/registry.h"
#include "components/browser_watcher/features.h"
#include "components/browser_watcher/postmortem_report_collector.h"
@@ -161,6 +164,14 @@ void LogCollectionInitStatus(CollectionInitializationStatus status) {
INIT_STATUS_MAX);
}
+// Returns a task runner appropriate for running background tasks that perform
+// file I/O.
+scoped_refptr<base::TaskRunner> CreateBackgroundTaskRunner() {
+ return base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+}
+
} // namespace
const char WatcherMetricsProviderWin::kBrowserExitCodeHistogramName[] =
@@ -170,18 +181,15 @@ WatcherMetricsProviderWin::WatcherMetricsProviderWin(
const base::string16& registry_path,
const base::FilePath& user_data_dir,
const base::FilePath& crash_dir,
- const GetExecutableDetailsCallback& exe_details_cb,
- base::TaskRunner* io_task_runner)
+ const GetExecutableDetailsCallback& exe_details_cb)
: recording_enabled_(false),
cleanup_scheduled_(false),
registry_path_(registry_path),
user_data_dir_(user_data_dir),
crash_dir_(crash_dir),
exe_details_cb_(exe_details_cb),
- io_task_runner_(io_task_runner),
- weak_ptr_factory_(this) {
- DCHECK(io_task_runner_);
-}
+ task_runner_(CreateBackgroundTaskRunner()),
+ weak_ptr_factory_(this) {}
WatcherMetricsProviderWin::~WatcherMetricsProviderWin() {
}
@@ -194,9 +202,10 @@ void WatcherMetricsProviderWin::OnRecordingDisabled() {
if (!recording_enabled_ && !cleanup_scheduled_) {
// When metrics reporting is disabled, the providers get an
// OnRecordingDisabled notification at startup. Use that first notification
- // to issue the cleanup task.
- io_task_runner_->PostTask(
- FROM_HERE, base::Bind(&DeleteExitCodeRegistryKey, registry_path_));
+ // to issue the cleanup task. Runs in the background because interacting
+ // with the registry can block.
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&DeleteExitCodeRegistryKey, registry_path_));
cleanup_scheduled_ = true;
}
@@ -215,44 +224,56 @@ void WatcherMetricsProviderWin::ProvideStabilityMetrics(
void WatcherMetricsProviderWin::CollectPostmortemReports(
const base::Closure& done_callback) {
- io_task_runner_->PostTaskAndReply(
+ task_runner_->PostTaskAndReply(
FROM_HERE,
- base::Bind(
- &WatcherMetricsProviderWin::CollectPostmortemReportsOnBlockingPool,
- weak_ptr_factory_.GetWeakPtr()),
+ base::BindOnce(&WatcherMetricsProviderWin::CollectPostmortemReportsImpl,
+ weak_ptr_factory_.GetWeakPtr()),
done_callback);
}
-void WatcherMetricsProviderWin::CollectPostmortemReportsOnBlockingPool() {
- // Note: the feature controls both instrumentation and collection.
+// TODO(manzagop): consider mechanisms for partial collection if this is to be
+// used on a critical path.
+void WatcherMetricsProviderWin::CollectPostmortemReportsImpl() {
+ SCOPED_UMA_HISTOGRAM_TIMER("ActivityTracker.Collect.TotalTime");
+
bool is_stability_debugging_on =
base::FeatureList::IsEnabled(browser_watcher::kStabilityDebuggingFeature);
if (!is_stability_debugging_on) {
- // TODO(manzagop): delete possible leftover data.
- return;
+ return; // TODO(manzagop): scan for possible data to delete?
}
- SCOPED_UMA_HISTOGRAM_TIMER("ActivityTracker.Collect.TotalTime");
-
if (user_data_dir_.empty() || crash_dir_.empty()) {
- LOG(ERROR) << "User data directory or crash directory is unknown.";
LogCollectionInitStatus(UNKNOWN_DIR);
return;
}
- // Determine the stability directory and the stability file for the current
- // process.
+ // Determine which files to harvest.
base::FilePath stability_dir = GetStabilityDir(user_data_dir_);
+
base::FilePath current_stability_file;
if (!GetStabilityFileForProcess(base::Process::Current(), user_data_dir_,
&current_stability_file)) {
- LOG(ERROR) << "Failed to get the current stability file.";
LogCollectionInitStatus(GET_STABILITY_FILE_PATH_FAILED);
return;
}
- const std::set<base::FilePath>& excluded_debug_files = {
+ const std::set<base::FilePath>& excluded_stability_files = {
current_stability_file};
+ std::vector<base::FilePath> stability_files = GetStabilityFiles(
+ stability_dir, GetStabilityFilePattern(), excluded_stability_files);
+ UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount",
+ stability_files.size());
+
+ // If postmortem collection is disabled, delete the files.
+ const bool should_collect = base::GetFieldTrialParamByFeatureAsBool(
+ browser_watcher::kStabilityDebuggingFeature,
+ browser_watcher::kCollectPostmortemParam, false);
+ if (!should_collect) {
+ PostmortemDeleter deleter;
+ deleter.Process(stability_files);
+ return;
+ }
+
// Create a database. Note: Chrome already has a g_database in crashpad.cc but
// it has internal linkage. Create a new one.
std::unique_ptr<crashpad::CrashReportDatabase> crashpad_database =
@@ -273,10 +294,8 @@ void WatcherMetricsProviderWin::CollectPostmortemReportsOnBlockingPool() {
SystemSessionAnalyzer analyzer(kSystemSessionsToInspect);
PostmortemReportCollector collector(
base::UTF16ToUTF8(product_name), base::UTF16ToUTF8(version_number),
- base::UTF16ToUTF8(channel_name), &analyzer);
- collector.CollectAndSubmitAllPendingReports(
- stability_dir, GetStabilityFilePattern(), excluded_debug_files,
- crashpad_database.get());
+ base::UTF16ToUTF8(channel_name), crashpad_database.get(), &analyzer);
+ collector.Process(stability_files);
}
} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/watcher_metrics_provider_win.h b/chromium/components/browser_watcher/watcher_metrics_provider_win.h
index 65c5307d301..fbd408233c0 100644
--- a/chromium/components/browser_watcher/watcher_metrics_provider_win.h
+++ b/chromium/components/browser_watcher/watcher_metrics_provider_win.h
@@ -11,7 +11,6 @@
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "base/task_runner.h"
-#include "base/threading/thread_checker.h"
#include "components/metrics/metrics_provider.h"
namespace browser_watcher {
@@ -26,14 +25,11 @@ class WatcherMetricsProviderWin : public metrics::MetricsProvider {
static const char kBrowserExitCodeHistogramName[];
- // Initializes the reporter. |io_task_runner| is used for collecting
- // postmortem reports and clearing leftover data in registry if metrics
- // reporting is disabled.
+ // Initializes the reporter.
WatcherMetricsProviderWin(const base::string16& registry_path,
const base::FilePath& user_data_dir,
const base::FilePath& crash_dir,
- const GetExecutableDetailsCallback& exe_details_cb,
- base::TaskRunner* io_task_runner);
+ const GetExecutableDetailsCallback& exe_details_cb);
~WatcherMetricsProviderWin() override;
// metrics::MetricsProvider implementation.
@@ -59,7 +55,7 @@ class WatcherMetricsProviderWin : public metrics::MetricsProvider {
private:
// TODO(manzagop): avoid collecting reports for clean exits from the fast exit
// path.
- void CollectPostmortemReportsOnBlockingPool();
+ void CollectPostmortemReportsImpl();
bool recording_enabled_;
bool cleanup_scheduled_;
@@ -67,7 +63,11 @@ class WatcherMetricsProviderWin : public metrics::MetricsProvider {
const base::FilePath user_data_dir_;
const base::FilePath crash_dir_;
GetExecutableDetailsCallback exe_details_cb_;
- scoped_refptr<base::TaskRunner> io_task_runner_;
+
+ // Used for collecting postmortem reports and clearing leftover data in
+ // registry if metrics reporting is disabled.
+ scoped_refptr<base::TaskRunner> task_runner_;
+
base::WeakPtrFactory<WatcherMetricsProviderWin> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(WatcherMetricsProviderWin);
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 fbce251f84b..af78d5d816b 100644
--- a/chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
+++ b/chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
@@ -13,8 +13,8 @@
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/test/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
#include "base/test/test_reg_util_win.h"
-#include "base/test/test_simple_task_runner.h"
#include "base/win/registry.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,7 +36,6 @@ class WatcherMetricsProviderWinTest : public testing::Test {
ASSERT_NO_FATAL_FAILURE(
override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
- test_task_runner_ = new base::TestSimpleTaskRunner();
}
void AddProcessExitCode(bool use_own_pid, int exit_code) {
@@ -66,9 +65,9 @@ class WatcherMetricsProviderWinTest : public testing::Test {
}
protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
registry_util::RegistryOverrideManager override_manager_;
base::HistogramTester histogram_tester_;
- scoped_refptr<base::TestSimpleTaskRunner> test_task_runner_;
};
} // namespace
@@ -81,9 +80,9 @@ TEST_F(WatcherMetricsProviderWinTest, RecordsStabilityHistogram) {
// Record a single failure.
AddProcessExitCode(false, 100);
- WatcherMetricsProviderWin provider(
- kRegistryPath, base::FilePath(), base::FilePath(),
- GetExecutableDetailsCallback(), test_task_runner_.get());
+ WatcherMetricsProviderWin provider(kRegistryPath, base::FilePath(),
+ base::FilePath(),
+ GetExecutableDetailsCallback());
provider.ProvideStabilityMetrics(NULL);
histogram_tester_.ExpectBucketCount(
@@ -105,9 +104,9 @@ TEST_F(WatcherMetricsProviderWinTest, DoesNotReportOwnProcessId) {
// Record own process as STILL_ACTIVE.
AddProcessExitCode(true, STILL_ACTIVE);
- WatcherMetricsProviderWin provider(
- kRegistryPath, base::FilePath(), base::FilePath(),
- GetExecutableDetailsCallback(), test_task_runner_.get());
+ WatcherMetricsProviderWin provider(kRegistryPath, base::FilePath(),
+ base::FilePath(),
+ GetExecutableDetailsCallback());
provider.ProvideStabilityMetrics(NULL);
histogram_tester_.ExpectUniqueSample(
@@ -127,22 +126,19 @@ TEST_F(WatcherMetricsProviderWinTest, DeletesExitcodeKeyWhenNotReporting) {
// Record a single failure.
AddProcessExitCode(false, 100);
+ // Verify that the key exists prior to deletion.
+ base::win::RegKey key;
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, kRegistryPath, KEY_READ));
+
// Make like the user is opted out of reporting.
- WatcherMetricsProviderWin provider(
- kRegistryPath, base::FilePath(), base::FilePath(),
- GetExecutableDetailsCallback(), test_task_runner_.get());
+ WatcherMetricsProviderWin provider(kRegistryPath, base::FilePath(),
+ base::FilePath(),
+ GetExecutableDetailsCallback());
provider.OnRecordingDisabled();
- base::win::RegKey key;
- {
- // The deletion should be scheduled to the test_task_runner, and not happen
- // immediately.
- ASSERT_EQ(ERROR_SUCCESS,
- key.Open(HKEY_CURRENT_USER, kRegistryPath, KEY_READ));
- }
-
// Flush the task(s).
- test_task_runner_->RunPendingTasks();
+ scoped_task_environment_.RunUntilIdle();
// Make sure the subkey for the pseudo process has been deleted on reporting.
ASSERT_EQ(ERROR_FILE_NOT_FOUND,
diff --git a/chromium/components/browsing_data/core/BUILD.gn b/chromium/components/browsing_data/core/BUILD.gn
index c2761d75a39..4f0e4796ad6 100644
--- a/chromium/components/browsing_data/core/BUILD.gn
+++ b/chromium/components/browsing_data/core/BUILD.gn
@@ -13,6 +13,8 @@ static_library("core") {
"clear_browsing_data_tab.h",
"counters/autofill_counter.cc",
"counters/autofill_counter.h",
+ "counters/bookmark_counter.cc",
+ "counters/bookmark_counter.h",
"counters/browsing_data_counter.cc",
"counters/browsing_data_counter.h",
"counters/history_counter.cc",
@@ -21,6 +23,8 @@ static_library("core") {
"counters/passwords_counter.h",
"counters/site_settings_counter.cc",
"counters/site_settings_counter.h",
+ "counters/sync_tracker.cc",
+ "counters/sync_tracker.h",
"history_notice_utils.cc",
"history_notice_utils.h",
"pref_names.cc",
@@ -30,12 +34,13 @@ static_library("core") {
deps = [
"//base",
"//components/autofill/core/browser",
- "//components/content_settings/core/browser:browser",
- "//components/content_settings/core/common:common",
+ "//components/bookmarks/browser",
+ "//components/content_settings/core/browser",
+ "//components/content_settings/core/common",
"//components/history/core/browser",
"//components/password_manager/core/browser",
- "//components/pref_registry:pref_registry",
- "//components/prefs:prefs",
+ "//components/pref_registry",
+ "//components/prefs",
"//components/strings",
"//components/sync",
"//components/version_info",
diff --git a/chromium/components/browsing_data/core/DEPS b/chromium/components/browsing_data/core/DEPS
index c8a900dd7de..17612bfe5c3 100644
--- a/chromium/components/browsing_data/core/DEPS
+++ b/chromium/components/browsing_data/core/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/autofill/core/browser",
+ "+components/bookmarks/browser",
"+components/browser_sync",
"+components/content_settings",
"+components/history/core/browser",
diff --git a/chromium/components/browsing_data/core/browsing_data_utils.cc b/chromium/components/browsing_data/core/browsing_data_utils.cc
index ea7e7bb5cb4..5e04932b640 100644
--- a/chromium/components/browsing_data/core/browsing_data_utils.cc
+++ b/chromium/components/browsing_data/core/browsing_data_utils.cc
@@ -4,6 +4,7 @@
#include "components/browsing_data/core/browsing_data_utils.h"
+#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "components/browsing_data/core/counters/autofill_counter.h"
#include "components/browsing_data/core/counters/history_counter.h"
@@ -65,6 +66,31 @@ void RecordDeletionForPeriod(TimePeriod period) {
}
}
+void RecordTimePeriodChange(TimePeriod period) {
+ switch (period) {
+ case TimePeriod::LAST_HOUR:
+ base::RecordAction(base::UserMetricsAction(
+ "ClearBrowsingData_TimePeriodChanged_LastHour"));
+ break;
+ case TimePeriod::LAST_DAY:
+ base::RecordAction(base::UserMetricsAction(
+ "ClearBrowsingData_TimePeriodChanged_LastDay"));
+ break;
+ case TimePeriod::LAST_WEEK:
+ base::RecordAction(base::UserMetricsAction(
+ "ClearBrowsingData_TimePeriodChanged_LastWeek"));
+ break;
+ case TimePeriod::FOUR_WEEKS:
+ base::RecordAction(base::UserMetricsAction(
+ "ClearBrowsingData_TimePeriodChanged_LastMonth"));
+ break;
+ case TimePeriod::ALL_TIME:
+ base::RecordAction(base::UserMetricsAction(
+ "ClearBrowsingData_TimePeriodChanged_Everything"));
+ break;
+ }
+}
+
base::string16 GetCounterTextFromResult(
const BrowsingDataCounter::Result* result) {
base::string16 text;
diff --git a/chromium/components/browsing_data/core/browsing_data_utils.h b/chromium/components/browsing_data/core/browsing_data_utils.h
index 3e087f8cf1e..d97e68cd69d 100644
--- a/chromium/components/browsing_data/core/browsing_data_utils.h
+++ b/chromium/components/browsing_data/core/browsing_data_utils.h
@@ -51,6 +51,9 @@ base::Time CalculateEndDeleteTime(TimePeriod time_period);
// Records the UMA action of UI-triggered data deletion for |time_period|.
void RecordDeletionForPeriod(TimePeriod time_period);
+// Records the UMA action of a change of the clear browsing data time period.
+void RecordTimePeriodChange(TimePeriod period);
+
// Constructs the text to be displayed by a counter from the given |result|.
// Currently this can only be used for counters for which the Result is
// defined in components/browsing_data/core/counters.
diff --git a/chromium/components/browsing_data/core/clear_browsing_data_tab.h b/chromium/components/browsing_data/core/clear_browsing_data_tab.h
index c6c0b5bc2cd..22efd849bb5 100644
--- a/chromium/components/browsing_data/core/clear_browsing_data_tab.h
+++ b/chromium/components/browsing_data/core/clear_browsing_data_tab.h
@@ -11,6 +11,10 @@ namespace browsing_data {
// advanced tab and manage their state separately. It is important that all
// preferences and the timeperiod selection have the same type. The default
// value for dialogs without separate tabs is advanced.
+// TODO(dullweber): Maybe rename "ADVANCED" to "DEFAULT" because it is used in
+// multiple places without a differentiation between advanced and basic.
+//
+// Do not change the values here, as they are used for UMA histograms.
//
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.browsing_data
diff --git a/chromium/components/browsing_data/core/counters/autofill_counter.cc b/chromium/components/browsing_data/core/counters/autofill_counter.cc
index 0b9a7d8deaa..30127cb0294 100644
--- a/chromium/components/browsing_data/core/counters/autofill_counter.cc
+++ b/chromium/components/browsing_data/core/counters/autofill_counter.cc
@@ -31,26 +31,21 @@ AutofillCounter::AutofillCounter(
scoped_refptr<autofill::AutofillWebDataService> web_data_service,
syncer::SyncService* sync_service)
: web_data_service_(web_data_service),
- sync_service_(sync_service),
+ sync_tracker_(this, sync_service),
suggestions_query_(0),
credit_cards_query_(0),
addresses_query_(0),
num_suggestions_(0),
num_credit_cards_(0),
- num_addresses_(0),
- autofill_sync_enabled_() {}
+ num_addresses_(0) {}
AutofillCounter::~AutofillCounter() {
CancelAllRequests();
- if (sync_service_)
- sync_service_->RemoveObserver(this);
}
void AutofillCounter::OnInitialized() {
DCHECK(web_data_service_);
- if (sync_service_)
- sync_service_->AddObserver(this);
- autofill_sync_enabled_ = IsAutofillSyncEnabled(sync_service_);
+ sync_tracker_.OnInitialized(base::Bind(&IsAutofillSyncEnabled));
}
const char* AutofillCounter::GetPrefName() const {
@@ -172,7 +167,7 @@ void AutofillCounter::OnWebDataServiceRequestDone(
auto reported_result = base::MakeUnique<AutofillResult>(
this, num_suggestions_, num_credit_cards_, num_addresses_,
- autofill_sync_enabled_);
+ sync_tracker_.IsSyncActive());
ReportResult(std::move(reported_result));
}
@@ -185,14 +180,6 @@ void AutofillCounter::CancelAllRequests() {
web_data_service_->CancelRequest(addresses_query_);
}
-void AutofillCounter::OnStateChanged(syncer::SyncService* sync) {
- bool sync_enabled_new = IsAutofillSyncEnabled(sync);
- if (autofill_sync_enabled_ != sync_enabled_new) {
- autofill_sync_enabled_ = sync_enabled_new;
- Restart();
- }
-}
-
// AutofillCounter::AutofillResult ---------------------------------------------
AutofillCounter::AutofillResult::AutofillResult(const AutofillCounter* source,
diff --git a/chromium/components/browsing_data/core/counters/autofill_counter.h b/chromium/components/browsing_data/core/counters/autofill_counter.h
index 7352a0c4f68..8c657dcfc4c 100644
--- a/chromium/components/browsing_data/core/counters/autofill_counter.h
+++ b/chromium/components/browsing_data/core/counters/autofill_counter.h
@@ -10,7 +10,7 @@
#include "base/time/time.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/browsing_data/core/counters/browsing_data_counter.h"
-#include "components/sync/driver/sync_service_observer.h"
+#include "components/browsing_data/core/counters/sync_tracker.h"
#include "components/webdata/common/web_data_service_consumer.h"
namespace autofill {
@@ -20,8 +20,7 @@ class AutofillWebDataService;
namespace browsing_data {
class AutofillCounter : public browsing_data::BrowsingDataCounter,
- public WebDataServiceConsumer,
- public syncer::SyncServiceObserver {
+ public WebDataServiceConsumer {
public:
class AutofillResult : public SyncResult {
public:
@@ -68,16 +67,13 @@ class AutofillCounter : public browsing_data::BrowsingDataCounter,
WebDataServiceBase::Handle handle,
std::unique_ptr<WDTypedResult> result) override;
- // SyncServiceObserver implementation.
- void OnStateChanged(syncer::SyncService* sync) override;
-
// Cancel all pending requests to AutofillWebdataService.
void CancelAllRequests();
base::ThreadChecker thread_checker_;
scoped_refptr<autofill::AutofillWebDataService> web_data_service_;
- syncer::SyncService* sync_service_;
+ SyncTracker sync_tracker_;
WebDataServiceBase::Handle suggestions_query_;
WebDataServiceBase::Handle credit_cards_query_;
@@ -86,7 +82,6 @@ class AutofillCounter : public browsing_data::BrowsingDataCounter,
ResultInt num_suggestions_;
ResultInt num_credit_cards_;
ResultInt num_addresses_;
- bool autofill_sync_enabled_;
base::Time period_start_for_testing_;
diff --git a/chromium/components/browsing_data/core/counters/bookmark_counter.cc b/chromium/components/browsing_data/core/counters/bookmark_counter.cc
new file mode 100644
index 00000000000..03681a6dc9c
--- /dev/null
+++ b/chromium/components/browsing_data/core/counters/bookmark_counter.cc
@@ -0,0 +1,102 @@
+// 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/browsing_data/core/counters/bookmark_counter.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/bookmarks/browser/base_bookmark_model_observer.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+
+namespace {
+
+int CountBookmarksFromNode(const bookmarks::BookmarkNode* node,
+ base::Time period_start) {
+ int count = 0;
+ if (node->is_url()) {
+ if (node->date_added() >= period_start)
+ ++count;
+ } else {
+ for (int i = 0; i < node->child_count(); ++i)
+ count += CountBookmarksFromNode(node->GetChild(i), period_start);
+ }
+ return count;
+}
+
+using BookmarkModelCallback =
+ base::OnceCallback<void(const bookmarks::BookmarkModel*)>;
+
+// This class waits for the |bookmark_model| to load, then executes |callback|
+// and destroys itself afterwards.
+class BookmarkModelHelper : public bookmarks::BaseBookmarkModelObserver {
+ public:
+ BookmarkModelHelper(bookmarks::BookmarkModel* bookmark_model,
+ BookmarkModelCallback callback)
+ : bookmark_model_(bookmark_model), callback_(std::move(callback)) {
+ DCHECK(!bookmark_model_->loaded());
+ bookmark_model_->AddObserver(this);
+ }
+
+ void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
+ bool ids_reassigned) override {
+ std::move(callback_).Run(model);
+ delete this;
+ };
+
+ void BookmarkModelBeingDeleted(
+ bookmarks::BookmarkModel* bookmark_model) override {
+ // Don't leak this instance if the BookmarkModel never loads.
+ delete this;
+ }
+
+ void BookmarkModelChanged() override {}
+
+ private:
+ ~BookmarkModelHelper() override { bookmark_model_->RemoveObserver(this); }
+
+ bookmarks::BookmarkModel* bookmark_model_;
+ BookmarkModelCallback callback_;
+};
+
+} // namespace
+
+namespace browsing_data {
+
+const char BookmarkCounter::kPrefName[] =
+ "browser.clear_data.fake.pref.bookmarks";
+
+BookmarkCounter::BookmarkCounter(bookmarks::BookmarkModel* bookmark_model)
+ : bookmark_model_(bookmark_model), weak_ptr_factory_(this) {
+ DCHECK(bookmark_model);
+}
+
+BookmarkCounter::~BookmarkCounter() {}
+
+void BookmarkCounter::OnInitialized() {}
+
+const char* BookmarkCounter::GetPrefName() const {
+ return kPrefName;
+}
+
+void BookmarkCounter::Count() {
+ if (bookmark_model_->loaded()) {
+ CountBookmarks(bookmark_model_);
+ } else {
+ new BookmarkModelHelper(bookmark_model_,
+ base::BindOnce(&BookmarkCounter::CountBookmarks,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void BookmarkCounter::CountBookmarks(
+ const bookmarks::BookmarkModel* bookmark_model) {
+ base::Time start = GetPeriodStart();
+ int count =
+ CountBookmarksFromNode(bookmark_model->bookmark_bar_node(), start) +
+ CountBookmarksFromNode(bookmark_model->other_node(), start) +
+ CountBookmarksFromNode(bookmark_model->mobile_node(), start);
+ ReportResult(count);
+}
+
+} // namespace browsing_data
diff --git a/chromium/components/browsing_data/core/counters/bookmark_counter.h b/chromium/components/browsing_data/core/counters/bookmark_counter.h
new file mode 100644
index 00000000000..59f09f65637
--- /dev/null
+++ b/chromium/components/browsing_data/core/counters/bookmark_counter.h
@@ -0,0 +1,41 @@
+// 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_BROWSING_DATA_CORE_COUNTERS_BOOKMARK_COUNTER_H_
+#define COMPONENTS_BROWSING_DATA_CORE_COUNTERS_BOOKMARK_COUNTER_H_
+
+#include <memory>
+
+#include "components/browsing_data/core/counters/browsing_data_counter.h"
+
+namespace bookmarks {
+class BookmarkModel;
+}
+
+namespace browsing_data {
+
+class BookmarkCounter : public browsing_data::BrowsingDataCounter {
+ public:
+ // This is not a registered preference. It is here because a pref_name
+ // is required for mapping a counter to ui elements.
+ static const char kPrefName[];
+
+ explicit BookmarkCounter(bookmarks::BookmarkModel* bookmark_model);
+ ~BookmarkCounter() override;
+
+ void OnInitialized() override;
+
+ const char* GetPrefName() const override;
+
+ private:
+ void Count() override;
+ void CountBookmarks(const bookmarks::BookmarkModel* bookmark_model);
+
+ bookmarks::BookmarkModel* bookmark_model_;
+ base::WeakPtrFactory<BookmarkCounter> weak_ptr_factory_;
+};
+
+} // namespace browsing_data
+
+#endif // COMPONENTS_BROWSING_DATA_CORE_COUNTERS_BOOKMARK_COUNTER_H_
diff --git a/chromium/components/browsing_data/core/counters/browsing_data_counter.cc b/chromium/components/browsing_data/core/counters/browsing_data_counter.cc
index bc26908d6b7..62befe0f59a 100644
--- a/chromium/components/browsing_data/core/counters/browsing_data_counter.cc
+++ b/chromium/components/browsing_data/core/counters/browsing_data_counter.cc
@@ -19,8 +19,7 @@ static const int kDelayUntilReadyToShowResultMs = 1000;
}
BrowsingDataCounter::BrowsingDataCounter()
- : initialized_(false),
- state_(State::IDLE) {}
+ : initialized_(false), use_delay_(true), state_(State::IDLE) {}
BrowsingDataCounter::~BrowsingDataCounter() {}
@@ -40,9 +39,22 @@ void BrowsingDataCounter::Init(PrefService* pref_service,
OnInitialized();
}
+void BrowsingDataCounter::InitWithoutPref(base::Time begin_time,
+ const Callback& callback) {
+ DCHECK(!initialized_);
+ use_delay_ = false;
+ callback_ = callback;
+ clear_browsing_data_tab_ = ClearBrowsingDataTab::ADVANCED;
+ begin_time_ = begin_time;
+ initialized_ = true;
+ OnInitialized();
+}
+
void BrowsingDataCounter::OnInitialized() {}
base::Time BrowsingDataCounter::GetPeriodStart() {
+ if (period_.GetPrefName().empty())
+ return begin_time_;
return CalculateBeginDeleteTime(static_cast<TimePeriod>(*period_));
}
@@ -59,9 +71,14 @@ void BrowsingDataCounter::Restart() {
state_transitions_.clear();
state_transitions_.push_back(state_);
- timer_.Start(FROM_HERE,
- base::TimeDelta::FromMilliseconds(kDelayUntilShowCalculatingMs),
- this, &BrowsingDataCounter::TransitionToShowCalculating);
+ if (use_delay_) {
+ timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kDelayUntilShowCalculatingMs), this,
+ &BrowsingDataCounter::TransitionToShowCalculating);
+ } else {
+ state_ = State::READY_TO_REPORT_RESULT;
+ }
Count();
}
diff --git a/chromium/components/browsing_data/core/counters/browsing_data_counter.h b/chromium/components/browsing_data/core/counters/browsing_data_counter.h
index a6cb28804d0..3173a6d3e0e 100644
--- a/chromium/components/browsing_data/core/counters/browsing_data_counter.h
+++ b/chromium/components/browsing_data/core/counters/browsing_data_counter.h
@@ -107,6 +107,11 @@ class BrowsingDataCounter {
ClearBrowsingDataTab clear_browsing_data_tab,
const Callback& callback);
+ // Can be called instead of |Init()|, to create a counter that doesn't
+ // observe pref changes and counts data that was changed since |begin_time|.
+ // This mode doesn't use delayed responses.
+ void InitWithoutPref(base::Time begin_time, const Callback& callback);
+
// Name of the preference associated with this counter.
virtual const char* GetPrefName() const = 0;
@@ -168,9 +173,15 @@ class BrowsingDataCounter {
// is to be deleted.
IntegerPrefMember period_;
+ // This time period is used when |period_| is not initialized.
+ base::Time begin_time_;
+
// Whether this class was properly initialized by calling |Init|.
bool initialized_;
+ // Whether to introduce a delayed response to avoid flickering.
+ bool use_delay_;
+
// State of the counter.
State state_;
diff --git a/chromium/components/browsing_data/core/counters/history_counter.cc b/chromium/components/browsing_data/core/counters/history_counter.cc
index fd22f4a7c65..376e5e8aa73 100644
--- a/chromium/components/browsing_data/core/counters/history_counter.cc
+++ b/chromium/components/browsing_data/core/counters/history_counter.cc
@@ -23,22 +23,19 @@ HistoryCounter::HistoryCounter(
syncer::SyncService* sync_service)
: history_service_(history_service),
web_history_service_callback_(callback),
- sync_service_(sync_service),
+ sync_tracker_(this, sync_service),
has_synced_visits_(false),
local_counting_finished_(false),
web_counting_finished_(false),
- history_sync_enabled_(false),
- weak_ptr_factory_(this) {}
-
-HistoryCounter::~HistoryCounter() {
- if (sync_service_)
- sync_service_->RemoveObserver(this);
+ weak_ptr_factory_(this) {
+ DCHECK(history_service_);
}
+HistoryCounter::~HistoryCounter() {}
+
void HistoryCounter::OnInitialized() {
- if (sync_service_)
- sync_service_->AddObserver(this);
- history_sync_enabled_ = !!web_history_service_callback_.Run();
+ sync_tracker_.OnInitialized(base::Bind(&HistoryCounter::IsHistorySyncEnabled,
+ base::Unretained(this)));
}
bool HistoryCounter::HasTrackedTasks() {
@@ -51,6 +48,12 @@ const char* HistoryCounter::GetPrefName() const {
: browsing_data::prefs::kDeleteBrowsingHistory;
}
+history::WebHistoryService* HistoryCounter::GetWebHistoryService() {
+ if (web_history_service_callback_)
+ return web_history_service_callback_.Run();
+ return nullptr;
+}
+
void HistoryCounter::Count() {
// Reset the state.
cancelable_task_tracker_.TryCancelAll();
@@ -69,8 +72,7 @@ void HistoryCounter::Count() {
&cancelable_task_tracker_);
// If the history sync is enabled, test if there is at least one synced item.
- history::WebHistoryService* web_history =
- web_history_service_callback_.Run();
+ history::WebHistoryService* web_history = GetWebHistoryService();
if (!web_history) {
web_counting_finished_ = true;
@@ -87,10 +89,34 @@ void HistoryCounter::Count() {
options.max_count = 1;
options.begin_time = GetPeriodStart();
options.end_time = base::Time::Max();
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation("web_history_counter",
+ "web_history_service",
+ R"(
+ semantics {
+ description:
+ "If history sync is enabled, this queries history.google.com to "
+ "determine if there is any synced history. This information is "
+ "displayed in the Clear Browsing Data dialog."
+ trigger:
+ "Checking the 'Browsing history' option in the Clear Browsing Data "
+ "dialog, or enabling history sync while the dialog is open."
+ data:
+ "A version info token to resolve transaction conflicts, and an "
+ "OAuth2 token authenticating the user."
+ }
+ policy {
+ chrome_policy {
+ SyncDisabled {
+ SyncDisabled: true
+ }
+ }
+ })");
web_history_request_ = web_history->QueryHistory(
base::string16(), options,
base::Bind(&HistoryCounter::OnGetWebHistoryCount,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ partial_traffic_annotation);
// TODO(msramek): Include web history count when there is an API for it.
}
@@ -151,18 +177,12 @@ void HistoryCounter::MergeResults() {
return;
ReportResult(base::MakeUnique<HistoryResult>(
- this, local_result_, history_sync_enabled_, has_synced_visits_));
+ this, local_result_, sync_tracker_.IsSyncActive(), has_synced_visits_));
}
-void HistoryCounter::OnStateChanged(syncer::SyncService* sync) {
- bool history_sync_enabled_new_state = !!web_history_service_callback_.Run();
-
- // If the history sync was just enabled or disabled, restart the counter
- // so that we update the result accordingly.
- if (history_sync_enabled_ != history_sync_enabled_new_state) {
- history_sync_enabled_ = history_sync_enabled_new_state;
- Restart();
- }
+bool HistoryCounter::IsHistorySyncEnabled(
+ const syncer::SyncService* sync_service) {
+ return !!GetWebHistoryService();
}
HistoryCounter::HistoryResult::HistoryResult(const HistoryCounter* source,
diff --git a/chromium/components/browsing_data/core/counters/history_counter.h b/chromium/components/browsing_data/core/counters/history_counter.h
index 12e907d9d2b..985365ca5e4 100644
--- a/chromium/components/browsing_data/core/counters/history_counter.h
+++ b/chromium/components/browsing_data/core/counters/history_counter.h
@@ -10,15 +10,14 @@
#include "base/task/cancelable_task_tracker.h"
#include "base/timer/timer.h"
#include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/browsing_data/core/counters/sync_tracker.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/web_history_service.h"
#include "components/sync/driver/sync_service.h"
-#include "components/sync/driver/sync_service_observer.h"
namespace browsing_data {
-class HistoryCounter : public browsing_data::BrowsingDataCounter,
- public syncer::SyncServiceObserver {
+class HistoryCounter : public browsing_data::BrowsingDataCounter {
public:
typedef base::Callback<history::WebHistoryService*()>
GetUpdatedWebHistoryServiceCallback;
@@ -58,14 +57,15 @@ class HistoryCounter : public browsing_data::BrowsingDataCounter,
void OnWebHistoryTimeout();
void MergeResults();
- // SyncServiceObserver implementation.
- void OnStateChanged(syncer::SyncService* sync) override;
+ history::WebHistoryService* GetWebHistoryService();
+
+ bool IsHistorySyncEnabled(const syncer::SyncService* sync_service);
history::HistoryService* history_service_;
GetUpdatedWebHistoryServiceCallback web_history_service_callback_;
- syncer::SyncService* sync_service_;
+ SyncTracker sync_tracker_;
bool has_synced_visits_;
@@ -80,8 +80,6 @@ class HistoryCounter : public browsing_data::BrowsingDataCounter,
BrowsingDataCounter::ResultInt local_result_;
- bool history_sync_enabled_;
-
base::WeakPtrFactory<HistoryCounter> weak_ptr_factory_;
};
diff --git a/chromium/components/browsing_data/core/counters/passwords_counter.cc b/chromium/components/browsing_data/core/counters/passwords_counter.cc
index b78cd06b8f2..9ee62cf335a 100644
--- a/chromium/components/browsing_data/core/counters/passwords_counter.cc
+++ b/chromium/components/browsing_data/core/counters/passwords_counter.cc
@@ -25,21 +25,17 @@ namespace browsing_data {
PasswordsCounter::PasswordsCounter(
scoped_refptr<password_manager::PasswordStore> store,
syncer::SyncService* sync_service)
- : store_(store), sync_service_(sync_service), password_sync_enabled_() {
+ : store_(store), sync_tracker_(this, sync_service) {
DCHECK(store_);
}
PasswordsCounter::~PasswordsCounter() {
store_->RemoveObserver(this);
- if (sync_service_)
- sync_service_->RemoveObserver(this);
}
void PasswordsCounter::OnInitialized() {
+ sync_tracker_.OnInitialized(base::Bind(&IsPasswordSyncEnabled));
store_->AddObserver(this);
- if (sync_service_)
- sync_service_->AddObserver(this);
- password_sync_enabled_ = IsPasswordSyncEnabled(sync_service_);
}
const char* PasswordsCounter::GetPrefName() const {
@@ -64,7 +60,7 @@ void PasswordsCounter::OnGetPasswordStoreResults(
return form->date_created >= start;
});
ReportResult(base::MakeUnique<SyncResult>(this, num_passwords,
- password_sync_enabled_));
+ sync_tracker_.IsSyncActive()));
}
void PasswordsCounter::OnLoginsChanged(
@@ -72,13 +68,4 @@ void PasswordsCounter::OnLoginsChanged(
Restart();
}
-void PasswordsCounter::OnStateChanged(syncer::SyncService* sync) {
- bool sync_enabled_new = IsPasswordSyncEnabled(sync_service_);
-
- if (password_sync_enabled_ != sync_enabled_new) {
- password_sync_enabled_ = sync_enabled_new;
- Restart();
- }
-}
-
} // namespace browsing_data
diff --git a/chromium/components/browsing_data/core/counters/passwords_counter.h b/chromium/components/browsing_data/core/counters/passwords_counter.h
index bb71ff30013..bfdb0865801 100644
--- a/chromium/components/browsing_data/core/counters/passwords_counter.h
+++ b/chromium/components/browsing_data/core/counters/passwords_counter.h
@@ -9,16 +9,15 @@
#include <vector>
#include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/browsing_data/core/counters/sync_tracker.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
-#include "components/sync/driver/sync_service_observer.h"
namespace browsing_data {
class PasswordsCounter : public browsing_data::BrowsingDataCounter,
public password_manager::PasswordStoreConsumer,
- public password_manager::PasswordStore::Observer,
- public syncer::SyncServiceObserver {
+ public password_manager::PasswordStore::Observer {
public:
explicit PasswordsCounter(
scoped_refptr<password_manager::PasswordStore> store,
@@ -40,15 +39,11 @@ class PasswordsCounter : public browsing_data::BrowsingDataCounter,
void OnLoginsChanged(
const password_manager::PasswordStoreChangeList& changes) override;
- // SyncServiceObserver implementation.
- void OnStateChanged(syncer::SyncService* sync) override;
-
void Count() override;
base::CancelableTaskTracker cancelable_task_tracker_;
scoped_refptr<password_manager::PasswordStore> store_;
- syncer::SyncService* sync_service_;
- bool password_sync_enabled_;
+ SyncTracker sync_tracker_;
};
} // namespace browsing_data
diff --git a/chromium/components/browsing_data/core/counters/sync_tracker.cc b/chromium/components/browsing_data/core/counters/sync_tracker.cc
new file mode 100644
index 00000000000..5c25a3c78f0
--- /dev/null
+++ b/chromium/components/browsing_data/core/counters/sync_tracker.cc
@@ -0,0 +1,44 @@
+// 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/browsing_data/core/counters/sync_tracker.h"
+
+#include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/sync/driver/sync_service.h"
+
+namespace browsing_data {
+
+SyncTracker::SyncTracker(BrowsingDataCounter* counter,
+ syncer::SyncService* sync_service)
+ : counter_(counter), sync_service_(sync_service), sync_enabled_() {
+ DCHECK(counter_);
+}
+
+SyncTracker::~SyncTracker() {
+ if (sync_service_)
+ sync_service_->RemoveObserver(this);
+}
+
+void SyncTracker::OnInitialized(SyncPredicate predicate) {
+ DCHECK(!predicate.is_null());
+ predicate_ = predicate;
+ if (sync_service_)
+ sync_service_->AddObserver(this);
+ sync_enabled_ = predicate_.Run(sync_service_);
+}
+
+bool SyncTracker::IsSyncActive() {
+ return sync_enabled_;
+}
+
+void SyncTracker::OnStateChanged(syncer::SyncService* sync) {
+ bool sync_enabled_new = predicate_.Run(sync_service_);
+
+ if (sync_enabled_ != sync_enabled_new) {
+ sync_enabled_ = sync_enabled_new;
+ counter_->Restart();
+ }
+}
+
+} // namespace browsing_data
diff --git a/chromium/components/browsing_data/core/counters/sync_tracker.h b/chromium/components/browsing_data/core/counters/sync_tracker.h
new file mode 100644
index 00000000000..df8ca4f2031
--- /dev/null
+++ b/chromium/components/browsing_data/core/counters/sync_tracker.h
@@ -0,0 +1,40 @@
+// 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_BROWSING_DATA_CORE_COUNTERS_SYNC_TRACKER_H_
+#define COMPONENTS_BROWSING_DATA_CORE_COUNTERS_SYNC_TRACKER_H_
+
+#include "base/callback.h"
+#include "components/sync/driver/sync_service_observer.h"
+
+namespace browsing_data {
+
+// A helper class that subscribes to sync changes and notifies
+// |counter| when the sync state changes.
+class BrowsingDataCounter;
+
+class SyncTracker : public syncer::SyncServiceObserver {
+ public:
+ using SyncPredicate = base::Callback<bool(const syncer::SyncService*)>;
+
+ SyncTracker(BrowsingDataCounter* counter, syncer::SyncService* sync_service);
+ ~SyncTracker() override;
+
+ void OnInitialized(SyncPredicate predicate);
+
+ bool IsSyncActive();
+
+ private:
+ // SyncServiceObserver implementation.
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ BrowsingDataCounter* counter_;
+ syncer::SyncService* sync_service_;
+ SyncPredicate predicate_;
+ bool sync_enabled_;
+};
+
+} // namespace browsing_data
+
+#endif // COMPONENTS_BROWSING_DATA_CORE_COUNTERS_SYNC_TRACKER_H_
diff --git a/chromium/components/browsing_data/core/history_notice_utils.cc b/chromium/components/browsing_data/core/history_notice_utils.cc
index 4c3612352e4..feec09b98d3 100644
--- a/chromium/components/browsing_data/core/history_notice_utils.cc
+++ b/chromium/components/browsing_data/core/history_notice_utils.cc
@@ -72,8 +72,33 @@ void ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
callback.Run(false);
return;
}
-
- history_service->QueryWebAndAppActivity(callback);
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation("history_notice_utils_notice",
+ "web_history_service", R"(
+ semantics {
+ description:
+ "Queries history.google.com to find out if user has the 'Include "
+ "Chrome browsing history and activity from websites and apps that "
+ "use Google services' option enabled in the Activity controls of "
+ "their Google account. This is done for users who sync their "
+ "browsing history without a custom passphrase in order to show "
+ "information about history.google.com on the history page and in "
+ "the Clear Browsing Data dialog."
+ trigger:
+ "This request is sent when user opens the history page or the "
+ "Clear Browsing Data dialog and history sync without a custom "
+ "passphrase is (re)enabled."
+ data:
+ "An OAuth2 token authenticating the user."
+ }
+ policy {
+ chrome_policy {
+ SyncDisabled {
+ SyncDisabled: true
+ }
+ }
+ })");
+ history_service->QueryWebAndAppActivity(callback, partial_traffic_annotation);
}
void ShouldPopupDialogAboutOtherFormsOfBrowsingHistory(
@@ -104,12 +129,36 @@ void ShouldPopupDialogAboutOtherFormsOfBrowsingHistory(
// QueryOtherFormsOfBrowsingHistory. MergeBooleanCallbacks deletes itself
// after processing both callbacks.
MergeBooleanCallbacks* merger = new MergeBooleanCallbacks(2, callback);
- history_service->QueryWebAndAppActivity(base::Bind(
- &MergeBooleanCallbacks::RunCallback, base::Unretained(merger)));
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation("history_notice_utils_popup",
+ "web_history_service", R"(
+ semantics {
+ description:
+ "Determines if the user has other forms of browsing history "
+ "(than Chrome browsing history) stored in their Google account. "
+ "This is used to inform the users about the existence of other "
+ "forms of browsing history when they delete their Chrome "
+ "browsing history from the Clear Browsing Data dialog."
+ trigger:
+ "This request is sent when user opens the Clear Browsing Data "
+ "dialog and history sync without a custom passphrase is "
+ "(re)enabled."
+ data: "An OAuth2 token authenticating the user."
+ }
+ policy {
+ chrome_policy {
+ SyncDisabled {
+ SyncDisabled: true
+ }
+ }
+ })");
+ history_service->QueryWebAndAppActivity(
+ base::Bind(&MergeBooleanCallbacks::RunCallback, base::Unretained(merger)),
+ partial_traffic_annotation);
history_service->QueryOtherFormsOfBrowsingHistory(
channel,
- base::Bind(
- &MergeBooleanCallbacks::RunCallback, base::Unretained(merger)));
+ base::Bind(&MergeBooleanCallbacks::RunCallback, base::Unretained(merger)),
+ partial_traffic_annotation);
}
} // namespace browsing_data
diff --git a/chromium/components/browsing_data_strings.grdp b/chromium/components/browsing_data_strings.grdp
index 993cd7f373e..ec6eeeb6775 100644
--- a/chromium/components/browsing_data_strings.grdp
+++ b/chromium/components/browsing_data_strings.grdp
@@ -22,12 +22,15 @@
<message name="IDS_DEL_CACHE_COUNTER_ALMOST_EMPTY" desc="A counter showing that the user's cache is almost empty, having less than 1 MB of data.">
Less than 1 MB
</message>
- <message name="IDS_DEL_CACHE_COUNTER_ALMOST_EMPTY_BASIC" desc="Text showing that the user has less than 1 MB of data. It will be included in the sentence 'Frees up $1.'">
- less than 1 MB
- </message>
- <message name="IDS_DEL_CACHE_COUNTER_BASIC" desc="A counter showing the size of the users's cache including either 'x MB' or 'less than x MB' as $1 and explaining that the cache is used to speed up the loading of websites.">
+ <message name="IDS_DEL_CACHE_COUNTER_BASIC" desc="A counter showing the size of the users's cache. The estimate (e.g. '328 MB' will be substituted). It also explains that the cache is used to speed up the loading of websites.">
Frees up <ph name="SIZE">$1<ex>328 MB</ex></ph>. Some sites may load more slowly on your next visit.
</message>
+ <message name="IDS_DEL_CACHE_COUNTER_UPPER_ESTIMATE_BASIC" desc="A counter showing an upper estimate of the size of the users's cache. The estimate (e.g. '328 MB' will be substituted). It also explains that the cache is used to speed up the loading of websites.">
+ Frees up less than <ph name="SIZE">$1<ex>328 MB</ex></ph>. Some sites may load more slowly on your next visit.
+ </message>
+ <message name="IDS_DEL_CACHE_COUNTER_ALMOST_EMPTY_BASIC" desc="A counter showing that the user's cache is almost empty and explaining that the cache is used to speed up the loading of websites.">
+ Frees up less than 1 MB. Some sites may load more slowly on your next visit.
+ </message>
<message name="IDS_DEL_PASSWORDS_COUNTER" desc="A counter showing how many passwords the user has.">
{COUNT, plural,
=0 {None}
@@ -116,10 +119,10 @@
=1 {and 1 more}
other {and # more}}
</message>
- <message name="IDS_DEL_MEDIA_LICENSES_COUNTER_GENERAL_COMMENT" desc="A static message shown in the Clear Browsing Data dialog explaining to the user that clearing media licenses will result in the user being unable to play some premium content">
- You may lose access to premium content from some sites.
+ <message name="IDS_DEL_MEDIA_LICENSES_COUNTER_GENERAL_COMMENT" desc="A static message shown in the Clear Browsing Data dialog explaining to the user that clearing media licenses will result in the user being unable to play some protected content">
+ You may lose access to protected content from some sites.
</message>
- <message name="IDS_DEL_MEDIA_LICENSES_COUNTER_SITE_COMMENT" desc="A message shown in the Clear Browsing Data dialog explaining to the user that clearing media licenses will result in the user being unable to play some premium content from at least one specific web site">
- You may lose access to premium content from <ph name="SITE">$1<ex>youtube.com</ex></ph> and some other sites.
+ <message name="IDS_DEL_MEDIA_LICENSES_COUNTER_SITE_COMMENT" desc="A message shown in the Clear Browsing Data dialog explaining to the user that clearing media licenses will result in the user being unable to play some protected content from at least one specific web site">
+ You may lose access to protected content from <ph name="SITE">$1<ex>youtube.com</ex></ph> and some other sites.
</message>
</grit-part>
diff --git a/chromium/components/captive_portal/captive_portal_detector.cc b/chromium/components/captive_portal/captive_portal_detector.cc
index 6ec4df23d66..a51b809164a 100644
--- a/chromium/components/captive_portal/captive_portal_detector.cc
+++ b/chromium/components/captive_portal/captive_portal_detector.cc
@@ -23,13 +23,14 @@ CaptivePortalDetector::CaptivePortalDetector(
}
CaptivePortalDetector::~CaptivePortalDetector() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void CaptivePortalDetector::DetectCaptivePortal(
const GURL& url,
const DetectionCallback& detection_callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!FetchingURL());
DCHECK(detection_callback_.is_null());
@@ -60,7 +61,7 @@ void CaptivePortalDetector::Cancel() {
}
void CaptivePortalDetector::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(FetchingURL());
DCHECK_EQ(url_fetcher_.get(), source);
DCHECK(!detection_callback_.is_null());
diff --git a/chromium/components/captive_portal/captive_portal_detector.h b/chromium/components/captive_portal/captive_portal_detector.h
index e25cd443f4e..b5a26331cf2 100644
--- a/chromium/components/captive_portal/captive_portal_detector.h
+++ b/chromium/components/captive_portal/captive_portal_detector.h
@@ -11,7 +11,7 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "components/captive_portal/captive_portal_export.h"
#include "components/captive_portal/captive_portal_types.h"
@@ -25,8 +25,7 @@ class GURL;
namespace captive_portal {
class CAPTIVE_PORTAL_EXPORT CaptivePortalDetector
- : public net::URLFetcherDelegate,
- public base::NonThreadSafe {
+ : public net::URLFetcherDelegate {
public:
struct Results {
Results()
@@ -104,6 +103,8 @@ class CAPTIVE_PORTAL_EXPORT CaptivePortalDetector
// Test time used by unit tests.
base::Time time_for_testing_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(CaptivePortalDetector);
};
diff --git a/chromium/components/cast_certificate/cast_cert_validator.cc b/chromium/components/cast_certificate/cast_cert_validator.cc
index afd7b644971..608f7a747e9 100644
--- a/chromium/components/cast_certificate/cast_cert_validator.cc
+++ b/chromium/components/cast_certificate/cast_cert_validator.cc
@@ -13,6 +13,7 @@
#include "base/memory/ptr_util.h"
#include "base/memory/singleton.h"
+#include "base/stl_util.h"
#include "components/cast_certificate/cast_crl.h"
#include "net/cert/internal/cert_issuer_source_static.h"
#include "net/cert/internal/certificate_policies.h"
@@ -158,15 +159,47 @@ bool GetCommonNameFromSubject(const net::der::Input& subject_tlv,
return false;
}
+// Cast device certificates use the policy 1.3.6.1.4.1.11129.2.5.2 to indicate
+// it is *restricted* to an audio-only device whereas the absence of a policy
+// means it is unrestricted.
+//
+// This is somewhat different than RFC 5280's notion of policies, so policies
+// are checked separately outside of path building.
+//
+// See the unit-tests VerifyCastDeviceCertTest.Policies* for some
+// concrete examples of how this works.
+void DetermineDeviceCertificatePolicy(
+ const net::CertPathBuilder::ResultPath* result_path,
+ CastDeviceCertPolicy* policy) {
+ // Iterate over all the certificates, including the root certificate. If any
+ // certificate contains the audio-only policy, the whole chain is considered
+ // constrained to audio-only device certificates.
+ //
+ // Policy mappings are not accounted for. The expectation is that top-level
+ // intermediates issued with audio-only will have no mappings. If subsequent
+ // certificates in the chain do, it won't matter as the chain is already
+ // restricted to being audio-only.
+ bool audio_only = false;
+ for (const auto& cert : result_path->path.certs) {
+ if (cert->has_policy_oids()) {
+ const std::vector<net::der::Input>& policies = cert->policy_oids();
+ if (base::ContainsValue(policies, AudioOnlyPolicyOid())) {
+ audio_only = true;
+ break;
+ }
+ }
+ }
+
+ *policy = audio_only ? CastDeviceCertPolicy::AUDIO_ONLY
+ : CastDeviceCertPolicy::NONE;
+}
+
// Checks properties on the target certificate.
//
// * The Key Usage must include Digital Signature
-// * May have the policy 1.3.6.1.4.1.11129.2.5.2 to indicate it
-// is an audio-only device.
WARN_UNUSED_RESULT bool CheckTargetCertificate(
const net::ParsedCertificate* cert,
- std::unique_ptr<CertVerificationContext>* context,
- CastDeviceCertPolicy* policy) {
+ std::unique_ptr<CertVerificationContext>* context) {
// Get the Key Usage extension.
if (!cert->has_key_usage())
return false;
@@ -175,17 +208,6 @@ WARN_UNUSED_RESULT bool CheckTargetCertificate(
if (!cert->key_usage().AssertsBit(net::KEY_USAGE_BIT_DIGITAL_SIGNATURE))
return false;
- // Check for an optional audio-only policy extension.
- *policy = CastDeviceCertPolicy::NONE;
- if (cert->has_policy_oids()) {
- const std::vector<net::der::Input>& policies = cert->policy_oids();
- // Look for an audio-only policy. Disregard any other policy found.
- if (std::find(policies.begin(), policies.end(), AudioOnlyPolicyOid()) !=
- policies.end()) {
- *policy = CastDeviceCertPolicy::AUDIO_ONLY;
- }
- }
-
// Get the Common Name for the certificate.
std::string common_name;
if (!GetCommonNameFromSubject(cert->tbs().subject_tlv, &common_name))
@@ -264,9 +286,11 @@ bool VerifyDeviceCertUsingCustomTrustStore(
if (!net::der::EncodeTimeAsGeneralizedTime(time, &verification_time))
return false;
net::CertPathBuilder::Result result;
- net::CertPathBuilder path_builder(target_cert.get(), trust_store,
- signature_policy.get(), verification_time,
- net::KeyPurpose::CLIENT_AUTH, &result);
+ net::CertPathBuilder path_builder(
+ target_cert.get(), trust_store, signature_policy.get(), verification_time,
+ net::KeyPurpose::CLIENT_AUTH, net::InitialExplicitPolicy::kFalse,
+ {net::AnyPolicy()}, net::InitialPolicyMappingInhibit::kFalse,
+ net::InitialAnyPolicyInhibit::kFalse, &result);
path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
path_builder.Run();
if (!result.HasValidPath()) {
@@ -274,9 +298,13 @@ bool VerifyDeviceCertUsingCustomTrustStore(
return false;
}
- // Check properties of the leaf certificate (key usage, policy), and construct
- // a CertVerificationContext that uses its public key.
- if (!CheckTargetCertificate(target_cert.get(), context, policy))
+ // Determine whether this device certificate is restricted to audio-only.
+ DetermineDeviceCertificatePolicy(result.GetBestValidPath(), policy);
+
+ // Check properties of the leaf certificate not already verified by path
+ // building (key usage), and construct a CertVerificationContext that uses
+ // its public key.
+ if (!CheckTargetCertificate(target_cert.get(), context))
return false;
// Check if a CRL is available.
diff --git a/chromium/components/cast_certificate/cast_cert_validator_unittest.cc b/chromium/components/cast_certificate/cast_cert_validator_unittest.cc
index 43f9aeacc59..7a8d1e55970 100644
--- a/chromium/components/cast_certificate/cast_cert_validator_unittest.cc
+++ b/chromium/components/cast_certificate/cast_cert_validator_unittest.cc
@@ -416,6 +416,138 @@ TEST(VerifyCastDeviceCertTest, ViolatesPathlenTrustAnchorConstraint) {
TRUST_STORE_FROM_TEST_FILE, "");
}
+// Tests verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={anyPolicy}
+// Leaf: policies={anyPolicy}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAnypolicyLeafAnypolicy) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::NONE,
+ "certificates/policies_ica_anypolicy_leaf_anypolicy.pem",
+ AprilFirst2016(), TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={anyPolicy}
+// Leaf: policies={audioOnly}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAnypolicyLeafAudioonly) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::AUDIO_ONLY,
+ "certificates/policies_ica_anypolicy_leaf_audioonly.pem",
+ AprilFirst2016(), TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={anyPolicy}
+// Leaf: policies={foo}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAnypolicyLeafFoo) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::NONE,
+ "certificates/policies_ica_anypolicy_leaf_foo.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={anyPolicy}
+// Leaf: policies={}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAnypolicyLeafNone) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::NONE,
+ "certificates/policies_ica_anypolicy_leaf_none.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={audioOnly}
+// Leaf: policies={anyPolicy}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAudioonlyLeafAnypolicy) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::AUDIO_ONLY,
+ "certificates/policies_ica_audioonly_leaf_anypolicy.pem",
+ AprilFirst2016(), TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={audioOnly}
+// Leaf: policies={audioOnly}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAudioonlyLeafAudioonly) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::AUDIO_ONLY,
+ "certificates/policies_ica_audioonly_leaf_audioonly.pem",
+ AprilFirst2016(), TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={audioOnly}
+// Leaf: policies={foo}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAudioonlyLeafFoo) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::AUDIO_ONLY,
+ "certificates/policies_ica_audioonly_leaf_foo.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={audioOnly}
+// Leaf: policies={}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaAudioonlyLeafNone) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::AUDIO_ONLY,
+ "certificates/policies_ica_audioonly_leaf_none.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={}
+// Leaf: policies={anyPolicy}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaNoneLeafAnypolicy) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::NONE,
+ "certificates/policies_ica_none_leaf_anypolicy.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={}
+// Leaf: policies={audioOnly}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaNoneLeafAudioonly) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::AUDIO_ONLY,
+ "certificates/policies_ica_none_leaf_audioonly.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={}
+// Leaf: policies={foo}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaNoneLeafFoo) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::NONE,
+ "certificates/policies_ica_none_leaf_foo.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
+// Test verifying a certificate chain with the policies:
+//
+// Root: policies={}
+// Intermediate: policies={}
+// Leaf: policies={}
+TEST(VerifyCastDeviceCertTest, PoliciesIcaNoneLeafNone) {
+ RunTest(RESULT_SUCCESS, "Leaf", CastDeviceCertPolicy::NONE,
+ "certificates/policies_ica_none_leaf_none.pem", AprilFirst2016(),
+ TRUST_STORE_FROM_TEST_FILE, "");
+}
+
// ------------------------------------------------------
// Valid signature using 1024-bit RSA key
// ------------------------------------------------------
diff --git a/chromium/components/cast_certificate/cast_crl.cc b/chromium/components/cast_certificate/cast_crl.cc
index e8e0595892d..4f57a9059e3 100644
--- a/chromium/components/cast_certificate/cast_crl.cc
+++ b/chromium/components/cast_certificate/cast_crl.cc
@@ -142,9 +142,11 @@ bool VerifyCRL(const Crl& crl,
return false;
}
net::CertPathBuilder::Result result;
- net::CertPathBuilder path_builder(parsed_cert.get(), trust_store,
- signature_policy.get(), verification_time,
- net::KeyPurpose::ANY_EKU, &result);
+ net::CertPathBuilder path_builder(
+ parsed_cert.get(), trust_store, signature_policy.get(), verification_time,
+ net::KeyPurpose::ANY_EKU, net::InitialExplicitPolicy::kFalse,
+ {net::AnyPolicy()}, net::InitialPolicyMappingInhibit::kFalse,
+ net::InitialAnyPolicyInhibit::kFalse, &result);
path_builder.Run();
if (!result.HasValidPath()) {
VLOG(2) << "CRL - Issuer certificate verification failed.";
diff --git a/chromium/components/cast_channel/BUILD.gn b/chromium/components/cast_channel/BUILD.gn
new file mode 100644
index 00000000000..a76687523b5
--- /dev/null
+++ b/chromium/components/cast_channel/BUILD.gn
@@ -0,0 +1,78 @@
+# 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.
+
+static_library("cast_channel") {
+ sources = [
+ "cast_auth_util.cc",
+ "cast_auth_util.h",
+ "cast_channel_enum.cc",
+ "cast_channel_enum.h",
+ "cast_framer.cc",
+ "cast_framer.h",
+ "cast_message_util.cc",
+ "cast_message_util.h",
+ "cast_socket.cc",
+ "cast_socket.h",
+ "cast_socket_service.cc",
+ "cast_socket_service.h",
+ "cast_transport.cc",
+ "cast_transport.h",
+ "keep_alive_delegate.cc",
+ "keep_alive_delegate.h",
+ "logger.cc",
+ "logger.h",
+ ]
+ deps = [
+ "//base",
+ "//components/cast_certificate",
+ "//components/cast_channel/proto:cast_channel_proto",
+ "//components/keyed_service/content",
+ "//components/keyed_service/core",
+ "//content/public/browser",
+ "//crypto",
+ "//net",
+ ]
+}
+
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "cast_test_util.cc",
+ "cast_test_util.h",
+ ]
+ deps = [
+ ":cast_channel",
+ "//base",
+ "//components/cast_channel/proto:cast_channel_proto",
+ "//net",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "cast_auth_util_unittest.cc",
+ "cast_framer_unittest.cc",
+ "cast_socket_service_unittest.cc",
+ "cast_socket_unittest.cc",
+ "cast_transport_unittest.cc",
+ "keep_alive_delegate_unittest.cc",
+ "logger_unittest.cc",
+ ]
+ deps = [
+ ":cast_channel",
+ ":test_support",
+ "//base/test:test_support",
+ "//components/cast_certificate",
+ "//components/cast_certificate:test_support",
+ "//components/cast_certificate/proto:unittest_proto",
+ "//components/cast_channel/proto:cast_channel_proto",
+ "//content/test:test_support",
+ "//net:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/cast_channel/DEPS b/chromium/components/cast_channel/DEPS
new file mode 100644
index 00000000000..97e6b97a2f8
--- /dev/null
+++ b/chromium/components/cast_channel/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+components/cast_certificate",
+ "+components/keyed_service",
+ "+content/public/browser",
+ "+content/public/test",
+ "+crypto",
+ "+net",
+]
diff --git a/chromium/components/cast_channel/OWNERS b/chromium/components/cast_channel/OWNERS
new file mode 100644
index 00000000000..fc1829b66e3
--- /dev/null
+++ b/chromium/components/cast_channel/OWNERS
@@ -0,0 +1,5 @@
+mfoltz@chromium.org
+kmarshall@chromium.org
+wez@chromium.org
+
+# COMPONENT: Internals>Cast>API
diff --git a/chromium/components/cast_channel/cast_auth_util.cc b/chromium/components/cast_channel/cast_auth_util.cc
new file mode 100644
index 00000000000..809d839262c
--- /dev/null
+++ b/chromium/components/cast_channel/cast_auth_util.cc
@@ -0,0 +1,394 @@
+// 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/cast_channel/cast_auth_util.h"
+
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/cast_certificate/cast_cert_validator.h"
+#include "components/cast_certificate/cast_crl.h"
+#include "components/cast_channel/cast_channel_enum.h"
+#include "components/cast_channel/cast_message_util.h"
+#include "crypto/random.h"
+#include "net/cert/x509_certificate.h"
+#include "net/der/parse_values.h"
+
+namespace cast_channel {
+namespace {
+
+const char kParseErrorPrefix[] = "Failed to parse auth message: ";
+
+// The maximum number of days a cert can live for.
+const int kMaxSelfSignedCertLifetimeInDays = 4;
+
+// The size of the nonce challenge in bytes.
+const int kNonceSizeInBytes = 16;
+
+// The number of hours after which a nonce is regenerated.
+long kNonceExpirationTimeInHours = 24;
+
+// Enforce certificate revocation when enabled.
+// If disabled, any revocation failures are ignored.
+//
+// This flags only controls the enforcement. Revocation is checked regardless.
+//
+// This flag tracks the changes necessary to fully enforce revocation.
+const base::Feature kEnforceRevocationChecking{
+ "CastCertificateRevocation", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enforce nonce checking when enabled.
+// If disabled, the nonce value returned from the device is not checked against
+// the one sent to the device. As a result, the nonce can be empty and omitted
+// from the signature. This allows backwards compatibility with legacy Cast
+// receivers.
+
+const base::Feature kEnforceNonceChecking{"CastNonceEnforced",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+namespace cast_crypto = ::cast_certificate;
+
+// Extracts an embedded DeviceAuthMessage payload from an auth challenge reply
+// message.
+AuthResult ParseAuthMessage(const CastMessage& challenge_reply,
+ DeviceAuthMessage* auth_message) {
+ if (challenge_reply.payload_type() != CastMessage_PayloadType_BINARY) {
+ return AuthResult::CreateWithParseError(
+ "Wrong payload type in challenge reply",
+ AuthResult::ERROR_WRONG_PAYLOAD_TYPE);
+ }
+ if (!challenge_reply.has_payload_binary()) {
+ return AuthResult::CreateWithParseError(
+ "Payload type is binary but payload_binary field not set",
+ AuthResult::ERROR_NO_PAYLOAD);
+ }
+ if (!auth_message->ParseFromString(challenge_reply.payload_binary())) {
+ return AuthResult::CreateWithParseError(
+ "Cannot parse binary payload into DeviceAuthMessage",
+ AuthResult::ERROR_PAYLOAD_PARSING_FAILED);
+ }
+
+ VLOG(1) << "Auth message: " << AuthMessageToString(*auth_message);
+
+ if (auth_message->has_error()) {
+ return AuthResult::CreateWithParseError(
+ "Auth message error: " +
+ base::IntToString(auth_message->error().error_type()),
+ AuthResult::ERROR_MESSAGE_ERROR);
+ }
+ if (!auth_message->has_response()) {
+ return AuthResult::CreateWithParseError(
+ "Auth message has no response field", AuthResult::ERROR_NO_RESPONSE);
+ }
+ return AuthResult();
+}
+
+class CastNonce {
+ public:
+ static CastNonce* GetInstance() {
+ return base::Singleton<CastNonce,
+ base::LeakySingletonTraits<CastNonce>>::get();
+ }
+
+ static const std::string& Get() {
+ GetInstance()->EnsureNonceTimely();
+ return GetInstance()->nonce_;
+ }
+
+ private:
+ friend struct base::DefaultSingletonTraits<CastNonce>;
+
+ CastNonce() { GenerateNonce(); }
+ void GenerateNonce() {
+ // Create a cryptographically secure nonce.
+ crypto::RandBytes(base::WriteInto(&nonce_, kNonceSizeInBytes + 1),
+ kNonceSizeInBytes);
+ nonce_generation_time_ = base::Time::Now();
+ }
+
+ void EnsureNonceTimely() {
+ if (base::Time::Now() >
+ (nonce_generation_time_ +
+ base::TimeDelta::FromHours(kNonceExpirationTimeInHours))) {
+ GenerateNonce();
+ }
+ }
+
+ // The nonce challenge to send to the Cast receiver.
+ // The nonce is updated daily.
+ std::string nonce_;
+ base::Time nonce_generation_time_;
+};
+
+// Must match with histogram enum CastCertificateStatus.
+// This should never be reordered.
+enum CertVerificationStatus {
+ CERT_STATUS_OK,
+ CERT_STATUS_INVALID_CRL,
+ CERT_STATUS_VERIFICATION_FAILED,
+ CERT_STATUS_REVOKED,
+ CERT_STATUS_COUNT,
+};
+
+// Must match with histogram enum CastNonce.
+// This should never be reordered.
+enum NonceVerificationStatus {
+ NONCE_MATCH,
+ NONCE_MISMATCH,
+ NONCE_MISSING,
+ NONCE_COUNT,
+};
+
+// Record certificate verification histogram events.
+void RecordCertificateEvent(CertVerificationStatus event) {
+ UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Certificate", event,
+ CERT_STATUS_COUNT);
+}
+
+// Record nonce verification histogram events.
+void RecordNonceEvent(NonceVerificationStatus event) {
+ UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Nonce", event, NONCE_COUNT);
+}
+
+} // namespace
+
+AuthResult::AuthResult()
+ : error_type(ERROR_NONE), channel_policies(POLICY_NONE) {}
+
+AuthResult::AuthResult(const std::string& error_message, ErrorType error_type)
+ : error_message(error_message), error_type(error_type) {}
+
+AuthResult::~AuthResult() {}
+
+// static
+AuthResult AuthResult::CreateWithParseError(const std::string& error_message,
+ ErrorType error_type) {
+ return AuthResult(kParseErrorPrefix + error_message, error_type);
+}
+
+// static
+AuthContext AuthContext::Create() {
+ return AuthContext(CastNonce::Get());
+}
+
+AuthContext::AuthContext(const std::string& nonce) : nonce_(nonce) {}
+
+AuthContext::~AuthContext() {}
+
+AuthResult AuthContext::VerifySenderNonce(
+ const std::string& nonce_response) const {
+ if (nonce_ != nonce_response) {
+ if (nonce_response.empty()) {
+ RecordNonceEvent(NONCE_MISSING);
+ } else {
+ RecordNonceEvent(NONCE_MISMATCH);
+ }
+ if (base::FeatureList::IsEnabled(kEnforceNonceChecking)) {
+ return AuthResult("Sender nonce mismatched.",
+ AuthResult::ERROR_SENDER_NONCE_MISMATCH);
+ }
+ } else {
+ RecordNonceEvent(NONCE_MATCH);
+ }
+ return AuthResult();
+}
+
+// Verifies the peer certificate and populates |peer_cert_der| with the DER
+// encoded certificate.
+AuthResult VerifyTLSCertificate(const net::X509Certificate& peer_cert,
+ std::string* peer_cert_der,
+ const base::Time& verification_time) {
+ // Get the DER-encoded form of the certificate.
+ if (!net::X509Certificate::GetDEREncoded(peer_cert.os_cert_handle(),
+ peer_cert_der) ||
+ peer_cert_der->empty()) {
+ return AuthResult::CreateWithParseError(
+ "Could not create DER-encoded peer cert.",
+ AuthResult::ERROR_CERT_PARSING_FAILED);
+ }
+
+ // Ensure the peer cert is valid and doesn't have an excessive remaining
+ // lifetime. Although it is not verified as an X.509 certificate, the entire
+ // structure is signed by the AuthResponse, so the validity field from X.509
+ // is repurposed as this signature's expiration.
+ base::Time expiry = peer_cert.valid_expiry();
+ base::Time lifetime_limit =
+ verification_time +
+ base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays);
+ if (peer_cert.valid_start().is_null() ||
+ peer_cert.valid_start() > verification_time) {
+ return AuthResult::CreateWithParseError(
+ "Certificate's valid start date is in the future.",
+ AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE);
+ }
+ if (expiry.is_null() || peer_cert.valid_expiry() < verification_time) {
+ return AuthResult::CreateWithParseError("Certificate has expired.",
+ AuthResult::ERROR_TLS_CERT_EXPIRED);
+ }
+ if (expiry > lifetime_limit) {
+ return AuthResult::CreateWithParseError(
+ "Peer cert lifetime is too long.",
+ AuthResult::ERROR_TLS_CERT_VALIDITY_PERIOD_TOO_LONG);
+ }
+ return AuthResult();
+}
+
+AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply,
+ const net::X509Certificate& peer_cert,
+ const AuthContext& auth_context) {
+ DeviceAuthMessage auth_message;
+ AuthResult result = ParseAuthMessage(challenge_reply, &auth_message);
+ if (!result.success()) {
+ return result;
+ }
+
+ std::string peer_cert_der;
+ result = VerifyTLSCertificate(peer_cert, &peer_cert_der, base::Time::Now());
+ if (!result.success()) {
+ return result;
+ }
+
+ const AuthResponse& response = auth_message.response();
+ const std::string& nonce_response = response.sender_nonce();
+
+ result = auth_context.VerifySenderNonce(nonce_response);
+ if (!result.success()) {
+ return result;
+ }
+
+ return VerifyCredentials(response, nonce_response + peer_cert_der);
+}
+
+// This function does the following
+//
+// * Verifies that the certificate chain |response.client_auth_certificate| +
+// |response.intermediate_certificate| is valid and chains to a trusted
+// Cast root. The list of trusted Cast roots can be overrided by providing a
+// non-nullptr |cast_trust_store|. The certificate is verified at
+// |verification_time|.
+//
+// * Verifies that none of the certificates in the chain are revoked based on
+// the CRL provided in the response |response.crl|. The CRL is verified to be
+// valid and its issuer certificate chains to a trusted Cast CRL root. The
+// list of trusted Cast CRL roots can be overrided by providing a non-nullptr
+// |crl_trust_store|. If |crl_policy| is CRL_OPTIONAL then the result of
+// revocation checking is ignored. The CRL is verified at
+// |verification_time|.
+//
+// * Verifies that |response.signature| matches the signature
+// of |signature_input| by |response.client_auth_certificate|'s public
+// key.
+AuthResult VerifyCredentialsImpl(const AuthResponse& response,
+ const std::string& signature_input,
+ const cast_crypto::CRLPolicy& crl_policy,
+ net::TrustStore* cast_trust_store,
+ net::TrustStore* crl_trust_store,
+ const base::Time& verification_time) {
+ // Verify the certificate
+ std::unique_ptr<cast_crypto::CertVerificationContext> verification_context;
+
+ // Build a single vector containing the certificate chain.
+ std::vector<std::string> cert_chain;
+ cert_chain.push_back(response.client_auth_certificate());
+ cert_chain.insert(cert_chain.end(),
+ response.intermediate_certificate().begin(),
+ response.intermediate_certificate().end());
+
+ // Parse the CRL.
+ std::unique_ptr<cast_crypto::CastCRL> crl =
+ cast_crypto::ParseAndVerifyCRLUsingCustomTrustStore(
+ response.crl(), verification_time, crl_trust_store);
+ if (!crl) {
+ // CRL is invalid.
+ RecordCertificateEvent(CERT_STATUS_INVALID_CRL);
+ if (crl_policy == cast_crypto::CRLPolicy::CRL_REQUIRED) {
+ return AuthResult("Failed verifying Cast CRL.",
+ AuthResult::ERROR_CRL_INVALID);
+ }
+ }
+
+ cast_crypto::CastDeviceCertPolicy device_policy;
+ bool verification_success =
+ cast_crypto::VerifyDeviceCertUsingCustomTrustStore(
+ cert_chain, verification_time, &verification_context, &device_policy,
+ crl.get(), crl_policy, cast_trust_store);
+ if (!verification_success) {
+ // TODO(ryanchung): Once this feature is completely rolled-out, remove the
+ // reverification step and use error reporting to get verification errors
+ // for metrics.
+ bool verification_no_crl_success =
+ cast_crypto::VerifyDeviceCertUsingCustomTrustStore(
+ cert_chain, verification_time, &verification_context,
+ &device_policy, nullptr, cast_crypto::CRLPolicy::CRL_OPTIONAL,
+ cast_trust_store);
+ if (!verification_no_crl_success) {
+ // TODO(eroman): The error information was lost; this error is ambiguous.
+ RecordCertificateEvent(CERT_STATUS_VERIFICATION_FAILED);
+ return AuthResult("Failed verifying cast device certificate",
+ AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA);
+ }
+ if (crl) {
+ // If CRL was not present, it should've been recorded as such.
+ RecordCertificateEvent(CERT_STATUS_REVOKED);
+ }
+ if (crl_policy == cast_crypto::CRLPolicy::CRL_REQUIRED) {
+ // Device is revoked.
+ return AuthResult("Failed certificate revocation check.",
+ AuthResult::ERROR_CERT_REVOKED);
+ }
+ }
+ // The certificate is verified at this point.
+ RecordCertificateEvent(CERT_STATUS_OK);
+ if (!verification_context->VerifySignatureOverData(response.signature(),
+ signature_input)) {
+ return AuthResult("Failed verifying signature over data",
+ AuthResult::ERROR_SIGNED_BLOBS_MISMATCH);
+ }
+
+ AuthResult success;
+
+ // Set the policy into the result.
+ switch (device_policy) {
+ case cast_crypto::CastDeviceCertPolicy::AUDIO_ONLY:
+ success.channel_policies = AuthResult::POLICY_AUDIO_ONLY;
+ break;
+ case cast_crypto::CastDeviceCertPolicy::NONE:
+ success.channel_policies = AuthResult::POLICY_NONE;
+ break;
+ }
+
+ return success;
+}
+
+AuthResult VerifyCredentials(const AuthResponse& response,
+ const std::string& signature_input) {
+ base::Time now = base::Time::Now();
+ cast_crypto::CRLPolicy policy = cast_crypto::CRLPolicy::CRL_REQUIRED;
+ if (!base::FeatureList::IsEnabled(kEnforceRevocationChecking)) {
+ policy = cast_crypto::CRLPolicy::CRL_OPTIONAL;
+ }
+ return VerifyCredentialsImpl(response, signature_input, policy, nullptr,
+ nullptr, now);
+}
+
+AuthResult VerifyCredentialsForTest(const AuthResponse& response,
+ const std::string& signature_input,
+ const cast_crypto::CRLPolicy& crl_policy,
+ net::TrustStore* cast_trust_store,
+ net::TrustStore* crl_trust_store,
+ const base::Time& verification_time) {
+ return VerifyCredentialsImpl(response, signature_input, crl_policy,
+ cast_trust_store, crl_trust_store,
+ verification_time);
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_auth_util.h b/chromium/components/cast_channel/cast_auth_util.h
new file mode 100644
index 00000000000..114dee20f57
--- /dev/null
+++ b/chromium/components/cast_channel/cast_auth_util.h
@@ -0,0 +1,123 @@
+// 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_CAST_CHANNEL_CAST_AUTH_UTIL_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_AUTH_UTIL_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+
+namespace cast_certificate {
+enum class CRLPolicy;
+}
+
+namespace net {
+class X509Certificate;
+class TrustStore;
+} // namespace net
+
+namespace cast_channel {
+
+class AuthResponse;
+class CastMessage;
+
+struct AuthResult {
+ public:
+ enum ErrorType {
+ ERROR_NONE,
+ ERROR_PEER_CERT_EMPTY,
+ ERROR_WRONG_PAYLOAD_TYPE,
+ ERROR_NO_PAYLOAD,
+ ERROR_PAYLOAD_PARSING_FAILED,
+ ERROR_MESSAGE_ERROR,
+ ERROR_NO_RESPONSE,
+ ERROR_FINGERPRINT_NOT_FOUND,
+ ERROR_CERT_PARSING_FAILED,
+ ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA,
+ ERROR_CANNOT_EXTRACT_PUBLIC_KEY,
+ ERROR_SIGNED_BLOBS_MISMATCH,
+ ERROR_TLS_CERT_VALIDITY_PERIOD_TOO_LONG,
+ ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE,
+ ERROR_TLS_CERT_EXPIRED,
+ ERROR_CRL_INVALID,
+ ERROR_CERT_REVOKED,
+ ERROR_SENDER_NONCE_MISMATCH,
+ };
+
+ enum PolicyType { POLICY_NONE = 0, POLICY_AUDIO_ONLY = 1 << 0 };
+
+ // Constructs a AuthResult that corresponds to success.
+ AuthResult();
+
+ AuthResult(const std::string& error_message, ErrorType error_type);
+
+ ~AuthResult();
+
+ static AuthResult CreateWithParseError(const std::string& error_message,
+ ErrorType error_type);
+
+ bool success() const { return error_type == ERROR_NONE; }
+
+ std::string error_message;
+ ErrorType error_type;
+ unsigned int channel_policies;
+};
+
+class AuthContext {
+ public:
+ ~AuthContext();
+
+ // Get an auth challenge context.
+ // The same context must be used in the challenge and reply.
+ static AuthContext Create();
+
+ // Verifies the nonce received in the response is equivalent to the one sent.
+ // Returns success if |nonce_response| matches nonce_
+ AuthResult VerifySenderNonce(const std::string& nonce_response) const;
+
+ // The nonce challenge.
+ const std::string& nonce() const { return nonce_; }
+
+ private:
+ explicit AuthContext(const std::string& nonce);
+
+ const std::string nonce_;
+};
+
+// Authenticates the given |challenge_reply|:
+// 1. Signature contained in the reply is valid.
+// 2. Certficate used to sign is rooted to a trusted CA.
+AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply,
+ const net::X509Certificate& peer_cert,
+ const AuthContext& auth_context);
+
+// Performs a quick check of the TLS certificate for time validity requirements.
+AuthResult VerifyTLSCertificate(const net::X509Certificate& peer_cert,
+ std::string* peer_cert_der,
+ const base::Time& verification_time);
+
+// Auth-library specific implementation of cryptographic signature
+// verification routines. Verifies that |response| contains a
+// valid signature of |signature_input|.
+AuthResult VerifyCredentials(const AuthResponse& response,
+ const std::string& signature_input);
+
+// Exposed for testing only.
+//
+// Overloaded version of VerifyCredentials that allows modifying
+// the crl policy, trust stores, and verification times.
+AuthResult VerifyCredentialsForTest(
+ const AuthResponse& response,
+ const std::string& signature_input,
+ const cast_certificate::CRLPolicy& crl_policy,
+ net::TrustStore* cast_trust_store,
+ net::TrustStore* crl_trust_store,
+ const base::Time& verification_time);
+
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_CAST_AUTH_UTIL_H_
diff --git a/chromium/components/cast_channel/cast_auth_util_unittest.cc b/chromium/components/cast_channel/cast_auth_util_unittest.cc
new file mode 100644
index 00000000000..fba22eb1301
--- /dev/null
+++ b/chromium/components/cast_channel/cast_auth_util_unittest.cc
@@ -0,0 +1,335 @@
+// 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/cast_channel/cast_auth_util.h"
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "components/cast_certificate/cast_cert_validator.h"
+#include "components/cast_certificate/cast_cert_validator_test_helpers.h"
+#include "components/cast_certificate/cast_crl.h"
+#include "components/cast_certificate/proto/test_suite.pb.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "net/cert/internal/trust_store_in_memory.h"
+#include "net/cert/x509_certificate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cast_channel {
+namespace {
+
+class CastAuthUtilTest : public testing::Test {
+ public:
+ CastAuthUtilTest() {}
+ ~CastAuthUtilTest() override {}
+
+ void SetUp() override {}
+
+ protected:
+ static AuthResponse CreateAuthResponse(std::string* signed_data) {
+ auto chain = cast_certificate::testing::ReadCertificateChainFromFile(
+ "certificates/chromecast_gen1.pem");
+ CHECK(!chain.empty());
+
+ auto signature_data = cast_certificate::testing::ReadSignatureTestData(
+ "signeddata/2ZZBG9_FA8FCA3EF91A.pem");
+
+ AuthResponse response;
+
+ response.set_client_auth_certificate(chain[0]);
+ for (size_t i = 1; i < chain.size(); ++i)
+ response.add_intermediate_certificate(chain[i]);
+
+ response.set_signature(signature_data.signature_sha1);
+ *signed_data = signature_data.message;
+
+ return response;
+ }
+
+ // Mangles a string by inverting the first byte.
+ static void MangleString(std::string* str) { (*str)[0] = ~(*str)[0]; }
+};
+
+// Note on expiration: VerifyCredentials() depends on the system clock. In
+// practice this shouldn't be a problem though since the certificate chain
+// being verified doesn't expire until 2032!
+TEST_F(CastAuthUtilTest, VerifySuccess) {
+ std::string signed_data;
+ AuthResponse auth_response = CreateAuthResponse(&signed_data);
+ base::Time now = base::Time::Now();
+ AuthResult result = VerifyCredentialsForTest(
+ auth_response, signed_data, cast_certificate::CRLPolicy::CRL_OPTIONAL,
+ nullptr, nullptr, now);
+ EXPECT_TRUE(result.success());
+ EXPECT_EQ(AuthResult::POLICY_NONE, result.channel_policies);
+}
+
+TEST_F(CastAuthUtilTest, VerifyBadCA) {
+ std::string signed_data;
+ AuthResponse auth_response = CreateAuthResponse(&signed_data);
+ MangleString(auth_response.mutable_intermediate_certificate(0));
+ AuthResult result = VerifyCredentials(auth_response, signed_data);
+ EXPECT_FALSE(result.success());
+ EXPECT_EQ(AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA, result.error_type);
+}
+
+TEST_F(CastAuthUtilTest, VerifyBadClientAuthCert) {
+ std::string signed_data;
+ AuthResponse auth_response = CreateAuthResponse(&signed_data);
+ MangleString(auth_response.mutable_client_auth_certificate());
+ AuthResult result = VerifyCredentials(auth_response, signed_data);
+ EXPECT_FALSE(result.success());
+ // TODO(eroman): Not quite right of an error.
+ EXPECT_EQ(AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA, result.error_type);
+}
+
+TEST_F(CastAuthUtilTest, VerifyBadSignature) {
+ std::string signed_data;
+ AuthResponse auth_response = CreateAuthResponse(&signed_data);
+ MangleString(auth_response.mutable_signature());
+ AuthResult result = VerifyCredentials(auth_response, signed_data);
+ EXPECT_FALSE(result.success());
+ EXPECT_EQ(AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, result.error_type);
+}
+
+TEST_F(CastAuthUtilTest, VerifyBadPeerCert) {
+ std::string signed_data;
+ AuthResponse auth_response = CreateAuthResponse(&signed_data);
+ MangleString(&signed_data);
+ AuthResult result = VerifyCredentials(auth_response, signed_data);
+ EXPECT_FALSE(result.success());
+ EXPECT_EQ(AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, result.error_type);
+}
+
+TEST_F(CastAuthUtilTest, VerifySenderNonceMatch) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT});
+ AuthContext context = AuthContext::Create();
+ AuthResult result = context.VerifySenderNonce(context.nonce());
+ EXPECT_TRUE(result.success());
+}
+
+TEST_F(CastAuthUtilTest, VerifySenderNonceMismatch) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT});
+ AuthContext context = AuthContext::Create();
+ std::string received_nonce = "test2";
+ EXPECT_NE(received_nonce, context.nonce());
+ AuthResult result = context.VerifySenderNonce(received_nonce);
+ EXPECT_FALSE(result.success());
+ EXPECT_EQ(AuthResult::ERROR_SENDER_NONCE_MISMATCH, result.error_type);
+}
+
+TEST_F(CastAuthUtilTest, VerifySenderNonceMissing) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT});
+ AuthContext context = AuthContext::Create();
+ std::string received_nonce = "";
+ EXPECT_FALSE(context.nonce().empty());
+ AuthResult result = context.VerifySenderNonce(received_nonce);
+ EXPECT_FALSE(result.success());
+ EXPECT_EQ(AuthResult::ERROR_SENDER_NONCE_MISMATCH, result.error_type);
+}
+
+TEST_F(CastAuthUtilTest, VerifyTLSCertificateSuccess) {
+ auto tls_cert_der = cast_certificate::testing::ReadCertificateChainFromFile(
+ "certificates/test_tls_cert.pem");
+
+ scoped_refptr<net::X509Certificate> tls_cert =
+ net::X509Certificate::CreateFromBytes(tls_cert_der[0].data(),
+ tls_cert_der[0].size());
+ std::string peer_cert_der;
+ AuthResult result =
+ VerifyTLSCertificate(*tls_cert, &peer_cert_der, tls_cert->valid_start());
+ EXPECT_TRUE(result.success());
+}
+
+TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooEarly) {
+ auto tls_cert_der = cast_certificate::testing::ReadCertificateChainFromFile(
+ "certificates/test_tls_cert.pem");
+
+ scoped_refptr<net::X509Certificate> tls_cert =
+ net::X509Certificate::CreateFromBytes(tls_cert_der[0].data(),
+ tls_cert_der[0].size());
+ std::string peer_cert_der;
+ AuthResult result = VerifyTLSCertificate(
+ *tls_cert, &peer_cert_der,
+ tls_cert->valid_start() - base::TimeDelta::FromSeconds(1));
+ EXPECT_FALSE(result.success());
+ EXPECT_EQ(AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE,
+ result.error_type);
+}
+
+TEST_F(CastAuthUtilTest, VerifyTLSCertificateTooLate) {
+ auto tls_cert_der = cast_certificate::testing::ReadCertificateChainFromFile(
+ "certificates/test_tls_cert.pem");
+
+ scoped_refptr<net::X509Certificate> tls_cert =
+ net::X509Certificate::CreateFromBytes(tls_cert_der[0].data(),
+ tls_cert_der[0].size());
+ std::string peer_cert_der;
+ AuthResult result = VerifyTLSCertificate(
+ *tls_cert, &peer_cert_der,
+ tls_cert->valid_expiry() + base::TimeDelta::FromSeconds(2));
+ EXPECT_FALSE(result.success());
+ EXPECT_EQ(AuthResult::ERROR_TLS_CERT_EXPIRED, result.error_type);
+}
+
+// Indicates the expected result of test step's verification.
+enum TestStepResult {
+ RESULT_SUCCESS,
+ RESULT_FAIL,
+};
+
+// Verifies that the certificate chain provided is not revoked according to
+// the provided Cast CRL at |verification_time|.
+// The provided CRL is verified at |verification_time|.
+// If |crl_required| is set, then a valid Cast CRL must be provided.
+// Otherwise, a missing CRL is be ignored.
+AuthResult TestVerifyRevocation(
+ const std::vector<std::string>& certificate_chain,
+ const std::string& crl_bundle,
+ const base::Time& verification_time,
+ bool crl_required,
+ net::TrustStore* cast_trust_store,
+ net::TrustStore* crl_trust_store) {
+ AuthResponse response;
+
+ if (certificate_chain.size() > 0) {
+ response.set_client_auth_certificate(certificate_chain[0]);
+ for (size_t i = 1; i < certificate_chain.size(); ++i)
+ response.add_intermediate_certificate(certificate_chain[i]);
+ }
+
+ response.set_crl(crl_bundle);
+
+ cast_certificate::CRLPolicy crl_policy =
+ cast_certificate::CRLPolicy::CRL_REQUIRED;
+ if (!crl_required && crl_bundle.empty())
+ crl_policy = cast_certificate::CRLPolicy::CRL_OPTIONAL;
+ AuthResult result =
+ VerifyCredentialsForTest(response, "", crl_policy, cast_trust_store,
+ crl_trust_store, verification_time);
+ // This test doesn't set the signature so it will just fail there.
+ EXPECT_FALSE(result.success());
+ return result;
+}
+
+// Runs a single test case.
+bool RunTest(const cast_certificate::DeviceCertTest& test_case) {
+ std::unique_ptr<net::TrustStore> crl_trust_store;
+ std::unique_ptr<net::TrustStore> cast_trust_store;
+ if (test_case.use_test_trust_anchors()) {
+ crl_trust_store = cast_certificate::testing::CreateTrustStoreFromFile(
+ "certificates/cast_crl_test_root_ca.pem");
+ cast_trust_store = cast_certificate::testing::CreateTrustStoreFromFile(
+ "certificates/cast_test_root_ca.pem");
+
+ EXPECT_TRUE(crl_trust_store.get());
+ EXPECT_TRUE(cast_trust_store.get());
+ }
+
+ std::vector<std::string> certificate_chain;
+ for (auto const& cert : test_case.der_cert_path()) {
+ certificate_chain.push_back(cert);
+ }
+
+ // CastAuthUtil verifies the CRL at the same time as the certificate.
+ base::Time verification_time;
+ uint64_t cert_verify_time = test_case.cert_verification_time_seconds();
+ if (cert_verify_time) {
+ verification_time = cast_certificate::testing::ConvertUnixTimestampSeconds(
+ cert_verify_time);
+ } else {
+ verification_time = cast_certificate::testing::ConvertUnixTimestampSeconds(
+ test_case.crl_verification_time_seconds());
+ }
+
+ std::string crl_bundle = test_case.crl_bundle();
+ AuthResult result;
+ switch (test_case.expected_result()) {
+ case cast_certificate::PATH_VERIFICATION_FAILED:
+ result = TestVerifyRevocation(
+ certificate_chain, crl_bundle, verification_time, false,
+ cast_trust_store.get(), crl_trust_store.get());
+ EXPECT_EQ(result.error_type,
+ AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA);
+ return result.error_type ==
+ AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA;
+ case cast_certificate::CRL_VERIFICATION_FAILED:
+ // Fall-through intended.
+ case cast_certificate::REVOCATION_CHECK_FAILED_WITHOUT_CRL:
+ result = TestVerifyRevocation(
+ certificate_chain, crl_bundle, verification_time, true,
+ cast_trust_store.get(), crl_trust_store.get());
+ EXPECT_EQ(result.error_type, AuthResult::ERROR_CRL_INVALID);
+ return result.error_type == AuthResult::ERROR_CRL_INVALID;
+ case cast_certificate::CRL_EXPIRED_AFTER_INITIAL_VERIFICATION:
+ // By-pass this test because CRL is always verified at the time the
+ // certificate is verified.
+ return true;
+ case cast_certificate::REVOCATION_CHECK_FAILED:
+ result = TestVerifyRevocation(
+ certificate_chain, crl_bundle, verification_time, true,
+ cast_trust_store.get(), crl_trust_store.get());
+ EXPECT_EQ(result.error_type, AuthResult::ERROR_CERT_REVOKED);
+ return result.error_type == AuthResult::ERROR_CERT_REVOKED;
+ case cast_certificate::SUCCESS:
+ result = TestVerifyRevocation(
+ certificate_chain, crl_bundle, verification_time, false,
+ cast_trust_store.get(), crl_trust_store.get());
+ EXPECT_EQ(result.error_type, AuthResult::ERROR_SIGNED_BLOBS_MISMATCH);
+ return result.error_type == AuthResult::ERROR_SIGNED_BLOBS_MISMATCH;
+ case UNSPECIFIED:
+ return false;
+ }
+ return false;
+}
+
+// Parses the provided test suite provided in wire-format proto.
+// Each test contains the inputs and the expected output.
+// To see the description of the test, execute the test.
+// These tests are generated by a test generator in google3.
+void RunTestSuite(const std::string& test_suite_file_name) {
+ std::string testsuite_raw =
+ cast_certificate::testing::ReadTestFileToString(test_suite_file_name);
+ cast_certificate::DeviceCertTestSuite test_suite;
+ EXPECT_TRUE(test_suite.ParseFromString(testsuite_raw));
+ uint16_t success = 0;
+ uint16_t failed = 0;
+ std::vector<std::string> failed_tests;
+
+ for (auto const& test_case : test_suite.tests()) {
+ LOG(INFO) << "[ RUN ] " << test_case.description();
+ bool result = RunTest(test_case);
+ EXPECT_TRUE(result);
+ if (!result) {
+ LOG(INFO) << "[ FAILED ] " << test_case.description();
+ ++failed;
+ failed_tests.push_back(test_case.description());
+ } else {
+ LOG(INFO) << "[ PASSED ] " << test_case.description();
+ ++success;
+ }
+ }
+ LOG(INFO) << "[ PASSED ] " << success << " test(s).";
+ if (failed) {
+ LOG(INFO) << "[ FAILED ] " << failed << " test(s), listed below:";
+ for (const auto& failed_test : failed_tests) {
+ LOG(INFO) << "[ FAILED ] " << failed_test;
+ }
+ }
+}
+
+TEST_F(CastAuthUtilTest, CRLTestSuite) {
+ RunTestSuite("testsuite/testsuite1.pb");
+}
+
+} // namespace
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_channel_enum.cc b/chromium/components/cast_channel/cast_channel_enum.cc
new file mode 100644
index 00000000000..ac9e1320134
--- /dev/null
+++ b/chromium/components/cast_channel/cast_channel_enum.cc
@@ -0,0 +1,46 @@
+// 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/cast_channel/cast_channel_enum.h"
+
+#include "base/logging.h"
+
+namespace cast_channel {
+
+#define CAST_CHANNEL_TYPE_TO_STRING(enum) \
+ case enum: \
+ return #enum
+
+std::string ReadyStateToString(ReadyState ready_state) {
+ switch (ready_state) {
+ CAST_CHANNEL_TYPE_TO_STRING(ReadyState::NONE);
+ CAST_CHANNEL_TYPE_TO_STRING(ReadyState::CONNECTING);
+ CAST_CHANNEL_TYPE_TO_STRING(ReadyState::OPEN);
+ CAST_CHANNEL_TYPE_TO_STRING(ReadyState::CLOSING);
+ CAST_CHANNEL_TYPE_TO_STRING(ReadyState::CLOSED);
+ }
+ NOTREACHED() << "Unknown ready_state " << ReadyStateToString(ready_state);
+ return "Unknown ready_state";
+}
+
+std::string ChannelErrorToString(ChannelError channel_error) {
+ switch (channel_error) {
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::NONE);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::CHANNEL_NOT_OPEN);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::AUTHENTICATION_ERROR);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::CONNECT_ERROR);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::CAST_SOCKET_ERROR);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::TRANSPORT_ERROR);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::INVALID_MESSAGE);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::INVALID_CHANNEL_ID);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::CONNECT_TIMEOUT);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::PING_TIMEOUT);
+ CAST_CHANNEL_TYPE_TO_STRING(ChannelError::UNKNOWN);
+ }
+ NOTREACHED() << "Unknown channel_error "
+ << ChannelErrorToString(channel_error);
+ return "Unknown channel_error";
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_channel_enum.h b/chromium/components/cast_channel/cast_channel_enum.h
new file mode 100644
index 00000000000..c1f7f2bb734
--- /dev/null
+++ b/chromium/components/cast_channel/cast_channel_enum.h
@@ -0,0 +1,144 @@
+// 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_CAST_CHANNEL_CAST_CHANNEL_ENUM_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_CHANNEL_ENUM_H_
+
+#include <string>
+
+namespace cast_channel {
+
+// Helper function to convert scoped enums to their underlying type, for use
+// with ostreams.
+template <typename Enumeration>
+auto AsInteger(Enumeration const value) ->
+ typename std::underlying_type<Enumeration>::type {
+ return static_cast<typename std::underlying_type<Enumeration>::type>(value);
+}
+
+// Maps to enum ReadyState in cast_channel.idl
+enum class ReadyState {
+ NONE,
+ CONNECTING,
+ OPEN,
+ CLOSING, // TODO(zhaobin): Remove this value because it is unused.
+ CLOSED,
+};
+
+// Maps to enum ChannelError in cast_channel.idl
+enum class ChannelError {
+ NONE,
+ CHANNEL_NOT_OPEN,
+ AUTHENTICATION_ERROR,
+ CONNECT_ERROR,
+ CAST_SOCKET_ERROR,
+ TRANSPORT_ERROR,
+ INVALID_MESSAGE,
+ INVALID_CHANNEL_ID,
+ CONNECT_TIMEOUT,
+ PING_TIMEOUT,
+ UNKNOWN,
+};
+
+// Used in ErrorInfo.eventType in cast_channel.idl
+enum class ChannelEvent {
+ UNKNOWN = 0,
+ CAST_SOCKET_CREATED,
+ READY_STATE_CHANGED,
+ CONNECTION_STATE_CHANGED,
+ READ_STATE_CHANGED,
+ WRITE_STATE_CHANGED,
+ ERROR_STATE_CHANGED,
+ CONNECT_FAILED,
+ TCP_SOCKET_CONNECT, // Logged with RV.
+ TCP_SOCKET_SET_KEEP_ALIVE,
+ SSL_CERT_WHITELISTED,
+ SSL_SOCKET_CONNECT, // Logged with RV.
+ SSL_INFO_OBTAINED,
+ DER_ENCODED_CERT_OBTAIN, // Logged with RV.
+ RECEIVED_CHALLENGE_REPLY,
+ AUTH_CHALLENGE_REPLY,
+ CONNECT_TIMED_OUT,
+ SEND_MESSAGE_FAILED,
+ MESSAGE_ENQUEUED, // Message
+ SOCKET_WRITE, // Logged with RV.
+ MESSAGE_WRITTEN, // Message
+ SOCKET_READ, // Logged with RV.
+ MESSAGE_READ, // Message
+ SOCKET_CLOSED,
+ SSL_CERT_EXCESSIVE_LIFETIME,
+ CHANNEL_POLICY_ENFORCED,
+ TCP_SOCKET_CONNECT_COMPLETE, // Logged with RV.
+ SSL_SOCKET_CONNECT_COMPLETE, // Logged with RV.
+ SSL_SOCKET_CONNECT_FAILED, // Logged with RV.
+ SEND_AUTH_CHALLENGE_FAILED, // Logged with RV.
+ AUTH_CHALLENGE_REPLY_INVALID,
+ PING_WRITE_ERROR, // Logged with RV.
+};
+
+// Used in ErrorInfo.challengeReplyErrorType in cast_channel.idl
+enum class ChallengeReplyError {
+ NONE = 1,
+ PEER_CERT_EMPTY,
+ WRONG_PAYLOAD_TYPE,
+ NO_PAYLOAD,
+ PAYLOAD_PARSING_FAILED,
+ MESSAGE_ERROR,
+ NO_RESPONSE,
+ FINGERPRINT_NOT_FOUND,
+ CERT_PARSING_FAILED,
+ CERT_NOT_SIGNED_BY_TRUSTED_CA,
+ CANNOT_EXTRACT_PUBLIC_KEY,
+ SIGNED_BLOBS_MISMATCH,
+ TLS_CERT_VALIDITY_PERIOD_TOO_LONG,
+ TLS_CERT_VALID_START_DATE_IN_FUTURE,
+ TLS_CERT_EXPIRED,
+ CRL_INVALID,
+ CERT_REVOKED,
+ SENDER_NONCE_MISMATCH,
+};
+
+// Used by CastSocket/CastTransport to track connection state.
+enum class ConnectionState {
+ UNKNOWN,
+ TCP_CONNECT,
+ TCP_CONNECT_COMPLETE,
+ SSL_CONNECT,
+ SSL_CONNECT_COMPLETE,
+ AUTH_CHALLENGE_SEND,
+ AUTH_CHALLENGE_SEND_COMPLETE,
+ AUTH_CHALLENGE_REPLY_COMPLETE,
+ START_CONNECT,
+ FINISHED, // Terminal states here and below.
+ CONNECT_ERROR,
+ TIMEOUT,
+};
+
+// Used by CastSocket/CastTransport to track read state.
+enum class ReadState {
+ UNKNOWN,
+ READ,
+ READ_COMPLETE,
+ DO_CALLBACK,
+ HANDLE_ERROR,
+ READ_ERROR, // Terminal state.
+};
+
+// Used by CastSocket/CastTransport to track write state.
+enum class WriteState {
+ UNKNOWN,
+ WRITE,
+ WRITE_COMPLETE,
+ DO_CALLBACK,
+ HANDLE_ERROR,
+ WRITE_ERROR, // Terminal states here and below.
+ IDLE,
+};
+
+std::string ReadyStateToString(ReadyState ready_state);
+std::string ChannelErrorToString(ChannelError channel_error);
+
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_CAST_CHANNEL_ENUM_H_
diff --git a/chromium/components/cast_channel/cast_framer.cc b/chromium/components/cast_channel/cast_framer.cc
new file mode 100644
index 00000000000..9b5d6fe5122
--- /dev/null
+++ b/chromium/components/cast_channel/cast_framer.cc
@@ -0,0 +1,174 @@
+// 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/cast_channel/cast_framer.h"
+
+#include <stdlib.h>
+
+#include <limits>
+
+#include "base/memory/free_deleter.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/sys_byteorder.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+
+namespace cast_channel {
+MessageFramer::MessageFramer(scoped_refptr<net::GrowableIOBuffer> input_buffer)
+ : input_buffer_(input_buffer), error_(false) {
+ Reset();
+}
+
+MessageFramer::~MessageFramer() {}
+
+MessageFramer::MessageHeader::MessageHeader() : message_size(0) {}
+
+void MessageFramer::MessageHeader::SetMessageSize(size_t size) {
+ DCHECK_LT(size, static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
+ DCHECK_GT(size, 0U);
+ message_size = size;
+}
+
+// TODO(mfoltz): Investigate replacing header serialization with base::Pickle,
+// if bit-for-bit compatible.
+void MessageFramer::MessageHeader::PrependToString(std::string* str) {
+ MessageHeader output = *this;
+ output.message_size = base::HostToNet32(message_size);
+ size_t header_size = MessageHeader::header_size();
+ std::unique_ptr<char, base::FreeDeleter> char_array(
+ static_cast<char*>(malloc(header_size)));
+ memcpy(char_array.get(), &output, header_size);
+ str->insert(0, char_array.get(), header_size);
+}
+
+// TODO(mfoltz): Investigate replacing header deserialization with base::Pickle,
+// if bit-for-bit compatible.
+void MessageFramer::MessageHeader::Deserialize(char* data,
+ MessageHeader* header) {
+ uint32_t message_size;
+ memcpy(&message_size, data, header_size());
+ header->message_size =
+ base::checked_cast<size_t>(base::NetToHost32(message_size));
+}
+
+// static
+size_t MessageFramer::MessageHeader::header_size() {
+ return sizeof(uint32_t);
+}
+
+// static
+size_t MessageFramer::MessageHeader::max_message_size() {
+ return 65535;
+}
+
+std::string MessageFramer::MessageHeader::ToString() {
+ return "{message_size: " +
+ base::UintToString(static_cast<uint32_t>(message_size)) + "}";
+}
+
+// static
+bool MessageFramer::Serialize(const CastMessage& message_proto,
+ std::string* message_data) {
+ DCHECK(message_data);
+ message_proto.SerializeToString(message_data);
+ size_t message_size = message_data->size();
+ if (message_size > MessageHeader::max_message_size()) {
+ message_data->clear();
+ return false;
+ }
+ MessageHeader header;
+ header.SetMessageSize(message_size);
+ header.PrependToString(message_data);
+ return true;
+}
+
+size_t MessageFramer::BytesRequested() {
+ size_t bytes_left;
+ if (error_) {
+ return 0;
+ }
+
+ switch (current_element_) {
+ case HEADER:
+ bytes_left = MessageHeader::header_size() - message_bytes_received_;
+ DCHECK_LE(bytes_left, MessageHeader::header_size());
+ VLOG(2) << "Bytes needed for header: " << bytes_left;
+ return bytes_left;
+ case BODY:
+ bytes_left =
+ (body_size_ + MessageHeader::header_size()) - message_bytes_received_;
+ DCHECK_LE(bytes_left, MessageHeader::max_message_size() -
+ MessageHeader::header_size());
+ VLOG(2) << "Bytes needed for body: " << bytes_left;
+ return bytes_left;
+ default:
+ NOTREACHED() << "Unhandled packet element type.";
+ return 0;
+ }
+}
+
+std::unique_ptr<CastMessage> MessageFramer::Ingest(size_t num_bytes,
+ size_t* message_length,
+ ChannelError* error) {
+ DCHECK(error);
+ DCHECK(message_length);
+ if (error_) {
+ *error = ChannelError::INVALID_MESSAGE;
+ return nullptr;
+ }
+
+ DCHECK_EQ(base::checked_cast<int32_t>(message_bytes_received_),
+ input_buffer_->offset());
+ CHECK_LE(num_bytes, BytesRequested());
+ message_bytes_received_ += num_bytes;
+ *error = ChannelError::NONE;
+ *message_length = 0;
+ switch (current_element_) {
+ case HEADER:
+ if (BytesRequested() == 0) {
+ MessageHeader header;
+ MessageHeader::Deserialize(input_buffer_->StartOfBuffer(), &header);
+ if (header.message_size > MessageHeader::max_message_size()) {
+ VLOG(1) << "Error parsing header (message size too large).";
+ *error = ChannelError::INVALID_MESSAGE;
+ error_ = true;
+ return nullptr;
+ }
+ current_element_ = BODY;
+ body_size_ = header.message_size;
+ }
+ break;
+ case BODY:
+ if (BytesRequested() == 0) {
+ std::unique_ptr<CastMessage> parsed_message(new CastMessage);
+ if (!parsed_message->ParseFromArray(
+ input_buffer_->StartOfBuffer() + MessageHeader::header_size(),
+ body_size_)) {
+ VLOG(1) << "Error parsing packet body.";
+ *error = ChannelError::INVALID_MESSAGE;
+ error_ = true;
+ return nullptr;
+ }
+ *message_length = body_size_;
+ Reset();
+ return parsed_message;
+ }
+ break;
+ default:
+ NOTREACHED() << "Unhandled packet element type.";
+ return nullptr;
+ }
+
+ input_buffer_->set_offset(message_bytes_received_);
+ return nullptr;
+}
+
+void MessageFramer::Reset() {
+ current_element_ = HEADER;
+ message_bytes_received_ = 0;
+ body_size_ = 0;
+ input_buffer_->set_offset(0);
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_framer.h b/chromium/components/cast_channel/cast_framer.h
new file mode 100644
index 00000000000..657f2adedde
--- /dev/null
+++ b/chromium/components/cast_channel/cast_framer.h
@@ -0,0 +1,101 @@
+// 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_CAST_CHANNEL_CAST_FRAMER_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_FRAMER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/cast_channel/cast_channel_enum.h"
+#include "net/base/io_buffer.h"
+
+namespace cast_channel {
+class CastMessage;
+
+// Class for constructing and parsing CastMessage packet data.
+class MessageFramer {
+ public:
+ using ChannelError = ::cast_channel::ChannelError;
+
+ // |input_buffer|: The input buffer used by all socket read operations that
+ // feed data into the framer.
+ explicit MessageFramer(scoped_refptr<net::GrowableIOBuffer> input_buffer);
+ ~MessageFramer();
+
+ // The number of bytes required from |input_buffer| to complete the
+ // CastMessage being read.
+ // Returns zero if |error_| is true (framer is in an invalid state.)
+ size_t BytesRequested();
+
+ // Serializes |message_proto| into |message_data|.
+ // Returns true if the message was serialized successfully, false otherwise.
+ static bool Serialize(const CastMessage& message_proto,
+ std::string* message_data);
+
+ // Reads bytes from |input_buffer_| and returns a new CastMessage if one
+ // is fully read.
+ //
+ // |num_bytes| The number of bytes received by a read operation.
+ // Value must be <= BytesRequested().
+ // |message_length| Size of the deserialized message object, in bytes. For
+ // logging purposes. Set to zero if no message was parsed.
+ // |error| The result of the ingest operation. Set to CHANNEL_ERROR_NONE
+ // if no error occurred.
+ // Returns A pointer to a parsed CastMessage if a message was received
+ // in its entirety, nullptr otherwise.
+ std::unique_ptr<CastMessage> Ingest(size_t num_bytes,
+ size_t* message_length,
+ ChannelError* error);
+
+ // Message header struct. If fields are added, be sure to update
+ // header_size(). Public to allow use of *_size() methods in unit tests.
+ struct MessageHeader {
+ MessageHeader();
+ // Sets the message size.
+ void SetMessageSize(size_t message_size);
+ // Prepends this header to |str|.
+ void PrependToString(std::string* str);
+ // Reads |header| from the bytes specified by |data|.
+ static void Deserialize(char* data, MessageHeader* header);
+ // Size (in bytes) of the message header.
+ static size_t header_size();
+ // Maximum size (in bytes) of a message payload on the wire (does not
+ // include header).
+ static size_t max_message_size();
+ std::string ToString();
+ // The size of the following protocol message in bytes, in host byte order.
+ size_t message_size;
+ };
+
+ private:
+ enum MessageElement { HEADER, BODY };
+
+ // Prepares the framer for ingesting a new message.
+ void Reset();
+
+ // The element of the message that will be read on the next call to Ingest().
+ MessageElement current_element_;
+
+ // Total size of the message, in bytes (head + body).
+ size_t message_bytes_received_;
+
+ // Size of the body alone, in bytes.
+ size_t body_size_;
+
+ // Input buffer which carries message data read from the socket.
+ // Caller is responsible for writing into this buffer.
+ scoped_refptr<net::GrowableIOBuffer> input_buffer_;
+
+ // Disables Ingest functionality is the parser receives invalid data.
+ bool error_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageFramer);
+};
+} // namespace cast_channel
+#endif // COMPONENTS_CAST_CHANNEL_CAST_FRAMER_H_
diff --git a/chromium/components/cast_channel/cast_framer_unittest.cc b/chromium/components/cast_channel/cast_framer_unittest.cc
new file mode 100644
index 00000000000..660e074b5fd
--- /dev/null
+++ b/chromium/components/cast_channel/cast_framer_unittest.cc
@@ -0,0 +1,138 @@
+// 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/cast_channel/cast_framer.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <string>
+
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cast_channel {
+
+using ::cast_channel::ChannelError;
+
+class CastFramerTest : public testing::Test {
+ public:
+ CastFramerTest() {}
+ ~CastFramerTest() override {}
+
+ void SetUp() override {
+ cast_message_.set_protocol_version(CastMessage::CASTV2_1_0);
+ cast_message_.set_source_id("source");
+ cast_message_.set_destination_id("destination");
+ cast_message_.set_namespace_("namespace");
+ cast_message_.set_payload_type(CastMessage::STRING);
+ cast_message_.set_payload_utf8("payload");
+ ASSERT_TRUE(MessageFramer::Serialize(cast_message_, &cast_message_str_));
+
+ buffer_ = new net::GrowableIOBuffer;
+ buffer_->SetCapacity(MessageFramer::MessageHeader::max_message_size());
+ framer_.reset(new MessageFramer(buffer_.get()));
+ }
+
+ void WriteToBuffer(const std::string& data) {
+ memcpy(buffer_->StartOfBuffer(), data.data(), data.size());
+ }
+
+ protected:
+ CastMessage cast_message_;
+ std::string cast_message_str_;
+ scoped_refptr<net::GrowableIOBuffer> buffer_;
+ std::unique_ptr<MessageFramer> framer_;
+};
+
+TEST_F(CastFramerTest, TestMessageFramerCompleteMessage) {
+ ChannelError error;
+ size_t message_length;
+
+ WriteToBuffer(cast_message_str_);
+
+ // Receive 1 byte of the header, framer demands 3 more bytes.
+ EXPECT_EQ(4u, framer_->BytesRequested());
+ EXPECT_EQ(nullptr, framer_->Ingest(1, &message_length, &error).get());
+ EXPECT_EQ(ChannelError::NONE, error);
+ EXPECT_EQ(3u, framer_->BytesRequested());
+
+ // Ingest remaining 3, expect that the framer has moved on to requesting the
+ // body contents.
+ EXPECT_EQ(nullptr, framer_->Ingest(3, &message_length, &error).get());
+ EXPECT_EQ(ChannelError::NONE, error);
+ EXPECT_EQ(
+ cast_message_str_.size() - MessageFramer::MessageHeader::header_size(),
+ framer_->BytesRequested());
+
+ // Remainder of packet sent over the wire.
+ std::unique_ptr<CastMessage> message;
+ message = framer_->Ingest(framer_->BytesRequested(), &message_length, &error);
+ EXPECT_NE(static_cast<CastMessage*>(nullptr), message.get());
+ EXPECT_EQ(ChannelError::NONE, error);
+ EXPECT_EQ(message->SerializeAsString(), cast_message_.SerializeAsString());
+ EXPECT_EQ(4u, framer_->BytesRequested());
+ EXPECT_EQ(message->SerializeAsString().size(), message_length);
+}
+
+TEST_F(CastFramerTest, TestSerializeErrorMessageTooLarge) {
+ std::string serialized;
+ CastMessage big_message;
+ big_message.CopyFrom(cast_message_);
+ std::string payload;
+ payload.append(MessageFramer::MessageHeader::max_message_size() + 1, 'x');
+ big_message.set_payload_utf8(payload);
+ EXPECT_FALSE(MessageFramer::Serialize(big_message, &serialized));
+}
+
+TEST_F(CastFramerTest, TestIngestIllegalLargeMessage) {
+ std::string mangled_cast_message = cast_message_str_;
+ mangled_cast_message[0] = 88;
+ mangled_cast_message[1] = 88;
+ mangled_cast_message[2] = 88;
+ mangled_cast_message[3] = 88;
+ WriteToBuffer(mangled_cast_message);
+
+ size_t bytes_ingested;
+ ChannelError error;
+ EXPECT_EQ(4u, framer_->BytesRequested());
+ EXPECT_EQ(nullptr, framer_->Ingest(4, &bytes_ingested, &error).get());
+ EXPECT_EQ(ChannelError::INVALID_MESSAGE, error);
+ EXPECT_EQ(0u, framer_->BytesRequested());
+
+ // Test that the parser enters a terminal error state.
+ WriteToBuffer(cast_message_str_);
+ EXPECT_EQ(0u, framer_->BytesRequested());
+ EXPECT_EQ(nullptr, framer_->Ingest(4, &bytes_ingested, &error).get());
+ EXPECT_EQ(ChannelError::INVALID_MESSAGE, error);
+ EXPECT_EQ(0u, framer_->BytesRequested());
+}
+
+TEST_F(CastFramerTest, TestUnparsableBodyProto) {
+ // Message header is OK, but the body is replaced with "x"en.
+ std::string mangled_cast_message = cast_message_str_;
+ for (size_t i = MessageFramer::MessageHeader::header_size();
+ i < mangled_cast_message.size(); ++i) {
+ std::fill(mangled_cast_message.begin() +
+ MessageFramer::MessageHeader::header_size(),
+ mangled_cast_message.end(), 'x');
+ }
+ WriteToBuffer(mangled_cast_message);
+
+ // Send header.
+ size_t message_length;
+ ChannelError error;
+ EXPECT_EQ(4u, framer_->BytesRequested());
+ EXPECT_EQ(nullptr, framer_->Ingest(4, &message_length, &error).get());
+ EXPECT_EQ(ChannelError::NONE, error);
+ EXPECT_EQ(cast_message_str_.size() - 4, framer_->BytesRequested());
+
+ // Send body, expect an error.
+ std::unique_ptr<CastMessage> message;
+ EXPECT_EQ(nullptr,
+ framer_->Ingest(framer_->BytesRequested(), &message_length, &error)
+ .get());
+ EXPECT_EQ(ChannelError::INVALID_MESSAGE, error);
+}
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_message_util.cc b/chromium/components/cast_channel/cast_message_util.cc
new file mode 100644
index 00000000000..50c4be1c858
--- /dev/null
+++ b/chromium/components/cast_channel/cast_message_util.cc
@@ -0,0 +1,87 @@
+// 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/cast_channel/cast_message_util.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "components/cast_channel/cast_auth_util.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+
+namespace {
+static const char kAuthNamespace[] = "urn:x-cast:com.google.cast.tp.deviceauth";
+// Sender and receiver IDs to use for platform messages.
+static const char kPlatformSenderId[] = "sender-0";
+static const char kPlatformReceiverId[] = "receiver-0";
+} // namespace
+
+namespace cast_channel {
+
+bool IsCastMessageValid(const CastMessage& message_proto) {
+ if (message_proto.namespace_().empty() || message_proto.source_id().empty() ||
+ message_proto.destination_id().empty()) {
+ return false;
+ }
+ return (message_proto.payload_type() == CastMessage_PayloadType_STRING &&
+ message_proto.has_payload_utf8()) ||
+ (message_proto.payload_type() == CastMessage_PayloadType_BINARY &&
+ message_proto.has_payload_binary());
+}
+
+std::string CastMessageToString(const CastMessage& message_proto) {
+ std::string out("{");
+ out += "namespace = " + message_proto.namespace_();
+ out += ", sourceId = " + message_proto.source_id();
+ out += ", destId = " + message_proto.destination_id();
+ out += ", type = " + base::IntToString(message_proto.payload_type());
+ out += ", str = \"" + message_proto.payload_utf8() + "\"}";
+ return out;
+}
+
+std::string AuthMessageToString(const DeviceAuthMessage& message) {
+ std::string out("{");
+ if (message.has_challenge()) {
+ out += "challenge: {}, ";
+ }
+ if (message.has_response()) {
+ out += "response: {signature: (";
+ out += base::SizeTToString(message.response().signature().length());
+ out += " bytes), certificate: (";
+ out += base::SizeTToString(
+ message.response().client_auth_certificate().length());
+ out += " bytes)}";
+ }
+ if (message.has_error()) {
+ out += ", error: {";
+ out += base::IntToString(message.error().error_type());
+ out += "}";
+ }
+ out += "}";
+ return out;
+}
+
+void CreateAuthChallengeMessage(CastMessage* message_proto,
+ const AuthContext& auth_context) {
+ CHECK(message_proto);
+ DeviceAuthMessage auth_message;
+ auth_message.mutable_challenge()->set_sender_nonce(auth_context.nonce());
+ std::string auth_message_string;
+ auth_message.SerializeToString(&auth_message_string);
+
+ message_proto->set_protocol_version(CastMessage_ProtocolVersion_CASTV2_1_0);
+ message_proto->set_source_id(kPlatformSenderId);
+ message_proto->set_destination_id(kPlatformReceiverId);
+ message_proto->set_namespace_(kAuthNamespace);
+ message_proto->set_payload_type(CastMessage_PayloadType_BINARY);
+ message_proto->set_payload_binary(auth_message_string);
+}
+
+bool IsAuthMessage(const CastMessage& message) {
+ return message.namespace_() == kAuthNamespace;
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_message_util.h b/chromium/components/cast_channel/cast_message_util.h
new file mode 100644
index 00000000000..db30a2273f0
--- /dev/null
+++ b/chromium/components/cast_channel/cast_message_util.h
@@ -0,0 +1,35 @@
+// 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_CAST_CHANNEL_CAST_MESSAGE_UTIL_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_MESSAGE_UTIL_H_
+
+#include <string>
+
+namespace cast_channel {
+
+class AuthContext;
+class CastMessage;
+class DeviceAuthMessage;
+
+// Checks if the contents of |message_proto| are valid.
+bool IsCastMessageValid(const CastMessage& message_proto);
+
+// Returns a human readable string for |message_proto|.
+std::string CastMessageToString(const CastMessage& message_proto);
+
+// Returns a human readable string for |message|.
+std::string AuthMessageToString(const DeviceAuthMessage& message);
+
+// Fills |message_proto| appropriately for an auth challenge request message.
+// Uses the nonce challenge in |auth_context|.
+void CreateAuthChallengeMessage(CastMessage* message_proto,
+ const AuthContext& auth_context);
+
+// Returns whether the given message is an auth handshake message.
+bool IsAuthMessage(const CastMessage& message);
+
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_CAST_MESSAGE_UTIL_H_
diff --git a/chromium/components/cast_channel/cast_socket.cc b/chromium/components/cast_channel/cast_socket.cc
new file mode 100644
index 00000000000..20effac80fc
--- /dev/null
+++ b/chromium/components/cast_channel/cast_socket.cc
@@ -0,0 +1,659 @@
+// 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/cast_channel/cast_socket.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/format_macros.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_byteorder.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/cast_channel/cast_auth_util.h"
+#include "components/cast_channel/cast_framer.h"
+#include "components/cast_channel/cast_message_util.h"
+#include "components/cast_channel/cast_transport.h"
+#include "components/cast_channel/keep_alive_delegate.h"
+#include "components/cast_channel/logger.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "net/base/address_list.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/ct_policy_enforcer.h"
+#include "net/cert/multi_log_ct_verifier.h"
+#include "net/cert/x509_certificate.h"
+#include "net/http/transport_security_state.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_source_type.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/socket/stream_socket.h"
+#include "net/socket/tcp_client_socket.h"
+#include "net/ssl/ssl_config_service.h"
+#include "net/ssl/ssl_info.h"
+
+// Helper for logging data with remote host IP and authentication state.
+// Assumes |ip_endpoint_| of type net::IPEndPoint and |channel_auth_| of enum
+// type ChannelAuthType are available in the current scope.
+#define CONNECTION_INFO() \
+ "[" << ip_endpoint_.ToString() << ", auth=SSL_VERIFIED" \
+ << "] "
+#define VLOG_WITH_CONNECTION(level) VLOG(level) << CONNECTION_INFO()
+#define LOG_WITH_CONNECTION(level) LOG(level) << CONNECTION_INFO()
+
+namespace cast_channel {
+namespace {
+
+bool IsTerminalState(ConnectionState state) {
+ return state == ConnectionState::FINISHED ||
+ state == ConnectionState::CONNECT_ERROR ||
+ state == ConnectionState::TIMEOUT;
+}
+
+// Cert verifier which blindly accepts all certificates, regardless of validity.
+class FakeCertVerifier : public net::CertVerifier {
+ public:
+ FakeCertVerifier() {}
+ ~FakeCertVerifier() override {}
+
+ int Verify(const RequestParams& params,
+ net::CRLSet*,
+ net::CertVerifyResult* verify_result,
+ const net::CompletionCallback&,
+ std::unique_ptr<Request>*,
+ const net::NetLogWithSource&) override {
+ verify_result->Reset();
+ verify_result->verified_cert = params.certificate();
+ return net::OK;
+ }
+};
+
+} // namespace
+
+CastSocketImpl::CastSocketImpl(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ base::TimeDelta timeout,
+ base::TimeDelta liveness_timeout,
+ base::TimeDelta ping_interval,
+ const scoped_refptr<Logger>& logger,
+ uint64_t device_capabilities)
+ : CastSocketImpl(ip_endpoint,
+ net_log,
+ timeout,
+ liveness_timeout,
+ ping_interval,
+ logger,
+ device_capabilities,
+ AuthContext::Create()) {}
+
+CastSocketImpl::CastSocketImpl(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ base::TimeDelta timeout,
+ base::TimeDelta liveness_timeout,
+ base::TimeDelta ping_interval,
+ const scoped_refptr<Logger>& logger,
+ uint64_t device_capabilities,
+ const AuthContext& auth_context)
+ : channel_id_(0),
+ ip_endpoint_(ip_endpoint),
+ net_log_(net_log),
+ liveness_timeout_(liveness_timeout),
+ ping_interval_(ping_interval),
+ logger_(logger),
+ auth_context_(auth_context),
+ connect_timeout_(timeout),
+ connect_timeout_timer_(new base::OneShotTimer),
+ is_canceled_(false),
+ device_capabilities_(device_capabilities),
+ audio_only_(false),
+ connect_state_(ConnectionState::START_CONNECT),
+ error_state_(ChannelError::NONE),
+ ready_state_(ReadyState::NONE),
+ auth_delegate_(nullptr) {
+ DCHECK(net_log_);
+ net_log_source_.type = net::NetLogSourceType::SOCKET;
+ net_log_source_.id = net_log_->NextID();
+}
+
+CastSocketImpl::~CastSocketImpl() {
+ // Ensure that resources are freed but do not run pending callbacks that
+ // would result in re-entrancy.
+ CloseInternal();
+
+ for (auto& connect_callback : connect_callbacks_)
+ std::move(connect_callback).Run(channel_id_, ChannelError::UNKNOWN);
+ connect_callbacks_.clear();
+}
+
+ReadyState CastSocketImpl::ready_state() const {
+ return ready_state_;
+}
+
+ChannelError CastSocketImpl::error_state() const {
+ return error_state_;
+}
+
+const net::IPEndPoint& CastSocketImpl::ip_endpoint() const {
+ return ip_endpoint_;
+}
+
+int CastSocketImpl::id() const {
+ return channel_id_;
+}
+
+void CastSocketImpl::set_id(int id) {
+ channel_id_ = id;
+}
+
+bool CastSocketImpl::keep_alive() const {
+ return liveness_timeout_ > base::TimeDelta();
+}
+
+bool CastSocketImpl::audio_only() const {
+ return audio_only_;
+}
+
+std::unique_ptr<net::TCPClientSocket> CastSocketImpl::CreateTcpSocket() {
+ net::AddressList addresses(ip_endpoint_);
+ return std::unique_ptr<net::TCPClientSocket>(
+ new net::TCPClientSocket(addresses, nullptr, net_log_, net_log_source_));
+ // Options cannot be set on the TCPClientSocket yet, because the
+ // underlying platform socket will not be created until Bind()
+ // or Connect() is called.
+}
+
+std::unique_ptr<net::SSLClientSocket> CastSocketImpl::CreateSslSocket(
+ std::unique_ptr<net::StreamSocket> socket) {
+ net::SSLConfig ssl_config;
+ cert_verifier_ = base::WrapUnique(new FakeCertVerifier);
+ transport_security_state_.reset(new net::TransportSecurityState);
+ cert_transparency_verifier_.reset(new net::MultiLogCTVerifier());
+ ct_policy_enforcer_.reset(new net::CTPolicyEnforcer());
+
+ // Note that |context| fields remain owned by CastSocketImpl.
+ net::SSLClientSocketContext context;
+ context.cert_verifier = cert_verifier_.get();
+ context.transport_security_state = transport_security_state_.get();
+ context.cert_transparency_verifier = cert_transparency_verifier_.get();
+ context.ct_policy_enforcer = ct_policy_enforcer_.get();
+
+ std::unique_ptr<net::ClientSocketHandle> connection(
+ new net::ClientSocketHandle);
+ connection->SetSocket(std::move(socket));
+ net::HostPortPair host_and_port =
+ net::HostPortPair::FromIPEndPoint(ip_endpoint_);
+
+ return net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
+ std::move(connection), host_and_port, ssl_config, context);
+}
+
+scoped_refptr<net::X509Certificate> CastSocketImpl::ExtractPeerCert() {
+ net::SSLInfo ssl_info;
+ if (!socket_->GetSSLInfo(&ssl_info) || !ssl_info.cert.get())
+ return nullptr;
+
+ return ssl_info.cert;
+}
+
+bool CastSocketImpl::VerifyChannelPolicy(const AuthResult& result) {
+ audio_only_ = (result.channel_policies & AuthResult::POLICY_AUDIO_ONLY) != 0;
+ if (audio_only_ &&
+ (device_capabilities_ & CastDeviceCapability::VIDEO_OUT) != 0) {
+ LOG_WITH_CONNECTION(ERROR)
+ << "Audio only channel policy enforced for video out capable device";
+ return false;
+ }
+ return true;
+}
+
+bool CastSocketImpl::VerifyChallengeReply() {
+ DCHECK(peer_cert_);
+ AuthResult result =
+ AuthenticateChallengeReply(*challenge_reply_, *peer_cert_, auth_context_);
+ logger_->LogSocketChallengeReplyEvent(channel_id_, result);
+ if (result.success()) {
+ VLOG(1) << result.error_message;
+ if (!VerifyChannelPolicy(result)) {
+ return false;
+ }
+ }
+ return result.success();
+}
+
+void CastSocketImpl::SetTransportForTesting(
+ std::unique_ptr<CastTransport> transport) {
+ transport_ = std::move(transport);
+}
+
+void CastSocketImpl::Connect(OnOpenCallback callback) {
+ switch (ready_state_) {
+ case ReadyState::NONE:
+ connect_callbacks_.push_back(std::move(callback));
+ Connect();
+ break;
+ case ReadyState::CONNECTING:
+ connect_callbacks_.push_back(std::move(callback));
+ break;
+ case ReadyState::OPEN:
+ std::move(callback).Run(channel_id_, ChannelError::NONE);
+ break;
+ case ReadyState::CLOSED:
+ std::move(callback).Run(channel_id_, ChannelError::CONNECT_ERROR);
+ break;
+ default:
+ NOTREACHED() << "Unknown ReadyState: "
+ << ReadyStateToString(ready_state_);
+ }
+}
+
+void CastSocketImpl::Connect() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ VLOG_WITH_CONNECTION(1) << "Connect readyState = "
+ << ReadyStateToString(ready_state_);
+ DCHECK_EQ(ReadyState::NONE, ready_state_);
+ DCHECK_EQ(ConnectionState::START_CONNECT, connect_state_);
+
+ delegate_ = base::MakeUnique<CastSocketMessageDelegate>(this);
+
+ SetReadyState(ReadyState::CONNECTING);
+ SetConnectState(ConnectionState::TCP_CONNECT);
+
+ // Set up connection timeout.
+ if (connect_timeout_.InMicroseconds() > 0) {
+ DCHECK(connect_timeout_callback_.IsCancelled());
+ connect_timeout_callback_.Reset(
+ base::Bind(&CastSocketImpl::OnConnectTimeout, base::Unretained(this)));
+ GetTimer()->Start(FROM_HERE, connect_timeout_,
+ connect_timeout_callback_.callback());
+ }
+
+ DoConnectLoop(net::OK);
+}
+
+CastTransport* CastSocketImpl::transport() const {
+ return transport_.get();
+}
+
+void CastSocketImpl::AddObserver(Observer* observer) {
+ DCHECK(observer);
+ if (!observers_.HasObserver(observer))
+ observers_.AddObserver(observer);
+}
+
+void CastSocketImpl::RemoveObserver(Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
+}
+
+void CastSocketImpl::OnConnectTimeout() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // Stop all pending connection setup tasks and report back to the client.
+ is_canceled_ = true;
+ VLOG_WITH_CONNECTION(1) << "Timeout while establishing a connection.";
+ SetErrorState(ChannelError::CONNECT_TIMEOUT);
+ DoConnectCallback();
+}
+
+void CastSocketImpl::ResetConnectLoopCallback() {
+ DCHECK(connect_loop_callback_.IsCancelled());
+ connect_loop_callback_.Reset(
+ base::Bind(&CastSocketImpl::DoConnectLoop, base::Unretained(this)));
+}
+
+void CastSocketImpl::PostTaskToStartConnectLoop(int result) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ ResetConnectLoopCallback();
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(connect_loop_callback_.callback(), result));
+}
+
+// This method performs the state machine transitions for connection flow.
+// There are two entry points to this method:
+// 1. Connect method: this starts the flow
+// 2. Callback from network operations that finish asynchronously.
+void CastSocketImpl::DoConnectLoop(int result) {
+ connect_loop_callback_.Cancel();
+ if (is_canceled_) {
+ LOG_WITH_CONNECTION(ERROR) << "CANCELLED - Aborting DoConnectLoop.";
+ return;
+ }
+
+ // Network operations can either finish synchronously or asynchronously.
+ // This method executes the state machine transitions in a loop so that
+ // correct state transitions happen even when network operations finish
+ // synchronously.
+ int rv = result;
+ do {
+ ConnectionState state = connect_state_;
+ connect_state_ = ConnectionState::UNKNOWN;
+ switch (state) {
+ case ConnectionState::TCP_CONNECT:
+ rv = DoTcpConnect();
+ break;
+ case ConnectionState::TCP_CONNECT_COMPLETE:
+ rv = DoTcpConnectComplete(rv);
+ break;
+ case ConnectionState::SSL_CONNECT:
+ DCHECK_EQ(net::OK, rv);
+ rv = DoSslConnect();
+ break;
+ case ConnectionState::SSL_CONNECT_COMPLETE:
+ rv = DoSslConnectComplete(rv);
+ break;
+ case ConnectionState::AUTH_CHALLENGE_SEND:
+ rv = DoAuthChallengeSend();
+ break;
+ case ConnectionState::AUTH_CHALLENGE_SEND_COMPLETE:
+ rv = DoAuthChallengeSendComplete(rv);
+ break;
+ case ConnectionState::AUTH_CHALLENGE_REPLY_COMPLETE:
+ rv = DoAuthChallengeReplyComplete(rv);
+ DCHECK(IsTerminalState(connect_state_));
+ break;
+ default:
+ NOTREACHED() << "Unknown state in connect flow: " << AsInteger(state);
+ SetConnectState(ConnectionState::FINISHED);
+ SetErrorState(ChannelError::UNKNOWN);
+ DoConnectCallback();
+ return;
+ }
+ } while (rv != net::ERR_IO_PENDING && !IsTerminalState(connect_state_));
+ // Exit the state machine if an asynchronous network operation is pending
+ // or if the state machine is in the terminal "finished" state.
+
+ if (IsTerminalState(connect_state_)) {
+ DCHECK_NE(rv, net::ERR_IO_PENDING);
+ GetTimer()->Stop();
+ DoConnectCallback();
+ } else {
+ DCHECK_EQ(rv, net::ERR_IO_PENDING);
+ }
+}
+
+int CastSocketImpl::DoTcpConnect() {
+ DCHECK(connect_loop_callback_.IsCancelled());
+ VLOG_WITH_CONNECTION(1) << "DoTcpConnect";
+ SetConnectState(ConnectionState::TCP_CONNECT_COMPLETE);
+ tcp_socket_ = CreateTcpSocket();
+
+ int rv = tcp_socket_->Connect(
+ base::Bind(&CastSocketImpl::DoConnectLoop, base::Unretained(this)));
+ logger_->LogSocketEventWithRv(channel_id_, ChannelEvent::TCP_SOCKET_CONNECT,
+ rv);
+ return rv;
+}
+
+int CastSocketImpl::DoTcpConnectComplete(int connect_result) {
+ VLOG_WITH_CONNECTION(1) << "DoTcpConnectComplete: " << connect_result;
+ logger_->LogSocketEventWithRv(
+ channel_id_, ChannelEvent::TCP_SOCKET_CONNECT_COMPLETE, connect_result);
+ if (connect_result == net::OK) {
+ SetConnectState(ConnectionState::SSL_CONNECT);
+ } else if (connect_result == net::ERR_CONNECTION_TIMED_OUT) {
+ SetConnectState(ConnectionState::FINISHED);
+ SetErrorState(ChannelError::CONNECT_TIMEOUT);
+ } else {
+ SetConnectState(ConnectionState::FINISHED);
+ SetErrorState(ChannelError::CONNECT_ERROR);
+ }
+ return connect_result;
+}
+
+int CastSocketImpl::DoSslConnect() {
+ DCHECK(connect_loop_callback_.IsCancelled());
+ VLOG_WITH_CONNECTION(1) << "DoSslConnect";
+ SetConnectState(ConnectionState::SSL_CONNECT_COMPLETE);
+ socket_ = CreateSslSocket(std::move(tcp_socket_));
+
+ int rv = socket_->Connect(
+ base::Bind(&CastSocketImpl::DoConnectLoop, base::Unretained(this)));
+ logger_->LogSocketEventWithRv(channel_id_, ChannelEvent::SSL_SOCKET_CONNECT,
+ rv);
+ return rv;
+}
+
+int CastSocketImpl::DoSslConnectComplete(int result) {
+ logger_->LogSocketEventWithRv(
+ channel_id_, ChannelEvent::SSL_SOCKET_CONNECT_COMPLETE, result);
+ VLOG_WITH_CONNECTION(1) << "DoSslConnectComplete: " << result;
+ if (result == net::OK) {
+ peer_cert_ = ExtractPeerCert();
+
+ if (!peer_cert_) {
+ LOG_WITH_CONNECTION(WARNING) << "Could not extract peer cert.";
+ SetConnectState(ConnectionState::FINISHED);
+ SetErrorState(ChannelError::AUTHENTICATION_ERROR);
+ return net::ERR_CERT_INVALID;
+ }
+
+ // SSL connection succeeded.
+ if (!transport_.get()) {
+ // Create a channel transport if one wasn't already set (e.g. by test
+ // code).
+ transport_.reset(new CastTransportImpl(this->socket_.get(), channel_id_,
+ ip_endpoint_, logger_));
+ }
+ auth_delegate_ = new AuthTransportDelegate(this);
+ transport_->SetReadDelegate(base::WrapUnique(auth_delegate_));
+ SetConnectState(ConnectionState::AUTH_CHALLENGE_SEND);
+ } else if (result == net::ERR_CONNECTION_TIMED_OUT) {
+ SetConnectState(ConnectionState::FINISHED);
+ SetErrorState(ChannelError::CONNECT_TIMEOUT);
+ } else {
+ SetConnectState(ConnectionState::FINISHED);
+ SetErrorState(ChannelError::AUTHENTICATION_ERROR);
+ }
+ return result;
+}
+
+int CastSocketImpl::DoAuthChallengeSend() {
+ VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSend";
+ SetConnectState(ConnectionState::AUTH_CHALLENGE_SEND_COMPLETE);
+
+ CastMessage challenge_message;
+ CreateAuthChallengeMessage(&challenge_message, auth_context_);
+ VLOG_WITH_CONNECTION(1) << "Sending challenge: "
+ << CastMessageToString(challenge_message);
+
+ ResetConnectLoopCallback();
+ transport_->SendMessage(challenge_message, connect_loop_callback_.callback());
+
+ // Always return IO_PENDING since the result is always asynchronous.
+ return net::ERR_IO_PENDING;
+}
+
+int CastSocketImpl::DoAuthChallengeSendComplete(int result) {
+ VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSendComplete: " << result;
+ if (result < 0) {
+ SetConnectState(ConnectionState::CONNECT_ERROR);
+ SetErrorState(ChannelError::CAST_SOCKET_ERROR);
+ logger_->LogSocketEventWithRv(
+ channel_id_, ChannelEvent::SEND_AUTH_CHALLENGE_FAILED, result);
+ return result;
+ }
+ transport_->Start();
+ SetConnectState(ConnectionState::AUTH_CHALLENGE_REPLY_COMPLETE);
+ return net::ERR_IO_PENDING;
+}
+
+CastSocketImpl::AuthTransportDelegate::AuthTransportDelegate(
+ CastSocketImpl* socket)
+ : socket_(socket), error_state_(ChannelError::NONE) {
+ DCHECK(socket);
+}
+
+ChannelError CastSocketImpl::AuthTransportDelegate::error_state() const {
+ return error_state_;
+}
+
+LastError CastSocketImpl::AuthTransportDelegate::last_error() const {
+ return last_error_;
+}
+
+void CastSocketImpl::AuthTransportDelegate::OnError(ChannelError error_state) {
+ error_state_ = error_state;
+ socket_->PostTaskToStartConnectLoop(net::ERR_CONNECTION_FAILED);
+}
+
+void CastSocketImpl::AuthTransportDelegate::OnMessage(
+ const CastMessage& message) {
+ if (!IsAuthMessage(message)) {
+ error_state_ = ChannelError::TRANSPORT_ERROR;
+ socket_->PostTaskToStartConnectLoop(net::ERR_INVALID_RESPONSE);
+ } else {
+ socket_->challenge_reply_.reset(new CastMessage(message));
+ socket_->PostTaskToStartConnectLoop(net::OK);
+ }
+}
+
+void CastSocketImpl::AuthTransportDelegate::Start() {}
+
+int CastSocketImpl::DoAuthChallengeReplyComplete(int result) {
+ VLOG_WITH_CONNECTION(1) << "DoAuthChallengeReplyComplete: " << result;
+
+ if (auth_delegate_->error_state() != ChannelError::NONE) {
+ SetErrorState(auth_delegate_->error_state());
+ SetConnectState(ConnectionState::CONNECT_ERROR);
+ return net::ERR_CONNECTION_FAILED;
+ }
+ auth_delegate_ = nullptr;
+
+ if (result < 0) {
+ SetConnectState(ConnectionState::CONNECT_ERROR);
+ return result;
+ }
+
+ if (!VerifyChallengeReply()) {
+ SetErrorState(ChannelError::AUTHENTICATION_ERROR);
+ SetConnectState(ConnectionState::CONNECT_ERROR);
+ return net::ERR_CONNECTION_FAILED;
+ }
+ VLOG_WITH_CONNECTION(1) << "Auth challenge verification succeeded";
+
+ SetConnectState(ConnectionState::FINISHED);
+ return net::OK;
+}
+
+void CastSocketImpl::DoConnectCallback() {
+ VLOG(1) << "DoConnectCallback (error_state = "
+ << ChannelErrorToString(error_state_) << ")";
+ if (connect_callbacks_.empty()) {
+ DLOG(FATAL) << "Connection callback invoked multiple times.";
+ return;
+ }
+
+ if (error_state_ == ChannelError::NONE) {
+ SetReadyState(ReadyState::OPEN);
+ if (keep_alive()) {
+ auto* keep_alive_delegate =
+ new KeepAliveDelegate(this, logger_, std::move(delegate_),
+ ping_interval_, liveness_timeout_);
+ delegate_.reset(keep_alive_delegate);
+ }
+ transport_->SetReadDelegate(std::move(delegate_));
+ } else {
+ CloseInternal();
+ }
+
+ for (auto& connect_callback : connect_callbacks_)
+ std::move(connect_callback).Run(channel_id_, error_state_);
+ connect_callbacks_.clear();
+}
+
+void CastSocketImpl::Close(const net::CompletionCallback& callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ CloseInternal();
+ // Run this callback last. It may delete the socket.
+ callback.Run(net::OK);
+}
+
+void CastSocketImpl::CloseInternal() {
+ // TODO(mfoltz): Enforce this when CastChannelAPITest is rewritten to create
+ // and free sockets on the same thread. crbug.com/398242
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (ready_state_ == ReadyState::CLOSED) {
+ return;
+ }
+
+ VLOG_WITH_CONNECTION(1) << "Close ReadyState = "
+ << ReadyStateToString(ready_state_);
+ transport_.reset();
+ tcp_socket_.reset();
+ socket_.reset();
+ transport_security_state_.reset();
+ if (GetTimer()) {
+ GetTimer()->Stop();
+ }
+
+ // Cancel callbacks that we queued ourselves to re-enter the connect or read
+ // loops.
+ connect_loop_callback_.Cancel();
+ connect_timeout_callback_.Cancel();
+ SetReadyState(ReadyState::CLOSED);
+}
+
+base::Timer* CastSocketImpl::GetTimer() {
+ return connect_timeout_timer_.get();
+}
+
+void CastSocketImpl::SetConnectState(ConnectionState connect_state) {
+ if (connect_state_ != connect_state) {
+ connect_state_ = connect_state;
+ }
+}
+
+void CastSocketImpl::SetReadyState(ReadyState ready_state) {
+ if (ready_state_ != ready_state)
+ ready_state_ = ready_state;
+}
+
+void CastSocketImpl::SetErrorState(ChannelError error_state) {
+ VLOG_WITH_CONNECTION(1) << "SetErrorState "
+ << ChannelErrorToString(error_state);
+ DCHECK_EQ(ChannelError::NONE, error_state_);
+ error_state_ = error_state;
+ delegate_->OnError(error_state_);
+}
+
+CastSocketImpl::CastSocketMessageDelegate::CastSocketMessageDelegate(
+ CastSocketImpl* socket)
+ : socket_(socket) {
+ DCHECK(socket_);
+}
+
+CastSocketImpl::CastSocketMessageDelegate::~CastSocketMessageDelegate() {}
+
+// CastTransport::Delegate implementation.
+void CastSocketImpl::CastSocketMessageDelegate::OnError(
+ ChannelError error_state) {
+ for (auto& observer : socket_->observers_)
+ observer.OnError(*socket_, error_state);
+}
+
+void CastSocketImpl::CastSocketMessageDelegate::OnMessage(
+ const CastMessage& message) {
+ for (auto& observer : socket_->observers_)
+ observer.OnMessage(*socket_, message);
+}
+
+void CastSocketImpl::CastSocketMessageDelegate::Start() {}
+
+} // namespace cast_channel
+#undef VLOG_WITH_CONNECTION
diff --git a/chromium/components/cast_channel/cast_socket.h b/chromium/components/cast_channel/cast_socket.h
new file mode 100644
index 00000000000..de1792b3ca7
--- /dev/null
+++ b/chromium/components/cast_channel/cast_socket.h
@@ -0,0 +1,420 @@
+// 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_CAST_CHANNEL_CAST_SOCKET_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_SOCKET_H_
+
+#include <stdint.h>
+
+#include <queue>
+#include <string>
+
+#include "base/cancelable_callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "base/timer/timer.h"
+#include "components/cast_channel/cast_auth_util.h"
+#include "components/cast_channel/cast_channel_enum.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/cast_transport.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/log/net_log_source.h"
+
+namespace net {
+class CertVerifier;
+class CTPolicyEnforcer;
+class CTVerifier;
+class NetLog;
+class SSLClientSocket;
+class StreamSocket;
+class TCPClientSocket;
+class TransportSecurityState;
+class X509Certificate;
+}
+
+namespace cast_channel {
+class CastMessage;
+class Logger;
+struct LastError;
+
+// Cast device capabilities.
+enum CastDeviceCapability {
+ NONE = 0,
+ VIDEO_OUT = 1 << 0,
+ VIDEO_IN = 1 << 1,
+ AUDIO_OUT = 1 << 2,
+ AUDIO_IN = 1 << 3,
+ DEV_MODE = 1 << 4
+};
+
+// Public interface of the CastSocket class.
+class CastSocket {
+ public:
+ using OnOpenCallback =
+ base::OnceCallback<void(int channel_id, ChannelError error_state)>;
+
+ class Observer {
+ public:
+ virtual ~Observer() {}
+
+ // Invoked when an error occurs on |socket|.
+ virtual void OnError(const CastSocket& socket,
+ ChannelError error_state) = 0;
+
+ // Invoked when |socket| receives a message.
+ virtual void OnMessage(const CastSocket& socket,
+ const CastMessage& message) = 0;
+ };
+
+ virtual ~CastSocket() {}
+
+ // Used by BrowserContextKeyedAPIFactory.
+ static const char* service_name() { return "CastSocketImplManager"; }
+
+ // Connects the channel to the peer. If successful, the channel will be in
+ // READY_STATE_OPEN. DO NOT delete the CastSocket object in |callback|.
+ // Instead use Close().
+ // |callback| will be invoked with any ChannelError that occurred, or
+ // CHANNEL_ERROR_NONE if successful.
+ // If the CastSocket is destroyed while the connection is pending, |callback|
+ // will be invoked with CHANNEL_ERROR_UNKNOWN. In this case, invoking
+ // |callback| must not result in any re-entrancy behavior.
+ virtual void Connect(OnOpenCallback callback) = 0;
+
+ // Closes the channel if not already closed. On completion, the channel will
+ // be in READY_STATE_CLOSED.
+ //
+ // It is fine to delete this object in |callback|.
+ virtual void Close(const net::CompletionCallback& callback) = 0;
+
+ // The IP endpoint for the destination of the channel.
+ virtual const net::IPEndPoint& ip_endpoint() const = 0;
+
+ // Channel id generated by the CastChannelService.
+ virtual int id() const = 0;
+
+ // Sets the channel id generated by CastChannelService.
+ virtual void set_id(int id) = 0;
+
+ // The ready state of the channel.
+ virtual ReadyState ready_state() const = 0;
+
+ // Returns the last error that occurred on this channel, or
+ // CHANNEL_ERROR_NONE if no error has occurred.
+ virtual ChannelError error_state() const = 0;
+
+ // True when keep-alive signaling is handled for this socket.
+ virtual bool keep_alive() const = 0;
+
+ // Whether the channel is audio only as identified by the device
+ // certificate during channel authentication.
+ virtual bool audio_only() const = 0;
+
+ // Marks a socket as invalid due to an error, and sends an OnError
+ // event to |delegate_|.
+ // The OnError event receipient is responsible for closing the socket in the
+ // event of an error.
+ // Setting the error state does not close the socket if it is open.
+ virtual void SetErrorState(ChannelError error_state) = 0;
+
+ // Returns a pointer to the socket's message transport layer. Can be used to
+ // send and receive CastMessages over the socket.
+ virtual CastTransport* transport() const = 0;
+
+ // Registers |observer| with the socket to receive messages and error events.
+ virtual void AddObserver(Observer* observer) = 0;
+
+ // Unregisters |observer|.
+ virtual void RemoveObserver(Observer* observer) = 0;
+};
+
+// This class implements a channel between Chrome and a Cast device using a TCP
+// socket with SSL. The channel may authenticate that the receiver is a genuine
+// Cast device. All CastSocketImpl objects must be used only on the IO thread.
+//
+// NOTE: Not called "CastChannel" to reduce confusion with the generated API
+// code.
+class CastSocketImpl : public CastSocket {
+ public:
+ // Creates a new CastSocket that connects to |ip_endpoint|.
+ // Parameters:
+ // |ip_endpoint|: IP address of the remote host.
+ // |net_log|: Log of socket events.
+ // |connect_timeout|: Connection timeout interval.
+ // |liveness_timeout|: Amount of idle time to wait before disconnecting.
+ // |ping_interval|: Amount of idle time to wait before pinging the receiver.
+ // |logger|: Log of cast channel events.
+ CastSocketImpl(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ base::TimeDelta connect_timeout,
+ base::TimeDelta liveness_timeout,
+ base::TimeDelta ping_interval,
+ const scoped_refptr<Logger>& logger,
+ uint64_t device_capabilities);
+
+ // For test-only.
+ // This constructor allows for setting a custom AuthContext.
+ CastSocketImpl(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ base::TimeDelta connect_timeout,
+ base::TimeDelta liveness_timeout,
+ base::TimeDelta ping_interval,
+ const scoped_refptr<Logger>& logger,
+ uint64_t device_capabilities,
+ const AuthContext& auth_context);
+
+ // Ensures that the socket is closed.
+ ~CastSocketImpl() override;
+
+ // CastSocket interface.
+ void Connect(OnOpenCallback callback) override;
+ CastTransport* transport() const override;
+ void Close(const net::CompletionCallback& callback) override;
+ const net::IPEndPoint& ip_endpoint() const override;
+ int id() const override;
+ void set_id(int channel_id) override;
+ ReadyState ready_state() const override;
+ ChannelError error_state() const override;
+ bool keep_alive() const override;
+ bool audio_only() const override;
+ void AddObserver(Observer* observer) override;
+ void RemoveObserver(Observer* observer) override;
+
+ protected:
+ // CastTransport::Delegate methods for receiving handshake messages.
+ class AuthTransportDelegate : public CastTransport::Delegate {
+ public:
+ explicit AuthTransportDelegate(CastSocketImpl* socket);
+
+ // Gets the error state of the channel.
+ // Returns CHANNEL_ERROR_NONE if no errors are present.
+ ChannelError error_state() const;
+
+ // Gets recorded error details.
+ LastError last_error() const;
+
+ // CastTransport::Delegate interface.
+ void OnError(ChannelError error_state) override;
+ void OnMessage(const CastMessage& message) override;
+ void Start() override;
+
+ private:
+ CastSocketImpl* socket_;
+ ChannelError error_state_;
+ LastError last_error_;
+ };
+
+ // CastTransport::Delegate methods to receive normal messages and errors.
+ class CastSocketMessageDelegate : public CastTransport::Delegate {
+ public:
+ CastSocketMessageDelegate(CastSocketImpl* socket);
+ ~CastSocketMessageDelegate() override;
+
+ // CastTransport::Delegate implementation.
+ void OnError(ChannelError error_state) override;
+ void OnMessage(const CastMessage& message) override;
+ void Start() override;
+
+ private:
+ CastSocketImpl* const socket_;
+ DISALLOW_COPY_AND_ASSIGN(CastSocketMessageDelegate);
+ };
+
+ // Replaces the internally-constructed transport object with one provided
+ // by the caller (e.g. a mock).
+ void SetTransportForTesting(std::unique_ptr<CastTransport> transport);
+
+ // Verifies whether the socket complies with cast channel policy.
+ // Audio only channel policy mandates that a device declaring a video out
+ // capability must not have a certificate with audio only policy.
+ bool VerifyChannelPolicy(const AuthResult& result);
+
+ void Connect();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(MockCastSocketTest, TestObservers);
+ friend class AuthTransportDelegate;
+
+ void SetErrorState(ChannelError error_state) override;
+
+ // Frees resources and cancels pending callbacks. |ready_state_| will be set
+ // READY_STATE_CLOSED on completion. A no-op if |ready_state_| is already
+ // READY_STATE_CLOSED.
+ void CloseInternal();
+
+ // Creates an instance of TCPClientSocket.
+ virtual std::unique_ptr<net::TCPClientSocket> CreateTcpSocket();
+ // Creates an instance of SSLClientSocket with the given underlying |socket|.
+ virtual std::unique_ptr<net::SSLClientSocket> CreateSslSocket(
+ std::unique_ptr<net::StreamSocket> socket);
+ // Extracts peer certificate from SSLClientSocket instance when the socket
+ // is in cert error state.
+ // Returns null if the certificate could not be extracted.
+ // TODO(kmarshall): Use MockSSLClientSocket for tests instead of overriding
+ // this function.
+ virtual scoped_refptr<net::X509Certificate> ExtractPeerCert();
+ // Verifies whether the challenge reply received from the peer is valid:
+ // 1. Signature in the reply is valid.
+ // 2. Certificate is rooted to a trusted CA.
+ virtual bool VerifyChallengeReply();
+
+ // Invoked by a cancelable closure when connection setup time
+ // exceeds the interval specified at |connect_timeout|.
+ void OnConnectTimeout();
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Following methods work together to implement the following flow:
+ // 1. Create a new TCP socket and connect to it
+ // 2. Create a new SSL socket and try connecting to it
+ // 3. If connection fails due to invalid cert authority, then extract the
+ // peer certificate from the error.
+ // 4. Whitelist the peer certificate and try #1 and #2 again.
+ // 5. If SSL socket is connected successfully, and if protocol is casts://
+ // then issue an auth challenge request.
+ // 6. Validate the auth challenge response.
+ //
+ // Main method that performs connection state transitions.
+ void DoConnectLoop(int result);
+ // Each of the below Do* method is executed in the corresponding
+ // connection state. For example when connection state is TCP_CONNECT
+ // DoTcpConnect is called, and so on.
+ int DoTcpConnect();
+ int DoTcpConnectComplete(int result);
+ int DoSslConnect();
+ int DoSslConnectComplete(int result);
+ int DoAuthChallengeSend();
+ int DoAuthChallengeSendComplete(int result);
+ int DoAuthChallengeReplyComplete(int result);
+ /////////////////////////////////////////////////////////////////////////////
+
+ // Resets the cancellable callback used for async invocations of
+ // DoConnectLoop.
+ void ResetConnectLoopCallback();
+
+ // Posts a task to invoke |connect_loop_callback_| with |result| on the
+ // current message loop.
+ void PostTaskToStartConnectLoop(int result);
+
+ // Runs the external connection callback and resets it.
+ void DoConnectCallback();
+
+ virtual base::Timer* GetTimer();
+
+ void SetConnectState(ConnectionState connect_state);
+ void SetReadyState(ReadyState ready_state);
+
+ THREAD_CHECKER(thread_checker_);
+
+ // The id of the channel.
+ int channel_id_;
+ // The IP endpoint that the the channel is connected to.
+ net::IPEndPoint ip_endpoint_;
+ // The NetLog for this service.
+ net::NetLog* net_log_;
+ // The NetLog source for this service.
+ net::NetLogSource net_log_source_;
+
+ // Amount of idle time to wait before disconnecting. If |liveness_timeout_| is
+ // set, wraps |delegate_| with a KeepAliveDelegate.
+ base::TimeDelta liveness_timeout_;
+
+ // Amount of idle time to wait before pinging the receiver, used to create
+ // KeepAliveDelegate.
+ base::TimeDelta ping_interval_;
+
+ // Shared logging object, used to log CastSocket events for diagnostics.
+ scoped_refptr<Logger> logger_;
+
+ // CertVerifier is owned by us but should be deleted AFTER SSLClientSocket
+ // since in some cases the destructor of SSLClientSocket may call a method
+ // to cancel a cert verification request.
+ std::unique_ptr<net::CertVerifier> cert_verifier_;
+ std::unique_ptr<net::TransportSecurityState> transport_security_state_;
+ std::unique_ptr<net::CTVerifier> cert_transparency_verifier_;
+ std::unique_ptr<net::CTPolicyEnforcer> ct_policy_enforcer_;
+
+ // Owned ptr to the underlying TCP socket.
+ std::unique_ptr<net::TCPClientSocket> tcp_socket_;
+
+ // Owned ptr to the underlying SSL socket.
+ std::unique_ptr<net::SSLClientSocket> socket_;
+
+ // Certificate of the peer. This field may be empty if the peer
+ // certificate is not yet fetched.
+ scoped_refptr<net::X509Certificate> peer_cert_;
+
+ // The challenge context for the current connection.
+ const AuthContext auth_context_;
+
+ // Reply received from the receiver to a challenge request.
+ std::unique_ptr<CastMessage> challenge_reply_;
+
+ // Callbacks invoked when the socket is connected or fails to connect.
+ std::vector<OnOpenCallback> connect_callbacks_;
+
+ // Callback invoked by |connect_timeout_timer_| to cancel the connection.
+ base::CancelableClosure connect_timeout_callback_;
+
+ // Duration to wait before timing out.
+ base::TimeDelta connect_timeout_;
+
+ // Timer invoked when the connection has timed out.
+ std::unique_ptr<base::Timer> connect_timeout_timer_;
+
+ // Set when a timeout is triggered and the connection process has
+ // canceled.
+ bool is_canceled_;
+
+ // Capabilities declared by the cast device.
+ uint64_t device_capabilities_;
+
+ // Whether the channel is audio only as identified by the device
+ // certificate during channel authentication.
+ bool audio_only_;
+
+ // Connection flow state machine state.
+ ConnectionState connect_state_;
+
+ // Write flow state machine state.
+ WriteState write_state_;
+
+ // Read flow state machine state.
+ ReadState read_state_;
+
+ // The last error encountered by the channel.
+ ChannelError error_state_;
+
+ // The current status of the channel.
+ ReadyState ready_state_;
+
+ // Callback which, when invoked, will re-enter the connection state machine.
+ // Oustanding callbacks will be cancelled when |this| is destroyed.
+ // The callback signature is based on net::CompletionCallback, which passes
+ // operation result codes as byte counts in the success case, or as
+ // net::Error enum values for error cases.
+ base::CancelableCallback<void(int)> connect_loop_callback_;
+
+ // Cast message formatting and parsing layer.
+ std::unique_ptr<CastTransport> transport_;
+
+ // Caller's message read and error handling delegate.
+ std::unique_ptr<CastTransport::Delegate> delegate_;
+
+ // Raw pointer to the auth handshake delegate. Used to get detailed error
+ // information.
+ AuthTransportDelegate* auth_delegate_;
+
+ // List of socket observers.
+ base::ObserverList<Observer> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(CastSocketImpl);
+};
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_CAST_SOCKET_H_
diff --git a/chromium/components/cast_channel/cast_socket_service.cc b/chromium/components/cast_channel/cast_socket_service.cc
new file mode 100644
index 00000000000..478dd55407a
--- /dev/null
+++ b/chromium/components/cast_channel/cast_socket_service.cc
@@ -0,0 +1,143 @@
+// 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/cast_channel/cast_socket_service.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/logger.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace {
+// Connect timeout for connect calls.
+const int kConnectTimeoutSecs = 10;
+
+// Ping interval
+const int kPingIntervalInSecs = 5;
+
+// Liveness timeout for connect calls, in milliseconds. If no message is
+// received from the receiver during kConnectLivenessTimeoutSecs, it is
+// considered gone.
+const int kConnectLivenessTimeoutSecs = kPingIntervalInSecs * 2;
+} // namespace
+
+namespace cast_channel {
+
+int CastSocketService::last_channel_id_ = 0;
+
+CastSocketService::CastSocketService() : logger_(new Logger()) {
+ DETACH_FROM_THREAD(thread_checker_);
+}
+
+// This is a leaky singleton and the dtor won't be called.
+CastSocketService::~CastSocketService() = default;
+
+// static
+CastSocketService* CastSocketService::GetInstance() {
+ return base::Singleton<CastSocketService,
+ base::LeakySingletonTraits<CastSocketService>>::get();
+}
+
+scoped_refptr<Logger> CastSocketService::GetLogger() {
+ return logger_;
+}
+
+CastSocket* CastSocketService::AddSocket(std::unique_ptr<CastSocket> socket) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(socket);
+ int id = ++last_channel_id_;
+ socket->set_id(id);
+
+ auto* socket_ptr = socket.get();
+ sockets_.insert(std::make_pair(id, std::move(socket)));
+ return socket_ptr;
+}
+
+std::unique_ptr<CastSocket> CastSocketService::RemoveSocket(int channel_id) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(channel_id > 0);
+ auto socket_it = sockets_.find(channel_id);
+
+ std::unique_ptr<CastSocket> socket;
+ if (socket_it != sockets_.end()) {
+ socket = std::move(socket_it->second);
+ sockets_.erase(socket_it);
+ }
+ return socket;
+}
+
+CastSocket* CastSocketService::GetSocket(int channel_id) const {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(channel_id > 0);
+ const auto& socket_it = sockets_.find(channel_id);
+ return socket_it == sockets_.end() ? nullptr : socket_it->second.get();
+}
+
+CastSocket* CastSocketService::GetSocket(
+ const net::IPEndPoint& ip_endpoint) const {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ auto it = std::find_if(
+ sockets_.begin(), sockets_.end(),
+ [&ip_endpoint](
+ const std::pair<const int, std::unique_ptr<CastSocket>>& pair) {
+ return pair.second->ip_endpoint() == ip_endpoint;
+ });
+ return it == sockets_.end() ? nullptr : it->second.get();
+}
+
+int CastSocketService::OpenSocket(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ const base::TimeDelta& connect_timeout,
+ const base::TimeDelta& liveness_timeout,
+ const base::TimeDelta& ping_interval,
+ uint64_t device_capabilities,
+ CastSocket::OnOpenCallback open_cb,
+ CastSocket::Observer* observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(observer);
+ auto* socket = GetSocket(ip_endpoint);
+
+ if (!socket) {
+ // If cast socket does not exist.
+ if (socket_for_test_) {
+ socket = AddSocket(std::move(socket_for_test_));
+ } else {
+ socket = new CastSocketImpl(ip_endpoint, net_log, connect_timeout,
+ liveness_timeout, ping_interval, logger_,
+ device_capabilities);
+ AddSocket(base::WrapUnique(socket));
+ }
+ }
+
+ socket->AddObserver(observer);
+ socket->Connect(std::move(open_cb));
+ return socket->id();
+}
+
+int CastSocketService::OpenSocket(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ CastSocket::OnOpenCallback open_cb,
+ CastSocket::Observer* observer) {
+ auto connect_timeout = base::TimeDelta::FromSeconds(kConnectTimeoutSecs);
+ auto ping_interval = base::TimeDelta::FromSeconds(kPingIntervalInSecs);
+ auto liveness_timeout =
+ base::TimeDelta::FromSeconds(kConnectLivenessTimeoutSecs);
+ return OpenSocket(ip_endpoint, net_log, connect_timeout, liveness_timeout,
+ ping_interval, CastDeviceCapability::NONE,
+ std::move(open_cb), observer);
+}
+
+void CastSocketService::RemoveObserver(CastSocket::Observer* observer) {
+ for (auto& socket_it : sockets_)
+ socket_it.second->RemoveObserver(observer);
+}
+
+void CastSocketService::SetSocketForTest(
+ std::unique_ptr<cast_channel::CastSocket> socket_for_test) {
+ socket_for_test_ = std::move(socket_for_test);
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_socket_service.h b/chromium/components/cast_channel/cast_socket_service.h
new file mode 100644
index 00000000000..e93ead0f670
--- /dev/null
+++ b/chromium/components/cast_channel/cast_socket_service.h
@@ -0,0 +1,112 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CAST_CHANNEL_CAST_CHANNEL_SERVICE_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_CHANNEL_SERVICE_H_
+
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/threading/thread_checker.h"
+#include "components/cast_channel/cast_socket.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace cast_channel {
+
+// This class adds, removes, and returns cast sockets created by CastChannelAPI
+// to underlying storage.
+// Instance of this class is created on the UI thread and destroyed on the IO
+// thread. All public API must be called from the IO thread.
+class CastSocketService {
+ public:
+ static CastSocketService* GetInstance();
+
+ // Returns a pointer to the Logger member variable.
+ scoped_refptr<cast_channel::Logger> GetLogger();
+
+ // Adds |socket| to |sockets_| and returns raw pointer of |socket|. Takes
+ // ownership of |socket|.
+ CastSocket* AddSocket(std::unique_ptr<CastSocket> socket);
+
+ // Removes the CastSocket corresponding to |channel_id| from the
+ // CastSocketRegistry. Returns nullptr if no such CastSocket exists.
+ std::unique_ptr<CastSocket> RemoveSocket(int channel_id);
+
+ // Returns the socket corresponding to |channel_id| if one exists, or nullptr
+ // otherwise.
+ virtual CastSocket* GetSocket(int channel_id) const;
+
+ CastSocket* GetSocket(const net::IPEndPoint& ip_endpoint) const;
+
+ // Opens cast socket with |ip_endpoint| and invokes |open_cb| when opening
+ // operation finishes. If cast socket with |ip_endpoint| already exists,
+ // invoke |open_cb| directly with existing socket's channel ID.
+ // Parameters:
+ // |ip_endpoint|: IP address and port of the remote host.
+ // |net_log|: Log of socket events.
+ // |connect_timeout|: Connection timeout interval.
+ // |liveness_timeout|: Liveness timeout for connect calls.
+ // |ping_interval|: Ping interval.
+ // |logger|: Log of cast channel events.
+ // |device_capabilities|: Device capabilities.
+ // |open_cb|: OnOpenCallback invoked when cast socket is opened.
+ // |observer|: Observer handles messages and errors on newly opened socket.
+ // Does not take ownership of |observer|.
+ int OpenSocket(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ const base::TimeDelta& connect_timeout,
+ const base::TimeDelta& liveness_timeout,
+ const base::TimeDelta& ping_interval,
+ uint64_t device_capabilities,
+ CastSocket::OnOpenCallback open_cb,
+ CastSocket::Observer* observer);
+
+ // Opens cast socket with |ip_endpoint| and invokes |open_cb| when opening
+ // operation finishes. If cast socket with |ip_endpoint| already exists,
+ // invoke |open_cb| directly with existing socket's channel ID.
+ // |ip_endpoint|: IP address and port of the remote host.
+ // |net_log|: Log of socket events.
+ // |open_cb|: OnOpenCallback invoked when cast socket is opened.
+ // |observer|: Observer handles messages and errors on newly opened socket.
+ // Does not take ownership of |observer|.
+ virtual int OpenSocket(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ CastSocket::OnOpenCallback open_cb,
+ CastSocket::Observer* observer);
+
+ // Remove |observer| from each socket in |sockets_|
+ void RemoveObserver(CastSocket::Observer* observer);
+
+ // Allow test to inject a mock cast socket.
+ void SetSocketForTest(std::unique_ptr<CastSocket> socket_for_test);
+
+ private:
+ friend class CastSocketServiceTest;
+ friend class MockCastSocketService;
+ friend struct base::DefaultSingletonTraits<CastSocketService>;
+ friend struct std::default_delete<CastSocketService>;
+
+ CastSocketService();
+ virtual ~CastSocketService();
+
+ // Used to generate CastSocket id.
+ static int last_channel_id_;
+
+ // The collection of CastSocket keyed by channel_id.
+ std::map<int, std::unique_ptr<CastSocket>> sockets_;
+
+ scoped_refptr<Logger> logger_;
+
+ std::unique_ptr<CastSocket> socket_for_test_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(CastSocketService);
+};
+
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_CAST_CHANNEL_SERVICE_H_
diff --git a/chromium/components/cast_channel/cast_socket_service_unittest.cc b/chromium/components/cast_channel/cast_socket_service_unittest.cc
new file mode 100644
index 00000000000..f2c0ce02b1f
--- /dev/null
+++ b/chromium/components/cast_channel/cast_socket_service_unittest.cc
@@ -0,0 +1,96 @@
+// 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/cast_channel/cast_socket_service.h"
+#include "base/test/mock_callback.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SaveArg;
+using testing::WithArgs;
+
+namespace cast_channel {
+
+class CastSocketServiceTest : public testing::Test {
+ public:
+ CastSocketServiceTest()
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ cast_socket_service_(new CastSocketService()) {}
+
+ CastSocket* AddSocket(std::unique_ptr<CastSocket> socket) {
+ return cast_socket_service_->AddSocket(std::move(socket));
+ }
+
+ void TearDown() override { cast_socket_service_ = nullptr; }
+
+ protected:
+ content::TestBrowserThreadBundle thread_bundle_;
+ std::unique_ptr<CastSocketService> cast_socket_service_;
+ base::MockCallback<CastSocket::OnOpenCallback> mock_on_open_callback_;
+ MockCastSocketObserver mock_observer_;
+};
+
+TEST_F(CastSocketServiceTest, TestAddSocket) {
+ auto socket1 = base::MakeUnique<MockCastSocket>();
+ auto* socket_ptr1 = AddSocket(std::move(socket1));
+ EXPECT_NE(0, socket_ptr1->id());
+
+ auto socket2 = base::MakeUnique<MockCastSocket>();
+ auto* socket_ptr2 = AddSocket(std::move(socket2));
+ EXPECT_NE(socket_ptr1->id(), socket_ptr2->id());
+
+ auto removed_socket = cast_socket_service_->RemoveSocket(socket_ptr2->id());
+ EXPECT_EQ(socket_ptr2, removed_socket.get());
+
+ auto socket3 = base::MakeUnique<MockCastSocket>();
+ auto* socket_ptr3 = AddSocket(std::move(socket3));
+ EXPECT_NE(socket_ptr1->id(), socket_ptr3->id());
+ EXPECT_NE(socket_ptr2->id(), socket_ptr3->id());
+}
+
+TEST_F(CastSocketServiceTest, TestRemoveAndGetSocket) {
+ int channel_id = 1;
+ auto* socket_ptr = cast_socket_service_->GetSocket(channel_id);
+ EXPECT_FALSE(socket_ptr);
+ auto socket = cast_socket_service_->RemoveSocket(channel_id);
+ EXPECT_FALSE(socket);
+
+ auto mock_socket = base::MakeUnique<MockCastSocket>();
+
+ auto* mock_socket_ptr = AddSocket(std::move(mock_socket));
+ channel_id = mock_socket_ptr->id();
+ EXPECT_EQ(mock_socket_ptr, cast_socket_service_->GetSocket(channel_id));
+
+ socket = cast_socket_service_->RemoveSocket(channel_id);
+ EXPECT_TRUE(socket);
+}
+
+TEST_F(CastSocketServiceTest, TestOpenChannel) {
+ auto* mock_socket = new MockCastSocket();
+ auto ip_endpoint = CreateIPEndPointForTest();
+ mock_socket->SetIPEndpoint(ip_endpoint);
+ cast_socket_service_->SetSocketForTest(base::WrapUnique(mock_socket));
+
+ EXPECT_CALL(*mock_socket, ConnectInternal(_))
+ .WillOnce(WithArgs<0>(
+ Invoke([&](const MockCastSocket::MockOnOpenCallback& callback) {
+ callback.Run(mock_socket->id(), ChannelError::NONE);
+ })));
+ EXPECT_CALL(mock_on_open_callback_, Run(_, ChannelError::NONE));
+ EXPECT_CALL(*mock_socket, AddObserver(_));
+
+ cast_socket_service_->OpenSocket(ip_endpoint, nullptr /* net_log */,
+ mock_on_open_callback_.Get(),
+ &mock_observer_);
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_socket_unittest.cc b/chromium/components/cast_channel/cast_socket_unittest.cc
new file mode 100644
index 00000000000..dc40a4320dc
--- /dev/null
+++ b/chromium/components/cast_channel/cast_socket_unittest.cc
@@ -0,0 +1,1124 @@
+// 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/cast_channel/cast_socket.h"
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/sys_byteorder.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/timer/mock_timer.h"
+#include "components/cast_channel/cast_auth_util.h"
+#include "components/cast_channel/cast_framer.h"
+#include "components/cast_channel/cast_message_util.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "components/cast_channel/cast_transport.h"
+#include "components/cast_channel/logger.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+#include "net/cert/pem_tokenizer.h"
+#include "net/log/net_log_source.h"
+#include "net/log/test_net_log.h"
+#include "net/socket/socket_test_util.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/socket/ssl_server_socket.h"
+#include "net/socket/tcp_client_socket.h"
+#include "net/socket/tcp_server_socket.h"
+#include "net/ssl/ssl_info.h"
+#include "net/ssl/ssl_server_config.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const int64_t kDistantTimeoutMillis = 100000; // 100 seconds (never hit).
+
+using ::testing::A;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InvokeArgument;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::_;
+
+namespace cast_channel {
+namespace {
+const char kAuthNamespace[] = "urn:x-cast:com.google.cast.tp.deviceauth";
+
+// Returns an auth challenge message inline.
+CastMessage CreateAuthChallenge() {
+ CastMessage output;
+ CreateAuthChallengeMessage(&output, AuthContext::Create());
+ return output;
+}
+
+// Returns an auth challenge response message inline.
+CastMessage CreateAuthReply() {
+ CastMessage output;
+ output.set_protocol_version(CastMessage::CASTV2_1_0);
+ output.set_source_id("sender-0");
+ output.set_destination_id("receiver-0");
+ output.set_payload_type(CastMessage::BINARY);
+ output.set_payload_binary("abcd");
+ output.set_namespace_(kAuthNamespace);
+ return output;
+}
+
+CastMessage CreateTestMessage() {
+ CastMessage test_message;
+ test_message.set_protocol_version(CastMessage::CASTV2_1_0);
+ test_message.set_namespace_("ns");
+ test_message.set_source_id("source");
+ test_message.set_destination_id("dest");
+ test_message.set_payload_type(CastMessage::STRING);
+ test_message.set_payload_utf8("payload");
+ return test_message;
+}
+
+base::FilePath GetTestCertsDirectory() {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.Append(FILE_PATH_LITERAL("components"));
+ path = path.Append(FILE_PATH_LITERAL("test"));
+ path = path.Append(FILE_PATH_LITERAL("data"));
+ path = path.Append(FILE_PATH_LITERAL("cast_channel"));
+ return path;
+}
+
+class MockTCPSocket : public net::TCPClientSocket {
+ public:
+ explicit MockTCPSocket(const net::MockConnect& connect_data)
+ : TCPClientSocket(net::AddressList(),
+ nullptr,
+ nullptr,
+ net::NetLogSource()),
+ connect_data_(connect_data),
+ do_nothing_(false) {}
+
+ explicit MockTCPSocket(bool do_nothing)
+ : TCPClientSocket(net::AddressList(),
+ nullptr,
+ nullptr,
+ net::NetLogSource()) {
+ CHECK(do_nothing);
+ do_nothing_ = do_nothing;
+ }
+
+ virtual int Connect(const net::CompletionCallback& callback) {
+ if (do_nothing_) {
+ // Stall the I/O event loop.
+ return net::ERR_IO_PENDING;
+ }
+
+ if (connect_data_.mode == net::ASYNC) {
+ CHECK_NE(connect_data_.result, net::ERR_IO_PENDING);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, connect_data_.result));
+ return net::ERR_IO_PENDING;
+ } else {
+ return connect_data_.result;
+ }
+ }
+
+ virtual bool SetKeepAlive(bool enable, int delay) {
+ // Always return true in tests
+ return true;
+ }
+
+ virtual bool SetNoDelay(bool no_delay) {
+ // Always return true in tests
+ return true;
+ }
+
+ MOCK_METHOD3(Read, int(net::IOBuffer*, int, const net::CompletionCallback&));
+ MOCK_METHOD3(Write, int(net::IOBuffer*, int, const net::CompletionCallback&));
+
+ virtual void Disconnect() {
+ // Do nothing in tests
+ }
+
+ private:
+ net::MockConnect connect_data_;
+ bool do_nothing_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockTCPSocket);
+};
+
+class CompleteHandler {
+ public:
+ CompleteHandler() {}
+ MOCK_METHOD1(OnCloseComplete, void(int result));
+ MOCK_METHOD2(OnConnectComplete,
+ void(int channel_id, ChannelError error_state));
+ MOCK_METHOD1(OnWriteComplete, void(int result));
+ MOCK_METHOD1(OnReadComplete, void(int result));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompleteHandler);
+};
+
+class TestCastSocketBase : public CastSocketImpl {
+ public:
+ TestCastSocketBase(const net::IPEndPoint& ip_endpoint,
+ int64_t timeout_ms,
+ Logger* logger,
+ uint64_t device_capabilities)
+ : TestCastSocketBase(ip_endpoint,
+ timeout_ms,
+ logger,
+ new net::TestNetLog(),
+ device_capabilities) {}
+
+ TestCastSocketBase(const net::IPEndPoint& ip_endpoint,
+ int64_t timeout_ms,
+ Logger* logger,
+ net::TestNetLog* capturing_net_log,
+ uint64_t device_capabilities)
+ : CastSocketImpl(ip_endpoint,
+ capturing_net_log,
+ base::TimeDelta::FromMilliseconds(timeout_ms),
+ base::TimeDelta(),
+ base::TimeDelta(),
+ logger,
+ device_capabilities,
+ AuthContext::Create()),
+ capturing_net_log_(capturing_net_log),
+ ip_(ip_endpoint),
+ extract_cert_result_(true),
+ verify_challenge_result_(true),
+ verify_challenge_disallow_(false),
+ mock_timer_(new base::MockTimer(false, false)) {}
+
+ void SetExtractCertResult(bool value) { extract_cert_result_ = value; }
+
+ void SetVerifyChallengeResult(bool value) {
+ verify_challenge_result_ = value;
+ }
+
+ void TriggerTimeout() { mock_timer_->Fire(); }
+
+ bool TestVerifyChannelPolicyNone() {
+ AuthResult authResult;
+ return VerifyChannelPolicy(authResult);
+ }
+
+ void DisallowVerifyChallengeResult() { verify_challenge_disallow_ = true; }
+
+ protected:
+ ~TestCastSocketBase() override {}
+
+ scoped_refptr<net::X509Certificate> ExtractPeerCert() override {
+ return extract_cert_result_
+ ? net::ImportCertFromFile(GetTestCertsDirectory(),
+ "self_signed.pem")
+ : nullptr;
+ }
+
+ bool VerifyChallengeReply() override {
+ EXPECT_FALSE(verify_challenge_disallow_);
+ return verify_challenge_result_;
+ }
+
+ base::Timer* GetTimer() override { return mock_timer_.get(); }
+
+ std::unique_ptr<net::TestNetLog> capturing_net_log_;
+ net::IPEndPoint ip_;
+ // Simulated result of peer cert extraction.
+ bool extract_cert_result_;
+ // Simulated result of verifying challenge reply.
+ bool verify_challenge_result_;
+ bool verify_challenge_disallow_;
+ std::unique_ptr<base::MockTimer> mock_timer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestCastSocketBase);
+};
+
+class MockTestCastSocket : public TestCastSocketBase {
+ public:
+ static std::unique_ptr<MockTestCastSocket> CreateSecure(
+ Logger* logger,
+ uint64_t device_capabilities = cast_channel::CastDeviceCapability::NONE) {
+ return std::unique_ptr<MockTestCastSocket>(
+ new MockTestCastSocket(CreateIPEndPointForTest(), kDistantTimeoutMillis,
+ logger, device_capabilities));
+ }
+
+ using TestCastSocketBase::TestCastSocketBase;
+
+ ~MockTestCastSocket() override {}
+
+ void SetupMockTransport() {
+ mock_transport_ = new MockCastTransport;
+ SetTransportForTesting(base::WrapUnique(mock_transport_));
+ }
+
+ // Socket connection helpers.
+ void SetupTcpConnect(net::IoMode mode, int result) {
+ tcp_connect_data_.reset(new net::MockConnect(mode, result, ip_));
+ }
+ void SetupSslConnect(net::IoMode mode, int result) {
+ ssl_connect_data_.reset(new net::MockConnect(mode, result, ip_));
+ }
+
+ // Socket I/O helpers.
+ void AddWriteResult(const net::MockWrite& write) { writes_.push_back(write); }
+ void AddWriteResult(net::IoMode mode, int result) {
+ AddWriteResult(net::MockWrite(mode, result));
+ }
+ void AddWriteResultForData(net::IoMode mode, const std::string& msg) {
+ AddWriteResult(mode, msg.size());
+ }
+ void AddReadResult(const net::MockRead& read) { reads_.push_back(read); }
+ void AddReadResult(net::IoMode mode, int result) {
+ AddReadResult(net::MockRead(mode, result));
+ }
+ void AddReadResultForData(net::IoMode mode, const std::string& data) {
+ AddReadResult(net::MockRead(mode, data.c_str(), data.size()));
+ }
+
+ // Helpers for modifying other connection-related behaviors.
+ void SetupTcpConnectUnresponsive() { tcp_unresponsive_ = true; }
+
+ bool TestVerifyChannelPolicyAudioOnly() {
+ AuthResult authResult;
+ authResult.channel_policies |= AuthResult::POLICY_AUDIO_ONLY;
+ return VerifyChannelPolicy(authResult);
+ }
+
+ MockCastTransport* GetMockTransport() {
+ CHECK(mock_transport_);
+ return mock_transport_;
+ }
+
+ private:
+ std::unique_ptr<net::TCPClientSocket> CreateTcpSocket() override {
+ if (tcp_unresponsive_) {
+ return std::unique_ptr<net::TCPClientSocket>(new MockTCPSocket(true));
+ } else {
+ return std::unique_ptr<net::TCPClientSocket>(
+ new MockTCPSocket(*tcp_connect_data_));
+ }
+ }
+
+ std::unique_ptr<net::SSLClientSocket> CreateSslSocket(
+ std::unique_ptr<net::StreamSocket>) override {
+ ssl_data_.reset(new net::StaticSocketDataProvider(
+ reads_.data(), reads_.size(), writes_.data(), writes_.size()));
+ ssl_data_->set_connect_data(*ssl_connect_data_);
+ // NOTE: net::MockTCPClientSocket inherits from net::SSLClientSocket !!
+ return std::unique_ptr<net::SSLClientSocket>(new net::MockTCPClientSocket(
+ net::AddressList(), capturing_net_log_.get(), ssl_data_.get()));
+ }
+
+ // Simulated connect data
+ std::unique_ptr<net::MockConnect> tcp_connect_data_;
+ std::unique_ptr<net::MockConnect> ssl_connect_data_;
+ // Simulated read / write data
+ std::vector<net::MockWrite> writes_;
+ std::vector<net::MockRead> reads_;
+ std::unique_ptr<net::SocketDataProvider> ssl_data_;
+ // If true, makes TCP connection process stall. For timeout testing.
+ bool tcp_unresponsive_ = false;
+ MockCastTransport* mock_transport_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(MockTestCastSocket);
+};
+
+class SslTestCastSocket : public TestCastSocketBase {
+ public:
+ static std::unique_ptr<SslTestCastSocket> CreateSecure(
+ Logger* logger,
+ uint64_t device_capabilities = cast_channel::CastDeviceCapability::NONE) {
+ return std::unique_ptr<SslTestCastSocket>(
+ new SslTestCastSocket(CreateIPEndPointForTest(), kDistantTimeoutMillis,
+ logger, device_capabilities));
+ }
+
+ using TestCastSocketBase::TestCastSocketBase;
+
+ void SetTcpSocket(std::unique_ptr<net::TCPClientSocket> tcp_client_socket) {
+ tcp_client_socket_ = std::move(tcp_client_socket);
+ }
+
+ private:
+ std::unique_ptr<net::TCPClientSocket> CreateTcpSocket() override {
+ return std::move(tcp_client_socket_);
+ }
+
+ std::unique_ptr<net::TCPClientSocket> tcp_client_socket_;
+};
+
+class CastSocketTestBase : public testing::Test {
+ protected:
+ CastSocketTestBase()
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ logger_(new Logger()),
+ observer_(new MockCastSocketObserver()) {}
+ ~CastSocketTestBase() override {}
+
+ void SetUp() override { EXPECT_CALL(*observer_, OnMessage(_, _)).Times(0); }
+
+ // Runs all pending tasks in the message loop.
+ void RunPendingTasks() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ Logger* logger_;
+ CompleteHandler handler_;
+ std::unique_ptr<MockCastSocketObserver> observer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CastSocketTestBase);
+};
+
+class MockCastSocketTest : public CastSocketTestBase {
+ protected:
+ MockCastSocketTest() {}
+
+ void TearDown() override {
+ if (socket_.get()) {
+ EXPECT_CALL(handler_, OnCloseComplete(net::OK));
+ socket_->Close(base::Bind(&CompleteHandler::OnCloseComplete,
+ base::Unretained(&handler_)));
+ }
+ }
+
+ void CreateCastSocketSecure() {
+ socket_ = MockTestCastSocket::CreateSecure(logger_);
+ }
+
+ void HandleAuthHandshake() {
+ socket_->SetupMockTransport();
+ CastMessage challenge_proto = CreateAuthChallenge();
+ EXPECT_CALL(*socket_->GetMockTransport(),
+ SendMessage(EqualsProto(challenge_proto), _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK));
+ EXPECT_CALL(*socket_->GetMockTransport(), Start());
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::NONE));
+ socket_->AddObserver(observer_.get());
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+ socket_->GetMockTransport()->current_delegate()->OnMessage(
+ CreateAuthReply());
+ RunPendingTasks();
+ }
+
+ std::unique_ptr<MockTestCastSocket> socket_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockCastSocketTest);
+};
+
+class SslCastSocketTest : public CastSocketTestBase {
+ protected:
+ SslCastSocketTest() {}
+
+ void TearDown() override {
+ if (socket_.get()) {
+ EXPECT_CALL(handler_, OnCloseComplete(net::OK));
+ socket_->Close(base::Bind(&CompleteHandler::OnCloseComplete,
+ base::Unretained(&handler_)));
+ }
+ }
+
+ void CreateSockets() {
+ socket_ = SslTestCastSocket::CreateSecure(logger_);
+
+ server_cert_ =
+ net::ImportCertFromFile(GetTestCertsDirectory(), "self_signed.pem");
+ ASSERT_TRUE(server_cert_);
+ server_private_key_ = ReadTestKeyFromPEM("self_signed.pem");
+ ASSERT_TRUE(server_private_key_);
+ server_context_ = CreateSSLServerContext(
+ server_cert_.get(), *server_private_key_, server_ssl_config_);
+
+ tcp_server_socket_.reset(
+ new net::TCPServerSocket(nullptr, net::NetLogSource()));
+ ASSERT_EQ(net::OK,
+ tcp_server_socket_->ListenWithAddressAndPort("127.0.0.1", 0, 1));
+ net::IPEndPoint server_address;
+ ASSERT_EQ(net::OK, tcp_server_socket_->GetLocalAddress(&server_address));
+ tcp_client_socket_.reset(
+ new net::TCPClientSocket(net::AddressList(server_address), nullptr,
+ nullptr, net::NetLogSource()));
+
+ std::unique_ptr<net::StreamSocket> accepted_socket;
+ accept_result_ = tcp_server_socket_->Accept(
+ &accepted_socket, base::Bind(&SslCastSocketTest::TcpAcceptCallback,
+ base::Unretained(this)));
+ connect_result_ = tcp_client_socket_->Connect(base::Bind(
+ &SslCastSocketTest::TcpConnectCallback, base::Unretained(this)));
+ while (accept_result_ == net::ERR_IO_PENDING ||
+ connect_result_ == net::ERR_IO_PENDING) {
+ RunPendingTasks();
+ }
+ ASSERT_EQ(net::OK, accept_result_);
+ ASSERT_EQ(net::OK, connect_result_);
+ ASSERT_TRUE(accepted_socket);
+ ASSERT_TRUE(tcp_client_socket_->IsConnected());
+
+ server_socket_ =
+ server_context_->CreateSSLServerSocket(std::move(accepted_socket));
+ ASSERT_TRUE(server_socket_);
+
+ socket_->SetTcpSocket(std::move(tcp_client_socket_));
+ }
+
+ void ConnectSockets() {
+ socket_->AddObserver(observer_.get());
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+
+ net::TestCompletionCallback handshake_callback;
+ int server_ret = handshake_callback.GetResult(
+ server_socket_->Handshake(handshake_callback.callback()));
+
+ ASSERT_EQ(net::OK, server_ret);
+ }
+
+ void TcpAcceptCallback(int result) { accept_result_ = result; }
+
+ void TcpConnectCallback(int result) { connect_result_ = result; }
+
+ std::unique_ptr<crypto::RSAPrivateKey> ReadTestKeyFromPEM(
+ const base::StringPiece& name) {
+ base::FilePath key_path = GetTestCertsDirectory().AppendASCII(name);
+ std::vector<std::string> headers({"PRIVATE KEY"});
+ std::string pem_data;
+ if (!base::ReadFileToString(key_path, &pem_data)) {
+ return nullptr;
+ }
+ net::PEMTokenizer pem_tokenizer(pem_data, headers);
+ if (!pem_tokenizer.GetNext()) {
+ return nullptr;
+ }
+ std::vector<uint8_t> key_vector(pem_tokenizer.data().begin(),
+ pem_tokenizer.data().end());
+ std::unique_ptr<crypto::RSAPrivateKey> key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector));
+ return key;
+ }
+
+ int ReadExactLength(net::IOBuffer* buffer,
+ int buffer_length,
+ net::Socket* socket) {
+ scoped_refptr<net::DrainableIOBuffer> draining_buffer(
+ new net::DrainableIOBuffer(buffer, buffer_length));
+ while (draining_buffer->BytesRemaining() > 0) {
+ net::TestCompletionCallback read_callback;
+ int read_result = read_callback.GetResult(server_socket_->Read(
+ draining_buffer.get(), draining_buffer->BytesRemaining(),
+ read_callback.callback()));
+ EXPECT_GT(read_result, 0);
+ draining_buffer->DidConsume(read_result);
+ }
+ return buffer_length;
+ }
+
+ int WriteExactLength(net::IOBuffer* buffer,
+ int buffer_length,
+ net::Socket* socket) {
+ scoped_refptr<net::DrainableIOBuffer> draining_buffer(
+ new net::DrainableIOBuffer(buffer, buffer_length));
+ while (draining_buffer->BytesRemaining() > 0) {
+ net::TestCompletionCallback write_callback;
+ int write_result = write_callback.GetResult(server_socket_->Write(
+ draining_buffer.get(), draining_buffer->BytesRemaining(),
+ write_callback.callback()));
+ EXPECT_GT(write_result, 0);
+ draining_buffer->DidConsume(write_result);
+ }
+ return buffer_length;
+ }
+
+ // Result values used for TCP socket setup. These should contain values from
+ // net::Error.
+ int accept_result_;
+ int connect_result_;
+
+ // Underlying TCP sockets for |socket_| to communicate with |server_socket_|
+ // when testing with the real SSL implementation.
+ std::unique_ptr<net::TCPClientSocket> tcp_client_socket_;
+ std::unique_ptr<net::TCPServerSocket> tcp_server_socket_;
+
+ std::unique_ptr<SslTestCastSocket> socket_;
+
+ // |server_socket_| is used for the *RealSSL tests in order to test the
+ // CastSocket over a real SSL socket. The other members below are used to
+ // initialize |server_socket_|.
+ std::unique_ptr<net::SSLServerSocket> server_socket_;
+ std::unique_ptr<net::SSLServerContext> server_context_;
+ std::unique_ptr<crypto::RSAPrivateKey> server_private_key_;
+ scoped_refptr<net::X509Certificate> server_cert_;
+ net::SSLServerConfig server_ssl_config_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SslCastSocketTest);
+};
+
+} // namespace
+
+// Tests that the following connection flow works:
+// - TCP connection succeeds (async)
+// - SSL connection succeeds (async)
+// - Cert is extracted successfully
+// - Challenge request is sent (async)
+// - Challenge response is received (async)
+// - Credentials are verified successfuly
+TEST_F(MockCastSocketTest, TestConnectFullSecureFlowAsync) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnect(net::ASYNC, net::OK);
+ socket_->SetupSslConnect(net::ASYNC, net::OK);
+
+ HandleAuthHandshake();
+
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+}
+
+// Tests that the following connection flow works:
+// - TCP connection succeeds (sync)
+// - SSL connection succeeds (sync)
+// - Cert is extracted successfully
+// - Challenge request is sent (sync)
+// - Challenge response is received (sync)
+// - Credentials are verified successfuly
+TEST_F(MockCastSocketTest, TestConnectFullSecureFlowSync) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::OK);
+
+ HandleAuthHandshake();
+
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+}
+
+// Test that an AuthMessage with a mangled namespace triggers cancelation
+// of the connection event loop.
+TEST_F(MockCastSocketTest, TestConnectAuthMessageCorrupted) {
+ CreateCastSocketSecure();
+ socket_->SetupMockTransport();
+
+ socket_->SetupTcpConnect(net::ASYNC, net::OK);
+ socket_->SetupSslConnect(net::ASYNC, net::OK);
+
+ CastMessage challenge_proto = CreateAuthChallenge();
+ EXPECT_CALL(*socket_->GetMockTransport(),
+ SendMessage(EqualsProto(challenge_proto), _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK));
+ EXPECT_CALL(*socket_->GetMockTransport(), Start());
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::TRANSPORT_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+ CastMessage mangled_auth_reply = CreateAuthReply();
+ mangled_auth_reply.set_namespace_("BOGUS_NAMESPACE");
+
+ socket_->GetMockTransport()->current_delegate()->OnMessage(
+ mangled_auth_reply);
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::TRANSPORT_ERROR, socket_->error_state());
+
+ // Verifies that the CastSocket's resources were torn down during channel
+ // close. (see http://crbug.com/504078)
+ EXPECT_EQ(nullptr, socket_->transport());
+}
+
+// Test connection error - TCP connect fails (async)
+TEST_F(MockCastSocketTest, TestConnectTcpConnectErrorAsync) {
+ CreateCastSocketSecure();
+
+ socket_->SetupTcpConnect(net::ASYNC, net::ERR_FAILED);
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CONNECT_ERROR, socket_->error_state());
+}
+
+// Test connection error - TCP connect fails (sync)
+TEST_F(MockCastSocketTest, TestConnectTcpConnectErrorSync) {
+ CreateCastSocketSecure();
+
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::ERR_FAILED);
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CONNECT_ERROR, socket_->error_state());
+}
+
+// Test connection error - timeout
+TEST_F(MockCastSocketTest, TestConnectTcpTimeoutError) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnectUnresponsive();
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_TIMEOUT));
+ EXPECT_CALL(*observer_, OnError(_, ChannelError::CONNECT_TIMEOUT));
+ socket_->AddObserver(observer_.get());
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CONNECTING, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+ socket_->TriggerTimeout();
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CONNECT_TIMEOUT, socket_->error_state());
+}
+
+// Test connection error - TCP socket returns timeout
+TEST_F(MockCastSocketTest, TestConnectTcpSocketTimeoutError) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::ERR_CONNECTION_TIMED_OUT);
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_TIMEOUT));
+ EXPECT_CALL(*observer_, OnError(_, ChannelError::CONNECT_TIMEOUT));
+ socket_->AddObserver(observer_.get());
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CONNECT_TIMEOUT, socket_->error_state());
+ EXPECT_EQ(net::ERR_CONNECTION_TIMED_OUT,
+ logger_->GetLastError(socket_->id()).net_return_value);
+}
+
+// Test connection error - SSL connect fails (async)
+TEST_F(MockCastSocketTest, TestConnectSslConnectErrorAsync) {
+ CreateCastSocketSecure();
+
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::ERR_FAILED);
+
+ EXPECT_CALL(handler_,
+ OnConnectComplete(_, ChannelError::AUTHENTICATION_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::AUTHENTICATION_ERROR, socket_->error_state());
+}
+
+// Test connection error - SSL connect fails (sync)
+TEST_F(MockCastSocketTest, TestConnectSslConnectErrorSync) {
+ CreateCastSocketSecure();
+
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::ERR_FAILED);
+
+ EXPECT_CALL(handler_,
+ OnConnectComplete(_, ChannelError::AUTHENTICATION_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::AUTHENTICATION_ERROR, socket_->error_state());
+ EXPECT_EQ(net::ERR_FAILED,
+ logger_->GetLastError(socket_->id()).net_return_value);
+}
+
+// Test connection error - SSL connect times out (sync)
+TEST_F(MockCastSocketTest, TestConnectSslConnectTimeoutSync) {
+ CreateCastSocketSecure();
+
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::ERR_CONNECTION_TIMED_OUT);
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_TIMEOUT));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CONNECT_TIMEOUT, socket_->error_state());
+ EXPECT_EQ(net::ERR_CONNECTION_TIMED_OUT,
+ logger_->GetLastError(socket_->id()).net_return_value);
+}
+
+// Test connection error - SSL connect times out (async)
+TEST_F(MockCastSocketTest, TestConnectSslConnectTimeoutAsync) {
+ CreateCastSocketSecure();
+
+ socket_->SetupTcpConnect(net::ASYNC, net::OK);
+ socket_->SetupSslConnect(net::ASYNC, net::ERR_CONNECTION_TIMED_OUT);
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_TIMEOUT));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CONNECT_TIMEOUT, socket_->error_state());
+}
+
+// Test connection error - challenge send fails
+TEST_F(MockCastSocketTest, TestConnectChallengeSendError) {
+ CreateCastSocketSecure();
+ socket_->SetupMockTransport();
+
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::OK);
+ EXPECT_CALL(*socket_->GetMockTransport(),
+ SendMessage(EqualsProto(CreateAuthChallenge()), _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::ERR_CONNECTION_RESET));
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CAST_SOCKET_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CAST_SOCKET_ERROR, socket_->error_state());
+}
+
+// Test connection error - connection is destroyed after the challenge is
+// sent, with the async result still lurking in the task queue.
+TEST_F(MockCastSocketTest, TestConnectDestroyedAfterChallengeSent) {
+ CreateCastSocketSecure();
+ socket_->SetupMockTransport();
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::OK);
+ EXPECT_CALL(*socket_->GetMockTransport(),
+ SendMessage(EqualsProto(CreateAuthChallenge()), _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::ERR_CONNECTION_RESET));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ socket_.reset();
+ RunPendingTasks();
+}
+
+// Test connection error - challenge reply receive fails
+TEST_F(MockCastSocketTest, TestConnectChallengeReplyReceiveError) {
+ CreateCastSocketSecure();
+ socket_->SetupMockTransport();
+
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::OK);
+ EXPECT_CALL(*socket_->GetMockTransport(),
+ SendMessage(EqualsProto(CreateAuthChallenge()), _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK));
+ socket_->AddReadResult(net::SYNCHRONOUS, net::ERR_FAILED);
+ EXPECT_CALL(*observer_, OnError(_, ChannelError::CAST_SOCKET_ERROR));
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CAST_SOCKET_ERROR));
+ EXPECT_CALL(*socket_->GetMockTransport(), Start());
+ socket_->AddObserver(observer_.get());
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+ socket_->GetMockTransport()->current_delegate()->OnError(
+ ChannelError::CAST_SOCKET_ERROR);
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::CAST_SOCKET_ERROR, socket_->error_state());
+}
+
+TEST_F(MockCastSocketTest, TestConnectChallengeVerificationFails) {
+ CreateCastSocketSecure();
+ socket_->SetupMockTransport();
+ socket_->SetupTcpConnect(net::ASYNC, net::OK);
+ socket_->SetupSslConnect(net::ASYNC, net::OK);
+ socket_->SetVerifyChallengeResult(false);
+
+ EXPECT_CALL(*observer_, OnError(_, ChannelError::AUTHENTICATION_ERROR));
+ CastMessage challenge_proto = CreateAuthChallenge();
+ EXPECT_CALL(*socket_->GetMockTransport(),
+ SendMessage(EqualsProto(challenge_proto), _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK));
+ EXPECT_CALL(handler_,
+ OnConnectComplete(_, ChannelError::AUTHENTICATION_ERROR));
+ EXPECT_CALL(*socket_->GetMockTransport(), Start());
+ socket_->AddObserver(observer_.get());
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+ socket_->GetMockTransport()->current_delegate()->OnMessage(CreateAuthReply());
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::CLOSED, socket_->ready_state());
+ EXPECT_EQ(ChannelError::AUTHENTICATION_ERROR, socket_->error_state());
+}
+
+// Sends message data through an actual non-mocked CastTransport object,
+// testing the two components in integration.
+TEST_F(MockCastSocketTest, TestConnectEndToEndWithRealTransportAsync) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnect(net::ASYNC, net::OK);
+ socket_->SetupSslConnect(net::ASYNC, net::OK);
+
+ // Set low-level auth challenge expectations.
+ CastMessage challenge = CreateAuthChallenge();
+ std::string challenge_str;
+ EXPECT_TRUE(MessageFramer::Serialize(challenge, &challenge_str));
+ socket_->AddWriteResultForData(net::ASYNC, challenge_str);
+
+ // Set low-level auth reply expectations.
+ CastMessage reply = CreateAuthReply();
+ std::string reply_str;
+ EXPECT_TRUE(MessageFramer::Serialize(reply, &reply_str));
+ socket_->AddReadResultForData(net::ASYNC, reply_str);
+ socket_->AddReadResult(net::ASYNC, net::ERR_IO_PENDING);
+
+ CastMessage test_message = CreateTestMessage();
+ std::string test_message_str;
+ EXPECT_TRUE(MessageFramer::Serialize(test_message, &test_message_str));
+ socket_->AddWriteResultForData(net::ASYNC, test_message_str);
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::NONE));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+
+ // Send the test message through a real transport object.
+ EXPECT_CALL(handler_, OnWriteComplete(net::OK));
+ socket_->transport()->SendMessage(
+ test_message, base::Bind(&CompleteHandler::OnWriteComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+}
+
+// Same as TestConnectEndToEndWithRealTransportAsync, except synchronous.
+TEST_F(MockCastSocketTest, TestConnectEndToEndWithRealTransportSync) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnect(net::SYNCHRONOUS, net::OK);
+ socket_->SetupSslConnect(net::SYNCHRONOUS, net::OK);
+
+ // Set low-level auth challenge expectations.
+ CastMessage challenge = CreateAuthChallenge();
+ std::string challenge_str;
+ EXPECT_TRUE(MessageFramer::Serialize(challenge, &challenge_str));
+ socket_->AddWriteResultForData(net::SYNCHRONOUS, challenge_str);
+
+ // Set low-level auth reply expectations.
+ CastMessage reply = CreateAuthReply();
+ std::string reply_str;
+ EXPECT_TRUE(MessageFramer::Serialize(reply, &reply_str));
+ socket_->AddReadResultForData(net::SYNCHRONOUS, reply_str);
+ socket_->AddReadResult(net::ASYNC, net::ERR_IO_PENDING);
+
+ CastMessage test_message = CreateTestMessage();
+ std::string test_message_str;
+ EXPECT_TRUE(MessageFramer::Serialize(test_message, &test_message_str));
+ socket_->AddWriteResultForData(net::SYNCHRONOUS, test_message_str);
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::NONE));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+
+ // Send the test message through a real transport object.
+ EXPECT_CALL(handler_, OnWriteComplete(net::OK));
+ socket_->transport()->SendMessage(
+ test_message, base::Bind(&CompleteHandler::OnWriteComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+}
+
+TEST_F(MockCastSocketTest, TestObservers) {
+ CreateCastSocketSecure();
+ // Test AddObserever
+ MockCastSocketObserver observer1;
+ MockCastSocketObserver observer2;
+ socket_->AddObserver(&observer1);
+ socket_->AddObserver(&observer1);
+ socket_->AddObserver(&observer2);
+ socket_->AddObserver(&observer2);
+
+ // Test notify observers
+ EXPECT_CALL(observer1, OnError(_, cast_channel::ChannelError::CONNECT_ERROR));
+ EXPECT_CALL(observer2, OnError(_, cast_channel::ChannelError::CONNECT_ERROR));
+ CastSocketImpl::CastSocketMessageDelegate delegate(socket_.get());
+ delegate.OnError(cast_channel::ChannelError::CONNECT_ERROR);
+}
+
+TEST_F(MockCastSocketTest, TestOpenChannelConnectingSocket) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnectUnresponsive();
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_TIMEOUT))
+ .Times(2);
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ socket_->TriggerTimeout();
+ RunPendingTasks();
+}
+
+TEST_F(MockCastSocketTest, TestOpenChannelConnectedSocket) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnect(net::ASYNC, net::OK);
+ socket_->SetupSslConnect(net::ASYNC, net::OK);
+
+ HandleAuthHandshake();
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::NONE));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+}
+
+TEST_F(MockCastSocketTest, TestOpenChannelClosedSocket) {
+ CreateCastSocketSecure();
+ socket_->SetupTcpConnect(net::ASYNC, net::ERR_FAILED);
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::CONNECT_ERROR));
+ socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
+ base::Unretained(&handler_)));
+}
+
+// Tests connecting through an actual non-mocked CastTransport object and
+// non-mocked SSLClientSocket, testing the components in integration.
+TEST_F(SslCastSocketTest, TestConnectEndToEndWithRealSSL) {
+ CreateSockets();
+ ConnectSockets();
+
+ // Set low-level auth challenge expectations.
+ CastMessage challenge = CreateAuthChallenge();
+ std::string challenge_str;
+ EXPECT_TRUE(MessageFramer::Serialize(challenge, &challenge_str));
+
+ int challenge_buffer_length = challenge_str.size();
+ scoped_refptr<net::IOBuffer> challenge_buffer(
+ new net::IOBuffer(challenge_buffer_length));
+ int read = ReadExactLength(challenge_buffer.get(), challenge_buffer_length,
+ server_socket_.get());
+
+ EXPECT_EQ(challenge_buffer_length, read);
+ EXPECT_EQ(challenge_str,
+ std::string(challenge_buffer->data(), challenge_buffer_length));
+
+ // Set low-level auth reply expectations.
+ CastMessage reply = CreateAuthReply();
+ std::string reply_str;
+ EXPECT_TRUE(MessageFramer::Serialize(reply, &reply_str));
+
+ scoped_refptr<net::StringIOBuffer> reply_buffer(
+ new net::StringIOBuffer(reply_str));
+ int written = WriteExactLength(reply_buffer.get(), reply_buffer->size(),
+ server_socket_.get());
+
+ EXPECT_EQ(reply_buffer->size(), written);
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::NONE));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+}
+
+// Sends message data through an actual non-mocked CastTransport object and
+// non-mocked SSLClientSocket, testing the components in integration.
+TEST_F(SslCastSocketTest, TestMessageEndToEndWithRealSSL) {
+ CreateSockets();
+ ConnectSockets();
+
+ // Set low-level auth challenge expectations.
+ CastMessage challenge = CreateAuthChallenge();
+ std::string challenge_str;
+ EXPECT_TRUE(MessageFramer::Serialize(challenge, &challenge_str));
+
+ int challenge_buffer_length = challenge_str.size();
+ scoped_refptr<net::IOBuffer> challenge_buffer(
+ new net::IOBuffer(challenge_buffer_length));
+
+ int read = ReadExactLength(challenge_buffer.get(), challenge_buffer_length,
+ server_socket_.get());
+
+ EXPECT_EQ(challenge_buffer_length, read);
+ EXPECT_EQ(challenge_str,
+ std::string(challenge_buffer->data(), challenge_buffer_length));
+
+ // Set low-level auth reply expectations.
+ CastMessage reply = CreateAuthReply();
+ std::string reply_str;
+ EXPECT_TRUE(MessageFramer::Serialize(reply, &reply_str));
+
+ scoped_refptr<net::StringIOBuffer> reply_buffer(
+ new net::StringIOBuffer(reply_str));
+ int written = WriteExactLength(reply_buffer.get(), reply_buffer->size(),
+ server_socket_.get());
+
+ EXPECT_EQ(reply_buffer->size(), written);
+ EXPECT_CALL(handler_, OnConnectComplete(_, ChannelError::NONE));
+ RunPendingTasks();
+
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+
+ // Send a test message through the ssl socket.
+ CastMessage test_message = CreateTestMessage();
+ std::string test_message_str;
+ EXPECT_TRUE(MessageFramer::Serialize(test_message, &test_message_str));
+
+ int test_message_length = test_message_str.size();
+ scoped_refptr<net::IOBuffer> test_message_buffer(
+ new net::IOBuffer(test_message_length));
+
+ EXPECT_CALL(handler_, OnWriteComplete(net::OK));
+ socket_->transport()->SendMessage(
+ test_message, base::Bind(&CompleteHandler::OnWriteComplete,
+ base::Unretained(&handler_)));
+ RunPendingTasks();
+
+ read = ReadExactLength(test_message_buffer.get(), test_message_length,
+ server_socket_.get());
+
+ EXPECT_EQ(test_message_length, read);
+ EXPECT_EQ(test_message_str,
+ std::string(test_message_buffer->data(), test_message_length));
+
+ EXPECT_EQ(ReadyState::OPEN, socket_->ready_state());
+ EXPECT_EQ(ChannelError::NONE, socket_->error_state());
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_test_util.cc b/chromium/components/cast_channel/cast_test_util.cc
new file mode 100644
index 00000000000..ab6ce72bb59
--- /dev/null
+++ b/chromium/components/cast_channel/cast_test_util.cc
@@ -0,0 +1,47 @@
+// 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/cast_channel/cast_test_util.h"
+
+#include <utility>
+
+#include "net/base/ip_address.h"
+
+namespace cast_channel {
+
+MockCastTransport::MockCastTransport() {}
+MockCastTransport::~MockCastTransport() {}
+
+CastTransport::Delegate* MockCastTransport::current_delegate() const {
+ CHECK(delegate_);
+ return delegate_.get();
+}
+
+void MockCastTransport::SetReadDelegate(
+ std::unique_ptr<CastTransport::Delegate> delegate) {
+ delegate_ = std::move(delegate);
+}
+
+MockCastTransportDelegate::MockCastTransportDelegate() {}
+MockCastTransportDelegate::~MockCastTransportDelegate() {}
+
+MockCastSocketObserver::MockCastSocketObserver() {}
+MockCastSocketObserver::~MockCastSocketObserver() {}
+
+MockCastSocketService::MockCastSocketService() {}
+MockCastSocketService::~MockCastSocketService() {}
+
+MockCastSocket::MockCastSocket()
+ : channel_id_(0),
+ error_state_(ChannelError::NONE),
+ keep_alive_(false),
+ audio_only_(false),
+ mock_transport_(new MockCastTransport()) {}
+MockCastSocket::~MockCastSocket() {}
+
+net::IPEndPoint CreateIPEndPointForTest() {
+ return net::IPEndPoint(net::IPAddress(192, 168, 1, 1), 8009);
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_test_util.h b/chromium/components/cast_channel/cast_test_util.h
new file mode 100644
index 00000000000..fbf0cc7ff76
--- /dev/null
+++ b/chromium/components/cast_channel/cast_test_util.h
@@ -0,0 +1,170 @@
+// 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_CAST_CHANNEL_CAST_TEST_UTIL_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_TEST_UTIL_H_
+
+#include <string>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/cast_socket_service.h"
+#include "components/cast_channel/cast_transport.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "net/base/ip_endpoint.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cast_channel {
+
+class MockCastTransport : public CastTransport {
+ public:
+ MockCastTransport();
+ ~MockCastTransport() override;
+
+ void SetReadDelegate(
+ std::unique_ptr<CastTransport::Delegate> delegate) override;
+
+ MOCK_METHOD2(SendMessage,
+ void(const CastMessage& message,
+ const net::CompletionCallback& callback));
+
+ MOCK_METHOD0(Start, void(void));
+
+ // Gets the read delegate that is currently active for this transport.
+ CastTransport::Delegate* current_delegate() const;
+
+ private:
+ std::unique_ptr<CastTransport::Delegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCastTransport);
+};
+
+class MockCastTransportDelegate : public CastTransport::Delegate {
+ public:
+ MockCastTransportDelegate();
+ ~MockCastTransportDelegate() override;
+
+ MOCK_METHOD1(OnError, void(ChannelError error));
+ MOCK_METHOD1(OnMessage, void(const CastMessage& message));
+ MOCK_METHOD0(Start, void(void));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockCastTransportDelegate);
+};
+
+class MockCastSocketObserver : public CastSocket::Observer {
+ public:
+ MockCastSocketObserver();
+ ~MockCastSocketObserver() override;
+
+ MOCK_METHOD2(OnError, void(const CastSocket& socket, ChannelError error));
+ MOCK_METHOD2(OnMessage,
+ void(const CastSocket& socket, const CastMessage& message));
+};
+
+class MockCastSocketService : public CastSocketService {
+ public:
+ MockCastSocketService();
+ ~MockCastSocketService() override;
+
+ int OpenSocket(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ CastSocket::OnOpenCallback open_cb,
+ CastSocket::Observer* observer) override {
+ // Unit test should not call |open_cb| more than once. Just use
+ // base::AdaptCallbackForRepeating to pass |open_cb| to a mock method.
+ return OpenSocketInternal(
+ ip_endpoint, net_log,
+ base::AdaptCallbackForRepeating(std::move(open_cb)), observer);
+ }
+
+ MOCK_METHOD4(OpenSocketInternal,
+ int(const net::IPEndPoint& ip_endpoint,
+ net::NetLog* net_log,
+ const base::Callback<void(int, ChannelError)>& open_cb,
+ CastSocket::Observer* observer));
+ MOCK_CONST_METHOD1(GetSocket, CastSocket*(int channel_id));
+};
+
+class MockCastSocket : public CastSocket {
+ public:
+ using MockOnOpenCallback =
+ base::Callback<void(int channel_id, ChannelError error_state)>;
+
+ MockCastSocket();
+ ~MockCastSocket() override;
+
+ void Connect(CastSocket::OnOpenCallback callback) override {
+ // Unit test should not call |open_cb| more than once. Just use
+ // base::AdaptCallbackForRepeating to pass |open_cb| to a mock method.
+ ConnectInternal(base::AdaptCallbackForRepeating(std::move(callback)));
+ }
+
+ MOCK_METHOD1(ConnectInternal, void(const MockOnOpenCallback& callback));
+ MOCK_METHOD1(Close, void(const net::CompletionCallback& callback));
+ MOCK_CONST_METHOD0(ready_state, ReadyState());
+ MOCK_METHOD1(AddObserver, void(Observer* observer));
+ MOCK_METHOD1(RemoveObserver, void(Observer* observer));
+
+ const net::IPEndPoint& ip_endpoint() const override { return ip_endpoint_; }
+ void SetIPEndpoint(const net::IPEndPoint& ip_endpoint) {
+ ip_endpoint_ = ip_endpoint;
+ }
+
+ int id() const override { return channel_id_; }
+ void set_id(int id) override { channel_id_ = id; }
+
+ ChannelError error_state() const override { return error_state_; }
+ void SetErrorState(ChannelError error_state) override {
+ error_state_ = error_state;
+ }
+
+ bool keep_alive() const override { return keep_alive_; }
+ void SetKeepAlive(bool keep_alive) { keep_alive_ = keep_alive; }
+
+ bool audio_only() const override { return audio_only_; }
+ void SetAudioOnly(bool audio_only) { audio_only_ = audio_only; }
+
+ CastTransport* transport() const override { return mock_transport_.get(); }
+ MockCastTransport* mock_transport() const { return mock_transport_.get(); }
+
+ private:
+ net::IPEndPoint ip_endpoint_;
+ int channel_id_;
+ ChannelError error_state_;
+ bool keep_alive_;
+ bool audio_only_;
+
+ std::unique_ptr<MockCastTransport> mock_transport_;
+ std::unique_ptr<Observer> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCastSocket);
+};
+
+// Creates the IPEndpoint 192.168.1.1.
+net::IPEndPoint CreateIPEndPointForTest();
+
+// Checks if two proto messages are the same.
+// From
+// third_party/cacheinvalidation/overrides/google/cacheinvalidation/deps/gmock.h
+// TODO(kmarshall): promote to a shared testing library.
+MATCHER_P(EqualsProto, message, "") {
+ std::string expected_serialized, actual_serialized;
+ message.SerializeToString(&expected_serialized);
+ arg.SerializeToString(&actual_serialized);
+ return expected_serialized == actual_serialized;
+}
+
+ACTION_TEMPLATE(PostCompletionCallbackTask,
+ HAS_1_TEMPLATE_PARAMS(int, cb_idx),
+ AND_1_VALUE_PARAMS(rv)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(testing::get<cb_idx>(args), rv));
+}
+
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_CAST_TEST_UTIL_H_
diff --git a/chromium/components/cast_channel/cast_transport.cc b/chromium/components/cast_channel/cast_transport.cc
new file mode 100644
index 00000000000..ad71aaaa859
--- /dev/null
+++ b/chromium/components/cast_channel/cast_transport.cc
@@ -0,0 +1,376 @@
+// 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/cast_channel/cast_transport.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/format_macros.h"
+#include "base/location.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/cast_channel/cast_framer.h"
+#include "components/cast_channel/cast_message_util.h"
+#include "components/cast_channel/logger.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "net/base/net_errors.h"
+#include "net/socket/socket.h"
+
+#define VLOG_WITH_CONNECTION(level) \
+ VLOG(level) << "[" << ip_endpoint_.ToString() << ", auth=SSL_VERIFIED] "
+
+namespace cast_channel {
+
+CastTransportImpl::CastTransportImpl(net::Socket* socket,
+ int channel_id,
+ const net::IPEndPoint& ip_endpoint,
+ scoped_refptr<Logger> logger)
+ : started_(false),
+ socket_(socket),
+ write_state_(WriteState::IDLE),
+ read_state_(ReadState::READ),
+ error_state_(ChannelError::NONE),
+ channel_id_(channel_id),
+ ip_endpoint_(ip_endpoint),
+ logger_(logger) {
+ DCHECK(socket);
+
+ // Buffer is reused across messages to minimize unnecessary buffer
+ // [re]allocations.
+ read_buffer_ = new net::GrowableIOBuffer();
+ read_buffer_->SetCapacity(MessageFramer::MessageHeader::max_message_size());
+ framer_.reset(new MessageFramer(read_buffer_));
+}
+
+CastTransportImpl::~CastTransportImpl() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ FlushWriteQueue();
+}
+
+bool CastTransportImpl::IsTerminalWriteState(WriteState write_state) {
+ return write_state == WriteState::WRITE_ERROR ||
+ write_state == WriteState::IDLE;
+}
+
+bool CastTransportImpl::IsTerminalReadState(ReadState read_state) {
+ return read_state == ReadState::READ_ERROR;
+}
+
+
+void CastTransportImpl::SetReadDelegate(std::unique_ptr<Delegate> delegate) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(delegate);
+ delegate_ = std::move(delegate);
+ if (started_) {
+ delegate_->Start();
+ }
+}
+
+void CastTransportImpl::FlushWriteQueue() {
+ for (; !write_queue_.empty(); write_queue_.pop()) {
+ net::CompletionCallback& callback = write_queue_.front().callback;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, net::ERR_FAILED));
+ callback.Reset();
+ }
+}
+
+void CastTransportImpl::SendMessage(const CastMessage& message,
+ const net::CompletionCallback& callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ std::string serialized_message;
+ if (!MessageFramer::Serialize(message, &serialized_message)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, net::ERR_FAILED));
+ return;
+ }
+ WriteRequest write_request(message.namespace_(), serialized_message,
+ callback);
+
+ write_queue_.push(write_request);
+ if (write_state_ == WriteState::IDLE) {
+ SetWriteState(WriteState::WRITE);
+ OnWriteResult(net::OK);
+ }
+}
+
+CastTransportImpl::WriteRequest::WriteRequest(
+ const std::string& namespace_,
+ const std::string& payload,
+ const net::CompletionCallback& callback)
+ : message_namespace(namespace_), callback(callback) {
+ VLOG(2) << "WriteRequest size: " << payload.size();
+ io_buffer = new net::DrainableIOBuffer(new net::StringIOBuffer(payload),
+ payload.size());
+}
+
+CastTransportImpl::WriteRequest::WriteRequest(const WriteRequest& other) =
+ default;
+
+CastTransportImpl::WriteRequest::~WriteRequest() {}
+
+void CastTransportImpl::SetReadState(ReadState read_state) {
+ if (read_state_ != read_state)
+ read_state_ = read_state;
+}
+
+void CastTransportImpl::SetWriteState(WriteState write_state) {
+ if (write_state_ != write_state)
+ write_state_ = write_state;
+}
+
+void CastTransportImpl::SetErrorState(ChannelError error_state) {
+ VLOG_WITH_CONNECTION(2) << "SetErrorState: "
+ << ::cast_channel::ChannelErrorToString(error_state);
+ error_state_ = error_state;
+}
+
+void CastTransportImpl::OnWriteResult(int result) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_NE(WriteState::IDLE, write_state_);
+ if (write_queue_.empty()) {
+ SetWriteState(WriteState::IDLE);
+ return;
+ }
+
+ // Network operations can either finish synchronously or asynchronously.
+ // This method executes the state machine transitions in a loop so that
+ // write state transitions happen even when network operations finish
+ // synchronously.
+ int rv = result;
+ do {
+ VLOG_WITH_CONNECTION(2)
+ << "OnWriteResult (state=" << AsInteger(write_state_) << ", "
+ << "result=" << rv << ", "
+ << "queue size=" << write_queue_.size() << ")";
+
+ WriteState state = write_state_;
+ write_state_ = WriteState::UNKNOWN;
+ switch (state) {
+ case WriteState::WRITE:
+ rv = DoWrite();
+ break;
+ case WriteState::WRITE_COMPLETE:
+ rv = DoWriteComplete(rv);
+ break;
+ case WriteState::DO_CALLBACK:
+ rv = DoWriteCallback();
+ break;
+ case WriteState::HANDLE_ERROR:
+ rv = DoWriteHandleError(rv);
+ DCHECK_EQ(WriteState::WRITE_ERROR, write_state_);
+ break;
+ default:
+ NOTREACHED() << "Unknown state in write state machine: "
+ << AsInteger(state);
+ SetWriteState(WriteState::WRITE_ERROR);
+ SetErrorState(ChannelError::UNKNOWN);
+ rv = net::ERR_FAILED;
+ break;
+ }
+ } while (rv != net::ERR_IO_PENDING && !IsTerminalWriteState(write_state_));
+
+ if (write_state_ == WriteState::WRITE_ERROR) {
+ FlushWriteQueue();
+ DCHECK_NE(ChannelError::NONE, error_state_);
+ VLOG_WITH_CONNECTION(2) << "Sending OnError().";
+ delegate_->OnError(error_state_);
+ }
+}
+
+int CastTransportImpl::DoWrite() {
+ DCHECK(!write_queue_.empty());
+ WriteRequest& request = write_queue_.front();
+
+ VLOG_WITH_CONNECTION(2) << "WriteData byte_count = "
+ << request.io_buffer->size() << " bytes_written "
+ << request.io_buffer->BytesConsumed();
+
+ SetWriteState(WriteState::WRITE_COMPLETE);
+
+ int rv = socket_->Write(
+ request.io_buffer.get(), request.io_buffer->BytesRemaining(),
+ base::Bind(&CastTransportImpl::OnWriteResult, base::Unretained(this)));
+ return rv;
+}
+
+int CastTransportImpl::DoWriteComplete(int result) {
+ VLOG_WITH_CONNECTION(2) << "DoWriteComplete result=" << result;
+ DCHECK(!write_queue_.empty());
+ if (result <= 0) { // NOTE that 0 also indicates an error
+ logger_->LogSocketEventWithRv(channel_id_, ChannelEvent::SOCKET_WRITE,
+ result);
+ SetErrorState(ChannelError::CAST_SOCKET_ERROR);
+ SetWriteState(WriteState::HANDLE_ERROR);
+ return result == 0 ? net::ERR_FAILED : result;
+ }
+
+ // Some bytes were successfully written
+ WriteRequest& request = write_queue_.front();
+ scoped_refptr<net::DrainableIOBuffer> io_buffer = request.io_buffer;
+ io_buffer->DidConsume(result);
+ if (io_buffer->BytesRemaining() == 0) { // Message fully sent
+ SetWriteState(WriteState::DO_CALLBACK);
+ } else {
+ SetWriteState(WriteState::WRITE);
+ }
+
+ return net::OK;
+}
+
+int CastTransportImpl::DoWriteCallback() {
+ VLOG_WITH_CONNECTION(2) << "DoWriteCallback";
+ DCHECK(!write_queue_.empty());
+
+ WriteRequest& request = write_queue_.front();
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(request.callback, net::OK));
+
+ write_queue_.pop();
+ if (write_queue_.empty()) {
+ SetWriteState(WriteState::IDLE);
+ } else {
+ SetWriteState(WriteState::WRITE);
+ }
+
+ return net::OK;
+}
+
+int CastTransportImpl::DoWriteHandleError(int result) {
+ VLOG_WITH_CONNECTION(2) << "DoWriteHandleError result=" << result;
+ DCHECK_NE(ChannelError::NONE, error_state_);
+ DCHECK_LT(result, 0);
+ SetWriteState(WriteState::WRITE_ERROR);
+ return net::ERR_FAILED;
+}
+
+void CastTransportImpl::Start() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!started_);
+ DCHECK_EQ(ReadState::READ, read_state_);
+ DCHECK(delegate_) << "Read delegate must be set prior to calling Start()";
+ started_ = true;
+ delegate_->Start();
+ SetReadState(ReadState::READ);
+
+ // Start the read state machine.
+ OnReadResult(net::OK);
+}
+
+void CastTransportImpl::OnReadResult(int result) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Network operations can either finish synchronously or asynchronously.
+ // This method executes the state machine transitions in a loop so that
+ // write state transitions happen even when network operations finish
+ // synchronously.
+ int rv = result;
+ do {
+ VLOG_WITH_CONNECTION(2) << "OnReadResult(state=" << AsInteger(read_state_)
+ << ", result=" << rv << ")";
+ ReadState state = read_state_;
+ read_state_ = ReadState::UNKNOWN;
+
+ switch (state) {
+ case ReadState::READ:
+ rv = DoRead();
+ break;
+ case ReadState::READ_COMPLETE:
+ rv = DoReadComplete(rv);
+ break;
+ case ReadState::DO_CALLBACK:
+ rv = DoReadCallback();
+ break;
+ case ReadState::HANDLE_ERROR:
+ rv = DoReadHandleError(rv);
+ DCHECK_EQ(read_state_, ReadState::READ_ERROR);
+ break;
+ default:
+ NOTREACHED() << "Unknown state in read state machine: "
+ << AsInteger(state);
+ SetReadState(ReadState::READ_ERROR);
+ SetErrorState(ChannelError::UNKNOWN);
+ rv = net::ERR_FAILED;
+ break;
+ }
+ } while (rv != net::ERR_IO_PENDING && !IsTerminalReadState(read_state_));
+
+ if (IsTerminalReadState(read_state_)) {
+ DCHECK_EQ(ReadState::READ_ERROR, read_state_);
+ VLOG_WITH_CONNECTION(2) << "Sending OnError().";
+ delegate_->OnError(error_state_);
+ }
+}
+
+int CastTransportImpl::DoRead() {
+ VLOG_WITH_CONNECTION(2) << "DoRead";
+ SetReadState(ReadState::READ_COMPLETE);
+
+ // Determine how many bytes need to be read.
+ size_t num_bytes_to_read = framer_->BytesRequested();
+ DCHECK_GT(num_bytes_to_read, 0u);
+
+ // Read up to num_bytes_to_read into |current_read_buffer_|.
+ return socket_->Read(
+ read_buffer_.get(), base::checked_cast<uint32_t>(num_bytes_to_read),
+ base::Bind(&CastTransportImpl::OnReadResult, base::Unretained(this)));
+}
+
+int CastTransportImpl::DoReadComplete(int result) {
+ VLOG_WITH_CONNECTION(2) << "DoReadComplete result = " << result;
+ if (result <= 0) {
+ logger_->LogSocketEventWithRv(channel_id_, ChannelEvent::SOCKET_READ,
+ result);
+ VLOG_WITH_CONNECTION(1) << "Read error, peer closed the socket.";
+ SetErrorState(ChannelError::CAST_SOCKET_ERROR);
+ SetReadState(ReadState::HANDLE_ERROR);
+ return result == 0 ? net::ERR_FAILED : result;
+ }
+
+ size_t message_size;
+ DCHECK(!current_message_);
+ ChannelError framing_error;
+ current_message_ = framer_->Ingest(result, &message_size, &framing_error);
+ if (current_message_.get() && (framing_error == ChannelError::NONE)) {
+ DCHECK_GT(message_size, static_cast<size_t>(0));
+ SetReadState(ReadState::DO_CALLBACK);
+ } else if (framing_error != ChannelError::NONE) {
+ DCHECK(!current_message_);
+ SetErrorState(ChannelError::INVALID_MESSAGE);
+ SetReadState(ReadState::HANDLE_ERROR);
+ } else {
+ DCHECK(!current_message_);
+ SetReadState(ReadState::READ);
+ }
+ return net::OK;
+}
+
+int CastTransportImpl::DoReadCallback() {
+ VLOG_WITH_CONNECTION(2) << "DoReadCallback";
+ if (!IsCastMessageValid(*current_message_)) {
+ SetReadState(ReadState::HANDLE_ERROR);
+ SetErrorState(ChannelError::INVALID_MESSAGE);
+ return net::ERR_INVALID_RESPONSE;
+ }
+ SetReadState(ReadState::READ);
+ delegate_->OnMessage(*current_message_);
+ current_message_.reset();
+ return net::OK;
+}
+
+int CastTransportImpl::DoReadHandleError(int result) {
+ VLOG_WITH_CONNECTION(2) << "DoReadHandleError";
+ DCHECK_NE(ChannelError::NONE, error_state_);
+ DCHECK_LE(result, 0);
+ SetReadState(ReadState::READ_ERROR);
+ return net::ERR_FAILED;
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/cast_transport.h b/chromium/components/cast_channel/cast_transport.h
new file mode 100644
index 00000000000..efb12ad9da2
--- /dev/null
+++ b/chromium/components/cast_channel/cast_transport.h
@@ -0,0 +1,194 @@
+// 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_CAST_CHANNEL_CAST_TRANSPORT_H_
+#define COMPONENTS_CAST_CHANNEL_CAST_TRANSPORT_H_
+
+#include <queue>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+#include "base/threading/thread_checker.h"
+#include "components/cast_channel/cast_channel_enum.h"
+#include "components/cast_channel/logger.h"
+#include "net/base/completion_callback.h"
+#include "net/base/ip_endpoint.h"
+
+namespace net {
+class DrainableIOBuffer;
+class DrainableIOBuffer;
+class GrowableIOBuffer;
+class Socket;
+} // namespace net
+
+namespace cast_channel {
+class CastMessage;
+class MessageFramer;
+
+class CastTransport {
+ public:
+ virtual ~CastTransport() {}
+
+ // Object to be informed of incoming messages and read errors.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called once Transport is successfully initialized and started.
+ // Owned read delegates are Start()ed automatically.
+ virtual void Start() = 0;
+
+ // An error occurred on the channel.
+ // The caller is responsible for closing |socket| if an error occurred.
+ virtual void OnError(ChannelError error_state) = 0;
+
+ // A message was received on the channel.
+ virtual void OnMessage(const CastMessage& message) = 0;
+ };
+
+ // Sends a CastMessage to |socket_|.
+ // |message|: The message to send.
+ // |callback|: Callback to be invoked when the write operation has finished.
+ // Virtual for testing.
+ virtual void SendMessage(const CastMessage& message,
+ const net::CompletionCallback& callback) = 0;
+
+ // Initializes the reading state machine and starts reading from the
+ // underlying socket.
+ // Virtual for testing.
+ virtual void Start() = 0;
+
+ // Changes the delegate for processing read events. Pending reads remain
+ // in-flight.
+ // Ownership of the pointee of |delegate| is assumed by the transport.
+ // Prior delegates are deleted automatically.
+ virtual void SetReadDelegate(std::unique_ptr<Delegate> delegate) = 0;
+};
+
+// Manager class for reading and writing messages to/from a socket.
+class CastTransportImpl : public CastTransport {
+ public:
+ using ChannelError = ::cast_channel::ChannelError;
+
+ // Adds a CastMessage read/write layer to a socket.
+ // Message read events are propagated to the owner via |read_delegate|.
+ // |vlog_prefix| sets the prefix used for all VLOGged output.
+ // |socket| and |logger| must all out-live the
+ // CastTransportImpl instance.
+ // |read_delegate| is owned by this CastTransportImpl object.
+ CastTransportImpl(net::Socket* socket,
+ int channel_id,
+ const net::IPEndPoint& ip_endpoint_,
+ scoped_refptr<Logger> logger);
+
+ ~CastTransportImpl() override;
+
+ // CastTransport interface.
+ void SendMessage(const CastMessage& message,
+ const net::CompletionCallback& callback) override;
+ void Start() override;
+ void SetReadDelegate(std::unique_ptr<Delegate> delegate) override;
+
+ private:
+ // Holds a message to be written to the socket. |callback| is invoked when the
+ // message is fully written or an error occurrs.
+ struct WriteRequest {
+ explicit WriteRequest(const std::string& namespace_,
+ const std::string& payload,
+ const net::CompletionCallback& callback);
+ WriteRequest(const WriteRequest& other);
+ ~WriteRequest();
+
+ // Namespace of the serialized message.
+ std::string message_namespace;
+ // Write completion callback, invoked when the operation has completed or
+ // failed.
+ net::CompletionCallback callback;
+ // Buffer with outgoing data.
+ scoped_refptr<net::DrainableIOBuffer> io_buffer;
+ };
+
+ static bool IsTerminalReadState(ReadState read_state);
+ static bool IsTerminalWriteState(WriteState write_state);
+
+ void SetReadState(ReadState read_state);
+ void SetWriteState(WriteState write_state);
+ void SetErrorState(ChannelError error_state);
+
+ // Terminates all in-flight write callbacks with error code ERR_FAILED.
+ void FlushWriteQueue();
+
+ // Main method that performs write flow state transitions.
+ void OnWriteResult(int result);
+
+ // Each of the below Do* method is executed in the corresponding
+ // write state. For example when write state is WRITE_STATE_WRITE_COMPLETE
+ // DowriteComplete is called, and so on.
+ int DoWrite();
+ int DoWriteComplete(int result);
+ int DoWriteCallback();
+ int DoWriteHandleError(int result);
+
+ // Main method that performs write flow state transitions.
+ void OnReadResult(int result);
+
+ // Each of the below Do* method is executed in the corresponding
+ // write state. For example when read state is READ_STATE_READ_COMPLETE
+ // DoReadComplete is called, and so on.
+ int DoRead();
+ int DoReadComplete(int result);
+ int DoReadCallback();
+ int DoReadHandleError(int result);
+
+ // Indicates that the transport object is started and may receive and send
+ // messages.
+ bool started_;
+
+ // Queue of pending writes. The message at the front of the queue is the one
+ // being written.
+ std::queue<WriteRequest> write_queue_;
+
+ // Buffer used for read operations. Reused for every read.
+ scoped_refptr<net::GrowableIOBuffer> read_buffer_;
+
+ // Constructs and parses the wire representation of message frames.
+ std::unique_ptr<MessageFramer> framer_;
+
+ // Last message received on the socket.
+ std::unique_ptr<CastMessage> current_message_;
+
+ // Socket used for I/O operations.
+ net::Socket* const socket_;
+
+ // Methods for communicating message receipt and error status to client code.
+ std::unique_ptr<Delegate> delegate_;
+
+ // Write flow state machine state.
+ WriteState write_state_;
+
+ // Read flow state machine state.
+ ReadState read_state_;
+
+ // The last error encountered by the channel.
+ ChannelError error_state_;
+
+ // Connection metadata for logging purposes.
+ // Socket ID assigned by ApiResourceManager.
+ int channel_id_;
+
+ // IP address of the remote end.
+ const net::IPEndPoint ip_endpoint_;
+
+ // Accumulates details of events and errors, for debugging purposes.
+ scoped_refptr<Logger> logger_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(CastTransportImpl);
+};
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_CAST_TRANSPORT_H_
diff --git a/chromium/components/cast_channel/cast_transport_unittest.cc b/chromium/components/cast_channel/cast_transport_unittest.cc
new file mode 100644
index 00000000000..8f72615e2b5
--- /dev/null
+++ b/chromium/components/cast_channel/cast_transport_unittest.cc
@@ -0,0 +1,682 @@
+// 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/cast_channel/cast_transport.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <queue>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/simple_test_clock.h"
+#include "components/cast_channel/cast_framer.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "components/cast_channel/logger.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "net/log/test_net_log.h"
+#include "net/socket/socket.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::InSequence;
+using testing::Invoke;
+using testing::NotNull;
+using testing::Return;
+using testing::WithArg;
+
+namespace cast_channel {
+namespace {
+
+const int kChannelId = 0;
+
+// Mockable placeholder for write completion events.
+class CompleteHandler {
+ public:
+ CompleteHandler() {}
+ MOCK_METHOD1(Complete, void(int result));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompleteHandler);
+};
+
+// Creates a CastMessage proto with the bare minimum required fields set.
+CastMessage CreateCastMessage() {
+ CastMessage output;
+ output.set_protocol_version(CastMessage::CASTV2_1_0);
+ output.set_namespace_("x");
+ output.set_source_id("source");
+ output.set_destination_id("destination");
+ output.set_payload_type(CastMessage::STRING);
+ output.set_payload_utf8("payload");
+ return output;
+}
+
+// FIFO queue of completion callbacks. Outstanding write operations are
+// Push()ed into the queue. Callback completion is simulated by invoking
+// Pop() in the same order as Push().
+class CompletionQueue {
+ public:
+ CompletionQueue() {}
+ ~CompletionQueue() { CHECK_EQ(0u, cb_queue_.size()); }
+
+ // Enqueues a pending completion callback.
+ void Push(const net::CompletionCallback& cb) { cb_queue_.push(cb); }
+ // Runs the next callback and removes it from the queue.
+ void Pop(int rv) {
+ CHECK_GT(cb_queue_.size(), 0u);
+ cb_queue_.front().Run(rv);
+ cb_queue_.pop();
+ }
+
+ private:
+ std::queue<net::CompletionCallback> cb_queue_;
+ DISALLOW_COPY_AND_ASSIGN(CompletionQueue);
+};
+
+// GMock action that reads data from an IOBuffer and writes it to a string
+// variable.
+//
+// buf_idx (template parameter 0): 0-based index of the net::IOBuffer
+// in the function mock arg list.
+// size_idx (template parameter 1): 0-based index of the byte count arg.
+// str: pointer to the string which will receive data from the buffer.
+ACTION_TEMPLATE(ReadBufferToString,
+ HAS_2_TEMPLATE_PARAMS(int, buf_idx, int, size_idx),
+ AND_1_VALUE_PARAMS(str)) {
+ str->assign(testing::get<buf_idx>(args)->data(),
+ testing::get<size_idx>(args));
+}
+
+// GMock action that writes data from a string to an IOBuffer.
+//
+// buf_idx (template parameter 0): 0-based index of the IOBuffer arg.
+// str: the string containing data to be written to the IOBuffer.
+ACTION_TEMPLATE(FillBufferFromString,
+ HAS_1_TEMPLATE_PARAMS(int, buf_idx),
+ AND_1_VALUE_PARAMS(str)) {
+ memcpy(testing::get<buf_idx>(args)->data(), str.data(), str.size());
+}
+
+// GMock action that enqueues a write completion callback in a queue.
+//
+// buf_idx (template parameter 0): 0-based index of the CompletionCallback.
+// completion_queue: a pointer to the CompletionQueue.
+ACTION_TEMPLATE(EnqueueCallback,
+ HAS_1_TEMPLATE_PARAMS(int, cb_idx),
+ AND_1_VALUE_PARAMS(completion_queue)) {
+ completion_queue->Push(testing::get<cb_idx>(args));
+}
+
+} // namespace
+
+class MockSocket : public net::Socket {
+ public:
+ MOCK_METHOD3(Read,
+ int(net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback));
+
+ MOCK_METHOD3(Write,
+ int(net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback));
+
+ virtual int SetReceiveBufferSize(int32_t size) {
+ NOTREACHED();
+ return 0;
+ }
+
+ virtual int SetSendBufferSize(int32_t size) {
+ NOTREACHED();
+ return 0;
+ }
+};
+
+class CastTransportTest : public testing::Test {
+ public:
+ CastTransportTest() : logger_(new Logger()) {
+ delegate_ = new MockCastTransportDelegate;
+ transport_.reset(new CastTransportImpl(&mock_socket_, kChannelId,
+ CreateIPEndPointForTest(), logger_));
+ transport_->SetReadDelegate(base::WrapUnique(delegate_));
+ }
+ ~CastTransportTest() override {}
+
+ protected:
+ // Runs all pending tasks in the message loop.
+ void RunPendingTasks() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ base::MessageLoop message_loop_;
+ MockCastTransportDelegate* delegate_;
+ MockSocket mock_socket_;
+ Logger* logger_;
+ std::unique_ptr<CastTransport> transport_;
+};
+
+// ----------------------------------------------------------------------------
+// Asynchronous write tests
+TEST_F(CastTransportTest, TestFullWriteAsync) {
+ CompletionQueue socket_cbs;
+ CompleteHandler write_handler;
+ std::string output;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(mock_socket_, Write(NotNull(), serialized_message.size(), _))
+ .WillOnce(DoAll(ReadBufferToString<0, 1>(&output),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)));
+ EXPECT_CALL(write_handler, Complete(net::OK));
+ transport_->SendMessage(
+ message,
+ base::Bind(&CompleteHandler::Complete, base::Unretained(&write_handler)));
+ RunPendingTasks();
+ socket_cbs.Pop(serialized_message.size());
+ RunPendingTasks();
+ EXPECT_EQ(serialized_message, output);
+}
+
+TEST_F(CastTransportTest, TestPartialWritesAsync) {
+ InSequence seq;
+ CompletionQueue socket_cbs;
+ CompleteHandler write_handler;
+ std::string output;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ // Only one byte is written.
+ EXPECT_CALL(mock_socket_,
+ Write(NotNull(), static_cast<int>(serialized_message.size()), _))
+ .WillOnce(DoAll(ReadBufferToString<0, 1>(&output),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)));
+ // Remainder of bytes are written.
+ EXPECT_CALL(
+ mock_socket_,
+ Write(NotNull(), static_cast<int>(serialized_message.size() - 1), _))
+ .WillOnce(DoAll(ReadBufferToString<0, 1>(&output),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)));
+
+ transport_->SendMessage(
+ message,
+ base::Bind(&CompleteHandler::Complete, base::Unretained(&write_handler)));
+ RunPendingTasks();
+ EXPECT_EQ(serialized_message, output);
+ socket_cbs.Pop(1);
+ RunPendingTasks();
+
+ EXPECT_CALL(write_handler, Complete(net::OK));
+ socket_cbs.Pop(serialized_message.size() - 1);
+ RunPendingTasks();
+ EXPECT_EQ(serialized_message.substr(1, serialized_message.size() - 1),
+ output);
+}
+
+TEST_F(CastTransportTest, TestWriteFailureAsync) {
+ CompletionQueue socket_cbs;
+ CompleteHandler write_handler;
+ CastMessage message = CreateCastMessage();
+ EXPECT_CALL(mock_socket_, Write(NotNull(), _, _))
+ .WillOnce(
+ DoAll(EnqueueCallback<2>(&socket_cbs), Return(net::ERR_IO_PENDING)));
+ EXPECT_CALL(write_handler, Complete(net::ERR_FAILED));
+ EXPECT_CALL(*delegate_, OnError(ChannelError::CAST_SOCKET_ERROR));
+ transport_->SendMessage(
+ message,
+ base::Bind(&CompleteHandler::Complete, base::Unretained(&write_handler)));
+ RunPendingTasks();
+ socket_cbs.Pop(net::ERR_CONNECTION_RESET);
+ RunPendingTasks();
+ EXPECT_EQ(ChannelEvent::SOCKET_WRITE,
+ logger_->GetLastError(kChannelId).channel_event);
+ EXPECT_EQ(net::ERR_CONNECTION_RESET,
+ logger_->GetLastError(kChannelId).net_return_value);
+}
+
+// ----------------------------------------------------------------------------
+// Synchronous write tests
+TEST_F(CastTransportTest, TestFullWriteSync) {
+ CompleteHandler write_handler;
+ std::string output;
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+ EXPECT_CALL(mock_socket_, Write(NotNull(), serialized_message.size(), _))
+ .WillOnce(DoAll(ReadBufferToString<0, 1>(&output),
+ Return(serialized_message.size())));
+ EXPECT_CALL(write_handler, Complete(net::OK));
+ transport_->SendMessage(
+ message,
+ base::Bind(&CompleteHandler::Complete, base::Unretained(&write_handler)));
+ RunPendingTasks();
+ EXPECT_EQ(serialized_message, output);
+}
+
+TEST_F(CastTransportTest, TestPartialWritesSync) {
+ InSequence seq;
+ CompleteHandler write_handler;
+ std::string output;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ // Only one byte is written.
+ EXPECT_CALL(mock_socket_, Write(NotNull(), serialized_message.size(), _))
+ .WillOnce(DoAll(ReadBufferToString<0, 1>(&output), Return(1)));
+ // Remainder of bytes are written.
+ EXPECT_CALL(mock_socket_, Write(NotNull(), serialized_message.size() - 1, _))
+ .WillOnce(DoAll(ReadBufferToString<0, 1>(&output),
+ Return(serialized_message.size() - 1)));
+
+ EXPECT_CALL(write_handler, Complete(net::OK));
+ transport_->SendMessage(
+ message,
+ base::Bind(&CompleteHandler::Complete, base::Unretained(&write_handler)));
+ RunPendingTasks();
+ EXPECT_EQ(serialized_message.substr(1, serialized_message.size() - 1),
+ output);
+}
+
+TEST_F(CastTransportTest, TestWriteFailureSync) {
+ CompleteHandler write_handler;
+ CastMessage message = CreateCastMessage();
+ EXPECT_CALL(mock_socket_, Write(NotNull(), _, _))
+ .WillOnce(Return(net::ERR_CONNECTION_RESET));
+ EXPECT_CALL(write_handler, Complete(net::ERR_FAILED));
+ transport_->SendMessage(
+ message,
+ base::Bind(&CompleteHandler::Complete, base::Unretained(&write_handler)));
+ RunPendingTasks();
+ EXPECT_EQ(ChannelEvent::SOCKET_WRITE,
+ logger_->GetLastError(kChannelId).channel_event);
+ EXPECT_EQ(net::ERR_CONNECTION_RESET,
+ logger_->GetLastError(kChannelId).net_return_value);
+}
+
+// ----------------------------------------------------------------------------
+// Asynchronous read tests
+TEST_F(CastTransportTest, TestFullReadAsync) {
+ InSequence s;
+ CompletionQueue socket_cbs;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)));
+
+ // Read bytes [4, n].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size())),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+
+ EXPECT_CALL(*delegate_, OnMessage(EqualsProto(message)));
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(Return(net::ERR_IO_PENDING));
+ transport_->Start();
+ RunPendingTasks();
+ socket_cbs.Pop(MessageFramer::MessageHeader::header_size());
+ socket_cbs.Pop(serialized_message.size() -
+ MessageFramer::MessageHeader::header_size());
+ RunPendingTasks();
+}
+
+TEST_F(CastTransportTest, TestPartialReadAsync) {
+ InSequence s;
+ CompletionQueue socket_cbs;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+ // Read bytes [4, n-1].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1)),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+ // Read final byte.
+ EXPECT_CALL(mock_socket_, Read(NotNull(), 1, _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ serialized_message.size() - 1, 1)),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*delegate_, OnMessage(EqualsProto(message)));
+ transport_->Start();
+ socket_cbs.Pop(MessageFramer::MessageHeader::header_size());
+ socket_cbs.Pop(serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1);
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(Return(net::ERR_IO_PENDING));
+ socket_cbs.Pop(1);
+}
+
+TEST_F(CastTransportTest, TestReadErrorInHeaderAsync) {
+ CompletionQueue socket_cbs;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+
+ EXPECT_CALL(*delegate_, OnError(ChannelError::CAST_SOCKET_ERROR));
+ transport_->Start();
+ // Header read failure.
+ socket_cbs.Pop(net::ERR_CONNECTION_RESET);
+ EXPECT_EQ(ChannelEvent::SOCKET_READ,
+ logger_->GetLastError(kChannelId).channel_event);
+ EXPECT_EQ(net::ERR_CONNECTION_RESET,
+ logger_->GetLastError(kChannelId).net_return_value);
+}
+
+TEST_F(CastTransportTest, TestReadErrorInBodyAsync) {
+ CompletionQueue socket_cbs;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+ // Read bytes [4, n-1].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1)),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*delegate_, OnError(ChannelError::CAST_SOCKET_ERROR));
+ transport_->Start();
+ // Header read is OK.
+ socket_cbs.Pop(MessageFramer::MessageHeader::header_size());
+ // Body read fails.
+ socket_cbs.Pop(net::ERR_CONNECTION_RESET);
+ EXPECT_EQ(ChannelEvent::SOCKET_READ,
+ logger_->GetLastError(kChannelId).channel_event);
+ EXPECT_EQ(net::ERR_CONNECTION_RESET,
+ logger_->GetLastError(kChannelId).net_return_value);
+}
+
+TEST_F(CastTransportTest, TestReadCorruptedMessageAsync) {
+ CompletionQueue socket_cbs;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ // Corrupt the serialized message body(set it to X's).
+ for (size_t i = MessageFramer::MessageHeader::header_size();
+ i < serialized_message.size(); ++i) {
+ serialized_message[i] = 'x';
+ }
+
+ EXPECT_CALL(*delegate_, Start());
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+ // Read bytes [4, n].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1)),
+ EnqueueCallback<2>(&socket_cbs),
+ Return(net::ERR_IO_PENDING)))
+ .RetiresOnSaturation();
+
+ EXPECT_CALL(*delegate_, OnError(ChannelError::INVALID_MESSAGE));
+ transport_->Start();
+ socket_cbs.Pop(MessageFramer::MessageHeader::header_size());
+ socket_cbs.Pop(serialized_message.size() -
+ MessageFramer::MessageHeader::header_size());
+}
+
+// ----------------------------------------------------------------------------
+// Synchronous read tests
+TEST_F(CastTransportTest, TestFullReadSync) {
+ InSequence s;
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ Return(MessageFramer::MessageHeader::header_size())))
+ .RetiresOnSaturation();
+ // Read bytes [4, n].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size())),
+ Return(serialized_message.size() -
+ MessageFramer::MessageHeader::header_size())))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*delegate_, OnMessage(EqualsProto(message)));
+ // Async result in order to discontinue the read loop.
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(Return(net::ERR_IO_PENDING));
+ transport_->Start();
+}
+
+TEST_F(CastTransportTest, TestPartialReadSync) {
+ InSequence s;
+
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ Return(MessageFramer::MessageHeader::header_size())))
+ .RetiresOnSaturation();
+ // Read bytes [4, n-1].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1)),
+ Return(serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1)))
+ .RetiresOnSaturation();
+ // Read final byte.
+ EXPECT_CALL(mock_socket_, Read(NotNull(), 1, _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ serialized_message.size() - 1, 1)),
+ Return(1)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*delegate_, OnMessage(EqualsProto(message)));
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(Return(net::ERR_IO_PENDING));
+ transport_->Start();
+}
+
+TEST_F(CastTransportTest, TestReadErrorInHeaderSync) {
+ InSequence s;
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ Return(net::ERR_CONNECTION_RESET)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*delegate_, OnError(ChannelError::CAST_SOCKET_ERROR));
+ transport_->Start();
+}
+
+TEST_F(CastTransportTest, TestReadErrorInBodySync) {
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ Return(MessageFramer::MessageHeader::header_size())))
+ .RetiresOnSaturation();
+ // Read bytes [4, n-1].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1)),
+ Return(net::ERR_CONNECTION_RESET)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*delegate_, OnError(ChannelError::CAST_SOCKET_ERROR));
+ transport_->Start();
+ EXPECT_EQ(ChannelEvent::SOCKET_READ,
+ logger_->GetLastError(kChannelId).channel_event);
+ EXPECT_EQ(net::ERR_CONNECTION_RESET,
+ logger_->GetLastError(kChannelId).net_return_value);
+}
+
+TEST_F(CastTransportTest, TestReadCorruptedMessageSync) {
+ InSequence s;
+ CastMessage message = CreateCastMessage();
+ std::string serialized_message;
+ EXPECT_TRUE(MessageFramer::Serialize(message, &serialized_message));
+
+ // Corrupt the serialized message body(set it to X's).
+ for (size_t i = MessageFramer::MessageHeader::header_size();
+ i < serialized_message.size(); ++i) {
+ serialized_message[i] = 'x';
+ }
+
+ EXPECT_CALL(*delegate_, Start());
+
+ // Read bytes [0, 3].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(), MessageFramer::MessageHeader::header_size(), _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message),
+ Return(MessageFramer::MessageHeader::header_size())))
+ .RetiresOnSaturation();
+ // Read bytes [4, n].
+ EXPECT_CALL(mock_socket_,
+ Read(NotNull(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size(),
+ _))
+ .WillOnce(DoAll(FillBufferFromString<0>(serialized_message.substr(
+ MessageFramer::MessageHeader::header_size(),
+ serialized_message.size() -
+ MessageFramer::MessageHeader::header_size() - 1)),
+ Return(serialized_message.size() -
+ MessageFramer::MessageHeader::header_size())))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*delegate_, OnError(ChannelError::INVALID_MESSAGE));
+ transport_->Start();
+}
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/keep_alive_delegate.cc b/chromium/components/cast_channel/keep_alive_delegate.cc
new file mode 100644
index 00000000000..eabc559a50a
--- /dev/null
+++ b/chromium/components/cast_channel/keep_alive_delegate.cc
@@ -0,0 +1,197 @@
+// 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/cast_channel/keep_alive_delegate.h"
+
+#include <string>
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/values.h"
+#include "components/cast_channel/cast_channel_enum.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/logger.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+#include "net/base/net_errors.h"
+
+namespace cast_channel {
+namespace {
+
+const char kHeartbeatNamespace[] = "urn:x-cast:com.google.cast.tp.heartbeat";
+const char kPingSenderId[] = "chrome";
+const char kPingReceiverId[] = "receiver-0";
+const char kTypeNodeId[] = "type";
+
+// Parses the JSON-encoded payload of |message| and returns the value in the
+// "type" field or the empty string if the parse fails or the field is not
+// found.
+std::string ParseForPayloadType(const CastMessage& message) {
+ std::unique_ptr<base::Value> parsed_payload(
+ base::JSONReader::Read(message.payload_utf8()));
+ base::DictionaryValue* payload_as_dict;
+ if (!parsed_payload || !parsed_payload->GetAsDictionary(&payload_as_dict))
+ return std::string();
+ std::string type_string;
+ if (!payload_as_dict->GetString(kTypeNodeId, &type_string))
+ return std::string();
+ return type_string;
+}
+
+} // namespace
+
+// static
+const char KeepAliveDelegate::kHeartbeatPingType[] = "PING";
+
+// static
+const char KeepAliveDelegate::kHeartbeatPongType[] = "PONG";
+
+using ::cast_channel::ChannelError;
+
+// static
+CastMessage KeepAliveDelegate::CreateKeepAliveMessage(
+ const char* message_type) {
+ CastMessage output;
+ output.set_protocol_version(CastMessage::CASTV2_1_0);
+ output.set_source_id(kPingSenderId);
+ output.set_destination_id(kPingReceiverId);
+ output.set_namespace_(kHeartbeatNamespace);
+ base::DictionaryValue type_dict;
+ type_dict.SetString(kTypeNodeId, message_type);
+ if (!base::JSONWriter::Write(type_dict, output.mutable_payload_utf8())) {
+ LOG(ERROR) << "Failed to serialize dictionary.";
+ return output;
+ }
+ output.set_payload_type(
+ CastMessage::PayloadType::CastMessage_PayloadType_STRING);
+ return output;
+}
+
+KeepAliveDelegate::KeepAliveDelegate(
+ CastSocket* socket,
+ scoped_refptr<Logger> logger,
+ std::unique_ptr<CastTransport::Delegate> inner_delegate,
+ base::TimeDelta ping_interval,
+ base::TimeDelta liveness_timeout)
+ : started_(false),
+ socket_(socket),
+ logger_(logger),
+ inner_delegate_(std::move(inner_delegate)),
+ liveness_timeout_(liveness_timeout),
+ ping_interval_(ping_interval) {
+ DCHECK(ping_interval_ < liveness_timeout_);
+ DCHECK(inner_delegate_);
+ DCHECK(socket_);
+ ping_message_ = CreateKeepAliveMessage(kHeartbeatPingType);
+ pong_message_ = CreateKeepAliveMessage(kHeartbeatPongType);
+}
+
+KeepAliveDelegate::~KeepAliveDelegate() {}
+
+void KeepAliveDelegate::SetTimersForTest(
+ std::unique_ptr<base::Timer> injected_ping_timer,
+ std::unique_ptr<base::Timer> injected_liveness_timer) {
+ ping_timer_ = std::move(injected_ping_timer);
+ liveness_timer_ = std::move(injected_liveness_timer);
+}
+
+void KeepAliveDelegate::Start() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!started_);
+
+ VLOG(1) << "Starting keep-alive timers.";
+ VLOG(1) << "Ping timeout: " << ping_interval_;
+ VLOG(1) << "Liveness timeout: " << liveness_timeout_;
+
+ // Use injected mock timers, if provided.
+ if (!ping_timer_) {
+ ping_timer_.reset(new base::Timer(true, false));
+ }
+ if (!liveness_timer_) {
+ liveness_timer_.reset(new base::Timer(true, false));
+ }
+
+ ping_timer_->Start(
+ FROM_HERE, ping_interval_,
+ base::Bind(&KeepAliveDelegate::SendKeepAliveMessage,
+ base::Unretained(this), ping_message_, kHeartbeatPingType));
+ liveness_timer_->Start(
+ FROM_HERE, liveness_timeout_,
+ base::Bind(&KeepAliveDelegate::LivenessTimeout, base::Unretained(this)));
+
+ started_ = true;
+ inner_delegate_->Start();
+}
+
+void KeepAliveDelegate::ResetTimers() {
+ DCHECK(started_);
+ ping_timer_->Reset();
+ liveness_timer_->Reset();
+}
+
+void KeepAliveDelegate::SendKeepAliveMessage(const CastMessage& message,
+ const char* message_type) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ VLOG(2) << "Sending " << message_type;
+ socket_->transport()->SendMessage(
+ message, base::Bind(&KeepAliveDelegate::SendKeepAliveMessageComplete,
+ base::Unretained(this), message_type));
+}
+
+void KeepAliveDelegate::SendKeepAliveMessageComplete(const char* message_type,
+ int rv) {
+ VLOG(2) << "Sending " << message_type << " complete, rv=" << rv;
+ if (rv != net::OK) {
+ // An error occurred while sending the ping response.
+ VLOG(1) << "Error sending " << message_type;
+ logger_->LogSocketEventWithRv(socket_->id(), ChannelEvent::PING_WRITE_ERROR,
+ rv);
+ OnError(ChannelError::CAST_SOCKET_ERROR);
+ }
+}
+
+void KeepAliveDelegate::LivenessTimeout() {
+ OnError(ChannelError::PING_TIMEOUT);
+ Stop();
+}
+
+// CastTransport::Delegate interface.
+void KeepAliveDelegate::OnError(ChannelError error_state) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ VLOG(1) << "KeepAlive::OnError: "
+ << ::cast_channel::ChannelErrorToString(error_state);
+ inner_delegate_->OnError(error_state);
+ Stop();
+}
+
+void KeepAliveDelegate::OnMessage(const CastMessage& message) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ VLOG(2) << "KeepAlive::OnMessage : " << message.payload_utf8();
+
+ if (started_)
+ ResetTimers();
+
+ // PING and PONG messages are intercepted and handled by KeepAliveDelegate
+ // here. All other messages are passed through to |inner_delegate_|.
+ const std::string payload_type = ParseForPayloadType(message);
+ if (payload_type == kHeartbeatPingType) {
+ VLOG(2) << "Received PING.";
+ if (started_)
+ SendKeepAliveMessage(pong_message_, kHeartbeatPongType);
+ } else if (payload_type == kHeartbeatPongType) {
+ VLOG(2) << "Received PONG.";
+ } else {
+ inner_delegate_->OnMessage(message);
+ }
+}
+
+void KeepAliveDelegate::Stop() {
+ if (started_) {
+ started_ = false;
+ ping_timer_->Stop();
+ liveness_timer_->Stop();
+ }
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/keep_alive_delegate.h b/chromium/components/cast_channel/keep_alive_delegate.h
new file mode 100644
index 00000000000..9840b602af6
--- /dev/null
+++ b/chromium/components/cast_channel/keep_alive_delegate.h
@@ -0,0 +1,113 @@
+// 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_CAST_CHANNEL_KEEP_ALIVE_DELEGATE_H_
+#define COMPONENTS_CAST_CHANNEL_KEEP_ALIVE_DELEGATE_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/timer/timer.h"
+#include "components/cast_channel/cast_transport.h"
+#include "components/cast_channel/proto/cast_channel.pb.h"
+
+namespace cast_channel {
+
+class CastSocket;
+class Logger;
+
+// Decorator delegate which provides keep-alive functionality.
+// Keep-alive messages are handled by this object; all other messages and
+// errors are passed to |inner_delegate_|.
+class KeepAliveDelegate : public CastTransport::Delegate {
+ public:
+ // |socket|: The socket to be kept alive.
+ // |logger|: The logging object which collects protocol events and error
+ // details.
+ // |inner_delegate|: The delegate which processes all non-keep-alive
+ // messages. This object assumes ownership of
+ // |inner_delegate|.
+ // |ping_interval|: The amount of idle time to wait before sending a PING to
+ // the remote end.
+ // |liveness_timeout|: The amount of idle time to wait before terminating the
+ // connection.
+ KeepAliveDelegate(CastSocket* socket,
+ scoped_refptr<Logger> logger,
+ std::unique_ptr<CastTransport::Delegate> inner_delegate,
+ base::TimeDelta ping_interval,
+ base::TimeDelta liveness_timeout);
+
+ ~KeepAliveDelegate() override;
+
+ // Creates a keep-alive message (e.g. PING or PONG).
+ static CastMessage CreateKeepAliveMessage(const char* message_type);
+
+ void SetTimersForTest(std::unique_ptr<base::Timer> injected_ping_timer,
+ std::unique_ptr<base::Timer> injected_liveness_timer);
+
+ // CastTransport::Delegate implementation.
+ void Start() override;
+ void OnError(ChannelError error_state) override;
+ void OnMessage(const CastMessage& message) override;
+
+ static const char kHeartbeatPingType[];
+ static const char kHeartbeatPongType[];
+
+ private:
+ // Restarts the ping/liveness timeout timers. Called when a message
+ // is received from the remote end.
+ void ResetTimers();
+
+ // Sends a formatted PING or PONG message to the remote side.
+ void SendKeepAliveMessage(const CastMessage& message,
+ const char* message_type);
+
+ // Callback for SendKeepAliveMessage.
+ void SendKeepAliveMessageComplete(const char* message_type, int rv);
+
+ // Called when the liveness timer expires, indicating that the remote
+ // end has not responded within the |liveness_timeout_| interval.
+ void LivenessTimeout();
+
+ // Stops the ping and liveness timers if they are started.
+ // To be called after an error.
+ void Stop();
+
+ // Indicates that Start() was called.
+ bool started_;
+
+ // Socket that is managed by the keep-alive object.
+ CastSocket* socket_;
+
+ // Logging object.
+ scoped_refptr<Logger> logger_;
+
+ // Delegate object which receives all non-keep alive messages.
+ std::unique_ptr<CastTransport::Delegate> inner_delegate_;
+
+ // Amount of idle time to wait before disconnecting.
+ base::TimeDelta liveness_timeout_;
+
+ // Amount of idle time to wait before pinging the receiver.
+ base::TimeDelta ping_interval_;
+
+ // Fired when |ping_interval_| is exceeded or when triggered by test code.
+ std::unique_ptr<base::Timer> ping_timer_;
+
+ // Fired when |liveness_timer_| is exceeded.
+ std::unique_ptr<base::Timer> liveness_timer_;
+
+ // The PING message to send over the wire.
+ CastMessage ping_message_;
+
+ // The PONG message to send over the wire.
+ CastMessage pong_message_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(KeepAliveDelegate);
+};
+
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_KEEP_ALIVE_DELEGATE_H_
diff --git a/chromium/components/cast_channel/keep_alive_delegate_unittest.cc b/chromium/components/cast_channel/keep_alive_delegate_unittest.cc
new file mode 100644
index 00000000000..391665bea7f
--- /dev/null
+++ b/chromium/components/cast_channel/keep_alive_delegate_unittest.cc
@@ -0,0 +1,215 @@
+// 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/cast_channel/keep_alive_delegate.h"
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/timer/mock_timer.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "net/base/net_errors.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Sequence;
+
+namespace cast_channel {
+namespace {
+
+const int64_t kTestPingTimeoutMillis = 1000;
+const int64_t kTestLivenessTimeoutMillis = 10000;
+
+// Extends MockTimer with a mockable method ResetTriggered() which permits
+// test code to set GMock expectations for Timer::Reset().
+class MockTimerWithMonitoredReset : public base::MockTimer {
+ public:
+ MockTimerWithMonitoredReset(bool retain_user_task, bool is_repeating)
+ : base::MockTimer(retain_user_task, is_repeating) {}
+ ~MockTimerWithMonitoredReset() override {}
+
+ // Instrumentation point for determining how many times Reset() was called.
+ MOCK_METHOD0(ResetTriggered, void(void));
+ MOCK_METHOD0(Stop, void(void));
+
+ // Passes through the Reset call to the base MockTimer and visits the mock
+ // ResetTriggered method.
+ void Reset() override {
+ base::MockTimer::Reset();
+ ResetTriggered();
+ }
+};
+
+class KeepAliveDelegateTest : public testing::Test {
+ public:
+ using ChannelError = ::cast_channel::ChannelError;
+
+ KeepAliveDelegateTest() {}
+ ~KeepAliveDelegateTest() override {}
+
+ protected:
+ void SetUp() override {
+ inner_delegate_ = new MockCastTransportDelegate;
+ logger_ = new Logger();
+ keep_alive_.reset(new KeepAliveDelegate(
+ &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);
+ EXPECT_CALL(*liveness_timer_, Stop()).Times(0);
+ EXPECT_CALL(*ping_timer_, Stop()).Times(0);
+ keep_alive_->SetTimersForTest(base::WrapUnique(ping_timer_),
+ base::WrapUnique(liveness_timer_));
+ }
+
+ // Runs all pending tasks in the message loop.
+ void RunPendingTasks() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ base::MessageLoop message_loop_;
+ MockCastSocket socket_;
+ std::unique_ptr<KeepAliveDelegate> keep_alive_;
+ scoped_refptr<Logger> logger_;
+ MockCastTransportDelegate* inner_delegate_;
+ MockTimerWithMonitoredReset* liveness_timer_;
+ MockTimerWithMonitoredReset* ping_timer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeepAliveDelegateTest);
+};
+
+TEST_F(KeepAliveDelegateTest, TestErrorHandledBeforeStarting) {
+ keep_alive_->OnError(ChannelError::CONNECT_ERROR);
+}
+
+TEST_F(KeepAliveDelegateTest, TestPing) {
+ EXPECT_CALL(*socket_.mock_transport(),
+ SendMessage(EqualsProto(KeepAliveDelegate::CreateKeepAliveMessage(
+ KeepAliveDelegate::kHeartbeatPingType)),
+ _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK));
+ EXPECT_CALL(*inner_delegate_, Start());
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(2);
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(2);
+ EXPECT_CALL(*ping_timer_, Stop());
+
+ keep_alive_->Start();
+ ping_timer_->Fire();
+ keep_alive_->OnMessage(KeepAliveDelegate::CreateKeepAliveMessage(
+ KeepAliveDelegate::kHeartbeatPongType));
+ RunPendingTasks();
+}
+
+TEST_F(KeepAliveDelegateTest, TestPingFailed) {
+ EXPECT_CALL(*socket_.mock_transport(),
+ SendMessage(EqualsProto(KeepAliveDelegate::CreateKeepAliveMessage(
+ KeepAliveDelegate::kHeartbeatPingType)),
+ _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::ERR_CONNECTION_RESET));
+ EXPECT_CALL(*inner_delegate_, Start());
+ EXPECT_CALL(*inner_delegate_, OnError(ChannelError::CAST_SOCKET_ERROR));
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(1);
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(1);
+ EXPECT_CALL(*liveness_timer_, Stop());
+ EXPECT_CALL(*ping_timer_, Stop()).Times(2);
+
+ keep_alive_->Start();
+ ping_timer_->Fire();
+ RunPendingTasks();
+ EXPECT_EQ(ChannelEvent::PING_WRITE_ERROR,
+ logger_->GetLastError(socket_.id()).channel_event);
+ EXPECT_EQ(net::ERR_CONNECTION_RESET,
+ logger_->GetLastError(socket_.id()).net_return_value);
+}
+
+TEST_F(KeepAliveDelegateTest, TestPingAndLivenessTimeout) {
+ EXPECT_CALL(*socket_.mock_transport(),
+ SendMessage(EqualsProto(KeepAliveDelegate::CreateKeepAliveMessage(
+ KeepAliveDelegate::kHeartbeatPingType)),
+ _))
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK));
+ EXPECT_CALL(*inner_delegate_, OnError(ChannelError::PING_TIMEOUT));
+ EXPECT_CALL(*inner_delegate_, Start());
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(1);
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(1);
+ EXPECT_CALL(*liveness_timer_, Stop()).Times(2);
+ EXPECT_CALL(*ping_timer_, Stop()).Times(2);
+
+ keep_alive_->Start();
+ ping_timer_->Fire();
+ liveness_timer_->Fire();
+ RunPendingTasks();
+}
+
+TEST_F(KeepAliveDelegateTest, TestResetTimersAndPassthroughAllOtherTraffic) {
+ CastMessage other_message =
+ KeepAliveDelegate::CreateKeepAliveMessage("NEITHER_PING_NOR_PONG");
+
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(other_message)));
+ EXPECT_CALL(*inner_delegate_, Start());
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(2);
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(2);
+
+ keep_alive_->Start();
+ keep_alive_->OnMessage(other_message);
+ RunPendingTasks();
+}
+
+TEST_F(KeepAliveDelegateTest, TestPassthroughMessagesAfterError) {
+ CastMessage message =
+ KeepAliveDelegate::CreateKeepAliveMessage("NEITHER_PING_NOR_PONG");
+ CastMessage message_after_error =
+ KeepAliveDelegate::CreateKeepAliveMessage("ANOTHER_NOT_PING_NOR_PONG");
+ CastMessage late_ping_message = KeepAliveDelegate::CreateKeepAliveMessage(
+ KeepAliveDelegate::kHeartbeatPingType);
+
+ EXPECT_CALL(*inner_delegate_, Start()).Times(1);
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(2);
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(2);
+ EXPECT_CALL(*liveness_timer_, Stop()).Times(1);
+ EXPECT_CALL(*ping_timer_, Stop()).Times(1);
+
+ Sequence message_and_error_sequence;
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(message)))
+ .Times(1)
+ .InSequence(message_and_error_sequence)
+ .RetiresOnSaturation();
+ EXPECT_CALL(*inner_delegate_, OnError(ChannelError::INVALID_MESSAGE))
+ .Times(1)
+ .InSequence(message_and_error_sequence);
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(message_after_error)))
+ .Times(1)
+ .InSequence(message_and_error_sequence)
+ .RetiresOnSaturation();
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(late_ping_message)))
+ .Times(0)
+ .InSequence(message_and_error_sequence)
+ .RetiresOnSaturation();
+
+ // Start, process one message, then error-out. KeepAliveDelegate will
+ // automatically stop itself.
+ keep_alive_->Start();
+ keep_alive_->OnMessage(message);
+ RunPendingTasks();
+ keep_alive_->OnError(ChannelError::INVALID_MESSAGE);
+ RunPendingTasks();
+
+ // Process a non-PING/PONG message and expect it to pass through.
+ keep_alive_->OnMessage(message_after_error);
+ RunPendingTasks();
+
+ // Process a late-arriving PING/PONG message, which should have no effect.
+ keep_alive_->OnMessage(late_ping_message);
+ RunPendingTasks();
+}
+
+} // namespace
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/logger.cc b/chromium/components/cast_channel/logger.cc
new file mode 100644
index 00000000000..49641fa994e
--- /dev/null
+++ b/chromium/components/cast_channel/logger.cc
@@ -0,0 +1,133 @@
+// 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/cast_channel/logger.h"
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "components/cast_channel/cast_auth_util.h"
+#include "components/cast_channel/cast_socket.h"
+#include "net/base/net_errors.h"
+
+namespace cast_channel {
+
+using net::IPEndPoint;
+
+namespace {
+
+ChallengeReplyError AuthErrorToChallengeReplyError(
+ AuthResult::ErrorType error_type) {
+ switch (error_type) {
+ case AuthResult::ERROR_NONE:
+ return ChallengeReplyError::NONE;
+ case AuthResult::ERROR_PEER_CERT_EMPTY:
+ return ChallengeReplyError::PEER_CERT_EMPTY;
+ case AuthResult::ERROR_WRONG_PAYLOAD_TYPE:
+ return ChallengeReplyError::WRONG_PAYLOAD_TYPE;
+ case AuthResult::ERROR_NO_PAYLOAD:
+ return ChallengeReplyError::NO_PAYLOAD;
+ case AuthResult::ERROR_PAYLOAD_PARSING_FAILED:
+ return ChallengeReplyError::PAYLOAD_PARSING_FAILED;
+ case AuthResult::ERROR_MESSAGE_ERROR:
+ return ChallengeReplyError::MESSAGE_ERROR;
+ case AuthResult::ERROR_NO_RESPONSE:
+ return ChallengeReplyError::NO_RESPONSE;
+ case AuthResult::ERROR_FINGERPRINT_NOT_FOUND:
+ return ChallengeReplyError::FINGERPRINT_NOT_FOUND;
+ case AuthResult::ERROR_CERT_PARSING_FAILED:
+ return ChallengeReplyError::CERT_PARSING_FAILED;
+ case AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA:
+ return ChallengeReplyError::CERT_NOT_SIGNED_BY_TRUSTED_CA;
+ case AuthResult::ERROR_CANNOT_EXTRACT_PUBLIC_KEY:
+ return ChallengeReplyError::CANNOT_EXTRACT_PUBLIC_KEY;
+ case AuthResult::ERROR_SIGNED_BLOBS_MISMATCH:
+ return ChallengeReplyError::SIGNED_BLOBS_MISMATCH;
+ case AuthResult::ERROR_TLS_CERT_VALIDITY_PERIOD_TOO_LONG:
+ return ChallengeReplyError::TLS_CERT_VALIDITY_PERIOD_TOO_LONG;
+ case AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE:
+ return ChallengeReplyError::TLS_CERT_VALID_START_DATE_IN_FUTURE;
+ case AuthResult::ERROR_TLS_CERT_EXPIRED:
+ return ChallengeReplyError::TLS_CERT_EXPIRED;
+ case AuthResult::ERROR_CRL_INVALID:
+ return ChallengeReplyError::CRL_INVALID;
+ case AuthResult::ERROR_CERT_REVOKED:
+ return ChallengeReplyError::CERT_REVOKED;
+ case AuthResult::ERROR_SENDER_NONCE_MISMATCH:
+ return ChallengeReplyError::SENDER_NONCE_MISMATCH;
+ default:
+ NOTREACHED();
+ return ChallengeReplyError::NONE;
+ }
+}
+
+} // namespace
+
+LastError::LastError()
+ : channel_event(ChannelEvent::UNKNOWN),
+ challenge_reply_error(ChallengeReplyError::NONE),
+ net_return_value(net::OK) {}
+
+LastError::~LastError() {}
+
+Logger::Logger() {
+ // Logger may not be necessarily be created on the IO thread, but logging
+ // happens exclusively there.
+ DETACH_FROM_THREAD(thread_checker_);
+}
+
+Logger::~Logger() {}
+
+void Logger::LogSocketEventWithRv(int channel_id,
+ ChannelEvent channel_event,
+ int rv) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ MaybeSetLastError(channel_id, channel_event, rv, ChallengeReplyError::NONE);
+}
+
+void Logger::LogSocketChallengeReplyEvent(int channel_id,
+ const AuthResult& auth_result) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ MaybeSetLastError(channel_id, ChannelEvent::AUTH_CHALLENGE_REPLY, net::OK,
+ AuthErrorToChallengeReplyError(auth_result.error_type));
+}
+
+LastError Logger::GetLastError(int channel_id) const {
+ const auto it = last_errors_.find(channel_id);
+ if (it != last_errors_.end()) {
+ return it->second;
+ } else {
+ return LastError();
+ }
+}
+
+void Logger::ClearLastError(int channel_id) {
+ last_errors_.erase(channel_id);
+}
+
+void Logger::MaybeSetLastError(int channel_id,
+ ChannelEvent channel_event,
+ int rv,
+ ChallengeReplyError challenge_reply_error) {
+ auto it = last_errors_.find(channel_id);
+ if (it == last_errors_.end())
+ last_errors_[channel_id] = LastError();
+
+ LastError* last_error = &last_errors_[channel_id];
+ if (rv < net::ERR_IO_PENDING) {
+ last_error->net_return_value = rv;
+ last_error->channel_event = channel_event;
+ }
+
+ if (challenge_reply_error != ChallengeReplyError::NONE) {
+ last_error->challenge_reply_error = challenge_reply_error;
+ last_error->channel_event = channel_event;
+ }
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/logger.h b/chromium/components/cast_channel/logger.h
new file mode 100644
index 00000000000..70a23fc7f5d
--- /dev/null
+++ b/chromium/components/cast_channel/logger.h
@@ -0,0 +1,81 @@
+// 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_CAST_CHANNEL_LOGGER_H_
+#define COMPONENTS_CAST_CHANNEL_LOGGER_H_
+
+#include <stddef.h>
+
+#include <deque>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "components/cast_channel/cast_channel_enum.h"
+
+namespace cast_channel {
+
+struct AuthResult;
+
+// Holds the most recent error encountered by a CastSocket.
+struct LastError {
+ public:
+ LastError();
+ ~LastError();
+
+ // The most recent event that occurred at the time of the error.
+ ChannelEvent channel_event;
+
+ // The most recent ChallengeReplyError logged for the socket.
+ // NOTE(mfoltz): AuthResult::ErrorType is zero-indexed and ChallengeReplyError
+ // is one-indexed, so we can't use AuthResult::ErrorType here.
+ ChallengeReplyError challenge_reply_error;
+
+ // The most recent net_return_value logged for the socket.
+ int net_return_value;
+};
+
+// Called with events that occur on a Cast Channel and remembers any that
+// warrant reporting to the caller in LastError.
+class Logger : public base::RefCountedThreadSafe<Logger> {
+ public:
+ Logger();
+
+ // For events that involves socket / crypto operations that returns a value.
+ void LogSocketEventWithRv(int channel_id, ChannelEvent channel_event, int rv);
+
+ // For AUTH_CHALLENGE_REPLY event.
+ void LogSocketChallengeReplyEvent(int channel_id,
+ const AuthResult& auth_result);
+
+ // Returns the last errors logged for |channel_id|.
+ LastError GetLastError(int channel_id) const;
+
+ // Removes a LastError entry for |channel_id| if one exists.
+ void ClearLastError(int channel_id);
+
+ private:
+ friend class base::RefCountedThreadSafe<Logger>;
+ ~Logger();
+
+ // Propagate any error values in |rv| or |challenge_reply_error| to the
+ // LastError for |channel_id|.
+ void MaybeSetLastError(int channel_id,
+ ChannelEvent channel_event,
+ int rv,
+ ChallengeReplyError challenge_reply_error);
+
+ // Maps channel_id to the LastError for the channel.
+ std::map<int, LastError> last_errors_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(Logger);
+};
+} // namespace cast_channel
+
+#endif // COMPONENTS_CAST_CHANNEL_LOGGER_H_
diff --git a/chromium/components/cast_channel/logger_unittest.cc b/chromium/components/cast_channel/logger_unittest.cc
new file mode 100644
index 00000000000..3bd67e0c15e
--- /dev/null
+++ b/chromium/components/cast_channel/logger_unittest.cc
@@ -0,0 +1,62 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "components/cast_channel/cast_auth_util.h"
+#include "components/cast_channel/logger.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cast_channel {
+
+TEST(CastChannelLoggerTest, LogLastErrorEvents) {
+ scoped_refptr<Logger> logger(new Logger());
+
+ // Net return value is set to an error
+ logger->LogSocketEventWithRv(1, ChannelEvent::TCP_SOCKET_CONNECT,
+ net::ERR_CONNECTION_FAILED);
+
+ LastError last_error = logger->GetLastError(1);
+ EXPECT_EQ(last_error.channel_event, ChannelEvent::TCP_SOCKET_CONNECT);
+ EXPECT_EQ(last_error.net_return_value, net::ERR_CONNECTION_FAILED);
+
+ // Challenge reply error set
+ AuthResult auth_result = AuthResult::CreateWithParseError(
+ "Some error", AuthResult::ErrorType::ERROR_PEER_CERT_EMPTY);
+
+ logger->LogSocketChallengeReplyEvent(2, auth_result);
+ last_error = logger->GetLastError(2);
+ EXPECT_EQ(last_error.channel_event, ChannelEvent::AUTH_CHALLENGE_REPLY);
+ EXPECT_EQ(last_error.challenge_reply_error,
+ ChallengeReplyError::PEER_CERT_EMPTY);
+
+ // Logging a non-error event does not set the LastError for the channel.
+ logger->LogSocketEventWithRv(3, ChannelEvent::TCP_SOCKET_CONNECT, net::OK);
+ last_error = logger->GetLastError(3);
+ EXPECT_EQ(last_error.channel_event, ChannelEvent::UNKNOWN);
+ EXPECT_EQ(last_error.net_return_value, net::OK);
+ EXPECT_EQ(last_error.challenge_reply_error, ChallengeReplyError::NONE);
+
+ // Now log a challenge reply error. LastError will be set.
+ auth_result =
+ AuthResult("Some error failed", AuthResult::ERROR_WRONG_PAYLOAD_TYPE);
+ logger->LogSocketChallengeReplyEvent(3, auth_result);
+ last_error = logger->GetLastError(3);
+ EXPECT_EQ(last_error.channel_event, ChannelEvent::AUTH_CHALLENGE_REPLY);
+ EXPECT_EQ(last_error.challenge_reply_error,
+ ChallengeReplyError::WRONG_PAYLOAD_TYPE);
+
+ // Logging a non-error event does not change the LastError for the channel.
+ logger->LogSocketEventWithRv(3, ChannelEvent::TCP_SOCKET_CONNECT, net::OK);
+ last_error = logger->GetLastError(3);
+ EXPECT_EQ(last_error.channel_event, ChannelEvent::AUTH_CHALLENGE_REPLY);
+ EXPECT_EQ(last_error.challenge_reply_error,
+ ChallengeReplyError::WRONG_PAYLOAD_TYPE);
+}
+
+} // namespace cast_channel
diff --git a/chromium/components/cast_channel/proto/BUILD.gn b/chromium/components/cast_channel/proto/BUILD.gn
new file mode 100644
index 00000000000..a1f22bf4c22
--- /dev/null
+++ b/chromium/components/cast_channel/proto/BUILD.gn
@@ -0,0 +1,12 @@
+# 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("cast_channel_proto") {
+ sources = [
+ "authority_keys.proto",
+ "cast_channel.proto",
+ ]
+}
diff --git a/chromium/components/cast_channel/proto/authority_keys.proto b/chromium/components/cast_channel/proto/authority_keys.proto
new file mode 100644
index 00000000000..9f62d200dc0
--- /dev/null
+++ b/chromium/components/cast_channel/proto/authority_keys.proto
@@ -0,0 +1,17 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package cast_channel.proto;
+
+message AuthorityKeys {
+ message Key {
+ required bytes fingerprint = 1;
+ required bytes public_key = 2;
+ }
+ repeated Key keys = 1;
+}
diff --git a/chromium/components/cast_channel/proto/cast_channel.proto b/chromium/components/cast_channel/proto/cast_channel.proto
new file mode 100644
index 00000000000..4cac6db6067
--- /dev/null
+++ b/chromium/components/cast_channel/proto/cast_channel.proto
@@ -0,0 +1,99 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package cast_channel;
+
+message CastMessage {
+ // Always pass a version of the protocol for future compatibility
+ // requirements.
+ enum ProtocolVersion { CASTV2_1_0 = 0; }
+ required ProtocolVersion protocol_version = 1;
+
+ // source and destination ids identify the origin and destination of the
+ // message. They are used to route messages between endpoints that share a
+ // device-to-device channel.
+ //
+ // For messages between applications:
+ // - The sender application id is a unique identifier generated on behalf of
+ // the sender application.
+ // - The receiver id is always the the session id for the application.
+ //
+ // For messages to or from the sender or receiver platform, the special ids
+ // 'sender-0' and 'receiver-0' can be used.
+ //
+ // For messages intended for all endpoints using a given channel, the
+ // wildcard destination_id '*' can be used.
+ required string source_id = 2;
+ required string destination_id = 3;
+
+ // This is the core multiplexing key. All messages are sent on a namespace
+ // and endpoints sharing a channel listen on one or more namespaces. The
+ // namespace defines the protocol and semantics of the message.
+ required string namespace = 4;
+
+ // Encoding and payload info follows.
+
+ // What type of data do we have in this message.
+ enum PayloadType {
+ STRING = 0;
+ BINARY = 1;
+ }
+ required PayloadType payload_type = 5;
+
+ // Depending on payload_type, exactly one of the following optional fields
+ // will always be set.
+ optional string payload_utf8 = 6;
+ optional bytes payload_binary = 7;
+}
+
+enum SignatureAlgorithm {
+ UNSPECIFIED = 0;
+ RSASSA_PKCS1v15 = 1;
+ RSASSA_PSS = 2;
+}
+
+enum HashAlgorithm {
+ SHA1 = 0;
+ SHA256 = 1;
+}
+
+// Messages for authentication protocol between a sender and a receiver.
+message AuthChallenge {
+ optional SignatureAlgorithm signature_algorithm = 1
+ [default = RSASSA_PKCS1v15];
+ optional bytes sender_nonce = 2;
+ optional HashAlgorithm hash_algorithm = 3 [default = SHA1];
+}
+
+message AuthResponse {
+ required bytes signature = 1;
+ required bytes client_auth_certificate = 2;
+ repeated bytes intermediate_certificate = 3;
+ optional SignatureAlgorithm signature_algorithm = 4
+ [default = RSASSA_PKCS1v15];
+ optional bytes sender_nonce = 5;
+ optional HashAlgorithm hash_algorithm = 6 [default = SHA1];
+ optional bytes crl = 7;
+}
+
+message AuthError {
+ enum ErrorType {
+ INTERNAL_ERROR = 0;
+ NO_TLS = 1; // The underlying connection is not TLS
+ SIGNATURE_ALGORITHM_UNAVAILABLE = 2;
+ }
+ required ErrorType error_type = 1;
+}
+
+message DeviceAuthMessage {
+ // Request fields
+ optional AuthChallenge challenge = 1;
+ // Response fields
+ optional AuthResponse response = 2;
+ optional AuthError error = 3;
+}
diff --git a/chromium/components/cdm/browser/cdm_message_filter_android.cc b/chromium/components/cdm/browser/cdm_message_filter_android.cc
index 8adab716db1..7840ea64ad3 100644
--- a/chromium/components/cdm/browser/cdm_message_filter_android.cc
+++ b/chromium/components/cdm/browser/cdm_message_filter_android.cc
@@ -9,12 +9,16 @@
#include <string>
#include <vector>
+#include "base/feature_list.h"
#include "base/macros.h"
#include "components/cdm/common/cdm_messages_android.h"
+#include "content/public/browser/android/android_overlay_provider.h"
+#include "content/public/browser/browser_thread.h"
#include "ipc/ipc_message_macros.h"
#include "media/base/android/media_codec_util.h"
#include "media/base/android/media_drm_bridge.h"
#include "media/base/audio_codecs.h"
+#include "media/base/media_switches.h"
#include "media/base/video_codecs.h"
#include "media/media_features.h"
@@ -64,14 +68,11 @@ const CodecInfo<media::AudioCodec> kAudioCodecsToQuery[] = {
static SupportedCodecs GetSupportedCodecs(
const SupportedKeySystemRequest& request,
- bool video_must_be_compositable) {
+ bool is_secure) {
const std::string& key_system = request.key_system;
SupportedCodecs supported_codecs = media::EME_CODEC_NONE;
for (const auto& info : kVideoCodecsToQuery) {
- // TODO(qinmin): Remove the composition logic when secure contents can be
- // composited.
- bool is_secure = !video_must_be_compositable;
if ((request.codecs & info.eme_codec) &&
MediaDrmBridge::IsKeySystemSupportedWithType(
key_system, info.container_mime_type) &&
@@ -92,8 +93,9 @@ static SupportedCodecs GetSupportedCodecs(
return supported_codecs;
}
-CdmMessageFilterAndroid::CdmMessageFilterAndroid()
- : BrowserMessageFilter(EncryptedMediaMsgStart) {}
+CdmMessageFilterAndroid::CdmMessageFilterAndroid(bool can_use_secure_codecs)
+ : BrowserMessageFilter(EncryptedMediaMsgStart),
+ force_to_support_secure_codecs_(can_use_secure_codecs) {}
CdmMessageFilterAndroid::~CdmMessageFilterAndroid() {}
@@ -134,9 +136,17 @@ void CdmMessageFilterAndroid::OnQueryKeySystemSupport(
DCHECK(request.codecs & media::EME_CODEC_ALL) << "unrecognized codec";
response->key_system = request.key_system;
- // TODO(qinmin): check composition is supported or not.
- response->compositing_codecs = GetSupportedCodecs(request, true);
- response->non_compositing_codecs = GetSupportedCodecs(request, false);
+ response->non_secure_codecs = GetSupportedCodecs(request, false);
+
+ bool are_overlay_supported =
+ content::AndroidOverlayProvider::GetInstance()->AreOverlaysSupported();
+ bool use_android_overlay =
+ base::FeatureList::IsEnabled(media::kUseAndroidOverlay);
+ if (force_to_support_secure_codecs_ ||
+ (are_overlay_supported && use_android_overlay)) {
+ DVLOG(1) << "Rendering the output of secure codecs is supported!";
+ response->secure_codecs = GetSupportedCodecs(request, true);
+ }
response->is_persistent_license_supported =
MediaDrmBridge::IsPersistentLicenseTypeSupported(request.key_system);
diff --git a/chromium/components/cdm/browser/cdm_message_filter_android.h b/chromium/components/cdm/browser/cdm_message_filter_android.h
index a7b7f8012f0..763bb85cc4f 100644
--- a/chromium/components/cdm/browser/cdm_message_filter_android.h
+++ b/chromium/components/cdm/browser/cdm_message_filter_android.h
@@ -13,12 +13,13 @@ struct SupportedKeySystemResponse;
namespace cdm {
-// Message filter for EME on android. It is responsible for getting the
+// Message filter for EME on Android. It is responsible for getting the
// SupportedKeySystems information and passing it back to renderer.
-class CdmMessageFilterAndroid
- : public content::BrowserMessageFilter {
+// TODO(xhwang): Convert this to a mojo interface or merge this with
+// desktop Chromium's IsPepperCdmAvailable() path.
+class CdmMessageFilterAndroid : public content::BrowserMessageFilter {
public:
- CdmMessageFilterAndroid();
+ explicit CdmMessageFilterAndroid(bool can_use_secure_codecs = false);
private:
~CdmMessageFilterAndroid() override;
@@ -34,6 +35,13 @@ class CdmMessageFilterAndroid
void OnGetPlatformKeySystemNames(std::vector<std::string>* key_systems);
+ // By default, rendering of secure codecs is supported when AndroidOverlay is
+ // enabled. However, on platforms like Cast on Android, secure codecs are
+ // always supported. This flag is used to force secure codecs support on such
+ // platforms.
+ // TODO(yucliu): Remove this and completely switch to the Clank model.
+ const bool force_to_support_secure_codecs_;
+
DISALLOW_COPY_AND_ASSIGN(CdmMessageFilterAndroid);
};
diff --git a/chromium/components/cdm/browser/media_drm_storage_impl.cc b/chromium/components/cdm/browser/media_drm_storage_impl.cc
index 800c6275cde..2f9ee4f4253 100644
--- a/chromium/components/cdm/browser/media_drm_storage_impl.cc
+++ b/chromium/components/cdm/browser/media_drm_storage_impl.cc
@@ -123,13 +123,13 @@ void MediaDrmStorageImpl::Initialize(const url::Origin& origin) {
initialized_ = true;
}
-void MediaDrmStorageImpl::OnProvisioned(const OnProvisionedCallback& callback) {
+void MediaDrmStorageImpl::OnProvisioned(OnProvisionedCallback callback) {
DVLOG(1) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
if (!initialized_) {
DVLOG(1) << __func__ << ": Not initialized.";
- callback.Run(false);
+ std::move(callback).Run(false);
return;
}
@@ -144,19 +144,19 @@ void MediaDrmStorageImpl::OnProvisioned(const OnProvisionedCallback& callback) {
storage_dict->SetWithoutPathExpansion(origin_string_,
CreateOriginDictionary());
- callback.Run(true);
+ std::move(callback).Run(true);
}
void MediaDrmStorageImpl::SavePersistentSession(
const std::string& session_id,
media::mojom::SessionDataPtr session_data,
- const SavePersistentSessionCallback& callback) {
+ SavePersistentSessionCallback callback) {
DVLOG(2) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
if (!initialized_) {
DVLOG(1) << __func__ << ": Not initialized.";
- callback.Run(false);
+ std::move(callback).Run(false);
return;
}
@@ -196,18 +196,18 @@ void MediaDrmStorageImpl::SavePersistentSession(
session_id, CreateSessionDictionary(session_data->key_set_id,
session_data->mime_type));
- callback.Run(true);
+ std::move(callback).Run(true);
}
void MediaDrmStorageImpl::LoadPersistentSession(
const std::string& session_id,
- const LoadPersistentSessionCallback& callback) {
+ LoadPersistentSessionCallback callback) {
DVLOG(2) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
if (!initialized_) {
DVLOG(1) << __func__ << ": Not initialized.";
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
@@ -221,14 +221,14 @@ void MediaDrmStorageImpl::LoadPersistentSession(
DVLOG(1) << __func__
<< ": Failed to save persistent session data; entry for origin "
<< origin_ << " does not exist.";
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
const base::DictionaryValue* sessions_dict = nullptr;
if (!origin_dict->GetDictionary(kSessions, &sessions_dict)) {
DVLOG(2) << __func__ << ": Sessions dictionary does not exist.";
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
@@ -236,7 +236,7 @@ void MediaDrmStorageImpl::LoadPersistentSession(
if (!sessions_dict->GetDictionaryWithoutPathExpansion(session_id,
&session_dict)) {
DVLOG(2) << __func__ << ": Session dictionary does not exist.";
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
@@ -244,22 +244,23 @@ void MediaDrmStorageImpl::LoadPersistentSession(
std::string mime_type;
if (!GetSessionData(session_dict, &key_set_id, &mime_type)) {
DVLOG(2) << __func__ << ": Failed to read session data.";
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
- callback.Run(media::mojom::SessionData::New(key_set_id, mime_type));
+ std::move(callback).Run(
+ media::mojom::SessionData::New(key_set_id, mime_type));
}
void MediaDrmStorageImpl::RemovePersistentSession(
const std::string& session_id,
- const RemovePersistentSessionCallback& callback) {
+ RemovePersistentSessionCallback callback) {
DVLOG(2) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
if (!initialized_) {
DVLOG(1) << __func__ << ": Not initialized.";
- callback.Run(false);
+ std::move(callback).Run(false);
return;
}
@@ -273,19 +274,19 @@ void MediaDrmStorageImpl::RemovePersistentSession(
if (!origin_dict) {
DVLOG(1) << __func__ << ": Entry for rigin " << origin_string_
<< " does not exist.";
- callback.Run(true);
+ std::move(callback).Run(true);
return;
}
base::DictionaryValue* sessions_dict = nullptr;
if (!origin_dict->GetDictionary(kSessions, &sessions_dict)) {
DVLOG(2) << __func__ << ": Sessions dictionary does not exist.";
- callback.Run(true);
+ std::move(callback).Run(true);
return;
}
sessions_dict->RemoveWithoutPathExpansion(session_id, nullptr);
- callback.Run(true);
+ std::move(callback).Run(true);
}
void MediaDrmStorageImpl::RenderFrameDeleted(
diff --git a/chromium/components/cdm/browser/media_drm_storage_impl.h b/chromium/components/cdm/browser/media_drm_storage_impl.h
index a581cb6f04d..f6970f650ca 100644
--- a/chromium/components/cdm/browser/media_drm_storage_impl.h
+++ b/chromium/components/cdm/browser/media_drm_storage_impl.h
@@ -37,17 +37,14 @@ class MediaDrmStorageImpl final : public media::mojom::MediaDrmStorage,
// media::mojom::MediaDrmStorage implementation.
void Initialize(const url::Origin& origin) final;
- void OnProvisioned(const OnProvisionedCallback& callback) final;
- void SavePersistentSession(
- const std::string& session_id,
- media::mojom::SessionDataPtr session_data,
- const SavePersistentSessionCallback& callback) final;
- void LoadPersistentSession(
- const std::string& session_id,
- const LoadPersistentSessionCallback& callback) final;
- void RemovePersistentSession(
- const std::string& session_id,
- const RemovePersistentSessionCallback& callback) final;
+ void OnProvisioned(OnProvisionedCallback callback) final;
+ void SavePersistentSession(const std::string& session_id,
+ media::mojom::SessionDataPtr session_data,
+ SavePersistentSessionCallback callback) final;
+ void LoadPersistentSession(const std::string& session_id,
+ LoadPersistentSessionCallback callback) final;
+ void RemovePersistentSession(const std::string& session_id,
+ RemovePersistentSessionCallback callback) final;
// content::WebContentsObserver implementation.
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) final;
diff --git a/chromium/components/cdm/common/cdm_messages_android.h b/chromium/components/cdm/common/cdm_messages_android.h
index 06104da658c..b1223efe23c 100644
--- a/chromium/components/cdm/common/cdm_messages_android.h
+++ b/chromium/components/cdm/common/cdm_messages_android.h
@@ -19,11 +19,9 @@ IPC_STRUCT_END()
IPC_STRUCT_BEGIN(SupportedKeySystemResponse)
IPC_STRUCT_MEMBER(std::string, key_system)
- IPC_STRUCT_MEMBER(media::SupportedCodecs,
- compositing_codecs,
+ IPC_STRUCT_MEMBER(media::SupportedCodecs, non_secure_codecs,
media::EME_CODEC_NONE)
- IPC_STRUCT_MEMBER(media::SupportedCodecs,
- non_compositing_codecs,
+ IPC_STRUCT_MEMBER(media::SupportedCodecs, secure_codecs,
media::EME_CODEC_NONE)
IPC_STRUCT_MEMBER(bool, is_persistent_license_supported)
IPC_STRUCT_END()
diff --git a/chromium/components/cdm/renderer/android_key_systems.cc b/chromium/components/cdm/renderer/android_key_systems.cc
index bd12b93422f..c7b3e96219c 100644
--- a/chromium/components/cdm/renderer/android_key_systems.cc
+++ b/chromium/components/cdm/renderer/android_key_systems.cc
@@ -101,9 +101,10 @@ SupportedKeySystemResponse QueryKeySystemSupport(
request.codecs = media::EME_CODEC_ALL;
content::RenderThread::Get()->Send(
new ChromeViewHostMsg_QueryKeySystemSupport(request, &response));
- DCHECK(!(response.compositing_codecs & ~media::EME_CODEC_ALL))
+
+ DCHECK(!(response.non_secure_codecs & ~media::EME_CODEC_ALL))
<< "unrecognized codec";
- DCHECK(!(response.non_compositing_codecs & ~media::EME_CODEC_ALL))
+ DCHECK(!(response.secure_codecs & ~media::EME_CODEC_ALL))
<< "unrecognized codec";
return response;
}
@@ -124,11 +125,11 @@ void AddAndroidWidevine(
? EmeSessionTypeSupport::SUPPORTED_WITH_IDENTIFIER
: EmeSessionTypeSupport::NOT_SUPPORTED;
- if (response.compositing_codecs != media::EME_CODEC_NONE) {
+ if (response.non_secure_codecs != media::EME_CODEC_NONE) {
DVLOG(3) << __func__ << " Widevine supported.";
concrete_key_systems->emplace_back(new WidevineKeySystemProperties(
- response.compositing_codecs, // Regular codecs.
- response.non_compositing_codecs, // Hardware-secure codecs.
+ 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.
@@ -138,7 +139,7 @@ void AddAndroidWidevine(
} else {
// It doesn't make sense to support secure codecs but not regular codecs.
DVLOG(3) << __func__ << " Widevine NOT supported.";
- DCHECK(response.non_compositing_codecs == media::EME_CODEC_NONE);
+ DCHECK(response.secure_codecs == media::EME_CODEC_NONE);
}
}
@@ -151,9 +152,9 @@ void AddAndroidPlatformKeySystems(
for (std::vector<std::string>::const_iterator it = key_system_names.begin();
it != key_system_names.end(); ++it) {
SupportedKeySystemResponse response = QueryKeySystemSupport(*it);
- if (response.compositing_codecs != media::EME_CODEC_NONE) {
+ if (response.non_secure_codecs != media::EME_CODEC_NONE) {
concrete_key_systems->emplace_back(new AndroidPlatformKeySystemProperties(
- *it, response.compositing_codecs));
+ *it, response.non_secure_codecs));
}
}
}
diff --git a/chromium/components/certificate_reporting/BUILD.gn b/chromium/components/certificate_reporting/BUILD.gn
index 74d48876457..d4717c24f69 100644
--- a/chromium/components/certificate_reporting/BUILD.gn
+++ b/chromium/components/certificate_reporting/BUILD.gn
@@ -21,6 +21,7 @@ static_library("certificate_reporting") {
deps = [
"//base",
"//components/network_time",
+ "//components/version_info",
"//crypto",
"//net",
"//url",
@@ -52,6 +53,7 @@ source_set("unit_tests") {
"//components/network_time",
"//components/network_time:network_time_test_support",
"//components/prefs:test_support",
+ "//components/version_info",
"//net:test_support",
"//testing/gtest",
]
diff --git a/chromium/components/certificate_reporting/DEPS b/chromium/components/certificate_reporting/DEPS
index bb3fb07d7e0..89d5a2fd6c4 100644
--- a/chromium/components/certificate_reporting/DEPS
+++ b/chromium/components/certificate_reporting/DEPS
@@ -1,7 +1,8 @@
include_rules = [
"+components/network_time",
"+components/prefs",
+ "+components/version_info",
"+crypto",
"+net",
"+third_party/boringssl/src/include",
-] \ No newline at end of file
+]
diff --git a/chromium/components/certificate_reporting/cert_logger.proto b/chromium/components/certificate_reporting/cert_logger.proto
index 811d918a323..8a3538fdc58 100644
--- a/chromium/components/certificate_reporting/cert_logger.proto
+++ b/chromium/components/certificate_reporting/cert_logger.proto
@@ -28,12 +28,15 @@ message CertLoggerInterstitialInfo {
// a user.
enum InterstitialReason {
UNKNOWN_INTERSTITIAL_REASON = 0;
- // A standard SSL interstitial
+ // A standard SSL interstitial.
INTERSTITIAL_SSL = 1;
- // An interstitial alerting the user that they are in a captive portal
+ // An interstitial alerting the user that they are in a captive portal.
INTERSTITIAL_CAPTIVE_PORTAL = 2;
- // An interstitial telling the user to update their system clock
+ // An interstitial telling the user to update their system clock.
INTERSTITIAL_CLOCK = 3;
+ // An interstitial telling the user to remove the Superfish software from
+ // their system.
+ INTERSTITIAL_SUPERFISH = 4;
}
// The type of interstitial that was shown
@@ -145,4 +148,20 @@ message CertLoggerRequest {
// False when the report is attempted to be uploaded for the first time. True
// in all other uploads.
optional bool is_retry_upload = 11;
+
+ enum ChromeChannel {
+ CHROME_CHANNEL_NONE = 0;
+ CHROME_CHANNEL_UNKNOWN = 1;
+ CHROME_CHANNEL_DEV = 2;
+ CHROME_CHANNEL_CANARY = 3;
+ CHROME_CHANNEL_BETA = 4;
+ CHROME_CHANNEL_STABLE = 5;
+ };
+
+ // The Chrome channel that this error occurred on.
+ optional ChromeChannel chrome_channel = 12;
+
+ // True if the machine is enterprise managed. Currently only available for
+ // Windows and ChromeOS clients.
+ optional bool is_enterprise_managed = 13;
};
diff --git a/chromium/components/certificate_reporting/error_report.cc b/chromium/components/certificate_reporting/error_report.cc
index a48e18c80f1..a0e3a5794e3 100644
--- a/chromium/components/certificate_reporting/error_report.cc
+++ b/chromium/components/certificate_reporting/error_report.cc
@@ -9,7 +9,6 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
-#include "components/certificate_reporting/cert_logger.pb.h"
#include "components/network_time/network_time_tracker.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/x509_certificate.h"
@@ -128,6 +127,10 @@ void ErrorReport::SetInterstitialInfo(
interstitial_info->set_interstitial_reason(
CertLoggerInterstitialInfo::INTERSTITIAL_CLOCK);
break;
+ case INTERSTITIAL_SUPERFISH:
+ interstitial_info->set_interstitial_reason(
+ CertLoggerInterstitialInfo::INTERSTITIAL_SUPERFISH);
+ break;
}
interstitial_info->set_user_proceeded(proceed_decision == USER_PROCEEDED);
@@ -170,6 +173,37 @@ void ErrorReport::AddNetworkTimeInfo(
network_time_info->set_network_time_query_behavior(report_behavior);
}
+void ErrorReport::AddChromeChannel(version_info::Channel channel) {
+ switch (channel) {
+ case version_info::Channel::STABLE:
+ cert_report_->set_chrome_channel(
+ CertLoggerRequest::CHROME_CHANNEL_STABLE);
+ break;
+
+ case version_info::Channel::BETA:
+ cert_report_->set_chrome_channel(CertLoggerRequest::CHROME_CHANNEL_BETA);
+ break;
+
+ case version_info::Channel::CANARY:
+ cert_report_->set_chrome_channel(
+ CertLoggerRequest::CHROME_CHANNEL_CANARY);
+ break;
+
+ case version_info::Channel::DEV:
+ cert_report_->set_chrome_channel(CertLoggerRequest::CHROME_CHANNEL_DEV);
+ break;
+
+ case version_info::Channel::UNKNOWN:
+ cert_report_->set_chrome_channel(
+ CertLoggerRequest::CHROME_CHANNEL_UNKNOWN);
+ break;
+ }
+}
+
+void ErrorReport::SetIsEnterpriseManaged(bool is_enterprise_managed) {
+ cert_report_->set_is_enterprise_managed(is_enterprise_managed);
+}
+
void ErrorReport::SetIsRetryUpload(bool is_retry_upload) {
cert_report_->set_is_retry_upload(is_retry_upload);
}
@@ -178,6 +212,14 @@ const std::string& ErrorReport::hostname() const {
return cert_report_->hostname();
}
+CertLoggerRequest::ChromeChannel ErrorReport::chrome_channel() const {
+ return cert_report_->chrome_channel();
+}
+
+bool ErrorReport::is_enterprise_managed() const {
+ return cert_report_->is_enterprise_managed();
+}
+
bool ErrorReport::is_retry_upload() const {
return cert_report_->is_retry_upload();
}
diff --git a/chromium/components/certificate_reporting/error_report.h b/chromium/components/certificate_reporting/error_report.h
index f9236e1aa77..6c30f606c8c 100644
--- a/chromium/components/certificate_reporting/error_report.h
+++ b/chromium/components/certificate_reporting/error_report.h
@@ -8,6 +8,9 @@
#include <memory>
#include <string>
+#include "components/certificate_reporting/cert_logger.pb.h"
+#include "components/version_info/version_info.h"
+
namespace base {
class Time;
} // namespace base
@@ -34,7 +37,8 @@ class ErrorReport {
enum InterstitialReason {
INTERSTITIAL_SSL,
INTERSTITIAL_CAPTIVE_PORTAL,
- INTERSTITIAL_CLOCK
+ INTERSTITIAL_CLOCK,
+ INTERSTITIAL_SUPERFISH,
};
// Whether the user clicked through the interstitial or not.
@@ -72,12 +76,22 @@ class ErrorReport {
void AddNetworkTimeInfo(
const network_time::NetworkTimeTracker* network_time_tracker);
+ void AddChromeChannel(version_info::Channel channel);
+
+ void SetIsEnterpriseManaged(bool is_enterprise_managed);
+
// Sets is_retry_upload field of the protobuf to |is_retry_upload|.
void SetIsRetryUpload(bool is_retry_upload);
// Gets the hostname to which this report corresponds.
const std::string& hostname() const;
+ // Gets the Chrome channel attached to this report.
+ CertLoggerRequest::ChromeChannel chrome_channel() const;
+
+ // Returns true if the device that issued the report is a managed device.
+ bool is_enterprise_managed() const;
+
// Returns true if the report has been retried.
bool is_retry_upload() const;
diff --git a/chromium/components/certificate_reporting/error_report_unittest.cc b/chromium/components/certificate_reporting/error_report_unittest.cc
index 18c548ca473..ab510258409 100644
--- a/chromium/components/certificate_reporting/error_report_unittest.cc
+++ b/chromium/components/certificate_reporting/error_report_unittest.cc
@@ -15,9 +15,11 @@
#include "base/threading/thread.h"
#include "base/time/default_clock.h"
#include "base/time/default_tick_clock.h"
+#include "build/build_config.h"
#include "components/certificate_reporting/cert_logger.pb.h"
#include "components/network_time/network_time_test_utils.h"
#include "components/prefs/testing_pref_service.h"
+#include "components/version_info/version_info.h"
#include "net/cert/cert_status_flags.h"
#include "net/ssl/ssl_info.h"
#include "net/test/cert_test_util.h"
@@ -41,7 +43,7 @@ namespace {
const char kDummyHostname[] = "dummy.hostname.com";
const char kDummyFailureLog[] = "dummy failure log";
-const char kTestCertFilename[] = "test_mail_google_com.pem";
+const char kTestCertFilename[] = "x509_verify_results.chain.pem";
const net::CertStatus kCertStatus =
net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_REVOKED;
@@ -76,10 +78,17 @@ void GetTestSSLInfo(UnverifiedCertChainStatus unverified_cert_chain_status,
}
std::string GetPEMEncodedChain() {
- base::FilePath cert_path =
- net::GetTestCertsDirectory().AppendASCII(kTestCertFilename);
std::string cert_data;
- EXPECT_TRUE(base::ReadFileToString(cert_path, &cert_data));
+ std::vector<std::string> pem_certs;
+ scoped_refptr<net::X509Certificate> cert =
+ net::ImportCertFromFile(net::GetTestCertsDirectory(), kTestCertFilename);
+ if (!cert || !cert->GetPEMEncodedChain(&pem_certs)) {
+ ADD_FAILURE();
+ return cert_data;
+ }
+ for (const auto& cert : pem_certs) {
+ cert_data += cert;
+ }
return cert_data;
}
@@ -240,6 +249,59 @@ TEST(ErrorReportTest, NetworkTimeQueryingFeatureInfo) {
.network_time_query_behavior());
}
+TEST(ErrorReportTest, TestChromeChannelIncluded) {
+ struct ChannelTestCase {
+ version_info::Channel channel;
+ CertLoggerRequest::ChromeChannel expected_channel;
+ } kTestCases[] = {
+ {version_info::Channel::UNKNOWN,
+ CertLoggerRequest::CHROME_CHANNEL_UNKNOWN},
+ {version_info::Channel::DEV, CertLoggerRequest::CHROME_CHANNEL_DEV},
+ {version_info::Channel::CANARY, CertLoggerRequest::CHROME_CHANNEL_CANARY},
+ {version_info::Channel::BETA, CertLoggerRequest::CHROME_CHANNEL_BETA},
+ {version_info::Channel::STABLE,
+ CertLoggerRequest::CHROME_CHANNEL_STABLE}};
+
+ // Create a report, set its channel value and check if we
+ // get back test_case.expected_channel.
+ for (const ChannelTestCase& test_case : kTestCases) {
+ SSLInfo ssl_info;
+ ASSERT_NO_FATAL_FAILURE(
+ GetTestSSLInfo(INCLUDE_UNVERIFIED_CERT_CHAIN, &ssl_info, kCertStatus));
+ ErrorReport report(kDummyHostname, ssl_info);
+
+ report.AddChromeChannel(test_case.channel);
+ std::string serialized_report;
+ ASSERT_TRUE(report.Serialize(&serialized_report));
+
+ CertLoggerRequest parsed;
+ ASSERT_TRUE(parsed.ParseFromString(serialized_report));
+ EXPECT_EQ(test_case.expected_channel, parsed.chrome_channel());
+ }
+}
+
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
+// Tests that the SetIsEnterpriseManaged() function populates
+// is_enterprise_managed correctly on Windows, and that value is correctly
+// extracted from the parsed report.
+// These tests are OS specific because SetIsEnterpriseManaged is called only
+// on the Windows and ChromeOS OS.
+TEST(ErrorReportTest, TestIsEnterpriseManagedPopulatedOnWindows) {
+ SSLInfo ssl_info;
+ ASSERT_NO_FATAL_FAILURE(
+ GetTestSSLInfo(INCLUDE_UNVERIFIED_CERT_CHAIN, &ssl_info, kCertStatus));
+ ErrorReport report(kDummyHostname, ssl_info);
+
+ report.SetIsEnterpriseManaged(true);
+ std::string serialized_report;
+ ASSERT_TRUE(report.Serialize(&serialized_report));
+
+ CertLoggerRequest parsed;
+ ASSERT_TRUE(parsed.ParseFromString(serialized_report));
+ EXPECT_EQ(true, parsed.is_enterprise_managed());
+}
+#endif
+
#if defined(OS_ANDROID)
// Tests that information about the Android AIA fetching feature is included in
// the report.
diff --git a/chromium/components/certificate_reporting/error_reporter.cc b/chromium/components/certificate_reporting/error_reporter.cc
index 5065e45cbe2..e189a4f49a6 100644
--- a/chromium/components/certificate_reporting/error_reporter.cc
+++ b/chromium/components/certificate_reporting/error_reporter.cc
@@ -100,7 +100,9 @@ bool EncryptSerializedReport(const uint8_t* server_public_key,
}
constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
- net::DefineNetworkTrafficAnnotation("safe_browsing_extended_reporting", R"(
+ net::DefineNetworkTrafficAnnotation(
+ "safe_browsing_certificate_error_reporting",
+ R"(
semantics {
sender: "Safe Browsing Extended Reporting"
description:
diff --git a/chromium/components/certificate_transparency/ct_policy_manager.cc b/chromium/components/certificate_transparency/ct_policy_manager.cc
index 445f3d204a7..833014cf817 100644
--- a/chromium/components/certificate_transparency/ct_policy_manager.cc
+++ b/chromium/components/certificate_transparency/ct_policy_manager.cc
@@ -89,7 +89,7 @@ void CTPolicyManager::CTDelegate::UpdateFromPrefs(
net::TransportSecurityState::RequireCTDelegate::CTRequirementLevel
CTPolicyManager::CTDelegate::IsCTRequiredForHost(const std::string& hostname) {
- DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(network_task_runner_->RunsTasksInCurrentSequence());
// Scheme and port are ignored by the policy, so it's OK to construct a
// new GURL here. However, |hostname| is in network form, not URL form,
@@ -125,7 +125,7 @@ CTPolicyManager::CTDelegate::IsCTRequiredForHost(const std::string& hostname) {
void CTPolicyManager::CTDelegate::Update(base::ListValue* required_hosts,
base::ListValue* excluded_hosts) {
- DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(network_task_runner_->RunsTasksInCurrentSequence());
url_matcher_.reset(new url_matcher::URLMatcher);
filters_.clear();
diff --git a/chromium/components/chrome_cleaner/public/constants/constants.cc b/chromium/components/chrome_cleaner/public/constants/constants.cc
index 89c10a267ba..3837e21fbbf 100644
--- a/chromium/components/chrome_cleaner/public/constants/constants.cc
+++ b/chromium/components/chrome_cleaner/public/constants/constants.cc
@@ -13,7 +13,6 @@ const char kChromeMojoPipeTokenSwitch[] = "chrome-mojo-pipe-token";
const char kChromePromptSwitch[] = "chrome-prompt";
const char kChromeSystemInstallSwitch[] = "chrome-system-install";
const char kChromeVersionSwitch[] = "chrome-version";
-const char kEnableCleanerLoggingSwitch[] = "enable-cleaner-logging";
const char kEnableCrashReportingSwitch[] = "enable-crash-reporting";
const char kEngineExperimentGroupSwitch[] = "engine-experiment-group";
const char kEngineSwitch[] = "engine";
@@ -31,6 +30,7 @@ const wchar_t kCleanerSubKey[] = L"Cleaner";
const wchar_t kScanTimesSubKey[] = L"ScanTimes";
// Registry value names.
+const wchar_t kCleanupCompletedValueName[] = L"cleanup-completed";
const wchar_t kEndTimeValueName[] = L"EndTime";
const wchar_t kEngineErrorCodeValueName[] = L"EngineErrorCode";
const wchar_t kExitCodeValueName[] = L"ExitCode";
diff --git a/chromium/components/chrome_cleaner/public/constants/constants.h b/chromium/components/chrome_cleaner/public/constants/constants.h
index ad1b8af203b..d28e875901e 100644
--- a/chromium/components/chrome_cleaner/public/constants/constants.h
+++ b/chromium/components/chrome_cleaner/public/constants/constants.h
@@ -33,12 +33,6 @@ extern const char kChromeSystemInstallSwitch[];
// The Chrome version string.
extern const char kChromeVersionSwitch[];
-// Indicates whether logs upload is enabled in the cleaner process. Should be
-// set by Chrome only be set if user has opted into Safe Browsing Extended
-// Reporting v2. Takes effect only if execution mode is not
-// ExecutionMode::kNone.
-extern const char kEnableCleanerLoggingSwitch[];
-
// Indicates that crash reporting is enabled for the current user.
extern const char kEnableCrashReportingSwitch[];
@@ -54,6 +48,7 @@ extern const char kEngineSwitch[];
extern const char kExecutionModeSwitch[];
// Indicates that the current user opted into Safe Browsing Extended Reporting.
+// This should not be used by non-legacy-mode Chrome Cleanup Tool.
extern const char kExtendedSafeBrowsingEnabledSwitch[];
// Specifies the suffix to the registry path where metrics data will be saved.
@@ -79,6 +74,9 @@ extern const wchar_t kCleanerSubKey[];
// The suffix for registry key paths where scan times will be written to.
extern const wchar_t kScanTimesSubKey[];
+// Registry value names that indicate if a cleanup has completed.
+extern const wchar_t kCleanupCompletedValueName[];
+
// Registry value names where metrics are written to.
extern const wchar_t kEndTimeValueName[];
extern const wchar_t kEngineErrorCodeValueName[];
@@ -112,17 +110,15 @@ enum class ExecutionMode {
// will show its own UI and handle logs uploading permissions.
kNone = 0,
// The cleaner will run in scanning mode. No UI will be shown to the user
- // (UI handled by Chrome) and logs will only be uploaded if the user opted
- // into Extended Safe Browsing Reporting.
+ // (UI handled by Chrome) and logs will not be uploaded.
kScanning = 1,
- // The cleaner will run in cleanup mode only. No UI will be shown to the
- // user (UI handled by Chrome) and logs will only be uploaded if the user
- // opted into Extended Safe Browsing Reporting v2.
+ // The cleaner will run in cleaning mode. No UI will be shown to the user
+ // (UI handled by Chrome) and logs will be uploaded if the user did not opt
+ // out of logs collection when it was offered by the Chrome UI.
+ // Chrome should not try to launch the Chrome Cleanup Tool with |kCleanup|.
+ // It should instead communicate through IPC with the cleaner launched with
+ // |kScanning| to ask it to start cleanup.
kCleanup = 2,
- // The cleaner will run in post-reboot validation mode. No UI will be shown
- // to the user and logs will only be uploaded if the user opted into Extended
- // Safe Browsing Reporting v2.
- kPostRebootValidation = 3,
// Auxiliary enumerator for range checking.
kNumValues,
diff --git a/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom b/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
index b10a3a1db85..0208967f5a8 100644
--- a/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
+++ b/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
@@ -4,71 +4,41 @@
module chrome_cleaner.mojom;
-import "mojo/common/file_path.mojom";
+// IMPORTANT NOTE: Avoid adding dependencies to typemapped .mojom files.
+// Enabling typemaps currently (as of July 2017) requires indirectly depending
+// on all existing typemap definitions. The Chrome Cleaner is built
+// independently from Chromium and would like to avoid these dependencies.
-// The behaviours that have been observed for a given UwS.
-struct ObservedBehaviours {
- bool ad_injector;
- bool settings_hijacker;
- bool extensions_injector;
- bool dropper;
-};
-
-// Information about removable Unwanted Software matched by the Software
-// Reporter to be shown in the Chrome prompt.
-struct UwS {
- // The id of this unwanted software.
- int32 id;
-
- // The name of this unwanted software.
- string name;
-
- // Behaviors observed for this UwS to be presented to the user in the Chrome
- // prompt.
- ObservedBehaviours observed_behaviours;
-
- // List of fully-qualified paths of the files that will be deleted by the
- // Chrome Cleanup Tool for this unwanted software.
- array<mojo.common.mojom.FilePath> files_to_delete;
-};
-
-// Indicates if elevation will be required for cleanup.
-[Extensible]
-enum ElevationStatus {
- NOT_REQUIRED = 0,
- REQUIRED = 1,
-};
+// Once it's possible to specify a limited subset of typemaps to use, be
+// careful not to add dependencies to [Native] mojo structures. The wire format
+// for [Native] structs is not guaranteed to be consistent between versions.
[Extensible]
enum PromptAcceptance {
UNSPECIFIED = 0,
- // The Chrome prompt was not shown to the user (for example, due to an
- // experiment run that shouldn't prompt the user or because the user has
- // been prompted recently).
- NOT_SHOWN = 1,
- // The user explicitly accepted the Chrome prompt, but didn't opt into
- // uploading logs to Google.
+ // The user explicitly accepted the cleanup operation and cleaner logs
+ // upload is allowed.
+ ACCEPTED_WITH_LOGS = 1,
+ // The user explicitly accepted the cleanup operation and cleaner logs
+ // upload is not allowed.
ACCEPTED_WITHOUT_LOGS = 2,
- // The user explicitly accepted the Chrome prompt and also opted into
- // uploading logs to Google.
- ACCEPTED_WITH_LOGS = 3,
// The user explicitly denied the Chrome prompt.
- DENIED = 4,
- // The user didn't interact with the Chrome prompt after a while.
- IGNORED = 5,
+ DENIED = 3,
+ NUM_VALUES,
+};
+
+struct FilePath {
+ array<uint16> value;
};
-// Service provided by Chrome to prompt the user to run the Chrome Cleanup Tool
-// if unwanted software is detected on the system. This service is used by the
-// Software Reporter Tool so that all user interaction is provided by Chrome.
+// 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 {
// Params:
- // - removable_uws_found: the list of UwS detected by the reporter;
- // - elevation_status: if the cleaner will need to run in elevated mode.
+ // - removable_uws_found: list of fully-qualified paths of the files that
+ // will be deleted by the Chrome Cleanup Tool.
// Returns:
- // - prompt_acceptance: indicates if the user accepted the prompt; if the
- // prompt is accepted, it also indicates if logs
- // uploading is allowed.
- PromptUser(array<UwS> removable_uws_found, ElevationStatus elevation_status)
+ // - prompt_acceptance: indicates if the user accepted the prompt.
+ PromptUser(array<FilePath> files_to_delete)
=> (PromptAcceptance prompt_acceptance);
};
diff --git a/chromium/components/chrome_cleaner/public/typemaps/DEPS b/chromium/components/chrome_cleaner/public/typemaps/DEPS
new file mode 100644
index 00000000000..e9aaaf9ce4c
--- /dev/null
+++ b/chromium/components/chrome_cleaner/public/typemaps/DEPS
@@ -0,0 +1,4 @@
+# Allow the typemaps to access their dependencies.
+include_rules = [
+ '+base/files/file_path.h',
+]
diff --git a/chromium/components/chrome_cleaner/public/typemaps/OWNERS b/chromium/components/chrome_cleaner/public/typemaps/OWNERS
new file mode 100644
index 00000000000..adcd752ea96
--- /dev/null
+++ b/chromium/components/chrome_cleaner/public/typemaps/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+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/chrome_cleaner/public/typemaps/chrome_prompt.typemap b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
new file mode 100644
index 00000000000..ec34d5ea537
--- /dev/null
+++ b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
@@ -0,0 +1,12 @@
+# 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.
+
+mojom = "//components/chrome_cleaner/public/interfaces/chrome_prompt.mojom"
+public_headers = [ "//base/files/file_path.h" ]
+traits_headers = [ "//components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h" ]
+sources = [
+ "//components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc",
+]
+
+type_mappings = [ "chrome_cleaner.mojom.FilePath=base::FilePath" ]
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
new file mode 100644
index 00000000000..d15c172ebef
--- /dev/null
+++ b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
@@ -0,0 +1,41 @@
+// 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/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h"
+
+namespace mojo {
+
+// static
+ConstCArray<uint16_t>
+StructTraits<chrome_cleaner::mojom::FilePathDataView, base::FilePath>::value(
+ const base::FilePath& file_path) {
+#if defined(OS_WIN)
+ return ConstCArray<uint16_t>(
+ file_path.value().size(),
+ reinterpret_cast<const uint16_t*>(file_path.value().data()));
+#else
+ NOTREACHED();
+ return ConstCArray<uint16_t>();
+#endif
+}
+
+// static
+bool StructTraits<chrome_cleaner::mojom::FilePathDataView,
+ base::FilePath>::Read(chrome_cleaner::mojom::FilePathDataView
+ path_view,
+ base::FilePath* out) {
+#if defined(OS_WIN)
+ ArrayDataView<uint16_t> view;
+ path_view.GetValueDataView(&view);
+ base::FilePath path = base::FilePath(base::string16(
+ reinterpret_cast<const base::char16*>(view.data()), view.size()));
+ *out = std::move(path);
+ 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
new file mode 100644
index 00000000000..76d123f9336
--- /dev/null
+++ b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
@@ -0,0 +1,22 @@
+// 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_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
+#define COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
+
+#include "base/files/file_path.h"
+#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<chrome_cleaner::mojom::FilePathDataView, base::FilePath> {
+ static ConstCArray<uint16_t> value(const base::FilePath& file_path);
+ static bool Read(chrome_cleaner::mojom::FilePathDataView path_view,
+ base::FilePath* out);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
diff --git a/chromium/components/cloud_devices/common/cloud_device_description.cc b/chromium/components/cloud_devices/common/cloud_device_description.cc
index 56cbb463bcc..75518b22ccc 100644
--- a/chromium/components/cloud_devices/common/cloud_device_description.cc
+++ b/chromium/components/cloud_devices/common/cloud_device_description.cc
@@ -63,9 +63,7 @@ const base::DictionaryValue* CloudDeviceDescription::GetItem(
base::DictionaryValue* CloudDeviceDescription::CreateItem(
const std::string& path) {
- base::DictionaryValue* value = new base::DictionaryValue;
- root_->Set(path, value);
- return value;
+ return root_->SetDictionary(path, base::MakeUnique<base::DictionaryValue>());
}
const base::ListValue* CloudDeviceDescription::GetListItem(
@@ -77,9 +75,7 @@ const base::ListValue* CloudDeviceDescription::GetListItem(
base::ListValue* CloudDeviceDescription::CreateListItem(
const std::string& path) {
- base::ListValue* value = new base::ListValue;
- root_->Set(path, value);
- return value;
+ return root_->SetList(path, base::MakeUnique<base::ListValue>());
}
} // namespace cloud_devices
diff --git a/chromium/components/cloud_devices/common/description_items.h b/chromium/components/cloud_devices/common/description_items.h
index fa15668e56c..8ceff58275d 100644
--- a/chromium/components/cloud_devices/common/description_items.h
+++ b/chromium/components/cloud_devices/common/description_items.h
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
#include "components/cloud_devices/common/cloud_device_description.h"
namespace base {
@@ -63,8 +64,7 @@ class ListCapability {
const Option& operator[](size_t i) const { return options_[i]; }
bool Contains(const Option& option) const {
- return std::find(options_.begin(), options_.end(), option) !=
- options_.end();
+ return base::ContainsValue(options_, option);
}
void AddOption(const Option& option) { options_.push_back(option); }
@@ -104,8 +104,7 @@ class SelectionCapability {
const Option& operator[](size_t i) const { return options_[i]; }
bool Contains(const Option& option) const {
- return std::find(options_.begin(), options_.end(), option) !=
- options_.end();
+ return base::ContainsValue(options_, option);
}
const Option& GetDefault() const {
diff --git a/chromium/components/cloud_devices/common/description_items_inl.h b/chromium/components/cloud_devices/common/description_items_inl.h
index 3c303105328..02fc0237253 100644
--- a/chromium/components/cloud_devices/common/description_items_inl.h
+++ b/chromium/components/cloud_devices/common/description_items_inl.h
@@ -11,7 +11,9 @@
#include <utility>
#include <vector>
+#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
+#include "base/values.h"
#include "components/cloud_devices/common/description_items.h"
// Implementation of templates defined in header file.
@@ -126,17 +128,16 @@ template <class Option, class Traits>
void SelectionCapability<Option, Traits>::SaveTo(
CloudDeviceDescription* description) const {
DCHECK(IsValid());
- base::ListValue* options_list = new base::ListValue;
- description->CreateItem(Traits::GetCapabilityPath())
- ->Set(json::kKeyOption, options_list);
+ auto options_list = base::MakeUnique<base::ListValue>();
for (size_t i = 0; i < options_.size(); ++i) {
- std::unique_ptr<base::DictionaryValue> option_value(
- new base::DictionaryValue);
+ auto option_value = base::MakeUnique<base::DictionaryValue>();
if (base::checked_cast<int>(i) == default_idx_)
option_value->SetBoolean(json::kKeyIsDefault, true);
Traits::Save(options_[i], option_value.get());
options_list->Append(std::move(option_value));
}
+ description->CreateItem(Traits::GetCapabilityPath())
+ ->Set(json::kKeyOption, std::move(options_list));
}
template <class Traits>
diff --git a/chromium/components/cloud_devices/common/printer_description.cc b/chromium/components/cloud_devices/common/printer_description.cc
index aa7fa38d517..3cff18981a9 100644
--- a/chromium/components/cloud_devices/common/printer_description.cc
+++ b/chromium/components/cloud_devices/common/printer_description.cc
@@ -13,6 +13,7 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "components/cloud_devices/common/cloud_device_description_consts.h"
@@ -733,16 +734,15 @@ class PageRangeTraits : public ItemsTraits<kOptionPageRange> {
static void Save(const PageRange& option, base::DictionaryValue* dict) {
if (!option.empty()) {
- base::ListValue* list = new base::ListValue;
- dict->Set(kPageRangeInterval, list);
+ auto list = base::MakeUnique<base::ListValue>();
for (size_t i = 0; i < option.size(); ++i) {
- std::unique_ptr<base::DictionaryValue> interval(
- new base::DictionaryValue);
+ auto interval = base::MakeUnique<base::DictionaryValue>();
interval->SetInteger(kPageRangeStart, option[i].start);
if (option[i].end < kMaxPageNumber)
interval->SetInteger(kPageRangeEnd, option[i].end);
list->Append(std::move(interval));
}
+ dict->Set(kPageRangeInterval, std::move(list));
}
}
};
diff --git a/chromium/components/component_updater/component_updater_service_unittest.cc b/chromium/components/component_updater/component_updater_service_unittest.cc
index 8cf31307d4d..7d703c4370b 100644
--- a/chromium/components/component_updater/component_updater_service_unittest.cc
+++ b/chromium/components/component_updater/component_updater_service_unittest.cc
@@ -5,6 +5,7 @@
#include "components/component_updater/component_updater_service.h"
#include <limits>
+#include <memory>
#include <string>
#include <vector>
@@ -47,9 +48,16 @@ class MockInstaller : public CrxInstaller {
public:
MockInstaller();
+ // gMock does not support mocking functions with parameters which have
+ // move semantics. This function is a shim to work around it.
+ Result Install(std::unique_ptr<base::DictionaryValue> manifest,
+ const base::FilePath& unpack_path) {
+ return Install_(manifest, unpack_path);
+ }
+
MOCK_METHOD1(OnUpdateError, void(int error));
- MOCK_METHOD2(Install,
- Result(const base::DictionaryValue& manifest,
+ MOCK_METHOD2(Install_,
+ Result(const std::unique_ptr<base::DictionaryValue>& manifest,
const base::FilePath& unpack_path));
MOCK_METHOD2(GetInstalledFile,
bool(const std::string& file, base::FilePath* installed_file));
diff --git a/chromium/components/component_updater/configurator_impl.cc b/chromium/components/component_updater/configurator_impl.cc
index 291b25a3bb8..efc46c515a6 100644
--- a/chromium/components/component_updater/configurator_impl.cc
+++ b/chromium/components/component_updater/configurator_impl.cc
@@ -10,6 +10,7 @@
#include "base/command_line.h"
#include "base/feature_list.h"
+#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/version.h"
@@ -56,14 +57,7 @@ const char kSwitchDisableBackgroundDownloads[] = "disable-background-downloads";
#endif // defined(OS_WIN)
const base::Feature kAlternateComponentUrls{"AlternateComponentUrls",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Returns true if and only if |test| is contained in |vec|.
-bool HasSwitchValue(const std::vector<std::string>& vec, const char* test) {
- if (vec.empty())
- return 0;
- return (std::find(vec.begin(), vec.end(), test) != vec.end());
-}
+ base::FEATURE_ENABLED_BY_DEFAULT};
// If there is an element of |vec| of the form |test|=.*, returns the right-
// hand side of that assignment. Otherwise, returns an empty string.
@@ -101,13 +95,14 @@ ConfiguratorImpl::ConfiguratorImpl(
std::vector<std::string> switch_values = base::SplitString(
cmdline->GetSwitchValueASCII(switches::kComponentUpdater), ",",
base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- fast_update_ = HasSwitchValue(switch_values, kSwitchFastUpdate);
- pings_enabled_ = !HasSwitchValue(switch_values, kSwitchDisablePings);
- deltas_enabled_ = !HasSwitchValue(switch_values, kSwitchDisableDeltaUpdates);
+ fast_update_ = base::ContainsValue(switch_values, kSwitchFastUpdate);
+ pings_enabled_ = !base::ContainsValue(switch_values, kSwitchDisablePings);
+ deltas_enabled_ =
+ !base::ContainsValue(switch_values, kSwitchDisableDeltaUpdates);
#if defined(OS_WIN)
background_downloads_enabled_ =
- !HasSwitchValue(switch_values, kSwitchDisableBackgroundDownloads);
+ !base::ContainsValue(switch_values, kSwitchDisableBackgroundDownloads);
#else
background_downloads_enabled_ = false;
#endif
@@ -119,7 +114,7 @@ ConfiguratorImpl::ConfiguratorImpl(
DCHECK(url_source_override_.is_valid());
}
- if (HasSwitchValue(switch_values, kSwitchRequestParam))
+ if (base::ContainsValue(switch_values, kSwitchRequestParam))
extra_info_ += "testrequest=\"1\"";
}
@@ -202,4 +197,11 @@ bool ConfiguratorImpl::EnabledCupSigning() const {
return true;
}
+std::vector<uint8_t> ConfiguratorImpl::GetRunActionKeyHash() const {
+ return std::vector<uint8_t>{0x5f, 0x94, 0xe0, 0x3c, 0x64, 0x30, 0x9f, 0xbc,
+ 0xfe, 0x00, 0x9a, 0x27, 0x3e, 0x52, 0xbf, 0xa5,
+ 0x84, 0xb9, 0xb3, 0x75, 0x07, 0x29, 0xde, 0xfa,
+ 0x32, 0x76, 0xd9, 0x93, 0xb5, 0xa3, 0xce, 0x02};
+}
+
} // namespace component_updater
diff --git a/chromium/components/component_updater/configurator_impl.h b/chromium/components/component_updater/configurator_impl.h
index bd0ea95ea9a..afc3d2d3fb6 100644
--- a/chromium/components/component_updater/configurator_impl.h
+++ b/chromium/components/component_updater/configurator_impl.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_COMPONENT_UPDATER_CONFIGURATOR_IMPL_H_
#define COMPONENTS_COMPONENT_UPDATER_CONFIGURATOR_IMPL_H_
+#include <stdint.h>
+
#include <string>
#include <vector>
@@ -85,6 +87,9 @@ class ConfiguratorImpl {
// True if signing of update checks is enabled.
bool EnabledCupSigning() const;
+ // Returns the key hash corresponding to a CRX trusted by ActionRun.
+ std::vector<uint8_t> GetRunActionKeyHash() const;
+
private:
net::URLRequestContextGetter* url_request_getter_;
std::string extra_info_;
diff --git a/chromium/components/component_updater/default_component_installer.cc b/chromium/components/component_updater/default_component_installer.cc
index 6cdca7edac5..c06c10c903b 100644
--- a/chromium/components/component_updater/default_component_installer.cc
+++ b/chromium/components/component_updater/default_component_installer.cc
@@ -11,7 +11,9 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
#include "base/location.h"
+#include "base/macros.h"
#include "base/path_service.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
@@ -39,8 +41,12 @@ using InstallError = update_client::InstallError;
} // namespace
-ComponentInstallerTraits::~ComponentInstallerTraits() {
-}
+ComponentInstallerTraits::~ComponentInstallerTraits() {}
+
+DefaultComponentInstaller::RegistrationInfo::RegistrationInfo()
+ : version(kNullVersion) {}
+
+DefaultComponentInstaller::RegistrationInfo::~RegistrationInfo() = default;
DefaultComponentInstaller::DefaultComponentInstaller(
std::unique_ptr<ComponentInstallerTraits> installer_traits)
@@ -63,12 +69,14 @@ void DefaultComponentInstaller::Register(
<< "has no installer traits.";
return;
}
+
+ auto registration_info = base::MakeRefCounted<RegistrationInfo>();
task_runner_->PostTaskAndReply(
FROM_HERE,
- base::Bind(&DefaultComponentInstaller::StartRegistration,
- this, cus),
- base::Bind(&DefaultComponentInstaller::FinishRegistration,
- this, cus, callback));
+ base::Bind(&DefaultComponentInstaller::StartRegistration, this,
+ registration_info, cus),
+ base::Bind(&DefaultComponentInstaller::FinishRegistration, this,
+ registration_info, cus, callback));
}
void DefaultComponentInstaller::OnUpdateError(int error) {
@@ -107,15 +115,21 @@ Result DefaultComponentInstaller::InstallHelper(
return Result(InstallError::NONE);
}
-Result DefaultComponentInstaller::Install(const base::DictionaryValue& manifest,
- const base::FilePath& unpack_path) {
+Result DefaultComponentInstaller::Install(
+ std::unique_ptr<base::DictionaryValue> manifest,
+ const base::FilePath& unpack_path) {
std::string manifest_version;
- manifest.GetStringASCII("version", &manifest_version);
+ manifest->GetStringASCII("version", &manifest_version);
base::Version version(manifest_version);
VLOG(1) << "Install: version=" << version.GetString()
<< " current version=" << current_version_.GetString();
+ // Take the ownership of the |unpack_path| to enforce its deletion.
+ DCHECK(DirectoryExists(unpack_path));
+ base::ScopedTempDir unpack_path_owner;
+ ignore_result(unpack_path_owner.Set(unpack_path));
+
if (!version.IsValid())
return Result(InstallError::INVALID_VERSION);
if (current_version_.CompareTo(version) > 0)
@@ -129,22 +143,22 @@ Result DefaultComponentInstaller::Install(const base::DictionaryValue& manifest,
if (!base::DeleteFile(install_path, true))
return Result(InstallError::CLEAN_INSTALL_DIR_FAILED);
}
- const auto result = InstallHelper(manifest, unpack_path, install_path);
+ const auto result = InstallHelper(*manifest, unpack_path, install_path);
if (result.error) {
base::DeleteFile(install_path, true);
return result;
}
+
+ // If install has been successful, the installer has deleted the unpack path.
+ DCHECK(!base::PathExists(unpack_path));
+
current_version_ = version;
current_install_dir_ = install_path;
- // TODO(ddorwin): Change parameter to std::unique_ptr<base::DictionaryValue>
- // so we can avoid this DeepCopy.
- current_manifest_.reset(manifest.DeepCopy());
- std::unique_ptr<base::DictionaryValue> manifest_copy(
- current_manifest_->DeepCopy());
+
main_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&DefaultComponentInstaller::ComponentReady,
- this, base::Passed(&manifest_copy)));
+ FROM_HERE, base::Bind(&DefaultComponentInstaller::ComponentReady, this,
+ base::Passed(std::move(manifest))));
+
return result;
}
@@ -166,7 +180,8 @@ bool DefaultComponentInstaller::Uninstall() {
}
bool DefaultComponentInstaller::FindPreinstallation(
- const base::FilePath& root) {
+ const base::FilePath& root,
+ const scoped_refptr<RegistrationInfo>& registration_info) {
base::FilePath path = root.Append(installer_traits_->GetRelativeInstallDir());
if (!base::PathExists(path)) {
DVLOG(1) << "Relative install dir does not exist: " << path.MaybeAsASCII();
@@ -201,32 +216,36 @@ bool DefaultComponentInstaller::FindPreinstallation(
<< " at " << path.MaybeAsASCII() << " with version " << version
<< ".";
- current_install_dir_ = path;
- current_manifest_ = std::move(manifest);
- current_version_ = version;
+ registration_info->install_dir = path;
+ registration_info->version = version;
+ registration_info->manifest = std::move(manifest);
+
return true;
}
-void DefaultComponentInstaller::StartRegistration(ComponentUpdateService* cus) {
+void DefaultComponentInstaller::StartRegistration(
+ const scoped_refptr<RegistrationInfo>& registration_info,
+ ComponentUpdateService* cus) {
VLOG(1) << __func__ << " for " << installer_traits_->GetName();
DCHECK(task_runner_.get());
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
base::Version latest_version(kNullVersion);
// First check for an installation set up alongside Chrome itself.
base::FilePath root;
if (PathService::Get(DIR_COMPONENT_PREINSTALLED, &root) &&
- FindPreinstallation(root)) {
- latest_version = current_version_;
+ FindPreinstallation(root, registration_info)) {
+ latest_version = registration_info->version;
}
// If there is a distinct alternate root, check there as well, and override
// anything found in the basic root.
base::FilePath root_alternate;
if (PathService::Get(DIR_COMPONENT_PREINSTALLED_ALT, &root_alternate) &&
- root != root_alternate && FindPreinstallation(root_alternate)) {
- latest_version = current_version_;
+ root != root_alternate &&
+ FindPreinstallation(root_alternate, registration_info)) {
+ latest_version = registration_info->version;
}
// Then check for a higher-versioned user-wide installation.
@@ -291,13 +310,11 @@ void DefaultComponentInstaller::StartRegistration(ComponentUpdateService* cus) {
}
if (latest_manifest) {
- current_version_ = latest_version;
- current_manifest_ = std::move(latest_manifest);
- current_install_dir_ = latest_path;
- // TODO(ddorwin): Remove these members and pass them directly to
- // FinishRegistration().
+ registration_info->version = latest_version;
+ registration_info->manifest = std::move(latest_manifest);
+ registration_info->install_dir = latest_path;
base::ReadFileToString(latest_path.AppendASCII("manifest.fingerprint"),
- &current_fingerprint_);
+ &registration_info->fingerprint);
}
// Remove older versions of the component. None should be in use during
@@ -308,7 +325,7 @@ void DefaultComponentInstaller::StartRegistration(ComponentUpdateService* cus) {
void DefaultComponentInstaller::UninstallOnTaskRunner() {
DCHECK(task_runner_.get());
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
// Only try to delete any files that are in our user-level install path.
base::FilePath userInstallPath;
@@ -335,17 +352,22 @@ void DefaultComponentInstaller::UninstallOnTaskRunner() {
// Delete the base directory if it's empty now.
if (base::IsDirectoryEmpty(base_dir)) {
- if (base::DeleteFile(base_dir, false))
+ if (!base::DeleteFile(base_dir, false))
DLOG(ERROR) << "Couldn't delete " << base_dir.value();
}
}
void DefaultComponentInstaller::FinishRegistration(
+ const scoped_refptr<RegistrationInfo>& registration_info,
ComponentUpdateService* cus,
const base::Closure& callback) {
VLOG(1) << __func__ << " for " << installer_traits_->GetName();
DCHECK(thread_checker_.CalledOnValidThread());
+ current_install_dir_ = registration_info->install_dir;
+ current_version_ = registration_info->version;
+ current_fingerprint_ = registration_info->fingerprint;
+
update_client::CrxComponent crx;
installer_traits_->GetHash(&crx.pk_hash);
crx.installer = this;
@@ -368,14 +390,12 @@ void DefaultComponentInstaller::FinishRegistration(
if (!callback.is_null())
callback.Run();
- if (!current_manifest_) {
+ if (!registration_info->manifest) {
DVLOG(1) << "No component found for " << installer_traits_->GetName();
return;
}
- std::unique_ptr<base::DictionaryValue> manifest_copy(
- current_manifest_->DeepCopy());
- ComponentReady(std::move(manifest_copy));
+ ComponentReady(std::move(registration_info->manifest));
}
void DefaultComponentInstaller::ComponentReady(
diff --git a/chromium/components/component_updater/default_component_installer.h b/chromium/components/component_updater/default_component_installer.h
index 61e6c56e767..392186ed9ba 100644
--- a/chromium/components/component_updater/default_component_installer.h
+++ b/chromium/components/component_updater/default_component_installer.h
@@ -117,7 +117,7 @@ class DefaultComponentInstaller : public update_client::CrxInstaller {
// Overridden from ComponentInstaller:
void OnUpdateError(int error) override;
update_client::CrxInstaller::Result Install(
- const base::DictionaryValue& manifest,
+ std::unique_ptr<base::DictionaryValue> manifest,
const base::FilePath& unpack_path) override;
bool GetInstalledFile(const std::string& file,
base::FilePath* installed_file) override;
@@ -125,27 +125,49 @@ class DefaultComponentInstaller : public update_client::CrxInstaller {
bool Uninstall() override;
private:
+ struct RegistrationInfo : base::RefCountedThreadSafe<RegistrationInfo> {
+ RegistrationInfo();
+
+ base::FilePath install_dir;
+ base::Version version;
+ std::string fingerprint;
+ std::unique_ptr<base::DictionaryValue> manifest;
+
+ private:
+ friend class base::RefCountedThreadSafe<RegistrationInfo>;
+
+ ~RegistrationInfo();
+
+ DISALLOW_COPY_AND_ASSIGN(RegistrationInfo);
+ };
+
~DefaultComponentInstaller() override;
// If there is a installation of the component set up alongside Chrome's
// files (as opposed to in the user data directory), sets current_* to the
// values associated with that installation and returns true; otherwise,
// returns false.
- bool FindPreinstallation(const base::FilePath& root);
+ bool FindPreinstallation(
+ const base::FilePath& root,
+ const scoped_refptr<RegistrationInfo>& registration_info);
update_client::CrxInstaller::Result InstallHelper(
const base::DictionaryValue& manifest,
const base::FilePath& unpack_path,
const base::FilePath& install_path);
- void StartRegistration(ComponentUpdateService* cus);
- void FinishRegistration(ComponentUpdateService* cus,
- const base::Closure& callback);
+ void StartRegistration(
+ const scoped_refptr<RegistrationInfo>& registration_info,
+ ComponentUpdateService* cus);
+ void FinishRegistration(
+ const scoped_refptr<RegistrationInfo>& registration_info,
+ ComponentUpdateService* cus,
+ const base::Closure& callback);
void ComponentReady(std::unique_ptr<base::DictionaryValue> manifest);
void UninstallOnTaskRunner();
base::FilePath current_install_dir_;
base::Version current_version_;
std::string current_fingerprint_;
- std::unique_ptr<base::DictionaryValue> current_manifest_;
+
std::unique_ptr<ComponentInstallerTraits> installer_traits_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chromium/components/component_updater/default_component_installer_unittest.cc b/chromium/components/component_updater/default_component_installer_unittest.cc
index 278e3aa36c2..70780726b23 100644
--- a/chromium/components/component_updater/default_component_installer_unittest.cc
+++ b/chromium/components/component_updater/default_component_installer_unittest.cc
@@ -8,16 +8,23 @@
#include <vector>
#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_path_override.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/version.h"
+#include "components/component_updater/component_updater_paths.h"
#include "components/component_updater/component_updater_service.h"
#include "components/component_updater/component_updater_service_internal.h"
#include "components/component_updater/default_component_installer.h"
+#include "components/update_client/component_unpacker.h"
#include "components/update_client/crx_update_item.h"
#include "components/update_client/test_configurator.h"
#include "components/update_client/update_client.h"
@@ -25,6 +32,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using ComponentUnpacker = update_client::ComponentUnpacker;
using Configurator = update_client::Configurator;
using CrxUpdateItem = update_client::CrxUpdateItem;
using TestConfigurator = update_client::TestConfigurator;
@@ -43,6 +51,19 @@ const uint8_t kSha256Hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec,
0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4,
0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
+constexpr base::FilePath::CharType relative_install_dir[] =
+ FILE_PATH_LITERAL("fake");
+
+base::FilePath test_file(const char* file) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return path.AppendASCII("components")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("update_client")
+ .AppendASCII(file);
+}
+
class MockUpdateClient : public UpdateClient {
public:
MockUpdateClient() {}
@@ -72,6 +93,7 @@ class MockUpdateClient : public UpdateClient {
class FakeInstallerTraits : public ComponentInstallerTraits {
public:
+ FakeInstallerTraits() {}
~FakeInstallerTraits() override {}
bool VerifyInstallation(const base::DictionaryValue& manifest,
@@ -97,7 +119,7 @@ class FakeInstallerTraits : public ComponentInstallerTraits {
std::unique_ptr<base::DictionaryValue> manifest) override {}
base::FilePath GetRelativeInstallDir() const override {
- return base::FilePath(FILE_PATH_LITERAL("fake"));
+ return base::FilePath(relative_install_dir);
}
void GetHash(std::vector<uint8_t>* hash) const override { GetPkHash(hash); }
@@ -135,15 +157,22 @@ class DefaultComponentInstallerTest : public testing::Test {
protected:
void RunThreads();
+ void Unpack(const base::FilePath& crx_path);
+ ComponentUnpacker::Result result() const { return result_; }
private:
+ void UnpackComplete(const ComponentUnpacker::Result& result);
+
base::test::ScopedTaskEnvironment scoped_task_environment_;
+ const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ =
+ base::ThreadTaskRunnerHandle::Get();
base::RunLoop runloop_;
base::Closure quit_closure_;
scoped_refptr<TestConfigurator> config_;
scoped_refptr<MockUpdateClient> update_client_;
std::unique_ptr<ComponentUpdateService> component_updater_;
+ ComponentUnpacker::Result result_;
};
DefaultComponentInstallerTest::DefaultComponentInstallerTest()
@@ -151,13 +180,14 @@ DefaultComponentInstallerTest::DefaultComponentInstallerTest()
base::test::ScopedTaskEnvironment::MainThreadType::UI) {
quit_closure_ = runloop_.QuitClosure();
- config_ = new TestConfigurator(
+ config_ = base::MakeRefCounted<TestConfigurator>(
base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}),
base::ThreadTaskRunnerHandle::Get());
- update_client_ = new MockUpdateClient();
+ update_client_ = base::MakeRefCounted<MockUpdateClient>();
EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
- component_updater_.reset(new CrxUpdateService(config_, update_client_));
+ component_updater_ =
+ base::MakeUnique<CrxUpdateService>(config_, update_client_);
}
DefaultComponentInstallerTest::~DefaultComponentInstallerTest() {
@@ -169,6 +199,25 @@ void DefaultComponentInstallerTest::RunThreads() {
runloop_.Run();
}
+void DefaultComponentInstallerTest::Unpack(const base::FilePath& crx_path) {
+ auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>(
+ std::vector<uint8_t>(std::begin(kSha256Hash), std::end(kSha256Hash)),
+ crx_path, nullptr, nullptr, config_->GetSequencedTaskRunner());
+ component_unpacker->Unpack(base::Bind(
+ &DefaultComponentInstallerTest::UnpackComplete, base::Unretained(this)));
+ RunThreads();
+}
+
+void DefaultComponentInstallerTest::UnpackComplete(
+ const ComponentUnpacker::Result& result) {
+ result_ = result;
+
+ EXPECT_EQ(update_client::UnpackerError::kNone, result_.error);
+ EXPECT_EQ(0, result_.extended_error);
+
+ main_thread_task_runner_->PostTask(FROM_HERE, quit_closure_);
+}
+
} // namespace
// Tests that the component metadata is propagated from the default
@@ -195,6 +244,8 @@ TEST_F(DefaultComponentInstallerTest, RegisterComponent) {
base::Closure quit_closure_;
};
+ base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
+
const std::string id("jebgalgnebhfojomionfpkfelancnnkf");
// Quit after one update check has been fired.
@@ -205,10 +256,8 @@ TEST_F(DefaultComponentInstallerTest, RegisterComponent) {
EXPECT_CALL(update_client(), GetCrxUpdateState(id, _)).Times(1);
EXPECT_CALL(update_client(), Stop()).Times(1);
- std::unique_ptr<ComponentInstallerTraits> traits(new FakeInstallerTraits());
-
- DefaultComponentInstaller* installer =
- new DefaultComponentInstaller(std::move(traits));
+ auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
+ base::MakeUnique<FakeInstallerTraits>());
installer->Register(component_updater(), base::Closure());
RunThreads();
@@ -232,4 +281,55 @@ TEST_F(DefaultComponentInstallerTest, RegisterComponent) {
EXPECT_TRUE(component.supports_group_policy_enable_component_updates);
}
+// Tests that the unpack path is removed when the install succeeded.
+TEST_F(DefaultComponentInstallerTest, UnpackPathInstallSuccess) {
+ auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
+ base::MakeUnique<FakeInstallerTraits>());
+
+ Unpack(test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
+ const auto unpack_path = result().unpack_path;
+ EXPECT_TRUE(base::DirectoryExists(unpack_path));
+
+ auto manifest = update_client::ReadManifest(unpack_path);
+
+ base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
+ base::FilePath base_dir;
+ EXPECT_TRUE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
+ base_dir = base_dir.Append(relative_install_dir);
+ EXPECT_TRUE(base::CreateDirectory(base_dir));
+ const auto result = installer->Install(std::move(manifest), unpack_path);
+ EXPECT_EQ(0, result.error);
+ EXPECT_FALSE(base::PathExists(unpack_path));
+
+ EXPECT_CALL(update_client(), Stop()).Times(1);
+}
+
+// Tests that the unpack path is removed when the install failed.
+TEST_F(DefaultComponentInstallerTest, UnpackPathInstallError) {
+ auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
+ base::MakeUnique<FakeInstallerTraits>());
+
+ Unpack(test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
+ const auto unpack_path = result().unpack_path;
+ EXPECT_TRUE(base::DirectoryExists(unpack_path));
+
+ auto manifest = update_client::ReadManifest(unpack_path);
+
+ // Test the precondition that DIR_COMPONENT_USER is not registered with
+ // the path service.
+ base::FilePath base_dir;
+ EXPECT_FALSE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
+
+ // Calling |Install| fails since DIR_COMPONENT_USER does not exist.
+ const auto result = installer->Install(std::move(manifest), unpack_path);
+ EXPECT_EQ(
+ static_cast<int>(update_client::InstallError::NO_DIR_COMPONENT_USER),
+ result.error);
+ EXPECT_FALSE(base::PathExists(unpack_path));
+
+ EXPECT_CALL(update_client(), Stop()).Times(1);
+}
+
} // namespace component_updater
diff --git a/chromium/components/components_strings.grd b/chromium/components/components_strings.grd
index 9b42f8965c7..d29cca930b1 100644
--- a/chromium/components/components_strings.grd
+++ b/chromium/components/components_strings.grd
@@ -206,6 +206,7 @@
<part file="pdf_strings.grdp" />
<part file="physical_web_ui_strings.grdp" />
<part file="policy_strings.grdp" />
+ <part file="safe_browsing_strings.grdp" />
<part file="security_interstitials_strings.grdp" />
<part file="security_state_strings.grdp" />
<part file="ssl_errors_strings.grdp" />
diff --git a/chromium/components/constrained_window/BUILD.gn b/chromium/components/constrained_window/BUILD.gn
index a62c55548af..d4653d8b934 100644
--- a/chromium/components/constrained_window/BUILD.gn
+++ b/chromium/components/constrained_window/BUILD.gn
@@ -37,6 +37,7 @@ static_library("constrained_window") {
if (use_aura) {
deps += [
"//ui/aura",
+ "//ui/compositor",
"//ui/wm",
]
}
diff --git a/chromium/components/constrained_window/DEPS b/chromium/components/constrained_window/DEPS
index b4fd76f3ab4..01e4dfd939f 100644
--- a/chromium/components/constrained_window/DEPS
+++ b/chromium/components/constrained_window/DEPS
@@ -4,6 +4,7 @@ include_rules = [
"+content/public/browser",
"+ui/aura",
"+ui/base",
+ "+ui/compositor",
"+ui/display",
"+ui/gfx",
"+ui/views",
diff --git a/chromium/components/constrained_window/constrained_window_views.cc b/chromium/components/constrained_window/constrained_window_views.cc
index 37de79c84e0..18398d6db60 100644
--- a/chromium/components/constrained_window/constrained_window_views.cc
+++ b/chromium/components/constrained_window/constrained_window_views.cc
@@ -24,6 +24,11 @@
#import "components/constrained_window/native_web_contents_modal_dialog_manager_views_mac.h"
#endif
+#if defined(USE_AURA)
+#include "ui/aura/window.h"
+#include "ui/compositor/dip_util.h"
+#endif
+
using web_modal::ModalDialogHost;
using web_modal::ModalDialogHostObserver;
@@ -126,6 +131,19 @@ void UpdateModalDialogPosition(views::Widget* widget,
}
widget->SetBounds(gfx::Rect(position, size));
+
+#if defined(USE_AURA)
+ if (!widget->is_top_level()) {
+ // Toplevel windows are automatiacally snapped, but CHILD windows
+ // may not. If it's not toplevel, snap the widget's layer to pixel
+ // based on the parent toplevel window, which should be snapped.
+ gfx::NativeView window = widget->GetNativeView();
+ views::Widget* toplevel =
+ views::Widget::GetTopLevelWidgetForNativeView(window->parent());
+ ui::SnapLayerToPhysicalPixelBoundary(toplevel->GetLayer(),
+ widget->GetLayer());
+ }
+#endif
}
} // namespace
diff --git a/chromium/components/content_settings/core/browser/BUILD.gn b/chromium/components/content_settings/core/browser/BUILD.gn
index 44e44e36352..2a1becd0f34 100644
--- a/chromium/components/content_settings/core/browser/BUILD.gn
+++ b/chromium/components/content_settings/core/browser/BUILD.gn
@@ -58,6 +58,10 @@ static_library("browser") {
"//url",
]
+ if (!is_ios) {
+ deps += [ "//media" ]
+ }
+
configs += [
"//build/config/compiler:no_size_t_to_int_warning",
"//build/config/compiler:wexit_time_destructors",
diff --git a/chromium/components/content_settings/core/browser/DEPS b/chromium/components/content_settings/core/browser/DEPS
index 01dbe2ae6df..aea66d24de1 100644
--- a/chromium/components/content_settings/core/browser/DEPS
+++ b/chromium/components/content_settings/core/browser/DEPS
@@ -6,6 +6,7 @@ include_rules = [
"+components/sync_preferences",
"+components/url_formatter",
"+extensions/features",
+ "+media/base/media_switches.h",
"+net/base",
"+net/cookies",
"+services/preferences/public",
diff --git a/chromium/components/content_settings/core/browser/content_settings_default_provider.cc b/chromium/components/content_settings/core/browser/content_settings_default_provider.cc
index ffa75aa7da7..d0cb24cc4cb 100644
--- a/chromium/components/content_settings/core/browser/content_settings_default_provider.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_default_provider.cc
@@ -19,6 +19,7 @@
#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"
+#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry.h"
@@ -189,8 +190,8 @@ DefaultProvider::DefaultProvider(PrefService* prefs, bool incognito)
GetPrefName(CONTENT_SETTINGS_TYPE_AUTOPLAY))),
CONTENT_SETTING_NUM_SETTINGS);
UMA_HISTOGRAM_ENUMERATION("ContentSettings.DefaultSubresourceFilterSetting",
- IntToContentSetting(prefs_->GetInteger(GetPrefName(
- CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER))),
+ IntToContentSetting(prefs_->GetInteger(
+ GetPrefName(CONTENT_SETTINGS_TYPE_ADS))),
CONTENT_SETTING_NUM_SETTINGS);
#endif
pref_change_registrar_.Init(prefs_);
diff --git a/chromium/components/content_settings/core/browser/content_settings_pref.cc b/chromium/components/content_settings/core/browser/content_settings_pref.cc
index 99e132b8151..49c2112bd3d 100644
--- a/chromium/components/content_settings/core/browser/content_settings_pref.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_pref.cc
@@ -19,6 +19,7 @@
#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/content_settings_pattern.h"
+#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "services/preferences/public/cpp/dictionary_value_update.h"
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 fc1d59bb35c..6eaff3f94fe 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
@@ -45,7 +45,6 @@ const char kObsoleteMouseLockExceptionsPref[] =
"profile.content_settings.exceptions.mouselock";
#endif // !defined(OS_ANDROID)
#endif // !defined(OS_IOS)
-const char kObsoleteLastUsed[] = "last_used";
} // namespace
@@ -253,34 +252,6 @@ void PrefProvider::DiscardObsoletePreferences() {
prefs_->Set(permission_autoblocker_data_pref, *old_dict);
prefs_->ClearPref(prompt_no_decision_count_pref);
#endif // !defined(OS_IOS)
-
- // TODO(timloh): See crbug.com/691893. This removal code was added in M58,
- // so is probably fine to remove in M60 or later.
- for (const WebsiteSettingsInfo* info :
- *WebsiteSettingsRegistry::GetInstance()) {
- if (!prefs_->GetDictionary(info->pref_name()))
- continue;
-
- prefs::ScopedDictionaryPrefUpdate update(prefs_, info->pref_name());
- auto all_settings = update.Get();
- std::vector<std::string> values_to_clean;
- for (base::DictionaryValue::Iterator i(*all_settings->AsConstDictionary());
- !i.IsAtEnd(); i.Advance()) {
- const base::DictionaryValue* pattern_settings = nullptr;
- bool is_dictionary = i.value().GetAsDictionary(&pattern_settings);
- DCHECK(is_dictionary);
- if (pattern_settings->GetWithoutPathExpansion(kObsoleteLastUsed, nullptr))
- values_to_clean.push_back(i.key());
- }
-
- for (const std::string& key : values_to_clean) {
- std::unique_ptr<prefs::DictionaryValueUpdate> pattern_settings;
- all_settings->GetDictionaryWithoutPathExpansion(key, &pattern_settings);
- pattern_settings->RemoveWithoutPathExpansion(kObsoleteLastUsed, nullptr);
- if (pattern_settings->empty())
- all_settings->RemoveWithoutPathExpansion(key, nullptr);
- }
- }
}
void PrefProvider::SetClockForTesting(std::unique_ptr<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 6b18f449543..9413166bcf6 100644
--- a/chromium/components/content_settings/core/browser/content_settings_registry.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_registry.cc
@@ -274,7 +274,15 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsRegistry::PLATFORM_ANDROID,
ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
- Register(CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER, "subresource-filter",
+ Register(CONTENT_SETTINGS_TYPE_SOUND, "sound", CONTENT_SETTING_ALLOW,
+ WebsiteSettingsInfo::UNSYNCABLE, WhitelistedSchemes(),
+ ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK),
+ WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
+ WebsiteSettingsRegistry::DESKTOP |
+ WebsiteSettingsRegistry::PLATFORM_ANDROID,
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+
+ Register(CONTENT_SETTINGS_TYPE_ADS, "subresource-filter",
CONTENT_SETTING_BLOCK, WebsiteSettingsInfo::UNSYNCABLE,
WhitelistedSchemes(),
ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK),
diff --git a/chromium/components/content_settings/core/browser/content_settings_utils.cc b/chromium/components/content_settings/core/browser/content_settings_utils.cc
index 55d33720378..2efa8f774ed 100644
--- a/chromium/components/content_settings/core/browser/content_settings_utils.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_utils.cc
@@ -6,15 +6,14 @@
#include <stddef.h>
-#include <memory>
#include <vector>
#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
#include "base/strings/string_split.h"
#include "base/values.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings_utils.h"
namespace {
@@ -120,34 +119,6 @@ PatternPair ParsePatternString(const std::string& pattern_str) {
return pattern_pair;
}
-ContentSetting ValueToContentSetting(const base::Value* value) {
- ContentSetting setting = CONTENT_SETTING_DEFAULT;
- bool valid = ParseContentSettingValue(value, &setting);
- DCHECK(valid);
- return setting;
-}
-
-bool ParseContentSettingValue(const base::Value* value,
- ContentSetting* setting) {
- if (!value) {
- *setting = CONTENT_SETTING_DEFAULT;
- return true;
- }
- int int_value = -1;
- if (!value->GetAsInteger(&int_value))
- return false;
- *setting = IntToContentSetting(int_value);
- return *setting != CONTENT_SETTING_DEFAULT;
-}
-
-std::unique_ptr<base::Value> ContentSettingToValue(ContentSetting setting) {
- if (setting <= CONTENT_SETTING_DEFAULT ||
- setting >= CONTENT_SETTING_NUM_SETTINGS) {
- return nullptr;
- }
- return base::MakeUnique<base::Value>(setting);
-}
-
void GetRendererContentSettingRules(const HostContentSettingsMap* map,
RendererContentSettingRules* rules) {
#if !defined(OS_ANDROID)
@@ -158,12 +129,10 @@ void GetRendererContentSettingRules(const HostContentSettingsMap* map,
#else
// Android doesn't use image content settings, so ALLOW rule is added for
// all origins.
- rules->image_rules.push_back(
- ContentSettingPatternSource(ContentSettingsPattern::Wildcard(),
- ContentSettingsPattern::Wildcard(),
- CONTENT_SETTING_ALLOW,
- std::string(),
- map->is_incognito()));
+ rules->image_rules.push_back(ContentSettingPatternSource(
+ ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
+ ContentSettingToValue(CONTENT_SETTING_ALLOW), std::string(),
+ map->is_incognito()));
#endif
map->GetSettingsForOneType(
CONTENT_SETTINGS_TYPE_JAVASCRIPT,
diff --git a/chromium/components/content_settings/core/browser/content_settings_utils.h b/chromium/components/content_settings/core/browser/content_settings_utils.h
index fdcaf0bc6fa..476398d7e31 100644
--- a/chromium/components/content_settings/core/browser/content_settings_utils.h
+++ b/chromium/components/content_settings/core/browser/content_settings_utils.h
@@ -5,7 +5,6 @@
#ifndef COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_UTILS_H_
#define COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_UTILS_H_
-#include <memory>
#include <string>
#include <utility>
@@ -14,10 +13,6 @@
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
-namespace base {
-class Value;
-}
-
class HostContentSettingsMap;
namespace content_settings {
@@ -58,25 +53,12 @@ std::string ContentSettingToString(ContentSetting setting);
// Returns true if |name| specifies a valid content setting, false otherwise.
bool ContentSettingFromString(const std::string& name, ContentSetting* setting);
-// Converts |Value| to |ContentSetting|.
-ContentSetting ValueToContentSetting(const base::Value* value);
-
-// Converts a |Value| to a |ContentSetting|. Returns true if |value| encodes
-// a valid content setting, false otherwise. Note that |CONTENT_SETTING_DEFAULT|
-// is encoded as a NULL value, so it is not allowed as an integer value.
-bool ParseContentSettingValue(const base::Value* value,
- ContentSetting* setting);
-
PatternPair ParsePatternString(const std::string& pattern_str);
std::string CreatePatternString(
const ContentSettingsPattern& item_pattern,
const ContentSettingsPattern& top_level_frame_pattern);
-// Returns a |base::Value*| representation of |setting| if |setting| is
-// a valid content setting. Otherwise, returns a nullptr.
-std::unique_ptr<base::Value> ContentSettingToValue(ContentSetting setting);
-
// Populates |rules| with content setting rules for content types that are
// handled by the renderer.
void GetRendererContentSettingRules(const HostContentSettingsMap* map,
diff --git a/chromium/components/content_settings/core/browser/cookie_settings.cc b/chromium/components/content_settings/core/browser/cookie_settings.cc
index e9603c73ff7..6dbac81c6ae 100644
--- a/chromium/components/content_settings/core/browser/cookie_settings.cc
+++ b/chromium/components/content_settings/core/browser/cookie_settings.cc
@@ -9,6 +9,7 @@
#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/common/content_settings_pattern.h"
+#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
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 52a6708ea74..19483f291ca 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
@@ -16,6 +16,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
+#include "base/values.h"
#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"
@@ -29,6 +30,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_pattern.h"
+#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
@@ -55,6 +57,7 @@ const ProviderNamesSourceMapEntry kProviderNamesSourceMap[] = {
{"policy", content_settings::SETTING_SOURCE_POLICY},
{"supervised_user", content_settings::SETTING_SOURCE_SUPERVISED},
{"extension", content_settings::SETTING_SOURCE_EXTENSION},
+ {"notification_android", content_settings::SETTING_SOURCE_USER},
{"preference", content_settings::SETTING_SOURCE_USER},
{"default", content_settings::SETTING_SOURCE_USER},
};
@@ -384,6 +387,8 @@ void HostContentSettingsMap::SetWebsiteSettingCustomScope(
std::unique_ptr<base::Value> value) {
DCHECK(SupportsResourceIdentifier(content_type) ||
resource_identifier.empty());
+ // TODO(crbug.com/731126): Verify that assumptions for notification content
+ // settings are met.
UsedContentSettingsProviders();
for (const auto& provider_pair : content_settings_providers_) {
@@ -526,7 +531,7 @@ void HostContentSettingsMap::MigrateDomainScopedSettings(bool after_sync) {
if (setting_entry.source != "preference")
continue;
// Migrate ALLOW settings only.
- if (setting_entry.setting != CONTENT_SETTING_ALLOW)
+ if (setting_entry.GetContentSetting() != CONTENT_SETTING_ALLOW)
continue;
// Skip default settings.
if (setting_entry.primary_pattern == ContentSettingsPattern::Wildcard())
@@ -733,7 +738,7 @@ void HostContentSettingsMap::AddSettingsForOneType(
while (rule_iterator->HasNext()) {
const content_settings::Rule& rule = rule_iterator->Next();
- ContentSetting setting_value = CONTENT_SETTING_DEFAULT;
+ std::unique_ptr<base::Value> setting_value;
// TODO(bauerb): Return rules as a list of values, not content settings.
// Handle the case using base::Values for its exceptions and default
// setting. Here we assume all the exceptions are granted as
@@ -742,12 +747,13 @@ void HostContentSettingsMap::AddSettingsForOneType(
content_type) &&
rule.value.get() &&
rule.primary_pattern != ContentSettingsPattern::Wildcard()) {
- setting_value = CONTENT_SETTING_ALLOW;
+ setting_value =
+ content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW);
} else {
- setting_value = content_settings::ValueToContentSetting(rule.value.get());
+ setting_value = base::MakeUnique<base::Value>(*(rule.value));
}
settings->push_back(ContentSettingPatternSource(
- rule.primary_pattern, rule.secondary_pattern, setting_value,
+ rule.primary_pattern, rule.secondary_pattern, std::move(setting_value),
kProviderNamesSourceMap[provider_type].provider_name, incognito));
}
}
@@ -910,4 +916,4 @@ HostContentSettingsMap::GetContentSettingValueAndPatterns(
void HostContentSettingsMap::SetClockForTesting(
std::unique_ptr<base::Clock> clock) {
pref_provider_->SetClockForTesting(std::move(clock));
-} \ No newline at end of file
+}
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 b5c7798b02f..b9f4bfb3689 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
@@ -57,6 +57,7 @@ class HostContentSettingsMap : public content_settings::Observer,
POLICY_PROVIDER,
SUPERVISED_PROVIDER,
CUSTOM_EXTENSION_PROVIDER,
+ NOTIFICATION_ANDROID_PROVIDER,
PREF_PROVIDER,
DEFAULT_PROVIDER,
NUM_PROVIDER_TYPES
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 4d49cef938c..b04fff48149 100644
--- a/chromium/components/content_settings/core/browser/website_settings_registry.cc
+++ b/chromium/components/content_settings/core/browser/website_settings_registry.cc
@@ -8,8 +8,13 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
#include "components/content_settings/core/common/content_settings.h"
+#if !defined(OS_IOS)
+#include "media/base/media_switches.h"
+#endif // !defined(OS_IOS)
+
namespace {
base::LazyInstance<content_settings::WebsiteSettingsRegistry>::DestructorAtExit
@@ -168,11 +173,20 @@ void WebsiteSettingsRegistry::Init() {
// 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.
- Register(
- CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER_DATA, "subresource-filter-data",
- nullptr, WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
- WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
- DESKTOP | PLATFORM_ANDROID, WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
+ Register(CONTENT_SETTINGS_TYPE_ADS_DATA, "subresource-filter-data", nullptr,
+ WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
+ WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
+ DESKTOP | PLATFORM_ANDROID,
+ WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
+#if !defined(OS_IOS)
+ if (base::FeatureList::IsEnabled(media::kRecordMediaEngagementScores)) {
+ Register(CONTENT_SETTINGS_TYPE_MEDIA_ENGAGEMENT, "media-engagement",
+ nullptr, WebsiteSettingsInfo::SYNCABLE, WebsiteSettingsInfo::LOSSY,
+ WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
+ DESKTOP | PLATFORM_ANDROID,
+ WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
+ }
+#endif //! defined(OS_IOS)
}
} // namespace content_settings
diff --git a/chromium/components/content_settings/core/common/BUILD.gn b/chromium/components/content_settings/core/common/BUILD.gn
index 42404221620..a70021e8a51 100644
--- a/chromium/components/content_settings/core/common/BUILD.gn
+++ b/chromium/components/content_settings/core/common/BUILD.gn
@@ -13,6 +13,8 @@ static_library("common") {
"content_settings_pattern_parser.cc",
"content_settings_pattern_parser.h",
"content_settings_types.h",
+ "content_settings_utils.cc",
+ "content_settings_utils.h",
"pref_names.cc",
"pref_names.h",
]
@@ -21,6 +23,8 @@ static_library("common") {
deps = [
"//base",
+ "//mojo/common:common_custom_types",
+ "//mojo/common:struct_traits",
"//mojo/public/cpp/bindings:struct_traits",
"//net",
"//url",
@@ -47,4 +51,8 @@ mojom("mojo_bindings") {
sources = [
"content_settings.mojom",
]
+
+ public_deps = [
+ "//mojo/common:common_custom_types",
+ ]
}
diff --git a/chromium/components/content_settings/core/common/DEPS b/chromium/components/content_settings/core/common/DEPS
index 4e501b277a1..54fa71b60e1 100644
--- a/chromium/components/content_settings/core/common/DEPS
+++ b/chromium/components/content_settings/core/common/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+mojo/common",
"+mojo/public/cpp/bindings",
"+net/base",
"+testing",
diff --git a/chromium/components/content_settings/core/common/OWNERS b/chromium/components/content_settings/core/common/OWNERS
index ac44cd00686..fda0d857f5e 100644
--- a/chromium/components/content_settings/core/common/OWNERS
+++ b/chromium/components/content_settings/core/common/OWNERS
@@ -3,3 +3,6 @@ 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/content_settings/core/common/content_settings.cc b/chromium/components/content_settings/core/common/content_settings.cc
index 4cf2d132898..4b32cb96c3e 100644
--- a/chromium/components/content_settings/core/common/content_settings.cc
+++ b/chromium/components/content_settings/core/common/content_settings.cc
@@ -4,17 +4,15 @@
#include "components/content_settings/core/common/content_settings.h"
-#include "base/containers/hash_tables.h"
+#include <algorithm>
+
#include "base/logging.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "build/build_config.h"
+#include "components/content_settings/core/common/content_settings_utils.h"
-ContentSetting IntToContentSetting(int content_setting) {
- return ((content_setting < 0) ||
- (content_setting >= CONTENT_SETTING_NUM_SETTINGS)) ?
- CONTENT_SETTING_DEFAULT : static_cast<ContentSetting>(content_setting);
-}
+namespace {
struct HistogramValue {
ContentSettingsType type;
@@ -25,9 +23,13 @@ struct HistogramValue {
// specified in the ContentType enum in histograms.xml. Since these values are
// used for histograms, please do not reuse the same value for a different
// content setting. Always append to the end and increment.
+//
// TODO(raymes): We should use a sparse histogram here on the hash of the
// content settings type name instead.
-HistogramValue kHistogramValue[] = {
+//
+// The array size must be explicit for the static_asserts below.
+constexpr size_t kNumHistogramValues = 30;
+constexpr HistogramValue kHistogramValue[kNumHistogramValues] = {
{CONTENT_SETTINGS_TYPE_COOKIES, 0},
{CONTENT_SETTINGS_TYPE_IMAGES, 1},
{CONTENT_SETTINGS_TYPE_JAVASCRIPT, 2},
@@ -44,9 +46,7 @@ HistogramValue kHistogramValue[] = {
{CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, 16},
{CONTENT_SETTINGS_TYPE_MIDI_SYSEX, 17},
{CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, 19},
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
{CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, 21},
-#endif
{CONTENT_SETTINGS_TYPE_APP_BANNER, 22},
{CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT, 23},
{CONTENT_SETTINGS_TYPE_DURABLE_STORAGE, 24},
@@ -55,43 +55,81 @@ HistogramValue kHistogramValue[] = {
{CONTENT_SETTINGS_TYPE_AUTOPLAY, 28},
{CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO, 30},
{CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA, 31},
- {CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER, 32},
- {CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER_DATA, 33},
+ {CONTENT_SETTINGS_TYPE_ADS, 32},
+ {CONTENT_SETTINGS_TYPE_ADS_DATA, 33},
+ {CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 34},
+ {CONTENT_SETTINGS_TYPE_MEDIA_ENGAGEMENT, 35},
+ {CONTENT_SETTINGS_TYPE_SOUND, 36},
};
+} // namespace
+
+ContentSetting IntToContentSetting(int content_setting) {
+ return ((content_setting < 0) ||
+ (content_setting >= CONTENT_SETTING_NUM_SETTINGS))
+ ? CONTENT_SETTING_DEFAULT
+ : static_cast<ContentSetting>(content_setting);
+}
+
int ContentSettingTypeToHistogramValue(ContentSettingsType content_setting,
size_t* num_values) {
- // Translate the list above into a map for fast lookup.
- typedef base::hash_map<int, int> Map;
- CR_DEFINE_STATIC_LOCAL(Map, kMap, ());
- if (kMap.empty()) {
- for (const HistogramValue& histogram_value : kHistogramValue)
- kMap[histogram_value.type] = histogram_value.value;
- }
-
- DCHECK(base::ContainsKey(kMap, content_setting));
*num_values = arraysize(kHistogramValue);
- return kMap[content_setting];
+
+ // Verify the array is sorted by enum type and contains all values.
+ DCHECK(std::is_sorted(std::begin(kHistogramValue), std::end(kHistogramValue),
+ [](const HistogramValue& a, const HistogramValue& b) {
+ return a.type < b.type;
+ }));
+ static_assert(kHistogramValue[kNumHistogramValues - 1].type ==
+ CONTENT_SETTINGS_NUM_TYPES - 1,
+ "Update content settings histogram lookup");
+
+ const HistogramValue* found = std::lower_bound(
+ std::begin(kHistogramValue), std::end(kHistogramValue), content_setting,
+ [](const HistogramValue& a, ContentSettingsType b) {
+ return a.type < b;
+ });
+ if (found != std::end(kHistogramValue) && found->type == content_setting)
+ return found->value;
+ NOTREACHED();
+ return -1;
}
ContentSettingPatternSource::ContentSettingPatternSource(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
- ContentSetting setting,
+ std::unique_ptr<base::Value> setting_value,
const std::string& source,
bool incognito)
: primary_pattern(primary_pattern),
secondary_pattern(secondary_pattern),
- setting(setting),
+ setting_value(std::move(setting_value)),
source(source),
incognito(incognito) {}
-ContentSettingPatternSource::ContentSettingPatternSource()
- : setting(CONTENT_SETTING_DEFAULT), incognito(false) {
-}
+ContentSettingPatternSource::ContentSettingPatternSource() : incognito(false) {}
ContentSettingPatternSource::ContentSettingPatternSource(
- const ContentSettingPatternSource& other) = default;
+ const ContentSettingPatternSource& other) {
+ *this = other;
+}
+
+ContentSettingPatternSource& ContentSettingPatternSource::operator=(
+ const ContentSettingPatternSource& other) {
+ primary_pattern = other.primary_pattern;
+ secondary_pattern = other.secondary_pattern;
+ if (other.setting_value)
+ setting_value = base::MakeUnique<base::Value>(*(other.setting_value));
+ source = other.source;
+ incognito = other.incognito;
+ return *this;
+}
+
+ContentSettingPatternSource::~ContentSettingPatternSource() {}
+
+ContentSetting ContentSettingPatternSource::GetContentSetting() const {
+ return content_settings::ValueToContentSetting(setting_value.get());
+}
RendererContentSettingRules::RendererContentSettingRules() {}
diff --git a/chromium/components/content_settings/core/common/content_settings.h b/chromium/components/content_settings/core/common/content_settings.h
index c441e280eaa..e28e51eed34 100644
--- a/chromium/components/content_settings/core/common/content_settings.h
+++ b/chromium/components/content_settings/core/common/content_settings.h
@@ -7,9 +7,12 @@
#include <stddef.h>
+#include <memory>
#include <string>
#include <vector>
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
@@ -43,14 +46,19 @@ int ContentSettingTypeToHistogramValue(ContentSettingsType content_setting,
struct ContentSettingPatternSource {
ContentSettingPatternSource(const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_patttern,
- ContentSetting setting,
+ std::unique_ptr<base::Value> setting_value,
const std::string& source,
bool incognito);
ContentSettingPatternSource(const ContentSettingPatternSource& other);
ContentSettingPatternSource();
+ ContentSettingPatternSource& operator=(
+ const ContentSettingPatternSource& other);
+ ~ContentSettingPatternSource();
+ ContentSetting GetContentSetting() const;
+
ContentSettingsPattern primary_pattern;
ContentSettingsPattern secondary_pattern;
- ContentSetting setting;
+ std::unique_ptr<base::Value> setting_value;
std::string source;
bool incognito;
};
diff --git a/chromium/components/content_settings/core/common/content_settings.mojom b/chromium/components/content_settings/core/common/content_settings.mojom
index 5c4c5d92a92..3669aec081a 100644
--- a/chromium/components/content_settings/core/common/content_settings.mojom
+++ b/chromium/components/content_settings/core/common/content_settings.mojom
@@ -4,6 +4,8 @@
module content_settings.mojom;
+import "mojo/common/values.mojom";
+
// This mirrors the C++ type in content_settings_pattern.h.
struct PatternParts {
// Lowercase string of the URL scheme to match. This string is empty if the
@@ -59,7 +61,7 @@ enum ContentSetting {
struct ContentSettingPatternSource {
ContentSettingsPattern primary_pattern;
ContentSettingsPattern secondary_pattern;
- ContentSetting setting;
+ mojo.common.mojom.Value setting_value;
string source;
bool incognito;
};
diff --git a/chromium/components/content_settings/core/common/content_settings_pattern.cc b/chromium/components/content_settings/core/common/content_settings_pattern.cc
index a83a6959a02..e88351a5d0b 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern.cc
+++ b/chromium/components/content_settings/core/common/content_settings_pattern.cc
@@ -10,6 +10,7 @@
#include <vector>
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/content_settings/core/common/content_settings_pattern_parser.h"
@@ -100,7 +101,7 @@ typedef ContentSettingsPattern::BuilderInterface BuilderInterface;
class ContentSettingsPattern::Builder :
public ContentSettingsPattern::BuilderInterface {
public:
- explicit Builder(bool use_legacy_validate);
+ explicit Builder();
~Builder() override;
// BuilderInterface:
@@ -124,20 +125,14 @@ class ContentSettingsPattern::Builder :
// Returns true when the pattern |parts| represent a valid pattern.
static bool Validate(const PatternParts& parts);
- static bool LegacyValidate(const PatternParts& parts);
-
bool is_valid_;
- bool use_legacy_validate_;
-
PatternParts parts_;
DISALLOW_COPY_AND_ASSIGN(Builder);
};
-ContentSettingsPattern::Builder::Builder(bool use_legacy_validate)
- : is_valid_(true),
- use_legacy_validate_(use_legacy_validate) {}
+ContentSettingsPattern::Builder::Builder() : is_valid_(true) {}
ContentSettingsPattern::Builder::~Builder() {}
@@ -201,17 +196,11 @@ ContentSettingsPattern ContentSettingsPattern::Builder::Build() {
return ContentSettingsPattern();
if (!Canonicalize(&parts_))
return ContentSettingsPattern();
- if (use_legacy_validate_) {
- is_valid_ = LegacyValidate(parts_);
- } else {
- is_valid_ = Validate(parts_);
- }
+ is_valid_ = Validate(parts_);
if (!is_valid_)
return ContentSettingsPattern();
// A pattern is invalid if canonicalization is not idempotent.
- // This check is here because it should be checked no matter
- // use_legacy_validate_ is.
PatternParts parts(parts_);
if (!Canonicalize(&parts))
return ContentSettingsPattern();
@@ -302,41 +291,6 @@ bool ContentSettingsPattern::Builder::Validate(const PatternParts& parts) {
return true;
}
-// static
-bool ContentSettingsPattern::Builder::LegacyValidate(
- const PatternParts& parts) {
- // If the pattern is for a "file-pattern" test if it is valid.
- if (parts.scheme == std::string(url::kFileScheme) &&
- !parts.is_scheme_wildcard &&
- parts.host.empty() &&
- parts.port.empty())
- return true;
-
- // If the pattern is for an extension URL test if it is valid.
- if (IsNonWildcardDomainNonPortScheme(parts.scheme) &&
- !parts.is_scheme_wildcard &&
- !parts.host.empty() &&
- !parts.has_domain_wildcard &&
- parts.port.empty() &&
- !parts.is_port_wildcard)
- return true;
-
- // Non-file patterns are invalid if either the scheme, host or port part is
- // empty.
- if ((!parts.is_scheme_wildcard) ||
- (parts.host.empty() && !parts.has_domain_wildcard) ||
- (!parts.is_port_wildcard))
- return false;
-
- // Test if the scheme is supported or a wildcard.
- if (!parts.is_scheme_wildcard &&
- parts.scheme != std::string(url::kHttpScheme) &&
- parts.scheme != std::string(url::kHttpsScheme)) {
- return false;
- }
- return true;
-}
-
// ////////////////////////////////////////////////////////////////////////////
// ContentSettingsPattern::PatternParts
//
@@ -367,9 +321,8 @@ ContentSettingsPattern::PatternParts::~PatternParts() {}
const int ContentSettingsPattern::kContentSettingsPatternVersion = 1;
// static
-BuilderInterface* ContentSettingsPattern::CreateBuilder(
- bool validate) {
- return new Builder(validate);
+std::unique_ptr<BuilderInterface> ContentSettingsPattern::CreateBuilder() {
+ return base::MakeUnique<Builder>();
}
// static
@@ -385,71 +338,67 @@ ContentSettingsPattern ContentSettingsPattern::Wildcard() {
// static
ContentSettingsPattern ContentSettingsPattern::FromURL(
const GURL& url) {
- std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder(
- ContentSettingsPattern::CreateBuilder(false));
+ ContentSettingsPattern::Builder builder;
const GURL* local_url = &url;
if (url.SchemeIsFileSystem() && url.inner_url()) {
local_url = url.inner_url();
}
if (local_url->SchemeIsFile()) {
- builder->WithScheme(local_url->scheme())->WithPath(local_url->path());
+ builder.WithScheme(local_url->scheme())->WithPath(local_url->path());
} else {
// Please keep the order of the ifs below as URLs with an IP as host can
// also have a "http" scheme.
if (local_url->HostIsIPAddress()) {
- builder->WithScheme(local_url->scheme())->WithHost(local_url->host());
+ builder.WithScheme(local_url->scheme())->WithHost(local_url->host());
} else if (local_url->SchemeIs(url::kHttpScheme)) {
- builder->WithSchemeWildcard()->WithDomainWildcard()->WithHost(
+ builder.WithSchemeWildcard()->WithDomainWildcard()->WithHost(
local_url->host());
} else if (local_url->SchemeIs(url::kHttpsScheme)) {
- builder->WithScheme(local_url->scheme())->WithDomainWildcard()->WithHost(
- local_url->host());
+ builder.WithScheme(local_url->scheme())
+ ->WithDomainWildcard()
+ ->WithHost(local_url->host());
} else {
// Unsupported scheme
}
if (local_url->port().empty()) {
if (local_url->SchemeIs(url::kHttpsScheme))
- builder->WithPort(GetDefaultPort(url::kHttpsScheme));
+ builder.WithPort(GetDefaultPort(url::kHttpsScheme));
else
- builder->WithPortWildcard();
+ builder.WithPortWildcard();
} else {
- builder->WithPort(local_url->port());
+ builder.WithPort(local_url->port());
}
}
- return builder->Build();
+ return builder.Build();
}
// static
ContentSettingsPattern ContentSettingsPattern::FromURLNoWildcard(
const GURL& url) {
- std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder(
- ContentSettingsPattern::CreateBuilder(false));
-
+ ContentSettingsPattern::Builder builder;
const GURL* local_url = &url;
if (url.SchemeIsFileSystem() && url.inner_url()) {
local_url = url.inner_url();
}
if (local_url->SchemeIsFile()) {
- builder->WithScheme(local_url->scheme())->WithPath(local_url->path());
+ builder.WithScheme(local_url->scheme())->WithPath(local_url->path());
} else {
- builder->WithScheme(local_url->scheme())->WithHost(local_url->host());
+ builder.WithScheme(local_url->scheme())->WithHost(local_url->host());
if (local_url->port().empty()) {
- builder->WithPort(GetDefaultPort(local_url->scheme()));
+ builder.WithPort(GetDefaultPort(local_url->scheme()));
} else {
- builder->WithPort(local_url->port());
+ builder.WithPort(local_url->port());
}
}
- return builder->Build();
+ return builder.Build();
}
// static
ContentSettingsPattern ContentSettingsPattern::FromString(
const std::string& pattern_spec) {
- std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder(
- ContentSettingsPattern::CreateBuilder(false));
- content_settings::PatternParser::Parse(pattern_spec,
- builder.get());
- return builder->Build();
+ ContentSettingsPattern::Builder builder;
+ content_settings::PatternParser::Parse(pattern_spec, &builder);
+ return builder.Build();
}
// static
@@ -481,27 +430,25 @@ bool ContentSettingsPattern::MigrateFromDomainToOrigin(
if (domain_pattern.parts_.host.empty())
return false;
- std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder(
- ContentSettingsPattern::CreateBuilder(false));
-
+ ContentSettingsPattern::Builder builder;
if (domain_pattern.parts_.is_scheme_wildcard)
- builder->WithScheme(url::kHttpScheme);
+ builder.WithScheme(url::kHttpScheme);
else
- builder->WithScheme(domain_pattern.parts_.scheme);
+ builder.WithScheme(domain_pattern.parts_.scheme);
- builder->WithHost(domain_pattern.parts_.host);
+ builder.WithHost(domain_pattern.parts_.host);
if (domain_pattern.parts_.is_port_wildcard) {
if (domain_pattern.parts_.scheme == url::kHttpsScheme) {
- builder->WithPort(GetDefaultPort(url::kHttpsScheme));
+ builder.WithPort(GetDefaultPort(url::kHttpsScheme));
} else {
- builder->WithPort(GetDefaultPort(url::kHttpScheme));
+ builder.WithPort(GetDefaultPort(url::kHttpScheme));
}
} else {
- builder->WithPort(domain_pattern.parts_.port);
+ builder.WithPort(domain_pattern.parts_.port);
}
- *origin_pattern = builder->Build();
+ *origin_pattern = builder.Build();
return true;
}
diff --git a/chromium/components/content_settings/core/common/content_settings_pattern.h b/chromium/components/content_settings/core/common/content_settings_pattern.h
index 7d6fc8d00dd..fe292d53d47 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern.h
+++ b/chromium/components/content_settings/core/common/content_settings_pattern.h
@@ -7,6 +7,7 @@
#ifndef COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_PATTERN_H_
#define COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_PATTERN_H_
+#include <memory>
#include <string>
#include "base/gtest_prod_util.h"
@@ -136,7 +137,7 @@ class ContentSettingsPattern {
virtual ContentSettingsPattern Build() = 0;
};
- static BuilderInterface* CreateBuilder(bool use_legacy_validate);
+ static std::unique_ptr<BuilderInterface> CreateBuilder();
// The version of the pattern format implemented.
static const int kContentSettingsPatternVersion;
diff --git a/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc b/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc
index d5aab80ab86..0a3938488c8 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc
+++ b/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc
@@ -184,8 +184,8 @@ TEST(ContentSettingsPatternTest, FromURLNoWildcard) {
// The static Wildcard() method goes through a fast path and avoids the Builder
// pattern. Ensure that it yields the exact same behavior.
TEST(ContentSettingsPatternTest, ValidWildcardFastPath) {
- std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder(
- ContentSettingsPattern::CreateBuilder(true));
+ std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder =
+ ContentSettingsPattern::CreateBuilder();
builder->WithSchemeWildcard()->WithDomainWildcard()->WithPortWildcard()->
WithPathWildcard();
ContentSettingsPattern built_wildcard = builder->Build();
diff --git a/chromium/components/content_settings/core/common/content_settings_struct_traits.cc b/chromium/components/content_settings/core/common/content_settings_struct_traits.cc
index 11ad9c92ed6..b5bac4a40d9 100644
--- a/chromium/components/content_settings/core/common/content_settings_struct_traits.cc
+++ b/chromium/components/content_settings/core/common/content_settings_struct_traits.cc
@@ -88,7 +88,8 @@ bool StructTraits<content_settings::mojom::ContentSettingPatternSourceDataView,
out->incognito = data.incognito();
return data.ReadPrimaryPattern(&out->primary_pattern) &&
data.ReadSecondaryPattern(&out->secondary_pattern) &&
- data.ReadSetting(&out->setting) && data.ReadSource(&out->source);
+ data.ReadSettingValue(&out->setting_value) &&
+ data.ReadSource(&out->source);
}
// static
diff --git a/chromium/components/content_settings/core/common/content_settings_struct_traits.h b/chromium/components/content_settings/core/common/content_settings_struct_traits.h
index 10c78efeafd..ab3fee43862 100644
--- a/chromium/components/content_settings/core/common/content_settings_struct_traits.h
+++ b/chromium/components/content_settings/core/common/content_settings_struct_traits.h
@@ -7,8 +7,11 @@
#include <string>
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings.mojom.h"
+#include "mojo/common/values_struct_traits.h"
#include "mojo/public/cpp/bindings/enum_traits.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
@@ -96,8 +99,9 @@ struct StructTraits<
return r.secondary_pattern;
}
- static ContentSetting setting(const ContentSettingPatternSource& r) {
- return r.setting;
+ static const std::unique_ptr<base::Value>& setting_value(
+ const ContentSettingPatternSource& r) {
+ return r.setting_value;
}
static const std::string& source(const ContentSettingPatternSource& r) {
diff --git a/chromium/components/content_settings/core/common/content_settings_types.h b/chromium/components/content_settings/core/common/content_settings_types.h
index 580a94ac8c6..e752e9e96e4 100644
--- a/chromium/components/content_settings/core/common/content_settings_types.h
+++ b/chromium/components/content_settings/core/common/content_settings_types.h
@@ -1,4 +1,4 @@
-// Copyright 2014 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.
@@ -48,11 +48,11 @@ enum ContentSettingsType {
CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
CONTENT_SETTINGS_TYPE_IMPORTANT_SITE_INFO,
CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA,
- CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER,
+ CONTENT_SETTINGS_TYPE_ADS,
// Website setting which stores metadata for the subresource filter to aid in
// decisions for whether or not to show the UI.
- CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER_DATA,
+ CONTENT_SETTINGS_TYPE_ADS_DATA,
// This is special-cased in the permissions layer to always allow, and as
// such doesn't have associated prefs data.
@@ -67,8 +67,15 @@ enum ContentSettingsType {
// verdicts of each origin.
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
- // WARNING: This enum is going to be removed soon. Do not depend on NUM_TYPES.
- CONTENT_SETTINGS_NUM_TYPES_DO_NOT_USE,
+ // Website setting which stores engagement data for media related to a
+ // specific origin.
+ CONTENT_SETTINGS_TYPE_MEDIA_ENGAGEMENT,
+
+ // Website setting which stores whether or not the site can play audible
+ // sound. This will not block playback but instead the user will not hear it.
+ CONTENT_SETTINGS_TYPE_SOUND,
+
+ CONTENT_SETTINGS_NUM_TYPES,
};
struct ContentSettingsTypeHash {
diff --git a/chromium/components/content_settings/core/common/content_settings_utils.cc b/chromium/components/content_settings/core/common/content_settings_utils.cc
new file mode 100644
index 00000000000..3f86905a69b
--- /dev/null
+++ b/chromium/components/content_settings/core/common/content_settings_utils.cc
@@ -0,0 +1,47 @@
+// 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/content_settings/core/common/content_settings_utils.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+
+namespace content_settings {
+
+namespace {
+
+// Converts a |Value| to a |ContentSetting|. Returns true if |value| encodes
+// a valid content setting, false otherwise. Note that |CONTENT_SETTING_DEFAULT|
+// is encoded as a NULL value, so it is not allowed as an integer value.
+bool ParseContentSettingValue(const base::Value* value,
+ ContentSetting* setting) {
+ if (!value) {
+ *setting = CONTENT_SETTING_DEFAULT;
+ return true;
+ }
+ int int_value = -1;
+ if (!value->GetAsInteger(&int_value))
+ return false;
+ *setting = IntToContentSetting(int_value);
+ return *setting != CONTENT_SETTING_DEFAULT;
+}
+
+} // namespace
+
+ContentSetting ValueToContentSetting(const base::Value* value) {
+ ContentSetting setting = CONTENT_SETTING_DEFAULT;
+ bool valid = ParseContentSettingValue(value, &setting);
+ DCHECK(valid);
+ return setting;
+}
+
+std::unique_ptr<base::Value> ContentSettingToValue(ContentSetting setting) {
+ if (setting <= CONTENT_SETTING_DEFAULT ||
+ setting >= CONTENT_SETTING_NUM_SETTINGS) {
+ return nullptr;
+ }
+ return base::MakeUnique<base::Value>(setting);
+}
+
+} // namespace content_settings
diff --git a/chromium/components/content_settings/core/common/content_settings_utils.h b/chromium/components/content_settings/core/common/content_settings_utils.h
new file mode 100644
index 00000000000..640483d9338
--- /dev/null
+++ b/chromium/components/content_settings/core/common/content_settings_utils.h
@@ -0,0 +1,27 @@
+// 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_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_UTILS_H_
+#define COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_UTILS_H_
+
+#include <memory>
+
+#include "components/content_settings/core/common/content_settings.h"
+
+namespace base {
+class Value;
+}
+
+namespace content_settings {
+
+// Converts |value| to |ContentSetting|.
+ContentSetting ValueToContentSetting(const base::Value* value);
+
+// Returns a base::Value representation of |setting| if |setting| is
+// a valid content setting. Otherwise, returns a nullptr.
+std::unique_ptr<base::Value> ContentSettingToValue(ContentSetting setting);
+
+} // namespace content_settings
+
+#endif // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_UTILS_H_
diff --git a/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.cc b/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.cc
index 5ebfd3ffd48..1cecfbc2bb3 100644
--- a/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.cc
+++ b/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.cc
@@ -27,7 +27,6 @@ void ContextualSearchJsApiServiceImpl::HandleSetCaption(
// static
void CreateContextualSearchJsApiService(
ContextualSearchJsApiHandler* contextual_search_js_api_handler,
- const service_manager::BindSourceInfo& source_info,
mojom::ContextualSearchJsApiServiceRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<ContextualSearchJsApiServiceImpl>(
contextual_search_js_api_handler),
diff --git a/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.h b/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.h
index 96d7c45736b..648d3d7fa68 100644
--- a/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.h
+++ b/chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.h
@@ -9,10 +9,6 @@
#include "components/contextual_search/browser/contextual_search_js_api_handler.h"
#include "components/contextual_search/common/contextual_search_js_api_service.mojom.h"
-namespace service_manager {
-struct BindSourceInfo;
-}
-
namespace contextual_search {
// This is the receiving end of Contextual Search JavaScript API calls.
@@ -36,7 +32,6 @@ class ContextualSearchJsApiServiceImpl
// static
void CreateContextualSearchJsApiService(
ContextualSearchJsApiHandler* contextual_search_js_api_handler,
- const service_manager::BindSourceInfo& source_info,
mojom::ContextualSearchJsApiServiceRequest request);
} // namespace contextual_search
diff --git a/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.cc b/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.cc
index 0464a82ce05..c2cd286f561 100644
--- a/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.cc
+++ b/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.cc
@@ -37,7 +37,6 @@ void OverlayJsRenderFrameObserver::RegisterMojoInterface() {
}
void OverlayJsRenderFrameObserver::CreateOverlayPageNotifierService(
- const service_manager::BindSourceInfo& source_info,
mojom::OverlayPageNotifierServiceRequest request) {
mojo::MakeStrongBinding(
base::MakeUnique<OverlayPageNotifierServiceImpl>(
diff --git a/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.h b/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.h
index fdaf82222e9..2bdc547406d 100644
--- a/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.h
+++ b/chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.h
@@ -10,7 +10,6 @@
#include "components/contextual_search/common/overlay_page_notifier_service.mojom.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "v8/include/v8.h"
@@ -43,7 +42,6 @@ class OverlayJsRenderFrameObserver : public content::RenderFrameObserver {
// Creates the OverlayPageNotifierService connecting the browser to this
// observer.
void CreateOverlayPageNotifierService(
- const service_manager::BindSourceInfo& source_info,
mojom::OverlayPageNotifierServiceRequest request);
// Destroys the OverlayPageNotifierService.
void DestroyOverlayPageNotifierService();
diff --git a/chromium/components/crash/content/app/breakpad_linux.cc b/chromium/components/crash/content/app/breakpad_linux.cc
index 0d3de65043a..2199920ee79 100644
--- a/chromium/components/crash/content/app/breakpad_linux.cc
+++ b/chromium/components/crash/content/app/breakpad_linux.cc
@@ -1715,6 +1715,8 @@ void HandleCrashDump(const BreakpadInfo& info) {
static const char android_build_fp[] = "android_build_fp";
static const char device[] = "device";
static const char gms_core_version[] = "gms_core_version";
+ static const char installer_package_name[] = "installer_package_name";
+ static const char abi_name[] = "abi_name";
static const char model[] = "model";
static const char brand[] = "brand";
static const char exception_info[] = "exception_info";
@@ -1736,6 +1738,11 @@ void HandleCrashDump(const BreakpadInfo& info) {
writer.AddPairString(gms_core_version,
android_build_info->gms_version_code());
writer.AddBoundary();
+ writer.AddPairString(installer_package_name,
+ android_build_info->installer_package_name());
+ writer.AddBoundary();
+ writer.AddPairString(abi_name, android_build_info->abi_name());
+ writer.AddBoundary();
WriteAndroidPackage(writer, android_build_info);
writer.AddBoundary();
if (android_build_info->java_exception_info() != nullptr) {
@@ -2092,4 +2099,8 @@ bool IsCrashReporterEnabled() {
return g_is_crash_reporter_enabled;
}
+void SetFirstChanceExceptionHandler(bool (*handler)(int, void*, void*)) {
+ google_breakpad::SetFirstChanceExceptionHandler(handler);
+}
+
} // namespace breakpad
diff --git a/chromium/components/crash/content/app/breakpad_linux.h b/chromium/components/crash/content/app/breakpad_linux.h
index 0160f628456..4a2a429fc05 100644
--- a/chromium/components/crash/content/app/breakpad_linux.h
+++ b/chromium/components/crash/content/app/breakpad_linux.h
@@ -61,6 +61,10 @@ bool IsCrashReporterEnabled();
// Generates a minidump on demand for this process, writing it to |dump_fd|.
void GenerateMinidumpOnDemandForAndroid(int dump_fd);
+
+// Install a handler that gets a change to handle faults before Breakpad does
+// any processing. This is used by V8 for trap-based bounds checks.
+void SetFirstChanceExceptionHandler(bool (*handler)(int, void*, void*));
} // namespace breakpad
#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
diff --git a/chromium/components/crash/content/app/breakpad_win.cc b/chromium/components/crash/content/app/breakpad_win.cc
index c09961c7f6a..0604383e799 100644
--- a/chromium/components/crash/content/app/breakpad_win.cc
+++ b/chromium/components/crash/content/app/breakpad_win.cc
@@ -121,41 +121,13 @@ extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
namespace {
-// We need to prevent ICF from folding DumpForHangDebuggingThread() and
-// DumpProcessWithoutCrashThread() together, since that makes them
-// indistinguishable in crash dumps. We do this by making the function
-// bodies unique, and prevent optimization from shuffling things around.
-MSVC_DISABLE_OPTIMIZE()
-MSVC_PUSH_DISABLE_WARNING(4748)
-
DWORD WINAPI DumpProcessWithoutCrashThread(void*) {
DumpProcessWithoutCrash();
return 0;
}
-// The following two functions do exactly the same thing as the two above. But
-// we want the signatures to be different so that we can easily track them in
-// crash reports.
-// TODO(yzshen): Remove when enough information is collected and the hang rate
-// of pepper/renderer processes is reduced.
-DWORD WINAPI DumpForHangDebuggingThread(void*) {
- DumpProcessWithoutCrash();
- VLOG(1) << "dumped for hang debugging";
- return 0;
-}
-
-MSVC_POP_WARNING()
-MSVC_ENABLE_OPTIMIZE()
-
} // namespace
-// Injects a thread into a remote process to dump state when there is no crash.
-extern "C" HANDLE __declspec(dllexport) __cdecl InjectDumpProcessWithoutCrash(
- HANDLE process) {
- return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, 0,
- 0, NULL);
-}
-
extern "C" HANDLE __declspec(dllexport) __cdecl InjectDumpForHungInput(
HANDLE process,
void* serialized_crash_keys) {
@@ -174,12 +146,6 @@ extern "C" HANDLE __declspec(
0, NULL);
}
-extern "C" HANDLE __declspec(dllexport) __cdecl
-InjectDumpForHangDebugging(HANDLE process) {
- return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread,
- 0, 0, NULL);
-}
-
// Returns a string containing a list of all modifiers for the loaded profile.
std::wstring GetProfileType() {
std::wstring profile_type;
diff --git a/chromium/components/crash/content/app/crashpad.cc b/chromium/components/crash/content/app/crashpad.cc
index dcec3468435..aabb8de5ac8 100644
--- a/chromium/components/crash/content/app/crashpad.cc
+++ b/chromium/components/crash/content/app/crashpad.cc
@@ -90,10 +90,6 @@ bool LogMessageHandler(int severity,
return false;
}
-void DumpWithoutCrashing() {
- CRASHPAD_SIMULATE_CRASH();
-}
-
void InitializeCrashpadImpl(bool initial_client,
const std::string& process_type,
bool embedded_handler) {
@@ -304,6 +300,10 @@ void RequestSingleCrashUpload(const std::string& local_id) {
g_database->RequestUpload(uuid);
}
+void DumpWithoutCrashing() {
+ CRASHPAD_SIMULATE_CRASH();
+}
+
} // namespace crash_reporter
#if defined(OS_WIN)
diff --git a/chromium/components/crash/content/app/crashpad.h b/chromium/components/crash/content/app/crashpad.h
index ada0eaec733..ae6d3a479ba 100644
--- a/chromium/components/crash/content/app/crashpad.h
+++ b/chromium/components/crash/content/app/crashpad.h
@@ -107,6 +107,8 @@ void GetReports(std::vector<Report>* reports);
// Requests a user triggered upload for a crash report with a given id.
void RequestSingleCrashUpload(const std::string& local_id);
+void DumpWithoutCrashing();
+
namespace internal {
#if defined(OS_WIN)
diff --git a/chromium/components/crash/content/app/crashpad_win.cc b/chromium/components/crash/content/app/crashpad_win.cc
index 94a350f4b5a..2c25a0b26fd 100644
--- a/chromium/components/crash/content/app/crashpad_win.cc
+++ b/chromium/components/crash/content/app/crashpad_win.cc
@@ -151,34 +151,15 @@ base::FilePath PlatformCrashpadInitialization(bool initial_client,
return database_path;
}
-// TODO(scottmg): http://crbug.com/546288 These exported functions are for
-// compatibility with how Breakpad worked, but it seems like there's no need to
-// do the CreateRemoteThread() dance with a minor extension of the Crashpad API
-// (to just pass the pid we want a dump for). We should add that and then modify
-// hang_crash_dump_win.cc to work in a more direct manner.
-
-// Used for dumping a process state when there is no crash.
-extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
- CRASHPAD_SIMULATE_CRASH();
-}
-
namespace {
-// We need to prevent ICF from folding DumpForHangDebuggingThread(),
-// DumpProcessForHungInputThread(), DumpProcessForHungInputNoCrashKeysThread()
-// and DumpProcessWithoutCrashThread() together, since that makes them
+// We need to prevent ICF from folding DumpProcessForHungInputThread(),
+// DumpProcessForHungInputNoCrashKeysThread() together, since that makes them
// indistinguishable in crash dumps. We do this by making the function
// bodies unique, and prevent optimization from shuffling things around.
MSVC_DISABLE_OPTIMIZE()
MSVC_PUSH_DISABLE_WARNING(4748)
-// Note that this function must be in a namespace for the [Renderer hang]
-// annotations to work on the crash server.
-DWORD WINAPI DumpProcessWithoutCrashThread(void*) {
- DumpProcessWithoutCrash();
- return 0;
-}
-
// TODO(dtapuska): Remove when enough information is gathered where the crash
// reports without crash keys come from.
DWORD WINAPI DumpProcessForHungInputThread(void* crash_keys_str) {
@@ -190,7 +171,7 @@ DWORD WINAPI DumpProcessForHungInputThread(void* crash_keys_str) {
base::debug::SetCrashKeyValue(crash_key.first, crash_key.second);
}
}
- DumpProcessWithoutCrash();
+ DumpWithoutCrashing();
return 0;
}
@@ -203,15 +184,7 @@ DWORD WINAPI DumpProcessForHungInputNoCrashKeysThread(void* reason) {
"hung-reason", base::IntToString(reinterpret_cast<int>(reason)));
#pragma warning(pop)
- DumpProcessWithoutCrash();
- return 0;
-}
-
-// TODO(yzshen): Remove when enough information is collected and the hang rate
-// of pepper/renderer processes is reduced.
-DWORD WINAPI DumpForHangDebuggingThread(void*) {
- DumpProcessWithoutCrash();
- VLOG(1) << "dumped for hang debugging";
+ DumpWithoutCrashing();
return 0;
}
@@ -238,15 +211,6 @@ int __declspec(dllexport) CrashForException(
}
// Injects a thread into a remote process to dump state when there is no crash.
-HANDLE __declspec(dllexport) __cdecl InjectDumpProcessWithoutCrash(
- HANDLE process) {
- return CreateRemoteThread(
- process, nullptr, 0,
- crash_reporter::internal::DumpProcessWithoutCrashThread, nullptr, 0,
- nullptr);
-}
-
-// Injects a thread into a remote process to dump state when there is no crash.
// |serialized_crash_keys| is a nul terminated string that represents serialized
// crash keys sent from the browser. Keys and values are separated by ':', and
// key/value pairs are separated by ','. All keys should be previously
@@ -272,13 +236,6 @@ HANDLE __declspec(dllexport) __cdecl InjectDumpForHungInputNoCrashKeys(
reinterpret_cast<void*>(reason), 0, nullptr);
}
-HANDLE __declspec(dllexport) __cdecl InjectDumpForHangDebugging(
- HANDLE process) {
- return CreateRemoteThread(
- process, nullptr, 0, crash_reporter::internal::DumpForHangDebuggingThread,
- 0, 0, nullptr);
-}
-
#if defined(ARCH_CPU_X86_64)
static int CrashForExceptionInNonABICompliantCodeRange(
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 c7b7628c239..fdcfa65f7d6 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android.cc
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android.cc
@@ -18,8 +18,8 @@
#include "base/rand_util.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
+#include "base/task_scheduler/post_task.h"
#include "components/crash/content/app/breakpad_linux.h"
-#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/file_descriptor_info.h"
#include "content/public/browser/notification_service.h"
@@ -27,8 +27,6 @@
#include "content/public/browser/render_process_host.h"
#include "jni/CrashDumpManager_jni.h"
-using content::BrowserThread;
-
namespace breakpad {
CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir,
@@ -40,8 +38,6 @@ CrashDumpManager::~CrashDumpManager() {
void CrashDumpManager::OnChildStart(int child_process_id,
content::FileDescriptorInfo* mappings) {
- DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
-
if (!breakpad::IsCrashReporterEnabled())
return;
@@ -79,7 +75,6 @@ void CrashDumpManager::ProcessMinidump(
content::ProcessType process_type,
base::TerminationStatus termination_status,
base::android::ApplicationState app_state) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
int64_t file_size = 0;
int r = base::GetFileSize(minidump_path, &file_size);
DCHECK(r) << "Failed to retrieve size for minidump "
@@ -185,8 +180,8 @@ void CrashDumpManager::OnChildExit(int child_process_id,
minidump_path = iter->second;
child_process_id_to_minidump_path_.erase(iter);
}
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
+ base::PostTaskWithTraits(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path,
crash_dump_dir_, pid, process_type, termination_status,
app_state));
diff --git a/chromium/components/crash/content/browser/crash_dump_observer_android.cc b/chromium/components/crash/content/browser/crash_dump_observer_android.cc
index 72189b15d96..acb05e07d66 100644
--- a/chromium/components/crash/content/browser/crash_dump_observer_android.cc
+++ b/chromium/components/crash/content/browser/crash_dump_observer_android.cc
@@ -85,7 +85,6 @@ void CrashDumpObserver::OnChildExit(int child_process_id,
void CrashDumpObserver::BrowserChildProcessStarted(
int child_process_id,
content::FileDescriptorInfo* mappings) {
- DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
std::vector<Client*> registered_clients_copy;
{
base::AutoLock auto_lock(registered_clients_lock_);
diff --git a/chromium/components/crash/core/browser/resources/crashes.html b/chromium/components/crash/core/browser/resources/crashes.html
index 19ba4001f42..f6b80a7475b 100644
--- a/chromium/components/crash/core/browser/resources/crashes.html
+++ b/chromium/components/crash/core/browser/resources/crashes.html
@@ -1,5 +1,5 @@
<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
@@ -12,7 +12,7 @@
<script src="chrome://resources/js/ios/web_ui.js"></script>
</if>
- <title i18n-content="crashesTitle"></title>
+ <title>$i18n{crashesTitle}</title>
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="chrome://resources/css/widgets.css">
<link rel="stylesheet" href="crashes.css">
@@ -23,19 +23,20 @@
<script src="chrome://crashes/crashes.js"></script>
</head>
<body>
- <header><h1 i18n-content="crashesTitle"></h1></header>
+ <header><h1>$i18n{crashesTitle}</h1></header>
<div id="crashUploadStatus" hidden>
- <a is="action-link" role="button" id="uploadCrashes"
- i18n-content="uploadCrashesLinkText"></a>
+ <a is="action-link" role="button" id="uploadCrashes">
+ $i18n{uploadCrashesLinkText}
+ </a>
</div>
<div id="disabledMode" hidden>
- <h2 i18n-content="disabledHeader"></h2>
- <p i18n-values=".innerHTML:disabledMessage"></p>
+ <h2>$i18n{disabledHeader}</h2>
+ <p>$i18nRaw{disabledMessage}</p>
</div>
<div id="crashesInfo">
<h2 id="countBanner"></h2>
<div id="crashList"></div>
- <p id="noCrashes" i18n-content="noCrashesMessage" hidden></p>
+ <p id="noCrashes" hidden>$i18n{noCrashesMessage}</p>
</div>
<script src="chrome://resources/js/i18n_template.js"></script>
<script src="chrome://resources/js/jstemplate_compiled.js"></script>
diff --git a/chromium/components/cronet/android/BUILD.gn b/chromium/components/cronet/android/BUILD.gn
index 811f6306288..4474e321496 100644
--- a/chromium/components/cronet/android/BUILD.gn
+++ b/chromium/components/cronet/android/BUILD.gn
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//base/android/jni_generator/jni_exception_list.gni")
import("//build/buildflag_header.gni")
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
@@ -25,6 +26,12 @@ generate_jni("cronet_jni_headers") {
jni_package = "cronet"
}
+generate_jni_registration("cronet_jni_registration") {
+ target = ":cronet_sample_apk"
+ output = "$root_gen_dir/components/cronet/android/${target_name}.h"
+ exception_files = jni_exception_files
+}
+
java_cpp_enum("effective_connection_type_java") {
sources = [
"//net/nqe/effective_connection_type.h",
@@ -165,6 +172,7 @@ template("cronet_static_tmpl") {
deps = [
":cronet_android_cert_proto",
":cronet_jni_headers",
+ ":cronet_jni_registration",
":cronet_version_header",
"//base",
"//base/third_party/dynamic_annotations",
@@ -195,6 +203,8 @@ template("cronet_static_tmpl") {
"//components/cronet/android/url_request_error.h",
"//components/cronet/histogram_manager.cc",
"//components/cronet/histogram_manager.h",
+ "//components/cronet/host_cache_persistence_manager.cc",
+ "//components/cronet/host_cache_persistence_manager.h",
"//components/cronet/stale_host_resolver.cc",
"//components/cronet/stale_host_resolver.h",
"//components/cronet/url_request_context_config.cc",
@@ -474,6 +484,7 @@ generate_jni("cronet_tests_jni_headers") {
testonly = true
sources = [
"test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.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",
@@ -499,6 +510,8 @@ shared_library("cronet_tests") {
"test/cronet_test_util.h",
"test/cronet_url_request_context_config_test.cc",
"test/cronet_url_request_context_config_test.h",
+ "test/experimental_options_test.cc",
+ "test/experimental_options_test.h",
"test/mock_cert_verifier.cc",
"test/mock_cert_verifier.h",
"test/mock_url_request_job_factory.cc",
@@ -527,6 +540,9 @@ shared_library("cronet_tests") {
]
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") {
@@ -900,6 +916,7 @@ test("cronet_unittests") {
sources = [
"//components/cronet/android/cert/cert_verifier_cache_serializer_unittest.cc",
"//components/cronet/histogram_manager_unittest.cc",
+ "//components/cronet/host_cache_persistence_manager_unittest.cc",
"//components/cronet/run_all_unittests.cc",
"//components/cronet/stale_host_resolver_unittest.cc",
"//components/cronet/url_request_context_config_unittest.cc",
@@ -911,6 +928,7 @@ test("cronet_unittests") {
"//base",
"//base/test:test_support",
"//components/metrics",
+ "//components/prefs:test_support",
"//net",
"//net:test_support",
"//net/android:net_java",
@@ -995,6 +1013,7 @@ action("extract_cronet_test_jars") {
"$root_out_dir/lib.java/base/base_java_test_support.jar",
"$root_out_dir/lib.java/components/cronet/android/cronet_javatests.jar",
"$root_out_dir/lib.java/components/cronet/android/cronet_test_apk_java.jar",
+ "$root_out_dir/lib.java/net/android/embedded_test_server_aidl_java.jar",
"$root_out_dir/lib.java/net/android/net_java.jar",
"$root_out_dir/lib.java/net/android/net_java_test_support.jar",
"$root_out_dir/lib.java/url/url_java.jar",
@@ -1023,6 +1042,7 @@ action("extract_cronet_test_jars") {
":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",
"//third_party/netty4:netty_all_java",
@@ -1181,13 +1201,17 @@ jar_src("jar_cronet_impl_native_java_source") {
action("generate_licenses") {
_license_path = "$_package_dir/LICENSE"
- script = "//components/cronet/tools/cronet_licenses.py"
+ script = "//tools/licenses.py"
outputs = [
_license_path,
]
args = [
- "license",
+ "license_file",
rebase_path(_license_path, root_build_dir),
+ "--gn-target",
+ "//components/cronet/android:cronet",
+ "--gn-out-dir",
+ ".",
]
}
diff --git a/chromium/components/cronet/ios/BUILD.gn b/chromium/components/cronet/ios/BUILD.gn
index 716fa0b47ef..3227d03ab13 100644
--- a/chromium/components/cronet/ios/BUILD.gn
+++ b/chromium/components/cronet/ios/BUILD.gn
@@ -32,34 +32,58 @@ process_version("cronet_version_header") {
]
}
-source_set("cronet_sources") {
- deps = [
- ":cronet_version_header",
- ":generate_accept_languages",
- "//base:base",
- "//components/grpc_support",
- "//components/metrics:metrics",
- "//components/metrics/proto:proto",
- "//components/prefs:prefs",
- "//ios/net:net",
- "//ios/web:user_agent",
- "//net",
- "//url",
- ]
+config("cronet_include_config") {
+ include_dirs = [ "//components/grpc_support/include" ]
+}
- sources = [
- "../histogram_manager.cc",
- "../histogram_manager.h",
- "../stale_host_resolver.cc",
- "../stale_host_resolver.h",
- "../url_request_context_config.cc",
- "../url_request_context_config.h",
- "Cronet.h",
- "Cronet.mm",
- "cronet_c_for_grpc.h",
- "cronet_environment.h",
- "cronet_environment.mm",
+config("cronet_static_config") {
+ libs = [
+ "Cronet.framework",
+ "UIKit.Framework",
+ "CFNetwork.framework",
+ "MobileCoreServices.framework",
+ "Security.framework",
+ "SystemConfiguration.framework",
+ "resolv",
]
+ configs = [ ":cronet_include_config" ]
+}
+
+_cronet_deps = [
+ ":cronet_version_header",
+ ":generate_accept_languages",
+ "//base:base",
+ "//components/grpc_support",
+ "//components/metrics:metrics",
+ "//components/metrics/proto:proto",
+ "//components/prefs:prefs",
+ "//ios/net:net",
+ "//ios/web:user_agent",
+ "//ios/web/public/global_state",
+ "//net",
+ "//url",
+]
+
+_cronet_sources = [
+ "../histogram_manager.cc",
+ "../histogram_manager.h",
+ "../stale_host_resolver.cc",
+ "../stale_host_resolver.h",
+ "../url_request_context_config.cc",
+ "../url_request_context_config.h",
+ "Cronet.h",
+ "Cronet.mm",
+ "cronet_environment.h",
+ "cronet_environment.mm",
+]
+
+_cronet_public_headers = [ "Cronet.h" ]
+_cronet_public_headers += grpc_public_headers
+
+source_set("cronet_sources") {
+ deps = _cronet_deps
+
+ sources = _cronet_sources
include_dirs = [ "//components/grpc_support/include" ]
@@ -85,17 +109,11 @@ ios_framework_bundle("cronet_framework") {
libs = [ "UIKit.Framework" ]
- include_dirs = [ "//components/grpc_support/include" ]
-
public_deps = [
"//components/grpc_support",
]
- public_headers = [
- "Cronet.h",
- "cronet_c_for_grpc.h",
- ]
- public_headers += grpc_public_headers
+ public_headers = _cronet_public_headers
sources = [
"Cronet.h",
@@ -103,6 +121,8 @@ ios_framework_bundle("cronet_framework") {
configs -= [ "//build/config/compiler:default_symbols" ]
configs += [ "//build/config/compiler:symbols" ]
+
+ public_configs = [ ":cronet_include_config" ]
}
test("cronet_unittests") {
@@ -137,26 +157,171 @@ action("generate_accept_languages") {
]
}
+# A static library which contains just _cronet_sources.
+static_library("cronet_static") {
+ visibility = [ ":*" ]
+ deps = _cronet_deps
+ sources = _cronet_sources
+ public_configs = [ ":cronet_include_config" ]
+ public_deps = [
+ "//components/grpc_support",
+ ]
+}
+
+# A static library which contains all dependencies of :cronet_static.
+static_library("cronet_deps_complete") {
+ visibility = [ ":*" ]
+ complete_static_lib = true
+ deps = [
+ ":cronet_static",
+ ]
+}
+
+# A static library which contains cronet and all dependendencies hidden inside.
+action("cronet_static_complete") {
+ visibility = [ ":*" ]
+ script = "//components/cronet/tools/hide_symbols.py"
+ deps = [
+ ":cronet_deps_complete",
+ ":cronet_static",
+ ]
+ outputs = [
+ "$target_out_dir/$current_cpu/cronet_static_complete.a",
+ ]
+ args = [
+ "--input_libs",
+ rebase_path("$target_out_dir/libcronet_static.a", root_build_dir),
+ "--deps_lib",
+ rebase_path("$target_out_dir/libcronet_deps_complete.a", root_build_dir),
+ "--output_obj",
+ rebase_path("$target_out_dir/$current_cpu/cronet_static_complete.o",
+ root_build_dir),
+ "--output_lib",
+ rebase_path("$target_out_dir/$current_cpu/cronet_static_complete.a",
+ root_build_dir),
+ "--current_cpu",
+ current_cpu,
+ ]
+
+ if (!use_system_xcode) {
+ args += [
+ "--developer_dir",
+ hermetic_xcode_path,
+ ]
+ }
+
+ public_configs = [ ":cronet_static_config" ]
+}
+
+# A fat static library which exports cronet public symbols and hides all dependendencies.
+lipo_binary("libcronet") {
+ arch_binary_target = ":cronet_static_complete"
+ arch_binary_output = "cronet_static_complete.a"
+ output_name = "libcronet.a"
+ enable_stripping = false
+ enable_dsyms = false
+}
+
+template("ios_static_framework") {
+ _target_name = target_name
+ _output_name = target_name
+ if (defined(invoker.output_name)) {
+ _output_name = invoker.output_name
+ }
+ _framework_name = target_name
+ if (defined(invoker.framework_name)) {
+ _framework_name = invoker.framework_name
+ }
+
+ _bundle_target_name = _target_name + "_bundle"
+ _framework_headers_target = _target_name + "_framework_headers"
+ bundle_data(_framework_headers_target) {
+ visibility = [ ":$_bundle_target_name" ]
+ sources = invoker.public_headers
+ outputs = [
+ "{{bundle_root_dir}}/Headers/{{source_file_part}}",
+ ]
+ }
+
+ _framework_binary_target = _target_name + "_framework_binary"
+ _static_library_target = invoker.static_library_target
+
+ bundle_data(_framework_binary_target) {
+ visibility = [ ":$_bundle_target_name" ]
+ sources = get_target_outputs(_static_library_target)
+ outputs = [
+ "{{bundle_root_dir}}/$_framework_name",
+ ]
+ public_deps = [
+ _static_library_target,
+ ]
+ }
+
+ create_bundle(_bundle_target_name) {
+ product_type = "com.apple.product-type.framework"
+ bundle_root_dir = "$root_out_dir/Static/$_output_name"
+ bundle_executable_dir = bundle_root_dir
+ bundle_resources_dir = bundle_root_dir
+ bundle_plugins_dir = bundle_root_dir
+ deps = [
+ ":$_framework_binary_target",
+ ":$_framework_headers_target",
+ ]
+ }
+
+ action(_target_name) {
+ script = "//build/config/ios/dummy.py"
+ outputs = [
+ "$root_out_dir/Static/$_output_name",
+ ]
+ deps = [
+ ":$_bundle_target_name",
+ ]
+ public_configs = invoker.public_configs
+ }
+}
+
+ios_static_framework("cronet_static_framework") {
+ output_name = "Cronet.framework"
+ framework_name = "Cronet"
+ public_headers = _cronet_public_headers
+ static_library_target = ":libcronet"
+ public_configs = [ ":cronet_static_config" ]
+}
+
if (additional_toolchains == [] || current_toolchain == default_toolchain) {
_package_dir = "$root_out_dir/cronet"
action("generate_license") {
_license_path = "$_package_dir/LICENSE"
- script = "//components/cronet/tools/cronet_licenses.py"
+ script = "//tools/licenses.py"
inputs = [
"//build/util/LASTCHANGE",
- "//buildtools/$host_os/gn",
]
outputs = [
_license_path,
]
args = [
- "license",
+ "license_file",
rebase_path(_license_path, root_build_dir),
- "--gn",
- "--gn-path",
- rebase_path("//buildtools/$host_os/gn", root_build_dir),
+ "--gn-target",
+ "//components/cronet/ios:cronet_framework",
+ "--gn-out-dir",
+ ".",
+ ]
+ }
+
+ copy("cronet_static_copy") {
+ sources = [
+ "$root_out_dir/Static/Cronet.framework",
+ ]
+ outputs = [
+ "$_package_dir/Static/Cronet.framework",
+ ]
+
+ deps = [
+ ":cronet_static_framework",
]
}
@@ -172,6 +337,7 @@ if (additional_toolchains == [] || current_toolchain == default_toolchain) {
deps = [
":cronet_framework",
+ ":cronet_static_copy",
]
}
diff --git a/chromium/components/cronet/ios/cronet_consumer/BUILD.gn b/chromium/components/cronet/ios/cronet_consumer/BUILD.gn
index 966057c455d..ea1be9030a4 100644
--- a/chromium/components/cronet/ios/cronet_consumer/BUILD.gn
+++ b/chromium/components/cronet/ios/cronet_consumer/BUILD.gn
@@ -3,28 +3,63 @@
# found in the LICENSE file.
import("//build/config/ios/rules.gni")
+import("//ios/features.gni")
-ios_app_bundle("cronet_consumer") {
- info_plist = "cronet-consumer-Info.plist"
+template("cronet_consumer_template") {
+ _target_name = target_name
+ ios_app_bundle(_target_name) {
+ info_plist = "cronet-consumer-Info.plist"
+
+ deps = [
+ "//base:base",
+ ]
+
+ deps += invoker.deps
+
+ sources = [
+ "cronet_consumer_app_delegate.h",
+ "cronet_consumer_app_delegate.mm",
+ "cronet_consumer_view_controller.h",
+ "cronet_consumer_view_controller.m",
+ "main.mm",
+ ]
+
+ forward_variables_from(invoker,
+ [
+ "bundle_deps",
+ "cflags",
+ "ldflags",
+ ])
+
+ configs += [ "//build/config/compiler:enable_arc" ]
+ }
+}
+
+cronet_consumer_template("cronet_consumer") {
deps = [
- "//base:base",
"//components/cronet/ios:cronet_framework+link",
-
- # All shared libraries must have the sanitizer deps to properly link in
- # asan mode (this target will be empty in other cases).
- "//build/config:exe_and_shlib_deps",
]
+ bundle_deps = [ "//components/cronet/ios:cronet_framework+bundle" ]
+}
- sources = [
- "cronet_consumer_app_delegate.h",
- "cronet_consumer_app_delegate.mm",
- "cronet_consumer_view_controller.h",
- "cronet_consumer_view_controller.m",
- "main.mm",
- ]
+# TODO(mef): Building "cronet_consumer_static" app with additional_target_cpus
+# causes "cronet_static_framework" to build lipo_binary("libcronet") for
+# duplicate architecture (e.g. arm64+arm64) and breaks the build.
+if (!defined(additional_target_cpus)) {
+ cronet_consumer_template("cronet_consumer_static") {
+ deps = [
+ "//components/cronet/ios:cronet_static_framework",
+ ]
- bundle_deps = [ "//components/cronet/ios:cronet_framework+bundle" ]
+ cflags = [
+ "-F",
+ "Static",
+ ]
- configs += [ "//build/config/compiler:enable_arc" ]
+ ldflags = [
+ "-F",
+ "Static",
+ ]
+ }
}
diff --git a/chromium/components/cronet/ios/test/BUILD.gn b/chromium/components/cronet/ios/test/BUILD.gn
index fc515a566eb..5c90a1ce0ca 100644
--- a/chromium/components/cronet/ios/test/BUILD.gn
+++ b/chromium/components/cronet/ios/test/BUILD.gn
@@ -11,6 +11,8 @@ test("cronet_test") {
"cronet_acceptlang_test.mm",
"cronet_http_test.mm",
"cronet_netlog_test.mm",
+ "cronet_pkp_test.mm",
+ "cronet_test_base.mm",
"cronet_test_runner.mm",
"get_stream_engine.mm",
"start_cronet.h",
@@ -32,4 +34,5 @@ test("cronet_test") {
]
bundle_deps = [ "//components/cronet/ios:cronet_framework+bundle" ]
+ configs += [ "//build/config/compiler:enable_arc" ]
}
diff --git a/chromium/components/cryptauth/BUILD.gn b/chromium/components/cryptauth/BUILD.gn
index b89eaaa91ae..dacb5510543 100644
--- a/chromium/components/cryptauth/BUILD.gn
+++ b/chromium/components/cryptauth/BUILD.gn
@@ -12,9 +12,6 @@ static_library("cryptauth") {
"authenticator.h",
"background_eid_generator.cc",
"background_eid_generator.h",
- "bluetooth_throttler.h",
- "bluetooth_throttler_impl.cc",
- "bluetooth_throttler_impl.h",
"connection.cc",
"connection.h",
"cryptauth_access_token_fetcher.h",
@@ -44,12 +41,14 @@ static_library("cryptauth") {
"data_with_timestamp.h",
"device_to_device_authenticator.cc",
"device_to_device_authenticator.h",
- "device_to_device_initiator_operations.cc",
- "device_to_device_initiator_operations.h",
+ "device_to_device_initiator_helper.cc",
+ "device_to_device_initiator_helper.h",
"device_to_device_secure_context.cc",
"device_to_device_secure_context.h",
"foreground_eid_generator.cc",
"foreground_eid_generator.h",
+ "local_device_data_provider.cc",
+ "local_device_data_provider.h",
"pref_names.cc",
"pref_names.h",
"raw_eid_generator.h",
@@ -124,6 +123,8 @@ static_library("test_support") {
"mock_cryptauth_client.h",
"mock_foreground_eid_generator.cc",
"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",
@@ -148,7 +149,6 @@ source_set("unit_tests") {
testonly = true
sources = [
"background_eid_generator_unittest.cc",
- "bluetooth_throttler_impl_unittest.cc",
"connection_unittest.cc",
"cryptauth_access_token_fetcher_impl_unittest.cc",
"cryptauth_api_call_flow_unittest.cc",
@@ -162,6 +162,7 @@ source_set("unit_tests") {
"device_to_device_secure_context_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_loader_unittest.cc",
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc
index be036e45e61..710c057a0b6 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc
@@ -35,7 +35,6 @@ BluetoothLowEnergyCharacteristicsFinder::
error_callback_(error_callback) {
if (!adapter_) {
error_callback_.Run(to_peripheral_char_, from_peripheral_char_);
- ResetCallbacks();
return;
}
@@ -50,93 +49,78 @@ BluetoothLowEnergyCharacteristicsFinder::
BluetoothLowEnergyCharacteristicsFinder::
~BluetoothLowEnergyCharacteristicsFinder() {
- ResetCallbacks();
if (adapter_) {
adapter_->RemoveObserver(this);
- adapter_ = NULL;
}
}
void BluetoothLowEnergyCharacteristicsFinder::GattCharacteristicAdded(
BluetoothAdapter* adapter,
BluetoothRemoteGattCharacteristic* characteristic) {
- PA_LOG(INFO) << "New char found: "
- << characteristic->GetUUID().canonical_value();
HandleCharacteristicUpdate(characteristic);
}
void BluetoothLowEnergyCharacteristicsFinder::GattDiscoveryCompleteForService(
BluetoothAdapter* adapter,
BluetoothRemoteGattService* service) {
- if (service && service->GetUUID() == remote_service_.uuid) {
- PA_LOG(INFO) << "All characteristics discovered for "
- << remote_service_.uuid.canonical_value();
-
- if (to_peripheral_char_.id.empty() || from_peripheral_char_.id.empty()) {
- if (!error_callback_.is_null()) {
- error_callback_.Run(to_peripheral_char_, from_peripheral_char_);
- ResetCallbacks();
- }
- }
- }
+ if (!service || service->GetUUID() != remote_service_.uuid)
+ return;
+
+ if (!to_peripheral_char_.id.empty() && !from_peripheral_char_.id.empty())
+ return;
+
+ error_callback_.Run(to_peripheral_char_, from_peripheral_char_);
}
void BluetoothLowEnergyCharacteristicsFinder::ScanRemoteCharacteristics(
BluetoothDevice* device,
const BluetoothUUID& service_uuid) {
- PA_LOG(INFO) << "Scanning remote characteristics.";
- if (device) {
- std::vector<BluetoothRemoteGattService*> services =
- device->GetGattServices();
- PA_LOG(INFO) << device->GetAddress() << " has " << services.size()
- << " services.";
- for (const auto* service : services) {
- if (service->GetUUID() == service_uuid) {
- // Right service found, now scaning its characteristics.
- std::vector<device::BluetoothRemoteGattCharacteristic*>
- characteristics = service->GetCharacteristics();
- PA_LOG(INFO) << "Service " << service_uuid.canonical_value() << " has "
- << characteristics.size() << " characteristics.";
- for (auto* characteristic : characteristics) {
- HandleCharacteristicUpdate(characteristic);
- }
- break;
- }
+ if (!device)
+ return;
+
+ for (const auto* service : device->GetGattServices()) {
+ if (service->GetUUID() != service_uuid)
+ continue;
+
+ // Right service found, now scaning its characteristics.
+ std::vector<device::BluetoothRemoteGattCharacteristic*> characteristics =
+ service->GetCharacteristics();
+ for (auto* characteristic : characteristics) {
+ if (HandleCharacteristicUpdate(characteristic))
+ return;
}
+ break;
}
}
-void BluetoothLowEnergyCharacteristicsFinder::HandleCharacteristicUpdate(
+bool BluetoothLowEnergyCharacteristicsFinder::HandleCharacteristicUpdate(
BluetoothRemoteGattCharacteristic* characteristic) {
UpdateCharacteristicsStatus(characteristic);
- if (!to_peripheral_char_.id.empty() && !from_peripheral_char_.id.empty() &&
- !success_callback_.is_null()) {
- PA_LOG(INFO) << "Found write and read characteristics on remote device.";
- success_callback_.Run(remote_service_, to_peripheral_char_,
- from_peripheral_char_);
- ResetCallbacks();
- }
+ if (to_peripheral_char_.id.empty() || from_peripheral_char_.id.empty())
+ return false;
+
+ success_callback_.Run(remote_service_, to_peripheral_char_,
+ from_peripheral_char_);
+ return true;
}
void BluetoothLowEnergyCharacteristicsFinder::UpdateCharacteristicsStatus(
BluetoothRemoteGattCharacteristic* characteristic) {
- if (characteristic &&
- characteristic->GetService()->GetUUID() == remote_service_.uuid) {
- BluetoothUUID uuid = characteristic->GetUUID();
- if (to_peripheral_char_.uuid == uuid)
- to_peripheral_char_.id = characteristic->GetIdentifier();
- if (from_peripheral_char_.uuid == uuid)
- from_peripheral_char_.id = characteristic->GetIdentifier();
-
- BluetoothRemoteGattService* service = characteristic->GetService();
- remote_service_.id = service->GetIdentifier();
+ if (!characteristic ||
+ characteristic->GetService()->GetUUID() != remote_service_.uuid) {
+ return;
}
-}
-void BluetoothLowEnergyCharacteristicsFinder::ResetCallbacks() {
- success_callback_.Reset();
- error_callback_.Reset();
+ BluetoothUUID uuid = characteristic->GetUUID();
+ if (to_peripheral_char_.uuid == uuid)
+ to_peripheral_char_.id = characteristic->GetIdentifier();
+ if (from_peripheral_char_.uuid == uuid)
+ from_peripheral_char_.id = characteristic->GetIdentifier();
+
+ BluetoothRemoteGattService* service = characteristic->GetService();
+ if (service)
+ remote_service_.id = service->GetIdentifier();
}
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h
index 8741765a2d8..35afee241ff 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h
@@ -74,8 +74,9 @@ class BluetoothLowEnergyCharacteristicsFinder
BluetoothLowEnergyCharacteristicsFinder();
private:
- // Handles the discovery of a new characteristic.
- void HandleCharacteristicUpdate(
+ // Handles the discovery of a new characteristic. Returns whether all
+ // characteristics were found.
+ bool HandleCharacteristicUpdate(
device::BluetoothRemoteGattCharacteristic* characteristic);
// Scans the remote chracteristics of the service with |uuid| in |device|
@@ -89,10 +90,6 @@ class BluetoothLowEnergyCharacteristicsFinder
void UpdateCharacteristicsStatus(
device::BluetoothRemoteGattCharacteristic* characteristic);
- // Resets |success_callback_| and |success_callback_|. This should be called
- // whenever a callback is called to avoid multiple callbacks calls.
- void ResetCallbacks();
-
// The Bluetooth adapter where the connection was established.
scoped_refptr<device::BluetoothAdapter> adapter_;
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 d9d959aee0a..d0f0466642a 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
@@ -4,14 +4,16 @@
#include "components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h"
+#include <sstream>
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
#include "base/task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/cryptauth/bluetooth_throttler.h"
+#include "base/timer/timer.h"
#include "components/cryptauth/connection_finder.h"
#include "components/cryptauth/wire_message.h"
#include "components/proximity_auth/logging/logging.h"
@@ -35,6 +37,13 @@ const char kRXCharacteristicUUID[] = "00000100-0004-1000-8000-001A11000102";
// each message gets 3 attempts: the first one, and 2 retries.
const int kMaxNumberOfRetryAttempts = 2;
+// Timeouts for various status types.
+const int kConnectionLatencyTimeoutSeconds = 2;
+const int kGattConnectionTimeoutSeconds = 15;
+const int kGattCharacteristicsTimeoutSeconds = 10;
+const int kNotifySessionTimeoutSeconds = 5;
+const int kConnectionResponseTimeoutSeconds = 2;
+
} // namespace
// static
@@ -48,17 +57,12 @@ BluetoothLowEnergyWeaveClientConnection::Factory::NewInstance(
const RemoteDevice& remote_device,
const std::string& device_address,
scoped_refptr<device::BluetoothAdapter> adapter,
- const device::BluetoothUUID remote_service_uuid,
- BluetoothThrottler* bluetooth_throttler) {
+ const device::BluetoothUUID remote_service_uuid) {
if (!factory_instance_) {
factory_instance_ = new Factory();
}
- return factory_instance_->BuildInstance(
- remote_device,
- device_address,
- adapter,
- remote_service_uuid,
- bluetooth_throttler);
+ return factory_instance_->BuildInstance(remote_device, device_address,
+ adapter, remote_service_uuid);
}
// static
@@ -72,11 +76,56 @@ BluetoothLowEnergyWeaveClientConnection::Factory::BuildInstance(
const RemoteDevice& remote_device,
const std::string& device_address,
scoped_refptr<device::BluetoothAdapter> adapter,
- const device::BluetoothUUID remote_service_uuid,
- BluetoothThrottler* bluetooth_throttler) {
+ const device::BluetoothUUID remote_service_uuid) {
return base::MakeUnique<BluetoothLowEnergyWeaveClientConnection>(
- remote_device, device_address, adapter, remote_service_uuid,
- bluetooth_throttler);
+ remote_device, device_address, adapter, remote_service_uuid);
+}
+
+// static
+base::TimeDelta BluetoothLowEnergyWeaveClientConnection::GetTimeoutForSubStatus(
+ SubStatus sub_status) {
+ switch (sub_status) {
+ case SubStatus::WAITING_CONNECTION_RESPONSE:
+ return base::TimeDelta::FromSeconds(kConnectionResponseTimeoutSeconds);
+ case SubStatus::WAITING_CONNECTION_LATENCY:
+ return base::TimeDelta::FromSeconds(kConnectionLatencyTimeoutSeconds);
+ case SubStatus::WAITING_GATT_CONNECTION:
+ return base::TimeDelta::FromSeconds(kGattConnectionTimeoutSeconds);
+ case SubStatus::WAITING_CHARACTERISTICS:
+ return base::TimeDelta::FromSeconds(kGattCharacteristicsTimeoutSeconds);
+ case SubStatus::WAITING_NOTIFY_SESSION:
+ return base::TimeDelta::FromSeconds(kNotifySessionTimeoutSeconds);
+ default:
+ // Max signifies that there should be no timeout.
+ return base::TimeDelta::Max();
+ }
+}
+
+// static
+std::string BluetoothLowEnergyWeaveClientConnection::SubStatusToString(
+ SubStatus sub_status) {
+ switch (sub_status) {
+ case SubStatus::DISCONNECTED:
+ return "[disconnected]";
+ case SubStatus::WAITING_CONNECTION_LATENCY:
+ return "[waiting to set connection latency]";
+ case SubStatus::WAITING_GATT_CONNECTION:
+ return "[waiting for GATT connection to be created]";
+ case SubStatus::WAITING_CHARACTERISTICS:
+ return "[waiting for GATT characteristics to be found]";
+ case SubStatus::CHARACTERISTICS_FOUND:
+ return "[GATT characteristics have been found]";
+ case SubStatus::WAITING_NOTIFY_SESSION:
+ return "[waiting for notify session to begin]";
+ case SubStatus::NOTIFY_SESSION_READY:
+ return "[notify session is ready]";
+ case SubStatus::WAITING_CONNECTION_RESPONSE:
+ return "[waiting for \"connection response\" uWeave packet]";
+ case SubStatus::CONNECTED:
+ return "[connected]";
+ default:
+ return "[invalid state]";
+ }
}
BluetoothLowEnergyWeaveClientConnection::
@@ -84,81 +133,45 @@ BluetoothLowEnergyWeaveClientConnection::
const RemoteDevice& device,
const std::string& device_address,
scoped_refptr<device::BluetoothAdapter> adapter,
- const device::BluetoothUUID remote_service_uuid,
- BluetoothThrottler* bluetooth_throttler)
+ const device::BluetoothUUID remote_service_uuid)
: Connection(device),
device_address_(device_address),
adapter_(adapter),
- remote_service_({remote_service_uuid, ""}),
+ remote_service_({remote_service_uuid, std::string()}),
packet_generator_(
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance()),
- packet_receiver_(
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
- BluetoothLowEnergyWeavePacketReceiver::ReceiverType::CLIENT)),
- tx_characteristic_({device::BluetoothUUID(kTXCharacteristicUUID), ""}),
- rx_characteristic_({device::BluetoothUUID(kRXCharacteristicUUID), ""}),
- bluetooth_throttler_(bluetooth_throttler),
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>()),
+ packet_receiver_(base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
+ BluetoothLowEnergyWeavePacketReceiver::ReceiverType::CLIENT)),
+ tx_characteristic_(
+ {device::BluetoothUUID(kTXCharacteristicUUID), std::string()}),
+ rx_characteristic_(
+ {device::BluetoothUUID(kRXCharacteristicUUID), std::string()}),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ timer_(base::MakeUnique<base::OneShotTimer>()),
sub_status_(SubStatus::DISCONNECTED),
- write_remote_characteristic_pending_(false),
weak_ptr_factory_(this) {
- DCHECK(adapter_);
- DCHECK(adapter_->IsInitialized());
-
adapter_->AddObserver(this);
}
BluetoothLowEnergyWeaveClientConnection::
~BluetoothLowEnergyWeaveClientConnection() {
- // Since the destructor can be called at anytime, it would be unwise to send a
- // connection close since we might not be connected at all.
DestroyConnection();
-
- if (adapter_) {
- adapter_->RemoveObserver(this);
- adapter_ = nullptr;
- }
}
void BluetoothLowEnergyWeaveClientConnection::Connect() {
DCHECK(sub_status() == SubStatus::DISCONNECTED);
SetSubStatus(SubStatus::WAITING_CONNECTION_LATENCY);
- base::TimeDelta throttler_delay = bluetooth_throttler_->GetDelay();
- PA_LOG(INFO) << "Connecting in " << throttler_delay;
-
- start_time_ = base::TimeTicks::Now();
-
- // If necessary, wait to create a new GATT connection.
- //
- // Avoid creating a new GATT connection immediately after a given device was
- // disconnected. This is a workaround for crbug.com/508919.
- if (!throttler_delay.is_zero()) {
- task_runner_->PostDelayedTask(
- FROM_HERE,
- base::Bind(
- &BluetoothLowEnergyWeaveClientConnection::SetConnectionLatency,
- weak_ptr_factory_.GetWeakPtr()),
- throttler_delay);
- return;
- }
-
- SetConnectionLatency();
-}
-
-void BluetoothLowEnergyWeaveClientConnection::SetConnectionLatency() {
- DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_LATENCY);
-
- PA_LOG(INFO) << "Setting connection latency for " << device_address_;
-
device::BluetoothDevice* bluetooth_device = GetBluetoothDevice();
if (!bluetooth_device) {
- PA_LOG(WARNING) << "Could not create GATT connection with "
- << device_address_ << " because the device could not be "
- << "found.";
+ PA_LOG(WARNING) << "Device not found; cannot set connection latency for "
+ << GetDeviceInfoLogString() << ".";
+ DestroyConnection();
return;
}
+ PA_LOG(INFO) << "Setting connection latency for " << GetDeviceInfoLogString()
+ << ".";
bluetooth_device->SetConnectionLatency(
device::BluetoothDevice::ConnectionLatency::CONNECTION_LATENCY_LOW,
base::Bind(&BluetoothLowEnergyWeaveClientConnection::CreateGattConnection,
@@ -172,16 +185,16 @@ void BluetoothLowEnergyWeaveClientConnection::CreateGattConnection() {
DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_LATENCY);
SetSubStatus(SubStatus::WAITING_GATT_CONNECTION);
- PA_LOG(INFO) << "Creating GATT connection with " << device_address_;
-
device::BluetoothDevice* bluetooth_device = GetBluetoothDevice();
if (!bluetooth_device) {
- PA_LOG(WARNING) << "Could not create GATT connection with "
- << device_address_ << " because the device could not be "
- << "found.";
+ PA_LOG(WARNING) << "Device not found; cannot create GATT connection to "
+ << GetDeviceInfoLogString() << ".";
+ DestroyConnection();
return;
}
+ PA_LOG(INFO) << "Creating GATT connection with " << GetDeviceInfoLogString()
+ << ".";
bluetooth_device->CreateGattConnection(
base::Bind(
&BluetoothLowEnergyWeaveClientConnection::OnGattConnectionCreated,
@@ -193,107 +206,137 @@ void BluetoothLowEnergyWeaveClientConnection::CreateGattConnection() {
void BluetoothLowEnergyWeaveClientConnection::Disconnect() {
if (sub_status_ == SubStatus::CONNECTED) {
- // Friendly disconnect by sending a connection close and then destroy the
- // the connection once the connection close has been sent.
- WriteRequest request(packet_generator_->CreateConnectionClose(
- ReasonForClose::CLOSE_WITHOUT_ERROR),
- WriteRequestType::CONNECTION_CLOSE);
- WriteRemoteCharacteristic(request);
- } else {
- DestroyConnection();
+ PA_LOG(INFO) << "Disconnection requested; sending \"connection close\" "
+ << "uWeave packet to " << GetDeviceInfoLogString() << ".";
+
+ // Send a "connection close" uWeave packet. After the send has completed,
+ // the connection will disconnect automatically.
+ ClearQueueAndSendConnectionClose();
+ return;
}
+
+ DestroyConnection();
}
void BluetoothLowEnergyWeaveClientConnection::DestroyConnection() {
- if (sub_status() != SubStatus::DISCONNECTED) {
- weak_ptr_factory_.InvalidateWeakPtrs();
- StopNotifySession();
- characteristic_finder_.reset();
- if (gatt_connection_) {
- PA_LOG(INFO) << "Disconnect from device "
- << gatt_connection_->GetDeviceAddress();
-
- // Destroying BluetoothGattConnection also disconnects it.
- gatt_connection_.reset();
- }
+ if (adapter_) {
+ adapter_->RemoveObserver(this);
+ adapter_ = nullptr;
+ }
+
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ notify_session_.reset();
+ characteristic_finder_.reset();
- // Only transition to the DISCONNECTED state after perfoming all necessary
- // operations. Otherwise, it'll trigger observers that can pontentially
- // destroy the current instance (causing a crash).
- SetSubStatus(SubStatus::DISCONNECTED);
+ if (gatt_connection_) {
+ PA_LOG(INFO) << "Disconnecting from " << GetDeviceInfoLogString();
+ gatt_connection_.reset();
}
+
+ SetSubStatus(SubStatus::DISCONNECTED);
}
void BluetoothLowEnergyWeaveClientConnection::SetSubStatus(
SubStatus new_sub_status) {
sub_status_ = new_sub_status;
+ timer_->Stop();
+
+ base::TimeDelta timeout_for_sub_status = GetTimeoutForSubStatus(sub_status_);
+ if (!timeout_for_sub_status.is_max()) {
+ timer_->Start(
+ FROM_HERE, timeout_for_sub_status,
+ base::Bind(
+ &BluetoothLowEnergyWeaveClientConnection::OnTimeoutForSubStatus,
+ weak_ptr_factory_.GetWeakPtr(), sub_status_));
+ }
- // Sets the status of parent class Connection accordingly.
- if (new_sub_status == SubStatus::CONNECTED) {
+ // Sets the status of base class Connection.
+ if (new_sub_status == SubStatus::CONNECTED)
SetStatus(Status::CONNECTED);
- } else if (new_sub_status == SubStatus::DISCONNECTED) {
+ else if (new_sub_status == SubStatus::DISCONNECTED)
SetStatus(Status::DISCONNECTED);
- } else {
+ else
SetStatus(Status::IN_PROGRESS);
- }
}
-void BluetoothLowEnergyWeaveClientConnection::SetTaskRunnerForTesting(
- scoped_refptr<base::TaskRunner> task_runner) {
- task_runner_ = task_runner;
+void BluetoothLowEnergyWeaveClientConnection::OnTimeoutForSubStatus(
+ SubStatus status) {
+ // Ensure that |sub_status| is still the active status.
+ DCHECK(status == sub_status());
+
+ PA_LOG(ERROR) << "Timed out waiting during SubStatus "
+ << SubStatusToString(status) << ". Destroying connection.";
+ DestroyConnection();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::SetupTestDoubles(
+ scoped_refptr<base::TaskRunner> test_task_runner,
+ std::unique_ptr<base::Timer> test_timer,
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> test_generator,
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> test_receiver) {
+ task_runner_ = test_task_runner;
+ timer_ = std::move(test_timer);
+ packet_generator_ = std::move(test_generator);
+ packet_receiver_ = std::move(test_receiver);
}
void BluetoothLowEnergyWeaveClientConnection::SendMessageImpl(
std::unique_ptr<WireMessage> message) {
- PA_LOG(INFO) << "Sending message " << message->Serialize();
- std::string serialized_msg = message->Serialize();
-
- std::vector<Packet> packets =
- packet_generator_->EncodeDataMessage(serialized_msg);
+ DCHECK(sub_status() == SubStatus::CONNECTED);
+
+ // Split |message| up into multiple packets which can be sent as one uWeave
+ // message.
+ std::vector<Packet> weave_packets =
+ packet_generator_->EncodeDataMessage(message->Serialize());
+
+ // For each packet, create a WriteRequest and add it to the queue.
+ for (uint32_t i = 0; i < weave_packets.size(); ++i) {
+ WriteRequestType request_type = (i != weave_packets.size() - 1)
+ ? WriteRequestType::REGULAR
+ : WriteRequestType::MESSAGE_COMPLETE;
+ queued_write_requests_.emplace(base::MakeUnique<WriteRequest>(
+ weave_packets[i], request_type, message.get()));
+ }
- std::shared_ptr<WireMessage> request_message(message.release());
+ // Add |message| to the queue of WireMessages.
+ queued_wire_messages_.emplace(std::move(message));
- for (uint32_t i = 0; i < packets.size(); ++i) {
- WriteRequestType request_type = (i == packets.size() - 1)
- ? WriteRequestType::MESSAGE_COMPLETE
- : WriteRequestType::REGULAR;
- WriteRequest request(packets[i], request_type, request_message);
- WriteRemoteCharacteristic(request);
- }
+ ProcessNextWriteRequest();
}
-// Changes in the GATT connection with the remote device should be observed
-// here. If the GATT connection is dropped, we should call DestroyConnection()
-// anyway, so the object can notify its observers.
void BluetoothLowEnergyWeaveClientConnection::DeviceChanged(
device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
- DCHECK(device);
- if (sub_status() == SubStatus::DISCONNECTED ||
- device->GetAddress() != GetDeviceAddress())
+ // Ignore updates about other devices.
+ if (device->GetAddress() != GetDeviceAddress())
return;
- if (sub_status() != SubStatus::WAITING_CONNECTION_LATENCY &&
- sub_status() != SubStatus::WAITING_GATT_CONNECTION &&
- !device->IsConnected()) {
- PA_LOG(INFO) << "GATT connection dropped " << GetDeviceAddress()
- << "\ndevice connected: " << device->IsConnected()
- << "\ngatt connection: "
- << (gatt_connection_ ? gatt_connection_->IsConnected()
- : false);
- DestroyConnection();
+ if (sub_status() == SubStatus::DISCONNECTED ||
+ sub_status() == SubStatus::WAITING_CONNECTION_LATENCY ||
+ sub_status() == SubStatus::WAITING_GATT_CONNECTION) {
+ // Ignore status change events if a connection has not yet occurred.
+ return;
}
+
+ // If a connection has already occurred and |device| is still connected, there
+ // is nothing to do.
+ if (device->IsConnected())
+ return;
+
+ PA_LOG(WARNING) << "GATT connection to " << GetDeviceInfoLogString()
+ << " has been dropped.";
+ DestroyConnection();
}
void BluetoothLowEnergyWeaveClientConnection::DeviceRemoved(
device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
- DCHECK(device);
- if (sub_status_ == SubStatus::DISCONNECTED ||
- device->GetAddress() != GetDeviceAddress())
+ // Ignore updates about other devices.
+ if (device->GetAddress() != GetDeviceAddress())
return;
- PA_LOG(INFO) << "Device removed " << GetDeviceAddress();
+ PA_LOG(WARNING) << "Device has been lost: " << GetDeviceInfoLogString()
+ << ".";
DestroyConnection();
}
@@ -302,58 +345,69 @@ void BluetoothLowEnergyWeaveClientConnection::GattCharacteristicValueChanged(
device::BluetoothRemoteGattCharacteristic* characteristic,
const Packet& value) {
DCHECK_EQ(adapter, adapter_.get());
- if (sub_status() != SubStatus::WAITING_CONNECTION_RESPONSE &&
- sub_status() != SubStatus::CONNECTED)
- return;
- PA_LOG(INFO) << "Characteristic value changed: "
- << characteristic->GetUUID().canonical_value();
+ // Ignore characteristics which do not apply to this connection.
+ if (characteristic->GetIdentifier() != rx_characteristic_.id)
+ return;
- if (characteristic->GetIdentifier() == rx_characteristic_.id) {
- ReceiverState state = packet_receiver_->ReceivePacket(value);
+ if (sub_status() != SubStatus::WAITING_CONNECTION_RESPONSE &&
+ sub_status() != SubStatus::CONNECTED) {
+ PA_LOG(WARNING) << "Received message from " << GetDeviceInfoLogString()
+ << ", but was not expecting one. sub_status() = "
+ << sub_status();
+ return;
+ }
- PA_LOG(INFO) << "\nReceiver State: " << state;
- switch (state) {
- case ReceiverState::DATA_READY:
- OnBytesReceived(packet_receiver_->GetDataMessage());
- break;
- case ReceiverState::CONNECTION_CLOSED:
- PA_LOG(ERROR) << "Connection closed due to: " << GetReasonForClose();
- DestroyConnection();
- break;
- case ReceiverState::ERROR_DETECTED:
- OnPacketReceiverError();
- break;
- case ReceiverState::WAITING:
- // Receiver state should have changed from CONNECTING to WAITING if
- // a proper connection response had been received.
- // The max packet size selected from the connection response will be
- // used to generate future data packets.
- packet_generator_->SetMaxPacketSize(
- packet_receiver_->GetMaxPacketSize());
- DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_RESPONSE);
- CompleteConnection();
- break;
- case ReceiverState::RECEIVING_DATA:
- // Normal in between states, so do nothing.
- break;
- default:
- NOTREACHED();
- }
+ switch (packet_receiver_->ReceivePacket(value)) {
+ case ReceiverState::DATA_READY:
+ OnBytesReceived(packet_receiver_->GetDataMessage());
+ break;
+ case ReceiverState::CONNECTION_CLOSED:
+ PA_LOG(INFO) << "Received \"connection close\" uWeave packet from "
+ << GetDeviceInfoLogString()
+ << ". Reason: " << GetReasonForClose() << ".";
+ DestroyConnection();
+ return;
+ case ReceiverState::ERROR_DETECTED:
+ PA_LOG(ERROR) << "Received invalid packet from "
+ << GetDeviceInfoLogString() << ". ";
+ ClearQueueAndSendConnectionClose();
+ break;
+ case ReceiverState::WAITING:
+ CompleteConnection();
+ break;
+ case ReceiverState::RECEIVING_DATA:
+ // Continue to wait for more packets to arrive; once the rest of the
+ // packets for this message are received, |packet_receiver_| will
+ // transition to the DATA_READY state.
+ break;
+ default:
+ NOTREACHED();
}
}
void BluetoothLowEnergyWeaveClientConnection::CompleteConnection() {
- PA_LOG(INFO) << "Connection completed. Time elapsed: "
- << base::TimeTicks::Now() - start_time_;
+ DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_RESPONSE);
SetSubStatus(SubStatus::CONNECTED);
+
+ uint16_t max_packet_size = packet_receiver_->GetMaxPacketSize();
+ PA_LOG(INFO) << "Received uWeave \"connection response\" packet; connection "
+ << "is now fully initialized for " << GetDeviceInfoLogString()
+ << ". Max packet size: " << max_packet_size;
+
+ // Now that the "connection close" uWeave packet has been received,
+ // |packet_receiver_| should have received a max packet size from the GATT
+ // server.
+ packet_generator_->SetMaxPacketSize(max_packet_size);
}
void BluetoothLowEnergyWeaveClientConnection::OnSetConnectionLatencyError() {
DCHECK(sub_status_ == SubStatus::WAITING_CONNECTION_LATENCY);
- PA_LOG(WARNING) << "Error setting connection latency.";
- // Even if setting the connection latency fails, we should continue with the
- // connection anyways.
+ PA_LOG(WARNING) << "Error setting connection latency for connection to "
+ << GetDeviceInfoLogString() << ".";
+
+ // Even if setting the connection latency fails, continue with the
+ // connection. This is unfortunate but should not be considered a fatal error.
CreateGattConnection();
}
@@ -361,23 +415,19 @@ void BluetoothLowEnergyWeaveClientConnection::OnCreateGattConnectionError(
device::BluetoothDevice::ConnectErrorCode error_code) {
DCHECK(sub_status_ == SubStatus::WAITING_GATT_CONNECTION);
PA_LOG(WARNING) << "Error creating GATT connection to "
- << remote_device().bluetooth_address
- << " error code: " << error_code;
+ << GetDeviceInfoLogString() << ". Error code: " << error_code;
DestroyConnection();
}
void BluetoothLowEnergyWeaveClientConnection::OnGattConnectionCreated(
std::unique_ptr<device::BluetoothGattConnection> gatt_connection) {
DCHECK(sub_status() == SubStatus::WAITING_GATT_CONNECTION);
- PA_LOG(INFO) << "GATT connection with " << gatt_connection->GetDeviceAddress()
- << " created.";
- PrintTimeElapsed();
-
- // Informing |bluetooth_trottler_| a new connection was established.
- bluetooth_throttler_->OnConnection(this);
gatt_connection_ = std::move(gatt_connection);
SetSubStatus(SubStatus::WAITING_CHARACTERISTICS);
+
+ PA_LOG(INFO) << "Finding GATT characteristics for "
+ << GetDeviceInfoLogString() << ".";
characteristic_finder_.reset(CreateCharacteristicsFinder(
base::Bind(
&BluetoothLowEnergyWeaveClientConnection::OnCharacteristicsFound,
@@ -402,15 +452,15 @@ void BluetoothLowEnergyWeaveClientConnection::OnCharacteristicsFound(
const RemoteAttribute& service,
const RemoteAttribute& tx_characteristic,
const RemoteAttribute& rx_characteristic) {
- PA_LOG(INFO) << "Remote chacteristics found.";
- PrintTimeElapsed();
-
DCHECK(sub_status() == SubStatus::WAITING_CHARACTERISTICS);
+
remote_service_ = service;
tx_characteristic_ = tx_characteristic;
rx_characteristic_ = rx_characteristic;
+ characteristic_finder_.reset();
SetSubStatus(SubStatus::CHARACTERISTICS_FOUND);
+
StartNotifySession();
}
@@ -418,38 +468,48 @@ void BluetoothLowEnergyWeaveClientConnection::OnCharacteristicsFinderError(
const RemoteAttribute& tx_characteristic,
const RemoteAttribute& rx_characteristic) {
DCHECK(sub_status() == SubStatus::WAITING_CHARACTERISTICS);
- PA_LOG(WARNING) << "Connection error, missing characteristics for SmartLock "
- "service.\n"
- << (tx_characteristic.id.empty()
- ? tx_characteristic.uuid.canonical_value()
- : "")
- << ", "
- << (rx_characteristic.id.empty()
- ? ", " + rx_characteristic.uuid.canonical_value()
- : "")
- << " not found.";
+
+ std::stringstream ss;
+ ss << "Could not find GATT characteristics for " << GetDeviceInfoLogString()
+ << ": ";
+ if (tx_characteristic.id.empty()) {
+ ss << "[TX: " << tx_characteristic.uuid.canonical_value() << "]";
+ if (!rx_characteristic.id.empty())
+ ss << ", ";
+ }
+ if (rx_characteristic.id.empty())
+ ss << "[RX: " << rx_characteristic.uuid.canonical_value() << "]";
+ PA_LOG(ERROR) << ss.str();
+
+ characteristic_finder_.reset();
DestroyConnection();
}
void BluetoothLowEnergyWeaveClientConnection::StartNotifySession() {
DCHECK(sub_status() == SubStatus::CHARACTERISTICS_FOUND);
+
device::BluetoothRemoteGattCharacteristic* characteristic =
GetGattCharacteristic(rx_characteristic_.id);
- CHECK(characteristic);
+ if (!characteristic) {
+ PA_LOG(ERROR) << "Characteristic no longer available after it was found. "
+ << "Cannot start notification session for "
+ << GetDeviceInfoLogString() << ".";
+ DestroyConnection();
+ return;
+ }
- // This is a workaround for crbug.com/507325. If |characteristic| is already
- // notifying |characteristic->StartNotifySession()| will fail with
- // GATT_ERROR_FAILED.
+ // Workaround for crbug.com/507325. If |characteristic| is already notifying,
+ // characteristic->StartNotifySession() fails with GATT_ERROR_FAILED.
if (characteristic->IsNotifying()) {
- PA_LOG(INFO) << characteristic->GetUUID().canonical_value()
- << " already notifying.";
SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
SendConnectionRequest();
return;
}
SetSubStatus(SubStatus::WAITING_NOTIFY_SESSION);
+ PA_LOG(INFO) << "Starting notification session for "
+ << GetDeviceInfoLogString() << ".";
characteristic->StartNotifySession(
base::Bind(
&BluetoothLowEnergyWeaveClientConnection::OnNotifySessionStarted,
@@ -461,193 +521,228 @@ void BluetoothLowEnergyWeaveClientConnection::StartNotifySession() {
void BluetoothLowEnergyWeaveClientConnection::OnNotifySessionStarted(
std::unique_ptr<device::BluetoothGattNotifySession> notify_session) {
DCHECK(sub_status() == SubStatus::WAITING_NOTIFY_SESSION);
- PA_LOG(INFO) << "Notification session started "
- << notify_session->GetCharacteristicIdentifier();
- PrintTimeElapsed();
-
- SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
notify_session_ = std::move(notify_session);
-
+ SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
SendConnectionRequest();
}
void BluetoothLowEnergyWeaveClientConnection::OnNotifySessionError(
device::BluetoothRemoteGattService::GattErrorCode error) {
DCHECK(sub_status() == SubStatus::WAITING_NOTIFY_SESSION);
- PA_LOG(WARNING) << "Error starting notification session: " << error;
+ PA_LOG(ERROR) << "Cannot start notification session for "
+ << GetDeviceInfoLogString() << ". Error: " << error << ".";
DestroyConnection();
}
-void BluetoothLowEnergyWeaveClientConnection::StopNotifySession() {
- if (notify_session_) {
- notify_session_->Stop(base::Bind(&base::DoNothing));
- notify_session_.reset();
- }
-}
-
void BluetoothLowEnergyWeaveClientConnection::SendConnectionRequest() {
- if (sub_status() == SubStatus::NOTIFY_SESSION_READY) {
- PA_LOG(INFO) << "Sending connection request to the server";
- SetSubStatus(SubStatus::WAITING_CONNECTION_RESPONSE);
+ DCHECK(sub_status() == SubStatus::NOTIFY_SESSION_READY);
+ SetSubStatus(SubStatus::WAITING_CONNECTION_RESPONSE);
- WriteRequest write_request(packet_generator_->CreateConnectionRequest(),
- WriteRequestType::CONNECTION_REQUEST);
+ PA_LOG(INFO) << "Sending \"connection request\" uWeave packet to "
+ << GetDeviceInfoLogString();
- WriteRemoteCharacteristic(write_request);
- }
-}
-
-void BluetoothLowEnergyWeaveClientConnection::WriteRemoteCharacteristic(
- const WriteRequest& request) {
- write_requests_queue_.push(request);
+ queued_write_requests_.emplace(base::MakeUnique<WriteRequest>(
+ packet_generator_->CreateConnectionRequest(),
+ WriteRequestType::CONNECTION_REQUEST));
ProcessNextWriteRequest();
}
void BluetoothLowEnergyWeaveClientConnection::ProcessNextWriteRequest() {
+ // If there is already an in-progress write or there are no pending
+ // WriteRequests, there is nothing to do.
+ if (pending_write_request_ || queued_write_requests_.empty())
+ return;
+
+ pending_write_request_ = std::move(queued_write_requests_.front());
+ queued_write_requests_.pop();
+
+ PA_LOG(INFO) << "Writing " << pending_write_request_->value.size() << " "
+ << "bytes to " << GetDeviceInfoLogString() << ".";
+ SendPendingWriteRequest();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::SendPendingWriteRequest() {
+ DCHECK(pending_write_request_);
+
device::BluetoothRemoteGattCharacteristic* characteristic =
GetGattCharacteristic(tx_characteristic_.id);
- if (!write_requests_queue_.empty() && !write_remote_characteristic_pending_ &&
- characteristic) {
- write_remote_characteristic_pending_ = true;
- const WriteRequest& next_request = write_requests_queue_.front();
-
- PA_LOG(INFO) << "Writing to characteristic " << next_request.value.size()
- << " bytes";
- characteristic->WriteRemoteCharacteristic(
- next_request.value,
- base::Bind(&BluetoothLowEnergyWeaveClientConnection::
- OnRemoteCharacteristicWritten,
- weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&BluetoothLowEnergyWeaveClientConnection::
- OnWriteRemoteCharacteristicError,
- weak_ptr_factory_.GetWeakPtr()));
+ if (!characteristic) {
+ PA_LOG(ERROR) << "Characteristic no longer available after it was found. "
+ << "Cannot process write request for "
+ << GetDeviceInfoLogString() << ".";
+ DestroyConnection();
+ return;
}
+
+ characteristic->WriteRemoteCharacteristic(
+ pending_write_request_->value,
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::
+ OnRemoteCharacteristicWritten,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::
+ OnWriteRemoteCharacteristicError,
+ weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothLowEnergyWeaveClientConnection::OnRemoteCharacteristicWritten() {
- PA_LOG(INFO) << "Characteristic written.";
+ DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_RESPONSE ||
+ sub_status() == SubStatus::CONNECTED);
- DCHECK(!write_requests_queue_.empty());
+ if (!pending_write_request_) {
+ PA_LOG(ERROR) << "OnRemoteCharacteristicWritten() called, but no pending "
+ << "WriteRequest. Stopping connection to "
+ << GetDeviceInfoLogString();
+ DestroyConnection();
+ return;
+ }
- const WriteRequest& request = write_requests_queue_.front();
+ if (pending_write_request_->request_type ==
+ WriteRequestType::CONNECTION_CLOSE) {
+ // Once a "connection close" uWeave packet has been sent, the connection
+ // is ready to be disconnected.
+ DestroyConnection();
+ return;
+ }
- switch (request.request_type) {
- case WriteRequestType::REGULAR:
- case WriteRequestType::CONNECTION_REQUEST:
- break;
- case WriteRequestType::MESSAGE_COMPLETE:
- OnDidSendMessage(*request.message, true);
- break;
- case WriteRequestType::CONNECTION_CLOSE:
+ if (pending_write_request_->request_type ==
+ WriteRequestType::MESSAGE_COMPLETE) {
+ if (queued_wire_messages_.empty()) {
+ PA_LOG(ERROR) << "Sent a WriteRequest with type == MESSAGE_COMPLETE, but "
+ << "there were no queued WireMessages. Cannot process "
+ << "completed write to " << GetDeviceInfoLogString();
DestroyConnection();
return;
- default:
- NOTREACHED();
+ }
+
+ std::unique_ptr<WireMessage> sent_message =
+ std::move(queued_wire_messages_.front());
+ queued_wire_messages_.pop();
+ DCHECK_EQ(sent_message.get(),
+ pending_write_request_->associated_wire_message);
+
+ // Notify observers of the message being sent via a task on the run loop to
+ // ensure that if an observer deletes this object in response to receiving
+ // the OnSendCompleted() callback, a null pointer is not deferenced.
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::OnDidSendMessage,
+ weak_ptr_factory_.GetWeakPtr(), *sent_message,
+ true /* success */));
}
- // Removes the top of queue (already processed) and process the next request.
- write_requests_queue_.pop();
- write_remote_characteristic_pending_ = false;
+ pending_write_request_.reset();
ProcessNextWriteRequest();
}
void BluetoothLowEnergyWeaveClientConnection::OnWriteRemoteCharacteristicError(
device::BluetoothRemoteGattService::GattErrorCode error) {
- PA_LOG(WARNING) << "Error " << error << " writing characteristic: "
- << tx_characteristic_.uuid.canonical_value();
-
- write_remote_characteristic_pending_ = false;
+ DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_RESPONSE ||
+ sub_status() == SubStatus::CONNECTED);
- DCHECK(!write_requests_queue_.empty());
- WriteRequest* request = &write_requests_queue_.front();
+ if (!pending_write_request_) {
+ PA_LOG(ERROR) << "OnWriteRemoteCharacteristicError() called, but no "
+ << "pending WriteRequest. Stopping connection to "
+ << GetDeviceInfoLogString();
+ DestroyConnection();
+ return;
+ }
- ++request->number_of_failed_attempts;
+ ++pending_write_request_->number_of_failed_attempts;
+ if (pending_write_request_->number_of_failed_attempts <
+ kMaxNumberOfRetryAttempts + 1) {
+ PA_LOG(WARNING) << "Error sending WriteRequest to "
+ << GetDeviceInfoLogString() << "; failure number "
+ << pending_write_request_->number_of_failed_attempts
+ << ". Retrying.";
+ SendPendingWriteRequest();
+ return;
+ }
- // If the message has failed to send the first time and up to
- // |kMaxNumberOfRetryAttempts| retries, give up.
- if (request->number_of_failed_attempts >= kMaxNumberOfRetryAttempts + 1) {
- switch (request->request_type) {
- case WriteRequestType::REGULAR:
- case WriteRequestType::MESSAGE_COMPLETE:
- OnDidSendMessage(*request->message, false);
- break;
- case WriteRequestType::CONNECTION_CLOSE:
- case WriteRequestType::CONNECTION_REQUEST:
- break;
- default:
- NOTREACHED();
- }
+ if (pending_write_request_->request_type == WriteRequestType::REGULAR ||
+ pending_write_request_->request_type ==
+ WriteRequestType::MESSAGE_COMPLETE) {
+ std::unique_ptr<WireMessage> failed_message =
+ std::move(queued_wire_messages_.front());
+ queued_wire_messages_.pop();
+ DCHECK_EQ(failed_message.get(),
+ pending_write_request_->associated_wire_message);
- // If the previous write has failed each retry attempt up to the maximum
- // number allowed, a "connection close" message cannot be sent since the
- // connection is not working. Destroy the connection.
- DestroyConnection();
- } else {
- ProcessNextWriteRequest();
+ OnDidSendMessage(*failed_message, false /* success */);
}
+
+ // Since the try limit has been hit, this is a fatal error. Destroy the
+ // connection, but post it as a new task to ensure that observers have a
+ // chance to process the OnSendCompleted() call.
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::DestroyConnection,
+ weak_ptr_factory_.GetWeakPtr()));
}
-void BluetoothLowEnergyWeaveClientConnection::OnPacketReceiverError() {
- PA_LOG(ERROR) << "Received erroneous packet. Closing connection.";
+void BluetoothLowEnergyWeaveClientConnection::OnDidSendMessage(
+ const WireMessage& message,
+ bool success) {
+ Connection::OnDidSendMessage(message, success);
+}
- WriteRequest request(packet_generator_->CreateConnectionClose(
- packet_receiver_->GetReasonToClose()),
- WriteRequestType::CONNECTION_CLOSE);
+void BluetoothLowEnergyWeaveClientConnection::
+ ClearQueueAndSendConnectionClose() {
+ DCHECK(sub_status() == SubStatus::WAITING_CONNECTION_RESPONSE ||
+ sub_status() == SubStatus::CONNECTED);
- // Skip all other writes that's not in progress.
+ // The connection is now in an invalid state. Clear queued writes.
+ while (!queued_write_requests_.empty())
+ queued_write_requests_.pop();
- // C++ queue does not have a clear method.
- // According to stackoverflow
- // "http://stackoverflow.com/questions/709146/how-do-i-clear-the-stdqueue-
- // efficiently"
- std::queue<WriteRequest> empty;
- std::swap(write_requests_queue_, empty);
+ // Now, queue up a "connection close" uWeave packet. If there was a pending
+ // write, we must wait for it to complete before the "connection close" can
+ // be sent.
+ queued_write_requests_.emplace(
+ base::MakeUnique<WriteRequest>(packet_generator_->CreateConnectionClose(
+ packet_receiver_->GetReasonToClose()),
+ WriteRequestType::CONNECTION_CLOSE));
- if (write_remote_characteristic_pending_) {
- // Add the in progress write back to the queue.
- write_requests_queue_.push(empty.front());
+ if (pending_write_request_) {
+ PA_LOG(WARNING) << "Waiting for current write to complete, then will send "
+ << "a \"connection close\" uWeave packet.";
+ } else {
+ PA_LOG(INFO) << "Sending a \"connection close\" uWeave packet.";
}
- WriteRemoteCharacteristic(request);
-}
-
-void BluetoothLowEnergyWeaveClientConnection::PrintTimeElapsed() {
- PA_LOG(INFO) << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
+ ProcessNextWriteRequest();
}
std::string BluetoothLowEnergyWeaveClientConnection::GetDeviceAddress() {
- // When the remote device is connected we should rely on the address given by
- // |gatt_connection_|. As the device address may change if the device is
- // paired. The address in |gatt_connection_| is automatically updated in this
- // case.
+ // When the remote device is connected, rely on the address given by
+ // |gatt_connection_|. Unpaired BLE device addresses are ephemeral and are
+ // expected to change periodically.
return gatt_connection_ ? gatt_connection_->GetDeviceAddress()
: device_address_;
}
device::BluetoothDevice*
BluetoothLowEnergyWeaveClientConnection::GetBluetoothDevice() {
- return adapter_->GetDevice(device_address_);
+ return adapter_->GetDevice(GetDeviceAddress());
}
device::BluetoothRemoteGattService*
BluetoothLowEnergyWeaveClientConnection::GetRemoteService() {
device::BluetoothDevice* bluetooth_device = GetBluetoothDevice();
if (!bluetooth_device) {
- PA_LOG(WARNING) << "Could not create GATT connection with "
- << device_address_ << " because the device could not be "
- << "found.";
+ PA_LOG(WARNING) << "Cannot find Bluetooth device for "
+ << GetDeviceInfoLogString();
return nullptr;
}
if (remote_service_.id.empty()) {
- std::vector<device::BluetoothRemoteGattService*> services =
- bluetooth_device->GetGattServices();
- for (auto* service : services)
+ for (auto* service : bluetooth_device->GetGattServices()) {
if (service->GetUUID() == remote_service_.uuid) {
remote_service_.id = service->GetIdentifier();
break;
}
+ }
}
+
return bluetooth_device->GetGattService(remote_service_.id);
}
@@ -656,7 +751,8 @@ BluetoothLowEnergyWeaveClientConnection::GetGattCharacteristic(
const std::string& gatt_characteristic) {
device::BluetoothRemoteGattService* remote_service = GetRemoteService();
if (!remote_service) {
- PA_LOG(WARNING) << "Remote service not found.";
+ PA_LOG(WARNING) << "Cannot find GATT service for "
+ << GetDeviceInfoLogString();
return nullptr;
}
return remote_service->GetCharacteristic(gatt_characteristic);
@@ -683,22 +779,15 @@ std::string BluetoothLowEnergyWeaveClientConnection::GetReasonForClose() {
BluetoothLowEnergyWeaveClientConnection::WriteRequest::WriteRequest(
const Packet& val,
WriteRequestType request_type,
- std::shared_ptr<WireMessage> message)
+ WireMessage* associated_wire_message)
: value(val),
request_type(request_type),
- message(message),
- number_of_failed_attempts(0) {}
+ associated_wire_message(associated_wire_message) {}
BluetoothLowEnergyWeaveClientConnection::WriteRequest::WriteRequest(
const Packet& val,
WriteRequestType request_type)
- : value(val),
- request_type(request_type),
- message(nullptr),
- number_of_failed_attempts(0) {}
-
-BluetoothLowEnergyWeaveClientConnection::WriteRequest::WriteRequest(
- const WriteRequest& other) = default;
+ : WriteRequest(val, request_type, nullptr /* associated_wire_message */) {}
BluetoothLowEnergyWeaveClientConnection::WriteRequest::~WriteRequest() {}
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 e86990c4526..1a74e45c6a9 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
@@ -1,7 +1,6 @@
// 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_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_CLIENT_CONNECTION_H_
#define COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_CLIENT_CONNECTION_H_
@@ -17,12 +16,12 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
+#include "base/timer/timer.h"
#include "components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h"
#include "components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h"
#include "components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h"
#include "components/cryptauth/ble/fake_wire_message.h"
#include "components/cryptauth/ble/remote_attribute.h"
-#include "components/cryptauth/bluetooth_throttler.h"
#include "components/cryptauth/connection.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
@@ -40,6 +39,7 @@ namespace weave {
// Creates GATT connection on top of the BLE connection and act as a Client.
// uWeave communication follows the flow:
+//
// Client | Server
// ---------------------------------|--------------------------------
// send connection request |
@@ -59,27 +59,20 @@ class BluetoothLowEnergyWeaveClientConnection
const RemoteDevice& remote_device,
const std::string& device_address,
scoped_refptr<device::BluetoothAdapter> adapter,
- const device::BluetoothUUID remote_service_uuid,
- BluetoothThrottler* bluetooth_throttler);
-
- // Exposed for testing.
+ const device::BluetoothUUID remote_service_uuid);
static void SetInstanceForTesting(Factory* factory);
protected:
- // Exposed for testing.
virtual std::unique_ptr<Connection> BuildInstance(
const RemoteDevice& remote_device,
const std::string& device_address,
scoped_refptr<device::BluetoothAdapter> adapter,
- const device::BluetoothUUID remote_service_uuid,
- BluetoothThrottler* bluetooth_throttler);
+ const device::BluetoothUUID remote_service_uuid);
private:
static Factory* factory_instance_;
};
- // The sub-state of a BluetoothLowEnergyWeaveClientConnection
- // extends the IN_PROGRESS state of Connection::Status.
enum SubStatus {
DISCONNECTED,
WAITING_CONNECTION_LATENCY,
@@ -92,47 +85,44 @@ class BluetoothLowEnergyWeaveClientConnection
CONNECTED,
};
- // Constructs a Bluetooth low energy connection to the service with
- // |remote_service_| on the |remote_device|. The |adapter| must be already
- // initialized and ready. The GATT connection may alreaady be established and
- // pass through |gatt_connection|. A subsequent call to Connect() must be
- // made.
+ // Constructs the Connection object; a subsequent call to Connect() is
+ // necessary to initiate the BLE connection.
BluetoothLowEnergyWeaveClientConnection(
const RemoteDevice& remote_device,
const std::string& device_address,
scoped_refptr<device::BluetoothAdapter> adapter,
- const device::BluetoothUUID remote_service_uuid,
- BluetoothThrottler* bluetooth_throttler);
+ const device::BluetoothUUID remote_service_uuid);
~BluetoothLowEnergyWeaveClientConnection() override;
- // namespace Connection:
+ // Connection:
void Connect() override;
void Disconnect() override;
std::string GetDeviceAddress() override;
protected:
- // Exposed for testing.
- // NOTE: This method may indirectly cause this object's destructor to be
- // called. Do not perform any operations that touch the internals of this
- // class after calling this method.
+ // Destroys the connection immediately; if there was an active connection, it
+ // will be disconnected after this call. Note that this function may notify
+ // observers of a connection status change.
void DestroyConnection();
- // Exposed for testing.
SubStatus sub_status() { return sub_status_; }
- // Sets |task_runner_| for testing.
- void SetTaskRunnerForTesting(scoped_refptr<base::TaskRunner> task_runner);
+ void SetupTestDoubles(
+ scoped_refptr<base::TaskRunner> test_task_runner,
+ std::unique_ptr<base::Timer> test_timer,
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> test_generator,
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> test_receiver);
- // Virtual for testing.
virtual BluetoothLowEnergyCharacteristicsFinder* CreateCharacteristicsFinder(
const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
success_callback,
const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
error_callback);
- // namespace Connection:
+ // Connection:
void SendMessageImpl(std::unique_ptr<WireMessage> message) override;
+ void OnDidSendMessage(const WireMessage& message, bool success) override;
// device::BluetoothAdapter::Observer:
void DeviceChanged(device::BluetoothAdapter* adapter,
@@ -158,169 +148,107 @@ class BluetoothLowEnergyWeaveClientConnection
struct WriteRequest {
WriteRequest(const Packet& val,
WriteRequestType request_type,
- std::shared_ptr<WireMessage> message);
+ WireMessage* associated_wire_message);
WriteRequest(const Packet& val, WriteRequestType request_type);
- WriteRequest(const WriteRequest& other);
- ~WriteRequest();
+ WriteRequest(const WireMessage& other);
+ virtual ~WriteRequest();
Packet value;
WriteRequestType request_type;
- std::shared_ptr<WireMessage> message;
- int number_of_failed_attempts;
+ WireMessage* associated_wire_message;
+ int number_of_failed_attempts = 0;
};
+ static std::string SubStatusToString(SubStatus sub_status);
+
+ // Returns the timeout for the given SubStatus. If no timeout is needed for
+ // |sub_status|, base::TimeDelta::Max() is returned.
+ static base::TimeDelta GetTimeoutForSubStatus(SubStatus sub_status);
+
+ // Sets the current status; if |status| corresponds to one of Connection's
+ // Status types, observers will be notified of the change.
void SetSubStatus(SubStatus status);
+ void OnTimeoutForSubStatus(SubStatus status);
- // Sets the connection interval before connecting.
+ // These functions are used to set up the connection so that it is ready to
+ // send/receive data.
void SetConnectionLatency();
-
- // Creates the GATT connection with |remote_device|.
void CreateGattConnection();
-
- // Called when a GATT connection is created.
void OnGattConnectionCreated(
std::unique_ptr<device::BluetoothGattConnection> gatt_connection);
-
- // Callback when there is an error setting the connection interval.
void OnSetConnectionLatencyError();
-
- // Callback called when there is an error creating the GATT connection.
void OnCreateGattConnectionError(
device::BluetoothDevice::ConnectErrorCode error_code);
-
- // Callback called when |tx_characteristic_| and |rx_characteristic_| were
- // found.
void OnCharacteristicsFound(const RemoteAttribute& service,
const RemoteAttribute& tx_characteristic,
const RemoteAttribute& rx_characteristic);
-
- // Callback called there was an error finding the characteristics.
void OnCharacteristicsFinderError(const RemoteAttribute& tx_characteristic,
const RemoteAttribute& rx_characteristic);
-
- // Starts a notify session for |rx_characteristic_| when ready
- // (SubStatus::CHARACTERISTICS_FOUND).
void StartNotifySession();
-
- // Called when a notification session is successfully started for
- // |rx_characteristic_| characteristic.
void OnNotifySessionStarted(
std::unique_ptr<device::BluetoothGattNotifySession> notify_session);
-
- // Called when there is an error starting a notification session for
- // |rx_characteristic_| characteristic.
void OnNotifySessionError(device::BluetoothGattService::GattErrorCode);
- // Stops |notify_session_|.
- void StopNotifySession();
-
- // Sends a connection request to server if ready
- // (SubStatus::NOTIFY_SESSION_READY).
+ // Sends the connection request message (the first message in the uWeave
+ // handshake).
void SendConnectionRequest();
// Completes and updates the status accordingly.
void CompleteConnection();
- // This is the only entry point for WriteRequests, which are processed
- // accordingly the following flow:
- // 1) |request| is enqueued;
- // 2) |request| will be processed by ProcessNextWriteRequest() when there is
- // no pending write request;
- // 3) |request| will be dequeued when it's successfully processed
- // (OnRemoteCharacteristicWritten());
- // 4) |request| is not dequeued if it fails
- // (OnWriteRemoteCharacteristicError()), it remains on the queue and will be
- // retried. |request| will remain on the queue until it succeeds or it
- // triggers a Disconnect() call (after |max_number_of_tries_|).
- void WriteRemoteCharacteristic(const WriteRequest& request);
-
- // Processes the next request in |write_requests_queue_|.
+ // If no write is in progress and there are queued packets, sends the next
+ // packet; if there is already a write in progress or there are no queued
+ // packets, this function is a no-op.
void ProcessNextWriteRequest();
- // Called when the
- // BluetoothRemoteGattCharacteristic::RemoteCharacteristicWrite() is
- // successfully complete.
+ void SendPendingWriteRequest();
void OnRemoteCharacteristicWritten();
-
- // Called when there is an error writing to the remote characteristic
- // |tx_characteristic_|.
void OnWriteRemoteCharacteristicError(
device::BluetoothRemoteGattService::GattErrorCode error);
+ void ClearQueueAndSendConnectionClose();
- void OnPacketReceiverError();
-
- // Prints the time elapsed since |Connect()| was called.
- void PrintTimeElapsed();
-
- // Returns the service corresponding to |remote_service_| in the current
- // device.
+ // Private getters for the Bluetooth classes corresponding to this connection.
device::BluetoothRemoteGattService* GetRemoteService();
-
- // Returns the characteristic corresponding to |identifier| in the current
- // service.
device::BluetoothRemoteGattCharacteristic* GetGattCharacteristic(
const std::string& identifier);
+ device::BluetoothDevice* GetBluetoothDevice();
// Get the reason that the other side of the connection decided to close the
// connection.
- // Will crash if the receiver is not in CONNECTION_CLOSED state.
std::string GetReasonForClose();
- // Returns the device corresponding to |device_address_|.
- device::BluetoothDevice* GetBluetoothDevice();
-
- // The device to which to connect.
+ // The device to which to connect. This is the starting value, but the device
+ // address may change during the connection because BLE addresses are
+ // ephemeral. Use GetDeviceAddress() to get the most up-to-date address.
const std::string device_address_;
- // The Bluetooth adapter over which the Bluetooth connection will be made.
scoped_refptr<device::BluetoothAdapter> adapter_;
-
- // Remote service the |gatt_connection_| was established with.
RemoteAttribute remote_service_;
-
- // uWeave packet generator.
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> packet_generator_;
-
- // uWeave packet receiver.
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> packet_receiver_;
-
- // Characteristic used to send data to the remote device.
RemoteAttribute tx_characteristic_;
-
- // Characteristic used to receive data from the remote device.
RemoteAttribute rx_characteristic_;
-
- // Throttles repeated connection attempts to the same device. This is a
- // workaround for crbug.com/508919. Not owned, must outlive this instance.
- BluetoothThrottler* bluetooth_throttler_;
-
scoped_refptr<base::TaskRunner> task_runner_;
+ std::unique_ptr<base::Timer> timer_;
- // The GATT connection with the remote device.
+ // These pointers start out null and are created during the connection
+ // process.
std::unique_ptr<device::BluetoothGattConnection> gatt_connection_;
-
- // The characteristics finder for remote device.
std::unique_ptr<BluetoothLowEnergyCharacteristicsFinder>
characteristic_finder_;
-
- // The notify session for |rx_characteristic|.
std::unique_ptr<device::BluetoothGattNotifySession> notify_session_;
- // Internal connection status
SubStatus sub_status_;
- // Bytes already received for the current receive operation.
- std::string incoming_bytes_buffer_;
-
- // Indicates there is a
- // BluetoothRemoteGattCharacteristic::WriteRemoteCharacteristic
- // operation pending.
- bool write_remote_characteristic_pending_;
-
- std::queue<WriteRequest> write_requests_queue_;
+ // The WriteRequest that is currently being sent as well as those queued to be
+ // sent. Each WriteRequest corresponds to one uWeave packet to be sent.
+ std::unique_ptr<WriteRequest> pending_write_request_;
+ std::queue<std::unique_ptr<WriteRequest>> queued_write_requests_;
- // Stores when the instace was created.
- base::TimeTicks start_time_;
+ // WireMessages queued to be sent. Each WireMessage correponds to one or more
+ // WriteRequests. WireMessages remain in this queue until the last
+ // corresponding WriteRequest has been sent.
+ std::queue<std::unique_ptr<WireMessage>> queued_wire_messages_;
base::WeakPtrFactory<BluetoothLowEnergyWeaveClientConnection>
weak_ptr_factory_;
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 894860c383a..87333d3ddb7 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
@@ -14,7 +14,8 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/test_simple_task_runner.h"
-#include "components/cryptauth/bluetooth_throttler.h"
+#include "base/timer/mock_timer.h"
+#include "base/timer/timer.h"
#include "components/cryptauth/connection_finder.h"
#include "components/cryptauth/connection_observer.h"
#include "components/cryptauth/cryptauth_test_util.h"
@@ -39,78 +40,9 @@ using testing::StrictMock;
using testing::SaveArg;
namespace cryptauth {
-namespace {
-
-const std::string kTestFeature = "testFeature";
-
-class MockBluetoothThrottler : public BluetoothThrottler {
- public:
- MockBluetoothThrottler() {}
- ~MockBluetoothThrottler() override {}
-
- MOCK_CONST_METHOD0(GetDelay, base::TimeDelta());
- MOCK_METHOD1(OnConnection, void(Connection* connection));
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockBluetoothThrottler);
-};
-
-class MockBluetoothLowEnergyCharacteristicsFinder
- : public BluetoothLowEnergyCharacteristicsFinder {
- public:
- MockBluetoothLowEnergyCharacteristicsFinder() {}
- ~MockBluetoothLowEnergyCharacteristicsFinder() override {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockBluetoothLowEnergyCharacteristicsFinder);
-};
-
-class MockConnectionObserver : public ConnectionObserver {
- public:
- MockConnectionObserver()
- : num_send_completed_(0), delete_on_disconnect_(false) {}
-
- void OnConnectionStatusChanged(Connection* connection,
- Connection::Status old_status,
- Connection::Status new_status) override {
- if (new_status == Connection::Status::DISCONNECTED && delete_on_disconnect_)
- delete connection;
- }
-
- void OnMessageReceived(const Connection& connection,
- const WireMessage& message) override {}
-
- void OnSendCompleted(const Connection& conenction,
- const WireMessage& message,
- bool success) override {
- last_deserialized_message_ = message.payload();
- last_send_success_ = success;
- num_send_completed_++;
- }
-
- std::string GetLastDeserializedMessage() {
- return last_deserialized_message_;
- }
-
- bool GetLastSendSuccess() { return last_send_success_; }
-
- int GetNumSendCompleted() { return num_send_completed_; }
-
- bool delete_on_disconnect() { return delete_on_disconnect_; }
- void set_delete_on_disconnect(bool delete_on_disconnect) {
- delete_on_disconnect_ = delete_on_disconnect;
- }
-
- private:
- std::string last_deserialized_message_;
- bool last_send_success_;
- int num_send_completed_;
- bool delete_on_disconnect_;
-};
-
-} // namespace
namespace weave {
+
namespace {
typedef BluetoothLowEnergyWeaveClientConnection::SubStatus SubStatus;
@@ -118,6 +50,8 @@ typedef BluetoothLowEnergyWeavePacketReceiver::State ReceiverState;
typedef BluetoothLowEnergyWeavePacketReceiver::ReceiverError ReceiverError;
typedef BluetoothLowEnergyWeavePacketReceiver::ReceiverType ReceiverType;
+const char kTestFeature[] = "testFeature";
+
const char kServiceUUID[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEEF";
const char kTXCharacteristicUUID[] = "977c6674-1239-4e72-993b-502369b8bb5a";
const char kRXCharacteristicUUID[] = "f4b904a2-a030-43b3-98a8-221c536c03cb";
@@ -188,11 +122,11 @@ class MockBluetoothLowEnergyWeavePacketGenerator
void SetMaxPacketSize(uint16_t size) override { max_packet_size_ = size; }
std::vector<Packet> EncodeDataMessage(std::string message) override {
- if (message == (kTestFeature + "," + kSmallMessage)
- && max_packet_size_ == kDefaultMaxPacketSize) {
+ if (message == (std::string(kTestFeature) + "," + kSmallMessage) &&
+ max_packet_size_ == kDefaultMaxPacketSize) {
return kSmallPackets;
- } else if (message == (kTestFeature + "," + kLargeMessage)
- && max_packet_size_ == kLargeMaxPacketSize) {
+ } else if (message == (std::string(kTestFeature) + "," + kLargeMessage) &&
+ max_packet_size_ == kLargeMaxPacketSize) {
return kLargePackets;
} else {
NOTREACHED();
@@ -271,46 +205,6 @@ class MockBluetoothLowEnergyWeavePacketReceiver
ReasonForClose reason_to_close_;
};
-class MockBluetoothLowEnergyWeavePacketGeneratorFactory
- : public BluetoothLowEnergyWeavePacketGenerator::Factory {
- public:
- // most_recent_instance_ will be obsolete after the connection class
- // destructs. Do not use if that's the case.
- MockBluetoothLowEnergyWeavePacketGenerator* GetMostRecentInstance() {
- return most_recent_instance_;
- }
-
- private:
- std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> BuildInstance()
- override {
- most_recent_instance_ = new MockBluetoothLowEnergyWeavePacketGenerator();
- return std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>(
- most_recent_instance_);
- }
-
- MockBluetoothLowEnergyWeavePacketGenerator* most_recent_instance_;
-};
-
-class MockBluetoothLowEnergyWeavePacketReceiverFactory
- : public BluetoothLowEnergyWeavePacketReceiver::Factory {
- public:
- // most_recent_instance_ will be obsolete after the connection class
- // destructs. Do not use if that's the case.
- MockBluetoothLowEnergyWeavePacketReceiver* GetMostRecentInstance() {
- return most_recent_instance_;
- }
-
- private:
- std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> BuildInstance(
- ReceiverType receiver_type) override {
- most_recent_instance_ = new MockBluetoothLowEnergyWeavePacketReceiver();
- return std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver>(
- most_recent_instance_);
- }
-
- MockBluetoothLowEnergyWeavePacketReceiver* most_recent_instance_;
-};
-
class TestBluetoothLowEnergyWeaveClientConnection
: public BluetoothLowEnergyWeaveClientConnection {
public:
@@ -318,13 +212,11 @@ class TestBluetoothLowEnergyWeaveClientConnection
const RemoteDevice& remote_device,
const std::string& device_address,
scoped_refptr<device::BluetoothAdapter> adapter,
- const device::BluetoothUUID remote_service_uuid,
- BluetoothThrottler* bluetooth_throttler)
+ const device::BluetoothUUID remote_service_uuid)
: BluetoothLowEnergyWeaveClientConnection(remote_device,
device_address,
adapter,
- remote_service_uuid,
- bluetooth_throttler) {}
+ remote_service_uuid) {}
~TestBluetoothLowEnergyWeaveClientConnection() override {}
@@ -339,7 +231,7 @@ class TestBluetoothLowEnergyWeaveClientConnection
// Exposing inherited protected methods for testing.
using BluetoothLowEnergyWeaveClientConnection::GattCharacteristicValueChanged;
- using BluetoothLowEnergyWeaveClientConnection::SetTaskRunnerForTesting;
+ using BluetoothLowEnergyWeaveClientConnection::SetupTestDoubles;
using BluetoothLowEnergyWeaveClientConnection::DestroyConnection;
// Exposing inherited protected fields for testing.
@@ -350,43 +242,96 @@ class TestBluetoothLowEnergyWeaveClientConnection
DISALLOW_COPY_AND_ASSIGN(TestBluetoothLowEnergyWeaveClientConnection);
};
+class MockBluetoothLowEnergyCharacteristicsFinder
+ : public BluetoothLowEnergyCharacteristicsFinder {
+ public:
+ MockBluetoothLowEnergyCharacteristicsFinder() {}
+ ~MockBluetoothLowEnergyCharacteristicsFinder() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockBluetoothLowEnergyCharacteristicsFinder);
+};
+
+class MockConnectionObserver : public ConnectionObserver {
+ public:
+ MockConnectionObserver(Connection* connection)
+ : connection_(connection),
+ num_send_completed_(0),
+ delete_on_disconnect_(false),
+ delete_on_message_sent_(false) {}
+
+ void OnConnectionStatusChanged(Connection* connection,
+ Connection::Status old_status,
+ Connection::Status new_status) override {
+ if (new_status == Connection::Status::DISCONNECTED && delete_on_disconnect_)
+ delete connection_;
+ }
+
+ void OnMessageReceived(const Connection& connection,
+ const WireMessage& message) override {}
+
+ void OnSendCompleted(const Connection& conenction,
+ const WireMessage& message,
+ bool success) override {
+ last_deserialized_message_ = message.payload();
+ last_send_success_ = success;
+ num_send_completed_++;
+
+ if (delete_on_message_sent_)
+ delete connection_;
+ }
+
+ std::string GetLastDeserializedMessage() {
+ return last_deserialized_message_;
+ }
+
+ bool GetLastSendSuccess() { return last_send_success_; }
+
+ int GetNumSendCompleted() { return num_send_completed_; }
+
+ bool delete_on_disconnect() { return delete_on_disconnect_; }
+
+ void set_delete_on_disconnect(bool delete_on_disconnect) {
+ delete_on_disconnect_ = delete_on_disconnect;
+ }
+
+ void set_delete_on_message_sent(bool delete_on_message_sent) {
+ delete_on_message_sent_ = delete_on_message_sent;
+ }
+
+ private:
+ Connection* connection_;
+ std::string last_deserialized_message_;
+ bool last_send_success_;
+ int num_send_completed_;
+ bool delete_on_disconnect_;
+ bool delete_on_message_sent_;
+};
+
} // namespace
class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
: public testing::Test {
public:
CryptAuthBluetoothLowEnergyWeaveClientConnectionTest()
- : adapter_(new NiceMock<device::MockBluetoothAdapter>),
- remote_device_(CreateLERemoteDeviceForTest()),
+ : remote_device_(CreateLERemoteDeviceForTest()),
service_uuid_(device::BluetoothUUID(kServiceUUID)),
tx_characteristic_uuid_(device::BluetoothUUID(kTXCharacteristicUUID)),
- rx_characteristic_uuid_(device::BluetoothUUID(kRXCharacteristicUUID)),
- notify_session_alias_(NULL),
- bluetooth_throttler_(new NiceMock<MockBluetoothThrottler>),
- task_runner_(new base::TestSimpleTaskRunner),
- generator_factory_(
- new MockBluetoothLowEnergyWeavePacketGeneratorFactory()),
- receiver_factory_(
- new MockBluetoothLowEnergyWeavePacketReceiverFactory()) {
- BluetoothLowEnergyWeavePacketGenerator::Factory::SetInstanceForTesting(
- generator_factory_.get());
- BluetoothLowEnergyWeavePacketReceiver::Factory::SetInstanceForTesting(
- receiver_factory_.get());
- }
-
- ~CryptAuthBluetoothLowEnergyWeaveClientConnectionTest() override {
- BluetoothLowEnergyWeavePacketGenerator::Factory::SetInstanceForTesting(
- nullptr);
- BluetoothLowEnergyWeavePacketReceiver::Factory::SetInstanceForTesting(
- nullptr);
- }
+ rx_characteristic_uuid_(device::BluetoothUUID(kRXCharacteristicUUID)) {}
+ ~CryptAuthBluetoothLowEnergyWeaveClientConnectionTest() override {}
void SetUp() override {
+ test_timer_ = nullptr;
+ generator_ = nullptr;
+ receiver_ = nullptr;
+
+ adapter_ = make_scoped_refptr(new NiceMock<device::MockBluetoothAdapter>());
+ task_runner_ = make_scoped_refptr(new base::TestSimpleTaskRunner());
+
mock_bluetooth_device_ =
base::MakeUnique<NiceMock<device::MockBluetoothDevice>>(
adapter_.get(), 0, kTestRemoteDeviceName,
kTestRemoteDeviceBluetoothAddress, false, false);
-
service_ = base::MakeUnique<NiceMock<device::MockBluetoothGattService>>(
mock_bluetooth_device_.get(), kServiceID, service_uuid_, true, false);
tx_characteristic_ =
@@ -394,15 +339,12 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
service_.get(), kTXCharacteristicID, tx_characteristic_uuid_, false,
kCharacteristicProperties,
device::BluetoothRemoteGattCharacteristic::PERMISSION_NONE);
-
rx_characteristic_ =
base::MakeUnique<NiceMock<device::MockBluetoothGattCharacteristic>>(
service_.get(), kRXCharacteristicID, rx_characteristic_uuid_, false,
kCharacteristicProperties,
device::BluetoothRemoteGattCharacteristic::PERMISSION_NONE);
- device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
-
std::vector<const device::BluetoothDevice*> devices;
devices.push_back(mock_bluetooth_device_.get());
ON_CALL(*adapter_, GetDevices()).WillByDefault(Return(devices));
@@ -414,8 +356,12 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
.WillByDefault(Return(rx_characteristic_.get()));
ON_CALL(*service_, GetCharacteristic(kTXCharacteristicID))
.WillByDefault(Return(tx_characteristic_.get()));
+
+ device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
}
+ void TearDown() override { connection_observer_.reset(); }
+
// Creates a BluetoothLowEnergyWeaveClientConnection and verifies it's in
// DISCONNECTED state.
std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection>
@@ -426,15 +372,23 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
new TestBluetoothLowEnergyWeaveClientConnection(
remote_device_, kTestRemoteDeviceBluetoothAddress, adapter_,
- service_uuid_, bluetooth_throttler_.get()));
+ service_uuid_));
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
// Add the mock observer to observe on OnDidMessageSend.
- connection->AddObserver(&connection_observer_);
-
- connection->SetTaskRunnerForTesting(task_runner_);
+ connection_observer_ =
+ base::WrapUnique(new MockConnectionObserver(connection.get()));
+ connection->AddObserver(connection_observer_.get());
+
+ test_timer_ = new base::MockTimer(false /* retains_user_task */,
+ false /* is_repeating */);
+ generator_ = new NiceMock<MockBluetoothLowEnergyWeavePacketGenerator>();
+ receiver_ = new NiceMock<MockBluetoothLowEnergyWeavePacketReceiver>();
+ connection->SetupTestDoubles(task_runner_, base::WrapUnique(test_timer_),
+ base::WrapUnique(generator_),
+ base::WrapUnique(receiver_));
return connection;
}
@@ -453,10 +407,6 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
.WillOnce(DoAll(SaveArg<0>(&create_gatt_connection_success_callback_),
SaveArg<1>(&create_gatt_connection_error_callback_)));
- // No throttling by default
- EXPECT_CALL(*bluetooth_throttler_, GetDelay())
- .WillOnce(Return(base::TimeDelta()));
-
connection->Connect();
// Handle setting the connection latency.
@@ -521,7 +471,6 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
std::unique_ptr<device::MockBluetoothGattNotifySession> notify_session(
new NiceMock<device::MockBluetoothGattNotifySession>(
tx_characteristic_->GetWeakPtr()));
- notify_session_alias_ = notify_session.get();
notify_session_success_callback_.Run(std::move(notify_session));
task_runner_->RunUntilIdle();
@@ -541,7 +490,7 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
EXPECT_EQ(last_value_written_on_tx_characteristic_, kConnectionRequest);
// OnDidSendMessage is not called.
- EXPECT_EQ(0, connection_observer_.GetNumSendCompleted());
+ EXPECT_EQ(0, connection_observer_->GetNumSendCompleted());
RunWriteCharacteristicSuccessCallback();
@@ -549,17 +498,13 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
if (selected_packet_size == kDefaultMaxPacketSize) {
connection->GattCharacteristicValueChanged(
adapter_.get(), rx_characteristic_.get(), kSmallConnectionResponse);
- EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetMaxPacketSize(),
- kDefaultMaxPacketSize);
- EXPECT_EQ(generator_factory_->GetMostRecentInstance()->GetMaxPacketSize(),
- kDefaultMaxPacketSize);
+ EXPECT_EQ(receiver_->GetMaxPacketSize(), kDefaultMaxPacketSize);
+ EXPECT_EQ(generator_->GetMaxPacketSize(), kDefaultMaxPacketSize);
} else if (selected_packet_size == kLargeMaxPacketSize) {
connection->GattCharacteristicValueChanged(
adapter_.get(), rx_characteristic_.get(), kLargeConnectionResponse);
- EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetMaxPacketSize(),
- kLargeMaxPacketSize);
- EXPECT_EQ(generator_factory_->GetMostRecentInstance()->GetMaxPacketSize(),
- kLargeMaxPacketSize);
+ EXPECT_EQ(receiver_->GetMaxPacketSize(), kLargeMaxPacketSize);
+ EXPECT_EQ(generator_->GetMaxPacketSize(), kLargeMaxPacketSize);
} else {
NOTREACHED();
}
@@ -571,10 +516,6 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
// Transitions |connection| to a DISCONNECTED state regardless of its initial
// state.
void Disconnect(TestBluetoothLowEnergyWeaveClientConnection* connection) {
- // A notify session was previously set.
- if (notify_session_alias_)
- EXPECT_CALL(*notify_session_alias_, Stop(_));
-
if (connection->sub_status() == SubStatus::CONNECTED) {
EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic(_, _, _))
.WillOnce(
@@ -609,29 +550,30 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
EXPECT_FALSE(write_remote_characteristic_error_callback_.is_null());
ASSERT_FALSE(write_remote_characteristic_success_callback_.is_null());
write_remote_characteristic_success_callback_.Run();
+ task_runner_->RunUntilIdle();
}
protected:
+ const RemoteDevice remote_device_;
+ const device::BluetoothUUID service_uuid_;
+ const device::BluetoothUUID tx_characteristic_uuid_;
+ const device::BluetoothUUID rx_characteristic_uuid_;
+ const proximity_auth::ScopedDisableLoggingForTesting disable_logging_;
+
scoped_refptr<device::MockBluetoothAdapter> adapter_;
- RemoteDevice remote_device_;
- device::BluetoothUUID service_uuid_;
- device::BluetoothUUID tx_characteristic_uuid_;
- device::BluetoothUUID rx_characteristic_uuid_;
+ base::MockTimer* test_timer_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+
std::unique_ptr<device::MockBluetoothDevice> mock_bluetooth_device_;
std::unique_ptr<device::MockBluetoothGattService> service_;
std::unique_ptr<device::MockBluetoothGattCharacteristic> tx_characteristic_;
std::unique_ptr<device::MockBluetoothGattCharacteristic> rx_characteristic_;
std::vector<uint8_t> last_value_written_on_tx_characteristic_;
- device::MockBluetoothGattNotifySession* notify_session_alias_;
- std::unique_ptr<MockBluetoothThrottler> bluetooth_throttler_;
- scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::MessageLoop message_loop_;
bool last_wire_message_success_;
- std::unique_ptr<MockBluetoothLowEnergyWeavePacketGeneratorFactory>
- generator_factory_;
- std::unique_ptr<MockBluetoothLowEnergyWeavePacketReceiverFactory>
- receiver_factory_;
- MockConnectionObserver connection_observer_;
+ NiceMock<MockBluetoothLowEnergyWeavePacketGenerator>* generator_;
+ NiceMock<MockBluetoothLowEnergyWeavePacketReceiver>* receiver_;
+ std::unique_ptr<MockConnectionObserver> connection_observer_;
// Callbacks
base::Closure connection_latency_callback_;
@@ -655,14 +597,16 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
device::BluetoothRemoteGattCharacteristic::ErrorCallback
write_remote_characteristic_error_callback_;
- proximity_auth::ScopedDisableLoggingForTesting disable_logging_;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(
+ CryptAuthBluetoothLowEnergyWeaveClientConnectionTest);
};
TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
CreateAndDestroyWithoutConnectCallDoesntCrash) {
BluetoothLowEnergyWeaveClientConnection connection(
remote_device_, kTestRemoteDeviceBluetoothAddress, adapter_,
- service_uuid_, bluetooth_throttler_.get());
+ service_uuid_);
}
TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
@@ -737,6 +681,28 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
}
TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectFailsCharacteristicsFoundThenUnavailable) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+
+ // Simulate the inability to fetch the characteristic after it was received.
+ // This would most likely be due to the Bluetooth device or service being
+ // removed during a connection attempt. See crbug.com/756174.
+ EXPECT_CALL(*service_, GetCharacteristic(_)).WillOnce(Return(nullptr));
+
+ EXPECT_FALSE(characteristics_finder_error_callback_.is_null());
+ ASSERT_FALSE(characteristics_finder_success_callback_.is_null());
+ characteristics_finder_success_callback_.Run(
+ {service_uuid_, kServiceID},
+ {tx_characteristic_uuid_, kTXCharacteristicID},
+ {rx_characteristic_uuid_, kRXCharacteristicID});
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
ConnectFailsNotifySessionError) {
std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
CreateConnection());
@@ -766,7 +732,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
// message |kMaxNumberOfTries| times. There is alredy one EXPECT_CALL for
// WriteRemoteCharacteristic(_,_,_) in NotifySessionStated, that's why we use
// |kMaxNumberOfTries-1| in the EXPECT_CALL statement.
- EXPECT_EQ(0, connection_observer_.GetNumSendCompleted());
+ EXPECT_EQ(0, connection_observer_->GetNumSendCompleted());
EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic(_, _, _))
.Times(kMaxNumberOfTries - 1)
.WillRepeatedly(
@@ -780,6 +746,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_FALSE(write_remote_characteristic_success_callback_.is_null());
write_remote_characteristic_error_callback_.Run(
device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
+ task_runner_->RunUntilIdle();
}
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
@@ -843,9 +810,9 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
RunWriteCharacteristicSuccessCallback();
- EXPECT_EQ(1, connection_observer_.GetNumSendCompleted());
- EXPECT_EQ(kSmallMessage, connection_observer_.GetLastDeserializedMessage());
- EXPECT_TRUE(connection_observer_.GetLastSendSuccess());
+ EXPECT_EQ(1, connection_observer_->GetNumSendCompleted());
+ EXPECT_EQ(kSmallMessage, connection_observer_->GetLastDeserializedMessage());
+ EXPECT_TRUE(connection_observer_->GetLastSendSuccess());
}
TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
@@ -887,9 +854,9 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
RunWriteCharacteristicSuccessCallback();
- EXPECT_EQ(1, connection_observer_.GetNumSendCompleted());
- EXPECT_EQ(kLargeMessage, connection_observer_.GetLastDeserializedMessage());
- EXPECT_TRUE(connection_observer_.GetLastSendSuccess());
+ EXPECT_EQ(1, connection_observer_->GetNumSendCompleted());
+ EXPECT_EQ(kLargeMessage, connection_observer_->GetLastDeserializedMessage());
+ EXPECT_TRUE(connection_observer_->GetLastSendSuccess());
}
TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
@@ -914,13 +881,14 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_FALSE(write_remote_characteristic_success_callback_.is_null());
write_remote_characteristic_error_callback_.Run(
device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
+ task_runner_->RunUntilIdle();
if (i == kMaxNumberOfTries - 1) {
- EXPECT_EQ(1, connection_observer_.GetNumSendCompleted());
+ EXPECT_EQ(1, connection_observer_->GetNumSendCompleted());
EXPECT_EQ(kSmallMessage,
- connection_observer_.GetLastDeserializedMessage());
- EXPECT_FALSE(connection_observer_.GetLastSendSuccess());
+ connection_observer_->GetLastDeserializedMessage());
+ EXPECT_FALSE(connection_observer_->GetLastSendSuccess());
} else {
- EXPECT_EQ(0, connection_observer_.GetNumSendCompleted());
+ EXPECT_EQ(0, connection_observer_->GetNumSendCompleted());
}
}
@@ -937,8 +905,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
connection->GattCharacteristicValueChanged(
adapter_.get(), rx_characteristic_.get(), kConnectionCloseUnknownError);
- EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetReasonForClose(),
- ReasonForClose::UNKNOWN_ERROR);
+ EXPECT_EQ(receiver_->GetReasonForClose(), ReasonForClose::UNKNOWN_ERROR);
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
}
@@ -961,8 +928,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_EQ(last_value_written_on_tx_characteristic_,
kConnectionCloseApplicationError);
- EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetReasonToClose(),
- ReasonForClose::APPLICATION_ERROR);
+ EXPECT_EQ(receiver_->GetReasonToClose(), ReasonForClose::APPLICATION_ERROR);
RunWriteCharacteristicSuccessCallback();
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
@@ -1000,8 +966,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_EQ(last_value_written_on_tx_characteristic_,
kConnectionCloseApplicationError);
- EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetReasonToClose(),
- ReasonForClose::APPLICATION_ERROR);
+ EXPECT_EQ(receiver_->GetReasonToClose(), ReasonForClose::APPLICATION_ERROR);
RunWriteCharacteristicSuccessCallback();
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
@@ -1010,11 +975,10 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
// Test for fix to crbug.com/708744. Without the fix, this test will crash.
TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
- ReceiverErrorAndConnectionDeletedTest) {
- connection_observer_.set_delete_on_disconnect(true);
-
+ ObserverDeletesConnectionOnDisconnect) {
TestBluetoothLowEnergyWeaveClientConnection* connection =
CreateConnection().release();
+ connection_observer_->set_delete_on_disconnect(true);
InitializeConnection(connection, kDefaultMaxPacketSize);
@@ -1029,10 +993,38 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_EQ(last_value_written_on_tx_characteristic_,
kConnectionCloseApplicationError);
- EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetReasonToClose(),
- ReasonForClose::APPLICATION_ERROR);
+ EXPECT_EQ(receiver_->GetReasonToClose(), ReasonForClose::APPLICATION_ERROR);
+
+ RunWriteCharacteristicSuccessCallback();
+
+ // We cannot check if connection's status and sub_status are DISCONNECTED
+ // because it has been deleted.
+}
+
+// Test for fix to crbug.com/ 751884. Without the fix, this test will crash.
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ObserverDeletesConnectionOnMessageSent) {
+ TestBluetoothLowEnergyWeaveClientConnection* connection =
+ CreateConnection().release();
+ connection_observer_->set_delete_on_message_sent(true);
+
+ InitializeConnection(connection, kDefaultMaxPacketSize);
+
+ EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic(_, _, _))
+ .WillOnce(
+ DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
+ SaveArg<1>(&write_remote_characteristic_success_callback_),
+ SaveArg<2>(&write_remote_characteristic_error_callback_)));
+
+ connection->SendMessage(
+ base::MakeUnique<FakeWireMessage>(kSmallMessage, kTestFeature));
+ EXPECT_EQ(last_value_written_on_tx_characteristic_, kSmallPackets0);
RunWriteCharacteristicSuccessCallback();
+ task_runner_->RunUntilIdle();
+ EXPECT_EQ(1, connection_observer_->GetNumSendCompleted());
+ EXPECT_EQ(kSmallMessage, connection_observer_->GetLastDeserializedMessage());
+ EXPECT_TRUE(connection_observer_->GetLastSendSuccess());
// We cannot check if connection's status and sub_status are DISCONNECTED
// because it has been deleted.
@@ -1070,6 +1062,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
write_remote_characteristic_error_callback_.Run(
device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
+ task_runner_->RunUntilIdle();
}
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
@@ -1081,8 +1074,6 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
CreateConnection());
- EXPECT_CALL(*bluetooth_throttler_, GetDelay())
- .WillOnce(Return(base::TimeDelta(base::TimeDelta::FromSeconds(1))));
EXPECT_CALL(*mock_bluetooth_device_,
SetConnectionLatency(
device::BluetoothDevice::CONNECTION_LATENCY_LOW, _, _))
@@ -1162,6 +1153,112 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
ConnectionResponseReceived(connection.get(), kDefaultMaxPacketSize);
}
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ Timeout_ConnectionLatency) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ EXPECT_CALL(*mock_bluetooth_device_,
+ SetConnectionLatency(
+ device::BluetoothDevice::CONNECTION_LATENCY_LOW, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&connection_latency_callback_),
+ SaveArg<2>(&connection_latency_error_callback_)));
+
+ // Call Connect(), which should set the connection latency.
+ connection->Connect();
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_CONNECTION_LATENCY);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+ ASSERT_FALSE(connection_latency_callback_.is_null());
+ ASSERT_FALSE(connection_latency_error_callback_.is_null());
+
+ // Simulate a timeout.
+ test_timer_->Fire();
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ Timeout_GattConnection) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ EXPECT_CALL(*mock_bluetooth_device_,
+ SetConnectionLatency(
+ device::BluetoothDevice::CONNECTION_LATENCY_LOW, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&connection_latency_callback_),
+ SaveArg<2>(&connection_latency_error_callback_)));
+
+ // Preparing |connection| for a CreateGattConnection call.
+ EXPECT_CALL(*mock_bluetooth_device_, CreateGattConnection(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&create_gatt_connection_success_callback_),
+ SaveArg<1>(&create_gatt_connection_error_callback_)));
+
+ connection->Connect();
+
+ // Handle setting the connection latency.
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_CONNECTION_LATENCY);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+ ASSERT_FALSE(connection_latency_callback_.is_null());
+ ASSERT_FALSE(connection_latency_error_callback_.is_null());
+ connection_latency_callback_.Run();
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_GATT_CONNECTION);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+
+ // Simulate a timeout.
+ test_timer_->Fire();
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ Timeout_GattCharacteristics) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_CHARACTERISTICS);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+
+ // Simulate a timeout.
+ test_timer_->Fire();
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ Timeout_NotifySession) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ CharacteristicsFound(connection.get());
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_NOTIFY_SESSION);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+
+ // Simulate a timeout.
+ test_timer_->Fire();
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ Timeout_ConnectionResponse) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ CharacteristicsFound(connection.get());
+ NotifySessionStarted(connection.get());
+
+ // Simulate a timeout.
+ test_timer_->Fire();
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
} // namespace weave
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.cc
index cba14631aaf..f4b0205f4b9 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.cc
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.cc
@@ -4,50 +4,25 @@
#include "components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h"
+#include <string.h>
+#include <algorithm>
#ifdef OS_WIN
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
-#include <string.h>
-
-#include <algorithm>
-
#include "base/logging.h"
namespace cryptauth {
namespace weave {
-// static.
-BluetoothLowEnergyWeavePacketGenerator::Factory*
- BluetoothLowEnergyWeavePacketGenerator::Factory::factory_instance_ =
- nullptr;
-
-// static.
-std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>
-BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance() {
- if (!factory_instance_) {
- factory_instance_ = new Factory();
- }
- return factory_instance_->BuildInstance();
-}
-
-// static.
-void BluetoothLowEnergyWeavePacketGenerator::Factory::SetInstanceForTesting(
- Factory* factory) {
- factory_instance_ = factory;
-}
-
-std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>
-BluetoothLowEnergyWeavePacketGenerator::Factory::BuildInstance() {
- return std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>(
- new BluetoothLowEnergyWeavePacketGenerator());
-}
-
BluetoothLowEnergyWeavePacketGenerator::BluetoothLowEnergyWeavePacketGenerator()
: max_packet_size_(kDefaultMaxPacketSize), next_packet_counter_(0) {}
+BluetoothLowEnergyWeavePacketGenerator::
+ ~BluetoothLowEnergyWeavePacketGenerator() {}
+
Packet BluetoothLowEnergyWeavePacketGenerator::CreateConnectionRequest() {
Packet packet(kMinConnectionRequestSize, 0);
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h
index 2cee2b8f164..e6aea4387f0 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h
@@ -21,22 +21,8 @@ namespace weave {
// Generates the messages sent using the uWeave protocol.
class BluetoothLowEnergyWeavePacketGenerator {
public:
- class Factory {
- public:
- static std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>
- NewInstance();
-
- // Exposed for testing.
- static void SetInstanceForTesting(Factory* factory);
-
- protected:
- // Exposed for testing.
- virtual std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>
- BuildInstance();
-
- private:
- static Factory* factory_instance_;
- };
+ BluetoothLowEnergyWeavePacketGenerator();
+ virtual ~BluetoothLowEnergyWeavePacketGenerator();
virtual Packet CreateConnectionRequest();
virtual Packet CreateConnectionResponse();
@@ -48,9 +34,6 @@ class BluetoothLowEnergyWeavePacketGenerator {
// Will crash if message is empty.
virtual std::vector<Packet> EncodeDataMessage(std::string message);
- protected:
- BluetoothLowEnergyWeavePacketGenerator();
-
private:
void SetShortField(uint32_t byte_offset, uint16_t val, Packet* packet);
void SetPacketTypeBit(PacketType val, Packet* packet);
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator_unittest.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator_unittest.cc
index 7e1b4ed56ae..f9bf22575f4 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator_unittest.cc
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator_unittest.cc
@@ -9,6 +9,7 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -23,7 +24,7 @@ class CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest
void TestConnectionCloseWithReason(ReasonForClose reason_for_close,
uint8_t expected_reason_for_close) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
Packet packet = generator->CreateConnectionClose(reason_for_close);
@@ -51,7 +52,7 @@ class CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
CreateConnectionRequestTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
Packet packet = generator->CreateConnectionRequest();
@@ -76,7 +77,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
CreateConnectionResponseWithDefaultPacketSizeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
Packet packet = generator->CreateConnectionResponse();
@@ -96,7 +97,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
CreateConnectionResponseWithSelectedPacketSizeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
const uint8_t kSelectedPacketSize = 30;
const uint16_t kResponseSize = 5;
@@ -137,7 +138,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
EncodeDataMessageWithDefaultPacketSizeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
std::string data = "abcdefghijklmnopqrstuvwxyz";
@@ -170,7 +171,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
EncodeDataMessageWithSelectedPacketSizeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
const uint32_t packet_size = 30;
const uint32_t residual_packet_size = 2;
@@ -222,7 +223,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
PacketCounterForMixedPacketTypesTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
Packet packet = generator->CreateConnectionRequest();
@@ -241,7 +242,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
PacketCounterWrappedAroundTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
- BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+ base::MakeUnique<BluetoothLowEnergyWeavePacketGenerator>();
const uint8_t kNumPackets = 100;
std::string data(kNumPackets * kByteDefaultMaxPacketSize, 'a');
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.cc
index b1055e86ba3..b90cdad4c40 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.cc
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.cc
@@ -21,32 +21,6 @@ const uint16_t kMaxPacketSizeLowerBound = 20;
} // namespace
-BluetoothLowEnergyWeavePacketReceiver::Factory*
- BluetoothLowEnergyWeavePacketReceiver::Factory::factory_instance_ = nullptr;
-
-// static
-std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver>
-BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
- ReceiverType receiver_type) {
- if (!factory_instance_) {
- factory_instance_ = new Factory();
- }
- return factory_instance_->BuildInstance(receiver_type);
-}
-
-// static
-void BluetoothLowEnergyWeavePacketReceiver::Factory::SetInstanceForTesting(
- Factory* factory) {
- factory_instance_ = factory;
-}
-
-std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver>
-BluetoothLowEnergyWeavePacketReceiver::Factory::BuildInstance(
- ReceiverType receiver_type) {
- return std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver>(
- new BluetoothLowEnergyWeavePacketReceiver(receiver_type));
-}
-
BluetoothLowEnergyWeavePacketReceiver::BluetoothLowEnergyWeavePacketReceiver(
ReceiverType receiver_type)
: receiver_type_(receiver_type),
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h
index e369f540366..c3f68e6ae08 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h
@@ -108,23 +108,7 @@ class BluetoothLowEnergyWeavePacketReceiver {
PACKET_OUT_OF_SEQUENCE
};
- class Factory {
- public:
- static std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> NewInstance(
- ReceiverType receiver_type);
-
- // Exposed for testing.
- static void SetInstanceForTesting(Factory* factory);
-
- protected:
- // Exposed for testing.
- virtual std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver>
- BuildInstance(ReceiverType receiver_type);
-
- private:
- static Factory* factory_instance_;
- };
-
+ explicit BluetoothLowEnergyWeavePacketReceiver(ReceiverType receiver_type);
~BluetoothLowEnergyWeavePacketReceiver();
typedef std::vector<uint8_t> Packet;
@@ -159,9 +143,6 @@ class BluetoothLowEnergyWeavePacketReceiver {
// Add a packet that's just been received over Connection to the receiver.
virtual State ReceivePacket(const Packet& packet);
- protected:
- explicit BluetoothLowEnergyWeavePacketReceiver(ReceiverType receiver_type);
-
private:
void ReceiveFirstPacket(const Packet& packet);
void ReceiveNonFirstPacket(const Packet& packet);
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver_unittest.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver_unittest.cc
index 827e88b5916..a76d19bd2fb 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver_unittest.cc
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver_unittest.cc
@@ -8,6 +8,7 @@
#include <string>
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -49,7 +50,7 @@ class CryptAuthBluetoothLowEnergyWeavePacketReceiverTest
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
WellBehavingServerPacketsNoControlDataTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -110,7 +111,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
WellBehavingServerPacketsWithFullControlDataTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader,
@@ -176,7 +177,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
WellBehavingServerPacketsWithSomeControlDataTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -226,7 +227,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
WellBehavingClientPacketsNoControlDataTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
const uint8_t kSelectedPacketSize = 30;
@@ -265,7 +266,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
WellBehavingClientPacketsWithFullControlDataTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader,
@@ -318,7 +319,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
WellBehavingClientPacketsWithSomeControlDataTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader,
@@ -359,7 +360,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
LegacyCloseWithoutReasonTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -383,7 +384,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
OneBytePacketTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
@@ -408,7 +409,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
EmptyPacketTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0;
@@ -421,7 +422,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ServerReceivingConnectionResponseTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
kByteWeaveVersion, kEmptyUpperByte,
@@ -436,7 +437,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ClientReceivingConnectionRequestTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
kByteWeaveVersion, kEmptyUpperByte,
@@ -452,7 +453,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ReceiveConnectionCloseInConnecting) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
// uWeave Header:
@@ -471,7 +472,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ReceiveDataInConnecting) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
// uWeave Header:
@@ -492,7 +493,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ConnectionRequestTooSmallTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
kByteWeaveVersion, kEmptyUpperByte,
@@ -507,7 +508,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ConnectionRequestTooLargeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0(kByteDefaultMaxPacketSize + 1, 0);
@@ -524,7 +525,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ConnectionResponseTooSmallTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
@@ -539,7 +540,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ConnectionResponseTooLargeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0(kByteDefaultMaxPacketSize + 1, 0);
@@ -556,7 +557,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ConnectionCloseTooLargeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -581,7 +582,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
DataPacketTooLargeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -610,7 +611,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
FirstPacketNoFirstNorLastBitTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -639,7 +640,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
FirstPacketNoFirstYesLastBitTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -668,7 +669,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
NonFirstPacketYesFirstBitTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -708,7 +709,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
OutOfOrderPacketTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -737,7 +738,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
InvalidVersionInConnectionRequestTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
const uint8_t kWrongVersion = 2;
@@ -758,7 +759,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
const uint8_t kSmallMaxPacketSize = 19;
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -775,7 +776,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
InvalidSelectedVersionInConnectionResponseTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader, kByteWeaveVersion,
@@ -792,7 +793,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
InvalidSelectedMaxPacketSizeInConnectionResponseTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
const uint8_t kSmallMaxPacketSize = 19;
@@ -809,7 +810,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
UnrecognizedReasonForCloseInConnectionCloseTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
@@ -835,7 +836,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
UnrecognizedControlCommandBitTwoTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
// uWeave Header:
@@ -860,7 +861,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
InvalidControlCommandBitThreeTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
// uWeave Header:
@@ -880,7 +881,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
InvalidBitOneInDataPacketHeaderTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
@@ -907,7 +908,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
InvalidBitZeroInDataPacketHeaderTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
@@ -934,7 +935,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ReceivedPacketInErrorState) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::CLIENT);
std::vector<uint8_t> p0;
@@ -954,7 +955,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
ReceivedPacketInConnectionClosedStateTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
@@ -992,7 +993,7 @@ TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
MultipleControlPacketTest) {
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
- BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ base::MakeUnique<BluetoothLowEnergyWeavePacketReceiver>(
ReceiverType::SERVER);
std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
diff --git a/chromium/components/cryptauth/bluetooth_throttler.h b/chromium/components/cryptauth/bluetooth_throttler.h
deleted file mode 100644
index 90eed117951..00000000000
--- a/chromium/components/cryptauth/bluetooth_throttler.h
+++ /dev/null
@@ -1,38 +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_CRYPTAUTH_BLUETOOTH_THROTTLER_H_
-#define COMPONENTS_CRYPTAUTH_BLUETOOTH_THROTTLER_H_
-
-namespace base {
-class TimeDelta;
-}
-
-namespace cryptauth {
-
-class Connection;
-
-// An interface for throttling repeated connection attempts to the same device.
-// This throttling is necessary to prevent a kernel race condition when
-// connecting before the previous connection fully closes, putting the
-// connection in a corrupted, and unrecoverable state. http://crbug.com/345232
-class BluetoothThrottler {
- public:
- virtual ~BluetoothThrottler() {}
-
- // Returns the current delay that must be respected prior to reattempting to
- // establish a connection with the remote device. The returned value is 0 if
- // no delay is needed.
- virtual base::TimeDelta GetDelay() const = 0;
-
- // Should be called when a connection to the remote device is established.
- // Note that the |connection| is passed as a weak reference. The throttler
- // will ensure, by registering as an observer, that it never attempts to use
- // the connection after it has been destroyed.
- virtual void OnConnection(Connection* connection) = 0;
-};
-
-} // namespace cryptauth
-
-#endif // COMPONENTS_CRYPTAUTH_BLUETOOTH_THROTTLER_H_
diff --git a/chromium/components/cryptauth/bluetooth_throttler_impl.cc b/chromium/components/cryptauth/bluetooth_throttler_impl.cc
deleted file mode 100644
index 74e09192655..00000000000
--- a/chromium/components/cryptauth/bluetooth_throttler_impl.cc
+++ /dev/null
@@ -1,76 +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/bluetooth_throttler_impl.h"
-
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
-#include "base/time/default_tick_clock.h"
-#include "base/time/tick_clock.h"
-#include "components/cryptauth/connection.h"
-
-namespace cryptauth {
-namespace {
-
-// Time to wait after disconnect before reconnecting.
-const int kCooldownTimeSecs = 1;
-
-} // namespace
-
-// static
-BluetoothThrottlerImpl* BluetoothThrottlerImpl::GetInstance() {
- return base::Singleton<BluetoothThrottlerImpl>::get();
-}
-
-BluetoothThrottlerImpl::BluetoothThrottlerImpl()
- : BluetoothThrottlerImpl(base::MakeUnique<base::DefaultTickClock>()) {}
-
-BluetoothThrottlerImpl::BluetoothThrottlerImpl(
- std::unique_ptr<base::TickClock> clock)
- : clock_(std::move(clock)) {}
-
-BluetoothThrottlerImpl::~BluetoothThrottlerImpl() {
- for (Connection* connection : connections_) {
- connection->RemoveObserver(this);
- }
-}
-
-base::TimeDelta BluetoothThrottlerImpl::GetDelay() const {
- if (last_disconnect_time_.is_null())
- return base::TimeDelta();
-
- base::TimeTicks now = clock_->NowTicks();
- base::TimeTicks throttled_start_time =
- last_disconnect_time_ + GetCooldownTimeDelta();
- if (now >= throttled_start_time)
- return base::TimeDelta();
-
- return throttled_start_time - now;
-}
-
-void BluetoothThrottlerImpl::OnConnection(Connection* connection) {
- DCHECK(!base::ContainsKey(connections_, connection));
- connections_.insert(connection);
- connection->AddObserver(this);
-}
-
-base::TimeDelta BluetoothThrottlerImpl::GetCooldownTimeDelta() const {
- return base::TimeDelta::FromSeconds(kCooldownTimeSecs);
-}
-
-void BluetoothThrottlerImpl::OnConnectionStatusChanged(
- Connection* connection,
- Connection::Status old_status,
- Connection::Status new_status) {
- DCHECK(base::ContainsKey(connections_, connection));
- if (new_status == Connection::DISCONNECTED) {
- last_disconnect_time_ = clock_->NowTicks();
- connection->RemoveObserver(this);
- connections_.erase(connection);
- }
-}
-
-} // namespace cryptauth
diff --git a/chromium/components/cryptauth/bluetooth_throttler_impl.h b/chromium/components/cryptauth/bluetooth_throttler_impl.h
deleted file mode 100644
index 7a9fef028f0..00000000000
--- a/chromium/components/cryptauth/bluetooth_throttler_impl.h
+++ /dev/null
@@ -1,76 +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_CRYPTAUTH_BLUETOOTH_THROTTLER_IMPL_H_
-#define COMPONENTS_CRYPTAUTH_BLUETOOTH_THROTTLER_IMPL_H_
-
-#include <memory>
-#include <set>
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "base/time/time.h"
-#include "components/cryptauth/bluetooth_throttler.h"
-#include "components/cryptauth/connection_observer.h"
-
-namespace base {
-class TickClock;
-}
-
-namespace cryptauth {
-
-class Connection;
-
-// This class throttles repeated connection attempts to the same device. This
-// throttling is necessary to prevent a kernel race condition when connecting
-// before the previous connection fully closes, putting the connection in a
-// corrupted, and unrecoverable state. http://crbug.com/345232
-class BluetoothThrottlerImpl : public BluetoothThrottler,
- public ConnectionObserver {
- public:
- static BluetoothThrottlerImpl* GetInstance();
- ~BluetoothThrottlerImpl() override;
-
- // BluetoothThrottler:
- base::TimeDelta GetDelay() const override;
- void OnConnection(Connection* connection) override;
-
- protected:
- // Creates a throttler for connections to a remote device, using the |clock|
- // as a time source.
- explicit BluetoothThrottlerImpl(std::unique_ptr<base::TickClock> clock);
-
- // Returns the duration to wait, after disconnecting, before reattempting a
- // connection to the remote device. Exposed for testing.
- base::TimeDelta GetCooldownTimeDelta() const;
-
- private:
- friend struct base::DefaultSingletonTraits<BluetoothThrottlerImpl>;
-
- BluetoothThrottlerImpl();
-
- // ConnectionObserver:
- void OnConnectionStatusChanged(Connection* connection,
- Connection::Status old_status,
- Connection::Status new_status) override;
-
- // Tracks the last seen disconnect time for the |remote_device_|.
- base::TimeTicks last_disconnect_time_;
-
- // The time source.
- std::unique_ptr<base::TickClock> clock_;
-
- // The currently connected connections.
- // Each connection is stored as a weak reference, which is safe because |this|
- // instance is registered as an observer, and will unregister when the
- // connection disconnects, which is guaranteed to occur before the connection
- // is destroyed.
- std::set<Connection*> connections_;
-
- DISALLOW_COPY_AND_ASSIGN(BluetoothThrottlerImpl);
-};
-
-} // namespace cryptauth
-
-#endif // COMPONENTS_CRYPTAUTH_BLUETOOTH_THROTTLER_IMPL_H_
diff --git a/chromium/components/cryptauth/bluetooth_throttler_impl_unittest.cc b/chromium/components/cryptauth/bluetooth_throttler_impl_unittest.cc
deleted file mode 100644
index 7a3a028f738..00000000000
--- a/chromium/components/cryptauth/bluetooth_throttler_impl_unittest.cc
+++ /dev/null
@@ -1,101 +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/bluetooth_throttler_impl.h"
-
-#include <utility>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/time/time.h"
-#include "components/cryptauth/fake_connection.h"
-#include "components/cryptauth/wire_message.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cryptauth {
-namespace {
-
-class TestBluetoothThrottler : public BluetoothThrottlerImpl {
- public:
- explicit TestBluetoothThrottler(std::unique_ptr<base::TickClock> clock)
- : BluetoothThrottlerImpl(std::move(clock)) {}
- ~TestBluetoothThrottler() override {}
-
- // Increase visibility for testing.
- using BluetoothThrottlerImpl::GetCooldownTimeDelta;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestBluetoothThrottler);
-};
-
-} // namespace
-
-class CryptAuthBluetoothThrottlerImplTest : public testing::Test {
- public:
- CryptAuthBluetoothThrottlerImplTest()
- : clock_(new base::SimpleTestTickClock),
- throttler_(base::WrapUnique(clock_)) {
- // The throttler treats null times as special, so start with a non-null
- // time.
- clock_->Advance(base::TimeDelta::FromSeconds(1));
- }
-
- void PerformConnectionStateTransition(Connection::Status old_status,
- Connection::Status new_status) {
- FakeConnection connection((RemoteDevice()));
- throttler_.OnConnection(&connection);
- static_cast<ConnectionObserver*>(&throttler_)
- ->OnConnectionStatusChanged(&connection, old_status, new_status);
- }
-
- protected:
- // The clock is owned by the |throttler_|.
- base::SimpleTestTickClock* clock_;
- TestBluetoothThrottler throttler_;
-};
-
-TEST_F(CryptAuthBluetoothThrottlerImplTest,
- GetDelay_FirstConnectionIsNotThrottled) {
- EXPECT_EQ(base::TimeDelta(), throttler_.GetDelay());
-}
-
-TEST_F(CryptAuthBluetoothThrottlerImplTest,
- GetDelay_ConnectionAfterDisconnectIsThrottled) {
- // Simulate a connection followed by a disconnection.
- PerformConnectionStateTransition(Connection::CONNECTED,
- Connection::DISCONNECTED);
- EXPECT_GT(throttler_.GetDelay(), base::TimeDelta());
-}
-
-TEST_F(CryptAuthBluetoothThrottlerImplTest,
- GetDelay_ConnectionAfterIsProgressDisconnectIsThrottled) {
- // Simulate an attempt to connect (in progress connection) followed by a
- // disconnection.
- PerformConnectionStateTransition(Connection::IN_PROGRESS,
- Connection::DISCONNECTED);
- EXPECT_GT(throttler_.GetDelay(), base::TimeDelta());
-}
-
-TEST_F(CryptAuthBluetoothThrottlerImplTest,
- GetDelay_DelayedConnectionAfterDisconnectIsNotThrottled) {
- // Simulate a connection followed by a disconnection, then allow the cooldown
- // period to elapse.
- PerformConnectionStateTransition(Connection::CONNECTED,
- Connection::DISCONNECTED);
- clock_->Advance(throttler_.GetCooldownTimeDelta());
- EXPECT_EQ(base::TimeDelta(), throttler_.GetDelay());
-}
-
-TEST_F(CryptAuthBluetoothThrottlerImplTest,
- GetDelay_DelayedConnectionAfterInProgressDisconnectIsNotThrottled) {
- // Simulate an attempt to connect (in progress connection) followed by a
- // disconnection, then allow the cooldown period to elapse.
- PerformConnectionStateTransition(Connection::IN_PROGRESS,
- Connection::DISCONNECTED);
- clock_->Advance(throttler_.GetCooldownTimeDelta());
- EXPECT_EQ(base::TimeDelta(), throttler_.GetDelay());
-}
-
-} // namespace cryptauth
diff --git a/chromium/components/cryptauth/connection.cc b/chromium/components/cryptauth/connection.cc
index ac0b2c318b2..a97fd688891 100644
--- a/chromium/components/cryptauth/connection.cc
+++ b/chromium/components/cryptauth/connection.cc
@@ -4,11 +4,13 @@
#include "components/cryptauth/connection.h"
+#include <sstream>
#include <utility>
#include "base/logging.h"
#include "components/cryptauth/connection_observer.h"
#include "components/cryptauth/wire_message.h"
+#include "components/proximity_auth/logging/logging.h"
namespace cryptauth {
@@ -25,12 +27,14 @@ bool Connection::IsConnected() const {
void Connection::SendMessage(std::unique_ptr<WireMessage> message) {
if (!IsConnected()) {
- VLOG(1) << "Cannot send message when disconnected.";
+ PA_LOG(ERROR) << "Not yet connected; cannot send message to "
+ << GetDeviceInfoLogString();
return;
}
if (is_sending_message_) {
- VLOG(1) << "Another message is currently in progress.";
+ PA_LOG(ERROR) << "Cannot send message because another message is currently "
+ << "being sent to " << GetDeviceInfoLogString();
return;
}
@@ -64,7 +68,8 @@ void Connection::SetStatus(Status status) {
void Connection::OnDidSendMessage(const WireMessage& message, bool success) {
if (!is_sending_message_) {
- VLOG(1) << "Send completed, but no message in progress.";
+ PA_LOG(ERROR) << "OnDidSendMessage(), but a send was not expected to be in "
+ << "progress to " << GetDeviceInfoLogString();
return;
}
@@ -75,7 +80,8 @@ void Connection::OnDidSendMessage(const WireMessage& message, bool success) {
void Connection::OnBytesReceived(const std::string& bytes) {
if (!IsConnected()) {
- VLOG(1) << "Received bytes, but not connected.";
+ PA_LOG(ERROR) << "OnBytesReceived(), but not connected to "
+ << GetDeviceInfoLogString();
return;
}
@@ -102,4 +108,11 @@ std::unique_ptr<WireMessage> Connection::DeserializeWireMessage(
return WireMessage::Deserialize(received_bytes_, is_incomplete_message);
}
+std::string Connection::GetDeviceInfoLogString() {
+ std::stringstream ss;
+ ss << "{id: \"" << remote_device().GetTruncatedDeviceIdForLogs()
+ << "\", addr: \"" << GetDeviceAddress() << "\"}";
+ return ss.str();
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/connection.h b/chromium/components/cryptauth/connection.h
index dbc731d728a..16077c5ef46 100644
--- a/chromium/components/cryptauth/connection.h
+++ b/chromium/components/cryptauth/connection.h
@@ -92,6 +92,9 @@ class Connection {
virtual std::unique_ptr<WireMessage> DeserializeWireMessage(
bool* is_incomplete_message);
+ // Returns a string describing the associated device for logging purposes.
+ std::string GetDeviceInfoLogString();
+
private:
// The remote device corresponding to this connection.
const RemoteDevice remote_device_;
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow.cc b/chromium/components/cryptauth/cryptauth_api_call_flow.cc
index cfacd8992bb..c0de9b4e446 100644
--- a/chromium/components/cryptauth/cryptauth_api_call_flow.cc
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow.cc
@@ -6,6 +6,7 @@
#include "base/strings/string_number_conversions.h"
#include "components/proximity_auth/logging/logging.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_fetcher.h"
namespace cryptauth {
@@ -80,4 +81,10 @@ void CryptAuthApiCallFlow::ProcessApiCallFailure(
error_callback_.Run(error_message);
}
+net::PartialNetworkTrafficAnnotationTag
+CryptAuthApiCallFlow::GetNetworkTrafficAnnotationTag() {
+ DCHECK(partial_network_annotation_ != nullptr);
+ return *partial_network_annotation_.get();
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow.h b/chromium/components/cryptauth/cryptauth_api_call_flow.h
index 07a487edca6..ce5e34d294e 100644
--- a/chromium/components/cryptauth/cryptauth_api_call_flow.h
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow.h
@@ -40,6 +40,14 @@ class CryptAuthApiCallFlow : public OAuth2ApiCallFlow {
const ResultCallback& result_callback,
const ErrorCallback& error_callback);
+ void SetPartialNetworkTrafficAnnotation(
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation) {
+ partial_network_annotation_.reset(
+ new net::PartialNetworkTrafficAnnotationTag(
+ partial_traffic_annotation));
+ }
+
protected:
// Reduce the visibility of OAuth2ApiCallFlow::Start() to avoid exposing
// overloaded methods.
@@ -53,6 +61,8 @@ class CryptAuthApiCallFlow : public OAuth2ApiCallFlow {
const std::string& body) override;
void ProcessApiCallSuccess(const net::URLFetcher* source) override;
void ProcessApiCallFailure(const net::URLFetcher* source) override;
+ net::PartialNetworkTrafficAnnotationTag GetNetworkTrafficAnnotationTag()
+ override;
private:
// The URL of the CryptAuth endpoint serving the request.
@@ -68,6 +78,9 @@ class CryptAuthApiCallFlow : public OAuth2ApiCallFlow {
// Callback invoked with an error message when the flow fails.
ErrorCallback error_callback_;
+ std::unique_ptr<net::PartialNetworkTrafficAnnotationTag>
+ partial_network_annotation_;
+
DISALLOW_COPY_AND_ASSIGN(CryptAuthApiCallFlow);
};
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc b/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
index cbc6701e4f8..2c97320736a 100644
--- a/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
@@ -30,7 +30,10 @@ class CryptAuthApiCallFlowTest
protected:
CryptAuthApiCallFlowTest()
: url_request_context_getter_(new net::TestURLRequestContextGetter(
- new base::TestSimpleTaskRunner())) {}
+ new base::TestSimpleTaskRunner())) {
+ flow_.SetPartialNetworkTrafficAnnotation(
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ }
void SetUp() override {
// The TestURLFetcherFactory will override the global URLFetcherFactory for
diff --git a/chromium/components/cryptauth/cryptauth_client.h b/chromium/components/cryptauth/cryptauth_client.h
index 98c83d417ba..1fe98a91bb6 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 "net/traffic_annotation/network_traffic_annotation.h"
namespace cryptauth {
class GetMyDevicesRequest;
@@ -24,6 +25,8 @@ class SetupEnrollmentRequest;
class SetupEnrollmentResponse;
class FinishEnrollmentRequest;
class FinishEnrollmentResponse;
+class FindEligibleForPromotionRequest;
+class FindEligibleForPromotionResponse;
}
namespace cryptauth {
@@ -45,7 +48,9 @@ class CryptAuthClient {
GetMyDevicesCallback;
virtual void GetMyDevices(const GetMyDevicesRequest& request,
const GetMyDevicesCallback& callback,
- const ErrorCallback& error_callback) = 0;
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation) = 0;
// FindEligibleUnlockDevices
typedef base::Callback<void(
@@ -56,13 +61,23 @@ class CryptAuthClient {
const FindEligibleUnlockDevicesCallback& callback,
const ErrorCallback& error_callback) = 0;
+ // FindEligibleForPromotion
+ typedef base::Callback<void(const FindEligibleForPromotionResponse&)>
+ FindEligibleForPromotionCallback;
+ virtual void FindEligibleForPromotion(
+ const FindEligibleForPromotionRequest& request,
+ const FindEligibleForPromotionCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
// SendDeviceSyncTickle
typedef base::Callback<void(const SendDeviceSyncTickleResponse&)>
SendDeviceSyncTickleCallback;
virtual void SendDeviceSyncTickle(
const SendDeviceSyncTickleRequest& request,
const SendDeviceSyncTickleCallback& callback,
- const ErrorCallback& error_callback) = 0;
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation) = 0;
// ToggleEasyUnlock
typedef base::Callback<void(const ToggleEasyUnlockResponse&)>
diff --git a/chromium/components/cryptauth/cryptauth_client_impl.cc b/chromium/components/cryptauth/cryptauth_client_impl.cc
index 259838598ef..8fbe82e07a1 100644
--- a/chromium/components/cryptauth/cryptauth_client_impl.cc
+++ b/chromium/components/cryptauth/cryptauth_client_impl.cc
@@ -26,6 +26,8 @@ const char kCryptAuthPath[] = "cryptauth/v1/";
const char kGetMyDevicesPath[] = "deviceSync/getmydevices";
const char kFindEligibleUnlockDevicesPath[] =
"deviceSync/findeligibleunlockdevices";
+const char kFindEligibleForPromotionPath[] =
+ "deviceSync/findeligibleforpromotion";
const char kSendDeviceSyncTicklePath[] = "deviceSync/senddevicesynctickle";
const char kToggleEasyUnlockPath[] = "deviceSync/toggleeasyunlock";
const char kSetupEnrollmentPath[] = "enrollment/setup";
@@ -66,44 +68,178 @@ CryptAuthClientImpl::~CryptAuthClientImpl() {
void CryptAuthClientImpl::GetMyDevices(
const GetMyDevicesRequest& request,
const GetMyDevicesCallback& callback,
- const ErrorCallback& error_callback) {
- MakeApiCall(kGetMyDevicesPath, request, callback, error_callback);
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
+ MakeApiCall(kGetMyDevicesPath, request, callback, error_callback,
+ partial_traffic_annotation);
}
void CryptAuthClientImpl::FindEligibleUnlockDevices(
const FindEligibleUnlockDevicesRequest& request,
const FindEligibleUnlockDevicesCallback& callback,
const ErrorCallback& error_callback) {
- MakeApiCall(kFindEligibleUnlockDevicesPath, request, callback,
- error_callback);
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation(
+ "cryptauth_find_eligible_unlock_devices", "oauth2_api_call_flow",
+ R"(
+ semantics {
+ sender: "CryptAuth Device Manager"
+ description:
+ "Gets the list of mobile devices that can be used by Smart Lock to "
+ "unlock the current device."
+ trigger:
+ "This request is sent when the user starts the Smart Lock setup flow."
+ data: "OAuth 2.0 token and the device's public key."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ setting:
+ "This feature cannot be disabled in settings, but the request will "
+ "only be sent if the user explicitly tries to enable Smart Lock "
+ "(EasyUnlock), i.e. starts the setup flow."
+ chrome_policy {
+ EasyUnlockAllowed {
+ EasyUnlockAllowed: false
+ }
+ }
+ })");
+ MakeApiCall(kFindEligibleUnlockDevicesPath, request, callback, error_callback,
+ partial_traffic_annotation);
+}
+
+void CryptAuthClientImpl::FindEligibleForPromotion(
+ const FindEligibleForPromotionRequest& request,
+ const FindEligibleForPromotionCallback& callback,
+ const ErrorCallback& error_callback) {
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation(
+ "cryptauth_find_eligible_for_promotion", "oauth2_api_call_flow",
+ R"(
+ semantics {
+ sender: "Promotion Manager"
+ description:
+ "Return whether the current device is eligible for a Smart Lock promotion."
+ trigger:
+ "This request is sent when the user starts the Smart Lock setup flow."
+ data: "OAuth 2.0 token and the device's public key."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ setting:
+ "This feature cannot be disabled in settings"
+ chrome_policy {
+ EasyUnlockAllowed {
+ EasyUnlockAllowed: false
+ }
+ }
+ })");
+ MakeApiCall(kFindEligibleForPromotionPath, request, callback, error_callback,
+ partial_traffic_annotation);
}
void CryptAuthClientImpl::SendDeviceSyncTickle(
const SendDeviceSyncTickleRequest& request,
const SendDeviceSyncTickleCallback& callback,
- const ErrorCallback& error_callback) {
- MakeApiCall(kSendDeviceSyncTicklePath, request, callback, error_callback);
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
+ MakeApiCall(kSendDeviceSyncTicklePath, request, callback, error_callback,
+ partial_traffic_annotation);
}
void CryptAuthClientImpl::ToggleEasyUnlock(
const ToggleEasyUnlockRequest& request,
const ToggleEasyUnlockCallback& callback,
const ErrorCallback& error_callback) {
- MakeApiCall(kToggleEasyUnlockPath, request, callback, error_callback);
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation("cryptauth_toggle_easyunlock",
+ "oauth2_api_call_flow", R"(
+ semantics {
+ sender: "CryptAuth Device Manager"
+ description: "Enables Smart Lock (EasyUnlock) for the current device."
+ trigger:
+ "This request is send after the user goes through the EasyUnlock "
+ "setup flow."
+ data: "OAuth 2.0 token and the device public key."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ setting:
+ "This feature cannot be disabled in settings, but the request will "
+ "only be send if the user explicitly enables Smart Lock "
+ "(EasyUnlock), i.e. uccessfully complete the setup flow."
+ chrome_policy {
+ EasyUnlockAllowed {
+ EasyUnlockAllowed: false
+ }
+ }
+ })");
+ MakeApiCall(kToggleEasyUnlockPath, request, callback, error_callback,
+ partial_traffic_annotation);
}
void CryptAuthClientImpl::SetupEnrollment(
const SetupEnrollmentRequest& request,
const SetupEnrollmentCallback& callback,
const ErrorCallback& error_callback) {
- MakeApiCall(kSetupEnrollmentPath, request, callback, error_callback);
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation(
+ "cryptauth_enrollment_flow_setup", "oauth2_api_call_flow", R"(
+ semantics {
+ sender: "CryptAuth Device Manager"
+ description: "Starts the CryptAuth registration flow."
+ trigger:
+ "Occurs periodically, at least once a month, because if the device "
+ "does not re-enroll for more than a specific number of days "
+ "(currently 45) it will be removed from the server."
+ data:
+ "Various device information (public key, bluetooth MAC address, "
+ "model, OS version, screen size, manufacturer, has screen lock "
+ "enabled), and OAuth 2.0 token."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ setting:
+ "This feature cannot be disabled by settings. However, this request "
+ "is made only for signed-in users."
+ chrome_policy {
+ SigninAllowed {
+ SigninAllowed: false
+ }
+ }
+ })");
+ MakeApiCall(kSetupEnrollmentPath, request, callback, error_callback,
+ partial_traffic_annotation);
}
void CryptAuthClientImpl::FinishEnrollment(
const FinishEnrollmentRequest& request,
const FinishEnrollmentCallback& callback,
const ErrorCallback& error_callback) {
- MakeApiCall(kFinishEnrollmentPath, request, callback, error_callback);
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation(
+ "cryptauth_enrollment_flow_finish", "oauth2_api_call_flow", R"(
+ semantics {
+ sender: "CryptAuth Device Manager"
+ description: "Finishes the CryptAuth registration flow."
+ trigger:
+ "Occurs periodically, at least once a month, because if the device "
+ "does not re-enroll for more than a specific number of days "
+ "(currently 45) it will be removed from the server."
+ data: "OAuth 2.0 token."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ setting:
+ "This feature cannot be disabled by settings. However, this request "
+ "is made only for signed-in users."
+ chrome_policy {
+ SigninAllowed {
+ SigninAllowed: false
+ }
+ }
+ })");
+ MakeApiCall(kFinishEnrollmentPath, request, callback, error_callback,
+ partial_traffic_annotation);
}
std::string CryptAuthClientImpl::GetAccessTokenUsed() {
@@ -115,7 +251,8 @@ void CryptAuthClientImpl::MakeApiCall(
const std::string& request_path,
const RequestProto& request_proto,
const base::Callback<void(const ResponseProto&)>& response_callback,
- const ErrorCallback& error_callback) {
+ 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.");
@@ -123,6 +260,9 @@ void CryptAuthClientImpl::MakeApiCall(
}
has_call_started_ = true;
+ api_call_flow_->SetPartialNetworkTrafficAnnotation(
+ partial_traffic_annotation);
+
// The |device_classifier| field must be present for all CryptAuth requests.
RequestProto request_copy(request_proto);
request_copy.mutable_device_classifier()->CopyFrom(device_classifier_);
diff --git a/chromium/components/cryptauth/cryptauth_client_impl.h b/chromium/components/cryptauth/cryptauth_client_impl.h
index d880a118e24..3ccea85148d 100644
--- a/chromium/components/cryptauth/cryptauth_client_impl.h
+++ b/chromium/components/cryptauth/cryptauth_client_impl.h
@@ -11,6 +11,7 @@
#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"
class OAuth2TokenService;
@@ -39,15 +40,22 @@ class CryptAuthClientImpl : public CryptAuthClient {
// CryptAuthClient:
void GetMyDevices(const GetMyDevicesRequest& request,
const GetMyDevicesCallback& callback,
- const ErrorCallback& error_callback) override;
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation) override;
void FindEligibleUnlockDevices(
const FindEligibleUnlockDevicesRequest& request,
const FindEligibleUnlockDevicesCallback& callback,
const ErrorCallback& error_callback) override;
- void SendDeviceSyncTickle(
- const SendDeviceSyncTickleRequest& request,
- const SendDeviceSyncTickleCallback& callback,
+ void FindEligibleForPromotion(
+ const FindEligibleForPromotionRequest& request,
+ const FindEligibleForPromotionCallback& callback,
const ErrorCallback& error_callback) override;
+ void SendDeviceSyncTickle(const SendDeviceSyncTickleRequest& request,
+ const SendDeviceSyncTickleCallback& callback,
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation) override;
void ToggleEasyUnlock(const ToggleEasyUnlockRequest& request,
const ToggleEasyUnlockCallback& callback,
const ErrorCallback& error_callback) override;
@@ -68,7 +76,9 @@ class CryptAuthClientImpl : public CryptAuthClient {
const std::string& request_path,
const RequestProto& request_proto,
const base::Callback<void(const ResponseProto&)>& response_callback,
- const ErrorCallback& error_callback);
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Called when the access token is obtained so the API request can be made.
template <class ResponseProto>
diff --git a/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
index 20224c00295..bcf4be213ed 100644
--- a/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
@@ -13,6 +13,7 @@
#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 "testing/gmock/include/gmock/gmock.h"
@@ -63,7 +64,9 @@ class FakeCryptAuthAccessTokenFetcher : public CryptAuthAccessTokenFetcher {
// Mock CryptAuthApiCallFlow, which handles the HTTP requests to CryptAuth.
class MockCryptAuthApiCallFlow : public CryptAuthApiCallFlow {
public:
- MockCryptAuthApiCallFlow() : CryptAuthApiCallFlow() {}
+ MockCryptAuthApiCallFlow() : CryptAuthApiCallFlow() {
+ SetPartialNetworkTrafficAnnotation(PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ }
virtual ~MockCryptAuthApiCallFlow() {}
MOCK_METHOD6(Start,
@@ -166,7 +169,8 @@ TEST_F(CryptAuthClientTest, GetMyDevicesSuccess) {
client_->GetMyDevices(
request_proto,
base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&NotCalled<std::string>),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
GetMyDevicesRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -203,7 +207,8 @@ TEST_F(CryptAuthClientTest, GetMyDevicesFailure) {
std::string error_message;
client_->GetMyDevices(GetMyDevicesRequest(),
base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
+ base::Bind(&SaveResult<std::string>, &error_message),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
std::string kStatus500Error("HTTP status: 500");
FailApiCallFlow(kStatus500Error);
@@ -271,6 +276,24 @@ TEST_F(CryptAuthClientTest, FindEligibleUnlockDevicesFailure) {
EXPECT_EQ(kStatus403Error, error_message);
}
+TEST_F(CryptAuthClientTest, FindEligibleForPromotionSuccess) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "findeligibleforpromotion?alt=proto");
+
+ FindEligibleForPromotionResponse result_proto;
+ client_->FindEligibleForPromotion(
+ FindEligibleForPromotionRequest(),
+ base::Bind(&SaveResult<FindEligibleForPromotionResponse>, &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ FindEligibleForPromotionRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+
+ FindEligibleForPromotionResponse response_proto;
+ FinishApiCallFlow(&response_proto);
+}
+
TEST_F(CryptAuthClientTest, SendDeviceSyncTickleSuccess) {
ExpectRequest(
"https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
@@ -279,9 +302,9 @@ TEST_F(CryptAuthClientTest, SendDeviceSyncTickleSuccess) {
SendDeviceSyncTickleResponse result_proto;
client_->SendDeviceSyncTickle(
SendDeviceSyncTickleRequest(),
- base::Bind(&SaveResult<SendDeviceSyncTickleResponse>,
- &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&SaveResult<SendDeviceSyncTickleResponse>, &result_proto),
+ base::Bind(&NotCalled<std::string>),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
SendDeviceSyncTickleRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -401,7 +424,8 @@ TEST_F(CryptAuthClientTest, FetchAccessTokenFailure) {
std::string error_message;
client_->GetMyDevices(GetMyDevicesRequest(),
base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
+ base::Bind(&SaveResult<std::string>, &error_message),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ("Failed to get a valid access token.", error_message);
}
@@ -414,7 +438,8 @@ TEST_F(CryptAuthClientTest, ParseResponseProtoFailure) {
std::string error_message;
client_->GetMyDevices(GetMyDevicesRequest(),
base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
+ base::Bind(&SaveResult<std::string>, &error_message),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
flow_result_callback_.Run("Not a valid serialized response message.");
EXPECT_EQ("Failed to parse response proto.", error_message);
@@ -431,7 +456,8 @@ TEST_F(CryptAuthClientTest,
client_->GetMyDevices(
GetMyDevicesRequest(),
base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&NotCalled<std::string>),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
// With request pending, make second request.
{
@@ -466,7 +492,8 @@ TEST_F(CryptAuthClientTest,
std::string error_message;
client_->GetMyDevices(GetMyDevicesRequest(),
base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
+ base::Bind(&SaveResult<std::string>, &error_message),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
// With request pending, make second request.
{
@@ -496,7 +523,8 @@ TEST_F(CryptAuthClientTest,
client_->GetMyDevices(
GetMyDevicesRequest(),
base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&NotCalled<std::string>),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
GetMyDevicesResponse response_proto;
response_proto.add_devices();
@@ -529,7 +557,8 @@ TEST_F(CryptAuthClientTest, DeviceClassifierIsSet) {
client_->GetMyDevices(
request_proto,
base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&NotCalled<std::string>),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
GetMyDevicesRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -555,7 +584,8 @@ TEST_F(CryptAuthClientTest, GetAccessTokenUsed) {
client_->GetMyDevices(
request_proto,
base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&NotCalled<std::string>),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(kAccessToken, client_->GetAccessTokenUsed());
}
diff --git a/chromium/components/cryptauth/cryptauth_device_manager.cc b/chromium/components/cryptauth/cryptauth_device_manager.cc
index 3c7a3cf5789..76d651d3473 100644
--- a/chromium/components/cryptauth/cryptauth_device_manager.cc
+++ b/chromium/components/cryptauth/cryptauth_device_manager.cc
@@ -18,6 +18,7 @@
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/proximity_auth/logging/logging.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
namespace cryptauth {
@@ -45,6 +46,8 @@ const char kExternalDeviceKeyMobileHotspotSupported[] =
"mobile_hotspot_supported";
const char kExternalDeviceKeyDeviceType[] = "device_type";
const char kExternalDeviceKeyBeaconSeeds[] = "beacon_seeds";
+const char kExternalDeviceKeyArcPlusPlus[] = "arc_plus_plus";
+const char kExternalDeviceKeyPixelPhone[] = "pixel_phone";
const char kExternalDeviceKeyBeaconSeedData[] = "beacon_seed_data";
const char kExternalDeviceKeyBeaconSeedStartMs[] = "beacon_seed_start_ms";
const char kExternalDeviceKeyBeaconSeedEndMs[] = "beacon_seed_end_ms";
@@ -155,6 +158,15 @@ std::unique_ptr<base::DictionaryValue> UnlockKeyToDictionary(
device.device_type());
}
+ if (device.has_arc_plus_plus()) {
+ dictionary->SetBoolean(kExternalDeviceKeyArcPlusPlus,
+ device.arc_plus_plus());
+ }
+
+ if (device.has_pixel_phone()) {
+ dictionary->SetBoolean(kExternalDeviceKeyPixelPhone, device.pixel_phone());
+ }
+
std::unique_ptr<base::ListValue> beacon_seed_list =
BeaconSeedsToListValue(device.beacon_seeds());
dictionary->Set(kExternalDeviceKeyBeaconSeeds, std::move(beacon_seed_list));
@@ -253,14 +265,12 @@ bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary,
}
bool unlock_key;
- if (dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key)) {
+ if (dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key))
external_device->set_unlock_key(unlock_key);
- }
bool unlockable;
- if (dictionary.GetBoolean(kExternalDeviceKeyUnlockable, &unlockable)) {
+ if (dictionary.GetBoolean(kExternalDeviceKeyUnlockable, &unlockable))
external_device->set_unlockable(unlockable);
- }
std::string last_update_time_millis_str;
if (dictionary.GetString(
@@ -290,13 +300,28 @@ bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary,
const base::ListValue* beacon_seeds = nullptr;
dictionary.GetList(kExternalDeviceKeyBeaconSeeds, &beacon_seeds);
- if (beacon_seeds) {
+ if (beacon_seeds)
AddBeaconSeedsToExternalDevice(*beacon_seeds, *external_device);
- }
+
+ bool arc_plus_plus;
+ if (dictionary.GetBoolean(kExternalDeviceKeyArcPlusPlus, &arc_plus_plus))
+ external_device->set_arc_plus_plus(arc_plus_plus);
+
+ bool pixel_phone;
+ if (dictionary.GetBoolean(kExternalDeviceKeyPixelPhone, &pixel_phone))
+ external_device->set_pixel_phone(pixel_phone);
return true;
}
+std::unique_ptr<SyncSchedulerImpl> CreateSyncScheduler(
+ SyncScheduler::Delegate* delegate) {
+ return base::MakeUnique<SyncSchedulerImpl>(
+ delegate, base::TimeDelta::FromHours(kRefreshPeriodHours),
+ base::TimeDelta::FromMinutes(kDeviceSyncBaseRecoveryPeriodMinutes),
+ kDeviceSyncMaxJitterRatio, "CryptAuth DeviceSync");
+}
+
} // namespace
CryptAuthDeviceManager::CryptAuthDeviceManager(
@@ -308,6 +333,7 @@ CryptAuthDeviceManager::CryptAuthDeviceManager(
client_factory_(std::move(client_factory)),
gcm_manager_(gcm_manager),
pref_service_(pref_service),
+ scheduler_(CreateSyncScheduler(this)),
weak_ptr_factory_(this) {
UpdateUnlockKeysFromPrefs();
}
@@ -349,7 +375,6 @@ void CryptAuthDeviceManager::Start() {
prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure) ||
last_successful_sync.is_null();
- scheduler_ = CreateSyncScheduler();
scheduler_->Start(elapsed_time_since_last_sync,
is_recovering_from_failure
? SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
@@ -405,6 +430,17 @@ std::vector<ExternalDeviceInfo> CryptAuthDeviceManager::GetUnlockKeys() const {
return unlock_keys;
}
+std::vector<ExternalDeviceInfo> CryptAuthDeviceManager::GetPixelUnlockKeys()
+ const {
+ std::vector<ExternalDeviceInfo> unlock_keys;
+ for (const auto& device : synced_devices_) {
+ if (device.unlock_key() && device.pixel_phone()) {
+ unlock_keys.push_back(device);
+ }
+ }
+ return unlock_keys;
+}
+
std::vector<ExternalDeviceInfo> CryptAuthDeviceManager::GetTetherHosts() const {
std::vector<ExternalDeviceInfo> tether_hosts;
for (const auto& device : synced_devices_) {
@@ -415,14 +451,37 @@ std::vector<ExternalDeviceInfo> CryptAuthDeviceManager::GetTetherHosts() const {
return tether_hosts;
}
+std::vector<ExternalDeviceInfo> CryptAuthDeviceManager::GetPixelTetherHosts()
+ const {
+ std::vector<ExternalDeviceInfo> tether_hosts;
+ for (const auto& device : synced_devices_) {
+ if (device.mobile_hotspot_supported() && device.pixel_phone()) {
+ tether_hosts.push_back(device);
+ }
+ }
+ return tether_hosts;
+}
+
void CryptAuthDeviceManager::OnGetMyDevicesSuccess(
const GetMyDevicesResponse& response) {
// Update the synced devices stored in the user's prefs.
std::unique_ptr<base::ListValue> devices_as_list(new base::ListValue());
+
+ if (!response.devices().empty())
+ PA_LOG(INFO) << "Devices were successfully synced.";
+
for (const auto& device : response.devices()) {
- devices_as_list->Append(UnlockKeyToDictionary(device));
+ std::unique_ptr<base::DictionaryValue> device_dictionary =
+ UnlockKeyToDictionary(device);
+
+ const std::string& device_name = device.has_friendly_device_name()
+ ? device.friendly_device_name()
+ : "[unknown]";
+ PA_LOG(INFO) << "Synced device '" << device_name
+ << "': " << *device_dictionary;
+
+ devices_as_list->Append(std::move(device_dictionary));
}
- PA_LOG(INFO) << "Devices Synced:\n" << *devices_as_list;
bool unlock_keys_changed = !devices_as_list->Equals(
pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys));
@@ -462,13 +521,6 @@ void CryptAuthDeviceManager::OnGetMyDevicesFailure(const std::string& error) {
observer.OnSyncFinished(SyncResult::FAILURE, DeviceChangeResult::UNCHANGED);
}
-std::unique_ptr<SyncScheduler> CryptAuthDeviceManager::CreateSyncScheduler() {
- return base::MakeUnique<SyncSchedulerImpl>(
- this, base::TimeDelta::FromHours(kRefreshPeriodHours),
- base::TimeDelta::FromMinutes(kDeviceSyncBaseRecoveryPeriodMinutes),
- kDeviceSyncMaxJitterRatio, "CryptAuth DeviceSync");
-}
-
void CryptAuthDeviceManager::OnResyncMessage() {
ForceSyncNow(INVOCATION_REASON_SERVER_INITIATED);
}
@@ -529,11 +581,38 @@ void CryptAuthDeviceManager::OnSyncRequested(
GetMyDevicesRequest request;
request.set_invocation_reason(invocation_reason);
request.set_allow_stale_read(is_sync_speculative);
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation("cryptauth_get_my_devices",
+ "oauth2_api_call_flow", R"(
+ semantics {
+ sender: "CryptAuth Device Manager"
+ description:
+ "Gets a list of the devices registered (for the same user) on "
+ "CryptAuth."
+ trigger:
+ "Once every day, or by API request. Periodic calls happen because "
+ "devides that do not re-enrolled for more than X days (currently 45) "
+ "are automatically removed from the server."
+ data: "OAuth 2.0 token."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ setting:
+ "This feature cannot be disabled in settings. However, this request "
+ "is made only for signed-in users."
+ chrome_policy {
+ SigninAllowed {
+ SigninAllowed: false
+ }
+ }
+ })");
cryptauth_client_->GetMyDevices(
- request, base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesSuccess,
- weak_ptr_factory_.GetWeakPtr()),
+ request,
+ base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesSuccess,
+ weak_ptr_factory_.GetWeakPtr()),
base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesFailure,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ partial_traffic_annotation);
}
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_device_manager.h b/chromium/components/cryptauth/cryptauth_device_manager.h
index a728f247abf..6994e54759b 100644
--- a/chromium/components/cryptauth/cryptauth_device_manager.h
+++ b/chromium/components/cryptauth/cryptauth_device_manager.h
@@ -110,17 +110,22 @@ class CryptAuthDeviceManager : public SyncScheduler::Delegate,
// Returns a list of remote devices that can unlock the user's other devices.
virtual std::vector<ExternalDeviceInfo> GetUnlockKeys() const;
+ // Like GetUnlockKeys(), but only returns Pixel devices.
+ virtual std::vector<ExternalDeviceInfo> GetPixelUnlockKeys() const;
// Returns a list of remote devices that can host tether hotspots.
virtual std::vector<ExternalDeviceInfo> GetTetherHosts() const;
+ // Like GetTetherHosts(), but only returns Pixel devices.
+ virtual std::vector<ExternalDeviceInfo> GetPixelTetherHosts() const;
protected:
// Empty constructor, to be used by tests to mock the device manager. Do not
// use this constructor outside of tests.
CryptAuthDeviceManager();
- // Creates a new SyncScheduler instance. Exposed for testing.
- virtual std::unique_ptr<SyncScheduler> CreateSyncScheduler();
+ void SetSyncSchedulerForTest(std::unique_ptr<SyncScheduler> sync_scheduler) {
+ scheduler_ = std::move(sync_scheduler);
+ }
private:
// CryptAuthGCMManager::Observer:
diff --git a/chromium/components/cryptauth/cryptauth_device_manager_unittest.cc b/chromium/components/cryptauth/cryptauth_device_manager_unittest.cc
index 7dcd284349e..ec960e085f1 100644
--- a/chromium/components/cryptauth/cryptauth_device_manager_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_device_manager_unittest.cc
@@ -21,6 +21,7 @@
#include "components/cryptauth/pref_names.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_service.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -52,7 +53,7 @@ const bool kStoredMobileHotspotSupported = true;
// ExternalDeviceInfo fields for the synced unlock key.
const char kPublicKey1[] = "GOOG";
-const char kDeviceName1[] = "Nexus 5";
+const char kDeviceName1[] = "Pixel XL";
const char kBluetoothAddress1[] = "aa:bb:cc:ee:dd:ff";
const bool kUnlockKey1 = true;
const bool kUnlockable1 = false;
@@ -63,6 +64,8 @@ const int64_t kBeaconSeed1EndTime = 123457;
const char kBeaconSeed2Data[] = "beaconSeed2Data";
const int64_t kBeaconSeed2StartTime = 234567;
const int64_t kBeaconSeed2EndTime = 234568;
+const bool kArcPlusPlus1 = true;
+const bool kPixelPhone1 = true;
// ExternalDeviceInfo fields for a non-synced unlockable device.
const char kPublicKey2[] = "MSFT";
@@ -76,6 +79,8 @@ const int64_t kBeaconSeed3EndTime = 123457;
const char kBeaconSeed4Data[] = "beaconSeed4Data";
const int64_t kBeaconSeed4StartTime = 234567;
const int64_t kBeaconSeed4EndTime = 234568;
+const bool kArcPlusPlus2 = false;
+const bool kPixelPhone2 = false;
// Validates that |devices| is equal to |expected_devices|.
void ExpectSyncedDevicesAreEqual(
@@ -297,23 +302,20 @@ class TestCryptAuthDeviceManager : public CryptAuthDeviceManager {
gcm_manager,
pref_service),
scoped_sync_scheduler_(new NiceMock<MockSyncScheduler>()),
- weak_sync_scheduler_factory_(scoped_sync_scheduler_.get()) {}
+ weak_sync_scheduler_factory_(scoped_sync_scheduler_) {
+ SetSyncSchedulerForTest(base::WrapUnique(scoped_sync_scheduler_));
+ }
~TestCryptAuthDeviceManager() override {}
- std::unique_ptr<SyncScheduler> CreateSyncScheduler() override {
- EXPECT_TRUE(scoped_sync_scheduler_);
- return std::move(scoped_sync_scheduler_);
- }
-
base::WeakPtr<MockSyncScheduler> GetSyncScheduler() {
return weak_sync_scheduler_factory_.GetWeakPtr();
}
private:
// Ownership is passed to |CryptAuthDeviceManager| super class when
- // |CreateSyncScheduler()| is called.
- std::unique_ptr<MockSyncScheduler> scoped_sync_scheduler_;
+ // SetSyncSchedulerForTest() is called.
+ MockSyncScheduler* scoped_sync_scheduler_;
// Stores the pointer of |scoped_sync_scheduler_| after ownership is passed to
// the super class.
@@ -353,6 +355,8 @@ class CryptAuthDeviceManagerTest
seed2->set_data(kBeaconSeed2Data);
seed2->set_start_time_millis(kBeaconSeed2StartTime);
seed2->set_end_time_millis(kBeaconSeed2EndTime);
+ unlock_key.set_arc_plus_plus(kArcPlusPlus1);
+ unlock_key.set_pixel_phone(kPixelPhone1);
devices_in_response_.push_back(unlock_key);
ExternalDeviceInfo unlockable_device;
@@ -369,6 +373,8 @@ class CryptAuthDeviceManagerTest
seed4->set_data(kBeaconSeed4Data);
seed4->set_start_time_millis(kBeaconSeed4StartTime);
seed4->set_end_time_millis(kBeaconSeed4EndTime);
+ unlockable_device.set_arc_plus_plus(kArcPlusPlus2);
+ unlockable_device.set_pixel_phone(kPixelPhone2);
devices_in_response_.push_back(unlockable_device);
}
@@ -411,8 +417,7 @@ class CryptAuthDeviceManagerTest
bluetooth_address_b64);
device_dictionary->SetBoolean("unlock_key", kStoredUnlockKey);
device_dictionary->SetBoolean("unlockable", kStoredUnlockable);
- device_dictionary->Set("beacon_seeds",
- base::WrapUnique(new base::ListValue()));
+ device_dictionary->Set("beacon_seeds", base::MakeUnique<base::ListValue>());
device_dictionary->SetBoolean("mobile_hotspot_supported",
kStoredMobileHotspotSupported);
{
@@ -473,7 +478,7 @@ class CryptAuthDeviceManagerTest
// MockCryptAuthClientFactory::Observer:
void OnCryptAuthClientCreated(MockCryptAuthClient* client) override {
- EXPECT_CALL(*client, GetMyDevices(_, _, _))
+ EXPECT_CALL(*client, GetMyDevices(_, _, _, _))
.WillOnce(DoAll(SaveArg<0>(&get_my_devices_request_),
SaveArg<1>(&success_callback_),
SaveArg<2>(&error_callback_)));
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_manager.cc b/chromium/components/cryptauth/cryptauth_enrollment_manager.cc
index 8381de24ccf..53303471fe9 100644
--- a/chromium/components/cryptauth/cryptauth_enrollment_manager.cc
+++ b/chromium/components/cryptauth/cryptauth_enrollment_manager.cc
@@ -42,6 +42,14 @@ const double kEnrollmentMaxJitterRatio = 0.2;
// registration.
const char kDeviceSoftwarePackage[] = "com.google.chrome.cryptauth";
+std::unique_ptr<SyncScheduler> CreateSyncScheduler(
+ SyncScheduler::Delegate* delegate) {
+ return base::MakeUnique<SyncSchedulerImpl>(
+ delegate, base::TimeDelta::FromDays(kEnrollmentRefreshPeriodDays),
+ base::TimeDelta::FromMinutes(kEnrollmentBaseRecoveryPeriodMinutes),
+ kEnrollmentMaxJitterRatio, "CryptAuth Enrollment");
+}
+
} // namespace
CryptAuthEnrollmentManager::CryptAuthEnrollmentManager(
@@ -57,6 +65,7 @@ CryptAuthEnrollmentManager::CryptAuthEnrollmentManager(
device_info_(device_info),
gcm_manager_(gcm_manager),
pref_service_(pref_service),
+ scheduler_(CreateSyncScheduler(this /* delegate */)),
weak_ptr_factory_(this) {}
CryptAuthEnrollmentManager::~CryptAuthEnrollmentManager() {
@@ -89,7 +98,6 @@ void CryptAuthEnrollmentManager::Start() {
base::TimeDelta elapsed_time_since_last_sync =
clock_->Now() - last_successful_enrollment;
- scheduler_ = CreateSyncScheduler();
scheduler_->Start(elapsed_time_since_last_sync,
is_recovering_from_failure
? SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
@@ -159,14 +167,6 @@ void CryptAuthEnrollmentManager::OnEnrollmentFinished(bool success) {
observer.OnEnrollmentFinished(success);
}
-std::unique_ptr<SyncScheduler>
-CryptAuthEnrollmentManager::CreateSyncScheduler() {
- return base::MakeUnique<SyncSchedulerImpl>(
- this, base::TimeDelta::FromDays(kEnrollmentRefreshPeriodDays),
- base::TimeDelta::FromMinutes(kEnrollmentBaseRecoveryPeriodMinutes),
- kEnrollmentMaxJitterRatio, "CryptAuth Enrollment");
-}
-
std::string CryptAuthEnrollmentManager::GetUserPublicKey() const {
std::string public_key;
if (!base::Base64UrlDecode(
@@ -189,6 +189,11 @@ std::string CryptAuthEnrollmentManager::GetUserPrivateKey() const {
return private_key;
}
+void CryptAuthEnrollmentManager::SetSyncSchedulerForTest(
+ std::unique_ptr<SyncScheduler> sync_scheduler) {
+ scheduler_ = std::move(sync_scheduler);
+}
+
void CryptAuthEnrollmentManager::OnGCMRegistrationResult(bool success) {
if (!sync_request_)
return;
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_manager.h b/chromium/components/cryptauth/cryptauth_enrollment_manager.h
index ce4c9da774d..8bd187a5691 100644
--- a/chromium/components/cryptauth/cryptauth_enrollment_manager.h
+++ b/chromium/components/cryptauth/cryptauth_enrollment_manager.h
@@ -116,8 +116,7 @@ class CryptAuthEnrollmentManager : public SyncScheduler::Delegate,
virtual std::string GetUserPrivateKey() const;
protected:
- // Creates a new SyncScheduler instance. Exposed for testing.
- virtual std::unique_ptr<SyncScheduler> CreateSyncScheduler();
+ void SetSyncSchedulerForTest(std::unique_ptr<SyncScheduler> sync_scheduler);
private:
// CryptAuthGCMManager::Observer:
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc b/chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc
index 7a98f86d8e4..d0a49203e6b 100644
--- a/chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc
@@ -112,15 +112,12 @@ class TestCryptAuthEnrollmentManager : public CryptAuthEnrollmentManager {
gcm_manager,
pref_service),
scoped_sync_scheduler_(new NiceMock<MockSyncScheduler>()),
- weak_sync_scheduler_factory_(scoped_sync_scheduler_.get()) {}
+ weak_sync_scheduler_factory_(scoped_sync_scheduler_) {
+ SetSyncSchedulerForTest(base::WrapUnique(scoped_sync_scheduler_));
+ }
~TestCryptAuthEnrollmentManager() override {}
- std::unique_ptr<SyncScheduler> CreateSyncScheduler() override {
- EXPECT_TRUE(scoped_sync_scheduler_);
- return std::move(scoped_sync_scheduler_);
- }
-
base::WeakPtr<MockSyncScheduler> GetSyncScheduler() {
return weak_sync_scheduler_factory_.GetWeakPtr();
}
@@ -128,7 +125,7 @@ class TestCryptAuthEnrollmentManager : public CryptAuthEnrollmentManager {
private:
// Ownership is passed to |CryptAuthEnrollmentManager| super class when
// |CreateSyncScheduler()| is called.
- std::unique_ptr<MockSyncScheduler> scoped_sync_scheduler_;
+ NiceMock<MockSyncScheduler>* scoped_sync_scheduler_;
// Stores the pointer of |scoped_sync_scheduler_| after ownership is passed to
// the super class.
diff --git a/chromium/components/cryptauth/device_to_device_authenticator.cc b/chromium/components/cryptauth/device_to_device_authenticator.cc
index 75e19f2f304..a8a648b390d 100644
--- a/chromium/components/cryptauth/device_to_device_authenticator.cc
+++ b/chromium/components/cryptauth/device_to_device_authenticator.cc
@@ -11,7 +11,6 @@
#include "base/timer/timer.h"
#include "components/cryptauth/authenticator.h"
#include "components/cryptauth/connection.h"
-#include "components/cryptauth/device_to_device_initiator_operations.h"
#include "components/cryptauth/device_to_device_secure_context.h"
#include "components/cryptauth/secure_context.h"
#include "components/cryptauth/secure_message_delegate.h"
@@ -69,6 +68,7 @@ DeviceToDeviceAuthenticator::DeviceToDeviceAuthenticator(
: connection_(connection),
account_id_(account_id),
secure_message_delegate_(std::move(secure_message_delegate)),
+ helper_(base::MakeUnique<DeviceToDeviceInitiatorHelper>()),
state_(State::NOT_STARTED),
weak_ptr_factory_(this) {
DCHECK(connection_);
@@ -112,9 +112,9 @@ void DeviceToDeviceAuthenticator::OnKeyPairGenerated(
}
local_session_private_key_ = private_key;
- // Create the [Hello] message to send to the remote device.
+ // Create the [Initiator Hello] message to send to the remote device.
state_ = State::SENDING_HELLO;
- DeviceToDeviceInitiatorOperations::CreateHelloMessage(
+ helper_->CreateHelloMessage(
public_key, connection_->remote_device().persistent_symmetric_key,
secure_message_delegate_.get(),
base::Bind(&DeviceToDeviceAuthenticator::OnHelloMessageCreated,
@@ -129,10 +129,12 @@ void DeviceToDeviceAuthenticator::OnHelloMessageCreated(
const std::string& message) {
DCHECK(state_ == State::SENDING_HELLO);
if (message.empty()) {
- Fail("Failed to create [Hello]");
+ Fail("Failed to create [Initiator Hello]");
return;
}
+ PA_LOG(INFO) << "Sending [Initiator Hello] message.";
+
// Add a timeout for receiving the [Responder Auth] message as a guard.
timer_ = CreateTimer();
timer_->Start(
@@ -140,7 +142,7 @@ void DeviceToDeviceAuthenticator::OnHelloMessageCreated(
base::Bind(&DeviceToDeviceAuthenticator::OnResponderAuthTimedOut,
weak_ptr_factory_.GetWeakPtr()));
- // Send the [Hello] message to the remote device.
+ // Send the [Initiator Hello] message to the remote device.
state_ = State::SENT_HELLO;
hello_message_ = message;
connection_->SendMessage(base::MakeUnique<WireMessage>(
@@ -166,7 +168,7 @@ void DeviceToDeviceAuthenticator::OnResponderAuthValidated(
session_keys_ = session_keys;
// Create the [Initiator Auth] message to send to the remote device.
- DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage(
+ helper_->CreateInitiatorAuthMessage(
session_keys_, connection_->remote_device().persistent_symmetric_key,
responder_auth_message_, secure_message_delegate_.get(),
base::Bind(&DeviceToDeviceAuthenticator::OnInitiatorAuthCreated,
@@ -241,7 +243,7 @@ void DeviceToDeviceAuthenticator::OnMessageReceived(
// Attempt to validate the [Responder Auth] message received from the remote
// device.
std::string responder_public_key = connection.remote_device().public_key;
- DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage(
+ helper_->ValidateResponderAuthMessage(
responder_auth_message_, responder_public_key,
connection_->remote_device().persistent_symmetric_key,
local_session_private_key_, hello_message_,
@@ -264,7 +266,7 @@ void DeviceToDeviceAuthenticator::OnSendCompleted(
Fail("Failed to send [Initiator Auth]");
} else if (!success && state_ == State::SENT_HELLO) {
DCHECK(message.payload() == hello_message_);
- Fail("Failed to send [Hello]");
+ Fail("Failed to send [Initiator Hello]");
}
}
diff --git a/chromium/components/cryptauth/device_to_device_authenticator.h b/chromium/components/cryptauth/device_to_device_authenticator.h
index 13ffa6c2226..ac34a9c7e15 100644
--- a/chromium/components/cryptauth/device_to_device_authenticator.h
+++ b/chromium/components/cryptauth/device_to_device_authenticator.h
@@ -13,6 +13,7 @@
#include "components/cryptauth/authenticator.h"
#include "components/cryptauth/connection.h"
#include "components/cryptauth/connection_observer.h"
+#include "components/cryptauth/device_to_device_initiator_helper.h"
#include "components/cryptauth/session_keys.h"
namespace base {
@@ -153,6 +154,9 @@ class DeviceToDeviceAuthenticator : public Authenticator,
// Handles SecureMessage crypto operations.
std::unique_ptr<SecureMessageDelegate> secure_message_delegate_;
+ // Performs authentication handshake.
+ std::unique_ptr<DeviceToDeviceInitiatorHelper> helper_;
+
// The current state in the authentication flow.
State state_;
diff --git a/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc b/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc
index 362cd8ea205..851df649c62 100644
--- a/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc
+++ b/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc
@@ -143,15 +143,15 @@ class DeviceToDeviceAuthenticatorForTest : public DeviceToDeviceAuthenticator {
} // namespace
-class ProximityAuthDeviceToDeviceAuthenticatorTest : public testing::Test {
+class CryptAuthDeviceToDeviceAuthenticatorTest : public testing::Test {
public:
- ProximityAuthDeviceToDeviceAuthenticatorTest()
+ CryptAuthDeviceToDeviceAuthenticatorTest()
: remote_device_(CreateClassicRemoteDeviceForTest()),
connection_(remote_device_),
secure_message_delegate_(new FakeSecureMessageDelegate),
authenticator_(&connection_,
base::WrapUnique(secure_message_delegate_)) {}
- ~ProximityAuthDeviceToDeviceAuthenticatorTest() override {}
+ ~CryptAuthDeviceToDeviceAuthenticatorTest() override {}
void SetUp() override {
// Set up the session asymmetric keys for both the local and remote devices.
@@ -179,7 +179,7 @@ class ProximityAuthDeviceToDeviceAuthenticatorTest : public testing::Test {
// device to the remote device.
std::string BeginAuthentication() {
authenticator_.Authenticate(base::Bind(
- &ProximityAuthDeviceToDeviceAuthenticatorTest::OnAuthenticationResult,
+ &CryptAuthDeviceToDeviceAuthenticatorTest::OnAuthenticationResult,
base::Unretained(this)));
EXPECT_EQ(1u, connection_.message_buffer().size());
@@ -251,10 +251,10 @@ class ProximityAuthDeviceToDeviceAuthenticatorTest : public testing::Test {
// Stores the SecureContext returned after authentication succeeds.
std::unique_ptr<SecureContext> secure_context_;
- DISALLOW_COPY_AND_ASSIGN(ProximityAuthDeviceToDeviceAuthenticatorTest);
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthDeviceToDeviceAuthenticatorTest);
};
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, AuthenticateSucceeds) {
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest, AuthenticateSucceeds) {
// Starts the authentication protocol and grab [Hello] message.
std::string hello_message = BeginAuthentication();
@@ -277,7 +277,7 @@ TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, AuthenticateSucceeds) {
ASSERT_TRUE(initiator_auth_validated);
}
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, ResponderRejectsHello) {
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest, ResponderRejectsHello) {
std::string hello_message = BeginAuthentication();
// If the responder could not validate the [Hello message], it essentially
@@ -290,7 +290,7 @@ TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, ResponderRejectsHello) {
EXPECT_FALSE(secure_context_);
}
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, ResponderAuthTimesOut) {
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest, ResponderAuthTimesOut) {
// Starts the authentication protocol and grab [Hello] message.
std::string hello_message = BeginAuthentication();
ASSERT_TRUE(authenticator_.timer());
@@ -300,7 +300,7 @@ TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, ResponderAuthTimesOut) {
EXPECT_FALSE(secure_context_);
}
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest,
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest,
DisconnectsWaitingForResponderAuth) {
std::string hello_message = BeginAuthentication();
EXPECT_CALL(*this,
@@ -309,27 +309,27 @@ TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest,
EXPECT_FALSE(secure_context_);
}
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, NotConnectedInitially) {
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest, NotConnectedInitially) {
connection_.Disconnect();
EXPECT_CALL(*this,
OnAuthenticationResultProxy(Authenticator::Result::DISCONNECTED));
authenticator_.Authenticate(base::Bind(
- &ProximityAuthDeviceToDeviceAuthenticatorTest::OnAuthenticationResult,
+ &CryptAuthDeviceToDeviceAuthenticatorTest::OnAuthenticationResult,
base::Unretained(this)));
EXPECT_FALSE(secure_context_);
}
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, FailToSendHello) {
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest, FailToSendHello) {
connection_.set_connection_blocked(true);
EXPECT_CALL(*this,
OnAuthenticationResultProxy(Authenticator::Result::FAILURE));
authenticator_.Authenticate(base::Bind(
- &ProximityAuthDeviceToDeviceAuthenticatorTest::OnAuthenticationResult,
+ &CryptAuthDeviceToDeviceAuthenticatorTest::OnAuthenticationResult,
base::Unretained(this)));
EXPECT_FALSE(secure_context_);
}
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, FailToSendInitiatorAuth) {
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest, FailToSendInitiatorAuth) {
std::string hello_message = BeginAuthentication();
connection_.set_connection_blocked(true);
@@ -339,7 +339,7 @@ TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest, FailToSendInitiatorAuth) {
EXPECT_FALSE(secure_context_);
}
-TEST_F(ProximityAuthDeviceToDeviceAuthenticatorTest,
+TEST_F(CryptAuthDeviceToDeviceAuthenticatorTest,
SendMessagesAfterAuthenticationSuccess) {
std::string hello_message = BeginAuthentication();
EXPECT_CALL(*this,
diff --git a/chromium/components/cryptauth/device_to_device_initiator_helper.cc b/chromium/components/cryptauth/device_to_device_initiator_helper.cc
new file mode 100644
index 00000000000..bf09eed0832
--- /dev/null
+++ b/chromium/components/cryptauth/device_to_device_initiator_helper.cc
@@ -0,0 +1,332 @@
+// 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/device_to_device_initiator_helper.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/secure_message_delegate.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace cryptauth {
+
+namespace {
+
+// TODO(tengs): Due to a bug with the ChromeOS secure message daemon, we cannot
+// create SecureMessages with empty payloads. To workaround this bug, this value
+// is put into the payload if it would otherwise be empty.
+// See crbug.com/512894.
+const char kPayloadFiller[] = "\xae";
+
+// The version to put in the GcmMetadata field.
+const int kGcmMetadataVersion = 1;
+
+// The D2D protocol version.
+const int kD2DProtocolVersion = 1;
+
+} // namespace
+
+DeviceToDeviceInitiatorHelper::DeviceToDeviceInitiatorHelper()
+ : weak_ptr_factory_(this) {}
+
+DeviceToDeviceInitiatorHelper::~DeviceToDeviceInitiatorHelper() {}
+
+void DeviceToDeviceInitiatorHelper::CreateHelloMessage(
+ const std::string& session_public_key,
+ const std::string& persistent_symmetric_key,
+ SecureMessageDelegate* secure_message_delegate,
+ const MessageCallback& callback) {
+ // Decode public key into the |initator_hello| proto.
+ securemessage::InitiatorHello initiator_hello;
+ if (!initiator_hello.mutable_public_dh_key()->ParseFromString(
+ session_public_key)) {
+ PA_LOG(ERROR) << "Unable to parse user's public key";
+ callback.Run(std::string());
+ return;
+ }
+ initiator_hello.set_protocol_version(kD2DProtocolVersion);
+
+ // The [Hello] message has the structure:
+ // {
+ // header: <session_public_key>,
+ // Sig(<session_public_key>, persistent_symmetric_key)
+ // payload: ""
+ // }
+ SecureMessageDelegate::CreateOptions create_options;
+ create_options.encryption_scheme = securemessage::NONE;
+ create_options.signature_scheme = securemessage::HMAC_SHA256;
+ initiator_hello.SerializeToString(&create_options.public_metadata);
+ secure_message_delegate->CreateSecureMessage(
+ kPayloadFiller, persistent_symmetric_key, create_options, callback);
+}
+
+void DeviceToDeviceInitiatorHelper::ValidateResponderAuthMessage(
+ const std::string& responder_auth_message,
+ const std::string& persistent_responder_public_key,
+ const std::string& persistent_symmetric_key,
+ const std::string& session_private_key,
+ const std::string& hello_message,
+ SecureMessageDelegate* secure_message_delegate,
+ const ValidateResponderAuthCallback& callback) {
+ // The [Responder Auth] message has the structure:
+ // {
+ // header: <responder_public_key>,
+ // Sig(<responder_public_key> + payload1,
+ // session_symmetric_key),
+ // payload1: Enc({
+ // header: Sig(payload2 + <hello_message>, persistent_symmetric_key),
+ // payload2: {
+ // sequence_number: 1,
+ // body: Enc({
+ // header: Sig(payload3 + <hello_message>,
+ // persistent_responder_public_key),
+ // payload3: ""
+ // }, persistent_symmetric_key)
+ // }
+ // }, session_symmetric_key),
+ // }
+ ValidateResponderAuthMessageContext context(
+ responder_auth_message, persistent_responder_public_key,
+ persistent_symmetric_key, session_private_key, hello_message,
+ secure_message_delegate, callback);
+ BeginResponderAuthValidation(context);
+}
+
+void DeviceToDeviceInitiatorHelper::CreateInitiatorAuthMessage(
+ const SessionKeys& session_keys,
+ const std::string& persistent_symmetric_key,
+ const std::string& responder_auth_message,
+ SecureMessageDelegate* secure_message_delegate,
+ const MessageCallback& callback) {
+ // The [Initiator Auth] message has the structure:
+ // {
+ // header: Sig(payload1, session_symmetric_key)
+ // payload1: Enc({
+ // sequence_number: 2,
+ // body: {
+ // header: Sig(payload2 + responder_auth_message,
+ // persistent_symmetric_key)
+ // payload2: ""
+ // }
+ // }, session_symmetric_key)
+ // }
+ SecureMessageDelegate::CreateOptions create_options;
+ create_options.encryption_scheme = securemessage::AES_256_CBC;
+ create_options.signature_scheme = securemessage::HMAC_SHA256;
+ create_options.associated_data = responder_auth_message;
+ secure_message_delegate->CreateSecureMessage(
+ kPayloadFiller, persistent_symmetric_key, create_options,
+ base::Bind(
+ &DeviceToDeviceInitiatorHelper::OnInnerMessageCreatedForInitiatorAuth,
+ weak_ptr_factory_.GetWeakPtr(), session_keys, secure_message_delegate,
+ callback));
+}
+
+DeviceToDeviceInitiatorHelper::ValidateResponderAuthMessageContext ::
+ ValidateResponderAuthMessageContext(
+ const std::string& responder_auth_message,
+ const std::string& persistent_responder_public_key,
+ const std::string& persistent_symmetric_key,
+ const std::string& session_private_key,
+ const std::string& hello_message,
+ SecureMessageDelegate* secure_message_delegate,
+ const ValidateResponderAuthCallback& callback)
+ : responder_auth_message(responder_auth_message),
+ persistent_responder_public_key(persistent_responder_public_key),
+ persistent_symmetric_key(persistent_symmetric_key),
+ session_private_key(session_private_key),
+ hello_message(hello_message),
+ secure_message_delegate(secure_message_delegate),
+ callback(callback) {}
+
+DeviceToDeviceInitiatorHelper::ValidateResponderAuthMessageContext ::
+ ValidateResponderAuthMessageContext(
+ const ValidateResponderAuthMessageContext& other)
+ : responder_auth_message(other.responder_auth_message),
+ persistent_responder_public_key(other.persistent_responder_public_key),
+ persistent_symmetric_key(other.persistent_symmetric_key),
+ session_private_key(other.session_private_key),
+ hello_message(other.hello_message),
+ secure_message_delegate(other.secure_message_delegate),
+ callback(other.callback),
+ responder_session_public_key(other.responder_session_public_key),
+ session_symmetric_key(other.session_symmetric_key) {}
+
+DeviceToDeviceInitiatorHelper::ValidateResponderAuthMessageContext ::
+ ~ValidateResponderAuthMessageContext() {}
+
+void DeviceToDeviceInitiatorHelper::OnInnerMessageCreatedForInitiatorAuth(
+ const SessionKeys& session_keys,
+ SecureMessageDelegate* secure_message_delegate,
+ const DeviceToDeviceInitiatorHelper::MessageCallback& callback,
+ const std::string& inner_message) {
+ if (inner_message.empty()) {
+ PA_LOG(INFO) << "Failed to create inner message for [Initiator Auth].";
+ callback.Run(std::string());
+ return;
+ }
+
+ GcmMetadata gcm_metadata;
+ gcm_metadata.set_type(DEVICE_TO_DEVICE_MESSAGE);
+ gcm_metadata.set_version(kGcmMetadataVersion);
+
+ // Store the inner message inside a DeviceToDeviceMessage proto.
+ securemessage::DeviceToDeviceMessage device_to_device_message;
+ device_to_device_message.set_message(inner_message);
+ device_to_device_message.set_sequence_number(1);
+
+ // Create and return the outer message, which wraps the inner message.
+ SecureMessageDelegate::CreateOptions create_options;
+ create_options.encryption_scheme = securemessage::AES_256_CBC;
+ create_options.signature_scheme = securemessage::HMAC_SHA256;
+ gcm_metadata.SerializeToString(&create_options.public_metadata);
+ secure_message_delegate->CreateSecureMessage(
+ device_to_device_message.SerializeAsString(),
+ session_keys.initiator_encode_key(), create_options, callback);
+}
+
+void DeviceToDeviceInitiatorHelper::BeginResponderAuthValidation(
+ ValidateResponderAuthMessageContext context) {
+ // Parse the encrypted SecureMessage so we can get plaintext data from the
+ // header. Note that the payload will be encrypted.
+ securemessage::SecureMessage encrypted_message;
+ securemessage::HeaderAndBody header_and_body;
+ if (!encrypted_message.ParseFromString(context.responder_auth_message) ||
+ !header_and_body.ParseFromString(encrypted_message.header_and_body())) {
+ PA_LOG(WARNING) << "Failed to parse [Responder Hello] message";
+ context.callback.Run(false, SessionKeys());
+ return;
+ }
+
+ // Check that header public_metadata contains the correct metadata fields.
+ securemessage::Header header = header_and_body.header();
+ GcmMetadata gcm_metadata;
+ if (!gcm_metadata.ParseFromString(header.public_metadata()) ||
+ gcm_metadata.type() != DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD ||
+ gcm_metadata.version() != kGcmMetadataVersion) {
+ PA_LOG(WARNING) << "Failed to validate GcmMetadata in "
+ << "[Responder Auth] header.";
+ context.callback.Run(false, SessionKeys());
+ return;
+ }
+
+ // Extract responder session public key from |decryption_key_id| field.
+ securemessage::ResponderHello responder_hello;
+ if (!responder_hello.ParseFromString(header.decryption_key_id()) ||
+ !responder_hello.public_dh_key().SerializeToString(
+ &context.responder_session_public_key)) {
+ PA_LOG(INFO) << "Failed to extract responder session public key in "
+ << "[Responder Auth] header.";
+ context.callback.Run(false, SessionKeys());
+ return;
+ }
+
+ // Perform a Diffie-Helmann key exchange to get the session symmetric key.
+ context.secure_message_delegate->DeriveKey(
+ context.session_private_key, context.responder_session_public_key,
+ base::Bind(&DeviceToDeviceInitiatorHelper::OnSessionSymmetricKeyDerived,
+ weak_ptr_factory_.GetWeakPtr(), context));
+}
+
+void DeviceToDeviceInitiatorHelper::OnSessionSymmetricKeyDerived(
+ ValidateResponderAuthMessageContext context,
+ const std::string& session_symmetric_key) {
+ context.session_symmetric_key = session_symmetric_key;
+
+ // Unwrap the outer message, using symmetric key encryption and signature.
+ SecureMessageDelegate::UnwrapOptions unwrap_options;
+ unwrap_options.encryption_scheme = securemessage::AES_256_CBC;
+ unwrap_options.signature_scheme = securemessage::HMAC_SHA256;
+ context.secure_message_delegate->UnwrapSecureMessage(
+ context.responder_auth_message,
+ SessionKeys(session_symmetric_key).responder_encode_key(), unwrap_options,
+ base::Bind(&DeviceToDeviceInitiatorHelper::
+ OnOuterMessageUnwrappedForResponderAuth,
+ weak_ptr_factory_.GetWeakPtr(), context));
+}
+
+void DeviceToDeviceInitiatorHelper::OnOuterMessageUnwrappedForResponderAuth(
+ const ValidateResponderAuthMessageContext& context,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header) {
+ if (!verified) {
+ PA_LOG(INFO) << "Failed to unwrap outer [Responder Auth] message.";
+ context.callback.Run(false, SessionKeys());
+ return;
+ }
+
+ // Parse the decrypted payload.
+ securemessage::DeviceToDeviceMessage device_to_device_message;
+ if (!device_to_device_message.ParseFromString(payload) ||
+ device_to_device_message.sequence_number() != 1) {
+ PA_LOG(INFO) << "Failed to validate DeviceToDeviceMessage payload.";
+ context.callback.Run(false, SessionKeys());
+ return;
+ }
+
+ // Unwrap the middle level SecureMessage, using symmetric key encryption and
+ // signature.
+ SecureMessageDelegate::UnwrapOptions unwrap_options;
+ unwrap_options.encryption_scheme = securemessage::AES_256_CBC;
+ unwrap_options.signature_scheme = securemessage::HMAC_SHA256;
+ unwrap_options.associated_data = context.hello_message;
+ context.secure_message_delegate->UnwrapSecureMessage(
+ device_to_device_message.message(), context.persistent_symmetric_key,
+ unwrap_options,
+ base::Bind(&DeviceToDeviceInitiatorHelper::
+ OnMiddleMessageUnwrappedForResponderAuth,
+ weak_ptr_factory_.GetWeakPtr(), context));
+}
+
+void DeviceToDeviceInitiatorHelper::OnMiddleMessageUnwrappedForResponderAuth(
+ const ValidateResponderAuthMessageContext& context,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header) {
+ if (!verified) {
+ PA_LOG(INFO) << "Failed to unwrap middle [Responder Auth] message.";
+ context.callback.Run(false, SessionKeys());
+ return;
+ }
+
+ // Unwrap the inner-most SecureMessage, using no encryption and an asymmetric
+ // key signature.
+ SecureMessageDelegate::UnwrapOptions unwrap_options;
+ unwrap_options.encryption_scheme = securemessage::NONE;
+ unwrap_options.signature_scheme = securemessage::ECDSA_P256_SHA256;
+ unwrap_options.associated_data = context.hello_message;
+ context.secure_message_delegate->UnwrapSecureMessage(
+ payload, context.persistent_responder_public_key, unwrap_options,
+ base::Bind(&DeviceToDeviceInitiatorHelper::
+ OnInnerMessageUnwrappedForResponderAuth,
+ weak_ptr_factory_.GetWeakPtr(), context));
+}
+
+// Called after the inner-most layer of [Responder Auth] is unwrapped.
+void DeviceToDeviceInitiatorHelper::OnInnerMessageUnwrappedForResponderAuth(
+ const ValidateResponderAuthMessageContext& context,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header) {
+ if (!verified)
+ PA_LOG(INFO) << "Failed to unwrap inner [Responder Auth] message.";
+
+ // Note: The GMS Core implementation does not properly set the metadata
+ // version, so we only check that the type is UNLOCK_KEY_SIGNED_CHALLENGE.
+ GcmMetadata gcm_metadata;
+ if (!gcm_metadata.ParseFromString(header.public_metadata()) ||
+ gcm_metadata.type() != UNLOCK_KEY_SIGNED_CHALLENGE) {
+ PA_LOG(WARNING) << "Failed to validate GcmMetadata in inner-most "
+ << "[Responder Auth] message.";
+ context.callback.Run(false, SessionKeys());
+ return;
+ }
+
+ context.callback.Run(verified, SessionKeys(context.session_symmetric_key));
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/device_to_device_initiator_helper.h b/chromium/components/cryptauth/device_to_device_initiator_helper.h
new file mode 100644
index 00000000000..c9627f93399
--- /dev/null
+++ b/chromium/components/cryptauth/device_to_device_initiator_helper.h
@@ -0,0 +1,187 @@
+// 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_CRYPTAUTH_DEVICE_TO_DEVICE_INITIATOR_HELPER_H_
+#define COMPONENTS_CRYPTAUTH_DEVICE_TO_DEVICE_INITIATOR_HELPER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/cryptauth/proto/securemessage.pb.h"
+#include "components/cryptauth/session_keys.h"
+
+namespace cryptauth {
+
+class SecureMessageDelegate;
+
+// Class containing operations in the DeviceToDevice protocol that the initiator
+// needs to perform. This class is instantiable rather than being a utility
+// class because it relies on a WeakPtrFactory to prevent referencing deleted
+// memory.
+//
+// All operations are asynchronous because we use the SecureMessageDelegate for
+// crypto operations, whose implementation may be asynchronous.
+//
+// In the DeviceToDevice protocol, the initiator needs to send two messages to
+// the responder and parse one message from the responder:
+// 1. Send [Hello] Message
+// This message contains a public key that the initiator generates for the
+// current session. This message is signed by the long term symmetric key.
+// 2. Parse [Responder Auth] Message
+// The responder parses [Hello] and sends this message, which contains the
+// responder's session public key. This message also contains sufficient
+// information for the initiator to authenticate the responder.
+// 3. Send [Initiator Auth] Message
+// After receiving the responder's session public key, the initiator crafts
+// and sends this message so the responder can authenticate the initiator.
+class DeviceToDeviceInitiatorHelper {
+ public:
+ // Callback for operations that create a message. Invoked with the serialized
+ // SecureMessage upon success or the empty string upon failure.
+ typedef base::Callback<void(const std::string&)> MessageCallback;
+
+ // Callback for ValidateResponderAuthMessage. The first argument will be
+ // called with the validation outcome. If validation succeeded, then the
+ // second argument will contain the session symmetric key derived from the
+ // [Responder Auth] message.
+ typedef base::Callback<void(bool, const SessionKeys&)>
+ ValidateResponderAuthCallback;
+
+ DeviceToDeviceInitiatorHelper();
+ virtual ~DeviceToDeviceInitiatorHelper();
+
+ // Creates the [Hello] message, which is the first message that is sent:
+ // |session_public_key|: This session public key will be stored in plaintext
+ // (but signed) so the responder can parse it.
+ // |persistent_symmetric_key|: The long-term symmetric key that is shared by
+ // the initiator and responder.
+ // |secure_message_delegate|: Delegate for SecureMessage operations. This
+ // instance is not owned, and must live until after |callback| is invoked.
+ // |callback|: Invoked upon operation completion with the serialized message
+ // or an empty string.
+ void CreateHelloMessage(const std::string& session_public_key,
+ const std::string& persistent_symmetric_key,
+ SecureMessageDelegate* secure_message_delegate,
+ const MessageCallback& callback);
+
+ // Validates that the [Responder Auth] message, received from the responder,
+ // is properly signed and encrypted.
+ // |responder_auth_message|: The bytes of the [Responder Auth] message to
+ // validate.
+ // |persistent_responder_public_key|: The long-term public key possessed by
+ // the responder device.
+ // |persistent_symmetric_key|: The long-term symmetric key that is shared by
+ // the initiator and responder.
+ // |session_private_key|: The session private key is used in an Diffie-Helmann
+ // key exchange once the responder public key is extracted. The derived
+ // session symmetric key is used in the validation process.
+ // |hello_message|: The initial [Hello] message that was sent, which is used
+ // in the signature calculation.
+ // |secure_message_delegate|: Delegate for SecureMessage operations. This
+ // instance is not owned, and must live until after |callback| is invoked.
+ // |callback|: Invoked upon operation completion with whether
+ // |responder_auth_message| is validated successfully.
+ void ValidateResponderAuthMessage(
+ const std::string& responder_auth_message,
+ const std::string& persistent_responder_public_key,
+ const std::string& persistent_symmetric_key,
+ const std::string& session_private_key,
+ const std::string& hello_message,
+ SecureMessageDelegate* secure_message_delegate,
+ const ValidateResponderAuthCallback& callback);
+
+ // Creates the [Initiator Auth] message, which allows the responder to
+ // authenticate the initiator:
+ // |session_keys|: The session symmetric keys.
+ // |persistent_symmetric_key|: The long-term symmetric key that is shared by
+ // the initiator and responder.
+ // |responder_auth_message|: The [Responder Auth] message sent previously to
+ // the responder. These bytes are used in the signature calculation.
+ // |secure_message_delegate|: Delegate for SecureMessage operations. This
+ // instance is not owned, and must live until after |callback| is invoked.
+ // |callback|: Invoked upon operation completion with the serialized message
+ // or an empty string.
+ void CreateInitiatorAuthMessage(
+ const SessionKeys& session_keys,
+ const std::string& persistent_symmetric_key,
+ const std::string& responder_auth_message,
+ SecureMessageDelegate* secure_message_delegate,
+ const MessageCallback& callback);
+
+ private:
+ // Helper struct containing all the context needed to validate the
+ // [Responder Auth] message.
+ struct ValidateResponderAuthMessageContext {
+ ValidateResponderAuthMessageContext(
+ const std::string& responder_auth_message,
+ const std::string& persistent_responder_public_key,
+ const std::string& persistent_symmetric_key,
+ const std::string& session_private_key,
+ const std::string& hello_message,
+ SecureMessageDelegate* secure_message_delegate,
+ const ValidateResponderAuthCallback& callback);
+ ValidateResponderAuthMessageContext(
+ const ValidateResponderAuthMessageContext& other);
+ ~ValidateResponderAuthMessageContext();
+
+ std::string responder_auth_message;
+ std::string persistent_responder_public_key;
+ std::string persistent_symmetric_key;
+ std::string session_private_key;
+ std::string hello_message;
+ SecureMessageDelegate* secure_message_delegate;
+ ValidateResponderAuthCallback callback;
+ std::string responder_session_public_key;
+ std::string session_symmetric_key;
+ };
+
+ // Begins the [Responder Auth] validation flow by validating the header.
+ void BeginResponderAuthValidation(
+ ValidateResponderAuthMessageContext context);
+
+ // Called after the session symmetric key is derived, so now we can unwrap the
+ // outer message of [Responder Auth].
+ void OnSessionSymmetricKeyDerived(ValidateResponderAuthMessageContext context,
+ const std::string& session_symmetric_key);
+
+ // Called after the outer-most layer of [Responder Auth] is unwrapped.
+ void OnOuterMessageUnwrappedForResponderAuth(
+ const ValidateResponderAuthMessageContext& context,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header);
+
+ // Called after the middle layer of [Responder Auth] is unwrapped.
+ void OnMiddleMessageUnwrappedForResponderAuth(
+ const ValidateResponderAuthMessageContext& context,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header);
+
+ // Called after inner message is created.
+ void OnInnerMessageCreatedForInitiatorAuth(
+ const SessionKeys& session_keys,
+ SecureMessageDelegate* secure_message_delegate,
+ const DeviceToDeviceInitiatorHelper::MessageCallback& callback,
+ const std::string& inner_message);
+
+ // Callback for CreateInitiatorAuthMessage(), after the inner message is
+ // created.
+ void OnInnerMessageUnwrappedForResponderAuth(
+ const ValidateResponderAuthMessageContext& context,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header);
+
+ base::WeakPtrFactory<DeviceToDeviceInitiatorHelper> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceToDeviceInitiatorHelper);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_DEVICE_TO_DEVICE_INITIATOR_HELPER_H_
diff --git a/chromium/components/cryptauth/device_to_device_initiator_operations.cc b/chromium/components/cryptauth/device_to_device_initiator_operations.cc
deleted file mode 100644
index 9df030f4888..00000000000
--- a/chromium/components/cryptauth/device_to_device_initiator_operations.cc
+++ /dev/null
@@ -1,336 +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/device_to_device_initiator_operations.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/memory/ptr_util.h"
-#include "components/cryptauth/proto/cryptauth_api.pb.h"
-#include "components/cryptauth/proto/securemessage.pb.h"
-#include "components/cryptauth/secure_message_delegate.h"
-#include "components/cryptauth/session_keys.h"
-#include "components/proximity_auth/logging/logging.h"
-
-namespace cryptauth {
-
-namespace {
-
-// TODO(tengs): Due to a bug with the ChromeOS secure message daemon, we cannot
-// create SecureMessages with empty payloads. To workaround this bug, this value
-// is put into the payload if it would otherwise be empty.
-// See crbug.com/512894.
-const char kPayloadFiller[] = "\xae";
-
-// The version to put in the GcmMetadata field.
-const int kGcmMetadataVersion = 1;
-
-// The D2D protocol version.
-const int kD2DProtocolVersion = 1;
-
-// Callback for DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage(),
-// after the inner message is created.
-void OnInnerMessageCreatedForInitiatorAuth(
- const SessionKeys& session_keys,
- SecureMessageDelegate* secure_message_delegate,
- const DeviceToDeviceInitiatorOperations::MessageCallback& callback,
- const std::string& inner_message) {
- if (inner_message.empty()) {
- PA_LOG(INFO) << "Failed to create inner message for [Initiator Auth].";
- callback.Run(std::string());
- return;
- }
-
- GcmMetadata gcm_metadata;
- gcm_metadata.set_type(DEVICE_TO_DEVICE_MESSAGE);
- gcm_metadata.set_version(kGcmMetadataVersion);
-
- // Store the inner message inside a DeviceToDeviceMessage proto.
- securemessage::DeviceToDeviceMessage device_to_device_message;
- device_to_device_message.set_message(inner_message);
- device_to_device_message.set_sequence_number(1);
-
- // Create and return the outer message, which wraps the inner message.
- SecureMessageDelegate::CreateOptions create_options;
- create_options.encryption_scheme = securemessage::AES_256_CBC;
- create_options.signature_scheme = securemessage::HMAC_SHA256;
- gcm_metadata.SerializeToString(&create_options.public_metadata);
- secure_message_delegate->CreateSecureMessage(
- device_to_device_message.SerializeAsString(),
- session_keys.initiator_encode_key(), create_options, callback);
-}
-
-// Helper struct containing all the context needed to validate the
-// [Responder Auth] message.
-struct ValidateResponderAuthMessageContext {
- std::string responder_auth_message;
- std::string persistent_responder_public_key;
- std::string persistent_symmetric_key;
- std::string session_private_key;
- std::string hello_message;
- SecureMessageDelegate* secure_message_delegate;
- DeviceToDeviceInitiatorOperations::ValidateResponderAuthCallback callback;
- std::string responder_session_public_key;
- std::string session_symmetric_key;
-};
-
-// Forward declarations of functions used in the [Responder Auth] validation
-// flow. These functions are declared in order in which they are called during
-// the flow.
-void BeginResponderAuthValidation(ValidateResponderAuthMessageContext context);
-void OnSessionSymmetricKeyDerived(ValidateResponderAuthMessageContext context,
- const std::string& session_symmetric_key);
-void OnOuterMessageUnwrappedForResponderAuth(
- const ValidateResponderAuthMessageContext& context,
- bool verified,
- const std::string& payload,
- const securemessage::Header& header);
-void OnMiddleMessageUnwrappedForResponderAuth(
- const ValidateResponderAuthMessageContext& context,
- bool verified,
- const std::string& payload,
- const securemessage::Header& header);
-void OnInnerMessageUnwrappedForResponderAuth(
- const ValidateResponderAuthMessageContext& context,
- bool verified,
- const std::string& payload,
- const securemessage::Header& header);
-
-// Begins the [Responder Auth] validation flow by validating the header.
-void BeginResponderAuthValidation(ValidateResponderAuthMessageContext context) {
- // Parse the encrypted SecureMessage so we can get plaintext data from the
- // header. Note that the payload will be encrypted.
- securemessage::SecureMessage encrypted_message;
- securemessage::HeaderAndBody header_and_body;
- if (!encrypted_message.ParseFromString(context.responder_auth_message) ||
- !header_and_body.ParseFromString(encrypted_message.header_and_body())) {
- PA_LOG(WARNING) << "Failed to parse [Responder Hello] message";
- context.callback.Run(false, SessionKeys());
- return;
- }
-
- // Check that header public_metadata contains the correct metadata fields.
- securemessage::Header header = header_and_body.header();
- GcmMetadata gcm_metadata;
- if (!gcm_metadata.ParseFromString(header.public_metadata()) ||
- gcm_metadata.type() !=
- DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD ||
- gcm_metadata.version() != kGcmMetadataVersion) {
- PA_LOG(WARNING) << "Failed to validate GcmMetadata in "
- << "[Responder Auth] header.";
- context.callback.Run(false, SessionKeys());
- return;
- }
-
- // Extract responder session public key from |decryption_key_id| field.
- securemessage::ResponderHello responder_hello;
- if (!responder_hello.ParseFromString(header.decryption_key_id()) ||
- !responder_hello.public_dh_key().SerializeToString(
- &context.responder_session_public_key)) {
- PA_LOG(INFO) << "Failed to extract responder session public key in "
- << "[Responder Auth] header.";
- context.callback.Run(false, SessionKeys());
- return;
- }
-
- // Perform a Diffie-Helmann key exchange to get the session symmetric key.
- context.secure_message_delegate->DeriveKey(
- context.session_private_key, context.responder_session_public_key,
- base::Bind(&OnSessionSymmetricKeyDerived, context));
-}
-
-// Called after the session symmetric key is derived, so now we can unwrap the
-// outer message of [Responder Auth].
-void OnSessionSymmetricKeyDerived(ValidateResponderAuthMessageContext context,
- const std::string& session_symmetric_key) {
- context.session_symmetric_key = session_symmetric_key;
-
- // Unwrap the outer message, using symmetric key encryption and signature.
- SecureMessageDelegate::UnwrapOptions unwrap_options;
- unwrap_options.encryption_scheme = securemessage::AES_256_CBC;
- unwrap_options.signature_scheme = securemessage::HMAC_SHA256;
- context.secure_message_delegate->UnwrapSecureMessage(
- context.responder_auth_message,
- SessionKeys(session_symmetric_key).responder_encode_key(), unwrap_options,
- base::Bind(&OnOuterMessageUnwrappedForResponderAuth, context));
-}
-
-// Called after the outer-most layer of [Responder Auth] is unwrapped.
-void OnOuterMessageUnwrappedForResponderAuth(
- const ValidateResponderAuthMessageContext& context,
- bool verified,
- const std::string& payload,
- const securemessage::Header& header) {
- if (!verified) {
- PA_LOG(INFO) << "Failed to unwrap outer [Responder Auth] message.";
- context.callback.Run(false, SessionKeys());
- return;
- }
-
- // Parse the decrypted payload.
- securemessage::DeviceToDeviceMessage device_to_device_message;
- if (!device_to_device_message.ParseFromString(payload) ||
- device_to_device_message.sequence_number() != 1) {
- PA_LOG(INFO) << "Failed to validate DeviceToDeviceMessage payload.";
- context.callback.Run(false, SessionKeys());
- return;
- }
-
- // Unwrap the middle level SecureMessage, using symmetric key encryption and
- // signature.
- SecureMessageDelegate::UnwrapOptions unwrap_options;
- unwrap_options.encryption_scheme = securemessage::AES_256_CBC;
- unwrap_options.signature_scheme = securemessage::HMAC_SHA256;
- unwrap_options.associated_data = context.hello_message;
- context.secure_message_delegate->UnwrapSecureMessage(
- device_to_device_message.message(), context.persistent_symmetric_key,
- unwrap_options,
- base::Bind(&OnMiddleMessageUnwrappedForResponderAuth, context));
-}
-
-// Called after the middle layer of [Responder Auth] is unwrapped.
-void OnMiddleMessageUnwrappedForResponderAuth(
- const ValidateResponderAuthMessageContext& context,
- bool verified,
- const std::string& payload,
- const securemessage::Header& header) {
- if (!verified) {
- PA_LOG(INFO) << "Failed to unwrap middle [Responder Auth] message.";
- context.callback.Run(false, SessionKeys());
- return;
- }
-
- // Unwrap the inner-most SecureMessage, using no encryption and an asymmetric
- // key signature.
- SecureMessageDelegate::UnwrapOptions unwrap_options;
- unwrap_options.encryption_scheme = securemessage::NONE;
- unwrap_options.signature_scheme = securemessage::ECDSA_P256_SHA256;
- unwrap_options.associated_data = context.hello_message;
- context.secure_message_delegate->UnwrapSecureMessage(
- payload, context.persistent_responder_public_key, unwrap_options,
- base::Bind(&OnInnerMessageUnwrappedForResponderAuth, context));
-}
-
-// Called after the inner-most layer of [Responder Auth] is unwrapped.
-void OnInnerMessageUnwrappedForResponderAuth(
- const ValidateResponderAuthMessageContext& context,
- bool verified,
- const std::string& payload,
- const securemessage::Header& header) {
- if (!verified)
- PA_LOG(INFO) << "Failed to unwrap inner [Responder Auth] message.";
-
- // Note: The GMS Core implementation does not properly set the metadata
- // version, so we only check that the type is UNLOCK_KEY_SIGNED_CHALLENGE.
- GcmMetadata gcm_metadata;
- if (!gcm_metadata.ParseFromString(header.public_metadata()) ||
- gcm_metadata.type() != UNLOCK_KEY_SIGNED_CHALLENGE) {
- PA_LOG(WARNING) << "Failed to validate GcmMetadata in inner-most "
- << "[Responder Auth] message.";
- context.callback.Run(false, SessionKeys());
- return;
- }
-
- context.callback.Run(verified, SessionKeys(context.session_symmetric_key));
-}
-
-} // namespace
-
-// static
-void DeviceToDeviceInitiatorOperations::CreateHelloMessage(
- const std::string& session_public_key,
- const std::string& persistent_symmetric_key,
- SecureMessageDelegate* secure_message_delegate,
- const MessageCallback& callback) {
- // Decode public key into the |initator_hello| proto.
- securemessage::InitiatorHello initiator_hello;
- if (!initiator_hello.mutable_public_dh_key()->ParseFromString(
- session_public_key)) {
- PA_LOG(ERROR) << "Unable to parse user's public key";
- callback.Run(std::string());
- return;
- }
- initiator_hello.set_protocol_version(kD2DProtocolVersion);
-
- // The [Hello] message has the structure:
- // {
- // header: <session_public_key>,
- // Sig(<session_public_key>, persistent_symmetric_key)
- // payload: ""
- // }
- SecureMessageDelegate::CreateOptions create_options;
- create_options.encryption_scheme = securemessage::NONE;
- create_options.signature_scheme = securemessage::HMAC_SHA256;
- initiator_hello.SerializeToString(&create_options.public_metadata);
- secure_message_delegate->CreateSecureMessage(
- kPayloadFiller, persistent_symmetric_key, create_options, callback);
-}
-
-// static
-void DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage(
- const std::string& responder_auth_message,
- const std::string& persistent_responder_public_key,
- const std::string& persistent_symmetric_key,
- const std::string& session_private_key,
- const std::string& hello_message,
- SecureMessageDelegate* secure_message_delegate,
- const ValidateResponderAuthCallback& callback) {
- // The [Responder Auth] message has the structure:
- // {
- // header: <responder_public_key>,
- // Sig(<responder_public_key> + payload1,
- // session_symmetric_key),
- // payload1: Enc({
- // header: Sig(payload2 + <hello_message>, persistent_symmetric_key),
- // payload2: {
- // sequence_number: 1,
- // body: Enc({
- // header: Sig(payload3 + <hello_message>,
- // persistent_responder_public_key),
- // payload3: ""
- // }, persistent_symmetric_key)
- // }
- // }, session_symmetric_key),
- // }
- struct ValidateResponderAuthMessageContext context = {
- responder_auth_message,
- persistent_responder_public_key,
- persistent_symmetric_key,
- session_private_key,
- hello_message,
- secure_message_delegate,
- callback};
- BeginResponderAuthValidation(context);
-}
-
-// static
-void DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage(
- const SessionKeys& session_keys,
- const std::string& persistent_symmetric_key,
- const std::string& responder_auth_message,
- SecureMessageDelegate* secure_message_delegate,
- const MessageCallback& callback) {
- // The [Initiator Auth] message has the structure:
- // {
- // header: Sig(payload1, session_symmetric_key)
- // payload1: Enc({
- // sequence_number: 2,
- // body: {
- // header: Sig(payload2 + responder_auth_message,
- // persistent_symmetric_key)
- // payload2: ""
- // }
- // }, session_symmetric_key)
- // }
- SecureMessageDelegate::CreateOptions create_options;
- create_options.encryption_scheme = securemessage::AES_256_CBC;
- create_options.signature_scheme = securemessage::HMAC_SHA256;
- create_options.associated_data = responder_auth_message;
- secure_message_delegate->CreateSecureMessage(
- kPayloadFiller, persistent_symmetric_key, create_options,
- base::Bind(&OnInnerMessageCreatedForInitiatorAuth, session_keys,
- secure_message_delegate, callback));
-}
-
-} // cryptauth
diff --git a/chromium/components/cryptauth/device_to_device_initiator_operations.h b/chromium/components/cryptauth/device_to_device_initiator_operations.h
deleted file mode 100644
index cdef8bc8040..00000000000
--- a/chromium/components/cryptauth/device_to_device_initiator_operations.h
+++ /dev/null
@@ -1,116 +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_CRYPTAUTH_DEVICE_TO_DEVICE_INITIATOR_OPERATIONS_H_
-#define COMPONENTS_CRYPTAUTH_DEVICE_TO_DEVICE_INITIATOR_OPERATIONS_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "components/cryptauth/session_keys.h"
-
-namespace cryptauth {
-
-class SecureMessageDelegate;
-
-// Utility class containing operations in the DeviceToDevice protocol that the
-// initiator needs to perform. For Smart Lock, in which a phone unlocks a
-// laptop, the initiator is laptop.
-//
-// All operations are asynchronous because we use the SecureMessageDelegate for
-// crypto operations, whose implementation may be asynchronous.
-//
-// In the DeviceToDevice protocol, the initiator needs to send two messages to
-// the responder and parse one message from the responder:
-// 1. Send [Hello] Message
-// This message contains a public key that the initiator generates for the
-// current session. This message is signed by the long term symmetric key.
-// 2. Parse [Responder Auth] Message
-// The responder parses [Hello] and sends this message, which contains the
-// responder's session public key. This message also contains sufficient
-// information for the initiator to authenticate the responder.
-// 3. Send [Initiator Auth] Message
-// After receiving the responder's session public key, the initiator crafts
-// and sends this message so the responder can authenticate the initiator.
-class DeviceToDeviceInitiatorOperations {
- public:
- // Callback for operations that create a message. Invoked with the serialized
- // SecureMessage upon success or the empty string upon failure.
- typedef base::Callback<void(const std::string&)> MessageCallback;
-
- // Callback for ValidateResponderAuthMessage. The first argument will be
- // called with the validation outcome. If validation succeeded, then the
- // second argument will contain the session symmetric key derived from the
- // [Responder Auth] message.
- typedef base::Callback<void(bool, const SessionKeys&)>
- ValidateResponderAuthCallback;
-
- // Creates the [Hello] message, which is the first message that is sent:
- // |session_public_key|: This session public key will be stored in plaintext
- // (but signed) so the responder can parse it.
- // |persistent_symmetric_key|: The long-term symmetric key that is shared by
- // the initiator and responder.
- // |secure_message_delegate|: Delegate for SecureMessage operations. This
- // instance is not owned, and must live until after |callback| is invoked.
- // |callback|: Invoked upon operation completion with the serialized message
- // or an empty string.
- static void CreateHelloMessage(
- const std::string& session_public_key,
- const std::string& persistent_symmetric_key,
- SecureMessageDelegate* secure_message_delegate,
- const MessageCallback& callback);
-
- // Validates that the [Responder Auth] message, received from the responder,
- // is properly signed and encrypted.
- // |responder_auth_message|: The bytes of the [Responder Auth] message to
- // validate.
- // |persistent_responder_public_key|: The long-term public key possessed by
- // the responder device.
- // |persistent_symmetric_key|: The long-term symmetric key that is shared by
- // the initiator and responder.
- // |session_private_key|: The session private key is used in an Diffie-Helmann
- // key exchange once the responder public key is extracted. The derived
- // session symmetric key is used in the validation process.
- // |hello_message|: The initial [Hello] message that was sent, which is used
- // in the signature calculation.
- // |secure_message_delegate|: Delegate for SecureMessage operations. This
- // instance is not owned, and must live until after |callback| is invoked.
- // |callback|: Invoked upon operation completion with whether
- // |responder_auth_message| is validated successfully.
- static void ValidateResponderAuthMessage(
- const std::string& responder_auth_message,
- const std::string& persistent_responder_public_key,
- const std::string& persistent_symmetric_key,
- const std::string& session_private_key,
- const std::string& hello_message,
- SecureMessageDelegate* secure_message_delegate,
- const ValidateResponderAuthCallback& callback);
-
- // Creates the [Initiator Auth] message, which allows the responder to
- // authenticate the initiator:
- // |session_keys|: The session symmetric keys.
- // |persistent_symmetric_key|: The long-term symmetric key that is shared by
- // the initiator and responder.
- // |responder_auth_message|: The [Responder Auth] message sent previously to
- // the responder. These bytes are used in the signature calculation.
- // |secure_message_delegate|: Delegate for SecureMessage operations. This
- // instance is not owned, and must live until after |callback| is invoked.
- // |callback|: Invoked upon operation completion with the serialized message
- // or an empty string.
- static void CreateInitiatorAuthMessage(
- const SessionKeys& session_keys,
- const std::string& persistent_symmetric_key,
- const std::string& responder_auth_message,
- SecureMessageDelegate* secure_message_delegate,
- const MessageCallback& callback);
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(DeviceToDeviceInitiatorOperations);
-};
-
-} // cryptauth
-
-#endif // COMPONENTS_CRYPTAUTH_DEVICE_TO_DEVICE_INITIATOR_OPERATIONS_H_
diff --git a/chromium/components/cryptauth/device_to_device_operations_unittest.cc b/chromium/components/cryptauth/device_to_device_operations_unittest.cc
index 8cffab2c410..31bba05a155 100644
--- a/chromium/components/cryptauth/device_to_device_operations_unittest.cc
+++ b/chromium/components/cryptauth/device_to_device_operations_unittest.cc
@@ -5,7 +5,8 @@
#include "base/base64url.h"
#include "base/bind.h"
#include "base/macros.h"
-#include "components/cryptauth/device_to_device_initiator_operations.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/device_to_device_initiator_helper.h"
#include "components/cryptauth/device_to_device_responder_operations.h"
#include "components/cryptauth/fake_secure_message_delegate.h"
#include "components/cryptauth/session_keys.h"
@@ -63,10 +64,10 @@ void SaveValidationResultWithSessionKeys(bool* out_success,
} // namespace
-class ProximityAuthDeviceToDeviceOperationsTest : public testing::Test {
+class CryptAuthDeviceToDeviceOperationsTest : public testing::Test {
protected:
- ProximityAuthDeviceToDeviceOperationsTest() {}
- ~ProximityAuthDeviceToDeviceOperationsTest() override {}
+ CryptAuthDeviceToDeviceOperationsTest() {}
+ ~CryptAuthDeviceToDeviceOperationsTest() override {}
void SetUp() override {
ASSERT_TRUE(
@@ -92,15 +93,17 @@ class ProximityAuthDeviceToDeviceOperationsTest : public testing::Test {
session_keys_ = SessionKeys(session_symmetric_key_);
persistent_symmetric_key_ = "persistent symmetric key";
+
+ helper_ = base::MakeUnique<DeviceToDeviceInitiatorHelper>();
}
// Creates the initator's [Hello] message.
std::string CreateHelloMessage() {
std::string hello_message;
- DeviceToDeviceInitiatorOperations::CreateHelloMessage(
- local_session_public_key_, persistent_symmetric_key_,
- &secure_message_delegate_,
- base::Bind(&SaveMessageResult, &hello_message));
+ helper_->CreateHelloMessage(local_session_public_key_,
+ persistent_symmetric_key_,
+ &secure_message_delegate_,
+ base::Bind(&SaveMessageResult, &hello_message));
EXPECT_FALSE(hello_message.empty());
return hello_message;
}
@@ -125,7 +128,7 @@ class ProximityAuthDeviceToDeviceOperationsTest : public testing::Test {
std::string CreateInitiatorAuthMessage(
const std::string& remote_auth_message) {
std::string local_auth_message;
- DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage(
+ helper_->CreateInitiatorAuthMessage(
session_keys_, persistent_symmetric_key_, remote_auth_message,
&secure_message_delegate_,
base::Bind(&SaveMessageResult, &local_auth_message));
@@ -143,11 +146,12 @@ class ProximityAuthDeviceToDeviceOperationsTest : public testing::Test {
std::string session_symmetric_key_;
SessionKeys session_keys_;
- DISALLOW_COPY_AND_ASSIGN(ProximityAuthDeviceToDeviceOperationsTest);
+ std::unique_ptr<DeviceToDeviceInitiatorHelper> helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthDeviceToDeviceOperationsTest);
};
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
- ValidateHelloMessage_Success) {
+TEST_F(CryptAuthDeviceToDeviceOperationsTest, ValidateHelloMessage_Success) {
bool validation_success = false;
std::string hello_public_key;
DeviceToDeviceResponderOperations::ValidateHelloMessage(
@@ -160,8 +164,7 @@ TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
EXPECT_EQ(local_session_public_key_, hello_public_key);
}
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
- ValidateHelloMessage_Failure) {
+TEST_F(CryptAuthDeviceToDeviceOperationsTest, ValidateHelloMessage_Failure) {
bool validation_success = true;
std::string hello_public_key = "non-empty string";
DeviceToDeviceResponderOperations::ValidateHelloMessage(
@@ -174,14 +177,14 @@ TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
EXPECT_TRUE(hello_public_key.empty());
}
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
+TEST_F(CryptAuthDeviceToDeviceOperationsTest,
ValidateResponderAuthMessage_Success) {
std::string hello_message = CreateHelloMessage();
std::string remote_auth_message = CreateResponderAuthMessage(hello_message);
bool validation_success = false;
SessionKeys session_keys;
- DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage(
+ helper_->ValidateResponderAuthMessage(
remote_auth_message, kResponderPersistentPublicKey,
persistent_symmetric_key_, local_session_private_key_, hello_message,
&secure_message_delegate_,
@@ -195,14 +198,14 @@ TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
session_keys.responder_encode_key());
}
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
+TEST_F(CryptAuthDeviceToDeviceOperationsTest,
ValidateResponderAuthMessage_InvalidHelloMessage) {
std::string hello_message = CreateHelloMessage();
std::string remote_auth_message = CreateResponderAuthMessage(hello_message);
bool validation_success = true;
SessionKeys session_keys("non empty");
- DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage(
+ helper_->ValidateResponderAuthMessage(
remote_auth_message, kResponderPersistentPublicKey,
persistent_symmetric_key_, local_session_private_key_,
"invalid hello message", &secure_message_delegate_,
@@ -214,14 +217,14 @@ TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
EXPECT_TRUE(session_keys.responder_encode_key().empty());
}
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
+TEST_F(CryptAuthDeviceToDeviceOperationsTest,
ValidateResponderAuthMessage_InvalidPSK) {
std::string hello_message = CreateHelloMessage();
std::string remote_auth_message = CreateResponderAuthMessage(hello_message);
bool validation_success = true;
SessionKeys session_keys("non empty");
- DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage(
+ helper_->ValidateResponderAuthMessage(
remote_auth_message, kResponderPersistentPublicKey,
"invalid persistent symmetric key", local_session_private_key_,
hello_message, &secure_message_delegate_,
@@ -233,7 +236,7 @@ TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
EXPECT_TRUE(session_keys.responder_encode_key().empty());
}
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
+TEST_F(CryptAuthDeviceToDeviceOperationsTest,
ValidateInitiatorAuthMessage_Success) {
std::string hello_message = CreateHelloMessage();
std::string remote_auth_message = CreateResponderAuthMessage(hello_message);
@@ -249,7 +252,7 @@ TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
EXPECT_TRUE(validation_success);
}
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
+TEST_F(CryptAuthDeviceToDeviceOperationsTest,
ValidateInitiatorAuthMessage_InvalidRemoteAuth) {
std::string hello_message = CreateHelloMessage();
std::string remote_auth_message = CreateResponderAuthMessage(hello_message);
@@ -265,7 +268,7 @@ TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
EXPECT_FALSE(validation_success);
}
-TEST_F(ProximityAuthDeviceToDeviceOperationsTest,
+TEST_F(CryptAuthDeviceToDeviceOperationsTest,
ValidateInitiatorAuthMessage_InvalidPSK) {
std::string hello_message = CreateHelloMessage();
std::string remote_auth_message = CreateResponderAuthMessage(hello_message);
diff --git a/chromium/components/cryptauth/fake_secure_channel.cc b/chromium/components/cryptauth/fake_secure_channel.cc
index 03956aadc48..77db2d948e4 100644
--- a/chromium/components/cryptauth/fake_secure_channel.cc
+++ b/chromium/components/cryptauth/fake_secure_channel.cc
@@ -38,11 +38,21 @@ void FakeSecureChannel::ReceiveMessage(const std::string& feature,
}
}
+void FakeSecureChannel::CompleteSendingMessage(int sequence_number) {
+ DCHECK(next_sequence_number_ > sequence_number);
+ // Copy to prevent channel from being removed during handler.
+ std::vector<Observer*> observers_copy = observers_;
+ for (auto* observer : observers_copy) {
+ observer->OnMessageSent(this, sequence_number);
+ }
+}
+
void FakeSecureChannel::Initialize() {}
-void FakeSecureChannel::SendMessage(const std::string& feature,
- const std::string& payload) {
+int FakeSecureChannel::SendMessage(const std::string& feature,
+ const std::string& payload) {
sent_messages_.push_back(SentMessage(feature, payload));
+ return next_sequence_number_++;
}
void FakeSecureChannel::Disconnect() {
diff --git a/chromium/components/cryptauth/fake_secure_channel.h b/chromium/components/cryptauth/fake_secure_channel.h
index 4a202d9a771..716b595e926 100644
--- a/chromium/components/cryptauth/fake_secure_channel.h
+++ b/chromium/components/cryptauth/fake_secure_channel.h
@@ -28,6 +28,7 @@ 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);
std::vector<Observer*> observers() { return observers_; }
@@ -35,13 +36,14 @@ class FakeSecureChannel : public SecureChannel {
// SecureChannel:
void Initialize() override;
- void SendMessage(const std::string& feature,
- const std::string& payload) override;
+ int SendMessage(const std::string& feature,
+ const std::string& payload) override;
void Disconnect() override;
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
private:
+ int next_sequence_number_ = 0;
std::vector<Observer*> observers_;
std::vector<SentMessage> sent_messages_;
diff --git a/chromium/components/cryptauth/foreground_eid_generator.cc b/chromium/components/cryptauth/foreground_eid_generator.cc
index 01e269e717c..5c2b742ff48 100644
--- a/chromium/components/cryptauth/foreground_eid_generator.cc
+++ b/chromium/components/cryptauth/foreground_eid_generator.cc
@@ -19,14 +19,12 @@
namespace cryptauth {
namespace {
-const int64_t kNoTimestamp = 0;
-const int64_t kMaxPositiveInt64TValue = 0x7FFFFFFF;
+constexpr int64_t kNoTimestamp = 0;
+constexpr int64_t kMaxPositiveInt64TValue = 0x7FFFFFFF;
+constexpr base::TimeDelta kEidPeriod = base::TimeDelta::FromHours(8);
+constexpr base::TimeDelta kBeginningOfEidPeriod = base::TimeDelta::FromHours(2);
} // namespace
-const int64_t ForegroundEidGenerator::kNumMsInEidPeriod =
- base::TimeDelta::FromHours(8).InMilliseconds();
-const int64_t ForegroundEidGenerator::kNumMsInBeginningOfEidPeriod =
- base::TimeDelta::FromHours(2).InMilliseconds();
const int8_t ForegroundEidGenerator::kBluetooth4Flag = 0x01;
ForegroundEidGenerator::EidData::EidData(
@@ -271,8 +269,9 @@ ForegroundEidGenerator::GetEidPeriodTimestamps(
// current EID period must be determined.
for (int64_t start_of_eid_period_ms = current_seed->start_time_millis();
start_of_eid_period_ms <= current_seed->end_time_millis();
- start_of_eid_period_ms += kNumMsInEidPeriod) {
- int64_t end_of_eid_period_ms = start_of_eid_period_ms + kNumMsInEidPeriod;
+ start_of_eid_period_ms += kEidPeriod.InMilliseconds()) {
+ int64_t end_of_eid_period_ms =
+ start_of_eid_period_ms + kEidPeriod.InMilliseconds();
if (start_of_eid_period_ms <= current_time_ms &&
current_time_ms < end_of_eid_period_ms) {
int64_t start_of_adjacent_period_ms;
@@ -282,12 +281,13 @@ ForegroundEidGenerator::GetEidPeriodTimestamps(
// If the current time is at the beginning of the period, the "adjacent
// period" is the period before this one.
start_of_adjacent_period_ms =
- start_of_eid_period_ms - kNumMsInEidPeriod;
+ start_of_eid_period_ms - kEidPeriod.InMilliseconds();
end_of_adjacent_period_ms = start_of_eid_period_ms;
} else {
// Otherwise, the "adjacent period" is the one after this one.
start_of_adjacent_period_ms = end_of_eid_period_ms;
- end_of_adjacent_period_ms = end_of_eid_period_ms + kNumMsInEidPeriod;
+ end_of_adjacent_period_ms =
+ end_of_eid_period_ms + kEidPeriod.InMilliseconds();
}
return base::WrapUnique(new EidPeriodTimestamps{
@@ -312,7 +312,7 @@ ForegroundEidGenerator::GetBeaconSeedForCurrentPeriod(
for (auto seed : scanning_device_beacon_seeds) {
int64_t seed_period_length_ms =
seed.end_time_millis() - seed.start_time_millis();
- if (seed_period_length_ms % kNumMsInEidPeriod != 0) {
+ if (seed_period_length_ms % kEidPeriod.InMilliseconds() != 0) {
PA_LOG(WARNING) << "Seed has period length which is not an multiple of "
<< "the rotation length.";
continue;
@@ -338,7 +338,7 @@ ForegroundEidGenerator::GetClosestPeriod(
for (auto seed : scanning_device_beacon_seeds) {
int64_t seed_period_length_ms =
seed.end_time_millis() - seed.start_time_millis();
- if (seed_period_length_ms % kNumMsInEidPeriod != 0) {
+ if (seed_period_length_ms % kEidPeriod.InMilliseconds() != 0) {
PA_LOG(WARNING) << "Seed has period length which is not an multiple of "
<< "the rotation length.";
continue;
@@ -353,7 +353,7 @@ ForegroundEidGenerator::GetClosestPeriod(
smallest_diff_so_far_ms = diff;
start_of_period_timestamp_ms = seed.start_time_millis();
end_of_period_timestamp_ms =
- seed.start_time_millis() + kNumMsInEidPeriod;
+ seed.start_time_millis() + kEidPeriod.InMilliseconds();
}
} else if (seed.end_time_millis() <= current_time_ms) {
int64_t diff = current_time_ms - seed.end_time_millis();
@@ -361,13 +361,13 @@ ForegroundEidGenerator::GetClosestPeriod(
smallest_diff_so_far_ms = diff;
end_of_period_timestamp_ms = seed.end_time_millis();
start_of_period_timestamp_ms =
- end_of_period_timestamp_ms - kNumMsInEidPeriod;
+ end_of_period_timestamp_ms - kEidPeriod.InMilliseconds();
}
}
}
if (smallest_diff_so_far_ms < kMaxPositiveInt64TValue &&
- smallest_diff_so_far_ms > kNumMsInEidPeriod) {
+ smallest_diff_so_far_ms > kEidPeriod.InMilliseconds()) {
return nullptr;
}
@@ -385,7 +385,7 @@ bool ForegroundEidGenerator::IsCurrentTimeAtStartOfEidPeriod(
DCHECK(current_timestamp_ms < end_of_period_timestamp_ms);
return current_timestamp_ms <
- start_of_period_timestamp_ms + kNumMsInBeginningOfEidPeriod;
+ start_of_period_timestamp_ms + kBeginningOfEidPeriod.InMilliseconds();
}
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/foreground_eid_generator.h b/chromium/components/cryptauth/foreground_eid_generator.h
index ec68de89920..8a7250cf3d5 100644
--- a/chromium/components/cryptauth/foreground_eid_generator.h
+++ b/chromium/components/cryptauth/foreground_eid_generator.h
@@ -104,9 +104,6 @@ class ForegroundEidGenerator {
int64_t adjacent_period_end_timestamp_ms;
};
- static const int64_t kNumMsInEidPeriod;
- static const int64_t kNumMsInBeginningOfEidPeriod;
-
ForegroundEidGenerator(std::unique_ptr<RawEidGenerator> raw_eid_generator,
std::unique_ptr<base::Clock> clock);
diff --git a/chromium/components/cryptauth/foreground_eid_generator_unittest.cc b/chromium/components/cryptauth/foreground_eid_generator_unittest.cc
index 97e90179685..a964f494349 100644
--- a/chromium/components/cryptauth/foreground_eid_generator_unittest.cc
+++ b/chromium/components/cryptauth/foreground_eid_generator_unittest.cc
@@ -27,13 +27,17 @@ using testing::SaveArg;
namespace cryptauth {
namespace {
-const int64_t kNumMsInEidPeriod =
- base::TimeDelta::FromHours(8).InMilliseconds();
-const int64_t kNumMsInBeginningOfEidPeriod =
- base::TimeDelta::FromHours(2).InMilliseconds();
+
+// These constants could be made as integer amounts of milliseconds by calling
+// .InMilliseconds(), but this would create a static initializer because there
+// is no constexpr implementation of TimeDelta::InMilliseconds() yet. Static
+// initializers are not a big problem in tests, but it is preferable to avoid
+// them here for consistensy with similar definitions going into release
+// binaries.
+constexpr base::TimeDelta kEidPeriod = base::TimeDelta::FromHours(8);
+constexpr base::TimeDelta kEidSeedPeriod = base::TimeDelta::FromDays(14);
+
const int32_t kNumBytesInEidValue = 2;
-const int64_t kNumMsInEidSeedPeriod =
- base::TimeDelta::FromDays(14).InMilliseconds();
// Midnight on 1/1/2020.
const int64_t kDefaultCurrentPeriodStart = 1577836800000L;
@@ -101,17 +105,20 @@ class CryptAuthForegroundEidGeneratorTest : public testing::Test {
protected:
CryptAuthForegroundEidGeneratorTest() {
scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
- kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ kFirstSeed,
+ kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds(),
kDefaultCurrentPeriodStart));
- scanning_device_beacon_seeds_.push_back(
- CreateBeaconSeed(kSecondSeed, kDefaultCurrentPeriodStart,
- kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod));
scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
- kThirdSeed, kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
- kDefaultCurrentPeriodStart + 2 * kNumMsInEidSeedPeriod));
+ kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds()));
+ scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
+ kThirdSeed,
+ kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds(),
+ kDefaultCurrentPeriodStart + 2 * kEidSeedPeriod.InMilliseconds()));
scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
- kFourthSeed, kDefaultCurrentPeriodStart + 2 * kNumMsInEidSeedPeriod,
- kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod));
+ kFourthSeed,
+ kDefaultCurrentPeriodStart + 2 * kEidSeedPeriod.InMilliseconds(),
+ kDefaultCurrentPeriodStart + 3 * kEidSeedPeriod.InMilliseconds()));
}
class TestRawEidGenerator : public RawEidGenerator {
@@ -162,25 +169,26 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
ForegroundEidGenerator::EidData::AdjacentDataType::PAST);
EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
- EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidPeriod.InMilliseconds(),
data->current_data.end_timestamp_ms);
EXPECT_EQ(
GenerateFakeEidData(kSecondSeed, kDefaultCurrentPeriodStart, nullptr),
data->current_data.data);
ASSERT_TRUE(data->adjacent_data);
- EXPECT_EQ(kDefaultCurrentPeriodStart - kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart - kEidPeriod.InMilliseconds(),
data->adjacent_data->start_timestamp_ms);
EXPECT_EQ(kDefaultCurrentPeriodStart, data->adjacent_data->end_timestamp_ms);
EXPECT_EQ(
GenerateFakeEidData(
- kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidPeriod, nullptr),
+ kFirstSeed, kDefaultCurrentPeriodStart - kEidPeriod.InMilliseconds(),
+ nullptr),
data->adjacent_data->data);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GenerateBackgroundScanFilter_StartOfPeriod_NoSeedBefore) {
- SetTestTime(kDefaultCurrentTime - kNumMsInEidSeedPeriod);
+ SetTestTime(kDefaultCurrentTime - kEidSeedPeriod.InMilliseconds());
std::unique_ptr<ForegroundEidGenerator::EidData> data =
eid_generator_->GenerateBackgroundScanFilter(
@@ -189,13 +197,14 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ(data->GetAdjacentDataType(),
ForegroundEidGenerator::EidData::AdjacentDataType::NONE);
- EXPECT_EQ(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds(),
data->current_data.start_timestamp_ms);
- EXPECT_EQ(
- kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod + kNumMsInEidPeriod,
- data->current_data.end_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds() +
+ kEidPeriod.InMilliseconds(),
+ data->current_data.end_timestamp_ms);
EXPECT_EQ(GenerateFakeEidData(
- kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ kFirstSeed,
+ kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds(),
nullptr),
data->current_data.data);
@@ -215,26 +224,27 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
ForegroundEidGenerator::EidData::AdjacentDataType::FUTURE);
EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
- EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidPeriod.InMilliseconds(),
data->current_data.end_timestamp_ms);
EXPECT_EQ(
GenerateFakeEidData(kSecondSeed, kDefaultCurrentPeriodStart, nullptr),
data->current_data.data);
ASSERT_TRUE(data->adjacent_data);
- EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidPeriod.InMilliseconds(),
data->adjacent_data->start_timestamp_ms);
- EXPECT_EQ(kDefaultCurrentPeriodStart + 2 * kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + 2 * kEidPeriod.InMilliseconds(),
data->adjacent_data->end_timestamp_ms);
EXPECT_EQ(
GenerateFakeEidData(
- kSecondSeed, kDefaultCurrentPeriodStart + kNumMsInEidPeriod, nullptr),
+ kSecondSeed, kDefaultCurrentPeriodStart + kEidPeriod.InMilliseconds(),
+ nullptr),
data->adjacent_data->data);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GenerateBackgroundScanFilter_EndOfPeriod) {
- SetTestTime(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod - 1);
+ SetTestTime(kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds() - 1);
std::unique_ptr<ForegroundEidGenerator::EidData> data =
eid_generator_->GenerateBackgroundScanFilter(
@@ -243,32 +253,35 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ(data->GetAdjacentDataType(),
ForegroundEidGenerator::EidData::AdjacentDataType::FUTURE);
- EXPECT_EQ(
- kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
- data->current_data.start_timestamp_ms);
- EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds() -
+ kEidPeriod.InMilliseconds(),
+ data->current_data.start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds(),
data->current_data.end_timestamp_ms);
EXPECT_EQ(GenerateFakeEidData(kSecondSeed,
kDefaultCurrentPeriodStart +
- kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
+ kEidSeedPeriod.InMilliseconds() -
+ kEidPeriod.InMilliseconds(),
nullptr),
data->current_data.data);
ASSERT_TRUE(data->adjacent_data);
- EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds(),
data->adjacent_data->start_timestamp_ms);
- EXPECT_EQ(
- kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod + kNumMsInEidPeriod,
- data->adjacent_data->end_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds() +
+ kEidPeriod.InMilliseconds(),
+ data->adjacent_data->end_timestamp_ms);
EXPECT_EQ(GenerateFakeEidData(
- kThirdSeed, kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+ kThirdSeed,
+ kDefaultCurrentPeriodStart + kEidSeedPeriod.InMilliseconds(),
nullptr),
data->adjacent_data->data);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GenerateBackgroundScanFilter_EndOfPeriod_NoSeedAfter) {
- SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod - 1);
+ SetTestTime(kDefaultCurrentPeriodStart + 3 * kEidSeedPeriod.InMilliseconds() -
+ 1);
std::unique_ptr<ForegroundEidGenerator::EidData> data =
eid_generator_->GenerateBackgroundScanFilter(
@@ -277,24 +290,25 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ(data->GetAdjacentDataType(),
ForegroundEidGenerator::EidData::AdjacentDataType::NONE);
- EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod -
- kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kEidSeedPeriod.InMilliseconds() -
+ kEidPeriod.InMilliseconds(),
data->current_data.start_timestamp_ms);
- EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kEidSeedPeriod.InMilliseconds(),
data->current_data.end_timestamp_ms);
- EXPECT_EQ(
- GenerateFakeEidData(kFourthSeed,
- kDefaultCurrentPeriodStart +
- 3 * kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
- nullptr),
- data->current_data.data);
+ EXPECT_EQ(GenerateFakeEidData(kFourthSeed,
+ kDefaultCurrentPeriodStart +
+ 3 * kEidSeedPeriod.InMilliseconds() -
+ kEidPeriod.InMilliseconds(),
+ nullptr),
+ data->current_data.data);
EXPECT_FALSE(data->adjacent_data);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GenerateBackgroundScanFilter_NoCurrentPeriodSeed) {
- SetTestTime(kDefaultCurrentPeriodStart + 4 * kNumMsInEidSeedPeriod - 1);
+ SetTestTime(kDefaultCurrentPeriodStart + 4 * kEidSeedPeriod.InMilliseconds() -
+ 1);
std::unique_ptr<ForegroundEidGenerator::EidData> data =
eid_generator_->GenerateBackgroundScanFilter(
@@ -341,14 +355,14 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
ForegroundEidGenerator::EidData::AdjacentDataType::PAST);
EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
- EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidPeriod.InMilliseconds(),
data->current_data.end_timestamp_ms);
// Since this uses the real RawEidGenerator, just make sure the data
// exists and has the proper length.
EXPECT_EQ((size_t)kNumBytesInEidValue, data->current_data.data.length());
ASSERT_TRUE(data->adjacent_data);
- EXPECT_EQ(kDefaultCurrentPeriodStart - kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart - kEidPeriod.InMilliseconds(),
data->adjacent_data->start_timestamp_ms);
EXPECT_EQ(kDefaultCurrentPeriodStart, data->adjacent_data->end_timestamp_ms);
// Since this uses the real RawEidGenerator, just make sure the data
@@ -365,7 +379,7 @@ TEST_F(CryptAuthForegroundEidGeneratorTest, GenerateAdvertisementData) {
ASSERT_TRUE(data);
EXPECT_EQ(kDefaultCurrentPeriodStart, data->start_timestamp_ms);
- EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kEidPeriod.InMilliseconds(),
data->end_timestamp_ms);
EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
kDefaultAdvertisingDevicePublicKey),
@@ -374,7 +388,7 @@ TEST_F(CryptAuthForegroundEidGeneratorTest, GenerateAdvertisementData) {
TEST_F(CryptAuthForegroundEidGeneratorTest,
GenerateAdvertisementData_NoSeedForPeriod) {
- SetTestTime(kDefaultCurrentTime + 4 * kNumMsInEidSeedPeriod);
+ SetTestTime(kDefaultCurrentTime + 4 * kEidSeedPeriod.InMilliseconds());
std::unique_ptr<DataWithTimestamp> data =
eid_generator_->GenerateAdvertisement(kDefaultAdvertisingDevicePublicKey,
@@ -384,7 +398,7 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
TEST_F(CryptAuthForegroundEidGeneratorTest,
GenerateAdvertisementData_EmptySeeds) {
- SetTestTime(kDefaultCurrentTime + 4 * kNumMsInEidSeedPeriod);
+ SetTestTime(kDefaultCurrentTime + 4 * kEidSeedPeriod.InMilliseconds());
std::vector<BeaconSeed> empty;
std::unique_ptr<DataWithTimestamp> data =
@@ -405,10 +419,11 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
kDefaultAdvertisingDevicePublicKey),
possible_advertisements[0]);
- EXPECT_EQ(GenerateFakeAdvertisement(
- kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidPeriod,
- kDefaultAdvertisingDevicePublicKey),
- possible_advertisements[1]);
+ EXPECT_EQ(
+ GenerateFakeAdvertisement(
+ kFirstSeed, kDefaultCurrentPeriodStart - kEidPeriod.InMilliseconds(),
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[1]);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
@@ -424,15 +439,16 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
kDefaultAdvertisingDevicePublicKey),
possible_advertisements[0]);
- EXPECT_EQ(GenerateFakeAdvertisement(
- kSecondSeed, kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
- kDefaultAdvertisingDevicePublicKey),
- possible_advertisements[1]);
+ EXPECT_EQ(
+ GenerateFakeAdvertisement(
+ kSecondSeed, kDefaultCurrentPeriodStart + kEidPeriod.InMilliseconds(),
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[1]);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GeneratePossibleAdvertisements_OnlyCurrentPeriod) {
- SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod);
+ SetTestTime(kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds());
std::vector<std::string> possible_advertisements =
eid_generator_->GeneratePossibleAdvertisements(
@@ -440,15 +456,16 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ((size_t)1, possible_advertisements.size());
EXPECT_EQ(GenerateFakeAdvertisement(
- kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ kFirstSeed,
+ kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds(),
kDefaultAdvertisingDevicePublicKey),
possible_advertisements[0]);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GeneratePossibleAdvertisements_OnlyFuturePeriod) {
- SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod -
- kNumMsInEidPeriod);
+ SetTestTime(kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds() -
+ kEidPeriod.InMilliseconds());
std::vector<std::string> possible_advertisements =
eid_generator_->GeneratePossibleAdvertisements(
@@ -456,15 +473,16 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ((size_t)1, possible_advertisements.size());
EXPECT_EQ(GenerateFakeAdvertisement(
- kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ kFirstSeed,
+ kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds(),
kDefaultAdvertisingDevicePublicKey),
possible_advertisements[0]);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInFuture) {
- SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod -
- kNumMsInEidPeriod - 1);
+ SetTestTime(kDefaultCurrentPeriodStart - kEidSeedPeriod.InMilliseconds() -
+ kEidPeriod.InMilliseconds() - 1);
std::vector<std::string> possible_advertisements =
eid_generator_->GeneratePossibleAdvertisements(
@@ -474,8 +492,8 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
TEST_F(CryptAuthForegroundEidGeneratorTest,
GeneratePossibleAdvertisements_OnlyPastPeriod) {
- SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod +
- kNumMsInEidPeriod);
+ SetTestTime(kDefaultCurrentPeriodStart + 3 * kEidSeedPeriod.InMilliseconds() +
+ kEidPeriod.InMilliseconds());
std::vector<std::string> possible_advertisements =
eid_generator_->GeneratePossibleAdvertisements(
@@ -484,16 +502,16 @@ TEST_F(CryptAuthForegroundEidGeneratorTest,
EXPECT_EQ((size_t)1, possible_advertisements.size());
EXPECT_EQ(GenerateFakeAdvertisement(kFourthSeed,
kDefaultCurrentPeriodStart +
- 3 * kNumMsInEidSeedPeriod -
- kNumMsInEidPeriod,
+ 3 * kEidSeedPeriod.InMilliseconds() -
+ kEidPeriod.InMilliseconds(),
kDefaultAdvertisingDevicePublicKey),
possible_advertisements[0]);
}
TEST_F(CryptAuthForegroundEidGeneratorTest,
GeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInPast) {
- SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod +
- kNumMsInEidPeriod + 1);
+ SetTestTime(kDefaultCurrentPeriodStart + 3 * kEidSeedPeriod.InMilliseconds() +
+ kEidPeriod.InMilliseconds() + 1);
std::vector<std::string> possible_advertisements =
eid_generator_->GeneratePossibleAdvertisements(
diff --git a/chromium/components/cryptauth/local_device_data_provider.cc b/chromium/components/cryptauth/local_device_data_provider.cc
new file mode 100644
index 00000000000..f98cad8160f
--- /dev/null
+++ b/chromium/components/cryptauth/local_device_data_provider.cc
@@ -0,0 +1,54 @@
+// 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/local_device_data_provider.h"
+
+#include "components/cryptauth/cryptauth_device_manager.h"
+#include "components/cryptauth/cryptauth_enrollment_manager.h"
+#include "components/cryptauth/cryptauth_service.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+
+namespace cryptauth {
+
+LocalDeviceDataProvider::LocalDeviceDataProvider(
+ CryptAuthService* cryptauth_service)
+ : cryptauth_service_(cryptauth_service) {}
+
+LocalDeviceDataProvider::~LocalDeviceDataProvider() {}
+
+bool LocalDeviceDataProvider::GetLocalDeviceData(
+ std::string* public_key_out,
+ std::vector<BeaconSeed>* beacon_seeds_out) const {
+ DCHECK(public_key_out || beacon_seeds_out);
+
+ std::string public_key =
+ cryptauth_service_->GetCryptAuthEnrollmentManager()->GetUserPublicKey();
+ if (public_key.empty()) {
+ return false;
+ }
+
+ std::vector<ExternalDeviceInfo> synced_devices =
+ cryptauth_service_->GetCryptAuthDeviceManager()->GetSyncedDevices();
+ for (const auto& device : synced_devices) {
+ if (device.has_public_key() && device.public_key() == public_key &&
+ device.beacon_seeds_size() > 0) {
+ if (public_key_out) {
+ public_key_out->assign(public_key);
+ }
+
+ if (beacon_seeds_out) {
+ beacon_seeds_out->clear();
+ for (int i = 0; i < device.beacon_seeds_size(); i++) {
+ beacon_seeds_out->push_back(device.beacon_seeds(i));
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/local_device_data_provider.h b/chromium/components/cryptauth/local_device_data_provider.h
new file mode 100644
index 00000000000..927179d0dca
--- /dev/null
+++ b/chromium/components/cryptauth/local_device_data_provider.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_LOCAL_DEVICE_DATA_PROVIDER_H_
+#define COMPONENTS_CRYPTAUTH_LOCAL_DEVICE_DATA_PROVIDER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+
+namespace cryptauth {
+
+class BeaconSeed;
+class CryptAuthService;
+
+// Fetches CryptAuth data about the local device (i.e., the device on which this
+// code is running) for the current user (i.e., the one which is logged-in).
+class LocalDeviceDataProvider {
+ public:
+ explicit LocalDeviceDataProvider(CryptAuthService* cryptauth_service);
+ virtual ~LocalDeviceDataProvider();
+
+ // Fetches the public key and/or the beacon seeds for the local device.
+ // Returns whether the operation succeeded. If |nullptr| is passed as a
+ // parameter, the associated data will not be fetched.
+ virtual bool GetLocalDeviceData(
+ std::string* public_key_out,
+ std::vector<BeaconSeed>* beacon_seeds_out) const;
+
+ private:
+ friend class LocalDeviceDataProviderTest;
+
+ CryptAuthService* cryptauth_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalDeviceDataProvider);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_LOCAL_DEVICE_DATA_PROVIDER_H_
diff --git a/chromium/components/cryptauth/local_device_data_provider_unittest.cc b/chromium/components/cryptauth/local_device_data_provider_unittest.cc
new file mode 100644
index 00000000000..9259c5d0c54
--- /dev/null
+++ b/chromium/components/cryptauth/local_device_data_provider_unittest.cc
@@ -0,0 +1,236 @@
+// 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/local_device_data_provider.h"
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/cryptauth_device_manager.h"
+#include "components/cryptauth/cryptauth_enroller.h"
+#include "components/cryptauth/cryptauth_enrollment_manager.h"
+#include "components/cryptauth/fake_cryptauth_gcm_manager.h"
+#include "components/cryptauth/fake_cryptauth_service.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/secure_message_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::NiceMock;
+using testing::Return;
+
+namespace cryptauth {
+
+namespace {
+
+const char kDefaultPublicKey[] = "publicKey";
+
+const char kBeaconSeed1Data[] = "beaconSeed1Data";
+const int64_t kBeaconSeed1StartMs = 1000L;
+const int64_t kBeaconSeed1EndMs = 2000L;
+
+const char kBeaconSeed2Data[] = "beaconSeed2Data";
+const int64_t kBeaconSeed2StartMs = 2000L;
+const int64_t kBeaconSeed2EndMs = 3000L;
+
+class MockCryptAuthDeviceManager : public CryptAuthDeviceManager {
+ public:
+ MockCryptAuthDeviceManager() {}
+ ~MockCryptAuthDeviceManager() override {}
+
+ MOCK_CONST_METHOD0(GetSyncedDevices, std::vector<ExternalDeviceInfo>());
+};
+
+class MockCryptAuthEnrollmentManager : public CryptAuthEnrollmentManager {
+ public:
+ explicit MockCryptAuthEnrollmentManager(
+ FakeCryptAuthGCMManager* fake_cryptauth_gcm_manager)
+ : CryptAuthEnrollmentManager(nullptr /* clock */,
+ nullptr /* enroller_factory */,
+ nullptr /* secure_message_delegate */,
+ GcmDeviceInfo(),
+ fake_cryptauth_gcm_manager,
+ nullptr /* pref_service */) {}
+ ~MockCryptAuthEnrollmentManager() override {}
+
+ MOCK_CONST_METHOD0(GetUserPublicKey, std::string());
+};
+
+BeaconSeed CreateBeaconSeed(const std::string& data,
+ int64_t start_ms,
+ int64_t end_ms) {
+ BeaconSeed seed;
+ seed.set_data(data);
+ seed.set_start_time_millis(start_ms);
+ seed.set_end_time_millis(end_ms);
+ return seed;
+}
+
+} // namespace
+
+class LocalDeviceDataProviderTest : public testing::Test {
+ protected:
+ LocalDeviceDataProviderTest() {
+ fake_beacon_seeds_.push_back(CreateBeaconSeed(
+ kBeaconSeed1Data, kBeaconSeed1StartMs, kBeaconSeed1EndMs));
+ fake_beacon_seeds_.push_back(CreateBeaconSeed(
+ kBeaconSeed2Data, kBeaconSeed2StartMs, kBeaconSeed2EndMs));
+
+ // Has no public key and no BeaconSeeds.
+ ExternalDeviceInfo synced_device1;
+ fake_synced_devices_.push_back(synced_device1);
+
+ // Has no public key and some BeaconSeeds.
+ ExternalDeviceInfo synced_device2;
+ synced_device2.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
+ kBeaconSeed1Data, kBeaconSeed1StartMs, kBeaconSeed1EndMs));
+ synced_device2.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
+ kBeaconSeed2Data, kBeaconSeed2StartMs, kBeaconSeed2EndMs));
+ fake_synced_devices_.push_back(synced_device2);
+
+ // Has another different public key and no BeaconSeeds.
+ ExternalDeviceInfo synced_device3;
+ synced_device3.set_public_key("anotherPublicKey");
+ fake_synced_devices_.push_back(synced_device3);
+
+ // Has different public key and BeaconSeeds.
+ ExternalDeviceInfo synced_device4;
+ synced_device4.set_public_key("otherPublicKey");
+ synced_device4.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
+ kBeaconSeed1Data, kBeaconSeed1StartMs, kBeaconSeed1EndMs));
+ synced_device4.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
+ kBeaconSeed2Data, kBeaconSeed2StartMs, kBeaconSeed2EndMs));
+ fake_synced_devices_.push_back(synced_device4);
+
+ // Has public key but no BeaconSeeds.
+ ExternalDeviceInfo synced_device5;
+ synced_device5.set_public_key(kDefaultPublicKey);
+ fake_synced_devices_.push_back(synced_device5);
+
+ // Has public key and BeaconSeeds.
+ ExternalDeviceInfo synced_device6;
+ synced_device6.set_public_key(kDefaultPublicKey);
+ synced_device6.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
+ kBeaconSeed1Data, kBeaconSeed1StartMs, kBeaconSeed1EndMs));
+ synced_device6.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
+ kBeaconSeed2Data, kBeaconSeed2StartMs, kBeaconSeed2EndMs));
+ fake_synced_devices_.push_back(synced_device6);
+ }
+
+ void SetUp() override {
+ mock_device_manager_ =
+ base::WrapUnique(new NiceMock<MockCryptAuthDeviceManager>());
+ fake_cryptauth_gcm_manager_ =
+ base::MakeUnique<FakeCryptAuthGCMManager>("registrationId");
+ mock_enrollment_manager_ =
+ base::WrapUnique(new NiceMock<MockCryptAuthEnrollmentManager>(
+ fake_cryptauth_gcm_manager_.get()));
+
+ fake_cryptauth_service_ = base::MakeUnique<FakeCryptAuthService>();
+ fake_cryptauth_service_->set_cryptauth_device_manager(
+ mock_device_manager_.get());
+ fake_cryptauth_service_->set_cryptauth_enrollment_manager(
+ mock_enrollment_manager_.get());
+
+ provider_ = base::WrapUnique(
+ new LocalDeviceDataProvider(fake_cryptauth_service_.get()));
+ }
+
+ std::vector<BeaconSeed> fake_beacon_seeds_;
+ std::vector<ExternalDeviceInfo> fake_synced_devices_;
+
+ std::unique_ptr<FakeCryptAuthGCMManager> fake_cryptauth_gcm_manager_;
+ std::unique_ptr<NiceMock<MockCryptAuthDeviceManager>> mock_device_manager_;
+ std::unique_ptr<NiceMock<MockCryptAuthEnrollmentManager>>
+ mock_enrollment_manager_;
+ std::unique_ptr<FakeCryptAuthService> fake_cryptauth_service_;
+
+ std::unique_ptr<LocalDeviceDataProvider> provider_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LocalDeviceDataProviderTest);
+};
+
+TEST_F(LocalDeviceDataProviderTest, TestGetLocalDeviceData_NoPublicKey) {
+ ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
+ .WillByDefault(Return(std::string()));
+ ON_CALL(*mock_device_manager_, GetSyncedDevices())
+ .WillByDefault(Return(fake_synced_devices_));
+
+ std::string public_key;
+ std::vector<BeaconSeed> beacon_seeds;
+
+ EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
+}
+
+TEST_F(LocalDeviceDataProviderTest, TestGetLocalDeviceData_NoSyncedDevices) {
+ ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
+ .WillByDefault(Return(kDefaultPublicKey));
+ ON_CALL(*mock_device_manager_, GetSyncedDevices())
+ .WillByDefault(Return(std::vector<ExternalDeviceInfo>()));
+
+ std::string public_key;
+ std::vector<BeaconSeed> beacon_seeds;
+
+ EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
+}
+
+TEST_F(LocalDeviceDataProviderTest,
+ TestGetLocalDeviceData_NoSyncedDeviceMatchingPublicKey) {
+ ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
+ .WillByDefault(Return(kDefaultPublicKey));
+ ON_CALL(*mock_device_manager_, GetSyncedDevices())
+ .WillByDefault(Return(std::vector<ExternalDeviceInfo>{
+ fake_synced_devices_[0], fake_synced_devices_[1],
+ fake_synced_devices_[2], fake_synced_devices_[3]}));
+
+ std::string public_key;
+ std::vector<BeaconSeed> beacon_seeds;
+
+ EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
+}
+
+TEST_F(LocalDeviceDataProviderTest,
+ TestGetLocalDeviceData_SyncedDeviceIncludesPublicKeyButNoBeaconSeeds) {
+ ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
+ .WillByDefault(Return(kDefaultPublicKey));
+ ON_CALL(*mock_device_manager_, GetSyncedDevices())
+ .WillByDefault(Return(std::vector<ExternalDeviceInfo>{
+ fake_synced_devices_[4],
+ }));
+
+ std::string public_key;
+ std::vector<BeaconSeed> beacon_seeds;
+
+ EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
+}
+
+TEST_F(LocalDeviceDataProviderTest, TestGetLocalDeviceData_Success) {
+ ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
+ .WillByDefault(Return(kDefaultPublicKey));
+ ON_CALL(*mock_device_manager_, GetSyncedDevices())
+ .WillByDefault(Return(fake_synced_devices_));
+
+ std::string public_key;
+ std::vector<BeaconSeed> beacon_seeds;
+
+ EXPECT_TRUE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
+
+ EXPECT_EQ(kDefaultPublicKey, public_key);
+
+ ASSERT_EQ(fake_beacon_seeds_.size(), beacon_seeds.size());
+ for (size_t i = 0; i < fake_beacon_seeds_.size(); i++) {
+ // Note: google::protobuf::util::MessageDifferencer can only be used to diff
+ // Message, but BeaconSeed derives from the incompatible MessageLite class.
+ BeaconSeed expected = fake_beacon_seeds_[i];
+ BeaconSeed actual = beacon_seeds[i];
+ EXPECT_EQ(expected.data(), actual.data());
+ EXPECT_EQ(expected.start_time_millis(), actual.start_time_millis());
+ EXPECT_EQ(expected.end_time_millis(), actual.end_time_millis());
+ }
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_cryptauth_client.h b/chromium/components/cryptauth/mock_cryptauth_client.h
index ddc40e90537..d4df464043f 100644
--- a/chromium/components/cryptauth/mock_cryptauth_client.h
+++ b/chromium/components/cryptauth/mock_cryptauth_client.h
@@ -19,18 +19,26 @@ class MockCryptAuthClient : public CryptAuthClient {
~MockCryptAuthClient() override;
// CryptAuthClient:
- MOCK_METHOD3(GetMyDevices,
+ MOCK_METHOD4(GetMyDevices,
void(const GetMyDevicesRequest& request,
const GetMyDevicesCallback& callback,
- const ErrorCallback& error_callback));
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation));
MOCK_METHOD3(FindEligibleUnlockDevices,
void(const FindEligibleUnlockDevicesRequest& request,
const FindEligibleUnlockDevicesCallback& callback,
const ErrorCallback& error_callback));
- MOCK_METHOD3(SendDeviceSyncTickle,
+ MOCK_METHOD3(FindEligibleForPromotion,
+ void(const FindEligibleForPromotionRequest& request,
+ const FindEligibleForPromotionCallback& callback,
+ const ErrorCallback& error_callback));
+ MOCK_METHOD4(SendDeviceSyncTickle,
void(const SendDeviceSyncTickleRequest& request,
const SendDeviceSyncTickleCallback& callback,
- const ErrorCallback& error_callback));
+ const ErrorCallback& error_callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation));
MOCK_METHOD3(ToggleEasyUnlock,
void(const ToggleEasyUnlockRequest& request,
const ToggleEasyUnlockCallback& callback,
diff --git a/chromium/components/cryptauth/mock_local_device_data_provider.cc b/chromium/components/cryptauth/mock_local_device_data_provider.cc
new file mode 100644
index 00000000000..be56d0b9ad4
--- /dev/null
+++ b/chromium/components/cryptauth/mock_local_device_data_provider.cc
@@ -0,0 +1,54 @@
+// 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/mock_local_device_data_provider.h"
+
+#include "components/cryptauth/cryptauth_device_manager.h"
+#include "components/cryptauth/cryptauth_enrollment_manager.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+
+namespace cryptauth {
+
+MockLocalDeviceDataProvider::MockLocalDeviceDataProvider()
+ : LocalDeviceDataProvider(nullptr /* cryptauth_service */) {}
+
+MockLocalDeviceDataProvider::~MockLocalDeviceDataProvider() {}
+
+void MockLocalDeviceDataProvider::SetPublicKey(
+ std::unique_ptr<std::string> public_key) {
+ if (public_key) {
+ public_key_ = std::move(public_key);
+ } else {
+ public_key_.reset();
+ }
+}
+
+void MockLocalDeviceDataProvider::SetBeaconSeeds(
+ std::unique_ptr<std::vector<BeaconSeed>> beacon_seeds) {
+ if (beacon_seeds) {
+ beacon_seeds_ = std::move(beacon_seeds);
+ } else {
+ beacon_seeds_.reset();
+ }
+}
+
+bool MockLocalDeviceDataProvider::GetLocalDeviceData(
+ std::string* public_key_out,
+ std::vector<BeaconSeed>* beacon_seeds_out) const {
+ bool success = false;
+
+ if (public_key_ && public_key_out) {
+ *public_key_out = *public_key_;
+ success = true;
+ }
+
+ if (beacon_seeds_ && beacon_seeds_out) {
+ *beacon_seeds_out = *beacon_seeds_;
+ success = true;
+ }
+
+ return success;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_local_device_data_provider.h b/chromium/components/cryptauth/mock_local_device_data_provider.h
new file mode 100644
index 00000000000..563e1e866b3
--- /dev/null
+++ b/chromium/components/cryptauth/mock_local_device_data_provider.h
@@ -0,0 +1,43 @@
+// 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_LOCAL_DEVICE_DATA_PROVIDER_H_
+#define COMPONENTS_CRYPTAUTH_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/local_device_data_provider.h"
+
+namespace cryptauth {
+
+class BeaconSeed;
+
+// Test double for LocalDeviceDataProvider.
+class MockLocalDeviceDataProvider : public LocalDeviceDataProvider {
+ public:
+ MockLocalDeviceDataProvider();
+ ~MockLocalDeviceDataProvider() override;
+
+ void SetPublicKey(std::unique_ptr<std::string> public_key);
+ void SetBeaconSeeds(std::unique_ptr<std::vector<BeaconSeed>> beacon_seeds);
+
+ // LocalDeviceDataProvider:
+ bool GetLocalDeviceData(
+ std::string* public_key_out,
+ std::vector<BeaconSeed>* beacon_seeds_out) const override;
+
+ private:
+ std::unique_ptr<std::string> public_key_;
+ std::unique_ptr<std::vector<BeaconSeed>> beacon_seeds_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockLocalDeviceDataProvider);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
diff --git a/chromium/components/cryptauth/proto/cryptauth_api.proto b/chromium/components/cryptauth/proto/cryptauth_api.proto
index 6e2be7e2956..5672f239674 100644
--- a/chromium/components/cryptauth/proto/cryptauth_api.proto
+++ b/chromium/components/cryptauth/proto/cryptauth_api.proto
@@ -81,6 +81,12 @@ message ExternalDeviceInfo {
// A list of seeds for EID BLE advertisements targeting this device.
repeated BeaconSeed beacon_seeds = 9;
+
+ // Whether this device is an ARC++ device enrolled via gmscore.
+ optional bool arc_plus_plus = 10 [default = false];
+
+ // Whether this is a "Pixel Experience" phone.
+ optional bool pixel_phone = 11 [default = false];
}
// Determine if the calling device is allowed to promote the SmartLock
diff --git a/chromium/components/cryptauth/remote_device.cc b/chromium/components/cryptauth/remote_device.cc
index e3d26d0b646..b9556956e57 100644
--- a/chromium/components/cryptauth/remote_device.cc
+++ b/chromium/components/cryptauth/remote_device.cc
@@ -45,8 +45,7 @@ RemoteDevice::RemoteDevice(const std::string& user_id,
public_key(public_key),
bluetooth_address(bluetooth_address),
persistent_symmetric_key(persistent_symmetric_key),
- sign_in_challenge(sign_in_challenge),
- are_beacon_seeds_loaded(false) {}
+ sign_in_challenge(sign_in_challenge) {}
RemoteDevice::RemoteDevice(const RemoteDevice& other) = default;
diff --git a/chromium/components/cryptauth/remote_device.h b/chromium/components/cryptauth/remote_device.h
index cd4085f8d21..7662b1a38c7 100644
--- a/chromium/components/cryptauth/remote_device.h
+++ b/chromium/components/cryptauth/remote_device.h
@@ -23,7 +23,7 @@ struct RemoteDevice {
// Note: To save space, the BeaconSeeds may not necessarily be included in
// this object.
- bool are_beacon_seeds_loaded;
+ bool are_beacon_seeds_loaded = false;
std::vector<BeaconSeed> beacon_seeds;
RemoteDevice();
diff --git a/chromium/components/cryptauth/secure_channel.cc b/chromium/components/cryptauth/secure_channel.cc
index 9048f4d4f8b..068bdf02014 100644
--- a/chromium/components/cryptauth/secure_channel.cc
+++ b/chromium/components/cryptauth/secure_channel.cc
@@ -56,11 +56,10 @@ std::string SecureChannel::StatusToString(const Status& status) {
}
}
-SecureChannel::PendingMessage::PendingMessage() {}
-
-SecureChannel::PendingMessage::PendingMessage(
- const std::string& feature, const std::string& payload)
- : feature(feature), payload(payload) {}
+SecureChannel::PendingMessage::PendingMessage(const std::string& feature,
+ const std::string& payload,
+ int sequence_number)
+ : feature(feature), payload(payload), sequence_number(sequence_number) {}
SecureChannel::PendingMessage::~PendingMessage() {}
@@ -70,11 +69,6 @@ SecureChannel::SecureChannel(std::unique_ptr<Connection> connection,
connection_(std::move(connection)),
cryptauth_service_(cryptauth_service),
weak_ptr_factory_(this) {
- DCHECK(connection_);
- DCHECK(!connection_->IsConnected());
- DCHECK(!connection_->remote_device().user_id.empty());
- DCHECK(cryptauth_service);
-
connection_->AddObserver(this);
}
@@ -88,22 +82,27 @@ void SecureChannel::Initialize() {
TransitionToStatus(Status::CONNECTING);
}
-void SecureChannel::SendMessage(
- const std::string& feature, const std::string& payload) {
+int SecureChannel::SendMessage(const std::string& feature,
+ const std::string& payload) {
DCHECK(status_ == Status::AUTHENTICATED);
- PA_LOG(INFO) << "Queuing new message to send: {"
- << "feature: \"" << feature << "\", "
- << "payload: \"" << payload << "\""
- << "}";
+ int sequence_number = next_sequence_number_;
+ next_sequence_number_++;
- queued_messages_.push_back(PendingMessage(feature, payload));
+ queued_messages_.emplace(
+ base::MakeUnique<PendingMessage>(feature, payload, sequence_number));
ProcessMessageQueue();
+
+ return sequence_number;
}
void SecureChannel::Disconnect() {
if (connection_->IsConnected()) {
+ // If |connection_| is active, calling Disconnect() will eventually cause
+ // its status to transition to DISCONNECTED, which will in turn cause this
+ // class to transition to DISCONNECTED.
connection_->Disconnect();
+ return;
}
TransitionToStatus(Status::DISCONNECTED);
@@ -148,6 +147,13 @@ void SecureChannel::OnMessageReceived(const Connection& connection,
return;
}
+ if (!secure_context_) {
+ PA_LOG(WARNING) << "Received unexpected message before authentication "
+ << "was complete. Feature: " << wire_message.feature()
+ << ", Payload: " << wire_message.payload();
+ return;
+ }
+
secure_context_->Decode(wire_message.payload(),
base::Bind(&SecureChannel::OnMessageDecoded,
weak_ptr_factory_.GetWeakPtr(),
@@ -157,11 +163,40 @@ void SecureChannel::OnMessageReceived(const Connection& connection,
void SecureChannel::OnSendCompleted(const cryptauth::Connection& connection,
const cryptauth::WireMessage& wire_message,
bool success) {
- DCHECK(pending_message_->feature == wire_message.feature());
+ if (wire_message.feature() == Authenticator::kAuthenticationFeature) {
+ // No need to process authentication messages; these are handled by
+ // |authenticator_|.
+ return;
+ }
+
+ if (!pending_message_) {
+ PA_LOG(ERROR) << "OnSendCompleted(), but a send was not expected to be in "
+ << "progress. Disconnecting from "
+ << connection_->GetDeviceAddress();
+ Disconnect();
+ return;
+ }
if (success && status_ != Status::DISCONNECTED) {
pending_message_.reset();
- ProcessMessageQueue();
+
+ // Create a WeakPtr to |this| before invoking observer callbacks. It is
+ // possible that an Observer will respond to the OnMessageSent() call by
+ // destroying the connection (e.g., if the client only wanted to send one
+ // message and destroyed the connection after the message was sent).
+ base::WeakPtr<SecureChannel> weak_this = weak_ptr_factory_.GetWeakPtr();
+
+ if (wire_message.sequence_number() != -1) {
+ for (auto& observer : observer_list_)
+ observer.OnMessageSent(this, wire_message.sequence_number());
+ }
+
+ // Process the next message if possible. Note that if the SecureChannel was
+ // deleted by the OnMessageSent() callback, this will be a no-op since
+ // |weak_this| will have been invalidated in that case.
+ if (weak_this.get())
+ weak_this->ProcessMessageQueue();
+
return;
}
@@ -183,16 +218,11 @@ void SecureChannel::TransitionToStatus(const Status& new_status) {
return;
}
- PA_LOG(INFO) << "Connection status changed: "
- << StatusToString(status_)
- << " => " << StatusToString(new_status);
-
Status old_status = status_;
status_ = new_status;
- for (auto& observer : observer_list_) {
+ for (auto& observer : observer_list_)
observer.OnSecureChannelStatusChanged(this, old_status, status_);
- }
}
void SecureChannel::Authenticate() {
@@ -216,36 +246,39 @@ void SecureChannel::ProcessMessageQueue() {
DCHECK(!connection_->is_sending_message());
- pending_message_.reset(new PendingMessage(queued_messages_.front()));
- queued_messages_.pop_front();
+ pending_message_ = std::move(queued_messages_.front());
+ queued_messages_.pop();
- PA_LOG(INFO) << "Sending message: {"
+ PA_LOG(INFO) << "Sending message to " << connection_->GetDeviceAddress()
+ << ": {"
<< "feature: \"" << pending_message_->feature << "\", "
<< "payload: \"" << pending_message_->payload << "\""
<< "}";
- secure_context_->Encode(pending_message_->payload,
- base::Bind(&SecureChannel::OnMessageEncoded,
- weak_ptr_factory_.GetWeakPtr(),
- pending_message_->feature));
+ secure_context_->Encode(
+ pending_message_->payload,
+ base::Bind(&SecureChannel::OnMessageEncoded,
+ weak_ptr_factory_.GetWeakPtr(), pending_message_->feature,
+ pending_message_->sequence_number));
}
-void SecureChannel::OnMessageEncoded(
- const std::string& feature, const std::string& encoded_message) {
+void SecureChannel::OnMessageEncoded(const std::string& feature,
+ int sequence_number,
+ const std::string& encoded_message) {
connection_->SendMessage(base::MakeUnique<cryptauth::WireMessage>(
- encoded_message, feature));
+ encoded_message, feature, sequence_number));
}
void SecureChannel::OnMessageDecoded(
const std::string& feature, const std::string& decoded_message) {
- PA_LOG(INFO) << "Received message: {"
+ PA_LOG(INFO) << "Received message from " << connection_->GetDeviceAddress()
+ << ": {"
<< "feature: \"" << feature << "\", "
<< "payload: \"" << decoded_message << "\""
<< "}";
- for (auto& observer : observer_list_) {
+ for (auto& observer : observer_list_)
observer.OnMessageReceived(this, feature, decoded_message);
- }
}
void SecureChannel::OnAuthenticationResult(
diff --git a/chromium/components/cryptauth/secure_channel.h b/chromium/components/cryptauth/secure_channel.h
index e0d83b2f853..289500bd75c 100644
--- a/chromium/components/cryptauth/secure_channel.h
+++ b/chromium/components/cryptauth/secure_channel.h
@@ -5,7 +5,7 @@
#ifndef COMPONENTS_CRYPTAUTH_SECURE_CHANNEL_H_
#define COMPONENTS_CRYPTAUTH_SECURE_CHANNEL_H_
-#include <deque>
+#include <queue>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
@@ -59,6 +59,11 @@ class SecureChannel : public ConnectionObserver {
SecureChannel* secure_channel,
const std::string& feature,
const std::string& payload) = 0;
+
+ // 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) {}
};
class Factory {
@@ -82,8 +87,11 @@ class SecureChannel : public ConnectionObserver {
virtual void Initialize();
- virtual void SendMessage(const std::string& feature,
- const std::string& payload);
+ // Sends a message over the connection and returns a sequence number. If the
+ // message is successfully sent, observers will be notified that the message
+ // has been sent and will be provided this sequence number.
+ virtual int SendMessage(const std::string& feature,
+ const std::string& payload);
virtual void Disconnect();
@@ -111,23 +119,28 @@ class SecureChannel : public ConnectionObserver {
Status status_;
private:
+ friend class CryptAuthSecureChannelTest;
+
// Message waiting to be sent. Note that this is *not* the message that will
// end up being sent over the wire; before that can be done, the payload must
// be encrypted.
struct PendingMessage {
- PendingMessage();
- PendingMessage(const std::string& feature, const std::string& payload);
+ PendingMessage(const std::string& feature,
+ const std::string& payload,
+ int sequence_number);
virtual ~PendingMessage();
const std::string feature;
const std::string payload;
+ const int sequence_number;
};
void TransitionToStatus(const Status& new_status);
void Authenticate();
void ProcessMessageQueue();
- void OnMessageEncoded(
- const std::string& feature, const std::string& encoded_message);
+ void OnMessageEncoded(const std::string& feature,
+ int sequence_number,
+ const std::string& encoded_message);
void OnMessageDecoded(
const std::string& feature, const std::string& decoded_message);
void OnAuthenticationResult(
@@ -138,8 +151,9 @@ class SecureChannel : public ConnectionObserver {
CryptAuthService* cryptauth_service_; // Outlives this instance.
std::unique_ptr<Authenticator> authenticator_;
std::unique_ptr<SecureContext> secure_context_;
- std::deque<PendingMessage> queued_messages_;
+ std::queue<std::unique_ptr<PendingMessage>> queued_messages_;
std::unique_ptr<PendingMessage> pending_message_;
+ int next_sequence_number_ = 0;
base::ObserverList<Observer> observer_list_;
base::WeakPtrFactory<SecureChannel> weak_ptr_factory_;
diff --git a/chromium/components/cryptauth/secure_channel_unittest.cc b/chromium/components/cryptauth/secure_channel_unittest.cc
index f894110238c..97156a48374 100644
--- a/chromium/components/cryptauth/secure_channel_unittest.cc
+++ b/chromium/components/cryptauth/secure_channel_unittest.cc
@@ -64,18 +64,57 @@ class TestObserver : public SecureChannel::Observer {
received_messages_.push_back(ReceivedMessage(feature, payload));
}
- std::vector<SecureChannelStatusChange>& connection_status_changes() {
+ void OnMessageSent(SecureChannel* secure_channel,
+ int sequence_number) override {
+ DCHECK(secure_channel == secure_channel_);
+ sent_sequence_numbers_.push_back(sequence_number);
+ }
+
+ const std::vector<SecureChannelStatusChange>& connection_status_changes() {
return connection_status_changes_;
}
- std::vector<ReceivedMessage>& received_messages() {
+ const std::vector<ReceivedMessage>& received_messages() {
return received_messages_;
}
+ const std::vector<int>& sent_sequence_numbers() {
+ return sent_sequence_numbers_;
+ }
+
private:
SecureChannel* secure_channel_;
std::vector<SecureChannelStatusChange> connection_status_changes_;
std::vector<ReceivedMessage> received_messages_;
+ std::vector<int> sent_sequence_numbers_;
+};
+
+// Observer used in the ObserverDeletesChannel test. This Observer deletes the
+// SecureChannel when it receives an OnMessageSent() call.
+class DeletingObserver : public SecureChannel::Observer {
+ public:
+ DeletingObserver(std::unique_ptr<SecureChannel>* secure_channel)
+ : secure_channel_(secure_channel) {}
+
+ // SecureChannel::Observer:
+ void OnSecureChannelStatusChanged(
+ SecureChannel* secure_channel,
+ const SecureChannel::Status& old_status,
+ const SecureChannel::Status& new_status) override {}
+
+ void OnMessageReceived(SecureChannel* secure_channel,
+ const std::string& feature,
+ const std::string& payload) override {}
+
+ void OnMessageSent(SecureChannel* secure_channel,
+ int sequence_number) override {
+ DCHECK(secure_channel == secure_channel_->get());
+ // Delete the channel when an OnMessageSent() call occurs.
+ secure_channel_->reset();
+ }
+
+ private:
+ std::unique_ptr<SecureChannel>* secure_channel_;
};
class TestAuthenticatorFactory : public DeviceToDeviceAuthenticator::Factory {
@@ -105,13 +144,6 @@ RemoteDevice CreateTestRemoteDevice() {
return remote_device;
}
-class TestSecureChannel : public SecureChannel {
- public:
- TestSecureChannel(std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service)
- : SecureChannel(std::move(connection), cryptauth_service) {}
-};
-
} // namespace
class CryptAuthSecureChannelTest : public testing::Test {
@@ -133,8 +165,8 @@ class CryptAuthSecureChannelTest : public testing::Test {
new FakeConnection(test_device_, /* should_auto_connect */ false);
EXPECT_FALSE(fake_connection_->observers().size());
- secure_channel_ = base::MakeUnique<TestSecureChannel>(
- base::WrapUnique(fake_connection_), fake_cryptauth_service_.get());
+ secure_channel_ = base::WrapUnique(new SecureChannel(
+ base::WrapUnique(fake_connection_), fake_cryptauth_service_.get()));
EXPECT_EQ(static_cast<size_t>(1), fake_connection_->observers().size());
EXPECT_EQ(secure_channel_.get(), fake_connection_->observers()[0]);
@@ -151,7 +183,8 @@ class CryptAuthSecureChannelTest : public testing::Test {
VerifyReceivedMessages(std::vector<ReceivedMessage>());
// Same with messages being sent.
- VerifyNoMessageBeingSent();
+ if (secure_channel_)
+ VerifyNoMessageBeingSent();
}
void VerifyConnectionStateChanges(
@@ -243,16 +276,33 @@ class CryptAuthSecureChannelTest : public testing::Test {
});
}
- void StartSendingMessage(
- const std::string& feature, const std::string& payload) {
- secure_channel_->SendMessage(feature, payload);
+ // Starts sending the message and returns the sequence number.
+ int StartSendingMessage(const std::string& feature,
+ const std::string& payload) {
+ int sequence_number = secure_channel_->SendMessage(feature, payload);
VerifyMessageBeingSent(feature, payload);
+ return sequence_number;
+ }
+
+ void FinishSendingMessage(int sequence_number, bool success) {
+ std::vector<int> sent_sequence_numbers_before_send =
+ test_observer_->sent_sequence_numbers();
+
+ fake_connection_->FinishSendingMessageWithSuccess(success);
+
+ if (success) {
+ std::vector<int> sent_sequence_numbers_after_send =
+ test_observer_->sent_sequence_numbers();
+ EXPECT_EQ(sent_sequence_numbers_before_send.size() + 1u,
+ sent_sequence_numbers_after_send.size());
+ EXPECT_EQ(sequence_number, sent_sequence_numbers_after_send.back());
+ }
}
void StartAndFinishSendingMessage(
const std::string& feature, const std::string& payload, bool success) {
- StartSendingMessage(feature, payload);
- fake_connection_->FinishSendingMessageWithSuccess(success);
+ int sequence_number = StartSendingMessage(feature, payload);
+ FinishSendingMessage(sequence_number, success);
}
void VerifyNoMessageBeingSent() {
@@ -402,9 +452,33 @@ TEST_F(CryptAuthSecureChannelTest, AuthenticationFails_Failure) {
});
}
+// Regression test for crbug.com/765810. This test ensures that a crash does not
+// occur if an unexpected message is received before authentication is complete.
+TEST_F(CryptAuthSecureChannelTest, ReceiveMessageBeforeAuth) {
+ secure_channel_->Initialize();
+ VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+ {SecureChannel::Status::DISCONNECTED,
+ SecureChannel::Status::CONNECTING}});
+
+ fake_connection_->CompleteInProgressConnection(/* success */ true);
+ VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+ {SecureChannel::Status::CONNECTING, SecureChannel::Status::CONNECTED},
+ {SecureChannel::Status::CONNECTED,
+ SecureChannel::Status::AUTHENTICATING}});
+
+ // Receive an unexpected message (i.e., a non-auth message).
+ fake_connection_->ReceiveMessage("feature", "payload, but encoded");
+
+ // Still should be able to finish authentication.
+ AuthenticateSuccessfully();
+ VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+ {SecureChannel::Status::AUTHENTICATING,
+ SecureChannel::Status::AUTHENTICATED}});
+}
+
TEST_F(CryptAuthSecureChannelTest, SendMessage_DisconnectWhileSending) {
ConnectAndAuthenticate();
- StartSendingMessage("feature", "payload");
+ int sequence_number = StartSendingMessage("feature", "payload");
fake_connection_->Disconnect();
VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange> {
@@ -414,7 +488,7 @@ TEST_F(CryptAuthSecureChannelTest, SendMessage_DisconnectWhileSending) {
}
});
- fake_connection_->FinishSendingMessageWithSuccess(false);
+ FinishSendingMessage(sequence_number, false);
// No further state change should have occurred.
VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>());
}
@@ -461,32 +535,32 @@ TEST_F(CryptAuthSecureChannelTest, SendMessage_MultipleMessages_Success) {
ConnectAndAuthenticate();
// Send a second message before the first has completed.
- secure_channel_->SendMessage("feature1", "payload1");
- secure_channel_->SendMessage("feature2", "payload2");
+ int sequence_number1 = secure_channel_->SendMessage("feature1", "payload1");
+ int sequence_number2 = secure_channel_->SendMessage("feature2", "payload2");
// The first message should still be sending.
VerifyMessageBeingSent("feature1", "payload1");
// Send the first message.
- fake_connection_->FinishSendingMessageWithSuccess(true);
+ FinishSendingMessage(sequence_number1, true);
// Now, the second message should be sending.
VerifyMessageBeingSent("feature2", "payload2");
- fake_connection_->FinishSendingMessageWithSuccess(true);
+ FinishSendingMessage(sequence_number2, true);
}
TEST_F(CryptAuthSecureChannelTest, SendMessage_MultipleMessages_FirstFails) {
ConnectAndAuthenticate();
// Send a second message before the first has completed.
- secure_channel_->SendMessage("feature1", "payload1");
+ int sequence_number1 = secure_channel_->SendMessage("feature1", "payload1");
secure_channel_->SendMessage("feature2", "payload2");
// The first message should still be sending.
VerifyMessageBeingSent("feature1", "payload1");
// Fail sending the first message.
- fake_connection_->FinishSendingMessageWithSuccess(false);
+ FinishSendingMessage(sequence_number1, false);
// The connection should have become disconnected.
VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange> {
@@ -531,4 +605,20 @@ TEST_F(CryptAuthSecureChannelTest, SendAndReceiveMessages) {
});
}
+TEST_F(CryptAuthSecureChannelTest, ObserverDeletesChannel) {
+ // Add a special Observer which deletes |secure_channel_| once it receives an
+ // OnMessageSent() call.
+ std::unique_ptr<DeletingObserver> deleting_observer =
+ base::WrapUnique(new DeletingObserver(&secure_channel_));
+ secure_channel_->AddObserver(deleting_observer.get());
+
+ ConnectAndAuthenticate();
+
+ // Send a message successfully; this triggers an OnMessageSent() call which
+ // deletes the channel. Note that this would have caused a crash before the
+ // fix for crbug.com/751884.
+ StartAndFinishSendingMessage("feature", "request1", /* success */ true);
+ EXPECT_FALSE(secure_channel_);
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/wire_message.cc b/chromium/components/cryptauth/wire_message.cc
index 0e29872e58d..ae52678dcc8 100644
--- a/chromium/components/cryptauth/wire_message.cc
+++ b/chromium/components/cryptauth/wire_message.cc
@@ -181,9 +181,20 @@ std::string WireMessage::Serialize() const {
return header_string + json_body;
}
+WireMessage::WireMessage(const std::string& payload,
+ const std::string& feature,
+ int sequence_number)
+ : payload_(payload), feature_(feature), sequence_number_(sequence_number) {}
+
WireMessage::WireMessage(const std::string& payload, const std::string& feature)
: payload_(payload), feature_(feature) {}
WireMessage::WireMessage(const std::string& body) : body_(body) {}
+WireMessage::WireMessage(const WireMessage& other)
+ : payload_(other.payload_),
+ feature_(other.feature_),
+ body_(other.body_),
+ sequence_number_(other.sequence_number_) {}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/wire_message.h b/chromium/components/cryptauth/wire_message.h
index 5aed96d970c..d0fe80ca69d 100644
--- a/chromium/components/cryptauth/wire_message.h
+++ b/chromium/components/cryptauth/wire_message.h
@@ -14,8 +14,16 @@ namespace cryptauth {
class WireMessage {
public:
+ // Creates a WireMessage containing |payload| for feature |feature| and
+ // sequence number |sequence_number|.
+ WireMessage(const std::string& payload,
+ const std::string& feature,
+ int sequence_number);
+
// Creates a WireMessage containing |payload| for feature |feature|.
- explicit WireMessage(const std::string& payload, const std::string& feature);
+ WireMessage(const std::string& payload, const std::string& feature);
+
+ WireMessage(const WireMessage& other);
// Creates a WireMessage containing |body| (a serialized JSON) as the message
// body.
@@ -38,6 +46,8 @@ class WireMessage {
const std::string& payload() const { return payload_; }
const std::string& feature() const { return feature_; }
const std::string& body() const { return body_; }
+ // Will return -1 if no sequence number was passed to the constructor.
+ int sequence_number() const { return sequence_number_; }
private:
// The message payload.
@@ -51,7 +61,8 @@ class WireMessage {
// vice-versa.
const std::string body_;
- DISALLOW_COPY_AND_ASSIGN(WireMessage);
+ // Sequence number for this message; set to -1 if no sequence number if set.
+ const int sequence_number_ = -1;
};
} // namespace cryptauth
diff --git a/chromium/components/data_reduction_proxy/OWNERS b/chromium/components/data_reduction_proxy/OWNERS
index 340aa35a1f9..7d2916a76b2 100644
--- a/chromium/components/data_reduction_proxy/OWNERS
+++ b/chromium/components/data_reduction_proxy/OWNERS
@@ -1,5 +1,4 @@
bengr@chromium.org
-bolian@chromium.org
megjablon@chromium.org
rajendrant@chromium.org
ryansturm@chromium.org
@@ -11,3 +10,5 @@ per-file *_messages.cc=file://ipc/SECURITY_OWNERS
per-file *_messages*.h=set noparent
per-file *_messages*.h=file://ipc/SECURITY_OWNERS
+
+# COMPONENT: Internals>Network>DataProxy \ No newline at end of file
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 ac945240865..013475c4942 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
@@ -6,9 +6,11 @@
#include <string>
+#include "base/feature_list.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.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_params.h"
#include "content/public/browser/resource_request_info.h"
@@ -67,29 +69,48 @@ void ContentLoFiDecider::MaybeSetAcceptTransformHeader(
return;
}
- // The Lo-Fi and Lite Page directives should not be added for users in the
- // Lo-Fi field trial "Control" group.
- bool lofi_enabled_via_flags_or_field_trial =
- params::IsLoFiOnViaFlags() || params::IsIncludedInLoFiEnabledFieldTrial();
-
- bool lite_page_enabled_via_flags_or_field_trial =
- (params::IsLoFiOnViaFlags() && params::AreLitePagesEnabledViaFlags()) ||
- params::IsIncludedInLitePageFieldTrial();
-
- // User does not have previews enabled.
- if (!lofi_enabled_via_flags_or_field_trial &&
- !lite_page_enabled_via_flags_or_field_trial) {
- return;
- }
-
- // Previews has been disabled.
- if (are_previews_disabled)
- return;
+ content::PreviewsState previews_state = request_info->GetPreviewsState();
// Do not add the Chrome-Proxy-Accept-Transform header when the page load
// explicitly forbids previews transformations.
- if (request_info->GetPreviewsState() & content::PREVIEWS_NO_TRANSFORM)
+ if (previews_state & content::PREVIEWS_NO_TRANSFORM ||
+ previews_state & content::PREVIEWS_OFF) {
return;
+ }
+
+ // For the proxy-decides-transform feature, the header is set based only
+ // on the request's PreviewsState (no checking of previews flags).
+ // This is for a newer version of the proxy server protocol where the
+ // server makes the transform decision and client simply advertises
+ // if is accepts transforms for the resource type (determined here from
+ // previously set previews state bits). The previous logic in this method
+ // that checks various flags will be deprecated.
+ bool check_previews_flags = !base::FeatureList::IsEnabled(
+ features::kDataReductionProxyDecidesTransform);
+
+ bool lofi_enabled_via_flags_or_field_trial = false;
+ bool lite_page_enabled_via_flags_or_field_trial = false;
+ if (check_previews_flags) {
+ // The Lo-Fi and Lite Page directives should not be added for users in the
+ // Lo-Fi field trial "Control" group.
+ lofi_enabled_via_flags_or_field_trial =
+ params::IsLoFiOnViaFlags() ||
+ params::IsIncludedInLoFiEnabledFieldTrial();
+
+ lite_page_enabled_via_flags_or_field_trial =
+ (params::IsLoFiOnViaFlags() && params::AreLitePagesEnabledViaFlags()) ||
+ params::IsIncludedInLitePageFieldTrial();
+
+ // Previews have been disabled.
+ if (are_previews_disabled)
+ return;
+
+ // User does not have previews enabled.
+ if (!lofi_enabled_via_flags_or_field_trial &&
+ !lite_page_enabled_via_flags_or_field_trial) {
+ return;
+ }
+ }
// Lo-Fi is not allowed on the main frame, stylesheet, script, font resource,
// media, service worker, or CSP report.
@@ -101,34 +122,43 @@ void ContentLoFiDecider::MaybeSetAcceptTransformHeader(
resource_type == content::RESOURCE_TYPE_MEDIA ||
resource_type == content::RESOURCE_TYPE_CSP_REPORT);
- // If the Lite Page field trial or flag is enabled, only add the "lite-page"
- // directive on main frame requests. Only add "empty-image" directives to
- // other requests when Lite Page previews are not enabled after the main
- // frame. Add the "if-heavy" qualifier to allow the server to provide a
- // preview when the page is data heavy on if a preview was not otherwise
- // triggered.
std::string accept_transform_value;
- if (lite_page_enabled_via_flags_or_field_trial &&
- resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
+ if (!check_previews_flags) {
+ // For the proxy-decides-transform feature, check PreviewsState bits and
+ // type of resource to determine which, if any, transformation to accept.
+ if ((previews_state & content::SERVER_LITE_PAGE_ON) &&
+ resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
+ accept_transform_value = lite_page_directive();
+ } else if ((previews_state & content::SERVER_LOFI_ON) &&
+ resource_type_supports_empty_image) {
+ // Note that for subresource requests, the Lo-Fi bit should only be set
+ // if the main frame response provided the "empty-image" directive (for
+ // the client to echo back to the server here for any image resources).
+ accept_transform_value = empty_image_directive();
+ }
+ } else if (lite_page_enabled_via_flags_or_field_trial &&
+ resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
+ // If the Lite Page field trial or flag is enabled, only add the "lite-page"
+ // directive on main frame requests. Only add "empty-image" directives to
+ // other requests when Lite Page previews are not enabled after the main
+ // frame. Add the "if-heavy" qualifier to allow the server to provide a
+ // preview when the page is data heavy on if a preview was not otherwise
+ // triggered.
accept_transform_value = lite_page_directive();
// Since a Lite Page was not triggered client side, ask the server to
// provide a Lite Page only if the page is otherwise data-heavy.
- if (!(request_info->GetPreviewsState() & content::SERVER_LITE_PAGE_ON))
+ if (!(previews_state & content::SERVER_LITE_PAGE_ON))
accept_transform_value += base::StringPrintf(";%s", if_heavy_qualifier());
} else if (lofi_enabled_via_flags_or_field_trial &&
- // Only use Lo-Fi if Lite Pages aren't enabled or fallback from
- // Lite Pages to Lo-Fi is enabled.
- (!lite_page_enabled_via_flags_or_field_trial ||
- params::IsLitePageFallbackEnabled()) &&
+ !lite_page_enabled_via_flags_or_field_trial &&
resource_type_supports_empty_image &&
- !(request_info->GetPreviewsState() &
- content::SERVER_LITE_PAGE_ON)) {
+ !(previews_state & content::SERVER_LITE_PAGE_ON)) {
accept_transform_value = empty_image_directive();
// Since Lo-Fi was not triggered client side, ask the server to provide
// Lo-Fi only if the page is otherwise data-heavy.
- if (!(request_info->GetPreviewsState() & content::SERVER_LOFI_ON))
+ if (!(previews_state & content::SERVER_LOFI_ON))
accept_transform_value += base::StringPrintf(";%s", if_heavy_qualifier());
}
if (accept_transform_value.empty())
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc b/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
index d033a521d8f..8b4522f948d 100644
--- a/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
+++ b/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
@@ -15,12 +15,14 @@
#include "base/metrics/field_trial.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
#include "build/build_config.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_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.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/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_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
@@ -143,10 +145,13 @@ class ContentLoFiDeciderTest : public testing::Test {
net::ProxyRetryInfoMap proxy_retry_info;
if (use_data_reduction_proxy) {
- std::string data_reduction_proxy;
- base::TrimString(test_context_->config()->test_params()->DefaultOrigin(),
- "/", &data_reduction_proxy);
- data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
+ test_context_->config()->test_params()->UseNonSecureProxiesForHttp();
+ data_reduction_proxy_info.UseProxyServer(test_context_->config()
+ ->test_params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server());
+
} else {
data_reduction_proxy_info.UseNamedProxy("proxy.com");
}
@@ -192,6 +197,25 @@ class ContentLoFiDeciderTest : public testing::Test {
}
}
+ static void VerifyAcceptTransformHeader(const net::URLRequest& request,
+ bool expected_accept_lite_page,
+ bool expected_accept_empty_image) {
+ std::unique_ptr<data_reduction_proxy::ContentLoFiDecider> lofi_decider(
+ new data_reduction_proxy::ContentLoFiDecider());
+ net::HttpRequestHeaders headers;
+ lofi_decider->MaybeSetAcceptTransformHeader(request, false, &headers);
+
+ std::string header_value;
+ EXPECT_EQ(expected_accept_lite_page || expected_accept_empty_image,
+ headers.GetHeader(chrome_proxy_accept_transform_header(),
+ &header_value));
+ if (expected_accept_lite_page) {
+ EXPECT_TRUE(header_value == lite_page_directive());
+ } else if (expected_accept_empty_image) {
+ EXPECT_TRUE(header_value == empty_image_directive());
+ }
+ }
+
static void VerifyVideoHeader(bool expected_compressed_video_used,
const net::HttpRequestHeaders& headers) {
EXPECT_EQ(expected_compressed_video_used,
@@ -213,6 +237,11 @@ class ContentLoFiDeciderTest : public testing::Test {
};
TEST_F(ContentLoFiDeciderTest, LoFiFlags) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
// Enable Lo-Fi.
const struct {
bool is_using_lofi;
@@ -230,8 +259,6 @@ TEST_F(ContentLoFiDeciderTest, LoFiFlags) {
previews_state |= content::SERVER_LOFI_ON;
if (tests[i].is_using_lite_page)
previews_state |= content::SERVER_LITE_PAGE_ON;
- if (previews_state == content::PREVIEWS_UNSPECIFIED)
- previews_state = content::PREVIEWS_OFF;
std::unique_ptr<net::URLRequest> request =
CreateRequest(tests[i].is_main_frame, previews_state);
@@ -306,14 +333,111 @@ TEST_F(ContentLoFiDeciderTest, LoFiFlags) {
}
}
+TEST_F(ContentLoFiDeciderTest, MaybeSetAcceptTransformNoAcceptForPreviewsOff) {
+ // Turn on proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
+ std::unique_ptr<net::URLRequest> request =
+ CreateRequest(true /* is main */, content::PREVIEWS_OFF);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+
+ request = CreateRequest(true /* is main */, content::PREVIEWS_NO_TRANSFORM);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+
+ request = CreateRequest(true /* is main */, content::PREVIEWS_UNSPECIFIED);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+}
+
+TEST_F(ContentLoFiDeciderTest, MaybeSetAcceptTransformNoAcceptForHttps) {
+ // Turn on proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
+ content::PreviewsState both_previews_enabled =
+ content::SERVER_LITE_PAGE_ON | content::SERVER_LOFI_ON;
+
+ // Verify no accept header for HTTPS.
+ std::unique_ptr<net::URLRequest> request =
+ CreateRequestByType(content::RESOURCE_TYPE_MAIN_FRAME, true /* https */,
+ both_previews_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+
+ request = CreateRequestByType(content::RESOURCE_TYPE_IMAGE, true /* https */,
+ both_previews_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+}
+
+TEST_F(ContentLoFiDeciderTest, MaybeSetAcceptTransformHeaderAcceptLitePage) {
+ // Turn on proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
+ content::PreviewsState lite_page_enabled = content::SERVER_LITE_PAGE_ON;
+
+ // Verify accepting lite-page per resource type.
+ std::unique_ptr<net::URLRequest> request =
+ CreateRequest(true /* is main */, lite_page_enabled);
+ VerifyAcceptTransformHeader(*request.get(), true /* lite-page */,
+ false /* empty-image */);
+
+ request = CreateRequest(false /* is main */, lite_page_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+}
+
+TEST_F(ContentLoFiDeciderTest, MaybeSetAcceptTransformHeaderAcceptEmptyImage) {
+ // Turn on proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
+ content::PreviewsState lofi_enabled = content::SERVER_LOFI_ON;
+
+ // Verify accepting empty-image per resource type.
+ std::unique_ptr<net::URLRequest> request = CreateRequestByType(
+ content::RESOURCE_TYPE_MAIN_FRAME, false /* https */, lofi_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+
+ request = CreateRequestByType(content::RESOURCE_TYPE_IMAGE, false /* https */,
+ lofi_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ true /* empty-image */);
+
+ request = CreateRequestByType(content::RESOURCE_TYPE_FAVICON,
+ false /* https */, lofi_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ true /* empty-image */);
+
+ request = CreateRequestByType(content::RESOURCE_TYPE_SCRIPT,
+ false /* https */, lofi_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+
+ request = CreateRequestByType(content::RESOURCE_TYPE_STYLESHEET,
+ false /* https */, lofi_enabled);
+ VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+ false /* empty-image */);
+}
+
TEST_F(ContentLoFiDeciderTest, LoFiEnabledFieldTrial) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
base::FieldTrialList field_trial_list(nullptr);
base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
"Enabled");
- // Add the Lite Page fallback field trial. Having this enabled when not in the
- // Enabled_Previews group should not affect how Lo-Fi works.
- base::FieldTrialList::CreateFieldTrial(
- params::GetLitePageFallbackFieldTrialName(), "Enabled");
// Enable Lo-Fi.
const struct {
@@ -358,10 +482,10 @@ TEST_F(ContentLoFiDeciderTest, LoFiEnabledFieldTrial) {
{true, content::RESOURCE_TYPE_PLUGIN_RESOURCE}};
for (size_t i = 0; i < arraysize(tests); ++i) {
- std::unique_ptr<net::URLRequest> request =
- CreateRequestByType(tests[i].resource_type, false,
- tests[i].is_using_lofi ? content::SERVER_LOFI_ON
- : content::PREVIEWS_OFF);
+ std::unique_ptr<net::URLRequest> request = CreateRequestByType(
+ tests[i].resource_type, false,
+ tests[i].is_using_lofi ? content::SERVER_LOFI_ON
+ : content::PREVIEWS_UNSPECIFIED);
net::HttpRequestHeaders headers;
NotifyBeforeSendHeaders(&headers, request.get(), true);
bool is_lofi_resource_type =
@@ -380,6 +504,11 @@ TEST_F(ContentLoFiDeciderTest, LoFiEnabledFieldTrial) {
}
TEST_F(ContentLoFiDeciderTest, LoFiControlFieldTrial) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
base::FieldTrialList field_trial_list(nullptr);
base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
"Control");
@@ -390,9 +519,10 @@ TEST_F(ContentLoFiDeciderTest, LoFiControlFieldTrial) {
} tests[] = {{false, false}, {false, true}, {true, false}, {true, true}};
for (size_t i = 0; i < arraysize(tests); ++i) {
- std::unique_ptr<net::URLRequest> request = CreateRequest(
- tests[i].is_main_frame, tests[i].is_using_lofi ? content::SERVER_LOFI_ON
- : content::PREVIEWS_OFF);
+ std::unique_ptr<net::URLRequest> request =
+ CreateRequest(tests[i].is_main_frame,
+ tests[i].is_using_lofi ? content::SERVER_LOFI_ON
+ : content::PREVIEWS_UNSPECIFIED);
net::HttpRequestHeaders headers;
NotifyBeforeSendHeaders(&headers, request.get(), true);
VerifyLoFiHeader(false, false, headers);
@@ -403,6 +533,11 @@ TEST_F(ContentLoFiDeciderTest, LoFiControlFieldTrial) {
}
TEST_F(ContentLoFiDeciderTest, LitePageFieldTrial) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
base::FieldTrialList field_trial_list(nullptr);
base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
"Enabled_Preview");
@@ -415,10 +550,10 @@ TEST_F(ContentLoFiDeciderTest, LitePageFieldTrial) {
};
for (size_t i = 0; i < arraysize(tests); ++i) {
- std::unique_ptr<net::URLRequest> request =
- CreateRequest(tests[i].is_main_frame, tests[i].is_using_lite_page
- ? content::SERVER_LITE_PAGE_ON
- : content::PREVIEWS_OFF);
+ std::unique_ptr<net::URLRequest> request = CreateRequest(
+ tests[i].is_main_frame, tests[i].is_using_lite_page
+ ? content::SERVER_LITE_PAGE_ON
+ : content::PREVIEWS_UNSPECIFIED);
net::HttpRequestHeaders headers;
NotifyBeforeSendHeaders(&headers, request.get(), true);
VerifyLoFiHeader(false, false, headers);
@@ -429,52 +564,12 @@ TEST_F(ContentLoFiDeciderTest, LitePageFieldTrial) {
}
}
-TEST_F(ContentLoFiDeciderTest, LitePageFieldTrialFallbackEnabled) {
- base::FieldTrialList field_trial_list(nullptr);
- base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
- "Enabled_Preview");
- base::FieldTrialList::CreateFieldTrial(
- params::GetLitePageFallbackFieldTrialName(), "Enabled");
-
- // Enable Lo-Fi.
- const struct {
- bool is_using_lofi;
- bool is_using_lite_page;
- bool is_main_frame;
- } tests[] = {
- {false, false, false}, {false, false, true}, {true, false, true},
- {true, false, false}, {false, true, false}, {false, true, true},
- {true, true, true}, {true, true, false},
- };
-
- for (size_t i = 0; i < arraysize(tests); ++i) {
- content::PreviewsState previews_state = content::PREVIEWS_UNSPECIFIED;
- if (tests[i].is_using_lofi)
- previews_state |= content::SERVER_LOFI_ON;
- if (tests[i].is_using_lite_page)
- previews_state |= content::SERVER_LITE_PAGE_ON;
- if (previews_state == content::PREVIEWS_UNSPECIFIED)
- previews_state = content::PREVIEWS_OFF;
-
- std::unique_ptr<net::URLRequest> request =
- CreateRequest(tests[i].is_main_frame, previews_state);
- net::HttpRequestHeaders headers;
- NotifyBeforeSendHeaders(&headers, request.get(), true);
- VerifyLoFiHeader(!tests[i].is_main_frame && !tests[i].is_using_lite_page,
- !tests[i].is_main_frame && !tests[i].is_using_lofi &&
- !tests[i].is_using_lite_page,
- headers);
- VerifyLitePageHeader(tests[i].is_main_frame,
- tests[i].is_main_frame && !tests[i].is_using_lite_page,
- headers);
- DataReductionProxyData* data = DataReductionProxyData::GetData(*request);
- EXPECT_EQ(tests[i].is_using_lofi || tests[i].is_using_lite_page,
- data->lofi_requested())
- << i;
- }
-}
-
TEST_F(ContentLoFiDeciderTest, LitePageFieldTrialFallbackDisabled) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
base::FieldTrialList field_trial_list(nullptr);
base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
"Enabled_Preview");
@@ -495,8 +590,6 @@ TEST_F(ContentLoFiDeciderTest, LitePageFieldTrialFallbackDisabled) {
previews_state |= content::SERVER_LOFI_ON;
if (tests[i].is_using_lite_page)
previews_state |= content::SERVER_LITE_PAGE_ON;
- if (previews_state == content::PREVIEWS_UNSPECIFIED)
- previews_state = content::PREVIEWS_OFF;
std::unique_ptr<net::URLRequest> request =
CreateRequest(tests[i].is_main_frame, previews_state);
@@ -514,6 +607,11 @@ TEST_F(ContentLoFiDeciderTest, LitePageFieldTrialFallbackDisabled) {
}
TEST_F(ContentLoFiDeciderTest, AutoLoFi) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
const struct {
bool auto_lofi_enabled_group;
bool auto_lofi_control_group;
@@ -562,6 +660,11 @@ TEST_F(ContentLoFiDeciderTest, AutoLoFi) {
}
TEST_F(ContentLoFiDeciderTest, SlowConnectionsFlag) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
const struct {
bool slow_connections_flag_enabled;
bool network_prohibitively_slow;
@@ -671,11 +774,11 @@ TEST_F(ContentLoFiDeciderTest, VideoDirectiveDoesNotOverride) {
std::unique_ptr<net::URLRequest> request =
CreateRequestByType(content::RESOURCE_TYPE_MEDIA, false, true);
net::HttpRequestHeaders headers;
- headers.SetHeader(chrome_proxy_accept_transform_header(), "foo");
+ headers.SetHeader(chrome_proxy_accept_transform_header(), "empty-image");
NotifyBeforeSendHeaders(&headers, request.get(), true);
std::string header_value;
headers.GetHeader(chrome_proxy_accept_transform_header(), &header_value);
- EXPECT_EQ("foo", header_value);
+ EXPECT_EQ("empty-image", header_value);
}
TEST_F(ContentLoFiDeciderTest, IsSlowPagePreviewRequested) {
diff --git a/chromium/components/data_reduction_proxy/core/browser/BUILD.gn b/chromium/components/data_reduction_proxy/core/browser/BUILD.gn
index dc9c93e0631..b5d3c926fec 100644
--- a/chromium/components/data_reduction_proxy/core/browser/BUILD.gn
+++ b/chromium/components/data_reduction_proxy/core/browser/BUILD.gn
@@ -42,8 +42,6 @@ browser_sources = [
"data_reduction_proxy_service_observer.h",
"data_reduction_proxy_settings.cc",
"data_reduction_proxy_settings.h",
- "data_reduction_proxy_tamper_detection.cc",
- "data_reduction_proxy_tamper_detection.h",
"data_store.cc",
"data_store.h",
"data_usage_store.cc",
@@ -173,7 +171,6 @@ source_set("unit_tests") {
"data_reduction_proxy_prefs_unittest.cc",
"data_reduction_proxy_request_options_unittest.cc",
"data_reduction_proxy_settings_unittest.cc",
- "data_reduction_proxy_tamper_detection_unittest.cc",
"data_usage_store_unittest.cc",
]
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 ef62c610e40..72801848901 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
@@ -743,9 +743,21 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) {
BYPASS_EVENT_TYPE_SHORT
},
};
- std::string primary = test_context_->config()->test_params()->DefaultOrigin();
- std::string fallback =
- test_context_->config()->test_params()->DefaultFallbackOrigin();
+ test_context_->config()->test_params()->UseNonSecureProxiesForHttp();
+ std::string primary = test_context_->config()
+ ->test_params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server()
+ .host_port_pair()
+ .ToString();
+ std::string fallback = test_context_->config()
+ ->test_params()
+ ->proxies_for_http()
+ .at(1)
+ .proxy_server()
+ .host_port_pair()
+ .ToString();
for (size_t i = 0; i < arraysize(tests); ++i) {
ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
net::ProxyServer::FromURI(
@@ -930,6 +942,7 @@ TEST_F(DataReductionProxyBypassProtocolEndToEndTest,
AttachToContextAndInit();
if (test.enable_data_reduction_proxy)
drp_test_context()->EnableDataReductionProxyWithSecureProxyCheckSuccess();
+ drp_test_context()->config()->test_params()->UseNonSecureProxiesForHttp();
MockRead reads[] = {MockRead(test.response_headers),
MockRead(""),
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
index 718c1f234b0..75927fb53c9 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
@@ -8,7 +8,6 @@
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.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"
#include "net/base/load_flags.h"
@@ -280,18 +279,6 @@ void DataReductionProxyBypassStats::RecordBypassedBytesHistograms(
RecordBypassedBytes(last_bypass_type_,
DataReductionProxyBypassStats::NOT_BYPASSED,
content_length);
-
- if (data_reduction_proxy_type_info.proxy_servers.empty())
- return;
-
- // Obtain the proxy that this request used.
- const net::ProxyServer& proxy =
- data_reduction_proxy_type_info.proxy_servers.front();
- if (proxy.is_valid() && !proxy.host_port_pair().IsEmpty()) {
- DataReductionProxyTamperDetection::DetectAndReport(
- request.response_info().headers.get(),
- proxy.is_https() || proxy.is_quic(), content_length);
- }
return;
}
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 7aa01480080..531bcaae4bf 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
@@ -258,7 +258,6 @@ class DataReductionProxyBypassStatsEndToEndTest : public testing::Test {
void SetUp() override {
drp_test_context_ = DataReductionProxyTestContext::Builder()
- .WithParamsFlags(0)
.WithURLRequestContext(&context_)
.WithMockClientSocketFactory(&mock_socket_factory_)
.Build();
@@ -273,9 +272,9 @@ class DataReductionProxyBypassStatsEndToEndTest : public testing::Test {
// fully bypassed.
std::vector<DataReductionProxyServer> data_reduction_proxy_servers;
data_reduction_proxy_servers.push_back(DataReductionProxyServer(
- net::ProxyServer::FromURI(config()->test_params()->DefaultOrigin(),
- net::ProxyServer::SCHEME_HTTP),
+ config()->test_params()->proxies_for_http().front().proxy_server(),
ProxyServer::CORE));
+ config()->test_params()->UseNonSecureProxiesForHttp();
config()->test_params()->SetProxiesForHttp(data_reduction_proxy_servers);
}
@@ -797,11 +796,14 @@ TEST_F(DataReductionProxyBypassStatsEndToEndTest,
TEST_F(DataReductionProxyBypassStatsEndToEndTest, BypassedBytesNetErrorOther) {
// Make the data reduction proxy host fail to resolve.
- net::ProxyServer origin =
- config()->test_params()->proxies_for_http().front().proxy_server();
std::unique_ptr<net::MockHostResolver> host_resolver(
new net::MockHostResolver());
- host_resolver->rules()->AddSimulatedFailure(origin.host_port_pair().host());
+
+ for (const auto& proxy_server : config()->test_params()->proxies_for_http()) {
+ host_resolver->rules()->AddSimulatedFailure(
+ proxy_server.proxy_server().host_port_pair().host());
+ }
+
set_host_resolver(host_resolver.get());
InitializeContext();
@@ -1062,25 +1064,4 @@ TEST_F(DataReductionProxyBypassStatsEndToEndTest, HttpProxyScheme) {
1 /*PROXY_SCHEME_HTTP */, 1);
}
-// Verifies that the scheme of the HTTPS data reduction proxy used is recorded
-// correctly.
-TEST_F(DataReductionProxyBypassStatsEndToEndTest, HttpsProxyScheme) {
- net::ProxyServer origin =
- net::ProxyServer::FromURI("test.com:443", net::ProxyServer::SCHEME_HTTPS);
- std::vector<DataReductionProxyServer> data_reduction_proxy_servers;
- data_reduction_proxy_servers.push_back(
- DataReductionProxyServer(origin, ProxyServer::UNSPECIFIED_TYPE));
- config()->test_params()->SetProxiesForHttp(data_reduction_proxy_servers);
-
- InitializeContext();
-
- base::HistogramTester histogram_tester;
- CreateAndExecuteRequest(GURL("http://bar.com"), net::LOAD_NORMAL, net::OK,
- "HTTP/1.1 200 OK\r\n"
- "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
- kNextBody.c_str(), nullptr, nullptr);
- histogram_tester.ExpectUniqueSample("DataReductionProxy.ProxySchemeUsed",
- 2 /*PROXY_SCHEME_HTTPS */, 1);
-}
-
} // namespace data_reduction_proxy
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 662892a0087..bfeeb165adf 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
@@ -20,7 +20,6 @@
#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"
-#include "components/data_reduction_proxy/core/browser/data_use_group.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
@@ -437,43 +436,23 @@ void DataReductionProxyCompressionStats::Init() {
InitListPref(prefs::kDailyOriginalContentLengthWithDataReductionProxyEnabled);
}
-void DataReductionProxyCompressionStats::UpdateContentLengths(
+void DataReductionProxyCompressionStats::RecordDataUseWithMimeType(
int64_t data_used,
int64_t original_size,
bool data_saver_enabled,
DataReductionProxyRequestType request_type,
- const scoped_refptr<DataUseGroup>& data_use_group,
const std::string& mime_type) {
DCHECK(thread_checker_.CalledOnValidThread());
+ TRACE_EVENT0("loader",
+ "DataReductionProxyCompressionStats::RecordDataUseWithMimeType")
session_total_received_ += data_used;
session_total_original_ += original_size;
- std::string data_use_host;
- if (data_use_group) {
- data_use_host = data_use_group->GetHostname();
- }
-
- RecordData(data_used, original_size, data_saver_enabled, request_type,
- data_use_host, mime_type);
-}
-
-void DataReductionProxyCompressionStats::RecordData(
- int64_t data_used,
- int64_t original_size,
- bool data_saver_enabled,
- DataReductionProxyRequestType request_type,
- const std::string& data_use_host,
- const std::string& mime_type) {
- DCHECK(thread_checker_.CalledOnValidThread());
- TRACE_EVENT0("loader", "DataReductionProxyCompressionStats::RecordData")
IncreaseInt64Pref(data_reduction_proxy::prefs::kHttpReceivedContentLength,
data_used);
IncreaseInt64Pref(data_reduction_proxy::prefs::kHttpOriginalContentLength,
original_size);
- // TODO(rajendrant): Remove RecordDataUsage once data use ascriber based per
- // domain data usage recording is enabled.
- RecordDataUsage(data_use_host, data_used, original_size, base::Time::Now());
RecordRequestSizePrefs(data_used, original_size, data_saver_enabled,
request_type, mime_type, base::Time::Now());
}
@@ -1099,6 +1078,7 @@ void DataReductionProxyCompressionStats::RecordRequestSizePrefs(
long_bypass.Add(data_used);
break;
case UPDATE:
+ case DIRECT_HTTP:
// Don't record any request level prefs. If this is an update, this data
// was already recorded at the URLRequest level. Updates are generally
// page load level optimizations and don't correspond to request types.
@@ -1188,11 +1168,12 @@ void DataReductionProxyCompressionStats::IncrementDailyUmaPrefs(
}
}
-void DataReductionProxyCompressionStats::RecordDataUsage(
+void DataReductionProxyCompressionStats::RecordDataUseByHost(
const std::string& data_usage_host,
int64_t data_used,
int64_t original_size,
const base::Time time) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (current_data_usage_load_status_ != LOADED)
return;
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 324a1da2a28..611402ba274 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
@@ -34,7 +34,6 @@ class Value;
namespace data_reduction_proxy {
class DataReductionProxyService;
class DataUsageBucket;
-class DataUseGroup;
class PerSiteDataUsage;
// Data reduction proxy delayed pref service reduces the number calls to pref
@@ -61,27 +60,23 @@ class DataReductionProxyCompressionStats {
const base::TimeDelta& delay);
~DataReductionProxyCompressionStats();
- // Records detailed data usage broken down by connection type and domain. Also
- // records daily data savings statistics to prefs and reports data savings
- // UMA. |compressed_size| and |original_size| are measured in bytes.
- // TODO(rajendrant): This can be changed to RecordRequestMimeType and
- // |data_use_group| param removed. It records daily data savings statistics to
- // prefs and reports data savings UMA.
- void UpdateContentLengths(int64_t compressed_size,
- int64_t original_size,
- bool data_reduction_proxy_enabled,
- DataReductionProxyRequestType request_type,
- const scoped_refptr<DataUseGroup>& data_use_group,
- const std::string& mime_type);
+ // 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);
// 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
// which the data usage occurred. This method should be called in real time,
// so |time| is expected to be |Time::Now()|.
- void RecordDataUsage(const std::string& data_usage_host,
- int64_t original_request_size,
- int64_t data_used,
- const base::Time time);
+ void RecordDataUseByHost(const std::string& data_usage_host,
+ int64_t original_request_size,
+ int64_t data_used,
+ const base::Time time);
// Creates a |Value| summary of the persistent state of the network
// statistics.
@@ -240,16 +235,6 @@ class DataReductionProxyCompressionStats {
// Example: "http://www.finance.google.com" -> "www.finance.google.com"
static std::string NormalizeHostname(const std::string& host);
- // Records detailed data usage broken down by |host|. Also records daily data
- // savings statistics to prefs and reports data savings UMA. |data_used| and
- // |original_size| are measured in bytes.
- void RecordData(int64_t data_used,
- int64_t original_size,
- bool data_saver_enabled,
- DataReductionProxyRequestType request_type,
- const std::string& data_use_host,
- const std::string& mime_type);
-
DataReductionProxyService* service_;
PrefService* pref_service_;
const base::TimeDelta delay_;
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 d05f679441f..f701d40fb89 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
@@ -20,7 +20,6 @@
#include "base/values.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
-#include "components/data_reduction_proxy/core/browser/data_use_group.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/data_reduction_proxy/proto/data_store.pb.h"
@@ -418,8 +417,8 @@ class DataReductionProxyCompressionStatsTest : public testing::Test {
int64_t data_used,
int64_t original_size,
const base::Time& time) {
- compression_stats_->RecordDataUsage(data_usage_host, data_used,
- original_size, time);
+ compression_stats_->RecordDataUseByHost(data_usage_host, data_used,
+ original_size, time);
}
void GetHistoricalDataUsage(
@@ -570,9 +569,9 @@ TEST_F(DataReductionProxyCompressionStatsTest, TotalLengths) {
const int64_t kOriginalLength = 200;
const int64_t kReceivedLength = 100;
- compression_stats()->UpdateContentLengths(
+ compression_stats()->RecordDataUseWithMimeType(
kReceivedLength, kOriginalLength, IsDataReductionProxyEnabled(),
- UNKNOWN_TYPE, nullptr, std::string());
+ UNKNOWN_TYPE, std::string());
EXPECT_EQ(kReceivedLength,
GetInt64(data_reduction_proxy::prefs::kHttpReceivedContentLength));
@@ -581,9 +580,9 @@ TEST_F(DataReductionProxyCompressionStatsTest, TotalLengths) {
GetInt64(data_reduction_proxy::prefs::kHttpOriginalContentLength));
// Record the same numbers again, and total lengths should be doubled.
- compression_stats()->UpdateContentLengths(
+ compression_stats()->RecordDataUseWithMimeType(
kReceivedLength, kOriginalLength, IsDataReductionProxyEnabled(),
- UNKNOWN_TYPE, nullptr, std::string());
+ UNKNOWN_TYPE, std::string());
EXPECT_EQ(kReceivedLength * 2,
GetInt64(data_reduction_proxy::prefs::kHttpReceivedContentLength));
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 16cf8fef44f..d35166936ef 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
@@ -54,6 +54,17 @@ using base::FieldTrialList;
namespace {
+// Values of the UMA DataReductionProxy.Protocol.NotAcceptingTransform histogram
+// defined in metrics/histograms/histograms.xml. This enum must remain
+// synchronized with DataReductionProxyProtocolNotAcceptingTransformReason in
+// tools/metrics/histograms/enums.xml.
+enum NotAcceptingTransformReason {
+ NOT_ACCEPTING_TRANSFORM_DISABLED = 0,
+ NOT_ACCEPTING_TRANSFORM_BLACKLISTED = 1,
+ NOT_ACCEPTING_TRANSFORM_CELLULAR_ONLY = 2,
+ NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY
+};
+
// Values of the UMA DataReductionProxy.NetworkChangeEvents histograms.
// This enum must remain synchronized with the enum of the same
// name in metrics/histograms/histograms.xml.
@@ -246,8 +257,7 @@ class SecureProxyChecker : public net::URLFetcherDelegate {
fetcher_callback_.Run(response, status, source->GetResponseCode());
}
- void CheckIfSecureProxyIsAllowed(const GURL& secure_proxy_check_url,
- FetcherResponseCallback fetcher_callback) {
+ void CheckIfSecureProxyIsAllowed(FetcherResponseCallback fetcher_callback) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation(
"data_reduction_proxy_secure_proxy_check", R"(
@@ -272,8 +282,9 @@ class SecureProxyChecker : public net::URLFetcherDelegate {
"it is enabled by installing the Data Saver extension."
policy_exception_justification: "Not implemented."
})");
- fetcher_ = net::URLFetcher::Create(
- secure_proxy_check_url, net::URLFetcher::GET, this, traffic_annotation);
+ fetcher_ =
+ net::URLFetcher::Create(params::GetSecureProxyCheckURL(),
+ net::URLFetcher::GET, this, traffic_annotation);
data_use_measurement::DataUseUserData::AttachToFetcher(
fetcher_.get(),
data_use_measurement::DataUseUserData::DATA_REDUCTION_PROXY);
@@ -454,7 +465,7 @@ void DataReductionProxyConfig::ReloadConfig() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(configurator_);
- if (enabled_by_user_ && !config_values_->holdback() &&
+ if (enabled_by_user_ && !params::IsIncludedInHoldbackFieldTrial() &&
!config_values_->proxies_for_http().empty()) {
configurator_->Enable(!secure_proxy_allowed_ || is_captive_portal_,
config_values_->proxies_for_http());
@@ -736,12 +747,6 @@ bool DataReductionProxyConfig::ContainsDataReductionProxy(
return false;
}
-// Returns true if the Data Reduction Proxy promo may be shown. This is not
-// tied to whether the Data Reduction Proxy is enabled.
-bool DataReductionProxyConfig::promo_allowed() const {
- return config_values_->promo_allowed();
-}
-
void DataReductionProxyConfig::SetProxyConfig(bool enabled, bool at_startup) {
DCHECK(thread_checker_.CalledOnValidThread());
enabled_by_user_ = enabled;
@@ -756,7 +761,6 @@ void DataReductionProxyConfig::SetProxyConfig(bool enabled, bool at_startup) {
// synchronously on the IO thread, and |this| outlives
// |secure_proxy_checker_|.
SecureProxyCheck(
- config_values_->secure_proxy_check_url(),
base::Bind(&DataReductionProxyConfig::HandleSecureProxyCheckResponse,
base::Unretained(this)));
}
@@ -857,7 +861,6 @@ void DataReductionProxyConfig::OnIPAddressChanged() {
// synchronously on the IO thread, and |this| outlives
// |secure_proxy_checker_|.
SecureProxyCheck(
- config_values_->secure_proxy_check_url(),
base::Bind(&DataReductionProxyConfig::HandleSecureProxyCheckResponse,
base::Unretained(this)));
}
@@ -890,17 +893,15 @@ void DataReductionProxyConfig::AddDefaultProxyBypassRules() {
}
void DataReductionProxyConfig::SecureProxyCheck(
- const GURL& secure_proxy_check_url,
FetcherResponseCallback fetcher_callback) {
net_log_with_source_ = net::NetLogWithSource::Make(
net_log_, net::NetLogSourceType::DATA_REDUCTION_PROXY);
if (event_creator_) {
- event_creator_->BeginSecureProxyCheck(
- net_log_with_source_, config_values_->secure_proxy_check_url());
+ event_creator_->BeginSecureProxyCheck(net_log_with_source_,
+ params::GetSecureProxyCheckURL());
}
- secure_proxy_checker_->CheckIfSecureProxyIsAllowed(secure_proxy_check_url,
- fetcher_callback);
+ secure_proxy_checker_->CheckIfSecureProxyIsAllowed(fetcher_callback);
}
void DataReductionProxyConfig::FetchWarmupURL() {
@@ -1014,7 +1015,7 @@ bool DataReductionProxyConfig::ShouldEnableLoFi(
if (base::FeatureList::IsEnabled(
features::kDataReductionProxyDecidesTransform)) {
- return ShouldAcceptServerLoFi(request, previews_decider);
+ return ShouldAcceptServerPreview(request, previews_decider);
}
bool enable_lofi = ShouldEnableLoFiInternal(request, previews_decider);
@@ -1038,7 +1039,7 @@ bool DataReductionProxyConfig::ShouldEnableLitePages(
if (base::FeatureList::IsEnabled(
features::kDataReductionProxyDecidesTransform)) {
- return ShouldAcceptLitePages(request, previews_decider);
+ return ShouldAcceptServerPreview(request, previews_decider);
}
return ShouldEnableLitePagesInternal(request, previews_decider);
@@ -1060,7 +1061,8 @@ bool DataReductionProxyConfig::IsBlackListedOrDisabled(
// TODO(ryansturm): Use the correct ECT value (or add new method to
// just check blacklist). crbug.com/720102
return !previews_decider.ShouldAllowPreviewAtECT(
- request, previews_type, net::EFFECTIVE_CONNECTION_TYPE_4G);
+ request, previews_type, net::EFFECTIVE_CONNECTION_TYPE_4G,
+ std::vector<std::string>());
} else {
// If Lo-Fi has been turned off, its status can't change. This Lo-Fi bit
// will be removed when Lo-Fi and Lite Pages are moved over to using the
@@ -1069,67 +1071,48 @@ bool DataReductionProxyConfig::IsBlackListedOrDisabled(
}
}
-bool DataReductionProxyConfig::ShouldAcceptServerLoFi(
+bool DataReductionProxyConfig::ShouldAcceptServerPreview(
const net::URLRequest& request,
const previews::PreviewsDecider& previews_decider) const {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(base::FeatureList::IsEnabled(
features::kDataReductionProxyDecidesTransform));
- if (IsBlackListedOrDisabled(request, previews_decider,
- previews::PreviewsType::LOFI)) {
+ // For the transition to server-driven previews decisions, we will
+ // use existing Lo-Fi flags for disabling and cellular-only mode.
+ // TODO(dougarnett): Refactor flag names as part of bug 725645.
+ if (params::IsLoFiDisabledViaFlags() ||
+ (!params::IsBlackListEnabledForServerPreviews() && lofi_off())) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "DataReductionProxy.Protocol.NotAcceptingTransform",
+ NOT_ACCEPTING_TRANSFORM_DISABLED,
+ NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY);
return false;
}
- if (params::IsLoFiAlwaysOnViaFlags()) {
- return true;
- }
-
- if (params::IsLoFiCellularOnlyViaFlags()) {
- return net::NetworkChangeNotifier::IsConnectionCellular(connection_type_);
- }
-
- if (params::IsLoFiSlowConnectionsOnlyViaFlags() ||
- params::IsIncludedInLoFiEnabledFieldTrial()) {
- // Accept Lo-Fi from the data reduction proxy (it will handle the effective
- // connection type check).
+ // AlwaysOn skips blacklist or disabled checks.
+ if (params::IsLoFiAlwaysOnViaFlags())
return true;
- }
-
- return false;
-}
-
-bool DataReductionProxyConfig::ShouldAcceptLitePages(
- const net::URLRequest& request,
- const previews::PreviewsDecider& previews_decider) const {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(base::FeatureList::IsEnabled(
- features::kDataReductionProxyDecidesTransform));
if (IsBlackListedOrDisabled(request, previews_decider,
previews::PreviewsType::LITE_PAGE)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "DataReductionProxy.Protocol.NotAcceptingTransform",
+ NOT_ACCEPTING_TRANSFORM_BLACKLISTED,
+ NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY);
return false;
}
- if (params::IsLoFiAlwaysOnViaFlags() &&
- params::AreLitePagesEnabledViaFlags()) {
- return true;
- }
-
if (params::IsLoFiCellularOnlyViaFlags() &&
- params::AreLitePagesEnabledViaFlags()) {
- return net::NetworkChangeNotifier::IsConnectionCellular(connection_type_);
- }
-
- if ((params::IsLoFiSlowConnectionsOnlyViaFlags() &&
- params::AreLitePagesEnabledViaFlags()) ||
- params::IsIncludedInLitePageFieldTrial()) {
- // Accept LitePages from the data reduction proxy (it will handle the
- // effective connection type check).
- return true;
+ !net::NetworkChangeNotifier::IsConnectionCellular(connection_type_)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "DataReductionProxy.Protocol.NotAcceptingTransform",
+ NOT_ACCEPTING_TRANSFORM_CELLULAR_ONLY,
+ NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY);
+ return false;
}
- return false;
+ return true;
}
bool DataReductionProxyConfig::ShouldEnableLoFiInternal(
@@ -1150,14 +1133,15 @@ bool DataReductionProxyConfig::ShouldEnableLoFiInternal(
return false;
}
+ // AlwaysOn skips blacklist or disabled checks.
+ if (params::IsLoFiAlwaysOnViaFlags())
+ return true;
+
if (IsBlackListedOrDisabled(request, previews_decider,
previews::PreviewsType::LOFI)) {
return false;
}
- if (params::IsLoFiAlwaysOnViaFlags())
- return true;
-
if (params::IsLoFiCellularOnlyViaFlags()) {
return net::NetworkChangeNotifier::IsConnectionCellular(connection_type_);
}
@@ -1183,14 +1167,15 @@ bool DataReductionProxyConfig::ShouldEnableLitePagesInternal(
DCHECK(!base::FeatureList::IsEnabled(
features::kDataReductionProxyDecidesTransform));
+ // AlwaysOn skips blacklist or disabled checks.
+ if (params::IsLoFiAlwaysOnViaFlags() && params::AreLitePagesEnabledViaFlags())
+ return true;
+
if (IsBlackListedOrDisabled(request, previews_decider,
previews::PreviewsType::LITE_PAGE)) {
return false;
}
- if (params::IsLoFiAlwaysOnViaFlags() && params::AreLitePagesEnabledViaFlags())
- return true;
-
if (params::IsLoFiCellularOnlyViaFlags() &&
params::AreLitePagesEnabledViaFlags()) {
return net::NetworkChangeNotifier::IsConnectionCellular(
@@ -1211,12 +1196,6 @@ bool DataReductionProxyConfig::ShouldEnableLitePagesInternal(
return false;
}
-void DataReductionProxyConfig::GetNetworkList(
- net::NetworkInterfaceList* interfaces,
- int policy) {
- net::GetNetworkList(interfaces, policy);
-}
-
const std::vector<base::TimeDelta>&
DataReductionProxyConfig::GetLofiAccuracyRecordingIntervals() const {
DCHECK(thread_checker_.CalledOnValidThread());
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 e4529cbf34f..0df055b9582 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
@@ -22,15 +22,12 @@
#include "components/previews/core/previews_experiments.h"
#include "net/base/net_errors.h"
#include "net/base/network_change_notifier.h"
-#include "net/base/network_interfaces.h"
#include "net/log/net_log_with_source.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/network_quality_estimator.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_retry_info.h"
-class GURL;
-
namespace base {
class SingleThreadTaskRunner;
}
@@ -186,10 +183,6 @@ class DataReductionProxyConfig
virtual bool ContainsDataReductionProxy(
const net::ProxyConfig::ProxyRules& proxy_rules) const;
- // Returns true if the Data Reduction Proxy promo may be shown. This is not
- // tied to whether the Data Reduction Proxy is enabled.
- bool promo_allowed() const;
-
// Sets |lofi_off_| to true.
void SetLoFiModeOff();
@@ -222,11 +215,6 @@ class DataReductionProxyConfig
std::vector<DataReductionProxyServer> GetProxiesForHttp() const;
protected:
- // Virtualized for mocking. Returns the list of network interfaces in use.
- // |interfaces| can be null.
- virtual void GetNetworkList(net::NetworkInterfaceList* interfaces,
- int policy);
-
// Virtualized for testing. Returns the list of intervals at which accuracy of
// network quality prediction should be recorded.
virtual const std::vector<base::TimeDelta>&
@@ -256,7 +244,8 @@ class DataReductionProxyConfig
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest, WarmupURL);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
ShouldAcceptServerLoFi);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest, ShouldAcceptLitePages);
+ FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
+ ShouldAcceptServerPreview);
// Values of the estimated network quality at the beginning of the most
// recent query of the Network Quality Estimator.
@@ -275,11 +264,9 @@ class DataReductionProxyConfig
// of either Lo-Fi enabled or Lo-Fi control field trial group.
void PopulateAutoLoFiParams();
- // Requests the given |secure_proxy_check_url|. Upon completion, returns the
- // results to the caller via the |fetcher_callback|. Virtualized for unit
- // testing.
- virtual void SecureProxyCheck(const GURL& secure_proxy_check_url,
- FetcherResponseCallback fetcher_callback);
+ // Requests the secure proxy check URL. Upon completion, returns the results
+ // to the caller via the |fetcher_callback|. Virtualized for unit testing.
+ virtual void SecureProxyCheck(FetcherResponseCallback fetcher_callback);
// Parses the secure proxy check responses and appropriately configures the
// Data Reduction Proxy rules.
@@ -310,20 +297,11 @@ class DataReductionProxyConfig
previews::PreviewsType previews_type) const;
// Returns whether the client should report to the data reduction proxy that
- // it is willing to accept the Server Lo-Fi optimization for |request|.
- // |previews_decider| is used to check if |request| is locally blacklisted.
- // Should only be used if the kDataReductionProxyDecidesTransform feature is
- // enabled.
- bool ShouldAcceptServerLoFi(
- const net::URLRequest& request,
- const previews::PreviewsDecider& previews_decider) const;
-
- // Returns whether the client should report to the data reduction proxy that
- // it is willing to accept a LitePage optimization for |request|.
+ // it is willing to accept server previews for |request|.
// |previews_decider| is used to check if |request| is locally blacklisted.
// Should only be used if the kDataReductionProxyDecidesTransform feature is
// enabled.
- bool ShouldAcceptLitePages(
+ bool ShouldAcceptServerPreview(
const net::URLRequest& request,
const previews::PreviewsDecider& previews_decider) const;
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 2c798fa7c3b..08ba416ba5a 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
@@ -144,7 +144,6 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
mock_socket_factory_.reset(nullptr);
test_context_ =
DataReductionProxyTestContext::Builder()
- .WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING)
.WithURLRequestContext(context_.get())
.WithMockClientSocketFactory(mock_socket_factory_.get())
.WithMockRequestOptions()
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
index 470bc614fa0..ce2beb17215 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
@@ -26,15 +26,12 @@ class NetworkQualityEstimator;
namespace data_reduction_proxy {
TestDataReductionProxyConfig::TestDataReductionProxyConfig(
- int params_flags,
- unsigned int params_definitions,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
net::NetLog* net_log,
DataReductionProxyConfigurator* configurator,
DataReductionProxyEventCreator* event_creator)
: TestDataReductionProxyConfig(
- base::MakeUnique<TestDataReductionProxyParams>(params_flags,
- params_definitions),
+ base::MakeUnique<TestDataReductionProxyParams>(),
io_task_runner,
net_log,
configurator,
@@ -56,7 +53,6 @@ TestDataReductionProxyConfig::TestDataReductionProxyConfig(
network_quality_prohibitively_slow_(false),
lofi_accuracy_recording_intervals_set_(false),
is_captive_portal_(false) {
- network_interfaces_.reset(new net::NetworkInterfaceList());
}
TestDataReductionProxyConfig::~TestDataReductionProxyConfig() {
@@ -70,16 +66,8 @@ bool TestDataReductionProxyConfig::IsNetworkQualityProhibitivelySlow(
network_quality_estimator);
}
-void TestDataReductionProxyConfig::GetNetworkList(
- net::NetworkInterfaceList* interfaces,
- int policy) {
- for (size_t i = 0; i < network_interfaces_->size(); ++i)
- interfaces->push_back(network_interfaces_->at(i));
-}
-
-void TestDataReductionProxyConfig::ResetParamFlagsForTest(int flags) {
- config_values_ = base::MakeUnique<TestDataReductionProxyParams>(
- flags, TestDataReductionProxyParams::HAS_EVERYTHING);
+void TestDataReductionProxyConfig::ResetParamFlagsForTest() {
+ config_values_ = base::MakeUnique<TestDataReductionProxyParams>();
}
TestDataReductionProxyParams* TestDataReductionProxyConfig::test_params() {
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
index 9a96c341552..c124e62290e 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -13,7 +13,6 @@
#include "base/optional.h"
#include "base/time/time.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
-#include "net/base/network_interfaces.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace base {
@@ -40,10 +39,7 @@ class TestDataReductionProxyParams;
// change the underlying state.
class TestDataReductionProxyConfig : public DataReductionProxyConfig {
public:
- // Creates a |TestDataReductionProxyConfig| with the provided |params_flags|.
TestDataReductionProxyConfig(
- int params_flags,
- unsigned int params_definitions,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
net::NetLog* net_log,
DataReductionProxyConfigurator* configurator,
@@ -61,11 +57,8 @@ class TestDataReductionProxyConfig : public DataReductionProxyConfig {
~TestDataReductionProxyConfig() override;
- void GetNetworkList(net::NetworkInterfaceList* interfaces,
- int policy) override;
-
// Allows tests to reset the params being used for configuration.
- void ResetParamFlagsForTest(int flags);
+ void ResetParamFlagsForTest();
// Retrieves the test params being used for the configuration.
TestDataReductionProxyParams* test_params();
@@ -83,10 +76,6 @@ class TestDataReductionProxyConfig : public DataReductionProxyConfig {
bool IsNetworkQualityProhibitivelySlow(
const net::NetworkQualityEstimator* network_quality_estimator) override;
- net::NetworkInterfaceList* interfaces() {
- return network_interfaces_.get();
- }
-
void SetLofiAccuracyRecordingIntervals(
const std::vector<base::TimeDelta>& lofi_accuracy_recording_intervals);
@@ -132,8 +121,6 @@ class TestDataReductionProxyConfig : public DataReductionProxyConfig {
base::Optional<bool> was_data_reduction_proxy_used_;
base::Optional<int> proxy_index_;
- std::unique_ptr<net::NetworkInterfaceList> network_interfaces_;
-
bool network_quality_prohibitively_slow_set_;
// True if the network quality is slow enough to turn Lo-Fi ON.
bool network_quality_prohibitively_slow_;
@@ -177,9 +164,8 @@ class MockDataReductionProxyConfig : public TestDataReductionProxyConfig {
bool(const net::URLRequest& request,
const net::ProxyConfig& data_reduction_proxy_config,
base::TimeDelta* min_retry_delay));
- MOCK_METHOD2(SecureProxyCheck,
- void(const GURL& secure_proxy_check_url,
- FetcherResponseCallback fetcher_callback));
+ MOCK_METHOD1(SecureProxyCheck,
+ void(FetcherResponseCallback fetcher_callback));
MOCK_METHOD1(
IsNetworkQualityProhibitivelySlow,
bool(const net::NetworkQualityEstimator* network_quality_estimator));
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 ed3784b1b06..8e9f7281d5d 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
@@ -93,7 +93,8 @@ class TestPreviewsDecider : public previews::PreviewsDecider {
bool ShouldAllowPreviewAtECT(
const net::URLRequest& request,
previews::PreviewsType type,
- net::EffectiveConnectionType effective_connection_type_threshold)
+ net::EffectiveConnectionType effective_connection_type_threshold,
+ const std::vector<std::string>& host_blacklist_from_server)
const override {
return allow_previews_;
}
@@ -123,21 +124,12 @@ class DataReductionProxyConfigTest : public testing::Test {
.WithMockDataReductionProxyService()
.Build();
- ResetSettings(true, false);
+ ResetSettings();
- expected_params_.reset(new TestDataReductionProxyParams(
- DataReductionProxyParams::kPromoAllowed,
- TestDataReductionProxyParams::HAS_EVERYTHING));
+ expected_params_.reset(new TestDataReductionProxyParams());
}
- void ResetSettings(bool promo_allowed, bool holdback) {
- int flags = 0;
- if (promo_allowed)
- flags |= DataReductionProxyParams::kPromoAllowed;
- if (holdback)
- flags |= DataReductionProxyParams::kHoldback;
- config()->ResetParamFlagsForTest(flags);
- }
+ void ResetSettings() { config()->ResetParamFlagsForTest(); }
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() {
return message_loop_.task_runner();
@@ -167,9 +159,9 @@ class DataReductionProxyConfigTest : public testing::Test {
responder.response = response;
responder.status = status;
responder.http_response_code = response_code;
- EXPECT_CALL(*config(), SecureProxyCheck(_, _))
+ EXPECT_CALL(*config(), SecureProxyCheck(_))
.Times(1)
- .WillRepeatedly(testing::WithArgs<1>(
+ .WillRepeatedly(testing::WithArgs<0>(
testing::Invoke(&responder, &TestResponder::ExecuteCallback)));
config()->SetIsCaptivePortal(is_captive_portal);
net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
@@ -235,13 +227,17 @@ class DataReductionProxyConfigTest : public testing::Test {
};
TEST_F(DataReductionProxyConfigTest, TestReloadConfigHoldback) {
+ base::FieldTrialList field_trial_list(nullptr);
+ ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+ "DataCompressionProxyHoldback", "Enabled"));
+
const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
"https://secure_origin.net:443", net::ProxyServer::SCHEME_HTTP);
const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
"insecure_origin.net:80", net::ProxyServer::SCHEME_HTTP);
SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
- ResetSettings(true, true);
+ ResetSettings();
config()->UpdateConfigForTesting(true, false);
config()->ReloadConfig();
@@ -256,7 +252,7 @@ TEST_F(DataReductionProxyConfigTest, TestOnIPAddressChanged) {
"insecure_origin.net:80", net::ProxyServer::SCHEME_HTTP);
SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
- ResetSettings(true, false);
+ ResetSettings();
// The proxy is enabled initially.
config()->UpdateConfigForTesting(true, true);
@@ -367,7 +363,7 @@ TEST_F(DataReductionProxyConfigTest, WarmupURL) {
base::HistogramTester histogram_tester;
SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
- ResetSettings(true, false);
+ ResetSettings();
variations::testing::ClearAllVariationParams();
std::map<std::string, std::string> variation_params;
@@ -383,9 +379,8 @@ TEST_F(DataReductionProxyConfigTest, WarmupURL) {
"Enabled");
base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- TestDataReductionProxyConfig config(
- 0, TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
- configurator(), event_creator());
+ TestDataReductionProxyConfig config(task_runner(), nullptr, configurator(),
+ event_creator());
scoped_refptr<net::URLRequestContextGetter> request_context_getter_ =
new net::TestURLRequestContextGetter(task_runner());
@@ -574,10 +569,19 @@ TEST_F(DataReductionProxyConfigTest, AreProxiesBypassed) {
};
// The retry map has the scheme prefix for https but not for http.
- std::string origin = GetRetryMapKeyFromOrigin(
- TestDataReductionProxyParams::DefaultOrigin());
- std::string fallback_origin = GetRetryMapKeyFromOrigin(
- TestDataReductionProxyParams::DefaultFallbackOrigin());
+ std::string origin = GetRetryMapKeyFromOrigin(params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server()
+ .host_port_pair()
+ .ToString());
+ std::string fallback_origin =
+ GetRetryMapKeyFromOrigin(params()
+ ->proxies_for_http()
+ .at(1)
+ .proxy_server()
+ .host_port_pair()
+ .ToString());
for (size_t i = 0; i < arraysize(tests); ++i) {
net::ProxyConfig::ProxyRules rules;
@@ -593,10 +597,8 @@ TEST_F(DataReductionProxyConfigTest, AreProxiesBypassed) {
rules.ParseFromString(proxy_rules);
- int flags = 0;
- unsigned int has_definitions = TestDataReductionProxyParams::HAS_EVERYTHING;
std::unique_ptr<TestDataReductionProxyParams> params(
- new TestDataReductionProxyParams(flags, has_definitions));
+ new TestDataReductionProxyParams());
std::unique_ptr<DataReductionProxyConfig> config =
BuildConfig(std::move(params));
@@ -619,10 +621,19 @@ TEST_F(DataReductionProxyConfigTest, AreProxiesBypassed) {
}
TEST_F(DataReductionProxyConfigTest, AreProxiesBypassedRetryDelay) {
- std::string origin = GetRetryMapKeyFromOrigin(
- TestDataReductionProxyParams::DefaultOrigin());
- std::string fallback_origin = GetRetryMapKeyFromOrigin(
- TestDataReductionProxyParams::DefaultFallbackOrigin());
+ std::string origin = GetRetryMapKeyFromOrigin(params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server()
+ .host_port_pair()
+ .ToString());
+ std::string fallback_origin =
+ GetRetryMapKeyFromOrigin(params()
+ ->proxies_for_http()
+ .at(1)
+ .proxy_server()
+ .host_port_pair()
+ .ToString());
net::ProxyConfig::ProxyRules rules;
std::vector<std::string> proxies;
@@ -635,10 +646,8 @@ TEST_F(DataReductionProxyConfigTest, AreProxiesBypassedRetryDelay) {
rules.ParseFromString(proxy_rules);
- int flags = 0;
- unsigned int has_definitions = TestDataReductionProxyParams::HAS_EVERYTHING;
std::unique_ptr<TestDataReductionProxyParams> params(
- new TestDataReductionProxyParams(flags, has_definitions));
+ new TestDataReductionProxyParams());
std::unique_ptr<DataReductionProxyConfig> config =
BuildConfig(std::move(params));
@@ -685,28 +694,16 @@ TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithParams) {
net::ProxyServer expected_second;
bool expected_is_fallback;
} tests[] = {
- {net::ProxyServer::FromURI(TestDataReductionProxyParams::DefaultOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- true,
- net::ProxyServer::FromURI(TestDataReductionProxyParams::DefaultOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- net::ProxyServer::FromURI(
- TestDataReductionProxyParams::DefaultFallbackOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- false},
- {net::ProxyServer::FromURI(
- TestDataReductionProxyParams::DefaultFallbackOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- true, net::ProxyServer::FromURI(
- TestDataReductionProxyParams::DefaultFallbackOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- net::ProxyServer(), true},
+ {params()->proxies_for_http().front().proxy_server(), true,
+ params()->proxies_for_http().front().proxy_server(),
+ params()->proxies_for_http().at(1).proxy_server(), false},
+ {params()->proxies_for_http().at(1).proxy_server(), true,
+ params()->proxies_for_http().at(1).proxy_server(), net::ProxyServer(),
+ true},
};
for (size_t i = 0; i < arraysize(tests); ++i) {
- int flags = 0;
- unsigned int has_definitions = TestDataReductionProxyParams::HAS_EVERYTHING;
std::unique_ptr<TestDataReductionProxyParams> params(
- new TestDataReductionProxyParams(flags, has_definitions));
+ new TestDataReductionProxyParams());
DataReductionProxyTypeInfo proxy_type_info;
std::unique_ptr<DataReductionProxyConfig> config(
new DataReductionProxyConfig(task_runner(), net_log(),
@@ -865,8 +862,8 @@ TEST_F(DataReductionProxyConfigTest, LoFiOn) {
},
{
// Lo-Fi is enabled through command line switch, but opted out. LoFi
- // should not be used.
- true, false, std::string(), false, false, 0,
+ // should be used.
+ true, false, std::string(), false, true, 0,
0, // not in enabled field trial, UMA is not recorded
true,
},
@@ -954,8 +951,8 @@ TEST_F(DataReductionProxyConfigTest, LoFiOn) {
},
{
// Lo-Fi is enabled through command line switch, but opted out. LoFi
- // should not be used.
- true, true, std::string(), false, false, 0,
+ // should be used.
+ true, true, std::string(), false, true, 0,
0, // not in enabled field trial, UMA is not recorded
true,
},
@@ -1273,9 +1270,8 @@ TEST_F(DataReductionProxyConfigTest, LoFiAccuracy) {
std::vector<base::TimeDelta> lofi_accuracy_recording_intervals;
lofi_accuracy_recording_intervals.push_back(base::TimeDelta::FromSeconds(0));
- TestDataReductionProxyConfig config(
- 0, TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
- configurator(), event_creator());
+ TestDataReductionProxyConfig config(task_runner(), nullptr, configurator(),
+ event_creator());
config.SetLofiAccuracyRecordingIntervals(lofi_accuracy_recording_intervals);
config.SetTickClock(tick_clock.get());
@@ -1361,9 +1357,8 @@ TEST_F(DataReductionProxyConfigTest, LoFiAccuracyNonZeroDelay) {
std::vector<base::TimeDelta> lofi_accuracy_recording_intervals;
lofi_accuracy_recording_intervals.push_back(base::TimeDelta::FromSeconds(1));
- TestDataReductionProxyConfig config(
- 0, TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
- configurator(), event_creator());
+ TestDataReductionProxyConfig config(task_runner(), nullptr, configurator(),
+ event_creator());
config.SetLofiAccuracyRecordingIntervals(lofi_accuracy_recording_intervals);
config.SetTickClock(tick_clock.get());
@@ -1553,12 +1548,16 @@ TEST_F(DataReductionProxyConfigTest, ShouldEnableLitePagesWithoutECTCheck) {
config()->ShouldEnableLitePages(*request.get(), *previews_decider.get()));
}
-TEST_F(DataReductionProxyConfigTest, ShouldAcceptServerLoFi) {
+TEST_F(DataReductionProxyConfigTest, ShouldAcceptServerPreview) {
// Turn on proxy-decides-transform feature to satisfy DCHECK.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kDataReductionProxyDecidesTransform);
+ base::FieldTrialList field_trial_list(nullptr);
+ base::FieldTrialList::CreateFieldTrial(
+ "DataReductionProxyPreviewsBlackListTransition", "Enabled");
+ base::HistogramTester histogram_tester;
net::TestURLRequestContext context_;
net::TestDelegate delegate_;
std::unique_ptr<net::URLRequest> request = context_.CreateRequest(
@@ -1566,37 +1565,30 @@ TEST_F(DataReductionProxyConfigTest, ShouldAcceptServerLoFi) {
request->SetLoadFlags(request->load_flags() |
net::LOAD_MAIN_FRAME_DEPRECATED);
std::unique_ptr<TestPreviewsDecider> previews_decider =
- base::MakeUnique<TestPreviewsDecider>(false);
-
- // Verify false for no flags.
- EXPECT_FALSE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
+ base::MakeUnique<TestPreviewsDecider>(true);
- // Verify true for Always-On flag.
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxyLoFi,
- switches::kDataReductionProxyLoFiValueAlwaysOn);
- EXPECT_TRUE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
+ // Verify true for no flags.
+ EXPECT_TRUE(config()->ShouldAcceptServerPreview(*request.get(),
+ *previews_decider.get()));
- // Verify true for Always-On with LitePages enabled too.
+ // Verify false for kill switch.
base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kDataReductionProxyLoFi,
- switches::kDataReductionProxyLoFiValueAlwaysOn);
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableDataReductionProxyLitePage);
- EXPECT_TRUE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
+ switches::kDataReductionProxyLoFiValueDisabled);
+ EXPECT_FALSE(config()->ShouldAcceptServerPreview(*request.get(),
+ *previews_decider.get()));
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.NotAcceptingTransform",
+ 0 /* NOT_ACCEPTING_TRANSFORM_DISABLED */, 1);
// Verify true for Slow Connection flag.
base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kDataReductionProxyLoFi,
switches::kDataReductionProxyLoFiValueSlowConnectionsOnly);
- EXPECT_TRUE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
+ EXPECT_TRUE(config()->ShouldAcceptServerPreview(*request.get(),
+ *previews_decider.get()));
// Verify false for Cellular Only flag and WIFI connection.
base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
@@ -1605,142 +1597,40 @@ TEST_F(DataReductionProxyConfigTest, ShouldAcceptServerLoFi) {
switches::kDataReductionProxyLoFiValueCellularOnly);
config()->SetConnectionTypeForTesting(
net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
- EXPECT_FALSE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
+ EXPECT_FALSE(config()->ShouldAcceptServerPreview(*request.get(),
+ *previews_decider.get()));
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.NotAcceptingTransform",
+ 2 /* NOT_ACCEPTING_TRANSFORM_CELLULAR_ONLY */, 1);
// Verify true for Cellular Only flag and 3G connection.
config()->SetConnectionTypeForTesting(
net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G);
- EXPECT_TRUE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
-
- // Verify true for field trial.
- {
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::FieldTrialList field_trial_list(nullptr);
- base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
- "Enabled");
- EXPECT_TRUE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
- }
-
- // Verify false for control field trial.
- {
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::FieldTrialList field_trial_list(nullptr);
- base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
- "Control");
- EXPECT_FALSE(config()->ShouldAcceptServerLoFi(*request.get(),
+ EXPECT_TRUE(config()->ShouldAcceptServerPreview(*request.get(),
*previews_decider.get()));
- }
// Verify PreviewsDecider check.
- {
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxyLoFi,
- switches::kDataReductionProxyLoFiValueAlwaysOn);
- base::FieldTrialList field_trial_list(nullptr);
- base::FieldTrialList::CreateFieldTrial(
- "DataReductionProxyPreviewsBlackListTransition", "Enabled");
- EXPECT_FALSE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
- previews_decider = base::MakeUnique<TestPreviewsDecider>(true);
- EXPECT_TRUE(config()->ShouldAcceptServerLoFi(*request.get(),
- *previews_decider.get()));
- }
-}
-
-TEST_F(DataReductionProxyConfigTest, ShouldAcceptLitePages) {
- // Turn on proxy-decides-transform feature to satisfy DCHECK.
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- features::kDataReductionProxyDecidesTransform);
-
- net::TestURLRequestContext context_;
- net::TestDelegate delegate_;
- std::unique_ptr<net::URLRequest> request = context_.CreateRequest(
- GURL(), net::IDLE, &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
- request->SetLoadFlags(request->load_flags() |
- net::LOAD_MAIN_FRAME_DEPRECATED);
- std::unique_ptr<TestPreviewsDecider> previews_decider =
- base::MakeUnique<TestPreviewsDecider>(false);
-
- // Verify false for no flags.
- EXPECT_FALSE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
+ previews_decider = base::MakeUnique<TestPreviewsDecider>(false);
+ EXPECT_FALSE(config()->ShouldAcceptServerPreview(*request.get(),
+ *previews_decider.get()));
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.NotAcceptingTransform",
+ 1 /* NOT_ACCEPTING_TRANSFORM_BLACKLISTED */, 1);
+ previews_decider = base::MakeUnique<TestPreviewsDecider>(true);
- // Verify true for Always-On flag and LitePage flag.
+ // Verfiy true for always on.
base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kDataReductionProxyLoFi,
switches::kDataReductionProxyLoFiValueAlwaysOn);
- EXPECT_FALSE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableDataReductionProxyLitePage);
- EXPECT_TRUE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
-
- // Verify true for Slow Connection flag and LitePage flag.
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxyLoFi,
- switches::kDataReductionProxyLoFiValueSlowConnectionsOnly);
- EXPECT_FALSE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableDataReductionProxyLitePage);
- EXPECT_TRUE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
-
- // Verify true for Cellular Only flag and 3G connection.
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxyLoFi,
- switches::kDataReductionProxyLoFiValueCellularOnly);
- config()->SetConnectionTypeForTesting(
- net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G);
- EXPECT_FALSE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableDataReductionProxyLitePage);
- EXPECT_TRUE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
-
- // Verify false for Cellular Only flag and WIFI connection.
- config()->SetConnectionTypeForTesting(
- net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
- EXPECT_FALSE(
- config()->ShouldAcceptLitePages(*request.get(), *previews_decider.get()));
-
- // Verify true for field trial.
- {
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::FieldTrialList field_trial_list(nullptr);
- base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
- "Enabled_Preview");
- EXPECT_TRUE(config()->ShouldAcceptLitePages(*request.get(),
- *previews_decider.get()));
- }
+ EXPECT_TRUE(config()->ShouldAcceptServerPreview(*request.get(),
+ *previews_decider.get()));
- // Verify PreviewsDecider check.
- {
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxyLoFi,
- switches::kDataReductionProxyLoFiValueAlwaysOn);
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableDataReductionProxyLitePage);
- base::FieldTrialList field_trial_list(nullptr);
- base::FieldTrialList::CreateFieldTrial(
- "DataReductionProxyPreviewsBlackListTransition", "Enabled");
- EXPECT_FALSE(config()->ShouldAcceptLitePages(*request.get(),
- *previews_decider.get()));
- previews_decider = base::MakeUnique<TestPreviewsDecider>(true);
- EXPECT_TRUE(config()->ShouldAcceptLitePages(*request.get(),
- *previews_decider.get()));
- }
+ // DataReductionProxyPreviewsBlackListTransition should not be affected by
+ // lofi being off by the prefs rules.
+ config()->SetLoFiModeOff();
+ EXPECT_TRUE(config()->ShouldAcceptServerPreview(*request.get(),
+ *previews_decider.get()));
}
} // 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 55b792b0239..c3f6d60ffd5 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
@@ -30,6 +30,8 @@ class DataReductionProxyData : public base::SupportsUserData::Data {
~DataReductionProxyData() override;
// Whether the DataReductionProxy was used for this request or navigation.
+ // Also true if the user is the holdback experiment, and the request would
+ // otherwise be eligible to use the proxy.
bool used_data_reduction_proxy() const { return used_data_reduction_proxy_; }
void set_used_data_reduction_proxy(bool used_data_reduction_proxy) {
used_data_reduction_proxy_ = used_data_reduction_proxy;
@@ -105,6 +107,8 @@ class DataReductionProxyData : public base::SupportsUserData::Data {
private:
// Whether the DataReductionProxy was used for this request or navigation.
+ // Also true if the user is the holdback experiment, and the request would
+ // otherwise be eligible to use the proxy.
bool used_data_reduction_proxy_;
// Whether server Lo-Fi was requested for this request or navigation. True if
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 8d433aefdbc..3812a8d531e 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
@@ -40,56 +40,14 @@ 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* DataUseUserDataBytes::kUserDataKey =
&DataUseUserDataBytes::kUserDataKey;
-// Estimate the size of the original headers of |request|. If |used_drp| is
-// true, then it's assumed that the original request would have used HTTP/1.1,
-// otherwise it assumes that the original request would have used the same
-// protocol as |request| did. This is to account for stuff like HTTP/2 header
-// compression.
-int64_t EstimateOriginalHeaderBytes(const net::URLRequest& request,
- bool used_drp) {
- if (used_drp) {
- // TODO(sclittle): Remove headers added by Data Reduction Proxy when
- // computing original size. https://crbug.com/535701.
- return request.response_headers()->raw_headers().size();
- }
- return std::max<int64_t>(0, request.GetTotalReceivedBytes() -
- request.received_response_content_length());
-}
-
-// Given a |request| that went through the Data Reduction Proxy if |used_drp| is
-// true, this function estimates how many bytes would have been received if the
-// response had been received directly from the origin without any data saver
-// optimizations.
-int64_t EstimateOriginalReceivedBytes(
- const net::URLRequest& request,
- bool used_drp,
- const data_reduction_proxy::LoFiDecider* lofi_decider) {
- if (request.was_cached() || !request.response_headers())
- return request.GetTotalReceivedBytes();
-
- if (lofi_decider) {
- if (lofi_decider->IsClientLoFiAutoReloadRequest(request))
- return 0;
-
- int64_t first, last, length;
- if (lofi_decider->IsClientLoFiImageRequest(request) &&
- request.response_headers()->GetContentRangeFor206(&first, &last,
- &length) &&
- length > request.received_response_content_length()) {
- return EstimateOriginalHeaderBytes(request, used_drp) + length;
- }
- }
-
- return used_drp
- ? EstimateOriginalHeaderBytes(request, used_drp) +
- data_reduction_proxy::util::CalculateEffectiveOCL(request)
- : request.GetTotalReceivedBytes();
-}
-
} // namespace
namespace data_reduction_proxy {
@@ -130,8 +88,12 @@ void DataReductionProxyDataUseObserver::OnPageResourceLoad(
data_use_measurement::DataUse* data_use) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (data_use->traffic_type() !=
- data_use_measurement::DataUse::TrafficType::USER_TRAFFIC)
+ if (!request.url().SchemeIs(url::kHttpsScheme) &&
+ !request.url().SchemeIs(url::kHttpScheme)) {
+ return;
+ }
+
+ if (request.GetTotalReceivedBytes() <= 0)
return;
int64_t network_bytes = request.GetTotalReceivedBytes();
@@ -141,11 +103,13 @@ void DataReductionProxyDataUseObserver::OnPageResourceLoad(
// Estimate how many bytes would have been used if the DataReductionProxy was
// not used, and record the data usage.
- int64_t original_bytes = EstimateOriginalReceivedBytes(
+ int64_t original_bytes = util::EstimateOriginalReceivedBytes(
request, request_type == VIA_DATA_REDUCTION_PROXY,
data_reduction_proxy_io_data_->lofi_decider());
- if (!data_use->url().is_valid()) {
+ if (data_use->traffic_type() ==
+ data_use_measurement::DataUse::TrafficType::USER_TRAFFIC &&
+ !data_use->url().is_valid()) {
// URL will be empty until pageload navigation commits. Save the data use of
// these mainframe, subresource, redirected requests in user data until
// then.
@@ -160,7 +124,11 @@ void DataReductionProxyDataUseObserver::OnPageResourceLoad(
}
} else {
data_reduction_proxy_io_data_->UpdateDataUseForHost(
- network_bytes, original_bytes, data_use->url().HostNoBrackets());
+ network_bytes, original_bytes,
+ data_use->traffic_type() ==
+ data_use_measurement::DataUse::TrafficType::USER_TRAFFIC
+ ? data_use->url().HostNoBrackets()
+ : kOtherHostName);
}
}
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 5c4f2c15a4a..a39600bd77d 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
@@ -225,9 +225,14 @@ TEST(DataReductionProxyDelegate, IsTrustedSpdyProxy) {
test_context->io_data()->net_log());
base::FieldTrialList field_trial_list(nullptr);
- base::FieldTrialList::CreateFieldTrial(
- params::GetTrustedSpdyProxyFieldTrialName(),
- test.is_in_trusted_spdy_proxy_field_trial ? "Enabled" : "Control");
+ EXPECT_TRUE(params::IsIncludedInTrustedSpdyProxyFieldTrial());
+ if (!test.is_in_trusted_spdy_proxy_field_trial) {
+ // Trusted Spdy proxy field trial experiment is enabled by default.
+ base::FieldTrialList::CreateFieldTrial(
+ params::GetTrustedSpdyProxyFieldTrialName(), "Control");
+ }
+ EXPECT_EQ(test.is_in_trusted_spdy_proxy_field_trial,
+ params::IsIncludedInTrustedSpdyProxyFieldTrial());
EXPECT_EQ(test.expect_proxy_is_trusted,
delegate.IsTrustedSpdyProxy(first_proxy) ||
@@ -583,24 +588,26 @@ class DataReductionProxyDelegateTest : public testing::Test {
TEST_F(DataReductionProxyDelegateTest, OnResolveProxyHandler) {
GURL url("http://www.google.com/");
-
- // Data reduction proxy info
+ params()->UseNonSecureProxiesForHttp();
+ net::ProxyList proxy_list;
+ proxy_list.AddProxyServer(
+ params()->proxies_for_http().front().proxy_server());
+ proxy_list.AddProxyServer(net::ProxyServer::Direct());
net::ProxyInfo data_reduction_proxy_info;
- std::string data_reduction_proxy;
- base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
- data_reduction_proxy_info.UsePacString(
- "PROXY " +
- net::ProxyServer::FromURI(params()->DefaultOrigin(),
- net::ProxyServer::SCHEME_HTTP)
- .host_port_pair()
- .ToString() +
- "; DIRECT");
+ data_reduction_proxy_info.UseProxyList(proxy_list);
EXPECT_FALSE(data_reduction_proxy_info.is_empty());
// Data reduction proxy config
net::ProxyConfig data_reduction_proxy_config;
data_reduction_proxy_config.proxy_rules().ParseFromString(
- "http=" + data_reduction_proxy + ",direct://;");
+ "http=" +
+ params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server()
+ .host_port_pair()
+ .ToString() +
+ ",direct://;");
data_reduction_proxy_config.set_id(1);
// Other proxy info
@@ -728,17 +735,12 @@ TEST_F(DataReductionProxyDelegateTest, HTTPRequests) {
GURL url(test.url);
net::ProxyInfo data_reduction_proxy_info;
-
- std::string data_reduction_proxy;
if (!test.use_direct_proxy) {
- base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
- data_reduction_proxy_info.UsePacString(
- "PROXY " +
- net::ProxyServer::FromURI(params()->DefaultOrigin(),
- net::ProxyServer::SCHEME_HTTP)
- .host_port_pair()
- .ToString() +
- "; DIRECT");
+ net::ProxyList proxy_list;
+ proxy_list.AddProxyServer(
+ params()->proxies_for_http().front().proxy_server());
+ proxy_list.AddProxyServer(net::ProxyServer::Direct());
+ data_reduction_proxy_info.UseProxyList(proxy_list);
}
EXPECT_EQ(test.use_direct_proxy, data_reduction_proxy_info.is_empty());
@@ -748,7 +750,14 @@ TEST_F(DataReductionProxyDelegateTest, HTTPRequests) {
} else {
data_reduction_proxy_config.proxy_rules().ParseFromString(
- "http=" + data_reduction_proxy + ",direct://;");
+ "http=" +
+ params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server()
+ .host_port_pair()
+ .ToString() +
+ ",direct://;");
data_reduction_proxy_config.set_id(1);
}
EXPECT_NE(test.use_direct_proxy, data_reduction_proxy_config.is_valid());
@@ -793,6 +802,7 @@ TEST_F(DataReductionProxyDelegateTest, OnCompletedSizeFor200) {
"Chrome-Proxy: q=low\r\n"
"Content-Length: 1000\r\n\r\n";
+ params()->UseNonSecureProxiesForHttp();
std::unique_ptr<net::URLRequest> request = FetchURLRequest(
GURL("http://example.com/path/"), nullptr, kDrpResponseHeaders, 1000);
@@ -820,6 +830,7 @@ TEST_F(DataReductionProxyDelegateTest, TimeToFirstHttpDataSaverRequest) {
"Via: 1.1 Chrome-Compression-Proxy-Suffix\r\n"
"Content-Length: 10\r\n\r\n";
+ params()->UseNonSecureProxiesForHttp();
{
base::HistogramTester histogram_tester;
base::TimeDelta advance_time(base::TimeDelta::FromSeconds(1));
@@ -877,6 +888,9 @@ TEST_F(DataReductionProxyDelegateTest, Holdback) {
},
};
for (const auto& test : tests) {
+ if (!test.holdback)
+ params()->UseNonSecureProxiesForHttp();
+
base::FieldTrialList field_trial_list(nullptr);
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
"DataCompressionProxyHoldback", test.holdback ? "Enabled" : "Control"));
@@ -899,6 +913,7 @@ TEST_F(DataReductionProxyDelegateTest, OnCompletedSizeFor304) {
"Via: 1.1 Chrome-Compression-Proxy\r\n"
"X-Original-Content-Length: 10000\r\n\r\n";
+ params()->UseNonSecureProxiesForHttp();
std::unique_ptr<net::URLRequest> request = FetchURLRequest(
GURL("http://example.com/path/"), nullptr, kDrpResponseHeaders, 0);
@@ -916,6 +931,7 @@ TEST_F(DataReductionProxyDelegateTest, OnCompletedSizeForWriteError) {
int64_t baseline_received_bytes = total_received_bytes();
int64_t baseline_original_received_bytes = total_original_received_bytes();
+ params()->UseNonSecureProxiesForHttp();
net::MockWrite writes[] = {
net::MockWrite("GET http://example.com/path/ HTTP/1.1\r\n"
"Host: example.com\r\n"),
@@ -940,6 +956,7 @@ TEST_F(DataReductionProxyDelegateTest, OnCompletedSizeForReadError) {
int64_t baseline_received_bytes = total_received_bytes();
int64_t baseline_original_received_bytes = total_original_received_bytes();
+ params()->UseNonSecureProxiesForHttp();
net::MockRead reads[] = {net::MockRead("HTTP/1.1 "),
net::MockRead(net::ASYNC, net::ERR_ABORTED)};
net::StaticSocketDataProvider socket(reads, arraysize(reads), nullptr, 0);
@@ -1023,6 +1040,7 @@ TEST_F(DataReductionProxyDelegateTest, PartialRangeSavings) {
100, 300},
};
+ params()->UseNonSecureProxiesForHttp();
for (const auto& test : test_cases) {
base::HistogramTester histogram_tester;
int64_t baseline_received_bytes = total_received_bytes();
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 5790d4403d4..75fdb3bfadc 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
@@ -116,8 +116,6 @@ class DataReductionProxyInterceptorTest : public testing::Test {
DataReductionProxyInterceptorTest() {
test_context_ =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(0)
- .WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING)
.Build();
default_context_.reset(new TestURLRequestContextWithDataReductionProxy(
test_context_->config()
@@ -128,6 +126,7 @@ class DataReductionProxyInterceptorTest : public testing::Test {
&default_network_delegate_));
default_context_->set_network_delegate(&default_network_delegate_);
default_context_->set_net_log(test_context_->net_log());
+ test_context_->config()->test_params()->UseNonSecureProxiesForHttp();
}
~DataReductionProxyInterceptorTest() override {
@@ -215,7 +214,6 @@ class DataReductionProxyInterceptorWithServerTest : public testing::Test {
ASSERT_TRUE(direct_.Start());
test_context_ = DataReductionProxyTestContext::Builder()
- .WithParamsFlags(0)
.WithURLRequestContext(&context_)
.Build();
std::string spec;
@@ -301,6 +299,7 @@ class DataReductionProxyInterceptorEndToEndTest : public testing::Test {
.WithURLRequestContext(&context_)
.WithMockClientSocketFactory(&mock_socket_factory_)
.Build();
+ drp_test_context_->config()->test_params()->UseNonSecureProxiesForHttp();
drp_test_context_->AttachToURLRequestContext(&context_storage_);
context_.set_client_socket_factory(&mock_socket_factory_);
proxy_delegate_ = drp_test_context_->io_data()->CreateProxyDelegate();
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 90318bc3eea..e43687a3b97 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
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
#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_compression_stats.h"
@@ -23,7 +24,6 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/data_reduction_proxy/core/browser/data_use_group.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
@@ -92,7 +92,6 @@ BasicHTTPURLRequestContextGetter::~BasicHTTPURLRequestContextGetter() {
DataReductionProxyIOData::DataReductionProxyIOData(
Client client,
- int param_flags,
net::NetLog* net_log,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
@@ -103,7 +102,7 @@ DataReductionProxyIOData::DataReductionProxyIOData(
net_log_(net_log),
io_task_runner_(io_task_runner),
ui_task_runner_(ui_task_runner),
- data_use_group_provider_(nullptr),
+ data_use_observer_(nullptr),
enabled_(enabled),
url_request_context_getter_(nullptr),
basic_url_request_context_getter_(
@@ -114,7 +113,7 @@ DataReductionProxyIOData::DataReductionProxyIOData(
DCHECK(io_task_runner_);
DCHECK(ui_task_runner_);
std::unique_ptr<DataReductionProxyParams> params(
- new DataReductionProxyParams(param_flags));
+ new DataReductionProxyParams());
event_creator_.reset(new DataReductionProxyEventCreator(this));
configurator_.reset(
new DataReductionProxyConfigurator(net_log, event_creator_.get()));
@@ -244,10 +243,6 @@ DataReductionProxyIOData::CreateNetworkDelegate(
request_options_.get(), configurator_.get()));
if (track_proxy_bypass_statistics)
network_delegate->InitIODataAndUMA(this, bypass_stats_.get());
- if (data_use_group_provider_) {
- network_delegate->SetDataUseGroupProvider(
- std::move(data_use_group_provider_));
- }
return network_delegate;
}
@@ -333,7 +328,6 @@ void DataReductionProxyIOData::UpdateContentLengths(
int64_t original_size,
bool data_reduction_proxy_enabled,
DataReductionProxyRequestType request_type,
- const scoped_refptr<DataUseGroup>& data_use_group,
const std::string& mime_type) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
@@ -341,7 +335,7 @@ void DataReductionProxyIOData::UpdateContentLengths(
FROM_HERE,
base::Bind(&DataReductionProxyService::UpdateContentLengths, service_,
data_used, original_size, data_reduction_proxy_enabled,
- request_type, data_use_group, mime_type));
+ request_type, mime_type));
}
void DataReductionProxyIOData::SetLoFiModeActiveOnMainFrame(
@@ -419,4 +413,12 @@ void DataReductionProxyIOData::StoreSerializedConfig(
SetStringPref(prefs::kDataReductionProxyConfig, serialized_config);
}
+void DataReductionProxyIOData::SetDataUseAscriber(
+ data_use_measurement::DataUseAscriber* data_use_ascriber) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ DCHECK(data_use_ascriber);
+ data_use_observer_.reset(
+ new DataReductionProxyDataUseObserver(this, data_use_ascriber));
+}
+
} // 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 951a32bfc92..ed8d971d86f 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
@@ -15,11 +15,11 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
-#include "components/data_reduction_proxy/core/browser/data_use_group_provider.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
#include "components/data_reduction_proxy/core/common/lofi_decider.h"
@@ -53,13 +53,10 @@ class DataReductionProxyService;
// the IO thread.
class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
public:
- // Constructs a DataReductionProxyIOData object. |param_flags| is used to
- // set information about the DNS names used by the proxy, and allowable
- // configurations. |enabled| sets the initial state of the Data Reduction
- // Proxy.
+ // Constructs a DataReductionProxyIOData object. |enabled| sets the initial
+ // state of the Data Reduction Proxy.
DataReductionProxyIOData(
Client client,
- int param_flags,
net::NetLog* net_log,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
@@ -72,6 +69,9 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
// Performs UI thread specific shutdown logic.
void ShutdownOnUIThread();
+ void SetDataUseAscriber(
+ data_use_measurement::DataUseAscriber* data_use_ascriber);
+
// Sets the Data Reduction Proxy service after it has been created.
// Virtual for testing.
virtual void SetDataReductionProxyService(
@@ -127,7 +127,6 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
int64_t original_size,
bool data_reduction_proxy_enabled,
DataReductionProxyRequestType request_type,
- const scoped_refptr<DataUseGroup>& data_usage_source,
const std::string& mime_type);
void SetLoFiModeActiveOnMainFrame(bool lo_fi_mode_active);
@@ -209,11 +208,6 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
resource_type_provider_ = std::move(resource_type_provider);
}
- void set_data_usage_source_provider(
- std::unique_ptr<DataUseGroupProvider> data_usage_source_provider) {
- data_use_group_provider_ = std::move(data_usage_source_provider);
- }
-
// The production channel of this build.
std::string channel() const { return channel_; }
@@ -291,9 +285,8 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
- // Manages instances of |DataUsageSource| and maps |URLRequest| instances to
- // their appropriate |DataUsageSource|.
- std::unique_ptr<DataUseGroupProvider> data_use_group_provider_;
+ // Observes pageload events and records per host data use.
+ std::unique_ptr<DataReductionProxyDataUseObserver> data_use_observer_;
// Whether the Data Reduction Proxy has been enabled or not by the user. In
// practice, this can be overridden by the command line.
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 fb75ff2e24e..4cb09667a6b 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
@@ -103,7 +103,7 @@ class DataReductionProxyIODataTest : public testing::Test {
TEST_F(DataReductionProxyIODataTest, TestConstruction) {
std::unique_ptr<DataReductionProxyIOData> io_data(
- new DataReductionProxyIOData(Client::UNKNOWN, 0, net_log(), task_runner(),
+ new DataReductionProxyIOData(Client::UNKNOWN, net_log(), task_runner(),
task_runner(), false /* enabled */,
std::string() /* user_agent */,
std::string() /* channel */));
@@ -153,7 +153,6 @@ TEST_F(DataReductionProxyIODataTest, TestResetBadProxyListOnDisableDataSaver) {
net::TestURLRequestContext context(false);
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kPromoAllowed)
.WithURLRequestContext(&context)
.SkipSettingsInitialization()
.Build();
@@ -191,10 +190,11 @@ TEST_F(DataReductionProxyIODataTest, TestResetBadProxyListOnDisableDataSaver) {
TEST_F(DataReductionProxyIODataTest, HoldbackConfiguresProxies) {
net::TestURLRequestContext context(false);
+ base::FieldTrialList field_trial_list(nullptr);
+ ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+ "DataCompressionProxyHoldback", "Enabled"));
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kPromoAllowed |
- DataReductionProxyParams::kHoldback)
.WithURLRequestContext(&context)
.SkipSettingsInitialization()
.Build();
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc
index cfe96f4bfc4..53e11ebc18b 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc
@@ -29,27 +29,11 @@ DataReductionProxyRequestType GetDataReductionProxyRequestType(
return UNKNOWN_TYPE;
}
- // Check if the request came through the Data Reduction Proxy before checking
- // if proxies are bypassed, to avoid misreporting cases where the Data
- // Reduction Proxy was bypassed between the request being sent out and the
- // response coming in. For 304 responses, check if the request was sent to the
- // Data Reduction Proxy, since 304s aren't required to have a Via header even
- // if they came through the Data Reduction Proxy.
if (request.response_headers() &&
- (HasDataReductionProxyViaHeader(*request.response_headers(), nullptr) ||
- (request.response_headers()->response_code() == net::HTTP_NOT_MODIFIED &&
- config.WasDataReductionProxyUsed(&request, nullptr)))) {
+ HasDataReductionProxyViaHeader(*request.response_headers(), nullptr)) {
return VIA_DATA_REDUCTION_PROXY;
}
- base::TimeDelta bypass_delay;
- if (config.AreDataReductionProxiesBypassed(
- request, data_reduction_proxy_config, &bypass_delay)) {
- if (bypass_delay > base::TimeDelta::FromSeconds(kLongBypassDelayInSeconds))
- return LONG_BYPASS;
- return SHORT_BYPASS;
- }
-
// Treat bypasses that only apply to the individual request as SHORT_BYPASS.
// This includes bypasses triggered by "Chrome-Proxy: block-once", bypasses
// due to other proxies overriding the Data Reduction Proxy, and bypasses due
@@ -63,6 +47,31 @@ DataReductionProxyRequestType GetDataReductionProxyRequestType(
return SHORT_BYPASS;
}
+ if (request.proxy_server().is_direct() ||
+ !request.proxy_server().is_valid()) {
+ return DIRECT_HTTP;
+ }
+
+ base::TimeDelta bypass_delay;
+ if (config.AreDataReductionProxiesBypassed(
+ request, data_reduction_proxy_config, &bypass_delay)) {
+ if (bypass_delay > base::TimeDelta::FromSeconds(kLongBypassDelayInSeconds))
+ return LONG_BYPASS;
+ return SHORT_BYPASS;
+ }
+
+ // Check if the request came through the Data Reduction Proxy before checking
+ // if proxies are bypassed, to avoid misreporting cases where the Data
+ // Reduction Proxy was bypassed between the request being sent out and the
+ // response coming in. For 304 responses, check if the request was sent to the
+ // Data Reduction Proxy, since 304s aren't required to have a Via header even
+ // if they came through the Data Reduction Proxy.
+ if (request.response_headers() &&
+ request.response_headers()->response_code() == net::HTTP_NOT_MODIFIED &&
+ config.WasDataReductionProxyUsed(&request, nullptr)) {
+ return VIA_DATA_REDUCTION_PROXY;
+ }
+
return UNKNOWN_TYPE;
}
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
index c1ff06552b3..9c52af5940d 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
@@ -40,6 +40,7 @@ enum DataReductionProxyRequestType {
LONG_BYPASS, // The client is bypassed by the proxy for a long time (due
// to country bypass policy, for example).
UPDATE, // An update to already counted request data.
+ DIRECT_HTTP, // An http request with a disabled data reduction proxy.
UNKNOWN_TYPE, // Any other reason not listed above.
};
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics_unittest.cc
index 59f6f3d8825..3eaf9e5bbda 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics_unittest.cc
@@ -35,10 +35,9 @@ TEST(ChromeNetworkDailyDataSavingMetricsTest,
base::MessageLoopForIO message_loop;
std::unique_ptr<DataReductionProxyTestContext> test_context =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(0)
- .WithParamsDefinitions(TestDataReductionProxyParams::HAS_ORIGIN)
.Build();
TestDataReductionProxyConfig* config = test_context->config();
+ config->test_params()->UseNonSecureProxiesForHttp();
net::ProxyServer origin =
config->test_params()->proxies_for_http().front().proxy_server();
@@ -69,24 +68,40 @@ TEST(ChromeNetworkDailyDataSavingMetricsTest,
{
GURL("http://foo.com"), net::ProxyServer::Direct(),
base::TimeDelta::FromSeconds(1), net::LOAD_NORMAL,
- "HTTP/1.1 200 OK\r\n\r\n", SHORT_BYPASS,
+ "HTTP/1.1 200 OK\r\n\r\n", DIRECT_HTTP,
},
{
GURL("http://foo.com"), net::ProxyServer::Direct(),
base::TimeDelta::FromSeconds(1), net::LOAD_NORMAL,
- "HTTP/1.1 304 Not Modified\r\n\r\n", SHORT_BYPASS,
+ "HTTP/1.1 304 Not Modified\r\n\r\n", DIRECT_HTTP,
},
{
GURL("http://foo.com"), net::ProxyServer::Direct(),
base::TimeDelta::FromMinutes(60), net::LOAD_NORMAL,
- "HTTP/1.1 200 OK\r\n\r\n", LONG_BYPASS,
+ "HTTP/1.1 200 OK\r\n\r\n", DIRECT_HTTP,
},
{
GURL("http://foo.com"), net::ProxyServer::Direct(),
base::TimeDelta::FromMinutes(60), net::LOAD_NORMAL,
- "HTTP/1.1 304 Not Modified\r\n\r\n", LONG_BYPASS,
+ "HTTP/1.1 304 Not Modified\r\n\r\n", DIRECT_HTTP,
},
- // Requests with LOAD_BYPASS_PROXY (e.g. block-once) should be classified
+ {
+ GURL("http://foo.com"), origin, base::TimeDelta::FromSeconds(1),
+ net::LOAD_NORMAL, "HTTP/1.1 200 OK\r\n\r\n", SHORT_BYPASS,
+ },
+ {
+ GURL("http://foo.com"), origin, base::TimeDelta::FromSeconds(1),
+ net::LOAD_NORMAL, "HTTP/1.1 304 Not Modified\r\n\r\n", SHORT_BYPASS,
+ },
+ {
+ GURL("http://foo.com"), origin, base::TimeDelta::FromMinutes(60),
+ net::LOAD_NORMAL, "HTTP/1.1 200 OK\r\n\r\n", LONG_BYPASS,
+ },
+ {
+ GURL("http://foo.com"), origin, base::TimeDelta::FromMinutes(60),
+ net::LOAD_NORMAL, "HTTP/1.1 304 Not Modified\r\n\r\n", LONG_BYPASS,
+ }, // Requests with LOAD_BYPASS_PROXY (e.g. block-once) should be
+ // classified
// as SHORT_BYPASS.
{
GURL("http://foo.com"), net::ProxyServer::Direct(), base::TimeDelta(),
@@ -107,12 +122,12 @@ TEST(ChromeNetworkDailyDataSavingMetricsTest,
base::TimeDelta(), net::LOAD_NORMAL, "HTTP/1.1 200 OK\r\n\r\n",
SHORT_BYPASS,
},
- // Responses that seem like they should have come through the Data
- // Reduction Proxy, but did not, should be classified as UNKNOWN_TYPE.
{
GURL("http://foo.com"), net::ProxyServer::Direct(), base::TimeDelta(),
- net::LOAD_NORMAL, "HTTP/1.1 200 OK\r\n\r\n", UNKNOWN_TYPE,
+ net::LOAD_NORMAL, "HTTP/1.1 200 OK\r\n\r\n", DIRECT_HTTP,
},
+ // Responses that seem like they should have come through the Data
+ // Reduction Proxy, but did not, should be classified as UNKNOWN_TYPE.
{
GURL("http://foo.com"), origin, base::TimeDelta(), net::LOAD_NORMAL,
"HTTP/1.1 200 OK\r\n\r\n", UNKNOWN_TYPE,
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
index acb1d468538..4985b9a284e 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
@@ -16,16 +16,11 @@ DataReductionProxyMutableConfigValues::CreateFromParams(
const DataReductionProxyParams* params) {
std::unique_ptr<DataReductionProxyMutableConfigValues> config_values(
new DataReductionProxyMutableConfigValues());
- config_values->promo_allowed_ = params->promo_allowed();
- config_values->holdback_ = params->holdback();
- config_values->secure_proxy_check_url_ = params->secure_proxy_check_url();
return config_values;
}
DataReductionProxyMutableConfigValues::DataReductionProxyMutableConfigValues()
- : promo_allowed_(false),
- holdback_(false),
- use_override_proxies_for_http_(false) {
+ : use_override_proxies_for_http_(false) {
use_override_proxies_for_http_ =
params::GetOverrideProxiesForHttpFromCommandLine(
&override_proxies_for_http_);
@@ -38,14 +33,6 @@ DataReductionProxyMutableConfigValues::
~DataReductionProxyMutableConfigValues() {
}
-bool DataReductionProxyMutableConfigValues::promo_allowed() const {
- return promo_allowed_;
-}
-
-bool DataReductionProxyMutableConfigValues::holdback() const {
- return holdback_;
-}
-
const std::vector<DataReductionProxyServer>&
DataReductionProxyMutableConfigValues::proxies_for_http() const {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -62,11 +49,6 @@ DataReductionProxyMutableConfigValues::proxies_for_http() const {
return proxies_for_http_;
}
-const GURL& DataReductionProxyMutableConfigValues::secure_proxy_check_url()
- const {
- return secure_proxy_check_url_;
-}
-
void DataReductionProxyMutableConfigValues::UpdateValues(
const std::vector<DataReductionProxyServer>& proxies_for_http) {
DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
index c1906082c49..5e97de0b54e 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
@@ -40,20 +40,14 @@ class DataReductionProxyMutableConfigValues
void Invalidate();
// Overrides of |DataReductionProxyConfigValues|
- bool promo_allowed() const override;
- bool holdback() const override;
const std::vector<DataReductionProxyServer>& proxies_for_http()
const override;
- const GURL& secure_proxy_check_url() const override;
protected:
DataReductionProxyMutableConfigValues();
private:
- bool promo_allowed_;
- bool holdback_;
std::vector<DataReductionProxyServer> proxies_for_http_;
- GURL secure_proxy_check_url_;
// Permits use of locally specified Data Reduction Proxy servers instead of
// ones specified from the Data Saver API.
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
index 8753724173d..2e0917f62b6 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
@@ -24,7 +24,7 @@ class DataReductionProxyMutableConfigValuesTest : public testing::Test {
~DataReductionProxyMutableConfigValuesTest() override {}
void Init() {
- params_.reset(new DataReductionProxyParams(0));
+ params_.reset(new DataReductionProxyParams());
mutable_config_values_ =
DataReductionProxyMutableConfigValues::CreateFromParams(params_.get());
}
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 97fbec5be1c..ca8d3b05dab 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
@@ -21,8 +21,6 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
-#include "components/data_reduction_proxy/core/browser/data_use_group.h"
-#include "components/data_reduction_proxy/core/browser/data_use_group_provider.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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
@@ -45,6 +43,22 @@ namespace data_reduction_proxy {
namespace {
+// Values of the UMA DataReductionProxy.Protocol.AcceptTransform histogram
+// defined in metrics/histograms/histograms.xml. This enum must remain
+// synchronized with DataReductionProxyProtocolAcceptTransformEvent in
+// tools/metrics/histograms/enums.xml.
+enum AcceptTransformEvent {
+ LITE_PAGE_REQUESTED = 0,
+ LITE_PAGE_TRANSFORM_RECEIVED = 1,
+ EMPTY_IMAGE_POLICY_DIRECTIVE_RECEIVED = 2,
+ EMPTY_IMAGE_REQUESTED = 3,
+ EMPTY_IMAGE_TRANSFORM_RECEIVED = 4,
+ COMPRESSED_VIDEO_REQUESTED = 5,
+ IDENTITY_TRANSFORM_REQUESTED = 6,
+ IDENTITY_TRANSFORM_RECEIVED = 7,
+ ACCEPT_TRANSFORM_EVENT_BOUNDARY
+};
+
// Records the occurrence of |sample| in |name| histogram. UMA macros are not
// used because the |name| is not static.
void RecordNewContentLengthHistogram(const std::string& name, int64_t sample) {
@@ -72,6 +86,7 @@ void RecordNewContentLengthHistograms(
suffix = ".ViaDRP";
break;
case HTTPS:
+ case DIRECT_HTTP:
suffix = ".Direct";
break;
case SHORT_BYPASS:
@@ -138,9 +153,10 @@ void RecordContentLengthHistograms(bool lofi_low_header_added,
UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLength", received_content_length);
// Record the new histograms broken down by HTTP/HTTPS and video/non-video
- RecordNewContentLengthHistograms("Net.HttpContentLength", is_https, is_video,
- request_type, received_content_length);
- RecordNewContentLengthHistograms("Net.HttpOriginalContentLength", is_https,
+ RecordNewContentLengthHistograms("Net.HttpContentLengthV2", is_https,
+ is_video, request_type,
+ received_content_length);
+ RecordNewContentLengthHistograms("Net.HttpOriginalContentLengthV2", is_https,
is_video, request_type,
original_content_length);
@@ -167,52 +183,60 @@ void RecordContentLengthHistograms(bool lofi_low_header_added,
received_content_length);
}
-// Estimate the size of the original headers of |request|. If |used_drp| is
-// true, then it's assumed that the original request would have used HTTP/1.1,
-// otherwise it assumes that the original request would have used the same
-// protocol as |request| did. This is to account for stuff like HTTP/2 header
-// compression.
-// TODO(rajendrant): Remove this method when data use ascriber observers are
-// used to record the per-site data usage.
-int64_t EstimateOriginalHeaderBytes(const net::URLRequest& request,
- bool used_drp) {
- if (used_drp) {
- // TODO(sclittle): Remove headers added by Data Reduction Proxy when
- // computing original size. https://crbug.com/535701.
- return request.response_headers()->raw_headers().size();
- }
- return std::max<int64_t>(0, request.GetTotalReceivedBytes() -
- request.received_response_content_length());
+void RecordAcceptTransformEvent(AcceptTransformEvent event) {
+ UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.Protocol.AcceptTransform",
+ event, ACCEPT_TRANSFORM_EVENT_BOUNDARY);
}
-// Given a |request| that went through the Data Reduction Proxy if |used_drp| is
-// true, this function estimates how many bytes would have been received if the
-// response had been received directly from the origin without any data saver
-// optimizations.
-// TODO(rajendrant): Remove this method when data use ascriber observers are
-// used to record the per-site data usage.
-int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request,
- bool used_drp,
- const LoFiDecider* lofi_decider) {
- if (request.was_cached() || !request.response_headers())
- return request.GetTotalReceivedBytes();
-
- if (lofi_decider) {
- if (lofi_decider->IsClientLoFiAutoReloadRequest(request))
- return 0;
-
- int64_t first, last, length;
- if (lofi_decider->IsClientLoFiImageRequest(request) &&
- request.response_headers()->GetContentRangeFor206(&first, &last,
- &length) &&
- length > request.received_response_content_length()) {
- return EstimateOriginalHeaderBytes(request, used_drp) + length;
- }
+void RecordAcceptTransformSentUMA(
+ const net::HttpRequestHeaders& request_headers) {
+ switch (ParseRequestTransform(request_headers)) {
+ case TRANSFORM_LITE_PAGE:
+ RecordAcceptTransformEvent(LITE_PAGE_REQUESTED);
+ break;
+ case TRANSFORM_EMPTY_IMAGE:
+ RecordAcceptTransformEvent(EMPTY_IMAGE_REQUESTED);
+ break;
+ case TRANSFORM_COMPRESSED_VIDEO:
+ RecordAcceptTransformEvent(COMPRESSED_VIDEO_REQUESTED);
+ break;
+ case TRANSFORM_IDENTITY:
+ RecordAcceptTransformEvent(IDENTITY_TRANSFORM_REQUESTED);
+ break;
+ case TRANSFORM_NONE:
+ break;
+ case TRANSFORM_PAGE_POLICIES_EMPTY_IMAGE:
+ NOTREACHED();
+ break;
+ }
+}
+
+void RecordAcceptTransformReceivedUMA(const net::URLRequest& request) {
+ net::HttpResponseHeaders* response_headers = request.response_headers();
+ if (!response_headers) {
+ return;
}
- return used_drp ? EstimateOriginalHeaderBytes(request, used_drp) +
- util::CalculateEffectiveOCL(request)
- : request.GetTotalReceivedBytes();
+ switch (ParseResponseTransform(*response_headers)) {
+ case TRANSFORM_LITE_PAGE:
+ RecordAcceptTransformEvent(LITE_PAGE_TRANSFORM_RECEIVED);
+ break;
+ case TRANSFORM_PAGE_POLICIES_EMPTY_IMAGE:
+ RecordAcceptTransformEvent(EMPTY_IMAGE_POLICY_DIRECTIVE_RECEIVED);
+ break;
+ case TRANSFORM_EMPTY_IMAGE:
+ RecordAcceptTransformEvent(EMPTY_IMAGE_TRANSFORM_RECEIVED);
+ break;
+ case TRANSFORM_IDENTITY:
+ RecordAcceptTransformEvent(IDENTITY_TRANSFORM_RECEIVED);
+ break;
+ case TRANSFORM_NONE:
+ break;
+ case TRANSFORM_COMPRESSED_VIDEO:
+ // Compressed video response would instead be a redirect to resource.
+ NOTREACHED();
+ break;
+ }
}
// Verifies that the chrome proxy related request headers are set correctly.
@@ -277,15 +301,6 @@ void DataReductionProxyNetworkDelegate::OnBeforeURLRequestInternal(
const net::CompletionCallback& callback,
GURL* new_url) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (data_use_group_provider_) {
- // Creates and initializes a |DataUseGroup| for the |request| if it does not
- // exist. Even though we do not use the |DataUseGroup| here, we want to
- // associate one with a request as early as possible in case the frame
- // associated with the request goes away before the request is completed.
- scoped_refptr<DataUseGroup> data_use_group =
- data_use_group_provider_->GetDataUseGroup(request);
- data_use_group->Initialize();
- }
// |data_reduction_proxy_io_data_| can be NULL for Webview.
if (data_reduction_proxy_io_data_ &&
@@ -321,7 +336,10 @@ void DataReductionProxyNetworkDelegate::OnBeforeStartTransactionInternal(
if (data_reduction_proxy_io_data_->lofi_decider()) {
data_reduction_proxy_io_data_->lofi_decider()
->MaybeSetAcceptTransformHeader(
- *request, data_reduction_proxy_config_->lofi_off(), headers);
+ *request,
+ !params::IsBlackListEnabledForServerPreviews() &&
+ data_reduction_proxy_config_->lofi_off(),
+ headers);
}
MaybeAddChromeProxyECTHeader(headers, *request);
@@ -349,20 +367,7 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
// Reset |request|'s DataReductionProxyData.
DataReductionProxyData::ClearData(request);
-
- if (params::IsIncludedInHoldbackFieldTrial()) {
- if (WasEligibleWithoutHoldback(*request, proxy_info, proxy_retry_info)) {
- // For the holdback field trial, still log UMA as if the proxy was used.
- data = DataReductionProxyData::GetDataAndCreateIfNecessary(request);
- if (data)
- data->set_used_data_reduction_proxy(true);
- }
- // If holdback is enabled, |proxy_info| must not contain a data reduction
- // proxy.
- DCHECK(proxy_info.is_empty() ||
- !data_reduction_proxy_config_->IsDataReductionProxy(
- proxy_info.proxy_server(), nullptr));
- }
+ data = nullptr;
bool using_data_reduction_proxy = true;
// The following checks rule out direct, invalid, and other connection types.
@@ -375,10 +380,45 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
proxy_info.proxy_server(), nullptr)) {
using_data_reduction_proxy = false;
}
+
+ bool is_holdback_eligible = false;
+
+ if (params::IsIncludedInHoldbackFieldTrial() &&
+ WasEligibleWithoutHoldback(*request, proxy_info, proxy_retry_info)) {
+ is_holdback_eligible = true;
+ }
// If holdback is enabled, |using_data_reduction_proxy| must be false.
DCHECK(!params::IsIncludedInHoldbackFieldTrial() ||
!using_data_reduction_proxy);
+ // For the holdback field trial, still log UMA and send the pingback as if
+ // the proxy were used.
+ if (is_holdback_eligible || using_data_reduction_proxy) {
+ // Retrieves DataReductionProxyData from a request, creating a new instance
+ // if needed.
+ data = DataReductionProxyData::GetDataAndCreateIfNecessary(request);
+ data->set_used_data_reduction_proxy(true);
+ // Only set GURL, NQE and session key string for main frame requests since
+ // they are not needed for sub-resources.
+ if (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) {
+ data->set_session_key(
+ data_reduction_proxy_request_options_->GetSecureSession());
+ data->set_request_url(request->url());
+ if (request->context()->network_quality_estimator()) {
+ data->set_effective_connection_type(request->context()
+ ->network_quality_estimator()
+ ->GetEffectiveConnectionType());
+ }
+ // Generate a page ID for main frame requests that don't already have one.
+ // TODO(ryansturm): remove LOAD_MAIN_FRAME_DEPRECATED from d_r_p.
+ // crbug.com/709621
+ if (!page_id) {
+ page_id = data_reduction_proxy_request_options_->GeneratePageId();
+ }
+ data->set_page_id(page_id.value());
+ }
+ }
+
LoFiDecider* lofi_decider = nullptr;
if (data_reduction_proxy_io_data_)
lofi_decider = data_reduction_proxy_io_data_->lofi_decider();
@@ -395,25 +435,7 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
return;
}
- // Retrieves DataReductionProxyData from a request, creating a new instance
- // if needed.
- data = DataReductionProxyData::GetDataAndCreateIfNecessary(request);
- if (data) {
- data->set_used_data_reduction_proxy(true);
- // Only set GURL, NQE and session key string for main frame requests since
- // they are not needed for sub-resources.
- if (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) {
- data->set_session_key(
- data_reduction_proxy_request_options_->GetSecureSession());
- data->set_request_url(request->url());
- if (request->context()->network_quality_estimator()) {
- data->set_effective_connection_type(request->context()
- ->network_quality_estimator()
- ->GetEffectiveConnectionType());
- }
- }
- }
-
+ DCHECK(data);
if (data_reduction_proxy_io_data_ &&
(request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED)) {
data_reduction_proxy_io_data_->SetLoFiModeActiveOnMainFrame(
@@ -421,25 +443,14 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
: false);
}
- if (data) {
- data->set_lofi_requested(
- lofi_decider ? lofi_decider->ShouldRecordLoFiUMA(*request) : false);
- }
+ data->set_lofi_requested(
+ lofi_decider ? lofi_decider->ShouldRecordLoFiUMA(*request) : false);
MaybeAddBrotliToAcceptEncodingHeader(proxy_info, headers, *request);
- // Generate a page ID for main frame requests that don't already have one.
- // TODO(ryansturm): remove LOAD_MAIN_FRAME_DEPRECATED from d_r_p.
- // crbug.com/709621
- if (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) {
- if (!page_id) {
- page_id = data_reduction_proxy_request_options_->GeneratePageId();
- }
- data->set_page_id(page_id.value());
- }
-
data_reduction_proxy_request_options_->AddRequestHeader(headers, page_id);
VerifyHttpRequestHeaders(true, *headers);
+ RecordAcceptTransformSentUMA(*headers);
}
void DataReductionProxyNetworkDelegate::OnBeforeRedirectInternal(
@@ -523,6 +534,7 @@ void DataReductionProxyNetworkDelegate::OnCompletedInternal(
CalculateAndRecordDataUsage(*request, request_type);
RecordContentLength(*request, request_type, original_content_length);
+ RecordAcceptTransformReceivedUMA(*request);
}
void DataReductionProxyNetworkDelegate::OnHeadersReceivedInternal(
@@ -561,7 +573,7 @@ void DataReductionProxyNetworkDelegate::CalculateAndRecordDataUsage(
// Estimate how many bytes would have been used if the DataReductionProxy was
// not used, and record the data usage.
- int64_t original_size = EstimateOriginalReceivedBytes(
+ int64_t original_size = util::EstimateOriginalReceivedBytes(
request, request_type == VIA_DATA_REDUCTION_PROXY,
data_reduction_proxy_io_data_
? data_reduction_proxy_io_data_->lofi_decider()
@@ -571,19 +583,13 @@ void DataReductionProxyNetworkDelegate::CalculateAndRecordDataUsage(
if (request.response_headers())
request.response_headers()->GetMimeType(&mime_type);
- scoped_refptr<DataUseGroup> data_use_group =
- data_use_group_provider_
- ? data_use_group_provider_->GetDataUseGroup(&request)
- : nullptr;
- AccumulateDataUsage(data_used, original_size, request_type, data_use_group,
- mime_type);
+ AccumulateDataUsage(data_used, original_size, request_type, mime_type);
}
void DataReductionProxyNetworkDelegate::AccumulateDataUsage(
int64_t data_used,
int64_t original_size,
DataReductionProxyRequestType request_type,
- const scoped_refptr<DataUseGroup>& data_use_group,
const std::string& mime_type) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_GE(data_used, 0);
@@ -591,7 +597,7 @@ void DataReductionProxyNetworkDelegate::AccumulateDataUsage(
if (data_reduction_proxy_io_data_) {
data_reduction_proxy_io_data_->UpdateContentLengths(
data_used, original_size, data_reduction_proxy_io_data_->IsEnabled(),
- request_type, data_use_group, mime_type);
+ request_type, mime_type);
}
}
@@ -662,12 +668,6 @@ bool DataReductionProxyNetworkDelegate::WasEligibleWithoutHoldback(
&data_reduction_proxy_info);
}
-void DataReductionProxyNetworkDelegate::SetDataUseGroupProvider(
- std::unique_ptr<DataUseGroupProvider> data_use_group_provider) {
- DCHECK(thread_checker_.CalledOnValidThread());
- data_use_group_provider_ = std::move(data_use_group_provider);
-}
-
void DataReductionProxyNetworkDelegate::MaybeAddBrotliToAcceptEncodingHeader(
const net::ProxyInfo& proxy_info,
net::HttpRequestHeaders* request_headers,
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 600445d9eb1..68342475af6 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
@@ -35,8 +35,6 @@ class DataReductionProxyConfig;
class DataReductionProxyConfigurator;
class DataReductionProxyIOData;
class DataReductionProxyRequestOptions;
-class DataUseGroupProvider;
-class DataUseGroup;
// Values of the UMA DataReductionProxy.LoFi.TransformationType histogram.
// This enum must remain synchronized with
@@ -76,9 +74,6 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
DataReductionProxyIOData* io_data,
DataReductionProxyBypassStats* bypass_stats);
- void SetDataUseGroupProvider(
- std::unique_ptr<DataUseGroupProvider> data_use_group_provider);
-
private:
friend class DataReductionProxyTestContext;
@@ -136,7 +131,6 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
void AccumulateDataUsage(int64_t data_used,
int64_t original_size,
DataReductionProxyRequestType request_type,
- const scoped_refptr<DataUseGroup>& data_use_group,
const std::string& mime_type);
// Record information such as histograms related to the Content-Length of
@@ -188,8 +182,6 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
const DataReductionProxyConfigurator* configurator_;
- std::unique_ptr<DataUseGroupProvider> data_use_group_provider_;
-
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyNetworkDelegate);
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 fe69c111390..46b1d24828b 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
@@ -18,12 +18,15 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/numerics/safe_conversions.h"
+#include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h"
+#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/mock_entropy_provider.h"
+#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
@@ -32,6 +35,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/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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
@@ -86,40 +90,40 @@ const char kDifferenceValidOCLHistogramName[] =
// HTTP original content length
const char kOriginalInsecureDirectHistogramName[] =
- "Net.HttpOriginalContentLength.Http.Direct";
+ "Net.HttpOriginalContentLengthV2.Http.Direct";
const char kOriginalInsecureViaDRPHistogramName[] =
- "Net.HttpOriginalContentLength.Http.ViaDRP";
+ "Net.HttpOriginalContentLengthV2.Http.ViaDRP";
const char kOriginalInsecureBypassedHistogramName[] =
- "Net.HttpOriginalContentLength.Http.BypassedDRP";
+ "Net.HttpOriginalContentLengthV2.Http.BypassedDRP";
const char kOriginalInsecureOtherHistogramName[] =
- "Net.HttpOriginalContentLength.Http.Other";
+ "Net.HttpOriginalContentLengthV2.Http.Other";
// HTTP video original content length
const char kOriginalVideoInsecureDirectHistogramName[] =
- "Net.HttpOriginalContentLength.Http.Direct.Video";
+ "Net.HttpOriginalContentLengthV2.Http.Direct.Video";
const char kOriginalVideoInsecureViaDRPHistogramName[] =
- "Net.HttpOriginalContentLength.Http.ViaDRP.Video";
+ "Net.HttpOriginalContentLengthV2.Http.ViaDRP.Video";
const char kOriginalVideoInsecureBypassedHistogramName[] =
- "Net.HttpOriginalContentLength.Http.BypassedDRP.Video";
+ "Net.HttpOriginalContentLengthV2.Http.BypassedDRP.Video";
const char kOriginalVideoInsecureOtherHistogramName[] =
- "Net.HttpOriginalContentLength.Http.Other.Video";
+ "Net.HttpOriginalContentLengthV2.Http.Other.Video";
// HTTPS original content length
const char kOriginalSecureDirectHistogramName[] =
- "Net.HttpOriginalContentLength.Https.Direct";
+ "Net.HttpOriginalContentLengthV2.Https.Direct";
const char kOriginalSecureViaDRPHistogramName[] =
- "Net.HttpOriginalContentLength.Https.ViaDRP";
+ "Net.HttpOriginalContentLengthV2.Https.ViaDRP";
const char kOriginalSecureBypassedHistogramName[] =
- "Net.HttpOriginalContentLength.Https.BypassedDRP";
+ "Net.HttpOriginalContentLengthV2.Https.BypassedDRP";
const char kOriginalSecureOtherHistogramName[] =
- "Net.HttpOriginalContentLength.Https.Other";
+ "Net.HttpOriginalContentLengthV2.Https.Other";
// HTTPS video original content length
const char kOriginalVideoSecureDirectHistogramName[] =
- "Net.HttpOriginalContentLength.Https.Direct.Video";
+ "Net.HttpOriginalContentLengthV2.Https.Direct.Video";
const char kOriginalVideoSecureViaDRPHistogramName[] =
- "Net.HttpOriginalContentLength.Https.ViaDRP.Video";
+ "Net.HttpOriginalContentLengthV2.Https.ViaDRP.Video";
const char kOriginalVideoSecureBypassedHistogramName[] =
- "Net.HttpOriginalContentLength.Https.BypassedDRP.Video";
+ "Net.HttpOriginalContentLengthV2.Https.BypassedDRP.Video";
const char kOriginalVideoSecureOtherHistogramName[] =
- "Net.HttpOriginalContentLength.Https.Other.Video";
+ "Net.HttpOriginalContentLengthV2.Https.Other.Video";
// Lo-Fi histograms.
const char kReceivedValidOCLLoFiOnHistogramName[] =
@@ -131,37 +135,37 @@ const char kDifferenceValidOCLLoFiOnHistogramName[] =
const char kReceivedHistogramName[] = "Net.HttpContentLength";
const char kReceivedInsecureDirectHistogramName[] =
- "Net.HttpContentLength.Http.Direct";
+ "Net.HttpContentLengthV2.Http.Direct";
const char kReceivedInsecureViaDRPHistogramName[] =
- "Net.HttpContentLength.Http.ViaDRP";
+ "Net.HttpContentLengthV2.Http.ViaDRP";
const char kReceivedInsecureBypassedHistogramName[] =
- "Net.HttpContentLength.Http.BypassedDRP";
+ "Net.HttpContentLengthV2.Http.BypassedDRP";
const char kReceivedInsecureOtherHistogramName[] =
- "Net.HttpContentLength.Http.Other";
+ "Net.HttpContentLengthV2.Http.Other";
const char kReceivedSecureDirectHistogramName[] =
- "Net.HttpContentLength.Https.Direct";
+ "Net.HttpContentLengthV2.Https.Direct";
const char kReceivedSecureViaDRPHistogramName[] =
- "Net.HttpContentLength.Https.ViaDRP";
+ "Net.HttpContentLengthV2.Https.ViaDRP";
const char kReceivedSecureBypassedHistogramName[] =
- "Net.HttpContentLength.Https.BypassedDRP";
+ "Net.HttpContentLengthV2.Https.BypassedDRP";
const char kReceivedSecureOtherHistogramName[] =
- "Net.HttpContentLength.Https.Other";
+ "Net.HttpContentLengthV2.Https.Other";
const char kReceivedVideoInsecureDirectHistogramName[] =
- "Net.HttpContentLength.Http.Direct.Video";
+ "Net.HttpContentLengthV2.Http.Direct.Video";
const char kReceivedVideoInsecureViaDRPHistogramName[] =
- "Net.HttpContentLength.Http.ViaDRP.Video";
+ "Net.HttpContentLengthV2.Http.ViaDRP.Video";
const char kReceivedVideoInsecureBypassedHistogramName[] =
- "Net.HttpContentLength.Http.BypassedDRP.Video";
+ "Net.HttpContentLengthV2.Http.BypassedDRP.Video";
const char kReceivedVideoInsecureOtherHistogramName[] =
- "Net.HttpContentLength.Http.Other.Video";
+ "Net.HttpContentLengthV2.Http.Other.Video";
const char kReceivedVideoSecureDirectHistogramName[] =
- "Net.HttpContentLength.Https.Direct.Video";
+ "Net.HttpContentLengthV2.Https.Direct.Video";
const char kReceivedVideoSecureViaDRPHistogramName[] =
- "Net.HttpContentLength.Https.ViaDRP.Video";
+ "Net.HttpContentLengthV2.Https.ViaDRP.Video";
const char kReceivedVideoSecureBypassedHistogramName[] =
- "Net.HttpContentLength.Https.BypassedDRP.Video";
+ "Net.HttpContentLengthV2.Https.BypassedDRP.Video";
const char kReceivedVideoSecureOtherHistogramName[] =
- "Net.HttpContentLength.Https.Other.Video";
+ "Net.HttpContentLengthV2.Https.Other.Video";
const char kOriginalHistogramName[] = "Net.HttpOriginalContentLength";
const char kDifferenceHistogramName[] = "Net.HttpContentLengthDifference";
const char kFreshnessLifetimeHistogramName[] =
@@ -238,7 +242,8 @@ class TestLoFiDecider : public LoFiDecider {
std::string header_value;
if (headers.GetHeader(chrome_proxy_accept_transform_header(),
&header_value)) {
- return header_value == empty_image_directive();
+ return header_value == empty_image_directive() ||
+ header_value == lite_page_directive();
}
return false;
}
@@ -309,13 +314,14 @@ class TestPreviewsDecider : public previews::PreviewsDecider {
bool ShouldAllowPreviewAtECT(
const net::URLRequest& request,
previews::PreviewsType type,
- net::EffectiveConnectionType effective_connection_type_threshold)
+ net::EffectiveConnectionType effective_connection_type_threshold,
+ const std::vector<std::string>& host_blacklist_from_server)
const override {
return true;
}
// Same as ShouldAllowPreviewAtECT, but uses the previews default
- // EffectiveConnectionType.
+ // EffectiveConnectionType and no blacklisted hosts from the server.
bool ShouldAllowPreview(const net::URLRequest& request,
previews::PreviewsType type) const override {
return true;
@@ -672,7 +678,7 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
}
}
- void FetchURLRequestAndVerifyPageIdDirective(const std::string& page_id_value,
+ void FetchURLRequestAndVerifyPageIdDirective(base::Optional<uint64_t> page_id,
bool redirect_once) {
std::string response_headers =
"HTTP/1.1 200 OK\r\n"
@@ -706,6 +712,15 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
EXPECT_FALSE(
io_data()->test_request_options()->GetHeaderValueForTesting().empty());
+ std::string page_id_value;
+ if (page_id) {
+ char page_id_buffer[17];
+ if (base::strings::SafeSPrintf(page_id_buffer, "%x", page_id.value()) >
+ 0) {
+ page_id_value = std::string("pid=") + page_id_buffer;
+ }
+ }
+
std::string mock_write =
"GET http://www.google.com/ HTTP/1.1\r\nHost: "
"www.google.com\r\nProxy-Connection: "
@@ -870,8 +885,8 @@ TEST_F(DataReductionProxyNetworkDelegateTest, AuthenticationTest) {
net::ProxyInfo data_reduction_proxy_info;
net::ProxyRetryInfoMap proxy_retry_info;
std::string data_reduction_proxy;
- base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
- data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
+ data_reduction_proxy_info.UseProxyServer(
+ params()->proxies_for_http().front().proxy_server());
net::HttpRequestHeaders headers;
// Call network delegate methods to ensure that appropriate chrome proxy
@@ -935,11 +950,13 @@ TEST_F(DataReductionProxyNetworkDelegateTest, LoFiTransitions) {
net::ProxyInfo data_reduction_proxy_info;
std::string proxy;
- if (tests[i].is_data_reduction_proxy)
- base::TrimString(params()->DefaultOrigin(), "/", &proxy);
- else
+ if (tests[i].is_data_reduction_proxy) {
+ data_reduction_proxy_info.UseProxyServer(
+ params()->proxies_for_http().front().proxy_server());
+ } else {
base::TrimString(kOtherProxy, "/", &proxy);
- data_reduction_proxy_info.UseNamedProxy(proxy);
+ data_reduction_proxy_info.UseNamedProxy(proxy);
+ }
// Needed as a parameter, but functionality is not tested.
TestPreviewsDecider test_previews_decider;
@@ -1077,12 +1094,12 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RequestDataConfigurations) {
for (const auto& test : tests) {
net::ProxyInfo data_reduction_proxy_info;
- std::string data_reduction_proxy;
- base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
- if (test.used_data_reduction_proxy)
- data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
- else
+ if (test.used_data_reduction_proxy) {
+ data_reduction_proxy_info.UseProxyServer(
+ params()->proxies_for_http().front().proxy_server());
+ } else {
data_reduction_proxy_info.UseNamedProxy("port.of.other.proxy");
+ }
// Main frame loaded. Lo-Fi should be used.
net::HttpRequestHeaders headers;
net::ProxyRetryInfoMap proxy_retry_info;
@@ -1144,6 +1161,8 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
true, true,
},
};
+ test_network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_4G);
base::FieldTrialList field_trial_list(nullptr);
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
"DataCompressionProxyHoldback", "Enabled"));
@@ -1157,7 +1176,9 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
std::unique_ptr<net::URLRequest> request =
context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE,
nullptr, TRAFFIC_ANNOTATION_FOR_TESTS);
+ request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED);
request->set_method("GET");
+ io_data()->request_options()->SetSecureSession("fake-session");
net::HttpRequestHeaders headers;
net::ProxyRetryInfoMap proxy_retry_info;
network_delegate()->NotifyBeforeSendHeaders(
@@ -1169,6 +1190,11 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
} else {
EXPECT_TRUE(data);
EXPECT_TRUE(data->used_data_reduction_proxy());
+ EXPECT_EQ("fake-session", data->session_key());
+ EXPECT_EQ(GURL(kTestURL), data->request_url());
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_4G,
+ data->effective_connection_type());
+ EXPECT_TRUE(data->page_id());
}
}
}
@@ -1176,9 +1202,8 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) {
Init(USE_INSECURE_PROXY, false);
net::ProxyInfo data_reduction_proxy_info;
- std::string data_reduction_proxy;
- base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
- data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
+ data_reduction_proxy_info.UseProxyServer(
+ params()->proxies_for_http().front().proxy_server());
// Main frame loaded. Lo-Fi should be used.
net::HttpRequestHeaders headers_original;
@@ -1242,6 +1267,11 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) {
}
TEST_F(DataReductionProxyNetworkDelegateTest, NetHistograms) {
+ // Turn off proxy-decides-transform feature for these unit tests.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyDecidesTransform);
+
Init(USE_INSECURE_PROXY, false);
base::HistogramTester histogram_tester;
@@ -1509,8 +1539,8 @@ TEST_F(DataReductionProxyNetworkDelegateTest, DetailedNetHistograms) {
kOriginalContentLength,
kResponseContentLength,
{
- {kReceivedInsecureOtherHistogramName, kResponseContentLength},
- {kOriginalInsecureOtherHistogramName, kResponseContentLength},
+ {kReceivedInsecureDirectHistogramName, kResponseContentLength},
+ {kOriginalInsecureDirectHistogramName, kResponseContentLength},
}},
{"DRP not configured for http video",
true,
@@ -1519,10 +1549,10 @@ TEST_F(DataReductionProxyNetworkDelegateTest, DetailedNetHistograms) {
kOriginalContentLength,
kResponseContentLength,
{
- {kReceivedInsecureOtherHistogramName, kResponseContentLength},
- {kOriginalInsecureOtherHistogramName, kResponseContentLength},
- {kReceivedVideoInsecureOtherHistogramName, kResponseContentLength},
- {kOriginalVideoInsecureOtherHistogramName, kResponseContentLength},
+ {kReceivedInsecureDirectHistogramName, kResponseContentLength},
+ {kOriginalInsecureDirectHistogramName, kResponseContentLength},
+ {kReceivedVideoInsecureDirectHistogramName, kResponseContentLength},
+ {kOriginalVideoInsecureDirectHistogramName, kResponseContentLength},
}},
{"nonvideo over https",
false,
@@ -1763,12 +1793,13 @@ TEST_F(DataReductionProxyNetworkDelegateTest, IncrementingMainFramePageId) {
Init(USE_SECURE_PROXY, false /* enable_brotli_globally */);
io_data()->request_options()->SetSecureSession("new-session");
+ uint64_t page_id = io_data()->request_options()->GeneratePageId();
- FetchURLRequestAndVerifyPageIdDirective("pid=1", false);
+ FetchURLRequestAndVerifyPageIdDirective(++page_id, false);
- FetchURLRequestAndVerifyPageIdDirective("pid=2", false);
+ FetchURLRequestAndVerifyPageIdDirective(++page_id, false);
- FetchURLRequestAndVerifyPageIdDirective("pid=3", false);
+ FetchURLRequestAndVerifyPageIdDirective(++page_id, false);
}
TEST_F(DataReductionProxyNetworkDelegateTest, ResetSessionResetsId) {
@@ -1776,19 +1807,21 @@ TEST_F(DataReductionProxyNetworkDelegateTest, ResetSessionResetsId) {
Init(USE_SECURE_PROXY, false /* enable_brotli_globally */);
io_data()->request_options()->SetSecureSession("new-session");
+ uint64_t page_id = io_data()->request_options()->GeneratePageId();
- FetchURLRequestAndVerifyPageIdDirective("pid=1", false);
+ FetchURLRequestAndVerifyPageIdDirective(++page_id, false);
io_data()->request_options()->SetSecureSession("new-session-2");
- FetchURLRequestAndVerifyPageIdDirective("pid=1", false);
+ page_id = io_data()->request_options()->GeneratePageId();
+ FetchURLRequestAndVerifyPageIdDirective(++page_id, false);
}
TEST_F(DataReductionProxyNetworkDelegateTest, SubResourceNoPageId) {
// This is unaffacted by brotil and insecure proxy.
Init(USE_SECURE_PROXY, false /* enable_brotli_globally */);
io_data()->request_options()->SetSecureSession("new-session");
- FetchURLRequestAndVerifyPageIdDirective(std::string(), false);
+ FetchURLRequestAndVerifyPageIdDirective(base::Optional<uint64_t>(), false);
}
TEST_F(DataReductionProxyNetworkDelegateTest, RedirectSharePid) {
@@ -1796,8 +1829,9 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RedirectSharePid) {
Init(USE_SECURE_PROXY, false /* enable_brotli_globally */);
io_data()->request_options()->SetSecureSession("new-session");
+ uint64_t page_id = io_data()->request_options()->GeneratePageId();
- FetchURLRequestAndVerifyPageIdDirective("pid=1", true);
+ FetchURLRequestAndVerifyPageIdDirective(++page_id, true);
}
TEST_F(DataReductionProxyNetworkDelegateTest,
@@ -1808,9 +1842,8 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
// This is unaffacted by brotil and insecure proxy.
Init(USE_INSECURE_PROXY, false /* enable_brotli_globally */);
net::ProxyInfo data_reduction_proxy_info;
- std::string data_reduction_proxy;
- base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
- data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
+ data_reduction_proxy_info.UseProxyServer(
+ params()->proxies_for_http().front().proxy_server());
std::unique_ptr<net::URLRequest> request =
context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE,
@@ -1818,6 +1851,8 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED);
io_data()->request_options()->SetSecureSession("fake-session");
+ uint64_t page_id = io_data()->request_options()->GeneratePageId();
+
net::HttpRequestHeaders headers;
net::ProxyRetryInfoMap proxy_retry_info;
@@ -1832,7 +1867,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
DataReductionProxyData* data =
DataReductionProxyData::GetData(*request.get());
EXPECT_TRUE(data_reduction_proxy_info.is_http());
- EXPECT_EQ(1u, data->page_id().value());
+ EXPECT_EQ(++page_id, data->page_id().value());
// Send a second request and verify the page ID incremements.
request = context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE,
@@ -1847,22 +1882,23 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
network_delegate()->NotifyBeforeSendHeaders(
request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
data = DataReductionProxyData::GetData(*request.get());
- EXPECT_EQ(2u, data->page_id().value());
+ EXPECT_EQ(++page_id, data->page_id().value());
// Verify that redirects are the same page ID.
network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL));
network_delegate()->NotifyBeforeSendHeaders(
request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
data = DataReductionProxyData::GetData(*request.get());
- EXPECT_EQ(2u, data->page_id().value());
+ EXPECT_EQ(page_id, data->page_id().value());
- // Verify that redirects into a new session get a new page ID.
network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL));
io_data()->request_options()->SetSecureSession("new-session");
+
+ page_id = io_data()->request_options()->GeneratePageId();
network_delegate()->NotifyBeforeSendHeaders(
request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
data = DataReductionProxyData::GetData(*request.get());
- EXPECT_EQ(1u, data->page_id().value());
+ EXPECT_EQ(++page_id, data->page_id().value());
}
// Test that effective connection type is correctly added to the request
@@ -2130,6 +2166,89 @@ TEST_F(DataReductionProxyNetworkDelegateClientLoFiTest, DataSavingsThroughDRP) {
GetSavings());
}
+TEST_F(DataReductionProxyNetworkDelegateTest, TestAcceptTransformHistogram) {
+ Init(USE_INSECURE_PROXY, false);
+ base::HistogramTester histogram_tester;
+
+ // Verify lite page request.
+ net::HttpRequestHeaders request_headers;
+ request_headers.SetHeader("chrome-proxy-accept-transform", "lite-page");
+ FetchURLRequest(GURL(kTestURL), &request_headers, std::string(), 140, 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.Protocol.AcceptTransform", 1);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.AcceptTransform",
+ 0 /* LITE_PAGE_REQUESTED */, 1);
+ // Check legacy histogram too:
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.LoFi.TransformationType",
+ NO_TRANSFORMATION_LITE_PAGE_REQUESTED, 1);
+
+ // Verify empty image request.
+ request_headers.SetHeader("chrome-proxy-accept-transform", "empty-image");
+ FetchURLRequest(GURL(kTestURL), &request_headers, std::string(), 140, 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.Protocol.AcceptTransform", 2);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.AcceptTransform",
+ 3 /* EMPTY_IMAGE_REQUESTED */, 1);
+
+ // Verify lite page response.
+ std::string response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Chrome-Proxy-Content-Transform: lite-page\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"
+ "x-original-content-length: 200\r\n"
+ "\r\n";
+ auto request =
+ FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0);
+ EXPECT_TRUE(DataReductionProxyData::GetData(*request)->lite_page_received());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.Protocol.AcceptTransform", 3);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.AcceptTransform",
+ 1 /* LITE_PAGE_TRANSFORM_RECEIVED */, 1);
+ // Check legacy histogram too:
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.LoFi.TransformationType", LITE_PAGE, 1);
+
+ // Verify page policy response.
+ response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Chrome-Proxy: page-policies=empty-image\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"
+ "x-original-content-length: 200\r\n"
+ "\r\n";
+ request = FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0);
+ EXPECT_FALSE(DataReductionProxyData::GetData(*request)->lite_page_received());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.Protocol.AcceptTransform", 4);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.AcceptTransform",
+ 2 /* EMPTY_IMAGE_POLICY_DIRECTIVE_RECEIVED */, 1);
+
+ // Verify empty image response.
+ response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Chrome-Proxy-Content-Transform: empty-image\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"
+ "x-original-content-length: 200\r\n"
+ "\r\n";
+ request = FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0);
+ EXPECT_TRUE(DataReductionProxyData::GetData(*request)->lofi_received());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.Protocol.AcceptTransform", 5);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.Protocol.AcceptTransform",
+ 4 /* EMPTY_IMAGE_TRANSFORM_RECEIVED */, 1);
+}
+
} // namespace
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc
index 24aa6a384bc..bae6a4ed6d0 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc
@@ -42,6 +42,7 @@ void AddDataToPageloadMetrics(const DataReductionProxyData& request_data,
PageloadMetrics* request,
bool opted_out) {
request->set_session_key(request_data.session_key());
+ request->set_holdback_group(params::HoldbackFieldTrialGroup());
// For the timing events, any of them could be zero. Fill the message as a
// best effort.
request->set_allocated_first_request_time(
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc
index 65c03f3309a..829bc3d9f7e 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc
@@ -13,6 +13,7 @@
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
+#include "base/metrics/field_trial.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/histogram_tester.h"
@@ -215,6 +216,31 @@ TEST_F(DataReductionProxyPingbackClientTest, VerifyPingbackContent) {
EXPECT_EQ(
PageloadMetrics_EffectiveConnectionType_EFFECTIVE_CONNECTION_TYPE_OFFLINE,
pageload_metrics.effective_connection_type());
+ EXPECT_EQ(std::string(), pageload_metrics.holdback_group());
+ test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
+ EXPECT_FALSE(factory()->GetFetcherByID(0));
+}
+
+TEST_F(DataReductionProxyPingbackClientTest, VerifyHoldback) {
+ base::FieldTrialList field_trial_list(nullptr);
+ ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+ "DataCompressionProxyHoldback", "Enabled"));
+ Init();
+ EXPECT_FALSE(factory()->GetFetcherByID(0));
+ pingback_client()->OverrideRandom(true, 0.5f);
+ pingback_client()->SetPingbackReportingFraction(1.0f);
+ CreateAndSendPingback(false /* lofi_received */,
+ false /* lite_page_received */,
+ false /* app_background_occurred */);
+ histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
+ net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
+ EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ RecordPageloadMetricsRequest batched_request;
+ batched_request.ParseFromString(test_fetcher->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);
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
EXPECT_FALSE(factory()->GetFetcherByID(0));
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 2dc55ff8b81..6af9128460a 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
@@ -4,8 +4,11 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
+#include <stdint.h>
+
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/safe_sprintf.h"
#include "base/strings/string_piece.h"
@@ -78,7 +81,7 @@ DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
: client_(util::GetStringForClient(client)),
use_assigned_credentials_(false),
data_reduction_proxy_config_(config),
- current_page_id_(0u) {
+ current_page_id_(base::RandUint64()) {
DCHECK(data_reduction_proxy_config_);
util::GetChromiumBuildAndPatch(version, &build_, &patch_);
}
@@ -116,7 +119,10 @@ void DataReductionProxyRequestOptions::UpdateExperiments() {
experiments_.push_back(experiment_tokenizer.token());
}
} else if (params::AreLitePagesEnabledViaFlags()) {
- experiments_.push_back(chrome_proxy_force_lite_page_experiment());
+ experiments_.push_back(chrome_proxy_experiment_force_lite_page());
+ } else if (params::IsLoFiAlwaysOnViaFlags() ||
+ params::IsLoFiCellularOnlyViaFlags()) {
+ experiments_.push_back(chrome_proxy_experiment_force_empty_image());
} else {
// If no other "exp" directive is forced by flags, add the field trial
// value.
@@ -177,12 +183,17 @@ void DataReductionProxyRequestOptions::AddRequestHeader(
header_value += header_value_;
if (page_id) {
- char page_id_buffer[16];
+ // 64 bit uint fits in 16 characters when represented in hexadecimal, but
+ // there needs to be a trailing null termianted character in the buffer.
+ char page_id_buffer[17];
if (base::strings::SafeSPrintf(page_id_buffer, "%x", page_id.value()) > 0) {
header_value += ", " + FormatOption(kPageIdOption, page_id_buffer);
}
+ uint64_t page_id_tested;
+ DCHECK(base::HexStringToUInt64(page_id_buffer, &page_id_tested) &&
+ page_id_tested == page_id.value());
+ ALLOW_UNUSED_LOCAL(page_id_tested);
}
-
request_headers->SetHeader(kChromeProxyHeader, header_value);
}
@@ -228,6 +239,7 @@ void DataReductionProxyRequestOptions::SetSecureSession(
session_.clear();
credentials_.clear();
secure_session_ = secure_session;
+ // Reset Page ID, so users can't be tracked across sessions.
ResetPageId();
// Force skipping of credential regeneration. It should be handled by the
// caller.
@@ -324,8 +336,7 @@ uint64_t DataReductionProxyRequestOptions::GeneratePageId() {
}
void DataReductionProxyRequestOptions::ResetPageId() {
- // Caller should not depend on reset setting the page ID to 0.
- current_page_id_ = 0;
+ current_page_id_ = base::RandUint64();
}
} // namespace data_reduction_proxy
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 fa8df6aff62..acbf5bb3e42 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
@@ -144,8 +144,6 @@ class DataReductionProxyRequestOptionsTest : public testing::Test {
DataReductionProxyRequestOptionsTest() {
test_context_ =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(0)
- .WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING)
.Build();
}
@@ -362,7 +360,7 @@ TEST_F(DataReductionProxyRequestOptionsTest, TestExperimentPrecedence) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableDataReductionProxyLitePage);
expected_experiments.clear();
- expected_experiments.push_back(chrome_proxy_force_lite_page_experiment());
+ expected_experiments.push_back(chrome_proxy_experiment_force_lite_page());
SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(),
kClientStr, kExpectedBuild, kExpectedPatch, kPageId,
expected_experiments, &expected_header);
@@ -381,6 +379,34 @@ TEST_F(DataReductionProxyRequestOptionsTest, TestExperimentPrecedence) {
VerifyExpectedHeader(expected_header, kPageIdValue);
}
+TEST_F(DataReductionProxyRequestOptionsTest, TestExperimentOtherLoFiFlags) {
+ std::string expected_header;
+ std::vector<std::string> expected_experiments;
+
+ // No "exp=force_*" is set for SlowConnectionOnly flag.
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kDataReductionProxyLoFi,
+ switches::kDataReductionProxyLoFiValueSlowConnectionsOnly);
+ expected_experiments.clear();
+ SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(),
+ kClientStr, kExpectedBuild, kExpectedPatch, kPageId,
+ expected_experiments, &expected_header);
+ CreateRequestOptions(kVersion);
+ VerifyExpectedHeader(expected_header, kPageIdValue);
+
+ // "exp=force_empty_image" is set for CellularOnly flag.
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kDataReductionProxyLoFi,
+ switches::kDataReductionProxyLoFiValueAlwaysOn);
+ expected_experiments.clear();
+ expected_experiments.push_back(chrome_proxy_experiment_force_empty_image());
+ SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(),
+ kClientStr, kExpectedBuild, kExpectedPatch, kPageId,
+ expected_experiments, &expected_header);
+ CreateRequestOptions(kVersion);
+ VerifyExpectedHeader(expected_header, kPageIdValue);
+}
+
TEST_F(DataReductionProxyRequestOptionsTest, GetSessionKeyFromRequestHeaders) {
const struct {
std::string chrome_proxy_header_key;
@@ -419,15 +445,17 @@ TEST_F(DataReductionProxyRequestOptionsTest, GetSessionKeyFromRequestHeaders) {
TEST_F(DataReductionProxyRequestOptionsTest, PageIdIncrementing) {
CreateRequestOptions(kVersion);
- DCHECK_EQ(1u, request_options()->GeneratePageId());
- DCHECK_EQ(2u, request_options()->GeneratePageId());
- DCHECK_EQ(3u, request_options()->GeneratePageId());
+ uint64_t page_id = request_options()->GeneratePageId();
+ DCHECK_EQ(++page_id, request_options()->GeneratePageId());
+ DCHECK_EQ(++page_id, request_options()->GeneratePageId());
+ DCHECK_EQ(++page_id, request_options()->GeneratePageId());
request_options()->SetSecureSession("blah");
- DCHECK_EQ(1u, request_options()->GeneratePageId());
- DCHECK_EQ(2u, request_options()->GeneratePageId());
- DCHECK_EQ(3u, request_options()->GeneratePageId());
+ page_id = request_options()->GeneratePageId();
+ DCHECK_EQ(++page_id, request_options()->GeneratePageId());
+ DCHECK_EQ(++page_id, request_options()->GeneratePageId());
+ DCHECK_EQ(++page_id, request_options()->GeneratePageId());
}
} // namespace data_reduction_proxy
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 970be81f2ab..4d175cd88b4 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
@@ -20,7 +20,6 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service_observer.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
#include "components/data_reduction_proxy/core/browser/data_store.h"
-#include "components/data_reduction_proxy/core/browser/data_use_group.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
@@ -60,14 +59,14 @@ DataReductionProxyService::DataReductionProxyService(
}
DataReductionProxyService::~DataReductionProxyService() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
compression_stats_.reset();
db_task_runner_->DeleteSoon(FROM_HERE, db_data_owner_.release());
}
void DataReductionProxyService::SetIOData(
base::WeakPtr<DataReductionProxyIOData> io_data) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
io_data_ = io_data;
initialized_ = true;
for (DataReductionProxyServiceObserver& observer : observer_list_)
@@ -88,17 +87,17 @@ void DataReductionProxyService::SetIOData(
}
void DataReductionProxyService::Shutdown() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
weak_factory_.InvalidateWeakPtrs();
}
void DataReductionProxyService::UpdateDataUseForHost(int64_t network_bytes,
int64_t original_bytes,
const std::string& host) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (compression_stats_) {
- compression_stats_->RecordDataUsage(host, original_bytes, network_bytes,
- base::Time::Now());
+ compression_stats_->RecordDataUseByHost(host, network_bytes, original_bytes,
+ base::Time::Now());
}
}
@@ -107,55 +106,54 @@ void DataReductionProxyService::UpdateContentLengths(
int64_t original_size,
bool data_reduction_proxy_enabled,
DataReductionProxyRequestType request_type,
- scoped_refptr<DataUseGroup> data_use_group,
const std::string& mime_type) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (compression_stats_) {
- compression_stats_->UpdateContentLengths(
- data_used, original_size, data_reduction_proxy_enabled, request_type,
- data_use_group, mime_type);
+ compression_stats_->RecordDataUseWithMimeType(data_used, original_size,
+ data_reduction_proxy_enabled,
+ request_type, mime_type);
}
}
void DataReductionProxyService::AddEvent(std::unique_ptr<base::Value> event) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
event_store_->AddEvent(std::move(event));
}
void DataReductionProxyService::AddEnabledEvent(
std::unique_ptr<base::Value> event,
bool enabled) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
event_store_->AddEnabledEvent(std::move(event), enabled);
}
void DataReductionProxyService::AddEventAndSecureProxyCheckState(
std::unique_ptr<base::Value> event,
SecureProxyCheckState state) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
event_store_->AddEventAndSecureProxyCheckState(std::move(event), state);
}
void DataReductionProxyService::AddAndSetLastBypassEvent(
std::unique_ptr<base::Value> event,
int64_t expiration_ticks) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
event_store_->AddAndSetLastBypassEvent(std::move(event), expiration_ticks);
}
void DataReductionProxyService::SetUnreachable(bool unreachable) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
settings_->SetUnreachable(unreachable);
}
void DataReductionProxyService::SetLoFiModeActiveOnMainFrame(
bool lo_fi_mode_active) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
settings_->SetLoFiModeActiveOnMainFrame(lo_fi_mode_active);
}
void DataReductionProxyService::SetLoFiModeOff() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (io_task_runner_->BelongsToCurrentThread()) {
io_task_runner_->PostTask(
FROM_HERE,
@@ -168,7 +166,7 @@ void DataReductionProxyService::SetLoFiModeOff() {
}
void DataReductionProxyService::InitializeLoFiPrefs() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!prefs_)
return;
@@ -254,7 +252,7 @@ void DataReductionProxyService::SetStringPref(const std::string& pref_path,
}
void DataReductionProxyService::SetProxyPrefs(bool enabled, bool at_startup) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (io_task_runner_->BelongsToCurrentThread()) {
io_data_->SetProxyPrefs(enabled, at_startup);
return;
@@ -266,7 +264,7 @@ void DataReductionProxyService::SetProxyPrefs(bool enabled, bool at_startup) {
void DataReductionProxyService::SetPingbackReportingFraction(
float pingback_reporting_fraction) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pingback_client_->SetPingbackReportingFraction(pingback_reporting_fraction);
}
@@ -317,24 +315,24 @@ void DataReductionProxyService::DeleteBrowsingHistory(const base::Time& start,
void DataReductionProxyService::AddObserver(
DataReductionProxyServiceObserver* observer) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.AddObserver(observer);
}
void DataReductionProxyService::RemoveObserver(
DataReductionProxyServiceObserver* observer) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.RemoveObserver(observer);
}
bool DataReductionProxyService::Initialized() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return initialized_;
}
base::WeakPtr<DataReductionProxyService>
DataReductionProxyService::GetWeakPtr() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return weak_factory_.GetWeakPtr();
}
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 077f0d20a1c..4686c1d8431 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
@@ -16,7 +16,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#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"
@@ -42,13 +42,11 @@ class DataReductionProxyIOData;
class DataReductionProxyPingbackClient;
class DataReductionProxyServiceObserver;
class DataReductionProxySettings;
-class DataUseGroup;
// Contains and initializes all Data Reduction Proxy objects that have a
// lifetime based on the UI thread.
class DataReductionProxyService
- : public base::NonThreadSafe,
- public DataReductionProxyEventStorageDelegate {
+ : public DataReductionProxyEventStorageDelegate {
public:
// The caller must ensure that |settings|, |prefs|, |request_context|, and
// |io_task_runner| remain alive for the lifetime of the
@@ -87,7 +85,6 @@ class DataReductionProxyService
int64_t original_size,
bool data_reduction_proxy_enabled,
DataReductionProxyRequestType request_type,
- scoped_refptr<DataUseGroup> data_use_group,
const std::string& mime_type);
// Overrides of DataReductionProxyEventStorageDelegate.
@@ -204,6 +201,8 @@ class DataReductionProxyService
bool initialized_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
base::WeakPtrFactory<DataReductionProxyService> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyService);
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index b07e7dd8c8b..7b7975913f1 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -48,13 +48,9 @@ void RecordDaysSinceEnabledMetric(int days_since_enabled) {
namespace data_reduction_proxy {
-const char kDataReductionPassThroughHeader[] =
- "Chrome-Proxy-Accept-Transform: identity\nCache-Control: no-cache";
-
DataReductionProxySettings::DataReductionProxySettings()
: unreachable_(false),
deferred_initialization_(false),
- promo_allowed_(false),
data_reduction_proxy_enabled_pref_name_(),
prefs_(NULL),
config_(nullptr),
@@ -79,11 +75,6 @@ void DataReductionProxySettings::InitPrefMembers() {
base::Unretained(this)));
}
-void DataReductionProxySettings::UpdateConfigValues() {
- DCHECK(config_);
- promo_allowed_ = config_->promo_allowed();
-}
-
void DataReductionProxySettings::InitDataReductionProxySettings(
const std::string& data_reduction_proxy_enabled_pref_name,
PrefService* prefs,
@@ -102,7 +93,6 @@ void DataReductionProxySettings::InitDataReductionProxySettings(
data_reduction_proxy_service_ = std::move(data_reduction_proxy_service);
data_reduction_proxy_service_->AddObserver(this);
InitPrefMembers();
- UpdateConfigValues();
RecordDataReductionInit();
data_reduction_proxy_service_->InitializeLoFiPrefs();
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
index b329f5f1cb3..a128a804508 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -34,11 +34,6 @@ class DataReductionProxyIOData;
class DataReductionProxyService;
class DataReductionProxyCompressionStats;
-// The header used to request a data reduction proxy pass through. When a
-// request is sent to the data reduction proxy with this header, it will respond
-// with the original uncompressed response.
-extern const char kDataReductionPassThroughHeader[];
-
// Values of the UMA DataReductionProxy.StartupState histogram.
// This enum must remain synchronized with DataReductionProxyStartupState
// in metrics/histograms/histograms.xml.
@@ -169,12 +164,6 @@ class DataReductionProxySettings : public DataReductionProxyServiceObserver {
// InitDataReductionProxySettings has not been called.
DataReductionProxyEventStore* GetEventStore() const;
- // Returns true if the data reduction proxy promo may be shown.
- // This is independent of whether the data reduction proxy is allowed.
- bool PromoAllowed() const {
- return promo_allowed_;
- }
-
DataReductionProxyService* data_reduction_proxy_service() {
return data_reduction_proxy_service_.get();
}
@@ -194,8 +183,6 @@ class DataReductionProxySettings : public DataReductionProxyServiceObserver {
protected:
void InitPrefMembers();
- void UpdateConfigValues();
-
// Virtualized for unit test support.
virtual PrefService* GetOriginalProfilePrefs();
@@ -281,11 +268,6 @@ class DataReductionProxySettings : public DataReductionProxyServiceObserver {
// IO object calls will be performed at that time.
bool deferred_initialization_;
- // The following values are cached in order to access the values on the
- // correct thread.
- bool allowed_;
- bool promo_allowed_;
-
// The number of requests to reload the page with images from the Lo-Fi
// UI until Lo-Fi is disabled for the remainder of the session.
int lo_fi_user_requests_for_images_per_session_;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
index 90476c4db2b..124a47a6b7c 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
@@ -54,7 +54,7 @@ void DataReductionProxySettingsTestBase::SetUp() {
pref_service->registry()->RegisterDictionaryPref(kProxy);
pref_service->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore, false);
- ResetSettings(nullptr, true, false);
+ ResetSettings(nullptr);
ListPrefUpdate original_update(test_context_->pref_service(),
prefs::kDailyHttpOriginalContentLength);
@@ -73,14 +73,7 @@ void DataReductionProxySettingsTestBase::SetUp() {
template <class C>
void DataReductionProxySettingsTestBase::ResetSettings(
- std::unique_ptr<base::Clock> clock,
- bool promo_allowed,
- bool holdback) {
- int flags = 0;
- if (promo_allowed)
- flags |= DataReductionProxyParams::kPromoAllowed;
- if (holdback)
- flags |= DataReductionProxyParams::kHoldback;
+ std::unique_ptr<base::Clock> clock) {
MockDataReductionProxySettings<C>* settings =
new MockDataReductionProxySettings<C>();
settings->config_ = test_context_->config();
@@ -89,8 +82,6 @@ void DataReductionProxySettingsTestBase::ResetSettings(
test_context_->CreateDataReductionProxyService(settings);
if (clock)
settings->clock_ = std::move(clock);
- test_context_->config()->ResetParamFlagsForTest(flags);
- settings->UpdateConfigValues();
EXPECT_CALL(*settings, GetOriginalProfilePrefs())
.Times(AnyNumber())
.WillRepeatedly(Return(test_context_->pref_service()));
@@ -102,9 +93,7 @@ void DataReductionProxySettingsTestBase::ResetSettings(
// Explicitly generate required instantiations.
template void DataReductionProxySettingsTestBase::ResetSettings<
- DataReductionProxySettings>(std::unique_ptr<base::Clock> clock,
- bool promo_allowed,
- bool holdback);
+ DataReductionProxySettings>(std::unique_ptr<base::Clock> clock);
void DataReductionProxySettingsTestBase::ExpectSetProxyPrefs(
bool expected_enabled,
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
index 6846173a474..eb864a6659f 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
@@ -50,12 +50,8 @@ class DataReductionProxySettingsTestBase : public testing::Test {
void SetUp() override;
template <class C>
- void ResetSettings(std::unique_ptr<base::Clock> clock,
- bool promo_allowed,
- bool holdback);
- virtual void ResetSettings(std::unique_ptr<base::Clock> clock,
- bool promo_allowed,
- bool holdback) = 0;
+ void ResetSettings(std::unique_ptr<base::Clock> clock);
+ virtual void ResetSettings(std::unique_ptr<base::Clock> clock) = 0;
void ExpectSetProxyPrefs(bool expected_enabled,
bool expected_at_startup);
@@ -88,11 +84,9 @@ class ConcreteDataReductionProxySettingsTest
: public DataReductionProxySettingsTestBase {
public:
typedef MockDataReductionProxySettings<C> MockSettings;
- void ResetSettings(std::unique_ptr<base::Clock> clock,
- bool promo_allowed,
- bool holdback) override {
+ void ResetSettings(std::unique_ptr<base::Clock> clock) override {
return DataReductionProxySettingsTestBase::ResetSettings<C>(
- std::move(clock), promo_allowed, holdback);
+ std::move(clock));
}
};
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 e111c5a745c..2846cdefd6a 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
@@ -649,7 +649,7 @@ TEST_F(DataReductionProxySettingsTest, TestDaysSinceEnabledWithTestClock) {
std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
base::SimpleTestClock* clock_ptr = clock.get();
clock_ptr->Advance(base::TimeDelta::FromDays(1));
- ResetSettings(std::move(clock), false, false);
+ ResetSettings(std::move(clock));
base::Time last_enabled_time = clock_ptr->Now();
@@ -733,7 +733,7 @@ TEST_F(DataReductionProxySettingsTest, TestDaysSinceSavingsCleared) {
std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
base::SimpleTestClock* clock_ptr = clock.get();
clock_ptr->Advance(base::TimeDelta::FromDays(1));
- ResetSettings(std::move(clock), false, false);
+ ResetSettings(std::move(clock));
InitPrefMembers();
base::HistogramTester histogram_tester;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc
deleted file mode 100644
index f8483575138..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc
+++ /dev/null
@@ -1,561 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <utility>
-
-#include "base/base64.h"
-#include "base/macros.h"
-#include "base/md5.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "build/build_config.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
-#include "net/base/mime_util.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
-
-#if defined(OS_ANDROID)
-#include "net/android/network_library.h"
-#endif
-
-// Macro for UMA reporting of tamper detection. HTTP response first reports to
-// histogram events |http_histogram| by |carrier_id|; then reports the total
-// counts to |http_histogram|_Total. HTTPS response reports to histograms
-// |https_histogram| and |https_histogram|_Total similarly.
-#define REPORT_TAMPER_DETECTION_UMA( \
- scheme_is_https, https_histogram, http_histogram, carrier_id) \
- do { \
- if (scheme_is_https) { \
- UMA_HISTOGRAM_SPARSE_SLOWLY(https_histogram, carrier_id); \
- UMA_HISTOGRAM_COUNTS(https_histogram "_Total", 1); \
- } else { \
- UMA_HISTOGRAM_SPARSE_SLOWLY(http_histogram, carrier_id); \
- UMA_HISTOGRAM_COUNTS(http_histogram "_Total", 1); \
- } \
- } while (0)
-
-// Macro for UMA reporting of compression ratio. Reports |compression_ratio|
-// to either "HTTPS" histogram or "HTTP" histogram, depending on the response
-// type.
-#define REPORT_TAMPER_DETECTION_UMA_COMPRESSION_RATIO( \
- scheme_is_https, histogram, compression_ratio) \
- do { \
- if (scheme_is_https) { \
- UMA_HISTOGRAM_SPARSE_SLOWLY( \
- "DataReductionProxy.HeaderTamperedHTTPS_CompressionRatio" \
- histogram, compression_ratio); \
- } else { \
- UMA_HISTOGRAM_SPARSE_SLOWLY( \
- "DataReductionProxy.HeaderTamperedHTTP_CompressionRatio" \
- histogram, compression_ratio); \
- } \
- } while (0)
-
-// Macro for UMA reporting of tamper detection as well as compression ratio.
-#define REPORT_TAMPER_DETECTION_UMA_AND_COMPRESSION_RATIO( \
- scheme_is_https, https_histogram, http_histogram, carrier_id, \
- histogram_compression_ratio, compression_ratio) \
- do { \
- if (scheme_is_https) { \
- UMA_HISTOGRAM_SPARSE_SLOWLY(https_histogram, carrier_id); \
- UMA_HISTOGRAM_COUNTS(https_histogram "_Total", 1); \
- UMA_HISTOGRAM_SPARSE_SLOWLY( \
- "DataReductionProxy.HeaderTamperedHTTPS_CompressionRatio" \
- histogram_compression_ratio, compression_ratio); \
- } else { \
- UMA_HISTOGRAM_SPARSE_SLOWLY(http_histogram, carrier_id); \
- UMA_HISTOGRAM_COUNTS(http_histogram "_Total", 1); \
- UMA_HISTOGRAM_SPARSE_SLOWLY( \
- "DataReductionProxy.HeaderTamperedHTTP_CompressionRatio" \
- histogram_compression_ratio, compression_ratio); \
- } \
- } while (0)
-
-namespace data_reduction_proxy {
-
-// static
-bool DataReductionProxyTamperDetection::DetectAndReport(
- const net::HttpResponseHeaders* headers,
- bool scheme_is_https,
- int64_t content_length) {
- if (headers == nullptr) {
- return false;
- }
-
- // Abort tamper detection, if the fingerprint of the Chrome-Proxy header is
- // absent.
- std::string chrome_proxy_fingerprint;
- if (!GetDataReductionProxyActionFingerprintChromeProxy(
- headers, &chrome_proxy_fingerprint))
- return false;
-
- // Get carrier ID.
- unsigned carrier_id = 0;
-#if defined(OS_ANDROID)
- base::StringToUint(net::android::GetTelephonyNetworkOperator(), &carrier_id);
-#endif
-
- DataReductionProxyTamperDetection tamper_detection(
- headers, scheme_is_https, carrier_id);
-
- // Checks if the Chrome-Proxy header has been tampered with.
- if (tamper_detection.ValidateChromeProxyHeader(chrome_proxy_fingerprint)) {
- tamper_detection.ReportUMAForChromeProxyHeaderValidation();
- return true;
- }
-
- // Chrome-Proxy header has not been tampered with, and thus other
- // fingerprints are valid.
- bool tampered = false;
- int64_t original_content_length = -1;
- std::string fingerprint;
-
- if (GetDataReductionProxyActionFingerprintVia(headers, &fingerprint)) {
- bool has_chrome_proxy_via_header;
- if (tamper_detection.ValidateViaHeader(
- fingerprint, &has_chrome_proxy_via_header)) {
- tamper_detection.ReportUMAForViaHeaderValidation(
- has_chrome_proxy_via_header);
- tampered = true;
- }
- }
-
- if (GetDataReductionProxyActionFingerprintOtherHeaders(
- headers, &fingerprint)) {
- if (tamper_detection.ValidateOtherHeaders(fingerprint)) {
- tamper_detection.ReportUMAForOtherHeadersValidation();
- tampered = true;
- }
- }
-
- if (GetDataReductionProxyActionFingerprintContentLength(
- headers, &fingerprint)) {
- if (tamper_detection.ValidateContentLength(fingerprint,
- content_length,
- &original_content_length)) {
- tamper_detection.ReportUMAForContentLength(content_length,
- original_content_length);
- tampered = true;
- }
- }
-
- if (!tampered) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https,
- "DataReductionProxy.HeaderTamperDetectionPassHTTPS",
- "DataReductionProxy.HeaderTamperDetectionPassHTTP",
- carrier_id);
- }
-
- // Reports the number of responses that other fingerprints will be checked,
- // separated by MIME type.
- tamper_detection.ReportUMAForTamperDetectionCount(original_content_length);
-
- return tampered;
-}
-
-// Constructor initializes the map of fingerprint names to codes.
-DataReductionProxyTamperDetection::DataReductionProxyTamperDetection(
- const net::HttpResponseHeaders* headers,
- const bool is_secure,
- const unsigned carrier_id)
- : response_headers_(headers),
- scheme_is_https_(is_secure),
- carrier_id_(carrier_id) {
- DCHECK(headers);
-}
-
-DataReductionProxyTamperDetection::~DataReductionProxyTamperDetection() {};
-
-void DataReductionProxyTamperDetection::ReportUMAForTamperDetectionCount(
- int64_t original_content_length) const {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS",
- "DataReductionProxy.HeaderTamperDetectionHTTP", carrier_id_);
-
- std::string mime_type;
- response_headers_->GetMimeType(&mime_type);
-
- if (net::MatchesMimeType("text/javascript", mime_type) ||
- net::MatchesMimeType("application/x-javascript", mime_type) ||
- net::MatchesMimeType("application/javascript", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS_JS",
- "DataReductionProxy.HeaderTamperDetectionHTTP_JS", carrier_id_);
- } else if (net::MatchesMimeType("text/css", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS_CSS",
- "DataReductionProxy.HeaderTamperDetectionHTTP_CSS", carrier_id_);
- } else if (net::MatchesMimeType("image/*", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS_Image",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image", carrier_id_);
-
- if (net::MatchesMimeType("image/gif", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_GIF",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_GIF",
- carrier_id_);
- } else if (net::MatchesMimeType("image/jpeg", mime_type) ||
- net::MatchesMimeType("image/jpg", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_JPG",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_JPG",
- carrier_id_);
- } else if (net::MatchesMimeType("image/png", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_PNG",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_PNG",
- carrier_id_);
- } else if (net::MatchesMimeType("image/webp", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_WEBP",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_WEBP",
- carrier_id_);
- }
-
- if (original_content_length == -1)
- return;
-
- if (original_content_length < 10 * 1024) { // 0-10KB
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_0_10KB",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_0_10KB",
- carrier_id_);
- } else if (original_content_length < 100 * 1024) { // 10-100KB
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_10_100KB",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_10_100KB",
- carrier_id_);
- } else if (original_content_length < 500 * 1024) { // 100-500KB
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_100_500KB",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_100_500KB",
- carrier_id_);
- } else { // >=500KB
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_500KB",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Image_500KB",
- carrier_id_);
- }
- } else if (net::MatchesMimeType("video/*", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS_Video",
- "DataReductionProxy.HeaderTamperDetectionHTTP_Video", carrier_id_);
- }
-}
-
-// |fingerprint| is Base64 encoded. Decodes it first. Then calculates the
-// fingerprint of received Chrome-Proxy header, and compares the two to see
-// whether they are equal or not.
-bool DataReductionProxyTamperDetection::ValidateChromeProxyHeader(
- base::StringPiece fingerprint) const {
- std::string received_fingerprint;
- if (!base::Base64Decode(fingerprint, &received_fingerprint))
- return true;
-
- // Gets the Chrome-Proxy header values with its fingerprint removed.
- std::vector<std::string> chrome_proxy_header_values;
- GetDataReductionProxyHeaderWithFingerprintRemoved(
- response_headers_, &chrome_proxy_header_values);
-
- // Calculates the MD5 hash value of Chrome-Proxy.
- std::string actual_fingerprint;
- GetMD5(ValuesToSortedString(&chrome_proxy_header_values),
- &actual_fingerprint);
-
- return received_fingerprint != actual_fingerprint;
-}
-
-void DataReductionProxyTamperDetection::
- ReportUMAForChromeProxyHeaderValidation() const {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ChromeProxy",
- "DataReductionProxy.HeaderTamperedHTTP_ChromeProxy",
- carrier_id_);
-}
-
-// Checks whether there are other proxies/middleboxes' named after the Data
-// Reduction Proxy's name in Via header. |has_chrome_proxy_via_header| marks
-// that whether the Data Reduction Proxy's Via header occurs or not.
-bool DataReductionProxyTamperDetection::ValidateViaHeader(
- base::StringPiece fingerprint,
- bool* has_chrome_proxy_via_header) const {
- bool has_intermediary;
- *has_chrome_proxy_via_header =
- HasDataReductionProxyViaHeader(*response_headers_, &has_intermediary);
-
- if (*has_chrome_proxy_via_header)
- return !has_intermediary;
- return true;
-}
-
-void DataReductionProxyTamperDetection::ReportUMAForViaHeaderValidation(
- bool has_chrome_proxy) const {
- // The Via header of the Data Reduction Proxy is missing.
- if (!has_chrome_proxy) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_Via_Missing",
- "DataReductionProxy.HeaderTamperedHTTP_Via_Missing",
- carrier_id_);
- return;
- }
-
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_Via",
- "DataReductionProxy.HeaderTamperedHTTP_Via",
- carrier_id_);
-}
-
-// The Data Reduction Proxy constructs a canonical representation of values of
-// a list of headers. The fingerprint is constructed as follows:
-// 1) for each header, gets the string representation of its values (same as
-// ValuesToSortedString);
-// 2) concatenates all header's string representations using ";" as a delimiter;
-// 3) calculates the MD5 hash value of above concatenated string;
-// 4) appends the header names to the fingerprint, with a delimiter "|".
-// The constructed fingerprint looks like:
-// [hashed_fingerprint]|header_name1|header_namer2:...
-//
-// To check whether such a fingerprint matches the response that the Chromium
-// client receives, the client firstly extracts the header names. For
-// each header, gets its string representation (by ValuesToSortedString),
-// concatenates them and calculates the MD5 hash value. Compares the hash
-// value to the fingerprint received from the Data Reduction Proxy.
-bool DataReductionProxyTamperDetection::ValidateOtherHeaders(
- const std::string& fingerprint) const {
- DCHECK(!fingerprint.empty());
-
- // According to RFC 2616, "|" is not a valid character in a header name; and
- // it is not a valid base64 encoding character, so there is no ambituity in
- //using it as a delimiter.
- net::HttpUtil::ValuesIterator it(
- fingerprint.begin(), fingerprint.end(), '|');
-
- // The first value is the base64 encoded fingerprint.
- std::string received_fingerprint;
- if (!it.GetNext() ||
- !base::Base64Decode(base::StringPiece(it.value_begin(), it.value_end()),
- &received_fingerprint)) {
- NOTREACHED();
- return true;
- }
-
- std::string header_values;
- // The following values are the header names included in the fingerprint
- // calculation.
- while (it.GetNext()) {
- // Gets values of one header.
- std::vector<std::string> response_header_values = GetHeaderValues(
- response_headers_, base::StringPiece(it.value_begin(), it.value_end()));
- // Sorts the values and concatenate them, with delimiter ";". ";" can occur
- // in a header value and thus two different sets of header values could map
- // to the same string representation. This should be very rare.
- // TODO(xingx): find an unambiguous representation.
- header_values += ValuesToSortedString(&response_header_values) + ";";
- }
-
- // Calculates the MD5 hash of the concatenated string.
- std::string actual_fingerprint;
- GetMD5(header_values, &actual_fingerprint);
-
- return received_fingerprint != actual_fingerprint;
-}
-
-void DataReductionProxyTamperDetection::
- ReportUMAForOtherHeadersValidation() const {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_OtherHeaders",
- "DataReductionProxy.HeaderTamperedHTTP_OtherHeaders",
- carrier_id_);
-}
-
-bool DataReductionProxyTamperDetection::ValidateContentLength(
- base::StringPiece fingerprint,
- int64_t content_length,
- int64_t* original_content_length) const {
- DCHECK(original_content_length);
- // Abort, if Content-Length value from the Data Reduction Proxy does not
- // exist or it cannot be converted to an integer.
- if (!base::StringToInt64(fingerprint, original_content_length))
- return false;
-
- return *original_content_length != content_length;
-}
-
-void DataReductionProxyTamperDetection::ReportUMAForContentLength(
- int64_t content_length,
- int64_t original_content_length) const {
- // Gets MIME type of the response and reports to UMA histograms separately.
- // Divides MIME types into 4 groups: JavaScript, CSS, Images, and others.
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength",
- carrier_id_);
-
- // Gets MIME type.
- std::string mime_type;
- response_headers_->GetMimeType(&mime_type);
-
- // Gets the compression ratio as a percentage. The compression ratio is
- // defined as the received content length of the Chromium client, over the
- // sent content length of the Data Reduction Proxy.
- int compression_ratio = 0;
- if (original_content_length != 0) {
- compression_ratio = content_length * 100 / original_content_length;
- }
-
- if (net::MatchesMimeType("text/javascript", mime_type) ||
- net::MatchesMimeType("application/x-javascript", mime_type) ||
- net::MatchesMimeType("application/javascript", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_JS",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_JS",
- carrier_id_);
- } else if (net::MatchesMimeType("text/css", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_CSS",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_CSS",
- carrier_id_);
- } else if (net::MatchesMimeType("image/*", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image",
- carrier_id_);
-
- if (net::MatchesMimeType("image/gif", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA_AND_COMPRESSION_RATIO(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image_GIF",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image_GIF",
- carrier_id_,
- "_Image_GIF",
- compression_ratio);
- } else if (net::MatchesMimeType("image/jpeg", mime_type) ||
- net::MatchesMimeType("image/jpg", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA_AND_COMPRESSION_RATIO(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image_JPG",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image_JPG",
- carrier_id_,
- "_Image_JPG",
- compression_ratio);
- } else if (net::MatchesMimeType("image/png", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA_AND_COMPRESSION_RATIO(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image_PNG",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image_PNG",
- carrier_id_,
- "_Image_PNG",
- compression_ratio);
- } else if (net::MatchesMimeType("image/webp", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA_AND_COMPRESSION_RATIO(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image_WEBP",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image_WEBP",
- carrier_id_,
- "_Image_WEBP",
- compression_ratio);
- }
-
- REPORT_TAMPER_DETECTION_UMA_COMPRESSION_RATIO(
- scheme_is_https_, "_Image", compression_ratio);
-
- if (original_content_length < 10*1024) { // 0-10KB
- REPORT_TAMPER_DETECTION_UMA_COMPRESSION_RATIO(
- scheme_is_https_, "_Image_0_10KB", compression_ratio);
- } else if (original_content_length < 100*1024) { // 10-100KB
- REPORT_TAMPER_DETECTION_UMA_COMPRESSION_RATIO(
- scheme_is_https_, "_Image_10_100KB", compression_ratio);
- } else if (original_content_length < 500*1024) { // 100-500KB
- REPORT_TAMPER_DETECTION_UMA_COMPRESSION_RATIO(
- scheme_is_https_, "_Image_100_500KB", compression_ratio);
- } else { // >=500KB
- REPORT_TAMPER_DETECTION_UMA_COMPRESSION_RATIO(
- scheme_is_https_, "_Image_500KB", compression_ratio);
- }
- } else if (net::MatchesMimeType("video/*", mime_type)) {
- REPORT_TAMPER_DETECTION_UMA_AND_COMPRESSION_RATIO(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Video",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Video",
- carrier_id_, "_Video", compression_ratio);
- } else {
- REPORT_TAMPER_DETECTION_UMA(
- scheme_is_https_,
- "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Other",
- "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Other",
- carrier_id_);
- }
-}
-
-// We construct a canonical representation of the header so that reordered
-// header values will produce the same fingerprint. The fingerprint is
-// constructed as follows:
-// 1) sort the values;
-// 2) concatenate sorted values with a "," delimiter.
-std::string DataReductionProxyTamperDetection::ValuesToSortedString(
- std::vector<std::string>* values) {
- DCHECK(values);
- if (!values) return "";
-
- std::sort(values->begin(), values->end());
-
- size_t expected_size = 0;
- for (const auto& value : *values)
- expected_size += value.size() + 1;
-
- std::string joined;
- joined.reserve(expected_size);
- // Join all the header values together, including a trailing ','.
- for (const auto& value : *values) {
- joined.append(value);
- joined.append(1, ',');
- }
- return joined;
-}
-
-void DataReductionProxyTamperDetection::GetMD5(base::StringPiece input,
- std::string* output) {
- base::MD5Digest digest;
- base::MD5Sum(input.data(), input.size(), &digest);
- *output = std::string(reinterpret_cast<char*>(digest.a), arraysize(digest.a));
-}
-
-std::vector<std::string> DataReductionProxyTamperDetection::GetHeaderValues(
- const net::HttpResponseHeaders* headers,
- base::StringPiece header_name) {
- std::vector<std::string> values;
- std::string value;
- size_t iter = 0;
- while (headers->EnumerateHeader(&iter, header_name, &value)) {
- values.push_back(std::move(value));
- }
- return values;
-}
-
-} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h
deleted file mode 100644
index a6aa3f341b8..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h
+++ /dev/null
@@ -1,185 +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.
-
-// This file implements the tamper detection logic, which detects whether
-// there are middleboxes and whether they are tampering with the response
-// which may break correct communication and data transfer between the Chromium
-// client and the Data Reduction Proxy.
-//
-// At a high level, the tamper detection process works in two steps:
-// 1. The Data Reduction Proxy selects a fraction of responses to analyze,
-// generates a series of fingerprints for each, and appends them to the
-// Chrome-Proxy response headers;
-// 2. The client re-generate the fingerprints using the same method as the
-// proxy, compares them to the fingerprints in the response, and generates
-// UMA. A response is considered to have been tampered with if the
-// fingerprints do not match.
-//
-// Four fingerprints are generated by the Data Reduction Proxy:
-// 1. Fingerprint of the Chrome-Proxy header, which is designed to check
-// whether the Chrome-Proxy header has been modified or not;
-// 2. Fingerprint of the Via header, which is designed to check whether there
-// are middleboxes between the Chromium client and the Data Reduction Proxy;
-// 3. Fingerprint of a list of headers, which is designed to check whether the
-// values of a list of headers (list is defined by the Data Reduction Proxy)
-// have been modified or deleted;
-// 4. Fingerprint of the Content-Length header, which is designed to check
-// whether the response body has been modified or not (the code assumes that
-// different Content-Length values indicate different response bodies).
-//
-// On the client side, the fingerprint of the Chrome-Proxy header will be
-// checked first. If the fingerprint indicates that the Chrome-Proxy header has
-// not been modified, then the other fingerprints will be considered to be
-// reliable and will be checked next; if not, then it's possible that the other
-// fingerprints have been tampered with and thus they will not be checked.
-// If middlebox removes all the fingerprints then such tampering will not be
-// detected.
-//
-// Detected tampering information will be reported to UMA. In general, for each
-// fingerprint, the client reports the number of responses that have been
-// tampered with for different carriers. For the fingerprint of the
-// Content-Length header, which indicates whether the response body has been
-// modified or not, the reports of tampering are separated by MIME type of the
-// response body.
-
-#ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_
-#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <stdint.h>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "net/proxy/proxy_service.h"
-
-namespace net {
-class HttpResponseHeaders;
-}
-
-namespace data_reduction_proxy {
-
-// Detects if the response sent by the Data Reduction Proxy has been modified
-// by intermediaries on the Web.
-class DataReductionProxyTamperDetection {
- public:
- // Checks if the response contains tamper detection fingerprints added by the
- // Data Reduction Proxy, and determines if the response had been tampered
- // with if so. |content_length| is the accurate response body length, it will
- // be used to detect whether response body had been tampered with. Results
- // are reported to UMA. Traffic carried by HTTP proxy and HTTPS proxy are
- // reported separately, specified by |scheme_is_https|. Returns true if the
- // response had been tampered with.
- static bool DetectAndReport(const net::HttpResponseHeaders* headers,
- bool scheme_is_https,
- int64_t content_length);
-
- // Tamper detection checks |response_headers|. Histogram events are reported
- // by |carrier_id|; |scheme_is_https| determines which histogram to report
- // (HTTP and HTTPS are reported separately).
- DataReductionProxyTamperDetection(
- const net::HttpResponseHeaders* response_headers,
- bool scheme_is_https,
- unsigned carrier_id);
-
- virtual ~DataReductionProxyTamperDetection();
-
- private:
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- TestFingerprintCommon);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- ChromeProxy);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- Via);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- OtherHeaders);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- ContentLength);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- HeaderRemoving);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- ValuesToSortedString);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- GetHeaderValues);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- HistogramCount);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- DetectAndReport);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
- CompressionRatio);
-
- // Reports UMA for the numbers of responses with valid fingerprints, separated
- // by MIME type.
- void ReportUMAForTamperDetectionCount(int64_t original_content_length) const;
-
- // Returns the result of validating Chrome-Proxy header.
- bool ValidateChromeProxyHeader(base::StringPiece fingerprint) const;
-
- // Reports UMA for tampering of the Chrome-Proxy header.
- void ReportUMAForChromeProxyHeaderValidation() const;
-
- // Returns the result of validating the Via header.
- // |has_chrome_proxy_via_header| indicates that the Data Reduction Proxy's
- // Via header occurs or not.
- bool ValidateViaHeader(base::StringPiece fingerprint,
- bool* has_chrome_proxy_via_header) const;
-
- // Reports UMA for tampering of the Via header.
- void ReportUMAForViaHeaderValidation(bool has_chrome_proxy_via_header) const;
-
- // Returns the result of validating a list of headers.
- bool ValidateOtherHeaders(const std::string& fingerprint) const;
-
- // Reports UMA for tampering of values of the list of headers.
- void ReportUMAForOtherHeadersValidation() const;
-
- // Returns the result of validating the contents. It validates this by
- // comparing the content length sent by the Data Reduction Proxy (from the
- // fingerprint), to the actual |content_length| received by the Chromium
- // client. The content length sent by the Data Reduction Proxy is retuned as
- // |original_content_length| for future use, |original_content_length| cannot
- // be NULL.
- bool ValidateContentLength(base::StringPiece fingerprint,
- int64_t received_content_length,
- int64_t* original_content_length) const;
-
- // Reports UMA for tampering of the contents and the compression ratio. The
- // compression ratio is calculated from |content_length|, which is the
- // content length received by the Chromium client, and
- // |original_content_length|, which is the content length sent by the Data
- // Reduction Proxy.
- void ReportUMAForContentLength(int64_t content_length,
- int64_t original_content_length) const;
-
- // Returns a string representation of |values| in sorted order separated by
- // commas, including a trailing comma at the end of the string.
- static std::string ValuesToSortedString(std::vector<std::string>* values);
-
- // Returns raw MD5 hash value for a given string |input|. It is different to
- // base::MD5String which is base16 encoded.
- static void GetMD5(base::StringPiece input, std::string* output);
-
- // Returns all the values of |header_name| of the response |headers| as a
- // vector. This function is used for values that need to be sorted later.
- static std::vector<std::string> GetHeaderValues(
- const net::HttpResponseHeaders* headers,
- base::StringPiece header_name);
-
- // Pointer to response headers.
- const net::HttpResponseHeaders* response_headers_;
-
- // If true, the connection to the Data Reduction Proxy is over HTTPS;
- const bool scheme_is_https_;
-
- // Carrier ID: the numeric name of the current registered operator.
- const unsigned carrier_id_;
-
- DISALLOW_COPY_AND_ASSIGN(DataReductionProxyTamperDetection);
-};
-
-} // namespace data_reduction_proxy
-#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc
deleted file mode 100644
index af8f9017f4f..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc
+++ /dev/null
@@ -1,829 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/macros.h"
-#include "base/md5.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
-#include "build/build_config.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"
-#include "net/http/http_response_headers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-// Calcuates MD5 hash value for a string and then base64 encode it. Testcases
-// contain expected fingerprint in plain text, which needs to be encoded before
-// comparison.
-std::string GetEncoded(const std::string& input) {
- base::MD5Digest digest;
- base::MD5Sum(input.c_str(), input.size(), &digest);
- std::string base64encoded;
- base::Base64Encode(
- std::string(reinterpret_cast<char*>(digest.a), arraysize(digest.a)),
- &base64encoded);
- return base64encoded;
-}
-
-// Replaces all contents within "[]" by corresponding base64 encoded MD5 value.
-// It can handle nested case like: [[abc]def]. This helper function transforms
-// fingerprint in plain text to actual encoded fingerprint.
-void ReplaceWithEncodedString(std::string* input) {
- size_t start, end, temp;
- while (true) {
- start = input->find("[");
- if (start == std::string::npos) break;
- while (true) {
- temp = input->find("[", start + 1);
- end = input->find("]", start + 1);
- if (end != std::string::npos && end < temp)
- break;
-
- start = temp;
- }
- std::string need_to_encode = input->substr(start + 1, end - start - 1);
- *input = input->substr(0, start) + GetEncoded(need_to_encode) +
- input->substr(end + 1);
- }
-}
-
-// Returns a vector contains all the values from a comma-separated string.
-// Some testcases contain string representation of a vector, this helper
-// function generates a vector from a input string.
-std::vector<std::string> StringsToVector(const std::string& values) {
- std::vector<std::string> ret;
- if (values.empty())
- return ret;
- size_t now = 0;
- size_t next;
- while ((next = values.find(",", now)) != std::string::npos) {
- ret.push_back(values.substr(now, next - now));
- now = next + 1;
- }
- return ret;
-}
-
-} // namespace
-
-namespace data_reduction_proxy {
-
-using DataReductionProxyTamperDetectionTest = testing::Test;
-
-// Tests function ValidateChromeProxyHeader.
-TEST_F(DataReductionProxyTamperDetectionTest, ChromeProxy) {
- // |received_fingerprint| is not the actual fingerprint from Data Reduction
- // Proxy, instead, the base64 encoded field is in plain text (within "[]")
- // and needs to be encoded first.
- struct {
- std::string label;
- std::string raw_header;
- std::string received_fingerprint;
- bool expected_tampered_with;
- } test[] = {
- {
- "Checks sorting.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: c,b,a,3,2,1,fcp=f\n",
- "[1,2,3,a,b,c,]",
- false,
- },
- {
- "Checks Chrome-Proxy's fingerprint removing.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a,b,c,d,e,3,2,1,fcp=f\n",
- "[1,2,3,a,b,c,d,e,]",
- false,
- },
- {
- "Checks no Chrome-Proxy header case (should not happen).",
- "HTTP/1.1 200 OK\n",
- "[]",
- false,
- },
- {
- "Checks empty Chrome-Proxy header case (should not happen).",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: \n",
- "[,]",
- false,
- },
- {
- "Checks Chrome-Proxy header with its fingerprint only case.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: fcp=f\n",
- "[]",
- false,
- },
- {
- "Checks empty Chrome-Proxy header case, with extra ',' and ' '",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: fcp=f , \n",
- "[]",
- false,
- },
- {
- "Changed no value to empty value.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: fcp=f\n",
- "[,]",
- true,
- },
- {
- "Changed header values.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a,b=2,c,d=1,fcp=f\n",
- "[a,b=3,c,d=1,]",
- true,
- },
- {
- "Changed order of header values.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: c,b,a,fcp=1\n",
- "[c,b,a,]",
- true,
- },
- {
- "Checks Chrome-Proxy header with extra ' '.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a , b , c, d, fcp=f\n",
- "[a,b,c,d,]",
- false
- },
- {
- "Check Chrome-Proxy header with multiple lines and ' '.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a , c , d, fcp=f \n"
- "Chrome-Proxy: b \n",
- "[a,b,c,d,]",
- false
- },
- {
- "Checks Chrome-Proxy header with multiple lines, at different positions",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a \n"
- "Chrome-Proxy: c \n"
- "Content-Type: 1\n"
- "Cache-Control: 2\n"
- "ETag: 3\n"
- "Chrome-Proxy: b \n"
- "Connection: 4\n"
- "Expires: 5\n"
- "Chrome-Proxy: fcp=f \n"
- "Via: \n"
- "Content-Length: 12345\n",
- "[a,b,c,]",
- false
- },
- {
- "Checks Chrome-Proxy header with multiple same values.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a \n"
- "Chrome-Proxy: b\n"
- "Chrome-Proxy: c\n"
- "Chrome-Proxy: d, fcp=f \n"
- "Chrome-Proxy: a \n",
- "[a,a,b,c,d,]",
- false
- },
- {
- "Changed Chrome-Proxy header with multiple lines..",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a\n"
- "Chrome-Proxy: a\n"
- "Chrome-Proxy: b\n"
- "Chrome-Proxy: c,fcp=f\n",
- "[a,b,c,]",
- true,
- },
- {
- "Checks case whose received fingerprint is empty.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a,b,c,fcp=1\n",
- "[]",
- true,
- },
- {
- "Checks case whose received fingerprint cannot be base64 decoded.",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: a,b,c,fcp=1\n",
- "not_base64_encoded",
- true,
- },
- };
-
- for (size_t i = 0; i < arraysize(test); ++i) {
- ReplaceWithEncodedString(&test[i].received_fingerprint);
-
- std::string raw_headers(test[i].raw_header);
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
-
- bool tampered = tamper_detection.ValidateChromeProxyHeader(
- test[i].received_fingerprint);
-
- EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
- }
-}
-
-// Tests function ValidateViaHeader.
-TEST_F(DataReductionProxyTamperDetectionTest, Via) {
- struct {
- std::string label;
- std::string raw_header;
- std::string received_fingerprint;
- bool expected_tampered_with;
- bool expected_has_chrome_proxy_via_header;
- } test[] = {
- {
- "Checks the case that Chrome-Compression-Proxy occurs at the last.",
- "HTTP/1.1 200 OK\n"
- "Via: a, b, c, 1.1 Chrome-Compression-Proxy\n",
- "",
- false,
- true,
- },
- {
- "Checks when there is intermediary.",
- "HTTP/1.1 200 OK\n"
- "Via: a, b, c, 1.1 Chrome-Compression-Proxy, xyz\n",
- "",
- true,
- true,
- },
- {
- "Checks the case of empty Via header.",
- "HTTP/1.1 200 OK\n"
- "Via: \n",
- "",
- true,
- false,
- },
- {
- "Checks the case that only the Data Reduction Proxy's Via header occurs.",
- "HTTP/1.1 200 OK\n"
- "Via: 1.1 Chrome-Compression-Proxy \n",
- "",
- false,
- true,
- },
- {
- "Checks the case that there are ' ', i.e., empty value after the Data"
- " Reduction Proxy's Via header.",
- "HTTP/1.1 200 OK\n"
- "Via: 1.1 Chrome-Compression-Proxy , , \n",
- "",
- false,
- true,
- },
- {
- "Checks the case when there is no Via header",
- "HTTP/1.1 200 OK\n",
- "",
- true,
- false,
- },
- };
-
- for (size_t i = 0; i < arraysize(test); ++i) {
- std::string raw_headers(test[i].raw_header);
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
-
- bool has_chrome_proxy_via_header;
- bool tampered = tamper_detection.ValidateViaHeader(
- test[i].received_fingerprint, &has_chrome_proxy_via_header);
-
- EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
- EXPECT_EQ(test[i].expected_has_chrome_proxy_via_header,
- has_chrome_proxy_via_header) << test[i].label;
- }
-}
-
-// Tests function ValidateOtherHeaders.
-TEST_F(DataReductionProxyTamperDetectionTest, OtherHeaders) {
- // For following testcases, |received_fingerprint| is not the actual
- // fingerprint from Data Reduction Proxy, instead, the base64 encoded field
- // is in plain text (within "[]") and needs to be encoded first. For example,
- // "[12345;]|content-length" needs to be encoded to
- // "Base64Encoded(MD5(12345;))|content-length" before calling the checking
- // function.
- struct {
- std::string label;
- std::string raw_header;
- std::string received_fingerprint;
- bool expected_tampered_with;
- } test[] = {
- {
- "Checks the case that only one header is requested.",
- "HTTP/1.1 200 OK\n"
- "Content-Length: 12345\n",
- "[12345,;]|content-length",
- false
- },
- {
- "Checks the case that there is only one requested header and it does not"
- "exist.",
- "HTTP/1.1 200 OK\n",
- "[;]|non_exist_header",
- false
- },
- {
- "Checks the case of multiple headers are requested.",
- "HTTP/1.1 200 OK\n"
- "Content-Type: 1\n"
- "Cache-Control: 2\n"
- "ETag: 3\n"
- "Connection: 4\n"
- "Expires: 5\n",
- "[1,;2,;3,;4,;5,;]|content-type|cache-control|etag|connection|expires",
- false
- },
- {
- "Checks the case that one header has multiple values.",
- "HTTP/1.1 200 OK\n"
- "Content-Type: aaa1, bbb1, ccc1\n"
- "Cache-Control: aaa2\n",
- "[aaa1,bbb1,ccc1,;aaa2,;]|content-type|cache-control",
- false
- },
- {
- "Checks the case that one header has multiple lines.",
- "HTTP/1.1 200 OK\n"
- "Content-Type: aaa1, ccc1\n"
- "Content-Type: xxx1, bbb1, ccc1\n"
- "Cache-Control: aaa2\n",
- "[aaa1,bbb1,ccc1,ccc1,xxx1,;aaa2,;]|content-type|cache-control",
- false
- },
- {
- "Checks the case that more than one headers have multiple values.",
- "HTTP/1.1 200 OK\n"
- "Content-Type: aaa1, ccc1\n"
- "Cache-Control: ccc2 , bbb2\n"
- "Content-Type: bbb1, ccc1\n"
- "Cache-Control: aaa2 \n",
- "[aaa1,bbb1,ccc1,ccc1,;aaa2,bbb2,ccc2,;]|content-type|cache-control",
- false
- },
- {
- "Checks the case that one of the requested headers is missing (Expires).",
- "HTTP/1.1 200 OK\n"
- "Content-Type: aaa1, ccc1\n",
- "[aaa1,ccc1,;;]|content-type|expires",
- false
- },
- {
- "Checks the case that some of the requested headers have empty value.",
- "HTTP/1.1 200 OK\n"
- "Content-Type: \n"
- "Cache-Control: \n",
- "[,;,;]|content-type|cache-control",
- false
- },
- {
- "Checks the case that all the requested headers are missing.",
- "HTTP/1.1 200 OK\n",
- "[;;]|content-type|expires",
- false
- },
- {
- "Checks the case that some headers are missing, some of them are empty.",
- "HTTP/1.1 200 OK\n"
- "Cache-Control: \n",
- "[;,;]|content-type|cache-control",
- false
- },
- {
- "Checks the case there is no requested header (header list is empty).",
- "HTTP/1.1 200 OK\n"
- "Chrome-Proxy: aut=aauutthh,bbbypas=0,aaxxx=xxx,bbbloc=1\n"
- "Content-Type: 1\n"
- "Cache-Control: 2\n",
- "[]",
- false
- },
- {
- "Checks tampered requested header values.",
- "HTTP/1.1 200 OK\n"
- "Content-Type: aaa1, ccc1\n"
- "Cache-Control: ccc2 , bbb2\n",
- "[aaa1,bbb1,;bbb2,ccc2,;]|content-type|cache-control",
- true
- },
- };
-
- for (size_t i = 0; i < arraysize(test); ++i) {
- ReplaceWithEncodedString(&test[i].received_fingerprint);
-
- std::string raw_headers(test[i].raw_header);
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
-
- bool tampered = tamper_detection.ValidateOtherHeaders(
- test[i].received_fingerprint);
-
- EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
- }
-}
-
-// Tests function ValidateContentLength.
-TEST_F(DataReductionProxyTamperDetectionTest, ContentLength) {
- struct {
- std::string label;
- std::string received_fingerprint;
- int64_t actual_content_length;
- int64_t expected_original_content_length;
- bool expected_tampered_with;
- } test[] = {
- {
- "Checks the case fingerprint matches actual content length.",
- "12345",
- 12345,
- 12345,
- false,
- },
- {
- "Checks case that response got modified.",
- "12345",
- 12346,
- 12345,
- true,
- },
- };
-
- for (size_t i = 0; i < arraysize(test); ++i) {
- std::string raw_headers("HTTP/1.1 200 OK\n");
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
-
- int64_t original_content_length;
- bool tampered = tamper_detection.ValidateContentLength(
- test[i].received_fingerprint,
- test[i].actual_content_length,
- &original_content_length);
-
- EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
- EXPECT_EQ(test[i].expected_original_content_length,
- original_content_length) << test[i].label;
- }
-}
-
-// Tests ValuesToSortedString function.
-TEST_F(DataReductionProxyTamperDetectionTest, ValuesToSortedString) {
- struct {
- std::string label;
- std::string input_values;
- std::string expected_output_string;
- } test[] = {
- {
- "Checks the correctness of sorting.",
- "3,2,1,",
- "1,2,3,",
- },
- {
- "Checks the case that there is an empty input vector.",
- "",
- "",
- },
- {
- "Checks the case that there is an empty string in the input vector.",
- ",",
- ",",
- },
- };
-
- for (size_t i = 0; i < arraysize(test); ++i) {
- std::vector<std::string> input_values =
- StringsToVector(test[i].input_values);
- std::string output_string =
- DataReductionProxyTamperDetection::ValuesToSortedString(&input_values);
- EXPECT_EQ(output_string, test[i].expected_output_string) << test[i].label;
- }
-}
-
-// Tests GetHeaderValues function.
-TEST_F(DataReductionProxyTamperDetectionTest, GetHeaderValues) {
- struct {
- std::string label;
- std::string raw_header;
- std::string header_name;
- std::string expected_output_values;
- } test[] = {
- {
- "Checks the correctness of getting single line header.",
- "HTTP/1.1 200 OK\n"
- "test: 1, 2, 3\n",
- "test",
- "1,2,3,",
- },
- {
- "Checks the correctness of getting multiple lines header.",
- "HTTP/1.1 200 OK\n"
- "test: 1, 2, 3\n"
- "test: 4, 5, 6\n"
- "test: 7, 8, 9\n",
- "test",
- "1,2,3,4,5,6,7,8,9,",
- },
- {
- "Checks the correctness of getting missing header.",
- "HTTP/1.1 200 OK\n",
- "test",
- "",
- },
- {
- "Checks the correctness of getting empty header.",
- "HTTP/1.1 200 OK\n"
- "test: \n",
- "test",
- ",",
- },
- };
-
- for (size_t i = 0; i < arraysize(test); ++i) {
- std::string raw_headers(test[i].raw_header);
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- std::vector<std::string> expected_output_values =
- StringsToVector(test[i].expected_output_values);
-
- std::vector<std::string> output_values =
- DataReductionProxyTamperDetection::GetHeaderValues(headers.get(),
- test[i].header_name);
- EXPECT_EQ(expected_output_values, output_values) << test[i].label;
- }
-}
-
-// Tests UMA histogram count.
-TEST_F(DataReductionProxyTamperDetectionTest, HistogramCount) {
- struct {
- std::string raw_header;
- std::string histogram_name_suffix;
- int original_content_length;
- std::string image_histogram_name_suffix;
- } tests[] = {
- // Checks the correctness of histogram for Javascript
- {"HTTP/1.1 200 OK\n"
- "Content-Type: text/javascript\n",
- "_JS", -1, ""},
- // Checks the correctness of histogram for CSS
- {"HTTP/1.1 200 OK\n"
- "Content-Type: text/css\n",
- "_CSS", -1, ""},
- // Checks the correctness of histogram for image
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/test\n",
- "_Image", 1, "_Image_0_10KB"},
- // Checks the correctness of histogram for GIF
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/gif\n",
- "_Image_GIF", 20 * 1024, "_Image_10_100KB"},
- // Checks the correctness of histogram for JPG
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/jpeg\n",
- "_Image_JPG", 200 * 1024, "_Image_100_500KB"},
- // Checks the correctness of histogram for PNG
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/png\n",
- "_Image_PNG", 600 * 1024, "_Image_500KB"},
- // Checks the correctness of histogram for WebP
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/webp\n",
- "_Image_WEBP", -1, ""},
- // Checks the correctness of histogram for Video
- {"HTTP/1.1 200 OK\n"
- "Content-Type: video/webm\n",
- "_Video", -1, ""},
- };
-
- const int carrier_id = 100;
-
- for (auto& test : tests) {
- std::string raw_headers(test.raw_header);
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- // Test HTTPS and HTTP separately.
- int https_values[] = {true, false};
- for (auto https : https_values) {
- base::HistogramTester histogram_tester;
-
- DataReductionProxyTamperDetection tamper_detection(headers.get(), https,
- carrier_id);
- tamper_detection.ReportUMAForTamperDetectionCount(
- test.original_content_length);
- histogram_tester.ExpectTotalCount(
- base::StringPrintf("DataReductionProxy.HeaderTamperDetectionHTTP%s"
- "%s_Total",
- (https ? "S" : ""),
- test.histogram_name_suffix.c_str()),
- 1);
- histogram_tester.ExpectUniqueSample(
- base::StringPrintf("DataReductionProxy.HeaderTamperDetectionHTTP%s"
- "%s",
- (https ? "S" : ""),
- test.histogram_name_suffix.c_str()),
- carrier_id, 1);
- histogram_tester.ExpectTotalCount(
- base::StringPrintf("DataReductionProxy.HeaderTamperDetectionHTTP%s"
- "_Total",
- (https ? "S" : "")),
- 1);
- histogram_tester.ExpectUniqueSample(
- base::StringPrintf("DataReductionProxy.HeaderTamperDetectionHTTP%s",
- (https ? "S" : "")),
- carrier_id, 1);
-
- if (test.original_content_length != -1) {
- histogram_tester.ExpectTotalCount(
- base::StringPrintf("DataReductionProxy.HeaderTamperDetectionHTTP%s"
- "%s_Total",
- (https ? "S" : ""),
- test.image_histogram_name_suffix.c_str()),
- 1);
- histogram_tester.ExpectUniqueSample(
- base::StringPrintf("DataReductionProxy.HeaderTamperDetectionHTTP%s"
- "%s",
- (https ? "S" : ""),
- test.image_histogram_name_suffix.c_str()),
- carrier_id, 1);
- }
- }
- }
-}
-
-// Tests function ReportUMAForContentLength, with the focus on compression
-// ratio.
-TEST_F(DataReductionProxyTamperDetectionTest, CompressionRatio) {
- struct {
- std::string raw_header;
- std::string histogram_name_suffix;
- int original_content_length;
- int content_length;
- int compression_ratio;
- } tests[] = {
- // Checks the correctness of histogram for Video
- {"HTTP/1.1 200 OK\n"
- "Content-Type: video/webm\n",
- "_Video", 1000, 800, 80},
- // Checks the correctness of histogram for JPEG
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/jpg\n",
- "_Image_JPG", 1000, 1, 0},
- // Checks the correctness of histogram for PNG
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/png\n",
- "_Image_PNG", 1000, 0, 0},
- // Checks the correctness of histogram for WebP
- {"HTTP/1.1 200 OK\n"
- "Content-Type: image/webp\n",
- "_Image_WEBP", 1000, 5000, 500},
- };
-
- const int carrier_id = 100;
-
- for (auto& test : tests) {
- std::string raw_headers(test.raw_header);
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- // Test HTTPS and HTTP separately.
- int https_values[] = {true, false};
- for (auto https : https_values) {
- base::HistogramTester histogram_tester;
-
- DataReductionProxyTamperDetection tamper_detection(headers.get(), https,
- carrier_id);
- tamper_detection.ReportUMAForContentLength(test.content_length,
- test.original_content_length);
- histogram_tester.ExpectTotalCount(
- base::StringPrintf("DataReductionProxy.HeaderTamperedHTTP%s"
- "_ContentLength%s_Total",
- (https ? "S" : ""),
- test.histogram_name_suffix.c_str()),
- 1);
- histogram_tester.ExpectUniqueSample(
- base::StringPrintf("DataReductionProxy.HeaderTamperedHTTP%s"
- "_ContentLength%s",
- (https ? "S" : ""),
- test.histogram_name_suffix.c_str()),
- carrier_id, 1);
- histogram_tester.ExpectUniqueSample(
- base::StringPrintf("DataReductionProxy.HeaderTamperedHTTP%s"
- "_CompressionRatio%s",
- (https ? "S" : ""),
- test.histogram_name_suffix.c_str()),
- test.compression_ratio, 1);
- }
- }
-}
-
-// Tests main function DetectAndReport.
-TEST_F(DataReductionProxyTamperDetectionTest, DetectAndReport) {
- struct {
- std::string label;
- std::string raw_header;
- int64_t content_length;
- bool expected_tampered_with;
- } test[] = {
- {
- "Check no fingerprint added case.",
- "HTTP/1.1 200 OK\n"
- "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
- "Content-Length: 12345\n"
- "Chrome-Proxy: bypass=0\n",
- 12345,
- false,
- },
- {
- "Check the case Chrome-Proxy fingerprint doesn't match.",
- "HTTP/1.1 200 OK\n"
- "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
- "Content-Length: 12345\n"
- "header1: header_1\n"
- "header2: header_2\n"
- "header3: header_3\n"
- "Chrome-Proxy: fcl=12345, "
- "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,fvia=0,"
- "fcp=abc\n",
- 12345,
- true,
- },
- {
- "Check the case response matches the fingerprint completely.",
- "HTTP/1.1 200 OK\n"
- "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
- "Content-Length: 12345\n"
- "header1: header_1\n"
- "header2: header_2\n"
- "header3: header_3\n"
- "Chrome-Proxy: fcl=12345, "
- "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,"
- "fvia=0, fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]"
- "|header1|header2|header3,fvia=0,]\n",
- 12345,
- false,
- },
- {
- "Check the case that actual content length doesn't match.",
- "HTTP/1.1 200 OK\n"
- "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
- "Content-Length: 0\n"
- "header1: header_1\n"
- "header2: header_2\n"
- "header3: header_3\n"
- "Chrome-Proxy: fcl=12345, "
- "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3, fvia=0, "
- "fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]|"
- "header1|header2|header3,fvia=0,]\n",
- 123,
- true,
- },
- };
-
- for (size_t i = 0; i < arraysize(test); ++i) {
- std::string raw_headers(test[i].raw_header);
- ReplaceWithEncodedString(&raw_headers);
- HeadersToRaw(&raw_headers);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_headers));
-
- EXPECT_EQ(
- test[i].expected_tampered_with,
- DataReductionProxyTamperDetection::DetectAndReport(
- headers.get(), true, test[i].content_length)) << test[i].label;
- }
-}
-
-} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index f5a610b1dfd..df652759579 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -294,9 +294,7 @@ DataStore::Status TestDataStore::RecreateDB() {
}
DataReductionProxyTestContext::Builder::Builder()
- : params_flags_(DataReductionProxyParams::kPromoAllowed),
- params_definitions_(TestDataReductionProxyParams::HAS_EVERYTHING),
- client_(Client::UNKNOWN),
+ : client_(Client::UNKNOWN),
request_context_(nullptr),
mock_socket_factory_(nullptr),
use_mock_config_(false),
@@ -309,19 +307,6 @@ DataReductionProxyTestContext::Builder::Builder()
DataReductionProxyTestContext::Builder::~Builder() {}
DataReductionProxyTestContext::Builder&
-DataReductionProxyTestContext::Builder::WithParamsFlags(int params_flags) {
- params_flags_ = params_flags;
- return *this;
-}
-
-DataReductionProxyTestContext::Builder&
-DataReductionProxyTestContext::Builder::WithParamsDefinitions(
- unsigned int params_definitions) {
- params_definitions_ = params_definitions;
- return *this;
-}
-
-DataReductionProxyTestContext::Builder&
DataReductionProxyTestContext::Builder::WithURLRequestContext(
net::URLRequestContext* request_context) {
request_context_ = request_context;
@@ -424,7 +409,7 @@ DataReductionProxyTestContext::Builder::Build() {
std::unique_ptr<DataReductionProxyConfigServiceClient> config_client;
DataReductionProxyMutableConfigValues* raw_mutable_config = nullptr;
std::unique_ptr<TestDataReductionProxyParams> params(
- new TestDataReductionProxyParams(params_flags_, params_definitions_));
+ new TestDataReductionProxyParams());
TestDataReductionProxyParams* raw_params = params.get();
if (use_config_client_) {
test_context_flags |= USE_CONFIG_CLIENT;
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 a6eb4039e03..953af6fba60 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
@@ -282,12 +282,6 @@ class DataReductionProxyTestContext {
~Builder();
- // |DataReductionProxyParams| flags to use.
- Builder& WithParamsFlags(int params_flags);
-
- // |TestDataReductionProxyParams| flags to use.
- Builder& WithParamsDefinitions(unsigned int params_definitions);
-
// The |Client| enum to use for |DataReductionProxyRequestOptions|.
Builder& WithClient(Client client);
@@ -334,8 +328,6 @@ class DataReductionProxyTestContext {
std::unique_ptr<DataReductionProxyTestContext> Build();
private:
- int params_flags_;
- unsigned int params_definitions_;
Client client_;
net::URLRequestContext* request_context_;
net::MockClientSocketFactory* mock_socket_factory_;
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 3c7c180fe96..564b70b721b 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
@@ -121,17 +121,15 @@ DataStore::Status DataStoreImpl::OpenDB() {
// these log entries are deleted.
options.reuse_logs = false;
std::string db_name = profile_path_.Append(kDBName).AsUTF8Unsafe();
- leveldb::DB* dbptr = nullptr;
+ db_.reset();
Status status =
- LevelDbToDRPStoreStatus(leveldb::DB::Open(options, db_name, &dbptr));
+ LevelDbToDRPStoreStatus(leveldb_env::OpenDB(options, db_name, &db_));
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.LevelDBOpenStatus", status,
STATUS_MAX);
if (status != OK)
LOG(ERROR) << "Failed to open Data Reduction Proxy DB: " << status;
- db_.reset(dbptr);
-
if (db_) {
leveldb::Range range;
uint64_t size;
@@ -139,7 +137,7 @@ DataStore::Status DataStoreImpl::OpenDB() {
// lowest keys.
range.start = "";
range.limit = "z"; // Keys starting with 'z' will not be included.
- dbptr->GetApproximateSizes(&range, 1, &size);
+ db_->GetApproximateSizes(&range, 1, &size);
UMA_HISTOGRAM_MEMORY_KB("DataReductionProxy.LevelDBSize", size / 1024);
}
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_use_group.h b/chromium/components/data_reduction_proxy/core/browser/data_use_group.h
deleted file mode 100644
index 94b53412d34..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_use_group.h
+++ /dev/null
@@ -1,35 +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_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_USE_GROUP_H_
-#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_USE_GROUP_H_
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-
-namespace data_reduction_proxy {
-
-// Abstract class that tracks data usage for a group of requests, e.g. those
-// corresponding to the same page load. Applications are responsible for
-// providing overrides that track data usage for groups they care about.
-// This class is ref counted because their lifetimes can depends on the lifetime
-// of frames as well as lifetime of URL requests whose data usage is being
-// tracked.
-class DataUseGroup : public base::RefCountedThreadSafe<DataUseGroup> {
- public:
- // Initialize this instance.
- virtual void Initialize() = 0;
-
- // The hostname that this data usage should be ascribed to.
- virtual std::string GetHostname() = 0;
-
- protected:
- friend class base::RefCountedThreadSafe<DataUseGroup>;
- virtual ~DataUseGroup() {}
-};
-
-} // namespace data_reduction_proxy
-
-#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_USE_GROUP_H_
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_use_group_provider.h b/chromium/components/data_reduction_proxy/core/browser/data_use_group_provider.h
deleted file mode 100644
index ca3f266767e..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_use_group_provider.h
+++ /dev/null
@@ -1,33 +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_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_USE_GROUP_PROVIDER_H_
-#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_USE_GROUP_PROVIDER_H_
-
-#include "base/memory/ref_counted.h"
-
-namespace net {
-class URLRequest;
-}
-
-namespace data_reduction_proxy {
-
-class DataUseGroup;
-
-// Abstract class that manages instances of |DataUsageGroup| and maps
-// |URLRequest| instances to their appropriate |DataUsageGroup|. Applications
-// should provide overrides if they are interested in tracking data usage
-// and surfacing it to the end user.
-class DataUseGroupProvider {
- public:
- // Returns the |DataUseGroup| to which data usage for the given URL should
- // be ascribed. If no existing |DataUseGroup| exists, a new one will be
- // created.
- virtual scoped_refptr<DataUseGroup> GetDataUseGroup(
- const net::URLRequest* request) = 0;
-};
-
-} // namespace data_reduction_proxy
-
-#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_USE_GROUP_PROVIDER_H_
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
index c2f79e9b84e..008d01928d3 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
@@ -7,8 +7,6 @@
#include <vector>
-class GURL;
-
namespace data_reduction_proxy {
class DataReductionProxyServer;
@@ -17,22 +15,9 @@ class DataReductionProxyConfigValues {
public:
virtual ~DataReductionProxyConfigValues() {}
- // Returns true if the data reduction proxy promo may be shown.
- // This is independent of whether the data reduction proxy is allowed.
- // TODO(bengr): maybe tie to whether proxy is allowed.
- virtual bool promo_allowed() const = 0;
-
- // Returns true if the data reduction proxy should not actually use the
- // proxy if enabled.
- virtual bool holdback() const = 0;
-
// Returns the HTTP proxy servers to be used.
virtual const std::vector<DataReductionProxyServer>& proxies_for_http()
const = 0;
-
- // Returns the URL to check to decide if the secure proxy origin should be
- // used.
- virtual const GURL& secure_proxy_check_url() const = 0;
};
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
index fd6ddc7bd21..ee0fa9dfcd1 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
@@ -106,8 +106,8 @@ DataReductionProxyEventStore::GetSummaryValue() const {
auto data_reduction_proxy_values = base::MakeUnique<base::DictionaryValue>();
data_reduction_proxy_values->SetBoolean("enabled", enabled_);
if (current_configuration_) {
- data_reduction_proxy_values->Set("proxy_config",
- current_configuration_->DeepCopy());
+ data_reduction_proxy_values->Set(
+ "proxy_config", base::MakeUnique<base::Value>(*current_configuration_));
}
switch (secure_proxy_check_state_) {
@@ -129,8 +129,8 @@ DataReductionProxyEventStore::GetSummaryValue() const {
int current_time_ticks_ms =
(base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds();
if (expiration_ticks_ > current_time_ticks_ms) {
- data_reduction_proxy_values->Set("last_bypass",
- last_bypass_event->DeepCopy());
+ data_reduction_proxy_values->Set(
+ "last_bypass", base::MakeUnique<base::Value>(*last_bypass_event));
}
}
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 f2547d74569..d11c0199df4 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
@@ -20,7 +20,12 @@ const base::Feature kDataReductionSiteBreakdown{
// version required the client to make this decision. The new protocol relies
// on updates primarily to the Chrome-Proxy-Accept-Transform header.
const base::Feature kDataReductionProxyDecidesTransform{
- "DataReductionProxyDecidesTransform", base::FEATURE_DISABLED_BY_DEFAULT};
+ "DataReductionProxyDecidesTransform", base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Enables the data saver promo for low memory Android devices.
+const base::Feature kDataReductionProxyLowMemoryDevicePromo{
+ "DataReductionProxyLowMemoryDevicePromo",
+ 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 af951c894ae..4650fcff19b 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
@@ -13,6 +13,7 @@ namespace features {
extern const base::Feature kDataReductionMainMenu;
extern const base::Feature kDataReductionSiteBreakdown;
extern const base::Feature kDataReductionProxyDecidesTransform;
+extern const base::Feature kDataReductionProxyLowMemoryDevicePromo;
} // 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 8961f1b7afb..543bcc5cdf1 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
@@ -29,6 +29,9 @@ using base::TimeDelta;
namespace {
const char kChromeProxyHeader[] = "chrome-proxy";
+const char kDataReductionPassThroughHeader[] =
+ "Chrome-Proxy-Accept-Transform: identity\r\nCache-Control: "
+ "no-cache\r\n\r\n";
const char kChromeProxyECTHeader[] = "chrome-proxy-ect";
const char kChromeProxyAcceptTransformHeader[] =
"chrome-proxy-accept-transform";
@@ -42,11 +45,14 @@ const char kEmptyImageDirective[] = "empty-image";
const char kLitePageDirective[] = "lite-page";
const char kCompressedVideoDirective[] = "compressed-video";
const char kIdentityDirective[] = "identity";
+const char kChromeProxyPagePoliciesDirective[] = "page-policies";
// The legacy Chrome-Proxy response header directive for LoFi images.
const char kLegacyChromeProxyLoFiResponseDirective[] = "q=low";
-const char kChromeProxyForceLitePageExperiment[] = "force_lite_page";
+const char kChromeProxyExperimentForceLitePage[] = "force_lite_page";
+const char kChromeProxyExperimentForceEmptyImage[] =
+ "force_page_policies_empty_image";
const char kIfHeavyQualifier[] = "if-heavy";
@@ -118,6 +124,30 @@ bool HasURLRedirectCycle(const std::vector<GURL>& url_chain) {
url_chain.back()) != url_chain.rend();
}
+data_reduction_proxy::TransformDirective ParsePagePolicyDirective(
+ const std::string chrome_proxy_header_value) {
+ for (const auto& directive : base::SplitStringPiece(
+ chrome_proxy_header_value, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ if (!base::StartsWith(directive, kChromeProxyPagePoliciesDirective,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ continue;
+ }
+
+ // Check policy directive for empty-image entry.
+ base::StringPiece page_policies_value = base::StringPiece(directive).substr(
+ arraysize(kChromeProxyPagePoliciesDirective));
+ for (const auto& policy :
+ base::SplitStringPiece(page_policies_value, "|", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ if (base::LowerCaseEqualsASCII(policy, kEmptyImageDirective)) {
+ return data_reduction_proxy::TRANSFORM_PAGE_POLICIES_EMPTY_IMAGE;
+ }
+ }
+ }
+ return data_reduction_proxy::TRANSFORM_NONE;
+}
+
} // namespace
namespace data_reduction_proxy {
@@ -126,6 +156,10 @@ const char* chrome_proxy_header() {
return kChromeProxyHeader;
}
+const char* chrome_proxy_pass_through_header() {
+ return kDataReductionPassThroughHeader;
+}
+
const char* chrome_proxy_ect_header() {
return kChromeProxyECTHeader;
}
@@ -150,18 +184,70 @@ const char* compressed_video_directive() {
return kCompressedVideoDirective;
}
-const char* identity_directive() {
- return kIdentityDirective;
+const char* chrome_proxy_experiment_force_lite_page() {
+ return kChromeProxyExperimentForceLitePage;
}
-const char* chrome_proxy_force_lite_page_experiment() {
- return kChromeProxyForceLitePageExperiment;
+const char* chrome_proxy_experiment_force_empty_image() {
+ return kChromeProxyExperimentForceEmptyImage;
}
const char* if_heavy_qualifier() {
return kIfHeavyQualifier;
}
+TransformDirective ParseRequestTransform(
+ const net::HttpRequestHeaders& headers) {
+ std::string accept_transform_value;
+ if (!headers.GetHeader(chrome_proxy_accept_transform_header(),
+ &accept_transform_value)) {
+ return TRANSFORM_NONE;
+ }
+
+ if (base::LowerCaseEqualsASCII(accept_transform_value,
+ lite_page_directive())) {
+ return TRANSFORM_LITE_PAGE;
+ } else if (base::LowerCaseEqualsASCII(accept_transform_value,
+ empty_image_directive())) {
+ return TRANSFORM_EMPTY_IMAGE;
+ } else if (base::LowerCaseEqualsASCII(accept_transform_value,
+ compressed_video_directive())) {
+ return TRANSFORM_COMPRESSED_VIDEO;
+ } else if (base::LowerCaseEqualsASCII(accept_transform_value,
+ kIdentityDirective)) {
+ return TRANSFORM_IDENTITY;
+ } else {
+ return TRANSFORM_NONE;
+ }
+}
+
+TransformDirective ParseResponseTransform(
+ const net::HttpResponseHeaders& headers) {
+ std::string content_transform_value;
+ if (!headers.GetNormalizedHeader(chrome_proxy_content_transform_header(),
+ &content_transform_value)) {
+ // No content-transform so check for page-policies in chrome-proxy header.
+ std::string chrome_proxy_header_value;
+ if (headers.GetNormalizedHeader(chrome_proxy_header(),
+ &chrome_proxy_header_value)) {
+ return ParsePagePolicyDirective(chrome_proxy_header_value);
+ }
+ } else if (base::LowerCaseEqualsASCII(content_transform_value,
+ lite_page_directive())) {
+ return TRANSFORM_LITE_PAGE;
+ } else if (base::LowerCaseEqualsASCII(content_transform_value,
+ empty_image_directive())) {
+ return TRANSFORM_EMPTY_IMAGE;
+ } else if (base::LowerCaseEqualsASCII(content_transform_value,
+ kIdentityDirective)) {
+ return TRANSFORM_IDENTITY;
+ } else {
+ NOTREACHED() << "Unexpected content transform header: "
+ << content_transform_value;
+ }
+ return TRANSFORM_NONE;
+}
+
bool IsEmptyImagePreview(const net::HttpResponseHeaders& headers) {
return IsPreviewType(headers, kEmptyImageDirective) ||
headers.HasHeaderValue(kChromeProxyHeader,
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 83202075cf1..7dfedda6edc 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
@@ -11,6 +11,8 @@
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
#include "net/proxy/proxy_service.h"
class GURL;
@@ -21,6 +23,16 @@ class HttpResponseHeaders;
namespace data_reduction_proxy {
+// Transform directives that may be parsed out of http headers.
+enum TransformDirective {
+ TRANSFORM_NONE,
+ TRANSFORM_LITE_PAGE,
+ TRANSFORM_EMPTY_IMAGE,
+ TRANSFORM_COMPRESSED_VIDEO,
+ TRANSFORM_PAGE_POLICIES_EMPTY_IMAGE,
+ TRANSFORM_IDENTITY,
+};
+
// Values of the UMA DataReductionProxy.BypassType{Primary|Fallback} and
// DataReductionProxy.BlockType{Primary|Fallback} histograms. This enum must
// remain synchronized with the enum of the same name in
@@ -68,6 +80,11 @@ struct DataReductionProxyInfo {
// Gets the header used for data reduction proxy requests and responses.
const char* chrome_proxy_header();
+// The header used to request a data reduction proxy pass through. When a
+// request is sent to the data reduction proxy with this header, it will respond
+// with the original uncompressed response.
+const char* chrome_proxy_pass_through_header();
+
// Gets the chrome-proxy-ect request header that includes the effective
// connection type.
const char* chrome_proxy_ect_header();
@@ -90,17 +107,17 @@ const char* lite_page_directive();
// compressed video.
const char* compressed_video_directive();
-// Gets the directive used by the data reduction proxy to request that
-// a resource not be transformed.
-const char* identity_directive();
-
// Gets the Chrome-Proxy directive used by data reduction proxy lite page
// preview requests and responses.
const char* chrome_proxy_lite_page_directive();
// Gets the Chrome-Proxy experiment ("exp") value to force a lite page preview
// for requests that accept lite pages.
-const char* chrome_proxy_force_lite_page_experiment();
+const char* chrome_proxy_experiment_force_lite_page();
+
+// Gets the Chrome-Proxy experiment ("exp") value to force an empty image
+// preview for requests that enable server provided previews.
+const char* chrome_proxy_experiment_force_empty_image();
// Requests a transformation only if the server determines that the page is
// otherwise heavy (i.e., the associated page load ordinarily requires the
@@ -112,6 +129,18 @@ const char* if_heavy_qualifier();
// that an empty image has been provided.
bool IsEmptyImagePreview(const net::HttpResponseHeaders& headers);
+// Retrieves the accepted transform type, if any, from |headers|.
+TransformDirective ParseRequestTransform(
+ const net::HttpRequestHeaders& headers);
+
+// Retrieves the transform directive (whether applied or a page policy), if any,
+// from |headers|.
+// Note if the response headers contains both an applied content transform and
+// a page policies directive, only the applied content transform type will
+// be returned.
+TransformDirective ParseResponseTransform(
+ const net::HttpResponseHeaders& headers);
+
// Returns true if the provided value of the Chrome-Proxy-Content-Transform
// response header that is provided in |content_transform_value| indicates that
// an empty image has been provided.
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 f0a2d3d0831..66c9a2d0055 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
@@ -13,13 +13,17 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.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/variations/variations_associated_data.h"
#include "net/proxy/proxy_server.h"
#include "url/url_constants.h"
-using base::FieldTrialList;
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "base/sys_info.h"
+#endif
namespace {
@@ -27,22 +31,12 @@ const char kEnabled[] = "Enabled";
const char kControl[] = "Control";
const char kDisabled[] = "Disabled";
const char kLitePage[] = "Enabled_Preview";
-const char kDefaultSpdyOrigin[] = "https://proxy.googlezip.net:443";
-// A one-off change, until the Data Reduction Proxy configuration service is
-// available.
-const char kCarrierTestOrigin[] =
- "http://o-o.preferred.nttdocomodcp-hnd1.proxy-dev.googlezip.net:80";
-const char kDefaultFallbackOrigin[] = "compress.googlezip.net:80";
const char kDefaultSecureProxyCheckUrl[] = "http://check.googlezip.net/connect";
const char kDefaultWarmupUrl[] = "http://check.googlezip.net/generate_204";
-const char kAndroidOneIdentifier[] = "sprout";
-
const char kQuicFieldTrial[] = "DataReductionProxyUseQuic";
const char kLoFiFieldTrial[] = "DataCompressionProxyLoFi";
-const char kLitePageFallbackFieldTrial[] =
- "DataCompressionProxyLitePageFallback";
const char kLoFiFlagFieldTrial[] = "DataCompressionProxyLoFiFlag";
const char kBlackListTransitionFieldTrial[] =
@@ -66,7 +60,7 @@ const char kServerExperimentsFieldTrial[] =
const char kLitePageBlackListVersion[] = "lite-page-blacklist-version";
bool IsIncludedInFieldTrial(const std::string& name) {
- return base::StartsWith(FieldTrialList::FindFullName(name), kEnabled,
+ return base::StartsWith(base::FieldTrialList::FindFullName(name), kEnabled,
base::CompareCase::SENSITIVE);
}
@@ -82,22 +76,64 @@ std::string GetStringValueForVariationParamWithDefaultValue(
return it->second;
}
+bool IsIncludedInAndroidOnePromoFieldTrial(
+ base::StringPiece build_fingerprint) {
+ static const char kAndroidOneIdentifier[] = "sprout";
+ return build_fingerprint.find(kAndroidOneIdentifier) != std::string::npos;
+}
+
+bool CanShowAndroidLowMemoryDevicePromo() {
+#if defined(OS_ANDROID)
+ return base::SysInfo::IsLowEndDevice() &&
+ base::FeatureList::IsEnabled(
+ data_reduction_proxy::features::
+ kDataReductionProxyLowMemoryDevicePromo);
+#endif
+ return false;
+}
+
} // namespace
namespace data_reduction_proxy {
namespace params {
bool IsIncludedInPromoFieldTrial() {
- return IsIncludedInFieldTrial("DataCompressionProxyPromoVisibility");
+ if (IsIncludedInFieldTrial("DataCompressionProxyPromoVisibility"))
+ return true;
+
+#if defined(OS_ANDROID)
+ base::StringPiece android_build_fingerprint =
+ base::android::BuildInfo::GetInstance()->android_build_fp();
+ if (IsIncludedInAndroidOnePromoFieldTrial(android_build_fingerprint))
+ return true;
+#endif
+ return CanShowAndroidLowMemoryDevicePromo();
+}
+
+bool IsIncludedInFREPromoFieldTrial() {
+ if (IsIncludedInFieldTrial("DataReductionProxyFREPromo"))
+ return true;
+
+#if defined(OS_ANDROID)
+ base::StringPiece android_build_fingerprint =
+ base::android::BuildInfo::GetInstance()->android_build_fp();
+ if (IsIncludedInAndroidOnePromoFieldTrial(android_build_fingerprint))
+ return true;
+#endif
+ return CanShowAndroidLowMemoryDevicePromo();
+}
+
+bool IsIncludedInAndroidOnePromoFieldTrialForTesting(
+ base::StringPiece build_fingerprint) {
+ return IsIncludedInAndroidOnePromoFieldTrial(build_fingerprint);
}
bool IsIncludedInHoldbackFieldTrial() {
return IsIncludedInFieldTrial("DataCompressionProxyHoldback");
}
-bool IsIncludedInAndroidOnePromoFieldTrial(
- base::StringPiece build_fingerprint) {
- return build_fingerprint.find(kAndroidOneIdentifier) != std::string::npos;
+std::string HoldbackFieldTrialGroup() {
+ return base::FieldTrialList::FindFullName("DataCompressionProxyHoldback");
}
const char* GetTrustedSpdyProxyFieldTrialName() {
@@ -105,17 +141,24 @@ const char* GetTrustedSpdyProxyFieldTrialName() {
}
bool IsIncludedInTrustedSpdyProxyFieldTrial() {
- return IsIncludedInFieldTrial(GetTrustedSpdyProxyFieldTrialName());
+ if (base::StartsWith(base::FieldTrialList::FindFullName(
+ GetTrustedSpdyProxyFieldTrialName()),
+ kControl, base::CompareCase::SENSITIVE)) {
+ return false;
+ }
+ if (base::StartsWith(base::FieldTrialList::FindFullName(
+ GetTrustedSpdyProxyFieldTrialName()),
+ kDisabled, base::CompareCase::SENSITIVE)) {
+ return false;
+ }
+ // Trusted SPDY proxy experiment is enabled by default.
+ return true;
}
const char* GetLoFiFieldTrialName() {
return kLoFiFieldTrial;
}
-const char* GetLitePageFallbackFieldTrialName() {
- return kLitePageFallbackFieldTrial;
-}
-
const char* GetLoFiFlagFieldTrialName() {
return kLoFiFlagFieldTrial;
}
@@ -127,32 +170,29 @@ bool IsIncludedInLoFiEnabledFieldTrial() {
bool IsIncludedInLoFiControlFieldTrial() {
return !IsLoFiOnViaFlags() && !IsLoFiDisabledViaFlags() &&
- base::StartsWith(FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
- kControl, base::CompareCase::SENSITIVE);
+ base::StartsWith(
+ base::FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
+ kControl, base::CompareCase::SENSITIVE);
}
bool IsIncludedInLitePageFieldTrial() {
return !IsLoFiOnViaFlags() && !IsLoFiDisabledViaFlags() &&
- base::StartsWith(FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
- kLitePage, base::CompareCase::SENSITIVE);
-}
-
-bool IsLitePageFallbackEnabled() {
- return IsIncludedInFieldTrial(GetLitePageFallbackFieldTrialName()) ||
- (IsLoFiOnViaFlags() && AreLitePagesEnabledViaFlags());
+ base::StartsWith(
+ base::FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
+ kLitePage, base::CompareCase::SENSITIVE);
}
bool IsIncludedInServerExperimentsFieldTrial() {
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
data_reduction_proxy::switches::
kDataReductionProxyServerExperimentsDisabled) &&
- FieldTrialList::FindFullName(kServerExperimentsFieldTrial)
+ base::FieldTrialList::FindFullName(kServerExperimentsFieldTrial)
.find(kDisabled) != 0;
}
bool IsIncludedInTamperDetectionExperiment() {
return IsIncludedInServerExperimentsFieldTrial() &&
base::StartsWith(
- FieldTrialList::FindFullName(kServerExperimentsFieldTrial),
+ base::FieldTrialList::FindFullName(kServerExperimentsFieldTrial),
"TamperDetection_Enabled", base::CompareCase::SENSITIVE);
}
@@ -160,8 +200,8 @@ bool FetchWarmupURLEnabled() {
// Fetching of the warmup URL can be enabled only for Enabled* and Control*
// groups.
if (!IsIncludedInQuicFieldTrial() &&
- !base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kControl,
- base::CompareCase::SENSITIVE)) {
+ !base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
+ kControl, base::CompareCase::SENSITIVE)) {
return false;
}
@@ -231,12 +271,12 @@ bool WarnIfNoDataReductionProxy() {
}
bool IsIncludedInQuicFieldTrial() {
- if (base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kControl,
- base::CompareCase::SENSITIVE)) {
+ if (base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
+ kControl, base::CompareCase::SENSITIVE)) {
return false;
}
- if (base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kDisabled,
- base::CompareCase::SENSITIVE)) {
+ if (base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
+ kDisabled, base::CompareCase::SENSITIVE)) {
return false;
}
// QUIC is enabled by default.
@@ -408,6 +448,16 @@ const char* GetServerExperimentsFieldTrialName() {
return kServerExperimentsFieldTrial;
}
+GURL GetSecureProxyCheckURL() {
+ std::string secure_proxy_check_url =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kDataReductionProxySecureProxyCheckURL);
+ if (secure_proxy_check_url.empty())
+ secure_proxy_check_url = kDefaultSecureProxyCheckUrl;
+
+ return GURL(secure_proxy_check_url);
+}
+
} // namespace params
DataReductionProxyTypeInfo::DataReductionProxyTypeInfo() : proxy_index(0) {}
@@ -417,50 +467,10 @@ DataReductionProxyTypeInfo::DataReductionProxyTypeInfo(
DataReductionProxyTypeInfo::~DataReductionProxyTypeInfo() {}
-DataReductionProxyParams::DataReductionProxyParams(int flags)
- : DataReductionProxyParams(flags, true) {}
-
-DataReductionProxyParams::~DataReductionProxyParams() {}
-
-DataReductionProxyParams::DataReductionProxyParams(int flags,
- bool should_call_init)
- : promo_allowed_((flags & kPromoAllowed) == kPromoAllowed),
- holdback_((flags & kHoldback) == kHoldback),
- use_override_proxies_for_http_(false) {
- if (should_call_init) {
- bool result = Init();
- DCHECK(result);
- }
-}
-
-void DataReductionProxyParams::SetProxiesForHttpForTesting(
- const std::vector<DataReductionProxyServer>& proxies_for_http) {
- proxies_for_http_ = proxies_for_http;
-}
-
-bool DataReductionProxyParams::Init() {
- InitWithoutChecks();
- // Verify that all necessary params are set.
- if (!origin_.is_valid()) {
- DVLOG(1) << "Invalid data reduction proxy origin: " << origin_.ToURI();
- return false;
- }
-
- if (!fallback_origin_.is_valid()) {
- DVLOG(1) << "Invalid data reduction proxy fallback origin: "
- << fallback_origin_.ToURI();
- return false;
- }
-
- if (!secure_proxy_check_url_.is_valid()) {
- DVLOG(1) << "Invalid secure proxy check url: <null>";
- return false;
- }
- return true;
-}
-
-void DataReductionProxyParams::InitWithoutChecks() {
- DCHECK(proxies_for_http_.empty());
+DataReductionProxyParams::DataReductionProxyParams()
+ : use_override_proxies_for_http_(false) {
+ static const char kDefaultSpdyOrigin[] = "https://proxy.googlezip.net:443";
+ static const char kDefaultFallbackOrigin[] = "compress.googlezip.net:80";
use_override_proxies_for_http_ =
params::GetOverrideProxiesForHttpFromCommandLine(
@@ -468,39 +478,37 @@ void DataReductionProxyParams::InitWithoutChecks() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
- std::string origin;
- origin = command_line.GetSwitchValueASCII(switches::kDataReductionProxy);
+ std::string origin =
+ command_line.GetSwitchValueASCII(switches::kDataReductionProxy);
std::string fallback_origin =
command_line.GetSwitchValueASCII(switches::kDataReductionProxyFallback);
- std::string secure_proxy_check_url = command_line.GetSwitchValueASCII(
- switches::kDataReductionProxySecureProxyCheckURL);
- std::string warmup_url = command_line.GetSwitchValueASCII(
- switches::kDataReductionProxyWarmupURL);
// Set from preprocessor constants those params that are not specified on the
// command line.
if (origin.empty())
- origin = GetDefaultOrigin();
+ origin = kDefaultSpdyOrigin;
if (fallback_origin.empty())
- fallback_origin = GetDefaultFallbackOrigin();
- if (secure_proxy_check_url.empty())
- secure_proxy_check_url = GetDefaultSecureProxyCheckURL();
+ fallback_origin = kDefaultFallbackOrigin;
- origin_ = net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP);
- fallback_origin_ =
+ net::ProxyServer origin_proxy_server =
+ net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP);
+ net::ProxyServer fallback_proxy_server =
net::ProxyServer::FromURI(fallback_origin, net::ProxyServer::SCHEME_HTTP);
- if (origin_.is_valid()) {
- // |origin_| is the core proxy server.
+ if (origin_proxy_server.is_valid()) {
proxies_for_http_.push_back(
- DataReductionProxyServer(origin_, ProxyServer::CORE));
+ DataReductionProxyServer(origin_proxy_server, ProxyServer::CORE));
}
- if (fallback_origin_.is_valid()) {
- // |fallback| is also a core proxy server.
+ if (fallback_proxy_server.is_valid()) {
proxies_for_http_.push_back(
- DataReductionProxyServer(fallback_origin_, ProxyServer::CORE));
+ DataReductionProxyServer(fallback_proxy_server, ProxyServer::CORE));
}
+}
- secure_proxy_check_url_ = GURL(secure_proxy_check_url);
+DataReductionProxyParams::~DataReductionProxyParams() {}
+
+void DataReductionProxyParams::SetProxiesForHttpForTesting(
+ const std::vector<DataReductionProxyServer>& proxies_for_http) {
+ proxies_for_http_ = proxies_for_http;
}
const std::vector<DataReductionProxyServer>&
@@ -510,41 +518,4 @@ DataReductionProxyParams::proxies_for_http() const {
return proxies_for_http_;
}
-// Returns the URL to check to decide if the secure proxy origin should be
-// used.
-const GURL& DataReductionProxyParams::secure_proxy_check_url() const {
- return secure_proxy_check_url_;
-}
-
-// Returns true if the data reduction proxy promo may be shown.
-// This is idependent of whether the data reduction proxy is allowed.
-// TODO(bengr): maybe tie to whether proxy is allowed.
-bool DataReductionProxyParams::promo_allowed() const {
- return promo_allowed_;
-}
-
-// Returns true if the data reduction proxy should not actually use the
-// proxy if enabled.
-bool DataReductionProxyParams::holdback() const {
- return holdback_;
-}
-
-// TODO(kundaji): Remove tests for macro definitions.
-std::string DataReductionProxyParams::GetDefaultOrigin() const {
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- if (command_line.HasSwitch(switches::kEnableDataReductionProxyCarrierTest))
- return kCarrierTestOrigin;
- return kDefaultSpdyOrigin;
-}
-
-std::string DataReductionProxyParams::GetDefaultFallbackOrigin() const {
- return kDefaultFallbackOrigin;
-}
-
-std::string DataReductionProxyParams::GetDefaultSecureProxyCheckURL() const {
- return kDefaultSecureProxyCheckUrl;
-}
-
-
} // namespace data_reduction_proxy
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 badc927146f..96f7ba69c93 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
@@ -11,7 +11,6 @@
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h"
-#include "net/proxy/proxy_server.h"
#include "url/gurl.h"
namespace net {
@@ -27,10 +26,14 @@ class DataReductionProxyServer;
// by field trials and command line switches.
namespace params {
-// Returns true if this client is part of the field trial that should display
+// Returns true if this client is part of a field trial that should display
// a promotion for the data reduction proxy.
bool IsIncludedInPromoFieldTrial();
+// Returns true if this client is part of a field trial that should display
+// a FRE promotion for the data reduction proxy.
+bool IsIncludedInFREPromoFieldTrial();
+
// Returns true if this client is part of a field trial that runs a holdback
// experiment. A holdback experiment is one in which a fraction of browser
// instances will not be configured to use the data reduction proxy even if
@@ -38,6 +41,10 @@ bool IsIncludedInPromoFieldTrial();
// is in effect.
bool IsIncludedInHoldbackFieldTrial();
+// The name of the Holdback experiment group, this can return an empty string if
+// not included in a group.
+std::string HoldbackFieldTrialGroup();
+
// Returns the name of the trusted SPDY/HTTP2 proxy field trial.
const char* GetTrustedSpdyProxyFieldTrialName();
@@ -46,15 +53,14 @@ const char* GetTrustedSpdyProxyFieldTrialName();
bool IsIncludedInTrustedSpdyProxyFieldTrial();
// Returns true if this client is part of the field trial that should display
-// a promotion for the data reduction proxy on Android One devices.
-bool IsIncludedInAndroidOnePromoFieldTrial(base::StringPiece build_fingerprint);
+// a promotion for the data reduction proxy on Android One devices. This is for
+// testing purposes and should not be called outside of tests.
+bool IsIncludedInAndroidOnePromoFieldTrialForTesting(
+ base::StringPiece build_fingerprint);
// Returns the name of the Lo-Fi field trial.
const char* GetLoFiFieldTrialName();
-// Returns the name of the Lite Page fallback to Lo-Fi field trial.
-const char* GetLitePageFallbackFieldTrialName();
-
// Returns the name of the Lo-Fi field trial that configures LoFi flags when it
// is force enabled through flags.
const char* GetLoFiFlagFieldTrialName();
@@ -71,11 +77,6 @@ bool IsIncludedInLoFiControlFieldTrial();
// trial.
bool IsIncludedInLitePageFieldTrial();
-// Returns true if this client is part of the Lite Page fallback to Lo-Fi field
-// trial or if this client has the command line switch to enable lite pages,
-// which should always fallback.
-bool IsLitePageFallbackEnabled();
-
// Returns true if this client is part of the field trial that should enable
// server experiments for the data reduction proxy.
bool IsIncludedInServerExperimentsFieldTrial();
@@ -106,8 +107,7 @@ bool IsLoFiDisabledViaFlags();
// Returns true if this client has the command line switch to enable lite pages.
// This means a preview should be requested instead of placeholders whenever
-// Lo-Fi mode is on. If Lite Pages are enabled via flags, they will always
-// fallback to Lo-Fi placeholders.
+// Lo-Fi mode is on.
bool AreLitePagesEnabledViaFlags();
// Returns true if this client has the command line switch to enable forced
@@ -169,6 +169,10 @@ bool GetOverrideProxiesForHttpFromCommandLine(
// Returns the name of the server side experiment field trial.
const char* GetServerExperimentsFieldTrialName();
+// Returns the URL to check to decide if the secure proxy origin should be
+// used.
+GURL GetSecureProxyCheckURL();
+
// Returns true if fetching of the warmup URL is enabled.
bool FetchWarmupURLEnabled();
@@ -195,17 +199,9 @@ struct DataReductionProxyTypeInfo {
// Reduction Proxy.
class DataReductionProxyParams : public DataReductionProxyConfigValues {
public:
- // Flags used during construction that specify if the promotion is allowed to
- // be shown, and if this instance is part of a holdback experiment.
- static const unsigned int kPromoAllowed = (1 << 2);
- static const unsigned int kHoldback = (1 << 3);
-
- // Constructs configuration parameters. If |kPromoAllowed|, the client may
- // show a promotion for the data reduction proxy.
- //
- // A standard configuration has a primary proxy, and a fallback proxy for
- // HTTP traffic.
- explicit DataReductionProxyParams(int flags);
+ // Constructs configuration parameters. A standard configuration has a primary
+ // proxy, and a fallback proxy for HTTP traffic.
+ DataReductionProxyParams();
// Updates |proxies_for_http_|.
void SetProxiesForHttpForTesting(
@@ -216,43 +212,9 @@ class DataReductionProxyParams : public DataReductionProxyConfigValues {
const std::vector<DataReductionProxyServer>& proxies_for_http()
const override;
- const GURL& secure_proxy_check_url() const override;
-
- bool promo_allowed() const override;
-
- bool holdback() const override;
-
- protected:
- // Test constructor that optionally won't call Init();
- DataReductionProxyParams(int flags,
- bool should_call_init);
-
- // Initialize the values of the proxies, and secure proxy check URL, from
- // command line flags and preprocessor constants, and check that there are
- // corresponding definitions for the allowed configurations.
- bool Init();
-
- // Initialize the values of the proxies, and secure proxy check URL from
- // command line flags and preprocessor constants.
- void InitWithoutChecks();
-
- // Returns the corresponding string from preprocessor constants if defined,
- // and an empty string otherwise.
- virtual std::string GetDefaultOrigin() const;
- virtual std::string GetDefaultFallbackOrigin() const;
- virtual std::string GetDefaultSecureProxyCheckURL() const;
-
private:
std::vector<DataReductionProxyServer> proxies_for_http_;
- net::ProxyServer origin_;
- net::ProxyServer fallback_origin_;
-
- GURL secure_proxy_check_url_;
-
- bool promo_allowed_;
- bool holdback_;
-
bool use_override_proxies_for_http_;
std::vector<DataReductionProxyServer> override_data_reduction_proxy_servers_;
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc
index 3a68f3ab5d1..e79393a9f37 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc
@@ -4,79 +4,52 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
-namespace {
-// Test values to replace the values specified in preprocessor defines.
-static const char kDefaultOrigin[] = "origin.net:80";
-static const char kDefaultFallbackOrigin[] = "fallback.net:80";
-static const char kDefaultSecureProxyCheckURL[] = "http://proxycheck.net/";
-
-static const char kFlagOrigin[] = "https://origin.org:443";
-static const char kFlagFallbackOrigin[] = "fallback.org:80";
-static const char kFlagSecureProxyCheckURL[] = "http://proxycheck.org/";
-}
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
namespace data_reduction_proxy {
-TestDataReductionProxyParams::TestDataReductionProxyParams(
- int flags, unsigned int has_definitions)
- : DataReductionProxyParams(flags, false),
- has_definitions_(has_definitions) {
- init_result_ = Init();
- }
-
-bool TestDataReductionProxyParams::init_result() const {
- return init_result_;
-}
-
-void TestDataReductionProxyParams::SetProxiesForHttp(
- const std::vector<DataReductionProxyServer>& proxies) {
- SetProxiesForHttpForTesting(proxies);
-}
-// Test values to replace the values specified in preprocessor defines.
-std::string TestDataReductionProxyParams::DefaultOrigin() {
- return kDefaultOrigin;
-}
-
-std::string TestDataReductionProxyParams::DefaultFallbackOrigin() {
- return kDefaultFallbackOrigin;
-}
-
-std::string TestDataReductionProxyParams::DefaultSecureProxyCheckURL() {
- return kDefaultSecureProxyCheckURL;
-}
-std::string TestDataReductionProxyParams::FlagOrigin() {
- return kFlagOrigin;
-}
-
-std::string TestDataReductionProxyParams::FlagFallbackOrigin() {
- return kFlagFallbackOrigin;
-}
-
-std::string TestDataReductionProxyParams::FlagSecureProxyCheckURL() {
- return kFlagSecureProxyCheckURL;
-}
-
-std::string TestDataReductionProxyParams::GetDefaultOrigin() const {
- return GetDefinition(
- TestDataReductionProxyParams::HAS_ORIGIN, kDefaultOrigin);
-}
+TestDataReductionProxyParams::TestDataReductionProxyParams()
+ : DataReductionProxyParams(), override_non_secure_proxies_(false) {
+ proxies_for_http_.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("origin.net:80", net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
+ proxies_for_http_.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("fallback.net:80",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
+ }
-std::string TestDataReductionProxyParams::GetDefaultFallbackOrigin() const {
- return GetDefinition(
- TestDataReductionProxyParams::HAS_FALLBACK_ORIGIN,
- kDefaultFallbackOrigin);
-}
+ TestDataReductionProxyParams::~TestDataReductionProxyParams() {}
+
+ void TestDataReductionProxyParams::SetProxiesForHttp(
+ const std::vector<DataReductionProxyServer>& proxies) {
+ DCHECK_GE(2u, proxies_for_http_.size());
+
+ size_t secure_proxies = 0;
+ for (const auto& ps : proxies)
+ if (ps.proxy_server().is_https())
+ secure_proxies++;
+ DCHECK_GE(1u, secure_proxies);
+
+ SetProxiesForHttpForTesting(proxies);
+ proxies_for_http_.clear();
+ for (const auto& ps : proxies) {
+ if (override_non_secure_proxies_ && ps.proxy_server().is_https()) {
+ proxies_for_http_.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("origin.net:80",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
+ } else {
+ proxies_for_http_.push_back(ps);
+ }
+ }
+ }
-std::string TestDataReductionProxyParams::GetDefaultSecureProxyCheckURL()
- const {
- return GetDefinition(
- TestDataReductionProxyParams::HAS_SECURE_PROXY_CHECK_URL,
- kDefaultSecureProxyCheckURL);
+ const std::vector<DataReductionProxyServer>&
+ TestDataReductionProxyParams::proxies_for_http() const {
+ if (override_non_secure_proxies_)
+ return proxies_for_http_;
+ return DataReductionProxyParams::proxies_for_http();
}
-std::string TestDataReductionProxyParams::GetDefinition(
- unsigned int has_def,
- const std::string& definition) const {
- return ((has_definitions_ & has_def) ? definition : std::string());
-}
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h
index f779e291a6d..819063316fc 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h
@@ -15,43 +15,24 @@ class DataReductionProxyServer;
class TestDataReductionProxyParams : public DataReductionProxyParams {
public:
- // Used to emulate having constants defined by the preprocessor.
- enum HasNames {
- HAS_NOTHING = 0x0,
- HAS_ORIGIN = 0x2,
- HAS_FALLBACK_ORIGIN = 0x4,
- HAS_SECURE_PROXY_CHECK_URL = 0x40,
- HAS_EVERYTHING = 0xff,
- };
-
- TestDataReductionProxyParams(int flags,
- unsigned int has_definitions);
- bool init_result() const;
+ TestDataReductionProxyParams();
- void SetProxiesForHttp(const std::vector<DataReductionProxyServer>& proxies);
-
- // Test values to replace the values specified in preprocessor defines.
- static std::string DefaultOrigin();
- static std::string DefaultFallbackOrigin();
- static std::string DefaultSecureProxyCheckURL();
-
- static std::string FlagOrigin();
- static std::string FlagFallbackOrigin();
- static std::string FlagSecureProxyCheckURL();
+ ~TestDataReductionProxyParams() override;
- protected:
- std::string GetDefaultOrigin() const override;
+ void SetProxiesForHttp(const std::vector<DataReductionProxyServer>& proxies);
- std::string GetDefaultFallbackOrigin() const override;
+ // Use non-secure data saver proxies. Useful when a URL request is fetched
+ // from non-SSL mock sockets.
+ void UseNonSecureProxiesForHttp() { override_non_secure_proxies_ = true; }
- std::string GetDefaultSecureProxyCheckURL() const override;
+ const std::vector<DataReductionProxyServer>& proxies_for_http()
+ const override;
private:
- std::string GetDefinition(unsigned int has_def,
- const std::string& definition) const;
+ bool override_non_secure_proxies_;
- unsigned int has_definitions_;
- bool init_result_;
+ std::vector<DataReductionProxyServer> proxies_for_http_;
};
+
} // namespace data_reduction_proxy
#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_PARAMS_TEST_UTILS_H_
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
index 0c983328807..63e8cda5a02 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
@@ -13,6 +13,8 @@
#include "base/command_line.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.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"
@@ -22,19 +24,16 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_ANDROID)
+#include "base/sys_info.h"
+#endif
+
namespace data_reduction_proxy {
class DataReductionProxyParamsTest : public testing::Test {
public:
- void CheckParams(const TestDataReductionProxyParams& params,
- bool expected_init_result,
- bool expected_promo_allowed) {
- EXPECT_EQ(expected_init_result, params.init_result());
- EXPECT_EQ(expected_promo_allowed, params.promo_allowed());
- }
void CheckValues(const TestDataReductionProxyParams& params,
const std::string& expected_origin,
- const std::string& expected_fallback_origin,
- const std::string& expected_secure_proxy_check_url) {
+ const std::string& expected_fallback_origin) {
std::vector<net::ProxyServer> expected_proxies;
if (!expected_origin.empty()) {
expected_proxies.push_back(net::ProxyServer::FromURI(
@@ -49,101 +48,39 @@ class DataReductionProxyParamsTest : public testing::Test {
EXPECT_EQ(expected_proxies,
DataReductionProxyServer::ConvertToNetProxyServers(
params.proxies_for_http()));
- EXPECT_EQ(GURL(expected_secure_proxy_check_url),
- params.secure_proxy_check_url());
}
};
TEST_F(DataReductionProxyParamsTest, EverythingDefined) {
- TestDataReductionProxyParams params(
- DataReductionProxyParams::kPromoAllowed,
- TestDataReductionProxyParams::HAS_EVERYTHING);
- CheckParams(params, true, true);
+ TestDataReductionProxyParams params;
std::vector<DataReductionProxyServer> expected_proxies;
// Both the origin and fallback proxy must have type CORE.
expected_proxies.push_back(DataReductionProxyServer(
- net::ProxyServer::FromURI(TestDataReductionProxyParams::DefaultOrigin(),
+ net::ProxyServer::FromURI("https://proxy.googlezip.net:443",
net::ProxyServer::SCHEME_HTTP),
ProxyServer::CORE));
expected_proxies.push_back(DataReductionProxyServer(
- net::ProxyServer::FromURI(
- TestDataReductionProxyParams::DefaultFallbackOrigin(),
- net::ProxyServer::SCHEME_HTTP),
+ net::ProxyServer::FromURI("compress.googlezip.net:80",
+ net::ProxyServer::SCHEME_HTTP),
ProxyServer::CORE));
EXPECT_EQ(expected_proxies, params.proxies_for_http());
- EXPECT_EQ(GURL(TestDataReductionProxyParams::DefaultSecureProxyCheckURL()),
- params.secure_proxy_check_url());
}
TEST_F(DataReductionProxyParamsTest, Flags) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxy,
- TestDataReductionProxyParams::FlagOrigin());
+ switches::kDataReductionProxy, "http://ovveride-1.com/");
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxyFallback,
- TestDataReductionProxyParams::FlagFallbackOrigin());
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxySecureProxyCheckURL,
- TestDataReductionProxyParams::FlagSecureProxyCheckURL());
- TestDataReductionProxyParams params(
- DataReductionProxyParams::kPromoAllowed,
- TestDataReductionProxyParams::HAS_EVERYTHING);
- CheckParams(params, true, true);
- CheckValues(params, TestDataReductionProxyParams::FlagOrigin(),
- TestDataReductionProxyParams::FlagFallbackOrigin(),
- TestDataReductionProxyParams::FlagSecureProxyCheckURL());
-}
-
-TEST_F(DataReductionProxyParamsTest, CarrierTestFlag) {
- static const char kCarrierTestOrigin[] =
- "http://o-o.preferred.nttdocomodcp-hnd1.proxy-dev.googlezip.net:80";
- static const char kDefaultFallbackOrigin[] = "compress.googlezip.net:80";
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, nullptr);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kEnableDataReductionProxyCarrierTest, kCarrierTestOrigin);
- DataReductionProxyParams params(0);
- std::vector<DataReductionProxyServer> proxies_for_http;
- proxies_for_http.push_back(DataReductionProxyServer(
- net::ProxyServer::FromURI(kCarrierTestOrigin,
- net::ProxyServer::SCHEME_HTTP),
- ProxyServer::CORE));
- proxies_for_http.push_back(DataReductionProxyServer(
- net::ProxyServer::FromURI(kDefaultFallbackOrigin,
- net::ProxyServer::SCHEME_HTTP),
- ProxyServer::CORE));
- EXPECT_EQ(params.proxies_for_http(), proxies_for_http);
-}
-
-TEST_F(DataReductionProxyParamsTest, InvalidConfigurations) {
- const struct {
- bool promo_allowed;
- unsigned int missing_definitions;
- bool expected_result;
- } tests[] = {
- {true, TestDataReductionProxyParams::HAS_NOTHING, true},
- {true, TestDataReductionProxyParams::HAS_ORIGIN, false},
- {true, TestDataReductionProxyParams::HAS_FALLBACK_ORIGIN, false},
- {true, TestDataReductionProxyParams::HAS_SECURE_PROXY_CHECK_URL, false},
- };
-
- for (size_t i = 0; i < arraysize(tests); ++i) {
- int flags = 0;
- if (tests[i].promo_allowed)
- flags |= DataReductionProxyParams::kPromoAllowed;
- TestDataReductionProxyParams params(
- flags,
- TestDataReductionProxyParams::HAS_EVERYTHING &
- ~(tests[i].missing_definitions));
- EXPECT_EQ(tests[i].expected_result, params.init_result()) << i;
- }
+ switches::kDataReductionProxyFallback, "http://ovveride-2.com/");
+ TestDataReductionProxyParams params;
+ CheckValues(params, "http://ovveride-1.com/", "http://ovveride-2.com/");
}
TEST_F(DataReductionProxyParamsTest, AndroidOnePromoFieldTrial) {
- EXPECT_TRUE(params::IsIncludedInAndroidOnePromoFieldTrial(
+ EXPECT_TRUE(params::IsIncludedInAndroidOnePromoFieldTrialForTesting(
"google/sprout/sprout:4.4.4/KPW53/1379542:user/release-keys"));
- EXPECT_FALSE(params::IsIncludedInAndroidOnePromoFieldTrial(
+ EXPECT_FALSE(params::IsIncludedInAndroidOnePromoFieldTrialForTesting(
"google/hammerhead/hammerhead:5.0/LRX210/1570415:user/release-keys"));
}
@@ -403,6 +340,27 @@ TEST_F(DataReductionProxyParamsTest, LoFiPreviewFieldTrial) {
}
}
+TEST_F(DataReductionProxyParamsTest, TrustedSpdyProxyFieldTrial) {
+ const struct {
+ std::string trial_group_name;
+ bool expected_enabled;
+ } tests[] = {
+ {"Enabled", true}, {"Enabled_Control", true}, {"Control", false},
+ {"Disabled", false}, {"enabled", true}, {"Enabled", true},
+ {std::string(), true},
+ };
+
+ for (const auto& test : tests) {
+ variations::testing::ClearAllVariationParams();
+ base::FieldTrialList field_trial_list(nullptr);
+ base::FieldTrialList::CreateFieldTrial(
+ params::GetTrustedSpdyProxyFieldTrialName(), test.trial_group_name);
+
+ EXPECT_EQ(test.expected_enabled,
+ params::IsIncludedInTrustedSpdyProxyFieldTrial());
+ }
+}
+
// Tests if the QUIC field trial is set correctly.
TEST_F(DataReductionProxyParamsTest, QuicFieldTrial) {
const struct {
@@ -503,6 +461,7 @@ TEST_F(DataReductionProxyParamsTest, HoldbackEnabledFieldTrial) {
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
"DataCompressionProxyHoldback", test.trial_group_name));
+ EXPECT_EQ(test.trial_group_name, params::HoldbackFieldTrialGroup());
EXPECT_EQ(test.expected_enabled, params::IsIncludedInHoldbackFieldTrial())
<< test.trial_group_name;
}
@@ -529,6 +488,56 @@ TEST_F(DataReductionProxyParamsTest, PromoFieldTrial) {
}
}
+TEST_F(DataReductionProxyParamsTest, FREPromoFieldTrial) {
+ const struct {
+ std::string trial_group_name;
+ bool expected_enabled;
+ } tests[] = {
+ {"Enabled", true},
+ {"Enabled_Control", true},
+ {"Disabled", false},
+ {"enabled", false},
+ };
+
+ for (const auto& test : tests) {
+ base::FieldTrialList field_trial_list(nullptr);
+
+ ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+ "DataReductionProxyFREPromo", test.trial_group_name));
+ EXPECT_EQ(test.expected_enabled, params::IsIncludedInFREPromoFieldTrial())
+ << test.trial_group_name;
+ }
+}
+
+TEST_F(DataReductionProxyParamsTest, LowMemoryPromoFeature) {
+ const struct {
+ bool expected_in_field_trial;
+ } tests[] = {
+ {false}, {true},
+ };
+
+ for (const auto& test : tests) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ if (test.expected_in_field_trial) {
+ scoped_feature_list.InitAndDisableFeature(
+ features::kDataReductionProxyLowMemoryDevicePromo);
+ } else {
+ scoped_feature_list.InitAndEnableFeature(
+ features::kDataReductionProxyLowMemoryDevicePromo);
+ }
+
+#if defined(OS_ANDROID)
+ EXPECT_EQ(test.expected_in_field_trial && base::SysInfo::IsLowEndDevice(),
+ params::IsIncludedInPromoFieldTrial());
+ EXPECT_EQ(test.expected_in_field_trial && base::SysInfo::IsLowEndDevice(),
+ params::IsIncludedInFREPromoFieldTrial());
+#else
+ EXPECT_FALSE(params::IsIncludedInPromoFieldTrial());
+ EXPECT_FALSE(params::IsIncludedInFREPromoFieldTrial());
+#endif
+ }
+}
+
TEST_F(DataReductionProxyParamsTest, GetConfigServiceURL) {
const struct {
std::string test_case;
@@ -556,11 +565,38 @@ TEST_F(DataReductionProxyParamsTest, GetConfigServiceURL) {
}
}
+TEST_F(DataReductionProxyParamsTest, SecureProxyURL) {
+ const struct {
+ std::string test_case;
+ std::string flag_value;
+ GURL expected;
+ } tests[] = {
+ {
+ "Nothing set", "", GURL("http://check.googlezip.net/connect"),
+ },
+ {
+ "Only command line set", "http://example.com/flag",
+ GURL("http://example.com/flag"),
+ },
+ };
+
+ for (const auto& test : tests) {
+ // Reset all flags.
+ base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
+ if (!test.flag_value.empty()) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kDataReductionProxySecureProxyCheckURL, test.flag_value);
+ }
+ EXPECT_EQ(test.expected, params::GetSecureProxyCheckURL())
+ << test.test_case;
+ }
+}
+
TEST(DataReductionProxyParamsStandaloneTest, OverrideProxiesForHttp) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kDataReductionProxyHttpProxies,
"http://override-first.net;http://override-second.net");
- DataReductionProxyParams params(0);
+ DataReductionProxyParams params;
// Overriding proxies must have type UNSPECIFIED_TYPE.
std::vector<DataReductionProxyServer> expected_override_proxies_for_http;
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 4877facd493..6145b52f76c 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
@@ -59,10 +59,6 @@ const char kDataReductionProxySecureProxyCheckURL[] =
const char kDataReductionProxyServerExperimentsDisabled[] =
"data-reduction-proxy-server-experiments-disabled";
-// Sets a URL to fetch to warm up the data reduction proxy on startup and
-// network changes.
-const char kDataReductionProxyWarmupURL[] = "data-reduction-proxy-warmup-url";
-
// Enable the data reduction proxy.
const char kEnableDataReductionProxy[] = "enable-spdy-proxy-auth";
@@ -70,10 +66,6 @@ const char kEnableDataReductionProxy[] = "enable-spdy-proxy-auth";
const char kEnableDataReductionProxyBypassWarning[] =
"enable-data-reduction-proxy-bypass-warning";
-// Enables the origin of the carrier test data reduction proxy.
-const char kEnableDataReductionProxyCarrierTest[] =
- "enable-data-reduction-proxy-carrier-test";
-
// Enables lite page from the data reduction proxy. This means a lite page
// should be requested instead of placeholders whenever Lo-Fi mode is on. Lo-fi
// must also be enabled via a flag or field trial.
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 0773ba8d281..7ea15f89fde 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
@@ -26,10 +26,8 @@ extern const char kDataReductionProxyLoFiValueSlowConnectionsOnly[];
extern const char kDataReductionProxySecureProxyCheckURL[];
extern const char kDataReductionProxyServerExperimentsDisabled[];
extern const char kDataReductionProxyServerAlternative[];
-extern const char kDataReductionProxyWarmupURL[];
extern const char kEnableDataReductionProxy[];
extern const char kEnableDataReductionProxyBypassWarning[];
-extern const char kEnableDataReductionProxyCarrierTest[];
extern const char kEnableDataReductionProxyForcePingback[];
extern const char kEnableDataReductionProxyLitePage[];
extern const char kEnableDataReductionProxySavingsPromo[];
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc
index ed6759db08d..8a26cb849b6 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc
@@ -9,6 +9,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/version.h"
+#include "components/data_reduction_proxy/core/common/lofi_decider.h"
#include "components/data_reduction_proxy/core/common/version.h"
#include "net/base/net_errors.h"
#include "net/base/url_util.h"
@@ -58,6 +59,22 @@ int64_t ScaleByteCountByRatio(int64_t byte_count,
return static_cast<int64_t>(scaled_byte_count);
}
+// Estimate the size of the original headers of |request|. If |used_drp| is
+// true, then it's assumed that the original request would have used HTTP/1.1,
+// otherwise it assumes that the original request would have used the same
+// protocol as |request| did. This is to account for stuff like HTTP/2 header
+// compression.
+int64_t EstimateOriginalHeaderBytes(const net::URLRequest& request,
+ bool used_drp) {
+ if (used_drp) {
+ // TODO(sclittle): Remove headers added by Data Reduction Proxy when
+ // computing original size. https://crbug.com/535701.
+ return request.response_headers()->raw_headers().size();
+ }
+ return std::max<int64_t>(0, request.GetTotalReceivedBytes() -
+ request.received_response_content_length());
+}
+
} // namespace
namespace util {
@@ -187,6 +204,30 @@ int64_t CalculateEffectiveOCL(const net::URLRequest& request) {
content_length_from_header);
}
+int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request,
+ bool used_drp,
+ const LoFiDecider* lofi_decider) {
+ if (request.was_cached() || !request.response_headers())
+ return request.GetTotalReceivedBytes();
+
+ if (lofi_decider) {
+ if (lofi_decider->IsClientLoFiAutoReloadRequest(request))
+ return 0;
+
+ int64_t first, last, length;
+ if (lofi_decider->IsClientLoFiImageRequest(request) &&
+ request.response_headers()->GetContentRangeFor206(&first, &last,
+ &length) &&
+ length > request.received_response_content_length()) {
+ return EstimateOriginalHeaderBytes(request, used_drp) + length;
+ }
+ }
+
+ return used_drp ? EstimateOriginalHeaderBytes(request, used_drp) +
+ util::CalculateEffectiveOCL(request)
+ : request.GetTotalReceivedBytes();
+}
+
} // namespace util
namespace protobuf_parser {
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h
index 811faa9025f..a528060b421 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h
@@ -28,6 +28,8 @@ class URLRequest;
namespace data_reduction_proxy {
+class LoFiDecider;
+
enum class Client {
UNKNOWN,
CRONET_ANDROID,
@@ -89,6 +91,13 @@ bool ApplyProxyConfigToProxyInfo(const net::ProxyConfig& proxy_config,
// for partial responses if necessary.
int64_t CalculateEffectiveOCL(const net::URLRequest& request);
+// Given a |request| that went through the Data Reduction Proxy if |used_drp| is
+// true, this function estimates how many bytes would have been received if the
+// response had been received directly from the origin without any data saver
+// optimizations.
+int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request,
+ bool used_drp,
+ const LoFiDecider* lofi_decider);
} // namespace util
namespace protobuf_parser {
diff --git a/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto b/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto
index 1725604afae..457cbffdf63 100644
--- a/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto
+++ b/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto
@@ -101,4 +101,8 @@ message PageloadMetrics {
// The previews type that was used on the page.
optional PreviewsType previews_type = 18;
+
+ // The name of the finch group for the data reduction proxy holdback
+ // experiment. This can be an empty string if not included in a finch group.
+ optional string holdback_group = 19;
}
diff --git a/chromium/components/data_usage/OWNERS b/chromium/components/data_usage/OWNERS
index 78d68ca2be7..e2172b46617 100644
--- a/chromium/components/data_usage/OWNERS
+++ b/chromium/components/data_usage/OWNERS
@@ -1,4 +1,6 @@
bengr@chromium.org
rajendrant@chromium.org
sclittle@chromium.org
-tbansal@chromium.org \ No newline at end of file
+tbansal@chromium.org
+
+# COMPONENT: Internals>Network>DataUse \ No newline at end of file
diff --git a/chromium/components/data_usage/core/data_use.cc b/chromium/components/data_usage/core/data_use.cc
index 04cb82300f6..838b7ffa232 100644
--- a/chromium/components/data_usage/core/data_use.cc
+++ b/chromium/components/data_usage/core/data_use.cc
@@ -11,8 +11,9 @@ namespace {
bool AreNonByteCountFieldsEqual(const DataUse& a, const DataUse& b) {
return a.url == b.url && a.request_start == b.request_start &&
a.first_party_for_cookies == b.first_party_for_cookies &&
- a.tab_id == b.tab_id && a.connection_type == b.connection_type &&
- a.mcc_mnc == b.mcc_mnc;
+ 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) {
@@ -21,6 +22,10 @@ bool AreByteCountFieldsEqual(const DataUse& a, const DataUse& b) {
} // namespace
+// static
+const DataUse::MainFrameGlobalRequestID
+ DataUse::kInvalidMainFrameGlobalRequestID(-1, -1);
+
DataUse::DataUse(const GURL& url,
const base::TimeTicks& request_start,
const GURL& first_party_for_cookies,
@@ -33,6 +38,7 @@ DataUse::DataUse(const GURL& url,
request_start(request_start),
first_party_for_cookies(first_party_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),
diff --git a/chromium/components/data_usage/core/data_use.h b/chromium/components/data_usage/core/data_use.h
index b56346adc07..c3994790ffb 100644
--- a/chromium/components/data_usage/core/data_use.h
+++ b/chromium/components/data_usage/core/data_use.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <string>
+#include <utility>
#include "base/time/time.h"
#include "net/base/network_change_notifier.h"
@@ -16,6 +17,11 @@
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& first_party_for_cookies,
@@ -39,7 +45,18 @@ struct DataUse {
// started.
base::TimeTicks request_start;
GURL first_party_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.
int32_t 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
diff --git a/chromium/components/data_use_measurement/OWNERS b/chromium/components/data_use_measurement/OWNERS
index 2783dea1c6e..9bcf864f6a6 100644
--- a/chromium/components/data_use_measurement/OWNERS
+++ b/chromium/components/data_use_measurement/OWNERS
@@ -1 +1,3 @@
file://components/data_reduction_proxy/OWNERS
+
+# COMPONENT: Internals>Network>DataUse
diff --git a/chromium/components/data_use_measurement/core/data_use.cc b/chromium/components/data_use_measurement/core/data_use.cc
index 99fb8cb2f95..8750f45e2bf 100644
--- a/chromium/components/data_use_measurement/core/data_use.cc
+++ b/chromium/components/data_use_measurement/core/data_use.cc
@@ -13,13 +13,11 @@ DataUse::DataUse(TrafficType traffic_type)
DataUse::~DataUse() {}
-void DataUse::MergeFrom(const DataUse& other) {
- // Traffic type need not be same while merging. One of the data use created
- // when mainframe is created could have UNKNOWN traffic type, and later merged
- // with the data use created for its mainframe request which could be
- // USER_TRAFFIC.
- total_bytes_sent_ += other.total_bytes_sent_;
- total_bytes_received_ += other.total_bytes_received_;
+void DataUse::IncrementTotalBytes(int64_t bytes_received, int64_t bytes_sent) {
+ total_bytes_received_ += bytes_received;
+ total_bytes_sent_ += bytes_sent;
+ DCHECK_LE(0, total_bytes_received_);
+ DCHECK_LE(0, total_bytes_sent_);
}
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/data_use.h b/chromium/components/data_use_measurement/core/data_use.h
index d2b789706fd..d9b41e2bb7e 100644
--- a/chromium/components/data_use_measurement/core/data_use.h
+++ b/chromium/components/data_use_measurement/core/data_use.h
@@ -40,11 +40,6 @@ class DataUse : public base::SupportsUserData {
explicit DataUse(TrafficType traffic_type);
~DataUse() override;
- // Merge data use from another instance.
- // TODO(rajendrant): Check if the merge can be removed. Otherwise user data
- // needs to support mergeability.
- void MergeFrom(const DataUse& other);
-
// Returns the page URL.
const GURL& url() const { return url_; }
@@ -56,6 +51,10 @@ class DataUse : public base::SupportsUserData {
description_ = description;
}
+ // Increments the total received and sent byte counts. Can be used to
+ // decrement the byte counts as well.
+ void IncrementTotalBytes(int64_t bytes_received, int64_t bytes_sent);
+
int64_t total_bytes_received() const { return total_bytes_received_; }
int64_t total_bytes_sent() const { return total_bytes_sent_; }
@@ -63,10 +62,6 @@ class DataUse : public base::SupportsUserData {
TrafficType traffic_type() const { return traffic_type_; }
private:
- // TODO(rajendrant): Remove this friend after adding member function to
- // increment total sent/received bytes.
- friend class DataUseRecorder;
-
GURL url_;
std::string description_;
const TrafficType traffic_type_;
diff --git a/chromium/components/data_use_measurement/core/data_use_recorder.cc b/chromium/components/data_use_measurement/core/data_use_recorder.cc
index f9a387043b6..c4864e5f959 100644
--- a/chromium/components/data_use_measurement/core/data_use_recorder.cc
+++ b/chromium/components/data_use_measurement/core/data_use_recorder.cc
@@ -14,17 +14,54 @@ DataUseRecorder::DataUseRecorder(DataUse::TrafficType traffic_type)
DataUseRecorder::~DataUseRecorder() {}
bool DataUseRecorder::IsDataUseComplete() {
- return pending_url_requests_.empty() && pending_data_sources_.empty();
+ return pending_url_requests_.empty();
+}
+
+void DataUseRecorder::GetPendingURLRequests(
+ std::vector<net::URLRequest*>* requests) const {
+ // Reference to |pending_url_requests_| could be returned instead of copying
+ // to a vector. But that leads to issues when the caller calls other member
+ // functions that modify/erase the same map, while iterating.
+ requests->reserve(pending_url_requests_.size());
+ for (const auto& request : pending_url_requests_)
+ requests->push_back(request.first);
}
void DataUseRecorder::AddPendingURLRequest(net::URLRequest* request) {
- pending_url_requests_.insert(request);
+ pending_url_requests_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(request),
+ std::forward_as_tuple());
}
void DataUseRecorder::OnUrlRequestDestroyed(net::URLRequest* request) {
pending_url_requests_.erase(request);
}
+void DataUseRecorder::MovePendingURLRequestTo(DataUseRecorder* other,
+ net::URLRequest* request) {
+ auto request_it = pending_url_requests_.find(request);
+ DCHECK(request_it != pending_url_requests_.end());
+ DCHECK(other->pending_url_requests_.find(request) ==
+ other->pending_url_requests_.end());
+
+ // Increment the bytes of the request in |other|, and decrement the bytes in
+ // |this|.
+ // TODO(rajendrant): Check if the moving the bytes in |data_use_| needs to be
+ // propogated to observers, which could store per-request user data.
+ other->AddPendingURLRequest(request);
+ other->UpdateNetworkByteCounts(request, request_it->second.bytes_received,
+ request_it->second.bytes_sent);
+ data_use_.IncrementTotalBytes(-request_it->second.bytes_received,
+ -request_it->second.bytes_sent);
+ pending_url_requests_.erase(request_it);
+}
+
+base::TimeTicks DataUseRecorder::GetPendingURLRequestStartTime(
+ net::URLRequest* request) {
+ auto request_it = pending_url_requests_.find(request);
+ return request_it->second.started_time;
+}
+
void DataUseRecorder::RemoveAllPendingURLRequests() {
pending_url_requests_.clear();
}
@@ -33,32 +70,21 @@ void DataUseRecorder::OnBeforeUrlRequest(net::URLRequest* request) {}
void DataUseRecorder::OnNetworkBytesReceived(net::URLRequest* request,
int64_t bytes_received) {
- data_use_.total_bytes_received_ += bytes_received;
+ UpdateNetworkByteCounts(request, bytes_received, 0);
}
void DataUseRecorder::OnNetworkBytesSent(net::URLRequest* request,
int64_t bytes_sent) {
- data_use_.total_bytes_sent_ += bytes_sent;
-}
-
-void DataUseRecorder::AddPendingDataSource(void* source) {
- pending_data_sources_.insert(source);
-}
-
-bool DataUseRecorder::HasPendingDataSource(void* source) {
- return pending_data_sources_.find(source) != pending_data_sources_.end();
-}
-
-void DataUseRecorder::RemovePendingDataSource(void* source) {
- pending_data_sources_.erase(source);
-}
-
-bool DataUseRecorder::HasPendingURLRequest(net::URLRequest* request) {
- return pending_url_requests_.find(request) != pending_url_requests_.end();
+ UpdateNetworkByteCounts(request, 0, bytes_sent);
}
-void DataUseRecorder::MergeFrom(DataUseRecorder* other) {
- data_use_.MergeFrom(other->data_use());
+void DataUseRecorder::UpdateNetworkByteCounts(net::URLRequest* request,
+ int64_t bytes_received,
+ int64_t bytes_sent) {
+ data_use_.IncrementTotalBytes(bytes_received, bytes_sent);
+ auto request_it = pending_url_requests_.find(request);
+ request_it->second.bytes_received += bytes_received;
+ request_it->second.bytes_sent += bytes_sent;
}
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/data_use_recorder.h b/chromium/components/data_use_measurement/core/data_use_recorder.h
index 2fee7bf043b..aa2dedbc94c 100644
--- a/chromium/components/data_use_measurement/core/data_use_recorder.h
+++ b/chromium/components/data_use_measurement/core/data_use_recorder.h
@@ -7,9 +7,12 @@
#include <stdint.h>
-#include "base/containers/hash_tables.h"
+#include <map>
+#include <vector>
+
#include "base/macros.h"
#include "base/supports_user_data.h"
+#include "base/time/time.h"
#include "components/data_use_measurement/core/data_use.h"
#include "net/base/net_export.h"
@@ -25,14 +28,29 @@ namespace data_use_measurement {
// tracked by exactly one DataUseRecorder.
class DataUseRecorder {
public:
+ // Stores network data used by a URLRequest.
+ struct URLRequestDataUse {
+ URLRequestDataUse()
+ : bytes_received(0),
+ bytes_sent(0),
+ started_time(base::TimeTicks::Now()) {}
+
+ int64_t bytes_received;
+ int64_t bytes_sent;
+
+ // Time the request started and associated with the DataUseRecorder.
+ base::TimeTicks started_time;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(URLRequestDataUse);
+ };
+
explicit DataUseRecorder(DataUse::TrafficType traffic_type);
virtual ~DataUseRecorder();
// Returns the actual data used by the entity being tracked.
DataUse& data_use() { return data_use_; }
- const base::hash_set<net::URLRequest*>& pending_url_requests() const {
- return pending_url_requests_;
- }
+
const net::URLRequest* main_url_request() const { return main_url_request_; }
void set_main_url_request(const net::URLRequest* request) {
@@ -53,46 +71,40 @@ class DataUseRecorder {
// by the entity tracked by this recorder. For example,
bool IsDataUseComplete();
+ // Populate the pending requests to |requests|.
+ // Reference to the map is not returned since other member functions that
+ // modify/erase could be called while iterating.
+ void GetPendingURLRequests(std::vector<net::URLRequest*>* requests) const;
+
// Adds |request| to the list of pending URLRequests that ascribe data use to
// this recorder.
void AddPendingURLRequest(net::URLRequest* request);
+ // Moves pending |request| from |this| recorder to |other| recorder, and
+ // updates the data use for the recorders.
+ void MovePendingURLRequestTo(DataUseRecorder* other,
+ net::URLRequest* request);
+
+ base::TimeTicks GetPendingURLRequestStartTime(net::URLRequest* request);
+
// Clears the list of pending URLRequests that ascribe data use to this
// recorder.
void RemoveAllPendingURLRequests();
- // Returns whether there are any pending URLRequests whose data use is tracked
- // by this DataUseRecorder.
- bool HasPendingURLRequest(net::URLRequest* request);
-
- // Merge another DataUseRecorder to this instance.
- void MergeFrom(DataUseRecorder* other);
-
- private:
- friend class DataUseAscriber;
-
- // Methods for tracking data use sources. These sources can initiate
- // URLRequests directly or indirectly. The entity whose data use is being
- // tracked by this recorder may comprise of sub-entities each of which use
- // network data. These helper methods help track these sub-entities.
- // A recorder will not be marked as having completed data use as long as it
- // has pending data sources.
- void AddPendingDataSource(void* source);
- bool HasPendingDataSource(void* source);
- void RemovePendingDataSource(void* source);
-
// Network Delegate methods:
void OnBeforeUrlRequest(net::URLRequest* request);
void OnUrlRequestDestroyed(net::URLRequest* request);
void OnNetworkBytesSent(net::URLRequest* request, int64_t bytes_sent);
void OnNetworkBytesReceived(net::URLRequest* request, int64_t bytes_received);
- // Pending URLRequests whose data is being tracked by this DataUseRecorder.
- base::hash_set<net::URLRequest*> pending_url_requests_;
+ private:
+ // Updates the network data use for the url request.
+ void UpdateNetworkByteCounts(net::URLRequest* request,
+ int64_t bytes_received,
+ int64_t bytes_sent);
- // Data sources other than URLRequests, whose data is being tracked by this
- // DataUseRecorder.
- base::hash_set<const void*> pending_data_sources_;
+ // Pending URLRequests whose data is being tracked by this DataUseRecorder.
+ std::map<net::URLRequest*, URLRequestDataUse> pending_url_requests_;
// The main frame URLRequest for page loads. Null if this is not tracking a
// page load.
diff --git a/chromium/components/data_use_measurement/core/data_use_user_data.cc b/chromium/components/data_use_measurement/core/data_use_user_data.cc
index 4cb0f12cdd0..d164110ee36 100644
--- a/chromium/components/data_use_measurement/core/data_use_user_data.cc
+++ b/chromium/components/data_use_measurement/core/data_use_user_data.cc
@@ -131,6 +131,8 @@ std::string DataUseUserData::GetServiceNameAsString(ServiceName service_name) {
return "Payments";
case LARGE_ICON_SERVICE:
return "LargeIconService";
+ case MACHINE_INTELLIGENCE:
+ return "MachineIntelligence";
}
return "INVALID";
}
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 0ea0ae8d437..b8338a2ef9c 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
@@ -67,6 +67,7 @@ class DataUseUserData : public base::SupportsUserData::Data {
UKM,
PAYMENTS,
LARGE_ICON_SERVICE,
+ MACHINE_INTELLIGENCE,
};
// Data use broken by content type. This enum must remain synchronized
diff --git a/chromium/components/device_event_log/device_event_log_impl.cc b/chromium/components/device_event_log/device_event_log_impl.cc
index 5ea67e9e20b..db7eb5b6adb 100644
--- a/chromium/components/device_event_log/device_event_log_impl.cc
+++ b/chromium/components/device_event_log/device_event_log_impl.cc
@@ -259,7 +259,7 @@ void DeviceEventLogImpl::AddEntry(const char* file,
LogLevel log_level,
const std::string& event) {
LogEntry entry(file, file_line, log_type, log_level, event);
- if (!task_runner_->RunsTasksOnCurrentThread()) {
+ if (!task_runner_->RunsTasksInCurrentSequence()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(&DeviceEventLogImpl::AddLogEntry,
weak_ptr_factory_.GetWeakPtr(), entry));
@@ -269,7 +269,7 @@ void DeviceEventLogImpl::AddEntry(const char* file,
}
void DeviceEventLogImpl::AddLogEntry(const LogEntry& entry) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (!entries_.empty()) {
LogEntry& last = entries_.back();
if (LogEntryMatches(last, entry)) {
@@ -307,7 +307,7 @@ std::string DeviceEventLogImpl::GetAsString(StringOrder order,
const std::string& types,
LogLevel max_level,
size_t max_events) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (entries_.empty())
return "No Log Entries.";
diff --git a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
index cd768d0f2d6..1ba13dfab55 100644
--- a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
+++ b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
@@ -34,7 +34,7 @@ namespace {
const size_t kAllocationSize = 4 * 1024 * 1024;
// Global atomic to generate unique discardable shared memory IDs.
-base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
+base::AtomicSequenceNumber g_next_discardable_shared_memory_id;
class DiscardableMemoryImpl : public base::DiscardableMemory {
public:
diff --git a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
index 0913399d492..aca8774ab80 100644
--- a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
+++ b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
@@ -209,7 +209,7 @@ int64_t GetDefaultMemoryLimit() {
const int kEnforceMemoryPolicyDelayMs = 1000;
// Global atomic to generate unique discardable shared memory IDs.
-base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
+base::AtomicSequenceNumber g_next_discardable_shared_memory_id;
} // namespace
@@ -247,8 +247,8 @@ DiscardableSharedMemoryManager::~DiscardableSharedMemoryManager() {
}
void DiscardableSharedMemoryManager::Bind(
- const service_manager::BindSourceInfo& source_info,
- mojom::DiscardableSharedMemoryManagerRequest request) {
+ mojom::DiscardableSharedMemoryManagerRequest request,
+ const service_manager::BindSourceInfo& source_info) {
mojo::MakeStrongBinding(
base::MakeUnique<MojoDiscardableSharedMemoryManagerImpl>(
next_client_id_++, this),
diff --git a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.h b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.h
index be03456e690..23f5203d7ae 100644
--- a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.h
+++ b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.h
@@ -49,8 +49,8 @@ class DISCARDABLE_MEMORY_EXPORT DiscardableSharedMemoryManager
~DiscardableSharedMemoryManager() override;
// Bind the manager to a mojo interface request.
- void Bind(const service_manager::BindSourceInfo& source_info,
- mojom::DiscardableSharedMemoryManagerRequest request);
+ void Bind(mojom::DiscardableSharedMemoryManagerRequest request,
+ const service_manager::BindSourceInfo& source_info);
// Overridden from base::DiscardableMemoryAllocator:
std::unique_ptr<base::DiscardableMemory> AllocateLockedDiscardableMemory(
diff --git a/chromium/components/dom_distiller/content/browser/distillability_driver.cc b/chromium/components/dom_distiller/content/browser/distillability_driver.cc
index 88d4ccf21f6..0bb7d2befee 100644
--- a/chromium/components/dom_distiller/content/browser/distillability_driver.cc
+++ b/chromium/components/dom_distiller/content/browser/distillability_driver.cc
@@ -54,7 +54,6 @@ DistillabilityDriver::~DistillabilityDriver() {
}
void DistillabilityDriver::CreateDistillabilityService(
- const service_manager::BindSourceInfo& source_info,
mojom::DistillabilityServiceRequest request) {
mojo::MakeStrongBinding(
base::MakeUnique<DistillabilityServiceImpl>(weak_factory_.GetWeakPtr()),
diff --git a/chromium/components/dom_distiller/content/browser/distillability_driver.h b/chromium/components/dom_distiller/content/browser/distillability_driver.h
index bf2f0477e07..0cc51eb9a6e 100644
--- a/chromium/components/dom_distiller/content/browser/distillability_driver.h
+++ b/chromium/components/dom_distiller/content/browser/distillability_driver.h
@@ -11,7 +11,6 @@
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
namespace dom_distiller {
@@ -22,7 +21,6 @@ class DistillabilityDriver
public:
~DistillabilityDriver() override;
void CreateDistillabilityService(
- const service_manager::BindSourceInfo& source_info,
mojom::DistillabilityServiceRequest request);
void SetDelegate(const base::Callback<void(bool, bool)>& delegate);
diff --git a/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc b/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
index 2dea5f1be2e..800881a29f2 100644
--- a/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
+++ b/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc
@@ -22,15 +22,6 @@ DistillerJavaScriptServiceImpl::DistillerJavaScriptServiceImpl(
DistillerJavaScriptServiceImpl::~DistillerJavaScriptServiceImpl() {}
-void DistillerJavaScriptServiceImpl::HandleDistillerClosePanelCall(
- bool animate) {
- base::RecordAction(base::UserMetricsAction("DomDistiller_ViewOriginal"));
- if (!distiller_ui_handle_) {
- return;
- }
- distiller_ui_handle_->ClosePanel(animate);
-}
-
void DistillerJavaScriptServiceImpl::HandleDistillerOpenSettingsCall() {
if (!distiller_ui_handle_) {
return;
@@ -43,7 +34,6 @@ void DistillerJavaScriptServiceImpl::HandleDistillerOpenSettingsCall() {
void CreateDistillerJavaScriptService(
content::RenderFrameHost* render_frame_host,
DistillerUIHandle* distiller_ui_handle,
- const service_manager::BindSourceInfo& source_info,
mojom::DistillerJavaScriptServiceRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<DistillerJavaScriptServiceImpl>(
render_frame_host, distiller_ui_handle),
diff --git a/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.h b/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
index d9a1048f81f..90698a638c9 100644
--- a/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
+++ b/chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.h
@@ -9,7 +9,6 @@
#include "components/dom_distiller/content/browser/distiller_ui_handle.h"
#include "components/dom_distiller/content/common/distiller_javascript_service.mojom.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
namespace dom_distiller {
@@ -23,9 +22,6 @@ class DistillerJavaScriptServiceImpl
// Mojo mojom::DistillerJavaScriptService implementation.
- // Make a call into Android to close the overlay panel containing reader mode.
- void HandleDistillerClosePanelCall(bool animate) override;
-
// Show the Android view containing Reader Mode settings.
void HandleDistillerOpenSettingsCall() override;
@@ -40,7 +36,6 @@ class DistillerJavaScriptServiceImpl
void CreateDistillerJavaScriptService(
content::RenderFrameHost* render_frame_host,
DistillerUIHandle* distiller_ui_handle,
- const service_manager::BindSourceInfo& source_info,
mojom::DistillerJavaScriptServiceRequest request);
} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc b/chromium/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc
index fb46895073e..eaf9ed8717e 100644
--- a/chromium/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc
+++ b/chromium/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc
@@ -238,7 +238,6 @@ IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeImages) {
HasSubstr("src=\"http://www.google.com/absoluteimage.png\""));
}
-
IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeVideos) {
DistillerPageWebContents distiller_page(
shell()->web_contents()->GetBrowserContext(),
@@ -314,7 +313,7 @@ IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
}
IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
- DISABLED_UsingCurrentWebContentsNotFinishedLoadingYet) {
+ UsingCurrentWebContentsNotFinishedLoadingYet) {
std::string url(kSimpleArticlePath);
bool expect_new_web_contents = false;
bool setup_main_frame_observer = true;
@@ -326,7 +325,7 @@ IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
}
IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
- DISABLED_UsingCurrentWebContentsReadyForDistillation) {
+ UsingCurrentWebContentsReadyForDistillation) {
std::string url(kSimpleArticlePath);
bool expect_new_web_contents = false;
bool setup_main_frame_observer = true;
diff --git a/chromium/components/dom_distiller/content/browser/distiller_ui_handle.h b/chromium/components/dom_distiller/content/browser/distiller_ui_handle.h
index d97384eafd5..36e58ff509f 100644
--- a/chromium/components/dom_distiller/content/browser/distiller_ui_handle.h
+++ b/chromium/components/dom_distiller/content/browser/distiller_ui_handle.h
@@ -21,9 +21,6 @@ class DistillerUIHandle {
// Open the UI settings for dom distiller.
virtual void OpenSettings(content::WebContents* web_contents) = 0;
- // Close the Reader Mode panel.
- virtual void ClosePanel(bool animate) = 0;
-
private:
DISALLOW_COPY_AND_ASSIGN(DistillerUIHandle);
};
diff --git a/chromium/components/dom_distiller/content/common/distiller_javascript_service.mojom b/chromium/components/dom_distiller/content/common/distiller_javascript_service.mojom
index 8589198d0ed..ffe03bac518 100644
--- a/chromium/components/dom_distiller/content/common/distiller_javascript_service.mojom
+++ b/chromium/components/dom_distiller/content/common/distiller_javascript_service.mojom
@@ -7,10 +7,6 @@ module dom_distiller.mojom;
// This service is implemented by the browser process and is used by the
// renderer when a distiller JavaScript function is called.
interface DistillerJavaScriptService {
- // Handle closing the overlay panel that contains Reader Mode; the
- // "distiller.close" function.
- HandleDistillerClosePanelCall(bool animate);
-
// Open the Android view containing settings for Reader Mode; the
// "distiller.openSettings" function.
HandleDistillerOpenSettingsCall();
diff --git a/chromium/components/dom_distiller/content/renderer/distillability_agent.cc b/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
index 4bee7c3e57f..deea34d2167 100644
--- a/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
+++ b/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
@@ -41,7 +41,8 @@ enum RejectionBuckets {
// The number of updates can be from 0 to 2. See the tests in
// "distillable_page_utils_browsertest.cc".
// Most heuristics types only require one update after parsing.
-// Adaboost is the only one doing the second update, which is after loading.
+// Adaboost-based heuristics are the only ones doing the second update,
+// which is after loading.
bool NeedToUpdate(bool is_loaded) {
switch (GetDistillerHeuristicsType()) {
case DistillerHeuristicsType::ALWAYS_TRUE:
@@ -49,6 +50,7 @@ bool NeedToUpdate(bool is_loaded) {
case DistillerHeuristicsType::OG_ARTICLE:
return !is_loaded;
case DistillerHeuristicsType::ADABOOST_MODEL:
+ case DistillerHeuristicsType::ALL_ARTICLES:
return true;
case DistillerHeuristicsType::NONE:
default:
@@ -58,7 +60,8 @@ bool NeedToUpdate(bool is_loaded) {
// Returns whether this update is the last one for the page.
bool IsLast(bool is_loaded) {
- if (GetDistillerHeuristicsType() == DistillerHeuristicsType::ADABOOST_MODEL)
+ if (GetDistillerHeuristicsType() == DistillerHeuristicsType::ADABOOST_MODEL ||
+ GetDistillerHeuristicsType() == DistillerHeuristicsType::ALL_ARTICLES)
return is_loaded;
return true;
@@ -76,7 +79,8 @@ bool IsBlacklisted(const GURL& url) {
bool IsDistillablePageAdaboost(WebDocument& doc,
const DistillablePageDetector* detector,
const DistillablePageDetector* long_page,
- bool is_last) {
+ bool is_last,
+ bool exclude_mobile) {
WebDistillabilityFeatures features = doc.DistillabilityFeatures();
GURL parsed_url(doc.Url());
if (!parsed_url.is_valid()) {
@@ -145,7 +149,7 @@ bool IsDistillablePageAdaboost(WebDocument& doc,
if (blacklisted) {
return false;
}
- if (features.is_mobile_friendly) {
+ if (exclude_mobile && features.is_mobile_friendly) {
return false;
}
return distillable && long_article;
@@ -158,9 +162,13 @@ bool IsDistillablePage(WebDocument& doc, bool is_last) {
case DistillerHeuristicsType::OG_ARTICLE:
return doc.DistillabilityFeatures().open_graph;
case DistillerHeuristicsType::ADABOOST_MODEL:
- return IsDistillablePageAdaboost(doc,
- DistillablePageDetector::GetNewModel(),
- DistillablePageDetector::GetLongPageModel(), is_last);
+ return IsDistillablePageAdaboost(
+ doc, DistillablePageDetector::GetNewModel(),
+ DistillablePageDetector::GetLongPageModel(), is_last, true);
+ case DistillerHeuristicsType::ALL_ARTICLES:
+ return IsDistillablePageAdaboost(
+ doc, DistillablePageDetector::GetNewModel(),
+ DistillablePageDetector::GetLongPageModel(), is_last, false);
case DistillerHeuristicsType::NONE:
default:
return false;
diff --git a/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.cc b/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.cc
index b3ae09b0648..5be613204b9 100644
--- a/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.cc
+++ b/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.cc
@@ -60,7 +60,6 @@ void DistillerJsRenderFrameObserver::RegisterMojoInterface() {
}
void DistillerJsRenderFrameObserver::CreateDistillerPageNotifierService(
- const service_manager::BindSourceInfo& source_info,
mojom::DistillerPageNotifierServiceRequest request) {
mojo::MakeStrongBinding(
base::MakeUnique<DistillerPageNotifierServiceImpl>(this),
diff --git a/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.h b/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.h
index f3ad5557d07..c57c3c4a0d0 100644
--- a/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.h
+++ b/chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.h
@@ -11,7 +11,6 @@
#include "components/dom_distiller/content/renderer/distiller_page_notifier_service_impl.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "v8/include/v8.h"
@@ -41,7 +40,6 @@ class DistillerJsRenderFrameObserver : public content::RenderFrameObserver {
private:
void CreateDistillerPageNotifierService(
- const service_manager::BindSourceInfo& source_info,
mojom::DistillerPageNotifierServiceRequest request);
// RenderFrameObserver implementation.
diff --git a/chromium/components/dom_distiller/content/renderer/distiller_native_javascript.cc b/chromium/components/dom_distiller/content/renderer/distiller_native_javascript.cc
index 86dd5ccd925..f4293659abf 100644
--- a/chromium/components/dom_distiller/content/renderer/distiller_native_javascript.cc
+++ b/chromium/components/dom_distiller/content/renderer/distiller_native_javascript.cc
@@ -45,12 +45,6 @@ void DistillerNativeJavaScript::AddJavaScriptObjectToFrame(
// wrapper function for binding. Note that calling distiller_js_service.get()
// does not transfer ownership of the interface.
BindFunctionToObject(
- distiller_obj, "closePanel",
- base::Bind(
- &mojom::DistillerJavaScriptService::HandleDistillerClosePanelCall,
- base::Unretained(distiller_js_service_.get())));
-
- BindFunctionToObject(
distiller_obj, "openSettings",
base::Bind(
&mojom::DistillerJavaScriptService::HandleDistillerOpenSettingsCall,
diff --git a/chromium/components/dom_distiller/core/BUILD.gn b/chromium/components/dom_distiller/core/BUILD.gn
index 55652429d24..47e5efc4a39 100644
--- a/chromium/components/dom_distiller/core/BUILD.gn
+++ b/chromium/components/dom_distiller/core/BUILD.gn
@@ -52,7 +52,6 @@ static_library("core") {
"url_utils.cc",
"url_utils.h",
"url_utils_android.cc",
- "url_utils_android.h",
"viewer.cc",
"viewer.h",
]
@@ -80,8 +79,6 @@ static_library("core") {
if (is_android) {
sources += [
- "android/core_jni_registrar.cc",
- "android/core_jni_registrar.h",
"dom_distiller_service_android.cc",
"dom_distiller_service_android.h",
]
diff --git a/chromium/components/dom_distiller/core/distilled_page_prefs_android.cc b/chromium/components/dom_distiller/core/distilled_page_prefs_android.cc
index b10cb0fb3ee..0dc6bd8b32b 100644
--- a/chromium/components/dom_distiller/core/distilled_page_prefs_android.cc
+++ b/chromium/components/dom_distiller/core/distilled_page_prefs_android.cc
@@ -71,10 +71,6 @@ jlong Init(JNIEnv* env,
return reinterpret_cast<intptr_t>(distilled_page_prefs_android);
}
-bool DistilledPagePrefsAndroid::Register(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
void DistilledPagePrefsAndroid::AddObserver(JNIEnv* env,
const JavaParamRef<jobject>& obj,
jlong observer_ptr) {
diff --git a/chromium/components/dom_distiller/core/distilled_page_prefs_android.h b/chromium/components/dom_distiller/core/distilled_page_prefs_android.h
index aa0a82003c2..22521ed3317 100644
--- a/chromium/components/dom_distiller/core/distilled_page_prefs_android.h
+++ b/chromium/components/dom_distiller/core/distilled_page_prefs_android.h
@@ -20,7 +20,6 @@ class DistilledPagePrefsAndroid {
jobject obj,
DistilledPagePrefs* distilled_page_prefs_ptr);
virtual ~DistilledPagePrefsAndroid();
- static bool Register(JNIEnv* env);
void SetFontFamily(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint font_family);
diff --git a/chromium/components/dom_distiller/core/dom_distiller_service_android.cc b/chromium/components/dom_distiller/core/dom_distiller_service_android.cc
index 0f4b1311996..81883a6ff8c 100644
--- a/chromium/components/dom_distiller/core/dom_distiller_service_android.cc
+++ b/chromium/components/dom_distiller/core/dom_distiller_service_android.cc
@@ -55,9 +55,5 @@ jlong DomDistillerServiceAndroid::GetDistilledPagePrefsPtr(
return reinterpret_cast<intptr_t>(service_->GetDistilledPagePrefs());
}
-bool DomDistillerServiceAndroid::Register(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
} // namespace android
} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/core/dom_distiller_service_android.h b/chromium/components/dom_distiller/core/dom_distiller_service_android.h
index 8f52615b6be..e0e133f179d 100644
--- a/chromium/components/dom_distiller/core/dom_distiller_service_android.h
+++ b/chromium/components/dom_distiller/core/dom_distiller_service_android.h
@@ -21,7 +21,6 @@ class DomDistillerServiceAndroid {
public:
DomDistillerServiceAndroid(DomDistillerService* service);
virtual ~DomDistillerServiceAndroid();
- static bool Register(JNIEnv* env);
// Returns native pointer to native DistilledPagePrefs registered with
// DomDistillerService.
jlong GetDistilledPagePrefsPtr(
diff --git a/chromium/components/dom_distiller/core/dom_distiller_switches.cc b/chromium/components/dom_distiller/core/dom_distiller_switches.cc
index 70a90f1d0d3..0d3976b6513 100644
--- a/chromium/components/dom_distiller/core/dom_distiller_switches.cc
+++ b/chromium/components/dom_distiller/core/dom_distiller_switches.cc
@@ -14,6 +14,7 @@ const char kReaderModeFeedback[] = "reader-mode-feedback";
namespace reader_mode_heuristics {
const char kAdaBoost[] = "adaboost";
+const char kAllArticles[] = "allarticles";
const char kOGArticle[] = "opengraph";
const char kAlwaysTrue[] = "alwaystrue";
const char kNone[] = "none";
diff --git a/chromium/components/dom_distiller/core/dom_distiller_switches.h b/chromium/components/dom_distiller/core/dom_distiller_switches.h
index 91952d7bedc..8216477e906 100644
--- a/chromium/components/dom_distiller/core/dom_distiller_switches.h
+++ b/chromium/components/dom_distiller/core/dom_distiller_switches.h
@@ -28,6 +28,7 @@ extern const char kReaderModeFeedback[];
namespace reader_mode_heuristics {
extern const char kAdaBoost[];
+extern const char kAllArticles[];
extern const char kOGArticle[];
extern const char kAlwaysTrue[];
extern const char kNone[];
diff --git a/chromium/components/dom_distiller/core/experiments.cc b/chromium/components/dom_distiller/core/experiments.cc
index 900daecd4b5..ba011f508f9 100644
--- a/chromium/components/dom_distiller/core/experiments.cc
+++ b/chromium/components/dom_distiller/core/experiments.cc
@@ -21,6 +21,9 @@ DistillerHeuristicsType GetDistillerHeuristicsType() {
if (switch_value == switches::reader_mode_heuristics::kAdaBoost) {
return DistillerHeuristicsType::ADABOOST_MODEL;
}
+ if (switch_value == switches::reader_mode_heuristics::kAllArticles) {
+ return DistillerHeuristicsType::ALL_ARTICLES;
+ }
if (switch_value == switches::reader_mode_heuristics::kOGArticle) {
return DistillerHeuristicsType::OG_ARTICLE;
}
@@ -36,6 +39,10 @@ DistillerHeuristicsType GetDistillerHeuristicsType() {
base::CompareCase::INSENSITIVE_ASCII)) {
return DistillerHeuristicsType::ADABOOST_MODEL;
}
+ if (base::StartsWith(group_name, "AllArticles",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return DistillerHeuristicsType::ALL_ARTICLES;
+ }
if (base::StartsWith(group_name, "OGArticle",
base::CompareCase::INSENSITIVE_ASCII)) {
return DistillerHeuristicsType::OG_ARTICLE;
diff --git a/chromium/components/dom_distiller/core/experiments.h b/chromium/components/dom_distiller/core/experiments.h
index 16edfcbee00..80d02fa8d51 100644
--- a/chromium/components/dom_distiller/core/experiments.h
+++ b/chromium/components/dom_distiller/core/experiments.h
@@ -6,14 +6,15 @@
#define COMPONENTS_DOM_DISTILLER_CORE_EXPERIMENTS_H_
namespace dom_distiller {
- enum class DistillerHeuristicsType {
- NONE,
- OG_ARTICLE,
- ADABOOST_MODEL,
- ALWAYS_TRUE,
- };
+enum class DistillerHeuristicsType {
+ NONE,
+ OG_ARTICLE,
+ ADABOOST_MODEL,
+ ALL_ARTICLES,
+ ALWAYS_TRUE,
+};
- DistillerHeuristicsType GetDistillerHeuristicsType();
+DistillerHeuristicsType GetDistillerHeuristicsType();
}
#endif // COMPONENTS_DOM_DISTILLER_CORE_EXPERIMENTS_H_
diff --git a/chromium/components/dom_distiller/core/javascript/domdistiller.js b/chromium/components/dom_distiller/core/javascript/domdistiller.js
index 6e9a417a3cf..f1e5c3bb491 100644
--- a/chromium/components/dom_distiller/core/javascript/domdistiller.js
+++ b/chromium/components/dom_distiller/core/javascript/domdistiller.js
@@ -33,5 +33,4 @@
if (e.stack != undefined) window.console.error(e.stack);
}
return undefined;
-})(options = $$OPTIONS,
- stringify_output = $$STRINGIFY)
+})($$OPTIONS, $$STRINGIFY)
diff --git a/chromium/components/dom_distiller/core/url_utils_android.cc b/chromium/components/dom_distiller/core/url_utils_android.cc
index cd07cdc0d13..217734ee096 100644
--- a/chromium/components/dom_distiller/core/url_utils_android.cc
+++ b/chromium/components/dom_distiller/core/url_utils_android.cc
@@ -2,8 +2,6 @@
// 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/url_utils_android.h"
-
#include <string>
#include "base/android/jni_string.h"
@@ -88,8 +86,6 @@ ScopedJavaLocalRef<jstring> GetValueForKeyInUrl(
env, dom_distiller::url_utils::GetValueForKeyInUrl(url, key));
}
-bool RegisterUrlUtils(JNIEnv* env) { return RegisterNativesImpl(env); }
-
} // namespace android
} // namespace url_utils
diff --git a/chromium/components/dom_distiller/core/url_utils_android.h b/chromium/components/dom_distiller/core/url_utils_android.h
deleted file mode 100644
index bedb1a1bad0..00000000000
--- a/chromium/components/dom_distiller/core/url_utils_android.h
+++ /dev/null
@@ -1,27 +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_DOM_DISTILLER_CORE_URL_UTILS_ANDROID_H_
-#define COMPONENTS_DOM_DISTILLER_CORE_URL_UTILS_ANDROID_H_
-
-#include <jni.h>
-
-#include <string>
-
-namespace dom_distiller {
-
-namespace url_utils {
-
-namespace android {
-
-// Register JNI methods
-bool RegisterUrlUtils(JNIEnv* env);
-
-} // namespace android
-
-} // namespace url_utils
-
-} // namespace dom_distiller
-
-#endif // COMPONENTS_DOM_DISTILLER_CORE_URL_UTILS_ANDROID_H_
diff --git a/chromium/components/dom_distiller/ios/BUILD.gn b/chromium/components/dom_distiller/ios/BUILD.gn
index 18c9b24d261..8c796adba20 100644
--- a/chromium/components/dom_distiller/ios/BUILD.gn
+++ b/chromium/components/dom_distiller/ios/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
source_set("ios") {
+ configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"distiller_page_factory_ios.h",
"distiller_page_factory_ios.mm",
diff --git a/chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm b/chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm
index 6aef14f56d8..65fb78f8aab 100644
--- a/chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm
+++ b/chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm
@@ -8,6 +8,10 @@
#include "components/dom_distiller/ios/distiller_page_ios.h"
#include "ios/web/public/browser_state.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace dom_distiller {
DistillerPageFactoryIOS::DistillerPageFactoryIOS(
diff --git a/chromium/components/dom_distiller/ios/distiller_page_ios.mm b/chromium/components/dom_distiller/ios/distiller_page_ios.mm
index 1b19a6fde56..110a09ff695 100644
--- a/chromium/components/dom_distiller/ios/distiller_page_ios.mm
+++ b/chromium/components/dom_distiller/ios/distiller_page_ios.mm
@@ -23,6 +23,10 @@
#import "ios/web/public/web_state/web_state.h"
#include "ios/web/public/web_state/web_state_observer.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace {
// This is duplicated here from ios/web/web_state/ui/web_view_js_utils.mm in
@@ -48,7 +52,7 @@ std::unique_ptr<base::Value> ValueResultFromScriptResult(id wk_result,
return result;
}
- CFTypeID result_type = CFGetTypeID(wk_result);
+ CFTypeID result_type = CFGetTypeID(reinterpret_cast<CFTypeRef>(wk_result));
if (result_type == CFStringGetTypeID()) {
result.reset(new base::Value(base::SysNSStringToUTF16(wk_result)));
DCHECK(result->IsType(base::Value::Type::STRING));
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 2a7c7de6322..91c4f49d9b4 100644
--- a/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html
+++ b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html
@@ -7,7 +7,7 @@ found in the LICENSE file.
<html>
<head>
<meta charset="utf-8">
- <title i18n-content="domDistillerTitle"></title>
+ <title>$i18n{domDistillerTitle}</title>
<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
<link rel="stylesheet" href="chrome://resources/css/overlay.css">
<link rel="stylesheet" href="about_dom_distiller.css">
@@ -26,21 +26,21 @@ found in the LICENSE file.
<div id="mainContent">
<div id="list-section">
<header>
- <h1 id="listTitle" i18n-content="domDistillerTitle"></h1>
+ <h1 id="listTitle">$i18n{domDistillerTitle}</h1>
</header>
<div id="add-entry">
- <label for="article_url" i18n-content="addArticleUrl"></label>
+ <label for="article_url">$i18n{addArticleUrl}</label>
<input type="text" id="article_url" />
<br/>
- <button id="addbutton" i18n-content="addArticleAddButtonLabel"></button>
- <button id="viewbutton" i18n-content="viewUrlButtonLabel"></button>
- <span id="add-entry-error" i18n-content="addArticleFailedLabel"></span>
- <span id="view-url-error" i18n-content="viewUrlFailedLabel"></span>
+ <button id="addbutton">$i18n{addArticleAddButtonLabel}</button>
+ <button id="viewbutton">$i18n{viewUrlButtonLabel}</button>
+ <span id="add-entry-error">$i18n{addArticleFailedLabel}</span>
+ <span id="view-url-error">$i18n{viewUrlFailedLabel}</span>
</div>
<div id="update-list">
<form>
- <button id="refreshbutton" i18n-content="refreshButtonLabel"></button>
- <span id="entries-list-loading" i18n-content="loadingEntries"></span>
+ <button id="refreshbutton">$i18n{refreshButtonLabel}</button>
+ <span id="entries-list-loading">$i18n{loadingEntries}</span>
</form>
</div>
<ul id="entries-list">
diff --git a/chromium/components/domain_reliability/beacon.cc b/chromium/components/domain_reliability/beacon.cc
index 8a8a549f8c2..590a666eaad 100644
--- a/chromium/components/domain_reliability/beacon.cc
+++ b/chromium/components/domain_reliability/beacon.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "components/domain_reliability/util.h"
#include "net/base/net_errors.h"
@@ -25,7 +26,7 @@ std::unique_ptr<Value> DomainReliabilityBeacon::ToValue(
base::TimeTicks last_network_change_time,
const GURL& collector_url,
const std::vector<std::unique_ptr<std::string>>& path_prefixes) const {
- std::unique_ptr<DictionaryValue> beacon_value(new DictionaryValue());
+ auto beacon_value = base::MakeUnique<DictionaryValue>();
DCHECK(url.is_valid());
GURL sanitized_url = SanitizeURLForReport(url, collector_url, path_prefixes);
beacon_value->SetString("url", sanitized_url.spec());
@@ -33,10 +34,10 @@ std::unique_ptr<Value> DomainReliabilityBeacon::ToValue(
if (!quic_error.empty())
beacon_value->SetString("quic_error", quic_error);
if (chrome_error != net::OK) {
- DictionaryValue* failure_value = new DictionaryValue();
+ auto failure_value = base::MakeUnique<DictionaryValue>();
failure_value->SetString("custom_error",
net::ErrorToString(chrome_error));
- beacon_value->Set("failure_data", failure_value);
+ beacon_value->Set("failure_data", std::move(failure_value));
}
beacon_value->SetString("server_ip", server_ip);
beacon_value->SetBoolean("was_proxied", was_proxied);
diff --git a/chromium/components/domain_reliability/context.cc b/chromium/components/domain_reliability/context.cc
index a5c0e197798..826b13cbd28 100644
--- a/chromium/components/domain_reliability/context.cc
+++ b/chromium/components/domain_reliability/context.cc
@@ -231,7 +231,7 @@ std::unique_ptr<const Value> DomainReliabilityContext::CreateReport(
std::unique_ptr<DictionaryValue> report_value(new DictionaryValue());
report_value->SetString("reporter", upload_reporter_string_);
- report_value->Set("entries", beacons_value.release());
+ report_value->Set("entries", std::move(beacons_value));
*max_upload_depth_out = max_upload_depth;
return std::move(report_value);
diff --git a/chromium/components/domain_reliability/monitor.cc b/chromium/components/domain_reliability/monitor.cc
index d5a437fba09..b9b11530a80 100644
--- a/chromium/components/domain_reliability/monitor.cc
+++ b/chromium/components/domain_reliability/monitor.cc
@@ -152,7 +152,7 @@ void DomainReliabilityMonitor::InitURLRequestContext(
// Make sure the URLRequestContext actually lives on what was declared to be
// the network thread.
DCHECK(url_request_context_getter->GetNetworkTaskRunner()->
- RunsTasksOnCurrentThread());
+ RunsTasksInCurrentSequence());
uploader_ = DomainReliabilityUploader::Create(time_.get(),
url_request_context_getter);
diff --git a/chromium/components/doodle/doodle_fetcher_impl_unittest.cc b/chromium/components/doodle/doodle_fetcher_impl_unittest.cc
index 8bdb1fb126c..94f95740da3 100644
--- a/chromium/components/doodle/doodle_fetcher_impl_unittest.cc
+++ b/chromium/components/doodle/doodle_fetcher_impl_unittest.cc
@@ -200,7 +200,6 @@ TEST_F(DoodleFetcherImplTest, ResponseContainsValidBaseInformation) {
"ddljson": {
"alt_text":"Mouseover Text",
"doodle_type":"SIMPLE",
- "interactive_html":"\u003cstyle\u003e\u003c\/style\u003e",
"target_url":"/search?q\u003dtest\u0026sa\u003dX\u0026ved\u003d0ahUKEw",
"time_to_live_ms":55000,
"large_image": {
@@ -218,11 +217,8 @@ TEST_F(DoodleFetcherImplTest, ResponseContainsValidBaseInformation) {
"0ahUKEw")));
EXPECT_THAT(config.doodle_type, Eq(DoodleType::SIMPLE));
EXPECT_THAT(config.alt_text, Eq("Mouseover Text"));
- EXPECT_THAT(config.interactive_html,
- Eq("\u003cstyle\u003e\u003c/style\u003e"));
EXPECT_FALSE(config.large_cta_image.has_value());
- EXPECT_FALSE(config.transparent_large_image.has_value());
EXPECT_THAT(time_to_live, Eq(base::TimeDelta::FromMilliseconds(55000)));
}
@@ -310,24 +306,13 @@ TEST_F(DoodleFetcherImplTest, ResponseContainsExactlyTheSampleImages) {
"time_to_live_ms":55000,
"large_image": {
"height":225,
- "is_animated_gif":true,
- "is_cta":false,
"url":"/logos/doodles/2015/new-years-eve-2015-59854387958251-hp.gif",
"width":489
},
"large_cta_image": {
"height":225,
- "is_animated_gif":true,
- "is_cta":true,
"url":"/logos/doodles/2015/new-years-eve-2015-59854387958251-cta.gif",
"width":489
- },
- "transparent_large_image": {
- "height":225,
- "is_animated_gif":false,
- "is_cta":false,
- "url":"/logos/doodles/2015/new-years-eve-2015-59854387958251-thp.png",
- "width":510
}
}})json");
@@ -339,30 +324,12 @@ TEST_F(DoodleFetcherImplTest, ResponseContainsExactlyTheSampleImages) {
EXPECT_THAT(config.large_image.url,
Eq(Resolve("/logos/doodles/2015/new-years-eve-2015-5985438795"
"8251-hp.gif")));
- EXPECT_THAT(config.large_image.width, Eq(489));
- EXPECT_THAT(config.large_image.height, Eq(225));
- EXPECT_TRUE(config.large_image.is_animated_gif);
- EXPECT_FALSE(config.large_image.is_cta);
-
- ASSERT_TRUE(config.transparent_large_image.has_value());
- EXPECT_TRUE(config.transparent_large_image->url.is_valid());
- EXPECT_THAT(config.transparent_large_image->url,
- Eq(Resolve("/logos/doodles/2015/new-years-eve-2015-5985438795"
- "8251-thp.png")));
- EXPECT_THAT(config.transparent_large_image->width, Eq(510));
- EXPECT_THAT(config.transparent_large_image->height, Eq(225));
- EXPECT_FALSE(config.transparent_large_image->is_animated_gif);
- EXPECT_FALSE(config.transparent_large_image->is_cta);
ASSERT_TRUE(config.large_cta_image.has_value());
EXPECT_TRUE(config.large_cta_image->url.is_valid());
EXPECT_THAT(config.large_cta_image->url,
Eq(Resolve("/logos/doodles/2015/new-years-eve-2015-5985438795"
"8251-cta.gif")));
- EXPECT_THAT(config.large_cta_image->width, Eq(489));
- EXPECT_THAT(config.large_cta_image->height, Eq(225));
- EXPECT_TRUE(config.large_cta_image->is_animated_gif);
- EXPECT_TRUE(config.large_cta_image->is_cta);
}
TEST_F(DoodleFetcherImplTest, RespondsToMultipleRequestsWithSameFetcher) {
diff --git a/chromium/components/doodle/doodle_service_unittest.cc b/chromium/components/doodle/doodle_service_unittest.cc
index 6698df02fd7..9fb046477d7 100644
--- a/chromium/components/doodle/doodle_service_unittest.cc
+++ b/chromium/components/doodle/doodle_service_unittest.cc
@@ -722,9 +722,7 @@ TEST_F(DoodleServiceTest, GetImageFetchesCTAImage) {
service()->Refresh();
DoodleConfig config = CreateConfig(DoodleType::SIMPLE);
// Set a CTA image, which should take precedence over the regular image.
- config.large_image.is_animated_gif = true;
config.large_cta_image = DoodleImage(GURL("https://doodle.com/cta.jpg"));
- config.large_cta_image->is_cta = true;
fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE,
base::TimeDelta::FromHours(1), config);
ASSERT_THAT(service()->config(), Eq(config));
diff --git a/chromium/components/doodle/doodle_types.cc b/chromium/components/doodle/doodle_types.cc
index 3ba743f4149..b438145b483 100644
--- a/chromium/components/doodle/doodle_types.cc
+++ b/chromium/components/doodle/doodle_types.cc
@@ -22,19 +22,13 @@ const char kDoodleTypeSlideshow[] = "SLIDESHOW";
// JSON keys for DoodleImage fields.
const char kKeyUrl[] = "url";
-const char kKeyHeight[] = "height";
-const char kKeyWidth[] = "width";
-const char kKeyIsAnimatedGif[] = "is_animated_gif";
-const char kKeyIsCta[] = "is_cta";
// JSON keys for DoodleConfig fields.
const char kKeyDoodleType[] = "doodle_type";
const char kKeyAltText[] = "alt_text";
-const char kKeyInteractiveHtml[] = "interactive_html";
const char kKeyTargetUrl[] = "target_url";
const char kKeyLargeImage[] = "large_image";
const char kKeyLargeCtaImage[] = "large_cta_image";
-const char kKeyTransparentLargeImage[] = "transparent_large_image";
std::string DoodleTypeToString(DoodleType type) {
switch (type) {
@@ -119,18 +113,10 @@ base::Optional<DoodleImage> DoodleImage::FromDictionary(
return base::nullopt;
}
- DoodleImage image(url);
-
- dict.GetInteger(kKeyHeight, &image.height);
- dict.GetInteger(kKeyWidth, &image.width);
- dict.GetBoolean(kKeyIsAnimatedGif, &image.is_animated_gif);
- dict.GetBoolean(kKeyIsCta, &image.is_cta);
-
- return image;
+ return DoodleImage(url);
}
-DoodleImage::DoodleImage(const GURL& url)
- : url(url), height(0), width(0), is_animated_gif(false), is_cta(false) {
+DoodleImage::DoodleImage(const GURL& url) : url(url) {
DCHECK(url.is_valid());
}
@@ -139,16 +125,11 @@ DoodleImage::~DoodleImage() = default;
std::unique_ptr<base::DictionaryValue> DoodleImage::ToDictionary() const {
auto dict = base::MakeUnique<base::DictionaryValue>();
dict->SetString(kKeyUrl, url.spec());
- dict->SetInteger(kKeyHeight, height);
- dict->SetInteger(kKeyWidth, width);
- dict->SetBoolean(kKeyIsAnimatedGif, is_animated_gif);
- dict->SetBoolean(kKeyIsCta, is_cta);
return dict;
}
bool DoodleImage::operator==(const DoodleImage& other) const {
- return url == other.url && height == other.height && width == other.width &&
- is_animated_gif == other.is_animated_gif && is_cta == other.is_cta;
+ return url == other.url;
}
bool DoodleImage::operator!=(const DoodleImage& other) const {
@@ -180,13 +161,9 @@ base::Optional<DoodleConfig> DoodleConfig::FromDictionary(
dict.GetString(kKeyAltText, &doodle.alt_text);
- dict.GetString(kKeyInteractiveHtml, &doodle.interactive_html);
-
doodle.target_url = ParseUrl(dict, kKeyTargetUrl, base_url);
doodle.large_cta_image = ParseImage(dict, kKeyLargeCtaImage, base_url);
- doodle.transparent_large_image =
- ParseImage(dict, kKeyTransparentLargeImage, base_url);
return doodle;
}
@@ -195,25 +172,18 @@ std::unique_ptr<base::DictionaryValue> DoodleConfig::ToDictionary() const {
auto dict = base::MakeUnique<base::DictionaryValue>();
dict->SetString(kKeyDoodleType, DoodleTypeToString(doodle_type));
dict->SetString(kKeyAltText, alt_text);
- dict->SetString(kKeyInteractiveHtml, interactive_html);
dict->SetString(kKeyTargetUrl, target_url.spec());
dict->Set(kKeyLargeImage, large_image.ToDictionary());
if (large_cta_image.has_value()) {
dict->Set(kKeyLargeCtaImage, large_cta_image->ToDictionary());
}
- if (transparent_large_image.has_value()) {
- dict->Set(kKeyTransparentLargeImage,
- transparent_large_image->ToDictionary());
- }
return dict;
}
bool DoodleConfig::operator==(const DoodleConfig& other) const {
return doodle_type == other.doodle_type && alt_text == other.alt_text &&
- interactive_html == other.interactive_html &&
large_image == other.large_image &&
- large_cta_image == other.large_cta_image &&
- transparent_large_image == other.transparent_large_image;
+ large_cta_image == other.large_cta_image;
}
bool DoodleConfig::operator!=(const DoodleConfig& other) const {
diff --git a/chromium/components/doodle/doodle_types.h b/chromium/components/doodle/doodle_types.h
index 66aa2c533c6..ecc7214923c 100644
--- a/chromium/components/doodle/doodle_types.h
+++ b/chromium/components/doodle/doodle_types.h
@@ -34,7 +34,7 @@ enum class DoodleType {
SLIDESHOW,
};
-// Information about a Doodle image. By default the dimensions are 0.
+// Information about a Doodle image.
struct DoodleImage {
DoodleImage(const GURL& url);
~DoodleImage();
@@ -49,10 +49,6 @@ struct DoodleImage {
bool operator!=(const DoodleImage& other) const;
GURL url;
- int height;
- int width;
- bool is_animated_gif;
- bool is_cta;
// Copying and assignment allowed.
};
@@ -75,13 +71,11 @@ struct DoodleConfig {
DoodleType doodle_type;
std::string alt_text;
- std::string interactive_html;
GURL target_url;
DoodleImage large_image;
base::Optional<DoodleImage> large_cta_image;
- base::Optional<DoodleImage> transparent_large_image;
// Copying and assignment allowed.
};
diff --git a/chromium/components/doodle/doodle_types_unittest.cc b/chromium/components/doodle/doodle_types_unittest.cc
index 91e2f749b4b..5b04c95c8eb 100644
--- a/chromium/components/doodle/doodle_types_unittest.cc
+++ b/chromium/components/doodle/doodle_types_unittest.cc
@@ -44,34 +44,13 @@ base::Optional<DoodleConfig> DoodleConfigFromJson(
} // namespace
-TEST(DoodleImageTest, ParsesMinimalImage) {
+TEST(DoodleImageTest, ParsesImage) {
std::string json = R"json({
"url":"https://www.doodle.com/doodle"
})json";
base::Optional<DoodleImage> image = DoodleImageFromJson(json, base::nullopt);
ASSERT_TRUE(image.has_value());
EXPECT_THAT(image->url, Eq(GURL("https://www.doodle.com/doodle")));
- EXPECT_THAT(image->height, Eq(0));
- EXPECT_THAT(image->width, Eq(0));
- EXPECT_THAT(image->is_animated_gif, Eq(false));
- EXPECT_THAT(image->is_cta, Eq(false));
-}
-
-TEST(DoodleImageTest, ParsesFullImage) {
- std::string json = R"json({
- "url":"https://www.doodle.com/doodle",
- "height":100,
- "width":50,
- "is_animated_gif":true,
- "is_cta":true
- })json";
- base::Optional<DoodleImage> image = DoodleImageFromJson(json, base::nullopt);
- ASSERT_TRUE(image.has_value());
- EXPECT_THAT(image->url, Eq(GURL("https://www.doodle.com/doodle")));
- EXPECT_THAT(image->height, Eq(100));
- EXPECT_THAT(image->width, Eq(50));
- EXPECT_THAT(image->is_animated_gif, Eq(true));
- EXPECT_THAT(image->is_cta, Eq(true));
}
TEST(DoodleImageTest, ResolvesRelativeUrl) {
@@ -97,9 +76,7 @@ TEST(DoodleImageTest, DoesNotResolveAbsoluteUrl) {
TEST(DoodleImageTest, RequiresUrl) {
std::string json = R"json({
"height":100,
- "width":50,
- "is_animated_gif":true,
- "is_cta":true
+ "width":50
})json";
base::Optional<DoodleImage> image = DoodleImageFromJson(json, base::nullopt);
EXPECT_FALSE(image.has_value());
@@ -117,10 +94,6 @@ TEST(DoodleImageTest, HandlesInvalidUrl) {
TEST(DoodleImageTest, PreservesFieldsOverRoundtrip) {
// Set all fields to non-default values.
DoodleImage image(GURL("https://www.doodle.com/doodle"));
- image.height = 100;
- image.width = 50;
- image.is_animated_gif = true;
- image.is_cta = true;
// Convert to a dictionary and back.
base::Optional<DoodleImage> after_roundtrip =
@@ -140,46 +113,36 @@ TEST(DoodleConfigTest, ParsesMinimalConfig) {
ASSERT_TRUE(config.has_value());
EXPECT_THAT(config->doodle_type, Eq(DoodleType::UNKNOWN));
EXPECT_THAT(config->alt_text, Eq(std::string()));
- EXPECT_THAT(config->interactive_html, Eq(std::string()));
EXPECT_THAT(config->target_url, Eq(GURL()));
EXPECT_FALSE(config->large_cta_image.has_value());
- EXPECT_FALSE(config->transparent_large_image.has_value());
}
TEST(DoodleConfigTest, ParsesFullConfig) {
std::string json = R"json({
"doodle_type":"SLIDESHOW",
"alt_text":"some text",
- "interactive_html":"<div id='dood'></div>",
"target_url":"https://doodle.com/target",
"large_image":{"url":"https://doodle.com/img.jpg"},
- "large_cta_image":{"url":"https://doodle.com/cta.jpg"},
- "transparent_large_image":{"url":"https://doodle.com/transparent.jpg"}
+ "large_cta_image":{"url":"https://doodle.com/cta.jpg"}
})json";
base::Optional<DoodleConfig> config =
DoodleConfigFromJson(json, base::nullopt);
ASSERT_TRUE(config.has_value());
EXPECT_THAT(config->doodle_type, Eq(DoodleType::SLIDESHOW));
EXPECT_THAT(config->alt_text, Eq("some text"));
- EXPECT_THAT(config->interactive_html, Eq("<div id='dood'></div>"));
EXPECT_THAT(config->target_url, Eq(GURL("https://doodle.com/target")));
EXPECT_THAT(config->large_image.url, Eq(GURL("https://doodle.com/img.jpg")));
ASSERT_TRUE(config->large_cta_image.has_value());
EXPECT_THAT(config->large_cta_image->url,
Eq(GURL("https://doodle.com/cta.jpg")));
- ASSERT_TRUE(config->transparent_large_image.has_value());
- EXPECT_THAT(config->transparent_large_image->url,
- Eq(GURL("https://doodle.com/transparent.jpg")));
}
TEST(DoodleConfigTest, RequiresLargeImage) {
std::string json = R"json({
"doodle_type":"SLIDESHOW",
"alt_text":"some text",
- "interactive_html":"<div id='dood'></div>",
"target_url":"https://doodle.com/target",
- "large_cta_image":{"url":"https://doodle.com/cta.jpg"},
- "transparent_large_image":{"url":"https://doodle.com/transparent.jpg"}
+ "large_cta_image":{"url":"https://doodle.com/cta.jpg"}
})json";
base::Optional<DoodleConfig> config =
DoodleConfigFromJson(json, base::nullopt);
@@ -190,11 +153,9 @@ TEST(DoodleConfigTest, RequiresValidLargeImage) {
std::string json = R"json({
"doodle_type":"SLIDESHOW",
"alt_text":"some text",
- "interactive_html":"<div id='dood'></div>",
"target_url":"https://doodle.com/target",
"large_image":{"no_url":"asdf"},
- "large_cta_image":{"url":"https://doodle.com/cta.jpg"},
- "transparent_large_image":{"url":"https://doodle.com/transparent.jpg"}
+ "large_cta_image":{"url":"https://doodle.com/cta.jpg"}
})json";
base::Optional<DoodleConfig> config =
DoodleConfigFromJson(json, base::nullopt);
@@ -205,8 +166,7 @@ TEST(DoodleConfigTest, ResolvesRelativeUrls) {
std::string json = R"json({
"target_url":"/target",
"large_image":{"url":"/large.jpg"},
- "large_cta_image":{"url":"/cta.jpg"},
- "transparent_large_image":{"url":"/transparent.jpg"}
+ "large_cta_image":{"url":"/cta.jpg"}
})json";
base::Optional<DoodleConfig> config =
DoodleConfigFromJson(json, GURL("https://doodle.com/"));
@@ -217,9 +177,6 @@ TEST(DoodleConfigTest, ResolvesRelativeUrls) {
ASSERT_TRUE(config->large_cta_image.has_value());
EXPECT_THAT(config->large_cta_image->url,
Eq(GURL("https://doodle.com/cta.jpg")));
- ASSERT_TRUE(config->transparent_large_image.has_value());
- EXPECT_THAT(config->transparent_large_image->url,
- Eq(GURL("https://doodle.com/transparent.jpg")));
}
TEST(DoodleConfigTest, HandlesInvalidUrls) {
@@ -239,11 +196,8 @@ TEST(DoodleConfigTest, PreservesFieldsOverRoundtrip) {
DoodleConfig config(DoodleType::SLIDESHOW,
DoodleImage(GURL("https://www.doodle.com/img.jpg")));
config.alt_text = "some text";
- config.interactive_html = "<div id='dood'></div>";
config.target_url = GURL("https://doodle.com/target");
config.large_cta_image = DoodleImage(GURL("https://www.doodle.com/cta.jpg"));
- config.transparent_large_image =
- DoodleImage(GURL("https://www.doodle.com/transparent.jpg"));
// Convert to a dictionary and back.
// Note: The different |base_url| should make no difference, since all
diff --git a/chromium/components/download/BUILD.gn b/chromium/components/download/BUILD.gn
index 618a7055c8b..441c1be542d 100644
--- a/chromium/components/download/BUILD.gn
+++ b/chromium/components/download/BUILD.gn
@@ -2,25 +2,24 @@
# 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")
-}
+# TODO(dtrainor): Remove this file and have components_unittests depend on
+# //components/download/internal:unit_tests directly.
+group("unit_tests") {
+ testonly = true
-group("download") {
- public_deps = [
- "//components/download/public",
+ deps = [
+ "//components/download/internal:unit_tests",
]
- deps = [
- "//components/download/internal",
+ data_deps = [
+ ":components_unittests_gtest_filter",
]
}
-group("unit_tests") {
+source_set("components_unittests_gtest_filter") {
testonly = true
- deps = [
- "//components/download/internal:unit_tests",
+ data = [
+ "components_unittests.filter",
]
}
diff --git a/chromium/components/download/components_unittests.filter b/chromium/components/download/components_unittests.filter
new file mode 100644
index 00000000000..f694bb67279
--- /dev/null
+++ b/chromium/components/download/components_unittests.filter
@@ -0,0 +1,14 @@
+AllDownloadItemNotifierTest.*
+DeviceStatusListenerTest.*
+DownloadDriverImplTest.*
+DownloadSchedulerImplTest.*
+DownloadServiceClientSetTest.*
+DownloadServiceControllerImplTest.*
+DownloadServiceImplTest.*
+DownloadServiceEntryUtilsTest.*
+DownloadServiceModelImplTest.*
+DownloadStoreTest.*
+FileMonitorTest.*
+NetworkListenerTest.*
+ProtoConversionsTest.*
+ServiceConfigImplTest.* \ No newline at end of file
diff --git a/chromium/components/download/content/BUILD.gn b/chromium/components/download/content/BUILD.gn
index 7ca79f683b1..049294f31a1 100644
--- a/chromium/components/download/content/BUILD.gn
+++ b/chromium/components/download/content/BUILD.gn
@@ -2,41 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-if (is_android) {
- import("//build/config/android/config.gni")
- import("//build/config/android/rules.gni")
-}
-
-source_set("content") {
- sources = [
- "download_driver_impl.cc",
- "download_driver_impl.h",
- ]
-
- public_deps = [
- "//components/download/internal",
- "//components/download/public",
- ]
-
- deps = [
- "//base",
- "//content/public/browser",
- "//net",
- "//url",
- ]
-}
-
-source_set("unit_tests") {
+# TODO(dtrainor): Remove this file and have components_unittests depend on
+# //components/download/content/internal:unit_tests directly.
+group("unit_tests") {
testonly = true
- sources = [
- "download_driver_impl_unittest.cc",
- ]
-
deps = [
- ":content",
- "//content/test:test_support",
- "//testing/gmock",
- "//testing/gtest",
+ "//components/download/content/internal:unit_tests",
+ "//components/download/content/public:unit_tests",
]
}
diff --git a/chromium/components/download/content/DEPS b/chromium/components/download/content/DEPS
index ea159c83af6..b72efab3087 100644
--- a/chromium/components/download/content/DEPS
+++ b/chromium/components/download/content/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/leveldb_proto",
"+content/public/browser",
"+content/public/test",
"+base",
diff --git a/chromium/components/download/content/download_driver_impl.cc b/chromium/components/download/content/download_driver_impl.cc
deleted file mode 100644
index 0d1d2811430..00000000000
--- a/chromium/components/download/content/download_driver_impl.cc
+++ /dev/null
@@ -1,188 +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/download/content/download_driver_impl.h"
-
-#include "components/download/internal/driver_entry.h"
-#include "content/public/browser/download_interrupt_reasons.h"
-#include "content/public/browser/download_url_parameters.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/http/http_response_headers.h"
-
-namespace download {
-
-namespace {
-
-// Converts a content::DownloadItem::DownloadState to DriverEntry::State.
-DriverEntry::State ToDriverEntryState(
- content::DownloadItem::DownloadState state) {
- switch (state) {
- case content::DownloadItem::IN_PROGRESS:
- return DriverEntry::State::IN_PROGRESS;
- case content::DownloadItem::COMPLETE:
- return DriverEntry::State::COMPLETE;
- case content::DownloadItem::CANCELLED:
- return DriverEntry::State::CANCELLED;
- case content::DownloadItem::INTERRUPTED:
- return DriverEntry::State::INTERRUPTED;
- case content::DownloadItem::MAX_DOWNLOAD_STATE:
- return DriverEntry::State::UNKNOWN;
- default:
- NOTREACHED();
- return DriverEntry::State::UNKNOWN;
- }
-}
-
-} // namespace
-
-// static
-DriverEntry DownloadDriverImpl::CreateDriverEntry(
- const content::DownloadItem* item) {
- DCHECK(item);
- DriverEntry entry;
- entry.guid = item->GetGuid();
- entry.state = ToDriverEntryState(item->GetState());
- entry.paused = item->IsPaused();
- entry.bytes_downloaded = item->GetReceivedBytes();
- entry.expected_total_size = item->GetTotalBytes();
- entry.response_headers = item->GetResponseHeaders();
- return entry;
-}
-
-DownloadDriverImpl::DownloadDriverImpl(content::DownloadManager* manager,
- const base::FilePath& dir)
- : download_manager_(manager), file_dir_(dir), client_(nullptr) {
- DCHECK(download_manager_);
-}
-
-DownloadDriverImpl::~DownloadDriverImpl() {
- if (download_manager_)
- download_manager_->RemoveObserver(this);
-}
-
-void DownloadDriverImpl::Initialize(DownloadDriver::Client* client) {
- DCHECK(!client_);
- client_ = client;
- DCHECK(client_);
-
- // |download_manager_| may be shut down. Informs the client.
- if (!download_manager_) {
- client_->OnDriverReady(false);
- return;
- }
-
- download_manager_->AddObserver(this);
- if (download_manager_->IsManagerInitialized())
- client_->OnDriverReady(true);
-}
-
-bool DownloadDriverImpl::IsReady() const {
- return client_ && download_manager_;
-}
-
-void DownloadDriverImpl::Start(const DownloadParams& params) {
- DCHECK(!params.request_params.url.is_empty());
- DCHECK(!params.guid.empty());
- if (!download_manager_)
- return;
-
- content::StoragePartition* storage_partition =
- content::BrowserContext::GetStoragePartitionForSite(
- download_manager_->GetBrowserContext(), params.request_params.url);
- DCHECK(storage_partition);
-
- std::unique_ptr<content::DownloadUrlParameters> download_url_params(
- new content::DownloadUrlParameters(
- params.request_params.url,
- storage_partition->GetURLRequestContext()));
-
- // TODO(xingliu): Handle the request headers from |params|, need to tweak
- // download network stack.
- // Make content::DownloadManager handle potential guid collision and return
- // an error to fail the download cleanly.
- download_url_params->set_guid(params.guid);
- download_url_params->set_transient(true);
- download_url_params->set_method(params.request_params.method);
- download_url_params->set_file_path(file_dir_.AppendASCII(params.guid));
-
- download_manager_->DownloadUrl(std::move(download_url_params));
-}
-
-void DownloadDriverImpl::Cancel(const std::string& guid) {
- if (!download_manager_)
- return;
- content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
- // Cancels the download and removes the persisted records in content layer.
- if (item) {
- item->RemoveObserver(this);
- item->Remove();
- }
-}
-
-void DownloadDriverImpl::Pause(const std::string& guid) {
- if (!download_manager_)
- return;
- content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
- if (item)
- item->Pause();
-}
-
-void DownloadDriverImpl::Resume(const std::string& guid) {
- if (!download_manager_)
- return;
- content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
- if (item)
- item->Resume();
-}
-
-base::Optional<DriverEntry> DownloadDriverImpl::Find(const std::string& guid) {
- if (!download_manager_)
- return base::nullopt;
- content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
- if (item)
- return CreateDriverEntry(item);
- return base::nullopt;
-}
-
-void DownloadDriverImpl::OnDownloadUpdated(content::DownloadItem* item) {
- DCHECK(client_);
-
- using DownloadState = content::DownloadItem::DownloadState;
- DownloadState state = item->GetState();
- content::DownloadInterruptReason reason = item->GetLastReason();
- DriverEntry entry = CreateDriverEntry(item);
-
- if (state == DownloadState::COMPLETE) {
- client_->OnDownloadSucceeded(entry, item->GetTargetFilePath());
- item->RemoveObserver(this);
- } else if (state == DownloadState::IN_PROGRESS) {
- client_->OnDownloadUpdated(entry);
- } else if (reason !=
- content::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE) {
- client_->OnDownloadFailed(entry, static_cast<int>(reason));
- item->RemoveObserver(this);
- }
-}
-
-void DownloadDriverImpl::OnDownloadCreated(content::DownloadManager* manager,
- content::DownloadItem* item) {
- // Listens to all downloads.
- item->AddObserver(this);
- DCHECK(client_);
- DriverEntry entry = CreateDriverEntry(item);
- client_->OnDownloadCreated(entry);
-}
-
-void DownloadDriverImpl::OnManagerInitialized() {
- DCHECK(client_);
- DCHECK(download_manager_);
- client_->OnDriverReady(true);
-}
-
-void DownloadDriverImpl::ManagerGoingDown(content::DownloadManager* manager) {
- DCHECK_EQ(download_manager_, manager);
- download_manager_ = nullptr;
-}
-
-} // namespace download
diff --git a/chromium/components/download/content/download_driver_impl.h b/chromium/components/download/content/download_driver_impl.h
deleted file mode 100644
index c7c5b1ce1b2..00000000000
--- a/chromium/components/download/content/download_driver_impl.h
+++ /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.
-
-#ifndef COMPONENTS_DOWNLOAD_CONTENT_DOWNLOAD_DRIVER_IMPL_H_
-#define COMPONENTS_DOWNLOAD_CONTENT_DOWNLOAD_DRIVER_IMPL_H_
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "components/download/internal/download_driver.h"
-#include "components/download/public/download_params.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/download_manager.h"
-
-namespace download {
-
-struct DriverEntry;
-
-// Aggregates and handles all interaction between download service and content
-// download logic.
-class DownloadDriverImpl : public DownloadDriver,
- public content::DownloadManager::Observer,
- public content::DownloadItem::Observer {
- public:
- // Creates a driver entry based on a download item.
- static DriverEntry CreateDriverEntry(const content::DownloadItem* item);
-
- // Create the driver. All files downloaded will be saved to |dir|.
- DownloadDriverImpl(content::DownloadManager* manager,
- const base::FilePath& dir);
- ~DownloadDriverImpl() override;
-
- // DownloadDriver implementation.
- void Initialize(DownloadDriver::Client* client) override;
- bool IsReady() const override;
- void Start(const DownloadParams& params) override;
- void Cancel(const std::string& guid) override;
- void Pause(const std::string& guid) override;
- void Resume(const std::string& guid) override;
- base::Optional<DriverEntry> Find(const std::string& guid) override;
-
- private:
- // content::DownloadItem::Observer implementation.
- void OnDownloadUpdated(content::DownloadItem* item) override;
-
- // content::DownloadManager::Observer implementation.
- void OnDownloadCreated(content::DownloadManager* manager,
- content::DownloadItem* item) override;
- void OnManagerInitialized() override;
- void ManagerGoingDown(content::DownloadManager* manager) override;
-
- // Low level download handle.
- content::DownloadManager* download_manager_;
-
- // Target directory of download files.
- base::FilePath file_dir_;
-
- // The client that receives updates from low level download logic.
- DownloadDriver::Client* client_;
-
- DISALLOW_COPY_AND_ASSIGN(DownloadDriverImpl);
-};
-
-} // namespace download
-
-#endif // COMPONENTS_DOWNLOAD_CONTENT_DOWNLOAD_DRIVER_IMPL_H_
diff --git a/chromium/components/download/content/download_driver_impl_unittest.cc b/chromium/components/download/content/download_driver_impl_unittest.cc
deleted file mode 100644
index c53b490fac4..00000000000
--- a/chromium/components/download/content/download_driver_impl_unittest.cc
+++ /dev/null
@@ -1,129 +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/download/content/download_driver_impl.h"
-
-#include <memory>
-#include <string>
-
-#include "base/memory/ptr_util.h"
-#include "content/public/test/fake_download_item.h"
-#include "content/public/test/mock_download_manager.h"
-#include "net/http/http_response_headers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::NiceMock;
-using testing::Return;
-
-namespace download {
-
-namespace {
-
-const char kFakeGuid[] = "fake_guid";
-
-// Matcher to compare driver entries. Not all the memeber fields are compared.
-// Currently no comparison in non test code, so no operator== override for
-// driver entry.
-MATCHER_P(DriverEntryEuqual, entry, "") {
- return entry.guid == arg.guid && entry.state == arg.state &&
- entry.bytes_downloaded == arg.bytes_downloaded;
-}
-
-} // namespace
-
-class MockDriverClient : public DownloadDriver::Client {
- public:
- MOCK_METHOD1(OnDriverReady, void(bool));
- MOCK_METHOD1(OnDownloadCreated, void(const DriverEntry&));
- MOCK_METHOD2(OnDownloadFailed, void(const DriverEntry&, int));
- MOCK_METHOD2(OnDownloadSucceeded,
- void(const DriverEntry&, const base::FilePath&));
- MOCK_METHOD1(OnDownloadUpdated, void(const DriverEntry&));
-};
-
-class DownloadDriverImplTest : public testing::Test {
- public:
- DownloadDriverImplTest() = default;
- ~DownloadDriverImplTest() override = default;
-
- void SetUp() override {
- driver_ =
- base::MakeUnique<DownloadDriverImpl>(&mock_manager_, base::FilePath());
- }
-
- // TODO(xingliu): implements test download manager for embedders to test.
- NiceMock<content::MockDownloadManager> mock_manager_;
- MockDriverClient mock_client_;
- std::unique_ptr<DownloadDriverImpl> driver_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DownloadDriverImplTest);
-};
-
-// Ensure the download manager can be initialized after the download driver.
-TEST_F(DownloadDriverImplTest, ManagerLateInitialization) {
- EXPECT_CALL(mock_manager_, IsManagerInitialized())
- .Times(1)
- .WillOnce(Return(false));
- driver_->Initialize(&mock_client_);
-
- EXPECT_CALL(mock_client_, OnDriverReady(true));
- static_cast<content::DownloadManager::Observer*>(driver_.get())
- ->OnManagerInitialized();
-}
-
-// Ensures download updates from download items are propagated correctly.
-TEST_F(DownloadDriverImplTest, DownloadItemUpdateEvents) {
- using DownloadState = content::DownloadItem::DownloadState;
- using DownloadInterruptReason = content::DownloadInterruptReason;
-
- EXPECT_CALL(mock_manager_, IsManagerInitialized())
- .Times(1)
- .WillOnce(Return(true));
- EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(1);
- driver_->Initialize(&mock_client_);
-
- content::FakeDownloadItem fake_item;
- fake_item.SetState(DownloadState::IN_PROGRESS);
- fake_item.SetGuid(kFakeGuid);
- fake_item.SetReceivedBytes(0);
- fake_item.SetTotalBytes(1024);
- DriverEntry entry = DownloadDriverImpl::CreateDriverEntry(&fake_item);
-
- EXPECT_CALL(mock_client_, OnDownloadUpdated(DriverEntryEuqual(entry)))
- .Times(1)
- .RetiresOnSaturation();
- static_cast<content::DownloadItem::Observer*>(driver_.get())
- ->OnDownloadUpdated(&fake_item);
-
- // Nothing happens for cancelled state.
- fake_item.SetState(DownloadState::CANCELLED);
- static_cast<content::DownloadItem::Observer*>(driver_.get())
- ->OnDownloadUpdated(&fake_item);
-
- fake_item.SetReceivedBytes(1024);
- fake_item.SetState(DownloadState::COMPLETE);
- entry = DownloadDriverImpl::CreateDriverEntry(&fake_item);
- EXPECT_CALL(mock_client_, OnDownloadSucceeded(DriverEntryEuqual(entry), _))
- .Times(1)
- .RetiresOnSaturation();
- static_cast<content::DownloadItem::Observer*>(driver_.get())
- ->OnDownloadUpdated(&fake_item);
-
- fake_item.SetState(DownloadState::INTERRUPTED);
- fake_item.SetLastReason(
- DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT);
- entry = DownloadDriverImpl::CreateDriverEntry(&fake_item);
- int reason = static_cast<int>(
- DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT);
- EXPECT_CALL(mock_client_, OnDownloadFailed(DriverEntryEuqual(entry), reason))
- .Times(1)
- .RetiresOnSaturation();
- static_cast<content::DownloadItem::Observer*>(driver_.get())
- ->OnDownloadUpdated(&fake_item);
-}
-
-} // namespace download
diff --git a/chromium/components/download/content/factory/BUILD.gn b/chromium/components/download/content/factory/BUILD.gn
new file mode 100644
index 00000000000..46f834f0296
--- /dev/null
+++ b/chromium/components/download/content/factory/BUILD.gn
@@ -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.
+
+source_set("factory") {
+ sources = [
+ "download_service_factory.cc",
+ "download_service_factory.h",
+ ]
+
+ public_deps = [
+ "//components/download/public",
+ ]
+
+ deps = [
+ "//base",
+ "//components/download/content/internal",
+ "//components/download/internal",
+ "//components/download/internal/proto",
+ "//components/leveldb_proto",
+ "//content/public/browser",
+ "//net",
+ "//url",
+ ]
+}
diff --git a/chromium/components/download/content/factory/download_service_factory.cc b/chromium/components/download/content/factory/download_service_factory.cc
new file mode 100644
index 00000000000..95fbf6ade1d
--- /dev/null
+++ b/chromium/components/download/content/factory/download_service_factory.cc
@@ -0,0 +1,58 @@
+// 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 "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
+#include "components/download/content/internal/download_driver_impl.h"
+#include "components/download/internal/client_set.h"
+#include "components/download/internal/config.h"
+#include "components/download/internal/controller_impl.h"
+#include "components/download/internal/download_service_impl.h"
+#include "components/download/internal/download_store.h"
+#include "components/download/internal/file_monitor_impl.h"
+#include "components/download/internal/model_impl.h"
+#include "components/download/internal/proto/entry.pb.h"
+#include "components/download/internal/scheduler/scheduler_impl.h"
+#include "components/leveldb_proto/proto_database_impl.h"
+
+namespace download {
+namespace {
+const base::FilePath::CharType kEntryDBStorageDir[] =
+ FILE_PATH_LITERAL("EntryDB");
+const base::FilePath::CharType kFilesStorageDir[] = FILE_PATH_LITERAL("Files");
+} // namespace
+
+DownloadService* CreateDownloadService(
+ std::unique_ptr<DownloadClientMap> clients,
+ content::DownloadManager* download_manager,
+ const base::FilePath& storage_dir,
+ const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
+ std::unique_ptr<TaskScheduler> task_scheduler) {
+ auto client_set = base::MakeUnique<ClientSet>(std::move(clients));
+ auto config = Configuration::CreateFromFinch();
+
+ auto files_storage_dir = storage_dir.Append(kFilesStorageDir);
+ auto driver = base::MakeUnique<DownloadDriverImpl>(download_manager);
+
+ auto entry_db_storage_dir = storage_dir.Append(kEntryDBStorageDir);
+ auto entry_db =
+ base::MakeUnique<leveldb_proto::ProtoDatabaseImpl<protodb::Entry>>(
+ background_task_runner);
+ auto store = base::MakeUnique<DownloadStore>(entry_db_storage_dir,
+ std::move(entry_db));
+ auto model = base::MakeUnique<ModelImpl>(std::move(store));
+ auto device_status_listener =
+ base::MakeUnique<DeviceStatusListener>(config->network_change_delay);
+ auto scheduler = base::MakeUnique<SchedulerImpl>(
+ task_scheduler.get(), config.get(), client_set.get());
+ auto file_monitor = base::MakeUnique<FileMonitorImpl>(
+ files_storage_dir, background_task_runner, config->file_keep_alive_time);
+ auto controller = base::MakeUnique<ControllerImpl>(
+ config.get(), std::move(client_set), std::move(driver), std::move(model),
+ std::move(device_status_listener), std::move(scheduler),
+ std::move(task_scheduler), std::move(file_monitor), files_storage_dir);
+ return new DownloadServiceImpl(std::move(config), std::move(controller));
+}
+
+} // namespace download
diff --git a/chromium/components/download/content/factory/download_service_factory.h b/chromium/components/download/content/factory/download_service_factory.h
new file mode 100644
index 00000000000..8df6dfe7da5
--- /dev/null
+++ b/chromium/components/download/content/factory/download_service_factory.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_H_
+#define COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "components/download/public/clients.h"
+
+namespace content {
+class DownloadManager;
+} // namespace content
+
+namespace download {
+
+class DownloadService;
+class TaskScheduler;
+
+// |clients| is a map of DownloadClient -> std::unique_ptr<Client>. This
+// represents all of the clients that are allowed to have requests made on
+// their behalf. This cannot be changed after startup. Any existing requests
+// no longer associated with a client will be cancelled.
+// |storage_dir| is a path to where all the local storage will be. This will
+// hold the internal database as well as any temporary files on disk. If this
+// is an empty path, the service will not persist any information to disk and
+// will act as an in-memory only service (this means no auto-retries after
+// restarts, no files written on completion, etc.).
+// |background_task_runner| will be used for all disk reads and writes.
+DownloadService* CreateDownloadService(
+ std::unique_ptr<DownloadClientMap> clients,
+ content::DownloadManager* download_manager,
+ const base::FilePath& storage_dir,
+ const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
+ std::unique_ptr<TaskScheduler> task_scheduler);
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_H_
diff --git a/chromium/components/download/content/internal/BUILD.gn b/chromium/components/download/content/internal/BUILD.gn
new file mode 100644
index 00000000000..77025b91da8
--- /dev/null
+++ b/chromium/components/download/content/internal/BUILD.gn
@@ -0,0 +1,45 @@
+# 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.
+
+source_set("internal") {
+ visibility = [
+ ":*",
+ "//components/download/content/factory",
+ ]
+
+ sources = [
+ "download_driver_impl.cc",
+ "download_driver_impl.h",
+ ]
+
+ public_deps = [
+ "//components/download/content/public",
+ "//components/download/internal",
+ "//components/download/public",
+ ]
+
+ deps = [
+ "//base",
+ "//content/public/browser",
+ "//net",
+ "//url",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "download_driver_impl_unittest.cc",
+ ]
+
+ deps = [
+ ":internal",
+ "//base/test:test_support",
+ "//components/download/content/public",
+ "//content/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/download/content/internal/download_driver_impl.cc b/chromium/components/download/content/internal/download_driver_impl.cc
new file mode 100644
index 00000000000..99686e18454
--- /dev/null
+++ b/chromium/components/download/content/internal/download_driver_impl.cc
@@ -0,0 +1,279 @@
+// 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/download/content/internal/download_driver_impl.h"
+
+#include <set>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/internal/driver_entry.h"
+#include "content/public/browser/download_interrupt_reasons.h"
+#include "content/public/browser/download_url_parameters.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/http/http_response_headers.h"
+
+namespace download {
+
+namespace {
+
+// Converts a content::DownloadItem::DownloadState to DriverEntry::State.
+DriverEntry::State ToDriverEntryState(
+ content::DownloadItem::DownloadState state) {
+ switch (state) {
+ case content::DownloadItem::IN_PROGRESS:
+ return DriverEntry::State::IN_PROGRESS;
+ case content::DownloadItem::COMPLETE:
+ return DriverEntry::State::COMPLETE;
+ case content::DownloadItem::CANCELLED:
+ return DriverEntry::State::CANCELLED;
+ case content::DownloadItem::INTERRUPTED:
+ return DriverEntry::State::INTERRUPTED;
+ case content::DownloadItem::MAX_DOWNLOAD_STATE:
+ return DriverEntry::State::UNKNOWN;
+ default:
+ NOTREACHED();
+ return DriverEntry::State::UNKNOWN;
+ }
+}
+
+FailureType FailureTypeFromInterruptReason(
+ content::DownloadInterruptReason reason) {
+ switch (reason) {
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED:
+ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED:
+ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM:
+ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN:
+ return FailureType::NOT_RECOVERABLE;
+ default:
+ return FailureType::RECOVERABLE;
+ }
+}
+
+// Logs interrupt reason when download fails.
+void LogDownloadInterruptReason(content::DownloadInterruptReason reason) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Download.Service.Driver.InterruptReason",
+ reason);
+}
+
+} // namespace
+
+// static
+DriverEntry DownloadDriverImpl::CreateDriverEntry(
+ const content::DownloadItem* item) {
+ DCHECK(item);
+ DriverEntry entry;
+ entry.guid = item->GetGuid();
+ entry.state = ToDriverEntryState(item->GetState());
+ entry.paused = item->IsPaused();
+ entry.bytes_downloaded = item->GetReceivedBytes();
+ entry.expected_total_size = item->GetTotalBytes();
+ entry.current_file_path =
+ item->GetState() == content::DownloadItem::DownloadState::COMPLETE
+ ? item->GetTargetFilePath()
+ : item->GetFullPath();
+ entry.completion_time = item->GetEndTime();
+ entry.response_headers = item->GetResponseHeaders();
+ entry.url_chain = item->GetUrlChain();
+ return entry;
+}
+
+DownloadDriverImpl::DownloadDriverImpl(content::DownloadManager* manager)
+ : download_manager_(manager), client_(nullptr), weak_ptr_factory_(this) {
+ DCHECK(download_manager_);
+}
+
+DownloadDriverImpl::~DownloadDriverImpl() = default;
+
+void DownloadDriverImpl::Initialize(DownloadDriver::Client* client) {
+ DCHECK(!client_);
+ client_ = client;
+ DCHECK(client_);
+
+ // |download_manager_| may be shut down. Informs the client.
+ if (!download_manager_) {
+ client_->OnDriverReady(false);
+ return;
+ }
+
+ notifier_ =
+ base::MakeUnique<AllDownloadItemNotifier>(download_manager_, this);
+}
+
+void DownloadDriverImpl::HardRecover() {
+ // TODO(dtrainor, xingliu): Implement recovery for the DownloadManager.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&DownloadDriverImpl::OnHardRecoverComplete,
+ weak_ptr_factory_.GetWeakPtr(), true));
+}
+
+bool DownloadDriverImpl::IsReady() const {
+ return client_ && download_manager_ &&
+ download_manager_->IsManagerInitialized();
+}
+
+void DownloadDriverImpl::Start(
+ const RequestParams& request_params,
+ const std::string& guid,
+ const base::FilePath& file_path,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation) {
+ DCHECK(!request_params.url.is_empty());
+ DCHECK(!guid.empty());
+ if (!download_manager_)
+ return;
+
+ content::StoragePartition* storage_partition =
+ content::BrowserContext::GetStoragePartitionForSite(
+ download_manager_->GetBrowserContext(), request_params.url);
+ DCHECK(storage_partition);
+
+ std::unique_ptr<content::DownloadUrlParameters> download_url_params(
+ new content::DownloadUrlParameters(
+ request_params.url, storage_partition->GetURLRequestContext(),
+ traffic_annotation));
+
+ // TODO(xingliu): Make content::DownloadManager handle potential guid
+ // collision and return an error to fail the download cleanly.
+ for (net::HttpRequestHeaders::Iterator it(request_params.request_headers);
+ it.GetNext();) {
+ download_url_params->add_request_header(it.name(), it.value());
+ }
+ download_url_params->set_guid(guid);
+ download_url_params->set_transient(true);
+ download_url_params->set_method(request_params.method);
+ download_url_params->set_file_path(file_path);
+
+ download_manager_->DownloadUrl(std::move(download_url_params));
+}
+
+void DownloadDriverImpl::Remove(const std::string& guid) {
+ guid_to_remove_.emplace(guid);
+
+ // DownloadItem::Remove will cause the item object removed from memory, post
+ // the remove task to avoid the object being accessed in the same call stack.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&DownloadDriverImpl::DoRemoveDownload,
+ weak_ptr_factory_.GetWeakPtr(), guid));
+}
+
+void DownloadDriverImpl::DoRemoveDownload(const std::string& guid) {
+ if (!download_manager_)
+ return;
+ content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
+ // Cancels the download and removes the persisted records in content layer.
+ if (item) {
+ item->Remove();
+ }
+}
+
+void DownloadDriverImpl::Pause(const std::string& guid) {
+ if (!download_manager_)
+ return;
+ content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
+ if (item)
+ item->Pause();
+}
+
+void DownloadDriverImpl::Resume(const std::string& guid) {
+ if (!download_manager_)
+ return;
+ content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
+ if (item)
+ item->Resume();
+}
+
+base::Optional<DriverEntry> DownloadDriverImpl::Find(const std::string& guid) {
+ if (!download_manager_)
+ return base::nullopt;
+ content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
+ if (item)
+ return CreateDriverEntry(item);
+ return base::nullopt;
+}
+
+std::set<std::string> DownloadDriverImpl::GetActiveDownloads() {
+ std::set<std::string> guids;
+ if (!download_manager_)
+ return guids;
+
+ std::vector<content::DownloadItem*> items;
+ download_manager_->GetAllDownloads(&items);
+
+ for (auto* item : items) {
+ DriverEntry::State state = ToDriverEntryState(item->GetState());
+ if (state == DriverEntry::State::IN_PROGRESS)
+ guids.insert(item->GetGuid());
+ }
+
+ return guids;
+}
+
+void DownloadDriverImpl::OnDownloadUpdated(content::DownloadManager* manager,
+ content::DownloadItem* item) {
+ DCHECK(client_);
+ // Blocks the observer call if we asked to remove the download.
+ if (guid_to_remove_.find(item->GetGuid()) != guid_to_remove_.end())
+ return;
+
+ using DownloadState = content::DownloadItem::DownloadState;
+ DownloadState state = item->GetState();
+ content::DownloadInterruptReason reason = item->GetLastReason();
+ DriverEntry entry = CreateDriverEntry(item);
+
+ if (state == DownloadState::COMPLETE) {
+ client_->OnDownloadSucceeded(entry);
+ } else if (state == DownloadState::IN_PROGRESS) {
+ client_->OnDownloadUpdated(entry);
+ } else if (reason !=
+ content::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE) {
+ LogDownloadInterruptReason(reason);
+ client_->OnDownloadFailed(entry, FailureTypeFromInterruptReason(reason));
+ }
+}
+
+void DownloadDriverImpl::OnDownloadRemoved(content::DownloadManager* manager,
+ content::DownloadItem* download) {
+ guid_to_remove_.erase(download->GetGuid());
+ // |download| is about to be deleted.
+}
+
+void DownloadDriverImpl::OnDownloadCreated(content::DownloadManager* manager,
+ content::DownloadItem* item) {
+ // Listens to all downloads.
+ DCHECK(client_);
+ DriverEntry entry = CreateDriverEntry(item);
+
+ // Only notifies the client about new downloads. Existing download data will
+ // be loaded before the driver is ready.
+ if (IsReady())
+ client_->OnDownloadCreated(entry);
+}
+
+void DownloadDriverImpl::OnManagerInitialized(
+ content::DownloadManager* manager) {
+ DCHECK_EQ(download_manager_, manager);
+ DCHECK(client_);
+ DCHECK(download_manager_);
+ client_->OnDriverReady(true);
+}
+
+void DownloadDriverImpl::OnManagerGoingDown(content::DownloadManager* manager) {
+ DCHECK_EQ(download_manager_, manager);
+ notifier_.reset();
+ download_manager_ = nullptr;
+}
+
+void DownloadDriverImpl::OnHardRecoverComplete(bool success) {
+ client_->OnDriverHardRecoverComplete(success);
+}
+
+} // namespace download
diff --git a/chromium/components/download/content/internal/download_driver_impl.h b/chromium/components/download/content/internal/download_driver_impl.h
new file mode 100644
index 00000000000..e634862041f
--- /dev/null
+++ b/chromium/components/download/content/internal/download_driver_impl.h
@@ -0,0 +1,88 @@
+// 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_CONTENT_INTERNAL_DOWNLOAD_DRIVER_IMPL_H_
+#define COMPONENTS_DOWNLOAD_CONTENT_INTERNAL_DOWNLOAD_DRIVER_IMPL_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/content/public/all_download_item_notifier.h"
+#include "components/download/internal/download_driver.h"
+#include "components/download/public/download_params.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/download_manager.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+
+namespace download {
+
+struct DriverEntry;
+
+// Aggregates and handles all interaction between download service and content
+// download logic.
+class DownloadDriverImpl : public DownloadDriver,
+ public AllDownloadItemNotifier::Observer {
+ public:
+ // Creates a driver entry based on a download item.
+ static DriverEntry CreateDriverEntry(const content::DownloadItem* item);
+
+ // Create the driver.
+ DownloadDriverImpl(content::DownloadManager* manager);
+ ~DownloadDriverImpl() override;
+
+ // DownloadDriver implementation.
+ void Initialize(DownloadDriver::Client* client) override;
+ void HardRecover() override;
+ bool IsReady() const override;
+ void Start(
+ const RequestParams& request_params,
+ const std::string& guid,
+ const base::FilePath& file_path,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
+ void Remove(const std::string& guid) override;
+ void Pause(const std::string& guid) override;
+ void Resume(const std::string& guid) override;
+ base::Optional<DriverEntry> Find(const std::string& guid) override;
+ std::set<std::string> GetActiveDownloads() override;
+
+ private:
+ // content::AllDownloadItemNotifier::Observer implementation.
+ void OnManagerInitialized(content::DownloadManager* manager) override;
+ void OnManagerGoingDown(content::DownloadManager* manager) override;
+ void OnDownloadCreated(content::DownloadManager* manager,
+ content::DownloadItem* item) override;
+ void OnDownloadUpdated(content::DownloadManager* manager,
+ content::DownloadItem* item) override;
+ void OnDownloadRemoved(content::DownloadManager* manager,
+ content::DownloadItem* item) override;
+
+ void OnHardRecoverComplete(bool success);
+
+ // Remove the download, used to be posted to the task queue.
+ void DoRemoveDownload(const std::string& guid);
+
+ // Low level download handle.
+ content::DownloadManager* download_manager_;
+
+ // The client that receives updates from low level download logic.
+ DownloadDriver::Client* client_;
+
+ // Built lazily on initialize and destroyed when/if the manager is torn down.
+ std::unique_ptr<AllDownloadItemNotifier> notifier_;
+
+ // Pending guid set of downloads that will be removed soon.
+ std::set<std::string> guid_to_remove_;
+
+ // Only used to post tasks on the same thread.
+ base::WeakPtrFactory<DownloadDriverImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadDriverImpl);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_CONTENT_INTERNAL_DOWNLOAD_DRIVER_IMPL_H_
diff --git a/chromium/components/download/content/internal/download_driver_impl_unittest.cc b/chromium/components/download/content/internal/download_driver_impl_unittest.cc
new file mode 100644
index 00000000000..f4937e02d99
--- /dev/null
+++ b/chromium/components/download/content/internal/download_driver_impl_unittest.cc
@@ -0,0 +1,189 @@
+// 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/download/content/internal/download_driver_impl.h"
+
+#include <memory>
+#include <string>
+
+#include "base/guid.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/content/public/all_download_item_notifier.h"
+#include "content/public/test/fake_download_item.h"
+#include "content/public/test/mock_download_manager.h"
+#include "net/http/http_response_headers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+
+namespace download {
+
+namespace {
+
+ACTION_P(PopulateVector, items) {
+ arg0->insert(arg0->begin(), items.begin(), items.end());
+}
+
+const char kFakeGuid[] = "fake_guid";
+
+// Matcher to compare driver entries. Not all the memeber fields are compared.
+// Currently no comparison in non test code, so no operator== override for
+// driver entry.
+MATCHER_P(DriverEntryEqual, entry, "") {
+ return entry.guid == arg.guid && entry.state == arg.state &&
+ entry.bytes_downloaded == arg.bytes_downloaded &&
+ entry.current_file_path.value() == arg.current_file_path.value();
+}
+
+} // namespace
+
+class MockDriverClient : public DownloadDriver::Client {
+ public:
+ MOCK_METHOD1(OnDriverReady, void(bool));
+ MOCK_METHOD1(OnDriverHardRecoverComplete, void(bool));
+ MOCK_METHOD1(OnDownloadCreated, void(const DriverEntry&));
+ MOCK_METHOD2(OnDownloadFailed, void(const DriverEntry&, FailureType));
+ MOCK_METHOD1(OnDownloadSucceeded, void(const DriverEntry&));
+ MOCK_METHOD1(OnDownloadUpdated, void(const DriverEntry&));
+};
+
+class DownloadDriverImplTest : public testing::Test {
+ public:
+ DownloadDriverImplTest()
+ : task_runner_(new base::TestSimpleTaskRunner), handle_(task_runner_) {}
+
+ ~DownloadDriverImplTest() override = default;
+
+ void SetUp() override {
+ driver_ = base::MakeUnique<DownloadDriverImpl>(&mock_manager_);
+ }
+
+ // TODO(xingliu): implements test download manager for embedders to test.
+ NiceMock<content::MockDownloadManager> mock_manager_;
+ MockDriverClient mock_client_;
+ std::unique_ptr<DownloadDriverImpl> driver_;
+
+ protected:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle handle_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DownloadDriverImplTest);
+};
+
+// Ensure the download manager can be initialized after the download driver.
+TEST_F(DownloadDriverImplTest, ManagerLateInitialization) {
+ EXPECT_CALL(mock_manager_, IsManagerInitialized())
+ .Times(1)
+ .WillOnce(Return(false));
+ driver_->Initialize(&mock_client_);
+
+ EXPECT_CALL(mock_client_, OnDriverReady(true));
+ static_cast<AllDownloadItemNotifier::Observer*>(driver_.get())
+ ->OnManagerInitialized(&mock_manager_);
+}
+
+TEST_F(DownloadDriverImplTest, TestHardRecover) {
+ EXPECT_CALL(mock_manager_, IsManagerInitialized())
+ .Times(1)
+ .WillOnce(Return(false));
+ driver_->Initialize(&mock_client_);
+
+ EXPECT_CALL(mock_client_, OnDriverHardRecoverComplete(true)).Times(1);
+ driver_->HardRecover();
+ task_runner_->RunUntilIdle();
+}
+
+// Ensures download updates from download items are propagated correctly.
+TEST_F(DownloadDriverImplTest, DownloadItemUpdateEvents) {
+ using DownloadState = content::DownloadItem::DownloadState;
+ using DownloadInterruptReason = content::DownloadInterruptReason;
+
+ EXPECT_CALL(mock_manager_, IsManagerInitialized())
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(1);
+ driver_->Initialize(&mock_client_);
+
+ content::FakeDownloadItem fake_item;
+ fake_item.SetState(DownloadState::IN_PROGRESS);
+ fake_item.SetGuid(kFakeGuid);
+ fake_item.SetReceivedBytes(0);
+ fake_item.SetTotalBytes(1024);
+ DriverEntry entry = DownloadDriverImpl::CreateDriverEntry(&fake_item);
+
+ EXPECT_CALL(mock_client_, OnDownloadUpdated(DriverEntryEqual(entry)))
+ .Times(1)
+ .RetiresOnSaturation();
+ static_cast<AllDownloadItemNotifier::Observer*>(driver_.get())
+ ->OnDownloadUpdated(&mock_manager_, &fake_item);
+
+ // Nothing happens for cancelled state.
+ fake_item.SetState(DownloadState::CANCELLED);
+ static_cast<AllDownloadItemNotifier::Observer*>(driver_.get())
+ ->OnDownloadUpdated(&mock_manager_, &fake_item);
+
+ fake_item.SetReceivedBytes(1024);
+ fake_item.SetState(DownloadState::COMPLETE);
+ entry = DownloadDriverImpl::CreateDriverEntry(&fake_item);
+ EXPECT_CALL(mock_client_, OnDownloadSucceeded(DriverEntryEqual(entry)))
+ .Times(1)
+ .RetiresOnSaturation();
+ static_cast<AllDownloadItemNotifier::Observer*>(driver_.get())
+ ->OnDownloadUpdated(&mock_manager_, &fake_item);
+
+ fake_item.SetState(DownloadState::INTERRUPTED);
+ fake_item.SetLastReason(
+ DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT);
+ entry = DownloadDriverImpl::CreateDriverEntry(&fake_item);
+ EXPECT_CALL(mock_client_, OnDownloadFailed(DriverEntryEqual(entry),
+ FailureType::RECOVERABLE))
+ .Times(1)
+ .RetiresOnSaturation();
+ static_cast<AllDownloadItemNotifier::Observer*>(driver_.get())
+ ->OnDownloadUpdated(&mock_manager_, &fake_item);
+}
+
+TEST_F(DownloadDriverImplTest, TestGetActiveDownloadsCall) {
+ using DownloadState = content::DownloadItem::DownloadState;
+ content::FakeDownloadItem item1;
+ item1.SetState(DownloadState::IN_PROGRESS);
+ item1.SetGuid(base::GenerateGUID());
+
+ content::FakeDownloadItem item2;
+ item2.SetState(DownloadState::CANCELLED);
+ item2.SetGuid(base::GenerateGUID());
+
+ content::FakeDownloadItem item3;
+ item3.SetState(DownloadState::COMPLETE);
+ item3.SetGuid(base::GenerateGUID());
+
+ content::FakeDownloadItem item4;
+ item4.SetState(DownloadState::INTERRUPTED);
+ item4.SetGuid(base::GenerateGUID());
+
+ std::vector<content::DownloadItem*> items{&item1, &item2, &item3, &item4};
+
+ ON_CALL(mock_manager_, GetAllDownloads(_))
+ .WillByDefault(PopulateVector(items));
+
+ EXPECT_CALL(mock_manager_, IsManagerInitialized())
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_client_, OnDriverReady(true)).Times(1);
+ driver_->Initialize(&mock_client_);
+
+ auto guids = driver_->GetActiveDownloads();
+
+ EXPECT_EQ(1U, guids.size());
+ EXPECT_NE(guids.end(), guids.find(item1.GetGuid()));
+}
+
+} // namespace download
diff --git a/chromium/components/download/content/public/BUILD.gn b/chromium/components/download/content/public/BUILD.gn
new file mode 100644
index 00000000000..4cb80da314b
--- /dev/null
+++ b/chromium/components/download/content/public/BUILD.gn
@@ -0,0 +1,38 @@
+# 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("public") {
+ sources = [
+ "all_download_item_notifier.cc",
+ "all_download_item_notifier.h",
+ ]
+
+ public_deps = [
+ "//base",
+ ]
+
+ deps = [
+ "//content/public/browser",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "all_download_item_notifier_unittest.cc",
+ ]
+
+ deps = [
+ ":public",
+ "//content/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/download/content/public/all_download_item_notifier.cc b/chromium/components/download/content/public/all_download_item_notifier.cc
new file mode 100644
index 00000000000..81159f57063
--- /dev/null
+++ b/chromium/components/download/content/public/all_download_item_notifier.cc
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/download/content/public/all_download_item_notifier.h"
+
+namespace download {
+
+AllDownloadItemNotifier::AllDownloadItemNotifier(
+ content::DownloadManager* manager,
+ AllDownloadItemNotifier::Observer* observer)
+ : manager_(manager), observer_(observer) {
+ DCHECK(observer_);
+ manager_->AddObserver(this);
+ content::DownloadManager::DownloadVector items;
+ manager_->GetAllDownloads(&items);
+ for (content::DownloadManager::DownloadVector::const_iterator it =
+ items.begin();
+ it != items.end(); ++it) {
+ (*it)->AddObserver(this);
+ observing_.insert(*it);
+ }
+
+ if (manager_->IsManagerInitialized())
+ observer_->OnManagerInitialized(manager_);
+}
+
+AllDownloadItemNotifier::~AllDownloadItemNotifier() {
+ if (manager_)
+ manager_->RemoveObserver(this);
+ for (std::set<content::DownloadItem*>::const_iterator it = observing_.begin();
+ it != observing_.end(); ++it) {
+ (*it)->RemoveObserver(this);
+ }
+ observing_.clear();
+}
+
+void AllDownloadItemNotifier::OnManagerInitialized() {
+ observer_->OnManagerInitialized(manager_);
+}
+
+void AllDownloadItemNotifier::ManagerGoingDown(
+ content::DownloadManager* manager) {
+ DCHECK_EQ(manager_, manager);
+ observer_->OnManagerGoingDown(manager);
+ manager_->RemoveObserver(this);
+ manager_ = NULL;
+}
+
+void AllDownloadItemNotifier::OnDownloadCreated(
+ content::DownloadManager* manager,
+ content::DownloadItem* item) {
+ item->AddObserver(this);
+ observing_.insert(item);
+ observer_->OnDownloadCreated(manager, item);
+}
+
+void AllDownloadItemNotifier::OnDownloadUpdated(content::DownloadItem* item) {
+ observer_->OnDownloadUpdated(manager_, item);
+}
+
+void AllDownloadItemNotifier::OnDownloadOpened(content::DownloadItem* item) {
+ observer_->OnDownloadOpened(manager_, item);
+}
+
+void AllDownloadItemNotifier::OnDownloadRemoved(content::DownloadItem* item) {
+ observer_->OnDownloadRemoved(manager_, item);
+}
+
+void AllDownloadItemNotifier::OnDownloadDestroyed(content::DownloadItem* item) {
+ item->RemoveObserver(this);
+ observing_.erase(item);
+}
+
+} // namespace download
diff --git a/chromium/components/download/content/public/all_download_item_notifier.h b/chromium/components/download/content/public/all_download_item_notifier.h
new file mode 100644
index 00000000000..d52cc92543c
--- /dev/null
+++ b/chromium/components/download/content/public/all_download_item_notifier.h
@@ -0,0 +1,94 @@
+// 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_CONTENT_PUBLIC_ALL_DOWNLOAD_ITEM_NOTIFIER_H_
+#define COMPONENTS_DOWNLOAD_CONTENT_PUBLIC_ALL_DOWNLOAD_ITEM_NOTIFIER_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "content/public/browser/download_item.h"
+#include "content/public/browser/download_manager.h"
+
+// AllDownloadItemNotifier observes ALL the DownloadItems on a given
+// DownloadManager.
+// Clients should use GetManager() instead of storing their own pointer to the
+// manager so that they can be sensitive to managers that have gone down.
+
+// Example Usage:
+// class DownloadSystemConsumer : public AllDownloadItemNotifier::Observer {
+// public:
+// DownloadSystemConsumer(DownloadManager* original_manager,
+// DownloadManager* incognito_manager)
+// : original_notifier_(original_manager, this),
+// incognito_notifier_(incognito_manager, this) {
+// }
+//
+// virtual void OnDownloadUpdated(
+// DownloadManager* manager, DownloadItem* item) { ... }
+//
+// private:
+// AllDownloadItemNotifier original_notifier_;
+// AllDownloadItemNotifier incognito_notifier_;
+// };
+
+namespace download {
+
+class AllDownloadItemNotifier : public content::DownloadManager::Observer,
+ public content::DownloadItem::Observer {
+ public:
+ // All of the methods take the DownloadManager so that subclasses can observe
+ // multiple managers at once and easily distinguish which manager a given item
+ // belongs to.
+ class Observer {
+ public:
+ Observer() {}
+ virtual ~Observer() {}
+
+ virtual void OnManagerInitialized(content::DownloadManager* manager) {}
+ virtual void OnManagerGoingDown(content::DownloadManager* manager) {}
+ virtual void OnDownloadCreated(content::DownloadManager* manager,
+ content::DownloadItem* item) {}
+ virtual void OnDownloadUpdated(content::DownloadManager* manager,
+ content::DownloadItem* item) {}
+ virtual void OnDownloadOpened(content::DownloadManager* manager,
+ content::DownloadItem* item) {}
+ virtual void OnDownloadRemoved(content::DownloadManager* manager,
+ content::DownloadItem* item) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Observer);
+ };
+
+ AllDownloadItemNotifier(content::DownloadManager* manager,
+ Observer* observer);
+
+ ~AllDownloadItemNotifier() override;
+
+ // Returns NULL if the manager has gone down.
+ content::DownloadManager* GetManager() const { return manager_; }
+
+ private:
+ // DownloadManager::Observer
+ void OnManagerInitialized() override;
+ void ManagerGoingDown(content::DownloadManager* manager) override;
+ void OnDownloadCreated(content::DownloadManager* manager,
+ content::DownloadItem* item) override;
+
+ // DownloadItem::Observer
+ void OnDownloadUpdated(content::DownloadItem* item) override;
+ void OnDownloadOpened(content::DownloadItem* item) override;
+ void OnDownloadRemoved(content::DownloadItem* item) override;
+ void OnDownloadDestroyed(content::DownloadItem* item) override;
+
+ content::DownloadManager* manager_;
+ AllDownloadItemNotifier::Observer* observer_;
+ std::set<content::DownloadItem*> observing_;
+
+ DISALLOW_COPY_AND_ASSIGN(AllDownloadItemNotifier);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_CONTENT_PUBLIC_ALL_DOWNLOAD_ITEM_NOTIFIER_H_
diff --git a/chromium/components/download/content/public/all_download_item_notifier_unittest.cc b/chromium/components/download/content/public/all_download_item_notifier_unittest.cc
new file mode 100644
index 00000000000..271fed869f7
--- /dev/null
+++ b/chromium/components/download/content/public/all_download_item_notifier_unittest.cc
@@ -0,0 +1,114 @@
+// 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/download/content/public/all_download_item_notifier.h"
+
+#include "base/macros.h"
+#include "content/public/test/mock_download_item.h"
+#include "content/public/test/mock_download_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::NiceMock;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace download {
+namespace {
+
+class MockNotifierObserver : public AllDownloadItemNotifier::Observer {
+ public:
+ MockNotifierObserver() {}
+ virtual ~MockNotifierObserver() {}
+
+ MOCK_METHOD2(OnDownloadCreated,
+ void(content::DownloadManager* manager,
+ content::DownloadItem* item));
+ MOCK_METHOD2(OnDownloadUpdated,
+ void(content::DownloadManager* manager,
+ content::DownloadItem* item));
+ MOCK_METHOD2(OnDownloadOpened,
+ void(content::DownloadManager* manager,
+ content::DownloadItem* item));
+ MOCK_METHOD2(OnDownloadRemoved,
+ void(content::DownloadManager* manager,
+ content::DownloadItem* item));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockNotifierObserver);
+};
+
+class AllDownloadItemNotifierTest : public testing::Test {
+ public:
+ AllDownloadItemNotifierTest()
+ : download_manager_(new content::MockDownloadManager) {}
+
+ virtual ~AllDownloadItemNotifierTest() {}
+
+ content::MockDownloadManager& manager() { return *download_manager_.get(); }
+
+ content::MockDownloadItem& item() { return item_; }
+
+ content::DownloadItem::Observer* NotifierAsItemObserver() const {
+ return notifier_.get();
+ }
+
+ content::DownloadManager::Observer* NotifierAsManagerObserver() const {
+ return notifier_.get();
+ }
+
+ MockNotifierObserver& observer() { return observer_; }
+
+ void SetNotifier() {
+ EXPECT_CALL(*download_manager_.get(), AddObserver(_));
+ notifier_.reset(
+ new AllDownloadItemNotifier(download_manager_.get(), &observer_));
+ }
+
+ void ClearNotifier() { notifier_.reset(); }
+
+ private:
+ NiceMock<content::MockDownloadItem> item_;
+ std::unique_ptr<content::MockDownloadManager> download_manager_;
+ std::unique_ptr<AllDownloadItemNotifier> notifier_;
+ NiceMock<MockNotifierObserver> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(AllDownloadItemNotifierTest);
+};
+
+} // namespace
+
+TEST_F(AllDownloadItemNotifierTest, AllDownloadItemNotifierTest_0) {
+ content::DownloadManager::DownloadVector items;
+ items.push_back(&item());
+ EXPECT_CALL(manager(), GetAllDownloads(_)).WillOnce(SetArgPointee<0>(items));
+ SetNotifier();
+
+ EXPECT_CALL(observer(), OnDownloadUpdated(&manager(), &item()));
+ NotifierAsItemObserver()->OnDownloadUpdated(&item());
+
+ EXPECT_CALL(observer(), OnDownloadOpened(&manager(), &item()));
+ NotifierAsItemObserver()->OnDownloadOpened(&item());
+
+ EXPECT_CALL(observer(), OnDownloadRemoved(&manager(), &item()));
+ NotifierAsItemObserver()->OnDownloadRemoved(&item());
+
+ EXPECT_CALL(manager(), RemoveObserver(NotifierAsManagerObserver()));
+ ClearNotifier();
+}
+
+TEST_F(AllDownloadItemNotifierTest, AllDownloadItemNotifierTest_1) {
+ EXPECT_CALL(manager(), GetAllDownloads(_));
+ SetNotifier();
+
+ EXPECT_CALL(observer(), OnDownloadCreated(&manager(), &item()));
+ NotifierAsManagerObserver()->OnDownloadCreated(&manager(), &item());
+
+ EXPECT_CALL(manager(), RemoveObserver(NotifierAsManagerObserver()));
+ NotifierAsManagerObserver()->ManagerGoingDown(&manager());
+
+ ClearNotifier();
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/BUILD.gn b/chromium/components/download/internal/BUILD.gn
index d1e1e43b885..f138251c6fc 100644
--- a/chromium/components/download/internal/BUILD.gn
+++ b/chromium/components/download/internal/BUILD.gn
@@ -10,36 +10,59 @@ if (is_android) {
static_library("internal") {
visibility = [
":*",
- "//components/download",
- "//components/download/content",
+ "//components/download/content/factory",
+ "//components/download/content/internal",
"//components/download/internal/test:test_support",
]
sources = [
+ "client_set.cc",
+ "client_set.h",
"config.cc",
"config.h",
+ "controller.h",
+ "controller_impl.cc",
+ "controller_impl.h",
"download_driver.h",
"download_service_impl.cc",
"download_service_impl.h",
+ "download_store.cc",
+ "download_store.h",
"driver_entry.cc",
"driver_entry.h",
"entry.cc",
"entry.h",
+ "entry_utils.cc",
+ "entry_utils.h",
+ "file_monitor.h",
+ "file_monitor_impl.cc",
+ "file_monitor_impl.h",
"model.h",
"model_impl.cc",
"model_impl.h",
- "noop_store.cc",
- "noop_store.h",
- "scheduler/battery_listener.cc",
- "scheduler/battery_listener.h",
- "scheduler/network_listener.cc",
- "scheduler/network_listener.h",
+ "proto_conversions.cc",
+ "proto_conversions.h",
+ "scheduler/device_status.cc",
+ "scheduler/device_status.h",
+ "scheduler/device_status_listener.cc",
+ "scheduler/device_status_listener.h",
+ "scheduler/scheduler.h",
+ "scheduler/scheduler_impl.cc",
+ "scheduler/scheduler_impl.h",
+ "service_config_impl.cc",
+ "service_config_impl.h",
+ "startup_status.cc",
+ "startup_status.h",
+ "stats.cc",
+ "stats.h",
"store.h",
]
deps = [
"//base",
+ "//components/download/internal/proto",
"//components/download/public",
+ "//components/leveldb_proto",
"//net",
]
}
@@ -47,18 +70,26 @@ static_library("internal") {
source_set("unit_tests") {
testonly = true
- visibility = [ "//components/download:unit_tests" ]
-
sources = [
+ "client_set_unittest.cc",
+ "controller_impl_unittest.cc",
+ "download_service_impl_unittest.cc",
+ "download_store_unittest.cc",
+ "entry_utils_unittest.cc",
+ "file_monitor_unittest.cc",
"model_impl_unittest.cc",
- "scheduler/battery_listener_unittest.cc",
- "scheduler/network_listener_unittest.cc",
+ "proto_conversions_unittest.cc",
+ "scheduler/device_status_listener_unittest.cc",
+ "scheduler/scheduler_impl_unittest.cc",
+ "service_config_impl_unittest.cc",
]
deps = [
":internal",
"//base/test:test_support",
+ "//components/download/internal/proto",
"//components/download/internal/test:test_support",
+ "//components/leveldb_proto:test_support",
"//testing/gmock",
"//testing/gtest",
]
diff --git a/chromium/components/download/internal/DEPS b/chromium/components/download/internal/DEPS
index 4ca86d9b1fe..6a99578f611 100644
--- a/chromium/components/download/internal/DEPS
+++ b/chromium/components/download/internal/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"-components/download/content",
"-content",
+ "+components/leveldb_proto",
"+base",
"+net",
]
diff --git a/chromium/components/download/internal/client_set.cc b/chromium/components/download/internal/client_set.cc
new file mode 100644
index 00000000000..f74834b0d9a
--- /dev/null
+++ b/chromium/components/download/internal/client_set.cc
@@ -0,0 +1,30 @@
+// 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/download/internal/client_set.h"
+
+namespace download {
+
+ClientSet::ClientSet(std::unique_ptr<DownloadClientMap> clients)
+ : clients_(std::move(clients)) {
+ DCHECK(clients_->find(DownloadClient::INVALID) == clients_->end());
+}
+
+ClientSet::~ClientSet() = default;
+
+std::set<DownloadClient> ClientSet::GetRegisteredClients() const {
+ std::set<DownloadClient> clients;
+ for (const auto& it : *clients_) {
+ clients.insert(it.first);
+ }
+
+ return clients;
+}
+
+Client* ClientSet::GetClient(DownloadClient id) const {
+ const auto& it = clients_->find(id);
+ return it == clients_->end() ? nullptr : it->second.get();
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/client_set.h b/chromium/components/download/internal/client_set.h
new file mode 100644
index 00000000000..2daa5206782
--- /dev/null
+++ b/chromium/components/download/internal/client_set.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_DOWNLOAD_INTERNAL_CLIENT_SET_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_CLIENT_SET_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/macros.h"
+#include "components/download/public/clients.h"
+
+namespace download {
+
+// Helper class to hold a list of Clients and associates them with their
+// respective DownloadClient identifier.
+class ClientSet {
+ public:
+ explicit ClientSet(std::unique_ptr<DownloadClientMap> clients);
+ virtual ~ClientSet();
+
+ std::set<DownloadClient> GetRegisteredClients() const;
+ Client* GetClient(DownloadClient id) const;
+
+ private:
+ std::unique_ptr<DownloadClientMap> clients_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientSet);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_CLIENT_SET_H_
diff --git a/chromium/components/download/internal/client_set_unittest.cc b/chromium/components/download/internal/client_set_unittest.cc
new file mode 100644
index 00000000000..04d454c72e3
--- /dev/null
+++ b/chromium/components/download/internal/client_set_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/download/internal/client_set.h"
+
+#include <algorithm>
+
+#include "base/memory/ptr_util.h"
+#include "components/download/internal/test/empty_client.h"
+#include "components/download/public/clients.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace download {
+
+TEST(DownloadServiceClientSetTest, TestGetClient) {
+ auto client = base::MakeUnique<test::EmptyClient>();
+ Client* raw_client = client.get();
+
+ auto client_map = base::MakeUnique<DownloadClientMap>();
+ client_map->insert(std::make_pair(DownloadClient::TEST, std::move(client)));
+ ClientSet clients(std::move(client_map));
+
+ EXPECT_EQ(raw_client, clients.GetClient(DownloadClient::TEST));
+ EXPECT_EQ(nullptr, clients.GetClient(DownloadClient::INVALID));
+}
+
+TEST(DownloadServiceClientSetTest, TestGetRegisteredClients) {
+ auto client = base::MakeUnique<test::EmptyClient>();
+
+ auto client_map = base::MakeUnique<DownloadClientMap>();
+ client_map->insert(std::make_pair(DownloadClient::TEST, std::move(client)));
+ ClientSet clients(std::move(client_map));
+
+ std::set<DownloadClient> expected_set = {DownloadClient::TEST};
+ std::set<DownloadClient> actual_set = clients.GetRegisteredClients();
+
+ EXPECT_EQ(expected_set.size(), actual_set.size());
+ EXPECT_TRUE(
+ std::equal(expected_set.begin(), expected_set.end(), actual_set.begin()));
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/config.cc b/chromium/components/download/internal/config.cc
index 564da91b881..02bcfdde4ef 100644
--- a/chromium/components/download/internal/config.cc
+++ b/chromium/components/download/internal/config.cc
@@ -16,29 +16,50 @@ namespace download {
namespace {
// Default value for max concurrent downloads configuration.
-const int kDefaultMaxConcurrentDownloads = 4;
+const uint32_t kDefaultMaxConcurrentDownloads = 4;
// Default value for maximum running downloads of the download service.
-const int kDefaultMaxRunningDownloads = 1;
+const uint32_t kDefaultMaxRunningDownloads = 2;
// Default value for maximum scheduled downloads.
-const int kDefaultMaxScheduledDownloads = 15;
+const uint32_t kDefaultMaxScheduledDownloads = 15;
// Default value for maximum retry count.
-const int kDefaultMaxRetryCount = 5;
+const uint32_t kDefaultMaxRetryCount = 5;
// Default value for file keep alive time in minutes, keep the file alive for
// 12 hours by default.
-const int kDefaultFileKeepAliveTimeMinutes = 12 * 60;
+const base::TimeDelta kDefaultFileKeepAliveTime =
+ base::TimeDelta::FromHours(12);
+
+// Default value for file cleanup window in minutes, the system will schedule a
+// cleanup task within this window.
+const base::TimeDelta kDefaultFileCleanupWindow =
+ base::TimeDelta::FromHours(24);
+
+// Default value for the start window time for OS to schedule background task.
+const base::TimeDelta kDefaultWindowStartTime = base::TimeDelta::FromMinutes(5);
+
+// Default value for the end window time for OS to schedule background task.
+const base::TimeDelta kDefaultWindowEndTime = base::TimeDelta::FromHours(8);
+
+// The default delay to notify the observer when network changes from
+// disconnected to connected.
+const base::TimeDelta kDefaultNetworkChangeDelay =
+ base::TimeDelta::FromSeconds(5);
+
+// The default value of download retry delay when the download is failed.
+const base::TimeDelta kDefaultDownloadRetryDelay =
+ base::TimeDelta::FromSeconds(20);
// Helper routine to get Finch experiment parameter. If no Finch seed was found,
// use the |default_value|. The |name| should match an experiment
// parameter in Finch server configuration.
-int GetFinchConfigInt(const std::string& name, int default_value) {
+uint32_t GetFinchConfigUInt(const std::string& name, uint32_t default_value) {
std::string finch_value =
base::GetFieldTrialParamValueByFeature(kDownloadServiceFeature, name);
- int result;
- return base::StringToInt(finch_value, &result) ? result : default_value;
+ uint32_t result;
+ return base::StringToUint(finch_value, &result) ? result : default_value;
}
} // namespace
@@ -46,16 +67,36 @@ int GetFinchConfigInt(const std::string& name, int default_value) {
// static
std::unique_ptr<Configuration> Configuration::CreateFromFinch() {
std::unique_ptr<Configuration> config(new Configuration());
- config->max_concurrent_downloads = GetFinchConfigInt(
+ config->max_concurrent_downloads = GetFinchConfigUInt(
kMaxConcurrentDownloadsConfig, kDefaultMaxConcurrentDownloads);
- config->max_running_downloads = GetFinchConfigInt(
+ config->max_running_downloads = GetFinchConfigUInt(
kMaxRunningDownloadsConfig, kDefaultMaxRunningDownloads);
- config->max_scheduled_downloads = GetFinchConfigInt(
+ config->max_scheduled_downloads = GetFinchConfigUInt(
kMaxScheduledDownloadsConfig, kDefaultMaxScheduledDownloads);
config->max_retry_count =
- GetFinchConfigInt(kMaxRetryCountConfig, kDefaultMaxRetryCount);
- config->file_keep_alive_time = base::TimeDelta::FromMinutes(GetFinchConfigInt(
- kFileKeepAliveTimeMinutesConfig, kDefaultFileKeepAliveTimeMinutes));
+ GetFinchConfigUInt(kMaxRetryCountConfig, kDefaultMaxRetryCount);
+ config->file_keep_alive_time =
+ base::TimeDelta::FromMinutes(base::saturated_cast<int>(
+ GetFinchConfigUInt(kFileKeepAliveTimeMinutesConfig,
+ kDefaultFileKeepAliveTime.InMinutes())));
+ config->file_cleanup_window =
+ base::TimeDelta::FromMinutes(base::saturated_cast<int>(
+ GetFinchConfigUInt(kFileCleanupWindowMinutesConfig,
+ kDefaultFileCleanupWindow.InMinutes())));
+ config->window_start_time =
+ base::TimeDelta::FromSeconds(base::saturated_cast<int>(GetFinchConfigUInt(
+ kWindowStartTimeSecondsConfig, kDefaultWindowStartTime.InSeconds())));
+ config->window_end_time =
+ base::TimeDelta::FromSeconds(base::saturated_cast<int>(GetFinchConfigUInt(
+ kWindowEndTimeSecondsConfig, kDefaultWindowEndTime.InSeconds())));
+ config->network_change_delay =
+ base::TimeDelta::FromMilliseconds(base::saturated_cast<int>(
+ GetFinchConfigUInt(kNetworkChangeDelayMsConfig,
+ kDefaultNetworkChangeDelay.InMilliseconds())));
+ config->download_retry_delay =
+ base::TimeDelta::FromMilliseconds(base::saturated_cast<int>(
+ GetFinchConfigUInt(kDownloadRetryDelayMsConfig,
+ kDefaultDownloadRetryDelay.InMilliseconds())));
return config;
}
@@ -64,7 +105,11 @@ Configuration::Configuration()
max_running_downloads(kDefaultMaxRunningDownloads),
max_scheduled_downloads(kDefaultMaxScheduledDownloads),
max_retry_count(kDefaultMaxRetryCount),
- file_keep_alive_time(
- base::TimeDelta::FromMinutes(kDefaultFileKeepAliveTimeMinutes)) {}
+ file_keep_alive_time(kDefaultFileKeepAliveTime),
+ file_cleanup_window(kDefaultFileCleanupWindow),
+ window_start_time(kDefaultWindowStartTime),
+ window_end_time(kDefaultWindowEndTime),
+ network_change_delay(kDefaultNetworkChangeDelay),
+ download_retry_delay(kDefaultDownloadRetryDelay) {}
} // namespace download
diff --git a/chromium/components/download/internal/config.h b/chromium/components/download/internal/config.h
index c5b3b0d44d3..073676fcc38 100644
--- a/chromium/components/download/internal/config.h
+++ b/chromium/components/download/internal/config.h
@@ -27,6 +27,23 @@ constexpr char kMaxRetryCountConfig[] = "max_retry_count";
// Configuration name for file keep alive time.
constexpr char kFileKeepAliveTimeMinutesConfig[] = "file_keep_alive_time";
+// Configuration name for file keep alive time.
+constexpr char kFileCleanupWindowMinutesConfig[] = "file_cleanup_window";
+
+// Configuration name for window start time.
+constexpr char kWindowStartTimeSecondsConfig[] = "window_start_time_seconds";
+
+// Configuration name for window end time.
+constexpr char kWindowEndTimeSecondsConfig[] = "window_end_time_seconds";
+
+// Configuration name for the delay to notify network status change, measured in
+// milliseconds.
+constexpr char kNetworkChangeDelayMsConfig[] = "network_change_delay_ms";
+
+// Configuration name for the retry delay when the download is failed, measured
+// in milliseconds.
+constexpr char kDownloadRetryDelayMsConfig[] = "retry_delay_ms";
+
// Download service configuration.
//
// Loaded based on experiment parameters from the server. Use default values if
@@ -39,23 +56,41 @@ struct Configuration {
// The maximum number of downloads the DownloadService can have currently in
// Active or Paused states.
- int max_concurrent_downloads;
+ uint32_t max_concurrent_downloads;
// The maximum number of downloads the DownloadService can have currently in
// only Active state.
- int max_running_downloads;
+ uint32_t max_running_downloads;
- // The maximum number of downloads that are scheduled but not yet in Active
- // state, for each client using the download service.
- int max_scheduled_downloads;
+ // The maximum number of downloads that are scheduled for each client using
+ // the download service.
+ uint32_t max_scheduled_downloads;
// The maximum number of retries before the download is aborted.
- int max_retry_count;
+ uint32_t max_retry_count;
// The time that the download service will keep the files around before
// deleting them if the client hasn't handle the files.
base::TimeDelta file_keep_alive_time;
+ // The length of the flexible time window during which the scheduler must
+ // schedule a file cleanup task.
+ base::TimeDelta file_cleanup_window;
+
+ // The start window time in seconds for OS to schedule background task.
+ // The OS will trigger the background task in this window.
+ base::TimeDelta window_start_time;
+
+ // The end window time in seconds for OS to schedule background task.
+ // The OS will trigger the background task in this window.
+ base::TimeDelta window_end_time;
+
+ // The delay to notify network status changes.
+ base::TimeDelta network_change_delay;
+
+ // The delay to retry a download when the download is failed.
+ base::TimeDelta download_retry_delay;
+
private:
DISALLOW_COPY_AND_ASSIGN(Configuration);
};
diff --git a/chromium/components/download/internal/controller.h b/chromium/components/download/internal/controller.h
new file mode 100644
index 00000000000..a19a223f0f5
--- /dev/null
+++ b/chromium/components/download/internal/controller.h
@@ -0,0 +1,110 @@
+// 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_INTERNAL_CONTROLLER_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_CONTROLLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/download/public/clients.h"
+#include "components/download/public/download_service.h"
+#include "components/download/public/download_task_types.h"
+
+namespace download {
+
+struct DownloadParams;
+struct SchedulingParams;
+
+// The type of completion when the download entry transits to complete state.
+// TODO(xingliu): Implement timeout and unknown failure types.
+enum class CompletionType {
+ // The download is successfully finished.
+ SUCCEED = 0,
+ // The download is interrupted and failed.
+ FAIL = 1,
+ // The download is aborted by the client.
+ ABORT = 2,
+ // The download is timed out and the connection is closed.
+ TIMEOUT = 3,
+ // The download is failed for unknown reasons.
+ UNKNOWN = 4,
+ // The download is cancelled by the client.
+ CANCEL = 5,
+ // The count of entries for the enum.
+ COUNT = 6,
+};
+
+// The core Controller responsible for gluing various DownloadService components
+// together to manage the active downloads.
+class Controller {
+ public:
+ enum class State {
+ // The Controller has been created but has not been initialized yet. It
+ // cannot be used.
+ CREATED = 1,
+
+ // The Controller has been created and Initialize() has been called but has
+ // not yet finished. It cannot be used.
+ INITIALIZING = 2,
+
+ // The Controller has been created and initialized. It can be used.
+ READY = 3,
+
+ // The Controller failed to initialize and is in the process of recovering.
+ // It cannot be used.
+ RECOVERING = 4,
+
+ // The Controller was unable to recover and is unusable this session.
+ UNAVAILABLE = 5,
+ };
+
+ Controller() = default;
+ virtual ~Controller() = default;
+
+ // Initializes the controller. Initialization may be asynchronous.
+ virtual void Initialize(const base::Closure& callback) = 0;
+
+ // Returns the status of Controller.
+ virtual State GetState() = 0;
+
+ // Starts a download with |params|. See DownloadParams::StartCallback and
+ // DownloadParams::StartResponse for information on how a caller can determine
+ // whether or not the download was successfully accepted and queued.
+ virtual void StartDownload(const DownloadParams& params) = 0;
+
+ // Pauses a download request associated with |guid| if one exists.
+ virtual void PauseDownload(const std::string& guid) = 0;
+
+ // Resumes a download request associated with |guid| if one exists. The
+ // download request may or may not start downloading at this time, but it will
+ // no longer be blocked by any prior PauseDownload() actions.
+ virtual void ResumeDownload(const std::string& guid) = 0;
+
+ // Cancels a download request associated with |guid| if one exists.
+ virtual void CancelDownload(const std::string& guid) = 0;
+
+ // Changes the SchedulingParams of a download request associated with |guid|
+ // to |params|.
+ virtual void ChangeDownloadCriteria(const std::string& guid,
+ const SchedulingParams& params) = 0;
+
+ // Exposes the owner of the download request for |guid| if one exists.
+ // Otherwise returns DownloadClient::INVALID for an unowned entry.
+ virtual DownloadClient GetOwnerOfDownload(const std::string& guid) = 0;
+
+ // See DownloadService::OnStartScheduledTask.
+ virtual void OnStartScheduledTask(DownloadTaskType task_type,
+ const TaskFinishedCallback& callback) = 0;
+
+ // See DownloadService::OnStopScheduledTask.
+ virtual bool OnStopScheduledTask(DownloadTaskType task_type) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Controller);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_CONTROLLER_H_
diff --git a/chromium/components/download/internal/controller_impl.cc b/chromium/components/download/internal/controller_impl.cc
new file mode 100644
index 00000000000..03fa70c46ec
--- /dev/null
+++ b/chromium/components/download/internal/controller_impl.cc
@@ -0,0 +1,1034 @@
+// 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/download/internal/controller_impl.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/internal/client_set.h"
+#include "components/download/internal/config.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/entry_utils.h"
+#include "components/download/internal/file_monitor.h"
+#include "components/download/internal/model.h"
+#include "components/download/internal/scheduler/scheduler.h"
+#include "components/download/internal/stats.h"
+#include "components/download/public/client.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+
+namespace download {
+namespace {
+
+// Helper function to transit the state of |entry| to |new_state|.
+void TransitTo(Entry* entry, Entry::State new_state, Model* model) {
+ DCHECK(entry);
+ if (entry->state == new_state)
+ return;
+ entry->state = new_state;
+ model->Update(*entry);
+}
+
+// Helper function to move from a CompletionType to a Client::FailureReason.
+Client::FailureReason FailureReasonFromCompletionType(CompletionType type) {
+ // SUCCEED does not map to a FailureReason.
+ DCHECK_NE(CompletionType::SUCCEED, type);
+
+ switch (type) {
+ case CompletionType::FAIL:
+ return Client::FailureReason::NETWORK;
+ case CompletionType::ABORT:
+ return Client::FailureReason::ABORTED;
+ case CompletionType::TIMEOUT:
+ return Client::FailureReason::TIMEDOUT;
+ case CompletionType::UNKNOWN:
+ return Client::FailureReason::UNKNOWN;
+ case CompletionType::CANCEL:
+ return Client::FailureReason::CANCELLED;
+ default:
+ NOTREACHED();
+ }
+
+ return Client::FailureReason::UNKNOWN;
+}
+
+// Helper function to determine if more downloads can be activated based on
+// configuration.
+bool CanActivateMoreDownloads(Configuration* config,
+ uint32_t active_count,
+ uint32_t paused_count) {
+ if (config->max_concurrent_downloads <= paused_count + active_count ||
+ config->max_running_downloads <= active_count) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+ControllerImpl::ControllerImpl(
+ Configuration* config,
+ std::unique_ptr<ClientSet> clients,
+ std::unique_ptr<DownloadDriver> driver,
+ std::unique_ptr<Model> model,
+ std::unique_ptr<DeviceStatusListener> device_status_listener,
+ std::unique_ptr<Scheduler> scheduler,
+ std::unique_ptr<TaskScheduler> task_scheduler,
+ std::unique_ptr<FileMonitor> file_monitor,
+ const base::FilePath& download_file_dir)
+ : config_(config),
+ download_file_dir_(download_file_dir),
+ clients_(std::move(clients)),
+ driver_(std::move(driver)),
+ model_(std::move(model)),
+ device_status_listener_(std::move(device_status_listener)),
+ scheduler_(std::move(scheduler)),
+ task_scheduler_(std::move(task_scheduler)),
+ file_monitor_(std::move(file_monitor)),
+ controller_state_(State::CREATED),
+ weak_ptr_factory_(this) {}
+
+ControllerImpl::~ControllerImpl() = default;
+
+void ControllerImpl::Initialize(const base::Closure& callback) {
+ DCHECK_EQ(controller_state_, State::CREATED);
+
+ init_callback_ = callback;
+ controller_state_ = State::INITIALIZING;
+
+ driver_->Initialize(this);
+ model_->Initialize(this);
+ file_monitor_->Initialize(base::Bind(&ControllerImpl::OnFileMonitorReady,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+Controller::State ControllerImpl::GetState() {
+ return controller_state_;
+}
+
+void ControllerImpl::StartDownload(const DownloadParams& params) {
+ DCHECK(controller_state_ == State::READY ||
+ controller_state_ == State::UNAVAILABLE);
+
+ // TODO(dtrainor): Validate all input parameters.
+ DCHECK_LE(base::Time::Now(), params.scheduling_params.cancel_time);
+
+ if (controller_state_ != State::READY) {
+ HandleStartDownloadResponse(params.client, params.guid,
+ DownloadParams::StartResult::INTERNAL_ERROR,
+ params.callback);
+ return;
+ }
+
+ KillTimedOutDownloads();
+
+ if (start_callbacks_.find(params.guid) != start_callbacks_.end() ||
+ model_->Get(params.guid) != nullptr) {
+ HandleStartDownloadResponse(params.client, params.guid,
+ DownloadParams::StartResult::UNEXPECTED_GUID,
+ params.callback);
+ return;
+ }
+
+ auto* client = clients_->GetClient(params.client);
+ if (!client) {
+ HandleStartDownloadResponse(params.client, params.guid,
+ DownloadParams::StartResult::UNEXPECTED_CLIENT,
+ params.callback);
+ return;
+ }
+
+ uint32_t client_count =
+ util::GetNumberOfEntriesForClient(params.client, model_->PeekEntries());
+ if (client_count >= config_->max_scheduled_downloads) {
+ HandleStartDownloadResponse(params.client, params.guid,
+ DownloadParams::StartResult::BACKOFF,
+ params.callback);
+ return;
+ }
+
+ start_callbacks_[params.guid] = params.callback;
+ Entry entry(params);
+ entry.target_file_path = download_file_dir_.AppendASCII(params.guid);
+ model_->Add(entry);
+}
+
+void ControllerImpl::PauseDownload(const std::string& guid) {
+ DCHECK(controller_state_ == State::READY ||
+ controller_state_ == State::UNAVAILABLE);
+ if (controller_state_ != State::READY)
+ return;
+
+ auto* entry = model_->Get(guid);
+
+ if (!entry || entry->state == Entry::State::PAUSED ||
+ entry->state == Entry::State::COMPLETE ||
+ entry->state == Entry::State::NEW) {
+ return;
+ }
+
+ TransitTo(entry, Entry::State::PAUSED, model_.get());
+ UpdateDriverState(entry);
+
+ // Pausing a download may yield a concurrent slot to start a new download, and
+ // may change the scheduling criteria.
+ ActivateMoreDownloads();
+}
+
+void ControllerImpl::ResumeDownload(const std::string& guid) {
+ DCHECK(controller_state_ == State::READY ||
+ controller_state_ == State::UNAVAILABLE);
+ if (controller_state_ != State::READY)
+ return;
+
+ auto* entry = model_->Get(guid);
+ DCHECK(entry);
+
+ if (entry->state != Entry::State::PAUSED)
+ return;
+
+ TransitTo(entry, Entry::State::ACTIVE, model_.get());
+ UpdateDriverState(entry);
+
+ ActivateMoreDownloads();
+}
+
+void ControllerImpl::CancelDownload(const std::string& guid) {
+ DCHECK(controller_state_ == State::READY ||
+ controller_state_ == State::UNAVAILABLE);
+ if (controller_state_ != State::READY)
+ return;
+
+ auto* entry = model_->Get(guid);
+ if (!entry)
+ return;
+
+ if (entry->state == Entry::State::NEW) {
+ // Check if we're currently trying to add the download.
+ DCHECK(start_callbacks_.find(entry->guid) != start_callbacks_.end());
+ HandleStartDownloadResponse(entry->client, guid,
+ DownloadParams::StartResult::CLIENT_CANCELLED);
+ return;
+ }
+
+ HandleCompleteDownload(CompletionType::CANCEL, guid);
+}
+
+void ControllerImpl::ChangeDownloadCriteria(const std::string& guid,
+ const SchedulingParams& params) {
+ DCHECK(controller_state_ == State::READY ||
+ controller_state_ == State::UNAVAILABLE);
+ if (controller_state_ != State::READY)
+ return;
+
+ auto* entry = model_->Get(guid);
+ if (!entry || entry->scheduling_params == params) {
+ DVLOG(1) << "Try to update the same scheduling parameters.";
+ return;
+ }
+
+ UpdateDriverState(entry);
+
+ // Update the scheduling parameters.
+ entry->scheduling_params = params;
+ model_->Update(*entry);
+
+ ActivateMoreDownloads();
+}
+
+DownloadClient ControllerImpl::GetOwnerOfDownload(const std::string& guid) {
+ DCHECK(controller_state_ == State::READY ||
+ controller_state_ == State::UNAVAILABLE);
+ if (controller_state_ != State::READY)
+ return DownloadClient::INVALID;
+
+ auto* entry = model_->Get(guid);
+ return entry ? entry->client : DownloadClient::INVALID;
+}
+
+void ControllerImpl::OnStartScheduledTask(
+ DownloadTaskType task_type,
+ const TaskFinishedCallback& callback) {
+ task_finished_callbacks_[task_type] = callback;
+
+ switch (controller_state_) {
+ case State::READY:
+ if (task_type == DownloadTaskType::DOWNLOAD_TASK) {
+ ActivateMoreDownloads();
+ } else if (task_type == DownloadTaskType::CLEANUP_TASK) {
+ RemoveCleanupEligibleDownloads();
+ ScheduleCleanupTask();
+ }
+ break;
+ case State::UNAVAILABLE:
+ HandleTaskFinished(task_type, false,
+ stats::ScheduledTaskStatus::ABORTED_ON_FAILED_INIT);
+ break;
+ case State::CREATED: // Intentional fallthrough.
+ case State::INITIALIZING: // Intentional fallthrough.
+ case State::RECOVERING: // Intentional fallthrough.
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+bool ControllerImpl::OnStopScheduledTask(DownloadTaskType task_type) {
+ HandleTaskFinished(task_type, false,
+ stats::ScheduledTaskStatus::CANCELLED_ON_STOP);
+ return true;
+}
+
+void ControllerImpl::OnCompleteCleanupTask() {
+ HandleTaskFinished(DownloadTaskType::CLEANUP_TASK, false,
+ stats::ScheduledTaskStatus::COMPLETED_NORMALLY);
+}
+
+void ControllerImpl::RemoveCleanupEligibleDownloads() {
+ auto timed_out_entries = file_monitor_->CleanupFilesForCompletedEntries(
+ model_->PeekEntries(), base::Bind(&ControllerImpl::OnCompleteCleanupTask,
+ weak_ptr_factory_.GetWeakPtr()));
+ for (auto* entry : timed_out_entries) {
+ DCHECK_EQ(Entry::State::COMPLETE, entry->state);
+ model_->Remove(entry->guid);
+ }
+}
+
+void ControllerImpl::HandleTaskFinished(DownloadTaskType task_type,
+ bool needs_reschedule,
+ stats::ScheduledTaskStatus status) {
+ if (task_finished_callbacks_.find(task_type) ==
+ task_finished_callbacks_.end()) {
+ return;
+ }
+
+ if (status != stats::ScheduledTaskStatus::CANCELLED_ON_STOP) {
+ base::ResetAndReturn(&task_finished_callbacks_[task_type])
+ .Run(needs_reschedule);
+ }
+ // TODO(dtrainor): It might be useful to log how many downloads we have
+ // running when we're asked to stop processing.
+ stats::LogScheduledTaskStatus(task_type, status);
+ task_finished_callbacks_.erase(task_type);
+}
+
+void ControllerImpl::OnDriverReady(bool success) {
+ DCHECK(!startup_status_.driver_ok.has_value());
+ startup_status_.driver_ok = success;
+ AttemptToFinalizeSetup();
+}
+
+void ControllerImpl::OnDriverHardRecoverComplete(bool success) {
+ DCHECK(!startup_status_.driver_ok.has_value());
+ startup_status_.driver_ok = success;
+ AttemptToFinalizeSetup();
+}
+
+void ControllerImpl::OnDownloadCreated(const DriverEntry& download) {
+ if (controller_state_ != State::READY)
+ return;
+
+ Entry* entry = model_->Get(download.guid);
+
+ if (!entry) {
+ HandleExternalDownload(download.guid, true);
+ return;
+ }
+
+ download::Client* client = clients_->GetClient(entry->client);
+ DCHECK(client);
+ using ShouldDownload = download::Client::ShouldDownload;
+ ShouldDownload should_download = client->OnDownloadStarted(
+ download.guid, download.url_chain, download.response_headers);
+ stats::LogStartDownloadResponse(entry->client, should_download);
+ if (should_download == ShouldDownload::ABORT) {
+ HandleCompleteDownload(CompletionType::ABORT, entry->guid);
+ }
+}
+
+void ControllerImpl::OnDownloadFailed(const DriverEntry& download,
+ FailureType failure_type) {
+ if (controller_state_ != State::READY)
+ return;
+
+ Entry* entry = model_->Get(download.guid);
+ if (!entry) {
+ HandleExternalDownload(download.guid, false);
+ return;
+ }
+
+ if (failure_type == FailureType::RECOVERABLE) {
+ // Because the network offline signal comes later than actual download
+ // failure, retry the download after a delay to avoid the retry to fail
+ // immediately again.
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ControllerImpl::UpdateDriverStateWithGuid,
+ weak_ptr_factory_.GetWeakPtr(), download.guid),
+ config_->download_retry_delay);
+ } else {
+ // TODO(dtrainor, xingliu): We probably have to prevent cancel calls from
+ // coming through here as we remove downloads (especially through
+ // initialization).
+ HandleCompleteDownload(CompletionType::FAIL, download.guid);
+ }
+}
+
+void ControllerImpl::OnDownloadSucceeded(const DriverEntry& download) {
+ if (controller_state_ != State::READY)
+ return;
+
+ Entry* entry = model_->Get(download.guid);
+ if (!entry) {
+ HandleExternalDownload(download.guid, false);
+ return;
+ }
+
+ HandleCompleteDownload(CompletionType::SUCCEED, download.guid);
+}
+
+void ControllerImpl::OnDownloadUpdated(const DriverEntry& download) {
+ if (controller_state_ != State::READY)
+ return;
+
+ Entry* entry = model_->Get(download.guid);
+ if (!entry) {
+ HandleExternalDownload(download.guid, !download.paused);
+ return;
+ }
+
+ DCHECK_EQ(download.state, DriverEntry::State::IN_PROGRESS);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&ControllerImpl::SendOnDownloadUpdated,
+ weak_ptr_factory_.GetWeakPtr(), entry->client,
+ download.guid, download.bytes_downloaded));
+}
+
+void ControllerImpl::OnFileMonitorReady(bool success) {
+ DCHECK(!startup_status_.file_monitor_ok.has_value());
+ startup_status_.file_monitor_ok = success;
+ AttemptToFinalizeSetup();
+}
+
+void ControllerImpl::OnFileMonitorHardRecoverComplete(bool success) {
+ DCHECK(!startup_status_.file_monitor_ok.has_value());
+ startup_status_.file_monitor_ok = success;
+ AttemptToFinalizeSetup();
+}
+
+void ControllerImpl::OnModelReady(bool success) {
+ DCHECK(!startup_status_.model_ok.has_value());
+ startup_status_.model_ok = success;
+ AttemptToFinalizeSetup();
+}
+
+void ControllerImpl::OnModelHardRecoverComplete(bool success) {
+ DCHECK(!startup_status_.model_ok.has_value());
+ startup_status_.model_ok = success;
+ AttemptToFinalizeSetup();
+}
+
+void ControllerImpl::OnItemAdded(bool success,
+ DownloadClient client,
+ const std::string& guid) {
+ // If the StartCallback doesn't exist, we already notified the Client about
+ // this item. That means something went wrong, so stop here.
+ if (start_callbacks_.find(guid) == start_callbacks_.end())
+ return;
+
+ if (!success) {
+ HandleStartDownloadResponse(client, guid,
+ DownloadParams::StartResult::INTERNAL_ERROR);
+ return;
+ }
+
+ HandleStartDownloadResponse(client, guid,
+ DownloadParams::StartResult::ACCEPTED);
+
+ Entry* entry = model_->Get(guid);
+ DCHECK(entry);
+ DCHECK_EQ(Entry::State::NEW, entry->state);
+ TransitTo(entry, Entry::State::AVAILABLE, model_.get());
+
+ ActivateMoreDownloads();
+}
+
+void ControllerImpl::OnItemUpdated(bool success,
+ DownloadClient client,
+ const std::string& guid) {
+ Entry* entry = model_->Get(guid);
+ DCHECK(entry);
+
+ // Now that we're sure that our state is set correctly, it is OK to remove the
+ // DriverEntry. If we restart we'll see a COMPLETE state and handle it
+ // accordingly.
+ if (entry->state == Entry::State::COMPLETE)
+ driver_->Remove(guid);
+
+ // TODO(dtrainor): If failed, clean up any download state accordingly.
+}
+
+void ControllerImpl::OnItemRemoved(bool success,
+ DownloadClient client,
+ const std::string& guid) {
+ // TODO(dtrainor): If failed, clean up any download state accordingly.
+}
+
+void ControllerImpl::OnDeviceStatusChanged(const DeviceStatus& device_status) {
+ if (controller_state_ != State::READY)
+ return;
+
+ UpdateDriverStates();
+ ActivateMoreDownloads();
+}
+
+void ControllerImpl::AttemptToFinalizeSetup() {
+ DCHECK(controller_state_ == State::INITIALIZING ||
+ controller_state_ == State::RECOVERING);
+
+ if (!startup_status_.Complete())
+ return;
+
+ bool in_recovery = controller_state_ == State::RECOVERING;
+
+ stats::LogControllerStartupStatus(in_recovery, startup_status_);
+ if (!startup_status_.Ok()) {
+ if (in_recovery) {
+ HandleUnrecoverableSetup();
+ NotifyServiceOfStartup();
+ } else {
+ StartHardRecoveryAttempt();
+ }
+
+ return;
+ }
+
+ device_status_listener_->Start(this);
+ PollActiveDriverDownloads();
+ CancelOrphanedRequests();
+ CleanupUnknownFiles();
+ RemoveCleanupEligibleDownloads();
+ ResolveInitialRequestStates();
+
+ NotifyClientsOfStartup(in_recovery);
+
+ controller_state_ = State::READY;
+
+ UpdateDriverStates();
+
+ KillTimedOutDownloads();
+ NotifyServiceOfStartup();
+
+ // Pull the initial straw if active downloads haven't reach maximum.
+ ActivateMoreDownloads();
+}
+
+void ControllerImpl::HandleUnrecoverableSetup() {
+ controller_state_ = State::UNAVAILABLE;
+
+ // If we cannot recover, notify Clients that the service is unavailable.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&ControllerImpl::SendOnServiceUnavailable,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ControllerImpl::StartHardRecoveryAttempt() {
+ startup_status_.Reset();
+ controller_state_ = State::RECOVERING;
+
+ driver_->HardRecover();
+ model_->HardRecover();
+ file_monitor_->HardRecover(
+ base::Bind(&ControllerImpl::OnFileMonitorHardRecoverComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ControllerImpl::PollActiveDriverDownloads() {
+ DCHECK(controller_state_ == State::INITIALIZING ||
+ controller_state_ == State::RECOVERING);
+
+ std::set<std::string> guids = driver_->GetActiveDownloads();
+
+ for (auto guid : guids) {
+ if (!model_->Get(guid))
+ externally_active_downloads_.insert(guid);
+ }
+}
+
+void ControllerImpl::CancelOrphanedRequests() {
+ DCHECK(controller_state_ == State::INITIALIZING ||
+ controller_state_ == State::RECOVERING);
+
+ auto entries = model_->PeekEntries();
+
+ std::vector<std::string> guids_to_remove;
+ std::set<base::FilePath> files_to_remove;
+ for (auto* entry : entries) {
+ if (!clients_->GetClient(entry->client)) {
+ guids_to_remove.push_back(entry->guid);
+ files_to_remove.insert(entry->target_file_path);
+ }
+ }
+
+ for (const auto& guid : guids_to_remove) {
+ driver_->Remove(guid);
+ model_->Remove(guid);
+ }
+
+ file_monitor_->DeleteFiles(files_to_remove,
+ stats::FileCleanupReason::ORPHANED);
+}
+
+void ControllerImpl::CleanupUnknownFiles() {
+ DCHECK(controller_state_ == State::INITIALIZING ||
+ controller_state_ == State::RECOVERING);
+
+ auto entries = model_->PeekEntries();
+ std::vector<DriverEntry> driver_entries;
+ for (auto* entry : entries) {
+ base::Optional<DriverEntry> driver_entry = driver_->Find(entry->guid);
+ if (driver_entry.has_value())
+ driver_entries.push_back(driver_entry.value());
+ }
+
+ file_monitor_->DeleteUnknownFiles(entries, driver_entries);
+}
+
+void ControllerImpl::ResolveInitialRequestStates() {
+ DCHECK(controller_state_ == State::INITIALIZING ||
+ controller_state_ == State::RECOVERING);
+
+ auto entries = model_->PeekEntries();
+ for (auto* entry : entries) {
+ // Pull the initial Entry::State and DriverEntry::State.
+ Entry::State state = entry->state;
+ auto driver_entry = driver_->Find(entry->guid);
+ base::Optional<DriverEntry::State> driver_state;
+ if (driver_entry.has_value()) {
+ DCHECK_NE(DriverEntry::State::UNKNOWN, driver_entry->state);
+ driver_state = driver_entry->state;
+ }
+
+ // Determine what the new Entry::State should be based on the two original
+ // states of the two different systems.
+ Entry::State new_state = state;
+ switch (state) {
+ case Entry::State::NEW:
+ // This means we shut down but may have not ACK'ed the download. That
+ // is OK, we will still notify the Client about the GUID when we send
+ // them our initialize method.
+ new_state = Entry::State::AVAILABLE;
+ break;
+ case Entry::State::COMPLETE:
+ // We're already in our end state. Just stay here.
+ new_state = Entry::State::COMPLETE;
+ break;
+ case Entry::State::AVAILABLE: // Intentional fallthrough.
+ case Entry::State::ACTIVE: // Intentional fallthrough.
+ case Entry::State::PAUSED: {
+ // All three of these states are effectively driven by the DriverEntry
+ // state.
+ if (!driver_state.has_value()) {
+ // If we don't have a DriverEntry::State, just leave the state alone.
+ new_state = state;
+ break;
+ }
+
+ // If we have a real DriverEntry::State, we need to determine which of
+ // those states makes sense for our Entry. Our entry can either be in
+ // two states now: It's effective 'active' state (ACTIVE or PAUSED) or
+ // COMPLETE.
+ bool is_paused = state == Entry::State::PAUSED;
+ Entry::State active =
+ is_paused ? Entry::State::PAUSED : Entry::State::ACTIVE;
+
+ switch (driver_state.value()) {
+ case DriverEntry::State::IN_PROGRESS: // Intentional fallthrough.
+ case DriverEntry::State::INTERRUPTED:
+ // The DriverEntry isn't done, so we need to set the Entry to the
+ // 'active' state.
+ new_state = active;
+ break;
+ case DriverEntry::State::COMPLETE: // Intentional fallthrough.
+ // TODO(dtrainor, xingliu) Revisit this CANCELLED state to make sure
+ // all embedders behave properly.
+ case DriverEntry::State::CANCELLED:
+ // The DriverEntry is done. We need to set the Entry to the
+ // COMPLETE state.
+ new_state = Entry::State::COMPLETE;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ // Update the Entry::State to the new correct state.
+ if (new_state != entry->state) {
+ stats::LogRecoveryOperation(new_state);
+ TransitTo(entry, new_state, model_.get());
+ }
+
+ // Given the new correct state, update the DriverEntry to reflect the Entry.
+ switch (new_state) {
+ case Entry::State::NEW: // Intentional fallthrough.
+ case Entry::State::AVAILABLE: // Intentional fallthrough.
+ // We should not have a DriverEntry here.
+ if (driver_entry.has_value())
+ driver_->Remove(entry->guid);
+ break;
+ case Entry::State::ACTIVE: // Intentional fallthrough.
+ case Entry::State::PAUSED:
+ // We're in the correct state. Let UpdateDriverStates() restart us if
+ // it wants to.
+ break;
+ case Entry::State::COMPLETE:
+ if (state != Entry::State::COMPLETE) {
+ // We are changing states to COMPLETE. Handle this like a normal
+ // completed download.
+
+ // Treat CANCELLED and INTERRUPTED as failures. We have to assume the
+ // DriverEntry might not have persisted in time.
+ CompletionType completion_type =
+ (!driver_entry.has_value() ||
+ driver_entry->state == DriverEntry::State::CANCELLED ||
+ driver_entry->state == DriverEntry::State::INTERRUPTED)
+ ? CompletionType::UNKNOWN
+ : CompletionType::SUCCEED;
+ HandleCompleteDownload(completion_type, entry->guid);
+ } else {
+ // We're staying in COMPLETE. Make sure there is no DriverEntry here.
+ if (driver_entry.has_value())
+ driver_->Remove(entry->guid);
+ }
+ break;
+ case Entry::State::COUNT:
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void ControllerImpl::UpdateDriverStates() {
+ DCHECK(startup_status_.Complete());
+
+ for (auto* entry : model_->PeekEntries())
+ UpdateDriverState(entry);
+}
+
+void ControllerImpl::UpdateDriverStateWithGuid(const std::string& guid) {
+ Entry* entry = model_->Get(guid);
+ if (entry)
+ UpdateDriverState(entry);
+}
+
+void ControllerImpl::UpdateDriverState(Entry* entry) {
+ DCHECK_EQ(controller_state_, State::READY);
+
+ if (entry->state != Entry::State::ACTIVE &&
+ entry->state != Entry::State::PAUSED) {
+ return;
+ }
+
+ // This method will need to figure out what to do with a failed download and
+ // either a) restart it or b) fail the download.
+
+ base::Optional<DriverEntry> driver_entry = driver_->Find(entry->guid);
+
+ bool meets_device_criteria = device_status_listener_->CurrentDeviceStatus()
+ .MeetsCondition(entry->scheduling_params)
+ .MeetsRequirements();
+ bool force_pause =
+ !externally_active_downloads_.empty() &&
+ entry->scheduling_params.priority != SchedulingParams::Priority::UI;
+ bool entry_paused = entry->state == Entry::State::PAUSED;
+
+ bool pause_driver = entry_paused || force_pause || !meets_device_criteria;
+
+ if (pause_driver) {
+ if (driver_entry.has_value())
+ driver_->Pause(entry->guid);
+ } else {
+ bool is_new_attempt =
+ !driver_entry.has_value() ||
+ driver_entry->state == DriverEntry::State::INTERRUPTED;
+ if (is_new_attempt) {
+ entry->attempt_count++;
+ model_->Update(*entry);
+ if (entry->attempt_count >= config_->max_retry_count) {
+ HandleCompleteDownload(CompletionType::FAIL, entry->guid);
+ return;
+ }
+ }
+
+ if (driver_entry.has_value()) {
+ driver_->Resume(entry->guid);
+ } else {
+ driver_->Start(entry->request_params, entry->guid,
+ entry->target_file_path, NO_TRAFFIC_ANNOTATION_YET);
+ }
+ }
+}
+
+void ControllerImpl::NotifyClientsOfStartup(bool state_lost) {
+ std::set<Entry::State> ignored_states = {Entry::State::COMPLETE};
+ auto categorized = util::MapEntriesToClients(
+ clients_->GetRegisteredClients(), model_->PeekEntries(), ignored_states);
+
+ for (auto client_id : clients_->GetRegisteredClients()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&ControllerImpl::SendOnServiceInitialized,
+ weak_ptr_factory_.GetWeakPtr(), client_id,
+ state_lost, categorized[client_id]));
+ }
+}
+
+void ControllerImpl::NotifyServiceOfStartup() {
+ if (init_callback_.is_null())
+ return;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::ResetAndReturn(&init_callback_));
+}
+
+void ControllerImpl::HandleStartDownloadResponse(
+ DownloadClient client,
+ const std::string& guid,
+ DownloadParams::StartResult result) {
+ auto callback = start_callbacks_[guid];
+ start_callbacks_.erase(guid);
+ HandleStartDownloadResponse(client, guid, result, callback);
+}
+
+void ControllerImpl::HandleStartDownloadResponse(
+ DownloadClient client,
+ const std::string& guid,
+ DownloadParams::StartResult result,
+ const DownloadParams::StartCallback& callback) {
+ stats::LogStartDownloadResult(client, result);
+
+ // UNEXPECTED_GUID means the guid was already in use. Don't remove this entry
+ // from the model because it's there due to another request.
+ if (result != DownloadParams::StartResult::ACCEPTED &&
+ result != DownloadParams::StartResult::UNEXPECTED_GUID &&
+ model_->Get(guid) != nullptr) {
+ model_->Remove(guid);
+ }
+
+ if (callback.is_null())
+ return;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, guid, result));
+}
+
+void ControllerImpl::HandleCompleteDownload(CompletionType type,
+ const std::string& guid) {
+ Entry* entry = model_->Get(guid);
+ DCHECK(entry);
+
+ if (entry->state == Entry::State::COMPLETE) {
+ DVLOG(1) << "Download is already completed.";
+ return;
+ }
+
+ auto driver_entry = driver_->Find(guid);
+ uint64_t file_size =
+ driver_entry.has_value() ? driver_entry->bytes_downloaded : 0;
+ stats::LogDownloadCompletion(
+ type, entry->completion_time - entry->create_time, file_size);
+
+ if (type == CompletionType::SUCCEED) {
+ DCHECK(driver_entry.has_value());
+ stats::LogFilePathRenamed(driver_entry->current_file_path !=
+ entry->target_file_path);
+ entry->target_file_path = driver_entry->current_file_path;
+
+ entry->completion_time = driver_entry->completion_time;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&ControllerImpl::SendOnDownloadSucceeded,
+ weak_ptr_factory_.GetWeakPtr(), entry->client,
+ guid, driver_entry->current_file_path,
+ driver_entry->bytes_downloaded));
+ TransitTo(entry, Entry::State::COMPLETE, model_.get());
+ ScheduleCleanupTask();
+ } else {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&ControllerImpl::SendOnDownloadFailed,
+ weak_ptr_factory_.GetWeakPtr(), entry->client,
+ guid, FailureReasonFromCompletionType(type)));
+ // TODO(dtrainor): Handle the case where we crash before the model write
+ // happens and we have no driver entry.
+ driver_->Remove(entry->guid);
+ model_->Remove(guid);
+ }
+
+ ActivateMoreDownloads();
+}
+
+void ControllerImpl::ScheduleCleanupTask() {
+ base::Time earliest_completion_time = base::Time::Max();
+ for (const Entry* entry : model_->PeekEntries()) {
+ if (entry->completion_time == base::Time() ||
+ entry->state != Entry::State::COMPLETE)
+ continue;
+ if (entry->completion_time < earliest_completion_time) {
+ earliest_completion_time = entry->completion_time;
+ }
+ }
+
+ if (earliest_completion_time == base::Time::Max())
+ return;
+
+ base::TimeDelta start_time = earliest_completion_time +
+ config_->file_keep_alive_time -
+ base::Time::Now();
+ base::TimeDelta end_time = start_time + config_->file_cleanup_window;
+
+ task_scheduler_->ScheduleTask(DownloadTaskType::CLEANUP_TASK, false, false,
+ std::ceil(start_time.InSecondsF()),
+ std::ceil(end_time.InSecondsF()));
+}
+
+void ControllerImpl::ScheduleKillDownloadTaskIfNecessary() {
+ base::Time earliest_cancel_time = base::Time::Max();
+ for (const Entry* entry : model_->PeekEntries()) {
+ if (entry->state != Entry::State::COMPLETE &&
+ entry->scheduling_params.cancel_time < earliest_cancel_time) {
+ earliest_cancel_time = entry->scheduling_params.cancel_time;
+ }
+ }
+
+ if (earliest_cancel_time == base::Time::Max())
+ return;
+
+ base::TimeDelta time_to_cancel =
+ earliest_cancel_time > base::Time::Now()
+ ? earliest_cancel_time - base::Time::Now()
+ : base::TimeDelta();
+
+ cancel_downloads_callback_.Reset(base::Bind(
+ &ControllerImpl::KillTimedOutDownloads, weak_ptr_factory_.GetWeakPtr()));
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, cancel_downloads_callback_.callback(), time_to_cancel);
+}
+
+void ControllerImpl::KillTimedOutDownloads() {
+ for (const Entry* entry : model_->PeekEntries()) {
+ if (entry->state != Entry::State::COMPLETE &&
+ entry->scheduling_params.cancel_time <= base::Time::Now()) {
+ HandleCompleteDownload(CompletionType::TIMEOUT, entry->guid);
+ }
+ }
+
+ ScheduleKillDownloadTaskIfNecessary();
+}
+
+void ControllerImpl::ActivateMoreDownloads() {
+ if (controller_state_ != State::READY)
+ return;
+
+ // Check all the entries and the configuration to throttle number of
+ // downloads.
+ std::map<Entry::State, uint32_t> entries_states;
+ Model::EntryList scheduling_candidates;
+ for (auto* const entry : model_->PeekEntries()) {
+ entries_states[entry->state]++;
+ // Only schedule background tasks based on available and active entries.
+ if (entry->state == Entry::State::AVAILABLE ||
+ entry->state == Entry::State::ACTIVE) {
+ scheduling_candidates.emplace_back(entry);
+ }
+ }
+
+ uint32_t paused_count = entries_states[Entry::State::PAUSED];
+ uint32_t active_count = entries_states[Entry::State::ACTIVE];
+
+ bool has_actionable_downloads = false;
+ while (CanActivateMoreDownloads(config_, active_count, paused_count)) {
+ Entry* next = scheduler_->Next(
+ model_->PeekEntries(), device_status_listener_->CurrentDeviceStatus());
+ if (!next)
+ break;
+
+ has_actionable_downloads = true;
+ DCHECK_EQ(Entry::State::AVAILABLE, next->state);
+ TransitTo(next, Entry::State::ACTIVE, model_.get());
+ active_count++;
+ UpdateDriverState(next);
+ }
+
+ if (!has_actionable_downloads) {
+ HandleTaskFinished(DownloadTaskType::DOWNLOAD_TASK, false,
+ stats::ScheduledTaskStatus::COMPLETED_NORMALLY);
+ }
+
+ scheduler_->Reschedule(scheduling_candidates);
+}
+
+void ControllerImpl::HandleExternalDownload(const std::string& guid,
+ bool active) {
+ if (active) {
+ externally_active_downloads_.insert(guid);
+ } else {
+ externally_active_downloads_.erase(guid);
+ }
+
+ UpdateDriverStates();
+}
+
+void ControllerImpl::SendOnServiceInitialized(
+ DownloadClient client_id,
+ bool state_lost,
+ const std::vector<std::string>& guids) {
+ auto* client = clients_->GetClient(client_id);
+ DCHECK(client);
+ client->OnServiceInitialized(state_lost, guids);
+}
+
+void ControllerImpl::SendOnServiceUnavailable() {
+ for (auto client_id : clients_->GetRegisteredClients()) {
+ clients_->GetClient(client_id)->OnServiceUnavailable();
+ }
+}
+
+void ControllerImpl::SendOnDownloadUpdated(DownloadClient client_id,
+ const std::string& guid,
+ uint64_t bytes_downloaded) {
+ if (!model_->Get(guid))
+ return;
+
+ auto* client = clients_->GetClient(client_id);
+ DCHECK(client);
+ client->OnDownloadUpdated(guid, bytes_downloaded);
+}
+
+void ControllerImpl::SendOnDownloadSucceeded(DownloadClient client_id,
+ const std::string& guid,
+ const base::FilePath& path,
+ uint64_t size) {
+ auto* client = clients_->GetClient(client_id);
+ DCHECK(client);
+ client->OnDownloadSucceeded(guid, path, size);
+}
+
+void ControllerImpl::SendOnDownloadFailed(
+ DownloadClient client_id,
+ const std::string& guid,
+ download::Client::FailureReason reason) {
+ auto* client = clients_->GetClient(client_id);
+ DCHECK(client);
+ client->OnDownloadFailed(guid, reason);
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/controller_impl.h b/chromium/components/download/internal/controller_impl.h
new file mode 100644
index 00000000000..911ada78f00
--- /dev/null
+++ b/chromium/components/download/internal/controller_impl.h
@@ -0,0 +1,234 @@
+// 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_INTERNAL_CONTROLLER_IMPL_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_CONTROLLER_IMPL_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/download/internal/controller.h"
+#include "components/download/internal/download_driver.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/model.h"
+#include "components/download/internal/scheduler/device_status_listener.h"
+#include "components/download/internal/startup_status.h"
+#include "components/download/internal/stats.h"
+#include "components/download/public/client.h"
+#include "components/download/public/download_params.h"
+#include "components/download/public/task_scheduler.h"
+
+namespace download {
+
+class ClientSet;
+class DownloadDriver;
+class FileMonitor;
+class Model;
+class Scheduler;
+
+struct Configuration;
+struct SchedulingParams;
+
+// The internal Controller implementation. This class does all of the heavy
+// lifting for the DownloadService.
+class ControllerImpl : public Controller,
+ public DownloadDriver::Client,
+ public Model::Client,
+ public DeviceStatusListener::Observer {
+ public:
+ // |config| is externally owned and must be guaranteed to outlive this class.
+ ControllerImpl(Configuration* config,
+ std::unique_ptr<ClientSet> clients,
+ std::unique_ptr<DownloadDriver> driver,
+ std::unique_ptr<Model> model,
+ std::unique_ptr<DeviceStatusListener> device_status_listener,
+ std::unique_ptr<Scheduler> scheduler,
+ std::unique_ptr<TaskScheduler> task_scheduler,
+ std::unique_ptr<FileMonitor> file_monitor,
+ const base::FilePath& download_file_dir);
+ ~ControllerImpl() override;
+
+ // Controller implementation.
+ void Initialize(const base::Closure& callback) override;
+ State GetState() override;
+ void StartDownload(const DownloadParams& params) override;
+ void PauseDownload(const std::string& guid) override;
+ void ResumeDownload(const std::string& guid) override;
+ void CancelDownload(const std::string& guid) override;
+ void ChangeDownloadCriteria(const std::string& guid,
+ const SchedulingParams& params) override;
+ DownloadClient GetOwnerOfDownload(const std::string& guid) override;
+ void OnStartScheduledTask(DownloadTaskType task_type,
+ const TaskFinishedCallback& callback) override;
+ bool OnStopScheduledTask(DownloadTaskType task_type) override;
+
+ private:
+ // DownloadDriver::Client implementation.
+ void OnDriverReady(bool success) override;
+ void OnDriverHardRecoverComplete(bool success) override;
+ void OnDownloadCreated(const DriverEntry& download) override;
+ void OnDownloadFailed(const DriverEntry& download,
+ FailureType failure_type) override;
+ void OnDownloadSucceeded(const DriverEntry& download) override;
+ void OnDownloadUpdated(const DriverEntry& download) override;
+
+ // Model::Client implementation.
+ void OnModelReady(bool success) override;
+ void OnModelHardRecoverComplete(bool success) override;
+ void OnItemAdded(bool success,
+ DownloadClient client,
+ const std::string& guid) override;
+ void OnItemUpdated(bool success,
+ DownloadClient client,
+ const std::string& guid) override;
+ void OnItemRemoved(bool success,
+ DownloadClient client,
+ const std::string& guid) override;
+
+ // Called when the file monitor and download file directory are initialized.
+ void OnFileMonitorReady(bool success);
+
+ // Called when the file monitor finishes attempting to recover itself.
+ void OnFileMonitorHardRecoverComplete(bool success);
+
+ // DeviceStatusListener::Observer implementation.
+ void OnDeviceStatusChanged(const DeviceStatus& device_status) override;
+
+ // Checks if initialization is complete and successful. If so, completes the
+ // internal state initialization.
+ void AttemptToFinalizeSetup();
+
+ // Called when setup and recovery failed. Shuts down the service and notifies
+ // the Clients.
+ void HandleUnrecoverableSetup();
+
+ // If initialization failed, try to reset the state of all components and
+ // restart them. If that attempt fails the service will be unavailable.
+ void StartHardRecoveryAttempt();
+
+ // Checks for all the currently active driver downloads. This lets us know
+ // which ones are active that we haven't tracked.
+ void PollActiveDriverDownloads();
+
+ // Cancels and cleans upany requests that are no longer associated with a
+ // Client in |clients_|.
+ void CancelOrphanedRequests();
+
+ // Cleans up any files that are left on the disk without any entries.
+ void CleanupUnknownFiles();
+
+ // Fixes any discrepancies in state between |model_| and |driver_|. Meant to
+ // resolve state issues during startup.
+ void ResolveInitialRequestStates();
+
+ // Updates the driver states based on the states of entries in download
+ // service.
+ void UpdateDriverStates();
+
+ // See |UpdateDriverState|.
+ void UpdateDriverStateWithGuid(const std::string& guid);
+
+ // Processes the download based on the state of |entry|. May start, pause
+ // or resume a download accordingly.
+ void UpdateDriverState(Entry* entry);
+
+ // Notifies all Client in |clients_| that this controller is initialized and
+ // lets them know which download requests we are aware of for their
+ // DownloadClient.
+ void NotifyClientsOfStartup(bool state_lost);
+
+ // Notifies the service that the startup has completed so that it can start
+ // processing any pending requests.
+ void NotifyServiceOfStartup();
+
+ void HandleStartDownloadResponse(DownloadClient client,
+ const std::string& guid,
+ DownloadParams::StartResult result);
+ void HandleStartDownloadResponse(
+ DownloadClient client,
+ const std::string& guid,
+ DownloadParams::StartResult result,
+ const DownloadParams::StartCallback& callback);
+
+ // Handles and clears any pending task finished callbacks.
+ void HandleTaskFinished(DownloadTaskType task_type,
+ bool needs_reschedule,
+ stats::ScheduledTaskStatus status);
+ void OnCompleteCleanupTask();
+
+ void HandleCompleteDownload(CompletionType type, const std::string& guid);
+
+ // Find more available entries to download, until the number of active entries
+ // reached maximum.
+ void ActivateMoreDownloads();
+
+ void RemoveCleanupEligibleDownloads();
+
+ void HandleExternalDownload(const std::string& guid, bool active);
+
+ // Postable methods meant to just be pass throughs to Client APIs. This is
+ // meant to help prevent reentrancy.
+ void SendOnServiceInitialized(DownloadClient client_id,
+ bool state_lost,
+ const std::vector<std::string>& guids);
+ void SendOnServiceUnavailable();
+ void SendOnDownloadUpdated(DownloadClient client_id,
+ const std::string& guid,
+ uint64_t bytes_downloaded);
+ void SendOnDownloadSucceeded(DownloadClient client_id,
+ const std::string& guid,
+ const base::FilePath& path,
+ uint64_t size);
+ void SendOnDownloadFailed(DownloadClient client_id,
+ const std::string& guid,
+ download::Client::FailureReason reason);
+
+ // Schedules a cleanup task in future based on status of entries.
+ void ScheduleCleanupTask();
+
+ // Posts a task to cancel the downloads in future based on the cancel_after
+ // time of the entries. If cancel time for an entry is already surpassed, the
+ // task will be posted right away which will clean the entry.
+ void ScheduleKillDownloadTaskIfNecessary();
+
+ // Kills the downloads which have surpassed their cancel_after time.
+ void KillTimedOutDownloads();
+
+ Configuration* config_;
+
+ // The directory in which the downloaded files are stored.
+ const base::FilePath download_file_dir_;
+
+ // Owned Dependencies.
+ std::unique_ptr<ClientSet> clients_;
+ std::unique_ptr<DownloadDriver> driver_;
+ std::unique_ptr<Model> model_;
+ std::unique_ptr<DeviceStatusListener> device_status_listener_;
+ std::unique_ptr<Scheduler> scheduler_;
+ std::unique_ptr<TaskScheduler> task_scheduler_;
+ std::unique_ptr<FileMonitor> file_monitor_;
+
+ // Internal state.
+ base::Closure init_callback_;
+ State controller_state_;
+ StartupStatus startup_status_;
+ std::set<std::string> externally_active_downloads_;
+ std::map<std::string, DownloadParams::StartCallback> start_callbacks_;
+ std::map<DownloadTaskType, TaskFinishedCallback> task_finished_callbacks_;
+ base::CancelableClosure cancel_downloads_callback_;
+
+ // Only used to post tasks on the same thread.
+ base::WeakPtrFactory<ControllerImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ControllerImpl);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_CONTROLLER_IMPL_H_
diff --git a/chromium/components/download/internal/controller_impl_unittest.cc b/chromium/components/download/internal/controller_impl_unittest.cc
new file mode 100644
index 00000000000..5a5a7a1bbb7
--- /dev/null
+++ b/chromium/components/download/internal/controller_impl_unittest.cc
@@ -0,0 +1,1405 @@
+// 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/download/internal/controller_impl.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/internal/client_set.h"
+#include "components/download/internal/config.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/file_monitor.h"
+#include "components/download/internal/model_impl.h"
+#include "components/download/internal/scheduler/scheduler.h"
+#include "components/download/internal/stats.h"
+#include "components/download/internal/test/entry_utils.h"
+#include "components/download/internal/test/mock_client.h"
+#include "components/download/internal/test/test_device_status_listener.h"
+#include "components/download/internal/test/test_download_driver.h"
+#include "components/download/internal/test/test_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+using testing::SaveArg;
+
+namespace download {
+
+namespace {
+
+const base::FilePath::CharType kDownloadDirPath[] =
+ FILE_PATH_LITERAL("/test/downloads");
+
+bool GuidInEntryList(const std::vector<Entry>& entries,
+ const std::string& guid) {
+ for (const auto& entry : entries) {
+ if (entry.guid == guid)
+ return true;
+ }
+ return false;
+}
+
+DriverEntry BuildDriverEntry(const Entry& entry, DriverEntry::State state) {
+ DriverEntry dentry;
+ dentry.guid = entry.guid;
+ dentry.state = state;
+ return dentry;
+}
+
+void NotifyTaskFinished(bool success) {}
+
+class MockTaskScheduler : public TaskScheduler {
+ public:
+ MockTaskScheduler() = default;
+ ~MockTaskScheduler() override = default;
+
+ // TaskScheduler implementation.
+ MOCK_METHOD5(ScheduleTask, void(DownloadTaskType, bool, bool, long, long));
+ MOCK_METHOD1(CancelTask, void(DownloadTaskType));
+};
+
+class MockScheduler : public Scheduler {
+ public:
+ MockScheduler() = default;
+ ~MockScheduler() override = default;
+
+ MOCK_METHOD1(Reschedule, void(const Model::EntryList&));
+ MOCK_METHOD2(Next, Entry*(const Model::EntryList&, const DeviceStatus&));
+};
+
+class MockFileMonitor : public FileMonitor {
+ public:
+ MockFileMonitor() = default;
+ ~MockFileMonitor() override = default;
+
+ void TriggerInit(bool success);
+ void TriggerHardRecover(bool success);
+
+ void Initialize(const FileMonitor::InitCallback& callback) override;
+ MOCK_METHOD2(DeleteUnknownFiles,
+ void(const Model::EntryList&, const std::vector<DriverEntry>&));
+ MOCK_METHOD2(CleanupFilesForCompletedEntries,
+ std::vector<Entry*>(const Model::EntryList&,
+ const base::Closure&));
+ MOCK_METHOD2(DeleteFiles,
+ void(const std::set<base::FilePath>&, stats::FileCleanupReason));
+ void HardRecover(const FileMonitor::InitCallback&) override;
+
+ private:
+ FileMonitor::InitCallback init_callback_;
+ FileMonitor::InitCallback recover_callback_;
+};
+
+void MockFileMonitor::TriggerInit(bool success) {
+ init_callback_.Run(success);
+}
+
+void MockFileMonitor::TriggerHardRecover(bool success) {
+ recover_callback_.Run(success);
+}
+
+void MockFileMonitor::Initialize(const FileMonitor::InitCallback& callback) {
+ init_callback_ = callback;
+}
+
+void MockFileMonitor::HardRecover(const FileMonitor::InitCallback& callback) {
+ recover_callback_ = callback;
+}
+
+class DownloadServiceControllerImplTest : public testing::Test {
+ public:
+ DownloadServiceControllerImplTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ handle_(task_runner_),
+ controller_(nullptr),
+ client_(nullptr),
+ driver_(nullptr),
+ store_(nullptr),
+ model_(nullptr),
+ device_status_listener_(nullptr),
+ scheduler_(nullptr),
+ file_monitor_(nullptr),
+ init_callback_called_(false) {
+ start_callback_ =
+ base::Bind(&DownloadServiceControllerImplTest::StartCallback,
+ base::Unretained(this));
+ }
+
+ ~DownloadServiceControllerImplTest() override = default;
+
+ void SetUp() override {
+ auto client = base::MakeUnique<test::MockClient>();
+ auto driver = base::MakeUnique<test::TestDownloadDriver>();
+ auto store = base::MakeUnique<test::TestStore>();
+ config_ = base::MakeUnique<Configuration>();
+ config_->max_retry_count = 2;
+ config_->file_keep_alive_time = base::TimeDelta::FromMinutes(10);
+ config_->file_cleanup_window = base::TimeDelta::FromMinutes(5);
+ config_->max_concurrent_downloads = 5;
+ config_->max_running_downloads = 5;
+
+ client_ = client.get();
+ driver_ = driver.get();
+ store_ = store.get();
+
+ auto clients = base::MakeUnique<DownloadClientMap>();
+ clients->insert(std::make_pair(DownloadClient::TEST, std::move(client)));
+ auto client_set = base::MakeUnique<ClientSet>(std::move(clients));
+ auto model = base::MakeUnique<ModelImpl>(std::move(store));
+ auto device_status_listener =
+ base::MakeUnique<test::TestDeviceStatusListener>();
+ auto scheduler = base::MakeUnique<NiceMock<MockScheduler>>();
+ auto task_scheduler = base::MakeUnique<MockTaskScheduler>();
+
+ auto download_file_dir = base::FilePath(kDownloadDirPath);
+ auto file_monitor = base::MakeUnique<MockFileMonitor>();
+
+ model_ = model.get();
+ device_status_listener_ = device_status_listener.get();
+ scheduler_ = scheduler.get();
+ task_scheduler_ = task_scheduler.get();
+ file_monitor_ = file_monitor.get();
+
+ controller_ = base::MakeUnique<ControllerImpl>(
+ config_.get(), std::move(client_set), std::move(driver),
+ std::move(model), std::move(device_status_listener),
+ std::move(scheduler), std::move(task_scheduler),
+ std::move(file_monitor), download_file_dir);
+ }
+
+ protected:
+ void OnInitCompleted() {
+ EXPECT_TRUE(controller_->GetState() == Controller::State::READY ||
+ controller_->GetState() == Controller::State::UNAVAILABLE);
+ init_callback_called_ = true;
+ }
+
+ void InitializeController() {
+ controller_->Initialize(
+ base::Bind(&DownloadServiceControllerImplTest::OnInitCompleted,
+ base::Unretained(this)));
+ }
+
+ DownloadParams MakeDownloadParams() {
+ DownloadParams params;
+ params.client = DownloadClient::TEST;
+ params.guid = base::GenerateGUID();
+ params.callback = start_callback_;
+ return params;
+ }
+
+ MOCK_METHOD2(StartCallback,
+ void(const std::string&, DownloadParams::StartResult));
+
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle handle_;
+
+ std::unique_ptr<ControllerImpl> controller_;
+ std::unique_ptr<Configuration> config_;
+ test::MockClient* client_;
+ test::TestDownloadDriver* driver_;
+ test::TestStore* store_;
+ ModelImpl* model_;
+ test::TestDeviceStatusListener* device_status_listener_;
+ MockScheduler* scheduler_;
+ MockTaskScheduler* task_scheduler_;
+ MockFileMonitor* file_monitor_;
+
+ DownloadParams::StartCallback start_callback_;
+ bool init_callback_called_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DownloadServiceControllerImplTest);
+};
+
+} // namespace
+
+TEST_F(DownloadServiceControllerImplTest, SuccessfulInitModelFirst) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(0);
+ EXPECT_EQ(controller_->GetState(), Controller::State::CREATED);
+
+ InitializeController();
+ EXPECT_TRUE(store_->init_called());
+ EXPECT_EQ(controller_->GetState(), Controller::State::INITIALIZING);
+
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+ EXPECT_EQ(controller_->GetState(), Controller::State::INITIALIZING);
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ driver_->MakeReady();
+ EXPECT_EQ(controller_->GetState(), Controller::State::READY);
+
+ task_runner_->RunUntilIdle();
+
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::SUCCESS),
+ 1);
+}
+
+TEST_F(DownloadServiceControllerImplTest, SuccessfulInitDriverFirst) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(0);
+ EXPECT_EQ(controller_->GetState(), Controller::State::CREATED);
+
+ InitializeController();
+ EXPECT_TRUE(store_->init_called());
+ EXPECT_EQ(controller_->GetState(), Controller::State::INITIALIZING);
+
+ driver_->MakeReady();
+ EXPECT_FALSE(init_callback_called_);
+ EXPECT_EQ(controller_->GetState(), Controller::State::INITIALIZING);
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+ EXPECT_EQ(controller_->GetState(), Controller::State::READY);
+
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(init_callback_called_);
+
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::SUCCESS),
+ 1);
+}
+
+TEST_F(DownloadServiceControllerImplTest, HardRecoveryAfterFailedModel) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(*client_, OnServiceInitialized(true, _)).Times(0);
+ EXPECT_EQ(controller_->GetState(), Controller::State::CREATED);
+
+ InitializeController();
+ driver_->MakeReady();
+ store_->TriggerInit(false, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+
+ EXPECT_EQ(controller_->GetState(), Controller::State::RECOVERING);
+ driver_->TriggerHardRecoverComplete(true);
+ store_->TriggerHardRecover(true);
+ file_monitor_->TriggerHardRecover(true);
+
+ EXPECT_CALL(*client_, OnServiceInitialized(true, _)).Times(1);
+ task_runner_->RunUntilIdle();
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::FAILURE),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(
+ stats::StartUpResult::FAILURE_REASON_MODEL),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Recovery",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::SUCCESS),
+ 1);
+}
+
+TEST_F(DownloadServiceControllerImplTest, HardRecoveryAfterFailedFileMonitor) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(*client_, OnServiceInitialized(true, _)).Times(0);
+ EXPECT_EQ(controller_->GetState(), Controller::State::CREATED);
+
+ InitializeController();
+ driver_->MakeReady();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(false);
+
+ EXPECT_EQ(controller_->GetState(), Controller::State::RECOVERING);
+ driver_->TriggerHardRecoverComplete(true);
+ store_->TriggerHardRecover(true);
+ file_monitor_->TriggerHardRecover(true);
+
+ EXPECT_CALL(*client_, OnServiceInitialized(true, _)).Times(1);
+ task_runner_->RunUntilIdle();
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::FAILURE),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(
+ stats::StartUpResult::FAILURE_REASON_FILE_MONITOR),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Recovery",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::SUCCESS),
+ 1);
+}
+
+TEST_F(DownloadServiceControllerImplTest, HardRecoveryFails) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(*client_, OnServiceInitialized(true, _)).Times(0);
+ EXPECT_EQ(controller_->GetState(), Controller::State::CREATED);
+
+ InitializeController();
+ driver_->MakeReady();
+ store_->TriggerInit(false, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+
+ EXPECT_EQ(controller_->GetState(), Controller::State::RECOVERING);
+ driver_->TriggerHardRecoverComplete(true);
+ store_->TriggerHardRecover(true);
+ file_monitor_->TriggerHardRecover(false);
+
+ EXPECT_CALL(*client_, OnServiceUnavailable()).Times(1);
+ task_runner_->RunUntilIdle();
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::FAILURE),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Initialization",
+ static_cast<base::HistogramBase::Sample>(
+ stats::StartUpResult::FAILURE_REASON_MODEL),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Recovery",
+ static_cast<base::HistogramBase::Sample>(stats::StartUpResult::FAILURE),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.StartUpStatus.Recovery",
+ static_cast<base::HistogramBase::Sample>(
+ stats::StartUpResult::FAILURE_REASON_FILE_MONITOR),
+ 1);
+}
+
+TEST_F(DownloadServiceControllerImplTest, SuccessfulInitWithExistingDownload) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ Entry entry3 =
+ test::BuildEntry(DownloadClient::INVALID, base::GenerateGUID());
+
+ std::vector<Entry> entries = {entry1, entry2, entry3};
+ std::vector<std::string> expected_guids = {entry1.guid, entry2.guid};
+
+ EXPECT_CALL(*client_,
+ OnServiceInitialized(
+ false, testing::UnorderedElementsAreArray(expected_guids)));
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ InitializeController();
+ driver_->MakeReady();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(init_callback_called_);
+}
+
+TEST_F(DownloadServiceControllerImplTest, UnknownFileDeletion) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ Entry entry3 = test::BuildBasicEntry();
+
+ std::vector<Entry> entries = {entry1, entry2, entry3};
+
+ DriverEntry dentry1 =
+ BuildDriverEntry(entry1, DriverEntry::State::IN_PROGRESS);
+ DriverEntry dentry3 =
+ BuildDriverEntry(entry3, DriverEntry::State::IN_PROGRESS);
+ std::vector<DriverEntry> dentries = {dentry1, dentry3};
+
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+ EXPECT_CALL(*file_monitor_, DeleteUnknownFiles(_, _)).Times(1);
+
+ driver_->AddTestData(dentries);
+ InitializeController();
+ driver_->MakeReady();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest,
+ CleanupTaskCallsFileMonitorAndSchedulesNewTaskInFuture) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ Entry entry3 = test::BuildBasicEntry();
+ entry3.state = Entry::State::COMPLETE;
+ entry3.completion_time = base::Time::Now();
+
+ std::vector<Entry> entries = {entry1, entry2, entry3};
+
+ EXPECT_CALL(*file_monitor_, CleanupFilesForCompletedEntries(_, _)).Times(2);
+ EXPECT_CALL(*task_scheduler_,
+ ScheduleTask(DownloadTaskType::CLEANUP_TASK, _, _, _, _))
+ .Times(1);
+
+ InitializeController();
+ driver_->MakeReady();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ controller_->OnStartScheduledTask(DownloadTaskType::CLEANUP_TASK,
+ base::Bind(&NotifyTaskFinished));
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, GetOwnerOfDownload) {
+ Entry entry = test::BuildBasicEntry();
+ std::vector<Entry> entries = {entry};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ InitializeController();
+ driver_->MakeReady();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+
+ task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(DownloadClient::TEST, controller_->GetOwnerOfDownload(entry.guid));
+ EXPECT_EQ(DownloadClient::INVALID,
+ controller_->GetOwnerOfDownload(base::GenerateGUID()));
+}
+
+TEST_F(DownloadServiceControllerImplTest, AddDownloadAccepted) {
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ // Set up the Controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Trigger the download.
+ DownloadParams params = MakeDownloadParams();
+ EXPECT_CALL(*this,
+ StartCallback(params.guid, DownloadParams::StartResult::ACCEPTED))
+ .Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+ controller_->StartDownload(params);
+
+ // TODO(dtrainor): Compare the full DownloadParams with the full Entry.
+ store_->TriggerUpdate(true);
+
+ std::vector<Entry> entries = store_->updated_entries();
+ Entry entry = entries[0];
+ DCHECK_EQ(entry.client, DownloadClient::TEST);
+ EXPECT_TRUE(base::StartsWith(entry.target_file_path.value(), kDownloadDirPath,
+ base::CompareCase::SENSITIVE));
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithBackoff) {
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ Entry entry = test::BuildBasicEntry();
+ std::vector<Entry> entries = {entry};
+
+ // Set up the Controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Set the failure expectations.
+ config_->max_scheduled_downloads = 1U;
+
+ // Trigger the download.
+ DownloadParams params = MakeDownloadParams();
+ EXPECT_CALL(*this,
+ StartCallback(params.guid, DownloadParams::StartResult::BACKOFF))
+ .Times(1);
+ controller_->StartDownload(params);
+
+ EXPECT_FALSE(GuidInEntryList(store_->updated_entries(), params.guid));
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest,
+ AddDownloadFailsWithDuplicateGuidInModel) {
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ Entry entry = test::BuildBasicEntry();
+ std::vector<Entry> entries = {entry};
+
+ // Set up the Controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Trigger the download.
+ DownloadParams params = MakeDownloadParams();
+ params.guid = entry.guid;
+ EXPECT_CALL(
+ *this,
+ StartCallback(params.guid, DownloadParams::StartResult::UNEXPECTED_GUID))
+ .Times(1);
+ controller_->StartDownload(params);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithDuplicateCall) {
+ testing::InSequence sequence;
+
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ // Set up the Controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ // Trigger the download twice.
+ DownloadParams params = MakeDownloadParams();
+ EXPECT_CALL(
+ *this,
+ StartCallback(params.guid, DownloadParams::StartResult::UNEXPECTED_GUID))
+ .Times(1);
+ EXPECT_CALL(*this,
+ StartCallback(params.guid, DownloadParams::StartResult::ACCEPTED))
+ .Times(1);
+ controller_->StartDownload(params);
+ controller_->StartDownload(params);
+ store_->TriggerUpdate(true);
+
+ EXPECT_TRUE(GuidInEntryList(store_->updated_entries(), params.guid));
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithBadClient) {
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ // Set up the Controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Trigger the download.
+ DownloadParams params = MakeDownloadParams();
+ params.client = DownloadClient::INVALID;
+ EXPECT_CALL(*this,
+ StartCallback(params.guid,
+ DownloadParams::StartResult::UNEXPECTED_CLIENT))
+ .Times(1);
+ controller_->StartDownload(params);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithClientCancel) {
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ // Set up the Controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Trigger the download.
+ DownloadParams params = MakeDownloadParams();
+ EXPECT_CALL(
+ *this,
+ StartCallback(params.guid, DownloadParams::StartResult::CLIENT_CANCELLED))
+ .Times(1);
+ controller_->StartDownload(params);
+
+ controller_->CancelDownload(params.guid);
+ store_->TriggerUpdate(true);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithInternalError) {
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ // Set up the Controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Trigger the download.
+ DownloadParams params = MakeDownloadParams();
+ EXPECT_CALL(*this, StartCallback(params.guid,
+ DownloadParams::StartResult::INTERNAL_ERROR))
+ .Times(1);
+ controller_->StartDownload(params);
+
+ store_->TriggerUpdate(false);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, Pause) {
+ // Setup download service test data.
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ Entry entry3 = test::BuildBasicEntry();
+ entry1.state = Entry::State::AVAILABLE;
+ entry2.state = Entry::State::ACTIVE;
+ entry3.state = Entry::State::COMPLETE;
+ std::vector<Entry> entries = {entry1, entry2, entry3};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(3);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(3);
+
+ // Set the network status to disconnected so no entries will be polled from
+ // the scheduler.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+
+ // Setup download driver test data.
+ DriverEntry driver_entry1, driver_entry2, driver_entry3;
+ driver_entry1.guid = entry1.guid;
+ driver_entry1.state = DriverEntry::State::IN_PROGRESS;
+ driver_entry2.guid = entry2.guid;
+ driver_entry2.state = DriverEntry::State::IN_PROGRESS;
+ driver_entry3.guid = entry3.guid;
+ driver_->AddTestData(
+ std::vector<DriverEntry>{driver_entry1, driver_entry2, driver_entry3});
+
+ // Pause in progress available entry.
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entry1.guid)->state);
+ controller_->PauseDownload(entry1.guid);
+ EXPECT_TRUE(driver_->Find(entry1.guid)->paused);
+ EXPECT_EQ(Entry::State::PAUSED, model_->Get(entry1.guid)->state);
+
+ // Pause in progress active entry.
+ controller_->PauseDownload(entry2.guid);
+ EXPECT_TRUE(driver_->Find(entry2.guid)->paused);
+ EXPECT_EQ(Entry::State::PAUSED, model_->Get(entry2.guid)->state);
+
+ // Entries in complete states can't be paused.
+ controller_->PauseDownload(entry3.guid);
+ EXPECT_FALSE(driver_->Find(entry3.guid)->paused);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry3.guid)->state);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, Resume) {
+ // Setup download service test data.
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ entry1.state = Entry::State::PAUSED;
+ entry2.state = Entry::State::ACTIVE;
+ std::vector<Entry> entries = {entry1, entry2};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+
+ // Setup download driver test data.
+ DriverEntry driver_entry1, driver_entry2;
+ driver_entry1.guid = entry1.guid;
+ driver_entry1.paused = true;
+ driver_entry2.guid = entry2.guid;
+ driver_entry2.paused = false;
+ driver_->AddTestData(std::vector<DriverEntry>{driver_entry1, driver_entry2});
+
+ // Resume the paused download.
+ device_status_listener_->SetDeviceStatus(
+ DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(Entry::State::PAUSED, model_->Get(entry1.guid)->state);
+ controller_->ResumeDownload(entry1.guid);
+ EXPECT_FALSE(driver_->Find(entry1.guid)->paused);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry1.guid)->state);
+
+ // Entries in paused state can't be resumed.
+ controller_->ResumeDownload(entry2.guid);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry2.guid)->state);
+ EXPECT_FALSE(driver_->Find(entry2.guid)->paused);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, Cancel) {
+ Entry entry = test::BuildBasicEntry();
+ entry.state = Entry::State::ACTIVE;
+ std::vector<Entry> entries = {entry};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*client_,
+ OnDownloadFailed(entry.guid, Client::FailureReason::CANCELLED))
+ .Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+
+ DriverEntry driver_entry;
+ driver_entry.guid = entry.guid;
+ driver_->AddTestData(std::vector<DriverEntry>{driver_entry});
+
+ controller_->CancelDownload(entry.guid);
+ EXPECT_EQ(nullptr, model_->Get(entry.guid));
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, OnDownloadFailed) {
+ Entry entry = test::BuildBasicEntry();
+ entry.state = Entry::State::ACTIVE;
+ std::vector<Entry> entries = {entry};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*client_,
+ OnDownloadFailed(entry.guid, Client::FailureReason::NETWORK))
+ .Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+
+ DriverEntry driver_entry;
+ driver_entry.guid = entry.guid;
+
+ driver_->NotifyDownloadFailed(driver_entry, FailureType::NOT_RECOVERABLE);
+ EXPECT_EQ(nullptr, model_->Get(entry.guid));
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, RetryOnFailure) {
+ Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ std::vector<Entry> entries = {entry1, entry2};
+
+ DriverEntry dentry1 =
+ BuildDriverEntry(entry1, DriverEntry::State::INTERRUPTED);
+ DriverEntry dentry2 =
+ BuildDriverEntry(entry2, DriverEntry::State::INTERRUPTED);
+ std::vector<DriverEntry> dentries = {dentry1, dentry2};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ // Set up the Controller.
+ device_status_listener_->SetDeviceStatus(
+ DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+ driver_->AddTestData(dentries);
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Test retry on failure.
+ config_->max_retry_count = 4;
+ EXPECT_CALL(*client_, OnDownloadSucceeded(entry1.guid, _, _)).Times(1);
+ base::FilePath path = base::FilePath::FromUTF8Unsafe("123");
+ driver_->NotifyDownloadFailed(dentry1, FailureType::RECOVERABLE);
+ driver_->NotifyDownloadFailed(dentry1, FailureType::RECOVERABLE);
+ driver_->NotifyDownloadSucceeded(dentry1);
+
+ EXPECT_CALL(*client_,
+ OnDownloadFailed(entry2.guid, Client::FailureReason::NETWORK))
+ .Times(1);
+ driver_->NotifyDownloadFailed(dentry2, FailureType::RECOVERABLE);
+ driver_->NotifyDownloadFailed(dentry2, FailureType::RECOVERABLE);
+ driver_->NotifyDownloadFailed(dentry2, FailureType::RECOVERABLE);
+ driver_->NotifyDownloadFailed(dentry2, FailureType::RECOVERABLE);
+ // Failed entry should exist because we retry after a delay.
+ EXPECT_NE(nullptr, model_->Get(entry2.guid));
+
+ task_runner_->RunUntilIdle();
+ // Retry is done, and failed entry should be removed.
+ EXPECT_EQ(nullptr, model_->Get(entry2.guid));
+}
+
+TEST_F(DownloadServiceControllerImplTest, OnDownloadSucceeded) {
+ Entry entry = test::BuildBasicEntry();
+ entry.state = Entry::State::ACTIVE;
+ std::vector<Entry> entries = {entry};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*client_, OnDownloadSucceeded(entry.guid, _, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+
+ DriverEntry driver_entry;
+ driver_entry.guid = entry.guid;
+ driver_entry.bytes_downloaded = 1024;
+ driver_entry.completion_time = base::Time::Now();
+ driver_entry.current_file_path = base::FilePath::FromUTF8Unsafe("123");
+
+ long start_time = 0;
+ EXPECT_CALL(*task_scheduler_,
+ ScheduleTask(DownloadTaskType::CLEANUP_TASK, _, _, _, _))
+ .WillOnce(SaveArg<3>(&start_time));
+ driver_->NotifyDownloadSucceeded(driver_entry);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
+ EXPECT_LE(driver_entry.completion_time + config_->file_keep_alive_time,
+ base::Time::Now() + base::TimeDelta::FromSeconds(start_time));
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, CleanupTaskScheduledAtEarliestTime) {
+ Entry entry1 = test::BuildBasicEntry();
+ entry1.state = Entry::State::ACTIVE;
+ entry1.completion_time = base::Time::Now() - base::TimeDelta::FromMinutes(1);
+ Entry entry2 = test::BuildBasicEntry();
+ entry2.state = Entry::State::COMPLETE;
+ entry2.completion_time = base::Time::Now() - base::TimeDelta::FromMinutes(2);
+ std::vector<Entry> entries = {entry1, entry2};
+
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+
+ DriverEntry driver_entry;
+ driver_entry.guid = entry1.guid;
+ driver_entry.bytes_downloaded = 1024;
+ driver_entry.completion_time = base::Time::Now();
+ driver_entry.current_file_path = base::FilePath::FromUTF8Unsafe("123");
+
+ EXPECT_CALL(*task_scheduler_, ScheduleTask(DownloadTaskType::CLEANUP_TASK,
+ false, false, 480, 780))
+ .Times(1);
+ driver_->NotifyDownloadSucceeded(driver_entry);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry1.guid)->state);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, OnDownloadUpdated) {
+ Entry entry = test::BuildBasicEntry();
+ entry.state = Entry::State::ACTIVE;
+ std::vector<Entry> entries = {entry};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+
+ DriverEntry driver_entry;
+ driver_entry.state = DriverEntry::State::IN_PROGRESS;
+ driver_entry.guid = entry.guid;
+ driver_entry.bytes_downloaded = 1024;
+
+ EXPECT_CALL(*client_,
+ OnDownloadUpdated(entry.guid, driver_entry.bytes_downloaded));
+ driver_->NotifyDownloadUpdate(driver_entry);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry.guid)->state);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, DownloadCompletionTest) {
+ // TODO(dtrainor): Simulate a UNKNOWN once that is supported.
+
+ Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ Entry entry3 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ Entry entry4 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ entry4.scheduling_params.cancel_time = base::Time::Now();
+
+ DriverEntry dentry1 =
+ BuildDriverEntry(entry1, DriverEntry::State::IN_PROGRESS);
+ // dentry2 will effectively be created by the test to simulate a start
+ // download.
+ DriverEntry dentry3 =
+ BuildDriverEntry(entry3, DriverEntry::State::IN_PROGRESS);
+
+ std::vector<Entry> entries = {entry1, entry2, entry3, entry4};
+ std::vector<DriverEntry> dentries = {dentry1, dentry3};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ // Test FailureReason::TIMEDOUT.
+ EXPECT_CALL(*client_,
+ OnDownloadFailed(entry4.guid, Client::FailureReason::TIMEDOUT))
+ .Times(1);
+
+ // Set up the Controller.
+ driver_->AddTestData(dentries);
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // Test FailureReason::CANCELLED.
+ EXPECT_CALL(*client_,
+ OnDownloadFailed(entry1.guid, Client::FailureReason::CANCELLED))
+ .Times(1);
+ controller_->CancelDownload(entry1.guid);
+
+ // Test FailureReason::ABORTED.
+ EXPECT_CALL(*client_, OnDownloadStarted(entry2.guid, _, _))
+ .Times(1)
+ .WillOnce(Return(Client::ShouldDownload::ABORT));
+ EXPECT_CALL(*client_,
+ OnDownloadFailed(entry2.guid, Client::FailureReason::ABORTED))
+ .Times(1);
+ driver_->Start(RequestParams(), entry2.guid, entry2.target_file_path,
+ NO_TRAFFIC_ANNOTATION_YET);
+
+ // Test FailureReason::NETWORK.
+ EXPECT_CALL(*client_,
+ OnDownloadFailed(entry3.guid, Client::FailureReason::NETWORK))
+ .Times(1);
+ driver_->NotifyDownloadFailed(dentry3, FailureType::NOT_RECOVERABLE);
+
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, StartupRecovery) {
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ std::vector<Entry> entries;
+ std::vector<DriverEntry> driver_entries;
+ entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+ entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+ entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::NEW));
+
+ entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+ entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+ entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::AVAILABLE));
+
+ entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+ entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+ entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::ACTIVE));
+
+ entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+ entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+ entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::PAUSED));
+
+ entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::IN_PROGRESS));
+ entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::COMPLETE));
+ entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::CANCELLED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+ driver_entries.push_back(
+ BuildDriverEntry(entries.back(), DriverEntry::State::INTERRUPTED));
+ entries.push_back(test::BuildBasicEntry(Entry::State::COMPLETE));
+
+ // Set up the Controller.
+ device_status_listener_->SetDeviceStatus(
+ DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+ InitializeController();
+ driver_->AddTestData(driver_entries);
+ driver_->MakeReady();
+ store_->AutomaticallyTriggerAllFutureCallbacks(true);
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+
+ // Allow the initialization routines and persistent layers to do their thing.
+ task_runner_->RunUntilIdle();
+
+ // Validate Model and DownloadDriver states.
+ // Note that we are accessing the Model instead of the Store here to make it
+ // easier to query the states.
+ // TODO(dtrainor): Check more of the DriverEntry state to validate that the
+ // entries are either paused or resumed accordingly.
+
+ // Entry::State::NEW.
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[0].guid)->state);
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[1].guid)->state);
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[2].guid)->state);
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[3].guid)->state);
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[4].guid)->state);
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[0].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[1].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[2].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[3].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[4].guid));
+
+ // Entry::State::AVAILABLE.
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[5].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[6].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[7].guid)->state);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[8].guid)->state);
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entries[9].guid)->state);
+ EXPECT_NE(base::nullopt, driver_->Find(entries[5].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[6].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[7].guid));
+ EXPECT_NE(base::nullopt, driver_->Find(entries[8].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[9].guid));
+
+ // Entry::State::ACTIVE.
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[10].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[11].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[12].guid)->state);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[13].guid)->state);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entries[14].guid)->state);
+ EXPECT_NE(base::nullopt, driver_->Find(entries[10].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[11].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[12].guid));
+ EXPECT_NE(base::nullopt, driver_->Find(entries[13].guid));
+ EXPECT_NE(base::nullopt, driver_->Find(entries[14].guid));
+
+ // Entry::State::PAUSED.
+ EXPECT_EQ(Entry::State::PAUSED, model_->Get(entries[15].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[16].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[17].guid)->state);
+ EXPECT_EQ(Entry::State::PAUSED, model_->Get(entries[18].guid)->state);
+ EXPECT_EQ(Entry::State::PAUSED, model_->Get(entries[19].guid)->state);
+ EXPECT_NE(base::nullopt, driver_->Find(entries[15].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[16].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[17].guid));
+ EXPECT_NE(base::nullopt, driver_->Find(entries[18].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[19].guid));
+
+ // prog, comp, canc, int, __
+ // Entry::State::COMPLETE.
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[20].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[21].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[22].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[23].guid)->state);
+ EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entries[24].guid)->state);
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[20].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[21].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[22].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[23].guid));
+ EXPECT_EQ(base::nullopt, driver_->Find(entries[24].guid));
+}
+
+TEST_F(DownloadServiceControllerImplTest, ExistingExternalDownload) {
+ Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ Entry entry3 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ entry3.scheduling_params.priority = SchedulingParams::Priority::UI;
+
+ // Simulate an existing download the service knows about and one it does not.
+ DriverEntry dentry1 =
+ BuildDriverEntry(entry2, DriverEntry::State::IN_PROGRESS);
+ DriverEntry dentry2;
+ dentry2.guid = base::GenerateGUID();
+ dentry2.state = DriverEntry::State::IN_PROGRESS;
+
+ std::vector<Entry> entries = {entry1, entry2, entry3};
+ std::vector<DriverEntry> dentries = {dentry1, dentry2};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ // Set up the Controller.
+ device_status_listener_->SetDeviceStatus(
+ DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+ driver_->AddTestData(dentries);
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry1.guid)->state);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry2.guid)->state);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry3.guid)->state);
+
+ EXPECT_FALSE(driver_->Find(entry1.guid).has_value());
+
+ EXPECT_TRUE(driver_->Find(entry2.guid).has_value());
+ EXPECT_TRUE(driver_->Find(entry2.guid).value().paused);
+
+ EXPECT_TRUE(driver_->Find(entry3.guid).has_value());
+ EXPECT_FALSE(driver_->Find(entry3.guid).value().paused);
+
+ // Simulate a successful external download.
+ driver_->NotifyDownloadSucceeded(dentry2);
+
+ EXPECT_TRUE(driver_->Find(entry1.guid).has_value());
+ EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry3.guid).value().paused);
+}
+
+TEST_F(DownloadServiceControllerImplTest, NewExternalDownload) {
+ Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ entry2.scheduling_params.priority = SchedulingParams::Priority::UI;
+
+ DriverEntry dentry1 =
+ BuildDriverEntry(entry2, DriverEntry::State::IN_PROGRESS);
+
+ std::vector<Entry> entries = {entry1, entry2};
+ std::vector<DriverEntry> dentries = {dentry1};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ // Set up the Controller.
+ device_status_listener_->SetDeviceStatus(
+ DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+ driver_->AddTestData(dentries);
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry1.guid)->state);
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry2.guid)->state);
+
+ EXPECT_TRUE(driver_->Find(entry1.guid).has_value());
+ EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_TRUE(driver_->Find(entry2.guid).has_value());
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+ DriverEntry dentry2;
+ dentry2.guid = base::GenerateGUID();
+ dentry2.state = DriverEntry::State::IN_PROGRESS;
+
+ // Simulate a newly created external download.
+ driver_->Start(RequestParams(), dentry2.guid, dentry2.current_file_path,
+ NO_TRAFFIC_ANNOTATION_YET);
+
+ EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+ // Simulate a paused external download.
+ dentry2.paused = true;
+ driver_->NotifyDownloadUpdate(dentry2);
+
+ EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+ // Simulate a resumed external download.
+ dentry2.paused = false;
+ driver_->NotifyDownloadUpdate(dentry2);
+
+ EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+ // Simulate a failed external download.
+ dentry2.state = DriverEntry::State::INTERRUPTED;
+ driver_->NotifyDownloadFailed(dentry2, FailureType::RECOVERABLE);
+
+ EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+ // Rebuild the download so we can simulate more.
+ dentry2.state = DriverEntry::State::IN_PROGRESS;
+ driver_->Start(RequestParams(), dentry2.guid, dentry2.current_file_path,
+ NO_TRAFFIC_ANNOTATION_YET);
+
+ EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+
+ // Simulate a successful external download.
+ dentry2.state = DriverEntry::State::COMPLETE;
+ driver_->NotifyDownloadSucceeded(dentry2);
+
+ EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
+ EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
+}
+
+TEST_F(DownloadServiceControllerImplTest, CancelTimeTest) {
+ Entry entry1 = test::BuildBasicEntry();
+ entry1.state = Entry::State::ACTIVE;
+ entry1.create_time = base::Time::Now() - base::TimeDelta::FromSeconds(10);
+ entry1.scheduling_params.cancel_time =
+ base::Time::Now() - base::TimeDelta::FromSeconds(5);
+
+ Entry entry2 = test::BuildBasicEntry();
+ entry2.state = Entry::State::COMPLETE;
+ entry2.create_time = base::Time::Now() - base::TimeDelta::FromSeconds(10);
+ entry2.scheduling_params.cancel_time =
+ base::Time::Now() - base::TimeDelta::FromSeconds(2);
+ std::vector<Entry> entries = {entry1, entry2};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ // At startup, timed out entries should be killed.
+ std::vector<Entry*> updated_entries = model_->PeekEntries();
+ EXPECT_EQ(1u, updated_entries.size());
+}
+
+// Ensures no more downloads are activated if the number of downloads exceeds
+// the max running download configuration.
+TEST_F(DownloadServiceControllerImplTest, ThrottlingConfigMaxRunning) {
+ Entry entry1 = test::BuildBasicEntry(Entry::State::AVAILABLE);
+ Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+ std::vector<Entry> entries = {entry1, entry2};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ // Setup the Configuration.
+ config_->max_concurrent_downloads = 1u;
+ config_->max_running_downloads = 1u;
+
+ // Setup the controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ store_->AutomaticallyTriggerAllFutureCallbacks(true);
+
+ // Hit the max running configuration threshold, nothing should be called.
+ EXPECT_CALL(*scheduler_, Next(_, _)).Times(0);
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entry1.guid)->state);
+}
+
+// Ensures max concurrent download configuration considers both active and
+// paused downloads.
+TEST_F(DownloadServiceControllerImplTest, ThrottlingConfigMaxConcurrent) {
+ Entry entry1 = test::BuildBasicEntry(Entry::State::AVAILABLE);
+ Entry entry2 = test::BuildBasicEntry(Entry::State::AVAILABLE);
+ Entry entry3 = test::BuildBasicEntry(Entry::State::PAUSED);
+ std::vector<Entry> entries = {entry1, entry2, entry3};
+
+ EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+ // Setup the Configuration.
+ config_->max_concurrent_downloads = 2u;
+ config_->max_running_downloads = 1u;
+
+ // Setup the controller.
+ InitializeController();
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ file_monitor_->TriggerInit(true);
+ store_->AutomaticallyTriggerAllFutureCallbacks(true);
+
+ // Can have one more download due to max concurrent configuration.
+ testing::InSequence seq;
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entry1.guid)->state);
+ EXPECT_CALL(*scheduler_, Next(_, _))
+ .Times(1)
+ .WillOnce(Return(model_->Get(entry1.guid)))
+ .RetiresOnSaturation();
+ // |scheduler_| will poll entry2 on next time, but it should not change the
+ // state of entry2 due to max running download configuration.
+ ON_CALL(*scheduler_, Next(_, _))
+ .WillByDefault(Return(model_->Get(entry2.guid)));
+
+ EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+ driver_->MakeReady();
+ task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry1.guid)->state);
+ EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entry2.guid)->state);
+ EXPECT_EQ(Entry::State::PAUSED, model_->Get(entry3.guid)->state);
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/download_driver.h b/chromium/components/download/internal/download_driver.h
index 88247d18d5d..40bac08f14d 100644
--- a/chromium/components/download/internal/download_driver.h
+++ b/chromium/components/download/internal/download_driver.h
@@ -5,10 +5,12 @@
#ifndef COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_DRIVER_H_
#define COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_DRIVER_H_
+#include <set>
#include <string>
#include "base/optional.h"
#include "components/download/internal/driver_entry.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
namespace base {
class FilePath;
@@ -16,7 +18,16 @@ class FilePath;
namespace download {
-struct DownloadParams;
+struct RequestParams;
+
+// Indicates the recovery type of a download.
+enum class FailureType {
+ // Download failed due to an irrecoverable error.
+ NOT_RECOVERABLE = 0,
+
+ // Download failed, but we might be able to recover if we try again.
+ RECOVERABLE = 1,
+};
// The interface that includes all the operations to interact with low level
// download library functionalities.
@@ -31,35 +42,49 @@ class DownloadDriver {
// when the low level download library is ready.
virtual void OnDriverReady(bool success) = 0;
+ // Called asynchronously in response to a DownloadDriver::HardRecover call.
+ // If |success| is |false|, recovery of the DownloadDriver failed.
+ virtual void OnDriverHardRecoverComplete(bool success) = 0;
+
// Called when any download is created.
virtual void OnDownloadCreated(const DriverEntry& download) = 0;
// Called when any download is failed. |reason| is propagated from low level
// download library.
- virtual void OnDownloadFailed(const DriverEntry& download, int reason) = 0;
+ virtual void OnDownloadFailed(const DriverEntry& download,
+ FailureType failure_type) = 0;
// Called when any download is successfully completed.
- virtual void OnDownloadSucceeded(const DriverEntry& download,
- const base::FilePath& path) = 0;
+ virtual void OnDownloadSucceeded(const DriverEntry& download) = 0;
// Called when any download is updated.
virtual void OnDownloadUpdated(const DriverEntry& download) = 0;
};
+ virtual ~DownloadDriver() = default;
+
// Initialize the driver to receive download updates.
virtual void Initialize(Client* client) = 0;
- // Returns if the driver is ready. Returns false when the driver is not
- // initialized by the client, or low level download library has been shut
- // down.
+ // Attempts to clean up and reset the DownloadDriver. It should remove all
+ // state relevant to the DownloadService.
+ virtual void HardRecover() = 0;
+
+ // Returns if the driver is ready after the low level library has loaded all
+ // the data. Returns false when the driver is not initialized by the client,
+ // or low level download library has been shut down.
virtual bool IsReady() const = 0;
// Starts a new download.
- virtual void Start(const DownloadParams& params) = 0;
+ virtual void Start(
+ const RequestParams& request_params,
+ const std::string& guid,
+ const base::FilePath& file_path,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation) = 0;
// Cancels an existing download, all data associated with this download should
// be removed.
- virtual void Cancel(const std::string& guid) = 0;
+ virtual void Remove(const std::string& guid) = 0;
// Pauses the download.
virtual void Pause(const std::string& guid) = 0;
@@ -67,8 +92,12 @@ class DownloadDriver {
// Resumes the download
virtual void Resume(const std::string& guid) = 0;
- // Find a download record from low level download library.
+ // Finds a download record from low level download library.
virtual base::Optional<DriverEntry> Find(const std::string& guid) = 0;
+
+ // Called to query the current set of active downloads. This doesn't
+ // necessarily mean downloads started by the service.
+ virtual std::set<std::string> GetActiveDownloads() = 0;
};
} // namespace download
diff --git a/chromium/components/download/internal/download_service_impl.cc b/chromium/components/download/internal/download_service_impl.cc
index 44610a9a208..ceff12d729f 100644
--- a/chromium/components/download/internal/download_service_impl.cc
+++ b/chromium/components/download/internal/download_service_impl.cc
@@ -4,27 +4,158 @@
#include "components/download/internal/download_service_impl.h"
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+#include "components/download/internal/controller.h"
+#include "components/download/internal/startup_status.h"
+#include "components/download/internal/stats.h"
+
namespace download {
-// static
-DownloadService* DownloadService::Create(
- const base::FilePath& storage_dir,
- const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) {
- return new DownloadServiceImpl(Configuration::CreateFromFinch());
+DownloadServiceImpl::DownloadServiceImpl(std::unique_ptr<Configuration> config,
+ std::unique_ptr<Controller> controller)
+ : config_(std::move(config)),
+ controller_(std::move(controller)),
+ service_config_(config_.get()),
+ startup_completed_(false) {
+ controller_->Initialize(base::Bind(
+ &DownloadServiceImpl::OnControllerInitialized, base::Unretained(this)));
}
-DownloadServiceImpl::DownloadServiceImpl(std::unique_ptr<Configuration> config)
- : config_(std::move(config)) {}
-
DownloadServiceImpl::~DownloadServiceImpl() = default;
+const ServiceConfig& DownloadServiceImpl::GetConfig() {
+ return service_config_;
+}
+
+void DownloadServiceImpl::OnStartScheduledTask(
+ DownloadTaskType task_type,
+ const TaskFinishedCallback& callback) {
+ if (startup_completed_) {
+ controller_->OnStartScheduledTask(task_type, callback);
+ return;
+ }
+
+ pending_tasks_[task_type] =
+ base::Bind(&Controller::OnStartScheduledTask,
+ base::Unretained(controller_.get()), task_type, callback);
+}
+
+bool DownloadServiceImpl::OnStopScheduledTask(DownloadTaskType task_type) {
+ if (startup_completed_) {
+ return controller_->OnStopScheduledTask(task_type);
+ }
+
+ auto iter = pending_tasks_.find(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();
+ pending_tasks_.erase(iter);
+ }
+
+ return true;
+}
+
+DownloadService::ServiceStatus DownloadServiceImpl::GetStatus() {
+ switch (controller_->GetState()) {
+ case Controller::State::CREATED: // Intentional fallthrough.
+ case Controller::State::INITIALIZING: // Intentional fallthrough.
+ case Controller::State::RECOVERING:
+ return DownloadService::ServiceStatus::STARTING_UP;
+ case Controller::State::READY:
+ return DownloadService::ServiceStatus::READY;
+ case Controller::State::UNAVAILABLE: // Intentional fallthrough.
+ default:
+ return DownloadService::ServiceStatus::UNAVAILABLE;
+ }
+}
+
void DownloadServiceImpl::StartDownload(const DownloadParams& download_params) {
+ stats::LogServiceApiAction(download_params.client,
+ stats::ServiceApiAction::START_DOWNLOAD);
+ DCHECK_EQ(download_params.guid, base::ToUpperASCII(download_params.guid));
+ stats::LogDownloadParams(download_params);
+ if (startup_completed_) {
+ controller_->StartDownload(download_params);
+ } else {
+ pending_actions_.push_back(base::Bind(&Controller::StartDownload,
+ base::Unretained(controller_.get()),
+ download_params));
+ }
}
-void DownloadServiceImpl::PauseDownload(const std::string& guid) {}
-void DownloadServiceImpl::ResumeDownload(const std::string& guid) {}
-void DownloadServiceImpl::CancelDownload(const std::string& guid) {}
+
+void DownloadServiceImpl::PauseDownload(const std::string& guid) {
+ stats::LogServiceApiAction(controller_->GetOwnerOfDownload(guid),
+ stats::ServiceApiAction::PAUSE_DOWNLOAD);
+ DCHECK_EQ(guid, base::ToUpperASCII(guid));
+
+ if (startup_completed_) {
+ controller_->PauseDownload(guid);
+ } else {
+ pending_actions_.push_back(base::Bind(
+ &Controller::PauseDownload, base::Unretained(controller_.get()), guid));
+ }
+}
+
+void DownloadServiceImpl::ResumeDownload(const std::string& guid) {
+ stats::LogServiceApiAction(controller_->GetOwnerOfDownload(guid),
+ stats::ServiceApiAction::RESUME_DOWNLOAD);
+ DCHECK_EQ(guid, base::ToUpperASCII(guid));
+
+ if (startup_completed_) {
+ controller_->ResumeDownload(guid);
+ } else {
+ pending_actions_.push_back(base::Bind(&Controller::ResumeDownload,
+ base::Unretained(controller_.get()),
+ guid));
+ }
+}
+
+void DownloadServiceImpl::CancelDownload(const std::string& guid) {
+ stats::LogServiceApiAction(controller_->GetOwnerOfDownload(guid),
+ stats::ServiceApiAction::CANCEL_DOWNLOAD);
+ DCHECK_EQ(guid, base::ToUpperASCII(guid));
+
+ if (startup_completed_) {
+ controller_->CancelDownload(guid);
+ } else {
+ pending_actions_.push_back(base::Bind(&Controller::CancelDownload,
+ base::Unretained(controller_.get()),
+ guid));
+ }
+}
+
void DownloadServiceImpl::ChangeDownloadCriteria(
const std::string& guid,
- const SchedulingParams& params) {}
+ const SchedulingParams& params) {
+ stats::LogServiceApiAction(controller_->GetOwnerOfDownload(guid),
+ stats::ServiceApiAction::CHANGE_CRITERIA);
+ DCHECK_EQ(guid, base::ToUpperASCII(guid));
+
+ if (startup_completed_) {
+ controller_->ChangeDownloadCriteria(guid, params);
+ } else {
+ pending_actions_.push_back(base::Bind(&Controller::ChangeDownloadCriteria,
+ base::Unretained(controller_.get()),
+ guid, params));
+ }
+}
+
+void DownloadServiceImpl::OnControllerInitialized() {
+ while (!pending_actions_.empty()) {
+ auto callback = pending_actions_.front();
+ callback.Run();
+ pending_actions_.pop_front();
+ }
+
+ while (!pending_tasks_.empty()) {
+ auto iter = pending_tasks_.begin();
+ iter->second.Run();
+ pending_tasks_.erase(iter);
+ }
+
+ startup_completed_ = true;
+}
} // namespace download
diff --git a/chromium/components/download/internal/download_service_impl.h b/chromium/components/download/internal/download_service_impl.h
index 319aea1976b..0333f9d0b1f 100644
--- a/chromium/components/download/internal/download_service_impl.h
+++ b/chromium/components/download/internal/download_service_impl.h
@@ -5,25 +5,35 @@
#ifndef COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_SERVICE_IMPL_H_
#define COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_SERVICE_IMPL_H_
+#include <deque>
+#include <map>
#include <memory>
#include <string>
#include "base/macros.h"
#include "components/download/internal/config.h"
+#include "components/download/internal/service_config_impl.h"
#include "components/download/public/download_service.h"
namespace download {
+class Controller;
struct DownloadParams;
struct SchedulingParams;
// The internal implementation of the DownloadService.
class DownloadServiceImpl : public DownloadService {
public:
- DownloadServiceImpl(std::unique_ptr<Configuration> config);
+ DownloadServiceImpl(std::unique_ptr<Configuration> config,
+ std::unique_ptr<Controller> controller);
~DownloadServiceImpl() override;
// DownloadService implementation.
+ const ServiceConfig& GetConfig() override;
+ void OnStartScheduledTask(DownloadTaskType task_type,
+ const TaskFinishedCallback& callback) override;
+ bool OnStopScheduledTask(DownloadTaskType task_type) override;
+ ServiceStatus GetStatus() override;
void StartDownload(const DownloadParams& download_params) override;
void PauseDownload(const std::string& guid) override;
void ResumeDownload(const std::string& guid) override;
@@ -32,8 +42,19 @@ class DownloadServiceImpl : public DownloadService {
const SchedulingParams& params) override;
private:
+ void OnControllerInitialized();
+
+ // config_ needs to be destructed after controller_ and service_config_ which
+ // hold onto references to it.
std::unique_ptr<Configuration> config_;
+ std::unique_ptr<Controller> controller_;
+ ServiceConfigImpl service_config_;
+
+ std::deque<base::Closure> pending_actions_;
+ std::map<DownloadTaskType, base::Closure> pending_tasks_;
+ bool startup_completed_;
+
DISALLOW_COPY_AND_ASSIGN(DownloadServiceImpl);
};
diff --git a/chromium/components/download/internal/download_service_impl_unittest.cc b/chromium/components/download/internal/download_service_impl_unittest.cc
new file mode 100644
index 00000000000..74090dfbca8
--- /dev/null
+++ b/chromium/components/download/internal/download_service_impl_unittest.cc
@@ -0,0 +1,145 @@
+// 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/download/internal/download_service_impl.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/internal/startup_status.h"
+#include "components/download/internal/stats.h"
+#include "components/download/internal/test/download_params_utils.h"
+#include "components/download/internal/test/mock_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Return;
+
+namespace download {
+namespace {
+
+class DownloadServiceImplTest : public testing::Test {
+ public:
+ DownloadServiceImplTest()
+ : controller_(nullptr),
+ task_runner_(new base::TestSimpleTaskRunner),
+ handle_(task_runner_) {}
+ ~DownloadServiceImplTest() override = default;
+
+ void SetUp() override {
+ auto config = base::MakeUnique<Configuration>();
+ auto controller = base::MakeUnique<test::MockController>();
+ controller_ = controller.get();
+ service_ = base::MakeUnique<DownloadServiceImpl>(std::move(config),
+ std::move(controller));
+ }
+
+ protected:
+ test::MockController* controller_;
+ std::unique_ptr<DownloadServiceImpl> service_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadServiceImplTest);
+};
+
+} // namespace
+
+TEST_F(DownloadServiceImplTest, TestGetStatus) {
+ StartupStatus startup_status;
+ EXPECT_CALL(*controller_, GetState())
+ .WillOnce(Return(Controller::State::INITIALIZING))
+ .WillOnce(Return(Controller::State::READY))
+ .WillOnce(Return(Controller::State::UNAVAILABLE));
+
+ EXPECT_EQ(DownloadService::ServiceStatus::STARTING_UP, service_->GetStatus());
+ EXPECT_EQ(DownloadService::ServiceStatus::READY, service_->GetStatus());
+ EXPECT_EQ(DownloadService::ServiceStatus::UNAVAILABLE, service_->GetStatus());
+}
+
+TEST_F(DownloadServiceImplTest, TestApiPassThrough) {
+ DownloadParams params = test::BuildBasicDownloadParams();
+ // TODO(xingliu): Remove the limitation of upper case guid in
+ // |download_params|, see http://crbug.com/734818.
+ params.guid = base::ToUpperASCII(params.guid);
+ SchedulingParams scheduling_params;
+ scheduling_params.priority = SchedulingParams::Priority::UI;
+
+ EXPECT_CALL(*controller_, GetOwnerOfDownload(_))
+ .WillRepeatedly(Return(DownloadClient::TEST));
+
+ EXPECT_CALL(*controller_, StartDownload(_)).Times(0);
+ EXPECT_CALL(*controller_, PauseDownload(params.guid)).Times(0);
+ EXPECT_CALL(*controller_, ResumeDownload(params.guid)).Times(0);
+ EXPECT_CALL(*controller_, CancelDownload(params.guid)).Times(0);
+ EXPECT_CALL(*controller_, ChangeDownloadCriteria(params.guid, _)).Times(0);
+
+ {
+ base::HistogramTester histogram_tester;
+
+ service_->StartDownload(params);
+
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.Request.ClientAction",
+ static_cast<base::HistogramBase::Sample>(
+ stats::ServiceApiAction::START_DOWNLOAD),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.Request.ClientAction.__Test__",
+ static_cast<base::HistogramBase::Sample>(
+ stats::ServiceApiAction::START_DOWNLOAD),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Download.Service.Request.BatteryRequirement",
+ static_cast<base::HistogramBase::Sample>(
+ params.scheduling_params.battery_requirements),
+ 1);
+
+ histogram_tester.ExpectTotalCount("Download.Service.Request.ClientAction",
+ 1);
+ histogram_tester.ExpectTotalCount(
+ "Download.Service.Request.ClientAction.__Test__", 1);
+ }
+ service_->PauseDownload(params.guid);
+ service_->ResumeDownload(params.guid);
+ service_->CancelDownload(params.guid);
+ service_->ChangeDownloadCriteria(params.guid, scheduling_params);
+ task_runner_->RunUntilIdle();
+
+ testing::Sequence seq1;
+ EXPECT_CALL(*controller_, StartDownload(_)).Times(1).InSequence(seq1);
+ EXPECT_CALL(*controller_, PauseDownload(params.guid))
+ .Times(1)
+ .InSequence(seq1);
+ EXPECT_CALL(*controller_, ResumeDownload(params.guid))
+ .Times(1)
+ .InSequence(seq1);
+ EXPECT_CALL(*controller_, CancelDownload(params.guid))
+ .Times(1)
+ .InSequence(seq1);
+ EXPECT_CALL(*controller_, ChangeDownloadCriteria(params.guid, _))
+ .Times(1)
+ .InSequence(seq1);
+
+ controller_->TriggerInitCompleted();
+ task_runner_->RunUntilIdle();
+
+ EXPECT_CALL(*controller_, PauseDownload(params.guid))
+ .Times(1)
+ .InSequence(seq1);
+ EXPECT_CALL(*controller_, ResumeDownload(params.guid))
+ .Times(1)
+ .InSequence(seq1);
+ service_->PauseDownload(params.guid);
+ service_->ResumeDownload(params.guid);
+ task_runner_->RunUntilIdle();
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/download_store.cc b/chromium/components/download/internal/download_store.cc
new file mode 100644
index 00000000000..877547846df
--- /dev/null
+++ b/chromium/components/download/internal/download_store.cc
@@ -0,0 +1,113 @@
+// 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/download/internal/download_store.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/proto/entry.pb.h"
+#include "components/download/internal/proto_conversions.h"
+#include "components/leveldb_proto/proto_database_impl.h"
+
+namespace download {
+
+namespace {
+
+const char kDatabaseClientName[] = "DownloadService";
+using KeyVector = std::vector<std::string>;
+using ProtoEntryVector = std::vector<protodb::Entry>;
+using KeyProtoEntryVector = std::vector<std::pair<std::string, protodb::Entry>>;
+
+} // namespace
+
+DownloadStore::DownloadStore(
+ const base::FilePath& database_dir,
+ std::unique_ptr<leveldb_proto::ProtoDatabase<protodb::Entry>> db)
+ : db_(std::move(db)),
+ database_dir_(database_dir),
+ is_initialized_(false),
+ weak_factory_(this) {}
+
+DownloadStore::~DownloadStore() = default;
+
+bool DownloadStore::IsInitialized() {
+ return is_initialized_;
+}
+
+void DownloadStore::Initialize(InitCallback callback) {
+ DCHECK(!IsInitialized());
+ db_->InitWithOptions(
+ kDatabaseClientName, leveldb_proto::Options(database_dir_),
+ base::BindOnce(&DownloadStore::OnDatabaseInited,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void DownloadStore::HardRecover(StoreCallback callback) {
+ is_initialized_ = false;
+ db_->Destroy(base::BindOnce(&DownloadStore::OnDatabaseDestroyed,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void DownloadStore::OnDatabaseInited(InitCallback callback, bool success) {
+ if (!success) {
+ std::move(callback).Run(success, base::MakeUnique<std::vector<Entry>>());
+ return;
+ }
+
+ db_->LoadEntries(base::BindOnce(&DownloadStore::OnDatabaseLoaded,
+ weak_factory_.GetWeakPtr(),
+ std::move(callback)));
+}
+
+void DownloadStore::OnDatabaseLoaded(InitCallback callback,
+ bool success,
+ std::unique_ptr<ProtoEntryVector> protos) {
+ if (!success) {
+ std::move(callback).Run(success, base::MakeUnique<std::vector<Entry>>());
+ return;
+ }
+
+ auto entries = ProtoConversions::EntryVectorFromProto(std::move(protos));
+ is_initialized_ = true;
+ std::move(callback).Run(success, std::move(entries));
+}
+
+void DownloadStore::OnDatabaseDestroyed(StoreCallback callback, bool success) {
+ if (!success) {
+ std::move(callback).Run(success);
+ return;
+ }
+
+ db_->InitWithOptions(
+ kDatabaseClientName, leveldb_proto::Options(database_dir_),
+ base::BindOnce(&DownloadStore::OnDatabaseInitedAfterDestroy,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void DownloadStore::OnDatabaseInitedAfterDestroy(StoreCallback callback,
+ bool success) {
+ is_initialized_ = success;
+ std::move(callback).Run(success);
+}
+
+void DownloadStore::Update(const Entry& entry, StoreCallback callback) {
+ DCHECK(IsInitialized());
+ auto entries_to_save = base::MakeUnique<KeyProtoEntryVector>();
+ protodb::Entry proto = ProtoConversions::EntryToProto(entry);
+ entries_to_save->emplace_back(entry.guid, std::move(proto));
+ db_->UpdateEntries(std::move(entries_to_save), base::MakeUnique<KeyVector>(),
+ std::move(callback));
+}
+
+void DownloadStore::Remove(const std::string& guid, StoreCallback callback) {
+ DCHECK(IsInitialized());
+ auto keys_to_remove = base::MakeUnique<KeyVector>();
+ keys_to_remove->push_back(guid);
+ db_->UpdateEntries(base::MakeUnique<KeyProtoEntryVector>(),
+ std::move(keys_to_remove), std::move(callback));
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/download_store.h b/chromium/components/download/internal/download_store.h
new file mode 100644
index 00000000000..adb397b6b3c
--- /dev/null
+++ b/chromium/components/download/internal/download_store.h
@@ -0,0 +1,60 @@
+// 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_INTERNAL_DOWNLOAD_STORE_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_STORE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/internal/store.h"
+#include "components/leveldb_proto/proto_database.h"
+
+namespace protodb {
+class Entry;
+} // namespace protodb
+
+namespace download {
+
+// DownloadStore provides a layer around a LevelDB proto database that persists
+// the download request, scheduling params and metadata to the disk. The data is
+// read during initialization and presented to the caller after converting to
+// Entry entries.
+class DownloadStore : public Store {
+ public:
+ DownloadStore(
+ const base::FilePath& database_dir,
+ std::unique_ptr<leveldb_proto::ProtoDatabase<protodb::Entry>> db);
+ ~DownloadStore() override;
+
+ // Store implementation.
+ bool IsInitialized() override;
+ void Initialize(InitCallback callback) override;
+ void HardRecover(StoreCallback callback) override;
+ void Update(const Entry& entry, StoreCallback callback) override;
+ void Remove(const std::string& guid, StoreCallback callback) override;
+
+ private:
+ void OnDatabaseInited(InitCallback callback, bool success);
+ void OnDatabaseLoaded(InitCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<protodb::Entry>> protos);
+ void OnDatabaseDestroyed(StoreCallback callback, bool success);
+ void OnDatabaseInitedAfterDestroy(StoreCallback callback, bool success);
+
+ std::unique_ptr<leveldb_proto::ProtoDatabase<protodb::Entry>> db_;
+ base::FilePath database_dir_;
+ bool is_initialized_;
+
+ base::WeakPtrFactory<DownloadStore> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadStore);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_DOWNLOAD_STORE_H_
diff --git a/chromium/components/download/internal/download_store_unittest.cc b/chromium/components/download/internal/download_store_unittest.cc
new file mode 100644
index 00000000000..57b9997d554
--- /dev/null
+++ b/chromium/components/download/internal/download_store_unittest.cc
@@ -0,0 +1,324 @@
+// 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/download/internal/download_store.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/guid.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/proto/entry.pb.h"
+#include "components/download/internal/proto_conversions.h"
+#include "components/download/internal/test/entry_utils.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 {
+
+class DownloadStoreTest : public testing::Test {
+ public:
+ DownloadStoreTest() : db_(nullptr) {}
+
+ ~DownloadStoreTest() override = default;
+
+ void CreateDatabase() {
+ auto db = base::MakeUnique<leveldb_proto::test::FakeDB<protodb::Entry>>(
+ &db_entries_);
+ db_ = db.get();
+ store_.reset(new DownloadStore(
+ base::FilePath(FILE_PATH_LITERAL("/test/db/fakepath")), std::move(db)));
+ }
+
+ void InitCallback(std::vector<Entry>* loaded_entries,
+ bool success,
+ std::unique_ptr<std::vector<Entry>> entries) {
+ loaded_entries->swap(*entries);
+ }
+
+ void LoadCallback(std::vector<protodb::Entry>* loaded_entries,
+ bool success,
+ std::unique_ptr<std::vector<protodb::Entry>> entries) {
+ loaded_entries->swap(*entries);
+ }
+
+ void RecoverCallback(bool success) { hard_recover_result_ = success; }
+
+ MOCK_METHOD1(StoreCallback, void(bool));
+
+ void PrepopulateSampleEntries() {
+ Entry item1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry item2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ db_entries_.insert(
+ std::make_pair(item1.guid, ProtoConversions::EntryToProto(item1)));
+ db_entries_.insert(
+ std::make_pair(item2.guid, ProtoConversions::EntryToProto(item2)));
+ }
+
+ protected:
+ std::map<std::string, protodb::Entry> db_entries_;
+ leveldb_proto::test::FakeDB<protodb::Entry>* db_;
+ std::unique_ptr<DownloadStore> store_;
+ base::Optional<bool> hard_recover_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadStoreTest);
+};
+
+TEST_F(DownloadStoreTest, Initialize) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+ ASSERT_FALSE(store_->IsInitialized());
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+
+ ASSERT_TRUE(store_->IsInitialized());
+ ASSERT_EQ(2u, preloaded_entries.size());
+}
+
+TEST_F(DownloadStoreTest, HardRecover) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+ ASSERT_FALSE(store_->IsInitialized());
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+
+ ASSERT_TRUE(store_->IsInitialized());
+ ASSERT_EQ(2u, preloaded_entries.size());
+
+ store_->HardRecover(
+ base::Bind(&DownloadStoreTest::RecoverCallback, base::Unretained(this)));
+
+ ASSERT_FALSE(store_->IsInitialized());
+
+ db_->DestroyCallback(true);
+ db_->InitCallback(true);
+
+ ASSERT_TRUE(store_->IsInitialized());
+ ASSERT_TRUE(hard_recover_result_.has_value());
+ ASSERT_TRUE(hard_recover_result_.value());
+}
+
+TEST_F(DownloadStoreTest, HardRecoverDestroyFails) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+ ASSERT_FALSE(store_->IsInitialized());
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+
+ ASSERT_TRUE(store_->IsInitialized());
+ ASSERT_EQ(2u, preloaded_entries.size());
+
+ store_->HardRecover(
+ base::Bind(&DownloadStoreTest::RecoverCallback, base::Unretained(this)));
+
+ ASSERT_FALSE(store_->IsInitialized());
+
+ db_->DestroyCallback(false);
+
+ ASSERT_FALSE(store_->IsInitialized());
+ ASSERT_TRUE(hard_recover_result_.has_value());
+ ASSERT_FALSE(hard_recover_result_.value());
+}
+
+TEST_F(DownloadStoreTest, HardRecoverInitFails) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+ ASSERT_FALSE(store_->IsInitialized());
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+
+ ASSERT_TRUE(store_->IsInitialized());
+ ASSERT_EQ(2u, preloaded_entries.size());
+
+ store_->HardRecover(
+ base::Bind(&DownloadStoreTest::RecoverCallback, base::Unretained(this)));
+
+ ASSERT_FALSE(store_->IsInitialized());
+
+ db_->DestroyCallback(true);
+ db_->InitCallback(false);
+
+ ASSERT_FALSE(store_->IsInitialized());
+ ASSERT_TRUE(hard_recover_result_.has_value());
+ ASSERT_FALSE(hard_recover_result_.value());
+}
+
+TEST_F(DownloadStoreTest, Update) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_TRUE(store_->IsInitialized());
+ ASSERT_EQ(2u, preloaded_entries.size());
+
+ Entry item1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry item2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ EXPECT_CALL(*this, StoreCallback(true)).Times(2);
+ store_->Update(item1, base::Bind(&DownloadStoreTest::StoreCallback,
+ base::Unretained(this)));
+ db_->UpdateCallback(true);
+ store_->Update(item2, base::Bind(&DownloadStoreTest::StoreCallback,
+ base::Unretained(this)));
+ db_->UpdateCallback(true);
+
+ // Query the database directly and check for the entry.
+ auto protos = base::MakeUnique<std::vector<protodb::Entry>>();
+ db_->LoadEntries(base::Bind(&DownloadStoreTest::LoadCallback,
+ base::Unretained(this), protos.get()));
+ db_->LoadCallback(true);
+ ASSERT_EQ(4u, protos->size());
+ ASSERT_TRUE(test::CompareEntryList(
+ {preloaded_entries[0], preloaded_entries[1], item1, item2},
+ *ProtoConversions::EntryVectorFromProto(std::move(protos))));
+}
+
+TEST_F(DownloadStoreTest, Remove) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_EQ(2u, preloaded_entries.size());
+
+ // Remove the entry.
+ EXPECT_CALL(*this, StoreCallback(true)).Times(1);
+ store_->Remove(
+ preloaded_entries[0].guid,
+ base::Bind(&DownloadStoreTest::StoreCallback, base::Unretained(this)));
+ db_->UpdateCallback(true);
+
+ // Query the database directly and check for the entry removed.
+ auto protos = base::MakeUnique<std::vector<protodb::Entry>>();
+ db_->LoadEntries(base::Bind(&DownloadStoreTest::LoadCallback,
+ base::Unretained(this), protos.get()));
+ db_->LoadCallback(true);
+ ASSERT_EQ(1u, protos->size());
+ ASSERT_TRUE(test::CompareEntryList(
+ {preloaded_entries[1]},
+ *ProtoConversions::EntryVectorFromProto(std::move(protos))));
+}
+
+TEST_F(DownloadStoreTest, InitializeFailed) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(false);
+ ASSERT_FALSE(store_->IsInitialized());
+ ASSERT_TRUE(preloaded_entries.empty());
+}
+
+TEST_F(DownloadStoreTest, InitialLoadFailed) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+
+ std::vector<Entry> preloaded_entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &preloaded_entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(false);
+ ASSERT_FALSE(store_->IsInitialized());
+ ASSERT_TRUE(preloaded_entries.empty());
+}
+
+TEST_F(DownloadStoreTest, UnsuccessfulUpdateOrRemove) {
+ Entry item1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ CreateDatabase();
+
+ std::vector<Entry> entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_TRUE(store_->IsInitialized());
+ ASSERT_TRUE(entries.empty());
+
+ // Update failed.
+ EXPECT_CALL(*this, StoreCallback(false)).Times(1);
+ store_->Update(item1, base::Bind(&DownloadStoreTest::StoreCallback,
+ base::Unretained(this)));
+ db_->UpdateCallback(false);
+
+ // Remove failed.
+ EXPECT_CALL(*this, StoreCallback(false)).Times(1);
+ store_->Remove(item1.guid, base::Bind(&DownloadStoreTest::StoreCallback,
+ base::Unretained(this)));
+ db_->UpdateCallback(false);
+}
+
+TEST_F(DownloadStoreTest, AddThenRemove) {
+ CreateDatabase();
+
+ std::vector<Entry> entries;
+ store_->Initialize(base::Bind(&DownloadStoreTest::InitCallback,
+ base::Unretained(this), &entries));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_TRUE(entries.empty());
+
+ Entry item1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry item2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ EXPECT_CALL(*this, StoreCallback(true)).Times(2);
+ store_->Update(item1, base::Bind(&DownloadStoreTest::StoreCallback,
+ base::Unretained(this)));
+ db_->UpdateCallback(true);
+ store_->Update(item2, base::Bind(&DownloadStoreTest::StoreCallback,
+ base::Unretained(this)));
+ db_->UpdateCallback(true);
+
+ // Query the database directly and check for the entry.
+ auto protos = base::MakeUnique<std::vector<protodb::Entry>>();
+ db_->LoadEntries(base::Bind(&DownloadStoreTest::LoadCallback,
+ base::Unretained(this), protos.get()));
+ db_->LoadCallback(true);
+ ASSERT_EQ(2u, protos->size());
+
+ // Remove the entry.
+ EXPECT_CALL(*this, StoreCallback(true)).Times(1);
+ store_->Remove(item1.guid, base::Bind(&DownloadStoreTest::StoreCallback,
+ base::Unretained(this)));
+ db_->UpdateCallback(true);
+
+ // Query the database directly and check for the entry removed.
+ protos->clear();
+ db_->LoadEntries(base::Bind(&DownloadStoreTest::LoadCallback,
+ base::Unretained(this), protos.get()));
+ db_->LoadCallback(true);
+ ASSERT_EQ(1u, protos->size());
+ ASSERT_TRUE(test::CompareEntryList(
+ {item2}, *ProtoConversions::EntryVectorFromProto(std::move(protos))));
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/driver_entry.h b/chromium/components/download/internal/driver_entry.h
index a21a4312c9e..c7b41632c8d 100644
--- a/chromium/components/download/internal/driver_entry.h
+++ b/chromium/components/download/internal/driver_entry.h
@@ -6,8 +6,11 @@
#define COMPONENTS_DOWNLOAD_INTERNAL_DRIVER_ENTRY_H_
#include <string>
+#include <vector>
+#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
+#include "url/gurl.h"
namespace net {
class HttpResponseHeaders;
@@ -52,8 +55,22 @@ struct DriverEntry {
// http header is not presented.
uint64_t expected_total_size;
+ // The physical file path for the download. It can be different from the
+ // target file path requested while the file is downloading, as it may
+ // download to a temporary path. After completion, this would be set to the
+ // target file path.
+ base::FilePath current_file_path;
+
+ // Time the download was marked as complete, base::Time() if the download is
+ // not yet complete.
+ base::Time completion_time;
+
// The response headers for the most recent download request.
scoped_refptr<const net::HttpResponseHeaders> response_headers;
+
+ // The url chain of the download. Download may encounter redirects, and
+ // fetches the content from the last url in the chain.
+ std::vector<GURL> url_chain;
};
} // namespace download
diff --git a/chromium/components/download/internal/entry.cc b/chromium/components/download/internal/entry.cc
index 56d819c39d8..4df4757a0b0 100644
--- a/chromium/components/download/internal/entry.cc
+++ b/chromium/components/download/internal/entry.cc
@@ -6,8 +6,35 @@
namespace download {
-Entry::Entry() = default;
+Entry::Entry() : attempt_count(0) {}
Entry::Entry(const Entry& other) = default;
+
+Entry::Entry(const DownloadParams& params)
+ : client(params.client),
+ guid(params.guid),
+ create_time(base::Time::Now()),
+ scheduling_params(params.scheduling_params),
+ request_params(params.request_params),
+ attempt_count(0) {}
+
Entry::~Entry() = default;
+bool Entry::operator==(const Entry& other) const {
+ return client == other.client && guid == other.guid &&
+ scheduling_params.cancel_time == other.scheduling_params.cancel_time &&
+ scheduling_params.network_requirements ==
+ other.scheduling_params.network_requirements &&
+ scheduling_params.battery_requirements ==
+ other.scheduling_params.battery_requirements &&
+ scheduling_params.priority == other.scheduling_params.priority &&
+ request_params.url == other.request_params.url &&
+ request_params.method == other.request_params.method &&
+ request_params.request_headers.ToString() ==
+ other.request_params.request_headers.ToString() &&
+ state == other.state && target_file_path == other.target_file_path &&
+ create_time == other.create_time &&
+ completion_time == other.completion_time &&
+ attempt_count == other.attempt_count;
+}
+
} // namespace download
diff --git a/chromium/components/download/internal/entry.h b/chromium/components/download/internal/entry.h
index 9ba0c427527..20766317889 100644
--- a/chromium/components/download/internal/entry.h
+++ b/chromium/components/download/internal/entry.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_DOWNLOAD_INTERNAL_ENTRY_H_
#define COMPONENTS_DOWNLOAD_INTERNAL_ENTRY_H_
+#include "base/time/time.h"
#include "components/download/public/client.h"
#include "components/download/public/clients.h"
#include "components/download/public/download_params.h"
@@ -32,25 +33,30 @@ struct Entry {
// be run until it is resumed by the Client.
PAUSED = 3,
- // The download is 'complete' by some definition of that term (could have
- // failed, could have succeeded, etc.). It is ready to have UMA logs saved.
+ // The download is 'complete' and successful. At this point we are leaving
+ // this entry around to make sure the files on disk are cleaned up.
COMPLETE = 4,
- // The download is finished. We are leaving this entry around to make sure
- // the files on disk are cleaned up.
- WATCHDOG = 5,
+ // The count of entries for the enum.
+ COUNT = 5,
};
Entry();
Entry(const Entry& other);
+ explicit Entry(const DownloadParams& params);
~Entry();
+ bool operator==(const Entry& other) const;
+
// The feature that is requesting this download.
DownloadClient client = DownloadClient::INVALID;
// A unique GUID that represents this download. See | base::GenerateGUID()|.
std::string guid;
+ // The time when the entry is created.
+ base::Time create_time;
+
// The parameters that determine under what device conditions this download
// will occur.
SchedulingParams scheduling_params;
@@ -61,6 +67,16 @@ struct Entry {
// The state of the download to help the scheduler and loggers make the right
// decisions about the download object.
State state = State::NEW;
+
+ // Target file path for this download.
+ base::FilePath target_file_path;
+
+ // Time the download was marked as complete, base::Time() if the download is
+ // not yet complete.
+ base::Time completion_time;
+
+ // Stores the number of retries for this download.
+ uint32_t attempt_count;
};
} // namespace download
diff --git a/chromium/components/download/internal/entry_utils.cc b/chromium/components/download/internal/entry_utils.cc
new file mode 100644
index 00000000000..35aa3ede353
--- /dev/null
+++ b/chromium/components/download/internal/entry_utils.cc
@@ -0,0 +1,67 @@
+// 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/download/internal/entry_utils.h"
+
+#include "components/download/internal/entry.h"
+
+namespace download {
+namespace util {
+
+uint32_t GetNumberOfEntriesForClient(DownloadClient client,
+ const std::vector<Entry*>& entries) {
+ uint32_t count = 0;
+ for (auto* entry : entries)
+ if (entry->client == client)
+ count++;
+
+ return count;
+}
+
+std::map<DownloadClient, std::vector<std::string>> MapEntriesToClients(
+ const std::set<DownloadClient>& clients,
+ const std::vector<Entry*>& entries,
+ const std::set<Entry::State>& ignored_states) {
+ std::map<DownloadClient, std::vector<std::string>> categorized;
+
+ for (auto* entry : entries) {
+ if (ignored_states.find(entry->state) != ignored_states.end())
+ continue;
+
+ DownloadClient client = entry->client;
+ if (clients.find(client) == clients.end())
+ client = DownloadClient::INVALID;
+
+ categorized[client].push_back(entry->guid);
+ }
+
+ return categorized;
+}
+
+Criteria GetSchedulingCriteria(const Model::EntryList& entries) {
+ Criteria criteria;
+ for (auto* const entry : entries) {
+ DCHECK(entry);
+ const SchedulingParams& scheduling_params = entry->scheduling_params;
+ if (scheduling_params.battery_requirements ==
+ SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE) {
+ criteria.requires_battery_charging = false;
+ }
+ if (scheduling_params.network_requirements ==
+ SchedulingParams::NetworkRequirements::NONE) {
+ criteria.requires_unmetered_network = false;
+ }
+ }
+ return criteria;
+}
+
+bool EntryBetterThan(const Entry& lhs, const Entry& rhs) {
+ return lhs.scheduling_params.priority > rhs.scheduling_params.priority ||
+ (lhs.scheduling_params.priority == rhs.scheduling_params.priority &&
+ lhs.scheduling_params.cancel_time <
+ rhs.scheduling_params.cancel_time);
+}
+
+} // namespace util
+} // namespace download
diff --git a/chromium/components/download/internal/entry_utils.h b/chromium/components/download/internal/entry_utils.h
new file mode 100644
index 00000000000..780b49467fb
--- /dev/null
+++ b/chromium/components/download/internal/entry_utils.h
@@ -0,0 +1,49 @@
+// 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_INTERNAL_ENTRY_UTILS_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_ENTRY_UTILS_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "components/download/internal/model.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "components/download/public/clients.h"
+
+namespace download {
+
+struct Entry;
+
+namespace util {
+
+// Helper method to return the number of Entry objects in |entries| are
+// associated with |client|.
+uint32_t GetNumberOfEntriesForClient(DownloadClient client,
+ const std::vector<Entry*>& entries);
+
+// Effectively runs a map reduce to turn a list of Entry objects into a map of
+// [Client Id] -> List of entries. Any Entry in |entries| that does not have a
+// matching DownloadClient in |clients| will be put in the
+// DownloadClient::INVALID bucket. Any Entry in |entries| with an Entry::State
+// that is in |ignored_states| will not be included.
+std::map<DownloadClient, std::vector<std::string>> MapEntriesToClients(
+ const std::set<DownloadClient>& clients,
+ const std::vector<Entry*>& entries,
+ const std::set<Entry::State>& ignored_states);
+
+// Gets the least strict scheduling criteria from |entries|, the criteria is
+// used to schedule platform background tasks.
+Criteria GetSchedulingCriteria(const Model::EntryList& entries);
+
+// Returns if |lhs| entry is a better candidate to be the next download than
+// |rhs| based on their priority and cancel time.
+bool EntryBetterThan(const Entry& lhs, const Entry& rhs);
+
+} // namespace util
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_ENTRY_UTILS_H_
diff --git a/chromium/components/download/internal/entry_utils_unittest.cc b/chromium/components/download/internal/entry_utils_unittest.cc
new file mode 100644
index 00000000000..472151dc50c
--- /dev/null
+++ b/chromium/components/download/internal/entry_utils_unittest.cc
@@ -0,0 +1,119 @@
+// 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/download/internal/entry_utils.h"
+
+#include <algorithm>
+
+#include "base/memory/ptr_util.h"
+#include "components/download/internal/test/entry_utils.h"
+#include "components/download/public/clients.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace download {
+
+TEST(DownloadServiceEntryUtilsTest, TestGetNumberOfEntriesForClient_NoEntries) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ Entry entry3 = test::BuildBasicEntry();
+
+ std::vector<Entry*> entries = {&entry1, &entry2, &entry3};
+
+ EXPECT_EQ(
+ 0U, util::GetNumberOfEntriesForClient(DownloadClient::INVALID, entries));
+ EXPECT_EQ(3U,
+ util::GetNumberOfEntriesForClient(DownloadClient::TEST, entries));
+}
+
+TEST(DownloadServiceEntryUtilsTest, MapEntriesToClients) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ Entry entry3 = test::BuildBasicEntry();
+ Entry entry4 = test::BuildBasicEntry(Entry::State::COMPLETE);
+ Entry entry5 = test::BuildBasicEntry(Entry::State::AVAILABLE);
+
+ std::vector<Entry*> entries = {&entry1, &entry2, &entry3, &entry4, &entry5};
+ std::vector<std::string> expected_list = {
+ entry1.guid, entry2.guid, entry3.guid, entry4.guid, entry5.guid};
+ std::vector<std::string> expected_pruned_list = {entry1.guid, entry2.guid,
+ entry3.guid, entry5.guid};
+ // If DownloadClient::TEST isn't a valid Client, all of the associated entries
+ // should move to the DownloadClient::INVALID bucket.
+ auto mapped1 = util::MapEntriesToClients(std::set<DownloadClient>(), entries,
+ std::set<Entry::State>());
+ EXPECT_EQ(1U, mapped1.size());
+ EXPECT_NE(mapped1.end(), mapped1.find(DownloadClient::INVALID));
+ EXPECT_EQ(mapped1.end(), mapped1.find(DownloadClient::TEST));
+
+ auto list1 = mapped1.find(DownloadClient::INVALID)->second;
+ EXPECT_EQ(5U, list1.size());
+ EXPECT_TRUE(
+ std::equal(expected_list.begin(), expected_list.end(), list1.begin()));
+
+ // If DownloadClient::TEST is a valid Client, it should have the associated
+ // entries.
+ std::set<DownloadClient> clients = {DownloadClient::TEST};
+ auto mapped2 =
+ util::MapEntriesToClients(clients, entries, std::set<Entry::State>());
+ EXPECT_EQ(1U, mapped2.size());
+ EXPECT_NE(mapped2.end(), mapped2.find(DownloadClient::TEST));
+ EXPECT_EQ(mapped2.end(), mapped2.find(DownloadClient::INVALID));
+
+ auto list2 = mapped2.find(DownloadClient::TEST)->second;
+ EXPECT_EQ(5U, list2.size());
+ EXPECT_TRUE(
+ std::equal(expected_list.begin(), expected_list.end(), list2.begin()));
+
+ // If we are pruning entries with certain states, make sure those entries
+ // don't show up in the results.
+ std::set<Entry::State> ignored_states = {Entry::State::ACTIVE,
+ Entry::State::COMPLETE};
+ auto mapped3 = util::MapEntriesToClients(clients, entries, ignored_states);
+ EXPECT_EQ(1U, mapped3.size());
+ auto list3 = mapped3.find(DownloadClient::TEST)->second;
+ EXPECT_EQ(4U, list3.size());
+ EXPECT_TRUE(std::equal(expected_pruned_list.begin(),
+ expected_pruned_list.end(), list3.begin()));
+}
+
+TEST(DownloadServiceEntryUtilsTest, GetSchedulingCriteria) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ Entry entry3 = test::BuildBasicEntry();
+ Entry entry4 = test::BuildBasicEntry();
+
+ entry1.scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::UNMETERED;
+ entry1.scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+
+ entry2.scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::NONE;
+ entry2.scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+
+ entry3.scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::UNMETERED;
+ entry3.scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+
+ entry4.scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::NONE;
+ entry4.scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+
+ Model::EntryList list1 = {&entry1};
+ Model::EntryList list2 = {&entry1, &entry2};
+ Model::EntryList list3 = {&entry1, &entry3};
+ Model::EntryList list4 = {&entry1, &entry2, &entry3};
+ Model::EntryList list5 = {&entry1, &entry4};
+
+ EXPECT_EQ(Criteria(true, true), util::GetSchedulingCriteria(list1));
+ EXPECT_EQ(Criteria(true, false), util::GetSchedulingCriteria(list2));
+ EXPECT_EQ(Criteria(false, true), util::GetSchedulingCriteria(list3));
+ EXPECT_EQ(Criteria(false, false), util::GetSchedulingCriteria(list4));
+ EXPECT_EQ(Criteria(false, false), util::GetSchedulingCriteria(list5));
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/file_monitor.h b/chromium/components/download/internal/file_monitor.h
new file mode 100644
index 00000000000..9df409c1e70
--- /dev/null
+++ b/chromium/components/download/internal/file_monitor.h
@@ -0,0 +1,58 @@
+// 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_INTERNAL_FILE_MONITOR_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_FILE_MONITOR_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "components/download/internal/model.h"
+#include "components/download/internal/stats.h"
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace download {
+
+struct DriverEntry;
+struct Entry;
+
+// An utility class containing various file cleanup methods.
+class FileMonitor {
+ public:
+ using InitCallback = base::Callback<void(bool)>;
+
+ // Creates the file directory for the downloads if it doesn't exist.
+ virtual void Initialize(const InitCallback& callback) = 0;
+
+ // Deletes the files in storage directory that are not related to any entries
+ // in either database.
+ virtual void DeleteUnknownFiles(
+ const Model::EntryList& known_entries,
+ const std::vector<DriverEntry>& known_driver_entries) = 0;
+
+ // Deletes the files for the database entries which have been completed and
+ // ready for cleanup. Returns the entries eligible for clean up.
+ virtual std::vector<Entry*> CleanupFilesForCompletedEntries(
+ const Model::EntryList& entries,
+ const base::Closure& completion_callback) = 0;
+
+ // Deletes a list of files and logs UMA.
+ virtual void DeleteFiles(const std::set<base::FilePath>& files_to_remove,
+ stats::FileCleanupReason reason) = 0;
+
+ // Deletes all files in the download service directory. This is a hard reset
+ // on this directory.
+ virtual void HardRecover(const InitCallback& callback) = 0;
+
+ virtual ~FileMonitor() = default;
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_FILE_MONITOR_H_
diff --git a/chromium/components/download/internal/file_monitor_impl.cc b/chromium/components/download/internal/file_monitor_impl.cc
new file mode 100644
index 00000000000..639db4b9f22
--- /dev/null
+++ b/chromium/components/download/internal/file_monitor_impl.cc
@@ -0,0 +1,219 @@
+// 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/download/internal/file_monitor_impl.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "base/sys_info.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace download {
+
+namespace {
+
+// Helper function to calculate total file size in a directory, the total
+// disk space and free disk space of the volume that contains that directory.
+// Returns false if failed to query disk space or total disk space is empty.
+bool CalculateDiskUtilization(const base::FilePath& file_dir,
+ int64_t& total_disk_space,
+ int64_t& free_disk_space,
+ int64_t& files_size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::FileEnumerator file_enumerator(file_dir, false /* recursive */,
+ base::FileEnumerator::FILES);
+
+ int64_t size = 0;
+ // Compute the total size of all files in |file_dir|.
+ for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
+ path = file_enumerator.Next()) {
+ if (!base::GetFileSize(path, &size)) {
+ DVLOG(1) << "File size query failed.";
+ return false;
+ }
+ files_size += size;
+ }
+
+ // Disk space of the volume that |file_dir| belongs to.
+ total_disk_space = base::SysInfo::AmountOfTotalDiskSpace(file_dir);
+ free_disk_space = base::SysInfo::AmountOfFreeDiskSpace(file_dir);
+ if (total_disk_space == -1 || free_disk_space == -1) {
+ DVLOG(1) << "System disk space query failed.";
+ return false;
+ }
+
+ if (total_disk_space == 0) {
+ DVLOG(1) << "Empty total system disk space.";
+ return false;
+ }
+ return true;
+}
+
+// Creates the download directory if it doesn't exist.
+bool InitializeAndCreateDownloadDirectory(const base::FilePath& dir_path) {
+ // Create the download directory.
+ bool success = base::PathExists(dir_path);
+ if (!success) {
+ base::File::Error error = base::File::Error::FILE_OK;
+ success = base::CreateDirectoryAndGetError(dir_path, &error);
+ if (!success)
+ stats::LogsFileDirectoryCreationError(error);
+ }
+ // Records disk utilization histograms.
+ if (success) {
+ int64_t files_size = 0, total_disk_space = 0, free_disk_space = 0;
+ if (CalculateDiskUtilization(dir_path, total_disk_space, free_disk_space,
+ files_size)) {
+ stats::LogFileDirDiskUtilization(total_disk_space, free_disk_space,
+ files_size);
+ }
+ }
+
+ return success;
+}
+
+void GetFilesInDirectory(const base::FilePath& directory,
+ std::set<base::FilePath>& paths_out) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::FileEnumerator file_enumerator(directory, false /* recursive */,
+ base::FileEnumerator::FILES);
+
+ for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
+ path = file_enumerator.Next()) {
+ paths_out.insert(path);
+ }
+}
+
+void DeleteFilesOnFileThread(const std::set<base::FilePath>& paths,
+ stats::FileCleanupReason reason) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int num_delete_attempted = 0;
+ int num_delete_failed = 0;
+ int num_delete_by_external = 0;
+ for (const base::FilePath& path : paths) {
+ if (!base::PathExists(path)) {
+ num_delete_by_external++;
+ continue;
+ }
+
+ num_delete_attempted++;
+ DCHECK(!base::DirectoryExists(path));
+
+ if (!base::DeleteFile(path, false /* recursive */)) {
+ num_delete_failed++;
+ }
+ }
+
+ stats::LogFileCleanupStatus(reason, num_delete_attempted, num_delete_failed,
+ num_delete_by_external);
+}
+
+void DeleteUnknownFilesOnFileThread(
+ const base::FilePath& directory,
+ const std::set<base::FilePath>& download_file_paths) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ std::set<base::FilePath> files_in_dir;
+ GetFilesInDirectory(directory, files_in_dir);
+
+ std::set<base::FilePath> files_to_remove =
+ base::STLSetDifference<std::set<base::FilePath>>(files_in_dir,
+ download_file_paths);
+ DeleteFilesOnFileThread(files_to_remove, stats::FileCleanupReason::UNKNOWN);
+}
+
+bool HardRecoverOnFileThread(const base::FilePath& directory) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ std::set<base::FilePath> files_in_dir;
+ GetFilesInDirectory(directory, files_in_dir);
+ DeleteFilesOnFileThread(files_in_dir,
+ stats::FileCleanupReason::HARD_RECOVERY);
+ return InitializeAndCreateDownloadDirectory(directory);
+}
+
+} // namespace
+
+FileMonitorImpl::FileMonitorImpl(
+ const base::FilePath& download_file_dir,
+ const scoped_refptr<base::SequencedTaskRunner>& file_thread_task_runner,
+ base::TimeDelta file_keep_alive_time)
+ : download_file_dir_(download_file_dir),
+ file_keep_alive_time_(file_keep_alive_time),
+ file_thread_task_runner_(file_thread_task_runner),
+ weak_factory_(this) {}
+
+FileMonitorImpl::~FileMonitorImpl() = default;
+
+void FileMonitorImpl::Initialize(const InitCallback& callback) {
+ base::PostTaskAndReplyWithResult(
+ file_thread_task_runner_.get(), FROM_HERE,
+ base::Bind(&InitializeAndCreateDownloadDirectory, download_file_dir_),
+ callback);
+}
+
+void FileMonitorImpl::DeleteUnknownFiles(
+ const Model::EntryList& known_entries,
+ const std::vector<DriverEntry>& known_driver_entries) {
+ std::set<base::FilePath> download_file_paths;
+ for (Entry* entry : known_entries) {
+ download_file_paths.insert(entry->target_file_path);
+ }
+
+ for (const DriverEntry& driver_entry : known_driver_entries) {
+ download_file_paths.insert(driver_entry.current_file_path);
+ }
+
+ file_thread_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&DeleteUnknownFilesOnFileThread, download_file_dir_,
+ download_file_paths));
+}
+
+std::vector<Entry*> FileMonitorImpl::CleanupFilesForCompletedEntries(
+ const Model::EntryList& entries,
+ const base::Closure& completion_callback) {
+ std::vector<Entry*> entries_to_remove;
+ std::set<base::FilePath> files_to_remove;
+ for (auto* entry : entries) {
+ if (!ReadyForCleanup(entry))
+ continue;
+
+ entries_to_remove.push_back(entry);
+ files_to_remove.insert(entry->target_file_path);
+
+ // TODO(xingliu): Consider logs life time after the file being deleted on
+ // the file thread.
+ stats::LogFileLifeTime(base::Time::Now() - entry->completion_time);
+ }
+
+ file_thread_task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DeleteFilesOnFileThread, files_to_remove,
+ stats::FileCleanupReason::TIMEOUT),
+ completion_callback);
+ return entries_to_remove;
+}
+
+void FileMonitorImpl::DeleteFiles(
+ const std::set<base::FilePath>& files_to_remove,
+ stats::FileCleanupReason reason) {
+ file_thread_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&DeleteFilesOnFileThread, files_to_remove, reason));
+}
+
+void FileMonitorImpl::HardRecover(const InitCallback& callback) {
+ base::PostTaskAndReplyWithResult(
+ file_thread_task_runner_.get(), FROM_HERE,
+ base::Bind(&HardRecoverOnFileThread, download_file_dir_), callback);
+}
+
+bool FileMonitorImpl::ReadyForCleanup(const Entry* entry) {
+ return entry->state == Entry::State::COMPLETE &&
+ (base::Time::Now() - entry->completion_time) > file_keep_alive_time_;
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/file_monitor_impl.h b/chromium/components/download/internal/file_monitor_impl.h
new file mode 100644
index 00000000000..f0e5b55ad51
--- /dev/null
+++ b/chromium/components/download/internal/file_monitor_impl.h
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOWNLOAD_INTERNAL_FILE_MONITOR_IMPL_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_FILE_MONITOR_IMPL_H_
+
+#include "components/download/internal/file_monitor.h"
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "components/download/internal/driver_entry.h"
+#include "components/download/internal/model.h"
+#include "components/download/internal/stats.h"
+
+namespace download {
+
+struct Entry;
+
+// An utility class containing various file cleanup methods.
+class FileMonitorImpl : public FileMonitor {
+ public:
+ FileMonitorImpl(
+ const base::FilePath& download_file_dir,
+ const scoped_refptr<base::SequencedTaskRunner>& file_thread_task_runner,
+ base::TimeDelta file_keep_alive_time);
+ ~FileMonitorImpl() override;
+
+ // FileMonitor implementation.
+ void Initialize(const InitCallback& callback) override;
+ void DeleteUnknownFiles(
+ const Model::EntryList& known_entries,
+ const std::vector<DriverEntry>& known_driver_entries) override;
+ std::vector<Entry*> CleanupFilesForCompletedEntries(
+ const Model::EntryList& entries,
+ const base::Closure& completion_callback) override;
+ void DeleteFiles(const std::set<base::FilePath>& files_to_remove,
+ stats::FileCleanupReason reason) override;
+ void HardRecover(const InitCallback& callback) override;
+
+ private:
+ bool ReadyForCleanup(const Entry* entry);
+
+ const base::FilePath download_file_dir_;
+ const base::TimeDelta file_keep_alive_time_;
+
+ scoped_refptr<base::SequencedTaskRunner> file_thread_task_runner_;
+ base::WeakPtrFactory<FileMonitorImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileMonitorImpl);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_FILE_MONITOR_IMPL_H_
diff --git a/chromium/components/download/internal/file_monitor_unittest.cc b/chromium/components/download/internal/file_monitor_unittest.cc
new file mode 100644
index 00000000000..d668872c802
--- /dev/null
+++ b/chromium/components/download/internal/file_monitor_unittest.cc
@@ -0,0 +1,173 @@
+// 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 <algorithm>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/internal/driver_entry.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/file_monitor_impl.h"
+#include "components/download/internal/test/entry_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace download {
+
+class FileMonitorTest : public testing::Test {
+ public:
+ FileMonitorTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ handle_(task_runner_),
+ completion_callback_called_(false) {
+ EXPECT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
+ download_dir_ = scoped_temp_dir_.GetPath();
+ base::TimeDelta keep_alive_time = base::TimeDelta::FromHours(12);
+ monitor_ = base::MakeUnique<FileMonitorImpl>(download_dir_, task_runner_,
+ keep_alive_time);
+ }
+ ~FileMonitorTest() override = default;
+
+ void HardRecoveryResponse(bool result);
+ void CompletionCallback() { completion_callback_called_ = true; }
+
+ protected:
+ base::FilePath CreateTemporaryFile(std::string file_name);
+
+ base::ScopedTempDir scoped_temp_dir_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle handle_;
+ base::FilePath download_dir_;
+ bool completion_callback_called_;
+ std::unique_ptr<FileMonitor> monitor_;
+
+ base::Optional<bool> hard_recovery_result_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileMonitorTest);
+};
+
+base::FilePath FileMonitorTest::CreateTemporaryFile(std::string file_name) {
+ base::FilePath file_path = download_dir_.AppendASCII(file_name);
+ base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ EXPECT_TRUE(file.IsValid());
+ file.Close();
+
+ return file_path;
+}
+
+void FileMonitorTest::HardRecoveryResponse(bool result) {
+ hard_recovery_result_ = result;
+}
+
+TEST_F(FileMonitorTest, TestDeleteUnknownFiles) {
+ Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ entry1.target_file_path = CreateTemporaryFile(entry1.guid);
+
+ Entry entry2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ entry2.target_file_path = CreateTemporaryFile(entry2.guid);
+
+ DriverEntry driver_entry1;
+ driver_entry1.guid = entry1.guid;
+ driver_entry1.current_file_path = entry1.target_file_path;
+
+ DriverEntry driver_entry2;
+ driver_entry2.guid = base::GenerateGUID();
+ driver_entry2.current_file_path = CreateTemporaryFile(driver_entry2.guid);
+
+ base::FilePath temp_file1 = CreateTemporaryFile("temp1");
+ base::FilePath temp_file2 = CreateTemporaryFile("temp2");
+
+ auto check_file_existence = [&](bool e1, bool e2, bool de1, bool de2, bool t1,
+ bool t2) {
+ EXPECT_EQ(e1, base::PathExists(entry1.target_file_path));
+ EXPECT_EQ(e2, base::PathExists(entry2.target_file_path));
+ EXPECT_EQ(de1, base::PathExists(driver_entry1.current_file_path));
+ EXPECT_EQ(de2, base::PathExists(driver_entry2.current_file_path));
+ EXPECT_EQ(t1, base::PathExists(temp_file1));
+ EXPECT_EQ(t2, base::PathExists(temp_file2));
+ };
+
+ check_file_existence(true, true, true, true, true, true);
+
+ std::vector<Entry*> entries = {&entry1, &entry2};
+ std::vector<DriverEntry> driver_entries = {driver_entry1, driver_entry2};
+
+ monitor_->DeleteUnknownFiles(entries, driver_entries);
+ task_runner_->RunUntilIdle();
+ check_file_existence(true, true, true, true, false, false);
+
+ entries = {&entry2};
+ driver_entries = {driver_entry1, driver_entry2};
+ monitor_->DeleteUnknownFiles(entries, driver_entries);
+ task_runner_->RunUntilIdle();
+ check_file_existence(true, true, true, true, false, false);
+
+ entries = {&entry2};
+ driver_entries = {driver_entry2};
+ monitor_->DeleteUnknownFiles(entries, driver_entries);
+ task_runner_->RunUntilIdle();
+ check_file_existence(false, true, false, true, false, false);
+
+ entries.clear();
+ driver_entries.clear();
+ monitor_->DeleteUnknownFiles(entries, driver_entries);
+ task_runner_->RunUntilIdle();
+ check_file_existence(false, false, false, false, false, false);
+}
+
+TEST_F(FileMonitorTest, TestCleanupFilesForCompletedEntries) {
+ Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ entry1.state = Entry::State::COMPLETE;
+ entry1.completion_time = base::Time::Now() - base::TimeDelta::FromHours(20);
+ EXPECT_TRUE(
+ base::CreateTemporaryFileInDir(download_dir_, &entry1.target_file_path));
+
+ Entry entry2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ entry2.state = Entry::State::ACTIVE;
+ EXPECT_TRUE(
+ base::CreateTemporaryFileInDir(download_dir_, &entry2.target_file_path));
+
+ std::vector<Entry*> entries = {&entry1, &entry2};
+ std::vector<Entry*> entries_to_remove =
+ monitor_->CleanupFilesForCompletedEntries(
+ entries, base::Bind(&FileMonitorTest::CompletionCallback,
+ base::Unretained(this)));
+ task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(1u, entries_to_remove.size());
+ EXPECT_FALSE(base::PathExists(entry1.target_file_path));
+ EXPECT_TRUE(base::PathExists(entry2.target_file_path));
+ EXPECT_TRUE(completion_callback_called_);
+}
+
+TEST_F(FileMonitorTest, TestHardRecovery) {
+ base::FilePath temp_file1 = CreateTemporaryFile("temp1");
+ base::FilePath temp_file2 = CreateTemporaryFile("temp2");
+
+ auto callback = base::Bind(&FileMonitorTest::HardRecoveryResponse,
+ base::Unretained(this));
+
+ EXPECT_TRUE(base::PathExists(temp_file1));
+ EXPECT_TRUE(base::PathExists(temp_file2));
+
+ monitor_->HardRecover(callback);
+ task_runner_->RunUntilIdle();
+
+ EXPECT_TRUE(hard_recovery_result_.has_value());
+ EXPECT_TRUE(hard_recovery_result_.value());
+ EXPECT_FALSE(base::PathExists(temp_file1));
+ EXPECT_FALSE(base::PathExists(temp_file2));
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/model.h b/chromium/components/download/internal/model.h
index ce5de88e450..c1b2ed0948f 100644
--- a/chromium/components/download/internal/model.h
+++ b/chromium/components/download/internal/model.h
@@ -9,11 +9,11 @@
#include <string>
#include <vector>
+#include "components/download/internal/entry.h"
#include "components/download/public/clients.h"
namespace download {
-struct Entry;
class Store;
// The model that contains a runtime representation of Entry entries. Any
@@ -30,12 +30,12 @@ class Model {
// |success| is |false|, initialization of the Model and/or the underlying
// Store failed. Initialization of the Model is complete after this
// callback. If |success| is true it can be accessed now.
- virtual void OnInitialized(bool success) = 0;
+ virtual void OnModelReady(bool success) = 0;
- // Called asynchronously in response to a Model::Destroy call. If |success|
- // is |false|, destruction of the Model and/or the underlying Store failed.
- // Destruction of the Model is effectively complete after this callback.
- virtual void OnDestroyed(bool success) = 0;
+ // Called asynchronously in response to a Model::HardRecover call. If
+ // |success| is |false|, recovery of the Model and/or the underlying Store
+ // failed. After this call there should be no entries stored in this Model.
+ virtual void OnModelHardRecoverComplete(bool success) = 0;
// Called when an Entry addition is complete. |success| determines whether
// or not the entry has been successfully persisted to the Store.
@@ -62,10 +62,11 @@ class Model {
// Initializes the Model. Client::OnInitialized() will be called in response.
// The Model can be used after that call.
- virtual void Initialize() = 0;
+ virtual void Initialize(Client* client) = 0;
- // Destroys the Model. Client::OnDestroyed() will be called in response.
- virtual void Destroy() = 0;
+ // Deletes and attempts to re-initialize the Store.
+ // Client::OnHardRecoveryComplete() will be called in response asynchronously.
+ virtual void HardRecover() = 0;
// Adds |entry| to this Model and attempts to write |entry| to the Store.
// Client::OnItemAdded() will be called in response asynchronously.
diff --git a/chromium/components/download/internal/model_impl.cc b/chromium/components/download/internal/model_impl.cc
index f061b5de07d..9a62961ab70 100644
--- a/chromium/components/download/internal/model_impl.cc
+++ b/chromium/components/download/internal/model_impl.cc
@@ -4,29 +4,37 @@
#include "components/download/internal/model_impl.h"
+#include <map>
+
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "components/download/internal/entry.h"
+#include "components/download/internal/stats.h"
namespace download {
-ModelImpl::ModelImpl(Client* client, std::unique_ptr<Store> store)
- : client_(client), store_(std::move(store)), weak_ptr_factory_(this) {
- DCHECK(client_);
+ModelImpl::ModelImpl(std::unique_ptr<Store> store)
+ : client_(nullptr), store_(std::move(store)), weak_ptr_factory_(this) {
DCHECK(store_);
}
ModelImpl::~ModelImpl() = default;
-void ModelImpl::Initialize() {
+void ModelImpl::Initialize(Client* client) {
+ DCHECK(!client_);
+ client_ = client;
+ DCHECK(client_);
+
DCHECK(!store_->IsInitialized());
store_->Initialize(base::Bind(&ModelImpl::OnInitializedFinished,
weak_ptr_factory_.GetWeakPtr()));
}
-void ModelImpl::Destroy() {
- store_->Destroy(base::Bind(&ModelImpl::OnDestroyFinished,
- weak_ptr_factory_.GetWeakPtr()));
+void ModelImpl::HardRecover() {
+ entries_.clear();
+
+ store_->HardRecover(base::BindOnce(&ModelImpl::OnHardRecoverFinished,
+ weak_ptr_factory_.GetWeakPtr()));
}
void ModelImpl::Add(const Entry& entry) {
@@ -35,19 +43,20 @@ void ModelImpl::Add(const Entry& entry) {
entries_.emplace(entry.guid, base::MakeUnique<Entry>(entry));
- store_->Update(entry, base::Bind(&ModelImpl::OnAddFinished,
- weak_ptr_factory_.GetWeakPtr(), entry.client,
- entry.guid));
+ store_->Update(entry, base::BindOnce(&ModelImpl::OnAddFinished,
+ weak_ptr_factory_.GetWeakPtr(),
+ entry.client, entry.guid));
}
void ModelImpl::Update(const Entry& entry) {
DCHECK(store_->IsInitialized());
DCHECK(entries_.find(entry.guid) != entries_.end());
- entries_[entry.guid] = base::MakeUnique<Entry>(entry);
- store_->Update(entry, base::Bind(&ModelImpl::OnUpdateFinished,
- weak_ptr_factory_.GetWeakPtr(), entry.client,
- entry.guid));
+ *entries_[entry.guid] = entry;
+
+ store_->Update(entry, base::BindOnce(&ModelImpl::OnUpdateFinished,
+ weak_ptr_factory_.GetWeakPtr(),
+ entry.client, entry.guid));
}
void ModelImpl::Remove(const std::string& guid) {
@@ -56,11 +65,14 @@ void ModelImpl::Remove(const std::string& guid) {
const auto& it = entries_.find(guid);
DCHECK(it != entries_.end());
+ // Pull out a separate guid and a DownloadClient so that when we destroy the
+ // entry we don't destroy the std::string that is backing the guid.
+ std::string standalone_guid = guid;
DownloadClient client = it->second->client;
entries_.erase(it);
- store_->Remove(guid,
- base::Bind(&ModelImpl::OnRemoveFinished,
- weak_ptr_factory_.GetWeakPtr(), client, guid));
+ store_->Remove(standalone_guid, base::BindOnce(&ModelImpl::OnRemoveFinished,
+ weak_ptr_factory_.GetWeakPtr(),
+ client, standalone_guid));
}
Entry* ModelImpl::Get(const std::string& guid) {
@@ -79,33 +91,41 @@ Model::EntryList ModelImpl::PeekEntries() {
void ModelImpl::OnInitializedFinished(
bool success,
std::unique_ptr<std::vector<Entry>> entries) {
+ stats::LogModelOperationResult(stats::ModelAction::INITIALIZE, success);
+
if (!success) {
- client_->OnInitialized(false);
+ client_->OnModelReady(false);
return;
}
- for (const auto& entry : *entries)
+ std::map<Entry::State, uint32_t> entries_count;
+ for (const auto& entry : *entries) {
+ entries_count[entry.state]++;
entries_.emplace(entry.guid, base::MakeUnique<Entry>(entry));
+ }
- client_->OnInitialized(true);
+ stats::LogEntries(entries_count);
+ client_->OnModelReady(true);
}
-void ModelImpl::OnDestroyFinished(bool success) {
- store_.reset();
- entries_.clear();
- client_->OnDestroyed(success);
+void ModelImpl::OnHardRecoverFinished(bool success) {
+ client_->OnModelHardRecoverComplete(success);
}
void ModelImpl::OnAddFinished(DownloadClient client,
const std::string& guid,
bool success) {
+ stats::LogModelOperationResult(stats::ModelAction::ADD, success);
+
// Don't notify the Client if the entry was already removed.
- if (entries_.find(guid) == entries_.end())
+ auto it = entries_.find(guid);
+ if (it == entries_.end())
return;
// Remove the entry from the map if the add failed.
- if (!success)
- entries_.erase(guid);
+ if (!success) {
+ entries_.erase(it);
+ }
client_->OnItemAdded(success, client, guid);
}
@@ -113,6 +133,8 @@ void ModelImpl::OnAddFinished(DownloadClient client,
void ModelImpl::OnUpdateFinished(DownloadClient client,
const std::string& guid,
bool success) {
+ stats::LogModelOperationResult(stats::ModelAction::UPDATE, success);
+
// Don't notify the Client if the entry was already removed.
if (entries_.find(guid) == entries_.end())
return;
@@ -123,6 +145,8 @@ void ModelImpl::OnUpdateFinished(DownloadClient client,
void ModelImpl::OnRemoveFinished(DownloadClient client,
const std::string& guid,
bool success) {
+ stats::LogModelOperationResult(stats::ModelAction::REMOVE, success);
+
DCHECK(entries_.find(guid) == entries_.end());
client_->OnItemRemoved(success, client, guid);
}
diff --git a/chromium/components/download/internal/model_impl.h b/chromium/components/download/internal/model_impl.h
index 8c785019566..b2809f697f0 100644
--- a/chromium/components/download/internal/model_impl.h
+++ b/chromium/components/download/internal/model_impl.h
@@ -23,12 +23,12 @@ struct Entry;
// The internal implementation of Model.
class ModelImpl : public Model {
public:
- ModelImpl(Client* client, std::unique_ptr<Store> store);
+ ModelImpl(std::unique_ptr<Store> store);
~ModelImpl() override;
// Model implementation.
- void Initialize() override;
- void Destroy() override;
+ void Initialize(Client* client) override;
+ void HardRecover() override;
void Add(const Entry& entry) override;
void Update(const Entry& entry) override;
void Remove(const std::string& guid) override;
@@ -40,7 +40,7 @@ class ModelImpl : public Model {
void OnInitializedFinished(bool success,
std::unique_ptr<std::vector<Entry>> entries);
- void OnDestroyFinished(bool success);
+ void OnHardRecoverFinished(bool success);
void OnAddFinished(DownloadClient client,
const std::string& guid,
bool success);
@@ -53,7 +53,7 @@ class ModelImpl : public Model {
// The external Model::Client reference that will receive all interesting
// Model notifications.
- Client* const client_;
+ Client* client_;
// The backing Store that is responsible for saving and loading the
// persisted entries.
diff --git a/chromium/components/download/internal/model_impl_unittest.cc b/chromium/components/download/internal/model_impl_unittest.cc
index 19a78391702..624715f7d25 100644
--- a/chromium/components/download/internal/model_impl_unittest.cc
+++ b/chromium/components/download/internal/model_impl_unittest.cc
@@ -11,7 +11,9 @@
#include "base/guid.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/test/histogram_tester.h"
#include "components/download/internal/entry.h"
+#include "components/download/internal/stats.h"
#include "components/download/internal/test/entry_utils.h"
#include "components/download/internal/test/mock_model_client.h"
#include "components/download/internal/test/test_store.h"
@@ -35,7 +37,7 @@ class DownloadServiceModelImplTest : public testing::Test {
void SetUp() override {
auto store = base::MakeUnique<test::TestStore>();
store_ = store.get();
- model_ = base::MakeUnique<ModelImpl>(&client_, std::move(store));
+ model_ = base::MakeUnique<ModelImpl>(std::move(store));
}
protected:
@@ -51,85 +53,141 @@ class DownloadServiceModelImplTest : public testing::Test {
TEST_F(DownloadServiceModelImplTest, SuccessfulLifecycle) {
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
- EXPECT_CALL(client_, OnDestroyed(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
EXPECT_TRUE(store_->init_called());
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
-
- model_->Destroy();
- EXPECT_TRUE(store_->destroy_called());
- store_->TriggerDestroy(true);
}
TEST_F(DownloadServiceModelImplTest, SuccessfulInitWithEntries) {
- Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
- Entry entry2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
std::vector<Entry> entries = {entry1, entry2};
+ base::HistogramTester histogram_tester;
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
EXPECT_TRUE(store_->init_called());
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry1, model_->Get(entry1.guid)));
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry2, model_->Get(entry2.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry1, model_->Get(entry1.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry2, model_->Get(entry2.guid)));
+
+ // Verify histograms.
+ histogram_tester.ExpectBucketCount("Download.Service.Db.Records", 2, 1);
+ histogram_tester.ExpectBucketCount("Download.Service.Db.Records.New", 2, 1);
+ histogram_tester.ExpectBucketCount("Download.Service.Db.Records.Available", 0,
+ 1);
+ histogram_tester.ExpectBucketCount("Download.Service.Db.Records.Active", 0,
+ 1);
+ histogram_tester.ExpectBucketCount("Download.Service.Db.Records.Paused", 0,
+ 1);
+ histogram_tester.ExpectBucketCount("Download.Service.Db.Records.Complete", 0,
+ 1);
}
TEST_F(DownloadServiceModelImplTest, BadInit) {
- EXPECT_CALL(client_, OnInitialized(false)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(false)).Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
EXPECT_TRUE(store_->init_called());
store_->TriggerInit(false, base::MakeUnique<std::vector<Entry>>());
}
-TEST_F(DownloadServiceModelImplTest, BadDestroy) {
- InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
- EXPECT_CALL(client_, OnDestroyed(false)).Times(1);
+TEST_F(DownloadServiceModelImplTest, HardRecoverGoodModel) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ std::vector<Entry> entries = {entry1, entry2};
- model_->Initialize();
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
+
+ model_->Initialize(&client_);
EXPECT_TRUE(store_->init_called());
- store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+
+ EXPECT_CALL(client_, OnModelHardRecoverComplete(true));
- model_->Destroy();
- EXPECT_TRUE(store_->destroy_called());
- store_->TriggerDestroy(false);
+ model_->HardRecover();
+ store_->TriggerHardRecover(true);
+ EXPECT_TRUE(model_->PeekEntries().empty());
+}
+
+TEST_F(DownloadServiceModelImplTest, HardRecoverBadModel) {
+ EXPECT_CALL(client_, OnModelReady(false)).Times(1);
+
+ model_->Initialize(&client_);
+ EXPECT_TRUE(store_->init_called());
+ store_->TriggerInit(false, base::MakeUnique<std::vector<Entry>>());
+
+ EXPECT_CALL(client_, OnModelHardRecoverComplete(true));
+
+ model_->HardRecover();
+ store_->TriggerHardRecover(true);
+ EXPECT_TRUE(model_->PeekEntries().empty());
+}
+
+TEST_F(DownloadServiceModelImplTest, HardRecoverFailsGoodModel) {
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
+ std::vector<Entry> entries = {entry1, entry2};
+
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
+
+ model_->Initialize(&client_);
+ EXPECT_TRUE(store_->init_called());
+ store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+
+ EXPECT_CALL(client_, OnModelHardRecoverComplete(false));
+
+ model_->HardRecover();
+ store_->TriggerHardRecover(false);
+ EXPECT_TRUE(model_->PeekEntries().empty());
+}
+
+TEST_F(DownloadServiceModelImplTest, HardRecoverFailsBadModel) {
+ EXPECT_CALL(client_, OnModelReady(false)).Times(1);
+
+ model_->Initialize(&client_);
+ EXPECT_TRUE(store_->init_called());
+ store_->TriggerInit(false, base::MakeUnique<std::vector<Entry>>());
+
+ EXPECT_CALL(client_, OnModelHardRecoverComplete(false));
+
+ model_->HardRecover();
+ store_->TriggerHardRecover(false);
+ EXPECT_TRUE(model_->PeekEntries().empty());
}
TEST_F(DownloadServiceModelImplTest, Add) {
- Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
- Entry entry2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
EXPECT_CALL(client_, OnItemAdded(true, entry1.client, entry1.guid)).Times(1);
EXPECT_CALL(client_, OnItemAdded(false, entry2.client, entry2.guid)).Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
model_->Add(entry1);
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry1, model_->Get(entry1.guid)));
- EXPECT_TRUE(
- test::SuperficialEntryCompare(&entry1, store_->LastUpdatedEntry()));
+ EXPECT_TRUE(test::CompareEntry(&entry1, model_->Get(entry1.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry1, store_->LastUpdatedEntry()));
store_->TriggerUpdate(true);
model_->Add(entry2);
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry2, model_->Get(entry2.guid)));
- EXPECT_TRUE(
- test::SuperficialEntryCompare(&entry2, store_->LastUpdatedEntry()));
+ EXPECT_TRUE(test::CompareEntry(&entry2, model_->Get(entry2.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry2, store_->LastUpdatedEntry()));
store_->TriggerUpdate(false);
EXPECT_EQ(nullptr, model_->Get(entry2.guid));
}
TEST_F(DownloadServiceModelImplTest, Update) {
- Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry1 = test::BuildBasicEntry();
Entry entry2(entry1);
entry2.state = Entry::State::AVAILABLE;
@@ -140,43 +198,51 @@ TEST_F(DownloadServiceModelImplTest, Update) {
std::vector<Entry> entries = {entry1};
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
EXPECT_CALL(client_, OnItemUpdated(true, entry1.client, entry1.guid))
- .Times(1);
+ .Times(2);
EXPECT_CALL(client_, OnItemUpdated(false, entry1.client, entry1.guid))
.Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+ std::vector<Entry*> entries_pointers = model_->PeekEntries();
+ // Update with a different object.
model_->Update(entry2);
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry2, model_->Get(entry2.guid)));
- EXPECT_TRUE(
- test::SuperficialEntryCompare(&entry2, store_->LastUpdatedEntry()));
+ EXPECT_TRUE(test::CompareEntry(&entry2, model_->Get(entry2.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry2, store_->LastUpdatedEntry()));
+ store_->TriggerUpdate(true);
+
+ // Update with the same object.
+ Entry* entry = model_->Get(entry1.guid);
+ entry->state = Entry::State::NEW;
+ model_->Update(*entry);
store_->TriggerUpdate(true);
+ // Peek entries should return the same set of pointers.
+ EXPECT_TRUE(test::CompareEntryList(entries_pointers, model_->PeekEntries()));
model_->Update(entry3);
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry3, model_->Get(entry3.guid)));
- EXPECT_TRUE(
- test::SuperficialEntryCompare(&entry3, store_->LastUpdatedEntry()));
+ EXPECT_TRUE(test::CompareEntry(&entry3, model_->Get(entry3.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry3, store_->LastUpdatedEntry()));
store_->TriggerUpdate(false);
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry3, model_->Get(entry3.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry3, model_->Get(entry3.guid)));
}
TEST_F(DownloadServiceModelImplTest, Remove) {
- Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
- Entry entry2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
std::vector<Entry> entries = {entry1, entry2};
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
EXPECT_CALL(client_, OnItemRemoved(true, entry1.client, entry1.guid))
.Times(1);
EXPECT_CALL(client_, OnItemRemoved(false, entry2.client, entry2.guid))
.Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
model_->Remove(entry1.guid);
@@ -191,50 +257,49 @@ TEST_F(DownloadServiceModelImplTest, Remove) {
}
TEST_F(DownloadServiceModelImplTest, Get) {
- Entry entry = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry = test::BuildBasicEntry();
std::vector<Entry> entries = {entry};
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry, model_->Get(entry.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry, model_->Get(entry.guid)));
EXPECT_EQ(nullptr, model_->Get(base::GenerateGUID()));
}
TEST_F(DownloadServiceModelImplTest, PeekEntries) {
- Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
- Entry entry2 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry1 = test::BuildBasicEntry();
+ Entry entry2 = test::BuildBasicEntry();
std::vector<Entry> entries = {entry1, entry2};
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
std::vector<Entry*> expected_peek = {&entry1, &entry2};
- EXPECT_TRUE(
- test::SuperficialEntryListCompare(expected_peek, model_->PeekEntries()));
+ EXPECT_TRUE(test::CompareEntryList(expected_peek, model_->PeekEntries()));
}
TEST_F(DownloadServiceModelImplTest, TestRemoveAfterAdd) {
- Entry entry = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry = test::BuildBasicEntry();
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
EXPECT_CALL(client_, OnItemAdded(_, _, _)).Times(0);
EXPECT_CALL(client_, OnItemRemoved(true, entry.client, entry.guid)).Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
model_->Add(entry);
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry, model_->Get(entry.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry, model_->Get(entry.guid)));
model_->Remove(entry.guid);
EXPECT_EQ(nullptr, model_->Get(entry.guid));
@@ -244,7 +309,7 @@ TEST_F(DownloadServiceModelImplTest, TestRemoveAfterAdd) {
}
TEST_F(DownloadServiceModelImplTest, TestRemoveAfterUpdate) {
- Entry entry1 = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry entry1 = test::BuildBasicEntry();
Entry entry2(entry1);
entry2.state = Entry::State::AVAILABLE;
@@ -252,17 +317,17 @@ TEST_F(DownloadServiceModelImplTest, TestRemoveAfterUpdate) {
std::vector<Entry> entries = {entry1};
InSequence sequence;
- EXPECT_CALL(client_, OnInitialized(true)).Times(1);
+ EXPECT_CALL(client_, OnModelReady(true)).Times(1);
EXPECT_CALL(client_, OnItemUpdated(_, _, _)).Times(0);
EXPECT_CALL(client_, OnItemRemoved(true, entry1.client, entry1.guid))
.Times(1);
- model_->Initialize();
+ model_->Initialize(&client_);
store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry1, model_->Get(entry1.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry1, model_->Get(entry1.guid)));
model_->Update(entry2);
- EXPECT_TRUE(test::SuperficialEntryCompare(&entry2, model_->Get(entry2.guid)));
+ EXPECT_TRUE(test::CompareEntry(&entry2, model_->Get(entry2.guid)));
model_->Remove(entry2.guid);
EXPECT_EQ(nullptr, model_->Get(entry2.guid));
diff --git a/chromium/components/download/internal/noop_store.cc b/chromium/components/download/internal/noop_store.cc
deleted file mode 100644
index 42785cbc5b1..00000000000
--- a/chromium/components/download/internal/noop_store.cc
+++ /dev/null
@@ -1,53 +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/download/internal/noop_store.h"
-
-#include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/download/internal/entry.h"
-
-namespace download {
-
-NoopStore::NoopStore() : initialized_(false), weak_ptr_factory_(this) {}
-
-NoopStore::~NoopStore() = default;
-
-bool NoopStore::IsInitialized() {
- return initialized_;
-}
-
-void NoopStore::Initialize(InitCallback callback) {
- DCHECK(!IsInitialized());
-
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&NoopStore::OnInitFinished, weak_ptr_factory_.GetWeakPtr(),
- std::move(callback)));
-}
-
-void NoopStore::Destroy(StoreCallback callback) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), true /** success */));
-}
-
-void NoopStore::Update(const Entry& entry, StoreCallback callback) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), true /** success */));
-}
-
-void NoopStore::Remove(const std::string& guid, StoreCallback callback) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), true /** success */));
-}
-
-void NoopStore::OnInitFinished(InitCallback callback) {
- initialized_ = true;
-
- std::unique_ptr<std::vector<Entry>> entries =
- base::MakeUnique<std::vector<Entry>>();
- std::move(callback).Run(true /** success */, std::move(entries));
-}
-
-} // namespace download
diff --git a/chromium/components/download/internal/noop_store.h b/chromium/components/download/internal/noop_store.h
deleted file mode 100644
index 4bad9ed2e13..00000000000
--- a/chromium/components/download/internal/noop_store.h
+++ /dev/null
@@ -1,46 +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_INTERNAL_NOOP_STORE_H_
-#define COMPONENTS_DOWNLOAD_INTERNAL_NOOP_STORE_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "components/download/internal/store.h"
-
-namespace download {
-
-struct Entry;
-
-// A Store implementation that doesn't do anything but honors the interface
-// requirements.
-// TODO(dtrainor, shaktisahu): Remove this if it's no longer necessary after the
-// real Store implementation is added.
-class NoopStore : public Store {
- public:
- NoopStore();
- ~NoopStore() override;
-
- // Store implementation.
- bool IsInitialized() override;
- void Initialize(InitCallback callback) override;
- void Destroy(StoreCallback callback) override;
- void Update(const Entry& entry, StoreCallback callback) override;
- void Remove(const std::string& guid, StoreCallback callback) override;
-
- private:
- void OnInitFinished(InitCallback callback);
-
- // Whether or not this Store is 'initialized.' Just gets set to |true| once
- // Initialize() is called.
- bool initialized_;
-
- base::WeakPtrFactory<NoopStore> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(NoopStore);
-};
-
-} // namespace download
-
-#endif // COMPONENTS_DOWNLOAD_INTERNAL_NOOP_STORE_H_
diff --git a/chromium/components/download/internal/proto/BUILD.gn b/chromium/components/download/internal/proto/BUILD.gn
new file mode 100644
index 00000000000..9d6c33e751f
--- /dev/null
+++ b/chromium/components/download/internal/proto/BUILD.gn
@@ -0,0 +1,18 @@
+# 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+ visibility = [
+ "//components/download/content/factory",
+ "//components/download/internal:*",
+ ]
+
+ sources = [
+ "entry.proto",
+ "request.proto",
+ "scheduling.proto",
+ ]
+}
diff --git a/chromium/components/download/internal/proto/entry.proto b/chromium/components/download/internal/proto/entry.proto
new file mode 100644
index 00000000000..426588a29e6
--- /dev/null
+++ b/chromium/components/download/internal/proto/entry.proto
@@ -0,0 +1,55 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package protodb;
+
+import "request.proto";
+import "scheduling.proto";
+
+// This should stay in sync with the DownloadClient enum
+// (components/download/public/clients.h).
+enum DownloadClient {
+ INVALID = 0;
+ TEST = 1;
+ TEST_2 = 2;
+ TEST_3 = 3;
+ OFFLINE_PAGE_PREFETCH = 4;
+ BOUNDARY = 5;
+}
+
+// Stores the request params, internal state, metrics and metadata associated
+// with a download request.
+message Entry {
+ // This should stay in sync with the State enum
+ // (components/download/internal/entry.h).
+ enum State {
+ NEW = 0;
+ AVAILABLE = 1;
+ ACTIVE = 2;
+ PAUSED = 3;
+ COMPLETE = 4;
+ }
+
+ // Identification Parameters.
+ optional DownloadClient name_space = 1;
+ optional string guid = 2;
+
+ // Requested Parameters.
+ optional SchedulingParams scheduling_params = 3;
+ optional RequestParams request_params = 4;
+
+ // Internal Tracking States.
+ optional State state = 5;
+ optional string target_file_path = 6;
+
+ // Uses internal time representation.
+ optional int64 create_time = 7;
+ optional int64 completion_time = 8;
+
+ optional uint32 attempt_count = 9;
+}
diff --git a/chromium/components/download/internal/proto/request.proto b/chromium/components/download/internal/proto/request.proto
new file mode 100644
index 00000000000..173c6856182
--- /dev/null
+++ b/chromium/components/download/internal/proto/request.proto
@@ -0,0 +1,21 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package protodb;
+
+message RequestHeader {
+ optional string key = 1;
+ optional string value = 2;
+}
+
+// Stores the HTTP request params associated with a download request.
+message RequestParams {
+ optional string url = 1;
+ optional string method = 2;
+ repeated RequestHeader headers = 3;
+}
diff --git a/chromium/components/download/internal/proto/scheduling.proto b/chromium/components/download/internal/proto/scheduling.proto
new file mode 100644
index 00000000000..2b8cc7ac747
--- /dev/null
+++ b/chromium/components/download/internal/proto/scheduling.proto
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package protodb;
+
+// Stores the scheduling params associated with a download request.
+message SchedulingParams {
+ // This should stay in sync with the NetworkRequirements enum
+ // (components/download/public/download_params.h).
+ enum NetworkRequirements {
+ NONE = 0;
+ OPTIMISTIC = 1;
+ UNMETERED = 2;
+ }
+
+ // This should stay in sync with the BatteryRequirements enum
+ // (components/download/public/download_params.h).
+ enum BatteryRequirements {
+ BATTERY_INSENSITIVE = 0;
+ BATTERY_SENSITIVE = 1;
+ }
+
+ // This should stay in sync with the Priority enum
+ // (components/download/public/download_params.h).
+ enum Priority {
+ LOW = 0;
+ NORMAL = 1;
+ HIGH = 2;
+ UI = 3;
+ }
+
+ // Uses internal time representation.
+ optional int64 cancel_time = 2;
+
+ optional Priority priority = 3;
+ optional NetworkRequirements network_requirements = 4;
+ optional BatteryRequirements battery_requirements = 5;
+}
diff --git a/chromium/components/download/internal/proto_conversions.cc b/chromium/components/download/internal/proto_conversions.cc
new file mode 100644
index 00000000000..f78f521b054
--- /dev/null
+++ b/chromium/components/download/internal/proto_conversions.cc
@@ -0,0 +1,307 @@
+// 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 <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "components/download/internal/proto_conversions.h"
+#include "net/http/http_request_headers.h"
+
+namespace download {
+
+protodb::Entry_State ProtoConversions::RequestStateToProto(Entry::State state) {
+ switch (state) {
+ case Entry::State::NEW:
+ return protodb::Entry_State_NEW;
+ case Entry::State::AVAILABLE:
+ return protodb::Entry_State_AVAILABLE;
+ case Entry::State::ACTIVE:
+ return protodb::Entry_State_ACTIVE;
+ case Entry::State::PAUSED:
+ return protodb::Entry_State_PAUSED;
+ case Entry::State::COMPLETE:
+ return protodb::Entry_State_COMPLETE;
+ case Entry::State::COUNT:
+ break;
+ }
+
+ NOTREACHED();
+ return protodb::Entry_State_NEW;
+}
+
+Entry::State ProtoConversions::RequestStateFromProto(
+ protodb::Entry_State state) {
+ switch (state) {
+ case protodb::Entry_State_NEW:
+ return Entry::State::NEW;
+ case protodb::Entry_State_AVAILABLE:
+ return Entry::State::AVAILABLE;
+ case protodb::Entry_State_ACTIVE:
+ return Entry::State::ACTIVE;
+ case protodb::Entry_State_PAUSED:
+ return Entry::State::PAUSED;
+ case protodb::Entry_State_COMPLETE:
+ return Entry::State::COMPLETE;
+ }
+
+ NOTREACHED();
+ return Entry::State::NEW;
+}
+
+protodb::DownloadClient ProtoConversions::DownloadClientToProto(
+ DownloadClient client) {
+ switch (client) {
+ case DownloadClient::INVALID:
+ return protodb::DownloadClient::INVALID;
+ case DownloadClient::TEST:
+ return protodb::DownloadClient::TEST;
+ case DownloadClient::TEST_2:
+ return protodb::DownloadClient::TEST_2;
+ case DownloadClient::TEST_3:
+ return protodb::DownloadClient::TEST_3;
+ case DownloadClient::OFFLINE_PAGE_PREFETCH:
+ return protodb::DownloadClient::OFFLINE_PAGE_PREFETCH;
+ case DownloadClient::BOUNDARY:
+ return protodb::DownloadClient::BOUNDARY;
+ }
+
+ NOTREACHED();
+ return protodb::DownloadClient::INVALID;
+}
+
+DownloadClient ProtoConversions::DownloadClientFromProto(
+ protodb::DownloadClient client) {
+ switch (client) {
+ case protodb::DownloadClient::INVALID:
+ return DownloadClient::INVALID;
+ case protodb::DownloadClient::TEST:
+ return DownloadClient::TEST;
+ case protodb::DownloadClient::TEST_2:
+ return DownloadClient::TEST_2;
+ case protodb::DownloadClient::TEST_3:
+ return DownloadClient::TEST_3;
+ case protodb::DownloadClient::OFFLINE_PAGE_PREFETCH:
+ return DownloadClient::OFFLINE_PAGE_PREFETCH;
+ case protodb::DownloadClient::BOUNDARY:
+ return DownloadClient::BOUNDARY;
+ }
+
+ NOTREACHED();
+ return DownloadClient::INVALID;
+}
+
+SchedulingParams::NetworkRequirements
+ProtoConversions::NetworkRequirementsFromProto(
+ protodb::SchedulingParams_NetworkRequirements network_requirements) {
+ switch (network_requirements) {
+ case protodb::SchedulingParams_NetworkRequirements_NONE:
+ return SchedulingParams::NetworkRequirements::NONE;
+ case protodb::SchedulingParams_NetworkRequirements_OPTIMISTIC:
+ return SchedulingParams::NetworkRequirements::OPTIMISTIC;
+ case protodb::SchedulingParams_NetworkRequirements_UNMETERED:
+ return SchedulingParams::NetworkRequirements::UNMETERED;
+ }
+
+ NOTREACHED();
+ return SchedulingParams::NetworkRequirements::NONE;
+}
+
+protodb::SchedulingParams_NetworkRequirements
+ProtoConversions::NetworkRequirementsToProto(
+ SchedulingParams::NetworkRequirements network_requirements) {
+ switch (network_requirements) {
+ case SchedulingParams::NetworkRequirements::NONE:
+ return protodb::SchedulingParams_NetworkRequirements_NONE;
+ case SchedulingParams::NetworkRequirements::OPTIMISTIC:
+ return protodb::SchedulingParams_NetworkRequirements_OPTIMISTIC;
+ case SchedulingParams::NetworkRequirements::UNMETERED:
+ return protodb::SchedulingParams_NetworkRequirements_UNMETERED;
+ case SchedulingParams::NetworkRequirements::COUNT:
+ break;
+ }
+
+ NOTREACHED();
+ return protodb::SchedulingParams_NetworkRequirements_NONE;
+}
+
+SchedulingParams::BatteryRequirements
+ProtoConversions::BatteryRequirementsFromProto(
+ protodb::SchedulingParams_BatteryRequirements battery_requirements) {
+ switch (battery_requirements) {
+ case protodb::SchedulingParams_BatteryRequirements_BATTERY_INSENSITIVE:
+ return SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+ case protodb::SchedulingParams_BatteryRequirements_BATTERY_SENSITIVE:
+ return SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+ }
+
+ NOTREACHED();
+ return SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+}
+
+protodb::SchedulingParams_BatteryRequirements
+ProtoConversions::BatteryRequirementsToProto(
+ SchedulingParams::BatteryRequirements battery_requirements) {
+ switch (battery_requirements) {
+ case SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE:
+ return protodb::SchedulingParams_BatteryRequirements_BATTERY_INSENSITIVE;
+ case SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE:
+ return protodb::SchedulingParams_BatteryRequirements_BATTERY_SENSITIVE;
+ case SchedulingParams::BatteryRequirements::COUNT:
+ break;
+ }
+
+ NOTREACHED();
+ return protodb::SchedulingParams_BatteryRequirements_BATTERY_INSENSITIVE;
+}
+
+SchedulingParams::Priority ProtoConversions::SchedulingPriorityFromProto(
+ protodb::SchedulingParams_Priority priority) {
+ switch (priority) {
+ case protodb::SchedulingParams_Priority_LOW:
+ return SchedulingParams::Priority::LOW;
+ case protodb::SchedulingParams_Priority_NORMAL:
+ return SchedulingParams::Priority::NORMAL;
+ case protodb::SchedulingParams_Priority_HIGH:
+ return SchedulingParams::Priority::HIGH;
+ case protodb::SchedulingParams_Priority_UI:
+ return SchedulingParams::Priority::UI;
+ }
+
+ NOTREACHED();
+ return SchedulingParams::Priority::LOW;
+}
+
+protodb::SchedulingParams_Priority ProtoConversions::SchedulingPriorityToProto(
+ SchedulingParams::Priority priority) {
+ switch (priority) {
+ case SchedulingParams::Priority::LOW:
+ return protodb::SchedulingParams_Priority_LOW;
+ case SchedulingParams::Priority::NORMAL:
+ return protodb::SchedulingParams_Priority_NORMAL;
+ case SchedulingParams::Priority::HIGH:
+ return protodb::SchedulingParams_Priority_HIGH;
+ case SchedulingParams::Priority::UI:
+ return protodb::SchedulingParams_Priority_UI;
+ case SchedulingParams::Priority::COUNT:
+ break;
+ }
+
+ NOTREACHED();
+ return protodb::SchedulingParams_Priority_LOW;
+}
+
+SchedulingParams ProtoConversions::SchedulingParamsFromProto(
+ const protodb::SchedulingParams& proto) {
+ SchedulingParams scheduling_params;
+
+ scheduling_params.cancel_time =
+ base::Time::FromInternalValue(proto.cancel_time());
+ scheduling_params.priority = SchedulingPriorityFromProto(proto.priority());
+ scheduling_params.network_requirements =
+ NetworkRequirementsFromProto(proto.network_requirements());
+ scheduling_params.battery_requirements =
+ BatteryRequirementsFromProto(proto.battery_requirements());
+
+ return scheduling_params;
+}
+
+void ProtoConversions::SchedulingParamsToProto(
+ const SchedulingParams& scheduling_params,
+ protodb::SchedulingParams* proto) {
+ proto->set_cancel_time(scheduling_params.cancel_time.ToInternalValue());
+ proto->set_priority(SchedulingPriorityToProto(scheduling_params.priority));
+ proto->set_network_requirements(
+ NetworkRequirementsToProto(scheduling_params.network_requirements));
+ proto->set_battery_requirements(
+ BatteryRequirementsToProto(scheduling_params.battery_requirements));
+}
+
+RequestParams ProtoConversions::RequestParamsFromProto(
+ const protodb::RequestParams& proto) {
+ RequestParams request_params;
+ request_params.url = GURL(proto.url());
+ request_params.method = proto.method();
+
+ for (int i = 0; i < proto.headers_size(); i++) {
+ protodb::RequestHeader header = proto.headers(i);
+ request_params.request_headers.SetHeader(header.key(), header.value());
+ }
+
+ return request_params;
+}
+
+void ProtoConversions::RequestParamsToProto(const RequestParams& request_params,
+ protodb::RequestParams* proto) {
+ proto->set_url(request_params.url.spec());
+ proto->set_method(request_params.method);
+
+ int i = 0;
+ net::HttpRequestHeaders::Iterator iter(request_params.request_headers);
+ while (iter.GetNext()) {
+ protodb::RequestHeader* header = proto->add_headers();
+ header->set_key(iter.name());
+ header->set_value(iter.value());
+ i++;
+ }
+}
+
+Entry ProtoConversions::EntryFromProto(const protodb::Entry& proto) {
+ Entry entry;
+
+ entry.guid = proto.guid();
+ entry.client = DownloadClientFromProto(proto.name_space());
+ entry.scheduling_params =
+ SchedulingParamsFromProto(proto.scheduling_params());
+ entry.request_params = RequestParamsFromProto(proto.request_params());
+ entry.state = RequestStateFromProto(proto.state());
+ entry.target_file_path =
+ base::FilePath::FromUTF8Unsafe(proto.target_file_path());
+ entry.create_time = base::Time::FromInternalValue(proto.create_time());
+ entry.completion_time =
+ base::Time::FromInternalValue(proto.completion_time());
+ entry.attempt_count = proto.attempt_count();
+
+ return entry;
+}
+
+protodb::Entry ProtoConversions::EntryToProto(const Entry& entry) {
+ protodb::Entry proto;
+
+ proto.set_guid(entry.guid);
+ proto.set_name_space(DownloadClientToProto(entry.client));
+ SchedulingParamsToProto(entry.scheduling_params,
+ proto.mutable_scheduling_params());
+ RequestParamsToProto(entry.request_params, proto.mutable_request_params());
+ proto.set_state(RequestStateToProto(entry.state));
+ proto.set_target_file_path(entry.target_file_path.AsUTF8Unsafe());
+ proto.set_create_time(entry.create_time.ToInternalValue());
+ proto.set_completion_time(entry.completion_time.ToInternalValue());
+ proto.set_attempt_count(entry.attempt_count);
+
+ return proto;
+}
+
+std::unique_ptr<std::vector<Entry>> ProtoConversions::EntryVectorFromProto(
+ std::unique_ptr<std::vector<protodb::Entry>> protos) {
+ auto entries = base::MakeUnique<std::vector<Entry>>();
+ for (auto& proto : *protos) {
+ entries->push_back(EntryFromProto(proto));
+ }
+
+ return entries;
+}
+
+std::unique_ptr<std::vector<protodb::Entry>>
+ProtoConversions::EntryVectorToProto(
+ std::unique_ptr<std::vector<Entry>> entries) {
+ auto protos = base::MakeUnique<std::vector<protodb::Entry>>();
+ for (auto& entry : *entries) {
+ protos->push_back(EntryToProto(entry));
+ }
+
+ return protos;
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/proto_conversions.h b/chromium/components/download/internal/proto_conversions.h
new file mode 100644
index 00000000000..4d2e4e53661
--- /dev/null
+++ b/chromium/components/download/internal/proto_conversions.h
@@ -0,0 +1,64 @@
+// 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_INTERNAL_PROTO_CONVERSIONS_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_PROTO_CONVERSIONS_H_
+
+#include "components/download/internal/entry.h"
+#include "components/download/internal/proto/entry.pb.h"
+#include "components/download/internal/proto/request.pb.h"
+#include "components/download/internal/proto/scheduling.pb.h"
+
+namespace download {
+
+class ProtoConversions {
+ public:
+ static Entry EntryFromProto(const protodb::Entry& proto);
+
+ static protodb::Entry EntryToProto(const Entry& entry);
+
+ static std::unique_ptr<std::vector<Entry>> EntryVectorFromProto(
+ std::unique_ptr<std::vector<protodb::Entry>> proto);
+
+ static std::unique_ptr<std::vector<protodb::Entry>> EntryVectorToProto(
+ std::unique_ptr<std::vector<Entry>> entries);
+
+ protected:
+ static protodb::Entry_State RequestStateToProto(Entry::State state);
+ static Entry::State RequestStateFromProto(protodb::Entry_State state);
+
+ static protodb::DownloadClient DownloadClientToProto(DownloadClient client);
+ static DownloadClient DownloadClientFromProto(protodb::DownloadClient client);
+
+ static SchedulingParams::NetworkRequirements NetworkRequirementsFromProto(
+ protodb::SchedulingParams_NetworkRequirements network_requirements);
+ static protodb::SchedulingParams_NetworkRequirements
+ NetworkRequirementsToProto(
+ SchedulingParams::NetworkRequirements network_requirements);
+
+ static SchedulingParams::BatteryRequirements BatteryRequirementsFromProto(
+ protodb::SchedulingParams_BatteryRequirements battery_requirements);
+ static protodb::SchedulingParams_BatteryRequirements
+ BatteryRequirementsToProto(
+ SchedulingParams::BatteryRequirements battery_requirements);
+
+ static SchedulingParams::Priority SchedulingPriorityFromProto(
+ protodb::SchedulingParams_Priority priority);
+ static protodb::SchedulingParams_Priority SchedulingPriorityToProto(
+ SchedulingParams::Priority priority);
+
+ static SchedulingParams SchedulingParamsFromProto(
+ const protodb::SchedulingParams& proto);
+ static void SchedulingParamsToProto(const SchedulingParams& scheduling_params,
+ protodb::SchedulingParams* proto);
+
+ static RequestParams RequestParamsFromProto(
+ const protodb::RequestParams& proto);
+ static void RequestParamsToProto(const RequestParams& request_params,
+ protodb::RequestParams* proto);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_PROTO_CONVERSIONS_H_
diff --git a/chromium/components/download/internal/proto_conversions_unittest.cc b/chromium/components/download/internal/proto_conversions_unittest.cc
new file mode 100644
index 00000000000..ea1e9cbf4fd
--- /dev/null
+++ b/chromium/components/download/internal/proto_conversions_unittest.cc
@@ -0,0 +1,153 @@
+// 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 <utility>
+
+#include "base/guid.h"
+#include "base/memory/ptr_util.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/proto_conversions.h"
+#include "components/download/internal/test/entry_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+std::string TEST_URL = "https://google.com";
+
+} // namespace
+
+namespace download {
+
+class ProtoConversionsTest : public testing::Test, public ProtoConversions {
+ public:
+ ~ProtoConversionsTest() override {}
+};
+
+TEST_F(ProtoConversionsTest, StateConversion) {
+ Entry::State states[] = {Entry::State::NEW, Entry::State::AVAILABLE,
+ Entry::State::ACTIVE, Entry::State::PAUSED,
+ Entry::State::COMPLETE};
+ for (auto state : states) {
+ ASSERT_EQ(state, RequestStateFromProto(RequestStateToProto(state)));
+ }
+}
+
+TEST_F(ProtoConversionsTest, DownloadClientConversion) {
+ DownloadClient clients[] = {DownloadClient::INVALID, DownloadClient::TEST,
+ DownloadClient::TEST_2, DownloadClient::BOUNDARY};
+ for (auto client : clients) {
+ ASSERT_EQ(client, DownloadClientFromProto(DownloadClientToProto(client)));
+ }
+}
+
+TEST_F(ProtoConversionsTest, NetworkRequirementsConversion) {
+ SchedulingParams::NetworkRequirements values[] = {
+ SchedulingParams::NetworkRequirements::NONE,
+ SchedulingParams::NetworkRequirements::OPTIMISTIC,
+ SchedulingParams::NetworkRequirements::UNMETERED};
+ for (auto value : values) {
+ ASSERT_EQ(value,
+ NetworkRequirementsFromProto(NetworkRequirementsToProto(value)));
+ }
+}
+
+TEST_F(ProtoConversionsTest, BatteryRequirementsConversion) {
+ SchedulingParams::BatteryRequirements values[] = {
+ SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE,
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE};
+ for (auto value : values) {
+ ASSERT_EQ(value,
+ BatteryRequirementsFromProto(BatteryRequirementsToProto(value)));
+ }
+}
+
+TEST_F(ProtoConversionsTest, SchedulingPriorityConversion) {
+ SchedulingParams::Priority values[] = {
+ SchedulingParams::Priority::LOW, SchedulingParams::Priority::NORMAL,
+ SchedulingParams::Priority::HIGH, SchedulingParams::Priority::UI,
+ SchedulingParams::Priority::DEFAULT};
+ for (auto value : values) {
+ ASSERT_EQ(value,
+ SchedulingPriorityFromProto(SchedulingPriorityToProto(value)));
+ }
+}
+
+TEST_F(ProtoConversionsTest, SchedulingParamsConversion) {
+ SchedulingParams expected;
+ expected.cancel_time = base::Time::Now();
+ expected.priority = SchedulingParams::Priority::DEFAULT;
+ expected.network_requirements =
+ SchedulingParams::NetworkRequirements::OPTIMISTIC;
+ expected.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+
+ protodb::SchedulingParams proto;
+ SchedulingParamsToProto(expected, &proto);
+ SchedulingParams actual = SchedulingParamsFromProto(proto);
+ EXPECT_EQ(expected.cancel_time, actual.cancel_time);
+ EXPECT_EQ(SchedulingParams::Priority::DEFAULT, actual.priority);
+ EXPECT_EQ(SchedulingParams::NetworkRequirements::OPTIMISTIC,
+ actual.network_requirements);
+ EXPECT_EQ(SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE,
+ actual.battery_requirements);
+}
+
+TEST_F(ProtoConversionsTest, RequestParamsWithHeadersConversion) {
+ RequestParams expected;
+ expected.url = GURL(TEST_URL);
+ expected.method = "GET";
+ expected.request_headers.SetHeader("key1", "value1");
+ expected.request_headers.SetHeader("key2", "value2");
+
+ protodb::RequestParams proto;
+ RequestParamsToProto(expected, &proto);
+ RequestParams actual = RequestParamsFromProto(proto);
+
+ EXPECT_EQ(expected.url, actual.url);
+ EXPECT_EQ(expected.method, actual.method);
+
+ std::string out;
+ actual.request_headers.GetHeader("key1", &out);
+ EXPECT_EQ("value1", out);
+ actual.request_headers.GetHeader("key2", &out);
+ EXPECT_EQ("value2", out);
+ EXPECT_EQ(expected.request_headers.ToString(),
+ actual.request_headers.ToString());
+}
+
+TEST_F(ProtoConversionsTest, EntryConversion) {
+ Entry expected = test::BuildEntry(DownloadClient::TEST, base::GenerateGUID());
+ Entry actual = EntryFromProto(EntryToProto(expected));
+ EXPECT_TRUE(test::CompareEntry(&expected, &actual));
+
+ expected = test::BuildEntry(
+ DownloadClient::TEST, base::GenerateGUID(), base::Time::Now(),
+ SchedulingParams::NetworkRequirements::OPTIMISTIC,
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE,
+ SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET",
+ Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")),
+ base::Time::Now(), base::Time::Now(), 3);
+ actual = EntryFromProto(EntryToProto(expected));
+ EXPECT_TRUE(test::CompareEntry(&expected, &actual));
+}
+
+TEST_F(ProtoConversionsTest, EntryVectorConversion) {
+ std::vector<Entry> expected;
+ expected.push_back(
+ test::BuildEntry(DownloadClient::TEST, base::GenerateGUID()));
+ expected.push_back(
+ test::BuildEntry(DownloadClient::TEST_2, base::GenerateGUID()));
+ expected.push_back(test::BuildEntry(
+ DownloadClient::TEST, base::GenerateGUID(), base::Time::Now(),
+ SchedulingParams::NetworkRequirements::OPTIMISTIC,
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE,
+ SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET",
+ Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")),
+ base::Time::Now(), base::Time::Now(), 2));
+
+ auto actual = EntryVectorFromProto(
+ EntryVectorToProto(base::MakeUnique<std::vector<Entry>>(expected)));
+ EXPECT_TRUE(test::CompareEntryList(expected, *actual));
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/scheduler/battery_listener.cc b/chromium/components/download/internal/scheduler/battery_listener.cc
deleted file mode 100644
index b015ca15ffd..00000000000
--- a/chromium/components/download/internal/scheduler/battery_listener.cc
+++ /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.
-
-#include "components/download/internal/scheduler/battery_listener.h"
-
-#include "base/power_monitor/power_monitor.h"
-
-namespace download {
-
-// Helper function that converts the battery status to battery requirement.
-SchedulingParams::BatteryRequirements ToBatteryRequirement(
- bool on_battery_power) {
- return on_battery_power
- ? SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE
- : SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
-}
-
-BatteryListener::BatteryListener() = default;
-
-BatteryListener::~BatteryListener() {
- Stop();
-}
-
-SchedulingParams::BatteryRequirements BatteryListener::CurrentBatteryStatus()
- const {
- return ToBatteryRequirement(base::PowerMonitor::Get()->IsOnBatteryPower());
-}
-
-void BatteryListener::Start() {
- base::PowerMonitor* monitor = base::PowerMonitor::Get();
- DCHECK(monitor);
- monitor->AddObserver(this);
-}
-
-void BatteryListener::Stop() {
- base::PowerMonitor::Get()->RemoveObserver(this);
-}
-
-void BatteryListener::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
-}
-
-void BatteryListener::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
-}
-
-void BatteryListener::OnPowerStateChange(bool on_battery_power) {
- NotifyBatteryChange(ToBatteryRequirement(on_battery_power));
-}
-
-void BatteryListener::NotifyBatteryChange(
- SchedulingParams::BatteryRequirements current_battery) {
- for (auto& observer : observers_)
- observer.OnBatteryChange(current_battery);
-}
-
-} // namespace download
diff --git a/chromium/components/download/internal/scheduler/battery_listener.h b/chromium/components/download/internal/scheduler/battery_listener.h
deleted file mode 100644
index 2ddc6109b55..00000000000
--- a/chromium/components/download/internal/scheduler/battery_listener.h
+++ /dev/null
@@ -1,59 +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_INTERNAL_SCHEDULER_BATTERY_LISTENER_H_
-#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_BATTERY_LISTENER_H_
-
-#include <memory>
-
-#include "base/observer_list.h"
-#include "base/power_monitor/power_observer.h"
-#include "components/download/public/download_params.h"
-
-namespace download {
-
-// Listen to battery charing state changes and notify observers in download
-// service.
-// TODO(xingliu): Converts to device service when it's fully done.
-class BatteryListener : public base::PowerObserver {
- public:
- class Observer {
- public:
- // Called when certain battery requirements are meet.
- virtual void OnBatteryChange(
- SchedulingParams::BatteryRequirements battery_status) = 0;
- };
-
- BatteryListener();
- ~BatteryListener() override;
-
- // Retrieves the current minimum battery requirement.
- SchedulingParams::BatteryRequirements CurrentBatteryStatus() const;
-
- // Start to listen to battery change.
- void Start();
-
- // Stop to listen to battery change.
- void Stop();
-
- // Adds/Removes observers.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-
- private:
- // base::PowerObserver implementation.
- void OnPowerStateChange(bool on_battery_power) override;
-
- // Notifies |observers_| about battery requirement change.
- void NotifyBatteryChange(SchedulingParams::BatteryRequirements);
-
- // Observers to monitor battery change in download service.
- base::ObserverList<Observer> observers_;
-
- DISALLOW_COPY_AND_ASSIGN(BatteryListener);
-};
-
-} // namespace download
-
-#endif // COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_BATTERY_LISTENER_H_
diff --git a/chromium/components/download/internal/scheduler/battery_listener_unittest.cc b/chromium/components/download/internal/scheduler/battery_listener_unittest.cc
deleted file mode 100644
index 3ddacffbf10..00000000000
--- a/chromium/components/download/internal/scheduler/battery_listener_unittest.cc
+++ /dev/null
@@ -1,74 +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/download/internal/scheduler/battery_listener.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/power_monitor_test_base.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace download {
-namespace {
-
-using BatteryRequirements = SchedulingParams::BatteryRequirements;
-
-class MockObserver : public BatteryListener::Observer {
- public:
- MOCK_METHOD1(OnBatteryChange, void(SchedulingParams::BatteryRequirements));
-};
-
-class BatteryListenerTest : public testing::Test {
- public:
- BatteryListenerTest() {}
- ~BatteryListenerTest() override = default;
-
- void CallBatteryChange(bool on_battery_power) {
- DCHECK(listener_);
- static_cast<base::PowerObserver*>(listener_.get())
- ->OnPowerStateChange(on_battery_power);
- }
-
- void SetUp() override {
- power_monitor_ = base::MakeUnique<base::PowerMonitor>(
- base::MakeUnique<base::PowerMonitorTestSource>());
-
- observer_ = base::MakeUnique<MockObserver>();
- listener_ = base::MakeUnique<BatteryListener>();
- listener_->AddObserver(observer_.get());
- }
-
- void TearDown() override {
- listener_.reset();
- observer_.reset();
- }
-
- std::unique_ptr<BatteryListener> listener_;
- std::unique_ptr<MockObserver> observer_;
-
- base::MessageLoop message_loop_;
- std::unique_ptr<base::PowerMonitor> power_monitor_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BatteryListenerTest);
-};
-
-// Ensures observer methods are corrected called.
-TEST_F(BatteryListenerTest, NotifyObservers) {
- listener_->Start();
- EXPECT_CALL(*observer_.get(),
- OnBatteryChange(BatteryRequirements::BATTERY_INSENSITIVE))
- .RetiresOnSaturation();
- CallBatteryChange(true);
-
- EXPECT_CALL(*observer_.get(),
- OnBatteryChange(BatteryRequirements::BATTERY_SENSITIVE))
- .RetiresOnSaturation();
- CallBatteryChange(false);
- listener_->Stop();
-};
-
-} // namespace
-} // namespace download
diff --git a/chromium/components/download/internal/scheduler/device_status.cc b/chromium/components/download/internal/scheduler/device_status.cc
new file mode 100644
index 00000000000..80c88c0053d
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/device_status.cc
@@ -0,0 +1,72 @@
+// 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/download/internal/scheduler/device_status.h"
+
+namespace download {
+
+DeviceStatus::Result::Result()
+ : meets_battery_requirement(false), meets_network_requirement(false) {}
+
+bool DeviceStatus::Result::MeetsRequirements() const {
+ return meets_battery_requirement && meets_network_requirement;
+}
+
+DeviceStatus::DeviceStatus()
+ : battery_status(BatteryStatus::NOT_CHARGING),
+ network_status(NetworkStatus::DISCONNECTED) {}
+
+DeviceStatus::DeviceStatus(BatteryStatus battery, NetworkStatus network)
+ : battery_status(battery), network_status(network) {}
+
+bool DeviceStatus::operator==(const DeviceStatus& rhs) const {
+ return network_status == rhs.network_status &&
+ battery_status == rhs.battery_status;
+}
+
+DeviceStatus::Result DeviceStatus::MeetsCondition(
+ const SchedulingParams& params) const {
+ DeviceStatus::Result result;
+
+ switch (params.battery_requirements) {
+ case SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE:
+ result.meets_battery_requirement = true;
+ break;
+ case SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE:
+ result.meets_battery_requirement =
+ battery_status == BatteryStatus::CHARGING;
+ break;
+ default:
+ NOTREACHED();
+ }
+ switch (params.network_requirements) {
+ case SchedulingParams::NetworkRequirements::NONE:
+ result.meets_network_requirement =
+ network_status != NetworkStatus::DISCONNECTED;
+ break;
+ case SchedulingParams::NetworkRequirements::OPTIMISTIC:
+ case SchedulingParams::NetworkRequirements::UNMETERED:
+ result.meets_network_requirement =
+ network_status == NetworkStatus::UNMETERED;
+ break;
+ default:
+ NOTREACHED();
+ }
+ return result;
+}
+
+Criteria::Criteria()
+ : requires_battery_charging(true), requires_unmetered_network(true) {}
+
+Criteria::Criteria(bool requires_battery_charging,
+ bool requires_unmetered_network)
+ : requires_battery_charging(requires_battery_charging),
+ requires_unmetered_network(requires_unmetered_network) {}
+
+bool Criteria::operator==(const Criteria& other) const {
+ return requires_battery_charging == other.requires_battery_charging &&
+ requires_unmetered_network == other.requires_unmetered_network;
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/scheduler/device_status.h b/chromium/components/download/internal/scheduler/device_status.h
new file mode 100644
index 00000000000..720a1266f0f
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/device_status.h
@@ -0,0 +1,60 @@
+// 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_INTERNAL_SCHEDULER_DEVICE_STATUS_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_DEVICE_STATUS_H_
+
+#include "components/download/public/download_params.h"
+
+namespace download {
+
+// Battery status used in download service.
+enum class BatteryStatus {
+ CHARGING = 0,
+ NOT_CHARGING = 1,
+};
+
+// NetworkStatus should mostly one to one map to
+// SchedulingParams::NetworkRequirements. Has coarser granularity than
+// network connection type.
+enum class NetworkStatus {
+ DISCONNECTED = 0,
+ UNMETERED = 1, // WIFI or Ethernet.
+ METERED = 2, // Mobile networks.
+};
+
+// Contains battery and network status.
+struct DeviceStatus {
+ DeviceStatus();
+ DeviceStatus(BatteryStatus battery, NetworkStatus network);
+
+ struct Result {
+ Result();
+ bool MeetsRequirements() const;
+ bool meets_battery_requirement;
+ bool meets_network_requirement;
+ };
+
+ BatteryStatus battery_status;
+ NetworkStatus network_status;
+
+ bool operator==(const DeviceStatus& rhs) const;
+
+ // Returns if the current device status meets all the conditions defined in
+ // the scheduling parameters.
+ Result MeetsCondition(const SchedulingParams& params) const;
+};
+
+// The criteria when the background download task should start.
+struct Criteria {
+ Criteria();
+ Criteria(bool requires_battery_charging, bool requires_unmetered_network);
+ bool operator==(const Criteria& other) const;
+ bool requires_battery_charging;
+ bool requires_unmetered_network;
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_DEVICE_STATUS_H_
diff --git a/chromium/components/download/internal/scheduler/device_status_listener.cc b/chromium/components/download/internal/scheduler/device_status_listener.cc
new file mode 100644
index 00000000000..ba277b04489
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/device_status_listener.cc
@@ -0,0 +1,122 @@
+// 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/download/internal/scheduler/device_status_listener.h"
+
+#include "base/power_monitor/power_monitor.h"
+
+namespace download {
+
+namespace {
+
+// Converts |on_battery_power| to battery status.
+BatteryStatus ToBatteryStatus(bool on_battery_power) {
+ return on_battery_power ? BatteryStatus::NOT_CHARGING
+ : BatteryStatus::CHARGING;
+}
+
+// Converts a ConnectionType to NetworkStatus.
+NetworkStatus ToNetworkStatus(net::NetworkChangeNotifier::ConnectionType type) {
+ switch (type) {
+ case net::NetworkChangeNotifier::CONNECTION_ETHERNET:
+ case net::NetworkChangeNotifier::CONNECTION_WIFI:
+ return NetworkStatus::UNMETERED;
+ case net::NetworkChangeNotifier::CONNECTION_2G:
+ case net::NetworkChangeNotifier::CONNECTION_3G:
+ case net::NetworkChangeNotifier::CONNECTION_4G:
+ return NetworkStatus::METERED;
+ case net::NetworkChangeNotifier::CONNECTION_UNKNOWN:
+ case net::NetworkChangeNotifier::CONNECTION_NONE:
+ case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ return NetworkStatus::DISCONNECTED;
+ }
+ NOTREACHED();
+ return NetworkStatus::DISCONNECTED;
+}
+
+} // namespace
+
+DeviceStatusListener::DeviceStatusListener(const base::TimeDelta& delay)
+ : observer_(nullptr), listening_(false), delay_(delay) {}
+
+DeviceStatusListener::~DeviceStatusListener() {
+ Stop();
+}
+
+const DeviceStatus& DeviceStatusListener::CurrentDeviceStatus() const {
+ DCHECK(listening_) << "Call Start() before querying the status.";
+ return status_;
+}
+
+void DeviceStatusListener::Start(DeviceStatusListener::Observer* observer) {
+ if (listening_)
+ return;
+
+ DCHECK(observer);
+ observer_ = observer;
+ base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
+ DCHECK(power_monitor);
+ power_monitor->AddObserver(this);
+
+ net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
+
+ status_.battery_status =
+ ToBatteryStatus(base::PowerMonitor::Get()->IsOnBatteryPower());
+ status_.network_status =
+ ToNetworkStatus(net::NetworkChangeNotifier::GetConnectionType());
+ listening_ = true;
+}
+
+void DeviceStatusListener::Stop() {
+ if (!listening_)
+ return;
+
+ base::PowerMonitor::Get()->RemoveObserver(this);
+ net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
+
+ status_ = DeviceStatus();
+ listening_ = false;
+ observer_ = nullptr;
+}
+
+void DeviceStatusListener::OnConnectionTypeChanged(
+ net::NetworkChangeNotifier::ConnectionType type) {
+ NetworkStatus new_network_status = ToNetworkStatus(type);
+ if (status_.network_status == new_network_status)
+ return;
+
+ bool change_to_online =
+ (status_.network_status == NetworkStatus::DISCONNECTED) &&
+ (new_network_status != NetworkStatus::DISCONNECTED);
+
+ // It's unreliable to send requests immediately after the network becomes
+ // online. Notify network change to the observer after a delay.
+ if (change_to_online) {
+ timer_.Start(
+ FROM_HERE, delay_,
+ base::Bind(&DeviceStatusListener::NotifyNetworkChangeAfterDelay,
+ base::Unretained(this), new_network_status));
+ } else {
+ status_.network_status = new_network_status;
+ timer_.Stop();
+ NotifyStatusChange();
+ }
+}
+
+void DeviceStatusListener::OnPowerStateChange(bool on_battery_power) {
+ status_.battery_status = ToBatteryStatus(on_battery_power);
+ NotifyStatusChange();
+}
+
+void DeviceStatusListener::NotifyStatusChange() {
+ observer_->OnDeviceStatusChanged(status_);
+}
+
+void DeviceStatusListener::NotifyNetworkChangeAfterDelay(
+ NetworkStatus network_status) {
+ status_.network_status = network_status;
+ NotifyStatusChange();
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/scheduler/device_status_listener.h b/chromium/components/download/internal/scheduler/device_status_listener.h
new file mode 100644
index 00000000000..a14a28c6f84
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/device_status_listener.h
@@ -0,0 +1,72 @@
+// 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_INTERNAL_SCHEDULER_DEVICE_STATUS_LISTENER_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_DEVICE_STATUS_LISTENER_H_
+
+#include "base/power_monitor/power_observer.h"
+#include "base/timer/timer.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "net/base/network_change_notifier.h"
+
+namespace download {
+
+// Listens to network and battery status change and notifies the observer.
+class DeviceStatusListener
+ : public net::NetworkChangeNotifier::ConnectionTypeObserver,
+ public base::PowerObserver {
+ public:
+ class Observer {
+ public:
+ // Called when device status is changed.
+ virtual void OnDeviceStatusChanged(const DeviceStatus& device_status) = 0;
+ };
+
+ DeviceStatusListener(const base::TimeDelta& delay);
+ ~DeviceStatusListener() override;
+
+ // Returns the current device status for download scheduling.
+ const DeviceStatus& CurrentDeviceStatus() const;
+
+ // Starts/stops to listen network and battery change events, virtual for
+ // testing.
+ virtual void Start(DeviceStatusListener::Observer* observer);
+ virtual void Stop();
+
+ protected:
+ // The current device status.
+ DeviceStatus status_;
+
+ // The observer that listens to device status change events.
+ Observer* observer_;
+
+ // If we are actively listening to network and battery change events.
+ bool listening_;
+
+ private:
+ // net::NetworkChangeNotifier::ConnectionTypeObserver implementation.
+ void OnConnectionTypeChanged(
+ net::NetworkChangeNotifier::ConnectionType type) override;
+
+ // base::PowerObserver implementation.
+ void OnPowerStateChange(bool on_battery_power) override;
+
+ // Notifies the observer about device status change.
+ void NotifyStatusChange();
+
+ // Called after a delay to notify the observer. See |delay_|.
+ void NotifyNetworkChangeAfterDelay(NetworkStatus network_status);
+
+ // Used to notify the observer after a delay when network becomes connected.
+ base::OneShotTimer timer_;
+
+ // The delay used by |timer_|.
+ base::TimeDelta delay_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceStatusListener);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_DEVICE_STATUS_LISTENER_H_
diff --git a/chromium/components/download/internal/scheduler/device_status_listener_unittest.cc b/chromium/components/download/internal/scheduler/device_status_listener_unittest.cc
new file mode 100644
index 00000000000..45fea61651c
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/device_status_listener_unittest.cc
@@ -0,0 +1,146 @@
+// 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/download/internal/scheduler/device_status_listener.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/power_monitor_test_base.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::InSequence;
+using ConnectionTypeObserver =
+ net::NetworkChangeNotifier::ConnectionTypeObserver;
+using ConnectionType = net::NetworkChangeNotifier::ConnectionType;
+
+namespace download {
+namespace {
+
+// NetworkChangeNotifier that can change network type in tests.
+class TestNetworkChangeNotifier : public net::NetworkChangeNotifier {
+ public:
+ TestNetworkChangeNotifier()
+ : net::NetworkChangeNotifier(),
+ conn_type_(ConnectionType::CONNECTION_UNKNOWN) {}
+
+ // net::NetworkChangeNotifier implementation.
+ ConnectionType GetCurrentConnectionType() const override {
+ return conn_type_;
+ }
+
+ // Changes the network type.
+ void ChangeNetworkType(ConnectionType type) {
+ conn_type_ = type;
+ net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
+ type);
+ }
+
+ private:
+ ConnectionType conn_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifier);
+};
+
+class MockObserver : public DeviceStatusListener::Observer {
+ public:
+ MOCK_METHOD1(OnDeviceStatusChanged, void(const DeviceStatus&));
+};
+
+class DeviceStatusListenerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ power_monitor_ = base::MakeUnique<base::PowerMonitor>(
+ base::MakeUnique<base::PowerMonitorTestSource>());
+
+ listener_ =
+ base::MakeUnique<DeviceStatusListener>(base::TimeDelta::FromSeconds(0));
+ }
+
+ void TearDown() override { listener_.reset(); }
+
+ // Simulates a network change call.
+ void ChangeNetworkType(ConnectionType type) {
+ test_network_notifier_.ChangeNetworkType(type);
+ }
+
+ // Simulates a battery change call.
+ void SimulateBatteryChange(bool on_battery_power) {
+ static_cast<base::PowerObserver*>(listener_.get())
+ ->OnPowerStateChange(on_battery_power);
+ }
+
+ protected:
+ std::unique_ptr<DeviceStatusListener> listener_;
+ MockObserver mock_observer_;
+
+ // Needed for network change notifier and power monitor.
+ base::MessageLoop message_loop_;
+ TestNetworkChangeNotifier test_network_notifier_;
+ std::unique_ptr<base::PowerMonitor> power_monitor_;
+};
+
+// Ensures the observer is notified when network condition changes.
+TEST_F(DeviceStatusListenerTest, NotifyObserverNetworkChange) {
+ listener_->Start(&mock_observer_);
+
+ // Initial states check.
+ DeviceStatus status = listener_->CurrentDeviceStatus();
+ EXPECT_EQ(NetworkStatus::DISCONNECTED, status.network_status);
+
+ // Network switch between mobile networks, the observer should be notified
+ // only once.
+ status.network_status = NetworkStatus::METERED;
+ EXPECT_CALL(mock_observer_, OnDeviceStatusChanged(status))
+ .Times(1)
+ .RetiresOnSaturation();
+
+ ChangeNetworkType(ConnectionType::CONNECTION_4G);
+ ChangeNetworkType(ConnectionType::CONNECTION_3G);
+ ChangeNetworkType(ConnectionType::CONNECTION_2G);
+
+ // Verifies the online signal is sent in a post task after a delay.
+ EXPECT_EQ(NetworkStatus::DISCONNECTED,
+ listener_->CurrentDeviceStatus().network_status);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(NetworkStatus::METERED,
+ listener_->CurrentDeviceStatus().network_status);
+
+ // Network is switched between wifi and ethernet, the observer should be
+ // notified only once.
+ status.network_status = NetworkStatus::UNMETERED;
+ EXPECT_CALL(mock_observer_, OnDeviceStatusChanged(status))
+ .Times(1)
+ .RetiresOnSaturation();
+
+ ChangeNetworkType(ConnectionType::CONNECTION_WIFI);
+ ChangeNetworkType(ConnectionType::CONNECTION_ETHERNET);
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(NetworkStatus::UNMETERED,
+ listener_->CurrentDeviceStatus().network_status);
+}
+
+// Ensures the observer is notified when battery condition changes.
+TEST_F(DeviceStatusListenerTest, NotifyObserverBatteryChange) {
+ InSequence s;
+ listener_->Start(&mock_observer_);
+ DeviceStatus status = listener_->CurrentDeviceStatus();
+ status.battery_status = BatteryStatus::NOT_CHARGING;
+ EXPECT_CALL(mock_observer_, OnDeviceStatusChanged(status))
+ .RetiresOnSaturation();
+ SimulateBatteryChange(true);
+
+ status.battery_status = BatteryStatus::CHARGING;
+ EXPECT_CALL(mock_observer_, OnDeviceStatusChanged(status))
+ .RetiresOnSaturation();
+ SimulateBatteryChange(false);
+ listener_->Stop();
+};
+
+} // namespace
+} // namespace download
diff --git a/chromium/components/download/internal/scheduler/network_listener.cc b/chromium/components/download/internal/scheduler/network_listener.cc
deleted file mode 100644
index d09d52773ee..00000000000
--- a/chromium/components/download/internal/scheduler/network_listener.cc
+++ /dev/null
@@ -1,84 +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/download/internal/scheduler/network_listener.h"
-
-namespace download {
-
-namespace {
-
-// Converts a ConnectionType to NetworkListener::NetworkStatus.
-NetworkListener::NetworkStatus ToNetworkStatus(
- net::NetworkChangeNotifier::ConnectionType type) {
- switch (type) {
- case net::NetworkChangeNotifier::CONNECTION_ETHERNET:
- case net::NetworkChangeNotifier::CONNECTION_WIFI:
- return NetworkListener::NetworkStatus::UNMETERED;
- case net::NetworkChangeNotifier::CONNECTION_2G:
- case net::NetworkChangeNotifier::CONNECTION_3G:
- case net::NetworkChangeNotifier::CONNECTION_4G:
- return NetworkListener::NetworkStatus::METERED;
- case net::NetworkChangeNotifier::CONNECTION_UNKNOWN:
- case net::NetworkChangeNotifier::CONNECTION_NONE:
- case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH:
- return NetworkListener::NetworkStatus::DISCONNECTED;
- }
- NOTREACHED();
- return NetworkListener::NetworkStatus::DISCONNECTED;
-}
-
-} // namespace
-
-NetworkListener::NetworkListener()
- : status_(NetworkStatus::DISCONNECTED), listening_(false) {}
-
-NetworkListener::~NetworkListener() {
- Stop();
-}
-
-NetworkListener::NetworkStatus NetworkListener::CurrentNetworkStatus() const {
- return ToNetworkStatus(net::NetworkChangeNotifier::GetConnectionType());
-}
-
-void NetworkListener::Start() {
- if (listening_)
- return;
-
- net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
- status_ = ToNetworkStatus(net::NetworkChangeNotifier::GetConnectionType());
- listening_ = true;
-}
-
-void NetworkListener::Stop() {
- if (!listening_)
- return;
-
- net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
- status_ = ToNetworkStatus(net::NetworkChangeNotifier::CONNECTION_UNKNOWN);
- listening_ = false;
-}
-
-void NetworkListener::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
-}
-
-void NetworkListener::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
-}
-
-void NetworkListener::OnConnectionTypeChanged(
- net::NetworkChangeNotifier::ConnectionType type) {
- NetworkStatus new_status = ToNetworkStatus(type);
- if (status_ != new_status) {
- status_ = new_status;
- NotifyNetworkChange(status_);
- }
-}
-
-void NetworkListener::NotifyNetworkChange(NetworkStatus network_status) {
- for (auto& observer : observers_)
- observer.OnNetworkChange(network_status);
-}
-
-} // namespace download
diff --git a/chromium/components/download/internal/scheduler/network_listener.h b/chromium/components/download/internal/scheduler/network_listener.h
deleted file mode 100644
index 6b05ffb16b0..00000000000
--- a/chromium/components/download/internal/scheduler/network_listener.h
+++ /dev/null
@@ -1,66 +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_INTERNAL_SCHEDULER_NETWORK_LISTENER_H_
-#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_NETWORK_LISTENER_H_
-
-#include "net/base/network_change_notifier.h"
-
-namespace download {
-
-// Listens to network status change and notifies observers in download service.
-class NetworkListener
- : public net::NetworkChangeNotifier::ConnectionTypeObserver {
- public:
- // NetworkStatus should mostly one to one map to
- // SchedulingParams::NetworkRequirements. Has coarser granularity than
- // network connection type.
- enum class NetworkStatus {
- DISCONNECTED = 0,
- UNMETERED = 1, // WIFI or Ethernet.
- METERED = 2, // Mobile networks.
- };
-
- class Observer {
- public:
- // Called when network status is changed.
- virtual void OnNetworkChange(NetworkStatus network_status) = 0;
- };
-
- NetworkListener();
- ~NetworkListener() override;
-
- // Returns the current network status for download scheduling.
- NetworkStatus CurrentNetworkStatus() const;
-
- // Starts/stops to listen network change events.
- void Start();
- void Stop();
-
- // Adds/Removes observers.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-
- private:
- // net::NetworkChangeNotifier implementation.
- void OnConnectionTypeChanged(
- net::NetworkChangeNotifier::ConnectionType type) override;
-
- // Notifies |observers_| about network status change. See |NetworkStatus|.
- void NotifyNetworkChange(NetworkStatus network_status);
-
- // The current network status.
- NetworkStatus status_;
-
- // If we are actively listening to network change events.
- bool listening_;
-
- base::ObserverList<Observer> observers_;
-
- DISALLOW_COPY_AND_ASSIGN(NetworkListener);
-};
-
-} // namespace download
-
-#endif // COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_NETWORK_LISTENER_H_
diff --git a/chromium/components/download/internal/scheduler/network_listener_unittest.cc b/chromium/components/download/internal/scheduler/network_listener_unittest.cc
deleted file mode 100644
index 4b8165dcef1..00000000000
--- a/chromium/components/download/internal/scheduler/network_listener_unittest.cc
+++ /dev/null
@@ -1,100 +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/download/internal/scheduler/network_listener.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ConnectionTypeObserver =
- net::NetworkChangeNotifier::ConnectionTypeObserver;
-using ConnectionType = net::NetworkChangeNotifier::ConnectionType;
-
-namespace download {
-namespace {
-
-// NetworkChangeNotifier that can change network type in tests.
-class TestNetworkChangeNotifier : public net::NetworkChangeNotifier {
- public:
- TestNetworkChangeNotifier()
- : net::NetworkChangeNotifier(),
- conn_type_(ConnectionType::CONNECTION_UNKNOWN) {}
-
- // net::NetworkChangeNotifier implementation.
- ConnectionType GetCurrentConnectionType() const override {
- return conn_type_;
- }
-
- // Change the network type.
- void ChangeNetworkType(ConnectionType type) {
- conn_type_ = type;
- net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
- type);
- base::RunLoop().RunUntilIdle();
- }
-
- private:
- ConnectionType conn_type_;
-
- DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifier);
-};
-
-class MockObserver : public NetworkListener::Observer {
- public:
- MOCK_METHOD1(OnNetworkChange, void(NetworkListener::NetworkStatus));
-};
-
-class NetworkListenerTest : public testing::Test {
- public:
- // Simulates a network change call.
- void ChangeNetworkType(ConnectionType type) {
- test_network_notifier_.ChangeNetworkType(type);
- }
-
- protected:
- NetworkListener network_listener_;
- MockObserver mock_observer_;
-
- // Needed for network change notifier.
- base::MessageLoop message_loop_;
- TestNetworkChangeNotifier test_network_notifier_;
-};
-
-TEST_F(NetworkListenerTest, NotifyObserverNetworkChange) {
- network_listener_.Start();
- network_listener_.AddObserver(&mock_observer_);
-
- // Initial states check.
- EXPECT_EQ(NetworkListener::NetworkStatus::DISCONNECTED,
- network_listener_.CurrentNetworkStatus());
-
- // Network switch between mobile networks, the observer should be notified
- // only once.
- EXPECT_CALL(mock_observer_,
- OnNetworkChange(NetworkListener::NetworkStatus::METERED))
- .Times(1)
- .RetiresOnSaturation();
-
- ChangeNetworkType(ConnectionType::CONNECTION_4G);
- ChangeNetworkType(ConnectionType::CONNECTION_3G);
- ChangeNetworkType(ConnectionType::CONNECTION_2G);
- EXPECT_EQ(NetworkListener::NetworkStatus::METERED,
- network_listener_.CurrentNetworkStatus());
-
- // Network is switched between wifi and ethernet, the observer should be
- // notified only once.
- EXPECT_CALL(mock_observer_,
- OnNetworkChange(NetworkListener::NetworkStatus::UNMETERED))
- .Times(1)
- .RetiresOnSaturation();
-
- ChangeNetworkType(ConnectionType::CONNECTION_WIFI);
- ChangeNetworkType(ConnectionType::CONNECTION_ETHERNET);
-}
-
-} // namespace
-} // namespace download
diff --git a/chromium/components/download/internal/scheduler/scheduler.h b/chromium/components/download/internal/scheduler/scheduler.h
new file mode 100644
index 00000000000..58d835a3ab3
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/scheduler.h
@@ -0,0 +1,36 @@
+// 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_INTERNAL_SCHEDULER_SCHEDULER_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_H_
+
+#include "components/download/internal/model.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "components/download/public/download_params.h"
+
+namespace download {
+
+struct DeviceStatus;
+
+// The interface that talks to download service to schedule platform background
+// download tasks.
+class Scheduler {
+ public:
+ // Reschedule another background platform task based on the scheduling
+ // parameters of |entries|. Should only pass in entries in active or available
+ // state.
+ virtual void Reschedule(const Model::EntryList& entries) = 0;
+
+ // Returns the next download that should be processed based on scheduling
+ // parameters, may return nullptr if no download meets the criteria.
+ // The sequence of polling on entries with exactly same states is undefined.
+ virtual Entry* Next(const Model::EntryList& entries,
+ const DeviceStatus& device_status) = 0;
+
+ virtual ~Scheduler() {}
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_CORE_SCHEDULER_H_
diff --git a/chromium/components/download/internal/scheduler/scheduler_impl.cc b/chromium/components/download/internal/scheduler/scheduler_impl.cc
new file mode 100644
index 00000000000..8ee8f7381f6
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/scheduler_impl.cc
@@ -0,0 +1,131 @@
+// 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/download/internal/scheduler/scheduler_impl.h"
+
+#include "components/download/internal/client_set.h"
+#include "components/download/internal/config.h"
+#include "components/download/internal/entry_utils.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "components/download/public/download_params.h"
+#include "components/download/public/task_scheduler.h"
+
+namespace download {
+
+namespace {
+
+// Returns a vector of elements contained in the |set|.
+template <typename T>
+std::vector<T> ToList(const std::set<T>& set) {
+ std::vector<T> list;
+ for (const auto& element : set) {
+ list.push_back(element);
+ }
+ return list;
+}
+
+} // namespace
+
+SchedulerImpl::SchedulerImpl(TaskScheduler* task_scheduler,
+ Configuration* config,
+ const ClientSet* clients)
+ : SchedulerImpl(task_scheduler,
+ config,
+ ToList<DownloadClient>(clients->GetRegisteredClients())) {}
+
+SchedulerImpl::SchedulerImpl(TaskScheduler* task_scheduler,
+ Configuration* config,
+ const std::vector<DownloadClient>& clients)
+ : task_scheduler_(task_scheduler),
+ config_(config),
+ download_clients_(clients),
+ current_client_index_(0) {
+ DCHECK(task_scheduler_);
+}
+
+SchedulerImpl::~SchedulerImpl() = default;
+
+void SchedulerImpl::Reschedule(const Model::EntryList& entries) {
+ if (entries.empty()) {
+ task_scheduler_->CancelTask(DownloadTaskType::DOWNLOAD_TASK);
+ return;
+ }
+
+ // TODO(xingliu): Support NetworkRequirements::OPTIMISTIC.
+ task_scheduler_->CancelTask(DownloadTaskType::DOWNLOAD_TASK);
+
+ Criteria criteria = util::GetSchedulingCriteria(entries);
+ task_scheduler_->ScheduleTask(
+ DownloadTaskType::DOWNLOAD_TASK, criteria.requires_unmetered_network,
+ criteria.requires_battery_charging,
+ base::saturated_cast<long>(config_->window_start_time.InSeconds()),
+ base::saturated_cast<long>(config_->window_end_time.InSeconds()));
+}
+
+Entry* SchedulerImpl::Next(const Model::EntryList& entries,
+ const DeviceStatus& device_status) {
+ std::map<DownloadClient, Entry*> candidates =
+ FindCandidates(entries, device_status);
+
+ Entry* entry = nullptr;
+ size_t index = current_client_index_;
+
+ // Finds the next entry to download.
+ for (size_t i = 0; i < download_clients_.size(); ++i) {
+ DownloadClient client =
+ download_clients_[(index + i) % download_clients_.size()];
+ Entry* candidate = candidates[client];
+
+ // Some clients may have no entries, continue to check other clients.
+ if (!candidate)
+ continue;
+
+ bool ui_priority =
+ candidate->scheduling_params.priority == SchedulingParams::Priority::UI;
+
+ // Records the first available candidate. Keep iterating to see if there
+ // are UI priority entries for other clients.
+ if (!entry || ui_priority) {
+ entry = candidate;
+
+ // Load balancing between clients.
+ current_client_index_ = (index + i + 1) % download_clients_.size();
+
+ // UI priority entry will be processed immediately.
+ if (ui_priority)
+ break;
+ }
+ }
+ return entry;
+}
+
+std::map<DownloadClient, Entry*> SchedulerImpl::FindCandidates(
+ const Model::EntryList& entries,
+ const DeviceStatus& device_status) {
+ std::map<DownloadClient, Entry*> candidates;
+
+ if (entries.empty())
+ return candidates;
+
+ for (auto* const entry : entries) {
+ DCHECK(entry);
+ const SchedulingParams& current_params = entry->scheduling_params;
+
+ // Every download needs to pass the state and device status check.
+ if (entry->state != Entry::State::AVAILABLE ||
+ !device_status.MeetsCondition(current_params).MeetsRequirements()) {
+ continue;
+ }
+
+ // Find the most appropriate download based on priority and cancel time.
+ Entry* candidate = candidates[entry->client];
+ if (!candidate || util::EntryBetterThan(*entry, *candidate)) {
+ candidates[entry->client] = entry;
+ }
+ }
+
+ return candidates;
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/scheduler/scheduler_impl.h b/chromium/components/download/internal/scheduler/scheduler_impl.h
new file mode 100644
index 00000000000..1284d38fe0b
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/scheduler_impl.h
@@ -0,0 +1,72 @@
+// 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_INTERNAL_SCHEDULER_SCHEDULER_IMPL_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_IMPL_H_
+
+#include "components/download/internal/scheduler/scheduler.h"
+
+#include <map>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/download/internal/entry.h"
+
+namespace download {
+
+class ClientSet;
+class TaskScheduler;
+struct Configuration;
+
+// Scheduler implementation that
+// 1. Creates platform background task based on the states of download entries.
+// 2. Polls the next entry to be processed by the service mainly according to
+// scheduling parameters and current device status.
+//
+// Provides load balancing between download clients using the service.
+class SchedulerImpl : public Scheduler {
+ public:
+ SchedulerImpl(TaskScheduler* task_scheduler,
+ Configuration* config,
+ const ClientSet* clients);
+ SchedulerImpl(TaskScheduler* task_scheduler,
+ Configuration* config,
+ const std::vector<DownloadClient>& clients);
+ ~SchedulerImpl() override;
+
+ // Scheduler implementation.
+ void Reschedule(const Model::EntryList& entries) override;
+ Entry* Next(const Model::EntryList& entries,
+ const DeviceStatus& device_status) override;
+
+ private:
+ // Finds a candidate for each download client to be processed next by the
+ // service.
+ // The candidates are selected based on scheduling parameters and current
+ // device status.
+ std::map<DownloadClient, Entry*> FindCandidates(
+ const Model::EntryList& entries,
+ const DeviceStatus& device_status);
+
+ // Used to create platform dependent background tasks.
+ TaskScheduler* task_scheduler_;
+
+ // Download service configuration.
+ Configuration* config_;
+
+ // List of all download client id, used in round robin load balancing.
+ // Downloads will be delivered to clients with incremental order based on
+ // the index of this list.
+ const std::vector<DownloadClient> download_clients_;
+
+ // The index of the current client.
+ // See |download_clients_|.
+ size_t current_client_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(SchedulerImpl);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_IMPL_H_
diff --git a/chromium/components/download/internal/scheduler/scheduler_impl_unittest.cc b/chromium/components/download/internal/scheduler/scheduler_impl_unittest.cc
new file mode 100644
index 00000000000..482d8141805
--- /dev/null
+++ b/chromium/components/download/internal/scheduler/scheduler_impl_unittest.cc
@@ -0,0 +1,429 @@
+// 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/download/internal/scheduler/scheduler_impl.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/download/internal/config.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "components/download/public/task_scheduler.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::InSequence;
+
+namespace download {
+namespace {
+
+class MockTaskScheduler : public TaskScheduler {
+ public:
+ MockTaskScheduler() = default;
+ ~MockTaskScheduler() override = default;
+
+ MOCK_METHOD5(ScheduleTask, void(DownloadTaskType, bool, bool, long, long));
+ MOCK_METHOD1(CancelTask, void(DownloadTaskType));
+};
+
+class DownloadSchedulerImplTest : public testing::Test {
+ public:
+ DownloadSchedulerImplTest() {}
+ ~DownloadSchedulerImplTest() override = default;
+
+ void TearDown() override { DestroyScheduler(); }
+
+ void BuildScheduler(const std::vector<DownloadClient> clients) {
+ scheduler_ =
+ base::MakeUnique<SchedulerImpl>(&task_scheduler_, &config_, clients);
+ }
+ void DestroyScheduler() { scheduler_.reset(); }
+
+ // Helper function to create a list of entries for the scheduler to query the
+ // next entry.
+ void BuildDataEntries(size_t size) {
+ entries_ = std::vector<Entry>(size, Entry());
+ for (size_t i = 0; i < size; ++i) {
+ entries_[i].guid = base::IntToString(i);
+ entries_[i].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+ entries_[i].scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::UNMETERED;
+ entries_[i].state = Entry::State::AVAILABLE;
+ }
+ }
+
+ // Returns list of entry pointers to feed to the scheduler.
+ Model::EntryList entries() {
+ Model::EntryList entry_list;
+ for (auto& entry : entries_) {
+ entry_list.emplace_back(&entry);
+ }
+ return entry_list;
+ }
+
+ // Simulates the entry has been processed by the download service and the
+ // state has changed.
+ void MakeEntryActive(Entry* entry) {
+ if (entry)
+ entry->state = Entry::State::ACTIVE;
+ }
+
+ // Reverts the states of entry so that the scheduler can poll it again.
+ void MakeEntryAvailable(Entry* entry) {
+ entry->state = Entry::State::AVAILABLE;
+ }
+
+ // Helper function to build a device status.
+ DeviceStatus BuildDeviceStatus(BatteryStatus battery, NetworkStatus network) {
+ DeviceStatus device_status;
+ device_status.battery_status = battery;
+ device_status.network_status = network;
+ return device_status;
+ }
+
+ protected:
+ std::unique_ptr<SchedulerImpl> scheduler_;
+ MockTaskScheduler task_scheduler_;
+ Configuration config_;
+
+ // Entries owned by the test fixture.
+ std::vector<Entry> entries_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DownloadSchedulerImplTest);
+};
+
+// Ensures normal polling logic is correct.
+TEST_F(DownloadSchedulerImplTest, BasicPolling) {
+ BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST_2,
+ DownloadClient::TEST});
+
+ // Client TEST: entry 0.
+ // Client TEST_2: entry 1.
+ // Poll sequence: 1 -> 0.
+ BuildDataEntries(2);
+ entries_[0].client = DownloadClient::TEST;
+ entries_[1].client = DownloadClient::TEST_2;
+
+ // First download belongs to first client.
+ Entry* next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(next, &entries_[1]);
+ MakeEntryActive(next);
+
+ // If the first one is processed, the next should be the other entry.
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(next, &entries_[0]);
+ MakeEntryActive(next);
+}
+
+// Tests the load balancing and polling downloads based on cancel time.
+TEST_F(DownloadSchedulerImplTest, BasicLoadBalancing) {
+ BuildScheduler(std::vector<DownloadClient>{
+ DownloadClient::TEST, DownloadClient::TEST_2, DownloadClient::TEST_3});
+
+ // Client TEST: entry 0, entry 1 (earlier cancel time).
+ // Client TEST_2: entry 2.
+ // Client TEST_3: No entries.
+ // Poll sequence: 1 -> 2 -> 0.
+ BuildDataEntries(3);
+ entries_[0].client = DownloadClient::TEST;
+ entries_[0].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+ entries_[1].client = DownloadClient::TEST;
+ entries_[1].scheduling_params.cancel_time = base::Time::FromInternalValue(10);
+ entries_[2].client = DownloadClient::TEST_2;
+ entries_[2].scheduling_params.cancel_time = base::Time::FromInternalValue(30);
+
+ // There are 2 downloads for client 0, the one with earlier create time will
+ // be the next download.
+ Entry* next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[1], next);
+ MakeEntryActive(next);
+
+ // The second download should belongs to client 1, because of the round robin
+ // load balancing.
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[2], next);
+ MakeEntryActive(next);
+
+ // Only one entry left, which will be the next.
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[0], next);
+ MakeEntryActive(next);
+
+ // Keep polling twice, since no available downloads, both will return nullptr.
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(nullptr, next);
+ MakeEntryActive(next);
+
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(nullptr, next);
+ MakeEntryActive(next);
+}
+
+// Ensures downloads are polled based on scheduling parameters and device
+// status.
+TEST_F(DownloadSchedulerImplTest, SchedulingParams) {
+ BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST});
+ BuildDataEntries(1);
+ entries_[0].client = DownloadClient::TEST;
+ entries_[0].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+ entries_[0].scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::UNMETERED;
+
+ Entry* next = nullptr;
+
+ // Tests network scheduling parameter.
+ // No downloads can be polled when network disconnected.
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::DISCONNECTED));
+ EXPECT_EQ(nullptr, next);
+
+ // If the network is metered, and scheduling parameter requires unmetered
+ // network, the download should not be polled.
+ next = scheduler_->Next(entries(), BuildDeviceStatus(BatteryStatus::CHARGING,
+ NetworkStatus::METERED));
+ EXPECT_EQ(nullptr, next);
+
+ // If the network requirement is none, the download can happen under metered
+ // network. However, download won't happen when network is disconnected.
+ entries_[0].scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::NONE;
+ next = scheduler_->Next(entries(), BuildDeviceStatus(BatteryStatus::CHARGING,
+ NetworkStatus::METERED));
+ EXPECT_EQ(&entries_[0], next);
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::DISCONNECTED));
+ EXPECT_EQ(nullptr, next);
+ MakeEntryActive(next);
+
+ // Tests battery sensitive scheduling parameter.
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[0], next);
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(nullptr, next);
+ MakeEntryActive(next);
+
+ entries_[0].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[0], next);
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[0], next);
+ MakeEntryActive(next);
+}
+
+// Ensures higher priority will be scheduled first.
+TEST_F(DownloadSchedulerImplTest, Priority) {
+ BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST});
+
+ // The second entry has higher priority but is created later than the first
+ // entry. This ensures priority is checked before the create time.
+ BuildDataEntries(2);
+ entries_[0].client = DownloadClient::TEST;
+ entries_[0].scheduling_params.priority = SchedulingParams::Priority::LOW;
+ entries_[0].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+ entries_[1].client = DownloadClient::TEST;
+ entries_[1].scheduling_params.priority = SchedulingParams::Priority::HIGH;
+ entries_[1].scheduling_params.cancel_time = base::Time::FromInternalValue(40);
+
+ // Download with higher priority should be polled first, even if there is
+ // another download created earlier.
+ Entry* next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[1], next);
+
+ // Download with non UI priority should be subject to network and battery
+ // scheduling parameters. The higher priority one will be ignored because of
+ // mismatching battery condition.
+ entries_[1].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+ entries_[0].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[0], next);
+ MakeEntryActive(next);
+}
+
+// Ensures UI priority entries are subject to device status check.
+TEST_F(DownloadSchedulerImplTest, UIPrioritySubjectToDeviceStatus) {
+ BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST,
+ DownloadClient::TEST_2});
+
+ // Client TEST: entry 0.
+ // Client TEST_2: entry 1 (UI priority, cancel later).
+ BuildDataEntries(2);
+ entries_[0].client = DownloadClient::TEST;
+ entries_[0].scheduling_params.priority = SchedulingParams::Priority::LOW;
+ entries_[1].client = DownloadClient::TEST_2;
+ entries_[1].scheduling_params.priority = SchedulingParams::Priority::UI;
+
+ // UI priority is also subject to device status validation.
+ Entry* next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::METERED));
+ EXPECT_EQ(nullptr, next);
+ MakeEntryActive(next);
+}
+
+// UI priority entries will be processed first even if they doesn't belong to
+// the current client in load balancing.
+TEST_F(DownloadSchedulerImplTest, UIPriorityLoadBalancing) {
+ BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST,
+ DownloadClient::TEST_2});
+
+ // Client TEST: entry 0(Low priority).
+ // Client TEST_2: entry 1(UI priority).
+ BuildDataEntries(2);
+ entries_[0].client = DownloadClient::TEST;
+ entries_[0].scheduling_params.priority = SchedulingParams::Priority::LOW;
+ entries_[1].client = DownloadClient::TEST_2;
+ entries_[1].scheduling_params.priority = SchedulingParams::Priority::UI;
+
+ Entry* next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[1], next);
+ MakeEntryActive(next);
+}
+
+// When multiple UI priority entries exist, the next entry is selected based on
+// cancel time and load balancing.
+TEST_F(DownloadSchedulerImplTest, MultipleUIPriorityEntries) {
+ BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST,
+ DownloadClient::TEST_2});
+ BuildDataEntries(4);
+
+ // Client TEST: entry 0(UI priority), entry 1(UI priority, early cancel time).
+ // Client TEST_2: entry 2(UI priority), entry 3(high priority, early cancel
+ // time). Poll sequence: 1 -> 2 -> 0 -> 3.
+ for (auto& entry : entries_) {
+ entry.scheduling_params.priority = SchedulingParams::Priority::UI;
+ }
+ entries_[0].client = DownloadClient::TEST;
+ entries_[0].scheduling_params.cancel_time = base::Time::FromInternalValue(40);
+ entries_[1].client = DownloadClient::TEST;
+ entries_[1].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+ entries_[2].client = DownloadClient::TEST_2;
+ entries_[2].scheduling_params.cancel_time = base::Time::FromInternalValue(50);
+ entries_[3].client = DownloadClient::TEST_2;
+ entries_[3].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+ entries_[3].scheduling_params.priority = SchedulingParams::Priority::HIGH;
+
+ // When device conditions are meet, UI priority entry with the earliest cancel
+ // time will be processed first.
+ Entry* next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[1], next);
+ MakeEntryActive(next);
+
+ // Next entry will be UI priority entry from another client.
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[2], next);
+ MakeEntryActive(next);
+
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[0], next);
+ MakeEntryActive(next);
+
+ next = scheduler_->Next(
+ entries(),
+ BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+ EXPECT_EQ(&entries_[3], next);
+ MakeEntryActive(next);
+}
+
+// Ensures the reschedule logic works correctly, and we can pass the correct
+// criteria to platform task scheduler.
+TEST_F(DownloadSchedulerImplTest, Reschedule) {
+ InSequence s;
+
+ BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST});
+ BuildDataEntries(2);
+ entries_[0].client = DownloadClient::TEST;
+ entries_[1].client = DownloadClient::TEST;
+
+ entries_[0].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+ entries_[0].scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::UNMETERED;
+ entries_[1].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+ entries_[1].scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::UNMETERED;
+
+ Criteria criteria;
+ EXPECT_CALL(task_scheduler_, CancelTask(DownloadTaskType::DOWNLOAD_TASK))
+ .RetiresOnSaturation();
+ EXPECT_CALL(task_scheduler_,
+ ScheduleTask(DownloadTaskType::DOWNLOAD_TASK,
+ criteria.requires_unmetered_network,
+ criteria.requires_battery_charging, _, _))
+ .RetiresOnSaturation();
+ scheduler_->Reschedule(entries());
+
+ entries_[0].scheduling_params.battery_requirements =
+ SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+ criteria.requires_battery_charging = false;
+ EXPECT_CALL(task_scheduler_, CancelTask(DownloadTaskType::DOWNLOAD_TASK))
+ .RetiresOnSaturation();
+ EXPECT_CALL(task_scheduler_,
+ ScheduleTask(DownloadTaskType::DOWNLOAD_TASK,
+ criteria.requires_unmetered_network,
+ criteria.requires_battery_charging, _, _))
+ .RetiresOnSaturation();
+ scheduler_->Reschedule(entries());
+
+ entries_[0].scheduling_params.network_requirements =
+ SchedulingParams::NetworkRequirements::NONE;
+ criteria.requires_unmetered_network = false;
+ EXPECT_CALL(task_scheduler_, CancelTask(DownloadTaskType::DOWNLOAD_TASK))
+ .RetiresOnSaturation();
+ EXPECT_CALL(task_scheduler_,
+ ScheduleTask(DownloadTaskType::DOWNLOAD_TASK,
+ criteria.requires_unmetered_network,
+ criteria.requires_battery_charging, _, _))
+ .RetiresOnSaturation();
+ scheduler_->Reschedule(entries());
+}
+
+} // namespace
+} // namespace download
diff --git a/chromium/components/download/internal/service_config_impl.cc b/chromium/components/download/internal/service_config_impl.cc
new file mode 100644
index 00000000000..bb0e5ec0040
--- /dev/null
+++ b/chromium/components/download/internal/service_config_impl.cc
@@ -0,0 +1,24 @@
+// 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/download/internal/service_config_impl.h"
+
+#include "base/time/time.h"
+#include "components/download/internal/config.h"
+
+namespace download {
+
+ServiceConfigImpl::ServiceConfigImpl(Configuration* config) : config_(config) {}
+
+ServiceConfigImpl::~ServiceConfigImpl() = default;
+
+uint32_t ServiceConfigImpl::GetMaxScheduledDownloadsPerClient() const {
+ return config_->max_scheduled_downloads;
+}
+
+const base::TimeDelta& ServiceConfigImpl::GetFileKeepAliveTime() const {
+ return config_->file_keep_alive_time;
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/service_config_impl.h b/chromium/components/download/internal/service_config_impl.h
new file mode 100644
index 00000000000..15da514166e
--- /dev/null
+++ b/chromium/components/download/internal/service_config_impl.h
@@ -0,0 +1,32 @@
+// 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_INTERNAL_SERVICE_CONFIG_IMPL_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SERVICE_CONFIG_IMPL_H_
+
+#include "base/macros.h"
+#include "components/download/public/service_config.h"
+
+namespace download {
+
+struct Configuration;
+
+class ServiceConfigImpl : public ServiceConfig {
+ public:
+ explicit ServiceConfigImpl(Configuration* config);
+ ~ServiceConfigImpl() override;
+
+ // ServiceConfig implementation.
+ uint32_t GetMaxScheduledDownloadsPerClient() const override;
+ const base::TimeDelta& GetFileKeepAliveTime() const override;
+
+ private:
+ struct Configuration* config_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceConfigImpl);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_SERVICE_CONFIG_IMPL_H_
diff --git a/chromium/components/download/internal/service_config_impl_unittest.cc b/chromium/components/download/internal/service_config_impl_unittest.cc
new file mode 100644
index 00000000000..e093dec9de3
--- /dev/null
+++ b/chromium/components/download/internal/service_config_impl_unittest.cc
@@ -0,0 +1,23 @@
+// 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/download/internal/service_config_impl.h"
+#include "components/download/internal/config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace download {
+
+TEST(ServiceConfigImplTest, TestApi) {
+ Configuration config;
+ ServiceConfigImpl impl(&config);
+
+ config.max_scheduled_downloads = 7;
+ config.file_keep_alive_time = base::TimeDelta::FromSeconds(12);
+
+ EXPECT_EQ(config.max_scheduled_downloads,
+ impl.GetMaxScheduledDownloadsPerClient());
+ EXPECT_EQ(config.file_keep_alive_time, impl.GetFileKeepAliveTime());
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/startup_status.cc b/chromium/components/download/internal/startup_status.cc
new file mode 100644
index 00000000000..393e289a6a3
--- /dev/null
+++ b/chromium/components/download/internal/startup_status.cc
@@ -0,0 +1,28 @@
+// 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/download/internal/startup_status.h"
+
+namespace download {
+
+StartupStatus::StartupStatus() = default;
+StartupStatus::~StartupStatus() = default;
+
+void StartupStatus::Reset() {
+ driver_ok.reset();
+ model_ok.reset();
+ file_monitor_ok.reset();
+}
+
+bool StartupStatus::Complete() const {
+ return driver_ok.has_value() && model_ok.has_value() &&
+ file_monitor_ok.has_value();
+}
+
+bool StartupStatus::Ok() const {
+ DCHECK(Complete());
+ return driver_ok.value() && model_ok.value() && file_monitor_ok.value();
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/startup_status.h b/chromium/components/download/internal/startup_status.h
new file mode 100644
index 00000000000..754428afad9
--- /dev/null
+++ b/chromium/components/download/internal/startup_status.h
@@ -0,0 +1,40 @@
+// 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_STARTUP_STATUS_H_
+#define COMPONENTS_DOWNLOAD_STARTUP_STATUS_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+
+namespace download {
+
+// Helper struct to track the initialization status of various Controller
+// internal components.
+struct StartupStatus {
+ StartupStatus();
+ ~StartupStatus();
+
+ base::Optional<bool> driver_ok;
+ base::Optional<bool> model_ok;
+ base::Optional<bool> file_monitor_ok;
+
+ // Resets all startup state tracking.
+ void Reset();
+
+ // Whether or not all components have finished initialization. Note that this
+ // does not mean that all components were initialized successfully.
+ bool Complete() const;
+
+ // Whether or not all components have initialized successfully. Should only
+ // be called if Complete() is true.
+ bool Ok() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StartupStatus);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_STARTUP_STATUS_H_
diff --git a/chromium/components/download/internal/stats.cc b/chromium/components/download/internal/stats.cc
new file mode 100644
index 00000000000..5b4f12c52a3
--- /dev/null
+++ b/chromium/components/download/internal/stats.cc
@@ -0,0 +1,310 @@
+// 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/download/internal/stats.h"
+
+#include <map>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/download/internal/startup_status.h"
+
+namespace download {
+namespace stats {
+namespace {
+
+// The maximum tracked file size in KB, larger files will fall into overflow
+// bucket.
+const int64_t kMaxFileSizeKB = 4 * 1024 * 1024; /* 4GB */
+
+// Converts DownloadTaskType to histogram suffix.
+// Should maps to suffix string in histograms.xml.
+std::string TaskTypeToHistogramSuffix(DownloadTaskType task_type) {
+ switch (task_type) {
+ case DownloadTaskType::DOWNLOAD_TASK:
+ return "DownloadTask";
+ case DownloadTaskType::CLEANUP_TASK:
+ return "CleanUpTask";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+// Converts Entry::State to histogram suffix.
+// Should maps to suffix string in histograms.xml.
+std::string EntryStateToHistogramSuffix(Entry::State state) {
+ std::string suffix;
+ switch (state) {
+ case Entry::State::NEW:
+ return "New";
+ case Entry::State::AVAILABLE:
+ return "Available";
+ case Entry::State::ACTIVE:
+ return "Active";
+ case Entry::State::PAUSED:
+ return "Paused";
+ case Entry::State::COMPLETE:
+ return "Complete";
+ case Entry::State::COUNT:
+ break;
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+// Converts DownloadClient to histogram suffix.
+// Should maps to suffix string in histograms.xml.
+std::string ClientToHistogramSuffix(DownloadClient client) {
+ switch (client) {
+ case DownloadClient::TEST:
+ case DownloadClient::TEST_2:
+ case DownloadClient::TEST_3:
+ case DownloadClient::INVALID:
+ return "__Test__";
+ case DownloadClient::OFFLINE_PAGE_PREFETCH:
+ return "OfflinePage";
+ case DownloadClient::BOUNDARY:
+ NOTREACHED();
+ break;
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+// Converts CompletionType to histogram suffix.
+// Should maps to suffix string in histograms.xml.
+std::string CompletionTypeToHistogramSuffix(CompletionType type) {
+ switch (type) {
+ case CompletionType::SUCCEED:
+ return "Succeed";
+ case CompletionType::FAIL:
+ return "Fail";
+ case CompletionType::ABORT:
+ return "Abort";
+ case CompletionType::TIMEOUT:
+ return "Timeout";
+ case CompletionType::UNKNOWN:
+ return "Unknown";
+ case CompletionType::CANCEL:
+ return "Cancel";
+ case CompletionType::COUNT:
+ NOTREACHED();
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+// Converts FileCleanupReason to histogram suffix.
+// Should maps to suffix string in histograms.xml.
+std::string FileCleanupReasonToHistogramSuffix(FileCleanupReason reason) {
+ switch (reason) {
+ case FileCleanupReason::TIMEOUT:
+ return "Timeout";
+ case FileCleanupReason::ORPHANED:
+ return "Orphaned";
+ case FileCleanupReason::UNKNOWN:
+ return "Unknown";
+ case FileCleanupReason::HARD_RECOVERY:
+ return "HardRecovery";
+ case FileCleanupReason::COUNT:
+ NOTREACHED();
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+// Helper method to log StartUpResult.
+void LogStartUpResult(bool in_recovery, StartUpResult result) {
+ if (in_recovery) {
+ base::UmaHistogramEnumeration("Download.Service.StartUpStatus.Recovery",
+ result, StartUpResult::COUNT);
+ } else {
+ base::UmaHistogramEnumeration(
+ "Download.Service.StartUpStatus.Initialization", result,
+ StartUpResult::COUNT);
+ }
+}
+
+// Helper method to log the number of entries under a particular state.
+void LogDatabaseRecords(Entry::State state, uint32_t record_count) {
+ std::string name("Download.Service.Db.Records");
+ name.append(".").append(EntryStateToHistogramSuffix(state));
+ base::UmaHistogramCustomCounts(name, record_count, 1, 500, 50);
+}
+
+} // namespace
+
+void LogControllerStartupStatus(bool in_recovery, const StartupStatus& status) {
+ DCHECK(status.Complete());
+
+ // Total counts for general success/failure rate.
+ LogStartUpResult(in_recovery, status.Ok() ? StartUpResult::SUCCESS
+ : StartUpResult::FAILURE);
+
+ // Failure reasons.
+ if (!status.driver_ok.value())
+ LogStartUpResult(in_recovery, StartUpResult::FAILURE_REASON_DRIVER);
+ if (!status.model_ok.value())
+ LogStartUpResult(in_recovery, StartUpResult::FAILURE_REASON_MODEL);
+ if (!status.file_monitor_ok.value())
+ LogStartUpResult(in_recovery, StartUpResult::FAILURE_REASON_FILE_MONITOR);
+}
+
+void LogServiceApiAction(DownloadClient client, ServiceApiAction action) {
+ // Total count for each action.
+ std::string name("Download.Service.Request.ClientAction");
+ base::UmaHistogramEnumeration(name, action, ServiceApiAction::COUNT);
+
+ // Total count for each action with client suffix.
+ name.append(".").append(ClientToHistogramSuffix(client));
+ base::UmaHistogramEnumeration(name, action, ServiceApiAction::COUNT);
+}
+
+void LogStartDownloadResult(DownloadClient client,
+ DownloadParams::StartResult result) {
+ // Total count for each start result.
+ std::string name("Download.Service.Request.StartResult");
+ base::UmaHistogramEnumeration(name, result,
+ DownloadParams::StartResult::COUNT);
+
+ // Total count for each client result with client suffix.
+ name.append(".").append(ClientToHistogramSuffix(client));
+ base::UmaHistogramEnumeration(name, result,
+ DownloadParams::StartResult::COUNT);
+}
+
+void LogStartDownloadResponse(DownloadClient client,
+ Client::ShouldDownload should_download) {
+ // Total count for each start response.
+ std::string name("Download.Service.Request.StartResponse");
+ base::UmaHistogramEnumeration(name, should_download,
+ Client::ShouldDownload::COUNT);
+
+ // Total count for each client response with client suffix.
+ name.append(".").append(ClientToHistogramSuffix(client));
+ base::UmaHistogramEnumeration(name, should_download,
+ Client::ShouldDownload::COUNT);
+}
+
+void LogDownloadParams(const DownloadParams& params) {
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Request.BatteryRequirement",
+ params.scheduling_params.battery_requirements,
+ SchedulingParams::BatteryRequirements::COUNT);
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Request.NetworkRequirement",
+ params.scheduling_params.network_requirements,
+ SchedulingParams::NetworkRequirements::COUNT);
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Request.Priority",
+ params.scheduling_params.priority,
+ SchedulingParams::Priority::COUNT);
+}
+
+void LogRecoveryOperation(Entry::State to_state) {
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Recovery", to_state,
+ Entry::State::COUNT);
+}
+
+void LogDownloadCompletion(CompletionType type,
+ const base::TimeDelta& time_span,
+ uint64_t file_size_bytes) {
+ // Records completion type.
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Finish.Type", type,
+ CompletionType::COUNT);
+
+ // TODO(xingliu): Use DownloadItem::GetStartTime and DownloadItem::GetEndTime
+ // to record the completion time to histogram "Download.Service.Finish.Time".
+ // Also propagates and records the mime type here.
+
+ // Records the file size.
+ std::string name("Download.Service.Finish.FileSize");
+ uint64_t file_size_kb = file_size_bytes / 1024;
+ base::UmaHistogramCustomCounts(name, file_size_kb, 1, kMaxFileSizeKB, 50);
+
+ name.append(".").append(CompletionTypeToHistogramSuffix(type));
+ base::UmaHistogramCustomCounts(name, file_size_kb, 1, kMaxFileSizeKB, 50);
+}
+
+void LogModelOperationResult(ModelAction action, bool success) {
+ if (success) {
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Db.Operation.Success", action,
+ ModelAction::COUNT);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Db.Operation.Failure", action,
+ ModelAction::COUNT);
+ }
+}
+
+void LogEntries(std::map<Entry::State, uint32_t>& entries_count) {
+ uint32_t total_records = 0;
+ for (const auto& entry_count : entries_count)
+ total_records += entry_count.second;
+
+ // Total number of records in database.
+ base::UmaHistogramCustomCounts("Download.Service.Db.Records", total_records,
+ 1, 500, 50);
+
+ // Number of records for each Entry::State.
+ for (Entry::State state = Entry::State::NEW; state != Entry::State::COUNT;
+ state = (Entry::State)((int)(state) + 1)) {
+ LogDatabaseRecords(state, entries_count[state]);
+ }
+}
+
+void LogScheduledTaskStatus(DownloadTaskType task_type,
+ ScheduledTaskStatus status) {
+ std::string name("Download.Service.TaskScheduler.Status");
+ base::UmaHistogramEnumeration(name, status, ScheduledTaskStatus::COUNT);
+
+ name.append(".").append(TaskTypeToHistogramSuffix(task_type));
+ base::UmaHistogramEnumeration(name, status, ScheduledTaskStatus::COUNT);
+}
+
+void LogsFileDirectoryCreationError(base::File::Error error) {
+ // Maps to histogram enum PlatformFileError.
+ UMA_HISTOGRAM_ENUMERATION("Download.Service.Files.DirCreationError", -error,
+ -base::File::Error::FILE_ERROR_MAX);
+}
+
+void LogFileCleanupStatus(FileCleanupReason reason,
+ int succeeded_cleanups,
+ int failed_cleanups,
+ int external_cleanups) {
+ std::string name("Download.Service.Files.CleanUp.Success");
+ base::UmaHistogramCounts100(name, succeeded_cleanups);
+ name.append(".").append(FileCleanupReasonToHistogramSuffix(reason));
+ base::UmaHistogramCounts100(name, succeeded_cleanups);
+
+ name = "Download.Service.Files.CleanUp.Failure";
+ base::UmaHistogramCounts100(name, failed_cleanups);
+ name.append(".").append(FileCleanupReasonToHistogramSuffix(reason));
+ base::UmaHistogramCounts100(name, failed_cleanups);
+
+ name = "Download.Service.Files.CleanUp.External";
+ base::UmaHistogramCounts100(name, external_cleanups);
+ name.append(".").append(FileCleanupReasonToHistogramSuffix(reason));
+ base::UmaHistogramCounts100(name, external_cleanups);
+}
+
+void LogFileLifeTime(const base::TimeDelta& file_life_time) {
+ UMA_HISTOGRAM_CUSTOM_TIMES("Download.Service.Files.LifeTime", file_life_time,
+ base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromDays(8), 100);
+}
+
+void LogFileDirDiskUtilization(int64_t total_disk_space,
+ int64_t free_disk_space,
+ int64_t files_size) {
+ UMA_HISTOGRAM_PERCENTAGE("Download.Service.Files.FreeDiskSpace",
+ (free_disk_space * 100) / total_disk_space);
+ UMA_HISTOGRAM_PERCENTAGE("Download.Service.Files.DiskUsed",
+ (files_size * 100) / total_disk_space);
+}
+
+void LogFilePathRenamed(bool renamed) {
+ UMA_HISTOGRAM_BOOLEAN("Download.Service.Files.PathRenamed", renamed);
+}
+
+} // namespace stats
+} // namespace download
diff --git a/chromium/components/download/internal/stats.h b/chromium/components/download/internal/stats.h
new file mode 100644
index 00000000000..e100d35189d
--- /dev/null
+++ b/chromium/components/download/internal/stats.h
@@ -0,0 +1,191 @@
+// 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_INTERNAL_STATS_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_STATS_H_
+
+#include "base/files/file.h"
+#include "components/download/internal/controller.h"
+#include "components/download/internal/entry.h"
+#include "components/download/public/clients.h"
+#include "components/download/public/download_params.h"
+#include "components/download/public/download_task_types.h"
+
+namespace download {
+
+struct StartupStatus;
+
+namespace stats {
+
+// Please follow the following rules for all enums:
+// 1. Keep them in sync with the corresponding entry in enums.xml.
+// 2. Treat them as append only.
+// 3. Do not remove any enums. Only mark them as deprecated.
+
+// Enum used to track download service start up result and failure reasons.
+// Most of the fields should map to StartupStatus.
+// Failure reasons are not mutually exclusive.
+enum class StartUpResult {
+ // Download service successfully started.
+ SUCCESS = 0,
+
+ // Download service start up failed.
+ FAILURE = 1,
+
+ // Download driver is not ready.
+ FAILURE_REASON_DRIVER = 2,
+
+ // Database layer failed to initialized.
+ FAILURE_REASON_MODEL = 3,
+
+ // File monitor failed to start.
+ FAILURE_REASON_FILE_MONITOR = 4,
+
+ // The count of entries for the enum.
+ COUNT = 5,
+};
+
+// Enum used by UMA metrics to track which actions a Client is taking on the
+// service.
+enum class ServiceApiAction {
+ // Represents a call to DownloadService::StartDownload.
+ START_DOWNLOAD = 0,
+
+ // Represents a call to DownloadService::PauseDownload.
+ PAUSE_DOWNLOAD = 1,
+
+ // Represents a call to DownloadService::ResumeDownload.
+ RESUME_DOWNLOAD = 2,
+
+ // Represents a call to DownloadService::CancelDownload.
+ CANCEL_DOWNLOAD = 3,
+
+ // Represents a call to DownloadService::ChangeCriteria.
+ CHANGE_CRITERIA = 4,
+
+ // The count of entries for the enum.
+ COUNT = 5,
+};
+
+// Enum used by UMA metrics to tie to specific actions taken on a Model. This
+// can be used to track failure events.
+enum class ModelAction {
+ // Represents an attempt to initialize the Model.
+ INITIALIZE = 0,
+
+ // Represents an attempt to add an Entry to the Model.
+ ADD = 1,
+
+ // Represents an attempt to update an Entry in the Model.
+ UPDATE = 2,
+
+ // Represents an attempt to remove an Entry from the Model.
+ REMOVE = 3,
+
+ // The count of entries for the enum.
+ COUNT = 4,
+};
+
+// Enum used by UMA metrics to log the status of scheduled tasks.
+enum class ScheduledTaskStatus {
+ // Startup failed and the task was not run.
+ ABORTED_ON_FAILED_INIT = 0,
+
+ // OnStopScheduledTask() was received before the task could be fired.
+ CANCELLED_ON_STOP = 1,
+
+ // Callback was run successfully after completion of the task.
+ COMPLETED_NORMALLY = 2,
+
+ // The count of entries for the enum.
+ COUNT = 3,
+};
+
+// Enum used by UMA metrics to track various types of cleanup actions taken by
+// the service.
+enum class FileCleanupReason {
+ // The file was deleted by the service after timeout.
+ TIMEOUT = 0,
+
+ // The database entry for the file was found not associated with any
+ // registered client.
+ ORPHANED = 1,
+
+ // At startup, the file was found not being associated with any model entry or
+ // driver entry.
+ UNKNOWN = 2,
+
+ // We're trying to remove all files as part of a hard recovery attempt.
+ HARD_RECOVERY = 3,
+
+ // The count of entries for the enum.
+ COUNT = 4,
+};
+
+// Logs the results of starting up the Controller. Will log each failure reason
+// if |status| contains more than one initialization failure.
+void LogControllerStartupStatus(bool in_recovery, const StartupStatus& status);
+
+// Logs an action taken on the service API.
+void LogServiceApiAction(DownloadClient client, ServiceApiAction action);
+
+// Logs the result of a StartDownload() attempt on the service.
+void LogStartDownloadResult(DownloadClient client,
+ DownloadParams::StartResult result);
+
+// Logs the client response to StartDownload() attempt on the service.
+void LogStartDownloadResponse(DownloadClient client,
+ download::Client::ShouldDownload should_download);
+
+// Logs the download parameters when StartDownload() is called.
+void LogDownloadParams(const DownloadParams& params);
+
+// Logs recovery operations that happened when we had to move from one state
+// to another on startup.
+void LogRecoveryOperation(Entry::State to_state);
+
+// Logs download completion event, download time, and the file size.
+void LogDownloadCompletion(CompletionType type,
+ const base::TimeDelta& time_span,
+ uint64_t file_size_bytes);
+
+// Logs statistics about the result of a model operation. Used to track failure
+// cases.
+void LogModelOperationResult(ModelAction action, bool success);
+
+// Logs the total number of all entries, and the number of entries in each
+// state after the model is initialized.
+void LogEntries(std::map<Entry::State, uint32_t>& entries_count);
+
+// Log statistics about the status of a TaskFinishedCallback.
+void LogScheduledTaskStatus(DownloadTaskType task_type,
+ ScheduledTaskStatus status);
+
+// Logs download files directory creation error.
+void LogsFileDirectoryCreationError(base::File::Error error);
+
+// Logs statistics about the reasons of a file cleanup.
+void LogFileCleanupStatus(FileCleanupReason reason,
+ int succeeded_cleanups,
+ int failed_cleanups,
+ int external_cleanups);
+
+// Logs the file life time for successfully completed download.
+void LogFileLifeTime(const base::TimeDelta& file_life_time);
+
+// Logs the total disk space utilized by download files.
+// This includes the total size of all the files in |file_dir|.
+// This function is costly and should be called only once.
+void LogFileDirDiskUtilization(int64_t total_disk_space,
+ int64_t free_disk_space,
+ int64_t files_size);
+
+// Logs if the final download file path is different from the requested file
+// path.
+void LogFilePathRenamed(bool renamed);
+
+} // namespace stats
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_STATS_H_
diff --git a/chromium/components/download/internal/store.h b/chromium/components/download/internal/store.h
index 04a39bffcc6..1d1e5686687 100644
--- a/chromium/components/download/internal/store.h
+++ b/chromium/components/download/internal/store.h
@@ -10,8 +10,6 @@
#include <vector>
#include "base/callback_forward.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner.h"
namespace download {
@@ -36,9 +34,8 @@ class Store {
// Store.
virtual void Initialize(InitCallback callback) = 0;
- // Destroyes the store and asynchronously returns whether or not that
- // destruction was successful.
- virtual void Destroy(StoreCallback callback) = 0;
+ // Destroys the underlying store and attempts to re-initialize.
+ virtual void HardRecover(StoreCallback callback) = 0;
// Adds or updates |entry| in this Store asynchronously and returns whether or
// not that was successful.
diff --git a/chromium/components/download/internal/test/BUILD.gn b/chromium/components/download/internal/test/BUILD.gn
index 87032dcfd7d..99011eef401 100644
--- a/chromium/components/download/internal/test/BUILD.gn
+++ b/chromium/components/download/internal/test/BUILD.gn
@@ -8,10 +8,22 @@ source_set("test_support") {
testonly = true
sources = [
+ "download_params_utils.cc",
+ "download_params_utils.h",
+ "empty_client.cc",
+ "empty_client.h",
"entry_utils.cc",
"entry_utils.h",
+ "mock_client.cc",
+ "mock_client.h",
+ "mock_controller.cc",
+ "mock_controller.h",
"mock_model_client.cc",
"mock_model_client.h",
+ "noop_store.cc",
+ "noop_store.h",
+ "test_device_status_listener.cc",
+ "test_device_status_listener.h",
"test_download_driver.cc",
"test_download_driver.h",
"test_store.cc",
diff --git a/chromium/components/download/public/BUILD.gn b/chromium/components/download/public/BUILD.gn
index 5f013fd35b6..f51a715b16d 100644
--- a/chromium/components/download/public/BUILD.gn
+++ b/chromium/components/download/public/BUILD.gn
@@ -14,8 +14,11 @@ source_set("public") {
"download_params.cc",
"download_params.h",
"download_service.h",
+ "download_task_types.h",
"features.cc",
"features.h",
+ "service_config.h",
+ "task_scheduler.h",
]
deps = [
@@ -28,3 +31,22 @@ source_set("public") {
"//url",
]
}
+
+if (is_android) {
+ android_library("public_java") {
+ srcjar_deps = [ ":jni_enums" ]
+
+ deps = [
+ "//base:base_java",
+ "//third_party/android_tools:android_support_annotations_java",
+ ]
+ }
+
+ java_cpp_enum("jni_enums") {
+ visibility = [ "*" ]
+
+ sources = [
+ "download_task_types.h",
+ ]
+ }
+}
diff --git a/chromium/components/download/public/client.h b/chromium/components/download/public/client.h
index 28264e7b97c..518d7e223b3 100644
--- a/chromium/components/download/public/client.h
+++ b/chromium/components/download/public/client.h
@@ -22,18 +22,56 @@ class Client {
// Used by OnDownloadStarted to determine whether or not the DownloadService
// should continue downloading the file or abort the attempt.
enum class ShouldDownload {
+ // Continue to download the file.
CONTINUE,
+
+ // Abort the download.
ABORT,
+
+ // The count of entries for the enum.
+ COUNT,
+ };
+
+ // Used by OnDownloadFailed to determine the reason of the abort.
+ enum class FailureReason {
+ // Used when the download has been aborted after reaching a threshold where
+ // we decide it is not worth attempting to start again. This could be
+ // either due to a specific number of failed retry attempts or a specific
+ // number of wasted bytes due to the download restarting.
+ NETWORK,
+
+ // Used when the download was not completed before the
+ // DownloadParams::cancel_after timeout.
+ TIMEDOUT,
+
+ // Used when the download was cancelled by the Client.
+ CANCELLED,
+
+ // Used when the download was aborted by the Client in response to the
+ // download starting (see OnDownloadStarted()).
+ ABORTED,
+
+ // Used when the failure reason is unknown. This generally means that we
+ // detect that the download failed during a restart, but aren't sure exactly
+ // what triggered the failure before shutdown.
+ UNKNOWN,
};
virtual ~Client() = default;
// Called when the DownloadService is initialized and ready to be interacted
// with. |outstanding_download_guids| is a list of all downloads the
- // DownloadService is aware of that are associated with this Client.
+ // DownloadService is aware of that are associated with this Client. If
+ // |state_lost| is |true|, the service ran into an error initializing and had
+ // to destroy all internal persisted state. At this point any saved files
+ // might not be available and any previously scheduled downloads are gone.
virtual void OnServiceInitialized(
+ bool state_lost,
const std::vector<std::string>& outstanding_download_guids) = 0;
+ // Called when the DownloadService fails to initialize and should not be used.
+ virtual void OnServiceUnavailable() = 0;
+
// Return whether or not the download should be aborted (potentially in
// response to |headers|). The download will be downloading at the time this
// call is made.
@@ -51,18 +89,11 @@ class Client {
virtual void OnDownloadUpdated(const std::string& guid,
uint64_t bytes_downloaded) = 0;
- // TODO(dtrainor): Expose a useful error message with the failed download.
- virtual void OnDownloadFailed(const std::string& guid) = 0;
-
- // Called when the download was not completed before the
- // DownloadParams::cancel_after timeout.
- virtual void OnDownloadTimedOut(const std::string& guid) = 0;
-
- // Called when the download has been aborted after reaching a treshold where
- // we decide it is not worth attempting to start again. This could be either
- // due to a specific number of failed retry attempts or a specific number of
- // wasted bytes due to the download restarting.
- virtual void OnDownloadAborted(const std::string& guid) = 0;
+ // Called when a download failed. Check FailureReason for a list of possible
+ // reasons why this failure occurred. Note that this will also be called for
+ // cancelled downloads.
+ virtual void OnDownloadFailed(const std::string& guid,
+ FailureReason reason) = 0;
// Called when a download has been successfully completed. After this call
// the download entry will be purged from the database. The file will be
diff --git a/chromium/components/download/public/clients.h b/chromium/components/download/public/clients.h
index a464d212017..3939aea2638 100644
--- a/chromium/components/download/public/clients.h
+++ b/chromium/components/download/public/clients.h
@@ -5,6 +5,11 @@
#ifndef COMPONENTS_DOWNLOAD_PUBLIC_CLIENTS_H_
#define COMPONENTS_DOWNLOAD_PUBLIC_CLIENTS_H_
+#include <map>
+#include <memory>
+
+#include "components/download/public/client.h"
+
namespace download {
// A list of all clients that are able to make download requests through the
@@ -15,19 +20,23 @@ namespace download {
// but also to make sure the underlying database properly associates each
// download with the right client.
enum class DownloadClient {
- // Represents an uninitialized DownloadClient variable.
- INVALID = 0,
-
// Test client values. Meant to be used by the testing framework and not
// production code. Callers will be unable to access the DownloadService with
// these test APIs.
- TEST = 1,
+ TEST = -1,
+ TEST_2 = -2,
+ TEST_3 = -3,
- OFFLINE_PAGE_PREFETCH = 2,
+ // Represents an uninitialized DownloadClient variable.
+ INVALID = 0,
- BOUNDARY = 3,
+ OFFLINE_PAGE_PREFETCH = 1,
+
+ BOUNDARY = 2,
};
+using DownloadClientMap = std::map<DownloadClient, std::unique_ptr<Client>>;
+
} // namespace download
#endif // COMPONENTS_DOWNLOAD_PUBLIC_CLIENTS_H_
diff --git a/chromium/components/download/public/download_params.cc b/chromium/components/download/public/download_params.cc
index f24d467b763..2c3f10652a2 100644
--- a/chromium/components/download/public/download_params.cc
+++ b/chromium/components/download/public/download_params.cc
@@ -9,10 +9,17 @@
namespace download {
SchedulingParams::SchedulingParams()
- : priority(Priority::DEFAULT),
+ : cancel_time(base::Time::Max()),
+ priority(Priority::DEFAULT),
network_requirements(NetworkRequirements::NONE),
battery_requirements(BatteryRequirements::BATTERY_INSENSITIVE) {}
+bool SchedulingParams::operator==(const SchedulingParams& rhs) const {
+ return network_requirements == rhs.network_requirements &&
+ battery_requirements == rhs.battery_requirements &&
+ priority == rhs.priority && cancel_time == rhs.cancel_time;
+}
+
RequestParams::RequestParams() : method("GET") {}
DownloadParams::DownloadParams() : client(DownloadClient::INVALID) {}
diff --git a/chromium/components/download/public/download_params.h b/chromium/components/download/public/download_params.h
index dcf25b41986..217718efbc9 100644
--- a/chromium/components/download/public/download_params.h
+++ b/chromium/components/download/public/download_params.h
@@ -30,6 +30,9 @@ struct SchedulingParams {
// The download can occur only if the network isn't metered.
UNMETERED = 2,
+
+ // Last value of the enum.
+ COUNT = 3,
};
enum class BatteryRequirements {
@@ -41,6 +44,9 @@ struct SchedulingParams {
// The download can only occur when charging or in optimal battery
// conditions.
BATTERY_SENSITIVE = 1,
+
+ // Last value of the enum.
+ COUNT = 2,
};
enum class Priority {
@@ -61,13 +67,19 @@ struct SchedulingParams {
// The default priority for all tasks unless overridden.
DEFAULT = NORMAL,
+
+ // Last value of the enum.
+ COUNT = 4,
};
SchedulingParams();
SchedulingParams(const SchedulingParams& other) = default;
~SchedulingParams() = default;
+ bool operator==(const SchedulingParams& rhs) const;
+
// Cancel the download after this time. Will cancel in-progress downloads.
+ // base::Time::Max() if not specified.
base::Time cancel_time;
// The suggested priority. Non-UI priorities may not be honored by the
@@ -102,12 +114,27 @@ struct DownloadParams {
// The DownloadService has too many downloads. Backoff and retry.
BACKOFF,
- // Failed to create the download. Invalid input parameters.
- BAD_PARAMETERS,
+ // The DownloadService has no knowledge of the DownloadClient associated
+ // with this request.
+ UNEXPECTED_CLIENT,
+
+ // Failed to create the download. The guid is already in use.
+ UNEXPECTED_GUID,
+
+ // The download was cancelled by the Client while it was being persisted.
+ CLIENT_CANCELLED,
+
+ // The DownloadService was unable to accept and persist this download due to
+ // an internal error like the underlying DB store failing to write to disk.
+ INTERNAL_ERROR,
// TODO(dtrainor): Add more error codes.
+ // The count of entries for the enum.
+ COUNT,
};
+ using StartCallback = base::Callback<void(const std::string&, StartResult)>;
+
DownloadParams();
DownloadParams(const DownloadParams& other);
~DownloadParams();
@@ -116,11 +143,13 @@ struct DownloadParams {
DownloadClient client;
// A unique GUID that represents this download. See |base::GenerateGUID()|.
+ // TODO(xingliu): guid in content download must be upper case, see
+ // http://crbug.com/734818.
std::string guid;
// A callback that will be notified if this download has been accepted and
// persisted by the DownloadService.
- base::Callback<void(const DownloadParams&, StartResult)> callback;
+ StartCallback callback;
// The parameters that determine under what device conditions this download
// will occur.
diff --git a/chromium/components/download/public/download_service.h b/chromium/components/download/public/download_service.h
index f0733de6ff4..fdf8ac512bf 100644
--- a/chromium/components/download/public/download_service.h
+++ b/chromium/components/download/public/download_service.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_DOWNLOAD_PUBLIC_DOWNLOAD_SERVICE_H_
#define COMPONENTS_DOWNLOAD_PUBLIC_DOWNLOAD_SERVICE_H_
+#include <memory>
#include <string>
#include "base/files/file_path.h"
@@ -12,12 +13,18 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
+#include "components/download/public/clients.h"
+#include "components/download/public/download_task_types.h"
#include "components/keyed_service/core/keyed_service.h"
namespace download {
+class Client;
struct DownloadParams;
struct SchedulingParams;
+class ServiceConfig;
+
+using TaskFinishedCallback = base::Callback<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
@@ -27,21 +34,50 @@ struct SchedulingParams;
// feature requesting a download will have to implement a download::Client
// interface so this class knows who to contact when a download completes after
// a process restart.
+// See the embedder specific factories for creation options.
class DownloadService : public KeyedService {
public:
- // |storage_dir| is a path to where all the local storage will be. This will
- // hold the internal database as well as any temporary files on disk. If this
- // is an empty path, the service will not persist any information to disk and
- // will act as an in-memory only service (this means no auto-retries after
- // restarts, no files written on completion, etc.).
- // |background_task_runner| will be used for all disk reads and writes.
- static DownloadService* Create(
- const base::FilePath& storage_dir,
- const scoped_refptr<base::SequencedTaskRunner>& background_task_runner);
+ // The current status of the Service.
+ enum class ServiceStatus {
+ // The service is in the process of initializing and should not be used yet.
+ // All registered Clients will be notified via
+ // Client::OnServiceInitialized() once the service is ready.
+ STARTING_UP = 0,
+
+ // The service is ready and available for use.
+ READY = 1,
+
+ // The service is unavailable. This is typically due to an unrecoverable
+ // error on some internal component like the persistence layer.
+ UNAVAILABLE = 2,
+ };
+
+ // Returns useful configuration information about the DownloadService.
+ virtual const ServiceConfig& GetConfig() = 0;
+
+ // Callback method to run by the service when a pre-scheduled task starts.
+ // This method is invoked on main thread and while it is running, the system
+ // holds a wakelock which is not released until either the |callback| is run
+ // or OnStopScheduledTask is invoked by the system. Do not call this method
+ // directly.
+ virtual void OnStartScheduledTask(DownloadTaskType task_type,
+ const 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
+ // TaskFinishedCallback should be reset after this call. Do not call this
+ // method directly.
+ virtual bool OnStopScheduledTask(DownloadTaskType task_type) = 0;
+
+ // Whether or not the DownloadService is currently available, initialized
+ // successfully, and ready to be used.
+ virtual ServiceStatus GetStatus() = 0;
// Sends the download to the service. A callback to
// |DownloadParams::callback| will be triggered once the download has been
- // persisted and saved in the service
+ // persisted and saved in the service.
+ // TODO(xingliu): Remove the limitation of upper case guid in
+ // |download_params|, see http://crbug.com/734818.
virtual void StartDownload(const DownloadParams& download_params) = 0;
// Allows any feature to pause or resume downloads at will. Paused downloads
diff --git a/chromium/components/download/public/download_task_types.h b/chromium/components/download/public/download_task_types.h
new file mode 100644
index 00000000000..a26bc013743
--- /dev/null
+++ b/chromium/components/download/public/download_task_types.h
@@ -0,0 +1,22 @@
+// 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_PUBLIC_DOWNLOAD_TASK_TYPES_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_DOWNLOAD_TASK_TYPES_H_
+
+namespace download {
+
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.download
+enum class DownloadTaskType {
+ // Task to invoke download service to take various download actions.
+ DOWNLOAD_TASK = 0,
+
+ // Task to remove unnecessary files from the system.
+ CLEANUP_TASK = 1,
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_PUBLIC_DOWNLOAD_TASK_TYPES_H_
diff --git a/chromium/components/download/public/service_config.h b/chromium/components/download/public/service_config.h
new file mode 100644
index 00000000000..de51fbd5dd9
--- /dev/null
+++ b/chromium/components/download/public/service_config.h
@@ -0,0 +1,37 @@
+// 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_PUBLIC_SERVICE_CONFIG_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_SERVICE_CONFIG_H_
+
+#include <stdint.h>
+
+namespace base {
+class TimeDelta;
+} // namespace base
+
+namespace download {
+
+// Contains the configuration used by this DownloadService for internal download
+// operations. Meant to be used by Clients for any tweaking they might want to
+// do based on the configuration parameters.
+class ServiceConfig {
+ public:
+ virtual ~ServiceConfig() = default;
+
+ // The maximum number of downloads that can be outstanding per Client. Any
+ // Client attempting to schedule more downloads than this limit will receive a
+ // DownloadParams::StartResult::BACKOFF return value.
+ virtual uint32_t GetMaxScheduledDownloadsPerClient() const = 0;
+
+ // Returns the minimum amount of time the DownloadService will wait before
+ // automatically erasing any files that remain on disk in the same place with
+ // the same name for a completed download. It is up to the Client to move or
+ // consume the file before this time limit is reached.
+ virtual const base::TimeDelta& GetFileKeepAliveTime() const = 0;
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_PUBLIC_SERVICE_CONFIG_H_
diff --git a/chromium/components/download/public/task_scheduler.h b/chromium/components/download/public/task_scheduler.h
new file mode 100644
index 00000000000..ed436432b99
--- /dev/null
+++ b/chromium/components/download/public/task_scheduler.h
@@ -0,0 +1,36 @@
+// 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_PUBLIC_TASK_SCHEDULER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_TASK_SCHEDULER_H_
+
+#include "components/download/public/download_task_types.h"
+
+namespace download {
+
+// A helper class backed by system APIs to schedule jobs in the background. The
+// tasks can run independently of each other as long as they have different
+// |task_type|. Scheduling another task of same |task_type| before the task is
+// fired will cancel the previous task.
+class TaskScheduler {
+ public:
+ // Schedules a task with the operating system. The system has the liberty of
+ // firing the task any time between |window_start_time_seconds| and
+ // |window_end_time_seconds|. If the trigger conditions are not met, the
+ // behavior is unknown.
+ virtual void ScheduleTask(DownloadTaskType task_type,
+ bool require_unmetered_network,
+ bool require_charging,
+ long window_start_time_seconds,
+ long window_end_time_seconds) = 0;
+
+ // Cancels a pre-scheduled task of type |task_type|.
+ virtual void CancelTask(DownloadTaskType task_type) = 0;
+
+ virtual ~TaskScheduler() {}
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_PUBLIC_TASK_SCHEDULER_H_
diff --git a/chromium/components/drive/BUILD.gn b/chromium/components/drive/BUILD.gn
index f709a4fd5c8..d16147b1409 100644
--- a/chromium/components/drive/BUILD.gn
+++ b/chromium/components/drive/BUILD.gn
@@ -56,9 +56,9 @@ static_library("drive") {
# TODO(lukasza): Remove this dependency (see DEPS file for more info).
"//content/public/browser",
- "//device/power_save_blocker",
"//google_apis:google_apis",
"//net:net",
+ "//services/device/public/interfaces",
"//third_party/cacheinvalidation",
"//third_party/leveldatabase",
"//third_party/re2",
diff --git a/chromium/components/error_page/common/localized_error.cc b/chromium/components/error_page/common/localized_error.cc
index 580d3d746b0..e0ac6f3399f 100644
--- a/chromium/components/error_page/common/localized_error.cc
+++ b/chromium/components/error_page/common/localized_error.cc
@@ -236,6 +236,12 @@ const LocalizedErrorMap net_error_options[] = {
SUGGEST_NONE,
SHOW_BUTTON_RELOAD,
},
+ {net::ERR_INVALID_REDIRECT,
+ IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING,
+ IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE,
+ SUGGEST_NONE,
+ SHOW_BUTTON_RELOAD,
+ },
{net::ERR_SSL_PROTOCOL_ERROR,
IDS_ERRORPAGES_HEADING_INSECURE_CONNECTION,
IDS_ERRORPAGES_SUMMARY_INVALID_RESPONSE,
@@ -506,7 +512,7 @@ void AddGoogleCachedCopyButton(base::ListValue* suggestions_summary_list,
l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_SHOW_SAVED_COPY));
cache_button->SetString("cacheUrl", cache_url);
cache_button->SetInteger("trackingId", cache_tracking_id);
- error_strings->Set("cacheButton", cache_button.release());
+ error_strings->Set("cacheButton", std::move(cache_button));
// Remove the item from suggestions dictionary so that it does not get
// displayed by the template in the details section.
@@ -900,13 +906,13 @@ void LocalizedError::GetStrings(
std::string icon_class = GetIconClassForError(error_domain, error_code);
error_strings->SetString("iconClass", icon_class);
- base::DictionaryValue* heading = new base::DictionaryValue;
+ auto heading = base::MakeUnique<base::DictionaryValue>();
heading->SetString("msg",
l10n_util::GetStringUTF16(options.heading_resource_id));
heading->SetString("hostName", host_name);
- error_strings->Set("heading", heading);
+ error_strings->Set("heading", std::move(heading));
- base::DictionaryValue* summary = new base::DictionaryValue;
+ auto summary = base::MakeUnique<base::DictionaryValue>();
// Set summary message under the heading.
summary->SetString(
@@ -934,7 +940,7 @@ void LocalizedError::GetStrings(
error_strings->SetString(
"hideDetails", l10n_util::GetStringUTF16(
IDS_ERRORPAGE_NET_BUTTON_HIDE_DETAILS));
- error_strings->Set("summary", summary);
+ error_strings->Set("summary", std::move(summary));
base::string16 error_string;
if (error_domain == net::kErrorDomain) {
@@ -961,16 +967,17 @@ void LocalizedError::GetStrings(
bool use_default_suggestions = true;
if (!params->override_suggestions) {
- suggestions_details = new base::ListValue();
- suggestions_summary_list = new base::ListValue();
// Detailed suggestion information.
- error_strings->Set("suggestionsDetails", suggestions_details);
+ suggestions_details = error_strings->SetList(
+ "suggestionsDetails", base::MakeUnique<base::ListValue>());
+ suggestions_summary_list = error_strings->SetList(
+ "suggestionsSummaryList", base::MakeUnique<base::ListValue>());
} else {
- suggestions_summary_list = params->override_suggestions.release();
+ suggestions_summary_list = error_strings->SetList(
+ "suggestionsSummaryList", std::move(params->override_suggestions));
use_default_suggestions = false;
AddGoogleCachedCopyButton(suggestions_summary_list, error_strings);
}
- error_strings->Set("suggestionsSummaryList", suggestions_summary_list);
if (params->search_url.is_valid()) {
std::unique_ptr<base::DictionaryValue> search_suggestion(
@@ -994,12 +1001,12 @@ void LocalizedError::GetStrings(
#if defined(OS_ANDROID)
reload_visible = true;
#endif // defined(OS_ANDROID)
- base::DictionaryValue* reload_button = new base::DictionaryValue;
+ auto reload_button = base::MakeUnique<base::DictionaryValue>();
reload_button->SetString(
"msg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD));
reload_button->SetString("reloadUrl", failed_url.spec());
- error_strings->Set("reloadButton", reload_button);
reload_button->SetInteger("reloadTrackingId", params->reload_tracking_id);
+ error_strings->Set("reloadButton", std::move(reload_button));
}
// If not using the default suggestions, nothing else to do.
@@ -1033,7 +1040,7 @@ void LocalizedError::GetStrings(
(show_saved_copy_primary || show_saved_copy_secondary));
if (show_saved_copy_visible) {
- base::DictionaryValue* show_saved_copy_button = new base::DictionaryValue;
+ auto show_saved_copy_button = base::MakeUnique<base::DictionaryValue>();
show_saved_copy_button->SetString(
"msg", l10n_util::GetStringUTF16(
IDS_ERRORPAGES_BUTTON_SHOW_SAVED_COPY));
@@ -1042,7 +1049,8 @@ void LocalizedError::GetStrings(
l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_SHOW_SAVED_COPY_HELP));
if (show_saved_copy_primary)
show_saved_copy_button->SetString("primary", "true");
- error_strings->Set("showSavedCopyButton", show_saved_copy_button);
+ error_strings->Set("showSavedCopyButton",
+ std::move(show_saved_copy_button));
}
#if defined(OS_ANDROID)
diff --git a/chromium/components/error_page/renderer/net_error_helper_core.cc b/chromium/components/error_page/renderer/net_error_helper_core.cc
index 287cd84def6..89dff328466 100644
--- a/chromium/components/error_page/renderer/net_error_helper_core.cc
+++ b/chromium/components/error_page/renderer/net_error_helper_core.cc
@@ -209,7 +209,7 @@ std::string CreateRequestBody(
base::DictionaryValue request_dict;
request_dict.SetString("method", method);
request_dict.SetString("apiVersion", "v1");
- request_dict.Set("params", params_dict.release());
+ request_dict.Set("params", std::move(params_dict));
std::string request_body;
bool success = base::JSONWriter::Write(request_dict, &request_body);
diff --git a/chromium/components/error_page/renderer/net_error_helper_core_unittest.cc b/chromium/components/error_page/renderer/net_error_helper_core_unittest.cc
index f67d1cd4991..12bb4a425ec 100644
--- a/chromium/components/error_page/renderer/net_error_helper_core_unittest.cc
+++ b/chromium/components/error_page/renderer/net_error_helper_core_unittest.cc
@@ -9,12 +9,14 @@
#include <map>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/statistics_recorder.h"
#include "base/strings/stringprintf.h"
#include "base/test/histogram_tester.h"
@@ -82,12 +84,12 @@ const NavigationCorrection kDefaultCorrections[] = {
std::string SuggestionsToResponse(const NavigationCorrection* corrections,
int num_corrections) {
- base::ListValue* url_corrections = new base::ListValue();
+ auto url_corrections = base::MakeUnique<base::ListValue>();
for (int i = 0; i < num_corrections; ++i)
url_corrections->Append(corrections[i].ToValue());
base::DictionaryValue response;
- response.Set("result.UrlCorrections", url_corrections);
+ response.Set("result.UrlCorrections", std::move(url_corrections));
response.SetString("result.eventId", kNavigationCorrectionEventId);
response.SetString("result.fingerprint", kNavigationCorrectionFingerprint);
diff --git a/chromium/components/error_page_strings.grdp b/chromium/components/error_page_strings.grdp
index 7f1e15d0672..73459a2e7f0 100644
--- a/chromium/components/error_page_strings.grdp
+++ b/chromium/components/error_page_strings.grdp
@@ -28,10 +28,10 @@
</message>
<if expr="is_android">
<message name="IDS_ERRORPAGES_BUTTON_DOWNLOAD" desc="Label for the button on an error page to download the failed-to-load page at a later time">
- Download page later
+ Download when online
</message>
- <message name="IDS_ERRORPAGES_BUTTON_DOWNLOADING" desc="Label for the button on an error page when it is clicked to start downloading the failed-to-load page">
- Downloading
+ <message name="IDS_ERRORPAGES_BUTTON_DOWNLOADING" desc="Label for the button on an error page when it is clicked to queue the download for the failed-to-load page">
+ Waiting for connection…
</message>
</if>
diff --git a/chromium/components/exo/BUILD.gn b/chromium/components/exo/BUILD.gn
index 1b48931fed8..0546b563962 100644
--- a/chromium/components/exo/BUILD.gn
+++ b/chromium/components/exo/BUILD.gn
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//chrome/common/features.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
@@ -9,16 +10,26 @@ source_set("exo") {
sources = [
"buffer.cc",
"buffer.h",
- "compositor_frame_sink_holder.cc",
- "compositor_frame_sink_holder.h",
+ "data_device.cc",
+ "data_device.h",
+ "data_device_delegate.h",
+ "data_offer.cc",
+ "data_offer.h",
+ "data_offer_delegate.h",
+ "data_offer_observer.h",
+ "data_source.cc",
+ "data_source.h",
+ "data_source_delegate.h",
"display.cc",
"display.h",
- "gaming_seat.cc",
"gaming_seat.h",
"keyboard.cc",
"keyboard.h",
"keyboard_delegate.h",
"keyboard_device_configuration_delegate.h",
+ "keyboard_observer.h",
+ "layer_tree_frame_sink_holder.cc",
+ "layer_tree_frame_sink_holder.h",
"notification_surface.cc",
"notification_surface.h",
"notification_surface_manager.h",
@@ -35,6 +46,8 @@ source_set("exo") {
"surface.h",
"surface_delegate.h",
"surface_observer.h",
+ "surface_tree_host.cc",
+ "surface_tree_host.h",
"touch.cc",
"touch.h",
"touch_delegate.h",
@@ -68,10 +81,16 @@ source_set("exo") {
"//ui/views",
"//ui/views/mus",
"//ui/wm",
+ "//ui/wm/public",
]
- if (use_ozone) {
+ if (enable_exo_ozone_gamepad) {
+ defines = [ "USE_OZONE_GAMEPAD" ]
+ sources += [ "gaming_seat_ozone.cc" ]
deps += [ "//ui/ozone" ]
+ deps += [ "//ui/events/ozone:events_ozone_evdev" ]
+ } else {
+ sources += [ "gaming_seat.cc" ]
}
if (is_chromeos) {
@@ -91,8 +110,8 @@ source_set("test_support") {
deps = [
":exo",
+ "//ash:test_support_without_content",
"//ash/public/cpp:ash_public_cpp",
- "//ash/test:test_support_without_content",
"//base",
"//gpu",
"//skia",
@@ -101,7 +120,7 @@ source_set("test_support") {
"//ui/compositor",
"//ui/gfx/geometry",
"//ui/views",
- "//ui/wm:wm",
+ "//ui/wm",
]
}
@@ -110,8 +129,8 @@ source_set("unit_tests") {
sources = [
"buffer_unittest.cc",
+ "data_offer_unittest.cc",
"display_unittest.cc",
- "gaming_seat_unittest.cc",
"keyboard_unittest.cc",
"pointer_unittest.cc",
"shared_memory_unittest.cc",
@@ -125,20 +144,22 @@ source_set("unit_tests") {
":exo",
":test_support",
"//ash",
+ "//ash:test_support_without_content",
"//ash/public/cpp:ash_public_cpp",
- "//ash/test:test_support_without_content",
"//base",
"//base/test:test_support",
"//cc",
"//cc:test_support",
"//cc/surfaces:surfaces",
"//components/user_manager",
+ "//components/viz/service",
"//device/gamepad:test_helpers",
"//gpu/command_buffer/client:gles2_interface",
"//skia",
"//testing/gmock",
"//testing/gtest",
"//ui/aura",
+ "//ui/compositor:test_support",
"//ui/events:dom_keycode_converter",
"//ui/events:test_support",
"//ui/gfx",
@@ -151,6 +172,14 @@ source_set("unit_tests") {
if (use_ozone) {
deps += [ "//ui/ozone" ]
}
+
+ if (enable_exo_ozone_gamepad) {
+ sources += [
+ "../../ui/events/ozone/gamepad/gamepad_event.cc",
+ "gaming_seat_unittest.cc",
+ ]
+ deps += [ "//ui/events/ozone:events_ozone_evdev" ]
+ }
}
test("exo_unittests") {
@@ -162,10 +191,12 @@ test("exo_unittests") {
deps = [
":unit_tests",
+ "//ash:test_support_without_content",
"//ash/public/cpp:ash_public_cpp",
- "//ash/test:test_support_without_content",
"//base",
"//base/test:test_support",
+ "//cc:test_support",
+ "//components/viz/test:test_support",
"//device/gamepad:test_helpers",
"//mojo/edk/embedder:headers",
"//testing/gtest",
diff --git a/chromium/components/exo/DEPS b/chromium/components/exo/DEPS
index 6971866e26f..860fb114185 100644
--- a/chromium/components/exo/DEPS
+++ b/chromium/components/exo/DEPS
@@ -2,6 +2,8 @@ include_rules = [
"+ash",
"+cc",
"+chromeos/audio/chromeos_sounds.h",
+ "+components/viz/common",
+ "+components/viz/service",
"+device/gamepad",
"+gpu",
"+mojo/public/cpp",
diff --git a/chromium/components/exo/buffer.cc b/chromium/components/exo/buffer.cc
index d2141c9d050..011aa770f65 100644
--- a/chromium/components/exo/buffer.cc
+++ b/chromium/components/exo/buffer.cc
@@ -21,10 +21,11 @@
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
-#include "cc/output/context_provider.h"
#include "cc/resources/single_release_callback.h"
-#include "cc/resources/texture_mailbox.h"
-#include "components/exo/compositor_frame_sink_holder.h"
+#include "components/exo/layer_tree_frame_sink_holder.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/quads/resource_format.h"
+#include "components/viz/common/quads/texture_mailbox.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "ui/aura/env.h"
@@ -46,6 +47,7 @@ GLenum GLInternalFormat(gfx::BufferFormat format) {
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, // DXT5
GL_ETC1_RGB8_OES, // ETC1
GL_R8_EXT, // R_8
+ GL_R16_EXT, // R_16
GL_RG8_EXT, // RG_88
GL_RGB, // BGR_565
GL_RGBA, // RGBA_4444
@@ -97,9 +99,9 @@ void CreateGLTextureMailbox(gpu::gles2::GLES2Interface* gles2,
class Buffer::Texture : public ui::ContextFactoryObserver {
public:
Texture(ui::ContextFactory* context_factory,
- cc::ContextProvider* context_provider);
+ viz::ContextProvider* context_provider);
Texture(ui::ContextFactory* context_factory,
- cc::ContextProvider* context_provider,
+ viz::ContextProvider* context_provider,
gfx::GpuMemoryBuffer* gpu_memory_buffer,
unsigned texture_target,
unsigned query_type);
@@ -144,8 +146,9 @@ class Buffer::Texture : public ui::ContextFactoryObserver {
void ScheduleWaitForRelease(base::TimeDelta delay);
void WaitForRelease();
+ gfx::GpuMemoryBuffer* const gpu_memory_buffer_;
ui::ContextFactory* context_factory_;
- scoped_refptr<cc::ContextProvider> context_provider_;
+ scoped_refptr<viz::ContextProvider> context_provider_;
const unsigned texture_target_;
const unsigned query_type_;
const GLenum internalformat_;
@@ -162,8 +165,9 @@ class Buffer::Texture : public ui::ContextFactoryObserver {
};
Buffer::Texture::Texture(ui::ContextFactory* context_factory,
- cc::ContextProvider* context_provider)
- : context_factory_(context_factory),
+ viz::ContextProvider* context_provider)
+ : gpu_memory_buffer_(nullptr),
+ context_factory_(context_factory),
context_provider_(context_provider),
texture_target_(GL_TEXTURE_2D),
query_type_(GL_COMMANDS_COMPLETED_CHROMIUM),
@@ -178,11 +182,12 @@ Buffer::Texture::Texture(ui::ContextFactory* context_factory,
}
Buffer::Texture::Texture(ui::ContextFactory* context_factory,
- cc::ContextProvider* context_provider,
+ viz::ContextProvider* context_provider,
gfx::GpuMemoryBuffer* gpu_memory_buffer,
unsigned texture_target,
unsigned query_type)
- : context_factory_(context_factory),
+ : gpu_memory_buffer_(gpu_memory_buffer),
+ context_factory_(context_factory),
context_provider_(context_provider),
texture_target_(texture_target),
query_type_(query_type),
@@ -203,12 +208,15 @@ Buffer::Texture::Texture(ui::ContextFactory* context_factory,
Buffer::Texture::~Texture() {
DestroyResources();
- context_factory_->RemoveObserver(this);
+ if (context_provider_)
+ context_factory_->RemoveObserver(this);
}
void Buffer::Texture::OnLostResources() {
DestroyResources();
+ context_factory_->RemoveObserver(this);
context_provider_ = nullptr;
+ context_factory_ = nullptr;
}
bool Buffer::Texture::IsLost() {
@@ -250,6 +258,8 @@ gpu::SyncToken Buffer::Texture::BindTexImage() {
uint64_t fence_sync = gles2->InsertFenceSyncCHROMIUM();
gles2->OrderingBarrierCHROMIUM();
gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
+ TRACE_EVENT_ASYNC_STEP_INTO0("exo", "BufferInUse", gpu_memory_buffer_,
+ "bound");
}
return sync_token;
}
@@ -327,6 +337,8 @@ void Buffer::Texture::ReleaseWhenQueryResultIsAvailable(
base::TimeDelta::FromMilliseconds(kWaitForReleaseDelayMs);
wait_for_release_time_ = base::TimeTicks::Now() + wait_for_release_delay;
ScheduleWaitForRelease(wait_for_release_delay);
+ TRACE_EVENT_ASYNC_STEP_INTO0("exo", "BufferInUse", gpu_memory_buffer_,
+ "pending_query");
context_provider_->ContextSupport()->SignalQuery(
query_id_,
base::Bind(&Buffer::Texture::Released, weak_ptr_factory_.GetWeakPtr()));
@@ -401,11 +413,13 @@ Buffer::Buffer(std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
Buffer::~Buffer() {}
bool Buffer::ProduceTransferableResource(
- CompositorFrameSinkHolder* compositor_frame_sink_holder,
+ LayerTreeFrameSinkHolder* layer_tree_frame_sink_holder,
cc::ResourceId resource_id,
bool secure_output_only,
bool client_usage,
cc::TransferableResource* resource) {
+ TRACE_EVENT0("exo", "Buffer::ProduceTransferableResource");
+
DCHECK(attach_count_);
DLOG_IF(WARNING, !release_contents_callback_.IsCancelled() && client_usage)
<< "Producing a texture mailbox for a buffer that has not been released";
@@ -419,7 +433,7 @@ bool Buffer::ProduceTransferableResource(
ui::ContextFactory* context_factory =
aura::Env::GetInstance()->context_factory();
// Note: This can fail if GPU acceleration has been disabled.
- scoped_refptr<cc::ContextProvider> context_provider =
+ scoped_refptr<viz::ContextProvider> context_provider =
context_factory->SharedMainThreadContextProvider();
if (!context_provider) {
DLOG(WARNING) << "Failed to acquire a context provider";
@@ -429,7 +443,7 @@ bool Buffer::ProduceTransferableResource(
}
resource->id = resource_id;
- resource->format = cc::RGBA_8888;
+ resource->format = viz::RGBA_8888;
resource->filter = GL_LINEAR;
resource->size = gpu_memory_buffer_->GetSize();
@@ -443,6 +457,9 @@ bool Buffer::ProduceTransferableResource(
}
Texture* contents_texture = contents_texture_.get();
+ if (release_contents_callback_.IsCancelled())
+ TRACE_EVENT_ASYNC_BEGIN0("exo", "BufferInUse", gpu_memory_buffer_.get());
+
// Cancel pending contents release callback.
release_contents_callback_.Reset(
base::Bind(&Buffer::ReleaseContents, base::Unretained(this)));
@@ -458,7 +475,7 @@ bool Buffer::ProduceTransferableResource(
// The contents texture will be released when no longer used by the
// compositor.
- compositor_frame_sink_holder->SetResourceReleaseCallback(
+ layer_tree_frame_sink_holder->SetResourceReleaseCallback(
resource_id,
base::Bind(&Buffer::Texture::ReleaseTexImage,
base::Unretained(contents_texture),
@@ -488,7 +505,7 @@ bool Buffer::ProduceTransferableResource(
// The mailbox texture will be released when no longer used by the
// compositor.
- compositor_frame_sink_holder->SetResourceReleaseCallback(
+ layer_tree_frame_sink_holder->SetResourceReleaseCallback(
resource_id,
base::Bind(&Buffer::Texture::Release, base::Unretained(texture),
base::Bind(&Buffer::ReleaseTexture, AsWeakPtr(),
@@ -535,6 +552,8 @@ std::unique_ptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const {
// Buffer, private:
void Buffer::Release() {
+ TRACE_EVENT_ASYNC_END0("exo", "BufferInUse", gpu_memory_buffer_.get());
+
// Run release callback to notify the client that buffer has been released.
if (!release_callback_.is_null())
release_callback_.Run();
@@ -556,9 +575,13 @@ void Buffer::ReleaseContents() {
// Cancel callback to indicate that buffer has been released.
release_contents_callback_.Cancel();
- // Release buffer if not attached to surface.
- if (!attach_count_)
+ if (attach_count_) {
+ TRACE_EVENT_ASYNC_STEP_INTO0("exo", "BufferInUse", gpu_memory_buffer_.get(),
+ "attached");
+ } else {
+ // Release buffer if not attached to surface.
Release();
+ }
}
} // namespace exo
diff --git a/chromium/components/exo/buffer.h b/chromium/components/exo/buffer.h
index 54097d315fb..890edf4ce9a 100644
--- a/chromium/components/exo/buffer.h
+++ b/chromium/components/exo/buffer.h
@@ -26,7 +26,7 @@ class GpuMemoryBuffer;
namespace exo {
-class CompositorFrameSinkHolder;
+class LayerTreeFrameSinkHolder;
// This class provides the content for a Surface. The mechanism by which a
// client provides and updates the contents is the responsibility of the client
@@ -53,7 +53,7 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
// be called before a new texture mailbox can be acquired unless
// |non_client_usage| is true.
bool ProduceTransferableResource(
- CompositorFrameSinkHolder* compositor_frame_sink_holder,
+ LayerTreeFrameSinkHolder* layer_tree_frame_sink_holder,
cc::ResourceId resource_id,
bool secure_output_only,
bool client_usage,
diff --git a/chromium/components/exo/buffer_unittest.cc b/chromium/components/exo/buffer_unittest.cc
index 9fa73791f3e..29e32e5b1d4 100644
--- a/chromium/components/exo/buffer_unittest.cc
+++ b/chromium/components/exo/buffer_unittest.cc
@@ -5,17 +5,18 @@
#include <GLES2/gl2extchromium.h>
#include "base/bind.h"
-#include "cc/output/context_provider.h"
#include "cc/resources/single_release_callback.h"
#include "components/exo/buffer.h"
#include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.h"
#include "components/exo/test/exo_test_helper.h"
+#include "components/viz/common/gpu/context_provider.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "ui/aura/env.h"
#include "ui/compositor/compositor.h"
+#include "ui/compositor/test/in_process_context_factory.h"
#include "ui/gfx/gpu_memory_buffer.h"
namespace exo {
@@ -32,9 +33,9 @@ TEST_F(BufferTest, ReleaseCallback) {
std::unique_ptr<Buffer> buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
std::unique_ptr<Surface> surface(new Surface);
- const cc::FrameSinkId arbitrary_frame_sink_id(1, 1);
- CompositorFrameSinkHolder* compositor_frame_sink_holder =
- surface->compositor_frame_sink_holder();
+ const viz::FrameSinkId arbitrary_frame_sink_id(1, 1);
+ LayerTreeFrameSinkHolder* layer_tree_frame_sink_holder =
+ surface->layer_tree_frame_sink_holder();
// Set the release callback.
int release_call_count = 0;
@@ -44,7 +45,7 @@ TEST_F(BufferTest, ReleaseCallback) {
buffer->OnAttach();
cc::TransferableResource resource;
// Produce a transferable resource for the contents of the buffer.
- bool rv = buffer->ProduceTransferableResource(compositor_frame_sink_holder, 0,
+ bool rv = buffer->ProduceTransferableResource(layer_tree_frame_sink_holder, 0,
false, true, &resource);
ASSERT_TRUE(rv);
@@ -53,8 +54,8 @@ TEST_F(BufferTest, ReleaseCallback) {
returned_resource.id = resource.id;
returned_resource.sync_token = resource.mailbox_holder.sync_token;
returned_resource.lost = false;
- cc::ReturnedResourceArray resources = {returned_resource};
- compositor_frame_sink_holder->ReclaimResources(resources);
+ std::vector<cc::ReturnedResource> resources = {returned_resource};
+ layer_tree_frame_sink_holder->ReclaimResources(resources);
RunAllPendingInMessageLoop();
ASSERT_EQ(release_call_count, 0);
@@ -69,20 +70,20 @@ TEST_F(BufferTest, IsLost) {
gfx::Size buffer_size(256, 256);
std::unique_ptr<Buffer> buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
- const cc::FrameSinkId arbitrary_frame_sink_id(1, 1);
+ const viz::FrameSinkId arbitrary_frame_sink_id(1, 1);
std::unique_ptr<Surface> surface(new Surface);
- CompositorFrameSinkHolder* compositor_frame_sink_holder =
- surface->compositor_frame_sink_holder();
+ LayerTreeFrameSinkHolder* layer_tree_frame_sink_holder =
+ surface->layer_tree_frame_sink_holder();
cc::ResourceId resource_id = 0;
buffer->OnAttach();
// Acquire a texture transferable resource for the contents of the buffer.
cc::TransferableResource resource;
bool rv = buffer->ProduceTransferableResource(
- compositor_frame_sink_holder, resource_id, false, true, &resource);
+ layer_tree_frame_sink_holder, resource_id, false, true, &resource);
ASSERT_TRUE(rv);
- scoped_refptr<cc::ContextProvider> context_provider =
+ scoped_refptr<viz::ContextProvider> context_provider =
aura::Env::GetInstance()
->context_factory()
->SharedMainThreadContextProvider();
@@ -98,8 +99,8 @@ TEST_F(BufferTest, IsLost) {
returned_resource.id = resource_id;
returned_resource.sync_token = gpu::SyncToken();
returned_resource.lost = is_lost;
- cc::ReturnedResourceArray resources = {returned_resource};
- compositor_frame_sink_holder->ReclaimResources(resources);
+ std::vector<cc::ReturnedResource> resources = {returned_resource};
+ layer_tree_frame_sink_holder->ReclaimResources(resources);
RunAllPendingInMessageLoop();
// Producing a new texture transferable resource for the contents of the
@@ -107,7 +108,7 @@ TEST_F(BufferTest, IsLost) {
++resource_id;
cc::TransferableResource new_resource;
rv = buffer->ProduceTransferableResource(
- compositor_frame_sink_holder, resource_id, false, false, &new_resource);
+ layer_tree_frame_sink_holder, resource_id, false, false, &new_resource);
ASSERT_TRUE(rv);
buffer->OnDetach();
@@ -115,10 +116,34 @@ TEST_F(BufferTest, IsLost) {
returned_resource2.id = resource_id;
returned_resource2.sync_token = gpu::SyncToken();
returned_resource2.lost = false;
- cc::ReturnedResourceArray resources2 = {returned_resource2};
- compositor_frame_sink_holder->ReclaimResources(resources2);
+ std::vector<cc::ReturnedResource> resources2 = {returned_resource2};
+ layer_tree_frame_sink_holder->ReclaimResources(resources2);
RunAllPendingInMessageLoop();
}
+// Buffer::Texture::OnLostResources is called when the gpu crashes. This test
+// verifies that the Texture is collected properly in such event.
+TEST_F(BufferTest, OnLostResources) {
+ // Create a Buffer and use it to produce a Texture.
+ constexpr gfx::Size buffer_size(256, 256);
+ auto buffer = base::MakeUnique<Buffer>(
+ exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
+ auto surface_tree_host =
+ base::MakeUnique<SurfaceTreeHost>("BufferTest", nullptr);
+ LayerTreeFrameSinkHolder* frame_sink_holder =
+ surface_tree_host->layer_tree_frame_sink_holder();
+
+ buffer->OnAttach();
+ // Acquire a texture transferable resource for the contents of the buffer.
+ viz::TransferableResource resource;
+ bool rv = buffer->ProduceTransferableResource(frame_sink_holder, false, true,
+ &resource);
+ ASSERT_TRUE(rv);
+
+ static_cast<ui::InProcessContextFactory*>(
+ aura::Env::GetInstance()->context_factory())
+ ->SendOnLostResources();
+}
+
} // namespace
} // namespace exo
diff --git a/chromium/components/exo/compositor_frame_sink_holder.cc b/chromium/components/exo/compositor_frame_sink_holder.cc
deleted file mode 100644
index f63c358bce8..00000000000
--- a/chromium/components/exo/compositor_frame_sink_holder.cc
+++ /dev/null
@@ -1,82 +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/exo/compositor_frame_sink_holder.h"
-
-#include "cc/output/compositor_frame_sink.h"
-#include "cc/resources/returned_resource.h"
-#include "components/exo/surface.h"
-
-namespace exo {
-
-////////////////////////////////////////////////////////////////////////////////
-// CompositorFrameSinkHolder, public:
-
-CompositorFrameSinkHolder::CompositorFrameSinkHolder(
- Surface* surface,
- std::unique_ptr<cc::CompositorFrameSink> frame_sink)
- : surface_(surface),
- frame_sink_(std::move(frame_sink)),
- weak_factory_(this) {
- surface_->AddSurfaceObserver(this);
- frame_sink_->BindToClient(this);
-}
-
-CompositorFrameSinkHolder::~CompositorFrameSinkHolder() {
- frame_sink_->DetachFromClient();
- if (surface_)
- surface_->RemoveSurfaceObserver(this);
-
- // Release all resources which aren't returned from CompositorFrameSink.
- for (auto& callback : release_callbacks_)
- callback.second.Run(gpu::SyncToken(), false);
-}
-
-bool CompositorFrameSinkHolder::HasReleaseCallbackForResource(
- cc::ResourceId id) {
- return release_callbacks_.find(id) != release_callbacks_.end();
-}
-
-void CompositorFrameSinkHolder::SetResourceReleaseCallback(
- cc::ResourceId id,
- const cc::ReleaseCallback& callback) {
- DCHECK(!callback.is_null());
- release_callbacks_[id] = callback;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// cc::CompositorFrameSinkClient overrides:
-
-void CompositorFrameSinkHolder::SetBeginFrameSource(
- cc::BeginFrameSource* source) {
- if (surface_)
- surface_->SetBeginFrameSource(source);
-}
-
-void CompositorFrameSinkHolder::ReclaimResources(
- const cc::ReturnedResourceArray& resources) {
- for (auto& resource : resources) {
- auto it = release_callbacks_.find(resource.id);
- DCHECK(it != release_callbacks_.end());
- if (it != release_callbacks_.end()) {
- it->second.Run(resource.sync_token, resource.lost);
- release_callbacks_.erase(it);
- }
- }
-}
-
-void CompositorFrameSinkHolder::DidReceiveCompositorFrameAck() {
- if (surface_)
- surface_->DidReceiveCompositorFrameAck();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// SurfaceObserver overrides:
-
-void CompositorFrameSinkHolder::OnSurfaceDestroying(Surface* surface) {
- surface_->RemoveSurfaceObserver(this);
- surface_ = nullptr;
-}
-
-} // namespace exo
diff --git a/chromium/components/exo/compositor_frame_sink_holder.h b/chromium/components/exo/compositor_frame_sink_holder.h
deleted file mode 100644
index 53255618d15..00000000000
--- a/chromium/components/exo/compositor_frame_sink_holder.h
+++ /dev/null
@@ -1,79 +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_EXO_COMPOSITOR_FRAME_SINK_HOLDER_H_
-#define COMPONENTS_EXO_COMPOSITOR_FRAME_SINK_HOLDER_H_
-
-#include <memory>
-
-#include "base/containers/flat_map.h"
-#include "cc/output/compositor_frame_sink_client.h"
-#include "cc/resources/release_callback.h"
-#include "components/exo/surface_observer.h"
-
-namespace cc {
-class CompositorFrameSink;
-}
-
-namespace exo {
-class Surface;
-
-// This class talks to MojoCompositorFrameSink and keeps track of references to
-// the contents of Buffers. It's keeped alive by references from
-// release_callbacks_. It's destroyed when its owning Surface is destroyed and
-// the last outstanding release callback is called.
-class CompositorFrameSinkHolder : public cc::CompositorFrameSinkClient,
- public SurfaceObserver {
- public:
- CompositorFrameSinkHolder(
- Surface* surface,
- std::unique_ptr<cc::CompositorFrameSink> frame_sink);
- ~CompositorFrameSinkHolder() override;
-
- bool HasReleaseCallbackForResource(cc::ResourceId id);
- void SetResourceReleaseCallback(cc::ResourceId id,
- const cc::ReleaseCallback& callback);
-
- cc::CompositorFrameSink* GetCompositorFrameSink() {
- return frame_sink_.get();
- }
-
- base::WeakPtr<CompositorFrameSinkHolder> GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
-
- // Overridden from cc::CompositorFrameSinkClient:
- void SetBeginFrameSource(cc::BeginFrameSource* source) override;
- void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
- void SetTreeActivationCallback(const base::Closure& callback) override {}
- void DidReceiveCompositorFrameAck() override;
- void DidLoseCompositorFrameSink() override {}
- void OnDraw(const gfx::Transform& transform,
- const gfx::Rect& viewport,
- bool resourceless_software_draw) override {}
- void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override {}
- void SetExternalTilePriorityConstraints(
- const gfx::Rect& viewport_rect,
- const gfx::Transform& transform) override {}
-
- // Overridden from SurfaceObserver:
- void OnSurfaceDestroying(Surface* surface) override;
-
- private:
-
- // A collection of callbacks used to release resources.
- using ResourceReleaseCallbackMap = base::flat_map<int, cc::ReleaseCallback>;
- ResourceReleaseCallbackMap release_callbacks_;
-
- Surface* surface_;
- std::unique_ptr<cc::CompositorFrameSink> frame_sink_;
-
- base::WeakPtrFactory<CompositorFrameSinkHolder> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkHolder);
-};
-
-} // namespace exo
-
-#endif // COMPONENTS_EXO_COMPOSITOR_FRAME_SINK_HOLDER_H_
diff --git a/chromium/components/exo/data_device.cc b/chromium/components/exo/data_device.cc
new file mode 100644
index 00000000000..4836e59163f
--- /dev/null
+++ b/chromium/components/exo/data_device.cc
@@ -0,0 +1,128 @@
+// 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/exo/data_device.h"
+
+#include "base/logging.h"
+#include "components/exo/data_device_delegate.h"
+#include "components/exo/data_offer.h"
+#include "components/exo/surface.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/drop_target_event.h"
+
+namespace exo {
+
+DataDevice::DataDevice(DataDeviceDelegate* delegate, FileHelper* file_helper)
+ : delegate_(delegate), file_helper_(file_helper), data_offer_(nullptr) {
+ WMHelper::GetInstance()->AddDragDropObserver(this);
+}
+
+DataDevice::~DataDevice() {
+ delegate_->OnDataDeviceDestroying(this);
+ WMHelper::GetInstance()->RemoveDragDropObserver(this);
+ ClearDataOffer();
+}
+
+void DataDevice::StartDrag(const DataSource* source_resource,
+ Surface* origin_resource,
+ Surface* icon_resource,
+ uint32_t serial) {
+ // TODO(hirono): Check if serial is valid. crbug.com/746111
+ NOTIMPLEMENTED();
+}
+
+void DataDevice::SetSelection(const DataSource* source, uint32_t serial) {
+ // TODO(hirono): Check if serial is valid. crbug.com/746111
+ NOTIMPLEMENTED();
+}
+
+void DataDevice::OnDragEntered(const ui::DropTargetEvent& event) {
+ DCHECK(!data_offer_);
+
+ Surface* surface = GetEffectiveTargetForEvent(event);
+ if (!surface)
+ return;
+
+ base::flat_set<DndAction> dnd_actions;
+ if (event.source_operations() & ui::DragDropTypes::DRAG_MOVE) {
+ dnd_actions.insert(DndAction::kMove);
+ }
+ if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) {
+ dnd_actions.insert(DndAction::kCopy);
+ }
+ if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) {
+ dnd_actions.insert(DndAction::kAsk);
+ }
+
+ data_offer_ = delegate_->OnDataOffer();
+ data_offer_->AddObserver(this);
+ data_offer_->SetDropData(file_helper_, event.data());
+ data_offer_->SetSourceActions(dnd_actions);
+ data_offer_->SetActions(base::flat_set<DndAction>(), DndAction::kAsk);
+ delegate_->OnEnter(surface, event.location_f(), *data_offer_);
+}
+
+int DataDevice::OnDragUpdated(const ui::DropTargetEvent& event) {
+ if (!data_offer_)
+ return ui::DragDropTypes::DRAG_NONE;
+
+ delegate_->OnMotion(event.time_stamp(), event.location_f());
+
+ // TODO(hirono): dnd_action() here may not be updated. Chrome needs to provide
+ // a way to update DND action asynchronously.
+ switch (data_offer_->dnd_action()) {
+ case DndAction::kMove:
+ return ui::DragDropTypes::DRAG_MOVE;
+ case DndAction::kCopy:
+ return ui::DragDropTypes::DRAG_COPY;
+ case DndAction::kAsk:
+ return ui::DragDropTypes::DRAG_LINK;
+ case DndAction::kNone:
+ return ui::DragDropTypes::DRAG_NONE;
+ }
+}
+
+void DataDevice::OnDragExited() {
+ if (!data_offer_)
+ return;
+
+ delegate_->OnLeave();
+ ClearDataOffer();
+}
+
+int DataDevice::OnPerformDrop(const ui::DropTargetEvent& event) {
+ if (!data_offer_)
+ return ui::DragDropTypes::DRAG_NONE;
+
+ delegate_->OnDrop();
+ ClearDataOffer();
+ return ui::DragDropTypes::DRAG_NONE;
+}
+
+void DataDevice::OnDataOfferDestroying(DataOffer* data_offer) {
+ if (data_offer_ == data_offer)
+ ClearDataOffer();
+}
+
+Surface* DataDevice::GetEffectiveTargetForEvent(
+ const ui::DropTargetEvent& event) const {
+ aura::Window* window = static_cast<aura::Window*>(event.target());
+ if (!window)
+ return nullptr;
+ Surface* target = Surface::AsSurface(window);
+ if (!target)
+ return nullptr;
+
+ return delegate_->CanAcceptDataEventsForSurface(target) ? target : nullptr;
+}
+
+void DataDevice::ClearDataOffer() {
+ if (!data_offer_)
+ return;
+ data_offer_->RemoveObserver(this);
+ data_offer_ = nullptr;
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/data_device.h b/chromium/components/exo/data_device.h
new file mode 100644
index 00000000000..7cdd332a067
--- /dev/null
+++ b/chromium/components/exo/data_device.h
@@ -0,0 +1,72 @@
+// 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_EXO_DATA_DEVICE_H_
+#define COMPONENTS_EXO_DATA_DEVICE_H_
+
+#include <cstdint>
+
+#include "base/macros.h"
+#include "components/exo/data_offer_observer.h"
+#include "components/exo/wm_helper.h"
+
+namespace ui {
+class DropTargetEvent;
+}
+
+namespace exo {
+
+class DataDeviceDelegate;
+class DataOffer;
+class DataSource;
+class FileHelper;
+class Surface;
+
+enum class DndAction { kNone, kCopy, kMove, kAsk };
+
+// Data transfer device providing access to inter-client data transfer
+// mechanisms such as copy-and-paste and drag-and-drop.
+class DataDevice : public WMHelper::DragDropObserver, public DataOfferObserver {
+ public:
+ explicit DataDevice(DataDeviceDelegate* delegate, FileHelper* file_helper);
+ ~DataDevice() override;
+
+ // Starts drag-and-drop operation.
+ // |source| is data source for the eventual transfer or null if data passing
+ // is handled by a client internally. |origin| is a surface where the drag
+ // originates. |icon| is drag-and-drop icon surface, which can be nullptr.
+ // |serial| is a unique number of implicit grab.
+ void StartDrag(const DataSource* source,
+ Surface* origin,
+ Surface* icon,
+ uint32_t serial);
+
+ // Copies data to the selection.
+ // |source| is data source for the selection, or nullptr to unset the
+ // selection. |serial| is a unique number of event which tigers SetSelection.
+ void SetSelection(const DataSource* source, uint32_t serial);
+
+ // Overridden from WMHelper::DragDropObserver:
+ void OnDragEntered(const ui::DropTargetEvent& event) override;
+ int OnDragUpdated(const ui::DropTargetEvent& event) override;
+ void OnDragExited() override;
+ int OnPerformDrop(const ui::DropTargetEvent& event) override;
+
+ // Overridden from DataOfferObserver:
+ void OnDataOfferDestroying(DataOffer* data_offer) override;
+
+ private:
+ Surface* GetEffectiveTargetForEvent(const ui::DropTargetEvent& event) const;
+ void ClearDataOffer();
+
+ DataDeviceDelegate* const delegate_;
+ FileHelper* const file_helper_;
+ DataOffer* data_offer_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataDevice);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_DEVICE_H_
diff --git a/chromium/components/exo/data_device_delegate.h b/chromium/components/exo/data_device_delegate.h
new file mode 100644
index 00000000000..83e87b3df71
--- /dev/null
+++ b/chromium/components/exo/data_device_delegate.h
@@ -0,0 +1,68 @@
+// 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_EXO_DATA_DEVICE_DELEGATE_H_
+#define COMPONENTS_EXO_DATA_DEVICE_DELEGATE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+
+namespace base {
+class TimeTicks;
+}
+
+namespace gfx {
+class PointF;
+}
+
+namespace exo {
+
+class DataDevice;
+class DataOffer;
+class Surface;
+enum class DndAction;
+
+// Handles events on data devices in context-specific ways.
+class DataDeviceDelegate {
+ public:
+ // Called at the top of the data device's destructor, to give observers a
+ // chance to remove themselves.
+ virtual void OnDataDeviceDestroying(DataDevice* data_device) = 0;
+
+ // Called when DataOffer object is delivered from a client. DataDeviceDelegate
+ // has responsibility to release the returned DataOffer object.
+ virtual DataOffer* OnDataOffer() = 0;
+
+ // Called during a drag operation when pointer enters |surface|.
+ virtual void OnEnter(Surface* surface,
+ const gfx::PointF& location,
+ const DataOffer& data_offer) = 0;
+
+ // Called during a drag operation when pointer leaves |surface|.
+ virtual void OnLeave() = 0;
+
+ // Called during a drag operation when pointer moves on the |surface|.
+ virtual void OnMotion(base::TimeTicks time_stamp,
+ const gfx::PointF& location) = 0;
+
+ // Called during a drag operation when user drops dragging data on the
+ // |surface|.
+ virtual void OnDrop() = 0;
+
+ // Called when the data is pasted on the DataDevice.
+ virtual void OnSelection(const DataOffer& data_offer) = 0;
+
+ // This should return true if |surface| is a valid target for this data
+ // device. E.g. the surface is owned by the same client as the data device.
+ virtual bool CanAcceptDataEventsForSurface(Surface* surface) = 0;
+
+ protected:
+ virtual ~DataDeviceDelegate() {}
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_DEVICE_DELEGATE_H_
diff --git a/chromium/components/exo/data_offer.cc b/chromium/components/exo/data_offer.cc
new file mode 100644
index 00000000000..d9fa390cd90
--- /dev/null
+++ b/chromium/components/exo/data_offer.cc
@@ -0,0 +1,130 @@
+// 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/exo/data_offer.h"
+
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "components/exo/data_offer_delegate.h"
+#include "components/exo/data_offer_observer.h"
+#include "components/exo/file_helper.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "url/gurl.h"
+
+namespace exo {
+namespace {
+
+class RefCountedString16 : public base::RefCountedMemory {
+ public:
+ static scoped_refptr<RefCountedString16> TakeString(
+ base::string16&& to_destroy) {
+ scoped_refptr<RefCountedString16> self(new RefCountedString16);
+ to_destroy.swap(self->data_);
+ return self;
+ }
+
+ // Overridden from base::RefCountedMemory:
+ const unsigned char* front() const override {
+ return reinterpret_cast<const unsigned char*>(data_.data());
+ }
+ size_t size() const override { return data_.size() * sizeof(base::char16); }
+
+ protected:
+ ~RefCountedString16() override {}
+
+ private:
+ base::string16 data_;
+};
+
+void WriteFileDescriptor(base::ScopedFD fd,
+ scoped_refptr<base::RefCountedMemory> memory) {
+ if (!base::WriteFileDescriptor(fd.get(),
+ reinterpret_cast<const char*>(memory->front()),
+ memory->size()))
+ DLOG(ERROR) << "Failed to write drop data";
+}
+
+} // namespace
+
+DataOffer::DataOffer(DataOfferDelegate* delegate) : delegate_(delegate) {}
+
+DataOffer::~DataOffer() {
+ delegate_->OnDataOfferDestroying(this);
+ for (DataOfferObserver& observer : observers_) {
+ observer.OnDataOfferDestroying(this);
+ }
+}
+
+void DataOffer::AddObserver(DataOfferObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void DataOffer::RemoveObserver(DataOfferObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void DataOffer::Accept(const std::string& mime_type) {
+ NOTIMPLEMENTED();
+}
+
+void DataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) {
+ const auto it = drop_data_.find(mime_type);
+ if (it == drop_data_.end()) {
+ DLOG(ERROR) << "Unexpected mime type is requested";
+ return;
+ }
+
+ base::PostTaskWithTraits(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+ base::BindOnce(&WriteFileDescriptor, std::move(fd), it->second));
+}
+
+void DataOffer::Finish() {
+ NOTIMPLEMENTED();
+}
+
+void DataOffer::SetActions(const base::flat_set<DndAction>& dnd_actions,
+ DndAction preferred_action) {
+ dnd_action_ = preferred_action;
+ delegate_->OnAction(preferred_action);
+}
+
+void DataOffer::SetSourceActions(
+ const base::flat_set<DndAction>& source_actions) {
+ source_actions_ = source_actions;
+ delegate_->OnSourceActions(source_actions);
+}
+
+void DataOffer::SetDropData(FileHelper* file_helper,
+ const ui::OSExchangeData& data) {
+ DCHECK_EQ(0u, drop_data_.size());
+ if (data.HasString()) {
+ base::string16 string_content;
+ if (data.GetString(&string_content)) {
+ drop_data_.emplace(
+ std::string(ui::Clipboard::kMimeTypeText),
+ RefCountedString16::TakeString(std::move(string_content)));
+ }
+ }
+ if (data.HasFile()) {
+ base::FilePath path;
+ if (data.GetFilename(&path)) {
+ GURL url;
+ if (file_helper->ConvertPathToUrl(path, &url)) {
+ base::string16 url_string = base::UTF8ToUTF16(url.spec());
+ drop_data_.emplace(
+ file_helper->GetMimeTypeForUriList(),
+ RefCountedString16::TakeString(std::move(url_string)));
+ }
+ }
+ }
+ for (const auto& pair : drop_data_) {
+ delegate_->OnOffer(pair.first);
+ }
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/data_offer.h b/chromium/components/exo/data_offer.h
new file mode 100644
index 00000000000..a1872a8f4d8
--- /dev/null
+++ b/chromium/components/exo/data_offer.h
@@ -0,0 +1,79 @@
+// 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_EXO_DATA_OFFER_H_
+#define COMPONENTS_EXO_DATA_OFFER_H_
+
+#include <cstdint>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "ui/base/class_property.h"
+
+namespace base {
+class RefCountedMemory;
+}
+
+namespace ui {
+class OSExchangeData;
+}
+
+namespace exo {
+
+class DataOfferDelegate;
+class DataOfferObserver;
+class FileHelper;
+enum class DndAction;
+
+// Object representing transferred data offered to a client.
+class DataOffer : public ui::PropertyHandler {
+ public:
+ explicit DataOffer(DataOfferDelegate* delegate);
+ ~DataOffer();
+
+ void AddObserver(DataOfferObserver* observer);
+ void RemoveObserver(DataOfferObserver* observer);
+
+ // Accepts one of the offered mime types.
+ void Accept(const std::string& mime_type);
+
+ // Requests that the data is transferred. |fd| is a file descriptor for data
+ // transfer.
+ void Receive(const std::string& mime_type, base::ScopedFD fd);
+
+ // Called when the client is no longer using the data offer object.
+ void Finish();
+
+ // Sets the available/preferred drag-and-drop actions.
+ void SetActions(const base::flat_set<DndAction>& dnd_actions,
+ DndAction preferred_action);
+
+ // Sets drop data.
+ void SetDropData(FileHelper* file_helper, const ui::OSExchangeData& data);
+
+ // Sets source actions.
+ void SetSourceActions(const base::flat_set<DndAction>& source_actions);
+
+ DndAction dnd_action() { return dnd_action_; }
+
+ private:
+ DataOfferDelegate* const delegate_;
+
+ // Map between mime type and drop data bytes.
+ base::flat_map<std::string, scoped_refptr<base::RefCountedMemory>> drop_data_;
+ base::flat_set<DndAction> source_actions_;
+ DndAction dnd_action_;
+ base::ObserverList<DataOfferObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataOffer);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_OFFER_H_
diff --git a/chromium/components/exo/data_offer_delegate.h b/chromium/components/exo/data_offer_delegate.h
new file mode 100644
index 00000000000..41337c345ae
--- /dev/null
+++ b/chromium/components/exo/data_offer_delegate.h
@@ -0,0 +1,38 @@
+// 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_EXO_DATA_OFFER_DELEGATE_H_
+#define COMPONENTS_EXO_DATA_OFFER_DELEGATE_H_
+
+#include <string>
+
+namespace exo {
+
+class DataOffer;
+enum class DndAction;
+
+// Handles events on data devices in context-specific ways.
+class DataOfferDelegate {
+ public:
+ // Called at the top of the data device's destructor, to give observers a
+ // chance to remove themselves.
+ virtual void OnDataOfferDestroying(DataOffer* offer) = 0;
+
+ // Called when |mime_type| is offered by the client.
+ virtual void OnOffer(const std::string& mime_type) = 0;
+
+ // Called when possible |source_actions| is offered by the client.
+ virtual void OnSourceActions(
+ const base::flat_set<DndAction>& source_actions) = 0;
+
+ // Called when current |action| is offered by the client.
+ virtual void OnAction(DndAction action) = 0;
+
+ protected:
+ virtual ~DataOfferDelegate() {}
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_OFFER_DELEGATE_H_
diff --git a/chromium/components/exo/data_offer_observer.h b/chromium/components/exo/data_offer_observer.h
new file mode 100644
index 00000000000..d05b0352146
--- /dev/null
+++ b/chromium/components/exo/data_offer_observer.h
@@ -0,0 +1,27 @@
+// 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_EXO_DATA_OFFER_OBSERVER_H_
+#define COMPONENTS_EXO_DATA_OFFER_OBSERVER_H_
+
+#include <string>
+
+namespace exo {
+
+class DataOffer;
+
+// Handles events on data devices in context-specific ways.
+class DataOfferObserver {
+ public:
+ // Called at the top of the data device's destructor, to give observers a
+ // chance to remove themselves.
+ virtual void OnDataOfferDestroying(DataOffer* offer) = 0;
+
+ protected:
+ virtual ~DataOfferObserver() {}
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_OFFER_OBSERVER_H_
diff --git a/chromium/components/exo/data_offer_unittest.cc b/chromium/components/exo/data_offer_unittest.cc
new file mode 100644
index 00000000000..03e9eae0d1c
--- /dev/null
+++ b/chromium/components/exo/data_offer_unittest.cc
@@ -0,0 +1,92 @@
+// 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/exo/data_offer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/exo/data_device.h"
+#include "components/exo/data_offer_delegate.h"
+#include "components/exo/test/exo_test_base.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+
+namespace exo {
+namespace {
+
+using DataOfferTest = test::ExoTestBase;
+
+class TestDataOfferDelegate : public DataOfferDelegate {
+ public:
+ TestDataOfferDelegate() : dnd_action_(DndAction::kNone) {}
+
+ // Called at the top of the data device's destructor, to give observers a
+ // chance to remove themselves.
+ void OnDataOfferDestroying(DataOffer* offer) override {}
+
+ // Called when |mime_type| is offered by the client.
+ void OnOffer(const std::string& mime_type) override {
+ mime_types_.push_back(mime_type);
+ }
+
+ // Called when possible |source_actions| is offered by the client.
+ void OnSourceActions(
+ const base::flat_set<DndAction>& source_actions) override {
+ source_actions_ = source_actions;
+ }
+
+ // Called when current |action| is offered by the client.
+ void OnAction(DndAction dnd_action) override { dnd_action_ = dnd_action; }
+
+ const std::vector<std::string>& mime_types() const { return mime_types_; }
+ const base::flat_set<DndAction>& source_actions() const {
+ return source_actions_;
+ }
+ DndAction dnd_action() const { return dnd_action_; }
+
+ private:
+ std::vector<std::string> mime_types_;
+ base::flat_set<DndAction> source_actions_;
+ DndAction dnd_action_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDataOfferDelegate);
+};
+
+TEST_F(DataOfferTest, SendEvents) {
+ ui::OSExchangeData data;
+ data.SetString(base::string16(base::ASCIIToUTF16("Test data")));
+
+ base::flat_set<DndAction> source_actions;
+ source_actions.insert(DndAction::kCopy);
+ source_actions.insert(DndAction::kMove);
+
+ std::unique_ptr<TestDataOfferDelegate> delegate =
+ base::MakeUnique<TestDataOfferDelegate>();
+ std::unique_ptr<DataOffer> data_offer =
+ base::MakeUnique<DataOffer>(delegate.get());
+
+ EXPECT_EQ(0u, delegate->mime_types().size());
+ EXPECT_EQ(0u, delegate->source_actions().size());
+ EXPECT_EQ(DndAction::kNone, delegate->dnd_action());
+
+ data_offer->SetDropData(nullptr, data);
+ data_offer->SetSourceActions(source_actions);
+ data_offer->SetActions(base::flat_set<DndAction>(), DndAction::kMove);
+
+ EXPECT_EQ(1u, delegate->mime_types().size());
+ EXPECT_EQ("text/plain", delegate->mime_types()[0]);
+ EXPECT_EQ(2u, delegate->source_actions().size());
+ EXPECT_EQ(1u, delegate->source_actions().count(DndAction::kCopy));
+ EXPECT_EQ(1u, delegate->source_actions().count(DndAction::kMove));
+ EXPECT_EQ(DndAction::kMove, delegate->dnd_action());
+}
+
+} // namespace
+} // namespace exo
diff --git a/chromium/components/exo/data_source.cc b/chromium/components/exo/data_source.cc
new file mode 100644
index 00000000000..e70ddfa7a55
--- /dev/null
+++ b/chromium/components/exo/data_source.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/exo/data_source.h"
+
+#include "components/exo/data_source_delegate.h"
+
+namespace exo {
+
+DataSource::DataSource(DataSourceDelegate* delegate) : delegate_(delegate) {}
+
+DataSource::~DataSource() {
+ delegate_->OnDataSourceDestroying(this);
+}
+
+void DataSource::Offer(const std::string& mime_type) {
+ NOTIMPLEMENTED();
+}
+
+void DataSource::SetActions(const base::flat_set<DndAction>& dnd_actions) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/data_source.h b/chromium/components/exo/data_source.h
new file mode 100644
index 00000000000..c8a1b7ac8b0
--- /dev/null
+++ b/chromium/components/exo/data_source.h
@@ -0,0 +1,38 @@
+// 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_EXO_DATA_SOURCE_H_
+#define COMPONENTS_EXO_DATA_SOURCE_H_
+
+#include <string>
+
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+
+namespace exo {
+
+class DataSourceDelegate;
+enum class DndAction;
+
+// Object representing transferred data offered by a client.
+class DataSource {
+ public:
+ explicit DataSource(DataSourceDelegate* delegate);
+ ~DataSource();
+
+ // Adds an offered mime type.
+ void Offer(const std::string& mime_type);
+
+ // Sets the available drag-and-drop actions.
+ void SetActions(const base::flat_set<DndAction>& dnd_actions);
+
+ private:
+ DataSourceDelegate* const delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataSource);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_SOURCE_H_
diff --git a/chromium/components/exo/data_source_delegate.h b/chromium/components/exo/data_source_delegate.h
new file mode 100644
index 00000000000..6c2c2bfe9d2
--- /dev/null
+++ b/chromium/components/exo/data_source_delegate.h
@@ -0,0 +1,47 @@
+// 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_EXO_DATA_SOURCE_DELEGATE_H_
+#define COMPONENTS_EXO_DATA_SOURCE_DELEGATE_H_
+
+#include <string>
+
+#include "base/files/scoped_file.h"
+
+namespace exo {
+
+class DataSource;
+
+// Handles events on data devices in context-specific ways.
+class DataSourceDelegate {
+ public:
+ // Called at the top of the data device's destructor, to give observers a
+ // chance to remove themselves.
+ virtual void OnDataSourceDestroying(DataSource* source) = 0;
+
+ // Called when a target accepts an offered mime type.
+ virtual void OnTarget(const std::string& mime_type) = 0;
+
+ // Called when the data is requested.
+ virtual void OnSend(const std::string& mime_type, base::ScopedFD fd) = 0;
+
+ // Called when selection was cancelled.
+ virtual void OnCancelled() = 0;
+
+ // Called when the drag-and-drop operation physically finished.
+ virtual void OnDndDropPerformed() = 0;
+
+ // Called when the drag-and-drop operation concluded.
+ virtual void OnDndFinished() = 0;
+
+ // Called when the action is selected by the compositor.
+ virtual void OnAction(DndAction dnd_action) = 0;
+
+ protected:
+ virtual ~DataSourceDelegate() {}
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_SOURCE_DELEGATE_H_
diff --git a/chromium/components/exo/display.cc b/chromium/components/exo/display.cc
index 7e38bb47686..6bf7395a08a 100644
--- a/chromium/components/exo/display.cc
+++ b/chromium/components/exo/display.cc
@@ -12,6 +12,8 @@
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
+#include "components/exo/data_device.h"
+#include "components/exo/file_helper.h"
#include "components/exo/notification_surface.h"
#include "components/exo/notification_surface_manager.h"
#include "components/exo/shared_memory.h"
@@ -53,10 +55,12 @@ const gfx::BufferFormat kOverlayFormatsForDrmAtomic[] = {
////////////////////////////////////////////////////////////////////////////////
// Display, public:
-Display::Display() : Display(nullptr) {}
+Display::Display() : Display(nullptr, std::unique_ptr<FileHelper>()) {}
-Display::Display(NotificationSurfaceManager* notification_surface_manager)
- : notification_surface_manager_(notification_surface_manager)
+Display::Display(NotificationSurfaceManager* notification_surface_manager,
+ std::unique_ptr<FileHelper> file_helper)
+ : notification_surface_manager_(notification_surface_manager),
+ file_helper_(std::move(file_helper))
#if defined(USE_OZONE)
,
overlay_formats_(std::begin(kOverlayFormats), std::end(kOverlayFormats))
@@ -216,18 +220,23 @@ std::unique_ptr<SubSurface> Display::CreateSubSurface(Surface* surface,
std::unique_ptr<NotificationSurface> Display::CreateNotificationSurface(
Surface* surface,
- const std::string& notification_id) {
+ const std::string& notification_key) {
TRACE_EVENT2("exo", "Display::CreateNotificationSurface", "surface",
- surface->AsTracedValue(), "notification_id", notification_id);
+ surface->AsTracedValue(), "notification_key", notification_key);
if (!notification_surface_manager_ ||
- notification_surface_manager_->GetSurface(notification_id)) {
- DLOG(ERROR) << "Invalid notification id, id=" << notification_id;
+ notification_surface_manager_->GetSurface(notification_key)) {
+ DLOG(ERROR) << "Invalid notification key, key=" << notification_key;
return nullptr;
}
return base::MakeUnique<NotificationSurface>(notification_surface_manager_,
- surface, notification_id);
+ surface, notification_key);
+}
+
+std::unique_ptr<DataDevice> Display::CreateDataDevice(
+ DataDeviceDelegate* delegate) {
+ return base::MakeUnique<DataDevice>(delegate, file_helper_.get());
}
} // namespace exo
diff --git a/chromium/components/exo/display.h b/chromium/components/exo/display.h
index 780f4040bb2..e115d200017 100644
--- a/chromium/components/exo/display.h
+++ b/chromium/components/exo/display.h
@@ -25,6 +25,9 @@ class Point;
}
namespace exo {
+class DataDevice;
+class DataDeviceDelegate;
+class FileHelper;
class NotificationSurface;
class NotificationSurfaceManager;
class SharedMemory;
@@ -42,7 +45,8 @@ class Buffer;
class Display {
public:
Display();
- explicit Display(NotificationSurfaceManager* notification_surface_manager);
+ Display(NotificationSurfaceManager* notification_surface_manager,
+ std::unique_ptr<FileHelper> file_helper);
~Display();
// Creates a new surface.
@@ -86,10 +90,14 @@ class Display {
// Creates a notification surface for a surface and notification id.
std::unique_ptr<NotificationSurface> CreateNotificationSurface(
Surface* surface,
- const std::string& notification_id);
+ const std::string& notification_key);
+
+ // Creates a data device for a |delegate|.
+ std::unique_ptr<DataDevice> CreateDataDevice(DataDeviceDelegate* delegate);
private:
NotificationSurfaceManager* const notification_surface_manager_;
+ std::unique_ptr<FileHelper> file_helper_;
#if defined(USE_OZONE)
std::vector<gfx::BufferFormat> overlay_formats_;
diff --git a/chromium/components/exo/display_unittest.cc b/chromium/components/exo/display_unittest.cc
index a9036a4115d..68aca8980e4 100644
--- a/chromium/components/exo/display_unittest.cc
+++ b/chromium/components/exo/display_unittest.cc
@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/exo/display.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "components/exo/buffer.h"
-#include "components/exo/display.h"
+#include "components/exo/data_device.h"
+#include "components/exo/data_device_delegate.h"
+#include "components/exo/file_helper.h"
#include "components/exo/shared_memory.h"
#include "components/exo/shell_surface.h"
#include "components/exo/sub_surface.h"
@@ -222,5 +225,42 @@ TEST_F(DisplayTest, CreateSubSurface) {
EXPECT_TRUE(display->CreateSubSurface(parent.get(), toplevel.get()));
}
+class TestDataDeviceDelegate : public DataDeviceDelegate {
+ public:
+ // Overriden from DataDeviceDelegate:
+ void OnDataDeviceDestroying(DataDevice* data_device) override {}
+ DataOffer* OnDataOffer() override { return nullptr; }
+ void OnEnter(Surface* surface,
+ const gfx::PointF& location,
+ const DataOffer& data_offer) override {}
+ void OnLeave() override {}
+ void OnMotion(base::TimeTicks time_stamp,
+ const gfx::PointF& location) override {}
+ void OnDrop() override {}
+ void OnSelection(const DataOffer& data_offer) override {}
+ bool CanAcceptDataEventsForSurface(Surface* surface) override {
+ return false;
+ }
+};
+
+class TestFileHelper : public FileHelper {
+ public:
+ // Overriden from TestFileHelper:
+ TestFileHelper() {}
+ std::string GetMimeTypeForUriList() const override { return ""; }
+ bool ConvertPathToUrl(const base::FilePath& path, GURL* out) override {
+ return true;
+ }
+};
+
+TEST_F(DisplayTest, CreateDataDevice) {
+ TestDataDeviceDelegate device_delegate;
+ Display display(nullptr, base::MakeUnique<TestFileHelper>());
+
+ std::unique_ptr<DataDevice> device =
+ display.CreateDataDevice(&device_delegate);
+ EXPECT_TRUE(device.get());
+}
+
} // namespace
} // namespace exo
diff --git a/chromium/components/exo/file_helper.h b/chromium/components/exo/file_helper.h
new file mode 100644
index 00000000000..ccf1c9c1e24
--- /dev/null
+++ b/chromium/components/exo/file_helper.h
@@ -0,0 +1,37 @@
+// 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_EXO_FILE_HELPER_H_
+#define COMPONENTS_EXO_FILE_HELPER_H_
+
+#include <string>
+
+class GURL;
+
+namespace base {
+class FilePath;
+}
+
+namespace exo {
+
+class FileHelper {
+ public:
+ virtual ~FileHelper() {}
+
+ // Returns mime type which is used for list of Uris returned by this
+ // FileHelper.
+ virtual std::string GetMimeTypeForUriList() const = 0;
+
+ // Convert natife file path to URL which can be used in container. We don't
+ // expose enter file system to a container directly. Instead we mount
+ // specific directory in the containers' namespace. Thus we need to convert
+ // native path to file URL which points mount point in containers. The
+ // conversion should be container specific, now we only have ARC container
+ // though.
+ virtual bool ConvertPathToUrl(const base::FilePath& path, GURL* out) = 0;
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_FILE_HELPER_H_
diff --git a/chromium/components/exo/gaming_seat.cc b/chromium/components/exo/gaming_seat.cc
index 016be145bb9..6a6a0549b98 100644
--- a/chromium/components/exo/gaming_seat.cc
+++ b/chromium/components/exo/gaming_seat.cc
@@ -7,10 +7,6 @@
#include <cmath>
#include "base/bind.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat_delegate.h"
#include "components/exo/shell_surface.h"
@@ -60,7 +56,7 @@ class GamingSeat::ThreadSafeGamepadChangeFetcher
create_fetcher_callback_(create_fetcher_callback),
polling_task_runner_(task_runner),
origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
- thread_checker_.DetachFromThread();
+ DETACH_FROM_THREAD(thread_checker_);
}
// Enable or disable gamepad polling. Can be called from any thread.
@@ -79,7 +75,7 @@ class GamingSeat::ThreadSafeGamepadChangeFetcher
// Enables or disables polling.
void EnablePollingOnPollingThread(bool enabled) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
is_enabled_ = enabled;
if (is_enabled_) {
@@ -96,7 +92,7 @@ class GamingSeat::ThreadSafeGamepadChangeFetcher
// Schedules the next poll on the polling thread.
void SchedulePollOnPollingThread() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(fetcher_);
if (!is_enabled_ || has_poll_scheduled_)
@@ -112,7 +108,7 @@ class GamingSeat::ThreadSafeGamepadChangeFetcher
// Polls devices for new data and posts gamepad changes back to origin thread.
void PollOnPollingThread() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
has_poll_scheduled_ = false;
if (!is_enabled_)
@@ -180,7 +176,7 @@ class GamingSeat::ThreadSafeGamepadChangeFetcher
bool is_enabled_ = false;
// ThreadChecker for the polling thread.
- base::ThreadChecker thread_checker_;
+ THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(ThreadSafeGamepadChangeFetcher);
};
@@ -190,20 +186,13 @@ class GamingSeat::ThreadSafeGamepadChangeFetcher
GamingSeat::GamingSeat(GamingSeatDelegate* gaming_seat_delegate,
base::SingleThreadTaskRunner* polling_task_runner)
- : GamingSeat(gaming_seat_delegate,
- polling_task_runner,
- base::Bind(CreateGamepadPlatformDataFetcher)) {}
-
-GamingSeat::GamingSeat(GamingSeatDelegate* gaming_seat_delegate,
- base::SingleThreadTaskRunner* polling_task_runner,
- CreateGamepadDataFetcherCallback create_fetcher_callback)
: delegate_(gaming_seat_delegate),
gamepad_delegates_{nullptr},
weak_ptr_factory_(this) {
gamepad_change_fetcher_ = new ThreadSafeGamepadChangeFetcher(
base::Bind(&GamingSeat::ProcessGamepadChanges,
weak_ptr_factory_.GetWeakPtr()),
- create_fetcher_callback, polling_task_runner);
+ base::Bind(CreateGamepadPlatformDataFetcher), polling_task_runner);
auto* helper = WMHelper::GetInstance();
helper->AddFocusObserver(this);
@@ -229,7 +218,7 @@ GamingSeat::~GamingSeat() {
void GamingSeat::OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Surface* target = nullptr;
if (gained_focus) {
target = Surface::AsSurface(gained_focus);
@@ -250,7 +239,7 @@ void GamingSeat::OnWindowFocused(aura::Window* gained_focus,
void GamingSeat::ProcessGamepadChanges(int index,
const device::Gamepad new_pad) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
bool send_frame = false;
device::Gamepad& pad_state = pad_state_.items[index];
diff --git a/chromium/components/exo/gaming_seat.h b/chromium/components/exo/gaming_seat.h
index 40e44ae3335..2c9787f08a9 100644
--- a/chromium/components/exo/gaming_seat.h
+++ b/chromium/components/exo/gaming_seat.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
@@ -17,6 +18,10 @@
#include "device/gamepad/gamepad_data_fetcher.h"
#include "ui/aura/client/focus_change_observer.h"
+#if defined(USE_OZONE_GAMEPAD)
+#include "ui/events/ozone/gamepad/gamepad_observer.h"
+#endif
+
namespace exo {
class GamingSeatDelegate;
class GamepadDelegate;
@@ -24,29 +29,43 @@ class GamepadDelegate;
using CreateGamepadDataFetcherCallback =
base::Callback<std::unique_ptr<device::GamepadDataFetcher>()>;
-// This class represents one gaming seat, it uses a background thread
-// for polling gamepad devices and notifies the corresponding GampadDelegate of
-// any changes.
-class GamingSeat : public WMHelper::FocusObserver {
+// TODO(jkwang): always use ozone_gamepad when ozone is default for all Chrome
+// OS builds. https://crbug.com/717246
+// This class represents one gaming seat. It uses /device/gamepad or
+// ozone/gamepad as backend and notifies corresponding GamepadDelegate of any
+// gamepad changes.
+class GamingSeat : public WMHelper::FocusObserver
+#if defined(USE_OZONE_GAMEPAD)
+ ,
+ public ui::GamepadObserver
+#endif
+{
public:
- // This class will post tasks to invoke the delegate on the thread runner
- // which is associated with the thread that is creating this instance.
+ // This class will monitor gamepad connection changes and manage gamepad
+ // returned by gaming_seat_delegate.
GamingSeat(GamingSeatDelegate* gaming_seat_delegate,
base::SingleThreadTaskRunner* polling_task_runner);
- // Allows test cases to specify a CreateGamepadDataFetcherCallback that
- // overrides the default GamepadPlatformDataFetcher.
- GamingSeat(GamingSeatDelegate* gaming_seat_delegate,
- base::SingleThreadTaskRunner* polling_task_runner,
- CreateGamepadDataFetcherCallback create_fetcher_callback);
-
~GamingSeat() override;
- // Overridden WMHelper::FocusObserver:
+ // Overridden from WMHelper::FocusObserver:
void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) override;
+#if defined(USE_OZONE_GAMEPAD)
+ // Overridden from ui::GamepadObserver:
+ void OnGamepadDevicesUpdated() override;
+ void OnGamepadEvent(const ui::GamepadEvent& event) override;
+#endif
+
private:
+ // The delegate that handles gamepad_added.
+ GamingSeatDelegate* const delegate_;
+
+#if defined(USE_OZONE_GAMEPAD)
+ // Contains the delegate for each gamepad device.
+ base::flat_map<int, GamepadDelegate*> gamepads_;
+#else
class ThreadSafeGamepadChangeFetcher;
// Processes updates of gamepad data and passes changes on to delegate.
@@ -56,9 +75,6 @@ class GamingSeat : public WMHelper::FocusObserver {
// polling thread.
scoped_refptr<ThreadSafeGamepadChangeFetcher> gamepad_change_fetcher_;
- // The delegate that handles gamepad_added.
- GamingSeatDelegate* const delegate_;
-
// The delegate instances that all other events are dispatched to.
GamepadDelegate* gamepad_delegates_[device::Gamepads::kItemsLengthCap];
@@ -66,9 +82,10 @@ class GamingSeat : public WMHelper::FocusObserver {
device::Gamepads pad_state_;
// ThreadChecker for the origin thread.
- base::ThreadChecker thread_checker_;
+ THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<GamingSeat> weak_ptr_factory_;
+#endif
DISALLOW_COPY_AND_ASSIGN(GamingSeat);
};
diff --git a/chromium/components/exo/gaming_seat_ozone.cc b/chromium/components/exo/gaming_seat_ozone.cc
new file mode 100644
index 00000000000..0429e95c925
--- /dev/null
+++ b/chromium/components/exo/gaming_seat_ozone.cc
@@ -0,0 +1,108 @@
+// 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/exo/gamepad_delegate.h"
+#include "components/exo/gaming_seat.h"
+#include "components/exo/gaming_seat_delegate.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/surface.h"
+#include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
+
+namespace exo {
+
+////////////////////////////////////////////////////////////////////////////////
+// GamingSeat, public:
+
+GamingSeat::GamingSeat(GamingSeatDelegate* delegate,
+ base::SingleThreadTaskRunner* task_runner)
+ : delegate_(delegate) {
+ auto* helper = WMHelper::GetInstance();
+ helper->AddFocusObserver(this);
+ OnWindowFocused(helper->GetFocusedWindow(), nullptr);
+}
+
+GamingSeat::~GamingSeat() {
+ ui::GamepadProviderOzone::GetInstance()->RemoveGamepadObserver(this);
+ delegate_->OnGamingSeatDestroying(this);
+ // Disconnect all the gamepads.
+ for (auto& entry : gamepads_)
+ entry.second->OnRemoved();
+
+ WMHelper::GetInstance()->RemoveFocusObserver(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WMHelper::FocusObserver overrides:
+
+void GamingSeat::OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) {
+ Surface* target = nullptr;
+ if (gained_focus) {
+ target = Surface::AsSurface(gained_focus);
+ if (!target) {
+ aura::Window* top_level_window = gained_focus->GetToplevelWindow();
+ if (top_level_window)
+ target = ShellSurface::GetMainSurface(top_level_window);
+ }
+ }
+
+ bool focused = target && delegate_->CanAcceptGamepadEventsForSurface(target);
+ if (focused) {
+ ui::GamepadProviderOzone::GetInstance()->AddGamepadObserver(this);
+ OnGamepadDevicesUpdated();
+ } else {
+ ui::GamepadProviderOzone::GetInstance()->RemoveGamepadObserver(this);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ui::GamepadObserver overrides:
+
+void GamingSeat::OnGamepadDevicesUpdated() {
+ std::vector<ui::InputDevice> gamepad_devices =
+ ui::GamepadProviderOzone::GetInstance()->GetGamepadDevices();
+
+ base::flat_map<int, GamepadDelegate*> new_gamepads;
+
+ // Copy the "still connected gamepads".
+ for (auto& device : gamepad_devices) {
+ auto it = gamepads_.find(device.id);
+ if (it != gamepads_.end()) {
+ new_gamepads[device.id] = it->second;
+ gamepads_.erase(it);
+ }
+ }
+
+ // Remove each disconected gamepad.
+ for (auto& entry : gamepads_)
+ entry.second->OnRemoved();
+
+ // Add each new connected gamepad.
+ for (auto& device : gamepad_devices) {
+ if (new_gamepads.find(device.id) == new_gamepads.end())
+ new_gamepads[device.id] = delegate_->GamepadAdded();
+ }
+
+ new_gamepads.swap(gamepads_);
+}
+
+void GamingSeat::OnGamepadEvent(const ui::GamepadEvent& event) {
+ auto it = gamepads_.find(event.device_id());
+ if (it == gamepads_.end())
+ return;
+
+ switch (event.type()) {
+ case ui::GamepadEventType::BUTTON:
+ it->second->OnButton(event.code(), event.value(), event.value());
+ break;
+ case ui::GamepadEventType::AXIS:
+ it->second->OnAxis(event.code(), event.value());
+ break;
+ case ui::GamepadEventType::FRAME:
+ it->second->OnFrame();
+ break;
+ }
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/gaming_seat_unittest.cc b/chromium/components/exo/gaming_seat_unittest.cc
index 5a7893deb61..74adde898e2 100644
--- a/chromium/components/exo/gaming_seat_unittest.cc
+++ b/chromium/components/exo/gaming_seat_unittest.cc
@@ -6,7 +6,6 @@
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/run_loop.h"
-#include "base/test/test_simple_task_runner.h"
#include "components/exo/buffer.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat_delegate.h"
@@ -18,6 +17,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/focus_client.h"
+#include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
namespace exo {
namespace {
@@ -45,54 +45,36 @@ class MockGamepadDelegate : public GamepadDelegate {
class GamingSeatTest : public test::ExoTestBase {
public:
GamingSeatTest() {}
-
- std::unique_ptr<device::GamepadDataFetcher> MockDataFetcherFactory() {
- device::Gamepads initial_data;
- std::unique_ptr<device::MockGamepadDataFetcher> fetcher(
- new device::MockGamepadDataFetcher(initial_data));
- mock_data_fetcher_ = fetcher.get();
- return std::move(fetcher);
- }
-
void InitializeGamingSeat(MockGamingSeatDelegate* delegate) {
- polling_task_runner_ = new base::TestSimpleTaskRunner();
- gaming_seat_.reset(
- new GamingSeat(delegate, polling_task_runner_.get(),
- base::Bind(&GamingSeatTest::MockDataFetcherFactory,
- base::Unretained(this))));
- // Run the polling task runner to have it create the data fetcher.
- polling_task_runner_->RunPendingTasks();
+ gaming_seat_.reset(new GamingSeat(delegate, nullptr));
}
void DestroyGamingSeat(MockGamingSeatDelegate* delegate) {
EXPECT_CALL(*delegate, Die()).Times(1);
- mock_data_fetcher_ = nullptr;
gaming_seat_.reset();
- // Process tasks until polling is shut down.
- polling_task_runner_->RunPendingTasks();
- polling_task_runner_ = nullptr;
}
- void SetDataAndPostToDelegate(const device::Gamepads& new_data) {
- ASSERT_TRUE(mock_data_fetcher_ != nullptr);
- mock_data_fetcher_->SetTestData(new_data);
- // Run one polling cycle, which will post a task to the origin task runner.
- polling_task_runner_->RunPendingTasks();
- // Run origin task runner to invoke delegate.
- base::RunLoop().RunUntilIdle();
+ void UpdateGamepadDevice(const std::vector<int>& gamepad_device_ids) {
+ std::vector<ui::InputDevice> gamepad_devices;
+ for (auto& id : gamepad_device_ids) {
+ gamepad_devices.push_back(ui::InputDevice(
+ id, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, "gamepad"));
+ }
+ ui::GamepadProviderOzone::GetInstance()->DispatchGamepadDevicesUpdated(
+ gamepad_devices);
+ }
+
+ void SendFrameToGamepads(const std::vector<int>& gamepad_device_ids) {
+ for (auto& id : gamepad_device_ids) {
+ ui::GamepadEvent event(id, ui::GamepadEventType::FRAME, 0, 0,
+ base::TimeTicks());
+ ui::GamepadProviderOzone::GetInstance()->DispatchGamepadEvent(event);
+ }
}
protected:
std::unique_ptr<GamingSeat> gaming_seat_;
- // Task runner to simulate the polling thread.
- scoped_refptr<base::TestSimpleTaskRunner> polling_task_runner_;
-
- // Weak reference to the mock data fetcher provided by MockDataFetcherFactory.
- // This instance is valid until both gamepad_ and polling_task_runner_ are
- // shut down.
- device::MockGamepadDataFetcher* mock_data_fetcher_;
-
DISALLOW_COPY_AND_ASSIGN(GamingSeatTest);
};
@@ -112,212 +94,51 @@ TEST_F(GamingSeatTest, ConnectionChange) {
.WillOnce(testing::Return(true));
InitializeGamingSeat(gaming_seat_delegate);
- testing::StrictMock<MockGamepadDelegate> gamepad_delegate0;
- testing::StrictMock<MockGamepadDelegate> gamepad_delegate1;
- testing::StrictMock<MockGamepadDelegate> gamepad_delegate2;
+ testing::StrictMock<MockGamepadDelegate> gamepad_delegate[6];
{ // Test sequence
testing::InSequence s;
- // connect gamepad 0
- // connect gamepad 2
- // connect gamepad 1
+ // Connect 2 gamepads.
+ EXPECT_CALL(*gaming_seat_delegate, GamepadAdded())
+ .WillOnce(testing::Return(&gamepad_delegate[0]))
+ .WillOnce(testing::Return(&gamepad_delegate[1]));
+ // Send frame to connected gamepad.
+ EXPECT_CALL(gamepad_delegate[0], OnFrame()).Times(1);
+ EXPECT_CALL(gamepad_delegate[1], OnFrame()).Times(1);
+ // Connect 3 more.
+ EXPECT_CALL(*gaming_seat_delegate, GamepadAdded())
+ .WillOnce(testing::Return(&gamepad_delegate[2]))
+ .WillOnce(testing::Return(&gamepad_delegate[3]))
+ .WillOnce(testing::Return(&gamepad_delegate[4]));
+ // Send frame to all gamepads.
+ EXPECT_CALL(gamepad_delegate[0], OnFrame()).Times(1);
+ EXPECT_CALL(gamepad_delegate[1], OnFrame()).Times(1);
+ EXPECT_CALL(gamepad_delegate[2], OnFrame()).Times(1);
+ EXPECT_CALL(gamepad_delegate[3], OnFrame()).Times(1);
+ EXPECT_CALL(gamepad_delegate[4], OnFrame()).Times(1);
+ // Disconnect gamepad 0 and gamepad 2 and connect a new gamepad.
+ EXPECT_CALL(gamepad_delegate[0], OnRemoved()).Times(1);
+ EXPECT_CALL(gamepad_delegate[2], OnRemoved()).Times(1);
+ EXPECT_CALL(gamepad_delegate[4], OnRemoved()).Times(1);
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded())
- .WillOnce(testing::Return(&gamepad_delegate0))
- .WillOnce(testing::Return(&gamepad_delegate2))
- .WillOnce(testing::Return(&gamepad_delegate1));
- // disconnect gamepad 1
- EXPECT_CALL(gamepad_delegate1, OnRemoved()).Times(1);
+ .WillOnce(testing::Return(&gamepad_delegate[5]));
+ // Send frame to all gamepads.
+ EXPECT_CALL(gamepad_delegate[1], OnFrame()).Times(1);
+ EXPECT_CALL(gamepad_delegate[3], OnFrame()).Times(1);
+ EXPECT_CALL(gamepad_delegate[5], OnFrame()).Times(1);
+
// disconnect other gamepads
- EXPECT_CALL(gamepad_delegate0, OnRemoved()).Times(1);
- EXPECT_CALL(gamepad_delegate2, OnRemoved()).Times(1);
+ EXPECT_CALL(gamepad_delegate[1], OnRemoved()).Times(1);
+ EXPECT_CALL(gamepad_delegate[3], OnRemoved()).Times(1);
+ EXPECT_CALL(gamepad_delegate[5], OnRemoved()).Times(1);
}
// Gamepad connected.
- device::Gamepads gamepad_connected;
- gamepad_connected.items[0].connected = true;
- gamepad_connected.items[0].timestamp = 1;
- SetDataAndPostToDelegate(gamepad_connected);
-
- gamepad_connected.items[2].connected = true;
- gamepad_connected.items[2].timestamp = 1;
- SetDataAndPostToDelegate(gamepad_connected);
-
- gamepad_connected.items[1].connected = true;
- gamepad_connected.items[1].timestamp = 1;
- SetDataAndPostToDelegate(gamepad_connected);
-
- // Gamepad 1 is dis connected
- gamepad_connected.items[1].connected = false;
- gamepad_connected.items[1].timestamp = 2;
-
- SetDataAndPostToDelegate(gamepad_connected);
-
- // Gamepad disconnected.
- device::Gamepads all_disconnected;
- SetDataAndPostToDelegate(all_disconnected);
-
- DestroyGamingSeat(gaming_seat_delegate);
-}
-
-TEST_F(GamingSeatTest, OnAxis) {
- 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();
-
- testing::StrictMock<MockGamingSeatDelegate>* gaming_seat_delegate =
- new testing::StrictMock<MockGamingSeatDelegate>();
- EXPECT_CALL(*gaming_seat_delegate,
- CanAcceptGamepadEventsForSurface(testing::_))
- .WillOnce(testing::Return(true));
-
- InitializeGamingSeat(gaming_seat_delegate);
- testing::StrictMock<MockGamepadDelegate> gamepad_delegate0;
- testing::StrictMock<MockGamepadDelegate> gamepad_delegate2;
-
- // connect gamepad 0 and 2
- EXPECT_CALL(*gaming_seat_delegate, GamepadAdded())
- .WillOnce(testing::Return(&gamepad_delegate0))
- .WillOnce(testing::Return(&gamepad_delegate2));
-
- device::Gamepads gamepad_connected;
- gamepad_connected.items[0].connected = true;
- gamepad_connected.items[0].timestamp = 1;
- SetDataAndPostToDelegate(gamepad_connected);
-
- gamepad_connected.items[2].connected = true;
- gamepad_connected.items[2].timestamp = 1;
- SetDataAndPostToDelegate(gamepad_connected);
-
- // send axis event to 2 and then 0
- device::Gamepads axis_moved;
- axis_moved.items[0].connected = true;
- axis_moved.items[0].timestamp = 1;
- axis_moved.items[2].connected = true;
- axis_moved.items[2].timestamp = 2;
- axis_moved.items[2].axes_length = 1;
- axis_moved.items[2].axes[0] = 1.0;
-
- EXPECT_CALL(gamepad_delegate2, OnAxis(0, 1.0)).Times(1);
- EXPECT_CALL(gamepad_delegate2, OnFrame()).Times(1);
- SetDataAndPostToDelegate(axis_moved);
-
- axis_moved.items[0].timestamp = 2;
- axis_moved.items[0].axes_length = 1;
- axis_moved.items[0].axes[0] = 2.0;
-
- EXPECT_CALL(gamepad_delegate0, OnAxis(0, 2.0)).Times(1);
- EXPECT_CALL(gamepad_delegate0, OnFrame()).Times(1);
- SetDataAndPostToDelegate(axis_moved);
-
- EXPECT_CALL(gamepad_delegate0, OnRemoved()).Times(1);
- EXPECT_CALL(gamepad_delegate2, OnRemoved()).Times(1);
- DestroyGamingSeat(gaming_seat_delegate);
-}
-
-TEST_F(GamingSeatTest, OnButton) {
- 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();
-
- testing::StrictMock<MockGamingSeatDelegate>* gaming_seat_delegate =
- new testing::StrictMock<MockGamingSeatDelegate>();
- EXPECT_CALL(*gaming_seat_delegate,
- CanAcceptGamepadEventsForSurface(testing::_))
- .WillOnce(testing::Return(true));
-
- InitializeGamingSeat(gaming_seat_delegate);
- testing::StrictMock<MockGamepadDelegate> gamepad_delegate0;
- testing::StrictMock<MockGamepadDelegate> gamepad_delegate2;
-
- // connect gamepad 0 and 2
- EXPECT_CALL(*gaming_seat_delegate, GamepadAdded())
- .WillOnce(testing::Return(&gamepad_delegate0))
- .WillOnce(testing::Return(&gamepad_delegate2));
-
- device::Gamepads gamepad_connected;
- gamepad_connected.items[0].connected = true;
- gamepad_connected.items[0].timestamp = 1;
- SetDataAndPostToDelegate(gamepad_connected);
-
- gamepad_connected.items[2].connected = true;
- gamepad_connected.items[2].timestamp = 1;
- SetDataAndPostToDelegate(gamepad_connected);
-
- // send axis event to 2 and then 0
- device::Gamepads axis_moved;
- axis_moved.items[0].connected = true;
- axis_moved.items[0].timestamp = 1;
- axis_moved.items[2].connected = true;
- axis_moved.items[2].timestamp = 2;
-
- axis_moved.items[2].buttons_length = 1;
- axis_moved.items[2].buttons[0].pressed = true;
- axis_moved.items[2].buttons[0].value = 1.0;
-
- EXPECT_CALL(gamepad_delegate2, OnButton(0, true, 1.0)).Times(1);
- EXPECT_CALL(gamepad_delegate2, OnFrame()).Times(1);
- SetDataAndPostToDelegate(axis_moved);
-
- axis_moved.items[0].timestamp = 2;
- axis_moved.items[0].buttons_length = 1;
- axis_moved.items[0].buttons[0].pressed = true;
- axis_moved.items[0].buttons[0].value = 2.0;
-
- EXPECT_CALL(gamepad_delegate0, OnButton(0, true, 2.0)).Times(1);
- EXPECT_CALL(gamepad_delegate0, OnFrame()).Times(1);
- SetDataAndPostToDelegate(axis_moved);
-
- EXPECT_CALL(gamepad_delegate0, OnRemoved()).Times(1);
- EXPECT_CALL(gamepad_delegate2, OnRemoved()).Times(1);
-
- DestroyGamingSeat(gaming_seat_delegate);
-}
-
-TEST_F(GamingSeatTest, OnWindowFocused) {
- // Create surface and move focus to it.
- 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();
-
- testing::StrictMock<MockGamingSeatDelegate>* gaming_seat_delegate =
- new testing::StrictMock<MockGamingSeatDelegate>();
- EXPECT_CALL(*gaming_seat_delegate,
- CanAcceptGamepadEventsForSurface(testing::_))
- .WillOnce(testing::Return(true));
-
- InitializeGamingSeat(gaming_seat_delegate);
-
- // In focus. Should be polling indefinitely, check a couple of time that the
- // poll task is re-posted.
- for (size_t i = 0; i < 5; ++i) {
- polling_task_runner_->RunPendingTasks();
- ASSERT_TRUE(polling_task_runner_->HasPendingTask());
- }
-
- // Remove focus from window.
- aura::client::FocusClient* focus_client =
- aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
- focus_client->FocusWindow(nullptr);
-
- // Run EnablePolling and OnPoll task, no more polls should be scheduled.
- // In the first round of RunPendingTasks we will execute
- // EnablePollingOnPollingThread, which will cause the polling to stop being
- // scheduled in the next round.
- polling_task_runner_->RunPendingTasks();
- polling_task_runner_->RunPendingTasks();
- ASSERT_FALSE(polling_task_runner_->HasPendingTask());
-
+ UpdateGamepadDevice({0, 1});
+ SendFrameToGamepads({0, 1});
+ UpdateGamepadDevice({0, 1, 2, 3, 4});
+ SendFrameToGamepads({0, 1, 2, 3, 4});
+ UpdateGamepadDevice({1, 3, 5});
+ SendFrameToGamepads({1, 2, 3, 4, 5});
DestroyGamingSeat(gaming_seat_delegate);
}
diff --git a/chromium/components/exo/keyboard.cc b/chromium/components/exo/keyboard.cc
index fd261e1ec9d..35b827ceee9 100644
--- a/chromium/components/exo/keyboard.cc
+++ b/chromium/components/exo/keyboard.cc
@@ -4,6 +4,7 @@
#include "components/exo/keyboard.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/shell_surface.h"
@@ -20,12 +21,24 @@
namespace exo {
namespace {
+// Delay until a key state change expected to be acknowledged is expired.
+const int kExpirationDelayForPendingKeyAcksMs = 1000;
+
+bool ProcessAccelerator(Surface* surface, const ui::KeyEvent* event) {
+ views::Widget* widget =
+ views::Widget::GetTopLevelWidgetForNativeView(surface->window());
+ if (widget) {
+ views::FocusManager* focus_manager = widget->GetFocusManager();
+ return focus_manager->ProcessAccelerator(ui::Accelerator(*event));
+ }
+ return false;
+}
+
bool ConsumedByIme(Surface* focus, const ui::KeyEvent* event) {
// Check if IME consumed the event, to avoid it to be doubly processed.
// First let us see whether IME is active and is in text input mode.
views::Widget* widget =
- focus ? views::Widget::GetTopLevelWidgetForNativeView(focus->window())
- : nullptr;
+ views::Widget::GetTopLevelWidgetForNativeView(focus->window());
ui::InputMethod* ime = widget ? widget->GetInputMethod() : nullptr;
if (!ime || ime->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
return false;
@@ -75,8 +88,8 @@ bool ConsumedByIme(Surface* focus, const ui::KeyEvent* event) {
}
bool IsPhysicalKeyboardEnabled() {
- // The internal keyboard is enabled if maximize mode is not enabled.
- if (!WMHelper::GetInstance()->IsMaximizeModeWindowManagerEnabled())
+ // The internal keyboard is enabled if tablet mode is not enabled.
+ if (!WMHelper::GetInstance()->IsTabletModeWindowManagerEnabled())
return true;
for (auto& keyboard :
@@ -92,25 +105,28 @@ bool IsPhysicalKeyboardEnabled() {
////////////////////////////////////////////////////////////////////////////////
// Keyboard, public:
-Keyboard::Keyboard(KeyboardDelegate* delegate) : delegate_(delegate) {
+Keyboard::Keyboard(KeyboardDelegate* delegate)
+ : delegate_(delegate),
+ expiration_delay_for_pending_key_acks_(base::TimeDelta::FromMilliseconds(
+ kExpirationDelayForPendingKeyAcksMs)),
+ weak_ptr_factory_(this) {
auto* helper = WMHelper::GetInstance();
helper->AddPostTargetHandler(this);
helper->AddFocusObserver(this);
- helper->AddMaximizeModeObserver(this);
+ helper->AddTabletModeObserver(this);
helper->AddInputDeviceEventObserver(this);
OnWindowFocused(helper->GetFocusedWindow(), nullptr);
}
Keyboard::~Keyboard() {
- delegate_->OnKeyboardDestroying(this);
- if (device_configuration_delegate_)
- device_configuration_delegate_->OnKeyboardDestroying(this);
+ for (KeyboardObserver& observer : observer_list_)
+ observer.OnKeyboardDestroying(this);
if (focus_)
focus_->RemoveSurfaceObserver(this);
auto* helper = WMHelper::GetInstance();
helper->RemoveFocusObserver(this);
helper->RemovePostTargetHandler(this);
- helper->RemoveMaximizeModeObserver(this);
+ helper->RemoveTabletModeObserver(this);
helper->RemoveInputDeviceEventObserver(this);
}
@@ -124,10 +140,49 @@ void Keyboard::SetDeviceConfigurationDelegate(
OnKeyboardDeviceConfigurationChanged();
}
+void Keyboard::AddObserver(KeyboardObserver* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+bool Keyboard::HasObserver(KeyboardObserver* observer) const {
+ return observer_list_.HasObserver(observer);
+}
+
+void Keyboard::RemoveObserver(KeyboardObserver* observer) {
+ observer_list_.HasObserver(observer);
+}
+
+void Keyboard::SetNeedKeyboardKeyAcks(bool need_acks) {
+ are_keyboard_key_acks_needed_ = need_acks;
+}
+
+bool Keyboard::AreKeyboardKeyAcksNeeded() const {
+ return are_keyboard_key_acks_needed_;
+}
+
+void Keyboard::AckKeyboardKey(uint32_t serial, bool handled) {
+ auto it = pending_key_acks_.find(serial);
+ if (it == pending_key_acks_.end())
+ return;
+
+ if (!handled && focus_)
+ ProcessAccelerator(focus_, &it->second.first);
+ pending_key_acks_.erase(serial);
+}
+
////////////////////////////////////////////////////////////////////////////////
// ui::EventHandler overrides:
void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
+ // Pass accelerators to ShellSurfaceWidget before passing it to the delegate
+ // if ack key event is not needed.
+ if (!are_keyboard_key_acks_needed_) {
+ if (focus_ && ProcessAccelerator(focus_, event)) {
+ event->StopPropagation();
+ return;
+ }
+ }
+
// These modifiers reflect what Wayland is aware of. For example,
// EF_SCROLL_LOCK_ON is missing because Wayland doesn't support scroll lock.
const int kModifierMask = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
@@ -144,15 +199,23 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
// When IME ate a key event, we use the event only for tracking key states and
// ignore for further processing. Otherwise it is handled in two places (IME
// and client) and causes undesired behavior.
- bool consumed_by_ime = ConsumedByIme(focus_, event);
+ bool consumed_by_ime = focus_ ? ConsumedByIme(focus_, event) : false;
switch (event->type()) {
case ui::ET_KEY_PRESSED: {
auto it =
std::find(pressed_keys_.begin(), pressed_keys_.end(), event->code());
if (it == pressed_keys_.end()) {
- if (focus_ && !consumed_by_ime)
- delegate_->OnKeyboardKey(event->time_stamp(), event->code(), true);
+ if (focus_ && !consumed_by_ime) {
+ uint32_t serial = delegate_->OnKeyboardKey(event->time_stamp(),
+ event->code(), true);
+ if (are_keyboard_key_acks_needed_) {
+ pending_key_acks_.insert(
+ {serial,
+ {*event, base::TimeTicks::Now() +
+ expiration_delay_for_pending_key_acks_}});
+ }
+ }
pressed_keys_.push_back(event->code());
}
@@ -161,8 +224,16 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
auto it =
std::find(pressed_keys_.begin(), pressed_keys_.end(), event->code());
if (it != pressed_keys_.end()) {
- if (focus_ && !consumed_by_ime)
- delegate_->OnKeyboardKey(event->time_stamp(), event->code(), false);
+ if (focus_ && !consumed_by_ime) {
+ uint32_t serial = delegate_->OnKeyboardKey(event->time_stamp(),
+ event->code(), false);
+ if (are_keyboard_key_acks_needed_) {
+ pending_key_acks_.insert(
+ {serial,
+ {*event, base::TimeTicks::Now() +
+ expiration_delay_for_pending_key_acks_}});
+ }
+ }
pressed_keys_.erase(it);
}
@@ -171,6 +242,13 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
NOTREACHED();
break;
}
+
+ if (pending_key_acks_.empty())
+ return;
+ if (process_expired_pending_key_acks_pending_)
+ return;
+
+ ScheduleProcessExpiredPendingKeyAcks(expiration_delay_for_pending_key_acks_);
}
////////////////////////////////////////////////////////////////////////////////
@@ -185,6 +263,7 @@ void Keyboard::OnWindowFocused(aura::Window* gained_focus,
delegate_->OnKeyboardLeave(focus_);
focus_->RemoveSurfaceObserver(this);
focus_ = nullptr;
+ pending_key_acks_.clear();
}
if (gained_focus_surface) {
delegate_->OnKeyboardModifiers(modifier_flags_);
@@ -215,15 +294,15 @@ void Keyboard::OnKeyboardDeviceConfigurationChanged() {
}
////////////////////////////////////////////////////////////////////////////////
-// WMHelper::MaximizeModeObserver overrides:
+// WMHelper::TabletModeObserver overrides:
-void Keyboard::OnMaximizeModeStarted() {
+void Keyboard::OnTabletModeStarted() {
OnKeyboardDeviceConfigurationChanged();
}
-void Keyboard::OnMaximizeModeEnding() {}
+void Keyboard::OnTabletModeEnding() {}
-void Keyboard::OnMaximizeModeEnded() {
+void Keyboard::OnTabletModeEnded() {
OnKeyboardDeviceConfigurationChanged();
}
@@ -244,4 +323,46 @@ Surface* Keyboard::GetEffectiveFocus(aura::Window* window) const {
: nullptr;
}
+void Keyboard::ProcessExpiredPendingKeyAcks() {
+ DCHECK(process_expired_pending_key_acks_pending_);
+ process_expired_pending_key_acks_pending_ = false;
+
+ // Check pending acks and process them as if it's not handled if
+ // expiration time passed.
+ base::TimeTicks current_time = base::TimeTicks::Now();
+
+ while (!pending_key_acks_.empty()) {
+ auto it = pending_key_acks_.begin();
+ const ui::KeyEvent event = it->second.first;
+
+ if (it->second.second > current_time)
+ break;
+
+ pending_key_acks_.erase(it);
+
+ // |pending_key_acks_| may change and an iterator of it become invalid when
+ // |ProcessAccelerator| is called.
+ if (focus_)
+ ProcessAccelerator(focus_, &event);
+ }
+
+ if (pending_key_acks_.empty())
+ return;
+
+ base::TimeDelta delay_until_next_process_expired_pending_key_acks =
+ pending_key_acks_.begin()->second.second - current_time;
+ ScheduleProcessExpiredPendingKeyAcks(
+ delay_until_next_process_expired_pending_key_acks);
+}
+
+void Keyboard::ScheduleProcessExpiredPendingKeyAcks(base::TimeDelta delay) {
+ DCHECK(!process_expired_pending_key_acks_pending_);
+ process_expired_pending_key_acks_pending_ = true;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&Keyboard::ProcessExpiredPendingKeyAcks,
+ weak_ptr_factory_.GetWeakPtr()),
+ delay);
+}
+
} // namespace exo
diff --git a/chromium/components/exo/keyboard.h b/chromium/components/exo/keyboard.h
index 98c4a4a9692..8b9d95b963f 100644
--- a/chromium/components/exo/keyboard.h
+++ b/chromium/components/exo/keyboard.h
@@ -7,9 +7,12 @@
#include <vector>
+#include "base/containers/flat_map.h"
#include "base/macros.h"
+#include "components/exo/keyboard_observer.h"
#include "components/exo/surface_observer.h"
#include "components/exo/wm_helper.h"
+#include "ui/events/event.h"
#include "ui/events/event_handler.h"
namespace ui {
@@ -27,7 +30,7 @@ class Surface;
class Keyboard : public ui::EventHandler,
public WMHelper::FocusObserver,
public WMHelper::InputDeviceEventObserver,
- public WMHelper::MaximizeModeObserver,
+ public WMHelper::TabletModeObserver,
public SurfaceObserver {
public:
explicit Keyboard(KeyboardDelegate* delegate);
@@ -37,6 +40,16 @@ class Keyboard : public ui::EventHandler,
void SetDeviceConfigurationDelegate(
KeyboardDeviceConfigurationDelegate* delegate);
+ // Management of the observer list.
+ void AddObserver(KeyboardObserver* observer);
+ bool HasObserver(KeyboardObserver* observer) const;
+ void RemoveObserver(KeyboardObserver* observer);
+
+ void SetNeedKeyboardKeyAcks(bool need_acks);
+ bool AreKeyboardKeyAcksNeeded() const;
+
+ void AckKeyboardKey(uint32_t serial, bool handled);
+
// Overridden from ui::EventHandler:
void OnKeyEvent(ui::KeyEvent* event) override;
@@ -50,15 +63,22 @@ class Keyboard : public ui::EventHandler,
// Overridden from ui::InputDeviceEventObserver:
void OnKeyboardDeviceConfigurationChanged() override;
- // Overridden from WMHelper::MaximizeModeObserver:
- void OnMaximizeModeStarted() override;
- void OnMaximizeModeEnding() override;
- void OnMaximizeModeEnded() override;
+ // Overridden from WMHelper::TabletModeObserver:
+ void OnTabletModeStarted() override;
+ void OnTabletModeEnding() override;
+ void OnTabletModeEnded() override;
private:
// Returns the effective focus for |window|.
Surface* GetEffectiveFocus(aura::Window* window) const;
+ // Processes expired key state changes in |pending_key_acks_| as they have not
+ // been acknowledged.
+ void ProcessExpiredPendingKeyAcks();
+
+ // Schedule next call of ProcessExpiredPendingKeyAcks after |delay|
+ void ScheduleProcessExpiredPendingKeyAcks(base::TimeDelta delay);
+
// The delegate instance that all events except for events about device
// configuration are dispatched to.
KeyboardDelegate* const delegate_;
@@ -67,6 +87,9 @@ class Keyboard : public ui::EventHandler,
// to.
KeyboardDeviceConfigurationDelegate* device_configuration_delegate_ = nullptr;
+ // Indicates that each key event is expected to be acknowledged.
+ bool are_keyboard_key_acks_needed_ = false;
+
// The current focus surface for the keyboard.
Surface* focus_ = nullptr;
@@ -76,6 +99,20 @@ class Keyboard : public ui::EventHandler,
// Current set of modifier flags.
int modifier_flags_ = 0;
+ // Key state changes that are expected to be acknowledged.
+ using KeyStateChange = std::pair<ui::KeyEvent, base::TimeTicks>;
+ base::flat_map<uint32_t, KeyStateChange> pending_key_acks_;
+
+ // Indicates that a ProcessExpiredPendingKeyAcks call is pending.
+ bool process_expired_pending_key_acks_pending_ = false;
+
+ // Delay until a key state change expected to be acknowledged is expired.
+ const base::TimeDelta expiration_delay_for_pending_key_acks_;
+
+ base::ObserverList<KeyboardObserver> observer_list_;
+
+ base::WeakPtrFactory<Keyboard> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(Keyboard);
};
diff --git a/chromium/components/exo/keyboard_delegate.h b/chromium/components/exo/keyboard_delegate.h
index 619f56a83fc..de321b7f466 100644
--- a/chromium/components/exo/keyboard_delegate.h
+++ b/chromium/components/exo/keyboard_delegate.h
@@ -14,16 +14,11 @@ enum class DomCode;
}
namespace exo {
-class Keyboard;
class Surface;
// Handles events on keyboards in context-specific ways.
class KeyboardDelegate {
public:
- // Called at the top of the keyboard's destructor, to give observers a
- // chance to remove themselves.
- virtual void OnKeyboardDestroying(Keyboard* keyboard) = 0;
-
// This should return true if |surface| is a valid target for this keyboard.
// E.g. the surface is owned by the same client as the keyboard.
virtual bool CanAcceptKeyboardEventsForSurface(Surface* surface) const = 0;
@@ -37,10 +32,12 @@ class KeyboardDelegate {
virtual void OnKeyboardLeave(Surface* surface) = 0;
// Called when keyboard key state changed. |pressed| is true when |key|
- // was pressed and false if it was released.
- virtual void OnKeyboardKey(base::TimeTicks time_stamp,
- ui::DomCode key,
- bool pressed) = 0;
+ // was pressed and false if it was released. Should return the serial
+ // number that will be used by the client to acknowledge the change in
+ // key state.
+ virtual uint32_t OnKeyboardKey(base::TimeTicks time_stamp,
+ ui::DomCode key,
+ bool pressed) = 0;
// Called when keyboard modifier state changed.
virtual void OnKeyboardModifiers(int modifier_flags) = 0;
diff --git a/chromium/components/exo/keyboard_device_configuration_delegate.h b/chromium/components/exo/keyboard_device_configuration_delegate.h
index 263be3d6549..aa80e4c8697 100644
--- a/chromium/components/exo/keyboard_device_configuration_delegate.h
+++ b/chromium/components/exo/keyboard_device_configuration_delegate.h
@@ -6,15 +6,10 @@
#define COMPONENTS_EXO_KEYBOARD_DEVICE_CONFIGURATION_H_
namespace exo {
-class Keyboard;
// Used as an extension to the KeyboardDelegate.
class KeyboardDeviceConfigurationDelegate {
public:
- // Called at the top of the keyboard's destructor, to give observers a chance
- // to remove themselves.
- virtual void OnKeyboardDestroying(Keyboard* keyboard) = 0;
-
// Called when used keyboard type changed.
virtual void OnKeyboardTypeChanged(bool is_physical) = 0;
diff --git a/chromium/components/exo/keyboard_observer.h b/chromium/components/exo/keyboard_observer.h
new file mode 100644
index 00000000000..1264132dac2
--- /dev/null
+++ b/chromium/components/exo/keyboard_observer.h
@@ -0,0 +1,23 @@
+// 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_EXO_KEYBOARD_OBSERVER_H_
+#define COMPONENTS_EXO_KEYBOARD_OBSERVER_H_
+
+namespace exo {
+class Keyboard;
+
+// Observers to the Keyboard are notified when the Keyboard destructs.
+class KeyboardObserver {
+ public:
+ virtual ~KeyboardObserver() {}
+
+ // Called at the top of the keyboard's destructor, to give observers a change
+ // to remove themselves.
+ virtual void OnKeyboardDestroying(Keyboard* keyboard) = 0;
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_KEYBOARD_OBSERVER_H_
diff --git a/chromium/components/exo/keyboard_unittest.cc b/chromium/components/exo/keyboard_unittest.cc
index c82c7c6190c..88441881556 100644
--- a/chromium/components/exo/keyboard_unittest.cc
+++ b/chromium/components/exo/keyboard_unittest.cc
@@ -2,17 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/exo/keyboard.h"
+
#include "ash/shell.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/macros.h"
#include "components/exo/buffer.h"
-#include "components/exo/keyboard.h"
#include "components/exo/keyboard_delegate.h"
+#include "components/exo/keyboard_device_configuration_delegate.h"
+#include "components/exo/keyboard_observer.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.h"
#include "components/exo/test/exo_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/aura/client/focus_client.h"
+#include "ui/events/devices/device_data_manager.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/test/event_generator.h"
@@ -31,10 +36,35 @@ class MockKeyboardDelegate : public KeyboardDelegate {
MOCK_METHOD2(OnKeyboardEnter,
void(Surface*, const std::vector<ui::DomCode>&));
MOCK_METHOD1(OnKeyboardLeave, void(Surface*));
- MOCK_METHOD3(OnKeyboardKey, void(base::TimeTicks, ui::DomCode, bool));
+ MOCK_METHOD3(OnKeyboardKey, uint32_t(base::TimeTicks, ui::DomCode, bool));
MOCK_METHOD1(OnKeyboardModifiers, void(int));
};
+class MockKeyboardDeviceConfigurationDelegate
+ : public KeyboardDeviceConfigurationDelegate {
+ public:
+ MockKeyboardDeviceConfigurationDelegate() {}
+
+ // Overridden from KeyboardDeviceConfigurationDelegate:
+ MOCK_METHOD1(OnKeyboardDestroying, void(Keyboard*));
+ MOCK_METHOD1(OnKeyboardTypeChanged, void(bool));
+};
+
+class MockKeyboardObserver : public KeyboardObserver {
+ public:
+ MockKeyboardObserver() {}
+
+ // Overridden from KeyboardObserver:
+ MOCK_METHOD1(OnKeyboardDestroying, void(Keyboard*));
+};
+
+class TestShellSurface : public ShellSurface {
+ public:
+ explicit TestShellSurface(Surface* surface) : ShellSurface(surface) {}
+
+ MOCK_METHOD1(AcceleratorPressed, bool(const ui::Accelerator& accelerator));
+};
+
TEST_F(KeyboardTest, OnKeyboardEnter) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
@@ -72,7 +102,6 @@ TEST_F(KeyboardTest, OnKeyboardEnter) {
// Surface should maintain keyboard focus when moved to top-level window.
focus_client->FocusWindow(surface->window()->GetToplevelWindow());
- EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get()));
keyboard.reset();
}
@@ -102,7 +131,6 @@ TEST_F(KeyboardTest, OnKeyboardLeave) {
EXPECT_CALL(delegate, OnKeyboardLeave(surface.get()));
focus_client->FocusWindow(nullptr);
- EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get()));
keyboard.reset();
}
@@ -141,7 +169,6 @@ TEST_F(KeyboardTest, OnKeyboardKey) {
generator.ReleaseKey(ui::VKEY_A, 0);
generator.ReleaseKey(ui::VKEY_A, 0);
- EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get()));
keyboard.reset();
}
@@ -185,7 +212,338 @@ TEST_F(KeyboardTest, OnKeyboardModifiers) {
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
generator.ReleaseKey(ui::VKEY_B, 0);
- EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get()));
+ keyboard.reset();
+}
+
+TEST_F(KeyboardTest, OnKeyboardTypeChanged) {
+ 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();
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ ui::DeviceDataManager* device_data_manager =
+ ui::DeviceDataManager::GetInstance();
+ ASSERT_TRUE(device_data_manager != nullptr);
+ const std::vector<ui::InputDevice> keyboards =
+ device_data_manager->GetKeyboardDevices();
+
+ ash::TabletModeController* tablet_mode_controller =
+ ash::Shell::Get()->tablet_mode_controller();
+ tablet_mode_controller->EnableTabletModeWindowManager(true);
+
+ MockKeyboardDelegate delegate;
+ std::unique_ptr<Keyboard> keyboard(new Keyboard(&delegate));
+ MockKeyboardDeviceConfigurationDelegate configuration_delegate;
+
+ EXPECT_CALL(configuration_delegate, OnKeyboardTypeChanged(true));
+ keyboard->SetDeviceConfigurationDelegate(&configuration_delegate);
+ EXPECT_TRUE(keyboard->HasDeviceConfigurationDelegate());
+
+ // Removing all keyboard devices in tablet mode calls
+ // OnKeyboardTypeChanged() with false.
+ EXPECT_CALL(configuration_delegate, OnKeyboardTypeChanged(false));
+ static_cast<ui::DeviceHotplugEventObserver*>(device_data_manager)
+ ->OnKeyboardDevicesUpdated(std::vector<ui::InputDevice>({}));
+
+ // Re-adding keyboards calls OnKeyboardTypeChanged() with true;
+ EXPECT_CALL(configuration_delegate, OnKeyboardTypeChanged(true));
+ static_cast<ui::DeviceHotplugEventObserver*>(device_data_manager)
+ ->OnKeyboardDevicesUpdated(keyboards);
+
+ keyboard.reset();
+
+ tablet_mode_controller->EnableTabletModeWindowManager(false);
+}
+
+TEST_F(KeyboardTest, KeyboardObserver) {
+ 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();
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ MockKeyboardDelegate delegate;
+ auto keyboard = base::MakeUnique<Keyboard>(&delegate);
+ MockKeyboardObserver observer;
+ keyboard->AddObserver(&observer);
+
+ EXPECT_CALL(observer, OnKeyboardDestroying(keyboard.get()));
+ keyboard.reset();
+}
+
+TEST_F(KeyboardTest, NeedKeyboardKeyAcks) {
+ 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();
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ MockKeyboardDelegate delegate;
+ auto keyboard = base::MakeUnique<Keyboard>(&delegate);
+
+ EXPECT_FALSE(keyboard->AreKeyboardKeyAcksNeeded());
+ keyboard->SetNeedKeyboardKeyAcks(true);
+ EXPECT_TRUE(keyboard->AreKeyboardKeyAcksNeeded());
+ keyboard->SetNeedKeyboardKeyAcks(false);
+ EXPECT_FALSE(keyboard->AreKeyboardKeyAcksNeeded());
+
+ keyboard.reset();
+}
+
+TEST_F(KeyboardTest, AckKeyboardKey) {
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface = base::MakeUnique<TestShellSurface>(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();
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ MockKeyboardDelegate delegate;
+ std::unique_ptr<Keyboard> keyboard(new Keyboard(&delegate));
+
+ EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get()))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(delegate, OnKeyboardModifiers(0));
+ EXPECT_CALL(delegate,
+ OnKeyboardEnter(surface.get(), std::vector<ui::DomCode>()));
+ focus_client->FocusWindow(surface->window());
+
+ // If we don't set NeedKeyboardAckKeys to true, accelerators are always passed
+ // to ShellSurface.
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+ // Press KEY_W with Ctrl.
+ EXPECT_CALL(delegate, OnKeyboardModifiers(4));
+ EXPECT_CALL(*shell_surface.get(), AcceleratorPressed(ui::Accelerator(
+ ui::VKEY_W, ui::EF_CONTROL_DOWN,
+ ui::Accelerator::KeyState::PRESSED)))
+ .WillOnce(testing::Return(true));
+ generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // Release KEY_W.
+ generator.ReleaseKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // If we set NeedKeyboardAckKeys to true, only unhandled accelerators are
+ // passed to ShellSurface.
+ keyboard->SetNeedKeyboardKeyAcks(true);
+
+ // Press KEY_W with Ctrl.
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
+ .WillOnce(testing::Return(1));
+ generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // Send ack for the key press.
+ EXPECT_CALL(*shell_surface.get(), AcceleratorPressed(ui::Accelerator(
+ ui::VKEY_W, ui::EF_CONTROL_DOWN,
+ ui::Accelerator::KeyState::PRESSED)))
+ .WillOnce(testing::Return(true));
+ keyboard->AckKeyboardKey(1, false /* handled */);
+
+ // Release KEY_W.
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, false))
+ .WillOnce(testing::Return(2));
+ generator.ReleaseKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // Send ack for the key release.
+ keyboard->AckKeyboardKey(2, false /* handled */);
+
+ // Press KEY_W with Ctrl again.
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
+ .WillOnce(testing::Return(3));
+ generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // Send ack for the key press.
+ // AcceleratorPressed is not called when the accelerator is already handled.
+ keyboard->AckKeyboardKey(3, true /* handled */);
+
+ // Release the key and reset modifier_flags.
+ EXPECT_CALL(delegate, OnKeyboardModifiers(0));
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, false));
+ generator.ReleaseKey(ui::VKEY_W, 0);
+
+ keyboard.reset();
+}
+
+TEST_F(KeyboardTest, AckKeyboardKeyMoveFocus) {
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface = base::MakeUnique<TestShellSurface>(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();
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ MockKeyboardDelegate delegate;
+ std::unique_ptr<Keyboard> keyboard(new Keyboard(&delegate));
+
+ EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get()))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(delegate, OnKeyboardModifiers(0)).Times(1);
+ EXPECT_CALL(delegate,
+ OnKeyboardEnter(surface.get(), std::vector<ui::DomCode>()));
+ focus_client->FocusWindow(surface->window());
+
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+ keyboard->SetNeedKeyboardKeyAcks(true);
+
+ // Press KEY_W with Ctrl.
+ EXPECT_CALL(delegate, OnKeyboardModifiers(4)).Times(1);
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
+ .WillOnce(testing::Return(1));
+ generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // Move focus from the window
+ EXPECT_CALL(delegate, OnKeyboardLeave(surface.get()));
+ focus_client->FocusWindow(nullptr);
+
+ // Send ack for the key press. |AcceleratorPressed()| should not be called.
+ keyboard->AckKeyboardKey(1, false /* handled */);
+
+ keyboard.reset();
+}
+
+TEST_F(KeyboardTest, AckKeyboardKeyExpired) {
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface = base::MakeUnique<TestShellSurface>(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();
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ MockKeyboardDelegate delegate;
+ std::unique_ptr<Keyboard> keyboard(new Keyboard(&delegate));
+
+ EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get()))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(delegate, OnKeyboardModifiers(0));
+ EXPECT_CALL(delegate,
+ OnKeyboardEnter(surface.get(), std::vector<ui::DomCode>()));
+ focus_client->FocusWindow(surface->window());
+
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+ keyboard->SetNeedKeyboardKeyAcks(true);
+
+ // Press KEY_W with Ctrl.
+ EXPECT_CALL(delegate, OnKeyboardModifiers(4));
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
+ .WillOnce(testing::Return(1));
+ generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // Keyboard processes pending events as if it's not handled if ack isnt' sent.
+ EXPECT_CALL(*shell_surface.get(), AcceleratorPressed(ui::Accelerator(
+ ui::VKEY_W, ui::EF_CONTROL_DOWN,
+ ui::Accelerator::KeyState::PRESSED)))
+ .WillOnce(testing::Return(true));
+ // Wait until |ProcessExpiredPendingKeyAcks| is fired.
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(1000));
+ run_loop.Run();
+ RunAllPendingInMessageLoop();
+
+ // Release the key and reset modifier_flags.
+ EXPECT_CALL(delegate, OnKeyboardModifiers(0));
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, false));
+ generator.ReleaseKey(ui::VKEY_W, 0);
+
+ keyboard.reset();
+}
+
+// Test for crbug.com/753539. If action for an accelerator moves the focus to
+// another window, it causes clearing the map of pending key acks in Keyboard.
+// We can't assume that an iterator of the map is valid after processing an
+// accelerator.
+class TestShellSurfaceWithMovingFocusAccelerator : public ShellSurface {
+ public:
+ explicit TestShellSurfaceWithMovingFocusAccelerator(Surface* surface)
+ : ShellSurface(surface) {}
+
+ bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+ return true;
+ }
+};
+
+TEST_F(KeyboardTest, AckKeyboardKeyExpiredWithMovingFocusAccelerator) {
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface =
+ base::MakeUnique<TestShellSurfaceWithMovingFocusAccelerator>(
+ 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();
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ MockKeyboardDelegate delegate;
+ std::unique_ptr<Keyboard> keyboard(new Keyboard(&delegate));
+
+ EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get()))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(delegate, OnKeyboardModifiers(0));
+ EXPECT_CALL(delegate,
+ OnKeyboardEnter(surface.get(), std::vector<ui::DomCode>()));
+ focus_client->FocusWindow(surface->window());
+
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+ keyboard->SetNeedKeyboardKeyAcks(true);
+
+ // Press KEY_W with Ctrl.
+ EXPECT_CALL(delegate, OnKeyboardModifiers(4));
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
+ .WillOnce(testing::Return(1));
+ generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+ // Wait until |ProcessExpiredPendingKeyAcks| is fired.
+ // |ProcessExpiredPendingKeyAcks| will call |AcceleratorPressed| and focus
+ // will be moved from the surface.
+ EXPECT_CALL(delegate, OnKeyboardLeave(surface.get()));
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(1000));
+ run_loop.Run();
+ RunAllPendingInMessageLoop();
+
keyboard.reset();
}
diff --git a/chromium/components/exo/layer_tree_frame_sink_holder.cc b/chromium/components/exo/layer_tree_frame_sink_holder.cc
new file mode 100644
index 00000000000..a750192d97a
--- /dev/null
+++ b/chromium/components/exo/layer_tree_frame_sink_holder.cc
@@ -0,0 +1,90 @@
+// 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/exo/layer_tree_frame_sink_holder.h"
+
+#include "cc/output/layer_tree_frame_sink.h"
+#include "cc/resources/returned_resource.h"
+#include "components/exo/surface.h"
+
+namespace exo {
+
+////////////////////////////////////////////////////////////////////////////////
+// LayerTreeFrameSinkHolder, public:
+
+LayerTreeFrameSinkHolder::LayerTreeFrameSinkHolder(
+ Surface* surface,
+ std::unique_ptr<cc::LayerTreeFrameSink> frame_sink)
+ : surface_(surface),
+ frame_sink_(std::move(frame_sink)),
+ weak_factory_(this) {
+ surface_->AddSurfaceObserver(this);
+ frame_sink_->BindToClient(this);
+}
+
+LayerTreeFrameSinkHolder::~LayerTreeFrameSinkHolder() {
+ frame_sink_->DetachFromClient();
+ if (surface_)
+ surface_->RemoveSurfaceObserver(this);
+
+ // Release all resources which aren't returned from LayerTreeFrameSink.
+ for (auto& callback : release_callbacks_)
+ callback.second.Run(gpu::SyncToken(), false);
+}
+
+bool LayerTreeFrameSinkHolder::HasReleaseCallbackForResource(
+ cc::ResourceId id) {
+ return release_callbacks_.find(id) != release_callbacks_.end();
+}
+
+void LayerTreeFrameSinkHolder::SetResourceReleaseCallback(
+ cc::ResourceId id,
+ const cc::ReleaseCallback& callback) {
+ DCHECK(!callback.is_null());
+ release_callbacks_[id] = callback;
+}
+
+int LayerTreeFrameSinkHolder::AllocateResourceId() {
+ return next_resource_id_++;
+}
+
+base::WeakPtr<LayerTreeFrameSinkHolder> LayerTreeFrameSinkHolder::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// cc::LayerTreeFrameSinkClient overrides:
+
+void LayerTreeFrameSinkHolder::SetBeginFrameSource(
+ cc::BeginFrameSource* source) {
+ if (surface_)
+ surface_->SetBeginFrameSource(source);
+}
+
+void LayerTreeFrameSinkHolder::ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) {
+ for (auto& resource : resources) {
+ auto it = release_callbacks_.find(resource.id);
+ DCHECK(it != release_callbacks_.end());
+ if (it != release_callbacks_.end()) {
+ it->second.Run(resource.sync_token, resource.lost);
+ release_callbacks_.erase(it);
+ }
+ }
+}
+
+void LayerTreeFrameSinkHolder::DidReceiveCompositorFrameAck() {
+ if (surface_)
+ surface_->DidReceiveCompositorFrameAck();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SurfaceObserver overrides:
+
+void LayerTreeFrameSinkHolder::OnSurfaceDestroying(Surface* surface) {
+ surface_->RemoveSurfaceObserver(this);
+ surface_ = nullptr;
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/layer_tree_frame_sink_holder.h b/chromium/components/exo/layer_tree_frame_sink_holder.h
new file mode 100644
index 00000000000..12b0c453c22
--- /dev/null
+++ b/chromium/components/exo/layer_tree_frame_sink_holder.h
@@ -0,0 +1,77 @@
+// 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_EXO_LAYER_TREE_FRAME_SINK_HOLDER_H_
+#define COMPONENTS_EXO_LAYER_TREE_FRAME_SINK_HOLDER_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "cc/output/layer_tree_frame_sink_client.h"
+#include "cc/resources/release_callback.h"
+#include "components/exo/surface_observer.h"
+
+namespace cc {
+class LayerTreeFrameSink;
+}
+
+namespace exo {
+class Surface;
+
+// This class talks to CompositorFrameSink and keeps track of references to
+// the contents of Buffers. It's keeped alive by references from
+// release_callbacks_. It's destroyed when its owning Surface is destroyed and
+// the last outstanding release callback is called.
+class LayerTreeFrameSinkHolder : public cc::LayerTreeFrameSinkClient,
+ public SurfaceObserver {
+ public:
+ LayerTreeFrameSinkHolder(Surface* surface,
+ std::unique_ptr<cc::LayerTreeFrameSink> frame_sink);
+ ~LayerTreeFrameSinkHolder() override;
+
+ bool HasReleaseCallbackForResource(cc::ResourceId id);
+ void SetResourceReleaseCallback(cc::ResourceId id,
+ const cc::ReleaseCallback& callback);
+ int AllocateResourceId();
+ base::WeakPtr<LayerTreeFrameSinkHolder> GetWeakPtr();
+
+ cc::LayerTreeFrameSink* frame_sink() { return frame_sink_.get(); }
+
+ // Overridden from cc::LayerTreeFrameSinkClient:
+ void SetBeginFrameSource(cc::BeginFrameSource* source) override;
+ void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void SetTreeActivationCallback(const base::Closure& callback) override {}
+ void DidReceiveCompositorFrameAck() override;
+ void DidLoseLayerTreeFrameSink() override {}
+ void OnDraw(const gfx::Transform& transform,
+ const gfx::Rect& viewport,
+ bool resourceless_software_draw) override {}
+ void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override {}
+ void SetExternalTilePriorityConstraints(
+ const gfx::Rect& viewport_rect,
+ const gfx::Transform& transform) override {}
+
+ // Overridden from SurfaceObserver:
+ void OnSurfaceDestroying(Surface* surface) override;
+
+ private:
+ // A collection of callbacks used to release resources.
+ using ResourceReleaseCallbackMap = base::flat_map<int, cc::ReleaseCallback>;
+ ResourceReleaseCallbackMap release_callbacks_;
+
+ Surface* surface_;
+ std::unique_ptr<cc::LayerTreeFrameSink> frame_sink_;
+
+ // The next resource id the buffer is attached to.
+ int next_resource_id_ = 1;
+
+ base::WeakPtrFactory<LayerTreeFrameSinkHolder> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(LayerTreeFrameSinkHolder);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_LAYER_TREE_FRAME_SINK_HOLDER_H_
diff --git a/chromium/components/exo/notification_surface.cc b/chromium/components/exo/notification_surface.cc
index 5f42548ddd9..79f613b5e92 100644
--- a/chromium/components/exo/notification_surface.cc
+++ b/chromium/components/exo/notification_surface.cc
@@ -19,7 +19,8 @@ namespace {
class CustomWindowDelegate : public aura::WindowDelegate {
public:
- explicit CustomWindowDelegate(Surface* surface) : surface_(surface) {}
+ explicit CustomWindowDelegate(NotificationSurface* notification_surface)
+ : notification_surface_(notification_surface) {}
~CustomWindowDelegate() override {}
// Overridden from aura::WindowDelegate:
@@ -28,7 +29,7 @@ class CustomWindowDelegate : public aura::WindowDelegate {
void OnBoundsChanged(const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) override {}
gfx::NativeCursor GetCursor(const gfx::Point& point) override {
- return surface_->GetCursor();
+ return notification_surface_->GetCursor(point);
}
int GetNonClientComponent(const gfx::Point& point) const override {
return HTNOWHERE;
@@ -45,22 +46,24 @@ class CustomWindowDelegate : public aura::WindowDelegate {
void OnWindowDestroying(aura::Window* window) override {}
void OnWindowDestroyed(aura::Window* window) override { delete this; }
void OnWindowTargetVisibilityChanged(bool visible) override {}
- bool HasHitTestMask() const override { return surface_->HasHitTestMask(); }
+ bool HasHitTestMask() const override {
+ return notification_surface_->HasHitTestMask();
+ }
void GetHitTestMask(gfx::Path* mask) const override {
- surface_->GetHitTestMask(mask);
+ notification_surface_->GetHitTestMask(mask);
}
void OnKeyEvent(ui::KeyEvent* event) override {
// Propagates the key event upto the top-level views Widget so that we can
// trigger proper events in the views/ash level there. Event handling for
// Surfaces is done in a post event handler in keyboard.cc.
- views::Widget* widget =
- views::Widget::GetTopLevelWidgetForNativeView(surface_->window());
+ views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(
+ notification_surface_->host_window());
if (widget)
widget->OnKeyEvent(event);
}
private:
- Surface* const surface_;
+ NotificationSurface* const notification_surface_;
DISALLOW_COPY_AND_ASSIGN(CustomWindowDelegate);
};
@@ -69,62 +72,47 @@ class CustomWindowDelegate : public aura::WindowDelegate {
NotificationSurface::NotificationSurface(NotificationSurfaceManager* manager,
Surface* surface,
- const std::string& notification_id)
- : manager_(manager),
- surface_(surface),
- notification_id_(notification_id),
- window_(new aura::Window(new CustomWindowDelegate(surface))) {
- surface_->SetSurfaceDelegate(this);
- surface_->AddSurfaceObserver(this);
-
- window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
- window_->SetName("ExoNotificationSurface");
- window_->Init(ui::LAYER_NOT_DRAWN);
- window_->set_owned_by_parent(false);
-
- // TODO(xiyuan): Fix after Surface no longer has an aura::Window.
- window_->AddChild(surface_->window());
- surface_->window()->Show();
+ const std::string& notification_key)
+ : SurfaceTreeHost("ExoNotificationSurface", new CustomWindowDelegate(this)),
+ manager_(manager),
+ notification_key_(notification_key) {
+ surface->AddSurfaceObserver(this);
+ SetRootSurface(surface);
+ host_window()->Show();
}
NotificationSurface::~NotificationSurface() {
- if (surface_) {
- surface_->SetSurfaceDelegate(nullptr);
- surface_->RemoveSurfaceObserver(this);
- }
if (added_to_manager_)
manager_->RemoveSurface(this);
+ if (root_surface())
+ root_surface()->RemoveSurfaceObserver(this);
}
-gfx::Size NotificationSurface::GetSize() const {
- return surface_->content_size();
+const gfx::Size& NotificationSurface::GetContentSize() const {
+ return root_surface()->content_size();
}
void NotificationSurface::OnSurfaceCommit() {
- surface_->CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
- surface_->CommitSurfaceHierarchy();
+ SurfaceTreeHost::OnSurfaceCommit();
- gfx::Rect bounds = window_->bounds();
- if (bounds.size() != surface_->content_size()) {
- bounds.set_size(surface_->content_size());
- window_->SetBounds(bounds);
+ gfx::Rect bounds = host_window()->bounds();
+ auto& size = host_window()->bounds().size();
+ if (bounds.size() != size) {
+ bounds.set_size(size);
+ host_window()->SetBounds(bounds);
}
// Defer AddSurface until there are contents to show.
- if (!added_to_manager_ && !surface_->content_size().IsEmpty()) {
+ if (!added_to_manager_ && !size.IsEmpty()) {
added_to_manager_ = true;
manager_->AddSurface(this);
}
}
-bool NotificationSurface::IsSurfaceSynchronized() const {
- return false;
-}
-
void NotificationSurface::OnSurfaceDestroying(Surface* surface) {
- window_.reset();
+ DCHECK_EQ(surface, root_surface());
surface->RemoveSurfaceObserver(this);
- surface_ = nullptr;
+ SetRootSurface(nullptr);
}
} // namespace exo
diff --git a/chromium/components/exo/notification_surface.h b/chromium/components/exo/notification_surface.h
index 33bfa2f0f4c..a202ba630bd 100644
--- a/chromium/components/exo/notification_surface.h
+++ b/chromium/components/exo/notification_surface.h
@@ -9,44 +9,37 @@
#include <string>
#include "base/macros.h"
-#include "components/exo/surface_delegate.h"
#include "components/exo/surface_observer.h"
+#include "components/exo/surface_tree_host.h"
#include "ui/gfx/geometry/size.h"
-namespace aura {
-class Window;
-}
-
namespace exo {
class NotificationSurfaceManager;
class Surface;
// Handles notification surface role of a given surface.
-class NotificationSurface : public SurfaceDelegate, public SurfaceObserver {
+class NotificationSurface : public SurfaceTreeHost, public SurfaceObserver {
public:
NotificationSurface(NotificationSurfaceManager* manager,
Surface* surface,
- const std::string& notification_id);
+ const std::string& notification_key);
~NotificationSurface() override;
- gfx::Size GetSize() const;
+ // Get the content size of the |root_surface()|.
+ const gfx::Size& GetContentSize() const;
- aura::Window* window() { return window_.get(); }
- const std::string& notification_id() const { return notification_id_; }
+ const std::string& notification_key() const { return notification_key_; }
// Overridden from SurfaceDelegate:
void OnSurfaceCommit() override;
- bool IsSurfaceSynchronized() const override;
// Overridden from SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override;
private:
NotificationSurfaceManager* const manager_; // Not owned.
- Surface* surface_; // Not owned.
- const std::string notification_id_;
+ const std::string notification_key_;
- std::unique_ptr<aura::Window> window_;
bool added_to_manager_ = false;
DISALLOW_COPY_AND_ASSIGN(NotificationSurface);
diff --git a/chromium/components/exo/notification_surface_manager.h b/chromium/components/exo/notification_surface_manager.h
index 2649638d36b..f5427b815fc 100644
--- a/chromium/components/exo/notification_surface_manager.h
+++ b/chromium/components/exo/notification_surface_manager.h
@@ -16,7 +16,7 @@ class NotificationSurfaceManager {
// Gets the NotificationSurface associated with the given notification id.
// Returns nullptr if no NotificationSurface is associated with the id.
virtual NotificationSurface* GetSurface(
- const std::string& notification_id) const = 0;
+ const std::string& notification_key) const = 0;
// Adds a NotificationSurface to the manager.
virtual void AddSurface(NotificationSurface* surface) = 0;
diff --git a/chromium/components/exo/pointer.cc b/chromium/components/exo/pointer.cc
index fa05f35e0eb..a08283c4959 100644
--- a/chromium/components/exo/pointer.cc
+++ b/chromium/components/exo/pointer.cc
@@ -77,7 +77,8 @@ display::ManagedDisplayInfo GetCaptureDisplayInfo() {
// Pointer, public:
Pointer::Pointer(PointerDelegate* delegate)
- : delegate_(delegate),
+ : SurfaceTreeHost("ExoPointer", nullptr),
+ delegate_(delegate),
cursor_(ui::CursorType::kNull),
capture_scale_(GetCaptureDisplayInfo().device_scale_factor()),
capture_ratio_(GetCaptureDisplayInfo().GetDensityRatio()),
@@ -91,21 +92,21 @@ Pointer::Pointer(PointerDelegate* delegate)
Pointer::~Pointer() {
delegate_->OnPointerDestroying(this);
- if (surface_)
- surface_->RemoveSurfaceObserver(this);
- if (focus_) {
- focus_->RemoveSurfaceObserver(this);
- focus_->UnregisterCursorProvider(this);
+ if (focus_surface_) {
+ focus_surface_->RemoveSurfaceObserver(this);
+ focus_surface_->UnregisterCursorProvider(this);
}
auto* helper = WMHelper::GetInstance();
helper->RemoveDisplayConfigurationObserver(this);
helper->RemoveCursorObserver(this);
helper->RemovePreTargetHandler(this);
+ if (root_surface())
+ root_surface()->RemoveSurfaceObserver(this);
}
void Pointer::SetCursor(Surface* surface, const gfx::Point& hotspot) {
// Early out if the pointer doesn't have a surface in focus.
- if (!focus_)
+ if (!focus_surface_)
return;
// This is used to avoid unnecessary cursor changes.
@@ -113,7 +114,7 @@ void Pointer::SetCursor(Surface* surface, const gfx::Point& hotspot) {
// If surface is different than the current pointer surface then remove the
// current surface and add the new surface.
- if (surface != surface_) {
+ if (surface != root_surface()) {
if (surface && surface->HasSurfaceDelegate()) {
DLOG(ERROR) << "Surface has already been assigned a role";
return;
@@ -122,8 +123,10 @@ void Pointer::SetCursor(Surface* surface, const gfx::Point& hotspot) {
cursor_changed = true;
}
- if (hotspot != hotspot_)
+ if (hotspot != cursor_hotspot_) {
+ hotspot_ = hotspot;
cursor_changed = true;
+ }
// Early out if cursor did not change.
if (!cursor_changed) {
@@ -132,9 +135,10 @@ void Pointer::SetCursor(Surface* surface, const gfx::Point& hotspot) {
return;
}
- // If |surface_| is set then asynchronously capture a snapshot of cursor,
- // otherwise cancel pending capture and immediately set the cursor to "none".
- if (surface_) {
+ // If |SurfaceTreeHost::root_surface_| is set then asynchronously capture a
+ // snapshot of cursor, otherwise cancel pending capture and immediately set
+ // the cursor to "none".
+ if (root_surface()) {
CaptureCursor(hotspot);
} else {
cursor_bitmap_.reset();
@@ -148,6 +152,33 @@ gfx::NativeCursor Pointer::GetCursor() {
}
////////////////////////////////////////////////////////////////////////////////
+// SurfaceDelegate overrides:
+
+void Pointer::OnSurfaceCommit() {
+ SurfaceTreeHost::OnSurfaceCommit();
+
+ // Capture new cursor to reflect result of commit.
+ if (focus_surface_)
+ CaptureCursor(hotspot_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SurfaceObserver overrides:
+
+void Pointer::OnSurfaceDestroying(Surface* surface) {
+ if (surface == focus_surface_) {
+ focus_surface_ = nullptr;
+ surface->RemoveSurfaceObserver(this);
+ return;
+ }
+ if (surface == root_surface()) {
+ UpdatePointerSurface(nullptr);
+ return;
+ }
+ NOTREACHED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
// ui::EventHandler overrides:
void Pointer::OnMouseEvent(ui::MouseEvent* event) {
@@ -155,15 +186,15 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) {
// If target is different than the current pointer focus then we need to
// generate enter and leave events.
- if (target != focus_) {
+ if (target != focus_surface_) {
// First generate a leave event if we currently have a target in focus.
- if (focus_) {
- delegate_->OnPointerLeave(focus_);
- focus_->RemoveSurfaceObserver(this);
+ if (focus_surface_) {
+ delegate_->OnPointerLeave(focus_surface_);
+ focus_surface_->RemoveSurfaceObserver(this);
// Require SetCursor() to be called and cursor to be re-defined in
// response to each OnPointerEnter() call.
- focus_->UnregisterCursorProvider(this);
- focus_ = nullptr;
+ focus_surface_->UnregisterCursorProvider(this);
+ focus_surface_ = nullptr;
cursor_ = ui::CursorType::kNull;
cursor_capture_weak_ptr_factory_.InvalidateWeakPtrs();
}
@@ -172,14 +203,14 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) {
delegate_->OnPointerEnter(target, event->location_f(),
event->button_flags());
location_ = event->location_f();
- focus_ = target;
- focus_->AddSurfaceObserver(this);
- focus_->RegisterCursorProvider(this);
+ focus_surface_ = target;
+ focus_surface_->AddSurfaceObserver(this);
+ focus_surface_->RegisterCursorProvider(this);
}
delegate_->OnPointerFrame();
}
- if (!focus_)
+ if (!focus_surface_)
return;
if (event->IsMouseEvent() && event->type() != ui::ET_MOUSE_EXITED) {
@@ -263,13 +294,13 @@ void Pointer::OnScrollEvent(ui::ScrollEvent* event) {
////////////////////////////////////////////////////////////////////////////////
// WMHelper::CursorObserver overrides:
-void Pointer::OnCursorSetChanged(ui::CursorSetType cursor_set) {
- if (focus_)
+void Pointer::OnCursorSizeChanged(ui::CursorSize cursor_size) {
+ if (focus_surface_)
UpdateCursor();
}
void Pointer::OnCursorDisplayChanged(const display::Display& display) {
- if (focus_)
+ if (focus_surface_)
UpdateCursor();
}
@@ -277,42 +308,13 @@ void Pointer::OnCursorDisplayChanged(const display::Display& display) {
// WMHelper::DisplayConfigurationObserver overrides:
void Pointer::OnDisplayConfigurationChanged() {
- UpdatePointerSurface(surface_);
+ UpdatePointerSurface(root_surface());
auto info = GetCaptureDisplayInfo();
capture_scale_ = info.device_scale_factor();
capture_ratio_ = info.GetDensityRatio();
}
////////////////////////////////////////////////////////////////////////////////
-// SurfaceDelegate overrides:
-
-void Pointer::OnSurfaceCommit() {
- surface_->CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
- surface_->CommitSurfaceHierarchy();
-
- // Capture new cursor to reflect result of commit.
- if (focus_)
- CaptureCursor(hotspot_);
-}
-
-bool Pointer::IsSurfaceSynchronized() const {
- // A pointer surface is always desynchronized.
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// SurfaceObserver overrides:
-
-void Pointer::OnSurfaceDestroying(Surface* surface) {
- DCHECK(surface == surface_ || surface == focus_);
- if (surface == surface_)
- surface_ = nullptr;
- if (surface == focus_)
- focus_ = nullptr;
- surface->RemoveSurfaceObserver(this);
-}
-
-////////////////////////////////////////////////////////////////////////////////
// Pointer, private:
Surface* Pointer::GetEffectiveTargetForEvent(ui::Event* event) const {
@@ -325,29 +327,29 @@ Surface* Pointer::GetEffectiveTargetForEvent(ui::Event* event) const {
}
void Pointer::UpdatePointerSurface(Surface* surface) {
- if (surface_) {
- surface_->window()->SetTransform(gfx::Transform());
- if (surface_->window()->parent())
- surface_->window()->parent()->RemoveChild(surface_->window());
- surface_->SetSurfaceDelegate(nullptr);
- surface_->RemoveSurfaceObserver(this);
+ if (root_surface()) {
+ host_window()->SetTransform(gfx::Transform());
+ if (host_window()->parent())
+ host_window()->parent()->RemoveChild(host_window());
+ root_surface()->RemoveSurfaceObserver(this);
+ SetRootSurface(nullptr);
}
- surface_ = surface;
- if (surface_) {
- surface_->SetSurfaceDelegate(this);
- surface_->AddSurfaceObserver(this);
+
+ if (surface) {
+ surface->AddSurfaceObserver(this);
// Note: Surface window needs to be added to the tree so we can take a
// snapshot. Where in the tree is not important but we might as well use
// the cursor container.
WMHelper::GetInstance()
->GetPrimaryDisplayContainer(ash::kShellWindowId_MouseCursorContainer)
- ->AddChild(surface_->window());
+ ->AddChild(host_window());
+ SetRootSurface(surface);
}
}
void Pointer::CaptureCursor(const gfx::Point& hotspot) {
- DCHECK(surface_);
- DCHECK(focus_);
+ DCHECK(root_surface());
+ DCHECK(focus_surface_);
// Surface size is in DIPs, while layer size is in pseudo-DIP units that
// depend on the DSF of the display mode. Scale the layer to capture the
@@ -357,20 +359,20 @@ void Pointer::CaptureCursor(const gfx::Point& hotspot) {
auto* helper = WMHelper::GetInstance();
float scale = helper->GetDisplayInfo(display.id()).GetEffectiveUIScale() *
capture_scale_ / display.device_scale_factor();
- surface_->window()->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale));
+ host_window()->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale));
std::unique_ptr<cc::CopyOutputRequest> request =
- cc::CopyOutputRequest::CreateBitmapRequest(
- base::Bind(&Pointer::OnCursorCaptured,
- cursor_capture_weak_ptr_factory_.GetWeakPtr(), hotspot));
+ cc::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+ &Pointer::OnCursorCaptured,
+ cursor_capture_weak_ptr_factory_.GetWeakPtr(), hotspot));
request->set_source(cursor_capture_source_id_);
- surface_->window()->layer()->RequestCopyOfOutput(std::move(request));
+ host_window()->layer()->RequestCopyOfOutput(std::move(request));
}
void Pointer::OnCursorCaptured(const gfx::Point& hotspot,
std::unique_ptr<cc::CopyOutputResult> result) {
- if (!focus_)
+ if (!focus_surface_)
return;
if (result->IsEmpty()) {
@@ -378,27 +380,28 @@ void Pointer::OnCursorCaptured(const gfx::Point& hotspot,
} else {
DCHECK(result->HasBitmap());
cursor_bitmap_ = *result->TakeBitmap();
- hotspot_ = hotspot;
+ cursor_hotspot_ = hotspot;
}
UpdateCursor();
}
void Pointer::UpdateCursor() {
- DCHECK(focus_);
+ DCHECK(focus_surface_);
if (cursor_bitmap_.drawsNothing()) {
cursor_ = ui::CursorType::kNone;
} else {
SkBitmap bitmap = cursor_bitmap_;
- gfx::Point hotspot = gfx::ScaleToFlooredPoint(hotspot_, capture_ratio_);
+ gfx::Point hotspot =
+ gfx::ScaleToFlooredPoint(cursor_hotspot_, capture_ratio_);
auto* helper = WMHelper::GetInstance();
const display::Display& display = helper->GetCursorDisplay();
float scale =
helper->GetDisplayInfo(display.id()).GetDensityRatio() / capture_ratio_;
- if (helper->GetCursorSet() == ui::CURSOR_SET_LARGE)
+ if (helper->GetCursorSize() == ui::CursorSize::kLarge)
scale *= kLargeCursorScale;
ui::ScaleAndRotateCursorBitmapAndHotpoint(scale, display.rotation(),
@@ -423,7 +426,7 @@ void Pointer::UpdateCursor() {
#endif
}
- aura::Window* root_window = focus_->window()->GetRootWindow();
+ aura::Window* root_window = focus_surface_->window()->GetRootWindow();
if (!root_window)
return;
diff --git a/chromium/components/exo/pointer.h b/chromium/components/exo/pointer.h
index b39988381f8..109bb592231 100644
--- a/chromium/components/exo/pointer.h
+++ b/chromium/components/exo/pointer.h
@@ -10,8 +10,8 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/unguessable_token.h"
-#include "components/exo/surface_delegate.h"
#include "components/exo/surface_observer.h"
+#include "components/exo/surface_tree_host.h"
#include "components/exo/wm_helper.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/cursor/cursor.h"
@@ -33,14 +33,15 @@ class MouseEvent;
namespace exo {
class PointerDelegate;
class Surface;
+class SurfaceTreeHost;
// This class implements a client pointer that represents one or more input
// devices, such as mice, which control the pointer location and pointer focus.
-class Pointer : public ui::EventHandler,
+class Pointer : public SurfaceTreeHost,
+ public SurfaceObserver,
+ public ui::EventHandler,
public WMHelper::CursorObserver,
- public WMHelper::DisplayConfigurationObserver,
- public SurfaceDelegate,
- public SurfaceObserver {
+ public WMHelper::DisplayConfigurationObserver {
public:
explicit Pointer(PointerDelegate* delegate);
~Pointer() override;
@@ -55,32 +56,33 @@ class Pointer : public ui::EventHandler,
// Returns the current cursor for the pointer.
gfx::NativeCursor GetCursor();
+ // Overridden from SurfaceDelegate:
+ void OnSurfaceCommit() override;
+
+ // Overriden from SurfaceObserver:
+ void OnSurfaceDestroying(Surface* surface) override;
+
// Overridden from ui::EventHandler:
void OnMouseEvent(ui::MouseEvent* event) override;
void OnScrollEvent(ui::ScrollEvent* event) override;
// Overridden from WMHelper::CursorObserver:
- void OnCursorSetChanged(ui::CursorSetType cursor_set) override;
+ void OnCursorSizeChanged(ui::CursorSize cursor_size) override;
void OnCursorDisplayChanged(const display::Display& display) override;
// Overridden from WMHelper::DisplayConfigurationObserver:
void OnDisplayConfigurationChanged() override;
- // Overridden from SurfaceDelegate:
- void OnSurfaceCommit() override;
- bool IsSurfaceSynchronized() const override;
-
- // Overridden from SurfaceObserver:
- void OnSurfaceDestroying(Surface* surface) override;
-
private:
// Returns the effective target for |event|.
Surface* GetEffectiveTargetForEvent(ui::Event* event) const;
- // Updates the |surface_| from which the cursor is captured.
+ // Updates the root_surface in |SurfaceTreeHost| from which the cursor
+ // is captured.
void UpdatePointerSurface(Surface* surface);
- // Asynchronously update the cursor by capturing a snapshot of |surface_|.
+ // Asynchronously update the cursor by capturing a snapshot of
+ // |SurfaceTreeHost::root_surface()|.
void CaptureCursor(const gfx::Point& hotspot);
// Called when cursor snapshot has been captured.
@@ -93,11 +95,8 @@ class Pointer : public ui::EventHandler,
// The delegate instance that all events are dispatched to.
PointerDelegate* const delegate_;
- // The current pointer surface.
- Surface* surface_ = nullptr;
-
// The current focus surface for the pointer.
- Surface* focus_ = nullptr;
+ Surface* focus_surface_ = nullptr;
// The location of the pointer in the current focus surface.
gfx::PointF location_;
@@ -111,6 +110,9 @@ class Pointer : public ui::EventHandler,
// The current cursor.
ui::Cursor cursor_;
+ // Hotspot to use with latest cursor snapshot.
+ gfx::Point cursor_hotspot_;
+
// Scale at which cursor snapshot is captured.
float capture_scale_;
diff --git a/chromium/components/exo/pointer_unittest.cc b/chromium/components/exo/pointer_unittest.cc
index 6df66bcce6c..1831ec5c2ab 100644
--- a/chromium/components/exo/pointer_unittest.cc
+++ b/chromium/components/exo/pointer_unittest.cc
@@ -446,5 +446,34 @@ TEST_F(PointerTest, IgnorePointerEventDuringModal) {
pointer.reset();
}
+TEST_F(PointerTest, OnPointerInStylusOnlyWindow) {
+ 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->SetStylusOnly();
+ 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));
+
+ EXPECT_CALL(delegate, OnPointerFrame()).Times(0);
+ EXPECT_CALL(delegate, OnPointerEnter(surface.get(), testing::_, 0)).Times(0);
+ EXPECT_CALL(delegate, OnPointerButton(testing::_, testing::_, testing::_))
+ .Times(0);
+
+ generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin());
+ generator.ClickLeftButton();
+
+ EXPECT_CALL(delegate, OnPointerDestroying(pointer.get()));
+ pointer.reset();
+}
+
} // namespace
} // namespace exo
diff --git a/chromium/components/exo/shell_surface.cc b/chromium/components/exo/shell_surface.cc
index 4259d841c31..6b48c787ef2 100644
--- a/chromium/components/exo/shell_surface.cc
+++ b/chromium/components/exo/shell_surface.cc
@@ -30,6 +30,7 @@
#include "ui/aura/window_tree_host.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/class_property.h"
+#include "ui/compositor/compositor.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/path.h"
@@ -44,15 +45,35 @@
namespace exo {
namespace {
-// This is a struct for accelerator keys used to close ShellSurfaces.
-const struct Accelerator {
+DEFINE_LOCAL_UI_CLASS_PROPERTY_KEY(Surface*, kMainSurfaceKey, nullptr)
+
+// Application Id set by the client.
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kApplicationIdKey, nullptr);
+
+// Maximum amount of time to wait until the window is resized
+// to match the display's orientation in tablet mode.
+// TODO(oshima): Looks like android is generating unnecessary frames.
+// Fix it on Android side and reduce the timeout.
+constexpr int kRotationLockTimeoutMs = 2500;
+
+// This is a struct for accelerator keys.
+struct Accelerator {
ui::KeyboardCode keycode;
int modifiers;
-} kCloseWindowAccelerators[] = {
+};
+
+// The accelerator keys used to close ShellSurfaces.
+const Accelerator kCloseWindowAccelerators[] = {
{ui::VKEY_W, ui::EF_CONTROL_DOWN},
{ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN},
{ui::VKEY_F4, ui::EF_ALT_DOWN}};
+// The accelerator keys reserved to be processed by the focus manager.
+const Accelerator kReservedAccelerators[] = {
+ {ui::VKEY_SPACE, ui::EF_CONTROL_DOWN},
+ {ui::VKEY_SPACE, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN},
+ {ui::VKEY_F13, ui::EF_NONE}};
+
class CustomFrameView : public views::NonClientFrameView {
public:
explicit CustomFrameView(views::Widget* widget) : widget_(widget) {}
@@ -144,7 +165,10 @@ class CustomWindowTargeter : public aura::WindowTargeter {
return shadow_underlay;
}
}
- return aura::WindowTargeter::FindTargetForEvent(root, event);
+ ui::EventTarget* target =
+ aura::WindowTargeter::FindTargetForEvent(root, event);
+ // Do not accept events in ShellSurface window.
+ return target != root ? target : nullptr;
}
private:
@@ -179,10 +203,16 @@ class ShellSurfaceWidget : public views::Widget {
void OnKeyEvent(ui::KeyEvent* event) override {
// TODO(hidehiko): Handle ESC + SHIFT + COMMAND accelerator key
// to escape pinned mode.
- // Handle only accelerators. Do not call Widget::OnKeyEvent that eats focus
- // management keys (like the tab key) as well.
- if (GetFocusManager()->ProcessAccelerator(ui::Accelerator(*event)))
- event->StopPropagation();
+ for (const auto& entry : kReservedAccelerators) {
+ // Handle only reserved accelerators.
+ if (event->flags() == entry.modifiers &&
+ event->key_code() == entry.keycode) {
+ if (GetFocusManager()->ProcessAccelerator(ui::Accelerator(*event)))
+ event->StopPropagation();
+ }
+ }
+ // Do not call Widget::OnKeyEvent that eats focus management keys (like the
+ // tab key) as well.
}
private:
@@ -191,6 +221,12 @@ class ShellSurfaceWidget : public views::Widget {
DISALLOW_COPY_AND_ASSIGN(ShellSurfaceWidget);
};
+Orientation SizeToOrientation(const gfx::Size& size) {
+ DCHECK_NE(size.width(), size.height());
+ return size.width() > size.height() ? Orientation::LANDSCAPE
+ : Orientation::PORTRAIT;
+}
+
} // namespace
// Helper class used to coalesce a number of changes into one "configure"
@@ -277,8 +313,6 @@ ShellSurface::ScopedAnimationsDisabled::~ScopedAnimationsDisabled() {
////////////////////////////////////////////////////////////////////////////////
// ShellSurface, public:
-DEFINE_LOCAL_UI_CLASS_PROPERTY_KEY(Surface*, kMainSurfaceKey, nullptr)
-
ShellSurface::ShellSurface(Surface* surface,
ShellSurface* parent,
BoundsMode bounds_mode,
@@ -286,8 +320,8 @@ ShellSurface::ShellSurface(Surface* surface,
bool activatable,
bool can_minimize,
int container)
- : widget_(nullptr),
- surface_(surface),
+ : SurfaceTreeHost("ExoShellSurfaceHost", nullptr),
+ widget_(nullptr),
parent_(parent ? parent->GetWidget()->GetNativeWindow() : nullptr),
bounds_mode_(bounds_mode),
primary_display_id_(
@@ -298,9 +332,10 @@ ShellSurface::ShellSurface(Surface* surface,
container_(container) {
WMHelper::GetInstance()->AddActivationObserver(this);
WMHelper::GetInstance()->AddDisplayConfigurationObserver(this);
- surface_->SetSurfaceDelegate(this);
- surface_->AddSurfaceObserver(this);
- surface_->window()->Show();
+ display::Screen::GetScreen()->AddObserver(this);
+ surface->AddSurfaceObserver(this);
+ SetRootSurface(surface);
+ host_window()->Show();
set_owned_by_client();
if (parent_)
parent_->AddObserver(this);
@@ -331,14 +366,11 @@ ShellSurface::~ShellSurface() {
}
WMHelper::GetInstance()->RemoveActivationObserver(this);
WMHelper::GetInstance()->RemoveDisplayConfigurationObserver(this);
+ display::Screen::GetScreen()->RemoveObserver(this);
if (parent_)
parent_->RemoveObserver(this);
- if (surface_) {
- if (scale_ != 1.0)
- surface_->window()->SetTransform(gfx::Transform());
- surface_->SetSurfaceDelegate(nullptr);
- surface_->RemoveSurfaceObserver(this);
- }
+ if (root_surface())
+ root_surface()->RemoveSurfaceObserver(this);
}
void ShellSurface::AcknowledgeConfigure(uint32_t serial) {
@@ -463,6 +495,10 @@ void ShellSurface::SetPinned(ash::mojom::WindowPinType type) {
void ShellSurface::SetSystemUiVisibility(bool autohide) {
TRACE_EVENT1("exo", "ShellSurface::SetSystemUiVisibility", "autohide",
autohide);
+
+ if (!widget_)
+ CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
+
ash::wm::SetAutoHideShelf(widget_->GetNativeWindow(), autohide);
}
@@ -486,6 +522,14 @@ void ShellSurface::SetTitle(const base::string16& title) {
widget_->UpdateWindowTitle();
}
+void ShellSurface::SetIcon(const gfx::ImageSkia& icon) {
+ TRACE_EVENT0("exo", "ShellSurface::SetIcon");
+
+ icon_ = icon;
+ if (widget_)
+ widget_->UpdateWindowIcon();
+}
+
void ShellSurface::SetSystemModal(bool system_modal) {
// System modal container is used by clients to implement client side
// managed system modal dialogs using a single ShellSurface instance.
@@ -520,14 +564,12 @@ void ShellSurface::UpdateSystemModal() {
void ShellSurface::SetApplicationId(aura::Window* window,
const std::string& id) {
TRACE_EVENT1("exo", "ShellSurface::SetApplicationId", "application_id", id);
- const ash::ShelfID shelf_id(id);
- window->SetProperty(ash::kShelfIDKey, new std::string(shelf_id.Serialize()));
+ window->SetProperty(kApplicationIdKey, new std::string(id));
}
// static
-const std::string ShellSurface::GetApplicationId(aura::Window* window) {
- return ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey))
- .app_id;
+const std::string* ShellSurface::GetApplicationId(aura::Window* window) {
+ return window->GetProperty(kApplicationIdKey);
}
void ShellSurface::SetApplicationId(const std::string& application_id) {
@@ -590,6 +632,12 @@ void ShellSurface::SetGeometry(const gfx::Rect& geometry) {
pending_geometry_ = geometry;
}
+void ShellSurface::SetOrientation(Orientation orientation) {
+ TRACE_EVENT1("exo", "ShellSurface::SetOrientation", "orientation",
+ orientation == Orientation::PORTRAIT ? "portrait" : "landscape");
+ pending_orientation_ = orientation;
+}
+
void ShellSurface::SetRectangularShadowEnabled(bool enabled) {
TRACE_EVENT1("exo", "ShellSurface::SetRectangularShadowEnabled", "enabled",
enabled);
@@ -678,10 +726,13 @@ std::unique_ptr<base::trace_event::TracedValue> ShellSurface::AsTracedValue()
std::unique_ptr<base::trace_event::TracedValue> value(
new base::trace_event::TracedValue());
value->SetString("title", base::UTF16ToUTF8(title_));
- std::string application_id;
- if (GetWidget() && GetWidget()->GetNativeWindow())
- application_id = GetApplicationId(GetWidget()->GetNativeWindow());
- value->SetString("application_id", application_id);
+ if (GetWidget() && GetWidget()->GetNativeWindow()) {
+ const std::string* application_id =
+ GetApplicationId(GetWidget()->GetNativeWindow());
+
+ if (application_id)
+ value->SetString("application_id", *application_id);
+ }
return value;
}
@@ -689,12 +740,11 @@ std::unique_ptr<base::trace_event::TracedValue> ShellSurface::AsTracedValue()
// SurfaceDelegate overrides:
void ShellSurface::OnSurfaceCommit() {
- surface_->CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
- surface_->CommitSurfaceHierarchy();
+ SurfaceTreeHost::OnSurfaceCommit();
if (enabled() && !widget_) {
// Defer widget creation until surface contains some contents.
- if (surface_->content_size().IsEmpty()) {
+ if (host_window()->bounds().size().IsEmpty()) {
Configure();
return;
}
@@ -729,7 +779,7 @@ void ShellSurface::OnSurfaceCommit() {
// region is empty, then it is non interactive window and won't be
// activated.
if (container_ == ash::kShellWindowId_SystemModalContainer) {
- gfx::Rect hit_test_bounds = surface_->GetHitTestBounds();
+ gfx::Rect hit_test_bounds = GetHitTestBounds();
// Prevent window from being activated when hit test bounds are empty.
bool activatable = activatable_ && !hit_test_bounds.IsEmpty();
@@ -750,7 +800,7 @@ void ShellSurface::OnSurfaceCommit() {
gfx::Transform transform;
DCHECK_NE(pending_scale_, 0.0);
transform.Scale(1.0 / pending_scale_, 1.0 / pending_scale_);
- surface_->window()->SetTransform(transform);
+ host_window()->SetTransform(transform);
scale_ = pending_scale_;
}
@@ -763,24 +813,26 @@ void ShellSurface::OnSurfaceCommit() {
if (container_ == ash::kShellWindowId_SystemModalContainer)
UpdateSystemModal();
}
+ orientation_ = pending_orientation_;
+ if (expected_orientation_ == orientation_)
+ compositor_lock_.reset();
+ } else {
+ compositor_lock_.reset();
}
}
-bool ShellSurface::IsSurfaceSynchronized() const {
- // A shell surface is always desynchronized.
- return false;
-}
-
////////////////////////////////////////////////////////////////////////////////
// SurfaceObserver overrides:
void ShellSurface::OnSurfaceDestroying(Surface* surface) {
+ DCHECK_EQ(root_surface(), surface);
+ surface->RemoveSurfaceObserver(this);
+ SetRootSurface(nullptr);
+
if (resizer_)
EndDrag(false /* revert */);
if (widget_)
SetMainSurface(widget_->GetNativeWindow(), nullptr);
- surface->RemoveSurfaceObserver(this);
- surface_ = nullptr;
// Hide widget before surface is destroyed. This allows hide animations to
// run using the current surface contents.
@@ -822,6 +874,10 @@ base::string16 ShellSurface::GetWindowTitle() const {
return title_;
}
+gfx::ImageSkia ShellSurface::GetWindowIcon() {
+ return icon_;
+}
+
void ShellSurface::SaveWindowPlacement(const gfx::Rect& bounds,
ui::WindowShowState show_state) {
if (bounds_mode_ != BoundsMode::CLIENT)
@@ -868,14 +924,19 @@ views::NonClientFrameView* ShellSurface::CreateNonClientFrameView(
}
bool ShellSurface::WidgetHasHitTestMask() const {
- return surface_ ? surface_->HasHitTestMask() : false;
+ return HasHitTestMask();
}
void ShellSurface::GetWidgetHitTestMask(gfx::Path* mask) const {
DCHECK(WidgetHasHitTestMask());
- surface_->GetHitTestMask(mask);
- gfx::Point origin = surface_->window()->bounds().origin();
- mask->offset(SkIntToScalar(origin.x()), SkIntToScalar(origin.y()));
+ GetHitTestMask(mask);
+
+ gfx::Point origin = host_window()->bounds().origin();
+ SkMatrix matrix;
+ matrix.setScaleTranslate(
+ SkFloatToScalar(1.0f / scale_), SkFloatToScalar(1.0f / scale_),
+ SkIntToScalar(origin.x()), SkIntToScalar(origin.y()));
+ mask->transform(matrix);
}
////////////////////////////////////////////////////////////////////////////////
@@ -885,7 +946,7 @@ gfx::Size ShellSurface::CalculatePreferredSize() const {
if (!geometry_.IsEmpty())
return geometry_.size();
- return surface_ ? surface_->window()->layer()->size() : gfx::Size();
+ return host_window()->bounds().size();
}
gfx::Size ShellSurface::GetMinimumSize() const {
@@ -930,6 +991,7 @@ void ShellSurface::OnPostWindowStateTypeChange(
if (widget_) {
UpdateWidgetBounds();
UpdateShadow();
+ UpdateBackdrop();
}
if (old_type != new_type && !state_changed_callback_.is_null())
@@ -950,7 +1012,7 @@ void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
if (bounds_mode_ == BoundsMode::CLIENT)
return;
- if (!widget_ || !surface_ || ignore_window_bounds_changes_)
+ if (!widget_ || !root_surface() || ignore_window_bounds_changes_)
return;
if (window == widget_->GetNativeWindow()) {
@@ -968,7 +1030,7 @@ void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
// The shadow size may be updated to match the widget. Change it back
// to the shadow content size.
- // TODO(oshima): When the arc window reiszing is enabled, we may want to
+ // TODO(oshima): When the window reiszing is enabled, we may want to
// implement shadow management here instead of using shadow controller.
UpdateShadow();
@@ -1054,6 +1116,38 @@ void ShellSurface::OnKeyEvent(ui::KeyEvent* event) {
}
}
+////////////////////////////////////////////////////////////////////////////////
+// display::DisplayObserver overrides:
+
+void ShellSurface::OnDisplayMetricsChanged(const display::Display& new_display,
+ uint32_t changed_metrics) {
+ if (!widget_ || !widget_->IsActive() || bounds_mode_ != BoundsMode::CLIENT ||
+ !WMHelper::GetInstance()->IsTabletModeWindowManagerEnabled()) {
+ return;
+ }
+
+ const display::Screen* screen = display::Screen::GetScreen();
+ display::Display current_display =
+ screen->GetDisplayNearestWindow(widget_->GetNativeWindow());
+ if (current_display.id() != new_display.id() ||
+ !(changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)) {
+ return;
+ }
+
+ Orientation target_orientation = SizeToOrientation(new_display.size());
+ if (orientation_ == target_orientation)
+ return;
+ expected_orientation_ = target_orientation;
+ EnsureCompositorIsLocked();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ui::CompositorLockClient overrides:
+
+void ShellSurface::CompositorLockTimedOut() {
+ compositor_lock_.reset();
+}
+
void ShellSurface::OnMouseEvent(ui::MouseEvent* event) {
if (!resizer_) {
views::View::OnMouseEvent(event);
@@ -1188,7 +1282,7 @@ void ShellSurface::CreateShellSurfaceWidget(ui::WindowShowState show_state) {
// 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 &= !surface_->GetHitTestBounds().IsEmpty();
+ activatable &= !GetHitTestBounds().IsEmpty();
params.activatable = activatable ? views::Widget::InitParams::ACTIVATABLE_YES
: views::Widget::InitParams::ACTIVATABLE_NO;
@@ -1200,10 +1294,10 @@ void ShellSurface::CreateShellSurfaceWidget(ui::WindowShowState show_state) {
window->SetName("ExoShellSurface");
window->SetProperty(aura::client::kAccessibilityFocusFallsbackToWidgetKey,
false);
- window->AddChild(surface_->window());
+ window->AddChild(host_window());
window->SetEventTargeter(base::WrapUnique(new CustomWindowTargeter(widget_)));
SetApplicationId(window, application_id_);
- SetMainSurface(window, surface_);
+ SetMainSurface(window, root_surface());
// Start tracking changes to window bounds and window state.
window->AddObserver(this);
@@ -1296,13 +1390,13 @@ void ShellSurface::Configure() {
<< pending_configs_.size();
}
-aura::Window* ShellSurface::GetDragWindow() const {
+aura::Window* ShellSurface::GetDragWindow() {
switch (bounds_mode_) {
case BoundsMode::SHELL:
return widget_->GetNativeWindow();
case BoundsMode::CLIENT:
- return surface_ ? surface_->window() : nullptr;
+ return root_surface() ? root_surface()->window() : nullptr;
case BoundsMode::FIXED:
return nullptr;
@@ -1365,7 +1459,7 @@ void ShellSurface::AttemptToStartDrag(int component) {
}
resizer_ = ash::CreateWindowResizer(window, GetMouseLocation(), component,
- aura::client::WINDOW_MOVE_SOURCE_MOUSE);
+ wm::WINDOW_MOVE_SOURCE_MOUSE);
if (!resizer_)
return;
@@ -1383,7 +1477,7 @@ void ShellSurface::AttemptToStartDrag(int component) {
DCHECK(!window_state->drag_details());
DCHECK(component == HTCAPTION);
window_state->CreateDragDetails(GetMouseLocation(), component,
- aura::client::WINDOW_MOVE_SOURCE_MOUSE);
+ wm::WINDOW_MOVE_SOURCE_MOUSE);
// Chained with a CustomWindowResizer, DragWindowResizer does not handle
// dragging. It only renders phantom windows and moves the window to the
@@ -1438,7 +1532,7 @@ bool ShellSurface::IsResizing() const {
gfx::Rect ShellSurface::GetVisibleBounds() const {
// Use |geometry_| if set, otherwise use the visual bounds of the surface.
- return geometry_.IsEmpty() ? gfx::Rect(surface_->window()->layer()->size())
+ return geometry_.IsEmpty() ? gfx::Rect(host_window()->bounds().size())
: geometry_;
}
@@ -1562,13 +1656,13 @@ void ShellSurface::UpdateSurfaceBounds() {
gfx::Rect client_view_bounds =
widget_->non_client_view()->frame_view()->GetBoundsForClientView();
- surface_->window()->SetBounds(
+ host_window()->SetBounds(
gfx::Rect(GetSurfaceOrigin() + client_view_bounds.OffsetFromOrigin(),
- surface_->window()->layer()->size()));
+ host_window()->bounds().size()));
}
void ShellSurface::UpdateShadow() {
- if (!widget_ || !surface_)
+ if (!widget_ || !root_surface())
return;
if (shadow_underlay_in_surface_ != pending_shadow_underlay_in_surface_) {
shadow_underlay_in_surface_ = pending_shadow_underlay_in_surface_;
@@ -1576,17 +1670,9 @@ void ShellSurface::UpdateShadow() {
shadow_underlay_.reset();
}
- aura::Window* window = widget_->GetNativeWindow();
+ UpdateBackdrop();
- // Enable the black backdrop layer behind the window if the window
- // is in immersive fullscreen, maximized, yet the window can control
- // the bounds of the window in fullscreen/maximize mode (thus the
- // background can be visible).
- bool enable_backdrop =
- (widget_->IsFullscreen() || widget_->IsMaximized()) &&
- ash::wm::GetWindowState(window)->allow_set_bounds_direct();
- if (window->GetProperty(aura::client::kHasBackdrop) != enable_backdrop)
- window->SetProperty(aura::client::kHasBackdrop, enable_backdrop);
+ aura::Window* window = widget_->GetNativeWindow();
if (!shadow_enabled_) {
wm::SetShadowElevation(window, wm::ShadowElevation::NONE);
@@ -1607,7 +1693,7 @@ void ShellSurface::UpdateShadow() {
gfx::Rect shadow_underlay_bounds = shadow_content_bounds_;
if (shadow_underlay_bounds.IsEmpty()) {
- shadow_underlay_bounds = gfx::Rect(surface_->window()->bounds().size());
+ shadow_underlay_bounds = gfx::Rect(host_window()->bounds().size());
} else if (shadow_underlay_in_surface_) {
// Since the shadow underlay is positioned relative to the surface, its
// origin corresponds to the shadow content position relative to the
@@ -1646,8 +1732,8 @@ void ShellSurface::UpdateShadow() {
shadow_underlay_->layer()->SetColor(SK_ColorBLACK);
DCHECK(shadow_underlay_->layer()->fills_bounds_opaquely());
if (shadow_underlay_in_surface_) {
- surface_->window()->AddChild(shadow_underlay());
- surface_->window()->StackChildAtBottom(shadow_underlay());
+ host_window()->AddChild(shadow_underlay());
+ host_window()->StackChildAtBottom(shadow_underlay());
} else {
window->AddChild(shadow_underlay());
window->StackChildAtBottom(shadow_underlay());
@@ -1693,7 +1779,7 @@ void ShellSurface::UpdateShadow() {
window->AddChild(shadow_overlay());
if (shadow_underlay_in_surface_) {
- window->StackChildBelow(shadow_overlay(), surface_->window());
+ window->StackChildBelow(shadow_overlay(), host_window());
} else {
window->StackChildAbove(shadow_overlay(), shadow_underlay());
}
@@ -1711,6 +1797,19 @@ void ShellSurface::UpdateShadow() {
}
}
+void ShellSurface::UpdateBackdrop() {
+ aura::Window* window = widget_->GetNativeWindow();
+ // Enable the black backdrop layer behind the window if the window
+ // is in immersive fullscreen, maximized, yet the window can control
+ // the bounds of the window in fullscreen/tablet mode (thus the
+ // background can be visible).
+ bool enable_backdrop =
+ (widget_->IsFullscreen() || widget_->IsMaximized()) &&
+ ash::wm::GetWindowState(window)->allow_set_bounds_direct();
+ if (window->GetProperty(aura::client::kHasBackdrop) != enable_backdrop)
+ window->SetProperty(aura::client::kHasBackdrop, enable_backdrop);
+}
+
gfx::Point ShellSurface::GetMouseLocation() const {
aura::Window* const root_window = widget_->GetNativeWindow()->GetRootWindow();
gfx::Point location =
@@ -1720,4 +1819,13 @@ gfx::Point ShellSurface::GetMouseLocation() const {
return location;
}
+void ShellSurface::EnsureCompositorIsLocked() {
+ if (!compositor_lock_) {
+ ui::Compositor* compositor =
+ widget_->GetNativeWindow()->layer()->GetCompositor();
+ compositor_lock_ = compositor->GetCompositorLock(
+ this, base::TimeDelta::FromMilliseconds(kRotationLockTimeoutMs));
+ }
+}
+
} // namespace exo
diff --git a/chromium/components/exo/shell_surface.h b/chromium/components/exo/shell_surface.h
index 46348790e55..efcd3292b90 100644
--- a/chromium/components/exo/shell_surface.h
+++ b/chromium/components/exo/shell_surface.h
@@ -13,11 +13,13 @@
#include "ash/wm/window_state_observer.h"
#include "base/macros.h"
#include "base/strings/string16.h"
-#include "components/exo/surface_delegate.h"
#include "components/exo/surface_observer.h"
+#include "components/exo/surface_tree_host.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/window_observer.h"
#include "ui/base/hit_test.h"
+#include "ui/compositor/compositor_lock.h"
+#include "ui/display/display_observer.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
@@ -36,20 +38,28 @@ class TracedValue;
}
}
+namespace ui {
+class CompositorLock;
+} // namespace ui
+
namespace exo {
class Surface;
+enum class Orientation { PORTRAIT, LANDSCAPE };
+
// This class provides functions for treating a surfaces like toplevel,
// fullscreen or popup widgets, move, resize or maximize them, associate
// metadata like title and class, etc.
-class ShellSurface : public SurfaceDelegate,
+class ShellSurface : public SurfaceTreeHost,
public SurfaceObserver,
public views::WidgetDelegate,
public views::View,
+ public display::DisplayObserver,
public ash::wm::WindowStateObserver,
public aura::WindowObserver,
public WMHelper::ActivationObserver,
- public WMHelper::DisplayConfigurationObserver {
+ public WMHelper::DisplayConfigurationObserver,
+ NON_EXPORTED_BASE(public ui::CompositorLockClient) {
public:
enum class BoundsMode { SHELL, CLIENT, FIXED };
@@ -140,16 +150,19 @@ class ShellSurface : public SurfaceDelegate,
// Set whether the surface is always on top.
void SetAlwaysOnTop(bool always_on_top);
- // Set title for surface.
+ // Set title for the surface.
void SetTitle(const base::string16& title);
+ // Set icon for the surface.
+ void SetIcon(const gfx::ImageSkia& icon);
+
// Sets the system modality.
void SetSystemModal(bool system_modal);
// 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, const std::string& id);
- static const std::string GetApplicationId(aura::Window* window);
+ static const std::string* GetApplicationId(aura::Window* window);
// Set the application ID for the surface.
void SetApplicationId(const std::string& application_id);
@@ -170,6 +183,9 @@ class ShellSurface : public SurfaceDelegate,
// for the surface from the user's perspective.
void SetGeometry(const gfx::Rect& geometry);
+ // Set orientation for surface.
+ void SetOrientation(Orientation orientation);
+
// Enable/disable rectangular shadow that uses the widget bounds as a content
// bounds.
void SetRectangularShadowEnabled(bool enabled);
@@ -217,7 +233,6 @@ class ShellSurface : public SurfaceDelegate,
// Overridden from SurfaceDelegate:
void OnSurfaceCommit() override;
- bool IsSurfaceSynchronized() const override;
// Overridden from SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override;
@@ -227,6 +242,7 @@ class ShellSurface : public SurfaceDelegate,
bool CanMaximize() const override;
bool CanMinimize() const override;
base::string16 GetWindowTitle() const override;
+ gfx::ImageSkia GetWindowIcon() override;
void SaveWindowPlacement(const gfx::Rect& bounds,
ui::WindowShowState show_state) override;
bool GetSavedWindowPlacement(const views::Widget* widget,
@@ -273,10 +289,17 @@ class ShellSurface : public SurfaceDelegate,
// Overridden from ui::AcceleratorTarget:
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+ // Overridden from display::DisplayObserver:
+ void OnDisplayMetricsChanged(const display::Display& display,
+ uint32_t changed_metrics) override;
+
+ // Ovrridden ui::CompositorLockClient:
+ void CompositorLockTimedOut() override;
+
aura::Window* shadow_overlay() { return shadow_overlay_.get(); }
aura::Window* shadow_underlay() { return shadow_underlay_.get(); }
- Surface* surface_for_testing() { return surface_; }
+ Surface* surface_for_testing() { return root_surface(); }
private:
class ScopedConfigure;
@@ -297,7 +320,7 @@ class ShellSurface : public SurfaceDelegate,
void Configure();
// Returns the window that has capture during dragging.
- aura::Window* GetDragWindow() const;
+ aura::Window* GetDragWindow();
// Attempt to start a drag operation. The type of drag operation to start is
// determined by |component|.
@@ -329,11 +352,18 @@ class ShellSurface : public SurfaceDelegate,
// Applies |system_modal_| to |widget_|.
void UpdateSystemModal();
+ // Updates the backdrop state of the shell surface based on the
+ // bounds mode and window state.
+ void UpdateBackdrop();
+
// In the coordinate system of the parent root window.
gfx::Point GetMouseLocation() const;
+ // Lock the compositor if it's not already locked, or extends the
+ // lock timeout if it's already locked.
+ void EnsureCompositorIsLocked();
+
views::Widget* widget_ = nullptr;
- Surface* surface_;
aura::Window* parent_;
const BoundsMode bounds_mode_;
int64_t primary_display_id_;
@@ -373,7 +403,12 @@ class ShellSurface : public SurfaceDelegate,
int pending_top_inset_height_ = 0;
bool shadow_underlay_in_surface_ = true;
bool pending_shadow_underlay_in_surface_ = true;
+ Orientation pending_orientation_ = Orientation::LANDSCAPE;
+ Orientation orientation_ = Orientation::LANDSCAPE;
+ Orientation expected_orientation_ = Orientation::LANDSCAPE;
+ std::unique_ptr<ui::CompositorLock> compositor_lock_;
bool system_modal_ = false;
+ gfx::ImageSkia icon_;
DISALLOW_COPY_AND_ASSIGN(ShellSurface);
};
diff --git a/chromium/components/exo/shell_surface_unittest.cc b/chromium/components/exo/shell_surface_unittest.cc
index ab7cc8176d8..888744cf1f1 100644
--- a/chromium/components/exo/shell_surface_unittest.cc
+++ b/chromium/components/exo/shell_surface_unittest.cc
@@ -10,13 +10,15 @@
#include "ash/public/interfaces/window_pin_type.mojom.h"
#include "ash/shell.h"
#include "ash/shell_port.h"
-#include "ash/test/shell_test_api.h"
-#include "ash/test/workspace_controller_test_api.h"
+#include "ash/shell_test_api.h"
+#include "ash/system/tray/system_tray.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/wm_event.h"
#include "ash/wm/workspace/workspace_window_resizer.h"
-#include "ash/wm_window.h"
+#include "ash/wm/workspace_controller_test_api.h"
#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/exo/buffer.h"
#include "components/exo/display.h"
@@ -28,8 +30,11 @@
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/base/hit_test.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/layer.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
+#include "ui/events/test/event_generator.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/shadow.h"
#include "ui/wm/core/shadow_controller.h"
@@ -62,6 +67,38 @@ bool IsWidgetPinned(views::Widget* widget) {
type == ash::mojom::WindowPinType::TRUSTED_PINNED;
}
+class ShellSurfaceBoundsModeTest
+ : public ShellSurfaceTest,
+ public testing::WithParamInterface<ShellSurface::BoundsMode> {
+ public:
+ ShellSurfaceBoundsModeTest() {}
+ ~ShellSurfaceBoundsModeTest() override {}
+
+ bool IsClientBoundsMode() const {
+ return GetParam() == ShellSurface::BoundsMode::CLIENT;
+ }
+
+ bool HasBackdrop() {
+ ash::WorkspaceController* wc =
+ ash::ShellTestApi(ash::Shell::Get()).workspace_controller();
+ return !!ash::WorkspaceControllerTestApi(wc).GetBackdropWindow();
+ }
+
+ std::unique_ptr<ShellSurface> CreateDefaultShellSurface(Surface* surface) {
+ return base::MakeUnique<ShellSurface>(surface, nullptr, GetParam(),
+ gfx::Point(), true, false,
+ ash::kShellWindowId_DefaultContainer);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShellSurfaceBoundsModeTest);
+};
+
+INSTANTIATE_TEST_CASE_P(,
+ ShellSurfaceBoundsModeTest,
+ testing::Values(ShellSurface::BoundsMode::CLIENT,
+ ShellSurface::BoundsMode::SHELL));
+
TEST_F(ShellSurfaceTest, AcknowledgeConfigure) {
gfx::Size buffer_size(32, 32);
std::unique_ptr<Buffer> buffer(
@@ -121,18 +158,38 @@ TEST_F(ShellSurfaceTest, SetParent) {
wm::GetTransientParent(shell_surface->GetWidget()->GetNativeWindow()));
}
-TEST_F(ShellSurfaceTest, Maximize) {
+TEST_P(ShellSurfaceBoundsModeTest, Maximize) {
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()));
+ std::unique_ptr<ShellSurface> shell_surface(
+ CreateDefaultShellSurface(surface.get()));
surface->Attach(buffer.get());
+ surface->Commit();
+ EXPECT_FALSE(HasBackdrop());
shell_surface->Maximize();
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
surface->Commit();
- EXPECT_EQ(CurrentContext()->bounds().width(),
- shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
+ if (!IsClientBoundsMode()) {
+ EXPECT_EQ(CurrentContext()->bounds().width(),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
+ }
+ EXPECT_TRUE(shell_surface->GetWidget()->IsMaximized());
+
+ // Toggle maximize.
+ ash::wm::WMEvent maximize_event(ash::wm::WM_EVENT_TOGGLE_MAXIMIZE);
+ aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+
+ ash::wm::GetWindowState(window)->OnWMEvent(&maximize_event);
+ EXPECT_FALSE(shell_surface->GetWidget()->IsMaximized());
+ EXPECT_FALSE(HasBackdrop());
+
+ ash::wm::GetWindowState(window)->OnWMEvent(&maximize_event);
+ EXPECT_TRUE(shell_surface->GetWidget()->IsMaximized());
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
}
TEST_F(ShellSurfaceTest, Minimize) {
@@ -158,34 +215,51 @@ TEST_F(ShellSurfaceTest, Minimize) {
EXPECT_TRUE(shell_surface->GetWidget()->IsMinimized());
}
-TEST_F(ShellSurfaceTest, Restore) {
+TEST_P(ShellSurfaceBoundsModeTest, Restore) {
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()));
+ std::unique_ptr<ShellSurface> shell_surface(
+ CreateDefaultShellSurface(surface.get()));
surface->Attach(buffer.get());
surface->Commit();
+ EXPECT_FALSE(HasBackdrop());
// Note: Remove contents to avoid issues with maximize animations in tests.
shell_surface->Maximize();
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
shell_surface->Restore();
- EXPECT_EQ(
- buffer_size.ToString(),
- shell_surface->GetWidget()->GetWindowBoundsInScreen().size().ToString());
+ EXPECT_FALSE(HasBackdrop());
+ if (!IsClientBoundsMode()) {
+ EXPECT_EQ(buffer_size.ToString(), shell_surface->GetWidget()
+ ->GetWindowBoundsInScreen()
+ .size()
+ .ToString());
+ }
}
-TEST_F(ShellSurfaceTest, SetFullscreen) {
+TEST_P(ShellSurfaceBoundsModeTest, SetFullscreen) {
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()));
+ std::unique_ptr<ShellSurface> shell_surface(
+ CreateDefaultShellSurface(surface.get()));
shell_surface->SetFullscreen(true);
surface->Attach(buffer.get());
surface->Commit();
- EXPECT_EQ(CurrentContext()->bounds().ToString(),
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
+ if (!IsClientBoundsMode()) {
+ EXPECT_EQ(CurrentContext()->bounds().ToString(),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen().ToString());
+ }
+
+ shell_surface->SetFullscreen(false);
+ surface->Commit();
+ EXPECT_FALSE(HasBackdrop());
+ EXPECT_NE(CurrentContext()->bounds().ToString(),
shell_surface->GetWidget()->GetWindowBoundsInScreen().ToString());
}
@@ -250,9 +324,9 @@ TEST_F(ShellSurfaceTest, SetApplicationId) {
surface->Attach(buffer.get());
surface->Commit();
aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
- EXPECT_EQ("pre-widget-id", ShellSurface::GetApplicationId(window));
+ EXPECT_EQ("pre-widget-id", *ShellSurface::GetApplicationId(window));
shell_surface->SetApplicationId("test");
- EXPECT_EQ("test", ShellSurface::GetApplicationId(window));
+ EXPECT_EQ("test", *ShellSurface::GetApplicationId(window));
}
TEST_F(ShellSurfaceTest, Move) {
@@ -299,7 +373,7 @@ TEST_F(ShellSurfaceTest, SetGeometry) {
shell_surface->GetWidget()->GetWindowBoundsInScreen().size().ToString());
EXPECT_EQ(gfx::Rect(gfx::Point() - geometry.OffsetFromOrigin(), buffer_size)
.ToString(),
- surface->window()->bounds().ToString());
+ shell_surface->host_window()->bounds().ToString());
}
TEST_F(ShellSurfaceTest, SetScale) {
@@ -316,8 +390,9 @@ TEST_F(ShellSurfaceTest, SetScale) {
gfx::Transform transform;
transform.Scale(1.0 / scale, 1.0 / scale);
- EXPECT_EQ(transform.ToString(),
- surface->window()->layer()->GetTargetTransform().ToString());
+ EXPECT_EQ(
+ transform.ToString(),
+ shell_surface->host_window()->layer()->GetTargetTransform().ToString());
}
TEST_F(ShellSurfaceTest, SetTopInset) {
@@ -360,10 +435,6 @@ TEST_F(ShellSurfaceTest, CloseCallback) {
EXPECT_EQ(0, close_call_count);
shell_surface->GetWidget()->Close();
EXPECT_EQ(1, close_call_count);
-
- ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
- shell_surface->GetWidget()->OnKeyEvent(&event);
- EXPECT_EQ(2, close_call_count);
}
void DestroyShellSurface(std::unique_ptr<ShellSurface>* shell_surface) {
@@ -628,10 +699,11 @@ TEST_F(ShellSurfaceTest, SurfaceShadow) {
EXPECT_TRUE(shadow->layer()->visible());
// For surface shadow, the underlay is placed at the bottom of shell surfaces.
- EXPECT_EQ(surface->window(), shell_surface->shadow_underlay()->parent());
+ EXPECT_EQ(shell_surface->host_window(),
+ shell_surface->shadow_underlay()->parent());
EXPECT_EQ(window, shell_surface->shadow_overlay()->parent());
- EXPECT_EQ(*surface->window()->children().begin(),
+ EXPECT_EQ(*shell_surface->host_window()->children().begin(),
shell_surface->shadow_underlay());
}
@@ -854,46 +926,57 @@ TEST_F(ShellSurfaceTest, ShadowStartMaximized) {
EXPECT_EQ(gfx::Rect(10, 10, 100, 100), shadow->layer()->parent()->bounds());
}
-TEST_F(ShellSurfaceTest, ToggleFullscreen) {
+TEST_P(ShellSurfaceBoundsModeTest, ToggleFullscreen) {
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()));
+ std::unique_ptr<ShellSurface> shell_surface(
+ CreateDefaultShellSurface(surface.get()));
surface->Attach(buffer.get());
surface->Commit();
- EXPECT_EQ(
- buffer_size.ToString(),
- shell_surface->GetWidget()->GetWindowBoundsInScreen().size().ToString());
-
+ EXPECT_FALSE(HasBackdrop());
+ if (!IsClientBoundsMode()) {
+ EXPECT_EQ(buffer_size.ToString(), shell_surface->GetWidget()
+ ->GetWindowBoundsInScreen()
+ .size()
+ .ToString());
+ }
shell_surface->Maximize();
- EXPECT_EQ(CurrentContext()->bounds().width(),
- shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
+ if (!IsClientBoundsMode()) {
+ EXPECT_EQ(CurrentContext()->bounds().width(),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
+ }
ash::wm::WMEvent event(ash::wm::WM_EVENT_TOGGLE_FULLSCREEN);
- ash::WmWindow* window =
- ash::WmWindow::Get(shell_surface->GetWidget()->GetNativeWindow());
+ aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
// Enter fullscreen mode.
- window->GetWindowState()->OnWMEvent(&event);
+ ash::wm::GetWindowState(window)->OnWMEvent(&event);
- EXPECT_EQ(CurrentContext()->bounds().ToString(),
- shell_surface->GetWidget()->GetWindowBoundsInScreen().ToString());
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
+ if (!IsClientBoundsMode()) {
+ EXPECT_EQ(CurrentContext()->bounds().ToString(),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen().ToString());
+ }
// Leave fullscreen mode.
- window->GetWindowState()->OnWMEvent(&event);
+ ash::wm::GetWindowState(window)->OnWMEvent(&event);
+ EXPECT_EQ(IsClientBoundsMode(), HasBackdrop());
// Check that shell surface is maximized.
- EXPECT_EQ(CurrentContext()->bounds().width(),
- shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
+ if (!IsClientBoundsMode()) {
+ EXPECT_EQ(CurrentContext()->bounds().width(),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
+ }
}
-TEST_F(ShellSurfaceTest, MaximizedAndImmersiveFullscreenBackdrop) {
- ash::WorkspaceController* wc =
- ash::test::ShellTestApi(ash::Shell::Get()).workspace_controller();
- ash::test::WorkspaceControllerTestApi test_helper(wc);
-
+// Make sure that a surface shell started in maximize creates deprecated
+// shadow correctly.
+TEST_F(ShellSurfaceTest,
+ StartMaximizedThenMinimizeWithSetRectangularShadow_DEPRECATED) {
const gfx::Size display_size =
display::Screen::GetScreen()->GetPrimaryDisplay().size();
const gfx::Size buffer_size(display_size);
@@ -904,11 +987,15 @@ TEST_F(ShellSurfaceTest, MaximizedAndImmersiveFullscreenBackdrop) {
surface.get(), nullptr, ShellSurface::BoundsMode::CLIENT, gfx::Point(),
true, false, ash::kShellWindowId_DefaultContainer));
+ // Start in maximized.
+ shell_surface->Maximize();
surface->Attach(buffer.get());
+ surface->Commit();
- gfx::Rect shadow_bounds(10, 10, 100, 100);
+ gfx::Rect shadow_bounds =
+ display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
shell_surface->SetGeometry(shadow_bounds);
- shell_surface->SetRectangularSurfaceShadow(shadow_bounds);
+ shell_surface->SetRectangularShadow_DEPRECATED(shadow_bounds);
surface->Commit();
EXPECT_EQ(shadow_bounds,
shell_surface->GetWidget()->GetWindowBoundsInScreen());
@@ -916,66 +1003,84 @@ TEST_F(ShellSurfaceTest, MaximizedAndImmersiveFullscreenBackdrop) {
EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().size(),
shell_surface->surface_for_testing()->window()->bounds().size());
- EXPECT_FALSE(test_helper.GetBackdropWindow());
+ ash::wm::WMEvent minimize_event(ash::wm::WM_EVENT_MINIMIZE);
+ aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+ ash::wm::GetWindowState(window)->OnWMEvent(&minimize_event);
+}
- ash::wm::WMEvent fullscreen_event(ash::wm::WM_EVENT_TOGGLE_FULLSCREEN);
- ash::WmWindow* window =
- ash::WmWindow::Get(shell_surface->GetWidget()->GetNativeWindow());
+TEST_F(ShellSurfaceTest, CompositorLockInRotation) {
+ UpdateDisplay("800x600");
+ 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);
+ std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(
+ surface.get(), nullptr, ShellSurface::BoundsMode::CLIENT, gfx::Point(),
+ true, false, ash::kShellWindowId_DefaultContainer));
+ ash::Shell* shell = ash::Shell::Get();
+ shell->tablet_mode_controller()->EnableTabletModeWindowManager(true);
- // Enter immersive fullscreen mode. Shadow underlay is fullscreen.
- window->GetWindowState()->OnWMEvent(&fullscreen_event);
+ // Start in maximized.
+ shell_surface->Maximize();
+ surface->Attach(buffer.get());
+ surface->Commit();
- EXPECT_TRUE(test_helper.GetBackdropWindow());
+ gfx::Rect maximum_bounds =
+ display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
+ shell_surface->SetGeometry(maximum_bounds);
+ shell_surface->SetOrientation(Orientation::LANDSCAPE);
+ surface->Commit();
- // Leave fullscreen mode. Shadow underlay is restored.
- window->GetWindowState()->OnWMEvent(&fullscreen_event);
- EXPECT_FALSE(test_helper.GetBackdropWindow());
+ ui::Compositor* compositor =
+ shell_surface->GetWidget()->GetNativeWindow()->layer()->GetCompositor();
- ash::wm::WMEvent maximize_event(ash::wm::WM_EVENT_TOGGLE_MAXIMIZE);
+ EXPECT_FALSE(compositor->IsLocked());
- // Enter maximized mode.
- window->GetWindowState()->OnWMEvent(&maximize_event);
- EXPECT_TRUE(test_helper.GetBackdropWindow());
+ UpdateDisplay("800x600/r");
- // Leave maximized mode.
- window->GetWindowState()->OnWMEvent(&maximize_event);
- EXPECT_FALSE(test_helper.GetBackdropWindow());
+ EXPECT_TRUE(compositor->IsLocked());
+
+ shell_surface->SetOrientation(Orientation::PORTRAIT);
+ surface->Commit();
+
+ EXPECT_FALSE(compositor->IsLocked());
}
-// Make sure that a surface shell started in maximize creates deprecated
-// shadow correctly.
-TEST_F(ShellSurfaceTest,
- StartMaximizedThenMinimizeWithSetRectangularShadow_DEPRECATED) {
- const gfx::Size display_size =
- display::Screen::GetScreen()->GetPrimaryDisplay().size();
- const gfx::Size buffer_size(display_size);
+// System tray should be activated if user presses tab key while shell surface
+// is active.
+TEST_F(ShellSurfaceTest, KeyboardNavigationWithSystemTray) {
+ 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);
+ std::unique_ptr<Surface> surface(new Surface());
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(
surface.get(), nullptr, ShellSurface::BoundsMode::CLIENT, gfx::Point(),
true, false, ash::kShellWindowId_DefaultContainer));
- // Start in maximized.
- shell_surface->Maximize();
surface->Attach(buffer.get());
surface->Commit();
- gfx::Rect shadow_bounds =
- display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
- shell_surface->SetGeometry(shadow_bounds);
- shell_surface->SetRectangularShadow_DEPRECATED(shadow_bounds);
- surface->Commit();
- EXPECT_EQ(shadow_bounds,
- shell_surface->GetWidget()->GetWindowBoundsInScreen());
- ASSERT_EQ(shadow_bounds, shell_surface->shadow_underlay()->bounds());
- EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().size(),
- shell_surface->surface_for_testing()->window()->bounds().size());
+ EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
- ash::wm::WMEvent minimize_event(ash::wm::WM_EVENT_MINIMIZE);
- ash::WmWindow* window =
- ash::WmWindow::Get(shell_surface->GetWidget()->GetNativeWindow());
- window->GetWindowState()->OnWMEvent(&minimize_event);
+ // Show system tray.
+ ash::SystemTray* system_tray = GetPrimarySystemTray();
+ system_tray->ShowDefaultView(ash::BUBBLE_CREATE_NEW);
+ 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->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);
+
+ // Confirm that system tray is activated.
+ EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
+ EXPECT_TRUE(
+ system_tray->GetSystemBubble()->bubble_view()->GetWidget()->IsActive());
}
} // namespace
diff --git a/chromium/components/exo/sub_surface.cc b/chromium/components/exo/sub_surface.cc
index 76e7978e926..1f8e2f3075f 100644
--- a/chromium/components/exo/sub_surface.cc
+++ b/chromium/components/exo/sub_surface.cc
@@ -96,7 +96,6 @@ void SubSurface::OnSurfaceCommit() {
if (IsSurfaceSynchronized())
return;
- surface_->CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
surface_->CommitSurfaceHierarchy();
}
diff --git a/chromium/components/exo/surface.cc b/chromium/components/exo/surface.cc
index 7649a08b9f0..7cca49f6a19 100644
--- a/chromium/components/exo/surface.cc
+++ b/chromium/components/exo/surface.cc
@@ -12,22 +12,23 @@
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
-#include "cc/output/compositor_frame_sink.h"
+#include "cc/output/layer_tree_frame_sink.h"
#include "cc/quads/render_pass.h"
#include "cc/quads/shared_quad_state.h"
#include "cc/quads/solid_color_draw_quad.h"
#include "cc/quads/texture_draw_quad.h"
#include "cc/resources/single_release_callback.h"
-#include "cc/surfaces/sequence_surface_reference_factory.h"
#include "cc/surfaces/surface.h"
#include "cc/surfaces/surface_manager.h"
#include "components/exo/buffer.h"
#include "components/exo/pointer.h"
#include "components/exo/surface_delegate.h"
#include "components/exo/surface_observer.h"
+#include "components/viz/common/surfaces/sequence_surface_reference_factory.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
+#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_targeter.h"
#include "ui/base/class_property.h"
@@ -145,6 +146,24 @@ class CustomWindowTargeter : public aura::WindowTargeter {
~CustomWindowTargeter() override {}
// Overridden from aura::WindowTargeter:
+ bool SubtreeCanAcceptEvent(aura::Window* window,
+ const ui::LocatedEvent& event) const override {
+ Surface* surface = Surface::AsSurface(window);
+ if (!surface)
+ return false;
+
+ if (surface->IsStylusOnly()) {
+ ui::EventPointerType type = ui::EventPointerType::POINTER_TYPE_UNKNOWN;
+ if (event.IsTouchEvent()) {
+ auto* touch_event = static_cast<const ui::TouchEvent*>(&event);
+ type = touch_event->pointer_details().pointer_type;
+ }
+ if (type != ui::EventPointerType::POINTER_TYPE_PEN)
+ return false;
+ }
+ return aura::WindowTargeter::SubtreeCanAcceptEvent(window, event);
+ }
+
bool EventLocationInsideBounds(aura::Window* window,
const ui::LocatedEvent& event) const override {
Surface* surface = Surface::AsSurface(window);
@@ -167,10 +186,6 @@ class CustomWindowTargeter : public aura::WindowTargeter {
////////////////////////////////////////////////////////////////////////////////
// Surface, public:
-// TODO(fsamuel): exo should not use context_factory_private. Instead, we should
-// request a CompositorFrameSink from the aura::Window. Setting up the
-// BeginFrame hierarchy should be an internal implementation detail of aura or
-// mus in aura-mus.
Surface::Surface() : window_(new aura::Window(new CustomWindowDelegate(this))) {
window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
window_->SetName("ExoSurface");
@@ -180,10 +195,10 @@ Surface::Surface() : window_(new aura::Window(new CustomWindowDelegate(this))) {
window_->set_owned_by_parent(false);
window_->AddObserver(this);
aura::Env::GetInstance()->context_factory()->AddObserver(this);
- compositor_frame_sink_holder_ = base::MakeUnique<CompositorFrameSinkHolder>(
- this, window_->CreateCompositorFrameSink());
+ layer_tree_frame_sink_holder_ = base::MakeUnique<LayerTreeFrameSinkHolder>(
+ this, window_->CreateLayerTreeFrameSink());
+ WMHelper::GetInstance()->SetDragDropDelegate(window_.get());
}
-
Surface::~Surface() {
aura::Env::GetInstance()->context_factory()->RemoveObserver(this);
for (SurfaceObserver& observer : observers_)
@@ -212,6 +227,8 @@ Surface::~Surface() {
// that they have been cancelled.
for (const auto& presentation_callback : swapped_presentation_callbacks_)
presentation_callback.Run(base::TimeTicks(), base::TimeDelta());
+
+ WMHelper::GetInstance()->ResetDragDropDelegate(window_.get());
}
// static
@@ -219,7 +236,7 @@ Surface* Surface::AsSurface(const aura::Window* window) {
return window->GetProperty(kSurfaceKey);
}
-cc::SurfaceId Surface::GetSurfaceId() const {
+viz::SurfaceId Surface::GetSurfaceId() const {
return window_->GetSurfaceId();
}
@@ -427,20 +444,19 @@ void Surface::Commit() {
if (delegate_) {
delegate_->OnSurfaceCommit();
} else {
- CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
CommitSurfaceHierarchy();
}
if (current_begin_frame_ack_.sequence_number !=
cc::BeginFrameArgs::kInvalidFrameNumber) {
- if (begin_frame_source_)
- begin_frame_source_->DidFinishFrame(this, current_begin_frame_ack_);
if (!current_begin_frame_ack_.has_damage) {
- compositor_frame_sink_holder_->GetCompositorFrameSink()
- ->DidNotProduceFrame(current_begin_frame_ack_);
+ layer_tree_frame_sink_holder_->frame_sink()->DidNotProduceFrame(
+ current_begin_frame_ack_);
}
current_begin_frame_ack_.sequence_number =
cc::BeginFrameArgs::kInvalidFrameNumber;
+ if (begin_frame_source_)
+ begin_frame_source_->DidFinishFrame(this);
}
}
@@ -449,6 +465,18 @@ void Surface::CommitSurfaceHierarchy() {
needs_commit_surface_hierarchy_ = false;
has_pending_layer_changes_ = false;
+ bool full_damage = false;
+ if (pending_state_.opaque_region != state_.opaque_region ||
+ pending_state_.buffer_scale != state_.buffer_scale ||
+ pending_state_.viewport != state_.viewport ||
+ pending_state_.crop != state_.crop ||
+ pending_state_.only_visible_on_secure_output !=
+ state_.only_visible_on_secure_output ||
+ pending_state_.blend_mode != state_.blend_mode ||
+ pending_state_.alpha != state_.alpha) {
+ full_damage = true;
+ }
+
state_ = pending_state_;
pending_state_.only_visible_on_secure_output = false;
@@ -468,21 +496,17 @@ void Surface::CommitSurfaceHierarchy() {
presentation_callbacks_.splice(presentation_callbacks_.end(),
pending_presentation_callbacks_);
- UpdateSurface(false);
+ UpdateSurface(full_damage);
- if (needs_commit_to_new_surface_) {
- needs_commit_to_new_surface_ = false;
- window_->layer()->SetFillsBoundsOpaquely(
- !current_resource_has_alpha_ ||
- state_.blend_mode == SkBlendMode::kSrc ||
- state_.opaque_region.contains(
- gfx::RectToSkIRect(gfx::Rect(content_size_))));
- }
+ window_->layer()->SetFillsBoundsOpaquely(
+ !current_resource_has_alpha_ || state_.blend_mode == SkBlendMode::kSrc ||
+ state_.opaque_region.contains(
+ gfx::RectToSkIRect(gfx::Rect(content_size_))));
// Reset damage.
pending_damage_.setEmpty();
DCHECK(!current_resource_.id ||
- compositor_frame_sink_holder_->HasReleaseCallbackForResource(
+ layer_tree_frame_sink_holder_->HasReleaseCallbackForResource(
current_resource_.id));
// Synchronize window hierarchy. This will position and update the stacking
@@ -511,8 +535,8 @@ void Surface::CommitSurfaceHierarchy() {
stacking_target = sub_surface->window();
// Update sub-surface position relative to surface origin.
- sub_surface->window()->SetBounds(gfx::Rect(
- sub_surface_entry.second, sub_surface->window()->layer()->size()));
+ sub_surface->window()->SetBounds(
+ gfx::Rect(sub_surface_entry.second, sub_surface->content_size_));
}
}
@@ -522,8 +546,7 @@ bool Surface::IsSynchronized() const {
gfx::Rect Surface::GetHitTestBounds() const {
SkIRect bounds = state_.input_region.getBounds();
- if (!bounds.intersect(
- gfx::RectToSkIRect(gfx::Rect(window_->layer()->size()))))
+ if (!bounds.intersect(gfx::RectToSkIRect(gfx::Rect(content_size_))))
return gfx::Rect();
return gfx::SkIRectToRect(bounds);
}
@@ -532,12 +555,12 @@ bool Surface::HitTestRect(const gfx::Rect& rect) const {
if (HasHitTestMask())
return state_.input_region.intersects(gfx::RectToSkIRect(rect));
- return rect.Intersects(gfx::Rect(window_->layer()->size()));
+ return rect.Intersects(gfx::Rect(content_size_));
}
bool Surface::HasHitTestMask() const {
return !state_.input_region.contains(
- gfx::RectToSkIRect(gfx::Rect(window_->layer()->size())));
+ gfx::RectToSkIRect(gfx::Rect(content_size_)));
}
void Surface::GetHitTestMask(gfx::Path* mask) const {
@@ -625,8 +648,8 @@ void Surface::UpdateNeedsBeginFrame() {
}
bool Surface::OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) {
- current_begin_frame_ack_ = cc::BeginFrameAck(
- args.source_id, args.sequence_number, args.sequence_number, false);
+ current_begin_frame_ack_ =
+ cc::BeginFrameAck(args.source_id, args.sequence_number, false);
while (!active_frame_callbacks_.empty()) {
active_frame_callbacks_.front().Run(args.frame_time);
active_frame_callbacks_.pop_front();
@@ -634,11 +657,6 @@ bool Surface::OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) {
return true;
}
-void Surface::CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces() {
- if (HasLayerHierarchyChanged())
- SetSurfaceHierarchyNeedsCommitToNewSurfaces();
-}
-
bool Surface::IsStylusOnly() {
return window_->GetProperty(kStylusOnlyKey);
}
@@ -738,28 +756,11 @@ void Surface::BufferAttachment::Reset(base::WeakPtr<Buffer> buffer) {
buffer_ = buffer;
}
-bool Surface::HasLayerHierarchyChanged() const {
- if (needs_commit_surface_hierarchy_ && has_pending_layer_changes_)
- return true;
-
- for (const auto& sub_surface_entry : pending_sub_surfaces_) {
- if (sub_surface_entry.first->HasLayerHierarchyChanged())
- return true;
- }
- return false;
-}
-
-void Surface::SetSurfaceHierarchyNeedsCommitToNewSurfaces() {
- needs_commit_to_new_surface_ = true;
- for (auto& sub_surface_entry : pending_sub_surfaces_) {
- sub_surface_entry.first->SetSurfaceHierarchyNeedsCommitToNewSurfaces();
- }
-}
-
void Surface::UpdateResource(bool client_usage) {
if (current_buffer_.buffer() &&
current_buffer_.buffer()->ProduceTransferableResource(
- compositor_frame_sink_holder_.get(), next_resource_id_++,
+ layer_tree_frame_sink_holder_.get(),
+ layer_tree_frame_sink_holder_->AllocateResourceId(),
state_.only_visible_on_secure_output, client_usage,
&current_resource_)) {
current_resource_has_alpha_ =
@@ -790,6 +791,10 @@ void Surface::UpdateSurface(bool full_damage) {
}
content_size_ = layer_size;
+ // We need update window_'s bounds with content size, because the
+ // LayerTreeFrameSink may not update the window's size base the size of
+ // the lastest submitted CompositorFrame.
+ window_->SetBounds(gfx::Rect(window_->bounds().origin(), content_size_));
// TODO(jbauman): Figure out how this interacts with the pixel size of
// CopyOutputRequests on the layer.
gfx::Size contents_surface_size = layer_size;
@@ -870,8 +875,8 @@ void Surface::UpdateSurface(bool full_damage) {
}
frame.render_pass_list.push_back(std::move(render_pass));
- compositor_frame_sink_holder_->GetCompositorFrameSink()
- ->SubmitCompositorFrame(std::move(frame));
+ layer_tree_frame_sink_holder_->frame_sink()->SubmitCompositorFrame(
+ std::move(frame));
}
} // namespace exo
diff --git a/chromium/components/exo/surface.h b/chromium/components/exo/surface.h
index 0abd5113788..ae168733029 100644
--- a/chromium/components/exo/surface.h
+++ b/chromium/components/exo/surface.h
@@ -16,7 +16,7 @@
#include "base/observer_list.h"
#include "cc/resources/transferable_resource.h"
#include "cc/scheduler/begin_frame_source.h"
-#include "components/exo/compositor_frame_sink_holder.h"
+#include "components/exo/layer_tree_frame_sink_holder.h"
#include "third_party/skia/include/core/SkBlendMode.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/aura/window.h"
@@ -68,10 +68,10 @@ class Surface : public ui::ContextFactoryObserver,
aura::Window* window() { return window_.get(); }
- cc::SurfaceId GetSurfaceId() const;
+ viz::SurfaceId GetSurfaceId() const;
- CompositorFrameSinkHolder* compositor_frame_sink_holder() {
- return compositor_frame_sink_holder_.get();
+ LayerTreeFrameSinkHolder* layer_tree_frame_sink_holder() {
+ return layer_tree_frame_sink_holder_.get();
}
// Set a buffer as the content of this surface. A buffer can only be attached
@@ -193,12 +193,8 @@ class Surface : public ui::ContextFactoryObserver,
// Called when the begin frame source has changed.
void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source);
- // Check whether this Surface and its children need to create new cc::Surface
- // IDs for their contents next time they get new buffer contents.
- void CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
-
// Returns the active contents size.
- gfx::Size content_size() const { return content_size_; }
+ const gfx::Size& content_size() const { return content_size_; }
// Returns true if the associated window is in 'stylus-only' mode.
bool IsStylusOnly();
@@ -266,13 +262,6 @@ class Surface : public ui::ContextFactoryObserver,
return needs_commit_surface_hierarchy_;
}
- // Returns true if this surface or any child surface needs a commit and has
- // has_pending_layer_changes_ true.
- bool HasLayerHierarchyChanged() const;
-
- // Sets that all children must create new cc::SurfaceIds for their contents.
- void SetSurfaceHierarchyNeedsCommitToNewSurfaces();
-
// Set SurfaceLayer contents to the current buffer.
void SetSurfaceLayerContents(ui::Layer* layer);
@@ -300,11 +289,6 @@ class Surface : public ui::ContextFactoryObserver,
// buffer with the same size as the old shouldn't set this to true.
bool has_pending_layer_changes_ = true;
- // This is true if the next commit to this surface should put its contents
- // into a new cc::SurfaceId. This allows for synchronization between Surface
- // and layer changes.
- bool needs_commit_to_new_surface_ = true;
-
// This is the size of the last committed contents.
gfx::Size content_size_;
@@ -318,10 +302,7 @@ class Surface : public ui::ContextFactoryObserver,
// The device scale factor sent in CompositorFrames.
float device_scale_factor_ = 1.0f;
- std::unique_ptr<CompositorFrameSinkHolder> compositor_frame_sink_holder_;
-
- // The next resource id the buffer will be attached to.
- int next_resource_id_ = 1;
+ std::unique_ptr<LayerTreeFrameSinkHolder> layer_tree_frame_sink_holder_;
// The damage region to schedule paint for when Commit() is called.
SkRegion pending_damage_;
diff --git a/chromium/components/exo/surface_tree_host.cc b/chromium/components/exo/surface_tree_host.cc
new file mode 100644
index 00000000000..36442f58f32
--- /dev/null
+++ b/chromium/components/exo/surface_tree_host.cc
@@ -0,0 +1,144 @@
+// 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/exo/surface_tree_host.h"
+
+#include <algorithm>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/exo/surface.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_targeter.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/gfx/path.h"
+
+namespace exo {
+
+namespace {
+
+class CustomWindowTargeter : public aura::WindowTargeter {
+ public:
+ explicit CustomWindowTargeter(SurfaceTreeHost* surface_tree_host)
+ : surface_tree_host_(surface_tree_host) {}
+ ~CustomWindowTargeter() override = default;
+
+ // Overridden from aura::WindowTargeter:
+ bool EventLocationInsideBounds(aura::Window* window,
+ const ui::LocatedEvent& event) const override {
+ if (window != surface_tree_host_->host_window())
+ return aura::WindowTargeter::EventLocationInsideBounds(window, event);
+
+ Surface* surface = surface_tree_host_->root_surface();
+ if (!surface)
+ return false;
+
+ gfx::Point local_point = event.location();
+
+ if (window->parent())
+ aura::Window::ConvertPointToTarget(window->parent(), window,
+ &local_point);
+ aura::Window::ConvertPointToTarget(window, surface->window(), &local_point);
+ return surface->HitTestRect(gfx::Rect(local_point, gfx::Size(1, 1)));
+ }
+
+ ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
+ ui::Event* event) override {
+ aura::Window* window = static_cast<aura::Window*>(root);
+ if (window != surface_tree_host_->host_window())
+ return aura::WindowTargeter::FindTargetForEvent(root, event);
+ ui::EventTarget* target =
+ aura::WindowTargeter::FindTargetForEvent(root, event);
+ // Do not accept events in SurfaceTreeHost window.
+ return target != root ? target : nullptr;
+ }
+
+ private:
+ SurfaceTreeHost* const surface_tree_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter);
+};
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// SurfaceTreeHost, public:
+
+SurfaceTreeHost::SurfaceTreeHost(const std::string& window_name,
+ aura::WindowDelegate* window_delegate) {
+ host_window_ = base::MakeUnique<aura::Window>(window_delegate);
+ host_window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
+ host_window_->SetName(window_name);
+ host_window_->Init(ui::LAYER_NOT_DRAWN);
+ host_window_->set_owned_by_parent(false);
+ host_window_->SetEventTargeter(base::MakeUnique<CustomWindowTargeter>(this));
+}
+
+SurfaceTreeHost::~SurfaceTreeHost() {
+ if (root_surface_) {
+ root_surface_->window()->Hide();
+ root_surface_->SetSurfaceDelegate(nullptr);
+ }
+}
+
+void SurfaceTreeHost::SetRootSurface(Surface* root_surface) {
+ if (root_surface == root_surface_)
+ return;
+
+ if (root_surface_) {
+ root_surface_->window()->Hide();
+ host_window_->RemoveChild(root_surface_->window());
+ host_window_->SetBounds(
+ gfx::Rect(host_window_->bounds().origin(), gfx::Size()));
+ root_surface_->SetSurfaceDelegate(nullptr);
+ root_surface_ = nullptr;
+ }
+
+ if (root_surface) {
+ root_surface_ = root_surface;
+ root_surface_->SetSurfaceDelegate(this);
+ host_window_->AddChild(root_surface_->window());
+ host_window_->SetBounds(gfx::Rect(host_window_->bounds().origin(),
+ root_surface_->content_size()));
+ root_surface_->window()->Show();
+ }
+}
+
+bool SurfaceTreeHost::HasHitTestMask() const {
+ return root_surface_ ? root_surface_->HasHitTestMask() : false;
+}
+
+void SurfaceTreeHost::GetHitTestMask(gfx::Path* mask) const {
+ if (root_surface_)
+ root_surface_->GetHitTestMask(mask);
+}
+
+gfx::Rect SurfaceTreeHost::GetHitTestBounds() const {
+ return root_surface_ ? root_surface_->GetHitTestBounds() : gfx::Rect();
+}
+
+gfx::NativeCursor SurfaceTreeHost::GetCursor(const gfx::Point& point) const {
+ return root_surface_ ? root_surface_->GetCursor() : ui::CursorType::kNull;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SurfaceDelegate overrides:
+
+void SurfaceTreeHost::OnSurfaceCommit() {
+ DCHECK(root_surface_);
+ root_surface_->CommitSurfaceHierarchy();
+ host_window_->SetBounds(gfx::Rect(host_window_->bounds().origin(),
+ root_surface_->content_size()));
+}
+
+bool SurfaceTreeHost::IsSurfaceSynchronized() const {
+ // To host a surface tree, the root surface has to be desynchronized.
+ DCHECK(root_surface_);
+ return false;
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/surface_tree_host.h b/chromium/components/exo/surface_tree_host.h
new file mode 100644
index 00000000000..c92a4342e04
--- /dev/null
+++ b/chromium/components/exo/surface_tree_host.h
@@ -0,0 +1,70 @@
+// 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_EXO_SURFACE_TREE_HOST_H_
+#define COMPONENTS_EXO_SURFACE_TREE_HOST_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/exo/surface.h"
+#include "components/exo/surface_delegate.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace aura {
+class Window;
+class WindowDelegate;
+} // namespace aura
+
+namespace gfx {
+class Path;
+} // namespace gfx
+
+namespace exo {
+
+// This class provides functionality for hosting a surface tree. The surface
+// tree is hosted in the |host_window_|.
+class SurfaceTreeHost : public SurfaceDelegate {
+ public:
+ SurfaceTreeHost(const std::string& window_name,
+ aura::WindowDelegate* window_delegate);
+ ~SurfaceTreeHost() override;
+
+ // Sets a root surface of a surface tree. This surface tree will be hosted in
+ // the |host_window_|.
+ void SetRootSurface(Surface* root_surface);
+
+ // Returns true if hosted surface tree has hittest mask.
+ bool HasHitTestMask() const;
+
+ // Returns the hittest mask in |mask| for the hosted surface tree.
+ void GetHitTestMask(gfx::Path* mask) const;
+
+ // Returns the bounds of the current input region of the hosted surface tree.
+ gfx::Rect GetHitTestBounds() const;
+
+ // Returns the cursor for the given position. If no cursor provider is
+ // registered then CursorType::kNull is returned.
+ gfx::NativeCursor GetCursor(const gfx::Point& point) const;
+
+ aura::Window* host_window() { return host_window_.get(); }
+ const aura::Window* host_window() const { return host_window_.get(); }
+
+ Surface* root_surface() { return root_surface_; }
+ const Surface* root_surface() const { return root_surface_; }
+
+ // Overridden from SurfaceDelegate:
+ void OnSurfaceCommit() override;
+ bool IsSurfaceSynchronized() const override;
+
+ private:
+ Surface* root_surface_ = nullptr;
+ std::unique_ptr<aura::Window> host_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceTreeHost);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_SURFACE_TREE_HOST_H_
diff --git a/chromium/components/exo/surface_unittest.cc b/chromium/components/exo/surface_unittest.cc
index 1eb280384a9..5aa26f8905d 100644
--- a/chromium/components/exo/surface_unittest.cc
+++ b/chromium/components/exo/surface_unittest.cc
@@ -6,13 +6,13 @@
#include "base/bind.h"
#include "cc/output/compositor_frame.h"
#include "cc/quads/texture_draw_quad.h"
-#include "cc/surfaces/surface_manager.h"
#include "cc/test/begin_frame_args_test.h"
#include "cc/test/fake_external_begin_frame_source.h"
#include "components/exo/buffer.h"
#include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.h"
#include "components/exo/test/exo_test_helper.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/env.h"
#include "ui/compositor/layer_tree_owner.h"
@@ -52,8 +52,8 @@ TEST_F(SurfaceTest, Attach) {
// attached buffer.
surface->Attach(nullptr);
surface->Commit();
- // CompositorFrameSinkHolder::ReclaimResources() gets called via
- // MojoCompositorFrameSinkClient interface. We need to wait here for the mojo
+ // LayerTreeFrameSinkHolder::ReclaimResources() gets called via
+ // CompositorFrameSinkClient interface. We need to wait here for the mojo
// call to finish so that the release callback finishes running before
// the assertion below.
RunAllPendingInMessageLoop();
@@ -100,9 +100,11 @@ TEST_F(SurfaceTest, RequestFrameCallback) {
}
const cc::CompositorFrame& GetFrameFromSurface(Surface* surface) {
- cc::SurfaceId surface_id = surface->GetSurfaceId();
- cc::SurfaceManager* surface_manager =
- aura::Env::GetInstance()->context_factory_private()->GetSurfaceManager();
+ viz::SurfaceId surface_id = surface->GetSurfaceId();
+ cc::SurfaceManager* surface_manager = aura::Env::GetInstance()
+ ->context_factory_private()
+ ->GetFrameSinkManager()
+ ->surface_manager();
const cc::CompositorFrame& frame =
surface_manager->GetSurfaceForId(surface_id)->GetActiveFrame();
return frame;
@@ -130,6 +132,8 @@ TEST_F(SurfaceTest, SetOpaqueRegion) {
EXPECT_FALSE(frame.render_pass_list.back()
->quad_list.back()
->ShouldDrawWithBlending());
+ EXPECT_EQ(gfx::Rect(0, 0, 1, 1),
+ frame.render_pass_list.back()->damage_rect);
}
// Setting an empty opaque region requires draw with blending.
@@ -144,6 +148,8 @@ TEST_F(SurfaceTest, SetOpaqueRegion) {
EXPECT_TRUE(frame.render_pass_list.back()
->quad_list.back()
->ShouldDrawWithBlending());
+ EXPECT_EQ(gfx::Rect(0, 0, 1, 1),
+ frame.render_pass_list.back()->damage_rect);
}
std::unique_ptr<Buffer> buffer_without_alpha(
@@ -163,6 +169,8 @@ TEST_F(SurfaceTest, SetOpaqueRegion) {
EXPECT_FALSE(frame.render_pass_list.back()
->quad_list.back()
->ShouldDrawWithBlending());
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0),
+ frame.render_pass_list.back()->damage_rect);
}
}
@@ -194,6 +202,13 @@ TEST_F(SurfaceTest, SetBufferScale) {
EXPECT_EQ(
gfx::ScaleToFlooredSize(buffer_size, 1.0f / kBufferScale).ToString(),
surface->content_size().ToString());
+
+ RunAllPendingInMessageLoop();
+
+ const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
+ ASSERT_EQ(1u, frame.render_pass_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 256, 256),
+ frame.render_pass_list.back()->damage_rect);
}
TEST_F(SurfaceTest, MirrorLayers) {
@@ -238,6 +253,13 @@ TEST_F(SurfaceTest, SetViewport) {
EXPECT_EQ(viewport2.ToString(),
surface->window()->bounds().size().ToString());
EXPECT_EQ(viewport2.ToString(), surface->content_size().ToString());
+
+ RunAllPendingInMessageLoop();
+
+ const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
+ ASSERT_EQ(1u, frame.render_pass_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 512, 512),
+ frame.render_pass_list.back()->damage_rect);
}
TEST_F(SurfaceTest, SetCrop) {
@@ -253,6 +275,13 @@ TEST_F(SurfaceTest, SetCrop) {
EXPECT_EQ(crop_size.ToString(),
surface->window()->bounds().size().ToString());
EXPECT_EQ(crop_size.ToString(), surface->content_size().ToString());
+
+ RunAllPendingInMessageLoop();
+
+ const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
+ ASSERT_EQ(1u, frame.render_pass_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 12, 12),
+ frame.render_pass_list.back()->damage_rect);
}
TEST_F(SurfaceTest, SetBlendMode) {
@@ -304,6 +333,11 @@ TEST_F(SurfaceTest, SetAlpha) {
surface->Attach(buffer.get());
surface->SetAlpha(0.5f);
surface->Commit();
+ RunAllPendingInMessageLoop();
+
+ const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
+ ASSERT_EQ(1u, frame.render_pass_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 1, 1), frame.render_pass_list.back()->damage_rect);
}
TEST_F(SurfaceTest, Commit) {
@@ -339,15 +373,14 @@ TEST_F(SurfaceTest, SendsBeginFrameAcks) {
source.TestOnBeginFrame(args); // Runs the frame callback.
EXPECT_EQ(args.frame_time, frame_time);
- surface->Commit(); // Acknowledges the BeginFrame.
+ surface->Commit(); // Acknowledges the BeginFrame via CompositorFrame.
RunAllPendingInMessageLoop();
- cc::BeginFrameAck expected_ack(args.source_id, args.sequence_number,
- args.sequence_number, true);
- EXPECT_EQ(expected_ack, source.LastAckForObserver(surface.get()));
-
const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
+ cc::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.
}
} // namespace
diff --git a/chromium/components/exo/touch_unittest.cc b/chromium/components/exo/touch_unittest.cc
index 665b2701c4e..743461d1b29 100644
--- a/chromium/components/exo/touch_unittest.cc
+++ b/chromium/components/exo/touch_unittest.cc
@@ -9,7 +9,6 @@
#include "ash/shell_port.h"
#include "ash/wm/window_positioner.h"
#include "ash/wm/window_positioning_utils.h"
-#include "ash/wm_window.h"
#include "components/exo/buffer.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
@@ -399,5 +398,30 @@ TEST_F(TouchTest, OnTouchTilt) {
touch.reset();
}
+TEST_F(TouchTest, OnTouchInStylusOnlyWindow) {
+ auto window = exo_test_helper()->CreateWindow(10, 10, true);
+ window.surface()->SetStylusOnly();
+
+ MockTouchDelegate delegate;
+ std::unique_ptr<Touch> touch(new Touch(&delegate));
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+
+ EXPECT_CALL(delegate, CanAcceptTouchEventsForSurface(window.surface()))
+ .WillRepeatedly(testing::Return(true));
+
+ EXPECT_CALL(delegate,
+ OnTouchDown(window.surface(), testing::_, testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(delegate, OnTouchMotion(testing::_, testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(delegate, OnTouchUp(testing::_, testing::_)).Times(0);
+ EXPECT_CALL(delegate, OnTouchFrame()).Times(0);
+ generator.set_current_location(window.origin());
+ generator.PressMoveAndReleaseTouchBy(5, 5);
+
+ EXPECT_CALL(delegate, OnTouchDestroying(touch.get()));
+ touch.reset();
+}
+
} // namespace
} // namespace exo
diff --git a/chromium/components/exo/wayland/BUILD.gn b/chromium/components/exo/wayland/BUILD.gn
index e93a23483d7..dc01f9428bf 100644
--- a/chromium/components/exo/wayland/BUILD.gn
+++ b/chromium/components/exo/wayland/BUILD.gn
@@ -40,6 +40,7 @@ source_set("wayland") {
"//third_party/wayland-protocols:alpha_compositing_protocol",
"//third_party/wayland-protocols:gaming_input_protocol",
"//third_party/wayland-protocols:keyboard_configuration_protocol",
+ "//third_party/wayland-protocols:keyboard_extension_protocol",
"//third_party/wayland-protocols:presentation_time_protocol",
"//third_party/wayland-protocols:remote_shell_protocol",
"//third_party/wayland-protocols:secure_output_protocol",
@@ -63,6 +64,10 @@ source_set("wayland") {
"//third_party/wayland-protocols:linux_dmabuf_protocol",
]
configs += [ ":libdrm" ]
+
+ if (is_chromeos) {
+ deps += [ "//ui/events/ozone:events_ozone_layout" ]
+ }
}
if (use_xkbcommon) {
diff --git a/chromium/components/exo/wayland/clients/client_base.cc b/chromium/components/exo/wayland/clients/client_base.cc
index e604367854d..ddc142a1aec 100644
--- a/chromium/components/exo/wayland/clients/client_base.cc
+++ b/chromium/components/exo/wayland/clients/client_base.cc
@@ -16,11 +16,13 @@
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkRefCnt.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"
#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
@@ -112,25 +114,11 @@ void BufferRelease(void* data, wl_buffer* /* buffer */) {
}
#if defined(OZONE_PLATFORM_GBM)
-void LinuxBufferParamsCreated(void* data,
- zwp_linux_buffer_params_v1* params,
- wl_buffer* new_buffer) {
- ClientBase::Buffer* buffer = static_cast<ClientBase::Buffer*>(data);
- buffer->buffer.reset(new_buffer);
-}
-
-void LinuxBufferParamsFailed(void* data, zwp_linux_buffer_params_v1* params) {
- LOG(ERROR) << "Linux buffer params failed";
-}
-
const GrGLInterface* GrGLCreateNativeInterface() {
return GrGLAssembleInterface(nullptr, [](void* ctx, const char name[]) {
return eglGetProcAddress(name);
});
}
-
-zwp_linux_buffer_params_v1_listener g_params_listener = {
- LinuxBufferParamsCreated, LinuxBufferParamsFailed};
#endif
wl_registry_listener g_registry_listener = {RegistryHandler, RegistryRemover};
@@ -274,7 +262,7 @@ bool ClientBase::Init(const InitParams& params) {
LOG(ERROR) << "Can't create gbm device";
return false;
}
-
+ ui_loop_.reset(new base::MessageLoopForUI);
ui::OzonePlatform::InitParams params;
params.single_process = true;
ui::OzonePlatform::InitializeForGPU(params);
@@ -313,10 +301,11 @@ bool ClientBase::Init(const InitParams& params) {
buffers_.push_back(std::move(buffer));
}
- wl_display_roundtrip(display_.get());
for (size_t i = 0; i < buffers_.size(); ++i) {
+ // If the buffer handle doesn't exist, we would either be killed by the
+ // server or die here.
if (!buffers_[i]->buffer) {
- LOG(ERROR) << "LinuxBufferParamsCreated was not called on the buffer.";
+ LOG(ERROR) << "buffer handle uninitialized.";
return false;
}
@@ -388,8 +377,6 @@ std::unique_ptr<ClientBase::Buffer> ClientBase::CreateBuffer(
buffer->params.reset(
zwp_linux_dmabuf_v1_create_params(globals_.linux_dmabuf.get()));
- zwp_linux_buffer_params_v1_add_listener(buffer->params.get(),
- &g_params_listener, buffer.get());
for (size_t i = 0; i < gbm_bo_get_num_planes(buffer->bo.get()); ++i) {
base::ScopedFD fd(gbm_bo_get_plane_fd(buffer->bo.get(), i));
uint32_t stride = gbm_bo_get_plane_stride(buffer->bo.get(), i);
@@ -397,8 +384,8 @@ std::unique_ptr<ClientBase::Buffer> ClientBase::CreateBuffer(
zwp_linux_buffer_params_v1_add(buffer->params.get(), fd.get(), i, offset,
stride, 0, 0);
}
- zwp_linux_buffer_params_v1_create(buffer->params.get(), width_, height_,
- drm_format, 0);
+ buffer->buffer.reset(zwp_linux_buffer_params_v1_create_immed(
+ buffer->params.get(), width_, height_, drm_format, 0));
if (gbm_bo_get_num_planes(buffer->bo.get()) != 1)
return buffer;
@@ -432,16 +419,11 @@ std::unique_ptr<ClientBase::Buffer> ClientBase::CreateBuffer(
GrGLTextureInfo texture_info;
texture_info.fID = buffer->texture->get();
texture_info.fTarget = GL_TEXTURE_2D;
- GrBackendTextureDesc desc;
- desc.fFlags = kRenderTarget_GrBackendTextureFlag;
- desc.fWidth = width_;
- desc.fHeight = height_;
- desc.fConfig = kGrPixelConfig;
- desc.fOrigin = kTopLeft_GrSurfaceOrigin;
- desc.fTextureHandle = reinterpret_cast<GrBackendObject>(&texture_info);
-
+ GrBackendTexture backend_texture(width_, height_, kGrPixelConfig,
+ texture_info);
buffer->sk_surface = SkSurface::MakeFromBackendTextureAsRenderTarget(
- gr_context_.get(), desc, nullptr);
+ gr_context_.get(), backend_texture, kTopLeft_GrSurfaceOrigin,
+ /* sampleCnt */ 0, /* colorSpace */ nullptr, /* props */ nullptr);
DCHECK(buffer->sk_surface);
return buffer;
}
diff --git a/chromium/components/exo/wayland/clients/client_base.h b/chromium/components/exo/wayland/clients/client_base.h
index be00a8c4bcf..2fef99afd61 100644
--- a/chromium/components/exo/wayland/clients/client_base.h
+++ b/chromium/components/exo/wayland/clients/client_base.h
@@ -19,6 +19,7 @@
namespace base {
class CommandLine;
+class MessageLoopForUI;
}
namespace exo {
@@ -92,6 +93,7 @@ class ClientBase {
std::unique_ptr<wl_shell_surface> shell_surface_;
Globals globals_;
#if defined(OZONE_PLATFORM_GBM)
+ std::unique_ptr<base::MessageLoopForUI> ui_loop_;
base::ScopedFD drm_fd_;
std::unique_ptr<gbm_device> device_;
#endif
diff --git a/chromium/components/exo/wayland/server.cc b/chromium/components/exo/wayland/server.cc
index 36441f2ee63..9890dd5e6bd 100644
--- a/chromium/components/exo/wayland/server.cc
+++ b/chromium/components/exo/wayland/server.cc
@@ -9,6 +9,7 @@
#include <gaming-input-unstable-v2-server-protocol.h>
#include <grp.h>
#include <keyboard-configuration-unstable-v1-server-protocol.h>
+#include <keyboard-extension-unstable-v1-server-protocol.h>
#include <linux/input.h>
#include <presentation-time-server-protocol.h>
#include <remote-shell-unstable-v1-server-protocol.h>
@@ -32,6 +33,7 @@
#include <utility>
#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
#include "ash/public/interfaces/window_pin_type.mojom.h"
#include "ash/shell.h"
#include "base/bind.h"
@@ -47,6 +49,12 @@
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/exo/buffer.h"
+#include "components/exo/data_device.h"
+#include "components/exo/data_device_delegate.h"
+#include "components/exo/data_offer.h"
+#include "components/exo/data_offer_delegate.h"
+#include "components/exo/data_source.h"
+#include "components/exo/data_source_delegate.h"
#include "components/exo/display.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat.h"
@@ -54,6 +62,7 @@
#include "components/exo/keyboard.h"
#include "components/exo/keyboard_delegate.h"
#include "components/exo/keyboard_device_configuration_delegate.h"
+#include "components/exo/keyboard_observer.h"
#include "components/exo/notification_surface.h"
#include "components/exo/notification_surface_manager.h"
#include "components/exo/pointer.h"
@@ -85,6 +94,11 @@
#include <drm_fourcc.h>
#include <linux-dmabuf-unstable-v1-server-protocol.h>
#include <wayland-drm-server-protocol.h>
+#if defined(OS_CHROMEOS)
+#include "ui/base/ime/chromeos/ime_keyboard.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h"
+#endif
#endif
#if BUILDFLAG(USE_XKBCOMMON)
@@ -144,6 +158,55 @@ uint32_t NowInMilliseconds() {
return TimeTicksToMilliseconds(base::TimeTicks::Now());
}
+uint32_t WaylandDataDeviceManagerDndAction(DndAction action) {
+ switch (action) {
+ case DndAction::kNone:
+ return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+ case DndAction::kCopy:
+ return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ case DndAction::kMove:
+ return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+ case DndAction::kAsk:
+ return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
+ }
+ NOTREACHED();
+}
+
+uint32_t WaylandDataDeviceManagerDndActions(
+ const base::flat_set<DndAction>& dnd_actions) {
+ uint32_t actions = 0;
+ for (DndAction action : dnd_actions)
+ actions |= WaylandDataDeviceManagerDndAction(action);
+ return actions;
+}
+
+DndAction DataDeviceManagerDndAction(uint32_t value) {
+ switch (value) {
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
+ return DndAction::kNone;
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY:
+ return DndAction::kCopy;
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE:
+ return DndAction::kMove;
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
+ return DndAction::kAsk;
+ default:
+ NOTREACHED();
+ return DndAction::kNone;
+ }
+}
+
+base::flat_set<DndAction> DataDeviceManagerDndActions(uint32_t value) {
+ base::flat_set<DndAction> actions;
+ if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+ actions.insert(DndAction::kCopy);
+ if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+ actions.insert(DndAction::kMove);
+ if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+ actions.insert(DndAction::kAsk);
+ return actions;
+}
+
// A property key containing the surface resource that is associated with
// window. If unset, no surface resource is associated with window.
DEFINE_UI_CLASS_PROPERTY_KEY(wl_resource*, kSurfaceResourceKey, nullptr);
@@ -160,19 +223,22 @@ DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasSecurityKey, false);
// associated with window.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasBlendingKey, false);
-// A property key containing a boolean set to true whether the current
-// OnWindowActivated invocation should be ignored. The defualt is true
-// to ignore the activation event originated by creation.
-DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIgnoreWindowActivated, true);
-
// A property key containing a boolean set to true if the stylus_tool
// object is associated with a window.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSurfaceHasStylusToolKey, false);
+// A property key containing the data offer resource that is associated with
+// data offer object.
+DEFINE_UI_CLASS_PROPERTY_KEY(wl_resource*, kDataOfferResourceKey, nullptr);
+
wl_resource* GetSurfaceResource(Surface* surface) {
return surface->GetProperty(kSurfaceResourceKey);
}
+wl_resource* GetDataOfferResource(const DataOffer* data_offer) {
+ return data_offer->GetProperty(kDataOfferResourceKey);
+}
+
////////////////////////////////////////////////////////////////////////////////
// wl_buffer_interface:
@@ -653,19 +719,55 @@ void linux_buffer_params_add(wl_client* client,
}
}
-void linux_buffer_params_create(wl_client* client,
- wl_resource* resource,
- int32_t width,
- int32_t height,
- uint32_t format,
- uint32_t flags) {
+bool ValidateLinuxBufferParams(wl_resource* resource,
+ int32_t width,
+ int32_t height,
+ gfx::BufferFormat format,
+ uint32_t flags) {
if (width <= 0 || height <= 0) {
wl_resource_post_error(resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
"invalid width or height");
- return;
+ return false;
+ }
+
+ if (flags & (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT |
+ ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED)) {
+ wl_resource_post_error(resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "flags not supported");
+ return false;
+ }
+
+ LinuxBufferParams* linux_buffer_params =
+ GetUserDataAs<LinuxBufferParams>(resource);
+ size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format);
+
+ for (uint32_t i = 0; i < num_planes; ++i) {
+ auto plane_it = linux_buffer_params->planes.find(i);
+ if (plane_it == linux_buffer_params->planes.end()) {
+ wl_resource_post_error(resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "missing a plane");
+ return false;
+ }
+ }
+
+ if (linux_buffer_params->planes.size() != num_planes) {
+ wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
+ "plane idx out of bounds");
+ return false;
}
+ return true;
+}
+
+void linux_buffer_params_create(wl_client* client,
+ wl_resource* resource,
+ int32_t width,
+ int32_t height,
+ uint32_t format,
+ uint32_t flags) {
const auto* supported_format = std::find_if(
std::begin(dmabuf_supported_formats), std::end(dmabuf_supported_formats),
[format](const dmabuf_supported_format& supported_format) {
@@ -678,13 +780,9 @@ void linux_buffer_params_create(wl_client* client,
return;
}
- if (flags & (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT |
- ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED)) {
- wl_resource_post_error(resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
- "flags not supported");
+ if (!ValidateLinuxBufferParams(resource, width, height,
+ supported_format->buffer_format, flags))
return;
- }
LinuxBufferParams* linux_buffer_params =
GetUserDataAs<LinuxBufferParams>(resource);
@@ -692,23 +790,11 @@ void linux_buffer_params_create(wl_client* client,
size_t num_planes =
gfx::NumberOfPlanesForBufferFormat(supported_format->buffer_format);
- if (linux_buffer_params->planes.size() != num_planes) {
- wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
- "plane idx out of bounds");
- return;
- }
-
std::vector<gfx::NativePixmapPlane> planes;
std::vector<base::ScopedFD> fds;
for (uint32_t i = 0; i < num_planes; ++i) {
auto plane_it = linux_buffer_params->planes.find(i);
- if (plane_it == linux_buffer_params->planes.end()) {
- wl_resource_post_error(resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
- "missing a plane");
- return;
- }
LinuxBufferParams::Plane& plane = plane_it->second;
planes.emplace_back(plane.stride, plane.offset, 0, 0);
fds.push_back(std::move(plane.fd));
@@ -734,10 +820,71 @@ void linux_buffer_params_create(wl_client* client,
zwp_linux_buffer_params_v1_send_created(resource, buffer_resource);
}
+void linux_buffer_params_create_immed(wl_client* client,
+ wl_resource* resource,
+ uint32_t buffer_id,
+ int32_t width,
+ int32_t height,
+ uint32_t format,
+ uint32_t flags) {
+ const auto* supported_format = std::find_if(
+ std::begin(dmabuf_supported_formats), std::end(dmabuf_supported_formats),
+ [format](const dmabuf_supported_format& supported_format) {
+ return supported_format.dmabuf_format == format;
+ });
+ if (supported_format == std::end(dmabuf_supported_formats)) {
+ wl_resource_post_error(resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
+ "format not supported");
+ return;
+ }
+
+ if (!ValidateLinuxBufferParams(resource, width, height,
+ supported_format->buffer_format, flags))
+ return;
+
+ LinuxBufferParams* linux_buffer_params =
+ GetUserDataAs<LinuxBufferParams>(resource);
+
+ size_t num_planes =
+ gfx::NumberOfPlanesForBufferFormat(supported_format->buffer_format);
+
+ std::vector<gfx::NativePixmapPlane> planes;
+ std::vector<base::ScopedFD> fds;
+
+ for (uint32_t i = 0; i < num_planes; ++i) {
+ auto plane_it = linux_buffer_params->planes.find(i);
+ LinuxBufferParams::Plane& plane = plane_it->second;
+ planes.emplace_back(plane.stride, plane.offset, 0, 0);
+ fds.push_back(std::move(plane.fd));
+ }
+
+ std::unique_ptr<Buffer> buffer =
+ linux_buffer_params->display->CreateLinuxDMABufBuffer(
+ gfx::Size(width, height), supported_format->buffer_format, planes,
+ std::move(fds));
+ if (!buffer) {
+ // On import failure in case of a create_immed request, the protocol
+ // allows us to raise a fatal error from zwp_linux_dmabuf_v1 version 2+.
+ wl_resource_post_error(resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
+ "dmabuf import failed");
+ return;
+ }
+
+ wl_resource* buffer_resource =
+ wl_resource_create(client, &wl_buffer_interface, 1, buffer_id);
+
+ buffer->set_release_callback(base::Bind(&HandleBufferReleaseCallback,
+ base::Unretained(buffer_resource)));
+
+ SetImplementation(buffer_resource, &buffer_implementation, std::move(buffer));
+}
+
const struct zwp_linux_buffer_params_v1_interface
- linux_buffer_params_implementation = {linux_buffer_params_destroy,
- linux_buffer_params_add,
- linux_buffer_params_create};
+ linux_buffer_params_implementation = {
+ linux_buffer_params_destroy, linux_buffer_params_add,
+ linux_buffer_params_create, linux_buffer_params_create_immed};
////////////////////////////////////////////////////////////////////////////////
// linux_dmabuf_interface:
@@ -753,7 +900,7 @@ void linux_dmabuf_create_params(wl_client* client,
base::MakeUnique<LinuxBufferParams>(GetUserDataAs<Display>(resource));
wl_resource* linux_buffer_params_resource =
- wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, 1, id);
+ wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, 2, id);
SetImplementation(linux_buffer_params_resource,
&linux_buffer_params_implementation,
@@ -768,7 +915,7 @@ void bind_linux_dmabuf(wl_client* client,
uint32_t version,
uint32_t id) {
wl_resource* resource =
- wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, 1, id);
+ wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, version, id);
wl_resource_set_implementation(resource, &linux_dmabuf_implementation, data,
nullptr);
@@ -1896,6 +2043,15 @@ void remote_surface_set_window_geometry(wl_client* client,
gfx::Rect(x, y, width, height));
}
+void remote_surface_set_orientation(wl_client* client,
+ wl_resource* resource,
+ int32_t orientation) {
+ GetUserDataAs<ShellSurface>(resource)->SetOrientation(
+ orientation == ZCR_REMOTE_SURFACE_V1_ORIENTATION_PORTRAIT
+ ? Orientation::PORTRAIT
+ : Orientation::LANDSCAPE);
+}
+
void remote_surface_set_scale(wl_client* client,
wl_resource* resource,
wl_fixed_t scale) {
@@ -1938,13 +2094,7 @@ void remote_surface_activate(wl_client* client,
wl_resource* resource,
uint32_t serial) {
ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
- aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
-
- // Activation on Aura is synchronous, so activation callbacks will be called
- // before the flag is reset.
- window->SetProperty(kIgnoreWindowActivated, true);
shell_surface->Activate();
- window->SetProperty(kIgnoreWindowActivated, false);
}
void remote_surface_maximize(wl_client* client, wl_resource* resource) {
@@ -2027,6 +2177,16 @@ void remote_surface_move(wl_client* client, wl_resource* resource) {
GetUserDataAs<ShellSurface>(resource)->Move();
}
+void remote_surface_set_window_type(wl_client* client,
+ wl_resource* resource,
+ uint32_t type) {
+ if (type == ZCR_REMOTE_SURFACE_V1_WINDOW_TYPE_SYSTEM_UI) {
+ auto* widget = GetUserDataAs<ShellSurface>(resource)->GetWidget();
+ if (widget)
+ widget->GetNativeWindow()->SetProperty(ash::kShowInOverviewKey, false);
+ }
+}
+
const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
remote_surface_destroy,
remote_surface_set_app_id,
@@ -2051,7 +2211,9 @@ const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
remote_surface_set_always_on_top,
remote_surface_unset_always_on_top,
remote_surface_ack_configure,
- remote_surface_move};
+ remote_surface_move,
+ remote_surface_set_orientation,
+ remote_surface_set_window_type};
////////////////////////////////////////////////////////////////////////////////
// notification_surface_interface:
@@ -2068,7 +2230,7 @@ const struct zcr_notification_surface_v1_interface
// Implements remote shell interface and monitors workspace state needed
// for the remote shell interface.
-class WaylandRemoteShell : public WMHelper::MaximizeModeObserver,
+class WaylandRemoteShell : public WMHelper::TabletModeObserver,
public WMHelper::ActivationObserver,
public display::DisplayObserver {
public:
@@ -2077,11 +2239,11 @@ class WaylandRemoteShell : public WMHelper::MaximizeModeObserver,
remote_shell_resource_(remote_shell_resource),
weak_ptr_factory_(this) {
auto* helper = WMHelper::GetInstance();
- helper->AddMaximizeModeObserver(this);
+ helper->AddTabletModeObserver(this);
helper->AddActivationObserver(this);
display::Screen::GetScreen()->AddObserver(this);
- layout_mode_ = helper->IsMaximizeModeWindowManagerEnabled()
+ layout_mode_ = helper->IsTabletModeWindowManagerEnabled()
? ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_TABLET
: ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_WINDOWED;
@@ -2090,7 +2252,7 @@ class WaylandRemoteShell : public WMHelper::MaximizeModeObserver,
}
~WaylandRemoteShell() override {
auto* helper = WMHelper::GetInstance();
- helper->RemoveMaximizeModeObserver(this);
+ helper->RemoveTabletModeObserver(this);
helper->RemoveActivationObserver(this);
display::Screen::GetScreen()->RemoveObserver(this);
}
@@ -2106,8 +2268,8 @@ class WaylandRemoteShell : public WMHelper::MaximizeModeObserver,
std::unique_ptr<NotificationSurface> CreateNotificationSurface(
Surface* surface,
- const std::string& notification_id) {
- return display_->CreateNotificationSurface(surface, notification_id);
+ const std::string& notification_key) {
+ return display_->CreateNotificationSurface(surface, notification_key);
}
// Overridden from display::DisplayObserver:
@@ -2137,30 +2299,20 @@ class WaylandRemoteShell : public WMHelper::MaximizeModeObserver,
}
}
- // Overridden from WMHelper::MaximizeModeObserver:
- void OnMaximizeModeStarted() override {
+ // Overridden from WMHelper::TabletModeObserver:
+ void OnTabletModeStarted() override {
layout_mode_ = ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_TABLET;
ScheduleSendDisplayMetrics(kConfigureDelayAfterLayoutSwitchMs);
}
- void OnMaximizeModeEnding() override {
+ void OnTabletModeEnding() override {
layout_mode_ = ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_WINDOWED;
ScheduleSendDisplayMetrics(kConfigureDelayAfterLayoutSwitchMs);
}
- void OnMaximizeModeEnded() override {}
+ void OnTabletModeEnded() override {}
// Overridden from WMHelper::ActivationObserver:
void OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) override {
- // If the origin of activation is Wayland client, then assume it's been
- // already activated on the client side, so do not notify about the
- // activation. It means that zcr_remote_shell_v1_send_activated is used
- // only to notify about activations originating in Aura.
- if (gained_active && ShellSurface::GetMainSurface(gained_active) &&
- gained_active->GetProperty(kIgnoreWindowActivated)) {
- gained_active->SetProperty(kIgnoreWindowActivated, false);
- return;
- }
-
SendActivated(gained_active, lost_active);
}
@@ -2375,7 +2527,7 @@ void remote_shell_get_notification_surface(wl_client* client,
wl_resource* resource,
uint32_t id,
wl_resource* surface,
- const char* notification_id) {
+ const char* notification_key) {
if (GetUserDataAs<Surface>(surface)->HasSurfaceDelegate()) {
wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE,
"surface has already been assigned a role");
@@ -2384,11 +2536,11 @@ void remote_shell_get_notification_surface(wl_client* client,
std::unique_ptr<NotificationSurface> notification_surface =
GetUserDataAs<WaylandRemoteShell>(resource)->CreateNotificationSurface(
- GetUserDataAs<Surface>(surface), std::string(notification_id));
+ GetUserDataAs<Surface>(surface), std::string(notification_key));
if (!notification_surface) {
wl_resource_post_error(resource,
- ZCR_REMOTE_SHELL_V1_ERROR_INVALID_NOTIFICATION_ID,
- "invalid notification id");
+ ZCR_REMOTE_SHELL_V1_ERROR_INVALID_NOTIFICATION_KEY,
+ "invalid notification key");
return;
}
@@ -2404,7 +2556,7 @@ const struct zcr_remote_shell_v1_interface remote_shell_implementation = {
remote_shell_destroy, remote_shell_get_remote_surface,
remote_shell_get_notification_surface};
-const uint32_t remote_shell_version = 5;
+const uint32_t remote_shell_version = 7;
void bind_remote_shell(wl_client* client,
void* data,
@@ -2532,26 +2684,208 @@ void bind_vsync_feedback(wl_client* client,
}
////////////////////////////////////////////////////////////////////////////////
+// wl_data_source_interface:
+
+class WaylandDataSourceDelegate : public DataSourceDelegate {
+ public:
+ explicit WaylandDataSourceDelegate(wl_resource* source)
+ : data_source_resource_(source) {}
+
+ // Overridden from DataSourceDelegate:
+ void OnDataSourceDestroying(DataSource* device) override { delete this; }
+ void OnTarget(const std::string& mime_type) override {
+ wl_data_source_send_target(data_source_resource_, mime_type.c_str());
+ }
+ void OnSend(const std::string& mime_type, base::ScopedFD fd) override {
+ wl_data_source_send_send(data_source_resource_, mime_type.c_str(),
+ fd.get());
+ }
+ void OnCancelled() override {
+ wl_data_source_send_cancelled(data_source_resource_);
+ }
+ void OnDndDropPerformed() override {
+ wl_data_source_send_dnd_drop_performed(data_source_resource_);
+ }
+ void OnDndFinished() override {
+ wl_data_source_send_dnd_finished(data_source_resource_);
+ }
+ void OnAction(DndAction dnd_action) override {
+ wl_data_source_send_action(data_source_resource_,
+ WaylandDataDeviceManagerDndAction(dnd_action));
+ }
+
+ private:
+ wl_resource* const data_source_resource_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaylandDataSourceDelegate);
+};
+
+void data_source_offer(wl_client* client,
+ wl_resource* resource,
+ const char* mime_type) {
+ GetUserDataAs<DataSource>(resource)->Offer(mime_type);
+}
+
+void data_source_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void data_source_set_actions(wl_client* client,
+ wl_resource* resource,
+ uint32_t dnd_actions) {
+ GetUserDataAs<DataSource>(resource)->SetActions(
+ DataDeviceManagerDndActions(dnd_actions));
+}
+
+const struct wl_data_source_interface data_source_implementation = {
+ data_source_offer, data_source_destroy, data_source_set_actions};
+
+////////////////////////////////////////////////////////////////////////////////
+// wl_data_offer_interface:
+
+class WaylandDataOfferDelegate : public DataOfferDelegate {
+ public:
+ explicit WaylandDataOfferDelegate(wl_resource* offer)
+ : data_offer_resource_(offer) {}
+
+ // Overridden from DataOfferDelegate:
+ void OnDataOfferDestroying(DataOffer* device) override { delete this; }
+ void OnOffer(const std::string& mime_type) override {
+ wl_data_offer_send_offer(data_offer_resource_, mime_type.c_str());
+ }
+ void OnSourceActions(
+ const base::flat_set<DndAction>& source_actions) override {
+ wl_data_offer_send_source_actions(
+ data_offer_resource_,
+ WaylandDataDeviceManagerDndActions(source_actions));
+ }
+ void OnAction(DndAction action) override {
+ wl_data_offer_send_action(data_offer_resource_,
+ WaylandDataDeviceManagerDndAction(action));
+ }
+
+ private:
+ wl_resource* const data_offer_resource_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaylandDataOfferDelegate);
+};
+
+void data_offer_accept(wl_client* client,
+ wl_resource* resource,
+ uint32_t serial,
+ const char* mime_type) {
+ GetUserDataAs<DataOffer>(resource)->Accept(mime_type);
+}
+
+void data_offer_receive(wl_client* client,
+ wl_resource* resource,
+ const char* mime_type,
+ int fd) {
+ GetUserDataAs<DataOffer>(resource)->Receive(mime_type, base::ScopedFD(fd));
+}
+
+void data_offer_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void data_offer_finish(wl_client* client, wl_resource* resource) {
+ GetUserDataAs<DataOffer>(resource)->Finish();
+}
+
+void data_offer_set_actions(wl_client* client,
+ wl_resource* resource,
+ uint32_t dnd_actions,
+ uint32_t preferred_action) {
+ GetUserDataAs<DataOffer>(resource)->SetActions(
+ DataDeviceManagerDndActions(dnd_actions),
+ DataDeviceManagerDndAction(preferred_action));
+}
+
+const struct wl_data_offer_interface data_offer_implementation = {
+ data_offer_accept, data_offer_receive, data_offer_finish,
+ data_offer_destroy, data_offer_set_actions};
+
+////////////////////////////////////////////////////////////////////////////////
// wl_data_device_interface:
+class WaylandDataDeviceDelegate : public DataDeviceDelegate {
+ public:
+ WaylandDataDeviceDelegate(wl_client* client, wl_resource* device_resource)
+ : client_(client), data_device_resource_(device_resource) {}
+
+ // Overridden from DataDeviceDelegate:
+ void OnDataDeviceDestroying(DataDevice* device) override { delete this; }
+ bool CanAcceptDataEventsForSurface(Surface* surface) override {
+ return surface &&
+ wl_resource_get_client(GetSurfaceResource(surface)) == client_;
+ }
+ DataOffer* OnDataOffer() override {
+ wl_resource* data_offer_resource =
+ wl_resource_create(client_, &wl_data_offer_interface, 1, 0);
+ std::unique_ptr<DataOffer> data_offer = base::MakeUnique<DataOffer>(
+ new WaylandDataOfferDelegate(data_offer_resource));
+ data_offer->SetProperty(kDataOfferResourceKey, data_offer_resource);
+ SetImplementation(data_offer_resource, &data_offer_implementation,
+ std::move(data_offer));
+
+ wl_data_device_send_data_offer(data_device_resource_, data_offer_resource);
+
+ return GetUserDataAs<DataOffer>(data_offer_resource);
+ }
+ void OnEnter(Surface* surface,
+ const gfx::PointF& point,
+ const DataOffer& data_offer) override {
+ wl_data_device_send_enter(
+ data_device_resource_,
+ wl_display_next_serial(wl_client_get_display(client_)),
+ GetSurfaceResource(surface), wl_fixed_from_double(point.x()),
+ wl_fixed_from_double(point.y()), GetDataOfferResource(&data_offer));
+ }
+ void OnLeave() override { wl_data_device_send_leave(data_device_resource_); }
+ void OnMotion(base::TimeTicks time_stamp, const gfx::PointF& point) override {
+ wl_data_device_send_motion(
+ data_device_resource_, TimeTicksToMilliseconds(time_stamp),
+ wl_fixed_from_double(point.x()), wl_fixed_from_double(point.y()));
+ }
+ void OnDrop() override { wl_data_device_send_drop(data_device_resource_); }
+ void OnSelection(const DataOffer& data_offer) override {
+ wl_data_device_send_selection(data_device_resource_,
+ GetDataOfferResource(&data_offer));
+ }
+
+ private:
+ wl_client* const client_;
+ wl_resource* const data_device_resource_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceDelegate);
+};
+
void data_device_start_drag(wl_client* client,
wl_resource* resource,
wl_resource* source_resource,
wl_resource* origin_resource,
wl_resource* icon_resource,
uint32_t serial) {
- NOTIMPLEMENTED();
+ GetUserDataAs<DataDevice>(resource)->StartDrag(
+ source_resource ? GetUserDataAs<DataSource>(source_resource) : nullptr,
+ GetUserDataAs<Surface>(origin_resource),
+ icon_resource ? GetUserDataAs<Surface>(icon_resource) : nullptr, serial);
}
void data_device_set_selection(wl_client* client,
wl_resource* resource,
wl_resource* data_source,
uint32_t serial) {
- NOTIMPLEMENTED();
+ GetUserDataAs<DataDevice>(resource)->SetSelection(
+ GetUserDataAs<DataSource>(data_source), serial);
+}
+
+void data_device_release(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
}
const struct wl_data_device_interface data_device_implementation = {
- data_device_start_drag, data_device_set_selection};
+ data_device_start_drag, data_device_set_selection, data_device_release};
////////////////////////////////////////////////////////////////////////////////
// wl_data_device_manager_interface:
@@ -2559,18 +2893,23 @@ const struct wl_data_device_interface data_device_implementation = {
void data_device_manager_create_data_source(wl_client* client,
wl_resource* resource,
uint32_t id) {
- NOTIMPLEMENTED();
+ wl_resource* data_source_resource =
+ wl_resource_create(client, &wl_data_device_interface, 1, id);
+ SetImplementation(data_source_resource, &data_source_implementation,
+ base::MakeUnique<DataSource>(
+ new WaylandDataSourceDelegate(data_source_resource)));
}
void data_device_manager_get_data_device(wl_client* client,
wl_resource* resource,
uint32_t id,
wl_resource* seat_resource) {
+ Display* display = GetUserDataAs<Display>(resource);
wl_resource* data_device_resource =
wl_resource_create(client, &wl_data_device_interface, 1, id);
-
- wl_resource_set_implementation(data_device_resource,
- &data_device_implementation, nullptr, nullptr);
+ SetImplementation(data_device_resource, &data_device_implementation,
+ display->CreateDataDevice(new WaylandDataDeviceDelegate(
+ client, data_device_resource)));
}
const struct wl_data_device_manager_interface
@@ -2584,7 +2923,6 @@ void bind_data_device_manager(wl_client* client,
uint32_t id) {
wl_resource* resource =
wl_resource_create(client, &wl_data_device_manager_interface, 1, id);
-
wl_resource_set_implementation(resource, &data_device_manager_implementation,
data, nullptr);
}
@@ -2737,29 +3075,37 @@ const struct wl_pointer_interface pointer_implementation = {pointer_set_cursor,
// Keyboard delegate class that accepts events for surfaces owned by the same
// client as a keyboard resource.
-class WaylandKeyboardDelegate : public KeyboardDelegate {
+class WaylandKeyboardDelegate
+ : public KeyboardDelegate,
+ public KeyboardObserver
+#if defined(USE_OZONE) && defined(OS_CHROMEOS)
+ ,
+ public chromeos::input_method::ImeKeyboard::Observer
+#endif
+{
public:
explicit WaylandKeyboardDelegate(wl_resource* keyboard_resource)
: keyboard_resource_(keyboard_resource),
- xkb_context_(xkb_context_new(XKB_CONTEXT_NO_FLAGS)),
- // TODO(reveman): Keep keymap synchronized with the keymap used by
- // chromium and the host OS.
- xkb_keymap_(xkb_keymap_new_from_names(xkb_context_.get(),
- nullptr,
- XKB_KEYMAP_COMPILE_NO_FLAGS)),
- xkb_state_(xkb_state_new(xkb_keymap_.get())) {
- std::unique_ptr<char, base::FreeDeleter> keymap_string(
- xkb_keymap_get_as_string(xkb_keymap_.get(), XKB_KEYMAP_FORMAT_TEXT_V1));
- DCHECK(keymap_string.get());
- size_t keymap_size = strlen(keymap_string.get()) + 1;
- base::SharedMemory shared_keymap;
- bool rv = shared_keymap.CreateAndMapAnonymous(keymap_size);
- DCHECK(rv);
- memcpy(shared_keymap.memory(), keymap_string.get(), keymap_size);
- wl_keyboard_send_keymap(keyboard_resource_,
- WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
- shared_keymap.handle().GetHandle(), keymap_size);
+ xkb_context_(xkb_context_new(XKB_CONTEXT_NO_FLAGS)) {
+#if defined(USE_OZONE) && defined(OS_CHROMEOS)
+ chromeos::input_method::ImeKeyboard* keyboard =
+ chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
+ if (keyboard) {
+ keyboard->AddObserver(this);
+ SendNamedLayout(keyboard->GetCurrentKeyboardLayoutName());
+ }
+#else
+ SendLayout(nullptr);
+#endif
+ }
+#if defined(USE_OZONE) && defined(OS_CHROMEOS)
+ ~WaylandKeyboardDelegate() override {
+ chromeos::input_method::ImeKeyboard* keyboard =
+ chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
+ if (keyboard)
+ keyboard->RemoveObserver(this);
}
+#endif
// Overridden from KeyboardDelegate:
void OnKeyboardDestroying(Keyboard* keyboard) override { delete this; }
@@ -2793,14 +3139,16 @@ class WaylandKeyboardDelegate : public KeyboardDelegate {
wl_keyboard_send_leave(keyboard_resource_, next_serial(), surface_resource);
wl_client_flush(client());
}
- void OnKeyboardKey(base::TimeTicks time_stamp,
- ui::DomCode key,
- bool pressed) override {
- wl_keyboard_send_key(keyboard_resource_, next_serial(),
+ uint32_t OnKeyboardKey(base::TimeTicks time_stamp,
+ ui::DomCode key,
+ bool pressed) override {
+ uint32_t serial = next_serial();
+ wl_keyboard_send_key(keyboard_resource_, serial,
TimeTicksToMilliseconds(time_stamp), DomCodeToKey(key),
pressed ? WL_KEYBOARD_KEY_STATE_PRESSED
: WL_KEYBOARD_KEY_STATE_RELEASED);
wl_client_flush(client());
+ return serial;
}
void OnKeyboardModifiers(int modifier_flags) override {
xkb_state_update_mask(xkb_state_.get(),
@@ -2816,6 +3164,14 @@ class WaylandKeyboardDelegate : public KeyboardDelegate {
wl_client_flush(client());
}
+#if defined(USE_OZONE) && defined(OS_CHROMEOS)
+ // Overridden from input_method::ImeKeyboard::Observer:
+ void OnCapsLockChanged(bool enabled) override {}
+ void OnLayoutChanging(const std::string& layout_name) override {
+ SendNamedLayout(layout_name);
+ }
+#endif
+
private:
// Returns the corresponding key given a dom code.
uint32_t DomCodeToKey(ui::DomCode code) const {
@@ -2853,6 +3209,40 @@ class WaylandKeyboardDelegate : public KeyboardDelegate {
return xkb_modifiers;
}
+
+#if defined(USE_OZONE) && defined(OS_CHROMEOS)
+ // Send the named keyboard layout to the client.
+ void SendNamedLayout(const std::string& layout_name) {
+ std::string layout_id, layout_variant;
+ ui::XkbKeyboardLayoutEngine::ParseLayoutName(layout_name, &layout_id,
+ &layout_variant);
+ xkb_rule_names names = {.rules = nullptr,
+ .model = "pc101",
+ .layout = layout_id.c_str(),
+ .variant = layout_variant.c_str(),
+ .options = ""};
+ SendLayout(&names);
+ }
+#endif
+
+ // Send the keyboard layout named by XKB rules to the client.
+ void SendLayout(const xkb_rule_names* names) {
+ xkb_keymap_.reset(xkb_keymap_new_from_names(xkb_context_.get(), names,
+ XKB_KEYMAP_COMPILE_NO_FLAGS));
+ xkb_state_.reset(xkb_state_new(xkb_keymap_.get()));
+ std::unique_ptr<char, base::FreeDeleter> keymap_string(
+ xkb_keymap_get_as_string(xkb_keymap_.get(), XKB_KEYMAP_FORMAT_TEXT_V1));
+ DCHECK(keymap_string.get());
+ size_t keymap_size = strlen(keymap_string.get()) + 1;
+ base::SharedMemory shared_keymap;
+ bool rv = shared_keymap.CreateAndMapAnonymous(keymap_size);
+ DCHECK(rv);
+ memcpy(shared_keymap.memory(), keymap_string.get(), keymap_size);
+ wl_keyboard_send_keymap(keyboard_resource_,
+ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ shared_keymap.handle().GetHandle(), keymap_size);
+ }
+
// The client who own this keyboard instance.
wl_client* client() const {
return wl_resource_get_client(keyboard_resource_);
@@ -2980,9 +3370,12 @@ void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) {
wl_resource* keyboard_resource =
wl_resource_create(client, &wl_keyboard_interface, version, id);
+ WaylandKeyboardDelegate* delegate =
+ new WaylandKeyboardDelegate(keyboard_resource);
+ std::unique_ptr<Keyboard> keyboard = base::MakeUnique<Keyboard>(delegate);
+ keyboard->AddObserver(delegate);
SetImplementation(keyboard_resource, &keyboard_implementation,
- base::MakeUnique<Keyboard>(
- new WaylandKeyboardDelegate(keyboard_resource)));
+ std::move(keyboard));
// TODO(reveman): Keep repeat info synchronized with chromium and the host OS.
if (version >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
@@ -3768,16 +4161,20 @@ void bind_stylus_v1_DEPRECATED(wl_client* client,
// keyboard_device_configuration interface:
class WaylandKeyboardDeviceConfigurationDelegate
- : public KeyboardDeviceConfigurationDelegate {
+ : public KeyboardDeviceConfigurationDelegate,
+ public KeyboardObserver {
public:
WaylandKeyboardDeviceConfigurationDelegate(wl_resource* resource,
Keyboard* keyboard)
: resource_(resource), keyboard_(keyboard) {
keyboard_->SetDeviceConfigurationDelegate(this);
+ keyboard_->AddObserver(this);
}
~WaylandKeyboardDeviceConfigurationDelegate() override {
- if (keyboard_)
+ if (keyboard_) {
keyboard_->SetDeviceConfigurationDelegate(nullptr);
+ keyboard_->RemoveObserver(this);
+ }
}
void OnKeyboardDestroying(Keyboard* keyboard) override {
@@ -3930,6 +4327,93 @@ void bind_stylus_tools(wl_client* client,
nullptr);
}
+////////////////////////////////////////////////////////////////////////////////
+// extended_keyboard interface:
+
+class WaylandExtendedKeyboardImpl : public KeyboardObserver {
+ public:
+ WaylandExtendedKeyboardImpl(Keyboard* keyboard) : keyboard_(keyboard) {
+ keyboard_->AddObserver(this);
+ keyboard_->SetNeedKeyboardKeyAcks(true);
+ }
+ ~WaylandExtendedKeyboardImpl() override {
+ if (keyboard_) {
+ keyboard_->RemoveObserver(this);
+ keyboard_->SetNeedKeyboardKeyAcks(false);
+ }
+ }
+
+ // Overridden from KeyboardObserver:
+ void OnKeyboardDestroying(Keyboard* keyboard) override {
+ DCHECK(keyboard_ == keyboard);
+ keyboard_ = nullptr;
+ }
+
+ void AckKeyboardKey(uint32_t serial, bool handled) {
+ if (keyboard_)
+ keyboard_->AckKeyboardKey(serial, handled);
+ }
+
+ private:
+ Keyboard* keyboard_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaylandExtendedKeyboardImpl);
+};
+
+void extended_keyboard_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void extended_keyboard_ack_key(wl_client* client,
+ wl_resource* resource,
+ uint32_t serial,
+ uint32_t handled_state) {
+ GetUserDataAs<WaylandExtendedKeyboardImpl>(resource)->AckKeyboardKey(
+ serial, handled_state == ZCR_EXTENDED_KEYBOARD_V1_HANDLED_STATE_HANDLED);
+}
+
+const struct zcr_extended_keyboard_v1_interface
+ extended_keyboard_implementation = {extended_keyboard_destroy,
+ extended_keyboard_ack_key};
+
+////////////////////////////////////////////////////////////////////////////////
+// keyboard_extension interface:
+
+void keyboard_extension_get_extended_keyboard(wl_client* client,
+ wl_resource* resource,
+ uint32_t id,
+ wl_resource* keyboard_resource) {
+ Keyboard* keyboard = GetUserDataAs<Keyboard>(keyboard_resource);
+ if (keyboard->AreKeyboardKeyAcksNeeded()) {
+ wl_resource_post_error(
+ resource, ZCR_KEYBOARD_EXTENSION_V1_ERROR_EXTENDED_KEYBOARD_EXISTS,
+ "keyboard has already been associated with a extended_keyboard object");
+ return;
+ }
+
+ wl_resource* extended_keyboard_resource =
+ wl_resource_create(client, &zcr_extended_keyboard_v1_interface, 1, id);
+
+ SetImplementation(extended_keyboard_resource,
+ &extended_keyboard_implementation,
+ base::MakeUnique<WaylandExtendedKeyboardImpl>(keyboard));
+}
+
+const struct zcr_keyboard_extension_v1_interface
+ keyboard_extension_implementation = {
+ keyboard_extension_get_extended_keyboard};
+
+void bind_keyboard_extension(wl_client* client,
+ void* data,
+ uint32_t version,
+ uint32_t id) {
+ wl_resource* resource = wl_resource_create(
+ client, &zcr_keyboard_extension_v1_interface, version, id);
+
+ wl_resource_set_implementation(resource, &keyboard_extension_implementation,
+ data, nullptr);
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -3943,7 +4427,7 @@ Server::Server(Display* display)
#if defined(USE_OZONE)
wl_global_create(wl_display_.get(), &wl_drm_interface, drm_version, display_,
bind_drm);
- wl_global_create(wl_display_.get(), &zwp_linux_dmabuf_v1_interface, 1,
+ wl_global_create(wl_display_.get(), &zwp_linux_dmabuf_v1_interface, 2,
display_, bind_linux_dmabuf);
#endif
wl_global_create(wl_display_.get(), &wl_subcompositor_interface, 1, display_,
@@ -3984,6 +4468,8 @@ Server::Server(Display* display)
2, display_, bind_keyboard_configuration);
wl_global_create(wl_display_.get(), &zcr_stylus_tools_v1_interface, 1,
display_, bind_stylus_tools);
+ wl_global_create(wl_display_.get(), &zcr_keyboard_extension_v1_interface, 1,
+ display_, bind_keyboard_extension);
}
Server::~Server() {}
diff --git a/chromium/components/exo/wayland/server_unittest.cc b/chromium/components/exo/wayland/server_unittest.cc
index 9ab42777723..77152fba174 100644
--- a/chromium/components/exo/wayland/server_unittest.cc
+++ b/chromium/components/exo/wayland/server_unittest.cc
@@ -23,7 +23,7 @@ namespace exo {
namespace wayland {
namespace {
-base::StaticAtomicSequenceNumber g_next_socket_id;
+base::AtomicSequenceNumber g_next_socket_id;
std::string GetUniqueSocketName() {
return base::StringPrintf("wayland-test-%d-%d", base::GetCurrentProcId(),
diff --git a/chromium/components/exo/wm_helper.cc b/chromium/components/exo/wm_helper.cc
index a24aa2f837e..ca2882f64d5 100644
--- a/chromium/components/exo/wm_helper.cc
+++ b/chromium/components/exo/wm_helper.cc
@@ -5,6 +5,8 @@
#include "components/exo/wm_helper.h"
#include "base/memory/ptr_util.h"
+#include "ui/aura/client/drag_drop_delegate.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
namespace exo {
namespace {
@@ -30,6 +32,11 @@ WMHelper* WMHelper::GetInstance() {
return g_instance;
}
+// static
+bool WMHelper::HasInstance() {
+ return !!g_instance;
+}
+
void WMHelper::AddActivationObserver(ActivationObserver* observer) {
activation_observers_.AddObserver(observer);
}
@@ -54,12 +61,12 @@ void WMHelper::RemoveCursorObserver(CursorObserver* observer) {
cursor_observers_.RemoveObserver(observer);
}
-void WMHelper::AddMaximizeModeObserver(MaximizeModeObserver* observer) {
- maximize_mode_observers_.AddObserver(observer);
+void WMHelper::AddTabletModeObserver(TabletModeObserver* observer) {
+ tablet_mode_observers_.AddObserver(observer);
}
-void WMHelper::RemoveMaximizeModeObserver(MaximizeModeObserver* observer) {
- maximize_mode_observers_.RemoveObserver(observer);
+void WMHelper::RemoveTabletModeObserver(TabletModeObserver* observer) {
+ tablet_mode_observers_.RemoveObserver(observer);
}
void WMHelper::AddInputDeviceEventObserver(InputDeviceEventObserver* observer) {
@@ -81,6 +88,22 @@ void WMHelper::RemoveDisplayConfigurationObserver(
display_config_observers_.RemoveObserver(observer);
}
+void WMHelper::AddDragDropObserver(DragDropObserver* observer) {
+ drag_drop_observers_.AddObserver(observer);
+}
+
+void WMHelper::RemoveDragDropObserver(DragDropObserver* observer) {
+ drag_drop_observers_.RemoveObserver(observer);
+}
+
+void WMHelper::SetDragDropDelegate(aura::Window* window) {
+ aura::client::SetDragDropDelegate(window, this);
+}
+
+void WMHelper::ResetDragDropDelegate(aura::Window* window) {
+ aura::client::SetDragDropDelegate(window, nullptr);
+}
+
void WMHelper::NotifyWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
for (ActivationObserver& observer : activation_observers_)
@@ -98,9 +121,9 @@ void WMHelper::NotifyCursorVisibilityChanged(bool is_visible) {
observer.OnCursorVisibilityChanged(is_visible);
}
-void WMHelper::NotifyCursorSetChanged(ui::CursorSetType cursor_set) {
+void WMHelper::NotifyCursorSizeChanged(ui::CursorSize cursor_size) {
for (CursorObserver& observer : cursor_observers_)
- observer.OnCursorSetChanged(cursor_set);
+ observer.OnCursorSizeChanged(cursor_size);
}
void WMHelper::NotifyCursorDisplayChanged(const display::Display& display) {
@@ -108,19 +131,19 @@ void WMHelper::NotifyCursorDisplayChanged(const display::Display& display) {
observer.OnCursorDisplayChanged(display);
}
-void WMHelper::NotifyMaximizeModeStarted() {
- for (MaximizeModeObserver& observer : maximize_mode_observers_)
- observer.OnMaximizeModeStarted();
+void WMHelper::NotifyTabletModeStarted() {
+ for (TabletModeObserver& observer : tablet_mode_observers_)
+ observer.OnTabletModeStarted();
}
-void WMHelper::NotifyMaximizeModeEnding() {
- for (MaximizeModeObserver& observer : maximize_mode_observers_)
- observer.OnMaximizeModeEnding();
+void WMHelper::NotifyTabletModeEnding() {
+ for (TabletModeObserver& observer : tablet_mode_observers_)
+ observer.OnTabletModeEnding();
}
-void WMHelper::NotifyMaximizeModeEnded() {
- for (MaximizeModeObserver& observer : maximize_mode_observers_)
- observer.OnMaximizeModeEnded();
+void WMHelper::NotifyTabletModeEnded() {
+ for (TabletModeObserver& observer : tablet_mode_observers_)
+ observer.OnTabletModeEnded();
}
void WMHelper::NotifyKeyboardDeviceConfigurationChanged() {
@@ -133,4 +156,28 @@ void WMHelper::NotifyDisplayConfigurationChanged() {
observer.OnDisplayConfigurationChanged();
}
+void WMHelper::OnDragEntered(const ui::DropTargetEvent& event) {
+ for (DragDropObserver& observer : drag_drop_observers_)
+ observer.OnDragEntered(event);
+}
+
+int WMHelper::OnDragUpdated(const ui::DropTargetEvent& event) {
+ int valid_operation = ui::DragDropTypes::DRAG_NONE;
+ for (DragDropObserver& observer : drag_drop_observers_)
+ valid_operation = valid_operation | observer.OnDragUpdated(event);
+ return valid_operation;
+}
+
+void WMHelper::OnDragExited() {
+ for (DragDropObserver& observer : drag_drop_observers_)
+ observer.OnDragExited();
+}
+
+int WMHelper::OnPerformDrop(const ui::DropTargetEvent& event) {
+ for (DragDropObserver& observer : drag_drop_observers_)
+ observer.OnPerformDrop(event);
+ // TODO(hirono): Return the correct result instead of always returning
+ // DRAG_MOVE.
+ return ui::DragDropTypes::DRAG_MOVE;
+}
} // namespace exo
diff --git a/chromium/components/exo/wm_helper.h b/chromium/components/exo/wm_helper.h
index d68237c54cc..8a8332d0833 100644
--- a/chromium/components/exo/wm_helper.h
+++ b/chromium/components/exo/wm_helper.h
@@ -5,8 +5,11 @@
#ifndef COMPONENTS_EXO_WM_HELPER_H_
#define COMPONENTS_EXO_WM_HELPER_H_
+#include <memory>
+
#include "base/macros.h"
#include "base/observer_list.h"
+#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/base/cursor/cursor.h"
namespace aura {
@@ -20,12 +23,13 @@ class ManagedDisplayInfo;
namespace ui {
class EventHandler;
+class DropTargetEvent;
}
namespace exo {
// A helper class for accessing WindowManager related features.
-class WMHelper {
+class WMHelper : public aura::client::DragDropDelegate {
public:
class ActivationObserver {
public:
@@ -48,21 +52,21 @@ class WMHelper {
class CursorObserver {
public:
virtual void OnCursorVisibilityChanged(bool is_visible) {}
- virtual void OnCursorSetChanged(ui::CursorSetType cursor_set) {}
+ virtual void OnCursorSizeChanged(ui::CursorSize cursor_size) {}
virtual void OnCursorDisplayChanged(const display::Display& display) {}
protected:
virtual ~CursorObserver() {}
};
- class MaximizeModeObserver {
+ class TabletModeObserver {
public:
- virtual void OnMaximizeModeStarted() = 0;
- virtual void OnMaximizeModeEnding() = 0;
- virtual void OnMaximizeModeEnded() = 0;
+ virtual void OnTabletModeStarted() = 0;
+ virtual void OnTabletModeEnding() = 0;
+ virtual void OnTabletModeEnded() = 0;
protected:
- virtual ~MaximizeModeObserver() {}
+ virtual ~TabletModeObserver() {}
};
class InputDeviceEventObserver {
@@ -81,10 +85,22 @@ class WMHelper {
virtual ~DisplayConfigurationObserver() {}
};
- virtual ~WMHelper();
+ class DragDropObserver {
+ public:
+ virtual void OnDragEntered(const ui::DropTargetEvent& event) = 0;
+ virtual int OnDragUpdated(const ui::DropTargetEvent& event) = 0;
+ virtual void OnDragExited() = 0;
+ virtual int OnPerformDrop(const ui::DropTargetEvent& event) = 0;
+
+ protected:
+ virtual ~DragDropObserver() {}
+ };
+
+ ~WMHelper() override;
static void SetInstance(WMHelper* helper);
static WMHelper* GetInstance();
+ static bool HasInstance();
void AddActivationObserver(ActivationObserver* observer);
void RemoveActivationObserver(ActivationObserver* observer);
@@ -92,27 +108,37 @@ class WMHelper {
void RemoveFocusObserver(FocusObserver* observer);
void AddCursorObserver(CursorObserver* observer);
void RemoveCursorObserver(CursorObserver* observer);
- void AddMaximizeModeObserver(MaximizeModeObserver* observer);
- void RemoveMaximizeModeObserver(MaximizeModeObserver* observer);
+ void AddTabletModeObserver(TabletModeObserver* observer);
+ void RemoveTabletModeObserver(TabletModeObserver* observer);
void AddInputDeviceEventObserver(InputDeviceEventObserver* observer);
void RemoveInputDeviceEventObserver(InputDeviceEventObserver* observer);
void AddDisplayConfigurationObserver(DisplayConfigurationObserver* observer);
void RemoveDisplayConfigurationObserver(
DisplayConfigurationObserver* observer);
+ void AddDragDropObserver(DragDropObserver* observer);
+ void RemoveDragDropObserver(DragDropObserver* observer);
+ void SetDragDropDelegate(aura::Window*);
+ void ResetDragDropDelegate(aura::Window*);
virtual const display::ManagedDisplayInfo& GetDisplayInfo(
int64_t display_id) const = 0;
virtual aura::Window* GetPrimaryDisplayContainer(int container_id) = 0;
virtual aura::Window* GetActiveWindow() const = 0;
virtual aura::Window* GetFocusedWindow() const = 0;
- virtual ui::CursorSetType GetCursorSet() const = 0;
+ virtual ui::CursorSize GetCursorSize() const = 0;
virtual const display::Display& GetCursorDisplay() const = 0;
virtual void AddPreTargetHandler(ui::EventHandler* handler) = 0;
virtual void PrependPreTargetHandler(ui::EventHandler* handler) = 0;
virtual void RemovePreTargetHandler(ui::EventHandler* handler) = 0;
virtual void AddPostTargetHandler(ui::EventHandler* handler) = 0;
virtual void RemovePostTargetHandler(ui::EventHandler* handler) = 0;
- virtual bool IsMaximizeModeWindowManagerEnabled() const = 0;
+ virtual bool IsTabletModeWindowManagerEnabled() const = 0;
+
+ // Overridden from aura::client::DragDropDelegate:
+ void OnDragEntered(const ui::DropTargetEvent& event) override;
+ int OnDragUpdated(const ui::DropTargetEvent& event) override;
+ void OnDragExited() override;
+ int OnPerformDrop(const ui::DropTargetEvent& event) override;
protected:
WMHelper();
@@ -122,11 +148,11 @@ class WMHelper {
void NotifyWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus);
void NotifyCursorVisibilityChanged(bool is_visible);
- void NotifyCursorSetChanged(ui::CursorSetType cursor_set);
+ void NotifyCursorSizeChanged(ui::CursorSize cursor_size);
void NotifyCursorDisplayChanged(const display::Display& display);
- void NotifyMaximizeModeStarted();
- void NotifyMaximizeModeEnding();
- void NotifyMaximizeModeEnded();
+ void NotifyTabletModeStarted();
+ void NotifyTabletModeEnding();
+ void NotifyTabletModeEnded();
void NotifyKeyboardDeviceConfigurationChanged();
void NotifyDisplayConfigurationChanged();
@@ -134,9 +160,10 @@ class WMHelper {
base::ObserverList<ActivationObserver> activation_observers_;
base::ObserverList<FocusObserver> focus_observers_;
base::ObserverList<CursorObserver> cursor_observers_;
- base::ObserverList<MaximizeModeObserver> maximize_mode_observers_;
+ base::ObserverList<TabletModeObserver> tablet_mode_observers_;
base::ObserverList<InputDeviceEventObserver> input_device_event_observers_;
base::ObserverList<DisplayConfigurationObserver> display_config_observers_;
+ base::ObserverList<DragDropObserver> drag_drop_observers_;
DISALLOW_COPY_AND_ASSIGN(WMHelper);
};
diff --git a/chromium/components/exo/wm_helper_ash.cc b/chromium/components/exo/wm_helper_ash.cc
index 23beac2a1e1..1d4caa1bc68 100644
--- a/chromium/components/exo/wm_helper_ash.cc
+++ b/chromium/components/exo/wm_helper_ash.cc
@@ -6,9 +6,8 @@
#include "ash/public/cpp/config.h"
#include "ash/shell.h"
-#include "ash/shell_port.h"
#include "ash/system/tray/system_tray_notifier.h"
-#include "ash/wm/maximize_mode/maximize_mode_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/memory/singleton.h"
#include "ui/aura/client/focus_client.h"
#include "ui/display/manager/display_manager.h"
@@ -21,12 +20,12 @@ namespace exo {
// WMHelperAsh, public:
WMHelperAsh::WMHelperAsh() {
- ash::Shell::Get()->AddShellObserver(this);
+ ash::Shell::Get()->tablet_mode_controller()->AddObserver(this);
ash::Shell::Get()->activation_client()->AddObserver(this);
// TODO(crbug.com/631103): Mushrome doesn't have a cursor manager yet.
- if (ash::ShellPort::Get()->GetAshConfig() != ash::Config::MUS)
+ if (ash::Shell::GetAshConfig() != ash::Config::MUS)
ash::Shell::Get()->cursor_manager()->AddObserver(this);
- ash::ShellPort::Get()->AddDisplayObserver(this);
+ ash::Shell::Get()->window_tree_host_manager()->AddObserver(this);
aura::client::FocusClient* focus_client =
aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
focus_client->AddObserver(this);
@@ -39,12 +38,12 @@ WMHelperAsh::~WMHelperAsh() {
aura::client::FocusClient* focus_client =
aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
focus_client->RemoveObserver(this);
- ash::ShellPort::Get()->RemoveDisplayObserver(this);
+ ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
// TODO(crbug.com/631103): Mushrome doesn't have a cursor manager yet.
- if (ash::ShellPort::Get()->GetAshConfig() != ash::Config::MUS)
+ if (ash::Shell::GetAshConfig() != ash::Config::MUS)
ash::Shell::Get()->cursor_manager()->RemoveObserver(this);
ash::Shell::Get()->activation_client()->RemoveObserver(this);
- ash::Shell::Get()->RemoveShellObserver(this);
+ ash::Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
}
@@ -71,16 +70,16 @@ aura::Window* WMHelperAsh::GetFocusedWindow() const {
return focus_client->GetFocusedWindow();
}
-ui::CursorSetType WMHelperAsh::GetCursorSet() const {
+ui::CursorSize WMHelperAsh::GetCursorSize() const {
// TODO(crbug.com/631103): Mushrome doesn't have a cursor manager yet.
- if (ash::ShellPort::Get()->GetAshConfig() == ash::Config::MUS)
- return ui::CURSOR_SET_NORMAL;
- return ash::Shell::Get()->cursor_manager()->GetCursorSet();
+ if (ash::Shell::GetAshConfig() == ash::Config::MUS)
+ return ui::CursorSize::kNormal;
+ return ash::Shell::Get()->cursor_manager()->GetCursorSize();
}
const display::Display& WMHelperAsh::GetCursorDisplay() const {
// TODO(crbug.com/631103): Mushrome doesn't have a cursor manager yet.
- if (ash::ShellPort::Get()->GetAshConfig() == ash::Config::MUS) {
+ if (ash::Shell::GetAshConfig() == ash::Config::MUS) {
static const display::Display display;
return display;
}
@@ -107,14 +106,14 @@ void WMHelperAsh::RemovePostTargetHandler(ui::EventHandler* handler) {
ash::Shell::Get()->RemovePostTargetHandler(handler);
}
-bool WMHelperAsh::IsMaximizeModeWindowManagerEnabled() const {
+bool WMHelperAsh::IsTabletModeWindowManagerEnabled() const {
return ash::Shell::Get()
- ->maximize_mode_controller()
- ->IsMaximizeModeWindowManagerEnabled();
+ ->tablet_mode_controller()
+ ->IsTabletModeWindowManagerEnabled();
}
void WMHelperAsh::OnWindowActivated(
- aura::client::ActivationChangeObserver::ActivationReason reason,
+ wm::ActivationChangeObserver::ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) {
NotifyWindowActivated(gained_active, lost_active);
@@ -129,24 +128,24 @@ void WMHelperAsh::OnCursorVisibilityChanged(bool is_visible) {
NotifyCursorVisibilityChanged(is_visible);
}
-void WMHelperAsh::OnCursorSetChanged(ui::CursorSetType cursor_set) {
- NotifyCursorSetChanged(cursor_set);
+void WMHelperAsh::OnCursorSizeChanged(ui::CursorSize cursor_size) {
+ NotifyCursorSizeChanged(cursor_size);
}
void WMHelperAsh::OnCursorDisplayChanged(const display::Display& display) {
NotifyCursorDisplayChanged(display);
}
-void WMHelperAsh::OnMaximizeModeStarted() {
- NotifyMaximizeModeStarted();
+void WMHelperAsh::OnTabletModeStarted() {
+ NotifyTabletModeStarted();
}
-void WMHelperAsh::OnMaximizeModeEnding() {
- NotifyMaximizeModeEnding();
+void WMHelperAsh::OnTabletModeEnding() {
+ NotifyTabletModeEnding();
}
-void WMHelperAsh::OnMaximizeModeEnded() {
- NotifyMaximizeModeEnded();
+void WMHelperAsh::OnTabletModeEnded() {
+ NotifyTabletModeEnded();
}
void WMHelperAsh::OnDisplayConfigurationChanged() {
diff --git a/chromium/components/exo/wm_helper_ash.h b/chromium/components/exo/wm_helper_ash.h
index 53f4cb7ff35..f557484a836 100644
--- a/chromium/components/exo/wm_helper_ash.h
+++ b/chromium/components/exo/wm_helper_ash.h
@@ -5,8 +5,8 @@
#ifndef COMPONENTS_EXO_WM_HELPER_ASH_H_
#define COMPONENTS_EXO_WM_HELPER_ASH_H_
-#include "ash/shell_observer.h"
-#include "ash/wm_display_observer.h"
+#include "ash/display/window_tree_host_manager.h"
+#include "ash/wm/tablet_mode/tablet_mode_observer.h"
#include "base/macros.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/client/cursor_client_observer.h"
@@ -18,11 +18,11 @@ namespace exo {
// A helper class for accessing WindowManager related features.
class WMHelperAsh : public WMHelper,
- public aura::client::ActivationChangeObserver,
+ public wm::ActivationChangeObserver,
public aura::client::FocusChangeObserver,
public aura::client::CursorClientObserver,
- public ash::ShellObserver,
- public ash::WmDisplayObserver,
+ public ash::TabletModeObserver,
+ public ash::WindowTreeHostManager::Observer,
public ui::InputDeviceEventObserver {
public:
WMHelperAsh();
@@ -34,20 +34,19 @@ class WMHelperAsh : public WMHelper,
aura::Window* GetPrimaryDisplayContainer(int container_id) override;
aura::Window* GetActiveWindow() const override;
aura::Window* GetFocusedWindow() const override;
- ui::CursorSetType GetCursorSet() const override;
+ ui::CursorSize GetCursorSize() const override;
const display::Display& GetCursorDisplay() const override;
void AddPreTargetHandler(ui::EventHandler* handler) override;
void PrependPreTargetHandler(ui::EventHandler* handler) override;
void RemovePreTargetHandler(ui::EventHandler* handler) override;
void AddPostTargetHandler(ui::EventHandler* handler) override;
void RemovePostTargetHandler(ui::EventHandler* handler) override;
- bool IsMaximizeModeWindowManagerEnabled() const override;
+ bool IsTabletModeWindowManagerEnabled() const override;
- // Overridden from aura::client::ActivationChangeObserver:
- void OnWindowActivated(
- aura::client::ActivationChangeObserver::ActivationReason reason,
- aura::Window* gained_active,
- aura::Window* lost_active) override;
+ // Overridden from wm::ActivationChangeObserver:
+ void OnWindowActivated(wm::ActivationChangeObserver::ActivationReason reason,
+ aura::Window* gained_active,
+ aura::Window* lost_active) override;
// Overridden from aura::client::FocusChangeObserver:
void OnWindowFocused(aura::Window* gained_focus,
@@ -55,15 +54,15 @@ class WMHelperAsh : public WMHelper,
// Overridden from aura::client::CursorClientObserver:
void OnCursorVisibilityChanged(bool is_visible) override;
- void OnCursorSetChanged(ui::CursorSetType cursor_set) override;
+ void OnCursorSizeChanged(ui::CursorSize cursor_size) override;
void OnCursorDisplayChanged(const display::Display& display) override;
- // Overridden from ash::ShellObserver:
- void OnMaximizeModeStarted() override;
- void OnMaximizeModeEnding() override;
- void OnMaximizeModeEnded() override;
+ // ash::TabletModeObserver:
+ void OnTabletModeStarted() override;
+ void OnTabletModeEnding() override;
+ void OnTabletModeEnded() override;
- // Overridden from ash::WmDisplayObserver:
+ // WindowTreeHostManager::Observer:
void OnDisplayConfigurationChanged() override;
// Overridden from ui::InputDeviceEventObserver:
diff --git a/chromium/components/exo/wm_helper_mus.cc b/chromium/components/exo/wm_helper_mus.cc
index 89543f3f908..8f8e8c2edeb 100644
--- a/chromium/components/exo/wm_helper_mus.cc
+++ b/chromium/components/exo/wm_helper_mus.cc
@@ -58,9 +58,9 @@ aura::Window* WMHelperMus::GetFocusedWindow() const {
return focused_window_;
}
-ui::CursorSetType WMHelperMus::GetCursorSet() const {
+ui::CursorSize WMHelperMus::GetCursorSize() const {
NOTIMPLEMENTED();
- return ui::CursorSetType::CURSOR_SET_NORMAL;
+ return ui::CursorSize::kNormal;
}
const display::Display& WMHelperMus::GetCursorDisplay() const {
@@ -90,7 +90,7 @@ void WMHelperMus::RemovePostTargetHandler(ui::EventHandler* handler) {
aura::Env::GetInstance()->RemovePostTargetHandler(handler);
}
-bool WMHelperMus::IsMaximizeModeWindowManagerEnabled() const {
+bool WMHelperMus::IsTabletModeWindowManagerEnabled() const {
NOTIMPLEMENTED();
return false;
}
@@ -148,9 +148,9 @@ void WMHelperMus::SetFocusedWindow(aura::Window* window) {
NotifyWindowFocused(focused_window_, lost_focus);
}
-aura::client::ActivationClient* WMHelperMus::GetActivationClient() {
+wm::ActivationClient* WMHelperMus::GetActivationClient() {
return root_with_active_focus_client_
- ? aura::client::GetActivationClient(root_with_active_focus_client_)
+ ? wm::GetActivationClient(root_with_active_focus_client_)
: nullptr;
}
diff --git a/chromium/components/exo/wm_helper_mus.h b/chromium/components/exo/wm_helper_mus.h
index 6fda7388396..820db7a929c 100644
--- a/chromium/components/exo/wm_helper_mus.h
+++ b/chromium/components/exo/wm_helper_mus.h
@@ -11,11 +11,9 @@
#include "ui/aura/mus/focus_synchronizer_observer.h"
#include "ui/events/devices/input_device_event_observer.h"
-namespace aura {
-namespace client {
+namespace wm {
class ActivationClient;
}
-}
namespace exo {
@@ -35,14 +33,14 @@ class WMHelperMus : public WMHelper,
aura::Window* GetPrimaryDisplayContainer(int container_id) override;
aura::Window* GetActiveWindow() const override;
aura::Window* GetFocusedWindow() const override;
- ui::CursorSetType GetCursorSet() const override;
+ ui::CursorSize GetCursorSize() const override;
const display::Display& GetCursorDisplay() const override;
void AddPreTargetHandler(ui::EventHandler* handler) override;
void PrependPreTargetHandler(ui::EventHandler* handler) override;
void RemovePreTargetHandler(ui::EventHandler* handler) override;
void AddPostTargetHandler(ui::EventHandler* handler) override;
void RemovePostTargetHandler(ui::EventHandler* handler) override;
- bool IsMaximizeModeWindowManagerEnabled() const override;
+ bool IsTabletModeWindowManagerEnabled() const override;
// Overridden from aura::FocusSynchronizerObserver:
void OnActiveFocusClientChanged(aura::client::FocusClient* focus_client,
@@ -61,7 +59,7 @@ class WMHelperMus : public WMHelper,
void SetActiveWindow(aura::Window* window);
void SetFocusedWindow(aura::Window* window);
- aura::client::ActivationClient* GetActivationClient();
+ wm::ActivationClient* GetActivationClient();
// Current FocusClient.
aura::client::FocusClient* active_focus_client_ = nullptr;
diff --git a/chromium/components/favicon/content/content_favicon_driver.cc b/chromium/components/favicon/content/content_favicon_driver.cc
index ece84e5ddac..09b45ef2953 100644
--- a/chromium/components/favicon/content/content_favicon_driver.cc
+++ b/chromium/components/favicon/content/content_favicon_driver.cc
@@ -210,7 +210,9 @@ void ContentFaviconDriver::DidStartNavigation(
return;
favicon_urls_.reset();
- manifest_url_ = GURL();
+
+ if (!navigation_handle->IsSameDocument())
+ manifest_url_ = GURL();
content::ReloadType reload_type = navigation_handle->GetReloadType();
if (reload_type == content::ReloadType::NONE || IsOffTheRecord())
diff --git a/chromium/components/favicon/content/content_favicon_driver_unittest.cc b/chromium/components/favicon/content/content_favicon_driver_unittest.cc
index 5439aee3d76..0c521434504 100644
--- a/chromium/components/favicon/content/content_favicon_driver_unittest.cc
+++ b/chromium/components/favicon/content/content_favicon_driver_unittest.cc
@@ -78,7 +78,7 @@ TEST_F(ContentFaviconDriverTest, ShouldCauseImageDownload) {
// Mimic a page load.
TestFetchFaviconForPage(
kPageURL,
- {content::FaviconURL(kIconURL, content::FaviconURL::FAVICON,
+ {content::FaviconURL(kIconURL, content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes)});
EXPECT_TRUE(web_contents_tester()->TestDidDownloadImage(
kIconURL, 200, kEmptyIcons, kEmptyIconSizes));
@@ -92,7 +92,7 @@ TEST_F(ContentFaviconDriverTest, ShouldNotRequestRepeatedlyIfUnavailable) {
// Mimic a page load.
TestFetchFaviconForPage(
kPageURL,
- {content::FaviconURL(kIconURL, content::FaviconURL::FAVICON,
+ {content::FaviconURL(kIconURL, content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes)});
// Verify that no download request is pending for the image.
EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL));
@@ -105,9 +105,10 @@ TEST_F(ContentFaviconDriverTest, ShouldDownloadSecondIfFirstUnavailable) {
// Mimic a page load.
TestFetchFaviconForPage(
kPageURL,
- {content::FaviconURL(kIconURL, content::FaviconURL::FAVICON,
+ {content::FaviconURL(kIconURL, content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes),
- content::FaviconURL(kOtherIconURL, content::FaviconURL::FAVICON,
+ content::FaviconURL(kOtherIconURL,
+ content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes)});
// Verify a download request is pending for the second image.
EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL));
@@ -121,9 +122,9 @@ TEST_F(ContentFaviconDriverTest, FaviconUpdateNoLastCommittedEntry) {
ASSERT_EQ(nullptr, web_contents()->GetController().GetLastCommittedEntry());
std::vector<content::FaviconURL> favicon_urls;
- favicon_urls.push_back(
- content::FaviconURL(GURL("http://www.google.ca/favicon.ico"),
- content::FaviconURL::FAVICON, kEmptyIconSizes));
+ favicon_urls.push_back(content::FaviconURL(
+ GURL("http://www.google.ca/favicon.ico"),
+ content::FaviconURL::IconType::kFavicon, kEmptyIconSizes));
favicon::ContentFaviconDriver* driver =
favicon::ContentFaviconDriver::FromWebContents(web_contents());
static_cast<content::WebContentsObserver*>(driver)
@@ -141,9 +142,9 @@ TEST_F(ContentFaviconDriverTest, RecordsHistorgramsForCandidates) {
// Navigation to a page updating one icon.
NavigateAndCommit(GURL("http://www.youtube.com"));
- driver_as_observer->DidUpdateFaviconURL(
- {content::FaviconURL(GURL("http://www.youtube.com/favicon.ico"),
- content::FaviconURL::FAVICON, kSizes16x16and32x32)});
+ driver_as_observer->DidUpdateFaviconURL({content::FaviconURL(
+ GURL("http://www.youtube.com/favicon.ico"),
+ content::FaviconURL::IconType::kFavicon, kSizes16x16and32x32)});
EXPECT_THAT(tester.GetAllSamples("Favicons.CandidatesCount"),
ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
@@ -154,12 +155,14 @@ TEST_F(ContentFaviconDriverTest, RecordsHistorgramsForCandidates) {
std::vector<content::FaviconURL> favicon_urls = {
content::FaviconURL(GURL("http://www.google.ca/favicon.ico"),
- content::FaviconURL::FAVICON, kSizes16x16and32x32),
+ content::FaviconURL::IconType::kFavicon,
+ kSizes16x16and32x32),
content::FaviconURL(GURL("http://www.google.ca/precomposed_icon.png"),
- content::FaviconURL::TOUCH_PRECOMPOSED_ICON,
+ content::FaviconURL::IconType::kTouchPrecomposedIcon,
kEmptyIconSizes),
content::FaviconURL(GURL("http://www.google.ca/touch_icon.png"),
- content::FaviconURL::TOUCH_ICON, kEmptyIconSizes)};
+ content::FaviconURL::IconType::kTouchIcon,
+ kEmptyIconSizes)};
// Double navigation to a page with 3 different icons.
NavigateAndCommit(GURL("http://www.google.ca"));
diff --git a/chromium/components/favicon/content/favicon_url_util.cc b/chromium/components/favicon/content/favicon_url_util.cc
index c77ff8b7808..f08149acc82 100644
--- a/chromium/components/favicon/content/favicon_url_util.cc
+++ b/chromium/components/favicon/content/favicon_url_util.cc
@@ -17,13 +17,13 @@ namespace {
favicon_base::IconType IconTypeFromContentIconType(
content::FaviconURL::IconType icon_type) {
switch (icon_type) {
- case content::FaviconURL::FAVICON:
+ case content::FaviconURL::IconType::kFavicon:
return favicon_base::FAVICON;
- case content::FaviconURL::TOUCH_ICON:
+ case content::FaviconURL::IconType::kTouchIcon:
return favicon_base::TOUCH_ICON;
- case content::FaviconURL::TOUCH_PRECOMPOSED_ICON:
+ case content::FaviconURL::IconType::kTouchPrecomposedIcon:
return favicon_base::TOUCH_PRECOMPOSED_ICON;
- case content::FaviconURL::INVALID_ICON:
+ case content::FaviconURL::IconType::kInvalid:
return favicon_base::INVALID_ICON;
}
NOTREACHED();
diff --git a/chromium/components/favicon/core/BUILD.gn b/chromium/components/favicon/core/BUILD.gn
index b4d4a79ec5f..c0468a9e90b 100644
--- a/chromium/components/favicon/core/BUILD.gn
+++ b/chromium/components/favicon/core/BUILD.gn
@@ -43,14 +43,6 @@ static_library("core") {
"//ui/gfx",
"//url",
]
- if (!is_ios) {
- deps += [ "//cc/paint" ]
- sources += [
- "fallback_icon_client.h",
- "fallback_icon_service.cc",
- "fallback_icon_service.h",
- ]
- }
}
source_set("unit_tests") {
@@ -72,6 +64,7 @@ source_set("unit_tests") {
"//components/history/core/test:test",
"//components/image_fetcher/core",
"//components/variations:test_support",
+ "//net:test_support",
"//skia",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/favicon/core/DEPS b/chromium/components/favicon/core/DEPS
index 07a7e022c46..06d5fdedeb1 100644
--- a/chromium/components/favicon/core/DEPS
+++ b/chromium/components/favicon/core/DEPS
@@ -8,6 +8,7 @@ include_rules = [
"+components/keyed_service/core",
"+components/variations",
"+net/base",
+ "+net/traffic_annotation",
"+skia",
"+third_party/skia",
"+third_party/skia/include",
diff --git a/chromium/components/favicon/core/fallback_icon_client.h b/chromium/components/favicon/core/fallback_icon_client.h
deleted file mode 100644
index 8efa28c2e7d..00000000000
--- a/chromium/components/favicon/core/fallback_icon_client.h
+++ /dev/null
@@ -1,28 +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_FAVICON_CORE_FALLBACK_ICON_CLIENT_H_
-#define COMPONENTS_FAVICON_CORE_FALLBACK_ICON_CLIENT_H_
-
-#include <string>
-#include <vector>
-
-#include "components/keyed_service/core/keyed_service.h"
-
-namespace favicon {
-
-// This class abstracts operations that depend on the embedder's environment,
-// e.g. Chrome.
-class FallbackIconClient : public KeyedService {
- public:
- // Returns a list of font names for fallback icon rendering.
- virtual const std::vector<std::string>& GetFontNameList() const = 0;
-
- protected:
- ~FallbackIconClient() override {}
-};
-
-} // namespace favicon
-
-#endif // COMPONENTS_FAVICON_CORE_FALLBACK_ICON_CLIENT_H_
diff --git a/chromium/components/favicon/core/fallback_icon_service.cc b/chromium/components/favicon/core/fallback_icon_service.cc
deleted file mode 100644
index 291e1fac8a9..00000000000
--- a/chromium/components/favicon/core/fallback_icon_service.cc
+++ /dev/null
@@ -1,90 +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/favicon/core/fallback_icon_service.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-
-#include "cc/paint/skia_paint_canvas.h"
-#include "components/favicon/core/fallback_icon_client.h"
-#include "components/favicon/core/fallback_url_util.h"
-#include "components/favicon_base/fallback_icon_style.h"
-#include "third_party/skia/include/core/SkPaint.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/codec/png_codec.h"
-#include "ui/gfx/font_list.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-#include "url/gurl.h"
-
-namespace favicon {
-namespace {
-
-// Arbitrary maximum icon size, can be reasonably increased if needed.
-const int kMaxFallbackFaviconSize = 288;
-
-} // namespace
-
-FallbackIconService::FallbackIconService(
- FallbackIconClient* fallback_icon_client)
- : fallback_icon_client_(fallback_icon_client) {
-}
-
-FallbackIconService::~FallbackIconService() {
-}
-
-std::vector<unsigned char> FallbackIconService::RenderFallbackIconBitmap(
- const GURL& icon_url,
- int size,
- const favicon_base::FallbackIconStyle& style) {
- int size_to_use = std::min(kMaxFallbackFaviconSize, size);
- SkBitmap bitmap;
- bitmap.allocN32Pixels(size_to_use, size_to_use, false);
- cc::SkiaPaintCanvas paint_canvas(bitmap);
- gfx::Canvas canvas(&paint_canvas, 1.f);
-
- DrawFallbackIcon(icon_url, size_to_use, style, &canvas);
-
- std::vector<unsigned char> bitmap_data;
- if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data))
- bitmap_data.clear();
- return bitmap_data;
-}
-
-void FallbackIconService::DrawFallbackIcon(
- const GURL& icon_url,
- int size,
- const favicon_base::FallbackIconStyle& style,
- gfx::Canvas* canvas) {
- const int kOffsetX = 0;
- const int kOffsetY = 0;
- cc::PaintFlags flags;
- flags.setStyle(cc::PaintFlags::kFill_Style);
- flags.setAntiAlias(true);
-
- // Draw a filled, colored rounded square.
- flags.setColor(style.background_color);
- int corner_radius = static_cast<int>(size * style.roundness * 0.5 + 0.5);
- canvas->DrawRoundRect(gfx::Rect(kOffsetX, kOffsetY, size, size),
- corner_radius, flags);
-
- // Draw text.
- base::string16 icon_text = GetFallbackIconText(icon_url);
- if (icon_text.empty())
- return;
- int font_size = static_cast<int>(size * style.font_size_ratio);
- if (font_size <= 0)
- return;
-
- canvas->DrawStringRectWithFlags(
- icon_text,
- gfx::FontList(fallback_icon_client_->GetFontNameList(), gfx::Font::NORMAL,
- font_size, gfx::Font::Weight::NORMAL),
- style.text_color, gfx::Rect(kOffsetX, kOffsetY, size, size),
- gfx::Canvas::TEXT_ALIGN_CENTER);
-}
-
-} // namespace favicon
diff --git a/chromium/components/favicon/core/fallback_icon_service.h b/chromium/components/favicon/core/fallback_icon_service.h
deleted file mode 100644
index 56b155e6270..00000000000
--- a/chromium/components/favicon/core/fallback_icon_service.h
+++ /dev/null
@@ -1,55 +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_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
-#define COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
-
-class GURL;
-
-namespace gfx {
-class Canvas;
-}
-
-namespace favicon_base {
-struct FallbackIconStyle;
-}
-
-namespace favicon {
-
-class FallbackIconClient;
-
-// A service to provide methods to render fallback favicons.
-class FallbackIconService : public KeyedService {
- public:
- explicit FallbackIconService(FallbackIconClient* fallback_icon_client);
- ~FallbackIconService() override;
-
- // Renders a fallback icon synchronously and returns the bitmap. Returns an
- // empty std::vector on failure. |size| is icon width and height in pixels.
- std::vector<unsigned char> RenderFallbackIconBitmap(
- const GURL& icon_url,
- int size,
- const favicon_base::FallbackIconStyle& style);
-
- private:
- // Renders a fallback icon on |canvas| at position (|x|, |y|). |size| is icon
- // width and height in pixels.
- void DrawFallbackIcon(const GURL& icon_url,
- int size,
- const favicon_base::FallbackIconStyle& style,
- gfx::Canvas* canvas);
-
- FallbackIconClient* fallback_icon_client_;
-
- DISALLOW_COPY_AND_ASSIGN(FallbackIconService);
-};
-
-} // namespace favicon
-
-#endif // COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
diff --git a/chromium/components/favicon/core/favicon_handler.cc b/chromium/components/favicon/core/favicon_handler.cc
index aa57bfcd22f..52b7f267cdd 100644
--- a/chromium/components/favicon/core/favicon_handler.cc
+++ b/chromium/components/favicon/core/favicon_handler.cc
@@ -31,6 +31,7 @@ const int kNonTouchLargestIconSize = 192;
// Size (along each axis) of a touch icon. This currently corresponds to
// the apple touch icon for iPad.
+// TODO(crbug.com/736290): Consider changing this to 192x192 for Android.
const int kTouchIconSize = 144;
// Return true if |bitmap_result| is expired.
@@ -124,7 +125,12 @@ bool HasValidResult(
}
std::vector<int> GetDesiredPixelSizes(
- FaviconDriverObserver::NotificationIconType handler_type) {
+ FaviconDriverObserver::NotificationIconType handler_type,
+ bool candidates_from_web_manifest) {
+ // When reading icons from web manifests, prefer kNonTouchLargestIconSize.
+ if (candidates_from_web_manifest)
+ return std::vector<int>(1U, kNonTouchLargestIconSize);
+
switch (handler_type) {
case FaviconDriverObserver::NON_TOUCH_16_DIP: {
std::vector<int> pixel_sizes;
@@ -414,9 +420,10 @@ void FaviconHandler::OnDidDownloadManifest(
void FaviconHandler::OnGotFinalIconURLCandidates(
const std::vector<FaviconURL>& candidates) {
- std::vector<FaviconCandidate> sorted_candidates;
const std::vector<int> desired_pixel_sizes =
- GetDesiredPixelSizes(handler_type_);
+ GetDesiredPixelSizes(handler_type_, !manifest_url_.is_empty());
+
+ std::vector<FaviconCandidate> sorted_candidates;
for (const FaviconURL& candidate : candidates) {
if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) {
sorted_candidates.push_back(
@@ -437,10 +444,13 @@ void FaviconHandler::OnGotFinalIconURLCandidates(
// static
int FaviconHandler::GetMaximalIconSize(
- FaviconDriverObserver::NotificationIconType handler_type) {
+ FaviconDriverObserver::NotificationIconType handler_type,
+ bool candidates_from_web_manifest) {
int max_size = 0;
- for (int size : GetDesiredPixelSizes(handler_type))
+ for (int size :
+ GetDesiredPixelSizes(handler_type, candidates_from_web_manifest)) {
max_size = std::max(max_size, size);
+ }
return max_size;
}
@@ -485,9 +495,10 @@ void FaviconHandler::OnDidDownloadFavicon(
gfx::ImageSkia image_skia;
if (download_largest_icon_) {
std::vector<size_t> best_indices;
- SelectFaviconFrameIndices(original_bitmap_sizes,
- GetDesiredPixelSizes(handler_type_),
- &best_indices, &score);
+ SelectFaviconFrameIndices(
+ original_bitmap_sizes,
+ GetDesiredPixelSizes(handler_type_, !manifest_url_.is_empty()),
+ &best_indices, &score);
DCHECK_EQ(1U, best_indices.size());
image_skia =
gfx::ImageSkia::CreateFrom1xBitmap(bitmaps[best_indices.front()]);
@@ -664,9 +675,9 @@ void FaviconHandler::ScheduleImageDownload(const GURL& image_url,
// A max bitmap size is specified to avoid receiving huge bitmaps in
// OnDidDownloadFavicon(). See FaviconDriver::StartDownload()
// for more details about the max bitmap size.
- const int download_id =
- delegate_->DownloadImage(image_url, GetMaximalIconSize(handler_type_),
- image_download_request_.callback());
+ const int download_id = delegate_->DownloadImage(
+ image_url, GetMaximalIconSize(handler_type_, !manifest_url_.is_empty()),
+ image_download_request_.callback());
DCHECK_NE(download_id, 0);
}
diff --git a/chromium/components/favicon/core/favicon_handler.h b/chromium/components/favicon/core/favicon_handler.h
index 5b4c7aa3bab..fce615f2b29 100644
--- a/chromium/components/favicon/core/favicon_handler.h
+++ b/chromium/components/favicon/core/favicon_handler.h
@@ -163,8 +163,11 @@ class FaviconHandler {
bool HasPendingTasksForTest();
// Get the maximal icon size in pixels for a handler of type |handler_type|.
+ // |candidates_from_web_manifest| represents whether the icons are coming
+ // from a Web Manifest.
static int GetMaximalIconSize(
- FaviconDriverObserver::NotificationIconType handler_type);
+ FaviconDriverObserver::NotificationIconType handler_type,
+ bool candidates_from_web_manifest);
private:
// Used to track a candidate for the favicon.
diff --git a/chromium/components/favicon/core/favicon_handler_unittest.cc b/chromium/components/favicon/core/favicon_handler_unittest.cc
index be6a075c140..af8a73cb194 100644
--- a/chromium/components/favicon/core/favicon_handler_unittest.cc
+++ b/chromium/components/favicon/core/favicon_handler_unittest.cc
@@ -22,6 +22,7 @@
#include "components/favicon/core/favicon_driver.h"
#include "components/favicon/core/features.h"
#include "components/favicon/core/test/mock_favicon_service.h"
+#include "skia/ext/image_operations.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -134,7 +135,18 @@ class FakeImageDownloader {
FaviconHandler::Delegate::ImageDownloadCallback callback) {
downloads_->push_back(url);
- const Response& response = responses_[url];
+ Response response = responses_[url];
+ DCHECK_EQ(response.bitmaps.size(), response.original_bitmap_sizes.size());
+ // Apply maximum image size.
+ for (size_t i = 0; i < response.bitmaps.size(); ++i) {
+ if (response.bitmaps[i].width() > max_image_size ||
+ response.bitmaps[i].height() > max_image_size) {
+ response.bitmaps[i] = skia::ImageOperations::Resize(
+ response.bitmaps[i], skia::ImageOperations::RESIZE_LANCZOS3,
+ max_image_size, max_image_size);
+ }
+ }
+
int download_id = next_download_id_++;
base::Closure bound_callback =
base::Bind(callback, download_id, response.http_status_code, url,
@@ -147,24 +159,17 @@ class FakeImageDownloader {
}
void Add(const GURL& icon_url,
- const IntVector& sizes,
const IntVector& original_sizes,
- SkColor color) {
- DCHECK_EQ(sizes.size(), original_sizes.size());
+ SkColor color = SK_ColorRED) {
Response response;
response.http_status_code = 200;
- for (int size : sizes) {
+ for (int size : original_sizes) {
response.original_bitmap_sizes.push_back(gfx::Size(size, size));
response.bitmaps.push_back(CreateBitmapWithEdgeSize(size, color));
}
responses_[icon_url] = response;
}
- // Simpler overload of the function above.
- void Add(const GURL& icon_url, const IntVector& sizes) {
- Add(icon_url, sizes, sizes, SK_ColorRED);
- }
-
void AddError(const GURL& icon_url, int http_status_code) {
Response response;
response.http_status_code = http_status_code;
@@ -685,7 +690,6 @@ TEST_F(FaviconHandlerTest, RedownloadExpiredPageUrlFavicon) {
gfx::kFaviconSize, kOldColor));
delegate_.fake_image_downloader().Add(kIconURL, IntVector{gfx::kFaviconSize},
- IntVector{gfx::kFaviconSize},
kNewColor);
EXPECT_CALL(favicon_service_,
@@ -734,7 +738,6 @@ TEST_F(FaviconHandlerTest, FaviconInHistoryInvalid) {
const GURL kIconURL("http://www.google.com/favicon");
delegate_.fake_image_downloader().Add(kIconURL, IntVector{gfx::kFaviconSize},
- IntVector{gfx::kFaviconSize},
SK_ColorBLUE);
// Set non-empty but invalid data.
@@ -815,14 +818,14 @@ TEST_F(FaviconHandlerTest, Download2ndFaviconURLCandidate) {
TEST_F(FaviconHandlerTest, UpdateDuringDownloading) {
const GURL kIconURL1("http://www.google.com/favicon");
const GURL kIconURL2 = kIconURL16x16;
- const GURL kIconURL3 = kIconURL64x64;
+ const GURL kIconURL3 = kIconURL12x12;
// Defer the download completion such that RunUntilIdle() doesn't complete
// the download.
delegate_.fake_image_downloader().SetRunCallbackManuallyForUrl(kIconURL1);
delegate_.fake_image_downloader().Add(kIconURL1, IntVector{16});
- delegate_.fake_image_downloader().Add(kIconURL3, IntVector{64});
+ delegate_.fake_image_downloader().Add(kIconURL3, IntVector{12});
std::unique_ptr<FaviconHandler> handler =
RunHandlerWithSimpleFaviconCandidates({kIconURL1, kIconURL2});
@@ -851,14 +854,14 @@ TEST_F(FaviconHandlerTest, UpdateDuringDownloading) {
// second.
TEST_F(FaviconHandlerTest, UpdateDuringDatabaseLookup) {
const GURL kIconURL1 = kIconURL16x16;
- const GURL kIconURL2 = kIconURL64x64;
+ const GURL kIconURL2 = kIconURL12x12;
// Defer the lookup completion such that RunUntilIdle() doesn't complete the
// lookup.
favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL1);
delegate_.fake_image_downloader().Add(kIconURL1, IntVector{16});
- delegate_.fake_image_downloader().Add(kIconURL2, IntVector{64});
+ delegate_.fake_image_downloader().Add(kIconURL2, IntVector{12});
std::unique_ptr<FaviconHandler> handler =
RunHandlerWithSimpleFaviconCandidates(URLVector{kIconURL1});
@@ -889,7 +892,7 @@ TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDownloadingShouldBeNoop) {
const GURL kSlowLoadingIconURL("http://www.google.com/slow_favicon");
const std::vector<FaviconURL> favicon_urls = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL12x12, FAVICON, kEmptySizes),
FaviconURL(kSlowLoadingIconURL, FAVICON, kEmptySizes),
};
@@ -903,7 +906,7 @@ TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDownloadingShouldBeNoop) {
FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls);
ASSERT_THAT(favicon_service_.fake()->db_requests(),
- ElementsAre(kPageURL, kIconURL64x64, kSlowLoadingIconURL));
+ ElementsAre(kPageURL, kIconURL12x12, kSlowLoadingIconURL));
ASSERT_TRUE(VerifyAndClearExpectations());
ASSERT_TRUE(delegate_.fake_image_downloader().HasPendingManualCallback());
@@ -926,17 +929,17 @@ TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDownloadingShouldBeNoop) {
// update during a database lookup is a no-op.
TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDatabaseLookupShouldBeNoop) {
const std::vector<FaviconURL> favicon_urls = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL12x12, FAVICON, kEmptySizes),
};
- favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL64x64);
+ favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL12x12);
std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates(
FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls);
// Ongoing database lookup.
ASSERT_THAT(favicon_service_.fake()->db_requests(),
- ElementsAre(kPageURL, kIconURL64x64));
+ ElementsAre(kPageURL, kIconURL12x12));
ASSERT_THAT(delegate_.downloads(), IsEmpty());
ASSERT_TRUE(VerifyAndClearExpectations());
ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
@@ -953,7 +956,7 @@ TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDatabaseLookupShouldBeNoop) {
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _));
EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
base::RunLoop().RunUntilIdle();
- EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL64x64));
+ EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
}
// Test that calling OnUpdateFaviconUrl() with the same icon URLs as before is a
@@ -1128,9 +1131,9 @@ TEST_F(FaviconHandlerTest, MultipleFavicons404) {
ON_CALL(favicon_service_, WasUnableToDownloadFavicon(k404IconURL))
.WillByDefault(Return(true));
- EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL64x64, _, _));
- RunHandlerWithSimpleFaviconCandidates({k404IconURL, kIconURL64x64});
- EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL64x64));
+ EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
+ RunHandlerWithSimpleFaviconCandidates({k404IconURL, kIconURL12x12});
+ EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
}
// Test that the best favicon is selected when:
@@ -1146,9 +1149,9 @@ TEST_F(FaviconHandlerTest, MultipleFaviconsLast404) {
ON_CALL(favicon_service_, WasUnableToDownloadFavicon(k404IconURL))
.WillByDefault(Return(true));
- EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL64x64, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL64x64, k404IconURL});
- EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL64x64));
+ EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
+ RunHandlerWithSimpleFaviconCandidates({kIconURL12x12, k404IconURL});
+ EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
}
// Test that no favicon is selected when:
@@ -1261,7 +1264,8 @@ TEST_F(FaviconHandlerTest, TestSelectLargestFavicon) {
TEST_F(FaviconHandlerTest, TestFaviconWasScaledAfterDownload) {
const int kMaximalSize = FaviconHandler::GetMaximalIconSize(
- FaviconDriverObserver::NON_TOUCH_LARGEST);
+ FaviconDriverObserver::NON_TOUCH_LARGEST,
+ /*candidates_from_web_manifest=*/false);
const GURL kIconURL1("http://www.google.com/b");
const GURL kIconURL2("http://www.google.com/c");
@@ -1269,11 +1273,9 @@ TEST_F(FaviconHandlerTest, TestFaviconWasScaledAfterDownload) {
const int kOriginalSize1 = kMaximalSize + 1;
const int kOriginalSize2 = kMaximalSize + 2;
- delegate_.fake_image_downloader().Add(kIconURL1, IntVector{kMaximalSize},
- IntVector{kOriginalSize1},
+ delegate_.fake_image_downloader().Add(kIconURL1, IntVector{kOriginalSize1},
SK_ColorBLUE);
- delegate_.fake_image_downloader().Add(kIconURL2, IntVector{kMaximalSize},
- IntVector{kOriginalSize2},
+ delegate_.fake_image_downloader().Add(kIconURL2, IntVector{kOriginalSize2},
SK_ColorBLUE);
// Verify the best bitmap was selected (although smaller than |kIconURL2|)
@@ -1460,31 +1462,47 @@ TEST_F(FaviconHandlerTest, TestRecordSkippedDownloadForKnownFailingUrl) {
/*expected_count=*/1)));
}
-// Test that the support for Web Manifest is disabled by default, unless the
-// feature is enabled.
-TEST_F(FaviconHandlerTest, IgnoreWebManifestByDefault) {
- const GURL kManifestURL("http://www.google.com/manifest.json");
-
- RunHandlerWithSimpleFaviconCandidates({kIconURL16x16}, kManifestURL);
- EXPECT_THAT(favicon_service_.fake()->db_requests(),
- Not(Contains(kManifestURL)));
- EXPECT_THAT(delegate_.downloads(), Not(Contains(kManifestURL)));
-}
-
+// Manifests are currently enabled by default. Leaving this fixture for
+// logical grouping and blame layer.
class FaviconHandlerManifestsEnabledTest : public FaviconHandlerTest {
protected:
const GURL kManifestURL = GURL("http://www.google.com/manifest.json");
- FaviconHandlerManifestsEnabledTest() {
- override_features_.InitAndEnableFeature(kFaviconsFromWebManifest);
+ FaviconHandlerManifestsEnabledTest() = default;
+
+ // Exercises the handler for the simplest case where all types are TOUCH_ICON
+ // and no sizes are provided, using a FaviconHandler of type TOUCH_LARGETS.
+ std::unique_ptr<FaviconHandler> RunHandlerWithSimpleTouchIconCandidates(
+ const std::vector<GURL>& urls,
+ const GURL& manifest_url) {
+ std::vector<favicon::FaviconURL> candidates;
+ for (const GURL& url : urls) {
+ candidates.emplace_back(url, TOUCH_ICON, kEmptySizes);
+ }
+ return RunHandlerWithCandidates(FaviconDriverObserver::TOUCH_LARGEST,
+ candidates, manifest_url);
}
private:
- base::test::ScopedFeatureList override_features_;
+ // Avoid accidental use of FAVICON type, since Web Manifests are handled by
+ // the FaviconHandler of type TOUCH_LARGEST.
+ using FaviconHandlerTest::RunHandlerWithSimpleFaviconCandidates;
DISALLOW_COPY_AND_ASSIGN(FaviconHandlerManifestsEnabledTest);
};
+// Test that the feature for loading icons from Web Manifests can be disabled.
+TEST_F(FaviconHandlerManifestsEnabledTest, IgnoreWebManifestIfDisabled) {
+ const GURL kManifestURL("http://www.google.com/manifest.json");
+ base::test::ScopedFeatureList override_features;
+ override_features.InitAndDisableFeature(kFaviconsFromWebManifest);
+
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16}, kManifestURL);
+ EXPECT_THAT(favicon_service_.fake()->db_requests(),
+ Not(Contains(kManifestURL)));
+ EXPECT_THAT(delegate_.downloads(), Not(Contains(kManifestURL)));
+}
+
// Test that a favicon corresponding to a web manifest is reported when:
// - There is data in the favicon database for the manifest URL.
// AND
@@ -1500,12 +1518,12 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
EXPECT_CALL(favicon_service_,
UpdateFaviconMappingsAndFetch(_, kManifestURL, WEB_MANIFEST_ICON,
- /*desired_size_in_dip=*/16, _, _));
+ /*desired_size_in_dip=*/0, _, _));
EXPECT_CALL(delegate_,
- OnFaviconUpdated(_, FaviconDriverObserver::NON_TOUCH_16_DIP,
+ OnFaviconUpdated(_, FaviconDriverObserver::TOUCH_LARGEST,
kManifestURL, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL));
EXPECT_THAT(delegate_.downloads(), IsEmpty());
@@ -1528,13 +1546,13 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
EXPECT_CALL(favicon_service_,
UpdateFaviconMappingsAndFetch(_, kManifestURL, WEB_MANIFEST_ICON,
- /*desired_size_in_dip=*/16, _, _));
+ /*desired_size_in_dip=*/0, _, _));
EXPECT_CALL(delegate_,
- OnFaviconUpdated(_, FaviconDriverObserver::NON_TOUCH_16_DIP,
+ OnFaviconUpdated(_, FaviconDriverObserver::TOUCH_LARGEST,
kManifestURL, _, _));
std::unique_ptr<FaviconHandler> handler =
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
// Complete the lookup.
@@ -1550,7 +1568,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
// data in the database for neither the page URL nor the manifest URL.
TEST_F(FaviconHandlerManifestsEnabledTest, GetFaviconFromUnknownManifest) {
const std::vector<favicon::FaviconURL> kManifestIcons = {
- FaviconURL(kIconURL16x16, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL16x16, WEB_MANIFEST_ICON, kEmptySizes),
};
delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
@@ -1561,17 +1579,59 @@ TEST_F(FaviconHandlerManifestsEnabledTest, GetFaviconFromUnknownManifest) {
SetFavicons(_, kManifestURL, WEB_MANIFEST_ICON, _));
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL16x16));
}
+// Test that icons from a web manifest use a desired size of 192x192.
+TEST_F(FaviconHandlerManifestsEnabledTest, Prefer192x192IconFromManifest) {
+ const GURL kIconURL144x144 = GURL("http://www.google.com/favicon144x144");
+ const GURL kIconURL192x192 = GURL("http://www.google.com/favicon192x192");
+
+ delegate_.fake_image_downloader().Add(kIconURL144x144, IntVector{144});
+ delegate_.fake_image_downloader().Add(kIconURL192x192, IntVector{192});
+
+ const std::vector<favicon::FaviconURL> kManifestIcons = {
+ FaviconURL(kIconURL144x144, WEB_MANIFEST_ICON,
+ SizeVector(1U, gfx::Size(144, 144))),
+ FaviconURL(kIconURL192x192, WEB_MANIFEST_ICON,
+ SizeVector(1U, gfx::Size(192, 192))),
+ };
+
+ delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
+
+ RunHandlerWithCandidates(FaviconDriverObserver::TOUCH_LARGEST,
+ std::vector<favicon::FaviconURL>(), kManifestURL);
+
+ EXPECT_THAT(delegate_.downloads(),
+ ElementsAre(kManifestURL, kIconURL192x192));
+}
+
+// Test that a 192x192 favicon corresponding to a web manifest is reported with
+// the appropriate size when there is data in the database for neither the page
+// URL nor the manifest URL.
+TEST_F(FaviconHandlerManifestsEnabledTest,
+ GetNonResized192x192FaviconFromUnknownManifest) {
+ const GURL kIconURL192x192 = GURL("http://www.google.com/favicon192x192");
+ const std::vector<favicon::FaviconURL> kManifestIcons = {
+ FaviconURL(kIconURL192x192, WEB_MANIFEST_ICON, kEmptySizes),
+ };
+ delegate_.fake_image_downloader().Add(kIconURL192x192, IntVector{192});
+ delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
+
+ EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, WEB_MANIFEST_ICON,
+ ImageSizeIs(192, 192)));
+
+ RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
+}
+
// Test that the manifest and icon are redownloaded if the icon cached for the
// page URL expired.
TEST_F(FaviconHandlerManifestsEnabledTest, GetFaviconFromExpiredManifest) {
const std::vector<favicon::FaviconURL> kManifestIcons = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL64x64, WEB_MANIFEST_ICON, kEmptySizes),
};
favicon_service_.fake()->Store(
@@ -1583,7 +1643,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest, GetFaviconFromExpiredManifest) {
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _)).Times(2);
EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
@@ -1596,7 +1656,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
GetFaviconFromExpiredManifestLinkedFromOtherPage) {
const GURL kSomePreviousPageURL("https://www.google.com/previous");
const std::vector<favicon::FaviconURL> kManifestIcons = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL64x64, WEB_MANIFEST_ICON, kEmptySizes),
};
favicon_service_.fake()->Store(
@@ -1608,7 +1668,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _)).Times(2);
EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
@@ -1622,17 +1682,18 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
GetFaviconFromUnknownManifestButKnownIcon) {
const GURL kSomePreviousPageURL("https://www.google.com/previous");
const std::vector<favicon::FaviconURL> kManifestIcons = {
- FaviconURL(kIconURL16x16, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL16x16, WEB_MANIFEST_ICON, kEmptySizes),
};
- favicon_service_.fake()->Store(kSomePreviousPageURL, kIconURL16x16,
- CreateRawBitmapResult(kIconURL16x16));
+ favicon_service_.fake()->Store(
+ kSomePreviousPageURL, kIconURL16x16,
+ CreateRawBitmapResult(kIconURL16x16, TOUCH_ICON));
delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _));
- RunHandlerWithSimpleFaviconCandidates(URLVector(), kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL));
// This is because, in the current implementation, FaviconHandler only checks
@@ -1648,7 +1709,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest, UnknownManifestReturning404) {
EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kManifestURL));
EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL, kIconURL12x12));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL12x12));
@@ -1663,7 +1724,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest, IgnoreManifestWithPrior404) {
EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kIconURL12x12));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
@@ -1684,7 +1745,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest, UnknownManifestWithoutIcons) {
EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kManifestURL));
EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL, kIconURL12x12));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL12x12));
@@ -1703,8 +1764,9 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
delegate_.fake_manifest_downloader().Add(kManifestURL,
std::vector<favicon::FaviconURL>());
- favicon_service_.fake()->Store(kSomePreviousPageURL, kIconURL12x12,
- CreateRawBitmapResult(kIconURL12x12));
+ favicon_service_.fake()->Store(
+ kSomePreviousPageURL, kIconURL12x12,
+ CreateRawBitmapResult(kIconURL12x12, TOUCH_ICON));
EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
@@ -1718,7 +1780,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
UpdateFaviconMappingsAndFetch(_, kIconURL12x12, _, _, _, _));
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
EXPECT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL, kIconURL12x12));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL));
@@ -1733,14 +1795,15 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
UnknownManifestWithoutIconsAndRegularIconInHistory) {
delegate_.fake_manifest_downloader().Add(kManifestURL,
std::vector<favicon::FaviconURL>());
- favicon_service_.fake()->Store(kPageURL, kIconURL16x16,
- CreateRawBitmapResult(kIconURL16x16));
+ favicon_service_.fake()->Store(
+ kPageURL, kIconURL16x16,
+ CreateRawBitmapResult(kIconURL16x16, TOUCH_ICON));
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _));
EXPECT_CALL(favicon_service_,
UpdateFaviconMappingsAndFetch(_, kManifestURL, _, _, _, _));
- RunHandlerWithSimpleFaviconCandidates({kIconURL16x16}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16}, kManifestURL);
ASSERT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL));
@@ -1753,10 +1816,10 @@ TEST_F(FaviconHandlerManifestsEnabledTest, ManifestUpdateViaJavascript) {
const GURL kManifestURL1("http://www.google.com/manifest1.json");
const GURL kManifestURL2("http://www.google.com/manifest2.json");
const std::vector<favicon::FaviconURL> kManifestIcons1 = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL64x64, WEB_MANIFEST_ICON, kEmptySizes),
};
const std::vector<favicon::FaviconURL> kManifestIcons2 = {
- FaviconURL(kIconURL10x10, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL10x10, WEB_MANIFEST_ICON, kEmptySizes),
};
delegate_.fake_manifest_downloader().Add(kManifestURL1, kManifestIcons1);
@@ -1765,7 +1828,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest, ManifestUpdateViaJavascript) {
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL1, _, _));
std::unique_ptr<FaviconHandler> handler =
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL1);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL1);
ASSERT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL1));
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL1, kIconURL64x64));
@@ -1773,9 +1836,9 @@ TEST_F(FaviconHandlerManifestsEnabledTest, ManifestUpdateViaJavascript) {
// Simulate the page changing it's manifest URL via Javascript.
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL2, _, _));
- handler->OnUpdateCandidates(kPageURL,
- {FaviconURL(kIconURL12x12, FAVICON, kEmptySizes)},
- kManifestURL2);
+ handler->OnUpdateCandidates(
+ kPageURL, {FaviconURL(kIconURL12x12, TOUCH_ICON, kEmptySizes)},
+ kManifestURL2);
base::RunLoop().RunUntilIdle();
ASSERT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kManifestURL2));
@@ -1788,7 +1851,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest, ManifestUpdateViaJavascript) {
TEST_F(FaviconHandlerManifestsEnabledTest,
RemoveManifestViaJavascriptWhileDatabaseLookup) {
const std::vector<favicon::FaviconURL> kManifestIcons = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL64x64, WEB_MANIFEST_ICON, kEmptySizes),
};
delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
@@ -1796,7 +1859,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
favicon_service_.fake()->SetRunCallbackManuallyForUrl(kManifestURL);
std::unique_ptr<FaviconHandler> handler =
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12}, kManifestURL);
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
ASSERT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kManifestURL));
// Database lookup for |kManifestURL| is ongoing.
@@ -1805,7 +1868,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
// Simulate the page changing it's manifest URL to empty via Javascript.
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
handler->OnUpdateCandidates(
- kPageURL, {FaviconURL(kIconURL12x12, FAVICON, kEmptySizes)}, GURL());
+ kPageURL, {FaviconURL(kIconURL12x12, TOUCH_ICON, kEmptySizes)}, GURL());
// Complete the lookup.
EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
base::RunLoop().RunUntilIdle();
@@ -1820,7 +1883,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
TEST_F(FaviconHandlerManifestsEnabledTest,
AddManifestViaJavascriptWhileDatabaseLookup) {
const std::vector<favicon::FaviconURL> kManifestIcons = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL64x64, WEB_MANIFEST_ICON, kEmptySizes),
};
delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
@@ -1828,7 +1891,8 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL12x12);
std::unique_ptr<FaviconHandler> handler =
- RunHandlerWithSimpleFaviconCandidates({kIconURL12x12});
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12},
+ /*manifest_url=*/GURL());
ASSERT_THAT(favicon_service_.fake()->db_requests(),
ElementsAre(kPageURL, kIconURL12x12));
// Database lookup for |kIconURL12x12| is ongoing.
@@ -1837,9 +1901,9 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
// Simulate the page changing it's manifest URL to |kManifestURL| via
// Javascript.
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _));
- handler->OnUpdateCandidates(kPageURL,
- {FaviconURL(kIconURL12x12, FAVICON, kEmptySizes)},
- kManifestURL);
+ handler->OnUpdateCandidates(
+ kPageURL, {FaviconURL(kIconURL12x12, TOUCH_ICON, kEmptySizes)},
+ kManifestURL);
// Complete the lookup.
EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
base::RunLoop().RunUntilIdle();
@@ -1867,7 +1931,8 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
delegate_.fake_image_downloader().SetRunCallbackManuallyForUrl(kIconURL16x16);
std::unique_ptr<FaviconHandler> handler =
- RunHandlerWithSimpleFaviconCandidates({kIconURL16x16});
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
+ /*manifest_url=*/GURL());
ASSERT_TRUE(VerifyAndClearExpectations());
ASSERT_TRUE(delegate_.fake_image_downloader().HasPendingManualCallback());
@@ -1877,9 +1942,9 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _));
- handler->OnUpdateCandidates(kPageURL,
- {FaviconURL(kIconURL16x16, FAVICON, kEmptySizes)},
- kManifestURL);
+ handler->OnUpdateCandidates(
+ kPageURL, {FaviconURL(kIconURL16x16, TOUCH_ICON, kEmptySizes)},
+ kManifestURL);
// Finalizes download, which should be thrown away as the manifest URL was
// provided.
@@ -1907,7 +1972,8 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0);
std::unique_ptr<FaviconHandler> handler =
- RunHandlerWithSimpleFaviconCandidates({kIconURL16x16});
+ RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
+ /*manifest_url=*/GURL());
ASSERT_TRUE(VerifyAndClearExpectations());
ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
@@ -1915,9 +1981,9 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL16x16, _, _));
EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _));
- handler->OnUpdateCandidates(kPageURL,
- {FaviconURL(kIconURL16x16, FAVICON, kEmptySizes)},
- kManifestURL);
+ handler->OnUpdateCandidates(
+ kPageURL, {FaviconURL(kIconURL16x16, TOUCH_ICON, kEmptySizes)},
+ kManifestURL);
// Finalizes lookup, which should be thrown away as the manifest URLs was
// provided.
@@ -1941,7 +2007,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
const GURL kManifestURL1("http://www.google.com/manifest1.json");
const GURL kManifestURL2("http://www.google.com/manifest2.json");
const std::vector<favicon::FaviconURL> kManifestIcons = {
- FaviconURL(kIconURL64x64, FAVICON, kEmptySizes),
+ FaviconURL(kIconURL64x64, WEB_MANIFEST_ICON, kEmptySizes),
};
delegate_.fake_manifest_downloader().Add(kManifestURL1, kManifestIcons);
@@ -1951,7 +2017,7 @@ TEST_F(FaviconHandlerManifestsEnabledTest,
delegate_.fake_image_downloader().SetRunCallbackManuallyForUrl(kIconURL64x64);
std::unique_ptr<FaviconHandler> handler =
- RunHandlerWithSimpleFaviconCandidates(URLVector(), kManifestURL1);
+ RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL1);
EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL1, kIconURL64x64));
ASSERT_TRUE(VerifyAndClearExpectations());
diff --git a/chromium/components/favicon/core/favicon_service.h b/chromium/components/favicon/core/favicon_service.h
index d3618009349..60552b13691 100644
--- a/chromium/components/favicon/core/favicon_service.h
+++ b/chromium/components/favicon/core/favicon_service.h
@@ -139,6 +139,15 @@ class FaviconService : public KeyedService {
// Marks all types of favicon for the page as being out of date.
virtual void SetFaviconOutOfDateForPage(const GURL& page_url) = 0;
+ // Mark that the on-demand favicon at |icon_url| was requested now. This
+ // postpones the automatic eviction of the favicon from the database. Not all
+ // calls end up in a write into the DB:
+ // - It is no-op if the bitmaps are not stored using SetOnDemandFavicons().
+ // - The updates of the "last requested time" have limited frequency for each
+ // particular favicon (e.g. once per week). This limits the overhead of
+ // cache management for on-demand favicons.
+ virtual void TouchOnDemandFavicon(const GURL& icon_url) = 0;
+
// Allows the importer to set many favicons for many pages at once. The pages
// must exist, any favicon sets for unknown pages will be discarded. Existing
// favicons will not be overwritten.
@@ -171,19 +180,29 @@ class FaviconService : public KeyedService {
favicon_base::IconType icon_type,
const gfx::Image& image) = 0;
- // Same as SetFavicons() with three differences:
+ // Same as SetFavicons with three differences:
// 1) It will be a no-op if there is an existing cached favicon for *any* type
// for |page_url|.
// 2) If |icon_url| is known to the database, |bitmaps| will be ignored (i.e.
// the icon won't be overwritten) but the mappings from |page_url| to
// |icon_url| will be stored (conditioned to point 1 above).
- // 3) If |icon_url| is stored, it will be marked as expired.
+ // 3) If |icon_url| is stored, it will be marked as "on-demand".
+ //
+ // On-demand favicons are those that are fetched without visiting their page.
+ // For this reason, their life-time cannot be bound to the life-time of the
+ // corresponding visit in history.
+ // - These bitmaps are evicted from the database based on the last time they
+ // get requested. The last requested time is initially set to Now() and is
+ // further updated by calling TouchOnDemandFavicon().
+ // - Furthermore, on-demand bitmaps are immediately marked as expired. Hence,
+ // they are always replaced by standard favicons whenever their page gets
+ // visited.
// The callback will receive whether the write actually happened.
- virtual void SetLastResortFavicons(const GURL& page_url,
- const GURL& icon_url,
- favicon_base::IconType icon_type,
- const gfx::Image& image,
- base::Callback<void(bool)> callback) = 0;
+ virtual void SetOnDemandFavicons(const GURL& page_url,
+ const GURL& icon_url,
+ favicon_base::IconType icon_type,
+ const gfx::Image& image,
+ base::Callback<void(bool)> callback) = 0;
// Avoid repeated requests to download missing favicon.
virtual void UnableToDownloadFavicon(const GURL& icon_url) = 0;
diff --git a/chromium/components/favicon/core/favicon_service_impl.cc b/chromium/components/favicon/core/favicon_service_impl.cc
index 55ac6b22ef3..e267981e81e 100644
--- a/chromium/components/favicon/core/favicon_service_impl.cc
+++ b/chromium/components/favicon/core/favicon_service_impl.cc
@@ -44,10 +44,8 @@ std::vector<SkBitmap> ExtractSkBitmapsToStore(const gfx::Image& image) {
const std::vector<float> favicon_scales = favicon_base::GetFaviconScales();
for (size_t i = 0; i < image_reps.size(); ++i) {
// Don't save if the scale isn't one of supported favicon scales.
- if (std::find(favicon_scales.begin(), favicon_scales.end(),
- image_reps[i].scale()) == favicon_scales.end()) {
+ if (!base::ContainsValue(favicon_scales, image_reps[i].scale()))
continue;
- }
bitmaps.push_back(image_reps[i].sk_bitmap());
}
return bitmaps;
@@ -209,6 +207,10 @@ void FaviconServiceImpl::SetFaviconOutOfDateForPage(const GURL& page_url) {
history_service_->SetFaviconsOutOfDateForPage(page_url);
}
+void FaviconServiceImpl::TouchOnDemandFavicon(const GURL& icon_url) {
+ history_service_->TouchOnDemandFavicon(icon_url);
+}
+
void FaviconServiceImpl::SetImportedFavicons(
const favicon_base::FaviconUsageDataList& favicon_usage) {
history_service_->SetImportedFavicons(favicon_usage);
@@ -232,13 +234,13 @@ void FaviconServiceImpl::SetFavicons(const GURL& page_url,
ExtractSkBitmapsToStore(image));
}
-void FaviconServiceImpl::SetLastResortFavicons(
+void FaviconServiceImpl::SetOnDemandFavicons(
const GURL& page_url,
const GURL& icon_url,
favicon_base::IconType icon_type,
const gfx::Image& image,
base::Callback<void(bool)> callback) {
- history_service_->SetLastResortFavicons(
+ history_service_->SetOnDemandFavicons(
page_url, icon_type, icon_url, ExtractSkBitmapsToStore(image), callback);
}
diff --git a/chromium/components/favicon/core/favicon_service_impl.h b/chromium/components/favicon/core/favicon_service_impl.h
index e1cb63c4b08..7ad2d0ac53e 100644
--- a/chromium/components/favicon/core/favicon_service_impl.h
+++ b/chromium/components/favicon/core/favicon_service_impl.h
@@ -91,6 +91,7 @@ class FaviconServiceImpl : public FaviconService {
const favicon_base::FaviconRawBitmapCallback& callback,
base::CancelableTaskTracker* tracker) override;
void SetFaviconOutOfDateForPage(const GURL& page_url) override;
+ void TouchOnDemandFavicon(const GURL& icon_url) override;
void SetImportedFavicons(
const favicon_base::FaviconUsageDataList& favicon_usage) override;
void MergeFavicon(const GURL& page_url,
@@ -102,11 +103,11 @@ class FaviconServiceImpl : public FaviconService {
const GURL& icon_url,
favicon_base::IconType icon_type,
const gfx::Image& image) override;
- void SetLastResortFavicons(const GURL& page_url,
- const GURL& icon_url,
- favicon_base::IconType icon_type,
- const gfx::Image& image,
- base::Callback<void(bool)> callback) override;
+ void SetOnDemandFavicons(const GURL& page_url,
+ const GURL& icon_url,
+ favicon_base::IconType icon_type,
+ const gfx::Image& image,
+ base::Callback<void(bool)> callback) override;
void UnableToDownloadFavicon(const GURL& icon_url) override;
bool WasUnableToDownloadFavicon(const GURL& icon_url) const override;
void ClearUnableToDownloadFavicons() override;
diff --git a/chromium/components/favicon/core/features.cc b/chromium/components/favicon/core/features.cc
index e69e139dd10..8a3e02b48cb 100644
--- a/chromium/components/favicon/core/features.cc
+++ b/chromium/components/favicon/core/features.cc
@@ -9,6 +9,6 @@
namespace favicon {
const base::Feature kFaviconsFromWebManifest{"FaviconsFromWebManifest",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
} // namespace favicon
diff --git a/chromium/components/favicon/core/large_icon_service.cc b/chromium/components/favicon/core/large_icon_service.cc
index a6587e9d381..72886e7e838 100644
--- a/chromium/components/favicon/core/large_icon_service.cc
+++ b/chromium/components/favicon/core/large_icon_service.cc
@@ -25,6 +25,7 @@
#include "components/favicon_base/favicon_types.h"
#include "components/favicon_base/favicon_util.h"
#include "components/image_fetcher/core/request_metadata.h"
+#include "net/base/network_change_notifier.h"
#include "skia/ext/image_operations.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
@@ -129,6 +130,7 @@ void ProcessIconOnBackgroundThread(
int desired_size,
favicon_base::FaviconRawBitmapResult* raw_result,
SkBitmap* bitmap,
+ GURL* icon_url,
favicon_base::FallbackIconStyle* fallback_icon_style) {
if (IsDbResultAdequate(db_result, min_source_size)) {
gfx::Image image;
@@ -144,6 +146,9 @@ void ProcessIconOnBackgroundThread(
if (bitmap) {
*bitmap = image.AsBitmap();
}
+ if (icon_url) {
+ *icon_url = db_result.icon_url;
+ }
return;
}
}
@@ -205,6 +210,7 @@ class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> {
favicon_base::FaviconRawBitmapResult raw_bitmap_result_;
SkBitmap bitmap_result_;
+ GURL icon_url_;
std::unique_ptr<favicon_base::FallbackIconStyle> fallback_icon_style_;
DISALLOW_COPY_AND_ASSIGN(LargeIconWorker);
@@ -237,6 +243,7 @@ void LargeIconWorker::OnIconLookupComplete(
min_source_size_in_pixel_, desired_size_in_pixel_,
raw_bitmap_callback_ ? &raw_bitmap_result_ : nullptr,
image_callback_ ? &bitmap_result_ : nullptr,
+ image_callback_ ? &icon_url_ : nullptr,
fallback_icon_style_.get()),
base::Bind(&LargeIconWorker::OnIconProcessingComplete, this));
}
@@ -256,7 +263,7 @@ void LargeIconWorker::OnIconProcessingComplete() {
if (!bitmap_result_.isNull()) {
image_callback_.Run(favicon_base::LargeIconImageResult(
- gfx::Image::CreateFrom1xBitmap(bitmap_result_)));
+ gfx::Image::CreateFrom1xBitmap(bitmap_result_), icon_url_));
return;
}
image_callback_.Run(
@@ -267,18 +274,31 @@ void ReportDownloadedSize(int size) {
UMA_HISTOGRAM_COUNTS_1000("Favicons.LargeIconService.DownloadedSize", size);
}
+void OnSetOnDemandFaviconComplete(
+ const favicon_base::GoogleFaviconServerCallback& callback,
+ bool success) {
+ callback.Run(
+ success
+ ? favicon_base::GoogleFaviconServerRequestStatus::SUCCESS
+ : favicon_base::GoogleFaviconServerRequestStatus::FAILURE_ON_WRITE);
+}
+
void OnFetchIconFromGoogleServerComplete(
FaviconService* favicon_service,
const GURL& page_url,
- const base::Callback<void(bool success)>& callback,
+ const favicon_base::GoogleFaviconServerCallback& callback,
const std::string& server_request_url,
const gfx::Image& image,
const image_fetcher::RequestMetadata& metadata) {
if (image.IsEmpty()) {
DLOG(WARNING) << "large icon server fetch empty " << server_request_url;
favicon_service->UnableToDownloadFavicon(GURL(server_request_url));
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(callback, false));
+ callback.Run(metadata.http_response_code ==
+ net::URLFetcher::RESPONSE_CODE_INVALID
+ ? favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_CONNECTION_ERROR
+ : favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_HTTP_ERROR);
ReportDownloadedSize(0);
return;
}
@@ -297,9 +317,9 @@ void OnFetchIconFromGoogleServerComplete(
// something else could've been written). By marking the icons initially
// expired (out-of-date), they will be refetched when we visit the original
// page any time in the future.
- favicon_service->SetLastResortFavicons(page_url, GURL(original_icon_url),
- favicon_base::IconType::TOUCH_ICON,
- image, callback);
+ favicon_service->SetOnDemandFavicons(
+ page_url, GURL(original_icon_url), favicon_base::IconType::TOUCH_ICON,
+ image, base::Bind(&OnSetOnDemandFaviconComplete, callback));
}
} // namespace
@@ -350,49 +370,47 @@ void LargeIconService::
int min_source_size_in_pixel,
int desired_size_in_pixel,
bool may_page_url_be_private,
- const base::Callback<void(bool success)>& callback) {
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ const favicon_base::GoogleFaviconServerCallback& callback) {
DCHECK_LE(0, min_source_size_in_pixel);
+ if (net::NetworkChangeNotifier::IsOffline()) {
+ // By exiting early when offline, we avoid caching the failure and thus
+ // allow icon fetches later when coming back online.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_CONNECTION_ERROR));
+ return;
+ }
+
const GURL trimmed_page_url = TrimPageUrlForGoogleServer(page_url);
const GURL server_request_url = GetRequestUrlForGoogleServerV2(
trimmed_page_url, min_source_size_in_pixel, desired_size_in_pixel,
may_page_url_be_private);
- // Do not download if the URL is invalid after trimming, or there is a
- // previous cache miss recorded for |server_request_url|.
if (!server_request_url.is_valid() || !trimmed_page_url.is_valid() ||
- !image_fetcher_ ||
- favicon_service_->WasUnableToDownloadFavicon(server_request_url)) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(callback, false));
+ !image_fetcher_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ callback,
+ favicon_base::GoogleFaviconServerRequestStatus::FAILURE_INVALID));
+ return;
+ }
+
+ // Do not download if there is a previous cache miss recorded for
+ // |server_request_url|.
+ if (favicon_service_->WasUnableToDownloadFavicon(server_request_url)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_HTTP_ERROR_CACHED));
return;
}
image_fetcher_->SetDataUseServiceName(
data_use_measurement::DataUseUserData::LARGE_ICON_SERVICE);
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("favicon_component", R"(
- semantics {
- sender: "Favicon Component"
- description:
- "Sends a request to a Google server to retrieve the favicon bitmap "
- "for a page URL."
- trigger:
- "A request can be sent if Chrome does not have a favicon for a "
- "particular page. This is done in two scenarios:\n"
- " 1- For articles suggestions on the new tab page (URLs are public "
- " and provided by Google).\n"
- " 2- For server-suggested most visited tiles on the new tab page "
- " (User gets these URLs from Google, only if history sync is "
- " enabled)."
- data: "Page URL and desired icon size."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: false
- setting: "This feature cannot be disabled by settings."
- policy_exception_justification: "Not implemented."
- })");
image_fetcher_->StartOrQueueNetworkRequest(
server_request_url.spec(), server_request_url,
base::Bind(&OnFetchIconFromGoogleServerComplete, favicon_service_,
@@ -400,6 +418,10 @@ void LargeIconService::
traffic_annotation);
}
+void LargeIconService::TouchIconFromGoogleServer(const GURL& icon_url) {
+ favicon_service_->TouchOnDemandFavicon(icon_url);
+}
+
base::CancelableTaskTracker::TaskId
LargeIconService::GetLargeIconOrFallbackStyleImpl(
const GURL& page_url,
@@ -415,12 +437,14 @@ LargeIconService::GetLargeIconOrFallbackStyleImpl(
min_source_size_in_pixel, desired_size_in_pixel, raw_bitmap_callback,
image_callback, background_task_runner_, tracker);
+ int max_size_in_pixel =
+ std::max(desired_size_in_pixel, min_source_size_in_pixel);
// TODO(beaudoin): For now this is just a wrapper around
// GetLargestRawFaviconForPageURL. Add the logic required to select the best
// possible large icon. Also add logic to fetch-on-demand when the URL of
// a large icon is known but its bitmap is not available.
return favicon_service_->GetLargestRawFaviconForPageURL(
- page_url, large_icon_types_, min_source_size_in_pixel,
+ page_url, large_icon_types_, max_size_in_pixel,
base::Bind(&LargeIconWorker::OnIconLookupComplete, worker), tracker);
}
diff --git a/chromium/components/favicon/core/large_icon_service.h b/chromium/components/favicon/core/large_icon_service.h
index b25ab3e3018..ec79fd54da1 100644
--- a/chromium/components/favicon/core/large_icon_service.h
+++ b/chromium/components/favicon/core/large_icon_service.h
@@ -12,6 +12,7 @@
#include "components/favicon_base/favicon_callback.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
class GURL;
@@ -38,15 +39,19 @@ class LargeIconService : public KeyedService {
~LargeIconService() override;
// Requests the best large icon for the page at |page_url|.
- // Case 1. An icon exists whose size is >= |min_source_size_in_pixel|:
+ // Case 1. An icon exists whose size is >= MAX(|min_source_size_in_pixel|,
+ // |desired_size_in_pixel|):
// - If |desired_size_in_pixel| == 0: returns icon as is.
// - Else: returns the icon resized to |desired_size_in_pixel|.
- // Case 2. An icon exists whose size is < |min_source_size_in_pixel|:
+ // Case 2. An icon exists whose size is >= |min_source_size_in_pixel| and <
+ // |desired_size_in_pixel|:
+ // - Same as 1 with the biggest icon.
+ // Case 4. An icon exists whose size is < |min_source_size_in_pixel|:
// - Extracts dominant color of smaller image, returns a fallback icon style
// that has a matching background.
- // Case 3. No icon exists.
+ // Case 5. No icon exists.
// - Returns the default fallback icon style.
- // For cases 2 and 3, this function returns the style of the fallback icon
+ // For cases 4 and 5, this function returns the style of the fallback icon
// instead of rendering an icon so clients can render the icon themselves.
// TODO(jkrcal): Rename to GetLargeIconRawBitmapOrFallbackStyle.
base::CancelableTaskTracker::TaskId GetLargeIconOrFallbackStyle(
@@ -94,7 +99,16 @@ class LargeIconService : public KeyedService {
int min_source_size_in_pixel,
int desired_size_in_pixel,
bool may_page_url_be_private,
- const base::Callback<void(bool success)>& callback);
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ const favicon_base::GoogleFaviconServerCallback& callback);
+
+ // Update the time that the icon at |icon_url| was requested. This should be
+ // called after obtaining the icon by GetLargeIcon*OrFallbackStyle() for any
+ // icon that _may_ originate from the Google favicon server (i.e. if the
+ // caller uses
+ // GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache()). This
+ // postpones the automatic eviction of the favicon from the database.
+ void TouchIconFromGoogleServer(const GURL& icon_url);
private:
base::CancelableTaskTracker::TaskId GetLargeIconOrFallbackStyleImpl(
diff --git a/chromium/components/favicon/core/large_icon_service_unittest.cc b/chromium/components/favicon/core/large_icon_service_unittest.cc
index 79f9a6bca87..9646cd98ebe 100644
--- a/chromium/components/favicon/core/large_icon_service_unittest.cc
+++ b/chromium/components/favicon/core/large_icon_service_unittest.cc
@@ -24,6 +24,7 @@
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/image_fetcher/core/request_metadata.h"
#include "components/variations/variations_params_manager.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -150,23 +151,24 @@ TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServer) {
EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
- base::MockCallback<base::Callback<void(bool success)>> callback;
+ base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
EXPECT_CALL(*mock_image_fetcher_,
StartOrQueueNetworkRequest(_, kExpectedServerUrl, _, _))
.WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap(
CreateTestSkBitmap(64, 64, kTestColor))));
EXPECT_CALL(mock_favicon_service_,
- SetLastResortFavicons(GURL(kDummyUrl), kExpectedServerUrl,
- favicon_base::IconType::TOUCH_ICON, _, _))
+ SetOnDemandFavicons(GURL(kDummyUrl), kExpectedServerUrl,
+ favicon_base::IconType::TOUCH_ICON, _, _))
.WillOnce(PostBoolReply(true));
large_icon_service_
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
- callback.Get());
+ TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
- EXPECT_CALL(callback, Run(true));
+ EXPECT_CALL(callback,
+ Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
base::RunLoop().RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"Favicons.LargeIconService.DownloadedSize", 64, /*expected_count=*/1);
@@ -187,23 +189,24 @@ TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerWithCustomUrl) {
EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
- base::MockCallback<base::Callback<void(bool success)>> callback;
+ base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
EXPECT_CALL(*mock_image_fetcher_,
StartOrQueueNetworkRequest(_, kExpectedServerUrl, _, _))
.WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap(
CreateTestSkBitmap(64, 64, kTestColor))));
EXPECT_CALL(mock_favicon_service_,
- SetLastResortFavicons(GURL(kDummyUrl), kExpectedServerUrl,
- favicon_base::IconType::TOUCH_ICON, _, _))
+ SetOnDemandFavicons(GURL(kDummyUrl), kExpectedServerUrl,
+ favicon_base::IconType::TOUCH_ICON, _, _))
.WillOnce(PostBoolReply(true));
large_icon_service_
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
- callback.Get());
+ TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
- EXPECT_CALL(callback, Run(true));
+ EXPECT_CALL(callback,
+ Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
base::RunLoop().RunUntilIdle();
}
@@ -223,18 +226,19 @@ TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerWithOriginalUrl) {
CreateTestSkBitmap(64, 64, kTestColor)),
expected_metadata));
EXPECT_CALL(mock_favicon_service_,
- SetLastResortFavicons(GURL(kDummyUrl), kExpectedOriginalUrl,
- favicon_base::IconType::TOUCH_ICON, _, _))
+ SetOnDemandFavicons(GURL(kDummyUrl), kExpectedOriginalUrl,
+ favicon_base::IconType::TOUCH_ICON, _, _))
.WillOnce(PostBoolReply(true));
- base::MockCallback<base::Callback<void(bool success)>> callback;
+ base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
large_icon_service_
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
- callback.Get());
+ TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
- EXPECT_CALL(callback, Run(true));
+ EXPECT_CALL(callback,
+ Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
base::RunLoop().RunUntilIdle();
}
@@ -251,13 +255,14 @@ TEST_F(LargeIconServiceTest, ShouldTrimQueryParametersForGoogleServer) {
CreateTestSkBitmap(64, 64, kTestColor))));
// Verify that the non-trimmed page URL is used when writing to the database.
EXPECT_CALL(mock_favicon_service_,
- SetLastResortFavicons(_, kExpectedServerUrl, _, _, _));
+ SetOnDemandFavicons(_, kExpectedServerUrl, _, _, _));
large_icon_service_
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
GURL(kDummyUrlWithQuery), /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
- base::Callback<void(bool success)>());
+ TRAFFIC_ANNOTATION_FOR_TESTS,
+ favicon_base::GoogleFaviconServerCallback());
base::RunLoop().RunUntilIdle();
}
@@ -270,15 +275,16 @@ TEST_F(LargeIconServiceTest, ShouldNotCheckOnPublicUrls) {
_, Property(&GURL::query, Not(HasSubstr("check_seen=true"))), _, _))
.WillOnce(PostFetchReply(gfx::Image()));
- base::MockCallback<base::Callback<void(bool success)>> callback;
+ base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
large_icon_service_
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/false,
- callback.Get());
+ TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
- EXPECT_CALL(callback, Run(false));
+ EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_CONNECTION_ERROR));
base::RunLoop().RunUntilIdle();
}
@@ -288,15 +294,17 @@ TEST_F(LargeIconServiceTest, ShouldNotQueryGoogleServerIfInvalidScheme) {
EXPECT_CALL(*mock_image_fetcher_, StartOrQueueNetworkRequest(_, _, _, _))
.Times(0);
- base::MockCallback<base::Callback<void(bool success)>> callback;
+ base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
large_icon_service_
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
GURL(kDummyFtpUrl), /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
- callback.Get());
+ TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
- EXPECT_CALL(callback, Run(false));
+ EXPECT_CALL(
+ callback,
+ Run(favicon_base::GoogleFaviconServerRequestStatus::FAILURE_INVALID));
base::RunLoop().RunUntilIdle();
EXPECT_THAT(histogram_tester_.GetAllSamples(
"Favicons.LargeIconService.DownloadedSize"),
@@ -310,10 +318,10 @@ TEST_F(LargeIconServiceTest, ShouldReportUnavailableIfFetchFromServerFails) {
"&check_seen=true&size=61&min_size=42&max_size=122"
"&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/");
- EXPECT_CALL(mock_favicon_service_, SetLastResortFavicons(_, _, _, _, _))
+ EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _))
.Times(0);
- base::MockCallback<base::Callback<void(bool success)>> callback;
+ base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
EXPECT_CALL(*mock_image_fetcher_,
StartOrQueueNetworkRequest(_, kExpectedServerUrl, _, _))
.WillOnce(PostFetchReply(gfx::Image()));
@@ -324,9 +332,10 @@ TEST_F(LargeIconServiceTest, ShouldReportUnavailableIfFetchFromServerFails) {
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
kDummyUrlWithQuery, /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
- callback.Get());
+ TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
- EXPECT_CALL(callback, Run(false));
+ EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_CONNECTION_ERROR));
base::RunLoop().RunUntilIdle();
// Verify that download failure gets recorded.
histogram_tester_.ExpectUniqueSample(
@@ -345,17 +354,18 @@ TEST_F(LargeIconServiceTest, ShouldNotGetFromGoogleServerIfUnavailable) {
EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
EXPECT_CALL(*mock_image_fetcher_, StartOrQueueNetworkRequest(_, _, _, _))
.Times(0);
- EXPECT_CALL(mock_favicon_service_, SetLastResortFavicons(_, _, _, _, _))
+ EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _))
.Times(0);
- base::MockCallback<base::Callback<void(bool success)>> callback;
+ base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
large_icon_service_
.GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
/*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
- callback.Get());
+ TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
- EXPECT_CALL(callback, Run(false));
+ EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_HTTP_ERROR_CACHED));
base::RunLoop().RunUntilIdle();
EXPECT_THAT(histogram_tester_.GetAllSamples(
"Favicons.LargeIconService.DownloadedSize"),
@@ -401,6 +411,7 @@ class LargeIconServiceGetterTest : public LargeIconServiceTest,
if (!result.image.IsEmpty()) {
returned_bitmap_size_ =
base::MakeUnique<gfx::Size>(result.image.ToImageSkia()->size());
+ ASSERT_TRUE(result.icon_url.is_valid());
}
StoreFallbackStyle(result.fallback_icon_style.get());
}
diff --git a/chromium/components/favicon_base/BUILD.gn b/chromium/components/favicon_base/BUILD.gn
index 3050e5ca967..b9ad7c40c4f 100644
--- a/chromium/components/favicon_base/BUILD.gn
+++ b/chromium/components/favicon_base/BUILD.gn
@@ -6,8 +6,6 @@ static_library("favicon_base") {
sources = [
"fallback_icon_style.cc",
"fallback_icon_style.h",
- "fallback_icon_url_parser.cc",
- "fallback_icon_url_parser.h",
"favicon_callback.h",
"favicon_types.cc",
"favicon_types.h",
@@ -17,8 +15,6 @@ static_library("favicon_base") {
"favicon_usage_data.h",
"favicon_util.cc",
"favicon_util.h",
- "large_icon_url_parser.cc",
- "large_icon_url_parser.h",
"select_favicon_frames.cc",
"select_favicon_frames.h",
]
@@ -40,9 +36,7 @@ static_library("favicon_base") {
source_set("unit_tests") {
testonly = true
sources = [
- "fallback_icon_url_parser_unittest.cc",
"favicon_url_parser_unittest.cc",
- "large_icon_url_parser_unittest.cc",
"select_favicon_frames_unittest.cc",
]
diff --git a/chromium/components/favicon_base/fallback_icon_style.cc b/chromium/components/favicon_base/fallback_icon_style.cc
index cbb478b1a17..c85d768e68f 100644
--- a/chromium/components/favicon_base/fallback_icon_style.cc
+++ b/chromium/components/favicon_base/fallback_icon_style.cc
@@ -13,10 +13,6 @@ namespace favicon_base {
namespace {
-// Luma threshold for background color determine whether to use dark or light
-// text color.
-const uint8_t kDarkTextLumaThreshold = 190;
-
// The maximum lightness of the background color to ensure light text is
// readable.
const double kMaxBackgroundColorLightness = 0.67;
@@ -24,20 +20,14 @@ const double kMinBackgroundColorLightness = 0.15;
// Default values for FallbackIconStyle.
const SkColor kDefaultBackgroundColor = SkColorSetRGB(0x78, 0x78, 0x78);
-const SkColor kDefaultTextColorDark = SK_ColorBLACK;
-const SkColor kDefaultTextColorLight = SK_ColorWHITE;
-const double kDefaultFontSizeRatio = 0.44;
-const double kDefaultRoundness = 0; // Square. Round corners are applied
- // externally (Javascript or Java).
+const SkColor kDefaultTextColor = SK_ColorWHITE;
} // namespace
FallbackIconStyle::FallbackIconStyle()
: background_color(kDefaultBackgroundColor),
is_default_background_color(true),
- text_color(kDefaultTextColorLight),
- font_size_ratio(kDefaultFontSizeRatio),
- roundness(kDefaultRoundness) {}
+ text_color(kDefaultTextColor) {}
FallbackIconStyle::~FallbackIconStyle() {
}
@@ -45,21 +35,7 @@ FallbackIconStyle::~FallbackIconStyle() {
bool FallbackIconStyle::operator==(const FallbackIconStyle& other) const {
return background_color == other.background_color &&
is_default_background_color == other.is_default_background_color &&
- text_color == other.text_color &&
- font_size_ratio == other.font_size_ratio &&
- roundness == other.roundness;
-}
-
-void MatchFallbackIconTextColorAgainstBackgroundColor(
- FallbackIconStyle* style) {
- const uint8_t luma = color_utils::GetLuma(style->background_color);
- style->text_color = (luma >= kDarkTextLumaThreshold) ?
- kDefaultTextColorDark : kDefaultTextColorLight;
-}
-
-bool ValidateFallbackIconStyle(const FallbackIconStyle& style) {
- return style.font_size_ratio >= 0.0 && style.font_size_ratio <= 1.0 &&
- style.roundness >= 0.0 && style.roundness <= 1.0;
+ text_color == other.text_color;
}
void SetDominantColorAsBackground(
diff --git a/chromium/components/favicon_base/fallback_icon_style.h b/chromium/components/favicon_base/fallback_icon_style.h
index 925773080eb..0680d54eab6 100644
--- a/chromium/components/favicon_base/fallback_icon_style.h
+++ b/chromium/components/favicon_base/fallback_icon_style.h
@@ -17,8 +17,6 @@ struct FallbackIconStyle {
FallbackIconStyle();
~FallbackIconStyle();
- // If any member changes, also update FallbackIconStyleBuilder.
-
// Icon background fill color.
SkColor background_color;
bool is_default_background_color;
@@ -26,21 +24,9 @@ struct FallbackIconStyle {
// Icon text color.
SkColor text_color;
- // Ratio in [0.0, 1.0] of the text font size (pixels) to the icon size.
- double font_size_ratio;
-
- // The roundness of the icon's corners. 0 => square icon, 1 => circle icon.
- double roundness;
-
bool operator==(const FallbackIconStyle& other) const;
};
-// Reassigns |style|'s |text_color| to matches well against |background_color|.
-void MatchFallbackIconTextColorAgainstBackgroundColor(FallbackIconStyle* style);
-
-// Returns whether |style| values are within bounds.
-bool ValidateFallbackIconStyle(const FallbackIconStyle& style);
-
// Set |style|'s background color to the dominant color of |bitmap_data|,
// clamping luminance down to a reasonable maximum value so that light text is
// readable.
diff --git a/chromium/components/favicon_base/fallback_icon_url_parser.cc b/chromium/components/favicon_base/fallback_icon_url_parser.cc
deleted file mode 100644
index 214c88ff9df..00000000000
--- a/chromium/components/favicon_base/fallback_icon_url_parser.cc
+++ /dev/null
@@ -1,142 +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/favicon_base/fallback_icon_url_parser.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "third_party/skia/include/utils/SkParse.h"
-#include "ui/gfx/favicon_size.h"
-
-namespace {
-
-// List of sizes corresponding to RGB, ARGB, RRGGBB, AARRGGBB.
-const size_t kValidHexColorSizes[] = {3, 4, 6, 8};
-
-// Returns whether |color_str| is a valid CSS color in hex format if we prepend
-// '#', i.e., whether |color_str| matches /^[0-9A-Fa-f]{3,4,6,8}$/.
-bool IsHexColorString(const std::string& color_str) {
- size_t len = color_str.length();
- const size_t* end = kValidHexColorSizes + arraysize(kValidHexColorSizes);
- if (std::find(kValidHexColorSizes, end, len) == end)
- return false;
- for (auto ch : color_str) {
- if (!base::IsHexDigit(ch))
- return false;
- }
- return true;
-}
-
-} // namespace
-
-namespace chrome {
-
-ParsedFallbackIconPath::ParsedFallbackIconPath()
- : size_in_pixels_(gfx::kFaviconSize) {
-}
-
-ParsedFallbackIconPath::~ParsedFallbackIconPath() {
-}
-
-bool ParsedFallbackIconPath::Parse(const std::string& path) {
- if (path.empty())
- return false;
-
- size_t slash = path.find("/", 0);
- if (slash == std::string::npos)
- return false;
- std::string spec_str = path.substr(0, slash);
- if (!ParseSpecs(spec_str, &size_in_pixels_, &style_))
- return false; // Parse failed.
-
- // Need to store the index of the URL field, so Instant Extended can translate
- // fallback icon URLs using advanced parameters.
- // Example:
- // "chrome-search://fallback-icon/48/<renderer-id>/<most-visited-id>"
- // would be translated to:
- // "chrome-search://fallback-icon/48/<most-visited-item-with-given-id>".
- path_index_ = slash + 1;
- url_string_ = path.substr(path_index_);
- return true;
-}
-
-// static
-bool ParsedFallbackIconPath::ParseSpecs(
- const std::string& specs_str,
- int *size,
- favicon_base::FallbackIconStyle* style) {
- DCHECK(size);
- DCHECK(style);
-
- std::vector<std::string> tokens = base::SplitString(
- specs_str, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
- if (tokens.size() != 5) // Force "," for empty fields.
- return false;
-
- *size = gfx::kFaviconSize;
- if (!tokens[0].empty() && !base::StringToInt(tokens[0], size))
- return false;
- if (*size <= 0)
- return false;
-
- *style = favicon_base::FallbackIconStyle();
-
- if (!tokens[1].empty()) {
- style->is_default_background_color = false;
- if (!ParseColor(tokens[1], &style->background_color))
- return false;
- }
-
- if (tokens[2].empty())
- favicon_base::MatchFallbackIconTextColorAgainstBackgroundColor(style);
- else if (!ParseColor(tokens[2], &style->text_color))
- return false;
-
- if (!tokens[3].empty() &&
- !base::StringToDouble(tokens[3], &style->font_size_ratio))
- return false;
-
- if (!tokens[4].empty() && !base::StringToDouble(tokens[4], &style->roundness))
- return false;
-
- return favicon_base::ValidateFallbackIconStyle(*style);
-}
-
-// static
-bool ParsedFallbackIconPath::ParseColor(const std::string& color_str,
- SkColor* color) {
- DCHECK(color);
- // Exclude the empty case. Also disallow the '#' prefix, since we want color
- // to be part of an URL, but in URL '#' is used for ref fragment.
- if (color_str.empty() || color_str[0] == '#')
- return false;
-
- // If a valid color hex string is given, prepend '#' and parse (always works).
- // This is unambiguous since named color never only use leters 'a' to 'f'.
- if (IsHexColorString(color_str)) {
- // Default alpha to 0xFF since FindColor() preserves unspecified alpha.
- *color = SK_ColorWHITE;
- // Need temp variable to avoid use-after-free of returned pointer.
- std::string color_str_with_hash = "#" + color_str;
- const char* end = SkParse::FindColor(color_str_with_hash.c_str(), color);
- DCHECK(end && !*end); // Call should succeed and consume string.
- return true;
- }
-
- // Default alpha to 0xFF.
- SkColor temp_color = SK_ColorWHITE;
- const char* end = SkParse::FindColor(color_str.c_str(), &temp_color);
- if (end && !*end) { // Successful if call succeeds and string is consumed.
- *color = temp_color;
- return true;
- }
- return false;
-}
-
-} // namespace chrome
diff --git a/chromium/components/favicon_base/fallback_icon_url_parser.h b/chromium/components/favicon_base/fallback_icon_url_parser.h
deleted file mode 100644
index 39a79fc3228..00000000000
--- a/chromium/components/favicon_base/fallback_icon_url_parser.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
-#define COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "components/favicon_base/fallback_icon_style.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace chrome {
-
-class ParsedFallbackIconPath {
- public:
- ParsedFallbackIconPath();
- ~ParsedFallbackIconPath();
-
- const std::string& url_string() const { return url_string_; }
-
- int size_in_pixels() const { return size_in_pixels_; }
-
- const favicon_base::FallbackIconStyle& style() const { return style_; }
-
- size_t path_index() const { return path_index_; }
-
- // Parses |path|, which should be in the format described at the top of the
- // file "chrome/browser/ui/webui/fallback_icon_source.h".
- bool Parse(const std::string& path);
-
- private:
- // Parses |specs_str|, which should be the comma-separated value portion
- // in the format described at the top of the file
- // "chrome/browser/ui/webui/fallback_icon_source.h".
- static bool ParseSpecs(const std::string& specs_str,
- int *size,
- favicon_base::FallbackIconStyle* style);
-
- // Helper to parse color string (e.g., "red", "#f00", "#aB0137"). On success,
- // returns true and writes te result to |*color|.
- static bool ParseColor(const std::string& color_str, SkColor* color);
-
- friend class FallbackIconUrlParserTest;
- FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseColorSuccess);
- FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseColorFailure);
- FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsEmpty);
- FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsPartial);
- FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsFull);
- FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsFailure);
-
- // The page URL string the fallback icon is requested for.
- std::string url_string_;
-
- // The size of the requested fallback icon in pixels.
- int size_in_pixels_;
-
- // Styling specifications of fallback icon.
- favicon_base::FallbackIconStyle style_;
-
- // The index of the first character (relative to the path) where the the URL
- // from which the fallback icon is being requested is located.
- size_t path_index_;
-
- DISALLOW_COPY_AND_ASSIGN(ParsedFallbackIconPath);
-};
-
-} // namespace chrome
-
-#endif // COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
diff --git a/chromium/components/favicon_base/fallback_icon_url_parser_unittest.cc b/chromium/components/favicon_base/fallback_icon_url_parser_unittest.cc
deleted file mode 100644
index 4dcb236adc8..00000000000
--- a/chromium/components/favicon_base/fallback_icon_url_parser_unittest.cc
+++ /dev/null
@@ -1,294 +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/favicon_base/fallback_icon_url_parser.h"
-
-#include <stddef.h>
-
-#include "base/macros.h"
-#include "components/favicon_base/fallback_icon_style.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/favicon_size.h"
-#include "url/gurl.h"
-
-using chrome::ParsedFallbackIconPath;
-using favicon_base::FallbackIconStyle;
-
-namespace chrome {
-
-namespace {
-
-// Default values for FallbackIconStyle, from
-// /components/favicon_base/fallback_icon_style.h
-const SkColor kDefaultBackgroundColor = SkColorSetRGB(0x78, 0x78, 0x78);
-const SkColor kDefaultTextColorDark = SK_ColorBLACK;
-const SkColor kDefaultTextColorLight = SK_ColorWHITE;
-const double kDefaultFontSizeRatio = 0.44;
-const double kDefaultRoundness = 0;
-
-const char kTestUrlStr[] = "https://www.google.ca/imghp?hl=en&tab=wi";
-
-} // namespace
-
-class FallbackIconUrlParserTest : public testing::Test {
- public:
- FallbackIconUrlParserTest() {
- }
-
- bool ParseSpecs(const std::string& specs_str,
- int *size,
- favicon_base::FallbackIconStyle* style) {
- return ParsedFallbackIconPath::ParseSpecs(specs_str, size, style);
- }
-
- bool ParseColor(const std::string& color_str, SkColor* color) {
- return ParsedFallbackIconPath::ParseColor(color_str, color);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FallbackIconUrlParserTest);
-};
-
-TEST_F(FallbackIconUrlParserTest, ParseColorSuccess) {
- SkColor c;
- EXPECT_TRUE(ParseColor("31aBf0f4", &c));
- EXPECT_EQ(SkColorSetARGB(0x31, 0xAB, 0xF0, 0xF4), c);
- EXPECT_TRUE(ParseColor("01aBf0", &c));
- EXPECT_EQ(SkColorSetRGB(0x01, 0xAB, 0xF0), c);
- EXPECT_TRUE(ParseColor("501a", &c));
- EXPECT_EQ(SkColorSetARGB(0x55, 0x00, 0x11, 0xAA), c);
- EXPECT_TRUE(ParseColor("01a", &c));
- EXPECT_EQ(SkColorSetRGB(0x00, 0x11, 0xAA), c);
- EXPECT_TRUE(ParseColor("000000", &c));
- EXPECT_EQ(SkColorSetARGB(0xFF, 0x00, 0x00, 0x00), c);
- EXPECT_TRUE(ParseColor("red", &c));
- EXPECT_EQ(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00), c);
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseColorFailure) {
- const char* test_cases[] = {
- "",
- "00000",
- "000000000",
- " 000000",
- "ABCDEFG",
- "#000",
- "#000000",
- "000000 ",
- "ABCDEFH",
- "#ABCDEF",
- };
- for (size_t i = 0; i < arraysize(test_cases); ++i) {
- SkColor c;
- EXPECT_FALSE(ParseColor(test_cases[i], &c))
- << "test_cases[" << i << "]";
- }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsEmpty) {
- int size;
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs(",,,,", &size, &style));
- EXPECT_EQ(16, size);
- EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
- EXPECT_TRUE(style.is_default_background_color);
- EXPECT_EQ(kDefaultTextColorLight, style.text_color);
- EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
- EXPECT_EQ(kDefaultRoundness, style.roundness);
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsPartial) {
- int size;
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs(",,aCE,,0.1", &size, &style));
- EXPECT_EQ(16, size);
- EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
- EXPECT_TRUE(style.is_default_background_color);
- EXPECT_EQ(SkColorSetRGB(0xAA, 0xCC, 0xEE), style.text_color);
- EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
- EXPECT_EQ(0.1, style.roundness);
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsFull) {
- int size;
-
- {
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs("16,000,f01,0.75,0.25", &size, &style));
- EXPECT_EQ(16, size);
- EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
- EXPECT_FALSE(style.is_default_background_color);
- EXPECT_EQ(SkColorSetRGB(0xff, 0x00, 0x11), style.text_color);
- EXPECT_EQ(0.75, style.font_size_ratio);
- EXPECT_EQ(0.25, style.roundness);
- }
-
- {
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs("48,black,123456,0.5,0.3", &size, &style));
- EXPECT_EQ(48, size);
- EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
- EXPECT_FALSE(style.is_default_background_color);
- EXPECT_EQ(SkColorSetRGB(0x12, 0x34, 0x56), style.text_color);
- EXPECT_EQ(0.5, style.font_size_ratio);
- EXPECT_EQ(0.3, style.roundness);
- }
-
- {
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs("1,000,red,0,0", &size, &style));
- EXPECT_EQ(1, size);
- EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
- EXPECT_FALSE(style.is_default_background_color);
- EXPECT_EQ(SkColorSetRGB(0xFF, 0x00, 0x00), style.text_color);
- EXPECT_EQ(0, style.font_size_ratio);
- EXPECT_EQ(0, style.roundness);
- }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsDefaultTextColor) {
- int size;
-
- {
- // Dark background -> Light text.
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs(",000,,,", &size, &style));
- EXPECT_EQ(kDefaultTextColorLight, style.text_color);
- }
-
- {
- // Light background -> Dark text.
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs(",fff,,,", &size, &style));
- EXPECT_EQ(kDefaultTextColorDark, style.text_color);
- }
-
- {
- // Light background -> Dark text, more params don't matter.
- FallbackIconStyle style;
- EXPECT_TRUE(ParseSpecs("107,fff,,0.3,0.5", &size, &style));
- EXPECT_EQ(kDefaultTextColorDark, style.text_color);
- }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsFailure) {
- const char* test_cases[] = {
- // Need exactly 5 params.
- "",
- "16",
- "16,black",
- "16,black,fff",
- "16,black,fff,0.75",
- ",,,"
- ",,,,,",
- "16,black,fff,0.75,0.25,junk",
- // Don't allow any space.
- "16,black,fff, 0.75,0.25",
- "16,black ,fff,0.75,0.25",
- "16,black,fff,0.75,0.25 ",
- // Adding junk text.
- "16,black,fff,0.75,0.25junk",
- "junk,black,fff,0.75,0.25",
- "16,#junk,fff,0.75,0.25",
- "16,black,junk,0.75,0.25",
- "16,black,fff,junk,0.25",
- "16,black,fff,0.75,junk",
- // Out of bound.
- "0,black,fff,0.75,0.25", // size.
- "4294967296,black,fff,0.75,0.25", // size.
- "-1,black,fff,0.75,0.25", // size.
- "16,black,fff,-0.1,0.25", // font_size_ratio.
- "16,black,fff,1.1,0.25", // font_size_ratio.
- "16,black,fff,0.75,-0.1", // roundness.
- "16,black,fff,0.75,1.1", // roundness.
- };
- for (size_t i = 0; i < arraysize(test_cases); ++i) {
- int size;
- FallbackIconStyle style;
- EXPECT_FALSE(ParseSpecs(test_cases[i], &size, &style))
- << "test_cases[" << i << "]";
-
- }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseFallbackIconPathSuccess) {
- const std::string specs = "31,black,fff,0.75,0.25";
-
- // Everything populated.
- {
- chrome::ParsedFallbackIconPath parsed;
- EXPECT_TRUE(parsed.Parse(specs + "/" + kTestUrlStr));
- EXPECT_EQ(31, parsed.size_in_pixels());
- const favicon_base::FallbackIconStyle& style = parsed.style();
- EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
- EXPECT_FALSE(style.is_default_background_color);
- EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
- EXPECT_EQ(0.75, style.font_size_ratio);
- EXPECT_EQ(0.25, style.roundness);
- EXPECT_EQ(GURL(kTestUrlStr), GURL(parsed.url_string()));
- EXPECT_EQ(specs.length() + 1, parsed.path_index());
- }
-
- // Empty URL.
- {
- chrome::ParsedFallbackIconPath parsed;
- EXPECT_TRUE(parsed.Parse(specs + "/"));
- EXPECT_EQ(31, parsed.size_in_pixels());
- const favicon_base::FallbackIconStyle& style = parsed.style();
- EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
- EXPECT_FALSE(style.is_default_background_color);
- EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
- EXPECT_EQ(0.75, style.font_size_ratio);
- EXPECT_EQ(0.25, style.roundness);
- EXPECT_EQ(GURL(), GURL(parsed.url_string()));
- EXPECT_EQ(specs.length() + 1, parsed.path_index());
- }
-
- // Tolerate invalid URL.
- {
- chrome::ParsedFallbackIconPath parsed;
- EXPECT_TRUE(parsed.Parse(specs + "/NOT A VALID URL"));
- EXPECT_EQ(31, parsed.size_in_pixels());
- const favicon_base::FallbackIconStyle& style = parsed.style();
- EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
- EXPECT_FALSE(style.is_default_background_color);
- EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
- EXPECT_EQ(0.75, style.font_size_ratio);
- EXPECT_EQ(0.25, style.roundness);
- EXPECT_EQ("NOT A VALID URL", parsed.url_string());
- EXPECT_EQ(specs.length() + 1, parsed.path_index());
- }
-
- // Size and style are default.
- {
- std::string specs2 = ",,,,";
- chrome::ParsedFallbackIconPath parsed;
- EXPECT_TRUE(parsed.Parse(specs2 + "/" + kTestUrlStr));
- EXPECT_EQ(gfx::kFaviconSize, parsed.size_in_pixels());
- const favicon_base::FallbackIconStyle& style = parsed.style();
- EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
- EXPECT_TRUE(style.is_default_background_color);
- EXPECT_EQ(kDefaultTextColorLight, style.text_color);
- EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
- EXPECT_EQ(kDefaultRoundness, style.roundness);
- EXPECT_EQ(GURL(kTestUrlStr), GURL(parsed.url_string()));
- EXPECT_EQ(specs2.length() + 1, parsed.path_index());
- }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseFallbackIconPathFailure) {
- const char* test_cases[] = {
- // Bad size.
- "-1,000,fff,0.75,0.25/http://www.google.com/",
- // Bad specs.
- "32,#junk,fff,0.75,0.25/http://www.google.com/",
- };
- for (size_t i = 0; i < arraysize(test_cases); ++i) {
- chrome::ParsedFallbackIconPath parsed;
- EXPECT_FALSE(parsed.Parse(test_cases[i])) << "test_cases[" << i << "]";
- }
-}
-
-} // namespace chrome
diff --git a/chromium/components/favicon_base/favicon_callback.h b/chromium/components/favicon_base/favicon_callback.h
index 28ad5b3206d..318f64c7061 100644
--- a/chromium/components/favicon_base/favicon_callback.h
+++ b/chromium/components/favicon_base/favicon_callback.h
@@ -15,6 +15,7 @@ struct FaviconRawBitmapResult;
struct FaviconImageResult;
struct LargeIconResult;
struct LargeIconImageResult;
+enum class GoogleFaviconServerRequestStatus;
// Callback for functions that can be used to return a |gfx::Image| and the
// |GURL| it is loaded from. They are returned as a |FaviconImageResult| object.
@@ -44,6 +45,9 @@ typedef base::Callback<void(const LargeIconResult&)> LargeIconCallback;
typedef base::Callback<void(const LargeIconImageResult&)>
LargeIconImageCallback;
+typedef base::Callback<void(GoogleFaviconServerRequestStatus)>
+ GoogleFaviconServerCallback;
+
} // namespace favicon_base
#endif // COMPONENTS_FAVICON_BASE_FAVICON_CALLBACK_H_
diff --git a/chromium/components/favicon_base/favicon_types.cc b/chromium/components/favicon_base/favicon_types.cc
index b03f0ecd4ae..3e66113723e 100644
--- a/chromium/components/favicon_base/favicon_types.cc
+++ b/chromium/components/favicon_base/favicon_types.cc
@@ -41,8 +41,9 @@ LargeIconResult::~LargeIconResult() {}
// --------------------------------------------------------
// LargeIconImageResult
-LargeIconImageResult::LargeIconImageResult(const gfx::Image& image_in)
- : image(image_in) {}
+LargeIconImageResult::LargeIconImageResult(const gfx::Image& image_in,
+ const GURL& icon_url_in)
+ : image(image_in), icon_url(icon_url_in) {}
LargeIconImageResult::LargeIconImageResult(
FallbackIconStyle* fallback_icon_style_in)
diff --git a/chromium/components/favicon_base/favicon_types.h b/chromium/components/favicon_base/favicon_types.h
index 7ad22ff0b3b..9f47be4653a 100644
--- a/chromium/components/favicon_base/favicon_types.h
+++ b/chromium/components/favicon_base/favicon_types.h
@@ -103,7 +103,8 @@ struct LargeIconResult {
// Contains either the gfx::Image if the favicon database has a sufficiently
// large favicon bitmap and the style of the fallback icon otherwise.
struct LargeIconImageResult {
- explicit LargeIconImageResult(const gfx::Image& image_in);
+ explicit LargeIconImageResult(const gfx::Image& image_in,
+ const GURL& icon_url_in);
// Takes ownership of |fallback_icon_style_in|.
explicit LargeIconImageResult(FallbackIconStyle* fallback_icon_style_in);
@@ -114,11 +115,36 @@ struct LargeIconImageResult {
// large one.
gfx::Image image;
+ // The URL of the containing favicon. Specified only if |image| is not empty.
+ GURL icon_url;
+
// The fallback icon style if a sufficiently large icon isn't available. This
// uses the dominant color of a smaller icon as the background if available.
std::unique_ptr<FallbackIconStyle> fallback_icon_style;
};
+// Enumeration listing all possible outcomes for fetch attempts from Google
+// favicon server. Used for UMA enum GoogleFaviconServerRequestStatus, so do not
+// change existing values. Insert new values at the end, and update the
+// histogram definition.
+enum class GoogleFaviconServerRequestStatus {
+ // Request sent out and the favicon successfully fetched.
+ SUCCESS = 0,
+ // Request sent out and a connection error occurred (no valid HTTP response
+ // recevied).
+ FAILURE_CONNECTION_ERROR = 1,
+ // Request sent out and a HTTP error received.
+ FAILURE_HTTP_ERROR = 2,
+ // Request not sent out (previous HTTP error in cache).
+ FAILURE_HTTP_ERROR_CACHED = 3,
+ // Request sent out and favicon fetched but writing to database failed.
+ FAILURE_ON_WRITE = 4,
+ // Request not sent out (the request or the fetcher was invalid).
+ FAILURE_INVALID = 5,
+ // Insert new values here.
+ COUNT
+};
+
} // namespace favicon_base
#endif // COMPONENTS_FAVICON_BASE_FAVICON_TYPES_H_
diff --git a/chromium/components/favicon_base/large_icon_url_parser.cc b/chromium/components/favicon_base/large_icon_url_parser.cc
deleted file mode 100644
index 9f1a9ac80a4..00000000000
--- a/chromium/components/favicon_base/large_icon_url_parser.cc
+++ /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.
-
-#include "components/favicon_base/large_icon_url_parser.h"
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "third_party/skia/include/utils/SkParse.h"
-#include "ui/gfx/favicon_size.h"
-
-LargeIconUrlParser::LargeIconUrlParser() : size_in_pixels_(48) {
-}
-
-LargeIconUrlParser::~LargeIconUrlParser() {
-}
-
-bool LargeIconUrlParser::Parse(base::StringPiece path) {
- if (path.empty())
- return false;
-
- size_t slash = path.find("/", 0); // |path| does not start with '/'.
- if (slash == base::StringPiece::npos)
- return false;
- base::StringPiece size_str = path.substr(0, slash);
- // Disallow empty, non-numeric, or non-positive sizes.
- if (size_str.empty() ||
- !base::StringToInt(size_str, &size_in_pixels_) ||
- size_in_pixels_ <= 0)
- return false;
-
- // Need to store the index of the URL field, so Instant Extended can translate
- // large icon URLs using advanced parameters.
- // Example:
- // "chrome-search://large-icon/48/<renderer-id>/<most-visited-id>"
- // would be translated to:
- // "chrome-search://large-icon/48/<most-visited-item-with-given-id>".
- path_index_ = slash + 1;
- url_string_ = path.substr(path_index_).as_string();
- return true;
-}
diff --git a/chromium/components/favicon_base/large_icon_url_parser.h b/chromium/components/favicon_base/large_icon_url_parser.h
deleted file mode 100644
index ca70c0d1e67..00000000000
--- a/chromium/components/favicon_base/large_icon_url_parser.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
-#define COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-
-// A parser for parameters to the chrome://large-icon/ host.
-class LargeIconUrlParser {
- public:
- LargeIconUrlParser();
- ~LargeIconUrlParser();
-
- std::string url_string() const { return url_string_; }
-
- int size_in_pixels() const { return size_in_pixels_; }
-
- size_t path_index() const { return path_index_; }
-
- // Parses |path|, which should be in the format described at the top of the
- // file "chrome/browser/ui/webui/large_icon_source.h". Note that |path| does
- // not have leading '/'.
- bool Parse(base::StringPiece path);
-
- private:
- friend class LargeIconUrlParserTest;
-
- // The page URL string of the requested large icon.
- std::string url_string_;
-
- // The size of the requested large icon in pixels.
- int size_in_pixels_;
-
- // The index of the first character (relative to the path) where the the URL
- // from which the large icon is being requested is located.
- size_t path_index_;
-
- DISALLOW_COPY_AND_ASSIGN(LargeIconUrlParser);
-};
-
-#endif // COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
diff --git a/chromium/components/favicon_base/large_icon_url_parser_unittest.cc b/chromium/components/favicon_base/large_icon_url_parser_unittest.cc
deleted file mode 100644
index d58ca3ff854..00000000000
--- a/chromium/components/favicon_base/large_icon_url_parser_unittest.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/favicon_base/large_icon_url_parser.h"
-
-#include <stddef.h>
-
-#include "base/macros.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace {
-
-const char kTestUrlStr[] = "https://www.google.ca/imghp?hl=en&tab=wi";
-
-} // namespace
-
-TEST(LargeIconUrlParserTest, ParseLargeIconPathSuccess) {
- // Everything populated.
- {
- LargeIconUrlParser parser;
- EXPECT_TRUE(parser.Parse(std::string("48/") + kTestUrlStr));
- EXPECT_EQ(48, parser.size_in_pixels());
- EXPECT_EQ(GURL(kTestUrlStr), GURL(parser.url_string()));
- EXPECT_EQ(3U, parser.path_index());
- }
-
- // Empty URL.
- {
- LargeIconUrlParser parser;
- EXPECT_TRUE(parser.Parse("48/"));
- EXPECT_EQ(48, parser.size_in_pixels());
- EXPECT_EQ(GURL(), GURL(parser.url_string()));
- EXPECT_EQ(3U, parser.path_index());
- }
-
- // Tolerate invalid URL.
- {
- LargeIconUrlParser parser;
- EXPECT_TRUE(parser.Parse("48/NOT A VALID URL"));
- EXPECT_EQ(48, parser.size_in_pixels());
- EXPECT_EQ("NOT A VALID URL", parser.url_string());
- EXPECT_EQ(3U, parser.path_index());
- }
-}
-
-TEST(LargeIconUrlParserTest, ParseLargeIconPathFailure) {
- const char* const test_cases[] = {
- "",
- "/",
- "//",
- "48", // Missing '/'.
- "/http://www.google.com/", // Missing size.
- "not_a_number/http://www.google.com/", // Bad size.
- "0/http://www.google.com/", // Non-positive size.
- "-1/http://www.google.com/", // Non-positive size.
- };
- for (size_t i = 0; i < arraysize(test_cases); ++i) {
- LargeIconUrlParser parser;
- EXPECT_FALSE(parser.Parse(test_cases[i])) << "test_cases[" << i << "]";
- }
-}
diff --git a/chromium/components/feature_engagement_tracker/OWNERS b/chromium/components/feature_engagement_tracker/OWNERS
index 6768d1d71d0..6b65e3713b5 100644
--- a/chromium/components/feature_engagement_tracker/OWNERS
+++ b/chromium/components/feature_engagement_tracker/OWNERS
@@ -1,2 +1,5 @@
dtrainor@chromium.org
nyquist@chromium.org
+
+# COMPONENT: Internals>FeatureEngagementTracker
+
diff --git a/chromium/components/feature_engagement_tracker/README.md b/chromium/components/feature_engagement_tracker/README.md
index e912703aa72..9c560d92f0f 100644
--- a/chromium/components/feature_engagement_tracker/README.md
+++ b/chromium/components/feature_engagement_tracker/README.md
@@ -7,28 +7,439 @@ input about user behavior. Whenever the frontend gives a trigger signal that
in-product help could be displayed, the backend will provide an answer to
whether it is appropriate to show it or not.
-The frontend only needs to deal with user interactions and how to display the
-feature enlightenment or in-product help itself.
+[TOC]
-The backend is feature agnostic and have no special logic for any specific
-features, but instead provides a generic API.
+## Objectives
-## Compiling for platforms other than Android
+We often add new features, but some are hard to find. Both new and old features
+could benefit from being surfaced for users that we believe would be the ones
+who benefit the most. This has lead to the effort of providing direct in-product
+help to our end users that should be extremely context aware to maximize the
+value of the new information.
-For now the code for the Feature Engagement Tracker is only compiled in
-for Android, but only a shim layer is really dependent on Android to provide a
-JNI bridge. The goal is to keep all the business logic in the cross-platform
-part of the code.
+Conceptually one could implement tracking whether In-Product Help should be
+displayed or not through a single preference for whether it has been shown
+before. However, that leads to a few issues that this component tries to solve:
-For local development, it is therefore possible to compile and run tests for
-the core of the tracker on other platforms. To do this, simply add the
-following line to the `//components:components_unittests` target:
+* Make showing In-Product Help context aware.
+ * If a user is continuously using a feature, there is no reason for Chrome
+ to display In-Product Help for it.
+ * Other events might be required to have happened first that would make it
+ more likely that the end user would be surprised and delighted when the
+ In-Product Help in fact does show up.
+ * Example: Having seen the Chrome offline dino 10 times the last week,
+ the user might be happier if they are informed that they can
+ download web pages exactly as a page successfully loads.
+* Tackle interactions between different In-Product Help features.
+ * If other In-Product Help has been shown within the current session, we
+ might not want to show a different one.
+ * Whether we have shown a particular In-Product Help or not might be a
+ precondition for whether we should show different one.
+* Users should be able to use try out a feature on their own for some time
+ before they see help.
+ * We should show In-Product Help only if they don't seem use it, but we
+ believe it would be helpful to them.
+* Share the same statistics framework across all of Chrome.
+ * Sharing a framework within Chrome makes it easier to track statistics
+ and share queries about In-Product Help in a common way.
+* Make it simpler to add new In-Product Help for developers, but still
+ enabling them to have a complex decision tree for when to show it.
-```python
-deps += [ "//components/feature_engagement_tracker:unit_tests" ]
+## Overview
+
+Each In-Product Help is called a feature in this documentation. Every feature
+will have a few important things that are tracked, particularly whether the
+in-product help has been displayed, whether the feature the IPH highlights has
+been used and whether any required preconditions have been met. All of these are
+tracked within **daily buckets**. This tracking is done only
+**locally on the device** itself.
+
+The client-side backend is feature agnostic and has no special logic for any
+specific features, but instead provides a generic API and uses the Chrome
+Variations framework to control how often IPH should be shown for end users. It
+does this by setting thresholds in the experiment params and compare these
+numbers to the local state.
+
+Whenever the triggering condition for possibly showing IPH happens, the frontend
+asks the backend whether it should display the IPH. The backend then
+compares the current local state with the experiment params to see if they are
+within the given thresholds. If they are, the frontend is informed that it
+should display the IPH. The backend does not display any UI.
+
+To ensure that there are not multiple IPHs displayed at the same time, the
+frontend also needs to inform the backend whenever the IPH has been dismissed.
+
+In addition, since each feature might have preconditions that must be met within
+the time window configured for the experiment, the frontend needs to inform the
+backend whenever such events happen.
+
+To ensure that it is possible to use whether a feature has been used or
+not as input to the algorithm to decide whether to show IPH and for tracking
+purposes, the frontend needs to inform whenever the feature has been used.
+
+Lastly, some preconditions might require something to never have happened.
+The first time a user has IPH available, that will typically be true, since
+the event was just started being tracked. Therefore, the first time the
+Chrome Variations experiment is made available to the user, the date is tracked
+so it can be used to require that the IPH must have been available for at least
+`N` days.
+
+The backend will track all the state in-memory and flush it to disk when
+necessary to ensure the data is consistent across restarts of the application.
+The time window for how long this data is stored is configured server-side.
+
+All of the local tracking of data will happen per Chrome user profile, but
+everything is configured on the server side.
+
+## Developing a new In-Product Help Feature
+
+You need to do the following things to enable your feature, all described in
+detail below.
+
+* [Declare your feature](#Declaring-your-feature) and make it available to the
+ `FeatureEngagementTracker`.
+* [Start using the `FeatureEngagementTracker` class](#Using-the-FeatureEngagementTracker)
+ by notifying about events, and checking whether In-Product Help should be
+ displayed.
+* [Configure UMA](#Configuring-UMA).
+
+### Declaring your feature
+
+You need to create a `base::Feature` that represents your In-Product Help
+feature, that enables the whole feature to be controlled server side.
+The name should be on the form:
+
+1. `kIPH` prefix
+1. Your unique CamelCased name, for example `MyFun`.
+1. `Feature` suffix.
+
+The name member of the `base::Feature` struct should match the constant name,
+and be on the form:
+
+1. `IPH_` prefix
+1. Your unique CamelCased name, for example `MyFun`.
+
+There are also a few more places where the feature should be added, so overall
+you would have to add it to the following places:
+
+* `//components/feature_engagement_tracker/public/feature_constants.cc`:
+
+ ```c++
+ const base::Feature kIPHMyFunFeature{"IPH_MyFun",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+ ```
+
+* `//components/feature_engagement_tracker/public/feature_constants.h`:
+
+ ```c++
+ extern const base::Feature kIPHMyFunFeature;
+ ```
+
+* `//components/feature_engagement_tracker/public/feature_list.cc`:
+ * Add to `const base::Feature* kAllFeatures[]`.
+* `//components/feature_engagement_tracker/public/feature_list.h`:
+ * `DEFINE_VARIATION_PARAM(kIPHMyFunFeature, "IPH_MyFun");`
+ * `VARIATION_ENTRY(kIPHMyFunFeature)`
+
+If the feature will also be used from Java, also add it to:
+`org.chromium.components.feature_engagement_tracker.FeatureConstants` as a
+`String` constant.
+
+### Using the FeatureEngagementTracker
+
+To retrieve the `FeatureEngagementTracker` you need to use your platform
+specific way for how to retrieve a `KeyedService`. For example for desktop
+platforms and Android, you can use the `FeatureEngagementTrackerFactory` in
+`//chrome/browser/feature_engagement_tracker/feature_engagement_tracker_factory.h`
+to retrieve it from the `Profile` or `BrowserContext`:
+
+```c++
+feature_engagement_tracker::FeatureEngagementTracker* tracker =
+ FeatureEngagementTrackerFactory::GetForBrowserContext(profile);
```
-## Testing
+That service can be first of all used to notify the backend about events:
+
+```c++
+tracker->NotifyEvent("your_event_name");
+```
+
+In addition, it can tell you whether it is a good time to trigger the help UI:
+
+```c++
+bool trigger_help_ui =
+ tracker->ShouldTriggerHelpUI(feature_engagement_tracker::kIPHMyFunFeature);
+if (trigger_help_ui) {
+ // Show IPH UI.
+}
+```
+
+If `FeatureEngagementTracker::ShouldTriggerHelpUI` return `true` you must
+display the In-Product Help, as it will be tracked as if you showed it. In
+addition you are required to inform when the feature has been dismissed:
+
+```c++
+tracker->Dismissed(feature_engagement_tracker::kIPHMyFunFeature);
+```
+
+
+### Configuring UMA
+
+To enable UMA tracking, you need to make the following changes to the metrics
+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`.
+ * 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">`
+
+## Demo mode
+
+The Feature Engagement Tracker supports a special demo mode, which enables a
+developer or testers to see how the UI looks like without using Chrome
+Variations configuration.
+
+The demo mode behaves differently than the code used in production where the
+chrome Variations configuration is used. Instead, it has only a few rules:
+
+* Event model must be ready (happens early).
+* No other features must be showing at the moment.
+* The given feature must not have been shown before in the current session.
+
+This basically leads to each selected IPH feature to be displayed once. The
+triggering condition code path must of course be triggered to display the IPH.
+
+How to select a feature or features is described below.
+
+### Enabling all In-Product Help features in demo-mode
+
+1. Go to chrome://flags
+1. Find "In-Product Help Demo Mode" (#in-product-help-demo-mode-choice)
+1. Select "Enabled"
+1. Restart Chrome
+
+### Enabling a single In-Product Help feature in demo-mode
+
+1. Go to chrome://flags
+1. Find “In-Product Help Demo Mode” (#enable-iph-demo-choice)
+1. Select the feature you want with the "Enabled " prefix, for example for
+ `IPH_MyFunFeature` you would select:
+ * Enabled IPH_MyFunFeature
+1. Restart Chrome
+
+## Using Chrome Variations
+
+Each In-Product Help feature must have its own feature configuration
+[FeatureConfig](#FeatureConfig), which has 4 required configuration items that
+must be set, and then there can be an arbitrary number of additional
+preconditions (but typically on the order of 0-5).
+
+The data types are listed below.
+
+### FeatureConfig
+
+Format:
+
+```
+{
+ "availability": "{Comparator}",
+ "session_rate": "{Comparator}",
+ "event_used": "{EventConfig}",
+ "event_trigger": "{EventConfig}",
+ "event_???": "{EventConfig}",
+ "x_???": "..."
+ }
+```
+
+The `FeatureConfig` fields `availability`, `session_rate`, `event_used` and
+`event_trigger` are required, and there can be an arbitrary amount of other
+`event_???` entries.
+
+* `availability`
+ * For how long must an in-product help experiment have been available to
+ the end user.
+ * The value of the `Comparator` is in a number of days.
+* `session_rate`
+ * How many other in-product help have been displayed within the current
+ end user session.
+ * The value of the `Comparator` is a count of total In-Product Help
+ displayed in the current end user session.
+* `event_used`
+ * Relates to what the in-product help wants to highlight, i.e. teach the
+ user about and increase usage of.
+ * This is typically the action that the In-Product Help should stimulate
+ usage of.
+ * Special UMA is tracked for this.
+* `event_trigger`
+ * Relates to the times in-product help is triggered.
+ * Special UMA is tracked for this.
+* `event_???`
+ * Similar to the other `event_` items, but for all other preconditions
+ that must have been met.
+ * Name must match `/^event_[a-zA-Z0-9-_]+$/` and not be `event_used` or
+ `event_trigger`.
+* `x_???`
+ * Any parameter starting with `x_` is ignored by the feature engagement
+ tracker.
+ * A typical use case for this would be if there are multiple experiments
+ for the same in-product help, and you want to specify different strings
+ to use in each of them, such as:
+
+ ```
+ "x_promo_string": "IDS_MYFUN_PROMO_2"
+ ```
+
+ * Failing to use an `x_`-prefix for parameters unrelated to the
+ `FeatureConfig` will end up being recorded as `FAILURE_UNKNOWN_KEY` in
+ the `InProductHelp.Config.ParsingEvent` histogram.
+
+**Examples**
+
+```
+{
+ "availability": ">=30",
+ "session_rate": "<1",
+ "event_used": "name:download_home_opened;comparator:any;window:90;storage:360",
+ "event_trigger": "name:download_home_iph_trigger;comparator:any;window:90;storage:360",
+ "event_1": "name:download_completed;comparator:>=1;window:120;storage:180"
+}
+```
+
+### EventConfig
+
+Format: ```name:{std::string};comparator:{COMPARATOR};window:{uint32_t};storage:{uint32_t}```
+
+The EventConfig is a semi-colon separate data structure with 4 key-value pairs,
+all described below:
+
+* `name`
+ * The name (unique identifier) of the event.
+ * Must match what is used in client side code.
+ * Must only contain alphanumeric, dash and underscore.
+ * Specifically must match this regex: `/^[a-zA-Z0-9-_]+$/`
+ * Value client side data type: std::string
+* `comparator`
+ * The comparator for the event. See [Comparator](#Comparator) below.
+* `window`
+ * Search for this occurrences of the event within this window.
+ * The value must be given as a number of days.
+ * Value client side data type: uint32_t
+* `storage`
+ * Store client side data related to events for this event minimum this
+ long.
+ * The value must be given as a number of days.
+ * The value should not exceed 10 years (3650 days).
+ * Value client side data type: uint32_t
+ * Whenever a particular event is used by multiple features, the maximum
+ value of all `storage` is used as the storage window.
+
+**Examples**
+
+```
+name:user_opened_app_menu;comparator:==0;window:14;storage:90
+name:user_has_seen_dino;comparator:>=5;window:30;storage:360
+name:user_has_seen_wifi;comparator:>=1;window:30;storage:180
+```
+
+### Comparator
+
+Format: ```{COMPARATOR}[value]```
+
+The following comparators are allowed:
+
+* `<` less than
+* `>` greater than
+* `<=` less than or equal
+* `>=` greater than or equal
+* `==` equal
+* `!=` not equal
+* `any` always true (no value allowed)
+
+Other than `any`, all comparators require a value.
+
+**Examples**
+
+```
+>=10
+==0
+any
+<15
+```
+
+### Using Chrome Variations at runtime
+
+It is possible to test the whole backend from parsing the configuration,
+to ensuring that help triggers at the correct time. To do that
+you need to provide a JSON configuration file, that is then
+parsed to become command line arguments for Chrome, and after
+that you can start Chrome and verify that it behaves correctly.
+
+1. Create a file which describes the configuration you are planning
+ on testing with, and store it. In the following example, store the
+ file `DownloadStudy.json`:
+
+ ```javascript
+ {
+ "DownloadStudy": [
+ {
+ "platforms": ["android"],
+ "experiments": [
+ {
+ "name": "DownloadExperiment",
+ "params": {
+ "availability": ">=30",
+ "session_rate": "<1",
+ "event_used": "name:download_home_opened;comparator:any;window:90;storage:360",
+ "event_trigger": "name:download_home_iph_trigger;comparator:any;window:90;storage:360",
+ "event_1": "name:download_completed;comparator:>=1;window:120;storage:180"
+ },
+ "enable_features": ["IPH_DownloadHome"],
+ "disable_features": []
+ }
+ ]
+ }
+ ]
+ }
+ ```
+
+1. Use the field trial utility to convert the JSON configuration to command
+ line arguments:
+
+ ```bash
+ python ./tools/variations/fieldtrial_util.py DownloadStudy.json android shell_cmd
+ ```
+
+1. Pass the command line along to the binary you are planning on running or the
+ command line utility for the Android platform.
+
+ For the target `chrome_public_apk` it would be:
+
+ ```bash
+ ./build/android/adb_chrome_public_command_line "--force-fieldtrials=DownloadStudy/DownloadExperiment" "--force-fieldtrial-params=DownloadStudy.DownloadExperiment:availability/>=30/event_1/name%3Adownload_completed;comparator%3A>=1;window%3A120;storage%3A180/event_trigger/name%3Adownload_home_iph_trigger;comparator%3Aany;window%3A90;storage%3A360/event_used/name%3Adownload_home_opened;comparator%3Aany;window%3A90;storage%3A360/session_rate/<1" "--enable-features=IPH_DownloadHome<DownloadStudy"
+ ```
+
+### Printf debugging
+
+Several parts of the feature engagement tracker has some debug logging
+available. To see if the current checked in code covers your needs, try starting
+a debug build of chrome with the following command line arguments:
+
+```bash
+--vmodule=feature_engagement_tracker_impl*=2,model_impl*=2,availability_store*=2,chrome_variations_configuration*=3
+```
+
+## Development of `//components/feature_engagement_tracker`
+
+### Testing
To compile and run tests, assuming the product out directory is `out/Debug`,
use:
@@ -41,4 +452,3 @@ ninja -C out/Debug components_unittests ;
When adding new test suites, also remember to add the suite to the filter file:
`//components/feature_engagement_tracker/components_unittests.filter`.
-
diff --git a/chromium/components/feature_engagement_tracker/internal/BUILD.gn b/chromium/components/feature_engagement_tracker/internal/BUILD.gn
index 5f7122165b4..0c084430e27 100644
--- a/chromium/components/feature_engagement_tracker/internal/BUILD.gn
+++ b/chromium/components/feature_engagement_tracker/internal/BUILD.gn
@@ -11,6 +11,7 @@ static_library("internal") {
visibility = [
":*",
"//components/feature_engagement_tracker",
+ "//components/feature_engagement_tracker/test:test_support",
]
sources = [
diff --git a/chromium/components/feature_engagement_tracker/internal/availability_model_impl.cc b/chromium/components/feature_engagement_tracker/internal/availability_model_impl.cc
index fb00d8495b4..994a9bfaa4a 100644
--- a/chromium/components/feature_engagement_tracker/internal/availability_model_impl.cc
+++ b/chromium/components/feature_engagement_tracker/internal/availability_model_impl.cc
@@ -37,7 +37,7 @@ bool AvailabilityModelImpl::IsReady() const {
base::Optional<uint32_t> AvailabilityModelImpl::GetAvailability(
const base::Feature& feature) const {
- auto search = feature_availabilities_.find(&feature);
+ auto search = feature_availabilities_.find(feature.name);
if (search == feature_availabilities_.end())
return base::nullopt;
@@ -47,8 +47,7 @@ base::Optional<uint32_t> AvailabilityModelImpl::GetAvailability(
void AvailabilityModelImpl::OnStoreLoadComplete(
OnInitializedCallback on_initialized_callback,
bool success,
- std::unique_ptr<std::map<const base::Feature*, uint32_t>>
- feature_availabilities) {
+ std::unique_ptr<std::map<std::string, uint32_t>> feature_availabilities) {
if (!success) {
std::move(on_initialized_callback).Run(false);
return;
diff --git a/chromium/components/feature_engagement_tracker/internal/availability_model_impl.h b/chromium/components/feature_engagement_tracker/internal/availability_model_impl.h
index f32b12f0b3d..a94d63c0f5d 100644
--- a/chromium/components/feature_engagement_tracker/internal/availability_model_impl.h
+++ b/chromium/components/feature_engagement_tracker/internal/availability_model_impl.h
@@ -9,6 +9,7 @@
#include <map>
#include <memory>
+#include <string>
#include "base/callback.h"
#include "base/macros.h"
@@ -45,12 +46,12 @@ class AvailabilityModelImpl : public AvailabilityModel {
void OnStoreLoadComplete(
OnInitializedCallback on_initialized_callback,
bool success,
- std::unique_ptr<std::map<const base::Feature*, uint32_t>>
- feature_availabilities);
+ std::unique_ptr<std::map<std::string, uint32_t>> feature_availabilities);
// Stores the day number since epoch (1970-01-01) in the local timezone for
- // when the particular |feature| was made available.
- std::map<const base::Feature*, uint32_t> feature_availabilities_;
+ // when the particular feature was made available. The key is the feature
+ // name.
+ std::map<std::string, uint32_t> feature_availabilities_;
// Whether the model has successfully initialied.
bool ready_;
diff --git a/chromium/components/feature_engagement_tracker/internal/availability_model_impl_unittest.cc b/chromium/components/feature_engagement_tracker/internal/availability_model_impl_unittest.cc
index 490955cc3a7..37c3672e3bd 100644
--- a/chromium/components/feature_engagement_tracker/internal/availability_model_impl_unittest.cc
+++ b/chromium/components/feature_engagement_tracker/internal/availability_model_impl_unittest.cc
@@ -41,7 +41,7 @@ class AvailabilityModelImplTest : public testing::Test {
// SetUpModel exists so that the filter can be changed for any test.
void SetUpModel(
bool success,
- std::unique_ptr<std::map<const base::Feature*, uint32_t>> store_content) {
+ std::unique_ptr<std::map<std::string, uint32_t>> store_content) {
auto store_loader = base::BindOnce(&AvailabilityModelImplTest::StoreLoader,
base::Unretained(this), success,
std::move(store_content));
@@ -53,7 +53,7 @@ class AvailabilityModelImplTest : public testing::Test {
void StoreLoader(
bool success,
- std::unique_ptr<std::map<const base::Feature*, uint32_t>> store_content,
+ std::unique_ptr<std::map<std::string, uint32_t>> store_content,
AvailabilityStore::OnLoadedCallback callback,
uint32_t current_day) {
current_day_ = current_day;
@@ -74,8 +74,7 @@ class AvailabilityModelImplTest : public testing::Test {
} // namespace
TEST_F(AvailabilityModelImplTest, InitializationSuccess) {
- SetUpModel(true,
- base::MakeUnique<std::map<const base::Feature*, uint32_t>>());
+ SetUpModel(true, base::MakeUnique<std::map<std::string, uint32_t>>());
EXPECT_FALSE(availability_model_->IsReady());
availability_model_->Initialize(std::move(initialized_callback_), 14u);
EXPECT_TRUE(availability_model_->IsReady());
@@ -85,8 +84,7 @@ TEST_F(AvailabilityModelImplTest, InitializationSuccess) {
}
TEST_F(AvailabilityModelImplTest, InitializationFailed) {
- SetUpModel(false,
- base::MakeUnique<std::map<const base::Feature*, uint32_t>>());
+ SetUpModel(false, base::MakeUnique<std::map<std::string, uint32_t>>());
EXPECT_FALSE(availability_model_->IsReady());
availability_model_->Initialize(std::move(initialized_callback_), 14u);
EXPECT_FALSE(availability_model_->IsReady());
@@ -96,11 +94,10 @@ TEST_F(AvailabilityModelImplTest, InitializationFailed) {
}
TEST_F(AvailabilityModelImplTest, SuccessfullyLoadThreeFeatures) {
- auto availabilities =
- base::MakeUnique<std::map<const base::Feature*, uint32_t>>();
- availabilities->insert(std::make_pair(&kTestFeatureFoo, 100u));
- availabilities->insert(std::make_pair(&kTestFeatureBar, 200u));
- availabilities->insert(std::make_pair(&kTestFeatureNop, 300u));
+ auto availabilities = base::MakeUnique<std::map<std::string, uint32_t>>();
+ availabilities->insert(std::make_pair(kTestFeatureFoo.name, 100u));
+ availabilities->insert(std::make_pair(kTestFeatureBar.name, 200u));
+ availabilities->insert(std::make_pair(kTestFeatureNop.name, 300u));
SetUpModel(true, std::move(availabilities));
availability_model_->Initialize(std::move(initialized_callback_), 14u);
@@ -114,11 +111,10 @@ TEST_F(AvailabilityModelImplTest, SuccessfullyLoadThreeFeatures) {
}
TEST_F(AvailabilityModelImplTest, FailToLoadThreeFeatures) {
- auto availabilities =
- base::MakeUnique<std::map<const base::Feature*, uint32_t>>();
- availabilities->insert(std::make_pair(&kTestFeatureFoo, 100u));
- availabilities->insert(std::make_pair(&kTestFeatureBar, 200u));
- availabilities->insert(std::make_pair(&kTestFeatureNop, 300u));
+ auto availabilities = base::MakeUnique<std::map<std::string, uint32_t>>();
+ availabilities->insert(std::make_pair(kTestFeatureFoo.name, 100u));
+ availabilities->insert(std::make_pair(kTestFeatureBar.name, 200u));
+ availabilities->insert(std::make_pair(kTestFeatureNop.name, 300u));
SetUpModel(false, std::move(availabilities));
availability_model_->Initialize(std::move(initialized_callback_), 14u);
diff --git a/chromium/components/feature_engagement_tracker/internal/availability_store.cc b/chromium/components/feature_engagement_tracker/internal/availability_store.cc
index 887781826d7..b1086579a11 100644
--- a/chromium/components/feature_engagement_tracker/internal/availability_store.cc
+++ b/chromium/components/feature_engagement_tracker/internal/availability_store.cc
@@ -32,8 +32,7 @@ const char kDatabaseUMAName[] = "FeatureEngagementTrackerAvailabilityStore";
void OnDBUpdateComplete(
std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,
AvailabilityStore::OnLoadedCallback on_loaded_callback,
- std::unique_ptr<std::map<const base::Feature*, uint32_t>>
- feature_availabilities,
+ std::unique_ptr<std::map<std::string, uint32_t>> feature_availabilities,
bool success) {
stats::RecordDbUpdate(success, stats::StoreType::AVAILABILITY_STORE);
std::move(on_loaded_callback).Run(success, std::move(feature_availabilities));
@@ -49,8 +48,7 @@ void OnDBLoadComplete(
stats::RecordAvailabilityDbLoadEvent(success);
if (!success) {
std::move(on_loaded_callback)
- .Run(false,
- base::MakeUnique<std::map<const base::Feature*, uint32_t>>());
+ .Run(false, base::MakeUnique<std::map<std::string, uint32_t>>());
return;
}
@@ -63,7 +61,7 @@ void OnDBLoadComplete(
// Find all availabilities from DB and find out what should be deleted.
auto feature_availabilities =
- base::MakeUnique<std::map<const base::Feature*, uint32_t>>();
+ base::MakeUnique<std::map<std::string, uint32_t>>();
auto deletes = base::MakeUnique<std::vector<std::string>>();
for (auto& availability : *availabilities) {
// Check if in |feature_filter|.
@@ -81,14 +79,18 @@ void OnDBLoadComplete(
}
// Both in |feature_filter| and is enabled, so keep around.
- feature_availabilities->insert(std::make_pair(feature, availability.day()));
+ feature_availabilities->insert(
+ std::make_pair(feature->name, availability.day()));
+ DVLOG(2) << "Keeping availability for " << feature->name << " @ "
+ << availability.day();
}
// Find features from |feature_filter| that are enabled, but not in DB yet.
auto additions = base::MakeUnique<KeyAvailabilityList>();
for (const base::Feature* feature : feature_filter) {
// Check if already in DB.
- if (feature_availabilities->find(feature) != feature_availabilities->end())
+ if (feature_availabilities->find(feature->name) !=
+ feature_availabilities->end())
continue;
// Check if enabled.
@@ -103,7 +105,10 @@ void OnDBLoadComplete(
std::make_pair(availability.feature_name(), std::move(availability)));
// Since it will be written to the DB, also add to the callback result.
- feature_availabilities->insert(std::make_pair(feature, availability.day()));
+ feature_availabilities->insert(
+ std::make_pair(feature->name, availability.day()));
+ DVLOG(2) << "Adding availability for " << feature->name << " @ "
+ << availability.day();
}
// Write all changes to the DB.
@@ -124,8 +129,7 @@ void OnDBInitComplete(
if (!success) {
std::move(on_loaded_callback)
- .Run(false,
- base::MakeUnique<std::map<const base::Feature*, uint32_t>>());
+ .Run(false, base::MakeUnique<std::map<std::string, uint32_t>>());
return;
}
diff --git a/chromium/components/feature_engagement_tracker/internal/availability_store.h b/chromium/components/feature_engagement_tracker/internal/availability_store.h
index 891b07c3248..9326940fadd 100644
--- a/chromium/components/feature_engagement_tracker/internal/availability_store.h
+++ b/chromium/components/feature_engagement_tracker/internal/availability_store.h
@@ -17,7 +17,6 @@
#include "components/leveldb_proto/proto_database.h"
namespace base {
-struct Feature;
class FilePath;
} // namespace base
@@ -32,9 +31,8 @@ class AvailabilityStore {
// empty. The value for each entry in the map is the day number since epoch
// (1970-01-01) in the local timezone for when the particular feature was made
// available.
- using OnLoadedCallback = base::OnceCallback<void(
- bool success,
- std::unique_ptr<std::map<const base::Feature*, uint32_t>>)>;
+ using OnLoadedCallback = base::OnceCallback<
+ void(bool success, std::unique_ptr<std::map<std::string, uint32_t>>)>;
// Loads the availability data, updates the DB with newly enabled features,
// deletes features that are not enabled anymore, and asynchronously invokes
diff --git a/chromium/components/feature_engagement_tracker/internal/availability_store_unittest.cc b/chromium/components/feature_engagement_tracker/internal/availability_store_unittest.cc
index a65f574458d..cfa4a5831c7 100644
--- a/chromium/components/feature_engagement_tracker/internal/availability_store_unittest.cc
+++ b/chromium/components/feature_engagement_tracker/internal/availability_store_unittest.cc
@@ -62,9 +62,9 @@ class AvailabilityStoreTest : public testing::Test {
return db;
}
- void LoadCallback(bool success,
- std::unique_ptr<std::map<const base::Feature*, uint32_t>>
- availabilities) {
+ void LoadCallback(
+ bool success,
+ std::unique_ptr<std::map<std::string, uint32_t>> availabilities) {
load_successful_ = success;
load_results_ = std::move(availabilities);
}
@@ -77,7 +77,7 @@ class AvailabilityStoreTest : public testing::Test {
// Callback results.
base::Optional<bool> load_successful_;
- std::unique_ptr<std::map<const base::Feature*, uint32_t>> load_results_;
+ std::unique_ptr<std::map<std::string, uint32_t>> load_results_;
// |db_availabilities_| is used during creation of the FakeDB in CreateDB(),
// to simplify what the DB has stored.
@@ -101,6 +101,9 @@ TEST_F(AvailabilityStoreTest, StorageDirectory) {
std::move(load_callback_), 14u);
db_->InitCallback(true);
EXPECT_EQ(storage_dir_, db_->GetDirectory());
+
+ // Finish the pipeline to ensure the test does not leak anything.
+ db_->LoadCallback(false);
}
TEST_F(AvailabilityStoreTest, InitFail) {
@@ -195,14 +198,16 @@ TEST_F(AvailabilityStoreTest, AllNewFeatures) {
ASSERT_EQ(2u, load_results_->size());
ASSERT_EQ(2u, db_availabilities_.size());
- ASSERT_TRUE(load_results_->find(&kTestFeatureFoo) != load_results_->end());
- EXPECT_EQ(14u, (*load_results_)[&kTestFeatureFoo]);
+ ASSERT_TRUE(load_results_->find(kTestFeatureFoo.name) !=
+ load_results_->end());
+ EXPECT_EQ(14u, (*load_results_)[kTestFeatureFoo.name]);
ASSERT_TRUE(db_availabilities_.find(kTestFeatureFoo.name) !=
db_availabilities_.end());
EXPECT_EQ(14u, db_availabilities_[kTestFeatureFoo.name].day());
- ASSERT_TRUE(load_results_->find(&kTestFeatureBar) != load_results_->end());
- EXPECT_EQ(14u, (*load_results_)[&kTestFeatureBar]);
+ ASSERT_TRUE(load_results_->find(kTestFeatureBar.name) !=
+ load_results_->end());
+ EXPECT_EQ(14u, (*load_results_)[kTestFeatureBar.name]);
ASSERT_TRUE(db_availabilities_.find(kTestFeatureBar.name) !=
db_availabilities_.end());
EXPECT_EQ(14u, db_availabilities_[kTestFeatureBar.name].day());
@@ -239,14 +244,16 @@ TEST_F(AvailabilityStoreTest, TestAllFilterCombinations) {
ASSERT_EQ(2u, load_results_->size());
ASSERT_EQ(2u, db_availabilities_.size());
- ASSERT_TRUE(load_results_->find(&kTestFeatureFoo) != load_results_->end());
- EXPECT_EQ(14u, (*load_results_)[&kTestFeatureFoo]);
+ ASSERT_TRUE(load_results_->find(kTestFeatureFoo.name) !=
+ load_results_->end());
+ EXPECT_EQ(14u, (*load_results_)[kTestFeatureFoo.name]);
ASSERT_TRUE(db_availabilities_.find(kTestFeatureFoo.name) !=
db_availabilities_.end());
EXPECT_EQ(14u, db_availabilities_[kTestFeatureFoo.name].day());
- ASSERT_TRUE(load_results_->find(&kTestFeatureBar) != load_results_->end());
- EXPECT_EQ(10u, (*load_results_)[&kTestFeatureBar]);
+ ASSERT_TRUE(load_results_->find(kTestFeatureBar.name) !=
+ load_results_->end());
+ EXPECT_EQ(10u, (*load_results_)[kTestFeatureBar.name]);
ASSERT_TRUE(db_availabilities_.find(kTestFeatureBar.name) !=
db_availabilities_.end());
EXPECT_EQ(10u, db_availabilities_[kTestFeatureBar.name].day());
diff --git a/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.cc b/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.cc
index 90d79b3a23a..bb1f35fa19b 100644
--- a/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.cc
+++ b/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.cc
@@ -36,6 +36,7 @@ const char kEventConfigTriggerKey[] = "event_trigger";
const char kEventConfigKeyPrefix[] = "event_";
const char kSessionRateKey[] = "session_rate";
const char kAvailabilityKey[] = "availability";
+const char kIgnoredKeyPrefix[] = "x_";
const char kEventConfigDataNameKey[] = "name";
const char kEventConfigDataComparatorKey[] = "comparator";
@@ -203,10 +204,12 @@ void ChromeVariationsConfiguration::ParseFeatureConfigs(
void ChromeVariationsConfiguration::ParseFeatureConfig(
const base::Feature* feature) {
DCHECK(feature);
- DCHECK(configs_.find(feature) == configs_.end());
+ DCHECK(configs_.find(feature->name) == configs_.end());
+
+ DVLOG(3) << "Parsing feature config for " << feature->name;
// Initially all new configurations are considered invalid.
- FeatureConfig& config = configs_[feature];
+ FeatureConfig& config = configs_[feature->name];
config.valid = false;
uint32_t parse_errors = 0;
@@ -217,6 +220,7 @@ void ChromeVariationsConfiguration::ParseFeatureConfig(
stats::ConfigParsingEvent::FAILURE_NO_FIELD_TRIAL);
// Returns early. If no field trial, ConfigParsingEvent::FAILURE will not be
// recorded.
+ DVLOG(3) << "No field trial for " << feature->name;
return;
}
@@ -268,8 +272,13 @@ void ChromeVariationsConfiguration::ParseFeatureConfig(
continue;
}
config.event_configs.insert(event_config);
+ } else if (base::StartsWith(key, kIgnoredKeyPrefix,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ // Intentionally ignoring parameter using registered ignored prefix.
+ DVLOG(2) << "Ignoring unknown key when parsing config for feature "
+ << feature->name << ": " << key;
} else {
- DVLOG(1) << "Ignoring unknown key when parsing config for feature "
+ DVLOG(1) << "Unknown key found when parsing config for feature "
<< feature->name << ": " << key;
stats::RecordConfigParsingEvent(
stats::ConfigParsingEvent::FAILURE_UNKNOWN_KEY);
@@ -284,8 +293,11 @@ void ChromeVariationsConfiguration::ParseFeatureConfig(
if (config.valid) {
stats::RecordConfigParsingEvent(stats::ConfigParsingEvent::SUCCESS);
+ DVLOG(2) << "Config for " << feature->name << " is valid.";
+ DVLOG(3) << "Config for " << feature->name << " = " << config;
} else {
stats::RecordConfigParsingEvent(stats::ConfigParsingEvent::FAILURE);
+ DVLOG(2) << "Config for " << feature->name << " is invalid.";
}
// Notice parse errors for used and trigger events will also cause the
@@ -302,7 +314,14 @@ void ChromeVariationsConfiguration::ParseFeatureConfig(
const FeatureConfig& ChromeVariationsConfiguration::GetFeatureConfig(
const base::Feature& feature) const {
- auto it = configs_.find(&feature);
+ auto it = configs_.find(feature.name);
+ DCHECK(it != configs_.end());
+ return it->second;
+}
+
+const FeatureConfig& ChromeVariationsConfiguration::GetFeatureConfigByName(
+ const std::string& feature_name) const {
+ auto it = configs_.find(feature_name);
DCHECK(it != configs_.end());
return it->second;
}
diff --git a/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.h b/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.h
index 526e7281eba..b4664a749b4 100644
--- a/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.h
+++ b/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration.h
@@ -26,6 +26,8 @@ class ChromeVariationsConfiguration : public Configuration {
// Configuration implementation.
const FeatureConfig& GetFeatureConfig(
const base::Feature& feature) const override;
+ const FeatureConfig& GetFeatureConfigByName(
+ const std::string& feature_name) const override;
const Configuration::ConfigMap& GetRegisteredFeatures() const override;
// Parses the variations configuration for all of the given |features| and
diff --git a/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration_unittest.cc b/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration_unittest.cc
index 9b6e1ea5f8b..d8027dd970a 100644
--- a/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration_unittest.cc
+++ b/chromium/components/feature_engagement_tracker/internal/chrome_variations_configuration_unittest.cc
@@ -44,9 +44,9 @@ class ChromeVariationsConfigurationTest : public ::testing::Test {
base::FieldTrialList::CreateFieldTrial(kBarTrialName, kGroupName);
base::FieldTrial* qux_trial =
base::FieldTrialList::CreateFieldTrial(kQuxTrialName, kGroupName);
- trials_[&kTestFeatureFoo] = foo_trial;
- trials_[&kTestFeatureBar] = bar_trial;
- trials_[&kTestFeatureQux] = qux_trial;
+ trials_[kTestFeatureFoo.name] = foo_trial;
+ trials_[kTestFeatureBar.name] = bar_trial;
+ trials_[kTestFeatureQux.name] = qux_trial;
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
feature_list->RegisterFieldTrialOverride(
@@ -73,9 +73,10 @@ class ChromeVariationsConfigurationTest : public ::testing::Test {
protected:
void SetFeatureParams(const base::Feature& feature,
std::map<std::string, std::string> params) {
- ASSERT_TRUE(base::FieldTrialParamAssociator::GetInstance()
- ->AssociateFieldTrialParams(trials_[&feature]->trial_name(),
- kGroupName, params));
+ ASSERT_TRUE(
+ base::FieldTrialParamAssociator::GetInstance()
+ ->AssociateFieldTrialParams(trials_[feature.name]->trial_name(),
+ kGroupName, params));
std::map<std::string, std::string> actualParams;
EXPECT_TRUE(base::GetFieldTrialParamsByFeature(feature, &actualParams));
@@ -100,7 +101,7 @@ class ChromeVariationsConfigurationTest : public ::testing::Test {
private:
base::FieldTrialList field_trials_;
- std::map<const base::Feature*, base::FieldTrial*> trials_;
+ std::map<std::string, base::FieldTrial*> trials_;
base::test::ScopedFeatureList scoped_feature_list;
DISALLOW_COPY_AND_ASSIGN(ChromeVariationsConfigurationTest);
@@ -280,10 +281,13 @@ TEST_F(ChromeVariationsConfigurationTest, WhitespaceIsValid) {
}
TEST_F(ChromeVariationsConfigurationTest, IgnoresInvalidConfigKeys) {
+ base::HistogramTester histogram_tester;
std::map<std::string, std::string> foo_params;
foo_params["event_used"] = "name:eu;comparator:any;window:0;storage:360";
foo_params["event_trigger"] = "name:et;comparator:any;window:0;storage:360";
- foo_params["not_there_yet"] = "bogus value";
+ foo_params["not_there_yet"] = "bogus value"; // Unrecognized.
+ foo_params["still_not_there"] = "another bogus value"; // Unrecognized.
+ foo_params["x_this_is_ignored"] = "this value is ignored"; // Ignored.
SetFeatureParams(kTestFeatureFoo, foo_params);
std::vector<const base::Feature*> features = {&kTestFeatureFoo};
@@ -297,6 +301,11 @@ TEST_F(ChromeVariationsConfigurationTest, IgnoresInvalidConfigKeys) {
expected_foo.used = EventConfig("eu", Comparator(ANY, 0), 0, 360);
expected_foo.trigger = EventConfig("et", Comparator(ANY, 0), 0, 360);
EXPECT_EQ(expected_foo, foo);
+
+ // Exactly 2 keys should be unrecognized and not ignored.
+ histogram_tester.ExpectBucketCount(
+ kConfigParseEventName,
+ static_cast<int>(stats::ConfigParsingEvent::FAILURE_UNKNOWN_KEY), 2);
}
TEST_F(ChromeVariationsConfigurationTest, IgnoresInvalidEventConfigTokens) {
diff --git a/chromium/components/feature_engagement_tracker/internal/condition_validator.cc b/chromium/components/feature_engagement_tracker/internal/condition_validator.cc
index 90113483c74..38b59b4c098 100644
--- a/chromium/components/feature_engagement_tracker/internal/condition_validator.cc
+++ b/chromium/components/feature_engagement_tracker/internal/condition_validator.cc
@@ -4,6 +4,8 @@
#include "components/feature_engagement_tracker/internal/condition_validator.h"
+#include <ostream>
+
namespace feature_engagement_tracker {
ConditionValidator::Result::Result(bool initial_values)
@@ -37,4 +39,19 @@ bool ConditionValidator::Result::NoErrors() const {
session_rate_ok && availability_model_ready_ok && availability_ok;
}
+std::ostream& operator<<(std::ostream& os,
+ const ConditionValidator::Result& result) {
+ return os << "{ event_model_ready_ok=" << result.event_model_ready_ok
+ << ", currently_showing_ok=" << result.currently_showing_ok
+ << ", feature_enabled_ok=" << result.feature_enabled_ok
+ << ", config_ok=" << result.config_ok
+ << ", used_ok=" << result.used_ok
+ << ", trigger_ok=" << result.trigger_ok
+ << ", preconditions_ok=" << result.preconditions_ok
+ << ", session_rate_ok=" << result.session_rate_ok
+ << ", availability_model_ready_ok="
+ << result.availability_model_ready_ok
+ << ", availability_ok=" << result.availability_ok << " }";
+}
+
} // namespace feature_engagement_tracker
diff --git a/chromium/components/feature_engagement_tracker/internal/condition_validator.h b/chromium/components/feature_engagement_tracker/internal/condition_validator.h
index a4686e03616..fcefb4b3f5e 100644
--- a/chromium/components/feature_engagement_tracker/internal/condition_validator.h
+++ b/chromium/components/feature_engagement_tracker/internal/condition_validator.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <ostream>
#include <string>
#include "base/macros.h"
@@ -90,6 +91,9 @@ class ConditionValidator {
DISALLOW_COPY_AND_ASSIGN(ConditionValidator);
};
+std::ostream& operator<<(std::ostream& os,
+ const ConditionValidator::Result& result);
+
} // namespace feature_engagement_tracker
#endif // COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_CONDITION_VALIDATOR_H_
diff --git a/chromium/components/feature_engagement_tracker/internal/configuration.cc b/chromium/components/feature_engagement_tracker/internal/configuration.cc
index 79870078cf6..60b3023011b 100644
--- a/chromium/components/feature_engagement_tracker/internal/configuration.cc
+++ b/chromium/components/feature_engagement_tracker/internal/configuration.cc
@@ -40,6 +40,29 @@ bool Comparator::MeetsCriteria(uint32_t v) const {
}
}
+std::ostream& operator<<(std::ostream& os, const Comparator& comparator) {
+ switch (comparator.type) {
+ case ANY:
+ return os << "ANY";
+ case LESS_THAN:
+ return os << "<" << comparator.value;
+ case GREATER_THAN:
+ return os << ">" << comparator.value;
+ case LESS_THAN_OR_EQUAL:
+ return os << "<=" << comparator.value;
+ case GREATER_THAN_OR_EQUAL:
+ return os << ">=" << comparator.value;
+ case EQUAL:
+ return os << "==" << comparator.value;
+ case NOT_EQUAL:
+ return os << "!=" << comparator.value;
+ default:
+ // All cases should be covered.
+ NOTREACHED();
+ return os;
+ }
+}
+
EventConfig::EventConfig() : window(0), storage(0) {}
EventConfig::EventConfig(const std::string& name,
@@ -50,6 +73,13 @@ EventConfig::EventConfig(const std::string& name,
EventConfig::~EventConfig() = default;
+std::ostream& operator<<(std::ostream& os, const EventConfig& event_config) {
+ return os << "{ name: " << event_config.name
+ << ", comparator: " << event_config.comparator
+ << ", window: " << event_config.window
+ << ", storage: " << event_config.storage << " }";
+}
+
FeatureConfig::FeatureConfig() : valid(false) {}
FeatureConfig::FeatureConfig(const FeatureConfig& other) = default;
@@ -85,4 +115,21 @@ bool operator==(const FeatureConfig& lhs, const FeatureConfig& rhs) {
rhs.session_rate, rhs.availability);
}
+std::ostream& operator<<(std::ostream& os,
+ const FeatureConfig& feature_config) {
+ os << "{ valid: " << feature_config.valid << ", used: " << feature_config.used
+ << ", trigger: " << feature_config.trigger << ", event_configs: [";
+ bool first = true;
+ for (const auto& event_config : feature_config.event_configs) {
+ if (first) {
+ first = false;
+ os << event_config;
+ } else {
+ os << ", " << event_config;
+ }
+ }
+ return os << "], session_rate: " << feature_config.session_rate
+ << ", availability: " << feature_config.availability << " }";
+}
+
} // namespace feature_engagement_tracker
diff --git a/chromium/components/feature_engagement_tracker/internal/configuration.h b/chromium/components/feature_engagement_tracker/internal/configuration.h
index c942e5b9c90..f9ebb7338ce 100644
--- a/chromium/components/feature_engagement_tracker/internal/configuration.h
+++ b/chromium/components/feature_engagement_tracker/internal/configuration.h
@@ -6,6 +6,7 @@
#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_CONFIGURATION_H_
#include <map>
+#include <ostream>
#include <set>
#include <string>
#include <vector>
@@ -47,6 +48,7 @@ struct Comparator {
bool operator==(const Comparator& lhs, const Comparator& rhs);
bool operator<(const Comparator& lhs, const Comparator& rhs);
+std::ostream& operator<<(std::ostream& os, const Comparator& comparator);
// A EventConfig contains all the information about how many times
// a particular event should or should not have triggered, for which window
@@ -76,6 +78,7 @@ struct EventConfig {
bool operator==(const EventConfig& lhs, const EventConfig& rhs);
bool operator!=(const EventConfig& lhs, const EventConfig& rhs);
bool operator<(const EventConfig& lhs, const EventConfig& rhs);
+std::ostream& operator<<(std::ostream& os, const EventConfig& event_config);
// A FeatureConfig contains all the configuration for a given feature.
struct FeatureConfig {
@@ -109,13 +112,15 @@ struct FeatureConfig {
};
bool operator==(const FeatureConfig& lhs, const FeatureConfig& rhs);
+std::ostream& operator<<(std::ostream& os, const FeatureConfig& feature_config);
+
// A Configuration contains the current set of runtime configurations.
// It is up to each implementation of Configuration to provide a way to
// register features and their configurations.
class Configuration {
public:
// Convenience alias for typical implementations of Configuration.
- using ConfigMap = std::map<const base::Feature*, FeatureConfig>;
+ using ConfigMap = std::map<std::string, FeatureConfig>;
virtual ~Configuration() = default;
@@ -124,6 +129,11 @@ class Configuration {
virtual const FeatureConfig& GetFeatureConfig(
const base::Feature& feature) const = 0;
+ // Returns the FeatureConfig for the given |feature|. The |feature_name| must
+ // be registered with the Configuration instance.
+ virtual const FeatureConfig& GetFeatureConfigByName(
+ const std::string& feature_name) const = 0;
+
// Returns the immutable ConfigMap that contains all registered features.
virtual const ConfigMap& GetRegisteredFeatures() const = 0;
diff --git a/chromium/components/feature_engagement_tracker/internal/editable_configuration.cc b/chromium/components/feature_engagement_tracker/internal/editable_configuration.cc
index 9559f1ea973..6ebbd7186b3 100644
--- a/chromium/components/feature_engagement_tracker/internal/editable_configuration.cc
+++ b/chromium/components/feature_engagement_tracker/internal/editable_configuration.cc
@@ -6,6 +6,7 @@
#include <map>
+#include "base/feature_list.h"
#include "base/logging.h"
#include "components/feature_engagement_tracker/internal/configuration.h"
@@ -18,12 +19,19 @@ EditableConfiguration::~EditableConfiguration() = default;
void EditableConfiguration::SetConfiguration(
const base::Feature* feature,
const FeatureConfig& feature_config) {
- configs_[feature] = feature_config;
+ configs_[feature->name] = feature_config;
}
const FeatureConfig& EditableConfiguration::GetFeatureConfig(
const base::Feature& feature) const {
- auto it = configs_.find(&feature);
+ auto it = configs_.find(feature.name);
+ DCHECK(it != configs_.end());
+ return it->second;
+}
+
+const FeatureConfig& EditableConfiguration::GetFeatureConfigByName(
+ const std::string& feature_name) const {
+ auto it = configs_.find(feature_name);
DCHECK(it != configs_.end());
return it->second;
}
diff --git a/chromium/components/feature_engagement_tracker/internal/editable_configuration.h b/chromium/components/feature_engagement_tracker/internal/editable_configuration.h
index 7d26122bcee..b0168acc9f7 100644
--- a/chromium/components/feature_engagement_tracker/internal/editable_configuration.h
+++ b/chromium/components/feature_engagement_tracker/internal/editable_configuration.h
@@ -25,6 +25,8 @@ class EditableConfiguration : public Configuration {
// Configuration implementation.
const FeatureConfig& GetFeatureConfig(
const base::Feature& feature) const override;
+ const FeatureConfig& GetFeatureConfigByName(
+ const std::string& feature_name) const override;
const Configuration::ConfigMap& GetRegisteredFeatures() const override;
// Adds a new FeatureConfig to the current configurations. If it already
diff --git a/chromium/components/feature_engagement_tracker/internal/feature_config_condition_validator_unittest.cc b/chromium/components/feature_engagement_tracker/internal/feature_config_condition_validator_unittest.cc
index 3853f39f5b9..652e8450b8e 100644
--- a/chromium/components/feature_engagement_tracker/internal/feature_config_condition_validator_unittest.cc
+++ b/chromium/components/feature_engagement_tracker/internal/feature_config_condition_validator_unittest.cc
@@ -84,7 +84,7 @@ class TestAvailabilityModel : public AvailabilityModel {
base::Optional<uint32_t> GetAvailability(
const base::Feature& feature) const override {
- auto search = availabilities_.find(&feature);
+ auto search = availabilities_.find(feature.name);
if (search == availabilities_.end())
return base::nullopt;
@@ -93,13 +93,13 @@ class TestAvailabilityModel : public AvailabilityModel {
void SetAvailability(const base::Feature* feature,
base::Optional<uint32_t> availability) {
- availabilities_[feature] = availability;
+ availabilities_[feature->name] = availability;
}
private:
bool ready_;
- std::map<const base::Feature*, base::Optional<uint32_t>> availabilities_;
+ std::map<std::string, base::Optional<uint32_t>> availabilities_;
DISALLOW_COPY_AND_ASSIGN(TestAvailabilityModel);
};
diff --git a/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc b/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
index 9d92884481c..f9ccba5354d 100644
--- a/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
+++ b/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
@@ -9,6 +9,8 @@
#include "base/bind.h"
#include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/user_metrics.h"
@@ -35,8 +37,10 @@
namespace feature_engagement_tracker {
namespace {
-const char kEventDBStorageDir[] = "EventDB";
-const char kAvailabilityDBStorageDir[] = "AvailabilityDB";
+const base::FilePath::CharType kEventDBStorageDir[] =
+ FILE_PATH_LITERAL("EventDB");
+const base::FilePath::CharType kAvailabilityDBStorageDir[] =
+ FILE_PATH_LITERAL("AvailabilityDB");
// Creates a FeatureEngagementTrackerImpl that is usable for a demo mode.
std::unique_ptr<FeatureEngagementTracker>
@@ -46,6 +50,8 @@ CreateDemoModeFeatureEngagementTracker() {
std::string chosen_feature_name = base::GetFieldTrialParamValueByFeature(
kIPHDemoMode, kIPHDemoModeFeatureChoiceParam);
+ DVLOG(2) << "Enabling demo mode. Chosen feature: " << chosen_feature_name;
+
std::unique_ptr<EditableConfiguration> configuration =
base::MakeUnique<EditableConfiguration>();
@@ -85,6 +91,7 @@ CreateDemoModeFeatureEngagementTracker() {
FeatureEngagementTracker* FeatureEngagementTracker::Create(
const base::FilePath& storage_dir,
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) {
+ DVLOG(2) << "Creating FeatureEngagementTracker";
if (base::FeatureList::IsEnabled(kIPHDemoMode))
return CreateDemoModeFeatureEngagementTracker().release();
@@ -175,10 +182,13 @@ bool FeatureEngagementTrackerImpl::ShouldTriggerHelpUI(
}
stats::RecordShouldTriggerHelpUI(feature, result);
+ DVLOG(2) << "Trigger result for " << feature.name
+ << ": trigger=" << result.NoErrors() << " " << result;
return result.NoErrors();
}
void FeatureEngagementTrackerImpl::Dismissed(const base::Feature& feature) {
+ DVLOG(2) << "Dismissing " << feature.name;
condition_validator_->NotifyDismissed(feature);
stats::RecordUserDismiss();
}
@@ -203,6 +213,8 @@ void FeatureEngagementTrackerImpl::OnEventModelInitializationFinished(
DCHECK_EQ(success, model_->IsReady());
event_model_initialization_finished_ = true;
+ DVLOG(2) << "Event model initialization result = " << success;
+
MaybePostInitializedCallbacks();
}
@@ -211,6 +223,8 @@ void FeatureEngagementTrackerImpl::OnAvailabilityModelInitializationFinished(
DCHECK_EQ(success, availability_model_->IsReady());
availability_model_initialization_finished_ = true;
+ DVLOG(2) << "Availability model initialization result = " << success;
+
MaybePostInitializedCallbacks();
}
@@ -223,6 +237,8 @@ void FeatureEngagementTrackerImpl::MaybePostInitializedCallbacks() {
if (!IsInitializationFinished())
return;
+ DVLOG(2) << "Initialization finished.";
+
for (auto& callback : on_initialized_callbacks_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(callback, IsInitialized()));
diff --git a/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl_unittest.cc b/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl_unittest.cc
index 74f3454dbc6..3b7fd924c22 100644
--- a/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl_unittest.cc
+++ b/chromium/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl_unittest.cc
@@ -13,6 +13,7 @@
#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/threading/thread_task_runner_handle.h"
#include "components/feature_engagement_tracker/internal/availability_model_impl.h"
@@ -195,6 +196,54 @@ class FeatureEngagementTrackerImplTest : public ::testing::Test {
EXPECT_EQ(count, trigger_event.events(0).count());
}
+ void VerifyHistogramsForFeature(const std::string& histogram_name,
+ bool check,
+ int expected_success_count,
+ int expected_failure_count) {
+ if (!check)
+ return;
+
+ histogram_tester_.ExpectBucketCount(
+ histogram_name, static_cast<int>(stats::TriggerHelpUIResult::SUCCESS),
+ expected_success_count);
+ histogram_tester_.ExpectBucketCount(
+ histogram_name, static_cast<int>(stats::TriggerHelpUIResult::FAILURE),
+ expected_failure_count);
+ }
+
+ // Histogram values are checked only if their respective |check_...| is true,
+ // since inspecting a bucket count for a histogram that has not been recorded
+ // yet leads to an error.
+ void VerifyHistograms(bool check_foo,
+ int expected_foo_success_count,
+ int expected_foo_failure_count,
+ bool check_bar,
+ int expected_bar_success_count,
+ int expected_bar_failure_count,
+ bool check_qux,
+ int expected_qux_success_count,
+ int expected_qux_failure_count) {
+ VerifyHistogramsForFeature("InProductHelp.ShouldTriggerHelpUI.test_foo",
+ check_foo, expected_foo_success_count,
+ expected_foo_failure_count);
+ VerifyHistogramsForFeature("InProductHelp.ShouldTriggerHelpUI.test_bar",
+ check_bar, expected_bar_success_count,
+ expected_bar_failure_count);
+ VerifyHistogramsForFeature("InProductHelp.ShouldTriggerHelpUI.test_qux",
+ check_qux, expected_qux_success_count,
+ expected_qux_failure_count);
+
+ int expected_total_successes = expected_foo_success_count +
+ expected_bar_success_count +
+ expected_qux_success_count;
+ int expected_total_failures = expected_foo_failure_count +
+ expected_bar_failure_count +
+ expected_qux_failure_count;
+ VerifyHistogramsForFeature("InProductHelp.ShouldTriggerHelpUI", true,
+ expected_total_successes,
+ expected_total_failures);
+ }
+
void VerifyUserActionsTriggerChecks(
const base::UserActionTester& user_action_tester,
int expected_foo_count,
@@ -269,6 +318,7 @@ class FeatureEngagementTrackerImplTest : public ::testing::Test {
TestInMemoryStore* store_;
TestAvailabilityModel* availability_model_;
Configuration* configuration_;
+ base::HistogramTester histogram_tester_;
private:
DISALLOW_COPY_AND_ASSIGN(FeatureEngagementTrackerImplTest);
@@ -473,6 +523,7 @@ TEST_F(FeatureEngagementTrackerImplTest, TestTriggering) {
VerifyUserActionsTriggered(user_action_tester, 1, 0, 0);
VerifyUserActionsNotTriggered(user_action_tester, 1, 0, 1);
VerifyUserActionsDismissed(user_action_tester, 0);
+ VerifyHistograms(true, 1, 1, false, 0, 0, true, 0, 1);
// While in-product help is currently showing, no other features should be
// shown.
@@ -484,6 +535,7 @@ TEST_F(FeatureEngagementTrackerImplTest, TestTriggering) {
VerifyUserActionsTriggered(user_action_tester, 1, 0, 0);
VerifyUserActionsNotTriggered(user_action_tester, 1, 1, 2);
VerifyUserActionsDismissed(user_action_tester, 0);
+ VerifyHistograms(true, 1, 1, true, 0, 1, true, 0, 2);
// After dismissing the current in-product help, that feature can not be shown
// again, but a different feature should.
@@ -498,6 +550,7 @@ TEST_F(FeatureEngagementTrackerImplTest, TestTriggering) {
VerifyUserActionsTriggered(user_action_tester, 1, 1, 0);
VerifyUserActionsNotTriggered(user_action_tester, 2, 1, 3);
VerifyUserActionsDismissed(user_action_tester, 1);
+ VerifyHistograms(true, 1, 2, true, 1, 1, true, 0, 3);
// After dismissing the second registered feature, no more in-product help
// should be shown, since kTestFeatureQux is invalid.
@@ -512,6 +565,7 @@ TEST_F(FeatureEngagementTrackerImplTest, TestTriggering) {
VerifyUserActionsTriggered(user_action_tester, 1, 1, 0);
VerifyUserActionsNotTriggered(user_action_tester, 3, 2, 4);
VerifyUserActionsDismissed(user_action_tester, 2);
+ VerifyHistograms(true, 1, 3, true, 1, 2, true, 0, 4);
}
TEST_F(FeatureEngagementTrackerImplTest, TestNotifyEvent) {
diff --git a/chromium/components/feature_engagement_tracker/internal/model_impl.cc b/chromium/components/feature_engagement_tracker/internal/model_impl.cc
index d1032f52a0c..aa1df6e246f 100644
--- a/chromium/components/feature_engagement_tracker/internal/model_impl.cc
+++ b/chromium/components/feature_engagement_tracker/internal/model_impl.cc
@@ -10,6 +10,7 @@
#include <vector>
#include "base/bind.h"
+#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
@@ -52,8 +53,12 @@ void ModelImpl::IncrementEvent(const std::string& event_name,
uint32_t current_day) {
DCHECK(ready_);
- if (!storage_validator_->ShouldStore(event_name))
+ if (!storage_validator_->ShouldStore(event_name)) {
+ DVLOG(2) << "Not incrementing event " << event_name << " @ " << current_day;
return;
+ }
+
+ DVLOG(2) << "Incrementing event " << event_name << " @ " << current_day;
Event& event = GetNonConstEvent(event_name);
for (int i = 0; i < event.events_size(); ++i) {
diff --git a/chromium/components/feature_engagement_tracker/internal/once_condition_validator.cc b/chromium/components/feature_engagement_tracker/internal/once_condition_validator.cc
index 4451e2cedcc..1ae562aaf55 100644
--- a/chromium/components/feature_engagement_tracker/internal/once_condition_validator.cc
+++ b/chromium/components/feature_engagement_tracker/internal/once_condition_validator.cc
@@ -9,8 +9,7 @@
namespace feature_engagement_tracker {
-OnceConditionValidator::OnceConditionValidator()
- : currently_showing_feature_(nullptr) {}
+OnceConditionValidator::OnceConditionValidator() = default;
OnceConditionValidator::~OnceConditionValidator() = default;
@@ -23,26 +22,26 @@ ConditionValidator::Result OnceConditionValidator::MeetsConditions(
ConditionValidator::Result result(true);
result.event_model_ready_ok = model.IsReady();
- result.currently_showing_ok = currently_showing_feature_ == nullptr;
+ result.currently_showing_ok = currently_showing_feature_.empty();
result.config_ok = config.valid;
result.session_rate_ok =
- shown_features_.find(&feature) == shown_features_.end();
+ shown_features_.find(feature.name) == shown_features_.end();
return result;
}
void OnceConditionValidator::NotifyIsShowing(const base::Feature& feature) {
- DCHECK(currently_showing_feature_ == nullptr);
- DCHECK(shown_features_.find(&feature) == shown_features_.end());
- shown_features_.insert(&feature);
- currently_showing_feature_ = &feature;
+ DCHECK(currently_showing_feature_.empty());
+ DCHECK(shown_features_.find(feature.name) == shown_features_.end());
+ shown_features_.insert(feature.name);
+ currently_showing_feature_ = feature.name;
}
void OnceConditionValidator::NotifyDismissed(const base::Feature& feature) {
- DCHECK(&feature == currently_showing_feature_);
- currently_showing_feature_ = nullptr;
+ DCHECK(feature.name == currently_showing_feature_);
+ currently_showing_feature_.clear();
}
} // namespace feature_engagement_tracker
diff --git a/chromium/components/feature_engagement_tracker/internal/once_condition_validator.h b/chromium/components/feature_engagement_tracker/internal/once_condition_validator.h
index 57ea648d316..53cca7bc521 100644
--- a/chromium/components/feature_engagement_tracker/internal/once_condition_validator.h
+++ b/chromium/components/feature_engagement_tracker/internal/once_condition_validator.h
@@ -49,11 +49,11 @@ class OnceConditionValidator : public ConditionValidator {
private:
// Contains all features that have met conditions within the current session.
- std::unordered_set<const base::Feature*> shown_features_;
+ std::unordered_set<std::string> shown_features_;
// Which feature that is currently being shown, or nullptr if nothing is
// currently showing.
- const base::Feature* currently_showing_feature_;
+ std::string currently_showing_feature_;
DISALLOW_COPY_AND_ASSIGN(OnceConditionValidator);
};
diff --git a/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.cc b/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.cc
index 5b82515d505..518fd632f6c 100644
--- a/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.cc
+++ b/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.cc
@@ -11,7 +11,7 @@ namespace feature_engagement_tracker {
SingleInvalidConfiguration::SingleInvalidConfiguration() {
invalid_feature_config_.valid = false;
invalid_feature_config_.used.name = "nothing_to_see_here";
-};
+}
SingleInvalidConfiguration::~SingleInvalidConfiguration() = default;
@@ -20,6 +20,11 @@ const FeatureConfig& SingleInvalidConfiguration::GetFeatureConfig(
return invalid_feature_config_;
}
+const FeatureConfig& SingleInvalidConfiguration::GetFeatureConfigByName(
+ const std::string& feature_name) const {
+ return invalid_feature_config_;
+}
+
const Configuration::ConfigMap&
SingleInvalidConfiguration::GetRegisteredFeatures() const {
return configs_;
diff --git a/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.h b/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.h
index dd0ea1ff7d9..1e069be895e 100644
--- a/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.h
+++ b/chromium/components/feature_engagement_tracker/internal/single_invalid_configuration.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_SINGLE_INVALID_CONFIGURATION_H_
#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_SINGLE_INVALID_CONFIGURATION_H_
+#include <string>
#include <unordered_set>
#include "base/macros.h"
@@ -26,6 +27,8 @@ class SingleInvalidConfiguration : public Configuration {
// Configuration implementation.
const FeatureConfig& GetFeatureConfig(
const base::Feature& feature) const override;
+ const FeatureConfig& GetFeatureConfigByName(
+ const std::string& feature_name) const override;
const Configuration::ConfigMap& GetRegisteredFeatures() const override;
private:
diff --git a/chromium/components/feature_engagement_tracker/internal/stats.cc b/chromium/components/feature_engagement_tracker/internal/stats.cc
index 9593f0319e1..ac780251eda 100644
--- a/chromium/components/feature_engagement_tracker/internal/stats.cc
+++ b/chromium/components/feature_engagement_tracker/internal/stats.cc
@@ -20,11 +20,19 @@ namespace {
const char kEventStoreSuffix[] = "EventStore";
const char kAvailabilityStoreSuffix[] = "AvailabilityStore";
+// A shadow histogram across all features. Also the base name for the suffix
+// based feature specific histograms; for example for IPH_MyFun, it would be:
+// InProductHelp.ShouldTriggerHelpUI.IPH_MyFun.
+const char kShouldTriggerHelpUIHistogram[] =
+ "InProductHelp.ShouldTriggerHelpUI";
+
// Helper function to log a TriggerHelpUIResult.
void LogTriggerHelpUIResult(const std::string& name,
TriggerHelpUIResult result) {
// Must not use histograms macros here because we pass in the histogram name.
base::UmaHistogramEnumeration(name, result, TriggerHelpUIResult::COUNT);
+ base::UmaHistogramEnumeration(kShouldTriggerHelpUIHistogram, result,
+ TriggerHelpUIResult::COUNT);
}
} // namespace
@@ -51,12 +59,12 @@ void RecordNotifyEvent(const std::string& event_name,
const Configuration::ConfigMap& features = config->GetRegisteredFeatures();
std::string feature_name;
for (const auto& element : features) {
- const base::Feature* feature = element.first;
+ const std::string fname = element.first;
const FeatureConfig& feature_config = element.second;
// Track used event separately.
if (feature_config.used.name == event_name) {
- feature_name = feature->name;
+ feature_name = fname;
DCHECK(!feature_name.empty());
std::string used_event_action = "InProductHelp.NotifyUsedEvent.";
used_event_action.append(feature_name);
@@ -67,12 +75,12 @@ void RecordNotifyEvent(const std::string& event_name,
// Find if the |event_name| matches any configuration.
for (const auto& event : feature_config.event_configs) {
if (event.name == event_name) {
- feature_name = feature->name;
+ feature_name = fname;
break;
}
}
if (feature_config.trigger.name == event_name) {
- feature_name = feature->name;
+ feature_name = fname;
break;
}
}
@@ -93,8 +101,9 @@ void RecordNotifyEvent(const std::string& event_name,
void RecordShouldTriggerHelpUI(const base::Feature& feature,
const ConditionValidator::Result& result) {
// Records the user action.
- std::string name = "InProductHelp.ShouldTriggerHelpUI.";
- name.append(feature.name);
+ std::string name = std::string(kShouldTriggerHelpUIHistogram)
+ .append(".")
+ .append(feature.name);
base::RecordComputedAction(name);
// Total count histogram, used to compute the percentage of each failure type,
diff --git a/chromium/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc b/chromium/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc
index 6bcf53c77c4..e9d08c08ea0 100644
--- a/chromium/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc
+++ b/chromium/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc
@@ -17,6 +17,7 @@ base::Time GetTime(int year, int month, int day) {
exploded_time.year = year;
exploded_time.month = month;
exploded_time.day_of_month = day;
+ exploded_time.day_of_week = 0;
exploded_time.hour = 0;
exploded_time.minute = 0;
exploded_time.second = 0;
diff --git a/chromium/components/feature_engagement_tracker/public/BUILD.gn b/chromium/components/feature_engagement_tracker/public/BUILD.gn
index 6d431bf09d1..55bd9ead819 100644
--- a/chromium/components/feature_engagement_tracker/public/BUILD.gn
+++ b/chromium/components/feature_engagement_tracker/public/BUILD.gn
@@ -9,6 +9,9 @@ if (is_android) {
source_set("public") {
sources = [
+ "event_constants.cc",
+ "event_constants.h",
+ "feature_constants.cc",
"feature_constants.h",
"feature_engagement_tracker.h",
"feature_list.cc",
diff --git a/chromium/components/feature_engagement_tracker/public/event_constants.cc b/chromium/components/feature_engagement_tracker/public/event_constants.cc
new file mode 100644
index 00000000000..1638095c4e2
--- /dev/null
+++ b/chromium/components/feature_engagement_tracker/public/event_constants.cc
@@ -0,0 +1,33 @@
+// 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/feature_engagement_tracker/public/event_constants.h"
+
+namespace feature_engagement_tracker {
+
+namespace events {
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+const char kOmniboxInteraction[] = "omnibox_used";
+
+const char kHistoryDeleted[] = "history_deleted";
+const char kIncognitoWindowOpened[] = "incognito_window_opened";
+
+const char kSessionTime[] = "session_time";
+#endif // defined(OS_WIN) || defined(OS_LINUX)
+
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
+const char kNewTabOpened[] = "new_tab_opened";
+#endif // defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
+
+#if defined(OS_IOS)
+const char kChromeOpened[] = "chrome_opened";
+const char kIncognitoTabOpened[] = "incognito_tab_opened";
+const char kClearedBrowsingData[] = "cleared_browsing_data";
+const char kViewedReadingList[] = "viewed_reading_list";
+#endif // defined(OS_IOS)
+
+} // namespace events
+
+} // namespace feature_engagement_tracker
diff --git a/chromium/components/feature_engagement_tracker/public/event_constants.h b/chromium/components/feature_engagement_tracker/public/event_constants.h
new file mode 100644
index 00000000000..fbe217c3f3b
--- /dev/null
+++ b/chromium/components/feature_engagement_tracker/public/event_constants.h
@@ -0,0 +1,67 @@
+// 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_FEATURE_ENGAGEMENT_TRACKER_PUBLIC_EVENT_CONSTANTS_H_
+#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_PUBLIC_EVENT_CONSTANTS_H_
+
+#include "build/build_config.h"
+
+namespace feature_engagement_tracker {
+
+namespace events {
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+// All the events declared below are the string names
+// of deferred onboarding events for the New Tab.
+
+// The user has interacted with the omnibox.
+extern const char kOmniboxInteraction[];
+
+// All the events declared below are the string names
+// of deferred onboarding events for the Incognito Window
+
+// The user has deleted browsing history.
+extern const char kHistoryDeleted[];
+// The user has opened an incognito window.
+extern const char kIncognitoWindowOpened[];
+
+// All the events declared below are the string names
+// of common deferred onboarding events
+
+// The user has accumulated 2 hours of active session time (one-off event).
+extern const char kSessionTime[];
+
+#endif // defined(OS_WIN) || defined(OS_LINUX)
+
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
+// This event is included in the deferred onboarding events for the New Tab
+// described above, but it is also used on iOS, so it must be compiled
+// separately.
+
+// The user has opened a new tab.
+extern const char kNewTabOpened[];
+
+#endif // defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
+
+#if defined(OS_IOS)
+
+// The user has opened Chrome (cold start or from background).
+extern const char kChromeOpened[];
+
+// The user has opened an incognito tab.
+extern const char kIncognitoTabOpened[];
+
+// The user has cleared their browsing data.
+extern const char kClearedBrowsingData[];
+
+// The user has viewed their reading list.
+extern const char kViewedReadingList[];
+
+#endif // defined(OS_IOS)
+
+} // namespace events
+
+} // namespace feature_engagement_tracker
+
+#endif // COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_PUBLIC_EVENT_CONSTANTS_H_
diff --git a/chromium/components/feature_engagement_tracker/public/feature_constants.cc b/chromium/components/feature_engagement_tracker/public/feature_constants.cc
new file mode 100644
index 00000000000..066bcdf28f5
--- /dev/null
+++ b/chromium/components/feature_engagement_tracker/public/feature_constants.cc
@@ -0,0 +1,44 @@
+/// 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/feature_engagement_tracker/public/feature_constants.h"
+
+namespace feature_engagement_tracker {
+
+const base::Feature kIPHDemoMode{"IPH_DemoMode",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kIPHDummyFeature{"IPH_Dummy",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+#if defined(OS_ANDROID)
+const base::Feature kIPHDataSaverDetailFeature{
+ "IPH_DataSaverDetail", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHDataSaverPreviewFeature{
+ "IPH_DataSaverPreview", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHDownloadHomeFeature{"IPH_DownloadHome",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHDownloadPageFeature{"IPH_DownloadPage",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHDownloadPageScreenshotFeature{
+ "IPH_DownloadPageScreenshot", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // defined(OS_ANDROID)
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+const base::Feature kIPHIncognitoWindowFeature{
+ "IPH_IncognitoWindow", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHNewTabFeature{"IPH_NewTab",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // defined(OS_WIN) || defined(OS_LINUX)
+
+#if defined(OS_IOS)
+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};
+#endif // defined(OS_IOS)
+
+} // namespace feature_engagement_tracker
diff --git a/chromium/components/feature_engagement_tracker/public/feature_constants.h b/chromium/components/feature_engagement_tracker/public/feature_constants.h
index 007a6c6e5e1..c0440fc919a 100644
--- a/chromium/components/feature_engagement_tracker/public/feature_constants.h
+++ b/chromium/components/feature_engagement_tracker/public/feature_constants.h
@@ -6,25 +6,37 @@
#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_PUBLIC_FEATURE_CONSTANTS_H_
#include "base/feature_list.h"
+#include "build/build_config.h"
namespace feature_engagement_tracker {
-// A feature for enabling a demonstration mode for In-Product Help.
-// This needs to be constexpr because of how it is used in
-// //chrome/browser/about_flags.cc.
-constexpr base::Feature kIPHDemoMode = {"IPH_DemoMode",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
-// All the features declared below should also be declared in the Java
-// version: org.chromium.components.feature_engagement_tracker.FeatureConstants.
-// These need to be constexpr, because they are used as
-// flags_ui::FeatureEntry::FeatureParams in feature_list.h.
-constexpr base::Feature kIPHDataSaverPreview = {
- "IPH_DataSaverPreview", base::FEATURE_DISABLED_BY_DEFAULT};
-constexpr base::Feature kIPHDownloadPageFeature = {
- "IPH_DownloadPage", base::FEATURE_DISABLED_BY_DEFAULT};
-constexpr base::Feature kIPHDownloadHomeFeature = {
- "IPH_DownloadHome", base::FEATURE_DISABLED_BY_DEFAULT};
+// A feature for enabling a demonstration mode for In-Product Help (IPH).
+extern const base::Feature kIPHDemoMode;
+
+// A feature to ensure all arrays can contain at least one feature.
+extern const base::Feature kIPHDummyFeature;
+
+// All the features declared for Android below that are also used in Java,
+// should also be declared in:
+// org.chromium.components.feature_engagement_tracker.FeatureConstants.
+#if defined(OS_ANDROID)
+extern const base::Feature kIPHDataSaverDetailFeature;
+extern const base::Feature kIPHDataSaverPreviewFeature;
+extern const base::Feature kIPHDownloadHomeFeature;
+extern const base::Feature kIPHDownloadPageFeature;
+extern const base::Feature kIPHDownloadPageScreenshotFeature;
+#endif // defined(OS_ANDROID)
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+extern const base::Feature kIPHIncognitoWindowFeature;
+extern const base::Feature kIPHNewTabFeature;
+#endif // defined(OS_WIN) || defined(OS_LINUX)
+
+#if defined(OS_IOS)
+extern const base::Feature kIPHNewTabTipFeature;
+extern const base::Feature kIPHNewIncognitoTabTipFeature;
+extern const base::Feature kIPHBadgedReadingListFeature;
+#endif // defined(OS_IOS)
} // namespace feature_engagement_tracker
diff --git a/chromium/components/feature_engagement_tracker/public/feature_list.cc b/chromium/components/feature_engagement_tracker/public/feature_list.cc
index e64aaf5a33c..92e2fd309c7 100644
--- a/chromium/components/feature_engagement_tracker/public/feature_list.cc
+++ b/chromium/components/feature_engagement_tracker/public/feature_list.cc
@@ -4,57 +4,37 @@
#include "components/feature_engagement_tracker/public/feature_list.h"
-#include "base/feature_list.h"
#include "components/feature_engagement_tracker/public/feature_constants.h"
-#include "components/flags_ui/feature_entry.h"
namespace feature_engagement_tracker {
namespace {
-
-// Defines a const flags_ui::FeatureEntry::FeatureParam for the given
-// base::Feature. The constant name will be on the form
-// kFooFeature --> kFooFeatureVariation. This is intended to be used with
-// VARIATION_ENTRY below to be able to insert it into an array of
-// flags_ui::FeatureEntry::FeatureVariation.
-#define DEFINE_VARIATION_PARAM(base_feature) \
- constexpr flags_ui::FeatureEntry::FeatureParam base_feature##Variation[] = { \
- {kIPHDemoModeFeatureChoiceParam, base_feature.name}}
-
-// Defines a single flags_ui::FeatureEntry::FeatureVariation entry, fully
-// enclosed. This is intended to be used with the declaration of
-// |kIPHDemoModeChoiceVariations| below.
-#define VARIATION_ENTRY(base_feature) \
- { \
- base_feature##Variation[0].param_value, base_feature##Variation, \
- arraysize(base_feature##Variation), nullptr \
- }
-
// Whenever a feature is added to |kAllFeatures|, it should also be added as
-// DEFINE_VARIATION_PARAM below, and also added to the
+// DEFINE_VARIATION_PARAM in the header, and also added to the
// |kIPHDemoModeChoiceVariations| array.
const base::Feature* kAllFeatures[] = {
- &kIPHDataSaverPreview, &kIPHDownloadPageFeature, &kIPHDownloadHomeFeature};
-
-// Defines a flags_ui::FeatureEntry::FeatureParam for each feature.
-DEFINE_VARIATION_PARAM(kIPHDataSaverPreview);
-DEFINE_VARIATION_PARAM(kIPHDownloadPageFeature);
-DEFINE_VARIATION_PARAM(kIPHDownloadHomeFeature);
-
+ &kIPHDummyFeature, // Ensures non-empty array for all platforms.
+#if defined(OS_ANDROID)
+ &kIPHDataSaverDetailFeature,
+ &kIPHDataSaverPreviewFeature,
+ &kIPHDownloadHomeFeature,
+ &kIPHDownloadPageFeature,
+ &kIPHDownloadPageScreenshotFeature,
+#endif // defined(OS_ANDROID)
+#if defined(OS_WIN) || defined(OS_LINUX)
+ &kIPHIncognitoWindowFeature,
+ &kIPHNewTabFeature,
+#endif // defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_IOS)
+ &kIPHNewTabTipFeature,
+ &kIPHNewIncognitoTabTipFeature,
+ &kIPHBadgedReadingListFeature,
+#endif // defined(OS_IOS)
+};
} // namespace
const char kIPHDemoModeFeatureChoiceParam[] = "chosen_feature";
-constexpr flags_ui::FeatureEntry::FeatureVariation
- kIPHDemoModeChoiceVariations[] = {
- VARIATION_ENTRY(kIPHDataSaverPreview),
- VARIATION_ENTRY(kIPHDownloadPageFeature),
- VARIATION_ENTRY(kIPHDownloadHomeFeature),
- // Note: When changing the number of entries in this array, the constant
- // kIPHDemoModeChoiceVariationsLen in feature_list.h must also be
- // updated to reflect the real size.
-};
-
std::vector<const base::Feature*> GetAllFeatures() {
return std::vector<const base::Feature*>(
kAllFeatures, kAllFeatures + arraysize(kAllFeatures));
diff --git a/chromium/components/feature_engagement_tracker/public/feature_list.h b/chromium/components/feature_engagement_tracker/public/feature_list.h
index 0cb72bd288e..a815e40e484 100644
--- a/chromium/components/feature_engagement_tracker/public/feature_list.h
+++ b/chromium/components/feature_engagement_tracker/public/feature_list.h
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_FEATURE_LIST_H_
-#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_FEATURE_LIST_H_
+#ifndef COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_PUBLIC_FEATURE_LIST_H_
+#define COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_PUBLIC_FEATURE_LIST_H_
#include <vector>
#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "components/feature_engagement_tracker/public/feature_constants.h"
#include "components/flags_ui/feature_entry.h"
namespace feature_engagement_tracker {
@@ -19,19 +21,78 @@ using FeatureVector = std::vector<const base::Feature*>;
// feature (if any) was selected by the end user.
extern const char kIPHDemoModeFeatureChoiceParam[];
-// The length of kIPHDemoModeChoiceVariations. This must be updated whenever
-// new features are added to the demo mode selection.
-constexpr size_t kIPHDemoModeChoiceVariationsLen = 3;
+namespace {
+
+// Defines a const flags_ui::FeatureEntry::FeatureParam for the given
+// base::Feature. The constant name will be on the form
+// kFooFeature --> kFooFeatureVariation. The |feature_name| argument must
+// match the base::Feature::name member of the |base_feature|.
+// This is intended to be used with VARIATION_ENTRY below to be able to insert
+// it into an array of flags_ui::FeatureEntry::FeatureVariation.
+#define DEFINE_VARIATION_PARAM(base_feature, feature_name) \
+ constexpr flags_ui::FeatureEntry::FeatureParam base_feature##Variation[] = { \
+ {kIPHDemoModeFeatureChoiceParam, feature_name}}
+
+// Defines a single flags_ui::FeatureEntry::FeatureVariation entry, fully
+// enclosed. This is intended to be used with the declaration of
+// |kIPHDemoModeChoiceVariations| below.
+#define VARIATION_ENTRY(base_feature) \
+ { \
+ base_feature##Variation[0].param_value, base_feature##Variation, \
+ arraysize(base_feature##Variation), nullptr \
+ }
+
+// Defines a flags_ui::FeatureEntry::FeatureParam for each feature.
+DEFINE_VARIATION_PARAM(kIPHDummyFeature, "IPH_Dummy");
+#if defined(OS_ANDROID)
+DEFINE_VARIATION_PARAM(kIPHDataSaverDetailFeature, "IPH_DataSaverDetail");
+DEFINE_VARIATION_PARAM(kIPHDataSaverPreviewFeature, "IPH_DataSaverPreview");
+DEFINE_VARIATION_PARAM(kIPHDownloadHomeFeature, "IPH_DownloadHome");
+DEFINE_VARIATION_PARAM(kIPHDownloadPageFeature, "IPH_DownloadPage");
+DEFINE_VARIATION_PARAM(kIPHDownloadPageScreenshotFeature,
+ "IPH_DownloadPageScreenshot");
+#endif // defined(OS_ANDROID)
+#if defined(OS_WIN) || defined(OS_LINUX)
+DEFINE_VARIATION_PARAM(kIPHIncognitoWindowFeature, "IPH_IncognitoWindow");
+DEFINE_VARIATION_PARAM(kIPHNewTabFeature, "IPH_NewTab");
+#endif // defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_IOS)
+DEFINE_VARIATION_PARAM(kIPHNewTabTipFeature, "IPH_NewTabTip");
+DEFINE_VARIATION_PARAM(kIPHNewIncognitoTabTipFeature, "IPH_NewIncognitoTabTip");
+DEFINE_VARIATION_PARAM(kIPHBadgedReadingListFeature, "IPH_BadgedReadingList");
+#endif // defined(OS_IOS)
+
+} // namespace
// Defines the array of which features should be listed in the chrome://flags
// UI to be able to select them alone for demo-mode. The features listed here
// are possible to enable on their own in demo mode.
-extern const flags_ui::FeatureEntry::FeatureVariation
- kIPHDemoModeChoiceVariations[kIPHDemoModeChoiceVariationsLen];
+constexpr flags_ui::FeatureEntry::FeatureVariation
+ kIPHDemoModeChoiceVariations[] = {
+#if defined(OS_ANDROID)
+ VARIATION_ENTRY(kIPHDataSaverDetailFeature),
+ VARIATION_ENTRY(kIPHDataSaverPreviewFeature),
+ VARIATION_ENTRY(kIPHDownloadHomeFeature),
+ VARIATION_ENTRY(kIPHDownloadPageFeature),
+ VARIATION_ENTRY(kIPHDownloadPageScreenshotFeature),
+#elif defined(OS_WIN) || defined(OS_LINUX)
+ VARIATION_ENTRY(kIPHIncognitoWindowFeature),
+ VARIATION_ENTRY(kIPHNewTabFeature),
+#elif defined(OS_IOS)
+ VARIATION_ENTRY(kIPHNewTabTipFeature),
+ VARIATION_ENTRY(kIPHNewIncognitoTabTipFeature),
+ VARIATION_ENTRY(kIPHBadgedReadingListFeature),
+#else
+ VARIATION_ENTRY(kIPHDummyFeature), // Ensures non-empty array.
+#endif
+};
+
+#undef DEFINE_VARIATION_PARAM
+#undef VARIATION_ENTRY
// Returns all the features that are in use for engagement tracking.
FeatureVector GetAllFeatures();
} // namespace feature_engagement_tracker
-#endif // COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_INTERNAL_FEATURE_LIST_H_
+#endif // COMPONENTS_FEATURE_ENGAGEMENT_TRACKER_PUBLIC_FEATURE_LIST_H_
diff --git a/chromium/components/feature_engagement_tracker/test/BUILD.gn b/chromium/components/feature_engagement_tracker/test/BUILD.gn
new file mode 100644
index 00000000000..41a0b9b6b1f
--- /dev/null
+++ b/chromium/components/feature_engagement_tracker/test/BUILD.gn
@@ -0,0 +1,18 @@
+# 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.
+
+source_set("test_support") {
+ testonly = true
+
+ sources = [
+ "test_feature_engagement_tracker.cc",
+ "test_feature_engagement_tracker.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/feature_engagement_tracker/internal",
+ "//components/feature_engagement_tracker/public",
+ ]
+}
diff --git a/chromium/components/feedback/BUILD.gn b/chromium/components/feedback/BUILD.gn
index 28cae94debc..e92d5d0e4ee 100644
--- a/chromium/components/feedback/BUILD.gn
+++ b/chromium/components/feedback/BUILD.gn
@@ -24,6 +24,10 @@ static_library("feedback") {
"feedback_uploader_factory.h",
"feedback_util.cc",
"feedback_util.h",
+ "system_logs/system_logs_fetcher.cc",
+ "system_logs/system_logs_fetcher.h",
+ "system_logs/system_logs_source.cc",
+ "system_logs/system_logs_source.h",
"tracing_manager.cc",
"tracing_manager.h",
]
diff --git a/chromium/components/feedback/feedback_data_unittest.cc b/chromium/components/feedback/feedback_data_unittest.cc
index 443d3b53044..5171b75af2e 100644
--- a/chromium/components/feedback/feedback_data_unittest.cc
+++ b/chromium/components/feedback/feedback_data_unittest.cc
@@ -10,6 +10,7 @@
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
+#include "components/feedback/feedback_report.h"
#include "components/feedback/feedback_uploader.h"
#include "components/feedback/feedback_uploader_factory.h"
#include "components/keyed_service/core/keyed_service.h"
@@ -20,23 +21,28 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace feedback {
+
namespace {
-const char kHistograms[] = "";
-const char kImageData[] = "";
-const char kFileData[] = "";
+constexpr char kHistograms[] = "";
+constexpr char kImageData[] = "";
+constexpr char kFileData[] = "";
class MockUploader : public feedback::FeedbackUploader, public KeyedService {
public:
- MockUploader(content::BrowserContext* context)
- : FeedbackUploader(context ? context->GetPath() : base::FilePath()) {}
+ MockUploader(content::BrowserContext* context,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : FeedbackUploader(context ? context->GetPath() : base::FilePath(),
+ task_runner) {}
- MOCK_METHOD1(DispatchReport, void(const std::string&));
+ MOCK_METHOD1(DispatchReport, void(scoped_refptr<FeedbackReport>));
};
std::unique_ptr<KeyedService> CreateFeedbackUploaderService(
content::BrowserContext* context) {
- std::unique_ptr<MockUploader> uploader(new MockUploader(context));
+ auto uploader = base::MakeUnique<MockUploader>(
+ context, FeedbackUploaderFactory::CreateUploaderTaskRunner());
EXPECT_CALL(*uploader, DispatchReport(testing::_)).Times(1);
return std::move(uploader);
}
@@ -47,8 +53,6 @@ std::unique_ptr<std::string> MakeScoped(const char* str) {
} // namespace
-namespace feedback {
-
class FeedbackDataTest : public testing::Test {
protected:
FeedbackDataTest()
diff --git a/chromium/components/feedback/feedback_report.cc b/chromium/components/feedback/feedback_report.cc
index fa0e4631633..48f825ec66f 100644
--- a/chromium/components/feedback/feedback_report.cc
+++ b/chromium/components/feedback/feedback_report.cc
@@ -15,10 +15,10 @@
namespace {
-const base::FilePath::CharType kFeedbackReportFilenameWildcard[] =
+constexpr base::FilePath::CharType kFeedbackReportFilenameWildcard[] =
FILE_PATH_LITERAL("Feedback Report.*");
-const char kFeedbackReportFilenamePrefix[] = "Feedback Report.";
+constexpr char kFeedbackReportFilenamePrefix[] = "Feedback Report.";
void WriteReportOnBlockingPool(const base::FilePath reports_path,
const base::FilePath& file,
@@ -29,7 +29,7 @@ void WriteReportOnBlockingPool(const base::FilePath reports_path,
if (!base::CreateDirectoryAndGetError(reports_path, &error))
return;
}
- base::ImportantFileWriter::WriteFileAtomically(file, data);
+ base::ImportantFileWriter::WriteFileAtomically(file, data, "FeedbackReport");
}
} // namespace
diff --git a/chromium/components/feedback/feedback_report.h b/chromium/components/feedback/feedback_report.h
index 11826c26530..d3fdb8d3c82 100644
--- a/chromium/components/feedback/feedback_report.h
+++ b/chromium/components/feedback/feedback_report.h
@@ -45,6 +45,7 @@ class FeedbackReport : public base::RefCounted<FeedbackReport> {
void DeleteReportOnDisk();
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_; }
private:
diff --git a/chromium/components/feedback/feedback_uploader.cc b/chromium/components/feedback/feedback_uploader.cc
index 7f8b79672ba..a17c9a7df03 100644
--- a/chromium/components/feedback/feedback_uploader.cc
+++ b/chromium/components/feedback/feedback_uploader.cc
@@ -8,102 +8,100 @@
#include "base/callback.h"
#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/sequenced_task_runner.h"
-#include "base/task_runner_util.h"
-#include "base/task_scheduler/post_task.h"
#include "components/feedback/feedback_report.h"
+#include "components/feedback/feedback_switches.h"
namespace feedback {
+
namespace {
-const char kFeedbackPostUrl[] =
+constexpr char kFeedbackPostUrl[] =
"https://www.google.com/tools/feedback/chrome/__submit";
-const int64_t kRetryDelayMinutes = 60;
-
-const base::FilePath::CharType kFeedbackReportPath[] =
+constexpr base::FilePath::CharType kFeedbackReportPath[] =
FILE_PATH_LITERAL("Feedback Reports");
-} // namespace
-
-bool FeedbackUploader::ReportsUploadTimeComparator::operator()(
- const scoped_refptr<FeedbackReport>& a,
- const scoped_refptr<FeedbackReport>& b) const {
- return a->upload_at() > b->upload_at();
+// 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
+// FeedbackUploader::SetMinimumRetryDelayForTesting().
+base::TimeDelta g_minimum_retry_delay = base::TimeDelta::FromMinutes(60);
+
+GURL GetFeedbackPostGURL() {
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ return GURL(command_line.HasSwitch(switches::kFeedbackServer)
+ ? command_line.GetSwitchValueASCII(switches::kFeedbackServer)
+ : kFeedbackPostUrl);
}
-FeedbackUploader::FeedbackUploader(const base::FilePath& path)
- : report_path_(path.Append(kFeedbackReportPath)),
- retry_delay_(base::TimeDelta::FromMinutes(kRetryDelayMinutes)),
- url_(kFeedbackPostUrl) {
- Init();
-}
+} // namespace
-FeedbackUploader::FeedbackUploader(const base::FilePath& path,
- const std::string& url)
- : report_path_(path.Append(kFeedbackReportPath)),
- retry_delay_(base::TimeDelta::FromMinutes(kRetryDelayMinutes)),
- url_(url) {
- Init();
+FeedbackUploader::FeedbackUploader(
+ const base::FilePath& path,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : feedback_reports_path_(path.Append(kFeedbackReportPath)),
+ task_runner_(task_runner),
+ retry_delay_(g_minimum_retry_delay),
+ feedback_post_url_(GetFeedbackPostGURL()) {
+ DCHECK(task_runner_);
}
FeedbackUploader::~FeedbackUploader() {}
-void FeedbackUploader::Init() {
- dispatch_callback_ = base::Bind(&FeedbackUploader::DispatchReport,
- AsWeakPtr());
+// static
+void FeedbackUploader::SetMinimumRetryDelayForTesting(base::TimeDelta delay) {
+ g_minimum_retry_delay = delay;
}
void FeedbackUploader::QueueReport(const std::string& data) {
QueueReportWithDelay(data, base::TimeDelta());
}
+void FeedbackUploader::OnReportUploadSuccess() {
+ retry_delay_ = g_minimum_retry_delay;
+ UpdateUploadTimer();
+}
+
+void FeedbackUploader::OnReportUploadFailure(
+ scoped_refptr<FeedbackReport> report) {
+ // Implement a backoff delay by doubling the retry delay on each failure.
+ retry_delay_ *= 2;
+ report->set_upload_at(retry_delay_ + base::Time::Now());
+ reports_queue_.emplace(report);
+ UpdateUploadTimer();
+}
+
+bool FeedbackUploader::ReportsUploadTimeComparator::operator()(
+ const scoped_refptr<FeedbackReport>& a,
+ const scoped_refptr<FeedbackReport>& b) const {
+ return a->upload_at() > b->upload_at();
+}
+
void FeedbackUploader::UpdateUploadTimer() {
if (reports_queue_.empty())
return;
scoped_refptr<FeedbackReport> report = reports_queue_.top();
- base::Time now = base::Time::Now();
+ const base::Time now = base::Time::Now();
if (report->upload_at() <= now) {
reports_queue_.pop();
- dispatch_callback_.Run(report->data());
+ DispatchReport(report);
report->DeleteReportOnDisk();
} else {
// Stop the old timer and start an updated one.
- if (upload_timer_.IsRunning())
- upload_timer_.Stop();
+ upload_timer_.Stop();
upload_timer_.Start(
FROM_HERE, report->upload_at() - now, this,
&FeedbackUploader::UpdateUploadTimer);
}
}
-void FeedbackUploader::RetryReport(const std::string& data) {
- QueueReportWithDelay(data, retry_delay_);
-}
-
void FeedbackUploader::QueueReportWithDelay(const std::string& data,
base::TimeDelta delay) {
- // Uses a BLOCK_SHUTDOWN file task runner because we really don't want to
- // lose reports.
- scoped_refptr<base::SequencedTaskRunner> task_runner =
- base::CreateSequencedTaskRunnerWithTraits(
- {base::MayBlock(), base::TaskPriority::BACKGROUND,
- base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
-
- reports_queue_.push(new FeedbackReport(report_path_,
- base::Time::Now() + delay,
- data,
- task_runner));
+ reports_queue_.emplace(base::MakeRefCounted<FeedbackReport>(
+ feedback_reports_path_, base::Time::Now() + delay, data, task_runner_));
UpdateUploadTimer();
}
-void FeedbackUploader::setup_for_test(
- const ReportDataCallback& dispatch_callback,
- const base::TimeDelta& retry_delay) {
- dispatch_callback_ = dispatch_callback;
- retry_delay_ = retry_delay;
-}
-
} // namespace feedback
diff --git a/chromium/components/feedback/feedback_uploader.h b/chromium/components/feedback/feedback_uploader.h
index 1b05727327b..eaed704ddd1 100644
--- a/chromium/components/feedback/feedback_uploader.h
+++ b/chromium/components/feedback/feedback_uploader.h
@@ -6,18 +6,19 @@
#define COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_H_
#include <queue>
-#include <string>
+#include <vector>
-#include "base/files/file_util.h"
+#include "base/files/file_path.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 "base/time/time.h"
#include "base/timer/timer.h"
+#include "url/gurl.h"
namespace feedback {
-typedef base::Callback<void(const std::string&)> ReportDataCallback;
-
class FeedbackReport;
// FeedbackUploader is used to add a feedback report to the queue of reports
@@ -25,19 +26,41 @@ class FeedbackReport;
// tried again when it's turn comes up next in the queue.
class FeedbackUploader : public base::SupportsWeakPtr<FeedbackUploader> {
public:
- FeedbackUploader(const base::FilePath& path);
FeedbackUploader(const base::FilePath& path,
- const std::string& url);
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
virtual ~FeedbackUploader();
- // Queues a report for uploading.
- virtual void QueueReport(const std::string& data);
+ static void SetMinimumRetryDelayForTesting(base::TimeDelta delay);
- base::FilePath GetFeedbackReportsPath() { return report_path_; }
+ // Queues a report for uploading.
+ void QueueReport(const std::string& data);
bool QueueEmpty() const { return reports_queue_.empty(); }
+ const base::FilePath& feedback_reports_path() const {
+ return feedback_reports_path_;
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
+ return task_runner_;
+ }
+
+ base::TimeDelta retry_delay() const { return retry_delay_; }
+
+ const GURL& feedback_post_url() const { return feedback_post_url_; }
+
protected:
+ // Invoked when a feedback report upload succeeds. It will reset the
+ // |retry_delay_| to its minimum value and schedules the next report upload if
+ // any.
+ void OnReportUploadSuccess();
+
+ // Invoked when |report| fails to upload. It will double the |retry_delay_|
+ // and reenqueue |report| with the new delay. All subsequent retries will keep
+ // increasing the delay until a successful upload is encountered.
+ void OnReportUploadFailure(scoped_refptr<FeedbackReport> report);
+
+ private:
friend class FeedbackUploaderTest;
struct ReportsUploadTimeComparator {
@@ -45,34 +68,31 @@ class FeedbackUploader : public base::SupportsWeakPtr<FeedbackUploader> {
const scoped_refptr<FeedbackReport>& b) const;
};
- void Init();
-
// Dispatches the report to be uploaded.
- virtual void DispatchReport(const std::string& data) = 0;
+ virtual void DispatchReport(scoped_refptr<FeedbackReport> report) = 0;
// Update our timer for uploading the next report.
void UpdateUploadTimer();
- // Requeue this report with a delay.
- void RetryReport(const std::string& data);
-
void QueueReportWithDelay(const std::string& data, base::TimeDelta delay);
- void setup_for_test(const ReportDataCallback& dispatch_callback,
- const base::TimeDelta& retry_delay);
+ const base::FilePath feedback_reports_path_;
- base::FilePath report_path_;
// Timer to upload the next report at.
base::OneShotTimer upload_timer_;
+
+ // See comment of |FeedbackUploaderFactory::task_runner_|.
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
// Priority queue of reports prioritized by the time the report is supposed
// to be uploaded at.
std::priority_queue<scoped_refptr<FeedbackReport>,
- std::vector<scoped_refptr<FeedbackReport> >,
- ReportsUploadTimeComparator> reports_queue_;
+ std::vector<scoped_refptr<FeedbackReport>>,
+ ReportsUploadTimeComparator>
+ reports_queue_;
- ReportDataCallback dispatch_callback_;
base::TimeDelta retry_delay_;
- std::string url_;
+ const GURL feedback_post_url_;
DISALLOW_COPY_AND_ASSIGN(FeedbackUploader);
};
diff --git a/chromium/components/feedback/feedback_uploader_chrome.cc b/chromium/components/feedback/feedback_uploader_chrome.cc
index 7587e5950d3..ff8f7caee65 100644
--- a/chromium/components/feedback/feedback_uploader_chrome.cc
+++ b/chromium/components/feedback/feedback_uploader_chrome.cc
@@ -4,15 +4,10 @@
#include "components/feedback/feedback_uploader_chrome.h"
-#include <string>
-
#include "base/callback.h"
-#include "base/command_line.h"
#include "base/files/file_path.h"
-#include "base/task_runner_util.h"
#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"
@@ -29,19 +24,17 @@ const char kProtoBufMimeType[] = "application/x-protobuf";
} // namespace
-FeedbackUploaderChrome::FeedbackUploaderChrome(content::BrowserContext* context)
- : FeedbackUploader(context ? context->GetPath() : base::FilePath()),
+FeedbackUploaderChrome::FeedbackUploaderChrome(
+ content::BrowserContext* context,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : FeedbackUploader(context ? context->GetPath() : base::FilePath(),
+ task_runner),
context_(context) {
CHECK(context_);
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- if (command_line.HasSwitch(switches::kFeedbackServer))
- url_ = command_line.GetSwitchValueASCII(switches::kFeedbackServer);
}
-void FeedbackUploaderChrome::DispatchReport(const std::string& data) {
- GURL post_url(url_);
-
+void FeedbackUploaderChrome::DispatchReport(
+ scoped_refptr<FeedbackReport> report) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("chrome_feedback_report_app", R"(
semantics {
@@ -74,12 +67,13 @@ void FeedbackUploaderChrome::DispatchReport(const std::string& data) {
// Note: FeedbackUploaderDelegate deletes itself and the fetcher.
net::URLFetcher* fetcher =
net::URLFetcher::Create(
- post_url, net::URLFetcher::POST,
+ feedback_post_url(), net::URLFetcher::POST,
new FeedbackUploaderDelegate(
- data,
- base::Bind(&FeedbackUploaderChrome::UpdateUploadTimer,
+ report,
+ base::Bind(&FeedbackUploaderChrome::OnReportUploadSuccess,
AsWeakPtr()),
- base::Bind(&FeedbackUploaderChrome::RetryReport, AsWeakPtr())),
+ base::Bind(&FeedbackUploaderChrome::OnReportUploadFailure,
+ AsWeakPtr())),
traffic_annotation)
.release();
data_use_measurement::DataUseUserData::AttachToFetcher(
@@ -88,13 +82,13 @@ void FeedbackUploaderChrome::DispatchReport(const std::string& data) {
net::HttpRequestHeaders headers;
// Note: It's OK to pass |is_signed_in| false if it's unknown, as it does
// not affect transmission of experiments coming from the variations server.
- bool is_signed_in = false;
+ const bool is_signed_in = false;
variations::AppendVariationHeaders(fetcher->GetOriginalURL(),
context_->IsOffTheRecord(), false,
is_signed_in, &headers);
fetcher->SetExtraRequestHeaders(headers.ToString());
- fetcher->SetUploadData(kProtoBufMimeType, data);
+ fetcher->SetUploadData(kProtoBufMimeType, report->data());
fetcher->SetRequestContext(
content::BrowserContext::GetDefaultStoragePartition(context_)->
GetURLRequestContext());
diff --git a/chromium/components/feedback/feedback_uploader_chrome.h b/chromium/components/feedback/feedback_uploader_chrome.h
index f2579b6ac1e..21cf13835fb 100644
--- a/chromium/components/feedback/feedback_uploader_chrome.h
+++ b/chromium/components/feedback/feedback_uploader_chrome.h
@@ -18,11 +18,14 @@ namespace feedback {
class FeedbackUploaderChrome : public FeedbackUploader,
public KeyedService {
public:
- explicit FeedbackUploaderChrome(content::BrowserContext* context);
-
- void DispatchReport(const std::string& data) override;
+ FeedbackUploaderChrome(
+ content::BrowserContext* context,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
private:
+ // feedback::FeedbackUploader:
+ void DispatchReport(scoped_refptr<FeedbackReport> report) override;
+
// Browser context this uploader was created for.
content::BrowserContext* context_;
diff --git a/chromium/components/feedback/feedback_uploader_chrome_unittest.cc b/chromium/components/feedback/feedback_uploader_chrome_unittest.cc
index 7db46872e2f..0f631b17ee2 100644
--- a/chromium/components/feedback/feedback_uploader_chrome_unittest.cc
+++ b/chromium/components/feedback/feedback_uploader_chrome_unittest.cc
@@ -8,6 +8,10 @@
#include "base/macros.h"
#include "base/metrics/field_trial.h"
+#include "base/run_loop.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.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"
@@ -18,6 +22,17 @@
namespace feedback {
+namespace {
+
+constexpr base::TimeDelta kTestRetryDelay =
+ base::TimeDelta::FromMilliseconds(1);
+
+constexpr int kHttpPostSuccessNoContent = 204;
+constexpr int kHttpPostFailClientError = 400;
+constexpr int kHttpPostFailServerError = 500;
+
+} // namespace
+
class FeedbackUploaderChromeTest : public ::testing::Test {
protected:
FeedbackUploaderChromeTest()
@@ -54,10 +69,11 @@ TEST_F(FeedbackUploaderChromeTest, VariationHeaders) {
CreateFieldTrialWithId("Test", "Group1", 123);
content::TestBrowserContext context;
- FeedbackUploaderChrome uploader(&context);
+ FeedbackUploaderChrome uploader(
+ &context, FeedbackUploaderFactory::CreateUploaderTaskRunner());
net::TestURLFetcherFactory factory;
- uploader.DispatchReport("test");
+ uploader.QueueReport("test");
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
net::HttpRequestHeaders headers;
@@ -71,4 +87,40 @@ TEST_F(FeedbackUploaderChromeTest, VariationHeaders) {
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
+TEST_F(FeedbackUploaderChromeTest, TestVariousServerResponses) {
+ content::TestBrowserContext context;
+ FeedbackUploader::SetMinimumRetryDelayForTesting(kTestRetryDelay);
+ FeedbackUploaderChrome uploader(
+ &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);
+ EXPECT_EQ(kTestRetryDelay, uploader.retry_delay());
+ EXPECT_TRUE(uploader.QueueEmpty());
+
+ // 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);
+ EXPECT_EQ(kTestRetryDelay, uploader.retry_delay());
+ EXPECT_TRUE(uploader.QueueEmpty());
+
+ // 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());
+ EXPECT_FALSE(uploader.QueueEmpty());
+}
+
} // namespace feedback
diff --git a/chromium/components/feedback/feedback_uploader_delegate.cc b/chromium/components/feedback/feedback_uploader_delegate.cc
index 0519a816407..3f0c3533652 100644
--- a/chromium/components/feedback/feedback_uploader_delegate.cc
+++ b/chromium/components/feedback/feedback_uploader_delegate.cc
@@ -8,26 +8,27 @@
#include <sstream>
#include "base/logging.h"
+#include "components/feedback/feedback_report.h"
#include "net/url_request/url_fetcher.h"
namespace feedback {
+
namespace {
-const int kHttpPostSuccessNoContent = 204;
-const int kHttpPostFailNoConnection = -1;
-const int kHttpPostFailClientError = 400;
-const int kHttpPostFailServerError = 500;
+constexpr int kHttpPostSuccessNoContent = 204;
+constexpr int kHttpPostFailNoConnection = -1;
+constexpr int kHttpPostFailClientError = 400;
+constexpr int kHttpPostFailServerError = 500;
} // namespace
FeedbackUploaderDelegate::FeedbackUploaderDelegate(
- const std::string& post_body,
+ scoped_refptr<FeedbackReport> pending_report,
const base::Closure& success_callback,
- const ReportDataCallback& error_callback)
- : post_body_(post_body),
- success_callback_(success_callback),
- error_callback_(error_callback) {
-}
+ const ReportFailureCallback& error_callback)
+ : pending_report_(pending_report),
+ success_callback_(success_callback),
+ error_callback_(error_callback) {}
FeedbackUploaderDelegate::~FeedbackUploaderDelegate() {}
@@ -41,18 +42,27 @@ void FeedbackUploaderDelegate::OnURLFetchComplete(
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) &&
+ } 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) {
+ } 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(post_body_);
+
+ if (should_retry)
+ error_callback_.Run(pending_report_);
}
LOG(WARNING) << "FEEDBACK: Submission to feedback server ("
diff --git a/chromium/components/feedback/feedback_uploader_delegate.h b/chromium/components/feedback/feedback_uploader_delegate.h
index b51de3945e9..ca858ebb164 100644
--- a/chromium/components/feedback/feedback_uploader_delegate.h
+++ b/chromium/components/feedback/feedback_uploader_delegate.h
@@ -5,32 +5,34 @@
#ifndef COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
#define COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
-#include <string>
-
#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 {
+using ReportFailureCallback =
+ base::Callback<void(scoped_refptr<FeedbackReport>)>;
+
// 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 std::string& post_body,
+ FeedbackUploaderDelegate(scoped_refptr<FeedbackReport> pending_report,
const base::Closure& success_callback,
- const ReportDataCallback& error_callback);
+ const ReportFailureCallback& error_callback);
~FeedbackUploaderDelegate() override;
private:
// Overridden from net::URLFetcherDelegate.
void OnURLFetchComplete(const net::URLFetcher* source) override;
- std::string post_body_;
+ scoped_refptr<FeedbackReport> pending_report_;
base::Closure success_callback_;
- ReportDataCallback error_callback_;
+ ReportFailureCallback error_callback_;
DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderDelegate);
};
diff --git a/chromium/components/feedback/feedback_uploader_factory.cc b/chromium/components/feedback/feedback_uploader_factory.cc
index 111d8935536..59023cc3576 100644
--- a/chromium/components/feedback/feedback_uploader_factory.cc
+++ b/chromium/components/feedback/feedback_uploader_factory.cc
@@ -5,6 +5,9 @@
#include "components/feedback/feedback_uploader_factory.h"
#include "base/memory/singleton.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "components/feedback/feedback_uploader.h"
#include "components/feedback/feedback_uploader_chrome.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -23,16 +26,27 @@ FeedbackUploader* FeedbackUploaderFactory::GetForBrowserContext(
GetInstance()->GetServiceForBrowserContext(context, true));
}
+// static
+scoped_refptr<base::SingleThreadTaskRunner>
+FeedbackUploaderFactory::CreateUploaderTaskRunner() {
+ // Uses a BLOCK_SHUTDOWN file task runner because we really don't want to
+ // lose reports or corrupt their files.
+ return base::CreateSingleThreadTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
+}
+
FeedbackUploaderFactory::FeedbackUploaderFactory()
: BrowserContextKeyedServiceFactory(
"feedback::FeedbackUploader",
- BrowserContextDependencyManager::GetInstance()) {}
+ BrowserContextDependencyManager::GetInstance()),
+ task_runner_(CreateUploaderTaskRunner()) {}
FeedbackUploaderFactory::~FeedbackUploaderFactory() {}
KeyedService* FeedbackUploaderFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
- return new FeedbackUploaderChrome(context);
+ return new FeedbackUploaderChrome(context, task_runner_);
}
content::BrowserContext* FeedbackUploaderFactory::GetBrowserContextToUse(
diff --git a/chromium/components/feedback/feedback_uploader_factory.h b/chromium/components/feedback/feedback_uploader_factory.h
index effad0100b0..b04df4acbbf 100644
--- a/chromium/components/feedback/feedback_uploader_factory.h
+++ b/chromium/components/feedback/feedback_uploader_factory.h
@@ -6,10 +6,12 @@
#define COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_FACTORY_H_
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace base {
template<typename T> struct DefaultSingletonTraits;
+class SingleThreadTaskRunner;
}
namespace content {
@@ -30,6 +32,11 @@ class FeedbackUploaderFactory : public BrowserContextKeyedServiceFactory {
static FeedbackUploader* GetForBrowserContext(
content::BrowserContext* context);
+ // Creates a new SingleThreadTaskRunner that is used to run feedback blocking
+ // background work. Tests can use this to create the exact same type of runner
+ // that's actually used in production code to simulate the same behavior.
+ static scoped_refptr<base::SingleThreadTaskRunner> CreateUploaderTaskRunner();
+
private:
friend struct base::DefaultSingletonTraits<FeedbackUploaderFactory>;
@@ -42,6 +49,11 @@ class FeedbackUploaderFactory : public BrowserContextKeyedServiceFactory {
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
+ // The task runner used to handle all blocking background feedback-reports
+ // work. It involves reading / writing reports from / to disk. Those
+ // operations must not interleave and thread affinity is required.
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderFactory);
};
diff --git a/chromium/components/feedback/feedback_uploader_unittest.cc b/chromium/components/feedback/feedback_uploader_unittest.cc
index 76201f3b32f..515847847f7 100644
--- a/chromium/components/feedback/feedback_uploader_unittest.cc
+++ b/chromium/components/feedback/feedback_uploader_unittest.cc
@@ -14,51 +14,103 @@
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "build/build_config.h"
+#include "components/feedback/feedback_report.h"
#include "components/feedback/feedback_uploader_chrome.h"
#include "components/feedback/feedback_uploader_factory.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
-#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace feedback {
+
namespace {
-const char kReportOne[] = "one";
-const char kReportTwo[] = "two";
-const char kReportThree[] = "three";
-const char kReportFour[] = "four";
-const char kReportFive[] = "five";
+constexpr char kReportOne[] = "one";
+constexpr char kReportTwo[] = "two";
+constexpr char kReportThree[] = "three";
+constexpr char kReportFour[] = "four";
+constexpr char kReportFive[] = "five";
-const base::TimeDelta kRetryDelayForTest =
+constexpr base::TimeDelta kRetryDelayForTest =
base::TimeDelta::FromMilliseconds(100);
+class MockFeedbackUploader : public FeedbackUploaderChrome {
+ public:
+ MockFeedbackUploader(content::BrowserContext* context,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : FeedbackUploaderChrome(context, task_runner) {}
+ ~MockFeedbackUploader() override {}
+
+ void RunMessageLoop() {
+ if (ProcessingComplete())
+ return;
+ run_loop_ = base::MakeUnique<base::RunLoop>();
+ run_loop_->Run();
+ }
+
+ const std::map<std::string, unsigned int>& dispatched_reports() const {
+ return dispatched_reports_;
+ }
+ void set_expected_reports(size_t value) { expected_reports_ = value; }
+ void set_simulate_failure(bool value) { simulate_failure_ = value; }
+
+ private:
+ // FeedbackUploaderChrome:
+ void DispatchReport(scoped_refptr<FeedbackReport> report) override {
+ if (base::ContainsKey(dispatched_reports_, report->data()))
+ dispatched_reports_[report->data()]++;
+ else
+ dispatched_reports_[report->data()] = 1;
+
+ dispatched_reports_count_++;
+
+ if (simulate_failure_)
+ OnReportUploadFailure(report);
+ else
+ OnReportUploadSuccess();
+
+ if (ProcessingComplete()) {
+ if (run_loop_)
+ run_loop_->Quit();
+ }
+ }
+
+ bool ProcessingComplete() {
+ return (dispatched_reports_count_ >= expected_reports_);
+ }
+
+ std::unique_ptr<base::RunLoop> run_loop_;
+ std::map<std::string, unsigned int> dispatched_reports_;
+ size_t dispatched_reports_count_ = 0;
+ size_t expected_reports_ = 0;
+ bool simulate_failure_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(MockFeedbackUploader);
+};
+
std::unique_ptr<KeyedService> CreateFeedbackUploaderService(
content::BrowserContext* context) {
- return base::MakeUnique<feedback::FeedbackUploaderChrome>(context);
+ return base::MakeUnique<MockFeedbackUploader>(
+ context, FeedbackUploaderFactory::CreateUploaderTaskRunner());
}
} // namespace
-namespace feedback {
-
class FeedbackUploaderTest : public testing::Test {
- protected:
+ public:
FeedbackUploaderTest()
- : context_(new content::TestBrowserContext()),
- prefs_(new sync_preferences::TestingPrefServiceSyncable()),
- dispatched_reports_count_(0),
- expected_reports_(0) {
+ : context_(base::MakeUnique<content::TestBrowserContext>()),
+ prefs_(
+ base::MakeUnique<sync_preferences::TestingPrefServiceSyncable>()) {
user_prefs::UserPrefs::Set(context_.get(), prefs_.get());
FeedbackUploaderFactory::GetInstance()->SetTestingFactory(
context_.get(), &CreateFeedbackUploaderService);
- uploader_ = FeedbackUploaderFactory::GetForBrowserContext(context_.get());
- uploader_->setup_for_test(
- base::Bind(&FeedbackUploaderTest::MockDispatchReport,
- base::Unretained(this)),
- kRetryDelayForTest);
+ FeedbackUploader::SetMinimumRetryDelayForTesting(kRetryDelayForTest);
+ uploader_ = static_cast<MockFeedbackUploader*>(
+ FeedbackUploaderFactory::GetForBrowserContext(context_.get()));
}
~FeedbackUploaderTest() override {
@@ -70,98 +122,60 @@ class FeedbackUploaderTest : public testing::Test {
uploader_->QueueReport(data);
}
- void ReportFailure(const std::string& data) {
- uploader_->RetryReport(data);
- }
-
- void MockDispatchReport(const std::string& report_data) {
- if (base::ContainsKey(dispatched_reports_, report_data)) {
- dispatched_reports_[report_data]++;
- } else {
- dispatched_reports_[report_data] = 1;
- }
- dispatched_reports_count_++;
-
- // Dispatch will always update the timer, whether successful or not,
- // simulate the same behavior.
- uploader_->UpdateUploadTimer();
-
- if (ProcessingComplete()) {
- if (run_loop_.get())
- run_loop_->Quit();
- }
- }
-
- bool ProcessingComplete() {
- return (dispatched_reports_count_ >= expected_reports_);
- }
-
- void RunMessageLoop() {
- if (ProcessingComplete())
- return;
- run_loop_.reset(new base::RunLoop());
- run_loop_->Run();
- }
+ MockFeedbackUploader* uploader() const { return uploader_; }
+ private:
content::TestBrowserThreadBundle test_browser_thread_bundle_;
- std::unique_ptr<base::RunLoop> run_loop_;
std::unique_ptr<content::TestBrowserContext> context_;
std::unique_ptr<PrefService> prefs_;
- FeedbackUploader* uploader_;
+ MockFeedbackUploader* uploader_;
- std::map<std::string, unsigned int> dispatched_reports_;
- size_t dispatched_reports_count_;
- size_t expected_reports_;
+ DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderTest);
};
-#if defined(OS_LINUX) || defined(OS_MACOSX)
-#define MAYBE_QueueMultiple QueueMultiple
-#else
-// crbug.com/330547
-#define MAYBE_QueueMultiple DISABLED_QueueMultiple
-#endif
-TEST_F(FeedbackUploaderTest, MAYBE_QueueMultiple) {
- dispatched_reports_.clear();
+TEST_F(FeedbackUploaderTest, QueueMultiple) {
QueueReport(kReportOne);
QueueReport(kReportTwo);
QueueReport(kReportThree);
QueueReport(kReportFour);
- EXPECT_EQ(dispatched_reports_.size(), 4u);
- EXPECT_EQ(dispatched_reports_[kReportOne], 1u);
- EXPECT_EQ(dispatched_reports_[kReportTwo], 1u);
- EXPECT_EQ(dispatched_reports_[kReportThree], 1u);
- EXPECT_EQ(dispatched_reports_[kReportFour], 1u);
+ EXPECT_EQ(uploader()->dispatched_reports().size(), 4u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportOne), 1u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportTwo), 1u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportThree), 1u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportFour), 1u);
}
-#if defined(OS_WIN) || defined(OS_ANDROID)
-// crbug.com/330547
-#define MAYBE_QueueMultipleWithFailures DISABLED_QueueMultipleWithFailures
-#else
-#define MAYBE_QueueMultipleWithFailures QueueMultipleWithFailures
-#endif
-TEST_F(FeedbackUploaderTest, MAYBE_QueueMultipleWithFailures) {
- dispatched_reports_.clear();
-
+TEST_F(FeedbackUploaderTest, QueueMultipleWithFailures) {
+ EXPECT_EQ(kRetryDelayForTest, uploader()->retry_delay());
QueueReport(kReportOne);
+
+ // Simulate a failure in reports two and three. Make sure the backoff delay
+ // will be applied twice, and the reports will eventually be sent.
+ uploader()->set_simulate_failure(true);
QueueReport(kReportTwo);
+ EXPECT_EQ(kRetryDelayForTest * 2, uploader()->retry_delay());
QueueReport(kReportThree);
- QueueReport(kReportFour);
+ EXPECT_EQ(kRetryDelayForTest * 4, uploader()->retry_delay());
+ uploader()->set_simulate_failure(false);
- ReportFailure(kReportThree);
- ReportFailure(kReportTwo);
+ // Once a successful report is sent, the backoff delay is reset back to its
+ // minimum value.
+ QueueReport(kReportFour);
+ EXPECT_EQ(kRetryDelayForTest, uploader()->retry_delay());
QueueReport(kReportFive);
- expected_reports_ = 7;
- RunMessageLoop();
+ // Wait for the pending two failed reports to be sent.
+ uploader()->set_expected_reports(7);
+ uploader()->RunMessageLoop();
- EXPECT_EQ(dispatched_reports_.size(), 5u);
- EXPECT_EQ(dispatched_reports_[kReportOne], 1u);
- EXPECT_EQ(dispatched_reports_[kReportTwo], 2u);
- EXPECT_EQ(dispatched_reports_[kReportThree], 2u);
- EXPECT_EQ(dispatched_reports_[kReportFour], 1u);
- EXPECT_EQ(dispatched_reports_[kReportFive], 1u);
+ EXPECT_EQ(uploader()->dispatched_reports().size(), 5u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportOne), 1u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportTwo), 2u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportThree), 2u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportFour), 1u);
+ EXPECT_EQ(uploader()->dispatched_reports().at(kReportFive), 1u);
}
} // namespace feedback
diff --git a/chromium/components/feedback/system_logs/system_logs_fetcher.cc b/chromium/components/feedback/system_logs/system_logs_fetcher.cc
new file mode 100644
index 00000000000..4e7e510cd4a
--- /dev/null
+++ b/chromium/components/feedback/system_logs/system_logs_fetcher.cc
@@ -0,0 +1,99 @@
+// 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/feedback/system_logs/system_logs_fetcher.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace system_logs {
+
+namespace {
+
+// List of keys in the SystemLogsResponse map whose corresponding values will
+// not be anonymized.
+constexpr const char* const kWhitelistedKeysOfUUIDs[] = {
+ "CHROMEOS_BOARD_APPID", "CHROMEOS_CANARY_APPID", "CHROMEOS_RELEASE_APPID",
+ "CLIENT_ID",
+};
+
+// Returns true if the given |key| is anonymizer-whitelisted and whose
+// corresponding value should not be anonymized.
+bool IsKeyWhitelisted(const std::string& key) {
+ for (auto* const whitelisted_key : kWhitelistedKeysOfUUIDs) {
+ if (key == whitelisted_key)
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+SystemLogsFetcher::SystemLogsFetcher(bool scrub_data)
+ : response_(base::MakeUnique<SystemLogsResponse>()),
+ num_pending_requests_(0),
+ weak_ptr_factory_(this) {
+ if (scrub_data)
+ anonymizer_ = base::MakeUnique<feedback::AnonymizerTool>();
+}
+
+SystemLogsFetcher::~SystemLogsFetcher() {}
+
+void SystemLogsFetcher::AddSource(std::unique_ptr<SystemLogsSource> source) {
+ data_sources_.emplace_back(std::move(source));
+ num_pending_requests_++;
+}
+
+void SystemLogsFetcher::Fetch(const SysLogsFetcherCallback& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(callback_.is_null());
+ DCHECK(!callback.is_null());
+
+ callback_ = callback;
+ for (size_t i = 0; i < data_sources_.size(); ++i) {
+ VLOG(1) << "Fetching SystemLogSource: " << data_sources_[i]->source_name();
+ data_sources_[i]->Fetch(base::Bind(&SystemLogsFetcher::OnFetched,
+ weak_ptr_factory_.GetWeakPtr(),
+ data_sources_[i]->source_name()));
+ }
+}
+
+void SystemLogsFetcher::OnFetched(const std::string& source_name,
+ SystemLogsResponse* response) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ VLOG(1) << "Received SystemLogSource: " << source_name;
+
+ if (anonymizer_) {
+ for (auto& element : *response) {
+ if (!IsKeyWhitelisted(element.first))
+ element.second = anonymizer_->Anonymize(element.second);
+ }
+ }
+ AddResponse(source_name, response);
+}
+
+void SystemLogsFetcher::AddResponse(const std::string& source_name,
+ SystemLogsResponse* response) {
+ for (const auto& it : *response) {
+ // An element with a duplicate key would not be successfully inserted.
+ bool ok = response_->emplace(it).second;
+ DCHECK(ok) << "Duplicate key found: " << it.first;
+ }
+
+ --num_pending_requests_;
+ if (num_pending_requests_ > 0)
+ return;
+
+ callback_.Run(std::move(response_));
+ BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
+}
+
+} // namespace system_logs
diff --git a/chromium/components/feedback/system_logs/system_logs_fetcher.h b/chromium/components/feedback/system_logs/system_logs_fetcher.h
new file mode 100644
index 00000000000..1799d4f4304
--- /dev/null
+++ b/chromium/components/feedback/system_logs/system_logs_fetcher.h
@@ -0,0 +1,83 @@
+// 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_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
+#define COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/feedback/anonymizer_tool.h"
+#include "components/feedback/feedback_common.h"
+#include "components/feedback/system_logs/system_logs_source.h"
+
+namespace system_logs {
+
+// Callback that the SystemLogsFetcher uses to return data.
+using SysLogsFetcherCallback =
+ base::Callback<void(std::unique_ptr<SystemLogsResponse>)>;
+
+// The SystemLogsFetcher fetches key-value data from a list of log sources.
+//
+// EXAMPLE:
+// class Example {
+// public:
+// void ProcessLogs(SystemLogsResponse* response) {
+// // do something with the logs
+// }
+// void GetLogs() {
+// SystemLogsFetcher* fetcher = new SystemLogsFetcher(/*scrub_data=*/ true);
+// fetcher->AddSource(base::MakeUnique<LogSourceOne>());
+// fetcher->AddSource(base::MakeUnique<LogSourceTwo>());
+// fetcher->Fetch(base::Bind(&Example::ProcessLogs, this));
+// }
+// };
+class SystemLogsFetcher {
+ public:
+ // If scrub_data is true, logs will be anonymized.
+ // TODO(battre): This class needs to be expanded to provide better scrubbing
+ // of system logs.
+ explicit SystemLogsFetcher(bool scrub_data);
+ ~SystemLogsFetcher();
+
+ // Adds a source to use when fetching.
+ void AddSource(std::unique_ptr<SystemLogsSource> source);
+
+ // Starts the fetch process.
+ void Fetch(const SysLogsFetcherCallback& callback);
+
+ private:
+ // Callback passed to all the data sources. May call Scrub(), then calls
+ // AddResponse().
+ void OnFetched(const std::string& source_name, SystemLogsResponse* response);
+
+ // Merges the |response| it receives into response_. When all the data sources
+ // have responded, it deletes their objects and returns the response to the
+ // callback_. After this it deletes this instance of the object.
+ void AddResponse(const std::string& source_name,
+ SystemLogsResponse* response);
+
+ std::vector<std::unique_ptr<SystemLogsSource>> data_sources_;
+ SysLogsFetcherCallback callback_;
+
+ std::unique_ptr<SystemLogsResponse> response_; // The actual response data.
+ size_t num_pending_requests_; // The number of callbacks it should get.
+
+ std::unique_ptr<feedback::AnonymizerTool> anonymizer_;
+
+ base::WeakPtrFactory<SystemLogsFetcher> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemLogsFetcher);
+};
+
+} // namespace system_logs
+
+#endif // COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_FETCHER_H_
diff --git a/chromium/components/feedback/system_logs/system_logs_source.cc b/chromium/components/feedback/system_logs/system_logs_source.cc
new file mode 100644
index 00000000000..f32cb2f128f
--- /dev/null
+++ b/chromium/components/feedback/system_logs/system_logs_source.cc
@@ -0,0 +1,14 @@
+// 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/feedback/system_logs/system_logs_source.h"
+
+namespace system_logs {
+
+SystemLogsSource::SystemLogsSource(const std::string& source_name)
+ : source_name_(source_name) {}
+
+SystemLogsSource::~SystemLogsSource() {}
+
+} // namespace system_logs
diff --git a/chromium/components/feedback/system_logs/system_logs_source.h b/chromium/components/feedback/system_logs/system_logs_source.h
new file mode 100644
index 00000000000..6b267ef0df7
--- /dev/null
+++ b/chromium/components/feedback/system_logs/system_logs_source.h
@@ -0,0 +1,39 @@
+// 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_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
+#define COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/feedback/feedback_common.h"
+
+namespace system_logs {
+
+using SystemLogsResponse = FeedbackCommon::SystemLogsMap;
+
+// Callback that the data sources use to return data.
+using SysLogsSourceCallback = base::Callback<void(SystemLogsResponse*)>;
+
+// The SystemLogsSource provides an interface for the data sources that
+// the SystemLogsFetcher class uses to fetch logs and other information.
+class SystemLogsSource {
+ public:
+ // |source_name| provides a descriptive identifier for debugging.
+ explicit SystemLogsSource(const std::string& source_name);
+ virtual ~SystemLogsSource();
+
+ // Fetches data and passes it by pointer to the callback
+ virtual void Fetch(const SysLogsSourceCallback& callback) = 0;
+
+ const std::string& source_name() const { return source_name_; }
+
+ private:
+ std::string source_name_;
+};
+
+} // namespace system_logs
+
+#endif // COMPONENTS_FEEDBACK_SYSTEM_LOGS_SYSTEM_LOGS_SOURCE_H_
diff --git a/chromium/components/filesystem/BUILD.gn b/chromium/components/filesystem/BUILD.gn
index dc3bc4157d7..ff3ec9b517a 100644
--- a/chromium/components/filesystem/BUILD.gn
+++ b/chromium/components/filesystem/BUILD.gn
@@ -36,60 +36,63 @@ static_library("lib") {
]
}
-service("filesystem") {
- sources = [
- "file_system_app.cc",
- "file_system_app.h",
- "main.cc",
- ]
+if (!is_ios) {
+ # service binaries are not supported on iOS.
+ service("filesystem") {
+ sources = [
+ "file_system_app.cc",
+ "file_system_app.h",
+ "main.cc",
+ ]
- deps = [
- ":lib",
- "//base",
- "//components/filesystem/public/interfaces",
- "//mojo/common",
- "//mojo/public/cpp/bindings",
- "//mojo/public/cpp/system",
- "//services/service_manager/public/cpp",
- ]
-}
+ deps = [
+ ":lib",
+ "//base",
+ "//components/filesystem/public/interfaces",
+ "//mojo/common",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/system",
+ "//services/service_manager/public/cpp",
+ ]
+ }
-service_manifest("manifest") {
- name = "filesystem"
- source = "manifest.json"
-}
+ service_manifest("manifest") {
+ name = "filesystem"
+ source = "manifest.json"
+ }
-service_test("filesystem_service_unittests") {
- sources = [
- "directory_impl_unittest.cc",
- "file_impl_unittest.cc",
- "files_test_base.cc",
- "files_test_base.h",
- ]
+ service_test("filesystem_service_unittests") {
+ sources = [
+ "directory_impl_unittest.cc",
+ "file_impl_unittest.cc",
+ "files_test_base.cc",
+ "files_test_base.h",
+ ]
- catalog = ":filesystem_service_unittests_catalog"
+ catalog = ":filesystem_service_unittests_catalog"
- deps = [
- "//base",
- "//components/filesystem/public/interfaces",
- "//mojo/common",
- "//mojo/public/cpp/bindings",
- "//mojo/public/cpp/system",
- "//services/service_manager/public/cpp",
- "//services/service_manager/public/cpp:service_test_support",
- ]
+ deps = [
+ "//base",
+ "//components/filesystem/public/interfaces",
+ "//mojo/common",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/system",
+ "//services/service_manager/public/cpp",
+ "//services/service_manager/public/cpp:service_test_support",
+ ]
- data_deps = [
- ":filesystem",
- ]
-}
+ data_deps = [
+ ":filesystem",
+ ]
+ }
-service_manifest("test_manifest") {
- name = "filesystem_service_unittests"
- source = "test_manifest.json"
-}
+ service_manifest("test_manifest") {
+ name = "filesystem_service_unittests"
+ source = "test_manifest.json"
+ }
-catalog("filesystem_service_unittests_catalog") {
- embedded_services = [ ":test_manifest" ]
- standalone_services = [ ":manifest" ]
+ catalog("filesystem_service_unittests_catalog") {
+ embedded_services = [ ":test_manifest" ]
+ standalone_services = [ ":manifest" ]
+ }
}
diff --git a/chromium/components/filesystem/directory_impl.cc b/chromium/components/filesystem/directory_impl.cc
index f2f71e60368..1209f6ecfcc 100644
--- a/chromium/components/filesystem/directory_impl.cc
+++ b/chromium/components/filesystem/directory_impl.cc
@@ -179,6 +179,33 @@ void DirectoryImpl::Rename(const std::string& raw_old_path,
std::move(callback).Run(mojom::FileError::OK);
}
+void DirectoryImpl::Replace(const std::string& raw_old_path,
+ const std::string& raw_new_path,
+ ReplaceCallback callback) {
+ base::FilePath old_path;
+ mojom::FileError error =
+ ValidatePath(raw_old_path, directory_path_, &old_path);
+ if (error != mojom::FileError::OK) {
+ std::move(callback).Run(error);
+ return;
+ }
+
+ base::FilePath new_path;
+ error = ValidatePath(raw_new_path, directory_path_, &new_path);
+ if (error != mojom::FileError::OK) {
+ std::move(callback).Run(error);
+ return;
+ }
+
+ base::File::Error file_error;
+ if (!base::ReplaceFile(old_path, new_path, &file_error)) {
+ std::move(callback).Run(static_cast<mojom::FileError>(file_error));
+ return;
+ }
+
+ std::move(callback).Run(mojom::FileError::OK);
+}
+
void DirectoryImpl::Delete(const std::string& raw_path,
uint32_t delete_flags,
DeleteCallback callback) {
@@ -224,6 +251,9 @@ void DirectoryImpl::IsWritable(const std::string& raw_path,
}
void DirectoryImpl::Flush(FlushCallback callback) {
+// On Windows no need to sync directories. Their metadata will be updated when
+// files are created, without an explicit sync.
+#if !defined(OS_WIN)
base::File file(directory_path_,
base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
@@ -235,7 +265,7 @@ void DirectoryImpl::Flush(FlushCallback callback) {
std::move(callback).Run(mojom::FileError::FAILED);
return;
}
-
+#endif
std::move(callback).Run(mojom::FileError::OK);
}
diff --git a/chromium/components/filesystem/directory_impl.h b/chromium/components/filesystem/directory_impl.h
index 755c42cfcb7..afdd30c87ac 100644
--- a/chromium/components/filesystem/directory_impl.h
+++ b/chromium/components/filesystem/directory_impl.h
@@ -47,6 +47,9 @@ class DirectoryImpl : public mojom::Directory {
void Rename(const std::string& path,
const std::string& new_path,
RenameCallback callback) override;
+ void Replace(const std::string& path,
+ const std::string& new_path,
+ ReplaceCallback callback) override;
void Delete(const std::string& path,
uint32_t delete_flags,
DeleteCallback callback) override;
diff --git a/chromium/components/filesystem/file_impl.cc b/chromium/components/filesystem/file_impl.cc
index 12ddb8a8be1..e853a91fd42 100644
--- a/chromium/components/filesystem/file_impl.cc
+++ b/chromium/components/filesystem/file_impl.cc
@@ -62,6 +62,7 @@ bool FileImpl::IsValid() const {
return file_.IsValid();
}
+#if !defined(OS_FUCHSIA)
base::File::Error FileImpl::RawLockFile() {
return file_.Lock();
}
@@ -69,6 +70,7 @@ base::File::Error FileImpl::RawLockFile() {
base::File::Error FileImpl::RawUnlockFile() {
return file_.Unlock();
}
+#endif // !OS_FUCHSIA
void FileImpl::Close(CloseCallback callback) {
if (!file_.IsValid()) {
diff --git a/chromium/components/filesystem/file_impl.h b/chromium/components/filesystem/file_impl.h
index 550f43b22a7..3b7db2ea2fd 100644
--- a/chromium/components/filesystem/file_impl.h
+++ b/chromium/components/filesystem/file_impl.h
@@ -10,6 +10,7 @@
#include "base/files/file.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
+#include "build/build_config.h"
#include "components/filesystem/public/interfaces/directory.mojom.h"
#include "mojo/public/cpp/bindings/interface_request.h"
@@ -37,10 +38,12 @@ class FileImpl : public mojom::File {
// Returns whether the underlying file handle is valid.
bool IsValid() const;
+#if !defined(OS_FUCHSIA)
// Attempts to perform the native operating system's locking operations on
- // the internal mojom::File handle
+ // the internal mojom::File handle. Not supported on Fuchsia.
base::File::Error RawLockFile();
base::File::Error RawUnlockFile();
+#endif // !OS_FUCHSIA
const base::FilePath& path() const { return path_; }
diff --git a/chromium/components/filesystem/file_system_app.cc b/chromium/components/filesystem/file_system_app.cc
index fc89c115e08..b4ab6fe3689 100644
--- a/chromium/components/filesystem/file_system_app.cc
+++ b/chromium/components/filesystem/file_system_app.cc
@@ -50,12 +50,12 @@ void FileSystemApp::OnBindInterface(
const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
- registry_.BindInterface(source_info, interface_name,
- std::move(interface_pipe));
+ registry_.BindInterface(interface_name, std::move(interface_pipe),
+ source_info);
}
-void FileSystemApp::Create(const service_manager::BindSourceInfo& source_info,
- mojom::FileSystemRequest request) {
+void FileSystemApp::Create(mojom::FileSystemRequest request,
+ const service_manager::BindSourceInfo& source_info) {
mojo::MakeStrongBinding(
base::MakeUnique<FileSystemImpl>(source_info.identity, GetUserDataDir(),
lock_table_),
diff --git a/chromium/components/filesystem/file_system_app.h b/chromium/components/filesystem/file_system_app.h
index b4a70f73740..e91baa5606c 100644
--- a/chromium/components/filesystem/file_system_app.h
+++ b/chromium/components/filesystem/file_system_app.h
@@ -30,10 +30,12 @@ class FileSystemApp : public service_manager::Service {
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override;
- void Create(const service_manager::BindSourceInfo& source_info,
- mojom::FileSystemRequest request);
+ void Create(mojom::FileSystemRequest request,
+ const service_manager::BindSourceInfo& source_info);
- service_manager::BinderRegistry registry_;
+ service_manager::BinderRegistryWithArgs<
+ const service_manager::BindSourceInfo&>
+ registry_;
scoped_refptr<LockTable> lock_table_;
diff --git a/chromium/components/filesystem/lock_table.cc b/chromium/components/filesystem/lock_table.cc
index 4f43672630a..badbbadcc11 100644
--- a/chromium/components/filesystem/lock_table.cc
+++ b/chromium/components/filesystem/lock_table.cc
@@ -4,6 +4,7 @@
#include "components/filesystem/lock_table.h"
+#include "build/build_config.h"
#include "components/filesystem/file_impl.h"
namespace filesystem {
@@ -22,11 +23,20 @@ base::File::Error LockTable::LockFile(FileImpl* file) {
return base::File::FILE_ERROR_FAILED;
}
+#if !defined(OS_FUCHSIA)
+ // Fuchsia doesn't provide a file locking mechanism, so file locks work only
+ // within a single process. File locking is used only by LevelDB which stores
+ // all files in the profile directory and normally there shouldn't be more
+ // than a single chrome process per profile. So in-process locks should be
+ // sufficient.
+ // TODO(fuchsia): Investigate if it's necessary to implement cross-process
+ // file locks. crbug.com/744893 .
base::File::Error lock_err = file->RawLockFile();
if (lock_err != base::File::FILE_OK) {
// Locking failed for some reason.
return lock_err;
}
+#endif // !OS_FUCHSIA
locked_files_.insert(file->path());
return base::File::FILE_OK;
@@ -35,12 +45,14 @@ base::File::Error LockTable::LockFile(FileImpl* file) {
base::File::Error LockTable::UnlockFile(FileImpl* file) {
auto it = locked_files_.find(file->path());
if (it != locked_files_.end()) {
+#if !defined(OS_FUCHSIA)
base::File::Error lock_err = file->RawUnlockFile();
if (lock_err != base::File::FILE_OK) {
// TODO(erg): When can we fail to release a lock?
NOTREACHED();
return lock_err;
}
+#endif // !OS_FUCHSIA
locked_files_.erase(it);
}
diff --git a/chromium/components/filesystem/public/cpp/prefs/BUILD.gn b/chromium/components/filesystem/public/cpp/prefs/BUILD.gn
deleted file mode 100644
index ed36b596392..00000000000
--- a/chromium/components/filesystem/public/cpp/prefs/BUILD.gn
+++ /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.
-
-source_set("prefs") {
- sources = [
- "filesystem_json_pref_store.cc",
- "filesystem_json_pref_store.h",
- "pref_service_factory.cc",
- "pref_service_factory.h",
- ]
-
- deps = [
- "//base",
- "//components/filesystem/public/interfaces",
- "//components/prefs",
- "//mojo/common",
- "//services/service_manager/public/cpp",
- ]
-
- data_deps = [
- "//components/filesystem",
- ]
-}
diff --git a/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc b/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
deleted file mode 100644
index eb15b25fd27..00000000000
--- a/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
+++ /dev/null
@@ -1,428 +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/filesystem/public/cpp/prefs/filesystem_json_pref_store.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/json_string_value_serializer.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/task_runner_util.h"
-#include "base/time/default_clock.h"
-#include "base/values.h"
-#include "components/prefs/pref_filter.h"
-
-namespace filesystem {
-
-// Result returned from internal read tasks.
-struct FilesystemJsonPrefStore::ReadResult {
- public:
- ReadResult();
- ~ReadResult();
-
- std::unique_ptr<base::Value> value;
- PrefReadError error;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReadResult);
-};
-
-FilesystemJsonPrefStore::ReadResult::ReadResult()
- : error(PersistentPrefStore::PREF_READ_ERROR_NONE) {}
-
-FilesystemJsonPrefStore::ReadResult::~ReadResult() {}
-
-namespace {
-
-PersistentPrefStore::PrefReadError HandleReadErrors(const base::Value* value) {
- if (!value->IsType(base::Value::Type::DICTIONARY))
- return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
- return PersistentPrefStore::PREF_READ_ERROR_NONE;
-}
-
-} // namespace
-
-FilesystemJsonPrefStore::FilesystemJsonPrefStore(
- const std::string& pref_filename,
- filesystem::mojom::FileSystemPtr filesystem,
- std::unique_ptr<PrefFilter> pref_filter)
- : path_(pref_filename),
- binding_(this),
- filesystem_(std::move(filesystem)),
- prefs_(new base::DictionaryValue()),
- read_only_(false),
- pref_filter_(std::move(pref_filter)),
- initialized_(false),
- filtering_in_progress_(false),
- pending_lossy_write_(false),
- read_error_(PREF_READ_ERROR_NONE) {
- DCHECK(!path_.empty());
-}
-
-bool FilesystemJsonPrefStore::GetValue(const std::string& key,
- const base::Value** result) const {
- DCHECK(CalledOnValidThread());
-
- base::Value* tmp = nullptr;
- if (!prefs_->Get(key, &tmp))
- return false;
-
- if (result)
- *result = tmp;
- return true;
-}
-
-std::unique_ptr<base::DictionaryValue> FilesystemJsonPrefStore::GetValues()
- const {
- return prefs_->CreateDeepCopy();
-}
-
-void FilesystemJsonPrefStore::AddObserver(PrefStore::Observer* observer) {
- DCHECK(CalledOnValidThread());
-
- observers_.AddObserver(observer);
-}
-
-void FilesystemJsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
- DCHECK(CalledOnValidThread());
-
- observers_.RemoveObserver(observer);
-}
-
-bool FilesystemJsonPrefStore::HasObservers() const {
- DCHECK(CalledOnValidThread());
-
- return observers_.might_have_observers();
-}
-
-bool FilesystemJsonPrefStore::IsInitializationComplete() const {
- DCHECK(CalledOnValidThread());
-
- return initialized_;
-}
-
-bool FilesystemJsonPrefStore::GetMutableValue(const std::string& key,
- base::Value** result) {
- DCHECK(CalledOnValidThread());
-
- return prefs_->Get(key, result);
-}
-
-void FilesystemJsonPrefStore::SetValue(const std::string& key,
- std::unique_ptr<base::Value> value,
- uint32_t flags) {
- DCHECK(CalledOnValidThread());
-
- DCHECK(value);
- base::Value* old_value = nullptr;
- prefs_->Get(key, &old_value);
- if (!old_value || !value->Equals(old_value)) {
- prefs_->Set(key, std::move(value));
- ReportValueChanged(key, flags);
- }
-}
-
-void FilesystemJsonPrefStore::SetValueSilently(
- const std::string& key,
- std::unique_ptr<base::Value> value,
- uint32_t flags) {
- DCHECK(CalledOnValidThread());
-
- DCHECK(value);
- base::Value* old_value = nullptr;
- prefs_->Get(key, &old_value);
- if (!old_value || !value->Equals(old_value)) {
- prefs_->Set(key, std::move(value));
- ScheduleWrite(flags);
- }
-}
-
-void FilesystemJsonPrefStore::RemoveValue(const std::string& key,
- uint32_t flags) {
- DCHECK(CalledOnValidThread());
-
- if (prefs_->RemovePath(key, nullptr))
- ReportValueChanged(key, flags);
-}
-
-void FilesystemJsonPrefStore::RemoveValueSilently(const std::string& key,
- uint32_t flags) {
- DCHECK(CalledOnValidThread());
-
- prefs_->RemovePath(key, nullptr);
- ScheduleWrite(flags);
-}
-
-void FilesystemJsonPrefStore::ClearMutableValues() {
- NOTIMPLEMENTED();
-}
-
-bool FilesystemJsonPrefStore::ReadOnly() const {
- DCHECK(CalledOnValidThread());
-
- return read_only_;
-}
-
-PersistentPrefStore::PrefReadError FilesystemJsonPrefStore::GetReadError()
- const {
- DCHECK(CalledOnValidThread());
-
- return read_error_;
-}
-
-PersistentPrefStore::PrefReadError FilesystemJsonPrefStore::ReadPrefs() {
- NOTREACHED();
- // TODO(erg): Synchronously reading files makes no sense in a mojo world and
- // should be removed from the API.
- return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE;
-}
-
-void FilesystemJsonPrefStore::ReadPrefsAsync(
- ReadErrorDelegate* error_delegate) {
- DCHECK(CalledOnValidThread());
-
- initialized_ = false;
- error_delegate_.reset(error_delegate);
-
- if (!directory_) {
- OpenFilesystem(
- Bind(&FilesystemJsonPrefStore::OnPreferencesReadStart, AsWeakPtr()));
- } else {
- OnPreferencesReadStart();
- }
-}
-
-void FilesystemJsonPrefStore::CommitPendingWrite() {
- DCHECK(CalledOnValidThread());
-
- // TODO(erg): This is another one of those cases where we have problems
- // because of mismatch between the models used in the pref service versus
- // here. Most of the time, CommitPendingWrite() is called from
- // PrefService. However, in JSONPrefStore, we also call this method on
- // shutdown and thus need to synchronously write. But in mojo:filesystem,
- // everything is done asynchronously. So we're sort of stuck until we can
- // change the interface, which we'll do in the longer term.
-
- SchedulePendingLossyWrites();
-}
-
-void FilesystemJsonPrefStore::SchedulePendingLossyWrites() {
- // This method is misnamed for the sake of the interface. Given that writing
- // is already asynchronous in this new world, "sheduleing" a pending write
- // just starts the asynchronous process.
- if (pending_lossy_write_)
- PerformWrite();
-}
-
-void FilesystemJsonPrefStore::ReportValueChanged(const std::string& key,
- uint32_t flags) {
- DCHECK(CalledOnValidThread());
-
- if (pref_filter_)
- pref_filter_->FilterUpdate(key);
-
- for (PrefStore::Observer& observer : observers_)
- observer.OnPrefValueChanged(key);
-
- ScheduleWrite(flags);
-}
-
-void FilesystemJsonPrefStore::OnFileSystemShutdown() {}
-
-void FilesystemJsonPrefStore::OnFileRead(
- std::unique_ptr<ReadResult> read_result) {
- DCHECK(CalledOnValidThread());
-
- DCHECK(read_result);
-
- std::unique_ptr<base::DictionaryValue> unfiltered_prefs(
- new base::DictionaryValue);
-
- read_error_ = read_result->error;
-
- switch (read_error_) {
- case PREF_READ_ERROR_ACCESS_DENIED:
- case PREF_READ_ERROR_FILE_OTHER:
- case PREF_READ_ERROR_FILE_LOCKED:
- case PREF_READ_ERROR_JSON_TYPE:
- case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
- read_only_ = true;
- break;
- case PREF_READ_ERROR_NONE:
- DCHECK(read_result->value.get());
- unfiltered_prefs.reset(
- static_cast<base::DictionaryValue*>(read_result->value.release()));
- break;
- case PREF_READ_ERROR_NO_FILE:
- // If the file just doesn't exist, maybe this is first run. In any case
- // there's no harm in writing out default prefs in this case.
- case PREF_READ_ERROR_JSON_PARSE:
- case PREF_READ_ERROR_JSON_REPEAT:
- break;
- case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
- // This is a special error code to be returned by ReadPrefs when it
- // can't complete synchronously, it should never be returned by the read
- // operation itself.
- case PREF_READ_ERROR_MAX_ENUM:
- NOTREACHED();
- break;
- }
-
- if (pref_filter_) {
- filtering_in_progress_ = true;
- const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
- base::Bind(&FilesystemJsonPrefStore::FinalizeFileRead, AsWeakPtr()));
- pref_filter_->FilterOnLoad(post_filter_on_load_callback,
- std::move(unfiltered_prefs));
- } else {
- FinalizeFileRead(std::move(unfiltered_prefs), false);
- }
-}
-
-FilesystemJsonPrefStore::~FilesystemJsonPrefStore() {
- // TODO(erg): Now that writing is asynchronous, we can't really async write
- // prefs at shutdown. See comment in CommitPendingWrite().
-}
-
-void FilesystemJsonPrefStore::FinalizeFileRead(
- std::unique_ptr<base::DictionaryValue> prefs,
- bool schedule_write) {
- DCHECK(CalledOnValidThread());
-
- filtering_in_progress_ = false;
-
- prefs_ = std::move(prefs);
-
- initialized_ = true;
-
- if (schedule_write)
- ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS);
-
- if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
- error_delegate_->OnError(read_error_);
-
- for (PrefStore::Observer& observer : observers_)
- observer.OnInitializationCompleted(true);
-}
-
-void FilesystemJsonPrefStore::ScheduleWrite(uint32_t flags) {
- if (read_only_)
- return;
-
- if (flags & LOSSY_PREF_WRITE_FLAG)
- pending_lossy_write_ = true;
- else
- PerformWrite();
-}
-
-void FilesystemJsonPrefStore::PerformWrite() {
- if (!directory_) {
- OpenFilesystem(
- Bind(&FilesystemJsonPrefStore::OnTempFileWriteStart, AsWeakPtr()));
- } else {
- OnTempFileWriteStart();
- }
-}
-
-void FilesystemJsonPrefStore::OpenFilesystem(base::Closure callback) {
- filesystem::mojom::FileSystemClientPtr client;
- binding_.Bind(MakeRequest(&client));
-
- filesystem_->OpenFileSystem(
- "origin", MakeRequest(&directory_), std::move(client),
- base::Bind(&FilesystemJsonPrefStore::OnOpenFilesystem, AsWeakPtr(),
- callback));
-}
-
-void FilesystemJsonPrefStore::OnOpenFilesystem(base::Closure callback,
- mojom::FileError err) {
- if (err != mojom::FileError::OK) {
- // Do real error checking.
- NOTIMPLEMENTED();
- return;
- }
-
- callback.Run();
-}
-
-void FilesystemJsonPrefStore::OnTempFileWriteStart() {
- // Calculate what we want to write, and then write to the temporary file.
- pending_lossy_write_ = false;
-
- if (pref_filter_)
- pref_filter_->FilterSerializeData(prefs_.get());
-
- std::string output;
- JSONStringValueSerializer serializer(&output);
- serializer.set_pretty_print(false);
- serializer.Serialize(*prefs_);
-
- directory_->WriteFile(
- "tmp", std::vector<uint8_t>(output.begin(), output.end()),
- Bind(&FilesystemJsonPrefStore::OnTempFileWrite, AsWeakPtr()));
-}
-
-void FilesystemJsonPrefStore::OnTempFileWrite(mojom::FileError err) {
- // TODO(erg): Error handling. The JsonPrefStore code assumes that writing the
- // file can never fail.
- CHECK_EQ(mojom::FileError::OK, err);
-
- directory_->Rename(
- "tmp", path_,
- Bind(&FilesystemJsonPrefStore::OnTempFileRenamed, AsWeakPtr()));
-}
-
-void FilesystemJsonPrefStore::OnTempFileRenamed(mojom::FileError err) {}
-
-void FilesystemJsonPrefStore::OnPreferencesReadStart() {
- directory_->ReadEntireFile(
- path_,
- Bind(&FilesystemJsonPrefStore::OnPreferencesFileRead, AsWeakPtr()));
-}
-
-void FilesystemJsonPrefStore::OnPreferencesFileRead(
- mojom::FileError err,
- const std::vector<uint8_t>& contents) {
- std::unique_ptr<FilesystemJsonPrefStore::ReadResult> read_result(
- new FilesystemJsonPrefStore::ReadResult);
- // TODO(erg): Needs even better error handling.
- switch (err) {
- case mojom::FileError::IN_USE:
- case mojom::FileError::ACCESS_DENIED:
- case mojom::FileError::NOT_A_FILE: {
- read_only_ = true;
- break;
- }
- case mojom::FileError::FAILED:
- case mojom::FileError::NOT_FOUND: {
- // If the file just doesn't exist, maybe this is the first run. Just
- // don't pass a value.
- read_result->error = PREF_READ_ERROR_NO_FILE;
- break;
- }
- default: {
- int error_code;
- std::string error_msg;
- JSONStringValueDeserializer deserializer(base::StringPiece(
- reinterpret_cast<char*>(&contents.front()), contents.size()));
- read_result->value = deserializer.Deserialize(&error_code, &error_msg);
- read_result->error = HandleReadErrors(read_result->value.get());
- }
- }
-
- OnFileRead(std::move(read_result));
-}
-
-} // namespace filesystem
diff --git a/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h b/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h
deleted file mode 100644
index fcd9e29f88b..00000000000
--- a/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h
+++ /dev/null
@@ -1,179 +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_FILESYSTEM_PUBLIC_CPP_PREFS_FILESYSTEM_JSON_PREF_STORE_H_
-#define COMPONENTS_FILESYSTEM_PUBLIC_CPP_PREFS_FILESYSTEM_JSON_PREF_STORE_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-#include <string>
-
-#include "base/callback_forward.h"
-#include "base/compiler_specific.h"
-#include "base/files/file_path.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/threading/non_thread_safe.h"
-#include "components/filesystem/public/interfaces/directory.mojom.h"
-#include "components/filesystem/public/interfaces/file.mojom.h"
-#include "components/filesystem/public/interfaces/file_system.mojom.h"
-#include "components/filesystem/public/interfaces/types.mojom.h"
-#include "components/prefs/base_prefs_export.h"
-#include "components/prefs/persistent_pref_store.h"
-#include "components/prefs/pref_filter.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-class PrefFilter;
-
-namespace base {
-class DictionaryValue;
-class JsonPrefStoreLossyWriteTest;
-class Value;
-}
-
-namespace filesystem {
-
-// A forked, hack'n'slashed copy of base::JsonPrefStore which writes its
-// preference data to the mojo filesystem instead of the real
-// filesystem. Unlike base::JsonPrefStore, this class can safely be used inside
-// a sandboxed process.
-//
-// In the long run, we'll want to replace the current PrefService code with
-// something very different, especially since this component hard punts on all
-// the hard things that the preference service does (enterprise management,
-// parental controls, extension integration, etc.) and its interface is really
-// not optimal for a mojoified world--there are a few places where we assume
-// that writing to disk is synchronous...but it no longer is!
-//
-// Removing this class is a part of crbug.com/580652.
-class FilesystemJsonPrefStore
- : public PersistentPrefStore,
- public filesystem::mojom::FileSystemClient,
- public base::SupportsWeakPtr<FilesystemJsonPrefStore>,
- public base::NonThreadSafe {
- public:
- struct ReadResult;
-
- FilesystemJsonPrefStore(const std::string& pref_filename,
- filesystem::mojom::FileSystemPtr filesystem,
- std::unique_ptr<PrefFilter> pref_filter);
-
- // PrefStore overrides:
- bool GetValue(const std::string& key,
- const base::Value** result) const override;
- std::unique_ptr<base::DictionaryValue> GetValues() const override;
- void AddObserver(PrefStore::Observer* observer) override;
- void RemoveObserver(PrefStore::Observer* observer) override;
- bool HasObservers() const override;
- bool IsInitializationComplete() const override;
-
- // PersistentPrefStore overrides:
- bool GetMutableValue(const std::string& key, base::Value** result) override;
- void SetValue(const std::string& key,
- std::unique_ptr<base::Value> value,
- uint32_t flags) override;
- void SetValueSilently(const std::string& key,
- std::unique_ptr<base::Value> value,
- uint32_t flags) override;
- void RemoveValue(const std::string& key, uint32_t flags) override;
- bool ReadOnly() const override;
- PrefReadError GetReadError() const override;
- // Note this method may be asynchronous if this instance has a |pref_filter_|
- // in which case it will return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE.
- // See details in pref_filter.h.
- PrefReadError ReadPrefs() override;
- void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
- void CommitPendingWrite() override;
- void SchedulePendingLossyWrites() override;
- void ReportValueChanged(const std::string& key, uint32_t flags) override;
-
- // FileSystemClient overrides:
- void OnFileSystemShutdown() override;
-
- // Just like RemoveValue(), but doesn't notify observers. Used when doing some
- // cleanup that shouldn't otherwise alert observers.
- void RemoveValueSilently(const std::string& key, uint32_t flags);
-
- void ClearMutableValues() override;
-
- private:
- friend class base::JsonPrefStoreLossyWriteTest;
-
- ~FilesystemJsonPrefStore() override;
-
- // This method is called after the JSON file has been read. It then hands
- // |value| (or an empty dictionary in some read error cases) to the
- // |pref_filter| if one is set. It also gives a callback pointing at
- // FinalizeFileRead() to that |pref_filter_| which is then responsible for
- // invoking it when done. If there is no |pref_filter_|, FinalizeFileRead()
- // is invoked directly.
- void OnFileRead(std::unique_ptr<ReadResult> read_result);
-
- // This method is called after the JSON file has been read and the result has
- // potentially been intercepted and modified by |pref_filter_|.
- // |schedule_write| indicates whether a write should be immediately scheduled
- // (typically because the |pref_filter_| has already altered the |prefs|) --
- // this will be ignored if this store is read-only.
- void FinalizeFileRead(std::unique_ptr<base::DictionaryValue> prefs,
- bool schedule_write);
-
- // Schedule a write with the file writer as long as |flags| doesn't contain
- // WriteablePrefStore::LOSSY_PREF_WRITE_FLAG.
- void ScheduleWrite(uint32_t flags);
-
- // Actually performs a write. Unlike the //base version of this class, we
- // don't use the ImportantFileWriter and instead write using the mojo
- // filesystem API.
- void PerformWrite();
-
- // Opens the filesystem and calls |callback| when completed, whether
- // successfully or unsuccessfully.
- void OpenFilesystem(base::Closure callback);
-
- // Callback method which verifies that there were no errors on opening the
- // filesystem, and if there aren't, invokes the passed in callback.
- void OnOpenFilesystem(base::Closure callback, mojom::FileError err);
-
- // Asynchronous implementation details of PerformWrite().
- void OnTempFileWriteStart();
- void OnTempFileWrite(mojom::FileError err);
- void OnTempFileRenamed(mojom::FileError err);
-
- // Asynchronous implementation details of ReadPrefsAsync().
- void OnPreferencesReadStart();
- void OnPreferencesFileRead(mojom::FileError err,
- const std::vector<uint8_t>& contents);
-
- const std::string path_;
- mojo::Binding<filesystem::mojom::FileSystemClient> binding_;
- filesystem::mojom::FileSystemPtr filesystem_;
-
- // |directory_| is only bound after the first attempt to access the
- // |filesystem. See OpenFilesystem().
- mojom::DirectoryPtr directory_;
-
- std::unique_ptr<base::DictionaryValue> prefs_;
-
- bool read_only_;
-
- std::unique_ptr<PrefFilter> pref_filter_;
- base::ObserverList<PrefStore::Observer, true> observers_;
-
- std::unique_ptr<ReadErrorDelegate> error_delegate_;
-
- bool initialized_;
- bool filtering_in_progress_;
- bool pending_lossy_write_;
- PrefReadError read_error_;
-
- DISALLOW_COPY_AND_ASSIGN(FilesystemJsonPrefStore);
-};
-
-} // namespace filesystem
-
-#endif // COMPONENTS_FILESYSTEM_PUBLIC_CPP_PREFS_FILESYSTEM_JSON_PREF_STORE_H_
diff --git a/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc b/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc
deleted file mode 100644
index e9ca2b95226..00000000000
--- a/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc
+++ /dev/null
@@ -1,47 +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/filesystem/public/cpp/prefs/pref_service_factory.h"
-
-#include "base/bind.h"
-#include "components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h"
-#include "components/prefs/pref_notifier_impl.h"
-#include "components/prefs/pref_registry.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/pref_value_store.h"
-#include "components/prefs/value_map_pref_store.h"
-#include "components/prefs/writeable_pref_store.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace filesystem {
-
-namespace {
-
-// Do-nothing default implementation.
-void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) {}
-
-} // namespace
-
-std::unique_ptr<PrefService> CreatePrefService(
- service_manager::Connector* connector,
- PrefRegistry* pref_registry) {
- filesystem::mojom::FileSystemPtr filesystem;
- connector->BindInterface("filesystem", &filesystem);
-
- scoped_refptr<FilesystemJsonPrefStore> user_prefs =
- new FilesystemJsonPrefStore("preferences.json", std::move(filesystem),
- nullptr /* TODO(erg): pref filter */);
-
- PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
- std::unique_ptr<PrefService> pref_service(new PrefService(
- pref_notifier,
- new PrefValueStore(nullptr, nullptr, nullptr, nullptr, user_prefs.get(),
- nullptr, pref_registry->defaults().get(),
- pref_notifier),
- user_prefs.get(), pref_registry, base::Bind(&DoNothingHandleReadError),
- true /* async */));
- return pref_service;
-}
-
-} // namespace filesystem
diff --git a/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.h b/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.h
deleted file mode 100644
index 86a2704da43..00000000000
--- a/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.h
+++ /dev/null
@@ -1,29 +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_FILESYSTEM_PUBLIC_CPP_PREFS_PREF_SERVICE_FACTORY_H_
-#define COMPONENTS_FILESYSTEM_PUBLIC_CPP_PREFS_PREF_SERVICE_FACTORY_H_
-
-#include <memory>
-
-#include "components/prefs/pref_service.h"
-
-namespace mojo {
-class Connector;
-}
-
-class PrefRegistry;
-
-namespace filesystem {
-
-// This factory method creates a PrefService for the local process based on the
-// preference registry passed in. This PrefService will synchronize with a JSON
-// file in the mojo:filesystem.
-std::unique_ptr<PrefService> CreatePrefService(
- service_manager::Connector* connector,
- PrefRegistry* registry);
-
-} // namespace filesystem
-
-#endif // COMPONENTS_FILESYSTEM_PUBLIC_CPP_PREFS_PREF_SERVICE_FACTORY_H_
diff --git a/chromium/components/filesystem/public/interfaces/directory.mojom b/chromium/components/filesystem/public/interfaces/directory.mojom
index ac3370e8e45..1cd6b01fbd7 100644
--- a/chromium/components/filesystem/public/interfaces/directory.mojom
+++ b/chromium/components/filesystem/public/interfaces/directory.mojom
@@ -65,6 +65,13 @@ interface Directory {
[Sync]
Rename(string path, string new_path) => (FileError error);
+ // Renames file |path| to |new_path|. Both paths must be on the same volume,
+ // or the function will fail. Destination file will be created if it doesn't
+ // exist. Prefer this function over Rename when dealing with temporary files.
+ // On Windows it preserves attributes of the target file.
+ [Sync]
+ Replace(string path, string new_path) => (FileError error);
+
// Deletes the given path, which may be a file or a directory (see
// |kDeleteFlag...| for details).
[Sync]
diff --git a/chromium/components/find_in_page_strings.grdp b/chromium/components/find_in_page_strings.grdp
index a1fd5422564..fa48a7985c1 100644
--- a/chromium/components/find_in_page_strings.grdp
+++ b/chromium/components/find_in_page_strings.grdp
@@ -2,7 +2,7 @@
<grit-part>
<message name="IDS_FIND_IN_PAGE_COUNT" desc="How many matches found and what item we are showing">
- <ph name="ACTIVE_MATCH">$1<ex>1</ex></ph> of <ph name="TOTAL_MATCHCOUNT">$2<ex>5</ex></ph>
+ <ph name="ACTIVE_MATCH">$1</ph>/<ph name="TOTAL_MATCHCOUNT">$2</ph>
</message>
<message name="IDS_FIND_IN_PAGE_PREVIOUS_TOOLTIP" desc="The tooltip for the previous button">
Previous
diff --git a/chromium/components/flags_ui/flags_state.cc b/chromium/components/flags_ui/flags_state.cc
index d89c3629c35..663d04ad37f 100644
--- a/chromium/components/flags_ui/flags_state.cc
+++ b/chromium/components/flags_ui/flags_state.cc
@@ -11,6 +11,7 @@
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
@@ -175,22 +176,23 @@ bool IsDefaultValue(const FeatureEntry& entry,
}
// Returns the Value representing the choice data in the specified entry.
-base::Value* CreateOptionsData(const FeatureEntry& entry,
- const std::set<std::string>& enabled_entries) {
+std::unique_ptr<base::Value> CreateOptionsData(
+ const FeatureEntry& entry,
+ const std::set<std::string>& enabled_entries) {
DCHECK(entry.type == FeatureEntry::MULTI_VALUE ||
entry.type == FeatureEntry::ENABLE_DISABLE_VALUE ||
entry.type == FeatureEntry::FEATURE_VALUE ||
entry.type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE);
- base::ListValue* result = new base::ListValue;
+ auto result = base::MakeUnique<base::ListValue>();
for (int i = 0; i < entry.num_options; ++i) {
- std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
+ auto value = base::MakeUnique<base::DictionaryValue>();
const std::string name = entry.NameForOption(i);
value->SetString("internal_name", name);
value->SetString("description", entry.DescriptionForOption(i));
value->SetBoolean("selected", enabled_entries.count(name) > 0);
result->Append(std::move(value));
}
- return result;
+ return std::move(result);
}
// Registers variation parameters specified by |feature_variation_params| for
@@ -515,9 +517,9 @@ void FlagsState::GetFlagFeatureEntries(
data->SetString("description",
base::StringPiece(entry.visible_description));
- base::ListValue* supported_platforms = new base::ListValue();
- AddOsStrings(entry.supported_platforms, supported_platforms);
- data->Set("supported_platforms", supported_platforms);
+ auto supported_platforms = base::MakeUnique<base::ListValue>();
+ AddOsStrings(entry.supported_platforms, supported_platforms.get());
+ data->Set("supported_platforms", std::move(supported_platforms));
// True if the switch is not currently passed.
bool is_default_value = IsDefaultValue(entry, enabled_entries);
data->SetBoolean("is_default", is_default_value);
@@ -546,10 +548,6 @@ void FlagsState::GetFlagFeatureEntries(
supported = true;
}
#endif
-#if defined(OS_IOS)
- if (access == kAppleReviewAccessToFlags)
- supported = ((entry.supported_platforms & kOsIosAppleReview) != 0);
-#endif
if (supported)
supported_entries->Append(std::move(data));
else
diff --git a/chromium/components/flags_ui/flags_state.h b/chromium/components/flags_ui/flags_state.h
index 17b3b99c532..b888a0ae633 100644
--- a/chromium/components/flags_ui/flags_state.h
+++ b/chromium/components/flags_ui/flags_state.h
@@ -43,7 +43,6 @@ enum {
kOsAndroid = 1 << 4,
kOsCrOSOwnerOnly = 1 << 5,
kOsIos = 1 << 6,
- kOsIosAppleReview = 1 << 7,
};
// A flag controlling the behavior of the |ConvertFlagsToSwitches| function -
@@ -54,10 +53,7 @@ enum SentinelsMode { kNoSentinels, kAddSentinels };
// that influence the whole machine and can be said by the admin only. This flag
// is relevant for ChromeOS for now only and dictates whether entries marked
// with the |kOsCrOSOwnerOnly| label should be enabled in the UI or not.
-// On iOS, |kAppleReviewAccessToFlags| indicates that the flags shown should
-// be the ones marked for Apple review (which otherwise will not be shown).
-enum FlagAccess { kGeneralAccessFlagsOnly, kOwnerAccessToFlags,
- kAppleReviewAccessToFlags };
+enum FlagAccess { kGeneralAccessFlagsOnly, kOwnerAccessToFlags };
// Stores and encapsulates the little state that about:flags has.
class FlagsState {
diff --git a/chromium/components/flags_ui/resources/apple_flags.css b/chromium/components/flags_ui/resources/apple_flags.css
deleted file mode 100644
index 96548ee45e0..00000000000
--- a/chromium/components/flags_ui/resources/apple_flags.css
+++ /dev/null
@@ -1,175 +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. */
-
-body {
- margin: 10px 10px 0;
-<if expr="not is_android and not is_ios">
- min-width: 47em;
-</if>
- /* Should match needs-restart.height + 5 */
- padding-bottom: 75px;
-}
-
-a {
- color: blue;
- font-size: 103%;
-}
-
-.permalink {
- color: #A0A0A0;
-}
-
-#header {
- -webkit-padding-start: 75px;
- box-sizing: border-box;
- display: table;
- margin-bottom: 1.05em;
- /* 67px is the height of the header's image. */
- min-height: 67px;
- overflow: hidden;
- position: relative;
-}
-
-#title-spacer {
- display: table-cell;
- vertical-align: middle;
-}
-
-html[dir=rtl] #header {
- background-position: right center;
-}
-
-h1 {
- font-size: 156%;
- font-weight: bold;
- margin: 0;
- padding: 0;
-}
-
-.blurb-container {
- font-size: 120%;
- padding-bottom: 1.5em;
-}
-
-#blurb-warning {
- color: red;
- font-weight: bold;
-}
-
-div.content {
- font-size: 88%;
- margin: 5px auto 10px;
-}
-
-div.content:last-of-type {
- margin-bottom: 0;
-}
-
-.section-header {
- background: rgb(235, 239, 249);
- border-top: 1px solid rgb(181, 199, 222);
- font-size: 99%;
- padding: 2px 5px 3px;
- width: 100%;
-}
-
-.section-header > table tr td:first-child {
- width: 100%;
-}
-
-.section-header > table {
- width: 100%;
-}
-
-.section-header-title {
- font-weight: bold;
- line-height: 200%;
-}
-
-#experiment-reset-all {
- float: right;
-}
-
-html[dir=rtl] #experiment-reset-all {
- float: left;
-}
-
-.vbox-container {
- -webkit-box-orient: vertical;
- display: -webkit-box;
-}
-
-.wbox {
- -webkit-box-align: stretch;
- -webkit-box-flex: 1;
- display: -webkit-box;
-}
-
-#top {
- -webkit-padding-end: 5px;
-}
-
-/* Disabled and unsupported experiments display grey text on a grey background.
- The title, however, should remain legible. */
-
-.experiment-unsupported > td,
-.experiment-disabled > td {
- background: #F0F0F0;
- color: #A0A0A0;
-}
-
-.experiment-unsupported .experiment-name,
-.experiment-disabled .experiment-name {
- color: #000;
-}
-
-.experiment {
- border-bottom: 1px solid #cdcdcd;
-}
-
-.experiment td {
- padding-bottom: 4px;
- padding-top: 5px;
-}
-
-/* Indent the text related to each experiment. */
-.experiment-text {
- -webkit-padding-start: 5px;
-}
-
-.experiment-name {
- font-weight: bold;
-}
-
-.referenced .experiment-name {
- background-color: rgb(255, 255, 0);
-}
-
-/* Match the indentation of .experiment-text. */
-.experiment-actions {
- -webkit-padding-start: 5px;
- margin-bottom: 0.2em;
- margin-top: 0.2em;
-}
-
-div.needs-restart {
- background: #FFF;
- border-top: 1px solid rgb(181, 199, 222);
- bottom: 0;
- box-sizing: border-box;
- /* If you change this, update body.padding-bottom */
- height: 70px;
- left: 0;
- padding-bottom: 25px;
- padding-left: 15px;
- padding-right: 15px;
- padding-top: 15px;
- position: fixed;
- width: 100%;
-}
-
-button {
- font-size: 104%;
-}
-
diff --git a/chromium/components/flags_ui/resources/apple_flags.html b/chromium/components/flags_ui/resources/apple_flags.html
deleted file mode 100644
index 5b34466ba15..00000000000
--- a/chromium/components/flags_ui/resources/apple_flags.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!DOCTYPE HTML>
-<html i18n-values="dir:textdirection;">
-<head>
-<meta charset="utf-8">
-<if expr="is_android or is_ios">
-<meta name="viewport" content="width=device-width, user-scalable=no">
-</if>
-<link rel="stylesheet" href="apple_flags.css">
-<if expr="is_ios">
-<script src="chrome://resources/js/ios/web_ui.js"></script>
-</if>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<script src="chrome://ui-alternatives/flags.js"></script>
-<script src="chrome://ui-alternatives/strings.js"></script>
-</head>
-<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
-<div id="body-container" style="visibility:hidden">
- <div id="flagsTemplate">
- <div id="container" class="vbox-container">
- <div id="top" class="wbox">
- <div class="section-header">
- <table cellpadding="0" cellspacing="0"><tr valign="center">
- <td>
- <span class="section-header-title" i18n-content="flagsTableTitle"
- >TITLE</span>
- <button id="experiment-reset-all"
- jsdisplay="supportedFeatures.length > 0" type="button"
- i18n-content="resetAllButton"></button>
- </td>
- </tr></table>
- </div>
- </div>
- </div>
-
- <div class="content">
- <div class="experiment-name no-experiments"
- jsdisplay="supportedFeatures.length == 0">
- <div i18n-content="flagsNoExperimentsAvailable"
- >NO_EXPERIMENTS_ARE_AVAILABLE</div>
- </div>
-
- <div jsdisplay="supportedFeatures.length > 0">
- <div class="experiment"
- jsselect="supportedFeatures"
- jsvalues="id:internal_name">
- <table width="100%" cellpadding="2" cellspacing="0">
- <!-- TODO(mkwst): This doesn't work exactly as expected for multivalue
- experiments. See http://crbug.com/73730 -->
- <tr jsvalues="class: enabled ? 'experiment-enabled'
- : 'experiment-disabled'">
- <td valign="top">
- <div class="experiment-text">
- <div>
- <span class="experiment-name"
- jscontent="name">NAME</span>
- <span jscontent="supported_platforms.join(', ')"></span>
- <div>
- <span jsvalues=".innerHTML:description"></span>
- <a class="permalink"
- jsvalues="href: '#' + internal_name"
- jscontent="'#' + internal_name"></a>
- </div>
- <div jsdisplay="choices && choices.length > 0">
- <select
- class="experiment-select"
- jsvalues=".internal_name:internal_name;.disabled:!enabled">
- <option jsvalues=".selected:selected"
- jsselect="choices"
- jscontent="description">NAME
- </option>
- </select>
- </div>
- </div>
- </div>
- <div class="experiment-actions">
- <span>
- <a
- class="experiment-disable-link"
- jsvalues=".internal_name:internal_name"
- jsdisplay="enabled"
- href="#"
- i18n-content="disable"
- >DISABLE</a>
- <a
- class="experiment-enable-link"
- jsvalues=".internal_name:internal_name"
- jsdisplay="!enabled"
- href="#"
- i18n-content="enable"
- >ENABLE</a>
- </span>
- </div>
- </td>
- </tr>
- </table>
- </div>
- </div>
- </div>
-
- <div class="needs-restart"
- jsdisplay="supportedFeatures.length > 0">
- <div i18n-content="flagsRestartNotice">NEEDS_RESTART</div>
- </div>
- </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/flags_ui/resources/flags.html b/chromium/components/flags_ui/resources/flags.html
index aca6d35d72c..5e2edd9a79e 100644
--- a/chromium/components/flags_ui/resources/flags.html
+++ b/chromium/components/flags_ui/resources/flags.html
@@ -1,5 +1,5 @@
<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<if expr="is_android or is_ios">
@@ -13,28 +13,26 @@
<script src="chrome://resources/js/ios/web_ui.js"></script>
</if>
-<script src="chrome://resources/js/load_time_data.js"></script>
<script src="chrome://resources/js/util.js"></script>
<script src="chrome://flags/flags.js"></script>
-<script src="chrome://flags/strings.js"></script>
</head>
<body>
<div id="body-container" style="visibility:hidden">
<div id="header">
- <div id="title-spacer"><h1 i18n-content="flagsLongTitle"></h1></div>
+ <div id="title-spacer"><h1>$i18n{flagsLongTitle}</h1></div>
</div>
<div class="blurb-container">
- <span id="blurb-warning" i18n-content="flagsWarningHeader">WARNING</span>
- <span i18n-content="flagsBlurb">WARNING TEXT</span>
- <span id="channel-promo-beta" i18n-content="channelPromoBeta" hidden></span>
- <span id="channel-promo-dev" i18n-content="channelPromoDev" hidden></span>
+ <span id="blurb-warning">$i18n{flagsWarningHeader}</span>
+ <span>$i18n{flagsBlurb}</span>
+ <span id="channel-promo-beta" hidden>$i18n{channelPromoBeta}</span>
+ <span id="channel-promo-dev" hidden>$i18n{channelPromoDev}</span>
</div>
<if expr="chromeos">
<div class="blurb-container" id="owner-warning">
- <span i18n-content="ownerWarning"></span>
+ <span>$i18n{ownerWarning}</span>
</div>
</if>
@@ -44,10 +42,10 @@
<div class="section-header">
<table cellpadding="0" cellspacing="0"><tr valign="center">
<td>
- <span class="section-header-title" i18n-content="flagsTableTitle"
- >TITLE</span>
- <button id="experiment-reset-all" type="button"
- i18n-content="resetAllButton"></button>
+ <span class="section-header-title">$i18n{flagsTableTitle}</span>
+ <button id="experiment-reset-all" type="button">
+ $i18n{resetAllButton}
+ </button>
</td>
</tr></table>
</div>
@@ -92,17 +90,11 @@
<a
class="experiment-disable-link"
jsvalues=".internal_name:internal_name"
- jsdisplay="enabled"
- href="#"
- i18n-content="disable"
- >DISABLE</a>
+ jsdisplay="enabled" href="#">$i18n{disable}</a>
<a
class="experiment-enable-link"
jsvalues=".internal_name:internal_name"
- jsdisplay="!enabled"
- href="#"
- i18n-content="enable"
- >ENABLE</a>
+ jsdisplay="!enabled" href="#">$i18n{enable}</a>
</span>
</div>
</td>
@@ -118,9 +110,8 @@
<div class="section-header">
<table cellpadding="0" cellspacing="0"><tr valign="center">
<td>
- <span class="section-header-title"
- i18n-content="flagsUnsupportedTableTitle"
- >TITLE
+ <span class="section-header-title">
+ $i18n{flagsUnsupportedTableTitle}
</span>
</td>
</tr></table>
@@ -149,7 +140,7 @@
</div>
</div>
<div class="experiment-actions">
- <div i18n-content="flagsNotSupported"></div>
+ <div>$i18n{flagsNotSupported}</div>
</div>
</td>
</tr>
@@ -159,13 +150,13 @@
</if>
<div class="needs-restart" jsdisplay="needsRestart">
- <div i18n-content="flagsRestartNotice">NEEDS_RESTART</div>
- <button class="experiment-restart-button" type="button"
- i18n-content="flagsRestartButton">RESTART</button>
+ <div>$i18n{flagsRestartNotice}</div>
+ <button class="experiment-restart-button" type="button">
+ $i18n{flagsRestartButton}
+ </button>
</div>
</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/flags_ui_strings.grdp b/chromium/components/flags_ui_strings.grdp
index 3fbc8f3e325..ce8bb8741d6 100644
--- a/chromium/components/flags_ui_strings.grdp
+++ b/chromium/components/flags_ui_strings.grdp
@@ -46,12 +46,4 @@
Flags that apply system-wide can only be set by the owner: <ph name="OWNER_EMAIL">$1<ex>owner@example.com</ex></ph>.
</message>
</if>
- <if expr="is_ios">
- <message name="IDS_FLAGS_UI_ALTERNATIVES_UI_TABLE_TITLE" desc="Title for the features table on the about:ui_alternatives page.">
- UI Alternatives
- </message>
- <message name="IDS_FLAGS_UI_ALTERNATIVES_UI_NO_EXPERIMENTS_AVAILABLE" desc="Shown on the about://ui-alternatives page if no experiments are available">
- No UI alternatives available
- </message>
- </if>
</grit-part>
diff --git a/chromium/components/font_service/font_service_app.cc b/chromium/components/font_service/font_service_app.cc
index 7a78c81af4f..6feb15649ca 100644
--- a/chromium/components/font_service/font_service_app.cc
+++ b/chromium/components/font_service/font_service_app.cc
@@ -52,8 +52,7 @@ void FontServiceApp::OnBindInterface(
const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
- registry_.BindInterface(source_info, interface_name,
- std::move(interface_pipe));
+ registry_.BindInterface(interface_name, std::move(interface_pipe));
}
void FontServiceApp::MatchFamilyName(const std::string& family_name,
@@ -108,8 +107,7 @@ void FontServiceApp::OpenStream(uint32_t id_number,
std::move(callback).Run(std::move(file));
}
-void FontServiceApp::Create(const service_manager::BindSourceInfo& source_info,
- mojom::FontServiceRequest request) {
+void FontServiceApp::Create(mojom::FontServiceRequest request) {
bindings_.AddBinding(this, std::move(request));
}
diff --git a/chromium/components/font_service/font_service_app.h b/chromium/components/font_service/font_service_app.h
index c60c315d8f6..d48cb1b6dc9 100644
--- a/chromium/components/font_service/font_service_app.h
+++ b/chromium/components/font_service/font_service_app.h
@@ -36,8 +36,7 @@ class FontServiceApp : public service_manager::Service,
MatchFamilyNameCallback callback) override;
void OpenStream(uint32_t id_number, OpenStreamCallback callback) override;
- void Create(const service_manager::BindSourceInfo& source_info,
- mojom::FontServiceRequest request);
+ void Create(mojom::FontServiceRequest request);
int FindOrAddPath(const SkString& path);
diff --git a/chromium/components/gcm_driver/BUILD.gn b/chromium/components/gcm_driver/BUILD.gn
index 2a549fa855c..dd9f678b8d3 100644
--- a/chromium/components/gcm_driver/BUILD.gn
+++ b/chromium/components/gcm_driver/BUILD.gn
@@ -2,12 +2,16 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/buildflag_header.gni")
+import("//components/gcm_driver/config.gni")
+
+buildflag_header("gcm_build_features") {
+ header = "gcm_build_features.h"
+ flags = [ "USE_GCM_FROM_PLATFORM=$use_gcm_from_platform" ]
+}
+
static_library("gcm_driver") {
sources = [
- "android/component_jni_registrar.cc",
- "android/component_jni_registrar.h",
- "gcm_account_mapper.cc",
- "gcm_account_mapper.h",
"gcm_account_tracker.cc",
"gcm_account_tracker.h",
"gcm_activity.cc",
@@ -16,40 +20,22 @@ static_library("gcm_driver") {
"gcm_app_handler.h",
"gcm_backoff_policy.cc",
"gcm_backoff_policy.h",
- "gcm_channel_status_request.cc",
- "gcm_channel_status_request.h",
- "gcm_channel_status_syncer.cc",
- "gcm_channel_status_syncer.h",
"gcm_client.cc",
"gcm_client.h",
- "gcm_client_factory.cc",
- "gcm_client_factory.h",
- "gcm_client_impl.cc",
- "gcm_client_impl.h",
"gcm_connection_observer.cc",
"gcm_connection_observer.h",
"gcm_delayed_task_controller.cc",
"gcm_delayed_task_controller.h",
- "gcm_desktop_utils.cc",
- "gcm_desktop_utils.h",
"gcm_driver.cc",
"gcm_driver.h",
- "gcm_driver_android.cc",
- "gcm_driver_android.h",
"gcm_driver_constants.cc",
"gcm_driver_constants.h",
- "gcm_driver_desktop.cc",
- "gcm_driver_desktop.h",
"gcm_internals_constants.cc",
"gcm_internals_constants.h",
"gcm_internals_helper.cc",
"gcm_internals_helper.h",
"gcm_profile_service.cc",
"gcm_profile_service.h",
- "gcm_stats_recorder_android.cc",
- "gcm_stats_recorder_android.h",
- "gcm_stats_recorder_impl.cc",
- "gcm_stats_recorder_impl.h",
"registration_info.cc",
"registration_info.h",
"system_encryptor.cc",
@@ -63,6 +49,7 @@ static_library("gcm_driver") {
"//components/gcm_driver/instance_id",
]
deps = [
+ ":gcm_build_features",
"//base:i18n",
"//components/crx_file",
"//components/data_use_measurement/core",
@@ -90,8 +77,15 @@ static_library("gcm_driver") {
deps += [ "//components/timers" ]
}
- if (is_android) {
- sources -= [
+ if (use_gcm_from_platform) {
+ sources += [
+ "gcm_driver_android.cc",
+ "gcm_driver_android.h",
+ "gcm_stats_recorder_android.cc",
+ "gcm_stats_recorder_android.h",
+ ]
+ } else {
+ sources += [
"gcm_account_mapper.cc",
"gcm_account_mapper.h",
"gcm_channel_status_request.cc",
@@ -109,10 +103,13 @@ static_library("gcm_driver") {
"gcm_stats_recorder_impl.cc",
"gcm_stats_recorder_impl.h",
]
- deps -= [
+ deps += [
"//components/crx_file",
"//google_apis/gcm",
]
+ }
+
+ if (is_android) {
deps += [ "android:jni_headers" ]
}
}
@@ -122,10 +119,6 @@ static_library("test_support") {
sources = [
"fake_gcm_app_handler.cc",
"fake_gcm_app_handler.h",
- "fake_gcm_client.cc",
- "fake_gcm_client.h",
- "fake_gcm_client_factory.cc",
- "fake_gcm_client_factory.h",
"fake_gcm_driver.cc",
"fake_gcm_driver.h",
]
@@ -140,14 +133,14 @@ static_library("test_support") {
"//testing/gtest",
]
- if (is_android) {
- sources -= [
+ if (!use_gcm_from_platform) {
+ sources += [
"fake_gcm_client.cc",
"fake_gcm_client.h",
"fake_gcm_client_factory.cc",
"fake_gcm_client_factory.h",
]
- deps -= [ "//google_apis/gcm:test_support" ]
+ deps += [ "//google_apis/gcm:test_support" ]
}
}
@@ -155,14 +148,9 @@ source_set("unit_tests") {
testonly = true
sources = [
- "gcm_account_mapper_unittest.cc",
"gcm_account_tracker_unittest.cc",
- "gcm_channel_status_request_unittest.cc",
- "gcm_client_impl_unittest.cc",
"gcm_delayed_task_controller_unittest.cc",
- "gcm_driver_desktop_unittest.cc",
"gcm_stats_recorder_android_unittest.cc",
- "gcm_stats_recorder_impl_unittest.cc",
]
deps = [
@@ -180,8 +168,8 @@ source_set("unit_tests") {
"//third_party/protobuf:protobuf_lite",
]
- if (is_android) {
- sources -= [
+ if (!use_gcm_from_platform) {
+ sources += [
"gcm_account_mapper_unittest.cc",
"gcm_channel_status_request_unittest.cc",
"gcm_client_impl_unittest.cc",
diff --git a/chromium/components/gcm_driver/config.gni b/chromium/components/gcm_driver/config.gni
new file mode 100644
index 00000000000..9620f1abc95
--- /dev/null
+++ b/chromium/components/gcm_driver/config.gni
@@ -0,0 +1,9 @@
+# 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.
+
+declare_args() {
+ # Use native GCM driver for all non-Android builds. On Android, the platform
+ # includes GMS which provides the GCM client.
+ use_gcm_from_platform = is_android
+}
diff --git a/chromium/components/gcm_driver/crypto/BUILD.gn b/chromium/components/gcm_driver/crypto/BUILD.gn
index 20d24ee5f85..5ee3c7db667 100644
--- a/chromium/components/gcm_driver/crypto/BUILD.gn
+++ b/chromium/components/gcm_driver/crypto/BUILD.gn
@@ -8,6 +8,8 @@ static_library("crypto") {
sources = [
"encryption_header_parsers.cc",
"encryption_header_parsers.h",
+ "gcm_decryption_result.cc",
+ "gcm_decryption_result.h",
"gcm_encryption_provider.cc",
"gcm_encryption_provider.h",
"gcm_key_store.cc",
diff --git a/chromium/components/gcm_driver/instance_id/BUILD.gn b/chromium/components/gcm_driver/instance_id/BUILD.gn
index b77c0344be4..b36c16ccbfc 100644
--- a/chromium/components/gcm_driver/instance_id/BUILD.gn
+++ b/chromium/components/gcm_driver/instance_id/BUILD.gn
@@ -2,10 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//components/gcm_driver/config.gni")
+
static_library("instance_id") {
sources = [
- "android/component_jni_registrar.cc",
- "android/component_jni_registrar.h",
"instance_id.cc",
"instance_id.h",
"instance_id_driver.cc",
@@ -19,7 +19,7 @@ static_library("instance_id") {
"//crypto",
]
- if (is_android) {
+ if (use_gcm_from_platform) {
sources -= [
"instance_id_impl.cc",
"instance_id_impl.h",
@@ -65,6 +65,7 @@ source_set("unit_tests") {
":instance_id",
":test_support",
"//base",
+ "//components/gcm_driver:gcm_build_features",
"//google_apis/gcm",
"//net:test_support",
"//testing/gtest",
diff --git a/chromium/components/google/core/browser/google_util.cc b/chromium/components/google/core/browser/google_util.cc
index b8f8c1daa9e..845e22a633c 100644
--- a/chromium/components/google/core/browser/google_util.cc
+++ b/chromium/components/google/core/browser/google_util.cc
@@ -283,4 +283,30 @@ bool IsYoutubeDomainUrl(const GURL& url,
nullptr);
}
+const std::vector<std::string>& GetGoogleRegistrableDomains() {
+ CR_DEFINE_STATIC_LOCAL(std::vector<std::string>, kGoogleRegisterableDomains,
+ ());
+
+ // Initialize the list.
+ if (kGoogleRegisterableDomains.empty()) {
+ std::vector<std::string> tlds{GOOGLE_TLD_LIST};
+ for (const std::string& tld : tlds) {
+ std::string domain = "google." + tld;
+
+ // The Google TLD list might contain domains that are not considered
+ // to be registrable domains by net::registry_controlled_domains.
+ if (GetDomainAndRegistry(
+ domain,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) !=
+ domain) {
+ continue;
+ }
+
+ kGoogleRegisterableDomains.push_back(domain);
+ }
+ }
+
+ return kGoogleRegisterableDomains;
+}
+
} // namespace google_util
diff --git a/chromium/components/google/core/browser/google_util.h b/chromium/components/google/core/browser/google_util.h
index d2526b22b38..d4730ef29e6 100644
--- a/chromium/components/google/core/browser/google_util.h
+++ b/chromium/components/google/core/browser/google_util.h
@@ -8,6 +8,7 @@
#define COMPONENTS_GOOGLE_CORE_BROWSER_GOOGLE_UTIL_H_
#include <string>
+#include <vector>
#include "base/strings/string_piece.h"
@@ -112,6 +113,14 @@ bool IsYoutubeDomainUrl(const GURL& url,
SubdomainPermission subdomain_permission,
PortPermission port_permission);
+// Returns the list of all Google's registerable domains, i.e. domains named
+// google.<eTLD> owned by Google.
+// TODO(msramek): This is currently only used to ensure the deletion of Google
+// service workers on signout. Remove this once we have other options to do it,
+// such as service workers discovering that signin cookies are missing and
+// unregistering themselves.
+const std::vector<std::string>& GetGoogleRegistrableDomains();
+
} // namespace google_util
#endif // COMPONENTS_GOOGLE_CORE_BROWSER_GOOGLE_UTIL_H_
diff --git a/chromium/components/guest_view/browser/BUILD.gn b/chromium/components/guest_view/browser/BUILD.gn
index 6430459a7f0..96b43908d41 100644
--- a/chromium/components/guest_view/browser/BUILD.gn
+++ b/chromium/components/guest_view/browser/BUILD.gn
@@ -2,9 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//extensions/features/features.gni")
-
-assert(enable_extensions)
+# Currently, GuestViews are only used by extensions, so we have this
+# assert to prevent the accidental building of GuestViews on mobile
+# platforms. If you're now using GuestViews on mobile, go ahead and
+# remove this assert.
+assert(!is_android && !is_ios)
static_library("browser") {
output_name = "guest_view_browser"
diff --git a/chromium/components/guest_view/browser/guest_view_base.cc b/chromium/components/guest_view/browser/guest_view_base.cc
index 27570202e8e..24ec4b67e72 100644
--- a/chromium/components/guest_view/browser/guest_view_base.cc
+++ b/chromium/components/guest_view/browser/guest_view_base.cc
@@ -16,6 +16,7 @@
#include "components/guest_view/common/guest_view_messages.h"
#include "components/zoom/page_zoom.h"
#include "components/zoom/zoom_controller.h"
+#include "content/public/browser/guest_mode.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
@@ -172,6 +173,7 @@ GuestViewBase::GuestViewBase(WebContents* owner_web_contents)
guest_instance_id_(GetGuestViewManager()->GetNextInstanceID()),
view_instance_id_(kInstanceIDNone),
element_instance_id_(kInstanceIDNone),
+ attach_in_progress_(false),
initialized_(false),
is_being_destroyed_(false),
guest_host_(nullptr),
@@ -237,8 +239,7 @@ void GuestViewBase::InitWithWebContents(
// Populate the view instance ID if we have it on creation.
create_params.GetInteger(kParameterInstanceId, &view_instance_id_);
- if (CanRunInDetachedState())
- SetUpSizing(create_params);
+ SetUpSizing(create_params);
// Observe guest zoom changes.
auto* zoom_controller = zoom::ZoomController::FromWebContents(web_contents());
@@ -405,6 +406,10 @@ WebContents* GuestViewBase::CreateNewGuestWindow(
void GuestViewBase::OnRenderFrameHostDeleted(int process_id, int routing_id) {}
void GuestViewBase::DidAttach(int guest_proxy_routing_id) {
+ DCHECK(attach_in_progress_);
+ // Clear this flag here, as functions called below may check attached().
+ attach_in_progress_ = false;
+
DCHECK(guest_proxy_routing_id_ == MSG_ROUTING_NONE ||
guest_proxy_routing_id == guest_proxy_routing_id_);
guest_proxy_routing_id_ = guest_proxy_routing_id;
@@ -426,13 +431,15 @@ void GuestViewBase::DidAttach(int guest_proxy_routing_id) {
SendQueuedEvents();
}
+// TODO(wjmaclean): Delete this when browser plugin goes away;
+// https://crbug.com/533069 .
void GuestViewBase::DidDetach() {
GuestViewManager::FromBrowserContext(browser_context_)->DetachGuest(this);
StopTrackingEmbedderZoomLevel();
owner_web_contents()->Send(new GuestViewMsg_GuestDetached(
element_instance_id_));
element_instance_id_ = kInstanceIDNone;
- if (!CanRunInDetachedState())
+ if (ShouldDestroyOnDetach())
Destroy(true);
}
@@ -448,6 +455,10 @@ const GURL& GuestViewBase::GetOwnerSiteURL() const {
return owner_web_contents()->GetLastCommittedURL();
}
+bool GuestViewBase::ShouldDestroyOnDetach() const {
+ return false;
+}
+
void GuestViewBase::Destroy(bool also_delete) {
if (is_being_destroyed_)
return;
@@ -524,6 +535,7 @@ void GuestViewBase::WillAttach(WebContents* embedder_web_contents,
// Start tracking the new embedder's zoom level.
StartTrackingEmbedderZoomLevel();
+ attach_in_progress_ = true;
element_instance_id_ = element_instance_id;
is_full_page_plugin_ = is_full_page_plugin;
@@ -741,13 +753,13 @@ void GuestViewBase::DispatchEventToGuestProxy(
}
void GuestViewBase::DispatchEventToView(std::unique_ptr<GuestViewEvent> event) {
- if (!attached() &&
- (!CanRunInDetachedState() || !can_owner_receive_events())) {
- pending_events_.push_back(std::move(event));
+ if ((attached() && pending_events_.empty()) ||
+ (can_owner_receive_events() &&
+ !content::GuestMode::IsCrossProcessFrameGuest(web_contents()))) {
+ event->Dispatch(this, view_instance_id_);
return;
}
-
- event->Dispatch(this, view_instance_id_);
+ pending_events_.push_back(std::move(event));
}
void GuestViewBase::SendQueuedEvents() {
diff --git a/chromium/components/guest_view/browser/guest_view_base.h b/chromium/components/guest_view/browser/guest_view_base.h
index 67fdaf0e373..8b7d21dfa7d 100644
--- a/chromium/components/guest_view/browser/guest_view_base.h
+++ b/chromium/components/guest_view/browser/guest_view_base.h
@@ -155,7 +155,7 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate,
// Returns whether this guest has an associated embedder.
bool attached() const {
- return element_instance_id_ != kInstanceIDNone;
+ return (element_instance_id_ != kInstanceIDNone) && !attach_in_progress_;
}
// Returns the instance ID of the <*view> element.
@@ -195,6 +195,11 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate,
// Destroy this guest.
void Destroy(bool also_delete);
+ // Indicates whether a guest should call destroy during DidDetach().
+ // TODO(wjmaclean): Delete this when browser plugin goes away;
+ // https://crbug.com/533069 .
+ virtual bool ShouldDestroyOnDetach() const;
+
// Saves the attach state of the custom element hosting this GuestView.
void SetAttachParams(const base::DictionaryValue& params);
void SetOpener(GuestViewBase* opener);
@@ -419,6 +424,12 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate,
// GuestViewContainer element.
int element_instance_id_;
+ // |attach_in_progress_| is used to make sure that attached() doesn't return
+ // true until after DidAttach() is called, since that's when we are guaranteed
+ // that the contentWindow for cross-process-iframe based guests will become
+ // valid.
+ bool attach_in_progress_;
+
// |initialized_| indicates whether GuestViewBase::Init has been called for
// this object.
bool initialized_;
diff --git a/chromium/components/guest_view/browser/guest_view_manager.h b/chromium/components/guest_view/browser/guest_view_manager.h
index 4c1ad70b451..9045a219f5b 100644
--- a/chromium/components/guest_view/browser/guest_view_manager.h
+++ b/chromium/components/guest_view/browser/guest_view_manager.h
@@ -66,10 +66,10 @@ class GuestViewManager : public content::BrowserPluginGuestManager,
// Associates the Browser Plugin with |element_instance_id| to a
// guest that has ID of |guest_instance_id| and sets initialization
// parameters, |params| for it.
- void AttachGuest(int embedder_process_id,
- int element_instance_id,
- int guest_instance_id,
- const base::DictionaryValue& attach_params);
+ virtual void AttachGuest(int embedder_process_id,
+ int element_instance_id,
+ int guest_instance_id,
+ const base::DictionaryValue& attach_params);
// Removes the association between |element_instance_id| and a guest instance
// ID if one exists.
diff --git a/chromium/components/guest_view/browser/guest_view_manager_unittest.cc b/chromium/components/guest_view/browser/guest_view_manager_unittest.cc
index aeec9016df1..654efcbc8b5 100644
--- a/chromium/components/guest_view/browser/guest_view_manager_unittest.cc
+++ b/chromium/components/guest_view/browser/guest_view_manager_unittest.cc
@@ -10,7 +10,6 @@
#include "base/macros.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
-#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,23 +28,20 @@ class GuestViewManagerTest : public content::RenderViewHostTestHarness {
std::unique_ptr<WebContents> CreateWebContents() {
return std::unique_ptr<WebContents>(
- WebContentsTester::CreateTestWebContents(&browser_context_, NULL));
+ WebContentsTester::CreateTestWebContents(browser_context(), nullptr));
}
private:
- content::TestBrowserContext browser_context_;
-
DISALLOW_COPY_AND_ASSIGN(GuestViewManagerTest);
};
} // namespace
TEST_F(GuestViewManagerTest, AddRemove) {
- content::TestBrowserContext browser_context;
std::unique_ptr<GuestViewManagerDelegate> delegate(
new GuestViewManagerDelegate());
std::unique_ptr<TestGuestViewManager> manager(
- new TestGuestViewManager(&browser_context, std::move(delegate)));
+ new TestGuestViewManager(browser_context(), std::move(delegate)));
std::unique_ptr<WebContents> web_contents1(CreateWebContents());
std::unique_ptr<WebContents> web_contents2(CreateWebContents());
diff --git a/chromium/components/guest_view/browser/test_guest_view_manager.cc b/chromium/components/guest_view/browser/test_guest_view_manager.cc
index efaacbcc5db..41dc908ab1d 100644
--- a/chromium/components/guest_view/browser/test_guest_view_manager.cc
+++ b/chromium/components/guest_view/browser/test_guest_view_manager.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/memory/ptr_util.h"
+#include "components/guest_view/browser/guest_view_base.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
namespace guest_view {
@@ -19,7 +20,8 @@ TestGuestViewManager::TestGuestViewManager(
num_guests_created_(0),
expected_num_guests_created_(0),
num_views_garbage_collected_(0),
- waiting_for_guests_created_(false) {}
+ waiting_for_guests_created_(false),
+ waiting_for_attach_(nullptr) {}
TestGuestViewManager::~TestGuestViewManager() {
}
@@ -82,6 +84,19 @@ void TestGuestViewManager::WaitForNumGuestsCreated(size_t count) {
num_created_message_loop_runner_->Run();
}
+void TestGuestViewManager::WaitUntilAttached(
+ content::WebContents* guest_web_contents) {
+ GuestViewBase* guest = GuestViewBase::FromWebContents(guest_web_contents);
+
+ if (guest->attached())
+ return;
+
+ waiting_for_attach_ = guest;
+
+ attached_message_loop_runner_ = new content::MessageLoopRunner;
+ attached_message_loop_runner_->Run();
+}
+
void TestGuestViewManager::WaitForViewGarbageCollected() {
gc_message_loop_runner_ = new content::MessageLoopRunner;
gc_message_loop_runner_->Run();
@@ -113,6 +128,22 @@ void TestGuestViewManager::AddGuest(int guest_instance_id,
num_created_message_loop_runner_->Quit();
}
+void TestGuestViewManager::AttachGuest(
+ int embedder_process_id,
+ int element_instance_id,
+ int guest_instance_id,
+ const base::DictionaryValue& attach_params) {
+ GuestViewManager::AttachGuest(embedder_process_id, element_instance_id,
+ guest_instance_id, attach_params);
+
+ if (waiting_for_attach_ &&
+ (waiting_for_attach_ ==
+ GuestViewBase::From(embedder_process_id, guest_instance_id))) {
+ attached_message_loop_runner_->Quit();
+ waiting_for_attach_ = nullptr;
+ }
+}
+
void TestGuestViewManager::GetGuestWebContentsList(
std::vector<content::WebContents*>* guest_web_contents_list) {
for (auto& watcher : guest_web_contents_watchers_)
diff --git a/chromium/components/guest_view/browser/test_guest_view_manager.h b/chromium/components/guest_view/browser/test_guest_view_manager.h
index 05c5bb69468..a287d0be871 100644
--- a/chromium/components/guest_view/browser/test_guest_view_manager.h
+++ b/chromium/components/guest_view/browser/test_guest_view_manager.h
@@ -35,6 +35,8 @@ class TestGuestViewManager : public GuestViewManager {
content::WebContents* GetLastGuestCreated();
+ void WaitUntilAttached(content::WebContents* web_contents);
+
// Returns the number of guests currently still alive at the time of calling
// this method.
size_t GetNumGuestsActive() const;
@@ -87,6 +89,10 @@ class TestGuestViewManager : public GuestViewManager {
void EmbedderProcessDestroyed(int embedder_process_id) override;
void ViewGarbageCollected(int embedder_process_id,
int view_instance_id) override;
+ void AttachGuest(int embedder_process_id,
+ int element_instance_id,
+ int guest_instance_id,
+ const base::DictionaryValue& attach_params) override;
void WaitForViewGarbageCollected();
@@ -103,6 +109,8 @@ class TestGuestViewManager : public GuestViewManager {
guest_web_contents_watchers_;
scoped_refptr<content::MessageLoopRunner> created_message_loop_runner_;
scoped_refptr<content::MessageLoopRunner> num_created_message_loop_runner_;
+ GuestViewBase* waiting_for_attach_;
+ scoped_refptr<content::MessageLoopRunner> attached_message_loop_runner_;
scoped_refptr<content::MessageLoopRunner> gc_message_loop_runner_;
DISALLOW_COPY_AND_ASSIGN(TestGuestViewManager);
diff --git a/chromium/components/guest_view/renderer/BUILD.gn b/chromium/components/guest_view/renderer/BUILD.gn
index 479bdef0feb..f3d79c79a8b 100644
--- a/chromium/components/guest_view/renderer/BUILD.gn
+++ b/chromium/components/guest_view/renderer/BUILD.gn
@@ -2,9 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//extensions/features/features.gni")
-
-assert(enable_extensions)
+# Currently, GuestViews are only used by extensions, so we have this
+# assert to prevent the accidental building of GuestViews on mobile
+# platforms. If you're now using GuestViews on mobile, go ahead and
+# remove this assert.
+assert(!is_android && !is_ios)
static_library("renderer") {
sources = [
diff --git a/chromium/components/guest_view/renderer/guest_view_request.cc b/chromium/components/guest_view/renderer/guest_view_request.cc
index ea96bc9b3c7..b9be45503d6 100644
--- a/chromium/components/guest_view/renderer/guest_view_request.cc
+++ b/chromium/components/guest_view/renderer/guest_view_request.cc
@@ -91,14 +91,7 @@ void GuestViewAttachRequest::HandleResponse(const IPC::Message& message) {
v8::HandleScope handle_scope(isolate());
blink::WebFrame* frame = guest_proxy_render_view->GetWebView()->MainFrame();
- // TODO(lazyboy,nasko): The WebLocalFrame branch is not used when running
- // on top of out-of-process iframes. Remove it once the code is converted.
- v8::Local<v8::Value> window;
- if (frame->IsWebLocalFrame()) {
- window = frame->MainWorldScriptContext()->Global();
- } else {
- window = frame->ToWebRemoteFrame()->GlobalProxy();
- }
+ v8::Local<v8::Value> window = frame->GlobalProxy();
const int argc = 1;
std::unique_ptr<v8::Local<v8::Value>[]> argv(new v8::Local<v8::Value>[argc]);
diff --git a/chromium/components/handoff/handoff_manager.h b/chromium/components/handoff/handoff_manager.h
index fedaf9b9572..f5ab5a28df2 100644
--- a/chromium/components/handoff/handoff_manager.h
+++ b/chromium/components/handoff/handoff_manager.h
@@ -7,6 +7,7 @@
#import <Foundation/Foundation.h>
+#include "base/mac/availability.h"
#include "build/build_config.h"
#include "components/handoff/handoff_utility.h"
#include "url/gurl.h"
@@ -31,12 +32,14 @@ class PrefRegistrySyncable;
// The active URL is defined as the URL of the most recently accessed tab. This
// method should be called any time the active URL might have changed. This
// method is idempotent.
-- (void)updateActiveURL:(const GURL&)url;
+- (void)updateActiveURL:(const GURL&)url API_AVAILABLE(macos(10.10));
@end
+#if defined(OS_IOS)
@interface HandoffManager (TestingOnly)
- (NSURL*)userActivityWebpageURL;
@end
+#endif
#endif // COMPONENTS_HANDOFF_HANDOFF_MANAGER_H_
diff --git a/chromium/components/handoff/handoff_manager.mm b/chromium/components/handoff/handoff_manager.mm
index cdcb8f16901..18dd9d9ad7d 100644
--- a/chromium/components/handoff/handoff_manager.mm
+++ b/chromium/components/handoff/handoff_manager.mm
@@ -23,20 +23,21 @@
@interface HandoffManager ()
// The active user activity.
-@property(nonatomic, retain) NSUserActivity* userActivity;
+@property(nonatomic, retain)
+ NSUserActivity* userActivity API_AVAILABLE(macos(10.10));
// Whether the URL of the current tab should be exposed for Handoff.
- (BOOL)shouldUseActiveURL;
// Updates the active NSUserActivity.
-- (void)updateUserActivity;
+- (void)updateUserActivity API_AVAILABLE(macos(10.10));
@end
@implementation HandoffManager {
base::mac::ObjCPropertyReleaser _propertyReleaser_HandoffManager;
GURL _activeURL;
- NSUserActivity* _userActivity;
+ NSUserActivity* _userActivity API_AVAILABLE(macos(10.10));
handoff::Origin _origin;
}
@@ -95,11 +96,9 @@
// Invalidate the old user activity and make a new one.
[self.userActivity invalidate];
- Class aClass = NSClassFromString(@"NSUserActivity");
- NSUserActivity* userActivity = [[aClass performSelector:@selector(alloc)]
- performSelector:@selector(initWithActivityType:)
- withObject:handoff::kChromeHandoffActivityType];
- self.userActivity = base::scoped_nsobject<NSUserActivity>(userActivity);
+ base::scoped_nsobject<NSUserActivity> userActivity([[NSUserActivity alloc]
+ initWithActivityType:handoff::kChromeHandoffActivityType]);
+ self.userActivity = userActivity;
self.userActivity.webpageURL = net::NSURLWithGURL(_activeURL);
NSString* origin = handoff::StringFromOrigin(_origin);
DCHECK(origin);
@@ -109,6 +108,7 @@
@end
+#if defined(OS_IOS)
@implementation HandoffManager (TestingOnly)
- (NSURL*)userActivityWebpageURL {
@@ -116,3 +116,4 @@
}
@end
+#endif
diff --git a/chromium/components/history/core/browser/BUILD.gn b/chromium/components/history/core/browser/BUILD.gn
index e60183b62e9..a8cd984f57e 100644
--- a/chromium/components/history/core/browser/BUILD.gn
+++ b/chromium/components/history/core/browser/BUILD.gn
@@ -33,8 +33,6 @@ static_library("browser") {
"history_db_task.h",
"history_delete_directives_data_type_controller.cc",
"history_delete_directives_data_type_controller.h",
- "history_match.cc",
- "history_match.h",
"history_model_worker.cc",
"history_model_worker.h",
"history_service.cc",
@@ -200,6 +198,7 @@ source_set("unit_tests") {
"top_sites_cache_unittest.cc",
"top_sites_database_unittest.cc",
"top_sites_impl_unittest.cc",
+ "typed_url_sync_bridge_unittest.cc",
"typed_url_sync_metadata_database_unittest.cc",
"typed_url_syncable_service_unittest.cc",
"url_database_unittest.cc",
diff --git a/chromium/components/history/core/browser/android/favicon_sql_handler.cc b/chromium/components/history/core/browser/android/favicon_sql_handler.cc
index dd7a094ea60..b630bae4ea3 100644
--- a/chromium/components/history/core/browser/android/favicon_sql_handler.cc
+++ b/chromium/components/history/core/browser/android/favicon_sql_handler.cc
@@ -39,7 +39,8 @@ bool FaviconSQLHandler::Update(const HistoryAndBookmarkRow& row,
// icon is already in database, just create a new favicon.
// TODO(pkotwicz): Pass in real pixel size.
favicon_base::FaviconID favicon_id = thumbnail_db_->AddFavicon(
- GURL(), favicon_base::FAVICON, row.favicon(), Time::Now(), gfx::Size());
+ GURL(), favicon_base::FAVICON, row.favicon(), FaviconBitmapType::ON_VISIT,
+ Time::Now(), gfx::Size());
if (!favicon_id)
return false;
@@ -104,7 +105,8 @@ bool FaviconSQLHandler::Insert(HistoryAndBookmarkRow* row) {
// Is it a problem to give a empty URL?
// TODO(pkotwicz): Pass in real pixel size.
favicon_base::FaviconID id = thumbnail_db_->AddFavicon(
- GURL(), favicon_base::FAVICON, row->favicon(), Time::Now(), gfx::Size());
+ GURL(), favicon_base::FAVICON, row->favicon(),
+ FaviconBitmapType::ON_VISIT, Time::Now(), gfx::Size());
if (!id)
return false;
return thumbnail_db_->AddIconMapping(row->url(), id);
diff --git a/chromium/components/history/core/browser/expire_history_backend_unittest.cc b/chromium/components/history/core/browser/expire_history_backend_unittest.cc
index 11cd46f7578..0a554fa8895 100644
--- a/chromium/components/history/core/browser/expire_history_backend_unittest.cc
+++ b/chromium/components/history/core/browser/expire_history_backend_unittest.cc
@@ -16,11 +16,11 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/scoped_observer.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
#include "components/history/core/browser/history_backend_client.h"
#include "components/history/core/browser/history_backend_notifier.h"
#include "components/history/core/browser/history_constants.h"
@@ -55,7 +55,9 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier {
public:
ExpireHistoryTest()
: backend_client_(history_client_.CreateBackendClient()),
- expirer_(this, backend_client_.get(), message_loop_.task_runner()),
+ expirer_(this,
+ backend_client_.get(),
+ scoped_task_environment_.GetMainThreadTaskRunner()),
now_(base::Time::Now()) {}
protected:
@@ -96,11 +98,11 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier {
// This must be destroyed last.
base::ScopedTempDir tmp_dir_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+
HistoryClientFakeBookmarks history_client_;
std::unique_ptr<HistoryBackendClient> backend_client_;
- base::MessageLoopForUI message_loop_;
-
ExpireHistoryBackend expirer_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
@@ -139,8 +141,7 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier {
PrepopulatedPageList(),
base::Bind(MockCanAddURLToHistory));
WaitTopSitesLoadedObserver wait_top_sites_observer(top_sites_);
- top_sites_->Init(path().Append(kTopSitesFilename),
- message_loop_.task_runner());
+ top_sites_->Init(path().Append(kTopSitesFilename));
wait_top_sites_observer.Run();
}
diff --git a/chromium/components/history/core/browser/history_backend.cc b/chromium/components/history/core/browser/history_backend.cc
index 14855781aac..bd7d01dc097 100644
--- a/chromium/components/history/core/browser/history_backend.cc
+++ b/chromium/components/history/core/browser/history_backend.cc
@@ -41,7 +41,6 @@
#include "components/history/core/browser/in_memory_history_backend.h"
#include "components/history/core/browser/keyword_search_term.h"
#include "components/history/core/browser/page_usage_data.h"
-#include "components/history/core/browser/typed_url_sync_bridge.h"
#include "components/history/core/browser/typed_url_syncable_service.h"
#include "components/history/core/browser/url_utils.h"
#include "components/sync/driver/sync_driver_switches.h"
@@ -224,6 +223,7 @@ void HistoryBackend::Init(
&ModelTypeChangeProcessor::Create,
// TODO(gangwu): use ReportUnrecoverableError before launch.
base::BindRepeating(base::IgnoreResult(&DumpWithoutCrashing))));
+ typed_url_sync_bridge_->Init();
} else {
typed_url_syncable_service_ =
base::MakeUnique<TypedUrlSyncableService>(this);
@@ -1236,6 +1236,7 @@ void HistoryBackend::QueryHistoryBasic(const QueryOptions& options,
DCHECK_LE(static_cast<int>(visits.size()), options.EffectiveMaxCount());
// Now add them and the URL rows to the results.
+ std::vector<URLResult> matching_results;
URLResult url_result;
for (size_t i = 0; i < visits.size(); i++) {
const VisitRow visit = visits[i];
@@ -1261,8 +1262,9 @@ void HistoryBackend::QueryHistoryBasic(const QueryOptions& options,
// We don't set any of the query-specific parts of the URLResult, since
// snippets and stuff don't apply to basic querying.
- result->AppendURLBySwapping(&url_result);
+ matching_results.push_back(std::move(url_result));
}
+ result->SetURLResults(std::move(matching_results));
if (!has_more_results && options.begin_time <= first_recorded_time_)
result->set_reached_beginning(true);
@@ -1295,13 +1297,14 @@ void HistoryBackend::QueryHistoryText(const base::string16& text_query,
size_t max_results = options.max_count == 0
? std::numeric_limits<size_t>::max()
: static_cast<int>(options.max_count);
- for (std::vector<URLResult>::iterator it = matching_visits.begin();
- it != matching_visits.end() && result->size() < max_results; ++it) {
- result->AppendURLBySwapping(&(*it));
+ bool has_more_results = false;
+ if (matching_visits.size() > max_results) {
+ has_more_results = true;
+ matching_visits.resize(max_results);
}
+ result->SetURLResults(std::move(matching_visits));
- if (matching_visits.size() == result->size() &&
- options.begin_time <= first_recorded_time_)
+ if(!has_more_results && options.begin_time <= first_recorded_time_)
result->set_reached_beginning(true);
}
@@ -1636,8 +1639,9 @@ void HistoryBackend::MergeFavicon(
thumbnail_db_->DeleteFaviconBitmap(bitmap_id_sizes[0].bitmap_id);
favicon_sizes.erase(favicon_sizes.begin());
}
- thumbnail_db_->AddFaviconBitmap(favicon_id, bitmap_data, base::Time::Now(),
- pixel_size);
+ thumbnail_db_->AddFaviconBitmap(favicon_id, bitmap_data,
+ FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), pixel_size);
favicon_sizes.push_back(pixel_size);
}
@@ -1698,9 +1702,10 @@ void HistoryBackend::MergeFavicon(
// Add the favicon bitmap as expired as it is not consistent with the
// merged in data.
- thumbnail_db_->AddFaviconBitmap(
- favicon_id, bitmaps_to_copy[j].bitmap_data, base::Time(),
- bitmaps_to_copy[j].pixel_size);
+ thumbnail_db_->AddFaviconBitmap(favicon_id,
+ bitmaps_to_copy[j].bitmap_data,
+ FaviconBitmapType::ON_VISIT, base::Time(),
+ bitmaps_to_copy[j].pixel_size);
favicon_sizes.push_back(bitmaps_to_copy[j].pixel_size);
favicon_bitmaps_copied = true;
@@ -1733,14 +1738,13 @@ void HistoryBackend::SetFavicons(const GURL& page_url,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps) {
SetFaviconsImpl(page_url, icon_type, icon_url, bitmaps,
- /*bitmaps_are_expired=*/false);
+ FaviconBitmapType::ON_VISIT);
}
-bool HistoryBackend::SetLastResortFavicons(
- const GURL& page_url,
- favicon_base::IconType icon_type,
- const GURL& icon_url,
- const std::vector<SkBitmap>& bitmaps) {
+bool HistoryBackend::SetOnDemandFavicons(const GURL& page_url,
+ favicon_base::IconType icon_type,
+ const GURL& icon_url,
+ const std::vector<SkBitmap>& bitmaps) {
if (!thumbnail_db_ || !db_)
return false;
@@ -1751,7 +1755,7 @@ bool HistoryBackend::SetLastResortFavicons(
}
return SetFaviconsImpl(page_url, icon_type, icon_url, bitmaps,
- /*bitmaps_are_expired=*/true);
+ FaviconBitmapType::ON_DEMAND);
}
void HistoryBackend::SetFaviconsOutOfDateForPage(const GURL& page_url) {
@@ -1768,6 +1772,14 @@ void HistoryBackend::SetFaviconsOutOfDateForPage(const GURL& page_url) {
ScheduleCommit();
}
+void HistoryBackend::TouchOnDemandFavicon(const GURL& icon_url) {
+ if (!thumbnail_db_)
+ return;
+
+ thumbnail_db_->TouchOnDemandFavicon(icon_url, Time::Now());
+ ScheduleCommit();
+}
+
void HistoryBackend::SetImportedFavicons(
const favicon_base::FaviconUsageDataList& favicon_usage) {
if (!db_ || !thumbnail_db_)
@@ -1787,8 +1799,8 @@ void HistoryBackend::SetImportedFavicons(
// TODO(pkotwicz): Pass in real pixel size.
favicon_id = thumbnail_db_->AddFavicon(
favicon_usage[i].favicon_url, favicon_base::FAVICON,
- new base::RefCountedBytes(favicon_usage[i].png_data), now,
- gfx::Size());
+ new base::RefCountedBytes(favicon_usage[i].png_data),
+ FaviconBitmapType::ON_VISIT, now, gfx::Size());
}
// Save the mapping from all the URLs to the favicon.
@@ -1833,7 +1845,7 @@ bool HistoryBackend::SetFaviconsImpl(const GURL& page_url,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps,
- bool bitmaps_are_expired) {
+ FaviconBitmapType type) {
if (!thumbnail_db_ || !db_)
return false;
@@ -1849,11 +1861,9 @@ bool HistoryBackend::SetFaviconsImpl(const GURL& page_url,
}
bool favicon_data_modified = false;
- if (favicon_created || !bitmaps_are_expired)
- favicon_data_modified = SetFaviconBitmaps(icon_id, bitmaps);
-
- if (favicon_created && bitmaps_are_expired)
- thumbnail_db_->SetFaviconOutOfDate(icon_id);
+ if (favicon_created || type == FaviconBitmapType::ON_VISIT) {
+ favicon_data_modified = SetFaviconBitmaps(icon_id, bitmaps, type);
+ }
std::vector<favicon_base::FaviconID> icon_ids(1u, icon_id);
bool mapping_changed =
@@ -1907,7 +1917,8 @@ void HistoryBackend::UpdateFaviconMappingsAndFetchImpl(
}
bool HistoryBackend::SetFaviconBitmaps(favicon_base::FaviconID icon_id,
- const std::vector<SkBitmap>& bitmaps) {
+ const std::vector<SkBitmap>& bitmaps,
+ FaviconBitmapType type) {
std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
thumbnail_db_->GetFaviconBitmapIDSizes(icon_id, &bitmap_id_sizes);
@@ -1944,11 +1955,12 @@ bool HistoryBackend::SetFaviconBitmaps(favicon_base::FaviconID icon_id,
} else {
if (!favicon_bitmaps_changed &&
IsFaviconBitmapDataEqual(bitmap_id, match_it->first)) {
- thumbnail_db_->SetFaviconBitmapLastUpdateTime(bitmap_id,
- base::Time::Now());
+ thumbnail_db_->SetFaviconBitmapLastUpdateTime(
+ bitmap_id, base::Time::Now() /* new last updated time */);
} else {
- thumbnail_db_->SetFaviconBitmap(bitmap_id, match_it->first,
- base::Time::Now());
+ thumbnail_db_->SetFaviconBitmap(
+ bitmap_id, match_it->first,
+ base::Time::Now() /* new last updated time */);
favicon_bitmaps_changed = true;
}
to_add.erase(match_it);
@@ -1956,8 +1968,10 @@ bool HistoryBackend::SetFaviconBitmaps(favicon_base::FaviconID icon_id,
}
for (size_t i = 0; i < to_add.size(); ++i) {
- thumbnail_db_->AddFaviconBitmap(icon_id, to_add[i].first, base::Time::Now(),
- to_add[i].second);
+ thumbnail_db_->AddFaviconBitmap(
+ icon_id, to_add[i].first, type,
+ base::Time::Now() /* new last updated / last requested time */,
+ to_add[i].second);
favicon_bitmaps_changed = true;
}
diff --git a/chromium/components/history/core/browser/history_backend.h b/chromium/components/history/core/browser/history_backend.h
index 59c58389c12..85c5fb83f05 100644
--- a/chromium/components/history/core/browser/history_backend.h
+++ b/chromium/components/history/core/browser/history_backend.h
@@ -32,6 +32,7 @@
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/keyword_id.h"
#include "components/history/core/browser/thumbnail_database.h"
+#include "components/history/core/browser/typed_url_sync_bridge.h"
#include "components/history/core/browser/visit_tracker.h"
#include "sql/init_status.h"
@@ -53,7 +54,6 @@ struct HistoryDatabaseParams;
class HistoryDBTask;
class InMemoryHistoryBackend;
class TypedUrlSyncableService;
-class TypedURLSyncBridge;
class HistoryBackendHelper;
class URLDatabase;
@@ -326,13 +326,15 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps);
- bool SetLastResortFavicons(const GURL& page_url,
- favicon_base::IconType icon_type,
- const GURL& icon_url,
- const std::vector<SkBitmap>& bitmaps);
+ bool SetOnDemandFavicons(const GURL& page_url,
+ favicon_base::IconType icon_type,
+ const GURL& icon_url,
+ const std::vector<SkBitmap>& bitmaps);
void SetFaviconsOutOfDateForPage(const GURL& page_url);
+ void TouchOnDemandFavicon(const GURL& icon_url);
+
void SetImportedFavicons(
const favicon_base::FaviconUsageDataList& favicon_usage);
@@ -476,6 +478,11 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
HistoryDatabase* db() const { return db_.get(); }
ExpireHistoryBackend* expire_backend() { return &expirer_; }
+
+ void SetTypedURLSyncBridgeForTest(
+ std::unique_ptr<TypedURLSyncBridge> bridge) {
+ typed_url_sync_bridge_ = std::move(bridge);
+ }
#endif
// Returns true if the passed visit time is already expired (used by the sync
@@ -523,11 +530,9 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetFaviconsReplaceBitmapData);
FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest,
SetFaviconsSameFaviconURLForTwoPages);
- FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetLastResortFaviconsForEmptyDB);
- FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest,
- SetLastResortFaviconsForPageInDB);
- FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest,
- SetLastResortFaviconsForIconInDB);
+ FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetOnDemandFaviconsForEmptyDB);
+ FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetOnDemandFaviconsForPageInDB);
+ FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetOnDemandFaviconsForIconInDB);
FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest,
UpdateFaviconMappingsAndFetchNoChange);
FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, MergeFaviconPageURLNotInDB);
@@ -681,7 +686,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps,
- bool bitmaps_are_expired);
+ FaviconBitmapType type);
// Used by both UpdateFaviconMappingsAndFetch() and GetFavicon().
// If |page_url| is non-null and there is a favicon stored in the database
@@ -694,16 +699,19 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
const std::vector<int>& desired_sizes,
std::vector<favicon_base::FaviconRawBitmapResult>* results);
- // Set the favicon bitmaps for |icon_id|.
+ // Set the favicon bitmaps of |type| for |icon_id|.
// For each entry in |bitmaps|, if a favicon bitmap already exists at the
// entry's pixel size, replace the favicon bitmap's data with the entry's
// bitmap data. Otherwise add a new favicon bitmap.
// Any favicon bitmaps already mapped to |icon_id| whose pixel size does not
// match the pixel size of one of |bitmaps| is deleted.
+ // For bitmap type FaviconBitmapType::ON_DEMAND, this is legal to call only
+ // for a newly created |icon_id| (that has no bitmaps yet).
// Returns true if any of the bitmap data at |icon_id| is changed as a result
// of calling this method.
bool SetFaviconBitmaps(favicon_base::FaviconID icon_id,
- const std::vector<SkBitmap>& bitmaps);
+ const std::vector<SkBitmap>& bitmaps,
+ FaviconBitmapType type);
// Returns true if the bitmap data at |bitmap_id| equals |new_bitmap_data|.
bool IsFaviconBitmapDataEqual(
diff --git a/chromium/components/history/core/browser/history_backend_unittest.cc b/chromium/components/history/core/browser/history_backend_unittest.cc
index 63dfe4b7309..c92e97e4d24 100644
--- a/chromium/components/history/core/browser/history_backend_unittest.cc
+++ b/chromium/components/history/core/browser/history_backend_unittest.cc
@@ -593,18 +593,22 @@ TEST_F(HistoryBackendTest, DeleteAll) {
std::vector<unsigned char> data;
data.push_back('a');
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
- new base::RefCountedBytes(data), base::Time::Now(), kSmallSize));
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+ favicon1, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), kSmallSize));
data[0] = 'b';
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
- new base::RefCountedBytes(data), base::Time::Now(), kLargeSize));
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+ favicon1, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), kLargeSize));
data[0] = 'c';
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
- new base::RefCountedBytes(data), base::Time::Now(), kSmallSize));
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+ favicon2, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), kSmallSize));
data[0] = 'd';
- EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
- new base::RefCountedBytes(data), base::Time::Now(), kLargeSize));
+ EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+ favicon2, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), kLargeSize));
// First visit two URLs.
URLRow row1(GURL("http://www.google.com/"));
@@ -737,7 +741,7 @@ TEST_F(HistoryBackendTest, DeleteAllURLPreviouslyDeleted) {
data.push_back('a');
favicon_base::FaviconID favicon = backend_->thumbnail_db_->AddFavicon(
kFaviconURL, favicon_base::FAVICON, new base::RefCountedBytes(data),
- base::Time::Now(), kSmallSize);
+ FaviconBitmapType::ON_VISIT, base::Time::Now(), kSmallSize);
backend_->thumbnail_db_->AddIconMapping(row.url(), favicon);
history_client_.AddBookmark(kPageURL);
@@ -821,20 +825,14 @@ TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
std::vector<unsigned char> data;
data.push_back('1');
- favicon_base::FaviconID favicon1 =
- backend_->thumbnail_db_->AddFavicon(favicon_url1,
- favicon_base::FAVICON,
- new base::RefCountedBytes(data),
- base::Time::Now(),
- gfx::Size());
+ favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
+ favicon_url1, favicon_base::FAVICON, new base::RefCountedBytes(data),
+ FaviconBitmapType::ON_VISIT, base::Time::Now(), gfx::Size());
data[0] = '2';
- favicon_base::FaviconID favicon2 =
- backend_->thumbnail_db_->AddFavicon(favicon_url2,
- favicon_base::FAVICON,
- new base::RefCountedBytes(data),
- base::Time::Now(),
- gfx::Size());
+ favicon_base::FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(
+ favicon_url2, favicon_base::FAVICON, new base::RefCountedBytes(data),
+ FaviconBitmapType::ON_VISIT, base::Time::Now(), gfx::Size());
// First visit two URLs.
URLRow row1(GURL("http://www.google.com/"));
@@ -1171,11 +1169,9 @@ TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
std::vector<unsigned char> data;
data.push_back('1');
favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
- favicon_url1,
- favicon_base::FAVICON,
- base::RefCountedBytes::TakeVector(&data),
- base::Time::Now(),
- gfx::Size());
+ favicon_url1, favicon_base::FAVICON,
+ base::RefCountedBytes::TakeVector(&data), FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), gfx::Size());
URLRow row1(GURL("http://www.google.com/"));
row1.set_visit_count(1);
row1.set_last_visit(base::Time::Now());
@@ -2076,18 +2072,18 @@ TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
EXPECT_EQ(2u, favicon_bitmaps.size());
}
-// Tests calling SetLastResortFavicons(). Neither |page_url| nor |icon_url| are
+// Tests calling SetOnDemandFavicons(). Neither |page_url| nor |icon_url| are
// known to the database.
-TEST_F(HistoryBackendTest, SetLastResortFaviconsForEmptyDB) {
+TEST_F(HistoryBackendTest, SetOnDemandFaviconsForEmptyDB) {
GURL page_url("http://www.google.com");
GURL icon_url("http:/www.google.com/favicon.ico");
std::vector<SkBitmap> bitmaps;
bitmaps.push_back(CreateBitmap(SK_ColorRED, kSmallEdgeSize));
- // Call SetLastResortFavicons() with a different icon URL and bitmap data.
- EXPECT_TRUE(backend_->SetLastResortFavicons(page_url, favicon_base::FAVICON,
- icon_url, bitmaps));
+ // Call SetOnDemandFavicons() with a different icon URL and bitmap data.
+ EXPECT_TRUE(backend_->SetOnDemandFavicons(page_url, favicon_base::FAVICON,
+ icon_url, bitmaps));
favicon_base::FaviconID favicon_id =
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(icon_url,
@@ -2102,9 +2098,9 @@ TEST_F(HistoryBackendTest, SetLastResortFaviconsForEmptyDB) {
EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
}
-// Tests calling SetLastResortFavicons(). |page_url| is known to the database
+// Tests calling SetOnDemandFavicons(). |page_url| is known to the database
// but |icon_url| is not (the second should be irrelevant though).
-TEST_F(HistoryBackendTest, SetLastResortFaviconsForPageInDB) {
+TEST_F(HistoryBackendTest, SetOnDemandFaviconsForPageInDB) {
GURL page_url("http://www.google.com");
GURL icon_url1("http:/www.google.com/favicon1.ico");
GURL icon_url2("http:/www.google.com/favicon2.ico");
@@ -2118,10 +2114,10 @@ TEST_F(HistoryBackendTest, SetLastResortFaviconsForPageInDB) {
favicon_base::FAVICON);
ASSERT_NE(0, original_favicon_id);
- // Call SetLastResortFavicons() with a different icon URL and bitmap data.
+ // Call SetOnDemandFavicons() with a different icon URL and bitmap data.
bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
- EXPECT_FALSE(backend_->SetLastResortFavicons(page_url, favicon_base::FAVICON,
- icon_url2, bitmaps));
+ EXPECT_FALSE(backend_->SetOnDemandFavicons(page_url, favicon_base::FAVICON,
+ icon_url2, bitmaps));
EXPECT_EQ(0, backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url2, favicon_base::FAVICON));
@@ -2133,9 +2129,9 @@ TEST_F(HistoryBackendTest, SetLastResortFaviconsForPageInDB) {
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
}
-// Tests calling SetLastResortFavicons(). |page_url| is not known to the
+// Tests calling SetOnDemandFavicons(). |page_url| is not known to the
// database but |icon_url| is.
-TEST_F(HistoryBackendTest, SetLastResortFaviconsForIconInDB) {
+TEST_F(HistoryBackendTest, SetOnDemandFaviconsForIconInDB) {
const GURL old_page_url("http://www.google.com/old");
const GURL page_url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon");
@@ -2149,10 +2145,10 @@ TEST_F(HistoryBackendTest, SetLastResortFaviconsForIconInDB) {
favicon_base::FAVICON);
ASSERT_NE(0, original_favicon_id);
- // Call SetLastResortFavicons() with a different bitmap.
+ // Call SetOnDemandFavicons() with a different bitmap.
bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
- EXPECT_FALSE(backend_->SetLastResortFavicons(page_url, favicon_base::FAVICON,
- icon_url, bitmaps));
+ EXPECT_FALSE(backend_->SetOnDemandFavicons(page_url, favicon_base::FAVICON,
+ icon_url, bitmaps));
EXPECT_EQ(original_favicon_id,
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
@@ -3048,7 +3044,8 @@ TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) {
base::RefCountedBytes::TakeVector(&data));
base::Time last_updated = base::Time::FromTimeT(0);
favicon_base::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
- icon_url, favicon_base::FAVICON, bitmap_data, last_updated, kSmallSize);
+ icon_url, favicon_base::FAVICON, bitmap_data, FaviconBitmapType::ON_VISIT,
+ last_updated, kSmallSize);
EXPECT_NE(0, icon_id);
EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
diff --git a/chromium/components/history/core/browser/history_match.cc b/chromium/components/history/core/browser/history_match.cc
deleted file mode 100644
index ae9787bc21d..00000000000
--- a/chromium/components/history/core/browser/history_match.cc
+++ /dev/null
@@ -1,39 +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/history/core/browser/history_match.h"
-
-#include "base/logging.h"
-
-namespace history {
-
-HistoryMatch::HistoryMatch()
- : url_info(),
- input_location(base::string16::npos),
- match_in_scheme(false),
- innermost_match(true) {
-}
-
-HistoryMatch::HistoryMatch(const URLRow& url_info,
- size_t input_location,
- bool match_in_scheme,
- bool innermost_match)
- : url_info(url_info),
- input_location(input_location),
- match_in_scheme(match_in_scheme),
- innermost_match(innermost_match) {
-}
-
-bool HistoryMatch::EqualsGURL(const HistoryMatch& h, const GURL& url) {
- return h.url_info.url() == url;
-}
-
-bool HistoryMatch::IsHostOnly() const {
- const GURL& gurl = url_info.url();
- DCHECK(gurl.is_valid());
- return (!gurl.has_path() || (gurl.path_piece() == "/")) &&
- !gurl.has_query() && !gurl.has_ref();
-}
-
-} // namespace history
diff --git a/chromium/components/history/core/browser/history_match.h b/chromium/components/history/core/browser/history_match.h
deleted file mode 100644
index 971db0316f7..00000000000
--- a/chromium/components/history/core/browser/history_match.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_HISTORY_CORE_BROWSER_HISTORY_MATCH_H_
-#define COMPONENTS_HISTORY_CORE_BROWSER_HISTORY_MATCH_H_
-
-#include <stddef.h>
-
-#include <deque>
-
-#include "components/history/core/browser/url_row.h"
-
-namespace history {
-
-// Used for intermediate history result operations.
-struct HistoryMatch {
- // Required for STL, we don't use this directly.
- HistoryMatch();
-
- HistoryMatch(const URLRow& url_info,
- size_t input_location,
- bool match_in_scheme,
- bool innermost_match);
-
- static bool EqualsGURL(const HistoryMatch& h, const GURL& url);
-
- // Returns true if url in this HistoryMatch is just a host
- // (e.g. "http://www.google.com/") and not some other subpage
- // (e.g. "http://www.google.com/foo.html").
- bool IsHostOnly() const;
-
- URLRow url_info;
-
- // The offset of the user's input within the URL.
- size_t input_location;
-
- // Whether this is a match in the scheme. This determines whether we'll go
- // ahead and show a scheme on the URL even if the user didn't type one.
- // If our best match was in the scheme, not showing the scheme is both
- // confusing and, for inline autocomplete of the fill_into_edit, dangerous.
- // (If the user types "h" and we match "http://foo/", we need to inline
- // autocomplete that, not "foo/", which won't show anything at all, and
- // will mislead the user into thinking the What You Typed match is what's
- // selected.)
- bool match_in_scheme;
-
- // A match after any scheme/"www.", if the user input could match at both
- // locations. If the user types "w", an innermost match ("website.com") is
- // better than a non-innermost match ("www.google.com"). If the user types
- // "x", no scheme in our prefix list (or "www.") begins with x, so all
- // matches are, vacuously, "innermost matches".
- bool innermost_match;
-};
-typedef std::deque<HistoryMatch> HistoryMatches;
-
-} // namespace history
-
-#endif // COMPONENTS_HISTORY_CORE_BROWSER_HISTORY_MATCH_H_
diff --git a/chromium/components/history/core/browser/history_model_worker.cc b/chromium/components/history/core/browser/history_model_worker.cc
index ad3f9911e7a..7ae6d603873 100644
--- a/chromium/components/history/core/browser/history_model_worker.cc
+++ b/chromium/components/history/core/browser/history_model_worker.cc
@@ -63,7 +63,7 @@ syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() {
return syncer::GROUP_HISTORY;
}
-bool HistoryModelWorker::IsOnModelThread() {
+bool HistoryModelWorker::IsOnModelSequence() {
// Ideally HistoryService would expose a way to check whether this is the
// history DB thread. Since it doesn't, just return true to bypass a CHECK in
// the sync code.
diff --git a/chromium/components/history/core/browser/history_model_worker.h b/chromium/components/history/core/browser/history_model_worker.h
index f72b00afbc5..9a669e4ec70 100644
--- a/chromium/components/history/core/browser/history_model_worker.h
+++ b/chromium/components/history/core/browser/history_model_worker.h
@@ -30,7 +30,7 @@ class HistoryModelWorker : public syncer::ModelSafeWorker {
// syncer::ModelSafeWorker implementation.
syncer::ModelSafeGroup GetModelSafeGroup() override;
- bool IsOnModelThread() override;
+ bool IsOnModelSequence() override;
private:
~HistoryModelWorker() override;
diff --git a/chromium/components/history/core/browser/history_service.cc b/chromium/components/history/core/browser/history_service.cc
index 788c90da089..6e8b789018b 100644
--- a/chromium/components/history/core/browser/history_service.cc
+++ b/chromium/components/history/core/browser/history_service.cc
@@ -78,9 +78,9 @@ void RunWithFaviconResult(
callback.Run(*bitmap_result);
}
-void RunWithQueryURLResult(const HistoryService::QueryURLCallback& callback,
+void RunWithQueryURLResult(HistoryService::QueryURLCallback callback,
const QueryURLResult* result) {
- callback.Run(result->success, result->row, result->visits);
+ std::move(callback).Run(result->success, result->row, result->visits);
}
void RunWithVisibleVisitCountToHostResult(
@@ -625,12 +625,11 @@ void HistoryService::SetFavicons(const GURL& page_url,
page_url, icon_type, icon_url, bitmaps));
}
-void HistoryService::SetLastResortFavicons(
- const GURL& page_url,
- favicon_base::IconType icon_type,
- const GURL& icon_url,
- const std::vector<SkBitmap>& bitmaps,
- base::Callback<void(bool)> callback) {
+void HistoryService::SetOnDemandFavicons(const GURL& page_url,
+ favicon_base::IconType icon_type,
+ const GURL& icon_url,
+ const std::vector<SkBitmap>& bitmaps,
+ base::Callback<void(bool)> callback) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(page_url))
@@ -638,7 +637,7 @@ void HistoryService::SetLastResortFavicons(
PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
- base::Bind(&HistoryBackend::SetLastResortFavicons, history_backend_,
+ base::Bind(&HistoryBackend::SetOnDemandFavicons, history_backend_,
page_url, icon_type, icon_url, bitmaps),
callback);
}
@@ -651,6 +650,14 @@ void HistoryService::SetFaviconsOutOfDateForPage(const GURL& page_url) {
history_backend_, page_url));
}
+void HistoryService::TouchOnDemandFavicon(const GURL& icon_url) {
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ScheduleTask(PRIORITY_NORMAL,
+ base::Bind(&HistoryBackend::TouchOnDemandFavicon,
+ history_backend_, icon_url));
+}
+
void HistoryService::SetImportedFavicons(
const favicon_base::FaviconUsageDataList& favicon_usage) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
@@ -663,7 +670,7 @@ void HistoryService::SetImportedFavicons(
base::CancelableTaskTracker::TaskId HistoryService::QueryURL(
const GURL& url,
bool want_visits,
- const QueryURLCallback& callback,
+ QueryURLCallback callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
@@ -672,8 +679,8 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryURL(
backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::QueryURL, history_backend_, url, want_visits,
base::Unretained(query_url_result)),
- base::Bind(&RunWithQueryURLResult, callback,
- base::Owned(query_url_result)));
+ base::BindOnce(&RunWithQueryURLResult, std::move(callback),
+ base::Owned(query_url_result)));
}
// Statistics ------------------------------------------------------------------
@@ -1069,8 +1076,33 @@ void HistoryService::ExpireLocalAndRemoteHistoryBetween(
//
// TODO(davidben): |callback| should not run until this operation completes
// too.
+ net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+ net::DefinePartialNetworkTrafficAnnotation(
+ "web_history_expire_between_dates", "web_history_service", R"(
+ semantics {
+ description:
+ "If a user who syncs their browsing history deletes history "
+ "items for a time range, Chrome sends a request to a google.com "
+ "host to execute the corresponding deletion serverside."
+ trigger:
+ "Deleting browsing history for a given time range, e.g. from the "
+ "Clear Browsing Data dialog, by an extension, or the "
+ "Clear-Site-Data header."
+ data:
+ "The begin and end timestamps of the selected time range, a "
+ "version info token to resolve transaction conflicts, and an "
+ "OAuth2 token authenticating the user."
+ }
+ policy {
+ chrome_policy {
+ AllowDeletingBrowserHistory {
+ AllowDeletingBrowserHistory: false
+ }
+ }
+ })");
web_history->ExpireHistoryBetween(restrict_urls, begin_time, end_time,
- base::Bind(&ExpireWebHistoryComplete));
+ base::Bind(&ExpireWebHistoryComplete),
+ partial_traffic_annotation);
}
ExpireHistoryBetween(restrict_urls, begin_time, end_time, callback, tracker);
}
diff --git a/chromium/components/history/core/browser/history_service.h b/chromium/components/history/core/browser/history_service.h
index b1aedcdf655..710ac708a86 100644
--- a/chromium/components/history/core/browser/history_service.h
+++ b/chromium/components/history/core/browser/history_service.h
@@ -245,10 +245,11 @@ class HistoryService : public syncer::SyncableService, public KeyedService {
// empty.
//
// If success is false, neither the row nor the vector will be valid.
- typedef base::Callback<void(
+ typedef base::OnceCallback<void(
bool, // Success flag, when false, nothing else is valid.
const URLRow&,
- const VisitVector&)> QueryURLCallback;
+ const VisitVector&)>
+ QueryURLCallback;
// Queries the basic information about the URL in the history database. If
// the caller is interested in the visits (each time the URL is visited),
@@ -257,7 +258,7 @@ class HistoryService : public syncer::SyncableService, public KeyedService {
base::CancelableTaskTracker::TaskId QueryURL(
const GURL& url,
bool want_visits,
- const QueryURLCallback& callback,
+ QueryURLCallback callback,
base::CancelableTaskTracker* tracker);
// Provides the result of a query. See QueryResults in history_types.h.
@@ -756,18 +757,37 @@ class HistoryService : public syncer::SyncableService, public KeyedService {
// 2) If |icon_url| is known to the database, |bitmaps| will be ignored (i.e.
// the icon won't be overwritten) but the mappings from |page_url| to
// |icon_url| will be stored (conditioned to point 1 above).
- // 3) If |icon_url| is stored, it will be marked as expired.
+ // 3) If |icon_url| is stored, it will be marked as "on-demand".
+ //
+ // On-demand favicons are those that are fetched without visiting their page.
+ // For this reason, their life-time cannot be bound to the life-time of the
+ // corresponding visit in history.
+ // - These bitmaps are evicted from the database based on the last time they
+ // get requested. The last requested time is initially set to Now() and is
+ // further updated by calling TouchOnDemandFavicon().
+ // - Furthermore, on-demand bitmaps are immediately marked as expired. Hence,
+ // they are always replaced by standard favicons whenever their page gets
+ // visited.
// The callback will receive whether the write actually happened.
- void SetLastResortFavicons(const GURL& page_url,
- favicon_base::IconType icon_type,
- const GURL& icon_url,
- const std::vector<SkBitmap>& bitmaps,
- base::Callback<void(bool)> callback);
+ void SetOnDemandFavicons(const GURL& page_url,
+ favicon_base::IconType icon_type,
+ const GURL& icon_url,
+ const std::vector<SkBitmap>& bitmaps,
+ base::Callback<void(bool)> callback);
// Used by the FaviconService to mark the favicon for the page as being out
// of date.
void SetFaviconsOutOfDateForPage(const GURL& page_url);
+ // Mark that the on-demand favicon at |icon_url| was requested now. This
+ // postpones the automatic eviction of the favicon from the database. Not all
+ // calls end up in a write into the DB:
+ // - it is no-op if the bitmaps are not stored using SetOnDemandFavicons();
+ // - the updates of the "last requested time" have limited frequency for each
+ // particular favicon (e.g. once per week). This limits the overhead of
+ // cache management for on-demand favicons.
+ void TouchOnDemandFavicon(const GURL& icon_url);
+
// Used by the FaviconService for importing many favicons for many pages at
// once. The pages must exist, any favicon sets for unknown pages will be
// discarded. Existing favicons will not be overwritten.
diff --git a/chromium/components/history/core/browser/history_types.cc b/chromium/components/history/core/browser/history_types.cc
index 93a242a2f0c..1624e636b2a 100644
--- a/chromium/components/history/core/browser/history_types.cc
+++ b/chromium/components/history/core/browser/history_types.cc
@@ -55,18 +55,18 @@ const size_t* QueryResults::MatchesForURL(const GURL& url,
}
void QueryResults::Swap(QueryResults* other) {
- std::swap(first_time_searched_, other->first_time_searched_);
std::swap(reached_beginning_, other->reached_beginning_);
results_.swap(other->results_);
url_to_results_.swap(other->url_to_results_);
}
-void QueryResults::AppendURLBySwapping(URLResult* result) {
- URLResult* new_result = new URLResult;
- new_result->SwapResult(result);
+void QueryResults::SetURLResults(std::vector<URLResult>&& results) {
+ results_ = std::move(results);
- results_.push_back(new_result);
- AddURLUsageAtIndex(new_result->url(), results_.size() - 1);
+ // Recreate the map for the results_ has been replaced.
+ url_to_results_.clear();
+ for(size_t i = 0; i < results_.size(); ++i)
+ AddURLUsageAtIndex(results_[i].url(), i);
}
void QueryResults::DeleteURL(const GURL& url) {
@@ -83,7 +83,7 @@ void QueryResults::DeleteRange(size_t begin, size_t end) {
// were modified. We will delete references to these later.
std::set<GURL> urls_modified;
for (size_t i = begin; i <= end; i++) {
- urls_modified.insert(results_[i]->url());
+ urls_modified.insert(results_[i].url());
}
// Now just delete that range in the vector en masse (the STL ending is
diff --git a/chromium/components/history/core/browser/history_types.h b/chromium/components/history/core/browser/history_types.h
index 9189b068c5a..17b97b4a038 100644
--- a/chromium/components/history/core/browser/history_types.h
+++ b/chromium/components/history/core/browser/history_types.h
@@ -18,7 +18,6 @@
#include "base/containers/stack_container.h"
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
-#include "base/memory/scoped_vector.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "components/favicon_base/favicon_types.h"
@@ -134,38 +133,22 @@ struct PageVisit {
// a given URL appears in those results.
class QueryResults {
public:
- typedef std::vector<URLResult*> URLResultVector;
+ typedef std::vector<URLResult> URLResultVector;
QueryResults();
~QueryResults();
- // Indicates the first time that the query includes results for (queries are
- // clipped at the beginning, so it will always include to the end of the time
- // queried).
- //
- // If the number of results was clipped as a result of the max count, this
- // will be the time of the first query returned. If there were fewer results
- // than we were allowed to return, this represents the first date considered
- // in the query (this will be before the first result if there was time
- // queried with no results).
- //
- // TODO(brettw): bug 1203054: This field is not currently set properly! Do
- // not use until the bug is fixed.
- base::Time first_time_searched() const { return first_time_searched_; }
- void set_first_time_searched(base::Time t) { first_time_searched_ = t; }
- // Note: If you need end_time_searched, it can be added.
-
void set_reached_beginning(bool reached) { reached_beginning_ = reached; }
bool reached_beginning() { return reached_beginning_; }
size_t size() const { return results_.size(); }
bool empty() const { return results_.empty(); }
- URLResult& back() { return *results_.back(); }
- const URLResult& back() const { return *results_.back(); }
+ URLResult& back() { return results_.back(); }
+ const URLResult& back() const { return results_.back(); }
- URLResult& operator[](size_t i) { return *results_[i]; }
- const URLResult& operator[](size_t i) const { return *results_[i]; }
+ URLResult& operator[](size_t i) { return results_[i]; }
+ const URLResult& operator[](size_t i) const { return results_[i]; }
URLResultVector::const_iterator begin() const { return results_.begin(); }
URLResultVector::const_iterator end() const { return results_.end(); }
@@ -189,10 +172,9 @@ class QueryResults {
// efficiently transferred without copying.
void Swap(QueryResults* other);
- // Adds the given result to the map, using swap() on the members to avoid
- // copying (there are a lot of strings and vectors). This means the parameter
- // object will be cleared after this call.
- void AppendURLBySwapping(URLResult* result);
+ // Set the result vector, the parameter vector will be moved to results_.
+ // It means the parameter vector will be empty after calling this method.
+ void SetURLResults(std::vector<URLResult>&& results);
// Removes all instances of the given URL from the result set.
void DeleteURL(const GURL& url);
@@ -215,14 +197,12 @@ class QueryResults {
// (this is inclusive). This is used when inserting or deleting.
void AdjustResultMap(size_t begin, size_t end, ptrdiff_t delta);
- base::Time first_time_searched_;
-
// Whether the query reaches the beginning of the database.
bool reached_beginning_;
// The ordered list of results. The pointers inside this are owned by this
// QueryResults object.
- ScopedVector<URLResult> results_;
+ URLResultVector results_;
// Maps URLs to entries in results_.
URLToResultIndices url_to_results_;
@@ -526,6 +506,24 @@ struct FaviconBitmapIDSize {
gfx::Size pixel_size;
};
+enum FaviconBitmapType {
+ // The bitmap gets downloaded while visiting its page. Their life-time is
+ // bound to the life-time of the corresponding visit in history.
+ // - These bitmaps are re-downloaded when visiting the page again and the
+ // last_updated timestamp is old enough.
+ ON_VISIT,
+
+ // The bitmap gets downloaded because it is demanded by some Chrome UI (while
+ // not visiting its page). For this reason, their life-time cannot be bound to
+ // the life-time of the corresponding visit in history.
+ // - These bitmaps are evicted from the database based on the last time they
+ // were requested.
+ // - Furthermore, on-demand bitmaps are immediately marked as expired. Hence,
+ // they are always replaced by ON_VISIT favicons whenever their page gets
+ // visited.
+ ON_DEMAND
+};
+
// Defines a favicon bitmap stored in the history backend.
struct FaviconBitmap {
FaviconBitmap();
diff --git a/chromium/components/history/core/browser/history_types_unittest.cc b/chromium/components/history/core/browser/history_types_unittest.cc
index 8641baa3304..09dd46cefb3 100644
--- a/chromium/components/history/core/browser/history_types_unittest.cc
+++ b/chromium/components/history/core/browser/history_types_unittest.cc
@@ -40,14 +40,14 @@ const char kURL2[] = "http://news.google.com/";
void AddSimpleData(QueryResults* results) {
GURL url1(kURL1);
GURL url2(kURL2);
- URLResult result1(url1, base::Time::Now());
- URLResult result2(url1, base::Time::Now());
- URLResult result3(url2, base::Time::Now());
+ std::vector<URLResult> test_vector;
+
+ test_vector.push_back(URLResult(url1, base::Time::Now()));
+ test_vector.push_back(URLResult(url1, base::Time::Now()));
+ test_vector.push_back(URLResult(url2, base::Time::Now()));
// The URLResults are invalid after being inserted.
- results->AppendURLBySwapping(&result1);
- results->AppendURLBySwapping(&result2);
- results->AppendURLBySwapping(&result3);
+ results->SetURLResults(std::move(test_vector));
CheckHistoryResultConsistency(*results);
}
diff --git a/chromium/components/history/core/browser/thumbnail_database.cc b/chromium/components/history/core/browser/thumbnail_database.cc
index 60445eb48f0..c8b430f4f71 100644
--- a/chromium/components/history/core/browser/thumbnail_database.cc
+++ b/chromium/components/history/core/browser/thumbnail_database.cc
@@ -64,13 +64,17 @@ namespace history {
// icon_id The ID of the favicon that the bitmap is associated to.
// last_updated The time at which this favicon was inserted into the
// table. This is used to determine if it needs to be
-// redownloaded from the web.
+// redownloaded from the web. Value 0 denotes that the bitmap
+// has been explicitly expired.
// image_data PNG encoded data of the favicon.
// width Pixel width of |image_data|.
// height Pixel height of |image_data|.
-// last_requested The time at which this bitmap was last requested. This is
-// used to determine the priority with which the bitmap
-// should be retained on cleanup.
+// last_requested The time at which this bitmap was last requested. This
+// entry is non-zero iff the bitmap is of type ON_DEMAND.
+// This info is used for clearing old ON_DEMAND bitmaps.
+// (On-demand bitmaps cannot get cleared along with expired
+// visits in history DB because there is no corresponding
+// visit.)
namespace {
@@ -503,12 +507,16 @@ bool ThumbnailDatabase::GetFaviconBitmap(
FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap(
favicon_base::FaviconID icon_id,
const scoped_refptr<base::RefCountedMemory>& icon_data,
+ FaviconBitmapType type,
base::Time time,
const gfx::Size& pixel_size) {
DCHECK(icon_id);
- sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
- "INSERT INTO favicon_bitmaps (icon_id, image_data, last_updated, width, "
- "height) VALUES (?, ?, ?, ?, ?)"));
+
+ sql::Statement statement(db_.GetCachedStatement(
+ SQL_FROM_HERE,
+ "INSERT INTO favicon_bitmaps (icon_id, image_data, last_updated, "
+ "last_requested, width, height) VALUES (?, ?, ?, ?, ?, ?)"));
+
statement.BindInt64(0, icon_id);
if (icon_data.get() && icon_data->size()) {
statement.BindBlob(1, icon_data->front(),
@@ -516,9 +524,19 @@ FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap(
} else {
statement.BindNull(1);
}
- statement.BindInt64(2, time.ToInternalValue());
- statement.BindInt(3, pixel_size.width());
- statement.BindInt(4, pixel_size.height());
+
+ // On-visit bitmaps:
+ // - keep track of last_updated: last write time is used for expiration;
+ // - always have last_requested==0: no need to keep track of last read time.
+ statement.BindInt64(2, type == ON_VISIT ? time.ToInternalValue() : 0);
+ // On-demand bitmaps:
+ // - always have last_updated==0: last write time is not stored as they are
+ // always expired and thus ready to be replaced by ON_VISIT icons;
+ // - keep track of last_requested: last read time is used for cache eviction.
+ statement.BindInt64(3, type == ON_DEMAND ? time.ToInternalValue() : 0);
+
+ statement.BindInt(4, pixel_size.width());
+ statement.BindInt(5, pixel_size.height());
if (!statement.Run())
return 0;
@@ -530,8 +548,13 @@ bool ThumbnailDatabase::SetFaviconBitmap(
scoped_refptr<base::RefCountedMemory> bitmap_data,
base::Time time) {
DCHECK(bitmap_id);
- sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
- "UPDATE favicon_bitmaps SET image_data=?, last_updated=? WHERE id=?"));
+ // By updating last_updated timestamp, we assume the icon is of type ON_VISIT.
+ // If it is ON_DEMAND, reset last_requested to 0 and thus silently change the
+ // type to ON_VISIT.
+ sql::Statement statement(
+ db_.GetCachedStatement(SQL_FROM_HERE,
+ "UPDATE favicon_bitmaps SET image_data=?, "
+ "last_updated=?, last_requested=? WHERE id=?"));
if (bitmap_data.get() && bitmap_data->size()) {
statement.BindBlob(0, bitmap_data->front(),
static_cast<int>(bitmap_data->size()));
@@ -539,7 +562,8 @@ bool ThumbnailDatabase::SetFaviconBitmap(
statement.BindNull(0);
}
statement.BindInt64(1, time.ToInternalValue());
- statement.BindInt64(2, bitmap_id);
+ statement.BindInt64(2, 0);
+ statement.BindInt64(3, bitmap_id);
return statement.Run();
}
@@ -548,22 +572,47 @@ bool ThumbnailDatabase::SetFaviconBitmapLastUpdateTime(
FaviconBitmapID bitmap_id,
base::Time time) {
DCHECK(bitmap_id);
- sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
- "UPDATE favicon_bitmaps SET last_updated=? WHERE id=?"));
+ // By updating last_updated timestamp, we assume the icon is of type ON_VISIT.
+ // If it is ON_DEMAND, reset last_requested to 0 and thus silently change the
+ // type to ON_VISIT.
+ sql::Statement statement(
+ db_.GetCachedStatement(SQL_FROM_HERE,
+ "UPDATE favicon_bitmaps SET last_updated=?, "
+ "last_requested=? WHERE id=?"));
statement.BindInt64(0, time.ToInternalValue());
- statement.BindInt64(1, bitmap_id);
+ statement.BindInt64(1, 0);
+ statement.BindInt64(2, bitmap_id);
return statement.Run();
}
-bool ThumbnailDatabase::SetFaviconBitmapLastRequestedTime(
- FaviconBitmapID bitmap_id,
- base::Time time) {
- DCHECK(bitmap_id);
- sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
- "UPDATE favicon_bitmaps SET last_requested=? WHERE id=?"));
- statement.BindInt64(0, time.ToInternalValue());
- statement.BindInt64(1, bitmap_id);
- return statement.Run();
+bool ThumbnailDatabase::TouchOnDemandFavicon(const GURL& icon_url,
+ base::Time time) {
+ // Look up the icon ids for the url.
+ sql::Statement id_statement(db_.GetCachedStatement(
+ SQL_FROM_HERE, "SELECT id FROM favicons WHERE url=?"));
+ id_statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
+
+ base::Time max_time =
+ time - base::TimeDelta::FromDays(kFaviconUpdateLastRequestedAfterDays);
+
+ while (id_statement.Step()) {
+ favicon_base::FaviconID icon_id = id_statement.ColumnInt64(0);
+
+ // Update the time only for ON_DEMAND bitmaps (i.e. with last_requested >
+ // 0). For performance reasons, update the time only if the currently stored
+ // time is old enough (UPDATEs where the WHERE condition does not match any
+ // entries are way faster than UPDATEs that really change some data).
+ sql::Statement statement(db_.GetCachedStatement(
+ SQL_FROM_HERE,
+ "UPDATE favicon_bitmaps SET last_requested=? WHERE icon_id=? AND "
+ "last_requested>0 AND last_requested<=?"));
+ statement.BindInt64(0, time.ToInternalValue());
+ statement.BindInt64(1, icon_id);
+ statement.BindInt64(2, max_time.ToInternalValue());
+ if (!statement.Run())
+ return false;
+ }
+ return true;
}
bool ThumbnailDatabase::DeleteFaviconBitmap(FaviconBitmapID bitmap_id) {
@@ -634,10 +683,11 @@ favicon_base::FaviconID ThumbnailDatabase::AddFavicon(
const GURL& icon_url,
favicon_base::IconType icon_type,
const scoped_refptr<base::RefCountedMemory>& icon_data,
+ FaviconBitmapType type,
base::Time time,
const gfx::Size& pixel_size) {
favicon_base::FaviconID icon_id = AddFavicon(icon_url, icon_type);
- if (!icon_id || !AddFaviconBitmap(icon_id, icon_data, time, pixel_size))
+ if (!icon_id || !AddFaviconBitmap(icon_id, icon_data, type, time, pixel_size))
return 0;
return icon_id;
diff --git a/chromium/components/history/core/browser/thumbnail_database.h b/chromium/components/history/core/browser/thumbnail_database.h
index ec21369d438..697faf3abec 100644
--- a/chromium/components/history/core/browser/thumbnail_database.h
+++ b/chromium/components/history/core/browser/thumbnail_database.h
@@ -26,6 +26,10 @@ namespace history {
class HistoryBackendClient;
+// The minimum number of days after which last_requested field gets updated.
+// All earlier updates are ignored.
+static const int kFaviconUpdateLastRequestedAfterDays = 14;
+
// This database interface is owned by the history backend and runs on the
// history thread. It is a totally separate component from history partially
// because we may want to move it to its own thread in the future. The
@@ -87,37 +91,36 @@ class ThumbnailDatabase {
scoped_refptr<base::RefCountedMemory>* png_icon_data,
gfx::Size* pixel_size);
- // Adds a bitmap component at |pixel_size| for the favicon with |icon_id|.
- // Only favicons representing a .ico file should have multiple favicon bitmaps
- // per favicon.
+ // Adds a bitmap component of |type| at |pixel_size| for the favicon with
+ // |icon_id|. Only favicons representing a .ico file should have multiple
+ // favicon bitmaps per favicon.
// |icon_data| is the png encoded data.
- // The |time| indicates the access time, and is used to detect when the
- // favicon should be refreshed.
+ // The |type| indicates how the lifetime of this icon should be managed.
+ // The |time| is used for lifetime management of the bitmap (should be Now()).
// |pixel_size| is the pixel dimensions of |icon_data|.
// Returns the id of the added bitmap or 0 if unsuccessful.
FaviconBitmapID AddFaviconBitmap(
favicon_base::FaviconID icon_id,
const scoped_refptr<base::RefCountedMemory>& icon_data,
+ FaviconBitmapType type,
base::Time time,
const gfx::Size& pixel_size);
// Sets the bitmap data and the last updated time for the favicon bitmap at
- // |bitmap_id|.
+ // |bitmap_id|. Should not be called for bitmaps of type ON_DEMAND as they
+ // should never get updated (the call silently changes the type to ON_VISIT).
// Returns true if successful.
bool SetFaviconBitmap(FaviconBitmapID bitmap_id,
scoped_refptr<base::RefCountedMemory> bitmap_data,
base::Time time);
- // Sets the last updated time for the favicon bitmap at |bitmap_id|.
- // Returns true if successful.
+ // Sets the last_updated time for the favicon bitmap at |bitmap_id|. Should
+ // not be called for bitmaps of type ON_DEMAND as last_updated time is only
+ // tracked for ON_VISIT bitmaps (the call silently changes the type to
+ // ON_VISIT). Returns true if successful.
bool SetFaviconBitmapLastUpdateTime(FaviconBitmapID bitmap_id,
base::Time time);
- // Sets the last requested time for the favicon bitmap at |bitmap_id|.
- // Returns true if successful.
- bool SetFaviconBitmapLastRequestedTime(FaviconBitmapID bitmap_id,
- base::Time time);
-
// Deletes the favicon bitmap with |bitmap_id|.
// Returns true if successful.
bool DeleteFaviconBitmap(FaviconBitmapID bitmap_id);
@@ -128,6 +131,16 @@ class ThumbnailDatabase {
// of the bitmaps for |icon_id| to be out of date.
bool SetFaviconOutOfDate(favicon_base::FaviconID icon_id);
+ // Mark all bitmaps of type ON_DEMAND at |icon_url| as requested at |time|.
+ // This postpones their automatic eviction from the database. Not all calls
+ // end up in a write into the DB:
+ // - it is no-op if the bitmaps are not of type ON_DEMAND;
+ // - the updates of the "last requested time" have limited frequency for each
+ // particular bitmap (e.g. once per week). This limits the overhead of
+ // cache management for on-demand favicons.
+ // Returns true if successful.
+ bool TouchOnDemandFavicon(const GURL& icon_url, base::Time time);
+
// Returns the id of the entry in the favicon database with the specified url
// and icon type.
// Returns 0 if no entry exists for the specified url.
@@ -146,11 +159,12 @@ class ThumbnailDatabase {
favicon_base::IconType icon_type);
// Adds a favicon with a single bitmap. This call is equivalent to calling
- // AddFavicon and AddFaviconBitmap.
+ // AddFavicon and AddFaviconBitmap of type |type|.
favicon_base::FaviconID AddFavicon(
const GURL& icon_url,
favicon_base::IconType icon_type,
const scoped_refptr<base::RefCountedMemory>& icon_data,
+ FaviconBitmapType type,
base::Time time,
const gfx::Size& pixel_size);
diff --git a/chromium/components/history/core/browser/thumbnail_database_unittest.cc b/chromium/components/history/core/browser/thumbnail_database_unittest.cc
index 0ece892cfe4..4dbd0c6f64e 100644
--- a/chromium/components/history/core/browser/thumbnail_database_unittest.cc
+++ b/chromium/components/history/core/browser/thumbnail_database_unittest.cc
@@ -85,7 +85,8 @@ void AddAndMapFaviconSimple(ThumbnailDatabase* db,
scoped_refptr<base::RefCountedStaticMemory> data(
new base::RefCountedStaticMemory(kBlob1, sizeof(kBlob1)));
favicon_base::FaviconID favicon_id =
- db->AddFavicon(icon_url, icon_type, data, base::Time::Now(), gfx::Size());
+ db->AddFavicon(icon_url, icon_type, data, FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), gfx::Size());
db->AddIconMapping(page_url, favicon_id);
}
@@ -213,7 +214,8 @@ TEST_F(ThumbnailDatabaseTest, AddIconMapping) {
GURL url("http://google.com");
base::Time time = base::Time::Now();
favicon_base::FaviconID id =
- db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, time, gfx::Size());
+ db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon,
+ FaviconBitmapType::ON_VISIT, time, gfx::Size());
EXPECT_NE(0, id);
EXPECT_NE(0, db.AddIconMapping(url, id));
@@ -224,37 +226,145 @@ TEST_F(ThumbnailDatabaseTest, AddIconMapping) {
EXPECT_EQ(id, icon_mappings.front().icon_id);
}
-TEST_F(ThumbnailDatabaseTest, LastRequestedTime) {
- ThumbnailDatabase db(NULL);
+TEST_F(ThumbnailDatabaseTest,
+ AddOnDemandFaviconBitmapCreatesCorrectTimestamps) {
+ ThumbnailDatabase db(nullptr);
ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
db.BeginTransaction();
+ base::Time add_time;
+ ASSERT_TRUE(
+ base::Time::FromUTCExploded({2017, 5, 0, 1, 0, 0, 0, 0}, &add_time));
std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
GURL url("http://google.com");
- base::Time now = base::Time::Now();
- favicon_base::FaviconID id =
- db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, now, gfx::Size());
- ASSERT_NE(0, id);
-
- // Fetching the last requested time of a non-existent bitmap should fail.
- base::Time last_requested = base::Time::UnixEpoch();
- EXPECT_FALSE(db.GetFaviconBitmap(id + 1, NULL, &last_requested, NULL, NULL));
- EXPECT_EQ(last_requested, base::Time::UnixEpoch()); // Remains unchanged.
-
- // Fetching the last requested time of a bitmap that has no last request
- // should return a null timestamp.
- last_requested = base::Time::UnixEpoch();
- EXPECT_TRUE(db.GetFaviconBitmap(id, NULL, &last_requested, NULL, NULL));
- EXPECT_TRUE(last_requested.is_null());
-
- // Setting the last requested time of an existing bitmap should succeed, and
- // the set time should be returned by the corresponding "Get".
- last_requested = base::Time::UnixEpoch();
- EXPECT_TRUE(db.SetFaviconBitmapLastRequestedTime(id, now));
- EXPECT_TRUE(db.GetFaviconBitmap(id, NULL, &last_requested, NULL, NULL));
- EXPECT_EQ(last_requested, now);
+ favicon_base::FaviconID icon = db.AddFavicon(url, favicon_base::FAVICON);
+ ASSERT_NE(0, icon);
+ FaviconBitmapID bitmap = db.AddFaviconBitmap(
+ icon, favicon, FaviconBitmapType::ON_DEMAND, add_time, gfx::Size());
+ ASSERT_NE(0, bitmap);
+
+ base::Time last_updated;
+ base::Time last_requested;
+ ASSERT_TRUE(db.GetFaviconBitmap(bitmap, &last_updated, &last_requested,
+ nullptr, nullptr));
+ EXPECT_EQ(base::Time(), last_updated);
+ EXPECT_EQ(add_time, last_requested);
+}
+
+TEST_F(ThumbnailDatabaseTest, AddFaviconBitmapCreatesCorrectTimestamps) {
+ ThumbnailDatabase db(nullptr);
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
+ db.BeginTransaction();
+
+ base::Time add_time;
+ ASSERT_TRUE(
+ base::Time::FromUTCExploded({2017, 5, 0, 1, 0, 0, 0, 0}, &add_time));
+ std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
+ scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
+
+ GURL url("http://google.com");
+ favicon_base::FaviconID icon = db.AddFavicon(url, favicon_base::FAVICON);
+ ASSERT_NE(0, icon);
+ FaviconBitmapID bitmap = db.AddFaviconBitmap(
+ icon, favicon, FaviconBitmapType::ON_VISIT, add_time, gfx::Size());
+ ASSERT_NE(0, bitmap);
+
+ base::Time last_updated;
+ base::Time last_requested;
+ ASSERT_TRUE(db.GetFaviconBitmap(bitmap, &last_updated, &last_requested,
+ nullptr, nullptr));
+ EXPECT_EQ(add_time, last_updated);
+ EXPECT_EQ(base::Time(), last_requested);
+}
+
+TEST_F(ThumbnailDatabaseTest, TouchUpdatesOnDemandFavicons) {
+ ThumbnailDatabase db(nullptr);
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
+ db.BeginTransaction();
+
+ base::Time start;
+ ASSERT_TRUE(base::Time::FromUTCExploded({2017, 5, 0, 1, 0, 0, 0, 0}, &start));
+ std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
+ scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
+
+ // Create an on-demand favicon.
+ GURL url("http://google.com");
+ favicon_base::FaviconID icon = db.AddFavicon(url, favicon_base::FAVICON);
+ ASSERT_NE(0, icon);
+ FaviconBitmapID bitmap = db.AddFaviconBitmap(
+ icon, favicon, FaviconBitmapType::ON_DEMAND, start, gfx::Size());
+ ASSERT_NE(0, bitmap);
+
+ base::Time end =
+ start + base::TimeDelta::FromDays(kFaviconUpdateLastRequestedAfterDays);
+ EXPECT_TRUE(db.TouchOnDemandFavicon(url, end));
+
+ base::Time last_updated;
+ base::Time last_requested;
+ EXPECT_TRUE(db.GetFaviconBitmap(bitmap, &last_updated, &last_requested,
+ nullptr, nullptr));
+ // Does not mess with the last_updated field.
+ EXPECT_EQ(base::Time(), last_updated);
+ EXPECT_EQ(end, last_requested); // Updates the last_requested field.
+}
+
+TEST_F(ThumbnailDatabaseTest, TouchUpdatesOnlyInfrequently) {
+ ThumbnailDatabase db(nullptr);
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
+ db.BeginTransaction();
+
+ base::Time start;
+ ASSERT_TRUE(base::Time::FromUTCExploded({2017, 5, 0, 1, 0, 0, 0, 0}, &start));
+ std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
+ scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
+
+ // Create an on-demand favicon.
+ GURL url("http://google.com");
+ favicon_base::FaviconID icon = db.AddFavicon(url, favicon_base::FAVICON);
+ ASSERT_NE(0, icon);
+ FaviconBitmapID bitmap = db.AddFaviconBitmap(
+ icon, favicon, FaviconBitmapType::ON_DEMAND, start, gfx::Size());
+ ASSERT_NE(0, bitmap);
+
+ base::Time end = start + base::TimeDelta::FromMinutes(1);
+ EXPECT_TRUE(db.TouchOnDemandFavicon(url, end));
+
+ base::Time last_requested;
+ EXPECT_TRUE(
+ db.GetFaviconBitmap(bitmap, nullptr, &last_requested, nullptr, nullptr));
+ EXPECT_EQ(start, last_requested); // No update.
+}
+
+TEST_F(ThumbnailDatabaseTest, TouchDoesNotUpdateStandardFavicons) {
+ ThumbnailDatabase db(nullptr);
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
+ db.BeginTransaction();
+
+ base::Time start;
+ ASSERT_TRUE(base::Time::FromUTCExploded({2017, 5, 0, 1, 0, 0, 0, 0}, &start));
+ std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
+ scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
+
+ // Create a standard favicon.
+ GURL url("http://google.com");
+ favicon_base::FaviconID icon = db.AddFavicon(url, favicon_base::FAVICON);
+ EXPECT_NE(0, icon);
+ FaviconBitmapID bitmap = db.AddFaviconBitmap(
+ icon, favicon, FaviconBitmapType::ON_VISIT, start, gfx::Size());
+ EXPECT_NE(0, bitmap);
+
+ base::Time end =
+ start + base::TimeDelta::FromDays(kFaviconUpdateLastRequestedAfterDays);
+ db.TouchOnDemandFavicon(url, end);
+
+ base::Time last_updated;
+ base::Time last_requested;
+ EXPECT_TRUE(db.GetFaviconBitmap(bitmap, &last_updated, &last_requested,
+ nullptr, nullptr));
+ EXPECT_EQ(start, last_updated); // Does not mess with last_updated.
+ EXPECT_EQ(base::Time(), last_requested); // No update.
}
TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
@@ -268,7 +378,8 @@ TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
GURL url("http://google.com");
favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::TOUCH_ICON);
base::Time time = base::Time::Now();
- db.AddFaviconBitmap(id, favicon, time, gfx::Size());
+ db.AddFaviconBitmap(id, favicon, FaviconBitmapType::ON_VISIT, time,
+ gfx::Size());
EXPECT_LT(0, db.AddIconMapping(url, id));
favicon_base::FaviconID id2 = db.AddFavicon(url, favicon_base::FAVICON);
@@ -299,13 +410,16 @@ TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) {
favicon_base::FaviconID id1 = db.AddFavicon(url, favicon_base::TOUCH_ICON);
base::Time time = base::Time::Now();
- db.AddFaviconBitmap(id1, favicon, time, kSmallSize);
- db.AddFaviconBitmap(id1, favicon, time, kLargeSize);
+ db.AddFaviconBitmap(id1, favicon, FaviconBitmapType::ON_VISIT, time,
+ kSmallSize);
+ db.AddFaviconBitmap(id1, favicon, FaviconBitmapType::ON_VISIT, time,
+ kLargeSize);
EXPECT_LT(0, db.AddIconMapping(url, id1));
favicon_base::FaviconID id2 = db.AddFavicon(url, favicon_base::FAVICON);
EXPECT_NE(id1, id2);
- db.AddFaviconBitmap(id2, favicon, time, kSmallSize);
+ db.AddFaviconBitmap(id2, favicon, FaviconBitmapType::ON_VISIT, time,
+ kSmallSize);
EXPECT_LT(0, db.AddIconMapping(url, id2));
std::vector<IconMapping> icon_mappings;
@@ -341,19 +455,22 @@ TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrls) {
favicon_base::FaviconID kept_id1 =
db.AddFavicon(kIconUrl1, favicon_base::FAVICON);
- db.AddFaviconBitmap(kept_id1, favicon1, base::Time::Now(), kLargeSize);
+ db.AddFaviconBitmap(kept_id1, favicon1, FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), kLargeSize);
db.AddIconMapping(kPageUrl1, kept_id1);
db.AddIconMapping(kPageUrl3, kept_id1);
db.AddIconMapping(kPageUrl4, kept_id1);
favicon_base::FaviconID unkept_id =
db.AddFavicon(kIconUrl2, favicon_base::FAVICON);
- db.AddFaviconBitmap(unkept_id, favicon1, base::Time::Now(), kLargeSize);
+ db.AddFaviconBitmap(unkept_id, favicon1, FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), kLargeSize);
db.AddIconMapping(kPageUrl2, unkept_id);
favicon_base::FaviconID kept_id2 =
db.AddFavicon(kIconUrl5, favicon_base::FAVICON);
- db.AddFaviconBitmap(kept_id2, favicon2, base::Time::Now(), kLargeSize);
+ db.AddFaviconBitmap(kept_id2, favicon2, FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), kLargeSize);
db.AddIconMapping(kPageUrl5, kept_id2);
// RetainDataForPageUrls() uses schema manipulations for efficiency.
@@ -406,8 +523,8 @@ TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrlsExpiresRetainedFavicons) {
scoped_refptr<base::RefCountedStaticMemory> favicon1(
new base::RefCountedStaticMemory(kBlob1, sizeof(kBlob1)));
favicon_base::FaviconID kept_id = db.AddFavicon(
- kIconUrl1, favicon_base::FAVICON, favicon1, base::Time::Now(),
- gfx::Size());
+ kIconUrl1, favicon_base::FAVICON, favicon1, FaviconBitmapType::ON_VISIT,
+ base::Time::Now(), gfx::Size());
db.AddIconMapping(kPageUrl1, kept_id);
EXPECT_TRUE(db.RetainDataForPageUrls(std::vector<GURL>(1u, kPageUrl1)));
@@ -439,8 +556,10 @@ TEST_F(ThumbnailDatabaseTest, DeleteFavicon) {
GURL url("http://google.com");
favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::FAVICON);
base::Time last_updated = base::Time::Now();
- db.AddFaviconBitmap(id, favicon1, last_updated, kSmallSize);
- db.AddFaviconBitmap(id, favicon2, last_updated, kLargeSize);
+ db.AddFaviconBitmap(id, favicon1, FaviconBitmapType::ON_VISIT, last_updated,
+ kSmallSize);
+ db.AddFaviconBitmap(id, favicon2, FaviconBitmapType::ON_VISIT, last_updated,
+ kLargeSize);
EXPECT_TRUE(db.GetFaviconBitmaps(id, NULL));
@@ -461,8 +580,9 @@ TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
GURL icon_url("http://google.com/favicon.ico");
base::Time time = base::Time::Now();
- favicon_base::FaviconID id = db.AddFavicon(
- icon_url, favicon_base::FAVICON, favicon, time, gfx::Size());
+ favicon_base::FaviconID id =
+ db.AddFavicon(icon_url, favicon_base::FAVICON, favicon,
+ FaviconBitmapType::ON_VISIT, time, gfx::Size());
EXPECT_NE(0, db.AddIconMapping(page_url, id));
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
@@ -477,8 +597,9 @@ TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
scoped_refptr<base::RefCountedBytes> favicon2 =
new base::RefCountedBytes(data);
- favicon_base::FaviconID id2 = db.AddFavicon(
- icon_url, favicon_base::TOUCH_ICON, favicon2, time, gfx::Size());
+ favicon_base::FaviconID id2 =
+ db.AddFavicon(icon_url, favicon_base::TOUCH_ICON, favicon2,
+ FaviconBitmapType::ON_VISIT, time, gfx::Size());
EXPECT_NE(0, db.AddIconMapping(page_url, id2));
icon_mappings.clear();
@@ -494,11 +615,8 @@ TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
new base::RefCountedBytes(data2);
favicon_base::FaviconID id3 =
- db.AddFavicon(icon_url,
- favicon_base::TOUCH_PRECOMPOSED_ICON,
- favicon3,
- time,
- gfx::Size());
+ db.AddFavicon(icon_url, favicon_base::TOUCH_PRECOMPOSED_ICON, favicon3,
+ FaviconBitmapType::ON_VISIT, time, gfx::Size());
EXPECT_NE(0, db.AddIconMapping(page_url, id3));
icon_mappings.clear();
@@ -548,31 +666,23 @@ TEST_F(ThumbnailDatabaseTest, HasMappingFor) {
// Add a favicon which will have icon_mappings
base::Time time = base::Time::Now();
- favicon_base::FaviconID id1 = db.AddFavicon(GURL("http://google.com"),
- favicon_base::FAVICON,
- favicon,
- time,
- gfx::Size());
+ favicon_base::FaviconID id1 =
+ db.AddFavicon(GURL("http://google.com"), favicon_base::FAVICON, favicon,
+ FaviconBitmapType::ON_VISIT, time, gfx::Size());
EXPECT_NE(id1, 0);
// Add another type of favicon
time = base::Time::Now();
- favicon_base::FaviconID id2 =
- db.AddFavicon(GURL("http://www.google.com/icon"),
- favicon_base::TOUCH_ICON,
- favicon,
- time,
- gfx::Size());
+ favicon_base::FaviconID id2 = db.AddFavicon(
+ GURL("http://www.google.com/icon"), favicon_base::TOUCH_ICON, favicon,
+ FaviconBitmapType::ON_VISIT, time, gfx::Size());
EXPECT_NE(id2, 0);
// Add 3rd favicon
time = base::Time::Now();
- favicon_base::FaviconID id3 =
- db.AddFavicon(GURL("http://www.google.com/icon"),
- favicon_base::TOUCH_ICON,
- favicon,
- time,
- gfx::Size());
+ favicon_base::FaviconID id3 = db.AddFavicon(
+ GURL("http://www.google.com/icon"), favicon_base::TOUCH_ICON, favicon,
+ FaviconBitmapType::ON_VISIT, time, gfx::Size());
EXPECT_NE(id3, 0);
// Add 2 icon mapping
diff --git a/chromium/components/history/core/browser/top_sites_backend.cc b/chromium/components/history/core/browser/top_sites_backend.cc
index 7124e827998..f343d5b3e3a 100644
--- a/chromium/components/history/core/browser/top_sites_backend.cc
+++ b/chromium/components/history/core/browser/top_sites_backend.cc
@@ -14,6 +14,8 @@
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/task/cancelable_task_tracker.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "components/history/core/browser/top_sites_database.h"
@@ -21,9 +23,10 @@
namespace history {
-TopSitesBackend::TopSitesBackend(
- const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner)
- : db_(new TopSitesDatabase()), db_task_runner_(db_task_runner) {
+TopSitesBackend::TopSitesBackend()
+ : db_(new TopSitesDatabase()),
+ db_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::TaskPriority::USER_VISIBLE, base::MayBlock()})) {
DCHECK(db_task_runner_);
}
@@ -83,7 +86,7 @@ TopSitesBackend::~TopSitesBackend() {
}
void TopSitesBackend::InitDBOnDBThread(const base::FilePath& path) {
- DCHECK(db_task_runner_->BelongsToCurrentThread());
+ DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
if (!db_->Init(path)) {
LOG(ERROR) << "Failed to initialize database.";
db_.reset();
@@ -91,13 +94,13 @@ void TopSitesBackend::InitDBOnDBThread(const base::FilePath& path) {
}
void TopSitesBackend::ShutdownDBOnDBThread() {
- DCHECK(db_task_runner_->BelongsToCurrentThread());
+ DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
db_.reset();
}
void TopSitesBackend::GetMostVisitedThumbnailsOnDBThread(
scoped_refptr<MostVisitedThumbnails> thumbnails) {
- DCHECK(db_task_runner_->BelongsToCurrentThread());
+ DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
if (db_) {
db_->GetPageThumbnails(&(thumbnails->most_visited),
@@ -139,7 +142,7 @@ void TopSitesBackend::SetPageThumbnailOnDBThread(const MostVisitedURL& url,
}
void TopSitesBackend::ResetDatabaseOnDBThread(const base::FilePath& file_path) {
- DCHECK(db_task_runner_->BelongsToCurrentThread());
+ DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
db_.reset(NULL);
sql::Connection::Delete(db_path_);
db_.reset(new TopSitesDatabase());
diff --git a/chromium/components/history/core/browser/top_sites_backend.h b/chromium/components/history/core/browser/top_sites_backend.h
index 807f3374b61..620a76ac245 100644
--- a/chromium/components/history/core/browser/top_sites_backend.h
+++ b/chromium/components/history/core/browser/top_sites_backend.h
@@ -16,7 +16,7 @@
namespace base {
class CancelableTaskTracker;
class FilePath;
-class SingleThreadTaskRunner;
+class SequencedTaskRunner;
}
namespace history {
@@ -42,8 +42,7 @@ class TopSitesBackend : public base::RefCountedThreadSafe<TopSitesBackend> {
typedef base::Callback<void(const scoped_refptr<MostVisitedThumbnails>&)>
GetMostVisitedThumbnailsCallback;
- explicit TopSitesBackend(
- const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner);
+ TopSitesBackend();
void Init(const base::FilePath& path);
@@ -103,7 +102,7 @@ class TopSitesBackend : public base::RefCountedThreadSafe<TopSitesBackend> {
base::FilePath db_path_;
std::unique_ptr<TopSitesDatabase> db_;
- scoped_refptr<base::SingleThreadTaskRunner> db_task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
DISALLOW_COPY_AND_ASSIGN(TopSitesBackend);
};
diff --git a/chromium/components/history/core/browser/top_sites_impl.cc b/chromium/components/history/core/browser/top_sites_impl.cc
index 53171ff079e..aa758327087 100644
--- a/chromium/components/history/core/browser/top_sites_impl.cc
+++ b/chromium/components/history/core/browser/top_sites_impl.cc
@@ -51,7 +51,7 @@ void RunOrPostGetMostVisitedURLsCallback(
const MostVisitedURLList& nonforced_urls) {
const MostVisitedURLList& urls =
include_forced_urls ? all_urls : nonforced_urls;
- if (task_runner->RunsTasksOnCurrentThread())
+ if (task_runner->RunsTasksInCurrentSequence())
callback.Run(urls);
else
task_runner->PostTask(FROM_HERE, base::Bind(callback, urls));
@@ -120,12 +120,10 @@ TopSitesImpl::TopSitesImpl(PrefService* pref_service,
DCHECK(!can_add_url_to_history_.is_null());
}
-void TopSitesImpl::Init(
- const base::FilePath& db_name,
- const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner) {
+void TopSitesImpl::Init(const base::FilePath& db_name) {
// Create the backend here, rather than in the constructor, so that
// unit tests that do not need the backend can run without a problem.
- backend_ = new TopSitesBackend(db_task_runner);
+ backend_ = new TopSitesBackend();
backend_->Init(db_name);
backend_->GetMostVisitedThumbnails(
base::Bind(&TopSitesImpl::OnGotMostVisitedThumbnails,
@@ -136,38 +134,25 @@ void TopSitesImpl::Init(
bool TopSitesImpl::SetPageThumbnail(const GURL& url,
const gfx::Image& thumbnail,
const ThumbnailScore& score) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (!loaded_) {
- // TODO(sky): I need to cache these and apply them after the load
- // completes.
- return false;
- }
-
- bool add_temp_thumbnail = false;
- if (!IsKnownURL(url)) {
- if (IsNonForcedFull())
- return false; // We're full, and this URL is not known to us.
-
- add_temp_thumbnail = true;
- }
-
- if (!can_add_url_to_history_.Run(url))
- return false; // It's not a real webpage.
+ ThumbnailEvent result = SetPageThumbnailImpl(url, thumbnail, score);
- scoped_refptr<base::RefCountedBytes> thumbnail_data;
- if (!EncodeBitmap(thumbnail, &thumbnail_data))
- return false;
+ UMA_HISTOGRAM_ENUMERATION("Thumbnails.AddedToTopSites", result,
+ THUMBNAIL_EVENT_COUNT);
- if (add_temp_thumbnail) {
- // Always remove the existing entry and then add it back. That way if we end
- // up with too many temp thumbnails we'll prune the oldest first.
- RemoveTemporaryThumbnailByURL(url);
- AddTemporaryThumbnail(url, thumbnail_data.get(), score);
- return true;
+ switch (result) {
+ case THUMBNAIL_FAILURE:
+ case THUMBNAIL_TOPSITES_FULL:
+ case THUMBNAIL_KEPT_EXISTING:
+ return false;
+ case THUMBNAIL_ADDED_TEMP:
+ case THUMBNAIL_ADDED_REGULAR:
+ return true;
+ case THUMBNAIL_PROMOTED_TEMP_TO_REGULAR:
+ case THUMBNAIL_EVENT_COUNT:
+ NOTREACHED();
}
- return SetPageThumbnailEncoded(url, thumbnail_data.get(), score);
+ return false;
}
// WARNING: this function may be invoked on any thread.
@@ -489,11 +474,51 @@ void TopSitesImpl::DiffMostVisited(const MostVisitedURLList& old_list,
}
}
-bool TopSitesImpl::SetPageThumbnailNoDB(
+TopSitesImpl::ThumbnailEvent TopSitesImpl::SetPageThumbnailImpl(
+ const GURL& url,
+ const gfx::Image& thumbnail,
+ const ThumbnailScore& score) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!loaded_) {
+ // TODO(sky): I need to cache these and apply them after the load
+ // completes.
+ return THUMBNAIL_FAILURE;
+ }
+
+ bool add_temp_thumbnail = false;
+ if (!IsKnownURL(url)) {
+ if (IsNonForcedFull()) {
+ // We're full, and this URL is not known to us.
+ return THUMBNAIL_TOPSITES_FULL;
+ }
+
+ add_temp_thumbnail = true;
+ }
+
+ if (!can_add_url_to_history_.Run(url))
+ return THUMBNAIL_FAILURE; // It's not a real webpage.
+
+ scoped_refptr<base::RefCountedBytes> thumbnail_data;
+ if (!EncodeBitmap(thumbnail, &thumbnail_data))
+ return THUMBNAIL_FAILURE;
+
+ if (add_temp_thumbnail) {
+ // Always remove the existing entry and then add it back. That way if we end
+ // up with too many temp thumbnails we'll prune the oldest first.
+ RemoveTemporaryThumbnailByURL(url);
+ AddTemporaryThumbnail(url, thumbnail_data.get(), score);
+ return THUMBNAIL_ADDED_TEMP;
+ }
+
+ bool success = SetPageThumbnailEncoded(url, thumbnail_data.get(), score);
+ return success ? THUMBNAIL_ADDED_REGULAR : THUMBNAIL_KEPT_EXISTING;
+}
+
+bool TopSitesImpl::SetPageThumbnailInCache(
const GURL& url,
const base::RefCountedMemory* thumbnail_data,
const ThumbnailScore& score) {
- // This should only be invoked when we know about the url.
DCHECK(cache_->IsKnownURL(url));
const MostVisitedURL& most_visited =
@@ -524,13 +549,12 @@ bool TopSitesImpl::SetPageThumbnailEncoded(
const GURL& url,
const base::RefCountedMemory* thumbnail,
const ThumbnailScore& score) {
- if (!SetPageThumbnailNoDB(url, thumbnail, score))
- return false;
+ DCHECK(cache_->IsKnownURL(url));
- // Update the database.
- if (!cache_->IsKnownURL(url))
+ if (!SetPageThumbnailInCache(url, thumbnail, score))
return false;
+ // Update the database.
size_t index = cache_->GetURLIndex(url);
int url_rank = index - cache_->GetNumForcedURLs();
const MostVisitedURL& most_visited = cache_->top_sites()[index];
@@ -743,8 +767,16 @@ void TopSitesImpl::SetTopSites(const MostVisitedURLList& new_top_sites,
for (TempImages::iterator it = temp_images_.begin();
it != temp_images_.end(); ++it) {
if (canonical_url == cache_->GetCanonicalURL(it->first)) {
- SetPageThumbnailEncoded(
+ bool success = SetPageThumbnailEncoded(
mv.url, it->second.thumbnail.get(), it->second.thumbnail_score);
+ // TODO(treib): We shouldn't have a non-temp thumbnail yet at this
+ // point, so this should always succeed, but it doesn't - see
+ // crbug.com/735395.
+ if (success) {
+ UMA_HISTOGRAM_ENUMERATION("Thumbnails.AddedToTopSites",
+ THUMBNAIL_PROMOTED_TEMP_TO_REGULAR,
+ THUMBNAIL_EVENT_COUNT);
+ }
temp_images_.erase(it);
break;
}
diff --git a/chromium/components/history/core/browser/top_sites_impl.h b/chromium/components/history/core/browser/top_sites_impl.h
index 3024d448a4c..e8d10234207 100644
--- a/chromium/components/history/core/browser/top_sites_impl.h
+++ b/chromium/components/history/core/browser/top_sites_impl.h
@@ -39,7 +39,6 @@ namespace base {
class FilePath;
class RefCountedBytes;
class RefCountedMemory;
-class SingleThreadTaskRunner;
}
namespace history {
@@ -64,8 +63,7 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
const CanAddURLToHistoryFn& can_add_url_to_history);
// Initializes TopSitesImpl.
- void Init(const base::FilePath& db_name,
- const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner);
+ void Init(const base::FilePath& db_name);
// TopSites implementation.
bool SetPageThumbnail(const GURL& url,
@@ -119,6 +117,21 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
CALL_LOCATION_FROM_OTHER_PLACES,
};
+ // An enum representing various outcomes of adding a page thumbnail, for use
+ // in UMA histograms. Most of these are recorded from SetPageThumbnail,
+ // except for PROMOTED_TEMP_TO_REGULAR which can happen during SetTopSites.
+ // Do not change existing entries, and only add new ones at the end!
+ enum ThumbnailEvent {
+ THUMBNAIL_FAILURE = 0,
+ THUMBNAIL_TOPSITES_FULL = 1,
+ THUMBNAIL_KEPT_EXISTING = 2,
+ THUMBNAIL_ADDED_TEMP = 3,
+ THUMBNAIL_ADDED_REGULAR = 4,
+ THUMBNAIL_PROMOTED_TEMP_TO_REGULAR = 5,
+ // Add new entries here.
+ THUMBNAIL_EVENT_COUNT = 6
+ };
+
friend class TopSitesImplTest;
FRIEND_TEST_ALL_PREFIXES(TopSitesImplTest, DiffMostVisited);
FRIEND_TEST_ALL_PREFIXES(TopSitesImplTest, DiffMostVisitedWithForced);
@@ -146,14 +159,22 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
const MostVisitedURLList& new_list,
TopSitesDelta* delta);
- // Sets the thumbnail without writing to the database.
- // Returns true if the thumbnail was set, false if the existing one is better.
- bool SetPageThumbnailNoDB(const GURL& url,
- const base::RefCountedMemory* thumbnail_data,
- const ThumbnailScore& score);
+ // The actual implementation of SetPageThumbnail. It returns a more detailed
+ // status code (for UMA) rather than just a bool.
+ ThumbnailEvent SetPageThumbnailImpl(const GURL& url,
+ const gfx::Image& thumbnail,
+ const ThumbnailScore& score);
+
+ // Sets the thumbnail without writing to the database, i.e. updates the cache
+ // only. Must only be called for known URLs. Returns true if the thumbnail was
+ // set, false if the existing one (if any) is better.
+ bool SetPageThumbnailInCache(const GURL& url,
+ const base::RefCountedMemory* thumbnail_data,
+ const ThumbnailScore& score);
- // A version of SetPageThumbnail that takes RefCountedBytes as
- // returned by HistoryService.
+ // The major part of SetPageThumbnail, which sets the thumbnail both in the
+ // cache and in the database. Must only be called for known URLs. Returns true
+ // if the thumbnail was set, false if the existing one (if any) is better.
bool SetPageThumbnailEncoded(const GURL& url,
const base::RefCountedMemory* thumbnail,
const ThumbnailScore& score);
diff --git a/chromium/components/history/core/browser/top_sites_impl_unittest.cc b/chromium/components/history/core/browser/top_sites_impl_unittest.cc
index 25c83ff3cf0..00d1e10793b 100644
--- a/chromium/components/history/core/browser/top_sites_impl_unittest.cc
+++ b/chromium/components/history/core/browser/top_sites_impl_unittest.cc
@@ -11,10 +11,10 @@
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
+#include "base/test/scoped_task_environment.h"
#include "build/build_config.h"
#include "components/history/core/browser/history_client.h"
#include "components/history/core/browser/history_constants.h"
@@ -72,13 +72,14 @@ class TopSitesQuerier {
bool wait,
bool include_forced_urls) {
int start_number_of_callbacks = number_of_callbacks_;
+ base::RunLoop run_loop;
top_sites->GetMostVisitedURLs(
base::Bind(&TopSitesQuerier::OnTopSitesAvailable,
- weak_ptr_factory_.GetWeakPtr()),
+ weak_ptr_factory_.GetWeakPtr(), &run_loop),
include_forced_urls);
if (wait && start_number_of_callbacks == number_of_callbacks_) {
waiting_ = true;
- base::RunLoop().Run();
+ run_loop.Run();
}
}
@@ -91,11 +92,12 @@ class TopSitesQuerier {
private:
// Callback for TopSitesImpl::GetMostVisitedURLs.
- void OnTopSitesAvailable(const history::MostVisitedURLList& data) {
+ void OnTopSitesAvailable(base::RunLoop* run_loop,
+ const history::MostVisitedURLList& data) {
urls_ = data;
number_of_callbacks_++;
if (waiting_) {
- base::MessageLoop::current()->QuitWhenIdle();
+ run_loop->QuitWhenIdle();
waiting_ = false;
}
}
@@ -166,15 +168,6 @@ class TopSitesImplTest : public HistoryUnitTestBase {
BlockUntilHistoryProcessesPendingRequests(history_service());
}
- // Waits for top sites to finish processing a task. This is useful if you need
- // to wait until top sites finishes processing a task.
- void WaitForTopSites() {
- top_sites()->backend_->DoEmptyRequest(
- base::Bind(&TopSitesImplTest::QuitCallback, base::Unretained(this)),
- &top_sites_tracker_);
- base::RunLoop().Run();
- }
-
TopSitesImpl* top_sites() { return top_sites_impl_.get(); }
HistoryService* history_service() { return history_service_.get(); }
@@ -196,10 +189,6 @@ class TopSitesImplTest : public HistoryUnitTestBase {
}
}
- // Quit the current message loop when invoked. Useful when running a nested
- // message loop.
- void QuitCallback() { base::MessageLoop::current()->QuitWhenIdle(); }
-
// Adds a page to history.
void AddPageToHistory(const GURL& url) {
RedirectList redirects;
@@ -299,8 +288,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
top_sites_impl_ = new TopSitesImpl(
pref_service_.get(), history_service_.get(),
prepopulated_pages, base::Bind(MockCanAddURLToHistory));
- top_sites_impl_->Init(scoped_temp_dir_.GetPath().Append(kTopSitesFilename),
- message_loop_.task_runner());
+ top_sites_impl_->Init(scoped_temp_dir_.GetPath().Append(kTopSitesFilename));
}
void DestroyTopSites() {
@@ -308,8 +296,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
top_sites_impl_->ShutdownOnUIThread();
top_sites_impl_ = nullptr;
- if (base::MessageLoop::current())
- base::RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
}
}
@@ -320,8 +307,9 @@ class TopSitesImplTest : public HistoryUnitTestBase {
}
private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+
base::ScopedTempDir scoped_temp_dir_;
- base::MessageLoopForUI message_loop_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<HistoryService> history_service_;
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 40d57d67272..150ae7c5058 100644
--- a/chromium/components/history/core/browser/typed_url_sync_bridge.cc
+++ b/chromium/components/history/core/browser/typed_url_sync_bridge.cc
@@ -4,47 +4,185 @@
#include "components/history/core/browser/typed_url_sync_bridge.h"
+#include "base/big_endian.h"
#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/history/core/browser/history_backend.h"
+#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model_impl/sync_metadata_store_change_list.h"
+#include "net/base/url_util.h"
+
+using sync_pb::TypedUrlSpecifics;
+using syncer::EntityChange;
+using syncer::EntityChangeList;
+using syncer::EntityData;
+using syncer::MetadataChangeList;
+using syncer::ModelError;
+using syncer::MutableDataBatch;
namespace history {
+namespace {
+
+// The server backend can't handle arbitrarily large node sizes, so to keep
+// the size under control we limit the visit array.
+static const int kMaxTypedUrlVisits = 100;
+
+// There's no limit on how many visits the history DB could have for a given
+// typed URL, so we limit how many we fetch from the DB to avoid crashes due to
+// running out of memory (http://crbug.com/89793). This value is different
+// from kMaxTypedUrlVisits, as some of the visits fetched from the DB may be
+// RELOAD visits, which will be stripped.
+static const int kMaxVisitsToFetch = 1000;
+
+// Enforce oldest to newest visit order.
+static bool CheckVisitOrdering(const VisitVector& visits) {
+ int64_t previous_visit_time = 0;
+ for (VisitVector::const_iterator visit = visits.begin();
+ visit != visits.end(); ++visit) {
+ if (visit != visits.begin() &&
+ previous_visit_time > visit->visit_time.ToInternalValue())
+ return false;
+
+ previous_visit_time = visit->visit_time.ToInternalValue();
+ }
+ return true;
+}
+
+std::string GetStorageKeyFromURLRow(const URLRow& row) {
+ std::string storage_key(sizeof(row.id()), 0);
+ base::WriteBigEndian<URLID>(&storage_key[0], row.id());
+ return storage_key;
+}
+
+bool HasTypedUrl(const VisitVector& visits) {
+ auto typed_url_visit = std::find_if(
+ visits.begin(), visits.end(), [](const history::VisitRow& visit) {
+ return ui::PageTransitionCoreTypeIs(visit.transition,
+ ui::PAGE_TRANSITION_TYPED);
+ });
+ return typed_url_visit != visits.end();
+}
+
+} // namespace
+
TypedURLSyncBridge::TypedURLSyncBridge(
HistoryBackend* history_backend,
- syncer::SyncMetadataStore* sync_metadata_store,
+ TypedURLSyncMetadataDatabase* sync_metadata_database,
const ChangeProcessorFactory& change_processor_factory)
: ModelTypeSyncBridge(change_processor_factory, syncer::TYPED_URLS),
history_backend_(history_backend),
- sync_metadata_store_(sync_metadata_store) {
+ sync_metadata_database_(sync_metadata_database),
+ num_db_accesses_(0),
+ num_db_errors_(0),
+ history_backend_observer_(this) {
DCHECK(history_backend_);
DCHECK(sequence_checker_.CalledOnValidSequence());
- DCHECK(sync_metadata_store_);
- NOTIMPLEMENTED();
+ DCHECK(sync_metadata_database_);
}
TypedURLSyncBridge::~TypedURLSyncBridge() {
- // TODO(gangwu): unregister as HistoryBackendObserver, can use ScopedObserver
- // to do it.
}
-std::unique_ptr<syncer::MetadataChangeList>
+std::unique_ptr<MetadataChangeList>
TypedURLSyncBridge::CreateMetadataChangeList() {
DCHECK(sequence_checker_.CalledOnValidSequence());
return base::MakeUnique<syncer::SyncMetadataStoreChangeList>(
- sync_metadata_store_, syncer::TYPED_URLS);
+ sync_metadata_database_, syncer::TYPED_URLS);
}
-base::Optional<syncer::ModelError> TypedURLSyncBridge::MergeSyncData(
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityDataMap entity_data_map) {
+base::Optional<ModelError> TypedURLSyncBridge::MergeSyncData(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityChangeList entity_data) {
DCHECK(sequence_checker_.CalledOnValidSequence());
- NOTIMPLEMENTED();
- return {};
+
+ // Create a mapping of all local data by URLID. These will be narrowed down
+ // by CreateOrUpdateUrl() to include only the entries different from sync
+ // server data.
+ TypedURLMap new_db_urls;
+
+ // Get all the visits and map the URLRows by URL.
+ URLVisitVectorMap local_visit_vectors;
+
+ if (!GetValidURLsAndVisits(&local_visit_vectors, &new_db_urls)) {
+ return ModelError(
+ FROM_HERE, "Could not get the typed_url entries from HistoryBackend.");
+ }
+
+ // New sync data organized for different write operations to history backend.
+ history::URLRows new_synced_urls;
+ history::URLRows updated_synced_urls;
+ TypedURLVisitVector new_synced_visits;
+
+ // Iterate through entity_data and check for all the urls that
+ // sync already knows about. CreateOrUpdateUrl() will remove urls that
+ // are the same as the synced ones from |new_db_urls|.
+ for (const EntityChange& entity_change : entity_data) {
+ DCHECK(entity_change.data().specifics.has_typed_url());
+ const TypedUrlSpecifics& specifics =
+ entity_change.data().specifics.typed_url();
+ if (ShouldIgnoreUrl(GURL(specifics.url())))
+ continue;
+
+ // Ignore old sync urls that don't have any transition data stored with
+ // them, or transition data that does not match the visit data (will be
+ // deleted below).
+ if (specifics.visit_transitions_size() == 0 ||
+ specifics.visit_transitions_size() != specifics.visits_size()) {
+ // Generate a debug assertion to help track down http://crbug.com/91473,
+ // even though we gracefully handle this case by overwriting this node.
+ DCHECK_EQ(specifics.visits_size(), specifics.visit_transitions_size());
+ DVLOG(1) << "Ignoring obsolete sync url with no visit transition info.";
+
+ continue;
+ }
+ UpdateUrlFromServer(specifics, &new_db_urls, &local_visit_vectors,
+ &new_synced_urls, &new_synced_visits,
+ &updated_synced_urls);
+ }
+
+ for (const auto& kv : new_db_urls) {
+ if (!HasTypedUrl(local_visit_vectors[kv.first])) {
+ // This URL has no TYPED visits, don't sync it
+ continue;
+ }
+ std::string storage_key = GetStorageKeyFromURLRow(kv.second);
+ change_processor()->Put(
+ storage_key, CreateEntityData(kv.second, local_visit_vectors[kv.first]),
+ metadata_change_list.get());
+ }
+
+ base::Optional<ModelError> error = WriteToHistoryBackend(
+ &new_synced_urls, &updated_synced_urls, NULL, &new_synced_visits, NULL);
+
+ if (error)
+ return error;
+
+ for (const EntityChange& entity_change : entity_data) {
+ DCHECK(entity_change.data().specifics.has_typed_url());
+ std::string storage_key =
+ GetStorageKeyInternal(entity_change.data().specifics.typed_url().url());
+ if (storage_key.empty()) {
+ // ignore entity change
+ } else {
+ change_processor()->UpdateStorageKey(entity_change.data(), storage_key,
+ metadata_change_list.get());
+ }
+ }
+
+ UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlMergeAndStartSyncingErrors",
+ GetErrorPercentage());
+ ClearErrorStats();
+
+ return static_cast<syncer::SyncMetadataStoreChangeList*>(
+ metadata_change_list.get())
+ ->TakeError();
}
-base::Optional<syncer::ModelError> TypedURLSyncBridge::ApplySyncChanges(
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityChangeList entity_changes) {
+base::Optional<ModelError> TypedURLSyncBridge::ApplySyncChanges(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityChangeList entity_changes) {
DCHECK(sequence_checker_.CalledOnValidSequence());
NOTIMPLEMENTED();
return {};
@@ -58,15 +196,31 @@ void TypedURLSyncBridge::GetData(StorageKeyList storage_keys,
void TypedURLSyncBridge::GetAllData(DataCallback callback) {
DCHECK(sequence_checker_.CalledOnValidSequence());
- NOTIMPLEMENTED();
+
+ history::URLRows typed_urls;
+ ++num_db_accesses_;
+ if (!history_backend_->GetAllTypedURLs(&typed_urls)) {
+ ++num_db_errors_;
+ change_processor()->ReportError(FROM_HERE,
+ "Could not get the typed_url entries.");
+ return;
+ }
+
+ auto batch = base::MakeUnique<MutableDataBatch>();
+ for (history::URLRow& url : typed_urls) {
+ VisitVector visits_vector;
+ FixupURLAndGetVisits(&url, &visits_vector);
+ batch->Put(GetStorageKeyFromURLRow(url),
+ CreateEntityData(url, visits_vector));
+ }
+ callback.Run(std::move(batch));
}
// Must be exactly the value of GURL::spec() for backwards comparability with
// the previous (Directory + SyncableService) iteration of sync integration.
// This can be large but it is assumed that this is not held in memory at steady
// state.
-std::string TypedURLSyncBridge::GetClientTag(
- const syncer::EntityData& entity_data) {
+std::string TypedURLSyncBridge::GetClientTag(const EntityData& entity_data) {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(entity_data.specifics.has_typed_url())
<< "EntityData does not have typed urls specifics.";
@@ -76,13 +230,15 @@ std::string TypedURLSyncBridge::GetClientTag(
// Prefer to use URLRow::id() to uniquely identify entities when coordinating
// with sync because it has a significantly low memory cost than a URL.
-std::string TypedURLSyncBridge::GetStorageKey(
- const syncer::EntityData& entity_data) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- NOTIMPLEMENTED();
+std::string TypedURLSyncBridge::GetStorageKey(const EntityData& entity_data) {
+ NOTREACHED() << "TypedURLSyncBridge do not support GetStorageKey.";
return std::string();
}
+bool TypedURLSyncBridge::SupportsGetStorageKey() const {
+ return false;
+}
+
void TypedURLSyncBridge::OnURLVisited(history::HistoryBackend* history_backend,
ui::PageTransition transition,
const history::URLRow& row,
@@ -108,4 +264,641 @@ void TypedURLSyncBridge::OnURLsDeleted(history::HistoryBackend* history_backend,
NOTIMPLEMENTED();
}
+void TypedURLSyncBridge::Init() {
+ DCHECK(sequence_checker_.CalledOnValidSequence());
+
+ history_backend_observer_.Add(history_backend_);
+ LoadMetadata();
+}
+
+int TypedURLSyncBridge::GetErrorPercentage() const {
+ return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0;
+}
+
+bool TypedURLSyncBridge::WriteToTypedUrlSpecifics(
+ const URLRow& url,
+ const VisitVector& visits,
+ TypedUrlSpecifics* typed_url) {
+ DCHECK(!url.last_visit().is_null());
+ DCHECK(!visits.empty());
+ DCHECK_EQ(url.last_visit().ToInternalValue(),
+ visits.back().visit_time.ToInternalValue());
+
+ typed_url->set_url(url.url().spec());
+ typed_url->set_title(base::UTF16ToUTF8(url.title()));
+ typed_url->set_hidden(url.hidden());
+
+ DCHECK(CheckVisitOrdering(visits));
+
+ bool only_typed = false;
+ int skip_count = 0;
+
+ if (std::find_if(visits.begin(), visits.end(),
+ [](const history::VisitRow& visit) {
+ return ui::PageTransitionCoreTypeIs(
+ visit.transition, ui::PAGE_TRANSITION_TYPED);
+ }) == visits.end()) {
+ // This URL has no TYPED visits, don't sync it
+ return false;
+ }
+
+ if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) {
+ int typed_count = 0;
+ int total = 0;
+ // Walk the passed-in visit vector and count the # of typed visits.
+ for (VisitRow visit : visits) {
+ // We ignore reload visits.
+ if (PageTransitionCoreTypeIs(visit.transition,
+ ui::PAGE_TRANSITION_RELOAD)) {
+ continue;
+ }
+ ++total;
+ if (PageTransitionCoreTypeIs(visit.transition,
+ ui::PAGE_TRANSITION_TYPED)) {
+ ++typed_count;
+ }
+ }
+
+ // We should have at least one typed visit. This can sometimes happen if
+ // the history DB has an inaccurate count for some reason (there's been
+ // bugs in the history code in the past which has left users in the wild
+ // with incorrect counts - http://crbug.com/84258).
+ DCHECK(typed_count > 0);
+
+ if (typed_count > kMaxTypedUrlVisits) {
+ only_typed = true;
+ skip_count = typed_count - kMaxTypedUrlVisits;
+ } else if (total > kMaxTypedUrlVisits) {
+ skip_count = total - kMaxTypedUrlVisits;
+ }
+ }
+
+ for (const auto& visit : visits) {
+ // Skip reload visits.
+ if (PageTransitionCoreTypeIs(visit.transition, ui::PAGE_TRANSITION_RELOAD))
+ continue;
+
+ // If we only have room for typed visits, then only add typed visits.
+ if (only_typed && !PageTransitionCoreTypeIs(visit.transition,
+ ui::PAGE_TRANSITION_TYPED)) {
+ continue;
+ }
+
+ if (skip_count > 0) {
+ // We have too many entries to fit, so we need to skip the oldest ones.
+ // Only skip typed URLs if there are too many typed URLs to fit.
+ if (only_typed || !PageTransitionCoreTypeIs(visit.transition,
+ ui::PAGE_TRANSITION_TYPED)) {
+ --skip_count;
+ continue;
+ }
+ }
+ typed_url->add_visits(visit.visit_time.ToInternalValue());
+ typed_url->add_visit_transitions(visit.transition);
+ }
+ DCHECK_EQ(skip_count, 0);
+
+ CHECK_GT(typed_url->visits_size(), 0);
+ CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits);
+ CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size());
+
+ return true;
+}
+
+// static
+TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls(
+ const TypedUrlSpecifics& sync_url,
+ const history::URLRow& url,
+ history::VisitVector* visits,
+ history::URLRow* new_url,
+ std::vector<history::VisitInfo>* new_visits) {
+ DCHECK(new_url);
+ DCHECK_EQ(sync_url.url(), url.url().spec());
+ DCHECK_EQ(sync_url.url(), new_url->url().spec());
+ DCHECK(visits->size());
+ DCHECK_GT(sync_url.visits_size(), 0);
+ CHECK_EQ(sync_url.visits_size(), sync_url.visit_transitions_size());
+
+ // Convert these values only once.
+ base::string16 sync_url_title(base::UTF8ToUTF16(sync_url.title()));
+ base::Time sync_url_last_visit = base::Time::FromInternalValue(
+ sync_url.visits(sync_url.visits_size() - 1));
+
+ // This is a bitfield representing what we'll need to update with the output
+ // value.
+ MergeResult different = DIFF_NONE;
+
+ // Check if the non-incremented values changed.
+ if ((sync_url_title != url.title()) || (sync_url.hidden() != url.hidden())) {
+ // Use the values from the most recent visit.
+ if (sync_url_last_visit >= url.last_visit()) {
+ new_url->set_title(sync_url_title);
+ new_url->set_hidden(sync_url.hidden());
+ different |= DIFF_LOCAL_ROW_CHANGED;
+ } else {
+ new_url->set_title(url.title());
+ new_url->set_hidden(url.hidden());
+ different |= DIFF_UPDATE_NODE;
+ }
+ } else {
+ // No difference.
+ new_url->set_title(url.title());
+ new_url->set_hidden(url.hidden());
+ }
+
+ size_t sync_url_num_visits = sync_url.visits_size();
+ size_t history_num_visits = visits->size();
+ size_t sync_url_visit_index = 0;
+ size_t history_visit_index = 0;
+ base::Time earliest_history_time = (*visits)[0].visit_time;
+ // Walk through the two sets of visits and figure out if any new visits were
+ // added on either side.
+ while (sync_url_visit_index < sync_url_num_visits ||
+ history_visit_index < history_num_visits) {
+ // Time objects are initialized to "earliest possible time".
+ base::Time sync_url_time, history_time;
+ if (sync_url_visit_index < sync_url_num_visits)
+ sync_url_time =
+ base::Time::FromInternalValue(sync_url.visits(sync_url_visit_index));
+ if (history_visit_index < history_num_visits)
+ history_time = (*visits)[history_visit_index].visit_time;
+ if (sync_url_visit_index >= sync_url_num_visits ||
+ (history_visit_index < history_num_visits &&
+ sync_url_time > history_time)) {
+ // We found a visit in the history DB that doesn't exist in the sync DB,
+ // so mark the sync_url as modified so the caller will update the sync
+ // node.
+ different |= DIFF_UPDATE_NODE;
+ ++history_visit_index;
+ } else if (history_visit_index >= history_num_visits ||
+ sync_url_time < history_time) {
+ // Found a visit in the sync node that doesn't exist in the history DB, so
+ // add it to our list of new visits and set the appropriate flag so the
+ // caller will update the history DB.
+ // If the sync_url visit is older than any existing visit in the history
+ // DB, don't re-add it - this keeps us from resurrecting visits that were
+ // aged out locally.
+ //
+ // TODO(sync): This extra check should be unnecessary now that filtering
+ // expired visits is performed separately. Non-expired visits older than
+ // the earliest existing history visits should still be synced, so this
+ // check should be removed.
+ if (sync_url_time > earliest_history_time) {
+ different |= DIFF_LOCAL_VISITS_ADDED;
+ new_visits->push_back(history::VisitInfo(
+ sync_url_time, ui::PageTransitionFromInt(sync_url.visit_transitions(
+ sync_url_visit_index))));
+ }
+ // This visit is added to visits below.
+ ++sync_url_visit_index;
+ } else {
+ // Same (already synced) entry found in both DBs - no need to do anything.
+ ++sync_url_visit_index;
+ ++history_visit_index;
+ }
+ }
+
+ DCHECK(CheckVisitOrdering(*visits));
+ if (different & DIFF_LOCAL_VISITS_ADDED) {
+ // If the server does not have the same visits as the local db, then the
+ // new visits from the server need to be added to the vector containing
+ // local visits. These visits will be passed to the server.
+ // Insert new visits into the appropriate place in the visits vector.
+ history::VisitVector::iterator visit_ix = visits->begin();
+ for (std::vector<history::VisitInfo>::iterator new_visit =
+ new_visits->begin();
+ new_visit != new_visits->end(); ++new_visit) {
+ while (visit_ix != visits->end() &&
+ new_visit->first > visit_ix->visit_time) {
+ ++visit_ix;
+ }
+ visit_ix =
+ visits->insert(visit_ix, history::VisitRow(url.id(), new_visit->first,
+ 0, new_visit->second, 0));
+ ++visit_ix;
+ }
+ }
+ DCHECK(CheckVisitOrdering(*visits));
+
+ new_url->set_last_visit(visits->back().visit_time);
+ return different;
+}
+
+// static
+void TypedURLSyncBridge::UpdateURLRowFromTypedUrlSpecifics(
+ const TypedUrlSpecifics& typed_url,
+ history::URLRow* new_url) {
+ DCHECK_GT(typed_url.visits_size(), 0);
+ CHECK_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
+ if (!new_url->url().is_valid()) {
+ new_url->set_url(GURL(typed_url.url()));
+ }
+ new_url->set_title(base::UTF8ToUTF16(typed_url.title()));
+ new_url->set_hidden(typed_url.hidden());
+ // Only provide the initial value for the last_visit field - after that, let
+ // the history code update the last_visit field on its own.
+ if (new_url->last_visit().is_null()) {
+ new_url->set_last_visit(base::Time::FromInternalValue(
+ typed_url.visits(typed_url.visits_size() - 1)));
+ }
+}
+
+void TypedURLSyncBridge::LoadMetadata() {
+ if (!history_backend_ || !sync_metadata_database_) {
+ change_processor()->ReportError(
+ FROM_HERE, "Failed to load TypedURLSyncMetadataDatabase.");
+ return;
+ }
+
+ auto batch = base::MakeUnique<syncer::MetadataBatch>();
+ if (!sync_metadata_database_->GetAllSyncMetadata(batch.get())) {
+ change_processor()->ReportError(
+ FROM_HERE,
+ "Failed reading typed url metadata from TypedURLSyncMetadataDatabase.");
+ return;
+ }
+ change_processor()->ModelReadyToSync(std::move(batch));
+}
+
+void TypedURLSyncBridge::ClearErrorStats() {
+ num_db_accesses_ = 0;
+ num_db_errors_ = 0;
+}
+
+void TypedURLSyncBridge::UpdateUrlFromServer(
+ const sync_pb::TypedUrlSpecifics& server_typed_url,
+ TypedURLMap* local_typed_urls,
+ URLVisitVectorMap* local_visit_vectors,
+ history::URLRows* new_synced_urls,
+ TypedURLVisitVector* new_synced_visits,
+ history::URLRows* updated_synced_urls) {
+ DCHECK(server_typed_url.visits_size() != 0);
+ DCHECK_EQ(server_typed_url.visits_size(),
+ server_typed_url.visit_transitions_size());
+
+ // Ignore empty urls.
+ if (server_typed_url.url().empty()) {
+ DVLOG(1) << "Ignoring empty URL in sync DB";
+ return;
+ }
+ // Now, get rid of the expired visits. If there are no un-expired visits
+ // left, ignore this url - any local data should just replace it.
+ TypedUrlSpecifics sync_url = FilterExpiredVisits(server_typed_url);
+ if (sync_url.visits_size() == 0) {
+ DVLOG(1) << "Ignoring expired URL in sync DB: " << sync_url.url();
+ return;
+ }
+
+ // Check if local db already has the url from sync.
+ TypedURLMap::iterator it = local_typed_urls->find(GURL(sync_url.url()));
+ if (it == local_typed_urls->end()) {
+ // There are no matching typed urls from the local db, check for untyped
+ history::URLRow untyped_url(GURL(sync_url.url()));
+
+ // The URL may still exist in the local db if it is an untyped url.
+ // An untyped url will transition to a typed url after receiving visits
+ // from sync, and sync should receive any visits already existing locally
+ // for the url, so the full list of visits is consistent.
+ bool is_existing_url =
+ history_backend_->GetURL(untyped_url.url(), &untyped_url);
+ if (is_existing_url) {
+ // Add a new entry to |local_typed_urls|, and set the iterator to it.
+ history::VisitVector untyped_visits;
+ if (!FixupURLAndGetVisits(&untyped_url, &untyped_visits)) {
+ return;
+ }
+ (*local_visit_vectors)[untyped_url.url()] = untyped_visits;
+
+ // Store row info that will be used to update sync's visits.
+ (*local_typed_urls)[untyped_url.url()] = untyped_url;
+
+ // Set iterator |it| to point to this entry.
+ it = local_typed_urls->find(untyped_url.url());
+ DCHECK(it != local_typed_urls->end());
+ // Continue with merge below.
+ } else {
+ // The url is new to the local history DB.
+ // Create new db entry for url.
+ history::URLRow new_url(GURL(sync_url.url()));
+ UpdateURLRowFromTypedUrlSpecifics(sync_url, &new_url);
+ new_synced_urls->push_back(new_url);
+
+ // Add entries for url visits.
+ std::vector<history::VisitInfo> added_visits;
+ size_t visit_count = sync_url.visits_size();
+
+ for (size_t index = 0; index < visit_count; ++index) {
+ base::Time visit_time =
+ base::Time::FromInternalValue(sync_url.visits(index));
+ ui::PageTransition transition =
+ ui::PageTransitionFromInt(sync_url.visit_transitions(index));
+ added_visits.push_back(history::VisitInfo(visit_time, transition));
+ }
+ new_synced_visits->push_back(
+ std::pair<GURL, std::vector<history::VisitInfo>>(new_url.url(),
+ added_visits));
+ return;
+ }
+ }
+
+ // Same URL exists in sync data and in history data - compare the
+ // entries to see if there's any difference.
+ history::VisitVector& visits = (*local_visit_vectors)[it->first];
+ std::vector<history::VisitInfo> added_visits;
+
+ // Empty URLs should be filtered out by ShouldIgnoreUrl() previously.
+ DCHECK(!it->second.url().spec().empty());
+
+ // Initialize fields in |new_url| to the same values as the fields in
+ // the existing URLRow in the history DB. This is needed because we
+ // overwrite the existing value in WriteToHistoryBackend(), but some of
+ // the values in that structure are not synced (like typed_count).
+ history::URLRow new_url(it->second);
+
+ MergeResult difference =
+ MergeUrls(sync_url, it->second, &visits, &new_url, &added_visits);
+
+ if (difference != DIFF_NONE) {
+ it->second = new_url;
+ if (difference & DIFF_UPDATE_NODE) {
+ // We don't want to resurrect old visits that have been aged out by
+ // other clients, so remove all visits that are older than the
+ // earliest existing visit in the sync node.
+ //
+ // TODO(sync): This logic should be unnecessary now that filtering of
+ // expired visits is performed separately. Non-expired visits older than
+ // the earliest existing sync visits should still be synced, so this
+ // logic should be removed.
+ if (sync_url.visits_size() > 0) {
+ base::Time earliest_visit =
+ base::Time::FromInternalValue(sync_url.visits(0));
+ for (history::VisitVector::iterator i = visits.begin();
+ i != visits.end() && i->visit_time < earliest_visit;) {
+ i = visits.erase(i);
+ }
+ // Should never be possible to delete all the items, since the
+ // visit vector contains newer local visits it will keep and/or the
+ // visits in typed_url.visits newer than older local visits.
+ DCHECK(visits.size() > 0);
+ }
+ DCHECK_EQ(new_url.last_visit().ToInternalValue(),
+ visits.back().visit_time.ToInternalValue());
+ }
+ if (difference & DIFF_LOCAL_ROW_CHANGED) {
+ // Add entry to updated_synced_urls to update the local db.
+ DCHECK_EQ(it->second.id(), new_url.id());
+ updated_synced_urls->push_back(new_url);
+ }
+ if (difference & DIFF_LOCAL_VISITS_ADDED) {
+ // Add entry with new visits to new_synced_visits to update the local db.
+ new_synced_visits->push_back(
+ std::pair<GURL, std::vector<history::VisitInfo>>(it->first,
+ added_visits));
+ }
+ } else {
+ // No difference in urls, erase from map
+ local_typed_urls->erase(it);
+ }
+}
+
+base::Optional<ModelError> TypedURLSyncBridge::WriteToHistoryBackend(
+ const history::URLRows* new_urls,
+ const history::URLRows* updated_urls,
+ const std::vector<GURL>* deleted_urls,
+ const TypedURLVisitVector* new_visits,
+ const history::VisitVector* deleted_visits) {
+ if (deleted_urls && !deleted_urls->empty())
+ history_backend_->DeleteURLs(*deleted_urls);
+
+ if (new_urls) {
+ history_backend_->AddPagesWithDetails(*new_urls, history::SOURCE_SYNCED);
+ }
+
+ if (updated_urls) {
+ ++num_db_accesses_;
+ // This is an existing entry in the URL database. We don't verify the
+ // visit_count or typed_count values here, because either one (or both)
+ // could be zero in the case of bookmarks, or in the case of a URL
+ // transitioning from non-typed to typed as a result of this sync.
+ // In the field we sometimes run into errors on specific URLs. It's OK
+ // to just continue on (we can try writing again on the next model
+ // association).
+ size_t num_successful_updates = history_backend_->UpdateURLs(*updated_urls);
+ num_db_errors_ += updated_urls->size() - num_successful_updates;
+ }
+
+ if (new_visits) {
+ for (const auto& visits : *new_visits) {
+ // If there are no visits to add, just skip this.
+ if (visits.second.empty())
+ continue;
+ ++num_db_accesses_;
+ if (!history_backend_->AddVisits(visits.first, visits.second,
+ history::SOURCE_SYNCED)) {
+ ++num_db_errors_;
+ return ModelError(FROM_HERE, "Could not add visits to HistoryBackend.");
+ }
+ }
+ }
+
+ if (deleted_visits) {
+ ++num_db_accesses_;
+ if (!history_backend_->RemoveVisits(*deleted_visits)) {
+ ++num_db_errors_;
+ return ModelError(FROM_HERE,
+ "Could not remove visits from HistoryBackend.");
+ // This is bad news, since it means we may end up resurrecting history
+ // entries on the next reload. It's unavoidable so we'll just keep on
+ // syncing.
+ }
+ }
+
+ return {};
+}
+
+TypedUrlSpecifics TypedURLSyncBridge::FilterExpiredVisits(
+ const TypedUrlSpecifics& source) {
+ // Make a copy of the source, then regenerate the visits.
+ TypedUrlSpecifics specifics(source);
+ specifics.clear_visits();
+ specifics.clear_visit_transitions();
+ for (int i = 0; i < source.visits_size(); ++i) {
+ base::Time time = base::Time::FromInternalValue(source.visits(i));
+ if (!history_backend_->IsExpiredVisitTime(time)) {
+ specifics.add_visits(source.visits(i));
+ specifics.add_visit_transitions(source.visit_transitions(i));
+ }
+ }
+ DCHECK(specifics.visits_size() == specifics.visit_transitions_size());
+ return specifics;
+}
+
+bool TypedURLSyncBridge::ShouldIgnoreUrl(const GURL& url) {
+ // Ignore empty URLs. Not sure how this can happen (maybe import from other
+ // busted browsers, or misuse of the history API, or just plain bugs) but we
+ // can't deal with them.
+ if (url.spec().empty())
+ return true;
+
+ // Ignore local file URLs.
+ if (url.SchemeIsFile())
+ return true;
+
+ // Ignore localhost URLs.
+ if (net::IsLocalhost(url.host_piece()))
+ return true;
+
+ // Ignore username and password, sonce history backend will remove user name
+ // and password in URLDatabase::GURLToDatabaseURL and send username/password
+ // removed url to sync later.
+ if (url.has_username() || url.has_password())
+ return true;
+
+ return false;
+}
+
+bool TypedURLSyncBridge::ShouldIgnoreVisits(
+ const history::VisitVector& visits) {
+ // We ignore URLs that were imported, but have never been visited by
+ // chromium.
+ static const int kFirstImportedSource = history::SOURCE_FIREFOX_IMPORTED;
+ history::VisitSourceMap map;
+ if (!history_backend_->GetVisitsSource(visits, &map))
+ return false; // If we can't read the visit, assume it's not imported.
+
+ // Walk the list of visits and look for a non-imported item.
+ for (const auto& visit : visits) {
+ if (map.count(visit.visit_id) == 0 ||
+ map[visit.visit_id] < kFirstImportedSource) {
+ return false;
+ }
+ }
+ // We only saw imported visits, so tell the caller to ignore them.
+ return true;
+}
+
+bool TypedURLSyncBridge::FixupURLAndGetVisits(URLRow* url,
+ VisitVector* visits) {
+ ++num_db_accesses_;
+ if (!history_backend_->GetMostRecentVisitsForURL(url->id(), kMaxVisitsToFetch,
+ visits)) {
+ ++num_db_errors_;
+ // Couldn't load the visits for this URL due to some kind of DB error.
+ // Don't bother writing this URL to the history DB (if we ignore the
+ // error and continue, we might end up duplicating existing visits).
+ DLOG(ERROR) << "Could not load visits for url: " << url->url();
+ return false;
+ }
+
+ // Sometimes (due to a bug elsewhere in the history or sync code, or due to
+ // a crash between adding a URL to the history database and updating the
+ // visit DB) the visit vector for a URL can be empty. If this happens, just
+ // create a new visit whose timestamp is the same as the last_visit time.
+ // This is a workaround for http://crbug.com/84258.
+ if (visits->empty()) {
+ DVLOG(1) << "Found empty visits for URL: " << url->url();
+ if (url->last_visit().is_null()) {
+ // If modified URL is bookmarked, history backend treats it as modified
+ // even if all its visits are deleted. Return false to stop further
+ // processing because sync expects valid visit time for modified entry.
+ return false;
+ }
+
+ VisitRow visit(url->id(), url->last_visit(), 0, ui::PAGE_TRANSITION_TYPED,
+ 0);
+ visits->push_back(visit);
+ }
+
+ // GetMostRecentVisitsForURL() returns the data in the opposite order that
+ // we need it, so reverse it.
+ std::reverse(visits->begin(), visits->end());
+
+ // Sometimes, the last_visit field in the URL doesn't match the timestamp of
+ // the last visit in our visit array (they come from different tables, so
+ // crashes/bugs can cause them to mismatch), so just set it here.
+ url->set_last_visit(visits->back().visit_time);
+ DCHECK(CheckVisitOrdering(*visits));
+
+ // Removes all visits that are older than the current expiration time. Visits
+ // are in ascending order now, so we can check from beginning to check how
+ // many expired visits.
+ size_t num_expired_visits = 0;
+ for (auto& visit : *visits) {
+ base::Time time = visit.visit_time;
+ if (history_backend_->IsExpiredVisitTime(time)) {
+ ++num_expired_visits;
+ } else {
+ break;
+ }
+ }
+ if (num_expired_visits != 0) {
+ if (num_expired_visits == visits->size()) {
+ DVLOG(1) << "All visits are expired for url: " << url->url();
+ visits->clear();
+ return false;
+ }
+ visits->erase(visits->begin(), visits->begin() + num_expired_visits);
+ }
+ DCHECK(CheckVisitOrdering(*visits));
+
+ return true;
+}
+
+std::unique_ptr<EntityData> TypedURLSyncBridge::CreateEntityData(
+ const URLRow& row,
+ const VisitVector& visits) {
+ auto entity_data = base::MakeUnique<EntityData>();
+ TypedUrlSpecifics* specifics = entity_data->specifics.mutable_typed_url();
+
+ if (!WriteToTypedUrlSpecifics(row, visits, specifics)) {
+ // Cannot write to specifics, ex. no TYPED visits.
+ return base::MakeUnique<EntityData>();
+ }
+ entity_data->non_unique_name = row.url().spec();
+
+ return entity_data;
+}
+
+bool TypedURLSyncBridge::GetValidURLsAndVisits(URLVisitVectorMap* url_to_visit,
+ TypedURLMap* url_to_urlrow) {
+ DCHECK(url_to_visit);
+ DCHECK(url_to_urlrow);
+
+ history::URLRows local_typed_urls;
+ ++num_db_accesses_;
+ if (!history_backend_->GetAllTypedURLs(&local_typed_urls)) {
+ ++num_db_errors_;
+ return false;
+ }
+ for (history::URLRow& url : local_typed_urls) {
+ DCHECK_EQ(0U, url_to_visit->count(url.url()));
+ if (!FixupURLAndGetVisits(&url, &((*url_to_visit)[url.url()])) ||
+ ShouldIgnoreUrl(url.url()) ||
+ ShouldIgnoreVisits((*url_to_visit)[url.url()])) {
+ // Ignore this URL if we couldn't load the visits or if there's some
+ // other problem with it (it was empty, or imported and never visited).
+ } else {
+ // Add url to url_to_urlrow.
+ (*url_to_urlrow)[url.url()] = url;
+ }
+ }
+ return true;
+}
+
+std::string TypedURLSyncBridge::GetStorageKeyInternal(const std::string& url) {
+ DCHECK(history_backend_);
+
+ URLRow existing_url;
+ ++num_db_accesses_;
+ bool is_existing_url = history_backend_->GetURL(GURL(url), &existing_url);
+
+ if (!is_existing_url) {
+ // The typed url did not save to local history database, so return empty
+ // string.
+ return std::string();
+ }
+
+ return GetStorageKeyFromURLRow(existing_url);
+}
+
} // namespace history
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 59eb0756af9..13a8bbd9bc5 100644
--- a/chromium/components/history/core/browser/typed_url_sync_bridge.h
+++ b/chromium/components/history/core/browser/typed_url_sync_bridge.h
@@ -5,15 +5,13 @@
#ifndef COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_SYNC_BRIDGE_H_
#define COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_SYNC_BRIDGE_H_
+#include "base/scoped_observer.h"
#include "components/history/core/browser/history_backend_observer.h"
+#include "components/history/core/browser/typed_url_sync_metadata_database.h"
#include "components/sync/model/metadata_change_list.h"
#include "components/sync/model/model_type_sync_bridge.h"
#include "components/sync/model/sync_error.h"
-namespace syncer {
-class SyncMetadataStore;
-}
-
namespace history {
class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
@@ -22,7 +20,7 @@ class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
// |sync_metadata_store| is owned by |history_backend|, and must outlive
// TypedURLSyncBridge.
TypedURLSyncBridge(HistoryBackend* history_backend,
- syncer::SyncMetadataStore* sync_metadata_store,
+ TypedURLSyncMetadataDatabase* sync_metadata_store,
const ChangeProcessorFactory& change_processor_factory);
~TypedURLSyncBridge() override;
@@ -31,7 +29,7 @@ class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
override;
base::Optional<syncer::ModelError> MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityDataMap entity_data_map) override;
+ syncer::EntityChangeList entity_data) override;
base::Optional<syncer::ModelError> ApplySyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) override;
@@ -39,6 +37,7 @@ class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
void GetAllData(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;
// history::HistoryBackendObserver:
void OnURLVisited(history::HistoryBackend* history_backend,
@@ -54,19 +53,148 @@ class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
const history::URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) override;
+ // Must be called after creation and before any operations.
+ void Init();
+
+ // Returns the percentage of DB accesses that have resulted in an error.
+ int GetErrorPercentage() const;
+
+ // Return true if this function successfully converts the passed URL
+ // information to a TypedUrlSpecifics structure for writing to the sync DB.
+ static bool WriteToTypedUrlSpecifics(const URLRow& url,
+ const VisitVector& visits,
+ sync_pb::TypedUrlSpecifics* specifics)
+ WARN_UNUSED_RESULT;
+
private:
+ friend class TypedURLSyncBridgeTest;
+
+ typedef std::vector<std::pair<GURL, std::vector<VisitInfo>>>
+ TypedURLVisitVector;
+
+ // This is a helper map used only in Merge functions.
+ typedef std::map<GURL, URLRow> TypedURLMap;
+
+ // This is a helper map used to associate visit vectors from the history db
+ // to the typed urls in the above map |TypedURLMap|.
+ typedef std::map<GURL, VisitVector> URLVisitVectorMap;
+
+ // Bitfield returned from MergeUrls to specify the result of a merge.
+ typedef uint32_t MergeResult;
+ static const MergeResult DIFF_NONE = 0;
+ static const MergeResult DIFF_UPDATE_NODE = 1 << 0;
+ static const MergeResult DIFF_LOCAL_ROW_CHANGED = 1 << 1;
+ static const MergeResult DIFF_LOCAL_VISITS_ADDED = 1 << 2;
+
+ // Merges the URL information in |typed_url| with the URL information from the
+ // history database in |url| and |visits|, and returns a bitmask with the
+ // results of the merge:
+ // DIFF_UPDATE_NODE - changes have been made to |new_url| and |visits| which
+ // should be persisted to the sync node.
+ // DIFF_LOCAL_ROW_CHANGED - The history data in |new_url| should be persisted
+ // to the history DB.
+ // DIFF_LOCAL_VISITS_ADDED - |new_visits| contains a list of visits that
+ // should be written to the history DB for this URL. Deletions are not
+ // written to the DB - each client is left to age out visits on their own.
+ static MergeResult MergeUrls(const sync_pb::TypedUrlSpecifics& typed_url,
+ const history::URLRow& url,
+ history::VisitVector* visits,
+ history::URLRow* new_url,
+ std::vector<history::VisitInfo>* new_visits);
+
+ // Fills |new_url| with formatted data from |typed_url|.
+ static void UpdateURLRowFromTypedUrlSpecifics(
+ const sync_pb::TypedUrlSpecifics& typed_url,
+ history::URLRow* new_url);
+
+ // Synchronously load sync metadata from the TypedURLSyncMetadataDatabase and
+ // pass it to the processor so that it can start tracking changes.
+ void LoadMetadata();
+
+ // Helper function that clears our error counters (used to reset stats after
+ // merge so we can track merge errors separately).
+ void ClearErrorStats();
+
+ // Compares |server_typed_url| from the server against local history to decide
+ // how to merge any existing data, and updates appropriate data containers to
+ // write to server and backend.
+ void UpdateUrlFromServer(const sync_pb::TypedUrlSpecifics& server_typed_url,
+ TypedURLMap* local_typed_urls,
+ URLVisitVectorMap* visit_vectors,
+ history::URLRows* new_synced_urls,
+ TypedURLVisitVector* new_synced_visits,
+ history::URLRows* updated_synced_urls);
+
+ // Writes new typed url data from sync server to history backend.
+ base::Optional<syncer::ModelError> WriteToHistoryBackend(
+ const history::URLRows* new_urls,
+ const history::URLRows* updated_urls,
+ const std::vector<GURL>* deleted_urls,
+ const TypedURLVisitVector* new_visits,
+ const history::VisitVector* deleted_visits);
+
+ // Given a TypedUrlSpecifics object, removes all visits that are older than
+ // the current expiration time. Note that this can result in having no visits
+ // at all.
+ sync_pb::TypedUrlSpecifics FilterExpiredVisits(
+ const sync_pb::TypedUrlSpecifics& specifics);
+
+ // Helper function that determines if we should ignore a URL for the purposes
+ // of sync, because it contains invalid data.
+ bool ShouldIgnoreUrl(const GURL& url);
+
+ // Helper function that determines if we should ignore a URL for the purposes
+ // of sync, based on the visits the URL had.
+ bool ShouldIgnoreVisits(const history::VisitVector& visits);
+
+ // Fetches visits from the history DB corresponding to the passed URL. This
+ // function compensates for the fact that the history DB has rather poor data
+ // integrity (duplicate visits, visit timestamps that don't match the
+ // last_visit timestamp, huge data sets that exhaust memory when fetched,
+ // expired visits that are not deleted by |ExpireHistoryBackend|, etc) by
+ // modifying the passed |url| object and |visits| vector. The order of
+ // |visits| will be from the oldest to the newest order.
+ // Returns false in two cases.
+ // 1. we could not fetch the visits for the passed URL, DB error.
+ // 2. No visits for the passed url, or all the visits are expired.
+ bool FixupURLAndGetVisits(URLRow* url, VisitVector* visits);
+
+ // Create an EntityData by URL |row| and its visits |visits|.
+ std::unique_ptr<syncer::EntityData> CreateEntityData(
+ const URLRow& row,
+ const VisitVector& visits);
+
+ // Get all the typed urls and visits from the history db, after filtering
+ // them, put them into |url_to_visit| and |url_to_urlrow|.
+ // Return false if cannot get urls from HistoryBackend.
+ bool GetValidURLsAndVisits(URLVisitVectorMap* url_to_visit,
+ TypedURLMap* url_to_urlrow);
+
+ // Get URLID from HistoryBackend, and return URLID as storage key.
+ std::string GetStorageKeyInternal(const std::string& url);
+
// A non-owning pointer to the backend, which we're syncing local changes from
// and sync changes to.
HistoryBackend* const history_backend_;
// A non-owning pointer to the database, which is for storing typed urls sync
// metadata and state.
- syncer::SyncMetadataStore* const sync_metadata_store_;
+ TypedURLSyncMetadataDatabase* const sync_metadata_database_;
+
+ // Statistics for the purposes of tracking the percentage of DB accesses that
+ // fail for each client via UMA.
+ int num_db_accesses_;
+ int num_db_errors_;
// Since HistoryBackend use SequencedTaskRunner, so should use SequenceChecker
// here.
base::SequenceChecker sequence_checker_;
+ // Tracks observed history backend, for receiving updates from history
+ // backend.
+ ScopedObserver<history::HistoryBackend, history::HistoryBackendObserver>
+ history_backend_observer_;
+
DISALLOW_COPY_AND_ASSIGN(TypedURLSyncBridge);
};
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
new file mode 100644
index 00000000000..4a36d3aca53
--- /dev/null
+++ b/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc
@@ -0,0 +1,409 @@
+// 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/history/core/browser/typed_url_sync_bridge.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/history/core/browser/history_backend.h"
+#include "components/history/core/browser/history_backend_client.h"
+#include "components/history/core/browser/history_database_params.h"
+#include "components/history/core/browser/in_memory_history_backend.h"
+#include "components/history/core/test/test_history_database.h"
+#include "components/sync/model/data_batch.h"
+#include "components/sync/model/recording_model_type_change_processor.h"
+#include "components/sync/model/sync_metadata_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_pb::TypedUrlSpecifics;
+using syncer::DataBatch;
+using syncer::EntityChange;
+using syncer::EntityChangeList;
+using syncer::EntityData;
+using syncer::EntityDataPtr;
+using syncer::KeyAndData;
+using syncer::MetadataBatch;
+using syncer::RecordingModelTypeChangeProcessor;
+
+namespace history {
+
+namespace {
+
+// Visits with this timestamp are treated as expired.
+const int kExpiredVisit = -1;
+
+// Helper constants for tests.
+const char kTitle[] = "pie";
+const char kTitle2[] = "cookie";
+const char kURL[] = "http://pie.com/";
+const char kURL2[] = "http://cookie.com/";
+
+// Create a new row object and the typed visit çorresponding with the time at
+// |last_visit| in the |visits| vector.
+URLRow MakeTypedUrlRow(const std::string& url,
+ const std::string& title,
+ int typed_count,
+ int64_t last_visit,
+ bool hidden,
+ VisitVector* visits) {
+ // Give each URL a unique ID, to mimic the behavior of the real database.
+ GURL gurl(url);
+ URLRow history_url(gurl);
+ history_url.set_title(base::UTF8ToUTF16(title));
+ history_url.set_typed_count(typed_count);
+ history_url.set_hidden(hidden);
+
+ base::Time last_visit_time = base::Time::FromInternalValue(last_visit);
+ history_url.set_last_visit(last_visit_time);
+
+ if (typed_count > 0) {
+ // Add a typed visit for time |last_visit|.
+ visits->push_back(VisitRow(history_url.id(), last_visit_time, 0,
+ ui::PAGE_TRANSITION_TYPED, 0));
+ } else {
+ // Add a non-typed visit for time |last_visit|.
+ visits->push_back(VisitRow(history_url.id(), last_visit_time, 0,
+ ui::PAGE_TRANSITION_RELOAD, 0));
+ }
+
+ history_url.set_visit_count(visits->size());
+ return history_url;
+}
+
+void VerifyEqual(const TypedUrlSpecifics& s1, const TypedUrlSpecifics& s2) {
+ // Instead of just comparing serialized strings, manually check fields to show
+ // differences on failure.
+ EXPECT_EQ(s1.url(), s2.url());
+ EXPECT_EQ(s1.title(), s2.title());
+ EXPECT_EQ(s1.hidden(), s2.hidden());
+ EXPECT_EQ(s1.visits_size(), s2.visits_size());
+ EXPECT_EQ(s1.visit_transitions_size(), s2.visit_transitions_size());
+ EXPECT_EQ(s1.visits_size(), s1.visit_transitions_size());
+ int size = s1.visits_size();
+ for (int i = 0; i < size; ++i) {
+ EXPECT_EQ(s1.visits(i), s2.visits(i)) << "visits differ at index " << i;
+ EXPECT_EQ(s1.visit_transitions(i), s2.visit_transitions(i))
+ << "visit_transitions differ at index " << i;
+ }
+}
+
+void VerifyDataBatch(std::map<std::string, TypedUrlSpecifics> 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.typed_url());
+ // Removing allows us to verify we don't see the same item multiple times,
+ // and that we saw everything we expected.
+ expected.erase(iter);
+ }
+ EXPECT_TRUE(expected.empty());
+}
+
+class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
+ public:
+ TestHistoryBackendDelegate() {}
+
+ void NotifyProfileError(sql::InitStatus init_status,
+ const std::string& diagnostics) override {}
+ void SetInMemoryBackend(
+ std::unique_ptr<InMemoryHistoryBackend> backend) override {}
+ void NotifyFaviconsChanged(const std::set<GURL>& page_urls,
+ const GURL& icon_url) override {}
+ void NotifyURLVisited(ui::PageTransition transition,
+ const URLRow& row,
+ const RedirectList& redirects,
+ base::Time visit_time) override {}
+ void NotifyURLsModified(const URLRows& changed_urls) override {}
+ void NotifyURLsDeleted(bool all_history,
+ bool expired,
+ const URLRows& deleted_rows,
+ const std::set<GURL>& favicon_urls) override {}
+ void NotifyKeywordSearchTermUpdated(const URLRow& row,
+ KeywordID keyword_id,
+ const base::string16& term) override {}
+ void NotifyKeywordSearchTermDeleted(URLID url_id) override {}
+ void DBLoaded() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestHistoryBackendDelegate);
+};
+
+class TestHistoryBackend : public HistoryBackend {
+ public:
+ TestHistoryBackend()
+ : HistoryBackend(new TestHistoryBackendDelegate(),
+ nullptr,
+ base::ThreadTaskRunnerHandle::Get()) {}
+
+ bool IsExpiredVisitTime(const base::Time& time) override {
+ return time.ToInternalValue() == kExpiredVisit;
+ }
+
+ URLID GetIdByUrl(const GURL& gurl) {
+ return db()->GetRowForURL(gurl, nullptr);
+ }
+
+ void SetVisitsForUrl(URLRow& new_url, const VisitVector visits) {
+ std::vector<history::VisitInfo> added_visits;
+ URLRows new_urls;
+ DeleteURL(new_url.url());
+ for (const auto& visit : visits) {
+ added_visits.push_back(
+ history::VisitInfo(visit.visit_time, visit.transition));
+ }
+ new_urls.push_back(new_url);
+ AddPagesWithDetails(new_urls, history::SOURCE_SYNCED);
+ AddVisits(new_url.url(), added_visits, history::SOURCE_SYNCED);
+ new_url.set_id(GetIdByUrl(new_url.url()));
+ }
+
+ private:
+ ~TestHistoryBackend() override {}
+};
+
+} // namespace
+
+class TypedURLSyncBridgeTest : public testing::Test {
+ public:
+ TypedURLSyncBridgeTest() : typed_url_sync_bridge_(NULL) {}
+ ~TypedURLSyncBridgeTest() override {}
+
+ void SetUp() override {
+ fake_history_backend_ = new TestHistoryBackend();
+ ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
+ fake_history_backend_->Init(
+ false, TestHistoryDatabaseParamsForPath(test_dir_.GetPath()));
+ std::unique_ptr<TypedURLSyncBridge> bridge =
+ base::MakeUnique<TypedURLSyncBridge>(
+ fake_history_backend_.get(), fake_history_backend_->db(),
+ RecordingModelTypeChangeProcessor::FactoryForBridgeTest(&processor_,
+ false));
+ typed_url_sync_bridge_ = bridge.get();
+ typed_url_sync_bridge_->Init();
+ typed_url_sync_bridge_->history_backend_observer_.RemoveAll();
+ fake_history_backend_->SetTypedURLSyncBridgeForTest(std::move(bridge));
+ }
+
+ void TearDown() override { fake_history_backend_->Closing(); }
+
+ // Starts sync for |typed_url_sync_bridge_| with |initial_data| as the
+ // initial sync data.
+ void StartSyncing(const std::vector<TypedUrlSpecifics>& specifics) {
+ // Set change processor.
+ const auto error =
+ bridge()->MergeSyncData(bridge()->CreateMetadataChangeList(),
+ CreateEntityChangeList(specifics));
+
+ EXPECT_FALSE(error);
+ }
+
+ // Fills |specifics| with the sync data for |url| and |visits|.
+ static bool WriteToTypedUrlSpecifics(const URLRow& url,
+ const VisitVector& visits,
+ TypedUrlSpecifics* specifics) {
+ return TypedURLSyncBridge::WriteToTypedUrlSpecifics(url, visits, specifics);
+ }
+
+ std::string GetStorageKey(const TypedUrlSpecifics& specifics) {
+ std::string key = bridge()->GetStorageKeyInternal(specifics.url());
+ return key;
+ }
+
+ EntityDataPtr SpecificsToEntity(const TypedUrlSpecifics& specifics) {
+ EntityData data;
+ data.client_tag_hash = "ignored";
+ *data.specifics.mutable_typed_url() = specifics;
+ return data.PassToPtr();
+ }
+
+ EntityChangeList CreateEntityChangeList(
+ const std::vector<TypedUrlSpecifics>& specifics_vector) {
+ EntityChangeList entity_change_list;
+ for (const auto& specifics : specifics_vector) {
+ entity_change_list.push_back(EntityChange::CreateAdd(
+ GetStorageKey(specifics), SpecificsToEntity(specifics)));
+ }
+ return entity_change_list;
+ }
+
+ std::map<std::string, TypedUrlSpecifics> ExpectedMap(
+ const std::vector<TypedUrlSpecifics>& specifics_vector) {
+ std::map<std::string, TypedUrlSpecifics> map;
+ for (const auto& specifics : specifics_vector) {
+ map[GetStorageKey(specifics)] = specifics;
+ }
+ return map;
+ }
+
+ void VerifyLocalHistoryData(const std::vector<TypedUrlSpecifics>& expected) {
+ bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
+ }
+
+ TypedURLSyncBridge* bridge() { return typed_url_sync_bridge_; }
+
+ TypedURLSyncMetadataDatabase* metadata_store() {
+ return bridge()->sync_metadata_database_;
+ }
+
+ const RecordingModelTypeChangeProcessor& processor() { return *processor_; }
+
+ protected:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir test_dir_;
+ scoped_refptr<TestHistoryBackend> fake_history_backend_;
+ TypedURLSyncBridge* typed_url_sync_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;
+};
+
+// Add two typed urls locally and verify bridge can get them from GetAllData.
+TEST_F(TypedURLSyncBridgeTest, GetAllData) {
+ // Add two urls to backend.
+ VisitVector visits1, visits2;
+ URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1);
+ URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, 2, 4, false, &visits2);
+ fake_history_backend_->SetVisitsForUrl(row1, visits1);
+ fake_history_backend_->SetVisitsForUrl(row2, visits2);
+
+ // Create the same data in sync.
+ TypedUrlSpecifics typed_url1, typed_url2;
+ WriteToTypedUrlSpecifics(row1, visits1, &typed_url1);
+ WriteToTypedUrlSpecifics(row2, visits2, &typed_url2);
+
+ // Check that the local cache is still correct.
+ VerifyLocalHistoryData({typed_url1, typed_url2});
+}
+
+// Add a typed url locally and one to sync with the same data. Starting sync
+// should result in no changes.
+TEST_F(TypedURLSyncBridgeTest, MergeUrlNoChange) {
+ // Add a url to backend.
+ VisitVector visits;
+ URLRow row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
+ fake_history_backend_->SetVisitsForUrl(row, visits);
+
+ // Create the same data in sync.
+ sync_pb::EntitySpecifics entity_specifics;
+ sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
+ WriteToTypedUrlSpecifics(row, visits, typed_url);
+
+ StartSyncing({*typed_url});
+ EXPECT_TRUE(processor().put_multimap().empty());
+
+ // Check that the local cache was is still correct.
+ VerifyLocalHistoryData({*typed_url});
+}
+
+// Add a corupted typed url locally, has typed url count 1, but no real typed
+// url visit. Starting sync should not pick up this url.
+TEST_F(TypedURLSyncBridgeTest, MergeUrlNoTypedUrl) {
+ // Add a url to backend.
+ VisitVector visits;
+ URLRow row = MakeTypedUrlRow(kURL, kTitle, 0, 3, false, &visits);
+
+ // Mark typed_count to 1 even when there is no typed url visit.
+ row.set_typed_count(1);
+ fake_history_backend_->SetVisitsForUrl(row, visits);
+
+ StartSyncing(std::vector<TypedUrlSpecifics>());
+ EXPECT_TRUE(processor().put_multimap().empty());
+
+ MetadataBatch metadata_batch;
+ metadata_store()->GetAllSyncMetadata(&metadata_batch);
+ EXPECT_EQ(0u, metadata_batch.TakeAllMetadata().size());
+}
+
+// Starting sync with no sync data should just push the local url to sync.
+TEST_F(TypedURLSyncBridgeTest, MergeUrlEmptySync) {
+ // Add a url to backend.
+ VisitVector visits;
+ URLRow row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
+ fake_history_backend_->SetVisitsForUrl(row, visits);
+
+ StartSyncing(std::vector<TypedUrlSpecifics>());
+
+ // Check that the local cache was is still correct.
+ sync_pb::EntitySpecifics entity_specifics;
+ sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
+ WriteToTypedUrlSpecifics(row, visits, typed_url);
+ VerifyLocalHistoryData({*typed_url});
+
+ // Check that the server was updated correctly.
+ ASSERT_EQ(1U, processor().put_multimap().size());
+ auto recorded_specifics_iterator =
+ processor().put_multimap().find(GetStorageKey(*typed_url));
+ EXPECT_NE(processor().put_multimap().end(), recorded_specifics_iterator);
+ TypedUrlSpecifics recorded_specifics =
+ recorded_specifics_iterator->second->specifics.typed_url();
+
+ ASSERT_EQ(1, recorded_specifics.visits_size());
+ EXPECT_EQ(3, recorded_specifics.visits(0));
+ ASSERT_EQ(1, recorded_specifics.visit_transitions_size());
+ EXPECT_EQ(static_cast<const int>(visits[0].transition),
+ recorded_specifics.visit_transitions(0));
+}
+
+// Starting sync with no local data should just push the synced url into the
+// backend.
+TEST_F(TypedURLSyncBridgeTest, MergeUrlEmptyLocal) {
+ // Create the sync data.
+ VisitVector visits;
+ URLRow row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
+ sync_pb::EntitySpecifics entity_specifics;
+ sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
+ WriteToTypedUrlSpecifics(row, visits, typed_url);
+
+ StartSyncing({*typed_url});
+ EXPECT_EQ(0u, processor().put_multimap().size());
+
+ // Check that the backend was updated correctly.
+ VisitVector all_visits;
+ base::Time server_time = base::Time::FromInternalValue(3);
+ URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
+ ASSERT_NE(0, url_id);
+ fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
+ ASSERT_EQ(1U, all_visits.size());
+ EXPECT_EQ(server_time, all_visits[0].visit_time);
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ all_visits[0].transition, visits[0].transition));
+}
+
+// Starting sync with both local and sync have same typed URL, but different
+// visit. After merge, both local and sync should have two same visits.
+TEST_F(TypedURLSyncBridgeTest, SimpleMerge) {
+ // Add a url to backend.
+ VisitVector visits1;
+ URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1);
+ fake_history_backend_->SetVisitsForUrl(row1, visits1);
+
+ // Create the sync data.
+ VisitVector visits2;
+ URLRow row2 = MakeTypedUrlRow(kURL, kTitle, 1, 4, false, &visits2);
+ sync_pb::EntitySpecifics entity_specifics;
+ sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
+ WriteToTypedUrlSpecifics(row2, visits2, typed_url);
+
+ StartSyncing({*typed_url});
+ EXPECT_EQ(1u, processor().put_multimap().size());
+
+ // Check that the backend was updated correctly.
+ VisitVector all_visits;
+ URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
+ ASSERT_NE(0, url_id);
+ fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
+ ASSERT_EQ(2U, all_visits.size());
+ EXPECT_EQ(base::Time::FromInternalValue(3), all_visits[0].visit_time);
+ EXPECT_EQ(base::Time::FromInternalValue(4), all_visits[1].visit_time);
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ all_visits[0].transition, visits1[0].transition));
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ all_visits[1].transition, visits2[0].transition));
+}
+
+} // namespace history
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 f0b2ce6ddf3..81e9da0a8e9 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
@@ -4,8 +4,9 @@
#include "components/history/core/browser/typed_url_sync_metadata_database.h"
+#include "base/big_endian.h"
#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
+#include "components/history/core/browser/url_row.h"
#include "sql/statement.h"
namespace history {
@@ -55,9 +56,11 @@ bool TypedURLSyncMetadataDatabase::UpdateSyncMetadata(
<< "Only the TYPED_URLS model type is supported";
int64_t storage_key_int = 0;
- if (!base::StringToInt64(storage_key, &storage_key_int)) {
- return false;
- }
+ DCHECK_EQ(storage_key.size(), sizeof(storage_key_int));
+ base::ReadBigEndian(storage_key.data(), &storage_key_int);
+ // Make sure storage_key_int is set.
+ DCHECK_NE(storage_key_int, 0);
+
sql::Statement s(GetDB().GetUniqueStatement(
"INSERT OR REPLACE INTO typed_url_sync_metadata "
"(storage_key, value) VALUES(?, ?)"));
@@ -74,9 +77,11 @@ bool TypedURLSyncMetadataDatabase::ClearSyncMetadata(
<< "Only the TYPED_URLS model type is supported";
int64_t storage_key_int = 0;
- if (!base::StringToInt64(storage_key, &storage_key_int)) {
- return false;
- }
+ DCHECK_EQ(storage_key.size(), sizeof(storage_key_int));
+ base::ReadBigEndian(storage_key.data(), &storage_key_int);
+ // Make sure storage_key_int is set.
+ DCHECK_NE(storage_key_int, 0);
+
sql::Statement s(GetDB().GetUniqueStatement(
"DELETE FROM typed_url_sync_metadata WHERE storage_key=?"));
s.BindInt64(0, storage_key_int);
@@ -122,7 +127,8 @@ bool TypedURLSyncMetadataDatabase::GetAllSyncEntityMetadata(
"SELECT storage_key, value FROM typed_url_sync_metadata"));
while (s.Step()) {
- std::string storage_key = base::Int64ToString(s.ColumnInt64(0));
+ std::string storage_key(sizeof(URLID), 0);
+ base::WriteBigEndian<URLID>(&storage_key[0], s.ColumnInt64(0));
std::string serialized_metadata = s.ColumnString(1);
sync_pb::EntityMetadata entity_metadata;
if (entity_metadata.ParseFromString(serialized_metadata)) {
diff --git a/chromium/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc b/chromium/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc
index c37bf3eeb7e..ec0f22f11da 100644
--- a/chromium/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc
+++ b/chromium/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc
@@ -4,8 +4,10 @@
#include "components/history/core/browser/typed_url_sync_metadata_database.h"
+#include "base/big_endian.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
+#include "components/history/core/browser/url_row.h"
#include "components/sync/protocol/model_type_state.pb.h"
#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -17,6 +19,16 @@ using syncer::MetadataBatch;
namespace history {
+namespace {
+
+std::string IntToStorageKey(int i) {
+ std::string storage_key(sizeof(URLID), 0);
+ base::WriteBigEndian<URLID>(&storage_key[0], i);
+ return storage_key;
+}
+
+} // namespace
+
class TypedURLSyncMetadataDatabaseTest : public testing::Test,
public TypedURLSyncMetadataDatabase {
public:
@@ -60,8 +72,8 @@ TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLNoMetadata) {
TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLGetAllSyncMetadata) {
EntityMetadata metadata;
- std::string storage_key = "1";
- std::string storage_key2 = "2";
+ std::string storage_key = IntToStorageKey(1);
+ std::string storage_key2 = IntToStorageKey(2);
metadata.set_sequence_number(1);
EXPECT_TRUE(UpdateSyncMetadata(syncer::TYPED_URLS, storage_key, metadata));
@@ -96,7 +108,7 @@ TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLGetAllSyncMetadata) {
TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLWriteThenDeleteSyncMetadata) {
EntityMetadata metadata;
MetadataBatch metadata_batch;
- std::string storage_key = "1";
+ std::string storage_key = IntToStorageKey(1);
ModelTypeState model_type_state;
model_type_state.set_initial_sync_done(true);
diff --git a/chromium/components/history/core/browser/typed_url_syncable_service_unittest.cc b/chromium/components/history/core/browser/typed_url_syncable_service_unittest.cc
index 936739677d4..844b182be35 100644
--- a/chromium/components/history/core/browser/typed_url_syncable_service_unittest.cc
+++ b/chromium/components/history/core/browser/typed_url_syncable_service_unittest.cc
@@ -154,23 +154,23 @@ class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
void NotifyProfileError(sql::InitStatus init_status,
const std::string& diagnostics) override {}
void SetInMemoryBackend(
- std::unique_ptr<InMemoryHistoryBackend> backend) override{};
+ std::unique_ptr<InMemoryHistoryBackend> backend) override {}
void NotifyFaviconsChanged(const std::set<GURL>& page_urls,
- const GURL& icon_url) override{};
+ const GURL& icon_url) override {}
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
const RedirectList& redirects,
- base::Time visit_time) override{};
- void NotifyURLsModified(const URLRows& changed_urls) override{};
+ base::Time visit_time) override {}
+ void NotifyURLsModified(const URLRows& changed_urls) override {}
void NotifyURLsDeleted(bool all_history,
bool expired,
const URLRows& deleted_rows,
- const std::set<GURL>& favicon_urls) override{};
+ const std::set<GURL>& favicon_urls) override {}
void NotifyKeywordSearchTermUpdated(const URLRow& row,
KeywordID keyword_id,
- const base::string16& term) override{};
- void NotifyKeywordSearchTermDeleted(URLID url_id) override{};
- void DBLoaded() override{};
+ const base::string16& term) override {}
+ void NotifyKeywordSearchTermDeleted(URLID url_id) override {}
+ void DBLoaded() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TestHistoryBackendDelegate);
diff --git a/chromium/components/history/core/browser/web_history_service.cc b/chromium/components/history/core/browser/web_history_service.cc
index 0ce75efc45b..f2516b3010d 100644
--- a/chromium/components/history/core/browser/web_history_service.cc
+++ b/chromium/components/history/core/browser/web_history_service.cc
@@ -5,6 +5,7 @@
#include "components/history/core/browser/web_history_service.h"
#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
@@ -89,7 +90,8 @@ class RequestImpl : public WebHistoryService::Request,
SigninManagerBase* signin_manager,
const scoped_refptr<net::URLRequestContextGetter>& request_context,
const GURL& url,
- const WebHistoryService::CompletionCallback& callback)
+ const WebHistoryService::CompletionCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation)
: OAuth2TokenService::Consumer("web_history"),
token_service_(token_service),
signin_manager_(signin_manager),
@@ -99,7 +101,8 @@ class RequestImpl : public WebHistoryService::Request,
response_code_(0),
auth_retry_count_(0),
callback_(callback),
- is_pending_(false) {
+ is_pending_(false),
+ partial_traffic_annotation_(partial_traffic_annotation) {
DCHECK(token_service_);
DCHECK(signin_manager_);
DCHECK(request_context_);
@@ -177,8 +180,23 @@ class RequestImpl : public WebHistoryService::Request,
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: false
+ 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);
+ net::URLFetcher::Create(url_, request_type, this, traffic_annotation);
data_use_measurement::DataUseUserData::AttachToFetcher(
fetcher.get(),
data_use_measurement::DataUseUserData::WEB_HISTORY_SERVICE);
@@ -255,6 +273,10 @@ 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
+ // request.
+ const net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation_;
};
// Converts a time into a string for use as a parameter in a request to the
@@ -356,9 +378,10 @@ void WebHistoryService::RemoveObserver(WebHistoryServiceObserver* observer) {
WebHistoryService::Request* WebHistoryService::CreateRequest(
const GURL& url,
- const CompletionCallback& callback) {
+ const CompletionCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
return new RequestImpl(token_service_, signin_manager_, request_context_, url,
- callback);
+ callback, partial_traffic_annotation);
}
// static
@@ -379,20 +402,23 @@ std::unique_ptr<base::DictionaryValue> WebHistoryService::ReadResponse(
std::unique_ptr<WebHistoryService::Request> WebHistoryService::QueryHistory(
const base::string16& text_query,
const QueryOptions& options,
- const WebHistoryService::QueryWebHistoryCallback& callback) {
+ const WebHistoryService::QueryWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
// Wrap the original callback into a generic completion callback.
CompletionCallback completion_callback = base::Bind(
&WebHistoryService::QueryHistoryCompletionCallback, callback);
GURL url = GetQueryUrl(text_query, options, server_version_info_);
- std::unique_ptr<Request> request(CreateRequest(url, completion_callback));
+ std::unique_ptr<Request> request(
+ CreateRequest(url, completion_callback, partial_traffic_annotation));
request->Start();
return request;
}
void WebHistoryService::ExpireHistory(
const std::vector<ExpireHistoryArgs>& expire_list,
- const ExpireWebHistoryCallback& callback) {
+ const ExpireWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
base::DictionaryValue delete_request;
std::unique_ptr<base::ListValue> deletions(new base::ListValue);
base::Time now = base::Time::Now();
@@ -414,7 +440,7 @@ void WebHistoryService::ExpireHistory(
if (expire.urls.empty())
deletions->Append(CreateDeletion(min_timestamp, max_timestamp, GURL()));
}
- delete_request.Set("del", deletions.release());
+ delete_request.Set("del", std::move(deletions));
std::string post_data;
base::JSONWriter::Write(delete_request, &post_data);
@@ -431,7 +457,8 @@ void WebHistoryService::ExpireHistory(
weak_ptr_factory_.GetWeakPtr(),
callback);
- std::unique_ptr<Request> request(CreateRequest(url, completion_callback));
+ std::unique_ptr<Request> request(
+ CreateRequest(url, completion_callback, partial_traffic_annotation));
request->SetPostData(post_data);
Request* request_ptr = request.get();
pending_expire_requests_[request_ptr] = std::move(request);
@@ -442,16 +469,18 @@ void WebHistoryService::ExpireHistoryBetween(
const std::set<GURL>& restrict_urls,
base::Time begin_time,
base::Time end_time,
- const ExpireWebHistoryCallback& callback) {
+ const ExpireWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
std::vector<ExpireHistoryArgs> expire_list(1);
expire_list.back().urls = restrict_urls;
expire_list.back().begin_time = begin_time;
expire_list.back().end_time = end_time;
- ExpireHistory(expire_list, callback);
+ ExpireHistory(expire_list, callback, partial_traffic_annotation);
}
void WebHistoryService::GetAudioHistoryEnabled(
- const AudioWebHistoryCallback& callback) {
+ const AudioWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
// Wrap the original callback into a generic completion callback.
CompletionCallback completion_callback =
base::Bind(&WebHistoryService::AudioHistoryCompletionCallback,
@@ -459,15 +488,18 @@ void WebHistoryService::GetAudioHistoryEnabled(
callback);
GURL url(kHistoryAudioHistoryUrl);
- std::unique_ptr<Request> request(CreateRequest(url, completion_callback));
+
+ std::unique_ptr<Request> request(
+ CreateRequest(url, completion_callback, partial_traffic_annotation));
request->Start();
Request* request_ptr = request.get();
pending_audio_history_requests_[request_ptr] = std::move(request);
}
void WebHistoryService::SetAudioHistoryEnabled(
- bool new_enabled_value,
- const AudioWebHistoryCallback& callback) {
+ bool new_enabled_value,
+ const AudioWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
// Wrap the original callback into a generic completion callback.
CompletionCallback completion_callback =
base::Bind(&WebHistoryService::AudioHistoryCompletionCallback,
@@ -475,7 +507,8 @@ void WebHistoryService::SetAudioHistoryEnabled(
callback);
GURL url(kHistoryAudioHistoryChangeUrl);
- std::unique_ptr<Request> request(CreateRequest(url, completion_callback));
+ std::unique_ptr<Request> request(
+ CreateRequest(url, completion_callback, partial_traffic_annotation));
base::DictionaryValue enable_audio_history;
enable_audio_history.SetBoolean("enable_history_recording",
@@ -495,7 +528,8 @@ size_t WebHistoryService::GetNumberOfPendingAudioHistoryRequests() {
}
void WebHistoryService::QueryWebAndAppActivity(
- const QueryWebAndAppActivityCallback& callback) {
+ const QueryWebAndAppActivityCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
// Wrap the original callback into a generic completion callback.
CompletionCallback completion_callback =
base::Bind(&WebHistoryService::QueryWebAndAppActivityCompletionCallback,
@@ -503,14 +537,16 @@ void WebHistoryService::QueryWebAndAppActivity(
callback);
GURL url(kQueryWebAndAppActivityUrl);
- Request* request = CreateRequest(url, completion_callback);
+ Request* request =
+ CreateRequest(url, completion_callback, partial_traffic_annotation);
pending_web_and_app_activity_requests_[request] = base::WrapUnique(request);
request->Start();
}
void WebHistoryService::QueryOtherFormsOfBrowsingHistory(
version_info::Channel channel,
- const QueryOtherFormsOfBrowsingHistoryCallback& callback) {
+ const QueryOtherFormsOfBrowsingHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
// Wrap the original callback into a generic completion callback.
CompletionCallback completion_callback = base::Bind(
&WebHistoryService::QueryOtherFormsOfBrowsingHistoryCompletionCallback,
@@ -527,7 +563,8 @@ void WebHistoryService::QueryOtherFormsOfBrowsingHistory(
url = url.ReplaceComponents(replace_path);
DCHECK(url.is_valid());
- Request* request = CreateRequest(url, completion_callback);
+ Request* request =
+ CreateRequest(url, completion_callback, partial_traffic_annotation);
// Set the Sync-specific user agent.
std::string user_agent = syncer::MakeUserAgentForSync(
diff --git a/chromium/components/history/core/browser/web_history_service.h b/chromium/components/history/core/browser/web_history_service.h
index b25ad05b8a2..3016fad0e91 100644
--- a/chromium/components/history/core/browser/web_history_service.h
+++ b/chromium/components/history/core/browser/web_history_service.h
@@ -19,6 +19,7 @@
#include "base/observer_list.h"
#include "components/history/core/browser/history_types.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
namespace base {
class DictionaryValue;
@@ -112,30 +113,44 @@ class WebHistoryService : public KeyedService {
std::unique_ptr<Request> QueryHistory(
const base::string16& text_query,
const QueryOptions& options,
- const QueryWebHistoryCallback& callback);
+ const QueryWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Removes all visits to specified URLs in specific time ranges.
// This is the of equivalent HistoryService::ExpireHistory().
void ExpireHistory(const std::vector<ExpireHistoryArgs>& expire_list,
- const ExpireWebHistoryCallback& callback);
+ const ExpireWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Removes all visits to specified URLs in the given time range.
// This is the of equivalent HistoryService::ExpireHistoryBetween().
void ExpireHistoryBetween(const std::set<GURL>& restrict_urls,
base::Time begin_time,
base::Time end_time,
- const ExpireWebHistoryCallback& callback);
+ const ExpireWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Requests whether audio history recording is enabled.
- virtual void GetAudioHistoryEnabled(const AudioWebHistoryCallback& callback);
+ virtual void GetAudioHistoryEnabled(
+ const AudioWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Sets the state of audio history recording to |new_enabled_value|.
- virtual void SetAudioHistoryEnabled(bool new_enabled_value,
- const AudioWebHistoryCallback& callback);
+ virtual void SetAudioHistoryEnabled(
+ bool new_enabled_value,
+ const AudioWebHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Queries whether web and app activity is enabled on the server.
virtual void QueryWebAndAppActivity(
- const QueryWebAndAppActivityCallback& callback);
+ const QueryWebAndAppActivityCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Used for tests.
size_t GetNumberOfPendingAudioHistoryRequests();
@@ -143,13 +158,17 @@ class WebHistoryService : public KeyedService {
// Whether there are other forms of browsing history stored on the server.
void QueryOtherFormsOfBrowsingHistory(
version_info::Channel channel,
- const QueryOtherFormsOfBrowsingHistoryCallback& callback);
+ const QueryOtherFormsOfBrowsingHistoryCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
protected:
// This function is pulled out for testing purposes. Caller takes ownership of
// the new Request.
virtual Request* CreateRequest(const GURL& url,
- const CompletionCallback& callback);
+ const CompletionCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag&
+ partial_traffic_annotation);
// Extracts a JSON-encoded HTTP response into a DictionaryValue.
// If |request|'s HTTP response code indicates failure, or if the response
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 7d528f3bcf3..f20cb048ad1 100644
--- a/chromium/components/history/core/browser/web_history_service_unittest.cc
+++ b/chromium/components/history/core/browser/web_history_service_unittest.cc
@@ -17,6 +17,7 @@
#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/signin/core/browser/test_signin_client.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 "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -42,7 +43,10 @@ class TestingWebHistoryService : public WebHistoryService {
~TestingWebHistoryService() override {}
WebHistoryService::Request* CreateRequest(
- const GURL& url, const CompletionCallback& callback) override;
+ const GURL& url,
+ const CompletionCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation)
+ override;
// This is sorta an override but override and static don't mix.
// This function just calls WebHistoryService::ReadResponse.
@@ -156,7 +160,9 @@ class TestRequest : public WebHistoryService::Request {
};
WebHistoryService::Request* TestingWebHistoryService::CreateRequest(
- const GURL& url, const CompletionCallback& callback) {
+ const GURL& url,
+ const CompletionCallback& callback,
+ const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
EXPECT_EQ(expected_url_, url);
WebHistoryService::Request* request =
new TestRequest(url, callback, this);
@@ -249,7 +255,8 @@ TEST_F(WebHistoryServiceTest, GetAudioHistoryEnabled) {
web_history_service()->SetExpectedAudioHistoryValue(true);
web_history_service()->GetAudioHistoryEnabled(
base::Bind(&TestingWebHistoryService::GetAudioHistoryCallback,
- base::Unretained(web_history_service())));
+ base::Unretained(web_history_service())),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&TestingWebHistoryService::EnsureNoPendingRequestsRemain,
@@ -263,8 +270,10 @@ TEST_F(WebHistoryServiceTest, SetAudioHistoryEnabledTrue) {
web_history_service()->SetExpectedPostData(
"{\"client\":\"audio\",\"enable_history_recording\":true}");
web_history_service()->SetAudioHistoryEnabled(
- true, base::Bind(&TestingWebHistoryService::SetAudioHistoryCallback,
- base::Unretained(web_history_service())));
+ true,
+ base::Bind(&TestingWebHistoryService::SetAudioHistoryCallback,
+ base::Unretained(web_history_service())),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&TestingWebHistoryService::EnsureNoPendingRequestsRemain,
@@ -278,8 +287,10 @@ TEST_F(WebHistoryServiceTest, SetAudioHistoryEnabledFalse) {
web_history_service()->SetExpectedPostData(
"{\"client\":\"audio\",\"enable_history_recording\":false}");
web_history_service()->SetAudioHistoryEnabled(
- false, base::Bind(&TestingWebHistoryService::SetAudioHistoryCallback,
- base::Unretained(web_history_service())));
+ false,
+ base::Bind(&TestingWebHistoryService::SetAudioHistoryCallback,
+ base::Unretained(web_history_service())),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&TestingWebHistoryService::EnsureNoPendingRequestsRemain,
@@ -293,15 +304,18 @@ TEST_F(WebHistoryServiceTest, MultipleRequests) {
web_history_service()->SetExpectedPostData(
"{\"client\":\"audio\",\"enable_history_recording\":false}");
web_history_service()->SetAudioHistoryEnabled(
- false, base::Bind(&TestingWebHistoryService::MultipleRequestsCallback,
- base::Unretained(web_history_service())));
+ false,
+ base::Bind(&TestingWebHistoryService::MultipleRequestsCallback,
+ base::Unretained(web_history_service())),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
web_history_service()->SetExpectedURL(
GURL("https://history.google.com/history/api/lookup?client=audio"));
web_history_service()->SetExpectedPostData("");
web_history_service()->GetAudioHistoryEnabled(
base::Bind(&TestingWebHistoryService::MultipleRequestsCallback,
- base::Unretained(web_history_service())));
+ base::Unretained(web_history_service())),
+ PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
// Check that both requests are no longer pending.
base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chromium/components/history_strings.grdp b/chromium/components/history_strings.grdp
index f7da1b48b42..6864dc59ebf 100644
--- a/chromium/components/history_strings.grdp
+++ b/chromium/components/history_strings.grdp
@@ -45,7 +45,7 @@
More from this site
</message>
<message name="IDS_HISTORY_NO_RESULTS" desc="Text shown when no history entries are found.">
- No history entries found
+ Your browsing history appears here
</message>
<message name="IDS_HISTORY_NO_SEARCH_RESULTS" desc="Text shown when no history search results have been found">
No search results found
diff --git a/chromium/components/image_fetcher/ios/DEPS b/chromium/components/image_fetcher/ios/DEPS
index dc46f3b0b57..27c935d0899 100644
--- a/chromium/components/image_fetcher/ios/DEPS
+++ b/chromium/components/image_fetcher/ios/DEPS
@@ -3,5 +3,5 @@ include_rules = [
"+ui/gfx",
# Only WebP decoding is allowed (no encoding).
- "+third_party/libwebp/webp/decode.h",
+ "+third_party/libwebp/src/webp/decode.h",
]
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 ba32199f114..38edf0b6fde 100644
--- a/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm
+++ b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm
@@ -66,16 +66,20 @@ void IOSImageDecoderImpl::DecodeImage(const std::string& image_data,
// The WebP image format is not supported by iOS natively. Therefore WebP
// images need to be decoded explicitly,
+ NSData* (^decodeBlock)();
if (webp_transcode::WebpDecoder::IsWebpImage(image_data)) {
- base::PostTaskAndReplyWithResult(
- task_runner_.get(), FROM_HERE, base::BindBlockArc(^NSData*() {
- return webp_transcode::WebpDecoder::DecodeWebpImage(data);
- }),
- base::Bind(&IOSImageDecoderImpl::CreateUIImageAndRunCallback,
- weak_factory_.GetWeakPtr(), callback));
+ decodeBlock = ^NSData*() {
+ return webp_transcode::WebpDecoder::DecodeWebpImage(data);
+ };
} else {
- CreateUIImageAndRunCallback(callback, data);
+ // Use block to prevent |data| from being released.
+ decodeBlock = ^NSData*() { return data; };
}
+
+ base::PostTaskAndReplyWithResult(
+ task_runner_.get(), FROM_HERE, base::BindBlockArc(decodeBlock),
+ base::Bind(&IOSImageDecoderImpl::CreateUIImageAndRunCallback,
+ weak_factory_.GetWeakPtr(), callback));
}
void IOSImageDecoderImpl::CreateUIImageAndRunCallback(
diff --git a/chromium/components/image_fetcher/ios/webp_decoder.h b/chromium/components/image_fetcher/ios/webp_decoder.h
index 214c7a3afda..f498e8197e4 100644
--- a/chromium/components/image_fetcher/ios/webp_decoder.h
+++ b/chromium/components/image_fetcher/ios/webp_decoder.h
@@ -12,7 +12,7 @@
#import "base/mac/scoped_nsobject.h"
#include "base/memory/ref_counted.h"
-#include "third_party/libwebp/webp/decode.h"
+#include "third_party/libwebp/src/webp/decode.h"
@class NSData;
diff --git a/chromium/components/infobars/core/infobar_delegate.h b/chromium/components/infobars/core/infobar_delegate.h
index 4de05e5c7b0..1405106b3c7 100644
--- a/chromium/components/infobars/core/infobar_delegate.h
+++ b/chromium/components/infobars/core/infobar_delegate.h
@@ -122,9 +122,9 @@ class InfoBarDelegate {
AUTOFILL_CC_INFOBAR_DELEGATE = 49,
TRANSLATE_INFOBAR_DELEGATE = 50,
IOS_CHROME_SAVE_PASSWORD_INFOBAR_DELEGATE = 51,
- NATIVE_APP_INSTALLER_INFOBAR_DELEGATE = 52,
- NATIVE_APP_LAUNCHER_INFOBAR_DELEGATE = 53,
- NATIVE_APP_OPEN_POLICY_INFOBAR_DELEGATE = 54,
+ // Removed: NATIVE_APP_INSTALLER_INFOBAR_DELEGATE = 52,
+ // Removed: NATIVE_APP_LAUNCHER_INFOBAR_DELEGATE = 53,
+ // Removed: NATIVE_APP_OPEN_POLICY_INFOBAR_DELEGATE = 54,
RE_SIGN_IN_INFOBAR_DELEGATE = 55,
SHOW_PASSKIT_INFOBAR_ERROR_DELEGATE = 56,
READER_MODE_INFOBAR_DELEGATE_IOS = 57,
@@ -136,7 +136,7 @@ class InfoBarDelegate {
UPDATE_PASSWORD_INFOBAR_DELEGATE = 63,
DATA_REDUCTION_PROMO_INFOBAR_DELEGATE_ANDROID = 64,
AUTOFILL_CREDIT_CARD_FILLING_INFOBAR_DELEGATE_ANDROID = 65,
- SUBRESOURCE_FILTER_INFOBAR_DELEGATE_ANDROID = 66,
+ ADS_BLOCKED_INFOBAR_DELEGATE_ANDROID = 66,
INSTANT_APPS_INFOBAR_DELEGATE_ANDROID = 67,
DATA_REDUCTION_PROXY_PREVIEW_INFOBAR_DELEGATE = 68,
SCREEN_CAPTURE_INFOBAR_DELEGATE_ANDROID = 69,
diff --git a/chromium/components/infobars/core/infobar_manager.h b/chromium/components/infobars/core/infobar_manager.h
index 9097dfc2d75..fb8cbcf846e 100644
--- a/chromium/components/infobars/core/infobar_manager.h
+++ b/chromium/components/infobars/core/infobar_manager.h
@@ -118,10 +118,10 @@ class InfoBarManager {
private:
// InfoBars associated with this InfoBarManager. We own these pointers.
- // However, this is not a ScopedVector, because we don't delete the infobars
- // directly once they've been added to this; instead, when we're done with an
- // infobar, we instruct it to delete itself and then orphan it. See
- // RemoveInfoBarInternal().
+ // However, this is not a vector of unique_ptr, because we don't delete the
+ // infobars directly once they've been added to this; instead, when we're
+ // done with an infobar, we instruct it to delete itself and then orphan it.
+ // See RemoveInfoBarInternal().
typedef std::vector<InfoBar*> InfoBars;
void RemoveInfoBarInternal(InfoBar* infobar, bool animate);
diff --git a/chromium/components/invalidation/impl/BUILD.gn b/chromium/components/invalidation/impl/BUILD.gn
index 4b782332d26..9b74d43f16b 100644
--- a/chromium/components/invalidation/impl/BUILD.gn
+++ b/chromium/components/invalidation/impl/BUILD.gn
@@ -89,8 +89,6 @@ static_library("impl") {
if (is_android) {
sources += [
- "android/component_jni_registrar.cc",
- "android/component_jni_registrar.h",
"invalidation_service_android.cc",
"invalidation_service_android.h",
]
diff --git a/chromium/components/json_schema/json_schema_validator_unittest_base.cc b/chromium/components/json_schema/json_schema_validator_unittest_base.cc
index d49bb0679c3..2b03abe6134 100644
--- a/chromium/components/json_schema/json_schema_validator_unittest_base.cc
+++ b/chromium/components/json_schema/json_schema_validator_unittest_base.cc
@@ -255,9 +255,9 @@ void JSONSchemaValidatorTestBase::TestObject() {
schema->Remove(schema::kPatternProperties, NULL);
// Test additional properties.
- base::DictionaryValue* additional_properties = new base::DictionaryValue();
+ base::DictionaryValue* additional_properties = schema->SetDictionary(
+ schema::kAdditionalProperties, base::MakeUnique<base::DictionaryValue>());
additional_properties->SetString(schema::kType, schema::kAny);
- schema->Set(schema::kAdditionalProperties, additional_properties);
instance->SetBoolean("extra", true);
ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
@@ -406,9 +406,9 @@ void JSONSchemaValidatorTestBase::TestArrayTuple() {
schema::kString,
schema::kInteger));
- base::DictionaryValue* additional_properties = new base::DictionaryValue();
+ base::DictionaryValue* additional_properties = schema->SetDictionary(
+ schema::kAdditionalProperties, base::MakeUnique<base::DictionaryValue>());
additional_properties->SetString(schema::kType, schema::kAny);
- schema->Set(schema::kAdditionalProperties, additional_properties);
instance->Set(0, base::MakeUnique<base::Value>("42"));
instance->AppendString("anything");
ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
diff --git a/chromium/components/keyed_service/content/browser_context_keyed_base_factory.cc b/chromium/components/keyed_service/content/browser_context_keyed_base_factory.cc
index 7c2d8b797c5..4c8aba3bde5 100644
--- a/chromium/components/keyed_service/content/browser_context_keyed_base_factory.cc
+++ b/chromium/components/keyed_service/content/browser_context_keyed_base_factory.cc
@@ -19,7 +19,7 @@ BrowserContextKeyedBaseFactory::~BrowserContextKeyedBaseFactory() {
content::BrowserContext* BrowserContextKeyedBaseFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
// TODO(crbug.com/701326): This DCHECK should be moved to GetContextToUse().
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Safe default for the Incognito mode: no service.
if (context->IsOffTheRecord())
diff --git a/chromium/components/keyed_service/content/browser_context_keyed_service_factory.cc b/chromium/components/keyed_service/content/browser_context_keyed_service_factory.cc
index 38f6ca6158a..6cc7d943e65 100644
--- a/chromium/components/keyed_service/content/browser_context_keyed_service_factory.cc
+++ b/chromium/components/keyed_service/content/browser_context_keyed_service_factory.cc
@@ -49,7 +49,7 @@ content::BrowserContext*
BrowserContextKeyedServiceFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
// TODO(crbug.com/701326): This DCHECK should be moved to GetContextToUse().
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Safe default for Incognito mode: no service.
if (context->IsOffTheRecord())
diff --git a/chromium/components/keyed_service/content/refcounted_browser_context_keyed_service_factory.cc b/chromium/components/keyed_service/content/refcounted_browser_context_keyed_service_factory.cc
index 7778de8abee..df8908ae50f 100644
--- a/chromium/components/keyed_service/content/refcounted_browser_context_keyed_service_factory.cc
+++ b/chromium/components/keyed_service/content/refcounted_browser_context_keyed_service_factory.cc
@@ -50,7 +50,7 @@ content::BrowserContext*
RefcountedBrowserContextKeyedServiceFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
// TODO(crbug.com/701326): This DCHECK should be moved to GetContextToUse().
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Safe default for Incognito mode: no service.
if (context->IsOffTheRecord())
diff --git a/chromium/components/keyed_service/core/keyed_service_base_factory.cc b/chromium/components/keyed_service/core/keyed_service_base_factory.cc
index 48643305759..773b1f941c9 100644
--- a/chromium/components/keyed_service/core/keyed_service_base_factory.cc
+++ b/chromium/components/keyed_service/core/keyed_service_base_factory.cc
@@ -53,6 +53,7 @@ KeyedServiceBaseFactory::KeyedServiceBaseFactory(const char* service_name,
}
KeyedServiceBaseFactory::~KeyedServiceBaseFactory() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dependency_manager_->RemoveComponent(this);
}
@@ -88,7 +89,7 @@ void KeyedServiceBaseFactory::AssertContextWasntDestroyed(
}
void KeyedServiceBaseFactory::MarkContextLive(base::SupportsUserData* context) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dependency_manager_->MarkContextLive(context);
}
@@ -104,7 +105,7 @@ void KeyedServiceBaseFactory::ContextDestroyed(
base::SupportsUserData* context) {
// While object destruction can be customized in ways where the object is
// only dereferenced, this still must run on the UI thread.
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
registered_preferences_.erase(context);
}
diff --git a/chromium/components/keyed_service/core/keyed_service_base_factory.h b/chromium/components/keyed_service/core/keyed_service_base_factory.h
index ea9223b0af3..94d01d77c7b 100644
--- a/chromium/components/keyed_service/core/keyed_service_base_factory.h
+++ b/chromium/components/keyed_service/core/keyed_service_base_factory.h
@@ -7,7 +7,7 @@
#include <set>
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "components/keyed_service/core/dependency_node.h"
#include "components/keyed_service/core/keyed_service_export.h"
@@ -29,8 +29,7 @@ class PrefRegistrySyncable;
// This object describes general dependency management between factories while
// direct subclasses react to lifecycle events and implement memory management.
class KEYED_SERVICE_EXPORT KeyedServiceBaseFactory
- : public base::NonThreadSafe,
- NON_EXPORTED_BASE(public DependencyNode) {
+ : NON_EXPORTED_BASE(public DependencyNode) {
public:
#ifndef NDEBUG
// Returns our name. We don't keep track of this in release mode.
@@ -112,6 +111,8 @@ class KEYED_SERVICE_EXPORT KeyedServiceBaseFactory
// Mark context has having preferences registered.
void MarkPreferencesSetOn(base::SupportsUserData* context);
+ SEQUENCE_CHECKER(sequence_checker_);
+
private:
friend class DependencyManager;
diff --git a/chromium/components/keyed_service/core/refcounted_keyed_service.cc b/chromium/components/keyed_service/core/refcounted_keyed_service.cc
index 04a1ea57288..e6c449fe587 100644
--- a/chromium/components/keyed_service/core/refcounted_keyed_service.cc
+++ b/chromium/components/keyed_service/core/refcounted_keyed_service.cc
@@ -10,7 +10,7 @@ namespace impl {
// static
void RefcountedKeyedServiceTraits::Destruct(const RefcountedKeyedService* obj) {
- if (obj->task_runner_ && !obj->task_runner_->RunsTasksOnCurrentThread()) {
+ if (obj->task_runner_ && !obj->task_runner_->RunsTasksInCurrentSequence()) {
obj->task_runner_->DeleteSoon(FROM_HERE, obj);
} else {
delete obj;
diff --git a/chromium/components/keyed_service/ios/browser_state_keyed_service_factory.cc b/chromium/components/keyed_service/ios/browser_state_keyed_service_factory.cc
index 105e6992ce4..d953120c460 100644
--- a/chromium/components/keyed_service/ios/browser_state_keyed_service_factory.cc
+++ b/chromium/components/keyed_service/ios/browser_state_keyed_service_factory.cc
@@ -43,7 +43,7 @@ KeyedService* BrowserStateKeyedServiceFactory::GetServiceForBrowserState(
web::BrowserState* BrowserStateKeyedServiceFactory::GetBrowserStateToUse(
web::BrowserState* context) const {
// TODO(crbug.com/701326): This DCHECK should be moved to GetContextToUse().
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Safe default for Incognito mode: no service.
if (context->IsOffTheRecord())
diff --git a/chromium/components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.cc b/chromium/components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.cc
index a9abd88e526..98a3cae8e4b 100644
--- a/chromium/components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.cc
+++ b/chromium/components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.cc
@@ -50,7 +50,7 @@ web::BrowserState*
RefcountedBrowserStateKeyedServiceFactory::GetBrowserStateToUse(
web::BrowserState* context) const {
// TODO(crbug.com/701326): This DCHECK should be moved to GetContextToUse().
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Safe default for Incognito mode: no service.
if (context->IsOffTheRecord())
diff --git a/chromium/components/language/DEPS b/chromium/components/language/DEPS
new file mode 100644
index 00000000000..a6d4a63679f
--- /dev/null
+++ b/chromium/components/language/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+components/pref_registry",
+ "+components/prefs",
+
+ # Language is a layered component; subdirectories must explicitly introduce
+ # the ability to use the content layer as appropriate.
+ "-components/language/content",
+]
diff --git a/chromium/components/language/OWNERS b/chromium/components/language/OWNERS
new file mode 100644
index 00000000000..019412ab7e6
--- /dev/null
+++ b/chromium/components/language/OWNERS
@@ -0,0 +1,4 @@
+sammc@chromium.org
+
+# TEAM: language@chromium.org
+# COMPONENT: UI>Browser>Language
diff --git a/chromium/components/language/README b/chromium/components/language/README
new file mode 100644
index 00000000000..e4c4821cc4c
--- /dev/null
+++ b/chromium/components/language/README
@@ -0,0 +1,15 @@
+This component provides a unified model of user language which will serve as the
+single source of truth for other components.
+
+Language is implemented as a layered component
+(https://sites.google.com/a/chromium.org/dev/developers/design-documents/layered-components-design)
+to enable it to be shared cleanly on iOS.
+
+This component has the following structure:
+
+core/: shared code that does not depend on src/content or src/ios.
+content/: Driver for the shared code based on the content layer.
+ browser/: Browser process code.
+ renderer/: Renderer process code.
+ common/: Code shared by the browser and the renderer.
+ios/: Driver for the shared code based on src/ios.
diff --git a/chromium/components/language/content/DEPS b/chromium/components/language/content/DEPS
new file mode 100644
index 00000000000..c57486bda67
--- /dev/null
+++ b/chromium/components/language/content/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/common",
+ "+mojo/public",
+]
diff --git a/chromium/components/language/content/browser/DEPS b/chromium/components/language/content/browser/DEPS
new file mode 100644
index 00000000000..1c35d9ca694
--- /dev/null
+++ b/chromium/components/language/content/browser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+content/public/browser",
+]
diff --git a/chromium/components/language/content/renderer/DEPS b/chromium/components/language/content/renderer/DEPS
new file mode 100644
index 00000000000..725e151a7a5
--- /dev/null
+++ b/chromium/components/language/content/renderer/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+content/public/renderer",
+]
diff --git a/chromium/components/language/core/browser/BUILD.gn b/chromium/components/language/core/browser/BUILD.gn
new file mode 100644
index 00000000000..54b103a5a17
--- /dev/null
+++ b/chromium/components/language/core/browser/BUILD.gn
@@ -0,0 +1,33 @@
+# 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.
+
+static_library("browser") {
+ sources = [
+ "url_language_histogram.cc",
+ "url_language_histogram.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/keyed_service/core",
+ "//components/pref_registry",
+ "//components/prefs",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "url_language_histogram_unittest.cc",
+ ]
+ deps = [
+ ":browser",
+ "//base",
+ "//components/pref_registry:pref_registry",
+ "//components/prefs",
+ "//components/prefs:test_support",
+ "//net:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/language/core/browser/DEPS b/chromium/components/language/core/browser/DEPS
new file mode 100644
index 00000000000..f0bf3d9693e
--- /dev/null
+++ b/chromium/components/language/core/browser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/keyed_service/core",
+]
diff --git a/chromium/components/language/core/browser/url_language_histogram.cc b/chromium/components/language/core/browser/url_language_histogram.cc
new file mode 100644
index 00000000000..a0b092ba45d
--- /dev/null
+++ b/chromium/components/language/core/browser/url_language_histogram.cc
@@ -0,0 +1,146 @@
+// 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/language/core/browser/url_language_histogram.h"
+
+#include <algorithm>
+#include <map>
+#include <set>
+
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+
+namespace language {
+
+namespace {
+
+const char kUrlLanguageHistogramCounters[] = "language_model_counters";
+
+const int kMaxCountersSum = 1000;
+const int kMinCountersSum = 10;
+const float kCutoffRatio = 0.005f;
+const float kDiscountFactor = 0.75f;
+
+// Gets the sum of the counter for all languages in the histogram.
+int GetCountersSum(const base::DictionaryValue& dict) {
+ int sum = 0;
+ int counter_value = 0;
+ for (base::DictionaryValue::Iterator itr(dict); !itr.IsAtEnd();
+ itr.Advance()) {
+ if (itr.value().GetAsInteger(&counter_value))
+ sum += counter_value;
+ }
+ return sum;
+}
+
+// Removes languages with small counter values and discount remaining counters.
+void DiscountAndCleanCounters(base::DictionaryValue* dict) {
+ std::set<std::string> remove_keys;
+
+ int counter_value = 0;
+ for (base::DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
+ itr.Advance()) {
+ // Remove languages with invalid or small values.
+ if (!itr.value().GetAsInteger(&counter_value) ||
+ counter_value < (kCutoffRatio * kMaxCountersSum)) {
+ remove_keys.insert(itr.key());
+ continue;
+ }
+
+ // Discount the value.
+ dict->SetInteger(itr.key(), counter_value * kDiscountFactor);
+ }
+
+ for (const std::string& lang_to_remove : remove_keys)
+ dict->Remove(lang_to_remove, nullptr);
+}
+
+// Transforms the counters from prefs into a list of LanguageInfo structs.
+std::vector<UrlLanguageHistogram::LanguageInfo> GetAllLanguages(
+ const base::DictionaryValue& dict) {
+ int counters_sum = GetCountersSum(dict);
+
+ // If the sample is not large enough yet, pretend there are no top languages.
+ if (counters_sum < kMinCountersSum)
+ return std::vector<UrlLanguageHistogram::LanguageInfo>();
+
+ std::vector<UrlLanguageHistogram::LanguageInfo> top_languages;
+ int counter_value = 0;
+ for (base::DictionaryValue::Iterator itr(dict); !itr.IsAtEnd();
+ itr.Advance()) {
+ if (!itr.value().GetAsInteger(&counter_value))
+ continue;
+ top_languages.emplace_back(
+ itr.key(), static_cast<float>(counter_value) / counters_sum);
+ }
+ return top_languages;
+}
+
+} // namespace
+
+UrlLanguageHistogram::UrlLanguageHistogram(PrefService* pref_service)
+ : pref_service_(pref_service) {}
+
+UrlLanguageHistogram::~UrlLanguageHistogram() = default;
+
+// static
+void UrlLanguageHistogram::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterDictionaryPref(kUrlLanguageHistogramCounters);
+}
+
+std::vector<UrlLanguageHistogram::LanguageInfo>
+UrlLanguageHistogram::GetTopLanguages() const {
+ std::vector<UrlLanguageHistogram::LanguageInfo> top_languages =
+ GetAllLanguages(
+ *pref_service_->GetDictionary(kUrlLanguageHistogramCounters));
+
+ std::sort(top_languages.begin(), top_languages.end(),
+ [](UrlLanguageHistogram::LanguageInfo a,
+ UrlLanguageHistogram::LanguageInfo b) {
+ return a.frequency > b.frequency;
+ });
+
+ return top_languages;
+}
+
+float UrlLanguageHistogram::GetLanguageFrequency(
+ const std::string& language_code) const {
+ const base::DictionaryValue* dict =
+ pref_service_->GetDictionary(kUrlLanguageHistogramCounters);
+ int counters_sum = GetCountersSum(*dict);
+ // If the sample is not large enough yet, pretend there are no top languages.
+ if (counters_sum < kMinCountersSum)
+ return 0;
+
+ int counter_value = 0;
+ // If the key |language_code| does not exist, |counter_value| stays 0.
+ dict->GetInteger(language_code, &counter_value);
+
+ return static_cast<float>(counter_value) / counters_sum;
+}
+
+void UrlLanguageHistogram::OnPageVisited(const std::string& language_code) {
+ DictionaryPrefUpdate update(pref_service_, kUrlLanguageHistogramCounters);
+ base::DictionaryValue* dict = update.Get();
+ int counter_value = 0;
+ // If the key |language_code| does not exist, |counter_value| stays 0.
+ dict->GetInteger(language_code, &counter_value);
+ dict->SetInteger(language_code, counter_value + 1);
+
+ if (GetCountersSum(*dict) > kMaxCountersSum)
+ DiscountAndCleanCounters(dict);
+}
+
+void UrlLanguageHistogram::ClearHistory(base::Time begin, base::Time end) {
+ // Ignore all partial removals and react only to "entire" history removal.
+ bool is_entire_history = (begin == base::Time() && end == base::Time::Max());
+ if (!is_entire_history) {
+ return;
+ }
+
+ pref_service_->ClearPref(kUrlLanguageHistogramCounters);
+}
+
+} // namespace language
diff --git a/chromium/components/language/core/browser/url_language_histogram.h b/chromium/components/language/core/browser/url_language_histogram.h
new file mode 100644
index 00000000000..11226ce8a8d
--- /dev/null
+++ b/chromium/components/language/core/browser/url_language_histogram.h
@@ -0,0 +1,71 @@
+// 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_LANGUAGE_CORE_BROWSER_URL_LANGUAGE_HISTOGRAM_H_
+#define COMPONENTS_LANGUAGE_CORE_BROWSER_URL_LANGUAGE_HISTOGRAM_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace language {
+
+// Collects data about languages in which the user reads the web and provides
+// access to current estimated language preferences. The past behaviour is
+// discounted so that the histogram reflects changes in browsing habits. This
+// histogram does not have to contain all languages that ever appeared in user's
+// browsing, languages with insignificant frequency are removed, eventually.
+class UrlLanguageHistogram : public KeyedService {
+ public:
+ struct LanguageInfo {
+ LanguageInfo() = default;
+ LanguageInfo(const std::string& language_code, float frequency)
+ : language_code(language_code), frequency(frequency) {}
+
+ // The ISO 639 language code.
+ std::string language_code;
+
+ // The current estimated frequency of the language share, a number between 0
+ // and 1 (can be understood as the probability that the next page the user
+ // opens is in this language). Frequencies over all LanguageInfos from
+ // GetTopLanguages() sum to 1 (unless there are no top languages, yet).
+ float frequency = 0.0f;
+ };
+
+ explicit UrlLanguageHistogram(PrefService* pref_service);
+ ~UrlLanguageHistogram() override;
+
+ // Registers profile prefs for the histogram.
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // Returns a list of the languages currently tracked by the histogram, sorted
+ // by frequency in decreasing order. The list is empty, if the histogram has
+ // not enough data points.
+ std::vector<LanguageInfo> GetTopLanguages() const;
+
+ // Returns the estimated frequency for the given language or 0 if the language
+ // is not among the top languages kept in the histogram.
+ float GetLanguageFrequency(const std::string& language_code) const;
+
+ // Informs the histogram that a page with the given language has been visited.
+ void OnPageVisited(const std::string& language_code);
+
+ // Reflect in the histogram that history from |begin| to |end| gets cleared.
+ void ClearHistory(base::Time begin, base::Time end);
+
+ private:
+ PrefService* pref_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlLanguageHistogram);
+};
+
+} // namespace language
+
+#endif // COMPONENTS_LANGUAGE_CORE_BROWSER_URL_LANGUAGE_HISTOGRAM_H_
diff --git a/chromium/components/language/core/browser/url_language_histogram_unittest.cc b/chromium/components/language/core/browser/url_language_histogram_unittest.cc
new file mode 100644
index 00000000000..6e5c4a893cf
--- /dev/null
+++ b/chromium/components/language/core/browser/url_language_histogram_unittest.cc
@@ -0,0 +1,147 @@
+// 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/language/core/browser/url_language_histogram.h"
+
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::FloatEq;
+using testing::Gt;
+using testing::SizeIs;
+
+namespace {
+
+const char kLang1[] = "en";
+const char kLang2[] = "de";
+const char kLang3[] = "es";
+
+} // namespace
+
+namespace language {
+
+bool operator==(const UrlLanguageHistogram::LanguageInfo& lhs,
+ const UrlLanguageHistogram::LanguageInfo& rhs) {
+ return lhs.language_code == rhs.language_code;
+}
+
+TEST(UrlLanguageHistogramTest, ListSorted) {
+ TestingPrefServiceSimple prefs;
+ UrlLanguageHistogram::RegisterProfilePrefs(prefs.registry());
+ UrlLanguageHistogram hist(&prefs);
+
+ for (int i = 0; i < 50; i++) {
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang2);
+ }
+
+ // Note: LanguageInfo's operator== only checks the language code, not the
+ // frequency.
+ EXPECT_THAT(hist.GetTopLanguages(),
+ ElementsAre(UrlLanguageHistogram::LanguageInfo(kLang1, 0.0f),
+ UrlLanguageHistogram::LanguageInfo(kLang2, 0.0f)));
+}
+
+TEST(UrlLanguageHistogramTest, ListSortedReversed) {
+ TestingPrefServiceSimple prefs;
+ UrlLanguageHistogram::RegisterProfilePrefs(prefs.registry());
+ UrlLanguageHistogram hist(&prefs);
+
+ for (int i = 0; i < 50; i++) {
+ hist.OnPageVisited(kLang2);
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang1);
+ }
+
+ // Note: LanguageInfo's operator== only checks the language code, not the
+ // frequency.
+ EXPECT_THAT(hist.GetTopLanguages(),
+ ElementsAre(UrlLanguageHistogram::LanguageInfo(kLang1, 0.0f),
+ UrlLanguageHistogram::LanguageInfo(kLang2, 0.0f)));
+}
+
+TEST(UrlLanguageHistogramTest, RightFrequencies) {
+ TestingPrefServiceSimple prefs;
+ UrlLanguageHistogram::RegisterProfilePrefs(prefs.registry());
+ UrlLanguageHistogram hist(&prefs);
+
+ for (int i = 0; i < 50; i++) {
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang1);
+ hist.OnPageVisited(kLang2);
+ }
+
+ // Corresponding frequencies are given by the hist.
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang1), FloatEq(0.75f));
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang2), FloatEq(0.25f));
+ // An unknown language gets frequency 0.
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang3), 0);
+}
+
+TEST(UrlLanguageHistogramTest, RareLanguageDiscarded) {
+ TestingPrefServiceSimple prefs;
+ UrlLanguageHistogram::RegisterProfilePrefs(prefs.registry());
+ UrlLanguageHistogram hist(&prefs);
+
+ hist.OnPageVisited(kLang2);
+
+ for (int i = 0; i < 900; i++)
+ hist.OnPageVisited(kLang1);
+
+ // Lang 2 is in the hist.
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang2), Gt(0.0f));
+
+ // Another 100 visits cause the cleanup (total > 1000).
+ for (int i = 0; i < 100; i++)
+ hist.OnPageVisited(kLang1);
+ // Lang 2 is removed from the hist.
+ EXPECT_THAT(hist.GetTopLanguages(),
+ ElementsAre(UrlLanguageHistogram::LanguageInfo{kLang1, 1}));
+}
+
+TEST(UrlLanguageHistogramTest, ShouldClearHistoryIfAllTimes) {
+ TestingPrefServiceSimple prefs;
+ UrlLanguageHistogram::RegisterProfilePrefs(prefs.registry());
+ UrlLanguageHistogram hist(&prefs);
+
+ for (int i = 0; i < 100; i++) {
+ hist.OnPageVisited(kLang1);
+ }
+
+ EXPECT_THAT(hist.GetTopLanguages(), SizeIs(1));
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang1), FloatEq(1.0));
+
+ hist.ClearHistory(base::Time(), base::Time::Max());
+
+ EXPECT_THAT(hist.GetTopLanguages(), SizeIs(0));
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang1), FloatEq(0.0));
+}
+
+TEST(UrlLanguageHistogramTest, ShouldNotClearHistoryIfNotAllTimes) {
+ TestingPrefServiceSimple prefs;
+ UrlLanguageHistogram::RegisterProfilePrefs(prefs.registry());
+ UrlLanguageHistogram hist(&prefs);
+
+ for (int i = 0; i < 100; i++) {
+ hist.OnPageVisited(kLang1);
+ }
+
+ EXPECT_THAT(hist.GetTopLanguages(), SizeIs(1));
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang1), FloatEq(1.0));
+
+ // Clearing only the last hour of the history has no effect.
+ hist.ClearHistory(base::Time::Now() - base::TimeDelta::FromHours(2),
+ base::Time::Max());
+
+ EXPECT_THAT(hist.GetTopLanguages(), SizeIs(1));
+ EXPECT_THAT(hist.GetLanguageFrequency(kLang1), FloatEq(1.0));
+}
+
+} // namespace language
diff --git a/chromium/components/language/ios/DEPS b/chromium/components/language/ios/DEPS
new file mode 100644
index 00000000000..0fc0ddddacd
--- /dev/null
+++ b/chromium/components/language/ios/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ios/web/public",
+]
diff --git a/chromium/components/leveldb/env_mojo.cc b/chromium/components/leveldb/env_mojo.cc
index 9b98405d125..0f17d71ec4b 100644
--- a/chromium/components/leveldb/env_mojo.cc
+++ b/chromium/components/leveldb/env_mojo.cc
@@ -45,6 +45,9 @@ Status FilesystemErrorToStatus(FileError error,
char buf[512];
snprintf(buf, sizeof(buf), "%s (MojoFSError: %d::%s)", err_str.c_str(),
method, MethodIDToString(method));
+
+ // TOOD(crbug.com/760362): Map FileError::NOT_FOUND to Status::NotFound, after
+ // fixing LevelDB to handle the NotFound correctly.
return Status::IOError(filename, buf);
}
@@ -218,6 +221,10 @@ class MojoWritableFile : public leveldb::WritableFile {
leveldb::Status SyncParent() {
FileError error = thread_->SyncDirectory(dir_, parent_dir_);
+ if (error != FileError::OK) {
+ uma_logger_->RecordOSError(leveldb_env::kSyncParent,
+ static_cast<base::File::Error>(error));
+ }
return error == FileError::OK
? Status::OK()
: Status::IOError(filename_,
@@ -257,6 +264,58 @@ class Thread : public base::PlatformThread::Delegate {
DISALLOW_COPY_AND_ASSIGN(Thread);
};
+class Retrier {
+ public:
+ Retrier(leveldb_env::MethodID method, MojoRetrierProvider* provider)
+ : start_(base::TimeTicks::Now()),
+ limit_(start_ + base::TimeDelta::FromMilliseconds(
+ provider->MaxRetryTimeMillis())),
+ last_(start_),
+ time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
+ success_(true),
+ method_(method),
+ last_error_(base::File::FILE_OK),
+ provider_(provider) {}
+
+ ~Retrier() {
+ if (success_) {
+ provider_->RecordRetryTime(method_, last_ - start_);
+ if (last_error_ != base::File::FILE_OK) {
+ DCHECK_LT(last_error_, 0);
+ provider_->RecordRecoveredFromError(method_, last_error_);
+ }
+ }
+ }
+
+ bool ShouldKeepTrying(FileError error) {
+ return ShouldKeepTrying(static_cast<base::File::Error>(error));
+ }
+
+ bool ShouldKeepTrying(base::File::Error last_error) {
+ DCHECK_NE(last_error, base::File::FILE_OK);
+ last_error_ = last_error;
+ if (last_ < limit_) {
+ base::PlatformThread::Sleep(time_to_sleep_);
+ last_ = base::TimeTicks::Now();
+ return true;
+ }
+ success_ = false;
+ return false;
+ }
+
+ private:
+ base::TimeTicks start_;
+ base::TimeTicks limit_;
+ base::TimeTicks last_;
+ base::TimeDelta time_to_sleep_;
+ bool success_;
+ leveldb_env::MethodID method_;
+ base::File::Error last_error_;
+ MojoRetrierProvider* provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(Retrier);
+};
+
} // namespace
MojoEnv::MojoEnv(scoped_refptr<LevelDBMojoProxy> file_thread,
@@ -358,7 +417,11 @@ Status MojoEnv::DeleteFile(const std::string& fname) {
Status MojoEnv::CreateDir(const std::string& dirname) {
TRACE_EVENT1("leveldb", "MojoEnv::CreateDir", "dirname", dirname);
- FileError error = thread_->CreateDir(dir_, dirname);
+ Retrier retrier(leveldb_env::kCreateDir, this);
+ FileError error;
+ do {
+ error = thread_->CreateDir(dir_, dirname);
+ } while (error != FileError::OK && retrier.ShouldKeepTrying(error));
if (error != FileError::OK)
RecordFileError(leveldb_env::kCreateDir, error);
return FilesystemErrorToStatus(error, dirname, leveldb_env::kCreateDir);
@@ -383,7 +446,13 @@ Status MojoEnv::GetFileSize(const std::string& fname, uint64_t* file_size) {
Status MojoEnv::RenameFile(const std::string& src, const std::string& target) {
TRACE_EVENT2("leveldb", "MojoEnv::RenameFile", "src", src, "target", target);
- FileError error = thread_->RenameFile(dir_, src, target);
+ if (!thread_->FileExists(dir_, src))
+ return Status::OK();
+ Retrier retrier(leveldb_env::kRenameFile, this);
+ FileError error;
+ do {
+ error = thread_->RenameFile(dir_, src, target);
+ } while (error != FileError::OK && retrier.ShouldKeepTrying(error));
if (error != FileError::OK)
RecordFileError(leveldb_env::kRenameFile, error);
return FilesystemErrorToStatus(error, src, leveldb_env::kRenameFile);
@@ -392,8 +461,11 @@ Status MojoEnv::RenameFile(const std::string& src, const std::string& target) {
Status MojoEnv::LockFile(const std::string& fname, FileLock** lock) {
TRACE_EVENT1("leveldb", "MojoEnv::LockFile", "fname", fname);
- std::pair<FileError, LevelDBMojoProxy::OpaqueLock*> p =
- thread_->LockFile(dir_, fname);
+ Retrier retrier(leveldb_env::kLockFile, this);
+ std::pair<FileError, LevelDBMojoProxy::OpaqueLock*> p;
+ do {
+ p = thread_->LockFile(dir_, fname);
+ } while (p.first != FileError::OK && retrier.ShouldKeepTrying(p.first));
if (p.first != FileError::OK)
RecordFileError(leveldb_env::kLockFile, p.first);
@@ -443,6 +515,26 @@ Status MojoEnv::NewLogger(const std::string& fname, Logger** result) {
}
}
+uint64_t MojoEnv::NowMicros() {
+ return base::TimeTicks::Now().ToInternalValue();
+}
+
+void MojoEnv::SleepForMicroseconds(int micros) {
+ // Round up to the next millisecond.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
+}
+
+void MojoEnv::Schedule(void (*function)(void* arg), void* arg) {
+ base::PostTaskWithTraits(FROM_HERE,
+ {base::MayBlock(), base::WithBaseSyncPrimitives(),
+ base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
+ base::Bind(function, arg));
+}
+
+void MojoEnv::StartThread(void (*function)(void* arg), void* arg) {
+ new Thread(function, arg); // Will self-delete.
+}
+
void MojoEnv::RecordErrorAt(leveldb_env::MethodID method) const {
UMA_HISTOGRAM_ENUMERATION("MojoLevelDBEnv.IOError", method,
leveldb_env::kNumEntries);
@@ -464,29 +556,29 @@ void MojoEnv::RecordBytesWritten(int amount) const {
UMA_HISTOGRAM_COUNTS_10M("Storage.BytesWritten.MojoLevelDBEnv", amount);
}
-void MojoEnv::RecordFileError(leveldb_env::MethodID method,
- FileError error) const {
- RecordOSError(method, static_cast<base::File::Error>(error));
-}
-
-uint64_t MojoEnv::NowMicros() {
- return base::TimeTicks::Now().ToInternalValue();
+int MojoEnv::MaxRetryTimeMillis() const {
+ return 1000;
}
-void MojoEnv::SleepForMicroseconds(int micros) {
- // Round up to the next millisecond.
- base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros));
+void MojoEnv::RecordRetryTime(leveldb_env::MethodID method,
+ base::TimeDelta time) const {
+ std::string uma_name = std::string("MojoLevelDBEnv.TimeUntilSuccessFor") +
+ MethodIDToString(method);
+ UmaHistogramCustomTimes(uma_name, time, base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(1001), 42);
}
-void MojoEnv::Schedule(void (*function)(void* arg), void* arg) {
- base::PostTaskWithTraits(FROM_HERE,
- {base::MayBlock(), base::WithBaseSyncPrimitives(),
- base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
- base::Bind(function, arg));
+void MojoEnv::RecordRecoveredFromError(leveldb_env::MethodID method,
+ base::File::Error error) const {
+ std::string uma_name =
+ std::string("MojoLevelDBEnv.RetryRecoveredFromErrorIn") +
+ MethodIDToString(method);
+ base::UmaHistogramExactLinear(uma_name, -error, -base::File::FILE_ERROR_MAX);
}
-void MojoEnv::StartThread(void (*function)(void* arg), void* arg) {
- new Thread(function, arg); // Will self-delete.
+void MojoEnv::RecordFileError(leveldb_env::MethodID method,
+ FileError error) const {
+ RecordOSError(method, static_cast<base::File::Error>(error));
}
} // namespace leveldb
diff --git a/chromium/components/leveldb/env_mojo.h b/chromium/components/leveldb/env_mojo.h
index ca0f683902e..db240462131 100644
--- a/chromium/components/leveldb/env_mojo.h
+++ b/chromium/components/leveldb/env_mojo.h
@@ -12,12 +12,23 @@
namespace leveldb {
+class MojoRetrierProvider {
+ public:
+ virtual int MaxRetryTimeMillis() const = 0;
+ virtual void RecordRetryTime(leveldb_env::MethodID method,
+ base::TimeDelta time) const = 0;
+ virtual void RecordRecoveredFromError(leveldb_env::MethodID method,
+ base::File::Error error) const = 0;
+};
+
// An implementation of the leveldb operating system interaction code which
// proxies to a specified mojo:filesystem directory. Most of these methods are
// synchronous and block on responses from the filesystem service. That's fine
// since, for the most part, they merely open files or check for a file's
// existence.
-class MojoEnv : public Env, public leveldb_env::UMALogger {
+class MojoEnv : public Env,
+ public leveldb_env::UMALogger,
+ public MojoRetrierProvider {
public:
MojoEnv(scoped_refptr<LevelDBMojoProxy> file_thread,
LevelDBMojoProxy::OpaqueDir* dir);
@@ -56,6 +67,11 @@ class MojoEnv : public Env, public leveldb_env::UMALogger {
base::File::Error error) const override;
void RecordBytesRead(int amount) const override;
void RecordBytesWritten(int amount) const override;
+ int MaxRetryTimeMillis() const override;
+ void RecordRetryTime(leveldb_env::MethodID method,
+ base::TimeDelta time) const override;
+ void RecordRecoveredFromError(leveldb_env::MethodID method,
+ base::File::Error error) const override;
void RecordFileError(leveldb_env::MethodID method,
filesystem::mojom::FileError error) const;
diff --git a/chromium/components/leveldb/leveldb_app.cc b/chromium/components/leveldb/leveldb_app.cc
index cd5447ea05e..76b48b38b19 100644
--- a/chromium/components/leveldb/leveldb_app.cc
+++ b/chromium/components/leveldb/leveldb_app.cc
@@ -4,12 +4,15 @@
#include "components/leveldb/leveldb_app.h"
+#include "base/task_scheduler/post_task.h"
#include "components/leveldb/leveldb_service_impl.h"
#include "services/service_manager/public/cpp/service_context.h"
namespace leveldb {
-LevelDBApp::LevelDBApp() : file_thread_("LevelDBFile") {
+LevelDBApp::LevelDBApp()
+ : file_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {
registry_.AddInterface<mojom::LevelDBService>(
base::Bind(&LevelDBApp::Create, base::Unretained(this)));
}
@@ -22,18 +25,12 @@ void LevelDBApp::OnBindInterface(
const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
- registry_.BindInterface(source_info, interface_name,
- std::move(interface_pipe));
+ registry_.BindInterface(interface_name, std::move(interface_pipe));
}
-void LevelDBApp::Create(const service_manager::BindSourceInfo& source_info,
- leveldb::mojom::LevelDBServiceRequest request) {
- if (!service_) {
- if (!file_thread_.IsRunning())
- file_thread_.Start();
- service_.reset(
- new LevelDBServiceImpl(file_thread_.message_loop()->task_runner()));
- }
+void LevelDBApp::Create(leveldb::mojom::LevelDBServiceRequest request) {
+ if (!service_)
+ service_.reset(new LevelDBServiceImpl(file_task_runner_));
bindings_.AddBinding(service_.get(), std::move(request));
}
diff --git a/chromium/components/leveldb/leveldb_app.h b/chromium/components/leveldb/leveldb_app.h
index 78d542b3437..f5696a31d6b 100644
--- a/chromium/components/leveldb/leveldb_app.h
+++ b/chromium/components/leveldb/leveldb_app.h
@@ -7,12 +7,15 @@
#include <memory>
-#include "base/threading/thread.h"
#include "components/leveldb/public/interfaces/leveldb.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/service.h"
+namespace base {
+class SequencedTaskRunner;
+}
+
namespace leveldb {
class LevelDBApp : public service_manager::Service {
@@ -27,14 +30,13 @@ class LevelDBApp : public service_manager::Service {
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override;
- void Create(const service_manager::BindSourceInfo& source_info,
- leveldb::mojom::LevelDBServiceRequest request);
+ void Create(leveldb::mojom::LevelDBServiceRequest request);
std::unique_ptr<mojom::LevelDBService> service_;
service_manager::BinderRegistry registry_;
mojo::BindingSet<mojom::LevelDBService> bindings_;
- base::Thread file_thread_;
+ scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
DISALLOW_COPY_AND_ASSIGN(LevelDBApp);
};
diff --git a/chromium/components/leveldb/leveldb_database_impl.cc b/chromium/components/leveldb/leveldb_database_impl.cc
index 59ab0787066..f0f509a28c6 100644
--- a/chromium/components/leveldb/leveldb_database_impl.cc
+++ b/chromium/components/leveldb/leveldb_database_impl.cc
@@ -4,14 +4,19 @@
#include "components/leveldb/leveldb_database_impl.h"
+#include <inttypes.h>
#include <map>
#include <string>
#include <utility>
#include "base/optional.h"
#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_dump_manager.h"
#include "components/leveldb/env_mojo.h"
#include "components/leveldb/public/cpp/util.h"
+#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
@@ -37,10 +42,22 @@ leveldb::Status ForEachWithPrefix(leveldb::DB* db,
LevelDBDatabaseImpl::LevelDBDatabaseImpl(
std::unique_ptr<leveldb::Env> environment,
- std::unique_ptr<leveldb::DB> db)
- : environment_(std::move(environment)), db_(std::move(db)) {}
+ std::unique_ptr<leveldb::DB> db,
+ std::unique_ptr<leveldb::Cache> cache,
+ base::Optional<base::trace_event::MemoryAllocatorDumpGuid> memory_dump_id)
+ : environment_(std::move(environment)),
+ cache_(std::move(cache)),
+ db_(std::move(db)),
+ memory_dump_id_(memory_dump_id) {
+ base::trace_event::MemoryDumpManager::GetInstance()
+ ->RegisterDumpProviderWithSequencedTaskRunner(
+ this, "MojoLevelDB", base::SequencedTaskRunnerHandle::Get(),
+ MemoryDumpProvider::Options());
+}
LevelDBDatabaseImpl::~LevelDBDatabaseImpl() {
+ base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+ this);
for (auto& p : iterator_map_)
delete p.second;
for (auto& p : snapshot_map_)
@@ -270,6 +287,48 @@ void LevelDBDatabaseImpl::IteratorPrev(const base::UnguessableToken& iterator,
ReplyToIteratorMessage(it->second, std::move(callback));
}
+bool LevelDBDatabaseImpl::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) {
+ std::string name = base::StringPrintf("leveldb/mojo/0x%" PRIXPTR,
+ reinterpret_cast<uintptr_t>(db_.get()));
+ auto* mad = pmd->CreateAllocatorDump(name);
+
+ uint64_t memory_usage = 0;
+ std::string memory_usage_string;
+ bool got_memory_usage =
+ db_->GetProperty("leveldb.approximate-memory-usage",
+ &memory_usage_string) &&
+ base::StringToUint64(memory_usage_string, &memory_usage);
+ DCHECK(got_memory_usage);
+ mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+ base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+ memory_usage);
+ if (cache_) {
+ auto* cache_mad = pmd->CreateAllocatorDump(name + "/block_cache");
+ cache_mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+ base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+ cache_->TotalCharge());
+ }
+
+ // All leveldb databases are already dumped by leveldb_env::DBTracker. Add
+ // an edge to avoid double counting.
+ pmd->AddSuballocation(mad->guid(),
+ leveldb_env::DBTracker::GetMemoryDumpName(db_.get()));
+
+ if (memory_dump_id_) {
+ auto* global_dump = pmd->CreateSharedGlobalAllocatorDump(*memory_dump_id_);
+ pmd->AddOwnershipEdge(global_dump->guid(), mad->guid());
+ // Add size to global dump to propagate the size of the database to the
+ // client's dump.
+ global_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+ base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+ memory_usage);
+ }
+
+ return true;
+}
+
void LevelDBDatabaseImpl::ReplyToIteratorMessage(
leveldb::Iterator* it,
IteratorSeekToFirstCallback callback) {
diff --git a/chromium/components/leveldb/leveldb_database_impl.h b/chromium/components/leveldb/leveldb_database_impl.h
index 277748d49ef..31c9cbabb05 100644
--- a/chromium/components/leveldb/leveldb_database_impl.h
+++ b/chromium/components/leveldb/leveldb_database_impl.h
@@ -7,18 +7,24 @@
#include <memory>
+#include "base/trace_event/memory_dump_provider.h"
#include "base/unguessable_token.h"
#include "components/leveldb/public/interfaces/leveldb.mojom.h"
#include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/leveldatabase/src/include/leveldb/cache.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
namespace leveldb {
// The backing to a database object that we pass to our called.
-class LevelDBDatabaseImpl : public mojom::LevelDBDatabase {
+class LevelDBDatabaseImpl : public mojom::LevelDBDatabase,
+ public base::trace_event::MemoryDumpProvider {
public:
LevelDBDatabaseImpl(std::unique_ptr<leveldb::Env> environment,
- std::unique_ptr<leveldb::DB> db);
+ std::unique_ptr<leveldb::DB> db,
+ std::unique_ptr<leveldb::Cache> cache,
+ base::Optional<base::trace_event::MemoryAllocatorDumpGuid>
+ memory_dump_id);
~LevelDBDatabaseImpl() override;
// Overridden from LevelDBDatabase:
@@ -56,6 +62,10 @@ class LevelDBDatabaseImpl : public mojom::LevelDBDatabase {
void IteratorPrev(const base::UnguessableToken& iterator,
IteratorPrevCallback callback) override;
+ // base::trace_event::MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) override;
+
private:
// Returns the state of |it| to a caller. Note: This assumes that all the
// iterator movement methods have the same callback signature. We don't
@@ -67,7 +77,9 @@ class LevelDBDatabaseImpl : public mojom::LevelDBDatabase {
leveldb::WriteBatch* batch);
std::unique_ptr<leveldb::Env> environment_;
+ std::unique_ptr<leveldb::Cache> cache_;
std::unique_ptr<leveldb::DB> db_;
+ base::Optional<base::trace_event::MemoryAllocatorDumpGuid> memory_dump_id_;
std::map<base::UnguessableToken, const Snapshot*> snapshot_map_;
diff --git a/chromium/components/leveldb/leveldb_mojo_proxy.cc b/chromium/components/leveldb/leveldb_mojo_proxy.cc
index 523859c3762..5c78d33d317 100644
--- a/chromium/components/leveldb/leveldb_mojo_proxy.cc
+++ b/chromium/components/leveldb/leveldb_mojo_proxy.cc
@@ -8,7 +8,6 @@
#include "base/bind.h"
#include "base/callback.h"
-#include "base/single_thread_task_runner.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
@@ -28,9 +27,9 @@ struct LevelDBMojoProxy::OpaqueDir {
};
LevelDBMojoProxy::LevelDBMojoProxy(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(std::move(task_runner)), outstanding_opaque_dirs_(0) {
- DCHECK(!task_runner_->BelongsToCurrentThread());
+ DCHECK(!task_runner_->RunsTasksInCurrentSequence());
}
LevelDBMojoProxy::OpaqueDir* LevelDBMojoProxy::RegisterDirectory(
@@ -144,7 +143,7 @@ LevelDBMojoProxy::~LevelDBMojoProxy() {
}
void LevelDBMojoProxy::RunInternal(const base::Closure& task) {
- if (task_runner_->BelongsToCurrentThread()) {
+ if (task_runner_->RunsTasksInCurrentSequence()) {
task.Run();
} else {
base::WaitableEvent done_event(
@@ -291,7 +290,7 @@ void LevelDBMojoProxy::RenameFileImpl(OpaqueDir* dir,
const std::string& new_path,
filesystem::mojom::FileError* out_error) {
mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync;
- bool completed = dir->directory->Rename(old_path, new_path, out_error);
+ bool completed = dir->directory->Replace(old_path, new_path, out_error);
DCHECK(completed);
}
diff --git a/chromium/components/leveldb/leveldb_mojo_proxy.h b/chromium/components/leveldb/leveldb_mojo_proxy.h
index 2eef1a41d1d..4b5e0ad6908 100644
--- a/chromium/components/leveldb/leveldb_mojo_proxy.h
+++ b/chromium/components/leveldb/leveldb_mojo_proxy.h
@@ -14,13 +14,11 @@
#include "base/callback_forward.h"
#include "base/files/file.h"
#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+
#include "base/synchronization/waitable_event.h"
#include "components/filesystem/public/interfaces/directory.mojom.h"
-namespace base {
-class SingleThreadTaskRunner;
-}
-
namespace leveldb {
// A proxy for thread safe access to Mojo objects from multiple threads.
@@ -35,7 +33,7 @@ namespace leveldb {
class LevelDBMojoProxy : public base::RefCountedThreadSafe<LevelDBMojoProxy> {
public:
explicit LevelDBMojoProxy(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ scoped_refptr<base::SequencedTaskRunner> task_runner);
// A private struct to hide the underlying file that holds the lock from our
// callers, forcing them to go through our LockFile()/UnlockFile() interface
@@ -152,9 +150,9 @@ class LevelDBMojoProxy : public base::RefCountedThreadSafe<LevelDBMojoProxy> {
void UnlockFileImpl(std::unique_ptr<OpaqueLock> lock,
filesystem::mojom::FileError* out_error);
- // The task runner which represents the thread that all mojo objects are
+ // The task runner which represents the sequence that all mojo objects are
// bound to.
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
int outstanding_opaque_dirs_;
diff --git a/chromium/components/leveldb/leveldb_service_impl.cc b/chromium/components/leveldb/leveldb_service_impl.cc
index f2e4eb8597d..a6406166403 100644
--- a/chromium/components/leveldb/leveldb_service_impl.cc
+++ b/chromium/components/leveldb/leveldb_service_impl.cc
@@ -22,7 +22,7 @@
namespace leveldb {
LevelDBServiceImpl::LevelDBServiceImpl(
- scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
+ scoped_refptr<base::SequencedTaskRunner> file_task_runner)
: thread_(new LevelDBMojoProxy(std::move(file_task_runner))) {}
LevelDBServiceImpl::~LevelDBServiceImpl() {}
@@ -30,16 +30,21 @@ LevelDBServiceImpl::~LevelDBServiceImpl() {}
void LevelDBServiceImpl::Open(
filesystem::mojom::DirectoryPtr directory,
const std::string& dbname,
+ const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+ memory_dump_id,
leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
OpenCallback callback) {
OpenWithOptions(leveldb::mojom::OpenOptions::New(), std::move(directory),
- dbname, std::move(database), std::move(callback));
+ dbname, memory_dump_id, std::move(database),
+ std::move(callback));
}
void LevelDBServiceImpl::OpenWithOptions(
leveldb::mojom::OpenOptionsPtr open_options,
filesystem::mojom::DirectoryPtr directory,
const std::string& dbname,
+ const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+ memory_dump_id,
leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
OpenCallback callback) {
leveldb::Options options;
@@ -59,20 +64,26 @@ void LevelDBServiceImpl::OpenWithOptions(
std::unique_ptr<MojoEnv> env_mojo(new MojoEnv(thread_, dir));
options.env = env_mojo.get();
- leveldb::DB* db = nullptr;
- leveldb::Status s = leveldb::DB::Open(options, dbname, &db);
+ std::unique_ptr<leveldb::Cache> cache(
+ leveldb::NewLRUCache(open_options->block_cache_size));
+ options.block_cache = cache.get();
+
+ std::unique_ptr<leveldb::DB> db;
+ leveldb::Status s = leveldb_env::OpenDB(options, dbname, &db);
if (s.ok()) {
- mojo::MakeStrongAssociatedBinding(
- base::MakeUnique<LevelDBDatabaseImpl>(std::move(env_mojo),
- base::WrapUnique(db)),
- std::move(database));
+ mojo::MakeStrongAssociatedBinding(base::MakeUnique<LevelDBDatabaseImpl>(
+ std::move(env_mojo), std::move(db),
+ std::move(cache), memory_dump_id),
+ std::move(database));
}
std::move(callback).Run(LeveldbStatusToError(s));
}
void LevelDBServiceImpl::OpenInMemory(
+ const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+ memory_dump_id,
leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
OpenCallback callback) {
leveldb::Options options;
@@ -83,13 +94,14 @@ void LevelDBServiceImpl::OpenInMemory(
leveldb::NewMemEnv(leveldb::Env::Default()));
options.env = env.get();
- leveldb::DB* db = nullptr;
- leveldb::Status s = leveldb::DB::Open(options, "", &db);
+ std::unique_ptr<leveldb::DB> db;
+ leveldb::Status s = leveldb_env::OpenDB(options, "", &db);
if (s.ok()) {
- mojo::MakeStrongAssociatedBinding(base::MakeUnique<LevelDBDatabaseImpl>(
- std::move(env), base::WrapUnique(db)),
- std::move(database));
+ mojo::MakeStrongAssociatedBinding(
+ base::MakeUnique<LevelDBDatabaseImpl>(std::move(env), std::move(db),
+ nullptr, memory_dump_id),
+ std::move(database));
}
std::move(callback).Run(LeveldbStatusToError(s));
diff --git a/chromium/components/leveldb/leveldb_service_impl.h b/chromium/components/leveldb/leveldb_service_impl.h
index 9a5beed6d99..1d5fab0b3d3 100644
--- a/chromium/components/leveldb/leveldb_service_impl.h
+++ b/chromium/components/leveldb/leveldb_service_impl.h
@@ -6,11 +6,14 @@
#define COMPONENTS_LEVELDB_LEVELDB_SERVICE_IMPL_H_
#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
#include "components/leveldb/leveldb_mojo_proxy.h"
#include "components/leveldb/public/interfaces/leveldb.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
+namespace base {
+class SequencedTaskRunner;
+}
+
namespace leveldb {
// Creates LevelDBDatabases based scoped to a |directory|/|dbname|.
@@ -19,23 +22,29 @@ class LevelDBServiceImpl : public mojom::LevelDBService {
// The |file_task_runner| is used to run tasks to interact with the
// file_service. Specifically this task runner must NOT be the same as the
// task runner this implementation runs on, or deadlock might occur.
- LevelDBServiceImpl(
- scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
+ LevelDBServiceImpl(scoped_refptr<base::SequencedTaskRunner> file_task_runner);
~LevelDBServiceImpl() override;
// Overridden from LevelDBService:
void Open(filesystem::mojom::DirectoryPtr directory,
const std::string& dbname,
+ const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+ memory_dump_id,
leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
OpenCallback callback) override;
void OpenWithOptions(
leveldb::mojom::OpenOptionsPtr open_options,
filesystem::mojom::DirectoryPtr directory,
const std::string& dbname,
+ const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+ memory_dump_id,
leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
OpenCallback callback) override;
- void OpenInMemory(leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
- OpenInMemoryCallback callback) override;
+ void OpenInMemory(
+ const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+ memory_dump_id,
+ leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
+ OpenInMemoryCallback callback) override;
void Destroy(filesystem::mojom::DirectoryPtr directory,
const std::string& dbname,
DestroyCallback callback) override;
diff --git a/chromium/components/leveldb/leveldb_service_unittest.cc b/chromium/components/leveldb/leveldb_service_unittest.cc
index 0651b07a56f..0ffefeb7e9a 100644
--- a/chromium/components/leveldb/leveldb_service_unittest.cc
+++ b/chromium/components/leveldb/leveldb_service_unittest.cc
@@ -98,7 +98,7 @@ void LevelDBSyncOpenInMemory(mojom::LevelDBService* leveldb,
mojom::LevelDBDatabaseAssociatedRequest database,
mojom::DatabaseError* out_error) {
base::RunLoop run_loop;
- leveldb->OpenInMemory(std::move(database),
+ leveldb->OpenInMemory(base::nullopt, std::move(database),
Capture(out_error, run_loop.QuitClosure()));
run_loop.Run();
}
@@ -265,7 +265,7 @@ TEST_F(LevelDBServiceTest, Reconnect) {
options->create_if_missing = true;
base::RunLoop run_loop;
leveldb()->OpenWithOptions(std::move(options), std::move(directory), "test",
- MakeRequest(&database),
+ base::nullopt, MakeRequest(&database),
Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -285,7 +285,8 @@ TEST_F(LevelDBServiceTest, Reconnect) {
// Reconnect to the database.
mojom::LevelDBDatabaseAssociatedPtr database;
base::RunLoop run_loop;
- leveldb()->Open(std::move(directory), "test", MakeRequest(&database),
+ leveldb()->Open(std::move(directory), "test", base::nullopt,
+ MakeRequest(&database),
Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -315,7 +316,7 @@ TEST_F(LevelDBServiceTest, Destroy) {
options->create_if_missing = true;
base::RunLoop run_loop;
leveldb()->OpenWithOptions(std::move(options), std::move(directory), "test",
- MakeRequest(&database),
+ base::nullopt, MakeRequest(&database),
Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -347,7 +348,8 @@ TEST_F(LevelDBServiceTest, Destroy) {
// Reconnect to the database should fail.
mojom::LevelDBDatabaseAssociatedPtr database;
base::RunLoop run_loop;
- leveldb()->Open(std::move(directory), "test", MakeRequest(&database),
+ leveldb()->Open(std::move(directory), "test", base::nullopt,
+ MakeRequest(&database),
Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::INVALID_ARGUMENT, error);
diff --git a/chromium/components/leveldb/public/interfaces/BUILD.gn b/chromium/components/leveldb/public/interfaces/BUILD.gn
index 514eeb9f785..1b3172e6408 100644
--- a/chromium/components/leveldb/public/interfaces/BUILD.gn
+++ b/chromium/components/leveldb/public/interfaces/BUILD.gn
@@ -5,6 +5,8 @@
import("//mojo/public/tools/bindings/mojom.gni")
mojom("interfaces") {
+ support_lazy_serialization = true
+
sources = [
"leveldb.mojom",
]
diff --git a/chromium/components/leveldb/public/interfaces/leveldb.mojom b/chromium/components/leveldb/public/interfaces/leveldb.mojom
index 11e56709553..938bd730d4a 100644
--- a/chromium/components/leveldb/public/interfaces/leveldb.mojom
+++ b/chromium/components/leveldb/public/interfaces/leveldb.mojom
@@ -6,6 +6,7 @@ module leveldb.mojom;
import "components/filesystem/public/interfaces/directory.mojom";
import "mojo/common/unguessable_token.mojom";
+import "mojo/common/memory_allocator_dump_cross_process_uid.mojom";
enum DatabaseError {
OK,
@@ -58,6 +59,9 @@ struct OpenOptions {
// https://code.google.com/p/chromium/issues/detail?id=227313#c11 for
// details.)
int32 max_open_files = 80;
+
+ // Default size is 8 megabytes.
+ uint64 block_cache_size = 8388608;
};
// Service which hands out databases.
@@ -66,16 +70,19 @@ interface LevelDBService {
// Fails if the database doesn't already exist.
Open(filesystem.mojom.Directory directory,
string dbname,
+ mojo.common.mojom.MemoryAllocatorDumpCrossProcessUid? memory_dump_id,
associated LevelDBDatabase& database) => (DatabaseError status);
// Open the database with the specified "name" in the specified "directory".
OpenWithOptions(OpenOptions options,
filesystem.mojom.Directory directory,
string dbname,
+ mojo.common.mojom.MemoryAllocatorDumpCrossProcessUid? memory_dump_id,
associated LevelDBDatabase& database) => (DatabaseError status);
// Opens a database stored purely in memory.
- OpenInMemory(associated LevelDBDatabase& database) => (DatabaseError status);
+ OpenInMemory(mojo.common.mojom.MemoryAllocatorDumpCrossProcessUid? memory_dump_id,
+ associated LevelDBDatabase& database) => (DatabaseError status);
// Destroys the contents of the specified database. Returns OK if the database
// already didn't exist.
diff --git a/chromium/components/leveldb/remote_iterator_unittest.cc b/chromium/components/leveldb/remote_iterator_unittest.cc
index 8665711ae65..f37bd48d1a1 100644
--- a/chromium/components/leveldb/remote_iterator_unittest.cc
+++ b/chromium/components/leveldb/remote_iterator_unittest.cc
@@ -52,7 +52,7 @@ class RemoteIteratorTest : public service_manager::test::ServiceTest {
mojom::DatabaseError error;
base::RunLoop run_loop;
- leveldb()->OpenInMemory(MakeRequest(&database_),
+ leveldb()->OpenInMemory(base::nullopt, MakeRequest(&database_),
Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::OK, error);
diff --git a/chromium/components/leveldb_proto/leveldb_database.cc b/chromium/components/leveldb_proto/leveldb_database.cc
index 9791c19d5ad..073dbec3f51 100644
--- a/chromium/components/leveldb_proto/leveldb_database.cc
+++ b/chromium/components/leveldb_proto/leveldb_database.cc
@@ -15,6 +15,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_checker.h"
#include "base/trace_event/memory_dump_manager.h"
@@ -61,19 +62,15 @@ bool LevelDB::InitWithOptions(const base::FilePath& database_dir,
std::string path = database_dir.AsUTF8Unsafe();
- leveldb::DB* db = NULL;
- leveldb::Status status = leveldb::DB::Open(options, path, &db);
+ leveldb::Status status = leveldb_env::OpenDB(options, path, &db_);
if (open_histogram_)
open_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(status));
if (status.IsCorruption()) {
base::DeleteFile(database_dir, true);
- status = leveldb::DB::Open(options, path, &db);
+ status = leveldb_env::OpenDB(options, path, &db_);
}
if (status.ok()) {
- CHECK(db);
- db_.reset(db);
-
base::trace_event::MemoryDumpManager::GetInstance()
->RegisterDumpProviderWithSequencedTaskRunner(
this, "LevelDB", base::SequencedTaskRunnerHandle::Get(),
@@ -86,16 +83,33 @@ bool LevelDB::InitWithOptions(const base::FilePath& database_dir,
return false;
}
+static size_t DefaultBlockCacheSize() {
+ if (base::SysInfo::IsLowEndDevice())
+ return 512 * 1024; // 512KB
+ else
+ return 8 * 1024 * 1024; // 8MB
+}
+
bool LevelDB::Init(const leveldb_proto::Options& options) {
leveldb::Options leveldb_options;
leveldb_options.create_if_missing = true;
leveldb_options.max_open_files = 0; // Use minimum.
leveldb_options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
+
+ static leveldb::Cache* default_block_cache =
+ leveldb::NewLRUCache(DefaultBlockCacheSize());
+
if (options.write_buffer_size != 0)
leveldb_options.write_buffer_size = options.write_buffer_size;
if (options.read_cache_size != 0) {
custom_block_cache_.reset(leveldb::NewLRUCache(options.read_cache_size));
leveldb_options.block_cache = custom_block_cache_.get();
+ } else {
+ // By default, allocate a single block cache to be shared across
+ // all LevelDB instances created via this method.
+ // See also content/browser/indexed_db/leveldb/leveldb_database.cc,
+ // which has its own shared block cache for IndexedDB databases.
+ leveldb_options.block_cache = default_block_cache;
}
if (options.database_dir.empty()) {
env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
@@ -205,12 +219,10 @@ bool LevelDB::OnMemoryDump(const base::trace_event::MemoryDumpArgs& dump_args,
dump->AddString("client_name", "", client_name_);
}
- // Memory is allocated from system allocator (malloc).
- const char* system_allocator_pool_name =
- base::trace_event::MemoryDumpManager::GetInstance()
- ->system_allocator_pool_name();
- if (system_allocator_pool_name)
- pmd->AddSuballocation(dump->guid(), system_allocator_pool_name);
+ // All leveldb databases are already dumped by leveldb_env::DBTracker. Add
+ // an edge to avoid double counting.
+ pmd->AddSuballocation(dump->guid(),
+ leveldb_env::DBTracker::GetMemoryDumpName(db_.get()));
return true;
}
diff --git a/chromium/components/leveldb_proto/options.h b/chromium/components/leveldb_proto/options.h
index 0778ade68a6..07cd18a2a76 100644
--- a/chromium/components/leveldb_proto/options.h
+++ b/chromium/components/leveldb_proto/options.h
@@ -10,6 +10,9 @@
namespace leveldb_proto {
struct Options {
+ // If read_cache_size is specified, a segregated block cache will be
+ // created for this LevelDB instance. Otherwise a shared block cache
+ // will be used.
Options(const base::FilePath& database_dir)
: database_dir(database_dir), write_buffer_size(0), read_cache_size(0) {}
Options(const base::FilePath& database_dir,
diff --git a/chromium/components/leveldb_proto/proto_database_impl_unittest.cc b/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
index c198b50c821..691d774d47a 100644
--- a/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
+++ b/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
@@ -647,16 +647,13 @@ TEST(ProtoDatabaseImplLevelDBTest, TestOnMemoryDumpEmitsData) {
new base::trace_event::ProcessMemoryDump(nullptr, dump_args));
db->OnMemoryDump(dump_args, process_memory_dump.get());
- const auto& allocator_dumps = process_memory_dump->allocator_dumps();
- const char* system_allocator_pool_name =
- base::trace_event::MemoryDumpManager::GetInstance()
- ->system_allocator_pool_name();
- size_t expected_dump_count = system_allocator_pool_name ? 2 : 1;
- EXPECT_EQ(expected_dump_count, allocator_dumps.size());
- for (const auto& dump : allocator_dumps) {
- ASSERT_TRUE(dump.first.find("leveldb/leveldb_proto/") == 0 ||
- dump.first.find(system_allocator_pool_name) == 0);
+ size_t leveldb_dump_count = 0;
+ for (const auto& dump : process_memory_dump->allocator_dumps()) {
+ if (dump.first.find("leveldb/leveldb_proto/") == 0) {
+ leveldb_dump_count++;
+ }
}
+ ASSERT_EQ(1u, leveldb_dump_count);
}
} // namespace leveldb_proto
diff --git a/chromium/components/leveldb_proto/testing/fake_db.h b/chromium/components/leveldb_proto/testing/fake_db.h
index bb2cb1be929..1bf7550bc81 100644
--- a/chromium/components/leveldb_proto/testing/fake_db.h
+++ b/chromium/components/leveldb_proto/testing/fake_db.h
@@ -57,6 +57,8 @@ class FakeDB : public ProtoDatabase<T> {
void UpdateCallback(bool success);
+ void DestroyCallback(bool success);
+
static base::FilePath DirectoryForTestDB();
private:
@@ -81,6 +83,7 @@ class FakeDB : public ProtoDatabase<T> {
Callback load_keys_callback_;
Callback get_callback_;
Callback update_callback_;
+ Callback destroy_callback_;
};
template <typename T>
@@ -147,7 +150,10 @@ void FakeDB<T>::GetEntry(const std::string& key,
}
template <typename T>
-void FakeDB<T>::Destroy(typename ProtoDatabase<T>::DestroyCallback callback) {}
+void FakeDB<T>::Destroy(typename ProtoDatabase<T>::DestroyCallback callback) {
+ db_->clear();
+ destroy_callback_ = std::move(callback);
+}
template <typename T>
base::FilePath& FakeDB<T>::GetDirectory() {
@@ -179,6 +185,11 @@ void FakeDB<T>::UpdateCallback(bool success) {
std::move(update_callback_).Run(success);
}
+template <typename T>
+void FakeDB<T>::DestroyCallback(bool success) {
+ std::move(destroy_callback_).Run(success);
+}
+
// static
template <typename T>
void FakeDB<T>::RunLoadCallback(
diff --git a/chromium/components/login/OWNERS b/chromium/components/login/OWNERS
index 5539f89a17c..8f195fe9977 100644
--- a/chromium/components/login/OWNERS
+++ b/chromium/components/login/OWNERS
@@ -1,4 +1,5 @@
achuith@chromium.org
alemate@chromium.org
+jdufault@chromium.org
piman@chromium.org
xiyuan@chromium.org
diff --git a/chromium/components/login/screens/screen_context.cc b/chromium/components/login/screens/screen_context.cc
index 5e170b682d8..343570b87f8 100644
--- a/chromium/components/login/screens/screen_context.cc
+++ b/chromium/components/login/screens/screen_context.cc
@@ -5,8 +5,10 @@
#include "components/login/screens/screen_context.h"
#include <memory>
+#include <utility>
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
namespace login {
@@ -29,6 +31,7 @@ ScreenContext::ScreenContext() {
}
ScreenContext::~ScreenContext() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
bool ScreenContext::SetBoolean(const KeyType& key, bool value) {
@@ -131,17 +134,17 @@ void ScreenContext::CopyFrom(ScreenContext& context) {
}
bool ScreenContext::HasKey(const KeyType& key) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return storage_.HasKey(key);
}
bool ScreenContext::HasChanges() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !changes_.empty();
}
void ScreenContext::GetChangesAndReset(base::DictionaryValue* diff) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(diff);
changes_.Swap(diff);
changes_.Clear();
@@ -149,7 +152,7 @@ void ScreenContext::GetChangesAndReset(base::DictionaryValue* diff) {
void ScreenContext::ApplyChanges(const base::DictionaryValue& diff,
std::vector<std::string>* keys) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!HasChanges());
if (keys) {
keys->clear();
@@ -165,7 +168,7 @@ void ScreenContext::ApplyChanges(const base::DictionaryValue& diff,
}
bool ScreenContext::Set(const KeyType& key, base::Value* value) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(value);
std::unique_ptr<base::Value> new_value(value);
@@ -176,8 +179,8 @@ bool ScreenContext::Set(const KeyType& key, base::Value* value) {
if (in_storage && new_value->Equals(current_value))
return false;
- changes_.Set(key, new_value->DeepCopy());
- storage_.Set(key, new_value.release());
+ changes_.Set(key, base::MakeUnique<base::Value>(*new_value));
+ storage_.Set(key, std::move(new_value));
return true;
}
diff --git a/chromium/components/login/screens/screen_context.h b/chromium/components/login/screens/screen_context.h
index 665f79894e6..03fc037ae71 100644
--- a/chromium/components/login/screens/screen_context.h
+++ b/chromium/components/login/screens/screen_context.h
@@ -10,8 +10,8 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "base/sequence_checker.h"
#include "base/strings/string16.h"
-#include "base/threading/non_thread_safe.h"
#include "base/values.h"
#include "components/login/base_screen_handler_utils.h"
#include "components/login/login_export.h"
@@ -31,7 +31,7 @@ namespace login {
// ScreenContext memorizes changed key-value pairs and returns them
// via GetChangesAndReset() method. After call to this method an
// internal buffer of changes will be cleared.
-class LOGIN_EXPORT ScreenContext : public base::NonThreadSafe {
+class LOGIN_EXPORT ScreenContext {
public:
typedef std::string KeyType;
typedef base::Value ValueType;
@@ -94,7 +94,7 @@ class LOGIN_EXPORT ScreenContext : public base::NonThreadSafe {
template <typename T>
T Get(const KeyType& key) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const base::Value* value;
bool has_key = storage_.Get(key, &value);
DCHECK(has_key);
@@ -108,7 +108,7 @@ class LOGIN_EXPORT ScreenContext : public base::NonThreadSafe {
template <typename T>
T Get(const KeyType& key, const T& default_value) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!HasKey(key))
return default_value;
return Get<T>(key);
@@ -120,6 +120,8 @@ class LOGIN_EXPORT ScreenContext : public base::NonThreadSafe {
// Contains all pending changes.
base::DictionaryValue changes_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(ScreenContext);
};
diff --git a/chromium/components/login_dialog_strings.grdp b/chromium/components/login_dialog_strings.grdp
index 1929e7bd62b..bd4417ced3a 100644
--- a/chromium/components/login_dialog_strings.grdp
+++ b/chromium/components/login_dialog_strings.grdp
@@ -1,23 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
- <message name="IDS_LOGIN_DIALOG_TITLE" desc="String to be displayed in the title bar of the login prompt dialog" formatter_data="android_java">
- Authentication Required
- </message>
+ <if expr="use_titlecase">
+ <message name="IDS_LOGIN_DIALOG_TITLE" desc="In title case: String to be displayed in the title bar of the login prompt dialog" formatter_data="android_java">
+ Authentication Required
+ </message>
+ <message name="IDS_LOGIN_DIALOG_OK_BUTTON_LABEL" desc="In title case: The label of the 'Log In' button on the login prompt dialog" formatter_data="android_java">
+ Log In
+ </message>
+ </if>
+ <if expr="not use_titlecase">
+ <message name="IDS_LOGIN_DIALOG_TITLE" desc="In sentence case: String to be displayed in the title bar of the login prompt dialog" formatter_data="android_java">
+ Authentication required
+ </message>
+ <message name="IDS_LOGIN_DIALOG_OK_BUTTON_LABEL" desc="In sentence case: The label of the 'Log in' button on the login prompt dialog" formatter_data="android_java">
+ Log in
+ </message>
+ </if>
<message name="IDS_LOGIN_DIALOG_AUTHORITY" desc="String to be displayed in the login prompt dialog to explain that the user needs to log in, and the name of the web site">
<ph name="DOMAIN">$1<ex>google.com</ex></ph> requires a username and password.
</message>
<message name="IDS_LOGIN_DIALOG_PROXY_AUTHORITY" desc="String to be displayed in the proxy login prompt dialog to explain that the user needs to log in, and the name of the proxy">
The proxy <ph name="DOMAIN">$1<ex>google.com</ex></ph> requires a username and password.
</message>
+ <message name="IDS_LOGIN_DIALOG_NOT_PRIVATE" desc="A phrase displayed on login dialogs if the connection to the current website is not private.">
+ Your connection to this site is not private
+ </message>
<message name="IDS_LOGIN_DIALOG_USERNAME_FIELD" desc="The label of the username field in the login prompt dialog" formatter_data="android_java">
- User Name:
+ Username
</message>
<message name="IDS_LOGIN_DIALOG_PASSWORD_FIELD" desc="The label of the password field in the login prompt dialog" formatter_data="android_java">
- Password:
- </message>
- <message name="IDS_LOGIN_DIALOG_OK_BUTTON_LABEL" desc="The label of the 'Log In' button on the login prompt dialog" formatter_data="android_java">
- Log In
+ Password
</message>
</grit-part>
diff --git a/chromium/components/machine_intelligence/BUILD.gn b/chromium/components/machine_intelligence/BUILD.gn
new file mode 100644
index 00000000000..e9d98df1a34
--- /dev/null
+++ b/chromium/components/machine_intelligence/BUILD.gn
@@ -0,0 +1,39 @@
+# 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.
+
+static_library("machine_intelligence") {
+ sources = [
+ "ranker_model.cc",
+ "ranker_model.h",
+ "ranker_model_loader.cc",
+ "ranker_model_loader.h",
+ "ranker_url_fetcher.cc",
+ "ranker_url_fetcher.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/data_use_measurement/core",
+ "//components/machine_intelligence/proto",
+ "//net",
+ "//url",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "ranker_model_loader_unittest.cc",
+ "ranker_model_unittest.cc",
+ ]
+
+ deps = [
+ ":machine_intelligence",
+ "//base",
+ "//components/machine_intelligence/proto",
+ "//net:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/machine_intelligence/DEPS b/chromium/components/machine_intelligence/DEPS
new file mode 100644
index 00000000000..be55946db4e
--- /dev/null
+++ b/chromium/components/machine_intelligence/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+components/data_use_measurement/core",
+ "+components/metrics",
+ "+net",
+] \ No newline at end of file
diff --git a/chromium/components/machine_intelligence/OWNERS b/chromium/components/machine_intelligence/OWNERS
new file mode 100644
index 00000000000..5f847a5bbc9
--- /dev/null
+++ b/chromium/components/machine_intelligence/OWNERS
@@ -0,0 +1,2 @@
+hamelphi@chromium.org
+rogerm@chromium.org \ No newline at end of file
diff --git a/chromium/components/translate/core/browser/proto/BUILD.gn b/chromium/components/machine_intelligence/proto/BUILD.gn
index e7424bd588b..e7424bd588b 100644
--- a/chromium/components/translate/core/browser/proto/BUILD.gn
+++ b/chromium/components/machine_intelligence/proto/BUILD.gn
diff --git a/chromium/components/machine_intelligence/proto/ranker_model.proto b/chromium/components/machine_intelligence/proto/ranker_model.proto
new file mode 100644
index 00000000000..e40473b5085
--- /dev/null
+++ b/chromium/components/machine_intelligence/proto/ranker_model.proto
@@ -0,0 +1,49 @@
+// Copyright (c) 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.
+//
+// Experimental Translation Assist Model to allow/suppress translation prompts.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+import "translate_ranker_model.proto";
+
+package machine_intelligence;
+
+// Metadata for a ranker model instance. This data describes how the ranker
+// model should be interpreted/used.
+message RankerModelMetadata {
+ // An identifier denoting the type or purpose of this model.
+ optional string name = 1;
+
+ // An identifier denoting the specific instance of this model. For example:
+ // "Experiment B"
+ optional string label = 2;
+
+ // An identifier, typically a URL, denoting the source from which this model
+ // was obtained. The model referenced with a given source is presumed to be
+ // immutable; this can be used as a cache control mechanism. If the currently
+ // configured model source matches the source of a cached model, and the
+ // cached model has not expired then there is no need to refresh the model.
+ optional string source = 3;
+
+ // The timestamp at which this model was downloaded. This will be set by the
+ // model loader before it caches the model to disk.
+ optional int64 last_modified_sec = 4;
+
+ // The (optional) number of seconds after which this model should be
+ // considered expired. If the value is zero or not set, then the cached
+ // instance of the model never expires. A new download can be triggered by
+ // changing the configured source URL for the model loader.
+ optional int64 cache_duration_sec = 5;
+}
+
+// Defines an envelope/wrapper for general models.
+message RankerModelProto {
+ // Metadata.
+ optional RankerModelMetadata metadata = 1;
+
+ oneof model { TranslateRankerModel translate = 2; }
+}
diff --git a/chromium/components/machine_intelligence/proto/translate_ranker_model.proto b/chromium/components/machine_intelligence/proto/translate_ranker_model.proto
new file mode 100644
index 00000000000..8a4f4ec1785
--- /dev/null
+++ b/chromium/components/machine_intelligence/proto/translate_ranker_model.proto
@@ -0,0 +1,46 @@
+// 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.
+//
+// Experimental Translation Assist Model to allow/suppress translation prompts.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package machine_intelligence;
+
+// Defines a Chrome Ranker Translate Model.
+// Next tag: 13
+message TranslateRankerModel {
+ // A number that identifies the version of this model.
+ optional uint32 version = 1;
+
+ // Defines the weights and bias of a Logistic Regression Model.
+ message LogisticRegressionModel {
+ // Decision threshold. If not defined, use 0.5.
+ optional float threshold = 12;
+
+ optional float bias = 1;
+
+ optional float accept_ratio_weight = 2;
+ optional float decline_ratio_weight = 3;
+ optional float ignore_ratio_weight = 4;
+
+ optional float accept_count_weight = 9;
+ optional float decline_count_weight = 10;
+ optional float ignore_count_weight = 11;
+
+ // One-hot features are encoded in the form of a map. These maps
+ // each contain an element 'UNKNOWN' to use in case the key is not
+ // found in the map.
+ map<string, float> source_language_weight = 5;
+ map<string, float> target_language_weight = 6;
+ map<string, float> country_weight = 7;
+ map<string, float> locale_weight = 8;
+ }
+
+ oneof model_revision {
+ LogisticRegressionModel logistic_regression_model = 2;
+ }
+}
diff --git a/chromium/components/machine_intelligence/ranker_model.cc b/chromium/components/machine_intelligence/ranker_model.cc
new file mode 100644
index 00000000000..c6158650934
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_model.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/machine_intelligence/ranker_model.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "components/machine_intelligence/proto/ranker_model.pb.h"
+
+namespace machine_intelligence {
+
+RankerModel::RankerModel() : proto_(base::MakeUnique<RankerModelProto>()) {}
+
+RankerModel::~RankerModel() {}
+
+// static
+std::unique_ptr<RankerModel> RankerModel::FromString(const std::string& data) {
+ auto model = base::MakeUnique<RankerModel>();
+ if (!model->mutable_proto()->ParseFromString(data))
+ return nullptr;
+ return model;
+}
+
+bool RankerModel::IsExpired() const {
+ if (!proto().has_metadata())
+ return true;
+
+ const auto& metadata = proto().metadata();
+
+ // If the age of the model cannot be determined, presume it to be expired.
+ if (!metadata.has_last_modified_sec())
+ return true;
+
+ // If the model has no set cache duration, then it never expires.
+ if (!metadata.has_cache_duration_sec() || metadata.cache_duration_sec() == 0)
+ return false;
+
+ // Otherwise, a model is expired if its age exceeds the cache duration.
+ base::Time last_modified =
+ base::Time() + base::TimeDelta::FromSeconds(metadata.last_modified_sec());
+ base::TimeDelta age = base::Time::Now() - last_modified;
+ return age > base::TimeDelta::FromSeconds(metadata.cache_duration_sec());
+}
+
+const std::string& RankerModel::GetSourceURL() const {
+ return proto_->metadata().source();
+}
+
+std::string RankerModel::SerializeAsString() const {
+ return proto_->SerializeAsString();
+}
+
+} // namespace machine_intelligence
diff --git a/chromium/components/machine_intelligence/ranker_model.h b/chromium/components/machine_intelligence/ranker_model.h
new file mode 100644
index 00000000000..80aa32abb7d
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_model.h
@@ -0,0 +1,45 @@
+// 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_MACHINE_INTELLIGENCE_RANKER_MODEL_H_
+#define COMPONENTS_MACHINE_INTELLIGENCE_RANKER_MODEL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+
+namespace machine_intelligence {
+
+class RankerModelProto;
+
+class RankerModel {
+ public:
+ RankerModel();
+ ~RankerModel();
+
+ // Returns a new ranker model constructed from |data|.
+ static std::unique_ptr<RankerModel> FromString(const std::string& data);
+
+ const RankerModelProto& proto() const { return *proto_; }
+ RankerModelProto* mutable_proto() const { return proto_.get(); }
+
+ // Returns true if this ranker model has expired.
+ bool IsExpired() const;
+
+ const std::string& GetSourceURL() const;
+
+ // Returns a serialization of this ranker model.
+ std::string SerializeAsString() const;
+
+ private:
+ // The underlying ranker model proto.
+ std::unique_ptr<RankerModelProto> proto_;
+
+ DISALLOW_COPY_AND_ASSIGN(RankerModel);
+};
+
+} // namespace machine_intelligence
+
+#endif // COMPONENTS_MACHINE_INTELLIGENCE_RANKER_MODEL_H_
diff --git a/chromium/components/machine_intelligence/ranker_model_loader.cc b/chromium/components/machine_intelligence/ranker_model_loader.cc
new file mode 100644
index 00000000000..927cd2cdf30
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_model_loader.cc
@@ -0,0 +1,306 @@
+// 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/machine_intelligence/ranker_model_loader.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/files/important_file_writer.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/profiler/scoped_tracker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/machine_intelligence/proto/ranker_model.pb.h"
+#include "components/machine_intelligence/ranker_model.h"
+#include "components/machine_intelligence/ranker_url_fetcher.h"
+
+namespace machine_intelligence {
+namespace {
+
+// The minimum duration, in minutes, between download attempts.
+constexpr int kMinRetryDelayMins = 3;
+
+// Suffixes for the various histograms produced by the backend.
+const char kWriteTimerHistogram[] = ".Timer.WriteModel";
+const char kReadTimerHistogram[] = ".Timer.ReadModel";
+const char kDownloadTimerHistogram[] = ".Timer.DownloadModel";
+const char kParsetimerHistogram[] = ".Timer.ParseModel";
+const char kModelStatusHistogram[] = ".Model.Status";
+
+// Helper function to UMA log a timer histograms.
+void RecordTimerHistogram(const std::string& name, base::TimeDelta duration) {
+ base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
+ name, base::TimeDelta::FromMilliseconds(10),
+ base::TimeDelta::FromMilliseconds(200000), 100,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ DCHECK(counter);
+ counter->AddTime(duration);
+}
+
+// A helper class to produce a scoped timer histogram that supports using a
+// non-static-const name.
+class MyScopedHistogramTimer {
+ public:
+ MyScopedHistogramTimer(const base::StringPiece& name)
+ : name_(name.begin(), name.end()), start_(base::TimeTicks::Now()) {}
+
+ ~MyScopedHistogramTimer() {
+ RecordTimerHistogram(name_, base::TimeTicks::Now() - start_);
+ }
+
+ private:
+ const std::string name_;
+ const base::TimeTicks start_;
+
+ DISALLOW_COPY_AND_ASSIGN(MyScopedHistogramTimer);
+};
+
+std::string LoadFromFile(const base::FilePath& model_path) {
+ DCHECK(!model_path.empty());
+ DVLOG(2) << "Reading data from: " << model_path.value();
+ std::string data;
+ if (!base::ReadFileToString(model_path, &data) || data.empty()) {
+ DVLOG(2) << "Failed to read data from: " << model_path.value();
+ data.clear();
+ }
+ return data;
+}
+
+void SaveToFile(const GURL& model_url,
+ const base::FilePath& model_path,
+ const std::string& model_data,
+ const std::string& uma_prefix) {
+ DVLOG(2) << "Saving model from '" << model_url << "'' to '"
+ << model_path.value() << "'.";
+ MyScopedHistogramTimer timer(uma_prefix + kWriteTimerHistogram);
+ base::ImportantFileWriter::WriteFileAtomically(model_path, model_data);
+}
+
+} // namespace
+
+RankerModelLoader::RankerModelLoader(
+ ValidateModelCallback validate_model_cb,
+ OnModelAvailableCallback on_model_available_cb,
+ net::URLRequestContextGetter* request_context_getter,
+ base::FilePath model_path,
+ GURL model_url,
+ std::string uma_prefix)
+ : background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ 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),
+ model_path_(std::move(model_path)),
+ model_url_(std::move(model_url)),
+ uma_prefix_(std::move(uma_prefix)),
+ url_fetcher_(base::MakeUnique<RankerURLFetcher>()),
+ weak_ptr_factory_(this) {}
+
+RankerModelLoader::~RankerModelLoader() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void RankerModelLoader::NotifyOfRankerActivity() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ switch (state_) {
+ case LoaderState::NOT_STARTED:
+ if (!model_path_.empty()) {
+ StartLoadFromFile();
+ break;
+ }
+ // There was no configured model path. Switch the state to IDLE and
+ // fall through to consider the URL.
+ state_ = LoaderState::IDLE;
+ case LoaderState::IDLE:
+ if (model_url_.is_valid()) {
+ StartLoadFromURL();
+ break;
+ }
+ // There was no configured model URL. Switch the state to FINISHED and
+ // fall through.
+ state_ = LoaderState::FINISHED;
+ case LoaderState::FINISHED:
+ case LoaderState::LOADING_FROM_FILE:
+ case LoaderState::LOADING_FROM_URL:
+ // Nothing to do.
+ break;
+ }
+}
+
+void RankerModelLoader::StartLoadFromFile() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(state_, LoaderState::NOT_STARTED);
+ DCHECK(!model_path_.empty());
+ state_ = LoaderState::LOADING_FROM_FILE;
+ load_start_time_ = base::TimeTicks::Now();
+ base::PostTaskAndReplyWithResult(background_task_runner_.get(), FROM_HERE,
+ base::Bind(&LoadFromFile, model_path_),
+ base::Bind(&RankerModelLoader::OnFileLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void RankerModelLoader::OnFileLoaded(const std::string& data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(state_, LoaderState::LOADING_FROM_FILE);
+
+ // Record the duration of the download.
+ RecordTimerHistogram(uma_prefix_ + kReadTimerHistogram,
+ base::TimeTicks::Now() - load_start_time_);
+
+ // Empty data means |model_path| wasn't successfully read. Otherwise,
+ // parse and validate the model.
+ std::unique_ptr<RankerModel> model;
+ if (data.empty()) {
+ ReportModelStatus(RankerModelStatus::LOAD_FROM_CACHE_FAILED);
+ } else {
+ model = CreateAndValidateModel(data);
+ }
+
+ // If |model| is nullptr, then data is empty or the parse failed. Transition
+ // to IDLE, from which URL download can be attempted.
+ if (!model) {
+ state_ = LoaderState::IDLE;
+ } else {
+ // The model is valid. The client is willing/able to use it. Keep track
+ // of where it originated and whether or not is has expired.
+ std::string url_spec = model->GetSourceURL();
+ bool is_expired = model->IsExpired();
+ bool is_finished = url_spec == model_url_.spec() && !is_expired;
+
+ DVLOG(2) << (is_expired ? "Expired m" : "M") << "odel in '"
+ << model_path_.value() << "' was originally downloaded from '"
+ << url_spec << "'.";
+
+ // If the cached model came from currently configured |model_url_| and has
+ // not expired, transition to FINISHED, as there is no need for a model
+ // download; otherwise, transition to IDLE.
+ state_ = is_finished ? LoaderState::FINISHED : LoaderState::IDLE;
+
+ // Transfer the model to the client.
+ on_model_available_cb_.Run(std::move(model));
+ }
+
+ // Notify the state machine. This will immediately kick off a download if
+ // one is required, instead of waiting for the next organic detection of
+ // ranker activity.
+ NotifyOfRankerActivity();
+}
+
+void RankerModelLoader::StartLoadFromURL() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(state_, LoaderState::IDLE);
+ DCHECK(model_url_.is_valid());
+
+ // Do nothing if the download attempts should be throttled.
+ if (base::TimeTicks::Now() < next_earliest_download_time_) {
+ DVLOG(2) << "Last download attempt was too recent.";
+ ReportModelStatus(RankerModelStatus::DOWNLOAD_THROTTLED);
+ return;
+ }
+
+ // Kick off the next download attempt and reset the time of the next earliest
+ // allowable download attempt.
+ DVLOG(2) << "Downloading model from: " << model_url_;
+ state_ = LoaderState::LOADING_FROM_URL;
+ load_start_time_ = base::TimeTicks::Now();
+ next_earliest_download_time_ =
+ load_start_time_ + base::TimeDelta::FromMinutes(kMinRetryDelayMins);
+ bool request_started =
+ url_fetcher_->Request(model_url_,
+ base::Bind(&RankerModelLoader::OnURLFetched,
+ weak_ptr_factory_.GetWeakPtr()),
+ request_context_getter_.get());
+
+ // |url_fetcher_| maintains a request retry counter. If all allowed attempts
+ // have already been exhausted, then the loader is finished and has abandoned
+ // loading the model.
+ if (!request_started) {
+ DVLOG(2) << "Model download abandoned.";
+ ReportModelStatus(RankerModelStatus::MODEL_LOADING_ABANDONED);
+ state_ = LoaderState::FINISHED;
+ }
+}
+
+void RankerModelLoader::OnURLFetched(bool success, const std::string& data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(state_, LoaderState::LOADING_FROM_URL);
+
+ // Record the duration of the download.
+ RecordTimerHistogram(uma_prefix_ + kDownloadTimerHistogram,
+ base::TimeTicks::Now() - load_start_time_);
+
+ // On request failure, transition back to IDLE. The loader will retry, or
+ // enforce the max download attempts, later.
+ if (!success || data.empty()) {
+ DVLOG(2) << "Download from '" << model_url_ << "'' failed.";
+ ReportModelStatus(RankerModelStatus::DOWNLOAD_FAILED);
+ state_ = LoaderState::IDLE;
+ return;
+ }
+
+ // Attempt to loads the model. If this fails, transition back to IDLE. The
+ // loader will retry, or enfore the max download attempts, later.
+ auto model = CreateAndValidateModel(data);
+ if (!model) {
+ DVLOG(2) << "Model from '" << model_url_ << "'' not valid.";
+ state_ = LoaderState::IDLE;
+ return;
+ }
+
+ // The model is valid. Update the metadata to track the source URL and
+ // download timestamp.
+ auto* metadata = model->mutable_proto()->mutable_metadata();
+ metadata->set_source(model_url_.spec());
+ metadata->set_last_modified_sec(
+ (base::Time::Now() - base::Time()).InSeconds());
+
+ // Cache the model to model_path_, in the background.
+ if (!model_path_.empty()) {
+ background_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&SaveToFile, model_url_, model_path_,
+ model->SerializeAsString(), uma_prefix_));
+ }
+
+ // The loader is finished. Transfer the model to the client.
+ state_ = LoaderState::FINISHED;
+ on_model_available_cb_.Run(std::move(model));
+}
+
+std::unique_ptr<RankerModel> RankerModelLoader::CreateAndValidateModel(
+ const std::string& data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ MyScopedHistogramTimer timer(uma_prefix_ + kParsetimerHistogram);
+ auto model = RankerModel::FromString(data);
+ if (ReportModelStatus(model ? validate_model_cb_.Run(*model)
+ : RankerModelStatus::PARSE_FAILED) !=
+ RankerModelStatus::OK) {
+ return nullptr;
+ }
+ return model;
+}
+
+RankerModelStatus RankerModelLoader::ReportModelStatus(
+ RankerModelStatus model_status) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+ uma_prefix_ + kModelStatusHistogram, 1,
+ static_cast<int>(RankerModelStatus::MAX),
+ static_cast<int>(RankerModelStatus::MAX) + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ if (histogram)
+ histogram->Add(static_cast<int>(model_status));
+ return model_status;
+}
+
+} // namespace machine_intelligence
diff --git a/chromium/components/machine_intelligence/ranker_model_loader.h b/chromium/components/machine_intelligence/ranker_model_loader.h
new file mode 100644
index 00000000000..60afa10fc8b
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_model_loader.h
@@ -0,0 +1,196 @@
+// 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_MACHINE_INTELLIGENCE_RANKER_MODEL_LOADER_H_
+#define COMPONENTS_MACHINE_INTELLIGENCE_RANKER_MODEL_LOADER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#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 machine_intelligence {
+
+class RankerURLFetcher;
+class RankerModel;
+
+// Enumeration denoting the outcome of an attempt to download the model. This
+// must be kept in sync with the RankerModelStatus enum in histograms.xml
+enum class RankerModelStatus {
+ OK = 0,
+ DOWNLOAD_THROTTLED = 1,
+ DOWNLOAD_FAILED = 2,
+ PARSE_FAILED = 3,
+ VALIDATION_FAILED = 4,
+ INCOMPATIBLE = 5,
+ LOAD_FROM_CACHE_FAILED = 6,
+ MODEL_LOADING_ABANDONED = 7,
+
+ // Insert new values above this line.
+ MAX
+};
+
+// Loads a ranker model. Will attempt to load the model from disk cache. If it
+// fails, will attempt to download from the given URL.
+class RankerModelLoader {
+ public:
+ // Callback to validate a ranker model on behalf of the model loader client.
+ // For example, the callback might validate that the model is compatible with
+ // the features generated when ranking translation offerings. This will be
+ // called on the sequence on which the model loader was constructed.
+ using ValidateModelCallback = base::RepeatingCallback<RankerModelStatus(
+ const machine_intelligence::RankerModel&)>;
+
+ // Called to transfer ownership of a loaded model back to the model loader
+ // client. This will be called on the sequence on which the model loader was
+ // constructed.
+ using OnModelAvailableCallback = base::RepeatingCallback<void(
+ std::unique_ptr<machine_intelligence::RankerModel>)>;
+
+ // |validate_model_callback| may be called on any sequence; it must be thread
+ // safe.
+ //
+ // |on_model_available_callback| will be called on the sequence on which the
+ // ranker model loader is constructed.
+ //
+ // |model_path| denotes the file path at which the model is cached. The loader
+ // will attempt to load the model from this path first, falling back to the
+ // |model_url| if the model cannot be loaded or has expired. Upon downloading
+ // a fresh model from |model_url| the model will be persisted to |model_path|
+ // for subsequent caching.
+ //
+ // |model_url| denotes the URL from which the model should be loaded, if it
+ // has not already been cached at |model_path|.
+ //
+ // |uma_prefix| will be used as a prefix for the names of all UMA metrics
+ // generated by this loader.
+ RankerModelLoader(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);
+
+ ~RankerModelLoader();
+
+ // Call this method periodically to notify the model loader the ranker is
+ // actively in use. The user's engagement with the ranked feature is used
+ // as a proxy for network availability and activity. If a model download
+ // is pending, this will trigger (subject to retry and frequency limits) a
+ // model download attempt.
+ void NotifyOfRankerActivity();
+
+ private:
+ // A enum to track the current loader state.
+ enum class LoaderState {
+ // The loader is newly created and has not started trying to load the model.
+ // This state can transition to LOADING_FROM_FILE or, if |model_path_| is
+ // empty, to LOADING_FROM_URL. If both |model_path_| and |model_url_| are
+ // empty/invalid then it can transition to FINISHED.
+ NOT_STARTED,
+ // The loader is busy loading the model from |model_path_| in the
+ // background. This state can transition to FINISHED if the loaded model is
+ // compatible and up to date; otherwise, this state can transition to IDLE.
+ LOADING_FROM_FILE,
+ // The loader is not currently busy. The loader can transition to the
+ // LOADING_FROM_URL_ state if |model_url_| is valid; the loader can also
+ // transition to FINISHED if it the maximum number of download attempts
+ // has been reached.
+ IDLE,
+ // The loader is busy loading the model from |model_url_| in the background.
+ // This state can transition to FINISHED if the loaded model is valid;
+ // otherwise, this state can re-transition to IDLE.
+ LOADING_FROM_URL,
+ // The loader has finished. This is the terminal state.
+ FINISHED
+ };
+
+ // Asynchronously initiates loading the model from model_path_;
+ void StartLoadFromFile();
+
+ // Called when the background worker has finished loading |data| from
+ // |model_path_|. If |data| is empty, the load from |model_path_| failed.
+ void OnFileLoaded(const std::string& data);
+
+ // Asynchronously initiates loading the model from |model_url_|.
+ void StartLoadFromURL();
+
+ // Called when |url_fetcher_| has finished loading |data| from |model_url_|.
+ //
+ // This call signature is mandated by RankerURLFetcher.
+ //
+ // success - true of the download was successful
+ // data - the body of the downloads response
+ void OnURLFetched(bool success, const std::string& data);
+
+ // Parse |data| and return a validated model. Returns nullptr on failure.
+ std::unique_ptr<machine_intelligence::RankerModel> CreateAndValidateModel(
+ const std::string& data);
+
+ // Helper function to log |model_status| to UMA and return it.
+ RankerModelStatus ReportModelStatus(RankerModelStatus model_status);
+
+ // Validates that ranker model loader tasks are all performed on the same
+ // sequence.
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ // The task runner on which background tasks are performed.
+ const scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
+ // Validates a ranker model on behalf of the model loader client. This will be
+ // called on the sequence on which the model leader was constructed.
+ const ValidateModelCallback validate_model_cb_;
+
+ // Transfers ownership of a loaded model back to the model loader client.
+ // This will be called on the sequence on which the model loader was
+ // constructed.
+ const OnModelAvailableCallback on_model_available_cb_;
+
+ // Request Context Getter used for RankerURLFetcher.
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+
+ // The path at which the model is (or should be) cached.
+ const base::FilePath model_path_;
+
+ // The URL from which to download the model if the model is not in the cache
+ // or the cached model is invalid/expired.
+ const GURL model_url_;
+
+ // This will prefix all UMA metrics generated by the model loader.
+ const std::string uma_prefix_;
+
+ // Used to download model data from |model_url_|.
+ std::unique_ptr<RankerURLFetcher> url_fetcher_;
+
+ // The next time before which no new attempts to download the model should be
+ // attempted.
+ base::TimeTicks next_earliest_download_time_;
+
+ // Tracks the last time of the last attempt to load a model, either from file
+ // of from URL. Used for UMA reporting of load durations.
+ base::TimeTicks load_start_time_;
+
+ // The current state of the loader.
+ LoaderState state_ = LoaderState::NOT_STARTED;
+
+ // Creates weak pointer references to the loader.
+ base::WeakPtrFactory<RankerModelLoader> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RankerModelLoader);
+};
+
+} // namespace machine_intelligence
+
+#endif // COMPONENTS_MACHINE_INTELLIGENCE_RANKER_MODEL_LOADER_H_
diff --git a/chromium/components/machine_intelligence/ranker_model_loader_unittest.cc b/chromium/components/machine_intelligence/ranker_model_loader_unittest.cc
new file mode 100644
index 00000000000..d96d7b2ad5b
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_model_loader_unittest.cc
@@ -0,0 +1,359 @@
+// 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/machine_intelligence/ranker_model_loader.h"
+
+#include <deque>
+#include <initializer_list>
+#include <memory>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_scheduler.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_scheduler.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/machine_intelligence/proto/ranker_model.pb.h"
+#include "components/machine_intelligence/proto/translate_ranker_model.pb.h"
+#include "components/machine_intelligence/ranker_model.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using base::TaskScheduler;
+using machine_intelligence::RankerModel;
+using machine_intelligence::RankerModelLoader;
+using machine_intelligence::RankerModelStatus;
+
+const char kInvalidModelData[] = "not a valid model";
+const int kInvalidModelSize = sizeof(kInvalidModelData) - 1;
+
+class RankerModelLoaderTest : public ::testing::Test {
+ protected:
+ RankerModelLoaderTest();
+
+ void SetUp() override;
+
+ void TearDown() override;
+
+ // Returns a copy of |model|.
+ static std::unique_ptr<RankerModel> Clone(const RankerModel& model);
+
+ // Returns true if |m1| and |m2| are identical.
+ static bool IsEqual(const RankerModel& m1, const RankerModel& m2);
+
+ // Returns true if |m1| and |m2| are identical modulo metadata.
+ static bool IsEquivalent(const RankerModel& m1, const RankerModel& m2);
+
+ // Helper method to drive the loader for |model_path| and |model_url|.
+ bool DoLoaderTest(const base::FilePath& model_path, const GURL& model_url);
+
+ // Initialize the "remote" model data used for testing.
+ void InitRemoteModels();
+
+ // Initialize the "local" model data used for testing.
+ void InitLocalModels();
+
+ // Helper method used by InitRemoteModels() and InitLocalModels().
+ void InitModel(const GURL& model_url,
+ const base::Time& last_modified,
+ const base::TimeDelta& cache_duration,
+ RankerModel* model);
+
+ // Save |model| to |model_path|. Used by InitRemoteModels() and
+ // InitLocalModels()
+ void SaveModel(const RankerModel& model, const base::FilePath& model_path);
+
+ // Implements RankerModelLoader's ValidateModelCallback interface.
+ RankerModelStatus ValidateModel(const RankerModel& model);
+
+ // Implements RankerModelLoader's OnModelAvailableCallback interface.
+ void OnModelAvailable(std::unique_ptr<RankerModel> model);
+
+ // Sets up the task scheduling/task-runner environment for each test.
+ base::test::ScopedTaskScheduler scoped_task_scheduler_;
+
+ // Override the default URL fetcher to return custom responses for tests.
+ net::FakeURLFetcherFactory url_fetcher_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'.
+ std::deque<RankerModelStatus> validate_model_response_;
+
+ // A cached to remember the model validation calls.
+ std::vector<std::unique_ptr<RankerModel>> validated_models_;
+
+ // A cache to remember the OnModelAvailable calls.
+ std::vector<std::unique_ptr<RankerModel>> available_models_;
+
+ // Cached model file paths.
+ base::FilePath local_model_path_;
+ base::FilePath expired_model_path_;
+ base::FilePath invalid_model_path_;
+
+ // Model URLS.
+ GURL remote_model_url_;
+ GURL invalid_model_url_;
+ GURL failed_model_url_;
+
+ // Model Data.
+ RankerModel remote_model_;
+ RankerModel local_model_;
+ RankerModel expired_model_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RankerModelLoaderTest);
+};
+
+RankerModelLoaderTest::RankerModelLoaderTest()
+ : url_fetcher_factory_(nullptr) {}
+
+void RankerModelLoaderTest::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();
+
+ // Setup the model file paths.
+ local_model_path_ = temp_dir_path.AppendASCII("local_model.bin");
+ expired_model_path_ = temp_dir_path.AppendASCII("expired_model.bin");
+ invalid_model_path_ = temp_dir_path.AppendASCII("invalid_model.bin");
+
+ // Setup the model URLs.
+ remote_model_url_ = GURL("https://some.url.net/good.model.bin");
+ invalid_model_url_ = GURL("https://some.url.net/bad.model.bin");
+ failed_model_url_ = GURL("https://some.url.net/fail");
+
+ // Initialize the model data.
+ ASSERT_NO_FATAL_FAILURE(InitRemoteModels());
+ ASSERT_NO_FATAL_FAILURE(InitLocalModels());
+}
+
+void RankerModelLoaderTest::TearDown() {
+ base::RunLoop().RunUntilIdle();
+}
+
+// static
+std::unique_ptr<RankerModel> RankerModelLoaderTest::Clone(
+ const RankerModel& model) {
+ auto copy = base::MakeUnique<RankerModel>();
+ *copy->mutable_proto() = model.proto();
+ return copy;
+}
+
+// static
+bool RankerModelLoaderTest::IsEqual(const RankerModel& m1,
+ const RankerModel& m2) {
+ return m1.SerializeAsString() == m2.SerializeAsString();
+}
+
+// static
+bool RankerModelLoaderTest::IsEquivalent(const RankerModel& m1,
+ const RankerModel& m2) {
+ auto copy_m1 = Clone(m1);
+ copy_m1->mutable_proto()->mutable_metadata()->Clear();
+
+ auto copy_m2 = Clone(m2);
+ copy_m2->mutable_proto()->mutable_metadata()->Clear();
+
+ return IsEqual(*copy_m1, *copy_m2);
+}
+
+bool RankerModelLoaderTest::DoLoaderTest(const base::FilePath& model_path,
+ const GURL& model_url) {
+ auto loader = base::MakeUnique<RankerModelLoader>(
+ base::Bind(&RankerModelLoaderTest::ValidateModel, base::Unretained(this)),
+ base::Bind(&RankerModelLoaderTest::OnModelAvailable,
+ base::Unretained(this)),
+ request_context_getter_.get(), model_path, model_url,
+ "RankerModelLoaderTest");
+ loader->NotifyOfRankerActivity();
+ base::RunLoop().RunUntilIdle();
+
+ return true;
+}
+
+void RankerModelLoaderTest::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);
+}
+
+void RankerModelLoaderTest::InitLocalModels() {
+ InitModel(remote_model_url_, base::Time::Now(), base::TimeDelta::FromDays(30),
+ &local_model_);
+ InitModel(remote_model_url_,
+ base::Time::Now() - base::TimeDelta::FromDays(60),
+ base::TimeDelta::FromDays(30), &expired_model_);
+ SaveModel(local_model_, local_model_path_);
+ SaveModel(expired_model_, expired_model_path_);
+ ASSERT_EQ(base::WriteFile(invalid_model_path_, kInvalidModelData,
+ kInvalidModelSize),
+ kInvalidModelSize);
+}
+
+void RankerModelLoaderTest::InitModel(const GURL& model_url,
+ const base::Time& last_modified,
+ const base::TimeDelta& cache_duration,
+ RankerModel* model) {
+ ASSERT_TRUE(model != nullptr);
+ model->mutable_proto()->Clear();
+
+ auto* metadata = model->mutable_proto()->mutable_metadata();
+ if (!model_url.is_empty())
+ metadata->set_source(model_url.spec());
+ if (!last_modified.is_null()) {
+ auto last_modified_sec = (last_modified - base::Time()).InSeconds();
+ metadata->set_last_modified_sec(last_modified_sec);
+ }
+ if (!cache_duration.is_zero())
+ metadata->set_cache_duration_sec(cache_duration.InSeconds());
+
+ auto* translate = model->mutable_proto()->mutable_translate();
+ translate->set_version(1);
+
+ auto* logit = translate->mutable_logistic_regression_model();
+ logit->set_bias(0.1f);
+ logit->set_accept_ratio_weight(0.2f);
+ logit->set_decline_ratio_weight(0.3f);
+ logit->set_ignore_ratio_weight(0.4f);
+}
+
+void RankerModelLoaderTest::SaveModel(const RankerModel& model,
+ const base::FilePath& model_path) {
+ std::string model_str = model.SerializeAsString();
+ ASSERT_EQ(base::WriteFile(model_path, model_str.data(), model_str.size()),
+ static_cast<int>(model_str.size()));
+}
+
+RankerModelStatus RankerModelLoaderTest::ValidateModel(
+ const RankerModel& model) {
+ validated_models_.push_back(Clone(model));
+ RankerModelStatus response = RankerModelStatus::OK;
+ if (!validate_model_response_.empty()) {
+ response = validate_model_response_.front();
+ validate_model_response_.pop_front();
+ }
+ return response;
+}
+
+void RankerModelLoaderTest::OnModelAvailable(
+ std::unique_ptr<RankerModel> model) {
+ available_models_.push_back(std::move(model));
+}
+
+} // namespace
+
+TEST_F(RankerModelLoaderTest, NoLocalOrRemoteModel) {
+ ASSERT_TRUE(DoLoaderTest(base::FilePath(), GURL()));
+
+ EXPECT_EQ(0U, validated_models_.size());
+ EXPECT_EQ(0U, available_models_.size());
+}
+
+TEST_F(RankerModelLoaderTest, BadLocalAndRemoteModel) {
+ ASSERT_TRUE(DoLoaderTest(invalid_model_path_, invalid_model_url_));
+
+ EXPECT_EQ(0U, validated_models_.size());
+ EXPECT_EQ(0U, available_models_.size());
+}
+
+TEST_F(RankerModelLoaderTest, LoadFromFileOnly) {
+ EXPECT_TRUE(DoLoaderTest(local_model_path_, GURL()));
+
+ ASSERT_EQ(1U, validated_models_.size());
+ ASSERT_EQ(1U, available_models_.size());
+ EXPECT_TRUE(IsEqual(*validated_models_[0], local_model_));
+ EXPECT_TRUE(IsEqual(*available_models_[0], local_model_));
+}
+
+TEST_F(RankerModelLoaderTest, LoadFromFileSkipsDownload) {
+ ASSERT_TRUE(DoLoaderTest(local_model_path_, remote_model_url_));
+
+ ASSERT_EQ(1U, validated_models_.size());
+ ASSERT_EQ(1U, available_models_.size());
+ EXPECT_TRUE(IsEqual(*validated_models_[0], local_model_));
+ EXPECT_TRUE(IsEqual(*available_models_[0], local_model_));
+}
+
+TEST_F(RankerModelLoaderTest, LoadFromFileAndBadUrl) {
+ ASSERT_TRUE(DoLoaderTest(local_model_path_, invalid_model_url_));
+ ASSERT_EQ(1U, validated_models_.size());
+ ASSERT_EQ(1U, available_models_.size());
+ EXPECT_TRUE(IsEqual(*validated_models_[0], local_model_));
+ EXPECT_TRUE(IsEqual(*available_models_[0], local_model_));
+}
+
+TEST_F(RankerModelLoaderTest, LoadFromURLOnly) {
+ ASSERT_TRUE(DoLoaderTest(base::FilePath(), remote_model_url_));
+ ASSERT_EQ(1U, validated_models_.size());
+ ASSERT_EQ(1U, available_models_.size());
+ EXPECT_TRUE(IsEquivalent(*validated_models_[0], remote_model_));
+ EXPECT_TRUE(IsEquivalent(*available_models_[0], remote_model_));
+}
+
+TEST_F(RankerModelLoaderTest, LoadFromExpiredFileTriggersDownload) {
+ ASSERT_TRUE(DoLoaderTest(expired_model_path_, remote_model_url_));
+ ASSERT_EQ(2U, validated_models_.size());
+ ASSERT_EQ(2U, available_models_.size());
+ EXPECT_TRUE(IsEquivalent(*validated_models_[0], local_model_));
+ EXPECT_TRUE(IsEquivalent(*available_models_[0], local_model_));
+ EXPECT_TRUE(IsEquivalent(*validated_models_[1], remote_model_));
+ EXPECT_TRUE(IsEquivalent(*available_models_[1], remote_model_));
+}
+
+TEST_F(RankerModelLoaderTest, LoadFromBadFileTriggersDownload) {
+ ASSERT_TRUE(DoLoaderTest(invalid_model_path_, remote_model_url_));
+ ASSERT_EQ(1U, validated_models_.size());
+ ASSERT_EQ(1U, available_models_.size());
+ EXPECT_TRUE(IsEquivalent(*validated_models_[0], remote_model_));
+ EXPECT_TRUE(IsEquivalent(*available_models_[0], remote_model_));
+}
+
+TEST_F(RankerModelLoaderTest, IncompatibleCachedFileTriggersDownload) {
+ validate_model_response_.push_back(RankerModelStatus::INCOMPATIBLE);
+
+ ASSERT_TRUE(DoLoaderTest(local_model_path_, remote_model_url_));
+ ASSERT_EQ(2U, validated_models_.size());
+ ASSERT_EQ(1U, available_models_.size());
+ EXPECT_TRUE(IsEquivalent(*validated_models_[0], local_model_));
+ EXPECT_TRUE(IsEquivalent(*validated_models_[1], remote_model_));
+ EXPECT_TRUE(IsEquivalent(*available_models_[0], remote_model_));
+}
+
+TEST_F(RankerModelLoaderTest, IncompatibleDownloadedFileKeepsExpired) {
+ validate_model_response_.push_back(RankerModelStatus::OK);
+ validate_model_response_.push_back(RankerModelStatus::INCOMPATIBLE);
+
+ ASSERT_TRUE(DoLoaderTest(expired_model_path_, remote_model_url_));
+ ASSERT_EQ(2U, validated_models_.size());
+ ASSERT_EQ(1U, available_models_.size());
+ EXPECT_TRUE(IsEquivalent(*validated_models_[0], local_model_));
+ EXPECT_TRUE(IsEquivalent(*validated_models_[1], remote_model_));
+ EXPECT_TRUE(IsEquivalent(*available_models_[0], local_model_));
+}
diff --git a/chromium/components/machine_intelligence/ranker_model_unittest.cc b/chromium/components/machine_intelligence/ranker_model_unittest.cc
new file mode 100644
index 00000000000..64e43fbfe46
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_model_unittest.cc
@@ -0,0 +1,76 @@
+// 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/machine_intelligence/ranker_model.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "components/machine_intelligence/proto/ranker_model.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using machine_intelligence::RankerModel;
+
+const char kModelURL[] = "https://some.url.net/model";
+
+int64_t InSeconds(const base::Time t) {
+ return (t - base::Time()).InSeconds();
+}
+
+std::unique_ptr<RankerModel> NewModel(const std::string& model_url,
+ base::Time last_modified,
+ base::TimeDelta cache_duration) {
+ std::unique_ptr<RankerModel> model = base::MakeUnique<RankerModel>();
+ auto* metadata = model->mutable_proto()->mutable_metadata();
+ if (!model_url.empty())
+ metadata->set_source(model_url);
+ if (!last_modified.is_null())
+ metadata->set_last_modified_sec(InSeconds(last_modified));
+ if (!cache_duration.is_zero())
+ metadata->set_cache_duration_sec(cache_duration.InSeconds());
+
+ auto* translate = model->mutable_proto()->mutable_translate();
+ translate->set_version(1);
+
+ auto* logit = translate->mutable_logistic_regression_model();
+ logit->set_bias(0.1f);
+ logit->set_accept_ratio_weight(0.2f);
+ logit->set_decline_ratio_weight(0.3f);
+ logit->set_ignore_ratio_weight(0.4f);
+ return model;
+}
+
+} // namespace
+
+TEST(RankerModelTest, Serialization) {
+ base::Time last_modified = base::Time::Now();
+ base::TimeDelta cache_duration = base::TimeDelta::FromDays(3);
+ std::unique_ptr<RankerModel> original_model =
+ NewModel(kModelURL, last_modified, cache_duration);
+ std::string original_model_str = original_model->SerializeAsString();
+ std::unique_ptr<RankerModel> serialized_model =
+ RankerModel::FromString(original_model_str);
+ std::string serialized_model_str = serialized_model->SerializeAsString();
+
+ EXPECT_EQ(serialized_model_str, original_model_str);
+ EXPECT_EQ(serialized_model->GetSourceURL(), kModelURL);
+ EXPECT_EQ(serialized_model->proto().metadata().last_modified_sec(),
+ InSeconds(last_modified));
+ EXPECT_EQ(serialized_model->proto().metadata().cache_duration_sec(),
+ cache_duration.InSeconds());
+}
+
+TEST(RankerModelTest, IsExpired) {
+ base::Time today = base::Time::Now();
+ base::TimeDelta days_15 = base::TimeDelta::FromDays(15);
+ base::TimeDelta days_30 = base::TimeDelta::FromDays(30);
+ base::TimeDelta days_60 = base::TimeDelta::FromDays(60);
+
+ EXPECT_FALSE(NewModel(kModelURL, today, days_30)->IsExpired());
+ EXPECT_FALSE(NewModel(kModelURL, today - days_15, days_30)->IsExpired());
+ EXPECT_TRUE(NewModel(kModelURL, today - days_60, days_30)->IsExpired());
+}
diff --git a/chromium/components/machine_intelligence/ranker_url_fetcher.cc b/chromium/components/machine_intelligence/ranker_url_fetcher.cc
new file mode 100644
index 00000000000..965d716d76b
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_url_fetcher.cc
@@ -0,0 +1,109 @@
+// 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/machine_intelligence/ranker_url_fetcher.h"
+
+#include "base/memory/ref_counted.h"
+#include "components/data_use_measurement/core/data_use_user_data.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"
+#include "net/url_request/url_request_status.h"
+
+namespace machine_intelligence {
+
+namespace {
+
+// Retry parameter for fetching.
+const int kMaxRetry = 16;
+
+} // namespace
+
+RankerURLFetcher::RankerURLFetcher() : state_(IDLE), retry_count_(0) {}
+
+RankerURLFetcher::~RankerURLFetcher() {}
+
+bool RankerURLFetcher::Request(
+ const GURL& url,
+ const RankerURLFetcher::Callback& callback,
+ net::URLRequestContextGetter* request_context_getter) {
+ // This function is not supposed to be called if the previous operation is not
+ // finished.
+ if (state_ == REQUESTING) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (retry_count_ >= kMaxRetry)
+ return false;
+ retry_count_++;
+
+ state_ = REQUESTING;
+ url_ = url;
+ callback_ = callback;
+
+ if (request_context_getter == nullptr)
+ return false;
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("ranker_url_fetcher", R"(
+ semantics {
+ sender: "AssistRanker"
+ description:
+ "Chrome can provide a better UI experience by using machine "
+ "learning models to determine if we should show you or not an "
+ "assist prompt. For instance, Chrome may use features such as "
+ "the detected language of the current page and the past "
+ "interaction with the TransalteUI to decide whether or not we "
+ "should offer you to translate this page. Google returns "
+ "trained machine learning models that will be used to take "
+ "such decision."
+ trigger:
+ "At startup."
+ data:
+ "Path to a model. No user data is included."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: false
+ setting:
+ "NA"
+ })");
+ // 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();
+
+ return true;
+}
+
+void RankerURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
+ DCHECK(fetcher_.get() == source);
+
+ std::string data;
+ if (source->GetStatus().status() == net::URLRequestStatus::SUCCESS &&
+ source->GetResponseCode() == net::HTTP_OK) {
+ state_ = COMPLETED;
+ source->GetResponseAsString(&data);
+ } 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);
+}
+
+} // namespace machine_intelligence
diff --git a/chromium/components/machine_intelligence/ranker_url_fetcher.h b/chromium/components/machine_intelligence/ranker_url_fetcher.h
new file mode 100644
index 00000000000..2e3d6aee1a6
--- /dev/null
+++ b/chromium/components/machine_intelligence/ranker_url_fetcher.h
@@ -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.
+
+#ifndef COMPONENTS_MACHINE_INTELLIGENCE_RANKER_URL_FETCHER_H_
+#define COMPONENTS_MACHINE_INTELLIGENCE_RANKER_URL_FETCHER_H_
+
+#include <memory>
+
+#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 machine_intelligence {
+
+// Downloads Ranker models.
+class RankerURLFetcher : public net::URLFetcherDelegate {
+ public:
+ // Callback type for Request().
+ typedef base::Callback<void(bool, const std::string&)> Callback;
+
+ // Represents internal state if the fetch is completed successfully.
+ enum State {
+ IDLE, // No fetch request was issued.
+ REQUESTING, // A fetch request was issued, but not finished yet.
+ COMPLETED, // The last fetch request was finished successfully.
+ FAILED, // The last fetch request was finished with a failure.
+ };
+
+ RankerURLFetcher();
+ ~RankerURLFetcher() override;
+
+ int max_retry_on_5xx() { return max_retry_on_5xx_; }
+ void set_max_retry_on_5xx(int count) { max_retry_on_5xx_ = count; }
+
+ // Requests to |url|. |callback| will be invoked when the function returns
+ // true, and the request is finished asynchronously.
+ // 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);
+
+ // Gets internal state.
+ State state() { return state_; }
+
+ // net::URLFetcherDelegate implementation:
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ private:
+ // URL to send the request.
+ GURL url_;
+
+ // Internal state.
+ enum State state_;
+
+ // URLFetcher instance.
+ std::unique_ptr<net::URLFetcher> fetcher_;
+
+ // Callback passed at Request(). It will be invoked when an asynchronous
+ // fetch operation is finished.
+ Callback callback_;
+
+ // Counts how many times did it try to fetch the language list.
+ int retry_count_;
+
+ // Max number how many times to retry on the server error
+ int max_retry_on_5xx_;
+
+ DISALLOW_COPY_AND_ASSIGN(RankerURLFetcher);
+};
+
+} // namespace machine_intelligence
+
+#endif // COMPONENTS_MACHINE_INTELLIGENCE_RANKER_URL_FETCHER_H_
diff --git a/chromium/components/metrics/BUILD.gn b/chromium/components/metrics/BUILD.gn
index 1d03801d2c9..319d951e816 100644
--- a/chromium/components/metrics/BUILD.gn
+++ b/chromium/components/metrics/BUILD.gn
@@ -31,6 +31,8 @@ static_library("metrics") {
"environment_recorder.h",
"execution_phase.cc",
"execution_phase.h",
+ "field_trials_provider.cc",
+ "field_trials_provider.h",
"file_metrics_provider.cc",
"file_metrics_provider.h",
"histogram_encoder.cc",
@@ -77,6 +79,8 @@ static_library("metrics") {
"persisted_logs_metrics.h",
"persisted_logs_metrics_impl.cc",
"persisted_logs_metrics_impl.h",
+ "persistent_system_profile.cc",
+ "persistent_system_profile.h",
"reporting_service.cc",
"reporting_service.h",
"stability_metrics_helper.cc",
@@ -88,6 +92,8 @@ static_library("metrics") {
"system_memory_stats_recorder_win.cc",
"url_constants.cc",
"url_constants.h",
+ "version_utils.cc",
+ "version_utils.h",
]
public_deps = [
@@ -100,6 +106,7 @@ static_library("metrics") {
"//base:base_static",
"//components/prefs",
"//components/variations",
+ "//components/version_info:version_info",
"//third_party/zlib/google:compression_utils",
]
@@ -149,8 +156,6 @@ static_library("net") {
"net/net_metrics_log_uploader.h",
"net/network_metrics_provider.cc",
"net/network_metrics_provider.h",
- "net/version_utils.cc",
- "net/version_utils.h",
"net/wifi_access_point_info_provider.cc",
"net/wifi_access_point_info_provider.h",
]
@@ -164,7 +169,6 @@ static_library("net") {
"//base",
"//components/data_use_measurement/core",
"//components/variations",
- "//components/version_info",
"//net",
"//url",
]
@@ -269,9 +273,12 @@ static_library("single_sample_metrics") {
source_set("call_stack_profile_params") {
sources = [
- "call_stack_profile_params.cc",
"call_stack_profile_params.h",
]
+
+ deps = [
+ "//base:base",
+ ]
}
source_set("call_stacks") {
@@ -341,6 +348,7 @@ source_set("unit_tests") {
"data_use_tracker_unittest.cc",
"drive_metrics_provider_unittest.cc",
"environment_recorder_unittest.cc",
+ "field_trials_provider_unittest.cc",
"file_metrics_provider_unittest.cc",
"histogram_encoder_unittest.cc",
"machine_id_provider_win_unittest.cc",
@@ -352,6 +360,7 @@ source_set("unit_tests") {
"net/net_metrics_log_uploader_unittest.cc",
"net/network_metrics_provider_unittest.cc",
"persisted_logs_unittest.cc",
+ "persistent_system_profile_unittest.cc",
"profiler/profiler_metrics_provider_unittest.cc",
"profiler/tracking_synchronizer_unittest.cc",
"single_sample_metrics_factory_impl_unittest.cc",
diff --git a/chromium/components/metrics/call_stack_profile_collector.cc b/chromium/components/metrics/call_stack_profile_collector.cc
index 4cdd9ec8348..3d35abf54ee 100644
--- a/chromium/components/metrics/call_stack_profile_collector.cc
+++ b/chromium/components/metrics/call_stack_profile_collector.cc
@@ -5,6 +5,7 @@
#include "components/metrics/call_stack_profile_collector.h"
#include <utility>
+#include <vector>
#include "base/memory/ptr_util.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
@@ -22,7 +23,6 @@ CallStackProfileCollector::~CallStackProfileCollector() {}
// static
void CallStackProfileCollector::Create(
CallStackProfileParams::Process expected_process,
- const service_manager::BindSourceInfo& source_info,
mojom::CallStackProfileCollectorRequest request) {
mojo::MakeStrongBinding(
base::MakeUnique<CallStackProfileCollector>(expected_process),
@@ -36,8 +36,10 @@ void CallStackProfileCollector::Collect(
if (params.process != expected_process_)
return;
+ CallStackProfileParams params_copy = params;
+ params_copy.start_timestamp = start_timestamp;
CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
- params, start_timestamp, std::move(profiles));
+ &params_copy, std::move(profiles));
}
} // namespace metrics
diff --git a/chromium/components/metrics/call_stack_profile_collector.h b/chromium/components/metrics/call_stack_profile_collector.h
index 80f391742be..6eeb6a87b44 100644
--- a/chromium/components/metrics/call_stack_profile_collector.h
+++ b/chromium/components/metrics/call_stack_profile_collector.h
@@ -8,10 +8,6 @@
#include "base/macros.h"
#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h"
-namespace service_manager {
-struct BindSourceInfo;
-}
-
namespace metrics {
class CallStackProfileCollector : public mojom::CallStackProfileCollector {
@@ -24,7 +20,6 @@ class CallStackProfileCollector : public mojom::CallStackProfileCollector {
// Create a collector to receive profiles from |expected_process|.
static void Create(CallStackProfileParams::Process expected_process,
- const service_manager::BindSourceInfo& source_info,
mojom::CallStackProfileCollectorRequest request);
// mojom::CallStackProfileCollector:
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.cc b/chromium/components/metrics/call_stack_profile_metrics_provider.cc
index 9bd823b34e9..472c6c54b6a 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider.cc
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider.cc
@@ -10,6 +10,7 @@
#include <algorithm>
#include <cstring>
#include <map>
+#include <string>
#include <utility>
#include <vector>
@@ -18,13 +19,15 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
-#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
#include "base/metrics/metrics_hashes.h"
+#include "base/process/process_info.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
+#include "build/build_config.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
using base::StackSamplingProfiler;
@@ -33,6 +36,10 @@ namespace metrics {
namespace {
+// Interval for periodic (post-startup) sampling, when enabled.
+constexpr base::TimeDelta kPeriodicSamplingInterval =
+ base::TimeDelta::FromSeconds(1);
+
// 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
@@ -49,14 +56,21 @@ const ProcessPhase
ProcessPhase::SHUTDOWN_START,
};
+// Parameters for browser process sampling. Not const since these may be
+// changed when transitioning from start-up profiling to periodic profiling.
+CallStackProfileParams g_browser_process_sampling_params(
+ metrics::CallStackProfileParams::BROWSER_PROCESS,
+ metrics::CallStackProfileParams::UI_THREAD,
+ metrics::CallStackProfileParams::PROCESS_STARTUP,
+ metrics::CallStackProfileParams::MAY_SHUFFLE);
+
// ProfilesState --------------------------------------------------------------
// A set of profiles and the CallStackProfileMetricsProvider state associated
// with them.
struct ProfilesState {
ProfilesState(const CallStackProfileParams& params,
- base::StackSamplingProfiler::CallStackProfiles profiles,
- base::TimeTicks start_timestamp);
+ StackSamplingProfiler::CallStackProfiles profiles);
ProfilesState(ProfilesState&&);
ProfilesState& operator=(ProfilesState&&);
@@ -65,25 +79,15 @@ struct ProfilesState {
CallStackProfileParams params;
// The call stack profiles collected by the profiler.
- base::StackSamplingProfiler::CallStackProfiles profiles;
-
- // The time at which the CallStackProfileMetricsProvider became aware of the
- // request for profiling. In particular, this is when callback was requested
- // via CallStackProfileMetricsProvider::GetProfilerCallback(). Used to
- // determine if collection was disabled during the collection of the profile.
- base::TimeTicks start_timestamp;
+ StackSamplingProfiler::CallStackProfiles profiles;
private:
DISALLOW_COPY_AND_ASSIGN(ProfilesState);
};
-ProfilesState::ProfilesState(
- const CallStackProfileParams& params,
- base::StackSamplingProfiler::CallStackProfiles profiles,
- base::TimeTicks start_timestamp)
- : params(params),
- profiles(std::move(profiles)),
- start_timestamp(start_timestamp) {}
+ProfilesState::ProfilesState(const CallStackProfileParams& params,
+ StackSamplingProfiler::CallStackProfiles profiles)
+ : params(params), profiles(std::move(profiles)) {}
ProfilesState::ProfilesState(ProfilesState&&) = default;
@@ -93,12 +97,11 @@ ProfilesState& ProfilesState::operator=(ProfilesState&&) = default;
// PendingProfiles ------------------------------------------------------------
// Singleton class responsible for retaining profiles received via the callback
-// created by CallStackProfileMetricsProvider::GetProfilerCallback(). These are
-// then sent to UMA on the invocation of
-// CallStackProfileMetricsProvider::ProvideGeneralMetrics(). We need to store
-// the profiles outside of a CallStackProfileMetricsProvider instance since
-// callers may start profiling before the CallStackProfileMetricsProvider is
-// created.
+// created by GetProfilerCallback(). These are then sent to UMA on the
+// invocation of CallStackProfileMetricsProvider::ProvideGeneralMetrics(). We
+// need to store the profiles outside of a CallStackProfileMetricsProvider
+// instance since callers may start profiling before the
+// CallStackProfileMetricsProvider is created.
//
// Member functions on this class may be called on any thread.
class PendingProfiles {
@@ -180,10 +183,19 @@ void PendingProfiles::CollectProfilesIfCollectionEnabled(
// 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_ >= profiles.params.start_timestamp)) {
return;
}
+ if (profiles.params.trigger == CallStackProfileParams::PERIODIC_COLLECTION) {
+ DCHECK_EQ(1U, profiles.profiles.size());
+ profiles.profiles[0].sampling_period = kPeriodicSamplingInterval;
+ // Use the process uptime as the collection time to indicate when this
+ // profile was collected. This is useful to account for uptime bias during
+ // analysis.
+ profiles.profiles[0].profile_duration = internal::GetUptime();
+ }
+
profiles_.push_back(std::move(profiles));
}
@@ -208,17 +220,40 @@ PendingProfiles::~PendingProfiles() {}
// 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(
- const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
+base::Optional<StackSamplingProfiler::SamplingParams>
+ReceiveCompletedProfilesImpl(
+ CallStackProfileParams* params,
StackSamplingProfiler::CallStackProfiles profiles) {
PendingProfiles::GetInstance()->CollectProfilesIfCollectionEnabled(
- ProfilesState(params, std::move(profiles), start_timestamp));
+ ProfilesState(*params, std::move(profiles)));
+
+ // Now, schedule periodic sampling every 1s, if enabled by trial.
+ // TODO(asvitkine): Support periodic sampling for non-browser processes.
+ // TODO(asvitkine): In the future, we may want to have finer grained control
+ // over this, for example ending sampling after some amount of time.
+ if (CallStackProfileMetricsProvider::IsPeriodicSamplingEnabled() &&
+ params->process == CallStackProfileParams::BROWSER_PROCESS &&
+ params->thread == CallStackProfileParams::UI_THREAD) {
+ params->trigger = metrics::CallStackProfileParams::PERIODIC_COLLECTION;
+ params->start_timestamp = base::TimeTicks::Now();
+
+ StackSamplingProfiler::SamplingParams sampling_params;
+ sampling_params.initial_delay = kPeriodicSamplingInterval;
+ sampling_params.bursts = 1;
+ sampling_params.samples_per_burst = 1;
+ // Below are unused:
+ sampling_params.burst_interval = base::TimeDelta::FromMilliseconds(0);
+ sampling_params.sampling_interval = base::TimeDelta::FromMilliseconds(0);
+ return sampling_params;
+ }
+ return base::Optional<StackSamplingProfiler::SamplingParams>();
}
// Invoked on an arbitrary thread. Ignores the provided profiles.
-void IgnoreCompletedProfiles(
- StackSamplingProfiler::CallStackProfiles profiles) {}
+base::Optional<StackSamplingProfiler::SamplingParams> IgnoreCompletedProfiles(
+ StackSamplingProfiler::CallStackProfiles profiles) {
+ return base::Optional<StackSamplingProfiler::SamplingParams>();
+}
// Functions to encode protobufs ----------------------------------------------
@@ -284,47 +319,40 @@ void CopyProfileToProto(
if (profile.samples.empty())
return;
- if (ordering_spec == CallStackProfileParams::PRESERVE_ORDER) {
- // Collapse only consecutive repeated samples together.
- CallStackProfile::Sample* current_sample_proto = nullptr;
- uint32_t milestones = 0;
- for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
- // Check if the sample is different than the previous one. Samples match
+ const bool preserve_order =
+ (ordering_spec == CallStackProfileParams::PRESERVE_ORDER);
+
+ std::map<StackSamplingProfiler::Sample, int> sample_index;
+ uint32_t milestones = 0;
+ for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
+ int existing_sample_index = -1;
+ if (preserve_order) {
+ // Collapse sample with the previous one if they match. Samples match
// if the frame and all annotations are the same.
- if (!current_sample_proto || *it != *(it - 1)) {
- current_sample_proto = proto_profile->add_sample();
- CopySampleToProto(*it, profile.modules, current_sample_proto);
- current_sample_proto->set_count(1);
- CopyAnnotationsToProto(it->process_milestones & ~milestones,
- current_sample_proto);
- milestones = it->process_milestones;
- } else {
- current_sample_proto->set_count(current_sample_proto->count() + 1);
- }
- }
- } else {
- // Collapse all repeated samples together.
- std::map<StackSamplingProfiler::Sample, int> sample_index;
- uint32_t milestones = 0;
- for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
- // Check for a sample already seen. Samples match if the frame and all
- // annotations are the same.
+ if (proto_profile->sample_size() > 0 && *it == *(it - 1))
+ existing_sample_index = proto_profile->sample_size() - 1;
+ } else {
auto location = sample_index.find(*it);
- if (location == sample_index.end()) {
- CallStackProfile::Sample* sample_proto = proto_profile->add_sample();
- CopySampleToProto(*it, profile.modules, sample_proto);
- sample_proto->set_count(1);
- CopyAnnotationsToProto(it->process_milestones & ~milestones,
- sample_proto);
- sample_index.insert(
- std::make_pair(
- *it, static_cast<int>(proto_profile->sample().size()) - 1));
- milestones = it->process_milestones;
- } else {
- CallStackProfile::Sample* sample_proto =
- proto_profile->mutable_sample()->Mutable(location->second);
- sample_proto->set_count(sample_proto->count() + 1);
- }
+ if (location != sample_index.end())
+ existing_sample_index = location->second;
+ }
+
+ if (existing_sample_index != -1) {
+ CallStackProfile::Sample* sample_proto =
+ proto_profile->mutable_sample()->Mutable(existing_sample_index);
+ sample_proto->set_count(sample_proto->count() + 1);
+ continue;
+ }
+
+ CallStackProfile::Sample* sample_proto = proto_profile->add_sample();
+ CopySampleToProto(*it, profile.modules, sample_proto);
+ sample_proto->set_count(1);
+ CopyAnnotationsToProto(it->process_milestones & ~milestones, sample_proto);
+ milestones = it->process_milestones;
+
+ if (!preserve_order) {
+ sample_index.insert(std::make_pair(
+ *it, static_cast<int>(proto_profile->sample_size()) - 1));
}
}
@@ -412,6 +440,8 @@ SampledProfile::TriggerEvent ToSampledProfileTriggerEvent(
return SampledProfile::JANKY_TASK;
case CallStackProfileParams::THREAD_HUNG:
return SampledProfile::THREAD_HUNG;
+ case CallStackProfileParams::PERIODIC_COLLECTION:
+ return SampledProfile::PERIODIC_COLLECTION;
}
NOTREACHED();
return SampledProfile::UNKNOWN_TRIGGER_EVENT;
@@ -419,39 +449,68 @@ SampledProfile::TriggerEvent ToSampledProfileTriggerEvent(
} // namespace
-// CallStackProfileMetricsProvider --------------------------------------------
-
-const char CallStackProfileMetricsProvider::kFieldTrialName[] =
- "StackProfiling";
-const char CallStackProfileMetricsProvider::kReportProfilesGroupName[] =
- "Report profiles";
+namespace internal {
-CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {
-}
-
-CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
+base::TimeDelta GetUptime() {
+ static base::Time process_creation_time;
+// base::CurrentProcessInfo::CreationTime() is only defined on some platforms.
+#if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) || \
+ defined(OS_LINUX)
+ if (process_creation_time.is_null())
+ process_creation_time = base::CurrentProcessInfo::CreationTime();
+#else
+ NOTREACHED();
+#endif
+ DCHECK(!process_creation_time.is_null());
+ return base::Time::Now() - process_creation_time;
}
-// This function can be invoked on an abitrary thread.
-base::StackSamplingProfiler::CompletedCallback
-CallStackProfileMetricsProvider::GetProfilerCallback(
- const CallStackProfileParams& params) {
+StackSamplingProfiler::CompletedCallback GetProfilerCallback(
+ CallStackProfileParams* params) {
// Ignore the profiles 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.
if (!PendingProfiles::GetInstance()->IsCollectionEnabled())
return base::Bind(&IgnoreCompletedProfiles);
- return base::Bind(&ReceiveCompletedProfilesImpl, params,
- base::TimeTicks::Now());
+ params->start_timestamp = base::TimeTicks::Now();
+ return base::Bind(&ReceiveCompletedProfilesImpl, params);
+}
+
+} // namespace internal
+
+// CallStackProfileMetricsProvider --------------------------------------------
+
+const base::Feature CallStackProfileMetricsProvider::kEnableReporting = {
+ "SamplingProfilerReporting", base::FEATURE_DISABLED_BY_DEFAULT};
+
+CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {
+}
+
+CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
+}
+
+StackSamplingProfiler::CompletedCallback
+CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcessStartup() {
+ return internal::GetProfilerCallback(&g_browser_process_sampling_params);
}
// static
void CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
- const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
+ CallStackProfileParams* params,
base::StackSamplingProfiler::CallStackProfiles profiles) {
- ReceiveCompletedProfilesImpl(params, start_timestamp, std::move(profiles));
+ ReceiveCompletedProfilesImpl(params, std::move(profiles));
+}
+
+// static
+bool CallStackProfileMetricsProvider::IsPeriodicSamplingEnabled() {
+ // Ensure FeatureList has been initialized before calling into an API that
+ // calls base::FeatureList::IsEnabled() internally. While extremely unlikely,
+ // it is possible that the profiler callback and therefore this function get
+ // called before FeatureList initialization (e.g. if machine was suspended).
+ return base::FeatureList::GetInstance() != nullptr &&
+ base::GetFieldTrialParamByFeatureAsBool(kEnableReporting, "periodic",
+ false);
}
void CallStackProfileMetricsProvider::OnRecordingEnabled() {
@@ -469,6 +528,9 @@ void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
DCHECK(IsReportingEnabledByFieldTrial() || pending_profiles.empty());
+ // TODO(asvitkine): For post-startup periodic samples, this is currently
+ // wasteful as each sample is reported in its own profile. We should attempt
+ // to merge profiles to save bandwidth.
for (const ProfilesState& profiles_state : pending_profiles) {
for (const StackSamplingProfiler::CallStackProfile& profile :
profiles_state.profiles) {
@@ -492,10 +554,7 @@ void CallStackProfileMetricsProvider::ResetStaticStateForTesting() {
// static
bool CallStackProfileMetricsProvider::IsReportingEnabledByFieldTrial() {
- const std::string group_name = base::FieldTrialList::FindFullName(
- CallStackProfileMetricsProvider::kFieldTrialName);
- return group_name ==
- CallStackProfileMetricsProvider::kReportProfilesGroupName;
+ return base::FeatureList::IsEnabled(kEnableReporting);
}
} // namespace metrics
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.h b/chromium/components/metrics/call_stack_profile_metrics_provider.h
index 5c4f0db7f94..d5e4f7e46f0 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider.h
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider.h
@@ -7,6 +7,7 @@
#include <vector>
+#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/profiler/stack_sampling_profiler.h"
@@ -14,8 +15,24 @@
#include "components/metrics/metrics_provider.h"
namespace metrics {
+
class ChromeUserMetricsExtension;
+// Internal to expose functions for testing.
+namespace internal {
+
+// Returns the process uptime as a TimeDelta.
+base::TimeDelta GetUptime();
+
+// 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(
+ CallStackProfileParams* params);
+
+} // namespace internal
+
// Performs metrics logging for the stack sampling profiler.
class CallStackProfileMetricsProvider : public MetricsProvider {
public:
@@ -36,32 +53,31 @@ class CallStackProfileMetricsProvider : public MetricsProvider {
CallStackProfileMetricsProvider();
~CallStackProfileMetricsProvider() override;
- // 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.
- static base::StackSamplingProfiler::CompletedCallback GetProfilerCallback(
- const CallStackProfileParams& params);
+ // Returns a callback for use with StackSamplingProfiler that sets up
+ // parameters for browser process startup sampling. The callback should be
+ // immediately passed to the StackSamplingProfiler, and should not be reused.
+ static base::StackSamplingProfiler::CompletedCallback
+ GetProfilerCallbackForBrowserProcessStartup();
// Provides completed stack profiles to the metrics provider. Intended for use
// when receiving profiles over IPC. In-process StackSamplingProfiler users
- // should use GetProfilerCallback() instead. |profiles| is not const& because
- // it must be passed with std::move.
+ // should instead use a variant of GetProfilerCallback*(). |profiles| is not
+ // const& because it must be passed with std::move.
static void ReceiveCompletedProfiles(
- const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
+ CallStackProfileParams* params,
base::StackSamplingProfiler::CallStackProfiles profiles);
+ // Whether periodic sampling is enabled via a trial.
+ static bool IsPeriodicSamplingEnabled();
+
// MetricsProvider:
void OnRecordingEnabled() override;
void OnRecordingDisabled() override;
void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override;
protected:
- // Finch field trial and group for reporting profiles. Provided here for test
- // use.
- static const char kFieldTrialName[];
- static const char kReportProfilesGroupName[];
+ // base::Feature for reporting profiles. Provided here for test use.
+ static const base::Feature kEnableReporting;
// Reset the static state to the defaults after startup.
static void ResetStaticStateForTesting();
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 c81e23922ac..11f2ddcbfa0 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
@@ -10,10 +10,10 @@
#include <utility>
#include "base/macros.h"
-#include "base/metrics/field_trial.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "components/metrics/call_stack_profile_params.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
@@ -58,8 +58,8 @@ struct ExpectedProtoProfile {
class ProfilesFactory {
public:
- ProfilesFactory(){};
- ~ProfilesFactory(){};
+ ProfilesFactory() {}
+ ~ProfilesFactory() {}
ProfilesFactory& AddMilestone(int milestone);
ProfilesFactory& NewProfile(int duration_ms, int interval_ms);
@@ -117,39 +117,34 @@ Profiles ProfilesFactory::Build() {
namespace metrics {
-// This test fixture enables the field trial that
+// This test fixture enables the feature that
// CallStackProfileMetricsProvider depends on to report profiles.
class CallStackProfileMetricsProviderTest : public testing::Test {
public:
- CallStackProfileMetricsProviderTest()
- : field_trial_list_(nullptr) {
- base::FieldTrialList::CreateFieldTrial(
- TestState::kFieldTrialName,
- TestState::kReportProfilesGroupName);
+ CallStackProfileMetricsProviderTest() {
+ scoped_feature_list_.InitAndEnableFeature(TestState::kEnableReporting);
TestState::ResetStaticStateForTesting();
}
~CallStackProfileMetricsProviderTest() override {}
// Utility function to append profiles to the metrics provider.
- void AppendProfiles(const CallStackProfileParams& params, Profiles profiles) {
- CallStackProfileMetricsProvider::GetProfilerCallback(params).Run(
- std::move(profiles));
+ void AppendProfiles(CallStackProfileParams* params, Profiles profiles) {
+ internal::GetProfilerCallback(params).Run(std::move(profiles));
}
void VerifyProfileProto(const ExpectedProtoProfile& expected,
const SampledProfile& proto);
private:
- // Exposes field trial/group names from the CallStackProfileMetricsProvider.
+ // Exposes the feature from the CallStackProfileMetricsProvider.
class TestState : public CallStackProfileMetricsProvider {
public:
- using CallStackProfileMetricsProvider::kFieldTrialName;
- using CallStackProfileMetricsProvider::kReportProfilesGroupName;
+ using CallStackProfileMetricsProvider::kEnableReporting;
using CallStackProfileMetricsProvider::ResetStaticStateForTesting;
};
- base::FieldTrialList field_trial_list_;
+ base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(CallStackProfileMetricsProviderTest);
};
@@ -343,12 +338,11 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
- AppendProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE),
- std::move(profiles));
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
+ AppendProfiles(&params, std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -428,12 +422,11 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
- AppendProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE),
- std::move(profiles));
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
+ AppendProfiles(&params, std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -515,11 +508,11 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
- AppendProfiles(CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::PRESERVE_ORDER),
- std::move(profiles));
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::PRESERVE_ORDER);
+ AppendProfiles(&params, std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -559,12 +552,11 @@ TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
- AppendProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE),
- std::move(profiles));
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
+ AppendProfiles(&params, std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -593,12 +585,11 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
- AppendProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE),
- std::move(profiles));
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
+ AppendProfiles(&params, std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -624,12 +615,11 @@ TEST_F(CallStackProfileMetricsProviderTest,
ASSERT_EQ(1U, profiles.size());
- AppendProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE),
- std::move(profiles));
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
+ AppendProfiles(&params, std::move(profiles));
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
@@ -652,12 +642,11 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingDisabled();
- AppendProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE),
- std::move(profiles));
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
+ AppendProfiles(&params, std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -670,12 +659,12 @@ TEST_F(CallStackProfileMetricsProviderTest,
ProfilesNotProvidedAfterChangeToDisabled) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
base::StackSamplingProfiler::CompletedCallback callback =
- CallStackProfileMetricsProvider::GetProfilerCallback(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE));
+ internal::GetProfilerCallback(&params);
provider.OnRecordingDisabled();
Profiles profiles = ProfilesFactory()
@@ -696,12 +685,12 @@ TEST_F(CallStackProfileMetricsProviderTest,
ProfilesNotProvidedAfterChangeToDisabledThenEnabled) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
base::StackSamplingProfiler::CompletedCallback callback =
- CallStackProfileMetricsProvider::GetProfilerCallback(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE));
+ internal::GetProfilerCallback(&params);
provider.OnRecordingDisabled();
provider.OnRecordingEnabled();
@@ -723,12 +712,12 @@ TEST_F(CallStackProfileMetricsProviderTest,
ProfilesNotProvidedAfterChangeFromDisabled) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingDisabled();
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PROCESS_STARTUP,
+ CallStackProfileParams::MAY_SHUFFLE);
base::StackSamplingProfiler::CompletedCallback callback =
- CallStackProfileMetricsProvider::GetProfilerCallback(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::UI_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE));
+ internal::GetProfilerCallback(&params);
provider.OnRecordingEnabled();
Profiles profiles = ProfilesFactory()
@@ -743,4 +732,109 @@ TEST_F(CallStackProfileMetricsProviderTest,
EXPECT_EQ(0, uma_proto.sampled_profile_size());
}
+// Only certain platforms support GetUptime() which is used both by the test
+// and the code being tested.
+#if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) || \
+ defined(OS_LINUX)
+#define MAYBE_PeriodicProfiles PeriodicProfiles
+#else
+#define MAYBE_PeriodicProfiles DISABLED_PeriodicProfiles
+#endif
+TEST_F(CallStackProfileMetricsProviderTest, MAYBE_PeriodicProfiles) {
+ const uintptr_t module_base_address = 0x1000;
+ const char* module_name = "ABCD";
+
+#if defined(OS_WIN)
+ uint64_t module_md5 = 0x46C3E4166659AC02ULL;
+ base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
+#else
+ uint64_t module_md5 = 0x554838A8451AC36CULL;
+ 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();
+
+ const ExpectedProtoModule expected_proto_modules[] = {
+ {module_name, module_md5, module_base_address},
+ };
+
+ const ExpectedProtoEntry expected_proto_entries[] = {
+ {0, 0x10}, {0, 0x20},
+ };
+ const ExpectedProtoSample expected_proto_samples[] = {
+ {1, &expected_proto_entries[0], 1, 3},
+ {0, &expected_proto_entries[1], 1, 1},
+ {2, &expected_proto_entries[0], 1, 3},
+ {0, &expected_proto_entries[1], 1, 1},
+ };
+
+ ExpectedProtoProfile expected_proto_profiles[] = {
+ {
+ 0, // Will be updated below.
+ 1000, // Based on kPeriodicSamplingInterval in the .cc.
+ expected_proto_modules, arraysize(expected_proto_modules),
+ expected_proto_samples, arraysize(expected_proto_samples),
+ },
+ };
+
+ ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
+
+ CallStackProfileMetricsProvider provider;
+ provider.OnRecordingEnabled();
+ CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::UI_THREAD,
+ CallStackProfileParams::PERIODIC_COLLECTION,
+ CallStackProfileParams::MAY_SHUFFLE);
+ const base::TimeDelta min_expected_uptime = internal::GetUptime();
+ AppendProfiles(&params, std::move(profiles));
+ const base::TimeDelta max_expected_uptime = internal::GetUptime();
+
+ ChromeUserMetricsExtension uma_proto;
+ provider.ProvideGeneralMetrics(&uma_proto);
+
+ // We expect duration_ms to be the process uptime. Check that it's within the
+ // min/max boundary values that were retrieved earlier. Then, set the value
+ // in |expected_proto_profiles| to be the actual value so it matches below.
+ const int32_t profile_duration_ms =
+ uma_proto.sampled_profile(0).call_stack_profile().profile_duration_ms();
+ EXPECT_GE(profile_duration_ms, min_expected_uptime.InMilliseconds());
+ EXPECT_LE(profile_duration_ms, max_expected_uptime.InMilliseconds());
+ expected_proto_profiles[0].duration_ms = profile_duration_ms;
+
+ 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::SizeTToString(p));
+ VerifyProfileProto(expected_proto_profiles[p],
+ uma_proto.sampled_profile().Get(p));
+ EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION,
+ uma_proto.sampled_profile(p).trigger_event());
+ }
+}
+
} // namespace metrics
diff --git a/chromium/components/metrics/call_stack_profile_params.cc b/chromium/components/metrics/call_stack_profile_params.cc
deleted file mode 100644
index e937283616c..00000000000
--- a/chromium/components/metrics/call_stack_profile_params.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/metrics/call_stack_profile_params.h"
-
-namespace metrics {
-
-CallStackProfileParams::CallStackProfileParams()
- : CallStackProfileParams(UNKNOWN_PROCESS, UNKNOWN_THREAD, UNKNOWN) {}
-
-CallStackProfileParams::CallStackProfileParams(Process process, Thread thread,
- Trigger trigger)
- : CallStackProfileParams(process, thread, trigger, MAY_SHUFFLE) {}
-
-CallStackProfileParams::CallStackProfileParams(Process process, Thread thread,
- Trigger trigger,
- SampleOrderingSpec ordering_spec)
- : process(process), thread(thread), trigger(trigger),
- ordering_spec(ordering_spec) {}
-
-} // namespace metrics
diff --git a/chromium/components/metrics/call_stack_profile_params.h b/chromium/components/metrics/call_stack_profile_params.h
index 4dd7cd4fef2..fad211616dc 100644
--- a/chromium/components/metrics/call_stack_profile_params.h
+++ b/chromium/components/metrics/call_stack_profile_params.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_METRICS_CALL_STACK_PROFILE_PARAMS_H_
#define COMPONENTS_METRICS_CALL_STACK_PROFILE_PARAMS_H_
+#include "base/time/time.h"
+
namespace metrics {
// Parameters to pass back to the metrics provider.
@@ -49,7 +51,8 @@ struct CallStackProfileParams {
PROCESS_STARTUP,
JANKY_TASK,
THREAD_HUNG,
- TRIGGER_LAST = THREAD_HUNG
+ PERIODIC_COLLECTION,
+ TRIGGER_LAST = PERIODIC_COLLECTION
};
// Allows the caller to specify whether sample ordering is
@@ -64,10 +67,20 @@ struct CallStackProfileParams {
// The default constructor is required for mojo and should not be used
// otherwise. A valid trigger should always be specified.
- CallStackProfileParams();
- CallStackProfileParams(Process process, Thread thread, Trigger trigger);
- CallStackProfileParams(Process process, Thread thread, Trigger trigger,
- SampleOrderingSpec ordering_spec);
+ constexpr CallStackProfileParams()
+ : CallStackProfileParams(UNKNOWN_PROCESS, UNKNOWN_THREAD, UNKNOWN) {}
+ constexpr CallStackProfileParams(Process process,
+ Thread thread,
+ Trigger trigger)
+ : CallStackProfileParams(process, thread, trigger, MAY_SHUFFLE) {}
+ constexpr CallStackProfileParams(Process process,
+ Thread thread,
+ Trigger trigger,
+ SampleOrderingSpec ordering_spec)
+ : process(process),
+ thread(thread),
+ trigger(trigger),
+ ordering_spec(ordering_spec) {}
// The collection process.
Process process;
@@ -80,6 +93,12 @@ struct CallStackProfileParams {
// Whether to preserve sample ordering.
SampleOrderingSpec ordering_spec;
+
+ // The time at which the CallStackProfileMetricsProvider became aware of the
+ // request for profiling. In particular, this is when callback was requested
+ // via CallStackProfileMetricsProvider::GetProfilerCallback(). Used to
+ // determine if collection was disabled during the collection of the profile.
+ base::TimeTicks start_timestamp;
};
} // namespace metrics
diff --git a/chromium/components/metrics/child_call_stack_profile_collector.cc b/chromium/components/metrics/child_call_stack_profile_collector.cc
index 52b93b2af09..09dc6c83eeb 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector.cc
+++ b/chromium/components/metrics/child_call_stack_profile_collector.cc
@@ -65,7 +65,19 @@ void ChildCallStackProfileCollector::SetParentProfileCollector(
profiles_.clear();
}
-void ChildCallStackProfileCollector::Collect(
+base::Optional<base::StackSamplingProfiler::SamplingParams>
+ChildCallStackProfileCollector::Collect(
+ const CallStackProfileParams& params,
+ base::TimeTicks start_timestamp,
+ std::vector<CallStackProfile> profiles) {
+ // 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));
+ // Empty return value indicates that collection should not be re-started.
+ return base::Optional<base::StackSamplingProfiler::SamplingParams>();
+}
+
+void ChildCallStackProfileCollector::CollectImpl(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
std::vector<CallStackProfile> profiles) {
@@ -76,13 +88,11 @@ void ChildCallStackProfileCollector::Collect(
(!base::ThreadTaskRunnerHandle::IsSet() ||
base::ThreadTaskRunnerHandle::Get() != task_runner_)) {
// Post back to the thread that owns the the parent interface.
- task_runner_->PostTask(FROM_HERE, base::Bind(
- &ChildCallStackProfileCollector::Collect,
- // This class has lazy instance lifetime.
- base::Unretained(this),
- params,
- start_timestamp,
- base::Passed(std::move(profiles))));
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChildCallStackProfileCollector::CollectImpl,
+ // This class has lazy instance lifetime.
+ base::Unretained(this), params, start_timestamp,
+ base::Passed(std::move(profiles))));
return;
}
diff --git a/chromium/components/metrics/child_call_stack_profile_collector.h b/chromium/components/metrics/child_call_stack_profile_collector.h
index 7eb3abae814..5cf08794fb6 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector.h
+++ b/chromium/components/metrics/child_call_stack_profile_collector.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_METRICS_CHILD_CALL_STACK_PROFILE_COLLECTOR_H_
#define COMPONENTS_METRICS_CHILD_CALL_STACK_PROFILE_COLLECTOR_H_
+#include <vector>
+
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
@@ -90,9 +92,14 @@ class ChildCallStackProfileCollector {
using CallStackProfile = base::StackSamplingProfiler::CallStackProfile;
- void Collect(const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles);
+ base::Optional<base::StackSamplingProfiler::SamplingParams> Collect(
+ const CallStackProfileParams& params,
+ base::TimeTicks start_timestamp,
+ std::vector<CallStackProfile> profiles);
+
+ void CollectImpl(const CallStackProfileParams& params,
+ base::TimeTicks start_timestamp,
+ std::vector<CallStackProfile> profiles);
// 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
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 9a3a67af92d..a00336c2060 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
+++ b/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
@@ -30,7 +30,7 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
public:
using CallStackProfile = base::StackSamplingProfiler::CallStackProfile;
- Receiver(mojom::CallStackProfileCollectorRequest request)
+ explicit Receiver(mojom::CallStackProfileCollectorRequest request)
: binding_(this, std::move(request)) {}
~Receiver() override {}
@@ -73,6 +73,7 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
std::unique_ptr<Receiver> receiver_impl_;
ChildCallStackProfileCollector child_collector_;
+ private:
DISALLOW_COPY_AND_ASSIGN(ChildCallStackProfileCollectorTest);
};
diff --git a/chromium/components/metrics/cloned_install_detector.cc b/chromium/components/metrics/cloned_install_detector.cc
index 7e9be8c3f10..d9e752d1f76 100644
--- a/chromium/components/metrics/cloned_install_detector.cc
+++ b/chromium/components/metrics/cloned_install_detector.cc
@@ -14,6 +14,7 @@
#include "base/metrics/metrics_hashes.h"
#include "base/single_thread_task_runner.h"
#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "components/metrics/machine_id_provider.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
@@ -47,23 +48,19 @@ void LogMachineIdState(MachineIdState state) {
} // namespace
-ClonedInstallDetector::ClonedInstallDetector(MachineIdProvider* raw_id_provider)
- : raw_id_provider_(raw_id_provider), weak_ptr_factory_(this) {
-}
+ClonedInstallDetector::ClonedInstallDetector() : weak_ptr_factory_(this) {}
ClonedInstallDetector::~ClonedInstallDetector() {
}
-void ClonedInstallDetector::CheckForClonedInstall(
- PrefService* local_state,
- scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
- base::PostTaskAndReplyWithResult(
- task_runner.get(),
+void ClonedInstallDetector::CheckForClonedInstall(PrefService* local_state) {
+ base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
- base::Bind(&MachineIdProvider::GetMachineId, raw_id_provider_),
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+ base::Bind(&MachineIdProvider::GetMachineId),
base::Bind(&ClonedInstallDetector::SaveMachineId,
- weak_ptr_factory_.GetWeakPtr(),
- local_state));
+ weak_ptr_factory_.GetWeakPtr(), local_state));
}
void ClonedInstallDetector::SaveMachineId(PrefService* local_state,
diff --git a/chromium/components/metrics/cloned_install_detector.h b/chromium/components/metrics/cloned_install_detector.h
index 0609122cdaf..6a728ae8a80 100644
--- a/chromium/components/metrics/cloned_install_detector.h
+++ b/chromium/components/metrics/cloned_install_detector.h
@@ -7,25 +7,18 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
class PrefRegistrySimple;
class PrefService;
-namespace base {
-class SingleThreadTaskRunner;
-}
-
namespace metrics {
-class MachineIdProvider;
-
// A class for detecting if an install is cloned. It does this by detecting
// when the hardware running Chrome changes.
class ClonedInstallDetector {
public:
- explicit ClonedInstallDetector(MachineIdProvider* raw_id_provider);
+ ClonedInstallDetector();
virtual ~ClonedInstallDetector();
// Posts a task to |task_runner| to generate a machine ID and store it to a
@@ -34,9 +27,7 @@ class ClonedInstallDetector {
// 24-bit value based off of machine characteristics. This value should never
// be sent over the network.
// TODO(jwd): Implement change detection.
- void CheckForClonedInstall(
- PrefService* local_state,
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ void CheckForClonedInstall(PrefService* local_state);
static void RegisterPrefs(PrefRegistrySimple* registry);
@@ -49,7 +40,6 @@ class ClonedInstallDetector {
// task.
void SaveMachineId(PrefService* local_state, const std::string& raw_id);
- scoped_refptr<MachineIdProvider> raw_id_provider_;
base::WeakPtrFactory<ClonedInstallDetector> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ClonedInstallDetector);
diff --git a/chromium/components/metrics/cloned_install_detector_unittest.cc b/chromium/components/metrics/cloned_install_detector_unittest.cc
index 92625996ac5..2e2ebd74176 100644
--- a/chromium/components/metrics/cloned_install_detector_unittest.cc
+++ b/chromium/components/metrics/cloned_install_detector_unittest.cc
@@ -27,10 +27,8 @@ TEST(ClonedInstallDetectorTest, SaveId) {
TestingPrefServiceSimple prefs;
ClonedInstallDetector::RegisterPrefs(prefs.registry());
- std::unique_ptr<ClonedInstallDetector> detector(
- new ClonedInstallDetector(MachineIdProvider::CreateInstance()));
-
- detector->SaveMachineId(&prefs, kTestRawId);
+ ClonedInstallDetector detector;
+ detector.SaveMachineId(&prefs, kTestRawId);
EXPECT_EQ(kTestHashedId, prefs.GetInteger(prefs::kMetricsMachineId));
}
@@ -42,10 +40,8 @@ TEST(ClonedInstallDetectorTest, DetectClone) {
// Save a machine id that will cause a clone to be detected.
prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
- std::unique_ptr<ClonedInstallDetector> detector(
- new ClonedInstallDetector(MachineIdProvider::CreateInstance()));
-
- detector->SaveMachineId(&prefs, kTestRawId);
+ ClonedInstallDetector detector;
+ detector.SaveMachineId(&prefs, kTestRawId);
EXPECT_TRUE(prefs.GetBoolean(prefs::kMetricsResetIds));
}
diff --git a/chromium/components/metrics/drive_metrics_provider.cc b/chromium/components/metrics/drive_metrics_provider.cc
index 22ad0891919..b067337a963 100644
--- a/chromium/components/metrics/drive_metrics_provider.cc
+++ b/chromium/components/metrics/drive_metrics_provider.cc
@@ -12,17 +12,15 @@
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
-#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
namespace metrics {
-DriveMetricsProvider::DriveMetricsProvider(
- scoped_refptr<base::SequencedTaskRunner> file_thread,
- int local_state_path_key)
- : file_thread_(file_thread),
- local_state_path_key_(local_state_path_key),
- weak_ptr_factory_(this) {}
+DriveMetricsProvider::DriveMetricsProvider(int local_state_path_key)
+ : local_state_path_key_(local_state_path_key), weak_ptr_factory_(this) {}
DriveMetricsProvider::~DriveMetricsProvider() {}
@@ -35,9 +33,11 @@ void DriveMetricsProvider::ProvideSystemProfileMetrics(
}
void DriveMetricsProvider::GetDriveMetrics(const base::Closure& done_callback) {
- base::PostTaskAndReplyWithResult(
- file_thread_.get(), FROM_HERE,
- base::Bind(&DriveMetricsProvider::GetDriveMetricsOnFileThread,
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE,
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::Bind(&DriveMetricsProvider::GetDriveMetricsOnBackgroundThread,
local_state_path_key_),
base::Bind(&DriveMetricsProvider::GotDriveMetrics,
weak_ptr_factory_.GetWeakPtr(), done_callback));
@@ -48,7 +48,10 @@ DriveMetricsProvider::SeekPenaltyResponse::SeekPenaltyResponse()
// static
DriveMetricsProvider::DriveMetrics
-DriveMetricsProvider::GetDriveMetricsOnFileThread(int local_state_path_key) {
+DriveMetricsProvider::GetDriveMetricsOnBackgroundThread(
+ int local_state_path_key) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
DriveMetricsProvider::DriveMetrics metrics;
QuerySeekPenalty(base::FILE_EXE, &metrics.app_drive);
QuerySeekPenalty(local_state_path_key, &metrics.user_data_drive);
@@ -82,7 +85,7 @@ void DriveMetricsProvider::QuerySeekPenalty(
void DriveMetricsProvider::GotDriveMetrics(
const base::Closure& done_callback,
const DriveMetricsProvider::DriveMetrics& metrics) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
metrics_ = metrics;
done_callback.Run();
}
diff --git a/chromium/components/metrics/drive_metrics_provider.h b/chromium/components/metrics/drive_metrics_provider.h
index cab799d40ea..280b0ae7eac 100644
--- a/chromium/components/metrics/drive_metrics_provider.h
+++ b/chromium/components/metrics/drive_metrics_provider.h
@@ -10,8 +10,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/sequenced_task_runner.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
#include "components/metrics/metrics_provider.h"
#include "components/metrics/proto/system_profile.pb.h"
@@ -23,13 +22,9 @@ namespace metrics {
// Provides metrics about the local drives on a user's computer. Currently only
// checks to see if they incur a seek-time penalty (e.g. if they're SSDs).
-//
-// Defers gathering metrics until after "rush hour" (startup) so as to not bog
-// down the file thread.
class DriveMetricsProvider : public metrics::MetricsProvider {
public:
- DriveMetricsProvider(scoped_refptr<base::SequencedTaskRunner> file_thread,
- int local_state_path_key);
+ explicit DriveMetricsProvider(int local_state_path_key);
~DriveMetricsProvider() override;
// metrics::MetricsDataProvider:
@@ -60,15 +55,16 @@ class DriveMetricsProvider : public metrics::MetricsProvider {
static bool HasSeekPenalty(const base::FilePath& path,
bool* has_seek_penalty);
- // Gather metrics about various drives on |file_thread_|.
- static DriveMetrics GetDriveMetricsOnFileThread(int local_state_path_key);
+ // Gather metrics about various drives. Should be run on a background thread.
+ static DriveMetrics GetDriveMetricsOnBackgroundThread(
+ int local_state_path_key);
// Tries to determine whether there is a penalty for seeking on the drive that
// hosts |path_service_key| (for example: the drive that holds "Local State").
static void QuerySeekPenalty(int path_service_key,
SeekPenaltyResponse* response);
- // Called when metrics are done being gathered from the FILE thread.
+ // Called when metrics are done being gathered asynchronously.
// |done_callback| is the callback that should be called once all metrics are
// gathered.
void GotDriveMetrics(const base::Closure& done_callback,
@@ -78,10 +74,6 @@ class DriveMetricsProvider : public metrics::MetricsProvider {
void FillDriveMetrics(const SeekPenaltyResponse& response,
metrics::SystemProfileProto::Hardware::Drive* drive);
- // The thread on which file operations are performed (supplied by the
- // embedder).
- scoped_refptr<base::SequencedTaskRunner> file_thread_;
-
// The key to give to base::PathService to obtain the path to local state
// (supplied by the embedder).
int local_state_path_key_;
@@ -89,7 +81,7 @@ class DriveMetricsProvider : public metrics::MetricsProvider {
// Information gathered about various important drives.
DriveMetrics metrics_;
- base::ThreadChecker thread_checker_;
+ SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<DriveMetricsProvider> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DriveMetricsProvider);
diff --git a/chromium/components/metrics/drive_metrics_provider_win.cc b/chromium/components/metrics/drive_metrics_provider_win.cc
index 360b80e690a..04647a9d41b 100644
--- a/chromium/components/metrics/drive_metrics_provider_win.cc
+++ b/chromium/components/metrics/drive_metrics_provider_win.cc
@@ -11,18 +11,12 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
-#include "base/win/windows_version.h"
namespace metrics {
// static
bool DriveMetricsProvider::HasSeekPenalty(const base::FilePath& path,
bool* has_seek_penalty) {
- if (base::win::GetVersion() < base::win::VERSION_WIN7) {
- // TODO(dbeam): re-enable XP and Vista detection in a utility process.
- return false;
- }
-
std::vector<base::FilePath::StringType> components;
path.GetComponents(&components);
diff --git a/chromium/components/metrics/field_trials_provider.cc b/chromium/components/metrics/field_trials_provider.cc
new file mode 100644
index 00000000000..25287d563d8
--- /dev/null
+++ b/chromium/components/metrics/field_trials_provider.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/metrics/field_trials_provider.h"
+
+#include "base/strings/string_piece.h"
+#include "components/metrics/proto/system_profile.pb.h"
+#include "components/variations/active_field_trials.h"
+#include "components/variations/synthetic_trial_registry.h"
+
+namespace variations {
+
+namespace {
+
+void WriteFieldTrials(const std::vector<ActiveGroupId>& field_trial_ids,
+ metrics::SystemProfileProto* system_profile) {
+ for (const ActiveGroupId& id : field_trial_ids) {
+ metrics::SystemProfileProto::FieldTrial* field_trial =
+ system_profile->add_field_trial();
+ field_trial->set_name_id(id.name);
+ field_trial->set_group_id(id.group);
+ }
+}
+
+} // namespace
+
+FieldTrialsProvider::FieldTrialsProvider(SyntheticTrialRegistry* registry,
+ base::StringPiece suffix)
+ : registry_(registry), suffix_(suffix) {}
+FieldTrialsProvider::~FieldTrialsProvider() = default;
+
+void FieldTrialsProvider::GetFieldTrialIds(
+ std::vector<ActiveGroupId>* field_trial_ids) const {
+ // We use the default field trial suffixing (no suffix).
+ variations::GetFieldTrialActiveGroupIds(suffix_, field_trial_ids);
+}
+
+void FieldTrialsProvider::OnDidCreateMetricsLog() {
+ if (registry_) {
+ creation_times_.push_back(base::TimeTicks::Now());
+ }
+}
+
+void FieldTrialsProvider::ProvideSystemProfileMetrics(
+ metrics::SystemProfileProto* system_profile_proto) {
+ std::vector<ActiveGroupId> field_trial_ids;
+ GetFieldTrialIds(&field_trial_ids);
+ WriteFieldTrials(field_trial_ids, system_profile_proto);
+
+ if (registry_) {
+ base::TimeTicks creation_time;
+ // Should always be true, but don't crash even if there is a bug.
+ if (!creation_times_.empty()) {
+ creation_time = creation_times_.back();
+ creation_times_.pop_back();
+ }
+ std::vector<ActiveGroupId> synthetic_trials;
+ registry_->GetSyntheticFieldTrialsOlderThan(creation_time,
+ &synthetic_trials);
+ WriteFieldTrials(synthetic_trials, system_profile_proto);
+ }
+}
+
+} // namespace variations
diff --git a/chromium/components/metrics/field_trials_provider.h b/chromium/components/metrics/field_trials_provider.h
new file mode 100644
index 00000000000..d93988edc68
--- /dev/null
+++ b/chromium/components/metrics/field_trials_provider.h
@@ -0,0 +1,55 @@
+// 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_METRICS_FIELD_TRIALS_PROVIDER_H_
+#define COMPONENTS_METRICS_FIELD_TRIALS_PROVIDER_H_
+
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "components/metrics/metrics_provider.h"
+
+// TODO(crbug/507665): Once MetricsProvider/SystemProfileProto are moved into
+// //services/metrics, then //components/variations can depend on them, and
+// this should be moved there.
+namespace variations {
+
+class SyntheticTrialRegistry;
+struct ActiveGroupId;
+
+class FieldTrialsProvider : public metrics::MetricsProvider {
+ public:
+ // |registry| must outlive this metrics provider.
+ FieldTrialsProvider(SyntheticTrialRegistry* registry,
+ base::StringPiece suffix);
+ ~FieldTrialsProvider() override;
+
+ // metrics::MetricsProvider:
+ void OnDidCreateMetricsLog() override;
+ void ProvideSystemProfileMetrics(
+ metrics::SystemProfileProto* system_profile_proto) override;
+
+ private:
+ // Overrideable for testing.
+ virtual void GetFieldTrialIds(
+ std::vector<ActiveGroupId>* field_trial_ids) const;
+
+ SyntheticTrialRegistry* registry_;
+
+ // Suffix used for the field trial names before they are hashed for uploads.
+ std::string suffix_;
+
+ // A stack of log creation times.
+ // While the initial metrics log exists, there will be two logs open.
+ // Use a stack so that we use the right creation time for the first ongoing
+ // log.
+ // TODO(crbug/746098): Simplify InitialMetricsLog logic so this is not
+ // necessary.
+ std::vector<base::TimeTicks> creation_times_;
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_METRICS_FIELD_TRIALS_PROVIDER_H_
diff --git a/chromium/components/metrics/field_trials_provider_unittest.cc b/chromium/components/metrics/field_trials_provider_unittest.cc
new file mode 100644
index 00000000000..c8b04fe6911
--- /dev/null
+++ b/chromium/components/metrics/field_trials_provider_unittest.cc
@@ -0,0 +1,103 @@
+// 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/metrics/field_trials_provider.h"
+
+#include "components/metrics/proto/system_profile.pb.h"
+#include "components/variations/active_field_trials.h"
+#include "components/variations/synthetic_trial_registry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace variations {
+
+namespace {
+
+const ActiveGroupId kFieldTrialIds[] = {{37, 43}, {13, 47}, {23, 17}};
+const ActiveGroupId kSyntheticTrials[] = {{55, 15}, {66, 16}};
+
+class TestProvider : public FieldTrialsProvider {
+ public:
+ TestProvider(SyntheticTrialRegistry* registry, base::StringPiece suffix)
+ : FieldTrialsProvider(registry, suffix) {}
+ ~TestProvider() override {}
+
+ void GetFieldTrialIds(
+ std::vector<ActiveGroupId>* field_trial_ids) const override {
+ ASSERT_TRUE(field_trial_ids->empty());
+ for (const ActiveGroupId& id : kFieldTrialIds) {
+ field_trial_ids->push_back(id);
+ }
+ }
+};
+
+// Check that the values in |system_values| correspond to the test data
+// defined at the top of this file.
+void CheckSystemProfile(const metrics::SystemProfileProto& system_profile) {
+ ASSERT_EQ(arraysize(kFieldTrialIds) + arraysize(kSyntheticTrials),
+ static_cast<size_t>(system_profile.field_trial_size()));
+ for (size_t i = 0; i < arraysize(kFieldTrialIds); ++i) {
+ const metrics::SystemProfileProto::FieldTrial& field_trial =
+ system_profile.field_trial(i);
+ EXPECT_EQ(kFieldTrialIds[i].name, field_trial.name_id());
+ EXPECT_EQ(kFieldTrialIds[i].group, field_trial.group_id());
+ }
+ // Verify the right data is present for the synthetic trials.
+ for (size_t i = 0; i < arraysize(kSyntheticTrials); ++i) {
+ const metrics::SystemProfileProto::FieldTrial& field_trial =
+ system_profile.field_trial(i + arraysize(kFieldTrialIds));
+ EXPECT_EQ(kSyntheticTrials[i].name, field_trial.name_id());
+ EXPECT_EQ(kSyntheticTrials[i].group, field_trial.group_id());
+ }
+}
+
+} // namespace
+
+class FieldTrialsProviderTest : public ::testing::Test {
+ public:
+ FieldTrialsProviderTest() {}
+ ~FieldTrialsProviderTest() override {}
+
+ protected:
+ // Register trials which should get recorded.
+ void RegisterExpectedSyntheticTrials() {
+ for (const ActiveGroupId& id : kSyntheticTrials) {
+ registry_.RegisterSyntheticFieldTrial(
+ SyntheticTrialGroup(id.name, id.group));
+ }
+ }
+ // Register trial which shouldn't get recorded.
+ void RegisterExtraSyntheticTrial() {
+ registry_.RegisterSyntheticFieldTrial(SyntheticTrialGroup(100, 1000));
+ }
+
+ // Waits until base::TimeTicks::Now() no longer equals |value|. This should
+ // take between 1-15ms per the documented resolution of base::TimeTicks.
+ void WaitUntilTimeChanges(const base::TimeTicks& value) {
+ while (base::TimeTicks::Now() == value) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
+ }
+ }
+
+ SyntheticTrialRegistry registry_;
+};
+
+TEST_F(FieldTrialsProviderTest, ProvideSyntheticTrials) {
+ TestProvider provider(&registry_, base::StringPiece());
+
+ RegisterExpectedSyntheticTrials();
+ // Make sure these trials are older than the log.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ provider.OnDidCreateMetricsLog();
+ // Make sure that the log is older than the trials that should be excluded.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ RegisterExtraSyntheticTrial();
+
+ metrics::SystemProfileProto proto;
+ provider.ProvideSystemProfileMetrics(&proto);
+ CheckSystemProfile(proto);
+}
+
+} // namespace variations
diff --git a/chromium/components/metrics/file_metrics_provider.cc b/chromium/components/metrics/file_metrics_provider.cc
index d9a6a042c23..d6165f238b5 100644
--- a/chromium/components/metrics/file_metrics_provider.cc
+++ b/chromium/components/metrics/file_metrics_provider.cc
@@ -17,9 +17,12 @@
#include "base/metrics/persistent_memory_allocator.h"
#include "base/strings/string_piece.h"
#include "base/task_runner.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "base/time/time.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_service.h"
+#include "components/metrics/persistent_system_profile.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -70,6 +73,19 @@ constexpr SourceOptions kSourceOptions[] = {
}
};
+enum EmbeddedProfileResult : int {
+ EMBEDDED_PROFILE_ATTEMPT,
+ EMBEDDED_PROFILE_FOUND,
+ EMBEDDED_PROFILE_FALLBACK,
+ EMBEDDED_PROFILE_DROPPED,
+ EMBEDDED_PROFILE_ACTION_MAX
+};
+
+void RecordEmbeddedProfileResult(EmbeddedProfileResult result) {
+ UMA_HISTOGRAM_ENUMERATION("UMA.FileMetricsProvider.EmbeddedProfileResult",
+ result, EMBEDDED_PROFILE_ACTION_MAX);
+}
+
void DeleteFileWhenPossible(const base::FilePath& path) {
// Open (with delete) and then immediately close the file by going out of
// scope. This is the only cross-platform safe way to delete a file that may
@@ -79,17 +95,35 @@ void DeleteFileWhenPossible(const base::FilePath& path) {
base::File::FLAG_DELETE_ON_CLOSE);
}
+// A task runner to use for testing.
+base::TaskRunner* g_task_runner_for_testing = nullptr;
+
+// Returns a task runner appropriate for running background tasks that perform
+// file I/O.
+scoped_refptr<base::TaskRunner> CreateBackgroundTaskRunner() {
+ if (g_task_runner_for_testing)
+ return scoped_refptr<base::TaskRunner>(g_task_runner_for_testing);
+
+ return base::CreateTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+}
+
} // namespace
// This structure stores all the information about the sources being monitored
// and their current reporting state.
struct FileMetricsProvider::SourceInfo {
- SourceInfo(SourceType source_type) : type(source_type) {}
+ SourceInfo(SourceType source_type, SourceAssociation source_association)
+ : type(source_type), association(source_association) {}
~SourceInfo() {}
// How to access this source (file/dir, atomic/active).
const SourceType type;
+ // With what run this source is associated.
+ const SourceAssociation association;
+
// Where on disk the directory is located. This will only be populated when
// a directory is being monitored.
base::FilePath directory;
@@ -115,10 +149,8 @@ struct FileMetricsProvider::SourceInfo {
DISALLOW_COPY_AND_ASSIGN(SourceInfo);
};
-FileMetricsProvider::FileMetricsProvider(
- const scoped_refptr<base::TaskRunner>& task_runner,
- PrefService* local_state)
- : task_runner_(task_runner),
+FileMetricsProvider::FileMetricsProvider(PrefService* local_state)
+ : task_runner_(CreateBackgroundTaskRunner()),
pref_service_(local_state),
weak_factory_(this) {
base::StatisticsRecorder::RegisterHistogramProvider(
@@ -131,12 +163,12 @@ void FileMetricsProvider::RegisterSource(const base::FilePath& path,
SourceType type,
SourceAssociation source_association,
const base::StringPiece prefs_key) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Ensure that kSourceOptions has been filled for this type.
DCHECK_GT(arraysize(kSourceOptions), static_cast<size_t>(type));
- std::unique_ptr<SourceInfo> source(new SourceInfo(type));
+ std::unique_ptr<SourceInfo> source(new SourceInfo(type, source_association));
source->prefs_key = prefs_key.as_string();
switch (source->type) {
@@ -161,9 +193,11 @@ void FileMetricsProvider::RegisterSource(const base::FilePath& path,
switch (source_association) {
case ASSOCIATE_CURRENT_RUN:
+ case ASSOCIATE_INTERNAL_PROFILE:
sources_to_check_.push_back(std::move(source));
break;
case ASSOCIATE_PREVIOUS_RUN:
+ case ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN:
DCHECK_EQ(SOURCE_HISTOGRAMS_ATOMIC_FILE, source->type);
sources_for_previous_run_.push_back(std::move(source));
break;
@@ -178,6 +212,13 @@ void FileMetricsProvider::RegisterPrefs(PrefRegistrySimple* prefs,
}
// static
+void FileMetricsProvider::SetTaskRunnerForTesting(
+ const scoped_refptr<base::TaskRunner>& task_runner) {
+ DCHECK(!g_task_runner_for_testing);
+ g_task_runner_for_testing = task_runner.get();
+}
+
+// static
bool FileMetricsProvider::LocateNextFileInDirectory(SourceInfo* source) {
DCHECK_EQ(SOURCE_HISTOGRAMS_ATOMIC_DIR, source->type);
DCHECK(!source->directory.empty());
@@ -250,6 +291,30 @@ bool FileMetricsProvider::LocateNextFileInDirectory(SourceInfo* source) {
}
// static
+void FileMetricsProvider::FinishedWithSource(SourceInfo* source,
+ AccessResult result) {
+ // Different source types require different post-processing.
+ switch (source->type) {
+ case SOURCE_HISTOGRAMS_ATOMIC_FILE:
+ case SOURCE_HISTOGRAMS_ATOMIC_DIR:
+ // Done with this file so delete the allocator and its owned file.
+ source->allocator.reset();
+ // Remove the file if has been recorded. This prevents them from
+ // accumulating or also being recorded by different instances of
+ // the browser.
+ if (result == ACCESS_RESULT_SUCCESS ||
+ result == ACCESS_RESULT_NOT_MODIFIED) {
+ DeleteFileWhenPossible(source->path);
+ }
+ break;
+ case SOURCE_HISTOGRAMS_ACTIVE_FILE:
+ // Keep the allocator open so it doesn't have to be re-mapped each
+ // time. This also allows the contents to be merged on-demand.
+ break;
+ }
+}
+
+// static
void FileMetricsProvider::CheckAndMergeMetricSourcesOnTaskRunner(
SourceInfoList* sources) {
// This method has all state information passed in |sources| and is intended
@@ -264,31 +329,19 @@ void FileMetricsProvider::CheckAndMergeMetricSourcesOnTaskRunner(
"UMA.FileMetricsProvider.AccessResult", result, ACCESS_RESULT_MAX);
}
+ // Metrics associated with internal profiles have to be fetched directly
+ // so just keep the mapping for use by the main thread.
+ if (source->association == ASSOCIATE_INTERNAL_PROFILE)
+ continue;
+
// Mapping was successful. Merge it.
if (result == ACCESS_RESULT_SUCCESS) {
MergeHistogramDeltasFromSource(source.get());
DCHECK(source->read_complete);
}
- // Different source types require different post-processing.
- switch (source->type) {
- case SOURCE_HISTOGRAMS_ATOMIC_FILE:
- case SOURCE_HISTOGRAMS_ATOMIC_DIR:
- // Done with this file so delete the allocator and its owned file.
- source->allocator.reset();
- // Remove the file if has been recorded. This prevents them from
- // accumulating or also being recorded by different instances of
- // the browser.
- if (result == ACCESS_RESULT_SUCCESS ||
- result == ACCESS_RESULT_NOT_MODIFIED) {
- base::DeleteFile(source->path, /*recursive=*/false);
- }
- break;
- case SOURCE_HISTOGRAMS_ACTIVE_FILE:
- // Keep the allocator open so it doesn't have to be re-mapped each
- // time. This also allows the contents to be merged on-demand.
- break;
- }
+ // All done with this source.
+ FinishedWithSource(source.get(), result);
}
}
@@ -297,8 +350,11 @@ void FileMetricsProvider::CheckAndMergeMetricSourcesOnTaskRunner(
// static
FileMetricsProvider::AccessResult FileMetricsProvider::CheckAndMapMetricSource(
SourceInfo* source) {
- DCHECK(!source->allocator);
+ // If source was read, clean up after it.
+ if (source->read_complete)
+ FinishedWithSource(source, ACCESS_RESULT_SUCCESS);
source->read_complete = false;
+ DCHECK(!source->allocator);
// If the source is a directory, look for files within it.
if (!source->directory.empty() && !LocateNextFileInDirectory(source))
@@ -383,7 +439,7 @@ void FileMetricsProvider::MergeHistogramDeltasFromSource(SourceInfo* source) {
void FileMetricsProvider::RecordHistogramSnapshotsFromSource(
base::HistogramSnapshotManager* snapshot_manager,
SourceInfo* source) {
- DCHECK_EQ(SOURCE_HISTOGRAMS_ATOMIC_FILE, source->type);
+ DCHECK_NE(SOURCE_HISTOGRAMS_ACTIVE_FILE, source->type);
base::PersistentHistogramAllocator::Iterator histogram_iter(
source->allocator.get());
@@ -403,7 +459,7 @@ void FileMetricsProvider::RecordHistogramSnapshotsFromSource(
}
void FileMetricsProvider::ScheduleSourcesCheck() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (sources_to_check_.empty())
return;
@@ -422,7 +478,7 @@ void FileMetricsProvider::ScheduleSourcesCheck() {
}
void FileMetricsProvider::RecordSourcesChecked(SourceInfoList* checked) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Sources that still have an allocator at this point are read/write "active"
// files that may need their contents merged on-demand. If there is no
@@ -437,10 +493,16 @@ void FileMetricsProvider::RecordSourcesChecked(SourceInfoList* checked) {
RecordSourceAsRead(source);
did_read = true;
}
- if (source->allocator)
- sources_mapped_.splice(sources_mapped_.end(), *checked, temp);
- else
+ if (source->allocator) {
+ if (source->association == ASSOCIATE_INTERNAL_PROFILE) {
+ sources_with_profile_.splice(sources_with_profile_.end(), *checked,
+ temp);
+ } else {
+ sources_mapped_.splice(sources_mapped_.end(), *checked, temp);
+ }
+ } else {
sources_to_check_.splice(sources_to_check_.end(), *checked, temp);
+ }
}
// If a read was done, schedule another one immediately. In the case of a
@@ -457,7 +519,7 @@ void FileMetricsProvider::DeleteFileAsync(const base::FilePath& path) {
}
void FileMetricsProvider::RecordSourceAsRead(SourceInfo* source) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Persistently record the "last seen" timestamp of the source file to
// ensure that the file is never read again unless it is modified again.
@@ -469,7 +531,7 @@ void FileMetricsProvider::RecordSourceAsRead(SourceInfo* source) {
}
void FileMetricsProvider::OnDidCreateMetricsLog() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Schedule a check to see if there are new metrics to load. If so, they
// will be reported during the next collection run after this one. The
@@ -487,8 +549,55 @@ void FileMetricsProvider::OnDidCreateMetricsLog() {
sources_for_previous_run_.clear();
}
+bool FileMetricsProvider::ProvideIndependentMetrics(
+ SystemProfileProto* system_profile_proto,
+ base::HistogramSnapshotManager* snapshot_manager) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ while (!sources_with_profile_.empty()) {
+ SourceInfo* source = sources_with_profile_.begin()->get();
+ DCHECK(source->allocator);
+
+ bool success = false;
+ RecordEmbeddedProfileResult(EMBEDDED_PROFILE_ATTEMPT);
+ if (PersistentSystemProfile::GetSystemProfile(
+ *source->allocator->memory_allocator(), system_profile_proto)) {
+ RecordHistogramSnapshotsFromSource(snapshot_manager, source);
+ success = true;
+ RecordEmbeddedProfileResult(EMBEDDED_PROFILE_FOUND);
+ } else {
+ RecordEmbeddedProfileResult(EMBEDDED_PROFILE_DROPPED);
+
+ // TODO(bcwhite): Remove these once crbug/695880 is resolved.
+
+ int histogram_count = 0;
+ base::PersistentHistogramAllocator::Iterator histogram_iter(
+ source->allocator.get());
+ while (histogram_iter.GetNext()) {
+ ++histogram_count;
+ }
+ UMA_HISTOGRAM_COUNTS_10000(
+ "UMA.FileMetricsProvider.EmbeddedProfile.DroppedHistogramCount",
+ histogram_count);
+ }
+
+ // Regardless of whether this source was successfully recorded, it is never
+ // read again.
+ source->read_complete = true;
+ RecordSourceAsRead(source);
+ sources_to_check_.splice(sources_to_check_.end(), sources_with_profile_,
+ sources_with_profile_.begin());
+ ScheduleSourcesCheck();
+
+ if (success)
+ return true;
+ }
+
+ return false;
+}
+
bool FileMetricsProvider::HasInitialStabilityMetrics() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Measure the total time spent checking all sources as well as the time
// per individual file. This method is called during startup and thus blocks
@@ -521,6 +630,21 @@ bool FileMetricsProvider::HasInitialStabilityMetrics() {
RecordSourceAsRead(source);
DeleteFileAsync(source->path);
sources_for_previous_run_.erase(temp);
+ continue;
+ }
+
+ DCHECK(source->allocator);
+
+ // If the source should be associated with an existing internal profile,
+ // move it to |sources_with_profile_| for later upload.
+ if (source->association == ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN) {
+ if (PersistentSystemProfile::HasSystemProfile(
+ *source->allocator->memory_allocator())) {
+ RecordEmbeddedProfileResult(EMBEDDED_PROFILE_ATTEMPT);
+ RecordEmbeddedProfileResult(EMBEDDED_PROFILE_FALLBACK);
+ sources_with_profile_.splice(sources_with_profile_.end(),
+ sources_for_previous_run_, temp);
+ }
}
}
@@ -529,7 +653,7 @@ bool FileMetricsProvider::HasInitialStabilityMetrics() {
void FileMetricsProvider::RecordInitialHistogramSnapshots(
base::HistogramSnapshotManager* snapshot_manager) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Measure the total time spent processing all sources as well as the time
// per individual file. This method is called during startup and thus blocks
@@ -556,7 +680,7 @@ void FileMetricsProvider::RecordInitialHistogramSnapshots(
}
void FileMetricsProvider::MergeHistogramDeltas() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Measure the total time spent processing all sources as well as the time
// per individual file. This method is called on the UI thread so it's
diff --git a/chromium/components/metrics/file_metrics_provider.h b/chromium/components/metrics/file_metrics_provider.h
index 43d4d304dea..66ce26612fa 100644
--- a/chromium/components/metrics/file_metrics_provider.h
+++ b/chromium/components/metrics/file_metrics_provider.h
@@ -14,7 +14,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/statistics_recorder.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "components/metrics/metrics_provider.h"
@@ -57,7 +57,9 @@ class FileMetricsProvider : public MetricsProvider,
// the ImportantFileWriter) will not get read. Files that have been
// read will be attempted to be deleted; should those files not be
// deletable by this process, it is the reponsibility of the producer
- // to keep the directory pruned in some manner.
+ // to keep the directory pruned in some manner. Added files must have a
+ // timestamp later (not the same or earlier) than the newest file that
+ // already exists or it may be assumed to have been already uploaded.
SOURCE_HISTOGRAMS_ATOMIC_DIR,
// "Active" files may be open by one or more other processes and updated
@@ -80,10 +82,22 @@ class FileMetricsProvider : public MetricsProvider,
// This is important when metrics are dumped as part of a crash of the
// previous run. This can only be used with FILE_HISTOGRAMS_ATOMIC.
ASSOCIATE_PREVIOUS_RUN,
+
+ // Associates the metrics in the file with the a profile embedded in the
+ // same file. The reporting will take place at a convenient time after
+ // startup when the browser is otherwise idle. If there is no embedded
+ // system profile, these metrics will be lost.
+ ASSOCIATE_INTERNAL_PROFILE,
+
+ // Like above but fall back to ASSOCIATE_PREVIOUS_RUN if there is no
+ // embedded profile. This has a small cost during startup as that is
+ // when previous-run metrics are sent so the file has be checked at
+ // that time even though actual transfer will be delayed if an
+ // embedded profile is found.
+ ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN,
};
- FileMetricsProvider(const scoped_refptr<base::TaskRunner>& task_runner,
- PrefService* local_state);
+ explicit FileMetricsProvider(PrefService* local_state);
~FileMetricsProvider() override;
// Indicates a file or directory to be monitored and how the file or files
@@ -104,6 +118,10 @@ class FileMetricsProvider : public MetricsProvider,
static void RegisterPrefs(PrefRegistrySimple* prefs,
const base::StringPiece prefs_key);
+ // Sets the task runner to use for testing.
+ static void SetTaskRunnerForTesting(
+ const scoped_refptr<base::TaskRunner>& task_runner);
+
private:
friend class FileMetricsProviderTest;
@@ -147,6 +165,9 @@ class FileMetricsProvider : public MetricsProvider,
// be internally updated to indicate the next file to be read.
static bool LocateNextFileInDirectory(SourceInfo* source);
+ // Handles the completion of a source.
+ static void FinishedWithSource(SourceInfo* source, AccessResult result);
+
// Checks a list of sources (on a task-runner allowed to do I/O) and merge
// any data found within them.
static void CheckAndMergeMetricSourcesOnTaskRunner(SourceInfoList* sources);
@@ -175,8 +196,11 @@ class FileMetricsProvider : public MetricsProvider,
// Updates the persistent state information to show a source as being read.
void RecordSourceAsRead(SourceInfo* source);
- // metrics::MetricsDataProvider:
+ // metrics::MetricsProvider:
void OnDidCreateMetricsLog() override;
+ bool ProvideIndependentMetrics(
+ SystemProfileProto* system_profile_proto,
+ base::HistogramSnapshotManager* snapshot_manager) override;
bool HasInitialStabilityMetrics() override;
void RecordInitialHistogramSnapshots(
base::HistogramSnapshotManager* snapshot_manager) override;
@@ -193,6 +217,9 @@ class FileMetricsProvider : public MetricsProvider,
// A list of currently active sources to be merged when required.
SourceInfoList sources_mapped_;
+ // A list of currently active sources to be merged when required.
+ SourceInfoList sources_with_profile_;
+
// A list of sources for a previous run. These are held separately because
// they are not subject to the periodic background checking that handles
// metrics for the current run.
@@ -201,7 +228,7 @@ class FileMetricsProvider : public MetricsProvider,
// The preferences-service used to store persistent state about sources.
PrefService* pref_service_;
- base::ThreadChecker thread_checker_;
+ SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<FileMetricsProvider> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FileMetricsProvider);
diff --git a/chromium/components/metrics/file_metrics_provider_unittest.cc b/chromium/components/metrics/file_metrics_provider_unittest.cc
index 9781cea05a3..982e28c06a1 100644
--- a/chromium/components/metrics/file_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/file_metrics_provider_unittest.cc
@@ -4,6 +4,8 @@
#include "components/metrics/file_metrics_provider.h"
+#include <functional>
+
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_temp_dir.h"
@@ -20,6 +22,8 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/persistent_system_profile.h"
+#include "components/metrics/proto/system_profile.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -83,6 +87,7 @@ class FileMetricsProviderTest : public testing::TestWithParam<bool> {
prefs_(new TestingPrefServiceSimple) {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
FileMetricsProvider::RegisterPrefs(prefs_->registry(), kMetricsName);
+ FileMetricsProvider::SetTaskRunnerForTesting(task_runner_);
}
~FileMetricsProviderTest() override {
@@ -102,7 +107,7 @@ class FileMetricsProviderTest : public testing::TestWithParam<bool> {
FileMetricsProvider* provider() {
if (!provider_)
- provider_.reset(new FileMetricsProvider(task_runner_, prefs()));
+ provider_.reset(new FileMetricsProvider(prefs()));
return provider_.get();
}
@@ -118,6 +123,13 @@ class FileMetricsProviderTest : public testing::TestWithParam<bool> {
provider()->MergeHistogramDeltas();
}
+ bool ProvideIndependentMetrics(
+ SystemProfileProto* profile_proto,
+ base::HistogramSnapshotManager* snapshot_manager) {
+ return provider()->ProvideIndependentMetrics(profile_proto,
+ snapshot_manager);
+ }
+
void RecordInitialHistogramSnapshots(
base::HistogramSnapshotManager* snapshot_manager) {
provider()->RecordInitialHistogramSnapshots(snapshot_manager);
@@ -176,7 +188,10 @@ class FileMetricsProviderTest : public testing::TestWithParam<bool> {
}
std::unique_ptr<base::PersistentHistogramAllocator>
- CreateMetricsFileWithHistograms(int histogram_count) {
+ CreateMetricsFileWithHistograms(
+ int histogram_count,
+ const std::function<void(base::PersistentHistogramAllocator*)>&
+ callback) {
// Get this first so it isn't created inside the persistent allocator.
base::GlobalHistogramAllocator::GetCreateHistogramResultHistogram();
@@ -188,10 +203,18 @@ class FileMetricsProviderTest : public testing::TestWithParam<bool> {
std::unique_ptr<base::PersistentHistogramAllocator> histogram_allocator =
base::GlobalHistogramAllocator::ReleaseForTesting();
+ callback(histogram_allocator.get());
+
WriteMetricsFile(metrics_file(), histogram_allocator.get());
return histogram_allocator;
}
+ std::unique_ptr<base::PersistentHistogramAllocator>
+ CreateMetricsFileWithHistograms(int histogram_count) {
+ return CreateMetricsFileWithHistograms(
+ histogram_count, [](base::PersistentHistogramAllocator* allocator) {});
+ }
+
base::HistogramBase* GetCreatedHistogram(int index) {
DCHECK_GT(kMaxCreateHistograms, index);
return created_histograms_[index];
@@ -411,7 +434,7 @@ TEST_P(FileMetricsProviderTest, AccessInitialMetrics) {
kMetricsName);
// Record embedded snapshots via snapshot-manager.
- HasInitialStabilityMetrics();
+ ASSERT_TRUE(HasInitialStabilityMetrics());
RunTasks();
{
HistogramFlattenerDeltaRecorder flattener;
@@ -435,4 +458,198 @@ TEST_P(FileMetricsProviderTest, AccessInitialMetrics) {
EXPECT_TRUE(base::PathExists(metrics_file()));
}
+TEST_P(FileMetricsProviderTest, AccessEmbeddedProfileMetricsWithoutProfile) {
+ ASSERT_FALSE(PathExists(metrics_file()));
+ CreateMetricsFileWithHistograms(2);
+
+ // Register the file and allow the "checker" task to run.
+ ASSERT_TRUE(PathExists(metrics_file()));
+ provider()->RegisterSource(
+ metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
+ FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName);
+
+ // Record embedded snapshots via snapshot-manager.
+ OnDidCreateMetricsLog();
+ RunTasks();
+ {
+ HistogramFlattenerDeltaRecorder flattener;
+ base::HistogramSnapshotManager snapshot_manager(&flattener);
+ SystemProfileProto profile;
+
+ // A read of metrics with internal profiles should return nothing.
+ EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+ }
+ EXPECT_TRUE(base::PathExists(metrics_file()));
+ OnDidCreateMetricsLog();
+ RunTasks();
+ EXPECT_FALSE(base::PathExists(metrics_file()));
+}
+
+TEST_P(FileMetricsProviderTest, AccessEmbeddedProfileMetricsWithProfile) {
+ ASSERT_FALSE(PathExists(metrics_file()));
+ CreateMetricsFileWithHistograms(
+ 2, [](base::PersistentHistogramAllocator* allocator) {
+ SystemProfileProto profile_proto;
+ SystemProfileProto::FieldTrial* trial = profile_proto.add_field_trial();
+ trial->set_name_id(123);
+ trial->set_group_id(456);
+
+ PersistentSystemProfile persistent_profile;
+ persistent_profile.RegisterPersistentAllocator(
+ allocator->memory_allocator());
+ persistent_profile.SetSystemProfile(profile_proto, true);
+ });
+
+ // Register the file and allow the "checker" task to run.
+ ASSERT_TRUE(PathExists(metrics_file()));
+ provider()->RegisterSource(
+ metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
+ FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName);
+
+ // Record embedded snapshots via snapshot-manager.
+ OnDidCreateMetricsLog();
+ RunTasks();
+ {
+ HistogramFlattenerDeltaRecorder flattener;
+ base::HistogramSnapshotManager snapshot_manager(&flattener);
+ RecordInitialHistogramSnapshots(&snapshot_manager);
+ EXPECT_EQ(0U, flattener.GetRecordedDeltaHistogramNames().size());
+
+ // A read of metrics with internal profiles should return one result.
+ SystemProfileProto profile;
+ EXPECT_TRUE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+ EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+ }
+ EXPECT_TRUE(base::PathExists(metrics_file()));
+ OnDidCreateMetricsLog();
+ RunTasks();
+ EXPECT_FALSE(base::PathExists(metrics_file()));
+}
+
+TEST_P(FileMetricsProviderTest, AccessEmbeddedFallbackMetricsWithoutProfile) {
+ ASSERT_FALSE(PathExists(metrics_file()));
+ CreateMetricsFileWithHistograms(2);
+
+ // Register the file and allow the "checker" task to run.
+ ASSERT_TRUE(PathExists(metrics_file()));
+ provider()->RegisterSource(
+ metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
+ FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN,
+ kMetricsName);
+
+ // Record embedded snapshots via snapshot-manager.
+ ASSERT_TRUE(HasInitialStabilityMetrics());
+ RunTasks();
+ {
+ HistogramFlattenerDeltaRecorder flattener;
+ base::HistogramSnapshotManager snapshot_manager(&flattener);
+ RecordInitialHistogramSnapshots(&snapshot_manager);
+ EXPECT_EQ(2U, flattener.GetRecordedDeltaHistogramNames().size());
+
+ // A read of metrics with internal profiles should return nothing.
+ SystemProfileProto profile;
+ EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+ }
+ EXPECT_TRUE(base::PathExists(metrics_file()));
+ OnDidCreateMetricsLog();
+ RunTasks();
+ EXPECT_FALSE(base::PathExists(metrics_file()));
+}
+
+TEST_P(FileMetricsProviderTest, AccessEmbeddedFallbackMetricsWithProfile) {
+ ASSERT_FALSE(PathExists(metrics_file()));
+ CreateMetricsFileWithHistograms(
+ 2, [](base::PersistentHistogramAllocator* allocator) {
+ SystemProfileProto profile_proto;
+ SystemProfileProto::FieldTrial* trial = profile_proto.add_field_trial();
+ trial->set_name_id(123);
+ trial->set_group_id(456);
+
+ PersistentSystemProfile persistent_profile;
+ persistent_profile.RegisterPersistentAllocator(
+ allocator->memory_allocator());
+ persistent_profile.SetSystemProfile(profile_proto, true);
+ });
+
+ // Register the file and allow the "checker" task to run.
+ ASSERT_TRUE(PathExists(metrics_file()));
+ provider()->RegisterSource(
+ metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
+ FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN,
+ kMetricsName);
+
+ // Record embedded snapshots via snapshot-manager.
+ EXPECT_FALSE(HasInitialStabilityMetrics());
+ RunTasks();
+ {
+ HistogramFlattenerDeltaRecorder flattener;
+ base::HistogramSnapshotManager snapshot_manager(&flattener);
+ RecordInitialHistogramSnapshots(&snapshot_manager);
+ EXPECT_EQ(0U, flattener.GetRecordedDeltaHistogramNames().size());
+
+ // A read of metrics with internal profiles should return one result.
+ SystemProfileProto profile;
+ EXPECT_TRUE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+ EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+ }
+ EXPECT_TRUE(base::PathExists(metrics_file()));
+ OnDidCreateMetricsLog();
+ RunTasks();
+ EXPECT_FALSE(base::PathExists(metrics_file()));
+}
+
+TEST_P(FileMetricsProviderTest, AccessEmbeddedProfileMetricsFromDir) {
+ const int file_count = 3;
+ base::Time file_base_time = base::Time::Now();
+ std::vector<base::FilePath> file_names;
+ for (int i = 0; i < file_count; ++i) {
+ CreateMetricsFileWithHistograms(
+ 2, [](base::PersistentHistogramAllocator* allocator) {
+ SystemProfileProto profile_proto;
+ SystemProfileProto::FieldTrial* trial =
+ profile_proto.add_field_trial();
+ trial->set_name_id(123);
+ trial->set_group_id(456);
+
+ PersistentSystemProfile persistent_profile;
+ persistent_profile.RegisterPersistentAllocator(
+ allocator->memory_allocator());
+ persistent_profile.SetSystemProfile(profile_proto, true);
+ });
+ ASSERT_TRUE(PathExists(metrics_file()));
+ char new_name[] = "hX";
+ new_name[1] = '1' + i;
+ base::FilePath file_name = temp_dir().AppendASCII(new_name).AddExtension(
+ base::PersistentMemoryAllocator::kFileExtension);
+ base::Time file_time =
+ file_base_time - base::TimeDelta::FromMinutes(file_count - i);
+ base::TouchFile(metrics_file(), file_time, file_time);
+ base::Move(metrics_file(), file_name);
+ file_names.push_back(std::move(file_name));
+ }
+
+ // Register the file and allow the "checker" task to run.
+ provider()->RegisterSource(
+ temp_dir(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
+ FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, "");
+
+ OnDidCreateMetricsLog();
+ RunTasks();
+
+ // A read of metrics with internal profiles should return one result.
+ HistogramFlattenerDeltaRecorder flattener;
+ base::HistogramSnapshotManager snapshot_manager(&flattener);
+ SystemProfileProto profile;
+ for (int i = 0; i < file_count; ++i) {
+ EXPECT_TRUE(ProvideIndependentMetrics(&profile, &snapshot_manager)) << i;
+ RunTasks();
+ }
+ EXPECT_FALSE(ProvideIndependentMetrics(&profile, &snapshot_manager));
+
+ OnDidCreateMetricsLog();
+ RunTasks();
+ for (const auto& file_name : file_names)
+ EXPECT_FALSE(base::PathExists(file_name));
+}
+
} // namespace metrics
diff --git a/chromium/components/metrics/leak_detector/leak_detector.cc b/chromium/components/metrics/leak_detector/leak_detector.cc
index 800ff3b00cd..ac03e612070 100644
--- a/chromium/components/metrics/leak_detector/leak_detector.cc
+++ b/chromium/components/metrics/leak_detector/leak_detector.cc
@@ -338,7 +338,7 @@ void LeakDetector::NotifyObservers(
if (reports.empty())
return;
- if (!task_runner_->RunsTasksOnCurrentThread()) {
+ if (!task_runner_->RunsTasksInCurrentSequence()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(&LeakDetector::NotifyObservers,
base::Unretained(this), reports));
diff --git a/chromium/components/metrics/machine_id_provider.h b/chromium/components/metrics/machine_id_provider.h
index d7fcc447639..b7a27043829 100644
--- a/chromium/components/metrics/machine_id_provider.h
+++ b/chromium/components/metrics/machine_id_provider.h
@@ -8,37 +8,28 @@
#include <string>
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
namespace metrics {
// Provides machine characteristics used as a machine id. The implementation is
-// platform specific with a default implementation that gives an empty id. The
-// class is ref-counted thread safe so it can be used to post to the FILE thread
-// and communicate back to the UI thread.
-// This raw machine id should not be stored or transmitted over the network.
-// TODO(jwd): Simplify implementation to get rid of the need for
-// RefCountedThreadSafe (crbug.com/354882).
-class MachineIdProvider : public base::RefCountedThreadSafe<MachineIdProvider> {
+// platform specific. GetMachineId() must be called on a thread which allows
+// I/O. GetMachineId() must not be called if HasId() returns false on this
+// platform.
+class MachineIdProvider {
public:
+ // Returns true if this platform provides a non-empty GetMachineId(). This is
+ // useful to avoid an async call to GetMachineId() on platforms with no
+ // implementation.
+ static bool HasId();
+
// Get a string containing machine characteristics, to be used as a machine
// id. The implementation is platform specific, with a default implementation
// returning an empty string.
// The return value should not be stored to disk or transmitted.
- std::string GetMachineId();
-
- // Returns a pointer to a new MachineIdProvider or NULL if there is no
- // provider implemented on a given platform. This is done to avoid posting a
- // task to the FILE thread on platforms with no implementation.
- static MachineIdProvider* CreateInstance();
+ static std::string GetMachineId();
private:
- friend class base::RefCountedThreadSafe<MachineIdProvider>;
-
- MachineIdProvider();
- virtual ~MachineIdProvider();
-
- DISALLOW_COPY_AND_ASSIGN(MachineIdProvider);
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MachineIdProvider);
};
} // namespace metrics
diff --git a/chromium/components/metrics/machine_id_provider_stub.cc b/chromium/components/metrics/machine_id_provider_stub.cc
index 626f2b7cdc8..d7472098745 100644
--- a/chromium/components/metrics/machine_id_provider_stub.cc
+++ b/chromium/components/metrics/machine_id_provider_stub.cc
@@ -4,20 +4,18 @@
#include "components/metrics/machine_id_provider.h"
-namespace metrics {
-
-MachineIdProvider::MachineIdProvider() {
-}
+#include "base/logging.h"
-MachineIdProvider::~MachineIdProvider() {
-}
+namespace metrics {
// static
-MachineIdProvider* MachineIdProvider::CreateInstance() {
- return NULL;
+bool MachineIdProvider::HasId() {
+ return false;
}
+// static
std::string MachineIdProvider::GetMachineId() {
+ NOTREACHED();
return std::string();
}
diff --git a/chromium/components/metrics/machine_id_provider_win.cc b/chromium/components/metrics/machine_id_provider_win.cc
index 41079a7dc2c..15dbb5fd449 100644
--- a/chromium/components/metrics/machine_id_provider_win.cc
+++ b/chromium/components/metrics/machine_id_provider_win.cc
@@ -16,14 +16,14 @@
namespace metrics {
-MachineIdProvider::MachineIdProvider() {
-}
-
-MachineIdProvider::~MachineIdProvider() {
+// static
+bool MachineIdProvider::HasId() {
+ return true;
}
// On windows, the machine id is based on the serial number of the drive Chrome
// is running from.
+// static
std::string MachineIdProvider::GetMachineId() {
base::ThreadRestrictions::AssertIOAllowed();
@@ -109,10 +109,4 @@ std::string MachineIdProvider::GetMachineId() {
return std::string(serial_number);
}
-
-// static
-MachineIdProvider* MachineIdProvider::CreateInstance() {
- return new MachineIdProvider();
-}
-
} // namespace metrics
diff --git a/chromium/components/metrics/machine_id_provider_win_unittest.cc b/chromium/components/metrics/machine_id_provider_win_unittest.cc
index 11ffd8f73df..6bcd0c41e56 100644
--- a/chromium/components/metrics/machine_id_provider_win_unittest.cc
+++ b/chromium/components/metrics/machine_id_provider_win_unittest.cc
@@ -4,24 +4,17 @@
#include "components/metrics/machine_id_provider.h"
-#include "base/memory/ref_counted.h"
-#include "base/win/windows_version.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
TEST(MachineIdProviderTest, GetId) {
- scoped_refptr<MachineIdProvider> provider(
- MachineIdProvider::CreateInstance());
- std::string id1 = provider->GetMachineId();
-
- // TODO(rpaquay): See crbug/458230
- if (base::win::GetVersion() <= base::win::VERSION_XP)
- return;
+ EXPECT_TRUE(MachineIdProvider::HasId());
+ const std::string id1 = MachineIdProvider::GetMachineId();
EXPECT_NE(std::string(), id1);
- std::string id2 = provider->GetMachineId();
+ const std::string id2 = MachineIdProvider::GetMachineId();
EXPECT_EQ(id1, id2);
}
diff --git a/chromium/components/metrics/metrics_log.cc b/chromium/components/metrics/metrics_log.cc
index ac35ce396a3..5169ea849a9 100644
--- a/chromium/components/metrics/metrics_log.cc
+++ b/chromium/components/metrics/metrics_log.cc
@@ -11,9 +11,13 @@
#include "base/build_time.h"
#include "base/cpu.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_flattener.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/histogram_samples.h"
+#include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/metrics_hashes.h"
+#include "base/strings/string_piece.h"
#include "base/sys_info.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -22,12 +26,12 @@
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_provider.h"
#include "components/metrics/metrics_service_client.h"
+#include "components/metrics/persistent_system_profile.h"
#include "components/metrics/proto/histogram_event.pb.h"
#include "components/metrics/proto/system_profile.pb.h"
#include "components/metrics/proto/user_action_event.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
-#include "components/variations/active_field_trials.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
@@ -38,28 +42,44 @@
#endif
using base::SampleCountIterator;
-typedef variations::ActiveGroupId ActiveGroupId;
namespace metrics {
+namespace internal {
+// Maximum number of events before truncation.
+extern const int kOmniboxEventLimit = 5000;
+extern const int kUserActionEventLimit = 5000;
+}
+
namespace {
+// A simple class to write histogram data to a log.
+class IndependentFlattener : public base::HistogramFlattener {
+ public:
+ explicit IndependentFlattener(MetricsLog* log) : log_(log) {}
+
+ // base::HistogramFlattener:
+ void RecordDelta(const base::HistogramBase& histogram,
+ const base::HistogramSamples& snapshot) override {
+ log_->RecordHistogramDelta(histogram.histogram_name(), snapshot);
+ }
+ void InconsistencyDetected(
+ base::HistogramBase::Inconsistency problem) override {}
+ void UniqueInconsistencyDetected(
+ base::HistogramBase::Inconsistency problem) override {}
+ void InconsistencyDetectedInLoggedCount(int amount) override {}
+
+ private:
+ MetricsLog* const log_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndependentFlattener);
+};
+
// Any id less than 16 bytes is considered to be a testing id.
bool IsTestingID(const std::string& id) {
return id.size() < 16;
}
-void WriteFieldTrials(const std::vector<ActiveGroupId>& field_trial_ids,
- SystemProfileProto* system_profile) {
- for (std::vector<ActiveGroupId>::const_iterator it =
- field_trial_ids.begin(); it != field_trial_ids.end(); ++it) {
- SystemProfileProto::FieldTrial* field_trial =
- system_profile->add_field_trial();
- field_trial->set_name_id(it->name);
- field_trial->set_group_id(it->group);
- }
-}
-
// Round a timestamp measured in seconds since epoch to one with a granularity
// of an hour. This can be used before uploaded potentially sensitive
// timestamps.
@@ -91,7 +111,12 @@ MetricsLog::MetricsLog(const std::string& client_id,
if (product != uma_proto_.product())
uma_proto_.set_product(product);
- RecordCoreSystemProfile(client_, uma_proto_.mutable_system_profile());
+ SystemProfileProto* system_profile = uma_proto()->mutable_system_profile();
+ RecordCoreSystemProfile(client_, system_profile);
+ if (log_type_ == ONGOING_LOG) {
+ GlobalPersistentSystemProfile::GetInstance()->SetSystemProfile(
+ *system_profile, /*complete=*/false);
+ }
}
MetricsLog::~MetricsLog() {
@@ -209,11 +234,6 @@ void MetricsLog::RecordGeneralMetrics(
metrics_providers[i]->ProvideGeneralMetrics(uma_proto());
}
-void MetricsLog::GetFieldTrialIds(
- std::vector<ActiveGroupId>* field_trial_ids) const {
- variations::GetFieldTrialActiveGroupIds(field_trial_ids);
-}
-
bool MetricsLog::HasEnvironment() const {
return uma_proto()->system_profile().has_uma_enabled_date();
}
@@ -266,7 +286,6 @@ void MetricsLog::WriteRealtimeStabilityAttributes(
std::string MetricsLog::RecordEnvironment(
const std::vector<std::unique_ptr<MetricsProvider>>& metrics_providers,
- const std::vector<variations::ActiveGroupId>& synthetic_trials,
int64_t install_date,
int64_t metrics_reporting_enabled_date) {
DCHECK(!HasEnvironment());
@@ -294,16 +313,28 @@ std::string MetricsLog::RecordEnvironment(
cpu->set_signature(cpu_info.signature());
cpu->set_num_cores(base::SysInfo::NumberOfProcessors());
- std::vector<ActiveGroupId> field_trial_ids;
- GetFieldTrialIds(&field_trial_ids);
- WriteFieldTrials(field_trial_ids, system_profile);
- WriteFieldTrials(synthetic_trials, system_profile);
-
for (size_t i = 0; i < metrics_providers.size(); ++i)
metrics_providers[i]->ProvideSystemProfileMetrics(system_profile);
EnvironmentRecorder recorder(local_state_);
- return recorder.SerializeAndRecordEnvironmentToPrefs(*system_profile);
+ std::string serialized_proto =
+ recorder.SerializeAndRecordEnvironmentToPrefs(*system_profile);
+
+ if (log_type_ == ONGOING_LOG) {
+ GlobalPersistentSystemProfile::GetInstance()->SetSystemProfile(
+ serialized_proto, /*complete=*/true);
+ }
+
+ return serialized_proto;
+}
+
+bool MetricsLog::LoadIndependentMetrics(MetricsProvider* metrics_provider) {
+ SystemProfileProto* system_profile = uma_proto()->mutable_system_profile();
+ IndependentFlattener flattener(this);
+ base::HistogramSnapshotManager snapshot_manager(&flattener);
+
+ return metrics_provider->ProvideIndependentMetrics(system_profile,
+ &snapshot_manager);
}
bool MetricsLog::LoadSavedEnvironmentFromPrefs(std::string* app_version) {
@@ -323,6 +354,25 @@ void MetricsLog::CloseLog() {
closed_ = true;
}
+void MetricsLog::TruncateEvents() {
+ DCHECK(!closed_);
+ if (uma_proto_.user_action_event_size() > internal::kUserActionEventLimit) {
+ UMA_HISTOGRAM_COUNTS_100000("UMA.TruncatedEvents.UserAction",
+ uma_proto_.user_action_event_size());
+ uma_proto_.mutable_user_action_event()->DeleteSubrange(
+ internal::kUserActionEventLimit,
+ uma_proto_.user_action_event_size() - internal::kUserActionEventLimit);
+ }
+
+ if (uma_proto_.omnibox_event_size() > internal::kOmniboxEventLimit) {
+ UMA_HISTOGRAM_COUNTS_100000("UMA.TruncatedEvents.Omnibox",
+ uma_proto_.omnibox_event_size());
+ uma_proto_.mutable_omnibox_event()->DeleteSubrange(
+ internal::kOmniboxEventLimit,
+ uma_proto_.omnibox_event_size() - internal::kOmniboxEventLimit);
+ }
+}
+
void MetricsLog::GetEncodedLog(std::string* encoded_log) {
DCHECK(closed_);
uma_proto_.SerializeToString(encoded_log);
diff --git a/chromium/components/metrics/metrics_log.h b/chromium/components/metrics/metrics_log.h
index 4f28b43b3ae..27e90330444 100644
--- a/chromium/components/metrics/metrics_log.h
+++ b/chromium/components/metrics/metrics_log.h
@@ -25,12 +25,13 @@ namespace base {
class HistogramSamples;
}
-namespace variations {
-struct ActiveGroupId;
-}
-
namespace metrics {
+namespace internal {
+extern const int kOmniboxEventLimit;
+extern const int kUserActionEventLimit;
+}
+
class MetricsProvider;
class MetricsServiceClient;
@@ -39,6 +40,7 @@ class MetricsLog {
enum LogType {
INITIAL_STABILITY_LOG, // The initial log containing stability stats.
ONGOING_LOG, // Subsequent logs in a session.
+ INDEPENDENT_LOG, // An independent log from a previous session.
};
// Creates a new metrics log of the specified type.
@@ -96,10 +98,14 @@ class MetricsLog {
// current environment is returned serialized as a string.
std::string RecordEnvironment(
const std::vector<std::unique_ptr<MetricsProvider>>& metrics_providers,
- const std::vector<variations::ActiveGroupId>& synthetic_trials,
int64_t install_date,
int64_t metrics_reporting_enabled_date);
+ // Loads a saved system profile and the associated metrics into the log.
+ // Returns true on success. Keep calling it with fresh logs until it returns
+ // false.
+ bool LoadIndependentMetrics(MetricsProvider* metrics_provider);
+
// Loads the environment proto that was saved by the last RecordEnvironment()
// call from prefs. On success, returns true and |app_version| contains the
// recovered version. Otherwise (if there was no saved environment in prefs
@@ -128,6 +134,10 @@ class MetricsLog {
// None of the Record* methods can be called after this is called.
void CloseLog();
+ // Truncate some of the fields within the log that we want to restrict in
+ // size due to bandwidth concerns.
+ void TruncateEvents();
+
// Fills |encoded_log| with the serialized protobuf representation of the
// record. Must only be called after CloseLog() has been called.
void GetEncodedLog(std::string* encoded_log);
@@ -136,21 +146,11 @@ class MetricsLog {
return creation_time_;
}
- int num_events() const {
- return uma_proto_.omnibox_event_size() +
- uma_proto_.user_action_event_size();
- }
-
LogType log_type() const { return log_type_; }
protected:
// Exposed for the sake of mocking/accessing in test code.
- // Fills |field_trial_ids| with the list of initialized field trials name and
- // group ids.
- virtual void GetFieldTrialIds(
- std::vector<variations::ActiveGroupId>* field_trial_ids) const;
-
ChromeUserMetricsExtension* uma_proto() { return &uma_proto_; }
// Exposed to allow subclass to access to export the uma_proto. Can be used
diff --git a/chromium/components/metrics/metrics_log_store.cc b/chromium/components/metrics/metrics_log_store.cc
index 8b1ec2a710f..30cbe6cfb97 100644
--- a/chromium/components/metrics/metrics_log_store.cc
+++ b/chromium/components/metrics/metrics_log_store.cc
@@ -73,6 +73,7 @@ void MetricsLogStore::StoreLog(const std::string& log_data,
initial_log_queue_.StoreLog(log_data);
break;
case MetricsLog::ONGOING_LOG:
+ case MetricsLog::INDEPENDENT_LOG:
ongoing_log_queue_.StoreLog(log_data);
break;
}
diff --git a/chromium/components/metrics/metrics_log_unittest.cc b/chromium/components/metrics/metrics_log_unittest.cc
index f1a7792f85d..cb48cc0922a 100644
--- a/chromium/components/metrics/metrics_log_unittest.cc
+++ b/chromium/components/metrics/metrics_log_unittest.cc
@@ -46,15 +46,6 @@ const int64_t kInstallDateExpected = 1373050800; // Computed from kInstallDate.
const int64_t kEnabledDate = 1373001211;
const int64_t kEnabledDateExpected = 1373000400; // Computed from kEnabledDate.
const int kSessionId = 127;
-const variations::ActiveGroupId kFieldTrialIds[] = {
- {37, 43},
- {13, 47},
- {23, 17}
-};
-const variations::ActiveGroupId kSyntheticTrials[] = {
- {55, 15},
- {66, 16}
-};
class TestMetricsLog : public MetricsLog {
public:
@@ -74,6 +65,10 @@ class TestMetricsLog : public MetricsLog {
return *MetricsLog::uma_proto();
}
+ ChromeUserMetricsExtension* mutable_uma_proto() {
+ return MetricsLog::uma_proto();
+ }
+
const SystemProfileProto& system_profile() const {
return uma_proto().system_profile();
}
@@ -84,15 +79,6 @@ class TestMetricsLog : public MetricsLog {
base::Int64ToString(kEnabledDate));
}
- void GetFieldTrialIds(
- std::vector<variations::ActiveGroupId>* field_trial_ids) const override {
- ASSERT_TRUE(field_trial_ids->empty());
-
- for (size_t i = 0; i < arraysize(kFieldTrialIds); ++i) {
- field_trial_ids->push_back(kFieldTrialIds[i]);
- }
- }
-
// Weak pointer to the PrefsService used by this log.
TestingPrefServiceSimple* prefs_;
@@ -117,22 +103,6 @@ class MetricsLogTest : public testing::Test {
EXPECT_EQ(kInstallDateExpected, system_profile.install_date());
EXPECT_EQ(kEnabledDateExpected, system_profile.uma_enabled_date());
- ASSERT_EQ(arraysize(kFieldTrialIds) + arraysize(kSyntheticTrials),
- static_cast<size_t>(system_profile.field_trial_size()));
- for (size_t i = 0; i < arraysize(kFieldTrialIds); ++i) {
- const SystemProfileProto::FieldTrial& field_trial =
- system_profile.field_trial(i);
- EXPECT_EQ(kFieldTrialIds[i].name, field_trial.name_id());
- EXPECT_EQ(kFieldTrialIds[i].group, field_trial.group_id());
- }
- // Verify the right data is present for the synthetic trials.
- for (size_t i = 0; i < arraysize(kSyntheticTrials); ++i) {
- const SystemProfileProto::FieldTrial& field_trial =
- system_profile.field_trial(i + arraysize(kFieldTrialIds));
- EXPECT_EQ(kSyntheticTrials[i].name, field_trial.name_id());
- EXPECT_EQ(kSyntheticTrials[i].group, field_trial.group_id());
- }
-
EXPECT_EQ(TestMetricsServiceClient::kBrandForTesting,
system_profile.brand_code());
@@ -285,13 +255,8 @@ TEST_F(MetricsLogTest, RecordEnvironment) {
TestMetricsLog log(
kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_);
- std::vector<variations::ActiveGroupId> synthetic_trials;
- // Add two synthetic trials.
- synthetic_trials.push_back(kSyntheticTrials[0]);
- synthetic_trials.push_back(kSyntheticTrials[1]);
-
log.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
- synthetic_trials, kInstallDate, kEnabledDate);
+ kInstallDate, kEnabledDate);
// Check that the system profile on the log has the correct values set.
CheckSystemProfile(log.system_profile());
@@ -307,17 +272,15 @@ TEST_F(MetricsLogTest, RecordEnvironmentEnableDefault) {
TestMetricsLog log_unknown(kClientId, kSessionId, MetricsLog::ONGOING_LOG,
&client, &prefs_);
- std::vector<variations::ActiveGroupId> synthetic_trials;
-
log_unknown.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
- synthetic_trials, kInstallDate, kEnabledDate);
+ kInstallDate, kEnabledDate);
EXPECT_FALSE(log_unknown.system_profile().has_uma_default_state());
client.set_enable_default(EnableMetricsDefault::OPT_IN);
TestMetricsLog log_opt_in(kClientId, kSessionId, MetricsLog::ONGOING_LOG,
&client, &prefs_);
log_opt_in.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
- synthetic_trials, kInstallDate, kEnabledDate);
+ kInstallDate, kEnabledDate);
EXPECT_TRUE(log_opt_in.system_profile().has_uma_default_state());
EXPECT_EQ(SystemProfileProto_UmaDefaultState_OPT_IN,
log_opt_in.system_profile().uma_default_state());
@@ -326,7 +289,7 @@ TEST_F(MetricsLogTest, RecordEnvironmentEnableDefault) {
TestMetricsLog log_opt_out(kClientId, kSessionId, MetricsLog::ONGOING_LOG,
&client, &prefs_);
log_opt_out.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
- synthetic_trials, kInstallDate, kEnabledDate);
+ kInstallDate, kEnabledDate);
EXPECT_TRUE(log_opt_out.system_profile().has_uma_default_state());
EXPECT_EQ(SystemProfileProto_UmaDefaultState_OPT_OUT,
log_opt_out.system_profile().uma_default_state());
@@ -335,7 +298,7 @@ TEST_F(MetricsLogTest, RecordEnvironmentEnableDefault) {
TestMetricsLog log_managed(kClientId, kSessionId, MetricsLog::ONGOING_LOG,
&client, &prefs_);
log_managed.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
- synthetic_trials, kInstallDate, kEnabledDate);
+ kInstallDate, kEnabledDate);
EXPECT_TRUE(log_managed.system_profile().has_uma_default_state());
EXPECT_EQ(SystemProfileProto_UmaDefaultState_POLICY_FORCED_ENABLED,
log_managed.system_profile().uma_default_state());
@@ -351,9 +314,7 @@ TEST_F(MetricsLogTest, InitialLogStabilityMetrics) {
TestMetricsProvider* test_provider = new TestMetricsProvider();
std::vector<std::unique_ptr<MetricsProvider>> metrics_providers;
metrics_providers.push_back(base::WrapUnique<MetricsProvider>(test_provider));
- log.RecordEnvironment(metrics_providers,
- std::vector<variations::ActiveGroupId>(), kInstallDate,
- kEnabledDate);
+ log.RecordEnvironment(metrics_providers, kInstallDate, kEnabledDate);
log.RecordStabilityMetrics(metrics_providers, base::TimeDelta(),
base::TimeDelta());
@@ -370,9 +331,7 @@ TEST_F(MetricsLogTest, OngoingLogStabilityMetrics) {
TestMetricsProvider* test_provider = new TestMetricsProvider();
std::vector<std::unique_ptr<MetricsProvider>> metrics_providers;
metrics_providers.push_back(base::WrapUnique<MetricsProvider>(test_provider));
- log.RecordEnvironment(metrics_providers,
- std::vector<variations::ActiveGroupId>(), kInstallDate,
- kEnabledDate);
+ log.RecordEnvironment(metrics_providers, kInstallDate, kEnabledDate);
log.RecordStabilityMetrics(metrics_providers, base::TimeDelta(),
base::TimeDelta());
@@ -413,4 +372,27 @@ TEST_F(MetricsLogTest, ProductSetIfNotDefault) {
EXPECT_EQ(kTestProduct, log.uma_proto().product());
}
+TEST_F(MetricsLogTest, TruncateEvents) {
+ TestMetricsServiceClient client;
+ TestMetricsLog log(kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client,
+ &prefs_);
+
+ for (int i = 0; i < internal::kUserActionEventLimit * 2; ++i) {
+ log.RecordUserAction("BasicAction");
+ EXPECT_EQ(i + 1, log.uma_proto().user_action_event_size());
+ }
+ for (int i = 0; i < internal::kOmniboxEventLimit * 2; ++i) {
+ // Add an empty omnibox event. Not fully realistic since these are normally
+ // supplied by a metrics provider.
+ log.mutable_uma_proto()->add_omnibox_event();
+ EXPECT_EQ(i + 1, log.uma_proto().omnibox_event_size());
+ }
+
+ // Truncate, and check that the current size is the limit.
+ log.TruncateEvents();
+ EXPECT_EQ(internal::kUserActionEventLimit,
+ log.uma_proto().user_action_event_size());
+ EXPECT_EQ(internal::kOmniboxEventLimit, log.uma_proto().omnibox_event_size());
+}
+
} // namespace metrics
diff --git a/chromium/components/metrics/metrics_provider.cc b/chromium/components/metrics/metrics_provider.cc
index fd864e41136..c5ca5853d34 100644
--- a/chromium/components/metrics/metrics_provider.cc
+++ b/chromium/components/metrics/metrics_provider.cc
@@ -27,6 +27,12 @@ void MetricsProvider::OnRecordingDisabled() {
void MetricsProvider::OnAppEnterBackground() {
}
+bool MetricsProvider::ProvideIndependentMetrics(
+ SystemProfileProto* system_profile_proto,
+ base::HistogramSnapshotManager* snapshot_manager) {
+ return false;
+}
+
void MetricsProvider::ProvideSystemProfileMetrics(
SystemProfileProto* system_profile_proto) {
}
diff --git a/chromium/components/metrics/metrics_provider.h b/chromium/components/metrics/metrics_provider.h
index 54c0a894904..a867ef45bcc 100644
--- a/chromium/components/metrics/metrics_provider.h
+++ b/chromium/components/metrics/metrics_provider.h
@@ -42,6 +42,14 @@ class MetricsProvider {
// further notification after this callback.
virtual void OnAppEnterBackground();
+ // Provides a complete and independent system profile + metrics for uploading.
+ // Any histograms added to the |snapshot_manager| will also be included. A
+ // return of false indicates there are none. Will be called repeatedly until
+ // there is nothing else.
+ virtual bool ProvideIndependentMetrics(
+ SystemProfileProto* system_profile_proto,
+ base::HistogramSnapshotManager* snapshot_manager);
+
// Provides additional metrics into the system profile.
virtual void ProvideSystemProfileMetrics(
SystemProfileProto* system_profile_proto);
diff --git a/chromium/components/metrics/metrics_service.cc b/chromium/components/metrics/metrics_service.cc
index 529af17d829..fae8af3ee6e 100644
--- a/chromium/components/metrics/metrics_service.cc
+++ b/chromium/components/metrics/metrics_service.cc
@@ -140,11 +140,13 @@
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/statistics_recorder.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/string_piece.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/tracked_objects.h"
#include "build/build_config.h"
#include "components/metrics/environment_recorder.h"
+#include "components/metrics/field_trials_provider.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_log_manager.h"
#include "components/metrics/metrics_log_uploader.h"
@@ -162,11 +164,6 @@ namespace metrics {
namespace {
-// This drops records if the number of events (user action and omnibox) exceeds
-// the kEventLimit.
-const base::Feature kUMAThrottleEvents{"UMAThrottleEvents",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
// The delay, in seconds, after starting recording before doing expensive
// initialization work.
#if defined(OS_ANDROID) || defined(OS_IOS)
@@ -179,9 +176,6 @@ const int kInitializationDelaySeconds = 5;
const int kInitializationDelaySeconds = 30;
#endif
-// The maximum number of events in a log uploaded to the UMA server.
-const int kEventLimit = 2400;
-
#if defined(OS_ANDROID) || defined(OS_IOS)
void MarkAppCleanShutdownAndCommit(CleanExitBeacon* clean_exit_beacon,
PrefService* local_state) {
@@ -223,7 +217,6 @@ MetricsService::MetricsService(MetricsStateManager* state_manager,
state_manager_(state_manager),
client_(client),
local_state_(local_state),
- clean_exit_beacon_(client->GetRegistryBackupKey(), local_state),
recording_state_(UNSET),
test_mode_active_(false),
state_(INITIALIZED),
@@ -240,8 +233,11 @@ MetricsService::MetricsService(MetricsStateManager* state_manager,
if (install_date == 0)
local_state_->SetInt64(prefs::kInstallDate, base::Time::Now().ToTimeT());
- RegisterMetricsProvider(std::unique_ptr<metrics::MetricsProvider>(
- new StabilityMetricsProvider(local_state_)));
+ RegisterMetricsProvider(
+ base::MakeUnique<StabilityMetricsProvider>(local_state_));
+
+ RegisterMetricsProvider(base::MakeUnique<variations::FieldTrialsProvider>(
+ &synthetic_trial_registry_, base::StringPiece()));
}
MetricsService::~MetricsService() {
@@ -309,7 +305,7 @@ int64_t MetricsService::GetMetricsReportingEnabledDate() {
}
bool MetricsService::WasLastShutdownClean() const {
- return clean_exit_beacon_.exited_cleanly();
+ return state_manager_->clean_exit_beacon()->exited_cleanly();
}
void MetricsService::EnableRecording() {
@@ -412,7 +408,8 @@ void MetricsService::OnAppEnterBackground() {
rotation_scheduler_->Stop();
reporting_service_.Stop();
- MarkAppCleanShutdownAndCommit(&clean_exit_beacon_, local_state_);
+ MarkAppCleanShutdownAndCommit(state_manager_->clean_exit_beacon(),
+ local_state_);
// Give providers a chance to persist histograms as part of being
// backgrounded.
@@ -433,13 +430,13 @@ void MetricsService::OnAppEnterBackground() {
}
void MetricsService::OnAppEnterForeground() {
- clean_exit_beacon_.WriteBeaconValue(false);
+ state_manager_->clean_exit_beacon()->WriteBeaconValue(false);
ExecutionPhaseManager(local_state_).OnAppEnterForeground();
StartSchedulerIfNecessary();
}
#else
void MetricsService::LogNeedForCleanShutdown() {
- clean_exit_beacon_.WriteBeaconValue(false);
+ state_manager_->clean_exit_beacon()->WriteBeaconValue(false);
// Redundant setting to be sure we call for a clean shutdown.
clean_shutdown_status_ = NEED_TO_SHUTDOWN;
}
@@ -501,11 +498,11 @@ void MetricsService::InitializeMetricsState() {
session_id_ = local_state_->GetInteger(prefs::kMetricsSessionID);
StabilityMetricsProvider provider(local_state_);
- if (!clean_exit_beacon_.exited_cleanly()) {
+ if (!state_manager_->clean_exit_beacon()->exited_cleanly()) {
provider.LogCrash();
// Reset flag, and wait until we call LogNeedForCleanShutdown() before
// monitoring.
- clean_exit_beacon_.WriteBeaconValue(true);
+ state_manager_->clean_exit_beacon()->WriteBeaconValue(true);
ExecutionPhaseManager manager(local_state_);
UMA_HISTOGRAM_SPARSE_SLOWLY("Chrome.Browser.CrashedExecutionPhase",
static_cast<int>(manager.GetExecutionPhase()));
@@ -516,7 +513,7 @@ void MetricsService::InitializeMetricsState() {
// bypassed.
const bool is_initial_stability_log_required =
ProvidersHaveInitialStabilityMetrics() ||
- !clean_exit_beacon_.exited_cleanly();
+ !state_manager_->clean_exit_beacon()->exited_cleanly();
bool has_initial_stability_log = false;
if (is_initial_stability_log_required) {
// If the previous session didn't exit cleanly, or if any provider
@@ -633,6 +630,12 @@ void MetricsService::OpenNewLog() {
FROM_HERE, base::Bind(&MetricsService::StartInitTask,
self_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(kInitializationDelaySeconds));
+
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MetricsService::PrepareProviderMetricsTask,
+ self_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(2 * kInitializationDelaySeconds));
}
}
@@ -646,16 +649,6 @@ void MetricsService::CloseCurrentLog() {
if (!log_manager_.current_log())
return;
- // TODO(rkaplow): Evaluate if this is needed.
- if (log_manager_.current_log()->num_events() > kEventLimit) {
- UMA_HISTOGRAM_COUNTS("UMA.Discarded Log Events",
- log_manager_.current_log()->num_events());
- if (base::FeatureList::IsEnabled(kUMAThrottleEvents)) {
- log_manager_.DiscardCurrentLog();
- OpenNewLog(); // Start trivial log to hold our histograms.
- }
- }
-
// If a persistent allocator is in use, update its internal histograms (such
// as how much memory is being used) before reporting.
base::PersistentHistogramAllocator* allocator =
@@ -678,6 +671,7 @@ void MetricsService::CloseCurrentLog() {
current_log->RecordGeneralMetrics(metrics_providers_);
RecordCurrentHistograms();
+ current_log->TruncateEvents();
DVLOG(1) << "Generated an ongoing log.";
log_manager_.FinishCurrentLog(log_store());
}
@@ -856,96 +850,14 @@ bool MetricsService::UmaMetricsProperlyShutdown() {
return clean_shutdown_status_ == CLEANLY_SHUTDOWN;
}
-void MetricsService::AddSyntheticTrialObserver(
- variations::SyntheticTrialObserver* observer) {
- synthetic_trial_observer_list_.AddObserver(observer);
- if (!synthetic_trial_groups_.empty())
- observer->OnSyntheticTrialsChanged(synthetic_trial_groups_);
-}
-
-void MetricsService::RemoveSyntheticTrialObserver(
- variations::SyntheticTrialObserver* observer) {
- synthetic_trial_observer_list_.RemoveObserver(observer);
-}
-
-void MetricsService::RegisterSyntheticFieldTrial(
- const variations::SyntheticTrialGroup& trial) {
- for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
- if (synthetic_trial_groups_[i].id.name == trial.id.name) {
- if (synthetic_trial_groups_[i].id.group != trial.id.group) {
- synthetic_trial_groups_[i].id.group = trial.id.group;
- synthetic_trial_groups_[i].start_time = base::TimeTicks::Now();
- NotifySyntheticTrialObservers();
- }
- return;
- }
- }
-
- variations::SyntheticTrialGroup trial_group = trial;
- trial_group.start_time = base::TimeTicks::Now();
- synthetic_trial_groups_.push_back(trial_group);
- NotifySyntheticTrialObservers();
-}
-
-void MetricsService::RegisterSyntheticMultiGroupFieldTrial(
- uint32_t trial_name_hash,
- const std::vector<uint32_t>& group_name_hashes) {
- auto has_same_trial_name =
- [trial_name_hash](const variations::SyntheticTrialGroup& x) {
- return x.id.name == trial_name_hash;
- };
- synthetic_trial_groups_.erase(
- std::remove_if(synthetic_trial_groups_.begin(),
- synthetic_trial_groups_.end(), has_same_trial_name),
- synthetic_trial_groups_.end());
-
- if (group_name_hashes.empty())
- return;
-
- variations::SyntheticTrialGroup trial_group(trial_name_hash,
- group_name_hashes[0]);
- trial_group.start_time = base::TimeTicks::Now();
- for (uint32_t group_name_hash : group_name_hashes) {
- // Note: Adding the trial group will copy it, so this re-uses the same
- // |trial_group| struct for convenience (e.g. so start_time's all match).
- trial_group.id.group = group_name_hash;
- synthetic_trial_groups_.push_back(trial_group);
- }
- NotifySyntheticTrialObservers();
-}
-
-void MetricsService::GetCurrentSyntheticFieldTrialsForTesting(
- std::vector<variations::ActiveGroupId>* synthetic_trials) {
- GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(), synthetic_trials);
-}
-
void MetricsService::RegisterMetricsProvider(
std::unique_ptr<MetricsProvider> provider) {
DCHECK_EQ(INITIALIZED, state_);
metrics_providers_.push_back(std::move(provider));
}
-void MetricsService::CheckForClonedInstall(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
- state_manager_->CheckForClonedInstall(task_runner);
-}
-
-void MetricsService::NotifySyntheticTrialObservers() {
- for (variations::SyntheticTrialObserver& observer :
- synthetic_trial_observer_list_) {
- observer.OnSyntheticTrialsChanged(synthetic_trial_groups_);
- }
-}
-
-void MetricsService::GetSyntheticFieldTrialsOlderThan(
- base::TimeTicks time,
- std::vector<variations::ActiveGroupId>* synthetic_trials) {
- DCHECK(synthetic_trials);
- synthetic_trials->clear();
- for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
- if (synthetic_trial_groups_[i].start_time <= time)
- synthetic_trials->push_back(synthetic_trial_groups_[i].id);
- }
+void MetricsService::CheckForClonedInstall() {
+ state_manager_->CheckForClonedInstall();
}
std::unique_ptr<MetricsLog> MetricsService::CreateLog(
@@ -956,11 +868,8 @@ std::unique_ptr<MetricsLog> MetricsService::CreateLog(
void MetricsService::RecordCurrentEnvironment(MetricsLog* log) {
DCHECK(client_);
- std::vector<variations::ActiveGroupId> synthetic_trials;
- GetSyntheticFieldTrialsOlderThan(log->creation_time(), &synthetic_trials);
std::string serialized_environment = log->RecordEnvironment(
- metrics_providers_, synthetic_trials, GetInstallDate(),
- GetMetricsReportingEnabledDate());
+ metrics_providers_, GetInstallDate(), GetMetricsReportingEnabledDate());
client_->OnEnvironmentUpdate(&serialized_environment);
}
@@ -988,13 +897,44 @@ void MetricsService::RecordCurrentStabilityHistograms() {
provider->RecordInitialHistogramSnapshots(&histogram_snapshot_manager_);
}
+bool MetricsService::PrepareProviderMetricsLog() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Create a new log. This will have some defaut values injected in it but
+ // those will be overwritten when an embedded profile is extracted.
+ std::unique_ptr<MetricsLog> log = CreateLog(MetricsLog::INDEPENDENT_LOG);
+
+ for (auto& provider : metrics_providers_) {
+ if (log->LoadIndependentMetrics(provider.get())) {
+ log_manager_.PauseCurrentLog();
+ log_manager_.BeginLoggingWithLog(std::move(log));
+ log_manager_.FinishCurrentLog(log_store());
+ log_manager_.ResumePausedLog();
+ return true;
+ }
+ }
+ return false;
+}
+
+void MetricsService::PrepareProviderMetricsTask() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ bool found = PrepareProviderMetricsLog();
+ base::TimeDelta next_check = found ? base::TimeDelta::FromSeconds(5)
+ : base::TimeDelta::FromMinutes(15);
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MetricsService::PrepareProviderMetricsTask,
+ self_ptr_factory_.GetWeakPtr()),
+ next_check);
+}
+
void MetricsService::LogCleanShutdown(bool end_completed) {
DCHECK(thread_checker_.CalledOnValidThread());
// Redundant setting to assure that we always reset this value at shutdown
// (and that we don't use some alternate path, and not call LogCleanShutdown).
clean_shutdown_status_ = CLEANLY_SHUTDOWN;
client_->OnLogCleanShutdown();
- clean_exit_beacon_.WriteBeaconValue(true);
+ state_manager_->clean_exit_beacon()->WriteBeaconValue(true);
SetExecutionPhase(ExecutionPhase::SHUTDOWN_COMPLETE, local_state_);
StabilityMetricsProvider(local_state_).MarkSessionEndCompleted(end_completed);
}
diff --git a/chromium/components/metrics/metrics_service.h b/chromium/components/metrics/metrics_service.h
index f1e6349eb2e..4bf850b42b5 100644
--- a/chromium/components/metrics/metrics_service.h
+++ b/chromium/components/metrics/metrics_service.h
@@ -22,8 +22,6 @@
#include "base/metrics/histogram_flattener.h"
#include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/user_metrics.h"
-#include "base/observer_list.h"
-#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -35,7 +33,7 @@
#include "components/metrics/metrics_provider.h"
#include "components/metrics/metrics_reporting_service.h"
#include "components/metrics/net/network_metrics_provider.h"
-#include "components/variations/synthetic_trials.h"
+#include "components/variations/synthetic_trial_registry.h"
class PrefService;
class PrefRegistrySimple;
@@ -45,14 +43,9 @@ class HistogramSamples;
class PrefService;
}
-namespace variations {
-struct ActiveGroupId;
-}
-
namespace metrics {
class MetricsRotationScheduler;
-class MetricsServiceAccessor;
class MetricsServiceClient;
class MetricsStateManager;
@@ -163,18 +156,6 @@ class MetricsService : public base::HistogramFlattener {
// This value should be true when process has completed shutdown.
static bool UmaMetricsProperlyShutdown();
- // Public accessor that returns the list of synthetic field trials. It must
- // only be used for testing.
- void GetCurrentSyntheticFieldTrialsForTesting(
- std::vector<variations::ActiveGroupId>* synthetic_trials);
-
- // Adds an observer to be notified when the synthetic trials list changes.
- void AddSyntheticTrialObserver(variations::SyntheticTrialObserver* observer);
-
- // Removes an existing observer of synthetic trials list changes.
- void RemoveSyntheticTrialObserver(
- variations::SyntheticTrialObserver* observer);
-
// Register the specified |provider| to provide additional metrics into the
// UMA log. Should be called during MetricsService initialization only.
void RegisterMetricsProvider(std::unique_ptr<MetricsProvider> provider);
@@ -182,8 +163,7 @@ class MetricsService : public base::HistogramFlattener {
// Check if this install was cloned or imaged from another machine. If a
// clone is detected, reset the client id and low entropy source. This
// should not be called more than once.
- void CheckForClonedInstall(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ void CheckForClonedInstall();
// Clears the stability metrics that are saved in local state.
void ClearSavedStabilityMetrics();
@@ -196,6 +176,10 @@ class MetricsService : public base::HistogramFlattener {
int message_size,
bool is_cellular);
+ variations::SyntheticTrialRegistry* synthetic_trial_registry() {
+ return &synthetic_trial_registry_;
+ }
+
protected:
// Exposed for testing.
MetricsLogManager* log_manager() { return &log_manager_; }
@@ -204,8 +188,6 @@ class MetricsService : public base::HistogramFlattener {
}
private:
- friend class MetricsServiceAccessor;
-
// The MetricsService has a lifecycle that is stored as a state.
// See metrics_service.cc for description of this lifecycle.
enum State {
@@ -229,27 +211,6 @@ class MetricsService : public base::HistogramFlattener {
UNSET
};
- typedef std::vector<variations::SyntheticTrialGroup> SyntheticTrialGroups;
-
- // Registers a field trial name and group to be used to annotate a UMA report
- // with a particular Chrome configuration state. A UMA report will be
- // annotated with this trial group if and only if all events in the report
- // were created after the trial is registered. Only one group name may be
- // registered at a time for a given trial_name. Only the last group name that
- // is registered for a given trial name will be recorded. The values passed
- // in must not correspond to any real field trial in the code.
- // Note: Should not be used to replace trials that were registered with
- // RegisterMultiGroupSyntheticFieldTrial().
- void RegisterSyntheticFieldTrial(
- const variations::SyntheticTrialGroup& trial_group);
-
- // Similar to RegisterSyntheticFieldTrial(), but registers a synthetic trial
- // that has multiple active groups for a given trial name hash. Any previous
- // groups registered for |trial_name_hash| will be replaced.
- void RegisterSyntheticMultiGroupFieldTrial(
- uint32_t trial_name_hash,
- const std::vector<uint32_t>& group_name_hashes);
-
// Calls into the client to initialize some system profile metrics.
void StartInitTask();
@@ -331,14 +292,6 @@ class MetricsService : public base::HistogramFlattener {
// Records that the browser was shut down cleanly.
void LogCleanShutdown(bool end_completed);
- // Notifies observers on a synthetic trial list change.
- void NotifySyntheticTrialObservers();
-
- // Returns a list of synthetic field trials that are older than |time|.
- void GetSyntheticFieldTrialsOlderThan(
- base::TimeTicks time,
- std::vector<variations::ActiveGroupId>* synthetic_trials);
-
// Creates a new MetricsLog instance with the given |log_type|.
std::unique_ptr<MetricsLog> CreateLog(MetricsLog::LogType log_type);
@@ -353,6 +306,15 @@ class MetricsService : public base::HistogramFlattener {
// i.e., histograms with the |kUmaStabilityHistogramFlag| flag set.
void RecordCurrentStabilityHistograms();
+ // Record a single independent profile and assocatied histogram from
+ // metrics providers. If this returns true, one was found and there may
+ // be more.
+ bool PrepareProviderMetricsLog();
+
+ // Records one independent histogram log and then reschedules itself to
+ // check for others. The interval is so as to not adversely impact the UI.
+ void PrepareProviderMetricsTask();
+
// Sub-service for uploading logs.
MetricsReportingService reporting_service_;
@@ -375,8 +337,6 @@ class MetricsService : public base::HistogramFlattener {
PrefService* local_state_;
- CleanExitBeacon clean_exit_beacon_;
-
base::ActionCallback action_callback_;
// Indicate whether recording and reporting are currently happening.
@@ -413,12 +373,7 @@ class MetricsService : public base::HistogramFlattener {
// Stores the time of the last call to |GetUptimes()|.
base::TimeTicks last_updated_time_;
- // Field trial groups that map to Chrome configuration states.
- SyntheticTrialGroups synthetic_trial_groups_;
-
- // List of observers of |synthetic_trial_groups_| changes.
- base::ObserverList<variations::SyntheticTrialObserver>
- synthetic_trial_observer_list_;
+ variations::SyntheticTrialRegistry synthetic_trial_registry_;
// Redundant marker to check that we completed our shutdown, and set the
// exited-cleanly bit in the prefs.
@@ -427,9 +382,6 @@ class MetricsService : public base::HistogramFlattener {
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, IsPluginProcess);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
PermutedEntropyCacheClearedWhenLowEntropyReset);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
- RegisterSyntheticMultiGroupFieldTrial);
base::ThreadChecker thread_checker_;
diff --git a/chromium/components/metrics/metrics_service_accessor.cc b/chromium/components/metrics/metrics_service_accessor.cc
index 29b67bd9118..467210af09c 100644
--- a/chromium/components/metrics/metrics_service_accessor.cc
+++ b/chromium/components/metrics/metrics_service_accessor.cc
@@ -46,8 +46,9 @@ bool MetricsServiceAccessor::RegisterSyntheticMultiGroupFieldTrial(
if (!metrics_service)
return false;
- metrics_service->RegisterSyntheticMultiGroupFieldTrial(HashName(trial_name),
- group_name_hashes);
+ metrics_service->synthetic_trial_registry()
+ ->RegisterSyntheticMultiGroupFieldTrial(HashName(trial_name),
+ group_name_hashes);
return true;
}
@@ -69,7 +70,8 @@ bool MetricsServiceAccessor::RegisterSyntheticFieldTrialWithNameAndGroupHash(
return false;
variations::SyntheticTrialGroup trial_group(trial_name_hash, group_name_hash);
- metrics_service->RegisterSyntheticFieldTrial(trial_group);
+ metrics_service->synthetic_trial_registry()->RegisterSyntheticFieldTrial(
+ trial_group);
return true;
}
diff --git a/chromium/components/metrics/metrics_service_client.cc b/chromium/components/metrics/metrics_service_client.cc
index 8a204b3e190..701b15dc264 100644
--- a/chromium/components/metrics/metrics_service_client.cc
+++ b/chromium/components/metrics/metrics_service_client.cc
@@ -4,23 +4,10 @@
#include "components/metrics/metrics_service_client.h"
-#include "base/feature_list.h"
#include "components/metrics/url_constants.h"
namespace metrics {
-namespace {
-
-#if defined(OS_ANDROID) || defined(OS_IOS)
-const base::Feature kNewMetricsUrlFeature{"NewMetricsUrl",
- base::FEATURE_ENABLED_BY_DEFAULT};
-#else
-const base::Feature kNewMetricsUrlFeature{"NewMetricsUrl",
- base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
-} // namespace
-
MetricsServiceClient::MetricsServiceClient() : update_running_services_() {}
MetricsServiceClient::~MetricsServiceClient() {}
@@ -29,10 +16,6 @@ ukm::UkmService* MetricsServiceClient::GetUkmService() {
return nullptr;
}
-base::string16 MetricsServiceClient::GetRegistryBackupKey() {
- return base::string16();
-}
-
bool MetricsServiceClient::IsReportingPolicyManaged() {
return false;
}
@@ -46,9 +29,7 @@ bool MetricsServiceClient::IsUMACellularUploadLogicEnabled() {
}
std::string MetricsServiceClient::GetMetricsServerUrl() {
- return base::FeatureList::IsEnabled(kNewMetricsUrlFeature)
- ? kNewMetricsServerUrl
- : kOldMetricsServerUrl;
+ return kNewMetricsServerUrl;
}
bool MetricsServiceClient::IsHistorySyncEnabledOnAllProfiles() {
diff --git a/chromium/components/metrics/metrics_service_client.h b/chromium/components/metrics/metrics_service_client.h
index 569e121c636..6d2f6d4543d 100644
--- a/chromium/components/metrics/metrics_service_client.h
+++ b/chromium/components/metrics/metrics_service_client.h
@@ -102,10 +102,6 @@ class MetricsServiceClient {
// Returns the standard interval between upload attempts.
virtual base::TimeDelta GetStandardUploadInterval() = 0;
- // Returns the name of a key under HKEY_CURRENT_USER that can be used to store
- // backups of metrics data. Unused except on Windows.
- virtual base::string16 GetRegistryBackupKey();
-
// Called on plugin loading errors.
virtual void OnPluginLoadingError(const base::FilePath& plugin_path) {}
@@ -130,7 +126,6 @@ class MetricsServiceClient {
// Sets the callback to run MetricsServiceManager::UpdateRunningServices.
void SetUpdateRunningServicesCallback(const base::Closure& callback);
- protected:
// Notify MetricsServiceManager to UpdateRunningServices using callback.
void UpdateRunningServices();
diff --git a/chromium/components/metrics/metrics_service_unittest.cc b/chromium/components/metrics/metrics_service_unittest.cc
index c4ccac83619..6c1497326b9 100644
--- a/chromium/components/metrics/metrics_service_unittest.cc
+++ b/chromium/components/metrics/metrics_service_unittest.cc
@@ -6,6 +6,7 @@
#include <stdint.h>
+#include <algorithm>
#include <memory>
#include <string>
@@ -15,6 +16,8 @@
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/statistics_recorder.h"
#include "base/metrics/user_metrics.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/platform_thread.h"
@@ -29,7 +32,6 @@
#include "components/metrics/test_metrics_provider.h"
#include "components/metrics/test_metrics_service_client.h"
#include "components/prefs/testing_pref_service.h"
-#include "components/variations/metrics_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/compression_utils.h"
@@ -85,9 +87,6 @@ class MetricsServiceTest : public testing::Test {
enabled_state_provider_(new TestEnabledStateProvider(false, false)) {
base::SetRecordActionTaskRunner(task_runner_);
MetricsService::RegisterPrefs(testing_local_state_.registry());
- metrics_state_manager_ = MetricsStateManager::Create(
- GetLocalState(), enabled_state_provider_.get(),
- base::Bind(&StoreNoClientInfoBackup), base::Bind(&ReturnNoBackup));
}
~MetricsServiceTest() override {
@@ -96,6 +95,13 @@ class MetricsServiceTest : public testing::Test {
}
MetricsStateManager* GetMetricsStateManager() {
+ // Lazy-initialize the metrics_state_manager so that it correctly reads the
+ // stability state from prefs after tests have a chance to initialize it.
+ if (!metrics_state_manager_) {
+ metrics_state_manager_ = MetricsStateManager::Create(
+ GetLocalState(), enabled_state_provider_.get(), base::string16(),
+ base::Bind(&StoreNoClientInfoBackup), base::Bind(&ReturnNoBackup));
+ }
return metrics_state_manager_.get();
}
@@ -107,29 +113,6 @@ class MetricsServiceTest : public testing::Test {
enabled_state_provider_->set_enabled(true);
}
- // Waits until base::TimeTicks::Now() no longer equals |value|. This should
- // take between 1-15ms per the documented resolution of base::TimeTicks.
- void WaitUntilTimeChanges(const base::TimeTicks& value) {
- while (base::TimeTicks::Now() == value) {
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
- }
- }
-
- // Returns true if there is a synthetic trial in the given vector that matches
- // the given trial name and trial group; returns false otherwise.
- bool HasSyntheticTrial(
- const std::vector<variations::ActiveGroupId>& synthetic_trials,
- const std::string& trial_name,
- const std::string& trial_group) {
- uint32_t trial_name_hash = HashName(trial_name);
- uint32_t trial_group_hash = HashName(trial_group);
- for (const variations::ActiveGroupId& trial : synthetic_trials) {
- if (trial.name == trial_name_hash && trial.group == trial_group_hash)
- return true;
- }
- return false;
- }
-
// Finds a histogram with the specified |name_hash| in |histograms|.
const base::HistogramBase* FindHistogram(
const base::StatisticsRecorder::Histograms& histograms,
@@ -208,8 +191,7 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAtProviderRequest) {
// saved from a previous session.
TestMetricsServiceClient client;
TestMetricsLog log("client", 1, &client, GetLocalState());
- log.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
- std::vector<variations::ActiveGroupId>(), 0, 0);
+ log.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(), 0, 0);
// Record stability build time and version from previous session, so that
// stability metrics (including exited cleanly flag) won't be cleared.
@@ -279,8 +261,7 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) {
// saved from a previous session.
TestMetricsServiceClient client;
TestMetricsLog log("client", 1, &client, GetLocalState());
- log.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
- std::vector<variations::ActiveGroupId>(), 0, 0);
+ log.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(), 0, 0);
// Record stability build time and version from previous session, so that
// stability metrics (including exited cleanly flag) won't be cleared.
@@ -335,119 +316,6 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) {
EXPECT_EQ(1, uma_log.system_profile().stability().crash_count());
}
-TEST_F(MetricsServiceTest, RegisterSyntheticTrial) {
- TestMetricsServiceClient client;
- MetricsService service(GetMetricsStateManager(), &client, GetLocalState());
-
- // Add two synthetic trials and confirm that they show up in the list.
- variations::SyntheticTrialGroup trial1(HashName("TestTrial1"),
- HashName("Group1"));
- service.RegisterSyntheticFieldTrial(trial1);
-
- variations::SyntheticTrialGroup trial2(HashName("TestTrial2"),
- HashName("Group2"));
- service.RegisterSyntheticFieldTrial(trial2);
- // Ensure that time has advanced by at least a tick before proceeding.
- WaitUntilTimeChanges(base::TimeTicks::Now());
-
- service.log_manager_.BeginLoggingWithLog(std::unique_ptr<MetricsLog>(
- new MetricsLog("clientID", 1, MetricsLog::INITIAL_STABILITY_LOG, &client,
- GetLocalState())));
- // Save the time when the log was started (it's okay for this to be greater
- // than the time recorded by the above call since it's used to ensure the
- // value changes).
- const base::TimeTicks begin_log_time = base::TimeTicks::Now();
-
- std::vector<variations::ActiveGroupId> synthetic_trials;
- service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
- &synthetic_trials);
- EXPECT_EQ(2U, synthetic_trials.size());
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group1"));
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
-
- // Ensure that time has advanced by at least a tick before proceeding.
- WaitUntilTimeChanges(begin_log_time);
-
- // Change the group for the first trial after the log started.
- variations::SyntheticTrialGroup trial3(HashName("TestTrial1"),
- HashName("Group2"));
- service.RegisterSyntheticFieldTrial(trial3);
- service.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
- EXPECT_EQ(1U, synthetic_trials.size());
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
-
- // Add a new trial after the log started and confirm that it doesn't show up.
- variations::SyntheticTrialGroup trial4(HashName("TestTrial3"),
- HashName("Group3"));
- service.RegisterSyntheticFieldTrial(trial4);
- service.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
- EXPECT_EQ(1U, synthetic_trials.size());
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
-
- // Ensure that time has advanced by at least a tick before proceeding.
- WaitUntilTimeChanges(base::TimeTicks::Now());
-
- // Start a new log and ensure all three trials appear in it.
- service.log_manager_.FinishCurrentLog(service.log_store());
- service.log_manager_.BeginLoggingWithLog(
- std::unique_ptr<MetricsLog>(new MetricsLog(
- "clientID", 1, MetricsLog::ONGOING_LOG, &client, GetLocalState())));
- service.GetSyntheticFieldTrialsOlderThan(
- service.log_manager_.current_log()->creation_time(), &synthetic_trials);
- EXPECT_EQ(3U, synthetic_trials.size());
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group2"));
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial3", "Group3"));
- service.log_manager_.FinishCurrentLog(service.log_store());
-}
-
-TEST_F(MetricsServiceTest, RegisterSyntheticMultiGroupFieldTrial) {
- TestMetricsServiceClient client;
- MetricsService service(GetMetricsStateManager(), &client, GetLocalState());
-
- // Register a synthetic trial TestTrial1 with groups A and B.
- uint32_t trial_name_hash = HashName("TestTrial1");
- std::vector<uint32_t> group_name_hashes = {HashName("A"), HashName("B")};
- service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
- group_name_hashes);
- // Ensure that time has advanced by at least a tick before proceeding.
- WaitUntilTimeChanges(base::TimeTicks::Now());
-
- service.log_manager_.BeginLoggingWithLog(std::unique_ptr<MetricsLog>(
- new MetricsLog("clientID", 1, MetricsLog::INITIAL_STABILITY_LOG, &client,
- GetLocalState())));
-
- std::vector<variations::ActiveGroupId> synthetic_trials;
- service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
- &synthetic_trials);
- EXPECT_EQ(2U, synthetic_trials.size());
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "A"));
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "B"));
-
- // Change the group for the trial to a single group.
- group_name_hashes = {HashName("X")};
- service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
- group_name_hashes);
- // Ensure that time has advanced by at least a tick before proceeding.
- WaitUntilTimeChanges(base::TimeTicks::Now());
-
- service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
- &synthetic_trials);
- EXPECT_EQ(1U, synthetic_trials.size());
- EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "X"));
-
- // Register a trial with no groups, which should effectively remove the trial.
- group_name_hashes.clear();
- service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
- group_name_hashes);
- // Ensure that time has advanced by at least a tick before proceeding.
- WaitUntilTimeChanges(base::TimeTicks::Now());
-
- service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
- &synthetic_trials);
- service.log_manager_.FinishCurrentLog(service.log_store());
-}
-
TEST_F(MetricsServiceTest,
MetricsProviderOnRecordingDisabledCalledOnInitialStop) {
TestMetricsServiceClient client;
@@ -486,45 +354,46 @@ TEST_F(MetricsServiceTest, SplitRotation) {
service.Start();
// Rotation loop should create a log and mark state as idle.
// Upload loop should start upload or be restarted.
+ // The independent-metrics upload job will be started and always be a task.
task_runner_->RunPendingTasks();
// Rotation loop should terminated due to being idle.
// Upload loop should start uploading if it isn't already.
task_runner_->RunPendingTasks();
EXPECT_TRUE(client.uploader()->is_uploading());
- EXPECT_EQ(0U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(1U, task_runner_->NumPendingTasks());
service.OnApplicationNotIdle();
EXPECT_TRUE(client.uploader()->is_uploading());
- EXPECT_EQ(1U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(2U, task_runner_->NumPendingTasks());
// Log generation should be suppressed due to unsent log.
// Idle state should not be reset.
task_runner_->RunPendingTasks();
EXPECT_TRUE(client.uploader()->is_uploading());
- EXPECT_EQ(1U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(2U, task_runner_->NumPendingTasks());
// Make sure idle state was not reset.
task_runner_->RunPendingTasks();
EXPECT_TRUE(client.uploader()->is_uploading());
- EXPECT_EQ(1U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(2U, task_runner_->NumPendingTasks());
// Upload should not be rescheduled, since there are no other logs.
client.uploader()->CompleteUpload(200);
EXPECT_FALSE(client.uploader()->is_uploading());
- EXPECT_EQ(1U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(2U, task_runner_->NumPendingTasks());
// Running should generate a log, restart upload loop, and mark idle.
task_runner_->RunPendingTasks();
EXPECT_FALSE(client.uploader()->is_uploading());
- EXPECT_EQ(2U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(3U, task_runner_->NumPendingTasks());
// Upload should start, and rotation loop should idle out.
task_runner_->RunPendingTasks();
EXPECT_TRUE(client.uploader()->is_uploading());
- EXPECT_EQ(0U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(1U, task_runner_->NumPendingTasks());
// Uploader should reschedule when there is another log available.
service.PushExternalLog("Blah");
client.uploader()->CompleteUpload(200);
EXPECT_FALSE(client.uploader()->is_uploading());
- EXPECT_EQ(1U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(2U, task_runner_->NumPendingTasks());
// Upload should start.
task_runner_->RunPendingTasks();
EXPECT_TRUE(client.uploader()->is_uploading());
- EXPECT_EQ(0U, task_runner_->NumPendingTasks());
+ EXPECT_EQ(1U, task_runner_->NumPendingTasks());
}
} // namespace metrics
diff --git a/chromium/components/metrics/metrics_state_manager.cc b/chromium/components/metrics/metrics_state_manager.cc
index 83d62b5f4f8..45c2c48e20a 100644
--- a/chromium/components/metrics/metrics_state_manager.cc
+++ b/chromium/components/metrics/metrics_state_manager.cc
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/guid.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/rand_util.h"
@@ -57,12 +58,14 @@ bool MetricsStateManager::instance_exists_ = false;
MetricsStateManager::MetricsStateManager(
PrefService* local_state,
EnabledStateProvider* enabled_state_provider,
+ const base::string16& backup_registry_key,
const StoreClientInfoCallback& store_client_info,
const LoadClientInfoCallback& retrieve_client_info)
: local_state_(local_state),
enabled_state_provider_(enabled_state_provider),
store_client_info_(store_client_info),
load_client_info_(retrieve_client_info),
+ clean_exit_beacon_(backup_registry_key, local_state),
low_entropy_source_(kLowEntropySourceNotSet),
entropy_source_returned_(ENTROPY_SOURCE_NONE) {
ResetMetricsIDsIfNecessary();
@@ -140,16 +143,14 @@ void MetricsStateManager::ForceClientIdCreation() {
BackUpCurrentClientInfo();
}
-void MetricsStateManager::CheckForClonedInstall(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+void MetricsStateManager::CheckForClonedInstall() {
DCHECK(!cloned_install_detector_);
- MachineIdProvider* provider = MachineIdProvider::CreateInstance();
- if (!provider)
+ if (!MachineIdProvider::HasId())
return;
- cloned_install_detector_.reset(new ClonedInstallDetector(provider));
- cloned_install_detector_->CheckForClonedInstall(local_state_, task_runner);
+ cloned_install_detector_ = base::MakeUnique<ClonedInstallDetector>();
+ cloned_install_detector_->CheckForClonedInstall(local_state_);
}
std::unique_ptr<const base::FieldTrial::EntropyProvider>
@@ -194,13 +195,14 @@ MetricsStateManager::CreateLowEntropyProvider() {
std::unique_ptr<MetricsStateManager> MetricsStateManager::Create(
PrefService* local_state,
EnabledStateProvider* enabled_state_provider,
+ const base::string16& backup_registry_key,
const StoreClientInfoCallback& store_client_info,
const LoadClientInfoCallback& retrieve_client_info) {
std::unique_ptr<MetricsStateManager> result;
// Note: |instance_exists_| is updated in the constructor and destructor.
if (!instance_exists_) {
result.reset(new MetricsStateManager(local_state, enabled_state_provider,
- store_client_info,
+ backup_registry_key, store_client_info,
retrieve_client_info));
}
return result;
diff --git a/chromium/components/metrics/metrics_state_manager.h b/chromium/components/metrics/metrics_state_manager.h
index 99c0f628d66..fbb0f595d6e 100644
--- a/chromium/components/metrics/metrics_state_manager.h
+++ b/chromium/components/metrics/metrics_state_manager.h
@@ -12,7 +12,8 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
-#include "base/single_thread_task_runner.h"
+#include "base/strings/string16.h"
+#include "components/metrics/clean_exit_beacon.h"
#include "components/metrics/client_info.h"
class PrefService;
@@ -49,6 +50,13 @@ class MetricsStateManager {
// not opted in to metrics reporting.
const std::string& client_id() const { return client_id_; }
+ // The CleanExitBeacon, used to determine whether the previous Chrome browser
+ // session terminated gracefully.
+ CleanExitBeacon* clean_exit_beacon() { return &clean_exit_beacon_; }
+ const CleanExitBeacon* clean_exit_beacon() const {
+ return &clean_exit_beacon_;
+ }
+
// Forces the client ID to be generated. This is useful in case it's needed
// before recording.
void ForceClientIdCreation();
@@ -56,8 +64,7 @@ class MetricsStateManager {
// Checks if this install was cloned or imaged from another machine. If a
// clone is detected, resets the client id and low entropy source. This
// should not be called more than once.
- void CheckForClonedInstall(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ void CheckForClonedInstall();
// Returns the preferred entropy provider used to seed persistent activities
// based on whether or not metrics reporting is permitted on this client.
@@ -78,9 +85,12 @@ class MetricsStateManager {
// Creates the MetricsStateManager, enforcing that only a single instance
// of the class exists at a time. Returns NULL if an instance exists already.
+ // On Windows, |backup_registry_key| is used to store a backup of the clean
+ // exit beacon. It is ignored on other platforms.
static std::unique_ptr<MetricsStateManager> Create(
PrefService* local_state,
EnabledStateProvider* enabled_state_provider,
+ const base::string16& backup_registry_key,
const StoreClientInfoCallback& store_client_info,
const LoadClientInfoCallback& load_client_info);
@@ -113,6 +123,7 @@ class MetricsStateManager {
// that it is later retrievable by |load_client_info|.
MetricsStateManager(PrefService* local_state,
EnabledStateProvider* enabled_state_provider,
+ const base::string16& backup_registry_key,
const StoreClientInfoCallback& store_client_info,
const LoadClientInfoCallback& load_client_info);
@@ -165,6 +176,10 @@ class MetricsStateManager {
// its typical location and wants to attempt loading it from this backup.
const LoadClientInfoCallback load_client_info_;
+ // A beacon used to determine whether the previous Chrome browser session
+ // terminated gracefully.
+ CleanExitBeacon clean_exit_beacon_;
+
// The identifier that's sent to the server with the log reports.
std::string client_id_;
diff --git a/chromium/components/metrics/metrics_state_manager_unittest.cc b/chromium/components/metrics/metrics_state_manager_unittest.cc
index a55c2fa9f03..801f74a2067 100644
--- a/chromium/components/metrics/metrics_state_manager_unittest.cc
+++ b/chromium/components/metrics/metrics_state_manager_unittest.cc
@@ -13,6 +13,7 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
+#include "base/strings/string16.h"
#include "components/metrics/client_info.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_service.h"
@@ -35,7 +36,7 @@ class MetricsStateManagerTest : public testing::Test {
std::unique_ptr<MetricsStateManager> CreateStateManager() {
return MetricsStateManager::Create(
- &prefs_, enabled_state_provider_.get(),
+ &prefs_, enabled_state_provider_.get(), base::string16(),
base::Bind(&MetricsStateManagerTest::MockStoreClientInfoBackup,
base::Unretained(this)),
base::Bind(&MetricsStateManagerTest::LoadFakeClientInfoBackup,
diff --git a/chromium/components/metrics/metrics_switches.cc b/chromium/components/metrics/metrics_switches.cc
index 1aef619d6a4..f380ff8e7ce 100644
--- a/chromium/components/metrics/metrics_switches.cc
+++ b/chromium/components/metrics/metrics_switches.cc
@@ -7,6 +7,13 @@
namespace metrics {
namespace switches {
+// Enables the recording of metrics reports but disables reporting. In contrast
+// to kDisableMetrics, this executes all the code that a normal client would
+// use for reporting, except the report is dropped rather than sent to the
+// server. This is useful for finding issues in the metrics code during UI and
+// performance tests.
+const char kMetricsRecordingOnly[] = "metrics-recording-only";
+
// Forces a reset of the one-time-randomized FieldTrials on this client, also
// known as the Chrome Variations state.
const char kResetVariationState[] = "reset-variation-state";
diff --git a/chromium/components/metrics/metrics_switches.h b/chromium/components/metrics/metrics_switches.h
index b3fe7fd9c03..bfdf4143afd 100644
--- a/chromium/components/metrics/metrics_switches.h
+++ b/chromium/components/metrics/metrics_switches.h
@@ -10,6 +10,8 @@ namespace switches {
// Alphabetical list of switches specific to the metrics component. Document
// each in the .cc file.
+
+extern const char kMetricsRecordingOnly[];
extern const char kResetVariationState[];
} // namespace switches
diff --git a/chromium/components/metrics/net/network_metrics_provider.cc b/chromium/components/metrics/net/network_metrics_provider.cc
index cd2f4f5b0f3..df7a8752f31 100644
--- a/chromium/components/metrics/net/network_metrics_provider.cc
+++ b/chromium/components/metrics/net/network_metrics_provider.cc
@@ -17,10 +17,11 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "net/base/net_errors.h"
+#include "net/nqe/effective_connection_type_observer.h"
#include "net/nqe/network_quality_estimator.h"
#if defined(OS_CHROMEOS)
@@ -41,11 +42,34 @@ void GetAndSetNetworkQualityEstimator(
network_quality_estimator_provider->GetNetworkQualityEstimator());
}
+SystemProfileProto::Network::EffectiveConnectionType
+ConvertEffectiveConnectionType(
+ net::EffectiveConnectionType effective_connection_type) {
+ switch (effective_connection_type) {
+ case net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN:
+ return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
+ case net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G:
+ return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
+ case net::EFFECTIVE_CONNECTION_TYPE_2G:
+ return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G;
+ case net::EFFECTIVE_CONNECTION_TYPE_3G:
+ return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_3G;
+ case net::EFFECTIVE_CONNECTION_TYPE_4G:
+ return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_4G;
+ case net::EFFECTIVE_CONNECTION_TYPE_OFFLINE:
+ case net::EFFECTIVE_CONNECTION_TYPE_LAST:
+ NOTREACHED();
+ return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
+ }
+ NOTREACHED();
+ return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
+}
+
} // namespace
// Listens to the changes in the effective conection type.
class NetworkMetricsProvider::EffectiveConnectionTypeObserver
- : public net::NetworkQualityEstimator::EffectiveConnectionTypeObserver {
+ : public net::EffectiveConnectionTypeObserver {
public:
// |network_quality_estimator| is used to provide the network quality
// estimates. Guaranteed to be non-null. |callback| is run on
@@ -100,15 +124,10 @@ class NetworkMetricsProvider::EffectiveConnectionTypeObserver
DISALLOW_COPY_AND_ASSIGN(EffectiveConnectionTypeObserver);
};
-NetworkMetricsProvider::NetworkMetricsProvider(base::TaskRunner* io_task_runner)
- : NetworkMetricsProvider(nullptr, io_task_runner) {}
-
NetworkMetricsProvider::NetworkMetricsProvider(
std::unique_ptr<NetworkQualityEstimatorProvider>
- network_quality_estimator_provider,
- base::TaskRunner* io_task_runner)
- : io_task_runner_(io_task_runner),
- connection_type_is_ambiguous_(false),
+ network_quality_estimator_provider)
+ : connection_type_is_ambiguous_(false),
wifi_phy_layer_protocol_is_ambiguous_(false),
wifi_phy_layer_protocol_(net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN),
total_aborts_(0),
@@ -116,7 +135,8 @@ NetworkMetricsProvider::NetworkMetricsProvider(
network_quality_estimator_provider_(
std::move(network_quality_estimator_provider)),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
- effective_connection_type_is_ambiguous_(false),
+ min_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
+ max_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
weak_ptr_factory_(this) {
net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
connection_type_ = net::NetworkChangeNotifier::GetConnectionType();
@@ -183,7 +203,11 @@ void NetworkMetricsProvider::ProvideSystemProfileMetrics(
network->set_wifi_phy_layer_protocol_is_ambiguous(
wifi_phy_layer_protocol_is_ambiguous_);
network->set_wifi_phy_layer_protocol(GetWifiPHYLayerProtocol());
- network->set_effective_connection_type(GetEffectiveConnectionType());
+
+ network->set_min_effective_connection_type(
+ ConvertEffectiveConnectionType(min_effective_connection_type_));
+ network->set_max_effective_connection_type(
+ ConvertEffectiveConnectionType(max_effective_connection_type_));
// Update the connection type. Note that this is necessary to set the network
// type to "none" if there is no network connection for an entire UMA logging
@@ -193,7 +217,8 @@ void NetworkMetricsProvider::ProvideSystemProfileMetrics(
// Reset the "ambiguous" flags, since a new metrics log session has started.
connection_type_is_ambiguous_ = false;
wifi_phy_layer_protocol_is_ambiguous_ = false;
- effective_connection_type_is_ambiguous_ = false;
+ min_effective_connection_type_ = effective_connection_type_;
+ max_effective_connection_type_ = effective_connection_type_;
if (!wifi_access_point_info_provider_.get()) {
#if defined(OS_CHROMEOS)
@@ -226,7 +251,6 @@ void NetworkMetricsProvider::OnConnectionTypeChanged(
if (type != connection_type_ &&
connection_type_ != net::NetworkChangeNotifier::CONNECTION_NONE) {
connection_type_is_ambiguous_ = true;
- effective_connection_type_is_ambiguous_ = true;
}
connection_type_ = type;
@@ -281,42 +305,15 @@ NetworkMetricsProvider::GetWifiPHYLayerProtocol() const {
return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
}
-SystemProfileProto::Network::EffectiveConnectionType
-NetworkMetricsProvider::GetEffectiveConnectionType() const {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (effective_connection_type_is_ambiguous_)
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_AMBIGUOUS;
-
- switch (effective_connection_type_) {
- case net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN:
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
- case net::EFFECTIVE_CONNECTION_TYPE_OFFLINE:
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_OFFLINE;
- case net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G:
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
- case net::EFFECTIVE_CONNECTION_TYPE_2G:
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G;
- case net::EFFECTIVE_CONNECTION_TYPE_3G:
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_3G;
- case net::EFFECTIVE_CONNECTION_TYPE_4G:
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_4G;
- case net::EFFECTIVE_CONNECTION_TYPE_LAST:
- NOTREACHED();
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
- }
- NOTREACHED();
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
-}
-
void NetworkMetricsProvider::ProbeWifiPHYLayerProtocol() {
DCHECK(thread_checker_.CalledOnValidThread());
- PostTaskAndReplyWithResult(
- io_task_runner_,
+ base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
- base::Bind(&net::GetWifiPHYLayerProtocol),
- base::Bind(&NetworkMetricsProvider::OnWifiPHYLayerProtocolResult,
- weak_ptr_factory_.GetWeakPtr()));
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+ base::BindOnce(&net::GetWifiPHYLayerProtocol),
+ base::BindOnce(&NetworkMetricsProvider::OnWifiPHYLayerProtocolResult,
+ weak_ptr_factory_.GetWeakPtr()));
}
void NetworkMetricsProvider::OnWifiPHYLayerProtocolResult(
@@ -418,7 +415,8 @@ void NetworkMetricsProvider::LogAggregatedMetrics() {
samples->GetCount(-net::ERR_ABORTED) - total_aborts_;
base::HistogramBase::Count new_codes = samples->TotalCount() - total_codes_;
if (new_codes > 0) {
- UMA_HISTOGRAM_COUNTS("Net.ErrAborted.CountPerUpload", new_aborts);
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.CountPerUpload2", new_aborts, 1,
+ 100000000, 50);
UMA_HISTOGRAM_PERCENTAGE("Net.ErrAborted.ProportionPerUpload",
(100 * new_aborts) / new_codes);
total_codes_ += new_codes;
@@ -429,12 +427,35 @@ void NetworkMetricsProvider::LogAggregatedMetrics() {
void NetworkMetricsProvider::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType type) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (effective_connection_type_ != type &&
- type != net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN &&
- effective_connection_type_ != net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
- effective_connection_type_is_ambiguous_ = true;
- }
effective_connection_type_ = type;
+
+ if (effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
+ effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
+ // The effective connection type may be reported as Unknown if there is a
+ // change in the connection type. Disregard it since network requests can't
+ // be send during the changes in connection type. Similarly, disregard
+ // offline as the type since it may be reported as the effective connection
+ // type for a short period when there is a change in the connection type.
+ return;
+ }
+
+ if (min_effective_connection_type_ ==
+ net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN &&
+ max_effective_connection_type_ ==
+ net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
+ min_effective_connection_type_ = type;
+ max_effective_connection_type_ = type;
+ return;
+ }
+
+ min_effective_connection_type_ =
+ std::min(min_effective_connection_type_, effective_connection_type_);
+ max_effective_connection_type_ =
+ std::max(max_effective_connection_type_, effective_connection_type_);
+
+ DCHECK_EQ(
+ min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
}
} // namespace metrics
diff --git a/chromium/components/metrics/net/network_metrics_provider.h b/chromium/components/metrics/net/network_metrics_provider.h
index afa488f6393..69d514a9f73 100644
--- a/chromium/components/metrics/net/network_metrics_provider.h
+++ b/chromium/components/metrics/net/network_metrics_provider.h
@@ -49,18 +49,12 @@ class NetworkMetricsProvider
DISALLOW_COPY_AND_ASSIGN(NetworkQualityEstimatorProvider);
};
- // Creates a NetworkMetricsProvider, where |io_task_runner| is used to post
- // network info collection tasks.
- explicit NetworkMetricsProvider(base::TaskRunner* io_task_runner);
-
- // Creates a NetworkMetricsProvider, where |io_task_runner| is used to post
- // network info collection tasks. |network_quality_estimator_provider|
- // should be set if it is useful to attach the quality of the network to the
- // metrics report.
- NetworkMetricsProvider(std::unique_ptr<NetworkQualityEstimatorProvider>
- network_quality_estimator_provider,
- base::TaskRunner* io_task_runner);
-
+ // Creates a NetworkMetricsProvider, where
+ // |network_quality_estimator_provider| should be set if it is useful to
+ // attach the quality of the network to the metrics report.
+ explicit NetworkMetricsProvider(
+ std::unique_ptr<NetworkQualityEstimatorProvider>
+ network_quality_estimator_provider = nullptr);
~NetworkMetricsProvider() override;
private:
@@ -82,8 +76,6 @@ class NetworkMetricsProvider
SystemProfileProto::Network::ConnectionType GetConnectionType() const;
SystemProfileProto::Network::WifiPHYLayerProtocol GetWifiPHYLayerProtocol()
const;
- SystemProfileProto::Network::EffectiveConnectionType
- GetEffectiveConnectionType() const;
// Posts a call to net::GetWifiPHYLayerProtocol on the blocking pool.
void ProbeWifiPHYLayerProtocol();
@@ -104,9 +96,6 @@ class NetworkMetricsProvider
// has changed to |type|.
void OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType type);
- // Task runner used for blocking file I/O.
- base::TaskRunner* io_task_runner_;
-
// True if |connection_type_| changed during the lifetime of the log.
bool connection_type_is_ambiguous_;
// The connection type according to net::NetworkChangeNotifier.
@@ -142,9 +131,10 @@ class NetworkMetricsProvider
// Last known effective connection type.
net::EffectiveConnectionType effective_connection_type_;
- // True if |effective_connection_type_| changed during the lifetime of the
- // log.
- bool effective_connection_type_is_ambiguous_;
+ // Minimum and maximum effective connection type since the metrics were last
+ // provided.
+ net::EffectiveConnectionType min_effective_connection_type_;
+ net::EffectiveConnectionType max_effective_connection_type_;
base::ThreadChecker thread_checker_;
diff --git a/chromium/components/metrics/net/network_metrics_provider_unittest.cc b/chromium/components/metrics/net/network_metrics_provider_unittest.cc
index 7006ee146be..469a409e165 100644
--- a/chromium/components/metrics/net/network_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/net/network_metrics_provider_unittest.cc
@@ -6,8 +6,8 @@
#include "base/callback.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/metrics/proto/system_profile.pb.h"
#include "net/base/network_change_notifier.h"
@@ -49,7 +49,9 @@ class TestNetworkQualityEstimatorProvider
class NetworkMetricsProviderTest : public testing::Test {
protected:
- NetworkMetricsProviderTest() : loop_(base::MessageLoop::TYPE_IO) {
+ NetworkMetricsProviderTest()
+ : scoped_task_environment_(
+ base::test::ScopedTaskEnvironment::MainThreadType::IO) {
#if defined(OS_CHROMEOS)
chromeos::DBusThreadManager::Initialize();
chromeos::NetworkHandler::Initialize();
@@ -57,7 +59,7 @@ class NetworkMetricsProviderTest : public testing::Test {
}
private:
- base::MessageLoop loop_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
};
// Verifies that the effective connection type is correctly set.
@@ -68,30 +70,45 @@ TEST_F(NetworkMetricsProviderTest, EffectiveConnectionType) {
new TestNetworkQualityEstimatorProvider(&estimator)));
SystemProfileProto system_profile;
NetworkMetricsProvider network_metrics_provider(
- std::move(estimator_provider), base::ThreadTaskRunnerHandle::Get().get());
+ std::move(estimator_provider));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
network_metrics_provider.effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.min_effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.max_effective_connection_type_);
network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
- system_profile.network().effective_connection_type());
+ system_profile.network().min_effective_connection_type());
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ system_profile.network().max_effective_connection_type());
// Set RTT so that the effective connection type is computed as 2G.
estimator.set_recent_http_rtt(base::TimeDelta::FromMilliseconds(1500));
estimator.set_start_time_null_http_rtt(
base::TimeDelta::FromMilliseconds(1500));
-
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
network_metrics_provider.effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.min_effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.max_effective_connection_type_);
// Running a request would cause the effective connection type to be computed
// as 2G, and observers to be notified.
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
network_metrics_provider.effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
+ network_metrics_provider.min_effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
+ network_metrics_provider.max_effective_connection_type_);
network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
- system_profile.network().effective_connection_type());
+ system_profile.network().min_effective_connection_type());
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
+ system_profile.network().max_effective_connection_type());
// Set RTT so that the effective connection type is computed as SLOW_2G.
estimator.set_recent_http_rtt(base::TimeDelta::FromMilliseconds(3000));
@@ -103,22 +120,29 @@ TEST_F(NetworkMetricsProviderTest, EffectiveConnectionType) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
network_metrics_provider.effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
+ network_metrics_provider.min_effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
+ network_metrics_provider.max_effective_connection_type_);
network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
- // Effective connection type is set to ambiguous since the effective
- // connection type changed from 2G to SLOW_2G during the lifetime of the log.
- EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_AMBIGUOUS,
- system_profile.network().effective_connection_type());
-
- // Getting the system profile again should return the actual effective
- // connection type since the effective connection type did not change during
- // the lifetime of the log.
+ // Effective connection type changed from 2G to SLOW_2G during the lifetime of
+ // the log. Minimum value of ECT must be different from the maximum value.
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
+ system_profile.network().min_effective_connection_type());
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
+ system_profile.network().max_effective_connection_type());
+
+ // Getting the system profile again should return the current effective
+ // connection type.
network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
- system_profile.network().effective_connection_type());
+ system_profile.network().min_effective_connection_type());
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
+ system_profile.network().max_effective_connection_type());
}
-// Verifies that the effective connection type is set to AMBIGUOUS when there is
-// a change in the connection type.
+// Verifies that the effective connection type is not set to UNKNOWN when there
+// is a change in the connection type.
TEST_F(NetworkMetricsProviderTest, ECTAmbiguousOnConnectionTypeChange) {
net::TestNetworkQualityEstimator estimator;
std::unique_ptr<NetworkMetricsProvider::NetworkQualityEstimatorProvider>
@@ -126,19 +150,47 @@ TEST_F(NetworkMetricsProviderTest, ECTAmbiguousOnConnectionTypeChange) {
new TestNetworkQualityEstimatorProvider(&estimator)));
SystemProfileProto system_profile;
NetworkMetricsProvider network_metrics_provider(
- std::move(estimator_provider), base::ThreadTaskRunnerHandle::Get().get());
+ std::move(estimator_provider));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
network_metrics_provider.effective_connection_type_);
- EXPECT_FALSE(
- network_metrics_provider.effective_connection_type_is_ambiguous_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.min_effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.max_effective_connection_type_);
+
+ // Set RTT so that the effective connection type is computed as 2G.
+ estimator.set_recent_http_rtt(base::TimeDelta::FromMilliseconds(1500));
+ estimator.set_start_time_null_http_rtt(
+ base::TimeDelta::FromMilliseconds(1500));
+ // Running a request would cause the effective connection type to be computed
+ // as 2G, and observers to be notified.
+ estimator.RunOneRequest();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
+ network_metrics_provider.effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
+ network_metrics_provider.min_effective_connection_type_);
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
+ network_metrics_provider.max_effective_connection_type_);
+ // There is no change in the connection type. Effective connection types
+ // should be reported as 2G.
+ network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
+ system_profile.network().min_effective_connection_type());
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
+ system_profile.network().max_effective_connection_type());
+
+ // Even with change in the connection type, effective connection types
+ // should be reported as 2G.
network_metrics_provider.OnConnectionTypeChanged(
net::NetworkChangeNotifier::CONNECTION_2G);
- EXPECT_TRUE(network_metrics_provider.effective_connection_type_is_ambiguous_);
network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
- EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_AMBIGUOUS,
- system_profile.network().effective_connection_type());
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
+ system_profile.network().min_effective_connection_type());
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
+ system_profile.network().max_effective_connection_type());
}
} // namespace metrics \ No newline at end of file
diff --git a/chromium/components/metrics/net/version_utils.cc b/chromium/components/metrics/net/version_utils.cc
deleted file mode 100644
index 37452330d9e..00000000000
--- a/chromium/components/metrics/net/version_utils.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/metrics/net/version_utils.h"
-
-#include "base/logging.h"
-#include "build/build_config.h"
-#include "components/version_info/version_info.h"
-
-namespace metrics {
-
-std::string GetVersionString() {
- std::string version = version_info::GetVersionNumber();
-#if defined(ARCH_CPU_64_BITS)
- version += "-64";
-#endif // defined(ARCH_CPU_64_BITS)
- if (!version_info::IsOfficialBuild())
- version.append("-devel");
- return version;
-}
-
-SystemProfileProto::Channel AsProtobufChannel(
- version_info::Channel channel) {
- switch (channel) {
- case version_info::Channel::UNKNOWN:
- return SystemProfileProto::CHANNEL_UNKNOWN;
- case version_info::Channel::CANARY:
- return SystemProfileProto::CHANNEL_CANARY;
- case version_info::Channel::DEV:
- return SystemProfileProto::CHANNEL_DEV;
- case version_info::Channel::BETA:
- return SystemProfileProto::CHANNEL_BETA;
- case version_info::Channel::STABLE:
- return SystemProfileProto::CHANNEL_STABLE;
- }
- NOTREACHED();
- return SystemProfileProto::CHANNEL_UNKNOWN;
-}
-
-} // namespace metrics
diff --git a/chromium/components/metrics/net/version_utils.h b/chromium/components/metrics/net/version_utils.h
deleted file mode 100644
index 853c846a875..00000000000
--- a/chromium/components/metrics/net/version_utils.h
+++ /dev/null
@@ -1,29 +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_METRICS_NET_VERSION_UTILS_H_
-#define COMPONENTS_METRICS_NET_VERSION_UTILS_H_
-
-#include <string>
-
-#include "components/metrics/proto/system_profile.pb.h"
-
-namespace version_info {
-enum class Channel;
-}
-
-namespace metrics {
-
-// Build a string including the Chrome app version, suffixed by "-64" on 64-bit
-// platforms, and "-devel" on developer builds.
-std::string GetVersionString();
-
-// Translates version_info::Channel to the equivalent
-// SystemProfileProto::Channel.
-SystemProfileProto::Channel AsProtobufChannel(
- version_info::Channel channel);
-
-} // namespace metrics
-
-#endif // COMPONENTS_METRICS_NET_VERSION_UTILS_H_
diff --git a/chromium/components/metrics/persistent_system_profile.cc b/chromium/components/metrics/persistent_system_profile.cc
new file mode 100644
index 00000000000..6c525509ce9
--- /dev/null
+++ b/chromium/components/metrics/persistent_system_profile.cc
@@ -0,0 +1,437 @@
+// 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/metrics/persistent_system_profile.h"
+
+#include <set>
+
+#include "base/atomicops.h"
+#include "base/bits.h"
+#include "base/memory/singleton.h"
+#include "base/metrics/persistent_memory_allocator.h"
+#include "base/pickle.h"
+#include "base/stl_util.h"
+#include "components/variations/active_field_trials.h"
+
+namespace metrics {
+
+namespace {
+
+// To provide atomic addition of records so that there is no confusion between
+// writers and readers, all of the metadata about a record is contained in a
+// structure that can be stored as a single atomic 32-bit word.
+union RecordHeader {
+ struct {
+ unsigned continued : 1; // Flag indicating if there is more after this.
+ unsigned type : 7; // The type of this record.
+ unsigned amount : 24; // The amount of data to follow.
+ } as_parts;
+ base::subtle::Atomic32 as_atomic;
+};
+
+constexpr uint32_t kTypeIdSystemProfile = 0x330A7150; // SHA1(SystemProfile)
+constexpr size_t kSystemProfileAllocSize = 4 << 10; // 4 KiB
+constexpr size_t kMaxRecordSize = (1 << 24) - sizeof(RecordHeader);
+
+static_assert(sizeof(RecordHeader) == sizeof(base::subtle::Atomic32),
+ "bad RecordHeader size");
+
+// Calculate the size of a record based on the amount of data. This adds room
+// for the record header and rounds up to the next multiple of the record-header
+// size.
+size_t CalculateRecordSize(size_t data_amount) {
+ return base::bits::Align(data_amount + sizeof(RecordHeader),
+ sizeof(RecordHeader));
+}
+
+} // namespace
+
+PersistentSystemProfile::RecordAllocator::RecordAllocator(
+ base::PersistentMemoryAllocator* memory_allocator,
+ size_t min_size)
+ : allocator_(memory_allocator),
+ has_complete_profile_(false),
+ alloc_reference_(0),
+ alloc_size_(0),
+ end_offset_(0) {
+ AddSegment(min_size);
+}
+
+PersistentSystemProfile::RecordAllocator::RecordAllocator(
+ const base::PersistentMemoryAllocator* memory_allocator)
+ : allocator_(
+ const_cast<base::PersistentMemoryAllocator*>(memory_allocator)),
+ alloc_reference_(0),
+ alloc_size_(0),
+ end_offset_(0) {}
+
+void PersistentSystemProfile::RecordAllocator::Reset() {
+ // Clear the first word of all blocks so they're known to be "empty".
+ alloc_reference_ = 0;
+ while (NextSegment()) {
+ // Get the block as a char* and cast it. It can't be fetched directly as
+ // an array of RecordHeader because that's not a fundamental type and only
+ // arrays of fundamental types are allowed.
+ RecordHeader* header =
+ reinterpret_cast<RecordHeader*>(allocator_->GetAsArray<char>(
+ alloc_reference_, kTypeIdSystemProfile, sizeof(RecordHeader)));
+ DCHECK(header);
+ base::subtle::NoBarrier_Store(&header->as_atomic, 0);
+ }
+
+ // Reset member variables.
+ has_complete_profile_ = false;
+ alloc_reference_ = 0;
+ alloc_size_ = 0;
+ end_offset_ = 0;
+}
+
+bool PersistentSystemProfile::RecordAllocator::Write(RecordType type,
+ base::StringPiece record) {
+ const char* data = record.data();
+ size_t remaining_size = record.size();
+
+ // Allocate space and write records until everything has been stored.
+ do {
+ if (end_offset_ == alloc_size_) {
+ if (!AddSegment(remaining_size))
+ return false;
+ }
+ // Write out as much of the data as possible. |data| and |remaining_size|
+ // are updated in place.
+ if (!WriteData(type, &data, &remaining_size))
+ return false;
+ } while (remaining_size > 0);
+
+ return true;
+}
+
+bool PersistentSystemProfile::RecordAllocator::HasMoreData() const {
+ if (alloc_reference_ == 0 && !NextSegment())
+ return false;
+
+ char* block =
+ allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
+ base::PersistentMemoryAllocator::kSizeAny);
+ if (!block)
+ return false;
+
+ RecordHeader header;
+ header.as_atomic = base::subtle::Acquire_Load(
+ reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_));
+ return header.as_parts.type != kUnusedSpace;
+}
+
+bool PersistentSystemProfile::RecordAllocator::Read(RecordType* type,
+ std::string* record) const {
+ *type = kUnusedSpace;
+ record->clear();
+
+ // Access data and read records until everything has been loaded.
+ while (true) {
+ if (end_offset_ == alloc_size_) {
+ if (!NextSegment())
+ return false;
+ }
+ if (ReadData(type, record))
+ return *type != kUnusedSpace;
+ }
+}
+
+bool PersistentSystemProfile::RecordAllocator::NextSegment() const {
+ base::PersistentMemoryAllocator::Iterator iter(allocator_, alloc_reference_);
+ alloc_reference_ = iter.GetNextOfType(kTypeIdSystemProfile);
+ alloc_size_ = allocator_->GetAllocSize(alloc_reference_);
+ end_offset_ = 0;
+ return alloc_reference_ != 0;
+}
+
+bool PersistentSystemProfile::RecordAllocator::AddSegment(size_t min_size) {
+ if (NextSegment()) {
+ // The first record-header should have been zeroed as part of the allocation
+ // or by the "reset" procedure.
+ DCHECK_EQ(0, base::subtle::NoBarrier_Load(
+ allocator_->GetAsArray<base::subtle::Atomic32>(
+ alloc_reference_, kTypeIdSystemProfile, 1)));
+ return true;
+ }
+
+ DCHECK_EQ(0U, alloc_reference_);
+ DCHECK_EQ(0U, end_offset_);
+
+ size_t size =
+ std::max(CalculateRecordSize(min_size), kSystemProfileAllocSize);
+
+ uint32_t ref = allocator_->Allocate(size, kTypeIdSystemProfile);
+ if (!ref)
+ return false; // Allocator must be full.
+ allocator_->MakeIterable(ref);
+
+ alloc_reference_ = ref;
+ alloc_size_ = allocator_->GetAllocSize(ref);
+ return true;
+}
+
+bool PersistentSystemProfile::RecordAllocator::WriteData(RecordType type,
+ const char** data,
+ size_t* data_size) {
+ char* block =
+ allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
+ base::PersistentMemoryAllocator::kSizeAny);
+ if (!block)
+ return false; // It's bad if there is no accessible block.
+
+ const size_t max_write_size = std::min(
+ kMaxRecordSize, alloc_size_ - end_offset_ - sizeof(RecordHeader));
+ const size_t write_size = std::min(*data_size, max_write_size);
+ const size_t record_size = CalculateRecordSize(write_size);
+ DCHECK_LT(write_size, record_size);
+
+ // Write the data and the record header.
+ RecordHeader header;
+ header.as_atomic = 0;
+ header.as_parts.type = type;
+ header.as_parts.amount = write_size;
+ header.as_parts.continued = (write_size < *data_size);
+ size_t offset = end_offset_;
+ end_offset_ += record_size;
+ DCHECK_GE(alloc_size_, end_offset_);
+ if (end_offset_ < alloc_size_) {
+ // An empty record header has to be next before this one gets written.
+ base::subtle::NoBarrier_Store(
+ reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_), 0);
+ }
+ memcpy(block + offset + sizeof(header), *data, write_size);
+ base::subtle::Release_Store(
+ reinterpret_cast<base::subtle::Atomic32*>(block + offset),
+ header.as_atomic);
+
+ // Account for what was stored and prepare for follow-on records with any
+ // remaining data.
+ *data += write_size;
+ *data_size -= write_size;
+
+ return true;
+}
+
+bool PersistentSystemProfile::RecordAllocator::ReadData(
+ RecordType* type,
+ std::string* record) const {
+ DCHECK_GT(alloc_size_, end_offset_);
+
+ char* block =
+ allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
+ base::PersistentMemoryAllocator::kSizeAny);
+ if (!block) {
+ *type = kUnusedSpace;
+ return true; // No more data.
+ }
+
+ // Get and validate the record header.
+ RecordHeader header;
+ header.as_atomic = base::subtle::Acquire_Load(
+ reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_));
+ bool continued = !!header.as_parts.continued;
+ if (header.as_parts.type == kUnusedSpace) {
+ *type = kUnusedSpace;
+ return true; // End of all records.
+ } else if (*type == kUnusedSpace) {
+ *type = static_cast<RecordType>(header.as_parts.type);
+ } else if (*type != header.as_parts.type) {
+ NOTREACHED(); // Continuation didn't match start of record.
+ *type = kUnusedSpace;
+ record->clear();
+ return false;
+ }
+ size_t read_size = header.as_parts.amount;
+ if (end_offset_ + sizeof(header) + read_size > alloc_size_) {
+ NOTREACHED(); // Invalid header amount.
+ *type = kUnusedSpace;
+ return true; // Don't try again.
+ }
+
+ // Append the record data to the output string.
+ record->append(block + end_offset_ + sizeof(header), read_size);
+ end_offset_ += CalculateRecordSize(read_size);
+ DCHECK_GE(alloc_size_, end_offset_);
+
+ return !continued;
+}
+
+PersistentSystemProfile::PersistentSystemProfile() {}
+
+PersistentSystemProfile::~PersistentSystemProfile() {}
+
+void PersistentSystemProfile::RegisterPersistentAllocator(
+ base::PersistentMemoryAllocator* memory_allocator) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // Create and store the allocator. A |min_size| of "1" ensures that a memory
+ // block is reserved now.
+ RecordAllocator allocator(memory_allocator, 1);
+ allocators_.push_back(std::move(allocator));
+ all_have_complete_profile_ = false;
+}
+
+void PersistentSystemProfile::DeregisterPersistentAllocator(
+ base::PersistentMemoryAllocator* memory_allocator) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // This would be more efficient with a std::map but it's not expected that
+ // allocators will get deregistered with any frequency, if at all.
+ base::EraseIf(allocators_, [=](RecordAllocator& records) {
+ return records.allocator() == memory_allocator;
+ });
+}
+
+void PersistentSystemProfile::SetSystemProfile(
+ const std::string& serialized_profile,
+ bool complete) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (allocators_.empty() || serialized_profile.empty())
+ return;
+
+ for (auto& allocator : allocators_) {
+ // Don't overwrite a complete profile with an incomplete one.
+ if (!complete && allocator.has_complete_profile())
+ continue;
+ // A full system profile always starts fresh. Incomplete keeps existing
+ // records for merging.
+ if (complete)
+ allocator.Reset();
+ // Write out the serialized profile.
+ allocator.Write(kSystemProfileProto, serialized_profile);
+ // Indicate if this is a complete profile.
+ if (complete)
+ allocator.set_complete_profile();
+ }
+
+ if (complete)
+ all_have_complete_profile_ = true;
+}
+
+void PersistentSystemProfile::SetSystemProfile(
+ const SystemProfileProto& profile,
+ bool complete) {
+ // Avoid serialization if passed profile is not complete and all allocators
+ // already have complete ones.
+ if (!complete && all_have_complete_profile_)
+ return;
+
+ std::string serialized_profile;
+ if (!profile.SerializeToString(&serialized_profile))
+ return;
+ SetSystemProfile(serialized_profile, complete);
+}
+
+void PersistentSystemProfile::AddFieldTrial(base::StringPiece trial,
+ base::StringPiece group) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!trial.empty());
+ DCHECK(!group.empty());
+
+ base::Pickle pickler;
+ if (!pickler.WriteString(trial) || !pickler.WriteString(group))
+ return;
+
+ WriteToAll(kFieldTrialInfo,
+ base::StringPiece(static_cast<const char*>(pickler.data()),
+ pickler.size()));
+}
+
+// static
+bool PersistentSystemProfile::HasSystemProfile(
+ const base::PersistentMemoryAllocator& memory_allocator) {
+ const RecordAllocator records(&memory_allocator);
+ return records.HasMoreData();
+}
+
+// static
+bool PersistentSystemProfile::GetSystemProfile(
+ const base::PersistentMemoryAllocator& memory_allocator,
+ SystemProfileProto* system_profile) {
+ const RecordAllocator records(&memory_allocator);
+
+ RecordType type;
+ std::string record;
+ do {
+ if (!records.Read(&type, &record))
+ return false;
+ } while (type != kSystemProfileProto);
+
+ if (!system_profile->ParseFromString(record))
+ return false;
+
+ MergeUpdateRecords(memory_allocator, system_profile);
+ return true;
+}
+
+// static
+void PersistentSystemProfile::MergeUpdateRecords(
+ const base::PersistentMemoryAllocator& memory_allocator,
+ SystemProfileProto* system_profile) {
+ const RecordAllocator records(&memory_allocator);
+
+ RecordType type;
+ std::string record;
+ std::set<uint32_t> known_field_trial_ids;
+
+ // This is done separate from the code that gets the profile because it
+ // compartmentalizes the code and makes it possible to reuse this section
+ // should it be needed to merge "update" records into a new "complete"
+ // system profile that somehow didn't get all the updates.
+ while (records.Read(&type, &record)) {
+ switch (type) {
+ case kUnusedSpace:
+ // These should never be returned.
+ NOTREACHED();
+ break;
+
+ case kSystemProfileProto:
+ // Profile was passed in; ignore this one.
+ break;
+
+ case kFieldTrialInfo: {
+ // Get the set of known trial IDs so duplicates don't get added.
+ if (known_field_trial_ids.empty()) {
+ for (int i = 0; i < system_profile->field_trial_size(); ++i) {
+ known_field_trial_ids.insert(
+ system_profile->field_trial(i).name_id());
+ }
+ }
+
+ base::Pickle pickler(record.data(), record.size());
+ base::PickleIterator iter(pickler);
+ base::StringPiece trial;
+ base::StringPiece group;
+ if (iter.ReadStringPiece(&trial) && iter.ReadStringPiece(&group)) {
+ variations::ActiveGroupId field_ids =
+ variations::MakeActiveGroupId(trial, group);
+ if (!base::ContainsKey(known_field_trial_ids, field_ids.name)) {
+ SystemProfileProto::FieldTrial* field_trial =
+ system_profile->add_field_trial();
+ field_trial->set_name_id(field_ids.name);
+ field_trial->set_group_id(field_ids.group);
+ known_field_trial_ids.insert(field_ids.name);
+ }
+ }
+ } break;
+ }
+ }
+}
+
+void PersistentSystemProfile::WriteToAll(RecordType type,
+ base::StringPiece record) {
+ for (auto& allocator : allocators_)
+ allocator.Write(type, record);
+}
+
+GlobalPersistentSystemProfile* GlobalPersistentSystemProfile::GetInstance() {
+ return base::Singleton<
+ GlobalPersistentSystemProfile,
+ base::LeakySingletonTraits<GlobalPersistentSystemProfile>>::get();
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/persistent_system_profile.h b/chromium/components/metrics/persistent_system_profile.h
new file mode 100644
index 00000000000..98352e9acbd
--- /dev/null
+++ b/chromium/components/metrics/persistent_system_profile.h
@@ -0,0 +1,158 @@
+// 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 BASE_METRICS_PERSISTENT_SYSTEM_PROFILE_H_
+#define BASE_METRICS_PERSISTENT_SYSTEM_PROFILE_H_
+
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "base/threading/thread_checker.h"
+#include "components/metrics/proto/system_profile.pb.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+class PersistentMemoryAllocator;
+} // namespace base
+
+namespace metrics {
+
+// Manages a copy of the system profile inside persistent memory segments.
+class PersistentSystemProfile {
+ public:
+ PersistentSystemProfile();
+ ~PersistentSystemProfile();
+
+ // This object can store records in multiple memory allocators.
+ void RegisterPersistentAllocator(
+ base::PersistentMemoryAllocator* memory_allocator);
+ void DeregisterPersistentAllocator(
+ base::PersistentMemoryAllocator* memory_allocator);
+
+ // Stores a complete system profile. Use the version taking the serialized
+ // version if available to avoid multiple serialization actions. The
+ // |complete| flag indicates that this profile contains all known information
+ // and can replace whatever exists. If the flag is false, the profile will be
+ // stored only if there is nothing else already present.
+ void SetSystemProfile(const std::string& serialized_profile, bool complete);
+ void SetSystemProfile(const SystemProfileProto& profile, bool complete);
+
+ // Records the existence of a field trial.
+ void AddFieldTrial(base::StringPiece trial, base::StringPiece group);
+
+ // Tests if a persistent memory allocator contains an system profile.
+ static bool HasSystemProfile(
+ const base::PersistentMemoryAllocator& memory_allocator);
+
+ // Retrieves the system profile from a persistent memory allocator. Returns
+ // true if a profile was successfully retrieved.
+ static bool GetSystemProfile(
+ const base::PersistentMemoryAllocator& memory_allocator,
+ SystemProfileProto* system_profile);
+
+ private:
+ friend class PersistentSystemProfileTest;
+
+ // Defines record types that can be stored inside our local Allocators.
+ enum RecordType : uint8_t {
+ kUnusedSpace = 0, // The default value for empty memory.
+ kSystemProfileProto,
+ kFieldTrialInfo,
+ };
+
+ // A class for managing record allocations inside a persistent memory segment.
+ class RecordAllocator {
+ public:
+ // Construct an allocator for writing.
+ RecordAllocator(base::PersistentMemoryAllocator* memory_allocator,
+ size_t min_size);
+
+ // Construct an allocator for reading.
+ RecordAllocator(const base::PersistentMemoryAllocator* memory_allocator);
+
+ // These methods manage writing records to the allocator. Do not mix these
+ // with "read" calls; it's one or the other.
+ void Reset();
+ bool Write(RecordType type, base::StringPiece record);
+
+ // Read a record from the allocator. Do not mix this with "write" calls;
+ // it's one or the other.
+ bool HasMoreData() const;
+ bool Read(RecordType* type, std::string* record) const;
+
+ base::PersistentMemoryAllocator* allocator() { return allocator_; }
+
+ bool has_complete_profile() { return has_complete_profile_; }
+ void set_complete_profile() { has_complete_profile_ = true; }
+
+ private:
+ // Advance to the next record segment in the memory allocator.
+ bool NextSegment() const;
+
+ // Advance to the next record segment, creating a new one if necessary with
+ // sufficent |min_size| space.
+ bool AddSegment(size_t min_size);
+
+ // Writes data to the current position, updating the passed values past
+ // the amount written. Returns false in case of an error.
+ bool WriteData(RecordType type, const char** data, size_t* data_size);
+
+ // Reads data from the current position, updating the passed string
+ // in-place. |type| must be initialized to kUnusedSpace and |record| must
+ // be an empty string before the first call but unchanged thereafter.
+ // Returns true when record is complete.
+ bool ReadData(RecordType* type, std::string* record) const;
+
+ // This never changes but can't be "const" because vector calls operator=().
+ base::PersistentMemoryAllocator* allocator_; // Storage location.
+
+ // Indicates if a complete profile has been stored.
+ bool has_complete_profile_;
+
+ // These change even though the underlying data may be "const".
+ mutable uint32_t alloc_reference_; // Last storage block.
+ mutable size_t alloc_size_; // Size of the block.
+ mutable size_t end_offset_; // End of data in block.
+
+ // Copy and assign are allowed for easy use with STL containers.
+ };
+
+ // Write a record to all registered allocators.
+ void WriteToAll(RecordType type, base::StringPiece record);
+
+ // Merges all "update" records into a system profile.
+ static void MergeUpdateRecords(
+ const base::PersistentMemoryAllocator& memory_allocator,
+ SystemProfileProto* system_profile);
+
+ // The list of registered persistent allocators, described by RecordAllocator
+ // instances.
+ std::vector<RecordAllocator> allocators_;
+
+ // Indicates if a complete profile has been stored to all allocators.
+ bool all_have_complete_profile_ = false;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(PersistentSystemProfile);
+};
+
+// A singleton instance of the above.
+class GlobalPersistentSystemProfile : public PersistentSystemProfile {
+ public:
+ static GlobalPersistentSystemProfile* GetInstance();
+
+ private:
+ friend struct base::DefaultSingletonTraits<GlobalPersistentSystemProfile>;
+
+ GlobalPersistentSystemProfile() {}
+ ~GlobalPersistentSystemProfile() {}
+
+ DISALLOW_COPY_AND_ASSIGN(GlobalPersistentSystemProfile);
+};
+
+} // namespace metrics
+
+#endif // BASE_METRICS_PERSISTENT_SYSTEM_PROFILE_H_
diff --git a/chromium/components/metrics/persistent_system_profile_unittest.cc b/chromium/components/metrics/persistent_system_profile_unittest.cc
new file mode 100644
index 00000000000..4adaed3fb73
--- /dev/null
+++ b/chromium/components/metrics/persistent_system_profile_unittest.cc
@@ -0,0 +1,171 @@
+// 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/metrics/persistent_system_profile.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/persistent_memory_allocator.h"
+#include "base/rand_util.h"
+#include "components/variations/metrics_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+class PersistentSystemProfileTest : public testing::Test {
+ public:
+ const int32_t kAllocatorMemorySize = 1 << 20; // 1 MiB
+
+ PersistentSystemProfileTest() {}
+ ~PersistentSystemProfileTest() override {}
+
+ void SetUp() override {
+ memory_allocator_ = base::MakeUnique<base::LocalPersistentMemoryAllocator>(
+ kAllocatorMemorySize, 0, "");
+ records_ = base::MakeUnique<PersistentSystemProfile::RecordAllocator>(
+ memory_allocator_.get());
+ persistent_profile_.RegisterPersistentAllocator(memory_allocator_.get());
+ }
+
+ void TearDown() override {
+ persistent_profile_.DeregisterPersistentAllocator(memory_allocator_.get());
+ records_.reset();
+ memory_allocator_.reset();
+ }
+
+ void WriteRecord(uint8_t type, const std::string& record) {
+ persistent_profile_.allocators_[0].Write(
+ static_cast<PersistentSystemProfile::RecordType>(type), record);
+ }
+
+ bool ReadRecord(uint8_t* type, std::string* record) {
+ PersistentSystemProfile::RecordType rec_type;
+
+ bool success = records_->Read(&rec_type, record);
+ *type = rec_type; // Convert to uint8_t for testing.
+ return success;
+ }
+
+ base::PersistentMemoryAllocator* memory_allocator() {
+ return memory_allocator_.get();
+ }
+
+ PersistentSystemProfile* persistent_profile() { return &persistent_profile_; }
+
+ private:
+ PersistentSystemProfile persistent_profile_;
+ std::unique_ptr<base::PersistentMemoryAllocator> memory_allocator_;
+ std::unique_ptr<PersistentSystemProfile::RecordAllocator> records_;
+
+ DISALLOW_COPY_AND_ASSIGN(PersistentSystemProfileTest);
+};
+
+TEST_F(PersistentSystemProfileTest, Create) {
+ uint32_t type;
+ base::PersistentMemoryAllocator::Iterator iter(memory_allocator());
+ base::PersistentMemoryAllocator::Reference ref = iter.GetNext(&type);
+ DCHECK(ref);
+ DCHECK_NE(0U, type);
+}
+
+TEST_F(PersistentSystemProfileTest, RecordSplitting) {
+ const size_t kRecordSize = 100 << 10; // 100 KiB
+ std::vector<char> buffer;
+ buffer.resize(kRecordSize);
+ base::RandBytes(&buffer[0], kRecordSize);
+
+ WriteRecord(42, std::string(&buffer[0], kRecordSize));
+
+ uint8_t type;
+ std::string record;
+ ASSERT_TRUE(ReadRecord(&type, &record));
+ EXPECT_EQ(42U, type);
+ ASSERT_EQ(kRecordSize, record.size());
+ for (size_t i = 0; i < kRecordSize; ++i)
+ EXPECT_EQ(buffer[i], record[i]);
+}
+
+TEST_F(PersistentSystemProfileTest, ProfileStorage) {
+ SystemProfileProto proto1;
+ SystemProfileProto::FieldTrial* trial = proto1.add_field_trial();
+ trial->set_name_id(123);
+ trial->set_group_id(456);
+
+ persistent_profile()->SetSystemProfile(proto1, false);
+
+ SystemProfileProto proto2;
+ ASSERT_TRUE(PersistentSystemProfile::HasSystemProfile(*memory_allocator()));
+ ASSERT_TRUE(
+ PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
+ ASSERT_EQ(1, proto2.field_trial_size());
+ EXPECT_EQ(123U, proto2.field_trial(0).name_id());
+ EXPECT_EQ(456U, proto2.field_trial(0).group_id());
+
+ // Check that the profile can be overwritten.
+
+ trial = proto1.add_field_trial();
+ trial->set_name_id(78);
+ trial->set_group_id(90);
+
+ persistent_profile()->SetSystemProfile(proto1, true);
+
+ ASSERT_TRUE(
+ PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
+ ASSERT_EQ(2, proto2.field_trial_size());
+ EXPECT_EQ(123U, proto2.field_trial(0).name_id());
+ EXPECT_EQ(456U, proto2.field_trial(0).group_id());
+ EXPECT_EQ(78U, proto2.field_trial(1).name_id());
+ EXPECT_EQ(90U, proto2.field_trial(1).group_id());
+
+ // Check that the profile won't be overwritten by a new non-complete profile.
+
+ trial = proto1.add_field_trial();
+ trial->set_name_id(0xC0DE);
+ trial->set_group_id(0xFEED);
+
+ persistent_profile()->SetSystemProfile(proto1, false);
+
+ ASSERT_TRUE(
+ PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
+ ASSERT_EQ(2, proto2.field_trial_size());
+ EXPECT_EQ(123U, proto2.field_trial(0).name_id());
+ EXPECT_EQ(456U, proto2.field_trial(0).group_id());
+ EXPECT_EQ(78U, proto2.field_trial(1).name_id());
+ EXPECT_EQ(90U, proto2.field_trial(1).group_id());
+}
+
+TEST_F(PersistentSystemProfileTest, ProfileExtensions) {
+ persistent_profile()->AddFieldTrial("sna", "foo");
+
+ SystemProfileProto fetched;
+ ASSERT_FALSE(
+ PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
+
+ SystemProfileProto proto;
+ SystemProfileProto::FieldTrial* trial = proto.add_field_trial();
+ trial->set_name_id(123);
+ trial->set_group_id(456);
+
+ persistent_profile()->SetSystemProfile(proto, false);
+ ASSERT_TRUE(
+ PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
+ ASSERT_EQ(2, fetched.field_trial_size());
+ EXPECT_EQ(123U, fetched.field_trial(0).name_id());
+ EXPECT_EQ(456U, fetched.field_trial(0).group_id());
+ EXPECT_EQ(metrics::HashName("sna"), fetched.field_trial(1).name_id());
+ EXPECT_EQ(metrics::HashName("foo"), fetched.field_trial(1).group_id());
+
+ persistent_profile()->AddFieldTrial("foo", "bar");
+ ASSERT_TRUE(
+ PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
+ ASSERT_EQ(3, fetched.field_trial_size());
+ EXPECT_EQ(123U, fetched.field_trial(0).name_id());
+ EXPECT_EQ(456U, fetched.field_trial(0).group_id());
+ EXPECT_EQ(metrics::HashName("sna"), fetched.field_trial(1).name_id());
+ EXPECT_EQ(metrics::HashName("foo"), fetched.field_trial(1).group_id());
+ EXPECT_EQ(metrics::HashName("foo"), fetched.field_trial(2).name_id());
+ EXPECT_EQ(metrics::HashName("bar"), fetched.field_trial(2).group_id());
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/proto/BUILD.gn b/chromium/components/metrics/proto/BUILD.gn
index d8f8c1fdc2a..c224887bf29 100644
--- a/chromium/components/metrics/proto/BUILD.gn
+++ b/chromium/components/metrics/proto/BUILD.gn
@@ -16,6 +16,7 @@ proto_library("proto") {
"omnibox_input_type.proto",
"perf_data.proto",
"perf_stat.proto",
+ "printer_event.proto",
"profiler_event.proto",
"sampled_profile.proto",
"system_profile.proto",
diff --git a/chromium/components/metrics/proto/cast_logs.proto b/chromium/components/metrics/proto/cast_logs.proto
index 4bea0fdf75a..171f86d5975 100644
--- a/chromium/components/metrics/proto/cast_logs.proto
+++ b/chromium/components/metrics/proto/cast_logs.proto
@@ -18,7 +18,7 @@ message CastLogsProto {
// Next tag: 6
message CastDeviceInfo {
// The product type of Cast device sent from Cast-enabled devices.
- // Next tag: 5
+ // Next tag: 7
enum CastProductType {
CAST_PRODUCT_TYPE_UNKNOWN = 0;
CAST_PRODUCT_TYPE_CHROMECAST = 1;
@@ -26,6 +26,7 @@ message CastLogsProto {
CAST_PRODUCT_TYPE_AUDIO = 3;
CAST_PRODUCT_TYPE_ANDROID_TV = 4;
CAST_PRODUCT_TYPE_ASSISTANT = 5;
+ CAST_PRODUCT_TYPE_ANDROID_THINGS = 6;
}
optional CastProductType type = 1;
@@ -84,7 +85,7 @@ message CastLogsProto {
optional string chrome_browser_version = 4;
// Platform of sender device.
- // Next tag: 7
+ // Next tag: 8
enum Platform {
// Any platform other then cases below.
PLATFORM_OTHER = 0;
@@ -95,6 +96,9 @@ message CastLogsProto {
PLATFORM_OSX = 4;
PLATFORM_CHROMEOS = 5;
PLATFORM_LINUX = 6;
+
+ // The sender is Cast device - including itself.
+ PLATFORM_CAST = 7;
}
optional Platform platform = 5;
@@ -107,6 +111,8 @@ message CastLogsProto {
CONNECTION_TYPE_UNKNOWN = 0;
CONNECTION_TYPE_LOCAL = 1;
CONNECTION_TYPE_RELAY = 2;
+ // A connection created by receiver itself internally.
+ CONNECTION_TYPE_INTERNAL = 3;
}
optional ConnectionType transport_connection_type = 7;
diff --git a/chromium/components/metrics/proto/chrome_user_metrics_extension.proto b/chromium/components/metrics/proto/chrome_user_metrics_extension.proto
index 77950f975a7..aa930da5671 100644
--- a/chromium/components/metrics/proto/chrome_user_metrics_extension.proto
+++ b/chromium/components/metrics/proto/chrome_user_metrics_extension.proto
@@ -19,11 +19,12 @@ import "omnibox_event.proto";
import "profiler_event.proto";
import "system_profile.proto";
import "translate_event.proto";
+import "printer_event.proto";
import "user_action_event.proto";
import "perf_data.proto";
import "sampled_profile.proto";
-// Next tag: 16
+// Next tag: 17
message ChromeUserMetricsExtension {
// The product (i.e. end user application) for a given UMA log.
enum Product {
@@ -70,6 +71,7 @@ message ChromeUserMetricsExtension {
repeated HistogramEventProto histogram_event = 6;
repeated ProfilerEventProto profiler_event = 7;
repeated TranslateEventProto translate_event = 15;
+ repeated PrinterEventProto printer_event = 16;
// This field is no longer used. Use |sampled_profile| instead.
repeated PerfDataProto perf_data = 8 [deprecated=true];
diff --git a/chromium/components/metrics/proto/printer_event.proto b/chromium/components/metrics/proto/printer_event.proto
new file mode 100644
index 00000000000..1c49a5741c7
--- /dev/null
+++ b/chromium/components/metrics/proto/printer_event.proto
@@ -0,0 +1,64 @@
+// 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.
+//
+// Stores information about printer configuration attempts.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "PrinterEventProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+// Stores information about a printer that a user is setting up/has attempted to
+// set up.
+// Next tag: 9
+message PrinterEventProto {
+ // The detected printer manufacuter name.
+ optional string usb_printer_manufacturer = 1;
+
+ // The detected printer model name.
+ optional string usb_printer_model = 2;
+
+ // The usb vendor id of the printer.
+ optional int32 usb_vendor_id = 3;
+
+ // The usb model id of the printer.
+ optional int32 usb_model_id = 4;
+
+ // The value reported as a printer's printer-make-and-model attribute.
+ optional string ipp_make_and_model = 5;
+
+ // A true value means that the user provided their own PPD.
+ optional bool user_ppd = 6;
+
+ // The identifier for PPDs from our serving system.
+ optional string ppd_identifier = 7;
+
+ // The action for which the printer was logged.
+ // Next tag: 5
+ enum EventType {
+ UNKNOWN = 0;
+
+ // Specified printer successfully installed using the detected
+ // configuration.
+ SETUP_AUTOMATIC = 1;
+
+ // Specified printer was installed when the user selected the appropriate
+ // configuration.
+ SETUP_MANUAL = 2;
+
+ // Setup was started but abandoned when user was prompted to choose a
+ // configuration.
+ SETUP_ABANDONED = 3;
+
+ // A printer, which had been successfully installed, was deleted from the
+ // user's preferences.
+ PRINTER_DELETED = 4;
+ }
+
+ // The event for which this was recorded.
+ optional EventType event_type = 8;
+}
diff --git a/chromium/components/metrics/proto/system_profile.proto b/chromium/components/metrics/proto/system_profile.proto
index dccd80d3db6..6b4dc6f4c8b 100644
--- a/chromium/components/metrics/proto/system_profile.proto
+++ b/chromium/components/metrics/proto/system_profile.proto
@@ -281,7 +281,7 @@ message SystemProfileProto {
optional Hardware hardware = 6;
// Information about the network connection.
- // Next tag: 7
+ // Next tag: 9
message Network {
// Set to true if connection_type changed during the lifetime of the log.
optional bool connection_type_is_ambiguous = 1;
@@ -372,22 +372,34 @@ message SystemProfileProto {
// translated through NetworkMetricsProvider::GetConnectionType.
enum EffectiveConnectionType {
EFFECTIVE_CONNECTION_TYPE_UNKNOWN = 0;
- // Specifies that the connection_type changed during the lifetime of the
- // log.
- EFFECTIVE_CONNECTION_TYPE_AMBIGUOUS = 1;
- EFFECTIVE_CONNECTION_TYPE_OFFLINE = 2;
+ // Deprecated: Specifies that the connection_type changed during the
+ // lifetime of the log.
+ DEPRECATED_EFFECTIVE_CONNECTION_TYPE_AMBIGUOUS = 1 [deprecated = true];
+ DEPRECATED_EFFECTIVE_CONNECTION_TYPE_OFFLINE = 2 [deprecated = true];
EFFECTIVE_CONNECTION_TYPE_SLOW_2G = 3;
EFFECTIVE_CONNECTION_TYPE_2G = 4;
EFFECTIVE_CONNECTION_TYPE_3G = 5;
EFFECTIVE_CONNECTION_TYPE_4G = 6;
}
- // The connection type according to net::NetworkQualityEstimator.
+ // Deprecated: The connection type according to
+ // net::NetworkQualityEstimator. EffectiveConnectionType is the connection
+ // type whose typical performance is most similar to the measured
+ // performance of the network in use. In many cases, the "effective"
+ // connection type and the actual type of connection in use are the same,
+ // but often a network connection performs significantly differently,
+ // usually worse, from its expected capabilities.
+ optional EffectiveConnectionType deprecated_effective_connection_type = 6
+ [deprecated = true];
+
+ // The minimum and maximum values of the effective connection type enum
+ // during the lifetime of the log according to net::NetworkQualityEstimator.
// EffectiveConnectionType is the connection type whose typical performance
// is most similar to the measured performance of the network in use. In
// many cases, the "effective" connection type and the actual type of
// connection in use are the same, but often a network connection performs
// significantly differently, usually worse, from its expected capabilities.
- optional EffectiveConnectionType effective_connection_type = 6;
+ optional EffectiveConnectionType min_effective_connection_type = 7;
+ optional EffectiveConnectionType max_effective_connection_type = 8;
}
optional Network network = 13;
diff --git a/chromium/components/metrics/proto/translate_event.proto b/chromium/components/metrics/proto/translate_event.proto
index fb9717372c8..c2ced5c8849 100644
--- a/chromium/components/metrics/proto/translate_event.proto
+++ b/chromium/components/metrics/proto/translate_event.proto
@@ -67,6 +67,7 @@ message TranslateEventProto {
optional RankerResponse ranker_response = 9;
// The action performed by the user in the translate UI.
+ // Next tag 27.
enum EventType {
// The feedback event does not correspond to any of the enumerated
// cases.
@@ -92,9 +93,15 @@ message TranslateEventProto {
// The user reverted the translation.
USER_REVERT = 9;
+ // Deprecated. Use AUTO_TRANSLATION_BY_PREF or AUTO_TRANSLATION_BY_LINK
+ // instead.
+ AUTOMATICALLY_TRANSLATED = 10;
// Automated feedback.
// An automatic translation was triggered.
- AUTOMATICALLY_TRANSLATED = 10;
+ // User sets always translate in user settings.
+ AUTO_TRANSLATION_BY_PREF = 25;
+ // User navigated through a click from a translated page.
+ AUTO_TRANSLATION_BY_LINK = 26;
// The translation was not offered because translate is disabled
// globally in the user preferences.
DISABLED_BY_PREF = 11;
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 ddf78d5f3c9..6a3af7cf363 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
@@ -5,9 +5,11 @@
// Defines StructTraits specializations for translating between mojo types and
// base::StackSamplingProfiler types, with data validity checks.
-#ifndef COMPONENTS_METRICS_CALL_STACK_PROFILE_STRUCT_TRAITS_H_
-#define COMPONENTS_METRICS_CALL_STACK_PROFILE_STRUCT_TRAITS_H_
+#ifndef COMPONENTS_METRICS_PUBLIC_CPP_CALL_STACK_PROFILE_STRUCT_TRAITS_H_
+#define COMPONENTS_METRICS_PUBLIC_CPP_CALL_STACK_PROFILE_STRUCT_TRAITS_H_
+#include <string>
+#include <utility>
#include <vector>
#include "base/files/file_path.h"
@@ -307,6 +309,8 @@ struct EnumTraits<metrics::mojom::Trigger,
return metrics::mojom::Trigger::JANKY_TASK;
case metrics::CallStackProfileParams::Trigger::THREAD_HUNG:
return metrics::mojom::Trigger::THREAD_HUNG;
+ case metrics::CallStackProfileParams::Trigger::PERIODIC_COLLECTION:
+ return metrics::mojom::Trigger::PERIODIC_COLLECTION;
}
NOTREACHED();
return metrics::mojom::Trigger::UNKNOWN;
@@ -327,6 +331,9 @@ struct EnumTraits<metrics::mojom::Trigger,
case metrics::mojom::Trigger::THREAD_HUNG:
*out = metrics::CallStackProfileParams::Trigger::THREAD_HUNG;
return true;
+ case metrics::mojom::Trigger::PERIODIC_COLLECTION:
+ *out = metrics::CallStackProfileParams::Trigger::PERIODIC_COLLECTION;
+ return true;
}
return false;
}
@@ -371,7 +378,6 @@ struct StructTraits<metrics::mojom::CallStackProfileParamsDataView,
template <>
struct EnumTraits<metrics::mojom::SampleOrderingSpec,
metrics::CallStackProfileParams::SampleOrderingSpec> {
-
static metrics::mojom::SampleOrderingSpec ToMojom(
metrics::CallStackProfileParams::SampleOrderingSpec spec) {
switch (spec) {
@@ -400,6 +406,6 @@ struct EnumTraits<metrics::mojom::SampleOrderingSpec,
}
};
-} // mojo
+} // namespace mojo
-#endif // COMPONENTS_METRICS_CALL_STACK_PROFILE_STRUCT_TRAITS_H_
+#endif // COMPONENTS_METRICS_PUBLIC_CPP_CALL_STACK_PROFILE_STRUCT_TRAITS_H_
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 ca3fe794e3b..3573f906410 100644
--- a/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom
+++ b/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom
@@ -66,6 +66,7 @@ enum Trigger {
PROCESS_STARTUP,
JANKY_TASK,
THREAD_HUNG,
+ PERIODIC_COLLECTION,
};
enum SampleOrderingSpec {
diff --git a/chromium/components/metrics/single_sample_metrics.cc b/chromium/components/metrics/single_sample_metrics.cc
index 0472dc8aa5f..0f16c492ab7 100644
--- a/chromium/components/metrics/single_sample_metrics.cc
+++ b/chromium/components/metrics/single_sample_metrics.cc
@@ -77,7 +77,6 @@ void InitializeSingleSampleMetricsFactory(CreateProviderCB create_provider_cb) {
// static
void CreateSingleSampleMetricsProvider(
- const service_manager::BindSourceInfo& source_info,
mojom::SingleSampleMetricsProviderRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<MojoSingleSampleMetricsProvider>(),
std::move(request));
diff --git a/chromium/components/metrics/single_sample_metrics.h b/chromium/components/metrics/single_sample_metrics.h
index cd7ab33f927..989f6a29b99 100644
--- a/chromium/components/metrics/single_sample_metrics.h
+++ b/chromium/components/metrics/single_sample_metrics.h
@@ -7,7 +7,6 @@
#include "base/callback.h"
#include "components/metrics/public/interfaces/single_sample_metrics.mojom.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
namespace metrics {
@@ -35,7 +34,6 @@ extern void InitializeSingleSampleMetricsFactory(
// that has a deterministic shutdown path and which serves as a stable endpoint
// for the factory created by the above initialize method in another process.
extern void CreateSingleSampleMetricsProvider(
- const service_manager::BindSourceInfo& source_info,
mojom::SingleSampleMetricsProviderRequest request);
} // namespace metrics
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 a649b5df789..165ad99ba9e 100644
--- a/chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc
+++ b/chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc
@@ -10,7 +10,6 @@
#include "base/test/histogram_tester.h"
#include "base/threading/thread.h"
#include "components/metrics/single_sample_metrics.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
@@ -51,8 +50,7 @@ class SingleSampleMetricsFactoryImplTest : public testing::Test {
}
void CreateProvider(mojom::SingleSampleMetricsProviderRequest request) {
- CreateSingleSampleMetricsProvider(service_manager::BindSourceInfo(),
- std::move(request));
+ CreateSingleSampleMetricsProvider(std::move(request));
provider_count_++;
}
diff --git a/chromium/components/metrics/stability_metrics_helper.cc b/chromium/components/metrics/stability_metrics_helper.cc
index f6254872b03..82b31d271eb 100644
--- a/chromium/components/metrics/stability_metrics_helper.cc
+++ b/chromium/components/metrics/stability_metrics_helper.cc
@@ -170,8 +170,6 @@ void StabilityMetricsHelper::BrowserChildProcessCrashed() {
void StabilityMetricsHelper::LogLoadStarted() {
base::RecordAction(base::UserMetricsAction("PageLoad"));
- // TODO(asvitkine): Check if this is used for anything and if not, remove.
- LOCAL_HISTOGRAM_BOOLEAN("Chrome.UmaPageloadCounter", true);
IncrementPrefValue(prefs::kStabilityPageLoadCount);
IncrementLongPrefsValue(prefs::kUninstallMetricsPageLoadCount);
// We need to save the prefs, as page load count is a critical stat, and it
diff --git a/chromium/components/metrics/version_utils.cc b/chromium/components/metrics/version_utils.cc
new file mode 100644
index 00000000000..d24932b1cf7
--- /dev/null
+++ b/chromium/components/metrics/version_utils.cc
@@ -0,0 +1,40 @@
+// 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/metrics/version_utils.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "components/version_info/version_info.h"
+
+namespace metrics {
+
+std::string GetVersionString() {
+ std::string version = version_info::GetVersionNumber();
+#if defined(ARCH_CPU_64_BITS)
+ version += "-64";
+#endif // defined(ARCH_CPU_64_BITS)
+ if (!version_info::IsOfficialBuild())
+ version.append("-devel");
+ return version;
+}
+
+SystemProfileProto::Channel AsProtobufChannel(version_info::Channel channel) {
+ switch (channel) {
+ case version_info::Channel::UNKNOWN:
+ return SystemProfileProto::CHANNEL_UNKNOWN;
+ case version_info::Channel::CANARY:
+ return SystemProfileProto::CHANNEL_CANARY;
+ case version_info::Channel::DEV:
+ return SystemProfileProto::CHANNEL_DEV;
+ case version_info::Channel::BETA:
+ return SystemProfileProto::CHANNEL_BETA;
+ case version_info::Channel::STABLE:
+ return SystemProfileProto::CHANNEL_STABLE;
+ }
+ NOTREACHED();
+ return SystemProfileProto::CHANNEL_UNKNOWN;
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/version_utils.h b/chromium/components/metrics/version_utils.h
new file mode 100644
index 00000000000..3daecc7b724
--- /dev/null
+++ b/chromium/components/metrics/version_utils.h
@@ -0,0 +1,28 @@
+// 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_METRICS_VERSION_UTILS_H_
+#define COMPONENTS_METRICS_VERSION_UTILS_H_
+
+#include <string>
+
+#include "components/metrics/proto/system_profile.pb.h"
+
+namespace version_info {
+enum class Channel;
+}
+
+namespace metrics {
+
+// Build a string including the Chrome app version, suffixed by "-64" on 64-bit
+// platforms, and "-devel" on developer builds.
+std::string GetVersionString();
+
+// Translates version_info::Channel to the equivalent
+// SystemProfileProto::Channel.
+SystemProfileProto::Channel AsProtobufChannel(version_info::Channel channel);
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_VERSION_UTILS_H_
diff --git a/chromium/components/metrics_services_manager/metrics_services_manager.cc b/chromium/components/metrics_services_manager/metrics_services_manager.cc
index 4659d8c30aa..0bd70581f81 100644
--- a/chromium/components/metrics_services_manager/metrics_services_manager.cc
+++ b/chromium/components/metrics_services_manager/metrics_services_manager.cc
@@ -6,10 +6,12 @@
#include <utility>
+#include "base/command_line.h"
#include "base/logging.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_service_client.h"
#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/metrics_switches.h"
#include "components/metrics_services_manager/metrics_services_manager_client.h"
#include "components/rappor/rappor_service_impl.h"
#include "components/ukm/ukm_service.h"
@@ -103,7 +105,8 @@ void MetricsServicesManager::UpdateRunningServices() {
DCHECK(thread_checker_.CalledOnValidThread());
metrics::MetricsService* metrics = GetMetricsService();
- if (client_->OnlyDoMetricsRecording()) {
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ if (cmdline->HasSwitch(metrics::switches::kMetricsRecordingOnly)) {
metrics->StartRecordingForTests();
GetRapporServiceImpl()->Update(true, false);
return;
diff --git a/chromium/components/metrics_services_manager/metrics_services_manager_client.h b/chromium/components/metrics_services_manager/metrics_services_manager_client.h
index 1b4e9a81907..351fc67be88 100644
--- a/chromium/components/metrics_services_manager/metrics_services_manager_client.h
+++ b/chromium/components/metrics_services_manager/metrics_services_manager_client.h
@@ -51,9 +51,6 @@ class MetricsServicesManagerClient {
// Returns whether metrics reporting is enabled.
virtual bool IsMetricsReportingEnabled() = 0;
- // Whether the metrics services should record but not report metrics.
- virtual bool OnlyDoMetricsRecording() = 0;
-
// Returns whether there are any Incognito browsers/tabs open.
virtual bool IsIncognitoSessionActive() = 0;
diff --git a/chromium/components/nacl/loader/BUILD.gn b/chromium/components/nacl/loader/BUILD.gn
index 74edc91a3fa..1eea0cea4eb 100644
--- a/chromium/components/nacl/loader/BUILD.gn
+++ b/chromium/components/nacl/loader/BUILD.gn
@@ -120,7 +120,7 @@ if (is_linux) {
deps = [
":loader",
"//base",
- "//build/config/sanitizers:deps_no_options",
+ "//build/config:exe_and_shlib_deps",
"//components/nacl/common:switches",
"//components/nacl/loader/sandbox_linux",
"//content/public/common",
diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.cc b/chromium/components/navigation_interception/intercept_navigation_delegate.cc
index b8cd316f64b..b6a3b119c7f 100644
--- a/chromium/components/navigation_interception/intercept_navigation_delegate.cc
+++ b/chromium/components/navigation_interception/intercept_navigation_delegate.cc
@@ -89,7 +89,7 @@ std::unique_ptr<content::NavigationThrottle>
InterceptNavigationDelegate::CreateThrottleFor(
content::NavigationHandle* handle) {
return base::MakeUnique<InterceptNavigationThrottle>(
- handle, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread), false);
+ handle, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread));
}
// static
diff --git a/chromium/components/navigation_interception/intercept_navigation_throttle.cc b/chromium/components/navigation_interception/intercept_navigation_throttle.cc
index 25875f0ae70..db7a1dd14ae 100644
--- a/chromium/components/navigation_interception/intercept_navigation_throttle.cc
+++ b/chromium/components/navigation_interception/intercept_navigation_throttle.cc
@@ -12,41 +12,11 @@ using content::BrowserThread;
namespace navigation_interception {
-namespace {
-
-using ChecksPerformedCallback = base::Callback<void(bool, bool)>;
-
-// This is used to run |should_ignore_callback| if it can destroy the
-// WebContents (and the InterceptNavigationThrottle along). In that case,
-// |on_checks_performed_callback| will be a no-op.
-void RunCallback(
- content::WebContents* web_contents,
- const NavigationParams& navigation_params,
- InterceptNavigationThrottle::CheckCallback should_ignore_callback,
- ChecksPerformedCallback on_checks_performed_callback,
- base::WeakPtr<InterceptNavigationThrottle> throttle) {
- bool should_ignore_navigation =
- should_ignore_callback.Run(web_contents, navigation_params);
-
- // If the InterceptNavigationThrottle that called RunCallback is still alive
- // after |should_ignore_callback| has run, this will run
- // InterceptNavigationThrottle::OnAsynchronousChecksPerformed.
- // TODO(clamy): remove this boolean after crbug.com/570200 is fixed.
- bool throttle_was_destroyed = !throttle.get();
- on_checks_performed_callback.Run(should_ignore_navigation,
- throttle_was_destroyed);
-}
-
-} // namespace
-
InterceptNavigationThrottle::InterceptNavigationThrottle(
content::NavigationHandle* navigation_handle,
- CheckCallback should_ignore_callback,
- bool run_callback_synchronously)
+ CheckCallback should_ignore_callback)
: content::NavigationThrottle(navigation_handle),
- should_ignore_callback_(should_ignore_callback),
- run_callback_synchronously_(run_callback_synchronously),
- weak_factory_(this) {}
+ should_ignore_callback_(should_ignore_callback) {}
InterceptNavigationThrottle::~InterceptNavigationThrottle() {}
@@ -75,62 +45,11 @@ InterceptNavigationThrottle::CheckIfShouldIgnoreNavigation(bool is_redirect) {
navigation_handle()->GetPageTransition(), is_redirect,
navigation_handle()->IsExternalProtocol(), true,
navigation_handle()->GetBaseURLForDataURL());
-
- if (run_callback_synchronously_) {
- 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;
- }
-
- // When the callback can potentially destroy the WebContents, along with the
- // NavigationHandle and this InterceptNavigationThrottle, it should be run
- // asynchronously. This will ensure that no objects on the stack can be
- // deleted, and that the stack does not unwind through them in a deleted
- // state.
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&InterceptNavigationThrottle::RunCallbackAsynchronously,
- weak_factory_.GetWeakPtr(), navigation_params));
- return DEFER;
-}
-
-void InterceptNavigationThrottle::RunCallbackAsynchronously(
- const NavigationParams& navigation_params) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
- // Run the callback in a helper function as it may lead ot the destruction of
- // this InterceptNavigationThrottle.
- RunCallback(
- navigation_handle()->GetWebContents(), navigation_params,
- should_ignore_callback_,
- base::Bind(&InterceptNavigationThrottle::OnAsynchronousChecksPerformed,
- weak_factory_.GetWeakPtr()),
- weak_factory_.GetWeakPtr());
-
- // DO NOT ADD CODE AFTER HERE: at this point the InterceptNavigationThrottle
- // may have been destroyed by the |should_ignore_callback_|. Adding code here
- // will cause use-after-free bugs.
- //
- // Code that needs to act on the result of the |should_ignore_callback_|
- // should be put inside OnAsynchronousChecksPerformed. This function will be
- // called after |should_ignore_callback_| has run, if this
- // InterceptNavigationThrottle is still alive.
-}
-
-void InterceptNavigationThrottle::OnAsynchronousChecksPerformed(
- bool should_ignore_navigation,
- bool throttle_was_destroyed) {
- CHECK(!throttle_was_destroyed);
- content::NavigationHandle* handle = navigation_handle();
- CHECK(handle);
- if (should_ignore_navigation) {
- navigation_handle()->CancelDeferredNavigation(
- content::NavigationThrottle::CANCEL_AND_IGNORE);
- } else {
- handle->Resume();
- }
+ 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 d8aee3458f8..7607fd5f523 100644
--- a/chromium/components/navigation_interception/intercept_navigation_throttle.h
+++ b/chromium/components/navigation_interception/intercept_navigation_throttle.h
@@ -30,8 +30,7 @@ class InterceptNavigationThrottle : public content::NavigationThrottle {
CheckCallback;
InterceptNavigationThrottle(content::NavigationHandle* navigation_handle,
- CheckCallback should_ignore_callback,
- bool run_callback_synchronously);
+ CheckCallback should_ignore_callback);
~InterceptNavigationThrottle() override;
// content::NavigationThrottle implementation:
@@ -42,22 +41,8 @@ class InterceptNavigationThrottle : public content::NavigationThrottle {
private:
ThrottleCheckResult CheckIfShouldIgnoreNavigation(bool is_redirect);
- // Called to perform the checks asynchronously
- void RunCallbackAsynchronously(const NavigationParams& navigation_params);
- // TODO(clamy): remove |throttle_was_destroyed| once crbug.com/570200 is
- // fixed.
- void OnAsynchronousChecksPerformed(bool should_ignore_navigation,
- bool throttle_was_destroyed);
-
CheckCallback should_ignore_callback_;
- // Whether the callback will be run synchronously or not. If the callback can
- // lead to the destruction of the WebContents, this should be false.
- // Otherwise this should be true.
- const bool run_callback_synchronously_;
-
- 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 a6d27c4945d..0b3ed14820b 100644
--- a/chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc
+++ b/chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc
@@ -68,8 +68,7 @@ class InterceptNavigationThrottleTest
base::MakeUnique<InterceptNavigationThrottle>(
test_handle.get(),
base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
- base::Unretained(mock_callback_receiver_.get())),
- true));
+ base::Unretained(mock_callback_receiver_.get()))));
return test_handle->CallWillStartRequestForTesting(
is_post, content::Referrer(), false, ui::PAGE_TRANSITION_LINK, false);
}
@@ -82,8 +81,7 @@ class InterceptNavigationThrottleTest
base::MakeUnique<InterceptNavigationThrottle>(
test_handle.get(),
base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
- base::Unretained(mock_callback_receiver_.get())),
- true));
+ base::Unretained(mock_callback_receiver_.get()))));
test_handle->CallWillStartRequestForTesting(
true, content::Referrer(), false, ui::PAGE_TRANSITION_LINK, false);
return test_handle->CallWillRedirectRequestForTesting(GURL(kTestUrl), false,
diff --git a/chromium/components/navigation_metrics/BUILD.gn b/chromium/components/navigation_metrics/BUILD.gn
index 3908ff129a7..7325fcb3650 100644
--- a/chromium/components/navigation_metrics/BUILD.gn
+++ b/chromium/components/navigation_metrics/BUILD.gn
@@ -10,6 +10,7 @@ static_library("navigation_metrics") {
deps = [
"//base",
+ "//components/dom_distiller/core:core",
"//url",
]
}
diff --git a/chromium/components/navigation_metrics/DEPS b/chromium/components/navigation_metrics/DEPS
index f0bf3d9693e..7b83d07ef10 100644
--- a/chromium/components/navigation_metrics/DEPS
+++ b/chromium/components/navigation_metrics/DEPS
@@ -1,3 +1,3 @@
include_rules = [
- "+components/keyed_service/core",
+ "+components/dom_distiller/core",
]
diff --git a/chromium/components/navigation_metrics/navigation_metrics.cc b/chromium/components/navigation_metrics/navigation_metrics.cc
index 78df32462d8..f63b6c4244b 100644
--- a/chromium/components/navigation_metrics/navigation_metrics.cc
+++ b/chromium/components/navigation_metrics/navigation_metrics.cc
@@ -6,24 +6,33 @@
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
+#include "components/dom_distiller/core/url_constants.h"
#include "url/gurl.h"
namespace {
-// This enum is used in building the histogram. So, this is append only,
-// any new scheme should be added at the end, before SCHEME_MAX
+// These values are written to logs. New enum values can be added, but existing
+// enums must never be renumbered or deleted and reused. Any new scheme should
+// be added at the end, before SCHEME_MAX.
enum Scheme {
- SCHEME_UNKNOWN,
- SCHEME_HTTP,
- SCHEME_HTTPS,
- SCHEME_FILE,
- SCHEME_FTP,
- SCHEME_DATA,
- SCHEME_JAVASCRIPT,
- SCHEME_ABOUT,
- SCHEME_CHROME,
- SCHEME_BLOB,
- SCHEME_FILESYSTEM,
+ SCHEME_UNKNOWN = 0,
+ SCHEME_HTTP = 1,
+ SCHEME_HTTPS = 2,
+ SCHEME_FILE = 3,
+ SCHEME_FTP = 4,
+ SCHEME_DATA = 5,
+ SCHEME_JAVASCRIPT = 6,
+ SCHEME_ABOUT = 7,
+ SCHEME_CHROME = 8,
+ SCHEME_BLOB = 9,
+ SCHEME_FILESYSTEM = 10,
+ SCHEME_CHROME_NATIVE = 11,
+ SCHEME_CHROME_SEARCH = 12,
+ SCHEME_CHROME_DISTILLER = 13,
+ SCHEME_CHROME_DEVTOOLS = 14,
+ SCHEME_CHROME_EXTENSION = 15,
+ SCHEME_VIEW_SOURCE = 16,
+ SCHEME_EXTERNALFILE = 17,
SCHEME_MAX,
};
@@ -39,11 +48,17 @@ const char* const kSchemeNames[] = {
"chrome",
url::kBlobScheme,
url::kFileSystemScheme,
- "max",
+ "chrome-native",
+ "chrome-search",
+ dom_distiller::kDomDistillerScheme,
+ "chrome-devtools",
+ "chrome-extension",
+ "view-source",
+ "externalfile",
};
-static_assert(arraysize(kSchemeNames) == SCHEME_MAX + 1,
- "kSchemeNames should have SCHEME_MAX + 1 elements");
+static_assert(arraysize(kSchemeNames) == SCHEME_MAX,
+ "kSchemeNames should have SCHEME_MAX elements");
} // namespace
diff --git a/chromium/components/net_log/BUILD.gn b/chromium/components/net_log/BUILD.gn
index fd7d129599f..133894e8046 100644
--- a/chromium/components/net_log/BUILD.gn
+++ b/chromium/components/net_log/BUILD.gn
@@ -6,10 +6,10 @@ static_library("net_log") {
sources = [
"chrome_net_log.cc",
"chrome_net_log.h",
+ "net_export_file_writer.cc",
+ "net_export_file_writer.h",
"net_export_ui_constants.cc",
"net_export_ui_constants.h",
- "net_log_file_writer.cc",
- "net_log_file_writer.h",
]
deps = [
@@ -23,7 +23,7 @@ static_library("net_log") {
source_set("unit_tests") {
testonly = true
sources = [
- "net_log_file_writer_unittest.cc",
+ "net_export_file_writer_unittest.cc",
]
deps = [
":net_log",
diff --git a/chromium/components/net_log/chrome_net_log.cc b/chromium/components/net_log/chrome_net_log.cc
index 5ec2e55a7c5..8c84541335b 100644
--- a/chromium/components/net_log/chrome_net_log.cc
+++ b/chromium/components/net_log/chrome_net_log.cc
@@ -4,73 +4,52 @@
#include "components/net_log/chrome_net_log.h"
-#include <stdio.h>
#include <utility>
#include "base/command_line.h"
-#include "base/files/scoped_file.h"
-#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
-#include "components/net_log/net_log_file_writer.h"
+#include "components/net_log/net_export_file_writer.h"
#include "components/version_info/version_info.h"
+#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_util.h"
#include "net/log/trace_net_log_observer.h"
-#include "net/log/write_to_file_net_log_observer.h"
namespace net_log {
-ChromeNetLog::ChromeNetLog(
- const base::FilePath& log_file,
- net::NetLogCaptureMode log_file_mode,
- const base::CommandLine::StringType& command_line_string,
- const std::string& channel_string)
- : net_log_file_writer_(
- new NetLogFileWriter(this, command_line_string, channel_string)) {
- if (!log_file.empty()) {
- // Much like logging.h, bypass threading restrictions by using fopen
- // directly. Have to write on a thread that's shutdown to handle events on
- // shutdown properly, and posting events to another thread as they occur
- // would result in an unbounded buffer size, so not much can be gained by
- // doing this on another thread. It's only used when debugging Chrome, so
- // performance is not a big concern.
- base::ScopedFILE file;
-#if defined(OS_WIN)
- file.reset(_wfopen(log_file.value().c_str(), L"w"));
-#elif defined(OS_POSIX)
- file.reset(fopen(log_file.value().c_str(), "w"));
-#endif
-
- if (!file) {
- LOG(ERROR) << "Could not open file " << log_file.value()
- << " for net logging";
- } else {
- std::unique_ptr<base::Value> constants(
- GetConstants(command_line_string, channel_string));
- write_to_file_observer_.reset(new net::WriteToFileNetLogObserver());
-
- write_to_file_observer_->set_capture_mode(log_file_mode);
-
- write_to_file_observer_->StartObserving(this, std::move(file),
- constants.get(), nullptr);
- }
- }
-
+ChromeNetLog::ChromeNetLog() {
trace_net_log_observer_.reset(new net::TraceNetLogObserver());
trace_net_log_observer_->WatchForTraceStart(this);
}
ChromeNetLog::~ChromeNetLog() {
- net_log_file_writer_.reset();
- // Remove the observers we own before we're destroyed.
- if (write_to_file_observer_)
- write_to_file_observer_->StopObserving(nullptr);
- if (trace_net_log_observer_)
- trace_net_log_observer_->StopWatchForTraceStart();
+ net_export_file_writer_.reset();
+ ClearFileNetLogObserver();
+ trace_net_log_observer_->StopWatchForTraceStart();
+}
+
+void ChromeNetLog::StartWritingToFile(
+ const base::FilePath& path,
+ net::NetLogCaptureMode capture_mode,
+ const base::CommandLine::StringType& command_line_string,
+ const std::string& channel_string) {
+ DCHECK(!path.empty());
+
+ // TODO(739485): The log file does not contain about:flags data.
+ file_net_log_observer_ = net::FileNetLogObserver::CreateUnbounded(
+ path, GetConstants(command_line_string, channel_string));
+
+ file_net_log_observer_->StartObserving(this, capture_mode);
+}
+
+NetExportFileWriter* ChromeNetLog::net_export_file_writer() {
+ if (!net_export_file_writer_)
+ net_export_file_writer_ = base::WrapUnique(new NetExportFileWriter(this));
+ return net_export_file_writer_.get();
}
// static
@@ -107,4 +86,29 @@ std::unique_ptr<base::Value> ChromeNetLog::GetConstants(
return std::move(constants_dict);
}
+void ChromeNetLog::ShutDownBeforeTaskScheduler() {
+ // TODO(eroman): Stop in-progress net_export_file_writer_ or delete its files?
+
+ ClearFileNetLogObserver();
+}
+
+void ChromeNetLog::ClearFileNetLogObserver() {
+ if (!file_net_log_observer_)
+ return;
+
+ // TODO(739487): The log file does not contain any polled data.
+ //
+ // TODO(eroman): FileNetLogObserver::StopObserving() posts to the file task
+ // runner to finish writing the log file. Despite that sequenced task runner
+ // being marked BLOCK_SHUTDOWN, those tasks are not actually running.
+ //
+ // This isn't a big deal when using the unbounded logger since the log
+ // loading code can handle such truncated logs. But this will need fixing
+ // if switching to log formats that are not so versatile (also if adding
+ // polled data).
+ file_net_log_observer_->StopObserving(nullptr /*polled_data*/,
+ base::Closure());
+ file_net_log_observer_.reset();
+}
+
} // namespace net_log
diff --git a/chromium/components/net_log/chrome_net_log.h b/chromium/components/net_log/chrome_net_log.h
index bca0d2889df..cfa0298bf97 100644
--- a/chromium/components/net_log/chrome_net_log.h
+++ b/chromium/components/net_log/chrome_net_log.h
@@ -18,29 +18,37 @@ class Value;
}
namespace net {
-class WriteToFileNetLogObserver;
+class FileNetLogObserver;
class TraceNetLogObserver;
}
namespace net_log {
-class NetLogFileWriter;
+class NetExportFileWriter;
-// ChromeNetLog is an implementation of NetLog that adds file loggers
-// as its observers.
+// ChromeNetLog is an implementation of NetLog that manages common observers
+// (for --log-net-log, chrome://net-export/, tracing), as well as acting as the
+// entry point for other consumers.
+//
+// Threading:
+// * The methods on net::NetLog are threadsafe
+// * The methods defined by ChromeNetLog must be sequenced.
class ChromeNetLog : public net::NetLog {
public:
- // The log is saved to |log_file|.
- // |log_file_mode| is the mode used to log in |log_file|.
- // If |log_file| is empty, only a temporary log is created, and
- // |log_file_mode| is not used.
- ChromeNetLog(const base::FilePath& log_file,
- net::NetLogCaptureMode log_file_mode,
- const base::CommandLine::StringType& command_line_string,
- const std::string& channel_string);
+ ChromeNetLog();
~ChromeNetLog() override;
- NetLogFileWriter* net_log_file_writer() { return net_log_file_writer_.get(); }
+ // Starts streaming the NetLog events to a file on disk. This will continue
+ // until the application shuts down.
+ // * |path| - destination file path of the log file.
+ // * |capture_mode| - capture mode for event granularity.
+ void StartWritingToFile(
+ const base::FilePath& path,
+ net::NetLogCaptureMode capture_mode,
+ const base::CommandLine::StringType& command_line_string,
+ const std::string& channel_string);
+
+ NetExportFileWriter* net_export_file_writer();
// Returns a Value containing constants needed to load a log file.
// Safe to call on any thread.
@@ -48,9 +56,31 @@ class ChromeNetLog : public net::NetLog {
const base::CommandLine::StringType& command_line_string,
const std::string& channel_string);
+ // Notify the ChromeNetLog that things are shutting-down.
+ //
+ // If ChromeNetLog does not outlive the TaskScheduler, there is no need to
+ // call this.
+ //
+ // However, if it can outlive the TaskScheduler, this should be called
+ // before the TaskScheduler is shutdown. This allows for any file writers
+ // using BLOCK_SHUTDOWN to finish posting their writes.
+ //
+ // Not calling this is not a fatal error, however may result in an incomplete
+ // NetLog file being written to disk.
+ void ShutDownBeforeTaskScheduler();
+
private:
- std::unique_ptr<net::WriteToFileNetLogObserver> write_to_file_observer_;
- std::unique_ptr<NetLogFileWriter> net_log_file_writer_;
+ // Deletes file_net_log_observer_.
+ void ClearFileNetLogObserver();
+
+ // This observer handles writing NetLogs specified via StartWritingToFile()
+ // (In Chrome this corresponds to the --log-net-log command line).
+ std::unique_ptr<net::FileNetLogObserver> file_net_log_observer_;
+
+ // This observer handles writing NetLogs started by chrome://net-export/
+ std::unique_ptr<NetExportFileWriter> net_export_file_writer_;
+
+ // This observer forwards NetLog events to the chrome://tracing system.
std::unique_ptr<net::TraceNetLogObserver> trace_net_log_observer_;
DISALLOW_COPY_AND_ASSIGN(ChromeNetLog);
diff --git a/chromium/components/net_log/net_export_file_writer.cc b/chromium/components/net_log/net_export_file_writer.cc
new file mode 100644
index 00000000000..81d34d036fd
--- /dev/null
+++ b/chromium/components/net_log/net_export_file_writer.cc
@@ -0,0 +1,370 @@
+// Copyright (c) 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/net_log/net_export_file_writer.h"
+
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/net_log/chrome_net_log.h"
+#include "net/log/file_net_log_observer.h"
+#include "net/log/net_log_util.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace net {
+class URLRequestContext;
+}
+
+namespace net_log {
+
+namespace {
+
+// Path of logs relative to default temporary directory given by
+// base::GetTempDir(). Must be kept in sync with
+// chrome/android/java/res/xml/file_paths.xml. Only used if not saving log file
+// to a custom path.
+const base::FilePath::CharType kLogRelativePath[] =
+ FILE_PATH_LITERAL("net-export/chrome-net-export-log.json");
+
+// Contains file-related initialization tasks for NetExportFileWriter.
+NetExportFileWriter::DefaultLogPathResults SetUpDefaultLogPath(
+ const NetExportFileWriter::DirectoryGetter& default_log_base_dir_getter) {
+ NetExportFileWriter::DefaultLogPathResults results;
+ results.default_log_path_success = false;
+ results.log_exists = false;
+
+ base::FilePath default_base_dir;
+ if (!default_log_base_dir_getter.Run(&default_base_dir))
+ return results;
+
+ results.default_log_path = default_base_dir.Append(kLogRelativePath);
+ if (!base::CreateDirectoryAndGetError(results.default_log_path.DirName(),
+ nullptr))
+ return results;
+
+ results.log_exists = base::PathExists(results.default_log_path);
+ results.default_log_path_success = true;
+ return results;
+}
+
+// Generates net log entries for ongoing events from |context_getters| and
+// adds them to |observer|.
+void CreateNetLogEntriesForActiveObjects(
+ const NetExportFileWriter::URLRequestContextGetterList& context_getters,
+ net::NetLog::ThreadSafeObserver* observer) {
+ std::set<net::URLRequestContext*> contexts;
+ for (const auto& getter : context_getters) {
+ DCHECK(getter->GetNetworkTaskRunner()->BelongsToCurrentThread());
+ contexts.insert(getter->GetURLRequestContext());
+ }
+ net::CreateNetLogEntriesForActiveObjects(contexts, observer);
+}
+
+// Adds net info from net::GetNetInfo() to |polled_data|.
+std::unique_ptr<base::DictionaryValue> AddNetInfo(
+ scoped_refptr<net::URLRequestContextGetter> context_getter,
+ std::unique_ptr<base::DictionaryValue> polled_data) {
+ DCHECK(context_getter);
+ std::unique_ptr<base::DictionaryValue> net_info = net::GetNetInfo(
+ context_getter->GetURLRequestContext(), net::NET_INFO_ALL_SOURCES);
+ if (polled_data)
+ net_info->MergeDictionary(polled_data.get());
+ return net_info;
+}
+
+// If running on a POSIX OS, this will attempt to set all the permission flags
+// of the file at |path| to 1. Will return |path| on success and the empty path
+// on failure.
+base::FilePath GetPathWithAllPermissions(const base::FilePath& path) {
+ if (!base::PathExists(path))
+ return base::FilePath();
+#if defined(OS_POSIX)
+ return base::SetPosixFilePermissions(path, base::FILE_PERMISSION_MASK)
+ ? path
+ : base::FilePath();
+#else
+ return path;
+#endif
+}
+
+} // namespace
+
+NetExportFileWriter::NetExportFileWriter(ChromeNetLog* chrome_net_log)
+ : state_(STATE_UNINITIALIZED),
+ log_exists_(false),
+ log_capture_mode_known_(false),
+ log_capture_mode_(net::NetLogCaptureMode::Default()),
+ chrome_net_log_(chrome_net_log),
+ default_log_base_dir_getter_(base::Bind(&base::GetTempDir)),
+ weak_ptr_factory_(this) {}
+
+NetExportFileWriter::~NetExportFileWriter() {
+ if (file_net_log_observer_)
+ file_net_log_observer_->StopObserving(nullptr, base::Closure());
+}
+
+void NetExportFileWriter::AddObserver(StateObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ state_observer_list_.AddObserver(observer);
+}
+
+void NetExportFileWriter::RemoveObserver(StateObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ state_observer_list_.RemoveObserver(observer);
+}
+
+void NetExportFileWriter::Initialize(
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> net_task_runner) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(file_task_runner);
+ DCHECK(net_task_runner);
+
+ if (file_task_runner_)
+ DCHECK_EQ(file_task_runner_, file_task_runner);
+ file_task_runner_ = file_task_runner;
+ if (net_task_runner_)
+ DCHECK_EQ(net_task_runner_, net_task_runner);
+ net_task_runner_ = net_task_runner;
+
+ if (state_ != STATE_UNINITIALIZED)
+ return;
+
+ state_ = STATE_INITIALIZING;
+
+ NotifyStateObserversAsync();
+
+ base::PostTaskAndReplyWithResult(
+ file_task_runner_.get(), FROM_HERE,
+ base::Bind(&SetUpDefaultLogPath, default_log_base_dir_getter_),
+ base::Bind(&NetExportFileWriter::SetStateAfterSetUpDefaultLogPath,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void NetExportFileWriter::StartNetLog(
+ const base::FilePath& log_path,
+ net::NetLogCaptureMode capture_mode,
+ size_t max_file_size,
+ const base::CommandLine::StringType& command_line_string,
+ const std::string& channel_string,
+ const URLRequestContextGetterList& context_getters) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(file_task_runner_);
+
+ if (state_ != STATE_NOT_LOGGING)
+ return;
+
+ if (!log_path.empty())
+ log_path_ = log_path;
+
+ DCHECK(!log_path_.empty());
+
+ state_ = STATE_STARTING_LOG;
+
+ NotifyStateObserversAsync();
+
+ std::unique_ptr<base::Value> constants(
+ ChromeNetLog::GetConstants(command_line_string, channel_string));
+
+ file_net_log_observer_ = net::FileNetLogObserver::CreateBounded(
+ log_path_, max_file_size, std::move(constants));
+
+ net_task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&CreateNetLogEntriesForActiveObjects, context_getters,
+ base::Unretained(file_net_log_observer_.get())),
+ base::Bind(
+ &NetExportFileWriter::StartNetLogAfterCreateEntriesForActiveObjects,
+ weak_ptr_factory_.GetWeakPtr(), capture_mode));
+}
+
+void NetExportFileWriter::StartNetLogAfterCreateEntriesForActiveObjects(
+ net::NetLogCaptureMode capture_mode) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(STATE_STARTING_LOG, state_);
+
+ state_ = STATE_LOGGING;
+ log_exists_ = true;
+ log_capture_mode_known_ = true;
+ log_capture_mode_ = capture_mode;
+
+ NotifyStateObservers();
+
+ file_net_log_observer_->StartObserving(chrome_net_log_, capture_mode);
+}
+
+void NetExportFileWriter::StopNetLog(
+ std::unique_ptr<base::DictionaryValue> polled_data,
+ scoped_refptr<net::URLRequestContextGetter> context_getter) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(net_task_runner_);
+
+ if (state_ != STATE_LOGGING)
+ return;
+
+ state_ = STATE_STOPPING_LOG;
+
+ NotifyStateObserversAsync();
+
+ if (context_getter) {
+ base::PostTaskAndReplyWithResult(
+ net_task_runner_.get(), FROM_HERE,
+ base::Bind(&AddNetInfo, context_getter, base::Passed(&polled_data)),
+ base::Bind(&NetExportFileWriter::StopNetLogAfterAddNetInfo,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ StopNetLogAfterAddNetInfo(std::move(polled_data));
+ }
+}
+
+std::unique_ptr<base::DictionaryValue> NetExportFileWriter::GetState() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+
+ dict->SetString("file", log_path_.LossyDisplayName());
+
+ base::StringPiece state_string;
+ switch (state_) {
+ case STATE_UNINITIALIZED:
+ state_string = "UNINITIALIZED";
+ break;
+ case STATE_INITIALIZING:
+ state_string = "INITIALIZING";
+ break;
+ case STATE_NOT_LOGGING:
+ state_string = "NOT_LOGGING";
+ break;
+ case STATE_STARTING_LOG:
+ state_string = "STARTING_LOG";
+ break;
+ case STATE_LOGGING:
+ state_string = "LOGGING";
+ break;
+ case STATE_STOPPING_LOG:
+ state_string = "STOPPING_LOG";
+ break;
+ }
+ dict->SetString("state", state_string);
+
+ dict->SetBoolean("logExists", log_exists_);
+ dict->SetBoolean("logCaptureModeKnown", log_capture_mode_known_);
+ dict->SetString("captureMode", CaptureModeToString(log_capture_mode_));
+
+ return dict;
+}
+
+void NetExportFileWriter::GetFilePathToCompletedLog(
+ const FilePathCallback& path_callback) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!(log_exists_ && state_ == STATE_NOT_LOGGING)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(path_callback, base::FilePath()));
+ return;
+ }
+
+ DCHECK(file_task_runner_);
+ DCHECK(!log_path_.empty());
+
+ base::PostTaskAndReplyWithResult(
+ file_task_runner_.get(), FROM_HERE,
+ base::Bind(&GetPathWithAllPermissions, log_path_), path_callback);
+}
+
+std::string NetExportFileWriter::CaptureModeToString(
+ net::NetLogCaptureMode capture_mode) {
+ if (capture_mode == net::NetLogCaptureMode::Default()) {
+ return "STRIP_PRIVATE_DATA";
+ } else if (capture_mode ==
+ net::NetLogCaptureMode::IncludeCookiesAndCredentials()) {
+ return "NORMAL";
+ } else if (capture_mode == net::NetLogCaptureMode::IncludeSocketBytes()) {
+ return "LOG_BYTES";
+ } else {
+ NOTREACHED();
+ return "STRIP_PRIVATE_DATA";
+ }
+}
+
+net::NetLogCaptureMode NetExportFileWriter::CaptureModeFromString(
+ const std::string& capture_mode_string) {
+ if (capture_mode_string == "STRIP_PRIVATE_DATA") {
+ return net::NetLogCaptureMode::Default();
+ } else if (capture_mode_string == "NORMAL") {
+ return net::NetLogCaptureMode::IncludeCookiesAndCredentials();
+ } else if (capture_mode_string == "LOG_BYTES") {
+ return net::NetLogCaptureMode::IncludeSocketBytes();
+ } else {
+ NOTREACHED();
+ return net::NetLogCaptureMode::Default();
+ }
+}
+
+void NetExportFileWriter::SetDefaultLogBaseDirectoryGetterForTest(
+ const DirectoryGetter& getter) {
+ default_log_base_dir_getter_ = getter;
+}
+
+void NetExportFileWriter::NotifyStateObservers() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ std::unique_ptr<base::DictionaryValue> state = GetState();
+ for (StateObserver& observer : state_observer_list_) {
+ observer.OnNewState(*state);
+ }
+}
+
+void NetExportFileWriter::NotifyStateObserversAsync() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&NetExportFileWriter::NotifyStateObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void NetExportFileWriter::SetStateAfterSetUpDefaultLogPath(
+ const DefaultLogPathResults& set_up_default_log_path_results) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(STATE_INITIALIZING, state_);
+
+ if (set_up_default_log_path_results.default_log_path_success) {
+ state_ = STATE_NOT_LOGGING;
+ log_path_ = set_up_default_log_path_results.default_log_path;
+ log_exists_ = set_up_default_log_path_results.log_exists;
+ DCHECK(!log_capture_mode_known_);
+ } else {
+ state_ = STATE_UNINITIALIZED;
+ }
+ NotifyStateObservers();
+}
+
+void NetExportFileWriter::StopNetLogAfterAddNetInfo(
+ std::unique_ptr<base::DictionaryValue> polled_data) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(STATE_STOPPING_LOG, state_);
+
+ file_net_log_observer_->StopObserving(
+ std::move(polled_data),
+ base::Bind(&NetExportFileWriter::ResetObserverThenSetStateNotLogging,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void NetExportFileWriter::ResetObserverThenSetStateNotLogging() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ file_net_log_observer_.reset();
+ state_ = STATE_NOT_LOGGING;
+
+ NotifyStateObservers();
+}
+
+} // namespace net_log
diff --git a/chromium/components/net_log/net_export_file_writer.h b/chromium/components/net_log/net_export_file_writer.h
new file mode 100644
index 00000000000..3d7f162b020
--- /dev/null
+++ b/chromium/components/net_log/net_export_file_writer.h
@@ -0,0 +1,249 @@
+// Copyright (c) 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_NET_LOG_NET_EXPORT_FILE_WRITER_H_
+#define COMPONENTS_NET_LOG_NET_EXPORT_FILE_WRITER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "net/log/file_net_log_observer.h"
+#include "net/log/net_log_capture_mode.h"
+
+namespace base {
+class DictionaryValue;
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+
+namespace net_log {
+
+class ChromeNetLog;
+
+// NetExportFileWriter is used exclusively as a support class for net-export.
+// It's a singleton that acts as the interface to all NetExportMessageHandlers
+// which can tell it to start or stop logging in response to user actions from
+// net-export UIs. Because it's a singleton, the logging state can be shared
+// between multiple instances of the net-export UI. Internally, it manages an
+// instance of net::FileNetLogObserver and handles the attaching/detaching of it
+// to the ChromeNetLog. This class is used by the iOS and non-iOS
+// implementations of net-export.
+//
+// NetExportFileWriter maintains the current logging state using the members
+// |state_|, |log_exists_|, |log_capture_mode_known_|, |log_capture_mode_|.
+// Its three main commands are Initialize(), StartNetLog(), and StopNetLog().
+// These are the only functions that may cause NetExportFileWriter to change
+// state. Initialize() must be called before NetExportFileWriter can process any
+// other commands. A portion of the initialization needs to run on the
+// |file_task_runner_|.
+//
+// This class is created and destroyed on the UI thread, and all public entry
+// points are to be called on the UI thread. Internally, the class may run some
+// code on the |file_task_runner_| and |net_task_runner_|.
+class NetExportFileWriter {
+ public:
+ // Special value meaning "can use an unlimited number of bytes".
+ static constexpr size_t kNoLimit = net::FileNetLogObserver::kNoLimit;
+
+ // The observer interface to be implemented by code that wishes to be notified
+ // of NetExportFileWriter's state changes.
+ class StateObserver {
+ public:
+ virtual void OnNewState(const base::DictionaryValue& state) = 0;
+ };
+
+ // Struct used to store the results of setting up the default log directory
+ // and log path.
+ struct DefaultLogPathResults {
+ bool default_log_path_success;
+ base::FilePath default_log_path;
+ bool log_exists;
+ };
+
+ using FilePathCallback = base::Callback<void(const base::FilePath&)>;
+ using DirectoryGetter = base::Callback<bool(base::FilePath*)>;
+ using URLRequestContextGetterList =
+ std::vector<scoped_refptr<net::URLRequestContextGetter>>;
+
+ ~NetExportFileWriter();
+
+ // Attaches a StateObserver. |observer| will be notified of state changes to
+ // NetExportFileWriter. State changes may occur in response to Initiailze(),
+ // StartNetLog(), or StopNetLog(). StateObserver::OnNewState() will be called
+ // asynchronously relative to the command that caused the state change.
+ // |observer| must remain alive until RemoveObserver() is called.
+ void AddObserver(StateObserver* observer);
+
+ // Detaches a StateObserver.
+ void RemoveObserver(StateObserver* observer);
+
+ // Initializes NetExportFileWriter if not initialized.
+ //
+ // Also sets the task runners used by NetExportFileWriter for doing file I/O
+ // and network I/O respectively. The task runners must not be changed once
+ // set. However, calling this function again with the same parameters is OK.
+ void Initialize(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> net_task_runner);
+
+ // Starts collecting NetLog data into the file at |log_path|. If |log_path| is
+ // empty, the default log path is used. If NetExportFileWriter is already
+ // logging, this is a no-op and |capture_mode| is ignored.
+ //
+ // |max_file_size| places a bound on how large the log file can grow. To make
+ // it grow unboundedly pass kNoLimit.
+ //
+ // |context_getters| is an optional list of URLRequestContextGetters used only
+ // to add log entries for ongoing events when logging starts. They are not
+ // used for retrieving polled data. All the contexts must be bound to the same
+ // thread.
+ void StartNetLog(const base::FilePath& log_path,
+ net::NetLogCaptureMode capture_mode,
+ size_t max_file_size,
+ const base::CommandLine::StringType& command_line_string,
+ const std::string& channel_string,
+ const URLRequestContextGetterList& context_getters);
+
+ // Stops collecting NetLog data into the file. It is a no-op if
+ // NetExportFileWriter is currently not logging.
+ //
+ // |polled_data| is a JSON dictionary that will be appended to the end of the
+ // log; it's for adding additional info to the log that aren't events.
+ // If |context_getter| is not null, then StopNetLog() will automatically
+ // append net info (from net::GetNetInfo() retrieved using |context_getter|)
+ // to |polled_data|.
+ // Note that StopNetLog() accepts (optionally) only one context getter for
+ // retrieving net polled data as opposed to StartNetLog() which accepts zero
+ // or more context getters for retrieving ongoing net events.
+ void StopNetLog(std::unique_ptr<base::DictionaryValue> polled_data,
+ scoped_refptr<net::URLRequestContextGetter> context_getter);
+
+ // Creates a DictionaryValue summary of the state of the NetExportFileWriter
+ std::unique_ptr<base::DictionaryValue> GetState() const;
+
+ // Gets the log filepath. |path_callback| will be used to notify the caller
+ // when the filepath is retrieved. |path_callback| will be executed with an
+ // empty filepath if any of the following occurs:
+ // (1) The NetExportFileWriter is not initialized.
+ // (2) The log file does not exist.
+ // (3) The NetExportFileWriter is currently logging.
+ // (4) The log file's permissions could not be set to all.
+ //
+ // |path_callback| will be executed at the end of GetFilePathToCompletedLog()
+ // asynchronously.
+ void GetFilePathToCompletedLog(const FilePathCallback& path_callback) const;
+
+ // Converts to/from the string representation of a capture mode used by
+ // net_export.js.
+ static std::string CaptureModeToString(net::NetLogCaptureMode capture_mode);
+ static net::NetLogCaptureMode CaptureModeFromString(
+ const std::string& capture_mode_string);
+
+ // Overrides the getter used to retrieve the default log base directory during
+ // initialization. Should only be used by unit tests.
+ void SetDefaultLogBaseDirectoryGetterForTest(const DirectoryGetter& getter);
+
+ protected:
+ // Constructs a NetExportFileWriter. Only one instance is created in browser
+ // process.
+ explicit NetExportFileWriter(ChromeNetLog* chrome_net_log);
+
+ private:
+ friend class ChromeNetLog;
+ friend class NetExportFileWriterTest;
+
+ // The possible logging states of NetExportFileWriter.
+ enum State {
+ STATE_UNINITIALIZED,
+ // Currently in the process of initializing.
+ STATE_INITIALIZING,
+ // Not currently logging to file.
+ STATE_NOT_LOGGING,
+ // Currently in the process of starting the log.
+ STATE_STARTING_LOG,
+ // Currently logging to file.
+ STATE_LOGGING,
+ // Currently in the process of stopping the log.
+ STATE_STOPPING_LOG,
+ };
+
+ void NotifyStateObservers();
+
+ // Posts NotifyStateObservers() to the current thread.
+ void NotifyStateObserversAsync();
+
+ // Called internally by Initialize(). Will initialize NetExportFileWriter's
+ // state variables after the default log directory is set up and the default
+ // log path is determined on the |file_task_runner_|.
+ void SetStateAfterSetUpDefaultLogPath(
+ const DefaultLogPathResults& set_up_default_log_path_results);
+
+ // Called internally by StartNetLog(). Contains tasks to be done to start
+ // logging after net log entries for ongoing events are added to the log from
+ // the |net_task_runner_|.
+ void StartNetLogAfterCreateEntriesForActiveObjects(
+ net::NetLogCaptureMode capture_mode);
+
+ // Called internally by StopNetLog(). Contains tasks to be done to stop
+ // logging after net-thread polled data is retrieved on the
+ // |net_task_runner_|.
+ void StopNetLogAfterAddNetInfo(
+ std::unique_ptr<base::DictionaryValue> polled_data);
+
+ // Contains tasks to be done after |file_net_log_observer_| has completely
+ // stopped writing.
+ void ResetObserverThenSetStateNotLogging();
+
+ // All members are accessed solely from the main thread (the thread that
+ // |thread_checker_| is bound to).
+
+ base::ThreadChecker thread_checker_;
+
+ // Task runners for file-specific and net-specific tasks that must run on a
+ // file or net thread.
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> net_task_runner_;
+
+ State state_; // Current logging state of NetExportFileWriter.
+
+ bool log_exists_; // Whether or not a log file exists on disk.
+ bool log_capture_mode_known_;
+ net::NetLogCaptureMode log_capture_mode_;
+
+ base::FilePath log_path_; // base::FilePath to the NetLog file.
+
+ // |file_net_log_observer_| watches the NetLog event stream, and
+ // sends all entries to the file created in StartNetLog().
+ std::unique_ptr<net::FileNetLogObserver> file_net_log_observer_;
+
+ // The |chrome_net_log_| is owned by the browser process, cached here to avoid
+ // using global (g_browser_process).
+ ChromeNetLog* chrome_net_log_;
+
+ // List of StateObservers to notify on state changes.
+ base::ObserverList<StateObserver, true> state_observer_list_;
+
+ // Used by unit tests to override the default log base directory retrieved
+ // during initialization. This getter is initialized to base::GetTempDir().
+ DirectoryGetter default_log_base_dir_getter_;
+
+ base::WeakPtrFactory<NetExportFileWriter> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetExportFileWriter);
+};
+
+} // namespace net_log
+
+#endif // COMPONENTS_NET_LOG_NET_LOG_FILE_WRITER_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
new file mode 100644
index 00000000000..88a1842ca3c
--- /dev/null
+++ b/chromium/components/net_log/net_export_file_writer_unittest.cc
@@ -0,0 +1,795 @@
+// Copyright (c) 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/net_log/net_export_file_writer.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/net_log/chrome_net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_network_session.h"
+#include "net/log/net_log_capture_mode.h"
+#include "net/log/net_log_event_type.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kChannelString[] = "SomeChannel";
+const size_t kMaxLogSizeBytes = 100 * 1024 * 1024; // 100MiB
+
+// Keep this in sync with kLogRelativePath defined in net_export_file_writer.cc.
+base::FilePath::CharType kLogRelativePath[] =
+ FILE_PATH_LITERAL("net-export/chrome-net-export-log.json");
+
+const char kCaptureModeDefaultString[] = "STRIP_PRIVATE_DATA";
+const char kCaptureModeIncludeCookiesAndCredentialsString[] = "NORMAL";
+const char kCaptureModeIncludeSocketBytesString[] = "LOG_BYTES";
+
+const char kStateUninitializedString[] = "UNINITIALIZED";
+const char kStateInitializingString[] = "INITIALIZING";
+const char kStateNotLoggingString[] = "NOT_LOGGING";
+const char kStateStartingLogString[] = "STARTING_LOG";
+const char kStateLoggingString[] = "LOGGING";
+const char kStateStoppingLogString[] = "STOPPING_LOG";
+} // namespace
+
+namespace net_log {
+
+// Sets |path| to |path_to_return| and always returns true. This function is
+// used to override NetExportFileWriter's usual getter for the default log base
+// directory.
+bool SetPathToGivenAndReturnTrue(const base::FilePath& path_to_return,
+ base::FilePath* path) {
+ *path = path_to_return;
+ return true;
+}
+
+// Checks the "state" string of a NetExportFileWriter state.
+WARN_UNUSED_RESULT ::testing::AssertionResult VerifyState(
+ std::unique_ptr<base::DictionaryValue> state,
+ const std::string& expected_state_string) {
+ std::string actual_state_string;
+ if (!state->GetString("state", &actual_state_string)) {
+ return ::testing::AssertionFailure()
+ << "State is missing \"state\" string.";
+ }
+ if (actual_state_string != expected_state_string) {
+ return ::testing::AssertionFailure()
+ << "\"state\" string of state does not match expected." << std::endl
+ << " Actual: " << actual_state_string << std::endl
+ << " Expected: " << expected_state_string;
+ }
+ return ::testing::AssertionSuccess();
+}
+
+// Checks all fields of a NetExportFileWriter state except possibly the
+// "captureMode" string; that field is only checked if
+// |expected_log_capture_mode_known| is true.
+WARN_UNUSED_RESULT ::testing::AssertionResult VerifyState(
+ std::unique_ptr<base::DictionaryValue> state,
+ const std::string& expected_state_string,
+ bool expected_log_exists,
+ bool expected_log_capture_mode_known,
+ const std::string& expected_log_capture_mode_string) {
+ base::DictionaryValue expected_state;
+ expected_state.SetString("state", expected_state_string);
+ expected_state.SetBoolean("logExists", expected_log_exists);
+ expected_state.SetBoolean("logCaptureModeKnown",
+ expected_log_capture_mode_known);
+ if (expected_log_capture_mode_known) {
+ expected_state.SetString("captureMode", expected_log_capture_mode_string);
+ } else {
+ state->Remove("captureMode", nullptr);
+ }
+
+ // Remove "file" field which is only added in debug mode.
+ state->Remove("file", nullptr);
+
+ std::string expected_state_json_string;
+ JSONStringValueSerializer expected_state_serializer(
+ &expected_state_json_string);
+ expected_state_serializer.Serialize(expected_state);
+
+ std::string actual_state_json_string;
+ JSONStringValueSerializer actual_state_serializer(&actual_state_json_string);
+ actual_state_serializer.Serialize(*state);
+
+ if (actual_state_json_string != expected_state_json_string) {
+ return ::testing::AssertionFailure()
+ << "State (possibly excluding \"captureMode\") does not match "
+ "expected."
+ << std::endl
+ << " Actual: " << actual_state_json_string << std::endl
+ << " Expected: " << expected_state_json_string;
+ }
+ return ::testing::AssertionSuccess();
+}
+
+WARN_UNUSED_RESULT ::testing::AssertionResult ReadCompleteLogFile(
+ const base::FilePath& log_path,
+ std::unique_ptr<base::DictionaryValue>* root) {
+ DCHECK(!log_path.empty());
+
+ if (!base::PathExists(log_path)) {
+ return ::testing::AssertionFailure()
+ << log_path.value() << " does not exist.";
+ }
+ // Parse log file contents into a dictionary
+ std::string log_string;
+ if (!base::ReadFileToString(log_path, &log_string)) {
+ return ::testing::AssertionFailure()
+ << log_path.value() << " could not be read.";
+ }
+ *root = base::DictionaryValue::From(base::JSONReader::Read(log_string));
+ if (!*root) {
+ return ::testing::AssertionFailure()
+ << "Contents of " << log_path.value()
+ << " do not form valid JSON dictionary.";
+ }
+ // Make sure the "constants" section exists
+ base::DictionaryValue* constants;
+ if (!(*root)->GetDictionary("constants", &constants)) {
+ root->reset();
+ return ::testing::AssertionFailure()
+ << log_path.value() << " is missing constants.";
+ }
+ // Make sure the "events" section exists
+ base::ListValue* events;
+ if (!(*root)->GetList("events", &events)) {
+ root->reset();
+ return ::testing::AssertionFailure()
+ << log_path.value() << " is missing events list.";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+void SetUpTestContextGetterWithQuicTimeoutInfo(
+ net::NetLog* net_log,
+ int quic_idle_connection_timeout_seconds,
+ scoped_refptr<net::TestURLRequestContextGetter>* context_getter) {
+ std::unique_ptr<net::TestURLRequestContext> context =
+ base::MakeUnique<net::TestURLRequestContext>(true);
+ context->set_net_log(net_log);
+
+ std::unique_ptr<net::HttpNetworkSession::Params> params(
+ new net::HttpNetworkSession::Params);
+ params->quic_idle_connection_timeout_seconds =
+ quic_idle_connection_timeout_seconds;
+
+ context->set_http_network_session_params(std::move(params));
+ context->Init();
+
+ *context_getter = new net::TestURLRequestContextGetter(
+ base::ThreadTaskRunnerHandle::Get(), std::move(context));
+}
+
+void SetUpTestContextGetterWithRequest(
+ net::NetLog* net_log,
+ const GURL& url,
+ net::URLRequest::Delegate* delegate,
+ scoped_refptr<net::TestURLRequestContextGetter>* context_getter,
+ std::unique_ptr<net::URLRequest>* request) {
+ auto context = base::MakeUnique<net::TestURLRequestContext>(true);
+ context->set_net_log(net_log);
+ context->Init();
+
+ *request = context->CreateRequest(url, net::IDLE, delegate,
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+ (*request)->Start();
+
+ *context_getter = new net::TestURLRequestContextGetter(
+ base::ThreadTaskRunnerHandle::Get(), std::move(context));
+}
+
+// An implementation of NetExportFileWriter::StateObserver that allows waiting
+// until it's notified of a new state.
+class TestStateObserver : public NetExportFileWriter::StateObserver {
+ public:
+ // NetExportFileWriter::StateObserver implementation
+ void OnNewState(const base::DictionaryValue& state) override {
+ test_closure_.closure().Run();
+ result_state_ = state.CreateDeepCopy();
+ }
+
+ std::unique_ptr<base::DictionaryValue> WaitForNewState() {
+ test_closure_.WaitForResult();
+ DCHECK(result_state_);
+ return std::move(result_state_);
+ }
+
+ private:
+ net::TestClosure test_closure_;
+ std::unique_ptr<base::DictionaryValue> result_state_;
+};
+
+// A class that wraps around TestClosure. Provides the ability to wait on a
+// file path callback and retrieve the result.
+class TestFilePathCallback {
+ public:
+ TestFilePathCallback()
+ : callback_(base::Bind(&TestFilePathCallback::SetResultThenNotify,
+ base::Unretained(this))) {}
+
+ const base::Callback<void(const base::FilePath&)>& callback() const {
+ return callback_;
+ }
+
+ const base::FilePath& WaitForResult() {
+ test_closure_.WaitForResult();
+ return result_;
+ }
+
+ private:
+ void SetResultThenNotify(const base::FilePath& result) {
+ result_ = result;
+ test_closure_.closure().Run();
+ }
+
+ net::TestClosure test_closure_;
+ base::FilePath result_;
+ base::Callback<void(const base::FilePath&)> callback_;
+};
+
+class NetExportFileWriterTest : public ::testing::Test {
+ public:
+ using URLRequestContextGetterList =
+ std::vector<scoped_refptr<net::URLRequestContextGetter>>;
+
+ NetExportFileWriterTest()
+ : file_writer_(&net_log_),
+ file_thread_("NetLogFileWriter file thread"),
+ net_thread_("NetLogFileWriter net thread") {}
+
+ // ::testing::Test implementation
+ void SetUp() override {
+ ASSERT_TRUE(log_temp_dir_.CreateUniqueTempDir());
+
+ // Override |file_writer_|'s default-log-base-directory-getter to
+ // a getter that returns the temp dir created for the test.
+ file_writer_.SetDefaultLogBaseDirectoryGetterForTest(
+ base::Bind(&SetPathToGivenAndReturnTrue, log_temp_dir_.GetPath()));
+
+ default_log_path_ = log_temp_dir_.GetPath().Append(kLogRelativePath);
+
+ ASSERT_TRUE(file_thread_.Start());
+ ASSERT_TRUE(net_thread_.Start());
+
+ file_writer_.AddObserver(&test_state_observer_);
+
+ ASSERT_TRUE(VerifyState(file_writer_.GetState(), kStateUninitializedString,
+ false, false, ""));
+ }
+
+ // ::testing::Test implementation
+ void TearDown() override {
+ file_writer_.RemoveObserver(&test_state_observer_);
+ ASSERT_TRUE(log_temp_dir_.Delete());
+ }
+
+ base::FilePath FileWriterGetFilePathToCompletedLog() {
+ TestFilePathCallback test_callback;
+ file_writer_.GetFilePathToCompletedLog(test_callback.callback());
+ return test_callback.WaitForResult();
+ }
+
+ WARN_UNUSED_RESULT ::testing::AssertionResult InitializeThenVerifyNewState(
+ bool expected_initialize_success,
+ bool expected_log_exists) {
+ file_writer_.Initialize(file_thread_.task_runner(),
+ net_thread_.task_runner());
+ std::unique_ptr<base::DictionaryValue> state =
+ test_state_observer_.WaitForNewState();
+ ::testing::AssertionResult result =
+ VerifyState(std::move(state), kStateInitializingString);
+ if (!result) {
+ return ::testing::AssertionFailure()
+ << "First state after Initialize() does not match expected:"
+ << std::endl
+ << result.message();
+ }
+
+ state = test_state_observer_.WaitForNewState();
+ result =
+ VerifyState(std::move(state),
+ expected_initialize_success ? kStateNotLoggingString
+ : kStateUninitializedString,
+ expected_log_exists, false, "");
+ if (!result) {
+ return ::testing::AssertionFailure()
+ << "Second state after Initialize() does not match expected:"
+ << std::endl
+ << result.message();
+ }
+
+ return ::testing::AssertionSuccess();
+ }
+
+ // If |custom_log_path| is empty path, |file_writer_| will use its
+ // default log path, which is cached in |default_log_path_|.
+ WARN_UNUSED_RESULT::testing::AssertionResult StartThenVerifyNewState(
+ const base::FilePath& custom_log_path,
+ net::NetLogCaptureMode capture_mode,
+ const std::string& expected_capture_mode_string,
+ const URLRequestContextGetterList& context_getters) {
+ file_writer_.StartNetLog(custom_log_path, capture_mode, kMaxLogSizeBytes,
+ base::CommandLine::StringType(), kChannelString,
+ context_getters);
+ std::unique_ptr<base::DictionaryValue> state =
+ test_state_observer_.WaitForNewState();
+ ::testing::AssertionResult result =
+ VerifyState(std::move(state), kStateStartingLogString);
+ if (!result) {
+ return ::testing::AssertionFailure()
+ << "First state after StartNetLog() does not match expected:"
+ << std::endl
+ << result.message();
+ }
+
+ state = test_state_observer_.WaitForNewState();
+ result = VerifyState(std::move(state), kStateLoggingString, true, true,
+ expected_capture_mode_string);
+ if (!result) {
+ return ::testing::AssertionFailure()
+ << "Second state after StartNetLog() does not match expected:"
+ << std::endl
+ << result.message();
+ }
+
+ // Make sure GetFilePath() returns empty path when logging.
+ base::FilePath actual_log_path = FileWriterGetFilePathToCompletedLog();
+ if (!actual_log_path.empty()) {
+ return ::testing::AssertionFailure()
+ << "GetFilePath() should return empty path after logging starts."
+ << " Actual: " << ::testing::PrintToString(actual_log_path);
+ }
+
+ return ::testing::AssertionSuccess();
+ }
+
+ // If |custom_log_path| is empty path, it's assumed the log file with be at
+ // |default_log_path_|.
+ WARN_UNUSED_RESULT ::testing::AssertionResult StopThenVerifyNewStateAndFile(
+ const base::FilePath& custom_log_path,
+ std::unique_ptr<base::DictionaryValue> polled_data,
+ scoped_refptr<net::URLRequestContextGetter> context_getter,
+ const std::string& expected_capture_mode_string) {
+ file_writer_.StopNetLog(std::move(polled_data), context_getter);
+ std::unique_ptr<base::DictionaryValue> state =
+ test_state_observer_.WaitForNewState();
+ ::testing::AssertionResult result =
+ VerifyState(std::move(state), kStateStoppingLogString);
+ if (!result) {
+ return ::testing::AssertionFailure()
+ << "First state after StopNetLog() does not match expected:"
+ << std::endl
+ << result.message();
+ }
+
+ state = test_state_observer_.WaitForNewState();
+ result = VerifyState(std::move(state), kStateNotLoggingString, true, true,
+ expected_capture_mode_string);
+ if (!result) {
+ return ::testing::AssertionFailure()
+ << "Second state after StopNetLog() does not match expected:"
+ << std::endl
+ << result.message();
+ }
+
+ // Make sure GetFilePath() returns the expected path.
+ const base::FilePath& expected_log_path =
+ custom_log_path.empty() ? default_log_path_ : custom_log_path;
+ base::FilePath actual_log_path = FileWriterGetFilePathToCompletedLog();
+ if (actual_log_path != expected_log_path) {
+ return ::testing::AssertionFailure()
+ << "GetFilePath() returned incorrect path after logging stopped."
+ << std::endl
+ << " Actual: " << ::testing::PrintToString(actual_log_path)
+ << std::endl
+ << " Expected: " << ::testing::PrintToString(expected_log_path);
+ }
+
+ // Make sure the generated log file is valid.
+ std::unique_ptr<base::DictionaryValue> root;
+ result = ReadCompleteLogFile(expected_log_path, &root);
+ if (!result) {
+ return ::testing::AssertionFailure()
+ << "Log file after logging stopped is not valid:" << std::endl
+ << result.message();
+ }
+
+ return ::testing::AssertionSuccess();
+ }
+
+ protected:
+ ChromeNetLog net_log_;
+
+ // |file_writer_| is initialized after |net_log_| so that it can stop
+ // obvserving on destruction.
+ NetExportFileWriter file_writer_;
+
+ base::ScopedTempDir log_temp_dir_;
+
+ // The default log path that |file_writer_| will use is cached here.
+ base::FilePath default_log_path_;
+
+ base::Thread file_thread_;
+ base::Thread net_thread_;
+
+ TestStateObserver test_state_observer_;
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST_F(NetExportFileWriterTest, InitFail) {
+ // Override file_writer_'s default log base directory getter to always
+ // fail.
+ file_writer_.SetDefaultLogBaseDirectoryGetterForTest(
+ base::Bind([](base::FilePath* path) -> bool { return false; }));
+
+ // Initialization should fail due to the override.
+ ASSERT_TRUE(InitializeThenVerifyNewState(false, false));
+
+ // NetExportFileWriter::GetFilePath() should return empty path if
+ // uninitialized.
+ EXPECT_TRUE(FileWriterGetFilePathToCompletedLog().empty());
+}
+
+TEST_F(NetExportFileWriterTest, InitWithoutExistingLog) {
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ // NetExportFileWriter::GetFilePathToCompletedLog() should return empty path
+ // when no log file exists.
+ EXPECT_TRUE(FileWriterGetFilePathToCompletedLog().empty());
+}
+
+TEST_F(NetExportFileWriterTest, InitWithExistingLog) {
+ // Create and close an empty log file to simulate existence of a previous log
+ // file.
+ ASSERT_TRUE(
+ base::CreateDirectoryAndGetError(default_log_path_.DirName(), nullptr));
+ base::ScopedFILE empty_file(base::OpenFile(default_log_path_, "w"));
+ ASSERT_TRUE(empty_file.get());
+ empty_file.reset();
+
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, true));
+
+ EXPECT_EQ(default_log_path_, FileWriterGetFilePathToCompletedLog());
+}
+
+TEST_F(NetExportFileWriterTest, StartAndStopWithAllCaptureModes) {
+ const net::NetLogCaptureMode capture_modes[3] = {
+ net::NetLogCaptureMode::Default(),
+ net::NetLogCaptureMode::IncludeCookiesAndCredentials(),
+ net::NetLogCaptureMode::IncludeSocketBytes()};
+
+ const std::string capture_mode_strings[3] = {
+ kCaptureModeDefaultString, kCaptureModeIncludeCookiesAndCredentialsString,
+ kCaptureModeIncludeSocketBytesString};
+
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ // For each capture mode, start and stop |file_writer_| in that mode.
+ for (int i = 0; i < 3; ++i) {
+ // StartNetLog(), should result in state change.
+ ASSERT_TRUE(StartThenVerifyNewState(base::FilePath(), capture_modes[i],
+ capture_mode_strings[i],
+ URLRequestContextGetterList()));
+
+ // Calling StartNetLog() again should be a no-op. Try doing StartNetLog()
+ // with various capture modes; they should all be ignored and result in no
+ // state change.
+ file_writer_.StartNetLog(base::FilePath(), capture_modes[i],
+ kMaxLogSizeBytes, base::CommandLine::StringType(),
+ kChannelString, URLRequestContextGetterList());
+ file_writer_.StartNetLog(base::FilePath(), capture_modes[(i + 1) % 3],
+ kMaxLogSizeBytes, base::CommandLine::StringType(),
+ kChannelString, URLRequestContextGetterList());
+ file_writer_.StartNetLog(base::FilePath(), capture_modes[(i + 2) % 3],
+ kMaxLogSizeBytes, base::CommandLine::StringType(),
+ kChannelString, URLRequestContextGetterList());
+
+ // StopNetLog(), should result in state change. The capture mode should
+ // match that of the first StartNetLog() call (called by
+ // StartThenVerifyNewState()).
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), nullptr, nullptr, capture_mode_strings[i]));
+
+ // Stopping a second time should be a no-op.
+ file_writer_.StopNetLog(nullptr, nullptr);
+ }
+
+ // Start and stop one more time just to make sure the last StopNetLog() call
+ // was properly ignored and left |file_writer_| in a valid state.
+ ASSERT_TRUE(StartThenVerifyNewState(base::FilePath(), capture_modes[0],
+ capture_mode_strings[0],
+ URLRequestContextGetterList()));
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+ capture_mode_strings[0]));
+}
+
+// Verify the file sizes after two consecutive starts/stops are the same (even
+// if some junk data is added in between).
+TEST_F(NetExportFileWriterTest, StartClearsFile) {
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ ASSERT_TRUE(StartThenVerifyNewState(
+ base::FilePath(), net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, URLRequestContextGetterList()));
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+ kCaptureModeDefaultString));
+
+ int64_t stop_file_size;
+ EXPECT_TRUE(base::GetFileSize(default_log_path_, &stop_file_size));
+
+ // Add some junk at the end of the file.
+ std::string junk_data("Hello");
+ EXPECT_TRUE(base::AppendToFile(default_log_path_, junk_data.c_str(),
+ junk_data.size()));
+
+ int64_t junk_file_size;
+ EXPECT_TRUE(base::GetFileSize(default_log_path_, &junk_file_size));
+ EXPECT_GT(junk_file_size, stop_file_size);
+
+ // Start and stop again and make sure the file is back to the size it was
+ // before adding the junk data.
+ ASSERT_TRUE(StartThenVerifyNewState(
+ base::FilePath(), net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, URLRequestContextGetterList()));
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+ kCaptureModeDefaultString));
+
+ int64_t new_stop_file_size;
+ EXPECT_TRUE(base::GetFileSize(default_log_path_, &new_stop_file_size));
+
+ EXPECT_EQ(stop_file_size, new_stop_file_size);
+}
+
+// Adds an event to the log file, then checks that the file is larger than
+// the file created without that event.
+TEST_F(NetExportFileWriterTest, AddEvent) {
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ ASSERT_TRUE(StartThenVerifyNewState(
+ base::FilePath(), net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, URLRequestContextGetterList()));
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+ kCaptureModeDefaultString));
+
+ // Get file size without the event.
+ int64_t stop_file_size;
+ EXPECT_TRUE(base::GetFileSize(default_log_path_, &stop_file_size));
+
+ ASSERT_TRUE(StartThenVerifyNewState(
+ base::FilePath(), net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, URLRequestContextGetterList()));
+
+ net_log_.AddGlobalEntry(net::NetLogEventType::CANCELLED);
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+ kCaptureModeDefaultString));
+
+ // Get file size after adding the event and make sure it's larger than before.
+ int64_t new_stop_file_size;
+ EXPECT_TRUE(base::GetFileSize(default_log_path_, &new_stop_file_size));
+ EXPECT_GE(new_stop_file_size, stop_file_size);
+}
+
+// Using a custom path to make sure logging can still occur when the path has
+// changed.
+TEST_F(NetExportFileWriterTest, AddEventCustomPath) {
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ base::FilePath::CharType kCustomRelativePath[] =
+ FILE_PATH_LITERAL("custom/custom/chrome-net-export-log.json");
+ base::FilePath custom_log_path =
+ log_temp_dir_.GetPath().Append(kCustomRelativePath);
+ EXPECT_TRUE(
+ base::CreateDirectoryAndGetError(custom_log_path.DirName(), nullptr));
+
+ ASSERT_TRUE(StartThenVerifyNewState(
+ custom_log_path, net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, URLRequestContextGetterList()));
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr, nullptr,
+ kCaptureModeDefaultString));
+
+ // Get file size without the event.
+ int64_t stop_file_size;
+ EXPECT_TRUE(base::GetFileSize(custom_log_path, &stop_file_size));
+
+ ASSERT_TRUE(StartThenVerifyNewState(
+ custom_log_path, net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, URLRequestContextGetterList()));
+
+ net_log_.AddGlobalEntry(net::NetLogEventType::CANCELLED);
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr, nullptr,
+ kCaptureModeDefaultString));
+
+ // Get file size after adding the event and make sure it's larger than before.
+ int64_t new_stop_file_size;
+ EXPECT_TRUE(base::GetFileSize(custom_log_path, &new_stop_file_size));
+ EXPECT_GE(new_stop_file_size, stop_file_size);
+}
+
+TEST_F(NetExportFileWriterTest, StopWithPolledDataAndContextGetter) {
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ // Create dummy polled data
+ const char kDummyPolledDataPath[] = "dummy_path";
+ const char kDummyPolledDataString[] = "dummy_info";
+ std::unique_ptr<base::DictionaryValue> dummy_polled_data =
+ base::MakeUnique<base::DictionaryValue>();
+ dummy_polled_data->SetString(kDummyPolledDataPath, kDummyPolledDataString);
+
+ // Create test context getter on |net_thread_| and wait for it to finish.
+ scoped_refptr<net::TestURLRequestContextGetter> context_getter;
+ const int kDummyQuicParam = 75;
+ net::TestClosure init_done;
+ net_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&SetUpTestContextGetterWithQuicTimeoutInfo, &net_log_,
+ kDummyQuicParam, &context_getter),
+ init_done.closure());
+ init_done.WaitForResult();
+
+ ASSERT_TRUE(StartThenVerifyNewState(
+ base::FilePath(), net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, URLRequestContextGetterList()));
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), std::move(dummy_polled_data), context_getter,
+ kCaptureModeDefaultString));
+
+ // Read polledData from log file.
+ std::unique_ptr<base::DictionaryValue> root;
+ ASSERT_TRUE(ReadCompleteLogFile(default_log_path_, &root));
+ base::DictionaryValue* polled_data;
+ ASSERT_TRUE(root->GetDictionary("polledData", &polled_data));
+
+ // Check that it contains the field from the polled data that was passed in.
+ std::string dummy_string;
+ ASSERT_TRUE(polled_data->GetString(kDummyPolledDataPath, &dummy_string));
+ EXPECT_EQ(kDummyPolledDataString, dummy_string);
+
+ // Check that it also contains the field from the URLRequestContext that was
+ // passed in.
+ base::DictionaryValue* quic_info;
+ ASSERT_TRUE(polled_data->GetDictionary("quicInfo", &quic_info));
+ base::Value* timeout_value = nullptr;
+ int timeout;
+ ASSERT_TRUE(
+ quic_info->Get("idle_connection_timeout_seconds", &timeout_value));
+ ASSERT_TRUE(timeout_value->GetAsInteger(&timeout));
+ EXPECT_EQ(kDummyQuicParam, timeout);
+}
+
+TEST_F(NetExportFileWriterTest, StartWithContextGetters) {
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ // Create test context getter and request on |net_thread_| and wait for it to
+ // finish.
+ const std::string kDummyUrl = "blah:blah";
+ scoped_refptr<net::TestURLRequestContextGetter> context_getter;
+ std::unique_ptr<net::URLRequest> request;
+ net::TestDelegate delegate;
+ delegate.set_quit_on_complete(false);
+
+ net::TestClosure init_done;
+ net_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&SetUpTestContextGetterWithRequest, &net_log_, GURL(kDummyUrl),
+ &delegate, &context_getter, &request),
+ init_done.closure());
+ init_done.WaitForResult();
+
+ ASSERT_TRUE(StartThenVerifyNewState(
+ base::FilePath(), net::NetLogCaptureMode::Default(),
+ kCaptureModeDefaultString, {context_getter}));
+
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+ kCaptureModeDefaultString));
+
+ // Read events from log file.
+ std::unique_ptr<base::DictionaryValue> root;
+ ASSERT_TRUE(ReadCompleteLogFile(default_log_path_, &root));
+ base::ListValue* events;
+ ASSERT_TRUE(root->GetList("events", &events));
+
+ // Check there is at least one event as a result of the ongoing request.
+ EXPECT_GE(events->GetSize(), 1u);
+
+ // Check the URL in the params of the first event.
+ base::DictionaryValue* event;
+ EXPECT_TRUE(events->GetDictionary(0, &event));
+ base::DictionaryValue* event_params;
+ EXPECT_TRUE(event->GetDictionary("params", &event_params));
+ std::string event_url;
+ EXPECT_TRUE(event_params->GetString("url", &event_url));
+ EXPECT_EQ(kDummyUrl, event_url);
+
+ net_thread_.task_runner()->DeleteSoon(FROM_HERE, request.release());
+}
+
+TEST_F(NetExportFileWriterTest, ReceiveStartWhileInitializing) {
+ // Trigger initialization of |file_writer_|.
+ file_writer_.Initialize(file_thread_.task_runner(),
+ net_thread_.task_runner());
+
+ // Before running the main message loop, tell |file_writer_| to start
+ // logging. Not running the main message loop prevents the initialization
+ // process from completing, so this ensures that StartNetLog() is received
+ // before |file_writer_| finishes initialization, which means this
+ // should be a no-op.
+ file_writer_.StartNetLog(base::FilePath(), net::NetLogCaptureMode::Default(),
+ kMaxLogSizeBytes, base::CommandLine::StringType(),
+ kChannelString, URLRequestContextGetterList());
+
+ // Now run the main message loop. Make sure StartNetLog() was ignored by
+ // checking that the next two states are "initializing" followed by
+ // "not-logging".
+ std::unique_ptr<base::DictionaryValue> state =
+ test_state_observer_.WaitForNewState();
+ ASSERT_TRUE(VerifyState(std::move(state), kStateInitializingString));
+ state = test_state_observer_.WaitForNewState();
+ ASSERT_TRUE(
+ VerifyState(std::move(state), kStateNotLoggingString, false, false, ""));
+}
+
+TEST_F(NetExportFileWriterTest, ReceiveStartWhileStoppingLog) {
+ ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
+
+ // Call StartNetLog() on |file_writer_| and wait for the state change.
+ ASSERT_TRUE(StartThenVerifyNewState(
+ base::FilePath(), net::NetLogCaptureMode::IncludeSocketBytes(),
+ kCaptureModeIncludeSocketBytesString, URLRequestContextGetterList()));
+
+ // Tell |file_writer_| to stop logging.
+ file_writer_.StopNetLog(nullptr, nullptr);
+
+ // Before running the main message loop, tell |file_writer_| to start
+ // logging. Not running the main message loop prevents the stopping process
+ // from completing, so this ensures StartNetLog() is received before
+ // |file_writer_| finishes stopping, which means this should be a
+ // no-op.
+ file_writer_.StartNetLog(base::FilePath(), net::NetLogCaptureMode::Default(),
+ kMaxLogSizeBytes, base::CommandLine::StringType(),
+ kChannelString, URLRequestContextGetterList());
+
+ // Now run the main message loop. Make sure the last StartNetLog() was
+ // ignored by checking that the next two states are "stopping-log" followed by
+ // "not-logging". Also make sure the capture mode matches that of the first
+ // StartNetLog() call (called by StartThenVerifyState()).
+ std::unique_ptr<base::DictionaryValue> state =
+ test_state_observer_.WaitForNewState();
+ ASSERT_TRUE(VerifyState(std::move(state), kStateStoppingLogString));
+ state = test_state_observer_.WaitForNewState();
+ ASSERT_TRUE(VerifyState(std::move(state), kStateNotLoggingString, true, true,
+ kCaptureModeIncludeSocketBytesString));
+}
+
+} // namespace net_log
diff --git a/chromium/components/net_log/net_log_file_writer.cc b/chromium/components/net_log/net_log_file_writer.cc
deleted file mode 100644
index 8be69a73e1a..00000000000
--- a/chromium/components/net_log/net_log_file_writer.cc
+++ /dev/null
@@ -1,380 +0,0 @@
-// Copyright (c) 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/net_log/net_log_file_writer.h"
-
-#include <set>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/memory/ptr_util.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "components/net_log/chrome_net_log.h"
-#include "net/log/file_net_log_observer.h"
-#include "net/log/net_log_util.h"
-#include "net/url_request/url_request_context_getter.h"
-
-namespace net {
-class URLRequestContext;
-}
-
-namespace net_log {
-
-namespace {
-
-// Path of logs relative to default temporary directory given by
-// base::GetTempDir(). Must be kept in sync with
-// chrome/android/java/res/xml/file_paths.xml. Only used if not saving log file
-// to a custom path.
-const base::FilePath::CharType kLogRelativePath[] =
- FILE_PATH_LITERAL("net-export/chrome-net-export-log.json");
-
-// Old path used by net-export. Used to delete old files.
-// TODO(mmenke): Should remove at some point. Added in M46.
-const base::FilePath::CharType kOldLogRelativePath[] =
- FILE_PATH_LITERAL("chrome-net-export-log.json");
-
-// Contains file-related initialization tasks for NetLogFileWriter.
-NetLogFileWriter::DefaultLogPathResults SetUpDefaultLogPath(
- const NetLogFileWriter::DirectoryGetter& default_log_base_dir_getter) {
- NetLogFileWriter::DefaultLogPathResults results;
- results.default_log_path_success = false;
- results.log_exists = false;
-
- base::FilePath default_base_dir;
- if (!default_log_base_dir_getter.Run(&default_base_dir))
- return results;
-
- // Delete log file at old location, if present.
- base::DeleteFile(default_base_dir.Append(kOldLogRelativePath), false);
-
- results.default_log_path = default_base_dir.Append(kLogRelativePath);
- if (!base::CreateDirectoryAndGetError(results.default_log_path.DirName(),
- nullptr))
- return results;
-
- results.log_exists = base::PathExists(results.default_log_path);
- results.default_log_path_success = true;
- return results;
-}
-
-// Generates net log entries for ongoing events from |context_getters| and
-// adds them to |observer|.
-void CreateNetLogEntriesForActiveObjects(
- const NetLogFileWriter::URLRequestContextGetterList& context_getters,
- net::NetLog::ThreadSafeObserver* observer) {
- std::set<net::URLRequestContext*> contexts;
- for (const auto& getter : context_getters) {
- DCHECK(getter->GetNetworkTaskRunner()->BelongsToCurrentThread());
- contexts.insert(getter->GetURLRequestContext());
- }
- net::CreateNetLogEntriesForActiveObjects(contexts, observer);
-}
-
-// Adds net info from net::GetNetInfo() to |polled_data|.
-std::unique_ptr<base::DictionaryValue> AddNetInfo(
- scoped_refptr<net::URLRequestContextGetter> context_getter,
- std::unique_ptr<base::DictionaryValue> polled_data) {
- DCHECK(context_getter);
- std::unique_ptr<base::DictionaryValue> net_info = net::GetNetInfo(
- context_getter->GetURLRequestContext(), net::NET_INFO_ALL_SOURCES);
- if (polled_data)
- net_info->MergeDictionary(polled_data.get());
- return net_info;
-}
-
-// If running on a POSIX OS, this will attempt to set all the permission flags
-// of the file at |path| to 1. Will return |path| on success and the empty path
-// on failure.
-base::FilePath GetPathWithAllPermissions(const base::FilePath& path) {
- if (!base::PathExists(path))
- return base::FilePath();
-#if defined(OS_POSIX)
- return base::SetPosixFilePermissions(path, base::FILE_PERMISSION_MASK)
- ? path
- : base::FilePath();
-#else
- return path;
-#endif
-}
-
-} // namespace
-
-NetLogFileWriter::NetLogFileWriter(
- ChromeNetLog* chrome_net_log,
- const base::CommandLine::StringType& command_line_string,
- const std::string& channel_string)
- : state_(STATE_UNINITIALIZED),
- log_exists_(false),
- log_capture_mode_known_(false),
- log_capture_mode_(net::NetLogCaptureMode::Default()),
- chrome_net_log_(chrome_net_log),
- command_line_string_(command_line_string),
- channel_string_(channel_string),
- default_log_base_dir_getter_(base::Bind(&base::GetTempDir)),
- weak_ptr_factory_(this) {}
-
-NetLogFileWriter::~NetLogFileWriter() {
- if (file_net_log_observer_)
- file_net_log_observer_->StopObserving(nullptr, base::Bind([] {}));
-}
-
-void NetLogFileWriter::AddObserver(StateObserver* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- state_observer_list_.AddObserver(observer);
-}
-
-void NetLogFileWriter::RemoveObserver(StateObserver* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- state_observer_list_.RemoveObserver(observer);
-}
-
-void NetLogFileWriter::Initialize(
- scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> net_task_runner) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(file_task_runner);
- DCHECK(net_task_runner);
-
- if (file_task_runner_)
- DCHECK_EQ(file_task_runner_, file_task_runner);
- file_task_runner_ = file_task_runner;
- if (net_task_runner_)
- DCHECK_EQ(net_task_runner_, net_task_runner);
- net_task_runner_ = net_task_runner;
-
- if (state_ != STATE_UNINITIALIZED)
- return;
-
- state_ = STATE_INITIALIZING;
-
- NotifyStateObserversAsync();
-
- base::PostTaskAndReplyWithResult(
- file_task_runner_.get(), FROM_HERE,
- base::Bind(&SetUpDefaultLogPath, default_log_base_dir_getter_),
- base::Bind(&NetLogFileWriter::SetStateAfterSetUpDefaultLogPath,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void NetLogFileWriter::StartNetLog(
- const base::FilePath& log_path,
- net::NetLogCaptureMode capture_mode,
- const URLRequestContextGetterList& context_getters) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(file_task_runner_);
-
- if (state_ != STATE_NOT_LOGGING)
- return;
-
- if (!log_path.empty())
- log_path_ = log_path;
-
- DCHECK(!log_path_.empty());
-
- state_ = STATE_STARTING_LOG;
-
- NotifyStateObserversAsync();
-
- std::unique_ptr<base::Value> constants(
- ChromeNetLog::GetConstants(command_line_string_, channel_string_));
- // Instantiate a FileNetLogObserver in unbounded mode.
- file_net_log_observer_ = net::FileNetLogObserver::CreateUnbounded(
- file_task_runner_, log_path_, std::move(constants));
-
- net_task_runner_->PostTaskAndReply(
- FROM_HERE,
- base::Bind(&CreateNetLogEntriesForActiveObjects, context_getters,
- base::Unretained(file_net_log_observer_.get())),
- base::Bind(
- &NetLogFileWriter::StartNetLogAfterCreateEntriesForActiveObjects,
- weak_ptr_factory_.GetWeakPtr(), capture_mode));
-}
-
-void NetLogFileWriter::StartNetLogAfterCreateEntriesForActiveObjects(
- net::NetLogCaptureMode capture_mode) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_EQ(STATE_STARTING_LOG, state_);
-
- state_ = STATE_LOGGING;
- log_exists_ = true;
- log_capture_mode_known_ = true;
- log_capture_mode_ = capture_mode;
-
- NotifyStateObservers();
-
- file_net_log_observer_->StartObserving(chrome_net_log_, capture_mode);
-}
-
-void NetLogFileWriter::StopNetLog(
- std::unique_ptr<base::DictionaryValue> polled_data,
- scoped_refptr<net::URLRequestContextGetter> context_getter) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(net_task_runner_);
-
- if (state_ != STATE_LOGGING)
- return;
-
- state_ = STATE_STOPPING_LOG;
-
- NotifyStateObserversAsync();
-
- if (context_getter) {
- base::PostTaskAndReplyWithResult(
- net_task_runner_.get(), FROM_HERE,
- base::Bind(&AddNetInfo, context_getter, base::Passed(&polled_data)),
- base::Bind(&NetLogFileWriter::StopNetLogAfterAddNetInfo,
- weak_ptr_factory_.GetWeakPtr()));
- } else {
- StopNetLogAfterAddNetInfo(std::move(polled_data));
- }
-}
-
-std::unique_ptr<base::DictionaryValue> NetLogFileWriter::GetState() const {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- auto dict = base::MakeUnique<base::DictionaryValue>();
-
- dict->SetString("file", log_path_.LossyDisplayName());
-
- base::StringPiece state_string;
- switch (state_) {
- case STATE_UNINITIALIZED:
- state_string = "UNINITIALIZED";
- break;
- case STATE_INITIALIZING:
- state_string = "INITIALIZING";
- break;
- case STATE_NOT_LOGGING:
- state_string = "NOT_LOGGING";
- break;
- case STATE_STARTING_LOG:
- state_string = "STARTING_LOG";
- break;
- case STATE_LOGGING:
- state_string = "LOGGING";
- break;
- case STATE_STOPPING_LOG:
- state_string = "STOPPING_LOG";
- break;
- }
- dict->SetString("state", state_string);
-
- dict->SetBoolean("logExists", log_exists_);
- dict->SetBoolean("logCaptureModeKnown", log_capture_mode_known_);
- dict->SetString("captureMode", CaptureModeToString(log_capture_mode_));
-
- return dict;
-}
-
-void NetLogFileWriter::GetFilePathToCompletedLog(
- const FilePathCallback& path_callback) const {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!(log_exists_ && state_ == STATE_NOT_LOGGING)) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(path_callback, base::FilePath()));
- return;
- }
-
- DCHECK(file_task_runner_);
- DCHECK(!log_path_.empty());
-
- base::PostTaskAndReplyWithResult(
- file_task_runner_.get(), FROM_HERE,
- base::Bind(&GetPathWithAllPermissions, log_path_), path_callback);
-}
-
-std::string NetLogFileWriter::CaptureModeToString(
- net::NetLogCaptureMode capture_mode) {
- if (capture_mode == net::NetLogCaptureMode::Default()) {
- return "STRIP_PRIVATE_DATA";
- } else if (capture_mode ==
- net::NetLogCaptureMode::IncludeCookiesAndCredentials()) {
- return "NORMAL";
- } else if (capture_mode == net::NetLogCaptureMode::IncludeSocketBytes()) {
- return "LOG_BYTES";
- } else {
- NOTREACHED();
- return "STRIP_PRIVATE_DATA";
- }
-}
-
-net::NetLogCaptureMode NetLogFileWriter::CaptureModeFromString(
- const std::string& capture_mode_string) {
- if (capture_mode_string == "STRIP_PRIVATE_DATA") {
- return net::NetLogCaptureMode::Default();
- } else if (capture_mode_string == "NORMAL") {
- return net::NetLogCaptureMode::IncludeCookiesAndCredentials();
- } else if (capture_mode_string == "LOG_BYTES") {
- return net::NetLogCaptureMode::IncludeSocketBytes();
- } else {
- NOTREACHED();
- return net::NetLogCaptureMode::Default();
- }
-}
-
-void NetLogFileWriter::SetDefaultLogBaseDirectoryGetterForTest(
- const DirectoryGetter& getter) {
- default_log_base_dir_getter_ = getter;
-}
-
-void NetLogFileWriter::NotifyStateObservers() {
- DCHECK(thread_checker_.CalledOnValidThread());
- std::unique_ptr<base::DictionaryValue> state = GetState();
- for (StateObserver& observer : state_observer_list_) {
- observer.OnNewState(*state);
- }
-}
-
-void NetLogFileWriter::NotifyStateObserversAsync() {
- DCHECK(thread_checker_.CalledOnValidThread());
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(&NetLogFileWriter::NotifyStateObservers,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void NetLogFileWriter::SetStateAfterSetUpDefaultLogPath(
- const DefaultLogPathResults& set_up_default_log_path_results) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_EQ(STATE_INITIALIZING, state_);
-
- if (set_up_default_log_path_results.default_log_path_success) {
- state_ = STATE_NOT_LOGGING;
- log_path_ = set_up_default_log_path_results.default_log_path;
- log_exists_ = set_up_default_log_path_results.log_exists;
- DCHECK(!log_capture_mode_known_);
- } else {
- state_ = STATE_UNINITIALIZED;
- }
- NotifyStateObservers();
-}
-
-void NetLogFileWriter::StopNetLogAfterAddNetInfo(
- std::unique_ptr<base::DictionaryValue> polled_data) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_EQ(STATE_STOPPING_LOG, state_);
-
- file_net_log_observer_->StopObserving(
- std::move(polled_data),
- base::Bind(&NetLogFileWriter::ResetObserverThenSetStateNotLogging,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void NetLogFileWriter::ResetObserverThenSetStateNotLogging() {
- DCHECK(thread_checker_.CalledOnValidThread());
- file_net_log_observer_.reset();
- state_ = STATE_NOT_LOGGING;
-
- NotifyStateObservers();
-}
-
-} // namespace net_log
diff --git a/chromium/components/net_log/net_log_file_writer.h b/chromium/components/net_log/net_log_file_writer.h
deleted file mode 100644
index 1f741c9e1d3..00000000000
--- a/chromium/components/net_log/net_log_file_writer.h
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright (c) 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_NET_LOG_NET_LOG_FILE_WRITER_H_
-#define COMPONENTS_NET_LOG_NET_LOG_FILE_WRITER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/threading/thread_checker.h"
-#include "net/log/net_log_capture_mode.h"
-
-namespace base {
-class DictionaryValue;
-class SingleThreadTaskRunner;
-}
-
-namespace net {
-class FileNetLogObserver;
-class URLRequestContextGetter;
-}
-
-namespace net_log {
-
-class ChromeNetLog;
-
-// NetLogFileWriter is used exclusively as a support class for net-export.
-// It's a singleton that acts as the interface to all NetExportMessageHandlers
-// which can tell it to start or stop logging in response to user actions from
-// net-export UIs. Because it's a singleton, the logging state can be shared
-// between multiple instances of the net-export UI. Internally, it manages an
-// instance of net::FileNetLogObserver and handles the attaching/detaching of it
-// to the ChromeNetLog. This class is used by the iOS and non-iOS
-// implementations of net-export.
-//
-// NetLogFileWriter maintains the current logging state using the members
-// |state_|, |log_exists_|, |log_capture_mode_known_|, |log_capture_mode_|.
-// Its three main commands are Initialize(), StartNetLog(), and StopNetLog().
-// These are the only functions that may cause NetLogFileWriter to change state.
-// Initialize() must be called before NetLogFileWriter can process any other
-// commands. A portion of the initialization needs to run on the
-// |file_task_runner_|.
-//
-// This class is created and destroyed on the UI thread, and all public entry
-// points are to be called on the UI thread. Internally, the class may run some
-// code on the |file_task_runner_| and |net_task_runner_|.
-class NetLogFileWriter {
- public:
- // The observer interface to be implemented by code that wishes to be notified
- // of NetLogFileWriter's state changes.
- class StateObserver {
- public:
- virtual void OnNewState(const base::DictionaryValue& state) = 0;
- };
-
- // Struct used to store the results of setting up the default log directory
- // and log path.
- struct DefaultLogPathResults {
- bool default_log_path_success;
- base::FilePath default_log_path;
- bool log_exists;
- };
-
- using FilePathCallback = base::Callback<void(const base::FilePath&)>;
- using DirectoryGetter = base::Callback<bool(base::FilePath*)>;
- using URLRequestContextGetterList =
- std::vector<scoped_refptr<net::URLRequestContextGetter>>;
-
- ~NetLogFileWriter();
-
- // Attaches a StateObserver. |observer| will be notified of state changes to
- // NetLogFileWriter. State changes may occur in response to Initiailze(),
- // StartNetLog(), or StopNetLog(). StateObserver::OnNewState() will be called
- // asynchronously relative to the command that caused the state change.
- // |observer| must remain alive until RemoveObserver() is called.
- void AddObserver(StateObserver* observer);
-
- // Detaches a StateObserver.
- void RemoveObserver(StateObserver* observer);
-
- // Initializes NetLogFileWriter if not initialized.
- //
- // Also sets the task runners used by NetLogFileWriter for doing file I/O and
- // network I/O respectively. The task runners must not be changed once set.
- // However, calling this function again with the same parameters is OK.
- void Initialize(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> net_task_runner);
-
- // Starts collecting NetLog data into the file at |log_path|. If |log_path| is
- // empty, the default log path is used. If NetLogFileWriter is already
- // logging, this is a no-op and |capture_mode| is ignored.
- //
- // |context_getters| is an optional list of URLRequestContextGetters used only
- // to add log entries for ongoing events when logging starts. They are not
- // used for retrieving polled data. All the contexts must be bound to the same
- // thread.
- void StartNetLog(const base::FilePath& log_path,
- net::NetLogCaptureMode capture_mode,
- const URLRequestContextGetterList& context_getters);
-
- // Stops collecting NetLog data into the file. It is a no-op if
- // NetLogFileWriter is currently not logging.
- //
- // |polled_data| is a JSON dictionary that will be appended to the end of the
- // log; it's for adding additional info to the log that aren't events.
- // If |context_getter| is not null, then StopNetLog() will automatically
- // append net info (from net::GetNetInfo() retrieved using |context_getter|)
- // to |polled_data|.
- // Note that StopNetLog() accepts (optionally) only one context getter for
- // retrieving net polled data as opposed to StartNetLog() which accepts zero
- // or more context getters for retrieving ongoing net events.
- void StopNetLog(std::unique_ptr<base::DictionaryValue> polled_data,
- scoped_refptr<net::URLRequestContextGetter> context_getter);
-
- // Creates a DictionaryValue summary of the state of the NetLogFileWriter
- std::unique_ptr<base::DictionaryValue> GetState() const;
-
- // Gets the log filepath. |path_callback| will be used to notify the caller
- // when the filepath is retrieved. |path_callback| will be executed with an
- // empty filepath if any of the following occurs:
- // (1) The NetLogFileWriter is not initialized.
- // (2) The log file does not exist.
- // (3) The NetLogFileWriter is currently logging.
- // (4) The log file's permissions could not be set to all.
- //
- // |path_callback| will be executed at the end of GetFilePathToCompletedLog()
- // asynchronously.
- void GetFilePathToCompletedLog(const FilePathCallback& path_callback) const;
-
- // Converts to/from the string representation of a capture mode used by
- // net_export.js.
- static std::string CaptureModeToString(net::NetLogCaptureMode capture_mode);
- static net::NetLogCaptureMode CaptureModeFromString(
- const std::string& capture_mode_string);
-
- // Overrides the getter used to retrieve the default log base directory during
- // initialization. Should only be used by unit tests.
- void SetDefaultLogBaseDirectoryGetterForTest(const DirectoryGetter& getter);
-
- protected:
- // Constructs a NetLogFileWriter. Only one instance is created in browser
- // process.
- NetLogFileWriter(ChromeNetLog* chrome_net_log,
- const base::CommandLine::StringType& command_line_string,
- const std::string& channel_string);
-
- private:
- friend class ChromeNetLog;
- friend class NetLogFileWriterTest;
-
- // The possible logging states of NetLogFileWriter.
- enum State {
- STATE_UNINITIALIZED,
- // Currently in the process of initializing.
- STATE_INITIALIZING,
- // Not currently logging to file.
- STATE_NOT_LOGGING,
- // Currently in the process of starting the log.
- STATE_STARTING_LOG,
- // Currently logging to file.
- STATE_LOGGING,
- // Currently in the process of stopping the log.
- STATE_STOPPING_LOG,
- };
-
- void NotifyStateObservers();
-
- // Posts NotifyStateObservers() to the current thread.
- void NotifyStateObserversAsync();
-
- // Called internally by Initialize(). Will initialize NetLogFileWriter's state
- // variables after the default log directory is set up and the default log
- // path is determined on the |file_task_runner_|.
- void SetStateAfterSetUpDefaultLogPath(
- const DefaultLogPathResults& set_up_default_log_path_results);
-
- // Called internally by StartNetLog(). Contains tasks to be done to start
- // logging after net log entries for ongoing events are added to the log from
- // the |net_task_runner_|.
- void StartNetLogAfterCreateEntriesForActiveObjects(
- net::NetLogCaptureMode capture_mode);
-
- // Called internally by StopNetLog(). Contains tasks to be done to stop
- // logging after net-thread polled data is retrieved on the
- // |net_task_runner_|.
- void StopNetLogAfterAddNetInfo(
- std::unique_ptr<base::DictionaryValue> polled_data);
-
- // Contains tasks to be done after |file_net_log_observer_| has completely
- // stopped writing.
- void ResetObserverThenSetStateNotLogging();
-
- // All members are accessed solely from the main thread (the thread that
- // |thread_checker_| is bound to).
-
- base::ThreadChecker thread_checker_;
-
- // Task runners for file-specific and net-specific tasks that must run on a
- // file or net thread.
- scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
- scoped_refptr<base::SingleThreadTaskRunner> net_task_runner_;
-
- State state_; // Current logging state of NetLogFileWriter.
-
- bool log_exists_; // Whether or not a log file exists on disk.
- bool log_capture_mode_known_;
- net::NetLogCaptureMode log_capture_mode_;
-
- base::FilePath log_path_; // base::FilePath to the NetLog file.
-
- // |file_net_log_observer_| watches the NetLog event stream, and
- // sends all entries to the file created in StartNetLog().
- std::unique_ptr<net::FileNetLogObserver> file_net_log_observer_;
-
- // The |chrome_net_log_| is owned by the browser process, cached here to avoid
- // using global (g_browser_process).
- ChromeNetLog* chrome_net_log_;
-
- // List of StateObservers to notify on state changes.
- base::ObserverList<StateObserver, true> state_observer_list_;
-
- const base::CommandLine::StringType command_line_string_;
- const std::string channel_string_;
-
- // Used by unit tests to override the default log base directory retrieved
- // during initialization. This getter is initialized to base::GetTempDir().
- DirectoryGetter default_log_base_dir_getter_;
-
- base::WeakPtrFactory<NetLogFileWriter> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(NetLogFileWriter);
-};
-
-} // namespace net_log
-
-#endif // COMPONENTS_NET_LOG_NET_LOG_FILE_WRITER_H_
diff --git a/chromium/components/net_log/net_log_file_writer_unittest.cc b/chromium/components/net_log/net_log_file_writer_unittest.cc
deleted file mode 100644
index bfae99d6b39..00000000000
--- a/chromium/components/net_log/net_log_file_writer_unittest.cc
+++ /dev/null
@@ -1,798 +0,0 @@
-// Copyright (c) 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/net_log/net_log_file_writer.h"
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/compiler_specific.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_string_value_serializer.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "components/net_log/chrome_net_log.h"
-#include "net/base/test_completion_callback.h"
-#include "net/http/http_network_session.h"
-#include "net/log/net_log_capture_mode.h"
-#include "net/log/net_log_event_type.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const char kChannelString[] = "SomeChannel";
-
-// Keep this in sync with kLogRelativePath defined in net_log_file_writer.cc.
-base::FilePath::CharType kLogRelativePath[] =
- FILE_PATH_LITERAL("net-export/chrome-net-export-log.json");
-
-const char kCaptureModeDefaultString[] = "STRIP_PRIVATE_DATA";
-const char kCaptureModeIncludeCookiesAndCredentialsString[] = "NORMAL";
-const char kCaptureModeIncludeSocketBytesString[] = "LOG_BYTES";
-
-const char kStateUninitializedString[] = "UNINITIALIZED";
-const char kStateInitializingString[] = "INITIALIZING";
-const char kStateNotLoggingString[] = "NOT_LOGGING";
-const char kStateStartingLogString[] = "STARTING_LOG";
-const char kStateLoggingString[] = "LOGGING";
-const char kStateStoppingLogString[] = "STOPPING_LOG";
-} // namespace
-
-namespace net_log {
-
-// Sets |path| to |path_to_return| and always returns true. This function is
-// used to override NetLogFileWriter's usual getter for the default log base
-// directory.
-bool SetPathToGivenAndReturnTrue(const base::FilePath& path_to_return,
- base::FilePath* path) {
- *path = path_to_return;
- return true;
-}
-
-// Checks the "state" string of a NetLogFileWriter state.
-WARN_UNUSED_RESULT ::testing::AssertionResult VerifyState(
- std::unique_ptr<base::DictionaryValue> state,
- const std::string& expected_state_string) {
- std::string actual_state_string;
- if (!state->GetString("state", &actual_state_string)) {
- return ::testing::AssertionFailure()
- << "State is missing \"state\" string.";
- }
- if (actual_state_string != expected_state_string) {
- return ::testing::AssertionFailure()
- << "\"state\" string of state does not match expected." << std::endl
- << " Actual: " << actual_state_string << std::endl
- << " Expected: " << expected_state_string;
- }
- return ::testing::AssertionSuccess();
-}
-
-// Checks all fields of a NetLogFileWriter state except possibly the
-// "captureMode" string; that field is only checked if
-// |expected_log_capture_mode_known| is true.
-WARN_UNUSED_RESULT ::testing::AssertionResult VerifyState(
- std::unique_ptr<base::DictionaryValue> state,
- const std::string& expected_state_string,
- bool expected_log_exists,
- bool expected_log_capture_mode_known,
- const std::string& expected_log_capture_mode_string) {
- base::DictionaryValue expected_state;
- expected_state.SetString("state", expected_state_string);
- expected_state.SetBoolean("logExists", expected_log_exists);
- expected_state.SetBoolean("logCaptureModeKnown",
- expected_log_capture_mode_known);
- if (expected_log_capture_mode_known) {
- expected_state.SetString("captureMode", expected_log_capture_mode_string);
- } else {
- state->Remove("captureMode", nullptr);
- }
-
- // Remove "file" field which is only added in debug mode.
- state->Remove("file", nullptr);
-
- std::string expected_state_json_string;
- JSONStringValueSerializer expected_state_serializer(
- &expected_state_json_string);
- expected_state_serializer.Serialize(expected_state);
-
- std::string actual_state_json_string;
- JSONStringValueSerializer actual_state_serializer(&actual_state_json_string);
- actual_state_serializer.Serialize(*state);
-
- if (actual_state_json_string != expected_state_json_string) {
- return ::testing::AssertionFailure()
- << "State (possibly excluding \"captureMode\") does not match "
- "expected."
- << std::endl
- << " Actual: " << actual_state_json_string << std::endl
- << " Expected: " << expected_state_json_string;
- }
- return ::testing::AssertionSuccess();
-}
-
-WARN_UNUSED_RESULT ::testing::AssertionResult ReadCompleteLogFile(
- const base::FilePath& log_path,
- std::unique_ptr<base::DictionaryValue>* root) {
- DCHECK(!log_path.empty());
-
- if (!base::PathExists(log_path)) {
- return ::testing::AssertionFailure() << log_path.value()
- << " does not exist.";
- }
- // Parse log file contents into a dictionary
- std::string log_string;
- if (!base::ReadFileToString(log_path, &log_string)) {
- return ::testing::AssertionFailure() << log_path.value()
- << " could not be read.";
- }
- *root = base::DictionaryValue::From(base::JSONReader::Read(log_string));
- if (!*root) {
- return ::testing::AssertionFailure()
- << "Contents of " << log_path.value()
- << " do not form valid JSON dictionary.";
- }
- // Make sure the "constants" section exists
- base::DictionaryValue* constants;
- if (!(*root)->GetDictionary("constants", &constants)) {
- root->reset();
- return ::testing::AssertionFailure() << log_path.value()
- << " is missing constants.";
- }
- // Make sure the "events" section exists
- base::ListValue* events;
- if (!(*root)->GetList("events", &events)) {
- root->reset();
- return ::testing::AssertionFailure() << log_path.value()
- << " is missing events list.";
- }
- return ::testing::AssertionSuccess();
-}
-
-void SetUpTestContextGetterWithQuicTimeoutInfo(
- net::NetLog* net_log,
- int quic_idle_connection_timeout_seconds,
- scoped_refptr<net::TestURLRequestContextGetter>* context_getter) {
- std::unique_ptr<net::TestURLRequestContext> context =
- base::MakeUnique<net::TestURLRequestContext>(true);
- context->set_net_log(net_log);
-
- std::unique_ptr<net::HttpNetworkSession::Params> params(
- new net::HttpNetworkSession::Params);
- params->quic_idle_connection_timeout_seconds =
- quic_idle_connection_timeout_seconds;
-
- context->set_http_network_session_params(std::move(params));
- context->Init();
-
- *context_getter = new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get(), std::move(context));
-}
-
-void SetUpTestContextGetterWithRequest(
- net::NetLog* net_log,
- const GURL& url,
- net::URLRequest::Delegate* delegate,
- scoped_refptr<net::TestURLRequestContextGetter>* context_getter,
- std::unique_ptr<net::URLRequest>* request) {
- auto context = base::MakeUnique<net::TestURLRequestContext>(true);
- context->set_net_log(net_log);
- context->Init();
-
- *request = context->CreateRequest(url, net::IDLE, delegate,
- TRAFFIC_ANNOTATION_FOR_TESTS);
- (*request)->Start();
-
- *context_getter = new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get(), std::move(context));
-}
-
-// An implementation of NetLogFileWriter::StateObserver that allows waiting
-// until it's notified of a new state.
-class TestStateObserver : public NetLogFileWriter::StateObserver {
- public:
- // NetLogFileWriter::StateObserver implementation
- void OnNewState(const base::DictionaryValue& state) override {
- test_closure_.closure().Run();
- result_state_ = state.CreateDeepCopy();
- }
-
- std::unique_ptr<base::DictionaryValue> WaitForNewState() {
- test_closure_.WaitForResult();
- DCHECK(result_state_);
- return std::move(result_state_);
- }
-
- private:
- net::TestClosure test_closure_;
- std::unique_ptr<base::DictionaryValue> result_state_;
-};
-
-// A class that wraps around TestClosure. Provides the ability to wait on a
-// file path callback and retrieve the result.
-class TestFilePathCallback {
- public:
- TestFilePathCallback()
- : callback_(base::Bind(&TestFilePathCallback::SetResultThenNotify,
- base::Unretained(this))) {}
-
- const base::Callback<void(const base::FilePath&)>& callback() const {
- return callback_;
- }
-
- const base::FilePath& WaitForResult() {
- test_closure_.WaitForResult();
- return result_;
- }
-
- private:
- void SetResultThenNotify(const base::FilePath& result) {
- result_ = result;
- test_closure_.closure().Run();
- }
-
- net::TestClosure test_closure_;
- base::FilePath result_;
- base::Callback<void(const base::FilePath&)> callback_;
-};
-
-class NetLogFileWriterTest : public ::testing::Test {
- public:
- using URLRequestContextGetterList =
- std::vector<scoped_refptr<net::URLRequestContextGetter>>;
-
- NetLogFileWriterTest()
- : net_log_(base::FilePath(),
- net::NetLogCaptureMode::Default(),
- base::CommandLine::ForCurrentProcess()->GetCommandLineString(),
- kChannelString),
- net_log_file_writer_(
- &net_log_,
- base::CommandLine::ForCurrentProcess()->GetCommandLineString(),
- kChannelString),
- file_thread_("NetLogFileWriter file thread"),
- net_thread_("NetLogFileWriter net thread") {}
-
- // ::testing::Test implementation
- void SetUp() override {
- ASSERT_TRUE(log_temp_dir_.CreateUniqueTempDir());
-
- // Override |net_log_file_writer_|'s default-log-base-directory-getter to
- // a getter that returns the temp dir created for the test.
- net_log_file_writer_.SetDefaultLogBaseDirectoryGetterForTest(
- base::Bind(&SetPathToGivenAndReturnTrue, log_temp_dir_.GetPath()));
-
- default_log_path_ = log_temp_dir_.GetPath().Append(kLogRelativePath);
-
- ASSERT_TRUE(file_thread_.Start());
- ASSERT_TRUE(net_thread_.Start());
-
- net_log_file_writer_.AddObserver(&test_state_observer_);
-
- ASSERT_TRUE(VerifyState(net_log_file_writer_.GetState(),
- kStateUninitializedString, false, false, ""));
- }
-
- // ::testing::Test implementation
- void TearDown() override {
- net_log_file_writer_.RemoveObserver(&test_state_observer_);
- ASSERT_TRUE(log_temp_dir_.Delete());
- }
-
- base::FilePath FileWriterGetFilePathToCompletedLog() {
- TestFilePathCallback test_callback;
- net_log_file_writer_.GetFilePathToCompletedLog(test_callback.callback());
- return test_callback.WaitForResult();
- }
-
- WARN_UNUSED_RESULT ::testing::AssertionResult InitializeThenVerifyNewState(
- bool expected_initialize_success,
- bool expected_log_exists) {
- net_log_file_writer_.Initialize(file_thread_.task_runner(),
- net_thread_.task_runner());
- std::unique_ptr<base::DictionaryValue> state =
- test_state_observer_.WaitForNewState();
- ::testing::AssertionResult result =
- VerifyState(std::move(state), kStateInitializingString);
- if (!result) {
- return ::testing::AssertionFailure()
- << "First state after Initialize() does not match expected:"
- << std::endl
- << result.message();
- }
-
- state = test_state_observer_.WaitForNewState();
- result = VerifyState(std::move(state), expected_initialize_success
- ? kStateNotLoggingString
- : kStateUninitializedString,
- expected_log_exists, false, "");
- if (!result) {
- return ::testing::AssertionFailure()
- << "Second state after Initialize() does not match expected:"
- << std::endl
- << result.message();
- }
-
- return ::testing::AssertionSuccess();
- }
-
- // If |custom_log_path| is empty path, |net_log_file_writer_| will use its
- // default log path, which is cached in |default_log_path_|.
- WARN_UNUSED_RESULT::testing::AssertionResult StartThenVerifyNewState(
- const base::FilePath& custom_log_path,
- net::NetLogCaptureMode capture_mode,
- const std::string& expected_capture_mode_string,
- const URLRequestContextGetterList& context_getters) {
- net_log_file_writer_.StartNetLog(custom_log_path, capture_mode,
- context_getters);
- std::unique_ptr<base::DictionaryValue> state =
- test_state_observer_.WaitForNewState();
- ::testing::AssertionResult result =
- VerifyState(std::move(state), kStateStartingLogString);
- if (!result) {
- return ::testing::AssertionFailure()
- << "First state after StartNetLog() does not match expected:"
- << std::endl
- << result.message();
- }
-
- state = test_state_observer_.WaitForNewState();
- result = VerifyState(std::move(state), kStateLoggingString, true, true,
- expected_capture_mode_string);
- if (!result) {
- return ::testing::AssertionFailure()
- << "Second state after StartNetLog() does not match expected:"
- << std::endl
- << result.message();
- }
-
- // Make sure GetFilePath() returns empty path when logging.
- base::FilePath actual_log_path = FileWriterGetFilePathToCompletedLog();
- if (!actual_log_path.empty()) {
- return ::testing::AssertionFailure()
- << "GetFilePath() should return empty path after logging starts."
- << " Actual: " << ::testing::PrintToString(actual_log_path);
- }
-
- return ::testing::AssertionSuccess();
- }
-
- // If |custom_log_path| is empty path, it's assumed the log file with be at
- // |default_log_path_|.
- WARN_UNUSED_RESULT ::testing::AssertionResult StopThenVerifyNewStateAndFile(
- const base::FilePath& custom_log_path,
- std::unique_ptr<base::DictionaryValue> polled_data,
- scoped_refptr<net::URLRequestContextGetter> context_getter,
- const std::string& expected_capture_mode_string) {
- net_log_file_writer_.StopNetLog(std::move(polled_data), context_getter);
- std::unique_ptr<base::DictionaryValue> state =
- test_state_observer_.WaitForNewState();
- ::testing::AssertionResult result =
- VerifyState(std::move(state), kStateStoppingLogString);
- if (!result) {
- return ::testing::AssertionFailure()
- << "First state after StopNetLog() does not match expected:"
- << std::endl
- << result.message();
- }
-
- state = test_state_observer_.WaitForNewState();
- result = VerifyState(std::move(state), kStateNotLoggingString, true, true,
- expected_capture_mode_string);
- if (!result) {
- return ::testing::AssertionFailure()
- << "Second state after StopNetLog() does not match expected:"
- << std::endl
- << result.message();
- }
-
- // Make sure GetFilePath() returns the expected path.
- const base::FilePath& expected_log_path =
- custom_log_path.empty() ? default_log_path_ : custom_log_path;
- base::FilePath actual_log_path = FileWriterGetFilePathToCompletedLog();
- if (actual_log_path != expected_log_path) {
- return ::testing::AssertionFailure()
- << "GetFilePath() returned incorrect path after logging stopped."
- << std::endl
- << " Actual: " << ::testing::PrintToString(actual_log_path)
- << std::endl
- << " Expected: " << ::testing::PrintToString(expected_log_path);
- }
-
- // Make sure the generated log file is valid.
- std::unique_ptr<base::DictionaryValue> root;
- result = ReadCompleteLogFile(expected_log_path, &root);
- if (!result) {
- return ::testing::AssertionFailure()
- << "Log file after logging stopped is not valid:" << std::endl
- << result.message();
- }
-
- return ::testing::AssertionSuccess();
- }
-
- protected:
- ChromeNetLog net_log_;
-
- // |net_log_file_writer_| is initialized after |net_log_| so that it can stop
- // obvserving on destruction.
- NetLogFileWriter net_log_file_writer_;
-
- base::ScopedTempDir log_temp_dir_;
-
- // The default log path that |net_log_file_writer_| will use is cached here.
- base::FilePath default_log_path_;
-
- base::Thread file_thread_;
- base::Thread net_thread_;
-
- TestStateObserver test_state_observer_;
-
- private:
- // Allows tasks to be posted to the main thread.
- base::MessageLoop message_loop_;
-};
-
-TEST_F(NetLogFileWriterTest, InitFail) {
- // Override net_log_file_writer_'s default log base directory getter to always
- // fail.
- net_log_file_writer_.SetDefaultLogBaseDirectoryGetterForTest(
- base::Bind([](base::FilePath* path) -> bool { return false; }));
-
- // Initialization should fail due to the override.
- ASSERT_TRUE(InitializeThenVerifyNewState(false, false));
-
- // NetLogFileWriter::GetFilePath() should return empty path if uninitialized.
- EXPECT_TRUE(FileWriterGetFilePathToCompletedLog().empty());
-}
-
-TEST_F(NetLogFileWriterTest, InitWithoutExistingLog) {
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- // NetLogFileWriter::GetFilePathToCompletedLog() should return empty path when
- // no log file exists.
- EXPECT_TRUE(FileWriterGetFilePathToCompletedLog().empty());
-}
-
-TEST_F(NetLogFileWriterTest, InitWithExistingLog) {
- // Create and close an empty log file to simulate existence of a previous log
- // file.
- ASSERT_TRUE(
- base::CreateDirectoryAndGetError(default_log_path_.DirName(), nullptr));
- base::ScopedFILE empty_file(base::OpenFile(default_log_path_, "w"));
- ASSERT_TRUE(empty_file.get());
- empty_file.reset();
-
- ASSERT_TRUE(InitializeThenVerifyNewState(true, true));
-
- EXPECT_EQ(default_log_path_, FileWriterGetFilePathToCompletedLog());
-}
-
-TEST_F(NetLogFileWriterTest, StartAndStopWithAllCaptureModes) {
- const net::NetLogCaptureMode capture_modes[3] = {
- net::NetLogCaptureMode::Default(),
- net::NetLogCaptureMode::IncludeCookiesAndCredentials(),
- net::NetLogCaptureMode::IncludeSocketBytes()};
-
- const std::string capture_mode_strings[3] = {
- kCaptureModeDefaultString, kCaptureModeIncludeCookiesAndCredentialsString,
- kCaptureModeIncludeSocketBytesString};
-
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- // For each capture mode, start and stop |net_log_file_writer_| in that mode.
- for (int i = 0; i < 3; ++i) {
- // StartNetLog(), should result in state change.
- ASSERT_TRUE(StartThenVerifyNewState(base::FilePath(), capture_modes[i],
- capture_mode_strings[i],
- URLRequestContextGetterList()));
-
- // Calling StartNetLog() again should be a no-op. Try doing StartNetLog()
- // with various capture modes; they should all be ignored and result in no
- // state change.
- net_log_file_writer_.StartNetLog(base::FilePath(), capture_modes[i],
- URLRequestContextGetterList());
- net_log_file_writer_.StartNetLog(base::FilePath(),
- capture_modes[(i + 1) % 3],
- URLRequestContextGetterList());
- net_log_file_writer_.StartNetLog(base::FilePath(),
- capture_modes[(i + 2) % 3],
- URLRequestContextGetterList());
-
- // StopNetLog(), should result in state change. The capture mode should
- // match that of the first StartNetLog() call (called by
- // StartThenVerifyNewState()).
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(
- base::FilePath(), nullptr, nullptr, capture_mode_strings[i]));
-
- // Stopping a second time should be a no-op.
- net_log_file_writer_.StopNetLog(nullptr, nullptr);
- }
-
- // Start and stop one more time just to make sure the last StopNetLog() call
- // was properly ignored and left |net_log_file_writer_| in a valid state.
- ASSERT_TRUE(StartThenVerifyNewState(base::FilePath(), capture_modes[0],
- capture_mode_strings[0],
- URLRequestContextGetterList()));
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
- capture_mode_strings[0]));
-}
-
-// Verify the file sizes after two consecutive starts/stops are the same (even
-// if some junk data is added in between).
-TEST_F(NetLogFileWriterTest, StartClearsFile) {
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- ASSERT_TRUE(StartThenVerifyNewState(
- base::FilePath(), net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, URLRequestContextGetterList()));
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
- kCaptureModeDefaultString));
-
- int64_t stop_file_size;
- EXPECT_TRUE(base::GetFileSize(default_log_path_, &stop_file_size));
-
- // Add some junk at the end of the file.
- std::string junk_data("Hello");
- EXPECT_TRUE(base::AppendToFile(default_log_path_, junk_data.c_str(),
- junk_data.size()));
-
- int64_t junk_file_size;
- EXPECT_TRUE(base::GetFileSize(default_log_path_, &junk_file_size));
- EXPECT_GT(junk_file_size, stop_file_size);
-
- // Start and stop again and make sure the file is back to the size it was
- // before adding the junk data.
- ASSERT_TRUE(StartThenVerifyNewState(
- base::FilePath(), net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, URLRequestContextGetterList()));
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
- kCaptureModeDefaultString));
-
- int64_t new_stop_file_size;
- EXPECT_TRUE(base::GetFileSize(default_log_path_, &new_stop_file_size));
-
- EXPECT_EQ(stop_file_size, new_stop_file_size);
-}
-
-// Adds an event to the log file, then checks that the file is larger than
-// the file created without that event.
-TEST_F(NetLogFileWriterTest, AddEvent) {
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- ASSERT_TRUE(StartThenVerifyNewState(
- base::FilePath(), net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, URLRequestContextGetterList()));
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
- kCaptureModeDefaultString));
-
- // Get file size without the event.
- int64_t stop_file_size;
- EXPECT_TRUE(base::GetFileSize(default_log_path_, &stop_file_size));
-
- ASSERT_TRUE(StartThenVerifyNewState(
- base::FilePath(), net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, URLRequestContextGetterList()));
-
- net_log_.AddGlobalEntry(net::NetLogEventType::CANCELLED);
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
- kCaptureModeDefaultString));
-
- // Get file size after adding the event and make sure it's larger than before.
- int64_t new_stop_file_size;
- EXPECT_TRUE(base::GetFileSize(default_log_path_, &new_stop_file_size));
- EXPECT_GE(new_stop_file_size, stop_file_size);
-}
-
-// Using a custom path to make sure logging can still occur when the path has
-// changed.
-TEST_F(NetLogFileWriterTest, AddEventCustomPath) {
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- base::FilePath::CharType kCustomRelativePath[] =
- FILE_PATH_LITERAL("custom/custom/chrome-net-export-log.json");
- base::FilePath custom_log_path =
- log_temp_dir_.GetPath().Append(kCustomRelativePath);
- EXPECT_TRUE(
- base::CreateDirectoryAndGetError(custom_log_path.DirName(), nullptr));
-
- ASSERT_TRUE(StartThenVerifyNewState(
- custom_log_path, net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, URLRequestContextGetterList()));
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr, nullptr,
- kCaptureModeDefaultString));
-
- // Get file size without the event.
- int64_t stop_file_size;
- EXPECT_TRUE(base::GetFileSize(custom_log_path, &stop_file_size));
-
- ASSERT_TRUE(StartThenVerifyNewState(
- custom_log_path, net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, URLRequestContextGetterList()));
-
- net_log_.AddGlobalEntry(net::NetLogEventType::CANCELLED);
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr, nullptr,
- kCaptureModeDefaultString));
-
- // Get file size after adding the event and make sure it's larger than before.
- int64_t new_stop_file_size;
- EXPECT_TRUE(base::GetFileSize(custom_log_path, &new_stop_file_size));
- EXPECT_GE(new_stop_file_size, stop_file_size);
-}
-
-TEST_F(NetLogFileWriterTest, StopWithPolledDataAndContextGetter) {
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- // Create dummy polled data
- const char kDummyPolledDataPath[] = "dummy_path";
- const char kDummyPolledDataString[] = "dummy_info";
- std::unique_ptr<base::DictionaryValue> dummy_polled_data =
- base::MakeUnique<base::DictionaryValue>();
- dummy_polled_data->SetString(kDummyPolledDataPath, kDummyPolledDataString);
-
- // Create test context getter on |net_thread_| and wait for it to finish.
- scoped_refptr<net::TestURLRequestContextGetter> context_getter;
- const int kDummyQuicParam = 75;
- net::TestClosure init_done;
- net_thread_.task_runner()->PostTaskAndReply(
- FROM_HERE, base::Bind(&SetUpTestContextGetterWithQuicTimeoutInfo,
- &net_log_, kDummyQuicParam, &context_getter),
- init_done.closure());
- init_done.WaitForResult();
-
- ASSERT_TRUE(StartThenVerifyNewState(
- base::FilePath(), net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, URLRequestContextGetterList()));
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(
- base::FilePath(), std::move(dummy_polled_data), context_getter,
- kCaptureModeDefaultString));
-
- // Read polledData from log file.
- std::unique_ptr<base::DictionaryValue> root;
- ASSERT_TRUE(ReadCompleteLogFile(default_log_path_, &root));
- base::DictionaryValue* polled_data;
- ASSERT_TRUE(root->GetDictionary("polledData", &polled_data));
-
- // Check that it contains the field from the polled data that was passed in.
- std::string dummy_string;
- ASSERT_TRUE(polled_data->GetString(kDummyPolledDataPath, &dummy_string));
- EXPECT_EQ(kDummyPolledDataString, dummy_string);
-
- // Check that it also contains the field from the URLRequestContext that was
- // passed in.
- base::DictionaryValue* quic_info;
- ASSERT_TRUE(polled_data->GetDictionary("quicInfo", &quic_info));
- base::Value* timeout_value = nullptr;
- int timeout;
- ASSERT_TRUE(
- quic_info->Get("idle_connection_timeout_seconds", &timeout_value));
- ASSERT_TRUE(timeout_value->GetAsInteger(&timeout));
- EXPECT_EQ(kDummyQuicParam, timeout);
-}
-
-TEST_F(NetLogFileWriterTest, StartWithContextGetters) {
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- // Create test context getter and request on |net_thread_| and wait for it to
- // finish.
- const std::string kDummyUrl = "blah:blah";
- scoped_refptr<net::TestURLRequestContextGetter> context_getter;
- std::unique_ptr<net::URLRequest> request;
- net::TestDelegate delegate;
- delegate.set_quit_on_complete(false);
-
- net::TestClosure init_done;
- net_thread_.task_runner()->PostTaskAndReply(
- FROM_HERE,
- base::Bind(&SetUpTestContextGetterWithRequest, &net_log_, GURL(kDummyUrl),
- &delegate, &context_getter, &request),
- init_done.closure());
- init_done.WaitForResult();
-
- ASSERT_TRUE(StartThenVerifyNewState(
- base::FilePath(), net::NetLogCaptureMode::Default(),
- kCaptureModeDefaultString, {context_getter}));
-
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
- kCaptureModeDefaultString));
-
- // Read events from log file.
- std::unique_ptr<base::DictionaryValue> root;
- ASSERT_TRUE(ReadCompleteLogFile(default_log_path_, &root));
- base::ListValue* events;
- ASSERT_TRUE(root->GetList("events", &events));
-
- // Check there is at least one event as a result of the ongoing request.
- EXPECT_GE(events->GetSize(), 1u);
-
- // Check the URL in the params of the first event.
- base::DictionaryValue* event;
- EXPECT_TRUE(events->GetDictionary(0, &event));
- base::DictionaryValue* event_params;
- EXPECT_TRUE(event->GetDictionary("params", &event_params));
- std::string event_url;
- EXPECT_TRUE(event_params->GetString("url", &event_url));
- EXPECT_EQ(kDummyUrl, event_url);
-
- net_thread_.task_runner()->DeleteSoon(FROM_HERE, request.release());
-}
-
-TEST_F(NetLogFileWriterTest, ReceiveStartWhileInitializing) {
- // Trigger initialization of |net_log_file_writer_|.
- net_log_file_writer_.Initialize(file_thread_.task_runner(),
- net_thread_.task_runner());
-
- // Before running the main message loop, tell |net_log_file_writer_| to start
- // logging. Not running the main message loop prevents the initialization
- // process from completing, so this ensures that StartNetLog() is received
- // before |net_log_file_writer_| finishes initialization, which means this
- // should be a no-op.
- net_log_file_writer_.StartNetLog(base::FilePath(),
- net::NetLogCaptureMode::Default(),
- URLRequestContextGetterList());
-
- // Now run the main message loop. Make sure StartNetLog() was ignored by
- // checking that the next two states are "initializing" followed by
- // "not-logging".
- std::unique_ptr<base::DictionaryValue> state =
- test_state_observer_.WaitForNewState();
- ASSERT_TRUE(VerifyState(std::move(state), kStateInitializingString));
- state = test_state_observer_.WaitForNewState();
- ASSERT_TRUE(
- VerifyState(std::move(state), kStateNotLoggingString, false, false, ""));
-}
-
-TEST_F(NetLogFileWriterTest, ReceiveStartWhileStoppingLog) {
- ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
-
- // Call StartNetLog() on |net_log_file_writer_| and wait for the state change.
- ASSERT_TRUE(StartThenVerifyNewState(
- base::FilePath(), net::NetLogCaptureMode::IncludeSocketBytes(),
- kCaptureModeIncludeSocketBytesString, URLRequestContextGetterList()));
-
- // Tell |net_log_file_writer_| to stop logging.
- net_log_file_writer_.StopNetLog(nullptr, nullptr);
-
- // Before running the main message loop, tell |net_log_file_writer_| to start
- // logging. Not running the main message loop prevents the stopping process
- // from completing, so this ensures StartNetLog() is received before
- // |net_log_file_writer_| finishes stopping, which means this should be a
- // no-op.
- net_log_file_writer_.StartNetLog(base::FilePath(),
- net::NetLogCaptureMode::Default(),
- URLRequestContextGetterList());
-
- // Now run the main message loop. Make sure the last StartNetLog() was
- // ignored by checking that the next two states are "stopping-log" followed by
- // "not-logging". Also make sure the capture mode matches that of the first
- // StartNetLog() call (called by StartThenVerifyState()).
- std::unique_ptr<base::DictionaryValue> state =
- test_state_observer_.WaitForNewState();
- ASSERT_TRUE(VerifyState(std::move(state), kStateStoppingLogString));
- state = test_state_observer_.WaitForNewState();
- ASSERT_TRUE(VerifyState(std::move(state), kStateNotLoggingString, true, true,
- kCaptureModeIncludeSocketBytesString));
-}
-
-} // namespace net_log
diff --git a/chromium/components/net_log/resources/net_export.css b/chromium/components/net_log/resources/net_export.css
index 322df333726..03a68843a03 100644
--- a/chromium/components/net_log/resources/net_export.css
+++ b/chromium/components/net_log/resources/net_export.css
@@ -20,7 +20,7 @@ button {
font-weight: bold;
margin: 10px auto;
padding: 1em;
- width: 14em;
+ width: 15em;
}
h2 {
diff --git a/chromium/components/net_log/resources/net_export.html b/chromium/components/net_log/resources/net_export.html
index b612b7ea30e..823892b04c0 100644
--- a/chromium/components/net_log/resources/net_export.html
+++ b/chromium/components/net_log/resources/net_export.html
@@ -70,6 +70,13 @@
Include raw bytes (will include cookies and credentials)
</label>
</div>
+ <div>
+ <label>
+ Maximum log size (megabytes):
+ <input id='log-max-filesize' value="100" size="4" />
+ (Blank means unlimited).
+ </label>
+ </div>
</div>
</div>
@@ -127,7 +134,28 @@
</if>
<if expr="not(is_ios or is_android)">
- Attach the log file to your bug report, and you are done!
+ <p>Attach the log file to your bug report, and you are done!</p>
+
+ <p>
+ If the file is too big, it can be compressed or truncated.
+ <a href="#" id="toobig-read-more-link">Read more...</a>
+ </p>
+
+ <div id="toobig-read-more">
+ <ul>
+ <li>The maximum log size can be specified when starting
+ logging.</li>
+ <li>Compressing the log file before attaching is a good
+ idea.</li>
+ <li>Capture logs over a shorter period of time; stop
+ logging once the issue has reproduced.</li>
+ <li>Existing log files can be shrunk using
+ <a
+ href="https://chromium.googlesource.com/chromium/src/+/master/net/tools/truncate_net_log.py"
+ target="_blank">
+ net/tools/truncate_net_log.py</a>.</li>
+ </ul>
+ </div>
</if>
</div>
diff --git a/chromium/components/net_log/resources/net_export.js b/chromium/components/net_log/resources/net_export.js
index be7c8e24f51..9ecbdfe6330 100644
--- a/chromium/components/net_log/resources/net_export.js
+++ b/chromium/components/net_log/resources/net_export.js
@@ -33,8 +33,11 @@ var NetExportView = (function() {
var kIdCaptureModeStopped = 'capture-mode-stopped';
var kIdFilePathStoppedLogging = 'file-path-stopped';
var kIdStartOverButton = 'startover';
- var kIdReadMoreLink = 'privacy-read-more-link';
- var kIdReadMoreDiv = 'privacy-read-more'
+ var kIdPrivacyReadMoreLink = 'privacy-read-more-link';
+ var kIdPrivacyReadMoreDiv = 'privacy-read-more'
+ var kIdTooBigReadMoreLink = 'toobig-read-more-link';
+ var kIdTooBigReadMoreDiv = 'toobig-read-more'
+ var kIdLogMaxFileSizeInput = 'log-max-filesize'
/**
* @constructor
@@ -54,9 +57,21 @@ var NetExportView = (function() {
* Starts saving NetLog data to a file.
*/
onStartLogging_: function() {
+ // Determine the capture mode to use.
var logMode =
document.querySelector('input[name="log-mode"]:checked').value;
- chrome.send('startNetLog', [logMode]);
+
+ // Determine the maximum file size, as the number of bytes (or -1 to mean
+ // no limit)
+ var maxLogFileSizeBytes = -1;
+ var fileSizeString = $(kIdLogMaxFileSizeInput).value;
+ var numMegabytes = parseFloat(fileSizeString);
+ if (!isNaN(numMegabytes)) {
+ // Convert to an integral number of bytes.
+ maxLogFileSizeBytes = Math.round(numMegabytes * 1024 * 1024);
+ }
+
+ chrome.send('startNetLog', [logMode, maxLogFileSizeBytes]);
},
/**
@@ -220,8 +235,14 @@ var NetExportView = (function() {
$(kIdCaptureModeStopped).textContent = this.getCaptureModeText_(info);
// Hook up the "read more..." link for privacy information.
- $(kIdReadMoreLink).onclick = this.showPrivacyReadMore_.bind(this, true);
+ $(kIdPrivacyReadMoreLink).onclick =
+ this.showPrivacyReadMore_.bind(this, true);
this.showPrivacyReadMore_(false);
+
+ // Hook up the "read more..." link for reducing log size information.
+ $(kIdTooBigReadMoreLink).onclick =
+ this.showTooBigReadMore_.bind(this, true);
+ this.showTooBigReadMore_(false);
},
/**
@@ -241,8 +262,13 @@ var NetExportView = (function() {
},
showPrivacyReadMore_: function(show) {
- $(kIdReadMoreDiv).hidden = !show;
- $(kIdReadMoreLink).hidden = show;
+ $(kIdPrivacyReadMoreDiv).hidden = !show;
+ $(kIdPrivacyReadMoreLink).hidden = show;
+ },
+
+ showTooBigReadMore_: function(show) {
+ $(kIdTooBigReadMoreDiv).hidden = !show;
+ $(kIdTooBigReadMoreLink).hidden = show;
},
showStateDiv_: function(divId) {
diff --git a/chromium/components/neterror/resources/neterror.css b/chromium/components/neterror/resources/neterror.css
index 4649cd82bbb..c7cd42e70cc 100644
--- a/chromium/components/neterror/resources/neterror.css
+++ b/chromium/components/neterror/resources/neterror.css
@@ -462,13 +462,6 @@ html[subframe] body {
}
}
-@media (min-width: 600px) and (max-width: 736px) and (orientation: landscape) {
- .offline .interstitial-wrapper {
- margin-left: 0;
- margin-right: 0;
- }
-}
-
@media (min-width: 420px) and (max-width: 736px) and
(min-height: 240px) and (max-height: 420px) and
(orientation:landscape) {
@@ -514,3 +507,31 @@ html[subframe] body {
width: auto;
}
}
+
+.arcade-mode,
+.arcade-mode .runner-container,
+.arcade-mode .runner-canvas {
+ max-width: 100%;
+ overflow: hidden;
+}
+
+.arcade-mode #buttons,
+.arcade-mode #main-content {
+ opacity: 0;
+ overflow: hidden;
+}
+
+.arcade-mode .interstitial-wrapper {
+ height: 100vh;
+ max-width: 100%;
+ overflow: hidden;
+}
+
+.arcade-mode .runner-container {
+ left: 0;
+ margin: auto;
+ right: 0;
+ transform-origin: top center;
+ transition: transform 250ms cubic-bezier(0.4, 0.0, 1, 1) .4s;
+ z-index: 2;
+}
diff --git a/chromium/components/neterror/resources/neterror.html b/chromium/components/neterror/resources/neterror.html
index 3851e4fee73..615d17162ed 100644
--- a/chromium/components/neterror/resources/neterror.html
+++ b/chromium/components/neterror/resources/neterror.html
@@ -1,18 +1,19 @@
<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0,
maximum-scale=1.0, user-scalable=no">
- <title i18n-content="title"></title>
- <link rel="stylesheet" href="../../../components/security_interstitials/core/browser/resources/interstitial_common.css">
- <link rel="stylesheet" href="../../../components/security_interstitials/core/browser/resources/interstitial_v2.css">
+ <title>$i18n{title}</title>
+ <link rel="stylesheet" href="../../../components/security_interstitials/core/common/resources/interstitial_core.css">
+ <link rel="stylesheet" href="../../../components/security_interstitials/core/common/resources/interstitial_common.css">
<link rel="stylesheet" href="neterror.css">
- <script src="../../../components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js"></script>
+ <script src="../../../components/security_interstitials/core/common/resources/interstitial_common.js"></script>
+ <script src="../../../components/security_interstitials/core/common/resources/interstitial_mobile_nav.js"></script>
<script src="neterror.js"></script>
<script src="offline.js"></script>
</head>
-<body id="t" i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
+<body id="t" style="font-family: $i18n{fontfamily}; font-size: $i18n{fontsize}">
<div id="main-frame-error" class="interstitial-wrapper">
<div id="main-content">
<div class="icon"
diff --git a/chromium/components/neterror/resources/neterror.js b/chromium/components/neterror/resources/neterror.js
index 16175091a1d..a7bc93484b3 100644
--- a/chromium/components/neterror/resources/neterror.js
+++ b/chromium/components/neterror/resources/neterror.js
@@ -4,19 +4,19 @@
function toggleHelpBox() {
var helpBoxOuter = document.getElementById('details');
- helpBoxOuter.classList.toggle('hidden');
+ helpBoxOuter.classList.toggle(HIDDEN_CLASS);
var detailsButton = document.getElementById('details-button');
- if (helpBoxOuter.classList.contains('hidden'))
+ if (helpBoxOuter.classList.contains(HIDDEN_CLASS))
detailsButton.innerText = detailsButton.detailsText;
else
detailsButton.innerText = detailsButton.hideDetailsText;
// Details appears over the main content on small screens.
if (mobileNav) {
- document.getElementById('main-content').classList.toggle('hidden');
+ document.getElementById('main-content').classList.toggle(HIDDEN_CLASS);
var runnerContainer = document.querySelector('.runner-container');
if (runnerContainer) {
- runnerContainer.classList.toggle('hidden');
+ runnerContainer.classList.toggle(HIDDEN_CLASS);
}
}
}
diff --git a/chromium/components/neterror/resources/offline.js b/chromium/components/neterror/resources/offline.js
index ed3582f278d..f7235b3b8a1 100644
--- a/chromium/components/neterror/resources/offline.js
+++ b/chromium/components/neterror/resources/offline.js
@@ -22,7 +22,7 @@ function Runner(outerContainerId, opt_config) {
this.snackbarEl = null;
this.config = opt_config || Runner.config;
-
+ // Logical dimensions of the container.
this.dimensions = Runner.defaultDimensions;
this.canvas = null;
@@ -96,6 +96,9 @@ var IS_MOBILE = /Android/.test(window.navigator.userAgent) || IS_IOS;
/** @const */
var IS_TOUCH_ENABLED = 'ontouchstart' in window;
+/** @const */
+var ARCADE_MODE_URL = 'chrome://dino/';
+
/**
* Default game configuration.
* @enum {number}
@@ -121,7 +124,9 @@ Runner.config = {
MOBILE_SPEED_COEFFICIENT: 1.2,
RESOURCE_TEMPLATE_ID: 'audio-resources',
SPEED: 6,
- SPEED_DROP_COEFFICIENT: 3
+ SPEED_DROP_COEFFICIENT: 3,
+ ARCADE_MODE_INITIAL_TOP_POSITION: 35,
+ ARCADE_MODE_TOP_POSITION_PERCENT: 0.1
};
@@ -140,6 +145,7 @@ Runner.defaultDimensions = {
* @enum {string}
*/
Runner.classes = {
+ ARCADE_MODE: 'arcade-mode',
CANVAS: 'runner-canvas',
CONTAINER: 'runner-container',
CRASHED: 'crashed',
@@ -225,7 +231,6 @@ Runner.events = {
LOAD: 'load'
};
-
Runner.prototype = {
/**
* Whether the easter egg has been disabled. CrOS enterprise enrolled devices.
@@ -418,6 +423,12 @@ Runner.prototype = {
boxStyles.paddingLeft.length - 2));
this.dimensions.WIDTH = this.outerContainerEl.offsetWidth - padding * 2;
+ if (this.isArcadeMode()) {
+ this.dimensions.WIDTH = Math.min(DEFAULT_WIDTH, this.dimensions.WIDTH);
+ if (this.activated) {
+ this.setArcadeModeContainerScale();
+ }
+ }
// Redraw the elements back onto the canvas.
if (this.canvas) {
@@ -486,6 +497,9 @@ Runner.prototype = {
* Update the game status to started.
*/
startGame: function() {
+ if (this.isArcadeMode()) {
+ this.setArcadeMode();
+ }
this.runningTime = 0;
this.playingIntro = false;
this.tRex.playingIntro = false;
@@ -826,6 +840,40 @@ Runner.prototype = {
},
/**
+ * Whether the game should go into arcade mode.
+ * @return {boolean}
+ */
+ isArcadeMode: function() {
+ return document.title == ARCADE_MODE_URL;
+ },
+
+ /**
+ * Hides offline messaging for a fullscreen game only experience.
+ */
+ setArcadeMode: function() {
+ document.body.classList.add(Runner.classes.ARCADE_MODE);
+ this.setArcadeModeContainerScale();
+ },
+
+ /**
+ * Sets the scaling for arcade mode.
+ */
+ setArcadeModeContainerScale: function() {
+ var windowHeight = window.innerHeight;
+ var scaleHeight = windowHeight / this.dimensions.HEIGHT;
+ var scaleWidth = window.innerWidth / this.dimensions.WIDTH;
+ var scale = Math.max(1, Math.min(scaleHeight, scaleWidth));
+ var scaledCanvasHeight = this.dimensions.HEIGHT * scale;
+ // Positions the game container at 10% of the available vertical window
+ // height minus the game container height.
+ var translateY = Math.max(0, (windowHeight - scaledCanvasHeight -
+ Runner.config.ARCADE_MODE_INITIAL_TOP_POSITION) *
+ Runner.config.ARCADE_MODE_TOP_POSITION_PERCENT);
+ this.containerEl.style.transform = 'scale(' + scale + ') translateY(' +
+ translateY + 'px)';
+ },
+
+ /**
* Pause the game if the tab is not in focus.
*/
onVisibilityChange: function(e) {
diff --git a/chromium/components/network_session_configurator/BUILD.gn b/chromium/components/network_session_configurator/BUILD.gn
deleted file mode 100644
index 63593e28c4f..00000000000
--- a/chromium/components/network_session_configurator/BUILD.gn
+++ /dev/null
@@ -1,34 +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.
-
-static_library("network_session_configurator") {
- sources = [
- "network_session_configurator.cc",
- "network_session_configurator.h",
- ]
-
- public_deps = [
- "//base",
- "//net",
- ]
-
- deps = [
- "//components/data_reduction_proxy/core/common",
- "//components/variations",
- "//components/version_info",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "network_session_configurator_unittest.cc",
- ]
- deps = [
- ":network_session_configurator",
- "//base/test:test_support",
- "//components/variations:variations",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/network_session_configurator/DEPS b/chromium/components/network_session_configurator/DEPS
deleted file mode 100644
index e810840c706..00000000000
--- a/chromium/components/network_session_configurator/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-include_rules = [
- "+base",
- "+components/data_reduction_proxy/core/common",
- "+components/variations",
- "+components/version_info",
- "+net",
-]
diff --git a/chromium/components/network_session_configurator/browser/BUILD.gn b/chromium/components/network_session_configurator/browser/BUILD.gn
new file mode 100644
index 00000000000..462bc665371
--- /dev/null
+++ b/chromium/components/network_session_configurator/browser/BUILD.gn
@@ -0,0 +1,33 @@
+# 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.
+
+static_library("browser") {
+ sources = [
+ "network_session_configurator.cc",
+ "network_session_configurator.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//components/network_session_configurator/common",
+ "//net",
+ ]
+
+ deps = [
+ "//components/variations",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "network_session_configurator_unittest.cc",
+ ]
+ deps = [
+ ":browser",
+ "//base/test:test_support",
+ "//components/variations:variations",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/network_session_configurator/browser/DEPS b/chromium/components/network_session_configurator/browser/DEPS
new file mode 100644
index 00000000000..a03c9b63fe5
--- /dev/null
+++ b/chromium/components/network_session_configurator/browser/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+base",
+ "+components/network_session_configurator",
+ "+components/variations",
+ "+net",
+]
diff --git a/chromium/components/network_session_configurator/browser/network_session_configurator.cc b/chromium/components/network_session_configurator/browser/network_session_configurator.cc
new file mode 100644
index 00000000000..2c34461e6b1
--- /dev/null
+++ b/chromium/components/network_session_configurator/browser/network_session_configurator.cc
@@ -0,0 +1,431 @@
+// 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/network_session_configurator/browser/network_session_configurator.h"
+
+#include <map>
+#include <unordered_set>
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "components/network_session_configurator/common/network_switches.h"
+#include "components/variations/variations_associated_data.h"
+#include "net/http/http_stream_factory.h"
+#include "net/quic/chromium/quic_utils_chromium.h"
+#include "net/quic/core/quic_packets.h"
+#include "net/spdy/core/spdy_protocol.h"
+
+namespace {
+
+// Map from name to value for all parameters associate with a field trial.
+using VariationParameters = std::map<std::string, std::string>;
+
+const char kTCPFastOpenFieldTrialName[] = "TCPFastOpen";
+const char kTCPFastOpenHttpsEnabledGroupName[] = "HttpsEnabled";
+
+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)
+// returns 0.
+int GetSwitchValueAsInt(const base::CommandLine& command_line,
+ const std::string& switch_name) {
+ int value;
+ if (!base::StringToInt(command_line.GetSwitchValueASCII(switch_name),
+ &value)) {
+ return 0;
+ }
+ return value;
+}
+
+// Returns the value associated with |key| in |params| or "" if the
+// key is not present in the map.
+const std::string& GetVariationParam(
+ const std::map<std::string, std::string>& params,
+ const std::string& key) {
+ std::map<std::string, std::string>::const_iterator it = params.find(key);
+ if (it == params.end())
+ return base::EmptyString();
+
+ return it->second;
+}
+
+void ConfigureTCPFastOpenParams(base::StringPiece tfo_trial_group,
+ net::HttpNetworkSession::Params* params) {
+ if (tfo_trial_group == kTCPFastOpenHttpsEnabledGroupName)
+ params->enable_tcp_fast_open_for_ssl = true;
+}
+
+net::SettingsMap GetHttp2Settings(
+ const VariationParameters& http2_trial_params) {
+ net::SettingsMap http2_settings;
+
+ const std::string settings_string =
+ GetVariationParam(http2_trial_params, "http2_settings");
+
+ base::StringPairs key_value_pairs;
+ if (!base::SplitStringIntoKeyValuePairs(settings_string, ':', ',',
+ &key_value_pairs)) {
+ return http2_settings;
+ }
+
+ for (auto key_value : key_value_pairs) {
+ uint32_t key;
+ if (!base::StringToUint(key_value.first, &key))
+ continue;
+ uint32_t value;
+ if (!base::StringToUint(key_value.second, &value))
+ continue;
+ http2_settings[static_cast<net::SpdySettingsIds>(key)] = value;
+ }
+
+ return http2_settings;
+}
+
+void ConfigureHttp2Params(base::StringPiece http2_trial_group,
+ const VariationParameters& http2_trial_params,
+ net::HttpNetworkSession::Params* params) {
+ if (http2_trial_group.starts_with(kHttp2FieldTrialDisablePrefix)) {
+ params->enable_http2 = false;
+ return;
+ }
+ params->http2_settings = GetHttp2Settings(http2_trial_params);
+}
+
+bool ShouldEnableQuic(base::StringPiece quic_trial_group,
+ const VariationParameters& quic_trial_params,
+ bool is_quic_force_disabled,
+ bool is_quic_force_enabled) {
+ if (is_quic_force_disabled)
+ return false;
+ if (is_quic_force_enabled)
+ return true;
+
+ return quic_trial_group.starts_with(kQuicFieldTrialEnabledGroupName) ||
+ quic_trial_group.starts_with(kQuicFieldTrialHttpsEnabledGroupName) ||
+ base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "enable_quic"), "true");
+}
+
+bool ShouldMarkQuicBrokenWhenNetworkBlackholes(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params,
+ "mark_quic_broken_when_network_blackholes"),
+ "true");
+}
+
+bool ShouldRetryWithoutAltSvcOnQuicErrors(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params,
+ "retry_without_alt_svc_on_quic_errors"),
+ "true");
+}
+
+net::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 net::ParseQuicConnectionOptions(it->second);
+}
+
+bool ShouldForceHolBlocking(const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "force_hol_blocking"), "true");
+}
+
+bool ShouldQuicCloseSessionsOnIpChange(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "close_sessions_on_ip_change"),
+ "true");
+}
+
+int GetQuicIdleConnectionTimeoutSeconds(
+ const VariationParameters& quic_trial_params) {
+ int value;
+ if (base::StringToInt(GetVariationParam(quic_trial_params,
+ "idle_connection_timeout_seconds"),
+ &value)) {
+ return value;
+ }
+ return 0;
+}
+
+int GetQuicReducedPingTimeoutSeconds(
+ const VariationParameters& quic_trial_params) {
+ int value;
+ if (base::StringToInt(
+ GetVariationParam(quic_trial_params, "reduced_ping_timeout_seconds"),
+ &value)) {
+ return value;
+ }
+ return 0;
+}
+
+int GetQuicPacketReaderYieldAfterDurationMilliseconds(
+ const VariationParameters& quic_trial_params) {
+ int value;
+ if (base::StringToInt(
+ GetVariationParam(quic_trial_params,
+ "packet_reader_yield_after_duration_milliseconds"),
+ &value)) {
+ return value;
+ }
+ return 0;
+}
+
+bool ShouldQuicRaceCertVerification(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "race_cert_verification"), "true");
+}
+
+bool ShouldQuicEstimateInitialRtt(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "estimate_initial_rtt"), "true");
+}
+
+bool ShouldQuicMigrateSessionsOnNetworkChange(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params,
+ "migrate_sessions_on_network_change"),
+ "true");
+}
+
+bool ShouldQuicMigrateSessionsEarly(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "migrate_sessions_early"), "true");
+}
+
+bool ShouldQuicAllowServerMigration(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "allow_server_migration"), "true");
+}
+
+size_t GetQuicMaxPacketLength(const VariationParameters& quic_trial_params) {
+ unsigned value;
+ if (base::StringToUint(
+ GetVariationParam(quic_trial_params, "max_packet_length"), &value)) {
+ return value;
+ }
+ return 0;
+}
+
+net::QuicVersionVector GetQuicVersions(
+ const VariationParameters& quic_trial_params) {
+ return network_session_configurator::ParseQuicVersions(
+ GetVariationParam(quic_trial_params, "quic_version"));
+}
+
+bool ShouldEnableServerPushCancelation(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "enable_server_push_cancellation"),
+ "true");
+}
+
+void ConfigureQuicParams(base::StringPiece quic_trial_group,
+ const VariationParameters& quic_trial_params,
+ bool is_quic_force_disabled,
+ bool is_quic_force_enabled,
+ const std::string& quic_user_agent_id,
+ net::HttpNetworkSession::Params* params) {
+ params->enable_quic =
+ ShouldEnableQuic(quic_trial_group, quic_trial_params,
+ is_quic_force_disabled, is_quic_force_enabled);
+ params->mark_quic_broken_when_network_blackholes =
+ ShouldMarkQuicBrokenWhenNetworkBlackholes(quic_trial_params);
+
+ params->enable_server_push_cancellation =
+ ShouldEnableServerPushCancelation(quic_trial_params);
+
+ params->retry_without_alt_svc_on_quic_errors =
+ ShouldRetryWithoutAltSvcOnQuicErrors(quic_trial_params);
+
+ if (params->enable_quic) {
+ params->quic_force_hol_blocking = ShouldForceHolBlocking(quic_trial_params);
+ params->quic_connection_options =
+ GetQuicConnectionOptions(quic_trial_params);
+ params->quic_close_sessions_on_ip_change =
+ ShouldQuicCloseSessionsOnIpChange(quic_trial_params);
+ int idle_connection_timeout_seconds =
+ GetQuicIdleConnectionTimeoutSeconds(quic_trial_params);
+ if (idle_connection_timeout_seconds != 0) {
+ params->quic_idle_connection_timeout_seconds =
+ idle_connection_timeout_seconds;
+ }
+ int reduced_ping_timeout_seconds =
+ GetQuicReducedPingTimeoutSeconds(quic_trial_params);
+ if (reduced_ping_timeout_seconds > 0 &&
+ reduced_ping_timeout_seconds < net::kPingTimeoutSecs) {
+ params->quic_reduced_ping_timeout_seconds = reduced_ping_timeout_seconds;
+ }
+ int packet_reader_yield_after_duration_milliseconds =
+ GetQuicPacketReaderYieldAfterDurationMilliseconds(quic_trial_params);
+ if (packet_reader_yield_after_duration_milliseconds != 0) {
+ params->quic_packet_reader_yield_after_duration_milliseconds =
+ packet_reader_yield_after_duration_milliseconds;
+ }
+ params->quic_race_cert_verification =
+ ShouldQuicRaceCertVerification(quic_trial_params);
+ params->quic_estimate_initial_rtt =
+ ShouldQuicEstimateInitialRtt(quic_trial_params);
+ params->quic_migrate_sessions_on_network_change =
+ ShouldQuicMigrateSessionsOnNetworkChange(quic_trial_params);
+ params->quic_migrate_sessions_early =
+ ShouldQuicMigrateSessionsEarly(quic_trial_params);
+ params->quic_allow_server_migration =
+ ShouldQuicAllowServerMigration(quic_trial_params);
+ }
+
+ size_t max_packet_length = GetQuicMaxPacketLength(quic_trial_params);
+ if (max_packet_length != 0) {
+ params->quic_max_packet_length = max_packet_length;
+ }
+
+ params->quic_user_agent_id = quic_user_agent_id;
+
+ net::QuicVersionVector supported_versions =
+ GetQuicVersions(quic_trial_params);
+ if (!supported_versions.empty())
+ params->quic_supported_versions = supported_versions;
+}
+
+} // anonymous namespace
+
+namespace network_session_configurator {
+
+net::QuicVersionVector ParseQuicVersions(const std::string& quic_versions) {
+ net::QuicVersionVector supported_versions;
+ net::QuicVersionVector all_supported_versions = net::AllSupportedVersions();
+
+ 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) {
+ supported_versions.push_back(*it);
+ // Remove the supported version to deduplicate versions extracted from
+ // |quic_versions|.
+ all_supported_versions.erase(it);
+ break;
+ }
+ it++;
+ }
+ }
+ return supported_versions;
+}
+
+void ParseCommandLineAndFieldTrials(const base::CommandLine& command_line,
+ bool is_quic_force_disabled,
+ const std::string& quic_user_agent_id,
+ net::HttpNetworkSession::Params* params) {
+ is_quic_force_disabled |= command_line.HasSwitch(switches::kDisableQuic);
+ bool is_quic_force_enabled = command_line.HasSwitch(switches::kEnableQuic);
+
+ std::string quic_trial_group =
+ base::FieldTrialList::FindFullName(kQuicFieldTrialName);
+ VariationParameters quic_trial_params;
+ if (!variations::GetVariationParams(kQuicFieldTrialName, &quic_trial_params))
+ quic_trial_params.clear();
+ ConfigureQuicParams(quic_trial_group, quic_trial_params,
+ is_quic_force_disabled, is_quic_force_enabled,
+ quic_user_agent_id, params);
+
+ std::string http2_trial_group =
+ base::FieldTrialList::FindFullName(kHttp2FieldTrialName);
+ VariationParameters http2_trial_params;
+ if (!variations::GetVariationParams(kHttp2FieldTrialName,
+ &http2_trial_params))
+ http2_trial_params.clear();
+ ConfigureHttp2Params(http2_trial_group, http2_trial_params, params);
+
+ const std::string tfo_trial_group =
+ base::FieldTrialList::FindFullName(kTCPFastOpenFieldTrialName);
+ ConfigureTCPFastOpenParams(tfo_trial_group, params);
+
+ // Command line flags override field trials.
+ if (command_line.HasSwitch(switches::kDisableHttp2))
+ params->enable_http2 = false;
+
+ if (params->enable_quic) {
+ if (command_line.HasSwitch(switches::kQuicConnectionOptions)) {
+ params->quic_connection_options = net::ParseQuicConnectionOptions(
+ command_line.GetSwitchValueASCII(switches::kQuicConnectionOptions));
+ }
+
+ if (command_line.HasSwitch(switches::kQuicMaxPacketLength)) {
+ unsigned value;
+ if (base::StringToUint(
+ command_line.GetSwitchValueASCII(switches::kQuicMaxPacketLength),
+ &value)) {
+ params->quic_max_packet_length = value;
+ }
+ }
+
+ if (command_line.HasSwitch(switches::kQuicVersion)) {
+ net::QuicVersionVector supported_versions =
+ network_session_configurator::ParseQuicVersions(
+ command_line.GetSwitchValueASCII(switches::kQuicVersion));
+ if (!supported_versions.empty())
+ params->quic_supported_versions = supported_versions;
+ }
+
+ if (command_line.HasSwitch(switches::kOriginToForceQuicOn)) {
+ std::string origins =
+ command_line.GetSwitchValueASCII(switches::kOriginToForceQuicOn);
+ for (const std::string& host_port : base::SplitString(
+ origins, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ if (host_port == "*")
+ params->origins_to_force_quic_on.insert(net::HostPortPair());
+ net::HostPortPair quic_origin =
+ net::HostPortPair::FromString(host_port);
+ if (!quic_origin.IsEmpty())
+ params->origins_to_force_quic_on.insert(quic_origin);
+ }
+ }
+ }
+
+ // Parameters only controlled by command line.
+ if (command_line.HasSwitch(switches::kEnableUserAlternateProtocolPorts)) {
+ params->enable_user_alternate_protocol_ports = true;
+ }
+ if (command_line.HasSwitch(switches::kIgnoreCertificateErrors)) {
+ params->ignore_certificate_errors = true;
+ }
+ UMA_HISTOGRAM_BOOLEAN(
+ "Net.Certificate.IgnoreErrors",
+ command_line.HasSwitch(switches::kIgnoreCertificateErrors));
+ if (command_line.HasSwitch(switches::kTestingFixedHttpPort)) {
+ params->testing_fixed_http_port =
+ GetSwitchValueAsInt(command_line, switches::kTestingFixedHttpPort);
+ }
+ if (command_line.HasSwitch(switches::kTestingFixedHttpsPort)) {
+ params->testing_fixed_https_port =
+ GetSwitchValueAsInt(command_line, switches::kTestingFixedHttpsPort);
+ }
+}
+
+} // namespace network_session_configurator
diff --git a/chromium/components/network_session_configurator/browser/network_session_configurator.h b/chromium/components/network_session_configurator/browser/network_session_configurator.h
new file mode 100644
index 00000000000..69e064bf364
--- /dev/null
+++ b/chromium/components/network_session_configurator/browser/network_session_configurator.h
@@ -0,0 +1,33 @@
+// 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_NETWORK_SESSION_CONFIGURATOR_BROWSER_NETWORK_SESSION_CONFIGURATOR_H_
+#define COMPONENTS_NETWORK_SESSION_CONFIGURATOR_BROWSER_NETWORK_SESSION_CONFIGURATOR_H_
+
+#include <string>
+
+#include "net/http/http_network_session.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace network_session_configurator {
+
+// Helper functions to configure HttpNetworkSession::Params based on field
+// trials and command line.
+
+// Parse serialized QUIC versions string.
+net::QuicVersionVector ParseQuicVersions(const std::string& quic_versions);
+
+// Configure |params| based on field trials and command line,
+// and forcing (policy or other command line) arguments.
+void ParseCommandLineAndFieldTrials(const base::CommandLine& command_line,
+ bool is_quic_force_disabled,
+ const std::string& quic_user_agent_id,
+ net::HttpNetworkSession::Params* params);
+
+} // namespace network_session_configurator
+
+#endif // COMPONENTS_NETWORK_SESSION_CONFIGURATOR_BROWSER_NETWORK_SESSION_CONFIGURATOR_H_
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
new file mode 100644
index 00000000000..b04421dd947
--- /dev/null
+++ b/chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc
@@ -0,0 +1,514 @@
+// 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/network_session_configurator/browser/network_session_configurator.h"
+
+#include <map>
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/test/mock_entropy_provider.h"
+#include "components/network_session_configurator/common/network_switches.h"
+#include "components/variations/variations_associated_data.h"
+#include "net/http/http_stream_factory.h"
+#include "net/quic/core/crypto/crypto_protocol.h"
+#include "net/quic/core/quic_packets.h"
+#include "net/spdy/core/spdy_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace test {
+
+class NetworkSessionConfiguratorTest : public testing::Test {
+ public:
+ NetworkSessionConfiguratorTest()
+ : quic_user_agent_id_("Chrome/52.0.2709.0 Linux x86_64") {
+ field_trial_list_.reset(new base::FieldTrialList(
+ base::MakeUnique<base::MockEntropyProvider>()));
+ variations::testing::ClearAllVariationParams();
+ }
+
+ void ParseCommandLineAndFieldTrials(const base::CommandLine& command_line) {
+ network_session_configurator::ParseCommandLineAndFieldTrials(
+ command_line, /*is_quic_force_disabled=*/false, quic_user_agent_id_,
+ &params_);
+ }
+
+ void ParseFieldTrials() {
+ ParseCommandLineAndFieldTrials(
+ base::CommandLine(base::CommandLine::NO_PROGRAM));
+ }
+
+ std::string quic_user_agent_id_;
+ std::unique_ptr<base::FieldTrialList> field_trial_list_;
+ net::HttpNetworkSession::Params params_;
+};
+
+TEST_F(NetworkSessionConfiguratorTest, Defaults) {
+ ParseFieldTrials();
+
+ EXPECT_FALSE(params_.ignore_certificate_errors);
+ EXPECT_EQ(0u, params_.testing_fixed_http_port);
+ EXPECT_EQ(0u, params_.testing_fixed_https_port);
+ EXPECT_FALSE(params_.enable_tcp_fast_open_for_ssl);
+ EXPECT_FALSE(params_.enable_user_alternate_protocol_ports);
+
+ EXPECT_TRUE(params_.enable_http2);
+ EXPECT_TRUE(params_.http2_settings.empty());
+
+ EXPECT_FALSE(params_.enable_quic);
+ EXPECT_EQ("Chrome/52.0.2709.0 Linux x86_64", params_.quic_user_agent_id);
+ EXPECT_EQ(0u, params_.origins_to_force_quic_on.size());
+ EXPECT_FALSE(params_.quic_force_hol_blocking);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, Http2FieldTrialHttp2Disable) {
+ base::FieldTrialList::CreateFieldTrial("HTTP2", "Disable");
+
+ ParseFieldTrials();
+
+ EXPECT_FALSE(params_.enable_http2);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, EnableQuicFromFieldTrialGroup) {
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.enable_quic);
+ EXPECT_FALSE(params_.mark_quic_broken_when_network_blackholes);
+ EXPECT_FALSE(params_.retry_without_alt_svc_on_quic_errors);
+ EXPECT_EQ(1350u, params_.quic_max_packet_length);
+ EXPECT_EQ(net::QuicTagVector(), params_.quic_connection_options);
+ EXPECT_FALSE(params_.enable_server_push_cancellation);
+ EXPECT_FALSE(params_.quic_close_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::kQuicYieldAfterDurationMilliseconds,
+ params_.quic_packet_reader_yield_after_duration_milliseconds);
+ 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_allow_server_migration);
+ EXPECT_FALSE(params_.quic_force_hol_blocking);
+
+ net::HttpNetworkSession::Params default_params;
+ EXPECT_EQ(default_params.quic_supported_versions,
+ params_.quic_supported_versions);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, EnableQuicFromParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["enable_quic"] = "true";
+ variations::AssociateVariationParams("QUIC", "UseQuic", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "UseQuic");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.enable_quic);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, EnableQuicForDataReductionProxy) {
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+ base::FieldTrialList::CreateFieldTrial("DataReductionProxyUseQuic",
+ "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.enable_quic);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ MarkQuicBrokenWhenNetworkBlackholesFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["mark_quic_broken_when_network_blackholes"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.mark_quic_broken_when_network_blackholes);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, RetryWithoutAltSvcOnQuicErrors) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["retry_without_alt_svc_on_quic_errors"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.retry_without_alt_svc_on_quic_errors);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ QuicCloseSessionsOnIpChangeFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["close_sessions_on_ip_change"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_close_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";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_EQ(300, params_.quic_idle_connection_timeout_seconds);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ NegativeQuicReducedPingTimeoutSecondsFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["reduced_ping_timeout_seconds"] = "-5";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+ ParseFieldTrials();
+ EXPECT_EQ(net::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ LargeQuicReducedPingTimeoutSecondsFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["reduced_ping_timeout_seconds"] = "50";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+ ParseFieldTrials();
+ EXPECT_EQ(net::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ QuicReducedPingTimeoutSecondsFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["reduced_ping_timeout_seconds"] = "10";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+ ParseFieldTrials();
+ EXPECT_EQ(10, params_.quic_reduced_ping_timeout_seconds);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ QuicPacketReaderYieldAfterDurationMillisecondsFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["packet_reader_yield_after_duration_milliseconds"] = "10";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_EQ(10, params_.quic_packet_reader_yield_after_duration_milliseconds);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, QuicRaceCertVerification) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["race_cert_verification"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_race_cert_verification);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, EnableServerPushCancellation) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["enable_server_push_cancellation"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.enable_server_push_cancellation);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, QuicEstimateInitialRtt) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["estimate_initial_rtt"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_estimate_initial_rtt);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ QuicMigrateSessionsOnNetworkChangeFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["migrate_sessions_on_network_change"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_migrate_sessions_on_network_change);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ QuicMigrateSessionsEarlyFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["migrate_sessions_early"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_migrate_sessions_early);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ QuicAllowServerMigrationFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["allow_server_migration"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_allow_server_migration);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, PacketLengthFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["max_packet_length"] = "1450";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_EQ(1450u, params_.quic_max_packet_length);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, QuicVersionFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["quic_version"] =
+ net::QuicVersionToString(net::AllSupportedVersions().back());
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ net::QuicVersionVector supported_versions;
+ supported_versions.push_back(net::AllSupportedVersions().back());
+ EXPECT_EQ(supported_versions, params_.quic_supported_versions);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ MultipleQuicVersionFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ std::string quic_versions =
+ net::QuicVersionToString(net::AllSupportedVersions().front()) + "," +
+ net::QuicVersionToString(net::AllSupportedVersions().back());
+
+ field_trial_params["quic_version"] = quic_versions;
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ net::QuicVersionVector supported_versions;
+ supported_versions.push_back(net::AllSupportedVersions().front());
+ supported_versions.push_back(net::AllSupportedVersions().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::AllSupportedVersions().front()) + "," +
+ net::QuicVersionToString(net::AllSupportedVersions().front());
+
+ field_trial_params["quic_version"] = quic_versions;
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ net::QuicVersionVector supported_versions;
+ supported_versions.push_back(net::AllSupportedVersions().front());
+ EXPECT_EQ(supported_versions, params_.quic_supported_versions);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ QuicConnectionOptionsFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["connection_options"] = "TIME,TBBR,REJ";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ net::QuicTagVector options;
+ options.push_back(net::kTIME);
+ options.push_back(net::kTBBR);
+ options.push_back(net::kREJ);
+ EXPECT_EQ(options, params_.quic_connection_options);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, Http2SettingsFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["http2_settings"] = "7:1234,25:5678";
+ variations::AssociateVariationParams("HTTP2", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("HTTP2", "Enabled");
+
+ ParseFieldTrials();
+
+ net::SettingsMap expected_settings;
+ expected_settings[static_cast<net::SpdySettingsIds>(7)] = 1234;
+ expected_settings[static_cast<net::SpdySettingsIds>(25)] = 5678;
+ EXPECT_EQ(expected_settings, params_.http2_settings);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, TCPFastOpenHttpsEnabled) {
+ base::FieldTrialList::CreateFieldTrial("TCPFastOpen", "HttpsEnabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.enable_tcp_fast_open_for_ssl);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, QuicForceHolBlocking) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["force_hol_blocking"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_force_hol_blocking);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, ForceQuic) {
+ struct {
+ bool force_enabled;
+ bool force_disabled;
+ bool expect_quic_enabled;
+ } kTests[] = {
+ {true /* force_enabled */, false /* force_disabled */,
+ true /* expect_quic_enabled */},
+ {false /* force_enabled */, true /* force_disabled */,
+ false /* expect_quic_enabled */},
+ {true /* force_enabled */, true /* force_disabled */,
+ false /* expect_quic_enabled */},
+ };
+
+ for (const auto& test : kTests) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ if (test.force_enabled)
+ command_line.AppendSwitch(switches::kEnableQuic);
+ if (test.force_disabled)
+ command_line.AppendSwitch(switches::kDisableQuic);
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_EQ(test.expect_quic_enabled, params_.enable_quic);
+ }
+}
+
+TEST_F(NetworkSessionConfiguratorTest, DisableHttp2) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kDisableHttp2);
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_FALSE(params_.enable_http2);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, QuicConnectionOptions) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kEnableQuic);
+ command_line.AppendSwitchASCII(switches::kQuicConnectionOptions,
+ "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);
+ EXPECT_EQ(expected_options, params_.quic_connection_options);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, QuicMaxPacketLength) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kEnableQuic);
+ command_line.AppendSwitchASCII(switches::kQuicMaxPacketLength, "42");
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_EQ(42u, params_.quic_max_packet_length);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, QuicVersion) {
+ net::QuicVersionVector supported_versions = net::AllSupportedVersions();
+ 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));
+ ParseCommandLineAndFieldTrials(command_line);
+ ASSERT_EQ(1u, params_.quic_supported_versions.size());
+ EXPECT_EQ(version, params_.quic_supported_versions[0]);
+ }
+}
+
+TEST_F(NetworkSessionConfiguratorTest, OriginToForceQuicOn) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kEnableQuic);
+ command_line.AppendSwitchASCII(switches::kOriginToForceQuicOn, "*");
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_EQ(1u, params_.origins_to_force_quic_on.size());
+ EXPECT_EQ(1u, params_.origins_to_force_quic_on.count(net::HostPortPair()));
+}
+
+TEST_F(NetworkSessionConfiguratorTest, OriginToForceQuicOn2) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kEnableQuic);
+ command_line.AppendSwitchASCII(switches::kOriginToForceQuicOn, "foo:1234");
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_EQ(1u, params_.origins_to_force_quic_on.size());
+ EXPECT_EQ(1u, params_.origins_to_force_quic_on.count(
+ net::HostPortPair("foo", 1234)));
+}
+
+TEST_F(NetworkSessionConfiguratorTest, OriginToForceQuicOn3) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kEnableQuic);
+ command_line.AppendSwitchASCII(switches::kOriginToForceQuicOn, "foo:1,bar:2");
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_EQ(2u, params_.origins_to_force_quic_on.size());
+ EXPECT_EQ(
+ 1u, params_.origins_to_force_quic_on.count(net::HostPortPair("foo", 1)));
+ EXPECT_EQ(
+ 1u, params_.origins_to_force_quic_on.count(net::HostPortPair("bar", 2)));
+}
+
+TEST_F(NetworkSessionConfiguratorTest, EnableUserAlternateProtocolPorts) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kEnableUserAlternateProtocolPorts);
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_TRUE(params_.enable_user_alternate_protocol_ports);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, IgnoreCertificateErrors) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitch(switches::kIgnoreCertificateErrors);
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_TRUE(params_.ignore_certificate_errors);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, TestingFixedPorts) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ command_line.AppendSwitchASCII(switches::kTestingFixedHttpPort, "800");
+ command_line.AppendSwitchASCII(switches::kTestingFixedHttpsPort, "801");
+ ParseCommandLineAndFieldTrials(command_line);
+ EXPECT_EQ(800, params_.testing_fixed_http_port);
+ EXPECT_EQ(801, params_.testing_fixed_https_port);
+}
+
+} // namespace test
diff --git a/chromium/components/network_session_configurator/common/BUILD.gn b/chromium/components/network_session_configurator/common/BUILD.gn
new file mode 100644
index 00000000000..60bd2ea64cc
--- /dev/null
+++ b/chromium/components/network_session_configurator/common/BUILD.gn
@@ -0,0 +1,15 @@
+# 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.
+
+static_library("common") {
+ sources = [
+ "network_switch_list.h",
+ "network_switches.cc",
+ "network_switches.h",
+ ]
+
+ public_deps = [
+ "//base",
+ ]
+}
diff --git a/chromium/components/network_session_configurator/common/DEPS b/chromium/components/network_session_configurator/common/DEPS
new file mode 100644
index 00000000000..5cd0867c848
--- /dev/null
+++ b/chromium/components/network_session_configurator/common/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+base",
+]
diff --git a/chromium/components/network_session_configurator/common/network_switch_list.h b/chromium/components/network_session_configurator/common/network_switch_list.h
new file mode 100644
index 00000000000..76bec0e25c9
--- /dev/null
+++ b/chromium/components/network_session_configurator/common/network_switch_list.h
@@ -0,0 +1,39 @@
+// 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 deliberately has no header guard, as it's inlined in a number of
+// files.
+
+// Disables the QUIC protocol.
+NETWORK_SWITCH(kDisableQuic, "disable-quic")
+
+// Disables the HTTP/2 protocol.
+NETWORK_SWITCH(kDisableHttp2, "disable-http2")
+
+// Enables Alternate-Protocol when the port is user controlled (> 1024).
+NETWORK_SWITCH(kEnableUserAlternateProtocolPorts,
+ "enable-user-controlled-alternate-protocol-ports")
+
+// Enables the QUIC protocol. This is a temporary testing flag.
+NETWORK_SWITCH(kEnableQuic, "enable-quic")
+
+// Ignores certificate-related errors.
+NETWORK_SWITCH(kIgnoreCertificateErrors, "ignore-certificate-errors")
+
+// Specifies a comma separated list of host-port pairs to force use of QUIC on.
+NETWORK_SWITCH(kOriginToForceQuicOn, "origin-to-force-quic-on")
+
+// Specifies a comma separated list of QUIC connection options to send to
+// the server.
+NETWORK_SWITCH(kQuicConnectionOptions, "quic-connection-options")
+
+// Specifies the maximum length for a QUIC packet.
+NETWORK_SWITCH(kQuicMaxPacketLength, "quic-max-packet-length")
+
+// Specifies the version of QUIC to use.
+NETWORK_SWITCH(kQuicVersion, "quic-version")
+
+// Allows for forcing socket connections to http/https to use fixed ports.
+NETWORK_SWITCH(kTestingFixedHttpPort, "testing-fixed-http-port")
+NETWORK_SWITCH(kTestingFixedHttpsPort, "testing-fixed-https-port")
diff --git a/chromium/components/network_session_configurator/common/network_switches.cc b/chromium/components/network_session_configurator/common/network_switches.cc
new file mode 100644
index 00000000000..7178b17d819
--- /dev/null
+++ b/chromium/components/network_session_configurator/common/network_switches.cc
@@ -0,0 +1,31 @@
+// 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/network_session_configurator/common/network_switches.h"
+
+#include "base/command_line.h"
+
+namespace switches {
+
+#define NETWORK_SWITCH(name, value) const char name[] = value;
+#include "components/network_session_configurator/common/network_switch_list.h"
+#undef NETWORK_SWITCH
+
+} // namespace switches
+
+namespace network_session_configurator {
+
+void CopyNetworkSwitches(const base::CommandLine& src_command_line,
+ base::CommandLine* dest_command_line) {
+ static const char* const kSwitchNames[] = {
+#define NETWORK_SWITCH(name, value) switches::name,
+#include "components/network_session_configurator/common/network_switch_list.h"
+#undef NETWORK_SWITCH
+ };
+
+ dest_command_line->CopySwitchesFrom(src_command_line, kSwitchNames,
+ arraysize(kSwitchNames));
+}
+
+} // namespace network_session_configurator
diff --git a/chromium/components/network_session_configurator/common/network_switches.h b/chromium/components/network_session_configurator/common/network_switches.h
new file mode 100644
index 00000000000..7a511f54aff
--- /dev/null
+++ b/chromium/components/network_session_configurator/common/network_switches.h
@@ -0,0 +1,29 @@
+// 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_NETWORK_SESSION_CONFIGURATOR_COMMON_NETWORK_SWITCHES_H_
+#define COMPONENTS_NETWORK_SESSION_CONFIGURATOR_COMMON_NETWORK_SWITCHES_H_
+
+namespace base {
+class CommandLine;
+}
+
+namespace switches {
+
+#define NETWORK_SWITCH(name, value) extern const char name[];
+#include "components/network_session_configurator/common/network_switch_list.h"
+#undef NETWORK_SWITCH
+
+} // namespace switches
+
+namespace network_session_configurator {
+
+// Copies all command line switches the configurator handles from the |src|
+// CommandLine to the |dest| one.
+void CopyNetworkSwitches(const base::CommandLine& src_command_line,
+ base::CommandLine* dest_command_line);
+
+} // namespace network_session_configurator
+
+#endif // COMPONENTS_NETWORK_SESSION_CONFIGURATOR_COMMON_NETWORK_SWITCHES_H_
diff --git a/chromium/components/network_session_configurator/network_session_configurator.cc b/chromium/components/network_session_configurator/network_session_configurator.cc
deleted file mode 100644
index e885f4ae809..00000000000
--- a/chromium/components/network_session_configurator/network_session_configurator.cc
+++ /dev/null
@@ -1,356 +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/network_session_configurator/network_session_configurator.h"
-
-#include <map>
-#include <unordered_set>
-
-#include "base/metrics/field_trial.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
-#include "components/variations/variations_associated_data.h"
-#include "components/version_info/version_info.h"
-#include "net/http/http_stream_factory.h"
-#include "net/quic/chromium/quic_utils_chromium.h"
-#include "net/quic/core/quic_packets.h"
-#include "net/spdy/core/spdy_protocol.h"
-#include "net/url_request/url_fetcher.h"
-
-namespace {
-
-// Map from name to value for all parameters associate with a field trial.
-using VariationParameters = std::map<std::string, std::string>;
-
-const char kTCPFastOpenFieldTrialName[] = "TCPFastOpen";
-const char kTCPFastOpenHttpsEnabledGroupName[] = "HttpsEnabled";
-
-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";
-
-// Returns the value associated with |key| in |params| or "" if the
-// key is not present in the map.
-const std::string& GetVariationParam(
- const std::map<std::string, std::string>& params,
- const std::string& key) {
- std::map<std::string, std::string>::const_iterator it = params.find(key);
- if (it == params.end())
- return base::EmptyString();
-
- return it->second;
-}
-
-void ConfigureTCPFastOpenParams(base::StringPiece tfo_trial_group,
- net::HttpNetworkSession::Params* params) {
- if (tfo_trial_group == kTCPFastOpenHttpsEnabledGroupName)
- params->enable_tcp_fast_open_for_ssl = true;
-}
-
-net::SettingsMap GetHttp2Settings(
- const VariationParameters& http2_trial_params) {
- net::SettingsMap http2_settings;
-
- const std::string settings_string =
- GetVariationParam(http2_trial_params, "http2_settings");
-
- base::StringPairs key_value_pairs;
- if (!base::SplitStringIntoKeyValuePairs(settings_string, ':', ',',
- &key_value_pairs)) {
- return http2_settings;
- }
-
- for (auto key_value : key_value_pairs) {
- uint32_t key;
- if (!base::StringToUint(key_value.first, &key))
- continue;
- uint32_t value;
- if (!base::StringToUint(key_value.second, &value))
- continue;
- http2_settings[static_cast<net::SpdySettingsIds>(key)] = value;
- }
-
- return http2_settings;
-}
-
-void ConfigureHttp2Params(base::StringPiece http2_trial_group,
- const VariationParameters& http2_trial_params,
- net::HttpNetworkSession::Params* params) {
- if (http2_trial_group.starts_with(kHttp2FieldTrialDisablePrefix)) {
- params->enable_http2 = false;
- return;
- }
- params->http2_settings = GetHttp2Settings(http2_trial_params);
-}
-
-bool ShouldEnableQuic(base::StringPiece quic_trial_group,
- const VariationParameters& quic_trial_params,
- bool is_quic_force_disabled,
- bool is_quic_force_enabled) {
- if (is_quic_force_disabled)
- return false;
- if (is_quic_force_enabled)
- return true;
-
- return quic_trial_group.starts_with(kQuicFieldTrialEnabledGroupName) ||
- quic_trial_group.starts_with(kQuicFieldTrialHttpsEnabledGroupName) ||
- base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "enable_quic"),
- "true");
-}
-
-bool ShouldMarkQuicBrokenWhenNetworkBlackholes(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params,
- "mark_quic_broken_when_network_blackholes"),
- "true");
-}
-
-bool ShouldRetryWithoutAltSvcOnQuicErrors(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params,
- "retry_without_alt_svc_on_quic_errors"),
- "true");
-}
-
-net::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 net::ParseQuicConnectionOptions(it->second);
-}
-
-bool ShouldForceHolBlocking(const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "force_hol_blocking"), "true");
-}
-
-bool ShouldQuicCloseSessionsOnIpChange(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "close_sessions_on_ip_change"),
- "true");
-}
-
-int GetQuicIdleConnectionTimeoutSeconds(
- const VariationParameters& quic_trial_params) {
- int value;
- if (base::StringToInt(GetVariationParam(quic_trial_params,
- "idle_connection_timeout_seconds"),
- &value)) {
- return value;
- }
- return 0;
-}
-
-int GetQuicReducedPingTimeoutSeconds(
- const VariationParameters& quic_trial_params) {
- int value;
- if (base::StringToInt(
- GetVariationParam(quic_trial_params, "reduced_ping_timeout_seconds"),
- &value)) {
- return value;
- }
- return 0;
-}
-
-int GetQuicPacketReaderYieldAfterDurationMilliseconds(
- const VariationParameters& quic_trial_params) {
- int value;
- if (base::StringToInt(
- GetVariationParam(quic_trial_params,
- "packet_reader_yield_after_duration_milliseconds"),
- &value)) {
- return value;
- }
- return 0;
-}
-
-bool ShouldQuicRaceCertVerification(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "race_cert_verification"), "true");
-}
-
-bool ShouldQuicDoNotFragment(const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "do_not_fragment"), "true");
-}
-
-bool ShouldQuicEstimateInitialRtt(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "estimate_initial_rtt"), "true");
-}
-
-bool ShouldQuicMigrateSessionsOnNetworkChange(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params,
- "migrate_sessions_on_network_change"),
- "true");
-}
-
-bool ShouldQuicMigrateSessionsEarly(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "migrate_sessions_early"), "true");
-}
-
-bool ShouldQuicAllowServerMigration(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params,
- "allow_server_migration"),
- "true");
-}
-
-size_t GetQuicMaxPacketLength(const VariationParameters& quic_trial_params) {
- unsigned value;
- if (base::StringToUint(
- GetVariationParam(quic_trial_params, "max_packet_length"), &value)) {
- return value;
- }
- return 0;
-}
-
-net::QuicVersion GetQuicVersion(const VariationParameters& quic_trial_params) {
- return network_session_configurator::ParseQuicVersion(
- GetVariationParam(quic_trial_params, "quic_version"));
-}
-
-bool ShouldEnableServerPushCancelation(
- const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "enable_server_push_cancellation"),
- "true");
-}
-
-void ConfigureQuicParams(base::StringPiece quic_trial_group,
- const VariationParameters& quic_trial_params,
- bool is_quic_force_disabled,
- bool is_quic_force_enabled,
- const std::string& quic_user_agent_id,
- net::HttpNetworkSession::Params* params) {
- params->enable_quic = ShouldEnableQuic(
- quic_trial_group, quic_trial_params, is_quic_force_disabled,
- is_quic_force_enabled);
- params->mark_quic_broken_when_network_blackholes =
- ShouldMarkQuicBrokenWhenNetworkBlackholes(quic_trial_params);
-
- params->enable_server_push_cancellation =
- ShouldEnableServerPushCancelation(quic_trial_params);
-
- params->retry_without_alt_svc_on_quic_errors =
- ShouldRetryWithoutAltSvcOnQuicErrors(quic_trial_params);
-
- if (params->enable_quic) {
- params->quic_force_hol_blocking = ShouldForceHolBlocking(quic_trial_params);
- params->quic_connection_options =
- GetQuicConnectionOptions(quic_trial_params);
- params->quic_close_sessions_on_ip_change =
- ShouldQuicCloseSessionsOnIpChange(quic_trial_params);
- int idle_connection_timeout_seconds =
- GetQuicIdleConnectionTimeoutSeconds(quic_trial_params);
- if (idle_connection_timeout_seconds != 0) {
- params->quic_idle_connection_timeout_seconds =
- idle_connection_timeout_seconds;
- }
- int reduced_ping_timeout_seconds =
- GetQuicReducedPingTimeoutSeconds(quic_trial_params);
- if (reduced_ping_timeout_seconds > 0 &&
- reduced_ping_timeout_seconds < net::kPingTimeoutSecs) {
- params->quic_reduced_ping_timeout_seconds = reduced_ping_timeout_seconds;
- }
- int packet_reader_yield_after_duration_milliseconds =
- GetQuicPacketReaderYieldAfterDurationMilliseconds(quic_trial_params);
- if (packet_reader_yield_after_duration_milliseconds != 0) {
- params->quic_packet_reader_yield_after_duration_milliseconds =
- packet_reader_yield_after_duration_milliseconds;
- }
- params->quic_race_cert_verification =
- ShouldQuicRaceCertVerification(quic_trial_params);
- params->quic_do_not_fragment =
- ShouldQuicDoNotFragment(quic_trial_params);
- params->quic_estimate_initial_rtt =
- ShouldQuicEstimateInitialRtt(quic_trial_params);
- params->quic_migrate_sessions_on_network_change =
- ShouldQuicMigrateSessionsOnNetworkChange(quic_trial_params);
- params->quic_migrate_sessions_early =
- ShouldQuicMigrateSessionsEarly(quic_trial_params);
- params->quic_allow_server_migration =
- ShouldQuicAllowServerMigration(quic_trial_params);
- }
-
- size_t max_packet_length = GetQuicMaxPacketLength(quic_trial_params);
- if (max_packet_length != 0) {
- params->quic_max_packet_length = max_packet_length;
- }
-
- params->quic_user_agent_id = quic_user_agent_id;
-
- net::QuicVersion version = GetQuicVersion(quic_trial_params);
- if (version != net::QUIC_VERSION_UNSUPPORTED) {
- net::QuicVersionVector supported_versions;
- supported_versions.push_back(version);
- params->quic_supported_versions = supported_versions;
- }
-}
-
-} // anonymous namespace
-
-namespace network_session_configurator {
-
-net::QuicVersion ParseQuicVersion(const std::string& quic_version) {
- net::QuicVersionVector supported_versions = net::AllSupportedVersions();
- for (size_t i = 0; i < supported_versions.size(); ++i) {
- net::QuicVersion version = supported_versions[i];
- if (net::QuicVersionToString(version) == quic_version) {
- return version;
- }
- }
-
- return net::QUIC_VERSION_UNSUPPORTED;
-}
-
-void ParseFieldTrials(bool is_quic_force_disabled,
- bool is_quic_force_enabled,
- const std::string& quic_user_agent_id,
- net::HttpNetworkSession::Params* params) {
- std::string quic_trial_group =
- base::FieldTrialList::FindFullName(kQuicFieldTrialName);
- VariationParameters quic_trial_params;
- if (!variations::GetVariationParams(kQuicFieldTrialName, &quic_trial_params))
- quic_trial_params.clear();
- ConfigureQuicParams(quic_trial_group, quic_trial_params,
- is_quic_force_disabled, is_quic_force_enabled,
- quic_user_agent_id, params);
-
- std::string http2_trial_group =
- base::FieldTrialList::FindFullName(kHttp2FieldTrialName);
- VariationParameters http2_trial_params;
- if (!variations::GetVariationParams(kHttp2FieldTrialName,
- &http2_trial_params))
- http2_trial_params.clear();
- ConfigureHttp2Params(http2_trial_group, http2_trial_params, params);
-
- const std::string tfo_trial_group =
- base::FieldTrialList::FindFullName(kTCPFastOpenFieldTrialName);
- ConfigureTCPFastOpenParams(tfo_trial_group, params);
-}
-
-} // namespace network_session_configurator
diff --git a/chromium/components/network_session_configurator/network_session_configurator.h b/chromium/components/network_session_configurator/network_session_configurator.h
deleted file mode 100644
index 4dcb2f6283f..00000000000
--- a/chromium/components/network_session_configurator/network_session_configurator.h
+++ /dev/null
@@ -1,30 +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.
-
-// Helper functions to configure HttpNetworkSession::Params based on field
-// trials, policy, and command line.
-
-#ifndef COMPONENTS_NETWORK_SESSION_CONFIGURATOR_NETWORK_SESSION_CONFIGURATOR_H_
-#define COMPONENTS_NETWORK_SESSION_CONFIGURATOR_NETWORK_SESSION_CONFIGURATOR_H_
-
-#include <string>
-
-#include "net/http/http_network_session.h"
-
-namespace network_session_configurator {
-
-// Parse serialized QUIC version string.
-// Return QUIC_VERSION_UNSUPPORTED on failure.
-net::QuicVersion ParseQuicVersion(const std::string& quic_version);
-
-// Configure |params| based on field trials
-// and forcing (policy or command line) arguments.
-void ParseFieldTrials(bool is_quic_force_disabled,
- bool is_quic_force_enabled,
- const std::string& quic_user_agent_id,
- net::HttpNetworkSession::Params* params);
-
-} // namespace network_session_configurator
-
-#endif // COMPONENTS_NETWORK_SESSION_CONFIGURATOR_NETWORK_SESSION_CONFIGURATOR_H_
diff --git a/chromium/components/network_session_configurator/network_session_configurator_unittest.cc b/chromium/components/network_session_configurator/network_session_configurator_unittest.cc
deleted file mode 100644
index 4fe02d241f0..00000000000
--- a/chromium/components/network_session_configurator/network_session_configurator_unittest.cc
+++ /dev/null
@@ -1,357 +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/network_session_configurator/network_session_configurator.h"
-
-#include <map>
-#include <memory>
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/test/mock_entropy_provider.h"
-#include "components/variations/variations_associated_data.h"
-#include "net/http/http_stream_factory.h"
-#include "net/quic/core/quic_packets.h"
-#include "net/spdy/core/spdy_protocol.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace test {
-
-class NetworkSessionConfiguratorTest : public testing::Test {
- public:
- NetworkSessionConfiguratorTest()
- : quic_user_agent_id_("Chrome/52.0.2709.0 Linux x86_64") {
- field_trial_list_.reset(
- new base::FieldTrialList(
- base::MakeUnique<base::MockEntropyProvider>()));
- variations::testing::ClearAllVariationParams();
- }
-
- void ParseFieldTrials() {
- network_session_configurator::ParseFieldTrials(
- /*is_quic_force_disabled=*/false,
- /*is_quic_force_enabled=*/false, quic_user_agent_id_, &params_);
- }
-
- std::string quic_user_agent_id_;
- std::unique_ptr<base::FieldTrialList> field_trial_list_;
- net::HttpNetworkSession::Params params_;
-};
-
-TEST_F(NetworkSessionConfiguratorTest, Defaults) {
- ParseFieldTrials();
-
- EXPECT_FALSE(params_.ignore_certificate_errors);
- EXPECT_EQ("Chrome/52.0.2709.0 Linux x86_64", params_.quic_user_agent_id);
- EXPECT_EQ(0u, params_.testing_fixed_http_port);
- EXPECT_EQ(0u, params_.testing_fixed_https_port);
- EXPECT_TRUE(params_.enable_http2);
- EXPECT_TRUE(params_.http2_settings.empty());
- EXPECT_FALSE(params_.enable_tcp_fast_open_for_ssl);
- EXPECT_FALSE(params_.enable_quic);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, Http2FieldTrialHttp2Disable) {
- base::FieldTrialList::CreateFieldTrial("HTTP2", "Disable");
-
- ParseFieldTrials();
-
- EXPECT_FALSE(params_.enable_http2);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, EnableQuicFromFieldTrialGroup) {
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.enable_quic);
- EXPECT_FALSE(params_.mark_quic_broken_when_network_blackholes);
- EXPECT_FALSE(params_.retry_without_alt_svc_on_quic_errors);
- EXPECT_EQ(1350u, params_.quic_max_packet_length);
- EXPECT_EQ(net::QuicTagVector(), params_.quic_connection_options);
- EXPECT_FALSE(params_.enable_server_push_cancellation);
- EXPECT_FALSE(params_.quic_close_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::kQuicYieldAfterDurationMilliseconds,
- params_.quic_packet_reader_yield_after_duration_milliseconds);
- EXPECT_FALSE(params_.quic_race_cert_verification);
- EXPECT_FALSE(params_.quic_do_not_fragment);
- 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_allow_server_migration);
- EXPECT_FALSE(params_.quic_force_hol_blocking);
-
- net::HttpNetworkSession::Params default_params;
- EXPECT_EQ(default_params.quic_supported_versions,
- params_.quic_supported_versions);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, EnableQuicFromParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["enable_quic"] = "true";
- variations::AssociateVariationParams("QUIC", "UseQuic", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "UseQuic");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.enable_quic);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, EnableQuicForDataReductionProxy) {
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
- base::FieldTrialList::CreateFieldTrial("DataReductionProxyUseQuic",
- "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.enable_quic);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- MarkQuicBrokenWhenNetworkBlackholesFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["mark_quic_broken_when_network_blackholes"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.mark_quic_broken_when_network_blackholes);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, RetryWithoutAltSvcOnQuicErrors) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["retry_without_alt_svc_on_quic_errors"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.retry_without_alt_svc_on_quic_errors);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- QuicCloseSessionsOnIpChangeFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["close_sessions_on_ip_change"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_close_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";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_EQ(300, params_.quic_idle_connection_timeout_seconds);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- NegativeQuicReducedPingTimeoutSecondsFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["reduced_ping_timeout_seconds"] = "-5";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
- ParseFieldTrials();
- EXPECT_EQ(net::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- LargeQuicReducedPingTimeoutSecondsFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["reduced_ping_timeout_seconds"] = "50";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
- ParseFieldTrials();
- EXPECT_EQ(net::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- QuicReducedPingTimeoutSecondsFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["reduced_ping_timeout_seconds"] = "10";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
- ParseFieldTrials();
- EXPECT_EQ(10, params_.quic_reduced_ping_timeout_seconds);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- QuicPacketReaderYieldAfterDurationMillisecondsFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["packet_reader_yield_after_duration_milliseconds"] = "10";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_EQ(10, params_.quic_packet_reader_yield_after_duration_milliseconds);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, QuicRaceCertVerification) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["race_cert_verification"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_race_cert_verification);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, EnableServerPushCancellation) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["enable_server_push_cancellation"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.enable_server_push_cancellation);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, QuicDoNotFragment) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["do_not_fragment"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_do_not_fragment);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, QuicEstimateInitialRtt) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["estimate_initial_rtt"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_estimate_initial_rtt);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- QuicMigrateSessionsOnNetworkChangeFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["migrate_sessions_on_network_change"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_migrate_sessions_on_network_change);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- QuicMigrateSessionsEarlyFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["migrate_sessions_early"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_migrate_sessions_early);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- QuicAllowServerMigrationFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["allow_server_migration"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_allow_server_migration);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, PacketLengthFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["max_packet_length"] = "1450";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_EQ(1450u, params_.quic_max_packet_length);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, QuicVersionFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["quic_version"] =
- net::QuicVersionToString(net::AllSupportedVersions().back());
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- net::QuicVersionVector supported_versions;
- supported_versions.push_back(net::AllSupportedVersions().back());
- EXPECT_EQ(supported_versions, params_.quic_supported_versions);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
- QuicConnectionOptionsFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["connection_options"] = "TIME,TBBR,REJ";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- net::QuicTagVector options;
- options.push_back(net::kTIME);
- options.push_back(net::kTBBR);
- options.push_back(net::kREJ);
- EXPECT_EQ(options, params_.quic_connection_options);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, Http2SettingsFromFieldTrialParams) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["http2_settings"] = "7:1234,25:5678";
- variations::AssociateVariationParams("HTTP2", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("HTTP2", "Enabled");
-
- ParseFieldTrials();
-
- net::SettingsMap expected_settings;
- expected_settings[static_cast<net::SpdySettingsIds>(7)] = 1234;
- expected_settings[static_cast<net::SpdySettingsIds>(25)] = 5678;
- EXPECT_EQ(expected_settings, params_.http2_settings);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, TCPFastOpenHttpsEnabled) {
- base::FieldTrialList::CreateFieldTrial("TCPFastOpen", "HttpsEnabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.enable_tcp_fast_open_for_ssl);
-}
-
-TEST_F(NetworkSessionConfiguratorTest, QuicForceHolBlocking) {
- std::map<std::string, std::string> field_trial_params;
- field_trial_params["force_hol_blocking"] = "true";
- variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
- base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
- ParseFieldTrials();
-
- EXPECT_TRUE(params_.quic_force_hol_blocking);
-}
-
-} // namespace test
diff --git a/chromium/components/network_time/network_time_test_utils.cc b/chromium/components/network_time/network_time_test_utils.cc
index 441ecba0269..2f28d5a417f 100644
--- a/chromium/components/network_time/network_time_test_utils.cc
+++ b/chromium/components/network_time/network_time_test_utils.cc
@@ -95,7 +95,14 @@ void FieldTrialTest::SetNetworkQueriesWithVariationsService(
// ScopedFeatureList helper class. If this comment was useful to you
// please send me a postcard.
- field_trial_list_.reset(); // Averts a CHECK fail in constructor below.
+ // SetNetworkQueriesWithVariationsService() is usually called during test
+ // fixture setup (to establish a default state) and then again in certain
+ // tests that want to set special params. FieldTrialList is meant to be a
+ // singleton with only one instance existing at once, and the constructor
+ // fails a CHECK if this is violated. To allow these duplicate calls to this
+ // method, any existing FieldTrialList must be destroyed before creating a new
+ // one. (See https://crbug.com/684216#c5 for more discussion.)
+ field_trial_list_.reset();
field_trial_list_.reset(
new base::FieldTrialList(base::MakeUnique<base::MockEntropyProvider>()));
diff --git a/chromium/components/network_time/network_time_tracker.cc b/chromium/components/network_time/network_time_tracker.cc
index 56331529789..5651b61ca81 100644
--- a/chromium/components/network_time/network_time_tracker.cc
+++ b/chromium/components/network_time/network_time_tracker.cc
@@ -36,8 +36,15 @@
namespace network_time {
+// Network time queries are enabled on all desktop platforms except ChromeOS,
+// which uses tlsdated to set the system time.
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(OS_IOS)
const base::Feature kNetworkTimeServiceQuerying{
"NetworkTimeServiceQuerying", base::FEATURE_DISABLED_BY_DEFAULT};
+#else
+const base::Feature kNetworkTimeServiceQuerying{
+ "NetworkTimeServiceQuerying", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
namespace {
@@ -111,7 +118,7 @@ const char kVariationsServiceRandomQueryProbability[] =
// not be issued (i.e. StartTimeFetch() will not start time queries.)
//
// - "on-demand-only": Time queries will not be issued except when
-// StartTimeFetch() is called.
+// StartTimeFetch() is called. This is the default value.
//
// - "background-and-on-demand": Time queries will be issued both in the
// background as needed and also on-demand.
@@ -306,7 +313,7 @@ NetworkTimeTracker::FetchBehavior NetworkTimeTracker::GetFetchBehavior() const {
} else if (param == "background-and-on-demand") {
return FETCHES_IN_BACKGROUND_AND_ON_DEMAND;
}
- return FETCH_BEHAVIOR_UNKNOWN;
+ return FETCHES_ON_DEMAND_ONLY;
}
void NetworkTimeTracker::SetTimeServerURLForTesting(const GURL& url) {
@@ -480,8 +487,8 @@ void NetworkTimeTracker::CheckTime() {
cookies_allowed: false
setting: "This feature cannot be disabled by settings."
chrome_policy {
- NetworkTimeQueriesEnabled {
- NetworkTimeQueriesEnabled: false
+ BrowserNetworkTimeQueriesEnabled {
+ BrowserNetworkTimeQueriesEnabled: false
}
}
})");
diff --git a/chromium/components/new_or_sad_tab_strings.grdp b/chromium/components/new_or_sad_tab_strings.grdp
index ca8f3c6db3d..46e48aaca94 100644
--- a/chromium/components/new_or_sad_tab_strings.grdp
+++ b/chromium/components/new_or_sad_tab_strings.grdp
@@ -12,16 +12,6 @@
<message name="IDS_SAD_TAB_MESSAGE" desc="The message displayed on the sad tab page." formatter_data="android_java">
Something went wrong while displaying this webpage.
</message>
- <if expr="_google_chrome">
- <message name="IDS_SAD_TAB_OOM_MESSAGE" desc="The message displayed on the sad tab page if the tab died due to being out of memory." formatter_data="android_java">
- Google Chrome ran out of memory while trying to display this webpage.
- </message>
- </if>
- <if expr="not _google_chrome">
- <message name="IDS_SAD_TAB_OOM_MESSAGE" desc="The message displayed on the sad tab page if the tab died due to being out of memory." formatter_data="android_java">
- Chromium ran out of memory while trying to display this webpage.
- </message>
- </if>
<message name="IDS_SAD_TAB_HELP_MESSAGE" desc="The help message displayed on the sad tab page, with IDS_SAD_TAB_HELP_LINK embedded as a link to help.">
If you're seeing this frequently, try these <ph name="HELP_LINK">$1<ex>suggestions</ex></ph>.
</message>
@@ -55,17 +45,17 @@
</message>
<if expr="is_macosx">
<message name="IDS_SAD_TAB_RELOAD_INCOGNITO" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to open the web page in Chrome's Incognito mode." formatter_data="android_java">
- Open page in Incognito mode (⇧⌘N)
+ Open page in a new Incognito window (⇧⌘N)
</message>
</if>
<if expr="is_win or is_linux or chromeos">
<message name="IDS_SAD_TAB_RELOAD_INCOGNITO" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to open the web page in Chrome's Incognito mode." formatter_data="android_java">
- Open page in Incognito mode (Ctrl-Shift-N)
+ Open page in a new Incognito window (Ctrl-Shift-N)
</message>
</if>
<if expr="is_android or is_ios">
<message name="IDS_SAD_TAB_RELOAD_INCOGNITO" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to open the web page in Chrome's Incognito mode." formatter_data="android_java">
- Open page in Incognito mode
+ Open page in a new Incognito tab
</message>
</if>
<if expr="is_macosx or chromeos">
diff --git a/chromium/components/ntp_snippets/BUILD.gn b/chromium/components/ntp_snippets/BUILD.gn
index 54a66cf3d0e..d783ae7facb 100644
--- a/chromium/components/ntp_snippets/BUILD.gn
+++ b/chromium/components/ntp_snippets/BUILD.gn
@@ -15,6 +15,17 @@ static_library("ntp_snippets") {
"bookmarks/bookmark_last_visit_utils.h",
"bookmarks/bookmark_suggestions_provider.cc",
"bookmarks/bookmark_suggestions_provider.h",
+ "breaking_news/breaking_news_gcm_app_handler.cc",
+ "breaking_news/breaking_news_gcm_app_handler.h",
+ "breaking_news/breaking_news_listener.h",
+ "breaking_news/breaking_news_suggestions_provider.cc",
+ "breaking_news/breaking_news_suggestions_provider.h",
+ "breaking_news/subscription_json_request.cc",
+ "breaking_news/subscription_json_request.h",
+ "breaking_news/subscription_manager.cc",
+ "breaking_news/subscription_manager.h",
+ "breaking_news/subscription_manager_impl.cc",
+ "breaking_news/subscription_manager_impl.h",
"callbacks.h",
"category.cc",
"category.h",
@@ -49,15 +60,26 @@ static_library("ntp_snippets") {
"pref_util.h",
"reading_list/reading_list_suggestions_provider.cc",
"reading_list/reading_list_suggestions_provider.h",
+ "remote/cached_image_fetcher.cc",
+ "remote/cached_image_fetcher.h",
+ "remote/contextual_json_request.cc",
+ "remote/contextual_json_request.h",
"remote/json_request.cc",
"remote/json_request.h",
+ "remote/json_to_categories.cc",
+ "remote/json_to_categories.h",
"remote/persistent_scheduler.h",
+ "remote/prefetched_pages_tracker.h",
+ "remote/prefetched_pages_tracker_impl.cc",
+ "remote/prefetched_pages_tracker_impl.h",
"remote/remote_suggestion.cc",
"remote/remote_suggestion.h",
"remote/remote_suggestions_database.cc",
"remote/remote_suggestions_database.h",
"remote/remote_suggestions_fetcher.cc",
"remote/remote_suggestions_fetcher.h",
+ "remote/remote_suggestions_fetcher_impl.cc",
+ "remote/remote_suggestions_fetcher_impl.h",
"remote/remote_suggestions_provider.cc",
"remote/remote_suggestions_provider.h",
"remote/remote_suggestions_provider_impl.cc",
@@ -103,8 +125,10 @@ static_library("ntp_snippets") {
"//components/data_use_measurement/core",
"//components/favicon/core",
"//components/favicon_base",
+ "//components/gcm_driver",
"//components/history/core/browser",
"//components/image_fetcher/core",
+ "//components/language/core/browser",
"//components/metrics",
"//components/ntp_snippets/remote/proto",
"//components/offline_pages/core",
@@ -115,7 +139,6 @@ static_library("ntp_snippets") {
"//components/sessions",
"//components/strings",
"//components/sync_sessions",
- "//components/translate/core/browser",
"//components/url_formatter",
"//components/variations",
"//components/variations/net",
@@ -141,6 +164,9 @@ source_set("unit_tests") {
sources = [
"bookmarks/bookmark_last_visit_utils_unittest.cc",
"bookmarks/bookmark_suggestions_provider_unittest.cc",
+ "breaking_news/breaking_news_suggestions_provider_unittest.cc",
+ "breaking_news/subscription_json_request_unittest.cc",
+ "breaking_news/subscription_manager_impl_unittest.cc",
"category_rankers/click_based_category_ranker_unittest.cc",
"category_rankers/constant_category_ranker_unittest.cc",
"category_unittest.cc",
@@ -149,10 +175,13 @@ source_set("unit_tests") {
"offline_pages/recent_tab_suggestions_provider_unittest.cc",
"physical_web_pages/physical_web_page_suggestions_provider_unittest.cc",
"reading_list/reading_list_suggestions_provider_unittest.cc",
+ "remote/cached_image_fetcher_unittest.cc",
+ "remote/contextual_json_request_unittest.cc",
"remote/json_request_unittest.cc",
+ "remote/prefetched_pages_tracker_impl_unittest.cc",
"remote/remote_suggestion_unittest.cc",
"remote/remote_suggestions_database_unittest.cc",
- "remote/remote_suggestions_fetcher_unittest.cc",
+ "remote/remote_suggestions_fetcher_impl_unittest.cc",
"remote/remote_suggestions_provider_impl_unittest.cc",
"remote/remote_suggestions_scheduler_impl_unittest.cc",
"remote/remote_suggestions_status_service_unittest.cc",
@@ -187,6 +216,7 @@ source_set("unit_tests") {
"//components/signin/core/common",
"//components/strings",
"//components/sync:test_support_driver",
+ "//components/sync_preferences:test_support",
"//components/sync_sessions",
"//components/variations:test_support",
"//components/web_resource:web_resource",
diff --git a/chromium/components/ntp_snippets/DEPS b/chromium/components/ntp_snippets/DEPS
index 777f897e174..9e6b868768e 100644
--- a/chromium/components/ntp_snippets/DEPS
+++ b/chromium/components/ntp_snippets/DEPS
@@ -2,17 +2,19 @@ include_rules = [
"+components/data_use_measurement/core",
"+components/favicon/core",
"+components/favicon_base",
+ "+components/gcm_driver",
"+components/history/core",
"+components/image_fetcher",
"+components/keyed_service/core",
+ "+components/language/core/browser",
"+components/leveldb_proto",
"+components/metrics",
"+components/prefs",
"+components/reading_list",
"+components/signin",
"+components/strings/grit/components_strings.h",
+ "+components/sync_preferences/testing_pref_service_syncable.h",
"+components/sync/driver",
- "+components/translate/core/browser",
"+components/url_formatter",
"+components/variations",
"+components/version_info",
diff --git a/chromium/components/ntp_snippets/OWNERS b/chromium/components/ntp_snippets/OWNERS
index c8c8515dcc5..7d3309a4b5b 100644
--- a/chromium/components/ntp_snippets/OWNERS
+++ b/chromium/components/ntp_snippets/OWNERS
@@ -4,6 +4,7 @@ treib@chromium.org
# Unsure who to ask? Choose from the above.
markusheintz@chromium.org
+sfiera@chromium.org
vitaliii@chromium.org
# For ios:
diff --git a/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.cc b/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.cc
new file mode 100644
index 00000000000..5a8584131c6
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.cc
@@ -0,0 +1,174 @@
+// 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/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.h"
+
+#include "base/strings/string_util.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/gcm_driver/gcm_profile_service.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/gcm_driver/instance_id/instance_id_driver.h"
+#include "components/ntp_snippets/pref_names.h"
+
+using instance_id::InstanceID;
+
+namespace ntp_snippets {
+
+const char kBreakingNewsGCMAppID[] = "com.google.breakingnews.gcm";
+
+// The sender ID is used in the registration process.
+// See: https://developers.google.com/cloud-messaging/gcm#senderid
+const char kBreakingNewsGCMSenderId[] = "667617379155";
+
+// OAuth2 Scope passed to getToken to obtain GCM registration tokens.
+// Must match Java GoogleCloudMessaging.INSTANCE_ID_SCOPE.
+const char kGCMScope[] = "GCM";
+
+// Key of the news json in the data in the pushed breaking news.
+const char kPushedNewsKey[] = "payload";
+
+BreakingNewsGCMAppHandler::BreakingNewsGCMAppHandler(
+ gcm::GCMDriver* gcm_driver,
+ instance_id::InstanceIDDriver* instance_id_driver,
+ PrefService* pref_service,
+ std::unique_ptr<SubscriptionManager> subscription_manager,
+ const ParseJSONCallback& parse_json_callback)
+ : gcm_driver_(gcm_driver),
+ instance_id_driver_(instance_id_driver),
+ pref_service_(pref_service),
+ subscription_manager_(std::move(subscription_manager)),
+ parse_json_callback_(parse_json_callback),
+ weak_ptr_factory_(this) {}
+
+BreakingNewsGCMAppHandler::~BreakingNewsGCMAppHandler() {
+ StopListening();
+}
+
+void BreakingNewsGCMAppHandler::StartListening(
+ OnNewContentCallback on_new_content_callback) {
+#if !defined(OS_ANDROID)
+ NOTREACHED()
+ << "The BreakingNewsGCMAppHandler should only be used on Android.";
+#endif
+ Subscribe();
+ on_new_content_callback_ = std::move(on_new_content_callback);
+ gcm_driver_->AddAppHandler(kBreakingNewsGCMAppID, this);
+}
+
+void BreakingNewsGCMAppHandler::StopListening() {
+ DCHECK_EQ(gcm_driver_->GetAppHandler(kBreakingNewsGCMAppID), this);
+ gcm_driver_->RemoveAppHandler(kBreakingNewsGCMAppID);
+ on_new_content_callback_ = OnNewContentCallback();
+ subscription_manager_->Unsubscribe();
+}
+
+void BreakingNewsGCMAppHandler::Subscribe() {
+ // TODO(mamir): This logic should be moved to the SubscriptionManager.
+ std::string token =
+ pref_service_->GetString(prefs::kBreakingNewsGCMSubscriptionTokenCache);
+ // If a token has been already obtained, subscribe directly at the content
+ // suggestions server. Otherwise, obtain a GCM token first.
+ if (!token.empty()) {
+ if (!subscription_manager_->IsSubscribed() ||
+ subscription_manager_->NeedsToResubscribe()) {
+ subscription_manager_->Subscribe(token);
+ }
+ return;
+ }
+
+ instance_id_driver_->GetInstanceID(kBreakingNewsGCMAppID)
+ ->GetToken(kBreakingNewsGCMSenderId, kGCMScope,
+ /*options=*/std::map<std::string, std::string>(),
+ base::Bind(&BreakingNewsGCMAppHandler::DidSubscribe,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BreakingNewsGCMAppHandler::DidSubscribe(
+ const std::string& subscription_token,
+ InstanceID::Result result) {
+ switch (result) {
+ case InstanceID::SUCCESS:
+ pref_service_->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache,
+ subscription_token);
+ subscription_manager_->Subscribe(subscription_token);
+ return;
+ case InstanceID::INVALID_PARAMETER:
+ case InstanceID::DISABLED:
+ case InstanceID::ASYNC_OPERATION_PENDING:
+ case InstanceID::SERVER_ERROR:
+ case InstanceID::UNKNOWN_ERROR:
+ DLOG(WARNING)
+ << "Push messaging subscription failed; InstanceID::Result = "
+ << result;
+ break;
+ case InstanceID::NETWORK_ERROR:
+ break;
+ }
+}
+
+void BreakingNewsGCMAppHandler::ShutdownHandler() {}
+
+void BreakingNewsGCMAppHandler::OnStoreReset() {
+ pref_service_->ClearPref(prefs::kBreakingNewsGCMSubscriptionTokenCache);
+}
+
+void BreakingNewsGCMAppHandler::OnMessage(const std::string& app_id,
+ const gcm::IncomingMessage& message) {
+ DCHECK_EQ(app_id, kBreakingNewsGCMAppID);
+
+ gcm::MessageData::const_iterator it = message.data.find(kPushedNewsKey);
+ if (it == message.data.end()) {
+ LOG(WARNING)
+ << "Receiving pushed content failure: Breaking News ID missing.";
+ return;
+ }
+
+ std::string news = it->second;
+
+ parse_json_callback_.Run(news,
+ base::Bind(&BreakingNewsGCMAppHandler::OnJsonSuccess,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BreakingNewsGCMAppHandler::OnJsonError,
+ weak_ptr_factory_.GetWeakPtr(), news));
+}
+
+void BreakingNewsGCMAppHandler::OnMessagesDeleted(const std::string& app_id) {
+ // Messages don't get deleted.
+ NOTREACHED() << "BreakingNewsGCMAppHandler messages don't get deleted.";
+}
+
+void BreakingNewsGCMAppHandler::OnSendError(
+ const std::string& app_id,
+ const gcm::GCMClient::SendErrorDetails& details) {
+ // Should never be called because we don't send GCM messages to
+ // the server.
+ NOTREACHED() << "BreakingNewsGCMAppHandler doesn't send GCM messages.";
+}
+
+void BreakingNewsGCMAppHandler::OnSendAcknowledged(
+ const std::string& app_id,
+ const std::string& message_id) {
+ // Should never be called because we don't send GCM messages to
+ // the server.
+ NOTREACHED() << "BreakingNewsGCMAppHandler doesn't send GCM messages.";
+}
+
+void BreakingNewsGCMAppHandler::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterStringPref(prefs::kBreakingNewsGCMSubscriptionTokenCache,
+ std::string());
+}
+
+void BreakingNewsGCMAppHandler::OnJsonSuccess(
+ std::unique_ptr<base::Value> content) {
+ on_new_content_callback_.Run(std::move(content));
+}
+
+void BreakingNewsGCMAppHandler::OnJsonError(const std::string& json_str,
+ const std::string& error) {
+ LOG(WARNING) << "Error parsing JSON:" << error
+ << " when parsing:" << json_str;
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.h b/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.h
new file mode 100644
index 00000000000..59527c39d21
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.h
@@ -0,0 +1,106 @@
+// 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_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_GCM_APP_HANDLER_H_
+#define COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_GCM_APP_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/ntp_snippets/breaking_news/breaking_news_listener.h"
+#include "components/ntp_snippets/breaking_news/subscription_manager.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+class PrefRegistrySimple;
+
+namespace gcm {
+class GCMDriver;
+}
+
+namespace instance_id {
+class InstanceIDDriver;
+}
+
+namespace ntp_snippets {
+
+// Handler for pushed GCM breaking news. It retrieves a subscription token
+// from the GCM server and registers/unregisters itself with the GCM service to
+// be called upon received push breaking news.
+class BreakingNewsGCMAppHandler : public BreakingNewsListener,
+ public gcm::GCMAppHandler {
+ public:
+ // Callbacks for JSON parsing to allow injecting platform-dependent code.
+ using SuccessCallback =
+ base::Callback<void(std::unique_ptr<base::Value> result)>;
+ using ErrorCallback = base::Callback<void(const std::string& error)>;
+ using ParseJSONCallback =
+ base::Callback<void(const std::string& raw_json_string,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback)>;
+
+ using OnNewContentCallback =
+ base::Callback<void(std::unique_ptr<base::Value> content)>;
+
+ BreakingNewsGCMAppHandler(
+ gcm::GCMDriver* gcm_driver,
+ instance_id::InstanceIDDriver* instance_id_driver,
+ PrefService* pref_service_,
+ std::unique_ptr<SubscriptionManager> subscription_manager,
+ const ParseJSONCallback& parse_json_callback);
+
+ // If still listening, calls StopListening()
+ ~BreakingNewsGCMAppHandler() override;
+
+ // BreakingNewsListener overrides.
+ void StartListening(OnNewContentCallback on_new_content_callback) override;
+ void StopListening() override;
+
+ // GCMAppHandler overrides.
+ void ShutdownHandler() override;
+ void OnStoreReset() override;
+ void OnMessage(const std::string& app_id,
+ const gcm::IncomingMessage& message) override;
+ void OnMessagesDeleted(const std::string& app_id) override;
+ void OnSendError(const std::string& app_id,
+ const gcm::GCMClient::SendErrorDetails& details) override;
+ void OnSendAcknowledged(const std::string& app_id,
+ const std::string& message_id) override;
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ private:
+ // Retrieves a subscription token that allows the content suggestions server
+ // to push content via GCM messages. Calling this method multiple times is not
+ // necessary but does not harm since the same token is returned everytime.
+ void Subscribe();
+
+ // Called after the subscription is obtained from the GCM server.
+ void DidSubscribe(const std::string& subscription_token,
+ instance_id::InstanceID::Result result);
+
+ // Called after successfully parsing the received suggestion JSON.
+ void OnJsonSuccess(std::unique_ptr<base::Value> content);
+
+ // Called in case the received suggestion JSON inside the GCM has a parse
+ // error.
+ void OnJsonError(const std::string& json_str, const std::string& error);
+
+ gcm::GCMDriver* const gcm_driver_;
+ instance_id::InstanceIDDriver* const instance_id_driver_;
+ PrefService* const pref_service_;
+ const std::unique_ptr<SubscriptionManager> subscription_manager_;
+ const ParseJSONCallback parse_json_callback_;
+
+ // Called after every time a new message is received in OnMessage() to notify
+ // the content provider.
+ OnNewContentCallback on_new_content_callback_;
+
+ base::WeakPtrFactory<BreakingNewsGCMAppHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BreakingNewsGCMAppHandler);
+};
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_GCM_APP_HANDLER_H_
diff --git a/chromium/components/ntp_snippets/breaking_news/breaking_news_listener.h b/chromium/components/ntp_snippets/breaking_news/breaking_news_listener.h
new file mode 100644
index 00000000000..e516322db1c
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/breaking_news_listener.h
@@ -0,0 +1,32 @@
+// 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_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_LISTENER_H_
+#define COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_LISTENER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/values.h"
+
+namespace ntp_snippets {
+
+class BreakingNewsListener {
+ public:
+ using OnNewContentCallback =
+ base::Callback<void(std::unique_ptr<base::Value> content)>;
+
+ virtual ~BreakingNewsListener() = default;
+
+ // Subscribe to the breaking news service and start listening for pushed
+ // breaking news. Must not be called if already listening.
+ virtual void StartListening(OnNewContentCallback on_new_content_callback) = 0;
+
+ // Stop listening for incoming breaking news. Any further pushed breaking news
+ // will be ignored. Must be called while listening.
+ virtual void StopListening() = 0;
+};
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_LISTENER_H_
diff --git a/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.cc b/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.cc
new file mode 100644
index 00000000000..d4672ad8f76
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.cc
@@ -0,0 +1,155 @@
+// 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/ntp_snippets/breaking_news/breaking_news_suggestions_provider.h"
+
+#include "base/bind.h"
+#include "base/json/json_writer.h"
+#include "base/time/clock.h"
+#include "components/ntp_snippets/breaking_news/breaking_news_listener.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/ntp_snippets/remote/json_to_categories.h"
+#include "components/strings/grit/components_strings.h"
+
+namespace ntp_snippets {
+
+BreakingNewsSuggestionsProvider::BreakingNewsSuggestionsProvider(
+ ContentSuggestionsProvider::Observer* observer,
+ std::unique_ptr<BreakingNewsListener> breaking_news_raw_data_provider,
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<RemoteSuggestionsDatabase> database)
+ : ContentSuggestionsProvider(observer),
+ breaking_news_raw_data_provider_(
+ std::move(breaking_news_raw_data_provider)),
+ clock_(std::move(clock)),
+ database_(std::move(database)),
+ provided_category_(
+ Category::FromKnownCategory(KnownCategories::BREAKING_NEWS)),
+ category_status_(CategoryStatus::INITIALIZING) {
+ database_->SetErrorCallback(
+ base::Bind(&BreakingNewsSuggestionsProvider::OnDatabaseError,
+ base::Unretained(this)));
+ database_->LoadSnippets(
+ base::Bind(&BreakingNewsSuggestionsProvider::OnDatabaseLoaded,
+ base::Unretained(this)));
+ // Unretained because |this| owns |breaking_news_listener_|.
+ breaking_news_raw_data_provider_->StartListening(
+ base::Bind(&BreakingNewsSuggestionsProvider::OnNewContentSuggestion,
+ base::Unretained(this)));
+}
+
+BreakingNewsSuggestionsProvider::~BreakingNewsSuggestionsProvider() {
+ breaking_news_raw_data_provider_->StopListening();
+}
+
+void BreakingNewsSuggestionsProvider::OnNewContentSuggestion(
+ std::unique_ptr<base::Value> content) {
+ DCHECK(content);
+ const base::Time receive_time = clock_->Now();
+ FetchedCategoriesVector categories;
+ if (!JsonToCategories(*content, &categories, receive_time)) {
+ std::string content_json;
+ base::JSONWriter::Write(*content, &content_json);
+ LOG(WARNING) << "Received invalid breaking news: " << content_json;
+ return;
+ }
+ DCHECK_EQ(categories.size(), static_cast<size_t>(1));
+ auto& fetched_category = categories[0];
+ Category category = fetched_category.category;
+ DCHECK(category.IsKnownCategory(KnownCategories::BREAKING_NEWS));
+ if (database_->IsInitialized()) {
+ database_->SaveSnippets(fetched_category.suggestions);
+ } else {
+ // TODO(mamir): Check how often a breaking news is received before DB is
+ // initialized.
+ LOG(WARNING) << "Cannot store breaking news, database is not initialized.";
+ }
+ NotifyNewSuggestions(std::move(fetched_category.suggestions));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private methods
+
+CategoryStatus BreakingNewsSuggestionsProvider::GetCategoryStatus(
+ Category category) {
+ DCHECK_EQ(category, provided_category_);
+ return category_status_;
+}
+
+CategoryInfo BreakingNewsSuggestionsProvider::GetCategoryInfo(
+ Category category) {
+ // TODO(mamir): needs to be corrected, just a placeholer
+ return CategoryInfo(base::string16(),
+ ContentSuggestionsCardLayout::MINIMAL_CARD,
+ ContentSuggestionsAdditionalAction::VIEW_ALL,
+ /*show_if_empty=*/false, base::string16());
+}
+
+void BreakingNewsSuggestionsProvider::DismissSuggestion(
+ const ContentSuggestion::ID& suggestion_id) {
+ // TODO(mamir): implement.
+}
+
+void BreakingNewsSuggestionsProvider::FetchSuggestionImage(
+ const ContentSuggestion::ID& suggestion_id,
+ const ImageFetchedCallback& callback) {
+ // TODO(mamir): implement.
+}
+
+void BreakingNewsSuggestionsProvider::Fetch(
+ const Category& category,
+ const std::set<std::string>& known_suggestion_ids,
+ const FetchDoneCallback& callback) {
+ // TODO(jkrcal): Make Fetch method optional.
+}
+
+void BreakingNewsSuggestionsProvider::ClearHistory(
+ base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter) {
+ observer()->OnNewSuggestions(this, provided_category_,
+ std::vector<ContentSuggestion>());
+}
+
+void BreakingNewsSuggestionsProvider::ClearCachedSuggestions(
+ Category category) {
+ DCHECK_EQ(category, provided_category_);
+ // TODO(mamir): clear the cached suggestions.
+}
+
+void BreakingNewsSuggestionsProvider::GetDismissedSuggestionsForDebugging(
+ Category category,
+ const DismissedSuggestionsCallback& callback) {
+ // TODO(mamir): implement.
+}
+
+void BreakingNewsSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
+ Category category) {
+ // TODO(mamir): implement.
+}
+
+void BreakingNewsSuggestionsProvider::OnDatabaseLoaded(
+ std::vector<std::unique_ptr<RemoteSuggestion>> suggestions) {
+ // TODO(mamir): check and update DB status.
+ NotifyNewSuggestions(std::move(suggestions));
+}
+
+void BreakingNewsSuggestionsProvider::OnDatabaseError() {
+ // TODO(mamir): implement.
+}
+
+void BreakingNewsSuggestionsProvider::NotifyNewSuggestions(
+ std::vector<std::unique_ptr<RemoteSuggestion>> suggestions) {
+ std::vector<ContentSuggestion> result;
+ for (const std::unique_ptr<RemoteSuggestion>& suggestion : suggestions) {
+ result.emplace_back(suggestion->ToContentSuggestion(provided_category_));
+ }
+
+ DVLOG(1) << "NotifyNewSuggestions(): " << result.size()
+ << " items in category " << provided_category_;
+ observer()->OnNewSuggestions(this, provided_category_, std::move(result));
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.h b/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.h
new file mode 100644
index 00000000000..eb62272dbb2
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.h
@@ -0,0 +1,83 @@
+// 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_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_SUGGESTIONS_PROVIDER_H_
+#define COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_SUGGESTIONS_PROVIDER_H_
+
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/content_suggestions_provider.h"
+#include "components/ntp_snippets/remote/remote_suggestions_database.h"
+#include "components/prefs/pref_registry_simple.h"
+
+namespace ntp_snippets {
+class BreakingNewsListener;
+}
+
+namespace base {
+class Clock;
+}
+
+namespace ntp_snippets {
+
+// Receives breaking news suggestions asynchronously via BreakingNewsListener,
+// stores them and provides them as content suggestions.
+// This class is final because it does things in its constructor which make it
+// unsafe to derive from it.
+class BreakingNewsSuggestionsProvider final
+ : public ContentSuggestionsProvider {
+ public:
+ BreakingNewsSuggestionsProvider(
+ ContentSuggestionsProvider::Observer* observer,
+ std::unique_ptr<BreakingNewsListener> breaking_news_raw_data_provider,
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<RemoteSuggestionsDatabase> database);
+ ~BreakingNewsSuggestionsProvider() override;
+
+ // ContentSuggestionsProvider implementation.
+ CategoryStatus GetCategoryStatus(Category category) override;
+ CategoryInfo GetCategoryInfo(Category category) override;
+ void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override;
+ void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id,
+ const ImageFetchedCallback& callback) override;
+ void Fetch(const Category& category,
+ const std::set<std::string>& known_suggestion_ids,
+ const FetchDoneCallback& callback) override;
+ void ClearHistory(
+ base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter) override;
+ void ClearCachedSuggestions(Category category) override;
+ void GetDismissedSuggestionsForDebugging(
+ Category category,
+ const DismissedSuggestionsCallback& callback) override;
+ void ClearDismissedSuggestionsForDebugging(Category category) override;
+
+ private:
+ // Callback called from the breaking news listener when new content has been
+ // pushed from the server.
+ void OnNewContentSuggestion(std::unique_ptr<base::Value> content);
+
+ // Callbacks for the RemoteSuggestionsDatabase.
+ void OnDatabaseLoaded(
+ std::vector<std::unique_ptr<RemoteSuggestion>> suggestions);
+ void OnDatabaseError();
+
+ void NotifyNewSuggestions(
+ std::vector<std::unique_ptr<RemoteSuggestion>> suggestions);
+
+ std::unique_ptr<BreakingNewsListener> breaking_news_raw_data_provider_;
+ std::unique_ptr<base::Clock> clock_;
+
+ // The database for persisting suggestions.
+ std::unique_ptr<RemoteSuggestionsDatabase> database_;
+
+ const Category provided_category_;
+ CategoryStatus category_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(BreakingNewsSuggestionsProvider);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_BREAKING_NEWS_SUGGESTIONS_PROVIDER_H_
diff --git a/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider_unittest.cc
new file mode 100644
index 00000000000..bdfbbddaa77
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/breaking_news_suggestions_provider_unittest.cc
@@ -0,0 +1,152 @@
+// 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/ntp_snippets/breaking_news/breaking_news_suggestions_provider.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/simple_test_clock.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/ntp_snippets/breaking_news/breaking_news_listener.h"
+#include "components/ntp_snippets/breaking_news/breaking_news_suggestions_provider.h"
+#include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Eq;
+using testing::Property;
+using testing::SaveArg;
+using testing::SizeIs;
+using testing::StrictMock;
+
+namespace ntp_snippets {
+
+namespace {
+
+class MockBreakingNewsListener : public BreakingNewsListener {
+ public:
+ MOCK_METHOD1(StartListening, void(OnNewContentCallback callback));
+ MOCK_METHOD0(StopListening, void());
+};
+
+class BreakingNewsSuggestionsProviderTest : public testing::Test {
+ public:
+ BreakingNewsSuggestionsProviderTest() {
+ CHECK(database_dir_.CreateUniqueTempDir());
+ }
+
+ ~BreakingNewsSuggestionsProviderTest() {
+ EXPECT_CALL(*listener_, StopListening()).RetiresOnSaturation();
+ }
+
+ protected:
+ void InitializeBreakingNewsSuggestionsProvider() {
+ auto listener = base::MakeUnique<StrictMock<MockBreakingNewsListener>>();
+ listener_ = listener.get();
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner(
+ base::ThreadTaskRunnerHandle::Get());
+ // TODO(mamir): Use a mock DB instead of a real one. A DB interface needs to
+ // be extracted for that.
+ // TODO(mamir): Add tests for database failure once the DB gets mockable.
+ auto database = base::MakeUnique<RemoteSuggestionsDatabase>(
+ database_dir_.GetPath(), task_runner);
+
+ EXPECT_CALL(*listener_, StartListening(_))
+ .WillOnce(SaveArg<0>(&on_new_content_callback_))
+ .RetiresOnSaturation();
+ // The observer will be updated with an empty list upon loading the
+ // database.
+ EXPECT_CALL(observer_, OnNewSuggestions(_,
+ Category::FromKnownCategory(
+ KnownCategories::BREAKING_NEWS),
+ SizeIs(0)))
+ .RetiresOnSaturation();
+ provider_ = base::MakeUnique<BreakingNewsSuggestionsProvider>(
+ &observer_, std::move(listener),
+ base::MakeUnique<base::SimpleTestClock>(), std::move(database));
+
+ // TODO(mamir): Find a better way to wait for initialization to finish.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ base::Time StringToTime(const std::string& time_string) {
+ base::Time out_time;
+ CHECK(base::Time::FromString(time_string.c_str(), &out_time));
+ return out_time;
+ }
+
+ BreakingNewsListener::OnNewContentCallback on_new_content_callback_;
+ base::MessageLoop message_loop_;
+ StrictMock<MockBreakingNewsListener>* listener_;
+ std::unique_ptr<BreakingNewsSuggestionsProvider> provider_;
+ StrictMock<MockContentSuggestionsProviderObserver> observer_;
+ base::ScopedTempDir database_dir_;
+};
+
+TEST_F(BreakingNewsSuggestionsProviderTest,
+ ShouldPropagatePushedNewsWithoutModifyingToObserver) {
+ InitializeBreakingNewsSuggestionsProvider();
+ std::string json =
+ "{\"categories\" : [{"
+ " \"id\": 8,"
+ " \"localizedTitle\": \"Breaking News\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/id\"],"
+ " \"title\" : \"Title string\","
+ " \"snippet\" : \"Snippet string\","
+ " \"fullPageUrl\" : \"http://localhost/fullUrl\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Attribution string\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\" "
+ " }]"
+ "}]}";
+
+ // TODO(mamir): Test imageUrl and expirationTime. They aren't directly
+ // testable because they aren't part of ContentSuggestion. However, they could
+ // be checked indirectly via FetchImage or by providing an expired suggestion
+ // and checking that it is not propagated further.
+ EXPECT_CALL(
+ observer_,
+ OnNewSuggestions(
+ _, Eq(Category::FromKnownCategory(KnownCategories::BREAKING_NEWS)),
+ ElementsAre(AllOf(
+ Property(&ContentSuggestion::id,
+ ContentSuggestion::ID(Category::FromRemoteCategory(8),
+ "http://localhost/id")),
+ Property(&ContentSuggestion::title,
+ base::UTF8ToUTF16("Title string")),
+ Property(&ContentSuggestion::snippet_text,
+ base::UTF8ToUTF16("Snippet string")),
+ Property(&ContentSuggestion::url,
+ GURL("http://localhost/fullUrl")),
+ Property(&ContentSuggestion::publish_date,
+ StringToTime("2016-06-30T11:01:37.000Z")),
+ Property(&ContentSuggestion::publisher_name,
+ base::UTF8ToUTF16("Attribution string"))
+
+ ))));
+ on_new_content_callback_.Run(base::JSONReader().ReadToValue(json));
+}
+
+TEST_F(BreakingNewsSuggestionsProviderTest,
+ ClearHistoryShouldNotifyObserverWithEmptySuggestionsList) {
+ InitializeBreakingNewsSuggestionsProvider();
+ EXPECT_CALL(observer_, OnNewSuggestions(_,
+ Category::FromKnownCategory(
+ KnownCategories::BREAKING_NEWS),
+ SizeIs(0)));
+ provider_->ClearHistory(base::Time::UnixEpoch(), base::Time::Max(),
+ base::Callback<bool(const GURL& url)>());
+}
+
+} // namespace
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc
new file mode 100644
index 00000000000..187e4638852
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc
@@ -0,0 +1,182 @@
+// 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/ntp_snippets/breaking_news/subscription_json_request.h"
+
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
+#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"
+
+using net::URLFetcher;
+using net::URLRequestContextGetter;
+using net::HttpRequestHeaders;
+using net::URLRequestStatus;
+
+namespace ntp_snippets {
+
+namespace internal {
+
+SubscriptionJsonRequest::SubscriptionJsonRequest() = default;
+
+SubscriptionJsonRequest::~SubscriptionJsonRequest() = default;
+
+void SubscriptionJsonRequest::Start(CompletedCallback callback) {
+ DCHECK(request_completed_callback_.is_null()) << "Request already running!";
+ request_completed_callback_ = std::move(callback);
+ url_fetcher_->Start();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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();
+
+ if (!status.is_success()) {
+ std::move(request_completed_callback_)
+ .Run(Status(StatusCode::TEMPORARY_ERROR,
+ base::StringPrintf("Network Error: %d", status.error())));
+ } else if (response != net::HTTP_OK) {
+ std::move(request_completed_callback_)
+ .Run(Status(StatusCode::PERMANENT_ERROR,
+ base::StringPrintf("HTTP Error: %d", response)));
+ } else {
+ std::move(request_completed_callback_)
+ .Run(Status(StatusCode::SUCCESS, std::string()));
+ }
+}
+
+SubscriptionJsonRequest::Builder::Builder() = default;
+SubscriptionJsonRequest::Builder::Builder(SubscriptionJsonRequest::Builder&&) =
+ default;
+SubscriptionJsonRequest::Builder::~Builder() = default;
+
+std::unique_ptr<SubscriptionJsonRequest>
+SubscriptionJsonRequest::Builder::Build() const {
+ DCHECK(!url_.is_empty());
+ DCHECK(url_request_context_getter_);
+ 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;
+
+ return request;
+}
+
+SubscriptionJsonRequest::Builder& SubscriptionJsonRequest::Builder::SetToken(
+ const std::string& token) {
+ token_ = token;
+ return *this;
+}
+
+SubscriptionJsonRequest::Builder& SubscriptionJsonRequest::Builder::SetUrl(
+ const GURL& url) {
+ url_ = url;
+ return *this;
+}
+
+SubscriptionJsonRequest::Builder&
+SubscriptionJsonRequest::Builder::SetUrlRequestContextGetter(
+ const scoped_refptr<URLRequestContextGetter>& context_getter) {
+ url_request_context_getter_ = context_getter;
+ return *this;
+}
+
+SubscriptionJsonRequest::Builder&
+SubscriptionJsonRequest::Builder::SetAuthenticationHeader(
+ const std::string& auth_header) {
+ auth_header_ = auth_header;
+ 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 |is_signed_in| false if it's unknown, as it does
+ // not affect transmission of experiments coming from the variations server.
+ variations::AppendVariationHeaders(url_,
+ false, // incognito
+ false, // uma_enabled
+ false, // is_signed_in
+ &headers);
+ return headers.ToString();
+}
+
+std::string SubscriptionJsonRequest::Builder::BuildBody() const {
+ base::DictionaryValue request;
+ request.SetString("token", token_);
+
+ std::string request_json;
+ bool success = base::JSONWriter::Write(request, &request_json);
+ DCHECK(success);
+ return request_json;
+}
+
+std::unique_ptr<URLFetcher> SubscriptionJsonRequest::Builder::BuildURLFetcher(
+ URLFetcherDelegate* delegate,
+ const std::string& headers,
+ const std::string& body) const {
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("gcm_subscription", R"(
+ semantics {
+ sender: "Subscribe for breaking news delivered via GCM push messages"
+ description:
+ "Chromium can receive breaking news via GCM push messages. "
+ "This request suscribes the client to receiving them."
+ trigger:
+ "Subscription takes place only once per profile lifetime. "
+ data:
+ "The subscription token that identifies this Chromium profile."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: false
+ setting:
+ "This feature cannot be disabled by settings now"
+ chrome_policy {
+ NTPContentSuggestionsEnabled {
+ policy_options {mode: MANDATORY}
+ NTPContentSuggestionsEnabled: false
+ }
+ }
+ })");
+ 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;
+}
+
+} // namespace internal
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_json_request.h b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.h
new file mode 100644
index 00000000000..b0aa71c4bad
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.h
@@ -0,0 +1,93 @@
+// 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_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_JSON_REQUEST_H_
+#define COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_JSON_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "components/ntp_snippets/status.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "net/http/http_request_headers.h"
+
+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 {
+ 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.
+ using CompletedCallback = base::OnceCallback<void(const Status& status)>;
+
+ // Builds non-authenticated and authenticated SubscriptionJsonRequests.
+ class Builder {
+ public:
+ Builder();
+ Builder(Builder&&);
+ ~Builder();
+
+ // Builds a Request object that contains all data to fetch new snippets.
+ std::unique_ptr<SubscriptionJsonRequest> Build() const;
+
+ Builder& SetToken(const std::string& token);
+ Builder& SetUrl(const GURL& url);
+ Builder& SetUrlRequestContextGetter(
+ const scoped_refptr<net::URLRequestContextGetter>& context_getter);
+ Builder& SetAuthenticationHeader(const std::string& auth_header);
+
+ private:
+ std::string BuildHeaders() const;
+ std::string BuildBody() const;
+ std::unique_ptr<net::URLFetcher> BuildURLFetcher(
+ net::URLFetcherDelegate* request,
+ const std::string& headers,
+ const std::string& body) const;
+
+ // GCM subscription token obtained from GCM driver (instanceID::getToken()).
+ std::string token_;
+ // TODO(mamir): Additional fields to be added: country, language.
+
+ GURL url_;
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ std::string auth_header_;
+
+ DISALLOW_COPY_AND_ASSIGN(Builder);
+ };
+
+ ~SubscriptionJsonRequest() override;
+
+ // Starts an async request. The callback is invoked when the request succeeds
+ // or fails. The callback is not called if the request is destroyed.
+ void Start(CompletedCallback callback);
+
+ private:
+ friend class Builder;
+ SubscriptionJsonRequest();
+ // URLFetcherDelegate implementation.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ // The fetcher for subscribing.
+ std::unique_ptr<net::URLFetcher> url_fetcher_;
+
+ // The callback to notify when URLFetcher 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);
+};
+
+} // namespace internal
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_JSON_REQUEST_H_
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
new file mode 100644
index 00000000000..0f09e8bd204
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_json_request_unittest.cc
@@ -0,0 +1,190 @@
+// 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/ntp_snippets/breaking_news/subscription_json_request.h"
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+namespace internal {
+
+namespace {
+
+using testing::_;
+using testing::SaveArg;
+
+// TODO(mamir): Create a test_helper.cc file instead of duplicating all this
+// code.
+MATCHER_P(EqualsJSON, json, "equals JSON") {
+ std::unique_ptr<base::Value> expected = base::JSONReader::Read(json);
+ if (!expected) {
+ *result_listener << "INTERNAL ERROR: couldn't parse expected JSON";
+ return false;
+ }
+
+ std::string err_msg;
+ int err_line, err_col;
+ std::unique_ptr<base::Value> actual = base::JSONReader::ReadAndReturnError(
+ arg, base::JSON_PARSE_RFC, nullptr, &err_msg, &err_line, &err_col);
+ if (!actual) {
+ *result_listener << "input:" << err_line << ":" << err_col << ": "
+ << "parse error: " << err_msg;
+ return false;
+ }
+ return base::Value::Equals(actual.get(), expected.get());
+}
+
+} // namespace
+
+class SubscriptionJsonRequestTest : public testing::Test {
+ public:
+ SubscriptionJsonRequestTest()
+ : request_context_getter_(
+ new net::TestURLRequestContextGetter(message_loop_.task_runner())) {
+ }
+
+ scoped_refptr<net::URLRequestContextGetter> GetRequestContext() {
+ return request_context_getter_.get();
+ }
+
+ 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 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 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);
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+ net::TestURLFetcherFactory url_fetcher_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SubscriptionJsonRequestTest);
+};
+
+TEST_F(SubscriptionJsonRequestTest, BuildRequest) {
+ std::string token = "1234567890";
+ GURL url("http://valid-url.test");
+
+ base::MockCallback<SubscriptionJsonRequest::CompletedCallback> callback;
+
+ SubscriptionJsonRequest::Builder builder;
+ std::unique_ptr<SubscriptionJsonRequest> request =
+ builder.SetToken(token)
+ .SetUrl(url)
+ .SetUrlRequestContextGetter(GetRequestContext())
+ .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 =
+ "{"
+ " \"token\": "
+ " \"1234567890\""
+ "}";
+ EXPECT_THAT(url_fetcher->upload_data(), EqualsJSON(expected_body));
+}
+
+TEST_F(SubscriptionJsonRequestTest, ShouldNotInvokeCallbackWhenCancelled) {
+ std::string token = "1234567890";
+ GURL url("http://valid-url.test");
+
+ base::MockCallback<SubscriptionJsonRequest::CompletedCallback> callback;
+ EXPECT_CALL(callback, Run(_)).Times(0);
+
+ SubscriptionJsonRequest::Builder builder;
+ std::unique_ptr<SubscriptionJsonRequest> request =
+ builder.SetToken(token)
+ .SetUrl(url)
+ .SetUrlRequestContextGetter(GetRequestContext())
+ .Build();
+ request->Start(callback.Get());
+
+ // Destroy the request before getting any response.
+ request.reset();
+}
+
+TEST_F(SubscriptionJsonRequestTest, SubscribeWithoutErrors) {
+ std::string token = "1234567890";
+ GURL url("http://valid-url.test");
+
+ base::MockCallback<SubscriptionJsonRequest::CompletedCallback> callback;
+ ntp_snippets::Status status(StatusCode::PERMANENT_ERROR, "initial");
+ EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&status));
+
+ SubscriptionJsonRequest::Builder builder;
+ std::unique_ptr<SubscriptionJsonRequest> request =
+ builder.SetToken(token)
+ .SetUrl(url)
+ .SetUrlRequestContextGetter(GetRequestContext())
+ .Build();
+ request->Start(callback.Get());
+
+ RespondWithData("{}");
+
+ EXPECT_EQ(status.code, StatusCode::SUCCESS);
+}
+
+TEST_F(SubscriptionJsonRequestTest, SubscribeWithErrors) {
+ std::string token = "1234567890";
+ GURL url("http://valid-url.test");
+
+ base::MockCallback<SubscriptionJsonRequest::CompletedCallback> callback;
+ ntp_snippets::Status status(StatusCode::SUCCESS, "initial");
+ EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&status));
+
+ SubscriptionJsonRequest::Builder builder;
+ std::unique_ptr<SubscriptionJsonRequest> request =
+ builder.SetToken(token)
+ .SetUrl(url)
+ .SetUrlRequestContextGetter(GetRequestContext())
+ .Build();
+ request->Start(callback.Get());
+
+ RespondWithError(net::ERR_TIMED_OUT);
+
+ EXPECT_EQ(status.code, StatusCode::TEMPORARY_ERROR);
+}
+
+} // namespace internal
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_manager.cc b/chromium/components/ntp_snippets/breaking_news/subscription_manager.cc
new file mode 100644
index 00000000000..1dbd535dbe8
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager.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/ntp_snippets/breaking_news/subscription_manager.h"
+
+#include "base/metrics/field_trial_params.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/ntp_snippets_constants.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+// Variation parameter for chrome-push-subscription backend.
+const char kPushSubscriptionBackendParam[] = "push_subscription_backend";
+
+// Variation parameter for chrome-push-unsubscription backend.
+const char kPushUnsubscriptionBackendParam[] = "push_unsubscription_backend";
+
+} // namespace
+
+GURL GetPushUpdatesSubscriptionEndpoint(version_info::Channel channel) {
+ std::string endpoint = base::GetFieldTrialParamValueByFeature(
+ kBreakingNewsPushFeature, kPushSubscriptionBackendParam);
+ if (!endpoint.empty()) {
+ return GURL{endpoint};
+ }
+
+ switch (channel) {
+ case version_info::Channel::STABLE:
+ case version_info::Channel::BETA:
+ return GURL{kPushUpdatesSubscriptionServer};
+
+ case version_info::Channel::DEV:
+ case version_info::Channel::CANARY:
+ case version_info::Channel::UNKNOWN:
+ return GURL{kPushUpdatesSubscriptionStagingServer};
+ }
+ NOTREACHED();
+ return GURL{kPushUpdatesSubscriptionStagingServer};
+}
+
+GURL GetPushUpdatesUnsubscriptionEndpoint(version_info::Channel channel) {
+ std::string endpoint = base::GetFieldTrialParamValueByFeature(
+ kBreakingNewsPushFeature, kPushUnsubscriptionBackendParam);
+ if (!endpoint.empty()) {
+ return GURL{endpoint};
+ }
+
+ switch (channel) {
+ case version_info::Channel::STABLE:
+ case version_info::Channel::BETA:
+ return GURL{kPushUpdatesUnsubscriptionServer};
+
+ case version_info::Channel::DEV:
+ case version_info::Channel::CANARY:
+ case version_info::Channel::UNKNOWN:
+ return GURL{kPushUpdatesUnsubscriptionStagingServer};
+ }
+ NOTREACHED();
+ return GURL{kPushUpdatesUnsubscriptionStagingServer};
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_manager.h b/chromium/components/ntp_snippets/breaking_news/subscription_manager.h
new file mode 100644
index 00000000000..fc2343e04eb
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager.h
@@ -0,0 +1,42 @@
+// 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_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_MANAGER_H_
+#define COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_MANAGER_H_
+
+#include <string>
+
+#include "components/version_info/version_info.h"
+#include "url/gurl.h"
+
+namespace ntp_snippets {
+
+// Returns the appropriate API endpoint for subscribing for push updates, in
+// consideration of the channel and field trial parameters.
+GURL GetPushUpdatesSubscriptionEndpoint(version_info::Channel channel);
+
+// Returns the appropriate API endpoint for unsubscribing for push updates, in
+// consideration of the channel and field trial parameters.
+GURL GetPushUpdatesUnsubscriptionEndpoint(version_info::Channel channel);
+
+// Handles subscription to content suggestions server for push updates (e.g. via
+// GCM).
+class SubscriptionManager {
+ public:
+ virtual ~SubscriptionManager() = default;
+
+ virtual void Subscribe(const std::string& token) = 0;
+ virtual void Unsubscribe() = 0;
+ virtual bool IsSubscribed() = 0;
+
+ virtual void Resubscribe(const std::string& new_token) = 0;
+
+ // Checks if some data that has been used when subscribing has changed. For
+ // example, the user has signed in.
+ virtual bool NeedsToResubscribe() = 0;
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_MANAGER_H_
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
new file mode 100644
index 00000000000..22fd71ae6c9
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
@@ -0,0 +1,270 @@
+// 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/ntp_snippets/breaking_news/subscription_manager_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/stringprintf.h"
+#include "components/ntp_snippets/breaking_news/subscription_json_request.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/ntp_snippets_constants.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/access_token_fetcher.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#include "net/base/url_util.h"
+
+namespace ntp_snippets {
+
+using internal::SubscriptionJsonRequest;
+
+namespace {
+
+const char kApiKeyParamName[] = "key";
+const char kAuthorizationRequestHeaderFormat[] = "Bearer %s";
+
+} // namespace
+
+class SubscriptionManagerImpl::SigninObserver
+ : public SigninManagerBase::Observer {
+ public:
+ SigninObserver(SigninManagerBase* signin_manager,
+ const base::Closure& signin_status_changed_callback)
+ : signin_manager_(signin_manager),
+ signin_status_changed_callback_(signin_status_changed_callback) {
+ signin_manager_->AddObserver(this);
+ }
+
+ ~SigninObserver() override { signin_manager_->RemoveObserver(this); }
+
+ private:
+ // SigninManagerBase::Observer implementation.
+ void GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username) override {
+ signin_status_changed_callback_.Run();
+ }
+
+ void GoogleSignedOut(const std::string& account_id,
+ const std::string& username) override {
+ signin_status_changed_callback_.Run();
+ }
+
+ SigninManagerBase* const signin_manager_;
+ base::Closure signin_status_changed_callback_;
+};
+
+SubscriptionManagerImpl::SubscriptionManagerImpl(
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+ PrefService* pref_service,
+ SigninManagerBase* signin_manager,
+ OAuth2TokenService* access_token_service,
+ const std::string& api_key,
+ const GURL& subscribe_url,
+ const GURL& unsubscribe_url)
+ : url_request_context_getter_(std::move(url_request_context_getter)),
+ pref_service_(pref_service),
+ signin_manager_(signin_manager),
+ signin_observer_(base::MakeUnique<SigninObserver>(
+ signin_manager,
+ base::Bind(&SubscriptionManagerImpl::SigninStatusChanged,
+ base::Unretained(this)))),
+ access_token_service_(access_token_service),
+ api_key_(api_key),
+ subscribe_url_(subscribe_url),
+ unsubscribe_url_(unsubscribe_url) {}
+
+SubscriptionManagerImpl::~SubscriptionManagerImpl() = default;
+
+void SubscriptionManagerImpl::Subscribe(const std::string& subscription_token) {
+ // If there is a request in flight, cancel it.
+ if (request_) {
+ request_ = nullptr;
+ }
+ if (signin_manager_->IsAuthenticated()) {
+ StartAccessTokenRequest(subscription_token);
+ } else {
+ SubscribeInternal(subscription_token, /*access_token=*/std::string());
+ }
+}
+
+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_);
+
+ if (!access_token.empty()) {
+ builder.SetUrl(subscribe_url_);
+ builder.SetAuthenticationHeader(base::StringPrintf(
+ kAuthorizationRequestHeaderFormat, access_token.c_str()));
+ } else {
+ // When not providing OAuth token, we need to pass the Google API key.
+ builder.SetUrl(
+ net::AppendQueryParameter(subscribe_url_, kApiKeyParamName, api_key_));
+ }
+
+ request_ = builder.Build();
+ request_->Start(base::BindOnce(&SubscriptionManagerImpl::DidSubscribe,
+ base::Unretained(this), subscription_token,
+ /*is_authenticated=*/!access_token.empty()));
+}
+
+void SubscriptionManagerImpl::StartAccessTokenRequest(
+ const std::string& subscription_token) {
+ // If there is already an ongoing token request, destroy it.
+ if (access_token_fetcher_) {
+ access_token_fetcher_ = nullptr;
+ }
+
+ OAuth2TokenService::ScopeSet scopes = {kContentSuggestionsApiScope};
+ access_token_fetcher_ = base::MakeUnique<AccessTokenFetcher>(
+ "ntp_snippets", signin_manager_, access_token_service_, scopes,
+ base::BindOnce(&SubscriptionManagerImpl::AccessTokenFetchFinished,
+ base::Unretained(this), subscription_token));
+}
+
+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<AccessTokenFetcher> access_token_fetcher_deleter(
+ std::move(access_token_fetcher_));
+
+ 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);
+}
+
+void SubscriptionManagerImpl::DidSubscribe(
+ const std::string& subscription_token,
+ bool is_authenticated,
+ const Status& status) {
+ // Delete the request only after we leave this method (which is called from
+ // the request itself).
+ std::unique_ptr<internal::SubscriptionJsonRequest> request_deleter(
+ std::move(request_));
+
+ switch (status.code) {
+ case StatusCode::SUCCESS:
+ // In case of successful subscription, store the same data used for
+ // subscription in order to be able to resubscribe in case of data
+ // change.
+ // TODO(mamir): Store region and language.
+ pref_service_->SetString(prefs::kBreakingNewsSubscriptionDataToken,
+ subscription_token);
+ pref_service_->SetBoolean(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated,
+ is_authenticated);
+ break;
+ default:
+ // TODO(mamir): Handle failure.
+ break;
+ }
+}
+
+void SubscriptionManagerImpl::Unsubscribe() {
+ std::string token =
+ pref_service_->GetString(prefs::kBreakingNewsSubscriptionDataToken);
+ ResubscribeInternal(/*old_token=*/token, /*new_token=*/std::string());
+}
+
+void SubscriptionManagerImpl::ResubscribeInternal(
+ const std::string& old_token,
+ const std::string& new_token) {
+ // If there is an request in flight, cancel it.
+ if (request_) {
+ request_ = nullptr;
+ }
+
+ SubscriptionJsonRequest::Builder builder;
+ builder.SetToken(old_token).SetUrlRequestContextGetter(
+ url_request_context_getter_);
+ builder.SetUrl(
+ net::AppendQueryParameter(unsubscribe_url_, kApiKeyParamName, api_key_));
+
+ request_ = builder.Build();
+ request_->Start(base::BindOnce(&SubscriptionManagerImpl::DidUnsubscribe,
+ base::Unretained(this), new_token));
+}
+
+bool SubscriptionManagerImpl::IsSubscribed() {
+ std::string subscription_token =
+ pref_service_->GetString(prefs::kBreakingNewsSubscriptionDataToken);
+ return !subscription_token.empty();
+}
+
+bool SubscriptionManagerImpl::NeedsToResubscribe() {
+ // Check if authentication state changed after subscription.
+ bool is_auth_subscribe = pref_service_->GetBoolean(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated);
+ bool is_authenticated = signin_manager_->IsAuthenticated();
+ return is_auth_subscribe != is_authenticated;
+}
+
+void SubscriptionManagerImpl::Resubscribe(const std::string& new_token) {
+ std::string old_token =
+ pref_service_->GetString(prefs::kBreakingNewsSubscriptionDataToken);
+ if (old_token == new_token) {
+ // If the token didn't change, subscribe directly. The server handles the
+ // unsubscription if previous subscriptions exists.
+ Subscribe(old_token);
+ } else {
+ ResubscribeInternal(old_token, new_token);
+ }
+}
+
+void SubscriptionManagerImpl::DidUnsubscribe(const std::string& new_token,
+ const Status& status) {
+ // Delete the request only after we leave this method (which is called from
+ // the request itself).
+ std::unique_ptr<internal::SubscriptionJsonRequest> request_deleter(
+ std::move(request_));
+
+ switch (status.code) {
+ case StatusCode::SUCCESS:
+ // In case of successful unsubscription, clear the previously stored data.
+ // TODO(mamir): Clear stored region and language.
+ pref_service_->ClearPref(prefs::kBreakingNewsSubscriptionDataToken);
+ pref_service_->ClearPref(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated);
+ if (!new_token.empty()) {
+ Subscribe(new_token);
+ }
+ break;
+ default:
+ // TODO(mamir): Handle failure.
+ break;
+ }
+}
+
+void SubscriptionManagerImpl::SigninStatusChanged() {
+ // If subscribed already, resubscribe.
+ if (IsSubscribed()) {
+ if (request_) {
+ request_ = nullptr;
+ }
+ std::string token =
+ pref_service_->GetString(prefs::kBreakingNewsSubscriptionDataToken);
+ Subscribe(token);
+ }
+}
+
+void SubscriptionManagerImpl::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterStringPref(prefs::kBreakingNewsSubscriptionDataToken,
+ std::string());
+ registry->RegisterBooleanPref(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated, false);
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h
new file mode 100644
index 00000000000..b1fd05bda06
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h
@@ -0,0 +1,105 @@
+// 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_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_MANAGER_IMPL_H_
+#define COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_MANAGER_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#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 "components/signin/core/browser/signin_manager_base.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+class AccessTokenFetcher;
+class OAuth2TokenService;
+class PrefRegistrySimple;
+class PrefService;
+
+namespace ntp_snippets {
+
+// Class that wraps around the functionality of SubscriptionJsonRequest. It uses
+// the SubscriptionJsonRequest to send subscription and unsubscription requests
+// to the content suggestions server and does the bookkeeping for the data used
+// for subscription. Bookkeeping is required to detect any change (e.g. the
+// token render invalid), and resubscribe accordingly.
+class SubscriptionManagerImpl : public SubscriptionManager {
+ public:
+ SubscriptionManagerImpl(
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+ PrefService* pref_service,
+ SigninManagerBase* signin_manager,
+ OAuth2TokenService* access_token_service,
+ const std::string& api_key,
+ const GURL& subscribe_url,
+ const GURL& unsubscribe_url);
+
+ ~SubscriptionManagerImpl() override;
+
+ // SubscriptionManager implementation.
+ void Subscribe(const std::string& token) override;
+ void Unsubscribe() override;
+ bool IsSubscribed() override;
+
+ void Resubscribe(const std::string& new_token) override;
+
+ // Checks if some data that has been used when subscribing has changed. For
+ // example, the user has signed in.
+ bool NeedsToResubscribe() override;
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ private:
+ class SigninObserver;
+
+ void SigninStatusChanged();
+
+ void DidSubscribe(const std::string& subscription_token,
+ bool is_authenticated,
+ const Status& status);
+ void DidUnsubscribe(const std::string& new_token, const Status& status);
+ void SubscribeInternal(const std::string& subscription_token,
+ const std::string& access_token);
+
+ // If |new_token| is empty, this will just unsubscribe. If |new_token| is
+ // non-empty, a subscription request with the |new_token| will be started upon
+ // successful unsubscription.
+ void ResubscribeInternal(const std::string& old_token,
+ const std::string& new_token);
+
+ // |subscription_token| is the token when subscribing after obtaining the
+ // access token.
+ void StartAccessTokenRequest(const std::string& subscription_token);
+ void AccessTokenFetchFinished(const std::string& subscription_token,
+ const GoogleServiceAuthError& error,
+ const std::string& access_token);
+
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+
+ std::unique_ptr<internal::SubscriptionJsonRequest> request_;
+ std::unique_ptr<AccessTokenFetcher> access_token_fetcher_;
+
+ PrefService* pref_service_;
+
+ // Authentication for signed-in users.
+ SigninManagerBase* signin_manager_;
+ std::unique_ptr<SigninObserver> signin_observer_;
+ OAuth2TokenService* access_token_service_;
+
+ // API key to use for non-authenticated requests.
+ const std::string api_key_;
+
+ // API endpoint for subscribing and unsubscribing.
+ const GURL subscribe_url_;
+ const GURL unsubscribe_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(SubscriptionManagerImpl);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_BREAKING_NEWS_SUBSCRIPTION_MANAGER_IMPL_H_
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
new file mode 100644
index 00000000000..2c8cebdb57f
--- /dev/null
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl_unittest.cc
@@ -0,0 +1,334 @@
+// 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/ntp_snippets/breaking_news/subscription_manager_impl.h"
+
+#include "base/message_loop/message_loop.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 "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 "google_apis/gaia/fake_oauth2_token_service_delegate.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+const char kTestEmail[] = "test@email.com";
+const char kAPIKey[] = "fakeAPIkey";
+const char kSubscriptionUrl[] = "http://valid-url.test/subscribe";
+const char kSubscriptionUrlSignedIn[] = "http://valid-url.test/subscribe";
+;
+const char kSubscriptionUrlSignedOut[] =
+ "http://valid-url.test/subscribe?key=fakeAPIkey";
+const char kUnsubscriptionUrl[] = "http://valid-url.test/unsubscribe";
+const char kUnsubscriptionUrlSignedIn[] = "http://valid-url.test/unsubscribe";
+const char kUnsubscriptionUrlSignedOut[] =
+ "http://valid-url.test/unsubscribe?key=fakeAPIkey";
+
+class SubscriptionManagerImplTest : public testing::Test {
+ public:
+ SubscriptionManagerImplTest()
+ : request_context_getter_(
+ new net::TestURLRequestContextGetter(message_loop_.task_runner())) {
+ }
+
+ void SetUp() override {
+ SubscriptionManagerImpl::RegisterProfilePrefs(
+ utils_.pref_service()->registry());
+ }
+
+ scoped_refptr<net::URLRequestContextGetter> GetRequestContext() {
+ return request_context_getter_.get();
+ }
+
+ PrefService* GetPrefService() { return utils_.pref_service(); }
+
+ FakeProfileOAuth2TokenService* GetOAuth2TokenService() {
+ return utils_.token_service();
+ }
+
+ SigninManagerBase* GetSigninManager() { return utils_.fake_signin_manager(); }
+
+ 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());
+ } else {
+ ASSERT_EQ(GURL(kSubscriptionUrlSignedOut), url_fetcher->GetOriginalURL());
+ }
+ RespondSuccessfully();
+ }
+
+ void RespondToUnsubscriptionRequestSuccessfully(bool is_signed_in) {
+ net::TestURLFetcher* url_fetcher = GetRunningFetcher();
+ if (is_signed_in) {
+ ASSERT_EQ(GURL(kUnsubscriptionUrlSignedIn),
+ url_fetcher->GetOriginalURL());
+ } else {
+ ASSERT_EQ(GURL(kUnsubscriptionUrlSignedOut),
+ url_fetcher->GetOriginalURL());
+ }
+ 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());
+ } else {
+ ASSERT_EQ(GURL(kSubscriptionUrlSignedOut), url_fetcher->GetOriginalURL());
+ }
+ 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());
+ } else {
+ ASSERT_EQ(GURL(kUnsubscriptionUrlSignedOut),
+ url_fetcher->GetOriginalURL());
+ }
+ RespondWithError(error_code);
+ }
+
+#if !defined(OS_CHROMEOS)
+ void SignIn() {
+ utils_.fake_signin_manager()->SignIn(kTestEmail, "user", "pass");
+ }
+
+ void SignOut() { utils_.fake_signin_manager()->ForceSignOut(); }
+#endif // !defined(OS_CHROMEOS)
+
+ void IssueRefreshToken(FakeProfileOAuth2TokenService* auth_token_service) {
+ auth_token_service->GetDelegate()->UpdateCredentials(kTestEmail, "token");
+ }
+
+ void IssueAccessToken(FakeProfileOAuth2TokenService* auth_token_service) {
+ auth_token_service->IssueAllTokensForAccount(kTestEmail, "access_token",
+ base::Time::Max());
+ }
+
+ 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 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);
+ }
+
+ base::MessageLoop message_loop_;
+ test::RemoteSuggestionsTestUtils utils_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+ net::TestURLFetcherFactory url_fetcher_factory_;
+};
+
+TEST_F(SubscriptionManagerImplTest, SubscribeSuccessfully) {
+ std::string subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), GetOAuth2TokenService(),
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+ manager.Subscribe(subscription_token);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+ ASSERT_TRUE(manager.IsSubscribed());
+ EXPECT_EQ(subscription_token, GetPrefService()->GetString(
+ prefs::kBreakingNewsSubscriptionDataToken));
+ EXPECT_FALSE(GetPrefService()->GetBoolean(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated));
+}
+
+// This test is relevant only on non-ChromeOS platforms, as the flow being
+// tested here is not possible on ChromeOS.
+#if !defined(OS_CHROMEOS)
+TEST_F(SubscriptionManagerImplTest,
+ ShouldSubscribeWithAuthenticationWhenAuthenticated) {
+ // Sign in.
+ FakeProfileOAuth2TokenService* auth_token_service = GetOAuth2TokenService();
+ SignIn();
+ IssueRefreshToken(auth_token_service);
+
+ // Create manager and subscribe.
+ std::string subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), auth_token_service,
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+ manager.Subscribe(subscription_token);
+
+ // Make sure that subscription is pending an access token.
+ ASSERT_FALSE(manager.IsSubscribed());
+ ASSERT_EQ(1u, auth_token_service->GetPendingRequests().size());
+
+ // Issue the access token and respond to the subscription request.
+ IssueAccessToken(auth_token_service);
+ ASSERT_FALSE(manager.IsSubscribed());
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/true);
+ ASSERT_TRUE(manager.IsSubscribed());
+
+ // Check that we are now subscribed correctly with authentication.
+ EXPECT_EQ(subscription_token, GetPrefService()->GetString(
+ prefs::kBreakingNewsSubscriptionDataToken));
+ EXPECT_TRUE(GetPrefService()->GetBoolean(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated));
+}
+#endif
+
+TEST_F(SubscriptionManagerImplTest, ShouldNotSubscribeIfError) {
+ std::string subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), GetOAuth2TokenService(),
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+
+ manager.Subscribe(subscription_token);
+ RespondToSubscriptionWithError(/*is_signed_in=*/false, net::ERR_TIMED_OUT);
+ EXPECT_FALSE(manager.IsSubscribed());
+}
+
+TEST_F(SubscriptionManagerImplTest, UnsubscribeSuccessfully) {
+ std::string subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), GetOAuth2TokenService(),
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+ manager.Subscribe(subscription_token);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+ ASSERT_TRUE(manager.IsSubscribed());
+ manager.Unsubscribe();
+ RespondToUnsubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+ EXPECT_FALSE(manager.IsSubscribed());
+ EXPECT_FALSE(
+ GetPrefService()->HasPrefPath(prefs::kBreakingNewsSubscriptionDataToken));
+}
+
+TEST_F(SubscriptionManagerImplTest,
+ ShouldRemainSubscribedIfErrorDuringUnsubscribe) {
+ std::string subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), GetOAuth2TokenService(),
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+ manager.Subscribe(subscription_token);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+ ASSERT_TRUE(manager.IsSubscribed());
+ manager.Unsubscribe();
+ RespondToUnsubscriptionWithError(/*is_signed_in=*/false, net::ERR_TIMED_OUT);
+ ASSERT_TRUE(manager.IsSubscribed());
+ EXPECT_EQ(subscription_token, GetPrefService()->GetString(
+ prefs::kBreakingNewsSubscriptionDataToken));
+}
+
+// This test is relevant only on non-ChromeOS platforms, as the flow being
+// tested here is not possible on ChromeOS.
+#if !defined(OS_CHROMEOS)
+TEST_F(SubscriptionManagerImplTest,
+ ShouldResubscribeIfSignInAfterSubscription) {
+ // Create manager and subscribe.
+ FakeProfileOAuth2TokenService* auth_token_service = GetOAuth2TokenService();
+ std::string subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), auth_token_service,
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+ manager.Subscribe(subscription_token);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+ ASSERT_FALSE(manager.NeedsToResubscribe());
+
+ // Sign in. This should trigger a resubscribe.
+ SignIn();
+ IssueRefreshToken(auth_token_service);
+ ASSERT_TRUE(manager.NeedsToResubscribe());
+ ASSERT_EQ(1u, auth_token_service->GetPendingRequests().size());
+ IssueAccessToken(auth_token_service);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/true);
+
+ // Check that we are now subscribed with authentication.
+ EXPECT_TRUE(GetPrefService()->GetBoolean(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated));
+}
+#endif
+
+// This test is relevant only on non-ChromeOS platforms, as the flow being
+// tested here is not possible on ChromeOS.
+#if !defined(OS_CHROMEOS)
+TEST_F(SubscriptionManagerImplTest,
+ ShouldResubscribeIfSignOutAfterSubscription) {
+ // Signin and subscribe.
+ FakeProfileOAuth2TokenService* auth_token_service = GetOAuth2TokenService();
+ SignIn();
+ IssueRefreshToken(auth_token_service);
+ std::string subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), auth_token_service,
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+ manager.Subscribe(subscription_token);
+ ASSERT_EQ(1u, auth_token_service->GetPendingRequests().size());
+ IssueAccessToken(auth_token_service);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/true);
+
+ // Signout, this should trigger a resubscribe.
+ SignOut();
+ EXPECT_TRUE(manager.NeedsToResubscribe());
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+
+ // Check that we are now subscribed without authentication.
+ EXPECT_FALSE(GetPrefService()->GetBoolean(
+ prefs::kBreakingNewsSubscriptionDataIsAuthenticated));
+}
+#endif
+
+TEST_F(SubscriptionManagerImplTest,
+ ShouldUpdateTokenInPrefWhenResubscribeWithChangeInToken) {
+ // Create manager and subscribe.
+ std::string old_subscription_token = "1234567890";
+ SubscriptionManagerImpl manager(GetRequestContext(), GetPrefService(),
+ GetSigninManager(), GetOAuth2TokenService(),
+ kAPIKey, GURL(kSubscriptionUrl),
+ GURL(kUnsubscriptionUrl));
+ manager.Subscribe(old_subscription_token);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+ EXPECT_EQ(
+ old_subscription_token,
+ GetPrefService()->GetString(prefs::kBreakingNewsSubscriptionDataToken));
+
+ // Resubscribe with a new token.
+ std::string new_subscription_token = "0987654321";
+ manager.Resubscribe(new_subscription_token);
+ // Resubscribe with a new token should issue an unsubscribe request before
+ // subscribing.
+ RespondToUnsubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+ RespondToSubscriptionRequestSuccessfully(/*is_signed_in=*/false);
+
+ // Check we are now subscribed with the new token.
+ EXPECT_EQ(
+ new_subscription_token,
+ GetPrefService()->GetString(prefs::kBreakingNewsSubscriptionDataToken));
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category.h b/chromium/components/ntp_snippets/category.h
index 97abf753c9b..b63c6af935c 100644
--- a/chromium/components/ntp_snippets/category.h
+++ b/chromium/components/ntp_snippets/category.h
@@ -51,10 +51,12 @@ enum class KnownCategories {
// Articles for you.
ARTICLES,
+ // Breaking News
+ BREAKING_NEWS = 10008,
// ****************** INSERT NEW REMOTE CATEGORIES HERE! ******************
// Tracks the last known remote category
- LAST_KNOWN_REMOTE_CATEGORY = ARTICLES,
+ LAST_KNOWN_REMOTE_CATEGORY = BREAKING_NEWS,
};
// A category groups ContentSuggestions which belong together. Use the
diff --git a/chromium/components/ntp_snippets/content_suggestion.cc b/chromium/components/ntp_snippets/content_suggestion.cc
index a26bfa1e255..c3919f7c740 100644
--- a/chromium/components/ntp_snippets/content_suggestion.cc
+++ b/chromium/components/ntp_snippets/content_suggestion.cc
@@ -25,12 +25,15 @@ bool ContentSuggestion::ID::operator!=(const ID& rhs) const {
}
ContentSuggestion::ContentSuggestion(const ID& id, const GURL& url)
- : id_(id), url_(url), score_(0) {}
+ : id_(id), url_(url), score_(0), is_video_suggestion_(false) {}
ContentSuggestion::ContentSuggestion(Category category,
const std::string& id_within_category,
const GURL& url)
- : id_(category, id_within_category), url_(url), score_(0) {}
+ : id_(category, id_within_category),
+ url_(url),
+ score_(0),
+ is_video_suggestion_(false) {}
ContentSuggestion::ContentSuggestion(ContentSuggestion&&) = default;
diff --git a/chromium/components/ntp_snippets/content_suggestion.h b/chromium/components/ntp_snippets/content_suggestion.h
index 1b70c38fbd1..312c710f062 100644
--- a/chromium/components/ntp_snippets/content_suggestion.h
+++ b/chromium/components/ntp_snippets/content_suggestion.h
@@ -141,6 +141,11 @@ class ContentSuggestion {
publisher_name_ = publisher_name;
}
+ bool is_video_suggestion() const { return is_video_suggestion_; }
+ void set_is_video_suggestion(bool is_video_suggestion) {
+ is_video_suggestion_ = is_video_suggestion;
+ }
+
// TODO(pke): Remove the score from the ContentSuggestion class. The UI only
// uses it to track user clicks (histogram data). Instead, the providers
// should be informed about clicks and do appropriate logging themselves.
@@ -209,6 +214,8 @@ class ContentSuggestion {
// RemoteSuggestion.
base::Time fetch_date_;
+ bool is_video_suggestion_;
+
DISALLOW_COPY_AND_ASSIGN(ContentSuggestion);
};
diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics.cc b/chromium/components/ntp_snippets/content_suggestions_metrics.cc
index 348a555694e..3be66f2a0eb 100644
--- a/chromium/components/ntp_snippets/content_suggestions_metrics.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_metrics.cc
@@ -23,8 +23,8 @@ const int kMaxSuggestionsPerCategory = 10;
const int kMaxSuggestionsTotal = 50;
const int kMaxCategories = 10;
-const char kHistogramCountOnNtpOpened[] =
- "NewTabPage.ContentSuggestions.CountOnNtpOpened";
+const char kHistogramCountOnNtpOpenedIfVisible[] =
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible";
const char kHistogramSectionCountOnNtpOpened[] =
"NewTabPage.ContentSuggestions.SectionCountOnNtpOpened";
const char kHistogramShown[] = "NewTabPage.ContentSuggestions.Shown";
@@ -78,6 +78,7 @@ enum HistogramCategories {
FOREIGN_TABS,
ARTICLES,
READING_LIST,
+ BREAKING_NEWS,
// Insert new values here!
COUNT
};
@@ -107,6 +108,8 @@ HistogramCategories GetHistogramCategory(Category category) {
return HistogramCategories::ARTICLES;
case KnownCategories::READING_LIST:
return HistogramCategories::READING_LIST;
+ case KnownCategories::BREAKING_NEWS:
+ return HistogramCategories::BREAKING_NEWS;
case KnownCategories::LOCAL_CATEGORIES_COUNT:
case KnownCategories::REMOTE_CATEGORIES_OFFSET:
NOTREACHED();
@@ -137,6 +140,8 @@ std::string GetCategorySuffix(Category category) {
return "Experimental";
case HistogramCategories::READING_LIST:
return "ReadingList";
+ case HistogramCategories::BREAKING_NEWS:
+ return "BreakingNews";
case HistogramCategories::COUNT:
NOTREACHED();
break;
@@ -213,17 +218,24 @@ void RecordContentSuggestionsUsage() {
} // namespace
-void OnPageShown(
- const std::vector<std::pair<Category, int>>& suggestions_per_category,
- int visible_categories_count) {
+void OnPageShown(const std::vector<Category>& categories,
+ const std::vector<int>& suggestions_per_category,
+ const std::vector<bool>& is_category_visible) {
+ DCHECK_EQ(categories.size(), suggestions_per_category.size());
+ DCHECK_EQ(categories.size(), is_category_visible.size());
int suggestions_total = 0;
- for (const std::pair<Category, int>& item : suggestions_per_category) {
- LogCategoryHistogramPosition(kHistogramCountOnNtpOpened, item.first,
- item.second, kMaxSuggestionsPerCategory);
- suggestions_total += item.second;
+ int visible_categories_count = 0;
+ for (size_t i = 0; i < categories.size(); ++i) {
+ if (is_category_visible[i]) {
+ LogCategoryHistogramPosition(kHistogramCountOnNtpOpenedIfVisible,
+ categories[i], suggestions_per_category[i],
+ kMaxSuggestionsPerCategory);
+ suggestions_total += suggestions_per_category[i];
+ ++visible_categories_count;
+ }
}
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramCountOnNtpOpened, suggestions_total,
- kMaxSuggestionsTotal);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramCountOnNtpOpenedIfVisible,
+ suggestions_total, kMaxSuggestionsTotal);
UMA_HISTOGRAM_EXACT_LINEAR(kHistogramSectionCountOnNtpOpened,
visible_categories_count, kMaxCategories);
}
diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics.h b/chromium/components/ntp_snippets/content_suggestions_metrics.h
index e31b7713633..160242aec10 100644
--- a/chromium/components/ntp_snippets/content_suggestions_metrics.h
+++ b/chromium/components/ntp_snippets/content_suggestions_metrics.h
@@ -15,9 +15,12 @@
namespace ntp_snippets {
namespace metrics {
-void OnPageShown(
- const std::vector<std::pair<Category, int>>& suggestions_per_category,
- int visible_categories_count);
+// |is_category_visible| contains true iff the corresponding category can be
+// seen by the user on this page (even if it is empty). It does not depend on
+// whether the user actually saw the category.
+void OnPageShown(const std::vector<Category>& categories,
+ const std::vector<int>& suggestions_per_category,
+ const std::vector<bool>& is_category_visible);
// Should only be called once per NTP for each suggestion.
void OnSuggestionShown(int global_position,
diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc b/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
index fe8193e300f..fd636e4b967 100644
--- a/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
@@ -15,6 +15,7 @@ namespace metrics {
namespace {
using testing::ElementsAre;
+using testing::IsEmpty;
TEST(ContentSuggestionsMetricsTest, ShouldLogOnSuggestionsShown) {
base::HistogramTester histogram_tester;
@@ -45,6 +46,87 @@ TEST(ContentSuggestionsMetricsTest, ShouldLogOnSuggestionsShown) {
base::Bucket(/*min=*/11, /*count=*/1)));
}
+TEST(ContentSuggestionsMetricsTest,
+ ShouldNotLogNotShownCategoriesWhenPageShown) {
+ base::HistogramTester histogram_tester;
+ OnPageShown(std::vector<Category>(
+ {Category::FromKnownCategory(KnownCategories::ARTICLES)}),
+ /*suggestions_per_category=*/{0},
+ /*is_category_visible=*/{false});
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible.Articles"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.SectionCountOnNtpOpened"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+}
+
+TEST(ContentSuggestionsMetricsTest,
+ ShouldLogEmptyShownCategoriesWhenPageShown) {
+ base::HistogramTester histogram_tester;
+ OnPageShown(std::vector<Category>(
+ {Category::FromKnownCategory(KnownCategories::ARTICLES)}),
+ /*suggestions_per_category=*/{0},
+ /*is_category_visible=*/{true});
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible.Articles"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.SectionCountOnNtpOpened"),
+ ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
+}
+
+TEST(ContentSuggestionsMetricsTest,
+ ShouldLogNonEmptyShownCategoryWhenPageShown) {
+ base::HistogramTester histogram_tester;
+ OnPageShown(std::vector<Category>(
+ {Category::FromKnownCategory(KnownCategories::ARTICLES)}),
+ /*suggestions_per_category=*/{10},
+ /*is_category_visible=*/{true});
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible.Articles"),
+ ElementsAre(base::Bucket(/*min=*/10, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible"),
+ ElementsAre(base::Bucket(/*min=*/10, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.SectionCountOnNtpOpened"),
+ ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
+}
+
+TEST(ContentSuggestionsMetricsTest,
+ ShouldLogMultipleNonEmptyShownCategoriesWhenPageShown) {
+ base::HistogramTester histogram_tester;
+ OnPageShown(std::vector<Category>(
+ {Category::FromKnownCategory(KnownCategories::ARTICLES),
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS)}),
+ /*suggestions_per_category=*/{10, 5},
+ /*is_category_visible=*/{true, true});
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible.Articles"),
+ ElementsAre(base::Bucket(/*min=*/10, /*count=*/1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible.Bookmarks"),
+ ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible"),
+ ElementsAre(base::Bucket(/*min=*/15, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.SectionCountOnNtpOpened"),
+ ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
+}
+
} // namespace
} // namespace metrics
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/content_suggestions_service.cc b/chromium/components/ntp_snippets/content_suggestions_service.cc
index 71f323594d2..d06e2170c45 100644
--- a/chromium/components/ntp_snippets/content_suggestions_service.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_service.cc
@@ -25,6 +25,7 @@
#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
#include "ui/gfx/image/image.h"
namespace ntp_snippets {
@@ -232,6 +233,9 @@ void ContentSuggestionsService::OnGetFaviconFromCacheFinished(
RecordFaviconFetchResult(continue_to_google_server
? FaviconFetchResult::SUCCESS_CACHED
: FaviconFetchResult::SUCCESS_FETCHED);
+ // Update the time when the icon was last requested - postpone thus the
+ // automatic eviction of the favicon from the favicon database.
+ large_icon_service_->TouchIconFromGoogleServer(result.icon_url);
return;
}
@@ -250,10 +254,29 @@ void ContentSuggestionsService::OnGetFaviconFromCacheFinished(
// TODO(jkrcal): Currently used only for Articles for you which have public
// URLs. Let the provider decide whether |publisher_url| may be private or
// not.
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("content_suggestion_get_favicon", R"(
+ semantics {
+ sender: "Content Suggestion"
+ description:
+ "Sends a request to a Google server to retrieve the favicon bitmap "
+ "for an article suggestion on the new tab page (URLs are public "
+ "and provided by Google)."
+ trigger:
+ "A request can be sent if Chrome does not have a favicon for a "
+ "particular page."
+ data: "Page URL and desired icon size."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: false
+ setting: "This feature cannot be disabled by settings."
+ policy_exception_justification: "Not implemented."
+ })");
large_icon_service_
->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
publisher_url, minimum_size_in_pixel, desired_size_in_pixel,
- /*may_page_url_be_private=*/false,
+ /*may_page_url_be_private=*/false, traffic_annotation,
base::Bind(
&ContentSuggestionsService::OnGetFaviconFromGoogleServerFinished,
base::Unretained(this), publisher_url, minimum_size_in_pixel,
@@ -265,8 +288,8 @@ void ContentSuggestionsService::OnGetFaviconFromGoogleServerFinished(
int minimum_size_in_pixel,
int desired_size_in_pixel,
const ImageFetchedCallback& callback,
- bool success) {
- if (!success) {
+ favicon_base::GoogleFaviconServerRequestStatus status) {
+ if (status != favicon_base::GoogleFaviconServerRequestStatus::SUCCESS) {
callback.Run(gfx::Image());
RecordFaviconFetchResult(FaviconFetchResult::FAILURE);
return;
@@ -412,19 +435,28 @@ void ContentSuggestionsService::ReloadSuggestions() {
}
void ContentSuggestionsService::SetRemoteSuggestionsEnabled(bool enabled) {
- pref_service_->SetBoolean(prefs::kEnableSnippets, enabled);
+ // TODO(dgn): Rewire if we decide to implement a dedicated prefs page. If not
+ // remove by M62.
+ NOTREACHED();
}
bool ContentSuggestionsService::AreRemoteSuggestionsEnabled() const {
- return pref_service_->GetBoolean(prefs::kEnableSnippets);
+ return remote_suggestions_provider_ &&
+ !remote_suggestions_provider_->IsDisabled();
}
bool ContentSuggestionsService::AreRemoteSuggestionsManaged() const {
- return pref_service_->IsManagedPreference(prefs::kEnableSnippets);
+ // TODO(dgn): Rewire if we decide to implement a dedicated prefs page. If not
+ // remove by M62.
+ NOTREACHED();
+ return false;
}
bool ContentSuggestionsService::AreRemoteSuggestionsManagedByCustodian() const {
- return pref_service_->IsPreferenceManagedByCustodian(prefs::kEnableSnippets);
+ // TODO(dgn): Rewire if we decide to implement a dedicated prefs page. If not
+ // remove by M62.
+ NOTREACHED();
+ return false;
}
////////////////////////////////////////////////////////////////////////////////
@@ -497,8 +529,7 @@ void ContentSuggestionsService::OnSuggestionInvalidated(
// SigninManagerBase::Observer implementation
void ContentSuggestionsService::GoogleSigninSucceeded(
const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
OnSignInStateChanged();
}
diff --git a/chromium/components/ntp_snippets/content_suggestions_service.h b/chromium/components/ntp_snippets/content_suggestions_service.h
index 40c442bc6bb..de03d6b5cb0 100644
--- a/chromium/components/ntp_snippets/content_suggestions_service.h
+++ b/chromium/components/ntp_snippets/content_suggestions_service.h
@@ -15,7 +15,6 @@
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/scoped_observer.h"
-#include "base/supports_user_data.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/time/time.h"
#include "components/history/core/browser/history_service.h"
@@ -48,7 +47,6 @@ class RemoteSuggestionsProvider;
// Retrieves suggestions from a number of ContentSuggestionsProviders and serves
// them grouped into categories. There can be at most one provider per category.
class ContentSuggestionsService : public KeyedService,
- public base::SupportsUserData,
public ContentSuggestionsProvider::Observer,
public SigninManagerBase::Observer,
public history::HistoryServiceObserver {
@@ -291,8 +289,7 @@ class ContentSuggestionsService : public KeyedService,
// SigninManagerBase::Observer implementation
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSignedOut(const std::string& account_id,
const std::string& username) override;
@@ -353,7 +350,7 @@ class ContentSuggestionsService : public KeyedService,
int minimum_size_in_pixel,
int desired_size_in_pixel,
const ImageFetchedCallback& callback,
- bool success);
+ favicon_base::GoogleFaviconServerRequestStatus status);
// Whether the content suggestions feature is enabled.
State state_;
diff --git a/chromium/components/ntp_snippets/features.cc b/chromium/components/ntp_snippets/features.cc
index 6910f402873..f32adff9f4b 100644
--- a/chromium/components/ntp_snippets/features.cc
+++ b/chromium/components/ntp_snippets/features.cc
@@ -18,11 +18,12 @@ const base::Feature*(kAllFeatures[]) = {&kArticleSuggestionsFeature,
&kBookmarkSuggestionsFeature,
&kCategoryOrder,
&kCategoryRanker,
+ &kBreakingNewsPushFeature,
&kForeignSessionsSuggestionsFeature,
&kIncreasedVisibility,
+ &kKeepPrefetchedContentSuggestions,
&kNotificationsFeature,
&kPhysicalWebPageSuggestionsFeature,
- &kPreferAmpUrlsFeature,
&kPublisherFaviconsFromNewServerFeature,
&kRecentOfflineTabSuggestionsFeature,
nullptr};
@@ -45,8 +46,8 @@ const base::Feature kPhysicalWebPageSuggestionsFeature{
const base::Feature kForeignSessionsSuggestionsFeature{
"NTPForeignSessionsSuggestions", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kPreferAmpUrlsFeature{"NTPPreferAmpUrls",
- base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kBreakingNewsPushFeature{"BreakingNewsPush",
+ base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kCategoryRanker{"ContentSuggestionsCategoryRanker",
base::FEATURE_ENABLED_BY_DEFAULT};
@@ -55,6 +56,10 @@ const base::Feature kPublisherFaviconsFromNewServerFeature{
"ContentSuggestionsFaviconsFromNewServer",
base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kRemoteSuggestionsEmulateM58FetchingSchedule{
+ "RemoteSuggestionsEmulateM58FetchingSchedule",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
const char kCategoryRankerParameter[] = "category_ranker";
const char kCategoryRankerConstantRanker[] = "constant";
const char kCategoryRankerClickBasedRanker[] = "click_based";
@@ -65,6 +70,9 @@ CategoryRankerChoice GetSelectedCategoryRanker() {
kCategoryRankerParameter);
if (category_ranker_value.empty()) {
+ // TODO(crbug.com/735066): Remove the experiment configurations from
+ // fieldtrial_testing_config.json when enabling ClickBasedRanker by default.
+
// Default, Enabled or Disabled.
return CategoryRankerChoice::CONSTANT;
}
@@ -141,4 +149,7 @@ const char kNotificationsOpenToNTPParam[] = "open_to_ntp";
const char kNotificationsDailyLimit[] = "daily_limit";
const char kNotificationsIgnoredLimitParam[] = "ignored_limit";
+const base::Feature kKeepPrefetchedContentSuggestions{
+ "KeepPrefetchedContentSuggestions", base::FEATURE_DISABLED_BY_DEFAULT};
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/features.h b/chromium/components/ntp_snippets/features.h
index 9946e7c2d0a..83800fa157c 100644
--- a/chromium/components/ntp_snippets/features.h
+++ b/chromium/components/ntp_snippets/features.h
@@ -37,8 +37,8 @@ extern const base::Feature kForeignSessionsSuggestionsFeature;
// Feature to allow UI as specified here: https://crbug.com/660837.
extern const base::Feature kIncreasedVisibility;
-// Feature to prefer AMP URLs over regular URLs when available.
-extern const base::Feature kPreferAmpUrlsFeature;
+// Feature to listen for GCM push updates from the server.
+extern const base::Feature kBreakingNewsPushFeature;
// Feature to choose a category ranker.
extern const base::Feature kCategoryRanker;
@@ -46,6 +46,12 @@ extern const base::Feature kCategoryRanker;
// Feature to allow the new Google favicon server for fetching publisher icons.
extern const base::Feature kPublisherFaviconsFromNewServerFeature;
+// Feature for simple experimental comparision and validation of changes since
+// M58: enabling this brings back the M58 Stable fetching schedule (which is
+// suitable for Holdback groups).
+// TODO(jkrcal): Remove when the comparision is done (probably after M62).
+extern const base::Feature kRemoteSuggestionsEmulateM58FetchingSchedule;
+
// Parameter and its values for the kCategoryRanker feature flag.
extern const char kCategoryRankerParameter[];
extern const char kCategoryRankerConstantRanker[];
@@ -113,6 +119,10 @@ constexpr int kNotificationsDefaultDailyLimit = 1;
extern const char kNotificationsIgnoredLimitParam[];
constexpr int kNotificationsIgnoredDefaultLimit = 3;
+// Whether to keep some prefetched content suggestions even when new suggestions
+// have been fetched.
+extern const base::Feature kKeepPrefetchedContentSuggestions;
+
} // namespace ntp_snippets
#endif // COMPONENTS_NTP_SNIPPETS_FEATURES_H_
diff --git a/chromium/components/ntp_snippets/ntp_snippets_constants.cc b/chromium/components/ntp_snippets/ntp_snippets_constants.cc
index 82304939470..52abe08ab69 100644
--- a/chromium/components/ntp_snippets/ntp_snippets_constants.cc
+++ b/chromium/components/ntp_snippets/ntp_snippets_constants.cc
@@ -9,6 +9,12 @@ namespace ntp_snippets {
const base::FilePath::CharType kDatabaseFolder[] =
FILE_PATH_LITERAL("NTPSnippets");
+const base::FilePath::CharType kBreakingNewsDatabaseFolder[] =
+ FILE_PATH_LITERAL("NTPBreakingNews");
+
+const char kContentSuggestionsApiScope[] =
+ "https://www.googleapis.com/auth/chrome-content-suggestions";
+
const char kContentSuggestionsServer[] =
"https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/fetch";
const char kContentSuggestionsStagingServer[] =
@@ -18,4 +24,24 @@ const char kContentSuggestionsAlphaServer[] =
"https://alpha-chromecontentsuggestions-pa.sandbox.googleapis.com/v1/"
"suggestions/fetch";
+const char kPushUpdatesSubscriptionServer[] =
+ "https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
+ "subscribe";
+const char kPushUpdatesSubscriptionStagingServer[] =
+ "https://staging-chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
+ "subscribe";
+const char kPushUpdatesSubscriptionAlphaServer[] =
+ "https://alpha-chromecontentsuggestions-pa.sandbox.googleapis.com/v1/"
+ "suggestions/subscribe";
+
+const char kPushUpdatesUnsubscriptionServer[] =
+ "https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
+ "unsubscribe";
+const char kPushUpdatesUnsubscriptionStagingServer[] =
+ "https://staging-chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
+ "unsubscribe";
+const char kPushUpdatesUnsubscriptionAlphaServer[] =
+ "https://alpha-chromecontentsuggestions-pa.sandbox.googleapis.com/v1/"
+ "suggestions/unsubscribe";
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/ntp_snippets_constants.h b/chromium/components/ntp_snippets/ntp_snippets_constants.h
index aff5c3720d4..f7ddd58acb3 100644
--- a/chromium/components/ntp_snippets/ntp_snippets_constants.h
+++ b/chromium/components/ntp_snippets/ntp_snippets_constants.h
@@ -13,12 +13,29 @@ namespace ntp_snippets {
// the name of the folder, not a full path - it must be appended to e.g. the
// profile path.
extern const base::FilePath::CharType kDatabaseFolder[];
+// TODO(mamir): Check if the same DB can be used.
+extern const base::FilePath::CharType kBreakingNewsDatabaseFolder[];
+
+// OAuth access token scope.
+extern const char kContentSuggestionsApiScope[];
// Server endpoints for fetching snippets.
extern const char kContentSuggestionsServer[]; // used on stable/beta
extern const char kContentSuggestionsStagingServer[]; // used on dev/canary
extern const char kContentSuggestionsAlphaServer[]; // for testing
+// Server endpoints for push updates subscription.
+extern const char kPushUpdatesSubscriptionServer[]; // used on stable/beta
+extern const char
+ kPushUpdatesSubscriptionStagingServer[]; // used on dev/canary
+extern const char kPushUpdatesSubscriptionAlphaServer[]; // for testing
+
+// Server endpoints for push updates unsubscription.
+extern const char kPushUpdatesUnsubscriptionServer[]; // used on stable/beta
+extern const char
+ kPushUpdatesUnsubscriptionStagingServer[]; // used on dev/canary
+extern const char kPushUpdatesUnsubscriptionAlphaServer[]; // for testing
+
} // namespace ntp_snippets
#endif
diff --git a/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc
index 7d8489ca0e0..c43ad141d2b 100644
--- a/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc
+++ b/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc
@@ -116,7 +116,9 @@ class RecentTabSuggestionsProviderTestNoLoad : public testing::Test {
int tab_id = offline_pages::RecentTabsUIAdapterDelegate::TabIdFromClientId(
item.client_id);
RemoveTab(tab_id);
- ui_adapter_->OfflinePageDeleted(item.offline_id, item.client_id);
+ ui_adapter_->OfflinePageDeleted(
+ offline_pages::OfflinePageModel::DeletedPageInfo(
+ item.offline_id, item.client_id, "" /* request_origin */));
}
std::set<std::string> ReadDismissedIDsFromPrefs() {
diff --git a/chromium/components/ntp_snippets/pref_names.cc b/chromium/components/ntp_snippets/pref_names.cc
index bea67c36392..72851b04e21 100644
--- a/chromium/components/ntp_snippets/pref_names.cc
+++ b/chromium/components/ntp_snippets/pref_names.cc
@@ -81,5 +81,14 @@ const char kClickBasedCategoryRankerOrderWithClicks[] =
const char kClickBasedCategoryRankerLastDecayTime[] =
"ntp_suggestions.click_based_category_ranker.last_decay_time";
+const char kBreakingNewsSubscriptionDataToken[] =
+ "ntp_suggestions.breaking_news_subscription_data.token";
+
+const char kBreakingNewsSubscriptionDataIsAuthenticated[] =
+ "ntp_suggestions.breaking_news_subscription_data.is_authenticated";
+
+const char kBreakingNewsGCMSubscriptionTokenCache[] =
+ "ntp_suggestions.breaking_news_gcm_subscription_token_cache";
+
} // namespace prefs
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/pref_names.h b/chromium/components/ntp_snippets/pref_names.h
index fe3e198c83e..110aafee4e2 100644
--- a/chromium/components/ntp_snippets/pref_names.h
+++ b/chromium/components/ntp_snippets/pref_names.h
@@ -90,6 +90,26 @@ extern const char kClickBasedCategoryRankerOrderWithClicks[];
// The pref name for the time when last click decay has happened.
extern const char kClickBasedCategoryRankerLastDecayTime[];
+// The folllowing prefs hold the data used when subscribing for content
+// suggestions via GCM push updates. They are stored in pref such that in case
+// of change (e.g. the token renders invalid), re-subscription is required.
+// They are stored in prefs for persisting them across Chrome restarts.
+///////////////////////////////////////////////////////////////////////////////
+// The pref name for the subscription token used when subscription for
+// breaking news push updates.
+extern const char kBreakingNewsSubscriptionDataToken[];
+// The pref name for whether the subscription is authenticated or not.
+extern const char kBreakingNewsSubscriptionDataIsAuthenticated[];
+//////////////////////// End of breaking news subscription-related prefs.
+
+// The pref name for the subscription token received from the gcm server. As
+// recommended by the GCM team, it is cached in pref for faster bookkeeping to
+// see if subscription exists. This is pref holds the valid token even if
+// different from the one used for subscription. When they are different, Chrome
+// unsubscribes the old token from the content suggestions server, subscribe
+// with the new one and update kBreakingNewsSubscriptionDataToken.
+extern const char kBreakingNewsGCMSubscriptionTokenCache[];
+
} // namespace prefs
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/DEPS b/chromium/components/ntp_snippets/remote/DEPS
new file mode 100644
index 00000000000..984d8cbcbb5
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/offline_pages"
+]
diff --git a/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc b/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
new file mode 100644
index 00000000000..5faf2830cc1
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
@@ -0,0 +1,142 @@
+// 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/ntp_snippets/remote/cached_image_fetcher.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "components/image_fetcher/core/image_decoder.h"
+#include "components/image_fetcher/core/image_fetcher.h"
+#include "components/ntp_snippets/remote/remote_suggestions_database.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image.h"
+
+namespace ntp_snippets {
+
+CachedImageFetcher::CachedImageFetcher(
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
+ PrefService* pref_service,
+ RemoteSuggestionsDatabase* database)
+ : image_fetcher_(std::move(image_fetcher)),
+ database_(database),
+ thumbnail_requests_throttler_(
+ pref_service,
+ RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) {
+ // |image_fetcher_| can be null in tests.
+ if (image_fetcher_) {
+ image_fetcher_->SetImageFetcherDelegate(this);
+ image_fetcher_->SetDataUseServiceName(
+ data_use_measurement::DataUseUserData::NTP_SNIPPETS_THUMBNAILS);
+ }
+}
+
+CachedImageFetcher::~CachedImageFetcher() {}
+
+void CachedImageFetcher::FetchSuggestionImage(
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ const ImageFetchedCallback& callback) {
+ database_->LoadImage(
+ suggestion_id.id_within_category(),
+ base::Bind(&CachedImageFetcher::OnImageFetchedFromDatabase,
+ base::Unretained(this), callback, suggestion_id, url));
+}
+
+// This function gets only called for caching the image data received from the
+// network. The actual decoding is done in OnImageDecodedFromDatabase().
+void CachedImageFetcher::OnImageDataFetched(
+ const std::string& id_within_category,
+ const std::string& image_data) {
+ if (image_data.empty()) {
+ return;
+ }
+ database_->SaveImage(id_within_category, image_data);
+}
+
+void CachedImageFetcher::OnImageDecodingDone(
+ const ImageFetchedCallback& callback,
+ const std::string& id_within_category,
+ const gfx::Image& image,
+ const image_fetcher::RequestMetadata& metadata) {
+ callback.Run(image);
+}
+
+void CachedImageFetcher::OnImageFetchedFromDatabase(
+ const ImageFetchedCallback& callback,
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ std::string data) { // SnippetImageCallback requires by-value.
+ // The image decoder is null in tests.
+ if (image_fetcher_->GetImageDecoder() && !data.empty()) {
+ image_fetcher_->GetImageDecoder()->DecodeImage(
+ data,
+ // We're not dealing with multi-frame images.
+ /*desired_image_frame_size=*/gfx::Size(),
+ base::Bind(&CachedImageFetcher::OnImageDecodedFromDatabase,
+ base::Unretained(this), callback, suggestion_id, url));
+ return;
+ }
+ // Fetching from the DB failed; start a network fetch.
+ FetchImageFromNetwork(suggestion_id, url, callback);
+}
+
+void CachedImageFetcher::OnImageDecodedFromDatabase(
+ const ImageFetchedCallback& callback,
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ const gfx::Image& image) {
+ if (!image.IsEmpty()) {
+ callback.Run(image);
+ return;
+ }
+ // If decoding the image failed, delete the DB entry.
+ database_->DeleteImage(suggestion_id.id_within_category());
+ FetchImageFromNetwork(suggestion_id, url, callback);
+}
+
+void CachedImageFetcher::FetchImageFromNetwork(
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ const ImageFetchedCallback& callback) {
+ if (url.is_empty() || !thumbnail_requests_throttler_.DemandQuotaForRequest(
+ /*interactive_request=*/true)) {
+ // Return an empty image. Directly, this is never synchronous with the
+ // original FetchSuggestionImage() call - an asynchronous database query has
+ // happened in the meantime.
+ callback.Run(gfx::Image());
+ return;
+ }
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("remote_suggestions_provider", R"(
+ semantics {
+ sender: "Content Suggestion Thumbnail Fetch"
+ description:
+ "Retrieves thumbnails for content suggestions, for display on the "
+ "New Tab page or Chrome Home."
+ trigger:
+ "Triggered when the user looks at a content suggestion (and its "
+ "thumbnail isn't cached yet)."
+ data: "None."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: false
+ setting: "Currently not available, but in progress: crbug.com/703684"
+ chrome_policy {
+ NTPContentSuggestionsEnabled {
+ policy_options {mode: MANDATORY}
+ NTPContentSuggestionsEnabled: false
+ }
+ }
+ })");
+ image_fetcher_->StartOrQueueNetworkRequest(
+ suggestion_id.id_within_category(), url,
+ base::Bind(&CachedImageFetcher::OnImageDecodingDone,
+ base::Unretained(this), callback),
+ traffic_annotation);
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/cached_image_fetcher.h b/chromium/components/ntp_snippets/remote/cached_image_fetcher.h
new file mode 100644
index 00000000000..1bb290e2758
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/cached_image_fetcher.h
@@ -0,0 +1,86 @@
+// 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_NTP_SNIPPETS_REMOTE_CACHED_IMAGE_FETCHER_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_CACHED_IMAGE_FETCHER_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "components/image_fetcher/core/image_fetcher_delegate.h"
+#include "components/ntp_snippets/callbacks.h"
+#include "components/ntp_snippets/content_suggestion.h"
+#include "components/ntp_snippets/remote/request_throttler.h"
+
+class PrefService;
+
+namespace gfx {
+class Image;
+} // namespace gfx
+
+namespace image_fetcher {
+class ImageFetcher;
+struct RequestMetadata;
+} // namespace image_fetcher
+
+namespace ntp_snippets {
+
+class RemoteSuggestionsDatabase;
+
+// CachedImageFetcher takes care of fetching images from the network and caching
+// them in the database.
+class CachedImageFetcher : public image_fetcher::ImageFetcherDelegate {
+ public:
+ // |pref_service| and |database| need to outlive the created image fetcher
+ // instance.
+ CachedImageFetcher(std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
+ PrefService* pref_service,
+ RemoteSuggestionsDatabase* database);
+ ~CachedImageFetcher() override;
+
+ // Fetches the image for a suggestion. The fetcher will first issue a lookup
+ // to the underlying cache with a fallback to the network.
+ void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id,
+ const GURL& image_url,
+ const ImageFetchedCallback& callback);
+
+ private:
+ // image_fetcher::ImageFetcherDelegate implementation.
+ void OnImageDataFetched(const std::string& id_within_category,
+ const std::string& image_data) override;
+
+ void OnImageDecodingDone(const ImageFetchedCallback& callback,
+ const std::string& id_within_category,
+ const gfx::Image& image,
+ const image_fetcher::RequestMetadata& metadata);
+ void OnImageFetchedFromDatabase(
+ const ImageFetchedCallback& callback,
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& image_url,
+ // SnippetImageCallback requires by-value (not const ref).
+ std::string data);
+ void OnImageDecodedFromDatabase(const ImageFetchedCallback& callback,
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ const gfx::Image& image);
+ void FetchImageFromNetwork(const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ const ImageFetchedCallback& callback);
+
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_;
+ RemoteSuggestionsDatabase* database_;
+ // Request throttler for limiting requests to thumbnail images.
+ RequestThrottler thumbnail_requests_throttler_;
+
+ DISALLOW_COPY_AND_ASSIGN(CachedImageFetcher);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_CACHED_IMAGE_FETCHER_H_
diff --git a/chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc b/chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
new file mode 100644
index 00000000000..b176acaa8ac
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
@@ -0,0 +1,153 @@
+// 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/ntp_snippets/remote/cached_image_fetcher.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/image_fetcher/core/image_decoder.h"
+#include "components/image_fetcher/core/image_fetcher.h"
+#include "components/image_fetcher/core/image_fetcher_impl.h"
+#include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
+#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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_unittest_util.h"
+
+using testing::_;
+using testing::Eq;
+using testing::Property;
+
+namespace ntp_snippets {
+
+namespace {
+
+const char kImageData[] = "data";
+const char kImageURL[] = "http://image.test/test.png";
+const char kSnippetID[] = "http://localhost";
+
+// Always decodes a valid image for all non-empty input.
+class FakeImageDecoder : public image_fetcher::ImageDecoder {
+ public:
+ void DecodeImage(
+ const std::string& image_data,
+ const gfx::Size& desired_image_frame_size,
+ const image_fetcher::ImageDecodedCallback& callback) override {
+ gfx::Image image;
+ if (!image_data.empty()) {
+ image = gfx::test::CreateImage();
+ }
+ callback.Run(image);
+ }
+};
+
+} // namespace
+
+class CachedImageFetcherTest : public testing::Test {
+ public:
+ CachedImageFetcherTest()
+ : fake_url_fetcher_factory_(nullptr),
+ mock_task_runner_(new base::TestSimpleTaskRunner()),
+ mock_task_runner_handle_(mock_task_runner_) {
+ EXPECT_TRUE(database_dir_.CreateUniqueTempDir());
+
+ RequestThrottler::RegisterProfilePrefs(pref_service_.registry());
+ database_ = base::MakeUnique<RemoteSuggestionsDatabase>(
+ database_dir_.GetPath(), mock_task_runner_);
+ request_context_getter_ = scoped_refptr<net::TestURLRequestContextGetter>(
+ new net::TestURLRequestContextGetter(mock_task_runner_.get()));
+
+ auto decoder = base::MakeUnique<FakeImageDecoder>();
+ fake_image_decoder_ = decoder.get();
+ cached_image_fetcher_ = base::MakeUnique<ntp_snippets::CachedImageFetcher>(
+ base::MakeUnique<image_fetcher::ImageFetcherImpl>(
+ std::move(decoder), request_context_getter_.get()),
+ &pref_service_, database_.get());
+ RunUntilIdle();
+ EXPECT_TRUE(database_->IsInitialized());
+ }
+
+ void FetchImage(const ImageFetchedCallback& callback) {
+ ContentSuggestion::ID content_suggestion_id(
+ Category::FromKnownCategory(KnownCategories::ARTICLES), kSnippetID);
+ cached_image_fetcher_->FetchSuggestionImage(content_suggestion_id,
+ GURL(kImageURL), callback);
+ }
+
+ void RunUntilIdle() { mock_task_runner_->RunUntilIdle(); }
+
+ RemoteSuggestionsDatabase* database() { return database_.get(); }
+ FakeImageDecoder* fake_image_decoder() { return fake_image_decoder_; }
+ net::FakeURLFetcherFactory* fake_url_fetcher_factory() {
+ return &fake_url_fetcher_factory_;
+ }
+
+ private:
+ 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_;
+ scoped_refptr<base::TestSimpleTaskRunner> mock_task_runner_;
+ base::ThreadTaskRunnerHandle mock_task_runner_handle_;
+ TestingPrefServiceSimple pref_service_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+
+ DISALLOW_COPY_AND_ASSIGN(CachedImageFetcherTest);
+};
+
+TEST_F(CachedImageFetcherTest, FetchImageFromCache) {
+ // Save the image in the database.
+ database()->SaveImage(kSnippetID, kImageData);
+ RunUntilIdle();
+
+ // Do not provide any URL responses and expect that the image is fetched (from
+ // cache).
+ base::MockCallback<ImageFetchedCallback> mock_image_fetched_callback;
+ EXPECT_CALL(mock_image_fetched_callback,
+ Run(Property(&gfx::Image::IsEmpty, Eq(false))));
+ FetchImage(mock_image_fetched_callback.Get());
+ RunUntilIdle();
+}
+
+TEST_F(CachedImageFetcherTest, FetchImageNotInCache) {
+ // Expect the image to be fetched by URL.
+ fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ base::MockCallback<ImageFetchedCallback> mock_image_fetched_callback;
+ EXPECT_CALL(mock_image_fetched_callback,
+ Run(Property(&gfx::Image::IsEmpty, Eq(false))));
+ FetchImage(mock_image_fetched_callback.Get());
+ RunUntilIdle();
+}
+
+TEST_F(CachedImageFetcherTest, FetchNonExistingImage) {
+ const std::string kErrorResponse = "error-response";
+ fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kErrorResponse,
+ net::HTTP_NOT_FOUND,
+ net::URLRequestStatus::FAILED);
+ // Expect an empty image is fetched if the URL cannot be requested.
+ const std::string kEmptyImageData;
+ base::MockCallback<ImageFetchedCallback> mock_image_fetched_callback;
+ EXPECT_CALL(mock_image_fetched_callback,
+ Run(Property(&gfx::Image::IsEmpty, Eq(true))));
+ FetchImage(mock_image_fetched_callback.Get());
+ RunUntilIdle();
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/contextual_json_request.cc b/chromium/components/ntp_snippets/remote/contextual_json_request.cc
new file mode 100644
index 00000000000..fa1d6a66e44
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/contextual_json_request.cc
@@ -0,0 +1,251 @@
+// 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/ntp_snippets/remote/contextual_json_request.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/ntp_snippets/category_info.h"
+#include "components/ntp_snippets/features.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/variations/net/variations_http_headers.h"
+#include "components/variations/variations_associated_data.h"
+#include "net/base/load_flags.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 "net/url_request/url_request_context_getter.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#include "third_party/icu/source/common/unicode/utypes.h"
+
+using net::URLFetcher;
+using net::URLRequestContextGetter;
+using net::HttpRequestHeaders;
+using net::URLRequestStatus;
+
+namespace ntp_snippets {
+
+namespace internal {
+
+namespace {
+
+const int k5xxRetries = 2;
+
+} // namespace
+
+ContextualJsonRequest::ContextualJsonRequest(const ParseJSONCallback& callback)
+ : parse_json_callback_(callback), weak_ptr_factory_(this) {}
+
+ContextualJsonRequest::~ContextualJsonRequest() {
+ DLOG_IF(ERROR, !request_completed_callback_.is_null())
+ << "The CompletionCallback was never called!";
+}
+
+void ContextualJsonRequest::Start(CompletedCallback callback) {
+ request_completed_callback_ = std::move(callback);
+ url_fetcher_->Start();
+}
+
+std::string ContextualJsonRequest::GetResponseString() const {
+ std::string response;
+ url_fetcher_->GetResponseAsString(&response);
+ return response;
+}
+
+// URLFetcherDelegate overrides
+void ContextualJsonRequest::OnURLFetchComplete(const net::URLFetcher* source) {
+ DCHECK_EQ(url_fetcher_.get(), source);
+ const URLRequestStatus& status = url_fetcher_->GetStatus();
+ int response = url_fetcher_->GetResponseCode();
+ // TODO(gaschler): Add UMA metrics for response status code
+
+ if (!status.is_success()) {
+ 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.
+ std::move(request_completed_callback_)
+ .Run(/*result=*/nullptr, FetchResult::HTTP_ERROR,
+ /*error_details=*/base::StringPrintf(" %d", response));
+ } else {
+ ParseJsonResponse();
+ }
+}
+
+void ContextualJsonRequest::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(&ContextualJsonRequest::OnJsonParsed,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&ContextualJsonRequest::OnJsonError,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ContextualJsonRequest::OnJsonParsed(std::unique_ptr<base::Value> result) {
+ std::move(request_completed_callback_)
+ .Run(std::move(result), FetchResult::SUCCESS,
+ /*error_details=*/std::string());
+}
+
+void ContextualJsonRequest::OnJsonError(const std::string& error) {
+ std::string json_string;
+ url_fetcher_->GetResponseAsString(&json_string);
+ LOG(WARNING) << "Received invalid JSON (" << error << "): " << json_string;
+ std::move(request_completed_callback_)
+ .Run(/*result=*/nullptr, FetchResult::JSON_PARSE_ERROR,
+ /*error_details=*/base::StringPrintf(" (error %s)", error.c_str()));
+}
+
+ContextualJsonRequest::Builder::Builder() = default;
+ContextualJsonRequest::Builder::Builder(ContextualJsonRequest::Builder&&) =
+ default;
+ContextualJsonRequest::Builder::~Builder() = default;
+
+std::unique_ptr<ContextualJsonRequest> ContextualJsonRequest::Builder::Build()
+ const {
+ DCHECK(url_request_context_getter_);
+ auto request = base::MakeUnique<ContextualJsonRequest>(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;
+
+ return request;
+}
+
+ContextualJsonRequest::Builder&
+ContextualJsonRequest::Builder::SetAuthentication(
+ const std::string& account_id,
+ const std::string& auth_header) {
+ auth_header_ = auth_header;
+ return *this;
+}
+
+ContextualJsonRequest::Builder&
+ContextualJsonRequest::Builder::SetParseJsonCallback(
+ ParseJSONCallback callback) {
+ parse_json_callback_ = callback;
+ return *this;
+}
+
+ContextualJsonRequest::Builder& ContextualJsonRequest::Builder::SetUrl(
+ const GURL& url) {
+ url_ = url;
+ return *this;
+}
+
+ContextualJsonRequest::Builder&
+ContextualJsonRequest::Builder::SetUrlRequestContextGetter(
+ const scoped_refptr<net::URLRequestContextGetter>& context_getter) {
+ url_request_context_getter_ = context_getter;
+ return *this;
+}
+
+ContextualJsonRequest::Builder& ContextualJsonRequest::Builder::SetContentUrl(
+ const GURL& url) {
+ content_url_ = url;
+ return *this;
+}
+
+std::string ContextualJsonRequest::Builder::BuildHeaders() const {
+ net::HttpRequestHeaders headers;
+ headers.SetHeader("Content-Type", "application/json; charset=UTF-8");
+ if (!auth_header_.empty()) {
+ headers.SetHeader("Authorization", auth_header_);
+ }
+ // Add X-Client-Data header with experiment IDs from field trials.
+ // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does
+ // not affect transmission of experiments coming from the variations server.
+ bool is_signed_in = false;
+ variations::AppendVariationHeaders(url_,
+ false, // incognito
+ false, // uma_enabled
+ is_signed_in, &headers);
+ return headers.ToString();
+}
+
+std::string ContextualJsonRequest::Builder::BuildBody() const {
+ auto request = base::MakeUnique<base::DictionaryValue>();
+
+ request->SetString("url", content_url_.spec());
+ auto categories = base::MakeUnique<base::ListValue>();
+ categories->AppendString("RELATED_ARTICLES");
+ categories->AppendString("PUBLIC_DEBATE");
+ request->Set("categories", std::move(categories));
+
+ std::string request_json;
+ bool success = base::JSONWriter::WriteWithOptions(
+ *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json);
+ DCHECK(success);
+ return request_json;
+}
+
+std::unique_ptr<net::URLFetcher>
+ContextualJsonRequest::Builder::BuildURLFetcher(
+ net::URLFetcherDelegate* delegate,
+ const std::string& headers,
+ const std::string& body) const {
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("ntp_snippets_fetch", R"(
+ semantics {
+ sender: "New Tab Page Contextual Suggestions Fetch"
+ description:
+ "Chromium can show contextual suggestions that are related to the "
+ "currently visited page on the New Tab page. "
+ trigger:
+ "Triggered when Home sheet is pulled up."
+ data:
+ "The Chromium UI language, as well as a second language the user "
+ "understands, based on translate::LanguageModel. For signed-in "
+ "users, the requests is authenticated."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: false
+ setting:
+ "This feature can be disabled by the flag "
+ "contextual-suggestions-carousel."
+ })");
+ 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(k5xxRetries);
+ return url_fetcher;
+}
+
+} // namespace internal
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/contextual_json_request.h b/chromium/components/ntp_snippets/remote/contextual_json_request.h
new file mode 100644
index 00000000000..cf379da180c
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/contextual_json_request.h
@@ -0,0 +1,115 @@
+// 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_NTP_SNIPPETS_REMOTE_CONTEXTUAL_JSON_REQUEST_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_CONTEXTUAL_JSON_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "components/ntp_snippets/remote/json_request.h"
+#include "components/ntp_snippets/status.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "net/http/http_request_headers.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace ntp_snippets {
+
+namespace internal {
+
+// A request to query contextual suggestions.
+class ContextualJsonRequest : public net::URLFetcherDelegate {
+ 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.
+ using CompletedCallback =
+ base::OnceCallback<void(std::unique_ptr<base::Value> result,
+ FetchResult result_code,
+ const std::string& error_details)>;
+
+ // Builds authenticated and non-authenticated ContextualJsonRequests.
+ class Builder {
+ public:
+ Builder();
+ Builder(Builder&&);
+ ~Builder();
+
+ // Builds a Request object that contains all data to fetch new snippets.
+ std::unique_ptr<ContextualJsonRequest> Build() const;
+
+ Builder& SetAuthentication(const std::string& account_id,
+ const std::string& auth_header);
+ Builder& SetParseJsonCallback(ParseJSONCallback callback);
+ Builder& SetUrl(const GURL& url);
+ Builder& SetUrlRequestContextGetter(
+ const scoped_refptr<net::URLRequestContextGetter>& context_getter);
+ Builder& SetContentUrl(const GURL& url);
+
+ // These preview methods allow to inspect the Request without exposing it
+ // publicly.
+ std::string PreviewRequestBodyForTesting() { return BuildBody(); }
+ std::string PreviewRequestHeadersForTesting() { return BuildHeaders(); }
+
+ private:
+ std::string BuildHeaders() const;
+ std::string BuildBody() const;
+ std::unique_ptr<net::URLFetcher> BuildURLFetcher(
+ net::URLFetcherDelegate* request,
+ const std::string& headers,
+ const std::string& body) const;
+
+ std::string auth_header_;
+ ParseJSONCallback parse_json_callback_;
+ GURL url_;
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+
+ // The URL for which to fetch contextual suggestions for.
+ GURL content_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(Builder);
+ };
+
+ ContextualJsonRequest(const ParseJSONCallback& callback);
+ ContextualJsonRequest(ContextualJsonRequest&&);
+ ~ContextualJsonRequest() override;
+
+ void Start(CompletedCallback callback);
+
+ std::string GetResponseString() const;
+
+ private:
+ // URLFetcherDelegate implementation.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ 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
+ // currently ongoing.
+ std::unique_ptr<net::URLFetcher> url_fetcher_;
+
+ // This callback is called to parse a json string. It contains callbacks for
+ // error and success cases.
+ ParseJSONCallback parse_json_callback_;
+
+ // The callback to notify when URLFetcher finished and results are available.
+ CompletedCallback request_completed_callback_;
+
+ base::WeakPtrFactory<ContextualJsonRequest> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextualJsonRequest);
+};
+
+} // namespace internal
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_CONTEXTUAL_JSON_REQUEST_H_
diff --git a/chromium/components/ntp_snippets/remote/contextual_json_request_unittest.cc b/chromium/components/ntp_snippets/remote/contextual_json_request_unittest.cc
new file mode 100644
index 00000000000..9e8f1ddf11c
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/contextual_json_request_unittest.cc
@@ -0,0 +1,90 @@
+// 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/ntp_snippets/remote/contextual_json_request.h"
+
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/values.h"
+#include "components/ntp_snippets/features.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 ntp_snippets {
+
+namespace internal {
+
+namespace {
+
+using testing::_;
+using testing::Eq;
+using testing::StrEq;
+
+MATCHER_P(EqualsJSON, json, "equals JSON") {
+ std::unique_ptr<base::Value> expected = base::JSONReader::Read(json);
+ if (!expected) {
+ *result_listener << "INTERNAL ERROR: couldn't parse expected JSON";
+ return false;
+ }
+
+ std::string err_msg;
+ int err_line, err_col;
+ std::unique_ptr<base::Value> actual = base::JSONReader::ReadAndReturnError(
+ arg, base::JSON_PARSE_RFC, nullptr, &err_msg, &err_line, &err_col);
+ if (!actual) {
+ *result_listener << "input:" << err_line << ":" << err_col << ": "
+ << "parse error: " << err_msg;
+ return false;
+ }
+ return base::Value::Equals(actual.get(), expected.get());
+}
+
+} // namespace
+
+class ContextualJsonRequestTest : public testing::Test {
+ public:
+ ContextualJsonRequestTest()
+ : request_context_getter_(
+ new net::TestURLRequestContextGetter(loop_.task_runner())) {}
+
+ ContextualJsonRequest::Builder CreateDefaultBuilder() {
+ ContextualJsonRequest::Builder builder;
+ builder.SetUrl(GURL("http://valid-url.test"))
+ .SetUrlRequestContextGetter(request_context_getter_.get());
+ return builder;
+ }
+
+ private:
+ base::MessageLoop loop_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextualJsonRequestTest);
+};
+
+TEST_F(ContextualJsonRequestTest, AuthenticatedRequest) {
+ ContextualJsonRequest::Builder builder = CreateDefaultBuilder();
+ builder.SetAuthentication("0BFUSGAIA", "headerstuff")
+ .SetContentUrl(GURL("http://my-url.test"))
+ .Build();
+
+ EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
+ StrEq("Content-Type: application/json; charset=UTF-8\r\n"
+ "Authorization: headerstuff\r\n"
+ "\r\n"));
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"categories\": [ \"RELATED_ARTICLES\","
+ " \"PUBLIC_DEBATE\" ],"
+ " \"url\": \"http://my-url.test/\""
+ "}"));
+}
+
+} // namespace internal
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/fetch.py b/chromium/components/ntp_snippets/remote/fetch.py
index aa753f3e7e4..d5624c05060 100755
--- a/chromium/components/ntp_snippets/remote/fetch.py
+++ b/chromium/components/ntp_snippets/remote/fetch.py
@@ -43,6 +43,8 @@ API_PATH = "/v1/suggestions/fetch"
def main():
+ default_lang = os.environ.get("LANG", "en_US").split(".")[0]
+
parser = argparse.ArgumentParser(
description="fetch articles from server",
parents=[oauth2client.tools.argparser])
@@ -51,6 +53,9 @@ def main():
help="component to fetch from (default: prod)")
parser.add_argument("-x", "--experiment", action="append", type=int,
help="include an experiment ID")
+ parser.add_argument("-l", "--ui-language", default=default_lang,
+ help="language code (default: %s)" % default_lang)
+ parser.add_argument("--ip", help="fake IP address")
parser.add_argument("--api-key", type=str,
help="API key to use for unauthenticated requests"
" (default: use official key)")
@@ -167,6 +172,9 @@ def PostRequest(args):
if args.experiment:
headers["X-Client-Data"] = EncodeExperiments(args.experiment)
+ if args.ip is not None:
+ headers["X-User-IP"] = args.ip
+
if args.signed_in:
if args.client:
client_id, client_secret = args.client.split(",")
@@ -180,7 +188,11 @@ def PostRequest(args):
api_key = GetAPIKey()
url += "?key=" + api_key
- return requests.post(url, headers=headers)
+ data = {
+ "uiLanguage": args.ui_language,
+ }
+
+ return requests.post(url, headers=headers, data=data)
def Authenticate(args, headers, client_id, client_secret):
@@ -199,7 +211,7 @@ def PrintShortResponse(j):
now = datetime.datetime.now()
for category in j["categories"]:
print("%s: " % category["localizedTitle"])
- for suggestion in category["suggestions"]:
+ for suggestion in category.get("suggestions", []):
attribution = suggestion["attribution"]
title = suggestion["title"]
full_url = suggestion["fullPageUrl"]
diff --git a/chromium/components/ntp_snippets/remote/json_request.cc b/chromium/components/ntp_snippets/remote/json_request.cc
index 2ea9f4f7d57..2be9a6b5d27 100644
--- a/chromium/components/ntp_snippets/remote/json_request.cc
+++ b/chromium/components/ntp_snippets/remote/json_request.cc
@@ -37,11 +37,11 @@
#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;
-using translate::LanguageModel;
namespace ntp_snippets {
@@ -52,7 +52,7 @@ namespace {
// Variation parameter for disabling the retry.
const char kBackground5xxRetriesName[] = "background_5xx_retries_count";
-// Variation parameter for sending LanguageModel info to the server.
+// Variation parameter for sending UrlLanguageHistogram info to the server.
const char kSendTopLanguagesName[] = "send_top_languages";
// Variation parameter for sending UserClassifier info to the server.
@@ -108,7 +108,7 @@ std::string ISO639FromPosixLocale(const std::string& locale) {
}
void AppendLanguageInfoToList(base::ListValue* list,
- const LanguageModel::LanguageInfo& info) {
+ const UrlLanguageHistogram::LanguageInfo& info) {
auto lang = base::MakeUnique<base::DictionaryValue>();
lang->SetString("language", info.language_code);
lang->SetDouble("frequency", info.frequency);
@@ -216,7 +216,7 @@ void JsonRequest::OnJsonError(const std::string& error) {
/*error_details=*/base::StringPrintf(" (error %s)", error.c_str()));
}
-JsonRequest::Builder::Builder() : language_model_(nullptr) {}
+JsonRequest::Builder::Builder() : language_histogram_(nullptr) {}
JsonRequest::Builder::Builder(JsonRequest::Builder&&) = default;
JsonRequest::Builder::~Builder() = default;
@@ -246,9 +246,9 @@ JsonRequest::Builder& JsonRequest::Builder::SetAuthentication(
return *this;
}
-JsonRequest::Builder& JsonRequest::Builder::SetLanguageModel(
- const translate::LanguageModel* language_model) {
- language_model_ = language_model;
+JsonRequest::Builder& JsonRequest::Builder::SetLanguageHistogram(
+ const language::UrlLanguageHistogram* language_histogram) {
+ language_histogram_ = language_histogram;
return *this;
}
@@ -326,8 +326,8 @@ std::string JsonRequest::Builder::BuildBody() const {
request->SetString("userActivenessClass", user_class_);
}
- translate::LanguageModel::LanguageInfo ui_language;
- translate::LanguageModel::LanguageInfo other_top_language;
+ language::UrlLanguageHistogram::LanguageInfo ui_language;
+ language::UrlLanguageHistogram::LanguageInfo other_top_language;
PrepareLanguages(&ui_language, &other_top_language);
if (ui_language.frequency != 0 || other_top_language.frequency != 0) {
auto language_list = base::MakeUnique<base::ListValue>();
@@ -366,8 +366,8 @@ std::unique_ptr<net::URLFetcher> JsonRequest::Builder::BuildURLFetcher(
"request."
data:
"The Chromium UI language, as well as a second language the user "
- "understands, based on translate::LanguageModel. For signed-in "
- "users, the requests is authenticated."
+ "understands, based on language::UrlLanguageHistogram. For "
+ "signed-in users, the requests is authenticated."
destination: GOOGLE_OWNED_SERVICE
}
policy {
@@ -402,12 +402,12 @@ std::unique_ptr<net::URLFetcher> JsonRequest::Builder::BuildURLFetcher(
}
void JsonRequest::Builder::PrepareLanguages(
- translate::LanguageModel::LanguageInfo* ui_language,
- translate::LanguageModel::LanguageInfo* other_top_language) const {
+ language::UrlLanguageHistogram::LanguageInfo* ui_language,
+ language::UrlLanguageHistogram::LanguageInfo* other_top_language) const {
// TODO(jkrcal): Add language model factory for iOS and add fakes to tests so
- // that |language_model| is never nullptr. Remove this check and add a DCHECK
- // into the constructor.
- if (!language_model_ || !IsSendingTopLanguagesEnabled()) {
+ // that |language_histogram| is never nullptr. Remove this check and add a
+ // DCHECK into the constructor.
+ if (!language_histogram_ || !IsSendingTopLanguagesEnabled()) {
return;
}
@@ -415,11 +415,11 @@ void JsonRequest::Builder::PrepareLanguages(
ui_language->language_code = ISO639FromPosixLocale(
PosixLocaleFromBCP47Language(params_.language_code));
ui_language->frequency =
- language_model_->GetLanguageFrequency(ui_language->language_code);
+ language_histogram_->GetLanguageFrequency(ui_language->language_code);
- std::vector<LanguageModel::LanguageInfo> top_languages =
- language_model_->GetTopLanguages();
- for (const LanguageModel::LanguageInfo& info : top_languages) {
+ std::vector<UrlLanguageHistogram::LanguageInfo> top_languages =
+ language_histogram_->GetTopLanguages();
+ for (const UrlLanguageHistogram::LanguageInfo& info : top_languages) {
if (info.language_code != ui_language->language_code) {
*other_top_language = info;
diff --git a/chromium/components/ntp_snippets/remote/json_request.h b/chromium/components/ntp_snippets/remote/json_request.h
index 849df611b6b..e7770e1376a 100644
--- a/chromium/components/ntp_snippets/remote/json_request.h
+++ b/chromium/components/ntp_snippets/remote/json_request.h
@@ -13,9 +13,9 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/time.h"
+#include "components/language/core/browser/url_language_histogram.h"
#include "components/ntp_snippets/remote/request_params.h"
#include "components/ntp_snippets/status.h"
-#include "components/translate/core/browser/language_model.h"
#include "google_apis/gaia/oauth2_token_service.h"
#include "net/http/http_request_headers.h"
@@ -70,9 +70,10 @@ class JsonRequest : public net::URLFetcherDelegate {
Builder& SetAuthentication(const std::string& account_id,
const std::string& auth_header);
Builder& SetCreationTime(base::TimeTicks creation_time);
- // The language_model borrowed from the fetcher needs to stay alive until
- // the request body is built.
- Builder& SetLanguageModel(const translate::LanguageModel* language_model);
+ // The language_histogram borrowed from the fetcher needs to stay alive
+ // until the request body is built.
+ Builder& SetLanguageHistogram(
+ const language::UrlLanguageHistogram* language_histogram);
Builder& SetParams(const RequestParams& params);
Builder& SetParseJsonCallback(ParseJSONCallback callback);
// The clock borrowed from the fetcher will be injected into the
@@ -104,8 +105,8 @@ class JsonRequest : public net::URLFetcherDelegate {
const std::string& body) const;
void PrepareLanguages(
- translate::LanguageModel::LanguageInfo* ui_language,
- translate::LanguageModel::LanguageInfo* other_top_language) const;
+ language::UrlLanguageHistogram::LanguageInfo* ui_language,
+ language::UrlLanguageHistogram::LanguageInfo* other_top_language) const;
// Only required, if the request needs to be sent.
std::string auth_header_;
@@ -118,7 +119,7 @@ class JsonRequest : public net::URLFetcherDelegate {
// Optional properties.
std::string obfuscated_gaia_id_;
std::string user_class_;
- const translate::LanguageModel* language_model_;
+ const language::UrlLanguageHistogram* language_histogram_;
DISALLOW_COPY_AND_ASSIGN(Builder);
};
diff --git a/chromium/components/ntp_snippets/remote/json_request_unittest.cc b/chromium/components/ntp_snippets/remote/json_request_unittest.cc
index 3791ccd5059..0e52824c3b0 100644
--- a/chromium/components/ntp_snippets/remote/json_request_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/json_request_unittest.cc
@@ -69,20 +69,21 @@ class JsonRequestTest : public testing::Test {
clock_(mock_task_runner_->GetMockClock()),
request_context_getter_(
new net::TestURLRequestContextGetter(mock_task_runner_.get())) {
- translate::LanguageModel::RegisterProfilePrefs(pref_service_->registry());
+ language::UrlLanguageHistogram::RegisterProfilePrefs(
+ pref_service_->registry());
}
- std::unique_ptr<translate::LanguageModel> MakeLanguageModel(
+ std::unique_ptr<language::UrlLanguageHistogram> MakeLanguageHistogram(
const std::set<std::string>& codes) {
- std::unique_ptr<translate::LanguageModel> language_model =
- base::MakeUnique<translate::LanguageModel>(pref_service_.get());
+ std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
+ base::MakeUnique<language::UrlLanguageHistogram>(pref_service_.get());
// There must be at least 10 visits before the top languages are defined.
for (int i = 0; i < 10; i++) {
for (const std::string& code : codes) {
- language_model->OnPageVisited(code);
+ language_histogram->OnPageVisited(code);
}
}
- return language_model;
+ return language_histogram;
}
JsonRequest::Builder CreateMinimalBuilder() {
@@ -221,12 +222,12 @@ TEST_F(JsonRequestTest, BuildRequestNoUserClass) {
TEST_F(JsonRequestTest, BuildRequestWithTwoLanguages) {
JsonRequest::Builder builder;
- std::unique_ptr<translate::LanguageModel> language_model =
- MakeLanguageModel({"de", "en"});
+ std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
+ MakeLanguageHistogram({"de", "en"});
RequestParams params;
params.interactive_request = true;
params.language_code = "en";
- builder.SetParams(params).SetLanguageModel(language_model.get());
+ builder.SetParams(params).SetLanguageHistogram(language_histogram.get());
EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
EqualsJSON("{"
@@ -248,12 +249,12 @@ TEST_F(JsonRequestTest, BuildRequestWithTwoLanguages) {
TEST_F(JsonRequestTest, BuildRequestWithUILanguageOnly) {
JsonRequest::Builder builder;
- std::unique_ptr<translate::LanguageModel> language_model =
- MakeLanguageModel({"en"});
+ std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
+ MakeLanguageHistogram({"en"});
RequestParams params;
params.interactive_request = true;
params.language_code = "en";
- builder.SetParams(params).SetLanguageModel(language_model.get());
+ builder.SetParams(params).SetLanguageHistogram(language_histogram.get());
EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
EqualsJSON("{"
diff --git a/chromium/components/ntp_snippets/remote/json_to_categories.cc b/chromium/components/ntp_snippets/remote/json_to_categories.cc
new file mode 100644
index 00000000000..17c0c59f006
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/json_to_categories.cc
@@ -0,0 +1,135 @@
+// 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/ntp_snippets/remote/json_to_categories.h"
+
+#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+// Creates suggestions from dictionary values in |list| and adds them to
+// |suggestions|. Returns true on success, false if anything went wrong.
+bool AddSuggestionsFromListValue(int remote_category_id,
+ const base::ListValue& list,
+ RemoteSuggestion::PtrVector* suggestions,
+ const base::Time& fetch_time) {
+ for (const auto& value : list) {
+ const base::DictionaryValue* dict = nullptr;
+ if (!value.GetAsDictionary(&dict)) {
+ return false;
+ }
+
+ std::unique_ptr<RemoteSuggestion> suggestion;
+
+ suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
+ *dict, remote_category_id, fetch_time);
+
+ if (!suggestion) {
+ return false;
+ }
+
+ suggestions->push_back(std::move(suggestion));
+ }
+ return true;
+}
+
+} // namespace
+
+FetchedCategory::FetchedCategory(Category c, CategoryInfo&& info)
+ : category(c), info(info) {}
+
+FetchedCategory::FetchedCategory(FetchedCategory&&) = default;
+
+FetchedCategory::~FetchedCategory() = default;
+
+FetchedCategory& FetchedCategory::operator=(FetchedCategory&&) = default;
+
+CategoryInfo BuildArticleCategoryInfo(
+ const base::Optional<base::string16>& title) {
+ return CategoryInfo(
+ title.has_value() ? title.value()
+ : l10n_util::GetStringUTF16(
+ IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER),
+ ContentSuggestionsCardLayout::FULL_CARD,
+ ContentSuggestionsAdditionalAction::FETCH,
+ /*show_if_empty=*/true,
+ l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
+}
+
+CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
+ bool allow_fetching_more_results) {
+ ContentSuggestionsAdditionalAction action =
+ ContentSuggestionsAdditionalAction::NONE;
+ if (allow_fetching_more_results) {
+ action = ContentSuggestionsAdditionalAction::FETCH;
+ }
+ return CategoryInfo(
+ title, ContentSuggestionsCardLayout::FULL_CARD, action,
+ /*show_if_empty=*/false,
+ // TODO(tschumann): The message for no-articles is likely wrong
+ // and needs to be added to the stubby protocol if we want to
+ // support it.
+ l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
+}
+
+bool JsonToCategories(const base::Value& parsed,
+ FetchedCategoriesVector* categories,
+ const base::Time& fetch_time) {
+ const base::DictionaryValue* top_dict = nullptr;
+ if (!parsed.GetAsDictionary(&top_dict)) {
+ return false;
+ }
+
+ const base::ListValue* categories_value = nullptr;
+ if (!top_dict->GetList("categories", &categories_value)) {
+ return false;
+ }
+
+ for (const auto& v : *categories_value) {
+ std::string utf8_title;
+ int remote_category_id = -1;
+ const base::DictionaryValue* category_value = nullptr;
+ if (!(v.GetAsDictionary(&category_value) &&
+ category_value->GetString("localizedTitle", &utf8_title) &&
+ category_value->GetInteger("id", &remote_category_id) &&
+ (remote_category_id > 0))) {
+ return false;
+ }
+
+ RemoteSuggestion::PtrVector suggestions;
+ const base::ListValue* suggestions_list = nullptr;
+ // Absence of a list of suggestions is treated as an empty list, which
+ // is permissible.
+ if (category_value->GetList("suggestions", &suggestions_list)) {
+ if (!AddSuggestionsFromListValue(remote_category_id, *suggestions_list,
+ &suggestions, fetch_time)) {
+ return false;
+ }
+ }
+ Category category = Category::FromRemoteCategory(remote_category_id);
+ if (category.IsKnownCategory(KnownCategories::ARTICLES)) {
+ categories->push_back(FetchedCategory(
+ category, BuildArticleCategoryInfo(base::UTF8ToUTF16(utf8_title))));
+ } else {
+ // TODO(tschumann): Right now, the backend does not yet populate this
+ // field. Make it mandatory once the backends provide it.
+ bool allow_fetching_more_results = false;
+ category_value->GetBoolean("allowFetchingMoreResults",
+ &allow_fetching_more_results);
+ categories->push_back(FetchedCategory(
+ category, BuildRemoteCategoryInfo(base::UTF8ToUTF16(utf8_title),
+ allow_fetching_more_results)));
+ }
+ categories->back().suggestions = std::move(suggestions);
+ }
+
+ return true;
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/json_to_categories.h b/chromium/components/ntp_snippets/remote/json_to_categories.h
new file mode 100644
index 00000000000..66eb80958fc
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/json_to_categories.h
@@ -0,0 +1,45 @@
+// 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_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_HELPER_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_HELPER_H_
+
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_info.h"
+#include "components/ntp_snippets/remote/remote_suggestion.h"
+
+namespace ntp_snippets {
+
+struct FetchedCategory {
+ Category category;
+ CategoryInfo info;
+ RemoteSuggestion::PtrVector suggestions;
+
+ FetchedCategory(Category c, CategoryInfo&& info);
+ FetchedCategory(FetchedCategory&&);
+ ~FetchedCategory();
+ FetchedCategory& operator=(FetchedCategory&&);
+};
+
+using FetchedCategoriesVector = std::vector<FetchedCategory>;
+
+// Provides the CategoryInfo data for article suggestions. If |title| is
+// nullopt, then the default, hard-coded title will be used.
+CategoryInfo BuildArticleCategoryInfo(
+ const base::Optional<base::string16>& title);
+
+// Provides the CategoryInfo data for other remote suggestions.
+CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
+ bool allow_fetching_more_results);
+
+bool JsonToCategories(const base::Value& parsed,
+ FetchedCategoriesVector* categories,
+ const base::Time& fetch_time);
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_HELPER_H_
diff --git a/chromium/components/ntp_snippets/remote/persistent_scheduler.h b/chromium/components/ntp_snippets/remote/persistent_scheduler.h
index 080ceb0b601..727c2adb2fb 100644
--- a/chromium/components/ntp_snippets/remote/persistent_scheduler.h
+++ b/chromium/components/ntp_snippets/remote/persistent_scheduler.h
@@ -15,13 +15,8 @@ namespace ntp_snippets {
// schedule independent of whether Chrome is running at that moment.
//
// Once per period, the concrete implementation should call
-// RemoteSuggestionsScheduler::OnFetchDue() where the scheduler object is
-// obtained from ContentSuggestionsService.
-//
-// The implementation may also call
-// RemoteSuggestionsScheduler::RescheduleFetching() when its own current
-// schedule got corrupted for whatever reason and needs to be applied again
-// (in turn, this will result in calling Schedule() on the implementation).
+// RemoteSuggestionsScheduler::OnPersistentSchedulerWakeUp() where the scheduler
+// object is obtained from ContentSuggestionsService.
class PersistentScheduler {
public:
// Schedule periodic fetching of remote suggestions, with different periods
diff --git a/chromium/components/ntp_snippets/remote/prefetched_pages_tracker.h b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker.h
new file mode 100644
index 00000000000..b81e78c1677
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker.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_NTP_SNIPPETS_REMOTE_PREFETCHED_PAGES_TRACKER_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_PREFETCHED_PAGES_TRACKER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "url/gurl.h"
+
+namespace ntp_snippets {
+
+// Synchronously answers whether there is a prefetched offline page for a given
+// URL.
+class PrefetchedPagesTracker {
+ public:
+ virtual ~PrefetchedPagesTracker() = default;
+
+ // Whether the tracker has finished initialization.
+ virtual bool IsInitialized() const = 0;
+
+ // Add a callback, which will be called when the initialization is completed.
+ // If the tracker has been initialized already, the callback is called
+ // immediately.
+ virtual void AddInitializationCompletedCallback(
+ base::OnceCallback<void()> callback) = 0;
+
+ virtual bool PrefetchedOfflinePageExists(const GURL& url) const = 0;
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_PREFETCHED_PAGES_TRACKER_H_
diff --git a/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc
new file mode 100644
index 00000000000..a595715a321
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.cc
@@ -0,0 +1,121 @@
+// 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/ntp_snippets/remote/prefetched_pages_tracker_impl.h"
+
+#include "base/bind.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+
+using offline_pages::OfflinePageItem;
+using offline_pages::OfflinePageModel;
+using offline_pages::OfflinePageModelQuery;
+using offline_pages::OfflinePageModelQueryBuilder;
+
+namespace ntp_snippets {
+
+namespace {
+
+std::unique_ptr<OfflinePageModelQuery> BuildPrefetchedPagesQuery(
+ OfflinePageModel* model) {
+ OfflinePageModelQueryBuilder builder;
+ builder.RequireNamespace(offline_pages::kSuggestedArticlesNamespace);
+ return builder.Build(model->GetPolicyController());
+}
+
+bool IsOfflineItemPrefetchedPage(const OfflinePageItem& offline_page_item) {
+ return offline_page_item.client_id.name_space ==
+ offline_pages::kSuggestedArticlesNamespace;
+}
+
+const GURL& GetOfflinePageUrl(const OfflinePageItem& offline_page_item) {
+ return offline_page_item.original_url != GURL()
+ ? offline_page_item.original_url
+ : offline_page_item.url;
+}
+
+} // namespace
+
+PrefetchedPagesTrackerImpl::PrefetchedPagesTrackerImpl(
+ OfflinePageModel* offline_page_model)
+ : initialized_(false),
+ offline_page_model_(offline_page_model),
+ weak_ptr_factory_(this) {
+ DCHECK(offline_page_model_);
+ // If Offline Page model is not loaded yet, it will process our query
+ // once it has finished loading.
+ offline_page_model_->GetPagesMatchingQuery(
+ BuildPrefetchedPagesQuery(offline_page_model),
+ base::Bind(&PrefetchedPagesTrackerImpl::Initialize,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+PrefetchedPagesTrackerImpl::~PrefetchedPagesTrackerImpl() {
+ offline_page_model_->RemoveObserver(this);
+}
+
+bool PrefetchedPagesTrackerImpl::IsInitialized() const {
+ return initialized_;
+}
+
+void PrefetchedPagesTrackerImpl::AddInitializationCompletedCallback(
+ base::OnceCallback<void()> callback) {
+ if (IsInitialized()) {
+ std::move(callback).Run();
+ }
+ initialization_completed_callbacks_.push_back(std::move(callback));
+}
+
+bool PrefetchedPagesTrackerImpl::PrefetchedOfflinePageExists(
+ const GURL& url) const {
+ DCHECK(initialized_);
+ return prefetched_urls_.count(url) == 1;
+}
+
+void PrefetchedPagesTrackerImpl::OfflinePageModelLoaded(
+ OfflinePageModel* model) {
+ // Ignored. Offline Page model delayes our requests until it is loaded.
+}
+
+void PrefetchedPagesTrackerImpl::OfflinePageAdded(
+ OfflinePageModel* model,
+ const OfflinePageItem& added_page) {
+ if (IsOfflineItemPrefetchedPage(added_page)) {
+ AddOfflinePage(added_page);
+ }
+}
+
+void PrefetchedPagesTrackerImpl::OfflinePageDeleted(
+ const offline_pages::OfflinePageModel::DeletedPageInfo& page_info) {
+ std::map<int64_t, GURL>::iterator it =
+ offline_id_to_url_mapping_.find(page_info.offline_id);
+ if (it != offline_id_to_url_mapping_.end()) {
+ DCHECK(prefetched_urls_.count(it->second));
+ prefetched_urls_.erase(it->second);
+ offline_id_to_url_mapping_.erase(it);
+ }
+}
+
+void PrefetchedPagesTrackerImpl::Initialize(
+ const std::vector<OfflinePageItem>& all_prefetched_offline_pages) {
+ for (const OfflinePageItem& item : all_prefetched_offline_pages) {
+ DCHECK(IsOfflineItemPrefetchedPage(item));
+ AddOfflinePage(item);
+ }
+
+ initialized_ = true;
+ offline_page_model_->AddObserver(this);
+ for (auto& callback : initialization_completed_callbacks_) {
+ std::move(callback).Run();
+ }
+}
+
+void PrefetchedPagesTrackerImpl::AddOfflinePage(
+ const OfflinePageItem& offline_page_item) {
+ const GURL& url = GetOfflinePageUrl(offline_page_item);
+ DCHECK(!prefetched_urls_.count(url));
+ prefetched_urls_.insert(url);
+ offline_id_to_url_mapping_[offline_page_item.offline_id] = url;
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h
new file mode 100644
index 00000000000..32be1db182e
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl.h
@@ -0,0 +1,68 @@
+// 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_NTP_SNIPPETS_REMOTE_PREFETCHED_PAGES_TRACKER_IMPL_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_PREFETCHED_PAGES_TRACKER_IMPL_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/ntp_snippets/remote/prefetched_pages_tracker.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+struct OfflinePageItem;
+}
+
+namespace ntp_snippets {
+
+// OfflinePageModel must outlive this class.
+class PrefetchedPagesTrackerImpl
+ : public PrefetchedPagesTracker,
+ public offline_pages::OfflinePageModel::Observer {
+ public:
+ PrefetchedPagesTrackerImpl(
+ offline_pages::OfflinePageModel* offline_page_model);
+ ~PrefetchedPagesTrackerImpl() override;
+
+ // PrefetchedPagesTracker implementation
+ bool IsInitialized() const override;
+ void AddInitializationCompletedCallback(
+ base::OnceCallback<void()> callback) override;
+ bool PrefetchedOfflinePageExists(const GURL& url) const override;
+
+ // OfflinePageModel::Observer implementation.
+ void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
+ void OfflinePageAdded(
+ offline_pages::OfflinePageModel* model,
+ const offline_pages::OfflinePageItem& added_page) override;
+ void OfflinePageDeleted(
+ const offline_pages::OfflinePageModel::DeletedPageInfo& page_info)
+ override;
+
+ private:
+ void Initialize(const std::vector<offline_pages::OfflinePageItem>&
+ all_prefetched_offline_pages);
+ void AddOfflinePage(const offline_pages::OfflinePageItem& offline_page_item);
+
+ bool initialized_;
+ offline_pages::OfflinePageModel* offline_page_model_;
+
+ std::set<GURL> prefetched_urls_;
+ std::map<int64_t, GURL> offline_id_to_url_mapping_;
+
+ std::vector<base::OnceCallback<void()>> initialization_completed_callbacks_;
+
+ base::WeakPtrFactory<PrefetchedPagesTrackerImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefetchedPagesTrackerImpl);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_PREFETCHED_PAGES_TRACKER_IMPL_H_
diff --git a/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl_unittest.cc b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl_unittest.cc
new file mode 100644
index 00000000000..ddbb295cb14
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/prefetched_pages_tracker_impl_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/ntp_snippets/remote/prefetched_pages_tracker_impl.h"
+
+#include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/mock_callback.h"
+#include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ntp_snippets::test::FakeOfflinePageModel;
+using offline_pages::MultipleOfflinePageItemCallback;
+using offline_pages::OfflinePageItem;
+using offline_pages::OfflinePageModelQuery;
+using testing::_;
+using testing::Eq;
+using testing::SaveArg;
+using testing::StrictMock;
+
+namespace ntp_snippets {
+
+namespace {
+
+class MockOfflinePageModel : public offline_pages::StubOfflinePageModel {
+ public:
+ ~MockOfflinePageModel() override = default;
+
+ // GMock does not support movable-only types (unique_ptr in this case).
+ // Therefore, the call is redirected to a mock method without movable-only
+ // types.
+ void GetPagesMatchingQuery(
+ std::unique_ptr<OfflinePageModelQuery> query,
+ const MultipleOfflinePageItemCallback& callback) override {
+ GetPagesMatchingQuery(query.get(), callback);
+ }
+
+ MOCK_METHOD2(GetPagesMatchingQuery,
+ void(OfflinePageModelQuery* query,
+ const MultipleOfflinePageItemCallback& callback));
+};
+
+OfflinePageItem CreateOfflinePageItem(const GURL& url,
+ const std::string& name_space) {
+ static int id = 0;
+ ++id;
+ return OfflinePageItem(
+ url, id, offline_pages::ClientId(name_space, base::IntToString(id)),
+ base::FilePath::FromUTF8Unsafe(
+ base::StringPrintf("some/folder/%d.mhtml", id)),
+ 0, base::Time::Now());
+}
+
+} // namespace
+
+class PrefetchedPagesTrackerImplTest : public ::testing::Test {
+ public:
+ PrefetchedPagesTrackerImplTest() = default;
+
+ FakeOfflinePageModel* fake_offline_page_model() {
+ return &fake_offline_page_model_;
+ }
+
+ MockOfflinePageModel* mock_offline_page_model() {
+ return &mock_offline_page_model_;
+ }
+
+ private:
+ FakeOfflinePageModel fake_offline_page_model_;
+ StrictMock<MockOfflinePageModel> mock_offline_page_model_;
+ DISALLOW_COPY_AND_ASSIGN(PrefetchedPagesTrackerImplTest);
+};
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldRetrievePrefetchedEarlierSuggestionsOnStartup) {
+ (*fake_offline_page_model()->mutable_items()) = {
+ CreateOfflinePageItem(GURL("http://prefetched.com"),
+ offline_pages::kSuggestedArticlesNamespace)};
+ PrefetchedPagesTrackerImpl tracker(fake_offline_page_model());
+
+ ASSERT_FALSE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://not_added_url.com")));
+ EXPECT_TRUE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldAddNewPrefetchedPagesWhenNotified) {
+ fake_offline_page_model()->mutable_items()->clear();
+ PrefetchedPagesTrackerImpl tracker(fake_offline_page_model());
+
+ ASSERT_FALSE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
+ tracker.OfflinePageAdded(
+ fake_offline_page_model(),
+ CreateOfflinePageItem(GURL("http://prefetched.com"),
+ offline_pages::kSuggestedArticlesNamespace));
+ EXPECT_TRUE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldIgnoreOtherTypesOfOfflinePagesWhenNotified) {
+ fake_offline_page_model()->mutable_items()->clear();
+ PrefetchedPagesTrackerImpl tracker(fake_offline_page_model());
+
+ ASSERT_FALSE(tracker.PrefetchedOfflinePageExists(
+ GURL("http://manually_downloaded.com")));
+ tracker.OfflinePageAdded(
+ fake_offline_page_model(),
+ CreateOfflinePageItem(GURL("http://manually_downloaded.com"),
+ offline_pages::kNTPSuggestionsNamespace));
+ EXPECT_FALSE(tracker.PrefetchedOfflinePageExists(
+ GURL("http://manually_downloaded.com")));
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldIgnoreOtherTypesOfOfflinePagesOnStartup) {
+ (*fake_offline_page_model()->mutable_items()) = {
+ CreateOfflinePageItem(GURL("http://manually_downloaded.com"),
+ offline_pages::kNTPSuggestionsNamespace)};
+ PrefetchedPagesTrackerImpl tracker(fake_offline_page_model());
+
+ ASSERT_FALSE(tracker.PrefetchedOfflinePageExists(
+ GURL("http://manually_downloaded.com")));
+ EXPECT_FALSE(tracker.PrefetchedOfflinePageExists(
+ GURL("http://manually_downloaded.com")));
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest, ShouldDeletePrefetchedURLWhenNotified) {
+ const OfflinePageItem item =
+ CreateOfflinePageItem(GURL("http://prefetched.com"),
+ offline_pages::kSuggestedArticlesNamespace);
+ (*fake_offline_page_model()->mutable_items()) = {item};
+ PrefetchedPagesTrackerImpl tracker(fake_offline_page_model());
+
+ ASSERT_TRUE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
+ tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo(
+ item.offline_id, item.client_id, "" /* request_origin */));
+ EXPECT_FALSE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldIgnoreDeletionOfOtherTypeOfflinePagesWhenNotified) {
+ const OfflinePageItem prefetched_item =
+ CreateOfflinePageItem(GURL("http://prefetched.com"),
+ offline_pages::kSuggestedArticlesNamespace);
+ // The URL is intentionally the same.
+ const OfflinePageItem manually_downloaded_item = CreateOfflinePageItem(
+ GURL("http://prefetched.com"), offline_pages::kNTPSuggestionsNamespace);
+ (*fake_offline_page_model()->mutable_items()) = {prefetched_item,
+ manually_downloaded_item};
+ PrefetchedPagesTrackerImpl tracker(fake_offline_page_model());
+
+ ASSERT_TRUE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
+ tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo(
+ manually_downloaded_item.offline_id, manually_downloaded_item.client_id,
+ "" /* request_origin */));
+ EXPECT_TRUE(
+ tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldReportAsNotInitializedBeforeInitialization) {
+ EXPECT_CALL(*mock_offline_page_model(), GetPagesMatchingQuery(_, _));
+ PrefetchedPagesTrackerImpl tracker(mock_offline_page_model());
+ EXPECT_FALSE(tracker.IsInitialized());
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldReportAsInitializedAfterInitialization) {
+ MultipleOfflinePageItemCallback offline_pages_callback;
+ EXPECT_CALL(*mock_offline_page_model(), GetPagesMatchingQuery(_, _))
+ .WillOnce(SaveArg<1>(&offline_pages_callback));
+ PrefetchedPagesTrackerImpl tracker(mock_offline_page_model());
+
+ ASSERT_FALSE(tracker.IsInitialized());
+ offline_pages_callback.Run(std::vector<OfflinePageItem>());
+ EXPECT_TRUE(tracker.IsInitialized());
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest, ShouldCallCallbackAfterInitialization) {
+ MultipleOfflinePageItemCallback offline_pages_callback;
+ EXPECT_CALL(*mock_offline_page_model(), GetPagesMatchingQuery(_, _))
+ .WillOnce(SaveArg<1>(&offline_pages_callback));
+ PrefetchedPagesTrackerImpl tracker(mock_offline_page_model());
+
+ base::MockCallback<base::OnceCallback<void()>>
+ mock_initialization_completed_callback;
+ tracker.AddInitializationCompletedCallback(
+ mock_initialization_completed_callback.Get());
+ EXPECT_CALL(mock_initialization_completed_callback, Run());
+ offline_pages_callback.Run(std::vector<OfflinePageItem>());
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldCallMultipleCallbacksAfterInitialization) {
+ MultipleOfflinePageItemCallback offline_pages_callback;
+ EXPECT_CALL(*mock_offline_page_model(), GetPagesMatchingQuery(_, _))
+ .WillOnce(SaveArg<1>(&offline_pages_callback));
+ PrefetchedPagesTrackerImpl tracker(mock_offline_page_model());
+
+ base::MockCallback<base::OnceCallback<void()>>
+ first_mock_initialization_completed_callback,
+ second_mock_initialization_completed_callback;
+ tracker.AddInitializationCompletedCallback(
+ first_mock_initialization_completed_callback.Get());
+ tracker.AddInitializationCompletedCallback(
+ second_mock_initialization_completed_callback.Get());
+ EXPECT_CALL(first_mock_initialization_completed_callback, Run());
+ EXPECT_CALL(second_mock_initialization_completed_callback, Run());
+ offline_pages_callback.Run(std::vector<OfflinePageItem>());
+}
+
+TEST_F(PrefetchedPagesTrackerImplTest,
+ ShouldCallCallbackImmediatelyIfAlreadyInitialiazed) {
+ MultipleOfflinePageItemCallback offline_pages_callback;
+ EXPECT_CALL(*mock_offline_page_model(), GetPagesMatchingQuery(_, _))
+ .WillOnce(SaveArg<1>(&offline_pages_callback));
+ PrefetchedPagesTrackerImpl tracker(mock_offline_page_model());
+ offline_pages_callback.Run(std::vector<OfflinePageItem>());
+
+ base::MockCallback<base::OnceCallback<void()>>
+ mock_initialization_completed_callback;
+ EXPECT_CALL(mock_initialization_completed_callback, Run());
+ tracker.AddInitializationCompletedCallback(
+ mock_initialization_completed_callback.Get());
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/proto/ntp_snippets.proto b/chromium/components/ntp_snippets/remote/proto/ntp_snippets.proto
index 823c4c1ce35..45decf8b264 100644
--- a/chromium/components/ntp_snippets/remote/proto/ntp_snippets.proto
+++ b/chromium/components/ntp_snippets/remote/proto/ntp_snippets.proto
@@ -27,6 +27,12 @@ message SnippetProto {
optional int32 remote_category_id = 10;
// The time when the snippet was fetched from the server.
optional int64 fetch_date = 11;
+
+ enum ContentType {
+ UNKNOWN = 0;
+ VIDEO = 1;
+ }
+ optional ContentType content_type = 12 [default = UNKNOWN];
}
message SnippetImageProto {
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestion.cc b/chromium/components/ntp_snippets/remote/remote_suggestion.cc
index d1159433740..fc0e4a3bcec 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestion.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestion.cc
@@ -4,14 +4,12 @@
#include "components/ntp_snippets/remote/remote_suggestion.h"
-#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/features.h"
#include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
namespace {
@@ -89,7 +87,8 @@ RemoteSuggestion::RemoteSuggestion(const std::vector<std::string>& ids,
score_(0),
is_dismissed_(false),
remote_category_id_(remote_category_id),
- should_notify_(false) {}
+ should_notify_(false),
+ content_type_(ContentType::UNKNOWN) {}
RemoteSuggestion::~RemoteSuggestion() = default;
@@ -269,6 +268,21 @@ RemoteSuggestion::CreateFromContentSuggestionsDictionary(
}
}
+ // In the JSON dictionary contentType is an optional field. The field
+ // content_type_ of the class |RemoteSuggestion| is by default initialized to
+ // ContentType::UNKNOWN.
+ std::string content_type;
+ if (dict.GetString("contentType", &content_type)) {
+ if (content_type == "VIDEO") {
+ snippet->content_type_ = ContentType::VIDEO;
+ } else {
+ // The supported values are: VIDEO, UNKNOWN. Therefore if the field is
+ // present the value has to be "UNKNOWN" here.
+ DCHECK_EQ(content_type, "UNKNOWN");
+ snippet->content_type_ = ContentType::UNKNOWN;
+ }
+ }
+
return snippet;
}
@@ -328,21 +342,9 @@ std::unique_ptr<RemoteSuggestion> RemoteSuggestion::CreateFromProto(
snippet->fetch_date_ = base::Time::FromInternalValue(proto.fetch_date());
}
- return snippet;
-}
-
-// static
-std::unique_ptr<RemoteSuggestion> RemoteSuggestion::CreateForTesting(
- const std::string& id,
- int remote_category_id,
- const GURL& url,
- const std::string& publisher_name,
- const GURL& amp_url) {
- auto snippet =
- MakeUnique(std::vector<std::string>(1, id), remote_category_id);
- snippet->url_ = url;
- snippet->publisher_name_ = publisher_name;
- snippet->amp_url_ = amp_url;
+ if (proto.content_type() == SnippetProto_ContentType_VIDEO) {
+ snippet->content_type_ = ContentType::VIDEO;
+ }
return snippet;
}
@@ -383,14 +385,17 @@ SnippetProto RemoteSuggestion::ToProto() const {
if (!fetch_date_.is_null()) {
result.set_fetch_date(fetch_date_.ToInternalValue());
}
+
+ if (content_type_ == ContentType::VIDEO) {
+ result.set_content_type(SnippetProto_ContentType_VIDEO);
+ }
return result;
}
ContentSuggestion RemoteSuggestion::ToContentSuggestion(
Category category) const {
GURL url = url_;
- bool use_amp = base::FeatureList::IsEnabled(kPreferAmpUrlsFeature) &&
- !amp_url_.is_empty();
+ bool use_amp = !amp_url_.is_empty();
if (use_amp) {
url = amp_url_;
}
@@ -412,6 +417,9 @@ ContentSuggestion RemoteSuggestion::ToContentSuggestion(
base::MakeUnique<NotificationExtra>(extra));
}
suggestion.set_fetch_date(fetch_date_);
+ if (content_type_ == ContentType::VIDEO) {
+ suggestion.set_is_video_suggestion(true);
+ }
return suggestion;
}
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestion.h b/chromium/components/ntp_snippets/remote/remote_suggestion.h
index 615c4fbe377..97bda87246d 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestion.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestion.h
@@ -31,6 +31,8 @@ class RemoteSuggestion {
public:
using PtrVector = std::vector<std::unique_ptr<RemoteSuggestion>>;
+ enum ContentType { UNKNOWN, VIDEO };
+
~RemoteSuggestion();
// Creates a RemoteSuggestion from a dictionary, as returned by Chrome Reader.
@@ -54,14 +56,6 @@ class RemoteSuggestion {
static std::unique_ptr<RemoteSuggestion> CreateFromProto(
const SnippetProto& proto);
- // TODO(treib): Make tests use the public interface and remove this.
- static std::unique_ptr<RemoteSuggestion> CreateForTesting(
- const std::string& id,
- int remote_category_id,
- const GURL& url,
- const std::string& publisher_name,
- const GURL& amp_url);
-
// Creates a protocol buffer corresponding to this suggestion, for persisting.
SnippetProto ToProto() const;
@@ -115,6 +109,8 @@ class RemoteSuggestion {
bool should_notify() const { return should_notify_; }
base::Time notification_deadline() const { return notification_deadline_; }
+ ContentType content_type() const { return content_type_; }
+
bool is_dismissed() const { return is_dismissed_; }
void set_dismissed(bool dismissed) { is_dismissed_ = dismissed; }
@@ -156,6 +152,8 @@ class RemoteSuggestion {
bool should_notify_;
base::Time notification_deadline_;
+ ContentType content_type_;
+
// The time when the remote suggestion was fetched from the server.
base::Time fetch_date_;
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestion_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestion_unittest.cc
index e68e1065db3..bc79f14dddd 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestion_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestion_unittest.cc
@@ -400,6 +400,7 @@ TEST(RemoteSuggestionTest, CreateFromProtoToProtoRoundtrip) {
proto.set_dismissed(false);
proto.set_remote_category_id(1);
proto.set_fetch_date(1476364691);
+ proto.set_content_type(SnippetProto_ContentType_VIDEO);
auto* source = proto.add_sources();
source->set_url("http://cool-suggestions.com/");
source->set_publisher_name("Great Suggestions Inc.");
@@ -565,5 +566,40 @@ TEST(RemoteSuggestionTest, ToContentSuggestionWithNotificationInfo) {
Eq(1467291697000));
}
+TEST(RemoteSuggestionTest, ToContentSuggestionWithContentTypeVideo) {
+ auto json = ContentSuggestionSnippet();
+ json->SetString("contentType", "VIDEO");
+ auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
+ *json, 0, base::Time());
+ ASSERT_THAT(snippet, NotNull());
+ ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
+ Category::FromKnownCategory(KnownCategories::ARTICLES));
+
+ EXPECT_THAT(content_suggestion.is_video_suggestion(), Eq(true));
+}
+
+TEST(RemoteSuggestionTest, ToContentSuggestionWithContentTypeUnknown) {
+ auto json = ContentSuggestionSnippet();
+ json->SetString("contentType", "UNKNOWN");
+ auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
+ *json, 0, base::Time());
+ ASSERT_THAT(snippet, NotNull());
+ ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
+ Category::FromKnownCategory(KnownCategories::ARTICLES));
+
+ EXPECT_THAT(content_suggestion.is_video_suggestion(), Eq(false));
+}
+
+TEST(RemoteSuggestionTest, ToContentSuggestionWithMissingContentType) {
+ auto json = ContentSuggestionSnippet();
+ auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
+ *json, 0, base::Time());
+ ASSERT_THAT(snippet, NotNull());
+ ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
+ Category::FromKnownCategory(KnownCategories::ARTICLES));
+
+ EXPECT_THAT(content_suggestion.is_video_suggestion(), Eq(false));
+}
+
} // namespace
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_database.h b/chromium/components/ntp_snippets/remote/remote_suggestions_database.h
index 648b124d768..34896ee5f73 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_database.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_database.h
@@ -28,6 +28,7 @@ namespace ntp_snippets {
class SnippetImageProto;
class SnippetProto;
+// TODO(gaschler): implement a Fake version for testing
class RemoteSuggestionsDatabase {
public:
using SnippetsCallback = base::Callback<void(RemoteSuggestion::PtrVector)>;
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc
index fd3e732767a..df46935a19b 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc
@@ -41,13 +41,20 @@ bool operator==(const RemoteSuggestion& lhs, const RemoteSuggestion& rhs) {
namespace {
std::unique_ptr<RemoteSuggestion> CreateTestSuggestion() {
- return RemoteSuggestion::CreateForTesting(
- "http://localhost", kArticlesRemoteId, GURL("http://localhost"),
- "Publisher", GURL("http://amp"));
+ SnippetProto proto;
+ proto.add_ids("http://localhost");
+ proto.set_remote_category_id(1); // Articles
+ auto* source = proto.add_sources();
+ source->set_url("http://localhost");
+ source->set_publisher_name("Publisher");
+ source->set_amp_url("http://amp");
+ return RemoteSuggestion::CreateFromProto(proto);
}
-MATCHER_P(SnippetEq, snippet, "") {
- return *arg == *snippet;
+// Eq matcher has to store the expected value, but RemoteSuggestion is movable-
+// only.
+MATCHER_P(PointeeEq, ptr_to_expected, "") {
+ return *arg == *ptr_to_expected;
}
} // namespace
@@ -159,7 +166,7 @@ TEST_F(RemoteSuggestionsDatabaseTest, Save) {
// Make sure they're there.
EXPECT_CALL(*this,
- OnSnippetsLoadedImpl(ElementsAre(SnippetEq(snippet.get()))));
+ OnSnippetsLoadedImpl(ElementsAre(PointeeEq(snippet.get()))));
db()->LoadSnippets(
base::Bind(&RemoteSuggestionsDatabaseTest::OnSnippetsLoaded,
base::Unretained(this)));
@@ -191,7 +198,7 @@ TEST_F(RemoteSuggestionsDatabaseTest, SavePersist) {
CreateDatabase();
EXPECT_CALL(*this,
- OnSnippetsLoadedImpl(ElementsAre(SnippetEq(snippet.get()))));
+ OnSnippetsLoadedImpl(ElementsAre(PointeeEq(snippet.get()))));
db()->LoadSnippets(
base::Bind(&RemoteSuggestionsDatabaseTest::OnSnippetsLoaded,
base::Unretained(this)));
@@ -218,7 +225,7 @@ TEST_F(RemoteSuggestionsDatabaseTest, Update) {
// Make sure we get the updated version.
EXPECT_CALL(*this,
- OnSnippetsLoadedImpl(ElementsAre(SnippetEq(snippet.get()))));
+ OnSnippetsLoadedImpl(ElementsAre(PointeeEq(snippet.get()))));
db()->LoadSnippets(
base::Bind(&RemoteSuggestionsDatabaseTest::OnSnippetsLoaded,
base::Unretained(this)));
@@ -237,7 +244,7 @@ TEST_F(RemoteSuggestionsDatabaseTest, Delete) {
// Make sure it's there.
EXPECT_CALL(*this,
- OnSnippetsLoadedImpl(ElementsAre(SnippetEq(snippet.get()))));
+ OnSnippetsLoadedImpl(ElementsAre(PointeeEq(snippet.get()))));
db()->LoadSnippets(
base::Bind(&RemoteSuggestionsDatabaseTest::OnSnippetsLoaded,
base::Unretained(this)));
@@ -271,7 +278,7 @@ TEST_F(RemoteSuggestionsDatabaseTest, DeleteSnippetDoesNotDeleteImage) {
// Make sure they're there.
EXPECT_CALL(*this,
- OnSnippetsLoadedImpl(ElementsAre(SnippetEq(snippet.get()))));
+ OnSnippetsLoadedImpl(ElementsAre(PointeeEq(snippet.get()))));
db()->LoadSnippets(
base::Bind(&RemoteSuggestionsDatabaseTest::OnSnippetsLoaded,
base::Unretained(this)));
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
index b07a116dfe3..183519908a0 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
@@ -4,172 +4,19 @@
#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
-#include <cstdlib>
-#include <utility>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/path_service.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/default_clock.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/features.h"
#include "components/ntp_snippets/ntp_snippets_constants.h"
-#include "components/ntp_snippets/remote/request_params.h"
-#include "components/ntp_snippets/user_classifier.h"
-#include "components/signin/core/browser/access_token_fetcher.h"
-#include "components/signin/core/browser/signin_manager.h"
-#include "components/signin/core/browser/signin_manager_base.h"
#include "components/strings/grit/components_strings.h"
#include "components/variations/variations_associated_data.h"
-#include "net/url_request/url_fetcher.h"
#include "ui/base/l10n/l10n_util.h"
-using net::URLFetcher;
-using net::URLRequestContextGetter;
-using net::HttpRequestHeaders;
-using net::URLRequestStatus;
-using translate::LanguageModel;
-
namespace ntp_snippets {
-using internal::JsonRequest;
-using internal::FetchResult;
-
namespace {
-const char kContentSuggestionsApiScope[] =
- "https://www.googleapis.com/auth/chrome-content-suggestions";
-const char kSnippetsServerNonAuthorizedFormat[] = "%s?key=%s";
-const char kAuthorizationRequestHeaderFormat[] = "Bearer %s";
-
// Variation parameter for chrome-content-suggestions backend.
const char kContentSuggestionsBackend[] = "content_suggestions_backend";
-const int kFetchTimeHistogramResolution = 5;
-
-std::string FetchResultToString(FetchResult result) {
- switch (result) {
- case FetchResult::SUCCESS:
- return "OK";
- case FetchResult::URL_REQUEST_STATUS_ERROR:
- return "URLRequestStatus error";
- case FetchResult::HTTP_ERROR:
- return "HTTP error";
- case FetchResult::JSON_PARSE_ERROR:
- return "Received invalid JSON";
- case FetchResult::INVALID_SNIPPET_CONTENT_ERROR:
- return "Invalid / empty list.";
- case FetchResult::OAUTH_TOKEN_ERROR:
- return "Error in obtaining an OAuth2 access token.";
- case FetchResult::MISSING_API_KEY:
- return "No API key available.";
- case FetchResult::RESULT_MAX:
- break;
- }
- NOTREACHED();
- return "Unknown error";
-}
-
-Status FetchResultToStatus(FetchResult result) {
- switch (result) {
- case FetchResult::SUCCESS:
- return Status::Success();
- // Permanent errors occur if it is more likely that the error originated
- // from the client.
- case FetchResult::OAUTH_TOKEN_ERROR:
- case FetchResult::MISSING_API_KEY:
- return Status(StatusCode::PERMANENT_ERROR, FetchResultToString(result));
- // Temporary errors occur if it's more likely that the client behaved
- // 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::URL_REQUEST_STATUS_ERROR:
- case FetchResult::INVALID_SNIPPET_CONTENT_ERROR:
- case FetchResult::JSON_PARSE_ERROR:
- return Status(StatusCode::TEMPORARY_ERROR, FetchResultToString(result));
- case FetchResult::RESULT_MAX:
- break;
- }
- NOTREACHED();
- return Status(StatusCode::PERMANENT_ERROR, std::string());
-}
-
-// Creates suggestions from dictionary values in |list| and adds them to
-// |suggestions|. Returns true on success, false if anything went wrong.
-// |remote_category_id| is only used if |content_suggestions_api| is true.
-bool AddSuggestionsFromListValue(bool content_suggestions_api,
- int remote_category_id,
- const base::ListValue& list,
- RemoteSuggestion::PtrVector* suggestions,
- const base::Time& fetch_time) {
- for (const auto& value : list) {
- const base::DictionaryValue* dict = nullptr;
- if (!value.GetAsDictionary(&dict)) {
- return false;
- }
-
- std::unique_ptr<RemoteSuggestion> suggestion;
- if (content_suggestions_api) {
- suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- *dict, remote_category_id, fetch_time);
- } else {
- suggestion =
- RemoteSuggestion::CreateFromChromeReaderDictionary(*dict, fetch_time);
- }
- if (!suggestion) {
- return false;
- }
-
- suggestions->push_back(std::move(suggestion));
- }
- return true;
-}
-
-int GetMinuteOfTheDay(bool local_time,
- bool reduced_resolution,
- base::Clock* clock) {
- base::Time now(clock->Now());
- base::Time::Exploded now_exploded{};
- local_time ? now.LocalExplode(&now_exploded) : now.UTCExplode(&now_exploded);
- int now_minute = reduced_resolution
- ? now_exploded.minute / kFetchTimeHistogramResolution *
- kFetchTimeHistogramResolution
- : now_exploded.minute;
- return now_exploded.hour * 60 + now_minute;
-}
-
-// The response from the backend might include suggestions from multiple
-// categories. If only a single category was requested, this function filters
-// all other categories out.
-void FilterCategories(
- RemoteSuggestionsFetcher::FetchedCategoriesVector* categories,
- base::Optional<Category> exclusive_category) {
- if (!exclusive_category.has_value()) {
- return;
- }
- Category exclusive = exclusive_category.value();
- auto category_it = std::find_if(
- categories->begin(), categories->end(),
- [&exclusive](const RemoteSuggestionsFetcher::FetchedCategory& c) -> bool {
- return c.category == exclusive;
- });
- if (category_it == categories->end()) {
- categories->clear();
- return;
- }
- RemoteSuggestionsFetcher::FetchedCategory category = std::move(*category_it);
- categories->clear();
- categories->push_back(std::move(category));
-}
-
} // namespace
GURL GetFetchEndpoint(version_info::Channel channel) {
@@ -193,307 +40,6 @@ GURL GetFetchEndpoint(version_info::Channel channel) {
return GURL{kContentSuggestionsStagingServer};
}
-CategoryInfo BuildArticleCategoryInfo(
- const base::Optional<base::string16>& title) {
- return CategoryInfo(
- title.has_value() ? title.value()
- : l10n_util::GetStringUTF16(
- IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER),
- ContentSuggestionsCardLayout::FULL_CARD,
- ContentSuggestionsAdditionalAction::FETCH,
- /*show_if_empty=*/true,
- l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
-}
-
-CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
- bool allow_fetching_more_results) {
- ContentSuggestionsAdditionalAction action =
- ContentSuggestionsAdditionalAction::NONE;
- if (allow_fetching_more_results) {
- action = ContentSuggestionsAdditionalAction::FETCH;
- }
- return CategoryInfo(
- title, ContentSuggestionsCardLayout::FULL_CARD, action,
- /*show_if_empty=*/false,
- // TODO(tschumann): The message for no-articles is likely wrong
- // and needs to be added to the stubby protocol if we want to
- // support it.
- l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
-}
-
-RemoteSuggestionsFetcher::FetchedCategory::FetchedCategory(Category c,
- CategoryInfo&& info)
- : category(c), info(info) {}
-
-RemoteSuggestionsFetcher::FetchedCategory::FetchedCategory(FetchedCategory&&) =
- default;
-
-RemoteSuggestionsFetcher::FetchedCategory::~FetchedCategory() = default;
-
-RemoteSuggestionsFetcher::FetchedCategory&
-RemoteSuggestionsFetcher::FetchedCategory::operator=(FetchedCategory&&) =
- default;
-
-RemoteSuggestionsFetcher::RemoteSuggestionsFetcher(
- SigninManagerBase* signin_manager,
- OAuth2TokenService* token_service,
- scoped_refptr<URLRequestContextGetter> url_request_context_getter,
- PrefService* pref_service,
- LanguageModel* language_model,
- const ParseJSONCallback& parse_json_callback,
- const GURL& api_endpoint,
- const std::string& api_key,
- const UserClassifier* user_classifier)
- : signin_manager_(signin_manager),
- token_service_(token_service),
- url_request_context_getter_(std::move(url_request_context_getter)),
- language_model_(language_model),
- parse_json_callback_(parse_json_callback),
- fetch_url_(api_endpoint),
- api_key_(api_key),
- clock_(new base::DefaultClock()),
- user_classifier_(user_classifier) {}
-
RemoteSuggestionsFetcher::~RemoteSuggestionsFetcher() = default;
-void RemoteSuggestionsFetcher::FetchSnippets(
- const RequestParams& params,
- SnippetsAvailableCallback callback) {
- if (!params.interactive_request) {
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "NewTabPage.Snippets.FetchTimeLocal",
- GetMinuteOfTheDay(/*local_time=*/true,
- /*reduced_resolution=*/true, clock_.get()));
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "NewTabPage.Snippets.FetchTimeUTC",
- GetMinuteOfTheDay(/*local_time=*/false,
- /*reduced_resolution=*/true, clock_.get()));
- }
-
- JsonRequest::Builder builder;
- builder.SetLanguageModel(language_model_)
- .SetParams(params)
- .SetParseJsonCallback(parse_json_callback_)
- .SetClock(clock_.get())
- .SetUrlRequestContextGetter(url_request_context_getter_)
- .SetUserClassifier(*user_classifier_);
-
- if (signin_manager_->IsAuthenticated() || signin_manager_->AuthInProgress()) {
- // Signed-in: get OAuth token --> fetch suggestions.
- pending_requests_.emplace(std::move(builder), std::move(callback));
- StartTokenRequest();
- } else {
- // Not signed in: fetch suggestions (without authentication).
- FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback));
- }
-}
-
-void RemoteSuggestionsFetcher::FetchSnippetsNonAuthenticated(
- JsonRequest::Builder builder,
- SnippetsAvailableCallback callback) {
- if (api_key_.empty()) {
- // If we don't have an API key, don't even try.
- FetchFinished(OptionalFetchedCategories(), std::move(callback),
- FetchResult::MISSING_API_KEY, std::string());
- return;
- }
- // When not providing OAuth token, we need to pass the Google API key.
- builder.SetUrl(
- GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat,
- fetch_url_.spec().c_str(), api_key_.c_str())));
- StartRequest(std::move(builder), std::move(callback));
-}
-
-void RemoteSuggestionsFetcher::FetchSnippetsAuthenticated(
- JsonRequest::Builder builder,
- SnippetsAvailableCallback callback,
- const std::string& oauth_access_token) {
- // TODO(jkrcal, treib): Add unit-tests for authenticated fetches.
- builder.SetUrl(fetch_url_)
- .SetAuthentication(signin_manager_->GetAuthenticatedAccountId(),
- base::StringPrintf(kAuthorizationRequestHeaderFormat,
- oauth_access_token.c_str()));
- StartRequest(std::move(builder), std::move(callback));
-}
-
-void RemoteSuggestionsFetcher::StartRequest(
- JsonRequest::Builder builder,
- SnippetsAvailableCallback callback) {
- std::unique_ptr<JsonRequest> request = builder.Build();
- JsonRequest* raw_request = request.get();
- raw_request->Start(base::BindOnce(&RemoteSuggestionsFetcher::JsonRequestDone,
- base::Unretained(this), std::move(request),
- std::move(callback)));
-}
-
-void RemoteSuggestionsFetcher::StartTokenRequest() {
- // If there is already an ongoing token request, just wait for that.
- if (token_fetcher_) {
- return;
- }
-
- OAuth2TokenService::ScopeSet scopes{kContentSuggestionsApiScope};
- token_fetcher_ = base::MakeUnique<AccessTokenFetcher>(
- "ntp_snippets", signin_manager_, token_service_, scopes,
- base::BindOnce(&RemoteSuggestionsFetcher::AccessTokenFetchFinished,
- base::Unretained(this)));
-}
-
-void RemoteSuggestionsFetcher::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).
- DCHECK(token_fetcher_);
- std::unique_ptr<AccessTokenFetcher> token_fetcher_deleter(
- std::move(token_fetcher_));
-
- if (error.state() != GoogleServiceAuthError::NONE) {
- AccessTokenError(error);
- return;
- }
-
- DCHECK(!access_token.empty());
-
- while (!pending_requests_.empty()) {
- std::pair<JsonRequest::Builder, SnippetsAvailableCallback>
- builder_and_callback = std::move(pending_requests_.front());
- pending_requests_.pop();
- FetchSnippetsAuthenticated(std::move(builder_and_callback.first),
- std::move(builder_and_callback.second),
- access_token);
- }
-}
-
-void RemoteSuggestionsFetcher::AccessTokenError(
- const GoogleServiceAuthError& error) {
- DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
-
- DLOG(ERROR) << "Unable to get token: " << error.ToString();
-
- while (!pending_requests_.empty()) {
- std::pair<JsonRequest::Builder, SnippetsAvailableCallback>
- builder_and_callback = std::move(pending_requests_.front());
-
- FetchFinished(OptionalFetchedCategories(),
- std::move(builder_and_callback.second),
- FetchResult::OAUTH_TOKEN_ERROR,
- /*error_details=*/
- base::StringPrintf(" (%s)", error.ToString().c_str()));
- pending_requests_.pop();
- }
-}
-
-void RemoteSuggestionsFetcher::JsonRequestDone(
- std::unique_ptr<JsonRequest> request,
- SnippetsAvailableCallback callback,
- std::unique_ptr<base::Value> result,
- FetchResult status_code,
- const std::string& error_details) {
- DCHECK(request);
- // Record the time when request for fetching remote content snippets finished.
- const base::Time fetch_time = clock_->Now();
-
- last_fetch_json_ = request->GetResponseString();
-
- UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime",
- request->GetFetchDuration());
-
- if (!result) {
- FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code,
- error_details);
- return;
- }
-
- FetchedCategoriesVector categories;
- if (!JsonToSnippets(*result, &categories, fetch_time)) {
- LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_;
- FetchFinished(OptionalFetchedCategories(), std::move(callback),
- FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string());
- return;
- }
- // Filter out unwanted categories if necessary.
- // TODO(fhorschig): As soon as the server supports filtering by category,
- // adjust the request instead of over-fetching and filtering here.
- FilterCategories(&categories, request->exclusive_category());
-
- FetchFinished(std::move(categories), std::move(callback),
- FetchResult::SUCCESS, std::string());
-}
-
-void RemoteSuggestionsFetcher::FetchFinished(
- OptionalFetchedCategories categories,
- SnippetsAvailableCallback callback,
- FetchResult fetch_result,
- const std::string& error_details) {
- DCHECK(fetch_result == FetchResult::SUCCESS || !categories.has_value());
-
- last_status_ = FetchResultToString(fetch_result) + error_details;
-
- UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.FetchResult",
- static_cast<int>(fetch_result),
- static_cast<int>(FetchResult::RESULT_MAX));
-
- DVLOG(1) << "Fetch finished: " << last_status_;
-
- std::move(callback).Run(FetchResultToStatus(fetch_result),
- std::move(categories));
-}
-
-bool RemoteSuggestionsFetcher::JsonToSnippets(
- const base::Value& parsed,
- FetchedCategoriesVector* categories,
- const base::Time& fetch_time) {
- const base::DictionaryValue* top_dict = nullptr;
- if (!parsed.GetAsDictionary(&top_dict)) {
- return false;
- }
-
- const base::ListValue* categories_value = nullptr;
- if (!top_dict->GetList("categories", &categories_value)) {
- return false;
- }
-
- for (const auto& v : *categories_value) {
- std::string utf8_title;
- int remote_category_id = -1;
- const base::DictionaryValue* category_value = nullptr;
- if (!(v.GetAsDictionary(&category_value) &&
- category_value->GetString("localizedTitle", &utf8_title) &&
- category_value->GetInteger("id", &remote_category_id) &&
- (remote_category_id > 0))) {
- return false;
- }
-
- RemoteSuggestion::PtrVector suggestions;
- const base::ListValue* suggestions_list = nullptr;
- // Absence of a list of suggestions is treated as an empty list, which
- // is permissible.
- if (category_value->GetList("suggestions", &suggestions_list)) {
- if (!AddSuggestionsFromListValue(
- /*content_suggestions_api=*/true, remote_category_id,
- *suggestions_list, &suggestions, fetch_time)) {
- return false;
- }
- }
- Category category = Category::FromRemoteCategory(remote_category_id);
- if (category.IsKnownCategory(KnownCategories::ARTICLES)) {
- categories->push_back(FetchedCategory(
- category, BuildArticleCategoryInfo(base::UTF8ToUTF16(utf8_title))));
- } else {
- // TODO(tschumann): Right now, the backend does not yet populate this
- // field. Make it mandatory once the backends provide it.
- bool allow_fetching_more_results = false;
- category_value->GetBoolean("allowFetchingMoreResults",
- &allow_fetching_more_results);
- categories->push_back(FetchedCategory(
- category, BuildRemoteCategoryInfo(base::UTF8ToUTF16(utf8_title),
- allow_fetching_more_results)));
- }
- categories->back().suggestions = std::move(suggestions);
- }
-
- return true;
-}
-
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h
index ffe7fd9be04..e16dec659f4 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h
@@ -5,197 +5,52 @@
#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_
#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_
-#include <memory>
-#include <queue>
#include <string>
-#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/optional.h"
-#include "base/time/clock.h"
-#include "base/time/tick_clock.h"
#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/category_info.h"
-#include "components/ntp_snippets/remote/json_request.h"
+#include "components/ntp_snippets/remote/json_to_categories.h"
#include "components/ntp_snippets/remote/remote_suggestion.h"
#include "components/ntp_snippets/remote/request_params.h"
#include "components/ntp_snippets/status.h"
-#include "components/translate/core/browser/language_model.h"
#include "components/version_info/version_info.h"
-#include "net/url_request/url_request_context_getter.h"
-
-class AccessTokenFetcher;
-class OAuth2TokenService;
-class PrefService;
-class SigninManagerBase;
-
-namespace base {
-class Value;
-} // namespace base
+#include "url/gurl.h"
namespace ntp_snippets {
-class UserClassifier;
-
// Returns the appropriate API endpoint for the fetcher, in consideration of
// the channel and variation parameters.
GURL GetFetchEndpoint(version_info::Channel channel);
-// TODO(tschumann): BuildArticleCategoryInfo() and BuildRemoteCategoryInfo()
-// don't really belong into this library. However, as the fetcher is
-// providing this data for server-defined remote sections it's a good starting
-// point. Candiates to add to such a library would be persisting categories
-// (have all category managment in one place) or turning parsed JSON into
-// FetchedCategory objects (all domain-specific logic in one place).
-
-// Provides the CategoryInfo data for article suggestions. If |title| is
-// nullopt, then the default, hard-coded title will be used.
-CategoryInfo BuildArticleCategoryInfo(
- const base::Optional<base::string16>& title);
-
-// Provides the CategoryInfo data for other remote suggestions.
-CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
- bool allow_fetching_more_results);
-
// Fetches suggestion data for the NTP from the server.
-// TODO(fhorschig): Untangle cyclic dependencies by introducing a
-// RemoteSuggestionsFetcherInterface. (Would be good for mock implementations,
-// too!)
class RemoteSuggestionsFetcher {
public:
- struct FetchedCategory {
- Category category;
- CategoryInfo info;
- RemoteSuggestion::PtrVector suggestions;
-
- FetchedCategory(Category c, CategoryInfo&& info);
- FetchedCategory(FetchedCategory&&); // = default, in .cc
- ~FetchedCategory(); // = default, in .cc
- FetchedCategory& operator=(FetchedCategory&&); // = default, in .cc
- };
- using FetchedCategoriesVector = std::vector<FetchedCategory>;
using OptionalFetchedCategories = base::Optional<FetchedCategoriesVector>;
-
using SnippetsAvailableCallback =
base::OnceCallback<void(Status status,
OptionalFetchedCategories fetched_categories)>;
- RemoteSuggestionsFetcher(
- SigninManagerBase* signin_manager,
- OAuth2TokenService* token_service,
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
- PrefService* pref_service,
- translate::LanguageModel* language_model,
- const ParseJSONCallback& parse_json_callback,
- const GURL& api_endpoint,
- const std::string& api_key,
- const UserClassifier* user_classifier);
- ~RemoteSuggestionsFetcher();
+ virtual ~RemoteSuggestionsFetcher();
// Initiates a fetch from the server. When done (successfully or not), calls
// the callback.
//
// If an ongoing fetch exists, both fetches won't influence each other (i.e.
// every callback will be called exactly once).
- void FetchSnippets(const RequestParams& params,
- SnippetsAvailableCallback callback);
+ virtual void FetchSnippets(const RequestParams& params,
+ SnippetsAvailableCallback callback) = 0;
// Debug string representing the status/result of the last fetch attempt.
- const std::string& last_status() const { return last_status_; }
+ virtual const std::string& GetLastStatusForDebugging() const = 0;
// Returns the last JSON fetched from the server.
- const std::string& last_json() const { return last_fetch_json_; }
+ virtual const std::string& GetLastJsonForDebugging() const = 0;
// Returns the URL endpoint used by the fetcher.
- const GURL& fetch_url() const { return fetch_url_; }
-
- // Overrides internal clock for testing purposes.
- void SetClockForTesting(std::unique_ptr<base::Clock> clock) {
- clock_ = std::move(clock);
- }
-
- private:
- FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
- BuildRequestAuthenticated);
- FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
- BuildRequestUnauthenticated);
- FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
- BuildRequestExcludedIds);
- FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
- BuildRequestNoUserClass);
- FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
- BuildRequestWithTwoLanguages);
- FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
- BuildRequestWithUILanguageOnly);
- FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
- BuildRequestWithOtherLanguageOnly);
- friend class ChromeReaderSnippetsFetcherTest;
-
- void FetchSnippetsNonAuthenticated(internal::JsonRequest::Builder builder,
- SnippetsAvailableCallback callback);
- void FetchSnippetsAuthenticated(internal::JsonRequest::Builder builder,
- SnippetsAvailableCallback callback,
- const std::string& oauth_access_token);
- void StartRequest(internal::JsonRequest::Builder builder,
- SnippetsAvailableCallback callback);
-
- void StartTokenRequest();
-
- void AccessTokenFetchFinished(const GoogleServiceAuthError& error,
- const std::string& access_token);
- void AccessTokenError(const GoogleServiceAuthError& error);
-
- void JsonRequestDone(std::unique_ptr<internal::JsonRequest> request,
- SnippetsAvailableCallback callback,
- std::unique_ptr<base::Value> result,
- internal::FetchResult status_code,
- const std::string& error_details);
- void FetchFinished(OptionalFetchedCategories categories,
- SnippetsAvailableCallback callback,
- internal::FetchResult status_code,
- const std::string& error_details);
-
- bool JsonToSnippets(const base::Value& parsed,
- FetchedCategoriesVector* categories,
- const base::Time& fetch_time);
-
- // Authentication for signed-in users.
- SigninManagerBase* signin_manager_;
- OAuth2TokenService* token_service_;
-
- std::unique_ptr<AccessTokenFetcher> token_fetcher_;
-
- // Holds the URL request context.
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
-
- // Stores requests that wait for an access token.
- std::queue<
- std::pair<internal::JsonRequest::Builder, SnippetsAvailableCallback>>
- pending_requests_;
-
- // Weak reference, not owned.
- translate::LanguageModel* const language_model_;
-
- const ParseJSONCallback parse_json_callback_;
-
- // API endpoint for fetching suggestions.
- const GURL fetch_url_;
-
- // API key to use for non-authenticated requests.
- const std::string api_key_;
-
- // Allow for an injectable clock for testing.
- std::unique_ptr<base::Clock> clock_;
-
- // Classifier that tells us how active the user is. Not owned.
- const UserClassifier* user_classifier_;
-
- // Info on the last finished fetch.
- std::string last_status_;
- std::string last_fetch_json_;
-
- DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcher);
+ virtual const GURL& GetFetchUrlForDebugging() const = 0;
};
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc
new file mode 100644
index 00000000000..5817f94fd36
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc
@@ -0,0 +1,344 @@
+// 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/ntp_snippets/remote/remote_suggestions_fetcher_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/language/core/browser/url_language_histogram.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/ntp_snippets_constants.h"
+#include "components/ntp_snippets/user_classifier.h"
+#include "components/signin/core/browser/access_token_fetcher.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+
+using language::UrlLanguageHistogram;
+using net::HttpRequestHeaders;
+using net::URLFetcher;
+using net::URLRequestContextGetter;
+using net::URLRequestStatus;
+
+namespace ntp_snippets {
+
+using internal::FetchResult;
+using internal::JsonRequest;
+
+namespace {
+
+const char kSnippetsServerNonAuthorizedFormat[] = "%s?key=%s";
+const char kAuthorizationRequestHeaderFormat[] = "Bearer %s";
+
+const int kFetchTimeHistogramResolution = 5;
+
+std::string FetchResultToString(FetchResult result) {
+ switch (result) {
+ case FetchResult::SUCCESS:
+ return "OK";
+ case FetchResult::URL_REQUEST_STATUS_ERROR:
+ return "URLRequestStatus error";
+ case FetchResult::HTTP_ERROR:
+ return "HTTP error";
+ case FetchResult::JSON_PARSE_ERROR:
+ return "Received invalid JSON";
+ case FetchResult::INVALID_SNIPPET_CONTENT_ERROR:
+ return "Invalid / empty list.";
+ case FetchResult::OAUTH_TOKEN_ERROR:
+ return "Error in obtaining an OAuth2 access token.";
+ case FetchResult::MISSING_API_KEY:
+ return "No API key available.";
+ case FetchResult::RESULT_MAX:
+ break;
+ }
+ NOTREACHED();
+ return "Unknown error";
+}
+
+Status FetchResultToStatus(FetchResult result) {
+ switch (result) {
+ case FetchResult::SUCCESS:
+ return Status::Success();
+ // Permanent errors occur if it is more likely that the error originated
+ // from the client.
+ case FetchResult::OAUTH_TOKEN_ERROR:
+ case FetchResult::MISSING_API_KEY:
+ return Status(StatusCode::PERMANENT_ERROR, FetchResultToString(result));
+ // Temporary errors occur if it's more likely that the client behaved
+ // 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::URL_REQUEST_STATUS_ERROR:
+ case FetchResult::INVALID_SNIPPET_CONTENT_ERROR:
+ case FetchResult::JSON_PARSE_ERROR:
+ return Status(StatusCode::TEMPORARY_ERROR, FetchResultToString(result));
+ case FetchResult::RESULT_MAX:
+ break;
+ }
+ NOTREACHED();
+ return Status(StatusCode::PERMANENT_ERROR, std::string());
+}
+
+int GetMinuteOfTheDay(bool local_time,
+ bool reduced_resolution,
+ base::Clock* clock) {
+ base::Time now(clock->Now());
+ base::Time::Exploded now_exploded{};
+ local_time ? now.LocalExplode(&now_exploded) : now.UTCExplode(&now_exploded);
+ int now_minute = reduced_resolution
+ ? now_exploded.minute / kFetchTimeHistogramResolution *
+ kFetchTimeHistogramResolution
+ : now_exploded.minute;
+ return now_exploded.hour * 60 + now_minute;
+}
+
+// The response from the backend might include suggestions from multiple
+// categories. If only a single category was requested, this function filters
+// all other categories out.
+void FilterCategories(FetchedCategoriesVector* categories,
+ base::Optional<Category> exclusive_category) {
+ if (!exclusive_category.has_value()) {
+ return;
+ }
+ Category exclusive = exclusive_category.value();
+ auto category_it =
+ std::find_if(categories->begin(), categories->end(),
+ [&exclusive](const FetchedCategory& c) -> bool {
+ return c.category == exclusive;
+ });
+ if (category_it == categories->end()) {
+ categories->clear();
+ return;
+ }
+ FetchedCategory category = std::move(*category_it);
+ categories->clear();
+ categories->push_back(std::move(category));
+}
+
+} // namespace
+
+RemoteSuggestionsFetcherImpl::RemoteSuggestionsFetcherImpl(
+ SigninManagerBase* signin_manager,
+ OAuth2TokenService* token_service,
+ scoped_refptr<URLRequestContextGetter> url_request_context_getter,
+ PrefService* pref_service,
+ UrlLanguageHistogram* language_histogram,
+ const ParseJSONCallback& parse_json_callback,
+ const GURL& api_endpoint,
+ const std::string& api_key,
+ const UserClassifier* user_classifier)
+ : signin_manager_(signin_manager),
+ token_service_(token_service),
+ url_request_context_getter_(std::move(url_request_context_getter)),
+ language_histogram_(language_histogram),
+ parse_json_callback_(parse_json_callback),
+ fetch_url_(api_endpoint),
+ api_key_(api_key),
+ clock_(new base::DefaultClock()),
+ user_classifier_(user_classifier) {}
+
+RemoteSuggestionsFetcherImpl::~RemoteSuggestionsFetcherImpl() = default;
+
+const std::string& RemoteSuggestionsFetcherImpl::GetLastStatusForDebugging()
+ const {
+ return last_status_;
+}
+const std::string& RemoteSuggestionsFetcherImpl::GetLastJsonForDebugging()
+ const {
+ return last_fetch_json_;
+}
+const GURL& RemoteSuggestionsFetcherImpl::GetFetchUrlForDebugging() const {
+ return fetch_url_;
+}
+
+void RemoteSuggestionsFetcherImpl::FetchSnippets(
+ const RequestParams& params,
+ SnippetsAvailableCallback callback) {
+ if (!params.interactive_request) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "NewTabPage.Snippets.FetchTimeLocal",
+ GetMinuteOfTheDay(/*local_time=*/true,
+ /*reduced_resolution=*/true, clock_.get()));
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "NewTabPage.Snippets.FetchTimeUTC",
+ GetMinuteOfTheDay(/*local_time=*/false,
+ /*reduced_resolution=*/true, clock_.get()));
+ }
+
+ JsonRequest::Builder builder;
+ builder.SetLanguageHistogram(language_histogram_)
+ .SetParams(params)
+ .SetParseJsonCallback(parse_json_callback_)
+ .SetClock(clock_.get())
+ .SetUrlRequestContextGetter(url_request_context_getter_)
+ .SetUserClassifier(*user_classifier_);
+
+ if (signin_manager_->IsAuthenticated() || signin_manager_->AuthInProgress()) {
+ // Signed-in: get OAuth token --> fetch suggestions.
+ pending_requests_.emplace(std::move(builder), std::move(callback));
+ StartTokenRequest();
+ } else {
+ // Not signed in: fetch suggestions (without authentication).
+ FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback));
+ }
+}
+
+void RemoteSuggestionsFetcherImpl::FetchSnippetsNonAuthenticated(
+ JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback) {
+ if (api_key_.empty()) {
+ // If we don't have an API key, don't even try.
+ FetchFinished(OptionalFetchedCategories(), std::move(callback),
+ FetchResult::MISSING_API_KEY, std::string());
+ return;
+ }
+ // When not providing OAuth token, we need to pass the Google API key.
+ builder.SetUrl(
+ GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat,
+ fetch_url_.spec().c_str(), api_key_.c_str())));
+ StartRequest(std::move(builder), std::move(callback));
+}
+
+void RemoteSuggestionsFetcherImpl::FetchSnippetsAuthenticated(
+ JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback,
+ const std::string& oauth_access_token) {
+ // TODO(jkrcal, treib): Add unit-tests for authenticated fetches.
+ builder.SetUrl(fetch_url_)
+ .SetAuthentication(signin_manager_->GetAuthenticatedAccountId(),
+ base::StringPrintf(kAuthorizationRequestHeaderFormat,
+ oauth_access_token.c_str()));
+ StartRequest(std::move(builder), std::move(callback));
+}
+
+void RemoteSuggestionsFetcherImpl::StartRequest(
+ JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback) {
+ 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)));
+}
+
+void RemoteSuggestionsFetcherImpl::StartTokenRequest() {
+ // If there is already an ongoing token request, just wait for that.
+ if (token_fetcher_) {
+ return;
+ }
+
+ OAuth2TokenService::ScopeSet scopes{kContentSuggestionsApiScope};
+ token_fetcher_ = base::MakeUnique<AccessTokenFetcher>(
+ "ntp_snippets", signin_manager_, token_service_, scopes,
+ base::BindOnce(&RemoteSuggestionsFetcherImpl::AccessTokenFetchFinished,
+ base::Unretained(this)));
+}
+
+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).
+ DCHECK(token_fetcher_);
+ std::unique_ptr<AccessTokenFetcher> token_fetcher_deleter(
+ std::move(token_fetcher_));
+
+ if (error.state() != GoogleServiceAuthError::NONE) {
+ AccessTokenError(error);
+ return;
+ }
+
+ DCHECK(!access_token.empty());
+
+ while (!pending_requests_.empty()) {
+ std::pair<JsonRequest::Builder, SnippetsAvailableCallback>
+ builder_and_callback = std::move(pending_requests_.front());
+ pending_requests_.pop();
+ FetchSnippetsAuthenticated(std::move(builder_and_callback.first),
+ std::move(builder_and_callback.second),
+ access_token);
+ }
+}
+
+void RemoteSuggestionsFetcherImpl::AccessTokenError(
+ const GoogleServiceAuthError& error) {
+ DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
+
+ DLOG(ERROR) << "Unable to get token: " << error.ToString();
+
+ while (!pending_requests_.empty()) {
+ std::pair<JsonRequest::Builder, SnippetsAvailableCallback>
+ builder_and_callback = std::move(pending_requests_.front());
+
+ FetchFinished(OptionalFetchedCategories(),
+ std::move(builder_and_callback.second),
+ FetchResult::OAUTH_TOKEN_ERROR,
+ /*error_details=*/
+ base::StringPrintf(" (%s)", error.ToString().c_str()));
+ pending_requests_.pop();
+ }
+}
+
+void RemoteSuggestionsFetcherImpl::JsonRequestDone(
+ std::unique_ptr<JsonRequest> request,
+ SnippetsAvailableCallback callback,
+ std::unique_ptr<base::Value> result,
+ FetchResult status_code,
+ const std::string& error_details) {
+ DCHECK(request);
+ // Record the time when request for fetching remote content snippets finished.
+ const base::Time fetch_time = clock_->Now();
+
+ last_fetch_json_ = request->GetResponseString();
+
+ UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime",
+ request->GetFetchDuration());
+
+ if (!result) {
+ FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code,
+ error_details);
+ return;
+ }
+
+ FetchedCategoriesVector categories;
+ if (!JsonToCategories(*result, &categories, fetch_time)) {
+ LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_;
+ FetchFinished(OptionalFetchedCategories(), std::move(callback),
+ FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string());
+ return;
+ }
+ // Filter out unwanted categories if necessary.
+ // TODO(fhorschig): As soon as the server supports filtering by category,
+ // adjust the request instead of over-fetching and filtering here.
+ FilterCategories(&categories, request->exclusive_category());
+
+ FetchFinished(std::move(categories), std::move(callback),
+ FetchResult::SUCCESS, std::string());
+}
+
+void RemoteSuggestionsFetcherImpl::FetchFinished(
+ OptionalFetchedCategories categories,
+ SnippetsAvailableCallback callback,
+ FetchResult fetch_result,
+ const std::string& error_details) {
+ DCHECK(fetch_result == FetchResult::SUCCESS || !categories.has_value());
+
+ last_status_ = FetchResultToString(fetch_result) + error_details;
+
+ UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.FetchResult",
+ static_cast<int>(fetch_result),
+ static_cast<int>(FetchResult::RESULT_MAX));
+
+ DVLOG(1) << "Fetch finished: " << last_status_;
+
+ std::move(callback).Run(FetchResultToStatus(fetch_result),
+ std::move(categories));
+}
+
+} // 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
new file mode 100644
index 00000000000..770ea5067c0
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h
@@ -0,0 +1,147 @@
+// 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_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_IMPL_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_IMPL_H_
+
+#include <memory>
+#include <queue>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/time/clock.h"
+#include "components/ntp_snippets/remote/json_request.h"
+#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"
+
+class AccessTokenFetcher;
+class OAuth2TokenService;
+class PrefService;
+class SigninManagerBase;
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace language {
+class UrlLanguageHistogram;
+} // namespace language
+
+namespace ntp_snippets {
+
+class UserClassifier;
+
+class RemoteSuggestionsFetcherImpl : public RemoteSuggestionsFetcher {
+ public:
+ RemoteSuggestionsFetcherImpl(
+ SigninManagerBase* signin_manager,
+ OAuth2TokenService* token_service,
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+ PrefService* pref_service,
+ language::UrlLanguageHistogram* language_histogram,
+ const ParseJSONCallback& parse_json_callback,
+ const GURL& api_endpoint,
+ const std::string& api_key,
+ const UserClassifier* user_classifier);
+ ~RemoteSuggestionsFetcherImpl() override;
+
+ void FetchSnippets(const RequestParams& params,
+ SnippetsAvailableCallback callback) override;
+
+ const std::string& GetLastStatusForDebugging() const override;
+ const std::string& GetLastJsonForDebugging() const override;
+ const GURL& GetFetchUrlForDebugging() const override;
+
+ // Overrides internal clock for testing purposes.
+ void SetClockForTesting(std::unique_ptr<base::Clock> clock) {
+ clock_ = std::move(clock);
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
+ BuildRequestAuthenticated);
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
+ BuildRequestUnauthenticated);
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
+ BuildRequestExcludedIds);
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
+ BuildRequestNoUserClass);
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
+ BuildRequestWithTwoLanguages);
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
+ BuildRequestWithUILanguageOnly);
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
+ BuildRequestWithOtherLanguageOnly);
+ friend class ChromeReaderSnippetsFetcherTest;
+
+ void FetchSnippetsNonAuthenticated(internal::JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback);
+ void FetchSnippetsAuthenticated(internal::JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback,
+ const std::string& oauth_access_token);
+ void StartRequest(internal::JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback);
+
+ void StartTokenRequest();
+
+ void AccessTokenFetchFinished(const GoogleServiceAuthError& error,
+ const std::string& access_token);
+ void AccessTokenError(const GoogleServiceAuthError& error);
+
+ void JsonRequestDone(std::unique_ptr<internal::JsonRequest> request,
+ SnippetsAvailableCallback callback,
+ std::unique_ptr<base::Value> result,
+ internal::FetchResult status_code,
+ const std::string& error_details);
+ void FetchFinished(OptionalFetchedCategories categories,
+ SnippetsAvailableCallback callback,
+ internal::FetchResult status_code,
+ const std::string& error_details);
+
+ // Authentication for signed-in users.
+ SigninManagerBase* signin_manager_;
+ OAuth2TokenService* token_service_;
+
+ std::unique_ptr<AccessTokenFetcher> token_fetcher_;
+
+ // Holds the URL request context.
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+
+ // Stores requests that wait for an access token.
+ std::queue<
+ std::pair<internal::JsonRequest::Builder, SnippetsAvailableCallback>>
+ pending_requests_;
+
+ // Weak reference, not owned.
+ language::UrlLanguageHistogram* const language_histogram_;
+
+ const ParseJSONCallback parse_json_callback_;
+
+ // API endpoint for fetching suggestions.
+ const GURL fetch_url_;
+
+ // API key to use for non-authenticated requests.
+ const std::string api_key_;
+
+ // Allow for an injectable clock for testing.
+ std::unique_ptr<base::Clock> clock_;
+
+ // Classifier that tells us how active the user is. Not owned.
+ const UserClassifier* user_classifier_;
+
+ // Info on the last finished fetch.
+ std::string last_status_;
+ std::string last_fetch_json_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcherImpl);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_IMPL_H_
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
new file mode 100644
index 00000000000..e4c2e2eb894
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc
@@ -0,0 +1,1168 @@
+// 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/ntp_snippets/remote/remote_suggestions_fetcher_impl.h"
+
+#include <deque>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "base/test/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"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/ntp_snippets_constants.h"
+#include "components/ntp_snippets/remote/remote_suggestion.h"
+#include "components/ntp_snippets/remote/request_params.h"
+#include "components/ntp_snippets/remote/test_utils.h"
+#include "components/ntp_snippets/user_classifier.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/variations/entropy_provider.h"
+#include "components/variations/variations_params_manager.h"
+#include "google_apis/gaia/fake_oauth2_token_service_delegate.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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+using testing::_;
+using testing::AllOf;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Field;
+using testing::IsEmpty;
+using testing::Not;
+using testing::NotNull;
+using testing::Property;
+using testing::StartsWith;
+
+const char kAPIKey[] = "fakeAPIkey";
+const char kTestChromeContentSuggestionsSignedOutUrl[] =
+ "https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
+ "fetch?key=fakeAPIkey";
+const char kTestChromeContentSuggestionsSignedInUrl[] =
+ "https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/fetch";
+
+const char kTestEmail[] = "foo@bar.com";
+
+// Artificial time delay for JSON parsing.
+const int64_t kTestJsonParsingLatencyMs = 20;
+
+ACTION_P(MoveArgument1PointeeTo, ptr) {
+ *ptr = std::move(*arg1);
+}
+
+MATCHER(IsEmptyCategoriesList, "is an empty list of categories") {
+ RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
+ *arg;
+ return fetched_categories && fetched_categories->empty();
+}
+
+MATCHER(IsEmptyArticleList, "is an empty list of articles") {
+ RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
+ *arg;
+ return fetched_categories && fetched_categories->size() == 1 &&
+ fetched_categories->begin()->suggestions.empty();
+}
+
+MATCHER_P(IsSingleArticle, url, "is a list with the single article %(url)s") {
+ RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
+ *arg;
+ if (!fetched_categories) {
+ *result_listener << "got empty categories.";
+ return false;
+ }
+ if (fetched_categories->size() != 1) {
+ *result_listener << "expected single category.";
+ return false;
+ }
+ auto category = fetched_categories->begin();
+ if (category->suggestions.size() != 1) {
+ *result_listener << "expected single snippet, got: "
+ << category->suggestions.size();
+ return false;
+ }
+ if (category->suggestions[0]->url().spec() != url) {
+ *result_listener << "unexpected url, got: "
+ << category->suggestions[0]->url().spec();
+ return false;
+ }
+ return true;
+}
+
+MATCHER(IsCategoryInfoForArticles, "") {
+ if (arg.additional_action() != ContentSuggestionsAdditionalAction::FETCH) {
+ *result_listener << "missing expected FETCH action";
+ return false;
+ }
+ if (!arg.show_if_empty()) {
+ *result_listener << "missing expected show_if_empty";
+ return false;
+ }
+ return true;
+}
+
+MATCHER_P(FirstCategoryHasInfo, info_matcher, "") {
+ if (!arg->has_value() || arg->value().size() == 0) {
+ *result_listener << "No category found.";
+ }
+ return testing::ExplainMatchResult(info_matcher, arg->value().front().info,
+ result_listener);
+}
+
+class MockSnippetsAvailableCallback {
+ public:
+ // Workaround for gMock's lack of support for movable arguments.
+ void WrappedRun(
+ Status status,
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories) {
+ Run(status, &fetched_categories);
+ }
+
+ MOCK_METHOD2(Run,
+ void(Status status,
+ RemoteSuggestionsFetcher::OptionalFetchedCategories*
+ 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);
+ }
+
+ std::deque<int> fetchers_; // std::queue doesn't support std::find.
+};
+
+// 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 base::MakeUnique<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) {
+ base::JSONReader json_reader;
+ std::unique_ptr<base::Value> value = json_reader.ReadToValue(json);
+ if (value) {
+ success_callback.Run(std::move(value));
+ } else {
+ error_callback.Run(json_reader.GetErrorMessage());
+ }
+}
+
+void ParseJsonDelayed(const std::string& json,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ParseJson, json, std::move(success_callback),
+ std::move(error_callback)),
+ base::TimeDelta::FromMilliseconds(kTestJsonParsingLatencyMs));
+}
+
+} // namespace
+
+class RemoteSuggestionsFetcherImplTestBase : public testing::Test {
+ public:
+ explicit RemoteSuggestionsFetcherImplTestBase(const GURL& gurl)
+ : default_variation_params_(
+ {{"send_top_languages", "true"}, {"send_user_class", "true"}}),
+ params_manager_(ntp_snippets::kArticleSuggestionsFeature.name,
+ default_variation_params_,
+ {ntp_snippets::kArticleSuggestionsFeature.name}),
+ mock_task_runner_(new base::TestMockTimeTaskRunner()),
+ mock_task_runner_handle_(mock_task_runner_),
+ test_url_(gurl) {
+ UserClassifier::RegisterProfilePrefs(utils_.pref_service()->registry());
+ user_classifier_ = base::MakeUnique<UserClassifier>(
+ utils_.pref_service(), base::MakeUnique<base::DefaultClock>());
+ // Increase initial time such that ticks are non-zero.
+ mock_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1234));
+ ResetFetcher();
+ }
+
+ 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());
+
+ fake_token_service_ = base::MakeUnique<FakeProfileOAuth2TokenService>(
+ base::MakeUnique<FakeOAuth2TokenServiceDelegate>(
+ request_context_getter.get()));
+
+ fetcher_ = base::MakeUnique<RemoteSuggestionsFetcherImpl>(
+ utils_.fake_signin_manager(), fake_token_service_.get(),
+ std::move(request_context_getter), utils_.pref_service(), nullptr,
+ base::Bind(&ParseJsonDelayed),
+ GetFetchEndpoint(version_info::Channel::STABLE), api_key,
+ user_classifier_.get());
+
+ fetcher_->SetClockForTesting(mock_task_runner_->GetMockClock());
+ }
+
+ void SignIn() {
+#if defined(OS_CHROMEOS)
+ utils_.fake_signin_manager()->SignIn(kTestEmail);
+#else
+ utils_.fake_signin_manager()->SignIn(kTestEmail, "user", "password");
+#endif
+ }
+
+ void IssueRefreshToken() {
+ fake_token_service_->GetDelegate()->UpdateCredentials(kTestEmail, "token");
+ }
+
+ void IssueOAuth2Token() {
+ fake_token_service_->IssueAllTokensForAccount(kTestEmail, "access_token",
+ base::Time::Max());
+ }
+
+ void CancelOAuth2TokenRequests() {
+ fake_token_service_->IssueErrorForAllPendingRequestsForAccount(
+ kTestEmail, GoogleServiceAuthError(
+ GoogleServiceAuthError::State::REQUEST_CANCELED));
+ }
+
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback
+ ToSnippetsAvailableCallback(MockSnippetsAvailableCallback* callback) {
+ return base::BindOnce(&MockSnippetsAvailableCallback::WrappedRun,
+ base::Unretained(callback));
+ }
+
+ RemoteSuggestionsFetcherImpl& fetcher() { return *fetcher_; }
+ MockSnippetsAvailableCallback& mock_callback() { return mock_callback_; }
+ void FastForwardUntilNoTasksRemain() {
+ mock_task_runner_->FastForwardUntilNoTasksRemain();
+ }
+ base::HistogramTester& histogram_tester() { return histogram_tester_; }
+
+ RequestParams test_params() {
+ RequestParams result;
+ result.count_to_fetch = 1;
+ result.interactive_request = true;
+ 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;
+
+ params_manager_.ClearAllVariationParams();
+ params_manager_.SetVariationParamsWithFeatureAssociations(
+ ntp_snippets::kArticleSuggestionsFeature.name, params,
+ {ntp_snippets::kArticleSuggestionsFeature.name});
+ }
+
+ void SetFakeResponse(const std::string& response_data,
+ net::HttpStatusCode response_code,
+ net::URLRequestStatus::Status status) {
+ InitFakeURLFetcherFactory();
+ fake_url_fetcher_factory_->SetFakeResponse(test_url_, response_data,
+ response_code, status);
+ }
+
+ protected:
+ std::map<std::string, std::string> default_variation_params_;
+
+ private:
+ test::RemoteSuggestionsTestUtils utils_;
+ variations::testing::VariationParamsManager params_manager_;
+ scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
+ base::ThreadTaskRunnerHandle mock_task_runner_handle_;
+ FailingFakeURLFetcherFactory failing_url_fetcher_factory_;
+ // Initialized lazily in SetFakeResponse().
+ std::unique_ptr<net::FakeURLFetcherFactory> fake_url_fetcher_factory_;
+ std::unique_ptr<FakeProfileOAuth2TokenService> fake_token_service_;
+ std::unique_ptr<RemoteSuggestionsFetcherImpl> fetcher_;
+ std::unique_ptr<UserClassifier> user_classifier_;
+ MockSnippetsAvailableCallback mock_callback_;
+ const GURL test_url_;
+ base::HistogramTester histogram_tester_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcherImplTestBase);
+};
+
+class RemoteSuggestionsSignedOutFetcherTest
+ : public RemoteSuggestionsFetcherImplTestBase {
+ public:
+ RemoteSuggestionsSignedOutFetcherTest()
+ : RemoteSuggestionsFetcherImplTestBase(
+ GURL(kTestChromeContentSuggestionsSignedOutUrl)) {}
+};
+
+// TODO(jkrcal): Investigate whether the "authentication in progress" case can
+// ever happen (see discussion on https://codereview.chromium.org/2582573002),
+// and if so, add unit-tests for it. This will require more changes (instead of
+// FakeSigninManagerBase use FakeSigninManager which does not exist on
+// ChromeOS). crbug.com/688310
+class RemoteSuggestionsSignedInFetcherTest
+ : public RemoteSuggestionsFetcherImplTestBase {
+ public:
+ RemoteSuggestionsSignedInFetcherTest()
+ : RemoteSuggestionsFetcherImplTestBase(
+ GURL(kTestChromeContentSuggestionsSignedInUrl)) {}
+};
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldNotFetchOnCreation) {
+ // The lack of registered baked in responses would cause any fetch to fail.
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ IsEmpty());
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(), IsEmpty());
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldFetchSuccessfully) {
+ const std::string kJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foobar\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foobar\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true),
+ /*fetched_categories=*/AllOf(
+ IsSingleArticle("http://localhost/foobar"),
+ FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(), Eq("OK"));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kJsonStr));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
+ /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedInFetcherTest, ShouldFetchSuccessfully) {
+ SignIn();
+ IssueRefreshToken();
+
+ const std::string kJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foobar\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foobar\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true),
+ /*fetched_categories=*/AllOf(
+ IsSingleArticle("http://localhost/foobar"),
+ FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
+
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+
+ IssueOAuth2Token();
+ // Wait for the fake response.
+ FastForwardUntilNoTasksRemain();
+
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(), Eq("OK"));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kJsonStr));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
+ /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedInFetcherTest, ShouldRetryWhenOAuthCancelled) {
+ SignIn();
+ IssueRefreshToken();
+
+ const std::string kJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foobar\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foobar\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true),
+ /*fetched_categories=*/AllOf(
+ IsSingleArticle("http://localhost/foobar"),
+ FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
+
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+
+ CancelOAuth2TokenRequests();
+ IssueOAuth2Token();
+ // Wait for the fake response.
+ FastForwardUntilNoTasksRemain();
+
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(), Eq("OK"));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kJsonStr));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
+ /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, EmptyCategoryIsOK) {
+ const std::string kJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\""
+ "}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true),
+ /*fetched_categories=*/IsEmptyArticleList()));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(), Eq("OK"));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kJsonStr));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
+ /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ServerCategories) {
+ const std::string kJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foobar\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foobar\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}, {"
+ " \"id\": 2,"
+ " \"localizedTitle\": \"Articles for Me\","
+ " \"allowFetchingMoreResults\": true,"
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foo2\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foo2\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foo2.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true), /*fetched_categories=*/_))
+ .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+
+ ASSERT_TRUE(fetched_categories);
+ ASSERT_THAT(fetched_categories->size(), Eq(2u));
+ for (const auto& category : *fetched_categories) {
+ const auto& articles = category.suggestions;
+ if (category.category.IsKnownCategory(KnownCategories::ARTICLES)) {
+ ASSERT_THAT(articles.size(), Eq(1u));
+ EXPECT_THAT(articles[0]->url().spec(), Eq("http://localhost/foobar"));
+ EXPECT_THAT(category.info, IsCategoryInfoForArticles());
+ } else if (category.category == Category::FromRemoteCategory(2)) {
+ ASSERT_THAT(articles.size(), Eq(1u));
+ EXPECT_THAT(articles[0]->url().spec(), Eq("http://localhost/foo2"));
+ EXPECT_THAT(category.info.additional_action(),
+ Eq(ContentSuggestionsAdditionalAction::FETCH));
+ EXPECT_THAT(category.info.show_if_empty(), Eq(false));
+ } else {
+ FAIL() << "unknown category ID " << category.category.id();
+ }
+ }
+
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(), Eq("OK"));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kJsonStr));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
+ /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ SupportMissingAllowFetchingMoreResultsOption) {
+ // This tests makes sure we handle the missing option although it's required
+ // by the interface. It's just that the Service doesn't follow that
+ // requirement (yet). TODO(tschumann): remove this test once not needed
+ // anymore.
+ const std::string kJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 2,"
+ " \"localizedTitle\": \"Articles for Me\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foo2\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foo2\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foo2.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true), /*fetched_categories=*/_))
+ .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+
+ ASSERT_TRUE(fetched_categories);
+ ASSERT_THAT(fetched_categories->size(), Eq(1u));
+ EXPECT_THAT(fetched_categories->front().info.additional_action(),
+ Eq(ContentSuggestionsAdditionalAction::NONE));
+ EXPECT_THAT(fetched_categories->front().info.title(),
+ Eq(base::UTF8ToUTF16("Articles for Me")));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ExclusiveCategoryOnly) {
+ const std::string kJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foobar\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foobar\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}, {"
+ " \"id\": 2,"
+ " \"localizedTitle\": \"Articles for Me\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foo2\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foo2\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foo2.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}, {"
+ " \"id\": 3,"
+ " \"localizedTitle\": \"Articles for Anybody\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foo3\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foo3\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foo3.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true), /*fetched_categories=*/_))
+ .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
+
+ RequestParams params = test_params();
+ params.exclusive_category =
+ base::Optional<Category>(Category::FromRemoteCategory(2));
+
+ fetcher().FetchSnippets(params,
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+
+ ASSERT_TRUE(fetched_categories);
+ ASSERT_THAT(fetched_categories->size(), Eq(1u));
+ const auto& category = (*fetched_categories)[0];
+ EXPECT_THAT(category.category.id(), Eq(Category::FromRemoteCategory(2).id()));
+ ASSERT_THAT(category.suggestions.size(), Eq(1u));
+ EXPECT_THAT(category.suggestions[0]->url().spec(),
+ Eq("http://localhost/foo2"));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldNotFetchWithoutApiKey) {
+ ResetFetcherWithAPIKey(std::string());
+
+ EXPECT_CALL(
+ mock_callback(),
+ Run(Field(&Status::code, StatusCode::PERMANENT_ERROR),
+ /*fetched_categories=*/Property(
+ &base::Optional<std::vector<FetchedCategory>>::has_value, false)))
+ .Times(1);
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ Eq("No API key available."));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ IsEmpty());
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ ShouldFetchSuccessfullyEmptyList) {
+ const std::string kJsonStr = "{\"categories\": []}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true),
+ /*fetched_categories=*/IsEmptyCategoriesList()));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(), Eq("OK"));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kJsonStr));
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, 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));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ RetriesConfigurableOnNonInteractiveRequests) {
+ struct ExpectationForVariationParam {
+ std::string param_value;
+ int expected_value;
+ std::string description;
+ };
+ const std::vector<ExpectationForVariationParam> retry_config_expectation = {
+ {"", 0, "Do not retry by default"},
+ {"0", 0, "Do not retry on param value 0"},
+ {"-1", 0, "Do not retry on negative param values."},
+ {"4", 4, "Retry as set in param value."}};
+
+ RequestParams params = test_params();
+ params.interactive_request = false;
+
+ for (const auto& retry_config : retry_config_expectation) {
+ 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))
+ << retry_config.description;
+ }
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportUrlStatusError) {
+ SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND,
+ net::URLRequestStatus::FAILED);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ Eq("URLRequestStatus error -2"));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), IsEmpty());
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/-2, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ Not(IsEmpty()));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportHttpError) {
+ SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND,
+ net::URLRequestStatus::SUCCESS);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), IsEmpty());
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/404, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ Not(IsEmpty()));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportJsonError) {
+ const std::string kInvalidJsonStr = "{ \"recos\": []";
+ SetFakeResponse(/*response_data=*/kInvalidJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ StartsWith("Received invalid JSON (error "));
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kInvalidJsonStr));
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/4, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
+ /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ ShouldReportJsonErrorForEmptyResponse) {
+ SetFakeResponse(/*response_data=*/std::string(), net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), std::string());
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/4, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportInvalidListError) {
+ const std::string kJsonStr =
+ "{\"recos\": [{ \"contentInfo\": { \"foo\" : \"bar\" }}]}";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastJsonForDebugging(), Eq(kJsonStr));
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ StartsWith("Invalid / empty list"));
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ Not(IsEmpty()));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ ShouldReportInvalidListErrorForIncompleteSuggestionButValidJson) {
+ // This is valid json, but it does not represent a valid suggestion
+ // (fullPageUrl is missing).
+ const std::string kValidJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foobar\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"INVALID_fullPageUrl\" : \"http://localhost/foobar\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kValidJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ StartsWith("Invalid / empty list"));
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ Not(IsEmpty()));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ ShouldReportInvalidListErrorForInvalidTimestampButValidJson) {
+ // This is valid json, but it does not represent a valid suggestion
+ // (creationTime is invalid).
+ const std::string kValidJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"http://localhost/foobar\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"http://localhost/foobar\","
+ " \"creationTime\" : \"INVALID_2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kValidJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ StartsWith("Invalid / empty list"));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ ShouldReportInvalidListErrorForInvalidUrlButValidJson) {
+ // This is valid json, but it does not represent a valid suggestion
+ // (URL is invalid).
+ const std::string kValidJsonStr =
+ "{\"categories\" : [{"
+ " \"id\": 1,"
+ " \"localizedTitle\": \"Articles for You\","
+ " \"suggestions\" : [{"
+ " \"ids\" : [\"NOT A URL\"],"
+ " \"title\" : \"Foo Barred from Baz\","
+ " \"snippet\" : \"...\","
+ " \"fullPageUrl\" : \"NOT A URL\","
+ " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
+ " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
+ " \"attribution\" : \"Foo News\","
+ " \"imageUrl\" : \"http://localhost/foobar.jpg\","
+ " \"ampUrl\" : \"http://localhost/amp\","
+ " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
+ " }]"
+ "}]}";
+ SetFakeResponse(/*response_data=*/kValidJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ StartsWith("Invalid / empty list"));
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ ShouldReportRequestFailureAsTemporaryError) {
+ SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND,
+ net::URLRequestStatus::FAILED);
+ 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()));
+ FastForwardUntilNoTasksRemain();
+}
+
+// This test actually verifies that the test setup itself is sane, to prevent
+// hard-to-reproduce test failures.
+TEST_F(RemoteSuggestionsSignedOutFetcherTest,
+ ShouldReportHttpErrorForMissingBakedResponse) {
+ InitFakeURLFetcherFactory();
+ 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()));
+ FastForwardUntilNoTasksRemain();
+}
+
+TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldProcessConcurrentFetches) {
+ const std::string kJsonStr = "{ \"categories\": [] }";
+ SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+ EXPECT_CALL(mock_callback(),
+ Run(Property(&Status::IsSuccess, true),
+ /*fetched_categories=*/IsEmptyCategoriesList()))
+ .Times(5);
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ // More calls to FetchSnippets() do not interrupt the previous.
+ // Callback is expected to be called once each time.
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/5)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/5)));
+ EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
+ ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
+ /*count=*/5)));
+}
+
+::std::ostream& operator<<(
+ ::std::ostream& os,
+ const RemoteSuggestionsFetcher::OptionalFetchedCategories&
+ fetched_categories) {
+ if (fetched_categories) {
+ // Matchers above aren't any more precise than this, so this is sufficient
+ // for test-failure diagnostics.
+ return os << "list with " << fetched_categories->size() << " elements";
+ }
+ return os << "null";
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc
deleted file mode 100644
index e5ffb4d0821..00000000000
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc
+++ /dev/null
@@ -1,1004 +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/ntp_snippets/remote/remote_suggestions_fetcher.h"
-
-#include <deque>
-#include <map>
-#include <utility>
-#include <vector>
-
-#include "base/json/json_reader.h"
-#include "base/memory/ptr_util.h"
-#include "base/test/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"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/features.h"
-#include "components/ntp_snippets/ntp_snippets_constants.h"
-#include "components/ntp_snippets/remote/remote_suggestion.h"
-#include "components/ntp_snippets/remote/request_params.h"
-#include "components/ntp_snippets/remote/test_utils.h"
-#include "components/ntp_snippets/user_classifier.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
-#include "components/signin/core/browser/fake_signin_manager.h"
-#include "components/variations/entropy_provider.h"
-#include "components/variations/variations_params_manager.h"
-#include "google_apis/gaia/fake_oauth2_token_service_delegate.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 "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ntp_snippets {
-
-namespace {
-
-using testing::_;
-using testing::AllOf;
-using testing::ElementsAre;
-using testing::Eq;
-using testing::IsEmpty;
-using testing::Not;
-using testing::NotNull;
-using testing::StartsWith;
-
-const char kAPIKey[] = "fakeAPIkey";
-const char kTestChromeContentSuggestionsSignedOutUrl[] =
- "https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey";
-const char kTestChromeContentSuggestionsSignedInUrl[] =
- "https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/fetch";
-
-const char kTestEmail[] = "foo@bar.com";
-
-// Artificial time delay for JSON parsing.
-const int64_t kTestJsonParsingLatencyMs = 20;
-
-ACTION_P(MoveArgument1PointeeTo, ptr) {
- *ptr = std::move(*arg1);
-}
-
-MATCHER(HasValue, "") {
- return static_cast<bool>(*arg);
-}
-
-// TODO(fhorschig): When there are more helpers for the Status class, consider a
-// helpers file.
-MATCHER_P(HasCode, code, "") {
- return arg.code == code;
-}
-
-MATCHER(IsSuccess, "") {
- return arg.IsSuccess();
-}
-
-MATCHER(IsEmptyCategoriesList, "is an empty list of categories") {
- RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
- *arg;
- return fetched_categories && fetched_categories->empty();
-}
-
-MATCHER(IsEmptyArticleList, "is an empty list of articles") {
- RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
- *arg;
- return fetched_categories && fetched_categories->size() == 1 &&
- fetched_categories->begin()->suggestions.empty();
-}
-
-MATCHER_P(IsSingleArticle, url, "is a list with the single article %(url)s") {
- RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
- *arg;
- if (!fetched_categories) {
- *result_listener << "got empty categories.";
- return false;
- }
- if (fetched_categories->size() != 1) {
- *result_listener << "expected single category.";
- return false;
- }
- auto category = fetched_categories->begin();
- if (category->suggestions.size() != 1) {
- *result_listener << "expected single snippet, got: "
- << category->suggestions.size();
- return false;
- }
- if (category->suggestions[0]->url().spec() != url) {
- *result_listener << "unexpected url, got: "
- << category->suggestions[0]->url().spec();
- return false;
- }
- return true;
-}
-
-MATCHER(IsCategoryInfoForArticles, "") {
- if (arg.additional_action() != ContentSuggestionsAdditionalAction::FETCH) {
- *result_listener << "missing expected FETCH action";
- return false;
- }
- if (!arg.show_if_empty()) {
- *result_listener << "missing expected show_if_empty";
- return false;
- }
- return true;
-}
-
-MATCHER_P(FirstCategoryHasInfo, info_matcher, "") {
- if (!arg->has_value() || arg->value().size() == 0) {
- *result_listener << "No category found.";
- }
- return testing::ExplainMatchResult(info_matcher, arg->value().front().info,
- result_listener);
-}
-
-class MockSnippetsAvailableCallback {
- public:
- // Workaround for gMock's lack of support for movable arguments.
- void WrappedRun(
- Status status,
- RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories) {
- Run(status, &fetched_categories);
- }
-
- MOCK_METHOD2(Run,
- void(Status status,
- RemoteSuggestionsFetcher::OptionalFetchedCategories*
- 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);
- }
-
- std::deque<int> fetchers_; // std::queue doesn't support std::find.
-};
-
-// 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* d,
- net::NetworkTrafficAnnotationTag traffic_annotation) override {
- return base::MakeUnique<net::FakeURLFetcher>(
- url, d, /*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) {
- base::JSONReader json_reader;
- std::unique_ptr<base::Value> value = json_reader.ReadToValue(json);
- if (value) {
- success_callback.Run(std::move(value));
- } else {
- error_callback.Run(json_reader.GetErrorMessage());
- }
-}
-
-void ParseJsonDelayed(const std::string& json,
- const SuccessCallback& success_callback,
- const ErrorCallback& error_callback) {
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&ParseJson, json, std::move(success_callback),
- std::move(error_callback)),
- base::TimeDelta::FromMilliseconds(kTestJsonParsingLatencyMs));
-}
-
-} // namespace
-
-class RemoteSuggestionsFetcherTestBase : public testing::Test {
- public:
- explicit RemoteSuggestionsFetcherTestBase(const GURL& gurl)
- : default_variation_params_(
- {{"send_top_languages", "true"}, {"send_user_class", "true"}}),
- params_manager_(ntp_snippets::kArticleSuggestionsFeature.name,
- default_variation_params_,
- {ntp_snippets::kArticleSuggestionsFeature.name}),
- mock_task_runner_(new base::TestMockTimeTaskRunner()),
- mock_task_runner_handle_(mock_task_runner_),
- test_url_(gurl) {
- UserClassifier::RegisterProfilePrefs(utils_.pref_service()->registry());
- user_classifier_ = base::MakeUnique<UserClassifier>(
- utils_.pref_service(), base::MakeUnique<base::DefaultClock>());
- // Increase initial time such that ticks are non-zero.
- mock_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1234));
- ResetFetcher();
- }
-
- 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());
-
- fake_token_service_ = base::MakeUnique<FakeProfileOAuth2TokenService>(
- base::MakeUnique<FakeOAuth2TokenServiceDelegate>(
- request_context_getter.get()));
-
- fetcher_ = base::MakeUnique<RemoteSuggestionsFetcher>(
- utils_.fake_signin_manager(), fake_token_service_.get(),
- std::move(request_context_getter), utils_.pref_service(), nullptr,
- base::Bind(&ParseJsonDelayed),
- GetFetchEndpoint(version_info::Channel::STABLE), api_key,
- user_classifier_.get());
-
- fetcher_->SetClockForTesting(mock_task_runner_->GetMockClock());
- }
-
- void SignIn() { utils_.fake_signin_manager()->SignIn(kTestEmail); }
-
- void IssueRefreshToken() {
- fake_token_service_->GetDelegate()->UpdateCredentials(kTestEmail, "token");
- }
-
- void IssueOAuth2Token() {
- fake_token_service_->IssueAllTokensForAccount(kTestEmail, "access_token",
- base::Time::Max());
- }
-
- void CancelOAuth2TokenRequests() {
- fake_token_service_->IssueErrorForAllPendingRequestsForAccount(
- kTestEmail, GoogleServiceAuthError(
- GoogleServiceAuthError::State::REQUEST_CANCELED));
- }
-
- RemoteSuggestionsFetcher::SnippetsAvailableCallback
- ToSnippetsAvailableCallback(MockSnippetsAvailableCallback* callback) {
- return base::BindOnce(&MockSnippetsAvailableCallback::WrappedRun,
- base::Unretained(callback));
- }
-
- RemoteSuggestionsFetcher& fetcher() { return *fetcher_; }
- MockSnippetsAvailableCallback& mock_callback() { return mock_callback_; }
- void FastForwardUntilNoTasksRemain() {
- mock_task_runner_->FastForwardUntilNoTasksRemain();
- }
- base::HistogramTester& histogram_tester() { return histogram_tester_; }
-
- RequestParams test_params() {
- RequestParams result;
- result.count_to_fetch = 1;
- result.interactive_request = true;
- 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;
-
- params_manager_.ClearAllVariationParams();
- params_manager_.SetVariationParamsWithFeatureAssociations(
- ntp_snippets::kArticleSuggestionsFeature.name, params,
- {ntp_snippets::kArticleSuggestionsFeature.name});
- }
-
- void SetFakeResponse(const std::string& response_data,
- net::HttpStatusCode response_code,
- net::URLRequestStatus::Status status) {
- InitFakeURLFetcherFactory();
- fake_url_fetcher_factory_->SetFakeResponse(test_url_, response_data,
- response_code, status);
- }
-
- protected:
- std::map<std::string, std::string> default_variation_params_;
-
- private:
- test::RemoteSuggestionsTestUtils utils_;
- variations::testing::VariationParamsManager params_manager_;
- scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
- base::ThreadTaskRunnerHandle mock_task_runner_handle_;
- FailingFakeURLFetcherFactory failing_url_fetcher_factory_;
- // Initialized lazily in SetFakeResponse().
- std::unique_ptr<net::FakeURLFetcherFactory> fake_url_fetcher_factory_;
- std::unique_ptr<FakeProfileOAuth2TokenService> fake_token_service_;
- std::unique_ptr<RemoteSuggestionsFetcher> fetcher_;
- std::unique_ptr<UserClassifier> user_classifier_;
- MockSnippetsAvailableCallback mock_callback_;
- const GURL test_url_;
- base::HistogramTester histogram_tester_;
-
- DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcherTestBase);
-};
-
-class RemoteSuggestionsSignedOutFetcherTest
- : public RemoteSuggestionsFetcherTestBase {
- public:
- RemoteSuggestionsSignedOutFetcherTest()
- : RemoteSuggestionsFetcherTestBase(
- GURL(kTestChromeContentSuggestionsSignedOutUrl)) {}
-};
-
-// TODO(jkrcal): Investigate whether the "authentication in progress" case can
-// ever happen (see discussion on https://codereview.chromium.org/2582573002),
-// and if so, add unit-tests for it. This will require more changes (instead of
-// FakeSigninManagerBase use FakeSigninManager which does not exist on
-// ChromeOS). crbug.com/688310
-class RemoteSuggestionsSignedInFetcherTest
- : public RemoteSuggestionsFetcherTestBase {
- public:
- RemoteSuggestionsSignedInFetcherTest()
- : RemoteSuggestionsFetcherTestBase(
- GURL(kTestChromeContentSuggestionsSignedInUrl)) {}
-};
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldNotFetchOnCreation) {
- // The lack of registered baked in responses would cause any fetch to fail.
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- IsEmpty());
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- IsEmpty());
- EXPECT_THAT(fetcher().last_status(), IsEmpty());
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldFetchSuccessfully) {
- const std::string kJsonStr =
- "{\"categories\" : [{"
- " \"id\": 1,"
- " \"localizedTitle\": \"Articles for You\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foobar\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foobar\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foobar.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(),
- Run(IsSuccess(),
- AllOf(IsSingleArticle("http://localhost/foobar"),
- FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_status(), Eq("OK"));
- EXPECT_THAT(fetcher().last_json(), Eq(kJsonStr));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedInFetcherTest, ShouldFetchSuccessfully) {
- SignIn();
- IssueRefreshToken();
-
- const std::string kJsonStr =
- "{\"categories\" : [{"
- " \"id\": 1,"
- " \"localizedTitle\": \"Articles for You\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foobar\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foobar\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foobar.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(),
- Run(IsSuccess(),
- AllOf(IsSingleArticle("http://localhost/foobar"),
- FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
-
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
-
- IssueOAuth2Token();
- // Wait for the fake response.
- FastForwardUntilNoTasksRemain();
-
- EXPECT_THAT(fetcher().last_status(), Eq("OK"));
- EXPECT_THAT(fetcher().last_json(), Eq(kJsonStr));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedInFetcherTest, ShouldRetryWhenOAuthCancelled) {
- SignIn();
- IssueRefreshToken();
-
- const std::string kJsonStr =
- "{\"categories\" : [{"
- " \"id\": 1,"
- " \"localizedTitle\": \"Articles for You\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foobar\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foobar\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foobar.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(),
- Run(IsSuccess(),
- AllOf(IsSingleArticle("http://localhost/foobar"),
- FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
-
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
-
- CancelOAuth2TokenRequests();
- IssueOAuth2Token();
- // Wait for the fake response.
- FastForwardUntilNoTasksRemain();
-
- EXPECT_THAT(fetcher().last_status(), Eq("OK"));
- EXPECT_THAT(fetcher().last_json(), Eq(kJsonStr));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, EmptyCategoryIsOK) {
- const std::string kJsonStr =
- "{\"categories\" : [{"
- " \"id\": 1,"
- " \"localizedTitle\": \"Articles for You\""
- "}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(IsSuccess(), IsEmptyArticleList()));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_status(), Eq("OK"));
- EXPECT_THAT(fetcher().last_json(), Eq(kJsonStr));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ServerCategories) {
- const std::string kJsonStr =
- "{\"categories\" : [{"
- " \"id\": 1,"
- " \"localizedTitle\": \"Articles for You\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foobar\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foobar\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foobar.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}, {"
- " \"id\": 2,"
- " \"localizedTitle\": \"Articles for Me\","
- " \"allowFetchingMoreResults\": true,"
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foo2\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foo2\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foo2.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
- EXPECT_CALL(mock_callback(), Run(IsSuccess(), _))
- .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
-
- ASSERT_TRUE(fetched_categories);
- ASSERT_THAT(fetched_categories->size(), Eq(2u));
- for (const auto& category : *fetched_categories) {
- const auto& articles = category.suggestions;
- if (category.category.IsKnownCategory(KnownCategories::ARTICLES)) {
- ASSERT_THAT(articles.size(), Eq(1u));
- EXPECT_THAT(articles[0]->url().spec(), Eq("http://localhost/foobar"));
- EXPECT_THAT(category.info, IsCategoryInfoForArticles());
- } else if (category.category == Category::FromRemoteCategory(2)) {
- ASSERT_THAT(articles.size(), Eq(1u));
- EXPECT_THAT(articles[0]->url().spec(), Eq("http://localhost/foo2"));
- EXPECT_THAT(category.info.additional_action(),
- Eq(ContentSuggestionsAdditionalAction::FETCH));
- EXPECT_THAT(category.info.show_if_empty(), Eq(false));
- } else {
- FAIL() << "unknown category ID " << category.category.id();
- }
- }
-
- EXPECT_THAT(fetcher().last_status(), Eq("OK"));
- EXPECT_THAT(fetcher().last_json(), Eq(kJsonStr));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest,
- SupportMissingAllowFetchingMoreResultsOption) {
- // This tests makes sure we handle the missing option although it's required
- // by the interface. It's just that the Service doesn't follow that
- // requirement (yet). TODO(tschumann): remove this test once not needed
- // anymore.
- const std::string kJsonStr =
- "{\"categories\" : [{"
- " \"id\": 2,"
- " \"localizedTitle\": \"Articles for Me\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foo2\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foo2\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foo2.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
- EXPECT_CALL(mock_callback(), Run(IsSuccess(), _))
- .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
-
- ASSERT_TRUE(fetched_categories);
- ASSERT_THAT(fetched_categories->size(), Eq(1u));
- EXPECT_THAT(fetched_categories->front().info.additional_action(),
- Eq(ContentSuggestionsAdditionalAction::NONE));
- EXPECT_THAT(fetched_categories->front().info.title(),
- Eq(base::UTF8ToUTF16("Articles for Me")));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ExclusiveCategoryOnly) {
- const std::string kJsonStr =
- "{\"categories\" : [{"
- " \"id\": 1,"
- " \"localizedTitle\": \"Articles for You\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foobar\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foobar\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foobar.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}, {"
- " \"id\": 2,"
- " \"localizedTitle\": \"Articles for Me\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foo2\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foo2\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foo2.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}, {"
- " \"id\": 3,"
- " \"localizedTitle\": \"Articles for Anybody\","
- " \"suggestions\" : [{"
- " \"ids\" : [\"http://localhost/foo3\"],"
- " \"title\" : \"Foo Barred from Baz\","
- " \"snippet\" : \"...\","
- " \"fullPageUrl\" : \"http://localhost/foo3\","
- " \"creationTime\" : \"2016-06-30T11:01:37.000Z\","
- " \"expirationTime\" : \"2016-07-01T11:01:37.000Z\","
- " \"attribution\" : \"Foo News\","
- " \"imageUrl\" : \"http://localhost/foo3.jpg\","
- " \"ampUrl\" : \"http://localhost/amp\","
- " \"faviconUrl\" : \"http://localhost/favicon.ico\" "
- " }]"
- "}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
- EXPECT_CALL(mock_callback(), Run(IsSuccess(), _))
- .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
-
- RequestParams params = test_params();
- params.exclusive_category =
- base::Optional<Category>(Category::FromRemoteCategory(2));
-
- fetcher().FetchSnippets(params,
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
-
- ASSERT_TRUE(fetched_categories);
- ASSERT_THAT(fetched_categories->size(), Eq(1u));
- const auto& category = (*fetched_categories)[0];
- EXPECT_THAT(category.category.id(), Eq(Category::FromRemoteCategory(2).id()));
- ASSERT_THAT(category.suggestions.size(), Eq(1u));
- EXPECT_THAT(category.suggestions[0]->url().spec(),
- Eq("http://localhost/foo2"));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldNotFetchWithoutApiKey) {
- ResetFetcherWithAPIKey(std::string());
-
- EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::PERMANENT_ERROR),
- /*snippets=*/Not(HasValue())))
- .Times(1);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
-
- EXPECT_THAT(fetcher().last_status(), Eq("No API key available."));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- IsEmpty());
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- IsEmpty());
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest,
- ShouldFetchSuccessfullyEmptyList) {
- const std::string kJsonStr = "{\"categories\": []}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(IsSuccess(), IsEmptyCategoriesList()));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_status(), Eq("OK"));
- EXPECT_THAT(fetcher().last_json(), Eq(kJsonStr));
- EXPECT_THAT(
- histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, 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));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest,
- RetriesConfigurableOnNonInteractiveRequests) {
- struct ExpectationForVariationParam {
- std::string param_value;
- int expected_value;
- std::string description;
- };
- const std::vector<ExpectationForVariationParam> retry_config_expectation = {
- {"", 0, "Do not retry by default"},
- {"0", 0, "Do not retry on param value 0"},
- {"-1", 0, "Do not retry on negative param values."},
- {"4", 4, "Retry as set in param value."}};
-
- RequestParams params = test_params();
- params.interactive_request = false;
-
- for (const auto& retry_config : retry_config_expectation) {
- 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))
- << retry_config.description;
- }
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportUrlStatusError) {
- SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
- EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
- /*snippets=*/Not(HasValue())))
- .Times(1);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_status(), Eq("URLRequestStatus error -2"));
- EXPECT_THAT(fetcher().last_json(), IsEmpty());
- EXPECT_THAT(
- histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/-2, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- Not(IsEmpty()));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportHttpError) {
- SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
- /*snippets=*/Not(HasValue())))
- .Times(1);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_json(), IsEmpty());
- EXPECT_THAT(
- histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/404, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- Not(IsEmpty()));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportJsonError) {
- const std::string kInvalidJsonStr = "{ \"recos\": []";
- SetFakeResponse(/*response_data=*/kInvalidJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
- /*snippets=*/Not(HasValue())))
- .Times(1);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_status(),
- StartsWith("Received invalid JSON (error "));
- EXPECT_THAT(fetcher().last_json(), Eq(kInvalidJsonStr));
- EXPECT_THAT(
- histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/4, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest,
- ShouldReportJsonErrorForEmptyResponse) {
- SetFakeResponse(/*response_data=*/std::string(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
- /*snippets=*/Not(HasValue())))
- .Times(1);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_json(), std::string());
- EXPECT_THAT(
- histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/4, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldReportInvalidListError) {
- const std::string kJsonStr =
- "{\"recos\": [{ \"contentInfo\": { \"foo\" : \"bar\" }}]}";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
- /*snippets=*/Not(HasValue())))
- .Times(1);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(fetcher().last_json(), Eq(kJsonStr));
- EXPECT_THAT(
- histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- Not(IsEmpty()));
-}
-
-// This test actually verifies that the test setup itself is sane, to prevent
-// hard-to-reproduce test failures.
-TEST_F(RemoteSuggestionsSignedOutFetcherTest,
- ShouldReportHttpErrorForMissingBakedResponse) {
- InitFakeURLFetcherFactory();
- EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
- /*snippets=*/Not(HasValue())))
- .Times(1);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
-}
-
-TEST_F(RemoteSuggestionsSignedOutFetcherTest, ShouldProcessConcurrentFetches) {
- const std::string kJsonStr = "{ \"categories\": [] }";
- SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(IsSuccess(), IsEmptyCategoriesList()))
- .Times(5);
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- // More calls to FetchSnippets() do not interrupt the previous.
- // Callback is expected to be called once each time.
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- fetcher().FetchSnippets(test_params(),
- ToSnippetsAvailableCallback(&mock_callback()));
- FastForwardUntilNoTasksRemain();
- EXPECT_THAT(
- histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/0, /*count=*/5)));
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/5)));
- EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
- ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/5)));
-}
-
-::std::ostream& operator<<(
- ::std::ostream& os,
- const RemoteSuggestionsFetcher::OptionalFetchedCategories&
- fetched_categories) {
- if (fetched_categories) {
- // Matchers above aren't any more precise than this, so this is sufficient
- // for test-failure diagnostics.
- return os << "list with " << fetched_categories->size() << " elements";
- }
- return os << "null";
-}
-
-} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider.h b/chromium/components/ntp_snippets/remote/remote_suggestions_provider.h
index 5e94b8a9db6..a2b76f5fa33 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider.h
@@ -41,6 +41,9 @@ class RemoteSuggestionsProvider : public ContentSuggestionsProvider {
virtual GURL GetUrlWithFavicon(
const ContentSuggestion::ID& suggestion_id) const = 0;
+ // Whether the service is explicity disabled.
+ virtual bool IsDisabled() const = 0;
+
protected:
RemoteSuggestionsProvider(Observer* observer);
};
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index a3a8b9f6873..c13ee94413e 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -12,6 +12,7 @@
#include "base/command_line.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/stl_util.h"
@@ -21,7 +22,6 @@
#include "base/time/time.h"
#include "base/values.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/image_fetcher/core/image_decoder.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/ntp_snippets/category_rankers/category_ranker.h"
#include "components/ntp_snippets/features.h"
@@ -33,8 +33,6 @@
#include "components/prefs/pref_service.h"
#include "components/strings/grit/components_strings.h"
#include "components/variations/variations_associated_data.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
namespace ntp_snippets {
@@ -63,7 +61,26 @@ const char kCategoryContentAllowFetchingMore[] = "allow_fetching_more";
const char kOrderNewRemoteCategoriesBasedOnArticlesCategory[] =
"order_new_remote_categories_based_on_articles_category";
+// Variation parameter for additional prefetched suggestions quantity. Not more
+// than this number of prefetched suggestions will be kept longer.
+const char kMaxAdditionalPrefetchedSuggestionsParamName[] =
+ "max_additional_prefetched_suggestions";
+
+const int kDefaultMaxAdditionalPrefetchedSuggestions = 5;
+
+// Variation parameter for additional prefetched suggestions age. Only
+// prefetched suggestions fetched not later than this are considered to be kept
+// longer.
+const char kMaxAgeForAdditionalPrefetchedSuggestionParamName[] =
+ "max_age_for_additional_prefetched_suggestion_minutes";
+
+const base::TimeDelta kDefaultMaxAgeForAdditionalPrefetchedSuggestion =
+ base::TimeDelta::FromHours(36);
+
bool IsOrderingNewRemoteCategoriesBasedOnArticlesCategoryEnabled() {
+ // TODO(vitaliii): Use GetFieldTrialParamByFeature(As.*)? from
+ // base/metrics/field_trial_params.h. GetVariationParamByFeature(As.*)? are
+ // deprecated.
return variations::GetVariationParamByFeatureAsBool(
ntp_snippets::kArticleSuggestionsFeature,
kOrderNewRemoteCategoriesBasedOnArticlesCategory,
@@ -72,12 +89,11 @@ bool IsOrderingNewRemoteCategoriesBasedOnArticlesCategoryEnabled() {
void AddFetchedCategoriesToRankerBasedOnArticlesCategory(
CategoryRanker* ranker,
- const RemoteSuggestionsFetcher::FetchedCategoriesVector& fetched_categories,
+ const FetchedCategoriesVector& fetched_categories,
Category articles_category) {
DCHECK(IsOrderingNewRemoteCategoriesBasedOnArticlesCategoryEnabled());
// Insert categories which precede "Articles" in the response.
- for (const RemoteSuggestionsFetcher::FetchedCategory& fetched_category :
- fetched_categories) {
+ for (const FetchedCategory& fetched_category : fetched_categories) {
if (fetched_category.category == articles_category) {
break;
}
@@ -99,6 +115,24 @@ void AddFetchedCategoriesToRankerBasedOnArticlesCategory(
NOTREACHED() << "Articles category was not found.";
}
+bool IsKeepingPrefetchedSuggestionsEnabled() {
+ return base::FeatureList::IsEnabled(kKeepPrefetchedContentSuggestions);
+}
+
+int GetMaxAdditionalPrefetchedSuggestions() {
+ return base::GetFieldTrialParamByFeatureAsInt(
+ kKeepPrefetchedContentSuggestions,
+ kMaxAdditionalPrefetchedSuggestionsParamName,
+ kDefaultMaxAdditionalPrefetchedSuggestions);
+}
+
+base::TimeDelta GetMaxAgeForAdditionalPrefetchedSuggestion() {
+ return base::TimeDelta::FromMinutes(base::GetFieldTrialParamByFeatureAsInt(
+ kKeepPrefetchedContentSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestionParamName,
+ kDefaultMaxAgeForAdditionalPrefetchedSuggestion.InMinutes()));
+}
+
template <typename SuggestionPtrContainer>
std::unique_ptr<std::vector<std::string>> GetSuggestionIDVector(
const SuggestionPtrContainer& suggestions) {
@@ -112,7 +146,7 @@ std::unique_ptr<std::vector<std::string>> GetSuggestionIDVector(
bool HasIntersection(const std::vector<std::string>& a,
const std::set<std::string>& b) {
for (const std::string& item : a) {
- if (base::ContainsValue(b, item)) {
+ if (b.count(item)) {
return true;
}
}
@@ -125,7 +159,7 @@ void EraseByPrimaryID(RemoteSuggestion::PtrVector* suggestions,
base::EraseIf(
*suggestions,
[&ids_lookup](const std::unique_ptr<RemoteSuggestion>& suggestion) {
- return base::ContainsValue(ids_lookup, suggestion->id());
+ return ids_lookup.count(suggestion->id());
});
}
@@ -210,130 +244,6 @@ void AddDismissedIdsToRequest(const RemoteSuggestion::PtrVector& dismissed,
} // namespace
-CachedImageFetcher::CachedImageFetcher(
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
- PrefService* pref_service,
- RemoteSuggestionsDatabase* database)
- : image_fetcher_(std::move(image_fetcher)),
- database_(database),
- thumbnail_requests_throttler_(
- pref_service,
- RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) {
- // |image_fetcher_| can be null in tests.
- if (image_fetcher_) {
- image_fetcher_->SetImageFetcherDelegate(this);
- image_fetcher_->SetDataUseServiceName(
- data_use_measurement::DataUseUserData::NTP_SNIPPETS_THUMBNAILS);
- }
-}
-
-CachedImageFetcher::~CachedImageFetcher() {}
-
-void CachedImageFetcher::FetchSuggestionImage(
- const ContentSuggestion::ID& suggestion_id,
- const GURL& url,
- const ImageFetchedCallback& callback) {
- database_->LoadImage(
- suggestion_id.id_within_category(),
- base::Bind(&CachedImageFetcher::OnImageFetchedFromDatabase,
- base::Unretained(this), callback, suggestion_id, url));
-}
-
-// This function gets only called for caching the image data received from the
-// network. The actual decoding is done in OnImageDecodedFromDatabase().
-void CachedImageFetcher::OnImageDataFetched(
- const std::string& id_within_category,
- const std::string& image_data) {
- if (image_data.empty()) {
- return;
- }
- database_->SaveImage(id_within_category, image_data);
-}
-
-void CachedImageFetcher::OnImageDecodingDone(
- const ImageFetchedCallback& callback,
- const std::string& id_within_category,
- const gfx::Image& image,
- const image_fetcher::RequestMetadata& metadata) {
- callback.Run(image);
-}
-
-void CachedImageFetcher::OnImageFetchedFromDatabase(
- const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- const GURL& url,
- std::string data) { // SnippetImageCallback requires by-value.
- // The image decoder is null in tests.
- if (image_fetcher_->GetImageDecoder() && !data.empty()) {
- image_fetcher_->GetImageDecoder()->DecodeImage(
- data,
- // We're not dealing with multi-frame images.
- /*desired_image_frame_size=*/gfx::Size(),
- base::Bind(&CachedImageFetcher::OnImageDecodedFromDatabase,
- base::Unretained(this), callback, suggestion_id, url));
- return;
- }
- // Fetching from the DB failed; start a network fetch.
- FetchImageFromNetwork(suggestion_id, url, callback);
-}
-
-void CachedImageFetcher::OnImageDecodedFromDatabase(
- const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- const GURL& url,
- const gfx::Image& image) {
- if (!image.IsEmpty()) {
- callback.Run(image);
- return;
- }
- // If decoding the image failed, delete the DB entry.
- database_->DeleteImage(suggestion_id.id_within_category());
- FetchImageFromNetwork(suggestion_id, url, callback);
-}
-
-void CachedImageFetcher::FetchImageFromNetwork(
- const ContentSuggestion::ID& suggestion_id,
- const GURL& url,
- const ImageFetchedCallback& callback) {
- if (url.is_empty() || !thumbnail_requests_throttler_.DemandQuotaForRequest(
- /*interactive_request=*/true)) {
- // Return an empty image. Directly, this is never synchronous with the
- // original FetchSuggestionImage() call - an asynchronous database query has
- // happened in the meantime.
- callback.Run(gfx::Image());
- return;
- }
-
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("remote_suggestions_provider", R"(
- semantics {
- sender: "Content Suggestion Thumbnail Fetch"
- description:
- "Retrieves thumbnails for content suggestions, for display on the "
- "New Tab page or Chrome Home."
- trigger:
- "Triggered when the user looks at a content suggestion (and its "
- "thumbnail isn't cached yet)."
- data: "None."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: false
- setting: "Currently not available, but in progress: crbug.com/703684"
- chrome_policy {
- NTPContentSuggestionsEnabled {
- policy_options {mode: MANDATORY}
- NTPContentSuggestionsEnabled: false
- }
- }
- })");
- image_fetcher_->StartOrQueueNetworkRequest(
- suggestion_id.id_within_category(), url,
- base::Bind(&CachedImageFetcher::OnImageDecodingDone,
- base::Unretained(this), callback),
- traffic_annotation);
-}
-
RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl(
Observer* observer,
PrefService* pref_service,
@@ -343,7 +253,8 @@ RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl(
std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher,
std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
std::unique_ptr<RemoteSuggestionsDatabase> database,
- std::unique_ptr<RemoteSuggestionsStatusService> status_service)
+ std::unique_ptr<RemoteSuggestionsStatusService> status_service,
+ std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker)
: RemoteSuggestionsProvider(observer),
state_(State::NOT_INITED),
pref_service_(pref_service),
@@ -359,7 +270,8 @@ RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl(
fetch_when_ready_(false),
fetch_when_ready_interactive_(false),
clear_history_dependent_state_when_initialized_(false),
- clock_(base::MakeUnique<base::DefaultClock>()) {
+ clock_(base::MakeUnique<base::DefaultClock>()),
+ prefetched_pages_tracker_(std::move(prefetched_pages_tracker)) {
RestoreCategoriesFromPrefs();
// The articles category always exists. Add it if we didn't get it from prefs.
// TODO(treib): Rethink this.
@@ -436,6 +348,10 @@ GURL RemoteSuggestionsProviderImpl::GetUrlWithFavicon(
return ContentSuggestion::GetFaviconDomain(suggestion->url());
}
+bool RemoteSuggestionsProviderImpl::IsDisabled() const {
+ return state_ == State::DISABLED;
+}
+
void RemoteSuggestionsProviderImpl::FetchSuggestions(
bool interactive_request,
const FetchStatusCallback& callback) {
@@ -739,11 +655,6 @@ void RemoteSuggestionsProviderImpl::OnFetchMoreFinished(
// |fetched_category.suggestions|.
ArchiveSuggestions(existing_content, &fetched_category.suggestions);
- // TODO(tschumann): We should properly honor the existing category state,
- // e.g. to make sure we don't serve results after the sign-out. Revisit this:
- // Should Nuke also cancel outstanding requests, or do we want to check the
- // status?
- UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
fetching_callback.Run(Status::Success(), std::move(result));
}
@@ -758,6 +669,16 @@ void RemoteSuggestionsProviderImpl::OnFetchFinished(
return;
}
+ if (IsKeepingPrefetchedSuggestionsEnabled() && prefetched_pages_tracker_ &&
+ !prefetched_pages_tracker_->IsInitialized()) {
+ // Wait until the tracker is initialized.
+ prefetched_pages_tracker_->AddInitializationCompletedCallback(
+ base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchFinished,
+ base::Unretained(this), callback, interactive_request,
+ status, std::move(fetched_categories)));
+ return;
+ }
+
// Record the fetch time of a successfull background fetch.
if (!interactive_request && status.IsSuccess()) {
pref_service_->SetInt64(prefs::kLastSuccessfulBackgroundFetchTime,
@@ -782,8 +703,7 @@ void RemoteSuggestionsProviderImpl::OnFetchFinished(
// TODO(treib): Reorder |category_contents_| to match the order we received
// from the server. crbug.com/653816
bool response_includes_article_category = false;
- for (RemoteSuggestionsFetcher::FetchedCategory& fetched_category :
- *fetched_categories) {
+ for (FetchedCategory& fetched_category : *fetched_categories) {
// TODO(tschumann): Remove this histogram once we only talk to the content
// suggestions cloud backend.
if (fetched_category.category == articles_category_) {
@@ -799,7 +719,8 @@ void RemoteSuggestionsProviderImpl::OnFetchFinished(
content->included_in_last_server_response = true;
SanitizeReceivedSuggestions(content->dismissed,
&fetched_category.suggestions);
- IntegrateSuggestions(content, std::move(fetched_category.suggestions));
+ IntegrateSuggestions(fetched_category.category, content,
+ std::move(fetched_category.suggestions));
}
// Add new remote categories to the ranker.
@@ -808,8 +729,7 @@ void RemoteSuggestionsProviderImpl::OnFetchFinished(
AddFetchedCategoriesToRankerBasedOnArticlesCategory(
category_ranker_, *fetched_categories, articles_category_);
} else {
- for (const RemoteSuggestionsFetcher::FetchedCategory& fetched_category :
- *fetched_categories) {
+ for (const FetchedCategory& fetched_category : *fetched_categories) {
category_ranker_->AppendCategoryIfNecessary(fetched_category.category);
}
}
@@ -876,6 +796,7 @@ void RemoteSuggestionsProviderImpl::SanitizeReceivedSuggestions(
}
void RemoteSuggestionsProviderImpl::IntegrateSuggestions(
+ Category category,
CategoryContent* content,
RemoteSuggestion::PtrVector new_suggestions) {
DCHECK(ready());
@@ -896,6 +817,51 @@ void RemoteSuggestionsProviderImpl::IntegrateSuggestions(
// IDs though).
EraseByPrimaryID(&content->suggestions,
*GetSuggestionIDVector(new_suggestions));
+
+ // If enabled, keep some older prefetched article suggestions, otherwise the
+ // user has little time to see them.
+ if (IsKeepingPrefetchedSuggestionsEnabled() &&
+ category == articles_category_ && prefetched_pages_tracker_) {
+ DCHECK(prefetched_pages_tracker_->IsInitialized());
+
+ // Select suggestions to keep.
+ std::sort(content->suggestions.begin(), content->suggestions.end(),
+ [](const std::unique_ptr<RemoteSuggestion>& first,
+ const std::unique_ptr<RemoteSuggestion>& second) {
+ return first->fetch_date() > second->fetch_date();
+ });
+ std::vector<std::unique_ptr<RemoteSuggestion>>
+ additional_prefetched_suggestions, other_suggestions;
+ for (auto& remote_suggestion : content->suggestions) {
+ const GURL& url = remote_suggestion->amp_url().is_empty()
+ ? remote_suggestion->url()
+ : remote_suggestion->amp_url();
+ if (prefetched_pages_tracker_->PrefetchedOfflinePageExists(url) &&
+ clock_->Now() - remote_suggestion->fetch_date() <
+ GetMaxAgeForAdditionalPrefetchedSuggestion() &&
+ static_cast<int>(additional_prefetched_suggestions.size()) <
+ GetMaxAdditionalPrefetchedSuggestions()) {
+ additional_prefetched_suggestions.push_back(
+ std::move(remote_suggestion));
+ } else {
+ other_suggestions.push_back(std::move(remote_suggestion));
+ }
+ }
+
+ // Mix them into the new set according to their score.
+ for (auto& remote_suggestion : additional_prefetched_suggestions) {
+ new_suggestions.push_back(std::move(remote_suggestion));
+ }
+ std::sort(new_suggestions.begin(), new_suggestions.end(),
+ [](const std::unique_ptr<RemoteSuggestion>& first,
+ const std::unique_ptr<RemoteSuggestion>& second) {
+ return first->score() > second->score();
+ });
+
+ // Treat remaining suggestions as usual.
+ content->suggestions = std::move(other_suggestions);
+ }
+
// Do not delete the thumbnail images as they are still handy on open NTPs.
database_->DeleteSnippets(GetSuggestionIDVector(content->suggestions));
// Note, that ArchiveSuggestions will clear |content->suggestions|.
@@ -997,6 +963,8 @@ void RemoteSuggestionsProviderImpl::ClearSuggestions() {
}
void RemoteSuggestionsProviderImpl::NukeAllSuggestions() {
+ // TODO(tschumann): Should Nuke also cancel outstanding requests? Or should we
+ // only block the results of such outstanding requests?
for (const auto& item : category_contents_) {
Category category = item.first;
const CategoryContent& content = item.second;
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
index d84d00d4548..ef70fc46b17 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
@@ -20,11 +20,13 @@
#include "base/optional.h"
#include "base/time/clock.h"
#include "base/time/time.h"
-#include "components/image_fetcher/core/image_fetcher_delegate.h"
#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/category_status.h"
#include "components/ntp_snippets/content_suggestion.h"
#include "components/ntp_snippets/content_suggestions_provider.h"
+#include "components/ntp_snippets/remote/cached_image_fetcher.h"
+#include "components/ntp_snippets/remote/json_to_categories.h"
+#include "components/ntp_snippets/remote/prefetched_pages_tracker.h"
#include "components/ntp_snippets/remote/remote_suggestion.h"
#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
@@ -35,13 +37,8 @@
class PrefRegistrySimple;
class PrefService;
-namespace gfx {
-class Image;
-} // namespace gfx
-
namespace image_fetcher {
class ImageFetcher;
-struct RequestMetadata;
} // namespace image_fetcher
namespace ntp_snippets {
@@ -50,58 +47,6 @@ class CategoryRanker;
class RemoteSuggestionsDatabase;
class RemoteSuggestionsScheduler;
-// CachedImageFetcher takes care of fetching images from the network and caching
-// them in the database.
-// TODO(tschumann): Move into a separate library and inject the
-// CachedImageFetcher into the RemoteSuggestionsProvider. This allows us to get
-// rid of exposing this member for testing and lets us test the caching logic
-// separately.
-class CachedImageFetcher : public image_fetcher::ImageFetcherDelegate {
- public:
- // |pref_service| and |database| need to outlive the created image fetcher
- // instance.
- CachedImageFetcher(std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
- PrefService* pref_service,
- RemoteSuggestionsDatabase* database);
- ~CachedImageFetcher() override;
-
- // Fetches the image for a suggestion. The fetcher will first issue a lookup
- // to the underlying cache with a fallback to the network.
- void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id,
- const GURL& image_url,
- const ImageFetchedCallback& callback);
-
- private:
- // image_fetcher::ImageFetcherDelegate implementation.
- void OnImageDataFetched(const std::string& id_within_category,
- const std::string& image_data) override;
-
- void OnImageDecodingDone(const ImageFetchedCallback& callback,
- const std::string& id_within_category,
- const gfx::Image& image,
- const image_fetcher::RequestMetadata& metadata);
- void OnImageFetchedFromDatabase(
- const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- const GURL& image_url,
- // SnippetImageCallback requires by-value (not const ref).
- std::string data);
- void OnImageDecodedFromDatabase(const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- const GURL& url,
- const gfx::Image& image);
- void FetchImageFromNetwork(const ContentSuggestion::ID& suggestion_id,
- const GURL& url,
- const ImageFetchedCallback& callback);
-
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_;
- RemoteSuggestionsDatabase* database_;
- // Request throttler for limiting requests to thumbnail images.
- RequestThrottler thumbnail_requests_throttler_;
-
- DISALLOW_COPY_AND_ASSIGN(CachedImageFetcher);
-};
-
// Retrieves fresh content data (articles) from the server, stores them and
// provides them as content suggestions.
// This class is final because it does things in its constructor which make it
@@ -122,7 +67,8 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher,
std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
std::unique_ptr<RemoteSuggestionsDatabase> database,
- std::unique_ptr<RemoteSuggestionsStatusService> status_service);
+ std::unique_ptr<RemoteSuggestionsStatusService> status_service,
+ std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker);
~RemoteSuggestionsProviderImpl() override;
@@ -148,6 +94,8 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
GURL GetUrlWithFavicon(
const ContentSuggestion::ID& suggestion_id) const override;
+ bool IsDisabled() const override;
+
// ContentSuggestionsProvider implementation.
CategoryStatus GetCategoryStatus(Category category) override;
CategoryInfo GetCategoryInfo(Category category) override;
@@ -333,8 +281,9 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
void SanitizeReceivedSuggestions(const RemoteSuggestion::PtrVector& dismissed,
RemoteSuggestion::PtrVector* suggestions);
- // Adds newly available suggestions to |content|.
- void IntegrateSuggestions(CategoryContent* content,
+ // Adds newly available suggestions to |content| corresponding to |category|.
+ void IntegrateSuggestions(Category category,
+ CategoryContent* content,
RemoteSuggestion::PtrVector new_suggestions);
// Dismisses a suggestion within a given category content.
@@ -463,6 +412,9 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
// A clock for getting the time. This allows to inject a clock in tests.
std::unique_ptr<base::Clock> clock_;
+ // Prefetched pages tracker to query which urls have been prefetched.
+ std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker_;
+
DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderImpl);
};
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 0759b4f1cb6..116dbad428b 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
@@ -4,7 +4,9 @@
#include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h"
+#include <map>
#include <memory>
+#include <string>
#include <utility>
#include <vector>
@@ -20,6 +22,7 @@
#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/test/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -38,10 +41,12 @@
#include "components/ntp_snippets/features.h"
#include "components/ntp_snippets/ntp_snippets_constants.h"
#include "components/ntp_snippets/pref_names.h"
+#include "components/ntp_snippets/remote/json_to_categories.h"
#include "components/ntp_snippets/remote/persistent_scheduler.h"
+#include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
#include "components/ntp_snippets/remote/remote_suggestion.h"
#include "components/ntp_snippets/remote/remote_suggestions_database.h"
-#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
+#include "components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h"
#include "components/ntp_snippets/remote/remote_suggestions_scheduler.h"
#include "components/ntp_snippets/remote/test_utils.h"
#include "components/ntp_snippets/user_classifier.h"
@@ -50,8 +55,6 @@
#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/variations/variations_params_manager.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 "testing/gmock/include/gmock/gmock.h"
#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -62,9 +65,11 @@
using image_fetcher::ImageFetcher;
using image_fetcher::ImageFetcherDelegate;
using testing::_;
+using testing::Contains;
using testing::CreateFunctor;
using testing::ElementsAre;
using testing::Eq;
+using testing::Field;
using testing::InSequence;
using testing::Invoke;
using testing::IsEmpty;
@@ -72,6 +77,7 @@ using testing::Mock;
using testing::MockFunction;
using testing::NiceMock;
using testing::Not;
+using testing::Property;
using testing::Return;
using testing::SaveArg;
using testing::SizeIs;
@@ -83,33 +89,24 @@ namespace ntp_snippets {
namespace {
-MATCHER_P(IdEq, value, "") {
- return arg->id() == value;
+ACTION_P(MoveFirstArgumentPointeeTo, ptr) {
+ // 0-based indexation.
+ *ptr = std::move(*arg0);
}
-MATCHER_P(IdWithinCategoryEq, expected_id, "") {
- return arg.id().id_within_category() == expected_id;
+ACTION_P(MoveSecondArgumentPointeeTo, ptr) {
+ // 0-based indexation.
+ *ptr = std::move(*arg1);
}
-MATCHER_P(HasCode, code, "") {
- return arg.code == code;
-}
+const int kMaxExcludedDismissedIds = 100;
const base::Time::Exploded kDefaultCreationTime = {2015, 11, 4, 25, 13, 46, 45};
-const char kTestContentSuggestionsServerEndpoint[] =
- "https://alpha-chromecontentsuggestions-pa.sandbox.googleapis.com/v1/"
- "suggestions/fetch";
-const char kAPIKey[] = "fakeAPIkey";
-const char kTestContentSuggestionsServerWithAPIKey[] =
- "https://alpha-chromecontentsuggestions-pa.sandbox.googleapis.com/v1/"
- "suggestions/fetch?key=fakeAPIkey";
const char kSuggestionUrl[] = "http://localhost/foobar";
const char kSuggestionTitle[] = "Title";
const char kSuggestionText[] = "Suggestion";
-const char kSuggestionSalientImage[] = "http://localhost/salient_image";
const char kSuggestionPublisherName[] = "Foo News";
-const char kSuggestionAmpUrl[] = "http://localhost/amp";
const char kSuggestionUrl2[] = "http://foo.com/bar";
@@ -117,6 +114,12 @@ const char kTestJsonDefaultCategoryTitle[] = "Some title";
const int kUnknownRemoteCategoryId = 1234;
+// Different from default values to confirm that variation param values are
+// used.
+const int kMaxAdditionalPrefetchedSuggestions = 7;
+const base::TimeDelta kMaxAgeForAdditionalPrefetchedSuggestion =
+ base::TimeDelta::FromHours(48);
+
base::Time GetDefaultCreationTime() {
base::Time out_time;
EXPECT_TRUE(base::Time::FromUTCExploded(kDefaultCreationTime, &out_time));
@@ -127,163 +130,194 @@ base::Time GetDefaultExpirationTime() {
return base::Time::Now() + base::TimeDelta::FromHours(1);
}
-std::string GetCategoryJson(const std::vector<std::string>& suggestions,
- int remote_category_id,
- const std::string& category_title) {
- return base::StringPrintf(
- " {\n"
- " \"id\": %d,\n"
- " \"localizedTitle\": \"%s\",\n"
- " \"suggestions\": [%s]\n"
- " }\n",
- remote_category_id, category_title.c_str(),
- base::JoinString(suggestions, ", ").c_str());
-}
-
-class MultiCategoryJsonBuilder {
+// TODO(vitaliii): Remove this and use RemoteSuggestionBuilder instead.
+std::unique_ptr<RemoteSuggestion> CreateTestRemoteSuggestion(
+ const std::string& url) {
+ SnippetProto snippet_proto;
+ snippet_proto.add_ids(url);
+ snippet_proto.set_title("title");
+ snippet_proto.set_snippet("snippet");
+ snippet_proto.set_salient_image_url(url + "p.jpg");
+ snippet_proto.set_publish_date(GetDefaultCreationTime().ToTimeT());
+ snippet_proto.set_expiry_date(GetDefaultExpirationTime().ToTimeT());
+ snippet_proto.set_remote_category_id(1);
+ auto* source = snippet_proto.add_sources();
+ source->set_url(url);
+ source->set_publisher_name("Publisher");
+ source->set_amp_url(url + "amp");
+ return RemoteSuggestion::CreateFromProto(snippet_proto);
+}
+
+class RemoteSuggestionBuilder {
public:
- MultiCategoryJsonBuilder() {}
-
- MultiCategoryJsonBuilder& AddCategoryWithCustomTitle(
- const std::vector<std::string>& suggestions,
- int remote_category_id,
- const std::string& category_title) {
- category_json_.push_back(
- GetCategoryJson(suggestions, remote_category_id, category_title));
+ RemoteSuggestionBuilder() = default;
+
+ RemoteSuggestionBuilder& AddId(const std::string& id) {
+ if (!ids_) {
+ ids_ = std::vector<std::string>();
+ }
+ ids_->push_back(id);
return *this;
}
-
- MultiCategoryJsonBuilder& AddCategory(
- const std::vector<std::string>& suggestions,
- int remote_category_id) {
- return AddCategoryWithCustomTitle(
- suggestions, remote_category_id,
- "Title" + base::IntToString(remote_category_id));
+ RemoteSuggestionBuilder& SetTitle(const std::string& title) {
+ title_ = title;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetSnippet(const std::string& snippet) {
+ snippet_ = snippet;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetImageUrl(const std::string& image_url) {
+ salient_image_url_ = image_url;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetPublishDate(const base::Time& publish_date) {
+ publish_date_ = publish_date;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetExpiryDate(const base::Time& expiry_date) {
+ expiry_date_ = expiry_date;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetScore(double score) {
+ score_ = score;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetIsDismissed(bool is_dismissed) {
+ is_dismissed_ = is_dismissed;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetRemoteCategoryId(int remote_category_id) {
+ remote_category_id_ = remote_category_id;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetUrl(const std::string& url) {
+ url_ = url;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetPublisher(const std::string& publisher) {
+ publisher_name_ = publisher;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetAmpUrl(const std::string& amp_url) {
+ amp_url_ = amp_url;
+ return *this;
+ }
+ RemoteSuggestionBuilder& SetFetchDate(const base::Time& fetch_date) {
+ fetch_date_ = fetch_date;
+ return *this;
}
- std::string Build() {
- return base::StringPrintf(
- "{\n"
- " \"categories\": [\n"
- "%s\n"
- " ]\n"
- "}\n",
- base::JoinString(category_json_, " ,\n").c_str());
+ std::unique_ptr<RemoteSuggestion> Build() const {
+ SnippetProto proto;
+ proto.set_title(title_.value_or("Title"));
+ proto.set_snippet(snippet_.value_or("Snippet"));
+ proto.set_salient_image_url(
+ salient_image_url_.value_or("http://image_url.com/"));
+ proto.set_publish_date(
+ publish_date_.value_or(GetDefaultCreationTime()).ToInternalValue());
+ proto.set_expiry_date(
+ expiry_date_.value_or(GetDefaultExpirationTime()).ToInternalValue());
+ proto.set_score(score_.value_or(1));
+ proto.set_dismissed(is_dismissed_.value_or(false));
+ proto.set_remote_category_id(remote_category_id_.value_or(1));
+ auto* source = proto.add_sources();
+ source->set_url(url_.value_or("http://url.com/"));
+ source->set_publisher_name(publisher_name_.value_or("Publisher"));
+ source->set_amp_url(amp_url_.value_or("http://amp_url.com/"));
+ proto.set_fetch_date(
+ fetch_date_.value_or(base::Time::Now()).ToInternalValue());
+ for (const auto& id :
+ ids_.value_or(std::vector<std::string>{source->url()})) {
+ proto.add_ids(id);
+ }
+ return RemoteSuggestion::CreateFromProto(proto);
}
private:
- std::vector<std::string> category_json_;
+ base::Optional<std::vector<std::string>> ids_;
+ base::Optional<std::string> title_;
+ base::Optional<std::string> snippet_;
+ base::Optional<std::string> salient_image_url_;
+ base::Optional<base::Time> publish_date_;
+ base::Optional<base::Time> expiry_date_;
+ base::Optional<double> score_;
+ base::Optional<bool> is_dismissed_;
+ base::Optional<int> remote_category_id_;
+ base::Optional<std::string> url_;
+ base::Optional<std::string> publisher_name_;
+ base::Optional<std::string> amp_url_;
+ base::Optional<base::Time> fetch_date_;
};
-// TODO(vitaliii): Remove these convenience functions as they do not provide
-// that much value and add additional redirections obscuring the code.
-std::string GetTestJson(const std::vector<std::string>& suggestions,
- const std::string& category_title) {
- return MultiCategoryJsonBuilder()
- .AddCategoryWithCustomTitle(suggestions, /*remote_category_id=*/1,
- category_title)
- .Build();
-}
-
-std::string GetTestJson(const std::vector<std::string>& suggestions) {
- return GetTestJson(suggestions, kTestJsonDefaultCategoryTitle);
-}
-
-std::string FormatTime(const base::Time& t) {
- base::Time::Exploded x;
- t.UTCExplode(&x);
- return base::StringPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", x.year, x.month,
- x.day_of_month, x.hour, x.minute, x.second);
-}
-
-std::string GetSuggestionWithUrlAndTimesAndSource(
- const std::vector<std::string>& ids,
- const std::string& url,
- const base::Time& creation_time,
- const base::Time& expiry_time,
- const std::string& publisher,
- const std::string& amp_url) {
- const std::string ids_string = base::JoinString(ids, "\",\n \"");
- return base::StringPrintf(
- "{\n"
- " \"ids\": [\n"
- " \"%s\"\n"
- " ],\n"
- " \"title\": \"%s\",\n"
- " \"snippet\": \"%s\",\n"
- " \"fullPageUrl\": \"%s\",\n"
- " \"creationTime\": \"%s\",\n"
- " \"expirationTime\": \"%s\",\n"
- " \"attribution\": \"%s\",\n"
- " \"imageUrl\": \"%s\",\n"
- " \"ampUrl\": \"%s\"\n"
- " }",
- ids_string.c_str(), kSuggestionTitle, kSuggestionText, url.c_str(),
- FormatTime(creation_time).c_str(), FormatTime(expiry_time).c_str(),
- publisher.c_str(), kSuggestionSalientImage, amp_url.c_str());
-}
-
-std::string GetSuggestionWithSources(const std::string& source_url,
- const std::string& publisher,
- const std::string& amp_url) {
- return GetSuggestionWithUrlAndTimesAndSource(
- {kSuggestionUrl}, source_url, GetDefaultCreationTime(),
- GetDefaultExpirationTime(), publisher, amp_url);
-}
-
-std::string GetSuggestionWithUrlAndTimes(
- const std::string& url,
- const base::Time& content_creation_time,
- const base::Time& expiry_time) {
- return GetSuggestionWithUrlAndTimesAndSource(
- {url}, url, content_creation_time, expiry_time, kSuggestionPublisherName,
- kSuggestionAmpUrl);
-}
-
-std::string GetSuggestionWithTimes(const base::Time& content_creation_time,
- const base::Time& expiry_time) {
- return GetSuggestionWithUrlAndTimes(kSuggestionUrl, content_creation_time,
- expiry_time);
-}
-
-std::string GetSuggestionWithUrl(const std::string& url) {
- return GetSuggestionWithUrlAndTimes(url, GetDefaultCreationTime(),
- GetDefaultExpirationTime());
-}
-
-std::string GetSuggestion() {
- return GetSuggestionWithUrlAndTimes(kSuggestionUrl, GetDefaultCreationTime(),
- GetDefaultExpirationTime());
-}
-
-std::string GetSuggestionN(int n) {
- return GetSuggestionWithUrlAndTimes(
- base::StringPrintf("%s/%d", kSuggestionUrl, n), GetDefaultCreationTime(),
- GetDefaultExpirationTime());
-}
-
-std::string GetExpiredSuggestion() {
- return GetSuggestionWithTimes(GetDefaultCreationTime(), base::Time::Now());
-}
+class FetchedCategoryBuilder {
+ public:
+ FetchedCategoryBuilder() = default;
-std::string GetInvalidSuggestion() {
- std::string json_str = GetSuggestion();
- // Make the json invalid by removing the final closing brace.
- return json_str.substr(0, json_str.size() - 1);
-}
+ FetchedCategoryBuilder& SetCategory(Category category) {
+ category_ = category;
+ return *this;
+ }
+ FetchedCategoryBuilder& SetTitle(const std::string& title) {
+ title_ = base::UTF8ToUTF16(title);
+ return *this;
+ }
+ FetchedCategoryBuilder& SetCardLayout(
+ ContentSuggestionsCardLayout card_layout) {
+ card_layout_ = card_layout;
+ return *this;
+ }
+ FetchedCategoryBuilder& SetAdditionalAction(
+ ContentSuggestionsAdditionalAction additional_action) {
+ additional_action_ = additional_action;
+ return *this;
+ }
+ FetchedCategoryBuilder& SetShowIfEmpty(bool show_if_empty) {
+ show_if_empty_ = show_if_empty;
+ return *this;
+ }
+ FetchedCategoryBuilder& SetNoSuggestionsMessage(
+ const std::string& no_suggestions_message) {
+ no_suggestions_message_ = base::UTF8ToUTF16(no_suggestions_message);
+ return *this;
+ }
+ FetchedCategoryBuilder& AddSuggestionViaBuilder(
+ const RemoteSuggestionBuilder& builder) {
+ if (!suggestion_builders_) {
+ suggestion_builders_ = std::vector<RemoteSuggestionBuilder>();
+ }
+ suggestion_builders_->push_back(builder);
+ return *this;
+ }
-std::string GetIncompleteSuggestion() {
- std::string json_str = GetSuggestion();
- // Rename the "url" entry. The result is syntactically valid json that will
- // fail to parse as suggestions.
- size_t pos = json_str.find("\"fullPageUrl\"");
- if (pos == std::string::npos) {
- NOTREACHED();
- return std::string();
+ FetchedCategory Build() const {
+ FetchedCategory result = FetchedCategory(
+ category_.value_or(Category::FromRemoteCategory(1)),
+ CategoryInfo(
+ title_.value_or(base::UTF8ToUTF16("Category title")),
+ card_layout_.value_or(ContentSuggestionsCardLayout::FULL_CARD),
+ additional_action_.value_or(
+ ContentSuggestionsAdditionalAction::FETCH),
+ show_if_empty_.value_or(false),
+ no_suggestions_message_.value_or(
+ base::UTF8ToUTF16("No suggestions message"))));
+
+ if (suggestion_builders_) {
+ for (const auto& suggestion_builder : *suggestion_builders_)
+ result.suggestions.push_back(suggestion_builder.Build());
+ }
+ return result;
}
- json_str[pos + 1] = 'x';
- return json_str;
-}
+
+ private:
+ base::Optional<Category> category_;
+ base::Optional<base::string16> title_;
+ base::Optional<ContentSuggestionsCardLayout> card_layout_;
+ base::Optional<ContentSuggestionsAdditionalAction> additional_action_;
+ base::Optional<bool> show_if_empty_;
+ base::Optional<base::string16> no_suggestions_message_;
+ base::Optional<std::vector<RemoteSuggestionBuilder>> suggestion_builders_;
+};
using ServeImageCallback = base::Callback<void(
const std::string&,
@@ -303,49 +337,22 @@ void ServeOneByOneImage(
notify->OnImageDataFetched(id, "1-by-1-image-data");
}
-gfx::Image FetchImage(RemoteSuggestionsProviderImpl* service,
+gfx::Image FetchImage(RemoteSuggestionsProviderImpl* provider,
const ContentSuggestion::ID& suggestion_id) {
gfx::Image result;
base::RunLoop run_loop;
- service->FetchSuggestionImage(suggestion_id,
- base::Bind(
- [](base::Closure signal, gfx::Image* output,
- const gfx::Image& loaded) {
- *output = loaded;
- signal.Run();
- },
- run_loop.QuitClosure(), &result));
+ provider->FetchSuggestionImage(
+ suggestion_id, base::Bind(
+ [](base::Closure signal, gfx::Image* output,
+ const gfx::Image& loaded) {
+ *output = loaded;
+ signal.Run();
+ },
+ run_loop.QuitClosure(), &result));
run_loop.Run();
return result;
}
-void ParseJson(const std::string& json,
- const SuccessCallback& success_callback,
- const ErrorCallback& error_callback) {
- base::JSONReader json_reader;
- std::unique_ptr<base::Value> value = json_reader.ReadToValue(json);
- if (value) {
- success_callback.Run(std::move(value));
- } else {
- error_callback.Run(json_reader.GetErrorMessage());
- }
-}
-
-// 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* d,
- net::NetworkTrafficAnnotationTag traffic_annotation) override {
- return base::MakeUnique<net::FakeURLFetcher>(
- url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
- }
-};
-
class MockImageFetcher : public ImageFetcher {
public:
MOCK_METHOD1(SetImageFetcherDelegate, void(ImageFetcherDelegate*));
@@ -389,9 +396,42 @@ class MockScheduler : public RemoteSuggestionsScheduler {
MOCK_METHOD1(OnInteractiveFetchFinished, void(Status fetch_status));
MOCK_METHOD0(OnBrowserForegrounded, void());
MOCK_METHOD0(OnBrowserColdStart, void());
- MOCK_METHOD0(OnNTPOpened, void());
+ MOCK_METHOD0(OnSuggestionsSurfaceOpened, void());
MOCK_METHOD0(OnPersistentSchedulerWakeUp, void());
- MOCK_METHOD0(RescheduleFetching, void());
+ MOCK_METHOD0(OnBrowserUpgraded, void());
+};
+
+class MockRemoteSuggestionsFetcher : public RemoteSuggestionsFetcher {
+ public:
+ // GMock does not support movable-only types (SnippetsAvailableCallback is
+ // OnceCallback), therefore, the call is redirected to a mock method with a
+ // pointer to the callback.
+ void FetchSnippets(const RequestParams& params,
+ SnippetsAvailableCallback callback) override {
+ FetchSnippets(params, &callback);
+ }
+ MOCK_METHOD2(FetchSnippets,
+ void(const RequestParams& params,
+ SnippetsAvailableCallback* callback));
+ MOCK_CONST_METHOD0(GetLastStatusForDebugging, const std::string&());
+ MOCK_CONST_METHOD0(GetLastJsonForDebugging, const std::string&());
+ MOCK_CONST_METHOD0(GetFetchUrlForDebugging, const GURL&());
+};
+
+class MockPrefetchedPagesTracker : public PrefetchedPagesTracker {
+ public:
+ MOCK_CONST_METHOD0(IsInitialized, bool());
+
+ // GMock does not support movable-only types (e.g. OnceCallback), therefore,
+ // the call is redirected to a mock method with a pointer to the callback.
+ void AddInitializationCompletedCallback(
+ base::OnceCallback<void()> callback) override {
+ AddInitializationCompletedCallback(&callback);
+ }
+ MOCK_METHOD1(AddInitializationCompletedCallback,
+ void(base::OnceCallback<void()>* callback));
+
+ MOCK_CONST_METHOD1(PrefetchedOfflinePageExists, bool(const GURL& url));
};
} // namespace
@@ -399,17 +439,10 @@ class MockScheduler : public RemoteSuggestionsScheduler {
class RemoteSuggestionsProviderImplTest : public ::testing::Test {
public:
RemoteSuggestionsProviderImplTest()
- : params_manager_(ntp_snippets::kArticleSuggestionsFeature.name,
- {{"content_suggestions_backend",
- kTestContentSuggestionsServerEndpoint}},
- {ntp_snippets::kArticleSuggestionsFeature.name}),
- fake_url_fetcher_factory_(
- /*default_factory=*/&failing_url_fetcher_factory_),
- test_url_(kTestContentSuggestionsServerWithAPIKey),
- category_ranker_(base::MakeUnique<ConstantCategoryRanker>()),
+ : category_ranker_(base::MakeUnique<ConstantCategoryRanker>()),
user_classifier_(/*pref_service=*/nullptr,
base::MakeUnique<base::DefaultClock>()),
- suggestions_fetcher_(nullptr),
+ mock_suggestions_fetcher_(nullptr),
image_fetcher_(nullptr),
scheduler_(base::MakeUnique<NiceMock<MockScheduler>>()),
database_(nullptr) {
@@ -428,28 +461,31 @@ class RemoteSuggestionsProviderImplTest : public ::testing::Test {
}
// TODO(vitaliii): Rewrite this function to initialize a test class member
- // instead of creating a new service.
- std::unique_ptr<RemoteSuggestionsProviderImpl> MakeSuggestionsProvider(
- bool set_empty_response = true) {
- auto service = MakeSuggestionsProviderWithoutInitialization();
- WaitForSuggestionsProviderInitialization(service.get(), set_empty_response);
- return service;
+ // instead of creating a new provider.
+ std::unique_ptr<RemoteSuggestionsProviderImpl> MakeSuggestionsProvider() {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+ return provider;
}
std::unique_ptr<RemoteSuggestionsProviderImpl>
- MakeSuggestionsProviderWithoutInitialization() {
+ MakeSuggestionsProviderWithoutInitialization(
+ bool use_mock_prefetched_pages_tracker) {
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
base::ThreadTaskRunnerHandle::Get());
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter =
- new net::TestURLRequestContextGetter(task_runner.get());
utils_.ResetSigninManager();
- auto suggestions_fetcher = base::MakeUnique<RemoteSuggestionsFetcher>(
- utils_.fake_signin_manager(), /*token_service=*/nullptr,
- std::move(request_context_getter), utils_.pref_service(), nullptr,
- base::Bind(&ParseJson), GetFetchEndpoint(version_info::Channel::STABLE),
- kAPIKey, &user_classifier_);
- suggestions_fetcher_ = suggestions_fetcher.get();
+ auto mock_suggestions_fetcher =
+ base::MakeUnique<StrictMock<MockRemoteSuggestionsFetcher>>();
+ mock_suggestions_fetcher_ = mock_suggestions_fetcher.get();
+
+ std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker;
+ if (use_mock_prefetched_pages_tracker) {
+ prefetched_pages_tracker =
+ base::MakeUnique<StrictMock<MockPrefetchedPagesTracker>>();
+ }
+ prefetched_pages_tracker_ = prefetched_pages_tracker.get();
auto image_fetcher = base::MakeUnique<NiceMock<MockImageFetcher>>();
@@ -464,43 +500,36 @@ class RemoteSuggestionsProviderImplTest : public ::testing::Test {
database_ = database.get();
return base::MakeUnique<RemoteSuggestionsProviderImpl>(
observer_.get(), utils_.pref_service(), "fr", category_ranker_.get(),
- scheduler_.get(), std::move(suggestions_fetcher),
+ scheduler_.get(), std::move(mock_suggestions_fetcher),
std::move(image_fetcher), std::move(database),
base::MakeUnique<RemoteSuggestionsStatusService>(
- utils_.fake_signin_manager(), utils_.pref_service(),
- std::string()));
+ utils_.fake_signin_manager(), utils_.pref_service(), std::string()),
+ std::move(prefetched_pages_tracker));
}
std::unique_ptr<RemoteSuggestionsProviderImpl>
MakeSuggestionsProviderWithoutInitializationWithStrictScheduler() {
scheduler_ = base::MakeUnique<StrictMock<MockScheduler>>();
- return MakeSuggestionsProviderWithoutInitialization();
+ return MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
}
void WaitForSuggestionsProviderInitialization(
- RemoteSuggestionsProviderImpl* service,
- bool set_empty_response) {
+ RemoteSuggestionsProviderImpl* provider) {
EXPECT_EQ(RemoteSuggestionsProviderImpl::State::NOT_INITED,
- service->state_);
-
- // Add an initial fetch response, as the service tries to fetch when there
- // is nothing in the DB.
- if (set_empty_response) {
- SetUpFetchResponse(GetTestJson(std::vector<std::string>()));
- }
+ provider->state_);
// TODO(treib): Find a better way to wait for initialization to finish.
base::RunLoop().RunUntilIdle();
EXPECT_NE(RemoteSuggestionsProviderImpl::State::NOT_INITED,
- service->state_);
+ provider->state_);
}
void ResetSuggestionsProvider(
- std::unique_ptr<RemoteSuggestionsProviderImpl>* service,
- bool set_empty_response) {
- service->reset();
+ std::unique_ptr<RemoteSuggestionsProviderImpl>* provider) {
+ provider->reset();
observer_.reset();
- *service = MakeSuggestionsProvider(set_empty_response);
+ *provider = MakeSuggestionsProvider();
}
void SetCategoryRanker(std::unique_ptr<CategoryRanker> category_ranker) {
@@ -528,10 +557,12 @@ class RemoteSuggestionsProviderImplTest : public ::testing::Test {
}
protected:
- const GURL& test_url() { return test_url_; }
FakeContentSuggestionsProviderObserver& observer() { return *observer_; }
- RemoteSuggestionsFetcher* suggestions_fetcher() {
- return suggestions_fetcher_;
+ StrictMock<MockRemoteSuggestionsFetcher>* mock_suggestions_fetcher() {
+ return mock_suggestions_fetcher_;
+ }
+ PrefetchedPagesTracker* prefetched_pages_tracker() {
+ return prefetched_pages_tracker_;
}
// TODO(tschumann): Make this a strict-mock. We want to avoid unneccesary
// network requests.
@@ -541,66 +572,82 @@ class RemoteSuggestionsProviderImplTest : public ::testing::Test {
RemoteSuggestionsDatabase* database() { return database_; }
MockScheduler* scheduler() { return scheduler_.get(); }
- // Provide the json to be returned by the fake fetcher.
- void SetUpFetchResponse(const std::string& json) {
- fake_url_fetcher_factory_.SetFakeResponse(test_url_, json, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- }
-
- // Have the fake fetcher fail due to a HTTP error like a 404.
- void SetUpHttpError() {
- fake_url_fetcher_factory_.SetFakeResponse(test_url_, /*json=*/std::string(),
- net::HTTP_NOT_FOUND,
- net::URLRequestStatus::SUCCESS);
- }
-
- void LoadFromJSONString(RemoteSuggestionsProviderImpl* service,
- const std::string& json) {
- SetUpFetchResponse(json);
- service->FetchSuggestions(/*interactive_request=*/true,
- RemoteSuggestionsProvider::FetchStatusCallback());
- base::RunLoop().RunUntilIdle();
+ void FetchTheseSuggestions(
+ RemoteSuggestionsProviderImpl* provider,
+ bool interactive_request,
+ Status status,
+ base::Optional<std::vector<FetchedCategory>> fetched_categories) {
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback snippets_callback;
+ EXPECT_CALL(*mock_suggestions_fetcher(), FetchSnippets(_, _))
+ .WillOnce(MoveSecondArgumentPointeeTo(&snippets_callback))
+ .RetiresOnSaturation();
+ provider->FetchSuggestions(
+ interactive_request, RemoteSuggestionsProvider::FetchStatusCallback());
+ std::move(snippets_callback)
+ .Run(Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
}
- void LoadMoreFromJSONString(RemoteSuggestionsProviderImpl* service,
- const Category& category,
- const std::string& json,
- const std::set<std::string>& known_ids,
- FetchDoneCallback callback) {
- SetUpFetchResponse(json);
+ void FetchMoreTheseSuggestions(
+ RemoteSuggestionsProviderImpl* provider,
+ const Category& category,
+ const std::set<std::string>& known_suggestion_ids,
+ FetchDoneCallback fetch_done_callback,
+ Status status,
+ base::Optional<std::vector<FetchedCategory>> fetched_categories) {
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback snippets_callback;
+ EXPECT_CALL(*mock_suggestions_fetcher(), FetchSnippets(_, _))
+ .WillOnce(MoveSecondArgumentPointeeTo(&snippets_callback))
+ .RetiresOnSaturation();
EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
.WillOnce(Return(true))
.RetiresOnSaturation();
- service->Fetch(category, known_ids, callback);
- base::RunLoop().RunUntilIdle();
+ provider->Fetch(category, known_suggestion_ids, fetch_done_callback);
+ std::move(snippets_callback)
+ .Run(Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
}
void SetOrderNewRemoteCategoriesBasedOnArticlesCategoryParam(bool value) {
// params_manager supports only one
// |SetVariationParamsWithFeatureAssociations| at a time, so we clear
- // previous settings first and then set everything we need.
+ // previous settings first to make this explicit.
params_manager_.ClearAllVariationParams();
params_manager_.SetVariationParamsWithFeatureAssociations(
kArticleSuggestionsFeature.name,
{{"order_new_remote_categories_based_on_articles_category",
- value ? "true" : "false"},
- {"content_suggestions_backend",
- kTestContentSuggestionsServerEndpoint}},
+ value ? "true" : "false"}},
{kArticleSuggestionsFeature.name});
}
+ void EnableKeepingPrefetchedContentSuggestions(
+ int max_additional_prefetched_suggestions,
+ const base::TimeDelta& max_age_for_additional_prefetched_suggestion) {
+ // params_manager supports only one
+ // |SetVariationParamsWithFeatureAssociations| at a time, so we clear
+ // previous settings first to make this explicit.
+ params_manager_.ClearAllVariationParams();
+ params_manager_.SetVariationParamsWithFeatureAssociations(
+ kKeepPrefetchedContentSuggestions.name,
+ {
+ {"max_additional_prefetched_suggestions",
+ base::IntToString(max_additional_prefetched_suggestions)},
+ {"max_age_for_additional_prefetched_suggestion_minutes",
+ base::IntToString(
+ max_age_for_additional_prefetched_suggestion.InMinutes())},
+ },
+ {kKeepPrefetchedContentSuggestions.name});
+ }
+
private:
variations::testing::VariationParamsManager params_manager_;
test::RemoteSuggestionsTestUtils utils_;
base::MessageLoop message_loop_;
- FailingFakeURLFetcherFactory failing_url_fetcher_factory_;
- // Instantiation of factory automatically sets itself as URLFetcher's factory.
- net::FakeURLFetcherFactory fake_url_fetcher_factory_;
- const GURL test_url_;
std::unique_ptr<CategoryRanker> category_ranker_;
UserClassifier user_classifier_;
std::unique_ptr<FakeContentSuggestionsProviderObserver> observer_;
- RemoteSuggestionsFetcher* suggestions_fetcher_;
+ StrictMock<MockRemoteSuggestionsFetcher>* mock_suggestions_fetcher_;
+ PrefetchedPagesTracker* prefetched_pages_tracker_;
NiceMock<MockImageFetcher>* image_fetcher_;
FakeImageDecoder image_decoder_;
std::unique_ptr<MockScheduler> scheduler_;
@@ -612,15 +659,27 @@ class RemoteSuggestionsProviderImplTest : public ::testing::Test {
};
TEST_F(RemoteSuggestionsProviderImplTest, Full) {
- std::string json_str(GetTestJson({GetSuggestion()}));
-
- auto service = MakeSuggestionsProvider();
-
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+
+ // TODO(vitaliii): Inline the vector creation in FetchTheseSuggestions.
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId(kSuggestionUrl)
+ .SetTitle(kSuggestionTitle)
+ .SetSnippet(kSuggestionText)
+ .SetPublishDate(GetDefaultCreationTime())
+ .SetPublisher(kSuggestionPublisherName))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
SizeIs(1));
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
const ContentSuggestion& suggestion =
@@ -640,27 +699,35 @@ TEST_F(RemoteSuggestionsProviderImplTest, CategoryTitle) {
// Don't send an initial response -- we want to test what happens without any
// server status.
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
+ auto provider = MakeSuggestionsProvider();
// The articles category should be there by default, and have a title.
- CategoryInfo info_before = service->GetCategoryInfo(articles_category());
+ CategoryInfo info_before = provider->GetCategoryInfo(articles_category());
ASSERT_THAT(info_before.title(), Not(IsEmpty()));
ASSERT_THAT(info_before.title(), Not(Eq(test_default_title)));
EXPECT_THAT(info_before.additional_action(),
Eq(ContentSuggestionsAdditionalAction::FETCH));
EXPECT_THAT(info_before.show_if_empty(), Eq(true));
- std::string json_str_with_title(GetTestJson({GetSuggestion()}));
- LoadFromJSONString(service.get(), json_str_with_title);
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .SetTitle(base::UTF16ToUTF8(test_default_title))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder())
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
SizeIs(1));
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
// The response contained a title, |kTestJsonDefaultCategoryTitle|.
// Make sure we updated the title in the CategoryInfo.
- CategoryInfo info_with_title = service->GetCategoryInfo(articles_category());
+ CategoryInfo info_with_title = provider->GetCategoryInfo(articles_category());
EXPECT_THAT(info_before.title(), Not(Eq(info_with_title.title())));
EXPECT_THAT(test_default_title, Eq(info_with_title.title()));
EXPECT_THAT(info_before.additional_action(),
@@ -669,13 +736,33 @@ TEST_F(RemoteSuggestionsProviderImplTest, CategoryTitle) {
}
TEST_F(RemoteSuggestionsProviderImplTest, MultipleCategories) {
- auto service = MakeSuggestionsProvider();
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)}, /*remote_category_id=*/1)
- .AddCategory({GetSuggestionN(1)}, /*remote_category_id=*/2)
- .Build();
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(1))
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder()
+ .AddId(base::StringPrintf("%s/%d", kSuggestionUrl, 0))
+ .SetTitle(kSuggestionTitle)
+ .SetSnippet(kSuggestionText)
+ .SetPublishDate(GetDefaultCreationTime())
+ .SetPublisher(kSuggestionPublisherName))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(2))
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder()
+ .AddId(base::StringPrintf("%s/%d", kSuggestionUrl, 1))
+ .SetTitle(kSuggestionTitle)
+ .SetSnippet(kSuggestionText)
+ .SetPublishDate(GetDefaultCreationTime())
+ .SetPublisher(kSuggestionPublisherName))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
ASSERT_THAT(observer().statuses(),
Eq(std::map<Category, CategoryStatus, Category::CompareByID>{
@@ -683,9 +770,9 @@ TEST_F(RemoteSuggestionsProviderImplTest, MultipleCategories) {
{other_category(), CategoryStatus::AVAILABLE},
}));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
- EXPECT_THAT(service->GetSuggestionsForTesting(other_category()), SizeIs(1));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(other_category()), SizeIs(1));
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
SizeIs(1));
@@ -717,25 +804,34 @@ TEST_F(RemoteSuggestionsProviderImplTest, MultipleCategories) {
}
TEST_F(RemoteSuggestionsProviderImplTest, ArticleCategoryInfo) {
- auto service = MakeSuggestionsProvider();
- CategoryInfo article_info = service->GetCategoryInfo(articles_category());
+ auto provider = MakeSuggestionsProvider();
+ CategoryInfo article_info = provider->GetCategoryInfo(articles_category());
EXPECT_THAT(article_info.additional_action(),
Eq(ContentSuggestionsAdditionalAction::FETCH));
EXPECT_THAT(article_info.show_if_empty(), Eq(true));
}
TEST_F(RemoteSuggestionsProviderImplTest, ExperimentalCategoryInfo) {
- auto service = MakeSuggestionsProvider();
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)}, /*remote_category_id=*/1)
- .AddCategory({GetSuggestionN(1)}, kUnknownRemoteCategoryId)
- .Build();
+ auto provider = MakeSuggestionsProvider();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(1))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("1"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(kUnknownRemoteCategoryId))
+ .SetAdditionalAction(ContentSuggestionsAdditionalAction::NONE)
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("2"))
+ .Build());
// Load data with multiple categories so that a new experimental category gets
// registered.
- LoadFromJSONString(service.get(), json_str);
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
- CategoryInfo info = service->GetCategoryInfo(unknown_category());
+ CategoryInfo info = provider->GetCategoryInfo(unknown_category());
EXPECT_THAT(info.additional_action(),
Eq(ContentSuggestionsAdditionalAction::NONE));
EXPECT_THAT(info.show_if_empty(), Eq(false));
@@ -745,12 +841,22 @@ TEST_F(RemoteSuggestionsProviderImplTest, AddRemoteCategoriesToCategoryRanker) {
auto mock_ranker = base::MakeUnique<MockCategoryRanker>();
MockCategoryRanker* raw_mock_ranker = mock_ranker.get();
SetCategoryRanker(std::move(mock_ranker));
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)}, /*remote_category_id=*/11)
- .AddCategory({GetSuggestionN(1)}, /*remote_category_id=*/13)
- .AddCategory({GetSuggestionN(2)}, /*remote_category_id=*/12)
- .Build();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(11))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("11"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(13))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("13"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(12))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("12"))
+ .Build());
{
// The order of categories is determined by the order in which they are
// added. Thus, the latter is tested here.
@@ -762,8 +868,10 @@ TEST_F(RemoteSuggestionsProviderImplTest, AddRemoteCategoriesToCategoryRanker) {
EXPECT_CALL(*raw_mock_ranker,
AppendCategoryIfNecessary(Category::FromRemoteCategory(12)));
}
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
}
TEST_F(RemoteSuggestionsProviderImplTest,
@@ -772,14 +880,32 @@ TEST_F(RemoteSuggestionsProviderImplTest,
auto mock_ranker = base::MakeUnique<MockCategoryRanker>();
MockCategoryRanker* raw_mock_ranker = mock_ranker.get();
SetCategoryRanker(std::move(mock_ranker));
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)}, /*remote_category_id=*/14)
- .AddCategory({GetSuggestionN(1)}, /*remote_category_id=*/13)
- .AddCategory({GetSuggestionN(2)}, /*remote_category_id=*/1)
- .AddCategory({GetSuggestionN(3)}, /*remote_category_id=*/12)
- .AddCategory({GetSuggestionN(4)}, /*remote_category_id=*/11)
- .Build();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(14))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("14"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(13))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("13"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(1))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("1"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(12))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("12"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(11))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("11"))
+ .Build());
{
InSequence s;
EXPECT_CALL(*raw_mock_ranker,
@@ -795,8 +921,10 @@ TEST_F(RemoteSuggestionsProviderImplTest,
InsertCategoryAfterIfNecessary(Category::FromRemoteCategory(12),
articles_category()));
}
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
}
TEST_F(
@@ -806,81 +934,105 @@ TEST_F(
auto mock_ranker = base::MakeUnique<MockCategoryRanker>();
MockCategoryRanker* raw_mock_ranker = mock_ranker.get();
SetCategoryRanker(std::move(mock_ranker));
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)}, /*remote_category_id=*/11)
- .Build();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(11))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("11"))
+ .Build());
EXPECT_CALL(*raw_mock_ranker, InsertCategoryBeforeIfNecessary(_, _)).Times(0);
EXPECT_CALL(*raw_mock_ranker,
AppendCategoryIfNecessary(Category::FromRemoteCategory(11)));
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
}
TEST_F(RemoteSuggestionsProviderImplTest, PersistCategoryInfos) {
- auto service = MakeSuggestionsProvider();
- // TODO(vitaliii): Use |articles_category()| instead of constant ID below.
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategoryWithCustomTitle(
- {GetSuggestionN(0)}, /*remote_category_id=*/1, "Articles for You")
- .AddCategoryWithCustomTitle({GetSuggestionN(1)},
- kUnknownRemoteCategoryId, "Other Things")
- .Build();
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("1"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(kUnknownRemoteCategoryId))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("2"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
ASSERT_EQ(observer().StatusForCategory(articles_category()),
CategoryStatus::AVAILABLE);
- ASSERT_EQ(observer().StatusForCategory(unknown_category()),
+ ASSERT_EQ(observer().StatusForCategory(
+ Category::FromRemoteCategory(kUnknownRemoteCategoryId)),
CategoryStatus::AVAILABLE);
CategoryInfo info_articles_before =
- service->GetCategoryInfo(articles_category());
- CategoryInfo info_unknown_before =
- service->GetCategoryInfo(unknown_category());
+ provider->GetCategoryInfo(articles_category());
+ CategoryInfo info_unknown_before = provider->GetCategoryInfo(
+ Category::FromRemoteCategory(kUnknownRemoteCategoryId));
- // Recreate the service to simulate a Chrome restart.
- ResetSuggestionsProvider(&service, /*set_empty_response=*/true);
+ // Recreate the provider to simulate a Chrome restart.
+ ResetSuggestionsProvider(&provider);
// The categories should have been restored.
ASSERT_NE(observer().StatusForCategory(articles_category()),
CategoryStatus::NOT_PROVIDED);
- ASSERT_NE(observer().StatusForCategory(unknown_category()),
+ ASSERT_NE(observer().StatusForCategory(
+ Category::FromRemoteCategory(kUnknownRemoteCategoryId)),
CategoryStatus::NOT_PROVIDED);
EXPECT_EQ(observer().StatusForCategory(articles_category()),
CategoryStatus::AVAILABLE);
- EXPECT_EQ(observer().StatusForCategory(unknown_category()),
+ EXPECT_EQ(observer().StatusForCategory(
+ Category::FromRemoteCategory(kUnknownRemoteCategoryId)),
CategoryStatus::AVAILABLE);
CategoryInfo info_articles_after =
- service->GetCategoryInfo(articles_category());
- CategoryInfo info_unknown_after =
- service->GetCategoryInfo(unknown_category());
+ provider->GetCategoryInfo(articles_category());
+ CategoryInfo info_unknown_after = provider->GetCategoryInfo(
+ Category::FromRemoteCategory(kUnknownRemoteCategoryId));
EXPECT_EQ(info_articles_before.title(), info_articles_after.title());
EXPECT_EQ(info_unknown_before.title(), info_unknown_after.title());
}
TEST_F(RemoteSuggestionsProviderImplTest, PersistRemoteCategoryOrder) {
- // We create a service with a normal ranker to store the order.
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)}, /*remote_category_id=*/11)
- .AddCategory({GetSuggestionN(1)}, /*remote_category_id=*/13)
- .AddCategory({GetSuggestionN(2)}, /*remote_category_id=*/12)
- .Build();
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
- LoadFromJSONString(service.get(), json_str);
-
- // We manually recreate the service to simulate Chrome restart and enforce a
- // mock ranker. The response is cleared to ensure that the order is not
- // fetched.
- SetUpFetchResponse("");
+ // We create a provider with a normal ranker to store the order.
+ auto provider = MakeSuggestionsProvider();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(11))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("11"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(13))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("13"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(12))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("12"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ // We manually recreate the provider to simulate Chrome restart and enforce a
+ // mock ranker.
auto mock_ranker = base::MakeUnique<MockCategoryRanker>();
MockCategoryRanker* raw_mock_ranker = mock_ranker.get();
SetCategoryRanker(std::move(mock_ranker));
+ // Ensure that the order is not fetched.
+ EXPECT_CALL(*mock_suggestions_fetcher(), FetchSnippets(_, _)).Times(0);
{
// The order of categories is determined by the order in which they are
// added. Thus, the latter is tested here.
@@ -896,24 +1048,34 @@ TEST_F(RemoteSuggestionsProviderImplTest, PersistRemoteCategoryOrder) {
EXPECT_CALL(*raw_mock_ranker,
AppendCategoryIfNecessary(Category::FromRemoteCategory(12)));
}
- ResetSuggestionsProvider(&service, /*set_empty_response=*/false);
+ ResetSuggestionsProvider(&provider);
}
TEST_F(RemoteSuggestionsProviderImplTest, PersistSuggestions) {
- auto service = MakeSuggestionsProvider();
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)}, /*remote_category_id=*/1)
- .AddCategory({GetSuggestionN(2)}, /*remote_category_id=*/2)
- .Build();
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(1))
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("1").SetRemoteCategoryId(1))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(2))
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("2").SetRemoteCategoryId(2))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
SizeIs(1));
ASSERT_THAT(observer().SuggestionsForCategory(other_category()), SizeIs(1));
- // Recreate the service to simulate a Chrome restart.
- ResetSuggestionsProvider(&service, /*set_empty_response=*/true);
+ // Recreate the provider to simulate a Chrome restart.
+ ResetSuggestionsProvider(&provider);
// The suggestions in both categories should have been restored.
EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
@@ -923,29 +1085,36 @@ TEST_F(RemoteSuggestionsProviderImplTest, PersistSuggestions) {
TEST_F(RemoteSuggestionsProviderImplTest, DontNotifyIfNotAvailable) {
// Get some suggestions into the database.
- auto service = MakeSuggestionsProvider();
- std::string json_str =
- MultiCategoryJsonBuilder()
- .AddCategory({GetSuggestionN(0)},
- /*remote_category_id=*/1)
- .AddCategory({GetSuggestionN(1)}, /*remote_category_id=*/2)
- .Build();
- LoadFromJSONString(service.get(), json_str);
+ auto provider = MakeSuggestionsProvider();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(1))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("1"))
+ .Build());
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(Category::FromRemoteCategory(2))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("2"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
SizeIs(1));
ASSERT_THAT(observer().SuggestionsForCategory(other_category()), SizeIs(1));
- service.reset();
+ provider.reset();
// Set the pref that disables remote suggestions.
pref_service()->SetBoolean(prefs::kEnableSnippets, false);
- // Recreate the service to simulate a Chrome start.
- ResetSuggestionsProvider(&service, /*set_empty_response=*/true);
+ // Recreate the provider to simulate a Chrome start.
+ ResetSuggestionsProvider(&provider);
ASSERT_THAT(RemoteSuggestionsProviderImpl::State::DISABLED,
- Eq(service->state_));
+ Eq(provider->state_));
// Now the observer should not have received any suggestions.
EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
@@ -954,294 +1123,336 @@ TEST_F(RemoteSuggestionsProviderImplTest, DontNotifyIfNotAvailable) {
}
TEST_F(RemoteSuggestionsProviderImplTest, Clear) {
- auto service = MakeSuggestionsProvider();
-
- std::string json_str(GetTestJson({GetSuggestion()}));
-
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("1"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
- service->ClearCachedSuggestions(articles_category());
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ provider->ClearCachedSuggestions(articles_category());
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
}
TEST_F(RemoteSuggestionsProviderImplTest, ReplaceSuggestions) {
- auto service = MakeSuggestionsProvider();
+ auto provider = MakeSuggestionsProvider();
std::string first("http://first");
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestionWithUrl(first)}));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- ElementsAre(IdEq(first)));
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId(first))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
+ ElementsAre(Pointee(Property(&RemoteSuggestion::id, first))));
std::string second("http://second");
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrl(second)}));
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId(second))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
// The suggestions loaded last replace all that was loaded previously.
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- ElementsAre(IdEq(second)));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
+ ElementsAre(Pointee(Property(&RemoteSuggestion::id, second))));
}
-TEST_F(RemoteSuggestionsProviderImplTest, LoadsAdditionalSuggestions) {
- auto service = MakeSuggestionsProvider();
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldResolveFetchedSuggestionThumbnail) {
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("id"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
+ ElementsAre(Pointee(Property(&RemoteSuggestion::id, "id"))));
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrl("http://first")}));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- ElementsAre(IdEq("http://first")));
+ image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
+ ServeImageCallback serve_one_by_one_image_callback =
+ base::Bind(&ServeOneByOneImage, &provider->GetImageFetcherForTesting());
+ EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(
+ Invoke(CreateFunctor(serve_one_by_one_image_callback))));
+
+ gfx::Image image = FetchImage(provider.get(), MakeArticleID("id"));
+ ASSERT_FALSE(image.IsEmpty());
+ EXPECT_EQ(1, image.Width());
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest, ShouldFetchMore) {
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("first"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
+ ElementsAre(Pointee(Property(&RemoteSuggestion::id, "first"))));
auto expect_only_second_suggestion_received =
base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
EXPECT_THAT(suggestions, SizeIs(1));
- EXPECT_THAT(suggestions[0].id().id_within_category(),
- Eq("http://second"));
+ EXPECT_THAT(suggestions[0].id().id_within_category(), Eq("second"));
});
- LoadMoreFromJSONString(service.get(), articles_category(),
- GetTestJson({GetSuggestionWithUrl("http://second")}),
- /*known_ids=*/std::set<std::string>(),
- expect_only_second_suggestion_received);
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("second"))
+ .Build());
+ FetchMoreTheseSuggestions(
+ provider.get(), articles_category(),
+ /*known_suggestion_ids=*/std::set<std::string>(),
+ /*fetch_done_callback=*/expect_only_second_suggestion_received,
+ Status(StatusCode::SUCCESS, "message"), std::move(fetched_categories));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldResolveFetchedMoreSuggestionThumbnail) {
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId("id"))
+ .Build());
+
+ auto assert_only_first_suggestion_received =
+ base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
+ ASSERT_THAT(suggestions, SizeIs(1));
+ ASSERT_THAT(suggestions[0].id().id_within_category(), Eq("id"));
+ });
+ FetchMoreTheseSuggestions(
+ provider.get(), articles_category(),
+ /*known_suggestion_ids=*/std::set<std::string>(),
+ /*fetch_done_callback=*/assert_only_first_suggestion_received,
+ Status(StatusCode::SUCCESS, "message"), std::move(fetched_categories));
- // Verify we can resolve the image of the new suggestions.
- ServeImageCallback cb =
- base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
- EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
- .Times(2)
- .WillRepeatedly(WithArgs<0, 2>(Invoke(CreateFunctor(cb))));
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
- gfx::Image image = FetchImage(service.get(), MakeArticleID("http://first"));
- EXPECT_FALSE(image.IsEmpty());
- EXPECT_EQ(1, image.Width());
+ ServeImageCallback serve_one_by_one_image_callback =
+ base::Bind(&ServeOneByOneImage, &provider->GetImageFetcherForTesting());
+ EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(
+ Invoke(CreateFunctor(serve_one_by_one_image_callback))));
- image = FetchImage(service.get(), MakeArticleID("http://second"));
- EXPECT_FALSE(image.IsEmpty());
+ gfx::Image image = FetchImage(provider.get(), MakeArticleID("id"));
+ ASSERT_FALSE(image.IsEmpty());
EXPECT_EQ(1, image.Width());
}
-// The tests TestMergingFetchedMoreSuggestionsFillup and
-// TestMergingFetchedMoreSuggestionsReplaceAll simulate the following user
-// story:
-// 1) fetch suggestions in NTP A
-// 2) fetch more suggestions in NTP A.
-// 3) open new NTP B: See the first 10 results from step 1).
-// 4) fetch more suggestions in NTP B. Make sure the results are independent
-// from step 2)
-// TODO(tschumann): Test step 4) on a higher level instead of peeking into the
-// internal 'dismissed' data. The proper check is to make sure we tell the
-// backend to exclude these suggestions.
+// Imagine that we have surfaces A and B. The user fetches more in A, this
+// should not add any suggestions to B.
TEST_F(RemoteSuggestionsProviderImplTest,
- FewMoreFetchedSuggestionsShouldNotInterfere) {
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrl("http://id-1"),
- GetSuggestionWithUrl("http://id-2"),
- GetSuggestionWithUrl("http://id-3"),
- GetSuggestionWithUrl("http://id-4"),
- GetSuggestionWithUrl("http://id-5"),
- GetSuggestionWithUrl("http://id-6"),
- GetSuggestionWithUrl("http://id-7"),
- GetSuggestionWithUrl("http://id-8"),
- GetSuggestionWithUrl("http://id-9"),
- GetSuggestionWithUrl("http://id-10")}));
- EXPECT_THAT(
- observer().SuggestionsForCategory(articles_category()),
- ElementsAre(
- IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"),
- IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"),
- IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"),
- IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"),
- IdWithinCategoryEq("http://id-9"),
- IdWithinCategoryEq("http://id-10")));
-
- auto expect_receiving_two_new_suggestions =
- base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
- ASSERT_THAT(suggestions, SizeIs(2));
- EXPECT_THAT(suggestions[0], IdWithinCategoryEq("http://more-id-1"));
- EXPECT_THAT(suggestions[1], IdWithinCategoryEq("http://more-id-2"));
- });
- LoadMoreFromJSONString(
- service.get(), articles_category(),
- GetTestJson({GetSuggestionWithUrl("http://more-id-1"),
- GetSuggestionWithUrl("http://more-id-2")}),
- /*known_ids=*/
- {"http://id-1", "http://id-2", "http://id-3", "http://id-4",
- "http://id-5", "http://id-6", "http://id-7", "http://id-8",
- "http://id-9", "http://id-10"},
- expect_receiving_two_new_suggestions);
-
- // Verify that the observer still has the old set.
- EXPECT_THAT(
- observer().SuggestionsForCategory(articles_category()),
- ElementsAre(
- IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"),
- IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"),
- IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"),
- IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"),
- IdWithinCategoryEq("http://id-9"),
- IdWithinCategoryEq("http://id-10")));
-
- // No interference from previous Fetch more: we can receive two other ones.
- expect_receiving_two_new_suggestions =
+ ShouldNotChangeSuggestionsInOtherSurfacesWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ // Fetch a suggestion.
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://old.com/"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ ElementsAre(Property(&ContentSuggestion::id,
+ MakeArticleID("http://old.com/"))));
+
+ // Now fetch more, but first prepare a response.
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://fetched-more.com/"))
+ .Build());
+
+ // The surface issuing the fetch more gets response via callback.
+ auto assert_receiving_one_new_suggestion =
base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
- ASSERT_THAT(suggestions, SizeIs(2));
- EXPECT_THAT(suggestions[0], IdWithinCategoryEq("http://more-id-3"));
- EXPECT_THAT(suggestions[1], IdWithinCategoryEq("http://more-id-4"));
+ ASSERT_THAT(suggestions, SizeIs(1));
+ ASSERT_THAT(suggestions[0].id().id_within_category(),
+ Eq("http://fetched-more.com/"));
});
- LoadMoreFromJSONString(
- service.get(), articles_category(),
- GetTestJson({GetSuggestionWithUrl("http://more-id-3"),
- GetSuggestionWithUrl("http://more-id-4")}),
- /*known_ids=*/
- {"http://id-1", "http://id-2", "http://id-3", "http://id-4",
- "http://id-5", "http://id-6", "http://id-7", "http://id-8",
- "http://id-9", "http://id-10"},
- expect_receiving_two_new_suggestions);
+ FetchMoreTheseSuggestions(
+ provider.get(), articles_category(),
+ /*known_suggestion_ids=*/{"http://old.com/"},
+ /*fetch_done_callback=*/assert_receiving_one_new_suggestion,
+ Status(StatusCode::SUCCESS, "message"), std::move(fetched_categories));
+
+ // Other surfaces should remain the same.
+ EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+ ElementsAre(Property(&ContentSuggestion::id,
+ MakeArticleID("http://old.com/"))));
}
+// Imagine that we have surfaces A and B. The user fetches more in A. This
+// should not affect the next fetch more in B, i.e. assuming the same server
+// response the same suggestions must be fetched in B if the user fetches more
+// there as well.
TEST_F(RemoteSuggestionsProviderImplTest,
- TenMoreFetchedSuggestionsShouldNotInterfere) {
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrl("http://id-1"),
- GetSuggestionWithUrl("http://id-2"),
- GetSuggestionWithUrl("http://id-3"),
- GetSuggestionWithUrl("http://id-4"),
- GetSuggestionWithUrl("http://id-5"),
- GetSuggestionWithUrl("http://id-6"),
- GetSuggestionWithUrl("http://id-7"),
- GetSuggestionWithUrl("http://id-8"),
- GetSuggestionWithUrl("http://id-9"),
- GetSuggestionWithUrl("http://id-10")}));
- EXPECT_THAT(
- observer().SuggestionsForCategory(articles_category()),
- ElementsAre(
- IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"),
- IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"),
- IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"),
- IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"),
- IdWithinCategoryEq("http://id-9"),
- IdWithinCategoryEq("http://id-10")));
-
- auto expect_receiving_ten_new_suggestions =
+ ShouldNotAffectFetchMoreInOtherSurfacesWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ // Fetch more on the surface A.
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(FetchedCategory(
+ articles_category(),
+ BuildRemoteCategoryInfo(base::UTF8ToUTF16("title"),
+ /*allow_fetching_more_results=*/true)));
+ fetched_categories[0].suggestions.push_back(
+ CreateTestRemoteSuggestion("http://fetched-more.com/"));
+
+ auto assert_receiving_one_new_suggestion =
base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
- EXPECT_THAT(suggestions,
- ElementsAre(IdWithinCategoryEq("http://more-id-1"),
- IdWithinCategoryEq("http://more-id-2"),
- IdWithinCategoryEq("http://more-id-3"),
- IdWithinCategoryEq("http://more-id-4"),
- IdWithinCategoryEq("http://more-id-5"),
- IdWithinCategoryEq("http://more-id-6"),
- IdWithinCategoryEq("http://more-id-7"),
- IdWithinCategoryEq("http://more-id-8"),
- IdWithinCategoryEq("http://more-id-9"),
- IdWithinCategoryEq("http://more-id-10")));
+ ASSERT_THAT(suggestions, SizeIs(1));
+ ASSERT_THAT(suggestions[0].id().id_within_category(),
+ Eq("http://fetched-more.com/"));
});
- LoadMoreFromJSONString(
- service.get(), articles_category(),
- GetTestJson({GetSuggestionWithUrl("http://more-id-1"),
- GetSuggestionWithUrl("http://more-id-2"),
- GetSuggestionWithUrl("http://more-id-3"),
- GetSuggestionWithUrl("http://more-id-4"),
- GetSuggestionWithUrl("http://more-id-5"),
- GetSuggestionWithUrl("http://more-id-6"),
- GetSuggestionWithUrl("http://more-id-7"),
- GetSuggestionWithUrl("http://more-id-8"),
- GetSuggestionWithUrl("http://more-id-9"),
- GetSuggestionWithUrl("http://more-id-10")}),
- /*known_ids=*/
- {"http://id-1", "http://id-2", "http://id-3", "http://id-4",
- "http://id-5", "http://id-6", "http://id-7", "http://id-8",
- "http://id-9", "http://id-10"},
- expect_receiving_ten_new_suggestions);
- EXPECT_THAT(
- observer().SuggestionsForCategory(articles_category()),
- ElementsAre(
- IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"),
- IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"),
- IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"),
- IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"),
- IdWithinCategoryEq("http://id-9"),
- IdWithinCategoryEq("http://id-10")));
-
- // This time, test receiving the same set.
- expect_receiving_ten_new_suggestions =
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback snippets_callback;
+ EXPECT_CALL(*mock_suggestions_fetcher(), FetchSnippets(_, _))
+ .WillOnce(MoveSecondArgumentPointeeTo(&snippets_callback))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ provider->Fetch(articles_category(),
+ /*known_suggestion_ids=*/std::set<std::string>(),
+ assert_receiving_one_new_suggestion);
+ std::move(snippets_callback)
+ .Run(Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ // Now fetch more on the surface B. The response is the same as before.
+ fetched_categories.clear();
+ fetched_categories.push_back(FetchedCategory(
+ articles_category(),
+ BuildRemoteCategoryInfo(base::UTF8ToUTF16("title"),
+ /*allow_fetching_more_results=*/true)));
+ fetched_categories[0].suggestions.push_back(
+ CreateTestRemoteSuggestion("http://fetched-more.com/"));
+
+ // B should receive the same suggestion as was fetched more on A.
+ auto expect_receiving_same_suggestion =
base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) {
- EXPECT_THAT(suggestions,
- ElementsAre(IdWithinCategoryEq("http://more-id-1"),
- IdWithinCategoryEq("http://more-id-2"),
- IdWithinCategoryEq("http://more-id-3"),
- IdWithinCategoryEq("http://more-id-4"),
- IdWithinCategoryEq("http://more-id-5"),
- IdWithinCategoryEq("http://more-id-6"),
- IdWithinCategoryEq("http://more-id-7"),
- IdWithinCategoryEq("http://more-id-8"),
- IdWithinCategoryEq("http://more-id-9"),
- IdWithinCategoryEq("http://more-id-10")));
+ ASSERT_THAT(suggestions, SizeIs(1));
+ EXPECT_THAT(suggestions[0].id().id_within_category(),
+ Eq("http://fetched-more.com/"));
});
- LoadMoreFromJSONString(
- service.get(), articles_category(),
- GetTestJson({GetSuggestionWithUrl("http://more-id-1"),
- GetSuggestionWithUrl("http://more-id-2"),
- GetSuggestionWithUrl("http://more-id-3"),
- GetSuggestionWithUrl("http://more-id-4"),
- GetSuggestionWithUrl("http://more-id-5"),
- GetSuggestionWithUrl("http://more-id-6"),
- GetSuggestionWithUrl("http://more-id-7"),
- GetSuggestionWithUrl("http://more-id-8"),
- GetSuggestionWithUrl("http://more-id-9"),
- GetSuggestionWithUrl("http://more-id-10")}),
- /*known_ids=*/
- {"http://id-1", "http://id-2", "http://id-3", "http://id-4",
- "http://id-5", "http://id-6", "http://id-7", "http://id-8",
- "http://id-9", "http://id-10"},
- expect_receiving_ten_new_suggestions);
+ // The provider should not ask the fetcher to exclude the suggestion fetched
+ // more on A.
+ EXPECT_CALL(*mock_suggestions_fetcher(),
+ FetchSnippets(Field(&RequestParams::excluded_ids,
+ Not(Contains("http://fetched-more.com/"))),
+ _))
+ .WillOnce(MoveSecondArgumentPointeeTo(&snippets_callback))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ provider->Fetch(articles_category(),
+ /*known_suggestion_ids=*/std::set<std::string>(),
+ expect_receiving_same_suggestion);
+ std::move(snippets_callback)
+ .Run(Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
}
TEST_F(RemoteSuggestionsProviderImplTest,
ClearHistoryShouldDeleteArchivedSuggestions) {
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
+ auto provider = MakeSuggestionsProvider();
// First get suggestions into the archived state which happens through
// subsequent fetches. Then we verify the entries are gone from the 'archived'
// state by trying to load their images (and we shouldn't even know the URLs
// anymore).
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrl("http://id-1"),
- GetSuggestionWithUrl("http://id-2")}));
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrl("http://new-id-1"),
- GetSuggestionWithUrl("http://new-id-2")}));
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://id-1"))
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://id-2"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://new-id-1"))
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://new-id-2"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
// Make sure images of both batches are available. This is to sanity check our
// assumptions for the test are right.
ServeImageCallback cb =
- base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
+ base::Bind(&ServeOneByOneImage, &provider->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
.Times(2)
.WillRepeatedly(WithArgs<0, 2>(Invoke(CreateFunctor(cb))));
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
- gfx::Image image = FetchImage(service.get(), MakeArticleID("http://id-1"));
+ gfx::Image image = FetchImage(provider.get(), MakeArticleID("http://id-1"));
ASSERT_FALSE(image.IsEmpty());
ASSERT_EQ(1, image.Width());
- image = FetchImage(service.get(), MakeArticleID("http://new-id-1"));
+ image = FetchImage(provider.get(), MakeArticleID("http://new-id-1"));
ASSERT_FALSE(image.IsEmpty());
ASSERT_EQ(1, image.Width());
- service->ClearHistory(base::Time::UnixEpoch(), base::Time::Max(),
- base::Callback<bool(const GURL& url)>());
+ provider->ClearHistory(base::Time::UnixEpoch(), base::Time::Max(),
+ base::Callback<bool(const GURL& url)>());
// Make sure images of both batches are gone.
// Verify we cannot resolve the image of the new suggestions.
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
EXPECT_TRUE(
- FetchImage(service.get(), MakeArticleID("http://id-1")).IsEmpty());
+ FetchImage(provider.get(), MakeArticleID("http://id-1")).IsEmpty());
EXPECT_TRUE(
- FetchImage(service.get(), MakeArticleID("http://new-id-1")).IsEmpty());
+ FetchImage(provider.get(), MakeArticleID("http://new-id-1")).IsEmpty());
}
-// TODO(tschumann): We don't have test making sure the RemoteSuggestionsFetcher
-// actually gets the proper parameters. Add tests with an injected
-// RemoteSuggestionsFetcher to verify the parameters, including proper handling
-// of dismissed and known_ids.
-
namespace {
// Workaround for gMock's lack of support for movable types.
@@ -1255,340 +1466,378 @@ void SuggestionsLoaded(
} // namespace
TEST_F(RemoteSuggestionsProviderImplTest, ReturnFetchRequestEmptyBeforeInit) {
- auto service = MakeSuggestionsProviderWithoutInitialization();
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback snippets_callback;
+ EXPECT_CALL(*mock_suggestions_fetcher(), FetchSnippets(_, _)).Times(0);
MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
- EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), IsEmpty()));
- service->Fetch(articles_category(), std::set<std::string>(),
- base::Bind(&SuggestionsLoaded, &loaded));
+ EXPECT_CALL(loaded, Call(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
+ IsEmpty()));
+ provider->Fetch(articles_category(), std::set<std::string>(),
+ base::Bind(&SuggestionsLoaded, &loaded));
base::RunLoop().RunUntilIdle();
}
-TEST_F(RemoteSuggestionsProviderImplTest, ReturnTemporaryErrorForInvalidJson) {
- auto service = MakeSuggestionsProvider();
-
- MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
- EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), IsEmpty()));
- LoadMoreFromJSONString(service.get(), articles_category(),
- "invalid json string}]}",
- /*known_ids=*/std::set<std::string>(),
- base::Bind(&SuggestionsLoaded, &loaded));
- EXPECT_THAT(suggestions_fetcher()->last_status(),
- StartsWith("Received invalid JSON"));
-}
-
-TEST_F(RemoteSuggestionsProviderImplTest,
- ReturnTemporaryErrorForInvalidSuggestion) {
- auto service = MakeSuggestionsProvider();
-
- MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
- EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), IsEmpty()));
- LoadMoreFromJSONString(service.get(), articles_category(),
- GetTestJson({GetIncompleteSuggestion()}),
- /*known_ids=*/std::set<std::string>(),
- base::Bind(&SuggestionsLoaded, &loaded));
- EXPECT_THAT(suggestions_fetcher()->last_status(),
- StartsWith("Invalid / empty list"));
-}
-
TEST_F(RemoteSuggestionsProviderImplTest,
- ReturnTemporaryErrorForRequestFailure) {
- // Created SuggestionsProvider will fail by default with unsuccessful request.
- auto service = MakeSuggestionsProvider(/*set_empty_response=*/false);
+ ShouldForwardTemporaryErrorFromFetcher) {
+ auto provider = MakeSuggestionsProvider();
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback snippets_callback;
MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
- EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), IsEmpty()));
- service->Fetch(articles_category(),
- /*known_ids=*/std::set<std::string>(),
- base::Bind(&SuggestionsLoaded, &loaded));
- base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(RemoteSuggestionsProviderImplTest, ReturnTemporaryErrorForHttpFailure) {
- auto service = MakeSuggestionsProvider();
- SetUpHttpError();
-
- MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
- EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), IsEmpty()));
- service->Fetch(articles_category(),
- /*known_ids=*/std::set<std::string>(),
- base::Bind(&SuggestionsLoaded, &loaded));
- base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(RemoteSuggestionsProviderImplTest, LoadInvalidJson) {
- auto service = MakeSuggestionsProvider();
-
- LoadFromJSONString(service.get(), GetTestJson({GetInvalidSuggestion()}));
- EXPECT_THAT(suggestions_fetcher()->last_status(),
- StartsWith("Received invalid JSON"));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- IsEmpty());
+ EXPECT_CALL(*mock_suggestions_fetcher(), FetchSnippets(_, _))
+ .WillOnce(MoveSecondArgumentPointeeTo(&snippets_callback));
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ provider->Fetch(articles_category(),
+ /*known_ids=*/std::set<std::string>(),
+ base::Bind(&SuggestionsLoaded, &loaded));
+
+ EXPECT_CALL(loaded, Call(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
+ IsEmpty()));
+ ASSERT_FALSE(snippets_callback.is_null());
+ std::move(snippets_callback)
+ .Run(Status(StatusCode::TEMPORARY_ERROR, "Received invalid JSON"),
+ base::nullopt);
}
TEST_F(RemoteSuggestionsProviderImplTest,
- LoadInvalidJsonWithExistingSuggestions) {
- auto service = MakeSuggestionsProvider();
-
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
- SizeIs(1));
- ASSERT_EQ("OK", suggestions_fetcher()->last_status());
-
- LoadFromJSONString(service.get(), GetTestJson({GetInvalidSuggestion()}));
- EXPECT_THAT(suggestions_fetcher()->last_status(),
- StartsWith("Received invalid JSON"));
- // This should not have changed the existing suggestions.
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- SizeIs(1));
-}
-
-TEST_F(RemoteSuggestionsProviderImplTest, LoadIncompleteJson) {
- auto service = MakeSuggestionsProvider();
-
- LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSuggestion()}));
- EXPECT_EQ("Invalid / empty list.", suggestions_fetcher()->last_status());
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ ShouldNotAddNewSuggestionsAfterFetchError) {
+ auto provider = MakeSuggestionsProvider();
+
+ FetchTheseSuggestions(
+ provider.get(), /*interactive_request=*/false,
+ Status(StatusCode::TEMPORARY_ERROR, "Received invalid JSON"),
+ base::nullopt);
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
}
TEST_F(RemoteSuggestionsProviderImplTest,
- LoadIncompleteJsonWithExistingSuggestions) {
- auto service = MakeSuggestionsProvider();
+ ShouldNotClearOldSuggestionsAfterFetchError) {
+ auto provider = MakeSuggestionsProvider();
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
- SizeIs(1));
-
- LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSuggestion()}));
- EXPECT_EQ("Invalid / empty list.", suggestions_fetcher()->last_status());
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(FetchedCategory(
+ articles_category(),
+ BuildRemoteCategoryInfo(base::UTF8ToUTF16("title"),
+ /*allow_fetching_more_results=*/true)));
+ fetched_categories[0].suggestions.push_back(
+ CreateTestRemoteSuggestion(base::StringPrintf("http://abc.com/")));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/false,
+ Status(StatusCode::SUCCESS, "success message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(
+ provider->GetSuggestionsForTesting(articles_category()),
+ ElementsAre(Pointee(Property(&RemoteSuggestion::id, "http://abc.com/"))));
+
+ FetchTheseSuggestions(
+ provider.get(), /*interactive_request=*/false,
+ Status(StatusCode::TEMPORARY_ERROR, "Received invalid JSON"),
+ base::nullopt);
// This should not have changed the existing suggestions.
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- SizeIs(1));
+ EXPECT_THAT(
+ provider->GetSuggestionsForTesting(articles_category()),
+ ElementsAre(Pointee(Property(&RemoteSuggestion::id, "http://abc.com/"))));
}
TEST_F(RemoteSuggestionsProviderImplTest, Dismiss) {
- auto service = MakeSuggestionsProvider();
-
- std::string json_str(GetTestJson(
- {GetSuggestionWithSources("http://site.com", "Source 1", "")}));
-
- LoadFromJSONString(service.get(), json_str);
-
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ const FetchedCategoryBuilder category_builder =
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://site.com"));
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
// Load the image to store it in the database.
ServeImageCallback cb =
- base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
+ base::Bind(&ServeOneByOneImage, &provider->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
.WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb))));
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
- gfx::Image image = FetchImage(service.get(), MakeArticleID(kSuggestionUrl));
+ gfx::Image image =
+ FetchImage(provider.get(), MakeArticleID("http://site.com"));
EXPECT_FALSE(image.IsEmpty());
EXPECT_EQ(1, image.Width());
// Dismissing a non-existent suggestion shouldn't do anything.
- service->DismissSuggestion(MakeArticleID("http://othersite.com"));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ provider->DismissSuggestion(MakeArticleID("http://othersite.com"));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
// Dismiss the suggestion.
- service->DismissSuggestion(MakeArticleID(kSuggestionUrl));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ provider->DismissSuggestion(MakeArticleID("http://site.com"));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
// Verify we can still load the image of the discarded suggestion (other NTPs
// might still reference it). This should come from the database -- no network
// fetch necessary.
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
- image = FetchImage(service.get(), MakeArticleID(kSuggestionUrl));
+ image = FetchImage(provider.get(), MakeArticleID("http://site.com"));
EXPECT_FALSE(image.IsEmpty());
EXPECT_EQ(1, image.Width());
// Make sure that fetching the same suggestion again does not re-add it.
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ fetched_categories.clear();
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
- // The suggestion should stay dismissed even after re-creating the service.
- ResetSuggestionsProvider(&service, /*set_empty_response=*/true);
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ // The suggestion should stay dismissed even after re-creating the provider.
+ ResetSuggestionsProvider(&provider);
+ fetched_categories.clear();
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
// The suggestion can be added again after clearing dismissed suggestions.
- service->ClearDismissedSuggestionsForDebugging(articles_category());
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ provider->ClearDismissedSuggestionsForDebugging(articles_category());
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ fetched_categories.clear();
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
}
TEST_F(RemoteSuggestionsProviderImplTest, GetDismissed) {
- auto service = MakeSuggestionsProvider();
-
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
-
- service->DismissSuggestion(MakeArticleID(kSuggestionUrl));
-
- service->GetDismissedSuggestionsForDebugging(
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://site.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ provider->DismissSuggestion(MakeArticleID("http://site.com"));
+
+ provider->GetDismissedSuggestionsForDebugging(
articles_category(),
base::Bind(
- [](RemoteSuggestionsProviderImpl* service,
+ [](RemoteSuggestionsProviderImpl* provider,
RemoteSuggestionsProviderImplTest* test,
std::vector<ContentSuggestion> dismissed_suggestions) {
EXPECT_EQ(1u, dismissed_suggestions.size());
for (auto& suggestion : dismissed_suggestions) {
- EXPECT_EQ(test->MakeArticleID(kSuggestionUrl), suggestion.id());
+ EXPECT_EQ(test->MakeArticleID("http://site.com"),
+ suggestion.id());
}
},
- service.get(), this));
+ provider.get(), this));
base::RunLoop().RunUntilIdle();
// There should be no dismissed suggestion after clearing the list.
- service->ClearDismissedSuggestionsForDebugging(articles_category());
- service->GetDismissedSuggestionsForDebugging(
+ provider->ClearDismissedSuggestionsForDebugging(articles_category());
+ provider->GetDismissedSuggestionsForDebugging(
articles_category(),
base::Bind(
- [](RemoteSuggestionsProviderImpl* service,
+ [](RemoteSuggestionsProviderImpl* provider,
RemoteSuggestionsProviderImplTest* test,
std::vector<ContentSuggestion> dismissed_suggestions) {
EXPECT_EQ(0u, dismissed_suggestions.size());
},
- service.get(), this));
+ provider.get(), this));
base::RunLoop().RunUntilIdle();
}
-TEST_F(RemoteSuggestionsProviderImplTest, CreationTimestampParseFail) {
- auto service = MakeSuggestionsProvider();
-
- std::string json = GetSuggestionWithTimes(GetDefaultCreationTime(),
- GetDefaultExpirationTime());
- base::ReplaceFirstSubstringAfterOffset(
- &json, 0, FormatTime(GetDefaultCreationTime()), "aaa1448459205");
- std::string json_str(GetTestJson({json}));
-
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- IsEmpty());
-}
-
TEST_F(RemoteSuggestionsProviderImplTest, RemoveExpiredDismissedContent) {
- auto service = MakeSuggestionsProvider();
-
- std::string json_str1(GetTestJson({GetExpiredSuggestion()}));
- // Load it.
- LoadFromJSONString(service.get(), json_str1);
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://first/")
+ .SetExpiryDate(base::Time::Now()))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
// Load the image to store it in the database.
// TODO(tschumann): Introduce some abstraction to nicely work with image
// fetching expectations.
ServeImageCallback cb =
- base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
+ base::Bind(&ServeOneByOneImage, &provider->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
.WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb))));
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
- gfx::Image image = FetchImage(service.get(), MakeArticleID(kSuggestionUrl));
+ gfx::Image image = FetchImage(provider.get(), MakeArticleID("http://first/"));
EXPECT_FALSE(image.IsEmpty());
EXPECT_EQ(1, image.Width());
// Dismiss the suggestion
- service->DismissSuggestion(
- ContentSuggestion::ID(articles_category(), kSuggestionUrl));
+ provider->DismissSuggestion(
+ ContentSuggestion::ID(articles_category(), "http://first/"));
// Load a different suggestion - this will clear the expired dismissed ones.
- std::string json_str2(GetTestJson({GetSuggestionWithUrl(kSuggestionUrl2)}));
- LoadFromJSONString(service.get(), json_str2);
-
- EXPECT_THAT(service->GetDismissedSuggestionsForTesting(articles_category()),
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://second/"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ EXPECT_THAT(provider->GetDismissedSuggestionsForTesting(articles_category()),
IsEmpty());
// Verify the image got removed, too.
EXPECT_TRUE(
- FetchImage(service.get(), MakeArticleID(kSuggestionUrl)).IsEmpty());
+ FetchImage(provider.get(), MakeArticleID("http://first/")).IsEmpty());
}
TEST_F(RemoteSuggestionsProviderImplTest, ExpiredContentNotRemoved) {
- auto service = MakeSuggestionsProvider();
-
- std::string json_str(GetTestJson({GetExpiredSuggestion()}));
-
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().SetExpiryDate(base::Time::Now()))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
}
TEST_F(RemoteSuggestionsProviderImplTest, TestSingleSource) {
- auto service = MakeSuggestionsProvider();
-
- std::string json_str(GetTestJson({GetSuggestionWithSources(
- "http://source1.com", "Source 1", "http://source1.amp.com")}));
-
- LoadFromJSONString(service.get(), json_str);
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://source1.com")
+ .SetUrl("http://source1.com")
+ .SetPublisher("Source 1")
+ .SetAmpUrl("http://source1.amp.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
const RemoteSuggestion& suggestion =
- *service->GetSuggestionsForTesting(articles_category()).front();
- EXPECT_EQ(suggestion.id(), kSuggestionUrl);
+ *provider->GetSuggestionsForTesting(articles_category()).front();
+ EXPECT_EQ(suggestion.id(), "http://source1.com");
EXPECT_EQ(suggestion.url(), GURL("http://source1.com"));
EXPECT_EQ(suggestion.publisher_name(), std::string("Source 1"));
EXPECT_EQ(suggestion.amp_url(), GURL("http://source1.amp.com"));
}
-TEST_F(RemoteSuggestionsProviderImplTest, TestSingleSourceWithMalformedUrl) {
- auto service = MakeSuggestionsProvider();
-
- std::string json_str(GetTestJson({GetSuggestionWithSources(
- "ceci n'est pas un url", "Source 1", "http://source1.amp.com")}));
-
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- IsEmpty());
-}
-
TEST_F(RemoteSuggestionsProviderImplTest, TestSingleSourceWithMissingData) {
- auto service = MakeSuggestionsProvider();
-
- std::string json_str(
- GetTestJson({GetSuggestionWithSources("http://source1.com", "", "")}));
-
- LoadFromJSONString(service.get(), json_str);
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().SetPublisher("").SetAmpUrl(""))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
}
TEST_F(RemoteSuggestionsProviderImplTest, LogNumArticlesHistogram) {
- auto service = MakeSuggestionsProvider();
+ auto provider = MakeSuggestionsProvider();
base::HistogramTester tester;
- LoadFromJSONString(service.get(), GetTestJson({GetInvalidSuggestion()}));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::TEMPORARY_ERROR, "message"),
+ base::nullopt);
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
-
- // Invalid JSON shouldn't contribute to NumArticlesFetched.
+ // Fetch error shouldn't contribute to NumArticlesFetched.
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"),
IsEmpty());
- // Valid JSON with empty list.
- LoadFromJSONString(service.get(), GetTestJson(std::vector<std::string>()));
+ // Emptry categories list.
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::vector<FetchedCategory>());
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/2)));
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"),
+ IsEmpty());
+
+ // Empty articles category.
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder().SetCategory(articles_category()).Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/3)));
+ EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
// Suggestion list should be populated with size 1.
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
+ const FetchedCategoryBuilder category_builder =
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://site.com/"));
+ fetched_categories.clear();
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"),
- ElementsAre(base::Bucket(/*min=*/0, /*count=*/2),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/3),
base::Bucket(/*min=*/1, /*count=*/1)));
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
base::Bucket(/*min=*/1, /*count=*/1)));
// Duplicate suggestion shouldn't increase the list size.
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
+ fetched_categories.clear();
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"),
- ElementsAre(base::Bucket(/*min=*/0, /*count=*/2),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/3),
base::Bucket(/*min=*/1, /*count=*/2)));
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
@@ -1599,10 +1848,14 @@ TEST_F(RemoteSuggestionsProviderImplTest, LogNumArticlesHistogram) {
// Dismissing a suggestion should decrease the list size. This will only be
// logged after the next fetch.
- service->DismissSuggestion(MakeArticleID(kSuggestionUrl));
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
+ provider->DismissSuggestion(MakeArticleID("http://site.com/"));
+ fetched_categories.clear();
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"),
- ElementsAre(base::Bucket(/*min=*/0, /*count=*/3),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/4),
base::Bucket(/*min=*/1, /*count=*/2)));
// Dismissed suggestions shouldn't influence NumArticlesFetched.
EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"),
@@ -1614,10 +1867,8 @@ TEST_F(RemoteSuggestionsProviderImplTest, LogNumArticlesHistogram) {
}
TEST_F(RemoteSuggestionsProviderImplTest, DismissShouldRespectAllKnownUrls) {
- auto service = MakeSuggestionsProvider();
+ auto provider = MakeSuggestionsProvider();
- const base::Time creation = GetDefaultCreationTime();
- const base::Time expiry = GetDefaultExpirationTime();
const std::vector<std::string> source_urls = {
"http://mashable.com/2016/05/11/stolen",
"http://www.aol.com/article/2016/05/stolen-doggie"};
@@ -1627,35 +1878,64 @@ TEST_F(RemoteSuggestionsProviderImplTest, DismissShouldRespectAllKnownUrls) {
"http://t2.gstatic.com/images?q=tbn:3"};
// Add the suggestion from the mashable domain.
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrlAndTimesAndSource(
- source_urls, source_urls[0], creation, expiry,
- publishers[0], amp_urls[0])}));
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId(source_urls[0])
+ .AddId(source_urls[1])
+ .SetUrl(source_urls[0])
+ .SetAmpUrl(amp_urls[0])
+ .SetPublisher(publishers[0]))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(1));
// Dismiss the suggestion via the mashable source corpus ID.
- service->DismissSuggestion(MakeArticleID(source_urls[0]));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ provider->DismissSuggestion(MakeArticleID(source_urls[0]));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
// The same article from the AOL domain should now be detected as dismissed.
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrlAndTimesAndSource(
- source_urls, source_urls[1], creation, expiry,
- publishers[1], amp_urls[1])}));
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId(source_urls[0])
+ .AddId(source_urls[1])
+ .SetUrl(source_urls[1])
+ .SetAmpUrl(amp_urls[1])
+ .SetPublisher(publishers[1]))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
IsEmpty());
}
TEST_F(RemoteSuggestionsProviderImplTest, ImageReturnedWithTheSameId) {
- auto service = MakeSuggestionsProvider();
-
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId(kSuggestionUrl))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
gfx::Image image;
MockFunction<void(const gfx::Image&)> image_fetched;
ServeImageCallback cb =
- base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
+ base::Bind(&ServeOneByOneImage, &provider->GetImageFetcherForTesting());
{
InSequence s;
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
@@ -1663,7 +1943,7 @@ TEST_F(RemoteSuggestionsProviderImplTest, ImageReturnedWithTheSameId) {
EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image));
}
- service->FetchSuggestionImage(
+ provider->FetchSuggestionImage(
MakeArticleID(kSuggestionUrl),
base::Bind(&MockFunction<void(const gfx::Image&)>::Call,
base::Unretained(&image_fetched)));
@@ -1673,15 +1953,15 @@ TEST_F(RemoteSuggestionsProviderImplTest, ImageReturnedWithTheSameId) {
}
TEST_F(RemoteSuggestionsProviderImplTest, EmptyImageReturnedForNonExistentId) {
- auto service = MakeSuggestionsProvider();
+ auto provider = MakeSuggestionsProvider();
// Create a non-empty image so that we can test the image gets updated.
gfx::Image image = gfx::test::CreateImage(1, 1);
MockFunction<void(const gfx::Image&)> image_fetched;
EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image));
- service->FetchSuggestionImage(
- MakeArticleID(kSuggestionUrl2),
+ provider->FetchSuggestionImage(
+ MakeArticleID("nonexistent"),
base::Bind(&MockFunction<void(const gfx::Image&)>::Call,
base::Unretained(&image_fetched)));
@@ -1694,7 +1974,7 @@ TEST_F(RemoteSuggestionsProviderImplTest,
// Testing that the provider is not accessing the database is tricky.
// Therefore, we simply put in some data making sure that if the provider asks
// the database, it will get a wrong answer.
- auto service = MakeSuggestionsProvider();
+ auto provider = MakeSuggestionsProvider();
ContentSuggestion::ID unknown_id = MakeArticleID(kSuggestionUrl2);
database()->SaveImage(unknown_id.id_within_category(), "some image blob");
@@ -1706,7 +1986,7 @@ TEST_F(RemoteSuggestionsProviderImplTest,
MockFunction<void(const gfx::Image&)> image_fetched;
EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image));
- service->FetchSuggestionImage(
+ provider->FetchSuggestionImage(
MakeArticleID(kSuggestionUrl2),
base::Bind(&MockFunction<void(const gfx::Image&)>::Call,
base::Unretained(&image_fetched)));
@@ -1716,30 +1996,38 @@ TEST_F(RemoteSuggestionsProviderImplTest,
}
TEST_F(RemoteSuggestionsProviderImplTest, ClearHistoryRemovesAllSuggestions) {
- auto service = MakeSuggestionsProvider();
-
- std::string first_suggestion = GetSuggestionWithUrl("http://url1.com");
- std::string second_suggestion = GetSuggestionWithUrl("http://url2.com");
- std::string json_str = GetTestJson({first_suggestion, second_suggestion});
- LoadFromJSONString(service.get(), json_str);
- ASSERT_THAT(service->GetSuggestionsForTesting(articles_category()),
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://first/"))
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://second/"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ ASSERT_THAT(provider->GetSuggestionsForTesting(articles_category()),
SizeIs(2));
- service->DismissSuggestion(MakeArticleID("http://url1.com"));
+ provider->DismissSuggestion(MakeArticleID("http://first/"));
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
Not(IsEmpty()));
- ASSERT_THAT(service->GetDismissedSuggestionsForTesting(articles_category()),
+ ASSERT_THAT(provider->GetDismissedSuggestionsForTesting(articles_category()),
SizeIs(1));
base::Time begin = base::Time::FromTimeT(123),
end = base::Time::FromTimeT(456);
base::Callback<bool(const GURL& url)> filter;
- service->ClearHistory(begin, end, filter);
+ provider->ClearHistory(begin, end, filter);
// Verify that the observer received the update with the empty data as well.
EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
IsEmpty());
- EXPECT_THAT(service->GetDismissedSuggestionsForTesting(articles_category()),
+ EXPECT_THAT(provider->GetDismissedSuggestionsForTesting(articles_category()),
IsEmpty());
}
@@ -1747,90 +2035,117 @@ TEST_F(RemoteSuggestionsProviderImplTest,
ShouldKeepArticlesCategoryAvailableAfterClearHistory) {
// If the provider marks that category as NOT_PROVIDED, then it won't be shown
// at all in the UI and the user cannot load new data :-/.
- auto service = MakeSuggestionsProvider();
+ auto provider = MakeSuggestionsProvider();
ASSERT_THAT(observer().StatusForCategory(articles_category()),
Eq(CategoryStatus::AVAILABLE));
- service->ClearHistory(base::Time::UnixEpoch(), base::Time::Max(),
- base::Callback<bool(const GURL& url)>());
+ provider->ClearHistory(base::Time::UnixEpoch(), base::Time::Max(),
+ base::Callback<bool(const GURL& url)>());
EXPECT_THAT(observer().StatusForCategory(articles_category()),
Eq(CategoryStatus::AVAILABLE));
}
TEST_F(RemoteSuggestionsProviderImplTest, ShouldClearOrphanedImagesOnRestart) {
- auto service = MakeSuggestionsProvider();
-
- LoadFromJSONString(service.get(), GetTestJson({GetSuggestion()}));
+ auto provider = MakeSuggestionsProvider();
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId(kSuggestionUrl))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
ServeImageCallback cb =
- base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
+ base::Bind(&ServeOneByOneImage, &provider->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _))
.WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb))));
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
- gfx::Image image = FetchImage(service.get(), MakeArticleID(kSuggestionUrl));
+ gfx::Image image = FetchImage(provider.get(), MakeArticleID(kSuggestionUrl));
EXPECT_EQ(1, image.Width());
EXPECT_FALSE(image.IsEmpty());
// Send new suggestion which don't include the suggestion referencing the
// image.
- LoadFromJSONString(service.get(),
- GetTestJson({GetSuggestionWithUrl(
- "http://something.com/pletely/unrelated")}));
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId(
+ "http://something.com/pletely/unrelated"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
// The image should still be available until a restart happens.
EXPECT_FALSE(
- FetchImage(service.get(), MakeArticleID(kSuggestionUrl)).IsEmpty());
- ResetSuggestionsProvider(&service, /*set_empty_response=*/true);
+ FetchImage(provider.get(), MakeArticleID(kSuggestionUrl)).IsEmpty());
+ ResetSuggestionsProvider(&provider);
// After the restart, the image should be garbage collected.
EXPECT_TRUE(
- FetchImage(service.get(), MakeArticleID(kSuggestionUrl)).IsEmpty());
+ FetchImage(provider.get(), MakeArticleID(kSuggestionUrl)).IsEmpty());
}
TEST_F(RemoteSuggestionsProviderImplTest,
ShouldHandleMoreThanMaxSuggestionsInResponse) {
- auto service = MakeSuggestionsProvider();
+ auto provider = MakeSuggestionsProvider();
- std::vector<std::string> suggestions;
- for (int i = 0; i < service->GetMaxSuggestionCountForTesting() + 1; ++i) {
- suggestions.push_back(GetSuggestionWithUrl(
+ std::vector<FetchedCategory> fetched_categories;
+ FetchedCategoryBuilder category_builder;
+ category_builder.SetCategory(articles_category());
+ for (int i = 0; i < provider->GetMaxSuggestionCountForTesting() + 1; ++i) {
+ category_builder.AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId(
base::StringPrintf("http://localhost/suggestion-id-%d", i)));
}
- LoadFromJSONString(service.get(), GetTestJson(suggestions));
+ fetched_categories.push_back(category_builder.Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
// TODO(tschumann): We should probably trim out any additional results and
// only serve the MaxSuggestionCount items.
- EXPECT_THAT(service->GetSuggestionsForTesting(articles_category()),
- SizeIs(service->GetMaxSuggestionCountForTesting() + 1));
+ EXPECT_THAT(provider->GetSuggestionsForTesting(articles_category()),
+ SizeIs(provider->GetMaxSuggestionCountForTesting() + 1));
}
TEST_F(RemoteSuggestionsProviderImplTest,
StoreLastSuccessfullBackgroundFetchTime) {
// On initialization of the RemoteSuggestionsProviderImpl a background fetch
- // is triggered since the suggestions DB is empty. Therefore the service must
+ // is triggered since the suggestions DB is empty. Therefore the provider must
// not be initialized until the test clock is set.
- auto service = MakeSuggestionsProviderWithoutInitialization();
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
auto simple_test_clock = base::MakeUnique<base::SimpleTestClock>();
base::SimpleTestClock* simple_test_clock_ptr = simple_test_clock.get();
- service->SetClockForTesting(std::move(simple_test_clock));
+ provider->SetClockForTesting(std::move(simple_test_clock));
// Test that the preference is correctly initialized with the default value 0.
EXPECT_EQ(
0, pref_service()->GetInt64(prefs::kLastSuccessfulBackgroundFetchTime));
- WaitForSuggestionsProviderInitialization(service.get(),
- /*set_empty_response=*/true);
+ WaitForSuggestionsProviderInitialization(provider.get());
EXPECT_EQ(
simple_test_clock_ptr->Now().ToInternalValue(),
pref_service()->GetInt64(prefs::kLastSuccessfulBackgroundFetchTime));
// Advance the time and check whether the time was updated correctly after the
// background fetch.
- simple_test_clock_ptr->Advance(TimeDelta::FromHours(1));
+ simple_test_clock_ptr->Advance(base::TimeDelta::FromHours(1));
- service->RefetchInTheBackground(
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback snippets_callback;
+ EXPECT_CALL(*mock_suggestions_fetcher(), FetchSnippets(_, _))
+ .WillOnce(MoveSecondArgumentPointeeTo(&snippets_callback))
+ .RetiresOnSaturation();
+ provider->RefetchInTheBackground(
RemoteSuggestionsProvider::FetchStatusCallback());
base::RunLoop().RunUntilIdle();
+ std::move(snippets_callback)
+ .Run(Status(StatusCode::SUCCESS, "message"), base::nullopt);
// TODO(jkrcal): Move together with the pref storage into the scheduler.
EXPECT_EQ(
simple_test_clock_ptr->Now().ToInternalValue(),
@@ -1840,26 +2155,25 @@ TEST_F(RemoteSuggestionsProviderImplTest,
}
TEST_F(RemoteSuggestionsProviderImplTest, CallsSchedulerWhenReady) {
- auto service =
+ auto provider =
MakeSuggestionsProviderWithoutInitializationWithStrictScheduler();
// Should be called when becoming ready.
EXPECT_CALL(*scheduler(), OnProviderActivated());
- WaitForSuggestionsProviderInitialization(service.get(),
- /*set_empty_response=*/true);
+ WaitForSuggestionsProviderInitialization(provider.get());
}
TEST_F(RemoteSuggestionsProviderImplTest, CallsSchedulerOnError) {
- auto service =
+ auto provider =
MakeSuggestionsProviderWithoutInitializationWithStrictScheduler();
// Should be called on error.
EXPECT_CALL(*scheduler(), OnProviderDeactivated());
- service->EnterState(RemoteSuggestionsProviderImpl::State::ERROR_OCCURRED);
+ provider->EnterState(RemoteSuggestionsProviderImpl::State::ERROR_OCCURRED);
}
TEST_F(RemoteSuggestionsProviderImplTest, CallsSchedulerWhenDisabled) {
- auto service =
+ auto provider =
MakeSuggestionsProviderWithoutInitializationWithStrictScheduler();
// Should be called when becoming disabled. First deactivate and only after
@@ -1867,52 +2181,736 @@ TEST_F(RemoteSuggestionsProviderImplTest, CallsSchedulerWhenDisabled) {
{
InSequence s;
EXPECT_CALL(*scheduler(), OnProviderDeactivated());
- ASSERT_THAT(service->ready(), Eq(false));
+ ASSERT_THAT(provider->ready(), Eq(false));
EXPECT_CALL(*scheduler(), OnSuggestionsCleared());
}
- service->EnterState(RemoteSuggestionsProviderImpl::State::DISABLED);
+ provider->EnterState(RemoteSuggestionsProviderImpl::State::DISABLED);
}
TEST_F(RemoteSuggestionsProviderImplTest, CallsSchedulerWhenHistoryCleared) {
- auto service =
+ auto provider =
MakeSuggestionsProviderWithoutInitializationWithStrictScheduler();
- // Initiate the service so that it is already READY.
+ // Initiate the provider so that it is already READY.
EXPECT_CALL(*scheduler(), OnProviderActivated());
- WaitForSuggestionsProviderInitialization(service.get(),
- /*set_empty_response=*/true);
+ WaitForSuggestionsProviderInitialization(provider.get());
// The scheduler should be notified of clearing the history.
EXPECT_CALL(*scheduler(), OnHistoryCleared());
- service->ClearHistory(GetDefaultCreationTime(), GetDefaultExpirationTime(),
- base::Callback<bool(const GURL& url)>());
+ provider->ClearHistory(GetDefaultCreationTime(), GetDefaultExpirationTime(),
+ base::Callback<bool(const GURL& url)>());
}
TEST_F(RemoteSuggestionsProviderImplTest, CallsSchedulerWhenSignedIn) {
- auto service =
+ auto provider =
MakeSuggestionsProviderWithoutInitializationWithStrictScheduler();
- // Initiate the service so that it is already READY.
+ // Initiate the provider so that it is already READY.
EXPECT_CALL(*scheduler(), OnProviderActivated());
- WaitForSuggestionsProviderInitialization(service.get(),
- /*set_empty_response=*/true);
+ WaitForSuggestionsProviderInitialization(provider.get());
// The scheduler should be notified of clearing the history.
EXPECT_CALL(*scheduler(), OnSuggestionsCleared());
- service->OnStatusChanged(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN,
- RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT);
+ provider->OnStatusChanged(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN,
+ RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT);
}
TEST_F(RemoteSuggestionsProviderImplTest, CallsSchedulerWhenSignedOut) {
- auto service =
+ auto provider =
MakeSuggestionsProviderWithoutInitializationWithStrictScheduler();
- // Initiate the service so that it is already READY.
+ // Initiate the provider so that it is already READY.
EXPECT_CALL(*scheduler(), OnProviderActivated());
- WaitForSuggestionsProviderInitialization(service.get(),
- /*set_empty_response=*/true);
+ WaitForSuggestionsProviderInitialization(provider.get());
// The scheduler should be notified of clearing the history.
EXPECT_CALL(*scheduler(), OnSuggestionsCleared());
- service->OnStatusChanged(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT,
- RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN);
+ provider->OnStatusChanged(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT,
+ RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN);
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldExcludeKnownSuggestionsWithoutTruncatingWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ std::set<std::string> known_ids;
+ for (int i = 0; i < 200; ++i) {
+ known_ids.insert(base::IntToString(i));
+ }
+
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_suggestions_fetcher(),
+ FetchSnippets(Field(&RequestParams::excluded_ids, known_ids), _));
+ provider->Fetch(
+ articles_category(), known_ids,
+ base::Bind([](Status status_code,
+ std::vector<ContentSuggestion> suggestions) {}));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldExcludeDismissedSuggestionsWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId("http://abc.com/"))
+ .Build());
+ ASSERT_TRUE(fetched_categories[0].suggestions[0]->is_complete());
+
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ provider->DismissSuggestion(MakeArticleID("http://abc.com/"));
+
+ std::set<std::string> expected_excluded_ids({"http://abc.com/"});
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ EXPECT_CALL(
+ *mock_suggestions_fetcher(),
+ FetchSnippets(Field(&RequestParams::excluded_ids, expected_excluded_ids),
+ _));
+ provider->Fetch(
+ articles_category(), std::set<std::string>(),
+ base::Bind([](Status status_code,
+ std::vector<ContentSuggestion> suggestions) {}));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldTruncateExcludedDismissedSuggestionsWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ std::vector<FetchedCategory> fetched_categories;
+ FetchedCategoryBuilder category_builder;
+ category_builder.SetCategory(articles_category());
+ const int kSuggestionsCount = kMaxExcludedDismissedIds + 1;
+ for (int i = 0; i < kSuggestionsCount; ++i) {
+ category_builder.AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId(
+ base::StringPrintf("http://abc.com/%d/", i)));
+ }
+ fetched_categories.push_back(category_builder.Build());
+
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ // Dismiss them.
+ for (int i = 0; i < kSuggestionsCount; ++i) {
+ provider->DismissSuggestion(
+ MakeArticleID(base::StringPrintf("http://abc.com/%d/", i)));
+ }
+
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_suggestions_fetcher(),
+ FetchSnippets(Field(&RequestParams::excluded_ids,
+ SizeIs(kMaxExcludedDismissedIds)),
+ _));
+ provider->Fetch(
+ articles_category(), std::set<std::string>(),
+ base::Bind([](Status status_code,
+ std::vector<ContentSuggestion> suggestions) {}));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldPreferLatestExcludedDismissedSuggestionsWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ std::vector<FetchedCategory> fetched_categories;
+ FetchedCategoryBuilder category_builder;
+ category_builder.SetCategory(articles_category());
+ const int kSuggestionsCount = kMaxExcludedDismissedIds + 1;
+ for (int i = 0; i < kSuggestionsCount; ++i) {
+ category_builder.AddSuggestionViaBuilder(RemoteSuggestionBuilder().AddId(
+ base::StringPrintf("http://abc.com/%d/", i)));
+ }
+ fetched_categories.push_back(category_builder.Build());
+
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ // Dismiss them in reverse order.
+ std::string first_dismissed_suggestion_id;
+ for (int i = kSuggestionsCount - 1; i >= 0; --i) {
+ const std::string id = base::StringPrintf("http://abc.com/%d/", i);
+ provider->DismissSuggestion(MakeArticleID(id));
+ if (first_dismissed_suggestion_id.empty()) {
+ first_dismissed_suggestion_id = id;
+ }
+ }
+
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ // The oldest dismissed suggestion should be absent, because there are
+ // |kMaxExcludedDismissedIds| newer dismissed suggestions.
+ EXPECT_CALL(*mock_suggestions_fetcher(),
+ FetchSnippets(Field(&RequestParams::excluded_ids,
+ Not(Contains(first_dismissed_suggestion_id))),
+ _));
+ provider->Fetch(
+ articles_category(), std::set<std::string>(),
+ base::Bind([](Status status_code,
+ std::vector<ContentSuggestion> suggestions) {}));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldExcludeDismissedSuggestionsFromAllCategoriesWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ // Add article suggestions.
+ std::vector<FetchedCategory> fetched_categories;
+ FetchedCategoryBuilder first_category_builder;
+ first_category_builder.SetCategory(articles_category());
+ const int kSuggestionsPerCategory = 2;
+ for (int i = 0; i < kSuggestionsPerCategory; ++i) {
+ first_category_builder.AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId(
+ base::StringPrintf("http://abc.com/%d/", i)));
+ }
+ fetched_categories.push_back(first_category_builder.Build());
+ // Add other category suggestions.
+ FetchedCategoryBuilder second_category_builder;
+ second_category_builder.SetCategory(other_category());
+ for (int i = 0; i < kSuggestionsPerCategory; ++i) {
+ second_category_builder.AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId(
+ base::StringPrintf("http://other.com/%d/", i)));
+ }
+ fetched_categories.push_back(second_category_builder.Build());
+
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ // Dismiss all suggestions.
+ std::set<std::string> expected_excluded_ids;
+ for (int i = 0; i < kSuggestionsPerCategory; ++i) {
+ const std::string article_id = base::StringPrintf("http://abc.com/%d/", i);
+ provider->DismissSuggestion(MakeArticleID(article_id));
+ expected_excluded_ids.insert(article_id);
+ const std::string other_id = base::StringPrintf("http://other.com/%d/", i);
+ provider->DismissSuggestion(MakeOtherID(other_id));
+ expected_excluded_ids.insert(other_id);
+ }
+
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ // Dismissed suggestions from all categories must be excluded (but not only
+ // target category).
+ EXPECT_CALL(
+ *mock_suggestions_fetcher(),
+ FetchSnippets(Field(&RequestParams::excluded_ids, expected_excluded_ids),
+ _));
+ provider->Fetch(
+ articles_category(), std::set<std::string>(),
+ base::Bind([](Status status_code,
+ std::vector<ContentSuggestion> suggestions) {}));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldPreferTargetCategoryExcludedDismissedSuggestionsWhenFetchingMore) {
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/false);
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ // Add article suggestions.
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(FetchedCategory(
+ articles_category(),
+ BuildRemoteCategoryInfo(base::UTF8ToUTF16("title"),
+ /*allow_fetching_more_results=*/true)));
+
+ for (int i = 0; i < kMaxExcludedDismissedIds; ++i) {
+ fetched_categories[0].suggestions.push_back(CreateTestRemoteSuggestion(
+ base::StringPrintf("http://abc.com/%d/", i)));
+ }
+ // Add other category suggestion.
+ fetched_categories.push_back(FetchedCategory(
+ other_category(),
+ BuildRemoteCategoryInfo(base::UTF8ToUTF16("title"),
+ /*allow_fetching_more_results=*/true)));
+ fetched_categories[1].suggestions.push_back(
+ CreateTestRemoteSuggestion("http://other.com/"));
+
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ // Dismiss article suggestions first.
+ for (int i = 0; i < kMaxExcludedDismissedIds; ++i) {
+ provider->DismissSuggestion(
+ MakeArticleID(base::StringPrintf("http://abc.com/%d/", i)));
+ }
+
+ // Then dismiss other category suggestion.
+ provider->DismissSuggestion(MakeOtherID("http://other.com/"));
+
+ EXPECT_CALL(*scheduler(), AcquireQuotaForInteractiveFetch())
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ // The other category dismissed suggestion should be absent, because the fetch
+ // is for articles and there are |kMaxExcludedDismissedIds| dismissed
+ // suggestions there.
+ EXPECT_CALL(*mock_suggestions_fetcher(),
+ FetchSnippets(Field(&RequestParams::excluded_ids,
+ Not(Contains("http://other.com/"))),
+ _));
+ provider->Fetch(
+ articles_category(), std::set<std::string>(),
+ base::Bind([](Status status_code,
+ std::vector<ContentSuggestion> suggestions) {}));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldFetchNormallyWithoutPrefetchedPagesTracker) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProvider();
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder())
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(1));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldKeepPrefetchedSuggestionsAfterFetchWhenEnabled) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/true);
+ auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+ prefetched_pages_tracker());
+ WaitForSuggestionsProviderInitialization(provider.get());
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://prefetched.com")
+ .SetUrl("http://prefetched.com")
+ .SetAmpUrl("http://amp.prefetched.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(1));
+
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_tracker,
+ PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com")))
+ .WillOnce(Return(true));
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://other.com")
+ .SetUrl("http://other.com")
+ .SetAmpUrl("http://amp.other.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ EXPECT_THAT(
+ observer().SuggestionsForCategory(articles_category()),
+ UnorderedElementsAre(
+ Property(&ContentSuggestion::id,
+ MakeArticleID("http://prefetched.com")),
+ Property(&ContentSuggestion::id, MakeArticleID("http://other.com"))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldIgnoreNotPrefetchedSuggestionsAfterFetchWhenEnabled) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/true);
+ auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+ prefetched_pages_tracker());
+ WaitForSuggestionsProviderInitialization(provider.get());
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder()
+ .AddId("http://not_prefetched.com")
+ .SetUrl("http://not_prefetched.com")
+ .SetAmpUrl("http://amp.not_prefetched.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(1));
+
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_tracker, PrefetchedOfflinePageExists(
+ GURL("http://amp.not_prefetched.com")))
+ .WillOnce(Return(false));
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://other.com")
+ .SetUrl("http://other.com")
+ .SetAmpUrl("http://amp.other.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+ UnorderedElementsAre(Property(
+ &ContentSuggestion::id, MakeArticleID("http://other.com"))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldLimitKeptPrefetchedSuggestionsAfterFetchWhenEnabled) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/true);
+ auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+ prefetched_pages_tracker());
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ const int prefetched_suggestions_count =
+ 2 * kMaxAdditionalPrefetchedSuggestions + 1;
+ std::vector<FetchedCategory> fetched_categories;
+ FetchedCategoryBuilder category_builder;
+ category_builder.SetCategory(articles_category());
+ for (int i = 0; i < prefetched_suggestions_count; ++i) {
+ const std::string url = base::StringPrintf("http://prefetched.com/%d", i);
+ category_builder.AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId(url).SetUrl(url).SetAmpUrl(
+ base::StringPrintf("http://amp.prefetched.com/%d", i)));
+ }
+ fetched_categories.push_back(category_builder.Build());
+
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(prefetched_suggestions_count));
+
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ for (int i = 0; i < prefetched_suggestions_count; ++i) {
+ EXPECT_CALL(*mock_tracker,
+ PrefetchedOfflinePageExists(GURL(
+ base::StringPrintf("http://amp.prefetched.com/%d", i))))
+ .WillOnce(Return(true));
+ }
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder()
+ .AddId("http://not_prefetched.com")
+ .SetUrl("http://not_prefetched.com")
+ .SetAmpUrl("http://amp.not_prefetched.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(kMaxAdditionalPrefetchedSuggestions + 1));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldMixInPrefetchedSuggestionsByScoreAfterFetchWhenEnabled) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/true);
+ auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+ prefetched_pages_tracker());
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://prefetched.com/1")
+ .SetUrl("http://prefetched.com/1")
+ .SetAmpUrl("http://amp.prefetched.com/1")
+ .SetScore(1))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://prefetched.com/3")
+ .SetUrl("http://prefetched.com/3")
+ .SetAmpUrl("http://amp.prefetched.com/3")
+ .SetScore(3))
+ .Build());
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(2));
+
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://new.com/2")
+ .SetUrl("http://new.com/2")
+ .SetAmpUrl("http://amp.new.com/2")
+ .SetScore(2))
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://new.com/4")
+ .SetUrl("http://new.com/4")
+ .SetAmpUrl("http://amp.new.com/4")
+ .SetScore(4))
+ .Build());
+
+ EXPECT_CALL(*mock_tracker,
+ PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com/1")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_tracker,
+ PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com/3")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ EXPECT_THAT(
+ observer().SuggestionsForCategory(articles_category()),
+ ElementsAre(
+ Property(&ContentSuggestion::id, MakeArticleID("http://new.com/4")),
+ Property(&ContentSuggestion::id,
+ MakeArticleID("http://prefetched.com/3")),
+ Property(&ContentSuggestion::id, MakeArticleID("http://new.com/2")),
+ Property(&ContentSuggestion::id,
+ MakeArticleID("http://prefetched.com/1"))));
+}
+
+TEST_F(
+ RemoteSuggestionsProviderImplTest,
+ ShouldKeepMostRecentlyFetchedPrefetchedSuggestionsFirstAfterFetchWhenEnabled) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/true);
+ auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+ prefetched_pages_tracker());
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ std::vector<FetchedCategory> fetched_categories;
+ const int prefetched_suggestions_count =
+ 2 * kMaxAdditionalPrefetchedSuggestions + 1;
+ for (int i = 0; i < prefetched_suggestions_count; ++i) {
+ const std::string url = base::StringPrintf("http://prefetched.com/%d", i);
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder().AddId(url).SetUrl(url).SetAmpUrl(
+ base::StringPrintf("http://amp.prefetched.com/%d", i)))
+ .Build());
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ if (i != 0) {
+ EXPECT_CALL(*mock_tracker,
+ PrefetchedOfflinePageExists(GURL(base::StringPrintf(
+ "http://amp.prefetched.com/%d", i - 1))))
+ .WillRepeatedly(Return(true));
+ }
+
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ }
+
+ const std::vector<ContentSuggestion>& actual_suggestions =
+ observer().SuggestionsForCategory(articles_category());
+
+ ASSERT_THAT(actual_suggestions,
+ SizeIs(kMaxAdditionalPrefetchedSuggestions + 1));
+
+ int matched = 0;
+ for (int i = prefetched_suggestions_count - 1; i >= 0; --i) {
+ EXPECT_THAT(actual_suggestions,
+ Contains(Property(&ContentSuggestion::id,
+ MakeArticleID(base::StringPrintf(
+ "http://prefetched.com/%d", i)))));
+ ++matched;
+ if (matched == kMaxAdditionalPrefetchedSuggestions + 1) {
+ break;
+ }
+ }
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldNotKeepStalePrefetchedSuggestionsAfterFetchWhenEnabled) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/true);
+ auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+ prefetched_pages_tracker());
+
+ auto wrapped_provider_clock = base::MakeUnique<base::SimpleTestClock>();
+ base::SimpleTestClock* provider_clock = wrapped_provider_clock.get();
+ provider->SetClockForTesting(std::move(wrapped_provider_clock));
+
+ provider_clock->SetNow(GetDefaultCreationTime() +
+ base::TimeDelta::FromHours(10));
+
+ WaitForSuggestionsProviderInitialization(provider.get());
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder()
+ .AddId("http://prefetched.com")
+ .SetUrl("http://prefetched.com")
+ .SetAmpUrl("http://amp.prefetched.com")
+ .SetFetchDate(provider_clock->Now())
+ .SetPublishDate(GetDefaultCreationTime()))
+ .Build());
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(1));
+
+ provider_clock->Advance(kMaxAgeForAdditionalPrefetchedSuggestion -
+ base::TimeDelta::FromSeconds(1));
+
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder()
+ .AddId("http://other.com")
+ .SetUrl("http://other.com")
+ .SetAmpUrl("http://amp.other.com")
+ .SetFetchDate(provider_clock->Now())
+ .SetPublishDate(GetDefaultCreationTime()))
+ .Build());
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_tracker,
+ PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com")))
+ .WillOnce(Return(true));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(2));
+
+ provider_clock->Advance(base::TimeDelta::FromSeconds(2));
+
+ fetched_categories.clear();
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(
+ RemoteSuggestionBuilder()
+ .AddId("http://other.com")
+ .SetUrl("http://other.com")
+ .SetAmpUrl("http://amp.other.com")
+ .SetFetchDate(provider_clock->Now())
+ .SetPublishDate(GetDefaultCreationTime()))
+ .Build());
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_tracker,
+ PrefetchedOfflinePageExists(GURL("http://amp.prefetched.com")))
+ .WillOnce(Return(true));
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+ ElementsAre(Property(&ContentSuggestion::id,
+ MakeArticleID("http://other.com"))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ShouldWaitForPrefetchedPagesTrackerInitialization) {
+ EnableKeepingPrefetchedContentSuggestions(
+ kMaxAdditionalPrefetchedSuggestions,
+ kMaxAgeForAdditionalPrefetchedSuggestion);
+
+ auto provider = MakeSuggestionsProviderWithoutInitialization(
+ /*use_mock_prefetched_pages_tracker=*/true);
+ auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
+ prefetched_pages_tracker());
+ WaitForSuggestionsProviderInitialization(provider.get());
+
+ base::OnceCallback<void()> initialization_completed_callback;
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_tracker, AddInitializationCompletedCallback(_))
+ .WillOnce(MoveFirstArgumentPointeeTo(&initialization_completed_callback));
+ std::vector<FetchedCategory> fetched_categories;
+ fetched_categories.push_back(
+ FetchedCategoryBuilder()
+ .SetCategory(articles_category())
+ .AddSuggestionViaBuilder(RemoteSuggestionBuilder()
+ .AddId("http://prefetched.com")
+ .SetUrl("http://prefetched.com")
+ .SetAmpUrl("http://amp.prefetched.com"))
+ .Build());
+ FetchTheseSuggestions(provider.get(), /*interactive_request=*/true,
+ Status(StatusCode::SUCCESS, "message"),
+ std::move(fetched_categories));
+ EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(0));
+
+ EXPECT_CALL(*mock_tracker, IsInitialized()).WillRepeatedly(Return(true));
+ std::move(initialization_completed_callback).Run();
+ EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+ SizeIs(1));
}
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler.h b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler.h
index 6af62879fc8..78d34176168 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler.h
@@ -56,15 +56,15 @@ class RemoteSuggestionsScheduler {
// To keep start ups fast, defer any work possible.
virtual void OnBrowserColdStart() = 0;
- // Called whenever a new NTP is opened. This may be called on cold starts.
- // So to keep start ups fast, defer heavy work for cold starts.
- virtual void OnNTPOpened() = 0;
+ // Called whenever a new suggestions surface is opened. This may be called on
+ // cold starts. So to keep start ups fast, defer heavy work for cold starts.
+ virtual void OnSuggestionsSurfaceOpened() = 0;
- // Fetch content suggestions.
+ // Called by PersistentScheduler implementation whenever it wakes up according
+ // to its schedule. Avoid heavy work, Chrome may be running in the background.
virtual void OnPersistentSchedulerWakeUp() = 0;
- // Force rescheduling of fetching.
- virtual void RescheduleFetching() = 0;
+ virtual void OnBrowserUpgraded() = 0;
};
} // namespace ntp_snippets
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 193628a55e4..064a30d3f46 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
@@ -4,11 +4,13 @@
#include "components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h"
+#include <cfloat>
#include <random>
#include <string>
#include <utility>
#include "base/bind.h"
+#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
@@ -62,11 +64,23 @@ enum class FetchingInterval {
// defined by the enum FetchingInterval. The default time intervals defined in
// the arrays can be overridden using different variation parameters.
const double kDefaultFetchingIntervalHoursRareNtpUser[] = {192.0, 96.0, 48.0,
- 24.0, 12.0, 8.0};
-const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {96.0, 48.0, 48.0,
- 24.0, 12.0, 8.0};
+ 48.0, 10.0, 10.0};
+const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {96.0, 48.0, 24.0,
+ 24.0, 10.0, 10.0};
const double kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[] = {
- 48.0, 24.0, 24.0, 6.0, 2.0, 1.0};
+ 48.0, 24.0, 12.0, 12.0, 1.0, 1.0};
+
+// For a simple comparision: fetching intervals that emulate the state really
+// rolled out to 100% M58 Stable. Used for evaluation of later changes. DBL_MAX
+// values simulate this interval being disabled.
+// TODO(jkrcal): Remove when not needed any more, incl. the feature. Probably
+// after M62 when CH is launched.
+const double kM58FetchingIntervalHoursRareNtpUser[] = {48.0, 24.0, DBL_MAX,
+ DBL_MAX, 4.0, 4.0};
+const double kM58FetchingIntervalHoursActiveNtpUser[] = {24.0, 8.0, DBL_MAX,
+ DBL_MAX, 10.0, 10.0};
+const double kM58FetchingIntervalHoursActiveSuggestionsConsumer[] = {
+ 24.0, 6.0, DBL_MAX, DBL_MAX, 1.0, 1.0};
// Variation parameters than can be used to override the default fetching
// intervals. For backwards compatibility, we do not rename
@@ -102,6 +116,12 @@ static_assert(
static_cast<unsigned int>(FetchingInterval::COUNT) ==
arraysize(kDefaultFetchingIntervalHoursActiveSuggestionsConsumer) &&
static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kM58FetchingIntervalHoursRareNtpUser) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kM58FetchingIntervalHoursActiveNtpUser) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kM58FetchingIntervalHoursActiveSuggestionsConsumer) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
arraysize(kFetchingIntervalParamNameRareNtpUser) &&
static_cast<unsigned int>(FetchingInterval::COUNT) ==
arraysize(kFetchingIntervalParamNameActiveNtpUser) &&
@@ -109,6 +129,8 @@ static_assert(
arraysize(kFetchingIntervalParamNameActiveSuggestionsConsumer),
"Fill in all the info for fetching intervals.");
+// For backward compatibility "ntp_opened" value is kept and denotes the
+// SURFACE_OPENED trigger type.
const char* kTriggerTypeNames[] = {"persistent_scheduler_wake_up", "ntp_opened",
"browser_foregrounded",
"browser_cold_start"};
@@ -132,20 +154,29 @@ base::TimeDelta GetDesiredFetchingInterval(
const unsigned int index = static_cast<unsigned int>(interval);
DCHECK(index < arraysize(kDefaultFetchingIntervalHoursRareNtpUser));
+ bool emulateM58 = base::FeatureList::IsEnabled(
+ kRemoteSuggestionsEmulateM58FetchingSchedule);
+
double default_value_hours = 0.0;
const char* param_name = nullptr;
switch (user_class) {
case UserClassifier::UserClass::RARE_NTP_USER:
- default_value_hours = kDefaultFetchingIntervalHoursRareNtpUser[index];
+ default_value_hours =
+ emulateM58 ? kM58FetchingIntervalHoursRareNtpUser[index]
+ : kDefaultFetchingIntervalHoursRareNtpUser[index];
param_name = kFetchingIntervalParamNameRareNtpUser[index];
break;
case UserClassifier::UserClass::ACTIVE_NTP_USER:
- default_value_hours = kDefaultFetchingIntervalHoursActiveNtpUser[index];
+ default_value_hours =
+ emulateM58 ? kM58FetchingIntervalHoursActiveNtpUser[index]
+ : kDefaultFetchingIntervalHoursActiveNtpUser[index];
param_name = kFetchingIntervalParamNameActiveNtpUser[index];
break;
case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
default_value_hours =
- kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[index];
+ emulateM58
+ ? kM58FetchingIntervalHoursActiveSuggestionsConsumer[index]
+ : kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[index];
param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index];
break;
}
@@ -157,29 +188,62 @@ base::TimeDelta GetDesiredFetchingInterval(
return base::TimeDelta::FromSecondsD(value_hours * 3600.0);
}
-void ReportTimeUntilFirstSoftTrigger(UserClassifier::UserClass user_class,
- base::TimeDelta time_until_first_trigger) {
+void ReportTimeUntilFirstShownTrigger(
+ UserClassifier::UserClass user_class,
+ base::TimeDelta time_until_first_shown_trigger) {
+ switch (user_class) {
+ case UserClassifier::UserClass::RARE_NTP_USER:
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "NewTabPage.ContentSuggestions.TimeUntilFirstShownTrigger."
+ "RareNTPUser",
+ time_until_first_shown_trigger, base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromDays(7),
+ /*bucket_count=*/50);
+ break;
+ case UserClassifier::UserClass::ACTIVE_NTP_USER:
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "NewTabPage.ContentSuggestions.TimeUntilFirstShownTrigger."
+ "ActiveNTPUser",
+ time_until_first_shown_trigger, base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromDays(7),
+ /*bucket_count=*/50);
+ break;
+ case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "NewTabPage.ContentSuggestions.TimeUntilFirstShownTrigger."
+ "ActiveSuggestionsConsumer",
+ time_until_first_shown_trigger, base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromDays(7),
+ /*bucket_count=*/50);
+ break;
+ }
+}
+
+void ReportTimeUntilFirstStartupTrigger(
+ UserClassifier::UserClass user_class,
+ base::TimeDelta time_until_first_startup_trigger) {
switch (user_class) {
case UserClassifier::UserClass::RARE_NTP_USER:
UMA_HISTOGRAM_CUSTOM_TIMES(
- "NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger.RareNTPUser",
- time_until_first_trigger, base::TimeDelta::FromSeconds(1),
+ "NewTabPage.ContentSuggestions.TimeUntilFirstStartupTrigger."
+ "RareNTPUser",
+ time_until_first_startup_trigger, base::TimeDelta::FromSeconds(1),
base::TimeDelta::FromDays(7),
/*bucket_count=*/50);
break;
case UserClassifier::UserClass::ACTIVE_NTP_USER:
UMA_HISTOGRAM_CUSTOM_TIMES(
- "NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger."
+ "NewTabPage.ContentSuggestions.TimeUntilFirstStartupTrigger."
"ActiveNTPUser",
- time_until_first_trigger, base::TimeDelta::FromSeconds(1),
+ time_until_first_startup_trigger, base::TimeDelta::FromSeconds(1),
base::TimeDelta::FromDays(7),
/*bucket_count=*/50);
break;
case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
UMA_HISTOGRAM_CUSTOM_TIMES(
- "NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger."
+ "NewTabPage.ContentSuggestions.TimeUntilFirstStartupTrigger."
"ActiveSuggestionsConsumer",
- time_until_first_trigger, base::TimeDelta::FromSeconds(1),
+ time_until_first_startup_trigger, base::TimeDelta::FromSeconds(1),
base::TimeDelta::FromDays(7),
/*bucket_count=*/50);
break;
@@ -356,7 +420,7 @@ bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::is_empty() const {
// |kTriggerTypeNames| above.
enum class RemoteSuggestionsSchedulerImpl::TriggerType {
PERSISTENT_SCHEDULER_WAKE_UP = 0,
- NTP_OPENED = 1,
+ SURFACE_OPENED = 1,
BROWSER_FOREGROUNDED = 2,
BROWSER_COLD_START = 3,
COUNT
@@ -384,7 +448,8 @@ RemoteSuggestionsSchedulerImpl::RemoteSuggestionsSchedulerImpl(
profile_prefs,
RequestThrottler::RequestType::
CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER),
- time_until_first_trigger_reported_(false),
+ time_until_first_shown_trigger_reported_(false),
+ time_until_first_startup_trigger_reported_(false),
eula_state_(base::MakeUnique<EulaState>(local_state_prefs, this)),
profile_prefs_(profile_prefs),
clock_(std::move(clock)),
@@ -454,8 +519,9 @@ void RemoteSuggestionsSchedulerImpl::OnHistoryCleared() {
ClearLastFetchAttemptTime();
}
-void RemoteSuggestionsSchedulerImpl::RescheduleFetching() {
- // Force the reschedule by stopping and starting it again.
+void RemoteSuggestionsSchedulerImpl::OnBrowserUpgraded() {
+ // After browser upgrade, persistent schedule needs to get reset. Force the
+ // reschedule by stopping and starting it again.
StopScheduling();
StartScheduling();
}
@@ -486,10 +552,10 @@ void RemoteSuggestionsSchedulerImpl::OnBrowserColdStart() {
RefetchInTheBackgroundIfAppropriate(TriggerType::BROWSER_COLD_START);
}
-void RemoteSuggestionsSchedulerImpl::OnNTPOpened() {
+void RemoteSuggestionsSchedulerImpl::OnSuggestionsSurfaceOpened() {
// TODO(jkrcal): Consider that this is called whenever we open an NTP.
// Therefore, keep work light for fast start up calls.
- RefetchInTheBackgroundIfAppropriate(TriggerType::NTP_OPENED);
+ RefetchInTheBackgroundIfAppropriate(TriggerType::SURFACE_OPENED);
}
void RemoteSuggestionsSchedulerImpl::StartScheduling() {
@@ -589,21 +655,36 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate(
return;
}
+ if (net::NetworkChangeNotifier::IsOffline()) {
+ // Do not let a request fail due to lack of internet connection. Then, such
+ // a failure would get logged and further requests would be blocked for a
+ // while (even after becoming online).
+ return;
+ }
+
if (BackgroundFetchesDisabled(trigger)) {
return;
}
- bool is_soft = trigger != TriggerType::PERSISTENT_SCHEDULER_WAKE_UP;
const base::Time last_fetch_attempt_time = base::Time::FromInternalValue(
profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttempt));
- if (is_soft && !time_until_first_trigger_reported_) {
- time_until_first_trigger_reported_ = true;
- ReportTimeUntilFirstSoftTrigger(user_classifier_->GetUserClass(),
- clock_->Now() - last_fetch_attempt_time);
+ if (trigger == TriggerType::SURFACE_OPENED &&
+ !time_until_first_shown_trigger_reported_) {
+ time_until_first_shown_trigger_reported_ = true;
+ ReportTimeUntilFirstShownTrigger(user_classifier_->GetUserClass(),
+ clock_->Now() - last_fetch_attempt_time);
+ }
+
+ if ((trigger == TriggerType::BROWSER_FOREGROUNDED ||
+ trigger == TriggerType::BROWSER_COLD_START) &&
+ !time_until_first_startup_trigger_reported_) {
+ time_until_first_startup_trigger_reported_ = true;
+ ReportTimeUntilFirstStartupTrigger(user_classifier_->GetUserClass(),
+ clock_->Now() - last_fetch_attempt_time);
}
- if (is_soft &&
+ if (trigger != TriggerType::PERSISTENT_SCHEDULER_WAKE_UP &&
!ShouldRefetchInTheBackgroundNow(last_fetch_attempt_time, trigger)) {
return;
}
@@ -617,7 +698,7 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate(
case TriggerType::PERSISTENT_SCHEDULER_WAKE_UP:
ReportTimeUntilPersistentFetch(user_classifier_->GetUserClass(), diff);
break;
- case TriggerType::NTP_OPENED:
+ case TriggerType::SURFACE_OPENED:
ReportTimeUntilShownFetch(user_classifier_->GetUserClass(), diff);
break;
case TriggerType::BROWSER_FOREGROUNDED:
@@ -648,7 +729,7 @@ bool RemoteSuggestionsSchedulerImpl::ShouldRefetchInTheBackgroundNow(
}
base::Time first_allowed_fetch_time = last_fetch_attempt_time;
- if (trigger == TriggerType::NTP_OPENED) {
+ if (trigger == TriggerType::SURFACE_OPENED) {
first_allowed_fetch_time += (wifi ? schedule_.interval_shown_wifi
: schedule_.interval_shown_fallback);
} else {
@@ -707,7 +788,8 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundFinished(
void RemoteSuggestionsSchedulerImpl::OnFetchCompleted(Status fetch_status) {
profile_prefs_->SetInt64(prefs::kSnippetLastFetchAttempt,
clock_->Now().ToInternalValue());
- time_until_first_trigger_reported_ = false;
+ time_until_first_shown_trigger_reported_ = false;
+ time_until_first_startup_trigger_reported_ = false;
// Reschedule after a fetch. The persistent schedule is applied only after a
// successful fetch. After a failed fetch, we want to keep the previous
@@ -764,8 +846,9 @@ RemoteSuggestionsSchedulerImpl::GetEnabledTriggerTypes() {
std::set<RemoteSuggestionsSchedulerImpl::TriggerType>
RemoteSuggestionsSchedulerImpl::GetDefaultEnabledTriggerTypes() {
- return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP, TriggerType::NTP_OPENED,
- TriggerType::BROWSER_COLD_START, TriggerType::BROWSER_FOREGROUNDED};
+ return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP,
+ TriggerType::SURFACE_OPENED, TriggerType::BROWSER_COLD_START,
+ TriggerType::BROWSER_FOREGROUNDED};
}
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h
index 3e103624bec..6c6e63df23b 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h
@@ -52,13 +52,13 @@ class RemoteSuggestionsSchedulerImpl : public RemoteSuggestionsScheduler {
void OnProviderDeactivated() override;
void OnSuggestionsCleared() override;
void OnHistoryCleared() override;
- void RescheduleFetching() override;
+ void OnBrowserUpgraded() override;
bool AcquireQuotaForInteractiveFetch() override;
void OnInteractiveFetchFinished(Status fetch_status) override;
void OnPersistentSchedulerWakeUp() override;
void OnBrowserForegrounded() override;
void OnBrowserColdStart() override;
- void OnNTPOpened() override;
+ void OnSuggestionsSurfaceOpened() override;
private:
// Abstract description of the fetching schedule. See the enum
@@ -147,8 +147,10 @@ class RemoteSuggestionsSchedulerImpl : public RemoteSuggestionsScheduler {
RequestThrottler request_throttler_active_ntp_user_;
RequestThrottler request_throttler_active_suggestions_consumer_;
- // To make sure we only report the first trigger to UMA.
- bool time_until_first_trigger_reported_;
+ // Variables to make sure we only report the first trigger of each kind to
+ // UMA.
+ bool time_until_first_shown_trigger_reported_;
+ bool time_until_first_startup_trigger_reported_;
// We should not fetch in background before EULA gets accepted.
std::unique_ptr<EulaState> eula_state_;
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
index 0c22282ea0a..8567849469f 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
@@ -15,6 +15,7 @@
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
@@ -28,8 +29,8 @@
#include "components/ntp_snippets/remote/test_utils.h"
#include "components/ntp_snippets/status.h"
#include "components/ntp_snippets/user_classifier.h"
-#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/variations/variations_params_manager.h"
#include "components/web_resource/web_resource_pref_names.h"
#include "net/base/network_change_notifier.h"
@@ -80,6 +81,7 @@ class MockRemoteSuggestionsProvider : public RemoteSuggestionsProvider {
const RemoteSuggestionsFetcher*());
MOCK_CONST_METHOD1(GetUrlWithFavicon,
GURL(const ContentSuggestion::ID& suggestion_id));
+ MOCK_CONST_METHOD0(IsDisabled, bool());
MOCK_METHOD1(GetCategoryStatus, CategoryStatus(Category));
MOCK_METHOD1(GetCategoryInfo, CategoryInfo(Category));
MOCK_METHOD3(ClearHistory,
@@ -101,6 +103,13 @@ class MockRemoteSuggestionsProvider : public RemoteSuggestionsProvider {
MOCK_METHOD0(OnSignInStateChanged, void());
};
+class FakeOfflineNetworkChangeNotifier : public net::NetworkChangeNotifier {
+ public:
+ ConnectionType GetCurrentConnectionType() const override {
+ return NetworkChangeNotifier::CONNECTION_NONE;
+ }
+};
+
} // namespace
class RemoteSuggestionsSchedulerImplTest : public ::testing::Test {
@@ -205,7 +214,7 @@ class RemoteSuggestionsSchedulerImplTest : public ::testing::Test {
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldIgnoreSignalsWhenNotEnabled) {
scheduler()->OnPersistentSchedulerWakeUp();
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
scheduler()->OnBrowserForegrounded();
scheduler()->OnBrowserColdStart();
}
@@ -238,7 +247,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
// All signals are ignored because of Eula not being accepted.
scheduler()->OnPersistentSchedulerWakeUp();
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
scheduler()->OnBrowserForegrounded();
scheduler()->OnBrowserColdStart();
}
@@ -271,7 +280,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
ActivateProvider();
scheduler()->OnPersistentSchedulerWakeUp();
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
scheduler()->OnBrowserForegrounded();
scheduler()->OnBrowserColdStart();
}
@@ -359,7 +368,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
- ShouldFetchOnNTPOpenedForTheFirstTime) {
+ ShouldFetchOnSuggestionsSurfaceOpenedForTheFirstTime) {
// First set only this type to be allowed.
SetVariationParameter("scheduler_trigger_types", "ntp_opened");
ResetProvider();
@@ -369,7 +378,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
ActivateProvider();
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
@@ -401,7 +410,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
- ShouldNotFetchOnNTPOpenedAfterSuccessfulSoftFetch) {
+ ShouldNotFetchOnSuggestionsSurfaceOpenedAfterSuccessfulSoftFetch) {
// First enable the scheduler; the second Schedule is called after the
// successful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
@@ -411,14 +420,14 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
signal_fetch_done.Run(Status::Success());
// The second call is ignored if it happens right after the first one.
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
- ShouldNotFetchOnNTPOpenedAfterSuccessfulPersistentFetch) {
+ ShouldNotFetchOnSuggestionsSurfaceOpenedAfterSuccessfulPersistentFetch) {
// First enable the scheduler; the second Schedule is called after the
// successful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
@@ -431,11 +440,11 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
scheduler()->OnPersistentSchedulerWakeUp();
signal_fetch_done.Run(Status::Success());
// The second call is ignored if it happens right after the first one.
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
- ShouldNotFetchOnNTPOpenedAfterFailedSoftFetch) {
+ ShouldNotFetchOnSuggestionsSurfaceOpenedAfterFailedSoftFetch) {
// First enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
@@ -444,15 +453,15 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
// The second call is ignored if it happens right after the first one.
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
- ShouldNotFetchOnNTPOpenedAfterFailedPersistentFetch) {
+ ShouldNotFetchOnSuggestionsSurfaceOpenedAfterFailedPersistentFetch) {
// First enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
@@ -465,7 +474,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
// The second call is ignored if it happens right after the first one.
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
@@ -494,10 +503,9 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
scheduler()->OnBrowserForegrounded();
}
-TEST_F(RemoteSuggestionsSchedulerImplTest,
- ShouldRescheduleOnRescheduleFetching) {
+TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldRescheduleOnBrowserUpgraded) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
- scheduler()->RescheduleFetching();
+ scheduler()->OnBrowserUpgraded();
}
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldScheduleOnActivation) {
@@ -662,8 +670,6 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForShownTriggerOnWifi) {
// Pretend we are on WiFi (already done in ctor, we make it explicit here).
EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
.WillRepeatedly(Return(true));
- // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 8h time
- // interval by default for shown trigger on WiFi.
// Initial scheduling after being enabled.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
@@ -673,19 +679,22 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForShownTriggerOnWifi) {
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
- // Open NTP again after too short delay. This time no fetch is executed.
- test_clock()->Advance(base::TimeDelta::FromHours(1));
- scheduler()->OnNTPOpened();
+ // Open NTP again after too short delay (one minute missing). UserClassifier
+ // defaults to UserClass::ACTIVE_NTP_USER - we work with the default interval
+ // for this class here. This time no fetch is executed.
+ test_clock()->Advance(base::TimeDelta::FromHours(10) -
+ base::TimeDelta::FromMinutes(1));
+ scheduler()->OnSuggestionsSurfaceOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
- test_clock()->Advance(base::TimeDelta::FromHours(7));
+ test_clock()->Advance(base::TimeDelta::FromMinutes(2));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
@@ -706,19 +715,19 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Open NTP again after too short delay. This time no fetch is executed.
test_clock()->Advance(base::TimeDelta::FromMinutes(20));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
test_clock()->Advance(base::TimeDelta::FromMinutes(10));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
@@ -737,19 +746,19 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Open NTP again after too short delay. This time no fetch is executed.
test_clock()->Advance(base::TimeDelta::FromHours(5));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
test_clock()->Advance(base::TimeDelta::FromHours(7));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
@@ -770,19 +779,19 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Open NTP again after too short delay. This time no fetch is executed.
test_clock()->Advance(base::TimeDelta::FromMinutes(20));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
test_clock()->Advance(base::TimeDelta::FromMinutes(10));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
- scheduler()->OnNTPOpened();
+ scheduler()->OnSuggestionsSurfaceOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
@@ -867,4 +876,45 @@ TEST_F(RemoteSuggestionsSchedulerImplTest,
scheduler()->OnPersistentSchedulerWakeUp();
}
+TEST_F(RemoteSuggestionsSchedulerImplTest,
+ ShouldIgnoreSubsequentStartupSignalsForM58) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ kRemoteSuggestionsEmulateM58FetchingSchedule);
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+
+ // First enable the scheduler -- this will trigger the persistent scheduling.
+ EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
+ ActivateProvider();
+
+ // The startup triggers are ignored.
+ EXPECT_CALL(*provider(), RefetchInTheBackground(_)).Times(0);
+ scheduler()->OnBrowserForegrounded();
+ scheduler()->OnBrowserColdStart();
+
+ // Foreground the browser again after a very long delay. Again, no fetch is
+ // executed for neither Foregrounded, nor ColdStart.
+ test_clock()->Advance(base::TimeDelta::FromHours(100000));
+ scheduler()->OnBrowserForegrounded();
+ scheduler()->OnBrowserColdStart();
+}
+
+TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldIgnoreSignalsWhenOffline) {
+ // Simulate being offline. NetworkChangeNotifier is a singleton, thus, this
+ // instance is actually globally accessible (from the static function
+ // NetworkChangeNotifier::IsOffline() that is called from the scheduler).
+ FakeOfflineNetworkChangeNotifier fake;
+
+ // Activating the provider should schedule the persistent background fetches.
+ EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
+ scheduler()->OnProviderActivated();
+
+ // All signals are ignored because of being offline.
+ EXPECT_CALL(*provider(), RefetchInTheBackground(_)).Times(0);
+ scheduler()->OnPersistentSchedulerWakeUp();
+ scheduler()->OnSuggestionsSurfaceOpened();
+ scheduler()->OnBrowserForegrounded();
+ scheduler()->OnBrowserColdStart();
+}
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_status_service_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_status_service_unittest.cc
index dcfc9b83e67..65ca301f231 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_status_service_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_status_service_unittest.cc
@@ -7,16 +7,17 @@
#include <memory>
#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
#include "components/ntp_snippets/features.h"
#include "components/ntp_snippets/ntp_snippets_constants.h"
#include "components/ntp_snippets/pref_names.h"
#include "components/ntp_snippets/remote/test_utils.h"
-#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "components/signin/core/common/signin_pref_names.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/variations/variations_params_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -48,7 +49,11 @@ TEST_F(RemoteSuggestionsStatusServiceTest, NoSigninNeeded) {
service->GetStatusFromDeps());
// One can still sign in.
+#if defined(OS_CHROMEOS)
utils_.fake_signin_manager()->SignIn("foo@bar.com");
+#else
+ utils_.fake_signin_manager()->SignIn("foo@bar.com", "user", "pass");
+#endif
EXPECT_EQ(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN,
service->GetStatusFromDeps());
}
@@ -66,7 +71,11 @@ TEST_F(RemoteSuggestionsStatusServiceTest, DisabledViaPref) {
service->GetStatusFromDeps());
// The other dependencies shouldn't matter anymore.
+#if defined(OS_CHROMEOS)
utils_.fake_signin_manager()->SignIn("foo@bar.com");
+#else
+ utils_.fake_signin_manager()->SignIn("foo@bar.com", "user", "pass");
+#endif
EXPECT_EQ(RemoteSuggestionsStatus::EXPLICITLY_DISABLED,
service->GetStatusFromDeps());
}
diff --git a/chromium/components/ntp_snippets/remote/test_utils.cc b/chromium/components/ntp_snippets/remote/test_utils.cc
index eb63bc40431..18809331a60 100644
--- a/chromium/components/ntp_snippets/remote/test_utils.cc
+++ b/chromium/components/ntp_snippets/remote/test_utils.cc
@@ -7,10 +7,9 @@
#include <memory>
#include "base/memory/ptr_util.h"
-#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "components/signin/core/common/signin_pref_names.h"
#include "components/sync/driver/fake_sync_service.h"
@@ -48,24 +47,37 @@ syncer::ModelTypeSet FakeSyncService::GetActiveDataTypes() const {
}
RemoteSuggestionsTestUtils::RemoteSuggestionsTestUtils()
- : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()) {
- pref_service_->registry()->RegisterStringPref(prefs::kGoogleServicesAccountId,
- std::string());
- pref_service_->registry()->RegisterStringPref(
- prefs::kGoogleServicesLastAccountId, std::string());
- pref_service_->registry()->RegisterStringPref(
- prefs::kGoogleServicesLastUsername, std::string());
+ : pref_service_(base::MakeUnique<TestingPrefServiceSyncable>()) {
+ AccountTrackerService::RegisterPrefs(pref_service_->registry());
+
+#if defined(OS_CHROMEOS)
+ SigninManagerBase::RegisterProfilePrefs(pref_service_->registry());
+ SigninManagerBase::RegisterPrefs(pref_service_->registry());
+#else
+ SigninManager::RegisterProfilePrefs(pref_service_->registry());
+ SigninManager::RegisterPrefs(pref_service_->registry());
+#endif // OS_CHROMEOS
+
+ token_service_ = base::MakeUnique<FakeProfileOAuth2TokenService>();
signin_client_ = base::MakeUnique<TestSigninClient>(pref_service_.get());
account_tracker_ = base::MakeUnique<AccountTrackerService>();
+ account_tracker_->Initialize(signin_client_.get());
fake_sync_service_ = base::MakeUnique<FakeSyncService>();
+
ResetSigninManager();
}
RemoteSuggestionsTestUtils::~RemoteSuggestionsTestUtils() = default;
void RemoteSuggestionsTestUtils::ResetSigninManager() {
+#if defined(OS_CHROMEOS)
fake_signin_manager_ = base::MakeUnique<FakeSigninManagerBase>(
signin_client_.get(), account_tracker_.get());
+#else
+ fake_signin_manager_ = base::MakeUnique<FakeSigninManager>(
+ signin_client_.get(), token_service_.get(), account_tracker_.get(),
+ /*cookie_manager_service=*/nullptr);
+#endif
}
} // namespace test
diff --git a/chromium/components/ntp_snippets/remote/test_utils.h b/chromium/components/ntp_snippets/remote/test_utils.h
index d7cdfa1ba50..ed825a6196f 100644
--- a/chromium/components/ntp_snippets/remote/test_utils.h
+++ b/chromium/components/ntp_snippets/remote/test_utils.h
@@ -7,14 +7,25 @@
#include <memory>
+#include "build/build_config.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
class AccountTrackerService;
-class FakeSigninManagerBase;
-class TestingPrefServiceSimple;
+class FakeProfileOAuth2TokenService;
class TestSigninClient;
+using sync_preferences::TestingPrefServiceSyncable;
+
+#if defined(OS_CHROMEOS)
+// ChromeOS doesn't have SigninManager.
+using SigninManagerForTest = FakeSigninManagerBase;
+#else
+using SigninManagerForTest = FakeSigninManager;
+#endif // OS_CHROMEOS
+
namespace ntp_snippets {
namespace test {
@@ -46,15 +57,19 @@ class RemoteSuggestionsTestUtils {
void ResetSigninManager();
FakeSyncService* fake_sync_service() { return fake_sync_service_.get(); }
- FakeSigninManagerBase* fake_signin_manager() {
+ SigninManagerForTest* fake_signin_manager() {
return fake_signin_manager_.get();
}
- TestingPrefServiceSimple* pref_service() { return pref_service_.get(); }
+ TestingPrefServiceSyncable* pref_service() { return pref_service_.get(); }
+ FakeProfileOAuth2TokenService* token_service() {
+ return token_service_.get();
+ }
private:
- std::unique_ptr<FakeSigninManagerBase> fake_signin_manager_;
+ std::unique_ptr<SigninManagerForTest> fake_signin_manager_;
std::unique_ptr<FakeSyncService> fake_sync_service_;
- std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+ std::unique_ptr<TestingPrefServiceSyncable> pref_service_;
+ std::unique_ptr<FakeProfileOAuth2TokenService> token_service_;
std::unique_ptr<TestSigninClient> signin_client_;
std::unique_ptr<AccountTrackerService> account_tracker_;
};
diff --git a/chromium/components/ntp_tiles/BUILD.gn b/chromium/components/ntp_tiles/BUILD.gn
index b0784a80bc1..d9704187e88 100644
--- a/chromium/components/ntp_tiles/BUILD.gn
+++ b/chromium/components/ntp_tiles/BUILD.gn
@@ -36,9 +36,6 @@ static_library("ntp_tiles") {
"webui/ntp_tiles_internals_message_handler.h",
"webui/ntp_tiles_internals_message_handler_client.cc",
"webui/ntp_tiles_internals_message_handler_client.h",
- "webui/popular_sites_internals_message_handler.cc",
- "webui/popular_sites_internals_message_handler.h",
- "webui/popular_sites_internals_message_handler_client.h",
]
public_deps = [
diff --git a/chromium/components/ntp_tiles/constants.cc b/chromium/components/ntp_tiles/constants.cc
index 20bf4c36a3e..6af7a1727cd 100644
--- a/chromium/components/ntp_tiles/constants.cc
+++ b/chromium/components/ntp_tiles/constants.cc
@@ -18,9 +18,6 @@ extern const base::Feature kPopularSitesBakedInContentFeature{
extern const base::Feature kNtpMostLikelyFaviconsFromServerFeature{
"NTPMostLikelyFaviconsFromServer", base::FEATURE_DISABLED_BY_DEFAULT};
-extern const base::Feature kPinHomePageAsTileFeature{
- "PinHomePageAsTile", base::FEATURE_DISABLED_BY_DEFAULT};
-
bool AreNtpMostLikelyFaviconsFromServerEnabled() {
// Check if the experimental flag is forced on or off.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
diff --git a/chromium/components/ntp_tiles/constants.h b/chromium/components/ntp_tiles/constants.h
index 6619450363b..4af8d54413b 100644
--- a/chromium/components/ntp_tiles/constants.h
+++ b/chromium/components/ntp_tiles/constants.h
@@ -23,9 +23,6 @@ extern const base::Feature kPopularSitesBakedInContentFeature;
// Likely tiles on the New Tab Page.
extern const base::Feature kNtpMostLikelyFaviconsFromServerFeature;
-// Feature to pin any set home page as first tile.
-extern const base::Feature kPinHomePageAsTileFeature;
-
// Use this to find out whether the kNtpMostLikelyFaviconsFromServerFeature is
// enabled. This helper function abstracts iOS special way to override the
// feature (via command-line params).
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl.cc b/chromium/components/ntp_tiles/icon_cacher_impl.cc
index 015685fac36..7b9e06ff2d4 100644
--- a/chromium/components/ntp_tiles/icon_cacher_impl.cc
+++ b/chromium/components/ntp_tiles/icon_cacher_impl.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon/core/favicon_util.h"
@@ -16,6 +17,7 @@
#include "components/favicon_base/favicon_util.h"
#include "components/image_fetcher/core/image_decoder.h"
#include "components/image_fetcher/core/image_fetcher.h"
+#include "components/ntp_tiles/constants.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/geometry/size.h"
@@ -31,8 +33,11 @@ constexpr int kDesiredFrameSize = 128;
// TODO(jkrcal): Make the size in dip and the scale factor be passed as
// arguments from the UI so that we desire for the right size on a given device.
// See crbug.com/696563.
-constexpr int kTileIconMinSizePx = 48;
-constexpr int kTileIconDesiredSizePx = 96;
+constexpr int kDefaultTileIconMinSizePx = 1;
+constexpr int kDefaultTileIconDesiredSizePx = 96;
+
+constexpr char kTileIconMinSizePxFieldParam[] = "min_size";
+constexpr char kTileIconDesiredSizePxFieldParam[] = "desired_size";
favicon_base::IconType IconType(const PopularSites::Site& site) {
return site.large_icon_url.is_valid() ? favicon_base::TOUCH_ICON
@@ -52,6 +57,18 @@ bool HasResultDefaultBackgroundColor(
return result.fallback_icon_style->is_default_background_color;
}
+int GetMinimumFetchingSizeForChromeSuggestionsFaviconsFromServer() {
+ return base::GetFieldTrialParamByFeatureAsInt(
+ kNtpMostLikelyFaviconsFromServerFeature, kTileIconMinSizePxFieldParam,
+ kDefaultTileIconMinSizePx);
+}
+
+int GetDesiredFetchingSizeForChromeSuggestionsFaviconsFromServer() {
+ return base::GetFieldTrialParamByFeatureAsInt(
+ kNtpMostLikelyFaviconsFromServerFeature, kTileIconDesiredSizePxFieldParam,
+ kDefaultTileIconDesiredSizePx);
+}
+
} // namespace
IconCacherImpl::IconCacherImpl(
@@ -200,7 +217,8 @@ void IconCacherImpl::StartFetchMostLikely(const GURL& page_url,
// Desired size 0 means that we do not want the service to resize the image
// (as we will not use it anyway).
large_icon_service_->GetLargeIconOrFallbackStyle(
- page_url, kTileIconMinSizePx, /*desired_size_in_pixel=*/0,
+ page_url, GetMinimumFetchingSizeForChromeSuggestionsFaviconsFromServer(),
+ /*desired_size_in_pixel=*/0,
base::Bind(&IconCacherImpl::OnGetLargeIconOrFallbackStyleFinished,
weak_ptr_factory_.GetWeakPtr(), page_url),
&tracker_);
@@ -210,24 +228,60 @@ void IconCacherImpl::OnGetLargeIconOrFallbackStyleFinished(
const GURL& page_url,
const favicon_base::LargeIconResult& result) {
if (!HasResultDefaultBackgroundColor(result)) {
- // We should only fetch for default "gray" tiles so that we never overrite
- // any favicon of any size.
+ // There is already an icon, there is nothing to do. (We should only fetch
+ // for default "gray" tiles so that we never overwrite any favicon of any
+ // size.)
FinishRequestAndNotifyIconAvailable(page_url, /*newly_available=*/false);
+ // Update the time when the icon was last requested - postpone thus the
+ // automatic eviction of the favicon from the favicon database.
+ large_icon_service_->TouchIconFromGoogleServer(result.bitmap.icon_url);
return;
}
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("icon_catcher_get_large_icon", R"(
+ semantics {
+ sender: "Favicon Component"
+ description:
+ "Sends a request to a Google server to retrieve the favicon bitmap "
+ "for a server-suggested most visited tile on the new tab page."
+ trigger:
+ "A request can be sent if Chrome does not have a favicon for a "
+ "particular page and history sync is enabled."
+ data: "Page URL and desired icon size."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: false
+ setting:
+ "Users can disable this feature via 'History' setting under "
+ "'Advanced sync settings'."
+ chrome_policy {
+ SyncDisabled {
+ policy_options {mode: MANDATORY}
+ SyncDisabled: true
+ }
+ }
+ })");
large_icon_service_
->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
- page_url, kTileIconMinSizePx, kTileIconDesiredSizePx,
- /*may_page_url_be_private=*/true,
+ page_url,
+ GetMinimumFetchingSizeForChromeSuggestionsFaviconsFromServer(),
+ GetDesiredFetchingSizeForChromeSuggestionsFaviconsFromServer(),
+ /*may_page_url_be_private=*/true, traffic_annotation,
base::Bind(&IconCacherImpl::OnMostLikelyFaviconDownloaded,
weak_ptr_factory_.GetWeakPtr(), page_url));
}
-void IconCacherImpl::OnMostLikelyFaviconDownloaded(const GURL& request_url,
- bool success) {
- UMA_HISTOGRAM_BOOLEAN("NewTabPage.TileFaviconFetchSuccess.Server", success);
- FinishRequestAndNotifyIconAvailable(request_url, success);
+void IconCacherImpl::OnMostLikelyFaviconDownloaded(
+ const GURL& request_url,
+ favicon_base::GoogleFaviconServerRequestStatus status) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "NewTabPage.TileFaviconFetchStatus.Server", status,
+ favicon_base::GoogleFaviconServerRequestStatus::COUNT);
+ FinishRequestAndNotifyIconAvailable(
+ request_url,
+ status == favicon_base::GoogleFaviconServerRequestStatus::SUCCESS);
}
bool IconCacherImpl::StartRequest(const GURL& request_url,
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl.h b/chromium/components/ntp_tiles/icon_cacher_impl.h
index a124b2c21d2..671b02098e5 100644
--- a/chromium/components/ntp_tiles/icon_cacher_impl.h
+++ b/chromium/components/ntp_tiles/icon_cacher_impl.h
@@ -25,6 +25,7 @@ class LargeIconService;
namespace favicon_base {
struct FaviconImageResult;
struct LargeIconResult;
+enum class GoogleFaviconServerRequestStatus;
} // namespace favicon_base
namespace gfx {
@@ -51,6 +52,8 @@ class IconCacherImpl : public IconCacher {
PopularSites::Site site,
const base::Closure& icon_available,
const base::Closure& preliminary_icon_available) override;
+
+ // TODO(jkrcal): Rename all instances of "MostLikely" to "ChromeSuggestions".
void StartFetchMostLikely(const GURL& page_url,
const base::Closure& icon_available) override;
@@ -83,7 +86,9 @@ class IconCacherImpl : public IconCacher {
const GURL& page_url,
const favicon_base::LargeIconResult& result);
- void OnMostLikelyFaviconDownloaded(const GURL& request_url, bool success);
+ void OnMostLikelyFaviconDownloaded(
+ const GURL& request_url,
+ favicon_base::GoogleFaviconServerRequestStatus status);
bool StartRequest(const GURL& request_url,
const base::Closure& icon_available);
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
index 54f2fc20f3f..2ce20e89b7d 100644
--- a/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
+++ b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
@@ -20,6 +20,7 @@
#include "components/favicon/core/favicon_service_impl.h"
#include "components/favicon/core/favicon_util.h"
#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon_base/favicon_types.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_service.h"
#include "components/image_fetcher/core/image_decoder.h"
@@ -479,7 +480,7 @@ TEST_F(IconCacherTestMostLikely, Cached) {
EXPECT_FALSE(IconIsCachedFor(page_url, favicon_base::FAVICON));
EXPECT_TRUE(IconIsCachedFor(page_url, favicon_base::TOUCH_ICON));
EXPECT_THAT(histogram_tester.GetAllSamples(
- "NewTabPage.TileFaviconFetchSuccess.Server"),
+ "NewTabPage.TileFaviconFetchStatus.Server"),
IsEmpty());
}
@@ -515,10 +516,12 @@ TEST_F(IconCacherTestMostLikely, NotCachedAndFetchSucceeded) {
loop.Run();
EXPECT_FALSE(IconIsCachedFor(page_url, favicon_base::FAVICON));
EXPECT_TRUE(IconIsCachedFor(page_url, favicon_base::TOUCH_ICON));
- EXPECT_THAT(
- histogram_tester.GetAllSamples(
- "NewTabPage.TileFaviconFetchSuccess.Server"),
- ElementsAre(Bucket(/*bucket=*/true, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.TileFaviconFetchStatus.Server"),
+ ElementsAre(Bucket(
+ /*bucket=*/static_cast<int>(
+ favicon_base::GoogleFaviconServerRequestStatus::SUCCESS),
+ /*count=*/1)));
}
TEST_F(IconCacherTestMostLikely, NotCachedAndFetchFailed) {
@@ -552,10 +555,13 @@ TEST_F(IconCacherTestMostLikely, NotCachedAndFetchFailed) {
EXPECT_FALSE(IconIsCachedFor(page_url, favicon_base::FAVICON));
EXPECT_FALSE(IconIsCachedFor(page_url, favicon_base::TOUCH_ICON));
- EXPECT_THAT(
- histogram_tester.GetAllSamples(
- "NewTabPage.TileFaviconFetchSuccess.Server"),
- ElementsAre(Bucket(/*bucket=*/false, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.TileFaviconFetchStatus.Server"),
+ ElementsAre(Bucket(
+ /*bucket=*/static_cast<int>(
+ favicon_base::GoogleFaviconServerRequestStatus::
+ FAILURE_CONNECTION_ERROR),
+ /*count=*/1)));
}
TEST_F(IconCacherTestMostLikely, HandlesEmptyCallbacksNicely) {
diff --git a/chromium/components/ntp_tiles/metrics.cc b/chromium/components/ntp_tiles/metrics.cc
index f657ced1822..4064c1594f3 100644
--- a/chromium/components/ntp_tiles/metrics.cc
+++ b/chromium/components/ntp_tiles/metrics.cc
@@ -85,46 +85,45 @@ const char* GetTileTypeSuffix(TileVisualType type) {
} // namespace
-void RecordPageImpression(const std::vector<TileImpression>& tiles,
+void RecordPageImpression(int number_of_tiles) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles", number_of_tiles);
+}
+
+void RecordTileImpression(int index,
+ TileSource source,
+ TileVisualType type,
+ const GURL& url,
rappor::RapporService* rappor_service) {
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles", tiles.size());
+ UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestionsImpression", index,
+ kMaxNumTiles);
- for (int index = 0; index < static_cast<int>(tiles.size()); index++) {
- TileSource source = tiles[index].source;
- TileVisualType tile_type = tiles[index].type;
+ std::string source_name = GetSourceHistogramName(source);
+ std::string impression_histogram = base::StringPrintf(
+ "NewTabPage.SuggestionsImpression.%s", source_name.c_str());
+ LogHistogramEvent(impression_histogram, index, kMaxNumTiles);
- UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestionsImpression", index,
- kMaxNumTiles);
+ if (type > LAST_RECORDED_TILE_TYPE) {
+ return;
+ }
- std::string source_name = GetSourceHistogramName(source);
- std::string impression_histogram = base::StringPrintf(
- "NewTabPage.SuggestionsImpression.%s", source_name.c_str());
- LogHistogramEvent(impression_histogram, index, kMaxNumTiles);
+ UMA_HISTOGRAM_ENUMERATION("NewTabPage.TileType", type,
+ LAST_RECORDED_TILE_TYPE + 1);
- if (tile_type > LAST_RECORDED_TILE_TYPE) {
- continue;
- }
+ std::string tile_type_histogram =
+ base::StringPrintf("NewTabPage.TileType.%s", source_name.c_str());
+ LogHistogramEvent(tile_type_histogram, type, LAST_RECORDED_TILE_TYPE + 1);
- UMA_HISTOGRAM_ENUMERATION("NewTabPage.TileType", tile_type,
- LAST_RECORDED_TILE_TYPE + 1);
-
- std::string tile_type_histogram =
- base::StringPrintf("NewTabPage.TileType.%s", source_name.c_str());
- LogHistogramEvent(tile_type_histogram, tile_type,
- LAST_RECORDED_TILE_TYPE + 1);
-
- const char* tile_type_suffix = GetTileTypeSuffix(tile_type);
- if (tile_type_suffix) {
- // Note: This handles a null |rappor_service|.
- rappor::SampleDomainAndRegistryFromGURL(
- rappor_service,
- base::StringPrintf("NTP.SuggestionsImpressions.%s", tile_type_suffix),
- tiles[index].url);
-
- std::string icon_impression_histogram = base::StringPrintf(
- "NewTabPage.SuggestionsImpression.%s", tile_type_suffix);
- LogHistogramEvent(icon_impression_histogram, index, kMaxNumTiles);
- }
+ const char* tile_type_suffix = GetTileTypeSuffix(type);
+ if (tile_type_suffix) {
+ // Note: This handles a null |rappor_service|.
+ rappor::SampleDomainAndRegistryFromGURL(
+ rappor_service,
+ base::StringPrintf("NTP.SuggestionsImpressions.%s", tile_type_suffix),
+ url);
+
+ std::string icon_impression_histogram = base::StringPrintf(
+ "NewTabPage.SuggestionsImpression.%s", tile_type_suffix);
+ LogHistogramEvent(icon_impression_histogram, index, kMaxNumTiles);
}
}
diff --git a/chromium/components/ntp_tiles/metrics.h b/chromium/components/ntp_tiles/metrics.h
index 187d71960d9..eb0bf2d2839 100644
--- a/chromium/components/ntp_tiles/metrics.h
+++ b/chromium/components/ntp_tiles/metrics.h
@@ -19,19 +19,16 @@ class RapporService;
namespace ntp_tiles {
namespace metrics {
-struct TileImpression {
- TileImpression(TileSource source, TileVisualType type, const GURL& url)
- : source(source), type(type), url(url) {}
-
- TileSource source;
- TileVisualType type;
- GURL url;
-};
-
// Records an NTP impression, after all tiles have loaded.
-// Includes the visual types (see above) of all visible tiles. If
-// |rappor_service| is null, no rappor metrics will be reported.
-void RecordPageImpression(const std::vector<TileImpression>& tiles,
+void RecordPageImpression(int number_of_tiles);
+
+// Records a tile impression at |index| (zero based) created by |source|. This
+// should be called only after the visual |type| of the tile has been
+// determined. If |rappor_service| is null, no rappor metrics will be reported.
+void RecordTileImpression(int index,
+ TileSource source,
+ TileVisualType type,
+ const GURL& url,
rappor::RapporService* rappor_service);
// Records a click on a tile.
diff --git a/chromium/components/ntp_tiles/metrics_unittest.cc b/chromium/components/ntp_tiles/metrics_unittest.cc
index 4b5eac9a7d5..dbbe4c1e8e3 100644
--- a/chromium/components/ntp_tiles/metrics_unittest.cc
+++ b/chromium/components/ntp_tiles/metrics_unittest.cc
@@ -22,19 +22,33 @@ namespace {
using testing::ElementsAre;
using testing::IsEmpty;
-TEST(RecordPageImpressionTest, ShouldRecordUmaForIcons) {
+TEST(RecordPageImpressionTest, ShouldRecordNumberOfTiles) {
base::HistogramTester histogram_tester;
- RecordPageImpression({{TileSource::TOP_SITES, ICON_REAL, GURL()},
- {TileSource::TOP_SITES, ICON_REAL, GURL()},
- {TileSource::TOP_SITES, ICON_REAL, GURL()},
- {TileSource::TOP_SITES, ICON_COLOR, GURL()},
- {TileSource::TOP_SITES, ICON_COLOR, GURL()},
- {TileSource::SUGGESTIONS_SERVICE, ICON_REAL, GURL()},
- {TileSource::SUGGESTIONS_SERVICE, ICON_DEFAULT, GURL()},
- {TileSource::POPULAR, ICON_COLOR, GURL()}},
- /*rappor_service=*/nullptr);
+ RecordPageImpression(5);
EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.NumberOfTiles"),
- ElementsAre(base::Bucket(/*min=*/8, /*count=*/1)));
+ ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
+}
+
+TEST(RecordTileImpressionTest, ShouldRecordUmaForIcons) {
+ base::HistogramTester histogram_tester;
+
+ RecordTileImpression(0, TileSource::TOP_SITES, ICON_REAL, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(1, TileSource::TOP_SITES, ICON_REAL, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(2, TileSource::TOP_SITES, ICON_REAL, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(3, TileSource::TOP_SITES, ICON_COLOR, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(4, TileSource::TOP_SITES, ICON_COLOR, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(5, TileSource::SUGGESTIONS_SERVICE, ICON_REAL, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(6, TileSource::SUGGESTIONS_SERVICE, ICON_DEFAULT, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(7, TileSource::POPULAR, ICON_COLOR, GURL(),
+ /*rappor_service=*/nullptr);
+
EXPECT_THAT(
histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
@@ -88,14 +102,16 @@ TEST(RecordPageImpressionTest, ShouldRecordUmaForIcons) {
ElementsAre(base::Bucket(/*min=*/6, /*count=*/1)));
}
-TEST(RecordPageImpressionTest, ShouldRecordUmaForThumbnails) {
+TEST(RecordTileImpressionTest, ShouldRecordUmaForThumbnails) {
base::HistogramTester histogram_tester;
- RecordPageImpression({{TileSource::TOP_SITES, THUMBNAIL_FAILED, GURL()},
- {TileSource::SUGGESTIONS_SERVICE, THUMBNAIL, GURL()},
- {TileSource::POPULAR, THUMBNAIL, GURL()}},
+
+ RecordTileImpression(0, TileSource::TOP_SITES, THUMBNAIL_FAILED, GURL(),
/*rappor_service=*/nullptr);
- EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.NumberOfTiles"),
- ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
+ RecordTileImpression(1, TileSource::SUGGESTIONS_SERVICE, THUMBNAIL, GURL(),
+ /*rappor_service=*/nullptr);
+ RecordTileImpression(2, TileSource::POPULAR, THUMBNAIL, GURL(),
+ /*rappor_service=*/nullptr);
+
EXPECT_THAT(
histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
@@ -216,14 +232,15 @@ TEST(RecordTileClickTest, ShouldNotRecordUnknownTileType) {
IsEmpty());
}
-TEST(RecordPageImpressionTest, ShouldRecordRappor) {
+TEST(RecordTileImpressionTest, ShouldRecordRappor) {
rappor::TestRapporServiceImpl rappor_service;
- RecordPageImpression(
- {{TileSource::TOP_SITES, ICON_REAL, GURL("http://www.site1.com/")},
- {TileSource::TOP_SITES, ICON_COLOR, GURL("http://www.site2.com/")},
- {TileSource::TOP_SITES, ICON_DEFAULT, GURL("http://www.site3.com/")}},
- &rappor_service);
+ RecordTileImpression(0, TileSource::TOP_SITES, ICON_REAL,
+ GURL("http://www.site1.com/"), &rappor_service);
+ RecordTileImpression(1, TileSource::TOP_SITES, ICON_COLOR,
+ GURL("http://www.site2.com/"), &rappor_service);
+ RecordTileImpression(2, TileSource::TOP_SITES, ICON_DEFAULT,
+ GURL("http://www.site3.com/"), &rappor_service);
EXPECT_EQ(3, rappor_service.GetReportsCount());
@@ -255,13 +272,13 @@ TEST(RecordPageImpressionTest, ShouldRecordRappor) {
}
}
-TEST(RecordPageImpressionTest, ShouldNotRecordRapporForUnknownTileType) {
+TEST(RecordTileImpressionTest, ShouldNotRecordRapporForUnknownTileType) {
rappor::TestRapporServiceImpl rappor_service;
- RecordPageImpression(
- {{TileSource::TOP_SITES, ICON_REAL, GURL("http://www.s1.com/")},
- {TileSource::TOP_SITES, UNKNOWN_TILE_TYPE, GURL("http://www.s2.com/")}},
- &rappor_service);
+ RecordTileImpression(0, TileSource::TOP_SITES, ICON_REAL,
+ GURL("http://www.s1.com/"), &rappor_service);
+ RecordTileImpression(1, TileSource::TOP_SITES, UNKNOWN_TILE_TYPE,
+ GURL("http://www.s2.com/"), &rappor_service);
// Unknown tile type shouldn't get reported.
EXPECT_EQ(1, rappor_service.GetReportsCount());
diff --git a/chromium/components/ntp_tiles/most_visited_sites.cc b/chromium/components/ntp_tiles/most_visited_sites.cc
index 19c2f957f89..45a4bcddbb8 100644
--- a/chromium/components/ntp_tiles/most_visited_sites.cc
+++ b/chromium/components/ntp_tiles/most_visited_sites.cc
@@ -33,6 +33,9 @@ namespace {
const base::Feature kDisplaySuggestionsServiceTiles{
"DisplaySuggestionsServiceTiles", base::FEATURE_ENABLED_BY_DEFAULT};
+// The maximum index of the home page tile.
+const size_t kMaxHomeTileIndex = 3;
+
// Determine whether we need any tiles from PopularSites to fill up a grid of
// |num_tiles| tiles.
bool NeedPopularSites(const PrefService* prefs, int num_tiles) {
@@ -44,6 +47,15 @@ bool AreURLsEquivalent(const GURL& url1, const GURL& url2) {
url1.path_piece() == url2.path_piece();
}
+bool HasHomeTile(const NTPTilesVector& tiles) {
+ for (const auto& tile : tiles) {
+ if (tile.source == TileSource::HOMEPAGE) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
MostVisitedSites::MostVisitedSites(
@@ -52,15 +64,13 @@ MostVisitedSites::MostVisitedSites(
SuggestionsService* suggestions,
std::unique_ptr<PopularSites> popular_sites,
std::unique_ptr<IconCacher> icon_cacher,
- std::unique_ptr<MostVisitedSitesSupervisor> supervisor,
- std::unique_ptr<HomePageClient> home_page_client)
+ std::unique_ptr<MostVisitedSitesSupervisor> supervisor)
: prefs_(prefs),
top_sites_(top_sites),
suggestions_service_(suggestions),
popular_sites_(std::move(popular_sites)),
icon_cacher_(std::move(icon_cacher)),
supervisor_(std::move(supervisor)),
- home_page_client_(std::move(home_page_client)),
observer_(nullptr),
num_sites_(0u),
top_sites_observer_(this),
@@ -74,21 +84,6 @@ MostVisitedSites::MostVisitedSites(
supervisor_->SetObserver(this);
}
-MostVisitedSites::MostVisitedSites(
- PrefService* prefs,
- scoped_refptr<history::TopSites> top_sites,
- SuggestionsService* suggestions,
- std::unique_ptr<PopularSites> popular_sites,
- std::unique_ptr<IconCacher> icon_cacher,
- std::unique_ptr<MostVisitedSitesSupervisor> supervisor)
- : MostVisitedSites(prefs,
- top_sites,
- suggestions,
- std::move(popular_sites),
- std::move(icon_cacher),
- std::move(supervisor),
- nullptr) {}
-
MostVisitedSites::~MostVisitedSites() {
if (supervisor_)
supervisor_->SetObserver(nullptr);
@@ -111,6 +106,12 @@ bool MostVisitedSites::DoesSourceExist(TileSource source) const {
return false;
}
+void MostVisitedSites::SetHomePageClient(
+ std::unique_ptr<HomePageClient> client) {
+ DCHECK(client);
+ home_page_client_ = std::move(client);
+}
+
void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer,
size_t num_sites) {
DCHECK(observer);
@@ -150,6 +151,10 @@ void MostVisitedSites::Refresh() {
suggestions_service_->FetchSuggestionsData();
}
+void MostVisitedSites::OnHomePageStateChanged() {
+ BuildCurrentTiles();
+}
+
void MostVisitedSites::AddOrRemoveBlacklistedUrl(const GURL& url,
bool add_url) {
if (add_url) {
@@ -211,7 +216,7 @@ void MostVisitedSites::InitiateTopSitesQuery() {
base::FilePath MostVisitedSites::GetWhitelistLargeIconPath(const GURL& url) {
if (supervisor_) {
- for (const auto& whitelist : supervisor_->whitelists()) {
+ for (const auto& whitelist : supervisor_->GetWhitelists()) {
if (AreURLsEquivalent(whitelist.entry_point, url))
return whitelist.large_icon_path;
}
@@ -246,7 +251,7 @@ void MostVisitedSites::OnMostVisitedURLsAvailable(
}
mv_source_ = TileSource::TOP_SITES;
- SaveNewTilesAndNotify(std::move(tiles));
+ InitiateNotificationForNewTiles(std::move(tiles));
}
void MostVisitedSites::OnSuggestionsProfileChanged(
@@ -302,7 +307,7 @@ void MostVisitedSites::BuildCurrentTilesGivenSuggestionsProfile(
}
mv_source_ = TileSource::SUGGESTIONS_SERVICE;
- SaveNewTilesAndNotify(std::move(tiles));
+ InitiateNotificationForNewTiles(std::move(tiles));
}
NTPTilesVector MostVisitedSites::CreateWhitelistEntryPointTiles(
@@ -313,7 +318,7 @@ NTPTilesVector MostVisitedSites::CreateWhitelistEntryPointTiles(
}
NTPTilesVector whitelist_tiles;
- for (const auto& whitelist : supervisor_->whitelists()) {
+ for (const auto& whitelist : supervisor_->GetWhitelists()) {
if (whitelist_tiles.size() + num_actual_tiles >= num_sites_)
break;
@@ -382,40 +387,76 @@ NTPTilesVector MostVisitedSites::CreatePopularSitesTiles(
return popular_sites_tiles;
}
-NTPTilesVector MostVisitedSites::CreatePersonalTilesWithHomeTile(
- NTPTilesVector tiles) const {
+void MostVisitedSites::OnHomePageTitleDetermined(
+ NTPTilesVector tiles,
+ const base::Optional<base::string16>& title) {
+ 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()));
+}
+
+NTPTilesVector MostVisitedSites::InsertHomeTile(
+ NTPTilesVector tiles,
+ const base::string16& title) const {
DCHECK(home_page_client_);
DCHECK_GT(num_sites_, 0u);
- const GURL& home_page_url = home_page_client_->GetHomepageUrl();
+ const GURL& home_page_url = home_page_client_->GetHomePageUrl();
NTPTilesVector new_tiles;
- // Add the home tile as first tile.
+ // Add the home tile to the first four tiles.
NTPTile home_tile;
home_tile.url = home_page_url;
+ home_tile.title = title;
home_tile.source = TileSource::HOMEPAGE;
- new_tiles.push_back(std::move(home_tile));
- for (auto& tile : tiles) {
- if (new_tiles.size() >= num_sites_) {
- break;
+ bool home_tile_added = false;
+ size_t index = 0;
+
+ while (index < tiles.size() && new_tiles.size() < num_sites_) {
+ bool hosts_are_equal = tiles[index].url.host() == home_page_url.host();
+
+ // Add the home tile to the first four tiles
+ // or at the position of a tile that has the same host
+ // and is ranked higher.
+ // TODO(fhorschig): Introduce a more sophisticated deduplication.
+ if (!home_tile_added && (index >= kMaxHomeTileIndex || hosts_are_equal)) {
+ new_tiles.push_back(std::move(home_tile));
+ home_tile_added = true;
+ continue; // Do not advance the current tile index.
}
- if (tile.url.host() == home_page_url.host()) {
- continue;
+ // Add non-home page tiles.
+ if (!hosts_are_equal) {
+ new_tiles.push_back(std::move(tiles[index]));
}
+ ++index;
+ }
- 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 && new_tiles.size() < num_sites_) {
+ new_tiles.push_back(std::move(home_tile));
}
return new_tiles;
}
-void MostVisitedSites::SaveNewTilesAndNotify(NTPTilesVector personal_tiles) {
+void MostVisitedSites::InitiateNotificationForNewTiles(
+ NTPTilesVector new_tiles) {
+ if (ShouldAddHomeTile() && !HasHomeTile(new_tiles)) {
+ home_page_client_->QueryHomePageTitle(
+ base::BindOnce(&MostVisitedSites::OnHomePageTitleDetermined,
+ base::Unretained(this), new_tiles));
+ // 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));
+}
+
+void MostVisitedSites::SaveTilesAndNotify(NTPTilesVector personal_tiles) {
std::set<std::string> used_hosts;
size_t num_actual_tiles = 0u;
-
- if (ShouldAddHomeTile()) {
- personal_tiles = CreatePersonalTilesWithHomeTile(std::move(personal_tiles));
- }
AddToHostsAndTotalCount(personal_tiles, &used_hosts, &num_actual_tiles);
NTPTilesVector whitelist_tiles =
@@ -490,12 +531,13 @@ void MostVisitedSites::TopSitesChanged(TopSites* top_sites,
}
bool MostVisitedSites::ShouldAddHomeTile() const {
- return base::FeatureList::IsEnabled(kPinHomePageAsTileFeature) &&
- num_sites_ > 0u && home_page_client_ &&
+ 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() &&
!(top_sites_ &&
- top_sites_->IsBlacklisted(home_page_client_->GetHomepageUrl()));
+ top_sites_->IsBlacklisted(home_page_client_->GetHomePageUrl()));
}
void MostVisitedSites::AddToHostsAndTotalCount(const NTPTilesVector& new_tiles,
diff --git a/chromium/components/ntp_tiles/most_visited_sites.h b/chromium/components/ntp_tiles/most_visited_sites.h
index 271d6084977..661623d30d4 100644
--- a/chromium/components/ntp_tiles/most_visited_sites.h
+++ b/chromium/components/ntp_tiles/most_visited_sites.h
@@ -12,6 +12,7 @@
#include <string>
#include <vector>
+#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/macros.h"
@@ -71,7 +72,7 @@ class MostVisitedSitesSupervisor {
virtual bool IsBlocked(const GURL& url) = 0;
// Explicitly-specified sites to show on NTP.
- virtual std::vector<Whitelist> whitelists() = 0;
+ virtual std::vector<Whitelist> GetWhitelists() = 0;
// If true, be conservative about suggesting sites from outside sources.
virtual bool IsChildProfile() = 0;
@@ -95,10 +96,14 @@ class MostVisitedSites : public history::TopSitesObserver,
// platform-specific implementation.
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 GURL GetHomePageUrl() const = 0;
+ virtual void QueryHomePageTitle(TitleCallback title_callback) = 0;
};
// Construct a MostVisitedSites instance.
@@ -111,16 +116,6 @@ class MostVisitedSites : public history::TopSitesObserver,
suggestions::SuggestionsService* suggestions,
std::unique_ptr<PopularSites> popular_sites,
std::unique_ptr<IconCacher> icon_cacher,
- std::unique_ptr<MostVisitedSitesSupervisor> supervisor,
- std::unique_ptr<HomePageClient> home_page_client);
-
- // TODO(fhorschig): Adjust all factories and delete this.
- // Constructs a MostVisitedSites instance without HomePageClient.
- MostVisitedSites(PrefService* prefs,
- scoped_refptr<history::TopSites> top_sites,
- suggestions::SuggestionsService* suggestions,
- std::unique_ptr<PopularSites> popular_sites,
- std::unique_ptr<IconCacher> icon_cacher,
std::unique_ptr<MostVisitedSitesSupervisor> supervisor);
~MostVisitedSites() override;
@@ -143,10 +138,18 @@ 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);
+
// 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();
+
void AddOrRemoveBlacklistedUrl(const GURL& url, bool add_url);
void ClearBlacklistedUrls();
@@ -195,10 +198,14 @@ class MostVisitedSites : public history::TopSitesObserver,
const std::set<std::string>& used_hosts,
size_t num_actual_tiles);
+ // Initiates a query for the home page 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 SaveNewTilesAndNotify(NTPTilesVector personal_tiles);
+ void SaveTilesAndNotify(NTPTilesVector personal_tiles);
void OnPopularSitesDownloaded(bool success);
@@ -213,7 +220,11 @@ class MostVisitedSites : public history::TopSitesObserver,
// Adds the home page 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 CreatePersonalTilesWithHomeTile(NTPTilesVector tiles) const;
+ NTPTilesVector InsertHomeTile(NTPTilesVector tiles,
+ const base::string16& title) const;
+
+ 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.
bool ShouldAddHomeTile() const;
@@ -229,7 +240,7 @@ class MostVisitedSites : public history::TopSitesObserver,
std::unique_ptr<PopularSites> const popular_sites_;
std::unique_ptr<IconCacher> const icon_cacher_;
std::unique_ptr<MostVisitedSitesSupervisor> supervisor_;
- std::unique_ptr<HomePageClient> const home_page_client_;
+ std::unique_ptr<HomePageClient> home_page_client_;
Observer* observer_;
diff --git a/chromium/components/ntp_tiles/most_visited_sites_unittest.cc b/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
index 4bd8bc92ab5..43bdea7dc97 100644
--- a/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -21,7 +21,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/test/scoped_feature_list.h"
-#include "base/test/sequenced_worker_pool_owner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/history/core/browser/top_sites.h"
#include "components/history/core/browser/top_sites_observer.h"
@@ -67,11 +66,13 @@ using testing::Mock;
using testing::Not;
using testing::Return;
using testing::ReturnRef;
+using testing::SaveArg;
using testing::SizeIs;
using testing::StrictMock;
using testing::_;
const char kHomePageUrl[] = "http://ho.me/";
+const char kHomePageTitle[] = "Home";
std::string PrintTile(const std::string& title,
const std::string& url,
@@ -213,14 +214,21 @@ class MockMostVisitedSitesObserver : public MostVisitedSites::Observer {
class FakeHomePageClient : public MostVisitedSites::HomePageClient {
public:
- FakeHomePageClient() : home_page_enabled_(false), ntp_is_homepage_(false) {}
+ FakeHomePageClient()
+ : home_page_enabled_(false),
+ ntp_is_homepage_(false),
+ home_page_url_(kHomePageUrl) {}
~FakeHomePageClient() override {}
bool IsHomePageEnabled() const override { return home_page_enabled_; }
bool IsNewTabPageUsedAsHomePage() const override { return ntp_is_homepage_; }
- GURL GetHomepageUrl() const override { return GURL(kHomePageUrl); }
+ 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;
@@ -230,9 +238,17 @@ class FakeHomePageClient : public MostVisitedSites::HomePageClient {
ntp_is_homepage_ = ntp_is_homepage;
}
+ void SetHomePageUrl(GURL home_page_url) { home_page_url_ = home_page_url; }
+
+ void SetHomePageTitle(const base::Optional<base::string16>& home_page_title) {
+ home_page_title_ = home_page_title;
+ }
+
private:
bool home_page_enabled_;
bool ntp_is_homepage_;
+ GURL home_page_url_;
+ base::Optional<base::string16> home_page_title_;
};
class MockIconCacher : public IconCacher {
@@ -253,8 +269,7 @@ class PopularSitesFactoryForTest {
: prefs_(pref_service),
url_fetcher_factory_(/*default_factory=*/nullptr),
url_request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- worker_pool_owner_(/*max_threads=*/2, "PopularSitesFactoryForTest.") {
+ base::ThreadTaskRunnerHandle::Get())) {
PopularSitesImpl::RegisterProfilePrefs(pref_service->registry());
if (enabled) {
prefs_->SetString(prefs::kPopularSitesOverrideCountry, "IN");
@@ -279,17 +294,16 @@ class PopularSitesFactoryForTest {
std::unique_ptr<PopularSites> New() {
return base::MakeUnique<PopularSitesImpl>(
- worker_pool_owner_.pool().get(), prefs_,
+ prefs_,
/*template_url_service=*/nullptr,
/*variations_service=*/nullptr, url_request_context_.get(),
- /*directory=*/base::FilePath(), base::Bind(JsonUnsafeParser::Parse));
+ base::Bind(JsonUnsafeParser::Parse));
}
private:
PrefService* prefs_;
net::FakeURLFetcherFactory url_fetcher_factory_;
scoped_refptr<net::TestURLRequestContextGetter> url_request_context_;
- base::SequencedWorkerPoolOwner worker_pool_owner_;
};
// CallbackList-like container without Subscription, mimicking the
@@ -367,13 +381,10 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
.Times(AtLeast(0));
}
- auto home_page_client = base::MakeUnique<FakeHomePageClient>();
- home_page_client_ = home_page_client.get();
-
most_visited_sites_ = base::MakeUnique<MostVisitedSites>(
&pref_service_, mock_top_sites_, &mock_suggestions_service_,
popular_sites_factory_.New(), std::move(icon_cacher),
- /*supervisor=*/nullptr, std::move(home_page_client));
+ /*supervisor=*/nullptr);
}
bool IsPopularSitesEnabledViaVariations() const { return GetParam(); }
@@ -392,6 +403,13 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
return success;
}
+ FakeHomePageClient* RegisterNewHomePageClient() {
+ auto home_page_client = base::MakeUnique<FakeHomePageClient>();
+ FakeHomePageClient* raw_client_ptr = home_page_client.get();
+ most_visited_sites_->SetHomePageClient(std::move(home_page_client));
+ return raw_client_ptr;
+ }
+
void DisableRemoteSuggestions() {
EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
.Times(AnyNumber())
@@ -415,7 +433,6 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
scoped_refptr<StrictMock<MockTopSites>> mock_top_sites_;
StrictMock<MockSuggestionsService> mock_suggestions_service_;
StrictMock<MockMostVisitedSitesObserver> mock_observer_;
- FakeHomePageClient* home_page_client_;
std::unique_ptr<MostVisitedSites> most_visited_sites_;
base::test::ScopedFeatureList feature_list_;
MockIconCacher* icon_cacher_;
@@ -428,9 +445,8 @@ TEST_P(MostVisitedSitesTest, ShouldStartNoCallInConstructor) {
}
TEST_P(MostVisitedSitesTest, ShouldIncludeTileForHomePage) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
@@ -445,17 +461,11 @@ TEST_P(MostVisitedSitesTest, ShouldIncludeTileForHomePage) {
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfFeatureDisabled) {
- base::test::ScopedFeatureList features;
- features.InitAndDisableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageWithoutClient) {
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_,
OnMostVisitedURLsAvailable(Not(Contains(
MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE)))));
@@ -464,10 +474,68 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfFeatureDisabled) {
base::RunLoop().RunUntilIdle();
}
+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));
+ 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));
+ {
+ testing::Sequence seq;
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(Not(Contains(
+ MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE)))));
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(Not(Contains(MatchesTile(
+ kHomePageTitle, kHomePageUrl, TileSource::HOMEPAGE)))));
+ }
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/3);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesTest, ShouldUpdateHomePageTileOnHomePageStateChanged) {
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(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))))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_observer_, OnMostVisitedURLsAvailable(FirstTileIs(
+ "", 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);
+ DisableRemoteSuggestions();
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory()).Times(0);
+ EXPECT_CALL(mock_observer_, OnMostVisitedURLsAvailable(Not(FirstTileIs(
+ "", kHomePageUrl, TileSource::HOMEPAGE))));
+ most_visited_sites_->OnHomePageStateChanged();
+ base::RunLoop().RunUntilIdle();
+}
+
TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfNoTileRequested) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
@@ -481,10 +549,9 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfNoTileRequested) {
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldReturnOnlyHomePageIfOneTileRequested) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldReturnMostPopularPageIfOneTileRequested) {
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(
@@ -494,17 +561,42 @@ TEST_P(MostVisitedSitesTest, ShouldReturnOnlyHomePageIfOneTileRequested) {
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_observer_,
- OnMostVisitedURLsAvailable(ElementsAre(
- MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE))));
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile(
+ "Site 1", "http://site1/", TileSource::TOP_SITES))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/1);
base::RunLoop().RunUntilIdle();
}
+TEST_P(MostVisitedSitesTest, ShouldContainHomePageInFirstFourTiles) {
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(true);
+ DisableRemoteSuggestions();
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillRepeatedly(InvokeCallbackArgument<0>((MostVisitedURLList{
+ MakeMostVisitedURL("Site 1", "http://site1/"),
+ MakeMostVisitedURL("Site 2", "http://site2/"),
+ MakeMostVisitedURL("Site 3", "http://site3/"),
+ MakeMostVisitedURL("Site 4", "http://site4/"),
+ MakeMostVisitedURL("Site 5", "http://site5/"),
+ })));
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(false));
+ std::vector<NTPTile> tiles;
+ EXPECT_CALL(mock_observer_, OnMostVisitedURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&tiles));
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/8);
+ base::RunLoop().RunUntilIdle();
+ // Assert that the home page tile is in the first four tiles.
+ EXPECT_THAT(tiles[3], MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE));
+}
+
TEST_P(MostVisitedSitesTest, ShouldDeduplicateHomePageWithTopSites) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(
@@ -515,20 +607,19 @@ TEST_P(MostVisitedSitesTest, ShouldDeduplicateHomePageWithTopSites) {
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_observer_,
- OnMostVisitedURLsAvailable(
- AllOf(FirstTileIs("", kHomePageUrl, TileSource::HOMEPAGE),
- Not(Contains(MatchesTile("", kHomePageUrl,
- TileSource::TOP_SITES))))));
+ OnMostVisitedURLsAvailable(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) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
- home_page_client_->SetNtpIsHomePage(true);
+ 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{}));
@@ -545,9 +636,8 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfItIsNewTabPage) {
}
TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfThereIsNone) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(false);
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(false);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
@@ -563,10 +653,29 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfThereIsNone) {
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));
+ 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)))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(Not(
+ FirstTileIs("", kEmptyHomePageUrl, TileSource::HOMEPAGE))));
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/3);
+ base::RunLoop().RunUntilIdle();
+}
+
TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfBlacklisted) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(
@@ -589,9 +698,8 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfBlacklisted) {
}
TEST_P(MostVisitedSitesTest, ShouldPinHomePageAgainIfBlacklistingUndone) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(ntp_tiles::kPinHomePageAsTileFeature);
- home_page_client_->SetHomePageEnabled(true);
+ FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+ home_page_client->SetHomePageEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
diff --git a/chromium/components/ntp_tiles/popular_sites_impl.cc b/chromium/components/ntp_tiles/popular_sites_impl.cc
index 4e0d73f150a..38fc464509b 100644
--- a/chromium/components/ntp_tiles/popular_sites_impl.cc
+++ b/chromium/components/ntp_tiles/popular_sites_impl.cc
@@ -10,12 +10,8 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
@@ -35,7 +31,7 @@
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
#include "base/json/json_reader.h"
#include "components/grit/components_resources.h"
#include "ui/base/resource/resource_bundle.h"
@@ -63,10 +59,6 @@ const char kPopularSitesLastDownloadPref[] = "popular_sites_last_download";
const char kPopularSitesURLPref[] = "popular_sites_url";
const char kPopularSitesJsonPref[] = "suggested_sites_json";
-// TODO(crbug.com/683890): This refers to a local cache stored by older
-// versions of Chrome, no longer used. Remove after M61.
-const char kPopularSitesLocalFilenameToCleanup[] = "suggested_sites.json";
-
GURL GetPopularSitesURL(const std::string& directory,
const std::string& country,
const std::string& version) {
@@ -140,7 +132,7 @@ PopularSites::SitesVector ParseSiteList(const base::ListValue& list) {
return sites;
}
-#if defined(GOOGLE_CHROME_BUILD) && defined(OS_ANDROID)
+#if defined(GOOGLE_CHROME_BUILD) && (defined(OS_ANDROID) || defined(OS_IOS))
void SetDefaultResourceForSite(int index,
int resource_id,
base::ListValue* sites) {
@@ -154,7 +146,7 @@ void SetDefaultResourceForSite(int index,
// Creates the list of popular sites based on a snapshot available for mobile.
std::unique_ptr<base::ListValue> DefaultPopularSites() {
-#if !defined(OS_ANDROID)
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
return base::MakeUnique<base::ListValue>();
#else
if (!base::FeatureList::IsEnabled(kPopularSitesBakedInContentFeature)) {
@@ -198,32 +190,19 @@ PopularSites::Site::Site(const Site& other) = default;
PopularSites::Site::~Site() {}
PopularSitesImpl::PopularSitesImpl(
- const scoped_refptr<base::SequencedWorkerPool>& blocking_pool,
PrefService* prefs,
const TemplateURLService* template_url_service,
VariationsService* variations_service,
net::URLRequestContextGetter* download_context,
- const base::FilePath& directory,
ParseJSONCallback parse_json)
- : blocking_runner_(blocking_pool->GetTaskRunnerWithShutdownBehavior(
- base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)),
- prefs_(prefs),
+ : prefs_(prefs),
template_url_service_(template_url_service),
variations_(variations_service),
download_context_(download_context),
parse_json_(std::move(parse_json)),
is_fallback_(false),
sites_(ParseSiteList(*prefs->GetList(kPopularSitesJsonPref))),
- weak_ptr_factory_(this) {
- // If valid path provided, remove local files created by older versions.
- if (!directory.empty() && blocking_runner_) {
- blocking_runner_->PostTask(
- FROM_HERE,
- base::Bind(base::IgnoreResult(&base::DeleteFile),
- directory.AppendASCII(kPopularSitesLocalFilenameToCleanup),
- /*recursive=*/false));
- }
-}
+ weak_ptr_factory_(this) {}
PopularSitesImpl::~PopularSitesImpl() {}
diff --git a/chromium/components/ntp_tiles/popular_sites_impl.h b/chromium/components/ntp_tiles/popular_sites_impl.h
index b045dea6a79..116297342df 100644
--- a/chromium/components/ntp_tiles/popular_sites_impl.h
+++ b/chromium/components/ntp_tiles/popular_sites_impl.h
@@ -10,7 +10,6 @@
#include <vector>
#include "base/callback.h"
-#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
@@ -19,8 +18,6 @@
#include "url/gurl.h"
namespace base {
-class TaskRunner;
-class SequencedWorkerPool;
class Value;
}
@@ -51,12 +48,10 @@ using ParseJSONCallback = base::Callback<void(
class PopularSitesImpl : public PopularSites, public net::URLFetcherDelegate {
public:
PopularSitesImpl(
- const scoped_refptr<base::SequencedWorkerPool>& blocking_pool,
PrefService* prefs,
const TemplateURLService* template_url_service,
variations::VariationsService* variations_service,
net::URLRequestContextGetter* download_context,
- const base::FilePath& directory,
ParseJSONCallback parse_json);
~PopularSitesImpl() override;
@@ -89,7 +84,6 @@ class PopularSitesImpl : public PopularSites, public net::URLFetcherDelegate {
void OnDownloadFailed();
// Parameters set from constructor.
- scoped_refptr<base::TaskRunner> const blocking_runner_;
PrefService* const prefs_;
const TemplateURLService* const template_url_service_;
variations::VariationsService* const variations_;
diff --git a/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc b/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
index b04bf8b8750..d29217f2045 100644
--- a/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
+++ b/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
@@ -11,16 +11,12 @@
#include "base/bind.h"
#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/files/important_file_writer.h"
-#include "base/files/scoped_temp_dir.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
-#include "base/test/sequenced_worker_pool_owner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/build_config.h"
@@ -61,7 +57,7 @@ using TestPopularSiteVector = std::vector<TestPopularSite>;
}
size_t GetNumberOfDefaultPopularSitesForPlatform() {
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
return 8ul;
#else
return 0ul;
@@ -86,14 +82,11 @@ class PopularSitesTest : public ::testing::Test {
{kUrl, "https://www.chromium.org/"},
{kFaviconUrl, "https://www.chromium.org/favicon.ico"},
},
- worker_pool_owner_(2, "PopularSitesTest."),
prefs_(new sync_preferences::TestingPrefServiceSyncable()),
url_fetcher_factory_(nullptr) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableNTPPopularSites);
PopularSitesImpl::RegisterProfilePrefs(prefs_->registry());
- CHECK(scoped_cache_dir_.CreateUniqueTempDir());
- cache_dir_ = scoped_cache_dir_.GetPath();
}
void SetCountryAndVersion(const std::string& country,
@@ -162,9 +155,9 @@ class PopularSitesTest : public ::testing::Test {
std::unique_ptr<PopularSites> CreatePopularSites(
net::URLRequestContextGetter* context) {
return base::MakeUnique<PopularSitesImpl>(
- worker_pool_owner_.pool().get(), prefs_.get(),
+ prefs_.get(),
/*template_url_service=*/nullptr,
- /*variations_service=*/nullptr, context, cache_dir_,
+ /*variations_service=*/nullptr, context,
base::Bind(JsonUnsafeParser::Parse));
}
@@ -173,9 +166,6 @@ class PopularSitesTest : public ::testing::Test {
const TestPopularSite kChromium;
base::MessageLoopForUI ui_loop_;
- base::SequencedWorkerPoolOwner worker_pool_owner_;
- base::ScopedTempDir scoped_cache_dir_;
- base::FilePath cache_dir_;
std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs_;
net::FakeURLFetcherFactory url_fetcher_factory_;
};
@@ -266,7 +256,7 @@ TEST_F(PopularSitesTest, AddsIconResourcesToDefaultPages) {
std::unique_ptr<PopularSites> popular_sites =
CreatePopularSites(url_request_context.get());
-#if defined(GOOGLE_CHROME_BUILD) && defined(OS_ANDROID)
+#if defined(GOOGLE_CHROME_BUILD) && (defined(OS_ANDROID) || defined(OS_IOS))
ASSERT_FALSE(popular_sites->sites().empty());
for (const auto& site : popular_sites->sites()) {
EXPECT_THAT(site.default_icon_resource, Gt(0));
@@ -310,22 +300,6 @@ TEST_F(PopularSitesTest, ProvidesDefaultSitesUntilCallbackReturns) {
EXPECT_THAT(popular_sites->sites().size(), Eq(1ul));
}
-TEST_F(PopularSitesTest, ClearsCacheFileFromOldVersions) {
- SetCountryAndVersion("ZZ", "9");
- RespondWithJSON(
- "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
- {kWikipedia});
-
- PopularSites::SitesVector sites;
- const base::FilePath old_cache_path =
- cache_dir_.AppendASCII("suggested_sites.json");
- CHECK(base::ImportantFileWriter::WriteFileAtomically(old_cache_path,
- "Old cache"));
- FetchPopularSites(/*force_download=*/false, &sites);
- worker_pool_owner_.pool()->FlushForTesting();
- EXPECT_FALSE(base::PathExists(old_cache_path));
-}
-
TEST_F(PopularSitesTest, UsesCachedJson) {
SetCountryAndVersion("ZZ", "9");
RespondWithJSON(
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 ea6ee51c758..098e7156ae7 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
@@ -4,6 +4,8 @@
#include "components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h"
+#include <array>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_util.h"
@@ -13,6 +15,7 @@
#include "base/memory/ptr_util.h"
#include "base/task_runner_util.h"
#include "base/values.h"
+#include "components/favicon/core/favicon_service.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"
@@ -24,6 +27,21 @@ namespace ntp_tiles {
namespace {
+using FaviconResultMap = std::map<std::pair<GURL, favicon_base::IconType>,
+ favicon_base::FaviconRawBitmapResult>;
+
+struct IconTypeAndName {
+ favicon_base::IconType type_enum;
+ const char* type_name;
+};
+
+constexpr std::array<IconTypeAndName, 4> kIconTypesAndNames{{
+ {favicon_base::FAVICON, "FAVICON"},
+ {favicon_base::TOUCH_ICON, "TOUCH_ICON"},
+ {favicon_base::TOUCH_PRECOMPOSED_ICON, "TOUCH_PRECOMPOSED_ICON"},
+ {favicon_base::WEB_MANIFEST_ICON, "WEB_MANIFEST_ICON"},
+}};
+
std::string FormatJson(const base::Value& value) {
std::string pretty_printed;
bool ok = base::JSONWriter::WriteWithOptions(
@@ -34,8 +52,12 @@ std::string FormatJson(const base::Value& value) {
} // namespace
-NTPTilesInternalsMessageHandler::NTPTilesInternalsMessageHandler()
- : client_(nullptr), site_count_(8), weak_ptr_factory_(this) {}
+NTPTilesInternalsMessageHandler::NTPTilesInternalsMessageHandler(
+ favicon::FaviconService* favicon_service)
+ : favicon_service_(favicon_service),
+ client_(nullptr),
+ site_count_(8),
+ weak_ptr_factory_(this) {}
NTPTilesInternalsMessageHandler::~NTPTilesInternalsMessageHandler() = default;
@@ -73,7 +95,7 @@ void NTPTilesInternalsMessageHandler::HandleRegisterForEvents(
disabled.SetBoolean("whitelist", false);
client_->CallJavascriptFunction(
"chrome.ntp_tiles_internals.receiveSourceInfo", disabled);
- SendTiles(NTPTilesVector());
+ SendTiles(NTPTilesVector(), FaviconResultMap());
return;
}
DCHECK(args->empty());
@@ -216,7 +238,9 @@ void NTPTilesInternalsMessageHandler::SendSourceInfo() {
"chrome.ntp_tiles_internals.receiveSourceInfo", value);
}
-void NTPTilesInternalsMessageHandler::SendTiles(const NTPTilesVector& tiles) {
+void NTPTilesInternalsMessageHandler::SendTiles(
+ const NTPTilesVector& tiles,
+ const FaviconResultMap& result_map) {
auto sites_list = base::MakeUnique<base::ListValue>();
for (const NTPTile& tile : tiles) {
auto entry = base::MakeUnique<base::DictionaryValue>();
@@ -225,6 +249,24 @@ void NTPTilesInternalsMessageHandler::SendTiles(const NTPTilesVector& tiles) {
entry->SetInteger("source", static_cast<int>(tile.source));
entry->SetString("whitelistIconPath",
tile.whitelist_icon_path.LossyDisplayName());
+
+ auto icon_list = base::MakeUnique<base::ListValue>();
+ for (const auto& entry : kIconTypesAndNames) {
+ FaviconResultMap::const_iterator it = result_map.find(
+ FaviconResultMap::key_type(tile.url, entry.type_enum));
+
+ if (it != result_map.end()) {
+ const favicon_base::FaviconRawBitmapResult& result = it->second;
+ auto icon = base::MakeUnique<base::DictionaryValue>();
+ icon->SetString("url", result.icon_url.spec());
+ icon->SetString("type", entry.type_name);
+ icon->SetInteger("width", result.pixel_size.width());
+ icon->SetInteger("height", result.pixel_size.height());
+ icon_list->Append(std::move(icon));
+ }
+ }
+ entry->Set("icons", std::move(icon_list));
+
sites_list->Append(std::move(entry));
}
@@ -236,10 +278,47 @@ void NTPTilesInternalsMessageHandler::SendTiles(const NTPTilesVector& tiles) {
void NTPTilesInternalsMessageHandler::OnMostVisitedURLsAvailable(
const NTPTilesVector& tiles) {
- SendTiles(tiles);
+ cancelable_task_tracker_.TryCancelAll();
+
+ if (tiles.empty()) {
+ SendTiles(tiles, FaviconResultMap());
+ return;
+ }
+
+ auto on_lookup_done = base::BindRepeating(
+ &NTPTilesInternalsMessageHandler::OnFaviconLookupDone,
+ // Unretained(this) is safe because of |cancelable_task_tracker_|.
+ base::Unretained(this), tiles, base::Owned(new FaviconResultMap()),
+ base::Owned(new size_t(tiles.size() * kIconTypesAndNames.size())));
+
+ for (const NTPTile& tile : tiles) {
+ for (const auto& entry : kIconTypesAndNames) {
+ favicon_service_->GetLargestRawFaviconForPageURL(
+ tile.url, std::vector<int>(1U, entry.type_enum),
+ /*minimum_size_in_pixels=*/0, base::Bind(on_lookup_done, tile.url),
+ &cancelable_task_tracker_);
+ }
+ }
}
void NTPTilesInternalsMessageHandler::OnIconMadeAvailable(
const GURL& site_url) {}
+void NTPTilesInternalsMessageHandler::OnFaviconLookupDone(
+ const NTPTilesVector& tiles,
+ FaviconResultMap* result_map,
+ size_t* num_pending_lookups,
+ const GURL& page_url,
+ const favicon_base::FaviconRawBitmapResult& result) {
+ DCHECK_NE(0u, *num_pending_lookups);
+
+ result_map->emplace(
+ std::pair<GURL, favicon_base::IconType>(page_url, result.icon_type),
+ result);
+
+ --*num_pending_lookups;
+ if (*num_pending_lookups == 0)
+ SendTiles(tiles, *result_map);
+}
+
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h
index 03b58c40f52..4306f832692 100644
--- a/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h
+++ b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h
@@ -5,18 +5,25 @@
#ifndef COMPONENTS_NTP_TILES_WEBUI_NTP_TILES_INTERNALS_MESSAGE_HANDLER_H_
#define COMPONENTS_NTP_TILES_WEBUI_NTP_TILES_INTERNALS_MESSAGE_HANDLER_H_
+#include <map>
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/favicon_base/favicon_types.h"
#include "components/ntp_tiles/most_visited_sites.h"
namespace base {
class ListValue;
} // namespace base
+namespace favicon {
+class FaviconService;
+} // namespace favicon
+
namespace ntp_tiles {
class MostVisitedSites;
@@ -29,7 +36,9 @@ class NTPTilesInternalsMessageHandlerClient;
// to the embedder's API. It cannot itself implement either API directly.
class NTPTilesInternalsMessageHandler : public MostVisitedSites::Observer {
public:
- NTPTilesInternalsMessageHandler();
+ // |favicon_service| must not be null and must outlive this object.
+ explicit NTPTilesInternalsMessageHandler(
+ favicon::FaviconService* favicon_service);
~NTPTilesInternalsMessageHandler() override;
// Called when the WebUI page's JavaScript has loaded and it is ready to
@@ -37,6 +46,9 @@ class NTPTilesInternalsMessageHandler : public MostVisitedSites::Observer {
void RegisterMessages(NTPTilesInternalsMessageHandlerClient* client);
private:
+ using FaviconResultMap = std::map<std::pair<GURL, favicon_base::IconType>,
+ favicon_base::FaviconRawBitmapResult>;
+
// Callbacks registered in RegisterMessages().
void HandleRegisterForEvents(const base::ListValue* args);
void HandleUpdate(const base::ListValue* args);
@@ -44,12 +56,21 @@ class NTPTilesInternalsMessageHandler : public MostVisitedSites::Observer {
void HandleViewPopularSitesJson(const base::ListValue* args);
void SendSourceInfo();
- void SendTiles(const NTPTilesVector& tiles);
+ void SendTiles(const NTPTilesVector& tiles,
+ const FaviconResultMap& result_map);
// MostVisitedSites::Observer.
void OnMostVisitedURLsAvailable(const NTPTilesVector& tiles) override;
void OnIconMadeAvailable(const GURL& site_url) override;
+ void OnFaviconLookupDone(const NTPTilesVector& tiles,
+ FaviconResultMap* result_map,
+ size_t* num_pending_lookups,
+ const GURL& page_url,
+ const favicon_base::FaviconRawBitmapResult& result);
+
+ favicon::FaviconService* favicon_service_;
+
// Bridge to embedder's API.
NTPTilesInternalsMessageHandlerClient* client_;
@@ -59,6 +80,7 @@ class NTPTilesInternalsMessageHandler : public MostVisitedSites::Observer {
std::string suggestions_status_;
std::string popular_sites_json_;
+ base::CancelableTaskTracker cancelable_task_tracker_;
base::WeakPtrFactory<NTPTilesInternalsMessageHandler> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NTPTilesInternalsMessageHandler);
diff --git a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.cc b/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.cc
deleted file mode 100644
index 68cc22283b4..00000000000
--- a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/ntp_tiles/webui/popular_sites_internals_message_handler.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/json/json_writer.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/values.h"
-#include "components/ntp_tiles/popular_sites.h"
-#include "components/ntp_tiles/pref_names.h"
-#include "components/ntp_tiles/webui/popular_sites_internals_message_handler_client.h"
-#include "components/prefs/pref_service.h"
-#include "components/url_formatter/url_fixer.h"
-#include "url/gurl.h"
-
-namespace ntp_tiles {
-
-PopularSitesInternalsMessageHandlerClient::
- PopularSitesInternalsMessageHandlerClient() = default;
-PopularSitesInternalsMessageHandlerClient::
- ~PopularSitesInternalsMessageHandlerClient() = default;
-
-PopularSitesInternalsMessageHandler::PopularSitesInternalsMessageHandler(
- PopularSitesInternalsMessageHandlerClient* web_ui)
- : web_ui_(web_ui), weak_ptr_factory_(this) {}
-
-PopularSitesInternalsMessageHandler::~PopularSitesInternalsMessageHandler() =
- default;
-
-void PopularSitesInternalsMessageHandler::RegisterMessages() {
- web_ui_->RegisterMessageCallback(
- "registerForEvents",
- base::Bind(&PopularSitesInternalsMessageHandler::HandleRegisterForEvents,
- base::Unretained(this)));
-
- web_ui_->RegisterMessageCallback(
- "update", base::Bind(&PopularSitesInternalsMessageHandler::HandleUpdate,
- base::Unretained(this)));
-
- web_ui_->RegisterMessageCallback(
- "viewJson",
- base::Bind(&PopularSitesInternalsMessageHandler::HandleViewJson,
- base::Unretained(this)));
-}
-
-void PopularSitesInternalsMessageHandler::HandleRegisterForEvents(
- const base::ListValue* args) {
- DCHECK(args->empty());
-
- SendOverrides();
-
- popular_sites_ = web_ui_->MakePopularSites();
- SendSites();
-}
-
-void PopularSitesInternalsMessageHandler::HandleUpdate(
- const base::ListValue* args) {
- DCHECK_EQ(4u, args->GetSize());
-
- PrefService* prefs = web_ui_->GetPrefs();
-
- std::string url;
- args->GetString(0, &url);
- if (url.empty())
- prefs->ClearPref(ntp_tiles::prefs::kPopularSitesOverrideURL);
- else
- prefs->SetString(ntp_tiles::prefs::kPopularSitesOverrideURL,
- url_formatter::FixupURL(url, std::string()).spec());
-
- std::string directory;
- args->GetString(1, &directory);
- if (directory.empty())
- prefs->ClearPref(ntp_tiles::prefs::kPopularSitesOverrideDirectory);
- else
- prefs->SetString(ntp_tiles::prefs::kPopularSitesOverrideDirectory,
- directory);
-
- std::string country;
- args->GetString(2, &country);
- if (country.empty())
- prefs->ClearPref(ntp_tiles::prefs::kPopularSitesOverrideCountry);
- else
- prefs->SetString(ntp_tiles::prefs::kPopularSitesOverrideCountry, country);
-
- std::string version;
- args->GetString(3, &version);
- if (version.empty())
- prefs->ClearPref(ntp_tiles::prefs::kPopularSitesOverrideVersion);
- else
- prefs->SetString(ntp_tiles::prefs::kPopularSitesOverrideVersion, version);
-
- popular_sites_ = web_ui_->MakePopularSites();
- popular_sites_->MaybeStartFetch(
- true,
- base::Bind(&PopularSitesInternalsMessageHandler::OnPopularSitesAvailable,
- base::Unretained(this)));
-}
-
-void PopularSitesInternalsMessageHandler::HandleViewJson(
- const base::ListValue* args) {
- DCHECK_EQ(0u, args->GetSize());
-
- const base::ListValue* json = popular_sites_->GetCachedJson();
- std::string json_string;
- if (json) {
- bool success = base::JSONWriter::Write(*json, &json_string);
- DCHECK(success);
- }
- SendJson(json_string);
-}
-
-void PopularSitesInternalsMessageHandler::SendOverrides() {
- PrefService* prefs = web_ui_->GetPrefs();
- std::string url =
- prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideURL);
- std::string directory =
- prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideDirectory);
- std::string country =
- prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideCountry);
- std::string version =
- prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideVersion);
- web_ui_->CallJavascriptFunction(
- "chrome.popular_sites_internals.receiveOverrides", base::Value(url),
- base::Value(directory), base::Value(country), base::Value(version));
-}
-
-void PopularSitesInternalsMessageHandler::SendDownloadResult(bool success) {
- base::Value result(success ? "Success" : "Fail");
- web_ui_->CallJavascriptFunction(
- "chrome.popular_sites_internals.receiveDownloadResult", result);
-}
-
-void PopularSitesInternalsMessageHandler::SendSites() {
- auto sites_list = base::MakeUnique<base::ListValue>();
- for (const PopularSites::Site& site : popular_sites_->sites()) {
- auto entry = base::MakeUnique<base::DictionaryValue>();
- entry->SetString("title", site.title);
- entry->SetString("url", site.url.spec());
- sites_list->Append(std::move(entry));
- }
-
- base::DictionaryValue result;
- result.Set("sites", std::move(sites_list));
- result.SetString("url", popular_sites_->GetLastURLFetched().spec());
- web_ui_->CallJavascriptFunction("chrome.popular_sites_internals.receiveSites",
- result);
-}
-
-void PopularSitesInternalsMessageHandler::SendJson(const std::string& json) {
- web_ui_->CallJavascriptFunction("chrome.popular_sites_internals.receiveJson",
- base::Value(json));
-}
-
-void PopularSitesInternalsMessageHandler::OnPopularSitesAvailable(
- bool success) {
- SendDownloadResult(success);
- SendSites();
-}
-
-} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.h b/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.h
deleted file mode 100644
index c0e05d0492b..00000000000
--- a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.h
+++ /dev/null
@@ -1,64 +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_NTP_TILES_WEBUI_POPULAR_SITES_HANDLER_H_
-#define COMPONENTS_NTP_TILES_WEBUI_POPULAR_SITES_HANDLER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-
-namespace base {
-class ListValue;
-} // namespace base
-
-namespace ntp_tiles {
-
-class PopularSites;
-class PopularSitesInternalsMessageHandlerClient;
-
-// Implements the WebUI message handler for chrome://popular-sites-internals/
-//
-// Because content and iOS use different implementations of WebUI, this class
-// implements the generic portion and depends on the embedder to inject a bridge
-// to the embedder's API. It cannot itself implement either API directly.
-class PopularSitesInternalsMessageHandler {
- public:
- explicit PopularSitesInternalsMessageHandler(
- PopularSitesInternalsMessageHandlerClient* web_ui);
- ~PopularSitesInternalsMessageHandler();
-
- // Called when the WebUI page's JavaScript has loaded and it is ready to
- // receive RegisterMessageCallback() calls.
- void RegisterMessages();
-
- private:
- // Callbacks registered in RegisterMessages().
- void HandleRegisterForEvents(const base::ListValue* args);
- void HandleUpdate(const base::ListValue* args);
- void HandleViewJson(const base::ListValue* args);
-
- void SendOverrides();
- void SendDownloadResult(bool success);
- void SendSites();
- void SendJson(const std::string& json);
-
- // Completion handler for popular_sites_->StartFetch().
- void OnPopularSitesAvailable(bool success);
-
- // Bridge to embedder's API.
- PopularSitesInternalsMessageHandlerClient* web_ui_;
-
- std::unique_ptr<PopularSites> popular_sites_;
-
- base::WeakPtrFactory<PopularSitesInternalsMessageHandler> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(PopularSitesInternalsMessageHandler);
-};
-
-} // namespace ntp_tiles
-
-#endif // COMPONENTS_NTP_TILES_WEBUI_POPULAR_SITES_HANDLER_H_
diff --git a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler_client.h b/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler_client.h
deleted file mode 100644
index 43368f8d8c6..00000000000
--- a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler_client.h
+++ /dev/null
@@ -1,61 +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_NTP_TILES_WEBUI_POPULAR_SITES_HANDLER_CLIENT_H_
-#define COMPONENTS_NTP_TILES_WEBUI_POPULAR_SITES_HANDLER_CLIENT_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-
-class PrefService;
-
-namespace base {
-class Value;
-class ListValue;
-} // namespace base
-
-namespace ntp_tiles {
-
-class PopularSites;
-
-// Implemented by embedders to hook up PopularSitesInternalsMessageHandler.
-class PopularSitesInternalsMessageHandlerClient {
- public:
- // Returns the PrefService for the embedder and containing WebUI page.
- virtual PrefService* GetPrefs() = 0;
-
- // Creates a new PopularSites based on the context pf the WebUI page.
- virtual std::unique_ptr<ntp_tiles::PopularSites> MakePopularSites() = 0;
-
- // Registers a callback in Javascript. See content::WebUI and web::WebUIIOS.
- virtual void RegisterMessageCallback(
- const std::string& message,
- const base::Callback<void(const base::ListValue*)>& callback) = 0;
-
- // Invokes a function in Javascript. See content::WebUI and web::WebUIIOS.
- virtual void CallJavascriptFunctionVector(
- const std::string& name,
- const std::vector<const base::Value*>& values) = 0;
-
- // Helper function for CallJavascriptFunctionVector().
- template <typename... Arg>
- void CallJavascriptFunction(const std::string& name, const Arg&... arg) {
- CallJavascriptFunctionVector(name, {&arg...});
- }
-
- protected:
- PopularSitesInternalsMessageHandlerClient();
- virtual ~PopularSitesInternalsMessageHandlerClient();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PopularSitesInternalsMessageHandlerClient);
-};
-
-} // namespace ntp_tiles
-
-#endif // COMPONENTS_NTP_TILES_WEBUI_POPULAR_SITES_HANDLER_CLIENT_H_
diff --git a/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
index ea70d72ccf7..c407b726676 100644
--- a/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
+++ b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
@@ -116,12 +116,18 @@ found in the LICENSE file.
<td class="value" jsdisplay="source == 1">SUGGESTIONS_SERVICE</td>
<td class="value" jsdisplay="source == 2">POPULAR</td>
<td class="value" jsdisplay="source == 3">WHITELIST</td>
- <td class="value" jsdisplay="source &gt; 3">???</td>
+ <td class="value" jsdisplay="source == 4">HOMEPAGE</td>
+ <td class="value" jsdisplay="source &gt; 4">???</td>
</tr>
<tr>
<td class="detail">URL</td>
<td class="value"><a jsvalues="href:url" jscontent="url"></a></td>
</tr>
+ <tr jsselect="icons">
+ <td class="detail" jscontent="type"></td>
+ <td class="value"><a jsvalues="href:url"
+ jscontent="url + ' (' + width + 'x' + height + ')'"></a></td>
+ </tr>
</tbody>
</table>
</div>
diff --git a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.css b/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.css
deleted file mode 100644
index ee4c1d4225e..00000000000
--- a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.css
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright 2015 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-html {
- font-size: 20px;
-}
-
-#info > div {
- width: 100%;
-}
-
-#info h2 {
- color: rgb(74, 142, 230);
- font-size: 100%;
- margin-bottom: 0;
-}
-
-#info .err {
- color: red;
-}
-
-#info .section {
- display: inline-block;
- margin-left: auto;
- margin-right: auto;
-}
-
-#info .section.hidden {
- display: none;
-}
-
-.section-details {
- width: 100%;
-}
-
-.section-details .detail,
-.section-details .value {
- width: 50%;
-}
-
-.section-details tr:nth-child(odd) {
- background: rgb(239, 243, 255);
-}
-
-#json-value {
- font-size: 75%;
-}
diff --git a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html b/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html
deleted file mode 100644
index 5ce97bcf64f..00000000000
--- a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html
+++ /dev/null
@@ -1,86 +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.
--->
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<if expr="is_android or is_ios">
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-</if>
-<title>Popular Sites Internals</title>
-<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
-<link rel="stylesheet" href="chrome://resources/css/list.css">
-<link rel="stylesheet" href="popular_sites_internals.css">
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/jstemplate_compiled.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<if expr="is_ios">
-<!-- TODO(crbug.com/487000): Remove this once injected by web. -->
-<script src="chrome://resources/js/ios/web_ui.js"></script>
-</if>
-<script src="popular_sites_internals.js"></script>
-</head>
-
-<body>
-<div id="info">
- <div class="section" jsskip="true">
- <h2>Download</h2>
- <table class="section-details">
- <tr>
- <td class="detail">URL (takes precedence over Directory, Country and Version)</td>
- <td class="value"><input id="override-url" type="text"></td>
- </tr>
- <tr>
- <td class="detail">Override Directory</td>
- <td class="value"><input id="override-directory" type="text"></td>
- </tr>
- <tr>
- <td class="detail">Override Country</td>
- <td class="value"><input id="override-country" type="text"></td>
- </tr>
- <tr>
- <td class="detail">Override Version</td>
- <td class="value"><input id="override-version" type="text"></td>
- </tr>
- <tr>
- <td class="detail">
- <input id="submit-update" type="submit" value="Update">
- </td>
- <td id="download-result" class="value"></td>
- </tr>
- </table>
- </div>
-
- <div class="section">
- <h2>Info</h2>
- <table class="section-details">
- <tr>
- <td class="detail">URL</td>
- <td class="value" jscontent="url"></td>
- </tr>
- </table>
- </div>
-
- <div class="section">
- <h2>Sites</h2>
- <table class="section-details">
- <tr jsselect="sites">
- <td class="detail" jscontent="title"></td>
- <td class="value" jscontent="url"></td>
- </tr>
- <tr jsskip="true">
- <td class="detail">
- <input id="view-json" type="submit" value="View JSON">
- </td>
- <td class="value"><pre id="json-value"></pre></td>
- </tr>
- </table>
- </div>
-</div>
-
-</body>
-</html>
diff --git a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.js b/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.js
deleted file mode 100644
index 3bfcf8b4c57..00000000000
--- a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('chrome.popular_sites_internals', function() {
- 'use strict';
-
- function initialize() {
- function submitUpdate(event) {
- $('download-result').textContent = '';
- chrome.send('update', [$('override-url').value,
- $('override-directory').value,
- $('override-country').value,
- $('override-version').value]);
- event.preventDefault();
- }
-
- $('submit-update').addEventListener('click', submitUpdate);
-
- function viewJson(event) {
- $('json-value').textContent = '';
- chrome.send('viewJson');
- event.preventDefault();
- }
-
- $('view-json').addEventListener('click', viewJson);
-
- chrome.send('registerForEvents');
- }
-
- function receiveOverrides(url, directory, country, version) {
- $('override-url').value = url;
- $('override-directory').value = directory;
- $('override-country').value = country;
- $('override-version').value = version;
- }
-
- function receiveDownloadResult(result) {
- $('download-result').textContent = result;
- }
-
- function receiveSites(sites) {
- jstProcess(new JsEvalContext(sites), $('info'));
- // Also clear the json string, since it's likely stale now.
- $('json-value').textContent = '';
- }
-
- function receiveJson(json) {
- $('json-value').textContent = json;
- }
-
- // Return an object with all of the exports.
- return {
- initialize: initialize,
- receiveOverrides: receiveOverrides,
- receiveDownloadResult: receiveDownloadResult,
- receiveSites: receiveSites,
- receiveJson: receiveJson,
- };
-});
-
-document.addEventListener('DOMContentLoaded',
- chrome.popular_sites_internals.initialize);
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 eef6057d986..feb37418940 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
@@ -45,11 +45,6 @@ void GetVisualsForItemHelperCallback(ScopedJavaGlobalRef<jobject> j_callback,
} // namespace
-// static.
-bool OfflineContentAggregatorBridge::Register(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
// static
base::android::ScopedJavaLocalRef<jobject>
OfflineContentAggregatorBridge::GetBridgeForOfflineContentAggregator(
diff --git a/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.h b/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.h
index e367edf468e..0c4dfc1f7a0 100644
--- a/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.h
+++ b/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.h
@@ -29,9 +29,6 @@ namespace android {
class OfflineContentAggregatorBridge : public OfflineContentProvider::Observer,
public base::SupportsUserData::Data {
public:
- // Helper method to initialize the JNI hooks between Java and C++.
- static bool Register(JNIEnv* env);
-
// Returns a Java OfflineContentAggregatorBridge for |aggregator|. There will
// be only one bridge per OfflineContentAggregator.
static base::android::ScopedJavaLocalRef<jobject>
diff --git a/chromium/components/offline_items_collection/core/offline_content_aggregator.cc b/chromium/components/offline_items_collection/core/offline_content_aggregator.cc
index 19eaa574efe..64ba2e23004 100644
--- a/chromium/components/offline_items_collection/core/offline_content_aggregator.cc
+++ b/chromium/components/offline_items_collection/core/offline_content_aggregator.cc
@@ -238,7 +238,7 @@ void OfflineContentAggregator::CheckAndNotifyItemsAvailable() {
// Notify all observers who haven't been told about the initialization that we
// are initialized. Track the observers so that we don't notify them again.
for (auto& observer : observers_) {
- if (!base::ContainsValue(signaled_observers_, &observer)) {
+ if (!base::ContainsKey(signaled_observers_, &observer)) {
observer.OnItemsAvailable(this);
signaled_observers_.insert(&observer);
}
diff --git a/chromium/components/offline_pages/content/BUILD.gn b/chromium/components/offline_pages/content/BUILD.gn
deleted file mode 100644
index 5b1ddb58446..00000000000
--- a/chromium/components/offline_pages/content/BUILD.gn
+++ /dev/null
@@ -1,45 +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.
-
-if (is_android) {
- import("//build/config/android/rules.gni")
-}
-
-static_library("content") {
- sources = [
- "prefetch_service_factory.cc",
- "prefetch_service_factory.h",
- "suggested_articles_observer.cc",
- "suggested_articles_observer.h",
- ]
-
- deps = [
- "//base",
- "//components/keyed_service/content",
- "//components/ntp_snippets",
- "//components/offline_pages/core",
- "//components/offline_pages/core:switches",
- "//components/offline_pages/core/prefetch",
- "//content/public/browser",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "suggested_articles_observer_unittest.cc",
- ]
-
- deps = [
- ":content",
- "//base",
- "//base/test:test_support",
- "//components/offline_pages/core",
- "//components/offline_pages/core:test_support",
- "//components/offline_pages/core/prefetch",
- "//content/test:test_support",
- "//testing/gtest",
- "//url",
- ]
-}
diff --git a/chromium/components/offline_pages/content/DEPS b/chromium/components/offline_pages/content/DEPS
deleted file mode 100644
index 4edd3b18c37..00000000000
--- a/chromium/components/offline_pages/content/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
- "+components/keyed_service/content",
- "+components/ntp_snippets",
- "+content/public/browser",
- "+content/public/test",
-]
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 3bfec122b17..3a08f96525a 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
@@ -63,6 +63,7 @@ void BackgroundLoaderContents::CanDownload(
bool BackgroundLoaderContents::ShouldCreateWebContents(
content::WebContents* web_contents,
+ content::RenderFrameHost* opener,
content::SiteInstance* source_site_instance,
int32_t route_id,
int32_t main_frame_route_id,
@@ -115,6 +116,25 @@ bool BackgroundLoaderContents::CheckMediaAccessPermission(
return false; // No permissions granted.
}
+void BackgroundLoaderContents::AdjustPreviewsStateForNavigation(
+ content::PreviewsState* previews_state) {
+ DCHECK(previews_state);
+
+ // If previews are already disabled, do nothing.
+ if (*previews_state == content::PREVIEWS_OFF ||
+ *previews_state == content::PREVIEWS_NO_TRANSFORM) {
+ return;
+ }
+
+ if (*previews_state == content::PREVIEWS_UNSPECIFIED) {
+ *previews_state = content::PARTIAL_CONTENT_SAFE_PREVIEWS;
+ } else {
+ *previews_state &= content::PARTIAL_CONTENT_SAFE_PREVIEWS;
+ if (*previews_state == 0)
+ *previews_state = content::PREVIEWS_OFF;
+ }
+}
+
BackgroundLoaderContents::BackgroundLoaderContents()
: browser_context_(nullptr) {
web_contents_.reset();
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 a8ba09d45e0..ceb0641d39e 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
@@ -45,6 +45,7 @@ class BackgroundLoaderContents : public content::WebContentsDelegate {
bool ShouldCreateWebContents(
content::WebContents* web_contents,
+ content::RenderFrameHost* opener,
content::SiteInstance* source_site_instance,
int32_t route_id,
int32_t main_frame_route_id,
@@ -75,6 +76,9 @@ class BackgroundLoaderContents : public content::WebContentsDelegate {
const GURL& security_origin,
content::MediaStreamType type) override;
+ void AdjustPreviewsStateForNavigation(
+ content::PreviewsState* previews_state) override;
+
private:
friend class BackgroundLoaderContentsTest;
friend class BackgroundLoaderContentsStub;
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 3a31cce5190..ddfe43a3e5b 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
@@ -96,9 +96,9 @@ TEST_F(BackgroundLoaderContentsTest, CannotDownload) {
TEST_F(BackgroundLoaderContentsTest, ShouldNotCreateWebContents) {
ASSERT_FALSE(contents()->ShouldCreateWebContents(
- nullptr /* contents */, nullptr /* source_site_instance */,
- 0 /* route_id */, 0 /* main_frame_route_id */,
- 0 /* main_frame_widget_route_id */,
+ nullptr /* contents */, nullptr /* opener */,
+ nullptr /* source_site_instance */, 0 /* route_id */,
+ 0 /* main_frame_route_id */, 0 /* main_frame_widget_route_id */,
content::mojom::WindowContainerType::NORMAL /* window_container_type */,
GURL() /* opener_url */, "foo" /* frame_name */,
GURL::EmptyGURL() /* target_url */, "bar" /* partition_id */,
@@ -146,4 +146,34 @@ TEST_F(BackgroundLoaderContentsTest, CheckMediaAccessPermissionFalse) {
content::MediaStreamType::MEDIA_TAB_VIDEO_CAPTURE /* type */));
}
+TEST_F(BackgroundLoaderContentsTest, AdjustPreviewsState) {
+ content::PreviewsState previews_state;
+
+ // If the state starts out as off or disabled, it should stay that way.
+ previews_state = content::PREVIEWS_OFF;
+ contents()->AdjustPreviewsStateForNavigation(&previews_state);
+ EXPECT_EQ(previews_state, content::PREVIEWS_OFF);
+ previews_state = content::PREVIEWS_NO_TRANSFORM;
+ contents()->AdjustPreviewsStateForNavigation(&previews_state);
+ EXPECT_EQ(previews_state, content::PREVIEWS_NO_TRANSFORM);
+
+ // If the state starts out as a state unfriendly to offlining, we should
+ // and out the unfriendly previews.
+ previews_state = content::SERVER_LOFI_ON | content::CLIENT_LOFI_ON;
+ contents()->AdjustPreviewsStateForNavigation(&previews_state);
+ EXPECT_EQ(previews_state, content::SERVER_LOFI_ON);
+
+ // If the state starts out as offlining friendly previews, we should preserve
+ // them.
+ previews_state = content::PARTIAL_CONTENT_SAFE_PREVIEWS;
+ contents()->AdjustPreviewsStateForNavigation(&previews_state);
+ EXPECT_EQ(previews_state, content::PARTIAL_CONTENT_SAFE_PREVIEWS);
+
+ // If there are only offlining unfriendly previews, they should all get turned
+ // off.
+ previews_state = content::CLIENT_LOFI_ON;
+ contents()->AdjustPreviewsStateForNavigation(&previews_state);
+ EXPECT_EQ(previews_state, content::PREVIEWS_OFF);
+}
+
} // namespace background_loader
diff --git a/chromium/components/offline_pages/content/prefetch_service_factory.cc b/chromium/components/offline_pages/content/prefetch_service_factory.cc
deleted file mode 100644
index ffe46b26308..00000000000
--- a/chromium/components/offline_pages/content/prefetch_service_factory.cc
+++ /dev/null
@@ -1,36 +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/offline_pages/content/prefetch_service_factory.h"
-
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
-#include "content/public/browser/browser_context.h"
-
-namespace offline_pages {
-
-PrefetchServiceFactory::PrefetchServiceFactory()
- : BrowserContextKeyedServiceFactory(
- "OfflinePagePrefetchService",
- BrowserContextDependencyManager::GetInstance()) {}
-
-// static
-PrefetchServiceFactory* PrefetchServiceFactory::GetInstance() {
- return base::Singleton<PrefetchServiceFactory>::get();
-}
-
-// static
-PrefetchService* PrefetchServiceFactory::GetForBrowserContext(
- content::BrowserContext* context) {
- return static_cast<PrefetchService*>(
- GetInstance()->GetServiceForBrowserContext(context, true));
-}
-
-KeyedService* PrefetchServiceFactory::BuildServiceInstanceFor(
- content::BrowserContext* context) const {
- return new PrefetchServiceImpl();
-}
-
-} // namespace offline_pages
diff --git a/chromium/components/offline_pages/content/prefetch_service_factory.h b/chromium/components/offline_pages/content/prefetch_service_factory.h
deleted file mode 100644
index c7f4fb16696..00000000000
--- a/chromium/components/offline_pages/content/prefetch_service_factory.h
+++ /dev/null
@@ -1,43 +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_OFFLINE_PAGES_CONTENT_PREFETCH_SERVICE_FACTORY_H_
-#define COMPONENTS_OFFLINE_PAGES_CONTENT_PREFETCH_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 base
-
-namespace offline_pages {
-
-class PrefetchService;
-
-// A factory to create one PrefetchServiceImpl per browser context. Prefetching
-// Offline Pages is not supported in incognito, so this class uses default
-// implementation of |GetBrowserContextToUse|.
-class PrefetchServiceFactory : public BrowserContextKeyedServiceFactory {
- public:
- static PrefetchServiceFactory* GetInstance();
- static PrefetchService* GetForBrowserContext(
- content::BrowserContext* context);
-
- private:
- friend struct base::DefaultSingletonTraits<PrefetchServiceFactory>;
-
- PrefetchServiceFactory();
- ~PrefetchServiceFactory() override {}
-
- KeyedService* BuildServiceInstanceFor(
- content::BrowserContext* context) const override;
-
- DISALLOW_COPY_AND_ASSIGN(PrefetchServiceFactory);
-};
-
-} // namespace offline_pages
-
-#endif // COMPONENTS_OFFLINE_PAGES_CONTENT_PREFETCH_SERVICE_FACTORY_H_
diff --git a/chromium/components/offline_pages/content/suggested_articles_observer.cc b/chromium/components/offline_pages/content/suggested_articles_observer.cc
deleted file mode 100644
index 4b60a286f7b..00000000000
--- a/chromium/components/offline_pages/content/suggested_articles_observer.cc
+++ /dev/null
@@ -1,170 +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/offline_pages/content/suggested_articles_observer.h"
-
-#include <unordered_set>
-
-#include "base/memory/ptr_util.h"
-#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/category_status.h"
-#include "components/offline_pages/content/prefetch_service_factory.h"
-#include "components/offline_pages/core/client_namespace_constants.h"
-#include "components/offline_pages/core/offline_page_feature.h"
-#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
-#include "components/offline_pages/core/prefetch/prefetch_service.h"
-
-using ntp_snippets::Category;
-using ntp_snippets::ContentSuggestion;
-
-namespace offline_pages {
-
-namespace {
-
-int kOfflinePageSuggestedArticlesObserverUserDataKey;
-
-const ntp_snippets::Category& ArticlesCategory() {
- static ntp_snippets::Category articles =
- Category::FromKnownCategory(ntp_snippets::KnownCategories::ARTICLES);
- return articles;
-}
-
-ClientId CreateClientIDFromSuggestionId(const ContentSuggestion::ID& id) {
- return ClientId(kSuggestedArticlesNamespace, id.id_within_category());
-}
-
-// The default delegate that contains external dependencies for the Offline Page
-// Suggestions Observer. This is unused in tests, which implement their own
-// Delegate.
-class DefaultDelegate : public SuggestedArticlesObserver::Delegate {
- public:
- explicit DefaultDelegate(ntp_snippets::ContentSuggestionsService* service);
- ~DefaultDelegate() override = default;
-
- const std::vector<ContentSuggestion>& GetSuggestions(
- const Category& category) override;
- PrefetchService* GetPrefetchService(
- content::BrowserContext* context) override;
-
- private:
- ntp_snippets::ContentSuggestionsService* service_;
-};
-
-DefaultDelegate::DefaultDelegate(
- ntp_snippets::ContentSuggestionsService* service)
- : service_(service) {}
-
-const std::vector<ContentSuggestion>& DefaultDelegate::GetSuggestions(
- const Category& category) {
- return service_->GetSuggestionsForCategory(category);
-}
-
-PrefetchService* DefaultDelegate::GetPrefetchService(
- content::BrowserContext* context) {
- return PrefetchServiceFactory::GetForBrowserContext(context);
-}
-
-} // namespace
-
-// static
-void SuggestedArticlesObserver::ObserveContentSuggestionsService(
- content::BrowserContext* browser_context,
- ntp_snippets::ContentSuggestionsService* service) {
- if (!offline_pages::IsPrefetchingOfflinePagesEnabled())
- return;
-
- auto suggestions_observer = base::MakeUnique<SuggestedArticlesObserver>(
- browser_context, base::MakeUnique<DefaultDelegate>(service));
- service->AddObserver(suggestions_observer.get());
- service->SetUserData(&kOfflinePageSuggestedArticlesObserverUserDataKey,
- std::move(suggestions_observer));
-}
-
-SuggestedArticlesObserver::SuggestedArticlesObserver(
- content::BrowserContext* browser_context,
- std::unique_ptr<Delegate> delegate)
- : browser_context_(browser_context), delegate_(std::move(delegate)) {}
-
-SuggestedArticlesObserver::~SuggestedArticlesObserver() = default;
-
-void SuggestedArticlesObserver::OnNewSuggestions(Category category) {
- // TODO(dewittj): Change this to check whether a given category is not
- // a _remote_ category.
- if (category != ArticlesCategory() ||
- category_status_ != ntp_snippets::CategoryStatus::AVAILABLE) {
- return;
- }
-
- const std::vector<ContentSuggestion>& suggestions =
- delegate_->GetSuggestions(ArticlesCategory());
- if (suggestions.empty())
- return;
-
- std::vector<PrefetchDispatcher::PrefetchURL> prefetch_urls;
- for (const ContentSuggestion& suggestion : suggestions) {
- prefetch_urls.push_back(
- {CreateClientIDFromSuggestionId(suggestion.id()), suggestion.url()});
- }
-
- PrefetchService* service = delegate_->GetPrefetchService(browser_context_);
- if (service == nullptr) {
- DVLOG(1) << "PrefetchService unavailable to the "
- "SuggestedArticlesObserver.";
- return;
- }
- service->GetDispatcher()->AddCandidatePrefetchURLs(prefetch_urls);
-}
-
-void SuggestedArticlesObserver::OnCategoryStatusChanged(
- Category category,
- ntp_snippets::CategoryStatus new_status) {
- if (category != ArticlesCategory() || category_status_ == new_status)
- return;
-
- category_status_ = new_status;
-
- if (category_status_ ==
- ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED ||
- category_status_ ==
- ntp_snippets::CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED) {
- PrefetchService* service = delegate_->GetPrefetchService(browser_context_);
- if (service == nullptr) {
- DVLOG(1) << "PrefetchService unavailable to the "
- "SuggestedArticlesObserver.";
- return;
- }
- service->GetDispatcher()->RemoveAllUnprocessedPrefetchURLs(
- kSuggestedArticlesNamespace);
- }
-}
-
-void SuggestedArticlesObserver::OnSuggestionInvalidated(
- const ContentSuggestion::ID& suggestion_id) {
- PrefetchService* service = delegate_->GetPrefetchService(browser_context_);
- if (service == nullptr) {
- DVLOG(1) << "PrefetchService unavailable to the "
- "SuggestedArticlesObserver.";
- return;
- }
- service->GetDispatcher()->RemovePrefetchURLsByClientId(
- CreateClientIDFromSuggestionId(suggestion_id));
-}
-
-void SuggestedArticlesObserver::OnFullRefreshRequired() {
- PrefetchService* service = delegate_->GetPrefetchService(browser_context_);
- if (service == nullptr) {
- DVLOG(1) << "PrefetchService unavailable to the "
- "SuggestedArticlesObserver.";
- return;
- }
- service->GetDispatcher()->RemoveAllUnprocessedPrefetchURLs(
- kSuggestedArticlesNamespace);
- OnNewSuggestions(ArticlesCategory());
-}
-
-void SuggestedArticlesObserver::ContentSuggestionsServiceShutdown() {
- // No need to do anything here, we will just stop getting events.
-}
-
-} // namespace offline_pages
diff --git a/chromium/components/offline_pages/content/suggested_articles_observer.h b/chromium/components/offline_pages/content/suggested_articles_observer.h
deleted file mode 100644
index 6448c013538..00000000000
--- a/chromium/components/offline_pages/content/suggested_articles_observer.h
+++ /dev/null
@@ -1,80 +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_OFFLINE_PAGES_CONTENT_SUGGESTED_ARTICLES_OBSERVER_H_
-#define COMPONENTS_OFFLINE_PAGES_CONTENT_SUGGESTED_ARTICLES_OBSERVER_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "components/ntp_snippets/content_suggestions_service.h"
-
-namespace content {
-class BrowserContext;
-} // namespace content
-
-namespace ntp_snippets {
-class Category;
-}
-
-namespace offline_pages {
-class PrefetchService;
-
-// Observes the ContentSuggestionsService, listening for new suggestions in the
-// ARTICLES category. When those suggestions arrive, it then forwards them to
-// the Prefetch Service, which does not know about Content Suggestions
-// specifically.
-class SuggestedArticlesObserver
- : public ntp_snippets::ContentSuggestionsService::Observer,
- public base::SupportsUserData::Data {
- public:
- // Delegate exists to allow for dependency injection in unit tests.
- // SuggestedArticlesObserver implements its own delegate, |DefaultDelegate| in
- // the .cc file that forwards to the ContentSuggestionsService and the
- // PrefetchServiceFactory. Code inside |DefaultDelegate| should be as simple
- // as possible, since it will only be covered by instrumentation/browser
- // tests.
- class Delegate {
- public:
- virtual const std::vector<ntp_snippets::ContentSuggestion>& GetSuggestions(
- const ntp_snippets::Category& category) = 0;
- virtual PrefetchService* GetPrefetchService(
- content::BrowserContext* context) = 0;
- virtual ~Delegate() = default;
- };
-
- // This API creates a new SuggestedArticlesObserver and adds it as an
- // observer to the ContentSuggestionsService provided. Its lifetime is
- // managed by the ContentSuggestionsService.
- static void ObserveContentSuggestionsService(
- content::BrowserContext* browser_context,
- ntp_snippets::ContentSuggestionsService* service);
-
- SuggestedArticlesObserver(content::BrowserContext* browser_context,
- std::unique_ptr<Delegate> delegate);
- ~SuggestedArticlesObserver() override;
-
- // ContentSuggestionsService::Observer overrides.
- void OnNewSuggestions(ntp_snippets::Category category) override;
- void OnCategoryStatusChanged(
- ntp_snippets::Category category,
- ntp_snippets::CategoryStatus new_status) override;
- void OnSuggestionInvalidated(
- const ntp_snippets::ContentSuggestion::ID& suggestion_id) override;
- void OnFullRefreshRequired() override;
- void ContentSuggestionsServiceShutdown() override;
-
- private:
- content::BrowserContext* browser_context_;
- ntp_snippets::CategoryStatus category_status_ =
- ntp_snippets::CategoryStatus::INITIALIZING;
- std::unique_ptr<Delegate> delegate_;
-
- DISALLOW_COPY_AND_ASSIGN(SuggestedArticlesObserver);
-};
-
-} // namespace offline_pages
-
-#endif // COMPONENTS_OFFLINE_PAGES_CONTENT_SUGGESTED_ARTICLES_OBSERVER_H_
diff --git a/chromium/components/offline_pages/content/suggested_articles_observer_unittest.cc b/chromium/components/offline_pages/content/suggested_articles_observer_unittest.cc
deleted file mode 100644
index 6fae70d85ce..00000000000
--- a/chromium/components/offline_pages/content/suggested_articles_observer_unittest.cc
+++ /dev/null
@@ -1,215 +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/offline_pages/content/suggested_articles_observer.h"
-
-#include "base/run_loop.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/core/client_namespace_constants.h"
-#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
-#include "components/offline_pages/core/prefetch/prefetch_service.h"
-#include "components/offline_pages/core/stub_offline_page_model.h"
-#include "content/public/test/test_browser_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-using ntp_snippets::Category;
-using ntp_snippets::ContentSuggestion;
-
-namespace offline_pages {
-
-namespace {
-
-ContentSuggestion ContentSuggestionFromTestURL(const GURL& test_url) {
- auto category =
- Category::FromKnownCategory(ntp_snippets::KnownCategories::ARTICLES);
- return ContentSuggestion(category, test_url.spec(), test_url);
-}
-
-class TestingPrefetchDispatcher : public PrefetchDispatcher {
- public:
- TestingPrefetchDispatcher() = default;
-
- void AddCandidatePrefetchURLs(
- const std::vector<PrefetchURL>& suggested_urls) override {
- latest_prefetch_urls = suggested_urls;
- new_suggestions_count++;
- }
-
- void RemoveAllUnprocessedPrefetchURLs(
- const std::string& name_space) override {
- DCHECK_EQ(name_space, kSuggestedArticlesNamespace);
- latest_prefetch_urls.clear();
- remove_all_suggestions_count++;
- }
-
- void RemovePrefetchURLsByClientId(const ClientId& client_id) override {
- DCHECK_EQ(client_id.name_space, kSuggestedArticlesNamespace);
- remove_by_client_id_count++;
- last_removed_client_id = base::MakeUnique<ClientId>(client_id);
- }
-
- void BeginBackgroundTask(
- std::unique_ptr<ScopedBackgroundTask> task) override {}
- void StopBackgroundTask(ScopedBackgroundTask* task) override {}
-
- std::vector<PrefetchURL> latest_prefetch_urls;
- std::unique_ptr<ClientId> last_removed_client_id;
-
- int new_suggestions_count = 0;
- int remove_all_suggestions_count = 0;
- int remove_by_client_id_count = 0;
-};
-
-class TestingPrefetchService : public PrefetchService {
- public:
- TestingPrefetchService() = default;
-
- PrefetchDispatcher* GetDispatcher() override { return &dispatcher; };
-
- TestingPrefetchDispatcher dispatcher;
-};
-
-class TestDelegate : public SuggestedArticlesObserver::Delegate {
- public:
- TestDelegate() = default;
- ~TestDelegate() override = default;
-
- const std::vector<ContentSuggestion>& GetSuggestions(
- const Category& category) override {
- get_suggestions_count++;
- return suggestions;
- }
-
- PrefetchService* GetPrefetchService(
- content::BrowserContext* context) override {
- return &prefetch_service;
- }
-
- TestingPrefetchService prefetch_service;
-
- // Public for test manipulation.
- std::vector<ContentSuggestion> suggestions;
-
- // Signals that delegate was called.
- int get_suggestions_count = 0;
-};
-
-} // namespace
-
-class OfflinePageSuggestedArticlesObserverTest : public testing::Test {
- public:
- OfflinePageSuggestedArticlesObserverTest() = default;
-
- void SetUp() override {
- observer_ =
- base::MakeUnique<SuggestedArticlesObserver>(&context_, MakeDelegate());
- }
-
- virtual std::unique_ptr<SuggestedArticlesObserver::Delegate> MakeDelegate() {
- auto delegate_ptr = base::MakeUnique<TestDelegate>();
- test_delegate_ = delegate_ptr.get();
- return std::move(delegate_ptr);
- }
-
- SuggestedArticlesObserver* observer() { return observer_.get(); }
-
- TestDelegate* test_delegate() { return test_delegate_; }
-
- TestingPrefetchService* test_prefetch_service() {
- return &(test_delegate()->prefetch_service);
- }
-
- TestingPrefetchDispatcher* test_prefetch_dispatcher() {
- return &(test_prefetch_service()->dispatcher);
- }
-
- protected:
- Category category =
- Category::FromKnownCategory(ntp_snippets::KnownCategories::ARTICLES);
- content::TestBrowserContext context_;
-
- private:
- std::unique_ptr<SuggestedArticlesObserver> observer_;
- TestDelegate* test_delegate_;
-};
-
-TEST_F(OfflinePageSuggestedArticlesObserverTest,
- CallsDelegateOnNewSuggestions) {
- // We should not do anything if the category is not loaded.
- observer()->OnNewSuggestions(category);
- EXPECT_EQ(0, test_delegate()->get_suggestions_count);
- EXPECT_EQ(0, test_prefetch_dispatcher()->new_suggestions_count);
-
- // Once the category becomes available, new suggestions should cause us to ask
- // the delegate for suggestion URLs.
- observer()->OnCategoryStatusChanged(category,
- ntp_snippets::CategoryStatus::AVAILABLE);
- observer()->OnNewSuggestions(category);
- EXPECT_EQ(1, test_delegate()->get_suggestions_count);
-
- // We expect that no pages were forwarded to the prefetch service since no
- // pages were prepopulated.
- EXPECT_EQ(0, test_prefetch_dispatcher()->new_suggestions_count);
-}
-
-TEST_F(OfflinePageSuggestedArticlesObserverTest,
- ForwardsSuggestionsToPrefetchService) {
- const GURL test_url_1("https://www.example.com/1");
- test_delegate()->suggestions.push_back(
- ContentSuggestionFromTestURL(test_url_1));
-
- observer()->OnCategoryStatusChanged(category,
- ntp_snippets::CategoryStatus::AVAILABLE);
- observer()->OnNewSuggestions(category);
- EXPECT_EQ(1, test_prefetch_dispatcher()->new_suggestions_count);
- EXPECT_EQ(1U, test_prefetch_dispatcher()->latest_prefetch_urls.size());
- EXPECT_EQ(test_url_1,
- test_prefetch_dispatcher()->latest_prefetch_urls[0].url);
- EXPECT_EQ(
- kSuggestedArticlesNamespace,
- test_prefetch_dispatcher()->latest_prefetch_urls[0].client_id.name_space);
-}
-
-TEST_F(OfflinePageSuggestedArticlesObserverTest, RemovesAllOnBadStatus) {
- const GURL test_url_1("https://www.example.com/1");
- const GURL test_url_2("https://www.example.com/2");
- test_delegate()->suggestions.push_back(
- ContentSuggestionFromTestURL(test_url_1));
- test_delegate()->suggestions.push_back(
- ContentSuggestionFromTestURL(test_url_2));
-
- observer()->OnCategoryStatusChanged(category,
- ntp_snippets::CategoryStatus::AVAILABLE);
- observer()->OnNewSuggestions(category);
- ASSERT_EQ(2U, test_prefetch_dispatcher()->latest_prefetch_urls.size());
-
- observer()->OnCategoryStatusChanged(
- category, ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
- EXPECT_EQ(1, test_prefetch_dispatcher()->remove_all_suggestions_count);
- observer()->OnCategoryStatusChanged(
- category,
- ntp_snippets::CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED);
- EXPECT_EQ(2, test_prefetch_dispatcher()->remove_all_suggestions_count);
-}
-
-TEST_F(OfflinePageSuggestedArticlesObserverTest, RemovesClientIdOnInvalidated) {
- const GURL test_url_1("https://www.example.com/1");
- test_delegate()->suggestions.push_back(
- ContentSuggestionFromTestURL(test_url_1));
- observer()->OnCategoryStatusChanged(category,
- ntp_snippets::CategoryStatus::AVAILABLE);
- observer()->OnNewSuggestions(category);
- ASSERT_EQ(1U, test_prefetch_dispatcher()->latest_prefetch_urls.size());
-
- observer()->OnSuggestionInvalidated(
- ntp_snippets::ContentSuggestion::ID(category, test_url_1.spec()));
-
- EXPECT_EQ(1, test_prefetch_dispatcher()->remove_by_client_id_count);
- EXPECT_EQ(ClientId(kSuggestedArticlesNamespace, test_url_1.spec()),
- *test_prefetch_dispatcher()->last_removed_client_id);
-}
-
-} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/BUILD.gn b/chromium/components/offline_pages/core/BUILD.gn
index 315f64b4376..90eb73a6000 100644
--- a/chromium/components/offline_pages/core/BUILD.gn
+++ b/chromium/components/offline_pages/core/BUILD.gn
@@ -10,6 +10,8 @@ static_library("core") {
sources = [
"archive_manager.cc",
"archive_manager.h",
+ "client_id.cc",
+ "client_id.h",
"client_namespace_constants.cc",
"client_namespace_constants.h",
"client_policy_controller.cc",
diff --git a/chromium/components/offline_pages/core/DEPS b/chromium/components/offline_pages/core/DEPS
index a2326888f13..5d8bc16f120 100644
--- a/chromium/components/offline_pages/core/DEPS
+++ b/chromium/components/offline_pages/core/DEPS
@@ -1,6 +1,4 @@
include_rules = [
"+components/keyed_service",
- "+components/version_info",
- "+net",
"+sql",
]
diff --git a/chromium/components/offline_pages/core/archive_manager.cc b/chromium/components/offline_pages/core/archive_manager.cc
index cd81776772c..400a059f26c 100644
--- a/chromium/components/offline_pages/core/archive_manager.cc
+++ b/chromium/components/offline_pages/core/archive_manager.cc
@@ -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/offline_pages/core/archive_manager.h"
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_enumerator.h"
@@ -9,10 +11,10 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
#include "base/sys_info.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/core/archive_manager.h"
namespace offline_pages {
@@ -22,7 +24,16 @@ using StorageStatsCallback =
base::Callback<void(const ArchiveManager::StorageStats& storage_stats)>;
void EnsureArchivesDirCreatedImpl(const base::FilePath& archives_dir) {
- CHECK(base::CreateDirectory(archives_dir));
+ base::File::Error error = base::File::FILE_OK;
+ if (!base::DirectoryExists(archives_dir)) {
+ if (!base::CreateDirectoryAndGetError(archives_dir, &error)) {
+ LOG(ERROR) << "Failed to create offline pages archive directory: "
+ << base::File::ErrorToString(error);
+ }
+ UMA_HISTOGRAM_ENUMERATION(
+ "OfflinePages.ArchiveManager.ArchiveDirsCreationResult", -error,
+ -base::File::FILE_ERROR_MAX);
+ }
}
void ExistsArchiveImpl(const base::FilePath& file_path,
diff --git a/chromium/components/offline_pages/core/archive_manager_unittest.cc b/chromium/components/offline_pages/core/archive_manager_unittest.cc
index 57f868a872c..10ffeb67fa8 100644
--- a/chromium/components/offline_pages/core/archive_manager_unittest.cc
+++ b/chromium/components/offline_pages/core/archive_manager_unittest.cc
@@ -10,9 +10,11 @@
#include <vector>
#include "base/bind.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/test/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"
@@ -53,6 +55,7 @@ class ArchiveManagerTest : public testing::Test {
ArchiveManager::StorageStats last_storage_sizes() const {
return last_storage_sizes_;
}
+ base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
private:
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
@@ -63,6 +66,7 @@ class ArchiveManagerTest : public testing::Test {
CallbackStatus callback_status_;
std::set<base::FilePath> last_archvie_paths_;
ArchiveManager::StorageStats last_storage_sizes_;
+ std::unique_ptr<base::HistogramTester> histogram_tester_;
};
ArchiveManagerTest::ArchiveManagerTest()
@@ -74,6 +78,7 @@ ArchiveManagerTest::ArchiveManagerTest()
void ArchiveManagerTest::SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
ResetManager(temp_dir_.GetPath());
+ histogram_tester_.reset(new base::HistogramTester());
}
void ArchiveManagerTest::PumpLoop() {
@@ -117,6 +122,11 @@ TEST_F(ArchiveManagerTest, EnsureArchivesDirCreated) {
PumpLoop();
EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
EXPECT_TRUE(base::PathExists(archive_dir));
+ histogram_tester()->ExpectUniqueSample(
+ "OfflinePages.ArchiveManager.ArchiveDirsCreationResult",
+ -base::File::Error::FILE_OK, 1);
+ histogram_tester()->ExpectTotalCount(
+ "OfflinePages.ArchiveManager.ArchiveDirsCreationResult", 1);
// Try again when the file already exists.
ResetResults();
@@ -125,6 +135,11 @@ TEST_F(ArchiveManagerTest, EnsureArchivesDirCreated) {
PumpLoop();
EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
EXPECT_TRUE(base::PathExists(archive_dir));
+ histogram_tester()->ExpectUniqueSample(
+ "OfflinePages.ArchiveManager.ArchiveDirsCreationResult",
+ -base::File::Error::FILE_OK, 1);
+ histogram_tester()->ExpectTotalCount(
+ "OfflinePages.ArchiveManager.ArchiveDirsCreationResult", 1);
}
TEST_F(ArchiveManagerTest, ExistsArchive) {
diff --git a/chromium/components/offline_pages/core/background/DEPS b/chromium/components/offline_pages/core/background/DEPS
index 6fff87d325a..5f8ec858be7 100644
--- a/chromium/components/offline_pages/core/background/DEPS
+++ b/chromium/components/offline_pages/core/background/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+sql",
+ "+net",
]
diff --git a/chromium/components/offline_pages/core/background/network_quality_provider_stub.cc b/chromium/components/offline_pages/core/background/network_quality_provider_stub.cc
index 9c5cf28f00f..168d277e09a 100644
--- a/chromium/components/offline_pages/core/background/network_quality_provider_stub.cc
+++ b/chromium/components/offline_pages/core/background/network_quality_provider_stub.cc
@@ -30,20 +30,6 @@ void NetworkQualityProviderStub::SetUserData(
supports_user_data->SetUserData(&kOfflineNQPKey, std::move(stub));
}
-void NetworkQualityProviderStub::AddEffectiveConnectionTypeObserver(
- net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
-
-void NetworkQualityProviderStub::RemoveEffectiveConnectionTypeObserver(
- net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
-
-void NetworkQualityProviderStub::AddRTTAndThroughputEstimatesObserver(
- net::NetworkQualityEstimator::RTTAndThroughputEstimatesObserver* observer) {
-}
-
-void NetworkQualityProviderStub::RemoveRTTAndThroughputEstimatesObserver(
- net::NetworkQualityEstimator::RTTAndThroughputEstimatesObserver* observer) {
-}
-
net::EffectiveConnectionType
NetworkQualityProviderStub::GetEffectiveConnectionType() const {
return connection_type_;
diff --git a/chromium/components/offline_pages/core/background/network_quality_provider_stub.h b/chromium/components/offline_pages/core/background/network_quality_provider_stub.h
index 54923f02392..b6341b94f4e 100644
--- a/chromium/components/offline_pages/core/background/network_quality_provider_stub.h
+++ b/chromium/components/offline_pages/core/background/network_quality_provider_stub.h
@@ -7,15 +7,14 @@
#include "base/supports_user_data.h"
#include "net/nqe/effective_connection_type.h"
-#include "net/nqe/network_quality_estimator.h"
+#include "net/nqe/network_quality_provider.h"
namespace offline_pages {
// Test class stubbing out the functionality of NQE::NetworkQualityProvider.
// It is only used for test support.
-class NetworkQualityProviderStub
- : public net::NetworkQualityEstimator::NetworkQualityProvider,
- public base::SupportsUserData::Data {
+class NetworkQualityProviderStub : public net::NetworkQualityProvider,
+ public base::SupportsUserData::Data {
public:
NetworkQualityProviderStub();
~NetworkQualityProviderStub() override;
@@ -27,22 +26,6 @@ class NetworkQualityProviderStub
net::EffectiveConnectionType GetEffectiveConnectionType() const override;
- void AddEffectiveConnectionTypeObserver(
- net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
- override;
-
- void RemoveEffectiveConnectionTypeObserver(
- net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
- override;
-
- void AddRTTAndThroughputEstimatesObserver(
- net::NetworkQualityEstimator::RTTAndThroughputEstimatesObserver* observer)
- override;
-
- void RemoveRTTAndThroughputEstimatesObserver(
- net::NetworkQualityEstimator::RTTAndThroughputEstimatesObserver* observer)
- override;
-
void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
connection_type_ = type;
}
diff --git a/chromium/components/offline_pages/core/background/request_coordinator.cc b/chromium/components/offline_pages/core/background/request_coordinator.cc
index 7e0882deb46..099a91fd2a4 100644
--- a/chromium/components/offline_pages/core/background/request_coordinator.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator.cc
@@ -205,8 +205,11 @@ RequestCoordinator::SavePageLaterParams::SavePageLaterParams(
user_requested = other.user_requested;
availability = other.availability;
original_url = other.original_url;
+ request_origin = other.request_origin;
}
+RequestCoordinator::SavePageLaterParams::~SavePageLaterParams() = default;
+
RequestCoordinator::RequestCoordinator(
std::unique_ptr<OfflinerPolicy> policy,
std::unique_ptr<Offliner> offliner,
@@ -263,6 +266,7 @@ int64_t RequestCoordinator::SavePageLater(
id, save_page_later_params.url, save_page_later_params.client_id,
base::Time::Now(), save_page_later_params.user_requested);
request.set_original_url(save_page_later_params.original_url);
+ request.set_request_origin(save_page_later_params.request_origin);
// If the download manager is not done with the request, put it on the
// disabled list.
diff --git a/chromium/components/offline_pages/core/background/request_coordinator.h b/chromium/components/offline_pages/core/background/request_coordinator.h
index 05a1bace1c5..9f27623f134 100644
--- a/chromium/components/offline_pages/core/background/request_coordinator.h
+++ b/chromium/components/offline_pages/core/background/request_coordinator.h
@@ -73,6 +73,7 @@ class RequestCoordinator : public KeyedService,
struct SavePageLaterParams {
SavePageLaterParams();
SavePageLaterParams(const SavePageLaterParams& other);
+ ~SavePageLaterParams();
// The last committed URL of the page to save.
GURL url;
@@ -88,6 +89,9 @@ class RequestCoordinator : public KeyedService,
// The original URL of the page to save. Empty if no redirect occurs.
GURL original_url;
+
+ // The origin of the request, if any.
+ std::string request_origin;
};
// Callback specifying which request IDs were actually removed.
diff --git a/chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc b/chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc
index d044c691e8f..df90ec6bda9 100644
--- a/chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc
@@ -43,6 +43,8 @@ static std::string OfflinerRequestStatusToString(
return "BACKGROUND_SCHEDULER_CANCELED";
case Offliner::SAVED_ON_LAST_RETRY:
return "SAVED_ON_LAST_RETRY";
+ case Offliner::BROWSER_KILLED:
+ return "BROWSER_KILLED";
default:
NOTREACHED();
return std::to_string(static_cast<int>(request_status));
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 9291c1841dd..d16f9d5c8f9 100644
--- a/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -52,6 +52,7 @@ const int kMaxCompletedTries = 3;
const bool kPowerRequired = true;
const bool kUserRequested = true;
const int kAttemptCount = 1;
+const std::string kRequestOrigin("abc.xyz");
} // namespace
class ObserverStub : public RequestCoordinator::Observer {
@@ -266,6 +267,7 @@ class RequestCoordinatorTest : public testing::Test {
params.url = kUrl1;
params.client_id = kClientId1;
params.user_requested = kUserRequested;
+ params.request_origin = kRequestOrigin;
return coordinator()->SavePageLater(params);
}
@@ -276,6 +278,7 @@ class RequestCoordinatorTest : public testing::Test {
params.client_id = kClientId1;
params.user_requested = kUserRequested;
params.availability = availability;
+ params.request_origin = kRequestOrigin;
return coordinator()->SavePageLater(params);
}
@@ -564,6 +567,7 @@ TEST_F(RequestCoordinatorTest, SavePageLater) {
params.url = kUrl1;
params.client_id = kClientId1;
params.original_url = kUrl2;
+ params.request_origin = kRequestOrigin;
EXPECT_NE(0, coordinator()->SavePageLater(params));
// Expect that a request got placed on the queue.
@@ -583,6 +587,7 @@ TEST_F(RequestCoordinatorTest, SavePageLater) {
EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
EXPECT_TRUE(last_requests().at(0)->user_requested());
EXPECT_EQ(kUrl2, last_requests().at(0)->original_url());
+ EXPECT_EQ(kRequestOrigin, last_requests().at(0)->request_origin());
// Expect that the scheduler got notified.
SchedulerStub* scheduler_stub =
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 87dc3bfab1b..406d3d7cdca 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
@@ -44,7 +44,8 @@ bool CreateRequestQueueTable(sql::Connection* db) {
" url VARCHAR NOT NULL,"
" client_namespace VARCHAR NOT NULL,"
" client_id VARCHAR NOT NULL,"
- " original_url VARCHAR NOT NULL DEFAULT ''"
+ " original_url VARCHAR NOT NULL DEFAULT '',"
+ " request_origin VARCHAR NOT NULL DEFAULT ''"
")";
return db->Execute(kSql);
}
@@ -75,6 +76,20 @@ bool UpgradeFrom57(sql::Connection* db) {
return UpgradeWithQuery(db, kSql);
}
+bool UpgradeFrom58(sql::Connection* db) {
+ 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, "
+ "client_namespace, client_id, original_url) "
+ "SELECT "
+ "request_id, creation_time, activation_time, last_attempt_time, "
+ "started_attempt_count, completed_attempt_count, state, url, "
+ "client_namespace, client_id, original_url "
+ "FROM temp_" REQUEST_QUEUE_TABLE_NAME;
+ return UpgradeWithQuery(db, kSql);
+}
+
bool CreateSchema(sql::Connection* db) {
sql::Transaction transaction(db);
if (!transaction.Begin())
@@ -85,7 +100,7 @@ bool CreateSchema(sql::Connection* db) {
return false;
}
- // If there is not already a state column, we need to drop the old table. We
+ // If there is not already a state column, we need to drop the old table. We
// are choosing to drop instead of upgrade since the feature is not yet
// released, so we don't try to migrate it.
if (!db->DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "state")) {
@@ -96,6 +111,9 @@ bool CreateSchema(sql::Connection* db) {
if (!db->DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "original_url")) {
if (!UpgradeFrom57(db))
return false;
+ } else if (!db->DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "request_origin")) {
+ if (!UpgradeFrom58(db))
+ return false;
}
// This would be a great place to add indices when we need them.
@@ -120,11 +138,13 @@ std::unique_ptr<SavePageRequest> MakeSavePageRequest(
const ClientId client_id(statement.ColumnString(8),
statement.ColumnString(9));
const GURL original_url(statement.ColumnString(10));
+ const std::string request_origin(statement.ColumnString(11));
DVLOG(2) << "making save page request - id " << id << " url " << url
<< " client_id " << client_id.name_space << "-" << client_id.id
<< " creation time " << creation_time << " user requested "
- << kUserRequested << " original_url " << original_url;
+ << kUserRequested << " original_url " << original_url
+ << " request_origin " << request_origin;
std::unique_ptr<SavePageRequest> request(
new SavePageRequest(id, url, client_id, creation_time, kUserRequested));
@@ -133,6 +153,7 @@ std::unique_ptr<SavePageRequest> MakeSavePageRequest(
request->set_completed_attempt_count(completed_attempt_count);
request->set_request_state(state);
request->set_original_url(original_url);
+ request->set_request_origin(request_origin);
return request;
}
@@ -142,7 +163,7 @@ std::unique_ptr<SavePageRequest> GetOneRequest(sql::Connection* db,
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"
+ " state, url, client_namespace, client_id, original_url, request_origin"
" FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -170,9 +191,10 @@ ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) {
"INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME
" (request_id, creation_time, activation_time,"
" last_attempt_time, started_attempt_count, completed_attempt_count,"
- " state, url, client_namespace, client_id, original_url)"
+ " state, url, client_namespace, client_id, original_url, "
+ "request_origin)"
" VALUES "
- " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, request.request_id());
@@ -186,6 +208,7 @@ ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) {
statement.BindString(8, request.client_id().name_space);
statement.BindString(9, request.client_id().id);
statement.BindString(10, request.original_url().spec());
+ statement.BindString(11, request.request_origin());
if (!statement.Run())
return ItemActionStatus::STORE_ERROR;
@@ -199,7 +222,8 @@ ItemActionStatus Update(sql::Connection* db, const SavePageRequest& request) {
"UPDATE OR IGNORE " REQUEST_QUEUE_TABLE_NAME
" SET creation_time = ?, activation_time = ?, last_attempt_time = ?,"
" started_attempt_count = ?, completed_attempt_count = ?, state = ?,"
- " url = ?, client_namespace = ?, client_id = ?, original_url = ?"
+ " url = ?, client_namespace = ?, client_id = ?, original_url = ?,"
+ "request_origin = ?"
" WHERE request_id = ?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -213,7 +237,8 @@ ItemActionStatus Update(sql::Connection* db, const SavePageRequest& request) {
statement.BindString(7, request.client_id().name_space);
statement.BindString(8, request.client_id().id);
statement.BindString(9, request.original_url().spec());
- statement.BindInt64(10, request.request_id());
+ statement.BindString(10, request.request_origin());
+ statement.BindInt64(11, request.request_id());
if (!statement.Run())
return ItemActionStatus::STORE_ERROR;
@@ -276,7 +301,7 @@ void GetRequestsSync(sql::Connection* db,
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"
+ " state, url, client_namespace, client_id, original_url, request_origin"
" FROM " REQUEST_QUEUE_TABLE_NAME;
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
diff --git a/chromium/components/offline_pages/core/background/request_queue_store_unittest.cc b/chromium/components/offline_pages/core/background/request_queue_store_unittest.cc
index 9b0009fde61..385bc78d123 100644
--- a/chromium/components/offline_pages/core/background/request_queue_store_unittest.cc
+++ b/chromium/components/offline_pages/core/background/request_queue_store_unittest.cc
@@ -34,6 +34,7 @@ const GURL kUrl2("http://another-example.com");
const ClientId kClientId("bookmark", "1234");
const ClientId kClientId2("async", "5678");
const bool kUserRequested = true;
+const std::string kRequestOrigin = "abc.xyz";
enum class LastResult {
RESULT_NONE,
@@ -86,6 +87,53 @@ void BuildTestStoreWithSchemaFromM57(const base::FilePath& file) {
connection.DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "original_url"));
}
+void BuildTestStoreWithSchemaFromM58(const base::FilePath& file) {
+ sql::Connection connection;
+ ASSERT_TRUE(
+ connection.Open(file.Append(FILE_PATH_LITERAL("RequestQueue.db"))));
+ ASSERT_TRUE(connection.is_open());
+ ASSERT_TRUE(connection.BeginTransaction());
+ ASSERT_TRUE(
+ connection.Execute("CREATE TABLE " 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"
+ ")"));
+
+ ASSERT_TRUE(connection.CommitTransaction());
+ sql::Statement statement(connection.GetUniqueStatement(
+ "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME
+ " (request_id, creation_time, activation_time,"
+ " last_attempt_time, started_attempt_count, completed_attempt_count,"
+ " state, url, client_namespace, client_id, original_url)"
+ " VALUES "
+ " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+
+ statement.BindInt64(0, kRequestId);
+ statement.BindInt64(1, 0);
+ statement.BindInt64(2, 0);
+ statement.BindInt64(3, 0);
+ statement.BindInt64(4, 0);
+ statement.BindInt64(5, 0);
+ statement.BindInt64(6, 0);
+ statement.BindString(7, kUrl.spec());
+ statement.BindString(8, kClientId.name_space);
+ statement.BindString(9, kClientId.id);
+ statement.BindString(10, kUrl2.spec());
+ ASSERT_TRUE(statement.Run());
+ ASSERT_TRUE(connection.DoesTableExist(REQUEST_QUEUE_TABLE_NAME));
+ ASSERT_FALSE(
+ connection.DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "request_origin"));
+}
+
} // namespace
// Class that serves as a base for testing different implementations of the
@@ -233,8 +281,11 @@ class RequestQueueStoreSQLFactory : public RequestQueueStoreFactory {
RequestQueueStore* BuildStoreWithOldSchema(const base::FilePath& path,
int version) override {
- EXPECT_EQ(57, version);
- BuildTestStoreWithSchemaFromM57(path);
+ if (version == 57) {
+ BuildTestStoreWithSchemaFromM57(path);
+ } else if (version == 58) {
+ BuildTestStoreWithSchemaFromM58(path);
+ }
RequestQueueStore* store =
new RequestQueueStoreSQL(base::ThreadTaskRunnerHandle::Get(), path);
@@ -295,6 +346,24 @@ TYPED_TEST(RequestQueueStoreTest, UpgradeFromVersion57Store) {
EXPECT_EQ(GURL(), this->last_requests()[0]->original_url());
}
+TYPED_TEST(RequestQueueStoreTest, UpgradeFromVersion58Store) {
+ std::unique_ptr<RequestQueueStore> store(this->BuildStoreWithOldSchema(58));
+ // In-memory store does not support upgrading.
+ if (!store)
+ return;
+ this->InitializeStore(store.get());
+
+ store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+ base::Unretained(this)));
+ this->PumpLoop();
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+ ASSERT_EQ(1u, this->last_requests().size());
+ EXPECT_EQ(kRequestId, this->last_requests()[0]->request_id());
+ EXPECT_EQ(kUrl, this->last_requests()[0]->url());
+ EXPECT_EQ(kUrl2, this->last_requests()[0]->original_url());
+ EXPECT_EQ("", this->last_requests()[0]->request_origin());
+}
+
TYPED_TEST(RequestQueueStoreTest, GetRequestsEmpty) {
std::unique_ptr<RequestQueueStore> store(this->BuildStore());
this->InitializeStore(store.get());
@@ -432,6 +501,7 @@ TYPED_TEST(RequestQueueStoreTest, UpdateRequest) {
SavePageRequest updated_request(kRequestId, kUrl, kClientId,
new_creation_time, kUserRequested);
updated_request.set_original_url(kUrl2);
+ updated_request.set_request_origin(kRequestOrigin);
// Try to update a non-existing request.
SavePageRequest updated_request2(kRequestId2, kUrl, kClientId,
new_creation_time, kUserRequested);
diff --git a/chromium/components/offline_pages/core/background/save_page_request.cc b/chromium/components/offline_pages/core/background/save_page_request.cc
index d95df18069e..ff8bdc2f3ef 100644
--- a/chromium/components/offline_pages/core/background/save_page_request.cc
+++ b/chromium/components/offline_pages/core/background/save_page_request.cc
@@ -30,7 +30,8 @@ SavePageRequest::SavePageRequest(const SavePageRequest& other)
last_attempt_time_(other.last_attempt_time_),
user_requested_(other.user_requested_),
state_(other.state_),
- original_url_(other.original_url_) {}
+ original_url_(other.original_url_),
+ request_origin_(other.request_origin_) {}
SavePageRequest::~SavePageRequest() {}
@@ -41,7 +42,8 @@ bool SavePageRequest::operator==(const SavePageRequest& other) const {
started_attempt_count_ == other.started_attempt_count_ &&
completed_attempt_count_ == other.completed_attempt_count_ &&
last_attempt_time_ == other.last_attempt_time_ &&
- state_ == other.state_ && original_url_ == other.original_url_;
+ state_ == other.state_ && original_url_ == other.original_url_ &&
+ request_origin_ == other.request_origin_;
}
void SavePageRequest::MarkAttemptStarted(const base::Time& start_time) {
diff --git a/chromium/components/offline_pages/core/background/save_page_request.h b/chromium/components/offline_pages/core/background/save_page_request.h
index 52dee9556a1..01ffcf47f21 100644
--- a/chromium/components/offline_pages/core/background/save_page_request.h
+++ b/chromium/components/offline_pages/core/background/save_page_request.h
@@ -74,7 +74,6 @@ class SavePageRequest {
}
bool user_requested() const { return user_requested_; }
-
void set_user_requested(bool user_requested) {
user_requested_ = user_requested;
}
@@ -84,6 +83,11 @@ class SavePageRequest {
original_url_ = original_url;
}
+ const std::string& request_origin() const { return request_origin_; }
+ void set_request_origin(const std::string& request_origin) {
+ request_origin_ = request_origin;
+ }
+
private:
// ID of this request.
int64_t request_id_;
@@ -117,6 +121,10 @@ class SavePageRequest {
// The original URL of the page to be offlined. Empty if no redirect occurs.
GURL original_url_;
+
+ // The app package origin of this save page request. Empty if cannot be
+ // determined or Chrome.
+ std::string request_origin_;
};
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/save_page_request_unittest.cc b/chromium/components/offline_pages/core/background/save_page_request_unittest.cc
index df073bd61e3..a8a27a2ef85 100644
--- a/chromium/components/offline_pages/core/background/save_page_request_unittest.cc
+++ b/chromium/components/offline_pages/core/background/save_page_request_unittest.cc
@@ -14,6 +14,7 @@ const GURL kUrl("http://example.com");
const GURL kUrl2("http://example.com/test");
const ClientId kClientId("bookmark", "1234");
const bool kUserRequested = true;
+const std::string kRequestOrigin = "abc.xyz";
} // namespace
class SavePageRequestTest : public testing::Test {
@@ -38,12 +39,14 @@ TEST_F(SavePageRequestTest, CreatePendingReqeust) {
EXPECT_EQ(0, request.started_attempt_count());
EXPECT_EQ(0, request.completed_attempt_count());
EXPECT_EQ(kUrl2, request.original_url());
+ EXPECT_EQ("", request.request_origin());
}
TEST_F(SavePageRequestTest, StartAndCompleteRequest) {
base::Time creation_time = base::Time::Now();
SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
kUserRequested);
+ request.set_request_origin(kRequestOrigin);
base::Time start_time = creation_time + base::TimeDelta::FromHours(3);
request.MarkAttemptStarted(start_time);
@@ -53,6 +56,7 @@ TEST_F(SavePageRequestTest, StartAndCompleteRequest) {
EXPECT_EQ(kUrl, request.url());
EXPECT_EQ(kClientId, request.client_id());
EXPECT_EQ(creation_time, request.creation_time());
+ EXPECT_EQ(kRequestOrigin, request.request_origin());
// Attempt time, attempt count and status will though.
EXPECT_EQ(start_time, request.last_attempt_time());
@@ -85,6 +89,7 @@ TEST_F(SavePageRequestTest, StartAndAbortRequest) {
EXPECT_EQ(kUrl, request.url());
EXPECT_EQ(kClientId, request.client_id());
EXPECT_EQ(creation_time, request.creation_time());
+ EXPECT_EQ("", request.request_origin());
// Attempt time and attempt count will though.
EXPECT_EQ(start_time, request.last_attempt_time());
@@ -98,6 +103,7 @@ TEST_F(SavePageRequestTest, StartAndAbortRequest) {
EXPECT_EQ(kUrl, request.url());
EXPECT_EQ(kClientId, request.client_id());
EXPECT_EQ(creation_time, request.creation_time());
+ EXPECT_EQ("", request.request_origin());
// Last attempt time is updated and completed attempt count did not rise.
EXPECT_EQ(0, request.completed_attempt_count());
diff --git a/chromium/components/offline_pages/core/client_id.cc b/chromium/components/offline_pages/core/client_id.cc
new file mode 100644
index 00000000000..df60634116b
--- /dev/null
+++ b/chromium/components/offline_pages/core/client_id.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/offline_pages/core/client_id.h"
+
+namespace offline_pages {
+
+ClientId::ClientId() {}
+
+ClientId::ClientId(const std::string& name_space, const std::string& id)
+ : name_space(name_space), id(id) {}
+
+bool ClientId::operator==(const ClientId& client_id) const {
+ return name_space == client_id.name_space && id == client_id.id;
+}
+
+bool ClientId::operator<(const ClientId& client_id) const {
+ if (name_space == client_id.name_space)
+ return (id < client_id.id);
+
+ return name_space < client_id.name_space;
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/client_id.h b/chromium/components/offline_pages/core/client_id.h
new file mode 100644
index 00000000000..8c749828344
--- /dev/null
+++ b/chromium/components/offline_pages/core/client_id.h
@@ -0,0 +1,36 @@
+// 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_OFFLINE_PAGES_CORE_CLIENT_ID_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_ID_H_
+
+#include <string>
+
+namespace offline_pages {
+
+// Defines a namespace/id pair that allows offline page clients to uniquely
+// identify their own items within adopting internal systems. It is the client's
+// responsibility to keep id values unique within its assigned namespace, but it
+// is not a requirement.
+struct ClientId {
+ ClientId();
+ ClientId(const std::string& name_space, const std::string& id);
+
+ bool operator==(const ClientId& client_id) const;
+
+ bool operator<(const ClientId& client_id) const;
+
+ // The namespace that identifies the client (of course 'namespace' is a
+ // reserved word, so...).
+ std::string name_space;
+
+ // The client specified id that allows it to uniquely identify entries within
+ // its namespace. These values are opaque to offline page systems and not used
+ // internally as an identifier.
+ std::string id;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_ID_H_ \ No newline at end of file
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 7e1368ec7ba..310531ad577 100644
--- a/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc
@@ -116,11 +116,11 @@ void DownloadUIAdapter::OfflinePageAdded(OfflinePageModel* model,
AddItemHelper(base::MakeUnique<ItemInfo>(added_page, temporarily_hidden));
}
-void DownloadUIAdapter::OfflinePageDeleted(int64_t offline_id,
- const ClientId& client_id) {
- if (!delegate_->IsVisibleInUI(client_id))
+void DownloadUIAdapter::OfflinePageDeleted(
+ const OfflinePageModel::DeletedPageInfo& page_info) {
+ if (!delegate_->IsVisibleInUI(page_info.client_id))
return;
- DeleteItemHelper(client_id.id);
+ DeleteItemHelper(page_info.client_id.id);
}
// RequestCoordinator::Observer
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 9868fb009ca..ec1f8d56219 100644
--- a/chromium/components/offline_pages/core/downloads/download_ui_adapter.h
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter.h
@@ -109,8 +109,8 @@ class DownloadUIAdapter : public OfflinePageModel::Observer,
void OfflinePageModelLoaded(OfflinePageModel* model) override;
void OfflinePageAdded(OfflinePageModel* model,
const OfflinePageItem& added_page) override;
- void OfflinePageDeleted(int64_t offline_id,
- const ClientId& client_id) override;
+ void OfflinePageDeleted(
+ const OfflinePageModel::DeletedPageInfo& page_info) override;
// RequestCoordinator::Observer
void OnAdded(const SavePageRequest& request) override;
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 a52a10eccc4..b5c2b32a83b 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
@@ -60,7 +60,7 @@ class DownloadUIAdapterDelegate : public DownloadUIAdapter::Delegate {
bool IsTemporarilyHiddenInUI(const ClientId& client_id) override {
return is_temporarily_hidden;
}
- void SetUIAdapter(DownloadUIAdapter* ui_adapter) override{};
+ void SetUIAdapter(DownloadUIAdapter* ui_adapter) override {}
bool is_visible = true;
bool is_temporarily_hidden = false;
@@ -112,8 +112,9 @@ class MockOfflinePageModel : public StubOfflinePageModel {
void DeletePageAndNotifyAdapter(const std::string& guid) {
for (const auto& page : pages) {
if (page.second.client_id.id == guid) {
- observer_->OfflinePageDeleted(page.second.offline_id,
- page.second.client_id);
+ DeletedPageInfo info(page.second.offline_id, page.second.client_id,
+ page.second.request_origin);
+ observer_->OfflinePageDeleted(info);
pages.erase(page.first);
return;
}
diff --git a/chromium/components/offline_pages/core/offline_page_feature.cc b/chromium/components/offline_pages/core/offline_page_feature.cc
index 9dfab58bfca..cbc572e8749 100644
--- a/chromium/components/offline_pages/core/offline_page_feature.cc
+++ b/chromium/components/offline_pages/core/offline_page_feature.cc
@@ -22,10 +22,10 @@ const char kOfflinePagesUseTestingSnapshotDelay[] =
namespace offline_pages {
const base::Feature kOfflineBookmarksFeature{"OfflineBookmarks",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOffliningRecentPagesFeature{
- "OfflineRecentPages", base::FEATURE_DISABLED_BY_DEFAULT};
+ "OfflineRecentPages", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOfflinePagesCTFeature{"OfflinePagesCT",
base::FEATURE_ENABLED_BY_DEFAULT};
@@ -39,6 +39,9 @@ const base::Feature kOfflinePagesSvelteConcurrentLoadingFeature{
const base::Feature kOfflinePagesLoadSignalCollectingFeature{
"OfflinePagesLoadSignalCollecting", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kOfflinePagesRenovationsFeature{
+ "OfflinePagesRenovations", base::FEATURE_DISABLED_BY_DEFAULT};
+
const base::Feature kBackgroundLoaderForDownloadsFeature{
"BackgroundLoadingForDownloads", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -52,6 +55,9 @@ const base::Feature kNewBackgroundLoaderFeature {
"BackgroundLoader", base::FEATURE_DISABLED_BY_DEFAULT
};
+const base::Feature kOfflinePagesCTV2Feature{"OfflinePagesCTV2",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
bool IsOfflineBookmarksEnabled() {
return base::FeatureList::IsEnabled(kOfflineBookmarksFeature);
}
@@ -89,6 +95,10 @@ bool IsOfflinePagesLoadSignalCollectingEnabled() {
return base::FeatureList::IsEnabled(kOfflinePagesLoadSignalCollectingFeature);
}
+bool IsOfflinePagesRenovationsEnabled() {
+ return base::FeatureList::IsEnabled(kOfflinePagesRenovationsFeature);
+}
+
bool ShouldUseNewBackgroundLoader() {
return base::FeatureList::IsEnabled(kNewBackgroundLoaderFeature);
}
@@ -98,4 +108,8 @@ bool ShouldUseTestingSnapshotDelay() {
return cl->HasSwitch(switches::kOfflinePagesUseTestingSnapshotDelay);
}
+bool IsOfflinePagesCTV2Enabled() {
+ return base::FeatureList::IsEnabled(kOfflinePagesCTV2Feature);
+}
+
} // 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 11a5b68cf83..383f6d1b916 100644
--- a/chromium/components/offline_pages/core/offline_page_feature.h
+++ b/chromium/components/offline_pages/core/offline_page_feature.h
@@ -20,6 +20,8 @@ extern const base::Feature kOfflinePagesAsyncDownloadFeature;
extern const base::Feature kPrefetchingOfflinePagesFeature;
extern const base::Feature kNewBackgroundLoaderFeature;
extern const base::Feature kOfflinePagesLoadSignalCollectingFeature;
+extern const base::Feature kOfflinePagesCTV2Feature;
+extern const base::Feature kOfflinePagesRenovationsFeature;
// Returns true if saving bookmarked pages for offline viewing is enabled.
bool IsOfflineBookmarksEnabled();
@@ -49,6 +51,10 @@ bool IsPrefetchingOfflinePagesEnabled();
// Returns true if we enable load timing signals to be collected.
bool IsOfflinePagesLoadSignalCollectingEnabled();
+// Returns true if we should use the "page renovation" framework in
+// the BackgroundLoaderOffliner.
+bool IsOfflinePagesRenovationsEnabled();
+
// Returns true if we should use background loader rather than prerenderer
// to offline pages.
bool ShouldUseNewBackgroundLoader();
@@ -57,6 +63,9 @@ bool ShouldUseNewBackgroundLoader();
// snapshot delay.
bool ShouldUseTestingSnapshotDelay();
+// Returns true if we should record request origin as part of custom tabs V2.
+bool IsOfflinePagesCTV2Enabled();
+
} // namespace offline_pages
#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
diff --git a/chromium/components/offline_pages/core/offline_page_item.cc b/chromium/components/offline_pages/core/offline_page_item.cc
index 7d83079a2d8..5b13c80d4ed 100644
--- a/chromium/components/offline_pages/core/offline_page_item.cc
+++ b/chromium/components/offline_pages/core/offline_page_item.cc
@@ -6,22 +6,6 @@
namespace offline_pages {
-ClientId::ClientId() : name_space(""), id("") {}
-
-ClientId::ClientId(std::string name_space, std::string id)
- : name_space(name_space), id(id) {}
-
-bool ClientId::operator==(const ClientId& client_id) const {
- return name_space == client_id.name_space && id == client_id.id;
-}
-
-bool ClientId::operator<(const ClientId& client_id) const {
- if (name_space == client_id.name_space)
- return (id < client_id.id);
-
- return name_space < client_id.name_space;
-}
-
OfflinePageItem::OfflinePageItem()
: file_size(0), access_count(0), flags(NO_FLAG) {}
@@ -54,6 +38,24 @@ OfflinePageItem::OfflinePageItem(const GURL& url,
access_count(0),
flags(NO_FLAG) {}
+OfflinePageItem::OfflinePageItem(const GURL& url,
+ int64_t offline_id,
+ const ClientId& client_id,
+ const base::FilePath& file_path,
+ int64_t file_size,
+ const base::Time& creation_time,
+ const std::string& request_origin)
+ : url(url),
+ offline_id(offline_id),
+ client_id(client_id),
+ file_path(file_path),
+ file_size(file_size),
+ creation_time(creation_time),
+ last_access_time(creation_time),
+ access_count(0),
+ flags(NO_FLAG),
+ request_origin(request_origin) {}
+
OfflinePageItem::OfflinePageItem(const OfflinePageItem& other) = default;
OfflinePageItem::~OfflinePageItem() {}
@@ -63,10 +65,9 @@ bool OfflinePageItem::operator==(const OfflinePageItem& other) const {
client_id == other.client_id && file_path == other.file_path &&
creation_time == other.creation_time &&
last_access_time == other.last_access_time &&
- access_count == other.access_count &&
- title == other.title &&
- flags == other.flags &&
- original_url == other.original_url;
+ access_count == other.access_count && title == other.title &&
+ flags == other.flags && original_url == other.original_url &&
+ request_origin == other.request_origin;
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_item.h b/chromium/components/offline_pages/core/offline_page_item.h
index bbc2380e509..f952e6f6918 100644
--- a/chromium/components/offline_pages/core/offline_page_item.h
+++ b/chromium/components/offline_pages/core/offline_page_item.h
@@ -7,29 +7,14 @@
#include <stdint.h>
-#include <string>
-
#include "base/files/file_path.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
+#include "components/offline_pages/core/client_id.h"
#include "url/gurl.h"
namespace offline_pages {
-struct ClientId {
- // The namespace for the id (of course 'namespace' is a reserved word, so...)
- std::string name_space;
- // The id in the client's namespace. Opaque to us.
- std::string id;
-
- ClientId();
- ClientId(std::string name_space, std::string id);
-
- bool operator==(const ClientId& client_id) const;
-
- bool operator<(const ClientId& client_id) const;
-};
-
// Metadata of the offline page.
struct OfflinePageItem {
public:
@@ -51,6 +36,13 @@ struct OfflinePageItem {
const base::FilePath& file_path,
int64_t file_size,
const base::Time& creation_time);
+ OfflinePageItem(const GURL& url,
+ int64_t offline_id,
+ const ClientId& client_id,
+ const base::FilePath& file_path,
+ int64_t file_size,
+ const base::Time& creation_time,
+ const std::string& origin_package);
OfflinePageItem(const OfflinePageItem& other);
~OfflinePageItem();
@@ -84,6 +76,9 @@ struct OfflinePageItem {
// The original URL of the page if not empty. Otherwise, this is set to empty
// and |url| should be accessed instead.
GURL original_url;
+ // The app, if any, that the item was saved on behalf of.
+ // Empty string implies Chrome.
+ std::string request_origin;
};
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc
index c4ac133441e..de6ef9e77fb 100644
--- a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc
@@ -39,7 +39,8 @@ bool CreateOfflinePagesTable(sql::Connection* db) {
" online_url VARCHAR NOT NULL,"
" file_path VARCHAR NOT NULL,"
" title VARCHAR NOT NULL DEFAULT '',"
- " original_url VARCHAR NOT NULL DEFAULT ''"
+ " original_url VARCHAR NOT NULL DEFAULT '',"
+ " request_origin VARCHAR NOT NULL DEFAULT ''"
")";
return db->Execute(kSql);
}
@@ -128,6 +129,20 @@ bool UpgradeFrom56(sql::Connection* db) {
return UpgradeWithQuery(db, kSql);
}
+bool UpgradeFrom57(sql::Connection* db) {
+ 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, "
+ "file_path, title, original_url) "
+ "SELECT "
+ "offline_id, creation_time, file_size, last_access_time, "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path, title, original_url "
+ "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
+ return UpgradeWithQuery(db, kSql);
+}
+
bool CreateSchema(sql::Connection* db) {
// If you create a transaction but don't Commit() it is automatically
// rolled back by its destructor when it falls out of scope.
@@ -157,6 +172,9 @@ bool CreateSchema(sql::Connection* db) {
} else if (db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time")) {
if (!UpgradeFrom56(db))
return false;
+ } else if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "request_origin")) {
+ if (!UpgradeFrom57(db))
+ return false;
}
// This would be a great place to add indices when we need them.
@@ -206,12 +224,14 @@ OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
base::FilePath path(GetPathFromUTF8String(statement->ColumnString(8)));
base::string16 title = statement->ColumnString16(9);
GURL original_url(statement->ColumnString(10));
+ std::string request_origin = statement->ColumnString(11);
OfflinePageItem item(url, id, client_id, path, file_size, creation_time);
item.last_access_time = last_access_time;
item.access_count = access_count;
item.title = title;
item.original_url = original_url;
+ item.request_origin = request_origin;
return item;
}
@@ -222,9 +242,9 @@ ItemActionStatus Insert(sql::Connection* db, const OfflinePageItem& item) {
"INSERT OR IGNORE INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, online_url, client_namespace, client_id, file_path, "
"file_size, creation_time, last_access_time, access_count, "
- "title, original_url)"
+ "title, original_url, request_origin)"
" VALUES "
- " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, item.offline_id);
@@ -238,6 +258,7 @@ ItemActionStatus Insert(sql::Connection* db, const OfflinePageItem& item) {
statement.BindInt(8, item.access_count);
statement.BindString16(9, item.title);
statement.BindString(10, item.original_url.spec());
+ statement.BindString(11, item.request_origin);
if (!statement.Run())
return ItemActionStatus::STORE_ERROR;
if (db->GetLastChangeCount() == 0)
@@ -250,7 +271,7 @@ bool Update(sql::Connection* db, const OfflinePageItem& item) {
"UPDATE OR IGNORE " OFFLINE_PAGES_TABLE_NAME
" SET online_url = ?, client_namespace = ?, client_id = ?, file_path = ?,"
" file_size = ?, creation_time = ?, last_access_time = ?,"
- " access_count = ?, title = ?, original_url = ?"
+ " access_count = ?, title = ?, original_url = ?, request_origin = ?"
" WHERE offline_id = ?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -264,7 +285,8 @@ bool Update(sql::Connection* db, const OfflinePageItem& item) {
statement.BindInt(7, item.access_count);
statement.BindString16(8, item.title);
statement.BindString(9, item.original_url.spec());
- statement.BindInt64(10, item.offline_id);
+ statement.BindString(10, item.request_origin);
+ statement.BindInt64(11, item.offline_id);
return statement.Run() && db->GetLastChangeCount() > 0;
}
diff --git a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h
index 41b96fa475c..e9c44ec6400 100644
--- a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h
@@ -37,6 +37,8 @@ namespace offline_pages {
// * In M56 original_url was added.
// * In M57 expiration_time was dropped. Existing expired pages would be
// removed when metadata consistency check happens.
+// * In M58-M60 there were no changes.
+// * In M61 request_origin was added.
//
// Here is a procedure to update the schema for this store:
// * Decide how to detect that the store is on a particular version, which
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 cdfdc473cc5..171d969fcb6 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
@@ -269,6 +269,49 @@ void BuildTestStoreWithSchemaFromM56(const base::FilePath& file) {
connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "expiration_time"));
}
+void BuildTestStoreWithSchemaFromM57(const base::FilePath& file) {
+ sql::Connection connection;
+ ASSERT_TRUE(
+ connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
+ ASSERT_TRUE(connection.is_open());
+ ASSERT_TRUE(connection.BeginTransaction());
+ ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
+ "(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,"
+ " 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 ''"
+ ")"));
+ ASSERT_TRUE(connection.CommitTransaction());
+ sql::Statement statement(connection.GetUniqueStatement(
+ "INSERT INTO " OFFLINE_PAGES_TABLE_V1
+ "(offline_id, creation_time, file_size, "
+ "last_access_time, access_count, client_namespace, "
+ "client_id, online_url, file_path, title, original_url) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ statement.BindInt64(0, kOfflineId);
+ statement.BindInt(1, 0);
+ statement.BindInt64(2, kFileSize);
+ statement.BindInt(3, 0);
+ statement.BindInt(4, 1);
+ statement.BindCString(5, kTestClientNamespace);
+ statement.BindString(6, kTestClientId2.id);
+ statement.BindCString(7, kTestURL);
+ statement.BindString(8, base::FilePath(kFilePath).MaybeAsASCII());
+ statement.BindString16(9, base::UTF8ToUTF16("Test title"));
+ statement.BindCString(10, kOriginalTestURL);
+ ASSERT_TRUE(statement.Run());
+ ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
+ ASSERT_FALSE(
+ connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "request_origin"));
+}
+
class OfflinePageMetadataStoreFactory {
public:
OfflinePageMetadataStore* BuildStore(const base::FilePath& file_path) {
@@ -311,6 +354,13 @@ class OfflinePageMetadataStoreFactory {
base::ThreadTaskRunnerHandle::Get(), file_path);
return store;
}
+
+ OfflinePageMetadataStore* BuildStoreM57(const base::FilePath& file_path) {
+ BuildTestStoreWithSchemaFromM57(file_path);
+ OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+ base::ThreadTaskRunnerHandle::Get(), file_path);
+ return store;
+ }
};
enum CalledCallback { NONE, LOAD, ADD, UPDATE, REMOVE, RESET };
@@ -332,6 +382,7 @@ class OfflinePageMetadataStoreTest : public testing::Test {
std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM54();
std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM55();
std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM56();
+ std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM57();
void PumpLoop();
@@ -550,6 +601,22 @@ OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM56() {
return store;
}
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM57() {
+ std::unique_ptr<OfflinePageMetadataStore> store(
+ factory_.BuildStoreM57(temp_directory_.GetPath()));
+ PumpLoop();
+ store->Initialize(
+ base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ store->GetOfflinePages(
+ base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ return store;
+}
+
// Loads empty store and makes sure that there are no offline pages stored in
// it.
TEST_F(OfflinePageMetadataStoreTest, LoadEmptyStore) {
@@ -685,6 +752,17 @@ TEST_F(OfflinePageMetadataStoreTest, LoadVersion56Store) {
CheckThatOfflinePageCanBeSaved(std::move(store));
}
+// Loads a string with schema from M57.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
+TEST_F(OfflinePageMetadataStoreTest, LoadVersion57Store) {
+ std::unique_ptr<OfflinePageMetadataStore> store(
+ BuildStoreWithSchemaFromM57());
+
+ OfflinePageItem item = CheckThatStoreHasOneItem();
+ CheckThatOfflinePageCanBeSaved(std::move(store));
+}
+
// Adds metadata of an offline page into a store and then opens the store
// again to make sure that stored metadata survives store restarts.
TEST_F(OfflinePageMetadataStoreTest, AddOfflinePage) {
@@ -796,7 +874,7 @@ TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
base::FilePath(FILE_PATH_LITERAL("//other.page.com.mhtml"));
OfflinePageItem offline_page_2(GURL("https://other.page.com"), 5678LL,
kTestClientId2, file_path_2, 12345,
- base::Time::Now());
+ base::Time::Now(), "abc.xyz");
offline_page_2.original_url = GURL("https://example.com/bar");
store->AddOfflinePage(offline_page_2,
base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
@@ -854,6 +932,7 @@ TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
offline_pages_[0].last_access_time);
EXPECT_EQ(offline_page_2.access_count, offline_pages_[0].access_count);
EXPECT_EQ(offline_page_2.client_id, offline_pages_[0].client_id);
+ EXPECT_EQ(offline_page_2.request_origin, offline_pages_[0].request_origin);
}
// Tests updating offline page metadata from the store.
@@ -884,6 +963,7 @@ TEST_F(OfflinePageMetadataStoreTest, UpdateOfflinePage) {
offline_page.file_size = kFileSize + 1;
offline_page.access_count++;
offline_page.original_url = GURL("https://example.com/bar");
+ offline_page.request_origin = "abc.xyz";
std::vector<OfflinePageItem> items_to_update;
items_to_update.push_back(offline_page);
store->UpdateOfflinePages(
diff --git a/chromium/components/offline_pages/core/offline_page_model.cc b/chromium/components/offline_pages/core/offline_page_model.cc
index e8d7edeb33b..9e3a4af45a5 100644
--- a/chromium/components/offline_pages/core/offline_page_model.cc
+++ b/chromium/components/offline_pages/core/offline_page_model.cc
@@ -14,21 +14,26 @@ OfflinePageModel::SavePageParams::SavePageParams()
: proposed_offline_id(OfflinePageModel::kInvalidOfflineId),
is_background(false) {}
-OfflinePageModel::SavePageParams::SavePageParams(const SavePageParams& other) {
- url = other.url;
- client_id = other.client_id;
- proposed_offline_id = other.proposed_offline_id;
- original_url = other.original_url;
- is_background = other.is_background;
-}
+OfflinePageModel::SavePageParams::SavePageParams(const SavePageParams& other) =
+ default;
+
+OfflinePageModel::SavePageParams::~SavePageParams() = default;
+
+OfflinePageModel::DeletedPageInfo::DeletedPageInfo(
+ int64_t offline_id,
+ const ClientId& client_id,
+ const std::string& request_origin)
+ : offline_id(offline_id),
+ client_id(client_id),
+ request_origin(request_origin) {}
// static
bool OfflinePageModel::CanSaveURL(const GURL& url) {
return url.is_valid() && url.SchemeIsHTTPOrHTTPS();
}
-OfflinePageModel::OfflinePageModel() {}
+OfflinePageModel::OfflinePageModel() = default;
-OfflinePageModel::~OfflinePageModel() {}
+OfflinePageModel::~OfflinePageModel() = default;
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_model.h b/chromium/components/offline_pages/core/offline_page_model.h
index 52c24570922..ee5fba6f6e4 100644
--- a/chromium/components/offline_pages/core/offline_page_model.h
+++ b/chromium/components/offline_pages/core/offline_page_model.h
@@ -57,6 +57,7 @@ class OfflinePageModel : public base::SupportsUserData {
struct SavePageParams {
SavePageParams();
SavePageParams(const SavePageParams& other);
+ ~SavePageParams();
// The last committed URL of the page to save.
GURL url;
@@ -76,6 +77,22 @@ class OfflinePageModel : public base::SupportsUserData {
// Run page problem detectors while generating MTHML if true.
bool use_page_problem_detectors;
+
+ // The app package that the request originated from.
+ std::string request_origin;
+ };
+
+ // Information about a deleted page.
+ struct DeletedPageInfo {
+ DeletedPageInfo(int64_t offline_id,
+ const ClientId& client_id,
+ const std::string& request_origin);
+ // The ID of the deleted page.
+ int64_t offline_id;
+ // Client ID of the deleted page.
+ ClientId client_id;
+ // The origin that the page was saved on behalf of.
+ std::string request_origin;
};
// Observer of the OfflinePageModel.
@@ -89,8 +106,7 @@ class OfflinePageModel : public base::SupportsUserData {
const OfflinePageItem& added_page) = 0;
// Invoked when an offline copy related to |offline_id| was deleted.
- virtual void OfflinePageDeleted(int64_t offline_id,
- const ClientId& client_id) = 0;
+ virtual void OfflinePageDeleted(const DeletedPageInfo& page_info) = 0;
protected:
virtual ~Observer() = default;
@@ -121,6 +137,10 @@ class OfflinePageModel : public base::SupportsUserData {
std::unique_ptr<OfflinePageArchiver> archiver,
const SavePageCallback& callback) = 0;
+ // Adds a page entry to the metadata store.
+ virtual void AddPage(const OfflinePageItem& page,
+ const 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,
// will be updated. Requires that the model is loaded.
diff --git a/chromium/components/offline_pages/core/offline_page_model_impl.cc b/chromium/components/offline_pages/core/offline_page_model_impl.cc
index 4c3bbdce98b..c6ee5cabb92 100644
--- a/chromium/components/offline_pages/core/offline_page_model_impl.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_impl.cc
@@ -323,7 +323,11 @@ void ReportInitializationAttemptsSpent(int attempts_spent) {
// protected
OfflinePageModelImpl::OfflinePageModelImpl()
- : OfflinePageModel(), is_loaded_(false), weak_ptr_factory_(this) {}
+ : OfflinePageModel(),
+ is_loaded_(false),
+ testing_clock_(nullptr),
+ skip_clearing_original_url_for_testing_(false),
+ weak_ptr_factory_(this) {}
OfflinePageModelImpl::OfflinePageModelImpl(
std::unique_ptr<OfflinePageMetadataStore> store,
@@ -335,6 +339,7 @@ OfflinePageModelImpl::OfflinePageModelImpl(
policy_controller_(new ClientPolicyController()),
archive_manager_(new ArchiveManager(archives_dir, task_runner)),
testing_clock_(nullptr),
+ skip_clearing_original_url_for_testing_(false),
weak_ptr_factory_(this) {
archive_manager_->EnsureArchivesDirCreated(
base::Bind(&OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone,
@@ -391,6 +396,20 @@ void OfflinePageModelImpl::SavePage(
pending_archivers_.push_back(std::move(archiver));
}
+void OfflinePageModelImpl::AddPage(const OfflinePageItem& page,
+ const AddPageCallback& callback) {
+ RunWhenLoaded(base::Bind(&OfflinePageModelImpl::AddPageWhenLoadDone,
+ weak_ptr_factory_.GetWeakPtr(), page, callback));
+}
+
+void OfflinePageModelImpl::AddPageWhenLoadDone(
+ const OfflinePageItem& page,
+ const AddPageCallback& callback) {
+ store_->AddOfflinePage(
+ page, base::Bind(&OfflinePageModelImpl::OnAddPageDone,
+ weak_ptr_factory_.GetWeakPtr(), page, callback));
+}
+
void OfflinePageModelImpl::MarkPageAccessed(int64_t offline_id) {
RunWhenLoaded(base::Bind(&OfflinePageModelImpl::MarkPageAccessedWhenLoadDone,
weak_ptr_factory_.GetWeakPtr(), offline_id));
@@ -711,11 +730,12 @@ void OfflinePageModelImpl::OnCreateArchiveDone(
const base::FilePath& file_path,
const base::string16& title,
int64_t file_size) {
+ DeletePendingArchiver(archiver);
+
if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
SavePageResult result = ToSavePageResult(archiver_result);
InformSavePageDone(
callback, result, save_page_params.client_id, offline_id);
- DeletePendingArchiver(archiver);
return;
}
@@ -723,32 +743,34 @@ void OfflinePageModelImpl::OnCreateArchiveDone(
DVLOG(1) << "Saved URL does not match requested URL.";
InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED,
save_page_params.client_id, offline_id);
- DeletePendingArchiver(archiver);
return;
}
- OfflinePageItem offline_page_item(saved_url, offline_id,
- save_page_params.client_id, file_path,
- file_size, start_time);
- offline_page_item.title = title;
- offline_page_item.original_url = save_page_params.original_url;
- store_->AddOfflinePage(offline_page_item,
- base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone,
- weak_ptr_factory_.GetWeakPtr(), archiver,
- file_path, callback, offline_page_item));
+ OfflinePageItem offline_page(saved_url, offline_id,
+ save_page_params.client_id, file_path, file_size,
+ start_time);
+ offline_page.title = title;
+ // 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
+ // the completion of certain action, i.e., authentication, in the middle.
+ if (skip_clearing_original_url_for_testing_ ||
+ save_page_params.original_url != offline_page.url) {
+ offline_page.original_url = save_page_params.original_url;
+ }
+ offline_page.request_origin = save_page_params.request_origin;
+ AddPageWhenLoadDone(
+ offline_page,
+ base::Bind(&OfflinePageModelImpl::OnAddSavedPageDone,
+ weak_ptr_factory_.GetWeakPtr(), offline_page, callback));
}
-void OfflinePageModelImpl::OnAddOfflinePageDone(
- OfflinePageArchiver* archiver,
- const base::FilePath& file_path,
- const SavePageCallback& callback,
- const OfflinePageItem& offline_page,
- ItemActionStatus status) {
- SavePageResult result;
-
+void OfflinePageModelImpl::OnAddPageDone(const OfflinePageItem& offline_page,
+ const AddPageCallback& callback,
+ ItemActionStatus status) {
+ AddPageResult result;
if (status == ItemActionStatus::SUCCESS) {
offline_pages_[offline_page.offline_id] = offline_page;
- result = SavePageResult::SUCCESS;
+ result = AddPageResult::SUCCESS;
ReportPageHistogramAfterSave(policy_controller_.get(), offline_pages_,
offline_page, GetCurrentTime());
offline_event_logger_.RecordPageSaved(offline_page.client_id.name_space,
@@ -756,27 +778,44 @@ void OfflinePageModelImpl::OnAddOfflinePageDone(
offline_page.offline_id);
} else if (status == ItemActionStatus::ALREADY_EXISTS) {
// Remove the orphaned archive. No callback necessary.
- archive_manager_->DeleteArchive(file_path, base::Bind([](bool) {}));
- result = SavePageResult::ALREADY_EXISTS;
+ archive_manager_->DeleteArchive(offline_page.file_path,
+ base::Bind([](bool) {}));
+ result = AddPageResult::ALREADY_EXISTS;
} else {
- result = SavePageResult::STORE_FAILURE;
- }
- InformSavePageDone(callback, result, offline_page.client_id,
- offline_page.offline_id);
- if (result == SavePageResult::SUCCESS) {
- DeleteExistingPagesWithSameURL(offline_page);
- } else {
- PostClearStorageIfNeededTask(false /* delayed */);
+ result = AddPageResult::STORE_FAILURE;
}
- DeletePendingArchiver(archiver);
+ callback.Run(result, offline_page.offline_id);
// We don't want to notify observers if the add failed.
- if (result != SavePageResult::SUCCESS)
- return;
+ if (status == ItemActionStatus::SUCCESS) {
+ for (Observer& observer : observers_)
+ observer.OfflinePageAdded(this, offline_page);
+ }
+}
- for (Observer& observer : observers_)
- observer.OfflinePageAdded(this, offline_page);
+void OfflinePageModelImpl::OnAddSavedPageDone(
+ const OfflinePageItem& offline_page,
+ const SavePageCallback& callback,
+ AddPageResult add_result,
+ int64_t offline_id) {
+ SavePageResult save_result;
+ if (add_result == AddPageResult::SUCCESS) {
+ save_result = SavePageResult::SUCCESS;
+ } else if (add_result == AddPageResult::ALREADY_EXISTS) {
+ save_result = SavePageResult::ALREADY_EXISTS;
+ } else if (add_result == AddPageResult::STORE_FAILURE) {
+ save_result = SavePageResult::STORE_FAILURE;
+ } else {
+ NOTREACHED();
+ save_result = SavePageResult::STORE_FAILURE;
+ }
+ InformSavePageDone(callback, save_result, offline_page.client_id, offline_id);
+ if (save_result == SavePageResult::SUCCESS) {
+ DeleteExistingPagesWithSameURL(offline_page);
+ } else {
+ PostClearStorageIfNeededTask(false /* delayed */);
+ }
}
void OfflinePageModelImpl::OnMarkPageAccesseDone(
@@ -883,6 +922,11 @@ void OfflinePageModelImpl::InformSavePageDone(const SavePageCallback& callback,
ReportSavePageResultHistogramAfterSave(client_id, result);
archive_manager_->GetStorageStats(
base::Bind(&ReportStorageHistogramsAfterSave));
+ // No need to pass in a callback, since if the archive manager fails every
+ // time when creating an archive directory, there's nothing else to do other
+ // than fail every attempt to save a page.
+ if (result == SavePageResult::ARCHIVE_CREATION_FAILED)
+ archive_manager_->EnsureArchivesDirCreated(base::Bind([]() {}));
callback.Run(result, offline_id);
}
@@ -976,8 +1020,9 @@ void OfflinePageModelImpl::OnRemoveOfflinePagesDone(
}
for (const auto& page : result->updated_items) {
+ DeletedPageInfo info(page.offline_id, page.client_id, page.request_origin);
for (Observer& observer : observers_)
- observer.OfflinePageDeleted(page.offline_id, page.client_id);
+ observer.OfflinePageDeleted(info);
}
DeletePageResult delete_result;
diff --git a/chromium/components/offline_pages/core/offline_page_model_impl.h b/chromium/components/offline_pages/core/offline_page_model_impl.h
index 0b9ba3e5065..8e4a7683252 100644
--- a/chromium/components/offline_pages/core/offline_page_model_impl.h
+++ b/chromium/components/offline_pages/core/offline_page_model_impl.h
@@ -67,6 +67,8 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
void SavePage(const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
const SavePageCallback& callback) override;
+ void AddPage(const OfflinePageItem& page,
+ const AddPageCallback& callback) override;
void MarkPageAccessed(int64_t offline_id) override;
void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
const DeletePageCallback& callback) override;
@@ -110,6 +112,10 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
OfflineEventLogger* GetLogger() override;
+ void set_skip_clearing_original_url_for_testing() {
+ skip_clearing_original_url_for_testing_ = true;
+ }
+
protected:
// Adding a protected constructor for testing-only purposes in
// offline_page_storage_manager_unittest.cc
@@ -166,17 +172,23 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
const base::FilePath& file_path,
const base::string16& title,
int64_t file_size);
- void OnAddOfflinePageDone(OfflinePageArchiver* archiver,
- const base::FilePath& file_path,
- const SavePageCallback& callback,
- const OfflinePageItem& offline_page,
- ItemActionStatus status);
+ void OnAddSavedPageDone(const OfflinePageItem& offline_page,
+ const SavePageCallback& callback,
+ AddPageResult add_result,
+ int64_t offline_id);
void InformSavePageDone(const SavePageCallback& callback,
SavePageResult result,
const ClientId& client_id,
int64_t offline_id);
void DeletePendingArchiver(OfflinePageArchiver* archiver);
+ // Steps for adding a page entry to metadata store.
+ void AddPageWhenLoadDone(const OfflinePageItem& page,
+ const AddPageCallback& callback);
+ void OnAddPageDone(const OfflinePageItem& offline_page,
+ const AddPageCallback& callback,
+ ItemActionStatus status);
+
// Steps for deleting files and data for an offline page.
void OnDeleteArchiveFilesDone(const std::vector<int64_t>& offline_ids,
const DeletePageCallback& callback,
@@ -285,6 +297,9 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
// it once it is not longer needed.
base::Clock* testing_clock_;
+ // Don't clear original URL if it is same as final URL. For testing only.
+ bool skip_clearing_original_url_for_testing_;
+
base::WeakPtrFactory<OfflinePageModelImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(OfflinePageModelImpl);
diff --git a/chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc b/chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc
index 6280ae09ff4..89b7432c2af 100644
--- a/chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc
@@ -15,6 +15,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/statistics_recorder.h"
+#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
@@ -56,6 +57,7 @@ const ClientId kTestClientId3(kTestClientNamespace, "42");
const ClientId kTestUserRequestedClientId(kUserRequestedNamespace, "714");
const int64_t kTestFileSize = 876543LL;
const base::string16 kTestTitle = base::UTF8ToUTF16("a title");
+const std::string kRequestOrigin("abc.xyz");
bool URLSpecContains(std::string contains_value, const GURL& url) {
std::string spec = url.spec();
@@ -80,14 +82,15 @@ class OfflinePageModelImplTest
void OfflinePageModelLoaded(OfflinePageModel* model) override;
void OfflinePageAdded(OfflinePageModel* model,
const OfflinePageItem& added_page) override;
- void OfflinePageDeleted(int64_t offline_id,
- const ClientId& client_id) override;
+ void OfflinePageDeleted(
+ const OfflinePageModel::DeletedPageInfo& pageInfo) override;
// OfflinePageTestArchiver::Observer implementation.
void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override;
// OfflinePageModel callbacks.
void OnSavePageDone(SavePageResult result, int64_t offline_id);
+ void OnAddPageDone(AddPageResult result, int64_t offline_id);
void OnDeletePageDone(DeletePageResult result);
void OnCheckPagesExistOfflineDone(const CheckPagesExistOfflineResult& result);
void OnGetOfflineIdsForClientIdDone(MultipleOfflineIdResult* storage,
@@ -136,11 +139,11 @@ class OfflinePageModelImplTest
std::unique_ptr<OfflinePageArchiver> archiver);
// Saves the page without waiting for it to finish.
- void SavePageWithArchiverAsync(
- const GURL& url,
- const ClientId& client_id,
- const GURL& original_url,
- std::unique_ptr<OfflinePageArchiver> archiver);
+ void SavePageWithArchiverAsync(const GURL& url,
+ const ClientId& client_id,
+ const GURL& original_url,
+ const std::string& request_origin,
+ std::unique_ptr<OfflinePageArchiver> archiver);
// All 3 methods below will wait for the save to finish.
void SavePageWithArchiver(
@@ -153,6 +156,12 @@ class OfflinePageModelImplTest
// Returns the offline ID of the saved page.
std::pair<SavePageResult, int64_t> SavePage(const GURL& url,
const ClientId& client_id);
+ std::pair<SavePageResult, int64_t> SavePage(
+ const GURL& url,
+ const ClientId& client_id,
+ const std::string& request_origin);
+
+ void AddPage(const OfflinePageItem& offline_page);
void DeletePage(int64_t offline_id, const DeletePageCallback& callback) {
std::vector<int64_t> offline_ids;
@@ -171,18 +180,28 @@ class OfflinePageModelImplTest
MultipleOfflinePageItemResult GetPagesByFinalURL(const GURL& url);
MultipleOfflinePageItemResult GetPagesByAllURLS(const GURL& url);
+ const base::FilePath& temp_path() const { return temp_dir_.GetPath(); }
+
OfflinePageModelImpl* model() { return model_.get(); }
int64_t last_save_offline_id() const { return last_save_offline_id_; }
SavePageResult last_save_result() const { return last_save_result_; }
+ AddPageResult last_add_result() const { return last_add_result_; }
+
+ int64_t last_add_offline_id() const { return last_add_offline_id_; }
+
DeletePageResult last_delete_result() const { return last_delete_result_; }
int64_t last_deleted_offline_id() const { return last_deleted_offline_id_; }
ClientId last_deleted_client_id() const { return last_deleted_client_id_; }
+ std::string last_deleted_request_origin() const {
+ return last_deleted_request_origin_;
+ }
+
const base::FilePath& last_archiver_path() { return last_archiver_path_; }
int last_cleared_pages_count() const { return last_cleared_pages_count_; }
@@ -203,10 +222,13 @@ class OfflinePageModelImplTest
std::unique_ptr<OfflinePageModelImpl> model_;
SavePageResult last_save_result_;
int64_t last_save_offline_id_;
+ AddPageResult last_add_result_;
+ int64_t last_add_offline_id_;
DeletePageResult last_delete_result_;
base::FilePath last_archiver_path_;
int64_t last_deleted_offline_id_;
ClientId last_deleted_client_id_;
+ std::string last_deleted_request_origin_;
CheckPagesExistOfflineResult last_pages_exist_result_;
int last_cleared_pages_count_;
DeletePageResult last_clear_page_result_;
@@ -220,6 +242,8 @@ OfflinePageModelImplTest::OfflinePageModelImplTest()
task_runner_handle_(task_runner_),
last_save_result_(SavePageResult::CANCELLED),
last_save_offline_id_(-1),
+ last_add_result_(AddPageResult::STORE_FAILURE),
+ last_add_offline_id_(-1),
last_delete_result_(DeletePageResult::CANCELLED),
last_deleted_offline_id_(-1) {}
@@ -250,10 +274,11 @@ void OfflinePageModelImplTest::OfflinePageModelLoaded(OfflinePageModel* model) {
ASSERT_EQ(model_.get(), model);
}
-void OfflinePageModelImplTest::OfflinePageDeleted(int64_t offline_id,
- const ClientId& client_id) {
- last_deleted_offline_id_ = offline_id;
- last_deleted_client_id_ = client_id;
+void OfflinePageModelImplTest::OfflinePageDeleted(
+ const OfflinePageModel::DeletedPageInfo& info) {
+ last_deleted_offline_id_ = info.offline_id;
+ last_deleted_client_id_ = info.client_id;
+ last_deleted_request_origin_ = info.request_origin;
}
void OfflinePageModelImplTest::OfflinePageAdded(
@@ -273,6 +298,12 @@ void OfflinePageModelImplTest::OnSavePageDone(SavePageResult result,
last_save_offline_id_ = offline_id;
}
+void OfflinePageModelImplTest::OnAddPageDone(AddPageResult result,
+ int64_t offline_id) {
+ last_add_result_ = result;
+ last_add_offline_id_ = offline_id;
+}
+
void OfflinePageModelImplTest::OnDeletePageDone(DeletePageResult result) {
last_delete_result_ = result;
}
@@ -354,12 +385,14 @@ void OfflinePageModelImplTest::SavePageWithArchiverAsync(
const GURL& url,
const ClientId& client_id,
const GURL& original_url,
+ const std::string& request_origin,
std::unique_ptr<OfflinePageArchiver> archiver) {
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.is_background = false;
+ save_page_params.request_origin = request_origin;
SavePageWithParamsAsync(save_page_params, std::move(archiver));
}
@@ -367,7 +400,7 @@ void OfflinePageModelImplTest::SavePageWithArchiver(
const GURL& url,
const ClientId& client_id,
std::unique_ptr<OfflinePageArchiver> archiver) {
- SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
+ SavePageWithArchiverAsync(url, client_id, GURL(), "", std::move(archiver));
PumpLoop();
}
@@ -376,19 +409,34 @@ void OfflinePageModelImplTest::SavePageWithArchiverResult(
const ClientId& client_id,
OfflinePageArchiver::ArchiverResult result) {
std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(url, result));
- SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
+ SavePageWithArchiverAsync(url, client_id, GURL(), "", std::move(archiver));
PumpLoop();
}
std::pair<SavePageResult, int64_t>
OfflinePageModelImplTest::SavePage(const GURL& url, const ClientId& client_id) {
+ return SavePage(url, client_id, "");
+}
+
+std::pair<SavePageResult, int64_t> OfflinePageModelImplTest::SavePage(
+ const GURL& url,
+ const ClientId& client_id,
+ const std::string& request_origin) {
std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
url, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
- SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
+ SavePageWithArchiverAsync(url, client_id, GURL(), request_origin,
+ std::move(archiver));
PumpLoop();
return std::make_pair(last_save_result_, last_save_offline_id_);
}
+void OfflinePageModelImplTest::AddPage(const OfflinePageItem& offline_page) {
+ model()->AddPage(
+ offline_page,
+ base::Bind(&OfflinePageModelImplTest::OnAddPageDone, AsWeakPtr()));
+ PumpLoop();
+}
+
MultipleOfflinePageItemResult OfflinePageModelImplTest::GetAllPages() {
MultipleOfflinePageItemResult result;
model()->GetAllPages(
@@ -514,8 +562,64 @@ TEST_F(OfflinePageModelImplTest, SavePageSuccessful) {
std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
- SavePageWithArchiverAsync(
- kTestUrl, kTestClientId1, kTestUrl2, std::move(archiver));
+ SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2, "",
+ std::move(archiver));
+ PumpLoop();
+ EXPECT_TRUE(HasPages(kTestClientNamespace));
+
+ OfflinePageTestStore* store = GetStore();
+ EXPECT_EQ(kTestUrl, store->last_saved_page().url);
+ EXPECT_EQ(kTestClientId1.id, store->last_saved_page().client_id.id);
+ EXPECT_EQ(kTestClientId1.name_space,
+ store->last_saved_page().client_id.name_space);
+ // Save last_archiver_path since it will be referred to later.
+ base::FilePath archiver_path = last_archiver_path();
+ EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
+ EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
+ EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+ ResetResults();
+
+ const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+ ASSERT_EQ(1UL, offline_pages.size());
+ EXPECT_EQ(kTestUrl, offline_pages[0].url);
+ EXPECT_EQ(kTestClientId1.id, offline_pages[0].client_id.id);
+ EXPECT_EQ(kTestClientId1.name_space, offline_pages[0].client_id.name_space);
+ EXPECT_EQ(archiver_path, offline_pages[0].file_path);
+ EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
+ EXPECT_EQ(0, offline_pages[0].access_count);
+ EXPECT_EQ(0, offline_pages[0].flags);
+ EXPECT_EQ(kTestTitle, offline_pages[0].title);
+ EXPECT_EQ(kTestUrl2, offline_pages[0].original_url);
+ EXPECT_EQ("", offline_pages[0].request_origin);
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageSuccessfulWithSameOriginalURL) {
+ std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+ kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+ // Pass the original URL same as the final URL.
+ SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl, "",
+ std::move(archiver));
+ PumpLoop();
+
+ EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+ ResetResults();
+
+ const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+ ASSERT_EQ(1UL, offline_pages.size());
+ EXPECT_EQ(kTestUrl, offline_pages[0].url);
+ // The original URL should be empty.
+ EXPECT_TRUE(offline_pages[0].original_url.is_empty());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageSuccessfulWithRequestOrigin) {
+ EXPECT_FALSE(HasPages(kTestClientNamespace));
+
+ std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+ kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+ SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2, kRequestOrigin,
+ std::move(archiver));
PumpLoop();
EXPECT_TRUE(HasPages(kTestClientNamespace));
@@ -543,6 +647,7 @@ TEST_F(OfflinePageModelImplTest, SavePageSuccessful) {
EXPECT_EQ(0, offline_pages[0].flags);
EXPECT_EQ(kTestTitle, offline_pages[0].title);
EXPECT_EQ(kTestUrl2, offline_pages[0].original_url);
+ EXPECT_EQ(kRequestOrigin, offline_pages[0].request_origin);
}
TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverCancelled) {
@@ -605,14 +710,15 @@ TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
// CompleteCreateArchive() is called.
OfflinePageTestArchiver* archiver_ptr = archiver.get();
archiver_ptr->set_delayed(true);
- SavePageWithArchiverAsync(
- kTestUrl, kTestClientId1, GURL(), std::move(archiver));
+ // First page has no request origin.
+ SavePageWithArchiverAsync(kTestUrl, kTestClientId1, GURL(), "",
+ std::move(archiver));
EXPECT_TRUE(archiver_ptr->create_archive_called());
// |remove_popup_overlay| should not be turned on on foreground mode.
EXPECT_FALSE(archiver_ptr->create_archive_params().remove_popup_overlay);
- // Request to save another page.
- SavePage(kTestUrl2, kTestClientId2);
+ // Request to save another page, with request origin.
+ SavePage(kTestUrl2, kTestClientId2, kRequestOrigin);
OfflinePageTestStore* store = GetStore();
@@ -621,6 +727,7 @@ TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
base::FilePath archiver_path2 = last_archiver_path();
EXPECT_EQ(archiver_path2, store->last_saved_page().file_path);
EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
+ EXPECT_EQ(kRequestOrigin, store->last_saved_page().request_origin);
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
ResetResults();
@@ -634,6 +741,7 @@ TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
base::FilePath archiver_path = last_archiver_path();
EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
+ EXPECT_EQ("", store->last_saved_page().request_origin);
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
ResetResults();
@@ -659,12 +767,14 @@ TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
EXPECT_EQ(kTestFileSize, page1->file_size);
EXPECT_EQ(0, page1->access_count);
EXPECT_EQ(0, page1->flags);
+ EXPECT_EQ("", page1->request_origin);
EXPECT_EQ(kTestUrl2, page2->url);
EXPECT_EQ(kTestClientId2, page2->client_id);
EXPECT_EQ(archiver_path2, page2->file_path);
EXPECT_EQ(kTestFileSize, page2->file_size);
EXPECT_EQ(0, page2->access_count);
EXPECT_EQ(0, page2->flags);
+ EXPECT_EQ(kRequestOrigin, page2->request_origin);
}
TEST_F(OfflinePageModelImplTest, SavePageOnBackground) {
@@ -686,6 +796,43 @@ TEST_F(OfflinePageModelImplTest, SavePageOnBackground) {
PumpLoop();
}
+TEST_F(OfflinePageModelImplTest, AddPage) {
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &file_path));
+ int64_t offline_id =
+ base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
+
+ // Adds a fresh page.
+ OfflinePageItem offline_page(kTestUrl, offline_id, kTestClientId1, file_path,
+ kTestFileSize);
+ offline_page.title = kTestTitle;
+ offline_page.original_url = kTestUrl2;
+ offline_page.request_origin = kRequestOrigin;
+ AddPage(offline_page);
+
+ EXPECT_EQ(AddPageResult::SUCCESS, last_add_result());
+
+ const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+ ASSERT_EQ(1UL, offline_pages.size());
+ EXPECT_EQ(kTestUrl, offline_pages[0].url);
+ EXPECT_EQ(kTestClientId1, offline_pages[0].client_id);
+ EXPECT_EQ(kTestUrl2, offline_pages[0].original_url);
+ EXPECT_EQ(kTestTitle, offline_pages[0].title);
+ EXPECT_EQ(file_path, offline_pages[0].file_path);
+ EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
+ EXPECT_EQ(kRequestOrigin, offline_pages[0].request_origin);
+
+ // Trying to adding a same page should result in ALREADY_EXISTS error.
+ AddPage(offline_page);
+ EXPECT_EQ(AddPageResult::ALREADY_EXISTS, last_add_result());
+
+ //
+ GetStore()->set_test_scenario(
+ OfflinePageTestStore::TestScenario::WRITE_FAILED);
+ AddPage(offline_page);
+ EXPECT_EQ(AddPageResult::STORE_FAILURE, last_add_result());
+}
+
TEST_F(OfflinePageModelImplTest, MarkPageAccessed) {
SavePage(kTestUrl, kTestClientId1);
@@ -711,15 +858,15 @@ TEST_F(OfflinePageModelImplTest, GetAllPagesStoreEmpty) {
TEST_F(OfflinePageModelImplTest, DeletePageSuccessful) {
OfflinePageTestStore* store = GetStore();
- // Save one page.
- SavePage(kTestUrl, kTestClientId1);
+ // Save one page with request origin.
+ SavePage(kTestUrl, kTestClientId1, kRequestOrigin);
int64_t offline1 = last_save_offline_id();
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
EXPECT_EQ(1u, store->GetAllPages().size());
ResetResults();
- // Save another page.
+ // Save another page, no request origin.
SavePage(kTestUrl2, kTestClientId2);
int64_t offline2 = last_save_offline_id();
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
@@ -735,6 +882,7 @@ TEST_F(OfflinePageModelImplTest, DeletePageSuccessful) {
EXPECT_EQ(last_deleted_offline_id(), offline1);
EXPECT_EQ(last_deleted_client_id(), kTestClientId1);
+ EXPECT_EQ(last_deleted_request_origin(), kRequestOrigin);
EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
ASSERT_EQ(1u, store->GetAllPages().size());
EXPECT_EQ(kTestUrl2, store->GetAllPages()[0].url);
@@ -749,6 +897,7 @@ TEST_F(OfflinePageModelImplTest, DeletePageSuccessful) {
EXPECT_EQ(last_deleted_offline_id(), offline2);
EXPECT_EQ(last_deleted_client_id(), kTestClientId2);
+ EXPECT_EQ(last_deleted_request_origin(), "");
EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
EXPECT_EQ(0u, store->GetAllPages().size());
}
@@ -1023,8 +1172,8 @@ TEST_F(OfflinePageModelImplTest, GetPagesByFinalURLWithFragmentStripped) {
TEST_F(OfflinePageModelImplTest, GetPagesByAllURLS) {
std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
- SavePageWithArchiverAsync(
- kTestUrl, kTestClientId1, kTestUrl2, std::move(archiver));
+ SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2, "",
+ std::move(archiver));
PumpLoop();
SavePage(kTestUrl2, kTestClientId2);
@@ -1277,7 +1426,7 @@ TEST_F(OfflinePageModelImplTest, StoreLoadFailurePersists) {
TEST_F(OfflinePageModelImplTest, GetPagesMatchingQuery) {
std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
- SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2,
+ SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2, "",
std::move(archiver));
PumpLoop();
@@ -1306,50 +1455,32 @@ TEST_F(OfflinePageModelImplTest, GetPagesMatchingQuery) {
}
TEST(CommandLineFlagsTest, OfflineBookmarks) {
- // Disabled by default.
- EXPECT_FALSE(offline_pages::IsOfflineBookmarksEnabled());
+ // Enabled by default.
+ EXPECT_TRUE(offline_pages::IsOfflineBookmarksEnabled());
- // Check if feature is correctly enabled by command-line flag.
+ // Check if feature is correctly disabled by command-line flag.
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(kOfflineBookmarksFeature);
- EXPECT_TRUE(offline_pages::IsOfflineBookmarksEnabled());
+ scoped_feature_list.InitAndDisableFeature(kOfflineBookmarksFeature);
+ EXPECT_FALSE(offline_pages::IsOfflineBookmarksEnabled());
}
TEST(CommandLineFlagsTest, OffliningRecentPages) {
- // Enable offline bookmarks feature first.
- // TODO(dimich): once offline pages are enabled by default, remove this.
- std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
- new base::test::ScopedFeatureList);
- scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
+ // Enabled by default.
+ EXPECT_TRUE(offline_pages::IsOffliningRecentPagesEnabled());
- // This feature is still disabled by default.
+ // Check if feature is correctly disabled by command-line flag.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(kOffliningRecentPagesFeature);
EXPECT_FALSE(offline_pages::IsOffliningRecentPagesEnabled());
-
- // Check if feature is correctly enabled by command-line flag.
- scoped_feature_list.reset(new base::test::ScopedFeatureList);
- scoped_feature_list->InitFromCommandLine(
- std::string(kOfflineBookmarksFeature.name) + "," +
- kOffliningRecentPagesFeature.name,
- "");
- EXPECT_TRUE(offline_pages::IsOffliningRecentPagesEnabled());
}
TEST(CommandLineFlagsTest, OfflinePagesSharing) {
- // Enable offline bookmarks feature first.
- // TODO(dimich): once offline pages are enabled by default, remove this.
- std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
- new base::test::ScopedFeatureList);
- scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
-
- // This feature is still disabled by default.
+ // This feature is disabled by default.
EXPECT_FALSE(offline_pages::IsOfflinePagesSharingEnabled());
- // Check if feature is correctly enabled by command-line flag.
- scoped_feature_list.reset(new base::test::ScopedFeatureList);
- scoped_feature_list->InitFromCommandLine(
- std::string(kOfflineBookmarksFeature.name) + "," +
- kOfflinePagesSharingFeature.name,
- "");
+ // Check if feature is correctly disabled by command-line flag.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(kOfflinePagesSharingFeature);
EXPECT_TRUE(offline_pages::IsOfflinePagesSharingEnabled());
}
diff --git a/chromium/components/offline_pages/core/offline_page_test_store.cc b/chromium/components/offline_pages/core/offline_page_test_store.cc
index 5ed9b202fc4..1f63830cff4 100644
--- a/chromium/components/offline_pages/core/offline_page_test_store.cc
+++ b/chromium/components/offline_pages/core/offline_page_test_store.cc
@@ -54,9 +54,13 @@ void OfflinePageTestStore::AddOfflinePage(const OfflinePageItem& offline_page,
ItemActionStatus result;
if (store_state_ == StoreState::LOADED &&
scenario_ != TestScenario::WRITE_FAILED) {
- offline_pages_[offline_page.offline_id] = offline_page;
- last_saved_page_ = offline_page;
- result = ItemActionStatus::SUCCESS;
+ if (offline_pages_.count(offline_page.offline_id)) {
+ result = ItemActionStatus::ALREADY_EXISTS;
+ } else {
+ offline_pages_[offline_page.offline_id] = offline_page;
+ last_saved_page_ = offline_page;
+ result = ItemActionStatus::SUCCESS;
+ }
} else {
result = ItemActionStatus::STORE_ERROR;
}
diff --git a/chromium/components/offline_pages/core/offline_page_types.h b/chromium/components/offline_pages/core/offline_page_types.h
index e266bba419b..552b32a92d8 100644
--- a/chromium/components/offline_pages/core/offline_page_types.h
+++ b/chromium/components/offline_pages/core/offline_page_types.h
@@ -44,6 +44,17 @@ enum class SavePageResult {
RESULT_COUNT,
};
+// Result of adding an offline page.
+enum class AddPageResult {
+ SUCCESS,
+ STORE_FAILURE,
+ ALREADY_EXISTS,
+ // NOTE: always keep this entry at the end. Add new result types only
+ // immediately above this line. Make sure to update the corresponding
+ // histogram enum accordingly.
+ RESULT_COUNT,
+};
+
// Result of deleting an offline page.
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
@@ -66,6 +77,7 @@ typedef std::vector<int64_t> MultipleOfflineIdResult;
typedef std::vector<OfflinePageItem> MultipleOfflinePageItemResult;
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(const CheckPagesExistOfflineResult&)>
CheckPagesExistOfflineCallback;
diff --git a/chromium/components/offline_pages/core/prefetch/BUILD.gn b/chromium/components/offline_pages/core/prefetch/BUILD.gn
index ae365d2bd5a..63ec7359b3f 100644
--- a/chromium/components/offline_pages/core/prefetch/BUILD.gn
+++ b/chromium/components/offline_pages/core/prefetch/BUILD.gn
@@ -9,34 +9,103 @@ import("//third_party/protobuf/proto_library.gni")
static_library("prefetch") {
sources = [
+ "add_unique_urls_task.cc",
+ "add_unique_urls_task.h",
"generate_page_bundle_request.cc",
"generate_page_bundle_request.h",
+ "generate_page_bundle_task.cc",
+ "generate_page_bundle_task.h",
"get_operation_request.cc",
"get_operation_request.h",
+ "get_operation_task.cc",
+ "get_operation_task.h",
+ "offline_metrics_collector.h",
"prefetch_dispatcher.h",
"prefetch_dispatcher_impl.cc",
"prefetch_dispatcher_impl.h",
+ "prefetch_downloader.cc",
+ "prefetch_downloader.h",
+ "prefetch_gcm_app_handler.cc",
+ "prefetch_gcm_app_handler.h",
+ "prefetch_gcm_handler.h",
+ "prefetch_importer.h",
"prefetch_item.cc",
"prefetch_item.h",
+ "prefetch_network_request_factory.h",
+ "prefetch_network_request_factory_impl.cc",
+ "prefetch_network_request_factory_impl.h",
"prefetch_proto_utils.cc",
"prefetch_proto_utils.h",
"prefetch_request_fetcher.cc",
"prefetch_request_fetcher.h",
+ "prefetch_server_urls.cc",
+ "prefetch_server_urls.h",
"prefetch_service.h",
"prefetch_service_impl.cc",
"prefetch_service_impl.h",
"prefetch_types.cc",
"prefetch_types.h",
+ "store/prefetch_store.cc",
+ "store/prefetch_store.h",
+ "store/prefetch_store_utils.cc",
+ "store/prefetch_store_utils.h",
+ "suggested_articles_observer.cc",
+ "suggested_articles_observer.h",
]
public_deps = [
":proto",
]
+
deps = [
"//base",
+ "//components/download/public",
+ "//components/gcm_driver",
+ "//components/gcm_driver/common",
"//components/keyed_service/core",
+ "//components/ntp_snippets",
"//components/offline_pages/core",
+ "//components/offline_pages/core:switches",
+ "//components/variations:variations",
+ "//components/version_info",
+ "//google_apis",
"//net:net",
+ "//sql:sql",
+ "//url",
+ ]
+}
+
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "prefetch_request_test_base.cc",
+ "prefetch_request_test_base.h",
+ "prefetch_service_test_taco.cc",
+ "prefetch_service_test_taco.h",
+ "store/prefetch_store_test_util.cc",
+ "store/prefetch_store_test_util.h",
+ "task_test_base.cc",
+ "task_test_base.h",
+ "test_offline_metrics_collector.h",
+ "test_prefetch_dispatcher.cc",
+ "test_prefetch_dispatcher.h",
+ "test_prefetch_gcm_handler.cc",
+ "test_prefetch_gcm_handler.h",
+ "test_prefetch_importer.h",
+ "test_prefetch_network_request_factory.cc",
+ "test_prefetch_network_request_factory.h",
+ ]
+
+ deps = [
+ ":prefetch",
+ "//base",
+ "//components/gcm_driver/instance_id",
+ "//components/keyed_service/core",
+ "//components/offline_pages/core",
+ "//components/prefs:test_support",
+ "//components/version_info:channel",
+ "//net:test_support",
+ "//sql:sql",
"//url",
]
}
@@ -47,27 +116,42 @@ proto_library("proto") {
"proto/offline_pages.proto",
"proto/operation.proto",
"proto/status.proto",
- "proto/timestamp.proto",
]
}
source_set("unit_tests") {
testonly = true
sources = [
+ "add_unique_urls_task_unittest.cc",
"generate_page_bundle_request_unittest.cc",
+ "generate_page_bundle_task_unittest.cc",
"get_operation_request_unittest.cc",
+ "get_operation_task_unittest.cc",
"prefetch_dispatcher_impl_unittest.cc",
+ "prefetch_downloader_unittest.cc",
+ "prefetch_gcm_app_handler_unittest.cc",
"prefetch_item_unittest.cc",
+ "prefetch_network_request_factory_impl_unittest.cc",
"prefetch_request_fetcher_unittest.cc",
"prefetch_request_operation_response_unittest.cc",
- "prefetch_request_test_base.cc",
- "prefetch_request_test_base.h",
+ "prefetch_server_urls_unittest.cc",
+ "store/prefetch_store_unittest.cc",
+ "suggested_articles_observer_unittest.cc",
]
deps = [
":prefetch",
+ ":test_support",
+ "//base",
+ "//components/download/public",
+ "//components/gcm_driver/instance_id",
"//components/offline_pages/core",
+ "//components/offline_pages/core:switches",
+ "//components/offline_pages/core:test_support",
+ "//components/variations:test_support",
+ "//components/version_info:channel",
"//net:test_support",
+ "//sql:sql",
"//testing/gmock",
"//testing/gtest",
"//url",
diff --git a/chromium/components/offline_pages/core/prefetch/DEPS b/chromium/components/offline_pages/core/prefetch/DEPS
new file mode 100644
index 00000000000..98d9dd2c4b6
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+components/download/public",
+ "+google_apis",
+ "+components/variations",
+ "+components/gcm_driver",
+ "+components/ntp_snippets",
+ "+components/version_info",
+ "+net",
+ "+sql",
+]
diff --git a/chromium/components/offline_pages/core/prefetch/README.md b/chromium/components/offline_pages/core/prefetch/README.md
index 46e5a0e5563..1d6e9ca11c8 100644
--- a/chromium/components/offline_pages/core/prefetch/README.md
+++ b/chromium/components/offline_pages/core/prefetch/README.md
@@ -1,6 +1,40 @@
-# Prefetching Offline Pages: development guidelines
+# Prefetching Offline Pages
-* Implementations that are injected dependencies should always provide
- lightweight construction and postpone heavier initialization (i.e. DB
+## Architecture overview
+
+### PrefetchService
+
+Is the ownership holder for the main components of the prefetching system and
+controls their lifetime.
+
+### PrefetchDispatcher
+
+Manages the prefetching pipeline tasks. It receives signals from external
+clients and creates the appropriate tasks to execute them. It _might_ at some
+point execute advanced task management operations like canceling queued tasks or
+changing their order of execution.
+
+### \*Task(s) (i.e. AddUniqueUrlsTask)
+
+They are the main wrapper of pipeline steps and interact with different
+abstracted components (Downloads, persistent store, GCM, etc) to execute them.
+They implement TaskQueue's Task API so that they can be exclusively executed.
+
+## Prefetch store
+
+* The PrefetchStore depends publicly on SQL and acts as a gateway to the SQLite
+ database.
+* It defines specific method signatures used to create callbacks that are passed
+ to the store for the proper execution of SQL commands.
+* SQL access resources are granted to those callbacks only when needed and in an
+ appropriate environment (correct thread, etc).
+* Pipeline tasks define methods following those signatures that contain the SQL
+ commands they require to do their work.
+* Tasks receive a pointer to the store to be able to execute their SQL commands.
+
+## Development guidelines
+
+* Implementations that are injected dependencies during service creation should
+ have lightweight construction and postpone heavier initialization (i.e. DB
connection) to a later moment. Lazy initialization upon first actual usage is
recommended.
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
new file mode 100644
index 00000000000..732941c67a9
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.cc
@@ -0,0 +1,145 @@
+// 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/offline_pages/core/prefetch/add_unique_urls_task.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_utils.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+namespace {
+
+std::map<std::string, std::pair<int64_t, PrefetchItemState>>
+FindExistingPrefetchItemsInNamespaceSync(sql::Connection* db,
+ const std::string& name_space) {
+ static const char kSql[] =
+ "SELECT offline_id, state, requested_url FROM prefetch_items"
+ " WHERE client_namespace = ?";
+ sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, name_space);
+
+ std::map<std::string, std::pair<int64_t, PrefetchItemState>> result;
+ while (statement.Step()) {
+ result.insert(std::make_pair(
+ statement.ColumnString(2),
+ std::make_pair(statement.ColumnInt64(0), static_cast<PrefetchItemState>(
+ statement.ColumnInt(1)))));
+ }
+
+ return result;
+}
+
+bool CreatePrefetchItemSync(sql::Connection* db,
+ const std::string& name_space,
+ const PrefetchURL& prefetch_url) {
+ static const char kSql[] =
+ "INSERT INTO prefetch_items"
+ " (offline_id, requested_url, client_namespace, client_id, creation_time,"
+ " freshness_time)"
+ " VALUES"
+ " (?, ?, ?, ?, ?, ?)";
+
+ int64_t now_internal = base::Time::Now().ToInternalValue();
+ sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, GenerateOfflineId());
+ statement.BindString(1, prefetch_url.url.spec());
+ statement.BindString(2, name_space);
+ statement.BindString(3, prefetch_url.id);
+ statement.BindInt64(4, now_internal);
+ statement.BindInt64(5, now_internal);
+
+ return statement.Run();
+}
+
+// Adds new prefetch item entries to the store using the URLs and client IDs
+// from |candidate_prefetch_urls| and the client's |name_space|. Also cleans up
+// entries in the Zombie state from the client's |name_space| except for the
+// ones whose URL is contained in |candidate_prefetch_urls|.
+// Returns the number of added prefecth items.
+AddUniqueUrlsTask::Result AddUrlsAndCleanupZombiesSync(
+ const std::string& name_space,
+ const std::vector<PrefetchURL>& candidate_prefetch_urls,
+ sql::Connection* db) {
+ if (!db)
+ return AddUniqueUrlsTask::Result::STORE_ERROR;
+
+ sql::Transaction transaction(db);
+ if (!transaction.Begin())
+ return AddUniqueUrlsTask::Result::STORE_ERROR;
+
+ std::map<std::string, std::pair<int64_t, PrefetchItemState>> existing_items =
+ FindExistingPrefetchItemsInNamespaceSync(db, name_space);
+
+ AddUniqueUrlsTask::Result result(AddUniqueUrlsTask::Result::NOTHING_ADDED);
+ for (const auto& prefetch_url : candidate_prefetch_urls) {
+ auto iter = existing_items.find(prefetch_url.url.spec());
+ if (iter == existing_items.end()) {
+ if (!CreatePrefetchItemSync(db, name_space, prefetch_url))
+ return AddUniqueUrlsTask::Result::STORE_ERROR; // Transaction rollback.
+ result = AddUniqueUrlsTask::Result::URLS_ADDED;
+ } else {
+ // Removing from the list of existing items if it was requested again, to
+ // prevent it from being removed in the next step.
+ existing_items.erase(iter);
+ }
+ }
+
+ // Purge remaining zombie IDs.
+ for (const auto& existing_item : existing_items) {
+ if (existing_item.second.second != PrefetchItemState::ZOMBIE)
+ continue;
+ if (!DeletePrefetchItemByOfflineIdSync(db, existing_item.second.first))
+ return AddUniqueUrlsTask::Result::STORE_ERROR; // Transaction rollback.
+ }
+
+ if (!transaction.Commit())
+ return AddUniqueUrlsTask::Result::STORE_ERROR; // Transaction rollback.
+
+ return result;
+}
+}
+
+AddUniqueUrlsTask::AddUniqueUrlsTask(
+ PrefetchStore* prefetch_store,
+ const std::string& name_space,
+ const std::vector<PrefetchURL>& prefetch_urls)
+ : prefetch_store_(prefetch_store),
+ name_space_(name_space),
+ prefetch_urls_(prefetch_urls),
+ weak_ptr_factory_(this) {}
+
+AddUniqueUrlsTask::~AddUniqueUrlsTask() {}
+
+void AddUniqueUrlsTask::Run() {
+ prefetch_store_->Execute(base::BindOnce(&AddUrlsAndCleanupZombiesSync,
+ name_space_, prefetch_urls_),
+ base::BindOnce(&AddUniqueUrlsTask::OnUrlsAdded,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AddUniqueUrlsTask::OnUrlsAdded(Result result) {
+ if (result == Result::URLS_ADDED) {
+ // TODO(carlosk): schedule NWake here if at least one new entry was added to
+ // the store.
+ NOTIMPLEMENTED();
+ }
+ TaskComplete();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.h b/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.h
new file mode 100644
index 00000000000..b4009158236
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.h
@@ -0,0 +1,58 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_ADD_UNIQUE_URLS_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_ADD_UNIQUE_URLS_TASK_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+class PrefetchStore;
+struct PrefetchURL;
+
+// Task that adds new URL suggestions to the pipeline. URLs are matched against
+// existing ones from any stage of the process so that only new, unique ones are
+// actually added.
+// Fully processed items are kept in the store in the PrefetchItemState::ZOMBIE
+// state until it is confirmed that the client for its namespace is not
+// recommending the same URL anymore to avoid processing it twice. So once the
+// step described above is done, all same namespace items in the ZOMBIE state
+// whose URL didn't match any of the just suggested ones are finally deleted
+// from the store.
+class AddUniqueUrlsTask : public Task {
+ public:
+ // Result of executing the command in the store.
+ enum class Result {
+ NOTHING_ADDED,
+ URLS_ADDED,
+ STORE_ERROR,
+ };
+
+ AddUniqueUrlsTask(PrefetchStore* prefetch_store,
+ const std::string& name_space,
+ const std::vector<PrefetchURL>& prefetch_urls);
+ ~AddUniqueUrlsTask() override;
+
+ void Run() override;
+
+ private:
+ void OnUrlsAdded(Result result);
+
+ // Prefetch store to execute against. Not owned.
+ PrefetchStore* prefetch_store_;
+ const std::string& name_space_;
+ std::vector<PrefetchURL> prefetch_urls_;
+
+ base::WeakPtrFactory<AddUniqueUrlsTask> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(AddUniqueUrlsTask);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_ADD_UNIQUE_URLS_TASK_H_
diff --git a/chromium/components/offline_pages/core/prefetch/add_unique_urls_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/add_unique_urls_task_unittest.cc
new file mode 100644
index 00000000000..4a9c0437519
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/add_unique_urls_task_unittest.cc
@@ -0,0 +1,129 @@
+// 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/offline_pages/core/prefetch/add_unique_urls_task.h"
+
+#include <string>
+#include <vector>
+
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+void VerifyItemCount(int expected_count, int actual_count) {
+ EXPECT_EQ(expected_count, actual_count);
+}
+} // namespace
+
+class AddUniqueUrlsTaskTest : public testing::Test {
+ public:
+ AddUniqueUrlsTaskTest();
+ ~AddUniqueUrlsTaskTest() override = default;
+
+ void SetUp() override;
+ void TearDown() override;
+
+ PrefetchStore* store() { return store_test_util_.store(); }
+
+ PrefetchStoreTestUtil* store_util() { return &store_test_util_; }
+
+ void PumpLoop();
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ PrefetchStoreTestUtil store_test_util_;
+};
+
+AddUniqueUrlsTaskTest::AddUniqueUrlsTaskTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+void AddUniqueUrlsTaskTest::SetUp() {
+ store_test_util_.BuildStoreInMemory();
+}
+
+void AddUniqueUrlsTaskTest::TearDown() {
+ store_test_util_.DeleteStore();
+ PumpLoop();
+}
+
+void AddUniqueUrlsTaskTest::PumpLoop() {
+ task_runner_->RunUntilIdle();
+}
+
+TEST_F(AddUniqueUrlsTaskTest, AddTaskInEmptyStore) {
+ std::string name_space("test");
+ std::vector<PrefetchURL> urls;
+ urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+ urls.push_back(PrefetchURL{"ID-2", GURL("http://www.example.com/")});
+ AddUniqueUrlsTask task(store(), name_space, urls);
+ task.Run();
+ PumpLoop();
+
+ store_util()->CountPrefetchItems(base::BindOnce(&VerifyItemCount, 2));
+ PumpLoop();
+}
+
+TEST_F(AddUniqueUrlsTaskTest, DontAddURLIfItExists) {
+ std::string name_space("test");
+ std::vector<PrefetchURL> urls;
+ urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+ urls.push_back(PrefetchURL{"ID-2", GURL("http://www.example.com/")});
+ AddUniqueUrlsTask task1(store(), name_space, urls);
+ task1.Run();
+ PumpLoop();
+
+ urls.clear();
+ urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+ urls.push_back(PrefetchURL{"ID-3", GURL("https://news.google.com/")});
+ AddUniqueUrlsTask task2(store(), name_space, urls);
+ task2.Run();
+ PumpLoop();
+
+ // Do the count here.
+ store_util()->CountPrefetchItems(base::BindOnce(&VerifyItemCount, 3));
+ PumpLoop();
+}
+
+TEST_F(AddUniqueUrlsTaskTest, HandleZombiePrefetchItems) {
+ std::string name_space("test");
+ std::vector<PrefetchURL> urls;
+ urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+ urls.push_back(PrefetchURL{"ID-2", GURL("http://www.example.com/")});
+ urls.push_back(PrefetchURL{"ID-3", GURL("https://news.google.com/")});
+ AddUniqueUrlsTask task1(store(), name_space, urls);
+ task1.Run();
+ PumpLoop();
+
+ store_util()->ZombifyPrefetchItem(name_space, urls[0].url,
+ base::BindOnce(&VerifyItemCount, 1));
+ PumpLoop();
+ store_util()->ZombifyPrefetchItem(name_space, urls[1].url,
+ base::BindOnce(&VerifyItemCount, 1));
+ PumpLoop();
+
+ urls.clear();
+ urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+ urls.push_back(PrefetchURL{"ID-3", GURL("https://news.google.com/")});
+ urls.push_back(PrefetchURL{"ID-4", GURL("https://chrome.google.com/")});
+ // ID-1 is expected to stay in zombie state.
+ // ID-2 is expected to be removed, because it is in zombie state.
+ // ID-3 is still requested, so it is ignored.
+ // ID-4 is added.
+ AddUniqueUrlsTask task2(store(), name_space, urls);
+ task2.Run();
+ PumpLoop();
+
+ // Do the count here.
+ store_util()->CountPrefetchItems(base::BindOnce(&VerifyItemCount, 3));
+ PumpLoop();
+}
+
+} // namespace offline_pages
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 e6a3c99e38b..3cb6b2b674b 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
@@ -9,21 +9,19 @@
#include "base/logging.h"
#include "components/offline_pages/core/prefetch/prefetch_proto_utils.h"
#include "components/offline_pages/core/prefetch/prefetch_request_fetcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
#include "components/offline_pages/core/prefetch/proto/offline_pages.pb.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
namespace offline_pages {
-namespace {
-const char kGeneratePageBundleRequestURLPath[] = "v1:GeneratePageBundle";
-} // namespace
-
GeneratePageBundleRequest::GeneratePageBundleRequest(
const std::string& user_agent,
const std::string& gcm_registration_id,
int max_bundle_size_bytes,
const std::vector<std::string>& page_urls,
+ version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
const PrefetchRequestFinishedCallback& callback)
: callback_(callback) {
@@ -43,7 +41,8 @@ GeneratePageBundleRequest::GeneratePageBundleRequest(
request.SerializeToString(&upload_data);
fetcher_ = PrefetchRequestFetcher::CreateForPost(
- kGeneratePageBundleRequestURLPath, upload_data, request_context_getter,
+ GeneratePageBundleRequestURL(channel), upload_data,
+ request_context_getter,
base::Bind(&GeneratePageBundleRequest::OnCompleted,
// Fetcher is owned by this instance.
base::Unretained(this)));
@@ -54,18 +53,19 @@ GeneratePageBundleRequest::~GeneratePageBundleRequest() {}
void GeneratePageBundleRequest::OnCompleted(PrefetchRequestStatus status,
const std::string& data) {
if (status != PrefetchRequestStatus::SUCCESS) {
- callback_.Run(status, std::vector<RenderPageInfo>());
+ callback_.Run(status, std::string(), std::vector<RenderPageInfo>());
return;
}
std::vector<RenderPageInfo> pages;
- if (!ParseOperationResponse(data, &pages)) {
+ std::string operation_name = ParseOperationResponse(data, &pages);
+ if (operation_name.empty()) {
callback_.Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
- std::vector<RenderPageInfo>());
+ std::string(), std::vector<RenderPageInfo>());
return;
}
- callback_.Run(PrefetchRequestStatus::SUCCESS, pages);
+ callback_.Run(PrefetchRequestStatus::SUCCESS, operation_name, pages);
}
} // 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 43113135169..6e13334ca37 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
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/version_info/channel.h"
namespace net {
class URLRequestContextGetter;
@@ -26,6 +27,7 @@ class GeneratePageBundleRequest {
const std::string& gcm_registration_id,
int max_bundle_size_bytes,
const std::vector<std::string>& page_urls,
+ version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
const PrefetchRequestFinishedCallback& callback);
~GeneratePageBundleRequest();
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 297b3e8aa8f..17e478a46f8 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
@@ -12,6 +12,7 @@
#include "net/url_request/url_request_status.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
+#include "url/url_constants.h"
using testing::_;
using testing::DoAll;
@@ -21,6 +22,7 @@ using testing::SaveArg;
namespace offline_pages {
namespace {
+const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
const char kTestURL[] = "http://example.com";
const char kTestURL2[] = "http://example.com/2";
const char kTestUserAgent[] = "Test User Agent";
@@ -37,9 +39,9 @@ class GeneratePageBundleRequestTest : public PrefetchRequestTestBase {
const PrefetchRequestFinishedCallback& callback) {
std::vector<std::string> page_urls = {kTestURL, kTestURL2};
return std::unique_ptr<GeneratePageBundleRequest>(
- new GeneratePageBundleRequest(kTestUserAgent, kTestGCMID,
- kTestMaxBundleSize, page_urls,
- request_context(), callback));
+ new GeneratePageBundleRequest(
+ kTestUserAgent, kTestGCMID, kTestMaxBundleSize, page_urls,
+ kTestChannel, request_context(), callback));
}
};
@@ -49,6 +51,10 @@ TEST_F(GeneratePageBundleRequestTest, RequestData) {
CreateRequest(callback.Get()));
net::TestURLFetcher* fetcher = GetRunningFetcher();
+ EXPECT_TRUE(fetcher->GetOriginalURL().SchemeIs(url::kHttpsScheme));
+ EXPECT_TRUE(base::StartsWith(fetcher->GetOriginalURL().query(), "key",
+ base::CompareCase::SENSITIVE));
+
EXPECT_FALSE(fetcher->upload_content_type().empty());
EXPECT_FALSE(fetcher->upload_data().empty());
@@ -71,12 +77,15 @@ TEST_F(GeneratePageBundleRequestTest, EmptyResponse) {
CreateRequest(callback.Get()));
PrefetchRequestStatus status;
+ std::string operation_name;
std::vector<RenderPageInfo> pages;
- EXPECT_CALL(callback, Run(_, _))
- .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+ EXPECT_CALL(callback, Run(_, _, _))
+ .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+ SaveArg<2>(&pages)));
RespondWithData("");
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+ EXPECT_TRUE(operation_name.empty());
EXPECT_TRUE(pages.empty());
}
@@ -86,12 +95,15 @@ TEST_F(GeneratePageBundleRequestTest, InvalidResponse) {
CreateRequest(callback.Get()));
PrefetchRequestStatus status;
+ std::string operation_name;
std::vector<RenderPageInfo> pages;
- EXPECT_CALL(callback, Run(_, _))
- .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+ EXPECT_CALL(callback, Run(_, _, _))
+ .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+ SaveArg<2>(&pages)));
RespondWithData("Some invalid data");
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+ EXPECT_TRUE(operation_name.empty());
EXPECT_TRUE(pages.empty());
}
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
new file mode 100644
index 00000000000..b5ded96f23d
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.cc
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/prefetch/generate_page_bundle_task.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
+#include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
+
+namespace offline_pages {
+
+namespace {
+
+// TODO(dewittj): Use the SQL store.
+// Once that TODO is done, this will choose from the list of available Prefetch
+// URLs stored in the prefetch model. Until then, this does nothing.
+static int SelectURLsToPrefetch() {
+ NOTIMPLEMENTED();
+ return 1;
+}
+
+// TODO(fgorski): replace this with the SQL executor.
+static void Execute(base::RepeatingCallback<int()> command_callback,
+ base::OnceCallback<void(int)> result_callback) {
+ std::move(result_callback).Run(command_callback.Run());
+}
+} // namespace
+
+GeneratePageBundleTask::GeneratePageBundleTask(
+ const std::vector<PrefetchURL>& prefetch_urls,
+ PrefetchGCMHandler* gcm_handler,
+ PrefetchNetworkRequestFactory* request_factory,
+ const PrefetchRequestFinishedCallback& callback)
+ : prefetch_urls_(prefetch_urls),
+ gcm_handler_(gcm_handler),
+ request_factory_(request_factory),
+ callback_(callback),
+ weak_factory_(this) {}
+
+GeneratePageBundleTask::~GeneratePageBundleTask() {}
+
+void GeneratePageBundleTask::Run() {
+ Execute(base::BindRepeating(&SelectURLsToPrefetch),
+ base::BindOnce(&GeneratePageBundleTask::StartGeneratePageBundle,
+ weak_factory_.GetWeakPtr()));
+}
+
+void GeneratePageBundleTask::StartGeneratePageBundle(int updated_entry_count) {
+ if (prefetch_urls_.empty()) {
+ TaskComplete();
+ return;
+ }
+
+ gcm_handler_->GetGCMToken(base::Bind(
+ &GeneratePageBundleTask::GotRegistrationId, weak_factory_.GetWeakPtr()));
+}
+
+void GeneratePageBundleTask::GotRegistrationId(
+ const std::string& id,
+ instance_id::InstanceID::Result result) {
+ std::vector<std::string> url_strings;
+ for (auto& prefetch_url : prefetch_urls_) {
+ url_strings.push_back(prefetch_url.url.spec());
+ }
+
+ request_factory_->MakeGeneratePageBundleRequest(url_strings, id, callback_);
+ TaskComplete();
+}
+
+} // namespace offline_pages
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
new file mode 100644
index 00000000000..4f990055fcb
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.h
@@ -0,0 +1,52 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_GENERATE_PAGE_BUNDLE_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_GENERATE_PAGE_BUNDLE_TASK_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+class PrefetchGCMHandler;
+class PrefetchNetworkRequestFactory;
+
+// Task that attempts to start archiving the URLs the prefetch service has
+// determined are viable to prefetch.
+class GeneratePageBundleTask : public Task {
+ public:
+ // TODO(dewittj): remove the list of prefetch URLs when the DB operation can
+ // supply the current set of URLs.
+ GeneratePageBundleTask(const std::vector<PrefetchURL>& prefetch_urls,
+ PrefetchGCMHandler* gcm_handler,
+ PrefetchNetworkRequestFactory* request_factory,
+ const PrefetchRequestFinishedCallback& callback);
+ ~GeneratePageBundleTask() override;
+
+ // Task implementation.
+ void Run() override;
+
+ private:
+ void StartGeneratePageBundle(int updated_entry_count);
+ void GotRegistrationId(const std::string& id,
+ instance_id::InstanceID::Result result);
+
+ std::vector<PrefetchURL> prefetch_urls_;
+ PrefetchGCMHandler* gcm_handler_;
+ PrefetchNetworkRequestFactory* request_factory_;
+ PrefetchRequestFinishedCallback callback_;
+
+ base::WeakPtrFactory<GeneratePageBundleTask> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GeneratePageBundleTask);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_GENERATE_PAGE_BUNDLE_TASK_H_
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc
new file mode 100644
index 00000000000..f57f8817837
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc
@@ -0,0 +1,69 @@
+// 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/offline_pages/core/prefetch/generate_page_bundle_task.h"
+
+#include "base/test/mock_callback.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/task_test_base.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h"
+#include "components/offline_pages/core/task.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::HasSubstr;
+using testing::DoAll;
+using testing::SaveArg;
+
+namespace offline_pages {
+
+// All tests cases here only validate the request data and check for general
+// http response. The tests for the Operation proto data returned in the http
+// response are covered in PrefetchRequestOperationResponseTest.
+class GeneratePageBundleTaskTest : public TaskTestBase {
+ public:
+ GeneratePageBundleTaskTest() = default;
+ ~GeneratePageBundleTaskTest() override = default;
+
+ TestPrefetchGCMHandler* gcm_handler() { return &gcm_handler_; }
+
+ private:
+ TestPrefetchGCMHandler gcm_handler_;
+};
+
+TEST_F(GeneratePageBundleTaskTest, EmptyTask) {
+ base::MockCallback<PrefetchRequestFinishedCallback> callback;
+ GeneratePageBundleTask task(std::vector<PrefetchURL>(), gcm_handler(),
+ prefetch_request_factory(), callback.Get());
+ ExpectTaskCompletes(&task);
+
+ task.Run();
+ task_runner->RunUntilIdle();
+
+ EXPECT_EQ(nullptr,
+ prefetch_request_factory()->CurrentGeneratePageBundleRequest());
+}
+
+TEST_F(GeneratePageBundleTaskTest, TaskMakesNetworkRequest) {
+ base::MockCallback<PrefetchRequestFinishedCallback> callback;
+ std::vector<PrefetchURL> urls = {
+ {"id1", GURL("https://example.com/1.html")},
+ {"id2", GURL("https://example.com/2.html")},
+ };
+ GeneratePageBundleTask task(urls, gcm_handler(), prefetch_request_factory(),
+ callback.Get());
+ ExpectTaskCompletes(&task);
+
+ task.Run();
+ task_runner->RunUntilIdle();
+
+ EXPECT_NE(nullptr,
+ prefetch_request_factory()->CurrentGeneratePageBundleRequest());
+ std::string upload_data =
+ url_fetcher_factory()->GetFetcherByID(0)->upload_data();
+ EXPECT_THAT(upload_data, HasSubstr("example.com"));
+}
+
+} // namespace offline_pages
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 1f3a78ac5b5..a8541ce847e 100644
--- a/chromium/components/offline_pages/core/prefetch/get_operation_request.cc
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_request.cc
@@ -9,24 +9,20 @@
#include "base/logging.h"
#include "components/offline_pages/core/prefetch/prefetch_proto_utils.h"
#include "components/offline_pages/core/prefetch/prefetch_request_fetcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
namespace offline_pages {
-namespace {
-const char kGetOperationURLPath[] = "v1/operations/";
-} // namespace
-
GetOperationRequest::GetOperationRequest(
const std::string& name,
+ version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
const PrefetchRequestFinishedCallback& callback)
: callback_(callback) {
- std::string path(kGetOperationURLPath);
- path += name;
fetcher_ = PrefetchRequestFetcher::CreateForGet(
- path, request_context_getter,
+ GetOperationRequestURL(name, channel), request_context_getter,
base::Bind(&GetOperationRequest::OnCompleted,
// Fetcher is owned by this instance.
base::Unretained(this)));
@@ -37,18 +33,19 @@ GetOperationRequest::~GetOperationRequest() {}
void GetOperationRequest::OnCompleted(PrefetchRequestStatus status,
const std::string& data) {
if (status != PrefetchRequestStatus::SUCCESS) {
- callback_.Run(status, std::vector<RenderPageInfo>());
+ callback_.Run(status, std::string(), std::vector<RenderPageInfo>());
return;
}
std::vector<RenderPageInfo> pages;
- if (!ParseOperationResponse(data, &pages)) {
+ std::string operation_name = ParseOperationResponse(data, &pages);
+ if (operation_name.empty()) {
callback_.Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
- std::vector<RenderPageInfo>());
+ std::string(), std::vector<RenderPageInfo>());
return;
}
- callback_.Run(PrefetchRequestStatus::SUCCESS, pages);
+ callback_.Run(PrefetchRequestStatus::SUCCESS, operation_name, pages);
}
} // 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 6793f2eb229..a074639f6db 100644
--- a/chromium/components/offline_pages/core/prefetch/get_operation_request.h
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_request.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/version_info/channel.h"
namespace net {
class URLRequestContextGetter;
@@ -27,6 +28,7 @@ class GetOperationRequest {
// It is retrieved from the operation data returned in the
// GeneratePageBundleRequest response.
GetOperationRequest(const std::string& name,
+ version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
const PrefetchRequestFinishedCallback& callback);
~GetOperationRequest();
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 e0535d9aeee..770cba5e5fe 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
@@ -12,6 +12,7 @@
#include "net/url_request/url_request_status.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
+#include "url/url_constants.h"
using testing::_;
using testing::DoAll;
@@ -21,7 +22,14 @@ using testing::SaveArg;
namespace offline_pages {
namespace {
-const char kTestMethodName[] = "Test name";
+const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
+
+// Operations include part of the path in the operation name.
+const char kTestOperationName[] = "operations/test-operation-1234";
+
+// This is hard coded so we can express the known URL structure for operations.
+const char kServerPathForTestOperation[] = "/v1/operations/test-operation-1234";
+
} // namespace
// All tests cases here only validate the request data and check for general
@@ -31,8 +39,8 @@ class GetOperationRequestTest : public PrefetchRequestTestBase {
public:
std::unique_ptr<GetOperationRequest> CreateRequest(
const PrefetchRequestFinishedCallback& callback) {
- return std::unique_ptr<GetOperationRequest>(
- new GetOperationRequest(kTestMethodName, request_context(), callback));
+ return std::unique_ptr<GetOperationRequest>(new GetOperationRequest(
+ kTestOperationName, kTestChannel, request_context(), callback));
}
};
@@ -41,6 +49,12 @@ TEST_F(GetOperationRequestTest, RequestData) {
std::unique_ptr<GetOperationRequest> request(CreateRequest(callback.Get()));
net::TestURLFetcher* fetcher = GetRunningFetcher();
+ GURL fetcher_url = fetcher->GetOriginalURL();
+ EXPECT_TRUE(fetcher_url.SchemeIs(url::kHttpsScheme));
+ EXPECT_EQ(kServerPathForTestOperation, fetcher_url.path());
+ EXPECT_TRUE(base::StartsWith(fetcher_url.query(), "key",
+ base::CompareCase::SENSITIVE));
+
net::HttpRequestHeaders headers;
fetcher->GetExtraRequestHeaders(&headers);
std::string content_type_header;
@@ -57,12 +71,15 @@ TEST_F(GetOperationRequestTest, EmptyResponse) {
std::unique_ptr<GetOperationRequest> request(CreateRequest(callback.Get()));
PrefetchRequestStatus status;
+ std::string operation_name;
std::vector<RenderPageInfo> pages;
- EXPECT_CALL(callback, Run(_, _))
- .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+ EXPECT_CALL(callback, Run(_, _, _))
+ .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+ SaveArg<2>(&pages)));
RespondWithData("");
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+ EXPECT_TRUE(operation_name.empty());
EXPECT_TRUE(pages.empty());
}
@@ -71,12 +88,15 @@ TEST_F(GetOperationRequestTest, InvalidResponse) {
std::unique_ptr<GetOperationRequest> request(CreateRequest(callback.Get()));
PrefetchRequestStatus status;
+ std::string operation_name;
std::vector<RenderPageInfo> pages;
- EXPECT_CALL(callback, Run(_, _))
- .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+ EXPECT_CALL(callback, Run(_, _, _))
+ .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+ SaveArg<2>(&pages)));
RespondWithData("Some invalid data");
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+ EXPECT_TRUE(operation_name.empty());
EXPECT_TRUE(pages.empty());
}
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_task.cc b/chromium/components/offline_pages/core/prefetch/get_operation_task.cc
new file mode 100644
index 00000000000..ec1f65d3595
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_task.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/prefetch/get_operation_task.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
+
+namespace offline_pages {
+
+namespace {
+
+// Will hold the actual SQL implementation for marking a GetOperation attempt in
+// the database.
+static int MarkOperationAttemptStarted(const std::string& operation_name) {
+ NOTIMPLEMENTED();
+ return 1;
+}
+
+// TODO(fgorski): replace this with the SQL executor.
+static void Execute(base::RepeatingCallback<int()> command_callback,
+ base::OnceCallback<void(int)> result_callback) {
+ std::move(result_callback).Run(command_callback.Run());
+}
+} // namespace
+
+GetOperationTask::GetOperationTask(
+ const std::string& operation_name,
+ PrefetchNetworkRequestFactory* request_factory,
+ const PrefetchRequestFinishedCallback& callback)
+ : operation_name_(operation_name),
+ request_factory_(request_factory),
+ callback_(callback),
+ weak_factory_(this) {}
+
+GetOperationTask::~GetOperationTask() {}
+
+void GetOperationTask::Run() {
+ Execute(base::BindRepeating(&MarkOperationAttemptStarted, operation_name_),
+ base::BindOnce(&GetOperationTask::StartGetOperation,
+ weak_factory_.GetWeakPtr()));
+}
+
+void GetOperationTask::StartGetOperation(int updated_entry_count) {
+ request_factory_->MakeGetOperationRequest(operation_name_, callback_);
+ TaskComplete();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_task.h b/chromium/components/offline_pages/core/prefetch/get_operation_task.h
new file mode 100644
index 00000000000..bedcebec00b
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_task.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_GET_OPERATION_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_GET_OPERATION_TASK_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+class PrefetchNetworkRequestFactory;
+
+// Task that attempts to fetch results for a completed operation.
+class GetOperationTask : public Task {
+ public:
+ GetOperationTask(const std::string& operation_name,
+ PrefetchNetworkRequestFactory* request_factory,
+ const PrefetchRequestFinishedCallback& callback);
+ ~GetOperationTask() override;
+
+ // Task implementation.
+ void Run() override;
+
+ private:
+ void StartGetOperation(int updated_entry_count);
+
+ const std::string& operation_name_;
+ PrefetchNetworkRequestFactory* request_factory_;
+ PrefetchRequestFinishedCallback callback_;
+
+ base::WeakPtrFactory<GetOperationTask> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetOperationTask);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_GET_OPERATION_TASK_H_
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/get_operation_task_unittest.cc
new file mode 100644
index 00000000000..59917a066d2
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_task_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/offline_pages/core/prefetch/get_operation_task.h"
+
+#include "base/test/mock_callback.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/task_test_base.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h"
+#include "components/offline_pages/core/task.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::HasSubstr;
+using testing::DoAll;
+using testing::SaveArg;
+
+namespace offline_pages {
+
+// All tests cases here only validate the request data and check for general
+// http response. The tests for the Operation proto data returned in the http
+// response are covered in PrefetchRequestOperationResponseTest.
+class GetOperationTaskTest : public TaskTestBase {
+ public:
+ GetOperationTaskTest() = default;
+ ~GetOperationTaskTest() override = default;
+
+ TestPrefetchGCMHandler* gcm_handler() { return &gcm_handler_; }
+
+ private:
+ TestPrefetchGCMHandler gcm_handler_;
+};
+
+TEST_F(GetOperationTaskTest, NormalOperationTask) {
+ base::MockCallback<PrefetchRequestFinishedCallback> callback;
+ std::string operation_name = "anoperation";
+ GetOperationTask task(operation_name, prefetch_request_factory(),
+ callback.Get());
+ ExpectTaskCompletes(&task);
+
+ task.Run();
+ task_runner->RunUntilIdle();
+
+ EXPECT_NE(nullptr, prefetch_request_factory()->FindGetOperationRequestByName(
+ operation_name));
+ std::string path =
+ url_fetcher_factory()->GetFetcherByID(0)->GetOriginalURL().path();
+ EXPECT_THAT(path, HasSubstr(operation_name));
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/offline_metrics_collector.h b/chromium/components/offline_pages/core/prefetch/offline_metrics_collector.h
new file mode 100644
index 00000000000..b11212e8b8c
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/offline_metrics_collector.h
@@ -0,0 +1,36 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_OFFLINE_METRICS_COLLECTOR_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_OFFLINE_METRICS_COLLECTOR_H_
+
+namespace offline_pages {
+
+// Observes various events and collects the data in order to classify
+// a day as 'offline', 'online' etc. Keeps the accumulated counters of each day
+// type until it is a good moment to report it (most often on a connected day).
+// The actual reporting is done by UMA.
+class OfflineMetricsCollector {
+ public:
+ virtual ~OfflineMetricsCollector() = default;
+
+ // Chrome started up or was (as on Android) brought from background to
+ // foreground. A day when this happened is a day the browser was used.
+ virtual void OnAppStartupOrResume() = 0;
+
+ // Successful online navigation committed. A day when this happens is counted
+ // as 'connected' day.
+ virtual void OnSuccessfulNavigationOnline() = 0;
+
+ // Successful navigation to an offline page happened. A day when it happens is
+ // at least a 'offline_content' day
+ virtual void OnSuccessfulNavigationOffline() = 0;
+
+ // Uses UMA to report the accumulated classification for the days past.
+ virtual void ReportAccumulatedStats() = 0;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_OFFLINE_METRICS_COLLECTOR_H_
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher.h b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher.h
index 36f9b206de3..033cd545194 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher.h
@@ -5,32 +5,23 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DISPATCHER_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DISPATCHER_H_
+#include <memory>
#include <string>
#include <vector>
#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/prefetch/prefetch_dispatcher.h"
-
-class GURL;
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
namespace offline_pages {
+class PrefetchService;
// Serves as the entry point for external signals into the prefetching system.
// It listens to these events, converts them to the appropriate internal tasks
// and manage their execution and inter-dependencies.
class PrefetchDispatcher {
public:
- struct PrefetchURL {
- // Used to differentiate URLs from different sources. |name_space| should
- // be unique per source. |id| can be anything useful to identify the page,
- // but will not be used for deduplication.
- ClientId client_id;
-
- // This URL will be prefetched by the system.
- GURL url;
- };
-
// A |ScopedBackgroundTask| is created when we are running in a background
// task. Destroying this object should notify the system that we are done
// processing the background task.
@@ -39,16 +30,22 @@ class PrefetchDispatcher {
ScopedBackgroundTask() = default;
virtual ~ScopedBackgroundTask() = default;
- // Used on destruction to inform the system about whether rescheduling is
- // required.
- virtual void SetNeedsReschedule(bool reschedule) = 0;
+ // Used on destruction to inform the system about whether rescheduling with
+ // or without backoff is required.
+ virtual void SetNeedsReschedule(bool reschedule, bool backoff) = 0;
};
virtual ~PrefetchDispatcher() = default;
- // Called when a consumer has candidate URLs for the system to prefetch.
- // Duplicates are accepted by the PrefetchDispatcher but ignored.
+ // Initializes the dispatcher with its respective service instance. This must
+ // be done before any other methods are called.
+ virtual void SetService(PrefetchService* service) = 0;
+
+ // Called when a client has candidate URLs for the system to prefetch, along
+ // with the client's unique namespace. URLs that are currently in the system
+ // for this client are acceptable but ignored.
virtual void AddCandidatePrefetchURLs(
+ const std::string& name_space,
const std::vector<PrefetchURL>& prefetch_urls) = 0;
// Called when all existing suggestions are no longer considered valid for a
@@ -70,7 +67,15 @@ class PrefetchDispatcher {
// Called when a task must stop immediately due to system constraints. After
// this call completes, the system will reschedule the task based on whether
// SetNeedsReschedule has been called.
- virtual void StopBackgroundTask(ScopedBackgroundTask* task) = 0;
+ virtual void StopBackgroundTask() = 0;
+
+ // Called when the GCM app handler receives a GCM message with an embeddeed
+ // operation name.
+ virtual void GCMOperationCompletedMessageReceived(
+ const std::string& operation_name) = 0;
+
+ // Used by the test to signal the completion of the background task.
+ virtual void RequestFinishBackgroundTaskForTest() = 0;
};
} // namespace offline_pages
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 3c84ca5e937..04b825495b8 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -4,35 +4,146 @@
#include "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
+#include <utility>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/memory/ptr_util.h"
+#include "base/task_runner.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/prefetch/add_unique_urls_task.h"
+#include "components/offline_pages/core/prefetch/generate_page_bundle_task.h"
+#include "components/offline_pages/core/prefetch/get_operation_task.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
+#include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
+#include "components/offline_pages/core/task.h"
#include "url/gurl.h"
namespace offline_pages {
-PrefetchDispatcherImpl::PrefetchDispatcherImpl() {}
+namespace {
+void DeleteBackgroundTaskHelper(
+ std::unique_ptr<PrefetchDispatcher::ScopedBackgroundTask> task) {
+ task.reset();
+}
+} // namespace
+
+PrefetchDispatcherImpl::PrefetchDispatcherImpl() : weak_factory_(this) {}
PrefetchDispatcherImpl::~PrefetchDispatcherImpl() = default;
+void PrefetchDispatcherImpl::SetService(PrefetchService* service) {
+ CHECK(service);
+ service_ = service;
+}
+
void PrefetchDispatcherImpl::AddCandidatePrefetchURLs(
- const std::vector<PrefetchURL>& url_suggestions) {
- NOTIMPLEMENTED();
+ const std::string& name_space,
+ const std::vector<PrefetchURL>& prefetch_urls) {
+ if (!IsPrefetchingOfflinePagesEnabled())
+ return;
+
+ PrefetchStore* prefetch_store = service_->GetPrefetchStore();
+ std::unique_ptr<Task> add_task = base::MakeUnique<AddUniqueUrlsTask>(
+ prefetch_store, name_space, prefetch_urls);
+ task_queue_.AddTask(std::move(add_task));
+
+ // TODO(dewittj): Remove when we have proper scheduling.
+ BeginBackgroundTask(nullptr);
}
+
void PrefetchDispatcherImpl::RemoveAllUnprocessedPrefetchURLs(
const std::string& name_space) {
+ if (!IsPrefetchingOfflinePagesEnabled())
+ return;
+
NOTIMPLEMENTED();
}
void PrefetchDispatcherImpl::RemovePrefetchURLsByClientId(
const ClientId& client_id) {
+ if (!IsPrefetchingOfflinePagesEnabled())
+ return;
+
NOTIMPLEMENTED();
}
void PrefetchDispatcherImpl::BeginBackgroundTask(
- std::unique_ptr<ScopedBackgroundTask> task) {
- NOTIMPLEMENTED();
+ std::unique_ptr<ScopedBackgroundTask> background_task) {
+ if (!IsPrefetchingOfflinePagesEnabled())
+ return;
+
+ background_task_ = std::move(background_task);
+
+ // TODO(dewittj): Remove this when the task can get the suggestions from the
+ // SQL store directly.
+ std::vector<PrefetchURL> prefetch_urls;
+ service_->GetSuggestedArticlesObserver()->GetCurrentSuggestions(
+ &prefetch_urls);
+
+ std::unique_ptr<Task> generate_page_bundle_task =
+ base::MakeUnique<GeneratePageBundleTask>(
+ prefetch_urls, service_->GetPrefetchGCMHandler(),
+ service_->GetPrefetchNetworkRequestFactory(),
+ base::Bind(&PrefetchDispatcherImpl::DidPrefetchRequest,
+ weak_factory_.GetWeakPtr(), "GeneratePageBundleRequest"));
+ task_queue_.AddTask(std::move(generate_page_bundle_task));
}
-void PrefetchDispatcherImpl::StopBackgroundTask(ScopedBackgroundTask* task) {
- NOTIMPLEMENTED();
+void PrefetchDispatcherImpl::StopBackgroundTask() {
+ if (!IsPrefetchingOfflinePagesEnabled())
+ return;
+
+ DisposeTask();
+}
+
+void PrefetchDispatcherImpl::RequestFinishBackgroundTaskForTest() {
+ if (!IsPrefetchingOfflinePagesEnabled())
+ return;
+
+ DisposeTask();
+}
+
+void PrefetchDispatcherImpl::DisposeTask() {
+ DCHECK(background_task_);
+ // Delay the deletion till the caller finishes.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&DeleteBackgroundTaskHelper,
+ base::Passed(std::move(background_task_))));
+}
+
+void PrefetchDispatcherImpl::GCMOperationCompletedMessageReceived(
+ const std::string& operation_name) {
+ if (!IsPrefetchingOfflinePagesEnabled())
+ return;
+
+ task_queue_.AddTask(base::MakeUnique<GetOperationTask>(
+ operation_name, service_->GetPrefetchNetworkRequestFactory(),
+ base::Bind(&PrefetchDispatcherImpl::DidPrefetchRequest,
+ weak_factory_.GetWeakPtr(), "GetOperation")));
+}
+
+void PrefetchDispatcherImpl::DidPrefetchRequest(
+ const std::string& request_name_for_logging,
+ PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages) {
+ service_->GetLogger()->RecordActivity(
+ "Finished " + request_name_for_logging +
+ " for operation: " + operation_name +
+ " with status: " + std::to_string(static_cast<int>(status)) +
+ "; included " + std::to_string(pages.size()) + " pages in result.");
+ for (const RenderPageInfo& page : pages) {
+ service_->GetLogger()->RecordActivity(
+ "Response for page: " + page.url +
+ "; status=" + std::to_string(static_cast<int>(page.status)));
+ }
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
index e826d0a9f9c..4559abd1a86 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
@@ -5,10 +5,19 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DISPATCHER_IMPL_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DISPATCHER_IMPL_H_
+#include <memory>
+#include <string>
+#include <vector>
+
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/task_queue.h"
+#include "components/version_info/channel.h"
+#include "net/url_request/url_request_context_getter.h"
namespace offline_pages {
+class PrefetchService;
class PrefetchDispatcherImpl : public PrefetchDispatcher {
public:
@@ -16,14 +25,34 @@ class PrefetchDispatcherImpl : public PrefetchDispatcher {
~PrefetchDispatcherImpl() override;
// PrefetchDispatcher implementation:
+ void SetService(PrefetchService* service) override;
void AddCandidatePrefetchURLs(
- const std::vector<PrefetchURL>& suggested_urls) override;
+ const std::string& name_space,
+ const std::vector<PrefetchURL>& prefetch_urls) override;
void RemoveAllUnprocessedPrefetchURLs(const std::string& name_space) override;
void RemovePrefetchURLsByClientId(const ClientId& client_id) override;
- void BeginBackgroundTask(std::unique_ptr<ScopedBackgroundTask> task) override;
- void StopBackgroundTask(ScopedBackgroundTask* task) override;
+ void BeginBackgroundTask(
+ std::unique_ptr<ScopedBackgroundTask> background_task) override;
+ void StopBackgroundTask() override;
+ void GCMOperationCompletedMessageReceived(
+ const std::string& operation_name) override;
+ void RequestFinishBackgroundTaskForTest() override;
private:
+ friend class PrefetchDispatcherTest;
+
+ void DisposeTask();
+ void DidPrefetchRequest(const std::string& request_name_for_logging,
+ PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages);
+
+ PrefetchService* service_;
+ TaskQueue task_queue_;
+ std::unique_ptr<ScopedBackgroundTask> background_task_;
+
+ base::WeakPtrFactory<PrefetchDispatcherImpl> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(PrefetchDispatcherImpl);
};
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 0a0bd961d48..7944ff133d0 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
@@ -4,21 +4,144 @@
#include "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "components/offline_pages/core/client_namespace_constants.h"
-#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/prefetch/generate_page_bundle_request.h"
+#include "components/offline_pages/core/prefetch/get_operation_request.h"
+#include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_service_test_taco.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+#include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h"
+#include "components/version_info/channel.h"
+#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace offline_pages {
+namespace {
-TEST(PrefetchDispatcherTest, DispatcherDoesNotCrash) {
- PrefetchDispatcherImpl dispatcher;
+const std::string kTestNamespace = "TestPrefetchClientNamespace";
- dispatcher.AddCandidatePrefetchURLs(
- std::vector<PrefetchDispatcher::PrefetchURL>());
- dispatcher.RemoveAllUnprocessedPrefetchURLs(kSuggestedArticlesNamespace);
- dispatcher.RemovePrefetchURLsByClientId({kSuggestedArticlesNamespace, "123"});
+class TestScopedBackgroundTask
+ : public PrefetchDispatcher::ScopedBackgroundTask {
+ public:
+ TestScopedBackgroundTask() = default;
+ ~TestScopedBackgroundTask() override = default;
+
+ void SetNeedsReschedule(bool reschedule, bool backoff) override {
+ needs_reschedule_called = true;
+ }
+
+ bool needs_reschedule_called = false;
+};
+
+} // namespace
+
+class PrefetchDispatcherTest : public testing::Test {
+ public:
+ PrefetchDispatcherTest();
+
+ // Test implementation.
+ void SetUp() override;
+ void TearDown() override;
+
+ void PumpLoop();
+
+ PrefetchDispatcher::ScopedBackgroundTask* GetBackgroundTask() {
+ return dispatcher_->background_task_.get();
+ }
+
+ TaskQueue* dispatcher_task_queue() { return &dispatcher_->task_queue_; }
+ PrefetchDispatcher* prefetch_dispatcher() { return dispatcher_; }
+
+ base::TestSimpleTaskRunner* task_runner() { return task_runner_.get(); }
+
+ protected:
+ std::vector<PrefetchURL> test_urls_;
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ std::unique_ptr<PrefetchServiceTestTaco> taco_;
+
+ base::test::ScopedFeatureList feature_list_;
+
+ // Owned by |taco_|.
+ PrefetchDispatcherImpl* dispatcher_;
+};
+
+PrefetchDispatcherTest::PrefetchDispatcherTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {
+ feature_list_.InitAndEnableFeature(kPrefetchingOfflinePagesFeature);
+}
+
+void PrefetchDispatcherTest::SetUp() {
+ dispatcher_ = new PrefetchDispatcherImpl();
+ taco_.reset(new PrefetchServiceTestTaco);
+ taco_->SetPrefetchDispatcher(base::WrapUnique(dispatcher_));
+ taco_->CreatePrefetchService();
+
+ ASSERT_TRUE(test_urls_.empty());
+ test_urls_.push_back({"1", GURL("http://testurl.com/foo")});
+ test_urls_.push_back({"2", GURL("https://testurl.com/bar")});
+}
+
+void PrefetchDispatcherTest::TearDown() {
+ // Ensures that the store is properly disposed off.
+ taco_.reset();
+ PumpLoop();
+}
+
+void PrefetchDispatcherTest::PumpLoop() {
+ task_runner()->RunUntilIdle();
+}
+
+TEST_F(PrefetchDispatcherTest, DispatcherDoesNotCrash) {
+ // TODO(https://crbug.com/735254): Ensure that Dispatcher unit test keep up
+ // with the state of adding tasks, and that the end state is we have tests
+ // that verify the proper tasks were added in the proper order at each wakeup
+ // signal of the dispatcher.
+ prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
+ prefetch_dispatcher()->RemoveAllUnprocessedPrefetchURLs(
+ kSuggestedArticlesNamespace);
+ prefetch_dispatcher()->RemovePrefetchURLsByClientId(
+ {kSuggestedArticlesNamespace, "123"});
+}
+
+TEST_F(PrefetchDispatcherTest, AddCandidatePrefetchURLsTask) {
+ prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
+ EXPECT_TRUE(dispatcher_task_queue()->HasPendingTasks());
+ PumpLoop();
+ EXPECT_FALSE(dispatcher_task_queue()->HasPendingTasks());
+ EXPECT_FALSE(dispatcher_task_queue()->HasRunningTask());
+}
+
+TEST_F(PrefetchDispatcherTest, DispatcherDoesNothingIfFeatureNotEnabled) {
+ base::test::ScopedFeatureList disabled_feature_list;
+ disabled_feature_list.InitAndDisableFeature(kPrefetchingOfflinePagesFeature);
+
+ // Don't add a task for new prefetch URLs.
+ PrefetchURL prefetch_url("id", GURL("https://www.chromium.org"));
+ prefetch_dispatcher()->AddCandidatePrefetchURLs(
+ kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
+ EXPECT_FALSE(dispatcher_task_queue()->HasRunningTask());
+
+ // Do nothing with a new background task.
+ prefetch_dispatcher()->BeginBackgroundTask(
+ base::MakeUnique<TestScopedBackgroundTask>());
+ EXPECT_EQ(nullptr, GetBackgroundTask());
+
+ // Everything else is unimplemented.
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_downloader.cc b/chromium/components/offline_pages/core/prefetch/prefetch_downloader.cc
new file mode 100644
index 00000000000..8f03b678637
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_downloader.cc
@@ -0,0 +1,113 @@
+// 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/offline_pages/core/prefetch/prefetch_downloader.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "components/download/public/download_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+PrefetchDownloader::PrefetchDownloader(
+ download::DownloadService* download_service,
+ version_info::Channel channel)
+ : download_service_(download_service),
+ channel_(channel),
+ weak_ptr_factory_(this) {
+ DCHECK(download_service);
+ service_started_ = download_service->GetStatus() ==
+ download::DownloadService::ServiceStatus::READY;
+}
+
+PrefetchDownloader::PrefetchDownloader(version_info::Channel channel)
+ : channel_(channel), weak_ptr_factory_(this) {}
+
+PrefetchDownloader::~PrefetchDownloader() = default;
+
+void PrefetchDownloader::SetCompletedCallback(
+ const PrefetchDownloadCompletedCallback& callback) {
+ callback_ = callback;
+}
+
+void PrefetchDownloader::StartDownload(const std::string& download_id,
+ const std::string& download_location) {
+ if (!service_started_) {
+ pending_downloads_.push_back(
+ std::make_pair(download_id, download_location));
+ return;
+ }
+
+ // TODO(jianli): Specify scheduling parameters, i.e. battery, network and etc.
+ // http://crbug.com/736156
+ download::DownloadParams params;
+ params.client = download::DownloadClient::OFFLINE_PAGE_PREFETCH;
+ // TODO(jianli): Remove the uppercase after the download service fixes
+ // this issue.
+ params.guid = base::ToUpperASCII(download_id);
+ params.callback = base::Bind(&PrefetchDownloader::OnStartDownload,
+ weak_ptr_factory_.GetWeakPtr());
+ params.request_params.url = PrefetchDownloadURL(download_location, channel_);
+ download_service_->StartDownload(params);
+}
+
+void PrefetchDownloader::CancelDownload(const std::string& download_id) {
+ if (service_started_) {
+ download_service_->CancelDownload(download_id);
+ return;
+ }
+ for (auto iter = pending_downloads_.begin(); iter != pending_downloads_.end();
+ ++iter) {
+ if (iter->first == download_id) {
+ pending_downloads_.erase(iter);
+ return;
+ }
+ }
+ pending_cancellations_.push_back(download_id);
+}
+
+void PrefetchDownloader::OnDownloadServiceReady() {
+ DCHECK_EQ(download::DownloadService::ServiceStatus::READY,
+ download_service_->GetStatus());
+ service_started_ = true;
+
+ for (const auto& entry : pending_downloads_)
+ StartDownload(entry.first, entry.second);
+ pending_downloads_.clear();
+
+ for (const auto& entry : pending_cancellations_)
+ download_service_->CancelDownload(entry);
+ pending_cancellations_.clear();
+}
+
+void PrefetchDownloader::OnDownloadServiceShutdown() {
+ service_started_ = false;
+}
+
+void PrefetchDownloader::OnDownloadSucceeded(const std::string& download_id,
+ const base::FilePath& file_path,
+ uint64_t file_size) {
+ if (callback_)
+ callback_.Run(PrefetchDownloadResult(download_id, file_path, file_size));
+}
+
+void PrefetchDownloader::OnDownloadFailed(const std::string& download_id) {
+ if (callback_) {
+ PrefetchDownloadResult result;
+ result.download_id = download_id;
+ callback_.Run(result);
+ }
+}
+
+void PrefetchDownloader::OnStartDownload(
+ const std::string& download_id,
+ download::DownloadParams::StartResult result) {
+ if (result != download::DownloadParams::StartResult::ACCEPTED)
+ OnDownloadFailed(download_id);
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_downloader.h b/chromium/components/offline_pages/core/prefetch/prefetch_downloader.h
new file mode 100644
index 00000000000..4dd0f2496dd
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_downloader.h
@@ -0,0 +1,96 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DOWNLOADER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DOWNLOADER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/public/download_params.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/version_info/channel.h"
+
+namespace download {
+class DownloadService;
+} // namespace download
+
+namespace offline_pages {
+
+class PrefetchServiceTestTaco;
+
+// Asynchronously downloads the archive.
+class PrefetchDownloader {
+ public:
+ PrefetchDownloader(download::DownloadService* download_service,
+ version_info::Channel channel);
+ ~PrefetchDownloader();
+
+ void SetCompletedCallback(const PrefetchDownloadCompletedCallback& callback);
+
+ // Starts to download an archive from |download_location|.
+ void StartDownload(const std::string& download_id,
+ const std::string& download_location);
+
+ // Cancels a previous scheduled download.
+ void CancelDownload(const std::string& download_id);
+
+ // Responding to download client event.
+
+ // Called when the download service is initialized and can accept the
+ // downloads.
+ void OnDownloadServiceReady();
+
+ // Called when the download service is tearing down.
+ void OnDownloadServiceShutdown();
+
+ // Called when a download is completed successfully. Note that the download
+ // can be scheduled in preious sessions.
+ void OnDownloadSucceeded(const std::string& download_id,
+ const base::FilePath& file_path,
+ uint64_t file_size);
+
+ // Called when a download fails.
+ void OnDownloadFailed(const std::string& download_id);
+
+ private:
+ friend class PrefetchServiceTestTaco;
+
+ // For test only.
+ explicit PrefetchDownloader(version_info::Channel channel);
+
+ // Callback for StartDownload.
+ void OnStartDownload(const std::string& download_id,
+ download::DownloadParams::StartResult result);
+
+ // Unowned. It is valid until |this| instance is disposed.
+ download::DownloadService* download_service_;
+
+ version_info::Channel channel_;
+ PrefetchDownloadCompletedCallback callback_;
+
+ // Flag to indicate if the download service is ready to take downloads.
+ bool service_started_ = false;
+
+ // TODO(jianli): Investigate making PrefetchService waits for DownloadService
+ // ready in order to avoid queueing.
+ // List of downloads pending to start after the download service starts. Each
+ // item is a pair of download id and download location.
+ std::vector<std::pair<std::string, std::string>> pending_downloads_;
+ // List of ids of downloads waiting to be cancelled after the download service
+ // starts.
+ std::vector<std::string> pending_cancellations_;
+
+ base::WeakPtrFactory<PrefetchDownloader> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefetchDownloader);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_DOWNLOADER_H_
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc
new file mode 100644
index 00000000000..1befed2dc73
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc
@@ -0,0 +1,293 @@
+// 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/offline_pages/core/prefetch/prefetch_downloader.h"
+
+#include <list>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/download/public/download_service.h"
+#include "components/download/public/service_config.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_service_test_taco.h"
+#include "net/base/url_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
+const char kDownloadId[] = "1234";
+const char kDownloadId2[] = "ABCD";
+const char kFailedDownloadId[] = "FFFFFF";
+const char kDownloadLocation[] = "page/1";
+const char kDownloadLocation2[] = "page/zz";
+const char kServerPathForDownload[] = "/v1/media/page/1";
+const uint64_t kTestFileSize = 12345678u;
+} // namespace
+
+namespace download {
+class TestServiceConfig : public ServiceConfig {
+ public:
+ TestServiceConfig() = default;
+ ~TestServiceConfig() override = default;
+
+ uint32_t GetMaxScheduledDownloadsPerClient() const override { return 0; }
+ const base::TimeDelta& GetFileKeepAliveTime() const override {
+ return time_delta_;
+ }
+
+ private:
+ base::TimeDelta time_delta_;
+};
+
+class TestDownloadService : public DownloadService {
+ public:
+ TestDownloadService() = default;
+ ~TestDownloadService() override = default;
+
+ // DownloadService implementation.
+ const ServiceConfig& GetConfig() override { return service_config_; }
+ void OnStartScheduledTask(DownloadTaskType task_type,
+ const TaskFinishedCallback& callback) override {}
+ bool OnStopScheduledTask(DownloadTaskType task_type) override { return true; }
+ ServiceStatus GetStatus() override {
+ return ready_ ? DownloadService::ServiceStatus::READY
+ : DownloadService::ServiceStatus::STARTING_UP;
+ }
+
+ void StartDownload(const DownloadParams& download_params) override {
+ if (!ready_) {
+ OnDownloadFailed(download_params.guid);
+ return;
+ }
+ downloads_.push_back(download_params);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&TestDownloadService::ProcessDownload,
+ base::Unretained(this)));
+ }
+
+ void PauseDownload(const std::string& guid) override {}
+ void ResumeDownload(const std::string& guid) override {}
+
+ void CancelDownload(const std::string& guid) override {
+ for (auto iter = downloads_.begin(); iter != downloads_.end(); ++iter) {
+ if (iter->guid == guid) {
+ downloads_.erase(iter);
+ return;
+ }
+ }
+ }
+
+ void ChangeDownloadCriteria(const std::string& guid,
+ const SchedulingParams& params) override {}
+
+ DownloadParams GetDownload(const std::string& guid) const {
+ for (auto iter = downloads_.begin(); iter != downloads_.end(); ++iter) {
+ if (iter->guid == guid)
+ return *iter;
+ }
+ return DownloadParams();
+ }
+
+ void set_ready(bool ready) { ready_ = ready; }
+ void set_prefetch_downloader(
+ offline_pages::PrefetchDownloader* prefetch_downloader) {
+ prefetch_downloader_ = prefetch_downloader;
+ }
+
+ private:
+ void ProcessDownload() {
+ if (!ready_ || downloads_.empty())
+ return;
+ DownloadParams params = downloads_.front();
+ downloads_.pop_front();
+ if (params.guid == kFailedDownloadId)
+ OnDownloadFailed(params.guid);
+ else
+ OnDownloadSucceeded(params.guid, base::FilePath(), kTestFileSize);
+ }
+
+ void OnDownloadSucceeded(const std::string& guid,
+ const base::FilePath& file_path,
+ uint64_t file_size) {
+ if (prefetch_downloader_)
+ prefetch_downloader_->OnDownloadSucceeded(guid, file_path, file_size);
+ }
+
+ void OnDownloadFailed(const std::string& guid) {
+ if (prefetch_downloader_)
+ prefetch_downloader_->OnDownloadFailed(guid);
+ }
+
+ bool ready_ = false;
+ offline_pages::PrefetchDownloader* prefetch_downloader_ = nullptr;
+ TestServiceConfig service_config_;
+ std::list<DownloadParams> downloads_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDownloadService);
+};
+} // namespace download
+
+namespace offline_pages {
+
+class PrefetchDownloaderTest : public testing::Test {
+ public:
+ PrefetchDownloaderTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+ void SetUp() override {
+ prefetch_service_taco_.reset(new PrefetchServiceTestTaco);
+
+ auto downloader =
+ base::MakeUnique<PrefetchDownloader>(&download_service_, kTestChannel);
+ download_service_.set_prefetch_downloader(downloader.get());
+ prefetch_service_taco_->SetPrefetchDownloader(std::move(downloader));
+
+ prefetch_service_taco_->CreatePrefetchService();
+ GetPrefetchDownloader()->SetCompletedCallback(base::Bind(
+ &PrefetchDownloaderTest::OnDownloadCompleted, base::Unretained(this)));
+ }
+
+ void TearDown() override {
+ prefetch_service_taco_.reset();
+ PumpLoop();
+ }
+
+ void SetDownloadServiceReady(bool ready) {
+ download_service_.set_ready(ready);
+ if (ready)
+ GetPrefetchDownloader()->OnDownloadServiceReady();
+ else
+ GetPrefetchDownloader()->OnDownloadServiceShutdown();
+ }
+
+ void StartDownload(const std::string& download_id,
+ const std::string& download_location) {
+ GetPrefetchDownloader()->StartDownload(download_id, download_location);
+ }
+
+ void CancelDownload(const std::string& download_id) {
+ GetPrefetchDownloader()->CancelDownload(download_id);
+ }
+
+ download::DownloadParams GetDownload(const std::string& guid) const {
+ return download_service_.GetDownload(guid);
+ }
+
+ void PumpLoop() { task_runner_->RunUntilIdle(); }
+
+ const std::vector<PrefetchDownloadResult>& completed_downloads() const {
+ return completed_downloads_;
+ }
+
+ private:
+ void OnDownloadCompleted(const PrefetchDownloadResult& result) {
+ completed_downloads_.push_back(result);
+ }
+
+ PrefetchDownloader* GetPrefetchDownloader() const {
+ return prefetch_service_taco_->prefetch_service()->GetPrefetchDownloader();
+ }
+
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ download::TestDownloadService download_service_;
+ std::unique_ptr<PrefetchServiceTestTaco> prefetch_service_taco_;
+ std::vector<PrefetchDownloadResult> completed_downloads_;
+};
+
+TEST_F(PrefetchDownloaderTest, DownloadParams) {
+ SetDownloadServiceReady(true);
+ StartDownload(kDownloadId, kDownloadLocation);
+ download::DownloadParams params = GetDownload(kDownloadId);
+ EXPECT_EQ(kDownloadId, params.guid);
+ EXPECT_EQ(download::DownloadClient::OFFLINE_PAGE_PREFETCH, params.client);
+ GURL download_url = params.request_params.url;
+ EXPECT_TRUE(download_url.SchemeIs(url::kHttpsScheme));
+ EXPECT_EQ(kServerPathForDownload, download_url.path());
+ std::string key_value;
+ EXPECT_TRUE(net::GetValueForKeyInQuery(download_url, "key", &key_value));
+ EXPECT_FALSE(key_value.empty());
+ std::string alt_value;
+ EXPECT_TRUE(net::GetValueForKeyInQuery(download_url, "alt", &alt_value));
+ EXPECT_EQ("media", alt_value);
+ PumpLoop();
+}
+
+TEST_F(PrefetchDownloaderTest, StartDownloadBeforeServiceReady) {
+ SetDownloadServiceReady(false);
+ StartDownload(kDownloadId, kDownloadLocation);
+ StartDownload(kDownloadId2, kDownloadLocation2);
+ PumpLoop();
+ ASSERT_EQ(0u, completed_downloads().size());
+ SetDownloadServiceReady(true);
+ PumpLoop();
+ ASSERT_EQ(2u, completed_downloads().size());
+ EXPECT_EQ(kDownloadId, completed_downloads()[0].download_id);
+ EXPECT_TRUE(completed_downloads()[0].success);
+ EXPECT_EQ(kDownloadId2, completed_downloads()[1].download_id);
+ EXPECT_TRUE(completed_downloads()[1].success);
+}
+
+TEST_F(PrefetchDownloaderTest, StartDownloadAfterServiceReady) {
+ SetDownloadServiceReady(true);
+ StartDownload(kDownloadId, kDownloadLocation);
+ StartDownload(kDownloadId2, kDownloadLocation2);
+ PumpLoop();
+ ASSERT_EQ(2u, completed_downloads().size());
+ EXPECT_EQ(kDownloadId, completed_downloads()[0].download_id);
+ EXPECT_TRUE(completed_downloads()[0].success);
+ EXPECT_EQ(kDownloadId2, completed_downloads()[1].download_id);
+ EXPECT_TRUE(completed_downloads()[1].success);
+}
+
+TEST_F(PrefetchDownloaderTest, DownloadFailed) {
+ SetDownloadServiceReady(true);
+ StartDownload(kFailedDownloadId, kDownloadLocation);
+ PumpLoop();
+ ASSERT_EQ(1u, completed_downloads().size());
+ EXPECT_EQ(kFailedDownloadId, completed_downloads()[0].download_id);
+ EXPECT_FALSE(completed_downloads()[0].success);
+}
+
+TEST_F(PrefetchDownloaderTest, CancelPendingDownloadBeforeServiceReady) {
+ SetDownloadServiceReady(false);
+ StartDownload(kDownloadId, kDownloadLocation);
+ StartDownload(kDownloadId2, kDownloadLocation2);
+ PumpLoop();
+ ASSERT_EQ(0u, completed_downloads().size());
+ CancelDownload(kDownloadId);
+ SetDownloadServiceReady(true);
+ PumpLoop();
+ ASSERT_EQ(1u, completed_downloads().size());
+ EXPECT_EQ(kDownloadId2, completed_downloads()[0].download_id);
+ EXPECT_TRUE(completed_downloads()[0].success);
+}
+
+TEST_F(PrefetchDownloaderTest, CancelStartedDownloadBeforeServiceReady) {
+ SetDownloadServiceReady(true);
+ StartDownload(kDownloadId, kDownloadLocation);
+ SetDownloadServiceReady(false);
+ CancelDownload(kDownloadId);
+ SetDownloadServiceReady(true);
+ PumpLoop();
+ ASSERT_EQ(0u, completed_downloads().size());
+}
+
+TEST_F(PrefetchDownloaderTest, CancelDownloadAfterServiceReady) {
+ SetDownloadServiceReady(true);
+ StartDownload(kDownloadId, kDownloadLocation);
+ StartDownload(kDownloadId2, kDownloadLocation2);
+ CancelDownload(kDownloadId);
+ PumpLoop();
+ ASSERT_EQ(1u, completed_downloads().size());
+ EXPECT_EQ(kDownloadId2, completed_downloads()[0].download_id);
+ EXPECT_TRUE(completed_downloads()[0].success);
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc
new file mode 100644
index 00000000000..6f25bf510dc
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.cc
@@ -0,0 +1,87 @@
+// 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/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+
+namespace offline_pages {
+const char kPrefetchingOfflinePagesAppId[] =
+ "com.google.chrome.OfflinePagePrefetch";
+
+PrefetchGCMAppHandler::PrefetchGCMAppHandler(
+ std::unique_ptr<TokenFactory> token_factory)
+ : token_factory_(std::move(token_factory)) {}
+
+PrefetchGCMAppHandler::~PrefetchGCMAppHandler() = default;
+
+void PrefetchGCMAppHandler::SetService(PrefetchService* service) {
+ prefetch_service_ = service;
+}
+
+void PrefetchGCMAppHandler::GetGCMToken(
+ instance_id::InstanceID::GetTokenCallback callback) {
+ token_factory_->GetGCMToken(callback);
+}
+
+void PrefetchGCMAppHandler::ShutdownHandler() {
+ NOTIMPLEMENTED();
+}
+
+void PrefetchGCMAppHandler::OnStoreReset() {
+ NOTIMPLEMENTED();
+}
+
+void PrefetchGCMAppHandler::OnMessage(const std::string& app_id,
+ const gcm::IncomingMessage& message) {
+ std::string pageBundle;
+ auto iter = message.data.find("pageBundle");
+ if (iter != message.data.end()) {
+ pageBundle = iter->second;
+ } else {
+ prefetch_service_->GetLogger()->RecordActivity(
+ "GCM Message without page bundle received!");
+ return;
+ }
+
+ prefetch_service_->GetPrefetchDispatcher()
+ ->GCMOperationCompletedMessageReceived(pageBundle);
+ prefetch_service_->GetLogger()->RecordActivity(
+ "Received GCM message. App ID: " + app_id +
+ "; pageBundle data: " + pageBundle);
+}
+
+void PrefetchGCMAppHandler::OnMessagesDeleted(const std::string& app_id) {
+ NOTIMPLEMENTED();
+}
+
+void PrefetchGCMAppHandler::OnSendError(
+ const std::string& app_id,
+ const gcm::GCMClient::SendErrorDetails& send_error_details) {
+ NOTIMPLEMENTED();
+}
+
+void PrefetchGCMAppHandler::OnSendAcknowledged(const std::string& app_id,
+ const std::string& message_id) {
+ NOTIMPLEMENTED();
+}
+
+bool PrefetchGCMAppHandler::CanHandle(const std::string& app_id) const {
+ return app_id == GetAppId();
+}
+
+gcm::GCMAppHandler* PrefetchGCMAppHandler::AsGCMAppHandler() {
+ return (gcm::GCMAppHandler*)this;
+}
+
+std::string PrefetchGCMAppHandler::GetAppId() const {
+ return kPrefetchingOfflinePagesAppId;
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h
new file mode 100644
index 00000000000..3b5ca1f2e4f
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h
@@ -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.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_GCM_APP_HANDLER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_GCM_APP_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
+
+namespace offline_pages {
+class PrefetchService;
+
+extern const char kPrefetchingOfflinePagesAppId[];
+
+// Receives GCM messages and other channel status messages on behalf of the
+// prefetch system.
+class PrefetchGCMAppHandler : public gcm::GCMAppHandler,
+ public PrefetchGCMHandler {
+ public:
+ class TokenFactory {
+ public:
+ virtual ~TokenFactory() = default;
+
+ virtual void GetGCMToken(
+ instance_id::InstanceID::GetTokenCallback callback) = 0;
+ };
+
+ explicit PrefetchGCMAppHandler(std::unique_ptr<TokenFactory> token_factory);
+ ~PrefetchGCMAppHandler() override;
+
+ // gcm::GCMAppHandler implementation.
+ void ShutdownHandler() override;
+ void OnStoreReset() override;
+ void OnMessage(const std::string& app_id,
+ const gcm::IncomingMessage& message) override;
+ void OnMessagesDeleted(const std::string& app_id) override;
+ void OnSendError(
+ const std::string& app_id,
+ const gcm::GCMClient::SendErrorDetails& send_error_details) override;
+ void OnSendAcknowledged(const std::string& app_id,
+ const std::string& message_id) override;
+ bool CanHandle(const std::string& app_id) const override;
+
+ // offline_pages::PrefetchGCMHandler implementation.
+ void SetService(PrefetchService* service) override;
+ gcm::GCMAppHandler* AsGCMAppHandler() override;
+ std::string GetAppId() const override;
+ void GetGCMToken(instance_id::InstanceID::GetTokenCallback callback) override;
+
+ private:
+ // Not owned, PrefetchService owns |this.
+ PrefetchService* prefetch_service_;
+ std::unique_ptr<TokenFactory> token_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefetchGCMAppHandler);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_GCM_APP_HANDLER_H_
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
new file mode 100644
index 00000000000..fe969a87d52
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
@@ -0,0 +1,114 @@
+// 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/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/offline_pages/core/prefetch/prefetch_service_test_taco.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+class TestTokenFactory : public PrefetchGCMAppHandler::TokenFactory {
+ public:
+ TestTokenFactory() = default;
+ ~TestTokenFactory() override = default;
+
+ void GetGCMToken(
+ instance_id::InstanceID::GetTokenCallback callback) override {
+ callback.Run(token, result);
+ }
+
+ instance_id::InstanceID::Result result = instance_id::InstanceID::SUCCESS;
+ std::string token = "default_token";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestTokenFactory);
+};
+
+class PrefetchGCMAppHandlerTest : public testing::Test {
+ public:
+ PrefetchGCMAppHandlerTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+ void SetUp() override {
+ auto dispatcher = base::MakeUnique<TestPrefetchDispatcher>();
+ test_dispatcher_ = dispatcher.get();
+
+ auto token_factory = base::MakeUnique<TestTokenFactory>();
+ token_factory_ = token_factory.get();
+
+ auto gcm_app_handler =
+ base::MakeUnique<PrefetchGCMAppHandler>(std::move(token_factory));
+ handler_ = gcm_app_handler.get();
+
+ prefetch_service_taco_.reset(new PrefetchServiceTestTaco);
+ prefetch_service_taco_->SetPrefetchGCMHandler(std::move(gcm_app_handler));
+ prefetch_service_taco_->SetPrefetchDispatcher(std::move(dispatcher));
+ prefetch_service_taco_->CreatePrefetchService();
+ }
+
+ void TearDown() override {
+ // Ensures that the store is properly disposed off.
+ prefetch_service_taco_.reset();
+ task_runner_->RunUntilIdle();
+ }
+
+ TestPrefetchDispatcher* dispatcher() { return test_dispatcher_; }
+ PrefetchGCMAppHandler* handler() { return handler_; }
+ TestTokenFactory* token_factory() { return token_factory_; }
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ std::unique_ptr<PrefetchServiceTestTaco> prefetch_service_taco_;
+
+ // Owned by the taco.
+ TestPrefetchDispatcher* test_dispatcher_;
+ // Owned by the taco.
+ PrefetchGCMAppHandler* handler_;
+ // Owned by the PrefetchGCMAppHandler.
+ TestTokenFactory* token_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefetchGCMAppHandlerTest);
+};
+
+TEST_F(PrefetchGCMAppHandlerTest, TestOnMessage) {
+ gcm::IncomingMessage message;
+ const char kMessage[] = "123";
+ message.data["pageBundle"] = kMessage;
+ handler()->OnMessage("An App ID", message);
+
+ EXPECT_EQ(1U, dispatcher()->operation_list.size());
+ EXPECT_EQ(kMessage, dispatcher()->operation_list[0]);
+}
+
+TEST_F(PrefetchGCMAppHandlerTest, TestInvalidMessage) {
+ gcm::IncomingMessage message;
+ const char kMessage[] = "123";
+ message.data["whatAMess"] = kMessage;
+ handler()->OnMessage("An App ID", message);
+
+ EXPECT_EQ(0U, dispatcher()->operation_list.size());
+}
+
+TEST_F(PrefetchGCMAppHandlerTest, TestGetToken) {
+ std::string result_token;
+
+ handler()->GetGCMToken(base::Bind(
+ [](std::string* result_token, const std::string& token,
+ instance_id::InstanceID::Result result) { *result_token = token; },
+ &result_token));
+ EXPECT_EQ(token_factory()->token, result_token);
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_gcm_handler.h b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_handler.h
new file mode 100644
index 00000000000..a3f529ba0cc
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_handler.h
@@ -0,0 +1,46 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_GCM_HANDLER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_GCM_HANDLER_H_
+
+#include <string>
+
+#include "components/gcm_driver/instance_id/instance_id.h"
+
+namespace gcm {
+class GCMAppHandler;
+} // namespace gcm
+
+namespace offline_pages {
+
+class PrefetchGCMHandler;
+class PrefetchService;
+
+// Main class and entry point for the Offline Pages Prefetching feature, that
+// controls the lifetime of all major subcomponents of the prefetching system.
+class PrefetchGCMHandler {
+ public:
+ PrefetchGCMHandler() = default;
+ virtual ~PrefetchGCMHandler() = default;
+
+ // Sets the prefetch service. Must be called before using the handler.
+ virtual void SetService(PrefetchService* service) = 0;
+
+ // Returns the GCMAppHandler for this object. Can return |nullptr| in unit
+ // tests.
+ virtual gcm::GCMAppHandler* AsGCMAppHandler() = 0;
+
+ // The app ID to register with at the GCM layer.
+ virtual std::string GetAppId() const = 0;
+
+ // Gets a token suitable for sending to Offline Page Service for notifications
+ // when work is completed.
+ virtual void GetGCMToken(
+ instance_id::InstanceID::GetTokenCallback callback) = 0;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_GCM_HANDLER_H_
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_importer.h b/chromium/components/offline_pages/core/prefetch/prefetch_importer.h
new file mode 100644
index 00000000000..1ef3edf6117
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_importer.h
@@ -0,0 +1,42 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_IMPORTER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_IMPORTER_H_
+
+#include <string>
+
+class GURL;
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace offline_pages {
+
+struct ClientId;
+
+// Interface to import the downloaded archive such that it can be rendered as
+// offline page.
+class PrefetchImporter {
+ public:
+ using CompletedCallback =
+ base::Callback<void(bool success, int64_t offline_id)>;
+
+ virtual ~PrefetchImporter() = default;
+
+ // Imports the downloaded archive by moving the file into archive directory
+ // and creating an entry in the offline metadata database.
+ virtual void ImportFile(const GURL& url,
+ const GURL& original_url,
+ const base::string16& title,
+ int64_t offline_id,
+ const ClientId& client_id,
+ const base::FilePath& file_path,
+ int64_t file_size,
+ const CompletedCallback& callback) = 0;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_IMPORTER_H_
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_item.cc b/chromium/components/offline_pages/core/prefetch/prefetch_item.cc
index 8e1119d4f69..375bf091d7d 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_item.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_item.cc
@@ -13,7 +13,7 @@ PrefetchItem::PrefetchItem(const PrefetchItem& other) = default;
PrefetchItem::~PrefetchItem(){};
bool PrefetchItem::operator==(const PrefetchItem& other) const {
- return guid == other.guid && client_name_space == other.client_name_space &&
+ return offline_id == other.offline_id && guid == other.guid &&
client_id == other.client_id && state == other.state &&
url == other.url && final_archived_url == other.final_archived_url &&
request_archive_attempt_count == other.request_archive_attempt_count &&
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_item.h b/chromium/components/offline_pages/core/prefetch/prefetch_item.h
index e648ba9290a..ad22a92398c 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_item.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_item.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/time/time.h"
+#include "components/offline_pages/core/client_id.h"
#include "components/offline_pages/core/prefetch/prefetch_types.h"
#include "url/gurl.h"
@@ -27,17 +28,18 @@ struct PrefetchItem {
bool operator==(const PrefetchItem& other) const;
bool operator!=(const PrefetchItem& other) const;
+ // Primary key that stays consistent between prefetch item, request and
+ // offline page.
+ int64_t offline_id = 0;
+
// Primary key/ID for this prefetch item (See |base::GenerateGUID()|). This
// value will be reused when communicating with other systems accepting GUID
// identifiers for operations linked to this item.
std::string guid;
- // Externally provided namespace and id values so that this item can be
- // uniquely identified by the client requesting it. It is the client's
- // responsibility to make sure the id is unique within the context of its
- // assigned namespace.
- std::string client_name_space;
- std::string client_id;
+ // Data composed of a namespace and an uid values to allow this item to be
+ // uniquely identified by the client that requested it.
+ ClientId client_id;
// Current prefetching progress state.
PrefetchItemState state = PrefetchItemState::NEW_REQUEST;
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_item_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_item_unittest.cc
index 68433d477dc..3778281b23a 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_item_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_item_unittest.cc
@@ -16,17 +16,17 @@ TEST(PrefetchItemTest, OperatorEqualsAndCopyConstructor) {
EXPECT_EQ(item1, PrefetchItem());
EXPECT_EQ(item1, PrefetchItem(item1));
- item1.guid = "A";
+ item1.offline_id = 77L;
EXPECT_NE(item1, PrefetchItem());
EXPECT_EQ(item1, PrefetchItem(item1));
item1 = PrefetchItem();
- item1.client_name_space = "B";
+ item1.guid = "A";
EXPECT_NE(item1, PrefetchItem());
EXPECT_EQ(item1, PrefetchItem(item1));
item1 = PrefetchItem();
- item1.client_id = "C";
+ item1.client_id = ClientId("B", "C");
EXPECT_NE(item1, PrefetchItem());
EXPECT_EQ(item1, PrefetchItem(item1));
item1 = PrefetchItem();
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
new file mode 100644
index 00000000000..de785448de5
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory.h
@@ -0,0 +1,47 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_NETWORK_REQUEST_FACTORY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_NETWORK_REQUEST_FACTORY_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+
+namespace offline_pages {
+class GeneratePageBundleRequest;
+class GetOperationRequest;
+
+class PrefetchNetworkRequestFactory {
+ public:
+ virtual ~PrefetchNetworkRequestFactory() = default;
+
+ // Creates and starts a new GeneratePageBundle request, retaining ownership.
+ // If a GeneratePageBundle request already exists, this will cancel the
+ // existing request and start a new one.
+ virtual void MakeGeneratePageBundleRequest(
+ const std::vector<std::string>& prefetch_urls,
+ const std::string& gcm_registration_id,
+ const PrefetchRequestFinishedCallback& callback) = 0;
+ // Gets the current GeneratePageBundleRequest. After |callback| is executed,
+ // this will return |nullptr|.
+ virtual GeneratePageBundleRequest* CurrentGeneratePageBundleRequest()
+ const = 0;
+
+ // Creates and starts a new GetOperationRequest, retaining ownership.
+ // If a GetOperation request already exists with the same operation name, this
+ // will cancel the existing request and start a new one.
+ virtual void MakeGetOperationRequest(
+ const std::string& operation_name,
+ const PrefetchRequestFinishedCallback& callback) = 0;
+ // Returns the current GetOperationRequest with the given name, if any.
+ virtual GetOperationRequest* FindGetOperationRequestByName(
+ const std::string& operation_name) const = 0;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_NETWORK_REQUEST_FACTORY_H_
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
new file mode 100644
index 00000000000..ddb9e4224f9
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc
@@ -0,0 +1,84 @@
+// 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/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/prefetch/generate_page_bundle_request.h"
+#include "components/offline_pages/core/prefetch/get_operation_request.h"
+
+constexpr int kMaxBundleSizeBytes = 20 * 1024 * 1024; // 20 MB
+
+namespace offline_pages {
+
+PrefetchNetworkRequestFactoryImpl::PrefetchNetworkRequestFactoryImpl(
+ net::URLRequestContextGetter* request_context,
+ version_info::Channel channel,
+ const std::string& user_agent)
+ : request_context_(request_context),
+ channel_(channel),
+ user_agent_(user_agent),
+ weak_factory_(this) {}
+
+PrefetchNetworkRequestFactoryImpl::~PrefetchNetworkRequestFactoryImpl() =
+ default;
+
+void PrefetchNetworkRequestFactoryImpl::MakeGeneratePageBundleRequest(
+ const std::vector<std::string>& url_strings,
+ const std::string& gcm_registration_id,
+ const PrefetchRequestFinishedCallback& callback) {
+ generate_page_bundle_request_ = base::MakeUnique<GeneratePageBundleRequest>(
+ user_agent_, gcm_registration_id, kMaxBundleSizeBytes, url_strings,
+ channel_, request_context_.get(),
+ base::Bind(
+ &PrefetchNetworkRequestFactoryImpl::GeneratePageBundleRequestDone,
+ weak_factory_.GetWeakPtr(), callback));
+}
+
+GeneratePageBundleRequest*
+PrefetchNetworkRequestFactoryImpl::CurrentGeneratePageBundleRequest() const {
+ return generate_page_bundle_request_.get();
+}
+
+void PrefetchNetworkRequestFactoryImpl::MakeGetOperationRequest(
+ const std::string& operation_name,
+ const PrefetchRequestFinishedCallback& callback) {
+ get_operation_requests_[operation_name] =
+ base::MakeUnique<GetOperationRequest>(
+ operation_name, channel_, request_context_.get(),
+ base::Bind(
+ &PrefetchNetworkRequestFactoryImpl::GetOperationRequestDone,
+ weak_factory_.GetWeakPtr(), callback));
+}
+
+void PrefetchNetworkRequestFactoryImpl::GeneratePageBundleRequestDone(
+ const PrefetchRequestFinishedCallback& callback,
+ PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages) {
+ callback.Run(status, operation_name, pages);
+ generate_page_bundle_request_.reset();
+}
+
+void PrefetchNetworkRequestFactoryImpl::GetOperationRequestDone(
+ const PrefetchRequestFinishedCallback& callback,
+ PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages) {
+ callback.Run(status, operation_name, pages);
+ get_operation_requests_.erase(operation_name);
+}
+
+GetOperationRequest*
+PrefetchNetworkRequestFactoryImpl::FindGetOperationRequestByName(
+ const std::string& operation_name) const {
+ auto iter = get_operation_requests_.find(operation_name);
+ if (iter == get_operation_requests_.end())
+ return nullptr;
+
+ return iter->second.get();
+}
+
+} // namespace offline_pages
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
new file mode 100644
index 00000000000..57f1d149f47
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h
@@ -0,0 +1,68 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_NETWORK_REQUEST_FACTORY_IMPL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_NETWORK_REQUEST_FACTORY_IMPL_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
+#include "components/version_info/channel.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace offline_pages {
+class GeneratePageBundleRequest;
+class GetOperationRequest;
+
+class PrefetchNetworkRequestFactoryImpl : public PrefetchNetworkRequestFactory {
+ public:
+ PrefetchNetworkRequestFactoryImpl(
+ net::URLRequestContextGetter* request_context,
+ version_info::Channel channel,
+ const std::string& user_agent);
+
+ ~PrefetchNetworkRequestFactoryImpl() override;
+
+ void MakeGeneratePageBundleRequest(
+ const std::vector<std::string>& prefetch_urls,
+ const std::string& gcm_registration_id,
+ const PrefetchRequestFinishedCallback& callback) override;
+ GeneratePageBundleRequest* CurrentGeneratePageBundleRequest() const override;
+
+ void MakeGetOperationRequest(
+ const std::string& operation_name,
+ const PrefetchRequestFinishedCallback& callback) override;
+ GetOperationRequest* FindGetOperationRequestByName(
+ const std::string& operation_name) const override;
+
+ private:
+ void GeneratePageBundleRequestDone(
+ const PrefetchRequestFinishedCallback& callback,
+ PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages);
+ void GetOperationRequestDone(const PrefetchRequestFinishedCallback& callback,
+ PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages);
+ scoped_refptr<net::URLRequestContextGetter> request_context_;
+ version_info::Channel channel_;
+ std::string user_agent_;
+
+ std::unique_ptr<GeneratePageBundleRequest> generate_page_bundle_request_;
+ std::map<std::string, std::unique_ptr<GetOperationRequest>>
+ get_operation_requests_;
+
+ base::WeakPtrFactory<PrefetchNetworkRequestFactoryImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefetchNetworkRequestFactoryImpl);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_NETWORK_REQUEST_FACTORY_IMPL_H_
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
new file mode 100644
index 00000000000..8a91b7632e5
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl_unittest.cc
@@ -0,0 +1,104 @@
+// 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/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/generate_page_bundle_request.h"
+#include "components/offline_pages/core/prefetch/get_operation_request.h"
+#include "components/version_info/channel.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+class PrefetchNetworkRequestFactoryTest : public testing::Test {
+ public:
+ PrefetchNetworkRequestFactoryTest();
+
+ PrefetchNetworkRequestFactoryImpl* request_factory() {
+ return request_factory_.get();
+ }
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ net::TestURLFetcherFactory url_fetcher_factory_;
+ std::unique_ptr<PrefetchNetworkRequestFactoryImpl> request_factory_;
+};
+
+PrefetchNetworkRequestFactoryTest::PrefetchNetworkRequestFactoryTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_),
+ request_context_(new net::TestURLRequestContextGetter(task_runner_)) {
+ request_factory_ = base::MakeUnique<PrefetchNetworkRequestFactoryImpl>(
+ request_context_.get(), version_info::Channel::UNKNOWN, "a user agent");
+}
+
+TEST_F(PrefetchNetworkRequestFactoryTest, TestMakeGetOperationRequest) {
+ // Query whether there is an operation to start with, before we make a
+ // request.
+ std::string operation_name = "an operation";
+ GetOperationRequest* request =
+ request_factory()->FindGetOperationRequestByName(operation_name);
+ EXPECT_EQ(nullptr, request);
+
+ // Then, make the request and ensure we can find it by name.
+ request_factory()->MakeGetOperationRequest(operation_name,
+ PrefetchRequestFinishedCallback());
+ request = request_factory()->FindGetOperationRequestByName(operation_name);
+ EXPECT_NE(nullptr, request);
+
+ // Then check that a request is not found for another name (which was not
+ // requested).
+ std::string operation_name_2 = "another operation";
+ GetOperationRequest* request_2 =
+ request_factory()->FindGetOperationRequestByName(operation_name_2);
+ EXPECT_EQ(nullptr, request_2);
+
+ // Then make the second request.
+ request_factory()->MakeGetOperationRequest(operation_name_2,
+ PrefetchRequestFinishedCallback());
+
+ // Query for the second request, ensure it is different than the first
+ // request, and ensure it didn't change the first request.
+ request_2 =
+ request_factory()->FindGetOperationRequestByName(operation_name_2);
+ EXPECT_NE(nullptr, request_2);
+ EXPECT_EQ(request,
+ request_factory()->FindGetOperationRequestByName(operation_name));
+ EXPECT_NE(request, request_2);
+
+ // Then overwrite the first request with a new one, and make sure it's
+ // different.
+ request_factory()->MakeGetOperationRequest(operation_name,
+ PrefetchRequestFinishedCallback());
+ EXPECT_NE(request,
+ request_factory()->FindGetOperationRequestByName(operation_name));
+}
+
+TEST_F(PrefetchNetworkRequestFactoryTest, TestMakeGeneratePageBundleRequest) {
+ std::vector<std::string> urls = {"example.com/1", "example.com/2"};
+ std::string reg_id = "a registration id";
+
+ GeneratePageBundleRequest* request =
+ request_factory()->CurrentGeneratePageBundleRequest();
+ EXPECT_EQ(nullptr, request);
+
+ request_factory()->MakeGeneratePageBundleRequest(
+ urls, reg_id, PrefetchRequestFinishedCallback());
+ request = request_factory()->CurrentGeneratePageBundleRequest();
+ EXPECT_NE(nullptr, request);
+
+ urls = {"example.com/3"};
+ request_factory()->MakeGeneratePageBundleRequest(
+ urls, reg_id, PrefetchRequestFinishedCallback());
+ EXPECT_NE(request, request_factory()->CurrentGeneratePageBundleRequest());
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.cc b/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.cc
index 0fe504049bc..0fcdfb7beb2 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.cc
@@ -123,18 +123,19 @@ bool ParsePendingOperationResponse(const proto::Operation& operation,
} // namespace
-bool ParseOperationResponse(const std::string& data,
- std::vector<RenderPageInfo>* pages) {
+std::string ParseOperationResponse(const std::string& data,
+ std::vector<RenderPageInfo>* pages) {
proto::Operation operation;
if (!operation.ParseFromString(data)) {
DVLOG(1) << "Failed to parse operation";
- return false;
+ return std::string();
}
- if (operation.done())
- return ParseDoneOperationResponse(operation, pages);
- else
- return ParsePendingOperationResponse(operation, pages);
+ std::string name = operation.name();
+ bool success = operation.done()
+ ? ParseDoneOperationResponse(operation, pages)
+ : ParsePendingOperationResponse(operation, pages);
+ return success ? name : std::string();
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.h b/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.h
index cc77de109bd..92813af8e2e 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_proto_utils.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_PROTO_UTILS_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_PROTO_UTILS_H_
+#include <string>
#include <vector>
#include "components/offline_pages/core/prefetch/prefetch_types.h"
@@ -13,9 +14,10 @@ namespace offline_pages {
// The fully qualified type name for PageBundle defined in proto.
extern const char kPageBundleTypeURL[];
-// Used to parse the Operation serialized in binary proto |data|.
-bool ParseOperationResponse(const std::string& data,
- std::vector<RenderPageInfo>* pages);
+// Parse the Operation serialized in binary proto |data|. Returns the operation
+// name if parsing succeeded. Otherwise, empty string is returned.
+std::string ParseOperationResponse(const std::string& data,
+ std::vector<RenderPageInfo>* pages);
} // namespace offline_pages
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 1974a1bcc32..d533ab1b0a7 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
@@ -6,7 +6,10 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
+#include "google_apis/google_api_keys.h"
#include "net/base/load_flags.h"
+#include "net/base/url_util.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
@@ -19,42 +22,33 @@ namespace offline_pages {
namespace {
-const char kPrefetchServer[] =
- "http://staging-offlinepages-pa.sandbox.googleapis.com/";
-
// Content type needed in order to communicate with the server in binary
// proto format.
const char kRequestContentType[] = "application/x-protobuf";
-GURL CompleteURL(const std::string& url_path) {
- GURL::Replacements replacements;
- replacements.SetPathStr(url_path);
- return GURL(kPrefetchServer).ReplaceComponents(replacements);
-}
-
} // namespace
// static
std::unique_ptr<PrefetchRequestFetcher> PrefetchRequestFetcher::CreateForGet(
- const std::string& url_path,
+ const GURL& url,
net::URLRequestContextGetter* request_context_getter,
const FinishedCallback& callback) {
return base::WrapUnique(new PrefetchRequestFetcher(
- url_path, std::string(), request_context_getter, callback));
+ url, std::string(), request_context_getter, callback));
}
// static
std::unique_ptr<PrefetchRequestFetcher> PrefetchRequestFetcher::CreateForPost(
- const std::string& url_path,
+ const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
const FinishedCallback& callback) {
return base::WrapUnique(new PrefetchRequestFetcher(
- url_path, message, request_context_getter, callback));
+ url, message, request_context_getter, callback));
}
PrefetchRequestFetcher::PrefetchRequestFetcher(
- const std::string& url_path,
+ const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
const FinishedCallback& callback)
@@ -81,8 +75,7 @@ PrefetchRequestFetcher::PrefetchRequestFetcher(
"Not implemented, considered not useful."
})");
url_fetcher_ = net::URLFetcher::Create(
- CompleteURL(url_path),
- message.empty() ? net::URLFetcher::GET : net::URLFetcher::POST, this,
+ url, message.empty() ? net::URLFetcher::GET : net::URLFetcher::POST, this,
traffic_annotation);
url_fetcher_->SetRequestContext(request_context_getter_.get());
url_fetcher_->SetAutomaticallyRetryOn5xx(false);
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 c24515ecf94..4ac86405b6e 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
@@ -10,6 +10,7 @@
#include "base/memory/ref_counted.h"
#include "components/offline_pages/core/prefetch/prefetch_types.h"
#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
namespace net {
class URLRequestContextGetter;
@@ -25,13 +26,13 @@ class PrefetchRequestFetcher : public net::URLFetcherDelegate {
// Creates a fetcher that will sends a GET request to the server.
static std::unique_ptr<PrefetchRequestFetcher> CreateForGet(
- const std::string& url_path,
+ const GURL& url,
net::URLRequestContextGetter* request_context_getter,
const FinishedCallback& callback);
// Creates a fetcher that will sends a POST request to the server.
static std::unique_ptr<PrefetchRequestFetcher> CreateForPost(
- const std::string& url_path,
+ const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
const FinishedCallback& callback);
@@ -44,7 +45,7 @@ class PrefetchRequestFetcher : public net::URLFetcherDelegate {
private:
// If |message| is empty, the GET request is sent. Otherwise, the POST request
// is sent with |message| as post data.
- PrefetchRequestFetcher(const std::string& url_path,
+ PrefetchRequestFetcher(const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
const FinishedCallback& callback);
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 3dbed4f1221..39b5d2183d3 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
@@ -20,7 +20,7 @@ using testing::SaveArg;
namespace offline_pages {
namespace {
-const char kTestURLPath[] = "/test";
+const GURL kTestURL("http://exmaple.org");
const char kTestMessage[] = "Testing";
} // namespace
@@ -72,7 +72,7 @@ PrefetchRequestStatus PrefetchRequestFetcherTest::RunFetcher(
std::string* data_received) {
base::MockCallback<PrefetchRequestFetcher::FinishedCallback> callback;
std::unique_ptr<PrefetchRequestFetcher> fetcher =
- PrefetchRequestFetcher::CreateForPost(kTestURLPath, kTestMessage,
+ PrefetchRequestFetcher::CreateForPost(kTestURL, kTestMessage,
request_context(), callback.Get());
PrefetchRequestStatus 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 22d676d84fc..3df538cf09a 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
@@ -23,6 +23,8 @@ using testing::SaveArg;
namespace offline_pages {
namespace {
+const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
+const char kTestOperationName[] = "operation/test123";
const char kTestURL[] = "http://example.com";
const char kTestURL2[] = "http://example.com/2";
const char kTestURL3[] = "http://example.com/3";
@@ -53,7 +55,7 @@ class GeneratePageBundleRequestBuilder : public RequestBuilder {
const PrefetchRequestFinishedCallback& callback) override {
std::vector<std::string> pages = {kTestURL, kTestURL2};
fetcher_.reset(new GeneratePageBundleRequest(
- kTestUserAgent, kTestGCMID, kTestMaxBundleSize, pages,
+ kTestUserAgent, kTestGCMID, kTestMaxBundleSize, pages, kTestChannel,
request_context_getter, callback));
}
@@ -65,7 +67,7 @@ class GetOperationRequestBuilder : public RequestBuilder {
public:
void CreateRequest(net::URLRequestContextGetter* request_context_getter,
const PrefetchRequestFinishedCallback& callback) override {
- fetcher_.reset(new GetOperationRequest(kTestMethodName,
+ fetcher_.reset(new GetOperationRequest(kTestMethodName, kTestChannel,
request_context_getter, callback));
}
@@ -100,6 +102,7 @@ class OperationBuilder {
const std::string& any_type_url,
const std::string& any_value) {
proto::Operation operation;
+ operation.set_name(kTestOperationName);
operation.set_done(is_done);
if (error_code != proto::OK) {
operation.mutable_error()->set_code(error_code);
@@ -233,6 +236,7 @@ class PrefetchRequestOperationResponseTest : public PrefetchRequestTestBase {
builder_.BuildFromAny(kPageBundleTypeURL, bundle_data));
}
+ const std::string& operation_name() const { return operation_name_; }
const std::vector<RenderPageInfo>& pages() const { return pages_; }
private:
@@ -241,14 +245,17 @@ class PrefetchRequestOperationResponseTest : public PrefetchRequestTestBase {
builder_.CreateRequest(request_context(), callback.Get());
PrefetchRequestStatus status;
+ operation_name_.clear();
pages_.clear();
- EXPECT_CALL(callback, Run(_, _))
- .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages_)));
+ EXPECT_CALL(callback, Run(_, _, _))
+ .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name_),
+ SaveArg<2>(&pages_)));
RespondWithData(response_data);
return status;
}
T builder_;
+ std::string operation_name_;
std::vector<RenderPageInfo> pages_;
};
@@ -265,24 +272,28 @@ TYPED_TEST(PrefetchRequestOperationResponseTest, EmptyOperation) {
// No error is set for OK. Thus this will cause the operation
// being filled with only done flag.
this->SendWithErrorResponse(proto::OK, ""));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
TYPED_TEST(PrefetchRequestOperationResponseTest, ErrorValue) {
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
this->SendWithErrorResponse(proto::UNKNOWN, kErrorMessage));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
TYPED_TEST(PrefetchRequestOperationResponseTest, InvalidTypeUrl) {
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
this->SendWithAnyResponse("foo", ""));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
TYPED_TEST(PrefetchRequestOperationResponseTest, InvalidValue) {
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
this->SendWithAnyResponse(kPageBundleTypeURL, "foo"));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
@@ -290,6 +301,7 @@ TYPED_TEST(PrefetchRequestOperationResponseTest, EmptyPageBundle) {
proto::PageBundle bundle;
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
@@ -298,6 +310,7 @@ TYPED_TEST(PrefetchRequestOperationResponseTest, EmptyArchive) {
bundle.add_archives();
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
@@ -308,6 +321,7 @@ TYPED_TEST(PrefetchRequestOperationResponseTest, NoPageInfo) {
archive->set_body_length(kTestBodyLength);
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
@@ -318,6 +332,7 @@ TYPED_TEST(PrefetchRequestOperationResponseTest, MissingPageInfoUrl) {
page_info->set_redirect_url(kTestURL);
EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->operation_name().empty());
EXPECT_TRUE(this->pages().empty());
}
@@ -337,6 +352,7 @@ TYPED_TEST(PrefetchRequestOperationResponseTest, SinglePage) {
1000000);
EXPECT_EQ(PrefetchRequestStatus::SUCCESS,
this->SendWithPageBundleResponse(bundle));
+ EXPECT_EQ(kTestOperationName, this->operation_name());
ASSERT_EQ(1u, this->pages().size());
EXPECT_EQ(kTestURL, this->pages().back().url);
EXPECT_EQ(kTestURL2, this->pages().back().redirect_url);
@@ -382,6 +398,7 @@ TYPED_TEST(PrefetchRequestOperationResponseTest, MultiplePages) {
EXPECT_EQ(PrefetchRequestStatus::SUCCESS,
this->SendWithPageBundleResponse(bundle));
+ EXPECT_EQ(kTestOperationName, this->operation_name());
ASSERT_EQ(4u, this->pages().size());
EXPECT_EQ(kTestURL, this->pages().at(0).url);
EXPECT_EQ(RenderStatus::PENDING, this->pages().at(0).status);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc
new file mode 100644
index 00000000000..fc430b1d0f1
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc
@@ -0,0 +1,77 @@
+// 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/offline_pages/core/prefetch/prefetch_server_urls.h"
+
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/variations/variations_associated_data.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/url_util.h"
+
+namespace offline_pages {
+
+const char kPrefetchServer[] = "https://offlinepages-pa.googleapis.com/";
+
+namespace {
+
+const char kOfflinePagesBackend[] = "offline_pages_backend";
+const char kGeneratePageBundleRequestURLPath[] = "v1:GeneratePageBundle";
+const char kGetOperationLeadingURLPath[] = "v1/";
+const char kDownloadLeadingURLPath[] = "v1/media/";
+
+// Used in all offline prefetch request URLs to specify API Key.
+const char kApiKeyName[] = "key";
+// Needed to download as a file.
+const char kAltKeyName[] = "alt";
+const char kAltKeyValueForDownload[] = "media";
+
+GURL GetServerURL() {
+ GURL endpoint(variations::GetVariationParamValueByFeature(
+ 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);
+}
+
+GURL GetServerURLForPath(const std::string& url_path) {
+ GURL::Replacements replacements;
+ replacements.SetPathStr(url_path);
+ return GetServerURL().ReplaceComponents(replacements);
+}
+
+GURL AppendApiKeyToURL(const GURL& url, version_info::Channel channel) {
+ bool is_stable_channel = channel == version_info::Channel::STABLE;
+ std::string api_key = is_stable_channel ? google_apis::GetAPIKey()
+ : google_apis::GetNonStableAPIKey();
+ return net::AppendQueryParameter(url, kApiKeyName, api_key);
+}
+
+} // namespace
+
+GURL GeneratePageBundleRequestURL(version_info::Channel channel) {
+ GURL server_url = GetServerURLForPath(kGeneratePageBundleRequestURLPath);
+ return AppendApiKeyToURL(server_url, channel);
+}
+
+GURL GetOperationRequestURL(const std::string& name,
+ version_info::Channel channel) {
+ std::string url_path = kGetOperationLeadingURLPath + name;
+ GURL server_url = GetServerURLForPath(url_path);
+ return AppendApiKeyToURL(server_url, channel);
+}
+
+GURL PrefetchDownloadURL(const std::string& download_location,
+ version_info::Channel channel) {
+ std::string url_path = kDownloadLeadingURLPath + download_location;
+ GURL server_url = GetServerURLForPath(url_path);
+
+ server_url = net::AppendQueryParameter(server_url, kAltKeyName,
+ kAltKeyValueForDownload);
+
+ return AppendApiKeyToURL(server_url, channel);
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h
new file mode 100644
index 00000000000..bf83ac6c81a
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h
@@ -0,0 +1,29 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVER_URLS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVER_URLS_H_
+
+#include <string>
+#include "components/version_info/channel.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+extern const char kPrefetchServer[];
+
+// Returns the URL to send a request to generate page bundle.
+GURL GeneratePageBundleRequestURL(version_info::Channel channel);
+
+// Returns the URL to send a request to get operation info.
+GURL GetOperationRequestURL(const std::string& name,
+ version_info::Channel channel);
+
+// Returns the URL to download an archive.
+GURL PrefetchDownloadURL(const std::string& download_location,
+ version_info::Channel channel);
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVER_URLS_H_
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
new file mode 100644
index 00000000000..02fc66b9bfe
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls_unittest.cc
@@ -0,0 +1,67 @@
+// 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/offline_pages/core/prefetch/prefetch_server_urls.h"
+
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/variations/variations_params_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace offline_pages {
+
+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 {
+ public:
+ PrefetchServerURLsTest() = default;
+ ~PrefetchServerURLsTest() override = default;
+ void SetTestingServerEndpoint(const std::string& server_config);
+
+ private:
+ variations::testing::VariationParamsManager params_manager_;
+};
+
+void PrefetchServerURLsTest::SetTestingServerEndpoint(
+ const std::string& server_config) {
+ params_manager_.ClearAllVariationParams();
+ params_manager_.SetVariationParamsWithFeatureAssociations(
+ kPrefetchingOfflinePagesFeature.name,
+ {{"offline_pages_backend", server_config}},
+ {kPrefetchingOfflinePagesFeature.name});
+}
+
+TEST_F(PrefetchServerURLsTest, TestVariationsConfig) {
+ GURL default_server(kPrefetchServer);
+ GURL request_url =
+ GeneratePageBundleRequestURL(version_info::Channel::UNKNOWN);
+ EXPECT_EQ(default_server.host(), request_url.host());
+ EXPECT_TRUE(request_url.SchemeIsCryptographic());
+
+ // Test reset to a valid, HTTPS URL.
+ SetTestingServerEndpoint(kTestOfflinePagesSuggestionsServerEndpoint);
+ request_url = GeneratePageBundleRequestURL(version_info::Channel::UNKNOWN);
+ EXPECT_EQ("test-offlinepages-pa.sandbox.googleapis.com", request_url.host());
+ EXPECT_TRUE(request_url.SchemeIsCryptographic());
+
+ // Test other variations of invalid URLS.
+ // First, a completely bogus endpoint.
+ SetTestingServerEndpoint(kInvalidServerEndpoint);
+ 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_service.h b/chromium/components/offline_pages/core/prefetch/prefetch_service.h
index 9e5ec6cbf62..974a3dcf5c7 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_service.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_service.h
@@ -8,8 +8,15 @@
#include "components/keyed_service/core/keyed_service.h"
namespace offline_pages {
-
+class OfflineEventLogger;
+class OfflineMetricsCollector;
class PrefetchDispatcher;
+class PrefetchDownloader;
+class PrefetchGCMHandler;
+class PrefetchImporter;
+class PrefetchNetworkRequestFactory;
+class PrefetchStore;
+class SuggestedArticlesObserver;
// Main class and entry point for the Offline Pages Prefetching feature, that
// controls the lifetime of all major subcomponents of the prefetching system.
@@ -17,7 +24,23 @@ class PrefetchService : public KeyedService {
public:
~PrefetchService() override = default;
- virtual PrefetchDispatcher* GetDispatcher() = 0;
+ // Subobjects that are created and owned by this service. Creation should be
+ // lightweight, all heavy work must be done on-demand only.
+ // The service manages lifetime, hookup and initialization of Prefetch
+ // system that consists of multiple specialized objects, all vended by this
+ // service.
+ virtual OfflineEventLogger* GetLogger() = 0;
+ virtual OfflineMetricsCollector* GetOfflineMetricsCollector() = 0;
+ virtual PrefetchDispatcher* GetPrefetchDispatcher() = 0;
+ virtual PrefetchGCMHandler* GetPrefetchGCMHandler() = 0;
+ virtual PrefetchNetworkRequestFactory* GetPrefetchNetworkRequestFactory() = 0;
+ virtual PrefetchDownloader* GetPrefetchDownloader() = 0;
+ virtual PrefetchStore* GetPrefetchStore() = 0;
+ virtual PrefetchImporter* GetPrefetchImporter() = 0;
+
+ // May be |nullptr| in tests. The PrefetchService does not depend on the
+ // SuggestedArticlesObserver, it merely owns it for lifetime purposes.
+ virtual SuggestedArticlesObserver* GetSuggestedArticlesObserver() = 0;
};
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.cc b/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.cc
index 9e86aba0b59..f8525bee927 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.cc
@@ -6,20 +6,102 @@
#include <utility>
+#include "base/bind.h"
#include "base/memory/ptr_util.h"
-#include "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
+#include "components/offline_pages/core/client_id.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/prefetch/offline_metrics_collector.h"
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_downloader.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
+#include "components/offline_pages/core/prefetch/prefetch_importer.h"
+#include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
namespace offline_pages {
-PrefetchServiceImpl::PrefetchServiceImpl()
- : dispatcher_(base::MakeUnique<PrefetchDispatcherImpl>()) {}
+PrefetchServiceImpl::PrefetchServiceImpl(
+ std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector,
+ std::unique_ptr<PrefetchDispatcher> dispatcher,
+ std::unique_ptr<PrefetchGCMHandler> gcm_handler,
+ std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory,
+ std::unique_ptr<PrefetchStore> prefetch_store,
+ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
+ std::unique_ptr<PrefetchDownloader> prefetch_downloader,
+ std::unique_ptr<PrefetchImporter> prefetch_importer)
+ : offline_metrics_collector_(std::move(offline_metrics_collector)),
+ prefetch_dispatcher_(std::move(dispatcher)),
+ prefetch_gcm_handler_(std::move(gcm_handler)),
+ network_request_factory_(std::move(network_request_factory)),
+ prefetch_store_(std::move(prefetch_store)),
+ suggested_articles_observer_(std::move(suggested_articles_observer)),
+ prefetch_downloader_(std::move(prefetch_downloader)),
+ prefetch_importer_(std::move(prefetch_importer)) {
+ prefetch_dispatcher_->SetService(this);
+ prefetch_gcm_handler_->SetService(this);
+ suggested_articles_observer_->SetPrefetchService(this);
+ prefetch_downloader_->SetCompletedCallback(
+ base::Bind(&PrefetchServiceImpl::OnDownloadCompleted,
+ // Downloader is owned by this instance.
+ base::Unretained(this)));
+}
PrefetchServiceImpl::~PrefetchServiceImpl() = default;
-PrefetchDispatcher* PrefetchServiceImpl::GetDispatcher() {
- return dispatcher_.get();
-};
+OfflineMetricsCollector* PrefetchServiceImpl::GetOfflineMetricsCollector() {
+ return offline_metrics_collector_.get();
+}
-void PrefetchServiceImpl::Shutdown() {}
+PrefetchDispatcher* PrefetchServiceImpl::GetPrefetchDispatcher() {
+ return prefetch_dispatcher_.get();
+}
+
+PrefetchGCMHandler* PrefetchServiceImpl::GetPrefetchGCMHandler() {
+ return prefetch_gcm_handler_.get();
+}
+
+PrefetchNetworkRequestFactory*
+PrefetchServiceImpl::GetPrefetchNetworkRequestFactory() {
+ return network_request_factory_.get();
+}
+
+PrefetchStore* PrefetchServiceImpl::GetPrefetchStore() {
+ return prefetch_store_.get();
+}
+
+SuggestedArticlesObserver* PrefetchServiceImpl::GetSuggestedArticlesObserver() {
+ return suggested_articles_observer_.get();
+}
+
+OfflineEventLogger* PrefetchServiceImpl::GetLogger() {
+ return &logger_;
+}
+
+PrefetchDownloader* PrefetchServiceImpl::GetPrefetchDownloader() {
+ return prefetch_downloader_.get();
+}
+
+PrefetchImporter* PrefetchServiceImpl::GetPrefetchImporter() {
+ return prefetch_importer_.get();
+}
+
+void PrefetchServiceImpl::Shutdown() {
+ suggested_articles_observer_.reset();
+ prefetch_downloader_.reset();
+}
+
+void PrefetchServiceImpl::OnDownloadCompleted(
+ const PrefetchDownloadResult& result) {
+ logger_.RecordActivity("Download " + result.download_id +
+ (result.success ? " succeeded" : " failed"));
+ if (!result.success)
+ return;
+
+ logger_.RecordActivity("Downloaded as " + result.file_path.MaybeAsASCII() +
+ " with size " + std::to_string(result.file_size));
+
+ // TODO(jianli): To hook up with prefetch importer.
+}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.h b/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.h
index c476f13e90e..e79935d750e 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_service_impl.h
@@ -8,23 +8,53 @@
#include <memory>
#include "base/macros.h"
+#include "components/offline_pages/core/offline_event_logger.h"
#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
namespace offline_pages {
class PrefetchServiceImpl : public PrefetchService {
public:
- PrefetchServiceImpl();
+ PrefetchServiceImpl(
+ std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector,
+ std::unique_ptr<PrefetchDispatcher> dispatcher,
+ std::unique_ptr<PrefetchGCMHandler> gcm_handler,
+ std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory,
+ std::unique_ptr<PrefetchStore> prefetch_store,
+ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
+ std::unique_ptr<PrefetchDownloader> prefetch_downloader,
+ std::unique_ptr<PrefetchImporter> prefetch_importer);
~PrefetchServiceImpl() override;
// PrefetchService implementation:
- PrefetchDispatcher* GetDispatcher() override;
+ OfflineMetricsCollector* GetOfflineMetricsCollector() override;
+ PrefetchDispatcher* GetPrefetchDispatcher() override;
+ PrefetchGCMHandler* GetPrefetchGCMHandler() override;
+ PrefetchNetworkRequestFactory* GetPrefetchNetworkRequestFactory() override;
+ PrefetchStore* GetPrefetchStore() override;
+ SuggestedArticlesObserver* GetSuggestedArticlesObserver() override;
+ OfflineEventLogger* GetLogger() override;
+ PrefetchDownloader* GetPrefetchDownloader() override;
+ PrefetchImporter* GetPrefetchImporter() override;
// KeyedService implementation:
void Shutdown() override;
private:
- std::unique_ptr<PrefetchDispatcher> dispatcher_;
+ // Called when a download completes.
+ void OnDownloadCompleted(const PrefetchDownloadResult& result);
+
+ OfflineEventLogger logger_;
+
+ std::unique_ptr<OfflineMetricsCollector> offline_metrics_collector_;
+ std::unique_ptr<PrefetchDispatcher> prefetch_dispatcher_;
+ std::unique_ptr<PrefetchGCMHandler> prefetch_gcm_handler_;
+ std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory_;
+ std::unique_ptr<PrefetchStore> prefetch_store_;
+ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
+ std::unique_ptr<PrefetchDownloader> prefetch_downloader_;
+ std::unique_ptr<PrefetchImporter> prefetch_importer_;
DISALLOW_COPY_AND_ASSIGN(PrefetchServiceImpl);
};
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_service_impl_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_service_impl_unittest.cc
new file mode 100644
index 00000000000..5dfee742961
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_service_impl_unittest.cc
@@ -0,0 +1,21 @@
+// 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/offline_pages/core/prefetch/prefetch_service_impl.h"
+
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+TEST(PrefetchServiceTest, ServiceDoesNotCrash) {
+ PrefetchServiceImpl service(nullptr);
+
+ service.AddCandidatePrefetchURLs(std::vector<PrefetchService::PrefetchURL>());
+ service.RemoveAllUnprocessedPrefetchURLs(kSuggestedArticlesNamespace);
+ service.RemovePrefetchURLsByClientId({kSuggestedArticlesNamespace, "123"});
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc b/chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
new file mode 100644
index 00000000000..5aaa109e5a5
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
@@ -0,0 +1,119 @@
+// 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/offline_pages/core/prefetch/prefetch_service_test_taco.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/offline_pages/core/prefetch/offline_metrics_collector.h"
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_downloader.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
+#include "components/offline_pages/core/prefetch/prefetch_importer.h"
+#include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+#include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
+#include "components/offline_pages/core/prefetch/test_offline_metrics_collector.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_importer.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h"
+
+namespace offline_pages {
+
+namespace {
+const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
+} // namespace
+
+PrefetchServiceTestTaco::PrefetchServiceTestTaco() {
+ dispatcher_ = base::MakeUnique<TestPrefetchDispatcher>();
+ metrics_collector_ = base::MakeUnique<TestOfflineMetricsCollector>(nullptr);
+ gcm_handler_ = base::MakeUnique<TestPrefetchGCMHandler>();
+ network_request_factory_ =
+ base::MakeUnique<TestPrefetchNetworkRequestFactory>();
+
+ PrefetchStoreTestUtil store_test_util;
+ store_test_util.BuildStoreInMemory();
+ prefetch_store_sql_ = store_test_util.ReleaseStore();
+
+ suggested_articles_observer_ = base::MakeUnique<SuggestedArticlesObserver>();
+ prefetch_downloader_ = base::WrapUnique(new PrefetchDownloader(kTestChannel));
+ prefetch_importer_ = base::MakeUnique<TestPrefetchImporter>();
+ // This sets up the testing articles as an empty vector, we can ignore the
+ // result here. This allows us to not create a ContentSuggestionsService.
+ suggested_articles_observer_->GetTestingArticles();
+}
+
+PrefetchServiceTestTaco::~PrefetchServiceTestTaco() = default;
+
+void PrefetchServiceTestTaco::SetOfflineMetricsCollector(
+ std::unique_ptr<OfflineMetricsCollector> metrics_collector) {
+ CHECK(!prefetch_service_);
+ metrics_collector_ = std::move(metrics_collector);
+}
+
+void PrefetchServiceTestTaco::SetPrefetchDispatcher(
+ std::unique_ptr<PrefetchDispatcher> dispatcher) {
+ CHECK(!prefetch_service_);
+ dispatcher_ = std::move(dispatcher);
+}
+
+void PrefetchServiceTestTaco::SetPrefetchGCMHandler(
+ std::unique_ptr<PrefetchGCMHandler> gcm_handler) {
+ CHECK(!prefetch_service_);
+ gcm_handler_ = std::move(gcm_handler);
+}
+
+void PrefetchServiceTestTaco::SetPrefetchNetworkRequestFactory(
+ std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory) {
+ CHECK(!prefetch_service_);
+ network_request_factory_ = std::move(network_request_factory);
+}
+
+void PrefetchServiceTestTaco::SetPrefetchStoreSql(
+ std::unique_ptr<PrefetchStore> prefetch_store_sql) {
+ CHECK(!prefetch_service_);
+ prefetch_store_sql_ = std::move(prefetch_store_sql);
+}
+
+void PrefetchServiceTestTaco::SetSuggestedArticlesObserver(
+ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer) {
+ CHECK(!prefetch_service_);
+ suggested_articles_observer_ = std::move(suggested_articles_observer);
+}
+
+void PrefetchServiceTestTaco::SetPrefetchDownloader(
+ std::unique_ptr<PrefetchDownloader> prefetch_downloader) {
+ CHECK(!prefetch_service_);
+ prefetch_downloader_ = std::move(prefetch_downloader);
+}
+
+void PrefetchServiceTestTaco::SetPrefetchImporter(
+ std::unique_ptr<PrefetchImporter> prefetch_importer) {
+ CHECK(!prefetch_service_);
+ prefetch_importer_ = std::move(prefetch_importer);
+}
+
+void PrefetchServiceTestTaco::CreatePrefetchService() {
+ CHECK(metrics_collector_ && dispatcher_ && gcm_handler_ &&
+ network_request_factory_ && prefetch_store_sql_ &&
+ suggested_articles_observer_ && prefetch_downloader_);
+
+ prefetch_service_ = base::MakeUnique<PrefetchServiceImpl>(
+ std::move(metrics_collector_), std::move(dispatcher_),
+ std::move(gcm_handler_), std::move(network_request_factory_),
+ std::move(prefetch_store_sql_), std::move(suggested_articles_observer_),
+ std::move(prefetch_downloader_), std::move(prefetch_importer_));
+}
+
+std::unique_ptr<PrefetchService>
+PrefetchServiceTestTaco::CreateAndReturnPrefetchService() {
+ CreatePrefetchService();
+ return std::move(prefetch_service_);
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.h b/chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
new file mode 100644
index 00000000000..3e15c51cd0a
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
@@ -0,0 +1,86 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVICE_TEST_TACO_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVICE_TEST_TACO_H_
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace offline_pages {
+class OfflineMetricsCollector;
+class PrefetchDispatcher;
+class PrefetchDownloader;
+class PrefetchGCMHandler;
+class PrefetchImporter;
+class PrefetchService;
+class PrefetchNetworkRequestFactory;
+class PrefetchStore;
+class SuggestedArticlesObserver;
+
+// The taco class acts as a wrapper around the prefetch service making
+// it easy to create for tests, using test versions of the dependencies.
+// This class is like a taco shell, and the filling is the prefetch service.
+// The default dependencies may be replaced by the test author to provide
+// custom versions that have test-specific hooks.
+class PrefetchServiceTestTaco {
+ public:
+ PrefetchServiceTestTaco();
+ ~PrefetchServiceTestTaco();
+
+ // These methods must be called before CreatePrefetchService() is invoked.
+ // If called after they will CHECK().
+ //
+ // Default type: TestOfflineMetricsCollector.
+ void SetOfflineMetricsCollector(
+ std::unique_ptr<OfflineMetricsCollector> metrics_collector);
+ // Default type: TestPrefetchDispatcher.
+ void SetPrefetchDispatcher(std::unique_ptr<PrefetchDispatcher> dispatcher);
+ // Default type: TestPrefetchGCMHandler.
+ void SetPrefetchGCMHandler(std::unique_ptr<PrefetchGCMHandler> gcm_handler);
+ // Default type: TestNetworkRequestFactory.
+ void SetPrefetchNetworkRequestFactory(
+ std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory);
+ void SetPrefetchStoreSql(std::unique_ptr<PrefetchStore> prefetch_store_sql);
+ // Defaults to SuggestedArticlesObserver. Initializes the testing suggestions
+ // by default, so no ContentSuggestionsService is required..
+ void SetSuggestedArticlesObserver(
+ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer);
+ void SetPrefetchDownloader(
+ std::unique_ptr<PrefetchDownloader> prefetch_downloader);
+ void SetPrefetchImporter(std::unique_ptr<PrefetchImporter> prefetch_importer);
+
+ // Creates and caches an instance of PrefetchService, using default or
+ // overridden test dependencies.
+ void CreatePrefetchService();
+
+ // Once CreatePrefetchService() is called, this accessor method starts
+ // returning the PrefetchService.
+ PrefetchService* prefetch_service() const {
+ CHECK(prefetch_service_);
+ return prefetch_service_.get();
+ }
+
+ // Creates and returns the ownership of the created PrefetchService instance.
+ // Leaves the taco empty, not usable.
+ std::unique_ptr<PrefetchService> CreateAndReturnPrefetchService();
+
+ private:
+ std::unique_ptr<OfflineMetricsCollector> metrics_collector_;
+ std::unique_ptr<PrefetchDispatcher> dispatcher_;
+ std::unique_ptr<PrefetchGCMHandler> gcm_handler_;
+ std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory_;
+ std::unique_ptr<PrefetchStore> prefetch_store_sql_;
+ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
+ std::unique_ptr<PrefetchDownloader> prefetch_downloader_;
+ std::unique_ptr<PrefetchImporter> prefetch_importer_;
+
+ std::unique_ptr<PrefetchService> prefetch_service_;
+};
+
+} // namespace offline_pages
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_SERVICE_TEST_TACO_H_
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_types.cc b/chromium/components/offline_pages/core/prefetch/prefetch_types.cc
index 8f06ddd2c8c..7f68f576d70 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_types.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_types.cc
@@ -6,8 +6,21 @@
namespace offline_pages {
-RenderPageInfo::RenderPageInfo() {}
+RenderPageInfo::RenderPageInfo() = default;
RenderPageInfo::RenderPageInfo(const RenderPageInfo& other) = default;
+PrefetchDownloadResult::PrefetchDownloadResult() = default;
+
+PrefetchDownloadResult::PrefetchDownloadResult(const std::string& download_id,
+ const base::FilePath& file_path,
+ uint64_t file_size)
+ : download_id(download_id),
+ success(true),
+ file_path(file_path),
+ file_size(file_size) {}
+
+PrefetchDownloadResult::PrefetchDownloadResult(
+ const PrefetchDownloadResult& other) = default;
+
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_types.h b/chromium/components/offline_pages/core/prefetch/prefetch_types.h
index f36526dcc7f..1fdb49a0983 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_types.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_types.h
@@ -5,9 +5,13 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_TYPES_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_TYPES_H_
+#include <string>
#include <vector>
-#include "base/macros.h"
+
+#include "base/files/file_path.h"
#include "base/time/time.h"
+#include "components/offline_pages/core/client_id.h"
+#include "url/gurl.h"
namespace offline_pages {
@@ -103,8 +107,41 @@ 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)>;
+// Holds information about a suggested URL to be prefetched.
+struct PrefetchURL {
+ PrefetchURL(const std::string& id, const GURL& url) : id(id), url(url) {}
+
+ // Client provided ID to allow the matching of provided URLs to the respective
+ // work item in the prefetching system within that client's assigned
+ // namespace. It can be any string value and it will not be used internally
+ // for de-duplication.
+ std::string id;
+
+ // This URL will be prefetched by the service.
+ GURL url;
+};
+
+// Result of a completed download.
+struct PrefetchDownloadResult {
+ PrefetchDownloadResult();
+ PrefetchDownloadResult(const std::string& download_id,
+ const base::FilePath& file_path,
+ uint64_t file_size);
+ PrefetchDownloadResult(const PrefetchDownloadResult& other);
+
+ std::string download_id;
+ bool success = false;
+ base::FilePath file_path;
+ uint64_t file_size = 0u;
+};
+
+// Callback invoked upon completion of a download.
+using PrefetchDownloadCompletedCallback =
+ base::Callback<void(const PrefetchDownloadResult& result)>;
+
} // namespace offline_pages
#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_TYPES_H_
diff --git a/chromium/components/offline_pages/core/prefetch/proto/any.proto b/chromium/components/offline_pages/core/prefetch/proto/any.proto
index bc7100281f1..c575c70b813 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/any.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/any.proto
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-syntax = "proto3";
+syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
message Any {
- string type_url = 1;
- bytes value = 2;
+ optional string type_url = 1;
+ optional bytes value = 2;
}
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 bffc766694a..37580909334 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto
@@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-syntax = "proto3";
+syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
import "status.proto";
-import "timestamp.proto";
// Type of transformation applied to a web page.
enum Transformation {
@@ -26,6 +25,19 @@ enum OutputFormat {
FORMAT_MHTML = 1;
}
+message Timestamp {
+ // Represents seconds of UTC time since Unix epoch
+ // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+ // 9999-12-31T23:59:59Z inclusive.
+ optional int64 seconds = 1;
+
+ // Non-negative fractions of a second at nanosecond resolution. Negative
+ // second values with fractions must still have non-negative nanos values
+ // that count forward in time. Must be from 0 to 999,999,999
+ // inclusive.
+ optional int32 nanos = 2;
+}
+
// Response to the GeneratePageBundle request.
message PageBundle {
// The list of archives in the bundle. The distribution of pages into archives
@@ -40,49 +52,49 @@ message Archive {
// those that encountered an error or were elided due to size considerations.
repeated PageInfo page_infos = 1;
// Format of the body.
- OutputFormat output_format = 2;
+ optional OutputFormat output_format = 2;
// Resource name for the body which can be read via the ByteStream API.
// This resource will remain available for a minimum of 24 hours after the
// GeneratePageBundle request.
- string body_name = 3;
+ optional string body_name = 3;
// Length of the body in bytes.
- int64 body_length = 4;
+ optional int64 body_length = 4;
}
// Information about a single returned offline page.
message PageInfo {
// The URL of the page that was rendered.
- string url = 1;
+ optional string url = 1;
// The final URL after redirects. Empty if the final URL is url.
- string redirect_url = 2;
+ optional string redirect_url = 2;
// Status of the render attempt. If status.code != OK, fields below this will
// be unset. If the operation is still running, status is NotFound to
// indicate the page is still being processed.
// If the page was not returned due to bundle size limits, status is
// FailedPrecondition. If the page failed to render for any other reason,
// status is Unknown.
- Status status = 3;
+ optional Status status = 3;
// Transformation that was applied to the page.
- Transformation transformation = 4;
+ optional Transformation transformation = 4;
// Time the page was rendered.
- Timestamp render_time = 5;
+ optional Timestamp render_time = 5;
}
// Request to return a list of pages in a format suitable for offline viewing.
message GeneratePageBundleRequest {
// The client's browser's user agent string.
- string user_agent = 1;
+ optional string user_agent = 1;
// Preferred browser language(s) as defined by
// [IETF BCP 47](https://tools.ietf.org/html/bcp47).
repeated string browser_languages = 2;
// Desired format of the web page archive(s).
- OutputFormat output_format = 3;
+ optional OutputFormat output_format = 3;
// Maximum size of the generated body. If all pages' output would exceed this
// size, only the first N pages are returned.
- int64 max_bundle_size_bytes = 4;
+ optional int64 max_bundle_size_bytes = 4;
// The GCM registration ID that can be used to inform the client
// of LRO completion.
- string gcm_registration_id = 5;
+ optional string gcm_registration_id = 5;
// List of individual page requests, in order of priority. At most 100 pages
// may be requested at a time.
repeated PageParameters pages = 6;
@@ -92,7 +104,7 @@ message GeneratePageBundleRequest {
// viewing.
message PageParameters {
// URL of the web page to return.
- string url = 1;
+ optional string url = 1;
// Transformation to apply. Must not be TRANSFORMATION_UNSPECIFIED.
- Transformation transformation = 2;
+ optional Transformation transformation = 2;
}
diff --git a/chromium/components/offline_pages/core/prefetch/proto/operation.proto b/chromium/components/offline_pages/core/prefetch/proto/operation.proto
index 77558d9fb65..db0b1eeedf4 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/operation.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/operation.proto
@@ -2,11 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-syntax = "proto3";
+syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
-;
import "any.proto";
import "status.proto";
@@ -16,18 +15,18 @@ import "status.proto";
message Operation {
// The server-assigned name, which is only unique within the same service that
// originally returns it.
- string name = 1;
+ optional string name = 1;
// Service-specific metadata associated with the operation. It typically
// contains progress information and common metadata such as create time.
// Some services might not provide such metadata. Any method that returns a
// long-running operation should document the metadata type, if any.
- Any metadata = 2;
+ optional Any metadata = 2;
// If the value is 'false', it means the operation is still in progress.
// If true, the operation is completed, and either 'error' or 'response' is
// available.
- bool done = 3;
+ optional bool done = 3;
// The operation result, which can be either an 'error' or a valid 'response'.
// If 'done' == 'false', neither 'error' nor 'response' is set.
diff --git a/chromium/components/offline_pages/core/prefetch/proto/status.proto b/chromium/components/offline_pages/core/prefetch/proto/status.proto
index f7187993d23..affec0b3a09 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/status.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/status.proto
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-syntax = "proto3";
+syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
@@ -18,10 +18,10 @@ enum Code {
message Status {
// The status code.
- int32 code = 1;
+ optional int32 code = 1;
// A developer-facing error message, which should be in English.
- string message = 2;
+ optional string message = 2;
// A list of messages that carry the error details. There will be a
// common set of message types for APIs to use.
diff --git a/chromium/components/offline_pages/core/prefetch/proto/timestamp.proto b/chromium/components/offline_pages/core/prefetch/proto/timestamp.proto
deleted file mode 100644
index c9658e73ca0..00000000000
--- a/chromium/components/offline_pages/core/prefetch/proto/timestamp.proto
+++ /dev/null
@@ -1,21 +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.
-
-syntax = "proto3";
-option optimize_for = LITE_RUNTIME;
-
-package offline_pages.proto;
-
-message Timestamp {
- // Represents seconds of UTC time since Unix epoch
- // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
- // 9999-12-31T23:59:59Z inclusive.
- int64 seconds = 1;
-
- // Non-negative fractions of a second at nanosecond resolution. Negative
- // second values with fractions must still have non-negative nanos values
- // that count forward in time. Must be from 0 to 999,999,999
- // inclusive.
- int32 nanos = 2;
-}
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc
new file mode 100644
index 00000000000..f6cecd57113
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc
@@ -0,0 +1,173 @@
+// 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/offline_pages/core/prefetch/store/prefetch_store.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/offline_store_types.h"
+#include "components/offline_pages/core/prefetch/prefetch_item.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_utils.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace offline_pages {
+namespace {
+
+// Name of the table with prefetch items.
+const char kPrefetchItemsTableName[] = "prefetch_items";
+const char kPrefetchStoreFileName[] = "PrefetchStore.db";
+
+const int kCurrentVersion = 1;
+const int kCompatibleVersion = 1;
+
+using InitializeCallback =
+ base::Callback<void(InitializationStatus,
+ std::unique_ptr<sql::Connection>)>;
+
+bool CreatePrefetchItemsTable(sql::Connection* db) {
+ static const char kSql[] =
+ "CREATE TABLE prefetch_items"
+ "(offline_id INTEGER PRIMARY KEY NOT NULL,"
+ " state INTEGER NOT NULL DEFAULT 0,"
+ " request_archive_attempt_count INTEGER NOT NULL DEFAULT 0,"
+ " archive_body_length INTEGER_NOT_NULL DEFAULT -1,"
+ " creation_time INTEGER NOT NULL,"
+ " freshness_time INTEGER NOT NULL,"
+ " error_code INTEGER NOT NULL DEFAULT 0,"
+ " guid VARCHAR NOT NULL DEFAULT '',"
+ " client_namespace VARCHAR NOT NULL DEFAULT '',"
+ " client_id VARCHAR NOT NULL DEFAULT '',"
+ " requested_url VARCHAR NOT NULL DEFAULT '',"
+ " final_archived_url VARCHAR NOT NULL DEFAULT '',"
+ " operation_name VARCHAR NOT NULL DEFAULT '',"
+ " archive_body_name VARCHAR NOT NULL DEFAULT ''"
+ ")";
+ return db->Execute(kSql);
+}
+
+bool CreateSchema(sql::Connection* db) {
+ // If you create a transaction but don't Commit() it is automatically
+ // rolled back by its destructor when it falls out of scope.
+ sql::Transaction transaction(db);
+ if (!transaction.Begin())
+ return false;
+
+ if (!db->DoesTableExist(kPrefetchItemsTableName)) {
+ if (!CreatePrefetchItemsTable(db))
+ return false;
+ }
+
+ sql::MetaTable meta_table;
+ meta_table.Init(db, kCurrentVersion, kCompatibleVersion);
+
+ // This would be a great place to add indices when we need them.
+ return transaction.Commit();
+}
+
+bool PrepareDirectory(const base::FilePath& path) {
+ base::File::Error error = base::File::FILE_OK;
+ if (!base::DirectoryExists(path.DirName())) {
+ if (!base::CreateDirectoryAndGetError(path.DirName(), &error)) {
+ LOG(ERROR) << "Failed to create prefetch db directory: "
+ << base::File::ErrorToString(error);
+ return false;
+ }
+ }
+ return true;
+}
+
+// TODO(fgorski): This function and this part of the system in general could
+// benefit from a better status code reportable through UMA to better capture
+// the reason for failure, aiding the process of repeated attempts to
+// open/initialize the database.
+bool InitializeSync(sql::Connection* db,
+ const base::FilePath& path,
+ bool in_memory) {
+ // These values are default.
+ db->set_page_size(4096);
+ db->set_cache_size(500);
+ db->set_histogram_tag("PrefetchStore");
+ db->set_exclusive_locking();
+
+ if (!in_memory && !PrepareDirectory(path))
+ return false;
+
+ bool open_db_result = false;
+ if (in_memory)
+ open_db_result = db->OpenInMemory();
+ else
+ open_db_result = db->Open(path);
+
+ if (!open_db_result) {
+ LOG(ERROR) << "Failed to open database, in memory: " << in_memory;
+ return false;
+ }
+ db->Preload();
+
+ return CreateSchema(db);
+}
+
+} // namespace
+
+PrefetchStore::PrefetchStore(
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
+ : blocking_task_runner_(std::move(blocking_task_runner)),
+ in_memory_(true),
+ db_(new sql::Connection,
+ base::OnTaskRunnerDeleter(blocking_task_runner_)),
+ initialization_status_(InitializationStatus::NOT_INITIALIZED),
+ weak_ptr_factory_(this) {}
+
+PrefetchStore::PrefetchStore(
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+ const base::FilePath& path)
+ : blocking_task_runner_(std::move(blocking_task_runner)),
+ db_file_path_(path.AppendASCII(kPrefetchStoreFileName)),
+ in_memory_(false),
+ db_(new sql::Connection,
+ base::OnTaskRunnerDeleter(blocking_task_runner_)),
+ initialization_status_(InitializationStatus::NOT_INITIALIZED),
+ weak_ptr_factory_(this) {}
+
+PrefetchStore::~PrefetchStore() {}
+
+void PrefetchStore::Initialize(base::OnceClosure pending_command) {
+ DCHECK_EQ(initialization_status_, InitializationStatus::NOT_INITIALIZED);
+
+ initialization_status_ = InitializationStatus::INITIALIZING;
+ base::PostTaskAndReplyWithResult(
+ blocking_task_runner_.get(), FROM_HERE,
+ base::BindOnce(&InitializeSync, db_.get(), db_file_path_, in_memory_),
+ base::BindOnce(&PrefetchStore::OnInitializeDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(pending_command)));
+}
+
+void PrefetchStore::OnInitializeDone(base::OnceClosure pending_command,
+ bool success) {
+ DCHECK_EQ(initialization_status_, InitializationStatus::INITIALIZING);
+ initialization_status_ =
+ success ? InitializationStatus::SUCCESS : InitializationStatus::FAILURE;
+
+ CHECK(!pending_command.is_null());
+ std::move(pending_command).Run();
+
+ // Once pending commands are empty, we get back to NOT_INITIALIZED state, to
+ // make it possible to retry initialization next time a DB operation is
+ // attempted.
+ if (initialization_status_ == InitializationStatus::FAILURE)
+ initialization_status_ = InitializationStatus::NOT_INITIALIZED;
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store.h b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.h
new file mode 100644
index 00000000000..15929a6b4e2
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.h
@@ -0,0 +1,122 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_H_
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_runner_util.h"
+
+namespace sql {
+class Connection;
+}
+
+namespace offline_pages {
+
+enum class InitializationStatus {
+ NOT_INITIALIZED,
+ INITIALIZING,
+ SUCCESS,
+ FAILURE,
+};
+
+// PrefetchStore is a front end to SQLite store hosting prefetch related
+// items.
+//
+// The store controls the pointer to the SQLite database and only makes it
+// available to the |RunCallback| of the |Execute| method on the blocking
+// thread.
+//
+// Store has a set of auxiliary functions meant to be used on the blocking
+// thread. They can be found in prefetch_store_sql_utils file.
+class PrefetchStore {
+ public:
+ // Definition of the callback that is going to run the core of the command in
+ // the |Execute| method.
+ template <typename T>
+ using RunCallback = base::OnceCallback<T(sql::Connection*)>;
+
+ // Definition of the callback used to pass the result back to the caller of
+ // |Execute| method.
+ template <typename T>
+ using ResultCallback = base::OnceCallback<void(T)>;
+
+ explicit PrefetchStore(
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
+ PrefetchStore(scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+ const base::FilePath& database_dir);
+ ~PrefetchStore();
+
+ // Executes a |run_callback| on SQL store on the blocking thread, and posts
+ // its result back to calling thread through |result_callback|. The work will
+ // be postponed if the store is in NOT_INITIALIZED or INITIALIZING, in which
+ // case the work will have to wait until initialization is completed. 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.
+ template <typename T>
+ void Execute(RunCallback<T> run_callback, ResultCallback<T> result_callback) {
+ 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)));
+ return;
+ }
+
+ 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),
+ std::move(result_callback));
+ }
+
+ // Gets the initialization status of the store.
+ InitializationStatus initialization_status() const {
+ return initialization_status_;
+ }
+
+ private:
+ // Used internally to initialize connection.
+ void Initialize(base::OnceClosure pending_command);
+
+ // Used to conclude opening/resetting DB connection.
+ void OnInitializeDone(base::OnceClosure pending_command, bool success);
+
+ // Background thread where all SQL access should be run.
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+
+ // Path to the database on disk.
+ base::FilePath db_file_path_;
+
+ // Only open the store in memory. Used for testing.
+ bool in_memory_;
+
+ // Database connection.
+ std::unique_ptr<sql::Connection, base::OnTaskRunnerDeleter> db_;
+
+ // Initialization status of the store.
+ InitializationStatus initialization_status_;
+
+ // Weak pointer to control the callback.
+ base::WeakPtrFactory<PrefetchStore> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefetchStore);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_H_
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
new file mode 100644
index 00000000000..b3fb7a43c6a
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
@@ -0,0 +1,90 @@
+// 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/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+namespace {
+
+int CountPrefetchItemsSync(sql::Connection* db) {
+ // Not starting transaction as this is a single read.
+ static const char kSql[] = "SELECT COUNT(offline_id) FROM prefetch_items";
+ sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ if (statement.Step())
+ return statement.ColumnInt(0);
+
+ return kStoreCommandFailed;
+}
+
+int UpdateItemsStateSync(const std::string& name_space,
+ const std::string& url,
+ PrefetchItemState state,
+ sql::Connection* db) {
+ static const char kSql[] =
+ "UPDATE prefetch_items"
+ " SET state = ?"
+ " WHERE client_namespace = ? AND requested_url = ?";
+
+ sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt(0, static_cast<int>(state));
+ statement.BindString(1, name_space);
+ statement.BindString(2, url);
+ if (statement.Run())
+ return db->GetLastChangeCount();
+
+ return kStoreCommandFailed;
+}
+
+} // namespace
+
+PrefetchStoreTestUtil::PrefetchStoreTestUtil() {}
+
+PrefetchStoreTestUtil::~PrefetchStoreTestUtil() = default;
+
+void PrefetchStoreTestUtil::BuildStore() {
+ if (!temp_directory_.CreateUniqueTempDir())
+ DVLOG(1) << "temp_directory_ not created";
+
+ store_.reset(new PrefetchStore(base::ThreadTaskRunnerHandle::Get(),
+ temp_directory_.GetPath()));
+}
+
+void PrefetchStoreTestUtil::BuildStoreInMemory() {
+ store_.reset(new PrefetchStore(base::ThreadTaskRunnerHandle::Get()));
+}
+
+std::unique_ptr<PrefetchStore> PrefetchStoreTestUtil::ReleaseStore() {
+ return std::move(store_);
+}
+
+void PrefetchStoreTestUtil::DeleteStore() {
+ store_.reset();
+ if (temp_directory_.IsValid()) {
+ if (!temp_directory_.Delete())
+ DVLOG(1) << "temp_directory_ not created";
+ }
+}
+
+void PrefetchStoreTestUtil::CountPrefetchItems(
+ PrefetchStore::ResultCallback<int> result_callback) {
+ store_->Execute(base::BindOnce(&CountPrefetchItemsSync),
+ std::move(result_callback));
+}
+
+void PrefetchStoreTestUtil::ZombifyPrefetchItem(
+ const std::string& name_space,
+ const GURL& url,
+ PrefetchStore::ResultCallback<int> result_callback) {
+ store_->Execute(base::BindOnce(&UpdateItemsStateSync, name_space, url.spec(),
+ PrefetchItemState::ZOMBIE),
+ std::move(result_callback));
+}
+} // namespace offline_pages
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
new file mode 100644
index 00000000000..c330308a4f2
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_TEST_UTIL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_TEST_UTIL_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+
+class GURL;
+
+namespace base {
+class ScopedTempDir;
+} // namespace base
+
+namespace offline_pages {
+const int kStoreCommandFailed = -1;
+
+class PrefetchStoreTestUtil {
+ public:
+ PrefetchStoreTestUtil();
+ ~PrefetchStoreTestUtil();
+
+ void BuildStore();
+ void BuildStoreInMemory();
+
+ void CountPrefetchItems(PrefetchStore::ResultCallback<int> result_callback);
+
+ void ZombifyPrefetchItem(const std::string& name_space,
+ const GURL& url,
+ PrefetchStore::ResultCallback<int> reuslt_callback);
+
+ // Releases the ownership of currently controlled store.
+ std::unique_ptr<PrefetchStore> ReleaseStore();
+
+ void DeleteStore();
+
+ PrefetchStore* store() { return store_.get(); }
+
+ private:
+ base::ScopedTempDir temp_directory_;
+ std::unique_ptr<PrefetchStore> store_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefetchStoreTestUtil);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_TEST_UTIL_H_
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc
new file mode 100644
index 00000000000..a463fce8cb0
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc
@@ -0,0 +1,66 @@
+// 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/offline_pages/core/prefetch/store/prefetch_store.h"
+
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+
+int CountPrefetchItems(sql::Connection* db) {
+ // Not starting transaction as this is a single read.
+ static const char kSql[] = "SELECT COUNT(offline_id) FROM prefetch_items";
+ sql::Statement statement(db->GetUniqueStatement(kSql));
+ if (statement.Step())
+ return statement.ColumnInt(0);
+
+ return -1;
+}
+
+void CountPrefetchItemsResult(int expected_count, int actual_count) {
+ EXPECT_EQ(expected_count, actual_count);
+}
+
+} // namespace
+
+class PrefetchStoreTest : public testing::Test {
+ public:
+ PrefetchStoreTest();
+ ~PrefetchStoreTest() override = default;
+
+ void SetUp() override { store_test_util_.BuildStoreInMemory(); }
+
+ void TearDown() override {
+ store_test_util_.DeleteStore();
+ PumpLoop();
+ }
+
+ PrefetchStore* store() { return store_test_util_.store(); }
+
+ void PumpLoop() { task_runner_->RunUntilIdle(); }
+
+ private:
+ PrefetchStoreTestUtil store_test_util_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+PrefetchStoreTest::PrefetchStoreTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+TEST_F(PrefetchStoreTest, InitializeStore) {
+ store()->Execute<int>(base::BindOnce(&CountPrefetchItems),
+ base::BindOnce(&CountPrefetchItemsResult, 0));
+ PumpLoop();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.cc b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.cc
new file mode 100644
index 00000000000..e5c28d90c74
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.cc
@@ -0,0 +1,29 @@
+// 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/offline_pages/core/prefetch/store/prefetch_store_utils.h"
+
+#include <limits>
+
+#include "base/rand_util.h"
+#include "components/offline_pages/core/prefetch/prefetch_item.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+
+namespace offline_pages {
+
+int64_t GenerateOfflineId() {
+ return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
+}
+
+bool DeletePrefetchItemByOfflineIdSync(sql::Connection* db,
+ int64_t offline_id) {
+ DCHECK(db);
+ static const char kSql[] = "DELETE FROM prefetch_items WHERE offline_id=?";
+ sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, offline_id);
+ return statement.Run();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.h b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.h
new file mode 100644
index 00000000000..d47696ba408
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_utils.h
@@ -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.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_UTILS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_UTILS_H_
+
+#include <stdint.h>
+
+namespace sql {
+class Connection;
+} // namespace sql
+
+namespace offline_pages {
+
+// Creates an offline ID for the prefetch item.
+int64_t GenerateOfflineId();
+
+// Deletes a prefetch item by its offline ID. Returns whether it was the item
+// was successfully deleted.
+bool DeletePrefetchItemByOfflineIdSync(sql::Connection* db, int64_t offline_id);
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_UTILS_H_
diff --git a/chromium/components/offline_pages/core/prefetch/suggested_articles_observer.cc b/chromium/components/offline_pages/core/prefetch/suggested_articles_observer.cc
new file mode 100644
index 00000000000..75a7f333f85
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/suggested_articles_observer.cc
@@ -0,0 +1,132 @@
+// 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/offline_pages/core/prefetch/suggested_articles_observer.h"
+
+#include <unordered_set>
+
+#include "base/memory/ptr_util.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_status.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+
+using ntp_snippets::Category;
+using ntp_snippets::ContentSuggestion;
+
+namespace offline_pages {
+
+namespace {
+
+const ntp_snippets::Category& ArticlesCategory() {
+ static ntp_snippets::Category articles =
+ Category::FromKnownCategory(ntp_snippets::KnownCategories::ARTICLES);
+ return articles;
+}
+
+} // namespace
+
+SuggestedArticlesObserver::SuggestedArticlesObserver() = default;
+SuggestedArticlesObserver::~SuggestedArticlesObserver() {
+ if (content_suggestions_service_)
+ content_suggestions_service_->RemoveObserver(this);
+}
+
+void SuggestedArticlesObserver::SetPrefetchService(PrefetchService* service) {
+ DCHECK(service);
+
+ prefetch_service_ = service;
+}
+
+void SuggestedArticlesObserver::SetContentSuggestionsServiceAndObserve(
+ ntp_snippets::ContentSuggestionsService* service) {
+ DCHECK(service);
+
+ content_suggestions_service_ = service;
+ content_suggestions_service_->AddObserver(this);
+}
+
+bool SuggestedArticlesObserver::GetCurrentSuggestions(
+ std::vector<PrefetchURL>* result) {
+ DCHECK(result);
+
+ std::vector<PrefetchURL> prefetch_urls;
+ if (category_status_ != ntp_snippets::CategoryStatus::AVAILABLE)
+ return false;
+
+ const std::vector<ContentSuggestion>& suggestions =
+ test_articles_ ? *test_articles_
+ : content_suggestions_service_->GetSuggestionsForCategory(
+ ArticlesCategory());
+ for (const ContentSuggestion& suggestion : suggestions) {
+ prefetch_urls.push_back(
+ {suggestion.id().id_within_category(), suggestion.url()});
+ }
+
+ *result = prefetch_urls;
+ return true;
+}
+
+void SuggestedArticlesObserver::OnNewSuggestions(Category category) {
+ // TODO(dewittj): Change this to check whether a given category is not
+ // a _remote_ category.
+ if (category != ArticlesCategory())
+ return;
+
+ std::vector<PrefetchURL> prefetch_urls;
+ if (!GetCurrentSuggestions(&prefetch_urls))
+ return;
+
+ prefetch_service_->GetPrefetchDispatcher()->AddCandidatePrefetchURLs(
+ kSuggestedArticlesNamespace, prefetch_urls);
+}
+
+void SuggestedArticlesObserver::OnCategoryStatusChanged(
+ Category category,
+ ntp_snippets::CategoryStatus new_status) {
+ if (category != ArticlesCategory() || category_status_ == new_status)
+ return;
+
+ category_status_ = new_status;
+
+ if (category_status_ ==
+ ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED ||
+ category_status_ ==
+ ntp_snippets::CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED) {
+ prefetch_service_->GetPrefetchDispatcher()
+ ->RemoveAllUnprocessedPrefetchURLs(kSuggestedArticlesNamespace);
+ }
+}
+
+void SuggestedArticlesObserver::OnSuggestionInvalidated(
+ const ContentSuggestion::ID& suggestion_id) {
+ prefetch_service_->GetPrefetchDispatcher()->RemovePrefetchURLsByClientId(
+ ClientId(kSuggestedArticlesNamespace,
+ suggestion_id.id_within_category()));
+}
+
+void SuggestedArticlesObserver::OnFullRefreshRequired() {
+ prefetch_service_->GetPrefetchDispatcher()->RemoveAllUnprocessedPrefetchURLs(
+ kSuggestedArticlesNamespace);
+ OnNewSuggestions(ArticlesCategory());
+}
+
+void SuggestedArticlesObserver::ContentSuggestionsServiceShutdown() {
+ // No need to do anything here, we will just stop getting events.
+}
+
+std::vector<ntp_snippets::ContentSuggestion>*
+SuggestedArticlesObserver::GetTestingArticles() {
+ if (!test_articles_) {
+ test_articles_ =
+ base::MakeUnique<std::vector<ntp_snippets::ContentSuggestion>>();
+ }
+ return test_articles_.get();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/suggested_articles_observer.h b/chromium/components/offline_pages/core/prefetch/suggested_articles_observer.h
new file mode 100644
index 00000000000..919bfd2b851
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/suggested_articles_observer.h
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_SUGGESTED_ARTICLES_OBSERVER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_SUGGESTED_ARTICLES_OBSERVER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "components/ntp_snippets/content_suggestions_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+
+namespace ntp_snippets {
+class Category;
+}
+
+namespace offline_pages {
+
+// Observes the ContentSuggestionsService, listening for new suggestions in the
+// ARTICLES category. When those suggestions arrive, it then forwards them to
+// the Prefetch Service, which does not know about Content Suggestions
+// specifically.
+class SuggestedArticlesObserver
+ : public ntp_snippets::ContentSuggestionsService::Observer {
+ public:
+ SuggestedArticlesObserver();
+ ~SuggestedArticlesObserver() override;
+
+ void SetPrefetchService(PrefetchService* service);
+ void SetContentSuggestionsServiceAndObserve(
+ ntp_snippets::ContentSuggestionsService* service);
+
+ // TODO(dewittj): Make this private when the SQL store is up and running.
+ bool GetCurrentSuggestions(std::vector<PrefetchURL>* result);
+
+ // ContentSuggestionsService::Observer overrides.
+ void OnNewSuggestions(ntp_snippets::Category category) override;
+ void OnCategoryStatusChanged(
+ ntp_snippets::Category category,
+ ntp_snippets::CategoryStatus new_status) override;
+ void OnSuggestionInvalidated(
+ const ntp_snippets::ContentSuggestion::ID& suggestion_id) override;
+ void OnFullRefreshRequired() override;
+ void ContentSuggestionsServiceShutdown() override;
+
+ // Returns a pointer to the list of testing articles. If there is no such
+ // list, allocates one before returning the list. The observer owns the list.
+ std::vector<ntp_snippets::ContentSuggestion>* GetTestingArticles();
+
+ private:
+ // Unowned, only used when we are called by observer methods (so the
+ // pointer will be valid).
+ ntp_snippets::ContentSuggestionsService* content_suggestions_service_ =
+ nullptr;
+
+ // Unowned, owns |this|.
+ PrefetchService* prefetch_service_;
+
+ // Normally null, but can be set in tests to override the default behavior.
+ std::unique_ptr<std::vector<ntp_snippets::ContentSuggestion>> test_articles_;
+
+ ntp_snippets::CategoryStatus category_status_ =
+ ntp_snippets::CategoryStatus::INITIALIZING;
+
+ DISALLOW_COPY_AND_ASSIGN(SuggestedArticlesObserver);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_SUGGESTED_ARTICLES_OBSERVER_H_
diff --git a/chromium/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc b/chromium/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
new file mode 100644
index 00000000000..a243c2ecffc
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
@@ -0,0 +1,138 @@
+// 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/offline_pages/core/prefetch/suggested_articles_observer.h"
+
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
+#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "components/offline_pages/core/prefetch/prefetch_service_test_taco.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using ntp_snippets::Category;
+using ntp_snippets::ContentSuggestion;
+
+namespace offline_pages {
+
+namespace {
+
+ContentSuggestion ContentSuggestionFromTestURL(const GURL& test_url) {
+ auto category =
+ Category::FromKnownCategory(ntp_snippets::KnownCategories::ARTICLES);
+ return ContentSuggestion(category, test_url.spec(), test_url);
+}
+
+} // namespace
+
+class OfflinePageSuggestedArticlesObserverTest : public testing::Test {
+ public:
+ OfflinePageSuggestedArticlesObserverTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+ void SetUp() override {
+ prefetch_service_test_taco_ = base::MakeUnique<PrefetchServiceTestTaco>();
+ test_prefetch_dispatcher_ = new TestPrefetchDispatcher();
+ prefetch_service_test_taco_->SetPrefetchDispatcher(
+ base::WrapUnique(test_prefetch_dispatcher_));
+ prefetch_service_test_taco_->SetSuggestedArticlesObserver(
+ base::MakeUnique<SuggestedArticlesObserver>());
+ prefetch_service_test_taco_->CreatePrefetchService();
+ }
+
+ void TearDown() override {
+ // Ensure the store can be properly disposed off.
+ prefetch_service_test_taco_.reset();
+ task_runner_->RunUntilIdle();
+ }
+
+ SuggestedArticlesObserver* observer() {
+ return prefetch_service_test_taco_->prefetch_service()
+ ->GetSuggestedArticlesObserver();
+ }
+
+ TestPrefetchDispatcher* test_prefetch_dispatcher() {
+ return test_prefetch_dispatcher_;
+ }
+
+ protected:
+ Category category =
+ Category::FromKnownCategory(ntp_snippets::KnownCategories::ARTICLES);
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ std::unique_ptr<PrefetchServiceTestTaco> prefetch_service_test_taco_;
+
+ // Owned by the PrefetchServiceTestTaco.
+ TestPrefetchDispatcher* test_prefetch_dispatcher_;
+};
+
+TEST_F(OfflinePageSuggestedArticlesObserverTest,
+ ForwardsSuggestionsToPrefetchService) {
+ const GURL test_url_1("https://www.example.com/1");
+ observer()->GetTestingArticles()->push_back(
+ ContentSuggestionFromTestURL(test_url_1));
+
+ observer()->OnCategoryStatusChanged(category,
+ ntp_snippets::CategoryStatus::AVAILABLE);
+ observer()->OnNewSuggestions(category);
+ EXPECT_EQ(1, test_prefetch_dispatcher()->new_suggestions_count);
+ EXPECT_EQ(1U, test_prefetch_dispatcher()->latest_prefetch_urls.size());
+ EXPECT_EQ(test_url_1,
+ test_prefetch_dispatcher()->latest_prefetch_urls[0].url);
+ EXPECT_EQ(kSuggestedArticlesNamespace,
+ test_prefetch_dispatcher()->latest_name_space);
+}
+
+TEST_F(OfflinePageSuggestedArticlesObserverTest, RemovesAllOnBadStatus) {
+ const GURL test_url_1("https://www.example.com/1");
+ const GURL test_url_2("https://www.example.com/2");
+ observer()->GetTestingArticles()->push_back(
+ ContentSuggestionFromTestURL(test_url_1));
+ observer()->GetTestingArticles()->push_back(
+ ContentSuggestionFromTestURL(test_url_2));
+
+ observer()->OnCategoryStatusChanged(category,
+ ntp_snippets::CategoryStatus::AVAILABLE);
+ observer()->OnNewSuggestions(category);
+ ASSERT_EQ(2U, test_prefetch_dispatcher()->latest_prefetch_urls.size());
+
+ observer()->OnCategoryStatusChanged(
+ category, ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
+ EXPECT_EQ(1, test_prefetch_dispatcher()->remove_all_suggestions_count);
+ observer()->OnCategoryStatusChanged(
+ category,
+ ntp_snippets::CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED);
+ EXPECT_EQ(2, test_prefetch_dispatcher()->remove_all_suggestions_count);
+}
+
+TEST_F(OfflinePageSuggestedArticlesObserverTest, RemovesClientIdOnInvalidated) {
+ const GURL test_url_1("https://www.example.com/1");
+ observer()->GetTestingArticles()->push_back(
+ ContentSuggestionFromTestURL(test_url_1));
+ observer()->OnCategoryStatusChanged(category,
+ ntp_snippets::CategoryStatus::AVAILABLE);
+ observer()->OnNewSuggestions(category);
+ ASSERT_EQ(1U, test_prefetch_dispatcher()->latest_prefetch_urls.size());
+
+ observer()->OnSuggestionInvalidated(
+ ntp_snippets::ContentSuggestion::ID(category, test_url_1.spec()));
+
+ EXPECT_EQ(1, test_prefetch_dispatcher()->remove_by_client_id_count);
+ EXPECT_NE(nullptr, test_prefetch_dispatcher()->last_removed_client_id.get());
+ EXPECT_EQ(test_url_1.spec(),
+ test_prefetch_dispatcher()->last_removed_client_id->id);
+ EXPECT_EQ(kSuggestedArticlesNamespace,
+ test_prefetch_dispatcher()->latest_name_space);
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/task_test_base.cc b/chromium/components/offline_pages/core/prefetch/task_test_base.cc
new file mode 100644
index 00000000000..555aaecd5b7
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/task_test_base.cc
@@ -0,0 +1,33 @@
+// 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/offline_pages/core/prefetch/task_test_base.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/test/mock_callback.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace offline_pages {
+
+TaskTestBase::TaskTestBase()
+ : task_runner(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner) {}
+
+TaskTestBase::~TaskTestBase() = default;
+
+void TaskTestBase::ExpectTaskCompletes(Task* task) {
+ completion_callbacks_.push_back(
+ base::MakeUnique<base::MockCallback<Task::TaskCompletionCallback>>());
+ EXPECT_CALL(*completion_callbacks_.back(), Run(_));
+
+ task->SetTaskCompletionCallbackForTesting(
+ task_runner.get(), completion_callbacks_.back()->Get());
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/task_test_base.h b/chromium/components/offline_pages/core/prefetch/task_test_base.h
new file mode 100644
index 00000000000..56c9c5478df
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/task_test_base.h
@@ -0,0 +1,52 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_TASK_TEST_BASE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TASK_TEST_BASE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h"
+#include "components/offline_pages/core/task.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+
+namespace offline_pages {
+class Task;
+
+// Base class for testing prefetch requests with simulated responses.
+class TaskTestBase : public testing::Test {
+ public:
+ TaskTestBase();
+ ~TaskTestBase() override;
+
+ void ExpectTaskCompletes(Task* task);
+
+ TestPrefetchNetworkRequestFactory* prefetch_request_factory() {
+ return &prefetch_request_factory_;
+ }
+
+ net::TestURLFetcherFactory* url_fetcher_factory() {
+ return &url_fetcher_factory_;
+ }
+
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner;
+
+ private:
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ net::TestURLFetcherFactory url_fetcher_factory_;
+ TestPrefetchNetworkRequestFactory prefetch_request_factory_;
+
+ std::vector<std::unique_ptr<base::MockCallback<Task::TaskCompletionCallback>>>
+ completion_callbacks_;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TASK_TEST_BASE_H_
diff --git a/chromium/components/offline_pages/core/prefetch/test_offline_metrics_collector.h b/chromium/components/offline_pages/core/prefetch/test_offline_metrics_collector.h
new file mode 100644
index 00000000000..14d68bc866a
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_offline_metrics_collector.h
@@ -0,0 +1,28 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_TEST_OFFLINE_METRICS_COLLECTOR_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_OFFLINE_METRICS_COLLECTOR_H_
+
+#include "components/offline_pages/core/prefetch/offline_metrics_collector.h"
+
+class PrefService;
+
+namespace offline_pages {
+
+// Testing metrics collector that does nothing.
+class TestOfflineMetricsCollector : public OfflineMetricsCollector {
+ public:
+ explicit TestOfflineMetricsCollector(PrefService*) {}
+ ~TestOfflineMetricsCollector() override = default;
+
+ void OnAppStartupOrResume() override {}
+ void OnSuccessfulNavigationOnline() override {}
+ void OnSuccessfulNavigationOffline() override {}
+ void ReportAccumulatedStats() override {}
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_OFFLINE_METRICS_COLLECTOR_H_
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc b/chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
new file mode 100644
index 00000000000..c0a555ebe6a
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
@@ -0,0 +1,48 @@
+// 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/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/offline_page_item.h"
+
+namespace offline_pages {
+
+TestPrefetchDispatcher::TestPrefetchDispatcher() = default;
+TestPrefetchDispatcher::~TestPrefetchDispatcher() = default;
+
+void TestPrefetchDispatcher::AddCandidatePrefetchURLs(
+ const std::string& name_space,
+ const std::vector<PrefetchURL>& prefetch_urls) {
+ latest_name_space = name_space;
+ latest_prefetch_urls = prefetch_urls;
+ new_suggestions_count++;
+}
+
+void TestPrefetchDispatcher::RemoveAllUnprocessedPrefetchURLs(
+ const std::string& name_space) {
+ latest_prefetch_urls.clear();
+ remove_all_suggestions_count++;
+}
+
+void TestPrefetchDispatcher::RemovePrefetchURLsByClientId(
+ const ClientId& client_id) {
+ remove_by_client_id_count++;
+ last_removed_client_id = base::MakeUnique<ClientId>(client_id);
+}
+
+void TestPrefetchDispatcher::BeginBackgroundTask(
+ std::unique_ptr<ScopedBackgroundTask> task) {}
+
+void TestPrefetchDispatcher::StopBackgroundTask() {}
+
+void TestPrefetchDispatcher::SetService(PrefetchService* service) {}
+
+void TestPrefetchDispatcher::GCMOperationCompletedMessageReceived(
+ const std::string& operation_name) {
+ operation_list.push_back(operation_name);
+}
+
+void TestPrefetchDispatcher::RequestFinishBackgroundTaskForTest() {}
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h b/chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
new file mode 100644
index 00000000000..fb6620d56ad
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
@@ -0,0 +1,48 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_DISPATCHER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_DISPATCHER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
+
+namespace offline_pages {
+struct ClientId;
+
+// Testing version of the prefetch dispatcher.
+class TestPrefetchDispatcher : public PrefetchDispatcher {
+ public:
+ TestPrefetchDispatcher();
+ ~TestPrefetchDispatcher() override;
+
+ // PrefetchDispatcher implementation.
+ void AddCandidatePrefetchURLs(
+ const std::string& name_space,
+ const std::vector<PrefetchURL>& prefetch_urls) override;
+ void RemoveAllUnprocessedPrefetchURLs(const std::string& name_space) override;
+ void RemovePrefetchURLsByClientId(const ClientId& client_id) override;
+ void BeginBackgroundTask(std::unique_ptr<ScopedBackgroundTask> task) override;
+ void StopBackgroundTask() override;
+ void SetService(PrefetchService* service) override;
+ void GCMOperationCompletedMessageReceived(
+ const std::string& operation_name) override;
+ void RequestFinishBackgroundTaskForTest() override;
+
+ std::string latest_name_space;
+ std::vector<PrefetchURL> latest_prefetch_urls;
+ std::unique_ptr<ClientId> last_removed_client_id;
+ std::vector<std::string> operation_list;
+
+ int new_suggestions_count = 0;
+ int remove_all_suggestions_count = 0;
+ int remove_by_client_id_count = 0;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_DISPATCHER_H_
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc b/chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc
new file mode 100644
index 00000000000..494d6ac1dd2
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.cc
@@ -0,0 +1,29 @@
+// 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/offline_pages/core/prefetch/test_prefetch_gcm_handler.h"
+
+namespace offline_pages {
+namespace {
+const char kToken[] = "an_instance_id_token";
+}
+
+TestPrefetchGCMHandler::TestPrefetchGCMHandler() = default;
+TestPrefetchGCMHandler::~TestPrefetchGCMHandler() = default;
+
+gcm::GCMAppHandler* TestPrefetchGCMHandler::AsGCMAppHandler() {
+ return nullptr;
+}
+
+std::string TestPrefetchGCMHandler::GetAppId() const {
+ return "com.google.test.PrefetchAppId";
+}
+
+void TestPrefetchGCMHandler::GetGCMToken(
+ instance_id::InstanceID::GetTokenCallback callback) {
+ callback.Run(kToken, instance_id::InstanceID::Result::SUCCESS);
+}
+
+void TestPrefetchGCMHandler::SetService(PrefetchService* service) {}
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h b/chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h
new file mode 100644
index 00000000000..8dae52bdce4
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h
@@ -0,0 +1,30 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_GCM_HANDLER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_GCM_HANDLER_H_
+
+#include <string>
+
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
+
+namespace offline_pages {
+class PrefetchService;
+
+// Test for testing.
+class TestPrefetchGCMHandler : public PrefetchGCMHandler {
+ public:
+ TestPrefetchGCMHandler();
+ ~TestPrefetchGCMHandler() override;
+
+ gcm::GCMAppHandler* AsGCMAppHandler() override;
+ std::string GetAppId() const override;
+ void GetGCMToken(instance_id::InstanceID::GetTokenCallback callback) override;
+ void SetService(PrefetchService* service) override;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_GCM_HANDLER_H_
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_importer.h b/chromium/components/offline_pages/core/prefetch/test_prefetch_importer.h
new file mode 100644
index 00000000000..939b4eadda6
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_importer.h
@@ -0,0 +1,30 @@
+// 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_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_IMPORTER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_IMPORTER_H_
+
+#include "components/offline_pages/core/prefetch/prefetch_importer.h"
+
+namespace offline_pages {
+
+// Testing prefetch importer that does nothing.
+class TestPrefetchImporter : public PrefetchImporter {
+ public:
+ TestPrefetchImporter() {}
+ ~TestPrefetchImporter() override = default;
+
+ void ImportFile(const GURL& url,
+ const GURL& original_url,
+ const base::string16& title,
+ int64_t offline_id,
+ const ClientId& client_id,
+ const base::FilePath& file_path,
+ int64_t file_size,
+ const CompletedCallback& callback) override {}
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_IMPORTER_H_ \ No newline at end of file
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.cc b/chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.cc
new file mode 100644
index 00000000000..98af6b05030
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.cc
@@ -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.
+
+#include "components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h"
+
+#include <memory>
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/prefetch/generate_page_bundle_request.h"
+#include "components/offline_pages/core/prefetch/get_operation_request.h"
+
+namespace offline_pages {
+namespace {
+version_info::Channel kChannel = version_info::Channel::UNKNOWN;
+const char kUserAgent[] = "Chrome/57.0.2987.133";
+} // namespace
+
+TestPrefetchNetworkRequestFactory::TestPrefetchNetworkRequestFactory()
+ : TestPrefetchNetworkRequestFactory(new net::TestURLRequestContextGetter(
+ base::ThreadTaskRunnerHandle::Get())) {}
+
+TestPrefetchNetworkRequestFactory::TestPrefetchNetworkRequestFactory(
+ net::TestURLRequestContextGetter* request_context_getter)
+ : PrefetchNetworkRequestFactoryImpl(request_context_getter,
+ kChannel,
+ kUserAgent) {
+ request_context = request_context_getter;
+}
+
+TestPrefetchNetworkRequestFactory::~TestPrefetchNetworkRequestFactory() =
+ default;
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h b/chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h
new file mode 100644
index 00000000000..729a6f1db9c
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_network_request_factory.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_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_NETWORK_REQUEST_FACTORY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_NETWORK_REQUEST_FACTORY_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/version_info/channel.h"
+#include "net/url_request/url_request_test_util.h"
+
+namespace offline_pages {
+
+// Test factory that uses a TestURLRequestContextGetter.
+// manipulation.
+class TestPrefetchNetworkRequestFactory
+ : public PrefetchNetworkRequestFactoryImpl {
+ public:
+ TestPrefetchNetworkRequestFactory();
+ explicit TestPrefetchNetworkRequestFactory(
+ net::TestURLRequestContextGetter* request_context);
+ ~TestPrefetchNetworkRequestFactory() override;
+
+ scoped_refptr<net::TestURLRequestContextGetter> request_context;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_PREFETCH_NETWORK_REQUEST_FACTORY_H_
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 dcc69c4b4d8..0398211fe6c 100644
--- a/chromium/components/offline_pages/core/stub_offline_page_model.cc
+++ b/chromium/components/offline_pages/core/stub_offline_page_model.cc
@@ -15,6 +15,8 @@ void StubOfflinePageModel::SavePage(
const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
const SavePageCallback& callback) {}
+void StubOfflinePageModel::AddPage(const OfflinePageItem& page,
+ const AddPageCallback& callback) {}
void StubOfflinePageModel::MarkPageAccessed(int64_t offline_id) {}
void StubOfflinePageModel::DeletePagesByOfflineId(
const std::vector<int64_t>& offline_ids,
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 6d3bc8e37c1..e540930d415 100644
--- a/chromium/components/offline_pages/core/stub_offline_page_model.h
+++ b/chromium/components/offline_pages/core/stub_offline_page_model.h
@@ -28,6 +28,8 @@ class StubOfflinePageModel : public OfflinePageModel, public KeyedService {
void SavePage(const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
const SavePageCallback& callback) override;
+ void AddPage(const OfflinePageItem& page,
+ const AddPageCallback& callback) override;
void MarkPageAccessed(int64_t offline_id) override;
void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
const DeletePageCallback& callback) override;
diff --git a/chromium/components/omnibox/browser/BUILD.gn b/chromium/components/omnibox/browser/BUILD.gn
index a6fc236517c..b7fbfc4e551 100644
--- a/chromium/components/omnibox/browser/BUILD.gn
+++ b/chromium/components/omnibox/browser/BUILD.gn
@@ -3,7 +3,7 @@
# found in the LICENSE file.
import("//third_party/protobuf/proto_library.gni")
-import("//ui/vector_icons/vector_icons.gni")
+import("//components/vector_icons/vector_icons.gni")
if (is_android) {
import("//build/config/android/rules.gni")
@@ -52,6 +52,8 @@ static_library("browser") {
"builtin_provider.h",
"clipboard_url_provider.cc",
"clipboard_url_provider.h",
+ "history_match.cc",
+ "history_match.h",
"history_provider.cc",
"history_provider.h",
"history_quick_provider.cc",
@@ -169,7 +171,7 @@ static_library("browser") {
sources += get_target_outputs(":omnibox_vector_icons")
deps += [
":omnibox_vector_icons",
- "//ui/vector_icons",
+ "//components/vector_icons",
]
}
diff --git a/chromium/components/omnibox_strings.grdp b/chromium/components/omnibox_strings.grdp
index 0c341ee424f..95e8094e8c0 100644
--- a/chromium/components/omnibox_strings.grdp
+++ b/chromium/components/omnibox_strings.grdp
@@ -32,6 +32,9 @@
<message name="IDS_DANGEROUS_VERBOSE_STATE" desc="Text for the Dangerous Omnibox Verbose State. Displayed when the current page fails the malware check.">
Dangerous
</message>
+ <message name="IDS_OFFLINE_VERBOSE_STATE" desc="Text for the Offline Omnibox Verbose state. Displayed when the current page is loaded from a previously-downloaded cache.">
+ Offline
+ </message>
<if expr="is_ios">
<message name="IDS_OMNIBOX_EMPTY_HINT" desc="The text displayed in the omnibox when it is empty.">
Search or type URL
diff --git a/chromium/components/os_crypt/BUILD.gn b/chromium/components/os_crypt/BUILD.gn
index e91c6815e6a..d2dbb8728dd 100644
--- a/chromium/components/os_crypt/BUILD.gn
+++ b/chromium/components/os_crypt/BUILD.gn
@@ -72,6 +72,8 @@ static_library("os_crypt") {
if (is_desktop_linux) {
sources -= [ "os_crypt_posix.cc" ]
sources += [
+ "key_storage_config_linux.cc",
+ "key_storage_config_linux.h",
"key_storage_linux.cc",
"key_storage_linux.h",
"key_storage_util_linux.cc",
@@ -160,7 +162,10 @@ source_set("unit_tests") {
]
if (is_desktop_linux) {
- sources += [ "os_crypt_linux_unittest.cc" ]
+ sources += [
+ "key_storage_util_linux_unittest.cc",
+ "os_crypt_linux_unittest.cc",
+ ]
defines = []
if (use_gnome_keyring) {
diff --git a/chromium/components/os_crypt/key_storage_config_linux.cc b/chromium/components/os_crypt/key_storage_config_linux.cc
new file mode 100644
index 00000000000..bef5c6af562
--- /dev/null
+++ b/chromium/components/os_crypt/key_storage_config_linux.cc
@@ -0,0 +1,12 @@
+// 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/os_crypt/key_storage_config_linux.h"
+
+namespace os_crypt {
+
+Config::Config() = default;
+Config::~Config() = default;
+
+} // namespace os_crypt
diff --git a/chromium/components/os_crypt/key_storage_config_linux.h b/chromium/components/os_crypt/key_storage_config_linux.h
new file mode 100644
index 00000000000..6199cbdadbd
--- /dev/null
+++ b/chromium/components/os_crypt/key_storage_config_linux.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OS_CRYPT_KEY_STORAGE_CONFIG_LINUX_H_
+#define COMPONENTS_OS_CRYPT_KEY_STORAGE_CONFIG_LINUX_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+
+namespace os_crypt {
+
+// A container for all the initialisation parameters for OSCrypt.
+struct Config {
+ public:
+ Config();
+ ~Config();
+
+ // Force OSCrypt to use a specific linux password store.
+ std::string store;
+ // The product name to use for permission prompts.
+ std::string product_name;
+ // A runner on the main thread for gnome-keyring to be called from.
+ // TODO(crbug/466975): Libsecret and KWallet don't need this. We can remove
+ // this when we stop supporting keyring.
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner;
+ // Controls whether preference on using or ignoring backends is used.
+ bool should_use_preference;
+ // Preferences are stored in a separate file in the user data directory.
+ base::FilePath user_data_path;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Config);
+};
+
+} // namespace os_crypt
+
+#endif // COMPONENTS_OS_CRYPT_KEY_STORAGE_CONFIG_LINUX_H_
diff --git a/chromium/components/os_crypt/key_storage_keyring.h b/chromium/components/os_crypt/key_storage_keyring.h
index 9e9c32747e1..8c0b778edd4 100644
--- a/chromium/components/os_crypt/key_storage_keyring.h
+++ b/chromium/components/os_crypt/key_storage_keyring.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "components/os_crypt/key_storage_linux.h"
namespace base {
diff --git a/chromium/components/os_crypt/key_storage_linux.cc b/chromium/components/os_crypt/key_storage_linux.cc
index c8b18324ce7..6c63c1ccca9 100644
--- a/chromium/components/os_crypt/key_storage_linux.cc
+++ b/chromium/components/os_crypt/key_storage_linux.cc
@@ -5,10 +5,9 @@
#include "components/os_crypt/key_storage_linux.h"
#include "base/environment.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/nix/xdg_util.h"
-#include "base/single_thread_task_runner.h"
+#include "components/os_crypt/key_storage_config_linux.h"
#include "components/os_crypt/key_storage_util_linux.h"
#if defined(USE_LIBSECRET)
@@ -29,47 +28,18 @@ const char KeyStorageLinux::kFolderName[] = "Chromium Keys";
const char KeyStorageLinux::kKey[] = "Chromium Safe Storage";
#endif
-namespace {
-
-// Parameters to OSCrypt, which are set before the first call to OSCrypt, are
-// stored here.
-struct Configuration {
- std::string store;
- std::string product_name;
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner;
-};
-
-base::LazyInstance<Configuration>::DestructorAtExit g_config =
- LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-// static
-void KeyStorageLinux::SetStore(const std::string& store_type) {
- g_config.Get().store = store_type;
- VLOG(1) << "OSCrypt store set to " << store_type;
-}
-
-// static
-void KeyStorageLinux::SetProductName(const std::string& product_name) {
- g_config.Get().product_name = product_name;
-}
-
-// static
-void KeyStorageLinux::SetMainThreadRunner(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner) {
- g_config.Get().main_thread_runner = main_thread_runner;
-}
-
// static
-std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService() {
+std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService(
+ const os_crypt::Config& config) {
#if defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
// Select a backend.
+ bool use_backend = !config.should_use_preference ||
+ os_crypt::GetBackendUse(config.user_data_path);
std::unique_ptr<base::Environment> env(base::Environment::Create());
base::nix::DesktopEnvironment desktop_env =
base::nix::GetDesktopEnvironment(env.get());
os_crypt::SelectedLinuxBackend selected_backend =
- os_crypt::SelectBackend(g_config.Get().store, desktop_env);
+ os_crypt::SelectBackend(config.store, use_backend, desktop_env);
// Try initializing the selected backend.
// In case of GNOME_ANY, prefer Libsecret
@@ -89,7 +59,7 @@ std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService() {
#if defined(USE_KEYRING)
if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
selected_backend == os_crypt::SelectedLinuxBackend::GNOME_KEYRING) {
- key_storage.reset(new KeyStorageKeyring(g_config.Get().main_thread_runner));
+ key_storage.reset(new KeyStorageKeyring(config.main_thread_runner));
if (key_storage->Init()) {
VLOG(1) << "OSCrypt using Keyring as backend.";
return key_storage;
@@ -100,13 +70,13 @@ std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService() {
#if defined(USE_KWALLET)
if (selected_backend == os_crypt::SelectedLinuxBackend::KWALLET ||
selected_backend == os_crypt::SelectedLinuxBackend::KWALLET5) {
- DCHECK(!g_config.Get().product_name.empty());
+ DCHECK(!config.product_name.empty());
base::nix::DesktopEnvironment used_desktop_env =
selected_backend == os_crypt::SelectedLinuxBackend::KWALLET
? base::nix::DESKTOP_ENVIRONMENT_KDE4
: base::nix::DESKTOP_ENVIRONMENT_KDE5;
key_storage.reset(
- new KeyStorageKWallet(used_desktop_env, g_config.Get().product_name));
+ new KeyStorageKWallet(used_desktop_env, config.product_name));
if (key_storage->Init()) {
VLOG(1) << "OSCrypt using KWallet as backend.";
return key_storage;
@@ -117,6 +87,6 @@ std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService() {
// defined(USE_KWALLET)
// The appropriate store was not available.
- VLOG(1) << "OSCrypt could not initialize a backend.";
+ VLOG(1) << "OSCrypt did not initialize a backend.";
return nullptr;
}
diff --git a/chromium/components/os_crypt/key_storage_linux.h b/chromium/components/os_crypt/key_storage_linux.h
index 394b6b38173..54752146a7f 100644
--- a/chromium/components/os_crypt/key_storage_linux.h
+++ b/chromium/components/os_crypt/key_storage_linux.h
@@ -9,11 +9,10 @@
#include <string>
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-namespace base {
-class SingleThreadTaskRunner;
-} // namespace base
+namespace os_crypt {
+struct Config;
+}
// An API for retrieving OSCrypt's password from the system's password storage
// service.
@@ -22,20 +21,9 @@ class KeyStorageLinux {
KeyStorageLinux() = default;
virtual ~KeyStorageLinux() = default;
- // Force OSCrypt to use a specific linux password store.
- static void SetStore(const std::string& store_type);
-
- // The product name to use for permission prompts.
- static void SetProductName(const std::string& product_name);
-
- // A runner on the main thread for gnome-keyring to be called from.
- // TODO(crbug/466975): Libsecret and KWallet don't need this. We can remove
- // this when we stop supporting keyring.
- static void SetMainThreadRunner(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner);
-
// Tries to load the appropriate key storage. Returns null if none succeed.
- static std::unique_ptr<KeyStorageLinux> CreateService();
+ static std::unique_ptr<KeyStorageLinux> CreateService(
+ const os_crypt::Config& config);
// Gets the encryption key from the OS password-managing library. If a key is
// not found, a new key will be generated, stored and returned.
diff --git a/chromium/components/os_crypt/key_storage_util_linux.cc b/chromium/components/os_crypt/key_storage_util_linux.cc
index 5be4252e58c..2daf7b0b95d 100644
--- a/chromium/components/os_crypt/key_storage_util_linux.cc
+++ b/chromium/components/os_crypt/key_storage_util_linux.cc
@@ -4,12 +4,33 @@
#include "components/os_crypt/key_storage_util_linux.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/logging.h"
+namespace {
+
+// OSCrypt has a setting that determines whether a backend will be used.
+// The presense of this file in the file system means that the backend
+// should be ignored. It's absence means we should use the backend.
+constexpr const char kPreferenceFileName[] = "Disable Local Encryption";
+
+bool ReadBackendUse(const base::FilePath& user_data_dir, bool* use) {
+ if (user_data_dir.empty())
+ return false;
+ base::FilePath pref_path = user_data_dir.Append(kPreferenceFileName);
+ *use = !base::PathExists(pref_path);
+ return true;
+}
+
+} // namespace
+
namespace os_crypt {
SelectedLinuxBackend SelectBackend(const std::string& type,
+ bool use_backend,
base::nix::DesktopEnvironment desktop_env) {
+ // Explicitly requesting a store overrides other production logic.
if (type == "kwallet")
return SelectedLinuxBackend::KWALLET;
if (type == "kwallet5")
@@ -23,11 +44,14 @@ SelectedLinuxBackend SelectBackend(const std::string& type,
if (type == "basic")
return SelectedLinuxBackend::BASIC_TEXT;
+ // Ignore the backends if requested to.
+ if (!use_backend)
+ return SelectedLinuxBackend::BASIC_TEXT;
+
+ // Detect the store to use automatically.
const char* name = base::nix::GetDesktopEnvironmentName(desktop_env);
VLOG(1) << "Password storage detected desktop environment: "
<< (name ? name : "(unknown)");
-
- // Detect the store to use automatically.
switch (desktop_env) {
case base::nix::DESKTOP_ENVIRONMENT_KDE4:
return SelectedLinuxBackend::KWALLET;
@@ -47,4 +71,21 @@ SelectedLinuxBackend SelectBackend(const std::string& type,
return SelectedLinuxBackend::BASIC_TEXT;
}
+bool WriteBackendUse(const base::FilePath& user_data_dir, bool use) {
+ if (user_data_dir.empty())
+ return false;
+ base::FilePath pref_path = user_data_dir.Append(kPreferenceFileName);
+ if (use)
+ return base::DeleteFile(pref_path, false);
+ FILE* f = base::OpenFile(pref_path, "w");
+ return f != nullptr && base::CloseFile(f);
+}
+
+bool GetBackendUse(const base::FilePath& user_data_dir) {
+ bool setting;
+ if (ReadBackendUse(user_data_dir, &setting))
+ return setting;
+ return true;
+}
+
} // namespace os_crypt
diff --git a/chromium/components/os_crypt/key_storage_util_linux.h b/chromium/components/os_crypt/key_storage_util_linux.h
index d5c04b6e70b..f91bfc68673 100644
--- a/chromium/components/os_crypt/key_storage_util_linux.h
+++ b/chromium/components/os_crypt/key_storage_util_linux.h
@@ -9,6 +9,10 @@
#include "base/nix/xdg_util.h"
+namespace base {
+class FilePath;
+}
+
namespace os_crypt {
// The supported Linux backends for storing passwords.
@@ -23,13 +27,22 @@ enum class SelectedLinuxBackend {
};
// Decide which backend to target. |type| is checked first. If it does not
-// match a supported backend, |desktop_env| will be used to decide.
+// match a supported backend and |use_backend| is true, |desktop_env| will be
+// used to decide.
// TODO(crbug/571003): This is exposed as a utility only for password manager to
// use. It should be merged into key_storage_linux, once no longer needed in
// password manager.
SelectedLinuxBackend SelectBackend(const std::string& type,
+ bool use_backend,
base::nix::DesktopEnvironment desktop_env);
+// Set the setting that disables using OS-level encryption. If |use| is true,
+// a backend will be used.
+bool WriteBackendUse(const base::FilePath& user_data_dir, bool use);
+
+// Decide whether the backend should be used based on the setting.
+bool GetBackendUse(const base::FilePath& user_data_dir);
+
} // namespace os_crypt
#endif // COMPONENTS_OS_CRYPT_KEY_STORAGE_UTIL_LINUX_H_
diff --git a/chromium/components/os_crypt/key_storage_util_linux_unittest.cc b/chromium/components/os_crypt/key_storage_util_linux_unittest.cc
new file mode 100644
index 00000000000..4a03aaad623
--- /dev/null
+++ b/chromium/components/os_crypt/key_storage_util_linux_unittest.cc
@@ -0,0 +1,112 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/os_crypt/key_storage_util_linux.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using namespace os_crypt;
+
+class KeyStorageUtilLinuxPreferenceTest : public testing::Test {
+ public:
+ KeyStorageUtilLinuxPreferenceTest() = default;
+ ~KeyStorageUtilLinuxPreferenceTest() override = default;
+
+ void SetUp() override {
+ ASSERT_TRUE(base::CreateNewTempDirectory("", &fake_user_data_dir_));
+ }
+
+ void TearDown() override {
+ ASSERT_TRUE(base::DeleteFile(fake_user_data_dir_, true));
+ }
+
+ protected:
+ base::FilePath fake_user_data_dir_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeyStorageUtilLinuxPreferenceTest);
+};
+
+TEST_F(KeyStorageUtilLinuxPreferenceTest, FirstTimeDefaultsToTrue) {
+ EXPECT_TRUE(GetBackendUse(fake_user_data_dir_));
+}
+
+TEST_F(KeyStorageUtilLinuxPreferenceTest, SetToTrue) {
+ EXPECT_TRUE(WriteBackendUse(fake_user_data_dir_, true));
+ EXPECT_TRUE(GetBackendUse(fake_user_data_dir_));
+}
+
+TEST_F(KeyStorageUtilLinuxPreferenceTest, SetToFalse) {
+ EXPECT_TRUE(WriteBackendUse(fake_user_data_dir_, false));
+ EXPECT_FALSE(GetBackendUse(fake_user_data_dir_));
+}
+
+TEST_F(KeyStorageUtilLinuxPreferenceTest, MultipleWrites) {
+ EXPECT_TRUE(WriteBackendUse(fake_user_data_dir_, false));
+ EXPECT_FALSE(GetBackendUse(fake_user_data_dir_));
+
+ EXPECT_TRUE(WriteBackendUse(fake_user_data_dir_, true));
+ EXPECT_TRUE(GetBackendUse(fake_user_data_dir_));
+
+ EXPECT_TRUE(WriteBackendUse(fake_user_data_dir_, false));
+ EXPECT_FALSE(GetBackendUse(fake_user_data_dir_));
+}
+
+class KeyStorageUtilLinuxTest : public testing::Test {
+ public:
+ KeyStorageUtilLinuxTest() = default;
+ ~KeyStorageUtilLinuxTest() override = default;
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeyStorageUtilLinuxTest);
+};
+
+TEST_F(KeyStorageUtilLinuxTest, PasswordStoreFlagOverrides) {
+ SelectedLinuxBackend selected;
+
+ selected = SelectBackend(
+ "basic", true, base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_GNOME);
+ EXPECT_EQ(selected, SelectedLinuxBackend::BASIC_TEXT);
+
+ selected =
+ SelectBackend("gnome-libsecret", false,
+ base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_KDE4);
+ EXPECT_EQ(selected, SelectedLinuxBackend::GNOME_LIBSECRET);
+
+ selected =
+ SelectBackend("gnome-libsecret", true,
+ base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_KDE4);
+ EXPECT_EQ(selected, SelectedLinuxBackend::GNOME_LIBSECRET);
+}
+
+TEST_F(KeyStorageUtilLinuxTest, IgnoreBackends) {
+ SelectedLinuxBackend selected;
+
+ selected = SelectBackend(
+ "", true, base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_GNOME);
+ EXPECT_EQ(selected, SelectedLinuxBackend::GNOME_ANY);
+
+ selected = SelectBackend(
+ "", false, base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_GNOME);
+ EXPECT_EQ(selected, SelectedLinuxBackend::BASIC_TEXT);
+
+ selected = SelectBackend(
+ "", true, base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_KDE5);
+ EXPECT_EQ(selected, SelectedLinuxBackend::KWALLET5);
+
+ selected = SelectBackend(
+ "", false, base::nix::DesktopEnvironment::DESKTOP_ENVIRONMENT_KDE5);
+ EXPECT_EQ(selected, SelectedLinuxBackend::BASIC_TEXT);
+}
+
+} // namespace os_crypt
diff --git a/chromium/components/os_crypt/kwallet_dbus_unittest.cc b/chromium/components/os_crypt/kwallet_dbus_unittest.cc
index bc494f161d4..53efb7663a1 100644
--- a/chromium/components/os_crypt/kwallet_dbus_unittest.cc
+++ b/chromium/components/os_crypt/kwallet_dbus_unittest.cc
@@ -21,6 +21,7 @@ namespace {
using testing::_;
using testing::AllOf;
+using testing::ByMove;
using testing::DoAll;
using testing::ElementsAreArray;
using testing::Invoke;
@@ -30,43 +31,41 @@ using testing::StrictMock;
const char kKWalletInterface[] = "org.kde.KWallet";
const char kKLauncherInterface[] = "org.kde.KLauncher";
-dbus::Response* RespondBool(bool value) {
+std::unique_ptr<dbus::Response> RespondBool(bool value) {
std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
dbus::MessageWriter writer(response.get());
writer.AppendBool(value);
- return response.release();
+ return response;
}
-dbus::Response* RespondString(const std::string& value) {
+std::unique_ptr<dbus::Response> RespondString(const std::string& value) {
std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
dbus::MessageWriter writer(response.get());
writer.AppendString(value);
- return response.release();
+ return response;
}
-dbus::Response* RespondBytes(const std::vector<uint8_t>& bytes) {
+std::unique_ptr<dbus::Response> RespondBytes(
+ const std::vector<uint8_t>& bytes) {
std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(bytes.data(), bytes.size());
- return response.release();
+ return response;
}
-dbus::Response* RespondArrayOfStrings(const std::vector<std::string>& strings) {
+std::unique_ptr<dbus::Response> RespondArrayOfStrings(
+ const std::vector<std::string>& strings) {
std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfStrings(strings);
- return response.release();
+ return response;
}
-dbus::Response* RespondInt32(int value) {
+std::unique_ptr<dbus::Response> RespondInt32(int value) {
std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
dbus::MessageWriter writer(response.get());
writer.AppendInt32(value);
- return response.release();
-}
-
-dbus::Response* RespondEmpty() {
- return dbus::Response::CreateEmpty().release();
+ return response;
}
class KWalletDBusTest
@@ -328,28 +327,27 @@ MATCHER_P3(ArgumentsAreIntBoolString, int_1, bool_2, str_3, "") {
}
TEST_P(KWalletDBusTest, StartWalletd) {
- // The receiver of the message takes ownership of the response object.
- dbus::Response* response_success = RespondEmpty();
- dbus::MessageWriter writer(response_success);
- writer.AppendInt32(0); // return code
- writer.AppendString("dbus_name");
- writer.AppendString(std::string()); // error message
- writer.AppendInt32(100); // pid
-
EXPECT_CALL(
*mock_session_bus_.get(),
GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
.WillOnce(Return(mock_klauncher_proxy_.get()));
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ writer.AppendInt32(0); // return code
+ writer.AppendString("dbus_name");
+ writer.AppendString(std::string()); // error message
+ writer.AppendInt32(100); // pid
+
EXPECT_CALL(
*mock_klauncher_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKLauncherInterface, "start_service_by_desktop_name"),
ArgumentsAreStringStringsStringsStringBool(
kwalletd_name_, std::vector<std::string>(),
std::vector<std::string>(), std::string(), false)),
_))
- .WillOnce(Return(response_success));
+ .WillOnce(Return(ByMove(std::move(response))));
EXPECT_TRUE(kwallet_dbus_.StartKWalletd());
}
@@ -362,9 +360,9 @@ TEST_P(KWalletDBusTest, StartWalletdErrorRead) {
EXPECT_CALL(
*mock_klauncher_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
Calls(kKLauncherInterface, "start_service_by_desktop_name"), _))
- .WillOnce(Return(RespondEmpty()));
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
EXPECT_FALSE(kwallet_dbus_.StartKWalletd());
}
@@ -377,17 +375,17 @@ TEST_P(KWalletDBusTest, StartWalletdErrorContact) {
EXPECT_CALL(
*mock_klauncher_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
Calls(kKLauncherInterface, "start_service_by_desktop_name"), _))
- .WillOnce(Return(nullptr));
+ .WillOnce(Return(ByMove(nullptr)));
EXPECT_FALSE(kwallet_dbus_.StartKWalletd());
}
TEST_P(KWalletDBusTest, IsEnabledTrue) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
- .WillOnce(Return(RespondBool(true)));
+ CallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
+ .WillOnce(Return(ByMove(RespondBool(true))));
bool is_enabled = false;
EXPECT_EQ(KWalletDBus::Error::SUCCESS, kwallet_dbus_.IsEnabled(&is_enabled));
@@ -396,8 +394,8 @@ TEST_P(KWalletDBusTest, IsEnabledTrue) {
TEST_P(KWalletDBusTest, IsEnabledFalse) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
- .WillOnce(Return(RespondBool(false)));
+ CallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
+ .WillOnce(Return(ByMove(RespondBool(false))));
bool is_enabled = true;
EXPECT_EQ(KWalletDBus::Error::SUCCESS, kwallet_dbus_.IsEnabled(&is_enabled));
@@ -406,8 +404,8 @@ TEST_P(KWalletDBusTest, IsEnabledFalse) {
TEST_P(KWalletDBusTest, IsEnabledErrorRead) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
bool is_enabled = true;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -416,8 +414,8 @@ TEST_P(KWalletDBusTest, IsEnabledErrorRead) {
TEST_P(KWalletDBusTest, IsEnabledErrorContact) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "isEnabled"), _))
+ .WillOnce(Return(ByMove(nullptr)));
bool is_enabled = true;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -427,8 +425,8 @@ TEST_P(KWalletDBusTest, IsEnabledErrorContact) {
TEST_P(KWalletDBusTest, NetworkWallet) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "networkWallet"), _))
- .WillOnce(Return(RespondString("mock_wallet")));
+ CallMethodAndBlock(Calls(kKWalletInterface, "networkWallet"), _))
+ .WillOnce(Return(ByMove(RespondString("mock_wallet"))));
std::string wallet;
EXPECT_EQ(KWalletDBus::Error::SUCCESS, kwallet_dbus_.NetworkWallet(&wallet));
@@ -438,8 +436,8 @@ TEST_P(KWalletDBusTest, NetworkWallet) {
TEST_P(KWalletDBusTest, NetworkWalletErrorRead) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "networkWallet"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "networkWallet"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
std::string wallet;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -449,8 +447,8 @@ TEST_P(KWalletDBusTest, NetworkWalletErrorRead) {
TEST_P(KWalletDBusTest, NetworkWalletErrorContact) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "networkWallet"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "networkWallet"), _))
+ .WillOnce(Return(ByMove(nullptr)));
std::string wallet;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -459,11 +457,11 @@ TEST_P(KWalletDBusTest, NetworkWalletErrorContact) {
TEST_P(KWalletDBusTest, Open) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "open"),
ArgumentsAreStringInt64String("wallet", 0, "app")),
_))
- .WillOnce(Return(RespondInt32(1234)));
+ .WillOnce(Return(ByMove(RespondInt32(1234))));
int ret;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -473,8 +471,8 @@ TEST_P(KWalletDBusTest, Open) {
TEST_P(KWalletDBusTest, OpenErrorRead) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "open"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "open"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
int ret;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -483,8 +481,8 @@ TEST_P(KWalletDBusTest, OpenErrorRead) {
TEST_P(KWalletDBusTest, OpenErrorContact) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "open"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "open"), _))
+ .WillOnce(Return(ByMove(nullptr)));
int ret;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -493,11 +491,11 @@ TEST_P(KWalletDBusTest, OpenErrorContact) {
TEST_P(KWalletDBusTest, HasEntry) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(AllOf(Calls(kKWalletInterface, "hasEntry"),
- ArgumentsAreIntStringStringString(
- 123, "folder", "realm", "app")),
- _))
- .WillOnce(Return(RespondBool(true)));
+ CallMethodAndBlock(AllOf(Calls(kKWalletInterface, "hasEntry"),
+ ArgumentsAreIntStringStringString(
+ 123, "folder", "realm", "app")),
+ _))
+ .WillOnce(Return(ByMove(RespondBool(true))));
bool has_entry = false;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -507,8 +505,8 @@ TEST_P(KWalletDBusTest, HasEntry) {
TEST_P(KWalletDBusTest, HasEntryErrorRead) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "hasEntry"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "hasEntry"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
bool has_entry = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -517,8 +515,8 @@ TEST_P(KWalletDBusTest, HasEntryErrorRead) {
TEST_P(KWalletDBusTest, HasEntryErrorContact) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "hasEntry"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "hasEntry"), _))
+ .WillOnce(Return(ByMove(nullptr)));
bool has_entry = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -529,11 +527,11 @@ TEST_P(KWalletDBusTest, ReadEntry) {
const std::vector<uint8_t> bytes_expected = {1, 2, 1, 2};
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(AllOf(Calls(kKWalletInterface, "readEntry"),
- ArgumentsAreIntStringStringString(
- 123, "folder", "realm", "app")),
- _))
- .WillOnce(Return(RespondBytes(bytes_expected)));
+ CallMethodAndBlock(AllOf(Calls(kKWalletInterface, "readEntry"),
+ ArgumentsAreIntStringStringString(
+ 123, "folder", "realm", "app")),
+ _))
+ .WillOnce(Return(ByMove(RespondBytes(bytes_expected))));
std::vector<uint8_t> bytes;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -542,10 +540,9 @@ TEST_P(KWalletDBusTest, ReadEntry) {
}
TEST_P(KWalletDBusTest, ReadEntryErrorRead) {
- std::vector<uint8_t> bytes_expected = {1, 2, 1, 2};
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "readEntry"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "readEntry"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
std::vector<uint8_t> bytes;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -553,10 +550,9 @@ TEST_P(KWalletDBusTest, ReadEntryErrorRead) {
}
TEST_P(KWalletDBusTest, ReadEntryErrorContact) {
- std::vector<uint8_t> bytes_expected = {1, 2, 1, 2};
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "readEntry"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "readEntry"), _))
+ .WillOnce(Return(ByMove(nullptr)));
std::vector<uint8_t> bytes;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -566,11 +562,11 @@ TEST_P(KWalletDBusTest, ReadEntryErrorContact) {
TEST_P(KWalletDBusTest, EntryList) {
std::vector<std::string> strings_expected = {"one", "two"};
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "entryList"),
ArgumentsAreIntStringString(123, "folder", "app")),
_))
- .WillOnce(Return(RespondArrayOfStrings(strings_expected)));
+ .WillOnce(Return(ByMove(RespondArrayOfStrings(strings_expected))));
std::vector<std::string> strings;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -579,10 +575,9 @@ TEST_P(KWalletDBusTest, EntryList) {
}
TEST_P(KWalletDBusTest, EntryListErrorRead) {
- std::vector<std::string> strings_expected = {"one", "two"};
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "entryList"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "entryList"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
std::vector<std::string> strings;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -592,8 +587,8 @@ TEST_P(KWalletDBusTest, EntryListErrorRead) {
TEST_P(KWalletDBusTest, EntryListErrorContact) {
std::vector<std::string> strings_expected = {"one", "two"};
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "entryList"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "entryList"), _))
+ .WillOnce(Return(ByMove(nullptr)));
std::vector<std::string> strings;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -603,11 +598,11 @@ TEST_P(KWalletDBusTest, EntryListErrorContact) {
TEST_P(KWalletDBusTest, RemoveEntry) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(AllOf(Calls(kKWalletInterface, "removeEntry"),
- ArgumentsAreIntStringStringString(
- 123, "folder", "realm", "app")),
- _))
- .WillOnce(Return(RespondInt32(0)));
+ CallMethodAndBlock(AllOf(Calls(kKWalletInterface, "removeEntry"),
+ ArgumentsAreIntStringStringString(
+ 123, "folder", "realm", "app")),
+ _))
+ .WillOnce(Return(ByMove(RespondInt32(0))));
int ret;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -618,8 +613,8 @@ TEST_P(KWalletDBusTest, RemoveEntry) {
TEST_P(KWalletDBusTest, RemoveEntryErrorRead) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "removeEntry"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "removeEntry"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
int ret;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -629,8 +624,8 @@ TEST_P(KWalletDBusTest, RemoveEntryErrorRead) {
TEST_P(KWalletDBusTest, RemoveEntryErrorContact) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "removeEntry"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "removeEntry"), _))
+ .WillOnce(Return(ByMove(nullptr)));
int ret;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -641,11 +636,11 @@ TEST_P(KWalletDBusTest, WriteEntry) {
std::vector<uint8_t> bytes = {1, 2, 3, 1};
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(AllOf(Calls(kKWalletInterface, "writeEntry"),
- ArgumentsAreIntStringStringBytesString(
- 123, "folder", "realm", bytes, "app")),
- _))
- .WillOnce(Return(RespondInt32(0)));
+ CallMethodAndBlock(AllOf(Calls(kKWalletInterface, "writeEntry"),
+ ArgumentsAreIntStringStringBytesString(
+ 123, "folder", "realm", bytes, "app")),
+ _))
+ .WillOnce(Return(ByMove(RespondInt32(0))));
int ret;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -657,8 +652,8 @@ TEST_P(KWalletDBusTest, WriteEntry) {
TEST_P(KWalletDBusTest, WriteEntryErrorRead) {
std::vector<uint8_t> bytes = {1, 2, 3, 1};
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "writeEntry"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "writeEntry"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
int ret;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -669,8 +664,8 @@ TEST_P(KWalletDBusTest, WriteEntryErrorRead) {
TEST_P(KWalletDBusTest, WriteEntryErrorContact) {
std::vector<uint8_t> bytes = {1, 2, 3, 1};
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "writeEntry"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "writeEntry"), _))
+ .WillOnce(Return(ByMove(nullptr)));
int ret;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -680,11 +675,11 @@ TEST_P(KWalletDBusTest, WriteEntryErrorContact) {
TEST_P(KWalletDBusTest, HasFolder) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "hasFolder"),
ArgumentsAreIntStringString(123, "wallet", "app")),
_))
- .WillOnce(Return(RespondBool(true)));
+ .WillOnce(Return(ByMove(RespondBool(true))));
bool has_folder = false;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -694,8 +689,8 @@ TEST_P(KWalletDBusTest, HasFolder) {
TEST_P(KWalletDBusTest, HasFolderErrorRead) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "hasFolder"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "hasFolder"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
bool has_folder = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -704,8 +699,8 @@ TEST_P(KWalletDBusTest, HasFolderErrorRead) {
TEST_P(KWalletDBusTest, HasFolderErrorContact) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "hasFolder"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "hasFolder"), _))
+ .WillOnce(Return(ByMove(nullptr)));
bool has_folder = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -714,11 +709,11 @@ TEST_P(KWalletDBusTest, HasFolderErrorContact) {
TEST_P(KWalletDBusTest, CreateFolder) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "createFolder"),
ArgumentsAreIntStringString(123, "folder", "app")),
_))
- .WillOnce(Return(RespondBool(true)));
+ .WillOnce(Return(ByMove(RespondBool(true))));
bool created_folder = false;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -729,8 +724,8 @@ TEST_P(KWalletDBusTest, CreateFolder) {
TEST_P(KWalletDBusTest, CreateFolderErrorRead) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "createFolder"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "createFolder"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
bool created_folder = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -740,8 +735,8 @@ TEST_P(KWalletDBusTest, CreateFolderErrorRead) {
TEST_P(KWalletDBusTest, CreateFolderErrorContact) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "createFolder"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "createFolder"), _))
+ .WillOnce(Return(ByMove(nullptr)));
bool created_folder = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -750,12 +745,12 @@ TEST_P(KWalletDBusTest, CreateFolderErrorContact) {
TEST_P(KWalletDBusTest, WritePassword) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "writePassword"),
ArgumentsAreIntStringStringStringString(
123, "folder", "key", "password", "app")),
_))
- .WillOnce(Return(RespondInt32(0)));
+ .WillOnce(Return(ByMove(RespondInt32(0))));
bool write_success = false;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -766,12 +761,12 @@ TEST_P(KWalletDBusTest, WritePassword) {
TEST_P(KWalletDBusTest, WritePasswordRejected) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "writePassword"),
ArgumentsAreIntStringStringStringString(
123, "folder", "key", "password", "app")),
_))
- .WillOnce(Return(RespondInt32(-1)));
+ .WillOnce(Return(ByMove(RespondInt32(-1))));
bool write_success = true;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -783,8 +778,8 @@ TEST_P(KWalletDBusTest, WritePasswordRejected) {
TEST_P(KWalletDBusTest, WritePasswordErrorRead) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "writePassword"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "writePassword"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
bool write_success = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -795,8 +790,8 @@ TEST_P(KWalletDBusTest, WritePasswordErrorRead) {
TEST_P(KWalletDBusTest, WritePasswordErrorContact) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "writePassword"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "writePassword"), _))
+ .WillOnce(Return(ByMove(nullptr)));
bool write_success = false;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -807,11 +802,11 @@ TEST_P(KWalletDBusTest, WritePasswordErrorContact) {
TEST_P(KWalletDBusTest, ReadPassword) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "readPassword"),
ArgumentsAreIntStringStringString(123, "folder", "key", "app")),
_))
- .WillOnce(Return(RespondString("password")));
+ .WillOnce(Return(ByMove(RespondString("password"))));
std::string password;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -822,8 +817,8 @@ TEST_P(KWalletDBusTest, ReadPassword) {
TEST_P(KWalletDBusTest, ReadPasswordErrorRead) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "readPassword"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "readPassword"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
std::string password;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -833,8 +828,8 @@ TEST_P(KWalletDBusTest, ReadPasswordErrorRead) {
TEST_P(KWalletDBusTest, ReadPasswordErrorContact) {
EXPECT_CALL(
*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "readPassword"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "readPassword"), _))
+ .WillOnce(Return(ByMove(nullptr)));
std::string password;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
@@ -843,11 +838,11 @@ TEST_P(KWalletDBusTest, ReadPasswordErrorContact) {
TEST_P(KWalletDBusTest, CloseSuccess) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "close"),
ArgumentsAreIntBoolString(123, false, "app")),
_))
- .WillOnce(Return(RespondInt32(0)));
+ .WillOnce(Return(ByMove(RespondInt32(0))));
bool success = false;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -857,11 +852,11 @@ TEST_P(KWalletDBusTest, CloseSuccess) {
TEST_P(KWalletDBusTest, CloseUnsuccessful) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(
+ CallMethodAndBlock(
AllOf(Calls(kKWalletInterface, "close"),
ArgumentsAreIntBoolString(123, false, "app")),
_))
- .WillOnce(Return(RespondInt32(1)));
+ .WillOnce(Return(ByMove(RespondInt32(1))));
bool success = true;
EXPECT_EQ(KWalletDBus::Error::SUCCESS,
@@ -871,8 +866,8 @@ TEST_P(KWalletDBusTest, CloseUnsuccessful) {
TEST_P(KWalletDBusTest, CloseErrorRead) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "close"), _))
- .WillOnce(Return(RespondEmpty()));
+ CallMethodAndBlock(Calls(kKWalletInterface, "close"), _))
+ .WillOnce(Return(ByMove(dbus::Response::CreateEmpty())));
bool success = true;
EXPECT_EQ(KWalletDBus::Error::CANNOT_READ,
@@ -881,8 +876,8 @@ TEST_P(KWalletDBusTest, CloseErrorRead) {
TEST_P(KWalletDBusTest, CloseErrorContact) {
EXPECT_CALL(*mock_kwallet_proxy_.get(),
- MockCallMethodAndBlock(Calls(kKWalletInterface, "close"), _))
- .WillOnce(Return(nullptr));
+ CallMethodAndBlock(Calls(kKWalletInterface, "close"), _))
+ .WillOnce(Return(ByMove(nullptr)));
bool success = true;
EXPECT_EQ(KWalletDBus::Error::CANNOT_CONTACT,
diff --git a/chromium/components/os_crypt/os_crypt.h b/chromium/components/os_crypt/os_crypt.h
index b03d72608d6..bd079816a94 100644
--- a/chromium/components/os_crypt/os_crypt.h
+++ b/chromium/components/os_crypt/os_crypt.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_OS_CRYPT_OS_CRYPT_H_
#define COMPONENTS_OS_CRYPT_OS_CRYPT_H_
+#include <memory>
#include <string>
#include "base/macros.h"
@@ -13,9 +14,13 @@
#include "base/strings/string16.h"
#include "build/build_config.h"
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-#include "components/os_crypt/key_storage_linux.h"
-#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(UNIT_TEST)
+class KeyStorageLinux;
+#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(UNIT_TEST)
+
+namespace os_crypt {
+struct Config;
+}
// The OSCrypt class gives access to simple encryption and decryption of
// strings. Note that on Mac, access to the system Keychain is required and
@@ -24,20 +29,8 @@
class OSCrypt {
public:
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
- // If |store_type| is a known password store, we will attempt to use it.
- // In any other case, we default to auto-detecting the store.
- // This should not be changed after OSCrypt has been used.
- static void SetStore(const std::string& store_type);
-
- // Some password stores may prompt the user for permission and show the
- // application name.
- static void SetProductName(const std::string& product_name);
-
- // The gnome-keyring implementation requires calls from the main thread.
- // TODO(crbug/466975): Libsecret and KWallet don't need this. We can remove
- // this when we stop supporting keyring.
- static void SetMainThreadRunner(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner);
+ // Set the configuration of OSCrypt.
+ static void SetConfig(std::unique_ptr<os_crypt::Config> config);
// Returns true iff the real secret key (not hardcoded one) is available.
static bool IsEncryptionAvailable();
diff --git a/chromium/components/os_crypt/os_crypt_linux.cc b/chromium/components/os_crypt/os_crypt_linux.cc
index b8b6ada030e..1a141836f48 100644
--- a/chromium/components/os_crypt/os_crypt_linux.cc
+++ b/chromium/components/os_crypt/os_crypt_linux.cc
@@ -8,7 +8,6 @@
#include <algorithm>
#include <iterator>
-#include <memory>
#include "base/lazy_instance.h"
#include "base/logging.h"
@@ -16,6 +15,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
+#include "components/os_crypt/key_storage_config_linux.h"
#include "components/os_crypt/key_storage_linux.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
@@ -56,6 +56,7 @@ struct Cache {
std::unique_ptr<std::string> password_v11_cache;
bool is_key_storage_cached;
bool is_password_v11_cached;
+ std::unique_ptr<os_crypt::Config> config;
// Guards access to |g_cache|, making lazy initialization of individual parts
// thread safe.
base::Lock lock;
@@ -67,8 +68,10 @@ base::LazyInstance<Cache>::Leaky g_cache = LAZY_INSTANCE_INITIALIZER;
// found.
KeyStorageLinux* GetKeyStorage() {
if (!g_cache.Get().is_key_storage_cached) {
+ DCHECK(g_cache.Get().config);
g_cache.Get().is_key_storage_cached = true;
- g_cache.Get().key_storage_cache = KeyStorageLinux::CreateService();
+ g_cache.Get().key_storage_cache =
+ KeyStorageLinux::CreateService(*g_cache.Get().config);
}
return g_cache.Get().key_storage_cache.get();
}
@@ -225,28 +228,10 @@ bool OSCrypt::DecryptString(const std::string& ciphertext,
}
// static
-void OSCrypt::SetStore(const std::string& store_type) {
- // Changing the targeted password store makes no sense after initializing.
+void OSCrypt::SetConfig(std::unique_ptr<os_crypt::Config> config) {
+ // Setting initialisation parameters makes no sense after initializing.
DCHECK(!g_cache.Get().is_key_storage_cached);
-
- KeyStorageLinux::SetStore(store_type);
-}
-
-// static
-void OSCrypt::SetProductName(const std::string& product_name) {
- // Setting the product name makes no sense after initializing.
- DCHECK(!g_cache.Get().is_key_storage_cached);
-
- KeyStorageLinux::SetProductName(product_name);
-}
-
-// static
-void OSCrypt::SetMainThreadRunner(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner) {
- // Setting the task runner makes no sense after initializing.
- DCHECK(!g_cache.Get().is_key_storage_cached);
-
- KeyStorageLinux::SetMainThreadRunner(main_thread_runner);
+ g_cache.Get().config = std::move(config);
}
// static
diff --git a/chromium/components/os_crypt/os_crypt_mocker_linux.cc b/chromium/components/os_crypt/os_crypt_mocker_linux.cc
index d4dc0ac361c..899616c99c2 100644
--- a/chromium/components/os_crypt/os_crypt_mocker_linux.cc
+++ b/chromium/components/os_crypt/os_crypt_mocker_linux.cc
@@ -4,9 +4,13 @@
#include "components/os_crypt/os_crypt_mocker_linux.h"
+#include <memory>
+
#include "base/base64.h"
#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
#include "base/rand_util.h"
+#include "components/os_crypt/key_storage_config_linux.h"
#include "components/os_crypt/os_crypt.h"
namespace {
@@ -50,6 +54,7 @@ OSCryptMockerLinux* OSCryptMockerLinux::GetInstance() {
// static
void OSCryptMockerLinux::SetUpWithSingleton() {
UseMockKeyStorageForTesting(&GetKeyStorage, &GetPassword);
+ OSCrypt::SetConfig(base::MakeUnique<os_crypt::Config>());
}
// static
diff --git a/chromium/components/packed_ct_ev_whitelist/BUILD.gn b/chromium/components/packed_ct_ev_whitelist/BUILD.gn
deleted file mode 100644
index 872c6d0634d..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/BUILD.gn
+++ /dev/null
@@ -1,34 +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.
-
-static_library("packed_ct_ev_whitelist") {
- sources = [
- "bit_stream_reader.cc",
- "bit_stream_reader.h",
- "packed_ct_ev_whitelist.cc",
- "packed_ct_ev_whitelist.h",
- ]
-
- configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
- deps = [
- "//base",
- "//content/public/browser",
- "//net",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "bit_stream_reader_unittest.cc",
- "packed_ct_ev_whitelist_unittest.cc",
- ]
-
- deps = [
- ":packed_ct_ev_whitelist",
- "//base",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/packed_ct_ev_whitelist/DEPS b/chromium/components/packed_ct_ev_whitelist/DEPS
deleted file mode 100644
index 8e7bbc49e43..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-include_rules = [
- "+content/public/browser",
- "+net/cert",
- "+net/ssl",
-]
diff --git a/chromium/components/packed_ct_ev_whitelist/OWNERS b/chromium/components/packed_ct_ev_whitelist/OWNERS
deleted file mode 100644
index 009e29e53fb..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-eranm@chromium.org
-rsleevi@chromium.org
-
-# TEAM: net-dev@chromium.org
-# COMPONENT: Internals>Network>CertTrans
diff --git a/chromium/components/packed_ct_ev_whitelist/bit_stream_reader.cc b/chromium/components/packed_ct_ev_whitelist/bit_stream_reader.cc
deleted file mode 100644
index 94ef00904bb..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/bit_stream_reader.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/packed_ct_ev_whitelist/bit_stream_reader.h"
-
-#include "base/big_endian.h"
-#include "base/logging.h"
-#include "base/numerics/safe_conversions.h"
-
-namespace packed_ct_ev_whitelist {
-namespace internal {
-
-BitStreamReader::BitStreamReader(const base::StringPiece& source)
- : source_(source), current_byte_(0), current_bit_(7) {
- DCHECK_LT(source_.length(), UINT32_MAX);
-}
-
-bool BitStreamReader::ReadUnaryEncoding(uint64_t* out) {
- if (BitsLeft() == 0)
- return false;
-
- *out = 0;
- while ((BitsLeft() > 0) && ReadBit())
- ++(*out);
-
- return true;
-}
-
-bool BitStreamReader::ReadBits(uint8_t num_bits, uint64_t* out) {
- DCHECK_LE(num_bits, 64);
-
- if (BitsLeft() < num_bits)
- return false;
-
- *out = 0;
-
- for (; num_bits && (current_bit_ != 7); --num_bits)
- (*out) |= (static_cast<uint64_t>(ReadBit()) << (num_bits - 1));
- for (; num_bits / 8; num_bits -= 8)
- (*out) |= (static_cast<uint64_t>(ReadByte()) << (num_bits - 8));
- for (; num_bits; --num_bits)
- (*out) |= (static_cast<uint64_t>(ReadBit()) << (num_bits - 1));
-
- return true;
-}
-
-uint64_t BitStreamReader::BitsLeft() const {
- if (current_byte_ == source_.length())
- return 0;
- DCHECK_GT(source_.length(), current_byte_);
- return (source_.length() - (current_byte_ + 1)) * 8 + current_bit_ + 1;
-}
-
-uint8_t BitStreamReader::ReadBit() {
- DCHECK_GT(BitsLeft(), 0u);
- DCHECK(current_bit_ < 8 && current_bit_ >= 0);
- uint8_t res =
- (source_.data()[current_byte_] & (1 << current_bit_)) >> current_bit_;
- current_bit_--;
- if (current_bit_ < 0) {
- current_byte_++;
- current_bit_ = 7;
- }
-
- return res;
-}
-
-uint8_t BitStreamReader::ReadByte() {
- DCHECK_GT(BitsLeft(), 7u);
- DCHECK_EQ(current_bit_, 7);
-
- return source_.data()[current_byte_++];
-
-}
-
-} // namespace internal
-} // namespace packed_ct_ev_whitelist
diff --git a/chromium/components/packed_ct_ev_whitelist/bit_stream_reader.h b/chromium/components/packed_ct_ev_whitelist/bit_stream_reader.h
deleted file mode 100644
index 3ad9819ae83..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/bit_stream_reader.h
+++ /dev/null
@@ -1,67 +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_PACKED_CT_EV_WHITELIST_BIT_STREAM_READER_H_
-#define COMPONENTS_PACKED_CT_EV_WHITELIST_BIT_STREAM_READER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-
-namespace packed_ct_ev_whitelist {
-namespace internal {
-
-// A class for reading individual bits from a packed buffer. Bits are read
-// MSB-first from the stream.
-// It is limited to 64-bit reads, 4GB streams and is inefficient as a design
-// choice. This class should not be used frequently.
-//
-// It is meant for data that is is packed across bytes, necessitating the need
-// to read a variable number of bits across a byte boundary.
-class BitStreamReader {
- public:
- explicit BitStreamReader(const base::StringPiece& source);
-
- // Reads unary-encoded number into |out|. Returns true if
- // there was at least one bit to read, false otherwise.
- bool ReadUnaryEncoding(uint64_t* out);
-
- // Reads |num_bits| (up to 64) into |out|. |out| is filled from the MSB to the
- // LSB. If |num_bits| is less than 64, the most significant |64 - num_bits|
- // bits are unused and left as zeros. Returns true if the stream had the
- // requested |num_bits|, false otherwise.
- bool ReadBits(uint8_t num_bits, uint64_t* out);
-
- // Returns the number of bits left in the stream.
- uint64_t BitsLeft() const;
-
- private:
- // Reads a single bit. Within a byte, the bits are read from the MSB to the
- // LSB.
- uint8_t ReadBit();
-
- // Reads a single byte.
- // Precondition: The stream must be byte-aligned (current_bit_ == 7) before
- // calling this function.
- uint8_t ReadByte();
-
- const base::StringPiece source_;
-
- // Index of the byte currently being read from.
- size_t current_byte_;
-
- // Index of the last bit read within |current_byte_|. Since bits are read
- // from the MSB to the LSB, this value is initialized to 7 and decremented
- // after each read.
- int8_t current_bit_;
-
- DISALLOW_COPY_AND_ASSIGN(BitStreamReader);
-};
-
-} // namespace internal
-} // namespace packed_ct_ev_whitelist
-
-#endif // COMPONENTS_PACKED_CT_EV_WHITELIST_BIT_STREAM_READER_H_
diff --git a/chromium/components/packed_ct_ev_whitelist/bit_stream_reader_unittest.cc b/chromium/components/packed_ct_ev_whitelist/bit_stream_reader_unittest.cc
deleted file mode 100644
index d3d84a80325..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/bit_stream_reader_unittest.cc
+++ /dev/null
@@ -1,100 +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/packed_ct_ev_whitelist/bit_stream_reader.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <string>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace packed_ct_ev_whitelist {
-namespace internal {
-
-const uint8_t kSomeData[] = {0xd5, 0xe2, 0xaf, 0xe5, 0xbb, 0x10, 0x7c, 0xd1};
-
-TEST(BitStreamReaderTest, CanReadSingleByte) {
- BitStreamReader reader(
- base::StringPiece(reinterpret_cast<const char*>(kSomeData), 1));
- uint64_t v(0);
-
- EXPECT_EQ(8u, reader.BitsLeft());
- EXPECT_TRUE(reader.ReadBits(8, &v));
- EXPECT_EQ(UINT64_C(0xd5), v);
-
- EXPECT_FALSE(reader.ReadBits(1, &v));
- EXPECT_EQ(0u, reader.BitsLeft());
-}
-
-TEST(BitStreamReaderTest, CanReadSingleBits) {
- const uint64_t expected_bits[] = {
- 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0};
- BitStreamReader reader(
- base::StringPiece(reinterpret_cast<const char*>(kSomeData), 2));
- EXPECT_EQ(16u, reader.BitsLeft());
- uint64_t v(0);
-
- for (int i = 0; i < 16; ++i) {
- EXPECT_TRUE(reader.ReadBits(1, &v));
- EXPECT_EQ(expected_bits[i], v);
- }
- EXPECT_EQ(0u, reader.BitsLeft());
-}
-
-TEST(BitStreamReaderTest, CanReadBitGroups) {
- BitStreamReader reader(
- base::StringPiece(reinterpret_cast<const char*>(kSomeData), 3));
- EXPECT_EQ(24u, reader.BitsLeft());
- uint64_t v(0);
- uint64_t res(0);
-
- EXPECT_TRUE(reader.ReadBits(5, &v));
- res |= v << 19;
- EXPECT_EQ(19u, reader.BitsLeft());
- EXPECT_TRUE(reader.ReadBits(13, &v));
- res |= v << 6;
- EXPECT_EQ(6u, reader.BitsLeft());
- EXPECT_TRUE(reader.ReadBits(6, &v));
- res |= v;
- EXPECT_EQ(UINT64_C(0xd5e2af), res);
-
- EXPECT_FALSE(reader.ReadBits(1, &v));
-}
-
-TEST(BitStreamReaderTest, CanRead64Bit) {
- BitStreamReader reader(
- base::StringPiece(reinterpret_cast<const char*>(kSomeData), 8));
- EXPECT_EQ(64u, reader.BitsLeft());
- uint64_t v(0);
-
- EXPECT_TRUE(reader.ReadBits(64, &v));
- EXPECT_EQ(UINT64_C(0xd5e2afe5bb107cd1), v);
-}
-
-TEST(BitStreamReaderTest, CanReadUnaryEncodedNumbers) {
- internal::BitStreamReader reader(
- base::StringPiece(reinterpret_cast<const char*>(kSomeData), 3));
- const uint64_t expected_values[] = {2, 1, 1, 4, 0, 0, 1, 1, 1, 4};
- uint64_t v(0);
- for (int i = 0; i < 10; ++i) {
- EXPECT_TRUE(reader.ReadUnaryEncoding(&v));
- EXPECT_EQ(expected_values[i], v) << "Values differ at position " << i;
- }
-}
-
-TEST(BitStreamReaderTest, CannotReadFromEmptyStream) {
- BitStreamReader reader(base::StringPiece(
- reinterpret_cast<const char*>(kSomeData), static_cast<size_t>(0u)));
- uint64_t v(0);
-
- EXPECT_EQ(0u, reader.BitsLeft());
- EXPECT_FALSE(reader.ReadBits(1, &v));
- EXPECT_FALSE(reader.ReadUnaryEncoding(&v));
-}
-
-} // namespace internal
-} // namespace packed_ct_ev_whitelist
diff --git a/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.cc b/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.cc
deleted file mode 100644
index bcb8cdb0bbf..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.cc
+++ /dev/null
@@ -1,152 +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/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h"
-
-#include <string.h>
-
-#include <algorithm>
-
-#include "base/big_endian.h"
-#include "base/files/file_util.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "components/packed_ct_ev_whitelist/bit_stream_reader.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/ssl/ssl_config_service.h"
-
-namespace {
-const uint8_t kCertHashLengthBits = 64; // 8 bytes
-const uint8_t kCertHashLength = kCertHashLengthBits / 8;
-const uint64_t kGolombMParameterBits = 47; // 2^47
-
-void SetEVWhitelistInSSLConfigService(
- const scoped_refptr<net::ct::EVCertsWhitelist>& new_whitelist) {
- VLOG(1) << "Setting new EV Certs whitelist.";
- net::SSLConfigService::SetEVCertsWhitelist(new_whitelist);
-}
-
-int TruncatedHashesComparator(const void* v1, const void* v2) {
- const uint64_t& h1(*(static_cast<const uint64_t*>(v1)));
- const uint64_t& h2(*(static_cast<const uint64_t*>(v2)));
- if (h1 < h2)
- return -1;
- else if (h1 > h2)
- return 1;
- return 0;
-}
-} // namespace
-
-namespace packed_ct_ev_whitelist {
-
-void SetEVCertsWhitelist(scoped_refptr<net::ct::EVCertsWhitelist> whitelist) {
- if (!whitelist->IsValid()) {
- VLOG(1) << "EV Certs whitelist is not valid, not setting.";
- return;
- }
-
- base::Closure assign_cb =
- base::Bind(SetEVWhitelistInSSLConfigService, whitelist);
- content::BrowserThread::PostTask(
- content::BrowserThread::IO, FROM_HERE, assign_cb);
-}
-
-bool PackedEVCertsWhitelist::UncompressEVWhitelist(
- const std::string& compressed_whitelist,
- std::vector<uint64_t>* uncompressed_list) {
- internal::BitStreamReader reader(base::StringPiece(
- compressed_whitelist.data(), compressed_whitelist.size()));
- std::vector<uint64_t> result;
- // Reserve exactly the right amount of memory to avoid reallocs. The size
- // changes very rarely and if it does change the code will still be correct,
- // just slightly less efficient.
- result.reserve(110610);
-
- VLOG(1) << "Uncompressing EV whitelist of size "
- << compressed_whitelist.size();
- uint64_t curr_hash(0);
- if (!reader.ReadBits(kCertHashLengthBits, &curr_hash)) {
- VLOG(1) << "Failed reading first hash.";
- return false;
- }
- result.push_back(curr_hash);
- // M is the tunable parameter used by the Golomb coding.
- static const uint64_t kGolombParameterM = static_cast<uint64_t>(1)
- << kGolombMParameterBits;
-
- while (reader.BitsLeft() > kGolombMParameterBits) {
- uint64_t read_prefix = 0;
- if (!reader.ReadUnaryEncoding(&read_prefix)) {
- VLOG(1) << "Failed reading unary-encoded prefix.";
- return false;
- }
- if (read_prefix > (UINT64_MAX / kGolombParameterM)) {
- VLOG(1) << "Received value that would cause overflow: " << read_prefix;
- return false;
- }
-
- uint64_t r = 0;
- if (!reader.ReadBits(kGolombMParameterBits, &r)) {
- VLOG(1) << "Failed reading " << kGolombMParameterBits << " bits.";
- return false;
- }
- DCHECK_LT(r, kGolombParameterM);
-
- uint64_t curr_diff = read_prefix * kGolombParameterM + r;
- curr_hash += curr_diff;
-
- result.push_back(curr_hash);
- }
-
- // If there is excess capacity then trim it.
- if (result.size() < result.capacity()) {
- std::vector<uint64_t> temp(result.size());
- memcpy(&temp[0], &result[0], result.size() * sizeof(result[0]));
-
- // Swap the right-sized vector with the over-sized vector.
- result.swap(temp);
- }
-
- // Make sure our size trimming code worked.
- DCHECK(result.size() == result.capacity());
-
- uncompressed_list->swap(result);
- return true;
-}
-
-PackedEVCertsWhitelist::PackedEVCertsWhitelist(
- const std::string& compressed_whitelist,
- const base::Version& version)
- : version_(version) {
- if (!UncompressEVWhitelist(compressed_whitelist, &whitelist_)) {
- whitelist_.clear();
- return;
- }
-}
-
-PackedEVCertsWhitelist::~PackedEVCertsWhitelist() {
-}
-
-bool PackedEVCertsWhitelist::ContainsCertificateHash(
- const std::string& certificate_hash) const {
- DCHECK(!whitelist_.empty());
- uint64_t hash_to_lookup;
-
- base::ReadBigEndian(certificate_hash.data(), &hash_to_lookup);
- return bsearch(&hash_to_lookup,
- &whitelist_[0],
- whitelist_.size(),
- kCertHashLength,
- TruncatedHashesComparator) != NULL;
-}
-
-bool PackedEVCertsWhitelist::IsValid() const {
- return whitelist_.size() > 0;
-}
-
-base::Version PackedEVCertsWhitelist::Version() const {
- return version_;
-}
-
-} // namespace packed_ct_ev_whitelist
diff --git a/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h b/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h
deleted file mode 100644
index 4ee5bf13936..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h
+++ /dev/null
@@ -1,86 +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_PACKED_CT_EV_WHITELIST_PACKED_CT_EV_WHITELIST_H_
-#define COMPONENTS_PACKED_CT_EV_WHITELIST_PACKED_CT_EV_WHITELIST_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/version.h"
-#include "net/cert/ct_ev_whitelist.h"
-
-namespace packed_ct_ev_whitelist {
-
-// An implementation of the EVCertsWhitelist that gets its data packed using
-// Golomb coding to encode the difference between subsequent hash values.
-// Format of the packed list:
-// * First 8 bytes: First hash
-// * Repeating Golomb-coded number which is the numeric difference of the
-// previous hash value from this one
-//
-// The resulting, unpacked list is a sorted list of hash values that can be
-// efficiently searched.
-class PackedEVCertsWhitelist : public net::ct::EVCertsWhitelist {
- public:
- // Unpacks the given |compressed_whitelist|. See the class documentation
- // for description of the |compressed_whitelist| format.
- PackedEVCertsWhitelist(const std::string& compressed_whitelist,
- const base::Version& version);
-
- // Returns true if the |certificate_hash| appears in the EV certificate hashes
- // whitelist. Must not be called if IsValid for this instance returned false.
- bool ContainsCertificateHash(
- const std::string& certificate_hash) const override;
-
- // Returns true if the EV certificate hashes whitelist provided in the c'tor
- // was valid, false otherwise.
- bool IsValid() const override;
-
- // Returns the version of the whitelist in use, if available.
- base::Version Version() const override;
-
- protected:
- ~PackedEVCertsWhitelist() override;
-
- private:
- FRIEND_TEST_ALL_PREFIXES(PackedEVCertsWhitelistTest,
- UncompressFailsForTooShortList);
- FRIEND_TEST_ALL_PREFIXES(PackedEVCertsWhitelistTest,
- UncompressFailsForTruncatedList);
- FRIEND_TEST_ALL_PREFIXES(PackedEVCertsWhitelistTest,
- UncompressFailsForInvalidValuesInList);
- FRIEND_TEST_ALL_PREFIXES(PackedEVCertsWhitelistTest,
- UncompressesWhitelistCorrectly);
-
- // Given a Golomb-coded list of hashes in |compressed_whitelist|, unpack into
- // |uncompressed_list|. Returns true if the format of the compressed whitelist
- // is valid, false otherwise.
- static bool UncompressEVWhitelist(const std::string& compressed_whitelist,
- std::vector<uint64_t>* uncompressed_list);
-
- // The whitelist is an array containing certificate hashes (truncated
- // to a fixed size of 8 bytes), sorted.
- // Binary search is used to locate hashes in the the array.
- // Benchmarking bsearch vs std::set (with 120K entries, doing 1.2M lookups)
- // shows that bsearch is about twice as fast as std::set lookups (and std::set
- // has additional memory overhead).
- std::vector<uint64_t> whitelist_;
- base::Version version_;
-
- DISALLOW_COPY_AND_ASSIGN(PackedEVCertsWhitelist);
-};
-
-// Sets the EV certificate hashes whitelist in the SSLConfigService
-// to the provided |whitelist|, if valid. Otherwise, does nothing.
-// To set the new whitelist, this function dispatches a task to the IO thread.
-void SetEVCertsWhitelist(scoped_refptr<net::ct::EVCertsWhitelist> whitelist);
-
-} // namespace packed_ct_ev_whitelist
-
-#endif // COMPONENTS_PACKED_CT_EV_WHITELIST_PACKED_CT_EV_WHITELIST_H_
diff --git a/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist_unittest.cc b/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist_unittest.cc
deleted file mode 100644
index 3af27605243..00000000000
--- a/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist_unittest.cc
+++ /dev/null
@@ -1,157 +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/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h"
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <string>
-
-#include "base/big_endian.h"
-#include "base/macros.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const uint8_t kFirstHashRaw[] =
- {0x00, 0x00, 0x03, 0xd7, 0xfc, 0x18, 0x02, 0xcb};
-std::string GetFirstHash() {
- return std::string(reinterpret_cast<const char*>(kFirstHashRaw), 8);
-}
-
-// Second hash: Diff from first hash is > 2^47
-const uint8_t kSecondHashRaw[] =
- {0x00, 0x01, 0x05, 0xd2, 0x58, 0x47, 0xa7, 0xbf};
-std::string GetSecondHash() {
- return std::string(reinterpret_cast<const char*>(kSecondHashRaw), 8);
-}
-
-// Third hash: Diff from 2nd hash is < 2^47
-const uint8_t kThirdHashRaw[] =
- {0x00, 0x01, 0x48, 0x45, 0x8c, 0x53, 0x03, 0x94};
-std::string GetThirdHash() {
- return std::string(reinterpret_cast<const char*>(kThirdHashRaw), 8);
-}
-
-uint64_t HashToUint64(const std::string& hash_str) {
- uint64_t ret;
- base::ReadBigEndian(hash_str.c_str(), &ret);
- return ret;
-}
-
-const uint8_t kWhitelistData[] = {
- 0x00, 0x00, 0x03, 0xd7, 0xfc, 0x18, 0x02, 0xcb, // First hash
- 0xc0, 0x7e, 0x97, 0x0b, 0xe9, 0x3d, 0x10, 0x9c,
- 0xcd, 0x02, 0xd6, 0xf5, 0x40,
-};
-
-std::string GetPartialWhitelistData(uint8_t num_bytes) {
- return std::string(reinterpret_cast<const char*>(kWhitelistData), num_bytes);
-}
-
-std::string GetAllWhitelistData() {
- return GetPartialWhitelistData(arraysize(kWhitelistData));
-}
-
-} // namespace
-
-namespace packed_ct_ev_whitelist {
-
-TEST(PackedEVCertsWhitelistTest, UncompressFailsForTooShortList) {
- // This list does not contain enough bytes even for the first hash.
- std::vector<uint64_t> res;
- EXPECT_FALSE(PackedEVCertsWhitelist::UncompressEVWhitelist(
- std::string(reinterpret_cast<const char*>(kWhitelistData), 7), &res));
-}
-
-TEST(PackedEVCertsWhitelistTest, UncompressFailsForTruncatedList) {
- // This list is missing bits for the second part of the diff.
- std::vector<uint64_t> res;
- EXPECT_FALSE(PackedEVCertsWhitelist::UncompressEVWhitelist(
- std::string(reinterpret_cast<const char*>(kWhitelistData), 14), &res));
-}
-
-TEST(PackedEVCertsWhitelistTest, UncompressFailsForInvalidValuesInList) {
- // A list with an invalid read_prefix value is the number 131072, unary
- // encoded, after the fist 8 bytes of a valid hash.
- // That translates to 16385 0xff bytes.
- // To make the hash otherwise valid, append 6 bytes of r value.
- const int num_ff_bytes = 16385;
- const int total_size = 8 + num_ff_bytes + 7;
- uint8_t* invalid_whitelist = new uint8_t[total_size];
- invalid_whitelist[total_size - 1] = '\0';
- // Valid first hash.
- memcpy(reinterpret_cast<char*>(invalid_whitelist),
- reinterpret_cast<const char*>(kWhitelistData),
- 8 * sizeof(char));
- // 0xff 16385 times.
- for (int i = 0; i < num_ff_bytes; i++) {
- invalid_whitelist[8 + i] = 0xff;
- }
- // Valid r value (any 6 bytes will do).
- memcpy(reinterpret_cast<char*>(invalid_whitelist + 8 + num_ff_bytes),
- reinterpret_cast<const char*>(kWhitelistData),
- 6 * sizeof(char));
-
- std::vector<uint64_t> res;
- EXPECT_FALSE(PackedEVCertsWhitelist::UncompressEVWhitelist(
- std::string(reinterpret_cast<const char*>(invalid_whitelist),
- total_size - 1),
- &res));
- delete[] invalid_whitelist;
-}
-
-TEST(PackedEVCertsWhitelistTest, UncompressesWhitelistCorrectly) {
- std::vector<uint64_t> res;
- ASSERT_TRUE(PackedEVCertsWhitelist::UncompressEVWhitelist(
- GetAllWhitelistData(), &res));
- ASSERT_TRUE(res.size() == res.capacity());
-
- // Ensure first hash is found
- EXPECT_TRUE(std::find(res.begin(), res.end(), HashToUint64(GetFirstHash())) !=
- res.end());
- // Ensure second hash is found
- EXPECT_TRUE(std::find(res.begin(),
- res.end(),
- HashToUint64(GetSecondHash())) != res.end());
- // Ensure last hash is found
- EXPECT_TRUE(std::find(res.begin(), res.end(), HashToUint64(GetThirdHash())) !=
- res.end());
- // Ensure that there are exactly 3 hashes.
- EXPECT_EQ(3u, res.size());
-}
-
-TEST(PackedEVCertsWhitelistTest, CanFindHashInSetList) {
- scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(GetAllWhitelistData(), base::Version()));
-
- EXPECT_TRUE(whitelist->IsValid());
- EXPECT_TRUE(whitelist->ContainsCertificateHash(GetFirstHash()));
- EXPECT_TRUE(whitelist->ContainsCertificateHash(GetSecondHash()));
- EXPECT_TRUE(whitelist->ContainsCertificateHash(GetThirdHash()));
-}
-
-TEST(PackedEVCertsWhitelistTest, CorrectlyIdentifiesEmptyWhitelistIsInvalid) {
- scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(std::string(), base::Version()));
-
- EXPECT_FALSE(whitelist->IsValid());
-}
-
-TEST(PackedEVCertsWhitelistTest, CorrectlyIdentifiesPartialWhitelistIsInvalid) {
- scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(GetPartialWhitelistData(14), base::Version()));
-
- EXPECT_FALSE(whitelist->IsValid());
-}
-
-TEST(PackedEVCertsWhitelistTest, CorrectlyIdentifiesWhitelistIsValid) {
- scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(GetAllWhitelistData(), base::Version()));
-
- EXPECT_TRUE(whitelist->IsValid());
-}
-
-} // namespace packed_ct_ev_whitelist
diff --git a/chromium/components/page_info_strings.grdp b/chromium/components/page_info_strings.grdp
index 7dac8f62dcc..59764dd57c2 100644
--- a/chromium/components/page_info_strings.grdp
+++ b/chromium/components/page_info_strings.grdp
@@ -8,7 +8,7 @@
<message name="IDS_PAGE_INFO_MIXED_CONTENT_SUMMARY" desc="A one-line summary at the top of the Page Info bubble (which shows when you click the security indicator) if the connection to the current website is using mainly using a secure connection but has some insecure parts (like insecurely loaded images).">
Your connection to this site is not fully secure
</message>
- <message name="IDS_PAGE_INFO_NOT_SECURE_SUMMARY" desc="A one-line summary at the top of the Page Info bubble (which shows when you click the security indicator) if the connection to the current website is secure.">
+ <message name="IDS_PAGE_INFO_NOT_SECURE_SUMMARY" desc="A one-line summary at the top of the Page Info bubble (which shows when you click the security indicator) if the connection to the current website is not secure.">
Your connection to this site is not secure
</message>
<message name="IDS_PAGE_INFO_MALWARE_SUMMARY" desc="A one-line summary at the top of the Page Info bubble (which shows when you click the security indicator) if the current website has been flagged as containing malware.">
@@ -60,10 +60,10 @@
<!-- Administrator-provided certificate notifications -->
<!-- TODO(crbug.com/717802): Only include this on Chrome OS. -->
- <message name="IDS_CERT_POLICY_PROVIDED_CERT_MESSAGE" desc="Text that is displayed in the Website Settings popup when using an administrator-provided certificate">
+ <message name="IDS_CERT_POLICY_PROVIDED_CERT_MESSAGE" desc="Text that is displayed in the Page Info popup when using an administrator-provided certificate">
You have accessed content using an administrator-provided certificate. Data you provide to <ph name="DOMAIN">$1<ex>www.google.com</ex></ph> can be intercepted by your administrator.
</message>
-
+
<!-- Certificate exception decision UI (after clicking through an SSL certificate error) -->
<!-- TODO(crbug.com/502473): Support revoking certificate overrides on iOS Page Info. -->
<message name="IDS_PAGE_INFO_INVALID_CERTIFICATE_DESCRIPTION" desc="A short paragraph to the user that security warnings are disabled. This is the case when the user has encountered a certificate error for the current site and chosen to override it.">
@@ -176,65 +176,68 @@
</if>
<!-- Permission names -->
- <message name="IDS_PAGE_INFO_TYPE_AUTOPLAY" desc="The label used for the autoplay permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_ADS" desc="The label used for the ads permission controls in the Page Info popup.">
+ Ads
+ </message>
+ <message name="IDS_PAGE_INFO_TYPE_AUTOPLAY" desc="The label used for the autoplay permission controls in the Page Info popup.">
Autoplay
</message>
- <message name="IDS_PAGE_INFO_TYPE_BACKGROUND_SYNC" desc="The label used for the background sync permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_BACKGROUND_SYNC" desc="The label used for the background sync permission controls in the Page Info popup.">
Background Sync
</message>
- <message name="IDS_PAGE_INFO_TYPE_IMAGES" desc="The label used for images permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_IMAGES" desc="The label used for images permission controls in the Page Info popup.">
Images
</message>
- <message name="IDS_PAGE_INFO_TYPE_JAVASCRIPT" desc="The label used for JavaScript permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_JAVASCRIPT" desc="The label used for JavaScript permission controls in the Page Info popup.">
JavaScript
</message>
- <message name="IDS_PAGE_INFO_TYPE_POPUPS" desc="The label used for popups permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_POPUPS" desc="The label used for popups permission controls in the Page Info popup.">
Popups
</message>
- <message name="IDS_PAGE_INFO_TYPE_FLASH" desc="The label used for Flash permissions in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_FLASH" desc="The label used for Flash permissions in the Page Info popup.">
Flash
</message>
- <message name="IDS_PAGE_INFO_TYPE_LOCATION" desc="The label used for location permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_LOCATION" desc="The label used for location permission controls in the Page Info popup.">
Location
</message>
- <message name="IDS_PAGE_INFO_TYPE_NOTIFICATIONS" desc="The label used for notifications permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_NOTIFICATIONS" desc="The label used for notifications permission controls in the Page Info popup.">
Notifications
</message>
- <message name="IDS_PAGE_INFO_TYPE_MIC" desc="The label used for the microphone permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_MIC" desc="The label used for the microphone permission controls in the Page Info popup.">
Microphone
</message>
- <message name="IDS_PAGE_INFO_TYPE_CAMERA" desc="The label used for the camera permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_CAMERA" desc="The label used for the camera permission controls in the Page Info popup.">
Camera
</message>
- <message name="IDS_PAGE_INFO_TYPE_MIDI_SYSEX" desc="The label used for MIDI system exclusive message permission controls in the Website Settings popup.">
+ <message name="IDS_PAGE_INFO_TYPE_MIDI_SYSEX" desc="The label used for MIDI system exclusive message permission controls in the Page Info popup.">
MIDI devices full control
</message>
<!-- TODO(crbug.com/716303): A few permissions are missing here. -->
<!-- Permission values -->
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_ALLOWED_BY_USER" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to allow by the user.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_ALLOWED_BY_USER" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to allow by the user.">
Allow
</message>
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_BLOCKED_BY_USER" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to block by the user.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_BLOCKED_BY_USER" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to block by the user.">
Block
</message>
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_ASK_BY_USER" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to ask by the user.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_ASK_BY_USER" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to ask by the user.">
Ask
</message>
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_DETECT_IMPORTANT_CONTENT_BY_USER" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to detect important content by the user.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_DETECT_IMPORTANT_CONTENT_BY_USER" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button was explicitly set to detect important content by the user.">
Detect
</message>
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_ALLOWED_BY_DEFAULT" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is allow.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_ALLOWED_BY_DEFAULT" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is allow.">
Allow (default)
</message>
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_BLOCKED_BY_DEFAULT" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is block.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_BLOCKED_BY_DEFAULT" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is block.">
Block (default)
</message>
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_ASK_BY_DEFAULT" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is ask.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_ASK_BY_DEFAULT" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is ask.">
Ask (default)
</message>
- <message name="IDS_PAGE_INFO_BUTTON_TEXT_DETECT_IMPORTANT_CONTENT_BY_DEFAULT" desc="The Website Settings popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is detect important content.">
+ <message name="IDS_PAGE_INFO_BUTTON_TEXT_DETECT_IMPORTANT_CONTENT_BY_DEFAULT" desc="The Page Info popup contains several buttons for opening dropdown menus and changing site permissions. This is the text of such a button if the permission controlled by the button is set to the default setting and the default settings is detect important content.">
Detect (default)
</message>
@@ -260,9 +263,8 @@
<message name="IDS_PAGE_INFO_MENU_ITEM_DETECT_IMPORTANT_CONTENT" desc="The text of the menu item of a permissions menu on the Page Info UI that sets the setting to detect important content.">
Always detect important content on this site
</message>
- <!-- IDS_PAGE_INFO_MENU_ITEM_SUBRESOURCE_FILTER_BLOCK is currently the same as IDS_PAGE_INFO_MENU_ITEM_BLOCK, but we use a different string identifier in the code for the flexibility to change it in the future. -->
- <message name="IDS_PAGE_INFO_MENU_ITEM_SUBRESOURCE_FILTER_BLOCK" desc="The text of the menu item of a permissions menu on the Page Info UI for the subresource filter permission in Block mode">
- Always block on this site
+ <message name="IDS_PAGE_INFO_MENU_ITEM_ADS_BLOCK" desc="The text of the menu item of a permissions menu on the Page Info UI for the ads permission in Block mode">
+ Block on this site
</message>
<!-- UI for device access granted to the site using the device chooser (e.g. USB, Bluetooth) -->
@@ -300,14 +302,14 @@
<message name="IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED" desc="The label used underneath a permission listed in the Page Info bubble if the permission was blocked by Chrome on behalf of the user.">
Automatically blocked
</message>
- <message name="IDS_PAGE_INFO_PERMISSION_SUBRESOURCE_FILTER_SUBTITLE" desc="The label used underneath the subresource filter permission in the Page Info UI. Used on desktop and Android platforms" translateable="false" formatter_data="android_java">
- Subresource filter activated
+ <message name="IDS_PAGE_INFO_PERMISSION_ADS_SUBTITLE" desc="The label used underneath the ads permission in the Page Info UI if the site is being considered for ad blocking. Used on both desktop and Android platforms" formatter_data="android_java">
+ Site tends to show intrusive ads
</message>
<!-- Permission change infobar. -->
<if expr="not is_android">
<message name="IDS_PAGE_INFO_INFOBAR_TEXT" desc="The string shown in the infobar after the user has changed site permissions settings, reminding them to reload the page in order for the new settings to take effect.">
- New site permissions settings will take effect after reloading the page.
+ To apply your updated settings to this site, reload this page
</message>
<message name="IDS_PAGE_INFO_INFOBAR_BUTTON" desc="The string used in the infobar button allowing the user to reload the page directly from the infobar.">
Reload
diff --git a/chromium/components/pairing/bluetooth_host_pairing_controller.cc b/chromium/components/pairing/bluetooth_host_pairing_controller.cc
index 2968c879956..6dfe60a6695 100644
--- a/chromium/components/pairing/bluetooth_host_pairing_controller.cc
+++ b/chromium/components/pairing/bluetooth_host_pairing_controller.cc
@@ -4,12 +4,15 @@
#include "components/pairing/bluetooth_host_pairing_controller.h"
+#include <utility>
+
#include "base/bind.h"
#include "base/hash.h"
#include "base/location.h"
#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
+#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "chromeos/system/devicetype.h"
#include "components/pairing/bluetooth_pairing_constants.h"
@@ -86,14 +89,9 @@ std::vector<BluetoothHostPairingController::InputDeviceInfo> GetDevices() {
} // namespace
BluetoothHostPairingController::BluetoothHostPairingController(
- const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
- : current_stage_(STAGE_NONE),
- connectivity_status_(CONNECTIVITY_UNTESTED),
- update_status_(UPDATE_STATUS_UNKNOWN),
- enrollment_status_(ENROLLMENT_STATUS_UNKNOWN),
- proto_decoder_(new ProtoDecoder(this)),
- file_task_runner_(file_task_runner),
- ptr_factory_(this) {}
+ scoped_refptr<base::TaskRunner> input_service_task_runner)
+ : proto_decoder_(base::MakeUnique<ProtoDecoder>(this)),
+ input_service_task_runner_(std::move(input_service_task_runner)) {}
BluetoothHostPairingController::~BluetoothHostPairingController() {
Reset();
@@ -150,6 +148,29 @@ void BluetoothHostPairingController::SendHostStatus() {
ptr_factory_.GetWeakPtr()));
}
+void BluetoothHostPairingController::SendErrorCodeAndMessage() {
+ if (error_code_ ==
+ static_cast<int>(HostPairingController::ErrorCode::ERROR_NONE)) {
+ return;
+ }
+
+ pairing_api::Error error_status;
+ error_status.set_api_version(kPairingAPIVersion);
+ error_status.mutable_parameters()->set_code(error_code_);
+ error_status.mutable_parameters()->set_description(error_message_);
+
+ int size = 0;
+ scoped_refptr<net::IOBuffer> io_buffer(
+ ProtoDecoder::SendError(error_status, &size));
+
+ controller_socket_->Send(
+ io_buffer, size,
+ base::Bind(&BluetoothHostPairingController::OnSendComplete,
+ ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothHostPairingController::OnSendError,
+ ptr_factory_.GetWeakPtr()));
+}
+
void BluetoothHostPairingController::Reset() {
if (adapter_.get()) {
device::BluetoothDevice* device =
@@ -167,7 +188,7 @@ void BluetoothHostPairingController::Reset() {
void BluetoothHostPairingController::OnGetAdapter(
scoped_refptr<device::BluetoothAdapter> adapter) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!adapter_.get());
adapter_ = adapter;
@@ -180,7 +201,7 @@ void BluetoothHostPairingController::OnGetAdapter(
}
void BluetoothHostPairingController::SetPowered() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (adapter_->IsPowered()) {
was_powered_ = true;
OnSetPowered();
@@ -195,7 +216,7 @@ void BluetoothHostPairingController::SetPowered() {
}
void BluetoothHostPairingController::OnSetPowered() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
adapter_->AddPairingDelegate(
this, device::BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
@@ -212,7 +233,7 @@ void BluetoothHostPairingController::OnSetPowered() {
void BluetoothHostPairingController::OnCreateService(
scoped_refptr<device::BluetoothSocket> socket) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
service_socket_ = socket;
service_socket_->Accept(
@@ -234,7 +255,7 @@ void BluetoothHostPairingController::OnAccept(
scoped_refptr<device::BluetoothSocket> socket) {
controller_device_address_ = device->GetAddress();
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
adapter_->SetDiscoverable(
false,
base::Bind(&BluetoothHostPairingController::OnSetDiscoverable,
@@ -254,7 +275,7 @@ void BluetoothHostPairingController::OnAccept(
}
void BluetoothHostPairingController::OnSetDiscoverable(bool change_stage) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (change_stage) {
DCHECK_EQ(current_stage_, STAGE_NONE);
ChangeStage(STAGE_WAITING_FOR_CONTROLLER);
@@ -265,7 +286,7 @@ void BluetoothHostPairingController::OnSendComplete(int bytes_sent) {}
void BluetoothHostPairingController::OnReceiveComplete(
int bytes, scoped_refptr<net::IOBuffer> io_buffer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
proto_decoder_->DecodeIOBuffer(bytes, io_buffer);
if (controller_socket_.get()) {
@@ -349,7 +370,7 @@ void BluetoothHostPairingController::OnForget() {
}
base::PostTaskAndReplyWithResult(
- file_task_runner_.get(), FROM_HERE, base::Bind(&GetDevices),
+ input_service_task_runner_.get(), FROM_HERE, base::Bind(&GetDevices),
base::Bind(&BluetoothHostPairingController::PowerOffAdapterIfApplicable,
ptr_factory_.GetWeakPtr()));
}
@@ -386,7 +407,7 @@ void BluetoothHostPairingController::OnConfigureHostMessage(
void BluetoothHostPairingController::OnPairDevicesMessage(
const pairing_api::PairDevices& message) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
enrollment_domain_ = message.parameters().enrolling_domain();
ChangeStage(STAGE_ENROLLING);
for (Observer& observer : observers_)
@@ -395,7 +416,7 @@ void BluetoothHostPairingController::OnPairDevicesMessage(
void BluetoothHostPairingController::OnCompleteSetupMessage(
const pairing_api::CompleteSetup& message) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (current_stage_ != STAGE_ENROLLMENT_SUCCESS) {
ChangeStage(STAGE_ENROLLMENT_ERROR);
} else {
@@ -412,14 +433,14 @@ void BluetoothHostPairingController::OnErrorMessage(
void BluetoothHostPairingController::OnRebootMessage(
const pairing_api::Reboot& message) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (Observer& observer : observers_)
observer.RebootHostRequested();
}
void BluetoothHostPairingController::OnAddNetworkMessage(
const pairing_api::AddNetwork& message) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (Observer& observer : observers_)
observer.AddNetworkRequested(message.parameters().onc_spec());
}
@@ -476,9 +497,12 @@ std::string BluetoothHostPairingController::GetEnrollmentDomain() {
void BluetoothHostPairingController::OnNetworkConnectivityChanged(
Connectivity connectivity_status) {
connectivity_status_ = connectivity_status;
- if (connectivity_status == CONNECTIVITY_NONE)
+ if (connectivity_status == CONNECTIVITY_NONE) {
ChangeStage(STAGE_SETUP_NETWORK_ERROR);
- SendHostStatus();
+ SendErrorCodeAndMessage();
+ } else {
+ SendHostStatus();
+ }
}
void BluetoothHostPairingController::OnUpdateStatusChanged(
@@ -492,15 +516,16 @@ void BluetoothHostPairingController::OnUpdateStatusChanged(
void BluetoothHostPairingController::OnEnrollmentStatusChanged(
EnrollmentStatus enrollment_status) {
DCHECK_EQ(current_stage_, STAGE_ENROLLING);
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
enrollment_status_ = enrollment_status;
if (enrollment_status == ENROLLMENT_STATUS_SUCCESS) {
ChangeStage(STAGE_ENROLLMENT_SUCCESS);
+ SendHostStatus();
} else if (enrollment_status == ENROLLMENT_STATUS_FAILURE) {
ChangeStage(STAGE_ENROLLMENT_ERROR);
+ SendErrorCodeAndMessage();
}
- SendHostStatus();
}
void BluetoothHostPairingController::SetPermanentId(
@@ -508,6 +533,13 @@ void BluetoothHostPairingController::SetPermanentId(
permanent_id_ = permanent_id;
}
+void BluetoothHostPairingController::SetErrorCodeAndMessage(
+ int error_code,
+ const std::string& error_message) {
+ error_code_ = error_code;
+ error_message_ = error_message;
+}
+
void BluetoothHostPairingController::RequestPinCode(
device::BluetoothDevice* device) {
// Disallow unknown device.
diff --git a/chromium/components/pairing/bluetooth_host_pairing_controller.h b/chromium/components/pairing/bluetooth_host_pairing_controller.h
index a9a9d4010a2..5c01dcfcdd6 100644
--- a/chromium/components/pairing/bluetooth_host_pairing_controller.h
+++ b/chromium/components/pairing/bluetooth_host_pairing_controller.h
@@ -22,7 +22,7 @@
#include "device/hid/input_service_linux.h"
namespace base {
-class SingleThreadTaskRunner;
+class TaskRunner;
}
namespace device {
@@ -62,7 +62,7 @@ class BluetoothHostPairingController
};
explicit BluetoothHostPairingController(
- const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner);
+ scoped_refptr<base::TaskRunner> input_service_task_runner);
~BluetoothHostPairingController() override;
// These functions should be only used in tests.
@@ -74,6 +74,7 @@ class BluetoothHostPairingController
void ChangeStage(Stage new_stage);
void SendHostStatus();
+ void SendErrorCodeAndMessage();
void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter);
void SetPowered();
@@ -109,6 +110,8 @@ class BluetoothHostPairingController
void OnUpdateStatusChanged(UpdateStatus update_status) override;
void OnEnrollmentStatusChanged(EnrollmentStatus enrollment_status) override;
void SetPermanentId(const std::string& permanent_id) override;
+ void SetErrorCodeAndMessage(int error_code,
+ const std::string& error_message) override;
void Reset() override;
// ProtoDecoder::Observer:
@@ -138,26 +141,37 @@ class BluetoothHostPairingController
uint32_t passkey) override;
void AuthorizePairing(device::BluetoothDevice* device) override;
- Stage current_stage_;
+ Stage current_stage_ = STAGE_NONE;
std::string confirmation_code_;
std::string enrollment_domain_;
- Connectivity connectivity_status_;
- UpdateStatus update_status_;
- EnrollmentStatus enrollment_status_;
+ Connectivity connectivity_status_ = CONNECTIVITY_UNTESTED;
+ UpdateStatus update_status_ = UPDATE_STATUS_UNKNOWN;
+ EnrollmentStatus enrollment_status_ = ENROLLMENT_STATUS_UNKNOWN;
std::string permanent_id_;
std::string controller_device_address_;
bool was_powered_ = false;
+ // The format of the |error_code_| is:
+ // [0, "no error"]
+ // [1*, "network error"]
+ // [2*, "authentication error"], e.g., [21, "Service unavailable"], ...
+ // [3*, "enrollment error"], e.g., [31, "DMserver registration error"], ...
+ // [4*, "other error"]
+ // The |error_code_| and |error_message_| will pass over to the master device
+ // to assist error diagnosis.
+ int error_code_ = 0;
+ std::string error_message_;
+
scoped_refptr<device::BluetoothAdapter> adapter_;
scoped_refptr<device::BluetoothSocket> service_socket_;
scoped_refptr<device::BluetoothSocket> controller_socket_;
std::unique_ptr<ProtoDecoder> proto_decoder_;
TestDelegate* delegate_ = nullptr;
- scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
- base::ThreadChecker thread_checker_;
+ scoped_refptr<base::TaskRunner> input_service_task_runner_;
+ THREAD_CHECKER(thread_checker_);
base::ObserverList<Observer> observers_;
- base::WeakPtrFactory<BluetoothHostPairingController> ptr_factory_;
+ base::WeakPtrFactory<BluetoothHostPairingController> ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(BluetoothHostPairingController);
};
diff --git a/chromium/components/pairing/fake_host_pairing_controller.cc b/chromium/components/pairing/fake_host_pairing_controller.cc
index f0300f2dd54..b6bfc5cdfef 100644
--- a/chromium/components/pairing/fake_host_pairing_controller.cc
+++ b/chromium/components/pairing/fake_host_pairing_controller.cc
@@ -141,6 +141,10 @@ void FakeHostPairingController::SetPermanentId(
const std::string& permanent_id) {
}
+void FakeHostPairingController::SetErrorCodeAndMessage(
+ int error_code,
+ const std::string& error_message) {}
+
void FakeHostPairingController::Reset() {
}
diff --git a/chromium/components/pairing/fake_host_pairing_controller.h b/chromium/components/pairing/fake_host_pairing_controller.h
index 01ae9226a71..437fbb74497 100644
--- a/chromium/components/pairing/fake_host_pairing_controller.h
+++ b/chromium/components/pairing/fake_host_pairing_controller.h
@@ -49,6 +49,8 @@ class FakeHostPairingController
void OnUpdateStatusChanged(UpdateStatus update_status) override;
void OnEnrollmentStatusChanged(EnrollmentStatus enrollment_status) override;
void SetPermanentId(const std::string& permanent_id) override;
+ void SetErrorCodeAndMessage(int error_code,
+ const std::string& error_message) override;
void Reset() override;
// HostPairingController::Observer:
diff --git a/chromium/components/pairing/host_pairing_controller.h b/chromium/components/pairing/host_pairing_controller.h
index 76fdd66e746..4e6b6122c4c 100644
--- a/chromium/components/pairing/host_pairing_controller.h
+++ b/chromium/components/pairing/host_pairing_controller.h
@@ -51,6 +51,14 @@ class HostPairingController {
ENROLLMENT_STATUS_SUCCESS,
};
+ enum class ErrorCode : int {
+ ERROR_NONE = 0,
+ NETWORK_ERROR,
+ AUTH_ERROR,
+ ENROLL_ERROR,
+ OTHER_ERROR,
+ };
+
class Observer {
public:
Observer();
@@ -115,6 +123,9 @@ class HostPairingController {
// Set the permanent id assigned during enrollment.
virtual void SetPermanentId(const std::string& permanent_id) = 0;
+ virtual void SetErrorCodeAndMessage(int error_code,
+ const std::string& error_message) = 0;
+
// Reset the controller.
virtual void Reset() = 0;
diff --git a/chromium/components/pairing/shark_connection_listener.cc b/chromium/components/pairing/shark_connection_listener.cc
index 85c554170a5..e5eb97d5764 100644
--- a/chromium/components/pairing/shark_connection_listener.cc
+++ b/chromium/components/pairing/shark_connection_listener.cc
@@ -7,16 +7,18 @@
#include <utility>
#include "base/logging.h"
+#include "base/task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "components/pairing/bluetooth_host_pairing_controller.h"
namespace pairing_chromeos {
SharkConnectionListener::SharkConnectionListener(
- const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner,
+ scoped_refptr<base::TaskRunner> input_service_task_runner,
OnConnectedCallback callback)
: callback_(callback) {
- controller_.reset(new BluetoothHostPairingController(file_task_runner));
+ controller_.reset(
+ new BluetoothHostPairingController(std::move(input_service_task_runner)));
controller_->AddObserver(this);
controller_->StartPairing();
}
diff --git a/chromium/components/pairing/shark_connection_listener.h b/chromium/components/pairing/shark_connection_listener.h
index e940fe05afc..9b0b49dfb20 100644
--- a/chromium/components/pairing/shark_connection_listener.h
+++ b/chromium/components/pairing/shark_connection_listener.h
@@ -13,7 +13,7 @@
#include "components/pairing/host_pairing_controller.h"
namespace base {
-class SingleThreadTaskRunner;
+class TaskRunner;
}
namespace pairing_chromeos {
@@ -29,7 +29,7 @@ class SharkConnectionListener : public HostPairingController::Observer {
base::Callback<void(std::unique_ptr<HostPairingController>)>;
SharkConnectionListener(
- const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner,
+ scoped_refptr<base::TaskRunner> input_service_task_runner,
OnConnectedCallback callback);
~SharkConnectionListener() override;
diff --git a/chromium/components/password_manager/DEPS b/chromium/components/password_manager/DEPS
index d5d8ecef063..174fecf52c8 100644
--- a/chromium/components/password_manager/DEPS
+++ b/chromium/components/password_manager/DEPS
@@ -12,6 +12,7 @@ include_rules = [
"+net/traffic_annotation",
"+net/url_request",
"+sql",
+ "+third_party/boringssl/src/include/openssl",
"+third_party/re2",
"+third_party/sqlite/sqlite3.h",
"+ui",
diff --git a/chromium/components/password_manager/OWNERS b/chromium/components/password_manager/OWNERS
index 568d03ba65c..fe38e2ac8e0 100644
--- a/chromium/components/password_manager/OWNERS
+++ b/chromium/components/password_manager/OWNERS
@@ -1,5 +1,6 @@
dvadym@chromium.org
engedy@chromium.org
+kolos@chromium.org
melandory@chromium.org
mkwst@chromium.org
vabr@chromium.org
diff --git a/chromium/components/password_manager/content/browser/BUILD.gn b/chromium/components/password_manager/content/browser/BUILD.gn
index c61b656c45d..3081181f703 100644
--- a/chromium/components/password_manager/content/browser/BUILD.gn
+++ b/chromium/components/password_manager/content/browser/BUILD.gn
@@ -14,8 +14,6 @@ static_library("browser") {
"credential_manager_impl.h",
"password_manager_internals_service_factory.cc",
"password_manager_internals_service_factory.h",
- "visible_password_observer.cc",
- "visible_password_observer.h",
]
public_deps = [
@@ -24,7 +22,6 @@ static_library("browser") {
"//components/autofill/content/common:mojo_interfaces",
"//components/autofill/core/common",
"//components/keyed_service/content",
- "//components/password_manager/content/common:mojo_interfaces",
"//components/password_manager/core/browser",
"//components/password_manager/core/common",
"//components/prefs",
diff --git a/chromium/components/password_manager/content/browser/DEPS b/chromium/components/password_manager/content/browser/DEPS
index d79664a5e7b..5897af94fe4 100644
--- a/chromium/components/password_manager/content/browser/DEPS
+++ b/chromium/components/password_manager/content/browser/DEPS
@@ -6,5 +6,4 @@ include_rules = [
"+content/public/browser",
"+net",
"+services/service_manager/public/cpp",
- "+third_party/WebKit/public/platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom.h",
]
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 3a847908681..6e3e0af169d 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
@@ -4,15 +4,17 @@
#include "components/password_manager/content/browser/content_password_manager_driver.h"
+#include <utility>
+
#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"
#include "components/password_manager/content/browser/bad_message.h"
#include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
-#include "components/password_manager/content/browser/visible_password_observer.h"
#include "components/password_manager/core/browser/log_manager.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_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"
@@ -41,10 +43,6 @@ ContentPasswordManagerDriver::ContentPasswordManagerDriver(
is_main_frame_(render_frame_host->GetParent() == nullptr),
password_manager_binding_(this),
weak_factory_(this) {
- // Does nothing if a VisiblePasswordObserver has already been created
- // for this WebContents.
- VisiblePasswordObserver::CreateForWebContents(
- content::WebContents::FromRenderFrameHost(render_frame_host_));
// 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
@@ -78,11 +76,6 @@ void ContentPasswordManagerDriver::BindRequest(
password_manager_binding_.Bind(std::move(request));
}
-void ContentPasswordManagerDriver::BindSensitiveInputVisibilityServiceRequest(
- blink::mojom::SensitiveInputVisibilityServiceRequest request) {
- sensitive_input_visibility_bindings_.AddBinding(this, std::move(request));
-}
-
void ContentPasswordManagerDriver::FillPasswordForm(
const autofill::PasswordFormFillData& form_data) {
const int key = next_free_key_++;
@@ -225,19 +218,6 @@ void ContentPasswordManagerDriver::OnFocusedPasswordFormFound(
GetPasswordManager()->OnPasswordFormForceSaveRequested(this, password_form);
}
-void ContentPasswordManagerDriver::PasswordFieldVisibleInInsecureContext() {
- VisiblePasswordObserver* observer = VisiblePasswordObserver::FromWebContents(
- content::WebContents::FromRenderFrameHost(render_frame_host_));
- observer->RenderFrameHasVisiblePasswordField(render_frame_host_);
-}
-
-void ContentPasswordManagerDriver::
- AllPasswordFieldsInInsecureContextInvisible() {
- VisiblePasswordObserver* observer = VisiblePasswordObserver::FromWebContents(
- content::WebContents::FromRenderFrameHost(render_frame_host_));
- observer->RenderFrameHasNoVisiblePasswordFields(render_frame_host_);
-}
-
void ContentPasswordManagerDriver::DidNavigateFrame(
content::NavigationHandle* navigation_handle) {
// Clear page specific data after main frame navigation.
@@ -272,8 +252,7 @@ void ContentPasswordManagerDriver::PasswordNoLongerGenerated(
password_form.origin,
BadMessageReason::CPMD_BAD_ORIGIN_PASSWORD_NO_LONGER_GENERATED))
return;
- GetPasswordManager()->SetHasGeneratedPasswordForForm(this, password_form,
- false);
+ GetPasswordManager()->OnPasswordNoLongerGenerated(password_form);
}
void ContentPasswordManagerDriver::SaveGenerationFieldDetectedByClassifier(
@@ -319,6 +298,10 @@ void ContentPasswordManagerDriver::RecordSavePasswordProgress(
client_->GetLogManager()->LogSavePasswordProgress(log);
}
+void ContentPasswordManagerDriver::UserModifiedPasswordField() {
+ client_->GetMetricsRecorder().RecordUserModifiedPasswordField();
+}
+
bool ContentPasswordManagerDriver::CheckChildProcessSecurityPolicy(
const GURL& url,
BadMessageReason reason) {
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 c7a867a23ec..8f948f83440 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
@@ -6,6 +6,7 @@
#define COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_CONTENT_PASSWORD_MANAGER_DRIVER_H_
#include <map>
+#include <string>
#include <vector>
#include "base/compiler_specific.h"
@@ -20,7 +21,6 @@
#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 "third_party/WebKit/public/platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom.h"
namespace autofill {
struct PasswordForm;
@@ -38,8 +38,7 @@ enum class BadMessageReason;
// The lifetime is managed by the ContentPasswordManagerDriverFactory.
class ContentPasswordManagerDriver
: public PasswordManagerDriver,
- public autofill::mojom::PasswordManagerDriver,
- public blink::mojom::SensitiveInputVisibilityService {
+ public autofill::mojom::PasswordManagerDriver {
public:
ContentPasswordManagerDriver(content::RenderFrameHost* render_frame_host,
PasswordManagerClient* client,
@@ -51,8 +50,6 @@ class ContentPasswordManagerDriver
content::RenderFrameHost* render_frame_host);
void BindRequest(autofill::mojom::PasswordManagerDriverRequest request);
- void BindSensitiveInputVisibilityServiceRequest(
- blink::mojom::SensitiveInputVisibilityServiceRequest request);
// PasswordManagerDriver implementation.
void FillPasswordForm(
@@ -107,6 +104,7 @@ class ContentPasswordManagerDriver
void ShowNotSecureWarning(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;
@@ -117,10 +115,6 @@ class ContentPasswordManagerDriver
const std::vector<autofill::PasswordForm>& forms);
void OnFocusedPasswordFormFound(const autofill::PasswordForm& password_form);
- // blink::mojom::SensitiveInputVisibility:
- void PasswordFieldVisibleInInsecureContext() override;
- void AllPasswordFieldsInInsecureContextInvisible() override;
-
private:
bool CheckChildProcessSecurityPolicy(const GURL& url,
BadMessageReason reason);
@@ -157,8 +151,6 @@ class ContentPasswordManagerDriver
mojo::Binding<autofill::mojom::PasswordManagerDriver>
password_manager_binding_;
- mojo::BindingSet<blink::mojom::SensitiveInputVisibilityService>
- sensitive_input_visibility_bindings_;
base::WeakPtrFactory<ContentPasswordManagerDriver> weak_factory_;
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 c1dddc0ee88..95cd740ebe1 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,9 +75,8 @@ ContentPasswordManagerDriverFactory::FromWebContents(
// static
void ContentPasswordManagerDriverFactory::BindPasswordManagerDriver(
- content::RenderFrameHost* render_frame_host,
- const service_manager::BindSourceInfo& source_info,
- autofill::mojom::PasswordManagerDriverRequest request) {
+ autofill::mojom::PasswordManagerDriverRequest request,
+ content::RenderFrameHost* render_frame_host) {
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents)
@@ -98,31 +97,6 @@ void ContentPasswordManagerDriverFactory::BindPasswordManagerDriver(
driver->BindRequest(std::move(request));
}
-// static
-void ContentPasswordManagerDriverFactory::BindSensitiveInputVisibilityService(
- content::RenderFrameHost* render_frame_host,
- const service_manager::BindSourceInfo& source_info,
- blink::mojom::SensitiveInputVisibilityServiceRequest request) {
- 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->BindSensitiveInputVisibilityServiceRequest(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 12c3aef641e..652529cee2a 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
@@ -18,7 +18,6 @@
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "content/public/browser/web_contents_observer.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
-#include "third_party/WebKit/public/platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom.h"
namespace content {
class WebContents;
@@ -43,14 +42,8 @@ class ContentPasswordManagerDriverFactory
content::WebContents* web_contents);
static void BindPasswordManagerDriver(
- content::RenderFrameHost* render_frame_host,
- const service_manager::BindSourceInfo& source_info,
- autofill::mojom::PasswordManagerDriverRequest request);
-
- static void BindSensitiveInputVisibilityService(
- content::RenderFrameHost* render_frame_host,
- const service_manager::BindSourceInfo& source_info,
- blink::mojom::SensitiveInputVisibilityServiceRequest request);
+ 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 69741b25a5a..0283690a766 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
@@ -4,6 +4,9 @@
#include "components/password_manager/content/browser/content_password_manager_driver.h"
+#include <memory>
+#include <utility>
+
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
@@ -219,200 +222,6 @@ TEST_P(ContentPasswordManagerDriverTest, SendLoggingStateAfterLogManagerReady) {
EXPECT_EQ(should_allow_logging, logging_activated);
}
-// Tests that password visibility notifications are forwarded to the
-// WebContents.
-TEST_P(ContentPasswordManagerDriverTest, PasswordVisibility) {
- std::unique_ptr<ContentPasswordManagerDriver> driver(
- new ContentPasswordManagerDriver(main_rfh(), &password_manager_client_,
- &autofill_client_));
-
- // Do a mock navigation so that there is a navigation entry on which
- // password visibility gets recorded.
- GURL url("http://example.test");
- NavigateAndCommit(url);
- content::NavigationEntry* entry =
- web_contents()->GetController().GetVisibleEntry();
- ASSERT_TRUE(entry);
- EXPECT_EQ(url, entry->GetURL());
- EXPECT_FALSE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- driver->PasswordFieldVisibleInInsecureContext();
-
- // Check that the password visibility notification was passed on to
- // the WebContents (and from there to the SSLStatus).
- entry = web_contents()->GetController().GetVisibleEntry();
- ASSERT_TRUE(entry);
- EXPECT_EQ(url, entry->GetURL());
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- // If the password field becomes hidden, then the flag should be unset
- // on the SSLStatus.
- driver->AllPasswordFieldsInInsecureContextInvisible();
- EXPECT_FALSE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-}
-
-// Tests that password visibility notifications from subframes are
-// recorded correctly.
-TEST_P(ContentPasswordManagerDriverTest, PasswordVisibilityWithSubframe) {
- // Do a mock navigation so that there is a navigation entry on which
- // password visibility gets recorded.
- GURL url("http://example.test");
- NavigateAndCommit(url);
-
- // Create a subframe with a password field and check that
- // notifications for it are handled properly.
- content::RenderFrameHost* subframe =
- content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
- auto subframe_driver = base::MakeUnique<ContentPasswordManagerDriver>(
- subframe, &password_manager_client_, &autofill_client_);
- content::RenderFrameHostTester* subframe_tester =
- content::RenderFrameHostTester::For(subframe);
- subframe_tester->SimulateNavigationStart(GURL("http://example2.test"));
- subframe_tester->SimulateNavigationCommit(GURL("http://example2.test"));
- subframe_driver->PasswordFieldVisibleInInsecureContext();
-
- content::NavigationEntry* entry =
- web_contents()->GetController().GetVisibleEntry();
- ASSERT_TRUE(entry);
- EXPECT_EQ(url, entry->GetURL());
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- subframe_driver->AllPasswordFieldsInInsecureContextInvisible();
- EXPECT_FALSE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-}
-
-// Tests that password visibility notifications are recorded correctly
-// when there is a password field in both the main frame and a subframe.
-TEST_P(ContentPasswordManagerDriverTest,
- PasswordVisibilityWithMainFrameAndSubframe) {
- std::unique_ptr<ContentPasswordManagerDriver> driver(
- new ContentPasswordManagerDriver(main_rfh(), &password_manager_client_,
- &autofill_client_));
- // Do a mock navigation so that there is a navigation entry on which
- // password visibility gets recorded.
- GURL url("http://example.test");
- NavigateAndCommit(url);
-
- driver->PasswordFieldVisibleInInsecureContext();
- content::NavigationEntry* entry =
- web_contents()->GetController().GetVisibleEntry();
- ASSERT_TRUE(entry);
- EXPECT_EQ(url, entry->GetURL());
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- // Create a subframe with a password field and check that
- // notifications for it are handled properly.
- content::RenderFrameHost* subframe =
- content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
- auto subframe_driver = base::MakeUnique<ContentPasswordManagerDriver>(
- subframe, &password_manager_client_, &autofill_client_);
- content::RenderFrameHostTester* subframe_tester =
- content::RenderFrameHostTester::For(subframe);
- subframe_tester->SimulateNavigationStart(GURL("http://example2.test"));
- subframe_tester->SimulateNavigationCommit(GURL("http://example2.test"));
- subframe_driver->PasswordFieldVisibleInInsecureContext();
-
- entry = web_contents()->GetController().GetVisibleEntry();
- ASSERT_TRUE(entry);
- EXPECT_EQ(url, entry->GetURL());
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- subframe_driver->AllPasswordFieldsInInsecureContextInvisible();
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- driver->AllPasswordFieldsInInsecureContextInvisible();
- EXPECT_FALSE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-}
-
-// Tests that when a frame is deleted, its password visibility flag gets
-// unset.
-TEST_P(ContentPasswordManagerDriverTest,
- PasswordVisibilityWithSubframeDeleted) {
- // Do a mock navigation so that there is a navigation entry on which
- // password visibility gets recorded.
- GURL url("http://example.test");
- NavigateAndCommit(url);
-
- // Create a subframe with a password field.
- content::RenderFrameHost* subframe =
- content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
- auto subframe_driver = base::MakeUnique<ContentPasswordManagerDriver>(
- subframe, &password_manager_client_, &autofill_client_);
- content::RenderFrameHostTester* subframe_tester =
- content::RenderFrameHostTester::For(subframe);
- subframe_tester->SimulateNavigationStart(GURL("http://example2.test"));
- subframe_tester->SimulateNavigationCommit(GURL("http://example2.test"));
- subframe_driver->PasswordFieldVisibleInInsecureContext();
-
- content::NavigationEntry* entry =
- web_contents()->GetController().GetVisibleEntry();
- ASSERT_TRUE(entry);
- EXPECT_EQ(url, entry->GetURL());
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- subframe_tester->Detach();
- EXPECT_FALSE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- // Check that the subframe's flag isn't hanging around preventing the
- // warning from being removed.
- std::unique_ptr<ContentPasswordManagerDriver> driver(
- new ContentPasswordManagerDriver(main_rfh(), &password_manager_client_,
- &autofill_client_));
- driver->PasswordFieldVisibleInInsecureContext();
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
- driver->AllPasswordFieldsInInsecureContextInvisible();
- EXPECT_FALSE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-}
-
-// Tests that a cross-process navigation does not remove the password
-// field flag when the RenderFrameHost for the original process goes
-// away. Regression test for https://crbug.com/664674
-TEST_F(ContentPasswordManagerDriverTest,
- RenderFrameHostDeletionOnCrossProcessNavigation) {
- NavigateAndCommit(GURL("http://example.test"));
-
- content::RenderFrameHostTester* old_rfh_tester =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame());
- content::WebContentsTester* tester =
- content::WebContentsTester::For(web_contents());
-
- controller().LoadURL(GURL("http://example2.test"), content::Referrer(),
- ui::PAGE_TRANSITION_TYPED, std::string());
- content::RenderFrameHost* pending_rfh = tester->GetPendingMainFrame();
- ASSERT_TRUE(pending_rfh);
- int entry_id = controller().GetPendingEntry()->GetUniqueID();
- tester->TestDidNavigate(pending_rfh, entry_id, true,
- GURL("http://example2.test"),
- ui::PAGE_TRANSITION_TYPED);
-
- auto driver = base::MakeUnique<ContentPasswordManagerDriver>(
- main_rfh(), &password_manager_client_, &autofill_client_);
- driver->PasswordFieldVisibleInInsecureContext();
- content::NavigationEntry* entry =
- web_contents()->GetController().GetVisibleEntry();
- ASSERT_TRUE(entry);
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-
- // After the old RenderFrameHost is deleted, the password flag should still be
- // set.
- old_rfh_tester->SimulateSwapOutACK();
- EXPECT_TRUE(!!(entry->GetSSL().content_status &
- content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP));
-}
TEST_F(ContentPasswordManagerDriverTest, ClearPasswordsOnAutofill) {
std::unique_ptr<ContentPasswordManagerDriver> driver(
diff --git a/chromium/components/password_manager/content/browser/credential_manager_impl.cc b/chromium/components/password_manager/content/browser/credential_manager_impl.cc
index d423a3a64f6..e50d8372a89 100644
--- a/chromium/components/password_manager/content/browser/credential_manager_impl.cc
+++ b/chromium/components/password_manager/content/browser/credential_manager_impl.cc
@@ -14,7 +14,7 @@
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
-#include "components/password_manager/core/browser/affiliated_match_helper.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
#include "components/password_manager/core/browser/credential_manager_logger.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/form_saver.h"
@@ -107,6 +107,7 @@ void CredentialManagerImpl::Store(const CredentialInfo& credential,
form_manager_ = base::MakeUnique<CredentialManagerPasswordFormManager>(
client_, GetDriver(), *observed_form, std::move(form), this, nullptr,
std::move(form_fetcher));
+ form_manager_->Init(nullptr);
}
void CredentialManagerImpl::OnProvisionalSaveComplete() {
diff --git a/chromium/components/password_manager/content/browser/credential_manager_impl.h b/chromium/components/password_manager/content/browser/credential_manager_impl.h
index 4e6369dfe30..8a4bf3d66e5 100644
--- a/chromium/components/password_manager/content/browser/credential_manager_impl.h
+++ b/chromium/components/password_manager/content/browser/credential_manager_impl.h
@@ -10,7 +10,6 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "components/password_manager/content/common/credential_manager.mojom.h"
#include "components/password_manager/core/browser/credential_manager_password_form_manager.h"
#include "components/password_manager/core/browser/credential_manager_pending_prevent_silent_access_task.h"
#include "components/password_manager/core/browser/credential_manager_pending_request_task.h"
@@ -19,6 +18,7 @@
#include "components/prefs/pref_member.h"
#include "content/public/browser/web_contents_observer.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.h"
class GURL;
diff --git a/chromium/components/password_manager/content/browser/credential_manager_impl_unittest.cc b/chromium/components/password_manager/content/browser/credential_manager_impl_unittest.cc
index 0e7a132ff74..af7f513ebf5 100644
--- a/chromium/components/password_manager/content/browser/credential_manager_impl_unittest.cc
+++ b/chromium/components/password_manager/content/browser/credential_manager_impl_unittest.cc
@@ -18,12 +18,11 @@
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h"
#include "components/password_manager/core/browser/credential_manager_password_form_manager.h"
-#include "components/password_manager/core/browser/mock_affiliated_match_helper.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
@@ -35,6 +34,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -103,7 +103,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
const CredentialsCallback& callback) override {
EXPECT_FALSE(local_forms.empty());
const autofill::PasswordForm* form = local_forms[0].get();
- base::ThreadTaskRunnerHandle::Get()->PostTask(
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(callback, base::Owned(new autofill::PasswordForm(*form))));
std::vector<autofill::PasswordForm*> raw_forms(local_forms.size());
@@ -165,10 +165,7 @@ base::WeakPtr<PasswordManagerDriver> TestCredentialManagerImpl::GetDriver() {
}
void RunAllPendingTasks() {
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
- run_loop.Run();
+ content::RunAllBlockingPoolTasksUntilIdle();
}
class SlightlyLessStubbyPasswordManagerDriver
diff --git a/chromium/components/password_manager/content/browser/visible_password_observer.cc b/chromium/components/password_manager/content/browser/visible_password_observer.cc
deleted file mode 100644
index 671b34fe0a9..00000000000
--- a/chromium/components/password_manager/content/browser/visible_password_observer.cc
+++ /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.
-
-#include "components/password_manager/content/browser/visible_password_observer.h"
-
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/common/origin_util.h"
-
-DEFINE_WEB_CONTENTS_USER_DATA_KEY(password_manager::VisiblePasswordObserver);
-
-namespace password_manager {
-
-VisiblePasswordObserver::~VisiblePasswordObserver() {}
-
-void VisiblePasswordObserver::RenderFrameHasVisiblePasswordField(
- content::RenderFrameHost* render_frame_host) {
- frames_with_visible_password_fields_.insert(render_frame_host);
- MaybeNotifyPasswordInputShownOnHttp();
-}
-
-void VisiblePasswordObserver::RenderFrameHasNoVisiblePasswordFields(
- content::RenderFrameHost* render_frame_host) {
- frames_with_visible_password_fields_.erase(render_frame_host);
- MaybeNotifyAllFieldsInvisible();
-}
-
-void VisiblePasswordObserver::RenderFrameDeleted(
- content::RenderFrameHost* render_frame_host) {
- // If a renderer process crashes, it won't send notifications that
- // the password fields have been hidden, so watch for crashing
- // processes and remove them from
- // |frames_with_visible_password_fields_|.
- frames_with_visible_password_fields_.erase(render_frame_host);
- MaybeNotifyAllFieldsInvisible();
-}
-
-VisiblePasswordObserver::VisiblePasswordObserver(
- content::WebContents* web_contents)
- : content::WebContentsObserver(web_contents), web_contents_(web_contents) {}
-
-void VisiblePasswordObserver::MaybeNotifyPasswordInputShownOnHttp() {
- if (!content::IsOriginSecure(web_contents_->GetVisibleURL())) {
- web_contents_->OnPasswordInputShownOnHttp();
- }
-}
-
-void VisiblePasswordObserver::MaybeNotifyAllFieldsInvisible() {
- if (frames_with_visible_password_fields_.empty() &&
- !content::IsOriginSecure(web_contents_->GetVisibleURL())) {
- web_contents_->OnAllPasswordInputsHiddenOnHttp();
- }
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/content/browser/visible_password_observer.h b/chromium/components/password_manager/content/browser/visible_password_observer.h
deleted file mode 100644
index 40d9716b658..00000000000
--- a/chromium/components/password_manager/content/browser/visible_password_observer.h
+++ /dev/null
@@ -1,74 +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_PASSWORD_MANAGER_CONTENT_BROWSER_VISIBLE_PASSWORD_OBSERVER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_VISIBLE_PASSWORD_OBSERVER_H_
-
-#include <set>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-namespace content {
-class RenderFrameHost;
-} // namespace content
-
-namespace password_manager {
-
-// This class tracks password visibility notifications for the
-// RenderFrameHosts in a WebContents. There is one
-// VisiblePasswordObserver per WebContents. When a RenderFrameHost has a
-// visible password field and the top-level URL is HTTP, the
-// VisiblePasswordObserver notifies the WebContents, which allows the
-// content embedder to adjust security UI. Similarly, when all
-// RenderFrameHosts have hidden their password fields (either because
-// the renderer sent a message reporting that all password fields are
-// gone, or because the renderer crashed), the WebContents is notified
-// so that security warnings can be removed by the embedder.
-//
-// The user of this class is responsible for listening for messages from
-// the renderer about password visibility and notifying the
-// VisiblePasswordObserver about them. The VisiblePasswordObserver
-// itself observes renderer process crashes. (Note that listening for
-// navigations explicitly is not required because on navigation the
-// renderer will send messages as password fields are removed.)
-class VisiblePasswordObserver
- : public content::WebContentsObserver,
- public content::WebContentsUserData<VisiblePasswordObserver> {
- public:
- ~VisiblePasswordObserver() override;
-
- // This method should be called when there is a message notifying
- // the browser process that the renderer has a visible password
- // field.
- void RenderFrameHasVisiblePasswordField(
- content::RenderFrameHost* render_frame_host);
-
- // This method should be called when there is a message notifying the browser
- // process that the renderer has no visible password fields anymore.
- void RenderFrameHasNoVisiblePasswordFields(
- content::RenderFrameHost* render_frame_host);
-
- // WebContentsObserver:
- void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
-
- private:
- explicit VisiblePasswordObserver(content::WebContents* web_contents);
- friend class content::WebContentsUserData<VisiblePasswordObserver>;
-
- void MaybeNotifyPasswordInputShownOnHttp();
-
- void MaybeNotifyAllFieldsInvisible();
-
- content::WebContents* web_contents_;
- std::set<content::RenderFrameHost*> frames_with_visible_password_fields_;
-
- DISALLOW_COPY_AND_ASSIGN(VisiblePasswordObserver);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_VISIBLE_PASSWORD_OBSERVER_H_
diff --git a/chromium/components/password_manager/content/common/BUILD.gn b/chromium/components/password_manager/content/common/BUILD.gn
deleted file mode 100644
index 0fafcea3ae0..00000000000
--- a/chromium/components/password_manager/content/common/BUILD.gn
+++ /dev/null
@@ -1,16 +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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojo_interfaces") {
- sources = [
- "credential_manager.mojom",
- ]
-
- public_deps = [
- "//url/mojo:url_mojom_gurl",
- "//url/mojo:url_mojom_origin",
- ]
-}
diff --git a/chromium/components/password_manager/content/common/OWNERS b/chromium/components/password_manager/content/common/OWNERS
index 2c44a463856..4df0c71cc7d 100644
--- a/chromium/components/password_manager/content/common/OWNERS
+++ b/chromium/components/password_manager/content/common/OWNERS
@@ -1,5 +1,3 @@
-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
diff --git a/chromium/components/password_manager/content/common/credential_manager.mojom b/chromium/components/password_manager/content/common/credential_manager.mojom
deleted file mode 100644
index ea7cf47559a..00000000000
--- a/chromium/components/password_manager/content/common/credential_manager.mojom
+++ /dev/null
@@ -1,53 +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.
-
-module password_manager.mojom;
-
-import "url/mojo/origin.mojom";
-import "url/mojo/url.mojom";
-
-enum CredentialType {
- EMPTY,
- PASSWORD,
- FEDERATED
-};
-
-enum CredentialMediationRequirement {
- kSilent,
- kOptional,
- kRequired
-};
-
-enum CredentialManagerError {
- SUCCESS,
- DISABLED,
- PENDINGREQUEST,
- PASSWORDSTOREUNAVAILABLE,
- UNKNOWN
-};
-
-struct CredentialInfo {
- CredentialType type;
- string id;
- string name;
- url.mojom.Url icon;
- string password;
- url.mojom.Origin federation;
-};
-
-interface CredentialManager {
- // Store credential. For navigator.credentials.store().
- Store(CredentialInfo credential) => ();
-
- // Require user mediation. For navigator.credentials.preventSilentAccess().
- PreventSilentAccess() => ();
-
- // Get Credential. For navigator.credentials.get().
- // The result callback will return a non-null and valid CredentialInfo
- // if succeeded, or null with a CredentialManagerError if failed.
- Get(CredentialMediationRequirement mediation,
- bool include_passwords,
- array<url.mojom.Url> federations)
- => (CredentialManagerError error, CredentialInfo? credential);
-};
diff --git a/chromium/components/password_manager/content/common/credential_manager.typemap b/chromium/components/password_manager/content/common/credential_manager.typemap
index 571ba267ca3..b3f4a5ae2d9 100644
--- a/chromium/components/password_manager/content/common/credential_manager.typemap
+++ b/chromium/components/password_manager/content/common/credential_manager.typemap
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-mojom = "//components/password_manager/content/common/credential_manager.mojom"
+mojom = "//third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom"
public_headers =
[ "//components/password_manager/core/common/credential_manager_types.h" ]
traits_headers = [ "//components/password_manager/content/common/credential_manager_struct_traits.h" ]
diff --git a/chromium/components/password_manager/content/common/credential_manager_struct_traits.h b/chromium/components/password_manager/content/common/credential_manager_struct_traits.h
index aa544b782ce..1fffa0d69b5 100644
--- a/chromium/components/password_manager/content/common/credential_manager_struct_traits.h
+++ b/chromium/components/password_manager/content/common/credential_manager_struct_traits.h
@@ -6,9 +6,9 @@
#define COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_STRUCT_TRAITS_H_
#include "base/strings/string16.h"
-#include "components/password_manager/content/common/credential_manager.mojom.h"
#include "components/password_manager/core/common/credential_manager_types.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.h"
namespace mojo {
diff --git a/chromium/components/password_manager/content/renderer/BUILD.gn b/chromium/components/password_manager/content/renderer/BUILD.gn
index 15db4320434..0d43743029e 100644
--- a/chromium/components/password_manager/content/renderer/BUILD.gn
+++ b/chromium/components/password_manager/content/renderer/BUILD.gn
@@ -10,7 +10,6 @@ static_library("renderer") {
deps = [
"//base",
- "//components/password_manager/content/common:mojo_interfaces",
"//components/password_manager/core/common",
"//components/strings",
"//content/public/common",
diff --git a/chromium/components/password_manager/content/renderer/credential_manager_client.cc b/chromium/components/password_manager/content/renderer/credential_manager_client.cc
index 6a6d0b2eba4..f0e3081fb3a 100644
--- a/chromium/components/password_manager/content/renderer/credential_manager_client.cc
+++ b/chromium/components/password_manager/content/renderer/credential_manager_client.cc
@@ -29,19 +29,27 @@ namespace {
void WebCredentialToCredentialInfo(const blink::WebCredential& credential,
CredentialInfo* out) {
out->id = credential.Id().Utf16();
- out->name = credential.GetName().Utf16();
- out->icon = credential.GetIconURL();
if (credential.IsPasswordCredential()) {
out->type = CredentialType::CREDENTIAL_TYPE_PASSWORD;
out->password = static_cast<const blink::WebPasswordCredential&>(credential)
.Password()
.Utf16();
+ out->name = static_cast<const blink::WebPasswordCredential&>(credential)
+ .Name()
+ .Utf16();
+ out->icon =
+ static_cast<const blink::WebPasswordCredential&>(credential).IconURL();
} else {
DCHECK(credential.IsFederatedCredential());
out->type = CredentialType::CREDENTIAL_TYPE_FEDERATED;
out->federation =
static_cast<const blink::WebFederatedCredential&>(credential)
.Provider();
+ out->name = static_cast<const blink::WebFederatedCredential&>(credential)
+ .Name()
+ .Utf16();
+ out->icon =
+ static_cast<const blink::WebFederatedCredential&>(credential).IconURL();
}
}
diff --git a/chromium/components/password_manager/content/renderer/credential_manager_client.h b/chromium/components/password_manager/content/renderer/credential_manager_client.h
index 513b389ecd2..918e35e6e99 100644
--- a/chromium/components/password_manager/content/renderer/credential_manager_client.h
+++ b/chromium/components/password_manager/content/renderer/credential_manager_client.h
@@ -7,12 +7,12 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "components/password_manager/content/common/credential_manager.mojom.h"
#include "content/public/renderer/render_view_observer.h"
#include "third_party/WebKit/public/platform/WebCredentialManagerClient.h"
#include "third_party/WebKit/public/platform/WebCredentialManagerError.h"
#include "third_party/WebKit/public/platform/WebCredentialMediationRequirement.h"
#include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.h"
namespace blink {
class WebCredential;
diff --git a/chromium/components/password_manager/content/renderer/credential_manager_client_browsertest.cc b/chromium/components/password_manager/content/renderer/credential_manager_client_browsertest.cc
index e384b24c885..85647cb9ea6 100644
--- a/chromium/components/password_manager/content/renderer/credential_manager_client_browsertest.cc
+++ b/chromium/components/password_manager/content/renderer/credential_manager_client_browsertest.cc
@@ -13,12 +13,11 @@
#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "content/public/common/associated_interface_provider.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
#include "content/public/test/render_view_test.h"
+#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/bindings/associated_binding_set.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebCredential.h"
@@ -173,10 +172,7 @@ class TestRequestCallbacks
};
void RunAllPendingTasks() {
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
- run_loop.Run();
+ content::RunAllBlockingPoolTasksUntilIdle();
}
} // namespace
diff --git a/chromium/components/password_manager/core/browser/BUILD.gn b/chromium/components/password_manager/core/browser/BUILD.gn
index 056d7d6164c..ddaef4ea34b 100644
--- a/chromium/components/password_manager/core/browser/BUILD.gn
+++ b/chromium/components/password_manager/core/browser/BUILD.gn
@@ -13,22 +13,26 @@ password_reuse_detection_support = !is_android && !is_ios
static_library("browser") {
sources = [
- "affiliated_match_helper.cc",
- "affiliated_match_helper.h",
- "affiliation_backend.cc",
- "affiliation_backend.h",
- "affiliation_database.cc",
- "affiliation_database.h",
- "affiliation_fetch_throttler.cc",
- "affiliation_fetch_throttler.h",
- "affiliation_fetch_throttler_delegate.h",
- "affiliation_fetcher.cc",
- "affiliation_fetcher.h",
- "affiliation_fetcher_delegate.h",
- "affiliation_service.cc",
- "affiliation_service.h",
- "affiliation_utils.cc",
- "affiliation_utils.h",
+ "android_affiliation/affiliated_match_helper.cc",
+ "android_affiliation/affiliated_match_helper.h",
+ "android_affiliation/affiliation_backend.cc",
+ "android_affiliation/affiliation_backend.h",
+ "android_affiliation/affiliation_database.cc",
+ "android_affiliation/affiliation_database.h",
+ "android_affiliation/affiliation_fetch_throttler.cc",
+ "android_affiliation/affiliation_fetch_throttler.h",
+ "android_affiliation/affiliation_fetch_throttler_delegate.h",
+ "android_affiliation/affiliation_fetcher.cc",
+ "android_affiliation/affiliation_fetcher.h",
+ "android_affiliation/affiliation_fetcher_delegate.h",
+ "android_affiliation/affiliation_service.cc",
+ "android_affiliation/affiliation_service.h",
+ "android_affiliation/affiliation_utils.cc",
+ "android_affiliation/affiliation_utils.h",
+ "android_affiliation/facet_manager.cc",
+ "android_affiliation/facet_manager.h",
+ "android_affiliation/facet_manager_host.h",
+ "android_affiliation/test_affiliation_fetcher_factory.h",
"browser_save_password_progress_logger.cc",
"browser_save_password_progress_logger.h",
"credential_manager_logger.cc",
@@ -46,9 +50,6 @@ static_library("browser") {
"export/password_csv_writer.h",
"export/password_exporter.cc",
"export/password_exporter.h",
- "facet_manager.cc",
- "facet_manager.h",
- "facet_manager_host.h",
"form_fetcher.h",
"form_fetcher_impl.cc",
"form_fetcher_impl.h",
@@ -83,6 +84,9 @@ static_library("browser") {
"password_bubble_experiment.h",
"password_form_manager.cc",
"password_form_manager.h",
+ "password_form_metrics_recorder.cc",
+ "password_form_metrics_recorder.h",
+ "password_form_user_action.h",
"password_generation_manager.cc",
"password_generation_manager.h",
"password_manager.cc",
@@ -94,12 +98,13 @@ static_library("browser") {
"password_manager_driver.h",
"password_manager_internals_service.cc",
"password_manager_internals_service.h",
+ "password_manager_metrics_recorder.cc",
+ "password_manager_metrics_recorder.h",
"password_manager_metrics_util.cc",
"password_manager_metrics_util.h",
- "password_manager_settings_migration_experiment.cc",
- "password_manager_settings_migration_experiment.h",
"password_manager_util.cc",
"password_manager_util.h",
+ "password_reuse_defines.h",
"password_store.cc",
"password_store.h",
"password_store_change.cc",
@@ -118,13 +123,16 @@ static_library("browser") {
"password_ui_utils.h",
"psl_matching_helper.cc",
"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",
"statistics_table.h",
"suppressed_form_fetcher.cc",
"suppressed_form_fetcher.h",
- "test_affiliation_fetcher_factory.h",
"webdata/logins_table.cc",
"webdata/logins_table.h",
"webdata/logins_table_win.cc",
@@ -141,6 +149,15 @@ static_library("browser") {
"password_reuse_detector_consumer.cc",
"password_reuse_detector_consumer.h",
]
+
+ if (is_win || is_mac || (is_linux && !is_chromeos)) {
+ sources += [
+ "hash_password_manager.cc",
+ "hash_password_manager.h",
+ "password_store_signin_notifier.cc",
+ "password_store_signin_notifier.h",
+ ]
+ }
}
public_deps = [
@@ -168,6 +185,7 @@ static_library("browser") {
"//crypto",
"//google_apis",
"//net",
+ "//services/metrics/public/cpp:metrics_cpp",
"//sql",
"//third_party/protobuf:protobuf_lite",
"//third_party/re2",
@@ -189,39 +207,36 @@ static_library("browser") {
sources -= [ "login_database_posix.cc" ]
}
- if (!is_ios) {
- deps += [ "//components/safe_browsing/password_protection" ]
- }
-
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
}
proto_library("proto") {
sources = [
- "affiliation_api.proto",
+ "android_affiliation/affiliation_api.proto",
]
}
static_library("test_support") {
testonly = true
sources = [
- "fake_affiliation_api.cc",
- "fake_affiliation_api.h",
- "fake_affiliation_fetcher.cc",
- "fake_affiliation_fetcher.h",
+ "android_affiliation/fake_affiliation_api.cc",
+ "android_affiliation/fake_affiliation_api.h",
+ "android_affiliation/fake_affiliation_fetcher.cc",
+ "android_affiliation/fake_affiliation_fetcher.h",
+ "android_affiliation/mock_affiliated_match_helper.cc",
+ "android_affiliation/mock_affiliated_match_helper.h",
+ "android_affiliation/mock_affiliation_consumer.cc",
+ "android_affiliation/mock_affiliation_consumer.h",
"fake_form_fetcher.cc",
"fake_form_fetcher.h",
- "mock_affiliated_match_helper.cc",
- "mock_affiliated_match_helper.h",
- "mock_affiliation_consumer.cc",
- "mock_affiliation_consumer.h",
"mock_password_store.cc",
"mock_password_store.h",
"password_manager_test_utils.cc",
"password_manager_test_utils.h",
"stub_credentials_filter.cc",
"stub_credentials_filter.h",
+ "stub_form_saver.cc",
"stub_form_saver.h",
"stub_log_manager.cc",
"stub_log_manager.h",
@@ -235,6 +250,7 @@ static_library("test_support") {
public_deps = [
":browser",
+ "//components/ukm",
"//testing/gmock",
"//url:url",
]
@@ -250,6 +266,8 @@ bundle_data("unit_tests_bundle_data") {
visibility = [ ":unit_tests" ]
testonly = true
sources = [
+ "//components/test/data/password_manager/affiliation_db_v1.sql",
+ "//components/test/data/password_manager/affiliation_db_v2.sql",
"//components/test/data/password_manager/login_db_v1.sql",
"//components/test/data/password_manager/login_db_v10.sql",
"//components/test/data/password_manager/login_db_v11.sql",
@@ -283,20 +301,20 @@ bundle_data("unit_tests_bundle_data") {
source_set("unit_tests") {
testonly = true
sources = [
- "affiliated_match_helper_unittest.cc",
- "affiliation_backend_unittest.cc",
- "affiliation_database_unittest.cc",
- "affiliation_fetch_throttler_unittest.cc",
- "affiliation_fetcher_unittest.cc",
- "affiliation_service_unittest.cc",
- "affiliation_utils_unittest.cc",
+ "android_affiliation/affiliated_match_helper_unittest.cc",
+ "android_affiliation/affiliation_backend_unittest.cc",
+ "android_affiliation/affiliation_database_unittest.cc",
+ "android_affiliation/affiliation_fetch_throttler_unittest.cc",
+ "android_affiliation/affiliation_fetcher_unittest.cc",
+ "android_affiliation/affiliation_service_unittest.cc",
+ "android_affiliation/affiliation_utils_unittest.cc",
+ "android_affiliation/facet_manager_unittest.cc",
"browser_save_password_progress_logger_unittest.cc",
"credential_manager_logger_unittest.cc",
"credential_manager_password_form_manager_unittest.cc",
"export/csv_writer_unittest.cc",
"export/password_csv_writer_unittest.cc",
"export/password_exporter_unittest.cc",
- "facet_manager_unittest.cc",
"form_fetcher_impl_unittest.cc",
"form_saver_impl_unittest.cc",
"http_password_store_migrator_unittest.cc",
@@ -310,8 +328,9 @@ source_set("unit_tests") {
"password_autofill_manager_unittest.cc",
"password_bubble_experiment_unittest.cc",
"password_form_manager_unittest.cc",
+ "password_form_metrics_recorder_unittest.cc",
"password_generation_manager_unittest.cc",
- "password_manager_settings_migration_experiment_unittest.cc",
+ "password_manager_metrics_recorder_unittest.cc",
"password_manager_unittest.cc",
"password_manager_util_unittest.cc",
"password_store_default_unittest.cc",
@@ -320,6 +339,8 @@ source_set("unit_tests") {
"password_syncable_service_unittest.cc",
"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",
@@ -341,6 +362,9 @@ source_set("unit_tests") {
"password_reuse_detector_unittest.cc",
]
}
+ if (is_win || is_mac || (is_linux && !is_chromeos)) {
+ sources += [ "hash_password_manager_unittest.cc" ]
+ }
deps = [
":test_support",
":unit_tests_bundle_data",
@@ -356,6 +380,7 @@ source_set("unit_tests") {
"//components/strings",
"//components/sync:test_support_driver",
"//components/sync:test_support_model",
+ "//components/ukm:test_support",
"//components/variations",
"//net:test_support",
"//sql:test_support",
diff --git a/chromium/components/password_manager/core/browser/DEPS b/chromium/components/password_manager/core/browser/DEPS
index d4cbab297ac..cf549c2f44a 100644
--- a/chromium/components/password_manager/core/browser/DEPS
+++ b/chromium/components/password_manager/core/browser/DEPS
@@ -2,7 +2,6 @@ include_rules = [
"+components/autofill/core/browser",
"+components/keyed_service/core",
"+components/pref_registry",
- "+components/safe_browsing/password_protection",
"+components/security_state",
"+components/sync/base",
"+components/sync/driver",
@@ -12,4 +11,17 @@ include_rules = [
"+crypto",
"+google_apis",
"+grit",
+ "+services/metrics/public/cpp",
]
+
+specific_include_rules = {
+ "password_form_manager_unittest\.cc": [
+ "+components/ukm",
+ ],
+ "password_form_metrics_recorder_unittest\.cc": [
+ "+components/ukm",
+ ],
+ "password_manager_metrics_recorder_unittest\.cc": [
+ "+components/ukm",
+ ],
+}
diff --git a/chromium/components/password_manager/core/browser/affiliated_match_helper.cc b/chromium/components/password_manager/core/browser/affiliated_match_helper.cc
deleted file mode 100644
index b88033ad5b9..00000000000
--- a/chromium/components/password_manager/core/browser/affiliated_match_helper.cc
+++ /dev/null
@@ -1,223 +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/password_manager/core/browser/affiliated_match_helper.h"
-
-#include <utility>
-
-#include "base/barrier_closure.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-
-namespace password_manager {
-
-namespace {
-
-// Returns whether or not |form| represents a credential for an Android
-// application, and if so, returns the |facet_uri| of that application.
-bool IsAndroidApplicationCredential(const autofill::PasswordForm& form,
- FacetURI* facet_uri) {
- DCHECK(facet_uri);
- if (form.scheme != autofill::PasswordForm::SCHEME_HTML)
- return false;
-
- *facet_uri = FacetURI::FromPotentiallyInvalidSpec(form.signon_realm);
- return facet_uri->IsValidAndroidFacetURI();
-}
-
-} // namespace
-
-// static
-constexpr base::TimeDelta AffiliatedMatchHelper::kInitializationDelayOnStartup;
-
-AffiliatedMatchHelper::AffiliatedMatchHelper(
- PasswordStore* password_store,
- std::unique_ptr<AffiliationService> affiliation_service)
- : password_store_(password_store),
- affiliation_service_(std::move(affiliation_service)),
- weak_ptr_factory_(this) {}
-
-AffiliatedMatchHelper::~AffiliatedMatchHelper() {
- if (password_store_)
- password_store_->RemoveObserver(this);
-}
-
-void AffiliatedMatchHelper::Initialize() {
- DCHECK(password_store_);
- DCHECK(affiliation_service_);
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&AffiliatedMatchHelper::DoDeferredInitialization,
- weak_ptr_factory_.GetWeakPtr()),
- kInitializationDelayOnStartup);
-}
-
-void AffiliatedMatchHelper::GetAffiliatedAndroidRealms(
- const PasswordStore::FormDigest& observed_form,
- const AffiliatedRealmsCallback& result_callback) {
- if (IsValidWebCredential(observed_form)) {
- FacetURI facet_uri(
- FacetURI::FromPotentiallyInvalidSpec(observed_form.signon_realm));
- affiliation_service_->GetAffiliations(
- facet_uri, AffiliationService::StrategyOnCacheMiss::FAIL,
- base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms,
- weak_ptr_factory_.GetWeakPtr(), facet_uri, result_callback));
- } else {
- result_callback.Run(std::vector<std::string>());
- }
-}
-
-void AffiliatedMatchHelper::GetAffiliatedWebRealms(
- const PasswordStore::FormDigest& android_form,
- const AffiliatedRealmsCallback& result_callback) {
- if (IsValidAndroidCredential(android_form)) {
- affiliation_service_->GetAffiliations(
- FacetURI::FromPotentiallyInvalidSpec(android_form.signon_realm),
- AffiliationService::StrategyOnCacheMiss::FETCH_OVER_NETWORK,
- base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms,
- weak_ptr_factory_.GetWeakPtr(), result_callback));
- } else {
- result_callback.Run(std::vector<std::string>());
- }
-}
-
-void AffiliatedMatchHelper::InjectAffiliatedWebRealms(
- std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
- const PasswordFormsCallback& result_callback) {
- std::vector<autofill::PasswordForm*> android_credentials;
- for (const auto& form : forms) {
- if (IsValidAndroidCredential(PasswordStore::FormDigest(*form)))
- android_credentials.push_back(form.get());
- }
- base::Closure on_get_all_realms(
- base::Bind(result_callback, base::Passed(&forms)));
- base::Closure barrier_closure =
- base::BarrierClosure(android_credentials.size(), on_get_all_realms);
- for (auto* form : android_credentials) {
- affiliation_service_->GetAffiliations(
- FacetURI::FromPotentiallyInvalidSpec(form->signon_realm),
- AffiliationService::StrategyOnCacheMiss::FAIL,
- base::Bind(&AffiliatedMatchHelper::CompleteInjectAffiliatedWebRealm,
- weak_ptr_factory_.GetWeakPtr(), base::Unretained(form),
- barrier_closure));
- }
-}
-
-void AffiliatedMatchHelper::CompleteInjectAffiliatedWebRealm(
- autofill::PasswordForm* form,
- base::Closure barrier_closure,
- const AffiliatedFacets& results,
- bool success) {
- // If there is a number of realms, choose the first in the list.
- if (success) {
- for (const FacetURI& affiliated_facet : results) {
- if (affiliated_facet.IsValidWebFacetURI()) {
- form->affiliated_web_realm = affiliated_facet.canonical_spec() + "/";
- break;
- }
- }
- }
- barrier_closure.Run();
-}
-
-void AffiliatedMatchHelper::TrimAffiliationCache() {
- affiliation_service_->TrimCache();
-}
-
-// static
-bool AffiliatedMatchHelper::IsValidAndroidCredential(
- const PasswordStore::FormDigest& form) {
- return form.scheme == autofill::PasswordForm::SCHEME_HTML &&
- IsValidAndroidFacetURI(form.signon_realm);
-}
-
-// static
-bool AffiliatedMatchHelper::IsValidWebCredential(
- const PasswordStore::FormDigest& form) {
- FacetURI facet_uri(FacetURI::FromPotentiallyInvalidSpec(form.signon_realm));
- return form.scheme == autofill::PasswordForm::SCHEME_HTML &&
- facet_uri.IsValidWebFacetURI();
-}
-
-void AffiliatedMatchHelper::DoDeferredInitialization() {
- // Must start observing for changes at the same time as when the snapshot is
- // taken to avoid inconsistencies due to any changes taking place in-between.
- password_store_->AddObserver(this);
- password_store_->GetAutofillableLogins(this);
- password_store_->GetBlacklistLogins(this);
-}
-
-void AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms(
- const FacetURI& original_facet_uri,
- const AffiliatedRealmsCallback& result_callback,
- const AffiliatedFacets& results,
- bool success) {
- std::vector<std::string> affiliated_realms;
- if (success) {
- for (const FacetURI& affiliated_facet : results) {
- if (affiliated_facet != original_facet_uri &&
- affiliated_facet.IsValidAndroidFacetURI())
- // Facet URIs have no trailing slash, whereas realms do.
- affiliated_realms.push_back(affiliated_facet.canonical_spec() + "/");
- }
- }
- result_callback.Run(affiliated_realms);
-}
-
-void AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms(
- const AffiliatedRealmsCallback& result_callback,
- const AffiliatedFacets& results,
- bool success) {
- std::vector<std::string> affiliated_realms;
- if (success) {
- for (const FacetURI& affiliated_facet : results) {
- if (affiliated_facet.IsValidWebFacetURI())
- // Facet URIs have no trailing slash, whereas realms do.
- affiliated_realms.push_back(affiliated_facet.canonical_spec() + "/");
- }
- }
- result_callback.Run(affiliated_realms);
-}
-
-void AffiliatedMatchHelper::OnLoginsChanged(
- const PasswordStoreChangeList& changes) {
- std::vector<FacetURI> facet_uris_to_trim;
- for (const PasswordStoreChange& change : changes) {
- FacetURI facet_uri;
- if (!IsAndroidApplicationCredential(change.form(), &facet_uri))
- continue;
-
- if (change.type() == PasswordStoreChange::ADD) {
- affiliation_service_->Prefetch(facet_uri, base::Time::Max());
- } else if (change.type() == PasswordStoreChange::REMOVE) {
- // Stop keeping affiliation information fresh for deleted Android logins,
- // and make a note to potentially remove any unneeded cached data later.
- facet_uris_to_trim.push_back(facet_uri);
- affiliation_service_->CancelPrefetch(facet_uri, base::Time::Max());
- }
- }
-
- // When the primary key for a login is updated, |changes| will contain both a
- // REMOVE and ADD change for that login. Cached affiliation data should not be
- // deleted in this case. A simple solution is to call TrimCacheForFacet()
- // always after Prefetch() calls -- the trimming logic will detect that there
- // is an active prefetch and not delete the corresponding data.
- for (const FacetURI& facet_uri : facet_uris_to_trim)
- affiliation_service_->TrimCacheForFacet(facet_uri);
-}
-
-void AffiliatedMatchHelper::OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
- for (const auto& form : results) {
- FacetURI facet_uri;
- if (IsAndroidApplicationCredential(*form, &facet_uri))
- affiliation_service_->Prefetch(facet_uri, base::Time::Max());
- }
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliated_match_helper.h b/chromium/components/password_manager/core/browser/affiliated_match_helper.h
deleted file mode 100644
index 50c7fd2d544..00000000000
--- a/chromium/components/password_manager/core/browser/affiliated_match_helper.h
+++ /dev/null
@@ -1,160 +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_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATED_MATCH_HELPER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATED_MATCH_HELPER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-#include "components/password_manager/core/browser/password_store.h"
-#include "components/password_manager/core/browser/password_store_consumer.h"
-
-namespace autofill {
-struct PasswordForm;
-} // namespace autofill
-
-namespace password_manager {
-
-class AffiliationService;
-
-// Interacts with the AffiliationService on behalf of the PasswordStore.
-// For each GetLogins() request, it provides the PasswordStore with a list of
-// additional realms that are affiliation-based matches to the observed realm.
-//
-// Currently, the only supported use-case is obtaining Android applications
-// affiliated with the web site containing the observed form. This is achieved
-// by implementing the "proactive fetching" strategy for interacting with the
-// AffiliationService (see affiliation_service.h for details), with Android
-// applications playing the role of facet Y.
-//
-// More specifically, this class prefetches affiliation information on start-up
-// for all Android applications that the PasswordStore has credentials stored
-// for. Then, the actual GetLogins() can be restricted to the cache, so that
-// realms of the observed web forms will never be looked up against the
-// Affiliation API.
-class AffiliatedMatchHelper : public PasswordStore::Observer,
- public PasswordStoreConsumer {
- public:
- // Callback to returns the list of affiliated signon_realms (as per defined in
- // autofill::PasswordForm) to the caller.
- typedef base::Callback<void(const std::vector<std::string>&)>
- AffiliatedRealmsCallback;
-
- typedef base::Callback<void(
- std::vector<std::unique_ptr<autofill::PasswordForm>>)>
- PasswordFormsCallback;
-
- // The |password_store| must outlive |this|. Both arguments must be non-NULL,
- // except in tests which do not Initialize() the object.
- AffiliatedMatchHelper(
- PasswordStore* password_store,
- std::unique_ptr<AffiliationService> affiliation_service);
- ~AffiliatedMatchHelper() override;
-
- // Schedules deferred initialization.
- void Initialize();
-
- // Retrieves realms of Android applications affiliated with the realm of the
- // |observed_form| if it is web-based. Otherwise, yields the empty list. The
- // |result_callback| will be invoked in both cases, on the same thread.
- virtual void GetAffiliatedAndroidRealms(
- const PasswordStore::FormDigest& observed_form,
- const AffiliatedRealmsCallback& result_callback);
-
- // Retrieves realms of web sites affiliated with the Android application that
- // |android_form| belongs to and invokes |result_callback| on the same thread;
- // or yields the empty list if |android_form| is not an Android credential.
- // NOTE: This will issue an on-demand network request against the Affiliation
- // API if affiliations of the Android application are not cached. However, as
- // long as the |android_form| is from the PasswordStore, this should rarely
- // happen as affiliation information for those applications are prefetched.
- virtual void GetAffiliatedWebRealms(
- const PasswordStore::FormDigest& android_form,
- const AffiliatedRealmsCallback& result_callback);
-
- // Retrieves realms of web sites affiliated with the Android credentials in
- // |forms|, sets |affiliated_web_realm| of forms, and invokes
- // |result_callback|.
- // NOTE: This will not issue an on-demand network request. If a request to
- // cache fails, no web realm will be injected into corresponding form.
- virtual void InjectAffiliatedWebRealms(
- std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
- const PasswordFormsCallback& result_callback);
-
- // Removes cached affiliation data that is no longer needed.
- void TrimAffiliationCache();
-
- // Returns whether or not |form| represents an Android credential.
- static bool IsValidAndroidCredential(const PasswordStore::FormDigest& form);
-
- // Returns whether or not |form| represents a valid Web credential for the
- // purposes of affiliation-based matching.
- static bool IsValidWebCredential(const PasswordStore::FormDigest& form);
-
- // I/O heavy initialization on start-up will be delayed by this long.
- // This should be high enough not to exacerbate start-up I/O contention too
- // much, but also low enough that the user be able log-in shortly after
- // browser start-up into web sites using Android credentials.
- // TODO(engedy): See if we can tie this instead to some meaningful event.
- static constexpr base::TimeDelta kInitializationDelayOnStartup =
- base::TimeDelta::FromSeconds(8);
-
- private:
- // Reads all autofillable credentials from the password store and starts
- // observing the store for future changes.
- void DoDeferredInitialization();
-
- // Called back by AffiliationService to supply the list of facets affiliated
- // with |original_facet_uri| so that a GetAffiliatedAndroidRealms() call can
- // be completed.
- void CompleteGetAffiliatedAndroidRealms(
- const FacetURI& original_facet_uri,
- const AffiliatedRealmsCallback& result_callback,
- const AffiliatedFacets& results,
- bool success);
-
- // Called back by AffiliationService to supply the list of facets affiliated
- // with the Android application that GetAffiliatedWebRealms() was called with,
- // so that the call can be completed.
- void CompleteGetAffiliatedWebRealms(
- const AffiliatedRealmsCallback& result_callback,
- const AffiliatedFacets& results,
- bool success);
-
- // Called back by AffiliationService to supply the list of facets affiliated
- // with the Android credential in |form|. Sets |form->affiliated_web_realm|,
- // if |success| is true and |results| is non-empty. Invokes |barrier_closure|.
- void CompleteInjectAffiliatedWebRealm(autofill::PasswordForm* form,
- base::Closure barrier_closure,
- const AffiliatedFacets& results,
- bool success);
-
- // PasswordStore::Observer:
- void OnLoginsChanged(const PasswordStoreChangeList& changes) override;
-
- // PasswordStoreConsumer:
- void OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
-
- PasswordStore* const password_store_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_waiting_;
-
- // Being the sole consumer of AffiliationService, |this| owns the service.
- std::unique_ptr<AffiliationService> affiliation_service_;
-
- base::WeakPtrFactory<AffiliatedMatchHelper> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliatedMatchHelper);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATED_MATCH_HELPER_H_
diff --git a/chromium/components/password_manager/core/browser/affiliated_match_helper_unittest.cc b/chromium/components/password_manager/core/browser/affiliated_match_helper_unittest.cc
deleted file mode 100644
index 80e169e3cea..00000000000
--- a/chromium/components/password_manager/core/browser/affiliated_match_helper_unittest.cc
+++ /dev/null
@@ -1,630 +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/password_manager/core/browser/affiliated_match_helper.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_mock_time_message_loop_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-#include "components/password_manager/core/browser/test_password_store.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-
-namespace {
-
-using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
-
-class MockAffiliationService : public testing::StrictMock<AffiliationService> {
- public:
- MockAffiliationService() : testing::StrictMock<AffiliationService>(nullptr) {
- testing::DefaultValue<AffiliatedFacets>::Set(AffiliatedFacets());
- }
-
- ~MockAffiliationService() override {}
-
- MOCK_METHOD2(OnGetAffiliationsCalled,
- AffiliatedFacets(const FacetURI&, StrategyOnCacheMiss));
- MOCK_METHOD2(Prefetch, void(const FacetURI&, const base::Time&));
- MOCK_METHOD2(CancelPrefetch, void(const FacetURI&, const base::Time&));
- MOCK_METHOD1(TrimCacheForFacet, void(const FacetURI&));
-
- void GetAffiliations(const FacetURI& facet_uri,
- StrategyOnCacheMiss cache_miss_strategy,
- const ResultCallback& result_callback) override {
- AffiliatedFacets affiliation =
- OnGetAffiliationsCalled(facet_uri, cache_miss_strategy);
- result_callback.Run(affiliation, !affiliation.empty());
- }
-
- void ExpectCallToGetAffiliationsAndSucceedWithResult(
- const FacetURI& expected_facet_uri,
- StrategyOnCacheMiss expected_cache_miss_strategy,
- const AffiliatedFacets& affiliations_to_return) {
- EXPECT_CALL(*this, OnGetAffiliationsCalled(expected_facet_uri,
- expected_cache_miss_strategy))
- .WillOnce(testing::Return(affiliations_to_return));
- }
-
- void ExpectCallToGetAffiliationsAndEmulateFailure(
- const FacetURI& expected_facet_uri,
- StrategyOnCacheMiss expected_cache_miss_strategy) {
- EXPECT_CALL(*this, OnGetAffiliationsCalled(expected_facet_uri,
- expected_cache_miss_strategy))
- .WillOnce(testing::Return(std::vector<FacetURI>()));
- }
-
- void ExpectCallToPrefetch(const char* expected_facet_uri_spec) {
- EXPECT_CALL(*this,
- Prefetch(FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
- base::Time::Max())).RetiresOnSaturation();
- }
-
- void ExpectCallToCancelPrefetch(const char* expected_facet_uri_spec) {
- EXPECT_CALL(*this, CancelPrefetch(
- FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
- base::Time::Max())).RetiresOnSaturation();
- }
-
- void ExpectCallToTrimCacheForFacet(const char* expected_facet_uri_spec) {
- EXPECT_CALL(*this, TrimCacheForFacet(FacetURI::FromCanonicalSpec(
- expected_facet_uri_spec))).RetiresOnSaturation();
- }
-
- private:
- DISALLOW_ASSIGN(MockAffiliationService);
-};
-
-const char kTestWebFacetURIAlpha1[] = "https://one.alpha.example.com";
-const char kTestWebFacetURIAlpha2[] = "https://two.alpha.example.com";
-const char kTestAndroidFacetURIAlpha3[] =
- "android://hash@com.example.alpha.android";
-const char kTestWebRealmAlpha1[] = "https://one.alpha.example.com/";
-const char kTestWebRealmAlpha2[] = "https://two.alpha.example.com/";
-const char kTestAndroidRealmAlpha3[] =
- "android://hash@com.example.alpha.android/";
-
-const char kTestWebFacetURIBeta1[] = "https://one.beta.example.com";
-const char kTestAndroidFacetURIBeta2[] =
- "android://hash@com.example.beta.android";
-const char kTestAndroidFacetURIBeta3[] =
- "android://hash@com.yetanother.beta.android";
-const char kTestWebRealmBeta1[] = "https://one.beta.example.com/";
-const char kTestAndroidRealmBeta2[] =
- "android://hash@com.example.beta.android/";
-const char kTestAndroidRealmBeta3[] =
- "android://hash@com.yetanother.beta.android/";
-
-const char kTestAndroidFacetURIGamma[] =
- "android://hash@com.example.gamma.android";
-const char kTestAndroidRealmGamma[] =
- "android://hash@com.example.gamma.android";
-
-const char kTestUsername[] = "JohnDoe";
-const char kTestPassword[] = "secret";
-
-AffiliatedFacets GetTestEquivalenceClassAlpha() {
- AffiliatedFacets affiliated_facets;
- affiliated_facets.push_back(
- FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1));
- affiliated_facets.push_back(
- FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha2));
- affiliated_facets.push_back(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3));
- return affiliated_facets;
-}
-
-AffiliatedFacets GetTestEquivalenceClassBeta() {
- AffiliatedFacets affiliated_facets;
- affiliated_facets.push_back(
- FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1));
- affiliated_facets.push_back(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2));
- affiliated_facets.push_back(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3));
- return affiliated_facets;
-}
-
-autofill::PasswordForm GetTestAndroidCredentials(const char* signon_realm) {
- autofill::PasswordForm form;
- form.scheme = autofill::PasswordForm::SCHEME_HTML;
- form.signon_realm = signon_realm;
- form.username_value = base::ASCIIToUTF16(kTestUsername);
- form.password_value = base::ASCIIToUTF16(kTestPassword);
- return form;
-}
-
-autofill::PasswordForm GetTestBlacklistedAndroidCredentials(
- const char* signon_realm) {
- autofill::PasswordForm form = GetTestAndroidCredentials(signon_realm);
- form.blacklisted_by_user = true;
- return form;
-}
-
-PasswordStore::FormDigest GetTestObservedWebForm(const char* signon_realm,
- const char* origin) {
- return {autofill::PasswordForm::SCHEME_HTML, signon_realm,
- origin ? GURL(origin) : GURL()};
-}
-
-} // namespace
-
-class AffiliatedMatchHelperTest : public testing::Test {
- public:
- AffiliatedMatchHelperTest()
- : expecting_result_callback_(false), mock_affiliation_service_(nullptr) {}
- ~AffiliatedMatchHelperTest() override {}
-
- protected:
- void RunDeferredInitialization() {
- mock_time_task_runner_->RunUntilIdle();
- ASSERT_EQ(AffiliatedMatchHelper::kInitializationDelayOnStartup,
- mock_time_task_runner_->NextPendingTaskDelay());
- mock_time_task_runner_->FastForwardBy(
- AffiliatedMatchHelper::kInitializationDelayOnStartup);
- }
-
- void RunUntilIdle() {
- // TODO(gab): Add support for base::RunLoop().RunUntilIdle() in scope of
- // ScopedMockTimeMessageLoopTaskRunner and use it instead of this helper
- // method.
- mock_time_task_runner_->RunUntilIdle();
- }
-
- void AddLogin(const autofill::PasswordForm& form) {
- password_store_->AddLogin(form);
- RunUntilIdle();
- }
-
- void UpdateLoginWithPrimaryKey(
- const autofill::PasswordForm& new_form,
- const autofill::PasswordForm& old_primary_key) {
- password_store_->UpdateLoginWithPrimaryKey(new_form, old_primary_key);
- RunUntilIdle();
- }
-
- void RemoveLogin(const autofill::PasswordForm& form) {
- password_store_->RemoveLogin(form);
- RunUntilIdle();
- }
-
- void AddAndroidAndNonAndroidTestLogins() {
- AddLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
- AddLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
- AddLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3));
- AddLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
-
- AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
- AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
- }
-
- void RemoveAndroidAndNonAndroidTestLogins() {
- RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
- RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
- RemoveLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3));
- RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
-
- RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
- RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
- }
-
- void ExpectPrefetchForAndroidTestLogins() {
- mock_affiliation_service()->ExpectCallToPrefetch(
- kTestAndroidFacetURIAlpha3);
- mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta2);
- mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta3);
- mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIGamma);
- }
-
- void ExpectCancelPrefetchForAndroidTestLogins() {
- mock_affiliation_service()->ExpectCallToCancelPrefetch(
- kTestAndroidFacetURIAlpha3);
- mock_affiliation_service()->ExpectCallToCancelPrefetch(
- kTestAndroidFacetURIBeta2);
- mock_affiliation_service()->ExpectCallToCancelPrefetch(
- kTestAndroidFacetURIBeta3);
- mock_affiliation_service()->ExpectCallToCancelPrefetch(
- kTestAndroidFacetURIGamma);
- }
-
- void ExpectTrimCacheForAndroidTestLogins() {
- mock_affiliation_service()->ExpectCallToTrimCacheForFacet(
- kTestAndroidFacetURIAlpha3);
- mock_affiliation_service()->ExpectCallToTrimCacheForFacet(
- kTestAndroidFacetURIBeta2);
- mock_affiliation_service()->ExpectCallToTrimCacheForFacet(
- kTestAndroidFacetURIBeta3);
- mock_affiliation_service()->ExpectCallToTrimCacheForFacet(
- kTestAndroidFacetURIGamma);
- }
-
- std::vector<std::string> GetAffiliatedAndroidRealms(
- const PasswordStore::FormDigest& observed_form) {
- expecting_result_callback_ = true;
- match_helper()->GetAffiliatedAndroidRealms(
- observed_form,
- base::Bind(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback,
- base::Unretained(this)));
- RunUntilIdle();
- EXPECT_FALSE(expecting_result_callback_);
- return last_result_realms_;
- }
-
- std::vector<std::string> GetAffiliatedWebRealms(
- const PasswordStore::FormDigest& android_form) {
- expecting_result_callback_ = true;
- match_helper()->GetAffiliatedWebRealms(
- android_form,
- base::Bind(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback,
- base::Unretained(this)));
- RunUntilIdle();
- EXPECT_FALSE(expecting_result_callback_);
- return last_result_realms_;
- }
-
- std::vector<std::unique_ptr<autofill::PasswordForm>>
- InjectAffiliatedWebRealms(
- std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
- expecting_result_callback_ = true;
- match_helper()->InjectAffiliatedWebRealms(
- std::move(forms),
- base::Bind(&AffiliatedMatchHelperTest::OnFormsCallback,
- base::Unretained(this)));
- RunUntilIdle();
- EXPECT_FALSE(expecting_result_callback_);
- return std::move(last_result_forms_);
- }
-
- void DestroyMatchHelper() { match_helper_.reset(); }
-
- TestPasswordStore* password_store() { return password_store_.get(); }
-
- MockAffiliationService* mock_affiliation_service() {
- return mock_affiliation_service_;
- }
-
- AffiliatedMatchHelper* match_helper() { return match_helper_.get(); }
-
- private:
- void OnAffiliatedRealmsCallback(
- const std::vector<std::string>& affiliated_realms) {
- EXPECT_TRUE(expecting_result_callback_);
- expecting_result_callback_ = false;
- last_result_realms_ = affiliated_realms;
- }
-
- void OnFormsCallback(
- std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
- EXPECT_TRUE(expecting_result_callback_);
- expecting_result_callback_ = false;
- last_result_forms_.swap(forms);
- }
-
- // testing::Test:
- void SetUp() override {
- std::unique_ptr<MockAffiliationService> service(
- new MockAffiliationService());
- mock_affiliation_service_ = service.get();
-
- password_store_ = new TestPasswordStore;
-
- match_helper_.reset(
- new AffiliatedMatchHelper(password_store_.get(), std::move(service)));
- }
-
- void TearDown() override {
- match_helper_.reset();
- password_store_->ShutdownOnUIThread();
- password_store_ = nullptr;
- }
-
- base::MessageLoop message_loop_;
- base::ScopedMockTimeMessageLoopTaskRunner mock_time_task_runner_;
-
- std::vector<std::string> last_result_realms_;
- std::vector<std::unique_ptr<autofill::PasswordForm>> last_result_forms_;
- bool expecting_result_callback_;
-
- scoped_refptr<TestPasswordStore> password_store_;
- std::unique_ptr<AffiliatedMatchHelper> match_helper_;
-
- // Owned by |match_helper_|.
- MockAffiliationService* mock_affiliation_service_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliatedMatchHelperTest);
-};
-
-// GetAffiliatedAndroidRealm* tests verify that GetAffiliatedAndroidRealms()
-// returns the realms of affiliated Android applications, but only Android
-// applications, and only if the observed form is a secure HTML login form.
-
-TEST_F(AffiliatedMatchHelperTest, GetAffiliatedAndroidRealmsYieldsResults) {
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult(
- FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1),
- StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
- EXPECT_THAT(GetAffiliatedAndroidRealms(
- GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)),
- testing::UnorderedElementsAre(kTestAndroidRealmBeta2,
- kTestAndroidRealmBeta3));
-}
-
-TEST_F(AffiliatedMatchHelperTest,
- GetAffiliatedAndroidRealmsYieldsOnlyAndroidApps) {
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult(
- FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
- StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha());
- // This verifies that |kTestWebRealmAlpha2| is not returned.
- EXPECT_THAT(GetAffiliatedAndroidRealms(
- GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)),
- testing::UnorderedElementsAre(kTestAndroidRealmAlpha3));
-}
-
-TEST_F(AffiliatedMatchHelperTest,
- GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPBasicAuthForms) {
- PasswordStore::FormDigest http_auth_observed_form(
- GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr));
- http_auth_observed_form.scheme = autofill::PasswordForm::SCHEME_BASIC;
- EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form),
- testing::IsEmpty());
-}
-
-TEST_F(AffiliatedMatchHelperTest,
- GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPDigestAuthForms) {
- PasswordStore::FormDigest http_auth_observed_form(
- GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr));
- http_auth_observed_form.scheme = autofill::PasswordForm::SCHEME_DIGEST;
- EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form),
- testing::IsEmpty());
-}
-
-TEST_F(AffiliatedMatchHelperTest,
- GetAffiliatedAndroidRealmsYieldsEmptyResultsForAndroidKeyedForms) {
- PasswordStore::FormDigest android_observed_form(
- GetTestAndroidCredentials(kTestAndroidRealmBeta2));
- EXPECT_THAT(GetAffiliatedAndroidRealms(android_observed_form),
- testing::IsEmpty());
-}
-
-TEST_F(AffiliatedMatchHelperTest,
- GetAffiliatedAndroidRealmsYieldsEmptyResultsWhenNoPrefetch) {
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndEmulateFailure(
- FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
- StrategyOnCacheMiss::FAIL);
- EXPECT_THAT(GetAffiliatedAndroidRealms(
- GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)),
- testing::IsEmpty());
-}
-
-// GetAffiliatedWebRealms* tests verify that GetAffiliatedWebRealms() returns
-// the realms of web sites affiliated with the given Android application, but
-// only web sites, and only if an Android application is queried.
-
-TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsResults) {
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassAlpha());
- PasswordStore::FormDigest android_form(
- GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
- EXPECT_THAT(
- GetAffiliatedWebRealms(android_form),
- testing::UnorderedElementsAre(kTestWebRealmAlpha1, kTestWebRealmAlpha2));
-}
-
-TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsOnlyWebsites) {
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassBeta());
- PasswordStore::FormDigest android_form(
- GetTestAndroidCredentials(kTestAndroidRealmBeta2));
- // This verifies that |kTestAndroidRealmBeta3| is not returned.
- EXPECT_THAT(GetAffiliatedWebRealms(android_form),
- testing::UnorderedElementsAre(kTestWebRealmBeta1));
-}
-
-TEST_F(AffiliatedMatchHelperTest,
- GetAffiliatedWebRealmsYieldsEmptyResultsForWebKeyedForms) {
- EXPECT_THAT(GetAffiliatedWebRealms(
- GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)),
- testing::IsEmpty());
-}
-
-// Verifies that InjectAffiliatedWebRealms() injects the realms of web sites
-// affiliated with the given Android application into password forms, if any.
-TEST_F(AffiliatedMatchHelperTest, InjectAffiliatedWebRealms) {
- std::vector<std::unique_ptr<autofill::PasswordForm>> forms;
-
- forms.push_back(base::MakeUnique<autofill::PasswordForm>(
- GetTestAndroidCredentials(kTestAndroidRealmAlpha3)));
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
- StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha());
-
- forms.push_back(base::MakeUnique<autofill::PasswordForm>(
- GetTestAndroidCredentials(kTestAndroidRealmBeta2)));
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
- StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
-
- forms.push_back(base::MakeUnique<autofill::PasswordForm>(
- GetTestAndroidCredentials(kTestAndroidRealmGamma)));
- mock_affiliation_service()->ExpectCallToGetAffiliationsAndEmulateFailure(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURIGamma),
- StrategyOnCacheMiss::FAIL);
-
- PasswordStore::FormDigest digest =
- GetTestObservedWebForm(kTestWebRealmBeta1, nullptr);
- autofill::PasswordForm web_form;
- web_form.scheme = digest.scheme;
- web_form.signon_realm = digest.signon_realm;
- web_form.origin = digest.origin;
- forms.push_back(base::MakeUnique<autofill::PasswordForm>(web_form));
-
- size_t expected_form_count = forms.size();
- std::vector<std::unique_ptr<autofill::PasswordForm>> results(
- InjectAffiliatedWebRealms(std::move(forms)));
- ASSERT_EQ(expected_form_count, results.size());
- EXPECT_THAT(results[0]->affiliated_web_realm,
- testing::AnyOf(kTestWebRealmAlpha1, kTestWebRealmAlpha2));
- EXPECT_THAT(results[1]->affiliated_web_realm,
- testing::Eq(kTestWebRealmBeta1));
- EXPECT_THAT(results[2]->affiliated_web_realm, testing::IsEmpty());
- EXPECT_THAT(results[3]->affiliated_web_realm, testing::IsEmpty());
-}
-
-// Note: IsValidWebCredential() is tested as part of GetAffiliatedAndroidRealms
-// tests above.
-TEST_F(AffiliatedMatchHelperTest, IsValidAndroidCredential) {
- EXPECT_FALSE(AffiliatedMatchHelper::IsValidAndroidCredential(
- GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)));
- PasswordStore::FormDigest android_credential(
- GetTestAndroidCredentials(kTestAndroidRealmBeta2));
- EXPECT_TRUE(
- AffiliatedMatchHelper::IsValidAndroidCredential(android_credential));
-}
-
-// Verifies that affiliations for Android applications with pre-existing
-// credentials on start-up are prefetched.
-TEST_F(AffiliatedMatchHelperTest,
- PrefetchAffiliationsForPreexistingAndroidCredentialsOnStartup) {
- AddAndroidAndNonAndroidTestLogins();
-
- match_helper()->Initialize();
- RunUntilIdle();
-
- ExpectPrefetchForAndroidTestLogins();
- ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-}
-
-// Stores credentials for Android applications between Initialize() and
-// DoDeferredInitialization(). Verifies that corresponding affiliation
-// information gets prefetched.
-TEST_F(AffiliatedMatchHelperTest,
- PrefetchAffiliationsForAndroidCredentialsAddedInInitializationDelay) {
- match_helper()->Initialize();
- RunUntilIdle();
-
- AddAndroidAndNonAndroidTestLogins();
-
- ExpectPrefetchForAndroidTestLogins();
- ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-}
-
-// Stores credentials for Android applications after DoDeferredInitialization().
-// Verifies that corresponding affiliation information gets prefetched.
-TEST_F(AffiliatedMatchHelperTest,
- PrefetchAffiliationsForAndroidCredentialsAddedAfterInitialization) {
- match_helper()->Initialize();
- ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
- ExpectPrefetchForAndroidTestLogins();
- AddAndroidAndNonAndroidTestLogins();
-}
-
-TEST_F(AffiliatedMatchHelperTest,
- CancelPrefetchingAffiliationsForRemovedAndroidCredentials) {
- AddAndroidAndNonAndroidTestLogins();
- match_helper()->Initialize();
- ExpectPrefetchForAndroidTestLogins();
- ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
- ExpectCancelPrefetchForAndroidTestLogins();
- ExpectTrimCacheForAndroidTestLogins();
- RemoveAndroidAndNonAndroidTestLogins();
-}
-
-// Verify that whenever the primary key is updated for a credential (in which
-// case both REMOVE and ADD change notifications are sent out), then Prefetch()
-// is called in response to the addition before the call to TrimCacheForFacet()
-// in response to the removal, so that cached data is not deleted and then
-// immediately re-fetched.
-TEST_F(AffiliatedMatchHelperTest, PrefetchBeforeTrimForPrimaryKeyUpdates) {
- AddAndroidAndNonAndroidTestLogins();
- match_helper()->Initialize();
- ExpectPrefetchForAndroidTestLogins();
- ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
- mock_affiliation_service()->ExpectCallToCancelPrefetch(
- kTestAndroidFacetURIAlpha3);
-
- {
- testing::InSequence in_sequence;
- mock_affiliation_service()->ExpectCallToPrefetch(
- kTestAndroidFacetURIAlpha3);
- mock_affiliation_service()->ExpectCallToTrimCacheForFacet(
- kTestAndroidFacetURIAlpha3);
- }
-
- autofill::PasswordForm old_form(
- GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
- autofill::PasswordForm new_form(old_form);
- new_form.username_value = base::ASCIIToUTF16("NewUserName");
- UpdateLoginWithPrimaryKey(new_form, old_form);
-}
-
-// Stores and removes four credentials for the same an Android application, and
-// expects that Prefetch() and CancelPrefetch() will each be called four times.
-TEST_F(AffiliatedMatchHelperTest,
- DuplicateCredentialsArePrefetchWithMultiplicity) {
- EXPECT_CALL(*mock_affiliation_service(),
- Prefetch(FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
- base::Time::Max())).Times(4);
-
- autofill::PasswordForm android_form(
- GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
- AddLogin(android_form);
-
- // Store two credentials before initialization.
- autofill::PasswordForm android_form2(android_form);
- android_form2.username_value = base::ASCIIToUTF16("JohnDoe2");
- AddLogin(android_form2);
-
- match_helper()->Initialize();
- RunUntilIdle();
-
- // Store one credential between initialization and deferred initialization.
- autofill::PasswordForm android_form3(android_form);
- android_form3.username_value = base::ASCIIToUTF16("JohnDoe3");
- AddLogin(android_form3);
-
- ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
- // Store one credential after deferred initialization.
- autofill::PasswordForm android_form4(android_form);
- android_form4.username_value = base::ASCIIToUTF16("JohnDoe4");
- AddLogin(android_form4);
-
- for (size_t i = 0; i < 4; ++i) {
- mock_affiliation_service()->ExpectCallToCancelPrefetch(
- kTestAndroidFacetURIAlpha3);
- mock_affiliation_service()->ExpectCallToTrimCacheForFacet(
- kTestAndroidFacetURIAlpha3);
- }
-
- RemoveLogin(android_form);
- RemoveLogin(android_form2);
- RemoveLogin(android_form3);
- RemoveLogin(android_form4);
-}
-
-TEST_F(AffiliatedMatchHelperTest, DestroyBeforeDeferredInitialization) {
- match_helper()->Initialize();
- RunUntilIdle();
- DestroyMatchHelper();
- ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_backend.cc b/chromium/components/password_manager/core/browser/affiliation_backend.cc
deleted file mode 100644
index 4ed027fbbf1..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_backend.cc
+++ /dev/null
@@ -1,294 +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/password_manager/core/browser/affiliation_backend.h"
-
-#include <stdint.h>
-#include <algorithm>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/clock.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/affiliation_database.h"
-#include "components/password_manager/core/browser/affiliation_fetch_throttler.h"
-#include "components/password_manager/core/browser/affiliation_fetcher.h"
-#include "components/password_manager/core/browser/facet_manager.h"
-#include "net/url_request/url_request_context_getter.h"
-
-namespace password_manager {
-
-AffiliationBackend::AffiliationBackend(
- const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
- std::unique_ptr<base::Clock> time_source,
- std::unique_ptr<base::TickClock> time_tick_source)
- : request_context_getter_(request_context_getter),
- task_runner_(task_runner),
- clock_(std::move(time_source)),
- tick_clock_(std::move(time_tick_source)),
- construction_time_(clock_->Now()),
- weak_ptr_factory_(this) {
- DCHECK_LT(base::Time(), clock_->Now());
-}
-
-AffiliationBackend::~AffiliationBackend() {
-}
-
-void AffiliationBackend::Initialize(const base::FilePath& db_path) {
- thread_checker_.reset(new base::ThreadChecker);
-
- DCHECK(!throttler_);
- throttler_.reset(
- new AffiliationFetchThrottler(this, task_runner_, tick_clock_.get()));
-
- // TODO(engedy): Currently, when Init() returns false, it always poisons the
- // DB, so subsequent operations will silently fail. Consider either fully
- // committing to this approach and making Init() a void, or handling the
- // return value here. See: https://crbug.com/478831.
- cache_.reset(new AffiliationDatabase());
- cache_->Init(db_path);
-}
-
-void AffiliationBackend::GetAffiliations(
- const FacetURI& facet_uri,
- StrategyOnCacheMiss cache_miss_strategy,
- const AffiliationService::ResultCallback& callback,
- const scoped_refptr<base::TaskRunner>& callback_task_runner) {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- FacetManager* facet_manager = GetOrCreateFacetManager(facet_uri);
- DCHECK(facet_manager);
- facet_manager->GetAffiliations(cache_miss_strategy, callback,
- callback_task_runner);
-
- if (facet_manager->CanBeDiscarded())
- facet_managers_.erase(facet_uri);
-}
-
-void AffiliationBackend::Prefetch(const FacetURI& facet_uri,
- const base::Time& keep_fresh_until) {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- FacetManager* facet_manager = GetOrCreateFacetManager(facet_uri);
- DCHECK(facet_manager);
- facet_manager->Prefetch(keep_fresh_until);
-
- if (facet_manager->CanBeDiscarded())
- facet_managers_.erase(facet_uri);
-}
-
-void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri,
- const base::Time& keep_fresh_until) {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- auto facet_manager_it = facet_managers_.find(facet_uri);
- if (facet_manager_it == facet_managers_.end())
- return;
- facet_manager_it->second->CancelPrefetch(keep_fresh_until);
-
- if (facet_manager_it->second->CanBeDiscarded())
- facet_managers_.erase(facet_uri);
-}
-
-void AffiliationBackend::TrimCache() {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- std::vector<AffiliatedFacetsWithUpdateTime> all_affiliations;
- cache_->GetAllAffiliations(&all_affiliations);
- for (const auto& affiliation : all_affiliations)
- DiscardCachedDataIfNoLongerNeeded(affiliation.facets);
-}
-
-void AffiliationBackend::TrimCacheForFacet(const FacetURI& facet_uri) {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- AffiliatedFacetsWithUpdateTime affiliation;
- if (cache_->GetAffiliationsForFacet(facet_uri, &affiliation))
- DiscardCachedDataIfNoLongerNeeded(affiliation.facets);
-}
-
-// static
-void AffiliationBackend::DeleteCache(const base::FilePath& db_path) {
- AffiliationDatabase::Delete(db_path);
-}
-
-FacetManager* AffiliationBackend::GetOrCreateFacetManager(
- const FacetURI& facet_uri) {
- std::unique_ptr<FacetManager>& facet_manager = facet_managers_[facet_uri];
- if (!facet_manager) {
- facet_manager =
- base::MakeUnique<FacetManager>(facet_uri, this, clock_.get());
- }
- return facet_manager.get();
-}
-
-void AffiliationBackend::DiscardCachedDataIfNoLongerNeeded(
- const AffiliatedFacets& affiliated_facets) {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- // Discard the equivalence class if there is no facet in the class whose
- // FacetManager claims that it needs to keep the data.
- for (const auto& facet_uri : affiliated_facets) {
- auto facet_manager_it = facet_managers_.find(facet_uri);
- if (facet_manager_it != facet_managers_.end() &&
- !facet_manager_it->second->CanCachedDataBeDiscarded()) {
- return;
- }
- }
-
- CHECK(!affiliated_facets.empty());
- cache_->DeleteAffiliationsForFacet(affiliated_facets[0]);
-}
-
-void AffiliationBackend::OnSendNotification(const FacetURI& facet_uri) {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- auto facet_manager_it = facet_managers_.find(facet_uri);
- if (facet_manager_it == facet_managers_.end())
- return;
- facet_manager_it->second->NotifyAtRequestedTime();
-
- if (facet_manager_it->second->CanBeDiscarded())
- facet_managers_.erase(facet_uri);
-}
-
-bool AffiliationBackend::ReadAffiliationsFromDatabase(
- const FacetURI& facet_uri,
- AffiliatedFacetsWithUpdateTime* affiliations) {
- return cache_->GetAffiliationsForFacet(facet_uri, affiliations);
-}
-
-void AffiliationBackend::SignalNeedNetworkRequest() {
- throttler_->SignalNetworkRequestNeeded();
-}
-
-void AffiliationBackend::RequestNotificationAtTime(const FacetURI& facet_uri,
- base::Time time) {
- // TODO(engedy): Avoid spamming the task runner; only ever schedule the first
- // callback. crbug.com/437865.
- task_runner_->PostDelayedTask(
- FROM_HERE, base::Bind(&AffiliationBackend::OnSendNotification,
- weak_ptr_factory_.GetWeakPtr(), facet_uri),
- time - clock_->Now());
-}
-
-void AffiliationBackend::OnFetchSucceeded(
- std::unique_ptr<AffiliationFetcherDelegate::Result> result) {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- fetcher_.reset();
- throttler_->InformOfNetworkRequestComplete(true);
-
- for (const AffiliatedFacets& affiliated_facets : *result) {
- AffiliatedFacetsWithUpdateTime affiliation;
- affiliation.facets = affiliated_facets;
- affiliation.last_update_time = clock_->Now();
-
- std::vector<AffiliatedFacetsWithUpdateTime> obsoleted_affiliations;
- cache_->StoreAndRemoveConflicting(affiliation, &obsoleted_affiliations);
-
- // Cached data in contradiction with newly stored data automatically gets
- // removed from the DB, and will be stored into |obsoleted_affiliations|.
- // TODO(engedy): Currently, handling this is entirely meaningless unless in
- // the edge case when the user has credentials for two Android applications
- // which are now being de-associated. But even in that case, nothing will
- // explode and the only symptom will be that credentials for the Android
- // application that is not being fetched right now, if any, will not be
- // filled into affiliated applications until the next fetch. Still, this
- // should be implemented at some point by letting facet managers know if
- // data. See: https://crbug.com/478832.
-
- for (const auto& facet_uri : affiliated_facets) {
- auto facet_manager_it = facet_managers_.find(facet_uri);
- if (facet_manager_it == facet_managers_.end())
- continue;
- FacetManager* facet_manager = facet_manager_it->second.get();
- facet_manager->OnFetchSucceeded(affiliation);
- if (facet_manager->CanBeDiscarded())
- facet_managers_.erase(facet_uri);
- }
- }
-
- // A subsequent fetch may be needed if any additional GetAffiliations()
- // requests came in while the current fetch was in flight.
- for (const auto& facet_manager_pair : facet_managers_) {
- if (facet_manager_pair.second->DoesRequireFetch()) {
- throttler_->SignalNetworkRequestNeeded();
- return;
- }
- }
-}
-
-void AffiliationBackend::OnFetchFailed() {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- fetcher_.reset();
- throttler_->InformOfNetworkRequestComplete(false);
-
- // Trigger a retry if a fetch is still needed.
- for (const auto& facet_manager_pair : facet_managers_) {
- if (facet_manager_pair.second->DoesRequireFetch()) {
- throttler_->SignalNetworkRequestNeeded();
- return;
- }
- }
-}
-
-void AffiliationBackend::OnMalformedResponse() {
- DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
-
- // TODO(engedy): Potentially handle this case differently. crbug.com/437865.
- OnFetchFailed();
-}
-
-bool AffiliationBackend::OnCanSendNetworkRequest() {
- DCHECK(!fetcher_);
- std::vector<FacetURI> requested_facet_uris;
- for (const auto& facet_manager_pair : facet_managers_) {
- if (facet_manager_pair.second->DoesRequireFetch())
- requested_facet_uris.push_back(facet_manager_pair.first);
- }
-
- // In case a request is no longer needed, return false to indicate this.
- if (requested_facet_uris.empty())
- return false;
-
- fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(),
- requested_facet_uris, this));
- fetcher_->StartRequest();
- ReportStatistics(requested_facet_uris.size());
- return true;
-}
-
-void AffiliationBackend::ReportStatistics(size_t requested_facet_uri_count) {
- UMA_HISTOGRAM_COUNTS_100("PasswordManager.AffiliationBackend.FetchSize",
- requested_facet_uri_count);
-
- if (last_request_time_.is_null()) {
- base::TimeDelta delay = clock_->Now() - construction_time_;
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "PasswordManager.AffiliationBackend.FirstFetchDelay", delay,
- base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(3), 50);
- } else {
- base::TimeDelta delay = clock_->Now() - last_request_time_;
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "PasswordManager.AffiliationBackend.SubsequentFetchDelay", delay,
- base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(3), 50);
- }
- last_request_time_ = clock_->Now();
-}
-
-void AffiliationBackend::SetThrottlerForTesting(
- std::unique_ptr<AffiliationFetchThrottler> throttler) {
- throttler_ = std::move(throttler);
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_backend.h b/chromium/components/password_manager/core/browser/affiliation_backend.h
deleted file mode 100644
index 832c305d54b..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_backend.h
+++ /dev/null
@@ -1,167 +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_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_BACKEND_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_BACKEND_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <unordered_map>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h"
-#include "components/password_manager/core/browser/affiliation_fetcher_delegate.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-#include "components/password_manager/core/browser/facet_manager_host.h"
-
-namespace base {
-class Clock;
-class FilePath;
-class SingleThreadTaskRunner;
-class TaskRunner;
-class ThreadChecker;
-class TickClock;
-class Time;
-} // namespace base
-
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
-
-namespace password_manager {
-
-class AffiliationDatabase;
-class AffiliationFetcher;
-class AffiliationFetchThrottler;
-class FacetManager;
-
-// The AffiliationBackend is the part of the AffiliationService that lives on a
-// background thread suitable for performing blocking I/O. As most tasks require
-// I/O, the backend ends up doing most of the work for the AffiliationService;
-// the latter being just a thin layer that delegates most tasks to the backend.
-//
-// This class is not thread-safe, but it is fine to construct it on one thread
-// and then transfer it to the background thread for the rest of its life.
-// Initialize() must be called already on the final (background) thread.
-class AffiliationBackend : public FacetManagerHost,
- public AffiliationFetcherDelegate,
- public AffiliationFetchThrottlerDelegate {
- public:
- using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
-
- // Constructs an instance that will use |request_context_getter| 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::SingleThreadTaskRunner>& task_runner,
- std::unique_ptr<base::Clock> time_source,
- std::unique_ptr<base::TickClock> time_tick_source);
- ~AffiliationBackend() override;
-
- // 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);
-
- // Implementations for methods of the same name in AffiliationService. They
- // are not documented here again. See affiliation_service.h for details:
- void GetAffiliations(
- const FacetURI& facet_uri,
- StrategyOnCacheMiss cache_miss_strategy,
- const AffiliationService::ResultCallback& callback,
- const scoped_refptr<base::TaskRunner>& callback_task_runner);
- void Prefetch(const FacetURI& facet_uri, const base::Time& keep_fresh_until);
- void CancelPrefetch(const FacetURI& facet_uri,
- const base::Time& keep_fresh_until);
- void TrimCache();
- void TrimCacheForFacet(const FacetURI& facet_uri);
-
- // Deletes the cache database file at |db_path|, and all auxiliary files. The
- // database must be closed before calling this.
- static void DeleteCache(const base::FilePath& db_path);
-
- private:
- friend class AffiliationBackendTest;
- FRIEND_TEST_ALL_PREFIXES(
- AffiliationBackendTest,
- DiscardCachedDataIfNoLongerNeededWithEmptyAffiliation);
-
- // Retrieves the FacetManager corresponding to |facet_uri|, creating it and
- // storing it into |facet_managers_| if it did not exist.
- FacetManager* GetOrCreateFacetManager(const FacetURI& facet_uri);
-
- // Discards cached data corresponding to |affiliated_facets| unless there are
- // FacetManagers that still need the data.
- void DiscardCachedDataIfNoLongerNeeded(
- const AffiliatedFacets& affiliated_facets);
-
- // Scheduled by RequestNotificationAtTime() to be called back at times when a
- // FacetManager needs to be notified.
- void OnSendNotification(const FacetURI& facet_uri);
-
- // FacetManagerHost:
- bool ReadAffiliationsFromDatabase(
- const FacetURI& facet_uri,
- AffiliatedFacetsWithUpdateTime* affiliations) override;
- void SignalNeedNetworkRequest() override;
- void RequestNotificationAtTime(const FacetURI& facet_uri,
- base::Time time) override;
-
- // AffiliationFetcherDelegate:
- void OnFetchSucceeded(
- std::unique_ptr<AffiliationFetcherDelegate::Result> result) override;
- void OnFetchFailed() override;
- void OnMalformedResponse() override;
-
- // AffiliationFetchThrottlerDelegate:
- bool OnCanSendNetworkRequest() override;
-
- // Returns the number of in-memory FacetManagers. Used only for testing.
- size_t facet_manager_count_for_testing() { return facet_managers_.size(); }
-
- // Reports the |requested_facet_uri_count| in a single fetch; and the elapsed
- // time before the first fetch, and in-between subsequent fetches.
- void ReportStatistics(size_t requested_facet_uri_count);
-
- // To be called after Initialize() to use |throttler| instead of the default
- // one. Used only for testing.
- void SetThrottlerForTesting(
- std::unique_ptr<AffiliationFetchThrottler> throttler);
-
- // Created in Initialize(), and ensures that all subsequent methods are called
- // on the same thread.
- std::unique_ptr<base::ThreadChecker> thread_checker_;
-
- scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- std::unique_ptr<base::Clock> clock_;
- std::unique_ptr<base::TickClock> tick_clock_;
-
- std::unique_ptr<AffiliationDatabase> cache_;
- std::unique_ptr<AffiliationFetcher> fetcher_;
- std::unique_ptr<AffiliationFetchThrottler> throttler_;
-
- base::Time construction_time_;
- base::Time last_request_time_;
-
- // Contains a FacetManager for each facet URI that need ongoing attention. To
- // save memory, managers are discarded as soon as they become redundant.
- std::unordered_map<FacetURI, std::unique_ptr<FacetManager>, FacetURIHash>
- facet_managers_;
-
- base::WeakPtrFactory<AffiliationBackend> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationBackend);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_BACKEND_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_backend_unittest.cc b/chromium/components/password_manager/core/browser/affiliation_backend_unittest.cc
deleted file mode 100644
index 9c10c76274c..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_backend_unittest.cc
+++ /dev/null
@@ -1,925 +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/password_manager/core/browser/affiliation_backend.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/time/clock.h"
-#include "base/time/tick_clock.h"
-#include "components/password_manager/core/browser/affiliation_database.h"
-#include "components/password_manager/core/browser/affiliation_fetch_throttler.h"
-#include "components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h"
-#include "components/password_manager/core/browser/facet_manager.h"
-#include "components/password_manager/core/browser/fake_affiliation_api.h"
-#include "components/password_manager/core/browser/mock_affiliation_consumer.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-
-namespace {
-
-using StrategyOnCacheMiss = AffiliationBackend::StrategyOnCacheMiss;
-
-// Mock fetch throttler that has some extra logic to accurately portray the real
-// AffiliationFetchThrottler in how it ignores SignalNetworkRequestNeeded()
-// requests when a request is already known to be needed or one is already in
-// flight, and in how it goes back to the idle state afterwards.
-class MockAffiliationFetchThrottler : public AffiliationFetchThrottler {
- public:
- MockAffiliationFetchThrottler(AffiliationFetchThrottlerDelegate* delegate)
- : AffiliationFetchThrottler(delegate, nullptr, nullptr),
- signaled_network_request_needed_(false) {
- EXPECT_CALL(*this, OnInformOfNetworkRequestComplete(testing::_)).Times(0);
- }
-
- ~MockAffiliationFetchThrottler() {
- EXPECT_FALSE(signaled_network_request_needed_);
- }
-
- // Expects that InformOfNetworkRequestComplete() will be called to indicate
- // either success or failure, depending on |expected_success|.
- void ExpectInformOfNetworkRequestComplete(bool expected_success) {
- EXPECT_CALL(*this, OnInformOfNetworkRequestComplete(expected_success));
- }
-
- // Informs the |delegate_| that it can send the needed network request.
- // Returns true if the |delegate_| reported that it actually ended up issuing
- // a request.
- bool LetNetworkRequestBeSent() {
- EXPECT_TRUE(has_signaled_network_request_needed());
- if (!delegate_->OnCanSendNetworkRequest()) {
- reset_signaled_network_request_needed();
- return false;
- }
- return true;
- }
-
- // Whether or not the throttler is 'signaled', meaning that the real throttler
- // would eventually call OnCanSendNetworkRequest() on the |delegate_|.
- bool has_signaled_network_request_needed() const {
- return signaled_network_request_needed_;
- }
-
- // Forces the mock throttler back to 'non-signaled' state. Normally, this does
- // not need to be manually called, as this is done by the mock automatically.
- void reset_signaled_network_request_needed() {
- signaled_network_request_needed_ = false;
- }
-
- private:
- MOCK_METHOD1(OnInformOfNetworkRequestComplete, void(bool));
-
- // AffiliationFetchThrottler:
- void SignalNetworkRequestNeeded() override {
- signaled_network_request_needed_ = true;
- }
-
- void InformOfNetworkRequestComplete(bool success) override {
- OnInformOfNetworkRequestComplete(success);
- reset_signaled_network_request_needed();
- }
-
- bool signaled_network_request_needed_;
-
- DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetchThrottler);
-};
-
-const char kTestFacetURIAlpha1[] = "https://one.alpha.example.com";
-const char kTestFacetURIAlpha2[] = "https://two.alpha.example.com";
-const char kTestFacetURIAlpha3[] = "https://three.alpha.example.com";
-const char kTestFacetURIBeta1[] = "https://one.beta.example.com";
-const char kTestFacetURIBeta2[] = "https://two.beta.example.com";
-const char kTestFacetURIGamma1[] = "https://gamma.example.com";
-
-AffiliatedFacets GetTestEquivalenceClassAlpha() {
- AffiliatedFacets affiliated_facets;
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha3));
- return affiliated_facets;
-}
-
-AffiliatedFacets GetTestEquivalenceClassBeta() {
- AffiliatedFacets affiliated_facets;
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIBeta2));
- return affiliated_facets;
-}
-
-AffiliatedFacets GetTestEquivalenceClassGamma() {
- AffiliatedFacets affiliated_facets;
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
- return affiliated_facets;
-}
-
-base::TimeDelta GetCacheHardExpiryPeriod() {
- return base::TimeDelta::FromHours(FacetManager::kCacheHardExpiryInHours);
-}
-
-base::TimeDelta GetCacheSoftExpiryPeriod() {
- return base::TimeDelta::FromHours(FacetManager::kCacheSoftExpiryInHours);
-}
-
-base::TimeDelta GetShortTestPeriod() {
- return base::TimeDelta::FromHours(1);
-}
-
-// Returns a smallest time difference that this test cares about.
-base::TimeDelta Epsilon() {
- return base::TimeDelta::FromMicroseconds(1);
-}
-
-} // namespace
-
-class AffiliationBackendTest : public testing::Test {
- public:
- AffiliationBackendTest()
- : backend_task_runner_(new base::TestMockTimeTaskRunner),
- consumer_task_runner_(new base::TestSimpleTaskRunner),
- mock_fetch_throttler_(nullptr) {}
- ~AffiliationBackendTest() override {}
-
- protected:
- void GetAffiliations(MockAffiliationConsumer* consumer,
- const FacetURI& facet_uri,
- StrategyOnCacheMiss cache_miss_strategy) {
- backend_->GetAffiliations(facet_uri, cache_miss_strategy,
- consumer->GetResultCallback(),
- consumer_task_runner());
- }
-
- void Prefetch(const FacetURI& facet_uri, base::Time keep_fresh_until) {
- backend_->Prefetch(facet_uri, keep_fresh_until);
- }
-
- void CancelPrefetch(const FacetURI& facet_uri, base::Time keep_fresh_until) {
- backend_->CancelPrefetch(facet_uri, keep_fresh_until);
- }
-
- void ExpectNeedForFetchAndLetItBeSent() {
- ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
- ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
- ASSERT_TRUE(mock_fetch_throttler()->LetNetworkRequestBeSent());
- }
-
- void ExpectAndCompleteFetch(
- const std::vector<FacetURI>& expected_requested_facet_uris) {
- ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
- EXPECT_THAT(
- fake_affiliation_api()->GetNextRequestedFacets(),
- testing::UnorderedElementsAreArray(expected_requested_facet_uris));
- mock_fetch_throttler()->ExpectInformOfNetworkRequestComplete(true);
- fake_affiliation_api()->ServeNextRequest();
- testing::Mock::VerifyAndClearExpectations(mock_fetch_throttler());
- }
-
- void ExpectAndCompleteFetch(const FacetURI& expected_requested_facet_uri) {
- std::vector<FacetURI> expected_facet_uris;
- expected_facet_uris.push_back(expected_requested_facet_uri);
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(expected_facet_uris));
- }
-
- void ExpectAndFailFetch(const FacetURI& expected_requested_facet_uri) {
- ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
- EXPECT_THAT(fake_affiliation_api()->GetNextRequestedFacets(),
- testing::UnorderedElementsAre(expected_requested_facet_uri));
- mock_fetch_throttler()->ExpectInformOfNetworkRequestComplete(false);
- fake_affiliation_api()->FailNextRequest();
- testing::Mock::VerifyAndClearExpectations(mock_fetch_throttler());
- }
-
- void ExpectNoFetchNeeded() {
- ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
- ASSERT_FALSE(mock_fetch_throttler()->has_signaled_network_request_needed());
- }
-
- void ExpectFailureWithoutFetch(MockAffiliationConsumer* consumer) {
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- consumer->ExpectFailure();
- consumer_task_runner_->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(consumer);
- }
-
- void GetAffiliationsAndExpectFetchAndThenResult(
- const FacetURI& facet_uri,
- const AffiliatedFacets& expected_result) {
- GetAffiliations(mock_consumer(), facet_uri,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
- mock_consumer()->ExpectSuccessWithResult(expected_result);
- consumer_task_runner_->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
- }
-
- void GetAffiliationsAndExpectResultWithoutFetch(
- const FacetURI& facet_uri,
- StrategyOnCacheMiss cache_miss_strategy,
- const AffiliatedFacets& expected_result) {
- GetAffiliations(mock_consumer(), facet_uri, cache_miss_strategy);
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- mock_consumer()->ExpectSuccessWithResult(expected_result);
- consumer_task_runner_->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
- }
-
- // TODO(engedy): Within this test fixture, the word "failure" refers to GTest
- // failures, simulated network failures (above), and also AffiliationService
- // failure callbacks. Make this less ambiguous.
- void GetAffiliationsAndExpectFailureWithoutFetch(const FacetURI& facet_uri) {
- GetAffiliations(mock_consumer(), facet_uri, StrategyOnCacheMiss::FAIL);
- ASSERT_NO_FATAL_FAILURE(ExpectFailureWithoutFetch(mock_consumer()));
- }
-
- void PrefetchAndExpectFetch(const FacetURI& facet_uri,
- base::Time keep_fresh_until) {
- Prefetch(facet_uri, keep_fresh_until);
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
- }
-
- // Verifies that both on-demand and cached-only GetAffiliations() requests for
- // each facet in |affiliated_facets| are served from cache with no fetches.
- void ExpectThatEquivalenceClassIsServedFromCache(
- const AffiliatedFacets& affiliated_facets) {
- for (const FacetURI& facet_uri : affiliated_facets) {
- SCOPED_TRACE(facet_uri);
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
- facet_uri, StrategyOnCacheMiss::FAIL, affiliated_facets));
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
- facet_uri, StrategyOnCacheMiss::FAIL, affiliated_facets));
- }
- }
-
- void DestroyBackend() { backend_.reset(); }
-
- void AdvanceTime(base::TimeDelta delta) {
- backend_task_runner_->FastForwardBy(delta);
- }
-
- // Directly opens the database and returns the number of equivalence classes
- // stored therein.
- size_t GetNumOfEquivalenceClassInDatabase() {
- AffiliationDatabase database;
- EXPECT_TRUE(database.Init(db_path()));
- std::vector<AffiliatedFacetsWithUpdateTime> all_affiliations;
- database.GetAllAffiliations(&all_affiliations);
- return all_affiliations.size();
- }
-
- size_t backend_facet_manager_count() {
- return backend()->facet_manager_count_for_testing();
- }
-
- bool IsCachedDataFreshForFacet(const FacetURI& facet_uri) {
- std::unique_ptr<base::Clock> clock(backend_task_runner_->GetMockClock());
- return FacetManager(facet_uri, backend(), clock.get()).IsCachedDataFresh();
- }
-
- bool IsCachedDataNearStaleForFacet(const FacetURI& facet_uri) {
- std::unique_ptr<base::Clock> clock(backend_task_runner_->GetMockClock());
- return FacetManager(facet_uri, backend(), clock.get())
- .IsCachedDataNearStale();
- }
-
- AffiliationBackend* backend() { return backend_.get(); }
-
- const base::FilePath& db_path() const { return db_path_; }
-
- base::TestMockTimeTaskRunner* backend_task_runner() {
- return backend_task_runner_.get();
- }
-
- base::TestSimpleTaskRunner* consumer_task_runner() {
- return consumer_task_runner_.get();
- }
-
- ScopedFakeAffiliationAPI* fake_affiliation_api() {
- return &fake_affiliation_api_;
- }
-
- MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
-
- MockAffiliationFetchThrottler* mock_fetch_throttler() {
- return mock_fetch_throttler_;
- }
-
- private:
- // testing::Test:
- void SetUp() override {
- ASSERT_TRUE(CreateTemporaryFile(&db_path_));
- backend_.reset(new AffiliationBackend(
- NULL, backend_task_runner_, backend_task_runner_->GetMockClock(),
- backend_task_runner_->GetMockTickClock()));
- backend_->Initialize(db_path());
- mock_fetch_throttler_ = new MockAffiliationFetchThrottler(backend_.get());
- backend_->SetThrottlerForTesting(
- base::WrapUnique<AffiliationFetchThrottler>(mock_fetch_throttler_));
-
- fake_affiliation_api_.AddTestEquivalenceClass(
- GetTestEquivalenceClassAlpha());
- fake_affiliation_api_.AddTestEquivalenceClass(
- GetTestEquivalenceClassBeta());
- fake_affiliation_api_.AddTestEquivalenceClass(
- GetTestEquivalenceClassGamma());
- }
-
- scoped_refptr<base::TestMockTimeTaskRunner> backend_task_runner_;
- scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
-
- base::FilePath db_path_;
- ScopedFakeAffiliationAPI fake_affiliation_api_;
- MockAffiliationConsumer mock_consumer_;
- std::unique_ptr<AffiliationBackend> backend_;
- MockAffiliationFetchThrottler* mock_fetch_throttler_; // Owned by |backend_|.
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationBackendTest);
-};
-
-TEST_F(AffiliationBackendTest, OnDemandRequestSucceedsWithFetch) {
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
- EXPECT_EQ(0u, backend_facet_manager_count());
-
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIBeta1),
- GetTestEquivalenceClassBeta()));
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-// This test also verifies that the FacetManager is immediately discarded.
-TEST_F(AffiliationBackendTest, CachedOnlyRequestFailsDueToCacheMiss) {
- GetAffiliationsAndExpectFailureWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-TEST_F(AffiliationBackendTest, PrefetchTriggersInitialFetch) {
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
-}
-
-// This test also verifies that the FacetManager is immediately discarded.
-TEST_F(AffiliationBackendTest, ExpiredPrefetchTriggersNoInitialFetch) {
- // Prefetch intervals are open from the right, thus intervals ending Now() are
- // already expired.
- Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- backend_task_runner()->Now());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_EQ(0u, backend_facet_manager_count());
- EXPECT_FALSE(backend_task_runner()->HasPendingTask());
-}
-
-// One additional GetAffiliations() and one Prefetch() request come in, both for
-// unrelated facets, shortly after an initial GetAffiliations() request.
-//
-// Suppose that the network request triggered by the first GetAffiliations()
-// request has already been initiated when the other requests arrive. As there
-// should be no simultaneous requests, the additional facets should be queried
-// together in a second fetch after the first fetch completes.
-TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests) {
- FacetURI facet_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
- FacetURI facet_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
- FacetURI facet_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
-
- // Pretend the fetch is already away when the two other requests come in.
- MockAffiliationConsumer second_consumer;
- GetAffiliations(mock_consumer(), facet_alpha,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- GetAffiliations(&second_consumer, facet_beta,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- Prefetch(facet_gamma, base::Time::Max());
-
- std::vector<FacetURI> second_fetch_uris;
- second_fetch_uris.push_back(facet_beta);
- second_fetch_uris.push_back(facet_gamma);
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_alpha));
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(second_fetch_uris));
-
- mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta());
- consumer_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-
- // Now that the two GetAffiliation() requests have been completed, the first
- // two FacetManagers should be discarded. The third FacetManager corresponding
- // to the prefetched facet should be kept.
- EXPECT_GE(1u, backend_facet_manager_count());
-}
-
-// Now suppose that the first fetch is somewhat delayed (e.g., because network
-// requests are throttled), so the other requests arrive before it is actually
-// issued. In this case, all facet URIs should be queried together in one fetch.
-TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests2) {
- FacetURI facet_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
- FacetURI facet_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
- FacetURI facet_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
-
- MockAffiliationConsumer second_consumer;
- GetAffiliations(mock_consumer(), facet_alpha,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- GetAffiliations(&second_consumer, facet_beta,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- Prefetch(facet_gamma, base::Time::Max());
-
- std::vector<FacetURI> fetched_uris;
- fetched_uris.push_back(facet_alpha);
- fetched_uris.push_back(facet_beta);
- fetched_uris.push_back(facet_gamma);
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(fetched_uris));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
-
- mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta());
- consumer_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-
- // Now that the two GetAffiliation() requests have been completed, the first
- // two FacetManagers should be discarded. The third FacetManager corresponding
- // to the prefetched facet should be kept.
- EXPECT_GE(1u, backend_facet_manager_count());
-}
-
-TEST_F(AffiliationBackendTest, RetryIsMadeOnFailedFetch) {
- FacetURI facet_uri(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
-
- GetAffiliations(mock_consumer(), facet_uri,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(ExpectAndFailFetch(facet_uri));
- EXPECT_EQ(1u, backend_facet_manager_count());
-
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
-
- mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- consumer_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-// The Prefetch() request expires before fetching corresponding affiliation
-// information would be allowed. The fetch should be abandoned.
-TEST_F(AffiliationBackendTest, FetchIsNoLongerNeededOnceAllowed) {
- Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- backend_task_runner()->Now() + GetShortTestPeriod());
- ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
- ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
-
- AdvanceTime(GetShortTestPeriod() + Epsilon());
-
- bool did_send_request = mock_fetch_throttler()->LetNetworkRequestBeSent();
- EXPECT_FALSE(did_send_request);
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForSameFacet) {
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
-
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassAlpha()));
-
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha()));
-
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForAffiliatedFacet) {
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
-
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-TEST_F(AffiliationBackendTest, CacheServesRequestsForPrefetchedFacets) {
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
-
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassAlpha()));
-
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha()));
-}
-
-TEST_F(AffiliationBackendTest,
- CacheServesRequestsForFacetsAffiliatedWithPrefetchedFacets) {
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
-
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-}
-
-// A second GetAffiliations() request for the same facet and a third request
-// for an affiliated facet comes in while the network fetch triggered by the
-// first request is in flight.
-//
-// There should be no simultaneous requests, and once the fetch completes, all
-// three requests should be served without further fetches (they have the data).
-TEST_F(AffiliationBackendTest,
- CacheServesConcurrentRequestsForAffiliatedFacets) {
- FacetURI facet_uri1(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
- FacetURI facet_uri2(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
-
- MockAffiliationConsumer second_consumer;
- MockAffiliationConsumer third_consumer;
- GetAffiliations(mock_consumer(), facet_uri1,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- GetAffiliations(&second_consumer, facet_uri1,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- GetAffiliations(&third_consumer, facet_uri2,
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
-
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
-
- mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- third_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- consumer_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-// A second Prefetch() request for the same facet and a third request for an
-// affiliated facet comes in while the initial fetch triggered by the first
-// request is in flight.
-//
-// There should be no simultaneous requests, and once the fetch completes, there
-// should be no further initial fetches as the data needed is already there.
-TEST_F(AffiliationBackendTest,
- CacheServesConcurrentPrefetchesForAffiliatedFacets) {
- FacetURI facet_uri1(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
- FacetURI facet_uri2(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
-
- Prefetch(facet_uri1, base::Time::Max());
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- Prefetch(facet_uri1, base::Time::Max());
- Prefetch(facet_uri2, base::Time::Max());
-
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
-
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-}
-
-TEST_F(AffiliationBackendTest, SimpleCacheExpiryWithoutPrefetches) {
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
-
- AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
-
- EXPECT_TRUE(IsCachedDataFreshForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-
- AdvanceTime(Epsilon());
-
- // After the data becomes stale, the cached-only request should fail, but the
- // subsequent on-demand request should fetch the data again and succeed.
- EXPECT_FALSE(IsCachedDataFreshForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- GetAffiliationsAndExpectFailureWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
-
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
- GetTestEquivalenceClassAlpha()));
-
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-
- EXPECT_EQ(0u, backend_facet_manager_count());
-}
-
-// A Prefetch() request for a finite period. It should trigger an initial fetch
-// and exactly one refetch, as the Prefetch() request expires exactly when the
-// cached data obtained with the refetch expires.
-TEST_F(AffiliationBackendTest,
- PrefetchTriggersOneInitialFetchAndOneRefetchBeforeExpiring) {
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- backend_task_runner()->Now() + GetCacheHardExpiryPeriod() +
- GetCacheSoftExpiryPeriod()));
-
- AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
-
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_FALSE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
-
- AdvanceTime(Epsilon());
-
- EXPECT_TRUE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(
- ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
-
- AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
-
- EXPECT_TRUE(IsCachedDataFreshForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- EXPECT_TRUE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-
- AdvanceTime(Epsilon());
-
- // The data should be allowed to expire and the FacetManager discarded.
- EXPECT_FALSE(IsCachedDataFreshForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_EQ(0u, backend_facet_manager_count());
- EXPECT_FALSE(backend_task_runner()->HasPendingTask());
-
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFailureWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFailureWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2)));
-
- // However, a subsequent on-demand request should be able to trigger a fetch.
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
-}
-
-// Affiliation data for prefetched facets should be automatically refetched once
-// every 23 hours, and GetAffiliations() requests regarding affiliated facets
-// should be continuously served from cache.
-TEST_F(AffiliationBackendTest, PrefetchTriggersPeriodicRefetch) {
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
-
- for (int cycle = 0; cycle < 3; ++cycle) {
- SCOPED_TRACE(cycle);
-
- AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
-
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_TRUE(IsCachedDataFreshForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- EXPECT_FALSE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-
- AdvanceTime(Epsilon());
-
- EXPECT_TRUE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
- }
-}
-
-TEST_F(AffiliationBackendTest,
- PrefetchTriggersNoInitialFetchIfDataIsAlreadyFresh) {
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
-
- EXPECT_FALSE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
-
- Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-}
-
-TEST_F(AffiliationBackendTest, CancelPrefetch) {
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
-
- AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
-
- // Cancel the prefetch the last microsecond before a refetch would take place.
- backend()->CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- base::Time::Max());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_EQ(0u, backend_facet_manager_count());
- EXPECT_TRUE(backend_task_runner()->HasPendingTask());
-
- AdvanceTime(GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod() +
- Epsilon());
-
- // The data should be allowed to expire.
- EXPECT_FALSE(backend_task_runner()->HasPendingTask());
- EXPECT_TRUE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFailureWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFailureWithoutFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2)));
-}
-
-TEST_F(AffiliationBackendTest, CancelDuplicatePrefetch) {
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
- Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max());
-
- AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
-
- // Cancel the prefetch the last microsecond before a refetch would take place.
- backend()->CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- base::Time::Max());
-
- AdvanceTime(Epsilon());
-
- // However, there is a second Prefetch() request which should keep the data
- // fresh.
- EXPECT_EQ(1u, backend_facet_manager_count());
- EXPECT_TRUE(IsCachedDataNearStaleForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
- ASSERT_NO_FATAL_FAILURE(
- ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
-
- AdvanceTime(GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod());
-
- EXPECT_TRUE(IsCachedDataFreshForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
-}
-
-// Canceling a non-existing prefetch request for a non-prefetched facet.
-TEST_F(AffiliationBackendTest, CancelingNonExistingPrefetchIsSilentlyIgnored) {
- CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- backend_task_runner()->Now() + base::TimeDelta::FromHours(24));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_EQ(0u, backend_facet_manager_count());
- EXPECT_FALSE(backend_task_runner()->HasPendingTask());
-}
-
-// Verify removal of equivalence classes that contain only facets for which
-// there are no FacetManagers.
-TEST_F(AffiliationBackendTest, TrimCacheDiscardsDataWithoutFacetManagers) {
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIBeta1),
- backend_task_runner()->Now() + GetShortTestPeriod()));
- Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIBeta2), base::Time::Max());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_EQ(2u, GetNumOfEquivalenceClassInDatabase());
-
- AdvanceTime(GetShortTestPeriod());
-
- // Worst-case scenario: the first prefetch has just expired, the other has
- // just been canceled when TrimCache() is called; plus immediately afterwards
- // the backend is destroyed.
- ASSERT_NO_FATAL_FAILURE(CancelPrefetch(
- FacetURI::FromCanonicalSpec(kTestFacetURIBeta2), base::Time::Max()));
- backend()->TrimCache();
- DestroyBackend();
- EXPECT_EQ(0u, GetNumOfEquivalenceClassInDatabase());
-}
-
-// Verify removal of equivalence classes that contain only facets for which
-// there are only FacetManagers that do not need the data.
-TEST_F(AffiliationBackendTest,
- TrimCacheDiscardsDataNoLongerNeededByFacetManagers) {
- FacetURI facet_uri(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
-
- // Set up some stale data in the database.
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- facet_uri, GetTestEquivalenceClassAlpha()));
- AdvanceTime(GetCacheHardExpiryPeriod());
- EXPECT_FALSE(IsCachedDataFreshForFacet(facet_uri));
-
- // Now start prefetching the same facet, but keep the network fetch hanging.
- Prefetch(facet_uri, base::Time::Max());
- ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
- EXPECT_EQ(1u, GetNumOfEquivalenceClassInDatabase());
-
- // The already stale data should be removed regardless of the active prefetch.
- backend()->TrimCache();
- EXPECT_EQ(0u, GetNumOfEquivalenceClassInDatabase());
-
- mock_fetch_throttler()->reset_signaled_network_request_needed();
-}
-
-// Verify preservation of equivalence classes that contain >= 1 facet for which
-// there is a FacetManager claiming that it needs to keep the data.
-TEST_F(AffiliationBackendTest, TrimCacheRetainsDataThatNeededByFacetManagers) {
- FacetURI facet_uri(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
-
- ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
- facet_uri, backend_task_runner()->Now() + GetCacheHardExpiryPeriod()));
- backend()->TrimCache();
-
- // Also verify that the last update time of the affiliation data is preserved,
- // i.e., that it expires when it would normally have expired.
- AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
- EXPECT_TRUE(IsCachedDataFreshForFacet(facet_uri));
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassAlpha()));
- AdvanceTime(Epsilon());
- EXPECT_FALSE(IsCachedDataFreshForFacet(facet_uri));
- ASSERT_NO_FATAL_FAILURE(
- GetAffiliationsAndExpectFailureWithoutFetch(facet_uri));
-}
-
-// Verify that TrimCacheForFacet() only removes the equivalence class for the
-// given facet, and preserves others (even if they could be discarded).
-TEST_F(AffiliationBackendTest,
- TrimCacheForFacetOnlyRemovesDataForTheGivenFacet) {
- FacetURI preserved_facet_uri(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- GetTestEquivalenceClassAlpha()));
- ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
- preserved_facet_uri, GetTestEquivalenceClassBeta()));
- EXPECT_EQ(2u, GetNumOfEquivalenceClassInDatabase());
-
- backend()->TrimCacheForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
- EXPECT_EQ(1u, GetNumOfEquivalenceClassInDatabase());
-
- // Also verify that the last update time of the affiliation data is preserved,
- // i.e., that it expires when it would normally have expired.
- AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
- EXPECT_TRUE(IsCachedDataFreshForFacet(preserved_facet_uri));
- ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
- GetTestEquivalenceClassBeta()));
- AdvanceTime(Epsilon());
- EXPECT_FALSE(IsCachedDataFreshForFacet(preserved_facet_uri));
- ASSERT_NO_FATAL_FAILURE(
- GetAffiliationsAndExpectFailureWithoutFetch(preserved_facet_uri));
-}
-
-TEST_F(AffiliationBackendTest, NothingExplodesWhenShutDownDuringFetch) {
- GetAffiliations(mock_consumer(),
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
- mock_fetch_throttler()->reset_signaled_network_request_needed();
- DestroyBackend();
-}
-
-TEST_F(AffiliationBackendTest,
- FailureCallbacksAreCalledIfBackendIsDestroyedWithPendingRequest) {
- GetAffiliations(mock_consumer(),
- FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- // Currently, a GetAffiliations() request can only be blocked due to fetch in
- // flight -- so emulate this condition when destroying the backend.
- ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
- mock_fetch_throttler()->reset_signaled_network_request_needed();
- DestroyBackend();
- mock_consumer()->ExpectFailure();
- consumer_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-}
-
-TEST_F(AffiliationBackendTest, DeleteCache) {
- DestroyBackend();
- ASSERT_TRUE(base::PathExists(db_path()));
- AffiliationBackend::DeleteCache(db_path());
- ASSERT_FALSE(base::PathExists(db_path()));
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_database.cc b/chromium/components/password_manager/core/browser/affiliation_database.cc
deleted file mode 100644
index 1052aa9aa78..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_database.cc
+++ /dev/null
@@ -1,266 +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/password_manager/core/browser/affiliation_database.h"
-
-#include <stdint.h>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "sql/connection.h"
-#include "sql/error_delegate_util.h"
-#include "sql/meta_table.h"
-#include "sql/statement.h"
-#include "sql/transaction.h"
-
-namespace password_manager {
-
-namespace {
-const int kVersion = 1;
-const int kCompatibleVersion = 1;
-} // namespace
-
-AffiliationDatabase::AffiliationDatabase() {
-}
-
-AffiliationDatabase::~AffiliationDatabase() {
-}
-
-bool AffiliationDatabase::Init(const base::FilePath& path) {
- sql_connection_.reset(new sql::Connection);
- sql_connection_->set_histogram_tag("Affiliation");
- sql_connection_->set_error_callback(base::Bind(
- &AffiliationDatabase::SQLErrorCallback, base::Unretained(this)));
-
- if (!sql_connection_->Open(path))
- return false;
-
- if (!sql_connection_->Execute("PRAGMA foreign_keys=1")) {
- sql_connection_->Poison();
- return false;
- }
-
- sql::MetaTable metatable;
- if (!metatable.Init(sql_connection_.get(), kVersion, kCompatibleVersion)) {
- sql_connection_->Poison();
- return false;
- }
-
- if (metatable.GetCompatibleVersionNumber() > kVersion) {
- LOG(WARNING) << "AffiliationDatabase is too new.";
- sql_connection_->Poison();
- return false;
- }
-
- if (!CreateTablesAndIndicesIfNeeded()) {
- sql_connection_->Poison();
- return false;
- }
-
- return true;
-}
-
-bool AffiliationDatabase::GetAffiliationsForFacet(
- const FacetURI& facet_uri,
- AffiliatedFacetsWithUpdateTime* result) const {
- DCHECK(result);
- result->facets.clear();
-
- sql::Statement statement(sql_connection_->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT m2.facet_uri, c.last_update_time "
- "FROM eq_class_members m1, eq_class_members m2, eq_classes c "
- "WHERE m1.facet_uri = ? AND m1.set_id = m2.set_id AND m1.set_id = c.id"));
- statement.BindString(0, facet_uri.canonical_spec());
-
- while (statement.Step()) {
- result->facets.push_back(
- FacetURI::FromCanonicalSpec(statement.ColumnString(0)));
- result->last_update_time =
- base::Time::FromInternalValue(statement.ColumnInt64(1));
- }
-
- return !result->facets.empty();
-}
-
-void AffiliationDatabase::GetAllAffiliations(
- std::vector<AffiliatedFacetsWithUpdateTime>* results) const {
- DCHECK(results);
- results->clear();
-
- sql::Statement statement(sql_connection_->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT m.facet_uri, c.last_update_time, c.id "
- "FROM eq_class_members m, eq_classes c "
- "WHERE m.set_id = c.id "
- "ORDER BY c.id"));
-
- int64_t last_eq_class_id = 0;
- while (statement.Step()) {
- int64_t eq_class_id = statement.ColumnInt64(2);
- if (results->empty() || eq_class_id != last_eq_class_id) {
- results->push_back(AffiliatedFacetsWithUpdateTime());
- last_eq_class_id = eq_class_id;
- }
- results->back().facets.push_back(
- FacetURI::FromCanonicalSpec(statement.ColumnString(0)));
- results->back().last_update_time =
- base::Time::FromInternalValue(statement.ColumnInt64(1));
- }
-}
-
-void AffiliationDatabase::DeleteAffiliationsForFacet(
- const FacetURI& facet_uri) {
- sql::Transaction transaction(sql_connection_.get());
- if (!transaction.Begin())
- return;
-
- sql::Statement statement_lookup(sql_connection_->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT m.set_id FROM eq_class_members m "
- "WHERE m.facet_uri = ?"));
- statement_lookup.BindString(0, facet_uri.canonical_spec());
-
- // No such |facet_uri|, nothing to do.
- if (!statement_lookup.Step())
- return;
-
- int64_t eq_class_id = statement_lookup.ColumnInt64(0);
-
- // Children will get deleted due to 'ON DELETE CASCADE'.
- sql::Statement statement_parent(sql_connection_->GetCachedStatement(
- SQL_FROM_HERE, "DELETE FROM eq_classes WHERE eq_classes.id = ?"));
- statement_parent.BindInt64(0, eq_class_id);
- if (!statement_parent.Run())
- return;
-
- transaction.Commit();
-}
-
-void AffiliationDatabase::DeleteAffiliationsOlderThan(
- const base::Time& cutoff_threshold) {
- // Children will get deleted due to 'ON DELETE CASCADE'.
- sql::Statement statement_parent(sql_connection_->GetCachedStatement(
- SQL_FROM_HERE,
- "DELETE FROM eq_classes "
- "WHERE eq_classes.last_update_time < ?"));
- statement_parent.BindInt64(0, cutoff_threshold.ToInternalValue());
- statement_parent.Run();
-}
-
-void AffiliationDatabase::DeleteAllAffiliations() {
- // Children will get deleted due to 'ON DELETE CASCADE'.
- sql::Statement statement_parent(
- sql_connection_->GetUniqueStatement("DELETE FROM eq_classes"));
- statement_parent.Run();
-}
-
-bool AffiliationDatabase::Store(
- const AffiliatedFacetsWithUpdateTime& affiliated_facets) {
- DCHECK(!affiliated_facets.facets.empty());
-
- sql::Statement statement_parent(sql_connection_->GetCachedStatement(
- SQL_FROM_HERE, "INSERT INTO eq_classes(last_update_time) VALUES (?)"));
-
- sql::Statement statement_child(sql_connection_->GetCachedStatement(
- SQL_FROM_HERE,
- "INSERT INTO eq_class_members(facet_uri, set_id) VALUES (?, ?)"));
-
- sql::Transaction transaction(sql_connection_.get());
- if (!transaction.Begin())
- return false;
-
- statement_parent.BindInt64(
- 0, affiliated_facets.last_update_time.ToInternalValue());
- if (!statement_parent.Run())
- return false;
-
- int64_t eq_class_id = sql_connection_->GetLastInsertRowId();
- for (const FacetURI& uri : affiliated_facets.facets) {
- statement_child.Reset(true);
- statement_child.BindString(0, uri.canonical_spec());
- statement_child.BindInt64(1, eq_class_id);
- if (!statement_child.Run())
- return false;
- }
-
- return transaction.Commit();
-}
-
-void AffiliationDatabase::StoreAndRemoveConflicting(
- const AffiliatedFacetsWithUpdateTime& affiliation,
- std::vector<AffiliatedFacetsWithUpdateTime>* removed_affiliations) {
- DCHECK(!affiliation.facets.empty());
- DCHECK(removed_affiliations);
- removed_affiliations->clear();
-
- sql::Transaction transaction(sql_connection_.get());
- if (!transaction.Begin())
- return;
-
- for (const FacetURI& uri : affiliation.facets) {
- AffiliatedFacetsWithUpdateTime old_affiliation;
- if (GetAffiliationsForFacet(uri, &old_affiliation)) {
- if (!AreEquivalenceClassesEqual(old_affiliation.facets,
- affiliation.facets)) {
- removed_affiliations->push_back(old_affiliation);
- }
- DeleteAffiliationsForFacet(uri);
- }
- }
-
- if (!Store(affiliation))
- NOTREACHED();
-
- transaction.Commit();
-}
-
-// static
-void AffiliationDatabase::Delete(const base::FilePath& path) {
- bool success = sql::Connection::Delete(path);
- DCHECK(success);
-}
-
-bool AffiliationDatabase::CreateTablesAndIndicesIfNeeded() {
- if (!sql_connection_->Execute(
- "CREATE TABLE IF NOT EXISTS eq_classes("
- "id INTEGER PRIMARY KEY,"
- "last_update_time INTEGER)")) {
- return false;
- }
-
- if (!sql_connection_->Execute(
- "CREATE TABLE IF NOT EXISTS eq_class_members("
- "id INTEGER PRIMARY KEY,"
- "facet_uri LONGVARCHAR UNIQUE NOT NULL,"
- "set_id INTEGER NOT NULL"
- " REFERENCES eq_classes(id) ON DELETE CASCADE)")) {
- return false;
- }
-
- // An index on eq_class_members.facet_uri is automatically created due to the
- // UNIQUE constraint, however, we must create one on eq_class_members.set_id
- // manually (to prevent linear scan when joining).
- return sql_connection_->Execute(
- "CREATE INDEX IF NOT EXISTS index_on_eq_class_members_set_id ON "
- "eq_class_members (set_id)");
-}
-
-void AffiliationDatabase::SQLErrorCallback(int error,
- sql::Statement* statement) {
- if (sql::IsErrorCatastrophic(error)) {
- // Normally this will poison the database, causing any subsequent operations
- // to silently fail without any side effects. However, if RazeAndClose() is
- // called from the error callback in response to an error raised from within
- // sql::Connection::Open, opening the now-razed database will be retried.
- sql_connection_->RazeAndClose();
- return;
- }
-
- // The default handling is to assert on debug and to ignore on release.
- if (!sql::Connection::IsExpectedSqliteError(error))
- DLOG(FATAL) << sql_connection_->GetErrorMessage();
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_database.h b/chromium/components/password_manager/core/browser/affiliation_database.h
deleted file mode 100644
index c9b418c5553..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_database.h
+++ /dev/null
@@ -1,93 +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_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_DATABASE_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_DATABASE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-
-namespace base {
-class FilePath;
-} // namespace base
-
-namespace sql {
-class Connection;
-class Statement;
-} // namespace sql
-
-namespace password_manager {
-
-// Stores equivalence classes of facets, i.e., facets that are affiliated with
-// each other, in an SQLite database. See affiliation_utils.h for a more
-// detailed definition of what this means.
-//
-// Under the assumption that there is most likely not much the caller can do in
-// case of database errors, most methods silently ignore them. Nevertheless, the
-// caller must plan ahead for this rare but non-negligible scenario, and expect
-// that in odd cases basic database invariants will not hold.
-class AffiliationDatabase {
- public:
- AffiliationDatabase();
- ~AffiliationDatabase();
-
- // Opens an existing database at |path|, or creates a new one if none exists,
- // and returns true on success.
- bool Init(const base::FilePath& path);
-
- // Looks up the equivalence class containing |facet_uri|, and returns true if
- // such a class is found, in which case it is also stored into |result|.
- bool GetAffiliationsForFacet(const FacetURI& facet_uri,
- AffiliatedFacetsWithUpdateTime* result) const;
-
- // Retrieves all stored equivalence classes.
- void GetAllAffiliations(
- std::vector<AffiliatedFacetsWithUpdateTime>* results) const;
-
- // Removes the stored equivalence class, if any, containing |facet_uri|.
- void DeleteAffiliationsForFacet(const FacetURI& facet_uri);
-
- // Removes stored equivalence classes that were last updated before the
- // |cutoff_threshold|.
- void DeleteAffiliationsOlderThan(const base::Time& cutoff_threshold);
-
- // Removes all records from all tables of the database.
- void DeleteAllAffiliations();
-
- // Stores the equivalence class defined by |affiliated_facets| to the DB and
- // returns true unless it has a non-empty subset with a preexisting class, in
- // which case no changes are made and the function returns false.
- bool Store(const AffiliatedFacetsWithUpdateTime& affiliated_facets);
-
- // Stores the equivalence class defined by |affiliated_facets| to the DB,
- // database, and removes any other equivalence classes that are in conflict
- // with |affiliated_facets|, i.e. those that are neither equal nor disjoint to
- // it. Removed equivalence classes are stored into |removed_affiliations|.
- void StoreAndRemoveConflicting(
- const AffiliatedFacetsWithUpdateTime& affiliated_facets,
- std::vector<AffiliatedFacetsWithUpdateTime>* removed_affiliations);
-
- // Deletes the database file at |path| along with all its auxiliary files. The
- // database must be closed before calling this.
- static void Delete(const base::FilePath& path);
-
- private:
- // Creates any tables and indices that do not already exist in the database.
- bool CreateTablesAndIndicesIfNeeded();
-
- // Called when SQLite encounters an error.
- void SQLErrorCallback(int error_number, sql::Statement* statement);
-
- // The SQL connection to the database.
- std::unique_ptr<sql::Connection> sql_connection_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationDatabase);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_DATABASE_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_database_unittest.cc b/chromium/components/password_manager/core/browser/affiliation_database_unittest.cc
deleted file mode 100644
index 939a5bdb108..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_database_unittest.cc
+++ /dev/null
@@ -1,309 +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/password_manager/core/browser/affiliation_database.h"
-
-#include <stdint.h>
-
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
-#include "sql/test/scoped_error_expecter.h"
-#include "sql/test/test_helpers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/sqlite/sqlite3.h"
-
-namespace password_manager {
-
-namespace {
-
-const char kTestFacetURI1[] = "https://alpha.example.com";
-const char kTestFacetURI2[] = "https://beta.example.com";
-const char kTestFacetURI3[] = "https://gamma.example.com";
-const char kTestFacetURI4[] = "https://delta.example.com";
-const char kTestFacetURI5[] = "https://epsilon.example.com";
-const char kTestFacetURI6[] = "https://zeta.example.com";
-
-const char kTestAndroidFacetURI[] = "android://hash@com.example.android";
-
-const int64_t kTestTimeUs1 = 1000000;
-const int64_t kTestTimeUs2 = 2000000;
-const int64_t kTestTimeUs3 = 3000000;
-
-void ExpectEquivalenceClassesAreEqual(
- const AffiliatedFacetsWithUpdateTime& expectation,
- const AffiliatedFacetsWithUpdateTime& reality) {
- EXPECT_EQ(expectation.last_update_time, reality.last_update_time);
- EXPECT_THAT(reality.facets,
- testing::UnorderedElementsAreArray(reality.facets));
-}
-
-AffiliatedFacetsWithUpdateTime TestEquivalenceClass1() {
- AffiliatedFacetsWithUpdateTime affiliation;
- affiliation.last_update_time = base::Time::FromInternalValue(kTestTimeUs1);
- affiliation.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI1));
- affiliation.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI2));
- affiliation.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
- return affiliation;
-}
-
-AffiliatedFacetsWithUpdateTime TestEquivalenceClass2() {
- AffiliatedFacetsWithUpdateTime affiliation;
- affiliation.last_update_time = base::Time::FromInternalValue(kTestTimeUs2);
- affiliation.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI4));
- affiliation.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI5));
- return affiliation;
-}
-
-AffiliatedFacetsWithUpdateTime TestEquivalenceClass3() {
- AffiliatedFacetsWithUpdateTime affiliation;
- affiliation.last_update_time = base::Time::FromInternalValue(kTestTimeUs3);
- affiliation.facets.push_back(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURI));
- return affiliation;
-}
-
-} // namespace
-
-class AffiliationDatabaseTest : public testing::Test {
- public:
- AffiliationDatabaseTest() {}
- ~AffiliationDatabaseTest() override {}
-
- void SetUp() override {
- ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
- OpenDatabase();
- }
-
- void OpenDatabase() {
- db_.reset(new AffiliationDatabase);
- ASSERT_TRUE(db_->Init(db_path()));
- }
-
- void CloseDatabase() { db_.reset(); }
-
- void StoreInitialTestData() {
- ASSERT_TRUE(db_->Store(TestEquivalenceClass1()));
- ASSERT_TRUE(db_->Store(TestEquivalenceClass2()));
- ASSERT_TRUE(db_->Store(TestEquivalenceClass3()));
- }
-
- AffiliationDatabase& db() { return *db_; }
-
- base::FilePath db_path() {
- return temp_directory_.GetPath().Append(
- FILE_PATH_LITERAL("Test Affiliation Database"));
- }
-
- private:
- base::ScopedTempDir temp_directory_;
- std::unique_ptr<AffiliationDatabase> db_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationDatabaseTest);
-};
-
-TEST_F(AffiliationDatabaseTest, Store) {
- LOG(ERROR) << "During this test, SQL errors (number 19) will be logged to "
- "the console. This is expected.";
-
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- // Verify that duplicate equivalence classes are not allowed to be stored.
- {
- sql::test::ScopedErrorExpecter expecter;
- expecter.ExpectError(SQLITE_CONSTRAINT);
- AffiliatedFacetsWithUpdateTime duplicate = TestEquivalenceClass1();
- EXPECT_FALSE(db().Store(duplicate));
- EXPECT_TRUE(expecter.SawExpectedErrors());
- }
-
- // Verify that intersecting equivalence classes are not allowed to be stored.
- {
- sql::test::ScopedErrorExpecter expecter;
- expecter.ExpectError(SQLITE_CONSTRAINT);
- AffiliatedFacetsWithUpdateTime intersecting;
- intersecting.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
- intersecting.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI4));
- EXPECT_FALSE(db().Store(intersecting));
- EXPECT_TRUE(expecter.SawExpectedErrors());
- }
-}
-
-TEST_F(AffiliationDatabaseTest, GetAllAffiliations) {
- std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
-
- // Empty database should not return any equivalence classes.
- db().GetAllAffiliations(&affiliations);
- EXPECT_EQ(0u, affiliations.size());
-
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- // The test data should be returned in order.
- db().GetAllAffiliations(&affiliations);
- ASSERT_EQ(3u, affiliations.size());
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass1(), affiliations[0]);
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass2(), affiliations[1]);
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass3(), affiliations[2]);
-}
-
-TEST_F(AffiliationDatabaseTest, GetAffiliationForFacet) {
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- // Verify that querying any element in the first equivalence class yields that
- // class.
- for (const auto& facet_uri : TestEquivalenceClass1().facets) {
- AffiliatedFacetsWithUpdateTime affiliation;
- EXPECT_TRUE(db().GetAffiliationsForFacet(facet_uri, &affiliation));
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass1(), affiliation);
- }
-
- // Verify that querying the sole element in the third equivalence class yields
- // that class.
- {
- AffiliatedFacetsWithUpdateTime affiliation;
- EXPECT_TRUE(db().GetAffiliationsForFacet(
- FacetURI::FromCanonicalSpec(kTestAndroidFacetURI), &affiliation));
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass3(), affiliation);
- }
-
- // Verify that querying a facet not in the database yields no result.
- {
- AffiliatedFacetsWithUpdateTime affiliation;
- EXPECT_FALSE(db().GetAffiliationsForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURI6), &affiliation));
- ExpectEquivalenceClassesAreEqual(AffiliatedFacetsWithUpdateTime(),
- affiliation);
- }
-}
-
-TEST_F(AffiliationDatabaseTest, StoreAndRemoveConflicting) {
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- AffiliatedFacetsWithUpdateTime updated = TestEquivalenceClass1();
- updated.last_update_time = base::Time::FromInternalValue(4000000);
-
- // Verify that duplicate equivalence classes are now allowed to be stored, and
- // the last update timestamp is updated.
- {
- std::vector<AffiliatedFacetsWithUpdateTime> removed;
- db().StoreAndRemoveConflicting(updated, &removed);
- EXPECT_EQ(0u, removed.size());
-
- AffiliatedFacetsWithUpdateTime affiliation;
- EXPECT_TRUE(db().GetAffiliationsForFacet(
- FacetURI::FromCanonicalSpec(kTestFacetURI1), &affiliation));
- ExpectEquivalenceClassesAreEqual(updated, affiliation);
- }
-
- // Verify that intersecting equivalence classes are now allowed to be stored,
- // the conflicting classes are removed, but unaffected classes are retained.
- {
- AffiliatedFacetsWithUpdateTime intersecting;
- std::vector<AffiliatedFacetsWithUpdateTime> removed;
- intersecting.last_update_time = base::Time::FromInternalValue(5000000);
- intersecting.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
- intersecting.facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI4));
- db().StoreAndRemoveConflicting(intersecting, &removed);
-
- ASSERT_EQ(2u, removed.size());
- ExpectEquivalenceClassesAreEqual(updated, removed[0]);
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass2(), removed[1]);
-
- std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
- db().GetAllAffiliations(&affiliations);
- ASSERT_EQ(2u, affiliations.size());
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass3(), affiliations[0]);
- ExpectEquivalenceClassesAreEqual(intersecting, affiliations[1]);
- }
-}
-
-TEST_F(AffiliationDatabaseTest, DeleteAllAffiliations) {
- db().DeleteAllAffiliations();
-
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- db().DeleteAllAffiliations();
-
- std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
- db().GetAllAffiliations(&affiliations);
- ASSERT_EQ(0u, affiliations.size());
-}
-
-TEST_F(AffiliationDatabaseTest, DeleteAffiliationsOlderThan) {
- db().DeleteAffiliationsOlderThan(base::Time::FromInternalValue(0));
-
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- db().DeleteAffiliationsOlderThan(base::Time::FromInternalValue(kTestTimeUs2));
-
- std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
- db().GetAllAffiliations(&affiliations);
- ASSERT_EQ(2u, affiliations.size());
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass2(), affiliations[0]);
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass3(), affiliations[1]);
-
- db().DeleteAffiliationsOlderThan(base::Time::Max());
-
- db().GetAllAffiliations(&affiliations);
- ASSERT_EQ(0u, affiliations.size());
-}
-
-// Verify that an existing DB can be reopened, and data is retained.
-TEST_F(AffiliationDatabaseTest, DBRetainsDataAfterReopening) {
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- CloseDatabase();
- OpenDatabase();
-
- // The test data should be returned in order.
- std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
- db().GetAllAffiliations(&affiliations);
- ASSERT_EQ(3u, affiliations.size());
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass1(), affiliations[0]);
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass2(), affiliations[1]);
- ExpectEquivalenceClassesAreEqual(TestEquivalenceClass3(), affiliations[2]);
-}
-
-// Verify that when it is discovered during opening that a DB is corrupt, it
-// gets razed, and then an empty (but again usable) DB is produced.
-TEST_F(AffiliationDatabaseTest, CorruptDBIsRazedThenOpened) {
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
-
- CloseDatabase();
- ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path()));
- ASSERT_NO_FATAL_FAILURE(OpenDatabase());
-
- std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
- db().GetAllAffiliations(&affiliations);
- EXPECT_EQ(0u, affiliations.size());
-
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
- db().GetAllAffiliations(&affiliations);
- EXPECT_EQ(3u, affiliations.size());
-}
-
-// Verify that when the DB becomes corrupt after it has been opened, it gets
-// poisoned so that operations fail silently without side effects.
-TEST_F(AffiliationDatabaseTest, CorruptDBGetsPoisoned) {
- ASSERT_TRUE(db().Store(TestEquivalenceClass1()));
-
- ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path()));
-
- EXPECT_FALSE(db().Store(TestEquivalenceClass2()));
- std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
- db().GetAllAffiliations(&affiliations);
- EXPECT_EQ(0u, affiliations.size());
-}
-
-// Verify that all files get deleted.
-TEST_F(AffiliationDatabaseTest, Delete) {
- ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
- CloseDatabase();
-
- AffiliationDatabase::Delete(db_path());
- EXPECT_TRUE(base::IsDirectoryEmpty(db_path().DirName()));
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler.cc b/chromium/components/password_manager/core/browser/affiliation_fetch_throttler.cc
deleted file mode 100644
index cb99eff00fc..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler.cc
+++ /dev/null
@@ -1,143 +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/password_manager/core/browser/affiliation_fetch_throttler.h"
-
-#include <stdint.h>
-
-#include "base/logging.h"
-#include "base/rand_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h"
-
-namespace password_manager {
-
-// static
-const net::BackoffEntry::Policy AffiliationFetchThrottler::kBackoffPolicy = {
- // Number of initial errors (in sequence) to ignore before going into
- // exponential backoff.
- 0,
-
- // Initial delay (in ms) once backoff starts.
- 10 * 1000, // 10 seconds
-
- // Factor by which the delay will be multiplied on each subsequent failure.
- 4,
-
- // Fuzzing percentage: 50% will spread delays randomly between 50%--100% of
- // the nominal time.
- .5, // 50%
-
- // Maximum delay (in ms) during exponential backoff.
- 6 * 3600 * 1000, // 6 hours
-
- // Time to keep an entry from being discarded even when it has no
- // significant state, -1 to never discard. (Not applicable.)
- -1,
-
- // False means that initial_delay_ms is the first delay once we start
- // exponential backoff, i.e., there is no delay after subsequent successful
- // requests.
- false,
-};
-
-// static
-const int64_t AffiliationFetchThrottler::kGracePeriodAfterReconnectMs =
- 10 * 1000; // 10 seconds
-
-AffiliationFetchThrottler::AffiliationFetchThrottler(
- AffiliationFetchThrottlerDelegate* delegate,
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
- base::TickClock* tick_clock)
- : delegate_(delegate),
- task_runner_(task_runner),
- tick_clock_(tick_clock),
- state_(IDLE),
- has_network_connectivity_(false),
- is_fetch_scheduled_(false),
- exponential_backoff_(new net::BackoffEntry(&kBackoffPolicy, tick_clock_)),
- weak_ptr_factory_(this) {
- 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);
- has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline();
-}
-
-AffiliationFetchThrottler::~AffiliationFetchThrottler() {
- net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
-}
-
-void AffiliationFetchThrottler::SignalNetworkRequestNeeded() {
- if (state_ != IDLE)
- return;
-
- state_ = FETCH_NEEDED;
- if (has_network_connectivity_)
- EnsureCallbackIsScheduled();
-}
-
-void AffiliationFetchThrottler::InformOfNetworkRequestComplete(bool success) {
- DCHECK_EQ(state_, FETCH_IN_FLIGHT);
- state_ = IDLE;
- exponential_backoff_->InformOfRequest(success);
-}
-
-void AffiliationFetchThrottler::EnsureCallbackIsScheduled() {
- DCHECK_EQ(state_, FETCH_NEEDED);
- DCHECK(has_network_connectivity_);
-
- if (is_fetch_scheduled_)
- return;
-
- is_fetch_scheduled_ = true;
- task_runner_->PostDelayedTask(
- FROM_HERE,
- base::Bind(&AffiliationFetchThrottler::OnBackoffDelayExpiredCallback,
- weak_ptr_factory_.GetWeakPtr()),
- exponential_backoff_->GetTimeUntilRelease());
-}
-
-void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() {
- DCHECK_EQ(state_, FETCH_NEEDED);
- DCHECK(is_fetch_scheduled_);
- 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
- // handler once again.
- if (!has_network_connectivity_)
- return;
-
- // The release time might have been increased if network connectivity was lost
- // and restored while this callback was in the task queue. If so, reschedule.
- if (exponential_backoff_->ShouldRejectRequest())
- EnsureCallbackIsScheduled();
- else
- state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE;
-}
-
-void AffiliationFetchThrottler::OnConnectionTypeChanged(
- net::NetworkChangeNotifier::ConnectionType type) {
- bool old_has_network_connectivity = has_network_connectivity_;
- has_network_connectivity_ =
- (type != net::NetworkChangeNotifier::CONNECTION_NONE);
-
- // Only react when network connectivity has been reestablished.
- if (!has_network_connectivity_ || old_has_network_connectivity)
- return;
-
- double grace_ms = kGracePeriodAfterReconnectMs *
- (1 - base::RandDouble() * kBackoffPolicy.jitter_factor);
- exponential_backoff_->SetCustomReleaseTime(std::max(
- exponential_backoff_->GetReleaseTime(),
- tick_clock_->NowTicks() + base::TimeDelta::FromMillisecondsD(grace_ms)));
-
- if (state_ == FETCH_NEEDED)
- EnsureCallbackIsScheduled();
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler.h b/chromium/components/password_manager/core/browser/affiliation_fetch_throttler.h
deleted file mode 100644
index 42743aaaf2d..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler.h
+++ /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.
-
-#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "net/base/backoff_entry.h"
-#include "net/base/network_change_notifier.h"
-
-namespace base {
-class TickClock;
-class SingleThreadTaskRunner;
-} // namespace base
-
-namespace password_manager {
-
-class AffiliationFetchThrottlerDelegate;
-
-// Implements the throttling logic that the AffiliationBackend will use when it
-// needs to issue requests over the network to fetch affiliation information.
-//
-// This class manages only the scheduling of the requests. It is up to the
-// consumer (the AffiliationBackend) to actually assemble and send the requests,
-// to report back about their success or failure, and to retry them if desired.
-// The process goes like this:
-// 1.) The consumer calls SignalNetworkRequestNeeded().
-// 2.) Once appropriate, OnCanSendNetworkRequest() is called on the delegate.
-// 3.) The consumer sends the request, and waits until it completes.
-// 4.) The consumer calls InformOfNetworkRequestComplete().
-// Note that only a single request at a time is supported.
-//
-// If the request fails in Step 3, the consumer should not automatically retry
-// it. Instead it should always proceed to Step 4, and then -- if retrying the
-// request is desired -- proceed immediately to Step 1. That is, it should act
-// as if another request was needed right away.
-//
-// Essentially, this class implements exponential backoff in case of network and
-// server errors with the additional constraint that no requests will be issued
-// in the first place while there is known to be no network connectivity. This
-// prevents the exponential backoff delay from growing huge during long offline
-// periods, so that requests will not be held back for too long after
-// connectivity is restored.
-class AffiliationFetchThrottler
- : public net::NetworkChangeNotifier::ConnectionTypeObserver {
- 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
- // |delegate| and |tick_clock| should outlive the throttler.
- AffiliationFetchThrottler(
- AffiliationFetchThrottlerDelegate* delegate,
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
- base::TickClock* tick_clock);
- ~AffiliationFetchThrottler() override;
-
- // Signals to the throttling logic that a network request is needed, and that
- // OnCanSendNetworkRequest() should be called as soon as the request can be
- // sent. OnCanSendNetworkRequest() will always be called asynchronously.
- //
- // Calls to this method will be ignored when a request is already known to be
- // needed or while a request is in flight. To signal that another request will
- // be needed right away after the current one, call this method after calling
- // InformOfNetworkRequestComplete().
- virtual void SignalNetworkRequestNeeded();
-
- // Informs the back-off logic that the in-flight network request has been
- // completed, either with |success| or not.
- virtual void InformOfNetworkRequestComplete(bool success);
-
- protected:
- AffiliationFetchThrottlerDelegate* delegate_;
-
- private:
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FailedRequests);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- GracePeriodAfterConnectivityIsRestored);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- GracePeriodAfterConnectivityIsRestored2);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- GracePeriodAfterConnectivityIsRestored3);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- ConnectivityLostDuringBackoff);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- ConnectivityLostAndRestoredDuringBackoff);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FlakyConnectivity);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- ConnectivityLostDuringRequest);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- ConnectivityLostAndRestoredDuringRequest);
- FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
- ConnectivityLostAndRestoredDuringRequest2);
-
- enum State { IDLE, FETCH_NEEDED, FETCH_IN_FLIGHT };
-
- // Exponential backoff parameters in case of network and server errors
- static const net::BackoffEntry::Policy kBackoffPolicy;
-
- // Minimum delay before sending the first request once network connectivity is
- // restored. The fuzzing factor in |kBackoffParameters.jitter_factor| applies.
- static const int64_t kGracePeriodAfterReconnectMs;
-
- // Ensures that OnBackoffDelayExpiredCallback() is scheduled to be called back
- // once the |exponential_backoff_| delay expires.
- void EnsureCallbackIsScheduled();
-
- // Called back when the |exponential_backoff_| delay expires.
- void OnBackoffDelayExpiredCallback();
-
- // net::NetworkChangeNotifier::ConnectionTypeObserver:
- void OnConnectionTypeChanged(
- net::NetworkChangeNotifier::ConnectionType type) override;
-
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- base::TickClock* tick_clock_;
- State state_;
- bool has_network_connectivity_;
- bool is_fetch_scheduled_;
- std::unique_ptr<net::BackoffEntry> exponential_backoff_;
-
- base::WeakPtrFactory<AffiliationFetchThrottler> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottler);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h b/chromium/components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h
deleted file mode 100644
index d1ad61cbf27..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h
+++ /dev/null
@@ -1,29 +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_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_DELEGATE_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_DELEGATE_H_
-
-namespace password_manager {
-
-// An interface that users of AffiliationFetchThrottler need to implement to get
-// notified once it is okay to issue the next network request.
-class AffiliationFetchThrottlerDelegate {
- public:
- // Will be called once the throttling policy allows issuing a network request,
- // provided SignalNetworkRequestNeeded() has been called at least once since
- // the last request.
- //
- // The implementation must return true if a request was actually issued in
- // response to this call, and then call InformOfNetworkRequestComplete() once
- // the request is complete. Otherwise, the implementation must return false.
- virtual bool OnCanSendNetworkRequest() = 0;
-
- protected:
- virtual ~AffiliationFetchThrottlerDelegate() {}
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_DELEGATE_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler_unittest.cc b/chromium/components/password_manager/core/browser/affiliation_fetch_throttler_unittest.cc
deleted file mode 100644
index 9d15f5a07b6..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetch_throttler_unittest.cc
+++ /dev/null
@@ -1,424 +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/password_manager/core/browser/affiliation_fetch_throttler.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <cmath>
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/numerics/safe_math.h"
-#include "base/run_loop.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h"
-#include "net/base/network_change_notifier.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-namespace {
-
-class MockAffiliationFetchThrottlerDelegate
- : public AffiliationFetchThrottlerDelegate {
- public:
- // The |tick_clock| should outlive this instance.
- explicit MockAffiliationFetchThrottlerDelegate(base::TickClock* tick_clock)
- : tick_clock_(tick_clock),
- emulated_return_value_(true),
- can_send_count_(0u) {}
- ~MockAffiliationFetchThrottlerDelegate() override {
- EXPECT_EQ(0u, can_send_count_);
- }
-
- void set_emulated_return_value(bool value) { emulated_return_value_ = value; }
- void reset_can_send_count() { can_send_count_ = 0u; }
- size_t can_send_count() const { return can_send_count_; }
- base::TimeTicks last_can_send_time() const { return last_can_send_time_; }
-
- // AffiliationFetchThrottlerDelegate:
- bool OnCanSendNetworkRequest() override {
- ++can_send_count_;
- last_can_send_time_ = tick_clock_->NowTicks();
- return emulated_return_value_;
- }
-
- private:
- base::TickClock* tick_clock_;
- bool emulated_return_value_;
- size_t can_send_count_;
- base::TimeTicks last_can_send_time_;
-
- DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetchThrottlerDelegate);
-};
-
-} // namespace
-
-class AffiliationFetchThrottlerTest : public testing::Test {
- public:
- AffiliationFetchThrottlerTest()
- : network_change_notifier_(net::NetworkChangeNotifier::CreateMock()),
- task_runner_(new base::TestMockTimeTaskRunner),
- mock_tick_clock_(task_runner_->GetMockTickClock()),
- mock_delegate_(mock_tick_clock_.get()) {}
- ~AffiliationFetchThrottlerTest() override {}
-
- std::unique_ptr<AffiliationFetchThrottler> CreateThrottler() {
- return base::MakeUnique<AffiliationFetchThrottler>(
- &mock_delegate_, task_runner_, mock_tick_clock_.get());
- }
-
- void SimulateHasNetworkConnectivity(bool has_connectivity) {
- net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
- has_connectivity ? net::NetworkChangeNotifier::CONNECTION_UNKNOWN
- : net::NetworkChangeNotifier::CONNECTION_NONE);
- base::RunLoop().RunUntilIdle();
- }
-
- // Runs the task runner until no tasks remain, and asserts that by this time,
- // OnCanSendNetworkRequest() will have been called exactly once, with a delay
- // between |min_delay_ms| and |max_delay_ms|, modulo 0.5 ms to allow for
- // floating point errors. When OnCanSendNetworkRequest() is called, the mock
- // will return |emulated_return_value|. This value normally indicates whether
- // or not a request was actually issued in response to the call.
- void AssertReleaseInBetween(bool emulated_return_value,
- double min_delay_ms,
- double max_delay_ms) {
- ASSERT_EQ(0u, mock_delegate_.can_send_count());
- base::TimeTicks ticks_at_start = task_runner_->NowTicks();
- mock_delegate_.set_emulated_return_value(emulated_return_value);
- task_runner_->FastForwardUntilNoTasksRemain();
- ASSERT_EQ(1u, mock_delegate_.can_send_count());
- base::TimeDelta delay =
- mock_delegate_.last_can_send_time() - ticks_at_start;
- EXPECT_LE(min_delay_ms - 1, delay.InMillisecondsF());
- EXPECT_GE(max_delay_ms + 1, delay.InMillisecondsF());
- mock_delegate_.reset_can_send_count();
- }
-
- // Runs the task runner for |secs| and asserts that OnCanSendNetworkRequest()
- // will not have been called by the end of this period.
- void AssertNoReleaseForSecs(int64_t secs) {
- task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(secs));
- ASSERT_EQ(0u, mock_delegate_.can_send_count());
- }
-
- // Runs the task runner until no tasks remain, and asserts that
- // OnCanSendNetworkRequest() will not have been called.
- void AssertNoReleaseUntilNoTasksRemain() {
- task_runner_->FastForwardUntilNoTasksRemain();
- ASSERT_EQ(0u, mock_delegate_.can_send_count());
- }
-
- size_t GetPendingTaskCount() const {
- return task_runner_->GetPendingTaskCount();
- }
-
- private:
- // Needed because NetworkChangeNotifier uses base::ObserverList, which
- // notifies
- // observers on the MessageLoop that belongs to the thread from which they
- // have registered.
- base::MessageLoop message_loop_;
- std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
- scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
- std::unique_ptr<base::TickClock> mock_tick_clock_;
- MockAffiliationFetchThrottlerDelegate mock_delegate_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottlerTest);
-};
-
-TEST_F(AffiliationFetchThrottlerTest, SuccessfulRequests) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-
- // Signal while request is in flight should be ignored.
- throttler->SignalNetworkRequestNeeded();
- AssertNoReleaseUntilNoTasksRemain();
- throttler->InformOfNetworkRequestComplete(true);
- AssertNoReleaseUntilNoTasksRemain();
-
- // Duplicate the second signal 3 times: still only 1 callback should arrive.
- throttler->SignalNetworkRequestNeeded();
- throttler->SignalNetworkRequestNeeded();
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-}
-
-TEST_F(AffiliationFetchThrottlerTest, FailedRequests) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
- throttler->InformOfNetworkRequestComplete(false);
-
- // The request after the first failure should be delayed by |initial_delay_ms|
- // spread out over Uniform(1 - |jitter_factor|, 1).
- throttler->SignalNetworkRequestNeeded();
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kPolicy.initial_delay_ms * (1 - kPolicy.jitter_factor),
- kPolicy.initial_delay_ms));
- throttler->InformOfNetworkRequestComplete(true);
-
- // After a successful request, the next one should be released immediately.
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
- throttler->InformOfNetworkRequestComplete(false);
-
- // In general, the request after the n-th failure should be delayed by
- // |multiply_factor| ^ (n-1) * |initial_delay_ms|,
- // spread out over Uniform(1 - |jitter_factor|, 1), up until
- // |maximum_backoff_ms|
- // is reached.
- for (int num_failures = 1; num_failures < 100; ++num_failures) {
- throttler->SignalNetworkRequestNeeded();
- double max_delay_ms = kPolicy.initial_delay_ms *
- pow(kPolicy.multiply_factor, num_failures - 1);
- double min_delay_ms = max_delay_ms * (1 - kPolicy.jitter_factor);
- if (max_delay_ms > kPolicy.maximum_backoff_ms)
- max_delay_ms = kPolicy.maximum_backoff_ms;
- if (min_delay_ms > kPolicy.maximum_backoff_ms)
- min_delay_ms = kPolicy.maximum_backoff_ms;
- ASSERT_NO_FATAL_FAILURE(
- AssertReleaseInBetween(true, min_delay_ms, max_delay_ms));
- throttler->InformOfNetworkRequestComplete(false);
- }
-}
-
-TEST_F(AffiliationFetchThrottlerTest, OnCanSendNetworkRequestReturnsFalse) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- // A need for a network request is signaled, but as OnCanSendNetworkRequest()
- // is called, the implementation returns false to indicate that the request
- // will not be needed after all. InformOfNetworkRequestComplete() must not be
- // called in this case.
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(false, 0, 0));
-
- // A subsequent signaling, however, should result in OnCanSendNetworkRequest()
- // being called immediately.
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-}
-
-TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
- SimulateHasNetworkConnectivity(false);
-
- // After connectivity is restored, the first request should be delayed by the
- // grace period, spread out over Uniform(1 - |jitter_factor|, 1).
- throttler->SignalNetworkRequestNeeded();
- AssertNoReleaseUntilNoTasksRemain();
-
- SimulateHasNetworkConnectivity(true);
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
- throttler->InformOfNetworkRequestComplete(true);
-
- // The next request should not be delayed.
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-}
-
-// Same as GracePeriodAfterConnectivityIsRestored, but the network comes back
-// just before SignalNetworkRequestNeeded() is called.
-TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored2) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
- SimulateHasNetworkConnectivity(false);
-
- SimulateHasNetworkConnectivity(true);
- throttler->SignalNetworkRequestNeeded();
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
- throttler->InformOfNetworkRequestComplete(true);
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-}
-
-TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringBackoff) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
- throttler->InformOfNetworkRequestComplete(false);
-
- throttler->SignalNetworkRequestNeeded();
- SimulateHasNetworkConnectivity(false);
-
- // Let the exponential backoff delay expire, and verify nothing happens.
- AssertNoReleaseUntilNoTasksRemain();
-
- // Verify that the request is, however, sent after the normal grace period
- // once connectivity is restored.
- SimulateHasNetworkConnectivity(true);
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
- throttler->InformOfNetworkRequestComplete(true);
-}
-
-TEST_F(AffiliationFetchThrottlerTest,
- ConnectivityLostAndRestoredDuringBackoff) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
- throttler->InformOfNetworkRequestComplete(false);
-
- throttler->SignalNetworkRequestNeeded();
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kPolicy.initial_delay_ms * (1 - kPolicy.jitter_factor),
- kPolicy.initial_delay_ms));
- throttler->InformOfNetworkRequestComplete(false);
-
- SimulateHasNetworkConnectivity(false);
- SimulateHasNetworkConnectivity(true);
-
- // This test expects that the exponential backoff interval after the 2nd error
- // is larger than the normal grace period after connectivity is restored.
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- EXPECT_PRED_FORMAT2(testing::DoubleLE, kGraceMs,
- kPolicy.initial_delay_ms * kPolicy.multiply_factor);
-
- // The release should come after the longest of the two intervals expire.
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kPolicy.initial_delay_ms * kPolicy.multiply_factor *
- (1 - kPolicy.jitter_factor),
- kPolicy.initial_delay_ms * kPolicy.multiply_factor));
- throttler->InformOfNetworkRequestComplete(false);
-}
-
-TEST_F(AffiliationFetchThrottlerTest, FlakyConnectivity) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
- throttler->InformOfNetworkRequestComplete(false);
-
- // Run for a total of 5 grace periods and simulate connectivity being lost and
- // restored every second. This verifies that a flaky connection will not flood
- // the task queue with lots of of tasks and also that release will not happen
- // while the connection is flaky even once the first grace period has expired.
- throttler->SignalNetworkRequestNeeded();
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- int64_t five_grace_periods_secs =
- kGraceMs * 5 / base::Time::kMillisecondsPerSecond;
- for (int64_t t = 0; t < five_grace_periods_secs; ++t) {
- SimulateHasNetworkConnectivity(false);
- AssertNoReleaseForSecs(1);
- SimulateHasNetworkConnectivity(true);
- EXPECT_EQ(1u, GetPendingTaskCount());
- }
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
-}
-
-TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringRequest) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-
- SimulateHasNetworkConnectivity(false);
- AssertNoReleaseUntilNoTasksRemain();
- throttler->InformOfNetworkRequestComplete(false);
- AssertNoReleaseUntilNoTasksRemain();
- throttler->SignalNetworkRequestNeeded();
- AssertNoReleaseUntilNoTasksRemain();
-
- SimulateHasNetworkConnectivity(true);
-
- // Verify that the next request is released after the normal grace period.
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
- throttler->InformOfNetworkRequestComplete(true);
-}
-
-TEST_F(AffiliationFetchThrottlerTest,
- ConnectivityLostAndRestoredDuringRequest) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-
- SimulateHasNetworkConnectivity(false);
- AssertNoReleaseUntilNoTasksRemain();
- SimulateHasNetworkConnectivity(true);
- AssertNoReleaseUntilNoTasksRemain();
- throttler->InformOfNetworkRequestComplete(true);
-
- // Even though the previous request succeeded, the next request should still
- // be held back for the normal grace period after connection is restored.
- throttler->SignalNetworkRequestNeeded();
- const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
- true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
- throttler->InformOfNetworkRequestComplete(true);
-}
-
-TEST_F(AffiliationFetchThrottlerTest,
- ConnectivityLostAndRestoredDuringRequest2) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-
- SimulateHasNetworkConnectivity(false);
- AssertNoReleaseUntilNoTasksRemain();
- SimulateHasNetworkConnectivity(true);
-
- const int64_t& kGraceMs =
- AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
- AssertNoReleaseForSecs(kGraceMs / base::Time::kMillisecondsPerSecond);
- throttler->InformOfNetworkRequestComplete(true);
-
- // The next request should not be held back.
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
-}
-
-TEST_F(AffiliationFetchThrottlerTest, InstanceDestroyedWhileInBackoff) {
- std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
-
- throttler->SignalNetworkRequestNeeded();
- ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
- throttler->InformOfNetworkRequestComplete(false);
-
- throttler->SignalNetworkRequestNeeded();
- throttler.reset();
- EXPECT_EQ(1u, GetPendingTaskCount());
- AssertNoReleaseUntilNoTasksRemain();
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetcher.cc b/chromium/components/password_manager/core/browser/affiliation_fetcher.cc
deleted file mode 100644
index fb486e7bb93..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetcher.cc
+++ /dev/null
@@ -1,251 +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/password_manager/core/browser/affiliation_fetcher.h"
-
-#include <stddef.h>
-#include <utility>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/sparse_histogram.h"
-#include "components/password_manager/core/browser/affiliation_api.pb.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-#include "components/password_manager/core/browser/test_affiliation_fetcher_factory.h"
-#include "google_apis/google_api_keys.h"
-#include "net/base/load_flags.h"
-#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 "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.
-enum AffiliationFetchResult {
- AFFILIATION_FETCH_RESULT_SUCCESS,
- AFFILIATION_FETCH_RESULT_FAILURE,
- AFFILIATION_FETCH_RESULT_MALFORMED,
- 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) {
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
- fetcher->GetResponseCode());
- // Network error codes are negative. See: src/net/base/net_error_list.h.
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "PasswordManager.AffiliationFetcher.FetchErrorCode",
- -fetcher->GetStatus().error());
- }
-}
-
-} // namespace
-
-static TestAffiliationFetcherFactory* g_testing_factory = nullptr;
-
-AffiliationFetcher::AffiliationFetcher(
- net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_uris,
- AffiliationFetcherDelegate* delegate)
- : request_context_getter_(request_context_getter),
- requested_facet_uris_(facet_uris),
- delegate_(delegate) {
- for (const FacetURI& uri : requested_facet_uris_) {
- DCHECK(uri.is_valid());
- }
-}
-
-AffiliationFetcher::~AffiliationFetcher() {
-}
-
-// static
-AffiliationFetcher* AffiliationFetcher::Create(
- net::URLRequestContextGetter* context_getter,
- const std::vector<FacetURI>& facet_uris,
- AffiliationFetcherDelegate* delegate) {
- if (g_testing_factory) {
- return g_testing_factory->CreateInstance(context_getter, facet_uris,
- delegate);
- }
- return new AffiliationFetcher(context_getter, facet_uris, delegate);
-}
-
-// static
-void AffiliationFetcher::SetFactoryForTesting(
- TestAffiliationFetcherFactory* factory) {
- g_testing_factory = factory;
-}
-
-void AffiliationFetcher::StartRequest() {
- DCHECK(!fetcher_);
-
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("affiliation_lookup", R"(
- semantics {
- sender: "Android Credentials Affiliation Fetcher"
- description:
- "Users syncing their passwords may have credentials stored for "
- "Android apps. Unless synced data is encrypted with a custom "
- "passphrase, this service downloads the associations between "
- "Android apps and the corresponding websites. Thus, the Android "
- "credentials can be used while browsing the web. "
- trigger: "Periodically in the background."
- data:
- "List of Android apps the user has credentials for. The passwords "
- "and usernames aren't sent."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: false
- setting:
- "Users can enable or disable this feature either by stoping "
- "syncing passwords to Google (via unchecking 'Passwords' in "
- "Chromium's settings under 'Sign In', 'Advanced sync settings') or "
- "by introducing a custom passphrase to disable this service. The "
- "feature is enabled by default."
- chrome_policy {
- SyncDisabled {
- policy_options {mode: MANDATORY}
- SyncDisabled: true
- }
- }
- })");
- 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();
-}
-
-GURL AffiliationFetcher::BuildQueryURL() const {
- return net::AppendQueryParameter(
- GURL("https://www.googleapis.com/affiliation/v1/affiliation:lookup"),
- "key", google_apis::GetAPIKey());
-}
-
-std::string AffiliationFetcher::PreparePayload() const {
- affiliation_pb::LookupAffiliationRequest lookup_request;
- for (const FacetURI& uri : requested_facet_uris_)
- lookup_request.add_facet(uri.canonical_spec());
-
- std::string serialized_request;
- bool success = lookup_request.SerializeToString(&serialized_request);
- DCHECK(success);
- return serialized_request;
-}
-
-bool AffiliationFetcher::ParseResponse(
- 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
- // validation and sanitization steps to make sure that the contract of
- // AffiliationFetcherDelegate is fulfilled. Possible discrepancies are:
- // * The server response will not have anything for facets that are not
- // affiliated with any other facet, while |result| must have them.
- // * The server response might contain future, unknown kinds of facet URIs,
- // while |result| must contain only those that are FacetURI::is_valid().
- // * The server response being ill-formed or self-inconsistent (in the sense
- // that there are overlapping equivalence classes) is indicative of server
- // side issues likely not remedied by re-fetching. Report failure in this
- // 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;
-
- result->reserve(requested_facet_uris_.size());
-
- std::map<FacetURI, size_t> facet_uri_to_class_index;
- for (int i = 0; i < response.affiliation_size(); ++i) {
- const affiliation_pb::Affiliation& equivalence_class(
- response.affiliation(i));
-
- AffiliatedFacets affiliated_uris;
- for (int j = 0; j < equivalence_class.facet_size(); ++j) {
- const std::string& uri_spec(equivalence_class.facet(j).id());
- FacetURI uri = FacetURI::FromPotentiallyInvalidSpec(uri_spec);
- // Ignore potential future kinds of facet URIs (e.g. for new platforms).
- if (!uri.is_valid())
- continue;
- affiliated_uris.push_back(uri);
- }
-
- // Be lenient and ignore empty (after filtering) equivalence classes.
- if (affiliated_uris.empty())
- continue;
-
- // Ignore equivalence classes that are duplicates of earlier ones. However,
- // fail in the case of a partial overlap, which violates the invariant that
- // affiliations must form an equivalence relation.
- for (const FacetURI& uri : affiliated_uris) {
- if (!facet_uri_to_class_index.count(uri))
- facet_uri_to_class_index[uri] = result->size();
- if (facet_uri_to_class_index[uri] !=
- facet_uri_to_class_index[affiliated_uris[0]]) {
- return false;
- }
- }
-
- // Filter out duplicate equivalence classes in the response.
- if (facet_uri_to_class_index[affiliated_uris[0]] == result->size())
- result->push_back(affiliated_uris);
- }
-
- // Synthesize an equivalence class (of size one) for each facet that did not
- // appear in the server response due to not being affiliated with any others.
- for (const FacetURI& uri : requested_facet_uris_) {
- if (!facet_uri_to_class_index.count(uri))
- result->push_back(AffiliatedFacets(1, uri));
- }
-
- return true;
-}
-
-void AffiliationFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK_EQ(source, fetcher_.get());
-
- // 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);
- delegate_->OnFetchSucceeded(std::move(result_data));
- } else {
- ReportStatistics(AFFILIATION_FETCH_RESULT_MALFORMED, nullptr);
- delegate_->OnMalformedResponse();
- }
- } else {
- ReportStatistics(AFFILIATION_FETCH_RESULT_FAILURE, fetcher_.get());
- delegate_->OnFetchFailed();
- }
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetcher.h b/chromium/components/password_manager/core/browser/affiliation_fetcher.h
deleted file mode 100644
index b606ce06453..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetcher.h
+++ /dev/null
@@ -1,99 +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_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCHER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCHER_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/password_manager/core/browser/affiliation_fetcher_delegate.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-#include "net/url_request/url_fetcher_delegate.h"
-
-class GURL;
-
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
-
-namespace password_manager {
-
-class TestAffiliationFetcherFactory;
-
-// Fetches authoritative information regarding which facets are affiliated with
-// each other, that is, which facets belong to the same logical application.
-// See affiliation_utils.h for a definition of what this means.
-//
-// 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 {
- public:
- ~AffiliationFetcher() override;
-
- // Constructs a fetcher to retrieve affiliations for each facet in |facet_ids|
- // using the specified |request_context_getter|, and will provide the results
- // to the |delegate| on the same thread that creates the instance.
- static AffiliationFetcher* Create(
- net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_uris,
- AffiliationFetcherDelegate* delegate);
-
- // Sets the |factory| to be used by Create() to construct AffiliationFetcher
- // instances. To be used only for testing.
- //
- // The caller must ensure that the |factory| outlives all potential Create()
- // calls. The caller may pass in NULL to resume using the default factory.
- static void SetFactoryForTesting(TestAffiliationFetcherFactory* factory);
-
- // Actually starts the request, and will call the delegate with the results on
- // the same thread when done. If |this| is destroyed before completion, the
- // in-flight request is cancelled, and the delegate will not be called.
- // Further details:
- // * 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();
-
- const std::vector<FacetURI>& requested_facet_uris() const {
- return requested_facet_uris_;
- }
-
- AffiliationFetcherDelegate* delegate() const { return delegate_; }
-
- protected:
- AffiliationFetcher(net::URLRequestContextGetter* request_context_getter,
- 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;
-
- // Parses and validates the response protocol buffer message for a list of
- // equivalence classes, stores them into |result| and returns true on success.
- // 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;
-
- // net::URLFetcherDelegate:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
-
- const scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
- const std::vector<FacetURI> requested_facet_uris_;
- AffiliationFetcherDelegate* const delegate_;
-
- std::unique_ptr<net::URLFetcher> fetcher_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationFetcher);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCHER_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetcher_delegate.h b/chromium/components/password_manager/core/browser/affiliation_fetcher_delegate.h
deleted file mode 100644
index b67e63656e3..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetcher_delegate.h
+++ /dev/null
@@ -1,46 +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_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCHER_DELEGATE_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCHER_DELEGATE_H_
-
-#include <memory>
-#include <vector>
-
-#include "components/password_manager/core/browser/affiliation_utils.h"
-
-namespace password_manager {
-
-// Interface that users of AffiliationFetcher should implement to get results of
-// the fetch. It is safe to destroy the fetcher in any of the event handlers.
-class AffiliationFetcherDelegate {
- public:
- // Encapsulates the response to an affiliations request.
- typedef std::vector<AffiliatedFacets> Result;
-
- // Called when affiliation information has been successfully retrieved. The
- // |result| will contain at most as many equivalence class as facet URIs in
- // the request, and each requested facet URI will appear in exactly one
- // equivalence class.
- virtual void OnFetchSucceeded(std::unique_ptr<Result> result) = 0;
-
- // Called when affiliation information could not be fetched due to a network
- // error or a presumably transient server error. The implementor may and will
- // probably want to retry the request (once network connectivity is
- // re-established, and/or with exponential back-off).
- virtual void OnFetchFailed() = 0;
-
- // Called when an affiliation response was received, but it was either gravely
- // ill-formed or self-inconsistent. It is likely that a repeated fetch would
- // yield the same, erroneous response, therefore, to avoid overloading the
- // server, the fetch must not be repeated in the short run.
- virtual void OnMalformedResponse() = 0;
-
- protected:
- virtual ~AffiliationFetcherDelegate() {}
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_FETCHER_DELEGATE_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_fetcher_unittest.cc b/chromium/components/password_manager/core/browser/affiliation_fetcher_unittest.cc
deleted file mode 100644
index 1ee3c940657..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_fetcher_unittest.cc
+++ /dev/null
@@ -1,337 +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/password_manager/core/browser/affiliation_fetcher.h"
-
-#include <utility>
-
-#include "base/macros.h"
-#include "base/test/null_task_runner.h"
-#include "components/password_manager/core/browser/affiliation_api.pb.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 {
-
-const char kExampleAndroidFacetURI[] = "android://hash@com.example";
-const char kExampleWebFacet1URI[] = "https://www.example.com";
-const char kExampleWebFacet2URI[] = "https://www.example.org";
-
-class MockAffiliationFetcherDelegate
- : public testing::StrictMock<AffiliationFetcherDelegate> {
- public:
- MockAffiliationFetcherDelegate() {}
- ~MockAffiliationFetcherDelegate() {}
-
- MOCK_METHOD0(OnFetchSucceededProxy, void());
- MOCK_METHOD0(OnFetchFailed, void());
- MOCK_METHOD0(OnMalformedResponse, void());
-
- void OnFetchSucceeded(std::unique_ptr<Result> result) override {
- OnFetchSucceededProxy();
- result_ = std::move(result);
- }
-
- const Result& result() const { return *result_.get(); }
-
- private:
- std::unique_ptr<Result> result_;
-
- DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetcherDelegate);
-};
-
-} // namespace
-
-class AffiliationFetcherTest : public testing::Test {
- public:
- AffiliationFetcherTest()
- : request_context_getter_(new net::TestURLRequestContextGetter(
- make_scoped_refptr(new base::NullTaskRunner))) {}
-
- ~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()));
-
- 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());
- EXPECT_THAT(actual_uris,
- testing::UnorderedElementsAreArray(expected_facet_uris));
- }
-
- void ServiceURLRequest(const std::string& response) {
- net::TestURLFetcher* url_fetcher =
- test_url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_NE(nullptr, url_fetcher);
-
- url_fetcher->set_response_code(200);
- url_fetcher->SetResponseString(response);
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
- }
-
- 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 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);
- }
-
- net::TestURLRequestContextGetter* request_context_getter() {
- return request_context_getter_.get();
- }
-
- private:
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
- net::TestURLFetcherFactory test_url_fetcher_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationFetcherTest);
-};
-
-TEST_F(AffiliationFetcherTest, BasicReqestAndResponse) {
- const char kNotExampleAndroidFacetURI[] =
- "android://hash1234@com.example.not";
- const char kNotExampleWebFacetURI[] = "https://not.example.com";
-
- affiliation_pb::LookupAffiliationResponse test_response;
- affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
- eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
- eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
- eq_class1->add_facet()->set_id(kExampleAndroidFacetURI);
- affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
- eq_class2->add_facet()->set_id(kNotExampleWebFacetURI);
- eq_class2->add_facet()->set_id(kNotExampleAndroidFacetURI);
-
- std::vector<FacetURI> requested_uris;
- requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
- requested_uris.push_back(
- FacetURI::FromCanonicalSpec(kNotExampleAndroidFacetURI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
- fetcher->StartRequest();
-
- 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());
- EXPECT_THAT(mock_delegate.result()[0],
- testing::UnorderedElementsAre(
- FacetURI::FromCanonicalSpec(kExampleWebFacet1URI),
- FacetURI::FromCanonicalSpec(kExampleWebFacet2URI),
- FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)));
- EXPECT_THAT(mock_delegate.result()[1],
- testing::UnorderedElementsAre(
- FacetURI::FromCanonicalSpec(kNotExampleWebFacetURI),
- FacetURI::FromCanonicalSpec(kNotExampleAndroidFacetURI)));
-}
-
-// The API contract of this class is to return an equivalence class for all
-// requested facets; however, the server will not return anything for facets
-// that are not affiliated with any other facet. Make sure an equivalence class
-// of size one is created for each of the missing facets.
-TEST_F(AffiliationFetcherTest, MissingEquivalenceClassesAreCreated) {
- affiliation_pb::LookupAffiliationResponse empty_test_response;
-
- std::vector<FacetURI> requested_uris;
- requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
- fetcher->StartRequest();
-
- 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());
- EXPECT_THAT(mock_delegate.result()[0],
- testing::UnorderedElementsAre(
- FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)));
-}
-
-TEST_F(AffiliationFetcherTest, DuplicateEquivalenceClassesAreIgnored) {
- affiliation_pb::LookupAffiliationResponse test_response;
- affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
- eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
- eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
- eq_class1->add_facet()->set_id(kExampleAndroidFacetURI);
- affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
- eq_class2->add_facet()->set_id(kExampleWebFacet2URI);
- eq_class2->add_facet()->set_id(kExampleAndroidFacetURI);
- eq_class2->add_facet()->set_id(kExampleWebFacet1URI);
-
- std::vector<FacetURI> requested_uris;
- requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
- fetcher->StartRequest();
-
- 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());
- EXPECT_THAT(mock_delegate.result()[0],
- testing::UnorderedElementsAre(
- FacetURI::FromCanonicalSpec(kExampleWebFacet1URI),
- FacetURI::FromCanonicalSpec(kExampleWebFacet2URI),
- FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)));
-}
-
-TEST_F(AffiliationFetcherTest, EmptyEquivalenceClassesAreIgnored) {
- affiliation_pb::LookupAffiliationResponse test_response;
- affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
- eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
- // Empty class.
- test_response.add_affiliation();
-
- std::vector<FacetURI> requested_uris;
- requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
- fetcher->StartRequest();
-
- 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());
- EXPECT_THAT(mock_delegate.result()[0],
- testing::UnorderedElementsAre(
- FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)));
-}
-
-TEST_F(AffiliationFetcherTest, UnrecognizedFacetURIsAreIgnored) {
- affiliation_pb::LookupAffiliationResponse test_response;
- // Equivalence class having, alongside known facet URIs, a facet URI that
- // corresponds to new platform unknown to this version.
- affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
- eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
- eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
- eq_class1->add_facet()->set_id(kExampleAndroidFacetURI);
- eq_class1->add_facet()->set_id("new-platform://app-id-on-new-platform");
- // Equivalence class consisting solely of an unknown facet URI.
- affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
- eq_class2->add_facet()->set_id("new-platform2://app2-id-on-new-platform2");
-
- std::vector<FacetURI> requested_uris;
- requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
- fetcher->StartRequest();
-
- 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());
- EXPECT_THAT(mock_delegate.result()[0],
- testing::UnorderedElementsAre(
- FacetURI::FromCanonicalSpec(kExampleWebFacet1URI),
- FacetURI::FromCanonicalSpec(kExampleWebFacet2URI),
- FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)));
-}
-
-TEST_F(AffiliationFetcherTest, FailureBecauseResponseIsNotAProtobuf) {
- const char kMalformedResponse[] = "This is not a protocol buffer!";
-
- std::vector<FacetURI> uris;
- uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
- fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnMalformedResponse());
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(kMalformedResponse));
-}
-
-// Partially overlapping equivalence classes violate the invariant that
-// affiliations must form an equivalence relation. Such a response is malformed.
-TEST_F(AffiliationFetcherTest,
- FailureBecausePartiallyOverlappingEquivalenceClasses) {
- affiliation_pb::LookupAffiliationResponse test_response;
- affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
- eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
- eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
- affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
- eq_class2->add_facet()->set_id(kExampleWebFacet1URI);
- eq_class2->add_facet()->set_id(kExampleAndroidFacetURI);
-
- std::vector<FacetURI> uris;
- uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
- fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnMalformedResponse());
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
-}
-
-TEST_F(AffiliationFetcherTest, FailOnServerError) {
- std::vector<FacetURI> uris;
- uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
- fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnFetchFailed());
- ASSERT_NO_FATAL_FAILURE(SimulateServerError());
-}
-
-TEST_F(AffiliationFetcherTest, FailOnNetworkError) {
- std::vector<FacetURI> uris;
- uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
-
- MockAffiliationFetcherDelegate mock_delegate;
- std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
- fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnFetchFailed());
- ASSERT_NO_FATAL_FAILURE(SimulateNetworkError());
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_service.cc b/chromium/components/password_manager/core/browser/affiliation_service.cc
deleted file mode 100644
index 0f986740693..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_service.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/password_manager/core/browser/affiliation_service.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/files/file_path.h"
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/default_clock.h"
-#include "base/time/default_tick_clock.h"
-#include "components/password_manager/core/browser/affiliation_backend.h"
-#include "net/url_request/url_request_context_getter.h"
-
-namespace password_manager {
-
-AffiliationService::AffiliationService(
- scoped_refptr<base::SingleThreadTaskRunner> backend_task_runner)
- : backend_(nullptr),
- backend_task_runner_(backend_task_runner),
- weak_ptr_factory_(this) {
-}
-
-AffiliationService::~AffiliationService() {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (backend_) {
- backend_task_runner_->DeleteSoon(FROM_HERE, backend_);
- backend_ = nullptr;
- }
-}
-
-void AffiliationService::Initialize(
- net::URLRequestContextGetter* request_context_getter,
- const base::FilePath& db_path) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!backend_);
- backend_ =
- new AffiliationBackend(request_context_getter, backend_task_runner_,
- base::WrapUnique(new base::DefaultClock),
- base::WrapUnique(new base::DefaultTickClock));
-
- std::unique_ptr<base::TickClock> tick_clock(new base::DefaultTickClock);
- backend_task_runner_->PostTask(
- FROM_HERE, base::Bind(&AffiliationBackend::Initialize,
- base::Unretained(backend_), db_path));
-}
-
-void AffiliationService::GetAffiliations(
- const FacetURI& facet_uri,
- StrategyOnCacheMiss cache_miss_strategy,
- const ResultCallback& result_callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(backend_);
- backend_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&AffiliationBackend::GetAffiliations,
- base::Unretained(backend_), facet_uri, cache_miss_strategy,
- result_callback, base::ThreadTaskRunnerHandle::Get()));
-}
-
-void AffiliationService::Prefetch(const FacetURI& facet_uri,
- const base::Time& keep_fresh_until) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(backend_);
- backend_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&AffiliationBackend::Prefetch, base::Unretained(backend_),
- facet_uri, keep_fresh_until));
-}
-
-void AffiliationService::CancelPrefetch(const FacetURI& facet_uri,
- const base::Time& keep_fresh_until) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(backend_);
- backend_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&AffiliationBackend::CancelPrefetch,
- base::Unretained(backend_), facet_uri, keep_fresh_until));
-}
-
-void AffiliationService::TrimCache() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(backend_);
- backend_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&AffiliationBackend::TrimCache, base::Unretained(backend_)));
-}
-
-void AffiliationService::TrimCacheForFacet(const FacetURI& facet_uri) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(backend_);
- backend_task_runner_->PostTask(
- FROM_HERE, base::Bind(&AffiliationBackend::TrimCacheForFacet,
- base::Unretained(backend_), facet_uri));
-}
-
-// static
-void AffiliationService::DeleteCache(
- const base::FilePath& db_path,
- base::SingleThreadTaskRunner* backend_task_runner) {
- backend_task_runner->PostTask(
- FROM_HERE, base::Bind(&AffiliationBackend::DeleteCache, db_path));
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_service.h b/chromium/components/password_manager/core/browser/affiliation_service.h
deleted file mode 100644
index db90a022a3f..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_service.h
+++ /dev/null
@@ -1,172 +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_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_SERVICE_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_SERVICE_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-
-namespace base {
-class FilePath;
-class SingleThreadTaskRunner;
-} // namespace base
-
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
-
-namespace password_manager {
-
-class AffiliationBackend;
-
-// A service that can be used to query the list of facets that are affiliated
-// with a given facet, i.e., facets that belong to the same logical application.
-// See affiliation_utils.h for details of what this means.
-//
-// The service must be accessed from the UI thread, and it can be utilized in
-// two ways:
-//
-// 1.) On-demand fetching: For the one-off query that wishes to learn
-// affiliations of facet X when (potentially) issuing an on-demand
-// network request to the Affiliation API containing the URI of facet X
-// is acceptable from the privacy and/or performance perspective.
-//
-// This mode of operation is achieved by invoking GetAffiliations() with
-// StrategyOnCacheMiss::FETCH_OVER_NETWORK.
-//
-// 2.) Proactive fetching: For the compound query that is concerned with
-// checking, over time, whether or not each element in a sequence of
-// facets, W_1, W_2, ..., W_n, is affiliated with a fixed facet Y; and
-// when it is desired, for privacy and/or performance reasons, that only
-// facet Y be looked up against the Affiliation API and that subsequent
-// requests regarding each W_i not trigger additional requests.
-//
-// This mode of operation can be useful when, for example, the password
-// manager has credentials stored for facet Y and wishes to check, for
-// each visited web site W_i, whether these credentials should be offered
-// to be autofilled onto W_i.
-//
-// Example code:
-//
-// class ExampleAffiliatedCredentialFiller
-// : public base::SupportsWeakPtr<...> {
-// public:
-// ExampleAffiliatedCredentialFiller(AffiliationService* service,
-// const FacetURI& y)
-// : service_(service), y_(y) {
-// cancel_handle_ = service_->Prefetch(y_, base::Time::Max());
-// }
-//
-// ~ExampleAffiliatedCredentialFiller() { cancel_handle_.Run(); }
-//
-// void ShouldFillInto(const FacetURI& wi, FillDelegate* delegate) {
-// service_->GetAffiliations(wi, StrategyOnCacheMiss::FAIL,
-// base::Bind(
-// &ExampleAffiliatedCredentialFiller::OnAffiliationResult,
-// AsWeakPtr(),
-// delegate));
-// }
-//
-// void OnAffiliationResult(FillDelegate* delegate,
-// const AffiliatedFacets& results,
-// bool success) {
-// if (success && std::count(results.begin(), results.end(), y_))
-// delegate->FillCredentialsFor(y_);
-// }
-//
-// private:
-// AffiliationService* service_;
-// const FacetURI& y_;
-// CancelPrefetchingHandle cancel_handle_;
-// };
-class AffiliationService : public KeyedService {
- public:
- typedef base::Callback<void(const AffiliatedFacets& /* results */,
- bool /* success */)> ResultCallback;
-
- // Controls whether to send a network request or fail on a cache miss.
- enum class StrategyOnCacheMiss { FETCH_OVER_NETWORK, FAIL };
-
- // The |backend_task_runner| should be a task runner corresponding to a thread
- // that can take blocking I/O, and is normally Chrome's DB thread.
- AffiliationService(
- scoped_refptr<base::SingleThreadTaskRunner> backend_task_runner);
- ~AffiliationService() override;
-
- // 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);
-
- // Looks up facets affiliated with the facet identified by |facet_uri|, and
- // invokes |result_callback| with the results.
- //
- // If the local cache contains fresh affiliation information for |facet_uri|,
- // the request will be served from cache. Otherwise, |cache_miss_policy|
- // controls whether to issue an on-demand network request, or to fail the
- // request without fetching.
- virtual void GetAffiliations(const FacetURI& facet_uri,
- StrategyOnCacheMiss cache_miss_strategy,
- const ResultCallback& result_callback);
-
- // Prefetches affiliation information for the facet identified by |facet_uri|,
- // and keeps the information fresh by periodic re-fetches (as needed) until
- // the clock strikes |keep_fresh_until| (exclusive), until a matching call to
- // CancelPrefetch(), or until Chrome is shut down, whichever is sooner. It is
- // a supported use-case to pass base::Time::Max() as |keep_fresh_until|.
- //
- // Canceling can be useful when a password is deleted, so that resources are
- // no longer wasted on repeatedly refreshing affiliation information. Note
- // that canceling will not blow away data already stored in the cache unless
- // it becomes stale.
- virtual void Prefetch(const FacetURI& facet_uri,
- const base::Time& keep_fresh_until);
-
- // Cancels the corresponding prefetch command, i.e., the one issued for the
- // same |facet_uri| and with the same |keep_fresh_until|.
- virtual void CancelPrefetch(const FacetURI& facet_uri,
- const base::Time& keep_fresh_until);
-
- // Wipes results of on-demand fetches and expired prefetches from the cache,
- // but retains information corresponding to facets that are being kept fresh.
- // As no required data is deleted, there will be no network requests directly
- // triggered by this call.
- //
- // The second version will only potentially remove data corresponding to the
- // given |facet_uri|, but still only as long as the data is no longer needed.
- virtual void TrimCache();
- virtual void TrimCacheForFacet(const FacetURI& facet_uri);
-
- // Posts a task to the |backend_task_runner| to delete the cache database file
- // at |db_path|, and all auxiliary files. The database must be closed before
- // calling this.
- static void DeleteCache(const base::FilePath& db_path,
- base::SingleThreadTaskRunner* backend_task_runner);
-
- private:
- // The backend, owned by this AffiliationService instance, but living on the
- // DB thread. It will be deleted asynchronously during shutdown on the DB
- // thread, so it will outlive |this| along with all its in-flight tasks.
- AffiliationBackend* backend_;
-
- // TaskRunner to be used to run the |backend_| (usually the DB thread).
- scoped_refptr<base::SingleThreadTaskRunner> backend_task_runner_;
-
- base::ThreadChecker thread_checker_;
- base::WeakPtrFactory<AffiliationService> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationService);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_SERVICE_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_service_unittest.cc b/chromium/components/password_manager/core/browser/affiliation_service_unittest.cc
deleted file mode 100644
index 6e1e38b399d..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_service_unittest.cc
+++ /dev/null
@@ -1,159 +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.
-
-// Note: This test focuses on functionality implemented in AffiliationService
-// itself. More thorough The AffiliationBackend is tested in-depth separarately.
-
-#include "components/password_manager/core/browser/affiliation_service.h"
-
-#include <memory>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.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"
-#include "components/password_manager/core/browser/fake_affiliation_api.h"
-#include "components/password_manager/core/browser/mock_affiliation_consumer.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-
-namespace {
-
-using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
-
-const char kTestFacetURIAlpha1[] = "https://one.alpha.example.com";
-const char kTestFacetURIAlpha2[] = "https://two.alpha.example.com";
-const char kTestFacetURIAlpha3[] = "https://three.alpha.example.com";
-const char kTestFacetURIBeta1[] = "https://one.beta.example.com";
-
-AffiliatedFacets GetTestEquivalenceClassAlpha() {
- AffiliatedFacets affiliated_facets;
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha3));
- return affiliated_facets;
-}
-
-} // namespace
-
-class AffiliationServiceTest : public testing::Test {
- public:
- AffiliationServiceTest()
- : main_task_runner_(new base::TestSimpleTaskRunner),
- main_task_runner_handle_(main_task_runner_),
- background_task_runner_(new base::TestMockTimeTaskRunner) {}
- ~AffiliationServiceTest() override {}
-
- protected:
- void DestroyService() { service_.reset(); }
-
- AffiliationService* service() { return service_.get(); }
- MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
-
- base::TestSimpleTaskRunner* main_task_runner() {
- return main_task_runner_.get();
- }
-
- base::TestMockTimeTaskRunner* background_task_runner() {
- return background_task_runner_.get();
- }
-
- ScopedFakeAffiliationAPI* fake_affiliation_api() {
- return &fake_affiliation_api_;
- }
-
- private:
- // testing::Test:
- void SetUp() override {
- base::FilePath database_path;
- ASSERT_TRUE(CreateTemporaryFile(&database_path));
- service_.reset(new AffiliationService(background_task_runner()));
- service_->Initialize(nullptr, 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.
- fake_affiliation_api_.AddTestEquivalenceClass(
- GetTestEquivalenceClassAlpha());
- }
-
- void TearDown() override {
- // The service uses DeleteSoon to asynchronously destroy its backend. Pump
- // the background thread to make sure destruction actually takes place.
- DestroyService();
- background_task_runner_->RunUntilIdle();
- }
-
- scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
- base::ThreadTaskRunnerHandle main_task_runner_handle_;
- scoped_refptr<base::TestMockTimeTaskRunner> background_task_runner_;
-
- ScopedFakeAffiliationAPI fake_affiliation_api_;
- MockAffiliationConsumer mock_consumer_;
-
- std::unique_ptr<AffiliationService> service_;
-
- DISALLOW_COPY_AND_ASSIGN(AffiliationServiceTest);
-};
-
-TEST_F(AffiliationServiceTest, GetAffiliations) {
- // The first request allows on-demand fetching, and should trigger a fetch.
- // Then, it should succeed after the fetch is complete.
- service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK,
- mock_consumer()->GetResultCallback());
-
- background_task_runner()->RunUntilIdle();
- ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
- fake_affiliation_api()->ServeNextRequest();
-
- mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- main_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-
- // The second request should be (and can be) served from cache.
- service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- StrategyOnCacheMiss::FAIL,
- mock_consumer()->GetResultCallback());
-
- background_task_runner()->RunUntilIdle();
- ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
-
- mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
- main_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-
- // The third request is also restricted to the cache, but cannot be served
- // from cache, thus it should fail.
- service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1),
- StrategyOnCacheMiss::FAIL,
- mock_consumer()->GetResultCallback());
-
- background_task_runner()->RunUntilIdle();
- ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
-
- mock_consumer()->ExpectFailure();
- main_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-}
-
-TEST_F(AffiliationServiceTest, ShutdownWhileTasksArePosted) {
- service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
- StrategyOnCacheMiss::FETCH_OVER_NETWORK,
- mock_consumer()->GetResultCallback());
- EXPECT_TRUE(background_task_runner()->HasPendingTask());
-
- DestroyService();
- background_task_runner()->RunUntilIdle();
-
- mock_consumer()->ExpectFailure();
- main_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_utils.cc b/chromium/components/password_manager/core/browser/affiliation_utils.cc
deleted file mode 100644
index 19b9ba1c01c..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_utils.cc
+++ /dev/null
@@ -1,314 +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/password_manager/core/browser/affiliation_utils.h"
-
-#include <algorithm>
-#include <ostream>
-
-#include "base/base64.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/url_formatter/elide_url.h"
-#include "components/variations/variations_associated_data.h"
-#include "net/base/escape.h"
-#include "url/third_party/mozilla/url_parse.h"
-#include "url/url_canon_stdstring.h"
-
-namespace password_manager {
-
-namespace {
-
-// The scheme used for identifying Android applications.
-const char kAndroidAppScheme[] = "android";
-
-// Returns a StringPiece corresponding to |component| in |uri|, or the empty
-// string in case there is no such component.
-base::StringPiece ComponentString(const std::string& uri,
- const url::Component& component) {
- if (!component.is_valid())
- return base::StringPiece();
- return base::StringPiece(uri.c_str() + component.begin, component.len);
-}
-
-// Returns true if the passed ASCII |input| string contains nothing else than
-// alphanumeric characters and those in |other_characters|.
-bool ContainsOnlyAlphanumericAnd(const base::StringPiece& input,
- const base::StringPiece& other_characters) {
- for (char c : input) {
- if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) &&
- other_characters.find(c) == base::StringPiece::npos)
- return false;
- }
- return true;
-}
-
-// Canonicalizes a Web facet URI, and returns true if canonicalization was
-// successful and produced a valid URI.
-bool CanonicalizeWebFacetURI(const std::string& input_uri,
- const url::Parsed& input_parsed,
- std::string* canonical_uri) {
- url::Parsed canonical_parsed;
- url::StdStringCanonOutput canonical_output(canonical_uri);
-
- bool canonicalization_succeeded = url::CanonicalizeStandardURL(
- input_uri.c_str(), input_uri.size(), input_parsed, nullptr,
- &canonical_output, &canonical_parsed);
- canonical_output.Complete();
-
- if (canonicalization_succeeded && canonical_parsed.host.is_nonempty() &&
- !canonical_parsed.username.is_valid() &&
- !canonical_parsed.password.is_valid() &&
- ComponentString(*canonical_uri, canonical_parsed.path) == "/" &&
- !canonical_parsed.query.is_valid() && !canonical_parsed.ref.is_valid()) {
- // Get rid of the trailing slash added by url::CanonicalizeStandardURL().
- DCHECK_EQ((size_t)canonical_parsed.path.begin, canonical_uri->size() - 1);
- canonical_uri->erase(canonical_parsed.path.begin,
- canonical_parsed.path.len);
- return true;
- }
- return false;
-}
-
-// Adds padding until the length of the base64-encoded |data| becomes a multiple
-// of 4, and returns true if the thusly obtained |data| is now correctly padded,
-// i.e., there are at most 2 padding characters ('=') at the very end.
-bool CanonicalizeBase64Padding(std::string* data) {
- while (data->size() % 4u != 0u)
- data->push_back('=');
-
- size_t first_padding = data->find_first_of('=');
- return first_padding == std::string::npos ||
- (data->size() - first_padding <= 2u &&
- data->find_first_not_of('=', first_padding) == std::string::npos);
-}
-
-// Canonicalizes the username component in an Android facet URI (containing the
-// certificate hash), and returns true if canonicalization was successful and
-// produced a valid non-empty component.
-bool CanonicalizeHashComponent(const base::StringPiece& input_hash,
- url::CanonOutput* canonical_output) {
- // Characters other than alphanumeric that are used in the "URL and filename
- // safe" base64 alphabet; plus the padding ('=').
- const char kBase64NonAlphanumericChars[] = "-_=";
-
- // We need net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS to
- // unescape the padding ('=').
- std::string base64_encoded_hash = net::UnescapeURLComponent(
- input_hash.as_string(),
- net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
-
- if (!base64_encoded_hash.empty() &&
- CanonicalizeBase64Padding(&base64_encoded_hash) &&
- ContainsOnlyAlphanumericAnd(base64_encoded_hash,
- kBase64NonAlphanumericChars)) {
- canonical_output->Append(base64_encoded_hash.data(),
- base64_encoded_hash.size());
- canonical_output->push_back('@');
- return true;
- }
- return false;
-}
-
-// Canonicalizes the host component in an Android facet URI (containing the
-// package name), and returns true if canonicalization was successful and
-// produced a valid non-empty component.
-bool CanonicalizePackageNameComponent(
- const base::StringPiece& input_package_name,
- url::CanonOutput* canonical_output) {
- // Characters other than alphanumeric that are permitted in the package names.
- const char kPackageNameNonAlphanumericChars[] = "._";
-
- std::string package_name = net::UnescapeURLComponent(
- input_package_name.as_string(), net::UnescapeRule::NORMAL);
-
- // TODO(engedy): We might want to use a regex to check this more throughly.
- if (!package_name.empty() &&
- ContainsOnlyAlphanumericAnd(package_name,
- kPackageNameNonAlphanumericChars)) {
- canonical_output->Append(package_name.data(), package_name.size());
- return true;
- }
- return false;
-}
-
-// Canonicalizes an Android facet URI, and returns true if canonicalization was
-// successful and produced a valid URI.
-bool CanonicalizeAndroidFacetURI(const std::string& input_uri,
- const url::Parsed& input_parsed,
- std::string* canonical_uri) {
- url::StdStringCanonOutput canonical_output(canonical_uri);
-
- url::Component unused;
- bool success = url::CanonicalizeScheme(
- input_uri.c_str(), input_parsed.scheme, &canonical_output, &unused);
-
- canonical_output.push_back('/');
- canonical_output.push_back('/');
-
- // We cannot use url::CanonicalizeUserInfo as that would percent encode the
- // base64 padding characters ('=').
- success &= CanonicalizeHashComponent(
- ComponentString(input_uri, input_parsed.username), &canonical_output);
-
- // We cannot use url::CanonicalizeHost as that would convert the package name
- // to lower case, but the package name is case sensitive.
- success &= CanonicalizePackageNameComponent(
- ComponentString(input_uri.data(), input_parsed.host), &canonical_output);
-
- canonical_output.Complete();
-
- return success && !input_parsed.password.is_nonempty() &&
- (!input_parsed.path.is_nonempty() ||
- ComponentString(input_uri, input_parsed.path) == "/") &&
- !input_parsed.port.is_nonempty() && !input_parsed.query.is_valid() &&
- !input_parsed.ref.is_valid();
-}
-
-// Computes the canonicalized form of |uri| into |canonical_uri|, and returns
-// true if canonicalization was successful and produced a valid URI.
-bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
- std::string* canonical_uri) {
- DCHECK(canonical_uri);
- canonical_uri->clear();
- canonical_uri->reserve(input_uri.size() + 32);
-
- url::Parsed input_parsed;
- url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
-
- base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
- if (base::LowerCaseEqualsASCII(scheme, url::kHttpsScheme)) {
- return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
- } else if (base::LowerCaseEqualsASCII(scheme, kAndroidAppScheme)) {
- return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
- }
- return false;
-}
-
-} // namespace
-
-
-// FacetURI -------------------------------------------------------------------
-
-FacetURI::FacetURI() : is_valid_(false) {
-}
-
-// static
-FacetURI FacetURI::FromPotentiallyInvalidSpec(const std::string& spec) {
- std::string canonical_spec;
- bool is_valid = ParseAndCanonicalizeFacetURI(spec, &canonical_spec);
- return FacetURI(canonical_spec, is_valid);
-}
-
-// static
-FacetURI FacetURI::FromCanonicalSpec(const std::string& canonical_spec) {
- return FacetURI(canonical_spec, true);
-}
-
-bool FacetURI::operator==(const FacetURI& other) const {
- DCHECK(is_empty() || is_valid());
- DCHECK(other.is_empty() || other.is_valid());
- return canonical_spec_ == other.canonical_spec_;
-}
-
-bool FacetURI::operator!=(const FacetURI& other) const {
- DCHECK(is_empty() || is_valid());
- DCHECK(other.is_empty() || other.is_valid());
- return canonical_spec_ != other.canonical_spec_;
-}
-
-bool FacetURI::operator<(const FacetURI& other) const {
- DCHECK(is_empty() || is_valid());
- DCHECK(other.is_empty() || other.is_valid());
- return canonical_spec_ < other.canonical_spec_;
-}
-
-bool FacetURI::operator>(const FacetURI& other) const {
- DCHECK(is_empty() || is_valid());
- DCHECK(other.is_empty() || other.is_valid());
- return canonical_spec_ > other.canonical_spec_;
-}
-
-bool FacetURI::IsValidWebFacetURI() const {
- return scheme() == url::kHttpsScheme;
-}
-
-bool FacetURI::IsValidAndroidFacetURI() const {
- return scheme() == kAndroidAppScheme;
-}
-
-std::string FacetURI::scheme() const {
- return is_valid()
- ? ComponentString(canonical_spec_, parsed_.scheme).as_string()
- : "";
-}
-
-std::string FacetURI::android_package_name() const {
- if (!IsValidAndroidFacetURI())
- return "";
- return ComponentString(canonical_spec_, parsed_.host).as_string();
-}
-
-FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
- : is_valid_(is_valid), canonical_spec_(canonical_spec) {
- // TODO(engedy): Refactor code in order to avoid to avoid parsing the URL
- // twice.
- url::ParseStandardURL(canonical_spec_.c_str(), canonical_spec_.size(),
- &parsed_);
-}
-
-
-// AffiliatedFacetsWithUpdateTime ---------------------------------------------
-
-AffiliatedFacetsWithUpdateTime::AffiliatedFacetsWithUpdateTime() {
-}
-
-AffiliatedFacetsWithUpdateTime::AffiliatedFacetsWithUpdateTime(
- const AffiliatedFacetsWithUpdateTime& other) = default;
-
-AffiliatedFacetsWithUpdateTime::~AffiliatedFacetsWithUpdateTime() {
-}
-
-
-// Helpers --------------------------------------------------------------------
-
-std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri) {
- return os << facet_uri.potentially_invalid_spec();
-}
-
-bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
- const AffiliatedFacets& b) {
- if (a.size() != b.size())
- return false;
-
- std::vector<FacetURI> a_sorted(a.begin(), a.end());
- std::vector<FacetURI> b_sorted(b.begin(), b.end());
- std::sort(a_sorted.begin(), a_sorted.end());
- std::sort(b_sorted.begin(), b_sorted.end());
- return std::equal(a_sorted.begin(), a_sorted.end(), b_sorted.begin());
-}
-
-bool IsValidAndroidFacetURI(const std::string& url) {
- FacetURI facet = FacetURI::FromPotentiallyInvalidSpec(url);
- return facet.IsValidAndroidFacetURI();
-}
-
-std::string GetHumanReadableOrigin(
- const autofill::PasswordForm& password_form) {
- FacetURI facet_uri =
- FacetURI::FromPotentiallyInvalidSpec(password_form.signon_realm);
- if (facet_uri.IsValidAndroidFacetURI())
- return GetHumanReadableOriginForAndroidUri(facet_uri);
-
- return base::UTF16ToUTF8(
- url_formatter::FormatUrlForSecurityDisplay(password_form.origin));
-}
-
-std::string GetHumanReadableOriginForAndroidUri(const FacetURI facet_uri) {
- DCHECK(facet_uri.IsValidAndroidFacetURI());
- return facet_uri.scheme() + "://" + facet_uri.android_package_name();
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_utils.h b/chromium/components/password_manager/core/browser/affiliation_utils.h
deleted file mode 100644
index 4d463c9837d..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_utils.h
+++ /dev/null
@@ -1,197 +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.
-
-// This file contains utilities related to working with "facets".
-//
-// A "facet" is defined as the manifestation of a logical application on a given
-// platform. For example, "My Bank" may have released an Android application
-// and a Web application accessible from a browser. These are all facets of the
-// "My Bank" logical application.
-//
-// Facets that belong to the same logical application are said to be affiliated
-// with each other. Conceptually, "affiliations" can be seen as an equivalence
-// relation defined over the set of all facets. Each equivalence class contains
-// facets that belong to the same logical application, and therefore should be
-// treated as synonymous for certain purposes, e.g., sharing credentials.
-//
-// A valid facet identifier will be a URI of the form:
-//
-// * https://<host>[:<port>]
-//
-// For web sites. Only HTTPS sites are supported. The syntax corresponds to
-// that of 'serialized-origin' in RFC 6454. That is, in canonical form, the
-// URI must not contain components other than the scheme (required, must be
-// "https"), host (required), and port (optional); with canonicalization
-// performed the same way as it normally would be for standard URLs.
-//
-// * android://<certificate_hash>@<package_name>
-//
-// For Android applications. In canonical form, the URI must not contain
-// components other than the scheme (must be "android"), username, and host
-// (all required). The host part must be a valid Android package name, with
-// no escaping, so it must be composed of characters [a-zA-Z0-9_.].
-//
-// The username part must be the hash of the certificate used to sign the
-// APK, base64-encoded using padding and the "URL and filename safe" base64
-// alphabet, with no further escaping. This is normally calculated as:
-//
-// echo -n -e "$PEM_KEY" |
-// openssl x509 -outform DER |
-// openssl sha -sha512 -binary | base64 | tr '+/' '-_'
-//
-
-#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_
-
-#include <iosfwd>
-#include <string>
-#include <vector>
-
-#include <stddef.h>
-
-#include "base/containers/hash_tables.h"
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "url/third_party/mozilla/url_parse.h"
-
-namespace autofill {
-struct PasswordForm;
-} // namespace autofill
-
-namespace password_manager {
-
-// Encapsulates a facet URI in canonical form.
-//
-// This is a very light-weight wrapper around an std::string containing the text
-// of the URI, and can be passed around as a value. The main rationale for the
-// existance of this class is to make it clearer in the code when a certain URI
-// is known to be a valid facet URI in canonical form, and to allow verifying
-// and converting URIs to such canonical form.
-//
-// Note that it would be impractical to use GURL to represent facet URIs, as
-// GURL has built-in logic to parse the rest of the URI according to its scheme,
-// and obviously, it does not recognize the "android" scheme. Therefore, after
-// parsing, everything ends up in the path component, which is not too helpful.
-class FacetURI {
- public:
- FacetURI();
-
- // As a light-weight std::string wrapper, allow copy and assign.
- FacetURI(const FacetURI&) = default;
- FacetURI& operator=(const FacetURI&) = default;
-
- // Constructs an instance to encapsulate the canonical form of |spec|.
- // If |spec| is not a valid facet URI, then an invalid instance is returned,
- // which then should be discarded.
- static FacetURI FromPotentiallyInvalidSpec(const std::string& spec);
-
- // Constructs a valid FacetURI instance from a valid |canonical_spec|.
- // Note: The passed-in URI is not verified at all. Use only when you are sure
- // the URI is valid and in canonical form.
- static FacetURI FromCanonicalSpec(const std::string& canonical_spec);
-
- // Comparison operators so that FacetURI can be used in std::equal.
- bool operator==(const FacetURI& other) const;
- bool operator!=(const FacetURI& other) const;
-
- // Relational operators so that FacetURI can be used in sorted containers.
- bool operator<(const FacetURI& other) const;
- bool operator>(const FacetURI& other) const;
-
- // Returns whether or not this instance represents a valid facet identifier
- // referring to a Web application.
- bool IsValidWebFacetURI() const;
-
- // Returns whether or not this instance represents a valid facet identifier
- // referring to an Android application.
- bool IsValidAndroidFacetURI() const;
-
- // Returns whether or not this instance represents a valid facet identifier
- // referring to either a Web or an Android application. The empty identfier is
- // not considered valid.
- bool is_valid() const { return is_valid_; }
-
- // Returns whether or not this instance represents the empty facet identifier.
- bool is_empty() const { return canonical_spec_.empty(); }
-
- // Returns the canonical scheme of the encapsulated facet URI, provided it is
- // valid, or the empty string otherwise.
- std::string scheme() const;
-
- // Returns the canonical package name that the encapsulated facet URI
- // references, provided it is a valid Android facet URI, or the empty string
- // otherwise.
- std::string android_package_name() const;
-
- // Returns the text of the encapsulated canonical URI, which must be valid.
- const std::string& canonical_spec() const {
- DCHECK(is_valid_);
- return canonical_spec_;
- }
-
- // Returns the text of the encapsulated canonical URI, even if it is invalid.
- const std::string& potentially_invalid_spec() const {
- return canonical_spec_;
- }
-
- private:
- // Internal constructor to be used by the static factory methods.
- FacetURI(const std::string& canonical_spec, bool is_valid);
-
- // Whether |canonical_spec_| contains a valid facet URI in canonical form.
- bool is_valid_;
-
- // The text of the encapsulated canonical URI, valid if and only if
- // |is_valid_| is true.
- std::string canonical_spec_;
-
- // Identified components of the canonical spec.
- url::Parsed parsed_;
-};
-
-// A collection of facets affiliated with each other, i.e. an equivalence class.
-typedef std::vector<FacetURI> AffiliatedFacets;
-
-// A collection of facets affiliated with each other, i.e. an equivalence class,
-// plus a timestamp that indicates the last time the data was updated from an
-// authoritative source.
-struct AffiliatedFacetsWithUpdateTime {
- AffiliatedFacetsWithUpdateTime();
- AffiliatedFacetsWithUpdateTime(const AffiliatedFacetsWithUpdateTime& other);
- ~AffiliatedFacetsWithUpdateTime();
-
- AffiliatedFacets facets;
- base::Time last_update_time;
-};
-
-// Returns whether or not equivalence classes |a| and |b| are equal, that is,
-// whether or not they consist of the same set of facets.
-//
-// Note that this will do some sorting, so it can be expensive for large inputs.
-bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
- const AffiliatedFacets& b);
-
-// A shorter way to spell FacetURI::IsValidAndroidFacetURI().
-bool IsValidAndroidFacetURI(const std::string& uri);
-
-// Returns the origin URI in a format which can be presented to a user based of
-// |password_from| field values.
-std::string GetHumanReadableOrigin(const autofill::PasswordForm& password_form);
-
-// Returns the Android origin URI for presenting to a user.
-std::string GetHumanReadableOriginForAndroidUri(const FacetURI facet_uri);
-
-// For logging use only.
-std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri);
-
-struct FacetURIHash {
- size_t operator()(const FacetURI& facet_uri) const {
- return std::hash<std::string>()(facet_uri.potentially_invalid_spec());
- }
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_
diff --git a/chromium/components/password_manager/core/browser/affiliation_utils_unittest.cc b/chromium/components/password_manager/core/browser/affiliation_utils_unittest.cc
deleted file mode 100644
index c55232fa6d0..00000000000
--- a/chromium/components/password_manager/core/browser/affiliation_utils_unittest.cc
+++ /dev/null
@@ -1,247 +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/password_manager/core/browser/affiliation_utils.h"
-
-#include "base/command_line.h"
-#include "base/metrics/field_trial.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/variations/variations_associated_data.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/url_constants.h"
-
-namespace password_manager {
-
-namespace {
-const std::string kSchemeHostExample = "http://example.com";
-const char kTestFacetURI1[] = "https://alpha.example.com/";
-const char kTestFacetURI2[] = "https://beta.example.com/";
-const char kTestFacetURI3[] = "https://gamma.example.com/";
-} // namespace
-
-TEST(AffiliationUtilsTest, ValidWebFacetURIs) {
- struct {
- const char* valid_facet_uri;
- const char* expected_canonical_facet_uri;
- } kTestCases[] = {
- {"https://www.example.com", "https://www.example.com"},
- {"HTTPS://www.EXAMPLE.com", "https://www.example.com"},
- {"https://0321.0x86.161.0043", "https://209.134.161.35"},
- {"https://www.%65xample.com", "https://www.example.com"},
- {"https://sz\xc3\xb3t\xc3\xa1r.example.com",
- "https://xn--sztr-7na0i.example.com"},
- {"https://www.example.com/", "https://www.example.com"},
- {"https://www.example.com:", "https://www.example.com"},
- {"https://@www.example.com", "https://www.example.com"},
- {"https://:@www.example.com", "https://www.example.com"},
- {"https://www.example.com:8080", "https://www.example.com:8080"},
- {"https://new-gtld", "https://new-gtld"}};
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(testing::Message("URI = ") << test_case.valid_facet_uri);
- FacetURI facet_uri =
- FacetURI::FromPotentiallyInvalidSpec(test_case.valid_facet_uri);
- ASSERT_TRUE(facet_uri.IsValidWebFacetURI());
- EXPECT_EQ(std::string(test_case.expected_canonical_facet_uri),
- facet_uri.canonical_spec());
- EXPECT_EQ(url::kHttpsScheme, facet_uri.scheme());
- EXPECT_EQ("", facet_uri.android_package_name());
- }
-}
-
-TEST(AffiliationUtilsTest, InvalidWebFacetURIs) {
- const char* kInvalidFacetURIs[]{
- // Invalid URL (actually, will be treated as having only a host part).
- "Does not look like a valid URL.",
- // Path is more than just the root path ('/').
- "https://www.example.com/path",
- // Empty scheme and not HTTPS scheme.
- "://www.example.com",
- "http://www.example.com/",
- // Forbidden non-empty components.
- "https://user@www.example.com/",
- "https://:password@www.example.com/",
- "https://www.example.com/?",
- "https://www.example.com/?query",
- "https://www.example.com/#",
- "https://www.example.com/#ref",
- // Valid Android facet URI.
- "android://hash@com.example.android"};
- for (const char* uri : kInvalidFacetURIs) {
- SCOPED_TRACE(testing::Message("URI = ") << uri);
- FacetURI facet_uri = FacetURI::FromPotentiallyInvalidSpec(uri);
- EXPECT_FALSE(facet_uri.IsValidWebFacetURI());
- }
-}
-
-TEST(AffiliationUtilsTest, ValidAndroidFacetURIs) {
- struct {
- const char* valid_facet_uri;
- const char* expected_canonical_facet_uri;
- const char* expected_package_name;
- } kTestCases[] = {
- {"android://"
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
- "@com.example.android",
- "android://"
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
- "@com.example.android",
- "com.example.android"},
- {"ANDROID://"
- "hash@abcdefghijklmnopqrstuvwxyz_0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ",
- "android://"
- "hash@abcdefghijklmnopqrstuvwxyz_0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ",
- "abcdefghijklmnopqrstuvwxyz_0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ"},
- {"android://needpadding@com.example.android",
- "android://needpadding=@com.example.android",
- "com.example.android"},
- {"android://needtounescape%3D%3D@com.%65xample.android",
- "android://needtounescape==@com.example.android",
- "com.example.android"},
- {"ANDROID://hash@com.example.android",
- "android://hash@com.example.android",
- "com.example.android"},
- {"android://hash@com.example.android/",
- "android://hash@com.example.android",
- "com.example.android"},
- {"android://hash:@com.example.android",
- "android://hash@com.example.android",
- "com.example.android"}};
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(testing::Message("URI = ") << test_case.valid_facet_uri);
- FacetURI facet_uri =
- FacetURI::FromPotentiallyInvalidSpec(test_case.valid_facet_uri);
- ASSERT_TRUE(facet_uri.IsValidAndroidFacetURI());
- EXPECT_EQ(test_case.expected_canonical_facet_uri,
- facet_uri.canonical_spec());
- EXPECT_EQ("android", facet_uri.scheme());
- EXPECT_EQ(test_case.expected_package_name,
- facet_uri.android_package_name());
- }
-}
-
-TEST(AffiliationUtilsTest, InvalidAndroidFacetURIs) {
- const char* kInvalidFacetURIs[]{
- // Invalid URL (actually, will be treated as having only a host part).
- "Does not look like a valid URL.",
- // Path is more than just the root path ('/').
- "android://hash@com.example.android/path",
- // Empty scheme or not "android" scheme.
- "://hash@com.example.android",
- "http://hash@com.example.android",
- // Package name with illegal characters.
- "android://hash@com.$example.android",
- "android://hash@com-example-android",
- "android://hash@com%2Dexample.android", // Escaped '-'.
- "android://hash@com.example.sz\xc3\xb3t\xc3\xa1r", // UTF-8 o' and a'.
- // Empty, non-existent and malformed hash part.
- "android://@com.example.android",
- "android://com.example.android",
- "android://badpadding=a@com.example.android",
- "android://toolongpaddin===@com.example.android",
- "android://invalid+characters@com.example.android",
- "android://invalid%2Fcharacters@com.example.android", // Escaped '/'.
- // Forbidden non-empty components.
- "android://hash:password@com.example.android",
- "android://hash@com.example.android:port",
- "android://hash@com.example.android/?",
- "android://hash@com.example.android/?query",
- "android://hash@com.example.android/#",
- "android://hash@com.example.android/#ref",
- // Valid Web facet URI.
- "https://www.example.com/"};
- for (const char* uri : kInvalidFacetURIs) {
- SCOPED_TRACE(testing::Message("URI = ") << uri);
- FacetURI facet_uri = FacetURI::FromPotentiallyInvalidSpec(uri);
- EXPECT_FALSE(facet_uri.IsValidAndroidFacetURI());
- EXPECT_EQ("", facet_uri.android_package_name());
- }
-}
-
-TEST(AffiliationUtilsTest, EqualEquivalenceClasses) {
- AffiliatedFacets a;
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI1));
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI2));
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
-
- AffiliatedFacets b;
- b.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
- b.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI1));
- b.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI2));
-
- EXPECT_TRUE(AreEquivalenceClassesEqual(a, a));
- EXPECT_TRUE(AreEquivalenceClassesEqual(b, b));
- EXPECT_TRUE(AreEquivalenceClassesEqual(b, a));
- EXPECT_TRUE(AreEquivalenceClassesEqual(a, b));
-}
-
-TEST(AffiliationUtilsTest, NotEqualEquivalenceClasses) {
- AffiliatedFacets a;
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI1));
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI2));
-
- AffiliatedFacets b;
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI2));
- b.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
-
- AffiliatedFacets c;
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI1));
- a.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI2));
- b.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
-
- EXPECT_FALSE(AreEquivalenceClassesEqual(a, b));
- EXPECT_FALSE(AreEquivalenceClassesEqual(a, c));
- EXPECT_FALSE(AreEquivalenceClassesEqual(b, a));
- EXPECT_FALSE(AreEquivalenceClassesEqual(b, c));
- EXPECT_FALSE(AreEquivalenceClassesEqual(c, a));
- EXPECT_FALSE(AreEquivalenceClassesEqual(c, b));
-}
-
-class GetHumanReadableOriginTest : public testing::Test {
- public:
- GetHumanReadableOriginTest() {
- form_template_.origin = GURL(kSchemeHostExample);
- form_template_.action = GURL(kSchemeHostExample);
- form_template_.username_element = base::ASCIIToUTF16("Email");
- form_template_.password_element = base::ASCIIToUTF16("Password");
- form_template_.submit_element = base::ASCIIToUTF16("signIn");
- form_template_.signon_realm = kSchemeHostExample;
- }
- const autofill::PasswordForm& form_remplate() { return form_template_; }
-
- private:
- autofill::PasswordForm form_template_;
-};
-
-TEST_F(GetHumanReadableOriginTest, OriginFromHtmlForm) {
- autofill::PasswordForm html_form(form_remplate());
- html_form.origin = GURL(kSchemeHostExample + "/LoginAuth");
- html_form.action = GURL(kSchemeHostExample + "/Login");
- html_form.scheme = autofill::PasswordForm::SCHEME_HTML;
- EXPECT_EQ(GetHumanReadableOrigin(html_form), "http://example.com");
-}
-
-TEST_F(GetHumanReadableOriginTest, OriginFromDigestForm) {
- autofill::PasswordForm non_html_form(form_remplate());
- non_html_form.scheme = autofill::PasswordForm::SCHEME_DIGEST;
- non_html_form.action = GURL();
- non_html_form.signon_realm = kSchemeHostExample + "42";
- EXPECT_EQ(GetHumanReadableOrigin(non_html_form), "http://example.com");
-}
-
-TEST_F(GetHumanReadableOriginTest, OriginFromAndroidForm) {
- autofill::PasswordForm android_form(form_remplate());
- android_form.action = GURL();
- android_form.origin = GURL();
- android_form.scheme = autofill::PasswordForm::SCHEME_OTHER;
- android_form.signon_realm =
- "android://"
- "m3HSJL1i83hdltRq0-o9czGb-8KJDKra4t_"
- "3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw=="
- "@com.example.android";
- EXPECT_EQ(GetHumanReadableOrigin(android_form),
- "android://com.example.android");
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc
new file mode 100644
index 00000000000..42a236bc61e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.cc
@@ -0,0 +1,242 @@
+// 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/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+
+namespace password_manager {
+
+namespace {
+
+// Returns whether or not |form| represents a credential for an Android
+// application, and if so, returns the |facet_uri| of that application.
+bool IsAndroidApplicationCredential(const autofill::PasswordForm& form,
+ FacetURI* facet_uri) {
+ DCHECK(facet_uri);
+ if (form.scheme != autofill::PasswordForm::SCHEME_HTML)
+ return false;
+
+ *facet_uri = FacetURI::FromPotentiallyInvalidSpec(form.signon_realm);
+ return facet_uri->IsValidAndroidFacetURI();
+}
+
+} // namespace
+
+// static
+constexpr base::TimeDelta AffiliatedMatchHelper::kInitializationDelayOnStartup;
+
+AffiliatedMatchHelper::AffiliatedMatchHelper(
+ PasswordStore* password_store,
+ std::unique_ptr<AffiliationService> affiliation_service)
+ : password_store_(password_store),
+ affiliation_service_(std::move(affiliation_service)),
+ weak_ptr_factory_(this) {}
+
+AffiliatedMatchHelper::~AffiliatedMatchHelper() {
+ if (password_store_)
+ password_store_->RemoveObserver(this);
+}
+
+void AffiliatedMatchHelper::Initialize() {
+ DCHECK(password_store_);
+ DCHECK(affiliation_service_);
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AffiliatedMatchHelper::DoDeferredInitialization,
+ weak_ptr_factory_.GetWeakPtr()),
+ kInitializationDelayOnStartup);
+}
+
+void AffiliatedMatchHelper::GetAffiliatedAndroidRealms(
+ const PasswordStore::FormDigest& observed_form,
+ const AffiliatedRealmsCallback& result_callback) {
+ if (IsValidWebCredential(observed_form)) {
+ FacetURI facet_uri(
+ FacetURI::FromPotentiallyInvalidSpec(observed_form.signon_realm));
+ affiliation_service_->GetAffiliationsAndBranding(
+ facet_uri, AffiliationService::StrategyOnCacheMiss::FAIL,
+ base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms,
+ weak_ptr_factory_.GetWeakPtr(), facet_uri, result_callback));
+ } else {
+ result_callback.Run(std::vector<std::string>());
+ }
+}
+
+void AffiliatedMatchHelper::GetAffiliatedWebRealms(
+ const PasswordStore::FormDigest& android_form,
+ const AffiliatedRealmsCallback& result_callback) {
+ if (IsValidAndroidCredential(android_form)) {
+ affiliation_service_->GetAffiliationsAndBranding(
+ FacetURI::FromPotentiallyInvalidSpec(android_form.signon_realm),
+ AffiliationService::StrategyOnCacheMiss::FETCH_OVER_NETWORK,
+ base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms,
+ weak_ptr_factory_.GetWeakPtr(), result_callback));
+ } else {
+ result_callback.Run(std::vector<std::string>());
+ }
+}
+
+void AffiliatedMatchHelper::InjectAffiliationAndBrandingInformation(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
+ const PasswordFormsCallback& result_callback) {
+ std::vector<autofill::PasswordForm*> android_credentials;
+ for (const auto& form : forms) {
+ if (IsValidAndroidCredential(PasswordStore::FormDigest(*form)))
+ android_credentials.push_back(form.get());
+ }
+ base::Closure on_get_all_realms(
+ base::Bind(result_callback, base::Passed(&forms)));
+ base::Closure barrier_closure =
+ base::BarrierClosure(android_credentials.size(), on_get_all_realms);
+ for (auto* form : android_credentials) {
+ affiliation_service_->GetAffiliationsAndBranding(
+ FacetURI::FromPotentiallyInvalidSpec(form->signon_realm),
+ AffiliationService::StrategyOnCacheMiss::FAIL,
+ base::Bind(&AffiliatedMatchHelper::
+ CompleteInjectAffiliationAndBrandingInformation,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(form),
+ barrier_closure));
+ }
+}
+
+void AffiliatedMatchHelper::CompleteInjectAffiliationAndBrandingInformation(
+ autofill::PasswordForm* form,
+ base::Closure barrier_closure,
+ const AffiliatedFacets& results,
+ bool success) {
+ if (!success) {
+ barrier_closure.Run();
+ return;
+ }
+
+ const FacetURI facet_uri(
+ FacetURI::FromPotentiallyInvalidSpec(form->signon_realm));
+ DCHECK(facet_uri.IsValidAndroidFacetURI());
+
+ // Inject branding information into the form (e.g. the Play Store name and
+ // icon URL). We expect to always find a matching facet URI in the results.
+ auto facet = std::find_if(results.begin(), results.end(),
+ [&facet_uri](const Facet& affiliated_facet) {
+ return affiliated_facet.uri == facet_uri;
+ });
+ DCHECK(facet != results.end());
+ form->app_display_name = facet->branding_info.name;
+ form->app_icon_url = facet->branding_info.icon_url;
+
+ // Inject the affiliated web realm into the form, if available. In case
+ // multiple web realms are available, this will always choose the first
+ // available web realm for injection.
+ auto affiliated_facet = std::find_if(
+ results.begin(), results.end(), [](const Facet& affiliated_facet) {
+ return affiliated_facet.uri.IsValidWebFacetURI();
+ });
+ if (affiliated_facet != results.end())
+ form->affiliated_web_realm = affiliated_facet->uri.canonical_spec() + "/";
+
+ barrier_closure.Run();
+}
+
+// static
+bool AffiliatedMatchHelper::IsValidAndroidCredential(
+ const PasswordStore::FormDigest& form) {
+ return form.scheme == autofill::PasswordForm::SCHEME_HTML &&
+ IsValidAndroidFacetURI(form.signon_realm);
+}
+
+// static
+bool AffiliatedMatchHelper::IsValidWebCredential(
+ const PasswordStore::FormDigest& form) {
+ FacetURI facet_uri(FacetURI::FromPotentiallyInvalidSpec(form.signon_realm));
+ return form.scheme == autofill::PasswordForm::SCHEME_HTML &&
+ facet_uri.IsValidWebFacetURI();
+}
+
+void AffiliatedMatchHelper::DoDeferredInitialization() {
+ // Must start observing for changes at the same time as when the snapshot is
+ // taken to avoid inconsistencies due to any changes taking place in-between.
+ password_store_->AddObserver(this);
+ password_store_->GetAutofillableLogins(this);
+ password_store_->GetBlacklistLogins(this);
+}
+
+void AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms(
+ const FacetURI& original_facet_uri,
+ const AffiliatedRealmsCallback& result_callback,
+ const AffiliatedFacets& results,
+ bool success) {
+ std::vector<std::string> affiliated_realms;
+ if (success) {
+ for (const Facet& affiliated_facet : results) {
+ if (affiliated_facet.uri != original_facet_uri &&
+ affiliated_facet.uri.IsValidAndroidFacetURI())
+ // Facet URIs have no trailing slash, whereas realms do.
+ affiliated_realms.push_back(affiliated_facet.uri.canonical_spec() +
+ "/");
+ }
+ }
+ result_callback.Run(affiliated_realms);
+}
+
+void AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms(
+ const AffiliatedRealmsCallback& result_callback,
+ const AffiliatedFacets& results,
+ bool success) {
+ std::vector<std::string> affiliated_realms;
+ if (success) {
+ for (const Facet& affiliated_facet : results) {
+ if (affiliated_facet.uri.IsValidWebFacetURI())
+ // Facet URIs have no trailing slash, whereas realms do.
+ affiliated_realms.push_back(affiliated_facet.uri.canonical_spec() +
+ "/");
+ }
+ }
+ result_callback.Run(affiliated_realms);
+}
+
+void AffiliatedMatchHelper::OnLoginsChanged(
+ const PasswordStoreChangeList& changes) {
+ std::vector<FacetURI> facet_uris_to_trim;
+ for (const PasswordStoreChange& change : changes) {
+ FacetURI facet_uri;
+ if (!IsAndroidApplicationCredential(change.form(), &facet_uri))
+ continue;
+
+ if (change.type() == PasswordStoreChange::ADD) {
+ affiliation_service_->Prefetch(facet_uri, base::Time::Max());
+ } else if (change.type() == PasswordStoreChange::REMOVE) {
+ // Stop keeping affiliation information fresh for deleted Android logins,
+ // and make a note to potentially remove any unneeded cached data later.
+ facet_uris_to_trim.push_back(facet_uri);
+ affiliation_service_->CancelPrefetch(facet_uri, base::Time::Max());
+ }
+ }
+
+ // When the primary key for a login is updated, |changes| will contain both a
+ // REMOVE and ADD change for that login. Cached affiliation data should not be
+ // deleted in this case. A simple solution is to call TrimCacheForFacetURI()
+ // always after Prefetch() calls -- the trimming logic will detect that there
+ // is an active prefetch and not delete the corresponding data.
+ for (const FacetURI& facet_uri : facet_uris_to_trim)
+ affiliation_service_->TrimCacheForFacetURI(facet_uri);
+}
+
+void AffiliatedMatchHelper::OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+ for (const auto& form : results) {
+ FacetURI facet_uri;
+ if (IsAndroidApplicationCredential(*form, &facet_uri))
+ affiliation_service_->Prefetch(facet_uri, base::Time::Max());
+ }
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h
new file mode 100644
index 00000000000..7de63805549
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h
@@ -0,0 +1,160 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATED_MATCH_HELPER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATED_MATCH_HELPER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+
+namespace autofill {
+struct PasswordForm;
+} // namespace autofill
+
+namespace password_manager {
+
+class AffiliationService;
+
+// Interacts with the AffiliationService on behalf of the PasswordStore.
+// For each GetLogins() request, it provides the PasswordStore with a list of
+// additional realms that are affiliation-based matches to the observed realm.
+//
+// Currently, the only supported use-case is obtaining Android applications
+// affiliated with the web site containing the observed form. This is achieved
+// by implementing the "proactive fetching" strategy for interacting with the
+// AffiliationService (see affiliation_service.h for details), with Android
+// applications playing the role of facet Y.
+//
+// More specifically, this class prefetches affiliation information on start-up
+// for all Android applications that the PasswordStore has credentials stored
+// for. Then, the actual GetLogins() can be restricted to the cache, so that
+// realms of the observed web forms will never be looked up against the
+// Affiliation API.
+class AffiliatedMatchHelper : public PasswordStore::Observer,
+ public PasswordStoreConsumer {
+ public:
+ // Callback to returns the list of affiliated signon_realms (as per defined in
+ // autofill::PasswordForm) to the caller.
+ typedef base::Callback<void(const std::vector<std::string>&)>
+ AffiliatedRealmsCallback;
+
+ typedef base::Callback<void(
+ std::vector<std::unique_ptr<autofill::PasswordForm>>)>
+ PasswordFormsCallback;
+
+ // The |password_store| must outlive |this|. Both arguments must be non-NULL,
+ // except in tests which do not Initialize() the object.
+ AffiliatedMatchHelper(
+ PasswordStore* password_store,
+ std::unique_ptr<AffiliationService> affiliation_service);
+ ~AffiliatedMatchHelper() override;
+
+ // Schedules deferred initialization.
+ void Initialize();
+
+ // Retrieves realms of Android applications affiliated with the realm of the
+ // |observed_form| if it is web-based. Otherwise, yields the empty list. The
+ // |result_callback| will be invoked in both cases, on the same thread.
+ virtual void GetAffiliatedAndroidRealms(
+ const PasswordStore::FormDigest& observed_form,
+ const AffiliatedRealmsCallback& result_callback);
+
+ // Retrieves realms of web sites affiliated with the Android application that
+ // |android_form| belongs to and invokes |result_callback| on the same thread;
+ // or yields the empty list if |android_form| is not an Android credential.
+ // NOTE: This will issue an on-demand network request against the Affiliation
+ // API if affiliations of the Android application are not cached. However, as
+ // long as the |android_form| is from the PasswordStore, this should rarely
+ // happen as affiliation information for those applications are prefetched.
+ virtual void GetAffiliatedWebRealms(
+ const PasswordStore::FormDigest& android_form,
+ const AffiliatedRealmsCallback& result_callback);
+
+ // Retrieves affiliation and branding information about the Android
+ // credentials in |forms|, sets |affiliated_web_realm|, |app_display_name| and
+ // |app_icon_url| of forms, and invokes |result_callback|.
+ // NOTE: This will not issue an on-demand network request. If a request to
+ // cache fails, no affiliation and branding information will be injected into
+ // corresponding form.
+ virtual void InjectAffiliationAndBrandingInformation(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
+ const PasswordFormsCallback& result_callback);
+
+ // Returns whether or not |form| represents an Android credential.
+ static bool IsValidAndroidCredential(const PasswordStore::FormDigest& form);
+
+ // Returns whether or not |form| represents a valid Web credential for the
+ // purposes of affiliation-based matching.
+ static bool IsValidWebCredential(const PasswordStore::FormDigest& form);
+
+ // I/O heavy initialization on start-up will be delayed by this long.
+ // This should be high enough not to exacerbate start-up I/O contention too
+ // much, but also low enough that the user be able log-in shortly after
+ // browser start-up into web sites using Android credentials.
+ // TODO(engedy): See if we can tie this instead to some meaningful event.
+ static constexpr base::TimeDelta kInitializationDelayOnStartup =
+ base::TimeDelta::FromSeconds(8);
+
+ private:
+ // Reads all autofillable credentials from the password store and starts
+ // observing the store for future changes.
+ void DoDeferredInitialization();
+
+ // Called back by AffiliationService to supply the list of facets affiliated
+ // with |original_facet_uri| so that a GetAffiliatedAndroidRealms() call can
+ // be completed.
+ void CompleteGetAffiliatedAndroidRealms(
+ const FacetURI& original_facet_uri,
+ const AffiliatedRealmsCallback& result_callback,
+ const AffiliatedFacets& results,
+ bool success);
+
+ // Called back by AffiliationService to supply the list of facets affiliated
+ // with the Android application that GetAffiliatedWebRealms() was called with,
+ // so that the call can be completed.
+ void CompleteGetAffiliatedWebRealms(
+ const AffiliatedRealmsCallback& result_callback,
+ const AffiliatedFacets& results,
+ bool success);
+
+ // Called back by AffiliationService to supply the list of facets affiliated
+ // with the Android credential in |form|. Injects affiliation and branding
+ // information by setting |affiliated_web_realm|, |app_display_name| and
+ // |app_icon_url| on |form| if |success| is true and |results| is non-empty.
+ // Invokes |barrier_closure|.
+ void CompleteInjectAffiliationAndBrandingInformation(
+ autofill::PasswordForm* form,
+ base::Closure barrier_closure,
+ const AffiliatedFacets& results,
+ bool success);
+
+ // PasswordStore::Observer:
+ void OnLoginsChanged(const PasswordStoreChangeList& changes) override;
+
+ // PasswordStoreConsumer:
+ void OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+ PasswordStore* const password_store_;
+
+ // Being the sole consumer of AffiliationService, |this| owns the service.
+ std::unique_ptr<AffiliationService> affiliation_service_;
+
+ base::WeakPtrFactory<AffiliatedMatchHelper> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliatedMatchHelper);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATED_MATCH_HELPER_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
new file mode 100644
index 00000000000..db62fe2ed5e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
@@ -0,0 +1,670 @@
+// 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/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_mock_time_message_loop_task_runner.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/test_password_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
+
+class MockAffiliationService : public testing::StrictMock<AffiliationService> {
+ public:
+ MockAffiliationService() : testing::StrictMock<AffiliationService>(nullptr) {
+ testing::DefaultValue<AffiliatedFacets>::Set(AffiliatedFacets());
+ }
+
+ ~MockAffiliationService() override {}
+
+ MOCK_METHOD2(OnGetAffiliationsAndBrandingCalled,
+ AffiliatedFacets(const FacetURI&, StrategyOnCacheMiss));
+ MOCK_METHOD2(Prefetch, void(const FacetURI&, const base::Time&));
+ MOCK_METHOD2(CancelPrefetch, void(const FacetURI&, const base::Time&));
+ MOCK_METHOD1(TrimCacheForFacetURI, void(const FacetURI&));
+
+ void GetAffiliationsAndBranding(
+ const FacetURI& facet_uri,
+ StrategyOnCacheMiss cache_miss_strategy,
+ const ResultCallback& result_callback) override {
+ AffiliatedFacets affiliation =
+ OnGetAffiliationsAndBrandingCalled(facet_uri, cache_miss_strategy);
+ result_callback.Run(affiliation, !affiliation.empty());
+ }
+
+ void ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ const FacetURI& expected_facet_uri,
+ StrategyOnCacheMiss expected_cache_miss_strategy,
+ const AffiliatedFacets& affiliations_to_return) {
+ EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled(
+ expected_facet_uri, expected_cache_miss_strategy))
+ .WillOnce(testing::Return(affiliations_to_return));
+ }
+
+ void ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(
+ const FacetURI& expected_facet_uri,
+ StrategyOnCacheMiss expected_cache_miss_strategy) {
+ EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled(
+ expected_facet_uri, expected_cache_miss_strategy))
+ .WillOnce(testing::Return(AffiliatedFacets()));
+ }
+
+ void ExpectCallToPrefetch(const char* expected_facet_uri_spec) {
+ EXPECT_CALL(*this,
+ Prefetch(FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
+ base::Time::Max()))
+ .RetiresOnSaturation();
+ }
+
+ void ExpectCallToCancelPrefetch(const char* expected_facet_uri_spec) {
+ EXPECT_CALL(*this, CancelPrefetch(
+ FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
+ base::Time::Max()))
+ .RetiresOnSaturation();
+ }
+
+ void ExpectCallToTrimCacheForFacetURI(const char* expected_facet_uri_spec) {
+ EXPECT_CALL(*this, TrimCacheForFacetURI(FacetURI::FromCanonicalSpec(
+ expected_facet_uri_spec)))
+ .RetiresOnSaturation();
+ }
+
+ private:
+ DISALLOW_ASSIGN(MockAffiliationService);
+};
+
+const char kTestWebFacetURIAlpha1[] = "https://one.alpha.example.com";
+const char kTestWebFacetURIAlpha2[] = "https://two.alpha.example.com";
+const char kTestAndroidFacetURIAlpha3[] =
+ "android://hash@com.example.alpha.android";
+const char kTestAndroidFacetNameAlpha3[] = "Facet Name Alpha 3";
+const char kTestAndroidFacetIconURLAlpha3[] = "https://example.com/alpha_3.png";
+const char kTestWebRealmAlpha1[] = "https://one.alpha.example.com/";
+const char kTestWebRealmAlpha2[] = "https://two.alpha.example.com/";
+const char kTestAndroidRealmAlpha3[] =
+ "android://hash@com.example.alpha.android/";
+
+const char kTestWebFacetURIBeta1[] = "https://one.beta.example.com";
+const char kTestAndroidFacetURIBeta2[] =
+ "android://hash@com.example.beta.android";
+const char kTestAndroidFacetNameBeta2[] = "Facet Name Beta 2";
+const char kTestAndroidFacetIconURLBeta2[] = "https://example.com/beta_2.png";
+const char kTestAndroidFacetURIBeta3[] =
+ "android://hash@com.yetanother.beta.android";
+const char kTestAndroidFacetNameBeta3[] = "Facet Name Beta 3";
+const char kTestAndroidFacetIconURLBeta3[] = "https://example.com/beta_3.png";
+const char kTestWebRealmBeta1[] = "https://one.beta.example.com/";
+const char kTestAndroidRealmBeta2[] =
+ "android://hash@com.example.beta.android/";
+const char kTestAndroidRealmBeta3[] =
+ "android://hash@com.yetanother.beta.android/";
+
+const char kTestAndroidFacetURIGamma[] =
+ "android://hash@com.example.gamma.android";
+const char kTestAndroidRealmGamma[] =
+ "android://hash@com.example.gamma.android";
+
+const char kTestUsername[] = "JohnDoe";
+const char kTestPassword[] = "secret";
+
+AffiliatedFacets GetTestEquivalenceClassAlpha() {
+ return {
+ {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1)},
+ {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha2)},
+ {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
+ FacetBrandingInfo{kTestAndroidFacetNameAlpha3,
+ GURL(kTestAndroidFacetIconURLAlpha3)}},
+ };
+}
+
+AffiliatedFacets GetTestEquivalenceClassBeta() {
+ return {
+ {FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1)},
+ {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
+ FacetBrandingInfo{kTestAndroidFacetNameBeta2,
+ GURL(kTestAndroidFacetIconURLBeta2)}},
+ {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3),
+ FacetBrandingInfo{kTestAndroidFacetNameBeta3,
+ GURL(kTestAndroidFacetIconURLBeta3)}},
+ };
+}
+
+autofill::PasswordForm GetTestAndroidCredentials(const char* signon_realm) {
+ autofill::PasswordForm form;
+ form.scheme = autofill::PasswordForm::SCHEME_HTML;
+ form.signon_realm = signon_realm;
+ form.username_value = base::ASCIIToUTF16(kTestUsername);
+ form.password_value = base::ASCIIToUTF16(kTestPassword);
+ return form;
+}
+
+autofill::PasswordForm GetTestBlacklistedAndroidCredentials(
+ const char* signon_realm) {
+ autofill::PasswordForm form = GetTestAndroidCredentials(signon_realm);
+ form.blacklisted_by_user = true;
+ return form;
+}
+
+PasswordStore::FormDigest GetTestObservedWebForm(const char* signon_realm,
+ const char* origin) {
+ return {autofill::PasswordForm::SCHEME_HTML, signon_realm,
+ origin ? GURL(origin) : GURL()};
+}
+
+} // namespace
+
+class AffiliatedMatchHelperTest : public testing::Test {
+ public:
+ AffiliatedMatchHelperTest()
+ : expecting_result_callback_(false), mock_affiliation_service_(nullptr) {}
+ ~AffiliatedMatchHelperTest() override {}
+
+ protected:
+ void RunDeferredInitialization() {
+ mock_time_task_runner_->RunUntilIdle();
+ ASSERT_EQ(AffiliatedMatchHelper::kInitializationDelayOnStartup,
+ mock_time_task_runner_->NextPendingTaskDelay());
+ mock_time_task_runner_->FastForwardBy(
+ AffiliatedMatchHelper::kInitializationDelayOnStartup);
+ }
+
+ void RunUntilIdle() {
+ // TODO(gab): Add support for base::RunLoop().RunUntilIdle() in scope of
+ // ScopedMockTimeMessageLoopTaskRunner and use it instead of this helper
+ // method.
+ mock_time_task_runner_->RunUntilIdle();
+ }
+
+ void AddLogin(const autofill::PasswordForm& form) {
+ password_store_->AddLogin(form);
+ RunUntilIdle();
+ }
+
+ void UpdateLoginWithPrimaryKey(
+ const autofill::PasswordForm& new_form,
+ const autofill::PasswordForm& old_primary_key) {
+ password_store_->UpdateLoginWithPrimaryKey(new_form, old_primary_key);
+ RunUntilIdle();
+ }
+
+ void RemoveLogin(const autofill::PasswordForm& form) {
+ password_store_->RemoveLogin(form);
+ RunUntilIdle();
+ }
+
+ void AddAndroidAndNonAndroidTestLogins() {
+ AddLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+ AddLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
+ AddLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3));
+ AddLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
+
+ AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
+ AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
+ }
+
+ void RemoveAndroidAndNonAndroidTestLogins() {
+ RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+ RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
+ RemoveLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3));
+ RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
+
+ RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
+ RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
+ }
+
+ void ExpectPrefetchForAndroidTestLogins() {
+ mock_affiliation_service()->ExpectCallToPrefetch(
+ kTestAndroidFacetURIAlpha3);
+ mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta2);
+ mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta3);
+ mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIGamma);
+ }
+
+ void ExpectCancelPrefetchForAndroidTestLogins() {
+ mock_affiliation_service()->ExpectCallToCancelPrefetch(
+ kTestAndroidFacetURIAlpha3);
+ mock_affiliation_service()->ExpectCallToCancelPrefetch(
+ kTestAndroidFacetURIBeta2);
+ mock_affiliation_service()->ExpectCallToCancelPrefetch(
+ kTestAndroidFacetURIBeta3);
+ mock_affiliation_service()->ExpectCallToCancelPrefetch(
+ kTestAndroidFacetURIGamma);
+ }
+
+ void ExpectTrimCacheForAndroidTestLogins() {
+ mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+ kTestAndroidFacetURIAlpha3);
+ mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+ kTestAndroidFacetURIBeta2);
+ mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+ kTestAndroidFacetURIBeta3);
+ mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+ kTestAndroidFacetURIGamma);
+ }
+
+ std::vector<std::string> GetAffiliatedAndroidRealms(
+ const PasswordStore::FormDigest& observed_form) {
+ expecting_result_callback_ = true;
+ match_helper()->GetAffiliatedAndroidRealms(
+ observed_form,
+ base::Bind(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback,
+ base::Unretained(this)));
+ RunUntilIdle();
+ EXPECT_FALSE(expecting_result_callback_);
+ return last_result_realms_;
+ }
+
+ std::vector<std::string> GetAffiliatedWebRealms(
+ const PasswordStore::FormDigest& android_form) {
+ expecting_result_callback_ = true;
+ match_helper()->GetAffiliatedWebRealms(
+ android_form,
+ base::Bind(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback,
+ base::Unretained(this)));
+ RunUntilIdle();
+ EXPECT_FALSE(expecting_result_callback_);
+ return last_result_realms_;
+ }
+
+ std::vector<std::unique_ptr<autofill::PasswordForm>>
+ InjectAffiliationAndBrandingInformation(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
+ expecting_result_callback_ = true;
+ match_helper()->InjectAffiliationAndBrandingInformation(
+ std::move(forms),
+ base::Bind(&AffiliatedMatchHelperTest::OnFormsCallback,
+ base::Unretained(this)));
+ RunUntilIdle();
+ EXPECT_FALSE(expecting_result_callback_);
+ return std::move(last_result_forms_);
+ }
+
+ void DestroyMatchHelper() { match_helper_.reset(); }
+
+ TestPasswordStore* password_store() { return password_store_.get(); }
+
+ MockAffiliationService* mock_affiliation_service() {
+ return mock_affiliation_service_;
+ }
+
+ AffiliatedMatchHelper* match_helper() { return match_helper_.get(); }
+
+ private:
+ void OnAffiliatedRealmsCallback(
+ const std::vector<std::string>& affiliated_realms) {
+ EXPECT_TRUE(expecting_result_callback_);
+ expecting_result_callback_ = false;
+ last_result_realms_ = affiliated_realms;
+ }
+
+ void OnFormsCallback(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
+ EXPECT_TRUE(expecting_result_callback_);
+ expecting_result_callback_ = false;
+ last_result_forms_.swap(forms);
+ }
+
+ // testing::Test:
+ void SetUp() override {
+ std::unique_ptr<MockAffiliationService> service(
+ new MockAffiliationService());
+ mock_affiliation_service_ = service.get();
+
+ password_store_ = new TestPasswordStore;
+
+ match_helper_.reset(
+ new AffiliatedMatchHelper(password_store_.get(), std::move(service)));
+ }
+
+ void TearDown() override {
+ match_helper_.reset();
+ password_store_->ShutdownOnUIThread();
+ password_store_ = nullptr;
+ }
+
+ base::MessageLoop message_loop_;
+ base::ScopedMockTimeMessageLoopTaskRunner mock_time_task_runner_;
+
+ std::vector<std::string> last_result_realms_;
+ std::vector<std::unique_ptr<autofill::PasswordForm>> last_result_forms_;
+ bool expecting_result_callback_;
+
+ scoped_refptr<TestPasswordStore> password_store_;
+ std::unique_ptr<AffiliatedMatchHelper> match_helper_;
+
+ // Owned by |match_helper_|.
+ MockAffiliationService* mock_affiliation_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliatedMatchHelperTest);
+};
+
+// GetAffiliatedAndroidRealm* tests verify that GetAffiliatedAndroidRealms()
+// returns the realms of affiliated Android applications, but only Android
+// applications, and only if the observed form is a secure HTML login form.
+
+TEST_F(AffiliatedMatchHelperTest, GetAffiliatedAndroidRealmsYieldsResults) {
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1),
+ StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
+ EXPECT_THAT(GetAffiliatedAndroidRealms(
+ GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)),
+ testing::UnorderedElementsAre(kTestAndroidRealmBeta2,
+ kTestAndroidRealmBeta3));
+}
+
+TEST_F(AffiliatedMatchHelperTest,
+ GetAffiliatedAndroidRealmsYieldsOnlyAndroidApps) {
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
+ StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha());
+ // This verifies that |kTestWebRealmAlpha2| is not returned.
+ EXPECT_THAT(GetAffiliatedAndroidRealms(
+ GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)),
+ testing::UnorderedElementsAre(kTestAndroidRealmAlpha3));
+}
+
+TEST_F(AffiliatedMatchHelperTest,
+ GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPBasicAuthForms) {
+ PasswordStore::FormDigest http_auth_observed_form(
+ GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr));
+ http_auth_observed_form.scheme = autofill::PasswordForm::SCHEME_BASIC;
+ EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form),
+ testing::IsEmpty());
+}
+
+TEST_F(AffiliatedMatchHelperTest,
+ GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPDigestAuthForms) {
+ PasswordStore::FormDigest http_auth_observed_form(
+ GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr));
+ http_auth_observed_form.scheme = autofill::PasswordForm::SCHEME_DIGEST;
+ EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form),
+ testing::IsEmpty());
+}
+
+TEST_F(AffiliatedMatchHelperTest,
+ GetAffiliatedAndroidRealmsYieldsEmptyResultsForAndroidKeyedForms) {
+ PasswordStore::FormDigest android_observed_form(
+ GetTestAndroidCredentials(kTestAndroidRealmBeta2));
+ EXPECT_THAT(GetAffiliatedAndroidRealms(android_observed_form),
+ testing::IsEmpty());
+}
+
+TEST_F(AffiliatedMatchHelperTest,
+ GetAffiliatedAndroidRealmsYieldsEmptyResultsWhenNoPrefetch) {
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(
+ FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
+ StrategyOnCacheMiss::FAIL);
+ EXPECT_THAT(GetAffiliatedAndroidRealms(
+ GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)),
+ testing::IsEmpty());
+}
+
+// GetAffiliatedWebRealms* tests verify that GetAffiliatedWebRealms() returns
+// the realms of web sites affiliated with the given Android application, but
+// only web sites, and only if an Android application is queried.
+
+TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsResults) {
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK,
+ GetTestEquivalenceClassAlpha());
+ PasswordStore::FormDigest android_form(
+ GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+ EXPECT_THAT(
+ GetAffiliatedWebRealms(android_form),
+ testing::UnorderedElementsAre(kTestWebRealmAlpha1, kTestWebRealmAlpha2));
+}
+
+TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsOnlyWebsites) {
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK,
+ GetTestEquivalenceClassBeta());
+ PasswordStore::FormDigest android_form(
+ GetTestAndroidCredentials(kTestAndroidRealmBeta2));
+ // This verifies that |kTestAndroidRealmBeta3| is not returned.
+ EXPECT_THAT(GetAffiliatedWebRealms(android_form),
+ testing::UnorderedElementsAre(kTestWebRealmBeta1));
+}
+
+TEST_F(AffiliatedMatchHelperTest,
+ GetAffiliatedWebRealmsYieldsEmptyResultsForWebKeyedForms) {
+ EXPECT_THAT(GetAffiliatedWebRealms(
+ GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)),
+ testing::IsEmpty());
+}
+
+// Verifies that InjectAffiliationAndBrandingInformation() injects the realms of
+// web sites affiliated with the given Android application into the password
+// forms, as well as branding information corresponding to the application, if
+// any.
+TEST_F(AffiliatedMatchHelperTest, InjectAffiliationAndBrandingInformation) {
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms;
+
+ forms.push_back(base::MakeUnique<autofill::PasswordForm>(
+ GetTestAndroidCredentials(kTestAndroidRealmAlpha3)));
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
+ StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha());
+
+ forms.push_back(base::MakeUnique<autofill::PasswordForm>(
+ GetTestAndroidCredentials(kTestAndroidRealmBeta2)));
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
+ StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
+
+ forms.push_back(base::MakeUnique<autofill::PasswordForm>(
+ GetTestAndroidCredentials(kTestAndroidRealmBeta3)));
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+ FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3),
+ StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
+
+ forms.push_back(base::MakeUnique<autofill::PasswordForm>(
+ GetTestAndroidCredentials(kTestAndroidRealmGamma)));
+ mock_affiliation_service()
+ ->ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(
+ FacetURI::FromCanonicalSpec(kTestAndroidFacetURIGamma),
+ StrategyOnCacheMiss::FAIL);
+
+ PasswordStore::FormDigest digest =
+ GetTestObservedWebForm(kTestWebRealmBeta1, nullptr);
+ autofill::PasswordForm web_form;
+ web_form.scheme = digest.scheme;
+ web_form.signon_realm = digest.signon_realm;
+ web_form.origin = digest.origin;
+ forms.push_back(base::MakeUnique<autofill::PasswordForm>(web_form));
+
+ size_t expected_form_count = forms.size();
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results(
+ InjectAffiliationAndBrandingInformation(std::move(forms)));
+ ASSERT_EQ(expected_form_count, results.size());
+ EXPECT_THAT(results[0]->affiliated_web_realm,
+ testing::AnyOf(kTestWebRealmAlpha1, kTestWebRealmAlpha2));
+ EXPECT_EQ(kTestAndroidFacetNameAlpha3, results[0]->app_display_name);
+ EXPECT_EQ(kTestAndroidFacetIconURLAlpha3,
+ results[0]->app_icon_url.possibly_invalid_spec());
+ EXPECT_THAT(results[1]->affiliated_web_realm,
+ testing::Eq(kTestWebRealmBeta1));
+ EXPECT_EQ(kTestAndroidFacetNameBeta2, results[1]->app_display_name);
+ EXPECT_EQ(kTestAndroidFacetIconURLBeta2,
+ results[1]->app_icon_url.possibly_invalid_spec());
+ EXPECT_THAT(results[2]->affiliated_web_realm,
+ testing::Eq(kTestWebRealmBeta1));
+ EXPECT_EQ(kTestAndroidFacetNameBeta3, results[2]->app_display_name);
+ EXPECT_EQ(kTestAndroidFacetIconURLBeta3,
+ results[2]->app_icon_url.possibly_invalid_spec());
+ EXPECT_THAT(results[3]->affiliated_web_realm, testing::IsEmpty());
+ EXPECT_THAT(results[4]->affiliated_web_realm, testing::IsEmpty());
+}
+
+// Note: IsValidWebCredential() is tested as part of GetAffiliatedAndroidRealms
+// tests above.
+TEST_F(AffiliatedMatchHelperTest, IsValidAndroidCredential) {
+ EXPECT_FALSE(AffiliatedMatchHelper::IsValidAndroidCredential(
+ GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)));
+ PasswordStore::FormDigest android_credential(
+ GetTestAndroidCredentials(kTestAndroidRealmBeta2));
+ EXPECT_TRUE(
+ AffiliatedMatchHelper::IsValidAndroidCredential(android_credential));
+}
+
+// Verifies that affiliations for Android applications with pre-existing
+// credentials on start-up are prefetched.
+TEST_F(
+ AffiliatedMatchHelperTest,
+ PrefetchAffiliationsAndBrandingForPreexistingAndroidCredentialsOnStartup) {
+ AddAndroidAndNonAndroidTestLogins();
+
+ match_helper()->Initialize();
+ RunUntilIdle();
+
+ ExpectPrefetchForAndroidTestLogins();
+ ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+}
+
+// Stores credentials for Android applications between Initialize() and
+// DoDeferredInitialization(). Verifies that corresponding affiliation
+// information gets prefetched.
+TEST_F(AffiliatedMatchHelperTest,
+ PrefetchAffiliationsForAndroidCredentialsAddedInInitializationDelay) {
+ match_helper()->Initialize();
+ RunUntilIdle();
+
+ AddAndroidAndNonAndroidTestLogins();
+
+ ExpectPrefetchForAndroidTestLogins();
+ ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+}
+
+// Stores credentials for Android applications after DoDeferredInitialization().
+// Verifies that corresponding affiliation information gets prefetched.
+TEST_F(AffiliatedMatchHelperTest,
+ PrefetchAffiliationsForAndroidCredentialsAddedAfterInitialization) {
+ match_helper()->Initialize();
+ ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+ ExpectPrefetchForAndroidTestLogins();
+ AddAndroidAndNonAndroidTestLogins();
+}
+
+TEST_F(AffiliatedMatchHelperTest,
+ CancelPrefetchingAffiliationsAndBrandingForRemovedAndroidCredentials) {
+ AddAndroidAndNonAndroidTestLogins();
+ match_helper()->Initialize();
+ ExpectPrefetchForAndroidTestLogins();
+ ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+ ExpectCancelPrefetchForAndroidTestLogins();
+ ExpectTrimCacheForAndroidTestLogins();
+ RemoveAndroidAndNonAndroidTestLogins();
+}
+
+// Verify that whenever the primary key is updated for a credential (in which
+// case both REMOVE and ADD change notifications are sent out), then Prefetch()
+// is called in response to the addition before the call to
+// TrimCacheForFacetURI() in response to the removal, so that cached data is not
+// deleted and then immediately re-fetched.
+TEST_F(AffiliatedMatchHelperTest, PrefetchBeforeTrimForPrimaryKeyUpdates) {
+ AddAndroidAndNonAndroidTestLogins();
+ match_helper()->Initialize();
+ ExpectPrefetchForAndroidTestLogins();
+ ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+ mock_affiliation_service()->ExpectCallToCancelPrefetch(
+ kTestAndroidFacetURIAlpha3);
+
+ {
+ testing::InSequence in_sequence;
+ mock_affiliation_service()->ExpectCallToPrefetch(
+ kTestAndroidFacetURIAlpha3);
+ mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+ kTestAndroidFacetURIAlpha3);
+ }
+
+ autofill::PasswordForm old_form(
+ GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+ autofill::PasswordForm new_form(old_form);
+ new_form.username_value = base::ASCIIToUTF16("NewUserName");
+ UpdateLoginWithPrimaryKey(new_form, old_form);
+}
+
+// Stores and removes four credentials for the same an Android application, and
+// expects that Prefetch() and CancelPrefetch() will each be called four times.
+TEST_F(AffiliatedMatchHelperTest,
+ DuplicateCredentialsArePrefetchWithMultiplicity) {
+ EXPECT_CALL(*mock_affiliation_service(),
+ Prefetch(FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
+ base::Time::Max()))
+ .Times(4);
+
+ autofill::PasswordForm android_form(
+ GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+ AddLogin(android_form);
+
+ // Store two credentials before initialization.
+ autofill::PasswordForm android_form2(android_form);
+ android_form2.username_value = base::ASCIIToUTF16("JohnDoe2");
+ AddLogin(android_form2);
+
+ match_helper()->Initialize();
+ RunUntilIdle();
+
+ // Store one credential between initialization and deferred initialization.
+ autofill::PasswordForm android_form3(android_form);
+ android_form3.username_value = base::ASCIIToUTF16("JohnDoe3");
+ AddLogin(android_form3);
+
+ ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+ // Store one credential after deferred initialization.
+ autofill::PasswordForm android_form4(android_form);
+ android_form4.username_value = base::ASCIIToUTF16("JohnDoe4");
+ AddLogin(android_form4);
+
+ for (size_t i = 0; i < 4; ++i) {
+ mock_affiliation_service()->ExpectCallToCancelPrefetch(
+ kTestAndroidFacetURIAlpha3);
+ mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+ kTestAndroidFacetURIAlpha3);
+ }
+
+ RemoveLogin(android_form);
+ RemoveLogin(android_form2);
+ RemoveLogin(android_form3);
+ RemoveLogin(android_form4);
+}
+
+TEST_F(AffiliatedMatchHelperTest, DestroyBeforeDeferredInitialization) {
+ match_helper()->Initialize();
+ RunUntilIdle();
+ DestroyMatchHelper();
+ ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/affiliation_api.proto b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_api.proto
index 353ab69b26d..353ab69b26d 100644
--- a/chromium/components/password_manager/core/browser/affiliation_api.proto
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_api.proto
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
new file mode 100644
index 00000000000..4c6aca9de71
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.cc
@@ -0,0 +1,286 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_backend.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/time/clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_database.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h"
+#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"
+
+namespace password_manager {
+
+AffiliationBackend::AffiliationBackend(
+ const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ std::unique_ptr<base::Clock> time_source,
+ std::unique_ptr<base::TickClock> time_tick_source)
+ : request_context_getter_(request_context_getter),
+ task_runner_(task_runner),
+ clock_(std::move(time_source)),
+ tick_clock_(std::move(time_tick_source)),
+ construction_time_(clock_->Now()),
+ weak_ptr_factory_(this) {
+ DCHECK_LT(base::Time(), clock_->Now());
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+AffiliationBackend::~AffiliationBackend() {
+}
+
+void AffiliationBackend::Initialize(const base::FilePath& db_path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!throttler_);
+ throttler_.reset(
+ new AffiliationFetchThrottler(this, task_runner_, tick_clock_.get()));
+
+ // TODO(engedy): Currently, when Init() returns false, it always poisons the
+ // DB, so subsequent operations will silently fail. Consider either fully
+ // committing to this approach and making Init() a void, or handling the
+ // return value here. See: https://crbug.com/478831.
+ cache_.reset(new AffiliationDatabase());
+ cache_->Init(db_path);
+}
+
+void AffiliationBackend::GetAffiliationsAndBranding(
+ const FacetURI& facet_uri,
+ StrategyOnCacheMiss cache_miss_strategy,
+ const AffiliationService::ResultCallback& callback,
+ const scoped_refptr<base::TaskRunner>& callback_task_runner) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ FacetManager* facet_manager = GetOrCreateFacetManager(facet_uri);
+ DCHECK(facet_manager);
+ facet_manager->GetAffiliationsAndBranding(cache_miss_strategy, callback,
+ callback_task_runner);
+
+ if (facet_manager->CanBeDiscarded())
+ facet_managers_.erase(facet_uri);
+}
+
+void AffiliationBackend::Prefetch(const FacetURI& facet_uri,
+ const base::Time& keep_fresh_until) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ FacetManager* facet_manager = GetOrCreateFacetManager(facet_uri);
+ DCHECK(facet_manager);
+ facet_manager->Prefetch(keep_fresh_until);
+
+ if (facet_manager->CanBeDiscarded())
+ facet_managers_.erase(facet_uri);
+}
+
+void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri,
+ const base::Time& keep_fresh_until) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ auto facet_manager_it = facet_managers_.find(facet_uri);
+ if (facet_manager_it == facet_managers_.end())
+ return;
+ facet_manager_it->second->CancelPrefetch(keep_fresh_until);
+
+ if (facet_manager_it->second->CanBeDiscarded())
+ facet_managers_.erase(facet_uri);
+}
+
+void AffiliationBackend::TrimCacheForFacetURI(const FacetURI& facet_uri) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ AffiliatedFacetsWithUpdateTime affiliation;
+ if (cache_->GetAffiliationsAndBrandingForFacetURI(facet_uri, &affiliation))
+ DiscardCachedDataIfNoLongerNeeded(affiliation.facets);
+}
+
+// static
+void AffiliationBackend::DeleteCache(const base::FilePath& db_path) {
+ AffiliationDatabase::Delete(db_path);
+}
+
+FacetManager* AffiliationBackend::GetOrCreateFacetManager(
+ const FacetURI& facet_uri) {
+ std::unique_ptr<FacetManager>& facet_manager = facet_managers_[facet_uri];
+ if (!facet_manager) {
+ facet_manager =
+ base::MakeUnique<FacetManager>(facet_uri, this, clock_.get());
+ }
+ return facet_manager.get();
+}
+
+void AffiliationBackend::DiscardCachedDataIfNoLongerNeeded(
+ const AffiliatedFacets& affiliated_facets) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Discard the equivalence class if there is no facet in the class whose
+ // FacetManager claims that it needs to keep the data.
+ for (const auto& facet : affiliated_facets) {
+ auto facet_manager_it = facet_managers_.find(facet.uri);
+ if (facet_manager_it != facet_managers_.end() &&
+ !facet_manager_it->second->CanCachedDataBeDiscarded()) {
+ return;
+ }
+ }
+
+ CHECK(!affiliated_facets.empty());
+ cache_->DeleteAffiliationsAndBrandingForFacetURI(affiliated_facets[0].uri);
+}
+
+void AffiliationBackend::OnSendNotification(const FacetURI& facet_uri) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ auto facet_manager_it = facet_managers_.find(facet_uri);
+ if (facet_manager_it == facet_managers_.end())
+ return;
+ facet_manager_it->second->NotifyAtRequestedTime();
+
+ if (facet_manager_it->second->CanBeDiscarded())
+ facet_managers_.erase(facet_uri);
+}
+
+bool AffiliationBackend::ReadAffiliationsAndBrandingFromDatabase(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* affiliations) {
+ return cache_->GetAffiliationsAndBrandingForFacetURI(facet_uri, affiliations);
+}
+
+void AffiliationBackend::SignalNeedNetworkRequest() {
+ throttler_->SignalNetworkRequestNeeded();
+}
+
+void AffiliationBackend::RequestNotificationAtTime(const FacetURI& facet_uri,
+ base::Time time) {
+ // TODO(engedy): Avoid spamming the task runner; only ever schedule the first
+ // callback. crbug.com/437865.
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AffiliationBackend::OnSendNotification,
+ weak_ptr_factory_.GetWeakPtr(), facet_uri),
+ time - clock_->Now());
+}
+
+void AffiliationBackend::OnFetchSucceeded(
+ std::unique_ptr<AffiliationFetcherDelegate::Result> result) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ fetcher_.reset();
+ throttler_->InformOfNetworkRequestComplete(true);
+
+ for (const AffiliatedFacets& affiliated_facets : *result) {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ affiliation.facets = affiliated_facets;
+ affiliation.last_update_time = clock_->Now();
+
+ std::vector<AffiliatedFacetsWithUpdateTime> obsoleted_affiliations;
+ cache_->StoreAndRemoveConflicting(affiliation, &obsoleted_affiliations);
+
+ // Cached data in contradiction with newly stored data automatically gets
+ // removed from the DB, and will be stored into |obsoleted_affiliations|.
+ // TODO(engedy): Currently, handling this is entirely meaningless unless in
+ // the edge case when the user has credentials for two Android applications
+ // which are now being de-associated. But even in that case, nothing will
+ // explode and the only symptom will be that credentials for the Android
+ // application that is not being fetched right now, if any, will not be
+ // filled into affiliated applications until the next fetch. Still, this
+ // should be implemented at some point by letting facet managers know if
+ // data. See: https://crbug.com/478832.
+
+ for (const auto& facet : affiliated_facets) {
+ auto facet_manager_it = facet_managers_.find(facet.uri);
+ if (facet_manager_it == facet_managers_.end())
+ continue;
+ FacetManager* facet_manager = facet_manager_it->second.get();
+ facet_manager->OnFetchSucceeded(affiliation);
+ if (facet_manager->CanBeDiscarded())
+ facet_managers_.erase(facet.uri);
+ }
+ }
+
+ // A subsequent fetch may be needed if any additional
+ // GetAffiliationsAndBranding() requests came in while the current fetch was
+ // in flight.
+ for (const auto& facet_manager_pair : facet_managers_) {
+ if (facet_manager_pair.second->DoesRequireFetch()) {
+ throttler_->SignalNetworkRequestNeeded();
+ return;
+ }
+ }
+}
+
+void AffiliationBackend::OnFetchFailed() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ fetcher_.reset();
+ throttler_->InformOfNetworkRequestComplete(false);
+
+ // Trigger a retry if a fetch is still needed.
+ for (const auto& facet_manager_pair : facet_managers_) {
+ if (facet_manager_pair.second->DoesRequireFetch()) {
+ throttler_->SignalNetworkRequestNeeded();
+ return;
+ }
+ }
+}
+
+void AffiliationBackend::OnMalformedResponse() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // TODO(engedy): Potentially handle this case differently. crbug.com/437865.
+ OnFetchFailed();
+}
+
+bool AffiliationBackend::OnCanSendNetworkRequest() {
+ DCHECK(!fetcher_);
+ std::vector<FacetURI> requested_facet_uris;
+ for (const auto& facet_manager_pair : facet_managers_) {
+ if (facet_manager_pair.second->DoesRequireFetch())
+ requested_facet_uris.push_back(facet_manager_pair.first);
+ }
+
+ // In case a request is no longer needed, return false to indicate this.
+ if (requested_facet_uris.empty())
+ return false;
+
+ fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(),
+ requested_facet_uris, this));
+ fetcher_->StartRequest();
+ ReportStatistics(requested_facet_uris.size());
+ return true;
+}
+
+void AffiliationBackend::ReportStatistics(size_t requested_facet_uri_count) {
+ UMA_HISTOGRAM_COUNTS_100("PasswordManager.AffiliationBackend.FetchSize",
+ requested_facet_uri_count);
+
+ if (last_request_time_.is_null()) {
+ base::TimeDelta delay = clock_->Now() - construction_time_;
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "PasswordManager.AffiliationBackend.FirstFetchDelay", delay,
+ base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(3), 50);
+ } else {
+ base::TimeDelta delay = clock_->Now() - last_request_time_;
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "PasswordManager.AffiliationBackend.SubsequentFetchDelay", delay,
+ base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(3), 50);
+ }
+ last_request_time_ = clock_->Now();
+}
+
+void AffiliationBackend::SetThrottlerForTesting(
+ std::unique_ptr<AffiliationFetchThrottler> throttler) {
+ throttler_ = std::move(throttler);
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..dadeb111e3d
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.h
@@ -0,0 +1,167 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_BACKEND_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_BACKEND_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_delegate.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/facet_manager_host.h"
+
+namespace base {
+class Clock;
+class FilePath;
+class SequencedTaskRunner;
+class TaskRunner;
+class TickClock;
+class Time;
+} // namespace base
+
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+
+namespace password_manager {
+
+class AffiliationDatabase;
+class AffiliationFetcher;
+class AffiliationFetchThrottler;
+class FacetManager;
+
+// The AffiliationBackend is the part of the AffiliationService that lives on a
+// background thread suitable for performing blocking I/O. As most tasks require
+// I/O, the backend ends up doing most of the work for the AffiliationService;
+// the latter being just a thin layer that delegates most tasks to the backend.
+//
+// This class is not thread-safe, but it is fine to construct it on one thread
+// and then transfer it to the background thread for the rest of its life.
+// Initialize() must be called already on the final (background) thread.
+class AffiliationBackend : public FacetManagerHost,
+ public AffiliationFetcherDelegate,
+ public AffiliationFetchThrottlerDelegate {
+ public:
+ using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
+
+ // Constructs an instance that will use |request_context_getter| 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,
+ std::unique_ptr<base::Clock> time_source,
+ std::unique_ptr<base::TickClock> time_tick_source);
+ ~AffiliationBackend() override;
+
+ // 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);
+
+ // Implementations for methods of the same name in AffiliationService. They
+ // are not documented here again. See affiliation_service.h for details:
+ void GetAffiliationsAndBranding(
+ const FacetURI& facet_uri,
+ StrategyOnCacheMiss cache_miss_strategy,
+ const AffiliationService::ResultCallback& callback,
+ const scoped_refptr<base::TaskRunner>& callback_task_runner);
+ void Prefetch(const FacetURI& facet_uri, const base::Time& keep_fresh_until);
+ void CancelPrefetch(const FacetURI& facet_uri,
+ const base::Time& keep_fresh_until);
+ void TrimCacheForFacetURI(const FacetURI& facet_uri);
+
+ // Deletes the cache database file at |db_path|, and all auxiliary files. The
+ // database must be closed before calling this.
+ static void DeleteCache(const base::FilePath& db_path);
+
+ private:
+ friend class AffiliationBackendTest;
+ FRIEND_TEST_ALL_PREFIXES(
+ AffiliationBackendTest,
+ DiscardCachedDataIfNoLongerNeededWithEmptyAffiliation);
+
+ // Retrieves the FacetManager corresponding to |facet_uri|, creating it and
+ // storing it into |facet_managers_| if it did not exist.
+ FacetManager* GetOrCreateFacetManager(const FacetURI& facet_uri);
+
+ // Discards cached data corresponding to |affiliated_facets| unless there are
+ // FacetManagers that still need the data.
+ void DiscardCachedDataIfNoLongerNeeded(
+ const AffiliatedFacets& affiliated_facets);
+
+ // Scheduled by RequestNotificationAtTime() to be called back at times when a
+ // FacetManager needs to be notified.
+ void OnSendNotification(const FacetURI& facet_uri);
+
+ // FacetManagerHost:
+ bool ReadAffiliationsAndBrandingFromDatabase(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* affiliations) override;
+ void SignalNeedNetworkRequest() override;
+ void RequestNotificationAtTime(const FacetURI& facet_uri,
+ base::Time time) override;
+
+ // AffiliationFetcherDelegate:
+ void OnFetchSucceeded(
+ std::unique_ptr<AffiliationFetcherDelegate::Result> result) override;
+ void OnFetchFailed() override;
+ void OnMalformedResponse() override;
+
+ // AffiliationFetchThrottlerDelegate:
+ bool OnCanSendNetworkRequest() override;
+
+ // Returns the number of in-memory FacetManagers. Used only for testing.
+ size_t facet_manager_count_for_testing() { return facet_managers_.size(); }
+
+ // Reports the |requested_facet_uri_count| in a single fetch; and the elapsed
+ // time before the first fetch, and in-between subsequent fetches.
+ void ReportStatistics(size_t requested_facet_uri_count);
+
+ // To be called after Initialize() to use |throttler| instead of the default
+ // one. Used only for testing.
+ void SetThrottlerForTesting(
+ std::unique_ptr<AffiliationFetchThrottler> throttler);
+
+ // Ensures that all methods, excluding construction, are called on the same
+ // sequence.
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ std::unique_ptr<base::Clock> clock_;
+ std::unique_ptr<base::TickClock> tick_clock_;
+
+ std::unique_ptr<AffiliationDatabase> cache_;
+ std::unique_ptr<AffiliationFetcher> fetcher_;
+ std::unique_ptr<AffiliationFetchThrottler> throttler_;
+
+ base::Time construction_time_;
+ base::Time last_request_time_;
+
+ // Contains a FacetManager for each facet URI that need ongoing attention. To
+ // save memory, managers are discarded as soon as they become redundant.
+ std::unordered_map<FacetURI, std::unique_ptr<FacetManager>, FacetURIHash>
+ facet_managers_;
+
+ base::WeakPtrFactory<AffiliationBackend> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationBackend);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_BACKEND_H_
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
new file mode 100644
index 00000000000..ce1bb521ea7
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend_unittest.cc
@@ -0,0 +1,874 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_backend.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/time/clock.h"
+#include "base/time/tick_clock.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_database.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h"
+#include "components/password_manager/core/browser/android_affiliation/facet_manager.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 "net/url_request/url_request_context_getter.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+using StrategyOnCacheMiss = AffiliationBackend::StrategyOnCacheMiss;
+
+// Mock fetch throttler that has some extra logic to accurately portray the real
+// AffiliationFetchThrottler in how it ignores SignalNetworkRequestNeeded()
+// requests when a request is already known to be needed or one is already in
+// flight, and in how it goes back to the idle state afterwards.
+class MockAffiliationFetchThrottler : public AffiliationFetchThrottler {
+ public:
+ explicit MockAffiliationFetchThrottler(
+ AffiliationFetchThrottlerDelegate* delegate)
+ : AffiliationFetchThrottler(delegate, nullptr, nullptr),
+ signaled_network_request_needed_(false) {
+ EXPECT_CALL(*this, OnInformOfNetworkRequestComplete(testing::_)).Times(0);
+ }
+
+ ~MockAffiliationFetchThrottler() {
+ EXPECT_FALSE(signaled_network_request_needed_);
+ }
+
+ // Expects that InformOfNetworkRequestComplete() will be called to indicate
+ // either success or failure, depending on |expected_success|.
+ void ExpectInformOfNetworkRequestComplete(bool expected_success) {
+ EXPECT_CALL(*this, OnInformOfNetworkRequestComplete(expected_success));
+ }
+
+ // Informs the |delegate_| that it can send the needed network request.
+ // Returns true if the |delegate_| reported that it actually ended up issuing
+ // a request.
+ bool LetNetworkRequestBeSent() {
+ EXPECT_TRUE(has_signaled_network_request_needed());
+ if (!delegate_->OnCanSendNetworkRequest()) {
+ reset_signaled_network_request_needed();
+ return false;
+ }
+ return true;
+ }
+
+ // Whether or not the throttler is 'signaled', meaning that the real throttler
+ // would eventually call OnCanSendNetworkRequest() on the |delegate_|.
+ bool has_signaled_network_request_needed() const {
+ return signaled_network_request_needed_;
+ }
+
+ // Forces the mock throttler back to 'non-signaled' state. Normally, this does
+ // not need to be manually called, as this is done by the mock automatically.
+ void reset_signaled_network_request_needed() {
+ signaled_network_request_needed_ = false;
+ }
+
+ private:
+ MOCK_METHOD1(OnInformOfNetworkRequestComplete, void(bool));
+
+ // AffiliationFetchThrottler:
+ void SignalNetworkRequestNeeded() override {
+ signaled_network_request_needed_ = true;
+ }
+
+ void InformOfNetworkRequestComplete(bool success) override {
+ OnInformOfNetworkRequestComplete(success);
+ reset_signaled_network_request_needed();
+ }
+
+ bool signaled_network_request_needed_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetchThrottler);
+};
+
+const char kTestFacetURIAlpha1[] = "https://one.alpha.example.com";
+const char kTestFacetURIAlpha2[] = "https://two.alpha.example.com";
+const char kTestFacetURIAlpha3[] = "https://three.alpha.example.com";
+const char kTestFacetURIAlpha4[] = "android://hash@com.example.alpha.android";
+const char kTestFacetNameAlpha4[] = "Facet Name Alpha";
+const char kTestFacetIconURLAlpha4[] = "https://example.com/alpha.png";
+const char kTestFacetURIBeta1[] = "https://one.beta.example.com";
+const char kTestFacetURIBeta2[] = "https://two.beta.example.com";
+const char kTestFacetURIGamma1[] = "https://gamma.example.com";
+
+AffiliatedFacets GetTestEquivalenceClassAlpha() {
+ return {
+ {FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURIAlpha3)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURIAlpha4),
+ FacetBrandingInfo{kTestFacetNameAlpha4, GURL(kTestFacetIconURLAlpha4)}},
+ };
+}
+
+AffiliatedFacets GetTestEquivalenceClassBeta() {
+ return {
+ {FacetURI::FromCanonicalSpec(kTestFacetURIBeta1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURIBeta2)},
+ };
+}
+
+AffiliatedFacets GetTestEquivalenceClassGamma() {
+ return {
+ {FacetURI::FromCanonicalSpec(kTestFacetURIGamma1)},
+ };
+}
+
+base::TimeDelta GetCacheHardExpiryPeriod() {
+ return base::TimeDelta::FromHours(FacetManager::kCacheHardExpiryInHours);
+}
+
+base::TimeDelta GetCacheSoftExpiryPeriod() {
+ return base::TimeDelta::FromHours(FacetManager::kCacheSoftExpiryInHours);
+}
+
+base::TimeDelta GetShortTestPeriod() {
+ return base::TimeDelta::FromHours(1);
+}
+
+// Returns a smallest time difference that this test cares about.
+base::TimeDelta Epsilon() {
+ return base::TimeDelta::FromMicroseconds(1);
+}
+
+} // namespace
+
+class AffiliationBackendTest : public testing::Test {
+ public:
+ AffiliationBackendTest()
+ : backend_task_runner_(new base::TestMockTimeTaskRunner),
+ consumer_task_runner_(new base::TestSimpleTaskRunner),
+ mock_fetch_throttler_(nullptr) {}
+ ~AffiliationBackendTest() override {}
+
+ protected:
+ void GetAffiliationsAndBranding(MockAffiliationConsumer* consumer,
+ const FacetURI& facet_uri,
+ StrategyOnCacheMiss cache_miss_strategy) {
+ backend_->GetAffiliationsAndBranding(facet_uri, cache_miss_strategy,
+ consumer->GetResultCallback(),
+ consumer_task_runner());
+ }
+
+ void Prefetch(const FacetURI& facet_uri, base::Time keep_fresh_until) {
+ backend_->Prefetch(facet_uri, keep_fresh_until);
+ }
+
+ void CancelPrefetch(const FacetURI& facet_uri, base::Time keep_fresh_until) {
+ backend_->CancelPrefetch(facet_uri, keep_fresh_until);
+ }
+
+ void ExpectNeedForFetchAndLetItBeSent() {
+ ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
+ ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
+ ASSERT_TRUE(mock_fetch_throttler()->LetNetworkRequestBeSent());
+ }
+
+ void ExpectAndCompleteFetch(
+ const std::vector<FacetURI>& expected_requested_facet_uris) {
+ ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
+ EXPECT_THAT(
+ fake_affiliation_api()->GetNextRequestedFacets(),
+ testing::UnorderedElementsAreArray(expected_requested_facet_uris));
+ mock_fetch_throttler()->ExpectInformOfNetworkRequestComplete(true);
+ fake_affiliation_api()->ServeNextRequest();
+ testing::Mock::VerifyAndClearExpectations(mock_fetch_throttler());
+ }
+
+ void ExpectAndCompleteFetch(const FacetURI& expected_requested_facet_uri) {
+ std::vector<FacetURI> expected_facet_uris;
+ expected_facet_uris.push_back(expected_requested_facet_uri);
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(expected_facet_uris));
+ }
+
+ void ExpectAndFailFetch(const FacetURI& expected_requested_facet_uri) {
+ ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
+ EXPECT_THAT(fake_affiliation_api()->GetNextRequestedFacets(),
+ testing::UnorderedElementsAre(expected_requested_facet_uri));
+ mock_fetch_throttler()->ExpectInformOfNetworkRequestComplete(false);
+ fake_affiliation_api()->FailNextRequest();
+ testing::Mock::VerifyAndClearExpectations(mock_fetch_throttler());
+ }
+
+ void ExpectNoFetchNeeded() {
+ ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
+ ASSERT_FALSE(mock_fetch_throttler()->has_signaled_network_request_needed());
+ }
+
+ void ExpectFailureWithoutFetch(MockAffiliationConsumer* consumer) {
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ consumer->ExpectFailure();
+ consumer_task_runner_->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(consumer);
+ }
+
+ void GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ const FacetURI& facet_uri,
+ const AffiliatedFacets& expected_result) {
+ GetAffiliationsAndBranding(mock_consumer(), facet_uri,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
+ mock_consumer()->ExpectSuccessWithResult(expected_result);
+ consumer_task_runner_->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+ }
+
+ void GetAffiliationsAndBrandingAndExpectResultWithoutFetch(
+ const FacetURI& facet_uri,
+ StrategyOnCacheMiss cache_miss_strategy,
+ const AffiliatedFacets& expected_result) {
+ GetAffiliationsAndBranding(mock_consumer(), facet_uri, cache_miss_strategy);
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ mock_consumer()->ExpectSuccessWithResult(expected_result);
+ consumer_task_runner_->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+ }
+
+ // TODO(engedy): Within this test fixture, the word "failure" refers to GTest
+ // failures, simulated network failures (above), and also AffiliationService
+ // failure callbacks. Make this less ambiguous.
+ void GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ const FacetURI& facet_uri) {
+ GetAffiliationsAndBranding(mock_consumer(), facet_uri,
+ StrategyOnCacheMiss::FAIL);
+ ASSERT_NO_FATAL_FAILURE(ExpectFailureWithoutFetch(mock_consumer()));
+ }
+
+ void PrefetchAndExpectFetch(const FacetURI& facet_uri,
+ base::Time keep_fresh_until) {
+ Prefetch(facet_uri, keep_fresh_until);
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
+ }
+
+ // Verifies that both on-demand and cached-only GetAffiliationsAndBranding()
+ // requests for each facet in |affiliated_facets| are served from cache with
+ // no fetches.
+ void ExpectThatEquivalenceClassIsServedFromCache(
+ const AffiliatedFacets& affiliated_facets) {
+ for (const Facet& facet : affiliated_facets) {
+ SCOPED_TRACE(facet.uri);
+ ASSERT_NO_FATAL_FAILURE(
+ GetAffiliationsAndBrandingAndExpectResultWithoutFetch(
+ facet.uri, StrategyOnCacheMiss::FAIL, affiliated_facets));
+ ASSERT_NO_FATAL_FAILURE(
+ GetAffiliationsAndBrandingAndExpectResultWithoutFetch(
+ facet.uri, StrategyOnCacheMiss::FAIL, affiliated_facets));
+ }
+ }
+
+ void DestroyBackend() { backend_.reset(); }
+
+ void AdvanceTime(base::TimeDelta delta) {
+ backend_task_runner_->FastForwardBy(delta);
+ }
+
+ // Directly opens the database and returns the number of equivalence classes
+ // stored therein.
+ size_t GetNumOfEquivalenceClassInDatabase() {
+ AffiliationDatabase database;
+ EXPECT_TRUE(database.Init(db_path()));
+ std::vector<AffiliatedFacetsWithUpdateTime> all_affiliations;
+ database.GetAllAffiliationsAndBranding(&all_affiliations);
+ return all_affiliations.size();
+ }
+
+ size_t backend_facet_manager_count() {
+ return backend()->facet_manager_count_for_testing();
+ }
+
+ bool IsCachedDataFreshForFacetURI(const FacetURI& facet_uri) {
+ std::unique_ptr<base::Clock> clock(backend_task_runner_->GetMockClock());
+ return FacetManager(facet_uri, backend(), clock.get()).IsCachedDataFresh();
+ }
+
+ bool IsCachedDataNearStaleForFacetURI(const FacetURI& facet_uri) {
+ std::unique_ptr<base::Clock> clock(backend_task_runner_->GetMockClock());
+ return FacetManager(facet_uri, backend(), clock.get())
+ .IsCachedDataNearStale();
+ }
+
+ AffiliationBackend* backend() { return backend_.get(); }
+
+ const base::FilePath& db_path() const { return db_path_; }
+
+ base::TestMockTimeTaskRunner* backend_task_runner() {
+ return backend_task_runner_.get();
+ }
+
+ base::TestSimpleTaskRunner* consumer_task_runner() {
+ return consumer_task_runner_.get();
+ }
+
+ ScopedFakeAffiliationAPI* fake_affiliation_api() {
+ return &fake_affiliation_api_;
+ }
+
+ MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
+
+ MockAffiliationFetchThrottler* mock_fetch_throttler() {
+ return mock_fetch_throttler_;
+ }
+
+ private:
+ // testing::Test:
+ void SetUp() override {
+ ASSERT_TRUE(CreateTemporaryFile(&db_path_));
+ backend_.reset(new AffiliationBackend(
+ NULL, backend_task_runner_, backend_task_runner_->GetMockClock(),
+ backend_task_runner_->GetMockTickClock()));
+ backend_->Initialize(db_path());
+ mock_fetch_throttler_ = new MockAffiliationFetchThrottler(backend_.get());
+ backend_->SetThrottlerForTesting(
+ base::WrapUnique<AffiliationFetchThrottler>(mock_fetch_throttler_));
+
+ fake_affiliation_api_.AddTestEquivalenceClass(
+ GetTestEquivalenceClassAlpha());
+ fake_affiliation_api_.AddTestEquivalenceClass(
+ GetTestEquivalenceClassBeta());
+ fake_affiliation_api_.AddTestEquivalenceClass(
+ GetTestEquivalenceClassGamma());
+ }
+
+ scoped_refptr<base::TestMockTimeTaskRunner> backend_task_runner_;
+ scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
+
+ base::FilePath db_path_;
+ ScopedFakeAffiliationAPI fake_affiliation_api_;
+ MockAffiliationConsumer mock_consumer_;
+ std::unique_ptr<AffiliationBackend> backend_;
+ MockAffiliationFetchThrottler* mock_fetch_throttler_; // Owned by |backend_|.
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationBackendTest);
+};
+
+TEST_F(AffiliationBackendTest, OnDemandRequestSucceedsWithFetch) {
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ GetTestEquivalenceClassAlpha()));
+ EXPECT_EQ(0u, backend_facet_manager_count());
+
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIBeta1),
+ GetTestEquivalenceClassBeta()));
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+// This test also verifies that the FacetManager is immediately discarded.
+TEST_F(AffiliationBackendTest, CachedOnlyRequestFailsDueToCacheMiss) {
+ GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+TEST_F(AffiliationBackendTest, PrefetchTriggersInitialFetch) {
+ ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
+}
+
+// This test also verifies that the FacetManager is immediately discarded.
+TEST_F(AffiliationBackendTest, ExpiredPrefetchTriggersNoInitialFetch) {
+ // Prefetch intervals are open from the right, thus intervals ending Now() are
+ // already expired.
+ Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ backend_task_runner()->Now());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_EQ(0u, backend_facet_manager_count());
+ EXPECT_FALSE(backend_task_runner()->HasPendingTask());
+}
+
+// One additional GetAffiliationsAndBranding() and one Prefetch() request come
+// in, both for unrelated facets, shortly after an initial
+// GetAffiliationsAndBranding() request.
+//
+// Suppose that the network request triggered by the first
+// GetAffiliationsAndBranding() request has already been initiated when the
+// other requests arrive. As there should be no simultaneous requests, the
+// additional facets should be queried together in a second fetch after the
+// first fetch completes.
+TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests) {
+ FacetURI facet_uri_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
+ FacetURI facet_uri_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
+ FacetURI facet_uri_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
+
+ // Pretend the fetch is already away when the two other requests come in.
+ MockAffiliationConsumer second_consumer;
+ GetAffiliationsAndBranding(mock_consumer(), facet_uri_alpha,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ GetAffiliationsAndBranding(&second_consumer, facet_uri_beta,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ Prefetch(facet_uri_gamma, base::Time::Max());
+
+ std::vector<FacetURI> second_fetch_uris;
+ second_fetch_uris.push_back(facet_uri_beta);
+ second_fetch_uris.push_back(facet_uri_gamma);
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri_alpha));
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(second_fetch_uris));
+
+ mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
+ second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta());
+ consumer_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+
+ // Now that the two GetAffiliation() requests have been completed, the first
+ // two FacetManagers should be discarded. The third FacetManager corresponding
+ // to the prefetched facet should be kept.
+ EXPECT_GE(1u, backend_facet_manager_count());
+}
+
+// Now suppose that the first fetch is somewhat delayed (e.g., because network
+// requests are throttled), so the other requests arrive before it is actually
+// issued. In this case, all facet URIs should be queried together in one fetch.
+TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests2) {
+ FacetURI facet_uri_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
+ FacetURI facet_uri_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
+ FacetURI facet_uri_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
+
+ MockAffiliationConsumer second_consumer;
+ GetAffiliationsAndBranding(mock_consumer(), facet_uri_alpha,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ GetAffiliationsAndBranding(&second_consumer, facet_uri_beta,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ Prefetch(facet_uri_gamma, base::Time::Max());
+
+ std::vector<FacetURI> fetched_uris;
+ fetched_uris.push_back(facet_uri_alpha);
+ fetched_uris.push_back(facet_uri_beta);
+ fetched_uris.push_back(facet_uri_gamma);
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(fetched_uris));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+
+ mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
+ second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta());
+ consumer_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+
+ // Now that the two GetAffiliation() requests have been completed, the first
+ // two FacetManagers should be discarded. The third FacetManager corresponding
+ // to the prefetched facet should be kept.
+ EXPECT_GE(1u, backend_facet_manager_count());
+}
+
+TEST_F(AffiliationBackendTest, RetryIsMadeOnFailedFetch) {
+ FacetURI facet_uri(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
+
+ GetAffiliationsAndBranding(mock_consumer(), facet_uri,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(ExpectAndFailFetch(facet_uri));
+ EXPECT_EQ(1u, backend_facet_manager_count());
+
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
+
+ mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
+ consumer_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+// The Prefetch() request expires before fetching corresponding affiliation
+// information would be allowed. The fetch should be abandoned.
+TEST_F(AffiliationBackendTest, FetchIsNoLongerNeededOnceAllowed) {
+ Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ backend_task_runner()->Now() + GetShortTestPeriod());
+ ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
+ ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
+
+ AdvanceTime(GetShortTestPeriod() + Epsilon());
+
+ bool did_send_request = mock_fetch_throttler()->LetNetworkRequestBeSent();
+ EXPECT_FALSE(did_send_request);
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForSameFacet) {
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ GetTestEquivalenceClassAlpha()));
+
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectResultWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassAlpha()));
+
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectResultWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha()));
+
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForAffiliatedFacet) {
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ GetTestEquivalenceClassAlpha()));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+TEST_F(AffiliationBackendTest, CacheServesRequestsForPrefetchedFacets) {
+ ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
+
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectResultWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassAlpha()));
+
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectResultWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha()));
+}
+
+TEST_F(AffiliationBackendTest,
+ CacheServesRequestsForFacetsAffiliatedWithPrefetchedFacets) {
+ ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+}
+
+// A second GetAffiliationsAndBranding() request for the same facet and a third
+// request for an affiliated facet comes in while the network fetch triggered by
+// the first request is in flight.
+//
+// There should be no simultaneous requests, and once the fetch completes, all
+// three requests should be served without further fetches (they have the data).
+TEST_F(AffiliationBackendTest,
+ CacheServesConcurrentRequestsForAffiliatedFacets) {
+ FacetURI facet_uri1(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
+ FacetURI facet_uri2(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
+
+ MockAffiliationConsumer second_consumer;
+ MockAffiliationConsumer third_consumer;
+ GetAffiliationsAndBranding(mock_consumer(), facet_uri1,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ GetAffiliationsAndBranding(&second_consumer, facet_uri1,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ GetAffiliationsAndBranding(&third_consumer, facet_uri2,
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+
+ mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
+ second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
+ third_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
+ consumer_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+// A second Prefetch() request for the same facet and a third request for an
+// affiliated facet comes in while the initial fetch triggered by the first
+// request is in flight.
+//
+// There should be no simultaneous requests, and once the fetch completes, there
+// should be no further initial fetches as the data needed is already there.
+TEST_F(AffiliationBackendTest,
+ CacheServesConcurrentPrefetchesForAffiliatedFacets) {
+ FacetURI facet_uri1(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
+ FacetURI facet_uri2(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
+
+ Prefetch(facet_uri1, base::Time::Max());
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ Prefetch(facet_uri1, base::Time::Max());
+ Prefetch(facet_uri2, base::Time::Max());
+
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+}
+
+TEST_F(AffiliationBackendTest, SimpleCacheExpiryWithoutPrefetches) {
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ GetTestEquivalenceClassAlpha()));
+
+ AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
+
+ EXPECT_TRUE(IsCachedDataFreshForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+
+ AdvanceTime(Epsilon());
+
+ // After the data becomes stale, the cached-only request should fail, but the
+ // subsequent on-demand request should fetch the data again and succeed.
+ EXPECT_FALSE(IsCachedDataFreshForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
+
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
+ GetTestEquivalenceClassAlpha()));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+
+ EXPECT_EQ(0u, backend_facet_manager_count());
+}
+
+// A Prefetch() request for a finite period. It should trigger an initial fetch
+// and exactly one refetch, as the Prefetch() request expires exactly when the
+// cached data obtained with the refetch expires.
+TEST_F(AffiliationBackendTest,
+ PrefetchTriggersOneInitialFetchAndOneRefetchBeforeExpiring) {
+ ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ backend_task_runner()->Now() + GetCacheHardExpiryPeriod() +
+ GetCacheSoftExpiryPeriod()));
+
+ AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
+
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_FALSE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+
+ AdvanceTime(Epsilon());
+
+ EXPECT_TRUE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+
+ AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
+
+ EXPECT_TRUE(IsCachedDataFreshForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ EXPECT_TRUE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+
+ AdvanceTime(Epsilon());
+
+ // The data should be allowed to expire and the FacetManager discarded.
+ EXPECT_FALSE(IsCachedDataFreshForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_EQ(0u, backend_facet_manager_count());
+ EXPECT_FALSE(backend_task_runner()->HasPendingTask());
+
+ ASSERT_NO_FATAL_FAILURE(
+ GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(
+ GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2)));
+
+ // However, a subsequent on-demand request should be able to trigger a fetch.
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ GetTestEquivalenceClassAlpha()));
+}
+
+// Affiliation data for prefetched facets should be automatically refetched once
+// every 23 hours, and GetAffiliationsAndBranding() requests regarding
+// affiliated facets should be continuously served from cache.
+TEST_F(AffiliationBackendTest, PrefetchTriggersPeriodicRefetch) {
+ ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
+
+ for (int cycle = 0; cycle < 3; ++cycle) {
+ SCOPED_TRACE(cycle);
+
+ AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
+
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_TRUE(IsCachedDataFreshForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ EXPECT_FALSE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+
+ AdvanceTime(Epsilon());
+
+ EXPECT_TRUE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+ }
+}
+
+TEST_F(AffiliationBackendTest,
+ PrefetchTriggersNoInitialFetchIfDataIsAlreadyFresh) {
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ GetTestEquivalenceClassAlpha()));
+
+ EXPECT_FALSE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+
+ Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+}
+
+TEST_F(AffiliationBackendTest, CancelPrefetch) {
+ ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
+
+ AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
+
+ // Cancel the prefetch the last microsecond before a refetch would take place.
+ backend()->CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ base::Time::Max());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_EQ(0u, backend_facet_manager_count());
+ EXPECT_TRUE(backend_task_runner()->HasPendingTask());
+
+ AdvanceTime(GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod() +
+ Epsilon());
+
+ // The data should be allowed to expire.
+ EXPECT_FALSE(backend_task_runner()->HasPendingTask());
+ EXPECT_TRUE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(
+ GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(
+ GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2)));
+}
+
+TEST_F(AffiliationBackendTest, CancelDuplicatePrefetch) {
+ ASSERT_NO_FATAL_FAILURE(PrefetchAndExpectFetch(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()));
+ Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max());
+
+ AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
+
+ // Cancel the prefetch the last microsecond before a refetch would take place.
+ backend()->CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ base::Time::Max());
+
+ AdvanceTime(Epsilon());
+
+ // However, there is a second Prefetch() request which should keep the data
+ // fresh.
+ EXPECT_EQ(1u, backend_facet_manager_count());
+ EXPECT_TRUE(IsCachedDataNearStaleForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+
+ AdvanceTime(GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod());
+
+ EXPECT_TRUE(IsCachedDataFreshForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassAlpha()));
+}
+
+// Canceling a non-existing prefetch request for a non-prefetched facet.
+TEST_F(AffiliationBackendTest, CancelingNonExistingPrefetchIsSilentlyIgnored) {
+ CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ backend_task_runner()->Now() + base::TimeDelta::FromHours(24));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_EQ(0u, backend_facet_manager_count());
+ EXPECT_FALSE(backend_task_runner()->HasPendingTask());
+}
+
+// Verify that TrimCacheForFacetURI() only removes the equivalence class for the
+// given facet, and preserves others (even if they could be discarded).
+TEST_F(AffiliationBackendTest,
+ TrimCacheForFacetURIOnlyRemovesDataForTheGivenFacet) {
+ FacetURI preserved_facet_uri(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ GetTestEquivalenceClassAlpha()));
+ ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndBrandingAndExpectFetchAndThenResult(
+ preserved_facet_uri, GetTestEquivalenceClassBeta()));
+ EXPECT_EQ(2u, GetNumOfEquivalenceClassInDatabase());
+
+ backend()->TrimCacheForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
+ EXPECT_EQ(1u, GetNumOfEquivalenceClassInDatabase());
+
+ // Also verify that the last update time of the affiliation data is preserved,
+ // i.e., that it expires when it would normally have expired.
+ AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
+ EXPECT_TRUE(IsCachedDataFreshForFacetURI(preserved_facet_uri));
+ ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
+ GetTestEquivalenceClassBeta()));
+ AdvanceTime(Epsilon());
+ EXPECT_FALSE(IsCachedDataFreshForFacetURI(preserved_facet_uri));
+ ASSERT_NO_FATAL_FAILURE(
+ GetAffiliationsAndBrandingAndExpectFailureWithoutFetch(
+ preserved_facet_uri));
+}
+
+TEST_F(AffiliationBackendTest, NothingExplodesWhenShutDownDuringFetch) {
+ GetAffiliationsAndBranding(mock_consumer(),
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
+ mock_fetch_throttler()->reset_signaled_network_request_needed();
+ DestroyBackend();
+}
+
+TEST_F(AffiliationBackendTest,
+ FailureCallbacksAreCalledIfBackendIsDestroyedWithPendingRequest) {
+ GetAffiliationsAndBranding(mock_consumer(),
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ // Currently, a GetAffiliationsAndBranding() request can only be blocked due
+ // to fetch in flight -- so emulate this condition when destroying the
+ // backend.
+ ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
+ mock_fetch_throttler()->reset_signaled_network_request_needed();
+ DestroyBackend();
+ mock_consumer()->ExpectFailure();
+ consumer_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+}
+
+TEST_F(AffiliationBackendTest, DeleteCache) {
+ DestroyBackend();
+ ASSERT_TRUE(base::PathExists(db_path()));
+ AffiliationBackend::DeleteCache(db_path());
+ ASSERT_FALSE(base::PathExists(db_path()));
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.cc
new file mode 100644
index 00000000000..560fcea689c
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.cc
@@ -0,0 +1,346 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_database.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "sql/connection.h"
+#include "sql/error_delegate_util.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace password_manager {
+
+namespace {
+
+// The current version number of the affiliation database schema.
+const int kVersion = 2;
+
+// The oldest version of the schema such that a legacy Chrome client using that
+// version can still read/write the current database.
+const int kCompatibleVersion = 1;
+
+} // namespace
+
+AffiliationDatabase::AffiliationDatabase() {
+}
+
+AffiliationDatabase::~AffiliationDatabase() {
+}
+
+bool AffiliationDatabase::Init(const base::FilePath& path) {
+ sql_connection_.reset(new sql::Connection);
+ sql_connection_->set_histogram_tag("Affiliation");
+ sql_connection_->set_error_callback(base::Bind(
+ &AffiliationDatabase::SQLErrorCallback, base::Unretained(this)));
+
+ if (!sql_connection_->Open(path))
+ return false;
+
+ if (!sql_connection_->Execute("PRAGMA foreign_keys=1")) {
+ sql_connection_->Poison();
+ return false;
+ }
+
+ sql::MetaTable metatable;
+ if (!metatable.Init(sql_connection_.get(), kVersion, kCompatibleVersion)) {
+ sql_connection_->Poison();
+ return false;
+ }
+
+ if (metatable.GetCompatibleVersionNumber() > kVersion) {
+ LOG(WARNING) << "AffiliationDatabase is too new.";
+ sql_connection_->Poison();
+ return false;
+ }
+
+ SQLTableBuilder eq_classes_builder("eq_classes");
+ SQLTableBuilder eq_class_members_builder("eq_class_members");
+ InitializeTableBuilders(&eq_classes_builder, &eq_class_members_builder);
+
+ if (!CreateTables(eq_classes_builder, eq_class_members_builder)) {
+ LOG(WARNING) << "Failed to create tables.";
+ sql_connection_->Poison();
+ return false;
+ }
+
+ int version = metatable.GetVersionNumber();
+ if (version < kVersion) {
+ if (!MigrateTablesFrom(eq_classes_builder, eq_class_members_builder,
+ version)) {
+ LOG(WARNING) << "Failed to migrate tables from version " << version
+ << ".";
+ sql_connection_->Poison();
+ return false;
+ }
+
+ // Set the current version number is case of a successful migration.
+ metatable.SetVersionNumber(kVersion);
+ }
+
+ return true;
+}
+
+bool AffiliationDatabase::GetAffiliationsAndBrandingForFacetURI(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* result) const {
+ DCHECK(result);
+ result->facets.clear();
+
+ sql::Statement statement(sql_connection_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "SELECT m2.facet_uri, m2.facet_display_name, m2.facet_icon_url,"
+ " c.last_update_time "
+ "FROM eq_class_members m1, eq_class_members m2, eq_classes c "
+ "WHERE m1.facet_uri = ? AND m1.set_id = m2.set_id AND m1.set_id = c.id"));
+ statement.BindString(0, facet_uri.canonical_spec());
+
+ while (statement.Step()) {
+ result->facets.push_back(
+ {FacetURI::FromCanonicalSpec(statement.ColumnString(0)),
+ FacetBrandingInfo{
+ statement.ColumnString(1), GURL(statement.ColumnString(2)),
+ }});
+ result->last_update_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(3));
+ }
+
+ return !result->facets.empty();
+}
+
+void AffiliationDatabase::GetAllAffiliationsAndBranding(
+ std::vector<AffiliatedFacetsWithUpdateTime>* results) const {
+ DCHECK(results);
+ results->clear();
+
+ sql::Statement statement(sql_connection_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "SELECT m.facet_uri, m.facet_display_name, m.facet_icon_url,"
+ " c.last_update_time, c.id "
+ "FROM eq_class_members m, eq_classes c "
+ "WHERE m.set_id = c.id "
+ "ORDER BY c.id"));
+
+ int64_t last_eq_class_id = 0;
+ while (statement.Step()) {
+ int64_t eq_class_id = statement.ColumnInt64(4);
+ if (results->empty() || eq_class_id != last_eq_class_id) {
+ results->push_back(AffiliatedFacetsWithUpdateTime());
+ last_eq_class_id = eq_class_id;
+ }
+ results->back().facets.push_back(
+ {FacetURI::FromCanonicalSpec(statement.ColumnString(0)),
+ FacetBrandingInfo{
+ statement.ColumnString(1), GURL(statement.ColumnString(2)),
+ }});
+ results->back().last_update_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(3));
+ }
+}
+
+void AffiliationDatabase::DeleteAffiliationsAndBrandingForFacetURI(
+ const FacetURI& facet_uri) {
+ sql::Transaction transaction(sql_connection_.get());
+ if (!transaction.Begin())
+ return;
+
+ sql::Statement statement_lookup(sql_connection_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "SELECT m.set_id FROM eq_class_members m "
+ "WHERE m.facet_uri = ?"));
+ statement_lookup.BindString(0, facet_uri.canonical_spec());
+
+ // No such |facet_uri|, nothing to do.
+ if (!statement_lookup.Step())
+ return;
+
+ int64_t eq_class_id = statement_lookup.ColumnInt64(0);
+
+ // Children will get deleted due to 'ON DELETE CASCADE'.
+ sql::Statement statement_parent(sql_connection_->GetCachedStatement(
+ SQL_FROM_HERE, "DELETE FROM eq_classes WHERE eq_classes.id = ?"));
+ statement_parent.BindInt64(0, eq_class_id);
+ if (!statement_parent.Run())
+ return;
+
+ transaction.Commit();
+}
+
+void AffiliationDatabase::DeleteAffiliationsAndBrandingOlderThan(
+ const base::Time& cutoff_threshold) {
+ // Children will get deleted due to 'ON DELETE CASCADE'.
+ sql::Statement statement_parent(sql_connection_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "DELETE FROM eq_classes "
+ "WHERE eq_classes.last_update_time < ?"));
+ statement_parent.BindInt64(0, cutoff_threshold.ToInternalValue());
+ statement_parent.Run();
+}
+
+void AffiliationDatabase::DeleteAllAffiliationsAndBranding() {
+ // Children will get deleted due to 'ON DELETE CASCADE'.
+ sql::Statement statement_parent(
+ sql_connection_->GetUniqueStatement("DELETE FROM eq_classes"));
+ statement_parent.Run();
+}
+
+bool AffiliationDatabase::Store(
+ const AffiliatedFacetsWithUpdateTime& affiliated_facets) {
+ DCHECK(!affiliated_facets.facets.empty());
+
+ sql::Statement statement_parent(sql_connection_->GetCachedStatement(
+ SQL_FROM_HERE, "INSERT INTO eq_classes(last_update_time) VALUES (?)"));
+
+ sql::Statement statement_child(sql_connection_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "INSERT INTO "
+ "eq_class_members(facet_uri, facet_display_name, facet_icon_url, set_id) "
+ "VALUES (?, ?, ?, ?)"));
+
+ sql::Transaction transaction(sql_connection_.get());
+ if (!transaction.Begin())
+ return false;
+
+ statement_parent.BindInt64(
+ 0, affiliated_facets.last_update_time.ToInternalValue());
+ if (!statement_parent.Run())
+ return false;
+
+ int64_t eq_class_id = sql_connection_->GetLastInsertRowId();
+ for (const Facet& facet : affiliated_facets.facets) {
+ statement_child.Reset(true);
+ statement_child.BindString(0, facet.uri.canonical_spec());
+ statement_child.BindString(1, facet.branding_info.name);
+ statement_child.BindString(
+ 2, facet.branding_info.icon_url.possibly_invalid_spec());
+ statement_child.BindInt64(3, eq_class_id);
+ if (!statement_child.Run())
+ return false;
+ }
+
+ return transaction.Commit();
+}
+
+void AffiliationDatabase::StoreAndRemoveConflicting(
+ const AffiliatedFacetsWithUpdateTime& affiliation,
+ std::vector<AffiliatedFacetsWithUpdateTime>* removed_affiliations) {
+ DCHECK(!affiliation.facets.empty());
+ DCHECK(removed_affiliations);
+ removed_affiliations->clear();
+
+ sql::Transaction transaction(sql_connection_.get());
+ if (!transaction.Begin())
+ return;
+
+ for (const Facet& facet : affiliation.facets) {
+ AffiliatedFacetsWithUpdateTime old_affiliation;
+ if (GetAffiliationsAndBrandingForFacetURI(facet.uri, &old_affiliation)) {
+ if (!AreEquivalenceClassesEqual(old_affiliation.facets,
+ affiliation.facets)) {
+ removed_affiliations->push_back(old_affiliation);
+ }
+ DeleteAffiliationsAndBrandingForFacetURI(facet.uri);
+ }
+ }
+
+ if (!Store(affiliation))
+ NOTREACHED();
+
+ transaction.Commit();
+}
+
+// static
+void AffiliationDatabase::Delete(const base::FilePath& path) {
+ bool success = sql::Connection::Delete(path);
+ DCHECK(success);
+}
+
+int AffiliationDatabase::GetDatabaseVersionForTesting() {
+ sql::MetaTable metatable;
+ // The second and third parameters to |MetaTable::Init| are ignored, given
+ // that a metatable already exists. Hence they are not influencing the version
+ // of the underlying database.
+ DCHECK(sql::MetaTable::DoesTableExist(sql_connection_.get()));
+ metatable.Init(sql_connection_.get(), 1, 1);
+ return metatable.GetVersionNumber();
+}
+
+// static
+void AffiliationDatabase::InitializeTableBuilders(
+ SQLTableBuilder* eq_classes_builder,
+ SQLTableBuilder* eq_class_members_builder) {
+ // Version 1 of the affiliation database.
+ eq_classes_builder->AddColumnToPrimaryKey("id", "INTEGER");
+ eq_classes_builder->AddColumn("last_update_time", "INTEGER");
+ // The first call to |SealVersion| sets the version to 0, that's why it is
+ // repeated.
+ eq_classes_builder->SealVersion();
+ unsigned eq_classes_version = eq_classes_builder->SealVersion();
+ DCHECK_EQ(1u, eq_classes_version);
+
+ eq_class_members_builder->AddColumnToPrimaryKey("id", "INTEGER");
+ eq_class_members_builder->AddColumnToUniqueKey("facet_uri",
+ "LONGVARCHAR NOT NULL");
+ eq_class_members_builder->AddColumn(
+ "set_id", "INTEGER NOT NULL REFERENCES eq_classes(id) ON DELETE CASCADE");
+ // An index on eq_class_members.facet_uri is automatically created due to the
+ // UNIQUE constraint, however, we must create one on eq_class_members.set_id
+ // manually (to prevent linear scan when joining).
+ eq_class_members_builder->AddIndex("index_on_eq_class_members_set_id",
+ {"set_id"});
+ // The first call to |SealVersion| sets the version to 0, that's why it is
+ // repeated.
+ eq_class_members_builder->SealVersion();
+ unsigned eq_class_members_version = eq_class_members_builder->SealVersion();
+ DCHECK_EQ(1u, eq_class_members_version);
+
+ // Version 2 of the affiliation database.
+ eq_classes_version = eq_classes_builder->SealVersion();
+ DCHECK_EQ(2u, eq_classes_version);
+
+ eq_class_members_builder->AddColumn("facet_display_name", "VARCHAR");
+ eq_class_members_builder->AddColumn("facet_icon_url", "VARCHAR");
+ eq_class_members_version = eq_class_members_builder->SealVersion();
+ DCHECK_EQ(2u, eq_class_members_version);
+}
+
+bool AffiliationDatabase::CreateTables(
+ const SQLTableBuilder& eq_classes_builder,
+ const SQLTableBuilder& eq_class_members_builder) {
+ return eq_classes_builder.CreateTable(sql_connection_.get()) &&
+ eq_class_members_builder.CreateTable(sql_connection_.get());
+}
+
+bool AffiliationDatabase::MigrateTablesFrom(
+ const SQLTableBuilder& eq_classes_builder,
+ const SQLTableBuilder& eq_class_members_builder,
+ unsigned version) {
+ return eq_classes_builder.MigrateFrom(version, sql_connection_.get()) &&
+ eq_class_members_builder.MigrateFrom(version, sql_connection_.get());
+}
+
+void AffiliationDatabase::SQLErrorCallback(int error,
+ sql::Statement* statement) {
+ if (sql::IsErrorCatastrophic(error)) {
+ // Normally this will poison the database, causing any subsequent operations
+ // to silently fail without any side effects. However, if RazeAndClose() is
+ // called from the error callback in response to an error raised from within
+ // sql::Connection::Open, opening the now-razed database will be retried.
+ sql_connection_->RazeAndClose();
+ return;
+ }
+
+ // The default handling is to assert on debug and to ignore on release.
+ if (!sql::Connection::IsExpectedSqliteError(error))
+ DLOG(FATAL) << sql_connection_->GetErrorMessage();
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.h
new file mode 100644
index 00000000000..b380c96e731
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database.h
@@ -0,0 +1,120 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_DATABASE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_DATABASE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/sql_table_builder.h"
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace sql {
+class Connection;
+class Statement;
+} // namespace sql
+
+namespace password_manager {
+
+// Stores equivalence classes of facets, i.e., facets that are affiliated with
+// each other, in an SQLite database. In addition, relevant branding information
+// is stored. See affiliation_utils.h for a more detailed definition of what
+// this means.
+//
+// Under the assumption that there is most likely not much the caller can do in
+// case of database errors, most methods silently ignore them. Nevertheless, the
+// caller must plan ahead for this rare but non-negligible scenario, and expect
+// that in odd cases basic database invariants will not hold.
+class AffiliationDatabase {
+ public:
+ AffiliationDatabase();
+ ~AffiliationDatabase();
+
+ // Opens an existing database at |path|, or creates a new one if none exists,
+ // and returns true on success.
+ bool Init(const base::FilePath& path);
+
+ // Looks up the equivalence class containing |facet_uri|, and returns true if
+ // such a class is found, in which case it is also stored into |result|
+ // together with branding information, if applicable.
+ bool GetAffiliationsAndBrandingForFacetURI(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* result) const;
+
+ // Retrieves all stored equivalence classes and branding information.
+ void GetAllAffiliationsAndBranding(
+ std::vector<AffiliatedFacetsWithUpdateTime>* results) const;
+
+ // Removes the stored equivalence class and branding information, if any,
+ // containing |facet_uri|.
+ void DeleteAffiliationsAndBrandingForFacetURI(const FacetURI& facet_uri);
+
+ // Removes stored equivalence classes and branding information that were last
+ // updated before the |cutoff_threshold|.
+ void DeleteAffiliationsAndBrandingOlderThan(
+ const base::Time& cutoff_threshold);
+
+ // Removes all records from all tables of the database.
+ void DeleteAllAffiliationsAndBranding();
+
+ // Stores the equivalence class and branding information defined by
+ // |affiliated_facets| to the DB and returns true unless it has a non-empty
+ // subset with a preexisting class, in which case no changes are made and the
+ // function returns false.
+ bool Store(const AffiliatedFacetsWithUpdateTime& affiliated_facets);
+
+ // Stores the equivalence class and branding information defined by
+ // |affiliated_facets| to the database, and removes any other equivalence
+ // classes that are in conflict with |affiliated_facets|, i.e. those that are
+ // neither equal nor disjoint to it. Removed equivalence classes are stored
+ // into |removed_affiliations|.
+ void StoreAndRemoveConflicting(
+ const AffiliatedFacetsWithUpdateTime& affiliated_facets,
+ std::vector<AffiliatedFacetsWithUpdateTime>* removed_affiliations);
+
+ // Deletes the database file at |path| along with all its auxiliary files. The
+ // database must be closed before calling this.
+ static void Delete(const base::FilePath& path);
+
+ // Exposes the version of the underlying database. Should only be used in
+ // tests.
+ int GetDatabaseVersionForTesting();
+
+ private:
+ // Initializes the passed in table builders and defines the structure of the
+ // tables.
+ static void InitializeTableBuilders(
+ SQLTableBuilder* eq_classes_builder,
+ SQLTableBuilder* eq_class_members_builder);
+
+ // Creates the tables in the database using the provided table builders.
+ // Returns |false| on error, |true| on success.
+ bool CreateTables(const SQLTableBuilder& eq_classes_builder,
+ const SQLTableBuilder& eq_class_members_builder);
+
+ // Migrates an existing database from an earlier |version| using the provided
+ // table builders. Returns |false| on error, |true| on success.
+ bool MigrateTablesFrom(const SQLTableBuilder& eq_classes_builder,
+ const SQLTableBuilder& eq_class_members_builder,
+ unsigned version);
+
+ // Called when SQLite encounters an error.
+ void SQLErrorCallback(int error_number, sql::Statement* statement);
+
+ // The SQL connection to the database.
+ std::unique_ptr<sql::Connection> sql_connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationDatabase);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_DATABASE_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database_unittest.cc
new file mode 100644
index 00000000000..9f6e67a7101
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_database_unittest.cc
@@ -0,0 +1,409 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_database.h"
+
+#include <stdint.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "sql/test/scoped_error_expecter.h"
+#include "sql/test/test_helpers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/sqlite/sqlite3.h"
+#include "url/gurl.h"
+
+namespace password_manager {
+
+namespace {
+
+const char kTestFacetURI1[] = "https://alpha.example.com";
+const char kTestFacetURI2[] = "https://beta.example.com";
+const char kTestFacetURI3[] = "https://gamma.example.com";
+const char kTestFacetURI4[] = "https://delta.example.com";
+const char kTestFacetURI5[] = "https://epsilon.example.com";
+const char kTestFacetURI6[] = "https://zeta.example.com";
+
+const char kTestAndroidFacetURI[] = "android://hash@com.example.android";
+const char kTestAndroidPlayName[] = "Test Android App";
+const char kTestAndroidIconURL[] = "https://example.com/icon.png";
+
+const int64_t kTestTimeUs1 = 1000000;
+const int64_t kTestTimeUs2 = 2000000;
+const int64_t kTestTimeUs3 = 3000000;
+
+void ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ const AffiliatedFacetsWithUpdateTime& expectation,
+ const AffiliatedFacetsWithUpdateTime& reality) {
+ EXPECT_EQ(expectation.last_update_time, reality.last_update_time);
+ EXPECT_THAT(reality.facets,
+ testing::UnorderedElementsAreArray(expectation.facets));
+}
+
+AffiliatedFacetsWithUpdateTime TestEquivalenceClass1() {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ affiliation.last_update_time = base::Time::FromInternalValue(kTestTimeUs1);
+ affiliation.facets = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI2)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ };
+ return affiliation;
+}
+
+AffiliatedFacetsWithUpdateTime TestEquivalenceClass2() {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ affiliation.last_update_time = base::Time::FromInternalValue(kTestTimeUs2);
+ affiliation.facets = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI4)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI5)},
+ };
+ return affiliation;
+}
+
+AffiliatedFacetsWithUpdateTime TestEquivalenceClass3() {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ affiliation.last_update_time = base::Time::FromInternalValue(kTestTimeUs3);
+ affiliation.facets = {
+ {FacetURI::FromCanonicalSpec(kTestAndroidFacetURI),
+ FacetBrandingInfo{kTestAndroidPlayName, GURL(kTestAndroidIconURL)}},
+ };
+ return affiliation;
+}
+
+} // namespace
+
+class AffiliationDatabaseTest : public testing::Test {
+ public:
+ AffiliationDatabaseTest() {}
+ ~AffiliationDatabaseTest() override {}
+
+ void SetUp() override {
+ ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
+ OpenDatabase();
+ }
+
+ void OpenDatabase() {
+ db_.reset(new AffiliationDatabase);
+ ASSERT_TRUE(db_->Init(db_path()));
+ }
+
+ void CloseDatabase() { db_.reset(); }
+
+ void StoreInitialTestData() {
+ ASSERT_TRUE(db_->Store(TestEquivalenceClass1()));
+ ASSERT_TRUE(db_->Store(TestEquivalenceClass2()));
+ ASSERT_TRUE(db_->Store(TestEquivalenceClass3()));
+ }
+
+ AffiliationDatabase& db() { return *db_; }
+
+ base::FilePath db_path() {
+ return temp_directory_.GetPath().Append(
+ FILE_PATH_LITERAL("Test Affiliation Database"));
+ }
+
+ private:
+ base::ScopedTempDir temp_directory_;
+ std::unique_ptr<AffiliationDatabase> db_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationDatabaseTest);
+};
+
+TEST_F(AffiliationDatabaseTest, Store) {
+ LOG(ERROR) << "During this test, SQL errors (number 19) will be logged to "
+ "the console. This is expected.";
+
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ // Verify that duplicate equivalence classes are not allowed to be stored.
+ {
+ sql::test::ScopedErrorExpecter expecter;
+ expecter.ExpectError(SQLITE_CONSTRAINT);
+ AffiliatedFacetsWithUpdateTime duplicate = TestEquivalenceClass1();
+ EXPECT_FALSE(db().Store(duplicate));
+ EXPECT_TRUE(expecter.SawExpectedErrors());
+ }
+
+ // Verify that intersecting equivalence classes are not allowed to be stored.
+ {
+ sql::test::ScopedErrorExpecter expecter;
+ expecter.ExpectError(SQLITE_CONSTRAINT);
+ AffiliatedFacetsWithUpdateTime intersecting;
+ intersecting.facets = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI4)},
+ };
+ EXPECT_FALSE(db().Store(intersecting));
+ EXPECT_TRUE(expecter.SawExpectedErrors());
+ }
+}
+
+TEST_F(AffiliationDatabaseTest, GetAllAffiliationsAndBranding) {
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+
+ // Empty database should not return any equivalence classes.
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ EXPECT_EQ(0u, affiliations.size());
+
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ // The test data should be returned in order.
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(3u, affiliations.size());
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass1(),
+ affiliations[0]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass2(),
+ affiliations[1]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass3(),
+ affiliations[2]);
+}
+
+TEST_F(AffiliationDatabaseTest, GetAffiliationForFacet) {
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ // Verify that querying any element in the first equivalence class yields that
+ // class.
+ for (const auto& facet : TestEquivalenceClass1().facets) {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ EXPECT_TRUE(
+ db().GetAffiliationsAndBrandingForFacetURI(facet.uri, &affiliation));
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ TestEquivalenceClass1(), affiliation);
+ }
+
+ // Verify that querying the sole element in the third equivalence class yields
+ // that class.
+ {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ EXPECT_TRUE(db().GetAffiliationsAndBrandingForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestAndroidFacetURI), &affiliation));
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ TestEquivalenceClass3(), affiliation);
+ }
+
+ // Verify that querying a facet not in the database yields no result.
+ {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ EXPECT_FALSE(db().GetAffiliationsAndBrandingForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURI6), &affiliation));
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ AffiliatedFacetsWithUpdateTime(), affiliation);
+ }
+}
+
+TEST_F(AffiliationDatabaseTest, StoreAndRemoveConflicting) {
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ AffiliatedFacetsWithUpdateTime updated = TestEquivalenceClass1();
+ updated.last_update_time = base::Time::FromInternalValue(4000000);
+
+ // Verify that duplicate equivalence classes are now allowed to be stored, and
+ // the last update timestamp is updated.
+ {
+ std::vector<AffiliatedFacetsWithUpdateTime> removed;
+ db().StoreAndRemoveConflicting(updated, &removed);
+ EXPECT_EQ(0u, removed.size());
+
+ AffiliatedFacetsWithUpdateTime affiliation;
+ EXPECT_TRUE(db().GetAffiliationsAndBrandingForFacetURI(
+ FacetURI::FromCanonicalSpec(kTestFacetURI1), &affiliation));
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(updated, affiliation);
+ }
+
+ // Verify that intersecting equivalence classes are now allowed to be stored,
+ // the conflicting classes are removed, but unaffected classes are retained.
+ {
+ AffiliatedFacetsWithUpdateTime intersecting;
+ std::vector<AffiliatedFacetsWithUpdateTime> removed;
+ intersecting.last_update_time = base::Time::FromInternalValue(5000000);
+ intersecting.facets = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI4)},
+ };
+ db().StoreAndRemoveConflicting(intersecting, &removed);
+
+ ASSERT_EQ(2u, removed.size());
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(updated, removed[0]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ TestEquivalenceClass2(), removed[1]);
+
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(2u, affiliations.size());
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ TestEquivalenceClass3(), affiliations[0]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(intersecting,
+ affiliations[1]);
+ }
+}
+
+TEST_F(AffiliationDatabaseTest, DeleteAllAffiliationsAndBranding) {
+ db().DeleteAllAffiliationsAndBranding();
+
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ db().DeleteAllAffiliationsAndBranding();
+
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(0u, affiliations.size());
+}
+
+TEST_F(AffiliationDatabaseTest, DeleteAffiliationsAndBrandingOlderThan) {
+ db().DeleteAffiliationsAndBrandingOlderThan(base::Time::FromInternalValue(0));
+
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ db().DeleteAffiliationsAndBrandingOlderThan(
+ base::Time::FromInternalValue(kTestTimeUs2));
+
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(2u, affiliations.size());
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass2(),
+ affiliations[0]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass3(),
+ affiliations[1]);
+
+ db().DeleteAffiliationsAndBrandingOlderThan(base::Time::Max());
+
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(0u, affiliations.size());
+}
+
+// Verify that an existing DB can be reopened, and data is retained.
+TEST_F(AffiliationDatabaseTest, DBRetainsDataAfterReopening) {
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ CloseDatabase();
+ OpenDatabase();
+
+ // The test data should be returned in order.
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(3u, affiliations.size());
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass1(),
+ affiliations[0]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass2(),
+ affiliations[1]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass3(),
+ affiliations[2]);
+}
+
+// Verify that when it is discovered during opening that a DB is corrupt, it
+// gets razed, and then an empty (but again usable) DB is produced.
+TEST_F(AffiliationDatabaseTest, CorruptDBIsRazedThenOpened) {
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+
+ CloseDatabase();
+ ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path()));
+ ASSERT_NO_FATAL_FAILURE(OpenDatabase());
+
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ EXPECT_EQ(0u, affiliations.size());
+
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ EXPECT_EQ(3u, affiliations.size());
+}
+
+// Verify that when the DB becomes corrupt after it has been opened, it gets
+// poisoned so that operations fail silently without side effects.
+TEST_F(AffiliationDatabaseTest, CorruptDBGetsPoisoned) {
+ ASSERT_TRUE(db().Store(TestEquivalenceClass1()));
+
+ ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path()));
+
+ EXPECT_FALSE(db().Store(TestEquivalenceClass2()));
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ EXPECT_EQ(0u, affiliations.size());
+}
+
+// Verify that all files get deleted.
+TEST_F(AffiliationDatabaseTest, Delete) {
+ ASSERT_NO_FATAL_FAILURE(StoreInitialTestData());
+ CloseDatabase();
+
+ AffiliationDatabase::Delete(db_path());
+ EXPECT_TRUE(base::IsDirectoryEmpty(db_path().DirName()));
+}
+
+TEST_F(AffiliationDatabaseTest, MigrateFromVersion1) {
+ // Close and delete the current database and create it from scratch with the
+ // SQLite statement stored in affiliation_db_v1.sql.
+ CloseDatabase();
+ AffiliationDatabase::Delete(db_path());
+ base::FilePath src_root_dir;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_root_dir));
+ base::FilePath sql_path_v1 = src_root_dir.AppendASCII("components")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("password_manager")
+ .AppendASCII("affiliation_db_v1.sql");
+ ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path(), sql_path_v1));
+
+ // Re-open the database, triggering the migration.
+ OpenDatabase();
+
+ // Check that migration was successful and existing data was untouched.
+ EXPECT_EQ(2, db().GetDatabaseVersionForTesting());
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(3u, affiliations.size());
+
+ // There was no branding information in version 1, thus we expect it to be
+ // empty after the migration.
+ const auto WithoutBrandingInfo =
+ [](AffiliatedFacetsWithUpdateTime affiliation) {
+ for (auto& facet : affiliation.facets)
+ facet.branding_info = FacetBrandingInfo();
+
+ return affiliation;
+ };
+
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ WithoutBrandingInfo(TestEquivalenceClass1()), affiliations[0]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ WithoutBrandingInfo(TestEquivalenceClass2()), affiliations[1]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(
+ WithoutBrandingInfo(TestEquivalenceClass3()), affiliations[2]);
+}
+
+TEST_F(AffiliationDatabaseTest, InitializeFromVersion2) {
+ // Close and delete the current database and create it from scratch with the
+ // SQLite statement stored in affiliation_db_v2.sql.
+ CloseDatabase();
+ AffiliationDatabase::Delete(db_path());
+ base::FilePath src_root_dir;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_root_dir));
+ base::FilePath sql_path_v2 = src_root_dir.AppendASCII("components")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("password_manager")
+ .AppendASCII("affiliation_db_v2.sql");
+ ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path(), sql_path_v2));
+
+ // Expect the migration to be a no-op that does not modify the existing data.
+ OpenDatabase();
+ std::vector<AffiliatedFacetsWithUpdateTime> affiliations;
+ db().GetAllAffiliationsAndBranding(&affiliations);
+ ASSERT_EQ(3u, affiliations.size());
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass1(),
+ affiliations[0]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass2(),
+ affiliations[1]);
+ ExpectEquivalenceClassesIncludingBrandingInfoAreEqual(TestEquivalenceClass3(),
+ affiliations[2]);
+ EXPECT_EQ(TestEquivalenceClass3().facets[0].branding_info,
+ affiliations[2].facets[0].branding_info);
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..9721b726b1b
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.cc
@@ -0,0 +1,144 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h"
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h"
+
+namespace password_manager {
+
+// static
+const net::BackoffEntry::Policy AffiliationFetchThrottler::kBackoffPolicy = {
+ // Number of initial errors (in sequence) to ignore before going into
+ // exponential backoff.
+ 0,
+
+ // Initial delay (in ms) once backoff starts.
+ 10 * 1000, // 10 seconds
+
+ // Factor by which the delay will be multiplied on each subsequent failure.
+ 4,
+
+ // Fuzzing percentage: 50% will spread delays randomly between 50%--100% of
+ // the nominal time.
+ .5, // 50%
+
+ // Maximum delay (in ms) during exponential backoff.
+ 6 * 3600 * 1000, // 6 hours
+
+ // Time to keep an entry from being discarded even when it has no
+ // significant state, -1 to never discard. (Not applicable.)
+ -1,
+
+ // False means that initial_delay_ms is the first delay once we start
+ // exponential backoff, i.e., there is no delay after subsequent successful
+ // requests.
+ false,
+};
+
+// static
+const int64_t AffiliationFetchThrottler::kGracePeriodAfterReconnectMs =
+ 10 * 1000; // 10 seconds
+
+AffiliationFetchThrottler::AffiliationFetchThrottler(
+ AffiliationFetchThrottlerDelegate* delegate,
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ base::TickClock* tick_clock)
+ : delegate_(delegate),
+ task_runner_(task_runner),
+ tick_clock_(tick_clock),
+ state_(IDLE),
+ has_network_connectivity_(false),
+ is_fetch_scheduled_(false),
+ exponential_backoff_(new net::BackoffEntry(&kBackoffPolicy, tick_clock_)),
+ weak_ptr_factory_(this) {
+ 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);
+ has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline();
+}
+
+AffiliationFetchThrottler::~AffiliationFetchThrottler() {
+ net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
+}
+
+void AffiliationFetchThrottler::SignalNetworkRequestNeeded() {
+ if (state_ != IDLE)
+ return;
+
+ state_ = FETCH_NEEDED;
+ if (has_network_connectivity_)
+ EnsureCallbackIsScheduled();
+}
+
+void AffiliationFetchThrottler::InformOfNetworkRequestComplete(bool success) {
+ DCHECK_EQ(state_, FETCH_IN_FLIGHT);
+ state_ = IDLE;
+ exponential_backoff_->InformOfRequest(success);
+}
+
+void AffiliationFetchThrottler::EnsureCallbackIsScheduled() {
+ DCHECK_EQ(state_, FETCH_NEEDED);
+ DCHECK(has_network_connectivity_);
+
+ if (is_fetch_scheduled_)
+ return;
+
+ is_fetch_scheduled_ = true;
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AffiliationFetchThrottler::OnBackoffDelayExpiredCallback,
+ weak_ptr_factory_.GetWeakPtr()),
+ exponential_backoff_->GetTimeUntilRelease());
+}
+
+void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() {
+ DCHECK_EQ(state_, FETCH_NEEDED);
+ DCHECK(is_fetch_scheduled_);
+ 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
+ // handler once again.
+ if (!has_network_connectivity_)
+ return;
+
+ // The release time might have been increased if network connectivity was lost
+ // and restored while this callback was in the task queue. If so, reschedule.
+ if (exponential_backoff_->ShouldRejectRequest())
+ EnsureCallbackIsScheduled();
+ else
+ state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE;
+}
+
+void AffiliationFetchThrottler::OnConnectionTypeChanged(
+ net::NetworkChangeNotifier::ConnectionType type) {
+ bool old_has_network_connectivity = has_network_connectivity_;
+ has_network_connectivity_ =
+ (type != net::NetworkChangeNotifier::CONNECTION_NONE);
+
+ // Only react when network connectivity has been reestablished.
+ if (!has_network_connectivity_ || old_has_network_connectivity)
+ return;
+
+ double grace_ms = kGracePeriodAfterReconnectMs *
+ (1 - base::RandDouble() * kBackoffPolicy.jitter_factor);
+ exponential_backoff_->SetCustomReleaseTime(std::max(
+ exponential_backoff_->GetReleaseTime(),
+ tick_clock_->NowTicks() + base::TimeDelta::FromMillisecondsD(grace_ms)));
+
+ if (state_ == FETCH_NEEDED)
+ EnsureCallbackIsScheduled();
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..d9a3cbadd95
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h
@@ -0,0 +1,135 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCH_THROTTLER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCH_THROTTLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/backoff_entry.h"
+#include "net/base/network_change_notifier.h"
+
+namespace base {
+class TickClock;
+class SequencedTaskRunner;
+} // namespace base
+
+namespace password_manager {
+
+class AffiliationFetchThrottlerDelegate;
+
+// Implements the throttling logic that the AffiliationBackend will use when it
+// needs to issue requests over the network to fetch affiliation information.
+//
+// This class manages only the scheduling of the requests. It is up to the
+// consumer (the AffiliationBackend) to actually assemble and send the requests,
+// to report back about their success or failure, and to retry them if desired.
+// The process goes like this:
+// 1.) The consumer calls SignalNetworkRequestNeeded().
+// 2.) Once appropriate, OnCanSendNetworkRequest() is called on the delegate.
+// 3.) The consumer sends the request, and waits until it completes.
+// 4.) The consumer calls InformOfNetworkRequestComplete().
+// Note that only a single request at a time is supported.
+//
+// If the request fails in Step 3, the consumer should not automatically retry
+// it. Instead it should always proceed to Step 4, and then -- if retrying the
+// request is desired -- proceed immediately to Step 1. That is, it should act
+// as if another request was needed right away.
+//
+// Essentially, this class implements exponential backoff in case of network and
+// server errors with the additional constraint that no requests will be issued
+// in the first place while there is known to be no network connectivity. This
+// prevents the exponential backoff delay from growing huge during long offline
+// periods, so that requests will not be held back for too long after
+// connectivity is restored.
+class AffiliationFetchThrottler
+ : public net::NetworkChangeNotifier::ConnectionTypeObserver {
+ 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
+ // |delegate| and |tick_clock| should outlive the throttler.
+ AffiliationFetchThrottler(
+ AffiliationFetchThrottlerDelegate* delegate,
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ base::TickClock* tick_clock);
+ ~AffiliationFetchThrottler() override;
+
+ // Signals to the throttling logic that a network request is needed, and that
+ // OnCanSendNetworkRequest() should be called as soon as the request can be
+ // sent. OnCanSendNetworkRequest() will always be called asynchronously.
+ //
+ // Calls to this method will be ignored when a request is already known to be
+ // needed or while a request is in flight. To signal that another request will
+ // be needed right away after the current one, call this method after calling
+ // InformOfNetworkRequestComplete().
+ virtual void SignalNetworkRequestNeeded();
+
+ // Informs the back-off logic that the in-flight network request has been
+ // completed, either with |success| or not.
+ virtual void InformOfNetworkRequestComplete(bool success);
+
+ protected:
+ AffiliationFetchThrottlerDelegate* delegate_;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FailedRequests);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ GracePeriodAfterConnectivityIsRestored);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ GracePeriodAfterConnectivityIsRestored2);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ GracePeriodAfterConnectivityIsRestored3);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ ConnectivityLostDuringBackoff);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ ConnectivityLostAndRestoredDuringBackoff);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FlakyConnectivity);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ ConnectivityLostDuringRequest);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ ConnectivityLostAndRestoredDuringRequest);
+ FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
+ ConnectivityLostAndRestoredDuringRequest2);
+
+ enum State { IDLE, FETCH_NEEDED, FETCH_IN_FLIGHT };
+
+ // Exponential backoff parameters in case of network and server errors
+ static const net::BackoffEntry::Policy kBackoffPolicy;
+
+ // Minimum delay before sending the first request once network connectivity is
+ // restored. The fuzzing factor in |kBackoffParameters.jitter_factor| applies.
+ static const int64_t kGracePeriodAfterReconnectMs;
+
+ // Ensures that OnBackoffDelayExpiredCallback() is scheduled to be called back
+ // once the |exponential_backoff_| delay expires.
+ void EnsureCallbackIsScheduled();
+
+ // Called back when the |exponential_backoff_| delay expires.
+ void OnBackoffDelayExpiredCallback();
+
+ // net::NetworkChangeNotifier::ConnectionTypeObserver:
+ void OnConnectionTypeChanged(
+ net::NetworkChangeNotifier::ConnectionType type) override;
+
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ base::TickClock* tick_clock_;
+ State state_;
+ bool has_network_connectivity_;
+ bool is_fetch_scheduled_;
+ std::unique_ptr<net::BackoffEntry> exponential_backoff_;
+
+ base::WeakPtrFactory<AffiliationFetchThrottler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottler);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCH_THROTTLER_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h
new file mode 100644
index 00000000000..36d00c9263c
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h
@@ -0,0 +1,29 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCH_THROTTLER_DELEGATE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCH_THROTTLER_DELEGATE_H_
+
+namespace password_manager {
+
+// An interface that users of AffiliationFetchThrottler need to implement to get
+// notified once it is okay to issue the next network request.
+class AffiliationFetchThrottlerDelegate {
+ public:
+ // Will be called once the throttling policy allows issuing a network request,
+ // provided SignalNetworkRequestNeeded() has been called at least once since
+ // the last request.
+ //
+ // The implementation must return true if a request was actually issued in
+ // response to this call, and then call InformOfNetworkRequestComplete() once
+ // the request is complete. Otherwise, the implementation must return false.
+ virtual bool OnCanSendNetworkRequest() = 0;
+
+ protected:
+ virtual ~AffiliationFetchThrottlerDelegate() {}
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCH_THROTTLER_DELEGATE_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc
new file mode 100644
index 00000000000..7def5f9828e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc
@@ -0,0 +1,420 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cmath>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/numerics/safe_math.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_delegate.h"
+#include "net/base/network_change_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+class MockAffiliationFetchThrottlerDelegate
+ : public AffiliationFetchThrottlerDelegate {
+ public:
+ // The |tick_clock| should outlive this instance.
+ explicit MockAffiliationFetchThrottlerDelegate(base::TickClock* tick_clock)
+ : tick_clock_(tick_clock),
+ emulated_return_value_(true),
+ can_send_count_(0u) {}
+ ~MockAffiliationFetchThrottlerDelegate() override {
+ EXPECT_EQ(0u, can_send_count_);
+ }
+
+ void set_emulated_return_value(bool value) { emulated_return_value_ = value; }
+ void reset_can_send_count() { can_send_count_ = 0u; }
+ size_t can_send_count() const { return can_send_count_; }
+ base::TimeTicks last_can_send_time() const { return last_can_send_time_; }
+
+ // AffiliationFetchThrottlerDelegate:
+ bool OnCanSendNetworkRequest() override {
+ ++can_send_count_;
+ last_can_send_time_ = tick_clock_->NowTicks();
+ return emulated_return_value_;
+ }
+
+ private:
+ base::TickClock* tick_clock_;
+ bool emulated_return_value_;
+ size_t can_send_count_;
+ base::TimeTicks last_can_send_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetchThrottlerDelegate);
+};
+
+} // namespace
+
+class AffiliationFetchThrottlerTest : public testing::Test {
+ public:
+ AffiliationFetchThrottlerTest()
+ : network_change_notifier_(net::NetworkChangeNotifier::CreateMock()),
+ task_runner_(new base::TestMockTimeTaskRunner),
+ mock_tick_clock_(task_runner_->GetMockTickClock()),
+ mock_delegate_(mock_tick_clock_.get()) {}
+ ~AffiliationFetchThrottlerTest() override {}
+
+ std::unique_ptr<AffiliationFetchThrottler> CreateThrottler() {
+ return base::MakeUnique<AffiliationFetchThrottler>(
+ &mock_delegate_, task_runner_, mock_tick_clock_.get());
+ }
+
+ void SimulateHasNetworkConnectivity(bool has_connectivity) {
+ net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
+ has_connectivity ? net::NetworkChangeNotifier::CONNECTION_UNKNOWN
+ : net::NetworkChangeNotifier::CONNECTION_NONE);
+ scoped_task_environment_.RunUntilIdle();
+ }
+
+ // Runs the task runner until no tasks remain, and asserts that by this time,
+ // OnCanSendNetworkRequest() will have been called exactly once, with a delay
+ // between |min_delay_ms| and |max_delay_ms|, modulo 0.5 ms to allow for
+ // floating point errors. When OnCanSendNetworkRequest() is called, the mock
+ // will return |emulated_return_value|. This value normally indicates whether
+ // or not a request was actually issued in response to the call.
+ void AssertReleaseInBetween(bool emulated_return_value,
+ double min_delay_ms,
+ double max_delay_ms) {
+ ASSERT_EQ(0u, mock_delegate_.can_send_count());
+ base::TimeTicks ticks_at_start = task_runner_->NowTicks();
+ mock_delegate_.set_emulated_return_value(emulated_return_value);
+ task_runner_->FastForwardUntilNoTasksRemain();
+ ASSERT_EQ(1u, mock_delegate_.can_send_count());
+ base::TimeDelta delay =
+ mock_delegate_.last_can_send_time() - ticks_at_start;
+ EXPECT_LE(min_delay_ms - 1, delay.InMillisecondsF());
+ EXPECT_GE(max_delay_ms + 1, delay.InMillisecondsF());
+ mock_delegate_.reset_can_send_count();
+ }
+
+ // Runs the task runner for |secs| and asserts that OnCanSendNetworkRequest()
+ // will not have been called by the end of this period.
+ void AssertNoReleaseForSecs(int64_t secs) {
+ task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(secs));
+ ASSERT_EQ(0u, mock_delegate_.can_send_count());
+ }
+
+ // Runs the task runner until no tasks remain, and asserts that
+ // OnCanSendNetworkRequest() will not have been called.
+ void AssertNoReleaseUntilNoTasksRemain() {
+ task_runner_->FastForwardUntilNoTasksRemain();
+ ASSERT_EQ(0u, mock_delegate_.can_send_count());
+ }
+
+ size_t GetPendingTaskCount() const {
+ return task_runner_->GetPendingTaskCount();
+ }
+
+ private:
+ // Needed because NetworkChangeNotifier uses base::ObserverList, which
+ // notifies observers on the sequence from which they have registered.
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+ std::unique_ptr<base::TickClock> mock_tick_clock_;
+ MockAffiliationFetchThrottlerDelegate mock_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottlerTest);
+};
+
+TEST_F(AffiliationFetchThrottlerTest, SuccessfulRequests) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+
+ // Signal while request is in flight should be ignored.
+ throttler->SignalNetworkRequestNeeded();
+ AssertNoReleaseUntilNoTasksRemain();
+ throttler->InformOfNetworkRequestComplete(true);
+ AssertNoReleaseUntilNoTasksRemain();
+
+ // Duplicate the second signal 3 times: still only 1 callback should arrive.
+ throttler->SignalNetworkRequestNeeded();
+ throttler->SignalNetworkRequestNeeded();
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+}
+
+TEST_F(AffiliationFetchThrottlerTest, FailedRequests) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+ throttler->InformOfNetworkRequestComplete(false);
+
+ // The request after the first failure should be delayed by |initial_delay_ms|
+ // spread out over Uniform(1 - |jitter_factor|, 1).
+ throttler->SignalNetworkRequestNeeded();
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kPolicy.initial_delay_ms * (1 - kPolicy.jitter_factor),
+ kPolicy.initial_delay_ms));
+ throttler->InformOfNetworkRequestComplete(true);
+
+ // After a successful request, the next one should be released immediately.
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+ throttler->InformOfNetworkRequestComplete(false);
+
+ // In general, the request after the n-th failure should be delayed by
+ // |multiply_factor| ^ (n-1) * |initial_delay_ms|,
+ // spread out over Uniform(1 - |jitter_factor|, 1), up until
+ // |maximum_backoff_ms|
+ // is reached.
+ for (int num_failures = 1; num_failures < 100; ++num_failures) {
+ throttler->SignalNetworkRequestNeeded();
+ double max_delay_ms = kPolicy.initial_delay_ms *
+ pow(kPolicy.multiply_factor, num_failures - 1);
+ double min_delay_ms = max_delay_ms * (1 - kPolicy.jitter_factor);
+ if (max_delay_ms > kPolicy.maximum_backoff_ms)
+ max_delay_ms = kPolicy.maximum_backoff_ms;
+ if (min_delay_ms > kPolicy.maximum_backoff_ms)
+ min_delay_ms = kPolicy.maximum_backoff_ms;
+ ASSERT_NO_FATAL_FAILURE(
+ AssertReleaseInBetween(true, min_delay_ms, max_delay_ms));
+ throttler->InformOfNetworkRequestComplete(false);
+ }
+}
+
+TEST_F(AffiliationFetchThrottlerTest, OnCanSendNetworkRequestReturnsFalse) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ // A need for a network request is signaled, but as OnCanSendNetworkRequest()
+ // is called, the implementation returns false to indicate that the request
+ // will not be needed after all. InformOfNetworkRequestComplete() must not be
+ // called in this case.
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(false, 0, 0));
+
+ // A subsequent signaling, however, should result in OnCanSendNetworkRequest()
+ // being called immediately.
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+}
+
+TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+ SimulateHasNetworkConnectivity(false);
+
+ // After connectivity is restored, the first request should be delayed by the
+ // grace period, spread out over Uniform(1 - |jitter_factor|, 1).
+ throttler->SignalNetworkRequestNeeded();
+ AssertNoReleaseUntilNoTasksRemain();
+
+ SimulateHasNetworkConnectivity(true);
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
+ throttler->InformOfNetworkRequestComplete(true);
+
+ // The next request should not be delayed.
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+}
+
+// Same as GracePeriodAfterConnectivityIsRestored, but the network comes back
+// just before SignalNetworkRequestNeeded() is called.
+TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored2) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+ SimulateHasNetworkConnectivity(false);
+
+ SimulateHasNetworkConnectivity(true);
+ throttler->SignalNetworkRequestNeeded();
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
+ throttler->InformOfNetworkRequestComplete(true);
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+}
+
+TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringBackoff) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+ throttler->InformOfNetworkRequestComplete(false);
+
+ throttler->SignalNetworkRequestNeeded();
+ SimulateHasNetworkConnectivity(false);
+
+ // Let the exponential backoff delay expire, and verify nothing happens.
+ AssertNoReleaseUntilNoTasksRemain();
+
+ // Verify that the request is, however, sent after the normal grace period
+ // once connectivity is restored.
+ SimulateHasNetworkConnectivity(true);
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
+ throttler->InformOfNetworkRequestComplete(true);
+}
+
+TEST_F(AffiliationFetchThrottlerTest,
+ ConnectivityLostAndRestoredDuringBackoff) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+ throttler->InformOfNetworkRequestComplete(false);
+
+ throttler->SignalNetworkRequestNeeded();
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kPolicy.initial_delay_ms * (1 - kPolicy.jitter_factor),
+ kPolicy.initial_delay_ms));
+ throttler->InformOfNetworkRequestComplete(false);
+
+ SimulateHasNetworkConnectivity(false);
+ SimulateHasNetworkConnectivity(true);
+
+ // This test expects that the exponential backoff interval after the 2nd error
+ // is larger than the normal grace period after connectivity is restored.
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ EXPECT_PRED_FORMAT2(testing::DoubleLE, kGraceMs,
+ kPolicy.initial_delay_ms * kPolicy.multiply_factor);
+
+ // The release should come after the longest of the two intervals expire.
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kPolicy.initial_delay_ms * kPolicy.multiply_factor *
+ (1 - kPolicy.jitter_factor),
+ kPolicy.initial_delay_ms * kPolicy.multiply_factor));
+ throttler->InformOfNetworkRequestComplete(false);
+}
+
+TEST_F(AffiliationFetchThrottlerTest, FlakyConnectivity) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+ throttler->InformOfNetworkRequestComplete(false);
+
+ // Run for a total of 5 grace periods and simulate connectivity being lost and
+ // restored every second. This verifies that a flaky connection will not flood
+ // the task queue with lots of of tasks and also that release will not happen
+ // while the connection is flaky even once the first grace period has expired.
+ throttler->SignalNetworkRequestNeeded();
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ int64_t five_grace_periods_secs =
+ kGraceMs * 5 / base::Time::kMillisecondsPerSecond;
+ for (int64_t t = 0; t < five_grace_periods_secs; ++t) {
+ SimulateHasNetworkConnectivity(false);
+ AssertNoReleaseForSecs(1);
+ SimulateHasNetworkConnectivity(true);
+ EXPECT_EQ(1u, GetPendingTaskCount());
+ }
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
+}
+
+TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringRequest) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+
+ SimulateHasNetworkConnectivity(false);
+ AssertNoReleaseUntilNoTasksRemain();
+ throttler->InformOfNetworkRequestComplete(false);
+ AssertNoReleaseUntilNoTasksRemain();
+ throttler->SignalNetworkRequestNeeded();
+ AssertNoReleaseUntilNoTasksRemain();
+
+ SimulateHasNetworkConnectivity(true);
+
+ // Verify that the next request is released after the normal grace period.
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
+ throttler->InformOfNetworkRequestComplete(true);
+}
+
+TEST_F(AffiliationFetchThrottlerTest,
+ ConnectivityLostAndRestoredDuringRequest) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+
+ SimulateHasNetworkConnectivity(false);
+ AssertNoReleaseUntilNoTasksRemain();
+ SimulateHasNetworkConnectivity(true);
+ AssertNoReleaseUntilNoTasksRemain();
+ throttler->InformOfNetworkRequestComplete(true);
+
+ // Even though the previous request succeeded, the next request should still
+ // be held back for the normal grace period after connection is restored.
+ throttler->SignalNetworkRequestNeeded();
+ const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy;
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
+ true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs));
+ throttler->InformOfNetworkRequestComplete(true);
+}
+
+TEST_F(AffiliationFetchThrottlerTest,
+ ConnectivityLostAndRestoredDuringRequest2) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+
+ SimulateHasNetworkConnectivity(false);
+ AssertNoReleaseUntilNoTasksRemain();
+ SimulateHasNetworkConnectivity(true);
+
+ const int64_t& kGraceMs =
+ AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
+ AssertNoReleaseForSecs(kGraceMs / base::Time::kMillisecondsPerSecond);
+ throttler->InformOfNetworkRequestComplete(true);
+
+ // The next request should not be held back.
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+}
+
+TEST_F(AffiliationFetchThrottlerTest, InstanceDestroyedWhileInBackoff) {
+ std::unique_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
+
+ throttler->SignalNetworkRequestNeeded();
+ ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
+ throttler->InformOfNetworkRequestComplete(false);
+
+ throttler->SignalNetworkRequestNeeded();
+ throttler.reset();
+ EXPECT_EQ(1u, GetPendingTaskCount());
+ AssertNoReleaseUntilNoTasksRemain();
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..7ecac02c139
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc
@@ -0,0 +1,264 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_fetcher.h"
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/load_flags.h"
+#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 "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.
+enum AffiliationFetchResult {
+ AFFILIATION_FETCH_RESULT_SUCCESS,
+ AFFILIATION_FETCH_RESULT_FAILURE,
+ AFFILIATION_FETCH_RESULT_MALFORMED,
+ 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) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
+ fetcher->GetResponseCode());
+ // Network error codes are negative. See: src/net/base/net_error_list.h.
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "PasswordManager.AffiliationFetcher.FetchErrorCode",
+ -fetcher->GetStatus().error());
+ }
+}
+
+} // namespace
+
+static TestAffiliationFetcherFactory* g_testing_factory = nullptr;
+
+AffiliationFetcher::AffiliationFetcher(
+ net::URLRequestContextGetter* request_context_getter,
+ const std::vector<FacetURI>& facet_uris,
+ AffiliationFetcherDelegate* delegate)
+ : request_context_getter_(request_context_getter),
+ requested_facet_uris_(facet_uris),
+ delegate_(delegate) {
+ for (const FacetURI& uri : requested_facet_uris_) {
+ DCHECK(uri.is_valid());
+ }
+}
+
+AffiliationFetcher::~AffiliationFetcher() {
+}
+
+// static
+AffiliationFetcher* AffiliationFetcher::Create(
+ net::URLRequestContextGetter* context_getter,
+ const std::vector<FacetURI>& facet_uris,
+ AffiliationFetcherDelegate* delegate) {
+ if (g_testing_factory) {
+ return g_testing_factory->CreateInstance(context_getter, facet_uris,
+ delegate);
+ }
+ return new AffiliationFetcher(context_getter, facet_uris, delegate);
+}
+
+// static
+void AffiliationFetcher::SetFactoryForTesting(
+ TestAffiliationFetcherFactory* factory) {
+ g_testing_factory = factory;
+}
+
+void AffiliationFetcher::StartRequest() {
+ DCHECK(!fetcher_);
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("affiliation_lookup", R"(
+ semantics {
+ sender: "Android Credentials Affiliation Fetcher"
+ description:
+ "Users syncing their passwords may have credentials stored for "
+ "Android apps. Unless synced data is encrypted with a custom "
+ "passphrase, this service downloads the associations between "
+ "Android apps and the corresponding websites. Thus, the Android "
+ "credentials can be used while browsing the web. "
+ trigger: "Periodically in the background."
+ data:
+ "List of Android apps the user has credentials for. The passwords "
+ "and usernames aren't sent."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: false
+ setting:
+ "Users can enable or disable this feature either by stoping "
+ "syncing passwords to Google (via unchecking 'Passwords' in "
+ "Chromium's settings under 'Sign In', 'Advanced sync settings') or "
+ "by introducing a custom passphrase to disable this service. The "
+ "feature is enabled by default."
+ chrome_policy {
+ SyncDisabled {
+ policy_options {mode: MANDATORY}
+ SyncDisabled: true
+ }
+ }
+ })");
+ 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();
+}
+
+GURL AffiliationFetcher::BuildQueryURL() const {
+ return net::AppendQueryParameter(
+ GURL("https://www.googleapis.com/affiliation/v1/affiliation:lookup"),
+ "key", google_apis::GetAPIKey());
+}
+
+std::string AffiliationFetcher::PreparePayload() const {
+ affiliation_pb::LookupAffiliationRequest lookup_request;
+ for (const FacetURI& uri : requested_facet_uris_)
+ lookup_request.add_facet(uri.canonical_spec());
+
+ // Enable request for branding information.
+ auto mask = base::MakeUnique<affiliation_pb::LookupAffiliationMask>();
+ mask->set_branding_info(true);
+ lookup_request.set_allocated_mask(mask.release());
+
+ std::string serialized_request;
+ bool success = lookup_request.SerializeToString(&serialized_request);
+ DCHECK(success);
+ return serialized_request;
+}
+
+bool AffiliationFetcher::ParseResponse(
+ 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
+ // validation and sanitization steps to make sure that the contract of
+ // AffiliationFetcherDelegate is fulfilled. Possible discrepancies are:
+ // * The server response will not have anything for facets that are not
+ // affiliated with any other facet, while |result| must have them.
+ // * The server response might contain future, unknown kinds of facet URIs,
+ // while |result| must contain only those that are FacetURI::is_valid().
+ // * The server response being ill-formed or self-inconsistent (in the sense
+ // that there are overlapping equivalence classes) is indicative of server
+ // side issues likely not remedied by re-fetching. Report failure in this
+ // 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;
+
+ result->reserve(requested_facet_uris_.size());
+
+ std::map<FacetURI, size_t> facet_uri_to_class_index;
+ for (int i = 0; i < response.affiliation_size(); ++i) {
+ const affiliation_pb::Affiliation& equivalence_class(
+ response.affiliation(i));
+
+ AffiliatedFacets affiliated_facets;
+ for (int j = 0; j < equivalence_class.facet_size(); ++j) {
+ const affiliation_pb::Facet& facet(equivalence_class.facet(j));
+ const std::string& uri_spec(facet.id());
+ FacetURI uri = FacetURI::FromPotentiallyInvalidSpec(uri_spec);
+ // Ignore potential future kinds of facet URIs (e.g. for new platforms).
+ if (!uri.is_valid())
+ continue;
+ affiliated_facets.push_back(
+ {uri, FacetBrandingInfo{facet.branding_info().name(),
+ GURL(facet.branding_info().icon_url())}});
+ }
+
+ // Be lenient and ignore empty (after filtering) equivalence classes.
+ if (affiliated_facets.empty())
+ continue;
+
+ // Ignore equivalence classes that are duplicates of earlier ones. However,
+ // fail in the case of a partial overlap, which violates the invariant that
+ // affiliations must form an equivalence relation.
+ for (const Facet& facet : affiliated_facets) {
+ if (!facet_uri_to_class_index.count(facet.uri))
+ facet_uri_to_class_index[facet.uri] = result->size();
+ if (facet_uri_to_class_index[facet.uri] !=
+ facet_uri_to_class_index[affiliated_facets[0].uri]) {
+ return false;
+ }
+ }
+
+ // Filter out duplicate equivalence classes in the response.
+ if (facet_uri_to_class_index[affiliated_facets[0].uri] == result->size())
+ result->push_back(affiliated_facets);
+ }
+
+ // Synthesize an equivalence class (of size one) for each facet that did not
+ // appear in the server response due to not being affiliated with any others.
+ for (const FacetURI& uri : requested_facet_uris_) {
+ if (!facet_uri_to_class_index.count(uri))
+ result->push_back({{uri}});
+ }
+
+ return true;
+}
+
+void AffiliationFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
+ DCHECK_EQ(source, fetcher_.get());
+
+ // 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);
+ delegate_->OnFetchSucceeded(std::move(result_data));
+ } else {
+ ReportStatistics(AFFILIATION_FETCH_RESULT_MALFORMED, nullptr);
+ delegate_->OnMalformedResponse();
+ }
+ } else {
+ ReportStatistics(AFFILIATION_FETCH_RESULT_FAILURE, fetcher_.get());
+ delegate_->OnFetchFailed();
+ }
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..2199744f6da
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h
@@ -0,0 +1,103 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#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 password_manager {
+
+class TestAffiliationFetcherFactory;
+
+// Fetches authoritative information regarding which facets are affiliated with
+// each other, that is, which facets belong to the same logical application.
+// See affiliation_utils.h for a definition of what this means.
+//
+// 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 {
+ public:
+ ~AffiliationFetcher() override;
+
+ // Constructs a fetcher to retrieve affiliations for each facet in |facet_ids|
+ // using the specified |request_context_getter|, and will provide the results
+ // to the |delegate| on the same thread that creates the instance.
+ static AffiliationFetcher* Create(
+ net::URLRequestContextGetter* request_context_getter,
+ const std::vector<FacetURI>& facet_uris,
+ AffiliationFetcherDelegate* delegate);
+
+ // Sets the |factory| to be used by Create() to construct AffiliationFetcher
+ // instances. To be used only for testing.
+ //
+ // The caller must ensure that the |factory| outlives all potential Create()
+ // calls. The caller may pass in NULL to resume using the default factory.
+ static void SetFactoryForTesting(TestAffiliationFetcherFactory* factory);
+
+ // Actually starts the request, and will call the delegate with the results on
+ // the same thread when done. If |this| is destroyed before completion, the
+ // in-flight request is cancelled, and the delegate will not be called.
+ // Further details:
+ // * 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();
+
+ const std::vector<FacetURI>& requested_facet_uris() const {
+ return requested_facet_uris_;
+ }
+
+ AffiliationFetcherDelegate* delegate() const { return delegate_; }
+
+ protected:
+ AffiliationFetcher(net::URLRequestContextGetter* request_context_getter,
+ 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;
+
+ // Parses and validates the response protocol buffer message for a list of
+ // equivalence classes, stores them into |result| and returns true on success.
+ // It is guaranteed that every one of the requested Facet URIs will be a
+ // 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;
+
+ // net::URLFetcherDelegate:
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ const scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ const std::vector<FacetURI> requested_facet_uris_;
+ AffiliationFetcherDelegate* const delegate_;
+
+ std::unique_ptr<net::URLFetcher> fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationFetcher);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_delegate.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_delegate.h
new file mode 100644
index 00000000000..d840ea05fe0
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_delegate.h
@@ -0,0 +1,46 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_DELEGATE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_DELEGATE_H_
+
+#include <memory>
+#include <vector>
+
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+
+namespace password_manager {
+
+// Interface that users of AffiliationFetcher should implement to get results of
+// the fetch. It is safe to destroy the fetcher in any of the event handlers.
+class AffiliationFetcherDelegate {
+ public:
+ // Encapsulates the response to an affiliations request.
+ typedef std::vector<AffiliatedFacets> Result;
+
+ // Called when affiliation information has been successfully retrieved. The
+ // |result| will contain at most as many equivalence class as facet URIs in
+ // the request, and each requested facet URI will appear in exactly one
+ // equivalence class.
+ virtual void OnFetchSucceeded(std::unique_ptr<Result> result) = 0;
+
+ // Called when affiliation information could not be fetched due to a network
+ // error or a presumably transient server error. The implementor may and will
+ // probably want to retry the request (once network connectivity is
+ // re-established, and/or with exponential back-off).
+ virtual void OnFetchFailed() = 0;
+
+ // Called when an affiliation response was received, but it was either gravely
+ // ill-formed or self-inconsistent. It is likely that a repeated fetch would
+ // yield the same, erroneous response, therefore, to avoid overloading the
+ // server, the fetch must not be repeated in the short run.
+ virtual void OnMalformedResponse() = 0;
+
+ protected:
+ virtual ~AffiliationFetcherDelegate() {}
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_DELEGATE_H_
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
new file mode 100644
index 00000000000..c7d1174a1dd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_unittest.cc
@@ -0,0 +1,383 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_fetcher.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/null_task_runner.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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace password_manager {
+
+namespace {
+
+const char kExampleAndroidFacetURI[] = "android://hash@com.example";
+const char kExampleAndroidPlayName[] = "Example Android App";
+const char kExampleAndroidIconURL[] = "https://example.com/icon.png";
+const char kExampleWebFacet1URI[] = "https://www.example.com";
+const char kExampleWebFacet2URI[] = "https://www.example.org";
+
+class MockAffiliationFetcherDelegate
+ : public testing::StrictMock<AffiliationFetcherDelegate> {
+ public:
+ MockAffiliationFetcherDelegate() {}
+ ~MockAffiliationFetcherDelegate() {}
+
+ MOCK_METHOD0(OnFetchSucceededProxy, void());
+ MOCK_METHOD0(OnFetchFailed, void());
+ MOCK_METHOD0(OnMalformedResponse, void());
+
+ void OnFetchSucceeded(std::unique_ptr<Result> result) override {
+ OnFetchSucceededProxy();
+ result_ = std::move(result);
+ }
+
+ const Result& result() const { return *result_.get(); }
+
+ private:
+ std::unique_ptr<Result> result_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetcherDelegate);
+};
+
+} // namespace
+
+class AffiliationFetcherTest : public testing::Test {
+ public:
+ AffiliationFetcherTest()
+ : request_context_getter_(new net::TestURLRequestContextGetter(
+ make_scoped_refptr(new base::NullTaskRunner))) {}
+
+ ~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()));
+
+ 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());
+ EXPECT_THAT(actual_uris,
+ testing::UnorderedElementsAreArray(expected_facet_uris));
+
+ // Verify that an affiliation mask is present that has branding_info set to
+ // true.
+ 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);
+
+ url_fetcher->set_response_code(200);
+ url_fetcher->SetResponseString(response);
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ }
+
+ 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 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);
+ }
+
+ net::TestURLRequestContextGetter* request_context_getter() {
+ return request_context_getter_.get();
+ }
+
+ private:
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+ net::TestURLFetcherFactory test_url_fetcher_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationFetcherTest);
+};
+
+TEST_F(AffiliationFetcherTest, BasicReqestAndResponse) {
+ const char kNotExampleAndroidFacetURI[] =
+ "android://hash1234@com.example.not";
+ const char kNotExampleWebFacetURI[] = "https://not.example.com";
+
+ affiliation_pb::LookupAffiliationResponse test_response;
+ affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
+ eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
+ eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
+ eq_class1->add_facet()->set_id(kExampleAndroidFacetURI);
+ affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
+ eq_class2->add_facet()->set_id(kNotExampleWebFacetURI);
+ eq_class2->add_facet()->set_id(kNotExampleAndroidFacetURI);
+
+ std::vector<FacetURI> requested_uris;
+ requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ requested_uris.push_back(
+ FacetURI::FromCanonicalSpec(kNotExampleAndroidFacetURI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), requested_uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ 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());
+ EXPECT_THAT(mock_delegate.result()[0],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet2URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)}));
+ EXPECT_THAT(
+ mock_delegate.result()[1],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kNotExampleWebFacetURI)},
+ Facet{FacetURI::FromCanonicalSpec(kNotExampleAndroidFacetURI)}));
+}
+
+TEST_F(AffiliationFetcherTest, AndroidBrandingInfoIsReturnedIfPresent) {
+ affiliation_pb::LookupAffiliationResponse test_response;
+ affiliation_pb::Affiliation* eq_class = test_response.add_affiliation();
+ eq_class->add_facet()->set_id(kExampleWebFacet1URI);
+ eq_class->add_facet()->set_id(kExampleWebFacet2URI);
+ auto android_branding_info = base::MakeUnique<affiliation_pb::BrandingInfo>();
+ android_branding_info->set_name(kExampleAndroidPlayName);
+ android_branding_info->set_icon_url(kExampleAndroidIconURL);
+ affiliation_pb::Facet* android_facet = eq_class->add_facet();
+ android_facet->set_id(kExampleAndroidFacetURI);
+ android_facet->set_allocated_branding_info(android_branding_info.release());
+
+ std::vector<FacetURI> requested_uris;
+ requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), requested_uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ 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());
+ EXPECT_THAT(mock_delegate.result()[0],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet2URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI),
+ FacetBrandingInfo{kExampleAndroidPlayName,
+ GURL(kExampleAndroidIconURL)}}));
+}
+
+// The API contract of this class is to return an equivalence class for all
+// requested facets; however, the server will not return anything for facets
+// that are not affiliated with any other facet. Make sure an equivalence class
+// of size one is created for each of the missing facets.
+TEST_F(AffiliationFetcherTest, MissingEquivalenceClassesAreCreated) {
+ affiliation_pb::LookupAffiliationResponse empty_test_response;
+
+ std::vector<FacetURI> requested_uris;
+ requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), requested_uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ 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());
+ EXPECT_THAT(mock_delegate.result()[0],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)}));
+}
+
+TEST_F(AffiliationFetcherTest, DuplicateEquivalenceClassesAreIgnored) {
+ affiliation_pb::LookupAffiliationResponse test_response;
+ affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
+ eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
+ eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
+ eq_class1->add_facet()->set_id(kExampleAndroidFacetURI);
+ affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
+ eq_class2->add_facet()->set_id(kExampleWebFacet2URI);
+ eq_class2->add_facet()->set_id(kExampleAndroidFacetURI);
+ eq_class2->add_facet()->set_id(kExampleWebFacet1URI);
+
+ std::vector<FacetURI> requested_uris;
+ requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), requested_uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ 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());
+ EXPECT_THAT(mock_delegate.result()[0],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet2URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)}));
+}
+
+TEST_F(AffiliationFetcherTest, EmptyEquivalenceClassesAreIgnored) {
+ affiliation_pb::LookupAffiliationResponse test_response;
+ affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
+ eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
+ // Empty class.
+ test_response.add_affiliation();
+
+ std::vector<FacetURI> requested_uris;
+ requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), requested_uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ 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());
+ EXPECT_THAT(mock_delegate.result()[0],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)}));
+}
+
+TEST_F(AffiliationFetcherTest, UnrecognizedFacetURIsAreIgnored) {
+ affiliation_pb::LookupAffiliationResponse test_response;
+ // Equivalence class having, alongside known facet URIs, a facet URI that
+ // corresponds to new platform unknown to this version.
+ affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
+ eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
+ eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
+ eq_class1->add_facet()->set_id(kExampleAndroidFacetURI);
+ eq_class1->add_facet()->set_id("new-platform://app-id-on-new-platform");
+ // Equivalence class consisting solely of an unknown facet URI.
+ affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
+ eq_class2->add_facet()->set_id("new-platform2://app2-id-on-new-platform2");
+
+ std::vector<FacetURI> requested_uris;
+ requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), requested_uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ 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());
+ EXPECT_THAT(mock_delegate.result()[0],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet1URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet2URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)}));
+}
+
+TEST_F(AffiliationFetcherTest, FailureBecauseResponseIsNotAProtobuf) {
+ const char kMalformedResponse[] = "This is not a protocol buffer!";
+
+ std::vector<FacetURI> uris;
+ uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ EXPECT_CALL(mock_delegate, OnMalformedResponse());
+ ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(kMalformedResponse));
+}
+
+// Partially overlapping equivalence classes violate the invariant that
+// affiliations must form an equivalence relation. Such a response is malformed.
+TEST_F(AffiliationFetcherTest,
+ FailureBecausePartiallyOverlappingEquivalenceClasses) {
+ affiliation_pb::LookupAffiliationResponse test_response;
+ affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
+ eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
+ eq_class1->add_facet()->set_id(kExampleWebFacet2URI);
+ affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
+ eq_class2->add_facet()->set_id(kExampleWebFacet1URI);
+ eq_class2->add_facet()->set_id(kExampleAndroidFacetURI);
+
+ std::vector<FacetURI> uris;
+ uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ EXPECT_CALL(mock_delegate, OnMalformedResponse());
+ ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
+}
+
+TEST_F(AffiliationFetcherTest, FailOnServerError) {
+ std::vector<FacetURI> uris;
+ uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ EXPECT_CALL(mock_delegate, OnFetchFailed());
+ ASSERT_NO_FATAL_FAILURE(SimulateServerError());
+}
+
+TEST_F(AffiliationFetcherTest, FailOnNetworkError) {
+ std::vector<FacetURI> uris;
+ uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+
+ MockAffiliationFetcherDelegate mock_delegate;
+ std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
+ request_context_getter(), uris, &mock_delegate));
+ fetcher->StartRequest();
+
+ EXPECT_CALL(mock_delegate, OnFetchFailed());
+ ASSERT_NO_FATAL_FAILURE(SimulateNetworkError());
+}
+
+} // 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
new file mode 100644
index 00000000000..620dbdac8fd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.cc
@@ -0,0 +1,94 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_service.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#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"
+
+namespace password_manager {
+
+AffiliationService::AffiliationService(
+ scoped_refptr<base::SequencedTaskRunner> backend_task_runner)
+ : backend_(nullptr),
+ backend_task_runner_(backend_task_runner),
+ weak_ptr_factory_(this) {}
+
+AffiliationService::~AffiliationService() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (backend_) {
+ backend_task_runner_->DeleteSoon(FROM_HERE, backend_);
+ backend_ = nullptr;
+ }
+}
+
+void AffiliationService::Initialize(
+ net::URLRequestContextGetter* request_context_getter,
+ const base::FilePath& db_path) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!backend_);
+ backend_ =
+ new AffiliationBackend(request_context_getter, backend_task_runner_,
+ base::WrapUnique(new base::DefaultClock),
+ base::WrapUnique(new base::DefaultTickClock));
+
+ std::unique_ptr<base::TickClock> tick_clock(new base::DefaultTickClock);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&AffiliationBackend::Initialize,
+ base::Unretained(backend_), db_path));
+}
+
+void AffiliationService::GetAffiliationsAndBranding(
+ const FacetURI& facet_uri,
+ StrategyOnCacheMiss cache_miss_strategy,
+ const ResultCallback& result_callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(backend_);
+ backend_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AffiliationBackend::GetAffiliationsAndBranding,
+ base::Unretained(backend_), facet_uri, cache_miss_strategy,
+ result_callback, base::SequencedTaskRunnerHandle::Get()));
+}
+
+void AffiliationService::Prefetch(const FacetURI& facet_uri,
+ const base::Time& keep_fresh_until) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(backend_);
+ backend_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AffiliationBackend::Prefetch, base::Unretained(backend_),
+ facet_uri, keep_fresh_until));
+}
+
+void AffiliationService::CancelPrefetch(const FacetURI& facet_uri,
+ const base::Time& keep_fresh_until) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(backend_);
+ backend_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AffiliationBackend::CancelPrefetch,
+ base::Unretained(backend_), facet_uri, keep_fresh_until));
+}
+
+void AffiliationService::TrimCacheForFacetURI(const FacetURI& facet_uri) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(backend_);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&AffiliationBackend::TrimCacheForFacetURI,
+ base::Unretained(backend_), facet_uri));
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..8c9d8e18032
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.h
@@ -0,0 +1,168 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_SERVICE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_SERVICE_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+} // namespace base
+
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+
+namespace password_manager {
+
+class AffiliationBackend;
+
+// A service that can be used to query the list of facets that are affiliated
+// with a given facet, i.e., facets that belong to the same logical application.
+// See affiliation_utils.h for details of what this means.
+//
+// The service must be accessed from the UI thread, and it can be utilized in
+// two ways:
+//
+// 1.) On-demand fetching: For the one-off query that wishes to learn
+// affiliations of facet X when (potentially) issuing an on-demand
+// network request to the Affiliation API containing the URI of facet X
+// is acceptable from the privacy and/or performance perspective.
+//
+// This mode of operation is achieved by invoking
+// GetAffiliationsAndBranding() with
+// StrategyOnCacheMiss::FETCH_OVER_NETWORK.
+//
+// 2.) Proactive fetching: For the compound query that is concerned with
+// checking, over time, whether or not each element in a sequence of
+// facets, W_1, W_2, ..., W_n, is affiliated with a fixed facet Y; and
+// when it is desired, for privacy and/or performance reasons, that only
+// facet Y be looked up against the Affiliation API and that subsequent
+// requests regarding each W_i not trigger additional requests.
+//
+// This mode of operation can be useful when, for example, the password
+// manager has credentials stored for facet Y and wishes to check, for
+// each visited web site W_i, whether these credentials should be offered
+// to be autofilled onto W_i.
+//
+// Example code:
+//
+// class ExampleAffiliatedCredentialFiller
+// : public base::SupportsWeakPtr<...> {
+// public:
+// ExampleAffiliatedCredentialFiller(AffiliationService* service,
+// const FacetURI& y)
+// : service_(service), y_(y) {
+// cancel_handle_ = service_->Prefetch(y_, base::Time::Max());
+// }
+//
+// ~ExampleAffiliatedCredentialFiller() { cancel_handle_.Run(); }
+//
+// void ShouldFillInto(const FacetURI& wi, FillDelegate* delegate) {
+// service_->GetAffiliationsAndBranding(wi, StrategyOnCacheMiss::FAIL,
+// base::Bind(
+// &ExampleAffiliatedCredentialFiller::OnAffiliationResult,
+// AsWeakPtr(),
+// delegate));
+// }
+//
+// void OnAffiliationResult(FillDelegate* delegate,
+// const AffiliatedFacets& results,
+// bool success) {
+// if (success && std::count(results.begin(), results.end(), y_))
+// delegate->FillCredentialsFor(y_);
+// }
+//
+// private:
+// AffiliationService* service_;
+// const FacetURI& y_;
+// CancelPrefetchingHandle cancel_handle_;
+// };
+class AffiliationService : public KeyedService {
+ public:
+ typedef base::Callback<void(const AffiliatedFacets& /* results */,
+ bool /* success */)> ResultCallback;
+
+ // Controls whether to send a network request or fail on a cache miss.
+ enum class StrategyOnCacheMiss { FETCH_OVER_NETWORK, FAIL };
+
+ // The |backend_task_runner| should be a task runner corresponding to a thread
+ // that can take blocking I/O, and is normally Chrome's DB thread.
+ explicit AffiliationService(
+ scoped_refptr<base::SequencedTaskRunner> backend_task_runner);
+ ~AffiliationService() override;
+
+ // 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);
+
+ // Looks up facets affiliated with the facet identified by |facet_uri| and
+ // branding information, and invokes |result_callback| with the results. It is
+ // guaranteed that the results will contain one facet with URI equal to
+ // |facet_uri| when |result_callback| is invoked with success set to true.
+ //
+ // If the local cache contains fresh affiliation and branding information for
+ // |facet_uri|, the request will be served from cache. Otherwise,
+ // |cache_miss_policy| controls whether to issue an on-demand network request,
+ // or to fail the request without fetching.
+ virtual void GetAffiliationsAndBranding(
+ const FacetURI& facet_uri,
+ StrategyOnCacheMiss cache_miss_strategy,
+ const ResultCallback& result_callback);
+
+ // Prefetches affiliation information for the facet identified by |facet_uri|,
+ // and keeps the information fresh by periodic re-fetches (as needed) until
+ // the clock strikes |keep_fresh_until| (exclusive), until a matching call to
+ // CancelPrefetch(), or until Chrome is shut down, whichever is sooner. It is
+ // a supported use-case to pass base::Time::Max() as |keep_fresh_until|.
+ //
+ // Canceling can be useful when a password is deleted, so that resources are
+ // no longer wasted on repeatedly refreshing affiliation information. Note
+ // that canceling will not blow away data already stored in the cache unless
+ // it becomes stale.
+ virtual void Prefetch(const FacetURI& facet_uri,
+ const base::Time& keep_fresh_until);
+
+ // Cancels the corresponding prefetch command, i.e., the one issued for the
+ // same |facet_uri| and with the same |keep_fresh_until|.
+ virtual void CancelPrefetch(const FacetURI& facet_uri,
+ const base::Time& keep_fresh_until);
+
+ // Wipes results of on-demand fetches and expired prefetches from the cache,
+ // but retains information corresponding to facets that are being kept fresh.
+ // As no required data is deleted, there will be no network requests directly
+ // triggered by this call. It will only potentially remove data
+ // corresponding to the given |facet_uri|, but still only as long as the
+ // data is no longer needed.
+ virtual void TrimCacheForFacetURI(const FacetURI& facet_uri);
+
+ private:
+ // The backend, owned by this AffiliationService instance, but living on the
+ // DB thread. It will be deleted asynchronously during shutdown on the DB
+ // thread, so it will outlive |this| along with all its in-flight tasks.
+ AffiliationBackend* backend_;
+
+ // TaskRunner to be used to run the |backend_|.
+ scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ base::WeakPtrFactory<AffiliationService> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationService);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_SERVICE_H_
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
new file mode 100644
index 00000000000..4b98c8f597e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service_unittest.cc
@@ -0,0 +1,167 @@
+// 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.
+
+// Note: This test focuses on functionality implemented in AffiliationService
+// itself. More thorough The AffiliationBackend is tested in-depth separarately.
+
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.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"
+#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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
+
+const char kTestFacetURIAlpha1[] = "https://one.alpha.example.com";
+const char kTestFacetURIAlpha2[] = "https://two.alpha.example.com";
+const char kTestFacetURIAlpha3[] = "https://three.alpha.example.com";
+const char kTestFacetURIBeta1[] = "https://one.beta.example.com";
+
+AffiliatedFacets GetTestEquivalenceClassAlpha() {
+ return {
+ {FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURIAlpha3)},
+ };
+}
+
+} // namespace
+
+class AffiliationServiceTest : public testing::Test {
+ public:
+ AffiliationServiceTest()
+ : main_task_runner_(new base::TestSimpleTaskRunner),
+ main_task_runner_handle_(main_task_runner_),
+ background_task_runner_(new base::TestMockTimeTaskRunner) {}
+ ~AffiliationServiceTest() override {}
+
+ protected:
+ void DestroyService() { service_.reset(); }
+
+ AffiliationService* service() { return service_.get(); }
+ MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
+
+ base::TestSimpleTaskRunner* main_task_runner() {
+ return main_task_runner_.get();
+ }
+
+ base::TestMockTimeTaskRunner* background_task_runner() {
+ return background_task_runner_.get();
+ }
+
+ ScopedFakeAffiliationAPI* fake_affiliation_api() {
+ return &fake_affiliation_api_;
+ }
+
+ private:
+ // testing::Test:
+ void SetUp() override {
+ base::FilePath database_path;
+ ASSERT_TRUE(CreateTemporaryFile(&database_path));
+ service_.reset(new AffiliationService(background_task_runner()));
+ service_->Initialize(nullptr, 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.
+ fake_affiliation_api_.AddTestEquivalenceClass(
+ GetTestEquivalenceClassAlpha());
+ }
+
+ void TearDown() override {
+ // The service uses DeleteSoon to asynchronously destroy its backend. Pump
+ // the background thread to make sure destruction actually takes place.
+ DestroyService();
+ background_task_runner_->RunUntilIdle();
+ }
+
+ scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
+ base::ThreadTaskRunnerHandle main_task_runner_handle_;
+ scoped_refptr<base::TestMockTimeTaskRunner> background_task_runner_;
+
+ ScopedFakeAffiliationAPI fake_affiliation_api_;
+ MockAffiliationConsumer mock_consumer_;
+
+ std::unique_ptr<AffiliationService> service_;
+
+ DISALLOW_COPY_AND_ASSIGN(AffiliationServiceTest);
+};
+
+TEST_F(AffiliationServiceTest, GetAffiliationsAndBranding) {
+ // The first request allows on-demand fetching, and should trigger a fetch.
+ // Then, it should succeed after the fetch is complete.
+ service()->GetAffiliationsAndBranding(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK,
+ mock_consumer()->GetResultCallback());
+
+ background_task_runner()->RunUntilIdle();
+ ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
+ fake_affiliation_api()->ServeNextRequest();
+
+ const auto equivalence_class_alpha(GetTestEquivalenceClassAlpha());
+ mock_consumer()->ExpectSuccessWithResult(equivalence_class_alpha);
+ EXPECT_THAT(
+ equivalence_class_alpha,
+ testing::Contains(testing::Field(
+ &Facet::uri, FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))));
+
+ main_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+
+ // The second request should be (and can be) served from cache.
+ service()->GetAffiliationsAndBranding(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ StrategyOnCacheMiss::FAIL, mock_consumer()->GetResultCallback());
+
+ background_task_runner()->RunUntilIdle();
+ ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
+
+ mock_consumer()->ExpectSuccessWithResult(equivalence_class_alpha);
+ main_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+
+ // The third request is also restricted to the cache, but cannot be served
+ // from cache, thus it should fail.
+ service()->GetAffiliationsAndBranding(
+ FacetURI::FromCanonicalSpec(kTestFacetURIBeta1),
+ StrategyOnCacheMiss::FAIL, mock_consumer()->GetResultCallback());
+
+ background_task_runner()->RunUntilIdle();
+ ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
+
+ mock_consumer()->ExpectFailure();
+ main_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+}
+
+TEST_F(AffiliationServiceTest, ShutdownWhileTasksArePosted) {
+ service()->GetAffiliationsAndBranding(
+ FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
+ StrategyOnCacheMiss::FETCH_OVER_NETWORK,
+ mock_consumer()->GetResultCallback());
+ EXPECT_TRUE(background_task_runner()->HasPendingTask());
+
+ DestroyService();
+ background_task_runner()->RunUntilIdle();
+
+ mock_consumer()->ExpectFailure();
+ main_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
new file mode 100644
index 00000000000..ce491749d48
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
@@ -0,0 +1,319 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include "base/base64.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "components/url_formatter/elide_url.h"
+#include "components/variations/variations_associated_data.h"
+#include "net/base/escape.h"
+#include "url/third_party/mozilla/url_parse.h"
+#include "url/url_canon_stdstring.h"
+
+namespace password_manager {
+
+namespace {
+
+// The scheme used for identifying Android applications.
+const char kAndroidAppScheme[] = "android";
+
+// Returns a StringPiece corresponding to |component| in |uri|, or the empty
+// string in case there is no such component.
+base::StringPiece ComponentString(const std::string& uri,
+ const url::Component& component) {
+ if (!component.is_valid())
+ return base::StringPiece();
+ return base::StringPiece(uri.c_str() + component.begin, component.len);
+}
+
+// Returns true if the passed ASCII |input| string contains nothing else than
+// alphanumeric characters and those in |other_characters|.
+bool ContainsOnlyAlphanumericAnd(const base::StringPiece& input,
+ const base::StringPiece& other_characters) {
+ for (char c : input) {
+ if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) &&
+ other_characters.find(c) == base::StringPiece::npos)
+ return false;
+ }
+ return true;
+}
+
+// Canonicalizes a Web facet URI, and returns true if canonicalization was
+// successful and produced a valid URI.
+bool CanonicalizeWebFacetURI(const std::string& input_uri,
+ const url::Parsed& input_parsed,
+ std::string* canonical_uri) {
+ url::Parsed canonical_parsed;
+ url::StdStringCanonOutput canonical_output(canonical_uri);
+
+ bool canonicalization_succeeded = url::CanonicalizeStandardURL(
+ input_uri.c_str(), input_uri.size(), input_parsed, nullptr,
+ &canonical_output, &canonical_parsed);
+ canonical_output.Complete();
+
+ if (canonicalization_succeeded && canonical_parsed.host.is_nonempty() &&
+ !canonical_parsed.username.is_valid() &&
+ !canonical_parsed.password.is_valid() &&
+ ComponentString(*canonical_uri, canonical_parsed.path) == "/" &&
+ !canonical_parsed.query.is_valid() && !canonical_parsed.ref.is_valid()) {
+ // Get rid of the trailing slash added by url::CanonicalizeStandardURL().
+ DCHECK_EQ((size_t)canonical_parsed.path.begin, canonical_uri->size() - 1);
+ canonical_uri->erase(canonical_parsed.path.begin,
+ canonical_parsed.path.len);
+ return true;
+ }
+ return false;
+}
+
+// Adds padding until the length of the base64-encoded |data| becomes a multiple
+// of 4, and returns true if the thusly obtained |data| is now correctly padded,
+// i.e., there are at most 2 padding characters ('=') at the very end.
+bool CanonicalizeBase64Padding(std::string* data) {
+ while (data->size() % 4u != 0u)
+ data->push_back('=');
+
+ size_t first_padding = data->find_first_of('=');
+ return first_padding == std::string::npos ||
+ (data->size() - first_padding <= 2u &&
+ data->find_first_not_of('=', first_padding) == std::string::npos);
+}
+
+// Canonicalizes the username component in an Android facet URI (containing the
+// certificate hash), and returns true if canonicalization was successful and
+// produced a valid non-empty component.
+bool CanonicalizeHashComponent(const base::StringPiece& input_hash,
+ url::CanonOutput* canonical_output) {
+ // Characters other than alphanumeric that are used in the "URL and filename
+ // safe" base64 alphabet; plus the padding ('=').
+ const char kBase64NonAlphanumericChars[] = "-_=";
+
+ // We need net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS to
+ // unescape the padding ('=').
+ std::string base64_encoded_hash = net::UnescapeURLComponent(
+ input_hash.as_string(),
+ net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
+
+ if (!base64_encoded_hash.empty() &&
+ CanonicalizeBase64Padding(&base64_encoded_hash) &&
+ ContainsOnlyAlphanumericAnd(base64_encoded_hash,
+ kBase64NonAlphanumericChars)) {
+ canonical_output->Append(base64_encoded_hash.data(),
+ base64_encoded_hash.size());
+ canonical_output->push_back('@');
+ return true;
+ }
+ return false;
+}
+
+// Canonicalizes the host component in an Android facet URI (containing the
+// package name), and returns true if canonicalization was successful and
+// produced a valid non-empty component.
+bool CanonicalizePackageNameComponent(
+ const base::StringPiece& input_package_name,
+ url::CanonOutput* canonical_output) {
+ // Characters other than alphanumeric that are permitted in the package names.
+ const char kPackageNameNonAlphanumericChars[] = "._";
+
+ std::string package_name = net::UnescapeURLComponent(
+ input_package_name.as_string(), net::UnescapeRule::NORMAL);
+
+ // TODO(engedy): We might want to use a regex to check this more throughly.
+ if (!package_name.empty() &&
+ ContainsOnlyAlphanumericAnd(package_name,
+ kPackageNameNonAlphanumericChars)) {
+ canonical_output->Append(package_name.data(), package_name.size());
+ return true;
+ }
+ return false;
+}
+
+// Canonicalizes an Android facet URI, and returns true if canonicalization was
+// successful and produced a valid URI.
+bool CanonicalizeAndroidFacetURI(const std::string& input_uri,
+ const url::Parsed& input_parsed,
+ std::string* canonical_uri) {
+ url::StdStringCanonOutput canonical_output(canonical_uri);
+
+ url::Component unused;
+ bool success = url::CanonicalizeScheme(
+ input_uri.c_str(), input_parsed.scheme, &canonical_output, &unused);
+
+ canonical_output.push_back('/');
+ canonical_output.push_back('/');
+
+ // We cannot use url::CanonicalizeUserInfo as that would percent encode the
+ // base64 padding characters ('=').
+ success &= CanonicalizeHashComponent(
+ ComponentString(input_uri, input_parsed.username), &canonical_output);
+
+ // We cannot use url::CanonicalizeHost as that would convert the package name
+ // to lower case, but the package name is case sensitive.
+ success &= CanonicalizePackageNameComponent(
+ ComponentString(input_uri.data(), input_parsed.host), &canonical_output);
+
+ canonical_output.Complete();
+
+ return success && !input_parsed.password.is_nonempty() &&
+ (!input_parsed.path.is_nonempty() ||
+ ComponentString(input_uri, input_parsed.path) == "/") &&
+ !input_parsed.port.is_nonempty() && !input_parsed.query.is_valid() &&
+ !input_parsed.ref.is_valid();
+}
+
+// Computes the canonicalized form of |uri| into |canonical_uri|, and returns
+// true if canonicalization was successful and produced a valid URI.
+bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
+ std::string* canonical_uri) {
+ DCHECK(canonical_uri);
+ canonical_uri->clear();
+ canonical_uri->reserve(input_uri.size() + 32);
+
+ url::Parsed input_parsed;
+ url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
+
+ base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
+ if (base::LowerCaseEqualsASCII(scheme, url::kHttpsScheme)) {
+ return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
+ } else if (base::LowerCaseEqualsASCII(scheme, kAndroidAppScheme)) {
+ return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
+ }
+ return false;
+}
+
+// Extracts and sorts the facet URIs of the given affiliated facets. This is
+// used to determine whether two equivalence classes are equal.
+std::vector<FacetURI> ExtractAndSortFacetURIs(const AffiliatedFacets& facets) {
+ std::vector<FacetURI> uris;
+ uris.reserve(facets.size());
+ std::transform(facets.begin(), facets.end(), std::back_inserter(uris),
+ [](const Facet& facet) { return facet.uri; });
+ std::sort(uris.begin(), uris.end());
+ return uris;
+}
+
+} // namespace
+
+
+// FacetURI -------------------------------------------------------------------
+
+FacetURI::FacetURI() : is_valid_(false) {
+}
+
+// static
+FacetURI FacetURI::FromPotentiallyInvalidSpec(const std::string& spec) {
+ std::string canonical_spec;
+ bool is_valid = ParseAndCanonicalizeFacetURI(spec, &canonical_spec);
+ return FacetURI(canonical_spec, is_valid);
+}
+
+// static
+FacetURI FacetURI::FromCanonicalSpec(const std::string& canonical_spec) {
+ return FacetURI(canonical_spec, true);
+}
+
+bool FacetURI::operator==(const FacetURI& other) const {
+ DCHECK(is_empty() || is_valid());
+ DCHECK(other.is_empty() || other.is_valid());
+ return canonical_spec_ == other.canonical_spec_;
+}
+
+bool FacetURI::operator!=(const FacetURI& other) const {
+ DCHECK(is_empty() || is_valid());
+ DCHECK(other.is_empty() || other.is_valid());
+ return canonical_spec_ != other.canonical_spec_;
+}
+
+bool FacetURI::operator<(const FacetURI& other) const {
+ DCHECK(is_empty() || is_valid());
+ DCHECK(other.is_empty() || other.is_valid());
+ return canonical_spec_ < other.canonical_spec_;
+}
+
+bool FacetURI::operator>(const FacetURI& other) const {
+ DCHECK(is_empty() || is_valid());
+ DCHECK(other.is_empty() || other.is_valid());
+ return canonical_spec_ > other.canonical_spec_;
+}
+
+bool FacetURI::IsValidWebFacetURI() const {
+ return scheme() == url::kHttpsScheme;
+}
+
+bool FacetURI::IsValidAndroidFacetURI() const {
+ return scheme() == kAndroidAppScheme;
+}
+
+std::string FacetURI::scheme() const {
+ return is_valid()
+ ? ComponentString(canonical_spec_, parsed_.scheme).as_string()
+ : "";
+}
+
+std::string FacetURI::android_package_name() const {
+ if (!IsValidAndroidFacetURI())
+ return "";
+ return ComponentString(canonical_spec_, parsed_.host).as_string();
+}
+
+FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
+ : is_valid_(is_valid), canonical_spec_(canonical_spec) {
+ // TODO(engedy): Refactor code in order to avoid to avoid parsing the URL
+ // twice.
+ url::ParseStandardURL(canonical_spec_.c_str(), canonical_spec_.size(),
+ &parsed_);
+}
+
+
+// AffiliatedFacetsWithUpdateTime ---------------------------------------------
+
+AffiliatedFacetsWithUpdateTime::AffiliatedFacetsWithUpdateTime() {
+}
+
+AffiliatedFacetsWithUpdateTime::AffiliatedFacetsWithUpdateTime(
+ const AffiliatedFacetsWithUpdateTime& other) = default;
+
+AffiliatedFacetsWithUpdateTime::~AffiliatedFacetsWithUpdateTime() {
+}
+
+
+// Helpers --------------------------------------------------------------------
+
+std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri) {
+ return os << facet_uri.potentially_invalid_spec();
+}
+
+bool operator==(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs) {
+ return std::tie(lhs.name, lhs.icon_url) == std::tie(rhs.name, rhs.icon_url);
+}
+
+bool operator!=(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs) {
+ return !(lhs == rhs);
+}
+
+bool operator==(const Facet& lhs, const Facet& rhs) {
+ return std::tie(lhs.uri, lhs.branding_info) ==
+ std::tie(rhs.uri, rhs.branding_info);
+}
+
+bool operator!=(const Facet& lhs, const Facet& rhs) {
+ return !(lhs == rhs);
+}
+
+bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
+ const AffiliatedFacets& b) {
+ return a.size() == b.size() &&
+ ExtractAndSortFacetURIs(a) == ExtractAndSortFacetURIs(b);
+}
+
+bool IsValidAndroidFacetURI(const std::string& url) {
+ FacetURI facet = FacetURI::FromPotentiallyInvalidSpec(url);
+ return facet.IsValidAndroidFacetURI();
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.h
new file mode 100644
index 00000000000..392bfe54225
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.h
@@ -0,0 +1,222 @@
+// 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.
+
+// This file contains utilities related to working with "facets".
+//
+// A "facet" is defined as the manifestation of a logical application on a given
+// platform. For example, "My Bank" may have released an Android application
+// and a Web application accessible from a browser. These are all facets of the
+// "My Bank" logical application.
+//
+// Facets that belong to the same logical application are said to be affiliated
+// with each other. Conceptually, "affiliations" can be seen as an equivalence
+// relation defined over the set of all facets. Each equivalence class contains
+// facets that belong to the same logical application, and therefore should be
+// treated as synonymous for certain purposes, e.g., sharing credentials.
+//
+// A valid facet identifier will be a URI of the form:
+//
+// * https://<host>[:<port>]
+//
+// For web sites. Only HTTPS sites are supported. The syntax corresponds to
+// that of 'serialized-origin' in RFC 6454. That is, in canonical form, the
+// URI must not contain components other than the scheme (required, must be
+// "https"), host (required), and port (optional); with canonicalization
+// performed the same way as it normally would be for standard URLs.
+//
+// * android://<certificate_hash>@<package_name>
+//
+// For Android applications. In canonical form, the URI must not contain
+// components other than the scheme (must be "android"), username, and host
+// (all required). The host part must be a valid Android package name, with
+// no escaping, so it must be composed of characters [a-zA-Z0-9_.].
+//
+// The username part must be the hash of the certificate used to sign the
+// APK, base64-encoded using padding and the "URL and filename safe" base64
+// alphabet, with no further escaping. This is normally calculated as:
+//
+// echo -n -e "$PEM_KEY" |
+// openssl x509 -outform DER |
+// openssl sha -sha512 -binary | base64 | tr '+/' '-_'
+//
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_UTILS_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_UTILS_H_
+
+#include <cstddef>
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+#include "url/third_party/mozilla/url_parse.h"
+
+namespace password_manager {
+
+// Encapsulates a facet URI in canonical form.
+//
+// This is a very light-weight wrapper around an std::string containing the text
+// of the URI, and can be passed around as a value. The main rationale for the
+// existence of this class is to make it clearer in the code when a certain URI
+// is known to be a valid facet URI in canonical form, and to allow verifying
+// and converting URIs to such canonical form.
+//
+// Note that it would be impractical to use GURL to represent facet URIs, as
+// GURL has built-in logic to parse the rest of the URI according to its scheme,
+// and obviously, it does not recognize the "android" scheme. Therefore, after
+// parsing, everything ends up in the path component, which is not too helpful.
+class FacetURI {
+ public:
+ FacetURI();
+
+ // As a light-weight std::string wrapper, allow copy and assign.
+ FacetURI(const FacetURI&) = default;
+ FacetURI& operator=(const FacetURI&) = default;
+
+ // Constructs an instance to encapsulate the canonical form of |spec|.
+ // If |spec| is not a valid facet URI, then an invalid instance is returned,
+ // which then should be discarded.
+ static FacetURI FromPotentiallyInvalidSpec(const std::string& spec);
+
+ // Constructs a valid FacetURI instance from a valid |canonical_spec|.
+ // Note: The passed-in URI is not verified at all. Use only when you are sure
+ // the URI is valid and in canonical form.
+ static FacetURI FromCanonicalSpec(const std::string& canonical_spec);
+
+ // Comparison operators so that FacetURI can be used in std::equal.
+ bool operator==(const FacetURI& other) const;
+ bool operator!=(const FacetURI& other) const;
+
+ // Relational operators so that FacetURI can be used in sorted containers.
+ bool operator<(const FacetURI& other) const;
+ bool operator>(const FacetURI& other) const;
+
+ // Returns whether or not this instance represents a valid facet identifier
+ // referring to a Web application.
+ bool IsValidWebFacetURI() const;
+
+ // Returns whether or not this instance represents a valid facet identifier
+ // referring to an Android application.
+ bool IsValidAndroidFacetURI() const;
+
+ // Returns whether or not this instance represents a valid facet identifier
+ // referring to either a Web or an Android application. The empty identfier is
+ // not considered valid.
+ bool is_valid() const { return is_valid_; }
+
+ // Returns whether or not this instance represents the empty facet identifier.
+ bool is_empty() const { return canonical_spec_.empty(); }
+
+ // Returns the canonical scheme of the encapsulated facet URI, provided it is
+ // valid, or the empty string otherwise.
+ std::string scheme() const;
+
+ // Returns the canonical package name that the encapsulated facet URI
+ // references, provided it is a valid Android facet URI, or the empty string
+ // otherwise.
+ std::string android_package_name() const;
+
+ // Returns the text of the encapsulated canonical URI, which must be valid.
+ const std::string& canonical_spec() const {
+ DCHECK(is_valid_);
+ return canonical_spec_;
+ }
+
+ // Returns the text of the encapsulated canonical URI, even if it is invalid.
+ const std::string& potentially_invalid_spec() const {
+ return canonical_spec_;
+ }
+
+ private:
+ // Internal constructor to be used by the static factory methods.
+ FacetURI(const std::string& canonical_spec, bool is_valid);
+
+ // Whether |canonical_spec_| contains a valid facet URI in canonical form.
+ bool is_valid_;
+
+ // The text of the encapsulated canonical URI, valid if and only if
+ // |is_valid_| is true.
+ std::string canonical_spec_;
+
+ // Identified components of the canonical spec.
+ url::Parsed parsed_;
+};
+
+// The branding information for a given facet. Corresponds to the |BrandingInfo|
+// message in affiliation_api.proto.
+struct FacetBrandingInfo {
+ // Human readable name of this facet, or empty if this information is not
+ // available.
+ //
+ // For example, this would be something like "Netflix" for the popular
+ // video-on-demand application corresponding to FacetURIs
+ // `android://...@com.netflix.mediaclient` and `https://netflix.com`.
+ std::string name;
+
+ // URL of the icon of this facet, or empty if this information is not
+ // available.
+ //
+ // For example, this would be something like
+ // "https://lh3.googleusercontent.com/..." for the popular video-on-demand
+ // application corresponding to FacetURIs
+ // `android://...@com.netflix.mediaclient` and `https://netflix.com`.
+ GURL icon_url;
+};
+
+// Facet struct, corresponds to the |Facet| message in affiliation_api.proto.
+struct Facet {
+ FacetURI uri;
+ FacetBrandingInfo branding_info;
+};
+
+// A collection of facets affiliated with each other, i.e. an equivalence class.
+typedef std::vector<Facet> AffiliatedFacets;
+
+// A collection of facets affiliated with each other, i.e. an equivalence class,
+// plus a timestamp that indicates the last time the data was updated from an
+// authoritative source.
+struct AffiliatedFacetsWithUpdateTime {
+ AffiliatedFacetsWithUpdateTime();
+ AffiliatedFacetsWithUpdateTime(const AffiliatedFacetsWithUpdateTime& other);
+ ~AffiliatedFacetsWithUpdateTime();
+
+ AffiliatedFacets facets;
+ base::Time last_update_time;
+};
+
+// Returns whether or not equivalence classes |a| and |b| are equal, that is,
+// whether or not they consist of the same set of facet URIs. Note that branding
+// information is ignored for this check.
+//
+// Note that this will do some sorting, so it can be expensive for large inputs.
+bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
+ const AffiliatedFacets& b);
+
+// A shorter way to spell FacetURI::IsValidAndroidFacetURI().
+bool IsValidAndroidFacetURI(const std::string& uri);
+
+// For logging use only.
+std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri);
+
+// Needed for testing.
+bool operator==(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs);
+bool operator!=(const FacetBrandingInfo& lhs, const FacetBrandingInfo& rhs);
+
+// Needed for testing.
+bool operator==(const Facet& lhs, const Facet& rhs);
+bool operator!=(const Facet& lhs, const Facet& rhs);
+
+struct FacetURIHash {
+ size_t operator()(const FacetURI& facet_uri) const {
+ return std::hash<std::string>()(facet_uri.potentially_invalid_spec());
+ }
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_UTILS_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils_unittest.cc
new file mode 100644
index 00000000000..5cd22af5efd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils_unittest.cc
@@ -0,0 +1,242 @@
+// 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/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/variations/variations_associated_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/url_constants.h"
+
+namespace password_manager {
+
+namespace {
+const char kTestFacetURI1[] = "https://alpha.example.com/";
+const char kTestFacetURI2[] = "https://beta.example.com/";
+const char kTestFacetURI3[] = "https://gamma.example.com/";
+} // namespace
+
+TEST(AffiliationUtilsTest, FacetBrandingInfoOperatorEq) {
+ FacetBrandingInfo facet_1 = {"First Facet", GURL()};
+ FacetBrandingInfo facet_1_with_icon = {
+ "First Facet", GURL("https://example.com/icon_1.png")};
+ FacetBrandingInfo facet_2 = {"Second Facet", GURL()};
+ FacetBrandingInfo facet_2_with_icon = {
+ "Second Facet", GURL("https://example.com/icon_2.png")};
+
+ EXPECT_EQ(facet_1, facet_1);
+ EXPECT_NE(facet_1, facet_1_with_icon);
+ EXPECT_NE(facet_1, facet_2);
+ EXPECT_NE(facet_1, facet_2_with_icon);
+
+ EXPECT_NE(facet_1_with_icon, facet_1);
+ EXPECT_EQ(facet_1_with_icon, facet_1_with_icon);
+ EXPECT_NE(facet_1_with_icon, facet_2);
+ EXPECT_NE(facet_1_with_icon, facet_2_with_icon);
+
+ EXPECT_NE(facet_2, facet_1);
+ EXPECT_NE(facet_2, facet_1_with_icon);
+ EXPECT_EQ(facet_2, facet_2);
+ EXPECT_NE(facet_2, facet_2_with_icon);
+
+ EXPECT_NE(facet_2_with_icon, facet_1);
+ EXPECT_NE(facet_2_with_icon, facet_1_with_icon);
+ EXPECT_NE(facet_2_with_icon, facet_2);
+ EXPECT_EQ(facet_2_with_icon, facet_2_with_icon);
+}
+
+TEST(AffiliationUtilsTest, FacetOperatorEq) {
+ Facet facet_1 = {FacetURI::FromPotentiallyInvalidSpec(kTestFacetURI1)};
+ Facet facet_2 = {FacetURI::FromPotentiallyInvalidSpec(kTestFacetURI2)};
+ EXPECT_EQ(facet_1, facet_1);
+ EXPECT_NE(facet_1, facet_2);
+ EXPECT_NE(facet_2, facet_1);
+ EXPECT_EQ(facet_2, facet_2);
+}
+
+TEST(AffiliationUtilsTest, ValidWebFacetURIs) {
+ struct {
+ const char* valid_facet_uri;
+ const char* expected_canonical_facet_uri;
+ } kTestCases[] = {
+ {"https://www.example.com", "https://www.example.com"},
+ {"HTTPS://www.EXAMPLE.com", "https://www.example.com"},
+ {"https://0321.0x86.161.0043", "https://209.134.161.35"},
+ {"https://www.%65xample.com", "https://www.example.com"},
+ {"https://sz\xc3\xb3t\xc3\xa1r.example.com",
+ "https://xn--sztr-7na0i.example.com"},
+ {"https://www.example.com/", "https://www.example.com"},
+ {"https://www.example.com:", "https://www.example.com"},
+ {"https://@www.example.com", "https://www.example.com"},
+ {"https://:@www.example.com", "https://www.example.com"},
+ {"https://www.example.com:8080", "https://www.example.com:8080"},
+ {"https://new-gtld", "https://new-gtld"}};
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(testing::Message("URI = ") << test_case.valid_facet_uri);
+ FacetURI facet_uri =
+ FacetURI::FromPotentiallyInvalidSpec(test_case.valid_facet_uri);
+ ASSERT_TRUE(facet_uri.IsValidWebFacetURI());
+ EXPECT_EQ(std::string(test_case.expected_canonical_facet_uri),
+ facet_uri.canonical_spec());
+ EXPECT_EQ(url::kHttpsScheme, facet_uri.scheme());
+ EXPECT_EQ("", facet_uri.android_package_name());
+ }
+}
+
+TEST(AffiliationUtilsTest, InvalidWebFacetURIs) {
+ const char* kInvalidFacetURIs[]{
+ // Invalid URL (actually, will be treated as having only a host part).
+ "Does not look like a valid URL.",
+ // Path is more than just the root path ('/').
+ "https://www.example.com/path",
+ // Empty scheme and not HTTPS scheme.
+ "://www.example.com",
+ "http://www.example.com/",
+ // Forbidden non-empty components.
+ "https://user@www.example.com/",
+ "https://:password@www.example.com/",
+ "https://www.example.com/?",
+ "https://www.example.com/?query",
+ "https://www.example.com/#",
+ "https://www.example.com/#ref",
+ // Valid Android facet URI.
+ "android://hash@com.example.android"};
+ for (const char* uri : kInvalidFacetURIs) {
+ SCOPED_TRACE(testing::Message("URI = ") << uri);
+ FacetURI facet_uri = FacetURI::FromPotentiallyInvalidSpec(uri);
+ EXPECT_FALSE(facet_uri.IsValidWebFacetURI());
+ }
+}
+
+TEST(AffiliationUtilsTest, ValidAndroidFacetURIs) {
+ struct {
+ const char* valid_facet_uri;
+ const char* expected_canonical_facet_uri;
+ const char* expected_package_name;
+ } kTestCases[] = {
+ {"android://"
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
+ "@com.example.android",
+ "android://"
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
+ "@com.example.android",
+ "com.example.android"},
+ {"ANDROID://"
+ "hash@abcdefghijklmnopqrstuvwxyz_0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "android://"
+ "hash@abcdefghijklmnopqrstuvwxyz_0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "abcdefghijklmnopqrstuvwxyz_0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ"},
+ {"android://needpadding@com.example.android",
+ "android://needpadding=@com.example.android",
+ "com.example.android"},
+ {"android://needtounescape%3D%3D@com.%65xample.android",
+ "android://needtounescape==@com.example.android",
+ "com.example.android"},
+ {"ANDROID://hash@com.example.android",
+ "android://hash@com.example.android",
+ "com.example.android"},
+ {"android://hash@com.example.android/",
+ "android://hash@com.example.android",
+ "com.example.android"},
+ {"android://hash:@com.example.android",
+ "android://hash@com.example.android",
+ "com.example.android"}};
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(testing::Message("URI = ") << test_case.valid_facet_uri);
+ FacetURI facet_uri =
+ FacetURI::FromPotentiallyInvalidSpec(test_case.valid_facet_uri);
+ ASSERT_TRUE(facet_uri.IsValidAndroidFacetURI());
+ EXPECT_EQ(test_case.expected_canonical_facet_uri,
+ facet_uri.canonical_spec());
+ EXPECT_EQ("android", facet_uri.scheme());
+ EXPECT_EQ(test_case.expected_package_name,
+ facet_uri.android_package_name());
+ }
+}
+
+TEST(AffiliationUtilsTest, InvalidAndroidFacetURIs) {
+ const char* kInvalidFacetURIs[]{
+ // Invalid URL (actually, will be treated as having only a host part).
+ "Does not look like a valid URL.",
+ // Path is more than just the root path ('/').
+ "android://hash@com.example.android/path",
+ // Empty scheme or not "android" scheme.
+ "://hash@com.example.android",
+ "http://hash@com.example.android",
+ // Package name with illegal characters.
+ "android://hash@com.$example.android",
+ "android://hash@com-example-android",
+ "android://hash@com%2Dexample.android", // Escaped '-'.
+ "android://hash@com.example.sz\xc3\xb3t\xc3\xa1r", // UTF-8 o' and a'.
+ // Empty, non-existent and malformed hash part.
+ "android://@com.example.android",
+ "android://com.example.android",
+ "android://badpadding=a@com.example.android",
+ "android://toolongpaddin===@com.example.android",
+ "android://invalid+characters@com.example.android",
+ "android://invalid%2Fcharacters@com.example.android", // Escaped '/'.
+ // Forbidden non-empty components.
+ "android://hash:password@com.example.android",
+ "android://hash@com.example.android:port",
+ "android://hash@com.example.android/?",
+ "android://hash@com.example.android/?query",
+ "android://hash@com.example.android/#",
+ "android://hash@com.example.android/#ref",
+ // Valid Web facet URI.
+ "https://www.example.com/"};
+ for (const char* uri : kInvalidFacetURIs) {
+ SCOPED_TRACE(testing::Message("URI = ") << uri);
+ FacetURI facet_uri = FacetURI::FromPotentiallyInvalidSpec(uri);
+ EXPECT_FALSE(facet_uri.IsValidAndroidFacetURI());
+ EXPECT_EQ("", facet_uri.android_package_name());
+ }
+}
+
+TEST(AffiliationUtilsTest, EqualEquivalenceClasses) {
+ AffiliatedFacets a = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI2)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ };
+
+ AffiliatedFacets b = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI2)},
+ };
+
+ EXPECT_TRUE(AreEquivalenceClassesEqual(a, a));
+ EXPECT_TRUE(AreEquivalenceClassesEqual(b, b));
+ EXPECT_TRUE(AreEquivalenceClassesEqual(b, a));
+ EXPECT_TRUE(AreEquivalenceClassesEqual(a, b));
+}
+
+TEST(AffiliationUtilsTest, NotEqualEquivalenceClasses) {
+ AffiliatedFacets a = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI2)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI2)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI2)},
+ };
+
+ AffiliatedFacets b = {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ };
+
+ AffiliatedFacets c;
+
+ EXPECT_FALSE(AreEquivalenceClassesEqual(a, b));
+ EXPECT_FALSE(AreEquivalenceClassesEqual(a, c));
+ EXPECT_FALSE(AreEquivalenceClassesEqual(b, a));
+ EXPECT_FALSE(AreEquivalenceClassesEqual(b, c));
+ EXPECT_FALSE(AreEquivalenceClassesEqual(c, a));
+ EXPECT_FALSE(AreEquivalenceClassesEqual(c, b));
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/facet_manager.cc b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager.cc
new file mode 100644
index 00000000000..af4c891affc
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager.cc
@@ -0,0 +1,251 @@
+// 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.
+
+// Note: Read the class comment of AffiliationService for the definition of the
+// terms used below.
+//
+// On-demand fetching strategy
+//
+// A GetAffiliationsAndBranding() request concerning facet X will be served from
+// the cache as long as the cache contains fresh affiliation information for
+// facet X, that is, if there is an equivalence class in the cache that contains
+// X and has been fetched less than |kCacheHardExpiryInHours| hours ago.
+//
+// Otherwise, a network request is issued against the Affiliation API as soon as
+// possible, that is, immediately if there is no fetch in flight, or right after
+// completion of the fetch in flight if there is one, provided that the required
+// data is not incidentally returned by the first fetch.
+//
+//
+// Proactive fetching strategy
+//
+// A Prefetch() request concerning facet Y can trigger an initial network fetch,
+// or periodic refetches only when:
+// * The prefetch request is not already expired, i.e., its |keep_fresh_until|
+// threshold is strictly in the future (that is, prefetch intervals are open
+// from the right).
+// * Affiliation information in the cache pertaining to facet Y will get stale
+// strictly before the specified |keep_fresh_until| threshold.
+//
+// An initial fetch will be issued as soon as possible if, in addition to the
+// two necessery conditions above, and at the time of the Prefetch() call, the
+// cache contains no affiliation information regarding facet Y, or if the data
+// in the cache for facet Y is near-stale, that is, it has been fetched more
+// than |kCacheHardExpiryInHours| hours ago.
+//
+// A refetch will be issued every time the data in the cache regarding facet Y
+// becomes near-stale, that is, exactly |kCacheSoftExpiry| hours after the last
+// fetch, provided that the above two necessary conditions are also met.
+//
+// Fetches are triggered already when the data gets near-stale, as opposed to
+// waiting until the data would get stale, in an effort to keep the data fresh
+// even in face of temporary network errors lasting no more than the difference
+// between soft and hard expiry times.
+//
+// The current fetch scheduling logic, however, can only deal with at most one
+// such 'early' fetch between taking place between the prior fetch and the
+// corresponding hard expiry time of the data, therefore it is assumed that:
+//
+// kCacheSoftExpiryInHours < kCacheHardExpiryInHours, and
+// 2 * kCacheSoftExpiryInHours > kCacheHardExpiryInHours.
+//
+//
+// Cache freshness terminology
+//
+//
+// Fetch (t=0) kCacheSoftExpiry kCacheHardExpiry
+// / / /
+// ---o------------------------o-----------------------o-----------------> t
+// | | |
+// | [-- Cache near-stale --------------------- ..
+// [--------------- Cache is fresh ----------------)[-- Cache is stale ..
+//
+
+#include "components/password_manager/core/browser/android_affiliation/facet_manager.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/task_runner.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/facet_manager_host.h"
+
+namespace password_manager {
+
+// statics
+const int FacetManager::kCacheSoftExpiryInHours = 21;
+const int FacetManager::kCacheHardExpiryInHours = 24;
+
+static_assert(
+ FacetManager::kCacheSoftExpiryInHours <
+ FacetManager::kCacheHardExpiryInHours,
+ "Soft expiry period must be shorter than the hard expiry period.");
+
+static_assert(
+ 2 * FacetManager::kCacheSoftExpiryInHours >
+ FacetManager::kCacheHardExpiryInHours,
+ "Soft expiry period must be longer than half of the hard expiry period.");
+
+// Encapsulates the details of a pending GetAffiliationsAndBranding() request.
+struct FacetManager::RequestInfo {
+ AffiliationService::ResultCallback callback;
+ scoped_refptr<base::TaskRunner> callback_task_runner;
+};
+
+FacetManager::FacetManager(const FacetURI& facet_uri,
+ FacetManagerHost* backend,
+ base::Clock* clock)
+ : facet_uri_(facet_uri), backend_(backend), clock_(clock) {
+ AffiliatedFacetsWithUpdateTime affiliations;
+ if (backend_->ReadAffiliationsAndBrandingFromDatabase(facet_uri_,
+ &affiliations))
+ last_update_time_ = affiliations.last_update_time;
+}
+
+FacetManager::~FacetManager() {
+ // The manager will be destroyed while there are pending requests only if the
+ // entire backend is going away. Fail pending requests in this case.
+ for (const auto& request_info : pending_requests_)
+ ServeRequestWithFailure(request_info);
+}
+
+void FacetManager::GetAffiliationsAndBranding(
+ StrategyOnCacheMiss cache_miss_strategy,
+ const AffiliationService::ResultCallback& callback,
+ const scoped_refptr<base::TaskRunner>& callback_task_runner) {
+ RequestInfo request_info;
+ request_info.callback = callback;
+ request_info.callback_task_runner = callback_task_runner;
+ if (IsCachedDataFresh()) {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ if (!backend_->ReadAffiliationsAndBrandingFromDatabase(facet_uri_,
+ &affiliation)) {
+ ServeRequestWithFailure(request_info);
+ return;
+ }
+ DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_;
+ ServeRequestWithSuccess(request_info, affiliation.facets);
+ } else if (cache_miss_strategy == StrategyOnCacheMiss::FETCH_OVER_NETWORK) {
+ pending_requests_.push_back(request_info);
+ backend_->SignalNeedNetworkRequest();
+ } else {
+ ServeRequestWithFailure(request_info);
+ }
+}
+
+void FacetManager::Prefetch(const base::Time& keep_fresh_until) {
+ keep_fresh_until_thresholds_.insert(keep_fresh_until);
+
+ // If an initial fetch if needed, trigger that (the refetch will be scheduled
+ // once the initial fetch completes). Otherwise schedule the next refetch.
+ base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
+ if (next_required_fetch <= clock_->Now())
+ backend_->SignalNeedNetworkRequest();
+ else if (next_required_fetch < base::Time::Max())
+ backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
+
+ // For a finite |keep_fresh_until|, schedule a callback so that once the
+ // prefetch expires, it can be removed from |keep_fresh_untils_|, and also the
+ // manager can get a chance to be destroyed unless it is otherwise needed.
+ if (keep_fresh_until > clock_->Now() && keep_fresh_until < base::Time::Max())
+ backend_->RequestNotificationAtTime(facet_uri_, keep_fresh_until);
+}
+
+void FacetManager::CancelPrefetch(const base::Time& keep_fresh_until) {
+ auto iter = keep_fresh_until_thresholds_.find(keep_fresh_until);
+ if (iter != keep_fresh_until_thresholds_.end())
+ keep_fresh_until_thresholds_.erase(iter);
+}
+
+void FacetManager::OnFetchSucceeded(
+ const AffiliatedFacetsWithUpdateTime& affiliation) {
+ last_update_time_ = affiliation.last_update_time;
+ DCHECK(IsCachedDataFresh()) << facet_uri_;
+ for (const auto& request_info : pending_requests_)
+ ServeRequestWithSuccess(request_info, affiliation.facets);
+ pending_requests_.clear();
+
+ base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
+ if (next_required_fetch < base::Time::Max())
+ backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
+}
+
+void FacetManager::NotifyAtRequestedTime() {
+ base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
+ if (next_required_fetch <= clock_->Now())
+ backend_->SignalNeedNetworkRequest();
+ else if (next_required_fetch < base::Time::Max())
+ backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
+
+ auto iter_first_non_expired =
+ keep_fresh_until_thresholds_.upper_bound(clock_->Now());
+ keep_fresh_until_thresholds_.erase(keep_fresh_until_thresholds_.begin(),
+ iter_first_non_expired);
+}
+
+bool FacetManager::CanBeDiscarded() const {
+ return pending_requests_.empty() &&
+ GetMaximumKeepFreshUntilThreshold() <= clock_->Now();
+}
+
+bool FacetManager::CanCachedDataBeDiscarded() const {
+ return GetMaximumKeepFreshUntilThreshold() <= clock_->Now() ||
+ !IsCachedDataFresh();
+}
+
+bool FacetManager::DoesRequireFetch() const {
+ return (!pending_requests_.empty() && !IsCachedDataFresh()) ||
+ GetNextRequiredFetchTimeDueToPrefetch() <= clock_->Now();
+}
+
+bool FacetManager::IsCachedDataFresh() const {
+ return clock_->Now() < GetCacheHardExpiryTime();
+}
+
+bool FacetManager::IsCachedDataNearStale() const {
+ return GetCacheSoftExpiryTime() <= clock_->Now();
+}
+
+base::Time FacetManager::GetCacheSoftExpiryTime() const {
+ return last_update_time_ +
+ base::TimeDelta::FromHours(kCacheSoftExpiryInHours);
+}
+
+base::Time FacetManager::GetCacheHardExpiryTime() const {
+ return last_update_time_ +
+ base::TimeDelta::FromHours(kCacheHardExpiryInHours);
+}
+
+base::Time FacetManager::GetMaximumKeepFreshUntilThreshold() const {
+ return !keep_fresh_until_thresholds_.empty()
+ ? *keep_fresh_until_thresholds_.rbegin()
+ : base::Time();
+}
+
+base::Time FacetManager::GetNextRequiredFetchTimeDueToPrefetch() const {
+ // If there is at least one non-expired Prefetch() request that requires the
+ // data to be kept fresh until some time later than its current hard expiry
+ // time, then a fetch is needed once the cached data becomes near-stale.
+ if (clock_->Now() < GetMaximumKeepFreshUntilThreshold() &&
+ GetCacheHardExpiryTime() < GetMaximumKeepFreshUntilThreshold()) {
+ return GetCacheSoftExpiryTime();
+ }
+ return base::Time::Max();
+}
+
+// static
+void FacetManager::ServeRequestWithSuccess(
+ const RequestInfo& request_info,
+ const AffiliatedFacets& affiliation) {
+ request_info.callback_task_runner->PostTask(
+ FROM_HERE, base::Bind(request_info.callback, affiliation, true));
+}
+
+// static
+void FacetManager::ServeRequestWithFailure(const RequestInfo& request_info) {
+ request_info.callback_task_runner->PostTask(
+ FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false));
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/facet_manager.h b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager.h
new file mode 100644
index 00000000000..ef26ec946cf
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager.h
@@ -0,0 +1,137 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FACET_MANAGER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FACET_MANAGER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+
+namespace base {
+class Clock;
+class TaskRunner;
+} // namespace base
+
+namespace password_manager {
+
+class FacetManagerHost;
+
+// Encapsulates the state and logic required for handling affiliation requests
+// concerning a single facet. The AffiliationBackend owns one instance for each
+// facet that requires attention, and it itself implements the FacetManagerHost
+// interface to provide shared functionality needed by all FacetManagers.
+class FacetManager {
+ public:
+ using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
+
+ // Both the |backend| and |clock| must outlive this object.
+ FacetManager(const FacetURI& facet_uri,
+ FacetManagerHost* backend,
+ base::Clock* clock);
+ ~FacetManager();
+
+ // Facet-specific implementations for methods in AffiliationService of the
+ // same name. See documentation in affiliation_service.h for details:
+ void GetAffiliationsAndBranding(
+ StrategyOnCacheMiss cache_miss_strategy,
+ const AffiliationService::ResultCallback& callback,
+ const scoped_refptr<base::TaskRunner>& callback_task_runner);
+ void Prefetch(const base::Time& keep_fresh_until);
+ void CancelPrefetch(const base::Time& keep_fresh_until);
+
+ // Called when |affiliation| information regarding this facet has just been
+ // fetched from the Affiliation API.
+ void OnFetchSucceeded(const AffiliatedFacetsWithUpdateTime& affiliation);
+
+ // Called by the backend when the time specified in RequestNotificationAtTime
+ // has come to pass, so that |this| can perform delayed administrative tasks.
+ void NotifyAtRequestedTime();
+
+ // Returns whether this instance has becomes redundant, that is, it has no
+ // more meaningful state than a newly created instance would have.
+ bool CanBeDiscarded() const;
+
+ // Returns whether or not cached data for this facet can be discarded without
+ // harm when trimming the database.
+ bool CanCachedDataBeDiscarded() const;
+
+ // Returns whether or not affiliation information relating to this facet needs
+ // to be fetched right now.
+ bool DoesRequireFetch() const;
+
+ // The members below are made public for the sake of tests.
+
+ // Returns whether or not cached data for this facet is fresh (not stale).
+ bool IsCachedDataFresh() const;
+
+ // Returns whether or not cached data for this facet is near-stale or stale.
+ bool IsCachedDataNearStale() const;
+
+ // The duration after which cached affiliation data is considered near-stale.
+ static const int kCacheSoftExpiryInHours;
+
+ // The duration after which cached affiliation data is considered stale.
+ static const int kCacheHardExpiryInHours;
+
+ private:
+ struct RequestInfo;
+
+ // Returns the time when the cached data for this facet will become stale.
+ // The data is considered stale with the returned time value inclusive.
+ base::Time GetCacheHardExpiryTime() const;
+
+ // Returns the time when cached data for this facet becomes near-stale.
+ // The data is considered near-stale with the returned time value inclusive.
+ base::Time GetCacheSoftExpiryTime() const;
+
+ // Returns the maximum of |keep_fresh_thresholds_|, or the NULL time if the
+ // set is empty.
+ base::Time GetMaximumKeepFreshUntilThreshold() const;
+
+ // Returns the next time affiliation data for this facet needs to be fetched
+ // due to active prefetch requests, or base::Time::Max() if not at all.
+ base::Time GetNextRequiredFetchTimeDueToPrefetch() const;
+
+ // Posts the callback of the request described by |request_info| with success.
+ static void ServeRequestWithSuccess(const RequestInfo& request_info,
+ const AffiliatedFacets& affiliation);
+
+ // Posts the callback of the request described by |request_info| with failure.
+ static void ServeRequestWithFailure(const RequestInfo& request_info);
+
+ FacetURI facet_uri_;
+ FacetManagerHost* backend_;
+ base::Clock* clock_;
+
+ // The last time affiliation information was fetched for this facet, i.e. the
+ // freshness of the data in the cache. If there is no corresponding data in
+ // the database, this will contain the NULL time. Otherwise, the update time
+ // in the database should match this value; it is stored to reduce disk I/O.
+ base::Time last_update_time_;
+
+ // Contains information about the GetAffiliationsAndBranding() requests that
+ // are waiting for the result of looking up this facet.
+ std::vector<RequestInfo> pending_requests_;
+
+ // Keeps track of |keep_fresh_until| thresholds corresponding to Prefetch()
+ // requests for this facet. Affiliation information for this facet must be
+ // kept fresh by periodic refetches until at least the maximum time in this
+ // set (exclusive).
+ //
+ // This is not a single timestamp but rather a multiset so that cancellation
+ // of individual prefetches can be supported even if there are two requests
+ // with the same |keep_fresh_until| threshold.
+ std::multiset<base::Time> keep_fresh_until_thresholds_;
+
+ DISALLOW_COPY_AND_ASSIGN(FacetManager);
+};
+
+} // namespace password_manager
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FACET_MANAGER_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/facet_manager_host.h b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager_host.h
new file mode 100644
index 00000000000..21c12e34814
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager_host.h
@@ -0,0 +1,37 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FACET_MANAGER_HOST_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FACET_MANAGER_HOST_H_
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+
+namespace password_manager {
+
+// Interface through which FacetManagers can access shared functionality
+// provided by the AffiliationBackend.
+class FacetManagerHost {
+ public:
+ virtual ~FacetManagerHost() {}
+
+ // Reads the equivalence class containing |facet_uri| from the database and
+ // returns true if found; returns false otherwise.
+ virtual bool ReadAffiliationsAndBrandingFromDatabase(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* affiliations) = 0;
+
+ // Signals the fetching logic that affiliation information for a facet needs
+ // to be fetched immediately.
+ virtual void SignalNeedNetworkRequest() = 0;
+
+ // Requests that the FacetManager corresponding to |facet_uri| be notified at
+ // the specified |time| so it can perform delayed administrative tasks.
+ virtual void RequestNotificationAtTime(const FacetURI& facet_uri,
+ base::Time time) = 0;
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FACET_MANAGER_HOST_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/facet_manager_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager_unittest.cc
new file mode 100644
index 00000000000..45fd0df5c15
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/facet_manager_unittest.cc
@@ -0,0 +1,1539 @@
+// 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/password_manager/core/browser/android_affiliation/facet_manager.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/rand_util.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/android_affiliation/facet_manager_host.h"
+#include "components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+// Helpers --------------------------------------------------------------------
+
+namespace {
+
+using StrategyOnCacheMiss = FacetManager::StrategyOnCacheMiss;
+enum class NotificationAccuracy { PERFECT, TOO_LATE, TOO_EARLY, NEVER_CALLED };
+
+// Helper class to post callbacks to FacetManager::NotifyAtRequestedTime(),
+// delayed by the requested time plus/minus a configurable error term to
+// simulate a real-life task runner.
+class TestFacetManagerNotifier {
+ public:
+ TestFacetManagerNotifier(
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner,
+ base::TimeDelta too_late_delay)
+ : accuracy_(NotificationAccuracy::PERFECT),
+ too_late_delay_(too_late_delay),
+ task_runner_(task_runner),
+ facet_manager_(nullptr) {}
+
+ void Notify(base::Time time) {
+ base::TimeDelta delay = time - task_runner_->Now();
+ if (accuracy_ == NotificationAccuracy::TOO_LATE) {
+ delay += too_late_delay_;
+ } else if (accuracy_ == NotificationAccuracy::TOO_EARLY) {
+ // This formula is a simple stateless solution for notifying FacetManagers
+ // prematurely multiple times in a row while also ensuring that the tests
+ // are still fast, with no more than log2(delay.InSeconds()) repetitions.
+ delay = std::min(delay, delay / 2 + base::TimeDelta::FromSeconds(1));
+ } else if (accuracy_ == NotificationAccuracy::NEVER_CALLED) {
+ return;
+ }
+ task_runner_->PostDelayedTask(
+ FROM_HERE, base::Bind(&FacetManager::NotifyAtRequestedTime,
+ base::Unretained(facet_manager_)),
+ delay);
+ }
+
+ void set_accuracy(NotificationAccuracy accuracy) { accuracy_ = accuracy; }
+ void set_facet_manager(FacetManager* facet_manager_under_test) {
+ facet_manager_ = facet_manager_under_test;
+ }
+
+ private:
+ NotificationAccuracy accuracy_;
+ const base::TimeDelta too_late_delay_;
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+ FacetManager* facet_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFacetManagerNotifier);
+};
+
+// Stub/mock implementation for FacetManagerHost.
+class MockFacetManagerHost : public FacetManagerHost {
+ public:
+ explicit MockFacetManagerHost(TestFacetManagerNotifier* notifier)
+ : notifier_(notifier), signaled_need_network_request_(false) {}
+
+ ~MockFacetManagerHost() override {}
+
+ // Sets the |facet_uri| that will be expected to appear in calls coming from
+ // the FacetManager under test.
+ void set_expected_facet_uri(const FacetURI& facet_uri) {
+ expected_facet_uri_ = facet_uri;
+ }
+
+ // Returns the facet URI that will be expected to appear in calls coming from
+ // the FacetManager under test.
+ const FacetURI& expected_facet_uri() const { return expected_facet_uri_; }
+
+ // Sets up fake |database_content| as the canned response to be returned to
+ // the FacetManager every time it calls
+ // ReadAffiliationsAndBrandingFromDatabase().
+ void set_fake_database_content(
+ const AffiliatedFacetsWithUpdateTime& database_content) {
+ fake_database_content_ = database_content;
+ }
+
+ void clear_fake_database_content() {
+ fake_database_content_.last_update_time = base::Time();
+ }
+
+ // Returns whether SignalNeedNetworkRequest() has been called at least once.
+ size_t signaled_need_network_request() const {
+ return signaled_need_network_request_;
+ }
+
+ void reset_need_network_request() { signaled_need_network_request_ = false; }
+
+ private:
+ // FacetManagerHost:
+ bool ReadAffiliationsAndBrandingFromDatabase(
+ const FacetURI& facet_uri,
+ AffiliatedFacetsWithUpdateTime* affiliations) override {
+ EXPECT_EQ(expected_facet_uri_, facet_uri);
+ if (fake_database_content_.last_update_time.is_null())
+ return false;
+ *affiliations = fake_database_content_;
+ return true;
+ }
+
+ void SignalNeedNetworkRequest() override {
+ signaled_need_network_request_ = true;
+ }
+
+ void RequestNotificationAtTime(const FacetURI& facet_uri,
+ base::Time time) override {
+ EXPECT_EQ(expected_facet_uri_, facet_uri);
+ // The absolute timing of notification requests is not all that interesting,
+ // only the ability to perturb it slightly, which is done by the notifier.
+ notifier_->Notify(time);
+ }
+
+ TestFacetManagerNotifier* notifier_;
+
+ FacetURI expected_facet_uri_;
+ AffiliatedFacetsWithUpdateTime fake_database_content_;
+ bool signaled_need_network_request_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockFacetManagerHost);
+};
+
+const bool kFalseTrue[] = {false, true};
+
+const char kTestFacetURI1[] = "https://one.example.com";
+const char kTestFacetURI2[] = "https://two.example.com";
+const char kTestFacetURI3[] = "https://three.example.com";
+
+AffiliatedFacets GetTestEquivalenceClass() {
+ return {
+ {FacetURI::FromCanonicalSpec(kTestFacetURI1)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI2)},
+ {FacetURI::FromCanonicalSpec(kTestFacetURI3)},
+ };
+}
+
+AffiliatedFacetsWithUpdateTime GetTestEquivalenceClassWithUpdateTime(
+ base::Time last_update_time) {
+ AffiliatedFacetsWithUpdateTime affiliation;
+ affiliation.last_update_time = last_update_time;
+ affiliation.facets = GetTestEquivalenceClass();
+ return affiliation;
+}
+
+base::TimeDelta GetCacheHardExpiryPeriod() {
+ return base::TimeDelta::FromHours(FacetManager::kCacheHardExpiryInHours);
+}
+
+base::TimeDelta GetCacheSoftExpiryPeriod() {
+ return base::TimeDelta::FromHours(FacetManager::kCacheSoftExpiryInHours);
+}
+
+base::TimeDelta GetShortTestPeriod() {
+ return base::TimeDelta::FromHours(1);
+}
+
+// Returns a smallest time difference that this test cares about.
+base::TimeDelta Epsilon() {
+ return base::TimeDelta::FromMicroseconds(1);
+}
+
+// Returns |time| + |delay| or the maximum time if |delay| is the maximum delta.
+base::Time SafeAdd(base::Time time, base::TimeDelta delay) {
+ if (delay == base::TimeDelta::Max())
+ return base::Time::Max();
+ return time + delay;
+}
+
+// Subdivides a time interval of a given |duration| into zero or more intervals
+// that lend themselves to be used for sampling a quantity every that often.
+// More specifically, returns the minimum number of intervals so that each
+// sub-interval is at most GetTestShortInterval() long, and that the last of the
+// sub-intervals (if any) is exactly Epsilon() long. No intervals are returned
+// if |duration| is of length zero.
+std::vector<base::TimeDelta> SamplingPoints(base::TimeDelta duration) {
+ std::vector<base::TimeDelta> deltas;
+ if (duration > base::TimeDelta()) {
+ while (duration > Epsilon()) {
+ deltas.push_back(std::min(GetShortTestPeriod(), duration - Epsilon()));
+ duration -= deltas.back();
+ }
+ deltas.push_back(Epsilon());
+ }
+ return deltas;
+}
+
+} // namespace
+
+// Test framework -------------------------------------------------------------
+
+class FacetManagerTest : public testing::Test {
+ public:
+ FacetManagerTest()
+ : consumer_task_runner_(new base::TestSimpleTaskRunner),
+ main_task_runner_(new base::TestMockTimeTaskRunner),
+ main_clock_(main_task_runner_->GetMockClock()),
+ facet_manager_notifier_(main_task_runner_, GetShortTestPeriod()),
+ facet_manager_host_(&facet_manager_notifier_) {}
+
+ protected:
+ struct ExpectedFetchDetails {
+ // The expected time of the fetch being triggered.
+ base::Time time;
+
+ // A simulated delay after which the fetch will be completed.
+ // base::TimeDelta::Max() means that the fetch will be left hanging.
+ base::TimeDelta completion_delay;
+ };
+
+ void CreateFacetManager() {
+ // The order is important: FacetManager will read the DB in its constructor.
+ facet_manager_host_.set_expected_facet_uri(
+ FacetURI::FromCanonicalSpec(kTestFacetURI1));
+ facet_manager_ = base::MakeUnique<FacetManager>(
+ FacetURI::FromCanonicalSpec(kTestFacetURI1), fake_facet_manager_host(),
+ main_clock_.get());
+ facet_manager_notifier_.set_facet_manager(facet_manager_.get());
+ facet_manager_creation_ = Now();
+ }
+
+ void DestroyFacetManager() {
+ main_task_runner_->ClearPendingTasks();
+ facet_manager_host_.set_expected_facet_uri(FacetURI());
+ facet_manager_notifier_.set_facet_manager(nullptr);
+ facet_manager_.reset();
+ }
+
+ void AdvanceTime(base::TimeDelta delta) {
+ main_task_runner_->FastForwardBy(delta);
+ }
+
+ base::Time Now() { return main_task_runner_->Now(); }
+
+ // Returns the elapsed time since CreateFacetManager() was last called.
+ base::TimeDelta DeltaNow() { return Now() - facet_manager_creation_; }
+
+ void GetAffiliationsAndBranding(StrategyOnCacheMiss cache_miss_strategy) {
+ facet_manager()->GetAffiliationsAndBranding(
+ cache_miss_strategy, mock_consumer()->GetResultCallback(),
+ consumer_task_runner());
+ }
+
+ void Prefetch(base::Time until) { facet_manager()->Prefetch(until); }
+ void CancelPrefetch(base::Time until) {
+ facet_manager()->CancelPrefetch(until);
+ }
+
+ void SchedulePrefetch(base::Time start, base::Time end) {
+ main_task_runner_->PostDelayedTask(
+ FROM_HERE, base::Bind(&FacetManager::Prefetch,
+ base::Unretained(facet_manager()), end),
+ start - Now());
+ }
+
+ void ScheduleCancelPrefetch(base::Time cancellation_time,
+ base::Time original_end_of_prefetch) {
+ main_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&FacetManager::CancelPrefetch,
+ base::Unretained(facet_manager()), original_end_of_prefetch),
+ cancellation_time - Now());
+ }
+
+ // Advances time |until_time|, and verifies that a Prefetch() request has the
+ // expected effects now and then (but not at |until_time|), namely that:
+ // * the FacetManager cannot be discarded, and
+ // * requests are served from the cache;
+ // and that no fetches are triggered in this interval.
+ void AdvanceTimeAndVerifyPrefetch(base::Time until_time) {
+ for (base::TimeDelta step : SamplingPoints(until_time - Now())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
+ ExpectRequestsServedFromCache();
+ ExpectNoFetchNeeded();
+ AdvanceTime(step);
+ }
+ }
+
+ // Advances time |until_time|, and verifies that between now and then, the
+ // FacetManager cannot be discarded, and a fetch is needed all the time.
+ void AdvanceTimeAndExpectFetchNeeded(base::Time until_time) {
+ for (base::TimeDelta step : SamplingPoints(until_time - Now())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ AdvanceTime(step);
+ }
+ }
+
+ // Advances time |until_time|, and verifies that a Prefetch() request has the
+ // expected effects between now and then (but not at |until_time|), namely:
+ // * the FacetManager cannot be discarded, and
+ // * requests are served from the cache;
+ // and that no fetches are made exactly as prescribed in |expected_fetches|.
+ void AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ base::Time until_time,
+ const std::vector<ExpectedFetchDetails>& expected_fetches) {
+ for (const auto& next_fetch : expected_fetches) {
+ AdvanceTimeAndVerifyPrefetch(next_fetch.time);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndExpectFetchNeeded(
+ std::min(until_time, SafeAdd(Now(), next_fetch.completion_delay))));
+ if (next_fetch.completion_delay < base::TimeDelta::Max())
+ CompleteFetch();
+ }
+ AdvanceTimeAndVerifyPrefetch(until_time);
+ }
+
+ void ExpectFetchNeeded() {
+ ASSERT_TRUE(facet_manager()->DoesRequireFetch());
+ ASSERT_TRUE(fake_facet_manager_host()->signaled_need_network_request());
+ }
+
+ void ExpectNoFetchNeeded() {
+ ASSERT_FALSE(facet_manager()->DoesRequireFetch());
+ ASSERT_FALSE(fake_facet_manager_host()->signaled_need_network_request());
+ }
+
+ void CompleteFetch() {
+ AffiliatedFacetsWithUpdateTime fetch_result(
+ GetTestEquivalenceClassWithUpdateTime(Now()));
+ fake_facet_manager_host()->set_fake_database_content(fetch_result);
+ fake_facet_manager_host()->reset_need_network_request();
+ facet_manager()->OnFetchSucceeded(fetch_result);
+
+ main_task_runner_->RunUntilIdle();
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ ASSERT_TRUE(facet_manager()->IsCachedDataFresh());
+ }
+
+ void ExpectConsumerSuccessCallback() {
+ const auto equivalence_class(GetTestEquivalenceClass());
+ mock_consumer()->ExpectSuccessWithResult(equivalence_class);
+ EXPECT_THAT(
+ equivalence_class,
+ testing::Contains(testing::Field(
+ &Facet::uri, fake_facet_manager_host()->expected_facet_uri())));
+
+ consumer_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+ }
+
+ void ExpectConsumerFailureCallback() {
+ mock_consumer()->ExpectFailure();
+ consumer_task_runner()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_consumer());
+ }
+
+ void ExpectRequestsServedFromCache() {
+ EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FAIL);
+ ExpectConsumerSuccessCallback();
+ }
+
+ MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
+
+ base::TestSimpleTaskRunner* consumer_task_runner() {
+ return consumer_task_runner_.get();
+ }
+
+ base::TestMockTimeTaskRunner* main_task_runner() {
+ return main_task_runner_.get();
+ }
+
+ MockFacetManagerHost* fake_facet_manager_host() {
+ return &facet_manager_host_;
+ }
+
+ TestFacetManagerNotifier* facet_manager_notifier() {
+ return &facet_manager_notifier_;
+ }
+
+ FacetManager* facet_manager() { return facet_manager_.get(); }
+
+ private:
+ // testing::Test:
+ void SetUp() override {
+ ASSERT_LT(3 * GetShortTestPeriod(), GetCacheSoftExpiryPeriod());
+ ASSERT_LT(GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod());
+ }
+
+ void TearDown() override { DestroyFacetManager(); }
+
+ MockAffiliationConsumer mock_consumer_;
+ scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
+ scoped_refptr<base::TestMockTimeTaskRunner> main_task_runner_;
+ std::unique_ptr<base::Clock> main_clock_;
+ TestFacetManagerNotifier facet_manager_notifier_;
+ MockFacetManagerHost facet_manager_host_;
+
+ std::unique_ptr<FacetManager> facet_manager_;
+ base::Time facet_manager_creation_;
+
+ DISALLOW_COPY_AND_ASSIGN(FacetManagerTest);
+};
+
+// Tests ----------------------------------------------------------------------
+
+TEST_F(FacetManagerTest, NewInstanceCanBeDiscarded) {
+ CreateFacetManager();
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
+ EXPECT_FALSE(facet_manager()->DoesRequireFetch());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+}
+
+// Both cached-only and on-demand GetAffiliationsAndBranding() requests should
+// be served from cache if it contains fresh data. Nothing should happen on
+// cache expiry.
+TEST_F(FacetManagerTest, GetAffiliationsAndBrandingServedFromCache) {
+ fake_facet_manager_host()->set_fake_database_content(
+ GetTestEquivalenceClassWithUpdateTime(Now()));
+ AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
+
+ CreateFacetManager();
+ EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FAIL);
+ ExpectConsumerSuccessCallback();
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ExpectConsumerSuccessCallback();
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+
+ AdvanceTime(Epsilon());
+
+ EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+}
+
+// On-demand GetAffiliationsAndBranding() requests should trigger a fetch if the
+// cache has already stale data, or no corresponding data whatsoever. Nothing
+// should happen once the newly fetched data expires.
+TEST_F(FacetManagerTest,
+ OnDemandGetAffiliationsAndBrandingRequestTriggersFetch) {
+ for (const bool cache_initially_has_stale_data : kFalseTrue) {
+ SCOPED_TRACE(cache_initially_has_stale_data);
+
+ if (cache_initially_has_stale_data) {
+ fake_facet_manager_host()->set_fake_database_content(
+ GetTestEquivalenceClassWithUpdateTime(Now()));
+ AdvanceTime(GetCacheHardExpiryPeriod());
+ } else {
+ fake_facet_manager_host()->clear_fake_database_content();
+ }
+
+ CreateFacetManager();
+ EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(CompleteFetch());
+ ExpectConsumerSuccessCallback();
+
+ AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
+ EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FAIL);
+ ExpectConsumerSuccessCallback();
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ExpectConsumerSuccessCallback();
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+
+ AdvanceTime(Epsilon());
+
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ }
+}
+
+TEST_F(FacetManagerTest,
+ CachedOnlyGetAffiliationsAndBrandingFailsDueToStaleCache) {
+ CreateFacetManager();
+ EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FAIL);
+ ExpectConsumerFailureCallback();
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+}
+
+TEST_F(FacetManagerTest,
+ GetAffiliationsAndBrandingFailureCallbackInvokedOnDestruction) {
+ CreateFacetManager();
+ EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+
+ // Leave the fetch hanging and destroy the facet manager.
+ DestroyFacetManager();
+
+ ExpectConsumerFailureCallback();
+ fake_facet_manager_host()->reset_need_network_request();
+}
+
+// The following tests verify both typical and edge case behavior of Prefetch()
+// requests: they should prevent the FacetManager from being discarded, and keep
+// the data fresh by initial fetches and refetches (scheduled as described in
+// facet_manager.cc).
+//
+// Legend:
+// [---): Interval representing a finite Prefetch request (open from right).
+// The data should be kept fresh, the FacetManager not discarded.
+// [--->: Interval representing a indefinite Prefetch request.
+// The data should be kept fresh, the FacetManager not discarded.
+// F: Fetch (initial or refetch) should take place here.
+// Fn: The time of the n-th fetch (starting from 1).
+// D: Time interval equal to GetShortTestPeriod().
+// N: Fetch is signaled to be needed here.
+// X: A corresponding CancelPrefetch call is placed here.
+// S: |kCacheSoftExpiryInHours| hours
+// H: |kCacheHardExpiryInHours| hours
+//
+// Note: It is guaranteed that S < H and that H < 2*S.
+//
+// Prefetches with the cache is initially stale/empty:
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o---------------o-------o---------> t
+// : : : : :
+// [) : : : :
+// [F--) : : : :
+// [F------------------------): : : :
+// [F--------------------------------): : :
+// [F-------------------------F----------) : :
+// [F-------------------------F----------------------): :
+// [F-------------------------F------------------------------):
+// [F-------------------------F-----------------------F------------------>
+//
+TEST_F(FacetManagerTest, PrefetchWithEmptyOrStaleCache) {
+ struct {
+ base::TimeDelta prefetch_length;
+ size_t expected_num_fetches;
+ } const kTestCases[] = {
+ // Note: Zero length prefetches are tested later.
+ {GetShortTestPeriod(), 1},
+ {GetCacheSoftExpiryPeriod(), 1},
+ {GetCacheHardExpiryPeriod(), 1},
+ {GetCacheHardExpiryPeriod() + GetShortTestPeriod(), 2},
+ {GetCacheSoftExpiryPeriod() + GetCacheSoftExpiryPeriod(), 2},
+ {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(), 2},
+ {base::TimeDelta::Max(), 3}};
+
+ const base::TimeDelta kExpectedFetchTimes[] = {
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ 2 * GetCacheSoftExpiryPeriod()};
+
+ const base::TimeDelta kMaximumTestDuration = 2 * GetCacheHardExpiryPeriod();
+
+ for (const bool cache_initially_stale : kFalseTrue) {
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Test case: #" << i);
+ SCOPED_TRACE(cache_initially_stale ? "Cache initially stale"
+ : "Cache initially empty");
+
+ if (cache_initially_stale) {
+ fake_facet_manager_host()->set_fake_database_content(
+ GetTestEquivalenceClassWithUpdateTime(Now()));
+ AdvanceTime(GetCacheHardExpiryPeriod());
+ } else {
+ fake_facet_manager_host()->clear_fake_database_content();
+ }
+
+ std::vector<ExpectedFetchDetails> expected_fetches;
+ expected_fetches.resize(kTestCases[i].expected_num_fetches);
+ for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
+ expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
+
+ CreateFacetManager();
+ Prefetch(SafeAdd(Now(), kTestCases[i].prefetch_length));
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + std::min(kTestCases[i].prefetch_length, kMaximumTestDuration),
+ expected_fetches));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ if (kTestCases[i].prefetch_length < base::TimeDelta::Max()) {
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ } else {
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ }
+
+ DestroyFacetManager();
+ }
+ }
+}
+
+// Prefetches with cached affiliation data that is fresh to some extent:
+//
+// Suppose an unrelated fetch at t=0 has resulted in affiliation information
+// being stored into the cache (freshness interval marked with '='). See legend
+// above.
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o---------------o-------o-----> t
+// [F================================): : :
+// : : : : :
+// [) : : : :
+// [--) : : : :
+// [-------------------------): : : :
+// [---------------------------------): : :
+// [--------------------------F-----------) : :
+// [--------------------------F----------------------): :
+// [--------------------------F------------------------------):
+// [--------------------------F-----------------------F------------->
+// : : : :
+// [) : : : :
+// [--) : : : :
+// [---------------------): : : :
+// [-----------------------------): : :
+// [----------------------F-----------) : :
+// [----------------------F----------------------): :
+// [----------------------F------------------------------):
+// [----------------------F-----------------------F------------->
+// : : : :
+// [) : : :
+// [----) : : :
+// [------): : :
+// [F----------) : :
+// [F---------------------): :
+// [F-----------------------------):
+// [F----------------------F------------->
+//
+// t=0 S S+D H F2+S F2+H
+// / \ / / \ /
+// ---o--------------------------o-o-----o-----------------o-------o-----> t
+// [F================================): : :
+// : : : :
+// : [) : : :
+// : [----): : :
+// : [F------) : :
+// : [F---------------------): :
+// : [F-----------------------------):
+// : [F----------------------F------------->
+//
+TEST_F(FacetManagerTest, PrefetchTriggeredFetchSchedulingAfterNonEmptyCache) {
+ struct {
+ base::TimeDelta prefetch_start;
+ base::TimeDelta prefetch_end;
+ size_t expected_num_fetches;
+ } const kTestCases[] = {
+ // Note: Zero length prefetches are tested later.
+
+ // Prefetch starts at the exact time the data was incidentally fetched.
+ {base::TimeDelta(), GetShortTestPeriod(), 0},
+ {base::TimeDelta(), GetCacheSoftExpiryPeriod(), 0},
+ {base::TimeDelta(), GetCacheHardExpiryPeriod(), 0},
+ {base::TimeDelta(), GetCacheHardExpiryPeriod() + GetShortTestPeriod(), 1},
+ {base::TimeDelta(), 2 * GetCacheSoftExpiryPeriod(), 1},
+ {base::TimeDelta(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
+ 1},
+ {base::TimeDelta(), base::TimeDelta::Max(), 2},
+
+ // Prefetch starts a short time after the unrelated fetch.
+ {GetShortTestPeriod(), 2 * GetShortTestPeriod(), 0},
+ {GetShortTestPeriod(), GetCacheSoftExpiryPeriod(), 0},
+ {GetShortTestPeriod(), GetCacheHardExpiryPeriod(), 0},
+ {GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
+ 1},
+ {GetShortTestPeriod(), 2 * GetCacheSoftExpiryPeriod(), 1},
+ {GetShortTestPeriod(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
+ 1},
+ {GetShortTestPeriod(), base::TimeDelta::Max(), 2},
+
+ // Prefetch starts at the soft expiry time of the unrelated fetch.
+ {GetCacheSoftExpiryPeriod(),
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 0},
+ {GetCacheSoftExpiryPeriod(), GetCacheHardExpiryPeriod(), 0},
+ {GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
+ 1},
+ {GetCacheSoftExpiryPeriod(), 2 * GetCacheSoftExpiryPeriod(), 1},
+ {GetCacheSoftExpiryPeriod(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
+ 1},
+ {GetCacheSoftExpiryPeriod(), base::TimeDelta::Max(), 2}};
+
+ const base::TimeDelta kExpectedFetchTimes[] = {
+ GetCacheSoftExpiryPeriod(), 2 * GetCacheSoftExpiryPeriod()};
+
+ const base::TimeDelta kMaximumTestDuration = 2 * GetCacheHardExpiryPeriod();
+
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Test case: #" << i);
+
+ fake_facet_manager_host()->set_fake_database_content(
+ GetTestEquivalenceClassWithUpdateTime(Now()));
+
+ const base::Time prefetch_end = SafeAdd(Now(), kTestCases[i].prefetch_end);
+ const base::Time testing_end =
+ std::min(prefetch_end, Now() + kMaximumTestDuration);
+
+ std::vector<ExpectedFetchDetails> expected_fetches;
+ expected_fetches.resize(kTestCases[i].expected_num_fetches);
+ for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
+ expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
+
+ AdvanceTime(kTestCases[i].prefetch_start);
+
+ CreateFacetManager();
+ Prefetch(prefetch_end);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ testing_end, expected_fetches));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ if (kTestCases[i].prefetch_end < base::TimeDelta::Max()) {
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ } else {
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ }
+ DestroyFacetManager();
+ }
+}
+
+// Last block of tests from above.
+TEST_F(FacetManagerTest, PrefetchTriggeredFetchSchedulingAfterNonEmptyCache2) {
+ struct {
+ base::TimeDelta prefetch_start;
+ base::TimeDelta prefetch_end;
+ size_t expected_num_fetches;
+ } const kTestCases[] = {
+ // Note: Zero length prefetches are tested later.
+
+ // Prefetch starts between the soft and hard expiry time.
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod(),
+ 0},
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
+ 1},
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 1},
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
+ GetShortTestPeriod(),
+ 1},
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ base::TimeDelta::Max(),
+ 2}};
+
+ const base::TimeDelta kExpectedFetchTimes[] = {
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod()};
+
+ const base::TimeDelta kMaximumTestDuration = 2 * GetCacheHardExpiryPeriod();
+
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Test case: #" << i);
+
+ fake_facet_manager_host()->set_fake_database_content(
+ GetTestEquivalenceClassWithUpdateTime(Now()));
+
+ const base::Time prefetch_end = SafeAdd(Now(), kTestCases[i].prefetch_end);
+ const base::Time testing_end =
+ std::min(prefetch_end, Now() + kMaximumTestDuration);
+
+ std::vector<ExpectedFetchDetails> expected_fetches;
+ expected_fetches.resize(kTestCases[i].expected_num_fetches);
+ for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
+ expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
+
+ AdvanceTime(kTestCases[i].prefetch_start);
+
+ CreateFacetManager();
+ Prefetch(prefetch_end);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ testing_end, expected_fetches));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ if (kTestCases[i].prefetch_end < base::TimeDelta::Max()) {
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ } else {
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ }
+ DestroyFacetManager();
+ }
+}
+
+// Prefetches from above that have zero length.
+TEST_F(FacetManagerTest, ExpiredPrefetchDoesNothing) {
+ base::TimeDelta kPrefetchStart[] = {base::TimeDelta(),
+ GetShortTestPeriod(),
+ GetCacheSoftExpiryPeriod(),
+ GetCacheHardExpiryPeriod(),
+ base::TimeDelta::Max()};
+
+ for (base::TimeDelta prefetch_start : kPrefetchStart) {
+ SCOPED_TRACE(testing::Message() << "Prefetch start: " << prefetch_start);
+
+ if (prefetch_start < base::TimeDelta::Max()) {
+ fake_facet_manager_host()->set_fake_database_content(
+ GetTestEquivalenceClassWithUpdateTime(Now()));
+ AdvanceTime(prefetch_start);
+ } else {
+ fake_facet_manager_host()->clear_fake_database_content();
+ }
+
+ CreateFacetManager();
+ Prefetch(Now());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+ }
+}
+
+// Nested prefetches. See legend above.
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o---------------o-------o---------> t
+// : : : : :
+// [F=========================F==============================):
+// [F=========================F=======================F===========>
+// [--) : : : :
+// : [--) : : : :
+// : [----------------------): : : :
+// : [------------------------------): : :
+// : [----------------------------------------------): :
+// : [------------------------------------------------------):
+// : [------): : :
+// : [----------------------): :
+// : [------------------------------):
+// : : [----): : :
+// : : [--------------------): :
+// : : [----------------------------):
+// : : [------):
+// : : : [----):
+//
+TEST_F(FacetManagerTest, NestedPrefetches) {
+ struct {
+ base::TimeDelta prefetch_length;
+ size_t expected_num_fetches;
+ } const kFirstPrefetchParams[] = {
+ {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(), 2},
+ {base::TimeDelta::Max(), 3},
+ };
+
+ struct {
+ base::TimeDelta second_prefetch_start;
+ base::TimeDelta second_prefetch_end;
+ } const kSecondPrefetchParams[] = {
+ {base::TimeDelta(), GetShortTestPeriod()},
+ {GetShortTestPeriod(), 2 * GetShortTestPeriod()},
+ {GetShortTestPeriod(), GetCacheSoftExpiryPeriod()},
+ {GetShortTestPeriod(), GetCacheHardExpiryPeriod()},
+ {GetShortTestPeriod(), 2 * GetCacheSoftExpiryPeriod()},
+ {GetShortTestPeriod(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod()},
+ {GetCacheSoftExpiryPeriod(), GetCacheHardExpiryPeriod()},
+ {GetCacheSoftExpiryPeriod(), 2 * GetCacheSoftExpiryPeriod()},
+ {GetCacheSoftExpiryPeriod(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod()},
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod()},
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 2 * GetCacheSoftExpiryPeriod()},
+ {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod()},
+ {2 * GetCacheSoftExpiryPeriod(),
+ GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod()},
+ {2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod()}};
+
+ const base::TimeDelta kExpectedFetchTimes[] = {
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ 2 * GetCacheSoftExpiryPeriod()};
+
+ const base::TimeDelta kTestDuration =
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+
+ for (size_t j = 0; j < arraysize(kFirstPrefetchParams); ++j) {
+ for (size_t i = 0; i < arraysize(kSecondPrefetchParams); ++i) {
+ SCOPED_TRACE(testing::Message() << "Test case: #" << j << "." << i);
+
+ fake_facet_manager_host()->clear_fake_database_content();
+
+ std::vector<ExpectedFetchDetails> expected_fetches;
+ expected_fetches.resize(kFirstPrefetchParams[j].expected_num_fetches);
+ for (size_t f = 0; f < kFirstPrefetchParams[j].expected_num_fetches; ++f)
+ expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
+
+ CreateFacetManager();
+ Prefetch(SafeAdd(Now(), kFirstPrefetchParams[j].prefetch_length));
+ SchedulePrefetch(Now() + kSecondPrefetchParams[i].second_prefetch_start,
+ Now() + kSecondPrefetchParams[i].second_prefetch_end);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + kTestDuration, expected_fetches));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ if (kFirstPrefetchParams[j].prefetch_length < base::TimeDelta::Max()) {
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ } else {
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ }
+
+ DestroyFacetManager();
+ }
+ }
+}
+
+// Overlapping prefetches. See legend above.
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o---------------o-------o---------> t
+// : : : : :
+// [F================================): : :
+// : [--------------------F------------------------------):
+// : [--------------------F-----------------------F----------->
+// : [F-----------------------------):
+// : [F----------------------F----------->
+//
+TEST_F(FacetManagerTest, OverlappingPrefetches) {
+ struct {
+ base::TimeDelta second_prefetch_start;
+ base::TimeDelta second_prefetch_end;
+ size_t expected_num_fetches;
+ } const kTestCases[] = {
+ {GetShortTestPeriod(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
+ 2},
+ {GetShortTestPeriod(), base::TimeDelta::Max(), 3},
+ {GetCacheSoftExpiryPeriod(),
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
+ 2},
+ {GetCacheSoftExpiryPeriod(), base::TimeDelta::Max(), 3}};
+
+ const base::TimeDelta kExpectedFetchTimes[] = {
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ 2 * GetCacheSoftExpiryPeriod()};
+
+ const base::TimeDelta kTestDuration =
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Test case: #" << i);
+
+ fake_facet_manager_host()->clear_fake_database_content();
+
+ std::vector<ExpectedFetchDetails> expected_fetches;
+ expected_fetches.resize(kTestCases[i].expected_num_fetches);
+ for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
+ expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
+
+ CreateFacetManager();
+ Prefetch(SafeAdd(Now(), GetCacheHardExpiryPeriod()));
+ SchedulePrefetch(Now() + kTestCases[i].second_prefetch_start,
+ SafeAdd(Now(), kTestCases[i].second_prefetch_end));
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + kTestDuration, expected_fetches));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ if (kTestCases[i].second_prefetch_end < base::TimeDelta::Max()) {
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ } else {
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ }
+
+ DestroyFacetManager();
+ }
+}
+
+// Prefetches with network fetches taking non-zero time. See legend above.
+//
+// t=0 S S+D H S+H S+H+2*D
+// / \ / / \ /
+// ---o--------------------------o-o-----o-----------------------o-o-o-----> t
+// : : : : : : :
+// [NNF------------------------------): : : :
+// [F-------------------------NNF----------------------------): : :
+// [NNNNNNNNNNNNNNNNNNNNNNNNNNF------------------------------): : :
+// : : : : : : :
+// [NNF----------------------------------) : : :
+// [F-------------------------NNF------------------------------): :
+// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNF------------------------------): :
+// [NNF-------------------------NNF------------------------------):
+// : : : : :
+// [NNN) : : : :
+// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN): :
+// [F-------------------------NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN):
+// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN):
+// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...
+//
+TEST_F(FacetManagerTest, PrefetchWithNonInstantFetches) {
+ struct {
+ base::TimeDelta prefetch_length;
+ base::TimeDelta expected_fetch_time1;
+ base::TimeDelta fetch_completion_delay1;
+ base::TimeDelta expected_fetch_time2;
+ base::TimeDelta fetch_completion_delay2;
+ } const kTestCases[] = {
+ {GetCacheHardExpiryPeriod(),
+ base::TimeDelta(),
+ GetShortTestPeriod(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()},
+ {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(),
+ base::TimeDelta(),
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod()},
+ {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(),
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()},
+ {GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
+ base::TimeDelta(),
+ GetShortTestPeriod(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()},
+ {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
+ GetShortTestPeriod(),
+ base::TimeDelta(),
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod()},
+ {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
+ GetShortTestPeriod(),
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()},
+ {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
+ 2 * GetShortTestPeriod(),
+ base::TimeDelta(),
+ GetShortTestPeriod(),
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ GetCacheSoftExpiryPeriod() + 2 * GetShortTestPeriod()},
+ {GetShortTestPeriod(),
+ base::TimeDelta(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()},
+ {GetCacheHardExpiryPeriod(),
+ base::TimeDelta(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()},
+ {GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
+ base::TimeDelta(),
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ base::TimeDelta::Max()},
+ {GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
+ base::TimeDelta(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()},
+ {base::TimeDelta::Max(),
+ base::TimeDelta(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max(),
+ base::TimeDelta::Max()}};
+
+ const base::TimeDelta kMaximumTestDuration = GetCacheSoftExpiryPeriod() +
+ GetCacheHardExpiryPeriod() +
+ 2 * GetShortTestPeriod();
+
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Test case: #" << i);
+
+ fake_facet_manager_host()->clear_fake_database_content();
+
+ const base::Time testing_end =
+ Now() + std::min(kTestCases[i].prefetch_length, kMaximumTestDuration);
+
+ std::vector<ExpectedFetchDetails> expected_fetches(1);
+ expected_fetches[0].time = Now() + kTestCases[i].expected_fetch_time1,
+ expected_fetches[0].completion_delay =
+ kTestCases[i].fetch_completion_delay1;
+ if (kTestCases[i].expected_fetch_time2 != base::TimeDelta::Max()) {
+ expected_fetches.resize(2);
+ expected_fetches[1].time = Now() + kTestCases[i].expected_fetch_time2,
+ expected_fetches[1].completion_delay =
+ kTestCases[i].fetch_completion_delay2;
+ }
+
+ CreateFacetManager();
+ Prefetch(SafeAdd(Now(), kTestCases[i].prefetch_length));
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ testing_end, expected_fetches));
+ if (kTestCases[i].prefetch_length < base::TimeDelta::Max()) {
+ EXPECT_FALSE(facet_manager()->DoesRequireFetch());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ } else {
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ }
+ DestroyFacetManager();
+ }
+}
+
+// Canceling prefetches. See legend above.
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o---------------o-------o---------> t
+// : : : : :
+// [F--X- - - - - - - - - - - - - - -): : :
+// [F-------------------------X - - -): : :
+// [F----------------------------X- -): : :
+// [F--X- - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->
+// [F-------------------------X - - - - - - - - - - - - - - - - - ->
+// [F-------------------------F--X- - - - - - - - - - - - - - - - ->
+// [F-------------------------F----------X- - - - - - - - - - - - ->
+// [F-------------------------F-----------------------X - - - - - ->
+// [F-------------------------F-----------------------F--X- - - - ->
+//
+TEST_F(FacetManagerTest, CancelPrefetch) {
+ struct {
+ base::TimeDelta prefetch_length;
+ base::TimeDelta cancel_time;
+ size_t expected_num_fetches;
+ } const kTestCases[] = {
+ {GetCacheHardExpiryPeriod(), GetShortTestPeriod(), 1},
+ {GetCacheHardExpiryPeriod(), GetCacheSoftExpiryPeriod(), 1},
+ {GetCacheHardExpiryPeriod(),
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 1},
+ {base::TimeDelta::Max(), GetShortTestPeriod(), 1},
+ {base::TimeDelta::Max(), GetCacheSoftExpiryPeriod(), 1},
+ {base::TimeDelta::Max(),
+ GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 2},
+ {base::TimeDelta::Max(),
+ GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
+ 2},
+ {base::TimeDelta::Max(), 2 * GetCacheSoftExpiryPeriod(), 2},
+ {base::TimeDelta::Max(),
+ 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
+ 3}};
+
+ const base::TimeDelta kExpectedFetchTimes[] = {
+ base::TimeDelta(),
+ GetCacheSoftExpiryPeriod(),
+ 2 * GetCacheSoftExpiryPeriod()};
+
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Test case: #" << i);
+
+ fake_facet_manager_host()->clear_fake_database_content();
+
+ std::vector<ExpectedFetchDetails> expected_fetches;
+ expected_fetches.resize(kTestCases[i].expected_num_fetches);
+ for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
+ expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
+
+ CreateFacetManager();
+ Prefetch(SafeAdd(Now(), kTestCases[i].prefetch_length));
+ ScheduleCancelPrefetch(Now() + kTestCases[i].cancel_time,
+ SafeAdd(Now(), kTestCases[i].prefetch_length));
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + kTestCases[i].cancel_time, expected_fetches));
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ AdvanceTime(GetCacheHardExpiryPeriod());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+ }
+}
+
+// Canceling in case of multiple nested prefetches. See legend above.
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o---------------o-------o---------> t
+// : : : : :
+// [F-------------------------F---------------------------X- ):
+// [---------------------------------X- - - - - - - - - - - -)
+// [--X- - - - - - - - - - - - - - - - - - - - - - - - - - -)
+//
+TEST_F(FacetManagerTest, CancelNestedPrefetches) {
+ const base::TimeDelta kPrefetchLength =
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+ const base::TimeDelta kTestDuration =
+ kPrefetchLength + 2 * GetShortTestPeriod();
+ const base::TimeDelta kLastCancelTime =
+ 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod();
+
+ std::vector<ExpectedFetchDetails> expected_fetches(2);
+ expected_fetches[0].time = Now();
+ expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
+
+ CreateFacetManager();
+ Prefetch(Now() + kPrefetchLength);
+ SchedulePrefetch(Now() + GetShortTestPeriod(),
+ Now() + kPrefetchLength + GetShortTestPeriod());
+ SchedulePrefetch(Now() + 2 * GetShortTestPeriod(),
+ Now() + kPrefetchLength + 2 * GetShortTestPeriod());
+ ScheduleCancelPrefetch(Now() + 3 * GetShortTestPeriod(),
+ Now() + 2 * GetShortTestPeriod() + kPrefetchLength);
+ ScheduleCancelPrefetch(
+ Now() + GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
+ Now() + kPrefetchLength + GetShortTestPeriod());
+ ScheduleCancelPrefetch(Now() + kLastCancelTime, Now() + kPrefetchLength);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + kLastCancelTime, expected_fetches));
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ AdvanceTime(kTestDuration - kLastCancelTime);
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+}
+
+// Canceling in case of duplicate prefetches with the same |until| value. See
+// legend above.
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o---------------o-------o---------> t
+// : : : : :
+// [F-------------------------F-----------------------F--X- - - - ->
+// [--------------------------X - - - - - - - - - - - - - - - - - ->
+// [--X - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->
+//
+TEST_F(FacetManagerTest, CancelNestedPrefetchesWithMultiplicity) {
+ const base::TimeDelta kTestPeriod = 3 * GetCacheSoftExpiryPeriod();
+ const base::TimeDelta kLastCancelTime =
+ 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod();
+
+ std::vector<ExpectedFetchDetails> expected_fetches(3);
+ expected_fetches[0].time = Now();
+ expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
+ expected_fetches[2].time = Now() + 2 * GetCacheSoftExpiryPeriod();
+
+ CreateFacetManager();
+ Prefetch(base::Time::Max());
+ SchedulePrefetch(Now() + GetShortTestPeriod(), base::Time::Max());
+ SchedulePrefetch(Now() + 2 * GetShortTestPeriod(), base::Time::Max());
+ ScheduleCancelPrefetch(Now() + GetShortTestPeriod(), base::Time::Max());
+ ScheduleCancelPrefetch(Now() + GetCacheSoftExpiryPeriod(), base::Time::Max());
+ ScheduleCancelPrefetch(Now() + kLastCancelTime, base::Time::Max());
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + kLastCancelTime, expected_fetches));
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ AdvanceTime(kTestPeriod - kLastCancelTime);
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+}
+
+TEST_F(FacetManagerTest, CancelingNonexistentPrefetchesIsSilentlyIgnored) {
+ const base::TimeDelta kPrefetchLength =
+ GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+
+ std::vector<ExpectedFetchDetails> expected_fetches(2);
+ expected_fetches[0].time = Now();
+ expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
+
+ CreateFacetManager();
+ CancelPrefetch(Now() + GetShortTestPeriod());
+ CancelPrefetch(base::Time::Max());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+
+ Prefetch(Now() + kPrefetchLength);
+ ScheduleCancelPrefetch(Now() + GetShortTestPeriod(),
+ Now() + GetShortTestPeriod());
+ ScheduleCancelPrefetch(Now() + GetShortTestPeriod(), base::Time::Max());
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + kPrefetchLength, expected_fetches));
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+}
+
+TEST_F(FacetManagerTest, CachedDataCannotBeDiscarded) {
+ CreateFacetManager();
+
+ const base::TimeDelta kPrefetchLength =
+ 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+
+ facet_manager_notifier()->set_accuracy(NotificationAccuracy::NEVER_CALLED);
+
+ const base::Time prefetch_end = Now() + kPrefetchLength;
+ std::vector<ExpectedFetchDetails> expected_fetches(1);
+ expected_fetches[0].time = Now();
+
+ Prefetch(prefetch_end);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + GetCacheSoftExpiryPeriod(), expected_fetches));
+ for (base::TimeDelta step : SamplingPoints(prefetch_end - Now())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ ASSERT_TRUE(facet_manager()->DoesRequireFetch());
+ if (DeltaNow() < GetCacheHardExpiryPeriod())
+ ExpectRequestsServedFromCache();
+ AdvanceTime(step);
+ }
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+}
+
+// RequestNotificationAtTime() ends up calling NotifyAtRequestedTime() always
+// a bit earlier than needed. This should result in NotifyAtRequestedTime()
+// being called repeatedly until the callback is finally on time, but should
+// not otherwise result in a change of behavior.
+TEST_F(FacetManagerTest, RequestedNotificationsComeTooEarly) {
+ const base::TimeDelta kTestPeriod =
+ 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+
+ facet_manager_notifier()->set_accuracy(NotificationAccuracy::TOO_EARLY);
+
+ std::vector<ExpectedFetchDetails> expected_fetches(3);
+ expected_fetches[0].time = Now();
+ expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
+ expected_fetches[2].time = Now() + 2 * GetCacheSoftExpiryPeriod();
+
+ CreateFacetManager();
+ Prefetch(Now() + kTestPeriod);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + kTestPeriod, expected_fetches));
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+}
+
+// RequestNotificationAtTime() ends up calling NotifyAtRequestedTime() always
+// a short time after desired. This may result in SignalNeedNetworkRequest()
+// coming in late, but DoesRequireFetch() should get set at the correct time.
+TEST_F(FacetManagerTest, RequestedNotificationsComeTooLate) {
+ const base::TimeDelta kPrefetchLength =
+ 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+
+ facet_manager_notifier()->set_accuracy(NotificationAccuracy::TOO_LATE);
+
+ const base::Time prefetch_end = Now() + kPrefetchLength;
+ std::vector<ExpectedFetchDetails> expected_fetches(1);
+ expected_fetches[0].time = Now();
+
+ CreateFacetManager();
+ Prefetch(prefetch_end);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + GetCacheSoftExpiryPeriod(), expected_fetches));
+
+ for (int cycle = 0; cycle < 2; ++cycle) {
+ for (base::TimeDelta step : SamplingPoints(GetShortTestPeriod())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ ASSERT_TRUE(facet_manager()->DoesRequireFetch());
+ ExpectRequestsServedFromCache();
+ AdvanceTime(step);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ CompleteFetch();
+
+ const base::TimeDelta idle_period =
+ cycle ? prefetch_end - Now() : GetCacheSoftExpiryPeriod();
+ for (base::TimeDelta step : SamplingPoints(idle_period)) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ ExpectNoFetchNeeded();
+ ExpectRequestsServedFromCache();
+ AdvanceTime(step);
+ }
+ }
+
+ ASSERT_EQ(kPrefetchLength, DeltaNow());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ AdvanceTime(GetShortTestPeriod());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+}
+
+// RequestNotificationAtTime() ends up not calling NotifyAtRequestedTime() at
+// all. This should result in SignalNeedNetworkRequest() not being called, but
+// DoesRequireFetch() should be set as long as the prefetch is active.
+TEST_F(FacetManagerTest, RequestedNotificationsNeverCome) {
+ const base::TimeDelta kPrefetchLength =
+ 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
+
+ facet_manager_notifier()->set_accuracy(NotificationAccuracy::NEVER_CALLED);
+
+ const base::Time prefetch_end = Now() + kPrefetchLength;
+ std::vector<ExpectedFetchDetails> expected_fetches(1);
+ expected_fetches[0].time = Now();
+
+ CreateFacetManager();
+ Prefetch(prefetch_end);
+ ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
+ Now() + GetCacheSoftExpiryPeriod(), expected_fetches));
+ for (base::TimeDelta step : SamplingPoints(prefetch_end - Now())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ ASSERT_TRUE(facet_manager()->DoesRequireFetch());
+ if (DeltaNow() < GetCacheHardExpiryPeriod())
+ ExpectRequestsServedFromCache();
+ AdvanceTime(step);
+ }
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_FALSE(main_task_runner()->HasPendingTask());
+ DestroyFacetManager();
+}
+
+TEST_F(FacetManagerTest, StaleCachedDataBeCanDiscardedWhilePendingFetch) {
+ CreateFacetManager();
+ ASSERT_FALSE(facet_manager()->IsCachedDataFresh());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ EXPECT_FALSE(facet_manager()->CanBeDiscarded());
+ EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
+
+ fake_facet_manager_host()->reset_need_network_request();
+}
+
+TEST_F(FacetManagerTest, CachedDataBeCanDiscardedAfterOnDemandGetAffiliatons) {
+ CreateFacetManager();
+ ASSERT_FALSE(facet_manager()->IsCachedDataFresh());
+
+ GetAffiliationsAndBranding(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ ASSERT_NO_FATAL_FAILURE(CompleteFetch());
+ ExpectConsumerSuccessCallback();
+
+ EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
+}
+
+// The cached data can be discarded (indicated by 'd') if and only if it is no
+// longer needed to be kept fresh, or if it already stale.
+//
+// t=0 S H F2+S F2+H
+// / / / / /
+// ---o--------------------------o-------o------------------o-------o------> t
+// : : :
+// [F-------------------------NNNNNNNNNNNF---)
+// ddd ddd...
+//
+TEST_F(FacetManagerTest,
+ CachedDataCanBeDiscardedAfterAndSometimesDuringPrefetch) {
+ CreateFacetManager();
+ Prefetch(Now() + GetCacheHardExpiryPeriod() + 2 * GetShortTestPeriod());
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ ASSERT_NO_FATAL_FAILURE(CompleteFetch());
+
+ for (base::TimeDelta step : SamplingPoints(GetCacheHardExpiryPeriod())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
+ AdvanceTime(step);
+ }
+
+ for (base::TimeDelta step : SamplingPoints(GetShortTestPeriod())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
+ AdvanceTime(step);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ ASSERT_NO_FATAL_FAILURE(CompleteFetch());
+
+ for (base::TimeDelta step : SamplingPoints(GetShortTestPeriod())) {
+ SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
+ EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
+ AdvanceTime(step);
+ }
+
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
+}
+
+TEST_F(FacetManagerTest, CachedDataBeCanDiscardedAfterCancelledPrefetch) {
+ CreateFacetManager();
+ Prefetch(base::Time::Max());
+ ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
+ ASSERT_NO_FATAL_FAILURE(CompleteFetch());
+
+ EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
+
+ AdvanceTime(GetShortTestPeriod());
+ CancelPrefetch(base::Time::Max());
+
+ EXPECT_TRUE(facet_manager()->CanBeDiscarded());
+ EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.cc b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.cc
new file mode 100644
index 00000000000..c926b0bb5a5
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.cc
@@ -0,0 +1,80 @@
+// 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/password_manager/core/browser/android_affiliation/fake_affiliation_api.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+ScopedFakeAffiliationAPI::ScopedFakeAffiliationAPI() {
+}
+
+ScopedFakeAffiliationAPI::~ScopedFakeAffiliationAPI() {
+ // Note that trying to provide details of dangling fetchers would be unwise,
+ // as it is quite possible that they have been destroyed already.
+ EXPECT_FALSE(HasPendingRequest())
+ << "Pending AffilitionFetcher on shutdown.\n"
+ << "Call IgnoreNextRequest() if this is intended.";
+}
+
+void ScopedFakeAffiliationAPI::AddTestEquivalenceClass(
+ const AffiliatedFacets& affiliated_facets) {
+ preset_equivalence_relation_.push_back(affiliated_facets);
+}
+
+bool ScopedFakeAffiliationAPI::HasPendingRequest() {
+ return fake_fetcher_factory_.has_pending_fetchers();
+}
+
+std::vector<FacetURI> ScopedFakeAffiliationAPI::GetNextRequestedFacets() {
+ if (fake_fetcher_factory_.has_pending_fetchers())
+ return fake_fetcher_factory_.PeekNextFetcher()->requested_facet_uris();
+ return std::vector<FacetURI>();
+}
+
+void ScopedFakeAffiliationAPI::ServeNextRequest() {
+ if (!fake_fetcher_factory_.has_pending_fetchers())
+ return;
+
+ FakeAffiliationFetcher* fetcher = fake_fetcher_factory_.PopNextFetcher();
+ std::unique_ptr<AffiliationFetcherDelegate::Result> fake_response(
+ new AffiliationFetcherDelegate::Result);
+ for (const auto& preset_equivalence_class : preset_equivalence_relation_) {
+ bool had_intersection_with_request = false;
+ for (const auto& requested_facet_uri : fetcher->requested_facet_uris()) {
+ if (std::any_of(preset_equivalence_class.begin(),
+ preset_equivalence_class.end(),
+ [&requested_facet_uri](const Facet& facet) {
+ return facet.uri == requested_facet_uri;
+ })) {
+ had_intersection_with_request = true;
+ break;
+ }
+ }
+ if (had_intersection_with_request)
+ fake_response->push_back(preset_equivalence_class);
+ }
+ fetcher->SimulateSuccess(std::move(fake_response));
+}
+
+void ScopedFakeAffiliationAPI::FailNextRequest() {
+ if (!fake_fetcher_factory_.has_pending_fetchers())
+ return;
+
+ FakeAffiliationFetcher* fetcher = fake_fetcher_factory_.PopNextFetcher();
+ fetcher->SimulateFailure();
+}
+
+void ScopedFakeAffiliationAPI::IgnoreNextRequest() {
+ if (!fake_fetcher_factory_.has_pending_fetchers())
+ return;
+ ignore_result(fake_fetcher_factory_.PopNextFetcher());
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.h b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.h
new file mode 100644
index 00000000000..a4129d6acf2
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_api.h
@@ -0,0 +1,53 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FAKE_AFFILIATION_API_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FAKE_AFFILIATION_API_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h"
+
+namespace password_manager {
+
+// Intercepts all AffiliationFetcher requests while in scope, and manufactures
+// API responses based on a set of equivalence classes predefined by the tests.
+class ScopedFakeAffiliationAPI {
+ public:
+ ScopedFakeAffiliationAPI();
+ ~ScopedFakeAffiliationAPI();
+
+ // Adds |affiliated_facets| to the set of equivalence classes that will form
+ // the basis for calculating the fake API responses.
+ void AddTestEquivalenceClass(const AffiliatedFacets& affiliated_facets);
+
+ // Returns whether or not there is at least one pending fetch.
+ bool HasPendingRequest();
+
+ // Returns the list of facet URIs being looked up by the next pending fetch;
+ // or an empty list if there are no pending fetches.
+ std::vector<FacetURI> GetNextRequestedFacets();
+
+ // Calculates the response to, and completes the next pending fetch, if any,
+ // with success.
+ void ServeNextRequest();
+
+ // Completes the next pending fetch, if any, with failure.
+ void FailNextRequest();
+
+ // Ignores the next pending request, if any, without completing it.
+ void IgnoreNextRequest();
+
+ private:
+ ScopedFakeAffiliationFetcherFactory fake_fetcher_factory_;
+ std::vector<AffiliatedFacets> preset_equivalence_relation_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFakeAffiliationAPI);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FAKE_AFFILIATION_API_H_
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
new file mode 100644
index 00000000000..e24ebe4cebc
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.cc
@@ -0,0 +1,66 @@
+// 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/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h"
+
+#include <utility>
+
+namespace password_manager {
+
+password_manager::FakeAffiliationFetcher::FakeAffiliationFetcher(
+ net::URLRequestContextGetter* request_context_getter,
+ const std::vector<FacetURI>& facet_ids,
+ AffiliationFetcherDelegate* delegate)
+ : AffiliationFetcher(request_context_getter, facet_ids, delegate) {
+}
+
+password_manager::FakeAffiliationFetcher::~FakeAffiliationFetcher() {
+}
+
+void password_manager::FakeAffiliationFetcher::SimulateSuccess(
+ std::unique_ptr<AffiliationFetcherDelegate::Result> fake_result) {
+ delegate()->OnFetchSucceeded(std::move(fake_result));
+}
+
+void password_manager::FakeAffiliationFetcher::SimulateFailure() {
+ delegate()->OnFetchFailed();
+}
+
+void password_manager::FakeAffiliationFetcher::StartRequest() {
+ // Fake. Does nothing.
+}
+
+password_manager::ScopedFakeAffiliationFetcherFactory::
+ ScopedFakeAffiliationFetcherFactory() {
+ AffiliationFetcher::SetFactoryForTesting(this);
+}
+
+password_manager::ScopedFakeAffiliationFetcherFactory::
+ ~ScopedFakeAffiliationFetcherFactory() {
+ AffiliationFetcher::SetFactoryForTesting(nullptr);
+}
+
+FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::PopNextFetcher() {
+ DCHECK(!pending_fetchers_.empty());
+ FakeAffiliationFetcher* first = pending_fetchers_.front();
+ pending_fetchers_.pop();
+ return first;
+}
+
+FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::PeekNextFetcher() {
+ DCHECK(!pending_fetchers_.empty());
+ return pending_fetchers_.front();
+}
+
+AffiliationFetcher* ScopedFakeAffiliationFetcherFactory::CreateInstance(
+ net::URLRequestContextGetter* request_context_getter,
+ const std::vector<FacetURI>& facet_ids,
+ AffiliationFetcherDelegate* delegate) {
+ FakeAffiliationFetcher* fetcher =
+ new FakeAffiliationFetcher(request_context_getter, facet_ids, delegate);
+ pending_fetchers_.push(fetcher);
+ return fetcher;
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..bf38b0cb370
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h
@@ -0,0 +1,83 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FAKE_AFFILIATION_FETCHER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FAKE_AFFILIATION_FETCHER_H_
+
+#include <memory>
+#include <queue>
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_delegate.h"
+#include "components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h"
+
+namespace password_manager {
+
+// A fake AffiliationFetcher that can be used in tests to return fake API
+// 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;
+
+ // Simulates successful completion of the request with |fake_result|. Note
+ // that the consumer may choose to destroy |this| from within this call.
+ void SimulateSuccess(
+ std::unique_ptr<AffiliationFetcherDelegate::Result> fake_result);
+
+ // Simulates completion of the request with failure. Note that the consumer
+ // may choose to destroy |this| from within this call.
+ void SimulateFailure();
+
+ private:
+ void StartRequest() override;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeAffiliationFetcher);
+};
+
+// While this factory is in scope, calls to AffiliationFetcher::Create() will
+// produce FakeAffiliationFetchers that can be used in tests to return fake API
+// responses to users of AffiliationFetcher. Nesting is not supported.
+class ScopedFakeAffiliationFetcherFactory
+ : public TestAffiliationFetcherFactory {
+ public:
+ ScopedFakeAffiliationFetcherFactory();
+ ~ScopedFakeAffiliationFetcherFactory() override;
+
+ // Returns the next FakeAffiliationFetcher instance previously produced, so
+ // that that the testing code can inject a response and simulate completion
+ // or failure of the request. The fetcher is removed from the queue of pending
+ // fetchers.
+ //
+ // Note that the factory does not retain ownership of the produced fetchers,
+ // so that the tests should ensure that the corresponding production code will
+ // not destroy them before they are accessed here.
+ FakeAffiliationFetcher* PopNextFetcher();
+
+ // Same as above, but the fetcher is not removed from the queue of pending
+ // fetchers.
+ FakeAffiliationFetcher* PeekNextFetcher();
+
+ bool has_pending_fetchers() const { return !pending_fetchers_.empty(); }
+
+ // AffiliationFetcherFactory:
+ AffiliationFetcher* CreateInstance(
+ net::URLRequestContextGetter* request_context_getter,
+ const std::vector<FacetURI>& facet_ids,
+ AffiliationFetcherDelegate* delegate) override;
+
+ private:
+ // Fakes created by this factory. The elements are owned by the production
+ // code that normally owns the result of AffiliationFetcher::Create().
+ std::queue<FakeAffiliationFetcher*> pending_fetchers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFakeAffiliationFetcherFactory);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_FAKE_AFFILIATION_FETCHER_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc
new file mode 100644
index 00000000000..22f53edf563
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 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/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+MockAffiliatedMatchHelper::MockAffiliatedMatchHelper()
+ : AffiliatedMatchHelper(nullptr,
+ base::WrapUnique<AffiliationService>(nullptr)) {}
+
+MockAffiliatedMatchHelper::~MockAffiliatedMatchHelper() {}
+
+void MockAffiliatedMatchHelper::ExpectCallToGetAffiliatedAndroidRealms(
+ const PasswordStore::FormDigest& expected_observed_form,
+ const std::vector<std::string>& results_to_return) {
+ EXPECT_CALL(*this, OnGetAffiliatedAndroidRealmsCalled(expected_observed_form))
+ .WillOnce(testing::Return(results_to_return));
+}
+
+void MockAffiliatedMatchHelper::ExpectCallToGetAffiliatedWebRealms(
+ const PasswordStore::FormDigest& expected_android_form,
+ const std::vector<std::string>& results_to_return) {
+ EXPECT_CALL(*this, OnGetAffiliatedWebRealmsCalled(expected_android_form))
+ .WillOnce(testing::Return(results_to_return));
+}
+
+void MockAffiliatedMatchHelper::
+ ExpectCallToInjectAffiliationAndBrandingInformation(
+ const std::vector<AffiliationAndBrandingInformation>&
+ results_to_inject) {
+ EXPECT_CALL(*this, OnInjectAffiliationAndBrandingInformationCalled())
+ .WillOnce(testing::Return(results_to_inject));
+}
+
+void MockAffiliatedMatchHelper::GetAffiliatedAndroidRealms(
+ const PasswordStore::FormDigest& observed_form,
+ const AffiliatedRealmsCallback& result_callback) {
+ std::vector<std::string> affiliated_android_realms =
+ OnGetAffiliatedAndroidRealmsCalled(observed_form);
+ result_callback.Run(affiliated_android_realms);
+}
+
+void MockAffiliatedMatchHelper::GetAffiliatedWebRealms(
+ const PasswordStore::FormDigest& android_form,
+ const AffiliatedRealmsCallback& result_callback) {
+ std::vector<std::string> affiliated_web_realms =
+ OnGetAffiliatedWebRealmsCalled(android_form);
+ result_callback.Run(affiliated_web_realms);
+}
+
+void MockAffiliatedMatchHelper::InjectAffiliationAndBrandingInformation(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
+ const PasswordFormsCallback& result_callback) {
+ const std::vector<AffiliationAndBrandingInformation>& information =
+ OnInjectAffiliationAndBrandingInformationCalled();
+ ASSERT_EQ(information.size(), forms.size());
+ for (size_t i = 0; i < forms.size(); ++i) {
+ forms[i]->affiliated_web_realm = information[i].affiliated_web_realm;
+ forms[i]->app_display_name = information[i].app_display_name;
+ forms[i]->app_icon_url = information[i].app_icon_url;
+ }
+ result_callback.Run(std::move(forms));
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h
new file mode 100644
index 00000000000..05095f351ff
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h
@@ -0,0 +1,78 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_MOCK_AFFILIATED_MATCH_HELPER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_MOCK_AFFILIATED_MATCH_HELPER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace autofill {
+struct PasswordForm;
+}
+
+namespace password_manager {
+
+class MockAffiliatedMatchHelper : public AffiliatedMatchHelper {
+ public:
+ // This struct mirrors the corresponding affiliation and branding information
+ // related fields from autofill::PasswordForm.
+ struct AffiliationAndBrandingInformation {
+ std::string affiliated_web_realm;
+ std::string app_display_name;
+ GURL app_icon_url;
+ };
+
+ MockAffiliatedMatchHelper();
+ ~MockAffiliatedMatchHelper() override;
+
+ // Expects GetAffiliatedAndroidRealms() to be called with the
+ // |expected_observed_form|, and will cause the result callback supplied to
+ // GetAffiliatedAndroidRealms() to be invoked with |results_to_return|.
+ void ExpectCallToGetAffiliatedAndroidRealms(
+ const PasswordStore::FormDigest& expected_observed_form,
+ const std::vector<std::string>& results_to_return);
+
+ // Expects GetAffiliatedWebRealms() to be called with the
+ // |expected_android_form|, and will cause the result callback supplied to
+ // GetAffiliatedWebRealms() to be invoked with |results_to_return|.
+ void ExpectCallToGetAffiliatedWebRealms(
+ const PasswordStore::FormDigest& expected_android_form,
+ const std::vector<std::string>& results_to_return);
+
+ void ExpectCallToInjectAffiliationAndBrandingInformation(
+ const std::vector<AffiliationAndBrandingInformation>& results_to_inject);
+
+ private:
+ MOCK_METHOD1(OnGetAffiliatedAndroidRealmsCalled,
+ std::vector<std::string>(const PasswordStore::FormDigest&));
+ MOCK_METHOD1(OnGetAffiliatedWebRealmsCalled,
+ std::vector<std::string>(const PasswordStore::FormDigest&));
+ MOCK_METHOD0(OnInjectAffiliationAndBrandingInformationCalled,
+ std::vector<AffiliationAndBrandingInformation>());
+
+ void GetAffiliatedAndroidRealms(
+ const PasswordStore::FormDigest& observed_form,
+ const AffiliatedRealmsCallback& result_callback) override;
+ void GetAffiliatedWebRealms(
+ const PasswordStore::FormDigest& android_form,
+ const AffiliatedRealmsCallback& result_callback) override;
+
+ void InjectAffiliationAndBrandingInformation(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
+ const PasswordFormsCallback& result_callback) override;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAffiliatedMatchHelper);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_MOCK_AFFILIATED_MATCH_HELPER_H_
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.cc b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.cc
new file mode 100644
index 00000000000..23a9c9ce2bd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.cc
@@ -0,0 +1,37 @@
+// 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/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+
+namespace password_manager {
+
+MockAffiliationConsumer::MockAffiliationConsumer() {
+ EXPECT_CALL(*this, OnResultCallback(testing::_, testing::_)).Times(0);
+}
+
+MockAffiliationConsumer::~MockAffiliationConsumer() {
+}
+
+void MockAffiliationConsumer::ExpectSuccessWithResult(
+ const AffiliatedFacets& expected_result) {
+ EXPECT_CALL(*this, OnResultCallback(
+ testing::UnorderedElementsAreArray(expected_result),
+ true)).Times(1);
+}
+
+void MockAffiliationConsumer::ExpectFailure() {
+ EXPECT_CALL(*this, OnResultCallback(testing::UnorderedElementsAre(), false))
+ .Times(1);
+}
+
+AffiliationService::ResultCallback
+MockAffiliationConsumer::GetResultCallback() {
+ return base::Bind(&MockAffiliationConsumer::OnResultCallback,
+ base::Unretained(this));
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.h b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.h
new file mode 100644
index 00000000000..d994555365e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.h
@@ -0,0 +1,39 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_MOCK_AFFILIATION_CONSUMER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_MOCK_AFFILIATION_CONSUMER_H_
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace password_manager {
+
+// A mock consumer of AffiliationService::GetAffiliationsAndBranding().
+class MockAffiliationConsumer {
+ public:
+ MockAffiliationConsumer();
+ ~MockAffiliationConsumer();
+
+ // Expects that the result callback will be called exactly once and that it
+ // will indicate success and return |expected_result|.
+ void ExpectSuccessWithResult(const AffiliatedFacets& expected_result);
+
+ // Expects that the result callback will be called exactly once and that it
+ // will indicate a failed lookup.
+ void ExpectFailure();
+
+ AffiliationService::ResultCallback GetResultCallback();
+
+ private:
+ MOCK_METHOD2(OnResultCallback, void(const AffiliatedFacets&, bool));
+
+ DISALLOW_COPY_AND_ASSIGN(MockAffiliationConsumer);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_MOCK_AFFILIATION_CONSUMER_H_
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
new file mode 100644
index 00000000000..0f7381285f3
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h
@@ -0,0 +1,39 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_TEST_AFFILIATION_FETCHER_FACTORY_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_TEST_AFFILIATION_FETCHER_FACTORY_H_
+
+#include <vector>
+
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+
+namespace password_manager {
+
+class FacetURI;
+class AffiliationFetcherDelegate;
+
+// Interface for a factory to be used by AffiliationFetcher::Create() in tests
+// to construct instances of test-specific AffiliationFetcher subclasses.
+//
+// The factory is registered with AffiliationFetcher::SetFactoryForTesting().
+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
+ // to the |delegate| on the same thread that creates the instance.
+ virtual AffiliationFetcher* CreateInstance(
+ net::URLRequestContextGetter* request_context_getter,
+ const std::vector<FacetURI>& facet_ids,
+ AffiliationFetcherDelegate* delegate) = 0;
+
+ protected:
+ virtual ~TestAffiliationFetcherFactory() {}
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_TEST_AFFILIATION_FETCHER_FACTORY_H_
diff --git a/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.cc b/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.cc
index 4406d199bef..ee3dd044892 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.cc
+++ b/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.cc
@@ -68,6 +68,11 @@ void CredentialManagerPasswordFormManager::ProcessMatches(
weak_factory_.GetWeakPtr()));
}
+metrics_util::CredentialSourceType
+CredentialManagerPasswordFormManager::GetCredentialSource() {
+ return metrics_util::CredentialSourceType::kCredentialManagementAPI;
+}
+
void CredentialManagerPasswordFormManager::NotifyDelegate() {
delegate_->OnProvisionalSaveComplete();
}
diff --git a/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.h b/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.h
index 4816e1768bc..6f052bbaed3 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.h
+++ b/chromium/components/password_manager/core/browser/credential_manager_password_form_manager.h
@@ -52,6 +52,8 @@ class CredentialManagerPasswordFormManager : public PasswordFormManager {
const std::vector<const autofill::PasswordForm*>& non_federated,
size_t filtered_count) override;
+ metrics_util::CredentialSourceType GetCredentialSource() override;
+
private:
// Calls OnProvisionalSaveComplete on |delegate_|.
void NotifyDelegate();
diff --git a/chromium/components/password_manager/core/browser/credential_manager_password_form_manager_unittest.cc b/chromium/components/password_manager/core/browser/credential_manager_password_form_manager_unittest.cc
index 88caa117d77..585d88e00dd 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_password_form_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/credential_manager_password_form_manager_unittest.cc
@@ -54,6 +54,7 @@ TEST_F(CredentialManagerPasswordFormManagerTest, AbortEarly) {
&client_, driver_.AsWeakPtr(), observed_form,
base::MakeUnique<PasswordForm>(observed_form), &delegate,
base::MakeUnique<StubFormSaver>(), base::MakeUnique<FakeFormFetcher>());
+ form_manager->Init(nullptr);
auto deleter = [&form_manager]() { form_manager.reset(); };
@@ -74,4 +75,18 @@ TEST_F(CredentialManagerPasswordFormManagerTest, AbortEarly) {
EXPECT_FALSE(form_manager);
}
+// Ensure that GetCredentialSource is actually overriden and returns the proper
+// value.
+TEST_F(CredentialManagerPasswordFormManagerTest, GetCredentialSource) {
+ PasswordForm observed_form;
+ MockDelegate delegate;
+ auto form_manager = base::MakeUnique<CredentialManagerPasswordFormManager>(
+ &client_, driver_.AsWeakPtr(), observed_form,
+ base::MakeUnique<PasswordForm>(observed_form), &delegate,
+ base::MakeUnique<StubFormSaver>(), base::MakeUnique<FakeFormFetcher>());
+ form_manager->Init(nullptr);
+ ASSERT_EQ(metrics_util::CredentialSourceType::kCredentialManagementAPI,
+ form_manager->GetCredentialSource());
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc b/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc
index 6901dc02dfe..f6e98544ab5 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc
+++ b/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc
@@ -13,7 +13,7 @@
#include "base/metrics/user_metrics.h"
#include "base/stl_util.h"
#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/affiliated_match_helper.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
#include "components/password_manager/core/browser/password_bubble_experiment.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
diff --git a/chromium/components/password_manager/core/browser/export/password_exporter.cc b/chromium/components/password_manager/core/browser/export/password_exporter.cc
index e95fd85d173..20ecc837622 100644
--- a/chromium/components/password_manager/core/browser/export/password_exporter.cc
+++ b/chromium/components/password_manager/core/browser/export/password_exporter.cc
@@ -8,7 +8,7 @@
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
-#include "base/task_runner.h"
+#include "base/task_scheduler/post_task.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/export/password_csv_writer.h"
@@ -28,10 +28,11 @@ void WriteToFile(const base::FilePath& path,
// static
void PasswordExporter::Export(
const base::FilePath& path,
- const std::vector<std::unique_ptr<autofill::PasswordForm>>& passwords,
- scoped_refptr<base::TaskRunner> blocking_task_runner) {
- blocking_task_runner->PostTask(
- FROM_HERE,
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>& passwords) {
+ // Posting with USER_VISIBLE priority, because the result of the export is
+ // visible to the user in the target file.
+ base::PostTaskWithTraits(
+ FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
base::Bind(&WriteToFile, path,
base::Passed(base::MakeUnique<std::string>(
PasswordCSVWriter::SerializePasswords(passwords)))));
diff --git a/chromium/components/password_manager/core/browser/export/password_exporter.h b/chromium/components/password_manager/core/browser/export/password_exporter.h
index b8a96b588a6..0422799a723 100644
--- a/chromium/components/password_manager/core/browser/export/password_exporter.h
+++ b/chromium/components/password_manager/core/browser/export/password_exporter.h
@@ -11,16 +11,11 @@
#include "base/files/file_path.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
namespace autofill {
struct PasswordForm;
}
-namespace base {
-class TaskRunner;
-}
-
namespace password_manager {
// Static-only class bundling together the API for exporting passwords into a
@@ -28,12 +23,11 @@ namespace password_manager {
class PasswordExporter {
public:
// Exports |passwords| into a file at |path|, overwriting any existing file.
- // Blocking IO tasks will be posted to |blocking_task_runner|. The format of
- // the export will be selected based on the file extension in |path|.
+ // The format of the export will be selected based on the file extension in
+ // |path|.
static void Export(
const base::FilePath& path,
- const std::vector<std::unique_ptr<autofill::PasswordForm>>& passwords,
- scoped_refptr<base::TaskRunner> blocking_task_runner);
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>& passwords);
// Returns the file extensions corresponding to supported formats.
// Inner vector indicates equivalent extensions. For example:
diff --git a/chromium/components/password_manager/core/browser/export/password_exporter_unittest.cc b/chromium/components/password_manager/core/browser/export/password_exporter_unittest.cc
index 57aefd12af7..4d48157ad65 100644
--- a/chromium/components/password_manager/core/browser/export/password_exporter_unittest.cc
+++ b/chromium/components/password_manager/core/browser/export/password_exporter_unittest.cc
@@ -10,10 +10,9 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
#include "components/autofill/core/common/password_form.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -48,11 +47,9 @@ class PasswordExporterTest : public testing::Test {
base::FilePath output_file =
temporary_dir.AppendASCII("passwords").AddExtension(provided_extension);
- PasswordExporter::Export(output_file, passwords,
- message_loop_.task_runner());
+ PasswordExporter::Export(output_file, passwords);
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
if (provided_extension != expected_extension) {
output_file = output_file.ReplaceExtension(expected_extension);
@@ -63,7 +60,7 @@ class PasswordExporterTest : public testing::Test {
}
private:
- base::MessageLoop message_loop_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(PasswordExporterTest);
};
diff --git a/chromium/components/password_manager/core/browser/facet_manager.cc b/chromium/components/password_manager/core/browser/facet_manager.cc
deleted file mode 100644
index a0e9e866737..00000000000
--- a/chromium/components/password_manager/core/browser/facet_manager.cc
+++ /dev/null
@@ -1,249 +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.
-
-// Note: Read the class comment of AffiliationService for the definition of the
-// terms used below.
-//
-// On-demand fetching strategy
-//
-// A GetAffiliations() request concerning facet X will be served from the cache
-// as long as the cache contains fresh affiliation information for facet X, that
-// is, if there is an equivalence class in the cache that contains X and has
-// been fetched less than |kCacheHardExpiryInHours| hours ago.
-//
-// Otherwise, a network request is issued against the Affiliation API as soon as
-// possible, that is, immediately if there is no fetch in flight, or right after
-// completion of the fetch in flight if there is one, provided that the required
-// data is not incidentally returned by the first fetch.
-//
-//
-// Proactive fetching strategy
-//
-// A Prefetch() request concerning facet Y can trigger an initial network fetch,
-// or periodic refetches only when:
-// * The prefetch request is not already expired, i.e., its |keep_fresh_until|
-// threshold is strictly in the future (that is, prefetch intervals are open
-// from the right).
-// * Affiliation information in the cache pertaining to facet Y will get stale
-// strictly before the specified |keep_fresh_until| threshold.
-//
-// An initial fetch will be issued as soon as possible if, in addition to the
-// two necessery conditions above, and at the time of the Prefetch() call, the
-// cache contains no affiliation information regarding facet Y, or if the data
-// in the cache for facet Y is near-stale, that is, it has been fetched more
-// than |kCacheHardExpiryInHours| hours ago.
-//
-// A refetch will be issued every time the data in the cache regarding facet Y
-// becomes near-stale, that is, exactly |kCacheSoftExpiry| hours after the last
-// fetch, provided that the above two necessary conditions are also met.
-//
-// Fetches are triggered already when the data gets near-stale, as opposed to
-// waiting until the data would get stale, in an effort to keep the data fresh
-// even in face of temporary network errors lasting no more than the difference
-// between soft and hard expiry times.
-//
-// The current fetch scheduling logic, however, can only deal with at most one
-// such 'early' fetch between taking place between the prior fetch and the
-// corresponding hard expiry time of the data, therefore it is assumed that:
-//
-// kCacheSoftExpiryInHours < kCacheHardExpiryInHours, and
-// 2 * kCacheSoftExpiryInHours > kCacheHardExpiryInHours.
-//
-//
-// Cache freshness terminology
-//
-//
-// Fetch (t=0) kCacheSoftExpiry kCacheHardExpiry
-// / / /
-// ---o------------------------o-----------------------o-----------------> t
-// | | |
-// | [-- Cache near-stale --------------------- ..
-// [--------------- Cache is fresh ----------------)[-- Cache is stale ..
-//
-
-#include "components/password_manager/core/browser/facet_manager.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/task_runner.h"
-#include "base/time/clock.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/facet_manager_host.h"
-
-namespace password_manager {
-
-// statics
-const int FacetManager::kCacheSoftExpiryInHours = 21;
-const int FacetManager::kCacheHardExpiryInHours = 24;
-
-static_assert(
- FacetManager::kCacheSoftExpiryInHours <
- FacetManager::kCacheHardExpiryInHours,
- "Soft expiry period must be shorter than the hard expiry period.");
-
-static_assert(
- 2 * FacetManager::kCacheSoftExpiryInHours >
- FacetManager::kCacheHardExpiryInHours,
- "Soft expiry period must be longer than half of the hard expiry period.");
-
-// Encapsulates the details of a pending GetAffiliations() request.
-struct FacetManager::RequestInfo {
- AffiliationService::ResultCallback callback;
- scoped_refptr<base::TaskRunner> callback_task_runner;
-};
-
-FacetManager::FacetManager(const FacetURI& facet_uri,
- FacetManagerHost* backend,
- base::Clock* clock)
- : facet_uri_(facet_uri), backend_(backend), clock_(clock) {
- AffiliatedFacetsWithUpdateTime affiliations;
- if (backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliations))
- last_update_time_ = affiliations.last_update_time;
-}
-
-FacetManager::~FacetManager() {
- // The manager will be destroyed while there are pending requests only if the
- // entire backend is going away. Fail pending requests in this case.
- for (const auto& request_info : pending_requests_)
- ServeRequestWithFailure(request_info);
-}
-
-void FacetManager::GetAffiliations(
- StrategyOnCacheMiss cache_miss_strategy,
- const AffiliationService::ResultCallback& callback,
- const scoped_refptr<base::TaskRunner>& callback_task_runner) {
- RequestInfo request_info;
- request_info.callback = callback;
- request_info.callback_task_runner = callback_task_runner;
- if (IsCachedDataFresh()) {
- AffiliatedFacetsWithUpdateTime affiliation;
- if (!backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliation)) {
- ServeRequestWithFailure(request_info);
- return;
- }
- DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_;
- ServeRequestWithSuccess(request_info, affiliation.facets);
- } else if (cache_miss_strategy == StrategyOnCacheMiss::FETCH_OVER_NETWORK) {
- pending_requests_.push_back(request_info);
- backend_->SignalNeedNetworkRequest();
- } else {
- ServeRequestWithFailure(request_info);
- }
-}
-
-void FacetManager::Prefetch(const base::Time& keep_fresh_until) {
- keep_fresh_until_thresholds_.insert(keep_fresh_until);
-
- // If an initial fetch if needed, trigger that (the refetch will be scheduled
- // once the initial fetch completes). Otherwise schedule the next refetch.
- base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
- if (next_required_fetch <= clock_->Now())
- backend_->SignalNeedNetworkRequest();
- else if (next_required_fetch < base::Time::Max())
- backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
-
- // For a finite |keep_fresh_until|, schedule a callback so that once the
- // prefetch expires, it can be removed from |keep_fresh_untils_|, and also the
- // manager can get a chance to be destroyed unless it is otherwise needed.
- if (keep_fresh_until > clock_->Now() && keep_fresh_until < base::Time::Max())
- backend_->RequestNotificationAtTime(facet_uri_, keep_fresh_until);
-}
-
-void FacetManager::CancelPrefetch(const base::Time& keep_fresh_until) {
- auto iter = keep_fresh_until_thresholds_.find(keep_fresh_until);
- if (iter != keep_fresh_until_thresholds_.end())
- keep_fresh_until_thresholds_.erase(iter);
-}
-
-void FacetManager::OnFetchSucceeded(
- const AffiliatedFacetsWithUpdateTime& affiliation) {
- last_update_time_ = affiliation.last_update_time;
- DCHECK(IsCachedDataFresh()) << facet_uri_;
- for (const auto& request_info : pending_requests_)
- ServeRequestWithSuccess(request_info, affiliation.facets);
- pending_requests_.clear();
-
- base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
- if (next_required_fetch < base::Time::Max())
- backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
-}
-
-void FacetManager::NotifyAtRequestedTime() {
- base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
- if (next_required_fetch <= clock_->Now())
- backend_->SignalNeedNetworkRequest();
- else if (next_required_fetch < base::Time::Max())
- backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
-
- auto iter_first_non_expired =
- keep_fresh_until_thresholds_.upper_bound(clock_->Now());
- keep_fresh_until_thresholds_.erase(keep_fresh_until_thresholds_.begin(),
- iter_first_non_expired);
-}
-
-bool FacetManager::CanBeDiscarded() const {
- return pending_requests_.empty() &&
- GetMaximumKeepFreshUntilThreshold() <= clock_->Now();
-}
-
-bool FacetManager::CanCachedDataBeDiscarded() const {
- return GetMaximumKeepFreshUntilThreshold() <= clock_->Now() ||
- !IsCachedDataFresh();
-}
-
-bool FacetManager::DoesRequireFetch() const {
- return (!pending_requests_.empty() && !IsCachedDataFresh()) ||
- GetNextRequiredFetchTimeDueToPrefetch() <= clock_->Now();
-}
-
-bool FacetManager::IsCachedDataFresh() const {
- return clock_->Now() < GetCacheHardExpiryTime();
-}
-
-bool FacetManager::IsCachedDataNearStale() const {
- return GetCacheSoftExpiryTime() <= clock_->Now();
-}
-
-base::Time FacetManager::GetCacheSoftExpiryTime() const {
- return last_update_time_ +
- base::TimeDelta::FromHours(kCacheSoftExpiryInHours);
-}
-
-base::Time FacetManager::GetCacheHardExpiryTime() const {
- return last_update_time_ +
- base::TimeDelta::FromHours(kCacheHardExpiryInHours);
-}
-
-base::Time FacetManager::GetMaximumKeepFreshUntilThreshold() const {
- return !keep_fresh_until_thresholds_.empty()
- ? *keep_fresh_until_thresholds_.rbegin()
- : base::Time();
-}
-
-base::Time FacetManager::GetNextRequiredFetchTimeDueToPrefetch() const {
- // If there is at least one non-expired Prefetch() request that requires the
- // data to be kept fresh until some time later than its current hard expiry
- // time, then a fetch is needed once the cached data becomes near-stale.
- if (clock_->Now() < GetMaximumKeepFreshUntilThreshold() &&
- GetCacheHardExpiryTime() < GetMaximumKeepFreshUntilThreshold()) {
- return GetCacheSoftExpiryTime();
- }
- return base::Time::Max();
-}
-
-// static
-void FacetManager::ServeRequestWithSuccess(
- const RequestInfo& request_info,
- const AffiliatedFacets& affiliation) {
- request_info.callback_task_runner->PostTask(
- FROM_HERE, base::Bind(request_info.callback, affiliation, true));
-}
-
-// static
-void FacetManager::ServeRequestWithFailure(const RequestInfo& request_info) {
- request_info.callback_task_runner->PostTask(
- FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false));
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/facet_manager.h b/chromium/components/password_manager/core/browser/facet_manager.h
deleted file mode 100644
index 5e3ee77279e..00000000000
--- a/chromium/components/password_manager/core/browser/facet_manager.h
+++ /dev/null
@@ -1,137 +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_PASSWORD_MANAGER_CORE_BROWSER_FACET_MANAGER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FACET_MANAGER_H_
-
-#include <set>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-
-namespace base {
-class Clock;
-class TaskRunner;
-} // namespace base
-
-namespace password_manager {
-
-class FacetManagerHost;
-
-// Encapsulates the state and logic required for handling affiliation requests
-// concerning a single facet. The AffiliationBackend owns one instance for each
-// facet that requires attention, and it itself implements the FacetManagerHost
-// interface to provide shared functionality needed by all FacetManagers.
-class FacetManager {
- public:
- using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
-
- // Both the |backend| and |clock| must outlive this object.
- FacetManager(const FacetURI& facet_uri,
- FacetManagerHost* backend,
- base::Clock* clock);
- ~FacetManager();
-
- // Facet-specific implementations for methods in AffiliationService of the
- // same name. See documentation in affiliation_service.h for details:
- void GetAffiliations(
- StrategyOnCacheMiss cache_miss_strategy,
- const AffiliationService::ResultCallback& callback,
- const scoped_refptr<base::TaskRunner>& callback_task_runner);
- void Prefetch(const base::Time& keep_fresh_until);
- void CancelPrefetch(const base::Time& keep_fresh_until);
-
- // Called when |affiliation| information regarding this facet has just been
- // fetched from the Affiliation API.
- void OnFetchSucceeded(const AffiliatedFacetsWithUpdateTime& affiliation);
-
- // Called by the backend when the time specified in RequestNotificationAtTime
- // has come to pass, so that |this| can perform delayed administrative tasks.
- void NotifyAtRequestedTime();
-
- // Returns whether this instance has becomes redundant, that is, it has no
- // more meaningful state than a newly created instance would have.
- bool CanBeDiscarded() const;
-
- // Returns whether or not cached data for this facet can be discarded without
- // harm when trimming the database.
- bool CanCachedDataBeDiscarded() const;
-
- // Returns whether or not affiliation information relating to this facet needs
- // to be fetched right now.
- bool DoesRequireFetch() const;
-
- // The members below are made public for the sake of tests.
-
- // Returns whether or not cached data for this facet is fresh (not stale).
- bool IsCachedDataFresh() const;
-
- // Returns whether or not cached data for this facet is near-stale or stale.
- bool IsCachedDataNearStale() const;
-
- // The duration after which cached affiliation data is considered near-stale.
- static const int kCacheSoftExpiryInHours;
-
- // The duration after which cached affiliation data is considered stale.
- static const int kCacheHardExpiryInHours;
-
- private:
- struct RequestInfo;
-
- // Returns the time when the cached data for this facet will become stale.
- // The data is considered stale with the returned time value inclusive.
- base::Time GetCacheHardExpiryTime() const;
-
- // Returns the time when cached data for this facet becomes near-stale.
- // The data is considered near-stale with the returned time value inclusive.
- base::Time GetCacheSoftExpiryTime() const;
-
- // Returns the maximum of |keep_fresh_thresholds_|, or the NULL time if the
- // set is empty.
- base::Time GetMaximumKeepFreshUntilThreshold() const;
-
- // Returns the next time affiliation data for this facet needs to be fetched
- // due to active prefetch requests, or base::Time::Max() if not at all.
- base::Time GetNextRequiredFetchTimeDueToPrefetch() const;
-
- // Posts the callback of the request described by |request_info| with success.
- static void ServeRequestWithSuccess(const RequestInfo& request_info,
- const AffiliatedFacets& affiliation);
-
- // Posts the callback of the request described by |request_info| with failure.
- static void ServeRequestWithFailure(const RequestInfo& request_info);
-
- FacetURI facet_uri_;
- FacetManagerHost* backend_;
- base::Clock* clock_;
-
- // The last time affiliation information was fetched for this facet, i.e. the
- // freshness of the data in the cache. If there is no corresponding data in
- // the database, this will contain the NULL time. Otherwise, the update time
- // in the database should match this value; it is stored to reduce disk I/O.
- base::Time last_update_time_;
-
- // Contains information about the GetAffiliations() requests that are waiting
- // for the result of looking up this facet.
- std::vector<RequestInfo> pending_requests_;
-
- // Keeps track of |keep_fresh_until| thresholds corresponding to Prefetch()
- // requests for this facet. Affiliation information for this facet must be
- // kept fresh by periodic refetches until at least the maximum time in this
- // set (exclusive).
- //
- // This is not a single timestamp but rather a multiset so that cancellation
- // of individual prefetches can be supported even if there are two requests
- // with the same |keep_fresh_until| threshold.
- std::multiset<base::Time> keep_fresh_until_thresholds_;
-
- DISALLOW_COPY_AND_ASSIGN(FacetManager);
-};
-
-} // namespace password_manager
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FACET_MANAGER_H_
diff --git a/chromium/components/password_manager/core/browser/facet_manager_host.h b/chromium/components/password_manager/core/browser/facet_manager_host.h
deleted file mode 100644
index 949616f36d1..00000000000
--- a/chromium/components/password_manager/core/browser/facet_manager_host.h
+++ /dev/null
@@ -1,36 +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_PASSWORD_MANAGER_CORE_BROWSER_FACET_MANAGER_HOST_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FACET_MANAGER_HOST_H_
-
-#include "base/macros.h"
-
-namespace password_manager {
-
-// Interface through which FacetManagers can access shared functionality
-// provided by the AffiliationBackend.
-class FacetManagerHost {
- public:
- virtual ~FacetManagerHost() {}
-
- // Reads the equivalence class containing |facet_uri| from the database and
- // returns true if found; returns false otherwise.
- virtual bool ReadAffiliationsFromDatabase(
- const FacetURI& facet_uri,
- AffiliatedFacetsWithUpdateTime* affiliations) = 0;
-
- // Signals the fetching logic that affiliation information for a facet needs
- // to be fetched immediately.
- virtual void SignalNeedNetworkRequest() = 0;
-
- // Requests that the FacetManager corresponding to |facet_uri| be notified at
- // the specified |time| so it can perform delayed administrative tasks.
- virtual void RequestNotificationAtTime(const FacetURI& facet_uri,
- base::Time time) = 0;
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FACET_MANAGER_HOST_H_
diff --git a/chromium/components/password_manager/core/browser/facet_manager_unittest.cc b/chromium/components/password_manager/core/browser/facet_manager_unittest.cc
deleted file mode 100644
index 7bb7c718eeb..00000000000
--- a/chromium/components/password_manager/core/browser/facet_manager_unittest.cc
+++ /dev/null
@@ -1,1522 +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/password_manager/core/browser/facet_manager.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/rand_util.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/time/clock.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/facet_manager_host.h"
-#include "components/password_manager/core/browser/mock_affiliation_consumer.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-
-// Helpers --------------------------------------------------------------------
-
-namespace {
-
-using StrategyOnCacheMiss = FacetManager::StrategyOnCacheMiss;
-enum class NotificationAccuracy { PERFECT, TOO_LATE, TOO_EARLY, NEVER_CALLED };
-
-// Helper class to post callbacks to FacetManager::NotifyAtRequestedTime(),
-// delayed by the requested time plus/minus a configurable error term to
-// simulate a real-life task runner.
-class TestFacetManagerNotifier {
- public:
- TestFacetManagerNotifier(
- scoped_refptr<base::TestMockTimeTaskRunner> task_runner,
- base::TimeDelta too_late_delay)
- : accuracy_(NotificationAccuracy::PERFECT),
- too_late_delay_(too_late_delay),
- task_runner_(task_runner),
- facet_manager_(nullptr) {}
-
- void Notify(base::Time time) {
- base::TimeDelta delay = time - task_runner_->Now();
- if (accuracy_ == NotificationAccuracy::TOO_LATE) {
- delay += too_late_delay_;
- } else if (accuracy_ == NotificationAccuracy::TOO_EARLY) {
- // This formula is a simple stateless solution for notifying FacetManagers
- // prematurely multiple times in a row while also ensuring that the tests
- // are still fast, with no more than log2(delay.InSeconds()) repetitions.
- delay = std::min(delay, delay / 2 + base::TimeDelta::FromSeconds(1));
- } else if (accuracy_ == NotificationAccuracy::NEVER_CALLED) {
- return;
- }
- task_runner_->PostDelayedTask(
- FROM_HERE, base::Bind(&FacetManager::NotifyAtRequestedTime,
- base::Unretained(facet_manager_)),
- delay);
- }
-
- void set_accuracy(NotificationAccuracy accuracy) { accuracy_ = accuracy; }
- void set_facet_manager(FacetManager* facet_manager_under_test) {
- facet_manager_ = facet_manager_under_test;
- }
-
- private:
- NotificationAccuracy accuracy_;
- const base::TimeDelta too_late_delay_;
- scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
- FacetManager* facet_manager_;
-
- DISALLOW_COPY_AND_ASSIGN(TestFacetManagerNotifier);
-};
-
-// Stub/mock implementation for FacetManagerHost.
-class MockFacetManagerHost : public FacetManagerHost {
- public:
- explicit MockFacetManagerHost(TestFacetManagerNotifier* notifier)
- : notifier_(notifier), signaled_need_network_request_(false) {}
-
- ~MockFacetManagerHost() override {}
-
- // Sets the |facet_uri| that will be expected to appear in calls coming from
- // the FacetManager under test.
- void set_expected_facet_uri(const FacetURI& facet_uri) {
- expected_facet_uri_ = facet_uri;
- }
-
- // Sets up fake |database_content| as the canned response to be returned to
- // the FacetManager every time it calls ReadAffiliationsFromDatabase().
- void set_fake_database_content(
- const AffiliatedFacetsWithUpdateTime& database_content) {
- fake_database_content_ = database_content;
- }
-
- void clear_fake_database_content() {
- fake_database_content_.last_update_time = base::Time();
- }
-
- // Returns whether SignalNeedNetworkRequest() has been called at least once.
- size_t signaled_need_network_request() const {
- return signaled_need_network_request_;
- }
-
- void reset_need_network_request() { signaled_need_network_request_ = false; }
-
- private:
- // FacetManagerHost:
- bool ReadAffiliationsFromDatabase(
- const FacetURI& facet_uri,
- AffiliatedFacetsWithUpdateTime* affiliations) override {
- EXPECT_EQ(expected_facet_uri_, facet_uri);
- if (fake_database_content_.last_update_time.is_null())
- return false;
- *affiliations = fake_database_content_;
- return true;
- }
-
- void SignalNeedNetworkRequest() override {
- signaled_need_network_request_ = true;
- }
-
- void RequestNotificationAtTime(const FacetURI& facet_uri,
- base::Time time) override {
- EXPECT_EQ(expected_facet_uri_, facet_uri);
- // The absolute timing of notification requests is not all that interesting,
- // only the ability to perturb it slightly, which is done by the notifier.
- notifier_->Notify(time);
- }
-
- TestFacetManagerNotifier* notifier_;
-
- FacetURI expected_facet_uri_;
- AffiliatedFacetsWithUpdateTime fake_database_content_;
- bool signaled_need_network_request_;
-
- DISALLOW_COPY_AND_ASSIGN(MockFacetManagerHost);
-};
-
-const bool kFalseTrue[] = {false, true};
-
-const char kTestFacetURI1[] = "https://one.example.com";
-const char kTestFacetURI2[] = "https://two.example.com";
-const char kTestFacetURI3[] = "https://three.example.com";
-
-AffiliatedFacets GetTestEquivalenceClass() {
- AffiliatedFacets affiliated_facets;
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI1));
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI2));
- affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURI3));
- return affiliated_facets;
-}
-
-AffiliatedFacetsWithUpdateTime GetTestEquivalenceClassWithUpdateTime(
- base::Time last_update_time) {
- AffiliatedFacetsWithUpdateTime affiliation;
- affiliation.last_update_time = last_update_time;
- affiliation.facets = GetTestEquivalenceClass();
- return affiliation;
-}
-
-base::TimeDelta GetCacheHardExpiryPeriod() {
- return base::TimeDelta::FromHours(FacetManager::kCacheHardExpiryInHours);
-}
-
-base::TimeDelta GetCacheSoftExpiryPeriod() {
- return base::TimeDelta::FromHours(FacetManager::kCacheSoftExpiryInHours);
-}
-
-base::TimeDelta GetShortTestPeriod() {
- return base::TimeDelta::FromHours(1);
-}
-
-// Returns a smallest time difference that this test cares about.
-base::TimeDelta Epsilon() {
- return base::TimeDelta::FromMicroseconds(1);
-}
-
-// Returns |time| + |delay| or the maximum time if |delay| is the maximum delta.
-base::Time SafeAdd(base::Time time, base::TimeDelta delay) {
- if (delay == base::TimeDelta::Max())
- return base::Time::Max();
- return time + delay;
-}
-
-// Subdivides a time interval of a given |duration| into zero or more intervals
-// that lend themselves to be used for sampling a quantity every that often.
-// More specifically, returns the minimum number of intervals so that each
-// sub-interval is at most GetTestShortInterval() long, and that the last of the
-// sub-intervals (if any) is exactly Epsilon() long. No intervals are returned
-// if |duration| is of length zero.
-std::vector<base::TimeDelta> SamplingPoints(base::TimeDelta duration) {
- std::vector<base::TimeDelta> deltas;
- if (duration > base::TimeDelta()) {
- while (duration > Epsilon()) {
- deltas.push_back(std::min(GetShortTestPeriod(), duration - Epsilon()));
- duration -= deltas.back();
- }
- deltas.push_back(Epsilon());
- }
- return deltas;
-}
-
-} // namespace
-
-// Test framework -------------------------------------------------------------
-
-class FacetManagerTest : public testing::Test {
- public:
- FacetManagerTest()
- : consumer_task_runner_(new base::TestSimpleTaskRunner),
- main_task_runner_(new base::TestMockTimeTaskRunner),
- main_clock_(main_task_runner_->GetMockClock()),
- facet_manager_notifier_(main_task_runner_, GetShortTestPeriod()),
- facet_manager_host_(&facet_manager_notifier_) {}
-
- protected:
- struct ExpectedFetchDetails {
- // The expected time of the fetch being triggered.
- base::Time time;
-
- // A simulated delay after which the fetch will be completed.
- // base::TimeDelta::Max() means that the fetch will be left hanging.
- base::TimeDelta completion_delay;
- };
-
- void CreateFacetManager() {
- // The order is important: FacetManager will read the DB in its constructor.
- facet_manager_host_.set_expected_facet_uri(
- FacetURI::FromCanonicalSpec(kTestFacetURI1));
- facet_manager_.reset(
- new FacetManager(FacetURI::FromCanonicalSpec(kTestFacetURI1),
- fake_facet_manager_host(), main_clock_.get()));
- facet_manager_notifier_.set_facet_manager(facet_manager_.get());
- facet_manager_creation_ = Now();
- }
-
- void DestroyFacetManager() {
- main_task_runner_->ClearPendingTasks();
- facet_manager_host_.set_expected_facet_uri(FacetURI());
- facet_manager_notifier_.set_facet_manager(nullptr);
- facet_manager_.reset();
- }
-
- void AdvanceTime(base::TimeDelta delta) {
- main_task_runner_->FastForwardBy(delta);
- }
-
- base::Time Now() { return main_task_runner_->Now(); }
-
- // Returns the elapsed time since CreateFacetManager() was last called.
- base::TimeDelta DeltaNow() { return Now() - facet_manager_creation_; }
-
- void GetAffiliations(StrategyOnCacheMiss cache_miss_strategy) {
- facet_manager()->GetAffiliations(cache_miss_strategy,
- mock_consumer()->GetResultCallback(),
- consumer_task_runner());
- }
-
- void Prefetch(base::Time until) { facet_manager()->Prefetch(until); }
- void CancelPrefetch(base::Time until) {
- facet_manager()->CancelPrefetch(until);
- }
-
- void SchedulePrefetch(base::Time start, base::Time end) {
- main_task_runner_->PostDelayedTask(
- FROM_HERE, base::Bind(&FacetManager::Prefetch,
- base::Unretained(facet_manager()), end),
- start - Now());
- }
-
- void ScheduleCancelPrefetch(base::Time cancellation_time,
- base::Time original_end_of_prefetch) {
- main_task_runner_->PostDelayedTask(
- FROM_HERE,
- base::Bind(&FacetManager::CancelPrefetch,
- base::Unretained(facet_manager()), original_end_of_prefetch),
- cancellation_time - Now());
- }
-
- // Advances time |until_time|, and verifies that a Prefetch() request has the
- // expected effects now and then (but not at |until_time|), namely that:
- // * the FacetManager cannot be discarded, and
- // * requests are served from the cache;
- // and that no fetches are triggered in this interval.
- void AdvanceTimeAndVerifyPrefetch(base::Time until_time) {
- for (base::TimeDelta step : SamplingPoints(until_time - Now())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
- ExpectRequestsServedFromCache();
- ExpectNoFetchNeeded();
- AdvanceTime(step);
- }
- }
-
- // Advances time |until_time|, and verifies that between now and then, the
- // FacetManager cannot be discarded, and a fetch is needed all the time.
- void AdvanceTimeAndExpectFetchNeeded(base::Time until_time) {
- for (base::TimeDelta step : SamplingPoints(until_time - Now())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- AdvanceTime(step);
- }
- }
-
- // Advances time |until_time|, and verifies that a Prefetch() request has the
- // expected effects between now and then (but not at |until_time|), namely:
- // * the FacetManager cannot be discarded, and
- // * requests are served from the cache;
- // and that no fetches are made exactly as prescribed in |expected_fetches|.
- void AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- base::Time until_time,
- const std::vector<ExpectedFetchDetails>& expected_fetches) {
- for (const auto& next_fetch : expected_fetches) {
- AdvanceTimeAndVerifyPrefetch(next_fetch.time);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndExpectFetchNeeded(
- std::min(until_time, SafeAdd(Now(), next_fetch.completion_delay))));
- if (next_fetch.completion_delay < base::TimeDelta::Max())
- CompleteFetch();
- }
- AdvanceTimeAndVerifyPrefetch(until_time);
- }
-
- void ExpectFetchNeeded() {
- ASSERT_TRUE(facet_manager()->DoesRequireFetch());
- ASSERT_TRUE(fake_facet_manager_host()->signaled_need_network_request());
- }
-
- void ExpectNoFetchNeeded() {
- ASSERT_FALSE(facet_manager()->DoesRequireFetch());
- ASSERT_FALSE(fake_facet_manager_host()->signaled_need_network_request());
- }
-
- void CompleteFetch() {
- AffiliatedFacetsWithUpdateTime fetch_result(
- GetTestEquivalenceClassWithUpdateTime(Now()));
- fake_facet_manager_host()->set_fake_database_content(fetch_result);
- fake_facet_manager_host()->reset_need_network_request();
- facet_manager()->OnFetchSucceeded(fetch_result);
-
- main_task_runner_->RunUntilIdle();
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- ASSERT_TRUE(facet_manager()->IsCachedDataFresh());
- }
-
- void ExpectConsumerSuccessCallback() {
- mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClass());
- consumer_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
- }
-
- void ExpectConsumerFailureCallback() {
- mock_consumer()->ExpectFailure();
- consumer_task_runner()->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_consumer());
- }
-
- void ExpectRequestsServedFromCache() {
- EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
- GetAffiliations(StrategyOnCacheMiss::FAIL);
- ExpectConsumerSuccessCallback();
- }
-
- MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
-
- base::TestSimpleTaskRunner* consumer_task_runner() {
- return consumer_task_runner_.get();
- }
-
- base::TestMockTimeTaskRunner* main_task_runner() {
- return main_task_runner_.get();
- }
-
- MockFacetManagerHost* fake_facet_manager_host() {
- return &facet_manager_host_;
- }
-
- TestFacetManagerNotifier* facet_manager_notifier() {
- return &facet_manager_notifier_;
- }
-
- FacetManager* facet_manager() { return facet_manager_.get(); }
-
- private:
- // testing::Test:
- void SetUp() override {
- ASSERT_LT(3 * GetShortTestPeriod(), GetCacheSoftExpiryPeriod());
- ASSERT_LT(GetShortTestPeriod(),
- GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod());
- }
-
- void TearDown() override { DestroyFacetManager(); }
-
- MockAffiliationConsumer mock_consumer_;
- scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
- scoped_refptr<base::TestMockTimeTaskRunner> main_task_runner_;
- std::unique_ptr<base::Clock> main_clock_;
- TestFacetManagerNotifier facet_manager_notifier_;
- MockFacetManagerHost facet_manager_host_;
-
- std::unique_ptr<FacetManager> facet_manager_;
- base::Time facet_manager_creation_;
-
- DISALLOW_COPY_AND_ASSIGN(FacetManagerTest);
-};
-
-// Tests ----------------------------------------------------------------------
-
-TEST_F(FacetManagerTest, NewInstanceCanBeDiscarded) {
- CreateFacetManager();
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
- EXPECT_FALSE(facet_manager()->DoesRequireFetch());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
-}
-
-// Both cached-only and on-demand GetAffiliations() requests should be served
-// from cache if it contains fresh data. Nothing should happen on cache expiry.
-TEST_F(FacetManagerTest, GetAffiliationsServedFromCache) {
- fake_facet_manager_host()->set_fake_database_content(
- GetTestEquivalenceClassWithUpdateTime(Now()));
- AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
-
- CreateFacetManager();
- EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
-
- GetAffiliations(StrategyOnCacheMiss::FAIL);
- ExpectConsumerSuccessCallback();
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
-
- GetAffiliations(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ExpectConsumerSuccessCallback();
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
-
- AdvanceTime(Epsilon());
-
- EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
-}
-
-// On-demand GetAffiliations() requests should trigger a fetch if the cache has
-// already stale data, or no corresponding data whatsoever. Nothing should
-// happen once the newly fetched data expires.
-TEST_F(FacetManagerTest, OnDemandGetAffiliationsRequestTriggersFetch) {
- for (const bool cache_initially_has_stale_data : kFalseTrue) {
- SCOPED_TRACE(cache_initially_has_stale_data);
-
- if (cache_initially_has_stale_data) {
- fake_facet_manager_host()->set_fake_database_content(
- GetTestEquivalenceClassWithUpdateTime(Now()));
- AdvanceTime(GetCacheHardExpiryPeriod());
- } else {
- fake_facet_manager_host()->clear_fake_database_content();
- }
-
- CreateFacetManager();
- EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
-
- GetAffiliations(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(CompleteFetch());
- ExpectConsumerSuccessCallback();
-
- AdvanceTime(GetCacheHardExpiryPeriod() - Epsilon());
- EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
-
- GetAffiliations(StrategyOnCacheMiss::FAIL);
- ExpectConsumerSuccessCallback();
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
-
- GetAffiliations(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ExpectConsumerSuccessCallback();
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
-
- AdvanceTime(Epsilon());
-
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- }
-}
-
-TEST_F(FacetManagerTest, CachedOnlyGetAffiliationsFailsDueToStaleCache) {
- CreateFacetManager();
- EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
-
- GetAffiliations(StrategyOnCacheMiss::FAIL);
- ExpectConsumerFailureCallback();
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
-}
-
-TEST_F(FacetManagerTest, GetAffiliationsFailureCallbackInvokedOnDestruction) {
- CreateFacetManager();
- EXPECT_FALSE(facet_manager()->IsCachedDataFresh());
-
- GetAffiliations(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
-
- // Leave the fetch hanging and destroy the facet manager.
- DestroyFacetManager();
-
- ExpectConsumerFailureCallback();
- fake_facet_manager_host()->reset_need_network_request();
-}
-
-// The following tests verify both typical and edge case behavior of Prefetch()
-// requests: they should prevent the FacetManager from being discarded, and keep
-// the data fresh by initial fetches and refetches (scheduled as described in
-// facet_manager.cc).
-//
-// Legend:
-// [---): Interval representing a finite Prefetch request (open from right).
-// The data should be kept fresh, the FacetManager not discarded.
-// [--->: Interval representing a indefinite Prefetch request.
-// The data should be kept fresh, the FacetManager not discarded.
-// F: Fetch (initial or refetch) should take place here.
-// Fn: The time of the n-th fetch (starting from 1).
-// D: Time interval equal to GetShortTestPeriod().
-// N: Fetch is signaled to be needed here.
-// X: A corresponding CancelPrefetch call is placed here.
-// S: |kCacheSoftExpiryInHours| hours
-// H: |kCacheHardExpiryInHours| hours
-//
-// Note: It is guaranteed that S < H and that H < 2*S.
-//
-// Prefetches with the cache is initially stale/empty:
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o---------------o-------o---------> t
-// : : : : :
-// [) : : : :
-// [F--) : : : :
-// [F------------------------): : : :
-// [F--------------------------------): : :
-// [F-------------------------F----------) : :
-// [F-------------------------F----------------------): :
-// [F-------------------------F------------------------------):
-// [F-------------------------F-----------------------F------------------>
-//
-TEST_F(FacetManagerTest, PrefetchWithEmptyOrStaleCache) {
- struct {
- base::TimeDelta prefetch_length;
- size_t expected_num_fetches;
- } const kTestCases[] = {
- // Note: Zero length prefetches are tested later.
- {GetShortTestPeriod(), 1},
- {GetCacheSoftExpiryPeriod(), 1},
- {GetCacheHardExpiryPeriod(), 1},
- {GetCacheHardExpiryPeriod() + GetShortTestPeriod(), 2},
- {GetCacheSoftExpiryPeriod() + GetCacheSoftExpiryPeriod(), 2},
- {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(), 2},
- {base::TimeDelta::Max(), 3}};
-
- const base::TimeDelta kExpectedFetchTimes[] = {
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- 2 * GetCacheSoftExpiryPeriod()};
-
- const base::TimeDelta kMaximumTestDuration = 2 * GetCacheHardExpiryPeriod();
-
- for (const bool cache_initially_stale : kFalseTrue) {
- for (size_t i = 0; i < arraysize(kTestCases); ++i) {
- SCOPED_TRACE(testing::Message() << "Test case: #" << i);
- SCOPED_TRACE(cache_initially_stale ? "Cache initially stale"
- : "Cache initially empty");
-
- if (cache_initially_stale) {
- fake_facet_manager_host()->set_fake_database_content(
- GetTestEquivalenceClassWithUpdateTime(Now()));
- AdvanceTime(GetCacheHardExpiryPeriod());
- } else {
- fake_facet_manager_host()->clear_fake_database_content();
- }
-
- std::vector<ExpectedFetchDetails> expected_fetches;
- expected_fetches.resize(kTestCases[i].expected_num_fetches);
- for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
- expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
-
- CreateFacetManager();
- Prefetch(SafeAdd(Now(), kTestCases[i].prefetch_length));
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + std::min(kTestCases[i].prefetch_length, kMaximumTestDuration),
- expected_fetches));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- if (kTestCases[i].prefetch_length < base::TimeDelta::Max()) {
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- } else {
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- }
-
- DestroyFacetManager();
- }
- }
-}
-
-// Prefetches with cached affiliation data that is fresh to some extent:
-//
-// Suppose an unrelated fetch at t=0 has resulted in affiliation information
-// being stored into the cache (freshness interval marked with '='). See legend
-// above.
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o---------------o-------o-----> t
-// [F================================): : :
-// : : : : :
-// [) : : : :
-// [--) : : : :
-// [-------------------------): : : :
-// [---------------------------------): : :
-// [--------------------------F-----------) : :
-// [--------------------------F----------------------): :
-// [--------------------------F------------------------------):
-// [--------------------------F-----------------------F------------->
-// : : : :
-// [) : : : :
-// [--) : : : :
-// [---------------------): : : :
-// [-----------------------------): : :
-// [----------------------F-----------) : :
-// [----------------------F----------------------): :
-// [----------------------F------------------------------):
-// [----------------------F-----------------------F------------->
-// : : : :
-// [) : : :
-// [----) : : :
-// [------): : :
-// [F----------) : :
-// [F---------------------): :
-// [F-----------------------------):
-// [F----------------------F------------->
-//
-// t=0 S S+D H F2+S F2+H
-// / \ / / \ /
-// ---o--------------------------o-o-----o-----------------o-------o-----> t
-// [F================================): : :
-// : : : :
-// : [) : : :
-// : [----): : :
-// : [F------) : :
-// : [F---------------------): :
-// : [F-----------------------------):
-// : [F----------------------F------------->
-//
-TEST_F(FacetManagerTest, PrefetchTriggeredFetchSchedulingAfterNonEmptyCache) {
- struct {
- base::TimeDelta prefetch_start;
- base::TimeDelta prefetch_end;
- size_t expected_num_fetches;
- } const kTestCases[] = {
- // Note: Zero length prefetches are tested later.
-
- // Prefetch starts at the exact time the data was incidentally fetched.
- {base::TimeDelta(), GetShortTestPeriod(), 0},
- {base::TimeDelta(), GetCacheSoftExpiryPeriod(), 0},
- {base::TimeDelta(), GetCacheHardExpiryPeriod(), 0},
- {base::TimeDelta(), GetCacheHardExpiryPeriod() + GetShortTestPeriod(), 1},
- {base::TimeDelta(), 2 * GetCacheSoftExpiryPeriod(), 1},
- {base::TimeDelta(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
- 1},
- {base::TimeDelta(), base::TimeDelta::Max(), 2},
-
- // Prefetch starts a short time after the unrelated fetch.
- {GetShortTestPeriod(), 2 * GetShortTestPeriod(), 0},
- {GetShortTestPeriod(), GetCacheSoftExpiryPeriod(), 0},
- {GetShortTestPeriod(), GetCacheHardExpiryPeriod(), 0},
- {GetShortTestPeriod(),
- GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
- 1},
- {GetShortTestPeriod(), 2 * GetCacheSoftExpiryPeriod(), 1},
- {GetShortTestPeriod(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
- 1},
- {GetShortTestPeriod(), base::TimeDelta::Max(), 2},
-
- // Prefetch starts at the soft expiry time of the unrelated fetch.
- {GetCacheSoftExpiryPeriod(),
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 0},
- {GetCacheSoftExpiryPeriod(), GetCacheHardExpiryPeriod(), 0},
- {GetShortTestPeriod(),
- GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
- 1},
- {GetCacheSoftExpiryPeriod(), 2 * GetCacheSoftExpiryPeriod(), 1},
- {GetCacheSoftExpiryPeriod(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
- 1},
- {GetCacheSoftExpiryPeriod(), base::TimeDelta::Max(), 2}};
-
- const base::TimeDelta kExpectedFetchTimes[] = {
- GetCacheSoftExpiryPeriod(), 2 * GetCacheSoftExpiryPeriod()};
-
- const base::TimeDelta kMaximumTestDuration = 2 * GetCacheHardExpiryPeriod();
-
- for (size_t i = 0; i < arraysize(kTestCases); ++i) {
- SCOPED_TRACE(testing::Message() << "Test case: #" << i);
-
- fake_facet_manager_host()->set_fake_database_content(
- GetTestEquivalenceClassWithUpdateTime(Now()));
-
- const base::Time prefetch_end = SafeAdd(Now(), kTestCases[i].prefetch_end);
- const base::Time testing_end =
- std::min(prefetch_end, Now() + kMaximumTestDuration);
-
- std::vector<ExpectedFetchDetails> expected_fetches;
- expected_fetches.resize(kTestCases[i].expected_num_fetches);
- for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
- expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
-
- AdvanceTime(kTestCases[i].prefetch_start);
-
- CreateFacetManager();
- Prefetch(prefetch_end);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- testing_end, expected_fetches));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- if (kTestCases[i].prefetch_end < base::TimeDelta::Max()) {
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- } else {
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- }
- DestroyFacetManager();
- }
-}
-
-// Last block of tests from above.
-TEST_F(FacetManagerTest, PrefetchTriggeredFetchSchedulingAfterNonEmptyCache2) {
- struct {
- base::TimeDelta prefetch_start;
- base::TimeDelta prefetch_end;
- size_t expected_num_fetches;
- } const kTestCases[] = {
- // Note: Zero length prefetches are tested later.
-
- // Prefetch starts between the soft and hard expiry time.
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- GetCacheHardExpiryPeriod(),
- 0},
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
- 1},
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 1},
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
- GetShortTestPeriod(),
- 1},
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- base::TimeDelta::Max(),
- 2}};
-
- const base::TimeDelta kExpectedFetchTimes[] = {
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod()};
-
- const base::TimeDelta kMaximumTestDuration = 2 * GetCacheHardExpiryPeriod();
-
- for (size_t i = 0; i < arraysize(kTestCases); ++i) {
- SCOPED_TRACE(testing::Message() << "Test case: #" << i);
-
- fake_facet_manager_host()->set_fake_database_content(
- GetTestEquivalenceClassWithUpdateTime(Now()));
-
- const base::Time prefetch_end = SafeAdd(Now(), kTestCases[i].prefetch_end);
- const base::Time testing_end =
- std::min(prefetch_end, Now() + kMaximumTestDuration);
-
- std::vector<ExpectedFetchDetails> expected_fetches;
- expected_fetches.resize(kTestCases[i].expected_num_fetches);
- for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
- expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
-
- AdvanceTime(kTestCases[i].prefetch_start);
-
- CreateFacetManager();
- Prefetch(prefetch_end);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- testing_end, expected_fetches));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- if (kTestCases[i].prefetch_end < base::TimeDelta::Max()) {
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- } else {
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- }
- DestroyFacetManager();
- }
-}
-
-// Prefetches from above that have zero length.
-TEST_F(FacetManagerTest, ExpiredPrefetchDoesNothing) {
- base::TimeDelta kPrefetchStart[] = {base::TimeDelta(),
- GetShortTestPeriod(),
- GetCacheSoftExpiryPeriod(),
- GetCacheHardExpiryPeriod(),
- base::TimeDelta::Max()};
-
- for (base::TimeDelta prefetch_start : kPrefetchStart) {
- SCOPED_TRACE(testing::Message() << "Prefetch start: " << prefetch_start);
-
- if (prefetch_start < base::TimeDelta::Max()) {
- fake_facet_manager_host()->set_fake_database_content(
- GetTestEquivalenceClassWithUpdateTime(Now()));
- AdvanceTime(prefetch_start);
- } else {
- fake_facet_manager_host()->clear_fake_database_content();
- }
-
- CreateFacetManager();
- Prefetch(Now());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
- }
-}
-
-// Nested prefetches. See legend above.
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o---------------o-------o---------> t
-// : : : : :
-// [F=========================F==============================):
-// [F=========================F=======================F===========>
-// [--) : : : :
-// : [--) : : : :
-// : [----------------------): : : :
-// : [------------------------------): : :
-// : [----------------------------------------------): :
-// : [------------------------------------------------------):
-// : [------): : :
-// : [----------------------): :
-// : [------------------------------):
-// : : [----): : :
-// : : [--------------------): :
-// : : [----------------------------):
-// : : [------):
-// : : : [----):
-//
-TEST_F(FacetManagerTest, NestedPrefetches) {
- struct {
- base::TimeDelta prefetch_length;
- size_t expected_num_fetches;
- } const kFirstPrefetchParams[] = {
- {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(), 2},
- {base::TimeDelta::Max(), 3},
- };
-
- struct {
- base::TimeDelta second_prefetch_start;
- base::TimeDelta second_prefetch_end;
- } const kSecondPrefetchParams[] = {
- {base::TimeDelta(), GetShortTestPeriod()},
- {GetShortTestPeriod(), 2 * GetShortTestPeriod()},
- {GetShortTestPeriod(), GetCacheSoftExpiryPeriod()},
- {GetShortTestPeriod(), GetCacheHardExpiryPeriod()},
- {GetShortTestPeriod(), 2 * GetCacheSoftExpiryPeriod()},
- {GetShortTestPeriod(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod()},
- {GetCacheSoftExpiryPeriod(), GetCacheHardExpiryPeriod()},
- {GetCacheSoftExpiryPeriod(), 2 * GetCacheSoftExpiryPeriod()},
- {GetCacheSoftExpiryPeriod(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod()},
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- GetCacheHardExpiryPeriod()},
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 2 * GetCacheSoftExpiryPeriod()},
- {GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod()},
- {2 * GetCacheSoftExpiryPeriod(),
- GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod()},
- {2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod()}};
-
- const base::TimeDelta kExpectedFetchTimes[] = {
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- 2 * GetCacheSoftExpiryPeriod()};
-
- const base::TimeDelta kTestDuration =
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
-
- for (size_t j = 0; j < arraysize(kFirstPrefetchParams); ++j) {
- for (size_t i = 0; i < arraysize(kSecondPrefetchParams); ++i) {
- SCOPED_TRACE(testing::Message() << "Test case: #" << j << "." << i);
-
- fake_facet_manager_host()->clear_fake_database_content();
-
- std::vector<ExpectedFetchDetails> expected_fetches;
- expected_fetches.resize(kFirstPrefetchParams[j].expected_num_fetches);
- for (size_t f = 0; f < kFirstPrefetchParams[j].expected_num_fetches; ++f)
- expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
-
- CreateFacetManager();
- Prefetch(SafeAdd(Now(), kFirstPrefetchParams[j].prefetch_length));
- SchedulePrefetch(Now() + kSecondPrefetchParams[i].second_prefetch_start,
- Now() + kSecondPrefetchParams[i].second_prefetch_end);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + kTestDuration, expected_fetches));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- if (kFirstPrefetchParams[j].prefetch_length < base::TimeDelta::Max()) {
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- } else {
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- }
-
- DestroyFacetManager();
- }
- }
-}
-
-// Overlapping prefetches. See legend above.
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o---------------o-------o---------> t
-// : : : : :
-// [F================================): : :
-// : [--------------------F------------------------------):
-// : [--------------------F-----------------------F----------->
-// : [F-----------------------------):
-// : [F----------------------F----------->
-//
-TEST_F(FacetManagerTest, OverlappingPrefetches) {
- struct {
- base::TimeDelta second_prefetch_start;
- base::TimeDelta second_prefetch_end;
- size_t expected_num_fetches;
- } const kTestCases[] = {
- {GetShortTestPeriod(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
- 2},
- {GetShortTestPeriod(), base::TimeDelta::Max(), 3},
- {GetCacheSoftExpiryPeriod(),
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
- 2},
- {GetCacheSoftExpiryPeriod(), base::TimeDelta::Max(), 3}};
-
- const base::TimeDelta kExpectedFetchTimes[] = {
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- 2 * GetCacheSoftExpiryPeriod()};
-
- const base::TimeDelta kTestDuration =
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
-
- for (size_t i = 0; i < arraysize(kTestCases); ++i) {
- SCOPED_TRACE(testing::Message() << "Test case: #" << i);
-
- fake_facet_manager_host()->clear_fake_database_content();
-
- std::vector<ExpectedFetchDetails> expected_fetches;
- expected_fetches.resize(kTestCases[i].expected_num_fetches);
- for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
- expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
-
- CreateFacetManager();
- Prefetch(SafeAdd(Now(), GetCacheHardExpiryPeriod()));
- SchedulePrefetch(Now() + kTestCases[i].second_prefetch_start,
- SafeAdd(Now(), kTestCases[i].second_prefetch_end));
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + kTestDuration, expected_fetches));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- if (kTestCases[i].second_prefetch_end < base::TimeDelta::Max()) {
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- } else {
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- }
-
- DestroyFacetManager();
- }
-}
-
-// Prefetches with network fetches taking non-zero time. See legend above.
-//
-// t=0 S S+D H S+H S+H+2*D
-// / \ / / \ /
-// ---o--------------------------o-o-----o-----------------------o-o-o-----> t
-// : : : : : : :
-// [NNF------------------------------): : : :
-// [F-------------------------NNF----------------------------): : :
-// [NNNNNNNNNNNNNNNNNNNNNNNNNNF------------------------------): : :
-// : : : : : : :
-// [NNF----------------------------------) : : :
-// [F-------------------------NNF------------------------------): :
-// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNF------------------------------): :
-// [NNF-------------------------NNF------------------------------):
-// : : : : :
-// [NNN) : : : :
-// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN): :
-// [F-------------------------NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN):
-// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN):
-// [NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...
-//
-TEST_F(FacetManagerTest, PrefetchWithNonInstantFetches) {
- struct {
- base::TimeDelta prefetch_length;
- base::TimeDelta expected_fetch_time1;
- base::TimeDelta fetch_completion_delay1;
- base::TimeDelta expected_fetch_time2;
- base::TimeDelta fetch_completion_delay2;
- } const kTestCases[] = {
- {GetCacheHardExpiryPeriod(),
- base::TimeDelta(),
- GetShortTestPeriod(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()},
- {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(),
- base::TimeDelta(),
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod()},
- {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod(),
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()},
- {GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
- base::TimeDelta(),
- GetShortTestPeriod(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()},
- {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
- GetShortTestPeriod(),
- base::TimeDelta(),
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod()},
- {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
- GetShortTestPeriod(),
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()},
- {GetCacheHardExpiryPeriod() + GetCacheSoftExpiryPeriod() +
- 2 * GetShortTestPeriod(),
- base::TimeDelta(),
- GetShortTestPeriod(),
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- GetCacheSoftExpiryPeriod() + 2 * GetShortTestPeriod()},
- {GetShortTestPeriod(),
- base::TimeDelta(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()},
- {GetCacheHardExpiryPeriod(),
- base::TimeDelta(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()},
- {GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
- base::TimeDelta(),
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- base::TimeDelta::Max()},
- {GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod(),
- base::TimeDelta(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()},
- {base::TimeDelta::Max(),
- base::TimeDelta(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max(),
- base::TimeDelta::Max()}};
-
- const base::TimeDelta kMaximumTestDuration = GetCacheSoftExpiryPeriod() +
- GetCacheHardExpiryPeriod() +
- 2 * GetShortTestPeriod();
-
- for (size_t i = 0; i < arraysize(kTestCases); ++i) {
- SCOPED_TRACE(testing::Message() << "Test case: #" << i);
-
- fake_facet_manager_host()->clear_fake_database_content();
-
- const base::Time testing_end =
- Now() + std::min(kTestCases[i].prefetch_length, kMaximumTestDuration);
-
- std::vector<ExpectedFetchDetails> expected_fetches(1);
- expected_fetches[0].time = Now() + kTestCases[i].expected_fetch_time1,
- expected_fetches[0].completion_delay =
- kTestCases[i].fetch_completion_delay1;
- if (kTestCases[i].expected_fetch_time2 != base::TimeDelta::Max()) {
- expected_fetches.resize(2);
- expected_fetches[1].time = Now() + kTestCases[i].expected_fetch_time2,
- expected_fetches[1].completion_delay =
- kTestCases[i].fetch_completion_delay2;
- }
-
- CreateFacetManager();
- Prefetch(SafeAdd(Now(), kTestCases[i].prefetch_length));
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- testing_end, expected_fetches));
- if (kTestCases[i].prefetch_length < base::TimeDelta::Max()) {
- EXPECT_FALSE(facet_manager()->DoesRequireFetch());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- } else {
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- }
- DestroyFacetManager();
- }
-}
-
-// Canceling prefetches. See legend above.
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o---------------o-------o---------> t
-// : : : : :
-// [F--X- - - - - - - - - - - - - - -): : :
-// [F-------------------------X - - -): : :
-// [F----------------------------X- -): : :
-// [F--X- - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->
-// [F-------------------------X - - - - - - - - - - - - - - - - - ->
-// [F-------------------------F--X- - - - - - - - - - - - - - - - ->
-// [F-------------------------F----------X- - - - - - - - - - - - ->
-// [F-------------------------F-----------------------X - - - - - ->
-// [F-------------------------F-----------------------F--X- - - - ->
-//
-TEST_F(FacetManagerTest, CancelPrefetch) {
- struct {
- base::TimeDelta prefetch_length;
- base::TimeDelta cancel_time;
- size_t expected_num_fetches;
- } const kTestCases[] = {
- {GetCacheHardExpiryPeriod(), GetShortTestPeriod(), 1},
- {GetCacheHardExpiryPeriod(), GetCacheSoftExpiryPeriod(), 1},
- {GetCacheHardExpiryPeriod(),
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 1},
- {base::TimeDelta::Max(), GetShortTestPeriod(), 1},
- {base::TimeDelta::Max(), GetCacheSoftExpiryPeriod(), 1},
- {base::TimeDelta::Max(),
- GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 2},
- {base::TimeDelta::Max(),
- GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
- 2},
- {base::TimeDelta::Max(), 2 * GetCacheSoftExpiryPeriod(), 2},
- {base::TimeDelta::Max(),
- 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod(),
- 3}};
-
- const base::TimeDelta kExpectedFetchTimes[] = {
- base::TimeDelta(),
- GetCacheSoftExpiryPeriod(),
- 2 * GetCacheSoftExpiryPeriod()};
-
- for (size_t i = 0; i < arraysize(kTestCases); ++i) {
- SCOPED_TRACE(testing::Message() << "Test case: #" << i);
-
- fake_facet_manager_host()->clear_fake_database_content();
-
- std::vector<ExpectedFetchDetails> expected_fetches;
- expected_fetches.resize(kTestCases[i].expected_num_fetches);
- for (size_t f = 0; f < kTestCases[i].expected_num_fetches; ++f)
- expected_fetches[f].time = Now() + kExpectedFetchTimes[f];
-
- CreateFacetManager();
- Prefetch(SafeAdd(Now(), kTestCases[i].prefetch_length));
- ScheduleCancelPrefetch(Now() + kTestCases[i].cancel_time,
- SafeAdd(Now(), kTestCases[i].prefetch_length));
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + kTestCases[i].cancel_time, expected_fetches));
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- AdvanceTime(GetCacheHardExpiryPeriod());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
- }
-}
-
-// Canceling in case of multiple nested prefetches. See legend above.
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o---------------o-------o---------> t
-// : : : : :
-// [F-------------------------F---------------------------X- ):
-// [---------------------------------X- - - - - - - - - - - -)
-// [--X- - - - - - - - - - - - - - - - - - - - - - - - - - -)
-//
-TEST_F(FacetManagerTest, CancelNestedPrefetches) {
- const base::TimeDelta kPrefetchLength =
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
- const base::TimeDelta kTestDuration =
- kPrefetchLength + 2 * GetShortTestPeriod();
- const base::TimeDelta kLastCancelTime =
- 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod();
-
- std::vector<ExpectedFetchDetails> expected_fetches(2);
- expected_fetches[0].time = Now();
- expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
-
- CreateFacetManager();
- Prefetch(Now() + kPrefetchLength);
- SchedulePrefetch(Now() + GetShortTestPeriod(),
- Now() + kPrefetchLength + GetShortTestPeriod());
- SchedulePrefetch(Now() + 2 * GetShortTestPeriod(),
- Now() + kPrefetchLength + 2 * GetShortTestPeriod());
- ScheduleCancelPrefetch(Now() + 3 * GetShortTestPeriod(),
- Now() + 2 * GetShortTestPeriod() + kPrefetchLength);
- ScheduleCancelPrefetch(
- Now() + GetCacheHardExpiryPeriod() + GetShortTestPeriod(),
- Now() + kPrefetchLength + GetShortTestPeriod());
- ScheduleCancelPrefetch(Now() + kLastCancelTime, Now() + kPrefetchLength);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + kLastCancelTime, expected_fetches));
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- AdvanceTime(kTestDuration - kLastCancelTime);
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
-}
-
-// Canceling in case of duplicate prefetches with the same |until| value. See
-// legend above.
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o---------------o-------o---------> t
-// : : : : :
-// [F-------------------------F-----------------------F--X- - - - ->
-// [--------------------------X - - - - - - - - - - - - - - - - - ->
-// [--X - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->
-//
-TEST_F(FacetManagerTest, CancelNestedPrefetchesWithMultiplicity) {
- const base::TimeDelta kTestPeriod = 3 * GetCacheSoftExpiryPeriod();
- const base::TimeDelta kLastCancelTime =
- 2 * GetCacheSoftExpiryPeriod() + GetShortTestPeriod();
-
- std::vector<ExpectedFetchDetails> expected_fetches(3);
- expected_fetches[0].time = Now();
- expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
- expected_fetches[2].time = Now() + 2 * GetCacheSoftExpiryPeriod();
-
- CreateFacetManager();
- Prefetch(base::Time::Max());
- SchedulePrefetch(Now() + GetShortTestPeriod(), base::Time::Max());
- SchedulePrefetch(Now() + 2 * GetShortTestPeriod(), base::Time::Max());
- ScheduleCancelPrefetch(Now() + GetShortTestPeriod(), base::Time::Max());
- ScheduleCancelPrefetch(Now() + GetCacheSoftExpiryPeriod(), base::Time::Max());
- ScheduleCancelPrefetch(Now() + kLastCancelTime, base::Time::Max());
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + kLastCancelTime, expected_fetches));
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- AdvanceTime(kTestPeriod - kLastCancelTime);
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
-}
-
-TEST_F(FacetManagerTest, CancelingNonexistentPrefetchesIsSilentlyIgnored) {
- const base::TimeDelta kPrefetchLength =
- GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
-
- std::vector<ExpectedFetchDetails> expected_fetches(2);
- expected_fetches[0].time = Now();
- expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
-
- CreateFacetManager();
- CancelPrefetch(Now() + GetShortTestPeriod());
- CancelPrefetch(base::Time::Max());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
-
- Prefetch(Now() + kPrefetchLength);
- ScheduleCancelPrefetch(Now() + GetShortTestPeriod(),
- Now() + GetShortTestPeriod());
- ScheduleCancelPrefetch(Now() + GetShortTestPeriod(), base::Time::Max());
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + kPrefetchLength, expected_fetches));
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
-}
-
-TEST_F(FacetManagerTest, CachedDataCannotBeDiscarded) {
- CreateFacetManager();
-
- const base::TimeDelta kPrefetchLength =
- 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
-
- facet_manager_notifier()->set_accuracy(NotificationAccuracy::NEVER_CALLED);
-
- const base::Time prefetch_end = Now() + kPrefetchLength;
- std::vector<ExpectedFetchDetails> expected_fetches(1);
- expected_fetches[0].time = Now();
-
- Prefetch(prefetch_end);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + GetCacheSoftExpiryPeriod(), expected_fetches));
- for (base::TimeDelta step : SamplingPoints(prefetch_end - Now())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- ASSERT_TRUE(facet_manager()->DoesRequireFetch());
- if (DeltaNow() < GetCacheHardExpiryPeriod())
- ExpectRequestsServedFromCache();
- AdvanceTime(step);
- }
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
-}
-
-// RequestNotificationAtTime() ends up calling NotifyAtRequestedTime() always
-// a bit earlier than needed. This should result in NotifyAtRequestedTime()
-// being called repeatedly until the callback is finally on time, but should
-// not otherwise result in a change of behavior.
-TEST_F(FacetManagerTest, RequestedNotificationsComeTooEarly) {
- const base::TimeDelta kTestPeriod =
- 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
-
- facet_manager_notifier()->set_accuracy(NotificationAccuracy::TOO_EARLY);
-
- std::vector<ExpectedFetchDetails> expected_fetches(3);
- expected_fetches[0].time = Now();
- expected_fetches[1].time = Now() + GetCacheSoftExpiryPeriod();
- expected_fetches[2].time = Now() + 2 * GetCacheSoftExpiryPeriod();
-
- CreateFacetManager();
- Prefetch(Now() + kTestPeriod);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + kTestPeriod, expected_fetches));
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
-}
-
-// RequestNotificationAtTime() ends up calling NotifyAtRequestedTime() always
-// a short time after desired. This may result in SignalNeedNetworkRequest()
-// coming in late, but DoesRequireFetch() should get set at the correct time.
-TEST_F(FacetManagerTest, RequestedNotificationsComeTooLate) {
- const base::TimeDelta kPrefetchLength =
- 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
-
- facet_manager_notifier()->set_accuracy(NotificationAccuracy::TOO_LATE);
-
- const base::Time prefetch_end = Now() + kPrefetchLength;
- std::vector<ExpectedFetchDetails> expected_fetches(1);
- expected_fetches[0].time = Now();
-
- CreateFacetManager();
- Prefetch(prefetch_end);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + GetCacheSoftExpiryPeriod(), expected_fetches));
-
- for (int cycle = 0; cycle < 2; ++cycle) {
- for (base::TimeDelta step : SamplingPoints(GetShortTestPeriod())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- ASSERT_TRUE(facet_manager()->DoesRequireFetch());
- ExpectRequestsServedFromCache();
- AdvanceTime(step);
- }
-
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- CompleteFetch();
-
- const base::TimeDelta idle_period =
- cycle ? prefetch_end - Now() : GetCacheSoftExpiryPeriod();
- for (base::TimeDelta step : SamplingPoints(idle_period)) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- ExpectNoFetchNeeded();
- ExpectRequestsServedFromCache();
- AdvanceTime(step);
- }
- }
-
- ASSERT_EQ(kPrefetchLength, DeltaNow());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- AdvanceTime(GetShortTestPeriod());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
-}
-
-// RequestNotificationAtTime() ends up not calling NotifyAtRequestedTime() at
-// all. This should result in SignalNeedNetworkRequest() not being called, but
-// DoesRequireFetch() should be set as long as the prefetch is active.
-TEST_F(FacetManagerTest, RequestedNotificationsNeverCome) {
- const base::TimeDelta kPrefetchLength =
- 2 * GetCacheSoftExpiryPeriod() + GetCacheHardExpiryPeriod();
-
- facet_manager_notifier()->set_accuracy(NotificationAccuracy::NEVER_CALLED);
-
- const base::Time prefetch_end = Now() + kPrefetchLength;
- std::vector<ExpectedFetchDetails> expected_fetches(1);
- expected_fetches[0].time = Now();
-
- CreateFacetManager();
- Prefetch(prefetch_end);
- ASSERT_NO_FATAL_FAILURE(AdvanceTimeAndVerifyPrefetchWithFetchesAt(
- Now() + GetCacheSoftExpiryPeriod(), expected_fetches));
- for (base::TimeDelta step : SamplingPoints(prefetch_end - Now())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- ASSERT_TRUE(facet_manager()->DoesRequireFetch());
- if (DeltaNow() < GetCacheHardExpiryPeriod())
- ExpectRequestsServedFromCache();
- AdvanceTime(step);
- }
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_FALSE(main_task_runner()->HasPendingTask());
- DestroyFacetManager();
-}
-
-TEST_F(FacetManagerTest, StaleCachedDataBeCanDiscardedWhilePendingFetch) {
- CreateFacetManager();
- ASSERT_FALSE(facet_manager()->IsCachedDataFresh());
-
- GetAffiliations(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- EXPECT_FALSE(facet_manager()->CanBeDiscarded());
- EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
-
- fake_facet_manager_host()->reset_need_network_request();
-}
-
-TEST_F(FacetManagerTest, CachedDataBeCanDiscardedAfterOnDemandGetAffiliatons) {
- CreateFacetManager();
- ASSERT_FALSE(facet_manager()->IsCachedDataFresh());
-
- GetAffiliations(StrategyOnCacheMiss::FETCH_OVER_NETWORK);
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- ASSERT_NO_FATAL_FAILURE(CompleteFetch());
- ExpectConsumerSuccessCallback();
-
- EXPECT_TRUE(facet_manager()->IsCachedDataFresh());
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
-}
-
-// The cached data can be discarded (indicated by 'd') if and only if it is no
-// longer needed to be kept fresh, or if it already stale.
-//
-// t=0 S H F2+S F2+H
-// / / / / /
-// ---o--------------------------o-------o------------------o-------o------> t
-// : : :
-// [F-------------------------NNNNNNNNNNNF---)
-// ddd ddd...
-//
-TEST_F(FacetManagerTest,
- CachedDataCanBeDiscardedAfterAndSometimesDuringPrefetch) {
- CreateFacetManager();
- Prefetch(Now() + GetCacheHardExpiryPeriod() + 2 * GetShortTestPeriod());
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- ASSERT_NO_FATAL_FAILURE(CompleteFetch());
-
- for (base::TimeDelta step : SamplingPoints(GetCacheHardExpiryPeriod())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
- AdvanceTime(step);
- }
-
- for (base::TimeDelta step : SamplingPoints(GetShortTestPeriod())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
- AdvanceTime(step);
- }
-
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- ASSERT_NO_FATAL_FAILURE(CompleteFetch());
-
- for (base::TimeDelta step : SamplingPoints(GetShortTestPeriod())) {
- SCOPED_TRACE(testing::Message() << "dT: " << DeltaNow());
- EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
- AdvanceTime(step);
- }
-
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
-}
-
-TEST_F(FacetManagerTest, CachedDataBeCanDiscardedAfterCancelledPrefetch) {
- CreateFacetManager();
- Prefetch(base::Time::Max());
- ASSERT_NO_FATAL_FAILURE(ExpectFetchNeeded());
- ASSERT_NO_FATAL_FAILURE(CompleteFetch());
-
- EXPECT_FALSE(facet_manager()->CanCachedDataBeDiscarded());
-
- AdvanceTime(GetShortTestPeriod());
- CancelPrefetch(base::Time::Max());
-
- EXPECT_TRUE(facet_manager()->CanBeDiscarded());
- EXPECT_TRUE(facet_manager()->CanCachedDataBeDiscarded());
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/fake_affiliation_api.cc b/chromium/components/password_manager/core/browser/fake_affiliation_api.cc
deleted file mode 100644
index 6db19f0ca52..00000000000
--- a/chromium/components/password_manager/core/browser/fake_affiliation_api.cc
+++ /dev/null
@@ -1,78 +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/password_manager/core/browser/fake_affiliation_api.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-
-ScopedFakeAffiliationAPI::ScopedFakeAffiliationAPI() {
-}
-
-ScopedFakeAffiliationAPI::~ScopedFakeAffiliationAPI() {
- // Note that trying to provide details of dangling fetchers would be unwise,
- // as it is quite possible that they have been destroyed already.
- EXPECT_FALSE(HasPendingRequest())
- << "Pending AffilitionFetcher on shutdown.\n"
- << "Call IgnoreNextRequest() if this is intended.";
-}
-
-void ScopedFakeAffiliationAPI::AddTestEquivalenceClass(
- const AffiliatedFacets& affiliated_facets) {
- preset_equivalence_relation_.push_back(affiliated_facets);
-}
-
-bool ScopedFakeAffiliationAPI::HasPendingRequest() {
- return fake_fetcher_factory_.has_pending_fetchers();
-}
-
-std::vector<FacetURI> ScopedFakeAffiliationAPI::GetNextRequestedFacets() {
- if (fake_fetcher_factory_.has_pending_fetchers())
- return fake_fetcher_factory_.PeekNextFetcher()->requested_facet_uris();
- return std::vector<FacetURI>();
-}
-
-void ScopedFakeAffiliationAPI::ServeNextRequest() {
- if (!fake_fetcher_factory_.has_pending_fetchers())
- return;
-
- FakeAffiliationFetcher* fetcher = fake_fetcher_factory_.PopNextFetcher();
- std::unique_ptr<AffiliationFetcherDelegate::Result> fake_response(
- new AffiliationFetcherDelegate::Result);
- for (const auto& preset_equivalence_class : preset_equivalence_relation_) {
- bool had_intersection_with_request = false;
- for (const auto& requested_facet_uri : fetcher->requested_facet_uris()) {
- if (std::find(preset_equivalence_class.begin(),
- preset_equivalence_class.end(),
- requested_facet_uri) != preset_equivalence_class.end()) {
- had_intersection_with_request = true;
- break;
- }
- }
- if (had_intersection_with_request)
- fake_response->push_back(preset_equivalence_class);
- }
- fetcher->SimulateSuccess(std::move(fake_response));
-}
-
-void ScopedFakeAffiliationAPI::FailNextRequest() {
- if (!fake_fetcher_factory_.has_pending_fetchers())
- return;
-
- FakeAffiliationFetcher* fetcher = fake_fetcher_factory_.PopNextFetcher();
- fetcher->SimulateFailure();
-}
-
-void ScopedFakeAffiliationAPI::IgnoreNextRequest() {
- if (!fake_fetcher_factory_.has_pending_fetchers())
- return;
- ignore_result(fake_fetcher_factory_.PopNextFetcher());
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/fake_affiliation_api.h b/chromium/components/password_manager/core/browser/fake_affiliation_api.h
deleted file mode 100644
index 98d4b2e9521..00000000000
--- a/chromium/components/password_manager/core/browser/fake_affiliation_api.h
+++ /dev/null
@@ -1,53 +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_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_API_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_API_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-#include "components/password_manager/core/browser/fake_affiliation_fetcher.h"
-
-namespace password_manager {
-
-// Intercepts all AffiliationFetcher requests while in scope, and manufactures
-// API responses based on a set of equivalence classes predefined by the tests.
-class ScopedFakeAffiliationAPI {
- public:
- ScopedFakeAffiliationAPI();
- ~ScopedFakeAffiliationAPI();
-
- // Adds |affiliated_facets| to the set of equivalence classes that will form
- // the basis for calculating the fake API responses.
- void AddTestEquivalenceClass(const AffiliatedFacets& affiliated_facets);
-
- // Returns whether or not there is at least one pending fetch.
- bool HasPendingRequest();
-
- // Returns the list of facet URIs being looked up by the next pending fetch;
- // or an empty list if there are no pending fetches.
- std::vector<FacetURI> GetNextRequestedFacets();
-
- // Calculates the response to, and completes the next pending fetch, if any,
- // with success.
- void ServeNextRequest();
-
- // Completes the next pending fetch, if any, with failure.
- void FailNextRequest();
-
- // Ignores the next pending request, if any, without completing it.
- void IgnoreNextRequest();
-
- private:
- ScopedFakeAffiliationFetcherFactory fake_fetcher_factory_;
- std::vector<AffiliatedFacets> preset_equivalence_relation_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFakeAffiliationAPI);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_API_H_
diff --git a/chromium/components/password_manager/core/browser/fake_affiliation_fetcher.cc b/chromium/components/password_manager/core/browser/fake_affiliation_fetcher.cc
deleted file mode 100644
index bad984f18ad..00000000000
--- a/chromium/components/password_manager/core/browser/fake_affiliation_fetcher.cc
+++ /dev/null
@@ -1,66 +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/password_manager/core/browser/fake_affiliation_fetcher.h"
-
-#include <utility>
-
-namespace password_manager {
-
-password_manager::FakeAffiliationFetcher::FakeAffiliationFetcher(
- net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_ids,
- AffiliationFetcherDelegate* delegate)
- : AffiliationFetcher(request_context_getter, facet_ids, delegate) {
-}
-
-password_manager::FakeAffiliationFetcher::~FakeAffiliationFetcher() {
-}
-
-void password_manager::FakeAffiliationFetcher::SimulateSuccess(
- std::unique_ptr<AffiliationFetcherDelegate::Result> fake_result) {
- delegate()->OnFetchSucceeded(std::move(fake_result));
-}
-
-void password_manager::FakeAffiliationFetcher::SimulateFailure() {
- delegate()->OnFetchFailed();
-}
-
-void password_manager::FakeAffiliationFetcher::StartRequest() {
- // Fake. Does nothing.
-}
-
-password_manager::ScopedFakeAffiliationFetcherFactory::
- ScopedFakeAffiliationFetcherFactory() {
- AffiliationFetcher::SetFactoryForTesting(this);
-}
-
-password_manager::ScopedFakeAffiliationFetcherFactory::
- ~ScopedFakeAffiliationFetcherFactory() {
- AffiliationFetcher::SetFactoryForTesting(nullptr);
-}
-
-FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::PopNextFetcher() {
- DCHECK(!pending_fetchers_.empty());
- FakeAffiliationFetcher* first = pending_fetchers_.front();
- pending_fetchers_.pop();
- return first;
-}
-
-FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::PeekNextFetcher() {
- DCHECK(!pending_fetchers_.empty());
- return pending_fetchers_.front();
-}
-
-AffiliationFetcher* ScopedFakeAffiliationFetcherFactory::CreateInstance(
- net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_ids,
- AffiliationFetcherDelegate* delegate) {
- FakeAffiliationFetcher* fetcher =
- new FakeAffiliationFetcher(request_context_getter, facet_ids, delegate);
- pending_fetchers_.push(fetcher);
- return fetcher;
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/fake_affiliation_fetcher.h b/chromium/components/password_manager/core/browser/fake_affiliation_fetcher.h
deleted file mode 100644
index d2a503702df..00000000000
--- a/chromium/components/password_manager/core/browser/fake_affiliation_fetcher.h
+++ /dev/null
@@ -1,83 +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_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_FETCHER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_FETCHER_H_
-
-#include <memory>
-#include <queue>
-
-#include "base/macros.h"
-#include "components/password_manager/core/browser/affiliation_fetcher.h"
-#include "components/password_manager/core/browser/affiliation_fetcher_delegate.h"
-#include "components/password_manager/core/browser/test_affiliation_fetcher_factory.h"
-
-namespace password_manager {
-
-// A fake AffiliationFetcher that can be used in tests to return fake API
-// 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;
-
- // Simulates successful completion of the request with |fake_result|. Note
- // that the consumer may choose to destroy |this| from within this call.
- void SimulateSuccess(
- std::unique_ptr<AffiliationFetcherDelegate::Result> fake_result);
-
- // Simulates completion of the request with failure. Note that the consumer
- // may choose to destroy |this| from within this call.
- void SimulateFailure();
-
- private:
- void StartRequest() override;
-
- DISALLOW_COPY_AND_ASSIGN(FakeAffiliationFetcher);
-};
-
-// While this factory is in scope, calls to AffiliationFetcher::Create() will
-// produce FakeAffiliationFetchers that can be used in tests to return fake API
-// responses to users of AffiliationFetcher. Nesting is not supported.
-class ScopedFakeAffiliationFetcherFactory
- : public TestAffiliationFetcherFactory {
- public:
- ScopedFakeAffiliationFetcherFactory();
- ~ScopedFakeAffiliationFetcherFactory() override;
-
- // Returns the next FakeAffiliationFetcher instance previously produced, so
- // that that the testing code can inject a response and simulate completion
- // or failure of the request. The fetcher is removed from the queue of pending
- // fetchers.
- //
- // Note that the factory does not retain ownership of the produced fetchers,
- // so that the tests should ensure that the corresponding production code will
- // not destroy them before they are accessed here.
- FakeAffiliationFetcher* PopNextFetcher();
-
- // Same as above, but the fetcher is not removed from the queue of pending
- // fetchers.
- FakeAffiliationFetcher* PeekNextFetcher();
-
- bool has_pending_fetchers() const { return !pending_fetchers_.empty(); }
-
- // AffiliationFetcherFactory:
- AffiliationFetcher* CreateInstance(
- net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_ids,
- AffiliationFetcherDelegate* delegate) override;
-
- private:
- // Fakes created by this factory. The elements are owned by the production
- // code that normally owns the result of AffiliationFetcher::Create().
- std::queue<FakeAffiliationFetcher*> pending_fetchers_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFakeAffiliationFetcherFactory);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_FETCHER_H_
diff --git a/chromium/components/password_manager/core/browser/fake_form_fetcher.cc b/chromium/components/password_manager/core/browser/fake_form_fetcher.cc
index 7e7911c3eee..fcac389123f 100644
--- a/chromium/components/password_manager/core/browser/fake_form_fetcher.cc
+++ b/chromium/components/password_manager/core/browser/fake_form_fetcher.cc
@@ -4,6 +4,7 @@
#include "components/password_manager/core/browser/fake_form_fetcher.h"
+#include "base/memory/ptr_util.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/statistics_table.h"
@@ -70,7 +71,7 @@ void FakeFormFetcher::Fetch() {
}
std::unique_ptr<FormFetcher> FakeFormFetcher::Clone() {
- return nullptr;
+ return base::MakeUnique<FakeFormFetcher>();
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/fake_form_fetcher.h b/chromium/components/password_manager/core/browser/fake_form_fetcher.h
index d7c64a327a2..0d507c19b4f 100644
--- a/chromium/components/password_manager/core/browser/fake_form_fetcher.h
+++ b/chromium/components/password_manager/core/browser/fake_form_fetcher.h
@@ -96,7 +96,7 @@ class FakeFormFetcher : public FormFetcher {
// Only sets the internal state to WAITING, no call to PasswordStore.
void Fetch() override;
- // A no-op, returns null.
+ // Returns a new FakeFormFetcher.
std::unique_ptr<FormFetcher> Clone() override;
private:
diff --git a/chromium/components/password_manager/core/browser/form_fetcher.h b/chromium/components/password_manager/core/browser/form_fetcher.h
index 22bcd52aa55..b7a862af7fb 100644
--- a/chromium/components/password_manager/core/browser/form_fetcher.h
+++ b/chromium/components/password_manager/core/browser/form_fetcher.h
@@ -114,8 +114,7 @@ class FormFetcher {
virtual void Fetch() = 0;
// Creates a copy of |*this| with contains the same credentials without the
- // need for calling Fetch(). Only call this if GetState() returns NOT_WAITING,
- // otherwise the original FormFetcher does not have any data to be cloned.
+ // need for calling Fetch().
virtual std::unique_ptr<FormFetcher> Clone() = 0;
private:
diff --git a/chromium/components/password_manager/core/browser/form_fetcher_impl.cc b/chromium/components/password_manager/core/browser/form_fetcher_impl.cc
index c8127b0ebd5..543580e9c14 100644
--- a/chromium/components/password_manager/core/browser/form_fetcher_impl.cc
+++ b/chromium/components/password_manager/core/browser/form_fetcher_impl.cc
@@ -269,13 +269,17 @@ void FormFetcherImpl::Fetch() {
}
std::unique_ptr<FormFetcher> FormFetcherImpl::Clone() {
- DCHECK_EQ(State::NOT_WAITING, state_);
-
// Create the copy without the "HTTPS migration" activated. If it was needed,
// then it was done by |this| already.
auto result = base::MakeUnique<FormFetcherImpl>(
form_digest_, client_, false, should_query_suppressed_forms_);
+ if (state_ != State::NOT_WAITING) {
+ // There are no store results to copy, trigger a Fetch on the clone instead.
+ result->Fetch();
+ return std::move(result);
+ }
+
result->non_federated_ = MakeCopies(this->non_federated_);
result->federated_ = MakeCopies(this->federated_);
result->interactions_stats_ = this->interactions_stats_;
@@ -299,7 +303,6 @@ std::unique_ptr<FormFetcher> FormFetcherImpl::Clone() {
result->state_ = this->state_;
result->need_to_refetch_ = this->need_to_refetch_;
- // TODO(crbug.com/703565): remove std::move() once Xcode 9.0+ is required.
return std::move(result);
}
diff --git a/chromium/components/password_manager/core/browser/form_saver.h b/chromium/components/password_manager/core/browser/form_saver.h
index 5c82c374e23..634c6a776ed 100644
--- a/chromium/components/password_manager/core/browser/form_saver.h
+++ b/chromium/components/password_manager/core/browser/form_saver.h
@@ -69,6 +69,9 @@ class FormSaver {
std::map<base::string16, const autofill::PasswordForm*>* best_matches,
const autofill::PasswordForm** preferred_match) = 0;
+ // Creates a new FormSaver with the same state as |*this|.
+ virtual std::unique_ptr<FormSaver> Clone() = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(FormSaver);
};
diff --git a/chromium/components/password_manager/core/browser/form_saver_impl.cc b/chromium/components/password_manager/core/browser/form_saver_impl.cc
index c64b7a0fd60..8a162b4986f 100644
--- a/chromium/components/password_manager/core/browser/form_saver_impl.cc
+++ b/chromium/components/password_manager/core/browser/form_saver_impl.cc
@@ -7,7 +7,7 @@
#include <memory>
#include <vector>
-#include "base/auto_reset.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
@@ -93,6 +93,13 @@ void FormSaverImpl::WipeOutdatedCopies(
}
}
+std::unique_ptr<FormSaver> FormSaverImpl::Clone() {
+ auto result = base::MakeUnique<FormSaverImpl>(store_);
+ if (presaved_)
+ result->presaved_ = base::MakeUnique<PasswordForm>(*presaved_);
+ return std::move(result);
+}
+
void FormSaverImpl::SaveImpl(
const PasswordForm& pending,
bool is_new_login,
@@ -102,23 +109,19 @@ void FormSaverImpl::SaveImpl(
DCHECK(pending.preferred);
DCHECK(!pending.blacklisted_by_user);
- base::AutoReset<const std::map<base::string16, const PasswordForm*>*> ar1(
- &best_matches_, &best_matches);
- base::AutoReset<const PasswordForm*> ar2(&pending_, &pending);
-
- UpdatePreferredLoginState();
+ UpdatePreferredLoginState(pending.username_value, best_matches);
if (presaved_) {
- store_->UpdateLoginWithPrimaryKey(*pending_, *presaved_);
+ store_->UpdateLoginWithPrimaryKey(pending, *presaved_);
presaved_ = nullptr;
} else if (is_new_login) {
- store_->AddLogin(*pending_);
- if (!pending_->username_value.empty())
- DeleteEmptyUsernameCredentials();
+ store_->AddLogin(pending);
+ if (!pending.username_value.empty())
+ DeleteEmptyUsernameCredentials(pending, best_matches);
} else {
if (old_primary_key)
- store_->UpdateLoginWithPrimaryKey(*pending_, *old_primary_key);
+ store_->UpdateLoginWithPrimaryKey(pending, *old_primary_key);
else
- store_->UpdateLogin(*pending_);
+ store_->UpdateLogin(pending);
}
if (credentials_to_update) {
@@ -128,9 +131,10 @@ void FormSaverImpl::SaveImpl(
}
}
-void FormSaverImpl::UpdatePreferredLoginState() {
- const base::string16& preferred_username = pending_->username_value;
- for (const auto& key_value_pair : *best_matches_) {
+void FormSaverImpl::UpdatePreferredLoginState(
+ const base::string16& preferred_username,
+ const std::map<base::string16, const PasswordForm*>& best_matches) {
+ for (const auto& key_value_pair : best_matches) {
const PasswordForm& form = *key_value_pair.second;
if (form.preferred && !form.is_public_suffix_match &&
form.username_value != preferred_username) {
@@ -142,13 +146,15 @@ void FormSaverImpl::UpdatePreferredLoginState() {
}
}
-void FormSaverImpl::DeleteEmptyUsernameCredentials() {
- DCHECK(!pending_->username_value.empty());
+void FormSaverImpl::DeleteEmptyUsernameCredentials(
+ const PasswordForm& pending,
+ const std::map<base::string16, const PasswordForm*>& best_matches) {
+ DCHECK(!pending.username_value.empty());
- for (const auto& match : *best_matches_) {
+ for (const auto& match : best_matches) {
const PasswordForm* form = match.second;
if (!form->is_public_suffix_match && form->username_value.empty() &&
- form->password_value == pending_->password_value) {
+ form->password_value == pending.password_value) {
store_->RemoveLogin(*form);
}
}
diff --git a/chromium/components/password_manager/core/browser/form_saver_impl.h b/chromium/components/password_manager/core/browser/form_saver_impl.h
index c881c4f219c..7fa8459873e 100644
--- a/chromium/components/password_manager/core/browser/form_saver_impl.h
+++ b/chromium/components/password_manager/core/browser/form_saver_impl.h
@@ -41,6 +41,7 @@ class FormSaverImpl : public FormSaver {
const autofill::PasswordForm& pending,
std::map<base::string16, const autofill::PasswordForm*>* best_matches,
const autofill::PasswordForm** preferred_match) override;
+ std::unique_ptr<FormSaver> Clone() override;
private:
// Implements both Save and Update, because those methods share most of the
@@ -53,25 +54,24 @@ class FormSaverImpl : public FormSaver {
const std::vector<autofill::PasswordForm>* credentials_to_update,
const autofill::PasswordForm* old_primary_key);
- // Marks all of |best_matches_| as not preferred unless the username is
+ // Marks all of |best_matches| as not preferred unless the username is
// |preferred_username| or the credential is PSL matched.
- void UpdatePreferredLoginState();
+ void UpdatePreferredLoginState(
+ const base::string16& preferred_username,
+ const std::map<base::string16, const autofill::PasswordForm*>&
+ best_matches);
// Iterates over all |best_matches| and deletes from the password store all
// which are not PSL-matched, have an empty username, and a password equal to
- // |pending_->password_value|.
- void DeleteEmptyUsernameCredentials();
+ // |pending.password_value|.
+ void DeleteEmptyUsernameCredentials(
+ const autofill::PasswordForm& pending,
+ const std::map<base::string16, const autofill::PasswordForm*>&
+ best_matches);
// Cached pointer to the PasswordStore.
PasswordStore* const store_;
- // Caches the best matches during a call to Save() or Update().
- const std::map<base::string16, const autofill::PasswordForm*>* best_matches_ =
- nullptr;
-
- // Caches the pending credential during a call to Save() or Update().
- const autofill::PasswordForm* pending_ = nullptr;
-
// Stores the pre-saved credential (happens during password generation).
std::unique_ptr<autofill::PasswordForm> presaved_;
diff --git a/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc b/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc
index d626697ddcd..562596000e2 100644
--- a/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc
+++ b/chromium/components/password_manager/core/browser/form_saver_impl_unittest.cc
@@ -591,4 +591,29 @@ TEST_F(FormSaverImplTest, WipeOutdatedCopies_NotOutdated) {
form_saver_.WipeOutdatedCopies(pending, &best_matches, &preferred);
}
+// Check that presaving a password once in original and then once in clone
+// results in the clone calling update, not a fresh save.
+TEST_F(FormSaverImplTest, PresaveGeneratedPassword_CloneUpdates) {
+ PasswordForm generated = CreatePending("nameofuser", "wordToP4a55");
+
+ EXPECT_CALL(*mock_store_, AddLogin(_));
+ form_saver_.PresaveGeneratedPassword(generated);
+ std::unique_ptr<FormSaver> clone = form_saver_.Clone();
+ EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _));
+ clone->PresaveGeneratedPassword(generated);
+}
+
+// Check that a clone can still work after the original is destroyed.
+TEST_F(FormSaverImplTest, PresaveGeneratedPassword_CloneSurvives) {
+ auto original = base::MakeUnique<FormSaverImpl>(mock_store_.get());
+ PasswordForm generated = CreatePending("nameofuser", "wordToP4a55");
+
+ EXPECT_CALL(*mock_store_, AddLogin(_));
+ original->PresaveGeneratedPassword(generated);
+ std::unique_ptr<FormSaver> clone = original->Clone();
+ original.reset();
+ EXPECT_CALL(*mock_store_, UpdateLoginWithPrimaryKey(_, _));
+ clone->PresaveGeneratedPassword(generated);
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/hash_password_manager.cc b/chromium/components/password_manager/core/browser/hash_password_manager.cc
new file mode 100644
index 00000000000..57b08c1dfa6
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/hash_password_manager.cc
@@ -0,0 +1,128 @@
+// 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/hash_password_manager.h"
+
+#include "base/base64.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/os_crypt/os_crypt.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "crypto/random.h"
+
+namespace {
+constexpr size_t kSyncPasswordSaltLength = 16;
+constexpr char kSeparator = '.';
+} // namespace
+
+namespace password_manager {
+
+bool HashPasswordManager::SavePasswordHash(const base::string16& password) {
+ if (!prefs_)
+ return false;
+
+ std::string salt = CreateRandomSalt();
+ std::string hash = base::Uint64ToString(
+ password_manager_util::CalculateSyncPasswordHash(password, salt));
+ EncryptAndSaveToPrefs(prefs::kSyncPasswordHash, hash);
+
+ // Password length and salt are stored together.
+ std::string length_salt = LengthAndSaltToString(salt, password.size());
+ return EncryptAndSaveToPrefs(prefs::kSyncPasswordLengthAndHashSalt,
+ length_salt);
+}
+
+void HashPasswordManager::ClearSavedPasswordHash() {
+ if (prefs_)
+ prefs_->ClearPref(prefs::kSyncPasswordHash);
+}
+
+base::Optional<SyncPasswordData> HashPasswordManager::RetrievePasswordHash() {
+ if (!prefs_ || !prefs_->HasPrefPath(prefs::kSyncPasswordHash))
+ return base::nullopt;
+
+ SyncPasswordData result;
+ std::string hash_str =
+ RetrivedDecryptedStringFromPrefs(prefs::kSyncPasswordHash);
+ if (!base::StringToUint64(hash_str, &result.hash))
+ return base::nullopt;
+
+ StringToLengthAndSalt(
+ RetrivedDecryptedStringFromPrefs(prefs::kSyncPasswordLengthAndHashSalt),
+ &result.length, &result.salt);
+ return result;
+}
+
+void HashPasswordManager::ReportIsSyncPasswordHashSavedMetric() {
+ if (!prefs_)
+ return;
+ auto hash_password_state =
+ prefs_->HasPrefPath(prefs::kSyncPasswordHash)
+ ? metrics_util::IsSyncPasswordHashSaved::SAVED
+ : metrics_util::IsSyncPasswordHashSaved::NOT_SAVED;
+ metrics_util::LogIsSyncPasswordHashSaved(hash_password_state);
+}
+
+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;
+}
+
+std::string HashPasswordManager::LengthAndSaltToString(const std::string& salt,
+ size_t password_length) {
+ return base::SizeTToString(password_length) + kSeparator + salt;
+}
+
+void HashPasswordManager::StringToLengthAndSalt(const std::string& s,
+ size_t* password_length,
+ std::string* salt) {
+ DCHECK(s.find(kSeparator) != std::string::npos);
+ DCHECK(salt);
+ size_t separator_index = s.find(kSeparator);
+ std::string prefix = s.substr(0, separator_index);
+
+ bool is_converted = base::StringToSizeT(prefix, password_length);
+ DCHECK(is_converted);
+
+ *salt = s.substr(separator_index + 1);
+}
+
+bool HashPasswordManager::EncryptAndSaveToPrefs(const std::string& pref_name,
+ const std::string& s) {
+ DCHECK(prefs_);
+ std::string encrypted_text;
+ if (!OSCrypt::EncryptString(s, &encrypted_text))
+ return false;
+ std::string encrypted_base64_text;
+ base::Base64Encode(encrypted_text, &encrypted_base64_text);
+ prefs_->SetString(pref_name, encrypted_base64_text);
+ return true;
+}
+
+std::string HashPasswordManager::RetrivedDecryptedStringFromPrefs(
+ const std::string& pref_name) {
+ DCHECK(prefs_);
+ std::string encrypted_base64_text = prefs_->GetString(pref_name);
+ if (encrypted_base64_text.empty())
+ return std::string();
+
+ std::string encrypted_text;
+ if (!base::Base64Decode(encrypted_base64_text, &encrypted_text))
+ return std::string();
+
+ std::string plain_text;
+ if (!OSCrypt::DecryptString(encrypted_text, &plain_text))
+ return std::string();
+
+ return plain_text;
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/hash_password_manager.h b/chromium/components/password_manager/core/browser/hash_password_manager.h
new file mode 100644
index 00000000000..217f4d4f816
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/hash_password_manager.h
@@ -0,0 +1,67 @@
+// 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_HASH_PASSWORD_MANAGER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_HASH_PASSWORD_MANAGER_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+
+class PrefService;
+
+namespace password_manager {
+
+struct SyncPasswordData {
+ uint64_t hash;
+ std::string salt;
+ size_t length;
+};
+
+// Responsible for saving, clearing, retrieving and encryption of a sync
+// password hash in preferences.
+// All methods should be called on UI thread.
+class HashPasswordManager {
+ public:
+ HashPasswordManager() = default;
+ ~HashPasswordManager() = default;
+
+ bool SavePasswordHash(const base::string16& password);
+ void ClearSavedPasswordHash();
+
+ // Returns empty if no hash is available.
+ base::Optional<SyncPasswordData> RetrievePasswordHash();
+
+ void ReportIsSyncPasswordHashSavedMetric();
+
+ void set_prefs(PrefService* prefs) { prefs_ = prefs; }
+
+ private:
+ std::string CreateRandomSalt();
+
+ // Packs |salt| and |password_length| to a string.
+ std::string LengthAndSaltToString(const std::string& salt,
+ size_t password_length);
+
+ // Unpacks |salt| and |password_length| from a string |s|.
+ void StringToLengthAndSalt(const std::string& s,
+ size_t* password_length,
+ std::string* salt);
+
+ // Saves encrypted string |s| in a preference |pref_name|. Return true on
+ // success.
+ bool EncryptAndSaveToPrefs(const std::string& pref_name,
+ const std::string& s);
+
+ // Retrieves and decrypts string value from a preference |pref_name|. Return
+ // an empty string on failure.
+ std::string RetrivedDecryptedStringFromPrefs(const std::string& pref_name);
+
+ PrefService* prefs_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(HashPasswordManager);
+};
+
+} // namespace password_manager
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_HASH_PASSWORD_MANAGER_H_
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
new file mode 100644
index 00000000000..050fb761c87
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/hash_password_manager.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/os_crypt/os_crypt_mocker.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+class HashPasswordManagerTest : public testing::Test {
+ public:
+ HashPasswordManagerTest() {
+ prefs_.registry()->RegisterStringPref(prefs::kSyncPasswordHash,
+ std::string(),
+ PrefRegistry::NO_REGISTRATION_FLAGS);
+ prefs_.registry()->RegisterStringPref(prefs::kSyncPasswordLengthAndHashSalt,
+ std::string(),
+ PrefRegistry::NO_REGISTRATION_FLAGS);
+ // Mock OSCrypt. There is a call to OSCrypt on initializling
+ // PasswordReuseDetector, so it should be mocked.
+ OSCryptMocker::SetUpWithSingleton();
+ }
+
+ ~HashPasswordManagerTest() override { OSCryptMocker::TearDown(); }
+
+ protected:
+ TestingPrefServiceSimple prefs_;
+};
+
+TEST_F(HashPasswordManagerTest, Saving) {
+ ASSERT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash));
+ HashPasswordManager hash_password_manager;
+ hash_password_manager.set_prefs(&prefs_);
+ hash_password_manager.SavePasswordHash(base::ASCIIToUTF16("sync_password"));
+ EXPECT_TRUE(prefs_.HasPrefPath(prefs::kSyncPasswordHash));
+}
+
+TEST_F(HashPasswordManagerTest, Clearing) {
+ ASSERT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash));
+ HashPasswordManager hash_password_manager;
+ hash_password_manager.set_prefs(&prefs_);
+ hash_password_manager.SavePasswordHash(base::ASCIIToUTF16("sync_password"));
+ hash_password_manager.ClearSavedPasswordHash();
+ EXPECT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash));
+}
+
+TEST_F(HashPasswordManagerTest, Retrieving) {
+ ASSERT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash));
+ HashPasswordManager hash_password_manager;
+ hash_password_manager.set_prefs(&prefs_);
+ hash_password_manager.SavePasswordHash(base::ASCIIToUTF16("sync_password"));
+ EXPECT_TRUE(prefs_.HasPrefPath(prefs::kSyncPasswordLengthAndHashSalt));
+
+ base::Optional<SyncPasswordData> sync_password_data =
+ hash_password_manager.RetrievePasswordHash();
+ ASSERT_TRUE(sync_password_data);
+ EXPECT_EQ(13u, sync_password_data->length);
+ EXPECT_EQ(16u, sync_password_data->salt.size());
+ uint64_t expected_hash = password_manager_util::CalculateSyncPasswordHash(
+ base::ASCIIToUTF16("sync_password"), sync_password_data->salt);
+ EXPECT_EQ(expected_hash, sync_password_data->hash);
+}
+
+} // namespace
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/http_data_cleaner.cc b/chromium/components/password_manager/core/browser/http_data_cleaner.cc
index 0df47eab8dc..9b29a50cf11 100644
--- a/chromium/components/password_manager/core/browser/http_data_cleaner.cc
+++ b/chromium/components/password_manager/core/browser/http_data_cleaner.cc
@@ -203,11 +203,11 @@ void WaitUntilCleaningIsDone(std::unique_ptr<ObsoleteHttpCleaner> cleaner,
// round trip of tasks will be scheduled. Finally, when no weak ptrs remain,
// this method sets a boolean preference flag and returns.
if (cleaner->HasWeakPtrs()) {
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner =
- base::ThreadTaskRunnerHandle::Get();
+ scoped_refptr<base::SequencedTaskRunner> main_thread_runner =
+ base::SequencedTaskRunnerHandle::Get();
const auto post_to_thread =
[](std::unique_ptr<ObsoleteHttpCleaner> cleaner, PrefService* prefs,
- scoped_refptr<base::SingleThreadTaskRunner> thread_runner) {
+ scoped_refptr<base::SequencedTaskRunner> thread_runner) {
thread_runner->PostTask(
FROM_HERE, base::Bind(&WaitUntilCleaningIsDone,
base::Passed(std::move(cleaner)), prefs));
@@ -243,7 +243,7 @@ void DelayCleanObsoleteHttpDataForPasswordStoreAndPrefsImpl(
int delay_in_seconds) {
if (!prefs->GetBoolean(
password_manager::prefs::kWasObsoleteHttpDataCleaned)) {
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&InitiateCleaning, make_scoped_refptr(store), prefs,
request_context),
diff --git a/chromium/components/password_manager/core/browser/http_password_store_migrator.cc b/chromium/components/password_manager/core/browser/http_password_store_migrator.cc
index 4611457227c..859b04afecc 100644
--- a/chromium/components/password_manager/core/browser/http_password_store_migrator.cc
+++ b/chromium/components/password_manager/core/browser/http_password_store_migrator.cc
@@ -53,7 +53,7 @@ HttpPasswordStoreMigrator::~HttpPasswordStoreMigrator() = default;
void HttpPasswordStoreMigrator::OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
results_ = std::move(results);
got_password_store_results_ = true;
@@ -62,7 +62,7 @@ void HttpPasswordStoreMigrator::OnGetPasswordStoreResults(
}
void HttpPasswordStoreMigrator::OnHSTSQueryResult(bool is_hsts) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mode_ = is_hsts ? MigrationMode::MOVE : MigrationMode::COPY;
got_hsts_query_result_ = true;
diff --git a/chromium/components/password_manager/core/browser/http_password_store_migrator.h b/chromium/components/password_manager/core/browser/http_password_store_migrator.h
index 47a378cc0a0..a3075cecb65 100644
--- a/chromium/components/password_manager/core/browser/http_password_store_migrator.h
+++ b/chromium/components/password_manager/core/browser/http_password_store_migrator.h
@@ -9,7 +9,7 @@
#include <vector>
#include "base/macros.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "url/gurl.h"
@@ -76,7 +76,7 @@ class HttpPasswordStoreMigrator : public PasswordStoreConsumer {
MigrationMode mode_;
std::vector<std::unique_ptr<autofill::PasswordForm>> results_;
GURL http_origin_domain_;
- base::ThreadChecker thread_checker_;
+ SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(HttpPasswordStoreMigrator);
};
diff --git a/chromium/components/password_manager/core/browser/import/password_csv_reader.cc b/chromium/components/password_manager/core/browser/import/password_csv_reader.cc
index 9b52c6704bc..8ffcbea2d4d 100644
--- a/chromium/components/password_manager/core/browser/import/password_csv_reader.cc
+++ b/chromium/components/password_manager/core/browser/import/password_csv_reader.cc
@@ -10,6 +10,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/import/csv_reader.h"
using autofill::PasswordForm;
@@ -114,7 +115,13 @@ bool PasswordCSVReader::RecordToPasswordForm(
password_value = base::UTF8ToUTF16(password_in_record->second);
form->origin.Swap(&origin);
- form->signon_realm = form->origin.GetOrigin().spec();
+ // |GURL::GetOrigin| returns an empty GURL for Android credentials due to the
+ // non-standard scheme ("android://"). Hence the following explicit check is
+ // necessary to set |signon_realm| correctly for both regular and Android
+ // credentials.
+ form->signon_realm = IsValidAndroidFacetURI(form->origin.spec())
+ ? form->origin.spec()
+ : form->origin.GetOrigin().spec();
form->username_value.swap(username_value);
form->password_value.swap(password_value);
return true;
diff --git a/chromium/components/password_manager/core/browser/import/password_csv_reader_unittest.cc b/chromium/components/password_manager/core/browser/import/password_csv_reader_unittest.cc
index b3b74acbb13..ea015e133f0 100644
--- a/chromium/components/password_manager/core/browser/import/password_csv_reader_unittest.cc
+++ b/chromium/components/password_manager/core/browser/import/password_csv_reader_unittest.cc
@@ -6,6 +6,7 @@
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/import/password_importer.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,6 +37,25 @@ TEST(PasswordCSVReaderTest, DeserializePasswords_SingleValid) {
EXPECT_EQ(base::UTF8ToUTF16("test1"), passwords[0].password_value);
}
+TEST(PasswordCSVReaderTest, DeserializePasswords_SingleAndroid) {
+ constexpr char kCSVInput[] =
+ "Url,Username,Password\n"
+ "android://hash@com.example.android,test@gmail.com,test1\n";
+ std::vector<autofill::PasswordForm> passwords;
+ PasswordCSVReader reader;
+ EXPECT_EQ(PasswordImporter::SUCCESS,
+ reader.DeserializePasswords(kCSVInput, &passwords));
+ EXPECT_EQ(1u, passwords.size());
+ const GURL expected_origin("android://hash@com.example.android");
+
+ const autofill::PasswordForm& password = passwords.front();
+ EXPECT_EQ(expected_origin, password.origin);
+ EXPECT_EQ(expected_origin.spec(), password.signon_realm);
+ EXPECT_TRUE(IsValidAndroidFacetURI(password.signon_realm));
+ EXPECT_EQ(base::UTF8ToUTF16("test@gmail.com"), password.username_value);
+ EXPECT_EQ(base::UTF8ToUTF16("test1"), password.password_value);
+}
+
TEST(PasswordCSVReaderTest, DeserializePasswords_TwoValid) {
const char kCSVInput[] =
"Url,Username,Password,Someotherfield\n"
diff --git a/chromium/components/password_manager/core/browser/import/password_importer.cc b/chromium/components/password_manager/core/browser/import/password_importer.cc
index f333524ada7..ef36842f77c 100644
--- a/chromium/components/password_manager/core/browser/import/password_importer.cc
+++ b/chromium/components/password_manager/core/browser/import/password_importer.cc
@@ -8,8 +8,7 @@
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/optional.h"
-#include "base/task_runner.h"
-#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/import/password_csv_reader.h"
@@ -46,13 +45,14 @@ static void ParsePasswords(
} // namespace
// static
-void PasswordImporter::Import(
- const base::FilePath& path,
- scoped_refptr<base::TaskRunner> blocking_task_runner,
- const CompletionCallback& completion) {
- base::PostTaskAndReplyWithResult(blocking_task_runner.get(), FROM_HERE,
- base::Bind(&ReadFileToString, path),
- base::Bind(&ParsePasswords, completion));
+void PasswordImporter::Import(const base::FilePath& path,
+ const CompletionCallback& completion) {
+ // Posting with USER_VISIBLE priority, because the result of the import is
+ // visible to the user in the password settings page.
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
+ base::Bind(&ReadFileToString, path),
+ base::Bind(&ParsePasswords, completion));
}
// static
diff --git a/chromium/components/password_manager/core/browser/import/password_importer.h b/chromium/components/password_manager/core/browser/import/password_importer.h
index a5fcc84e1e5..1e451c3096d 100644
--- a/chromium/components/password_manager/core/browser/import/password_importer.h
+++ b/chromium/components/password_manager/core/browser/import/password_importer.h
@@ -11,16 +11,11 @@
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
namespace autofill {
struct PasswordForm;
}
-namespace base {
-class TaskRunner;
-}
-
namespace password_manager {
// Static-only class bundling together the API for importing passwords from a
@@ -40,11 +35,9 @@ class PasswordImporter {
CompletionCallback;
// Imports passwords from the file at |path|, and fires |completion| callback
- // on the calling thread with the passwords when ready. Blocking IO operations
- // will be posted to |blocking_task_runner|. The only supported file format is
- // CSV.
+ // on the calling thread with the passwords when ready. The only supported
+ // file format is CSV.
static void Import(const base::FilePath& path,
- scoped_refptr<base::TaskRunner> blocking_task_runner,
const CompletionCallback& completion);
// Returns the file extensions corresponding to supported formats.
diff --git a/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc b/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc
index 26b0149ab9b..48a632bd0c0 100644
--- a/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc
+++ b/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc
@@ -8,12 +8,8 @@
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/test/scoped_task_environment.h"
#include "components/autofill/core/common/password_form.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,12 +32,11 @@ class PasswordImporterTest : public testing::Test {
protected:
void StartImportAndWaitForCompletion(const base::FilePath& input_file) {
- PasswordImporter::Import(input_file, message_loop_.task_runner(),
+ PasswordImporter::Import(input_file,
base::Bind(&PasswordImporterTest::OnImportFinished,
base::Unretained(this)));
- base::RunLoop run_loop;
- run_loop.RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(callback_called_);
}
@@ -62,7 +57,7 @@ class PasswordImporterTest : public testing::Test {
base::ScopedTempDir temp_directory_;
private:
- base::MessageLoop message_loop_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
bool callback_called_;
PasswordImporter::Result result_;
diff --git a/chromium/components/password_manager/core/browser/login_database.cc b/chromium/components/password_manager/core/browser/login_database.cc
index e2067b61a2f..e7726d2484b 100644
--- a/chromium/components/password_manager/core/browser/login_database.cc
+++ b/chromium/components/password_manager/core/browser/login_database.cc
@@ -26,7 +26,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/affiliation_utils.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_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
@@ -361,6 +361,7 @@ void InitializeBuilder(SQLTableBuilder* builder) {
builder->AddColumn("date_created", "INTEGER NOT NULL");
builder->AddColumn("blacklisted_by_user", "INTEGER NOT NULL");
builder->AddColumn("scheme", "INTEGER NOT NULL");
+ builder->AddIndex("logins_signon", {"signon_realm"});
builder->SealVersion();
unsigned version = builder->SealVersion();
DCHECK_EQ(1u, version);
@@ -521,8 +522,7 @@ std::string GeneratePlaceholders(size_t count) {
} // namespace
LoginDatabase::LoginDatabase(const base::FilePath& db_path)
- : db_path_(db_path), clear_password_values_(false) {
-}
+ : db_path_(db_path) {}
LoginDatabase::~LoginDatabase() {
}
@@ -568,7 +568,7 @@ bool LoginDatabase::Init() {
return false;
}
- SQLTableBuilder builder;
+ SQLTableBuilder builder("logins");
InitializeBuilder(&builder);
InitializeStatementStrings(builder);
@@ -814,9 +814,8 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) {
if (!DoesMatchConstraints(form))
return list;
std::string encrypted_password;
- if (EncryptedString(
- clear_password_values_ ? base::string16() : form.password_value,
- &encrypted_password) != ENCRYPTION_RESULT_SUCCESS)
+ if (EncryptedString(form.password_value, &encrypted_password) !=
+ ENCRYPTION_RESULT_SUCCESS)
return list;
DCHECK(!add_statement_.empty());
@@ -844,9 +843,8 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) {
PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) {
std::string encrypted_password;
- if (EncryptedString(
- clear_password_values_ ? base::string16() : form.password_value,
- &encrypted_password) != ENCRYPTION_RESULT_SUCCESS)
+ if (EncryptedString(form.password_value, &encrypted_password) !=
+ ENCRYPTION_RESULT_SUCCESS)
return PasswordStoreChangeList();
#if defined(OS_IOS)
diff --git a/chromium/components/password_manager/core/browser/login_database.h b/chromium/components/password_manager/core/browser/login_database.h
index ed53b3a8e08..d49b3a11145 100644
--- a/chromium/components/password_manager/core/browser/login_database.h
+++ b/chromium/components/password_manager/core/browser/login_database.h
@@ -147,8 +147,6 @@ class LoginDatabase {
StatisticsTable& stats_table() { return stats_table_; }
- void set_clear_password_values(bool val) { clear_password_values_ = val; }
-
private:
#if defined(OS_IOS)
friend class LoginDatabaseIOSTest;
@@ -219,13 +217,6 @@ class LoginDatabase {
sql::MetaTable meta_table_;
StatisticsTable stats_table_;
- // If set to 'true', then the password values are cleared before encrypting
- // and storing in the database. At the same time AddLogin/UpdateLogin return
- // PasswordStoreChangeList containing the real password.
- // This is a temporary measure for migration the Keychain on Mac.
- // crbug.com/466638
- bool clear_password_values_;
-
// These cached strings are used to build SQL statements.
std::string add_statement_;
std::string add_replace_statement_;
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 f219fd8be9e..b6a1011b182 100644
--- a/chromium/components/password_manager/core/browser/login_database_unittest.cc
+++ b/chromium/components/password_manager/core/browser/login_database_unittest.cc
@@ -1650,37 +1650,6 @@ TEST_F(LoginDatabaseTest, PasswordReuseMetrics) {
base::Bucket(5, 1)));
}
-TEST_F(LoginDatabaseTest, ClearPasswordValues) {
- db().set_clear_password_values(true);
-
- // Add a PasswordForm, the password should be cleared.
- base::HistogramTester histogram_tester;
- PasswordForm form;
- form.origin = GURL("http://accounts.google.com/LoginAuth");
- form.signon_realm = "http://accounts.google.com/";
- form.username_value = ASCIIToUTF16("my_username");
- form.password_value = ASCIIToUTF16("12345");
- EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
-
- std::vector<std::unique_ptr<PasswordForm>> result;
- EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
- ASSERT_EQ(1U, result.size());
- PasswordForm expected_form = form;
- expected_form.password_value.clear();
- EXPECT_EQ(expected_form, *result[0]);
-
- // Update the password, it should stay empty.
- form.password_value = ASCIIToUTF16("password");
- EXPECT_EQ(UpdateChangeForForm(form), db().UpdateLogin(form));
- EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
- ASSERT_EQ(1U, result.size());
- EXPECT_EQ(expected_form, *result[0]);
-
- // Encrypting/decrypting shouldn't happen. Thus there should be no keychain
- // access on Mac.
- histogram_tester.ExpectTotalCount("OSX.Keychain.Access", 0);
-}
-
#if defined(OS_POSIX)
// Only the current user has permission to read the database.
//
diff --git a/chromium/components/password_manager/core/browser/mock_affiliated_match_helper.cc b/chromium/components/password_manager/core/browser/mock_affiliated_match_helper.cc
deleted file mode 100644
index aaf1c997e98..00000000000
--- a/chromium/components/password_manager/core/browser/mock_affiliated_match_helper.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 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/password_manager/core/browser/mock_affiliated_match_helper.h"
-
-#include "base/memory/ptr_util.h"
-#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-
-MockAffiliatedMatchHelper::MockAffiliatedMatchHelper()
- : AffiliatedMatchHelper(nullptr,
- base::WrapUnique<AffiliationService>(nullptr)) {}
-
-MockAffiliatedMatchHelper::~MockAffiliatedMatchHelper() {}
-
-void MockAffiliatedMatchHelper::ExpectCallToGetAffiliatedAndroidRealms(
- const PasswordStore::FormDigest& expected_observed_form,
- const std::vector<std::string>& results_to_return) {
- EXPECT_CALL(*this, OnGetAffiliatedAndroidRealmsCalled(expected_observed_form))
- .WillOnce(testing::Return(results_to_return));
-}
-
-void MockAffiliatedMatchHelper::ExpectCallToGetAffiliatedWebRealms(
- const PasswordStore::FormDigest& expected_android_form,
- const std::vector<std::string>& results_to_return) {
- EXPECT_CALL(*this, OnGetAffiliatedWebRealmsCalled(expected_android_form))
- .WillOnce(testing::Return(results_to_return));
-}
-
-void MockAffiliatedMatchHelper::ExpectCallToInjectAffiliatedWebRealms(
- const std::vector<std::string>& results_to_inject) {
- EXPECT_CALL(*this, OnInjectAffiliatedWebRealmsCalled())
- .WillOnce(testing::Return(results_to_inject));
-}
-
-void MockAffiliatedMatchHelper::GetAffiliatedAndroidRealms(
- const PasswordStore::FormDigest& observed_form,
- const AffiliatedRealmsCallback& result_callback) {
- std::vector<std::string> affiliated_android_realms =
- OnGetAffiliatedAndroidRealmsCalled(observed_form);
- result_callback.Run(affiliated_android_realms);
-}
-
-void MockAffiliatedMatchHelper::GetAffiliatedWebRealms(
- const PasswordStore::FormDigest& android_form,
- const AffiliatedRealmsCallback& result_callback) {
- std::vector<std::string> affiliated_web_realms =
- OnGetAffiliatedWebRealmsCalled(android_form);
- result_callback.Run(affiliated_web_realms);
-}
-
-void MockAffiliatedMatchHelper::InjectAffiliatedWebRealms(
- std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
- const PasswordFormsCallback& result_callback) {
- std::vector<std::string> affiliated_web_realms =
- OnInjectAffiliatedWebRealmsCalled();
- DCHECK_EQ(affiliated_web_realms.size(), forms.size());
- for (size_t i = 0; i < forms.size(); ++i)
- forms[i]->affiliated_web_realm = affiliated_web_realms[i];
- result_callback.Run(std::move(forms));
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/mock_affiliated_match_helper.h b/chromium/components/password_manager/core/browser/mock_affiliated_match_helper.h
deleted file mode 100644
index cd843ce9dd4..00000000000
--- a/chromium/components/password_manager/core/browser/mock_affiliated_match_helper.h
+++ /dev/null
@@ -1,64 +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_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATED_MATCH_HELPER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATED_MATCH_HELPER_H_
-
-#include "base/macros.h"
-#include "components/password_manager/core/browser/affiliated_match_helper.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace autofill {
-struct PasswordForm;
-}
-
-namespace password_manager {
-
-class MockAffiliatedMatchHelper : public AffiliatedMatchHelper {
- public:
- MockAffiliatedMatchHelper();
- ~MockAffiliatedMatchHelper() override;
-
- // Expects GetAffiliatedAndroidRealms() to be called with the
- // |expected_observed_form|, and will cause the result callback supplied to
- // GetAffiliatedAndroidRealms() to be invoked with |results_to_return|.
- void ExpectCallToGetAffiliatedAndroidRealms(
- const PasswordStore::FormDigest& expected_observed_form,
- const std::vector<std::string>& results_to_return);
-
- // Expects GetAffiliatedWebRealms() to be called with the
- // |expected_android_form|, and will cause the result callback supplied to
- // GetAffiliatedWebRealms() to be invoked with |results_to_return|.
- void ExpectCallToGetAffiliatedWebRealms(
- const PasswordStore::FormDigest& expected_android_form,
- const std::vector<std::string>& results_to_return);
-
- void ExpectCallToInjectAffiliatedWebRealms(
- const std::vector<std::string>& results_to_inject);
-
- private:
- MOCK_METHOD1(OnGetAffiliatedAndroidRealmsCalled,
- std::vector<std::string>(const PasswordStore::FormDigest&));
- MOCK_METHOD1(OnGetAffiliatedWebRealmsCalled,
- std::vector<std::string>(const PasswordStore::FormDigest&));
- MOCK_METHOD0(OnInjectAffiliatedWebRealmsCalled, std::vector<std::string>());
-
- void GetAffiliatedAndroidRealms(
- const PasswordStore::FormDigest& observed_form,
- const AffiliatedRealmsCallback& result_callback) override;
- void GetAffiliatedWebRealms(
- const PasswordStore::FormDigest& android_form,
- const AffiliatedRealmsCallback& result_callback) override;
-
- void InjectAffiliatedWebRealms(
- std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
- const PasswordFormsCallback& result_callback) override;
-
- DISALLOW_COPY_AND_ASSIGN(MockAffiliatedMatchHelper);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATED_MATCH_HELPER_H_
diff --git a/chromium/components/password_manager/core/browser/mock_affiliation_consumer.cc b/chromium/components/password_manager/core/browser/mock_affiliation_consumer.cc
deleted file mode 100644
index 6f1d102d66e..00000000000
--- a/chromium/components/password_manager/core/browser/mock_affiliation_consumer.cc
+++ /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.
-
-#include "components/password_manager/core/browser/mock_affiliation_consumer.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-
-namespace password_manager {
-
-MockAffiliationConsumer::MockAffiliationConsumer() {
- EXPECT_CALL(*this, OnResultCallback(testing::_, testing::_)).Times(0);
-}
-
-MockAffiliationConsumer::~MockAffiliationConsumer() {
-}
-
-void MockAffiliationConsumer::ExpectSuccessWithResult(
- const AffiliatedFacets& expected_result) {
- EXPECT_CALL(*this, OnResultCallback(
- testing::UnorderedElementsAreArray(expected_result),
- true)).Times(1);
-}
-
-void MockAffiliationConsumer::ExpectFailure() {
- EXPECT_CALL(*this, OnResultCallback(testing::UnorderedElementsAre(), false))
- .Times(1);
-}
-
-AffiliationService::ResultCallback
-MockAffiliationConsumer::GetResultCallback() {
- return base::Bind(&MockAffiliationConsumer::OnResultCallback,
- base::Unretained(this));
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/mock_affiliation_consumer.h b/chromium/components/password_manager/core/browser/mock_affiliation_consumer.h
deleted file mode 100644
index 146e2995218..00000000000
--- a/chromium/components/password_manager/core/browser/mock_affiliation_consumer.h
+++ /dev/null
@@ -1,39 +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_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATION_CONSUMER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATION_CONSUMER_H_
-
-#include "base/macros.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace password_manager {
-
-// A mock consumer of AffiliationService::GetAffiliations().
-class MockAffiliationConsumer {
- public:
- MockAffiliationConsumer();
- ~MockAffiliationConsumer();
-
- // Expects that the result callback will be called exactly once and that it
- // will indicate success and return |expected_result|.
- void ExpectSuccessWithResult(const AffiliatedFacets& expected_result);
-
- // Expects that the result callback will be called exactly once and that it
- // will indicate a failed lookup.
- void ExpectFailure();
-
- AffiliationService::ResultCallback GetResultCallback();
-
- private:
- MOCK_METHOD2(OnResultCallback, void(const AffiliatedFacets&, bool));
-
- DISALLOW_COPY_AND_ASSIGN(MockAffiliationConsumer);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATION_CONSUMER_H_
diff --git a/chromium/components/password_manager/core/browser/mock_password_store.cc b/chromium/components/password_manager/core/browser/mock_password_store.cc
index b0e2c3acb0b..7992d2b0e16 100644
--- a/chromium/components/password_manager/core/browser/mock_password_store.cc
+++ b/chromium/components/password_manager/core/browser/mock_password_store.cc
@@ -5,14 +5,13 @@
#include "components/password_manager/core/browser/mock_password_store.h"
#include "base/memory/ptr_util.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
namespace password_manager {
MockPasswordStore::MockPasswordStore()
- : PasswordStore(base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get()) {
-}
+ : PasswordStore(base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get()) {}
MockPasswordStore::~MockPasswordStore() {
}
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 fe210f3da18..7c67c9bd263 100644
--- a/chromium/components/password_manager/core/browser/mock_password_store.h
+++ b/chromium/components/password_manager/core/browser/mock_password_store.h
@@ -82,6 +82,10 @@ class MockPasswordStore : public PasswordStore {
void(const base::string16&,
const std::string&,
PasswordReuseDetectorConsumer*));
+#if !defined(OS_CHROMEOS)
+ MOCK_METHOD1(SaveSyncPasswordHash, void(const base::string16&));
+ MOCK_METHOD0(ClearSyncPasswordHash, void());
+#endif
#endif
PasswordStoreSync* GetSyncInterface() { return this; }
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 a242d655fc0..7ffed22d491 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager.cc
@@ -24,7 +24,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/password_manager/core/browser/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/security_state/core/security_state.h"
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 98100d29dd9..12f6263d09f 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_form_manager.cc
@@ -24,7 +24,7 @@
#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/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.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_saver.h"
@@ -215,22 +215,20 @@ PasswordFormManager::PasswordFormManager(
? SplitPathToSegments(observed_form_.origin.path())
: std::vector<std::string>()),
is_new_login_(true),
+ has_autofilled_(false),
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),
- generation_available_(false),
password_manager_(password_manager),
preferred_match_(nullptr),
is_possible_change_password_form_without_username_(
observed_form.IsPossibleChangePasswordFormWithoutUsername()),
client_(client),
- manager_action_(kManagerActionNone),
- user_action_(kUserActionNone),
- submit_result_(kSubmitResultNotSubmitted),
- form_type_(kFormTypeUnspecified),
+ user_action_(UserAction::kNone),
form_saver_(std::move(form_saver)),
owned_form_fetcher_(
form_fetcher ? nullptr
@@ -241,143 +239,37 @@ PasswordFormManager::PasswordFormManager(
true /* should_query_suppressed_https_forms */)),
form_fetcher_(form_fetcher ? form_fetcher : owned_form_fetcher_.get()),
is_main_frame_secure_(client->IsMainFrameSecure()) {
- if (owned_form_fetcher_)
- owned_form_fetcher_->Fetch();
- DCHECK_EQ(observed_form.scheme == PasswordForm::SCHEME_HTML,
- driver != nullptr);
+ // Non-HTML forms should not need any interaction with the renderer, and hence
+ // no driver. Note that cloned PasswordFormManager instances can have HTML
+ // forms without drivers as well.
+ DCHECK((observed_form.scheme == PasswordForm::SCHEME_HTML) ||
+ (driver == nullptr))
+ << observed_form.scheme;
if (driver)
drivers_.push_back(driver);
- form_fetcher_->AddConsumer(this);
}
-PasswordFormManager::~PasswordFormManager() {
- form_fetcher_->RemoveConsumer(this);
-
- UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenV3", GetActionsTaken(),
- kMaxNumActionsTaken);
-
- // Use the visible main frame URL at the time the PasswordFormManager
- // is created, in case a navigation has already started and the
- // visible URL has changed.
- if (!is_main_frame_secure_) {
- UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenOnNonSecureForm",
- GetActionsTaken(), kMaxNumActionsTaken);
- }
-
- RecordHistogramsOnSuppressedAccounts();
-
- if (submit_result_ == kSubmitResultNotSubmitted) {
- if (has_generated_password_)
- metrics_util::LogPasswordGenerationSubmissionEvent(
- metrics_util::PASSWORD_NOT_SUBMITTED);
- else if (generation_available_)
- metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
- metrics_util::PASSWORD_NOT_SUBMITTED);
- }
-
- if (form_type_ != kFormTypeUnspecified) {
- UMA_HISTOGRAM_ENUMERATION("PasswordManager.SubmittedFormType", form_type_,
- kFormTypeMax);
- if (!is_main_frame_secure_) {
- UMA_HISTOGRAM_ENUMERATION("PasswordManager.SubmittedNonSecureFormType",
- form_type_, kFormTypeMax);
- }
- }
-}
-
-int PasswordFormManager::GetActionsTaken() const {
- return user_action_ +
- kUserActionMax *
- (manager_action_ + kManagerActionMax * submit_result_);
-}
-
-int PasswordFormManager::GetHistogramSampleForSuppressedAccounts(
- const std::vector<const autofill::PasswordForm*> suppressed_forms,
- PasswordForm::Type manual_or_generated) const {
- DCHECK(form_fetcher_->DidCompleteQueryingSuppressedForms());
-
- SuppressedAccountExistence best_matching_account = kSuppressedAccountNone;
- for (const autofill::PasswordForm* form : suppressed_forms) {
- if (form->type != manual_or_generated)
- continue;
-
- SuppressedAccountExistence current_account;
- if (pending_credentials_.password_value.empty())
- current_account = kSuppressedAccountExists;
- else if (form->username_value != pending_credentials_.username_value)
- current_account = kSuppressedAccountExistsDifferentUsername;
- else if (form->password_value != pending_credentials_.password_value)
- current_account = kSuppressedAccountExistsSameUsername;
- else
- current_account = kSuppressedAccountExistsSameUsernameAndPassword;
-
- best_matching_account = std::max(best_matching_account, current_account);
+void PasswordFormManager::Init(
+ scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder) {
+ DCHECK(!metrics_recorder_) << "Do not call Init twice.";
+ metrics_recorder_ = std::move(metrics_recorder);
+ if (!metrics_recorder_) {
+ metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
+ client_->IsMainFrameSecure(), client_->GetUkmRecorder(),
+ client_->GetUkmSourceId(), client_->GetMainFrameURL());
}
- // Merge kManagerActionNone and kManagerActionBlacklisted_Obsolete. This
- // lowers the number of histogram buckets used by 33%.
- ManagerActionNew manager_action_new =
- (manager_action_ == kManagerActionAutofilled)
- ? kManagerActionNewAutofilled
- : kManagerActionNewNone;
-
- // Encoding: most significant digit is the |best_matching_account|.
- int mixed_base_encoding = 0;
- mixed_base_encoding += best_matching_account;
- (mixed_base_encoding *= kSubmitResultMax) += submit_result_;
- (mixed_base_encoding *= kManagerActionNewMax) += manager_action_new;
- (mixed_base_encoding *= kUserActionMax) += user_action_;
- DCHECK_LT(mixed_base_encoding, kMaxSuppressedAccountStats);
- return mixed_base_encoding;
+ if (owned_form_fetcher_)
+ owned_form_fetcher_->Fetch();
+ form_fetcher_->AddConsumer(this);
}
-void PasswordFormManager::RecordHistogramsOnSuppressedAccounts() const {
- UMA_HISTOGRAM_BOOLEAN("PasswordManager.QueryingSuppressedAccountsFinished",
- form_fetcher_->DidCompleteQueryingSuppressedForms());
-
- if (!form_fetcher_->DidCompleteQueryingSuppressedForms())
- return;
-
- if (!observed_form_.origin.SchemeIsCryptographic()) {
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SuppressedAccount.Generated.HTTPSNotHTTP",
- GetHistogramSampleForSuppressedAccounts(
- form_fetcher_->GetSuppressedHTTPSForms(),
- PasswordForm::TYPE_GENERATED),
- kMaxSuppressedAccountStats);
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SuppressedAccount.Manual.HTTPSNotHTTP",
- GetHistogramSampleForSuppressedAccounts(
- form_fetcher_->GetSuppressedHTTPSForms(),
- PasswordForm::TYPE_MANUAL),
- kMaxSuppressedAccountStats);
- }
+PasswordFormManager::~PasswordFormManager() {
+ form_fetcher_->RemoveConsumer(this);
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SuppressedAccount.Generated.PSLMatching",
- GetHistogramSampleForSuppressedAccounts(
- form_fetcher_->GetSuppressedPSLMatchingForms(),
- PasswordForm::TYPE_GENERATED),
- kMaxSuppressedAccountStats);
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SuppressedAccount.Manual.PSLMatching",
- GetHistogramSampleForSuppressedAccounts(
- form_fetcher_->GetSuppressedPSLMatchingForms(),
- PasswordForm::TYPE_MANUAL),
- kMaxSuppressedAccountStats);
-
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SuppressedAccount.Generated.SameOrganizationName",
- GetHistogramSampleForSuppressedAccounts(
- form_fetcher_->GetSuppressedSameOrganizationNameForms(),
- PasswordForm::TYPE_GENERATED),
- kMaxSuppressedAccountStats);
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SuppressedAccount.Manual.SameOrganizationName",
- GetHistogramSampleForSuppressedAccounts(
- form_fetcher_->GetSuppressedSameOrganizationNameForms(),
- PasswordForm::TYPE_MANUAL),
- kMaxSuppressedAccountStats);
+ metrics_recorder_->RecordHistogramsOnSuppressedAccounts(
+ observed_form_.origin.SchemeIsCryptographic(), *form_fetcher_,
+ pending_credentials_);
}
// static
@@ -497,7 +389,6 @@ void PasswordFormManager::ProvisionallySave(
}
submitted_form_ = std::move(mutable_submitted_form);
other_possible_username_action_ = action;
- does_look_like_signup_form_ = credentials.does_look_like_signup_form;
if (form_fetcher_->GetState() == FormFetcher::State::NOT_WAITING)
CreatePendingCredentials();
@@ -510,9 +401,9 @@ void PasswordFormManager::Save() {
metrics_util::LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent(
submitted_form_->submission_event);
- if ((user_action_ == kUserActionNone) &&
+ if ((user_action_ == UserAction::kNone) &&
DidPreferenceChange(best_matches_, pending_credentials_.username_value)) {
- SetUserAction(kUserActionChoose);
+ SetUserAction(UserAction::kChoose);
}
base::Optional<PasswordForm> old_primary_key;
if (is_new_login_) {
@@ -565,6 +456,32 @@ void PasswordFormManager::Update(
old_primary_key ? &old_primary_key.value() : nullptr);
}
+void PasswordFormManager::UpdateUsername(const base::string16& new_username) {
+ pending_credentials_.username_value = new_username;
+ // Check if the username already exists.
+ const PasswordForm* match = FindBestSavedMatch(&pending_credentials_);
+ is_new_login_ = !match || match->is_public_suffix_match;
+}
+
+void PasswordFormManager::PresaveGeneratedPassword(
+ const autofill::PasswordForm& form) {
+ form_saver()->PresaveGeneratedPassword(form);
+ metrics_recorder_->SetHasGeneratedPassword(true);
+ if (has_generated_password_) {
+ generated_password_changed_ = true;
+ } else {
+ SetHasGeneratedPassword(true);
+ generated_password_changed_ = false;
+ }
+}
+
+void PasswordFormManager::PasswordNoLongerGenerated() {
+ DCHECK(has_generated_password_);
+ form_saver()->RemovePresavedPassword();
+ SetHasGeneratedPassword(false);
+ generated_password_changed_ = false;
+}
+
void PasswordFormManager::SetSubmittedForm(const autofill::PasswordForm& form) {
bool is_change_password_form =
!form.new_password_value.empty() && !form.password_value.empty();
@@ -572,20 +489,23 @@ void PasswordFormManager::SetSubmittedForm(const autofill::PasswordForm& form) {
!form.new_password_value.empty() && form.password_value.empty();
bool no_username = form.username_element.empty();
+ PasswordFormMetricsRecorder::SubmittedFormType type =
+ PasswordFormMetricsRecorder::kSubmittedFormTypeUnspecified;
if (form.layout == PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP) {
- form_type_ = kFormTypeLoginAndSignup;
+ type = PasswordFormMetricsRecorder::kSubmittedFormTypeLoginAndSignup;
} else if (is_change_password_form) {
- form_type_ = kFormTypeChangePasswordEnabled;
+ type = PasswordFormMetricsRecorder::kSubmittedFormTypeChangePasswordEnabled;
} else if (is_signup_form) {
if (no_username)
- form_type_ = kFormTypeSignupNoUsername;
+ type = PasswordFormMetricsRecorder::kSubmittedFormTypeSignupNoUsername;
else
- form_type_ = kFormTypeSignup;
+ type = PasswordFormMetricsRecorder::kSubmittedFormTypeSignup;
} else if (no_username) {
- form_type_ = kFormTypeLoginNoUsername;
+ type = PasswordFormMetricsRecorder::kSubmittedFormTypeLoginNoUsername;
} else {
- form_type_ = kFormTypeLogin;
+ type = PasswordFormMetricsRecorder::kSubmittedFormTypeLogin;
}
+ metrics_recorder_->SetSubmittedFormType(type);
}
void PasswordFormManager::ScoreMatches(
@@ -603,21 +523,22 @@ void PasswordFormManager::ScoreMatches(
// Compute scores.
std::vector<uint32_t> credential_scores(matches.size());
- std::transform(
- matches.begin(), matches.end(), credential_scores.begin(),
- [this](const PasswordForm* match) { return ScoreResult(*match); });
+ for (size_t i = 0; i < matches.size(); ++i)
+ credential_scores[i] = ScoreResult(*matches[i]);
const uint32_t best_score =
*std::max_element(credential_scores.begin(), credential_scores.end());
- std::map<base::string16, uint32_t> best_scores; // best scores for usernames
-
+ // Compute best score for each username.
+ std::map<base::string16, uint32_t> best_scores;
for (size_t i = 0; i < matches.size(); ++i) {
uint32_t& score = best_scores[matches[i]->username_value];
score = std::max(score, credential_scores[i]);
}
- // Assign best, non-best and preferred matches.
+ // Find the best match for each username, move the rest to
+ // |non_best_matches_|. Also assign the overall best match to
+ // |preferred_match_|.
not_best_matches_.reserve(matches.size() - best_scores.size());
// Fill |best_matches_| with the best-scoring credentials for each username.
for (size_t i = 0; i < matches.size(); ++i) {
@@ -632,14 +553,12 @@ void PasswordFormManager::ScoreMatches(
if (!preferred_match_ && credential_scores[i] == best_score)
preferred_match_ = match;
- // If there is another best-score match for the same username then leave it
- // and add the current form to |not_best_matches_|.
- auto best_match_username = best_matches_.find(username);
- if (best_match_username == best_matches_.end()) {
- best_matches_.insert(std::make_pair(username, match));
- } else {
+ // If there is already another best-score match for the same username, leave
+ // it and add the current form to |not_best_matches_|.
+ if (best_matches_.find(username) != best_matches_.end())
not_best_matches_.push_back(match);
- }
+ else
+ best_matches_.insert(std::make_pair(username, match));
}
}
@@ -711,8 +630,11 @@ void PasswordFormManager::ProcessFrameInternal(
driver->AllowPasswordGenerationForForm(observed_form_);
- if (best_matches_.empty())
+ if (best_matches_.empty()) {
+ metrics_recorder_->RecordFillEvent(
+ PasswordFormMetricsRecorder::kManagerFillEventNoCredential);
return;
+ }
// Proceed to autofill.
// Note that we provide the choices but don't actually prefill a value if:
@@ -723,9 +645,16 @@ void PasswordFormManager::ProcessFrameInternal(
preferred_match_->is_public_suffix_match ||
observed_form_.IsPossibleChangePasswordForm();
if (wait_for_username) {
- manager_action_ = kManagerActionNone;
+ metrics_recorder_->SetManagerAction(
+ PasswordFormMetricsRecorder::kManagerActionNone);
+ metrics_recorder_->RecordFillEvent(
+ PasswordFormMetricsRecorder::kManagerFillEventBlockedOnInteraction);
} else {
- manager_action_ = kManagerActionAutofilled;
+ has_autofilled_ = true;
+ metrics_recorder_->SetManagerAction(
+ PasswordFormMetricsRecorder::kManagerActionAutofilled);
+ metrics_recorder_->RecordFillEvent(
+ PasswordFormMetricsRecorder::kManagerFillEventAutofilled);
base::RecordAction(base::UserMetricsAction("PasswordManager_Autofilled"));
}
if (ShouldShowInitialPasswordAccountSuggestions()) {
@@ -747,10 +676,18 @@ void PasswordFormManager::ProcessFrameInternal(
void PasswordFormManager::ProcessLoginPrompt() {
DCHECK_NE(PasswordForm::SCHEME_HTML, observed_form_.scheme);
- if (!preferred_match_)
+ if (!preferred_match_) {
+ DCHECK(best_matches_.empty());
+ metrics_recorder_->RecordFillEvent(
+ PasswordFormMetricsRecorder::kManagerFillEventNoCredential);
return;
+ }
- manager_action_ = kManagerActionAutofilled;
+ has_autofilled_ = true;
+ metrics_recorder_->SetManagerAction(
+ PasswordFormMetricsRecorder::kManagerActionAutofilled);
+ metrics_recorder_->RecordFillEvent(
+ PasswordFormMetricsRecorder::kManagerFillEventAutofilled);
password_manager_->AutofillHttpAuth(best_matches_, *preferred_match_);
}
@@ -835,8 +772,8 @@ void PasswordFormManager::SendVoteOnCredentialsReuse(
FormStructure pending_structure(pending->form_data);
FormStructure observed_structure(observed.form_data);
- if (pending_structure.FormSignatureAsStr() !=
- observed_structure.FormSignatureAsStr()) {
+ 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.
@@ -975,6 +912,7 @@ void PasswordFormManager::AddGeneratedVote(
autofill::AutofillField* field = form_structure->field(i);
if (field->name == generation_element_) {
field->set_generation_type(type);
+ field->set_generated_password_changed(generated_password_changed_);
break;
}
}
@@ -1002,10 +940,11 @@ void PasswordFormManager::CreatePendingCredentials() {
DCHECK(submitted_form_);
base::string16 password_to_save(PasswordToSave(*submitted_form_));
- // Make sure the important fields stay the same as the initially observed or
- // autofilled ones, as they may have changed if the user experienced a login
- // failure.
- // Look for these credentials in the list containing auto-fill entries.
+ // Look for the actually submitted credentials in the list of previously saved
+ // credentials that were available to autofilling.
+ // This first match via FindBestSavedMatch focuses on matches by username and
+ // falls back to password based matches if |submitted_form_| has no username
+ // filled.
const PasswordForm* saved_form = FindBestSavedMatch(submitted_form_.get());
if (saved_form != nullptr) {
// The user signed in with a login we autofilled.
@@ -1014,11 +953,11 @@ void PasswordFormManager::CreatePendingCredentials() {
pending_credentials_.password_value != password_to_save;
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
+ // from Android apps, store a copy with the current origin and signon
// realm. This ensures that on the next visit, a precise match is found.
is_new_login_ = true;
- SetUserAction(password_overridden_ ? kUserActionOverridePassword
- : kUserActionChoosePslMatch);
+ SetUserAction(password_overridden_ ? UserAction::kOverridePassword
+ : UserAction::kChoosePslMatch);
// Since this credential will not overwrite a previously saved credential,
// username_value can be updated now.
@@ -1046,7 +985,7 @@ void PasswordFormManager::CreatePendingCredentials() {
//
// However, if the user changes the suggested password, it might indicate
// that the autofilled credentials and |submitted_form_|
- // actually correspond to two different accounts (see
+ // actually correspond to two different accounts (see
// http://crbug.com/385619). In that case the user should be asked again
// before saving the password. This is ensured by setting
// |password_overriden_| on |pending_credentials_| to false and setting
@@ -1065,10 +1004,13 @@ void PasswordFormManager::CreatePendingCredentials() {
pending_credentials_.is_public_suffix_match = false;
password_overridden_ = false;
}
- } else { // Not a PSL match.
+ } else { // Not a PSL match but a match of an already stored credential.
is_new_login_ = false;
- if (password_overridden_)
- SetUserAction(kUserActionOverridePassword);
+ if (password_overridden_) {
+ // Stored credential matched by username but with mismatching password.
+ // This means the user has overridden the password.
+ SetUserAction(UserAction::kOverridePassword);
+ }
}
} else if (other_possible_username_action_ ==
ALLOW_OTHER_POSSIBLE_USERNAMES &&
@@ -1082,30 +1024,45 @@ void PasswordFormManager::CreatePendingCredentials() {
is_new_login_ = false;
} else if (!best_matches_.empty() &&
submitted_form_->type != autofill::PasswordForm::TYPE_API &&
- (submitted_form_->IsPossibleChangePasswordFormWithoutUsername() ||
- submitted_form_->username_element.empty())) {
+ submitted_form_->username_element.empty()) {
+ // This branch deals with the case that the submitted form has no username
+ // element and needs to decide whether to offer to update any credentials.
+ // In that case, the user can select any previously stored credential as
+ // the one to update, but we still try to find the best candidate.
+
+ // Find the best candidate to select by default in the password update
+ // bubble. If no best candidate is found, any one can be offered.
const PasswordForm* best_update_match =
FindBestMatchForUpdatePassword(submitted_form_->password_value);
+ // A retry password form is one that consists of only an "old password"
+ // field, i.e. one that is not a "new password".
retry_password_form_password_update_ =
submitted_form_->username_element.empty() &&
submitted_form_->new_password_element.empty();
is_new_login_ = false;
if (best_update_match) {
+ // Chose |best_update_match| to be updated.
pending_credentials_ = *best_update_match;
} else if (has_generated_password_) {
- // If a password was generated and we didn't find match we have to save it
- // in separate entry since we have to store it but we don't know where.
+ // If a password was generated and we didn't find a match, we have to save
+ // it in a separate entry since we have to store it but we don't know
+ // where.
CreatePendingCredentialsForNewCredentials();
is_new_login_ = true;
} else {
- // We don't care about |pending_credentials_| if we didn't find the best
- // match, since the user will select the correct one.
+ // We don't have a good candidate to choose as the default credential for
+ // the update bubble and the user has to pick one.
+ // We set |pending_credentials_| to the bare minimum, which is the correct
+ // origin.
pending_credentials_.origin = submitted_form_->origin;
}
} else {
+ // No stored credentials can be matched to the submitted form. Offer to
+ // save new credentials.
CreatePendingCredentialsForNewCredentials();
+ // Generate username correction votes.
FindCorrectedUsernameElement(submitted_form_->username_value,
submitted_form_->password_value);
}
@@ -1114,7 +1071,7 @@ void PasswordFormManager::CreatePendingCredentials() {
pending_credentials_.action = submitted_form_->action;
// If the user selected credentials we autofilled from a PasswordForm
// that contained no action URL (IE6/7 imported passwords, for example),
- // bless it with the action URL from the observed form. See bug 1107719.
+ // bless it with the action URL from the observed form. See b/1107719.
if (pending_credentials_.action.is_empty())
pending_credentials_.action = observed_form_.action;
}
@@ -1135,7 +1092,7 @@ void PasswordFormManager::CreatePendingCredentials() {
pending_credentials_.signon_realm = submitted_form_->signon_realm;
}
- if (user_action_ == kUserActionOverridePassword &&
+ if (user_action_ == UserAction::kOverridePassword &&
pending_credentials_.type == PasswordForm::TYPE_GENERATED &&
!has_generated_password_) {
metrics_util::LogPasswordGenerationSubmissionEvent(
@@ -1242,14 +1199,19 @@ bool PasswordFormManager::IsBlacklistMatch(
const PasswordForm* PasswordFormManager::FindBestMatchForUpdatePassword(
const base::string16& password) const {
+ // This function is called for forms that do not contain a username field.
+ // This means that we cannot update credentials based on a matching username
+ // and that we may need to show an update prompt.
if (best_matches_.size() == 1 && !has_generated_password_) {
- // In case when the user has only one credential and the current password is
- // not generated, consider it the same as is being saved.
+ // In case the submitted form contained no username but a password, and if
+ // the user has only one credential stored, return it as the one that should
+ // be updated.
return best_matches_.begin()->second;
}
if (password.empty())
return nullptr;
+ // Return any existing credential that has the same |password| saved already.
for (const auto& key_value : best_matches_) {
if (key_value.second->password_value == password)
return key_value.second;
@@ -1258,19 +1220,33 @@ const PasswordForm* PasswordFormManager::FindBestMatchForUpdatePassword(
}
const PasswordForm* PasswordFormManager::FindBestSavedMatch(
- const PasswordForm* form) const {
- if (!form->federation_origin.unique())
+ const PasswordForm* submitted_form) const {
+ if (!submitted_form->federation_origin.unique())
return nullptr;
- auto it = best_matches_.find(form->username_value);
+
+ // Return form with matching |username_value|.
+ auto it = best_matches_.find(submitted_form->username_value);
if (it != best_matches_.end())
return it->second;
- if (form->type == autofill::PasswordForm::TYPE_API)
- // Match Credential API forms only by username.
+
+ // Match Credential API forms only by username. Stop here if nothing was found
+ // above.
+ if (submitted_form->type == autofill::PasswordForm::TYPE_API)
return nullptr;
- if (!form->username_element.empty() || !form->new_password_element.empty())
+
+ // Verify that the submitted form has no username and no "new password"
+ // and bail out with a nullptr otherwise.
+ bool submitted_form_has_username = !submitted_form->username_element.empty();
+ bool submitted_form_has_new_password_element =
+ !submitted_form->new_password_element.empty();
+ if (submitted_form_has_username || submitted_form_has_new_password_element)
return nullptr;
+
+ // At this line we are certain that the submitted form contains only a
+ // password field that is not a "new password". Now we can check whether we
+ // have a match by password of an already saved credential.
for (const auto& stored_match : best_matches_) {
- if (stored_match.second->password_value == form->password_value)
+ if (stored_match.second->password_value == submitted_form->password_value)
return stored_match.second;
}
return nullptr;
@@ -1278,7 +1254,7 @@ const PasswordForm* PasswordFormManager::FindBestSavedMatch(
void PasswordFormManager::CreatePendingCredentialsForNewCredentials() {
// User typed in a new, unknown username.
- SetUserAction(kUserActionOverrideUsernameAndPassword);
+ SetUserAction(UserAction::kOverrideUsernameAndPassword);
pending_credentials_ = observed_form_;
if (submitted_form_->was_parsed_using_autofill_predictions)
pending_credentials_.username_element = submitted_form_->username_element;
@@ -1310,39 +1286,30 @@ void PasswordFormManager::OnNeverClicked() {
}
void PasswordFormManager::OnNoInteraction(bool is_update) {
- if (is_update)
+ if (is_update) {
UploadPasswordVote(observed_form_, autofill::PROBABLY_NEW_PASSWORD,
std::string());
- else {
+ } else {
UploadPasswordVote(pending_credentials_, autofill::UNKNOWN_TYPE,
std::string());
}
}
+void PasswordFormManager::SetHasGeneratedPassword(bool generated_password) {
+ has_generated_password_ = generated_password;
+ metrics_recorder_->SetHasGeneratedPassword(generated_password);
+}
+
void PasswordFormManager::LogSubmitPassed() {
- if (submit_result_ != kSubmitResultFailed) {
- if (has_generated_password_) {
- metrics_util::LogPasswordGenerationSubmissionEvent(
- metrics_util::PASSWORD_SUBMITTED);
- } else if (generation_available_) {
- metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
- metrics_util::PASSWORD_SUBMITTED);
- }
- }
- base::RecordAction(base::UserMetricsAction("PasswordManager_LoginPassed"));
- submit_result_ = kSubmitResultPassed;
+ metrics_recorder_->LogSubmitPassed();
}
void PasswordFormManager::LogSubmitFailed() {
- if (has_generated_password_) {
- metrics_util::LogPasswordGenerationSubmissionEvent(
- metrics_util::GENERATED_PASSWORD_FORCE_SAVED);
- } else if (generation_available_) {
- metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
- metrics_util::PASSWORD_SUBMISSION_FAILED);
- }
- base::RecordAction(base::UserMetricsAction("PasswordManager_LoginFailed"));
- submit_result_ = kSubmitResultFailed;
+ metrics_recorder_->LogSubmitFailed();
+}
+
+void PasswordFormManager::MarkGenerationAvailable() {
+ metrics_recorder_->MarkGenerationAvailable();
}
void PasswordFormManager::WipeStoreCopyIfOutdated() {
@@ -1380,6 +1347,67 @@ void PasswordFormManager::GrabFetcher(std::unique_ptr<FormFetcher> fetcher) {
form_fetcher_->AddConsumer(this);
}
+std::unique_ptr<PasswordFormManager> PasswordFormManager::Clone() {
+ // Fetcher is cloned to avoid re-fetching data from PasswordStore.
+ std::unique_ptr<FormFetcher> fetcher = form_fetcher_->Clone();
+
+ // Some data is filled through the constructor. No PasswordManagerDriver is
+ // needed, because the UI does not need any functionality related to the
+ // renderer process, to which the driver serves as an interface. The full
+ // |observed_form_| needs to be copied, because it is used to created the
+ // blacklisting entry if needed.
+ auto result = base::MakeUnique<PasswordFormManager>(
+ password_manager_, client_, base::WeakPtr<PasswordManagerDriver>(),
+ observed_form_, form_saver_->Clone(), fetcher.get());
+ result->Init(metrics_recorder_);
+
+ // The constructor only can take a weak pointer to the fetcher, so moving the
+ // owning one needs to happen explicitly.
+ result->GrabFetcher(std::move(fetcher));
+
+ // |best_matches_| are skipped, because those are regenerated from the new
+ // fetcher automatically.
+
+ // These data members all satisfy:
+ // (1) They could have been changed by |*this| between its construction and
+ // calling Clone().
+ // (2) They are potentially used in the clone as the clone is used in the UI
+ // code.
+ // (3) They are not changed during ProcessMatches, triggered at some point
+ // by the cloned FormFetcher.
+ if (submitted_form_)
+ result->submitted_form_ = base::MakeUnique<PasswordForm>(*submitted_form_);
+ result->other_possible_username_action_ = other_possible_username_action_;
+ if (username_correction_vote_) {
+ result->username_correction_vote_ =
+ base::MakeUnique<PasswordForm>(*username_correction_vote_);
+ }
+ result->pending_credentials_ = pending_credentials_;
+ result->is_new_login_ = is_new_login_;
+ result->has_autofilled_ = has_autofilled_;
+ 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->selected_username_ = selected_username_;
+ result->is_possible_change_password_form_without_username_ =
+ is_possible_change_password_form_without_username_;
+ result->user_action_ = user_action_;
+
+ return result;
+}
+
+metrics_util::CredentialSourceType PasswordFormManager::GetCredentialSource() {
+ return metrics_util::CredentialSourceType::kPasswordManager;
+}
+
void PasswordFormManager::SendVotesOnSave() {
if (observed_form_.IsPossibleChangePasswordFormWithoutUsername())
return;
@@ -1403,7 +1431,7 @@ void PasswordFormManager::SendVotesOnSave() {
// to see if they are valid account creation forms.
if (pending_credentials_.times_used == 0) {
autofill::ServerFieldType password_type = autofill::PASSWORD;
- if (does_look_like_signup_form_)
+ if (submitted_form_->does_look_like_signup_form)
password_type = autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD;
UploadPasswordVote(pending_credentials_, password_type, std::string());
if (username_correction_vote_) {
@@ -1430,22 +1458,8 @@ void PasswordFormManager::SendSignInVote(const FormData& form_data) {
}
void PasswordFormManager::SetUserAction(UserAction user_action) {
- if (user_action == kUserActionChoose) {
- base::RecordAction(
- base::UserMetricsAction("PasswordManager_UsedNonDefaultUsername"));
- } else if (user_action == kUserActionChoosePslMatch) {
- base::RecordAction(
- base::UserMetricsAction("PasswordManager_ChoseSubdomainPassword"));
- } else if (user_action == kUserActionOverridePassword) {
- base::RecordAction(
- base::UserMetricsAction("PasswordManager_LoggedInWithNewPassword"));
- } else if (user_action == kUserActionOverrideUsernameAndPassword) {
- base::RecordAction(
- base::UserMetricsAction("PasswordManager_LoggedInWithNewUsername"));
- } else {
- NOTREACHED();
- }
user_action_ = user_action;
+ metrics_recorder_->SetUserAction(user_action);
}
base::Optional<PasswordForm> PasswordFormManager::UpdatePendingAndGetOldKey(
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 2bc3bb26ab8..3c56130a783 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.h
+++ b/chromium/components/password_manager/core/browser/password_form_manager.h
@@ -21,7 +21,10 @@
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/form_fetcher.h"
+#include "components/password_manager/core/browser/password_form_metrics_recorder.h"
+#include "components/password_manager/core/browser/password_form_user_action.h"
#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"
using autofill::FormData;
@@ -46,6 +49,8 @@ class PasswordFormManager : public FormFetcher::Consumer {
// |form_fetcher| to get saved data about the form. |form_fetcher| must not be
// destroyed before |this|.
//
+ // Make sure to also call Init before using |*this|.
+ //
// TODO(crbug.com/621355): So far, |form_fetcher| can be null. In that case
// |this| creates an instance of it itself (meant for production code). Once
// the fetcher is shared between PasswordFormManager instances, it will be
@@ -58,6 +63,10 @@ class PasswordFormManager : public FormFetcher::Consumer {
FormFetcher* form_fetcher);
~PasswordFormManager() override;
+ // Call this after construction to complete initialization. If
+ // |metrics_recorder| is null, a fresh one is created.
+ void Init(scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder);
+
// Flags describing the result of comparing two forms as performed by
// DoesMatch. Individual flags are only relevant for HTML forms, but
// RESULT_COMPLETE_MATCH will also be returned to indicate non-HTML forms
@@ -146,16 +155,34 @@ class PasswordFormManager : public FormFetcher::Consumer {
// |pending_credentials_|.
void Update(const autofill::PasswordForm& credentials_to_update);
+ // Updates the username value. Called when user edits the username and clicks
+ // the save button. Updates the username and modifies internal state
+ // accordingly. This function should be called after ProvisionallySave().
+ void UpdateUsername(const base::string16& new_username);
+
// Call these if/when we know the form submission worked or failed.
// These routines are used to update internal statistics ("ActionsTaken").
void LogSubmitPassed();
void LogSubmitFailed();
+ // Called when generated password is accepted or changed by user.
+ void PresaveGeneratedPassword(const autofill::PasswordForm& form);
+
+ // Called when user removed a generated password.
+ void PasswordNoLongerGenerated();
+
// These functions are used to determine if this form has had it's password
// auto generated by the browser.
bool has_generated_password() const { return has_generated_password_; }
- void set_has_generated_password(bool generated_password) {
- has_generated_password_ = generated_password;
+ void SetHasGeneratedPassword(bool generated_password);
+
+ // 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;
}
bool is_manual_generation() { return is_manual_generation_; }
@@ -182,7 +209,7 @@ class PasswordFormManager : public FormFetcher::Consumer {
}
// Called if the user could generate a password for this form.
- void MarkGenerationAvailable() { generation_available_ = true; }
+ void MarkGenerationAvailable();
// Returns the provisionally saved form, if it exists, otherwise nullptr.
const autofill::PasswordForm* submitted_form() const {
@@ -254,6 +281,22 @@ class PasswordFormManager : public FormFetcher::Consumer {
// adds itself as a consumer of the new one.
void GrabFetcher(std::unique_ptr<FormFetcher> fetcher);
+ PasswordFormMetricsRecorder* metrics_recorder() {
+ return metrics_recorder_.get();
+ }
+
+ // Create a copy of |*this| which can be passed to the code handling
+ // save-password related UI. This omits some parts of the internal data, so
+ // the result is not identical to the original.
+ // TODO(crbug.com/739366): Replace with translating one appropriate class into
+ // another one.
+ std::unique_ptr<PasswordFormManager> Clone();
+
+ // Returns who created this PasswordFormManager. The Credential Management API
+ // uses a derived class of the PasswordFormManager that can indicate its
+ // origin.
+ virtual metrics_util::CredentialSourceType GetCredentialSource();
+
protected:
// FormFetcher::Consumer:
void ProcessMatches(
@@ -261,65 +304,6 @@ class PasswordFormManager : public FormFetcher::Consumer {
size_t filtered_count) override;
private:
- // ManagerAction - What does the manager do with this form? Either it
- // fills it, or it doesn't. If it doesn't fill it, that's either
- // because it has no match or it is disabled via the AUTOCOMPLETE=off
- // attribute. Note that if we don't have an exact match, we still provide
- // candidates that the user may end up choosing.
- enum ManagerAction {
- kManagerActionNone = 0,
- kManagerActionAutofilled,
- kManagerActionBlacklisted_Obsolete,
- kManagerActionMax
- };
-
- // Same as above, without the obsoleted 'Blacklisted' action.
- enum ManagerActionNew {
- kManagerActionNewNone,
- kManagerActionNewAutofilled,
- kManagerActionNewMax
- };
-
- // UserAction - What does the user do with this form? If they do nothing
- // (either by accepting what the password manager did, or by simply (not
- // typing anything at all), you get None. If there were multiple choices and
- // the user selects one other than the default, you get Choose, if user
- // selects an entry from matching against the Public Suffix List you get
- // ChoosePslMatch, if the user types in a new value for just the password you
- // get OverridePassword, and if the user types in a new value for the
- // username and password you get OverrideUsernameAndPassword.
- enum UserAction {
- kUserActionNone = 0,
- kUserActionChoose,
- kUserActionChoosePslMatch,
- kUserActionOverridePassword,
- kUserActionOverrideUsernameAndPassword,
- kUserActionMax
- };
-
- // Result - What happens to the form?
- enum SubmitResult {
- kSubmitResultNotSubmitted = 0,
- kSubmitResultFailed,
- kSubmitResultPassed,
- kSubmitResultMax
- };
-
- // What the form is used for. kFormTypeUnspecified is only set before
- // the SetSubmittedForm() is called, and should never be actually uploaded.
- enum FormType {
- kFormTypeLogin,
- kFormTypeLoginNoUsername,
- kFormTypeChangePasswordEnabled,
- kFormTypeChangePasswordDisabled,
- kFormTypeChangePasswordNoUsername,
- kFormTypeSignup,
- kFormTypeSignupNoUsername,
- kFormTypeLoginAndSignup,
- kFormTypeUnspecified,
- kFormTypeMax
- };
-
// The outcome of the form classifier.
enum FormClassifierOutcome {
kNoOutcome,
@@ -327,35 +311,6 @@ class PasswordFormManager : public FormFetcher::Consumer {
kFoundGenerationElement
};
- // The maximum number of combinations of the three preceding enums.
- // This is used when recording the actions taken by the form in UMA.
- static const int kMaxNumActionsTaken =
- kManagerActionMax * kUserActionMax * kSubmitResultMax;
-
- // Enumerates whether there were `suppressed` credentials. These are stored
- // credentials that were not filled, even though they might be related to the
- // |observed_form_|. See FormFetcher::GetSuppressed* for details.
- //
- // If suppressed credentials exist, it is also recorded whether their username
- // and/or password matched those submitted.
- enum SuppressedAccountExistence {
- kSuppressedAccountNone,
- // Recorded when there exists a suppressed account, but there was no
- // submitted form to compare its username and password to.
- kSuppressedAccountExists,
- // Recorded when there was a submitted form.
- kSuppressedAccountExistsDifferentUsername,
- kSuppressedAccountExistsSameUsername,
- kSuppressedAccountExistsSameUsernameAndPassword,
- kSuppressedAccountExistenceMax,
- };
-
- // The maximum number of combinations recorded into histograms in the
- // PasswordManager.SuppressedAccount.* family.
- static constexpr int kMaxSuppressedAccountStats =
- kSuppressedAccountExistenceMax * kManagerActionNewMax * kUserActionMax *
- kSubmitResultMax;
-
// Through |driver|, supply the associated frame with appropriate information
// (fill data, whether to allow password generation, etc.).
void ProcessFrameInternal(const base::WeakPtr<PasswordManagerDriver>& driver);
@@ -426,27 +381,6 @@ class PasswordFormManager : public FormFetcher::Consumer {
bool UpdatePendingCredentialsIfUsernameChanged(
const autofill::PasswordForm& form);
- // Converts the "ActionsTaken" fields into an int so they can be logged to
- // UMA.
- int GetActionsTaken() const;
-
- // When supplied with the list of all |suppressed_forms| that belong to
- // certain suppressed credential type (see FormFetcher::GetSuppressed*),
- // filters that list down to forms that are either |manual_or_generated|, and
- // based on that, computes the histogram sample that is a mixed-based
- // representation of a combination of four attributes:
- // -- whether there were suppressed credentials (and if so, their relation to
- // the submitted username/password).
- // -- whether the |observed_form_| got ultimately submitted
- // -- what action the password manager performed (|manager_action_|),
- // -- and what action the user performed (|user_action_|_).
- int GetHistogramSampleForSuppressedAccounts(
- const std::vector<const autofill::PasswordForm*> suppressed_forms,
- autofill::PasswordForm::Type manual_or_generated) const;
-
- // Records all histograms in the PasswordManager.SuppressedAccount.* family.
- void RecordHistogramsOnSuppressedAccounts() const;
-
// 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,
@@ -467,10 +401,10 @@ class PasswordFormManager : public FormFetcher::Consumer {
// represents credentials that were not previosly saved.
void CreatePendingCredentialsForNewCredentials();
- // If |best_matches| contains only one entry then return this entry. Otherwise
- // for empty |password| return nullptr and for non-empty |password| returns
- // the unique entry in |best_matches_| with the same password, if it exists,
- // and nullptr otherwise.
+ // If |best_matches_| contains only one entry, then return this entry.
+ // Otherwise for empty |password| return nullptr and for non-empty |password|
+ // returns the any entry in |best_matches_| with the same password, if it
+ // exists, and nullptr otherwise.
const autofill::PasswordForm* FindBestMatchForUpdatePassword(
const base::string16& password) const;
@@ -506,8 +440,9 @@ class PasswordFormManager : public FormFetcher::Consumer {
std::vector<autofill::PasswordForm>* credentials_to_update);
// Set of nonblacklisted PasswordForms from the DB that best match the form
- // being managed by |this|, indexed by username. They are owned by
- // |form_fetcher_|.
+ // being managed by |this|, indexed by username. This means the best
+ // PasswordForm for each username is stored in this map. The PasswordForms are
+ // owned by |form_fetcher_|.
std::map<base::string16, const autofill::PasswordForm*> best_matches_;
// Set of forms from PasswordStore that correspond to the current site and
@@ -561,9 +496,15 @@ class PasswordFormManager : public FormFetcher::Consumer {
// to an existing one.
bool is_new_login_;
+ // Whether the form was autofilled with credentials.
+ bool has_autofilled_;
+
// 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_;
@@ -590,9 +531,6 @@ class PasswordFormManager : public FormFetcher::Consumer {
// and it was entered on a retry password form.
bool retry_password_form_password_update_;
- // Whether the user can choose to generate a password for this form.
- bool generation_available_;
-
// Set if the user has selected one of the other possible usernames in
// |pending_credentials_|.
base::string16 selected_username_;
@@ -612,10 +550,6 @@ class PasswordFormManager : public FormFetcher::Consumer {
// |observed_form_| but also on the credentials that the user submitted.
bool is_possible_change_password_form_without_username_;
- // True if |submitted_form_| looks like SignUp form according to
- // local heuristics.
- bool does_look_like_signup_form_ = false;
-
// The client which implements embedder-specific PasswordManager operations.
PasswordManagerClient* client_;
@@ -626,17 +560,9 @@ class PasswordFormManager : public FormFetcher::Consumer {
// the filling information when needed.
std::vector<base::WeakPtr<PasswordManagerDriver>> drivers_;
- // 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 manager is destroyed.
- ManagerAction manager_action_;
+ // Records the action the user has taken while interacting with the password
+ // form.
UserAction user_action_;
- SubmitResult submit_result_;
-
- // Form type of the form that |this| is managing. Set after SetSubmittedForm()
- // as our classification of the form can change depending on what data the
- // user has entered.
- FormType form_type_;
// FormSaver instance used by |this| to all tasks related to storing
// credentials.
@@ -652,6 +578,10 @@ class PasswordFormManager : public FormFetcher::Consumer {
// was created, is secure.
bool is_main_frame_secure_ = false;
+ // Takes care of recording metrics and events for this PasswordFormManager.
+ // Make sure to call Init before using |*this|, to ensure it is not null.
+ scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder_;
+
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 e4ed9f70800..57fb1d3d344 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,6 +13,8 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
@@ -39,6 +41,7 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
+#include "components/ukm/test_ukm_recorder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -50,9 +53,11 @@ using autofill::PasswordForm;
using autofill::PossibleUsernamePair;
using base::ASCIIToUTF16;
using ::testing::_;
+using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Mock;
using ::testing::NiceMock;
+using ::testing::Pair;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SaveArg;
@@ -90,6 +95,13 @@ class MockFormSaver : public StubFormSaver {
void(const autofill::PasswordForm& pending,
std::map<base::string16, const PasswordForm*>* best_matches,
const autofill::PasswordForm** preferred_match));
+ MOCK_METHOD1(PresaveGeneratedPassword,
+ void(const autofill::PasswordForm& generated));
+ MOCK_METHOD0(RemovePresavedPassword, void());
+
+ std::unique_ptr<FormSaver> Clone() override {
+ return base::MakeUnique<MockFormSaver>();
+ }
// Convenience downcasting method.
static MockFormSaver& Get(PasswordFormManager* form_manager) {
@@ -104,6 +116,7 @@ class MockFormFetcher : public FakeFormFetcher {
public:
MOCK_METHOD1(AddConsumer, void(Consumer*));
MOCK_METHOD1(RemoveConsumer, void(Consumer*));
+ MOCK_METHOD0(Clone, std::unique_ptr<FormFetcher>());
};
MATCHER_P(CheckUsername, username_value, "Username incorrect") {
@@ -153,9 +166,10 @@ MATCHER_P3(CheckUploadedAutofillTypesAndSignature,
return true;
}
-MATCHER_P2(CheckUploadedGenerationTypesAndSignature,
+MATCHER_P3(CheckUploadedGenerationTypesAndSignature,
form_signature,
expected_generation_types,
+ generated_password_changed,
"Unexpected generation types or form signature") {
if (form_signature != arg.FormSignatureAsStr()) {
// Unexpected form's signature.
@@ -184,6 +198,12 @@ MATCHER_P2(CheckUploadedGenerationTypesAndSignature,
<< ", but found " << field->generation_type();
return false;
}
+
+ if (field->generation_type() !=
+ autofill::AutofillUploadContents::Field::IGNORED_GENERATION_POPUP) {
+ EXPECT_EQ(generated_password_changed,
+ field->generated_password_changed());
+ }
}
}
return true;
@@ -390,6 +410,7 @@ class PasswordFormManagerTest : public testing::Test {
form_manager_.reset(new PasswordFormManager(
password_manager_.get(), &client_, client_.driver(), observed_form_,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fake_form_fetcher_));
+ form_manager_->Init(nullptr);
}
// Save saved_match() for observed_form() where |observed_form_data|,
@@ -409,6 +430,7 @@ class PasswordFormManagerTest : public testing::Test {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
PasswordForm match = CreateSavedMatch(false);
match.generation_upload_status = status;
match.times_used = times_used;
@@ -498,6 +520,7 @@ class PasswordFormManagerTest : public testing::Test {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
@@ -605,11 +628,13 @@ class PasswordFormManagerTest : public testing::Test {
void GeneratedVoteUploadTest(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);
PasswordForm form(*observed_form());
form.form_data = saved_match()->form_data;
@@ -641,7 +666,7 @@ class PasswordFormManagerTest : public testing::Test {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
-
+ form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::ServerFieldTypeSet expected_available_field_types;
@@ -655,7 +680,9 @@ class PasswordFormManagerTest : public testing::Test {
: form.password_element;
form_manager.set_generation_element(generation_element);
form_manager.set_generation_popup_was_shown(true);
- form_manager.set_has_generated_password(has_generated_password);
+ form_manager.SetHasGeneratedPassword(has_generated_password);
+ if (has_generated_password)
+ form_manager.set_generated_password_changed(generated_password_changed);
// Figure out expected generation event type.
autofill::AutofillUploadContents::Field::PasswordGenerationType
@@ -673,7 +700,8 @@ class PasswordFormManagerTest : public testing::Test {
*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(
CheckUploadedGenerationTypesAndSignature(
- form_structure.FormSignatureAsStr(), expected_generation_types),
+ form_structure.FormSignatureAsStr(), expected_generation_types,
+ generated_password_changed),
false, expected_available_field_types, std::string(), true));
form_manager.ProvisionallySave(
@@ -693,6 +721,30 @@ class PasswordFormManagerTest : public testing::Test {
client()->mock_driver()->mock_autofill_download_manager());
}
+ // Returns the sample values of |metric_value| in events named |event_name|.
+ std::vector<int64_t> GetAllUkmSamples(
+ const ukm::TestAutoSetUkmRecorder& test_ukm_recorder,
+ base::StringPiece event_name,
+ base::StringPiece metric_name) {
+ std::vector<int64_t> values;
+ const ukm::UkmSource* source =
+ test_ukm_recorder.GetSourceForSourceId(client()->GetUkmSourceId());
+ if (!source)
+ return values;
+
+ for (size_t i = 0; i < test_ukm_recorder.entries_count(); ++i) {
+ const ukm::mojom::UkmEntry* entry = test_ukm_recorder.GetEntry(i);
+ if (entry->event_hash != base::HashMetricName(event_name))
+ continue;
+
+ for (const ukm::mojom::UkmMetricPtr& metric : entry->metrics) {
+ if (metric->metric_hash == base::HashMetricName(metric_name))
+ values.push_back(metric->value);
+ }
+ }
+ return values;
+ }
+
PasswordForm* observed_form() { return &observed_form_; }
PasswordForm* saved_match() { return &saved_match_; }
PasswordForm* psl_saved_match() { return &psl_saved_match_; }
@@ -753,6 +805,7 @@ class PasswordFormManagerTest : public testing::Test {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<NiceMock<MockFormSaver>>(), fetcher);
+ form_manager.Init(nullptr);
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, _, _, _, _))
@@ -768,7 +821,7 @@ class PasswordFormManagerTest : public testing::Test {
if (manager_action != SimulatedManagerAction::NONE)
matches.push_back(&http_stored_form);
- // Extra mile: kUserActionChoose is only recorded if there were multiple
+ // Extra mile: kChoose is only recorded if there were multiple
// logins available and the preferred one was changed.
PasswordForm http_stored_form2 = http_stored_form;
if (manager_action == SimulatedManagerAction::OFFERED) {
@@ -933,6 +986,7 @@ TEST_F(PasswordFormManagerTest, TestBlacklistMatching) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
// Doesn't apply because it is just a PSL match of the observed form.
PasswordForm blacklisted_psl = *observed_form();
@@ -1073,6 +1127,7 @@ TEST_F(PasswordFormManagerTest, TestNewLoginFromNewPasswordElement) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
// User enters current and new credentials to the observed form.
@@ -1156,6 +1211,7 @@ TEST_F(PasswordFormManagerTest, TestUpdatePasswordFromNewPasswordElement) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
@@ -1207,6 +1263,7 @@ TEST_F(PasswordFormManagerTest, TestIgnoreResult_Paths) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), observed,
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
PasswordForm saved_form = observed;
saved_form.origin = GURL("https://accounts.google.com/a/OtherLoginAuth");
@@ -1378,6 +1435,7 @@ TEST_F(PasswordFormManagerTest,
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), signup_form,
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
PasswordForm simulated_result = CreateSavedMatch(false);
fetcher.SetNonFederated({&simulated_result}, 0u);
@@ -1530,6 +1588,7 @@ TEST_F(PasswordFormManagerTest, TestUpdateIncompleteCredentials) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), encountered_form,
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
PasswordForm incomplete_form;
incomplete_form.origin = GURL("http://accounts.google.com/LoginAuth");
@@ -1709,6 +1768,7 @@ TEST_F(PasswordFormManagerTest, InvalidActionURLsDoNotMatch) {
PasswordFormManager invalid_manager(
password_manager(), client(), client()->driver(), invalid_action_form,
base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
+ invalid_manager.Init(nullptr);
EXPECT_EQ(0, invalid_manager.DoesManage(valid_action_form, nullptr) &
PasswordFormManager::RESULT_ACTION_MATCH);
}
@@ -1726,6 +1786,7 @@ TEST_F(PasswordFormManagerTest, EmptyActionURLsDoNotMatchNonEmpty) {
PasswordFormManager empty_action_manager(
password_manager(), client(), client()->driver(), empty_action_form,
base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
+ empty_action_manager.Init(nullptr);
EXPECT_EQ(0, empty_action_manager.DoesManage(valid_action_form, nullptr) &
PasswordFormManager::RESULT_ACTION_MATCH);
}
@@ -1742,6 +1803,7 @@ TEST_F(PasswordFormManagerTest, NonHTMLFormsDoNotMatchHTMLForms) {
PasswordFormManager non_html_manager(
password_manager(), client(), kNoDriver, non_html_form,
base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
+ non_html_manager.Init(nullptr);
EXPECT_EQ(0, non_html_manager.DoesManage(html_form, nullptr) &
PasswordFormManager::RESULT_HTML_ATTRIBUTES_MATCH);
}
@@ -1779,6 +1841,7 @@ TEST_F(PasswordFormManagerTest,
PasswordFormManager secure_manager(
password_manager(), client(), client()->driver(), secure_observed_form,
base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
+ secure_manager.Init(nullptr);
// Also for HTTPS in the observed form, and HTTP in the compared form, an
// exact path match is expected.
EXPECT_EQ(0, secure_manager.DoesManage(form_longer_path, nullptr) &
@@ -1876,6 +1939,7 @@ TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *saved_match(),
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm form_to_save(*saved_match());
@@ -1900,6 +1964,7 @@ TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword_Blacklist) {
PasswordFormManager blacklist_form_manager(
password_manager(), client(), client()->driver(), *saved_match(),
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ blacklist_form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::ServerFieldTypeSet expected_available_field_types;
@@ -1996,6 +2061,7 @@ TEST_F(PasswordFormManagerTest, DriverDeletedBeforeStoreDone) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), form,
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
// Suddenly, the frame and its driver disappear.
client()->KillDriver();
@@ -2077,6 +2143,7 @@ TEST_F(PasswordFormManagerTest, TestSuggestingPasswordChangeForms) {
password_manager(), client(), client()->driver(),
observed_change_password_form, base::MakeUnique<MockFormSaver>(),
&fetcher);
+ manager_creds.Init(nullptr);
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
@@ -2113,7 +2180,7 @@ TEST_F(PasswordFormManagerTest, TestUpdateMethod) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
-
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
@@ -2178,7 +2245,7 @@ TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
-
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
@@ -2222,6 +2289,104 @@ TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) {
EXPECT_EQ(saved_match()->submit_element, new_credentials.submit_element);
}
+// Test that when user updates username, the pending credentials is updated
+// accordingly.
+TEST_F(PasswordFormManagerTest, TestUpdateUsernameMethod) {
+ fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
+
+ // User logs in, edits username.
+ PasswordForm credential(*observed_form());
+ credential.username_value = ASCIIToUTF16("oldusername");
+ credential.password_value = ASCIIToUTF16("password");
+ form_manager()->ProvisionallySave(
+ credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+ form_manager()->UpdateUsername(ASCIIToUTF16("newusername"));
+ EXPECT_EQ(form_manager()->pending_credentials().username_value,
+ ASCIIToUTF16("newusername"));
+ EXPECT_EQ(form_manager()->pending_credentials().password_value,
+ ASCIIToUTF16("password"));
+ EXPECT_EQ(form_manager()->IsNewLogin(), true);
+
+ // User clicks save, edited username is saved.
+ PasswordForm saved_result;
+ EXPECT_CALL(MockFormSaver::Get(form_manager()), Save(_, IsEmpty(), nullptr))
+ .WillOnce(SaveArg<0>(&saved_result));
+ form_manager()->Save();
+ EXPECT_EQ(ASCIIToUTF16("newusername"), saved_result.username_value);
+ EXPECT_EQ(ASCIIToUTF16("password"), saved_result.password_value);
+}
+
+// Test that when user updates username to an already existing one, is_new_login
+// status is false.
+TEST_F(PasswordFormManagerTest, TestUpdateUsernameToExisting) {
+ // We have an already existing credential.
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
+
+ // User submits credential to the observed form.
+ PasswordForm credential(*observed_form());
+ credential.username_value = ASCIIToUTF16("different_username");
+ credential.password_value = ASCIIToUTF16("different_pass");
+ credential.preferred = true;
+ form_manager()->ProvisionallySave(
+ credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+
+ // User edits the username to the already existing one.
+ form_manager()->UpdateUsername(saved_match()->username_value);
+
+ // The username in credentials is expected to be updated.
+ EXPECT_EQ(saved_match()->username_value,
+ form_manager()->pending_credentials().username_value);
+ EXPECT_EQ(ASCIIToUTF16("different_pass"),
+ form_manager()->pending_credentials().password_value);
+ EXPECT_EQ(form_manager()->IsNewLogin(), false);
+
+ // Create the expected variables for the test.
+ PasswordForm expected_pending(credential);
+ expected_pending.times_used = 1;
+ expected_pending.username_value = saved_match()->username_value;
+
+ // User clicks save, edited username is saved, password updated.
+ PasswordForm saved_result;
+ EXPECT_CALL(MockFormSaver::Get(form_manager()),
+ Update(expected_pending,
+ ElementsAre(Pair(saved_match()->username_value,
+ Pointee(*saved_match()))),
+ Pointee(IsEmpty()), nullptr));
+ form_manager()->Save();
+}
+
+// Test that when user updates username to a PSL matching credential, we should
+// handle it as a new login.
+TEST_F(PasswordFormManagerTest, TestUpdatePSLUsername) {
+ fake_form_fetcher()->SetNonFederated({psl_saved_match()}, 0u);
+ // User submits credential to the observed form.
+ PasswordForm credential(*observed_form());
+ credential.username_value = ASCIIToUTF16("some_username");
+ credential.password_value = ASCIIToUTF16("some_pass");
+ form_manager()->ProvisionallySave(
+ credential, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+
+ // User edits username to match the PSL.
+ form_manager()->UpdateUsername(psl_saved_match()->username_value);
+ EXPECT_EQ(psl_saved_match()->username_value,
+ form_manager()->pending_credentials().username_value);
+ EXPECT_EQ(true, form_manager()->IsNewLogin());
+ EXPECT_EQ(ASCIIToUTF16("some_pass"),
+ form_manager()->pending_credentials().password_value);
+
+ // User clicks save, edited username is saved.
+ PasswordForm saved_result;
+ EXPECT_CALL(MockFormSaver::Get(form_manager()),
+ Save(_,
+ ElementsAre(Pair(psl_saved_match()->username_value,
+ Pointee(*psl_saved_match()))),
+ nullptr))
+ .WillOnce(SaveArg<0>(&saved_result));
+ form_manager()->Save();
+ EXPECT_EQ(psl_saved_match()->username_value, saved_result.username_value);
+ EXPECT_EQ(ASCIIToUTF16("some_pass"), saved_result.password_value);
+}
+
// Test that if WipeStoreCopyIfOutdated is called before password store
// callback, the UMA is signalled accordingly.
TEST_F(PasswordFormManagerTest, WipeStoreCopyIfOutdated_BeforeStoreCallback) {
@@ -2233,6 +2398,7 @@ TEST_F(PasswordFormManagerTest, WipeStoreCopyIfOutdated_BeforeStoreCallback) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), form,
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
// The creation of |fetcher| keeps it waiting for store results. This test
// keeps the fetcher waiting on purpose.
@@ -2366,6 +2532,7 @@ TEST_F(PasswordFormManagerTest, TestUpdatePSLMatchedCredentials) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match(), psl_saved_match()}, 0u);
// User submits a credentials with an old username and a new password.
@@ -2418,6 +2585,7 @@ TEST_F(PasswordFormManagerTest,
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
psl_saved_match()->username_value += ASCIIToUTF16("1");
fetcher.SetNonFederated({saved_match(), psl_saved_match()}, 0u);
@@ -2459,6 +2627,7 @@ TEST_F(PasswordFormManagerTest,
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
psl_saved_match()->password_value += ASCIIToUTF16("1");
fetcher.SetNonFederated({saved_match(), psl_saved_match()}, 0u);
@@ -2499,6 +2668,7 @@ TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPSLMatched) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({psl_saved_match()}, 0u);
// User submits a credentials with an old username and a new password.
@@ -2529,7 +2699,6 @@ TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPSLMatched) {
TEST_F(PasswordFormManagerTest,
TestSavingOnChangePasswordFormGenerationNoStoredForms) {
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
- form_manager()->set_has_generated_password(true);
// User submits change password form and there is no stored credentials.
PasswordForm credentials = *observed_form();
@@ -2538,6 +2707,7 @@ TEST_F(PasswordFormManagerTest,
credentials.new_password_element = ASCIIToUTF16("NewPasswd");
credentials.new_password_value = ASCIIToUTF16("new_password");
credentials.preferred = true;
+ form_manager()->PresaveGeneratedPassword(credentials);
form_manager()->ProvisionallySave(
credentials, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -2564,7 +2734,6 @@ TEST_F(PasswordFormManagerTest,
TEST_F(PasswordFormManagerTest, TestUpdatingOnChangePasswordFormGeneration) {
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
- form_manager()->set_has_generated_password(true);
// User submits credentials for the change password form, and old password is
// coincide with password from an existing credentials, so stored credentials
@@ -2575,6 +2744,7 @@ TEST_F(PasswordFormManagerTest, TestUpdatingOnChangePasswordFormGeneration) {
credentials.new_password_element = ASCIIToUTF16("NewPasswd");
credentials.new_password_value = ASCIIToUTF16("new_password");
credentials.preferred = true;
+ form_manager()->PresaveGeneratedPassword(credentials);
form_manager()->ProvisionallySave(
credentials, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -2600,7 +2770,6 @@ TEST_F(PasswordFormManagerTest, TestUpdatingOnChangePasswordFormGeneration) {
TEST_F(PasswordFormManagerTest,
TestSavingOnChangePasswordFormGenerationNoMatchedForms) {
fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
- form_manager()->set_has_generated_password(true);
// User submits credentials for the change password form, and old password is
// not coincide with password from existing credentials, so new credentials
@@ -2612,6 +2781,7 @@ TEST_F(PasswordFormManagerTest,
credentials.new_password_element = ASCIIToUTF16("NewPasswd");
credentials.new_password_value = ASCIIToUTF16("new_password");
credentials.preferred = true;
+ form_manager()->PresaveGeneratedPassword(credentials);
form_manager()->ProvisionallySave(
credentials, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -2655,6 +2825,7 @@ TEST_F(PasswordFormManagerTest,
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
@@ -2710,15 +2881,42 @@ TEST_F(PasswordFormManagerTest, GeneratedVoteUpload) {
for (bool is_manual_generation : kFalseTrue) {
for (bool is_change_password_form : kFalseTrue) {
for (bool has_generated_password : kFalseTrue) {
- for (SavePromptInteraction interaction : kSavePromptInterations) {
- GeneratedVoteUploadTest(is_manual_generation, is_change_password_form,
- has_generated_password, interaction);
+ for (bool generated_password_changed : kFalseTrue) {
+ for (SavePromptInteraction interaction : kSavePromptInterations) {
+ GeneratedVoteUploadTest(is_manual_generation,
+ is_change_password_form,
+ has_generated_password,
+ generated_password_changed, interaction);
+ }
}
}
}
}
}
+TEST_F(PasswordFormManagerTest, PresaveGeneratedPasswordAndRemoveIt) {
+ PasswordForm credentials = *observed_form();
+
+ // Simulate the user accepted a generated password.
+ EXPECT_CALL(MockFormSaver::Get(form_manager()),
+ PresaveGeneratedPassword(credentials));
+ form_manager()->PresaveGeneratedPassword(credentials);
+ EXPECT_TRUE(form_manager()->has_generated_password());
+ EXPECT_FALSE(form_manager()->generated_password_changed());
+
+ // Simulate the user changed the presaved password.
+ credentials.password_value = ASCIIToUTF16("changed_password");
+ EXPECT_CALL(MockFormSaver::Get(form_manager()),
+ PresaveGeneratedPassword(credentials));
+ form_manager()->PresaveGeneratedPassword(credentials);
+ EXPECT_TRUE(form_manager()->has_generated_password());
+ EXPECT_TRUE(form_manager()->generated_password_changed());
+
+ // Simulate the user removed the presaved password.
+ EXPECT_CALL(MockFormSaver::Get(form_manager()), RemovePresavedPassword());
+ form_manager()->PasswordNoLongerGenerated();
+}
+
TEST_F(PasswordFormManagerTest, FormClassifierVoteUpload) {
const bool kFalseTrue[] = {false, true};
for (bool found_generation_element : kFalseTrue) {
@@ -2738,6 +2936,7 @@ TEST_F(PasswordFormManagerTest, FormClassifierVoteUpload) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
base::string16 generation_element = form.password_element;
if (found_generation_element)
form_manager.SaveGenerationFieldDetectedByClassifier(generation_element);
@@ -2775,6 +2974,7 @@ TEST_F(PasswordFormManagerTest, FieldPropertiesMasksUpload) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
DCHECK_EQ(3U, form.form_data.fields.size());
@@ -2810,6 +3010,7 @@ TEST_F(PasswordFormManagerTest, TestSavingAPIFormsWithSamePassword) {
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated({saved_match()}, 0u);
// User submits new credentials with the same password as in already saved
@@ -2876,6 +3077,7 @@ TEST_F(PasswordFormManagerTest, ProbablyAccountCreationUpload) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
PasswordForm form_to_save(form);
form_to_save.preferred = true;
@@ -2966,6 +3168,7 @@ TEST_F(PasswordFormManagerTest, RemoveResultsWithWrongScheme_ObservingHTML) {
(kCorrectScheme == PasswordForm::SCHEME_HTML ? client()->driver()
: nullptr),
observed, base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
PasswordForm match = *saved_match();
match.scheme = kCorrectScheme;
@@ -3135,6 +3338,7 @@ TEST_F(PasswordFormManagerTest, DropFetcherOnDestruction) {
auto form_manager = base::MakeUnique<PasswordFormManager>(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager->Init(nullptr);
EXPECT_EQ(form_manager.get(), added_consumer);
EXPECT_CALL(fetcher, RemoveConsumer(form_manager.get()));
@@ -3150,6 +3354,7 @@ TEST_F(PasswordFormManagerTest, GrabFetcher_Same) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), fetcher.get());
+ form_manager.Init(nullptr);
EXPECT_CALL(*fetcher, AddConsumer(_)).Times(0);
EXPECT_CALL(*fetcher, RemoveConsumer(_)).Times(0);
@@ -3192,6 +3397,7 @@ TEST_F(PasswordFormManagerTest, GrabFetcher_Remove) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<MockFormSaver>(), &old_fetcher);
+ form_manager.Init(nullptr);
EXPECT_EQ(&form_manager, added_consumer);
auto new_fetcher = base::MakeUnique<MockFormFetcher>();
@@ -3217,6 +3423,7 @@ TEST_F(PasswordFormManagerTest, UploadSignInForm_WithAutofillTypes) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm form_to_save(*observed_form());
@@ -3254,6 +3461,7 @@ TEST_F(PasswordFormManagerTest, NoUploadsForSubmittedFormWithOnlyOneField) {
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm form_to_save(*observed_form());
@@ -3278,6 +3486,7 @@ TEST_F(PasswordFormManagerTest,
base::MakeUnique<PasswordFormManager>(
password_manager(), client(), client()->driver(), *observed_form(),
base::MakeUnique<NiceMock<MockFormSaver>>(), fake_form_fetcher());
+ form_manager->Init(nullptr);
fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
form_manager.reset();
@@ -3410,6 +3619,7 @@ TEST_F(PasswordFormManagerTest, SuppressedFormsHistograms) {
SCOPED_TRACE(test_case.expected_histogram_sample_generated);
base::HistogramTester histogram_tester;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
std::vector<PasswordForm> suppressed_forms;
for (const auto* form_data : test_case.simulated_suppressed_forms) {
@@ -3418,6 +3628,11 @@ TEST_F(PasswordFormManagerTest, SuppressedFormsHistograms) {
form_data->password_value, form_data->manual_or_generated));
}
+ // Bind the UKM SourceId to any URL, it does not matter. The SourceId
+ // needs to be bound, though, for reporting to happen.
+ client()->GetUkmRecorder()->UpdateSourceURL(client()->GetUkmSourceId(),
+ GURL("https://example.com/"));
+
std::vector<const PasswordForm*> suppressed_forms_ptrs;
for (const auto& form : suppressed_forms)
suppressed_forms_ptrs.push_back(&form);
@@ -3443,11 +3658,27 @@ TEST_F(PasswordFormManagerTest, SuppressedFormsHistograms) {
::testing::ElementsAre(
base::Bucket(test_case.expected_histogram_sample_generated, 1)));
EXPECT_THAT(
+ GetAllUkmSamples(
+ test_ukm_recorder, "PasswordForm",
+ "SuppressedAccount.Generated." +
+ std::string(suppression_params.expected_histogram_suffix)),
+ ::testing::ElementsAre(
+ test_case.expected_histogram_sample_generated /
+ PasswordFormMetricsRecorder::kMaxNumActionsTakenNew));
+ EXPECT_THAT(
histogram_tester.GetAllSamples(
"PasswordManager.SuppressedAccount.Manual." +
std::string(suppression_params.expected_histogram_suffix)),
::testing::ElementsAre(
base::Bucket(test_case.expected_histogram_sample_manual, 1)));
+ EXPECT_THAT(
+ GetAllUkmSamples(
+ test_ukm_recorder, "PasswordForm",
+ "SuppressedAccount.Manual." +
+ std::string(suppression_params.expected_histogram_suffix)),
+ ::testing::ElementsAre(
+ test_case.expected_histogram_sample_manual /
+ PasswordFormMetricsRecorder::kMaxNumActionsTakenNew));
}
}
}
@@ -3476,6 +3707,7 @@ TEST_F(PasswordFormManagerTest, SuppressedHTTPSFormsHistogram_NotRecordedFor) {
base::MakeUnique<PasswordFormManager>(
password_manager(), client(), client()->driver(), https_observed_form,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager->Init(nullptr);
fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
form_manager.reset();
@@ -3495,4 +3727,141 @@ TEST_F(PasswordFormManagerTest, SuppressedHTTPSFormsHistogram_NotRecordedFor) {
"PasswordManager.SuppressedAccount.Manual.SameOrganizationName", 1);
}
+// Check that a cloned PasswordFormManager reacts correctly to Save.
+TEST_F(PasswordFormManagerTest, Clone_OnSave) {
+ FakeFormFetcher fetcher;
+ auto form_manager = base::MakeUnique<PasswordFormManager>(
+ password_manager(), client(), client()->driver(), *observed_form(),
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager->Init(nullptr);
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
+
+ PasswordForm saved_login = *observed_form();
+ saved_login.username_value = ASCIIToUTF16("newuser");
+ saved_login.password_value = ASCIIToUTF16("newpass");
+ form_manager->ProvisionallySave(
+ saved_login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+
+ const PasswordForm pending = form_manager->pending_credentials();
+
+ std::unique_ptr<PasswordFormManager> clone = form_manager->Clone();
+
+ PasswordForm passed;
+ EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty(), nullptr))
+ .WillOnce(SaveArg<0>(&passed));
+ clone->Save();
+ // The date is expected to be different. Reset it so that we can easily
+ // compare the rest with operator==.
+ passed.date_created = pending.date_created;
+ EXPECT_EQ(pending, passed);
+}
+
+// Check that a cloned PasswordFormManager reacts correctly to OnNeverClicked.
+TEST_F(PasswordFormManagerTest, Clone_OnNeverClicked) {
+ FakeFormFetcher fetcher;
+ auto form_manager = base::MakeUnique<PasswordFormManager>(
+ password_manager(), client(), client()->driver(), *observed_form(),
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ form_manager->Init(nullptr);
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
+
+ PasswordForm saved_login = *observed_form();
+ saved_login.username_value = ASCIIToUTF16("newuser");
+ saved_login.password_value = ASCIIToUTF16("newpass");
+ form_manager->ProvisionallySave(
+ saved_login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+
+ std::unique_ptr<PasswordFormManager> clone = form_manager->Clone();
+
+ EXPECT_CALL(MockFormSaver::Get(clone.get()),
+ PermanentlyBlacklist(Pointee(*observed_form())));
+ clone->OnNeverClicked();
+}
+
+// Check that a cloned PasswordFormManager works even after the original is
+// gone.
+TEST_F(PasswordFormManagerTest, Clone_SurvivesOriginal) {
+ FakeFormFetcher fetcher;
+ auto form_manager = base::MakeUnique<PasswordFormManager>(
+ password_manager(), client(), client()->driver(), *observed_form(),
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
+ form_manager->Init(nullptr);
+
+ PasswordForm saved_login = *observed_form();
+ saved_login.username_value = ASCIIToUTF16("newuser");
+ saved_login.password_value = ASCIIToUTF16("newpass");
+ form_manager->ProvisionallySave(
+ saved_login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+
+ const PasswordForm pending = form_manager->pending_credentials();
+
+ std::unique_ptr<PasswordFormManager> clone = form_manager->Clone();
+ form_manager.reset();
+
+ PasswordForm passed;
+ EXPECT_CALL(MockFormSaver::Get(clone.get()), Save(_, IsEmpty(), nullptr))
+ .WillOnce(SaveArg<0>(&passed));
+ clone->Save();
+ // The date is expected to be different. Reset it so that we can easily
+ // compare the rest with operator==.
+ passed.date_created = pending.date_created;
+ EXPECT_EQ(pending, passed);
+}
+
+// Verifies that URL keyed metrics are recorded for the filling of passwords
+// into forms and HTTP Basic auth.
+TEST_F(PasswordFormManagerTest, TestUkmForFilling) {
+ PasswordForm scheme_basic_form = *observed_form();
+ scheme_basic_form.scheme = PasswordForm::SCHEME_BASIC;
+
+ const struct {
+ // Whether the login is doing HTTP Basic Auth instead of form based login.
+ bool is_http_basic_auth;
+ // The matching stored credentials, or nullptr if none.
+ PasswordForm* fetched_form;
+ PasswordFormMetricsRecorder::ManagerAutofillEvent expected_event;
+ } kTestCases[] = {
+ {false, saved_match(),
+ PasswordFormMetricsRecorder::kManagerFillEventAutofilled},
+ {false, psl_saved_match(),
+ PasswordFormMetricsRecorder::kManagerFillEventBlockedOnInteraction},
+ {false, nullptr,
+ PasswordFormMetricsRecorder::kManagerFillEventNoCredential},
+ {true, &scheme_basic_form,
+ PasswordFormMetricsRecorder::kManagerFillEventAutofilled},
+ {true, nullptr,
+ PasswordFormMetricsRecorder::kManagerFillEventNoCredential},
+ };
+
+ for (const auto& test : kTestCases) {
+ const PasswordForm& form_to_fill =
+ test.is_http_basic_auth ? scheme_basic_form : *observed_form();
+
+ std::vector<const autofill::PasswordForm*> fetched_forms;
+ if (test.fetched_form)
+ fetched_forms.push_back(test.fetched_form);
+
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ auto metrics_recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>(
+ form_to_fill.origin.SchemeIsCryptographic(), &test_ukm_recorder,
+ test_ukm_recorder.GetNewSourceID(), form_to_fill.origin);
+ FakeFormFetcher fetcher;
+ PasswordFormManager form_manager(
+ password_manager(), client(),
+ test.is_http_basic_auth ? nullptr : client()->driver(), form_to_fill,
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ form_manager.Init(metrics_recorder);
+ fetcher.SetNonFederated(fetched_forms, 0u);
+ }
+
+ const auto* source =
+ test_ukm_recorder.GetSourceForUrl(form_to_fill.origin.spec().c_str());
+ ASSERT_TRUE(source);
+ test_ukm_recorder.ExpectMetric(*source, "PasswordForm",
+ kUkmManagerFillEvent, test.expected_event);
+ }
+}
+
} // 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
new file mode 100644
index 00000000000..c90e449422f
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc
@@ -0,0 +1,434 @@
+// 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/password_form_metrics_recorder.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/numerics/safe_conversions.h"
+#include "components/password_manager/core/browser/form_fetcher.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+
+using autofill::PasswordForm;
+
+namespace password_manager {
+
+const char kUkmSubmissionObserved[] = "Submission.Observed";
+const char kUkmSubmissionResult[] = "Submission.SubmissionResult";
+const char kUkmSubmissionFormType[] = "Submission.SubmittedFormType";
+const char kUkmUpdatingPromptShown[] = "Updating.Prompt.Shown";
+const char kUkmUpdatingPromptTrigger[] = "Updating.Prompt.Trigger";
+const char kUkmUpdatingPromptInteraction[] = "Updating.Prompt.Interaction";
+const char kUkmSavingPromptShown[] = "Saving.Prompt.Shown";
+const char kUkmSavingPromptTrigger[] = "Saving.Prompt.Trigger";
+const char kUkmSavingPromptInteraction[] = "Saving.Prompt.Interaction";
+const char kUkmManagerFillEvent[] = "ManagerFill.Action";
+const char kUkmUserActionSimplified[] = "User.ActionSimplified";
+const char kUkmUserAction[] = "User.Action";
+
+PasswordFormMetricsRecorder::PasswordFormMetricsRecorder(
+ bool is_main_frame_secure,
+ ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
+ const GURL& main_frame_url)
+ : is_main_frame_secure_(is_main_frame_secure),
+ ukm_recorder_(ukm_recorder),
+ source_id_(source_id),
+ main_frame_url_(main_frame_url),
+ ukm_entry_builder_(
+ ukm_recorder
+ ? ukm_recorder->GetEntryBuilder(source_id, "PasswordForm")
+ : nullptr) {}
+
+PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() {
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenV3", GetActionsTaken(),
+ kMaxNumActionsTaken);
+ RecordUkmMetric(kUkmUserActionSimplified, static_cast<int64_t>(user_action_));
+
+ // Use the visible main frame URL at the time the PasswordFormManager
+ // is created, in case a navigation has already started and the
+ // visible URL has changed.
+ if (!is_main_frame_secure_) {
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenOnNonSecureForm",
+ GetActionsTaken(), kMaxNumActionsTaken);
+ }
+
+ if (submit_result_ == kSubmitResultNotSubmitted) {
+ if (has_generated_password_) {
+ metrics_util::LogPasswordGenerationSubmissionEvent(
+ metrics_util::PASSWORD_NOT_SUBMITTED);
+ } else if (generation_available_) {
+ metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
+ metrics_util::PASSWORD_NOT_SUBMITTED);
+ }
+ RecordUkmMetric(kUkmSubmissionObserved, 0 /*false*/);
+ }
+
+ if (submitted_form_type_ != kSubmittedFormTypeUnspecified) {
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.SubmittedFormType",
+ submitted_form_type_, kSubmittedFormTypeMax);
+ if (!is_main_frame_secure_) {
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.SubmittedNonSecureFormType",
+ submitted_form_type_, kSubmittedFormTypeMax);
+ }
+
+ RecordUkmMetric(kUkmSubmissionFormType, submitted_form_type_);
+ }
+
+ RecordUkmMetric(kUkmUpdatingPromptShown, update_prompt_shown_);
+ RecordUkmMetric(kUkmSavingPromptShown, save_prompt_shown_);
+
+ for (const DetailedUserAction& action : one_time_report_user_actions_)
+ RecordUkmMetric(kUkmUserAction, static_cast<int64_t>(action));
+
+ // Bind |main_frame_url_| to |source_id_| directly before sending the content
+ // of |ukm_recorder_| to ensure that the binding has not been purged already.
+ if (ukm_recorder_)
+ ukm_recorder_->UpdateSourceURL(source_id_, main_frame_url_);
+}
+
+void PasswordFormMetricsRecorder::MarkGenerationAvailable() {
+ generation_available_ = true;
+}
+
+void PasswordFormMetricsRecorder::SetHasGeneratedPassword(
+ bool has_generated_password) {
+ has_generated_password_ = has_generated_password;
+}
+
+void PasswordFormMetricsRecorder::SetManagerAction(
+ ManagerAction manager_action) {
+ manager_action_ = manager_action;
+}
+
+void PasswordFormMetricsRecorder::SetUserAction(UserAction user_action) {
+ if (user_action == UserAction::kChoose) {
+ base::RecordAction(
+ base::UserMetricsAction("PasswordManager_UsedNonDefaultUsername"));
+ } else if (user_action == UserAction::kChoosePslMatch) {
+ base::RecordAction(
+ base::UserMetricsAction("PasswordManager_ChoseSubdomainPassword"));
+ } else if (user_action == UserAction::kOverridePassword) {
+ base::RecordAction(
+ base::UserMetricsAction("PasswordManager_LoggedInWithNewPassword"));
+ } else if (user_action == UserAction::kOverrideUsernameAndPassword) {
+ base::RecordAction(
+ base::UserMetricsAction("PasswordManager_LoggedInWithNewUsername"));
+ } else {
+ NOTREACHED();
+ }
+ user_action_ = user_action;
+}
+
+void PasswordFormMetricsRecorder::LogSubmitPassed() {
+ if (submit_result_ != kSubmitResultFailed) {
+ if (has_generated_password_) {
+ metrics_util::LogPasswordGenerationSubmissionEvent(
+ metrics_util::PASSWORD_SUBMITTED);
+ } else if (generation_available_) {
+ metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
+ metrics_util::PASSWORD_SUBMITTED);
+ }
+ }
+ base::RecordAction(base::UserMetricsAction("PasswordManager_LoginPassed"));
+ RecordUkmMetric(kUkmSubmissionObserved, 1 /*true*/);
+ RecordUkmMetric(kUkmSubmissionResult, kSubmitResultPassed);
+ submit_result_ = kSubmitResultPassed;
+}
+
+void PasswordFormMetricsRecorder::LogSubmitFailed() {
+ if (has_generated_password_) {
+ metrics_util::LogPasswordGenerationSubmissionEvent(
+ metrics_util::GENERATED_PASSWORD_FORCE_SAVED);
+ } else if (generation_available_) {
+ metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
+ metrics_util::PASSWORD_SUBMISSION_FAILED);
+ }
+ base::RecordAction(base::UserMetricsAction("PasswordManager_LoginFailed"));
+ RecordUkmMetric(kUkmSubmissionObserved, 1 /*true*/);
+ RecordUkmMetric(kUkmSubmissionResult, kSubmitResultFailed);
+ submit_result_ = kSubmitResultFailed;
+}
+
+void PasswordFormMetricsRecorder::SetSubmittedFormType(
+ SubmittedFormType form_type) {
+ submitted_form_type_ = form_type;
+}
+
+int PasswordFormMetricsRecorder::GetActionsTakenNew() const {
+ // Merge kManagerActionNone and kManagerActionBlacklisted_Obsolete. This
+ // lowers the number of histogram buckets used by 33%.
+ ManagerActionNew manager_action_new =
+ (manager_action_ == kManagerActionAutofilled)
+ ? kManagerActionNewAutofilled
+ : kManagerActionNewNone;
+
+ return static_cast<int>(user_action_) +
+ static_cast<int>(UserAction::kMax) *
+ (manager_action_new + kManagerActionNewMax * submit_result_);
+}
+
+void PasswordFormMetricsRecorder::RecordDetailedUserAction(
+ PasswordFormMetricsRecorder::DetailedUserAction action) {
+ // One-time actions are collected and reported during destruction of the
+ // PasswordFormMetricsRecorder.
+ if (!IsRepeatedUserAction(action)) {
+ one_time_report_user_actions_.insert(action);
+ return;
+ }
+ // Repeated actions can be reported immediately.
+ RecordUkmMetric(kUkmUserAction, static_cast<int64_t>(action));
+}
+
+int PasswordFormMetricsRecorder::GetActionsTaken() const {
+ return static_cast<int>(user_action_) +
+ static_cast<int>(UserAction::kMax) *
+ (manager_action_ + kManagerActionMax * submit_result_);
+}
+
+void PasswordFormMetricsRecorder::RecordHistogramsOnSuppressedAccounts(
+ bool observed_form_origin_has_cryptographic_scheme,
+ const FormFetcher& form_fetcher,
+ const PasswordForm& pending_credentials) {
+ UMA_HISTOGRAM_BOOLEAN("PasswordManager.QueryingSuppressedAccountsFinished",
+ form_fetcher.DidCompleteQueryingSuppressedForms());
+
+ if (!form_fetcher.DidCompleteQueryingSuppressedForms())
+ return;
+
+ SuppressedAccountExistence best_match = kSuppressedAccountNone;
+
+ if (!observed_form_origin_has_cryptographic_scheme) {
+ best_match = GetBestMatchingSuppressedAccount(
+ form_fetcher.GetSuppressedHTTPSForms(), PasswordForm::TYPE_GENERATED,
+ pending_credentials);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.SuppressedAccount.Generated.HTTPSNotHTTP",
+ GetHistogramSampleForSuppressedAccounts(best_match),
+ kMaxSuppressedAccountStats);
+ RecordUkmMetric("SuppressedAccount.Generated.HTTPSNotHTTP", best_match);
+
+ best_match = GetBestMatchingSuppressedAccount(
+ form_fetcher.GetSuppressedHTTPSForms(), PasswordForm::TYPE_MANUAL,
+ pending_credentials);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.SuppressedAccount.Manual.HTTPSNotHTTP",
+ GetHistogramSampleForSuppressedAccounts(best_match),
+ kMaxSuppressedAccountStats);
+ RecordUkmMetric("SuppressedAccount.Manual.HTTPSNotHTTP", best_match);
+ }
+
+ best_match = GetBestMatchingSuppressedAccount(
+ form_fetcher.GetSuppressedPSLMatchingForms(),
+ PasswordForm::TYPE_GENERATED, pending_credentials);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.SuppressedAccount.Generated.PSLMatching",
+ GetHistogramSampleForSuppressedAccounts(best_match),
+ kMaxSuppressedAccountStats);
+ RecordUkmMetric("SuppressedAccount.Generated.PSLMatching", best_match);
+ best_match = GetBestMatchingSuppressedAccount(
+ form_fetcher.GetSuppressedPSLMatchingForms(), PasswordForm::TYPE_MANUAL,
+ pending_credentials);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.SuppressedAccount.Manual.PSLMatching",
+ GetHistogramSampleForSuppressedAccounts(best_match),
+ kMaxSuppressedAccountStats);
+ RecordUkmMetric("SuppressedAccount.Manual.PSLMatching", best_match);
+
+ best_match = GetBestMatchingSuppressedAccount(
+ form_fetcher.GetSuppressedSameOrganizationNameForms(),
+ PasswordForm::TYPE_GENERATED, pending_credentials);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.SuppressedAccount.Generated.SameOrganizationName",
+ GetHistogramSampleForSuppressedAccounts(best_match),
+ kMaxSuppressedAccountStats);
+ RecordUkmMetric("SuppressedAccount.Generated.SameOrganizationName",
+ best_match);
+ best_match = GetBestMatchingSuppressedAccount(
+ form_fetcher.GetSuppressedSameOrganizationNameForms(),
+ PasswordForm::TYPE_MANUAL, pending_credentials);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.SuppressedAccount.Manual.SameOrganizationName",
+ GetHistogramSampleForSuppressedAccounts(best_match),
+ kMaxSuppressedAccountStats);
+ RecordUkmMetric("SuppressedAccount.Manual.SameOrganizationName", best_match);
+
+ if (current_bubble_ != CurrentBubbleOfInterest::kNone)
+ RecordUIDismissalReason(metrics_util::NO_DIRECT_INTERACTION);
+}
+
+void PasswordFormMetricsRecorder::RecordPasswordBubbleShown(
+ metrics_util::CredentialSourceType credential_source_type,
+ metrics_util::UIDisplayDisposition display_disposition) {
+ if (credential_source_type == metrics_util::CredentialSourceType::kUnknown)
+ return;
+ DCHECK_EQ(CurrentBubbleOfInterest::kNone, current_bubble_);
+ BubbleTrigger automatic_trigger_type =
+ credential_source_type ==
+ metrics_util::CredentialSourceType::kPasswordManager
+ ? BubbleTrigger::kPasswordManagerSuggestionAutomatic
+ : BubbleTrigger::kCredentialManagementAPIAutomatic;
+ BubbleTrigger manual_trigger_type =
+ credential_source_type ==
+ metrics_util::CredentialSourceType::kPasswordManager
+ ? BubbleTrigger::kPasswordManagerSuggestionManual
+ : BubbleTrigger::kCredentialManagementAPIManual;
+
+ switch (display_disposition) {
+ // New credential cases:
+ case metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING:
+ current_bubble_ = CurrentBubbleOfInterest::kSaveBubble;
+ save_prompt_shown_ = true;
+ RecordUkmMetric(kUkmSavingPromptTrigger,
+ static_cast<int64_t>(automatic_trigger_type));
+ break;
+ case metrics_util::MANUAL_WITH_PASSWORD_PENDING:
+ current_bubble_ = CurrentBubbleOfInterest::kSaveBubble;
+ save_prompt_shown_ = true;
+ RecordUkmMetric(kUkmSavingPromptTrigger,
+ static_cast<int64_t>(manual_trigger_type));
+ break;
+
+ // Update cases:
+ case metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING_UPDATE:
+ current_bubble_ = CurrentBubbleOfInterest::kUpdateBubble;
+ update_prompt_shown_ = true;
+ RecordUkmMetric(kUkmUpdatingPromptTrigger,
+ static_cast<int64_t>(automatic_trigger_type));
+ break;
+ case metrics_util::MANUAL_WITH_PASSWORD_PENDING_UPDATE:
+ current_bubble_ = CurrentBubbleOfInterest::kUpdateBubble;
+ update_prompt_shown_ = true;
+ RecordUkmMetric(kUkmUpdatingPromptTrigger,
+ static_cast<int64_t>(manual_trigger_type));
+ break;
+
+ // Other reasons to show a bubble:
+ case metrics_util::MANUAL_MANAGE_PASSWORDS:
+ case metrics_util::AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION:
+ case metrics_util::AUTOMATIC_SIGNIN_TOAST:
+ // Do nothing.
+ return;
+
+ // Obsolte display dispositions:
+ case metrics_util::MANUAL_BLACKLISTED_OBSOLETE:
+ case metrics_util::AUTOMATIC_CREDENTIAL_REQUEST_OBSOLETE:
+ case metrics_util::NUM_DISPLAY_DISPOSITIONS:
+ NOTREACHED();
+ return;
+ }
+}
+
+void PasswordFormMetricsRecorder::RecordUIDismissalReason(
+ metrics_util::UIDismissalReason ui_dismissal_reason) {
+ if (current_bubble_ != CurrentBubbleOfInterest::kUpdateBubble &&
+ current_bubble_ != CurrentBubbleOfInterest::kSaveBubble)
+ return;
+ const char* metric = current_bubble_ == CurrentBubbleOfInterest::kUpdateBubble
+ ? kUkmUpdatingPromptInteraction
+ : kUkmSavingPromptInteraction;
+ switch (ui_dismissal_reason) {
+ // Accepted by user.
+ case metrics_util::CLICKED_SAVE:
+ RecordUkmMetric(metric,
+ static_cast<int64_t>(BubbleDismissalReason::kAccepted));
+ break;
+
+ // Declined by user.
+ case metrics_util::CLICKED_CANCEL:
+ case metrics_util::CLICKED_NEVER:
+ RecordUkmMetric(metric,
+ static_cast<int64_t>(BubbleDismissalReason::kDeclined));
+ break;
+
+ // Ignored by user.
+ case metrics_util::NO_DIRECT_INTERACTION:
+ RecordUkmMetric(metric,
+ static_cast<int64_t>(BubbleDismissalReason::kIgnored));
+ break;
+
+ // Ignore these for metrics collection:
+ case metrics_util::CLICKED_MANAGE:
+ case metrics_util::CLICKED_DONE:
+ case metrics_util::CLICKED_OK:
+ case metrics_util::CLICKED_BRAND_NAME:
+ case metrics_util::CLICKED_PASSWORDS_DASHBOARD:
+ case metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT:
+ break;
+
+ // These should not reach here:
+ case metrics_util::CLICKED_UNBLACKLIST_OBSOLETE:
+ case metrics_util::CLICKED_CREDENTIAL_OBSOLETE:
+ case metrics_util::AUTO_SIGNIN_TOAST_CLICKED_OBSOLETE:
+ case metrics_util::NUM_UI_RESPONSES:
+ NOTREACHED();
+ break;
+ }
+
+ current_bubble_ = CurrentBubbleOfInterest::kNone;
+}
+
+void PasswordFormMetricsRecorder::RecordFillEvent(ManagerAutofillEvent event) {
+ RecordUkmMetric(kUkmManagerFillEvent, event);
+}
+
+PasswordFormMetricsRecorder::SuppressedAccountExistence
+PasswordFormMetricsRecorder::GetBestMatchingSuppressedAccount(
+ const std::vector<const PasswordForm*>& suppressed_forms,
+ PasswordForm::Type manual_or_generated,
+ const PasswordForm& pending_credentials) const {
+ SuppressedAccountExistence best_matching_account = kSuppressedAccountNone;
+ for (const PasswordForm* form : suppressed_forms) {
+ if (form->type != manual_or_generated)
+ continue;
+
+ SuppressedAccountExistence current_account;
+ if (pending_credentials.password_value.empty())
+ current_account = kSuppressedAccountExists;
+ else if (form->username_value != pending_credentials.username_value)
+ current_account = kSuppressedAccountExistsDifferentUsername;
+ else if (form->password_value != pending_credentials.password_value)
+ current_account = kSuppressedAccountExistsSameUsername;
+ else
+ current_account = kSuppressedAccountExistsSameUsernameAndPassword;
+
+ best_matching_account = std::max(best_matching_account, current_account);
+ }
+ return best_matching_account;
+}
+
+int PasswordFormMetricsRecorder::GetHistogramSampleForSuppressedAccounts(
+ SuppressedAccountExistence best_matching_account) const {
+ // Encoding: most significant digit is the |best_matching_account|.
+ int mixed_base_encoding = 0;
+ mixed_base_encoding += best_matching_account;
+ mixed_base_encoding *= PasswordFormMetricsRecorder::kMaxNumActionsTakenNew;
+ mixed_base_encoding += GetActionsTakenNew();
+ DCHECK_LT(mixed_base_encoding, kMaxSuppressedAccountStats);
+ return mixed_base_encoding;
+}
+
+void PasswordFormMetricsRecorder::RecordUkmMetric(const char* metric_name,
+ int64_t value) {
+ if (ukm_entry_builder_)
+ ukm_entry_builder_->AddMetric(metric_name, value);
+}
+
+// static
+bool PasswordFormMetricsRecorder::IsRepeatedUserAction(
+ DetailedUserAction action) {
+ switch (action) {
+ case DetailedUserAction::kUnknown:
+ return true;
+ case DetailedUserAction::kEditedUsernameInBubble:
+ return false;
+ }
+ NOTREACHED();
+ return true;
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..9d5203b6208
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h
@@ -0,0 +1,395 @@
+// 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_PASSWORD_FORM_METRICS_RECORDER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_METRICS_RECORDER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/password_form_user_action.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "url/gurl.h"
+
+namespace password_manager {
+
+// URL Keyed Metrics.
+
+// This metric records whether a submission of a password form has been
+// observed. The values 0 and 1 correspond to false and true respectively.
+extern const char kUkmSubmissionObserved[];
+
+// This metric records the outcome of a password form submission. The values are
+// numbered according to PasswordFormMetricsRecorder::SubmitResult.
+// Note that no metric is recorded for kSubmitResultNotSubmitted.
+extern const char kUkmSubmissionResult[];
+
+// This metric records the classification of a form at submission time. The
+// values correspond to PasswordFormMetricsRecorder::SubmittedFormType.
+// Note that no metric is recorded for kSubmittedFormTypeUnspecified.
+extern const char kUkmSubmissionFormType[];
+
+// This metric records the boolean value indicating whether a password update
+// prompt was shown, which asked the user for permission to update a password.
+extern const char kUkmUpdatingPromptShown[];
+
+// This metric records the reason why a password update prompt was shown to ask
+// the user for permission to update a password. The values correspond to
+// PasswordFormMetricsRecorder::BubbleTrigger.
+extern const char kUkmUpdatingPromptTrigger[];
+
+// This metric records how a user interacted with an updating prompt. The values
+// correspond to PasswordFormMetricsRecorder::BubbleDismissalReason.
+extern const char kUkmUpdatingPromptInteraction[];
+
+// This metric records the boolean value indicating whether a password save
+// prompt was shown, which asked the user for permission to save a new
+// credential.
+extern const char kUkmSavingPromptShown[];
+
+// This metric records the reason why a password save prompt was shown to ask
+// the user for permission to save a new credential. The values correspond to
+// PasswordFormMetricsRecorder::BubbleTrigger.
+extern const char kUkmSavingPromptTrigger[];
+
+// This metric records how a user interacted with a saving prompt. The values
+// correspond to PasswordFormMetricsRecorder::BubbleDismissalReason.
+extern const char kUkmSavingPromptInteraction[];
+
+// This metric records attempts to fill a password form. Values correspond to
+// PasswordFormMetricsRecorder::ManagerFillEvent.
+extern const char kUkmManagerFillEvent[];
+
+// This metric records what the user does with a form. Values correspond to the
+// enum UserAction.
+extern const char kUkmUserActionSimplified[];
+
+// This metric records what the user does with all UI entry points of the
+// password manager, like bubbles, context menus, forms, form fields, etc.
+// in relation to a given form. Values correspond to the enum
+// DetailedUserAction. In contrast to kUkmUserActionSimplified, ths metric is
+// intended to be extensible with new user action types.
+extern const char kUkmUserAction[];
+
+class FormFetcher;
+
+// The pupose of this class is to record various types of metrics about the
+// behavior of the PasswordFormManager and its interaction with the user and
+// the page. The recorder tracks events tied to the logical life of a password
+// form, from parsing to having been saved. These events happen on different
+// places in the code and the logical password form can be captured by multiple
+// instances of real objects. To allow sharing the single recorder object among
+// those, this class is refcounted. Reporting happens on destruction of the
+// metrics recorder. Note that UKM metrics are reported for intervals of length
+// metrics::GetUploadInterval(). Only metrics that are reported from the time
+// of creating the PasswordFormMetricsRecorder until the end of current upload
+// interval are recorded. Everything after the end of the current upload
+// interval is discarded. For this reason, it is essential that references are
+// not just kept until browser shutdown.
+class PasswordFormMetricsRecorder
+ : public base::RefCounted<PasswordFormMetricsRecorder> {
+ public:
+ // Records UKM metrics and reports them on destruction. The |source_id| is
+ // (re-)bound to |main_frame_url| shortly before reporting. As such it is
+ // crucial that the |source_id| is never bound to a different URL by another
+ // consumer. The reason for this late binding is that metrics can be
+ // collected for a WebContents for a long period of time and by the time the
+ // reporting happens, the binding of |source_id| to |main_frame_url| is
+ // already purged. |ukm_recorder| may be a nullptr, in which case no UKM
+ // metrics are recorded.
+ PasswordFormMetricsRecorder(bool is_main_frame_secure,
+ ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
+ const GURL& main_frame_url);
+
+ // ManagerAction - What does the PasswordFormManager do with this form? Either
+ // it fills it, or it doesn't. If it doesn't fill it, that's either
+ // because it has no match or it is disabled via the AUTOCOMPLETE=off
+ // attribute. Note that if we don't have an exact match, we still provide
+ // candidates that the user may end up choosing.
+ enum ManagerAction {
+ kManagerActionNone = 0,
+ kManagerActionAutofilled,
+ kManagerActionBlacklisted_Obsolete,
+ kManagerActionMax
+ };
+
+ // Same as above without the obsoleted 'Blacklisted' action.
+ enum ManagerActionNew {
+ kManagerActionNewNone = 0,
+ kManagerActionNewAutofilled,
+ kManagerActionNewMax
+ };
+
+ // Result - What happens to the form?
+ enum SubmitResult {
+ kSubmitResultNotSubmitted = 0,
+ kSubmitResultFailed,
+ kSubmitResultPassed,
+ kSubmitResultMax
+ };
+
+ // Whether the password manager filled a credential on a form.
+ enum ManagerAutofillEvent {
+ // No credential existed that could be filled into a password form.
+ kManagerFillEventNoCredential = 0,
+ // A credential could have been autofilled into a password form but was not
+ // due to a policy. E.g. incognito mode requires a user interaction before
+ // filling can happen. PSL matches are not autofilled and also on password
+ // change forms we do not autofill.
+ kManagerFillEventBlockedOnInteraction,
+ // A credential was autofilled into a form.
+ kManagerFillEventAutofilled
+ };
+
+ // Enumerates whether there were `suppressed` credentials. These are stored
+ // credentials that were not filled, even though they might be related to the
+ // observed form. See FormFetcher::GetSuppressed* for details.
+ //
+ // If suppressed credentials exist, it is also recorded whether their username
+ // and/or password matched those submitted.
+ enum SuppressedAccountExistence {
+ kSuppressedAccountNone,
+ // Recorded when there exists a suppressed account, but there was no
+ // submitted form to compare its username and password to.
+ kSuppressedAccountExists,
+ // Recorded when there was a submitted form.
+ kSuppressedAccountExistsDifferentUsername,
+ kSuppressedAccountExistsSameUsername,
+ kSuppressedAccountExistsSameUsernameAndPassword,
+ kSuppressedAccountExistenceMax,
+ };
+
+ // What the form is used for. kSubmittedFormTypeUnspecified is only set before
+ // the SetSubmittedFormType() is called, and should never be actually
+ // uploaded.
+ enum SubmittedFormType {
+ kSubmittedFormTypeLogin,
+ kSubmittedFormTypeLoginNoUsername,
+ kSubmittedFormTypeChangePasswordEnabled,
+ kSubmittedFormTypeChangePasswordDisabled,
+ kSubmittedFormTypeChangePasswordNoUsername,
+ kSubmittedFormTypeSignup,
+ kSubmittedFormTypeSignupNoUsername,
+ kSubmittedFormTypeLoginAndSignup,
+ kSubmittedFormTypeUnspecified,
+ kSubmittedFormTypeMax
+ };
+
+ // The reason why a password bubble was shown on the screen.
+ enum class BubbleTrigger {
+ kUnknown = 0,
+ // The password manager suggests the user to save a password and asks for
+ // confirmation.
+ kPasswordManagerSuggestionAutomatic,
+ kPasswordManagerSuggestionManual,
+ // The site asked the user to save a password via the credential management
+ // API.
+ kCredentialManagementAPIAutomatic,
+ kCredentialManagementAPIManual,
+ kMax,
+ };
+
+ // The reason why a password bubble was dismissed.
+ enum class BubbleDismissalReason {
+ kUnknown = 0,
+ kAccepted = 1,
+ kDeclined = 2,
+ kIgnored = 3
+ };
+
+ // This enum is a designed to be able to collect all kinds of potentially
+ // interesting user interactions with sites and password manager UI in
+ // relation to a given form. In contrast to UserAction, it is intended to be
+ // extensible.
+ enum class DetailedUserAction {
+ kUnknown = 0,
+
+ // Interactions with password bubble.
+ kEditedUsernameInBubble = 100,
+ };
+
+ // 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.
+ static constexpr int kMaxNumActionsTaken =
+ kManagerActionMax * static_cast<int>(UserAction::kMax) * kSubmitResultMax;
+
+ // Same as above but for ManagerActionNew instead of ManagerAction.
+ static constexpr int kMaxNumActionsTakenNew =
+ kManagerActionNewMax * static_cast<int>(UserAction::kMax) *
+ kSubmitResultMax;
+
+ // The maximum number of combinations recorded into histograms in the
+ // PasswordManager.SuppressedAccount.* family.
+ static constexpr int kMaxSuppressedAccountStats =
+ kSuppressedAccountExistenceMax *
+ PasswordFormMetricsRecorder::kManagerActionNewMax *
+ static_cast<int>(UserAction::kMax) *
+ PasswordFormMetricsRecorder::kSubmitResultMax;
+
+ // Called if the user could generate a password for this form.
+ void MarkGenerationAvailable();
+
+ // Stores whether the form has had its password auto generated by the browser.
+ void SetHasGeneratedPassword(bool has_generated_password);
+
+ // Stores the password manager and user actions and logs them.
+ void SetManagerAction(ManagerAction manager_action);
+ void SetUserAction(UserAction user_action);
+
+ // Call these if/when we know the form submission worked or failed.
+ // These routines are used to update internal statistics ("ActionsTaken").
+ void LogSubmitPassed();
+ void LogSubmitFailed();
+
+ // Call this once the submitted form type has been determined.
+ void SetSubmittedFormType(SubmittedFormType form_type);
+
+ // Records all histograms in the PasswordManager.SuppressedAccount.* family.
+ // Takes the FormFetcher intance which owns the login data from PasswordStore.
+ // |pending_credentials| stores credentials when the form was submitted but
+ // success was still unknown. It contains credentials that are ready to be
+ // written (saved or updated) to a password store.
+ void RecordHistogramsOnSuppressedAccounts(
+ bool observed_form_origin_has_cryptographic_scheme,
+ const FormFetcher& form_fetcher,
+ const autofill::PasswordForm& pending_credentials);
+
+ // Records the event that a password bubble was shown.
+ void RecordPasswordBubbleShown(
+ metrics_util::CredentialSourceType credential_source_type,
+ metrics_util::UIDisplayDisposition display_disposition);
+
+ // Records the dismissal of a password bubble.
+ void RecordUIDismissalReason(
+ metrics_util::UIDismissalReason ui_dismissal_reason);
+
+ // Records that the password manager managed or failed to fill a form.
+ void RecordFillEvent(ManagerAutofillEvent event);
+
+ // Converts the "ActionsTaken" fields (using ManagerActionNew) into an int so
+ // they can be logged to UMA.
+ // Public for testing.
+ int GetActionsTakenNew() const;
+
+ // Records a DetailedUserAction UKM metric.
+ void RecordDetailedUserAction(DetailedUserAction action);
+
+ private:
+ friend class base::RefCounted<PasswordFormMetricsRecorder>;
+
+ // Enum to track which password bubble is currently being displayed.
+ enum class CurrentBubbleOfInterest {
+ // This covers the cases that no password bubble is currently being
+ // displayed or the one displayed is none of the interesting cases.
+ kNone,
+ // The user is currently seeing a password save bubble.
+ kSaveBubble,
+ // The user is currently seeing a password update bubble.
+ kUpdateBubble,
+ };
+
+ // Destructor reports a couple of UMA metrics as well as calls
+ // RecordUkmMetric.
+ ~PasswordFormMetricsRecorder();
+
+ // Converts the "ActionsTaken" fields into an int so they can be logged to
+ // UMA.
+ int GetActionsTaken() const;
+
+ // When supplied with the list of all |suppressed_forms| that belong to
+ // certain suppressed credential type (see FormFetcher::GetSuppressed*),
+ // filters that list down to forms whose type matches |manual_or_generated|,
+ // and selects the suppressed account that matches |pending_credentials| most
+ // closely. |pending_credentials| stores credentials when the form was
+ // submitted but success was still unknown. It contains credentials that are
+ // ready to be written (saved or updated) to a password store.
+ SuppressedAccountExistence GetBestMatchingSuppressedAccount(
+ const std::vector<const autofill::PasswordForm*>& suppressed_forms,
+ autofill::PasswordForm::Type manual_or_generated,
+ const autofill::PasswordForm& pending_credentials) const;
+
+ // Encodes a UMA histogram sample for |best_matching_account| and
+ // GetActionsTakenNew(). This is a mixed-based representation of a combination
+ // of four attributes:
+ // -- whether there were suppressed credentials (and if so, their relation to
+ // the submitted username/password).
+ // -- whether the |observed_form_| got ultimately submitted
+ // -- what action the password manager performed (|manager_action_|),
+ // -- and what action the user performed (|user_action_|_).
+ int GetHistogramSampleForSuppressedAccounts(
+ SuppressedAccountExistence best_matching_account) const;
+
+ // Records a metric into |ukm_entry_builder_| if it is not nullptr.
+ void RecordUkmMetric(const char* metric_name, int64_t value);
+
+ // Returns true if an |action| should be recorded multiple times per life-cyle
+ // of a PasswordFormMetricsRecorder.
+ static bool IsRepeatedUserAction(DetailedUserAction action);
+
+ // True if the main frame's visible URL, at the time this PasswordFormManager
+ // was created, is secure.
+ const bool is_main_frame_secure_;
+
+ // Whether the user can choose to generate a password for this form.
+ bool generation_available_ = false;
+
+ // Whether this form has an auto generated password.
+ bool has_generated_password_ = false;
+
+ // Tracks which bubble is currently being displayed to the user.
+ CurrentBubbleOfInterest current_bubble_ = CurrentBubbleOfInterest::kNone;
+
+ // Whether the user was shown a prompt to update a password.
+ bool update_prompt_shown_ = false;
+
+ // Whether the user was shown a prompt to save a new credential.
+ bool save_prompt_shown_ = false;
+
+ // 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.
+ ManagerAction manager_action_ = kManagerActionNone;
+ UserAction user_action_ = UserAction::kNone;
+ SubmitResult submit_result_ = kSubmitResultNotSubmitted;
+
+ // Form type of the form that the PasswordFormManager is managing. Set after
+ // submission as the classification of the form can change depending on what
+ // data the user has entered.
+ SubmittedFormType submitted_form_type_ = kSubmittedFormTypeUnspecified;
+
+ // Recorder to which metrics are sent. Has to outlive this
+ // PasswordFormMetricsRecorder.
+ ukm::UkmRecorder* ukm_recorder_;
+
+ // A SourceId of |ukm_recorder_|. This id gets bound to |main_frame_url_| on
+ // destruction. It can be shared across multiple metrics recorders as long as
+ // they all bind it to the same URL.
+ ukm::SourceId source_id_;
+
+ // URL for which UKMs are reported.
+ GURL main_frame_url_;
+
+ // Records URL keyed metrics (UKMs) and submits them on its destruction. May
+ // be a nullptr in which case no recording is expected.
+ std::unique_ptr<ukm::UkmEntryBuilder> ukm_entry_builder_;
+
+ // Set of observed user actions that are only recorded once for the lifetime
+ // of a PasswordFormMetricsRecorder.
+ std::set<DetailedUserAction> one_time_report_user_actions_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordFormMetricsRecorder);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_METRICS_RECORDER_H_
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
new file mode 100644
index 00000000000..b75b0ebe1f1
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
@@ -0,0 +1,586 @@
+// 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/password_form_metrics_recorder.h"
+
+#include "base/logging.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/test/histogram_tester.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"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+constexpr char kTestUrl[] = "https://www.example.com/";
+
+// Create a UkmEntryBuilder with a SourceId that is initialized for kTestUrl.
+scoped_refptr<PasswordFormMetricsRecorder> CreatePasswordFormMetricsRecorder(
+ bool is_main_frame_secure,
+ ukm::TestUkmRecorder* test_ukm_recorder) {
+ return base::MakeRefCounted<PasswordFormMetricsRecorder>(
+ is_main_frame_secure, test_ukm_recorder,
+ test_ukm_recorder->GetNewSourceID(), GURL(kTestUrl));
+}
+
+// TODO(crbug.com/738921) Replace this with generalized infrastructure.
+// Verifies that the metric |metric_name| was recorded with value |value| in the
+// single entry of |test_ukm_recorder_| exactly |expected_count| times.
+void ExpectUkmValueCount(ukm::TestUkmRecorder* test_ukm_recorder,
+ const char* metric_name,
+ int64_t value,
+ int64_t expected_count) {
+ const ukm::UkmSource* source = test_ukm_recorder->GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+
+ ASSERT_EQ(1U, test_ukm_recorder->entries_count());
+ const ukm::mojom::UkmEntry* entry = test_ukm_recorder->GetEntry(0);
+
+ int64_t occurrences = 0;
+ for (const ukm::mojom::UkmMetricPtr& metric : entry->metrics) {
+ if (metric->metric_hash == base::HashMetricName(metric_name) &&
+ metric->value == value)
+ ++occurrences;
+ }
+ EXPECT_EQ(expected_count, occurrences) << metric_name << ": " << value;
+}
+
+} // namespace
+
+// Test the metrics recorded around password generation and the user's
+// interaction with the offer to generate passwords.
+TEST(PasswordFormMetricsRecorder, Generation) {
+ static constexpr struct {
+ bool generation_available;
+ bool has_generated_password;
+ PasswordFormMetricsRecorder::SubmitResult submission;
+ } kTests[] = {
+ {false, false, PasswordFormMetricsRecorder::kSubmitResultNotSubmitted},
+ {true, false, PasswordFormMetricsRecorder::kSubmitResultNotSubmitted},
+ {true, true, PasswordFormMetricsRecorder::kSubmitResultNotSubmitted},
+ {false, false, PasswordFormMetricsRecorder::kSubmitResultFailed},
+ {true, false, PasswordFormMetricsRecorder::kSubmitResultFailed},
+ {true, true, PasswordFormMetricsRecorder::kSubmitResultFailed},
+ {false, false, PasswordFormMetricsRecorder::kSubmitResultPassed},
+ {true, false, PasswordFormMetricsRecorder::kSubmitResultPassed},
+ {true, true, PasswordFormMetricsRecorder::kSubmitResultPassed},
+ };
+
+ for (const auto& test : kTests) {
+ SCOPED_TRACE(testing::Message()
+ << "generation_available=" << test.generation_available
+ << ", has_generated_password=" << test.has_generated_password
+ << ", submission=" << test.submission);
+
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ base::HistogramTester histogram_tester;
+ base::UserActionTester user_action_tester;
+
+ // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
+ // on destruction.
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ /*is_main_frame_secure*/ true, &test_ukm_recorder);
+ if (test.generation_available)
+ recorder->MarkGenerationAvailable();
+ recorder->SetHasGeneratedPassword(test.has_generated_password);
+ switch (test.submission) {
+ case PasswordFormMetricsRecorder::kSubmitResultNotSubmitted:
+ // Do nothing.
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultFailed:
+ recorder->LogSubmitFailed();
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultPassed:
+ recorder->LogSubmitPassed();
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultMax:
+ NOTREACHED();
+ }
+ }
+
+ ExpectUkmValueCount(
+ &test_ukm_recorder, kUkmSubmissionObserved,
+ test.submission !=
+ PasswordFormMetricsRecorder::kSubmitResultNotSubmitted
+ ? 1
+ : 0,
+ 1);
+
+ int expected_login_failed =
+ test.submission == PasswordFormMetricsRecorder::kSubmitResultFailed ? 1
+ : 0;
+ EXPECT_EQ(expected_login_failed,
+ user_action_tester.GetActionCount("PasswordManager_LoginFailed"));
+ ExpectUkmValueCount(&test_ukm_recorder, kUkmSubmissionResult,
+ PasswordFormMetricsRecorder::kSubmitResultFailed,
+ expected_login_failed);
+
+ int expected_login_passed =
+ test.submission == PasswordFormMetricsRecorder::kSubmitResultPassed ? 1
+ : 0;
+ EXPECT_EQ(expected_login_passed,
+ user_action_tester.GetActionCount("PasswordManager_LoginPassed"));
+ ExpectUkmValueCount(&test_ukm_recorder, kUkmSubmissionResult,
+ PasswordFormMetricsRecorder::kSubmitResultPassed,
+ expected_login_passed);
+
+ if (test.has_generated_password) {
+ switch (test.submission) {
+ case PasswordFormMetricsRecorder::kSubmitResultNotSubmitted:
+ histogram_tester.ExpectBucketCount(
+ "PasswordGeneration.SubmissionEvent",
+ metrics_util::PASSWORD_NOT_SUBMITTED, 1);
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultFailed:
+ histogram_tester.ExpectBucketCount(
+ "PasswordGeneration.SubmissionEvent",
+ metrics_util::GENERATED_PASSWORD_FORCE_SAVED, 1);
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultPassed:
+ histogram_tester.ExpectBucketCount(
+ "PasswordGeneration.SubmissionEvent",
+ metrics_util::PASSWORD_SUBMITTED, 1);
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultMax:
+ NOTREACHED();
+ }
+ }
+
+ if (!test.has_generated_password && test.generation_available) {
+ switch (test.submission) {
+ case PasswordFormMetricsRecorder::kSubmitResultNotSubmitted:
+ histogram_tester.ExpectBucketCount(
+ "PasswordGeneration.SubmissionAvailableEvent",
+ metrics_util::PASSWORD_NOT_SUBMITTED, 1);
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultFailed:
+ histogram_tester.ExpectBucketCount(
+ "PasswordGeneration.SubmissionAvailableEvent",
+ metrics_util::PASSWORD_SUBMISSION_FAILED, 1);
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultPassed:
+ histogram_tester.ExpectBucketCount(
+ "PasswordGeneration.SubmissionAvailableEvent",
+ metrics_util::PASSWORD_SUBMITTED, 1);
+ break;
+ case PasswordFormMetricsRecorder::kSubmitResultMax:
+ NOTREACHED();
+ }
+ }
+ }
+}
+
+// Test the recording of metrics around manager_action, user_action, and
+// submit_result.
+TEST(PasswordFormMetricsRecorder, Actions) {
+ static constexpr struct {
+ // Stimuli:
+ bool is_main_frame_secure;
+ PasswordFormMetricsRecorder::ManagerAction manager_action;
+ UserAction user_action;
+ PasswordFormMetricsRecorder::SubmitResult submit_result;
+ // Expectations:
+ // Histogram bucket for PasswordManager.ActionsTakenV3 and
+ // PasswordManager.ActionsTakenOnNonSecureForm.
+ int32_t actions_taken;
+ // Result of GetActionsTakenNew.
+ int32_t actions_taken_new;
+ } kTests[] = {
+ // Test values of manager_action.
+ {true, PasswordFormMetricsRecorder::kManagerActionNone /*0*/,
+ UserAction::kNone /*0*/,
+ PasswordFormMetricsRecorder::kSubmitResultNotSubmitted /*0*/, 0, 0},
+ {true, PasswordFormMetricsRecorder::kManagerActionAutofilled /*1*/,
+ UserAction::kNone /*0*/,
+ PasswordFormMetricsRecorder::kSubmitResultNotSubmitted /*0*/, 5, 5},
+ // Test values of user_action.
+ {true, PasswordFormMetricsRecorder::kManagerActionNone /*0*/,
+ UserAction::kChoose /*1*/,
+ PasswordFormMetricsRecorder::kSubmitResultNotSubmitted /*0*/, 1, 1},
+ {true, PasswordFormMetricsRecorder::kManagerActionNone /*0*/,
+ UserAction::kChoosePslMatch /*2*/,
+ PasswordFormMetricsRecorder::kSubmitResultNotSubmitted /*0*/, 2, 2},
+ {true, PasswordFormMetricsRecorder::kManagerActionNone /*0*/,
+ UserAction::kOverridePassword /*3*/,
+ PasswordFormMetricsRecorder::kSubmitResultNotSubmitted /*0*/, 3, 3},
+ {true, PasswordFormMetricsRecorder::kManagerActionNone /*0*/,
+ UserAction::kOverrideUsernameAndPassword /*4*/,
+ PasswordFormMetricsRecorder::kSubmitResultNotSubmitted /*0*/, 4, 4},
+ // Test values of submit_result.
+ {true, PasswordFormMetricsRecorder::kManagerActionNone /*0*/,
+ UserAction::kNone /*0*/,
+ PasswordFormMetricsRecorder::kSubmitResultFailed /*1*/, 15, 10},
+ {true, PasswordFormMetricsRecorder::kManagerActionNone /*0*/,
+ UserAction::kNone /*0*/,
+ PasswordFormMetricsRecorder::kSubmitResultPassed /*2*/, 30, 20},
+ // Test combination.
+ {true, PasswordFormMetricsRecorder::kManagerActionAutofilled /*1*/,
+ UserAction::kOverrideUsernameAndPassword /*4*/,
+ PasswordFormMetricsRecorder::kSubmitResultFailed /*2*/, 24, 19},
+ // Test non-secure form.
+ {false, PasswordFormMetricsRecorder::kManagerActionAutofilled /*1*/,
+ UserAction::kOverrideUsernameAndPassword /*4*/,
+ PasswordFormMetricsRecorder::kSubmitResultFailed /*2*/, 24, 19},
+ };
+
+ for (const auto& test : kTests) {
+ SCOPED_TRACE(testing::Message()
+ << "is_main_frame_secure=" << test.is_main_frame_secure
+ << ", manager_action=" << test.manager_action
+ << ", user_action=" << static_cast<int>(test.user_action)
+ << ", submit_result=" << test.submit_result);
+
+ base::HistogramTester histogram_tester;
+ base::UserActionTester user_action_tester;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+
+ // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
+ // on destruction.
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ test.is_main_frame_secure, &test_ukm_recorder);
+
+ recorder->SetManagerAction(test.manager_action);
+ if (test.user_action != UserAction::kNone)
+ recorder->SetUserAction(test.user_action);
+ if (test.submit_result ==
+ PasswordFormMetricsRecorder::kSubmitResultFailed) {
+ recorder->LogSubmitFailed();
+ } else if (test.submit_result ==
+ PasswordFormMetricsRecorder::kSubmitResultPassed) {
+ recorder->LogSubmitPassed();
+ }
+
+ EXPECT_EQ(test.actions_taken_new, recorder->GetActionsTakenNew());
+ }
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("PasswordManager.ActionsTakenV3"),
+ ::testing::ElementsAre(base::Bucket(test.actions_taken, 1)));
+ if (!test.is_main_frame_secure) {
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "PasswordManager.ActionsTakenOnNonSecureForm"),
+ ::testing::ElementsAre(base::Bucket(test.actions_taken, 1)));
+ }
+
+ switch (test.user_action) {
+ case UserAction::kNone:
+ break;
+ case UserAction::kChoose:
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "PasswordManager_UsedNonDefaultUsername"));
+ break;
+ case UserAction::kChoosePslMatch:
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "PasswordManager_ChoseSubdomainPassword"));
+ break;
+ case UserAction::kOverridePassword:
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "PasswordManager_LoggedInWithNewPassword"));
+ break;
+ case UserAction::kOverrideUsernameAndPassword:
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "PasswordManager_LoggedInWithNewUsername"));
+ break;
+ case UserAction::kMax:
+ break;
+ }
+
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ test_ukm_recorder.ExpectMetric(*source, "PasswordForm",
+ kUkmUserActionSimplified,
+ static_cast<int64_t>(test.user_action));
+ }
+}
+
+// Test that in the case of a sequence of user actions, only the last one is
+// recorded in ActionsV3 but all are recorded as UMA user actions.
+TEST(PasswordFormMetricsRecorder, ActionSequence) {
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ base::HistogramTester histogram_tester;
+ base::UserActionTester user_action_tester;
+
+ // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
+ // on destruction.
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ true /*is_main_frame_secure*/, &test_ukm_recorder);
+ recorder->SetManagerAction(
+ PasswordFormMetricsRecorder::kManagerActionAutofilled);
+ recorder->SetUserAction(UserAction::kChoosePslMatch);
+ recorder->SetUserAction(UserAction::kOverrideUsernameAndPassword);
+ recorder->LogSubmitPassed();
+ }
+
+ EXPECT_THAT(histogram_tester.GetAllSamples("PasswordManager.ActionsTakenV3"),
+ ::testing::ElementsAre(base::Bucket(39, 1)));
+
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "PasswordManager_ChoseSubdomainPassword"));
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "PasswordManager_LoggedInWithNewUsername"));
+}
+
+TEST(PasswordFormMetricsRecorder, SubmittedFormType) {
+ static constexpr struct {
+ // Stimuli:
+ bool is_main_frame_secure;
+ PasswordFormMetricsRecorder::SubmittedFormType form_type;
+ // Expectations:
+ // Expectation for PasswordManager.SubmittedFormType:
+ int expected_submitted_form_type;
+ // Expectation for PasswordManager.SubmittedNonSecureFormType:
+ int expected_submitted_non_secure_form_type;
+ } kTests[] = {
+ {false, PasswordFormMetricsRecorder::kSubmittedFormTypeUnspecified, 0, 0},
+ {true, PasswordFormMetricsRecorder::kSubmittedFormTypeUnspecified, 0, 0},
+ {false, PasswordFormMetricsRecorder::kSubmittedFormTypeLogin, 1, 1},
+ {true, PasswordFormMetricsRecorder::kSubmittedFormTypeLogin, 1, 0},
+ };
+ for (const auto& test : kTests) {
+ SCOPED_TRACE(testing::Message()
+ << "is_main_frame_secure=" << test.is_main_frame_secure
+ << ", form_type=" << test.form_type);
+
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ base::HistogramTester histogram_tester;
+
+ // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
+ // on destruction.
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ test.is_main_frame_secure, &test_ukm_recorder);
+ recorder->SetSubmittedFormType(test.form_type);
+ }
+
+ if (test.form_type !=
+ PasswordFormMetricsRecorder::kSubmittedFormTypeUnspecified) {
+ ExpectUkmValueCount(&test_ukm_recorder, kUkmSubmissionFormType,
+ test.form_type, 1);
+ }
+
+ if (test.expected_submitted_form_type) {
+ histogram_tester.ExpectBucketCount("PasswordManager.SubmittedFormType",
+ test.form_type,
+ test.expected_submitted_form_type);
+ } else {
+ histogram_tester.ExpectTotalCount("PasswordManager.SubmittedFormType", 0);
+ }
+
+ if (test.expected_submitted_non_secure_form_type) {
+ histogram_tester.ExpectBucketCount(
+ "PasswordManager.SubmittedNonSecureFormType", test.form_type,
+ test.expected_submitted_non_secure_form_type);
+ } else {
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.SubmittedNonSecureFormType", 0);
+ }
+ }
+}
+
+TEST(PasswordFormMetricsRecorder, RecordPasswordBubbleShown) {
+ using Trigger = PasswordFormMetricsRecorder::BubbleTrigger;
+ static constexpr struct {
+ // Stimuli:
+ metrics_util::CredentialSourceType credential_source_type;
+ metrics_util::UIDisplayDisposition display_disposition;
+ // Expectations:
+ const char* expected_trigger_metric;
+ Trigger expected_trigger_value;
+ bool expected_save_prompt_shown;
+ bool expected_update_prompt_shown;
+ } kTests[] = {
+ // Source = PasswordManager, Saving.
+ {metrics_util::CredentialSourceType::kPasswordManager,
+ metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING, kUkmSavingPromptTrigger,
+ Trigger::kPasswordManagerSuggestionAutomatic, true, false},
+ {metrics_util::CredentialSourceType::kPasswordManager,
+ metrics_util::MANUAL_WITH_PASSWORD_PENDING, kUkmSavingPromptTrigger,
+ Trigger::kPasswordManagerSuggestionManual, true, false},
+ // Source = PasswordManager, Updating.
+ {metrics_util::CredentialSourceType::kPasswordManager,
+ metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING_UPDATE,
+ kUkmUpdatingPromptTrigger, Trigger::kPasswordManagerSuggestionAutomatic,
+ false, true},
+ {metrics_util::CredentialSourceType::kPasswordManager,
+ metrics_util::MANUAL_WITH_PASSWORD_PENDING_UPDATE,
+ kUkmUpdatingPromptTrigger, Trigger::kPasswordManagerSuggestionManual,
+ false, true},
+ // Source = Credential Management API, Saving.
+ {metrics_util::CredentialSourceType::kCredentialManagementAPI,
+ metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING, kUkmSavingPromptTrigger,
+ Trigger::kCredentialManagementAPIAutomatic, true, false},
+ {metrics_util::CredentialSourceType::kCredentialManagementAPI,
+ metrics_util::MANUAL_WITH_PASSWORD_PENDING, kUkmSavingPromptTrigger,
+ Trigger::kCredentialManagementAPIManual, true, false},
+ // Source = Credential Management API, Updating.
+ {metrics_util::CredentialSourceType::kCredentialManagementAPI,
+ metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING_UPDATE,
+ kUkmUpdatingPromptTrigger, Trigger::kCredentialManagementAPIAutomatic,
+ false, true},
+ {metrics_util::CredentialSourceType::kCredentialManagementAPI,
+ metrics_util::MANUAL_WITH_PASSWORD_PENDING_UPDATE,
+ kUkmUpdatingPromptTrigger, Trigger::kCredentialManagementAPIManual,
+ false, true},
+ // Source = Unknown, Saving.
+ {metrics_util::CredentialSourceType::kUnknown,
+ metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING, kUkmSavingPromptTrigger,
+ Trigger::kPasswordManagerSuggestionAutomatic, false, false},
+ };
+
+ for (const auto& test : kTests) {
+ SCOPED_TRACE(testing::Message()
+ << "credential_source_type = "
+ << static_cast<int64_t>(test.credential_source_type)
+ << ", display_disposition = " << test.display_disposition);
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ true /*is_main_frame_secure*/, &test_ukm_recorder);
+ recorder->RecordPasswordBubbleShown(test.credential_source_type,
+ test.display_disposition);
+ }
+ // Verify data
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ if (test.credential_source_type !=
+ metrics_util::CredentialSourceType::kUnknown) {
+ test_ukm_recorder.ExpectMetric(
+ *source, "PasswordForm", test.expected_trigger_metric,
+ static_cast<int64_t>(test.expected_trigger_value));
+ } else {
+ EXPECT_FALSE(test_ukm_recorder.HasMetric(*source, "PasswordForm",
+ kUkmSavingPromptTrigger));
+ EXPECT_FALSE(test_ukm_recorder.HasMetric(*source, "PasswordForm",
+ kUkmUpdatingPromptTrigger));
+ }
+ test_ukm_recorder.ExpectMetric(*source, "PasswordForm",
+ kUkmSavingPromptShown,
+ test.expected_save_prompt_shown);
+ test_ukm_recorder.ExpectMetric(*source, "PasswordForm",
+ kUkmUpdatingPromptShown,
+ test.expected_update_prompt_shown);
+ }
+}
+
+TEST(PasswordFormMetricsRecorder, RecordUIDismissalReason) {
+ static constexpr struct {
+ // Stimuli:
+ metrics_util::UIDisplayDisposition display_disposition;
+ metrics_util::UIDismissalReason dismissal_reason;
+ // Expectations:
+ const char* expected_trigger_metric;
+ PasswordFormMetricsRecorder::BubbleDismissalReason expected_metric_value;
+ } kTests[] = {
+ {metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING,
+ metrics_util::CLICKED_SAVE, kUkmSavingPromptInteraction,
+ PasswordFormMetricsRecorder::BubbleDismissalReason::kAccepted},
+ {metrics_util::MANUAL_WITH_PASSWORD_PENDING, metrics_util::CLICKED_CANCEL,
+ kUkmSavingPromptInteraction,
+ PasswordFormMetricsRecorder::BubbleDismissalReason::kDeclined},
+ {metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING_UPDATE,
+ metrics_util::CLICKED_NEVER, kUkmUpdatingPromptInteraction,
+ PasswordFormMetricsRecorder::BubbleDismissalReason::kDeclined},
+ {metrics_util::MANUAL_WITH_PASSWORD_PENDING_UPDATE,
+ metrics_util::NO_DIRECT_INTERACTION, kUkmUpdatingPromptInteraction,
+ PasswordFormMetricsRecorder::BubbleDismissalReason::kIgnored},
+ };
+
+ for (const auto& test : kTests) {
+ SCOPED_TRACE(testing::Message()
+ << "display_disposition = " << test.display_disposition
+ << ", dismissal_reason = " << test.dismissal_reason);
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ true /*is_main_frame_secure*/, &test_ukm_recorder);
+ recorder->RecordPasswordBubbleShown(
+ metrics_util::CredentialSourceType::kPasswordManager,
+ test.display_disposition);
+ recorder->RecordUIDismissalReason(test.dismissal_reason);
+ }
+ // Verify data
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ test_ukm_recorder.ExpectMetric(
+ *source, "PasswordForm", test.expected_trigger_metric,
+ static_cast<int64_t>(test.expected_metric_value));
+ }
+}
+
+// Verify that it is ok to open and close the password bubble more than once
+// and still get accurate metrics.
+TEST(PasswordFormMetricsRecorder, SequencesOfBubbles) {
+ using BubbleDismissalReason =
+ PasswordFormMetricsRecorder::BubbleDismissalReason;
+ using BubbleTrigger = PasswordFormMetricsRecorder::BubbleTrigger;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ true /*is_main_frame_secure*/, &test_ukm_recorder);
+ // Open and confirm an automatically triggered saving prompt.
+ recorder->RecordPasswordBubbleShown(
+ metrics_util::CredentialSourceType::kPasswordManager,
+ metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING);
+ recorder->RecordUIDismissalReason(metrics_util::CLICKED_SAVE);
+ // Open and confirm a manually triggered update prompt.
+ recorder->RecordPasswordBubbleShown(
+ metrics_util::CredentialSourceType::kPasswordManager,
+ metrics_util::MANUAL_WITH_PASSWORD_PENDING_UPDATE);
+ recorder->RecordUIDismissalReason(metrics_util::CLICKED_SAVE);
+ }
+ // Verify recorded UKM data.
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ test_ukm_recorder.ExpectMetric(
+ *source, "PasswordForm", kUkmSavingPromptInteraction,
+ static_cast<int64_t>(BubbleDismissalReason::kAccepted));
+ test_ukm_recorder.ExpectMetric(
+ *source, "PasswordForm", kUkmUpdatingPromptInteraction,
+ static_cast<int64_t>(BubbleDismissalReason::kAccepted));
+ test_ukm_recorder.ExpectMetric(*source, "PasswordForm",
+ kUkmUpdatingPromptShown, 1);
+ test_ukm_recorder.ExpectMetric(*source, "PasswordForm", kUkmSavingPromptShown,
+ 1);
+ test_ukm_recorder.ExpectMetric(
+ *source, "PasswordForm", kUkmSavingPromptTrigger,
+ static_cast<int64_t>(BubbleTrigger::kPasswordManagerSuggestionAutomatic));
+ test_ukm_recorder.ExpectMetric(
+ *source, "PasswordForm", kUkmUpdatingPromptTrigger,
+ static_cast<int64_t>(BubbleTrigger::kPasswordManagerSuggestionManual));
+}
+
+// Verify that one-time actions are only recorded once per life-cycle of a
+// PasswordFormMetricsRecorder.
+TEST(PasswordFormMetricsRecorder, RecordDetailedUserAction) {
+ using DetailedUserAction = PasswordFormMetricsRecorder::DetailedUserAction;
+ const DetailedUserAction kOneTimeAction =
+ DetailedUserAction::kEditedUsernameInBubble;
+ const DetailedUserAction kRepeatedAction = DetailedUserAction::kUnknown;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ auto recorder = CreatePasswordFormMetricsRecorder(
+ true /*is_main_frame_secure*/, &test_ukm_recorder);
+ recorder->RecordDetailedUserAction(kOneTimeAction);
+ recorder->RecordDetailedUserAction(kOneTimeAction);
+ recorder->RecordDetailedUserAction(kRepeatedAction);
+ recorder->RecordDetailedUserAction(kRepeatedAction);
+ }
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ test_ukm_recorder.ExpectMetrics(*source, "PasswordForm", kUkmUserAction,
+ {static_cast<int64_t>(kOneTimeAction),
+ static_cast<int64_t>(kRepeatedAction),
+ static_cast<int64_t>(kRepeatedAction)});
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_form_user_action.h b/chromium/components/password_manager/core/browser/password_form_user_action.h
new file mode 100644
index 00000000000..0665dabfa71
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_form_user_action.h
@@ -0,0 +1,30 @@
+// 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_PASSWORD_FORM_USER_ACTION_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_USER_ACTION_H_
+
+namespace password_manager {
+
+// UserAction - What does the user do with a form?
+enum class UserAction {
+ // User did nothing, either by accepting what the password manager did, or by
+ // simply not typing anything at all.
+ kNone = 0,
+ // There were multiple choices and the user selected a different one than the
+ // default.
+ kChoose,
+ // The user selected an entry that originated from matching the form against
+ // the Public Suffix List.
+ kChoosePslMatch,
+ // User typed a new value just for the password.
+ kOverridePassword,
+ // User typed a new value for the username and the password.
+ kOverrideUsernameAndPassword,
+ kMax
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_USER_ACTION_H_
diff --git a/chromium/components/password_manager/core/browser/password_manager.cc b/chromium/components/password_manager/core/browser/password_manager.cc
index 804aa5d4348..2c1f81b2557 100644
--- a/chromium/components/password_manager/core/browser/password_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_manager.cc
@@ -19,7 +19,7 @@
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/password_form_field_prediction_map.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/form_saver_impl.h"
#include "components/password_manager/core/browser/keychain_migration_status_mac.h"
@@ -28,6 +28,7 @@
#include "components/password_manager/core/browser/password_form_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_metrics_recorder.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/common/password_manager_features.h"
@@ -141,13 +142,6 @@ bool AreAllFieldsEmpty(const PasswordForm& form) {
// static
void PasswordManager::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
-#if defined(OS_IOS) || defined(OS_ANDROID)
- uint32_t flags = PrefRegistry::NO_REGISTRATION_FLAGS;
-#else
- uint32_t flags = user_prefs::PrefRegistrySyncable::SYNCABLE_PREF;
-#endif
- registry->RegisterBooleanPref(prefs::kPasswordManagerSavingEnabled, true,
- flags);
registry->RegisterBooleanPref(
prefs::kCredentialsEnableService, true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
@@ -157,6 +151,9 @@ void PasswordManager::RegisterProfilePrefs(
registry->RegisterBooleanPref(prefs::kWasObsoleteHttpDataCleaned, false);
registry->RegisterStringPref(prefs::kSyncPasswordHash, std::string(),
PrefRegistry::NO_REGISTRATION_FLAGS);
+ registry->RegisterStringPref(prefs::kSyncPasswordLengthAndHashSalt,
+ std::string(),
+ PrefRegistry::NO_REGISTRATION_FLAGS);
#if defined(OS_MACOSX)
registry->RegisterIntegerPref(
prefs::kKeychainMigrationStatus,
@@ -197,27 +194,23 @@ void PasswordManager::OnPresaveGeneratedPassword(
DCHECK(client_->IsSavingAndFillingEnabledForCurrentPage());
PasswordFormManager* form_manager = GetMatchingPendingManager(form);
if (form_manager) {
- form_manager->form_saver()->PresaveGeneratedPassword(form);
+ form_manager->PresaveGeneratedPassword(form);
+ UMA_HISTOGRAM_BOOLEAN("PasswordManager.GeneratedFormHasNoFormManager",
+ false);
return;
}
+
+ UMA_HISTOGRAM_BOOLEAN("PasswordManager.GeneratedFormHasNoFormManager", true);
}
-void PasswordManager::SetHasGeneratedPasswordForForm(
- password_manager::PasswordManagerDriver* driver,
- const PasswordForm& form,
- bool password_is_generated) {
+void PasswordManager::OnPasswordNoLongerGenerated(const PasswordForm& form) {
DCHECK(client_->IsSavingAndFillingEnabledForCurrentPage());
PasswordFormManager* form_manager = GetMatchingPendingManager(form);
if (form_manager) {
- if (!password_is_generated)
- form_manager->form_saver()->RemovePresavedPassword();
- form_manager->set_has_generated_password(password_is_generated);
+ form_manager->PasswordNoLongerGenerated();
return;
}
-
- UMA_HISTOGRAM_BOOLEAN("PasswordManager.GeneratedFormHasNoFormManager",
- password_is_generated);
}
void PasswordManager::SetGenerationElementAndReasonForForm(
@@ -242,6 +235,7 @@ void PasswordManager::SetGenerationElementAndReasonForForm(
this, client_, driver->AsWeakPtr(), form,
base::WrapUnique(new FormSaverImpl(client_->GetPasswordStore())),
nullptr);
+ manager->Init(nullptr);
pending_login_managers_.push_back(std::move(manager));
}
@@ -271,13 +265,17 @@ void PasswordManager::ProvisionallySavePassword(
}
if (!is_saving_and_filling_enabled) {
- RecordFailure(SAVING_DISABLED, form.origin, logger.get());
+ client_->GetMetricsRecorder().RecordProvisionalSaveFailure(
+ PasswordManagerMetricsRecorder::SAVING_DISABLED, main_frame_url_,
+ form.origin, logger.get());
return;
}
// No password to save? Then don't.
if (PasswordFormManager::PasswordToSave(form).empty()) {
- RecordFailure(EMPTY_PASSWORD, form.origin, logger.get());
+ client_->GetMetricsRecorder().RecordProvisionalSaveFailure(
+ PasswordManagerMetricsRecorder::EMPTY_PASSWORD, main_frame_url_,
+ form.origin, logger.get());
return;
}
@@ -285,10 +283,9 @@ void PasswordManager::ProvisionallySavePassword(
metrics_util::LogShouldBlockPasswordForSameOriginButDifferentScheme(
should_block);
if (should_block) {
- if (logger)
- logger->LogSuccessiveOrigins(
- Logger::STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME,
- main_frame_url_.GetOrigin(), form.origin.GetOrigin());
+ client_->GetMetricsRecorder().RecordProvisionalSaveFailure(
+ PasswordManagerMetricsRecorder::SAVING_ON_HTTP_AFTER_HTTPS,
+ main_frame_url_, form.origin, logger.get());
return;
}
@@ -345,15 +342,13 @@ void PasswordManager::ProvisionallySavePassword(
// first loading the page containing the form. Don't offer to save
// passwords in this case.
if (matched_manager_it == pending_login_managers_.end()) {
- RecordFailure(NO_MATCHING_FORM, form.origin, logger.get());
+ client_->GetMetricsRecorder().RecordProvisionalSaveFailure(
+ PasswordManagerMetricsRecorder::NO_MATCHING_FORM, main_frame_url_,
+ form.origin, logger.get());
return;
}
- std::unique_ptr<PasswordFormManager> manager;
- // Transfer ownership of the manager from |pending_login_managers_| to
- // |manager|.
- manager.swap(*matched_manager_it);
- pending_login_managers_.erase(matched_manager_it);
+ std::unique_ptr<PasswordFormManager> manager = (*matched_manager_it)->Clone();
PasswordForm submitted_form(form);
submitted_form.preferred = true;
@@ -418,43 +413,6 @@ bool PasswordManager::IsPasswordFieldDetectedOnPage() {
return !pending_login_managers_.empty();
}
-void PasswordManager::RecordFailure(ProvisionalSaveFailure failure,
- const GURL& form_origin,
- BrowserSavePasswordProgressLogger* logger) {
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.ProvisionalSaveFailure", failure, MAX_FAILURE_VALUE);
-
- if (logger) {
- switch (failure) {
- case SAVING_DISABLED:
- logger->LogMessage(Logger::STRING_SAVING_DISABLED);
- break;
- case EMPTY_PASSWORD:
- logger->LogMessage(Logger::STRING_EMPTY_PASSWORD);
- break;
- case MATCHING_NOT_COMPLETE:
- logger->LogMessage(Logger::STRING_MATCHING_NOT_COMPLETE);
- break;
- case NO_MATCHING_FORM:
- logger->LogMessage(Logger::STRING_NO_MATCHING_FORM);
- break;
- case FORM_BLACKLISTED:
- logger->LogMessage(Logger::STRING_FORM_BLACKLISTED);
- break;
- case INVALID_FORM:
- logger->LogMessage(Logger::STRING_INVALID_FORM);
- break;
- case SYNC_CREDENTIAL:
- logger->LogMessage(Logger::STRING_SYNC_CREDENTIAL);
- break;
- case MAX_FAILURE_VALUE:
- NOTREACHED();
- return;
- }
- logger->LogMessage(Logger::STRING_DECISION_DROP);
- }
-}
-
void PasswordManager::AddSubmissionCallback(
const PasswordSubmittedCallback& callback) {
submission_callbacks_.push_back(callback);
@@ -572,6 +530,7 @@ void PasswordManager::CreatePendingLoginManagers(
(driver ? driver->AsWeakPtr() : base::WeakPtr<PasswordManagerDriver>()),
*iter, base::WrapUnique(new FormSaverImpl(client_->GetPasswordStore())),
nullptr);
+ manager->Init(nullptr);
pending_login_managers_.push_back(std::move(manager));
}
@@ -600,9 +559,9 @@ bool PasswordManager::CanProvisionalManagerSave() {
FormFetcher::State::WAITING) {
// We have a provisional save manager, but it didn't finish matching yet.
// We just give up.
- RecordFailure(MATCHING_NOT_COMPLETE,
- provisional_save_manager_->observed_form().origin,
- logger.get());
+ client_->GetMetricsRecorder().RecordProvisionalSaveFailure(
+ PasswordManagerMetricsRecorder::MATCHING_NOT_COMPLETE, main_frame_url_,
+ provisional_save_manager_->observed_form().origin, logger.get());
provisional_save_manager_.reset();
return false;
}
@@ -750,10 +709,27 @@ void PasswordManager::OnLoginSuccessful() {
DCHECK(provisional_save_manager_->submitted_form());
if (!client_->GetStoreResultFilter()->ShouldSave(
*provisional_save_manager_->submitted_form())) {
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+ // 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.
+ if (!provisional_save_manager_->submitted_form()
+ ->username_value.empty()) {
+ password_manager::PasswordStore* store = client_->GetPasswordStore();
+ // May be null in tests.
+ if (store) {
+ metrics_util::LogSyncPasswordHashChange(
+ metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA);
+ store->SaveSyncPasswordHash(
+ provisional_save_manager_->submitted_form()->password_value);
+ }
+ }
+#endif
provisional_save_manager_->WipeStoreCopyIfOutdated();
- RecordFailure(SYNC_CREDENTIAL,
- provisional_save_manager_->observed_form().origin,
- logger.get());
+ client_->GetMetricsRecorder().RecordProvisionalSaveFailure(
+ PasswordManagerMetricsRecorder::SYNC_CREDENTIAL, main_frame_url_,
+ provisional_save_manager_->observed_form().origin, logger.get());
provisional_save_manager_.reset();
return;
}
diff --git a/chromium/components/password_manager/core/browser/password_manager.h b/chromium/components/password_manager/core/browser/password_manager.h
index b85165d98ed..68281080eb8 100644
--- a/chromium/components/password_manager/core/browser/password_manager.h
+++ b/chromium/components/password_manager/core/browser/password_manager.h
@@ -33,7 +33,6 @@ class FormStructure;
namespace password_manager {
-class BrowserSavePasswordProgressLogger;
class PasswordManagerClient;
class PasswordManagerDriver;
class PasswordFormManager;
@@ -101,14 +100,10 @@ class PasswordManager : public LoginModel {
void GenerationAvailableForForm(const autofill::PasswordForm& form);
// Presaves the form with generated password.
- void OnPresaveGeneratedPassword(const autofill::PasswordForm& password_form);
+ void OnPresaveGeneratedPassword(const autofill::PasswordForm& form);
- // Update the state of generation for this form.
- // If |password_is_generated| == false, removes the presaved form.
- void SetHasGeneratedPasswordForForm(
- password_manager::PasswordManagerDriver* driver,
- const autofill::PasswordForm& form,
- bool password_is_generated);
+ // Stops treating a password as generated.
+ void OnPasswordNoLongerGenerated(const autofill::PasswordForm& form);
// Update the generation element and whether generation was triggered
// manually.
@@ -195,25 +190,6 @@ class PasswordManager : public LoginModel {
PasswordManagerTest,
ShouldBlockPasswordForSameOriginButDifferentSchemeTest);
- enum ProvisionalSaveFailure {
- SAVING_DISABLED,
- EMPTY_PASSWORD,
- NO_MATCHING_FORM,
- MATCHING_NOT_COMPLETE,
- FORM_BLACKLISTED,
- INVALID_FORM,
- SYNC_CREDENTIAL,
- MAX_FAILURE_VALUE
- };
-
- // Log failure for UMA. Logs additional metrics if the |form_origin|
- // corresponds to one of the top, explicitly monitored websites. For some
- // values of |failure| also sends logs to the internals page through |logger|,
- // it |logger| is not NULL.
- void RecordFailure(ProvisionalSaveFailure failure,
- const GURL& form_origin,
- BrowserSavePasswordProgressLogger* logger);
-
// Returns true if we can show possible usernames to users in cases where
// the username for the form is ambigious.
bool OtherPossibleUsernamesEnabled() const;
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 7004aee79ab..58bc5ed1cfb 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client.h
+++ b/chromium/components/password_manager/core/browser/password_manager_client.h
@@ -12,6 +12,7 @@
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/credentials_filter.h"
#include "components/password_manager/core/browser/password_store.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
class PrefService;
@@ -32,6 +33,7 @@ namespace password_manager {
class LogManager;
class PasswordFormManager;
class PasswordManager;
+class PasswordManagerMetricsRecorder;
class PasswordStore;
enum PasswordSyncState {
@@ -211,9 +213,24 @@ class PasswordManagerClient {
// Checks the safe browsing reputation of the webpage where password reuse
// happens.
virtual void CheckProtectedPasswordEntry(
- const std::string& password_saved_domain) = 0;
+ const std::string& password_saved_domain,
+ bool password_field_exists) = 0;
#endif
+ // Gets the UKM service associated with this client (for metrics).
+ virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
+
+ // Gets a ukm::SourceId that is associated with the WebContents object
+ // and its last committed main frame navigation. Note that the URL binding
+ // has to happen by the caller at a later point.
+ virtual ukm::SourceId GetUkmSourceId() = 0;
+
+ // Gets a metrics recorder for the currently committed navigation.
+ // As PasswordManagerMetricsRecorder submits metrics on destruction, a new
+ // instance will be returned for each committed navigation. A caller must not
+ // hold on to the pointer.
+ virtual PasswordManagerMetricsRecorder& GetMetricsRecorder() = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(PasswordManagerClient);
};
diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc
new file mode 100644
index 00000000000..14d2026fd06
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.cc
@@ -0,0 +1,105 @@
+// 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/password_manager_metrics_recorder.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "components/autofill/core/common/save_password_progress_logger.h"
+#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
+#include "url/gurl.h"
+
+// Shorten the name to spare line breaks. The code provides enough context
+// already.
+typedef autofill::SavePasswordProgressLogger Logger;
+
+namespace password_manager {
+
+// URL Keyed Metrics.
+const char kUkmUserModifiedPasswordField[] = "UserModifiedPasswordField";
+const char kUkmProvisionalSaveFailure[] = "ProvisionalSaveFailure";
+
+PasswordManagerMetricsRecorder::PasswordManagerMetricsRecorder(
+ ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
+ const GURL& main_frame_url)
+ : ukm_recorder_(ukm_recorder),
+ source_id_(source_id),
+ main_frame_url_(main_frame_url),
+ ukm_entry_builder_(
+ ukm_recorder
+ ? ukm_recorder->GetEntryBuilder(source_id, "PageWithPassword")
+ : nullptr) {}
+
+PasswordManagerMetricsRecorder::PasswordManagerMetricsRecorder(
+ PasswordManagerMetricsRecorder&& that) noexcept = default;
+
+PasswordManagerMetricsRecorder::~PasswordManagerMetricsRecorder() {
+ if (user_modified_password_field_)
+ RecordUkmMetric(kUkmUserModifiedPasswordField, 1);
+
+ // Bind |main_frame_url_| to |source_id_| directly before sending the content
+ // of |ukm_recorder_| to ensure that the binding has not been purged already.
+ if (ukm_recorder_)
+ ukm_recorder_->UpdateSourceURL(source_id_, main_frame_url_);
+}
+
+PasswordManagerMetricsRecorder& PasswordManagerMetricsRecorder::operator=(
+ PasswordManagerMetricsRecorder&& that) = default;
+
+void PasswordManagerMetricsRecorder::RecordUserModifiedPasswordField() {
+ user_modified_password_field_ = true;
+}
+
+void PasswordManagerMetricsRecorder::RecordProvisionalSaveFailure(
+ ProvisionalSaveFailure failure,
+ const GURL& main_frame_url,
+ const GURL& form_origin,
+ BrowserSavePasswordProgressLogger* logger) {
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.ProvisionalSaveFailure", failure,
+ MAX_FAILURE_VALUE);
+ RecordUkmMetric(kUkmProvisionalSaveFailure, static_cast<int64_t>(failure));
+
+ if (logger) {
+ switch (failure) {
+ case SAVING_DISABLED:
+ logger->LogMessage(Logger::STRING_SAVING_DISABLED);
+ break;
+ case EMPTY_PASSWORD:
+ logger->LogMessage(Logger::STRING_EMPTY_PASSWORD);
+ break;
+ case MATCHING_NOT_COMPLETE:
+ logger->LogMessage(Logger::STRING_MATCHING_NOT_COMPLETE);
+ break;
+ case NO_MATCHING_FORM:
+ logger->LogMessage(Logger::STRING_NO_MATCHING_FORM);
+ break;
+ case FORM_BLACKLISTED:
+ logger->LogMessage(Logger::STRING_FORM_BLACKLISTED);
+ break;
+ case INVALID_FORM:
+ logger->LogMessage(Logger::STRING_INVALID_FORM);
+ break;
+ case SYNC_CREDENTIAL:
+ logger->LogMessage(Logger::STRING_SYNC_CREDENTIAL);
+ break;
+ case SAVING_ON_HTTP_AFTER_HTTPS:
+ logger->LogSuccessiveOrigins(
+ Logger::STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME,
+ main_frame_url.GetOrigin(), form_origin.GetOrigin());
+ break;
+ case MAX_FAILURE_VALUE:
+ NOTREACHED();
+ return;
+ }
+ logger->LogMessage(Logger::STRING_DECISION_DROP);
+ }
+}
+
+void PasswordManagerMetricsRecorder::RecordUkmMetric(const char* metric_name,
+ int64_t value) {
+ if (ukm_entry_builder_)
+ ukm_entry_builder_->AddMetric(metric_name, value);
+}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..8b95daa8897
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h
@@ -0,0 +1,122 @@
+// 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_PASSWORD_MANAGER_METRICS_RECORDER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_METRICS_RECORDER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "url/gurl.h"
+
+class GURL;
+
+namespace password_manager {
+
+// URL Keyed Metrics.
+
+// Records a 1 for every page on which a user has modified the content of a
+// password field - regardless of how many password fields a page contains or
+// the user modifies.
+extern const char kUkmUserModifiedPasswordField[];
+
+// UKM that records a ProvisionalSaveFailure in case the password manager cannot
+// offer to save a credential.
+extern const char kUkmProvisionalSaveFailure[];
+
+class BrowserSavePasswordProgressLogger;
+
+// The pupose of this class is to record various types of metrics about the
+// behavior of the PasswordManager and its interaction with the user and the
+// page.
+// The PasswordManagerMetricsRecorder flushes metrics on destruction. As such
+// any owner needs to destroy this instance when navigations are committed.
+class PasswordManagerMetricsRecorder {
+ public:
+ // Reasons why the password manager failed to do a provisional saving and
+ // therefore did not offer the user to save a password.
+ enum ProvisionalSaveFailure {
+ // Password manager is disabled or user is in incognito mode.
+ SAVING_DISABLED,
+ // Submitted form contains an empty password.
+ EMPTY_PASSWORD,
+ // No PasswordFormManager exists for this form.
+ NO_MATCHING_FORM,
+ // FormFetcher of PasswordFormManager is still loading.
+ MATCHING_NOT_COMPLETE,
+ // Form is blacklisted for saving. Obsolete since M47.
+ FORM_BLACKLISTED,
+ // <unknown purpose>. Obsolete since M48.
+ INVALID_FORM,
+ // A Google credential cannot be saved by policy because it is the Chrome
+ // Sync credential and therefore acts as a master password that gives access
+ // to all other credentials on https://passwords.google.com.
+ SYNC_CREDENTIAL,
+ // Credentials are not offered to be saved on HTTP pages if a credential is
+ // stored for the corresponding HTTPS page.
+ SAVING_ON_HTTP_AFTER_HTTPS,
+ MAX_FAILURE_VALUE
+ };
+
+ // Records UKM metrics and reports them on destruction. The |source_id| is
+ // (re-)bound to |main_frame_url| shortly before reporting. As such it is
+ // crucial that the |source_id| is never bound to a different URL by another
+ // consumer. The reason for this late binding is that metrics can be
+ // collected for a WebContents for a long period of time and by the time the
+ // reporting happens, the binding of |source_id| to |main_frame_url| is
+ // already purged. |ukm_recorder| may be a nullptr, in which case no UKM
+ // metrics are recorded.
+ PasswordManagerMetricsRecorder(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
+ const GURL& main_frame_url);
+
+ PasswordManagerMetricsRecorder(
+ PasswordManagerMetricsRecorder&& that) noexcept;
+ ~PasswordManagerMetricsRecorder();
+
+ PasswordManagerMetricsRecorder& operator=(
+ PasswordManagerMetricsRecorder&& that);
+
+ // Records that the user has modified a password field on a page. This may be
+ // called multiple times but a single metric will be reported.
+ void RecordUserModifiedPasswordField();
+
+ // Log failure to provisionally save a password to in the PasswordManager to
+ // UMA and the |logger|.
+ void RecordProvisionalSaveFailure(ProvisionalSaveFailure failure,
+ const GURL& main_frame_url,
+ const GURL& form_origin,
+ BrowserSavePasswordProgressLogger* logger);
+
+ private:
+ // Records a metric into |ukm_entry_builder_| if it is not nullptr.
+ void RecordUkmMetric(const char* metric_name, int64_t value);
+
+ // Recorder to which metrics are sent. Has to outlive this
+ // PasswordManagerMetircsRecorder.
+ ukm::UkmRecorder* ukm_recorder_;
+
+ // A SourceId of |ukm_recorder_|. This id gets bound to |main_frame_url_| on
+ // destruction. It can be shared across multiple metrics recorders as long as
+ // they all bind it to the same URL.
+ ukm::SourceId source_id_;
+
+ // URL for which UKMs are reported.
+ GURL main_frame_url_;
+
+ // Records URL keyed metrics (UKMs) and submits them on its destruction. May
+ // be a nullptr in which case no recording is expected.
+ std::unique_ptr<ukm::UkmEntryBuilder> ukm_entry_builder_;
+
+ bool user_modified_password_field_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordManagerMetricsRecorder);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_METRICS_RECORDER_H_
diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc
new file mode 100644
index 00000000000..70a26a1e927
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/password_manager_metrics_recorder.h"
+
+#include <memory>
+
+#include "base/metrics/metrics_hashes.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "components/ukm/ukm_source.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Contains;
+using ::testing::Not;
+
+namespace password_manager {
+
+namespace {
+
+constexpr char kTestUrl[] = "https://www.example.com/";
+
+// Creates a PasswordManagerMetricsRecorder that reports metrics for kTestUrl.
+PasswordManagerMetricsRecorder CreateMetricsRecorder(
+ ukm::UkmRecorder* ukm_recorder) {
+ return PasswordManagerMetricsRecorder(
+ ukm_recorder, ukm_recorder->GetNewSourceID(), GURL(kTestUrl));
+}
+
+} // namespace
+
+TEST(PasswordManagerMetricsRecorder, UserModifiedPasswordField) {
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ PasswordManagerMetricsRecorder recorder(
+ CreateMetricsRecorder(&test_ukm_recorder));
+ recorder.RecordUserModifiedPasswordField();
+ }
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ test_ukm_recorder.ExpectMetric(*source, "PageWithPassword",
+ kUkmUserModifiedPasswordField, 1);
+}
+
+TEST(PasswordManagerMetricsRecorder, UserModifiedPasswordFieldMultipleTimes) {
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ PasswordManagerMetricsRecorder recorder(
+ CreateMetricsRecorder(&test_ukm_recorder));
+ // Multiple calls should not create more than one entry.
+ recorder.RecordUserModifiedPasswordField();
+ recorder.RecordUserModifiedPasswordField();
+ }
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ test_ukm_recorder.ExpectMetric(*source, "PageWithPassword",
+ kUkmUserModifiedPasswordField, 1);
+}
+
+TEST(PasswordManagerMetricsRecorder, UserModifiedPasswordFieldNotCalled) {
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ {
+ PasswordManagerMetricsRecorder recorder(
+ CreateMetricsRecorder(&test_ukm_recorder));
+ }
+ const ukm::UkmSource* source = test_ukm_recorder.GetSourceForUrl(kTestUrl);
+ ASSERT_TRUE(source);
+ EXPECT_THAT(test_ukm_recorder.GetMetrics(*source, "PageWithPassword",
+ kUkmUserModifiedPasswordField),
+ Not(Contains(1)));
+}
+
+} // namespace password_manager
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 a7bc2fe54a5..3dc8207fd38 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
@@ -199,6 +199,21 @@ void LogSubmittedFormFrame(SubmittedFormFrame frame) {
SubmittedFormFrame::SUBMITTED_FORM_FRAME_COUNT);
}
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+void LogSyncPasswordHashChange(SyncPasswordHashChange event) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.SyncPasswordHashChange", event,
+ SyncPasswordHashChange::SAVED_SYNC_PASSWORD_CHANGE_COUNT);
+}
+
+void LogIsSyncPasswordHashSaved(IsSyncPasswordHashSaved state) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.IsSyncPasswordHashSaved", state,
+ IsSyncPasswordHashSaved::IS_SYNC_PASSWORD_HASH_SAVED_COUNT);
+}
+#endif
+
} // namespace metrics_util
} // namespace password_manager
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 203be60f70d..5605db72859 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
@@ -193,6 +193,55 @@ enum class SubmittedFormFrame {
SUBMITTED_FORM_FRAME_COUNT
};
+// Metrics: "PasswordManager.AccessPasswordInSettings"
+enum AccessPasswordInSettingsEvent {
+ ACCESS_PASSWORD_VIEWED = 0,
+ ACCESS_PASSWORD_COPIED = 1,
+ ACCESS_PASSWORD_COUNT
+};
+
+// Metrics: PasswordManager.ReauthToAccessPasswordInSettings
+enum ReauthToAccessPasswordInSettingsEvent {
+ REAUTH_SUCCESS = 0,
+ REAUTH_FAILURE = 1,
+ REAUTH_SKIPPED = 2,
+ REAUTH_COUNT
+};
+
+// Metrics: PasswordManager.IE7LookupResult
+enum IE7LookupResultStatus {
+ IE7_RESULTS_ABSENT = 0,
+ IE7_RESULTS_PRESENT = 1,
+ IE7_RESULTS_COUNT
+};
+
+// Specifies the type of PasswordFormManagers and derived classes to distinguish
+// the context in which a PasswordFormManager is being created and used.
+enum class CredentialSourceType {
+ kUnknown,
+ // This is used for form based credential management (PasswordFormManager).
+ kPasswordManager,
+ // This is used for credential management API based credential management
+ // (CredentialManagerPasswordFormManager).
+ kCredentialManagementAPI
+};
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+enum class SyncPasswordHashChange {
+ SAVED_ON_CHROME_SIGNIN,
+ SAVED_IN_CONTENT_AREA,
+ CLEARED_ON_CHROME_SIGNOUT,
+ SAVED_SYNC_PASSWORD_CHANGE_COUNT
+};
+
+enum class IsSyncPasswordHashSaved {
+ NOT_SAVED,
+ SAVED,
+ IS_SYNC_PASSWORD_HASH_SAVED_COUNT
+};
+#endif
+
// 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);
@@ -282,6 +331,15 @@ void LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent(
// Log a frame of a submitted password form.
void LogSubmittedFormFrame(SubmittedFormFrame frame);
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+// Log a save sync password change event.
+void LogSyncPasswordHashChange(SyncPasswordHashChange event);
+
+// Log whether a sync password hash saved.
+void LogIsSyncPasswordHashSaved(IsSyncPasswordHashSaved state);
+#endif
+
} // namespace metrics_util
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.cc b/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.cc
deleted file mode 100644
index 28041664641..00000000000
--- a/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.cc
+++ /dev/null
@@ -1,20 +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/password_manager/core/browser/password_manager_settings_migration_experiment.h"
-
-#include "base/metrics/field_trial.h"
-#include "base/strings/string_util.h"
-
-namespace password_manager {
-
-bool IsSettingsMigrationActive() {
- const char kFieldTrialName[] = "PasswordManagerSettingsMigration";
- const char kEnabledGroupNamePrefix[] = "Enable";
- return base::StartsWith(base::FieldTrialList::FindFullName(kFieldTrialName),
- kEnabledGroupNamePrefix,
- base::CompareCase::INSENSITIVE_ASCII);
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.h b/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.h
deleted file mode 100644
index 0dcbd1cb49a..00000000000
--- a/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment.h
+++ /dev/null
@@ -1,15 +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_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTINGS_MIGRATOR_EXPERIMENT_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTINGS_MIGRATOR_EXPERIMENT_H_
-
-namespace password_manager {
-
-// Returns true if settings migration should be active for the user.
-bool IsSettingsMigrationActive();
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTINGS_MIGRATOR_EXPERIMENT_H_
diff --git a/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment_unittest.cc
deleted file mode 100644
index 81d8d42d337..00000000000
--- a/chromium/components/password_manager/core/browser/password_manager_settings_migration_experiment_unittest.cc
+++ /dev/null
@@ -1,51 +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/password_manager/core/browser/password_manager_settings_migration_experiment.h"
-
-#include "base/macros.h"
-#include "base/metrics/field_trial.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const char kPasswordManagerSettingMigrationFieldTrialName[] =
- "PasswordManagerSettingsMigration";
-const char kEnabledPasswordManagerSettingsMigrationGroupName[] = "Enable";
-const char kDisablePasswordManagerSettingsMigrationGroupName[] = "Disable";
-
-} // namespace
-
-namespace password_manager {
-
-class PasswordManagerSettingsMigrationExperimentTest : public testing::Test {
- public:
- PasswordManagerSettingsMigrationExperimentTest()
- : field_trial_list_(nullptr) {}
-
- void EnforcePasswordManagerSettingMigrationExperimentGroup(const char* name) {
- ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
- kPasswordManagerSettingMigrationFieldTrialName, name));
- }
-
- protected:
- base::FieldTrialList field_trial_list_;
-
- DISALLOW_COPY_AND_ASSIGN(PasswordManagerSettingsMigrationExperimentTest);
-};
-
-TEST_F(PasswordManagerSettingsMigrationExperimentTest, IsSettingsMigrationOn) {
- EnforcePasswordManagerSettingMigrationExperimentGroup(
- kEnabledPasswordManagerSettingsMigrationGroupName);
- EXPECT_TRUE(IsSettingsMigrationActive());
-}
-
-TEST_F(PasswordManagerSettingsMigrationExperimentTest, IsSettingsMigrationOff) {
- EnforcePasswordManagerSettingMigrationExperimentGroup(
- kDisablePasswordManagerSettingsMigrationGroupName);
- EXPECT_FALSE(IsSettingsMigrationActive());
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_test_utils.cc b/chromium/components/password_manager/core/browser/password_manager_test_utils.cc
index 080b607facb..8eb289b508e 100644
--- a/chromium/components/password_manager/core/browser/password_manager_test_utils.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_test_utils.cc
@@ -46,7 +46,6 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromDataForTesting(
if (form_data.username_value) {
form->username_value = base::WideToUTF16(form_data.username_value);
form->display_name = form->username_value;
- form->skip_zero_click = true;
if (form_data.password_value) {
if (wcscmp(form_data.password_value, kTestingFederatedLoginMarker) == 0)
form->federation_origin = url::Origin(GURL(kTestingFederationUrlSpec));
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 d0895e8c8b5..485458a8e53 100644
--- a/chromium/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_unittest.cc
@@ -14,6 +14,7 @@
#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 "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/mock_password_store.h"
@@ -115,6 +116,10 @@ ACTION_P(SaveToScopedPtr, scoped) { scoped->reset(arg0); }
} // namespace
class PasswordManagerTest : public testing::Test {
+ public:
+ PasswordManagerTest() : test_url_("https://www.example.com") {}
+ ~PasswordManagerTest() override = default;
+
protected:
void SetUp() override {
store_ = new testing::StrictMock<MockPasswordStore>;
@@ -139,8 +144,7 @@ class PasswordManagerTest : public testing::Test {
EXPECT_CALL(client_, DidLastPageLoadEncounterSSLErrors())
.WillRepeatedly(Return(false));
- ON_CALL(client_, GetMainFrameURL())
- .WillByDefault(ReturnRef(GURL::EmptyGURL()));
+ ON_CALL(client_, GetMainFrameURL()).WillByDefault(ReturnRef(test_url_));
}
void TearDown() override {
@@ -234,6 +238,7 @@ class PasswordManagerTest : public testing::Test {
void FormSubmitted(const PasswordForm& form) { submitted_form_ = form; }
+ const GURL test_url_;
base::MessageLoop message_loop_;
scoped_refptr<MockPasswordStore> store_;
testing::NiceMock<MockPasswordManagerClient> client_;
@@ -306,7 +311,8 @@ TEST_F(PasswordManagerTest, GeneratedPasswordFormSubmitEmptyStore) {
// Simulate the user generating the password and submitting the form.
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
+ EXPECT_CALL(*store_, AddLogin(_));
+ manager()->OnPresaveGeneratedPassword(form);
OnPasswordFormSubmitted(form);
// The user should not need to confirm saving as they have already given
@@ -315,7 +321,8 @@ TEST_F(PasswordManagerTest, GeneratedPasswordFormSubmitEmptyStore) {
// occured.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
PasswordForm form_to_save;
- EXPECT_CALL(*store_, AddLogin(_)).WillOnce(SaveArg<0>(&form_to_save));
+ EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _))
+ .WillOnce(SaveArg<0>(&form_to_save));
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator());
// Now the password manager waits for the navigation to complete.
@@ -573,6 +580,10 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotSaved) {
// User should not be prompted and password should not be saved.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+ EXPECT_CALL(*store_, SaveSyncPasswordHash(form.password_value));
+#endif
// Prefs are needed for failure logging about sync credentials.
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
@@ -598,11 +609,12 @@ TEST_F(PasswordManagerTest, ReportFormLoginSuccessAndShouldSaveCalled) {
// |observed_form| and not the |stored_form| what is passed to ShouldSave.
observed_form.username_element += ASCIIToUTF16("1");
observed.push_back(observed_form);
- EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
// Simulate that |form| is already in the store, making this an update.
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeConsumer(stored_form)));
+ EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsParsed(&driver_, observed);
+ EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// Submit form and finish navigation.
@@ -622,6 +634,9 @@ TEST_F(PasswordManagerTest, ReportFormLoginSuccessAndShouldSaveCalled) {
EXPECT_CALL(*store_, UpdateLogin(_));
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
+ // As the clone PasswordFormManager ends up saving the form, it triggers an
+ // update of the pending login managers, which in turn triggers new filling.
+ EXPECT_CALL(driver_, FillPasswordForm(_));
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
@@ -644,6 +659,10 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotDroppedIfUpToDate) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+ EXPECT_CALL(*store_, SaveSyncPasswordHash(form.password_value));
+#endif
manager()->ProvisionallySavePassword(form, nullptr);
// Chrome should not remove the sync credential, because it was successfully
@@ -677,6 +696,10 @@ TEST_F(PasswordManagerTest, SyncCredentialsDroppedWhenObsolete) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+ EXPECT_CALL(*store_, SaveSyncPasswordHash(ASCIIToUTF16("n3w passw0rd")));
+#endif
manager()->ProvisionallySavePassword(updated_form, nullptr);
client_.FilterAllResultsForSaving();
@@ -953,10 +976,13 @@ TEST_F(PasswordManagerTest, InPageNavigation) {
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
manager()->OnInPageNavigation(&driver_, form);
+ ASSERT_TRUE(form_manager_to_save);
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
- ASSERT_TRUE(form_manager_to_save);
+ // The Save() call triggers updating for |pending_login_managers_|, hence the
+ // further GetLogins call.
+ EXPECT_CALL(*store_, GetLogins(_, _));
form_manager_to_save->Save();
}
@@ -1174,24 +1200,26 @@ TEST_F(PasswordManagerTest, SaveFormFetchedAfterSubmit) {
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
- // No call-back from store after GetLogins is called emulates that
- // PasswordStore did not fetch a form in time before submission.
+ // GetLogins calls remain unanswered to emulate that PasswordStore did not
+ // fetch a form in time before submission.
EXPECT_CALL(*store_, GetLogins(_, _));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
ASSERT_EQ(1u, manager()->pending_login_managers().size());
- PasswordFormManager* form_manager =
- manager()->pending_login_managers().front().get();
+ PasswordStoreConsumer* store_consumer = nullptr;
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
+ // This second call is from the new FormFetcher, which is cloned during
+ // ProvisionalSavePasswords.
+ EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&store_consumer));
OnPasswordFormSubmitted(form);
// Emulate fetching password form from PasswordStore after submission but
// before post-navigation load.
- ASSERT_TRUE(form_manager);
- static_cast<FormFetcherImpl*>(form_manager->form_fetcher())
- ->OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>>());
+ ASSERT_TRUE(store_consumer);
+ store_consumer->OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<PasswordForm>>());
std::unique_ptr<PasswordFormManager> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
@@ -1219,7 +1247,8 @@ TEST_F(PasswordManagerTest, PasswordGeneration_FailedSubmission) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
+ EXPECT_CALL(*store_, AddLogin(_));
+ manager()->OnPresaveGeneratedPassword(form);
// Do not save generated password when the password form reappears.
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
@@ -1246,7 +1275,8 @@ TEST_F(PasswordManagerTest, PasswordGenerationPasswordEdited_FailedSubmission) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
+ EXPECT_CALL(*store_, AddLogin(_));
+ manager()->OnPresaveGeneratedPassword(form);
// Simulate user editing and submitting a different password. Verify that
// the edited password is the one that is saved.
@@ -1279,11 +1309,13 @@ TEST_F(PasswordManagerTest,
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
+ EXPECT_CALL(*store_, AddLogin(_));
+ manager()->OnPresaveGeneratedPassword(form);
// Simulate user removing generated password and adding a new one.
form.new_password_value = ASCIIToUTF16("different_password");
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, false);
+ EXPECT_CALL(*store_, RemoveLogin(_));
+ manager()->OnPasswordNoLongerGenerated(form);
OnPasswordFormSubmitted(form);
@@ -1311,11 +1343,13 @@ TEST_F(PasswordManagerTest,
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
+ EXPECT_CALL(*store_, AddLogin(_));
+ manager()->OnPresaveGeneratedPassword(form);
// Simulate user removing generated password and adding a new one.
form.new_password_value = ASCIIToUTF16("different_password");
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, false);
+ EXPECT_CALL(*store_, RemoveLogin(_));
+ manager()->OnPasswordNoLongerGenerated(form);
OnPasswordFormSubmitted(form);
@@ -1342,7 +1376,8 @@ TEST_F(PasswordManagerTest, PasswordGenerationUsernameChanged) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
+ EXPECT_CALL(*store_, AddLogin(_));
+ manager()->OnPresaveGeneratedPassword(form);
// Simulate user changing the password and username, without ever completely
// deleting the password.
@@ -1352,7 +1387,8 @@ TEST_F(PasswordManagerTest, PasswordGenerationUsernameChanged) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
PasswordForm form_to_save;
- EXPECT_CALL(*store_, AddLogin(_)).WillOnce(SaveArg<0>(&form_to_save));
+ EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, _))
+ .WillOnce(SaveArg<0>(&form_to_save));
EXPECT_CALL(client_, AutomaticPasswordSaveIndicator());
observed.clear();
@@ -1375,11 +1411,12 @@ TEST_F(PasswordManagerTest, PasswordGenerationPresavePassword) {
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
+ base::HistogramTester histogram_tester;
+
// The user accepts a generated password.
form.password_value = base::ASCIIToUTF16("password");
EXPECT_CALL(*store_, AddLogin(form)).WillOnce(Return());
manager()->OnPresaveGeneratedPassword(form);
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
// The user updates the generated password.
PasswordForm updated_form(form);
@@ -1387,10 +1424,36 @@ TEST_F(PasswordManagerTest, PasswordGenerationPresavePassword) {
EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(updated_form, form))
.WillOnce(Return());
manager()->OnPresaveGeneratedPassword(updated_form);
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.GeneratedFormHasNoFormManager", false, 2);
// The user removes the generated password.
EXPECT_CALL(*store_, RemoveLogin(updated_form)).WillOnce(Return());
- manager()->SetHasGeneratedPasswordForForm(&driver_, updated_form, false);
+ manager()->OnPasswordNoLongerGenerated(updated_form);
+}
+
+TEST_F(PasswordManagerTest, PasswordGenerationPresavePassword_NoFormManager) {
+ // Checks that GeneratedFormHasNoFormManager metric is sent if there is no
+ // corresponding PasswordFormManager for the given form. It should be uncommon
+ // case.
+ std::vector<PasswordForm> observed;
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*store_, GetLogins(_, _))
+ .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+ base::HistogramTester histogram_tester;
+
+ // The user accepts a generated password.
+ PasswordForm form(MakeFormWithOnlyNewPasswordField());
+ form.password_value = base::ASCIIToUTF16("password");
+ EXPECT_CALL(*store_, AddLogin(_)).Times(0);
+
+ manager()->OnPresaveGeneratedPassword(form);
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.GeneratedFormHasNoFormManager", true, 1);
}
TEST_F(PasswordManagerTest, PasswordGenerationPresavePasswordAndLogin) {
@@ -1428,7 +1491,6 @@ TEST_F(PasswordManagerTest, PasswordGenerationPresavePasswordAndLogin) {
// The user accepts generated password and makes successful login.
EXPECT_CALL(*store_, AddLogin(form)).WillOnce(Return());
manager()->OnPresaveGeneratedPassword(form);
- manager()->SetHasGeneratedPasswordForForm(&driver_, form, true);
::testing::Mock::VerifyAndClearExpectations(store_.get());
if (!found_matched_logins_in_store)
@@ -1688,4 +1750,63 @@ TEST_F(PasswordManagerTest, ClearedFieldsSuccessCriteria) {
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+// Check that no sync password hash is saved when no username is available,
+// because we it's not clear whether the submitted credentials are sync
+// credentials.
+TEST_F(PasswordManagerTest, NotSavingSyncPasswordHash_NoUsername) {
+ // Simulate loading a simple form with no existing stored password.
+ std::vector<PasswordForm> observed;
+ PasswordForm form(MakeSimpleGAIAForm());
+ // Simulate that no username is found.
+ form.username_value.clear();
+ observed.push_back(form);
+ EXPECT_CALL(*store_, GetLogins(_, _))
+ .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+ EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+
+ // 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);
+ OnPasswordFormSubmitted(form);
+ observed.clear();
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+
+// Check that no sync password hash is saved when the submitted credentials are
+// not qualified as sync credentials.
+TEST_F(PasswordManagerTest, NotSavingSyncPasswordHash_NotSyncCredentials) {
+ // Simulate loading a simple form with no existing stored password.
+ std::vector<PasswordForm> observed;
+ PasswordForm form(MakeSimpleGAIAForm());
+ observed.push_back(form);
+ EXPECT_CALL(*store_, GetLogins(_, _))
+ .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+ EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+
+ // Check that no sync credential password hash is saved since these
+ // credentials are eligible for saving.
+ EXPECT_CALL(*store_, SaveSyncPasswordHash(_)).Times(0);
+
+ std::unique_ptr<PasswordFormManager> form_manager_to_save;
+ EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
+ .WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
+
+ OnPasswordFormSubmitted(form);
+ observed.clear();
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+#endif
+
} // 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 d104a6b7cdc..e28624cd5db 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_util.cc
@@ -11,7 +11,8 @@
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/log_manager.h"
#include "components/sync/driver/sync_service.h"
-#include "crypto/sha2.h"
+#include "crypto/openssl_util.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
namespace password_manager_util {
@@ -84,17 +85,37 @@ bool IsLoggingActive(const password_manager::PasswordManagerClient* client) {
return log_manager && log_manager->IsLoggingActive();
}
-uint64_t Calculate37BitsOfSHA256Hash(const base::StringPiece16& text) {
- constexpr size_t kBytesFromSha256Hash = 5;
- uint8_t hash[kBytesFromSha256Hash];
+uint64_t CalculateSyncPasswordHash(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);
- crypto::SHA256HashString(text_8bits, hash, kBytesFromSha256Hash);
+ 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;
}
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 65a0c09762c..b23906adf93 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.h
+++ b/chromium/components/password_manager/core/browser/password_manager_util.h
@@ -49,8 +49,10 @@ void TrimUsernameOnlyCredentials(
// and required to always return non-null.
bool IsLoggingActive(const password_manager::PasswordManagerClient* client);
-// Returns 37 bits from Sha256 hash.
-uint64_t Calculate37BitsOfSHA256Hash(const base::StringPiece16& text);
+// Calculates 37 bits hash for a sync password. The calculation is based on a
+// slow hash function. The running time is ~10^{-4} seconds on Desktop.
+uint64_t CalculateSyncPasswordHash(const base::StringPiece16& text,
+ const std::string& salt);
} // namespace password_manager_util
diff --git a/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc
index 3cc9fe20d6d..9dc7b88764b 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc
@@ -58,18 +58,24 @@ TEST(PasswordManagerUtil, TrimUsernameOnlyCredentials) {
EXPECT_THAT(forms, UnorderedPasswordFormElementsAre(&expected_forms));
}
-TEST(PasswordManagerUtil, Calculate37BitsOfSHA256Hash) {
- const char* kInputData[] = {"", "password", "secret"};
-
- const uint64_t kExpectedResult[] = {
- UINT64_C(0x1842c4b0e3), UINT64_C(0x55d0601e2), UINT64_C(0x8b9dea8b3)};
-
- ASSERT_EQ(arraysize(kInputData), arraysize(kExpectedResult));
-
- for (size_t i = 0; i < arraysize(kInputData); ++i) {
- base::string16 input = base::UTF8ToUTF16(kInputData[i]);
-
- EXPECT_EQ(kExpectedResult[i],
- password_manager_util::Calculate37BitsOfSHA256Hash(input));
+TEST(PasswordManagerUtil, CalculateSyncPasswordHash) {
+ 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],
+ password_manager_util::CalculateSyncPasswordHash(text, kSalt[i]));
}
}
diff --git a/chromium/components/password_manager/core/browser/password_reuse_defines.h b/chromium/components/password_manager/core/browser/password_reuse_defines.h
new file mode 100644
index 00000000000..99c8f73a742
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_defines.h
@@ -0,0 +1,14 @@
+// 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_PASSWORD_REUSE_DEFINES_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DEFINES_H_
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+// Enable the detection when the sync password is typed not on the sync domain.
+#define SYNC_PASSWORD_REUSE_DETECTION_ENABLED
+#endif
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DEFINES_H_
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 f549345cfcf..27dd2931285 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
@@ -4,24 +4,27 @@
#include "components/password_manager/core/browser/password_reuse_detection_manager.h"
+#include "base/time/default_clock.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_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
-#if defined(SAFE_BROWSING_DB_LOCAL) || defined(SAFE_BROWSING_DB_REMOTE)
-#include "components/safe_browsing/password_protection/password_protection_service.h"
-#endif
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+
+using base::Time;
+using base::TimeDelta;
namespace password_manager {
namespace {
constexpr size_t kMaxNumberOfCharactersToStore = 30;
+constexpr TimeDelta kMaxInactivityTime = TimeDelta::FromSeconds(10);
}
PasswordReuseDetectionManager::PasswordReuseDetectionManager(
PasswordManagerClient* client)
- : client_(client) {
+ : client_(client), clock_(new base::DefaultClock) {
DCHECK(client_);
}
@@ -31,9 +34,28 @@ void PasswordReuseDetectionManager::DidNavigateMainFrame(
const GURL& main_frame_url) {
main_frame_url_ = main_frame_url;
input_characters_.clear();
+ reuse_on_this_page_was_found_ = false;
}
void PasswordReuseDetectionManager::OnKeyPressed(const base::string16& text) {
+ // Do not check reuse if it was already found on this page.
+ if (reuse_on_this_page_was_found_)
+ return;
+
+ // Clear the buffer if last keystoke was more than kMaxInactivityTime ago.
+ Time now = clock_->Now();
+ if (!last_keystroke_time_.is_null() &&
+ (now - last_keystroke_time_) >= kMaxInactivityTime) {
+ input_characters_.clear();
+ }
+ last_keystroke_time_ = now;
+
+ // Clear the buffer and return when enter is pressed.
+ if (text.size() == 1 && text[0] == ui::VKEY_RETURN) {
+ input_characters_.clear();
+ return;
+ }
+
input_characters_ += text;
if (input_characters_.size() > kMaxNumberOfCharactersToStore) {
input_characters_.erase(
@@ -52,6 +74,7 @@ void PasswordReuseDetectionManager::OnReuseFound(
const std::string& legitimate_domain,
int saved_passwords,
int number_matches) {
+ reuse_on_this_page_was_found_ = true;
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
@@ -60,14 +83,25 @@ void PasswordReuseDetectionManager::OnReuseFound(
legitimate_domain);
}
- metrics_util::LogPasswordReuse(
- password.size(), saved_passwords, number_matches,
- client_->GetPasswordManager()->IsPasswordFieldDetectedOnPage());
+ // PasswordManager could be nullptr in tests.
+ bool password_field_detected =
+ client_->GetPasswordManager()
+ ? client_->GetPasswordManager()->IsPasswordFieldDetectedOnPage()
+ : false;
+
+ metrics_util::LogPasswordReuse(password.size(), saved_passwords,
+ number_matches, password_field_detected);
#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.
- client_->CheckProtectedPasswordEntry(legitimate_domain);
+ client_->CheckProtectedPasswordEntry(legitimate_domain,
+ password_field_detected);
#endif
}
+void PasswordReuseDetectionManager::SetClockForTesting(
+ std::unique_ptr<base::Clock> clock) {
+ clock_ = std::move(clock);
+}
+
} // 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 9aba12ba0e9..bad3370f762 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
@@ -7,9 +7,14 @@
#include "base/macros.h"
#include "base/strings/string16.h"
+#include "base/time/time.h"
#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
#include "url/gurl.h"
+namespace base {
+class Clock;
+}
+
namespace password_manager {
class PasswordManagerClient;
@@ -30,10 +35,16 @@ class PasswordReuseDetectionManager : public PasswordReuseDetectorConsumer {
int saved_passwords,
int number_matches) override;
+ void SetClockForTesting(std::unique_ptr<base::Clock> clock);
+
private:
PasswordManagerClient* client_;
base::string16 input_characters_;
GURL main_frame_url_;
+ base::Time last_keystroke_time_;
+ // Used to retrieve the current time, in base::Time units.
+ std::unique_ptr<base::Clock> clock_;
+ bool reuse_on_this_page_was_found_ = false;
DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetectionManager);
};
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 d166a5b6a85..50e0c8903f4 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
@@ -7,10 +7,12 @@
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
+#include "base/test/simple_test_clock.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "url/gurl.h"
using base::ASCIIToUTF16;
@@ -86,6 +88,68 @@ TEST_F(PasswordReuseDetectionManagerTest, CheckReuseCalled) {
}
}
+// Verify that the keystroke buffer is cleared after 10 seconds of user
+// inactivity.
+TEST_F(PasswordReuseDetectionManagerTest,
+ CheckThatBufferClearedAfterInactivity) {
+ EXPECT_CALL(client_, GetPasswordStore())
+ .WillRepeatedly(testing::Return(store_.get()));
+ PasswordReuseDetectionManager manager(&client_);
+
+ std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock);
+ base::Time now = base::Time::Now();
+ clock->SetNow(now);
+ base::SimpleTestClock* clock_weak = clock.get();
+ manager.SetClockForTesting(std::move(clock));
+
+ EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
+ manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+
+ // Simulate 10 seconds of inactivity.
+ clock_weak->SetNow(now + base::TimeDelta::FromSeconds(10));
+ // Expect that a keystroke typed before inactivity is cleared.
+ EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("2"), _, _));
+ manager.OnKeyPressed(base::ASCIIToUTF16("2"));
+}
+
+// Verify that the keystroke buffer is cleared after user presses enter.
+TEST_F(PasswordReuseDetectionManagerTest, CheckThatBufferClearedAfterEnter) {
+ EXPECT_CALL(client_, GetPasswordStore())
+ .WillRepeatedly(testing::Return(store_.get()));
+ PasswordReuseDetectionManager manager(&client_);
+
+ EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
+ manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+
+ base::string16 enter_text(1, ui::VKEY_RETURN);
+ EXPECT_CALL(*store_, CheckReuse(_, _, _)).Times(0);
+ manager.OnKeyPressed(enter_text);
+
+ // Expect only a keystroke typed after enter.
+ EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("2"), _, _));
+ manager.OnKeyPressed(base::ASCIIToUTF16("2"));
+}
+
+// Verify that after reuse found, no reuse checking happens till next main frame
+// navigation.
+TEST_F(PasswordReuseDetectionManagerTest, NoReuseCheckingAfterReuseFound) {
+ EXPECT_CALL(client_, GetPasswordStore())
+ .WillRepeatedly(testing::Return(store_.get()));
+ PasswordReuseDetectionManager manager(&client_);
+
+ // Simulate that reuse found.
+ manager.OnReuseFound(base::string16(), std::string(), 0, 0);
+
+ // Expect no checking of reuse.
+ EXPECT_CALL(*store_, CheckReuse(_, _, _)).Times(0);
+ manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+
+ // Expect that after main frame navigation checking is restored.
+ manager.DidNavigateMainFrame(GURL("https://www.example.com"));
+ EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
+ manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+}
+
} // namespace
} // namespace password_manager
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 a06e94eb183..7d6e1ca7b81 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detector.cc
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector.cc
@@ -5,13 +5,12 @@
#include "components/password_manager/core/browser/password_reuse_detector.h"
#include <algorithm>
+#include <utility>
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
#include "components/password_manager/core/browser/psl_matching_helper.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_service.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_urls.h"
#include "url/origin.h"
@@ -37,14 +36,15 @@ bool IsSuffix(const base::string16& str,
} // namespace
+const char kSyncPasswordDomain[] = "CHROME SYNC";
+
bool ReverseStringLess::operator()(const base::string16& lhs,
const base::string16& rhs) const {
return std::lexicographical_compare(lhs.rbegin(), lhs.rend(), rhs.rbegin(),
rhs.rend());
}
-PasswordReuseDetector::PasswordReuseDetector(PrefService* prefs)
- : prefs_(prefs) {}
+PasswordReuseDetector::PasswordReuseDetector() {}
PasswordReuseDetector::~PasswordReuseDetector() {}
@@ -82,24 +82,25 @@ bool PasswordReuseDetector::CheckSyncPasswordReuse(
const base::string16& input,
const std::string& domain,
PasswordReuseDetectorConsumer* consumer) {
- if (!sync_password_hash_.has_value())
+ if (!sync_password_data_.has_value())
return false;
const Origin gaia_origin(GaiaUrls::GetInstance()->gaia_url().GetOrigin());
if (Origin(GURL(domain)).IsSameOriginWith(gaia_origin))
return false;
- // Check that some suffix of |input| has the same hash as the sync password.
- for (size_t i = 0; i + kMinPasswordLengthToCheck <= input.size(); ++i) {
- base::StringPiece16 input_suffix(input.c_str() + i, input.size() - i);
- if (password_manager_util::Calculate37BitsOfSHA256Hash(input_suffix) ==
- sync_password_hash_.value()) {
- consumer->OnReuseFound(input_suffix.as_string(), gaia_origin.host(), 1,
- 0);
- return true;
- }
- }
+ if (input.size() < sync_password_data_->length)
+ return false;
+ size_t offset = input.size() - sync_password_data_->length;
+ base::string16 reuse_candidate = input.substr(offset);
+
+ if (password_manager_util::CalculateSyncPasswordHash(
+ reuse_candidate, sync_password_data_->salt) ==
+ sync_password_data_->hash) {
+ consumer->OnReuseFound(reuse_candidate, kSyncPasswordDomain, 1, 0);
+ return true;
+ }
return false;
}
@@ -126,15 +127,13 @@ bool PasswordReuseDetector::CheckSavedPasswordReuse(
return false;
}
-void PasswordReuseDetector::SaveSyncPasswordHash(
- const base::string16& password) {
- sync_password_hash_ =
- password_manager_util::Calculate37BitsOfSHA256Hash(password);
- if (prefs_) {
- // TODO(crbug.com/657041) Implement encrypting and saving of
- // |sync_password_hash_| into preference kSyncPasswordHash.
- prefs_->SetString(prefs::kSyncPasswordHash, std::string());
- }
+void PasswordReuseDetector::UseSyncPasswordHash(
+ base::Optional<SyncPasswordData> sync_password_data) {
+ sync_password_data_ = std::move(sync_password_data);
+}
+
+void PasswordReuseDetector::ClearSyncPasswordHash() {
+ sync_password_data_.reset();
}
void PasswordReuseDetector::AddPassword(const autofill::PasswordForm& form) {
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 d42f3137c77..4453debc4b2 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detector.h
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector.h
@@ -15,11 +15,10 @@
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/string16.h"
+#include "components/password_manager/core/browser/hash_password_manager.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
-class PrefService;
-
namespace password_manager {
class PasswordReuseDetectorConsumer;
@@ -29,6 +28,9 @@ struct ReverseStringLess {
bool operator()(const base::string16& lhs, const base::string16& rhs) const;
};
+// Used to identify chrome sync password in password entry event.
+extern const char kSyncPasswordDomain[];
+
// Per-profile class responsible for detection of password reuse, i.e. that the
// user input on some site contains the password saved on another site.
// It receives saved passwords through PasswordStoreConsumer interface.
@@ -36,7 +38,7 @@ struct ReverseStringLess {
// a password reuse.
class PasswordReuseDetector : public PasswordStoreConsumer {
public:
- explicit PasswordReuseDetector(PrefService* prefs);
+ PasswordReuseDetector();
~PasswordReuseDetector() override;
// PasswordStoreConsumer
@@ -55,8 +57,11 @@ class PasswordReuseDetector : public PasswordStoreConsumer {
const std::string& domain,
PasswordReuseDetectorConsumer* consumer);
- // Saves a hash of |password| for password reuse checking.
- void SaveSyncPasswordHash(const base::string16& password);
+ // Stores internal |sync_password_data| for password reuse checking.
+ void UseSyncPasswordHash(base::Optional<SyncPasswordData> sync_password_data);
+
+ // Clears a sync password hash if it was saved.
+ void ClearSyncPasswordHash();
private:
using passwords_iterator = std::map<base::string16,
@@ -93,8 +98,7 @@ class PasswordReuseDetector : public PasswordStoreConsumer {
// of times how many different sites it's saved on.
int saved_passwords_ = 0;
- base::Optional<uint64_t> sync_password_hash_;
- PrefService* const prefs_;
+ base::Optional<SyncPasswordData> sync_password_data_;
DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetector);
};
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.h b/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.h
index b621b777e4f..fb6dc8c817a 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.h
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.h
@@ -21,8 +21,8 @@ class PasswordReuseDetectorConsumer
virtual ~PasswordReuseDetectorConsumer();
// Called when a password reuse is found.
- // |legitimate_domain| is the domain on which |password| is saved or the sync
- // domain if |password| is a sync password.
+ // |legitimate_domain| is the domain on which |password| is saved or
+ // safe_browsing::kChromeSyncDomain if |password| is a sync password.
// |saved_passwords| is total number of passwords stored in Password Manager.
// |number_matches| is a number of sites on which |password| is saved.
virtual void OnReuseFound(const base::string16& password,
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 94d2fa82665..8b510f85b52 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
@@ -10,10 +10,9 @@
#include "base/strings/utf_string_conversions.h"
#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/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -62,8 +61,17 @@ PasswordStoreChangeList GetChangeList(
return changes;
}
+SyncPasswordData GetSyncPasswordData(const std::string& sync_password) {
+ SyncPasswordData sync_password_data;
+ sync_password_data.salt = "1234567890123456";
+ sync_password_data.length = sync_password.size();
+ sync_password_data.hash = password_manager_util::CalculateSyncPasswordHash(
+ base::ASCIIToUTF16(sync_password), sync_password_data.salt);
+ return sync_password_data;
+}
+
TEST(PasswordReuseDetectorTest, TypingPasswordOnDifferentSite) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -94,7 +102,7 @@ TEST(PasswordReuseDetectorTest, TypingPasswordOnDifferentSite) {
}
TEST(PasswordReuseDetectorTest, PSLMatchNoReuseEvent) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -104,7 +112,7 @@ TEST(PasswordReuseDetectorTest, PSLMatchNoReuseEvent) {
}
TEST(PasswordReuseDetectorTest, NoPSLMatchReuseEvent) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -117,7 +125,7 @@ TEST(PasswordReuseDetectorTest, NoPSLMatchReuseEvent) {
}
TEST(PasswordReuseDetectorTest, TooShortPasswordNoReuseEvent) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -126,7 +134,7 @@ TEST(PasswordReuseDetectorTest, TooShortPasswordNoReuseEvent) {
}
TEST(PasswordReuseDetectorTest, PasswordNotInputSuffixNoReuseEvent) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -141,7 +149,7 @@ TEST(PasswordReuseDetectorTest, OnLoginsChanged) {
for (PasswordStoreChange::Type type :
{PasswordStoreChange::ADD, PasswordStoreChange::UPDATE,
PasswordStoreChange::REMOVE}) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
PasswordStoreChangeList changes =
GetChangeList(type, GetForms(GetTestDomainsPasswords()));
reuse_detector.OnLoginsChanged(changes);
@@ -165,7 +173,7 @@ TEST(PasswordReuseDetectorTest, CheckLongestPasswordMatchReturn) {
{"https://example3.com", "1234567890"},
};
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(domain_passwords));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -188,14 +196,16 @@ TEST(PasswordReuseDetectorTest, CheckLongestPasswordMatchReturn) {
}
TEST(PasswordReuseDetectorTest, SyncPasswordNoReuse) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
- reuse_detector.SaveSyncPasswordHash(ASCIIToUTF16("sync_password"));
+ std::string sync_password = "sync_password";
+ reuse_detector.UseSyncPasswordHash(GetSyncPasswordData(sync_password));
EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
- reuse_detector.CheckReuse(ASCIIToUTF16("sync_password"),
+ // Typing sync password on https://accounts.google.com is OK.
+ reuse_detector.CheckReuse(ASCIIToUTF16("123sync_password"),
"https://accounts.google.com", &mockConsumer);
// Only suffixes are verifed.
reuse_detector.CheckReuse(ASCIIToUTF16("sync_password123"),
@@ -203,14 +213,16 @@ TEST(PasswordReuseDetectorTest, SyncPasswordNoReuse) {
}
TEST(PasswordReuseDetectorTest, SyncPasswordReuseFound) {
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
- reuse_detector.SaveSyncPasswordHash(ASCIIToUTF16("sync_password"));
+ std::string sync_password = "sync_password";
+ reuse_detector.UseSyncPasswordHash(GetSyncPasswordData(sync_password));
- EXPECT_CALL(mockConsumer, OnReuseFound(ASCIIToUTF16("sync_password"),
- "accounts.google.com", 1, 0));
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("sync_password"),
+ std::string(kSyncPasswordDomain), 1, 0));
reuse_detector.CheckReuse(ASCIIToUTF16("sync_password"), "https://evil.com",
&mockConsumer);
}
@@ -218,11 +230,12 @@ TEST(PasswordReuseDetectorTest, SyncPasswordReuseFound) {
TEST(PasswordReuseDetectorTest, SavedPasswordsReuseSyncPasswordAvailable) {
// Check that reuse of saved passwords is detected also if the sync password
// hash is saved.
- PasswordReuseDetector reuse_detector(nullptr);
+ PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
- reuse_detector.SaveSyncPasswordHash(ASCIIToUTF16("sync_password"));
+ std::string sync_password = "sync_password";
+ reuse_detector.UseSyncPasswordHash(GetSyncPasswordData(sync_password));
EXPECT_CALL(mockConsumer,
OnReuseFound(ASCIIToUTF16("password"), "google.com", 5, 1));
@@ -230,14 +243,18 @@ TEST(PasswordReuseDetectorTest, SavedPasswordsReuseSyncPasswordAvailable) {
&mockConsumer);
}
-TEST(PasswordReuseDetectorTest, CheckThatSyncPasswordIsStoredIntoPreferences) {
- TestingPrefServiceSimple prefs;
- prefs.registry()->RegisterStringPref(prefs::kSyncPasswordHash, std::string(),
- PrefRegistry::NO_REGISTRATION_FLAGS);
- ASSERT_FALSE(prefs.HasPrefPath(prefs::kSyncPasswordHash));
- PasswordReuseDetector reuse_detector(&prefs);
- reuse_detector.SaveSyncPasswordHash(ASCIIToUTF16("sync_password"));
- EXPECT_TRUE(prefs.HasPrefPath(prefs::kSyncPasswordHash));
+TEST(PasswordReuseDetectorTest, ClearSyncPasswordHash) {
+ PasswordReuseDetector reuse_detector;
+ reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ std::string sync_password = "sync_password";
+ reuse_detector.UseSyncPasswordHash(GetSyncPasswordData(sync_password));
+ reuse_detector.ClearSyncPasswordHash();
+
+ // Check that no reuse is found, since hash is cleared.
+ reuse_detector.CheckReuse(ASCIIToUTF16("sync_password"), "https://evil.com",
+ &mockConsumer);
}
} // namespace
diff --git a/chromium/components/password_manager/core/browser/password_store.cc b/chromium/components/password_manager/core/browser/password_store.cc
index 0575e5de99a..5adf79f2de2 100644
--- a/chromium/components/password_manager/core/browser/password_store.cc
+++ b/chromium/components/password_manager/core/browser/password_store.cc
@@ -14,14 +14,18 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/affiliated_match_helper.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/password_syncable_service.h"
#include "components/password_manager/core/browser/statistics_table.h"
+#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
+#include "components/password_manager/core/browser/password_store_signin_notifier.h"
+#endif
+
using autofill::PasswordForm;
namespace password_manager {
@@ -29,7 +33,7 @@ namespace password_manager {
PasswordStore::GetLoginsRequest::GetLoginsRequest(
PasswordStoreConsumer* consumer)
: consumer_weak_(consumer->GetWeakPtr()) {
- origin_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ origin_task_runner_ = base::SequencedTaskRunnerHandle::Get();
}
PasswordStore::GetLoginsRequest::~GetLoginsRequest() {
@@ -60,7 +64,7 @@ void PasswordStore::GetLoginsRequest::NotifyWithSiteStatistics(
#if !defined(OS_ANDROID) && !defined(OS_IOS)
PasswordStore::CheckReuseRequest::CheckReuseRequest(
PasswordReuseDetectorConsumer* consumer)
- : origin_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ : origin_task_runner_(base::SequencedTaskRunnerHandle::Get()),
consumer_weak_(consumer->AsWeakPtr()) {}
PasswordStore::CheckReuseRequest::~CheckReuseRequest() {}
@@ -103,8 +107,8 @@ bool PasswordStore::FormDigest::operator==(const FormDigest& other) const {
}
PasswordStore::PasswordStore(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner)
+ scoped_refptr<base::SequencedTaskRunner> main_thread_runner,
+ scoped_refptr<base::SequencedTaskRunner> db_thread_runner)
: main_thread_runner_(main_thread_runner),
db_thread_runner_(db_thread_runner),
observers_(new base::ObserverListThreadSafe<Observer>()),
@@ -113,8 +117,13 @@ PasswordStore::PasswordStore(
bool PasswordStore::Init(const syncer::SyncableService::StartSyncFlare& flare,
PrefService* prefs) {
+ ScheduleTask(base::Bind(&PasswordStore::InitOnBackgroundThread, this, flare));
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ hash_password_manager_.set_prefs(prefs);
ScheduleTask(
- base::Bind(&PasswordStore::InitOnBackgroundThread, this, flare, prefs));
+ base::Bind(&PasswordStore::SaveSyncPasswordHashImpl, this,
+ base::Passed(hash_password_manager_.RetrievePasswordHash())));
+#endif
return true;
}
@@ -184,11 +193,6 @@ void PasswordStore::DisableAutoSignInForOrigins(
base::Callback<bool(const GURL&)>(origin_filter), completion));
}
-void PasswordStore::TrimAffiliationCache() {
- if (affiliated_match_helper_)
- affiliated_match_helper_->TrimAffiliationCache();
-}
-
void PasswordStore::GetLogins(const FormDigest& form,
PasswordStoreConsumer* consumer) {
// Per http://crbug.com/121738, we deliberately ignore saved logins for
@@ -237,9 +241,10 @@ void PasswordStore::GetAutofillableLogins(PasswordStoreConsumer* consumer) {
Schedule(&PasswordStore::GetAutofillableLoginsImpl, consumer);
}
-void PasswordStore::GetAutofillableLoginsWithAffiliatedRealms(
+void PasswordStore::GetAutofillableLoginsWithAffiliationAndBrandingInformation(
PasswordStoreConsumer* consumer) {
- Schedule(&PasswordStore::GetAutofillableLoginsWithAffiliatedRealmsImpl,
+ Schedule(&PasswordStore::
+ GetAutofillableLoginsWithAffiliationAndBrandingInformationImpl,
consumer);
}
@@ -247,15 +252,16 @@ void PasswordStore::GetBlacklistLogins(PasswordStoreConsumer* consumer) {
Schedule(&PasswordStore::GetBlacklistLoginsImpl, consumer);
}
-void PasswordStore::GetBlacklistLoginsWithAffiliatedRealms(
+void PasswordStore::GetBlacklistLoginsWithAffiliationAndBrandingInformation(
PasswordStoreConsumer* consumer) {
- Schedule(&PasswordStore::GetBlacklistLoginsWithAffiliatedRealmsImpl,
+ Schedule(&PasswordStore::
+ GetBlacklistLoginsWithAffiliationAndBrandingInformationImpl,
consumer);
}
void PasswordStore::ReportMetrics(const std::string& sync_username,
bool custom_passphrase_sync_enabled) {
- scoped_refptr<base::SingleThreadTaskRunner> task_runner(
+ scoped_refptr<base::SequencedTaskRunner> task_runner(
GetBackgroundTaskRunner());
if (task_runner) {
base::Closure task =
@@ -264,6 +270,12 @@ void PasswordStore::ReportMetrics(const std::string& sync_username,
task_runner->PostDelayedTask(FROM_HERE, task,
base::TimeDelta::FromSeconds(30));
}
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+ if (!sync_username.empty())
+ hash_password_manager_.ReportIsSyncPasswordHashSavedMetric();
+#endif
}
void PasswordStore::AddSiteStats(const InteractionsStats& stats) {
@@ -297,7 +309,7 @@ void PasswordStore::RemoveObserver(Observer* observer) {
}
bool PasswordStore::ScheduleTask(const base::Closure& task) {
- scoped_refptr<base::SingleThreadTaskRunner> task_runner(
+ scoped_refptr<base::SequencedTaskRunner> task_runner(
GetBackgroundTaskRunner());
if (task_runner.get())
return task_runner->PostTask(FROM_HERE, task);
@@ -309,11 +321,15 @@ void PasswordStore::ShutdownOnUIThread() {
// The AffiliationService must be destroyed from the main thread.
affiliated_match_helper_.reset();
shutdown_called_ = true;
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ if (notifier_)
+ notifier_->UnsubscribeFromSigninEvents();
+#endif
}
base::WeakPtr<syncer::SyncableService>
PasswordStore::GetPasswordSyncableService() {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
DCHECK(syncable_service_);
return syncable_service_->AsWeakPtr();
}
@@ -327,10 +343,29 @@ void PasswordStore::CheckReuse(const base::string16& input,
ScheduleTask(base::Bind(&PasswordStore::CheckReuseImpl, this,
base::Passed(&check_reuse_request), input, domain));
}
+#endif
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
void PasswordStore::SaveSyncPasswordHash(const base::string16& password) {
- ScheduleTask(
- base::Bind(&PasswordStore::SaveSyncPasswordHashImpl, this, password));
+ // TODO(crbug.com/657041): Log success of saving password hash to UMA.
+ hash_password_manager_.SavePasswordHash(password);
+ base::Optional<SyncPasswordData> sync_password_data =
+ hash_password_manager_.RetrievePasswordHash();
+ ScheduleTask(base::Bind(&PasswordStore::SaveSyncPasswordHashImpl, this,
+ std::move(sync_password_data)));
+}
+
+void PasswordStore::ClearSyncPasswordHash() {
+ hash_password_manager_.ClearSavedPasswordHash();
+ ScheduleTask(base::Bind(&PasswordStore::ClearSyncPasswordHashImpl, this));
+}
+
+void PasswordStore::SetPasswordStoreSigninNotifier(
+ std::unique_ptr<PasswordStoreSigninNotifier> notifier) {
+ DCHECK(!notifier_);
+ DCHECK(notifier);
+ notifier_ = std::move(notifier);
+ notifier_->SubscribeToSigninEvents(this);
}
#endif
@@ -338,7 +373,7 @@ PasswordStore::~PasswordStore() {
DCHECK(shutdown_called_);
}
-scoped_refptr<base::SingleThreadTaskRunner>
+scoped_refptr<base::SequencedTaskRunner>
PasswordStore::GetBackgroundTaskRunner() {
return db_thread_runner_;
}
@@ -393,7 +428,7 @@ PasswordStoreChangeList PasswordStore::RemoveLoginSync(
void PasswordStore::NotifyLoginsChanged(
const PasswordStoreChangeList& changes) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
if (!changes.empty()) {
observers_->Notify(FROM_HERE, &Observer::OnLoginsChanged, changes);
if (syncable_service_)
@@ -415,9 +450,15 @@ void PasswordStore::CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request,
reuse_detector_->CheckReuse(input, domain, request.get());
}
-void PasswordStore::SaveSyncPasswordHashImpl(const base::string16& password) {
+void PasswordStore::SaveSyncPasswordHashImpl(
+ base::Optional<SyncPasswordData> sync_password_data) {
+ if (reuse_detector_)
+ reuse_detector_->UseSyncPasswordHash(std::move(sync_password_data));
+}
+
+void PasswordStore::ClearSyncPasswordHashImpl() {
if (reuse_detector_)
- reuse_detector_->SaveSyncPasswordHash(password);
+ reuse_detector_->ClearSyncPasswordHash();
}
#endif
@@ -522,8 +563,9 @@ void PasswordStore::GetAutofillableLoginsImpl(
request->NotifyConsumerWithResults(std::move(obtained_forms));
}
-void PasswordStore::GetAutofillableLoginsWithAffiliatedRealmsImpl(
- std::unique_ptr<GetLoginsRequest> request) {
+void PasswordStore::
+ GetAutofillableLoginsWithAffiliationAndBrandingInformationImpl(
+ std::unique_ptr<GetLoginsRequest> request) {
std::vector<std::unique_ptr<PasswordForm>> obtained_forms;
if (!FillAutofillableLogins(&obtained_forms))
obtained_forms.clear();
@@ -531,16 +573,10 @@ void PasswordStore::GetAutofillableLoginsWithAffiliatedRealmsImpl(
// post a request to UI thread.
main_thread_runner_->PostTask(
FROM_HERE,
- base::Bind(&PasswordStore::InjectAffiliatedWebRealms, this,
+ base::Bind(&PasswordStore::InjectAffiliationAndBrandingInformation, this,
base::Passed(&obtained_forms), base::Passed(&request)));
}
-void PasswordStore::NotifyLoginsWithAffiliatedRealms(
- std::unique_ptr<GetLoginsRequest> request,
- std::vector<std::unique_ptr<PasswordForm>> obtained_forms) {
- request->NotifyConsumerWithResults(std::move(obtained_forms));
-}
-
void PasswordStore::GetBlacklistLoginsImpl(
std::unique_ptr<GetLoginsRequest> request) {
std::vector<std::unique_ptr<PasswordForm>> obtained_forms;
@@ -549,7 +585,7 @@ void PasswordStore::GetBlacklistLoginsImpl(
request->NotifyConsumerWithResults(std::move(obtained_forms));
}
-void PasswordStore::GetBlacklistLoginsWithAffiliatedRealmsImpl(
+void PasswordStore::GetBlacklistLoginsWithAffiliationAndBrandingInformationImpl(
std::unique_ptr<GetLoginsRequest> request) {
std::vector<std::unique_ptr<PasswordForm>> obtained_forms;
if (!FillBlacklistLogins(&obtained_forms))
@@ -558,7 +594,7 @@ void PasswordStore::GetBlacklistLoginsWithAffiliatedRealmsImpl(
// post a request to UI thread.
main_thread_runner_->PostTask(
FROM_HERE,
- base::Bind(&PasswordStore::InjectAffiliatedWebRealms, this,
+ base::Bind(&PasswordStore::InjectAffiliationAndBrandingInformation, this,
base::Passed(&obtained_forms), base::Passed(&request)));
}
@@ -576,7 +612,7 @@ void PasswordStore::GetLoginsWithAffiliationsImpl(
const FormDigest& form,
std::unique_ptr<GetLoginsRequest> request,
const std::vector<std::string>& additional_android_realms) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
std::vector<std::unique_ptr<PasswordForm>> results(FillMatchingLogins(form));
for (const std::string& realm : additional_android_realms) {
std::vector<std::unique_ptr<PasswordForm>> more_results(
@@ -592,11 +628,11 @@ void PasswordStore::GetLoginsWithAffiliationsImpl(
request->NotifyConsumerWithResults(std::move(results));
}
-void PasswordStore::InjectAffiliatedWebRealms(
+void PasswordStore::InjectAffiliationAndBrandingInformation(
std::vector<std::unique_ptr<PasswordForm>> forms,
std::unique_ptr<GetLoginsRequest> request) {
if (affiliated_match_helper_) {
- affiliated_match_helper_->InjectAffiliatedWebRealms(
+ affiliated_match_helper_->InjectAffiliationAndBrandingInformation(
std::move(forms),
base::Bind(&PasswordStore::GetLoginsRequest::NotifyConsumerWithResults,
base::Owned(request.release())));
@@ -616,7 +652,7 @@ void PasswordStore::ScheduleGetLoginsWithAffiliations(
std::unique_ptr<PasswordForm> PasswordStore::GetLoginImpl(
const PasswordForm& primary_key) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
std::vector<std::unique_ptr<PasswordForm>> candidates(
FillMatchingLogins(FormDigest(primary_key)));
for (auto& candidate : candidates) {
@@ -650,7 +686,7 @@ void PasswordStore::ScheduleFindAndUpdateAffiliatedWebLogins(
void PasswordStore::UpdateAffiliatedWebLoginsImpl(
const PasswordForm& updated_android_form,
const std::vector<std::string>& affiliated_web_realms) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
PasswordStoreChangeList all_changes;
for (const std::string& affiliated_web_realm : affiliated_web_realms) {
std::vector<std::unique_ptr<PasswordForm>> web_logins(FillMatchingLogins(
@@ -735,22 +771,21 @@ void PasswordStore::ScheduleUpdateAffiliatedWebLoginsImpl(
}
void PasswordStore::InitOnBackgroundThread(
- const syncer::SyncableService::StartSyncFlare& flare,
- PrefService* prefs) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ const syncer::SyncableService::StartSyncFlare& flare) {
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
DCHECK(!syncable_service_);
syncable_service_.reset(new PasswordSyncableService(this));
syncable_service_->InjectStartSyncFlare(flare);
// TODO(crbug.com/706392): Fix password reuse detection for Android.
#if !defined(OS_ANDROID) && !defined(OS_IOS)
- reuse_detector_ = base::MakeUnique<PasswordReuseDetector>(prefs);
+ reuse_detector_ = base::MakeUnique<PasswordReuseDetector>();
GetAutofillableLoginsImpl(
base::MakeUnique<GetLoginsRequest>(reuse_detector_.get()));
#endif
}
void PasswordStore::DestroyOnBackgroundThread() {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
syncable_service_.reset();
// TODO(crbug.com/706392): Fix password reuse detection for Android.
#if !defined(OS_ANDROID) && !defined(OS_IOS)
diff --git a/chromium/components/password_manager/core/browser/password_store.h b/chromium/components/password_manager/core/browser/password_store.h
index 6c2f104e326..5616935d49e 100644
--- a/chromium/components/password_manager/core/browser/password_store.h
+++ b/chromium/components/password_manager/core/browser/password_store.h
@@ -14,20 +14,21 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/observer_list_threadsafe.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
#include "components/keyed_service/core/refcounted_keyed_service.h"
+#include "components/password_manager/core/browser/password_reuse_defines.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/browser/password_store_sync.h"
#include "components/sync/model/syncable_service.h"
// TODO(crbug.com/706392): Fix password reuse detection for Android.
#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#include "components/password_manager/core/browser/hash_password_manager.h"
#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
#endif
-class PasswordStoreProxyMac;
class PrefService;
namespace autofill {
@@ -42,6 +43,7 @@ namespace password_manager {
class AffiliatedMatchHelper;
class PasswordStoreConsumer;
+class PasswordStoreSigninNotifier;
class PasswordSyncableService;
struct InteractionsStats;
@@ -89,8 +91,8 @@ class PasswordStore : protected PasswordStoreSync,
GURL origin;
};
- PasswordStore(scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner);
+ PasswordStore(scoped_refptr<base::SequencedTaskRunner> main_thread_runner,
+ scoped_refptr<base::SequencedTaskRunner> db_thread_runner);
// Reimplement this to add custom initialization. Always call this too.
virtual bool Init(const syncer::SyncableService::StartSyncFlare& flare,
@@ -173,10 +175,6 @@ class PasswordStore : protected PasswordStoreSync,
const base::Callback<bool(const GURL&)>& origin_filter,
const base::Closure& completion);
- // Removes cached affiliation data that is no longer needed; provided that
- // affiliation-based matching is enabled.
- void TrimAffiliationCache();
-
// Searches for a matching PasswordForm, and notifies |consumer| on
// completion. The request will be cancelled if the consumer is destroyed.
virtual void GetLogins(const FormDigest& form,
@@ -202,9 +200,9 @@ class PasswordStore : protected PasswordStoreSync,
// The request will be cancelled if the consumer is destroyed.
virtual void GetAutofillableLogins(PasswordStoreConsumer* consumer);
- // Same as above, but also fills in |affiliated_web_realm| for Android
- // credentials.
- virtual void GetAutofillableLoginsWithAffiliatedRealms(
+ // Same as above, but also fills in affiliation and branding information for
+ // Android credentials.
+ virtual void GetAutofillableLoginsWithAffiliationAndBrandingInformation(
PasswordStoreConsumer* consumer);
// Gets the complete list of PasswordForms that are blacklist entries,
@@ -212,9 +210,9 @@ class PasswordStore : protected PasswordStoreSync,
// consumer is destroyed.
virtual void GetBlacklistLogins(PasswordStoreConsumer* consumer);
- // Same as above, but also fills in |affiliated_web_realm| for Android
- // credentials.
- virtual void GetBlacklistLoginsWithAffiliatedRealms(
+ // Same as above, but also fills in affiliation and branding information for
+ // Android credentials.
+ virtual void GetBlacklistLoginsWithAffiliationAndBrandingInformation(
PasswordStoreConsumer* consumer);
// Reports usage metrics for the database. |sync_username| and
@@ -259,8 +257,17 @@ class PasswordStore : protected PasswordStoreSync,
const std::string& domain,
PasswordReuseDetectorConsumer* consumer);
+#if !defined(OS_CHROMEOS)
// Saves a hash of |password| for password reuse checking.
- void SaveSyncPasswordHash(const base::string16& password);
+ virtual void SaveSyncPasswordHash(const base::string16& password);
+
+ // Clears the saved sync password hash.
+ virtual void ClearSyncPasswordHash();
+
+ // Shouldn't be called more than once, |notifier| must be not nullptr.
+ void SetPasswordStoreSigninNotifier(
+ std::unique_ptr<PasswordStoreSigninNotifier> notifier);
+#endif
#endif
protected:
@@ -292,7 +299,7 @@ class PasswordStore : protected PasswordStoreSync,
// See GetLogins(). Logins older than this will be removed from the reply.
base::Time ignore_logins_cutoff_;
- scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
base::WeakPtr<PasswordStoreConsumer> consumer_weak_;
DISALLOW_COPY_AND_ASSIGN(GetLoginsRequest);
@@ -316,7 +323,7 @@ class PasswordStore : protected PasswordStoreSync,
int number_matches) override;
private:
- const scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+ const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
const base::WeakPtr<PasswordReuseDetectorConsumer> consumer_weak_;
DISALLOW_COPY_AND_ASSIGN(CheckReuseRequest);
@@ -326,9 +333,9 @@ class PasswordStore : protected PasswordStoreSync,
~PasswordStore() override;
// Get the TaskRunner to use for PasswordStore background tasks.
- // By default, a SingleThreadTaskRunner on the DB thread is used, but
+ // By default, a SequencedTaskRunner on the DB thread is used, but
// subclasses can override.
- virtual scoped_refptr<base::SingleThreadTaskRunner> GetBackgroundTaskRunner();
+ virtual scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner();
// Methods below will be run in PasswordStore's own thread.
// Synchronous implementation that reports usage metrics.
@@ -429,22 +436,24 @@ class PasswordStore : protected PasswordStoreSync,
const std::string& domain);
// Synchronous implementation of SaveSyncPasswordHash().
- void SaveSyncPasswordHashImpl(const base::string16& password);
+ void SaveSyncPasswordHashImpl(
+ base::Optional<SyncPasswordData> sync_password_data);
+
+ // Synchronous implementation of ClearSyncPasswordHash().
+ void ClearSyncPasswordHashImpl();
#endif
// TaskRunner for tasks that run on the main thread (usually the UI thread).
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner_;
+ scoped_refptr<base::SequencedTaskRunner> main_thread_runner_;
// TaskRunner for the DB thread. By default, this is the task runner used for
// background tasks -- see |GetBackgroundTaskRunner|.
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner_;
+ scoped_refptr<base::SequencedTaskRunner> db_thread_runner_;
private:
FRIEND_TEST_ALL_PREFIXES(PasswordStoreTest, GetLoginImpl);
FRIEND_TEST_ALL_PREFIXES(PasswordStoreTest,
UpdatePasswordsStoredForAffiliatedWebsites);
- // TODO(vasilii): remove this together with PasswordStoreProxyMac.
- friend class ::PasswordStoreProxyMac;
// Schedule the given |func| to be run in the PasswordStore's own thread with
// responses delivered to |consumer| on the current thread.
@@ -493,17 +502,17 @@ class PasswordStore : protected PasswordStoreSync,
// Finds all non-blacklist PasswordForms, and notifies the consumer.
void GetAutofillableLoginsImpl(std::unique_ptr<GetLoginsRequest> request);
- // Same as above, but also fills in |affiliated_web_realm| for Android
- // credentials.
- void GetAutofillableLoginsWithAffiliatedRealmsImpl(
+ // Same as above, but also fills in affiliation and branding information for
+ // Android credentials.
+ void GetAutofillableLoginsWithAffiliationAndBrandingInformationImpl(
std::unique_ptr<GetLoginsRequest> request);
// Finds all blacklist PasswordForms, and notifies the consumer.
void GetBlacklistLoginsImpl(std::unique_ptr<GetLoginsRequest> request);
- // Same as above, but also fills in |affiliated_web_realm| for Android
- // credentials.
- void GetBlacklistLoginsWithAffiliatedRealmsImpl(
+ // Same as above, but also fills in affiliation and branding information for
+ // Android credentials.
+ void GetBlacklistLoginsWithAffiliationAndBrandingInformationImpl(
std::unique_ptr<GetLoginsRequest> request);
// Notifies |request| about the stats for all sites.
@@ -513,12 +522,6 @@ class PasswordStore : protected PasswordStoreSync,
void NotifySiteStats(const GURL& origin_domain,
std::unique_ptr<GetLoginsRequest> request);
- // Notifies |request| about the autofillable logins with affiliated web
- // realms for Android credentials.
- void NotifyLoginsWithAffiliatedRealms(
- std::unique_ptr<GetLoginsRequest> request,
- std::vector<std::unique_ptr<autofill::PasswordForm>> obtained_forms);
-
// Extended version of GetLoginsImpl that also returns credentials stored for
// the specified affiliated Android applications. That is, it finds all
// PasswordForms with a signon_realm that is either:
@@ -531,9 +534,9 @@ class PasswordStore : protected PasswordStoreSync,
std::unique_ptr<GetLoginsRequest> request,
const std::vector<std::string>& additional_android_realms);
- // Retrieves and fills in |affiliated_web_realm| values for Android
+ // Retrieves and fills in affiliation and branding information for Android
// credentials in |forms|. Called on the main thread.
- void InjectAffiliatedWebRealms(
+ void InjectAffiliationAndBrandingInformation(
std::vector<std::unique_ptr<autofill::PasswordForm>> forms,
std::unique_ptr<GetLoginsRequest> request);
@@ -578,8 +581,7 @@ class PasswordStore : protected PasswordStoreSync,
// Creates PasswordSyncableService and PasswordReuseDetector instances on the
// background thread.
void InitOnBackgroundThread(
- const syncer::SyncableService::StartSyncFlare& flare,
- PrefService* prefs);
+ const syncer::SyncableService::StartSyncFlare& flare);
// Deletes objest that should be destroyed on the background thread.
void DestroyOnBackgroundThread();
@@ -593,6 +595,11 @@ class PasswordStore : protected PasswordStoreSync,
#if !defined(OS_ANDROID) && !defined(OS_IOS)
std::unique_ptr<PasswordReuseDetector> reuse_detector_;
#endif
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ std::unique_ptr<PasswordStoreSigninNotifier> notifier_;
+ HashPasswordManager hash_password_manager_;
+#endif
+
bool is_propagating_password_changes_to_web_credentials_enabled_;
bool shutdown_called_;
diff --git a/chromium/components/password_manager/core/browser/password_store_default.cc b/chromium/components/password_manager/core/browser/password_store_default.cc
index 2e05c0a7c3d..64261d99f93 100644
--- a/chromium/components/password_manager/core/browser/password_store_default.cc
+++ b/chromium/components/password_manager/core/browser/password_store_default.cc
@@ -17,8 +17,8 @@ using autofill::PasswordForm;
namespace password_manager {
PasswordStoreDefault::PasswordStoreDefault(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
+ scoped_refptr<base::SequencedTaskRunner> main_thread_runner,
+ scoped_refptr<base::SequencedTaskRunner> db_thread_runner,
std::unique_ptr<LoginDatabase> login_db)
: PasswordStore(main_thread_runner, db_thread_runner),
login_db_(std::move(login_db)) {}
@@ -39,7 +39,7 @@ void PasswordStoreDefault::ShutdownOnUIThread() {
}
void PasswordStoreDefault::InitOnDBThread() {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
DCHECK(login_db_);
if (!login_db_->Init()) {
login_db_.reset();
@@ -52,13 +52,13 @@ void PasswordStoreDefault::ReportMetricsImpl(
bool custom_passphrase_sync_enabled) {
if (!login_db_)
return;
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
login_db_->ReportMetrics(sync_username, custom_passphrase_sync_enabled);
}
PasswordStoreChangeList PasswordStoreDefault::AddLoginImpl(
const PasswordForm& form) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
if (!login_db_)
return PasswordStoreChangeList();
return login_db_->AddLogin(form);
@@ -66,7 +66,7 @@ PasswordStoreChangeList PasswordStoreDefault::AddLoginImpl(
PasswordStoreChangeList PasswordStoreDefault::UpdateLoginImpl(
const PasswordForm& form) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
if (!login_db_)
return PasswordStoreChangeList();
return login_db_->UpdateLogin(form);
@@ -74,7 +74,7 @@ PasswordStoreChangeList PasswordStoreDefault::UpdateLoginImpl(
PasswordStoreChangeList PasswordStoreDefault::RemoveLoginImpl(
const PasswordForm& form) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
PasswordStoreChangeList changes;
if (login_db_ && login_db_->RemoveLogin(form))
changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
@@ -194,43 +194,43 @@ PasswordStoreDefault::FillLoginsForSameOrganizationName(
bool PasswordStoreDefault::FillAutofillableLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
return login_db_ && login_db_->GetAutofillableLogins(forms);
}
bool PasswordStoreDefault::FillBlacklistLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
return login_db_ && login_db_->GetBlacklistLogins(forms);
}
void PasswordStoreDefault::AddSiteStatsImpl(const InteractionsStats& stats) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
if (login_db_)
login_db_->stats_table().AddRow(stats);
}
void PasswordStoreDefault::RemoveSiteStatsImpl(const GURL& origin_domain) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
if (login_db_)
login_db_->stats_table().RemoveRow(origin_domain);
}
std::vector<InteractionsStats> PasswordStoreDefault::GetAllSiteStatsImpl() {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
return login_db_ ? login_db_->stats_table().GetAllRows()
: std::vector<InteractionsStats>();
}
std::vector<InteractionsStats> PasswordStoreDefault::GetSiteStatsImpl(
const GURL& origin_domain) {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
return login_db_ ? login_db_->stats_table().GetRows(origin_domain)
: std::vector<InteractionsStats>();
}
void PasswordStoreDefault::ResetLoginDB() {
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
+ DCHECK(GetBackgroundTaskRunner()->RunsTasksInCurrentSequence());
login_db_.reset();
}
diff --git a/chromium/components/password_manager/core/browser/password_store_default.h b/chromium/components/password_manager/core/browser/password_store_default.h
index 0c7faebef2d..4fd557081f9 100644
--- a/chromium/components/password_manager/core/browser/password_store_default.h
+++ b/chromium/components/password_manager/core/browser/password_store_default.h
@@ -10,7 +10,7 @@
#include <vector>
#include "base/macros.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
#include "components/password_manager/core/browser/login_database.h"
#include "components/password_manager/core/browser/password_store.h"
@@ -25,8 +25,8 @@ class PasswordStoreDefault : public PasswordStore {
// The |login_db| must not have been Init()-ed yet. It will be initialized in
// a deferred manner on the DB thread.
PasswordStoreDefault(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
+ scoped_refptr<base::SequencedTaskRunner> main_thread_runner,
+ scoped_refptr<base::SequencedTaskRunner> db_thread_runner,
std::unique_ptr<LoginDatabase> login_db);
bool Init(const syncer::SyncableService::StartSyncFlare& flare,
@@ -34,7 +34,7 @@ class PasswordStoreDefault : public PasswordStore {
void ShutdownOnUIThread() override;
- // To be used only for testing.
+ // To be used only for testing or in subclasses.
LoginDatabase* login_db() const { return login_db_.get(); }
protected:
@@ -86,10 +86,6 @@ class PasswordStoreDefault : public PasswordStore {
return login_db_->DeleteAndRecreateDatabaseFile();
}
- void set_login_db(std::unique_ptr<password_manager::LoginDatabase> login_db) {
- login_db_.swap(login_db);
- }
-
private:
// Resets |login_db_| on the background thread.
void ResetLoginDB();
diff --git a/chromium/components/password_manager/core/browser/password_store_default_unittest.cc b/chromium/components/password_manager/core/browser/password_store_default_unittest.cc
index cc2e8afd92e..8a7374a965a 100644
--- a/chromium/components/password_manager/core/browser/password_store_default_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_default_unittest.cc
@@ -16,7 +16,7 @@
#include "base/strings/string_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 "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "components/password_manager/core/browser/login_database.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
@@ -143,8 +143,8 @@ scoped_refptr<PasswordStoreDefault>
PasswordStoreDefaultTestDelegate::CreateInitializedStore(
std::unique_ptr<LoginDatabase> database) {
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
- std::move(database)));
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(), std::move(database)));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
return store;
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 f4c93150afc..6f19620433d 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
@@ -7,9 +7,10 @@
#include <utility>
#include "base/memory/ptr_util.h"
-#include "components/password_manager/core/browser/affiliated_match_helper.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
+#include "base/task_scheduler/post_task.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+#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"
@@ -28,13 +29,17 @@ bool ShouldAffiliationBasedMatchingBeActive(syncer::SyncService* sync_service) {
void ActivateAffiliationBasedMatching(
PasswordStore* password_store,
net::URLRequestContextGetter* request_context_getter,
- const base::FilePath& db_path,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner) {
+ const base::FilePath& db_path) {
// The PasswordStore is so far the only consumer of the AffiliationService,
// therefore the service is owned by the AffiliatedMatchHelper, which in
// turn is owned by the PasswordStore.
+ // Task priority is USER_VISIBLE, because AffiliationService-related tasks
+ // block obtaining credentials from PasswordStore, which matches the
+ // USER_VISIBLE example: "Loading data that might be shown in the UI after a
+ // future user interaction."
std::unique_ptr<AffiliationService> affiliation_service(
- new AffiliationService(db_thread_runner));
+ new AffiliationService(base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE})));
affiliation_service->Initialize(request_context_getter, db_path);
std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper(
new AffiliatedMatchHelper(password_store,
@@ -56,8 +61,7 @@ void ToggleAffiliationBasedMatchingBasedOnPasswordSyncedState(
PasswordStore* password_store,
syncer::SyncService* sync_service,
net::URLRequestContextGetter* request_context_getter,
- const base::FilePath& profile_path,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner) {
+ const base::FilePath& profile_path) {
DCHECK(password_store);
const bool matching_should_be_active =
@@ -67,26 +71,13 @@ void ToggleAffiliationBasedMatchingBasedOnPasswordSyncedState(
if (matching_should_be_active && !matching_is_active) {
ActivateAffiliationBasedMatching(password_store, request_context_getter,
- GetAffiliationDatabasePath(profile_path),
- db_thread_runner);
+ GetAffiliationDatabasePath(profile_path));
} else if (!matching_should_be_active && matching_is_active) {
password_store->SetAffiliatedMatchHelper(
base::WrapUnique<AffiliatedMatchHelper>(nullptr));
}
}
-void TrimOrDeleteAffiliationCacheForStoreAndPath(
- PasswordStore* password_store,
- const base::FilePath& profile_path,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner) {
- if (password_store && password_store->affiliated_match_helper()) {
- password_store->TrimAffiliationCache();
- } else {
- AffiliationService::DeleteCache(GetAffiliationDatabasePath(profile_path),
- db_thread_runner.get());
- }
-}
-
std::unique_ptr<LoginDatabase> CreateLoginDatabase(
const base::FilePath& profile_path) {
base::FilePath login_db_file_path = profile_path.Append(kLoginDataFileName);
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 bb9371a8980..b3f7a1c4dea 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
@@ -9,7 +9,6 @@
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/password_manager/core/browser/login_database.h"
@@ -22,25 +21,14 @@ 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 |db_thread_runner| as its backend
-// thread, and |request_context_getter| to fetch affiliation information. This
-// function should be called whenever there is a possibility that syncing
-// passwords has just started or ended.
+// therein. The AffiliationService will use |request_context_getter| 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,
- const base::FilePath& profile_path,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner);
-
-// Trims the affiliation cache (placed in |profile_path|) for |password_store|
-// if affiliation-based matching is enabled, and completely deletes it
-// otherwise. The AffiliationService will use |db_thread_runner| as its backend
-// thread.
-void TrimOrDeleteAffiliationCacheForStoreAndPath(
- PasswordStore* password_store,
- const base::FilePath& profile_path,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner);
+ const base::FilePath& profile_path);
// Creates a LoginDatabase. Looks in |profile_path| for the database file.
// Does not call LoginDatabase::Init() -- to avoid UI jank, that needs to be
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
new file mode 100644
index 00000000000..e874af1d8e1
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_store_signin_notifier.cc
@@ -0,0 +1,30 @@
+// 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/password_store_signin_notifier.h"
+
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_store.h"
+
+namespace password_manager {
+
+PasswordStoreSigninNotifier::PasswordStoreSigninNotifier() {}
+
+PasswordStoreSigninNotifier::~PasswordStoreSigninNotifier() {}
+
+void PasswordStoreSigninNotifier::NotifySignin(const std::string& password) {
+ metrics_util::LogSyncPasswordHashChange(
+ metrics_util::SyncPasswordHashChange::SAVED_ON_CHROME_SIGNIN);
+ if (store_)
+ store_->SaveSyncPasswordHash(base::UTF8ToUTF16(password));
+}
+
+void PasswordStoreSigninNotifier::NotifySignedOut() {
+ metrics_util::LogSyncPasswordHashChange(
+ metrics_util::SyncPasswordHashChange::CLEARED_ON_CHROME_SIGNOUT);
+ if (store_)
+ store_->ClearSyncPasswordHash();
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store_signin_notifier.h b/chromium/components/password_manager/core/browser/password_store_signin_notifier.h
new file mode 100644
index 00000000000..65e8c89b9d2
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_store_signin_notifier.h
@@ -0,0 +1,47 @@
+// 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_PASSWORD_STORE_SIGNIN_NOTIFIER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_STORE_SIGNIN_NOTIFIER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace password_manager {
+
+class PasswordStore;
+
+// Abstract class for notifying PasswordStore about Chrome sign-in events.
+// The logic of receiving sign-in events and notifying PasswordStore is split
+// in the base abstract class (this class, in components/) and an
+// implementation (in the chrome/browser/), because components/ doesn't know
+// anything about Chrome sign-in.
+class PasswordStoreSigninNotifier {
+ public:
+ PasswordStoreSigninNotifier();
+ virtual ~PasswordStoreSigninNotifier();
+
+ virtual void SubscribeToSigninEvents(PasswordStore* store) = 0;
+ virtual void UnsubscribeFromSigninEvents() = 0;
+
+ protected:
+ void set_store(PasswordStore* store) { store_ = store; }
+
+ // Passes sign-in to |store_|.
+ void NotifySignin(const std::string& password);
+
+ // Passes signed-out to |store_|.
+ void NotifySignedOut();
+
+ private:
+ PasswordStore* store_ = nullptr; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordStoreSigninNotifier);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_STORE_SIGNIN_NOTIFIER_H_
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 53835261704..3cf24b2b7eb 100644
--- a/chromium/components/password_manager/core/browser/password_store_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_unittest.cc
@@ -21,22 +21,24 @@
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
-#include "components/password_manager/core/browser/affiliated_match_helper.h"
-#include "components/password_manager/core/browser/affiliation_service.h"
-#include "components/password_manager/core/browser/mock_affiliated_match_helper.h"
+#include "components/os_crypt/os_crypt_mocker.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
+#include "components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/password_store_default.h"
+#include "components/password_manager/core/browser/password_store_signin_notifier.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(OS_MACOSX)
-#include "components/os_crypt/os_crypt_mocker.h"
-#endif
-
using autofill::PasswordForm;
using base::WaitableEvent;
using testing::_;
@@ -74,6 +76,10 @@ constexpr const char kTestAndroidRealm3[] =
"android://hash@com.example.three.android/";
constexpr const char kTestUnrelatedAndroidRealm[] =
"android://hash@com.notexample.android/";
+constexpr const char kTestAndroidName1[] = "Example Android App 1";
+constexpr const char kTestAndroidIconURL1[] = "https://example.com/icon_1.png";
+constexpr const char kTestAndroidName2[] = "Example Android App 2";
+constexpr const char kTestAndroidIconURL2[] = "https://example.com/icon_2.png";
class MockPasswordStoreConsumer : public PasswordStoreConsumer {
public:
@@ -87,6 +93,12 @@ class MockPasswordStoreConsumer : public PasswordStoreConsumer {
}
};
+class MockPasswordStoreSigninNotifier : public PasswordStoreSigninNotifier {
+ public:
+ MOCK_METHOD1(SubscribeToSigninEvents, void(PasswordStore* store));
+ MOCK_METHOD0(UnsubscribeFromSigninEvents, void());
+};
+
class StartSyncFlareMock {
public:
StartSyncFlareMock() {}
@@ -103,9 +115,17 @@ class PasswordStoreTest : public testing::Test {
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
- void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ // Mock OSCrypt. There is a call to OSCrypt on initializling
+ // PasswordReuseDetector, so it should be mocked.
+ OSCryptMocker::SetUpWithSingleton();
+ }
- void TearDown() override { ASSERT_TRUE(temp_dir_.Delete()); }
+ void TearDown() override {
+ ASSERT_TRUE(temp_dir_.Delete());
+ OSCryptMocker::TearDown();
+ }
base::FilePath test_login_db_file_path() const {
return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("login_test"));
@@ -117,7 +137,8 @@ class PasswordStoreTest : public testing::Test {
TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) {
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -211,7 +232,8 @@ TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) {
TEST_F(PasswordStoreTest, StartSyncFlare) {
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
StartSyncFlareMock mock;
store->Init(
@@ -241,7 +263,8 @@ TEST_F(PasswordStoreTest, GetLoginImpl) {
/* clang-format on */
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -304,7 +327,8 @@ TEST_F(PasswordStoreTest, UpdateLoginPrimaryKeyFields) {
/* clang-format on */
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -357,7 +381,8 @@ TEST_F(PasswordStoreTest, RemoveLoginsCreatedBetweenCallbackIsCalled) {
/* clang-format on */
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::WrapUnique(new LoginDatabase(test_login_db_file_path()))));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -410,7 +435,8 @@ TEST_F(PasswordStoreTest, GetLoginsWithoutAffiliations) {
/* clang-format on */
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::WrapUnique(new LoginDatabase(test_login_db_file_path()))));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -515,7 +541,8 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliations) {
/* clang-format on */
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::WrapUnique(new LoginDatabase(test_login_db_file_path()))));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -698,8 +725,8 @@ TEST_F(PasswordStoreTest, MAYBE_UpdatePasswordsStoredForAffiliatedWebsites) {
<< test_remove_and_add_login);
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::WrapUnique(new LoginDatabase(test_login_db_file_path()))));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
store->RemoveLoginsCreatedBetween(base::Time(), base::Time::Max(),
@@ -783,76 +810,75 @@ TEST_F(PasswordStoreTest, MAYBE_UpdatePasswordsStoredForAffiliatedWebsites) {
}
}
-TEST_F(PasswordStoreTest, GetLoginsWithAffiliatedRealms) {
- /* clang-format off */
+TEST_F(PasswordStoreTest, GetLoginsWithAffiliationAndBrandingInformation) {
static const PasswordFormData kTestCredentials[] = {
- {PasswordForm::SCHEME_HTML,
- kTestAndroidRealm1,
- "", "", L"", L"", L"",
- L"username_value_1",
- L"", true, 1},
- {PasswordForm::SCHEME_HTML,
- kTestAndroidRealm2,
- "", "", L"", L"", L"",
- L"username_value_2",
- L"", true, 1},
- {PasswordForm::SCHEME_HTML,
- kTestAndroidRealm3,
- "", "", L"", L"", L"",
- L"username_value_3",
- L"", true, 1}};
- /* clang-format on */
+ {PasswordForm::SCHEME_HTML, kTestAndroidRealm1, "", "", L"", L"", L"",
+ L"username_value_1", L"", true, 1},
+ {PasswordForm::SCHEME_HTML, kTestAndroidRealm2, "", "", L"", L"", L"",
+ L"username_value_2", L"", true, 1},
+ {PasswordForm::SCHEME_HTML, kTestAndroidRealm3, "", "", L"", L"", L"",
+ L"username_value_3", L"", true, 1},
+ {PasswordForm::SCHEME_HTML, kTestWebRealm1, kTestWebOrigin1, "", L"", L"",
+ L"", L"username_value_4", L"", true, 1}};
- const bool kFalseTrue[] = {false, true};
- for (bool blacklisted : kFalseTrue) {
+ for (bool blacklisted : {false, true}) {
SCOPED_TRACE(testing::Message("use blacklisted logins: ") << blacklisted);
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
store->RemoveLoginsCreatedBetween(base::Time(), base::Time::Max(),
base::Closure());
std::vector<std::unique_ptr<PasswordForm>> all_credentials;
- for (size_t i = 0; i < arraysize(kTestCredentials); ++i) {
+ for (const auto& test_credential : kTestCredentials) {
all_credentials.push_back(
- CreatePasswordFormFromDataForTesting(kTestCredentials[i]));
- if (blacklisted)
- all_credentials.back()->blacklisted_by_user = true;
+ CreatePasswordFormFromDataForTesting(test_credential));
+ all_credentials.back()->blacklisted_by_user = blacklisted;
store->AddLogin(*all_credentials.back());
base::RunLoop().RunUntilIdle();
}
MockPasswordStoreConsumer mock_consumer;
std::vector<std::unique_ptr<PasswordForm>> expected_results;
- for (size_t i = 0; i < arraysize(kTestCredentials); ++i) {
- expected_results.push_back(
- base::MakeUnique<PasswordForm>(*all_credentials[i]));
+ for (const auto& credential : all_credentials)
+ expected_results.push_back(base::MakeUnique<PasswordForm>(*credential));
+
+ std::vector<MockAffiliatedMatchHelper::AffiliationAndBrandingInformation>
+ affiliation_info_for_results = {
+ {kTestWebRealm1, kTestAndroidName1, GURL(kTestAndroidIconURL1)},
+ {kTestWebRealm2, kTestAndroidName2, GURL(kTestAndroidIconURL2)},
+ {/* Pretend affiliation or branding info is unavailable. */},
+ {/* Pretend affiliation or branding info is unavailable. */}};
+
+ auto mock_helper = base::MakeUnique<MockAffiliatedMatchHelper>();
+ mock_helper->ExpectCallToInjectAffiliationAndBrandingInformation(
+ affiliation_info_for_results);
+ store->SetAffiliatedMatchHelper(std::move(mock_helper));
+ for (size_t i = 0; i < expected_results.size(); ++i) {
+ expected_results[i]->affiliated_web_realm =
+ affiliation_info_for_results[i].affiliated_web_realm;
+ expected_results[i]->app_display_name =
+ affiliation_info_for_results[i].app_display_name;
+ expected_results[i]->app_icon_url =
+ affiliation_info_for_results[i].app_icon_url;
}
- MockAffiliatedMatchHelper* mock_helper = new MockAffiliatedMatchHelper;
- store->SetAffiliatedMatchHelper(base::WrapUnique(mock_helper));
-
- std::vector<std::string> affiliated_web_realms;
- affiliated_web_realms.push_back(kTestWebRealm1);
- affiliated_web_realms.push_back(kTestWebRealm2);
- affiliated_web_realms.push_back(std::string());
- mock_helper->ExpectCallToInjectAffiliatedWebRealms(affiliated_web_realms);
- for (size_t i = 0; i < expected_results.size(); ++i)
- expected_results[i]->affiliated_web_realm = affiliated_web_realms[i];
-
EXPECT_CALL(mock_consumer,
OnGetPasswordStoreResultsConstRef(
UnorderedPasswordFormElementsAre(&expected_results)));
- if (blacklisted)
- store->GetBlacklistLoginsWithAffiliatedRealms(&mock_consumer);
- else
- store->GetAutofillableLoginsWithAffiliatedRealms(&mock_consumer);
-
- // Since GetAutofillableLoginsWithAffiliatedRealms schedules a request for
- // affiliated realms to UI thread, don't shutdown UI thread until there are
- // no tasks in the UI queue.
+ if (blacklisted) {
+ store->GetBlacklistLoginsWithAffiliationAndBrandingInformation(
+ &mock_consumer);
+ } else {
+ store->GetAutofillableLoginsWithAffiliationAndBrandingInformation(
+ &mock_consumer);
+ }
+
+ // Since GetAutofillableLoginsWithAffiliationAndBrandingInformation
+ // schedules a request for affiliation information to UI thread, don't
+ // shutdown UI thread until there are no tasks in the UI queue.
base::RunLoop().RunUntilIdle();
store->ShutdownOnUIThread();
base::RunLoop().RunUntilIdle();
@@ -889,7 +915,8 @@ TEST_F(PasswordStoreTest, GetLoginsForSameOrganizationName) {
L"username_value_6", L"", true, 1}};
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -923,13 +950,9 @@ TEST_F(PasswordStoreTest, CheckPasswordReuse) {
{PasswordForm::SCHEME_HTML, "https://facebook.com",
"https://facebook.com", "", L"", L"", L"", L"", L"topsecret", true, 1}};
-#if defined(OS_MACOSX)
- // Mock Keychain. There is a call to Keychain on initializling
- // PasswordReuseDetector, so it should be mocked.
- OSCryptMocker::SetUpWithSingleton();
-#endif
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
- base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
@@ -967,9 +990,71 @@ TEST_F(PasswordStoreTest, CheckPasswordReuse) {
store->ShutdownOnUIThread();
base::RunLoop().RunUntilIdle();
-#if defined(OS_MACOSX)
- OSCryptMocker::TearDown();
+}
#endif
+
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+TEST_F(PasswordStoreTest, SavingClearingSyncPassword) {
+ scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
+
+ TestingPrefServiceSimple prefs;
+ prefs.registry()->RegisterStringPref(prefs::kSyncPasswordHash, std::string(),
+ PrefRegistry::NO_REGISTRATION_FLAGS);
+ prefs.registry()->RegisterStringPref(prefs::kSyncPasswordLengthAndHashSalt,
+ std::string(),
+ PrefRegistry::NO_REGISTRATION_FLAGS);
+ ASSERT_FALSE(prefs.HasPrefPath(prefs::kSyncPasswordHash));
+ store->Init(syncer::SyncableService::StartSyncFlare(), &prefs);
+
+ const base::string16 sync_password = base::ASCIIToUTF16("password");
+ const base::string16 input = base::ASCIIToUTF16("123password");
+ store->SaveSyncPasswordHash(sync_password);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(prefs.HasPrefPath(prefs::kSyncPasswordHash));
+
+ // Check that sync password reuse is found.
+ MockPasswordReuseDetectorConsumer mock_consumer;
+ EXPECT_CALL(
+ mock_consumer,
+ OnReuseFound(sync_password, std::string(kSyncPasswordDomain), 1, 0));
+ store->CheckReuse(input, "https://facebook.com", &mock_consumer);
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(&mock_consumer);
+
+ // Check that no sync password reuse is found after clearing the saved sync
+ // password hash.
+ store->ClearSyncPasswordHash();
+ EXPECT_FALSE(prefs.HasPrefPath(prefs::kSyncPasswordHash));
+ EXPECT_CALL(mock_consumer, OnReuseFound(_, _, _, _)).Times(0);
+ store->CheckReuse(input, "https://facebook.com", &mock_consumer);
+ base::RunLoop().RunUntilIdle();
+
+ store->ShutdownOnUIThread();
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(PasswordStoreTest, SubscriptionAndUnsubscriptionFromSignInEvents) {
+ scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get(),
+ base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
+
+ std::unique_ptr<MockPasswordStoreSigninNotifier> notifier =
+ base::MakeUnique<MockPasswordStoreSigninNotifier>();
+ MockPasswordStoreSigninNotifier* notifier_weak = notifier.get();
+
+ // Check that |store| is subscribed to sign-in events.
+ EXPECT_CALL(*notifier_weak, SubscribeToSigninEvents(store.get()));
+ store->SetPasswordStoreSigninNotifier(std::move(notifier));
+ testing::Mock::VerifyAndClearExpectations(store.get());
+
+ // Check that |store| is unsubscribed from sign-in events on shutdown.
+ EXPECT_CALL(*notifier_weak, UnsubscribeFromSigninEvents());
+ store->ShutdownOnUIThread();
+ base::RunLoop().RunUntilIdle();
}
#endif
diff --git a/chromium/components/password_manager/core/browser/password_syncable_service.cc b/chromium/components/password_manager/core/browser/password_syncable_service.cc
index 03746ff6a06..6584b88d194 100644
--- a/chromium/components/password_manager/core/browser/password_syncable_service.cc
+++ b/chromium/components/password_manager/core/browser/password_syncable_service.cc
@@ -12,8 +12,12 @@
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/time/default_clock.h"
#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_store_sync.h"
#include "components/sync/model/sync_error_factory.h"
@@ -36,20 +40,12 @@ std::string MakePasswordSyncTag(const autofill::PasswordForm& password);
namespace {
// Returns true iff |password_specifics| and |password_specifics| are equal
-// memberwise.
-bool AreLocalAndSyncPasswordsEqual(
+// in the fields which don't comprise the sync tag.
+bool AreLocalAndSyncPasswordsNonSyncTagEqual(
const sync_pb::PasswordSpecificsData& password_specifics,
const autofill::PasswordForm& password_form) {
return (password_form.scheme == password_specifics.scheme() &&
- password_form.signon_realm == password_specifics.signon_realm() &&
- password_form.origin.spec() == password_specifics.origin() &&
password_form.action.spec() == password_specifics.action() &&
- base::UTF16ToUTF8(password_form.username_element) ==
- password_specifics.username_element() &&
- base::UTF16ToUTF8(password_form.password_element) ==
- password_specifics.password_element() &&
- base::UTF16ToUTF8(password_form.username_value) ==
- password_specifics.username_value() &&
base::UTF16ToUTF8(password_form.password_value) ==
password_specifics.password_value() &&
password_form.preferred == password_specifics.preferred() &&
@@ -66,6 +62,38 @@ bool AreLocalAndSyncPasswordsEqual(
password_form.federation_origin.Serialize());
}
+// Returns true iff |password_specifics| and |password_specifics| are equal
+// memberwise.
+bool AreLocalAndSyncPasswordsEqual(
+ const sync_pb::PasswordSpecificsData& password_specifics,
+ const autofill::PasswordForm& password_form) {
+ return (password_form.signon_realm == password_specifics.signon_realm() &&
+ password_form.origin.spec() == password_specifics.origin() &&
+ base::UTF16ToUTF8(password_form.username_element) ==
+ password_specifics.username_element() &&
+ base::UTF16ToUTF8(password_form.password_element) ==
+ password_specifics.password_element() &&
+ base::UTF16ToUTF8(password_form.username_value) ==
+ password_specifics.username_value() &&
+ AreLocalAndSyncPasswordsNonSyncTagEqual(password_specifics,
+ password_form));
+}
+
+// Compares the fields which are not part of the sync tag.
+bool AreNonSyncTagFieldsEqual(const sync_pb::PasswordSpecificsData& left,
+ const sync_pb::PasswordSpecificsData& right) {
+ return (left.scheme() == right.scheme() && left.action() == right.action() &&
+ left.password_value() == right.password_value() &&
+ left.preferred() == right.preferred() &&
+ left.date_created() == right.date_created() &&
+ left.blacklisted() == right.blacklisted() &&
+ left.type() == right.type() &&
+ left.times_used() == right.times_used() &&
+ left.display_name() == right.display_name() &&
+ left.avatar_url() == right.avatar_url() &&
+ left.federation_url() == right.federation_url());
+}
+
syncer::SyncChange::SyncChangeType GetSyncChangeType(
PasswordStoreChange::Type type) {
switch (type) {
@@ -91,17 +119,188 @@ void AppendPasswordFromSpecifics(
entries->back()->date_synced = sync_time;
}
-bool IsEmptyPasswordForm(const autofill::PasswordForm& form) {
- return (form.username_value.empty() &&
- form.password_value.empty() &&
- !form.blacklisted_by_user);
+// Android autofill saves credential in a different format without trailing '/'.
+std::string GetIncorrectAndroidSignonRealm(std::string android_autofill_realm) {
+ if (base::EndsWith(android_autofill_realm, "/", base::CompareCase::SENSITIVE))
+ android_autofill_realm.erase(android_autofill_realm.size() - 1);
+ return android_autofill_realm;
+}
+
+// The correct Android signon_realm should have a trailing '/'.
+std::string GetCorrectAndroidSignonRealm(std::string android_realm) {
+ if (!base::EndsWith(android_realm, "/", base::CompareCase::SENSITIVE))
+ android_realm += '/';
+ return android_realm;
+}
+
+// Android autofill saves credentials in a different format without trailing
+// '/'. Return a sync tag for the style used by Android Autofill in GMS Core
+// v12.
+std::string AndroidAutofillSyncTag(
+ const sync_pb::PasswordSpecificsData& password) {
+ // realm has the same value as the origin.
+ std::string origin = GetIncorrectAndroidSignonRealm(password.origin());
+ std::string signon_realm =
+ GetIncorrectAndroidSignonRealm(password.signon_realm());
+ return (net::EscapePath(origin) + "|" +
+ net::EscapePath(password.username_element()) + "|" +
+ net::EscapePath(password.username_value()) + "|" +
+ net::EscapePath(password.password_element()) + "|" +
+ net::EscapePath(signon_realm));
+}
+
+// Return a sync tag for the correct format used by Android.
+std::string AndroidCorrectSyncTag(
+ const sync_pb::PasswordSpecificsData& password) {
+ // realm has the same value as the origin.
+ std::string origin = GetCorrectAndroidSignonRealm(password.origin());
+ std::string signon_realm =
+ GetCorrectAndroidSignonRealm(password.signon_realm());
+ return (net::EscapePath(origin) + "|" +
+ net::EscapePath(password.username_element()) + "|" +
+ net::EscapePath(password.username_value()) + "|" +
+ net::EscapePath(password.password_element()) + "|" +
+ net::EscapePath(signon_realm));
+}
+
+void PasswordSpecificsFromPassword(
+ const autofill::PasswordForm& password_form,
+ sync_pb::PasswordSpecificsData* password_specifics) {
+#define CopyField(field) password_specifics->set_##field(password_form.field)
+#define CopyStringField(field) \
+ password_specifics->set_##field(base::UTF16ToUTF8(password_form.field))
+ CopyField(scheme);
+ CopyField(signon_realm);
+ password_specifics->set_origin(password_form.origin.spec());
+ password_specifics->set_action(password_form.action.spec());
+ CopyStringField(username_element);
+ CopyStringField(password_element);
+ CopyStringField(username_value);
+ CopyStringField(password_value);
+ CopyField(preferred);
+ password_specifics->set_date_created(
+ password_form.date_created.ToInternalValue());
+ password_specifics->set_blacklisted(password_form.blacklisted_by_user);
+ CopyField(type);
+ CopyField(times_used);
+ CopyStringField(display_name);
+ password_specifics->set_avatar_url(password_form.icon_url.spec());
+ password_specifics->set_federation_url(
+ password_form.federation_origin.unique()
+ ? std::string()
+ : password_form.federation_origin.Serialize());
+#undef CopyStringField
+#undef CopyField
}
-bool IsEmptyPasswordSpecificsData(
- const sync_pb::PasswordSpecificsData& specifics) {
- return (specifics.username_value().empty() &&
- specifics.password_value().empty() &&
- !specifics.blacklisted());
+struct AndroidMergeResult {
+ // New value for Android entry in the correct format.
+ base::Optional<syncer::SyncData> new_android_correct;
+ // New value for Android autofill entry.
+ base::Optional<syncer::SyncData> new_android_incorrect;
+ // New value for local entry in the correct format.
+ base::Optional<autofill::PasswordForm> new_local_correct;
+ // New value for local entry in the Android autofill format.
+ base::Optional<autofill::PasswordForm> new_local_incorrect;
+};
+
+// Perform deduplication of Android credentials saved in the wrong format. As
+// the result all the four entries should be created and have the same value.
+AndroidMergeResult Perform4WayMergeAndroidCredentials(
+ const sync_pb::PasswordSpecificsData* correct_android,
+ const sync_pb::PasswordSpecificsData* incorrect_android,
+ const autofill::PasswordForm* correct_local,
+ const autofill::PasswordForm* incorrect_local) {
+ AndroidMergeResult result;
+
+ base::Optional<sync_pb::PasswordSpecificsData> local_correct_ps;
+ if (correct_local) {
+ local_correct_ps = sync_pb::PasswordSpecificsData();
+ PasswordSpecificsFromPassword(*correct_local, &local_correct_ps.value());
+ }
+
+ base::Optional<sync_pb::PasswordSpecificsData> local_incorrect_ps;
+ if (incorrect_local) {
+ local_incorrect_ps = sync_pb::PasswordSpecificsData();
+ PasswordSpecificsFromPassword(*incorrect_local,
+ &local_incorrect_ps.value());
+ }
+
+ const sync_pb::PasswordSpecificsData* all_data[4] = {
+ correct_android, incorrect_android,
+ local_correct_ps ? &local_correct_ps.value() : nullptr,
+ local_incorrect_ps ? &local_incorrect_ps.value() : nullptr};
+
+ // |newest_data| will point to the newest entry out of all 4.
+ const sync_pb::PasswordSpecificsData* newest_data = nullptr;
+ for (int i = 0; i < 4; ++i) {
+ if (newest_data && all_data[i]) {
+ if (all_data[i]->date_created() > newest_data->date_created())
+ newest_data = all_data[i];
+ } else if (all_data[i]) {
+ newest_data = all_data[i];
+ }
+ }
+ DCHECK(newest_data);
+
+ const std::string correct_tag = AndroidCorrectSyncTag(*newest_data);
+ const std::string incorrect_tag = AndroidAutofillSyncTag(*newest_data);
+ const std::string correct_signon_realm =
+ GetCorrectAndroidSignonRealm(newest_data->signon_realm());
+ const std::string incorrect_signon_realm =
+ GetIncorrectAndroidSignonRealm(newest_data->signon_realm());
+ const std::string correct_origin =
+ GetCorrectAndroidSignonRealm(newest_data->origin());
+ const std::string incorrect_origin =
+ GetIncorrectAndroidSignonRealm(newest_data->origin());
+ DCHECK_EQ(GURL(incorrect_origin).spec(), incorrect_origin);
+
+ // Set the correct Sync entry if needed.
+ if (newest_data != correct_android &&
+ (!correct_android ||
+ !AreNonSyncTagFieldsEqual(*correct_android, *newest_data))) {
+ sync_pb::EntitySpecifics password_data;
+ sync_pb::PasswordSpecificsData* password_specifics =
+ password_data.mutable_password()->mutable_client_only_encrypted_data();
+ *password_specifics = *newest_data;
+ password_specifics->set_origin(correct_origin);
+ password_specifics->set_signon_realm(correct_signon_realm);
+ result.new_android_correct = syncer::SyncData::CreateLocalData(
+ correct_tag, correct_tag, password_data);
+ }
+
+ // Set the Andoroid Autofill Sync entry if needed.
+ if (newest_data != incorrect_android &&
+ (!incorrect_android ||
+ !AreNonSyncTagFieldsEqual(*incorrect_android, *newest_data))) {
+ sync_pb::EntitySpecifics password_data;
+ sync_pb::PasswordSpecificsData* password_specifics =
+ password_data.mutable_password()->mutable_client_only_encrypted_data();
+ *password_specifics = *newest_data;
+ password_specifics->set_origin(incorrect_origin);
+ password_specifics->set_signon_realm(incorrect_signon_realm);
+ result.new_android_incorrect = syncer::SyncData::CreateLocalData(
+ incorrect_tag, incorrect_tag, password_data);
+ }
+
+ // Set the correct local entry if needed.
+ if (!local_correct_ps ||
+ (newest_data != &local_correct_ps.value() &&
+ !AreNonSyncTagFieldsEqual(local_correct_ps.value(), *newest_data))) {
+ result.new_local_correct = PasswordFromSpecifics(*newest_data);
+ result.new_local_correct.value().origin = GURL(correct_origin);
+ result.new_local_correct.value().signon_realm = correct_signon_realm;
+ }
+
+ // Set the incorrect local entry if needed.
+ if (!local_incorrect_ps ||
+ (newest_data != &local_incorrect_ps.value() &&
+ !AreNonSyncTagFieldsEqual(local_incorrect_ps.value(), *newest_data))) {
+ result.new_local_incorrect = PasswordFromSpecifics(*newest_data);
+ result.new_local_incorrect.value().origin = GURL(incorrect_origin);
+ result.new_local_incorrect.value().signon_realm = incorrect_signon_realm;
+ }
+ return result;
}
} // namespace
@@ -137,10 +336,12 @@ struct PasswordSyncableService::SyncEntries {
PasswordSyncableService::PasswordSyncableService(
PasswordStoreSync* password_store)
- : password_store_(password_store), is_processing_sync_changes_(false) {
-}
+ : password_store_(password_store),
+ clock_(new base::DefaultClock),
+ is_processing_sync_changes_(false) {}
PasswordSyncableService::~PasswordSyncableService() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
@@ -148,7 +349,7 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
const syncer::SyncDataList& initial_sync_data,
std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(syncer::PASSWORDS, type);
base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
syncer::SyncMergeResult merge_result(type);
@@ -175,33 +376,18 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
}
merge_result.set_num_items_before_association(new_local_entries.size());
+ // Changes from Sync to be applied locally.
SyncEntries sync_entries;
// Changes from password db that need to be propagated to sync.
syncer::SyncChangeList updated_db_entries;
- for (syncer::SyncDataList::const_iterator sync_iter =
- initial_sync_data.begin();
- sync_iter != initial_sync_data.end(); ++sync_iter) {
- CreateOrUpdateEntry(*sync_iter,
- &new_local_entries,
- &sync_entries,
- &updated_db_entries);
- }
+ MergeSyncDataWithLocalData(initial_sync_data, &new_local_entries,
+ &sync_entries, &updated_db_entries);
for (PasswordEntryMap::iterator it = new_local_entries.begin();
it != new_local_entries.end(); ++it) {
- if (IsEmptyPasswordForm(*it->second)) {
- // http://crbug.com/404012. Remove the empty password from the local db.
- // This code can be removed once all users have their password store
- // cleaned up. This should happen in M43 and can be verified using crash
- // reports.
- sync_entries.deleted_entries.push_back(
- base::MakeUnique<autofill::PasswordForm>(*it->second));
- } else {
- updated_db_entries.push_back(
- syncer::SyncChange(FROM_HERE,
- syncer::SyncChange::ACTION_ADD,
- SyncDataFromPassword(*it->second)));
- }
+ updated_db_entries.push_back(
+ syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD,
+ SyncDataFromPassword(*it->second)));
}
WriteToPasswordStore(sync_entries);
@@ -229,7 +415,7 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
}
void PasswordSyncableService::StopSyncing(syncer::ModelType type) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(syncer::PASSWORDS, type);
sync_processor_.reset();
@@ -238,7 +424,7 @@ void PasswordSyncableService::StopSyncing(syncer::ModelType type) {
syncer::SyncDataList PasswordSyncableService::GetAllSyncData(
syncer::ModelType type) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(syncer::PASSWORDS, type);
std::vector<std::unique_ptr<autofill::PasswordForm>> password_entries;
ReadFromPasswordStore(&password_entries, nullptr);
@@ -256,10 +442,10 @@ syncer::SyncDataList PasswordSyncableService::GetAllSyncData(
syncer::SyncError PasswordSyncableService::ProcessSyncChanges(
const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
SyncEntries sync_entries;
- base::Time time_now = base::Time::Now();
+ base::Time time_now = clock_->Now();
for (syncer::SyncChangeList::const_iterator it = change_list.begin();
it != change_list.end(); ++it) {
@@ -272,6 +458,13 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges(
}
AppendPasswordFromSpecifics(
specifics.password().client_only_encrypted_data(), time_now, entries);
+ if (IsValidAndroidFacetURI(entries->back()->signon_realm)) {
+ // Fix the Android Autofill credentials if needed.
+ entries->back()->signon_realm =
+ GetCorrectAndroidSignonRealm(entries->back()->signon_realm);
+ entries->back()->origin =
+ GURL(GetCorrectAndroidSignonRealm(entries->back()->origin.spec()));
+ }
}
WriteToPasswordStore(sync_entries);
@@ -280,7 +473,7 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges(
void PasswordSyncableService::ActOnPasswordStoreChanges(
const PasswordStoreChangeList& local_changes) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!sync_processor_) {
if (!flare_.is_null()) {
@@ -309,7 +502,7 @@ void PasswordSyncableService::ActOnPasswordStoreChanges(
void PasswordSyncableService::InjectStartSyncFlare(
const syncer::SyncableService::StartSyncFlare& flare) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
flare_ = flare;
}
@@ -361,37 +554,137 @@ void PasswordSyncableService::WriteToPasswordStore(const SyncEntries& entries) {
password_store_->NotifyLoginsChanged(changes);
}
-// static
+void PasswordSyncableService::MergeSyncDataWithLocalData(
+ const syncer::SyncDataList& sync_data,
+ PasswordEntryMap* unmatched_data_from_password_db,
+ SyncEntries* sync_entries,
+ syncer::SyncChangeList* updated_db_entries) {
+ std::map<std::string, const sync_pb::PasswordSpecificsData*> sync_data_map;
+ for (const auto& data : sync_data) {
+ const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
+ const sync_pb::PasswordSpecificsData* password_specifics =
+ &specifics.password().client_only_encrypted_data();
+ sync_data_map[MakePasswordSyncTag(*password_specifics)] =
+ password_specifics;
+ }
+ DCHECK_EQ(sync_data_map.size(), sync_data.size());
+
+ for (auto it = sync_data_map.begin(); it != sync_data_map.end();) {
+ if (IsValidAndroidFacetURI(it->second->signon_realm())) {
+ // Perform deduplication of Android credentials saved in the wrong format.
+ // For each incorrect entry, a duplicate of it is created in the correct
+ // format, so Chrome can make use of it. The incorrect sync entries are
+ // not deleted for now.
+ std::string incorrect_tag = AndroidAutofillSyncTag(*it->second);
+ std::string correct_tag = AndroidCorrectSyncTag(*it->second);
+ auto it_sync_incorrect = sync_data_map.find(incorrect_tag);
+ auto it_sync_correct = sync_data_map.find(correct_tag);
+ auto it_local_data_correct =
+ unmatched_data_from_password_db->find(correct_tag);
+ auto it_local_data_incorrect =
+ unmatched_data_from_password_db->find(incorrect_tag);
+ if ((it != it_sync_incorrect && it != it_sync_correct) ||
+ (it_sync_incorrect == sync_data_map.end() &&
+ it_local_data_incorrect == unmatched_data_from_password_db->end())) {
+ // The current credential is in an unexpected format or incorrect
+ // credential don't exist. Just do what Sync would normally do.
+ CreateOrUpdateEntry(*it->second, unmatched_data_from_password_db,
+ sync_entries, updated_db_entries);
+ ++it;
+ } else {
+ AndroidMergeResult result = Perform4WayMergeAndroidCredentials(
+ it_sync_correct == sync_data_map.end() ? nullptr
+ : it_sync_correct->second,
+ it_sync_incorrect == sync_data_map.end()
+ ? nullptr
+ : it_sync_incorrect->second,
+ it_local_data_correct == unmatched_data_from_password_db->end()
+ ? nullptr
+ : it_local_data_correct->second,
+ it_local_data_incorrect == unmatched_data_from_password_db->end()
+ ? nullptr
+ : it_local_data_incorrect->second);
+ // Add or update the correct local entry.
+ if (result.new_local_correct) {
+ auto* local_changes = sync_entries->EntriesForChangeType(
+ it_local_data_correct == unmatched_data_from_password_db->end()
+ ? syncer::SyncChange::ACTION_ADD
+ : syncer::SyncChange::ACTION_UPDATE);
+ local_changes->push_back(base::MakeUnique<autofill::PasswordForm>(
+ result.new_local_correct.value()));
+ local_changes->back()->date_synced = clock_->Now();
+ }
+ // Add or update the incorrect local entry.
+ if (result.new_local_incorrect) {
+ auto* local_changes = sync_entries->EntriesForChangeType(
+ it_local_data_incorrect == unmatched_data_from_password_db->end()
+ ? syncer::SyncChange::ACTION_ADD
+ : syncer::SyncChange::ACTION_UPDATE);
+ local_changes->push_back(base::MakeUnique<autofill::PasswordForm>(
+ result.new_local_incorrect.value()));
+ local_changes->back()->date_synced = clock_->Now();
+ }
+ if (it_local_data_correct != unmatched_data_from_password_db->end())
+ unmatched_data_from_password_db->erase(it_local_data_correct);
+ if (it_local_data_incorrect != unmatched_data_from_password_db->end())
+ unmatched_data_from_password_db->erase(it_local_data_incorrect);
+ // Add or update the correct sync entry.
+ if (result.new_android_correct) {
+ updated_db_entries->push_back(
+ syncer::SyncChange(FROM_HERE,
+ it_sync_correct == sync_data_map.end()
+ ? syncer::SyncChange::ACTION_ADD
+ : syncer::SyncChange::ACTION_UPDATE,
+ result.new_android_correct.value()));
+ }
+ // Add or update the Android Autofill sync entry.
+ if (result.new_android_incorrect) {
+ updated_db_entries->push_back(
+ syncer::SyncChange(FROM_HERE,
+ it_sync_incorrect == sync_data_map.end()
+ ? syncer::SyncChange::ACTION_ADD
+ : syncer::SyncChange::ACTION_UPDATE,
+ result.new_android_incorrect.value()));
+ }
+ bool increment = true;
+ for (auto sync_data_it : {it_sync_incorrect, it_sync_correct}) {
+ if (sync_data_it != sync_data_map.end()) {
+ if (sync_data_it == it) {
+ it = sync_data_map.erase(it);
+ increment = false;
+ } else {
+ sync_data_map.erase(sync_data_it);
+ }
+ }
+ }
+ if (increment)
+ ++it;
+ }
+ } else {
+ // Not Android.
+ CreateOrUpdateEntry(*it->second, unmatched_data_from_password_db,
+ sync_entries, updated_db_entries);
+ ++it;
+ }
+ }
+}
+
void PasswordSyncableService::CreateOrUpdateEntry(
- const syncer::SyncData& data,
+ const sync_pb::PasswordSpecificsData& password_specifics,
PasswordEntryMap* unmatched_data_from_password_db,
SyncEntries* sync_entries,
syncer::SyncChangeList* updated_db_entries) {
- const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
- const sync_pb::PasswordSpecificsData& password_specifics(
- specifics.password().client_only_encrypted_data());
std::string tag = MakePasswordSyncTag(password_specifics);
// Check whether the data from sync is already in the password store.
PasswordEntryMap::iterator existing_local_entry_iter =
unmatched_data_from_password_db->find(tag);
- base::Time time_now = base::Time::Now();
+ base::Time time_now = clock_->Now();
if (existing_local_entry_iter == unmatched_data_from_password_db->end()) {
- if (IsEmptyPasswordSpecificsData(password_specifics)) {
- // http://crbug.com/404012. Remove the empty password from the Sync
- // server. This code can be removed once all users have their password
- // store cleaned up. This should happen in M43 and can be verified using
- // crash reports.
- updated_db_entries->push_back(
- syncer::SyncChange(FROM_HERE,
- syncer::SyncChange::ACTION_DELETE,
- data));
- } else {
// The sync data is not in the password store, so we need to create it in
// the password store. Add the entry to the new_entries list.
AppendPasswordFromSpecifics(password_specifics, time_now,
&sync_entries->new_entries);
- }
} else {
// The entry is in password store. If the entries are not identical, then
// the entries need to be merged.
@@ -409,17 +702,6 @@ void PasswordSyncableService::CreateOrUpdateEntry(
AppendPasswordFromSpecifics(password_specifics, time_now,
&sync_entries->updated_entries);
}
- } else if (IsEmptyPasswordForm(password_form)) {
- // http://crbug.com/404012. Remove empty passwords from both the Sync
- // server and the local db. This code can be removed once all users have
- // their password store cleaned up. This should happen in M43 and can be
- // verified using crash reports.
- updated_db_entries->push_back(
- syncer::SyncChange(FROM_HERE,
- syncer::SyncChange::ACTION_DELETE,
- SyncDataFromPassword(password_form)));
- AppendPasswordFromSpecifics(password_specifics, time_now,
- &sync_entries->deleted_entries);
}
// Remove the entry from the entry map to indicate a match has been found.
// Entries that remain in the map at the end of associating all sync entries
@@ -445,31 +727,7 @@ syncer::SyncData SyncDataFromPassword(
sync_pb::EntitySpecifics password_data;
sync_pb::PasswordSpecificsData* password_specifics =
password_data.mutable_password()->mutable_client_only_encrypted_data();
-#define CopyField(field) password_specifics->set_##field(password_form.field)
-#define CopyStringField(field) \
- password_specifics->set_##field(base::UTF16ToUTF8(password_form.field))
- CopyField(scheme);
- CopyField(signon_realm);
- password_specifics->set_origin(password_form.origin.spec());
- password_specifics->set_action(password_form.action.spec());
- CopyStringField(username_element);
- CopyStringField(password_element);
- CopyStringField(username_value);
- CopyStringField(password_value);
- CopyField(preferred);
- password_specifics->set_date_created(
- password_form.date_created.ToInternalValue());
- password_specifics->set_blacklisted(password_form.blacklisted_by_user);
- CopyField(type);
- CopyField(times_used);
- CopyStringField(display_name);
- password_specifics->set_avatar_url(password_form.icon_url.spec());
- password_specifics->set_federation_url(
- password_form.federation_origin.unique()
- ? std::string()
- : password_form.federation_origin.Serialize());
-#undef CopyStringField
-#undef CopyField
+ PasswordSpecificsFromPassword(password_form, password_specifics);
std::string tag = MakePasswordSyncTag(*password_specifics);
return syncer::SyncData::CreateLocalData(tag, tag, password_data);
diff --git a/chromium/components/password_manager/core/browser/password_syncable_service.h b/chromium/components/password_manager/core/browser/password_syncable_service.h
index 6eb21079a6f..98b6bcc8ab3 100644
--- a/chromium/components/password_manager/core/browser/password_syncable_service.h
+++ b/chromium/components/password_manager/core/browser/password_syncable_service.h
@@ -10,7 +10,8 @@
#include <vector>
#include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
+#include "base/time/clock.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/sync/model/sync_change.h"
#include "components/sync/model/sync_data.h"
@@ -32,8 +33,7 @@ namespace password_manager {
class PasswordStoreSync;
// The implementation of the SyncableService API for passwords.
-class PasswordSyncableService : public syncer::SyncableService,
- public base::NonThreadSafe {
+class PasswordSyncableService : public syncer::SyncableService {
public:
// Since the constructed |PasswordSyncableService| is typically owned by the
// |password_store|, the constructor doesn't take ownership of the
@@ -61,6 +61,12 @@ class PasswordSyncableService : public syncer::SyncableService,
void InjectStartSyncFlare(
const syncer::SyncableService::StartSyncFlare& flare);
+#if defined(UNIT_TEST)
+ void set_clock(std::unique_ptr<base::Clock> clock) {
+ clock_ = std::move(clock);
+ }
+#endif
+
private:
// Map from password sync tag to password form.
typedef std::map<std::string, autofill::PasswordForm*> PasswordEntryMap;
@@ -81,15 +87,24 @@ class PasswordSyncableService : public syncer::SyncableService,
// Uses the |PasswordStore| APIs to change entries.
void WriteToPasswordStore(const SyncEntries& entries);
- // Examines |data|, an entry in sync db, and updates |sync_entries| or
- // |updated_db_entries| accordingly. An element is removed from
- // |unmatched_data_from_password_db| if its tag is identical to |data|'s.
- static void CreateOrUpdateEntry(
- const syncer::SyncData& data,
+ // Goes through |sync_data| and for each entry merges the data with
+ // |unmatched_data_from_password_db|. The result of merge is recorded in
+ // |sync_entries| or |updated_db_entries|. Successfully merged elements are
+ // removed from |unmatched_data_from_password_db|.
+ void MergeSyncDataWithLocalData(
+ const syncer::SyncDataList& sync_data,
PasswordEntryMap* unmatched_data_from_password_db,
SyncEntries* sync_entries,
syncer::SyncChangeList* updated_db_entries);
+ // Examines |data|, an entry in sync db, and updates |sync_entries| or
+ // |updated_db_entries| accordingly. An element is removed from
+ // |unmatched_data_from_password_db| if its tag is identical to |data|'s.
+ void CreateOrUpdateEntry(const sync_pb::PasswordSpecificsData& data,
+ PasswordEntryMap* unmatched_data_from_password_db,
+ SyncEntries* sync_entries,
+ syncer::SyncChangeList* updated_db_entries);
+
// Calls |operation| for each element in |entries| and appends the changes to
// |all_changes|.
void WriteEntriesToDatabase(
@@ -110,9 +125,14 @@ class PasswordSyncableService : public syncer::SyncableService,
// A signal activated by this class to start sync as soon as possible.
syncer::SyncableService::StartSyncFlare flare_;
+ // Clock for date_synced updates.
+ std::unique_ptr<base::Clock> clock_;
+
// True if processing sync changes is in progress.
bool is_processing_sync_changes_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(PasswordSyncableService);
};
diff --git a/chromium/components/password_manager/core/browser/password_syncable_service_unittest.cc b/chromium/components/password_manager/core/browser/password_syncable_service_unittest.cc
index bb3a731bfc9..c487e6682cc 100644
--- a/chromium/components/password_manager/core/browser/password_syncable_service_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_syncable_service_unittest.cc
@@ -14,9 +14,11 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/simple_test_clock.h"
#include "components/password_manager/core/browser/mock_password_store.h"
+#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/sync/model/sync_change_processor.h"
#include "components/sync/model/sync_error.h"
#include "components/sync/model/sync_error_factory_mock.h"
@@ -51,17 +53,19 @@ std::string MakePasswordSyncTag(const autofill::PasswordForm& password);
namespace {
// PasswordForm values for tests.
-const autofill::PasswordForm::Type kArbitraryType =
+constexpr autofill::PasswordForm::Type kArbitraryType =
autofill::PasswordForm::TYPE_GENERATED;
-const char kIconUrl[] = "https://fb.com/Icon";
-const char kDisplayName[] = "Agent Smith";
-const char kFederationUrl[] = "https://fb.com/";
-const char kPassword[] = "abcdef";
-const char kSignonRealm[] = "abc";
-const char kSignonRealm2[] = "def";
-const char kSignonRealm3[] = "xyz";
-const int kTimesUsed = 5;
-const char kUsername[] = "godzilla";
+constexpr char kAndroidAutofillRealm[] = "android://hash@com.magisto";
+constexpr char kAndroidCorrectRealm[] = "android://hash@com.magisto/";
+constexpr char kIconUrl[] = "https://fb.com/Icon";
+constexpr char kDisplayName[] = "Agent Smith";
+constexpr char kFederationUrl[] = "https://fb.com/";
+constexpr char kPassword[] = "abcdef";
+constexpr char kSignonRealm[] = "abc";
+constexpr char kSignonRealm2[] = "def";
+constexpr char kSignonRealm3[] = "xyz";
+constexpr int kTimesUsed = 5;
+constexpr char kUsername[] = "godzilla";
typedef std::vector<SyncChange> SyncChangeList;
@@ -70,8 +74,8 @@ const sync_pb::PasswordSpecificsData& GetPasswordSpecifics(
return sync_data.GetSpecifics().password().client_only_encrypted_data();
}
-MATCHER(HasDateSynced, "") {
- return !arg.date_synced.is_null() && !arg.date_synced.is_max();
+MATCHER_P(HasDateSynced, time, "") {
+ return arg.date_synced == time;
}
MATCHER_P(PasswordIs, form, "") {
@@ -124,6 +128,14 @@ ACTION_P(AppendForm, form) {
return true;
}
+// The argument is std::vector<autofill::PasswordForm*>*. The caller is
+// responsible for the lifetime of all the password forms.
+ACTION_P(AppendForms, forms) {
+ for (const autofill::PasswordForm& form : forms)
+ arg0->push_back(base::MakeUnique<autofill::PasswordForm>(form));
+ return true;
+}
+
// Creates a sync data consisting of password specifics. The sign on realm is
// set to |signon_realm|.
SyncData CreateSyncData(const std::string& signon_realm) {
@@ -149,19 +161,6 @@ SyncChange CreateSyncChange(const autofill::PasswordForm& password,
return SyncChange(FROM_HERE, type, data);
}
-// A testable implementation of the |PasswordSyncableService| that mocks
-// out all interaction with the password database.
-class MockPasswordSyncableService : public PasswordSyncableService {
- public:
- explicit MockPasswordSyncableService(PasswordStoreSync* password_store)
- : PasswordSyncableService(password_store) {}
-
- MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType));
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockPasswordSyncableService);
-};
-
// Mock implementation of SyncChangeProcessor.
class MockSyncChangeProcessor : public syncer::SyncChangeProcessor {
public:
@@ -186,12 +185,15 @@ class PasswordSyncableServiceWrapper {
PasswordSyncableServiceWrapper() {
password_store_ = new testing::StrictMock<MockPasswordStore>;
service_.reset(
- new MockPasswordSyncableService(password_store_->GetSyncInterface()));
- ON_CALL(*password_store_, AddLoginImpl(HasDateSynced()))
+ new PasswordSyncableService(password_store_->GetSyncInterface()));
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ clock->SetNow(time());
+ service_->set_clock(std::move(clock));
+ ON_CALL(*password_store_, AddLoginImpl(HasDateSynced(time())))
.WillByDefault(Return(PasswordStoreChangeList()));
ON_CALL(*password_store_, RemoveLoginImpl(_))
.WillByDefault(Return(PasswordStoreChangeList()));
- ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced()))
+ ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced(time())))
.WillByDefault(Return(PasswordStoreChangeList()));
EXPECT_CALL(*password_store(), NotifyLoginsChanged(_)).Times(AnyNumber());
}
@@ -200,7 +202,9 @@ class PasswordSyncableServiceWrapper {
MockPasswordStore* password_store() { return password_store_.get(); }
- MockPasswordSyncableService* service() { return service_.get(); }
+ PasswordSyncableService* service() { return service_.get(); }
+
+ static base::Time time() { return base::Time::FromInternalValue(100000); }
// Returnes the scoped_ptr to |service_| thus NULLing out it.
std::unique_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() {
@@ -209,7 +213,7 @@ class PasswordSyncableServiceWrapper {
private:
scoped_refptr<MockPasswordStore> password_store_;
- std::unique_ptr<MockPasswordSyncableService> service_;
+ std::unique_ptr<PasswordSyncableService> service_;
DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper);
};
@@ -222,13 +226,15 @@ class PasswordSyncableServiceTest : public testing::Test {
.WillByDefault(Return(SyncError()));
}
MockPasswordStore* password_store() { return wrapper_.password_store(); }
- MockPasswordSyncableService* service() { return wrapper_.service(); }
+ PasswordSyncableService* service() { return wrapper_.service(); }
+
+ MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType));
protected:
- base::MessageLoop message_loop_;
std::unique_ptr<MockSyncChangeProcessor> processor_;
private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
PasswordSyncableServiceWrapper wrapper_;
};
@@ -488,10 +494,9 @@ TEST_F(PasswordSyncableServiceTest, StartSyncFlare) {
service()->ActOnPasswordStoreChanges(list);
// Set the flare. It should be called as there is no SyncChangeProcessor.
- service()->InjectStartSyncFlare(
- base::Bind(&MockPasswordSyncableService::StartSyncFlare,
- base::Unretained(service())));
- EXPECT_CALL(*service(), StartSyncFlare(syncer::PASSWORDS));
+ service()->InjectStartSyncFlare(base::Bind(
+ &PasswordSyncableServiceTest::StartSyncFlare, base::Unretained(this)));
+ EXPECT_CALL(*this, StartSyncFlare(syncer::PASSWORDS));
service()->ActOnPasswordStoreChanges(list);
}
@@ -553,38 +558,6 @@ TEST_F(PasswordSyncableServiceTest, FailedProcessSyncChanges) {
service()->ActOnPasswordStoreChanges(list);
}
-// Tests that empty forms known to the client and/or the server are deleted.
-TEST_F(PasswordSyncableServiceTest, MergeEmptyPasswords) {
- autofill::PasswordForm old_empty_form;
- old_empty_form.signon_realm = kSignonRealm;
- old_empty_form.times_used = kTimesUsed;
- old_empty_form.type = kArbitraryType;
-
- autofill::PasswordForm db_empty_form = old_empty_form;
- db_empty_form.signon_realm = kSignonRealm2;
-
- autofill::PasswordForm sync_empty_form = old_empty_form;
- sync_empty_form.signon_realm = kSignonRealm3;
- SyncDataList sync_data;
- sync_data.push_back(SyncDataFromPassword(old_empty_form));
- sync_data.push_back(SyncDataFromPassword(sync_empty_form));
-
- EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
- .WillOnce(DoAll(IgnoreResult(AppendForm(old_empty_form)),
- AppendForm(db_empty_form)));
- EXPECT_CALL(*password_store(), FillBlacklistLogins(_)).WillOnce(Return(true));
-
- EXPECT_CALL(*password_store(), RemoveLoginImpl(PasswordIs(old_empty_form)));
- EXPECT_CALL(*password_store(), RemoveLoginImpl(PasswordIs(db_empty_form)));
- EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre(
- SyncChangeIs(SyncChange::ACTION_DELETE, old_empty_form),
- SyncChangeIs(SyncChange::ACTION_DELETE, sync_empty_form))));
-
- service()->MergeDataAndStartSyncing(
- syncer::PASSWORDS, sync_data, std::move(processor_),
- std::unique_ptr<syncer::SyncErrorFactory>());
-}
-
// Serialize and deserialize empty federation_origin and make sure it's an empty
// string.
TEST_F(PasswordSyncableServiceTest, SerializeEmptyFederation) {
@@ -705,6 +678,550 @@ TEST_F(PasswordSyncableServiceTest, SerializeNonEmptyPasswordForm) {
EXPECT_EQ("https://google.com", specifics.federation_url());
}
+// Tests for Android Autofill credentials. Those are saved in the wrong format
+// without trailing '/'. Nevertheless, password store should always contain the
+// correct values.
+class PasswordSyncableServiceAndroidAutofillTest : public testing::Test {
+ public:
+ PasswordSyncableServiceAndroidAutofillTest() = default;
+
+ static PasswordFormData android_incorrect(double creation_time) {
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML,
+ kAndroidAutofillRealm,
+ kAndroidAutofillRealm,
+ "",
+ L"",
+ L"",
+ L"",
+ L"username_value_1",
+ L"11111",
+ true,
+ creation_time};
+ return data;
+ }
+
+ static PasswordFormData android_correct(double creation_time) {
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML,
+ kAndroidCorrectRealm,
+ kAndroidCorrectRealm,
+ "",
+ L"",
+ L"",
+ L"",
+ L"username_value_1",
+ L"22222",
+ true,
+ creation_time};
+ return data;
+ }
+
+ static PasswordFormData android_incorrect2(double creation_time) {
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML,
+ kAndroidAutofillRealm,
+ kAndroidAutofillRealm,
+ "",
+ L"",
+ L"",
+ L"",
+ L"username_value_1",
+ L"33333",
+ false,
+ creation_time};
+ return data;
+ }
+
+ static PasswordFormData android_correct2(double creation_time) {
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML,
+ kAndroidCorrectRealm,
+ kAndroidCorrectRealm,
+ "",
+ L"",
+ L"",
+ L"",
+ L"username_value_1",
+ L"444444",
+ false,
+ creation_time};
+ return data;
+ }
+
+ static autofill::PasswordForm FormWithCorrectTag(PasswordFormData data) {
+ autofill::PasswordForm form = *CreatePasswordFormFromDataForTesting(data);
+ form.signon_realm = kAndroidCorrectRealm;
+ form.origin = GURL(kAndroidCorrectRealm);
+ form.date_synced = PasswordSyncableServiceWrapper::time();
+ return form;
+ }
+
+ static autofill::PasswordForm FormWithAndroidAutofillTag(
+ PasswordFormData data) {
+ autofill::PasswordForm form = *CreatePasswordFormFromDataForTesting(data);
+ form.signon_realm = kAndroidAutofillRealm;
+ form.origin = GURL(kAndroidAutofillRealm);
+ form.date_synced = PasswordSyncableServiceWrapper::time();
+ return form;
+ }
+
+ // Transforms |val| into |count| numbers from 1 to |count| inclusive.
+ static std::vector<unsigned> ExtractTimestamps(unsigned val, unsigned count) {
+ std::vector<unsigned> result;
+ for (unsigned i = 0; i < count; ++i) {
+ result.push_back(val % count + 1);
+ val /= count;
+ }
+ return result;
+ }
+
+ static testing::Message FormDataMessage(const std::string& prefix,
+ const PasswordFormData* data) {
+ testing::Message message;
+ message << prefix;
+ if (data)
+ message << *CreatePasswordFormFromDataForTesting(*data);
+ else
+ message << "NULL";
+ return message;
+ }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, FourWayMerge) {
+ for (unsigned val = 0; val < 4 * 4 * 4 * 4; ++val) {
+ // Generate 4 creation timestamps for all the entries.
+ std::vector<unsigned> dates = ExtractTimestamps(val, 4);
+ ASSERT_EQ(4u, dates.size());
+ const unsigned latest = *std::max_element(dates.begin(), dates.end());
+ // Sync correct, Sync Android autofill, local correct, local incorrect.
+ const PasswordFormData data[4] = {
+ android_correct(dates[0]), android_incorrect(dates[1]),
+ android_correct2(dates[2]), android_incorrect2(dates[3])};
+ const PasswordFormData* latest_data =
+ std::find_if(data, data + 4, [latest](const PasswordFormData& data) {
+ return data.creation_time == latest;
+ });
+ ASSERT_TRUE(latest_data);
+ std::vector<autofill::PasswordForm> expected_sync_updates;
+ if (latest_data != &data[0])
+ expected_sync_updates.push_back(FormWithCorrectTag(*latest_data));
+ if (latest_data != &data[1])
+ expected_sync_updates.push_back(FormWithAndroidAutofillTag(*latest_data));
+ autofill::PasswordForm local_correct =
+ *CreatePasswordFormFromDataForTesting(data[2]);
+ autofill::PasswordForm local_incorrect =
+ *CreatePasswordFormFromDataForTesting(data[3]);
+ syncer::SyncData sync_correct =
+ SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data[0]));
+ syncer::SyncData sync_incorrect =
+ SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data[1]));
+
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[0]));
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[1]));
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[2]));
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[3]));
+
+ for (bool correct_sync_first : {true, false}) {
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>();
+ auto processor =
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>();
+
+ std::vector<autofill::PasswordForm> stored_forms = {local_correct,
+ local_incorrect};
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_))
+ .WillOnce(AppendForms(stored_forms));
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_))
+ .WillOnce(Return(true));
+ if (latest_data != &data[2]) {
+ EXPECT_CALL(*wrapper->password_store(),
+ UpdateLoginImpl(FormWithCorrectTag(*latest_data)));
+ }
+ if (latest_data != &data[3]) {
+ EXPECT_CALL(*wrapper->password_store(),
+ UpdateLoginImpl(FormWithAndroidAutofillTag(*latest_data)));
+ }
+
+ if (expected_sync_updates.size() == 1) {
+ EXPECT_CALL(*processor,
+ ProcessSyncChanges(_, ElementsAre(SyncChangeIs(
+ SyncChange::ACTION_UPDATE,
+ expected_sync_updates[0]))));
+ } else {
+ EXPECT_CALL(
+ *processor,
+ ProcessSyncChanges(_, UnorderedElementsAre(
+ SyncChangeIs(SyncChange::ACTION_UPDATE,
+ expected_sync_updates[0]),
+ SyncChangeIs(SyncChange::ACTION_UPDATE,
+ expected_sync_updates[1]))));
+ }
+
+ SyncDataList sync_list = {sync_correct, sync_incorrect};
+ if (!correct_sync_first)
+ std::swap(sync_list[0], sync_list[1]);
+ wrapper->service()->MergeDataAndStartSyncing(
+ syncer::PASSWORDS, sync_list, std::move(processor),
+ std::unique_ptr<syncer::SyncErrorFactory>());
+ wrapper.reset();
+ // Wait til PasswordStore is destroy end therefore all the expectations on
+ // it are checked.
+ scoped_task_environment_.RunUntilIdle();
+ }
+ }
+}
+
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, ThreeWayMerge) {
+ for (int j = 0; j < 4; ++j) {
+ // Whether the entry exists: Sync correct, Sync Android autofill,
+ // local correct, local incorrect.
+ bool entry_present[4] = {true, true, true, true};
+ entry_present[j] = false;
+ for (unsigned val = 0; val < 3 * 3 * 3; ++val) {
+ // Generate 3 creation timestamps for all the entries.
+ std::vector<unsigned> dates = ExtractTimestamps(val, 3);
+ ASSERT_EQ(3u, dates.size());
+ const unsigned latest = *std::max_element(dates.begin(), dates.end());
+
+ // Sync correct, Sync Android autofill, local correct, local incorrect.
+ std::vector<std::unique_ptr<PasswordFormData>> data;
+ int date_index = 0;
+ data.push_back(entry_present[0]
+ ? base::MakeUnique<PasswordFormData>(
+ android_correct(dates[date_index++]))
+ : nullptr);
+ data.push_back(entry_present[1]
+ ? base::MakeUnique<PasswordFormData>(
+ android_incorrect(dates[date_index++]))
+ : nullptr);
+ data.push_back(entry_present[2]
+ ? base::MakeUnique<PasswordFormData>(
+ android_correct2(dates[date_index++]))
+ : nullptr);
+ data.push_back(entry_present[3]
+ ? base::MakeUnique<PasswordFormData>(
+ android_incorrect2(dates[date_index++]))
+ : nullptr);
+
+ SCOPED_TRACE(val);
+ SCOPED_TRACE(j);
+ SCOPED_TRACE(FormDataMessage("data[0]=", data[0].get()));
+ SCOPED_TRACE(FormDataMessage("data[1]=", data[1].get()));
+ SCOPED_TRACE(FormDataMessage("data[2]=", data[2].get()));
+ SCOPED_TRACE(FormDataMessage("data[3]=", data[3].get()));
+
+ const PasswordFormData* latest_data =
+ std::find_if(data.begin(), data.end(),
+ [latest](const std::unique_ptr<PasswordFormData>& data) {
+ return data && data->creation_time == latest;
+ })
+ ->get();
+ ASSERT_TRUE(latest_data);
+ std::vector<std::pair<SyncChange::SyncChangeType, autofill::PasswordForm>>
+ expected_sync_updates;
+ for (int i = 0; i < 2; ++i) {
+ if (latest_data != data[i].get()) {
+ expected_sync_updates.push_back(std::make_pair(
+ data[i] ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD,
+ i == 0 ? FormWithCorrectTag(*latest_data)
+ : FormWithAndroidAutofillTag(*latest_data)));
+ }
+ }
+
+ std::vector<autofill::PasswordForm> stored_forms;
+ for (int i = 2; i < 4; ++i) {
+ if (data[i])
+ stored_forms.push_back(
+ *CreatePasswordFormFromDataForTesting(*data[i]));
+ }
+
+ SyncDataList sync_list;
+ for (int i = 0; i < 2; ++i) {
+ if (data[i]) {
+ sync_list.push_back(SyncDataFromPassword(
+ *CreatePasswordFormFromDataForTesting(*data[i])));
+ }
+ }
+
+ for (bool swap_sync_list : {false, true}) {
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>();
+ auto processor =
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>();
+
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_))
+ .WillOnce(AppendForms(stored_forms));
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_))
+ .WillOnce(Return(true));
+ for (int i = 2; i < 4; ++i) {
+ if (latest_data != data[i].get()) {
+ autofill::PasswordForm latest_form =
+ i == 2 ? FormWithCorrectTag(*latest_data)
+ : FormWithAndroidAutofillTag(*latest_data);
+ if (data[i]) {
+ EXPECT_CALL(*wrapper->password_store(),
+ UpdateLoginImpl(latest_form));
+ } else {
+ EXPECT_CALL(*wrapper->password_store(),
+ AddLoginImpl(latest_form));
+ }
+ }
+ }
+
+ if (expected_sync_updates.size() == 0) {
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty()));
+ } else if (expected_sync_updates.size() == 1) {
+ EXPECT_CALL(
+ *processor,
+ ProcessSyncChanges(_, ElementsAre(SyncChangeIs(
+ expected_sync_updates[0].first,
+ expected_sync_updates[0].second))));
+ } else if (expected_sync_updates.size() == 2) {
+ EXPECT_CALL(
+ *processor,
+ ProcessSyncChanges(
+ _, UnorderedElementsAre(
+ SyncChangeIs(expected_sync_updates[0].first,
+ expected_sync_updates[0].second),
+ SyncChangeIs(expected_sync_updates[1].first,
+ expected_sync_updates[1].second))));
+ }
+
+ if (swap_sync_list && sync_list.size() == 2)
+ std::swap(sync_list[0], sync_list[1]);
+ wrapper->service()->MergeDataAndStartSyncing(
+ syncer::PASSWORDS, sync_list, std::move(processor),
+ std::unique_ptr<syncer::SyncErrorFactory>());
+ wrapper.reset();
+ // Wait til PasswordStore is destroy end therefore all the expectations
+ // on it are checked.
+ scoped_task_environment_.RunUntilIdle();
+ }
+ }
+ }
+}
+
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, TwoWayServerAndLocalMerge) {
+ for (unsigned i = 0; i < 2 * 2; ++i) {
+ // Generate 4 different combinations for local/server entries.
+ std::vector<unsigned> combination = ExtractTimestamps(i, 2);
+ ASSERT_EQ(2u, combination.size());
+ const bool sync_data_correct = !!combination[0];
+ const bool local_data_correct = !!combination[1];
+
+ for (unsigned val = 0; val < 2 * 2; ++val) {
+ std::vector<unsigned> dates = ExtractTimestamps(i, 2);
+ ASSERT_EQ(2u, dates.size());
+
+ const PasswordFormData sync_data = sync_data_correct
+ ? android_correct(dates[0])
+ : android_incorrect(dates[0]);
+ const PasswordFormData local_data = local_data_correct
+ ? android_correct2(dates[1])
+ : android_incorrect2(dates[1]);
+
+ const PasswordFormData* latest_data =
+ dates[1] > dates[0] ? &local_data : &sync_data;
+
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>();
+ auto processor =
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>();
+
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_))
+ .WillOnce(
+ AppendForm(*CreatePasswordFormFromDataForTesting(local_data)));
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_))
+ .WillOnce(Return(true));
+ if (!local_data_correct || latest_data == &sync_data) {
+ if (local_data_correct) {
+ EXPECT_CALL(*wrapper->password_store(),
+ UpdateLoginImpl(FormWithCorrectTag(*latest_data)));
+ } else {
+ EXPECT_CALL(*wrapper->password_store(),
+ AddLoginImpl(FormWithCorrectTag(*latest_data)));
+ }
+ }
+ if (!local_data_correct && latest_data == &sync_data) {
+ EXPECT_CALL(*wrapper->password_store(),
+ UpdateLoginImpl(FormWithAndroidAutofillTag(*latest_data)));
+ } else if (local_data_correct && !sync_data_correct) {
+ EXPECT_CALL(*wrapper->password_store(),
+ AddLoginImpl(FormWithAndroidAutofillTag(*latest_data)));
+ }
+
+ std::vector<std::pair<SyncChange::SyncChangeType, autofill::PasswordForm>>
+ expected_sync_updates;
+ // Deal with the correct sync entry and incorrect one.
+ if (sync_data_correct) {
+ if (latest_data != &sync_data) {
+ expected_sync_updates.push_back(std::make_pair(
+ SyncChange::ACTION_UPDATE, FormWithCorrectTag(*latest_data)));
+ }
+ if (!local_data_correct) {
+ expected_sync_updates.push_back(
+ std::make_pair(SyncChange::ACTION_ADD,
+ FormWithAndroidAutofillTag(*latest_data)));
+ }
+ } else {
+ expected_sync_updates.push_back(std::make_pair(
+ SyncChange::ACTION_ADD, FormWithCorrectTag(*latest_data)));
+ if (latest_data != &sync_data) {
+ expected_sync_updates.push_back(
+ std::make_pair(SyncChange::ACTION_UPDATE,
+ FormWithAndroidAutofillTag(*latest_data)));
+ }
+ }
+
+ // Set expectation on |processor|.
+ if (expected_sync_updates.size() == 0) {
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty()));
+ } else if (expected_sync_updates.size() == 1) {
+ EXPECT_CALL(
+ *processor,
+ ProcessSyncChanges(
+ _, ElementsAre(SyncChangeIs(expected_sync_updates[0].first,
+ expected_sync_updates[0].second))));
+ } else if (expected_sync_updates.size() == 2) {
+ EXPECT_CALL(*processor,
+ ProcessSyncChanges(
+ _, UnorderedElementsAre(
+ SyncChangeIs(expected_sync_updates[0].first,
+ expected_sync_updates[0].second),
+ SyncChangeIs(expected_sync_updates[1].first,
+ expected_sync_updates[1].second))));
+ }
+
+ SyncDataList sync_list = {SyncDataFromPassword(
+ *CreatePasswordFormFromDataForTesting(sync_data))};
+ wrapper->service()->MergeDataAndStartSyncing(
+ syncer::PASSWORDS, sync_list, std::move(processor),
+ std::unique_ptr<syncer::SyncErrorFactory>());
+ wrapper.reset();
+ // Wait til PasswordStore is destroy end therefore all the expectations on
+ // it are checked.
+ scoped_task_environment_.RunUntilIdle();
+ }
+ }
+}
+
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, OneEntryOnly) {
+ for (int i = 0; i < 3; ++i) {
+ // The case when only local incorrect entry exists is excluded. It's very
+ // exotic because a local incorrect entry can come only from the server.
+ // In such a case a copy will be uploaded to the server and next
+ // MergeDataAndStartSyncing will do a proper migration.
+ SCOPED_TRACE(i);
+ // Whether the entry exists: Sync correct, Sync Android autofill,
+ // local correct, local incorrect.
+ const bool entry_is_correct = i == 0 || i == 2;
+ const bool entry_is_local = i >= 2;
+ PasswordFormData data =
+ entry_is_correct ? android_correct(100) : android_incorrect(100);
+
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>();
+ auto processor =
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>();
+
+ if (entry_is_local) {
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_))
+ .WillOnce(AppendForm(*CreatePasswordFormFromDataForTesting(data)));
+ } else {
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_))
+ .WillOnce(Return(true));
+ }
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_))
+ .WillOnce(Return(true));
+ if (!entry_is_local && !entry_is_correct) {
+ EXPECT_CALL(*wrapper->password_store(),
+ AddLoginImpl(FormWithAndroidAutofillTag(data)));
+ }
+ if (!entry_is_local) {
+ EXPECT_CALL(*wrapper->password_store(),
+ AddLoginImpl(FormWithCorrectTag(data)));
+ }
+
+ if (entry_is_correct && !entry_is_local) {
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty()));
+ } else {
+ EXPECT_CALL(*processor,
+ ProcessSyncChanges(
+ _, ElementsAre(SyncChangeIs(SyncChange::ACTION_ADD,
+ FormWithCorrectTag(data)))));
+ }
+
+ SyncDataList sync_list;
+ if (!entry_is_local) {
+ sync_list.push_back(
+ SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data)));
+ }
+ wrapper->service()->MergeDataAndStartSyncing(
+ syncer::PASSWORDS, sync_list, std::move(processor),
+ std::unique_ptr<syncer::SyncErrorFactory>());
+ wrapper.reset();
+ // Wait til PasswordStore is destroy end therefore all the expectations on
+ // it are checked.
+ scoped_task_environment_.RunUntilIdle();
+ }
+}
+
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, FourEqualEntries) {
+ // Sync correct, Sync Android autofill, local correct, local incorrect with
+ // the same content. Nothing should happen.
+ const PasswordFormData data = android_correct(100);
+ autofill::PasswordForm local_correct = FormWithCorrectTag(data);
+ autofill::PasswordForm local_incorrect = FormWithAndroidAutofillTag(data);
+ syncer::SyncData sync_correct = SyncDataFromPassword(local_correct);
+ syncer::SyncData sync_incorrect = SyncDataFromPassword(local_incorrect);
+
+ for (bool correct_sync_first : {true, false}) {
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>();
+ auto processor =
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>();
+
+ std::vector<autofill::PasswordForm> stored_forms = {local_correct,
+ local_incorrect};
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_))
+ .WillOnce(AppendForms(stored_forms));
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty()));
+
+ SyncDataList sync_list = {sync_correct, sync_incorrect};
+ if (!correct_sync_first)
+ std::swap(sync_list[0], sync_list[1]);
+ wrapper->service()->MergeDataAndStartSyncing(
+ syncer::PASSWORDS, sync_list, std::move(processor),
+ std::unique_ptr<syncer::SyncErrorFactory>());
+ wrapper.reset();
+ // Wait til PasswordStore is destroy end therefore all the expectations on
+ // it are checked.
+ scoped_task_environment_.RunUntilIdle();
+ }
+}
+
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, AndroidCorrectEqualEntries) {
+ // Sync correct, local correct with the same content. Nothing should happen.
+ const PasswordFormData data = android_correct(100);
+ autofill::PasswordForm local_correct = FormWithCorrectTag(data);
+ syncer::SyncData sync_correct = SyncDataFromPassword(local_correct);
+
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>();
+ auto processor =
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>();
+
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_))
+ .WillOnce(AppendForm(local_correct));
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty()));
+
+ wrapper->service()->MergeDataAndStartSyncing(
+ syncer::PASSWORDS, {sync_correct}, std::move(processor),
+ std::unique_ptr<syncer::SyncErrorFactory>());
+ wrapper.reset();
+ // Wait til PasswordStore is destroy end therefore all the expectations on
+ // it are checked.
+ scoped_task_environment_.RunUntilIdle();
+}
+
} // namespace
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_ui_utils.cc b/chromium/components/password_manager/core/browser/password_ui_utils.cc
index 34f03d6833a..8e4cf6aed39 100644
--- a/chromium/components/password_manager/core/browser/password_ui_utils.cc
+++ b/chromium/components/password_manager/core/browser/password_ui_utils.cc
@@ -6,12 +6,13 @@
#include <algorithm>
#include <string>
+#include <vector>
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/affiliation_utils.h"
+#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/url_formatter/elide_url.h"
namespace password_manager {
@@ -21,6 +22,9 @@ namespace {
// The URL prefixes that are removed from shown origin.
const char* const kRemovedPrefixes[] = {"m.", "mobile.", "www."};
+constexpr char kPlayStoreAppPrefix[] =
+ "https://play.google.com/store/apps/details?id=";
+
} // namespace
std::string SplitByDotAndReverse(base::StringPiece host) {
@@ -30,41 +34,24 @@ std::string SplitByDotAndReverse(base::StringPiece host) {
return base::JoinString(parts, ".");
}
-std::string StripAndroidAndReverse(const std::string& origin) {
- const int kAndroidAppSchemeAndDelimiterLength = sizeof("android://") - 1;
- return SplitByDotAndReverse(
- base::StringPiece(&origin[kAndroidAppSchemeAndDelimiterLength],
- origin.length() - kAndroidAppSchemeAndDelimiterLength));
-}
-
-std::string GetShownOriginAndLinkUrl(
- const autofill::PasswordForm& password_form,
- bool* is_android_uri,
- GURL* link_url,
- bool* origin_is_clickable) {
- DCHECK(is_android_uri);
- DCHECK(origin_is_clickable);
- DCHECK(link_url);
+std::pair<std::string, GURL> GetShownOriginAndLinkUrl(
+ const autofill::PasswordForm& password_form) {
+ std::string shown_origin;
+ GURL link_url;
- password_manager::FacetURI facet_uri =
- password_manager::FacetURI::FromPotentiallyInvalidSpec(
- password_form.signon_realm);
- *is_android_uri = facet_uri.IsValidAndroidFacetURI();
- if (*is_android_uri) {
- if (password_form.affiliated_web_realm.empty()) {
- *origin_is_clickable = false;
- // Since the full url should be shown in the tooltip even for
- // non-clickable origins, return it as |link_url|.
- *link_url = GURL(password_form.signon_realm);
- return GetHumanReadableOriginForAndroidUri(facet_uri);
- }
- *origin_is_clickable = true;
- *link_url = GURL(password_form.affiliated_web_realm);
- return GetShownOrigin(*link_url);
+ FacetURI facet_uri =
+ FacetURI::FromPotentiallyInvalidSpec(password_form.signon_realm);
+ if (facet_uri.IsValidAndroidFacetURI()) {
+ shown_origin = password_form.app_display_name.empty()
+ ? SplitByDotAndReverse(facet_uri.android_package_name())
+ : password_form.app_display_name;
+ link_url = GURL(kPlayStoreAppPrefix + facet_uri.android_package_name());
+ } else {
+ shown_origin = GetShownOrigin(password_form.origin);
+ link_url = password_form.origin;
}
- *origin_is_clickable = true;
- *link_url = password_form.origin;
- return GetShownOrigin(password_form.origin);
+
+ return {std::move(shown_origin), std::move(link_url)};
}
std::string GetShownOrigin(const GURL& origin) {
diff --git a/chromium/components/password_manager/core/browser/password_ui_utils.h b/chromium/components/password_manager/core/browser/password_ui_utils.h
index 10a8ddb8b82..b4d98c8c9e7 100644
--- a/chromium/components/password_manager/core/browser/password_ui_utils.h
+++ b/chromium/components/password_manager/core/browser/password_ui_utils.h
@@ -8,6 +8,7 @@
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_UI_UTILS_H_
#include <string>
+#include <utility>
#include "base/strings/string_piece.h"
@@ -22,25 +23,18 @@ namespace password_manager {
// Reverses order of labels in hostname.
std::string SplitByDotAndReverse(base::StringPiece host);
-// Removes 'android://' and reverses order of labels in hostname.
-std::string StripAndroidAndReverse(const std::string& origin);
-
-// Returns a string suitable for security display to the user (just like
-// |FormatUrlForSecurityDisplay| with OMIT_HTTP_AND_HTTPS) based on origin of
-// |password_form| and without prefixes "m.", "mobile." or "www.". Also
-// returns the full URL of the origin as |link_url|. |link_url| will be also
-// shown as tooltip on the password page.
-// For Android forms with empty |password_form.affiliated_web_realm|,
-// returns the result of GetHumanReadableOriginForAndroidUri. For other Android
-// forms, returns |password_form.affiliated_web_realm|.
-// |*origin_is_clickable| is set to true, except for Android forms with empty
-// |password_form.affiliated_web_realm|.
-// |is_android_url|, |link_url|, |origin_is_clickable| are required to non-null.
-std::string GetShownOriginAndLinkUrl(
- const autofill::PasswordForm& password_form,
- bool* is_android_uri,
- GURL* link_url,
- bool* origin_is_clickable);
+// Returns a human readable origin and a link URL for the provided
+// |password_form|.
+//
+// For Web credentials the returned origin is suitable for security display and
+// is stripped off common prefixes like "m.", "mobile." or "www.". Furthermore
+// the link URL is set to the full origin of the original form.
+//
+// For Android credentials the returned origin is set to the Play Store name
+// if available, otherwise it is the reversed package name (e.g.
+// com.example.android gets transformed to android.example.com).
+std::pair<std::string, GURL> GetShownOriginAndLinkUrl(
+ const autofill::PasswordForm& password_form);
// Returns a string suitable for security display to the user (just like
// |FormatUrlForSecurityDisplay| with OMIT_HTTP_AND_HTTPS) based on origin of
diff --git a/chromium/components/password_manager/core/browser/password_ui_utils_unittest.cc b/chromium/components/password_manager/core/browser/password_ui_utils_unittest.cc
index 40f7e1768cc..4ba9a9eb588 100644
--- a/chromium/components/password_manager/core/browser/password_ui_utils_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_ui_utils_unittest.cc
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/password_ui_utils.h"
+#include <tuple>
+
+#include "components/autofill/core/common/password_form.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
namespace password_manager {
@@ -37,44 +40,32 @@ TEST(GetShownOriginTest, RemovePrefixes) {
}
}
-TEST(GetShownOriginAndLinkUrlTest, OriginFromAndroidForm_NoAffiliatedRealm) {
+TEST(GetShownOriginAndLinkUrlTest, OriginFromAndroidForm_NoAppDisplayName) {
autofill::PasswordForm android_form;
- android_form.signon_realm =
- "android://"
- "m3HSJL1i83hdltRq0-o9czGb-8KJDKra4t_"
- "3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw=="
- "@com.example.android";
- android_form.affiliated_web_realm = std::string();
-
- bool is_android_uri;
+ android_form.signon_realm = "android://hash@com.example.android";
+ android_form.app_display_name.clear();
+
+ std::string shown_origin;
GURL link_url;
- bool origin_is_clickable;
- EXPECT_EQ("android://com.example.android",
- GetShownOriginAndLinkUrl(android_form, &is_android_uri, &link_url,
- &origin_is_clickable));
- EXPECT_TRUE(is_android_uri);
- EXPECT_FALSE(origin_is_clickable);
- EXPECT_EQ(GURL(android_form.signon_realm), link_url);
+ std::tie(shown_origin, link_url) = GetShownOriginAndLinkUrl(android_form);
+
+ EXPECT_EQ("android.example.com", shown_origin);
+ EXPECT_EQ("https://play.google.com/store/apps/details?id=com.example.android",
+ link_url.spec());
}
-TEST(GetShownOriginAndLinkUrlTest, OriginFromAndroidForm_WithAffiliatedRealm) {
+TEST(GetShownOriginAndLinkUrlTest, OriginFromAndroidForm_WithAppDisplayName) {
autofill::PasswordForm android_form;
- android_form.signon_realm =
- "android://"
- "m3HSJL1i83hdltRq0-o9czGb-8KJDKra4t_"
- "3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw=="
- "@com.example.android";
- android_form.affiliated_web_realm = "https://example.com/";
-
- bool is_android_uri;
+ android_form.signon_realm = "android://hash@com.example.android";
+ android_form.app_display_name = "Example Android App";
+
+ std::string shown_origin;
GURL link_url;
- bool origin_is_clickable;
- EXPECT_EQ("example.com",
- GetShownOriginAndLinkUrl(android_form, &is_android_uri, &link_url,
- &origin_is_clickable));
- EXPECT_TRUE(is_android_uri);
- EXPECT_TRUE(origin_is_clickable);
- EXPECT_EQ(GURL(android_form.affiliated_web_realm), link_url);
+ std::tie(shown_origin, link_url) = GetShownOriginAndLinkUrl(android_form);
+
+ EXPECT_EQ("Example Android App", shown_origin);
+ EXPECT_EQ("https://play.google.com/store/apps/details?id=com.example.android",
+ link_url.spec());
}
TEST(GetShownOriginAndLinkUrlTest, OriginFromNonAndroidForm) {
@@ -82,15 +73,12 @@ TEST(GetShownOriginAndLinkUrlTest, OriginFromNonAndroidForm) {
form.signon_realm = "https://example.com/";
form.origin = GURL("https://example.com/login?ref=1");
- bool is_android_uri;
+ std::string shown_origin;
GURL link_url;
- bool origin_is_clickable;
- EXPECT_EQ("example.com",
- GetShownOriginAndLinkUrl(form, &is_android_uri, &link_url,
- &origin_is_clickable));
- EXPECT_FALSE(is_android_uri);
- EXPECT_TRUE(origin_is_clickable);
- EXPECT_EQ(form.origin, link_url);
+ std::tie(shown_origin, link_url) = GetShownOriginAndLinkUrl(form);
+
+ EXPECT_EQ("example.com", shown_origin);
+ EXPECT_EQ(GURL("https://example.com/login?ref=1"), link_url);
}
TEST(SplitByDotAndReverseTest, ReversedHostname) {
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.cc b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.cc
new file mode 100644
index 00000000000..e20be71968e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.cc
@@ -0,0 +1,88 @@
+// 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_data.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/values.h"
+
+namespace password_manager {
+namespace {
+
+constexpr char kGetLoginsRelation[] =
+ "delegate_permission/common.get_login_creds";
+constexpr char kWebNamespace[] = "web";
+
+struct Target {
+ std::string target_namespace;
+ std::string site;
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<Target>* converter) {
+ converter->RegisterStringField("namespace", &Target::target_namespace);
+ converter->RegisterStringField("site", &Target::site);
+ }
+};
+
+struct Statement {
+ std::string include;
+ std::vector<std::unique_ptr<std::string>> relations;
+ Target target;
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<Statement>* converter) {
+ converter->RegisterStringField("include", &Statement::include);
+ converter->RegisterRepeatedString("relation", &Statement::relations);
+ converter->RegisterNestedField("target", &Statement::target);
+ }
+};
+
+} // namespace
+
+AssetLinkData::AssetLinkData() = default;
+
+AssetLinkData::AssetLinkData(AssetLinkData&& other) noexcept = default;
+
+AssetLinkData::~AssetLinkData() = default;
+
+AssetLinkData& AssetLinkData::operator=(AssetLinkData&& other) = default;
+
+bool AssetLinkData::Parse(const std::string& data) {
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(data);
+ if (!value || !value->is_list())
+ return false;
+ const base::Value::ListStorage& list_storage = value->GetList();
+ base::JSONValueConverter<Statement> converter;
+ for (const auto& item : list_storage) {
+ Statement statement;
+ if (converter.Convert(item, &statement)) {
+ if (!statement.include.empty()) {
+ // The statement is an 'include' statement.
+ GURL include(statement.include);
+ if (include.is_valid() && include.SchemeIs(url::kHttpsScheme))
+ includes_.push_back(std::move(include));
+ } else if (std::any_of(statement.relations.begin(),
+ statement.relations.end(),
+ [](const std::unique_ptr<std::string>& relation) {
+ return *relation == kGetLoginsRelation;
+ })) {
+ // 'get_login_creds' statement. Only web HTTPS targets are interesting.
+ if (statement.target.target_namespace == kWebNamespace) {
+ GURL site(statement.target.site);
+ if (site.is_valid() && site.SchemeIs(url::kHttpsScheme))
+ targets_.push_back(std::move(site));
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.h b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.h
new file mode 100644
index 00000000000..0f3766299ed
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data.h
@@ -0,0 +1,44 @@
+// 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_DATA_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_DATA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "url/gurl.h"
+
+namespace password_manager {
+
+// The class parses an asset link file. The spec for the format is
+// https://github.com/google/digitalassetlinks/blob/master/well-known/details.md
+// The class cares only about two types of statements:
+// - includes. Those are just a reference to a file to be loaded and parsed.
+// - "get_login_creds" permission to a web page. That means that the target is
+// allowed to get the credentials saved for the source.
+// Only HTTPS URLs are taken into account.
+class AssetLinkData {
+ public:
+ AssetLinkData();
+ AssetLinkData(AssetLinkData&& other) noexcept;
+ ~AssetLinkData();
+
+ AssetLinkData& operator=(AssetLinkData&& other);
+
+ bool Parse(const std::string& data);
+
+ const std::vector<GURL>& includes() const { return includes_; }
+ const std::vector<GURL>& targets() const { return targets_; }
+
+ private:
+ std::vector<GURL> includes_;
+ std::vector<GURL> targets_;
+
+ DISALLOW_COPY_AND_ASSIGN(AssetLinkData);
+};
+
+} // namespace password_manager
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_DATA_H_
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc
new file mode 100644
index 00000000000..2a8ca787620
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc
@@ -0,0 +1,226 @@
+// 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_data.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;
+using ::testing::UnorderedElementsAre;
+
+TEST(AssetLinkData, NonJSON) {
+ constexpr char json[] = u8R"([trash])";
+ AssetLinkData data;
+ EXPECT_FALSE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, NotList) {
+ constexpr char json[] =
+ u8R"({
+ "include": "https://example/.well-known/assetlinks.json"
+ })";
+ AssetLinkData data;
+ EXPECT_FALSE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, IncludeWrongValue) {
+ constexpr char json[] =
+ u8R"([{
+ "include": 24
+ }])";
+ AssetLinkData data;
+ EXPECT_FALSE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, IncludeFile) {
+ constexpr char json[] =
+ u8R"([{
+ "include": "https://example/.well-known/assetlinks.json"
+ }])";
+ AssetLinkData data;
+ EXPECT_TRUE(data.Parse(json));
+ EXPECT_THAT(data.includes(),
+ ElementsAre(GURL("https://example/.well-known/assetlinks.json")));
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, IncludeHTTPFile) {
+ constexpr char json[] =
+ u8R"([{
+ "include": "http://example/.well-known/assetlinks.json"
+ }])";
+ AssetLinkData data;
+ EXPECT_TRUE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, IncludeInvalidFile) {
+ constexpr char json[] =
+ u8R"([{
+ "include": "www.example/assetlinks.json"
+ }])";
+ AssetLinkData data;
+ data.Parse(json);
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, HandleURLsPermission) {
+ constexpr char json[] =
+ u8R"([{
+ "relation": ["delegate_permission/common.handle_all_urls"],
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.com"
+ }
+ },{
+ "relation": ["delegate_permission/common.handle_all_urls"],
+ "target": {
+ "namespace": "android_app",
+ "package_name": "org.digitalassetlinks.sampleapp",
+ "sha256_cert_fingerprints": ["10:39:38:EE:45:37:E5:9E:8E:E7:92:F6"]
+ }
+ }])";
+ AssetLinkData data;
+ EXPECT_TRUE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, BrokenRelation) {
+ constexpr char json[] =
+ u8R"([{
+ "relation": "delegate_permission/common.get_login_creds",
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.com"
+ }
+ }])";
+ AssetLinkData data;
+ EXPECT_FALSE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), IsEmpty());
+}
+
+TEST(AssetLinkData, GetLoginCredsPermission) {
+ constexpr char json[] =
+ u8R"([{
+ "relation": ["delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.com"
+ }
+ },{
+ "relation": ["delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "android_app",
+ "package_name": "org.digitalassetlinks.sampleapp",
+ "sha256_cert_fingerprints": ["10:39:38:EE:45:37:E5:9E:8E:E7:92:F6"]
+ }
+ },{
+ "relation": ["delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.ru"
+ }
+ },{
+ "relation": ["delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "web",
+ "site": "http://example.com"
+ }
+ }
+ ])";
+ AssetLinkData data;
+ EXPECT_TRUE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(),
+ UnorderedElementsAre(GURL("https://www.google.com"),
+ GURL("https://www.google.ru")));
+}
+
+TEST(AssetLinkData, MultiplePermissions) {
+ constexpr char json[] =
+ u8R"([{
+ "relation": ["something","delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.com"
+ }
+ },{
+ "relation": ["delegate_permission/common.get_login_creds",
+ "delegate_permission/common.handle_all_urls"],
+ "target": {
+ "namespace": "android_app",
+ "package_name": "org.digitalassetlinks.sampleapp",
+ "sha256_cert_fingerprints": ["10:39:38:EE:45:37:E5:9E:8E:E7:92:F6"]
+ }
+ },{
+ "relation": ["delegate_permission/common.get_login_creds", "something"],
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.ru"
+ }
+ },{
+ "relation": ["trash"],
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.de"
+ }
+ }
+ ])";
+ AssetLinkData data;
+ EXPECT_TRUE(data.Parse(json));
+ EXPECT_THAT(data.includes(), IsEmpty());
+ EXPECT_THAT(data.targets(), ElementsAre(GURL("https://www.google.com"),
+ GURL("https://www.google.ru")));
+}
+
+TEST(AssetLinkData, MixedStatements) {
+ constexpr char json[] =
+ u8R"([{
+ "relation": ["delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.com"
+ }
+ },{
+ "relation": ["unknown", "unknown"],
+ "target": {
+ "key": "value",
+ "key2": 12
+ }
+ },{
+ "include": "https://go/assetlinks.json"
+ },{
+ "relation": ["delegate_permission/common.get_login_creds"],
+ "key": 1234,
+ "target": {
+ "namespace": "web",
+ "site": "https://www.google.de",
+ "key": "additional_value"
+ }
+ }
+ ])";
+ AssetLinkData data;
+ EXPECT_TRUE(data.Parse(json));
+ EXPECT_THAT(data.includes(), ElementsAre(GURL("https://go/assetlinks.json")));
+ EXPECT_THAT(data.targets(), ElementsAre(GURL("https://www.google.com"),
+ GURL("https://www.google.de")));
+}
+
+} // namespace
+} // namespace password_manager
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
new file mode 100644
index 00000000000..22ca913a1bd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc
@@ -0,0 +1,99 @@
+// 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 <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.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: false
+ 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 = base::MakeUnique<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,
+ base::Passed(&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
new file mode 100644
index 00000000000..2beecdf7377
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h
@@ -0,0 +1,77 @@
+// 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
new file mode 100644
index 00000000000..df4fb29ecf2
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc
@@ -0,0 +1,165 @@
+// 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 base::MakeUnique<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/sql_table_builder.cc b/chromium/components/password_manager/core/browser/sql_table_builder.cc
index 3c11742188e..709c2add5d9 100644
--- a/chromium/components/password_manager/core/browser/sql_table_builder.cc
+++ b/chromium/components/password_manager/core/browser/sql_table_builder.cc
@@ -5,10 +5,13 @@
#include "components/password_manager/core/browser/sql_table_builder.h"
#include <algorithm>
+#include <set>
#include <utility>
-#include <vector>
#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "sql/connection.h"
#include "sql/transaction.h"
@@ -26,9 +29,15 @@ void Append(const std::string& name, std::string* list_of_names) {
} // namespace
+// static
+unsigned constexpr SQLTableBuilder::kInvalidVersion;
+
struct SQLTableBuilder::Column {
std::string name;
std::string type;
+ // Whether this column is part of the table's PRIMARY KEY constraint.
+ bool part_of_primary_key;
+ // Whether this column is part of the table's UNIQUE constraint.
bool part_of_unique_key;
// The first version this column is part of.
unsigned min_version;
@@ -44,39 +53,62 @@ struct SQLTableBuilder::Column {
bool gets_previous_data;
};
-SQLTableBuilder::SQLTableBuilder() = default;
+struct SQLTableBuilder::Index {
+ // The name of this index.
+ std::string name;
+ // The names of columns this index is built from.
+ std::vector<std::string> columns;
+ // The first version this index is part of.
+ unsigned min_version;
+ // The last version this index is part of. The value of kInvalidVersion
+ // means that it is part of all versions since |min_version|.
+ unsigned max_version;
+};
+
+SQLTableBuilder::SQLTableBuilder(const std::string& table_name)
+ : table_name_(table_name) {}
SQLTableBuilder::~SQLTableBuilder() = default;
-void SQLTableBuilder::AddColumn(const std::string& name,
- const std::string& type) {
- DCHECK(FindLastByName(name) == columns_.rend());
- Column column = {name, type, false, sealed_version_ + 1, kInvalidVersion,
- false};
- columns_.push_back(std::move(column));
+void SQLTableBuilder::AddColumn(std::string name, std::string type) {
+ DCHECK(FindLastColumnByName(name) == columns_.rend());
+ columns_.push_back({std::move(name), std::move(type), false, false,
+ sealed_version_ + 1, kInvalidVersion, false});
}
-void SQLTableBuilder::AddColumnToUniqueKey(const std::string& name,
- const std::string& type) {
- AddColumn(name, type);
+void SQLTableBuilder::AddColumnToPrimaryKey(std::string name,
+ std::string type) {
+ AddColumn(std::move(name), std::move(type));
+ columns_.back().part_of_primary_key = true;
+}
+
+void SQLTableBuilder::AddColumnToUniqueKey(std::string name, std::string type) {
+ AddColumn(std::move(name), std::move(type));
columns_.back().part_of_unique_key = true;
}
void SQLTableBuilder::RenameColumn(const std::string& old_name,
const std::string& new_name) {
- auto old_column = FindLastByName(old_name);
+ auto old_column = FindLastColumnByName(old_name);
DCHECK(old_column != columns_.rend());
-
if (old_name == new_name) // The easy case.
return;
- DCHECK_NE("signon_realm", old_name);
+ DCHECK(FindLastColumnByName(new_name) == columns_.rend());
+ // Check there is no index in the current version that references |old_name|.
+ DCHECK(std::none_of(indices_.begin(), indices_.end(),
+ [this, &old_name](const Index& index) {
+ return index.max_version == kInvalidVersion &&
+ base::ContainsValue(index.columns, old_name);
+ }));
+
if (sealed_version_ != kInvalidVersion &&
old_column->min_version <= sealed_version_) {
// This column exists in the last sealed version. Therefore it cannot be
// just replaced, it needs to be kept for generating the migration code.
Column new_column = {new_name,
old_column->type,
+ old_column->part_of_primary_key,
old_column->part_of_unique_key,
sealed_version_ + 1,
kInvalidVersion,
@@ -94,9 +126,14 @@ void SQLTableBuilder::RenameColumn(const std::string& old_name,
// Removes column |name|. |name| must have been added in the past.
void SQLTableBuilder::DropColumn(const std::string& name) {
- auto column = FindLastByName(name);
+ auto column = FindLastColumnByName(name);
DCHECK(column != columns_.rend());
- DCHECK_NE("signon_realm", name);
+ // Check there is no index in the current version that references |old_name|.
+ DCHECK(std::none_of(indices_.begin(), indices_.end(),
+ [this, &name](const Index& index) {
+ return index.max_version == kInvalidVersion &&
+ base::ContainsValue(index.columns, name);
+ }));
if (sealed_version_ != kInvalidVersion &&
column->min_version <= sealed_version_) {
// This column exists in the last sealed version. Therefore it cannot be
@@ -110,24 +147,96 @@ void SQLTableBuilder::DropColumn(const std::string& name) {
}
}
+void SQLTableBuilder::AddIndex(std::string name,
+ std::vector<std::string> columns) {
+ DCHECK(!columns.empty());
+ // Check if all entries of |columns| are unique.
+ DCHECK_EQ(std::set<std::string>(columns.begin(), columns.end()).size(),
+ columns.size());
+ // |name| must not have been added before.
+ DCHECK(FindLastIndexByName(name) == indices_.rend());
+ // Check that all referenced columns are present in the last version by making
+ // sure that the inner predicate applies to all columns names in |columns|.
+ DCHECK(std::all_of(
+ columns.begin(), columns.end(), [this](const std::string& column_name) {
+ // Check if there is any column with the required name which is also
+ // present in the latest version. Note that we don't require the last
+ // version to be sealed.
+ return std::any_of(columns_.begin(), columns_.end(),
+ [&column_name](const Column& column) {
+ return column.name == column_name &&
+ column.max_version == kInvalidVersion;
+ });
+ }));
+ indices_.push_back({std::move(name), std::move(columns), sealed_version_ + 1,
+ kInvalidVersion});
+}
+
+void SQLTableBuilder::RenameIndex(const std::string& old_name,
+ const std::string& new_name) {
+ auto old_index = FindLastIndexByName(old_name);
+ // Check that there is an index with the old name.
+ DCHECK(old_index != indices_.rend());
+ if (old_name == new_name) // The easy case.
+ return;
+
+ // Check that there is no index with the new name.
+ DCHECK(FindLastIndexByName(new_name) == indices_.rend());
+ // Check that there is at least one sealed version.
+ DCHECK_NE(sealed_version_, kInvalidVersion);
+ // Check that the old index has been added before the last version was sealed.
+ DCHECK_LE(old_index->min_version, sealed_version_);
+ // Check that the old index has not been renamed or deleted yet.
+ DCHECK_EQ(old_index->max_version, kInvalidVersion);
+ // This index exists in the last sealed version. Therefore it cannot be
+ // just replaced, it needs to be kept for generating the migration code.
+ old_index->max_version = sealed_version_;
+ // Add the new index.
+ indices_.push_back(
+ {new_name, old_index->columns, sealed_version_ + 1, kInvalidVersion});
+}
+
+void SQLTableBuilder::DropIndex(const std::string& name) {
+ auto index = FindLastIndexByName(name);
+ // Check that an index with the name exists.
+ DCHECK(index != indices_.rend());
+ // Check that this index exists in the last sealed version and hasn't been
+ // renamed or deleted yet.
+ // Check that there is at least one sealed version.
+ DCHECK_NE(sealed_version_, kInvalidVersion);
+ // Check that the index has been added before the last version was sealed.
+ DCHECK_LE(index->min_version, sealed_version_);
+ // Check that the index has not been renamed or deleted yet.
+ DCHECK_EQ(index->max_version, kInvalidVersion);
+ // This index exists in the last sealed version. Therefore it cannot be
+ // just deleted, it needs to be kept for generating the migration code.
+ index->max_version = sealed_version_;
+}
+
unsigned SQLTableBuilder::SealVersion() {
- DCHECK(FindLastByName("signon_realm") != columns_.rend());
if (sealed_version_ == kInvalidVersion) {
- DCHECK_EQ(std::string(), unique_constraint_);
- // First sealed version, time to compute the UNIQUE string.
+ DCHECK_EQ(std::string(), constraints_);
+ // First sealed version, time to compute the PRIMARY KEY and UNIQUE string.
+ std::string primary_key;
std::string unique_key;
for (const Column& column : columns_) {
+ if (column.part_of_primary_key)
+ Append(column.name, &primary_key);
if (column.part_of_unique_key)
Append(column.name, &unique_key);
}
- DCHECK(!unique_key.empty());
- unique_constraint_ = "UNIQUE (" + unique_key + ")";
+ DCHECK(!primary_key.empty() || !unique_key.empty());
+ if (!primary_key.empty())
+ Append("PRIMARY KEY (" + primary_key + ")", &constraints_);
+ if (!unique_key.empty())
+ Append("UNIQUE (" + unique_key + ")", &constraints_);
}
- DCHECK(!unique_constraint_.empty());
+ DCHECK(!constraints_.empty());
return ++sealed_version_;
}
-bool SQLTableBuilder::MigrateFrom(unsigned old_version, sql::Connection* db) {
+bool SQLTableBuilder::MigrateFrom(unsigned old_version,
+ sql::Connection* db) const {
for (; old_version < sealed_version_; ++old_version) {
if (!MigrateToNextFrom(old_version, db))
return false;
@@ -136,25 +245,39 @@ bool SQLTableBuilder::MigrateFrom(unsigned old_version, sql::Connection* db) {
return true;
}
-bool SQLTableBuilder::CreateTable(sql::Connection* db) {
+bool SQLTableBuilder::CreateTable(sql::Connection* db) const {
DCHECK(IsVersionLastAndSealed(sealed_version_));
- DCHECK(!unique_constraint_.empty());
+ DCHECK(!constraints_.empty());
- if (db->DoesTableExist("logins"))
+ if (db->DoesTableExist(table_name_.c_str()))
return true;
std::string names; // Names and types of the current columns.
for (const Column& column : columns_) {
- if (IsInLastVersion(column))
+ if (IsColumnInLastVersion(column))
Append(column.name + " " + column.type, &names);
}
+ std::vector<std::string>
+ create_index_sqls; // CREATE INDEX statements for the current indices.
+ for (const Index& index : indices_) {
+ if (IsIndexInLastVersion(index)) {
+ create_index_sqls.push_back(base::StringPrintf(
+ "CREATE INDEX %s ON %s (%s)", index.name.c_str(), table_name_.c_str(),
+ base::JoinString(index.columns, ", ").c_str()));
+ }
+ }
+
sql::Transaction transaction(db);
return transaction.Begin() &&
- db->Execute(
- ("CREATE TABLE logins (" + names + ", " + unique_constraint_ + ")")
- .c_str()) &&
- db->Execute("CREATE INDEX logins_signon ON logins (signon_realm)") &&
+ db->Execute(base::StringPrintf("CREATE TABLE %s (%s, %s)",
+ table_name_.c_str(), names.c_str(),
+ constraints_.c_str())
+ .c_str()) &&
+ std::all_of(create_index_sqls.begin(), create_index_sqls.end(),
+ [&db](const std::string& sql) {
+ return db->Execute(sql.c_str());
+ }) &&
transaction.Commit();
}
@@ -162,7 +285,7 @@ std::string SQLTableBuilder::ListAllColumnNames() const {
DCHECK(IsVersionLastAndSealed(sealed_version_));
std::string result;
for (const Column& column : columns_) {
- if (IsInLastVersion(column))
+ if (IsColumnInLastVersion(column))
Append(column.name, &result);
}
return result;
@@ -172,7 +295,8 @@ std::string SQLTableBuilder::ListAllNonuniqueKeyNames() const {
DCHECK(IsVersionLastAndSealed(sealed_version_));
std::string result;
for (const Column& column : columns_) {
- if (IsInLastVersion(column) && !column.part_of_unique_key)
+ if (IsColumnInLastVersion(column) &&
+ !(column.part_of_primary_key || column.part_of_unique_key))
Append(column.name + "=?", &result);
}
return result;
@@ -182,7 +306,8 @@ std::string SQLTableBuilder::ListAllUniqueKeyNames() const {
DCHECK(IsVersionLastAndSealed(sealed_version_));
std::string result;
for (const Column& column : columns_) {
- if (IsInLastVersion(column) && column.part_of_unique_key) {
+ if (IsColumnInLastVersion(column) &&
+ (column.part_of_primary_key || column.part_of_unique_key)) {
if (!result.empty())
result += " AND ";
result += column.name + "=?";
@@ -191,19 +316,50 @@ std::string SQLTableBuilder::ListAllUniqueKeyNames() const {
return result;
}
+std::vector<base::StringPiece> SQLTableBuilder::AllPrimaryKeyNames() const {
+ DCHECK(IsVersionLastAndSealed(sealed_version_));
+ std::vector<base::StringPiece> result;
+ result.reserve(columns_.size());
+ for (const Column& column : columns_) {
+ if (IsColumnInLastVersion(column) && column.part_of_primary_key) {
+ result.emplace_back(column.name);
+ }
+ }
+ return result;
+}
+
+std::vector<base::StringPiece> SQLTableBuilder::AllIndexNames() const {
+ DCHECK(IsVersionLastAndSealed(sealed_version_));
+ std::vector<base::StringPiece> result;
+ result.reserve(indices_.size());
+ for (const Index& index : indices_) {
+ if (IsIndexInLastVersion(index)) {
+ result.emplace_back(index.name);
+ }
+ }
+ return result;
+}
+
size_t SQLTableBuilder::NumberOfColumns() const {
DCHECK(IsVersionLastAndSealed(sealed_version_));
return base::checked_cast<size_t>(std::count_if(
columns_.begin(), columns_.end(),
- [this](const Column& column) { return (IsInLastVersion(column)); }));
+ [this](const Column& column) { return IsColumnInLastVersion(column); }));
+}
+
+size_t SQLTableBuilder::NumberOfIndices() const {
+ DCHECK(IsVersionLastAndSealed(sealed_version_));
+ return base::checked_cast<size_t>(std::count_if(
+ indices_.begin(), indices_.end(),
+ [this](const Index& index) { return IsIndexInLastVersion(index); }));
}
bool SQLTableBuilder::MigrateToNextFrom(unsigned old_version,
- sql::Connection* db) {
+ sql::Connection* db) const {
DCHECK_LT(old_version, sealed_version_);
DCHECK_GE(old_version, 0u);
DCHECK(IsVersionLastAndSealed(sealed_version_));
- DCHECK(!unique_constraint_.empty());
+ DCHECK(!constraints_.empty());
// Names of columns from old version, values of which are copied.
std::string old_names;
@@ -217,11 +373,11 @@ bool SQLTableBuilder::MigrateToNextFrom(unsigned old_version,
for (auto column = columns_.begin(); column != columns_.end(); ++column) {
if (column->max_version == old_version) {
+ DCHECK(!column->part_of_primary_key);
DCHECK(!column->part_of_unique_key);
// This column was deleted after |old_version|. It can have two reasons:
needs_temp_table = true;
- auto next_column = column;
- ++next_column;
+ auto next_column = std::next(column);
if (next_column != columns_.end() && next_column->gets_previous_data) {
// (1) The column is being renamed.
DCHECK_EQ(column->type, next_column->type);
@@ -234,6 +390,7 @@ bool SQLTableBuilder::MigrateToNextFrom(unsigned old_version,
}
} else if (column->min_version == old_version + 1) {
// This column was added after old_version.
+ DCHECK(!column->part_of_primary_key);
DCHECK(!column->part_of_unique_key);
added_names.push_back(column->name + " " + column->type);
} else if (column->min_version <= old_version &&
@@ -256,64 +413,123 @@ bool SQLTableBuilder::MigrateToNextFrom(unsigned old_version,
// Foreign key constraints are not enabled for the login database, so no
// PRAGMA foreign_keys=off needed.
+ const std::string temp_table_name = "temp_" + table_name_;
+
sql::Transaction transaction(db);
- if (!transaction.Begin() ||
- !db->Execute(("CREATE TABLE temp_logins (" + new_names + ", " +
- unique_constraint_ + ")")
- .c_str()) ||
- !db->Execute(("INSERT OR REPLACE INTO temp_logins SELECT " + old_names +
- " FROM logins")
- .c_str()) ||
- !db->Execute("DROP TABLE logins") ||
- !db->Execute("ALTER TABLE temp_logins RENAME TO logins") ||
- !db->Execute("CREATE INDEX logins_signon ON logins (signon_realm)") ||
- !transaction.Commit()) {
+ if (!(transaction.Begin() &&
+ db->Execute(base::StringPrintf(
+ "CREATE TABLE %s (%s, %s)", temp_table_name.c_str(),
+ new_names.c_str(), constraints_.c_str())
+ .c_str()) &&
+ db->Execute(
+ base::StringPrintf("INSERT OR REPLACE INTO %s SELECT %s FROM %s",
+ temp_table_name.c_str(), old_names.c_str(),
+ table_name_.c_str())
+ .c_str()) &&
+ db->Execute(base::StringPrintf("DROP TABLE %s", table_name_.c_str())
+ .c_str()) &&
+ db->Execute(base::StringPrintf("ALTER TABLE %s RENAME TO %s",
+ temp_table_name.c_str(),
+ table_name_.c_str())
+ .c_str()) &&
+ transaction.Commit())) {
return false;
}
}
if (!added_names.empty()) {
sql::Transaction transaction(db);
- if (!transaction.Begin())
+ if (!(transaction.Begin() &&
+ std::all_of(added_names.begin(), added_names.end(),
+ [this, &db](const std::string& name) {
+ return db->Execute(
+ base::StringPrintf("ALTER TABLE %s ADD COLUMN %s",
+ table_name_.c_str(),
+ name.c_str())
+ .c_str());
+ }) &&
+ transaction.Commit())) {
return false;
- for (const std::string& name : added_names) {
- if (!db->Execute(("ALTER TABLE logins ADD COLUMN " + name).c_str()))
- return false;
}
- if (!transaction.Commit())
+ }
+
+ return MigrateIndicesToNextFrom(old_version, db);
+}
+
+bool SQLTableBuilder::MigrateIndicesToNextFrom(unsigned old_version,
+ sql::Connection* db) const {
+ DCHECK_LT(old_version, sealed_version_);
+ DCHECK(IsVersionLastAndSealed(sealed_version_));
+ sql::Transaction transaction(db);
+ if (!transaction.Begin())
+ return false;
+
+ for (const auto& index : indices_) {
+ std::string sql;
+ if (index.max_version <= old_version) {
+ // Index is not supposed to exist in the new version.
+ sql = base::StringPrintf("DROP INDEX IF EXISTS %s", index.name.c_str());
+ } else if (index.min_version <= old_version + 1) {
+ // Index is supposed to exist in the new version.
+ sql = base::StringPrintf("CREATE INDEX IF NOT EXISTS %s ON %s (%s)",
+ index.name.c_str(), table_name_.c_str(),
+ base::JoinString(index.columns, ", ").c_str());
+ } else {
+ continue;
+ }
+
+ if (!db->Execute(sql.c_str()))
return false;
}
- return true;
+ return transaction.Commit();
}
-std::list<SQLTableBuilder::Column>::reverse_iterator
-SQLTableBuilder::FindLastByName(const std::string& name) {
+std::vector<SQLTableBuilder::Column>::reverse_iterator
+SQLTableBuilder::FindLastColumnByName(const std::string& name) {
return std::find_if(
columns_.rbegin(), columns_.rend(),
[&name](const Column& column) { return name == column.name; });
}
+std::vector<SQLTableBuilder::Index>::reverse_iterator
+SQLTableBuilder::FindLastIndexByName(const std::string& name) {
+ return std::find_if(
+ indices_.rbegin(), indices_.rend(),
+ [&name](const Index& index) { return name == index.name; });
+}
+
bool SQLTableBuilder::IsVersionLastAndSealed(unsigned version) const {
// Is |version| the last sealed one?
if (sealed_version_ != version)
return false;
// Is the current version the last sealed one? In other words, is there
- // either a column added past the sealed version (min_version > sealed) or
- // deleted one version after the sealed (max_version == sealed)?
- return columns_.end() ==
- std::find_if(columns_.begin(), columns_.end(),
+ // neither a column or index added past the sealed version (min_version >
+ // sealed) nor deleted one version after the sealed (max_version == sealed)?
+ return std::none_of(columns_.begin(), columns_.end(),
[this](const Column& column) {
return column.min_version > sealed_version_ ||
column.max_version == sealed_version_;
+ }) &&
+ std::none_of(indices_.begin(), indices_.end(),
+ [this](const Index& index) {
+ return index.min_version > sealed_version_ ||
+ index.max_version == sealed_version_;
});
}
-bool SQLTableBuilder::IsInLastVersion(const Column& column) const {
+bool SQLTableBuilder::IsColumnInLastVersion(const Column& column) const {
DCHECK(IsVersionLastAndSealed(sealed_version_));
return (column.min_version <= sealed_version_ &&
(column.max_version == kInvalidVersion ||
column.max_version >= sealed_version_));
}
+bool SQLTableBuilder::IsIndexInLastVersion(const Index& index) const {
+ DCHECK(IsVersionLastAndSealed(sealed_version_));
+ return (index.min_version <= sealed_version_ &&
+ (index.max_version == kInvalidVersion ||
+ index.max_version >= sealed_version_));
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/sql_table_builder.h b/chromium/components/password_manager/core/browser/sql_table_builder.h
index bac1394f123..da1c8920db1 100644
--- a/chromium/components/password_manager/core/browser/sql_table_builder.h
+++ b/chromium/components/password_manager/core/browser/sql_table_builder.h
@@ -6,10 +6,11 @@
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SQL_TABLE_BUILDER_H_
#include <limits>
-#include <list>
#include <string>
+#include <vector>
#include "base/macros.h"
+#include "base/strings/string_piece.h"
namespace sql {
class Connection;
@@ -20,16 +21,12 @@ namespace password_manager {
// Use this class to represent the versioned evolution of a SQLite table
// structure and generate creating and migrating statements for it.
//
-// Two values are currently hard-coded, because they are the same in the
-// current use-cases:
-// * The table name is hard-coded as "logins".
-// * The index is built for column "signon_realm" which is expected to exist.
-//
// Usage example:
//
-// SQLTableBuilder builder;
+// SQLTableBuilder builder("logins");
//
// // First describe a couple of versions:
+// builder.AddColumnToPrimaryKey("id", "INTEGER");
// builder.AddColumn("name", "VARCHAR");
// builder.AddColumn("icon", "VARCHAR");
// builder.AddColumn("password", "VARCHAR NOT NULL");
@@ -40,89 +37,139 @@ namespace password_manager {
// DCHECK_EQ(1u, version);
//
// // Now, assuming that |db| has a table "logins" in a state corresponding
-// // version 0, this will migrate it to version 1:
+// // version 0, this will migrate it to the latest version:
// sql::Connection* db = ...;
-// builder.MigrateToNextFrom(0, db);
+// builder.MigrateFrom(0, db);
//
// // And assuming |db| has no table called "logins", this will create one
// // in a state corresponding the latest sealed version:
// builder.CreateTable(db);
class SQLTableBuilder {
public:
- // Create the builder for "logins".
- SQLTableBuilder();
+ // Create the builder for an arbitrary table name.
+ explicit SQLTableBuilder(const std::string& table_name);
~SQLTableBuilder();
// Adds a column in the table description, with |name| and |type|. |name|
// must not have been added to the table in this version before.
- void AddColumn(const std::string& name, const std::string& type);
+ void AddColumn(std::string name, std::string type);
+
+ // As AddColumn but also adds column |name| to the primary key of the table.
+ // SealVersion must not have been called yet (the builder does not currently
+ // support migration code for changing the primary key between versions).
+ void AddColumnToPrimaryKey(std::string name, std::string type);
// As AddColumn but also adds column |name| to the unique key of the table.
// SealVersion must not have been called yet (the builder does not currently
// support migration code for changing the unique key between versions).
- void AddColumnToUniqueKey(const std::string& name, const std::string& type);
+ void AddColumnToUniqueKey(std::string name, std::string type);
- // Renames column |old_name| to |new_name|. |old_name| must have been added in
- // the past. The column "signon_realm" must not be renamed.
+ // Renames column |old_name| to |new_name|. |new_name| can not exist already.
+ // |old_name| must have been added in the past. Furthermore, there must be no
+ // index in this version referencing |old_name|.
void RenameColumn(const std::string& old_name, const std::string& new_name);
- // Removes column |name|. |name| must have been added in the past. The column
- // "signon_realm" must not be renamed.
+ // Removes column |name|. |name| must have been added in the past.
+ // Furthermore, there must be no index in this version referencing |name|.
void DropColumn(const std::string& name);
+ // Adds an index in the table description, with |name| and on columns
+ // |columns|. |name| must not have been added to the table in this version
+ // before. Furthermore, |columns| must be non-empty, and every column
+ // referenced in |columns| must be unique and exist in the current version.
+ void AddIndex(std::string name, std::vector<std::string> columns);
+
+ // Renames index |old_name| to |new_name|. |new_name| can not exist already
+ // and |old_name| must have been added in a previously sealed version, and can
+ // not have been renamed already.
+ void RenameIndex(const std::string& old_name, const std::string& new_name);
+
+ // Removes index |name|. |name| must have been added in a previously sealed
+ // version.
+ void DropIndex(const std::string& name);
+
// Increments the internal version counter and marks the current state of the
// table as that version. Returns the sealed version. Calling any of the
- // *Column* methods above will result in starting a new version which is not
- // considered sealed. The first version is 0. The column "signon_realm" must
- // be present in |columns_| every time this is called, and at least one call
+ // *Column* and *Index* methods above will result in starting a new version
+ // which is not considered sealed. The first version is 0. At least one call
// to AddColumnToUniqueKey must have been done before this is called the first
// time.
unsigned SealVersion();
// Assuming that the database connected through |db| contains a table called
- // "logins" in a state described by version |old_version|, migrates it to
+ // |table_name_| in a state described by version |old_version|, migrates it to
// the current version, which must be sealed. Returns true on success.
- bool MigrateFrom(unsigned old_version, sql::Connection* db);
+ bool MigrateFrom(unsigned old_version, sql::Connection* db) const;
- // If |db| connects to a database where table "logins" already exists,
- // this is a no-op and returns true. Otherwise, "logins" is created in a
+ // If |db| connects to a database where table |table_name_| already exists,
+ // this is a no-op and returns true. Otherwise, |table_name_| is created in a
// state described by the current version known to the builder. The current
// version must be sealed. Returns true on success.
- bool CreateTable(sql::Connection* db);
+ bool CreateTable(sql::Connection* db) const;
// Returns the comma-separated list of all column names present in the last
// version. The last version must be sealed.
std::string ListAllColumnNames() const;
- // Same as ListAllColumnNames, but for non-unique key names only, and with
- // names followed by " = ?".
+ // Same as ListAllColumnNames, but for non-unique key names only (i.e. keys
+ // that are part of neither the PRIMARY KEY nor the UNIQUE constraint), and
+ // with names followed by " = ?".
std::string ListAllNonuniqueKeyNames() const;
// Same as ListAllNonuniqueKeyNames, but for unique key names and separated by
// " AND ".
std::string ListAllUniqueKeyNames() const;
+ // Returns a vector of all PRIMARY KEY names that are present in the last
+ // version. The last version must be sealed.
+ std::vector<base::StringPiece> AllPrimaryKeyNames() const;
+
+ // Returns a vector of all index names that are present in the last
+ // version. The last version must be sealed.
+ std::vector<base::StringPiece> AllIndexNames() const;
+
// Returns the number of all columns present in the last version. The last
// version must be sealed.
size_t NumberOfColumns() const;
+ // Returns the number of all indices present in the last version. The last
+ // version must be sealed.
+ size_t NumberOfIndices() const;
+
private:
// Stores the information about one column (name, type, etc.).
struct Column;
+ // Stores the information about one index (name, columns, etc.).
+ struct Index;
+
static unsigned constexpr kInvalidVersion =
std::numeric_limits<unsigned>::max();
// Assuming that the database connected through |db| contains a table called
- // "logins" in a state described by version |old_version|, migrates it to
+ // |table_name_| in a state described by version |old_version|, migrates it to
// version |old_version + 1|. The current version known to the builder must be
// at least |old_version + 1| and sealed. Returns true on success.
- bool MigrateToNextFrom(unsigned old_version, sql::Connection* db);
+ bool MigrateToNextFrom(unsigned old_version, sql::Connection* db) const;
+
+ // Assuming that the database connected through |db| contains a table called
+ // |table_name_| in a state described by version |old_version|, migrates it
+ // indices to version |old_version + 1|. The current version known to the
+ // builder must be at least |old_version + 1| and sealed. Returns true on
+ // success.
+ bool MigrateIndicesToNextFrom(unsigned old_version,
+ sql::Connection* db) const;
// Looks up column named |name| in |columns_|. If present, returns the last
// one.
- std::list<Column>::reverse_iterator FindLastByName(const std::string& name);
+ std::vector<Column>::reverse_iterator FindLastColumnByName(
+ const std::string& name);
+
+ // Looks up index named |name| in |indices_|. If present, returns the last
+ // one.
+ std::vector<Index>::reverse_iterator FindLastIndexByName(
+ const std::string& name);
// Returns whether the last version is |version| and whether it was sealed
// (by calling SealVersion with no table modifications afterwards).
@@ -130,16 +177,25 @@ class SQLTableBuilder {
// Whether |column| is present in the last version. The last version must be
// sealed.
- bool IsInLastVersion(const Column& column) const;
+ bool IsColumnInLastVersion(const Column& column) const;
+
+ // Whether |index| is present in the last version. The last version must be
+ // sealed.
+ bool IsIndexInLastVersion(const Index& index) const;
// Last sealed version, kInvalidVersion means "none".
unsigned sealed_version_ = kInvalidVersion;
- std::list<Column> columns_; // Columns of the table, across all versions.
+ std::vector<Column> columns_; // Columns of the table, across all versions.
+
+ std::vector<Index> indices_; // Indices of the table, across all versions.
+
+ // The SQL CREATE TABLE constraints. This value is computed during sealing the
+ // first version (0).
+ std::string constraints_;
- // The "UNIQUE" part of an SQL CREATE TABLE constraint. This value is
- // computed dring sealing the first version (0).
- std::string unique_constraint_;
+ // The name of the table.
+ const std::string table_name_;
DISALLOW_COPY_AND_ASSIGN(SQLTableBuilder);
};
diff --git a/chromium/components/password_manager/core/browser/sql_table_builder_unittest.cc b/chromium/components/password_manager/core/browser/sql_table_builder_unittest.cc
index a15cf932c93..b1c1393cec5 100644
--- a/chromium/components/password_manager/core/browser/sql_table_builder_unittest.cc
+++ b/chromium/components/password_manager/core/browser/sql_table_builder_unittest.cc
@@ -9,13 +9,16 @@
#include "base/macros.h"
#include "sql/connection.h"
#include "sql/statement.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using ::testing::UnorderedElementsAre;
+
namespace password_manager {
class SQLTableBuilderTest : public testing::Test {
public:
- SQLTableBuilderTest() { Init(); }
+ SQLTableBuilderTest() : builder_("my_logins_table") { Init(); }
~SQLTableBuilderTest() override = default;
@@ -67,9 +70,8 @@ TEST_F(SQLTableBuilderTest, SealVersion_0) {
EXPECT_EQ(0u, builder()->SealVersion());
EXPECT_TRUE(builder()->MigrateFrom(0, db()));
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_TRUE(db()->DoesIndexExist("logins_signon"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "signon_realm"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "signon_realm"));
EXPECT_TRUE(IsColumnOfType("signon_realm", "VARCHAR NOT NULL"));
}
@@ -78,21 +80,42 @@ TEST_F(SQLTableBuilderTest, AddColumn) {
EXPECT_EQ(0u, builder()->SealVersion());
EXPECT_TRUE(builder()->MigrateFrom(0, db()));
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_TRUE(db()->DoesIndexExist("logins_signon"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "signon_realm"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "password_value"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "signon_realm"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "password_value"));
EXPECT_TRUE(IsColumnOfType("password_value", "BLOB"));
}
+TEST_F(SQLTableBuilderTest, AddIndex) {
+ builder()->AddIndex("my_logins_table_signon", {"signon_realm"});
+ EXPECT_EQ(0u, builder()->SealVersion());
+ EXPECT_TRUE(builder()->MigrateFrom(0, db()));
+ EXPECT_TRUE(builder()->CreateTable(db()));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesIndexExist("my_logins_table_signon"));
+}
+
+TEST_F(SQLTableBuilderTest, AddIndexOnMultipleColumns) {
+ builder()->AddColumn("column_1", "BLOB");
+ builder()->AddColumn("column_2", "BLOB");
+ builder()->AddIndex("my_index", {"column_1", "column_2"});
+ EXPECT_EQ(0u, builder()->SealVersion());
+ EXPECT_TRUE(builder()->MigrateFrom(0, db()));
+ EXPECT_TRUE(builder()->CreateTable(db()));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "column_1"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "column_2"));
+ EXPECT_TRUE(db()->DoesIndexExist("my_index"));
+}
+
TEST_F(SQLTableBuilderTest, RenameColumn_InSameVersion) {
builder()->AddColumn("old_name", "BLOB");
builder()->RenameColumn("old_name", "password_value");
EXPECT_EQ(0u, builder()->SealVersion());
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "old_name"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "password_value"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "old_name"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "password_value"));
EXPECT_TRUE(IsColumnOfType("password_value", "BLOB"));
}
@@ -102,19 +125,35 @@ TEST_F(SQLTableBuilderTest, RenameColumn_InNextVersion) {
builder()->RenameColumn("old_name", "password_value");
EXPECT_EQ(1u, builder()->SealVersion());
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "old_name"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "password_value"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "old_name"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "password_value"));
EXPECT_TRUE(IsColumnOfType("password_value", "BLOB"));
}
+// There is no test for renaming an index in the same version, as this is a
+// misuse of the API. Instead of invoking |builder()->AddIndex("foo", ...)| and
+// |builder->RenameIndex("foo", "bar")| callers should simply use
+// |builder->AddIndex("bar", ...)|.
+
+TEST_F(SQLTableBuilderTest, RenameIndex_InNextVersion) {
+ builder()->AddIndex("old_index", {"signon_realm"});
+ EXPECT_EQ(0u, builder()->SealVersion());
+ builder()->RenameIndex("old_index", "new_index");
+ EXPECT_EQ(1u, builder()->SealVersion());
+ EXPECT_TRUE(builder()->CreateTable(db()));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_FALSE(db()->DoesIndexExist("old_index"));
+ EXPECT_TRUE(db()->DoesIndexExist("new_index"));
+}
+
TEST_F(SQLTableBuilderTest, RenameColumn_SameNameInSameVersion) {
builder()->AddColumn("name", "BLOB");
builder()->RenameColumn("name", "name");
EXPECT_EQ(0u, builder()->SealVersion());
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "name"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "name"));
EXPECT_TRUE(IsColumnOfType("name", "BLOB"));
}
@@ -124,18 +163,28 @@ TEST_F(SQLTableBuilderTest, RenameColumn_SameNameInNextVersion) {
builder()->RenameColumn("name", "name");
EXPECT_EQ(1u, builder()->SealVersion());
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "name"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "name"));
EXPECT_TRUE(IsColumnOfType("name", "BLOB"));
}
+TEST_F(SQLTableBuilderTest, RenameIndex_SameNameInNextVersion) {
+ builder()->AddIndex("my_index", {"signon_realm"});
+ EXPECT_EQ(0u, builder()->SealVersion());
+ builder()->RenameIndex("my_index", "my_index");
+ EXPECT_EQ(1u, builder()->SealVersion());
+ EXPECT_TRUE(builder()->CreateTable(db()));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesIndexExist("my_index"));
+}
+
TEST_F(SQLTableBuilderTest, DropColumn_InSameVersion) {
builder()->AddColumn("password_value", "BLOB");
builder()->DropColumn("password_value");
EXPECT_EQ(0u, builder()->SealVersion());
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "password_value"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "password_value"));
}
TEST_F(SQLTableBuilderTest, DropColumn_InNextVersion) {
@@ -144,23 +193,37 @@ TEST_F(SQLTableBuilderTest, DropColumn_InNextVersion) {
builder()->DropColumn("password_value");
EXPECT_EQ(1u, builder()->SealVersion());
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "password_value"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "password_value"));
+}
+
+TEST_F(SQLTableBuilderTest, DropIndex_InNextVersion) {
+ builder()->AddIndex("my_index", {"signon_realm"});
+ EXPECT_EQ(0u, builder()->SealVersion());
+ builder()->DropIndex("my_index");
+ EXPECT_EQ(1u, builder()->SealVersion());
+ EXPECT_TRUE(builder()->CreateTable(db()));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_FALSE(db()->DoesIndexExist("my_index"));
}
TEST_F(SQLTableBuilderTest, MigrateFrom) {
// First, create a table at version 0, with some columns.
builder()->AddColumn("for_renaming", "INTEGER DEFAULT 100");
builder()->AddColumn("for_deletion", "INTEGER");
+ builder()->AddIndex("my_signon_index", {"signon_realm"});
+ builder()->AddIndex("my_changing_index_v0", {"for_renaming", "for_deletion"});
EXPECT_EQ(0u, builder()->SealVersion());
EXPECT_TRUE(builder()->CreateTable(db()));
- EXPECT_TRUE(db()->DoesTableExist("logins"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "for_renaming"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "for_deletion"));
+ EXPECT_TRUE(db()->DoesTableExist("my_logins_table"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "for_renaming"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "for_deletion"));
+ EXPECT_TRUE(db()->DoesIndexExist("my_signon_index"));
+ EXPECT_TRUE(db()->DoesIndexExist("my_changing_index_v0"));
EXPECT_TRUE(
- db()->Execute("INSERT INTO logins (signon_realm, for_renaming, "
+ db()->Execute("INSERT INTO my_logins_table (signon_realm, for_renaming, "
"for_deletion) VALUES ('abc', 123, 456)"));
- const char retrieval[] = "SELECT * FROM logins";
+ const char retrieval[] = "SELECT * FROM my_logins_table";
sql::Statement first_check(
db()->GetCachedStatement(SQL_FROM_HERE, retrieval));
EXPECT_TRUE(first_check.Step());
@@ -172,21 +235,28 @@ TEST_F(SQLTableBuilderTest, MigrateFrom) {
EXPECT_TRUE(first_check.Succeeded());
// Now, specify some modifications for version 1.
+ builder()->DropIndex("my_changing_index_v0");
builder()->RenameColumn("for_renaming", "renamed");
builder()->DropColumn("for_deletion");
builder()->AddColumn("new_column", "INTEGER DEFAULT 789");
+ builder()->AddIndex("my_changing_index_v1", {"renamed", "new_column"});
EXPECT_EQ(1u, builder()->SealVersion());
// The migration should have the following effect:
// * The renamed column should keep its non-default value.
// * The succession of column removal and addition should not result in the
// values from the deleted column to be copied to the added one.
+ // * Only the signon index and the second version of the changing index should
+ // be present in the last version.
EXPECT_TRUE(builder()->MigrateFrom(0, db()));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "for_renaming"));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "for_deletion"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "renamed"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "for_renaming"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "for_deletion"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "renamed"));
EXPECT_TRUE(IsColumnOfType("renamed", "INTEGER"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "new_column"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "new_column"));
+ EXPECT_TRUE(db()->DoesIndexExist("my_signon_index"));
+ EXPECT_FALSE(db()->DoesIndexExist("my_changing_index_v0"));
+ EXPECT_TRUE(db()->DoesIndexExist("my_changing_index_v1"));
sql::Statement second_check(
db()->GetCachedStatement(SQL_FROM_HERE, retrieval));
EXPECT_TRUE(second_check.Step());
@@ -198,7 +268,8 @@ TEST_F(SQLTableBuilderTest, MigrateFrom) {
EXPECT_TRUE(second_check.Succeeded());
}
-TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAdd) {
+TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddColumns) {
+ builder()->AddColumnToPrimaryKey("id", "INTEGER");
builder()->AddColumn("old_name", "INTEGER");
EXPECT_EQ(0u, builder()->SealVersion());
@@ -211,18 +282,45 @@ TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAdd) {
EXPECT_EQ(2u, builder()->SealVersion());
EXPECT_TRUE(builder()->MigrateFrom(0, db()));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "old_name"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "added"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "new_name"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "old_name"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "id"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "added"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "new_name"));
+ EXPECT_TRUE(IsColumnOfType("id", "INTEGER"));
EXPECT_TRUE(IsColumnOfType("added", "VARCHAR"));
EXPECT_TRUE(IsColumnOfType("new_name", "INTEGER"));
- EXPECT_EQ(3u, builder()->NumberOfColumns());
- EXPECT_EQ("signon_realm, new_name, added", builder()->ListAllColumnNames());
+ EXPECT_EQ(4u, builder()->NumberOfColumns());
+ EXPECT_EQ("signon_realm, id, new_name, added",
+ builder()->ListAllColumnNames());
EXPECT_EQ("new_name=?, added=?", builder()->ListAllNonuniqueKeyNames());
- EXPECT_EQ("signon_realm=?", builder()->ListAllUniqueKeyNames());
+ EXPECT_EQ("signon_realm=? AND id=?", builder()->ListAllUniqueKeyNames());
+ EXPECT_THAT(builder()->AllPrimaryKeyNames(), UnorderedElementsAre("id"));
}
-TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddAndDrop) {
+TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddIndices) {
+ builder()->AddIndex("old_name", {"signon_realm"});
+ EXPECT_EQ(0u, builder()->SealVersion());
+
+ EXPECT_TRUE(builder()->CreateTable(db()));
+
+ builder()->RenameIndex("old_name", "new_name");
+ EXPECT_EQ(1u, builder()->SealVersion());
+
+ builder()->AddIndex("added", {"signon_realm"});
+ EXPECT_EQ(2u, builder()->SealVersion());
+
+ EXPECT_TRUE(builder()->MigrateFrom(0, db()));
+ EXPECT_FALSE(db()->DoesIndexExist("old_name"));
+ EXPECT_TRUE(db()->DoesIndexExist("added"));
+ EXPECT_TRUE(db()->DoesIndexExist("new_name"));
+ EXPECT_EQ(2u, builder()->NumberOfIndices());
+ EXPECT_THAT(builder()->AllIndexNames(),
+ UnorderedElementsAre("new_name", "added"));
+}
+
+TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddAndDropColumns) {
+ builder()->AddColumnToPrimaryKey("pk_1", "VARCHAR NOT NULL");
+ builder()->AddColumnToPrimaryKey("pk_2", "VARCHAR NOT NULL");
builder()->AddColumnToUniqueKey("uni", "VARCHAR NOT NULL");
builder()->AddColumn("old_name", "INTEGER");
EXPECT_EQ(0u, builder()->SealVersion());
@@ -239,15 +337,44 @@ TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddAndDrop) {
EXPECT_EQ(3u, builder()->SealVersion());
EXPECT_TRUE(builder()->MigrateFrom(0, db()));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "old_name"));
- EXPECT_FALSE(db()->DoesColumnExist("logins", "added"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "uni"));
- EXPECT_TRUE(db()->DoesColumnExist("logins", "new_name"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "old_name"));
+ EXPECT_FALSE(db()->DoesColumnExist("my_logins_table", "added"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "pk_1"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "pk_2"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "uni"));
+ EXPECT_TRUE(db()->DoesColumnExist("my_logins_table", "new_name"));
EXPECT_TRUE(IsColumnOfType("new_name", "INTEGER"));
- EXPECT_EQ(3u, builder()->NumberOfColumns());
- EXPECT_EQ("signon_realm, uni, new_name", builder()->ListAllColumnNames());
+ EXPECT_EQ(5u, builder()->NumberOfColumns());
+ EXPECT_EQ("signon_realm, pk_1, pk_2, uni, new_name",
+ builder()->ListAllColumnNames());
EXPECT_EQ("new_name=?", builder()->ListAllNonuniqueKeyNames());
- EXPECT_EQ("signon_realm=? AND uni=?", builder()->ListAllUniqueKeyNames());
+ EXPECT_EQ("signon_realm=? AND pk_1=? AND pk_2=? AND uni=?",
+ builder()->ListAllUniqueKeyNames());
+ EXPECT_THAT(builder()->AllPrimaryKeyNames(),
+ UnorderedElementsAre("pk_1", "pk_2"));
+}
+
+TEST_F(SQLTableBuilderTest, MigrateFrom_RenameAndAddAndDropIndices) {
+ builder()->AddIndex("old_name", {"signon_realm"});
+ EXPECT_EQ(0u, builder()->SealVersion());
+
+ EXPECT_TRUE(builder()->CreateTable(db()));
+
+ builder()->RenameIndex("old_name", "new_name");
+ EXPECT_EQ(1u, builder()->SealVersion());
+
+ builder()->AddIndex("added", {"signon_realm"});
+ EXPECT_EQ(2u, builder()->SealVersion());
+
+ builder()->DropIndex("added");
+ EXPECT_EQ(3u, builder()->SealVersion());
+
+ EXPECT_TRUE(builder()->MigrateFrom(0, db()));
+ EXPECT_FALSE(db()->DoesIndexExist("old_name"));
+ EXPECT_FALSE(db()->DoesIndexExist("added"));
+ EXPECT_TRUE(db()->DoesIndexExist("new_name"));
+ EXPECT_EQ(1u, builder()->NumberOfColumns());
+ EXPECT_THAT(builder()->AllIndexNames(), UnorderedElementsAre("new_name"));
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/stub_form_saver.cc b/chromium/components/password_manager/core/browser/stub_form_saver.cc
new file mode 100644
index 00000000000..cd18a08850c
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/stub_form_saver.cc
@@ -0,0 +1,13 @@
+// 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/stub_form_saver.h"
+
+namespace password_manager {
+
+std::unique_ptr<FormSaver> StubFormSaver::Clone() {
+ return nullptr;
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/stub_form_saver.h b/chromium/components/password_manager/core/browser/stub_form_saver.h
index 5efa6834424..769fe436f3e 100644
--- a/chromium/components/password_manager/core/browser/stub_form_saver.h
+++ b/chromium/components/password_manager/core/browser/stub_form_saver.h
@@ -35,6 +35,7 @@ class StubFormSaver : public FormSaver {
const autofill::PasswordForm& pending,
std::map<base::string16, const autofill::PasswordForm*>* best_matches,
const autofill::PasswordForm** preferred_match) override {}
+ std::unique_ptr<FormSaver> Clone() override;
private:
DISALLOW_COPY_AND_ASSIGN(StubFormSaver);
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 309c877e946..14a3cbba52e 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
@@ -11,7 +11,10 @@
namespace password_manager {
-StubPasswordManagerClient::StubPasswordManagerClient() {}
+StubPasswordManagerClient::StubPasswordManagerClient()
+ : ukm_source_id_(ukm::UkmRecorder::Get()
+ ? ukm::UkmRecorder::Get()->GetNewSourceID()
+ : 0) {}
StubPasswordManagerClient::~StubPasswordManagerClient() {}
@@ -75,7 +78,25 @@ void StubPasswordManagerClient::CheckSafeBrowsingReputation(
const GURL& frame_url) {}
void StubPasswordManagerClient::CheckProtectedPasswordEntry(
- const std::string& password_saved_domain) {}
+ const std::string& password_saved_domain,
+ bool password_field_exists) {}
#endif
+ukm::UkmRecorder* StubPasswordManagerClient::GetUkmRecorder() {
+ return ukm::UkmRecorder::Get();
+}
+
+ukm::SourceId StubPasswordManagerClient::GetUkmSourceId() {
+ return ukm_source_id_;
+}
+
+PasswordManagerMetricsRecorder&
+StubPasswordManagerClient::GetMetricsRecorder() {
+ if (!metrics_recorder_) {
+ metrics_recorder_.emplace(GetUkmRecorder(), GetUkmSourceId(),
+ GetMainFrameURL());
+ }
+ return metrics_recorder_.value();
+}
+
} // namespace password_manager
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 77c740befb5..a33c7b2c8cd 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
@@ -6,7 +6,9 @@
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_STUB_PASSWORD_MANAGER_CLIENT_H_
#include "base/macros.h"
+#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/stub_credentials_filter.h"
#include "components/password_manager/core/browser/stub_log_manager.h"
@@ -48,13 +50,18 @@ class StubPasswordManagerClient : public PasswordManagerClient {
const override;
void CheckSafeBrowsingReputation(const GURL& form_action,
const GURL& frame_url) override;
- void CheckProtectedPasswordEntry(
- const std::string& password_saved_domain) override;
+ void CheckProtectedPasswordEntry(const std::string& password_saved_domain,
+ bool password_field_exists) override;
#endif
+ ukm::UkmRecorder* GetUkmRecorder() override;
+ ukm::SourceId GetUkmSourceId() override;
+ PasswordManagerMetricsRecorder& GetMetricsRecorder() override;
private:
const StubCredentialsFilter credentials_filter_;
StubLogManager log_manager_;
+ ukm::SourceId ukm_source_id_;
+ base::Optional<PasswordManagerMetricsRecorder> metrics_recorder_;
DISALLOW_COPY_AND_ASSIGN(StubPasswordManagerClient);
};
diff --git a/chromium/components/password_manager/core/browser/test_affiliation_fetcher_factory.h b/chromium/components/password_manager/core/browser/test_affiliation_fetcher_factory.h
deleted file mode 100644
index e313702e23a..00000000000
--- a/chromium/components/password_manager/core/browser/test_affiliation_fetcher_factory.h
+++ /dev/null
@@ -1,39 +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_PASSWORD_MANAGER_CORE_BROWSER_TEST_AFFILIATION_FETCHER_FACTORY_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_TEST_AFFILIATION_FETCHER_FACTORY_H_
-
-#include <vector>
-
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
-
-namespace password_manager {
-
-class FacetURI;
-class AffiliationFetcherDelegate;
-
-// Interface for a factory to be used by AffiliationFetcher::Create() in tests
-// to construct instances of test-specific AffiliationFetcher subclasses.
-//
-// The factory is registered with AffiliationFetcher::SetFactoryForTesting().
-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
- // to the |delegate| on the same thread that creates the instance.
- virtual AffiliationFetcher* CreateInstance(
- net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_ids,
- AffiliationFetcherDelegate* delegate) = 0;
-
- protected:
- virtual ~TestAffiliationFetcherFactory() {}
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_TEST_AFFILIATION_FETCHER_FACTORY_H_
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 1e11a3241d4..5c4c585e11d 100644
--- a/chromium/components/password_manager/core/browser/test_password_store.cc
+++ b/chromium/components/password_manager/core/browser/test_password_store.cc
@@ -7,7 +7,7 @@
#include <stddef.h>
#include "base/memory/ptr_util.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/psl_matching_helper.h"
#include "components/password_manager/core/browser/statistics_table.h"
@@ -15,9 +15,8 @@
namespace password_manager {
TestPasswordStore::TestPasswordStore()
- : PasswordStore(base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get()) {
-}
+ : PasswordStore(base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get()) {}
TestPasswordStore::~TestPasswordStore() {
}
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 1b880b338e2..5dce1e7eba2 100644
--- a/chromium/components/password_manager/core/common/password_manager_features.cc
+++ b/chromium/components/password_manager/core/common/password_manager_features.cc
@@ -27,6 +27,10 @@ const base::Feature kEnablePasswordForceSaving = {
extern const base::Feature kEnableManualPasswordGeneration = {
"enable-manual-password-generation", base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables username correction while saving username and password details.
+extern const base::Feature kEnableUsernameCorrection{
+ "EnableUsernameCorrection", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Disallow autofilling of the sync credential.
const base::Feature kProtectSyncCredential = {
"protect-sync-credential", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -40,7 +44,7 @@ const base::Feature kPasswordImportExport = {"password-import-export",
base::FEATURE_DISABLED_BY_DEFAULT};
// Control whether users can view and copy passwords. This is only used for
-// Android, the desktop version of Chrome always allows users to view
+// mobile, the desktop version of Chrome always allows users to view
// passwords.
const base::Feature kViewPasswords = {"view-passwords",
base::FEATURE_DISABLED_BY_DEFAULT};
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 57a216c7889..ba39cbdaba5 100644
--- a/chromium/components/password_manager/core/common/password_manager_features.h
+++ b/chromium/components/password_manager/core/common/password_manager_features.h
@@ -21,6 +21,7 @@ extern const base::Feature kAffiliationBasedMatching;
extern const base::Feature kDropSyncCredential;
extern const base::Feature kEnableManualPasswordGeneration;
extern const base::Feature kEnablePasswordForceSaving;
+extern const base::Feature kEnableUsernameCorrection;
extern const base::Feature kProtectSyncCredential;
extern const base::Feature kProtectSyncCredentialOnReauth;
extern const base::Feature kPasswordImportExport;
diff --git a/chromium/components/password_manager/core/common/password_manager_pref_names.cc b/chromium/components/password_manager/core/common/password_manager_pref_names.cc
index 34f10eae2a8..6d8e945a2b7 100644
--- a/chromium/components/password_manager/core/common/password_manager_pref_names.cc
+++ b/chromium/components/password_manager/core/common/password_manager_pref_names.cc
@@ -25,8 +25,6 @@ const char kOsPasswordLastChanged[] =
const char kKeychainMigrationStatus[] = "password_manager.keychain_migration";
#endif
-const char kPasswordManagerSavingEnabled[] = "profile.password_manager_enabled";
-
const char kWasAutoSignInFirstRunExperienceShown[] =
"profile.was_auto_sign_in_first_run_experience_shown";
@@ -41,5 +39,8 @@ const char kNumberSignInPasswordPromoShown[] =
const char kSyncPasswordHash[] = "profile.sync_password_hash";
+const char kSyncPasswordLengthAndHashSalt[] =
+ "profile.sync_password_length_and_hash_salt";
+
} // namespace prefs
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/common/password_manager_pref_names.h b/chromium/components/password_manager/core/common/password_manager_pref_names.h
index 9f05a765f1e..4a69c5d4171 100644
--- a/chromium/components/password_manager/core/common/password_manager_pref_names.h
+++ b/chromium/components/password_manager/core/common/password_manager_pref_names.h
@@ -19,9 +19,7 @@ extern const char kCredentialsEnableAutosignin[];
// The value of this preference controls whether the Password Manager will save
// credentials. When it is false, it doesn't ask if you want to save passwords
-// but will continue to fill passwords. This preference in a future will
-// substitute kPasswordManagerSavingEnabled, currently it's required that values
-// of these two preference are in sync with each other.
+// but will continue to fill passwords.
// TODO(melandory): Preference should also control autofill behavior for the
// passwords.
extern const char kCredentialsEnableService[];
@@ -46,15 +44,6 @@ extern const char kOsPasswordLastChanged[];
extern const char kKeychainMigrationStatus[];
#endif
-// Boolean that is true if password saving is on (will record new
-// passwords and fill in known passwords). When it is false, it doesn't
-// ask if you want to save passwords but will continue to fill passwords.
-// Constant name and its value differ because of historical reasons as it
-// was not deemed important enough to add migration code just for name
-// change.
-// See http://crbug.com/392387
-extern const char kPasswordManagerSavingEnabled[];
-
// Boolean that indicated whether first run experience for the auto sign-in
// prompt was shown or not.
extern const char kWasAutoSignInFirstRunExperienceShown[];
@@ -68,9 +57,14 @@ extern const char kWasSignInPasswordPromoClicked[];
// Number of times the Chrome Sign in promo popped up.
extern const char kNumberSignInPasswordPromoShown[];
-// String that represent the sync password hash.
+// String that represents the sync password hash.
extern const char kSyncPasswordHash[];
+// String that represents the sync password length and salt. Its format is
+// encrypted and converted to base64 string "<password length, as ascii
+// int>.<16 char salt>".
+extern const char kSyncPasswordLengthAndHashSalt[];
+
} // namespace prefs
} // namespace password_manager
diff --git a/chromium/components/password_manager/sync/browser/BUILD.gn b/chromium/components/password_manager/sync/browser/BUILD.gn
index 88b029040fe..77c38bc7c5a 100644
--- a/chromium/components/password_manager/sync/browser/BUILD.gn
+++ b/chromium/components/password_manager/sync/browser/BUILD.gn
@@ -6,8 +6,6 @@ static_library("browser") {
sources = [
"password_data_type_controller.cc",
"password_data_type_controller.h",
- "password_manager_setting_migrator_service.cc",
- "password_manager_setting_migrator_service.h",
"password_model_worker.cc",
"password_model_worker.h",
"password_sync_util.cc",
@@ -35,7 +33,6 @@ static_library("browser") {
source_set("unit_tests") {
testonly = true
sources = [
- "password_manager_setting_migrator_service_unittest.cc",
"password_sync_util_unittest.cc",
"sync_credentials_filter_unittest.cc",
"sync_username_test_base.cc",
diff --git a/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.cc b/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.cc
deleted file mode 100644
index 28147ea557f..00000000000
--- a/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.cc
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright (c) 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/password_manager/sync/browser/password_manager_setting_migrator_service.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/metrics/histogram_macros.h"
-#include "build/build_config.h"
-#include "components/password_manager/core/browser/password_manager_settings_migration_experiment.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/sync/driver/sync_service.h"
-#include "components/sync_preferences/pref_service_syncable.h"
-
-namespace {
-
-bool GetBooleanUserOrDefaultPrefValue(PrefService* prefs,
- const std::string& name) {
- bool result = false;
- const base::Value* value = prefs->GetUserPrefValue(name);
- if (!value)
- value = prefs->GetDefaultPrefValue(name);
- value->GetAsBoolean(&result);
- return result;
-}
-
-void ChangeOnePrefBecauseAnotherPrefHasChanged(
- PrefService* prefs,
- const std::string& other_pref_name,
- const std::string& changed_pref_name) {
- bool changed_pref =
- GetBooleanUserOrDefaultPrefValue(prefs, changed_pref_name);
- bool other_pref = GetBooleanUserOrDefaultPrefValue(prefs, other_pref_name);
- if (changed_pref != other_pref)
- prefs->SetBoolean(other_pref_name, changed_pref);
-}
-
-// Change value of both kPasswordManagerSavingEnabled and
-// kCredentialsEnableService to the |new_value|.
-void UpdatePreferencesValues(PrefService* prefs, bool new_value) {
- prefs->SetBoolean(password_manager::prefs::kPasswordManagerSavingEnabled,
- new_value);
- prefs->SetBoolean(password_manager::prefs::kCredentialsEnableService,
- new_value);
-}
-
-void SaveCurrentPrefState(PrefService* prefs,
- bool* new_pref_value,
- bool* legacy_pref_value) {
- *new_pref_value = GetBooleanUserOrDefaultPrefValue(
- prefs, password_manager::prefs::kCredentialsEnableService);
- *legacy_pref_value = GetBooleanUserOrDefaultPrefValue(
- prefs, password_manager::prefs::kPasswordManagerSavingEnabled);
-}
-
-void TrackInitialAndFinalValues(PrefService* prefs,
- bool initial_new_pref_value,
- bool initial_legacy_pref_value) {
- bool final_new_pref_value = GetBooleanUserOrDefaultPrefValue(
- prefs, password_manager::prefs::kCredentialsEnableService);
- bool final_legacy_pref_value = GetBooleanUserOrDefaultPrefValue(
- prefs, password_manager::prefs::kPasswordManagerSavingEnabled);
- const int kMaxInitValue = 0x10;
- int value_to_log = 0;
- const int kInitialNewValueMask = 0x8;
- const int kInitialLegacyValueMask = 0x4;
- const int kFinalNewValueMask = 0x2;
- const int kFinalLegacyValueMask = 0x1;
- if (initial_new_pref_value)
- value_to_log |= kInitialNewValueMask;
- if (initial_legacy_pref_value)
- value_to_log |= kInitialLegacyValueMask;
- if (final_new_pref_value)
- value_to_log |= kFinalNewValueMask;
- if (final_legacy_pref_value)
- value_to_log |= kFinalLegacyValueMask;
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SettingsReconciliation.InitialAndFinalValues",
- value_to_log, kMaxInitValue);
-}
-
-} // namespace
-
-namespace password_manager {
-
-bool PasswordManagerSettingMigratorService::force_disabled_for_testing_ = false;
-
-PasswordManagerSettingMigratorService::PasswordManagerSettingMigratorService(
- sync_preferences::PrefServiceSyncable* prefs)
- : prefs_(prefs), sync_service_(nullptr) {
- SaveCurrentPrefState(prefs_, &initial_new_pref_value_,
- &initial_legacy_pref_value_);
-}
-
-PasswordManagerSettingMigratorService::
- ~PasswordManagerSettingMigratorService() {}
-
-void PasswordManagerSettingMigratorService::InitializeMigration(
- syncer::SyncService* sync_service) {
- if (force_disabled_for_testing_)
- return;
- SaveCurrentPrefState(prefs_, &initial_new_pref_value_,
- &initial_legacy_pref_value_);
- const int kMaxInitialValues = 4;
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.SettingsReconciliation.InitialValues",
- (static_cast<int>(initial_new_pref_value_) << 1 |
- static_cast<int>(initial_legacy_pref_value_)),
- kMaxInitialValues);
- if (!password_manager::IsSettingsMigrationActive()) {
- return;
- }
- sync_service_ = sync_service;
- if (!sync_service_ || !CanSyncStart()) {
- MigrateOffState(prefs_);
- TrackInitialAndFinalValues(prefs_, initial_new_pref_value_,
- initial_legacy_pref_value_);
- }
- InitObservers();
-}
-
-void PasswordManagerSettingMigratorService::InitObservers() {
- pref_change_registrar_.Init(prefs_);
- pref_change_registrar_.Add(
- password_manager::prefs::kCredentialsEnableService,
- base::Bind(&PasswordManagerSettingMigratorService::
- OnCredentialsEnableServicePrefChanged,
- base::Unretained(this)));
- pref_change_registrar_.Add(
- password_manager::prefs::kPasswordManagerSavingEnabled,
- base::Bind(&PasswordManagerSettingMigratorService::
- OnPasswordManagerSavingEnabledPrefChanged,
- base::Unretained(this)));
- // This causes OnIsSyncingChanged to be called when the value of
- // PrefService::IsSyncing() changes.
- prefs_->AddObserver(this);
-}
-
-bool PasswordManagerSettingMigratorService::CanSyncStart() {
- return sync_service_ && sync_service_->CanSyncStart() &&
- syncer::UserSelectableTypes().Has(syncer::PREFERENCES);
-}
-
-void PasswordManagerSettingMigratorService::Shutdown() {
- prefs_->RemoveObserver(this);
-}
-
-void PasswordManagerSettingMigratorService::
- OnCredentialsEnableServicePrefChanged(
- const std::string& changed_pref_name) {
- sync_data_.push_back(GetBooleanUserOrDefaultPrefValue(
- prefs_, password_manager::prefs::kCredentialsEnableService));
- ChangeOnePrefBecauseAnotherPrefHasChanged(
- prefs_, password_manager::prefs::kPasswordManagerSavingEnabled,
- password_manager::prefs::kCredentialsEnableService);
-}
-
-void PasswordManagerSettingMigratorService::
- OnPasswordManagerSavingEnabledPrefChanged(
- const std::string& changed_pref_name) {
- sync_data_.push_back(GetBooleanUserOrDefaultPrefValue(
- prefs_, password_manager::prefs::kPasswordManagerSavingEnabled));
- ChangeOnePrefBecauseAnotherPrefHasChanged(
- prefs_, password_manager::prefs::kCredentialsEnableService,
- password_manager::prefs::kPasswordManagerSavingEnabled);
-}
-
-void PasswordManagerSettingMigratorService::OnIsSyncingChanged() {
- if (WasModelAssociationStepPerformed()) {
- // Initial sync has finished.
- MigrateAfterModelAssociation(prefs_);
- }
-
- if (prefs_->IsSyncing() == prefs_->IsPrioritySyncing()) {
- // Sync is not in model association step.
- SaveCurrentPrefState(prefs_, &initial_new_pref_value_,
- &initial_legacy_pref_value_);
- sync_data_.clear();
- }
-}
-
-bool PasswordManagerSettingMigratorService::WasModelAssociationStepPerformed() {
-#if defined(OS_ANDROID) || defined(OS_IOS)
- return prefs_->IsPrioritySyncing();
-#else
- return prefs_->IsSyncing() && prefs_->IsPrioritySyncing();
-#endif
-}
-
-void PasswordManagerSettingMigratorService::MigrateOffState(
- PrefService* prefs) {
- bool new_pref_value = GetBooleanUserOrDefaultPrefValue(
- prefs_, password_manager::prefs::kCredentialsEnableService);
- bool legacy_pref_value = GetBooleanUserOrDefaultPrefValue(
- prefs_, password_manager::prefs::kPasswordManagerSavingEnabled);
- UpdatePreferencesValues(prefs, new_pref_value && legacy_pref_value);
-}
-
-void PasswordManagerSettingMigratorService::MigrateAfterModelAssociation(
- PrefService* prefs) {
- if (sync_data_.empty()) {
- MigrateOffState(prefs);
- } else if (sync_data_.size() == 1) {
-#if defined(OS_ANDROID) || defined(OS_IOS)
- if (initial_new_pref_value_ != initial_legacy_pref_value_) {
- // Treat special case for mobile clients where only priority pref
- // arrives on the client.
- if (!initial_new_pref_value_ && initial_legacy_pref_value_)
- UpdatePreferencesValues(prefs, true);
- else
- UpdatePreferencesValues(prefs, false);
- } else {
- UpdatePreferencesValues(prefs, sync_data_[0]);
- }
-#else
- // Only one value came from sync. This value should be assigned to both
- // preferences.
- UpdatePreferencesValues(prefs, sync_data_[0]);
-#endif
- } else {
- bool sync_new_pref_value = sync_data_[0];
- bool sync_legacy_pref_value = sync_data_.back();
- if (sync_legacy_pref_value && sync_new_pref_value) {
- UpdatePreferencesValues(prefs, true);
- } else if (!sync_legacy_pref_value && !sync_new_pref_value) {
- UpdatePreferencesValues(prefs, false);
- } else if (!initial_legacy_pref_value_ && !initial_new_pref_value_) {
- UpdatePreferencesValues(prefs, true);
- } else {
- UpdatePreferencesValues(prefs, false);
- }
- }
- TrackInitialAndFinalValues(prefs, initial_new_pref_value_,
- initial_legacy_pref_value_);
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.h b/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.h
deleted file mode 100644
index 797d11c90b6..00000000000
--- a/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service.h
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 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_PASSWORD_MANAGER_SYNC_BROWSER_PASSWORD_MANAGER_SETTING_MIGRATOR_SERVICE_H_
-#define COMPONENTS_PASSWORD_MANAGER_SYNC_BROWSER_PASSWORD_MANAGER_SETTING_MIGRATOR_SERVICE_H_
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/sync_preferences/pref_service_syncable_observer.h"
-
-namespace syncer {
-class SyncService;
-}
-
-namespace sync_preferences {
-class PrefServiceSyncable;
-}
-
-namespace password_manager {
-
-// Service that is responsible for reconciling the legacy "Offer to save your
-// web passwords" setting (henceforth denoted 'L', for legacy) with the new
-// "Enable Smart Lock for Passwords" setting (henceforth denoted 'N', for new).
-//
-// It works as follows.
-//
-// Users who are not syncing (this is checked on start-up of the service
-// calling to SyncService:CanSyncStart()) are migrated on startup of the
-// service. These users can be in following states:
-// 1. N = 0, L = 0
-// 2. N = 1, L = 1
-// 3. N = 1, L = 0
-// 4. N = 0, L = 1
-//
-// Nothing should be done in case 1 and 2 since settings are already in sync;
-// in case 3,4 we change value of N to 0.
-//
-// For users who are syncing we save the values for the new and legacy
-// preference on service startup (|inital_values_|) and the values that come
-// from sync during models association step (|sync_data_|). Propagating change
-// of one preference to another preference without having this special treatment
-// will not always work. Here is an example which illustrates possible corner
-// case: the client executes the migration code for the first time, legacy
-// preference has a value 1, new preference was registered for the
-// first time and has default value which is also 1, the sync data snapshot
-// which user has on a client contains new preference equal to 0 and old
-// preference equal to 1, if we blindly propagate these values we first get
-// both preferences equal to 0 after priority pref model was associated and
-// then both preferences equal to 1 after preferences model was associated.
-// But the correct final value in this case is 0.
-//
-// At the end of model association step we derive the new values for both
-// settings using the following table (first column contains local values for
-// the preferences, first row contains values which came from sync, where _
-// means that the value did not come):
-//
-// NL* 0_ 1_ _0 _1 00 01 10 11
-//
-// 00 00 11 00 11 00 11 11 11
-// 01 00* 11* x x 00 00 00 11
-// 10 00* 00* x x 00 00 00 11
-// 11 00 11 00 11 00 00 00 11
-//
-// *these cases only possible on mobile platforms, where we sync only priority
-// preference data type.
-//
-// The service observes changes to both preferences (e.g. changes from sync,
-// changes from UI) and propagates the change to the other preference if needed.
-//
-// Note: componetization of this service currently is impossible, because it
-// depends on PrefServiceSyncable https://crbug.com/522536.
-class PasswordManagerSettingMigratorService
- : public KeyedService,
- public sync_preferences::PrefServiceSyncableObserver {
- public:
- explicit PasswordManagerSettingMigratorService(
- sync_preferences::PrefServiceSyncable* prefs);
- ~PasswordManagerSettingMigratorService() override;
-
- void Shutdown() override;
-
- // PrefServiceSyncableObserver:
- void OnIsSyncingChanged() override;
-
- void InitializeMigration(syncer::SyncService* sync_service);
-
- // Only use for testing.
- static void set_force_disabled_for_testing(bool force_disabled) {
- force_disabled_for_testing_ = force_disabled;
- }
-
- private:
- // Initializes the observers: preferences observers and sync status observers.
- void InitObservers();
-
- // Returns true if sync is expected to start for the user.
- bool CanSyncStart();
-
- // Called when the value of the |kCredentialsEnableService| preference
- // changes, and updates the value of |kPasswordManagerSavingEnabled|
- // preference accordingly.
- void OnCredentialsEnableServicePrefChanged(
- const std::string& changed_pref_name);
-
- // Called when the value of the |kPasswordManagerSavingEnabled| preference
- // changes, and updates the value of |kCredentialsEnableService| preference
- // accordingly.
- void OnPasswordManagerSavingEnabledPrefChanged(
- const std::string& changed_pref_name);
-
- // Determines if model association step was performed. For desktop platforms,
- // the condition is that for both priority preferences and regular preferences
- // types association step was finished. For mobile platforms, the association
- // only for priority prefs is required.
- bool WasModelAssociationStepPerformed();
-
- // Turns off one pref if another pref is off.
- void MigrateOffState(PrefService* prefs);
-
- // Performs a migration after sync associates models. Migration is performed
- // based on initial values for both preferences and values received from
- // sync.
- void MigrateAfterModelAssociation(PrefService* prefs);
-
- // Contains values which have come from sync during model association step.
- // The vector minimum size is 0 and the vector maximum size is 4:
- // * sync_data_ has length equal to 0, when no change to the preferences has
- // came from sync.
- // * sync_data has length equal to 1, when change to only one preference
- // came from sync and another pref already had this value, e.g local state
- // 01 and N=1 came from sync.
- // * sync_data has length equals to 4, changes to both preference has came
- // from sync, e.g. local state is 11 and 01 came from sync.
- // This way index 0 corresponds to kCredentialsEnableService, last index
- // corresponds to kPasswordManagerSavingEnabled if size of sync_data_ equals
- // to 4, otherwise the vector contains the value only for one preference.
- std::vector<bool> sync_data_;
-
- // The initial value for kCredentialsEnableService.
- bool initial_new_pref_value_;
- // The initial value for kPasswordManagerSavingEnabled.
- bool initial_legacy_pref_value_;
-
- sync_preferences::PrefServiceSyncable* prefs_;
- syncer::SyncService* sync_service_;
-
- PrefChangeRegistrar pref_change_registrar_;
-
- // If true, the service will refuse to initialize despite Field Trial
- // settings.
- // Default value is false. Only use for testing.
- static bool force_disabled_for_testing_;
-
- DISALLOW_COPY_AND_ASSIGN(PasswordManagerSettingMigratorService);
-};
-
-} // namespace password_manager
-
-#endif // COMPONENTS_PASSWORD_MANAGER_SYNC_BROWSER_PASSWORD_MANAGER_SETTING_MIGRATOR_SERVICE_H_
diff --git a/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service_unittest.cc b/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service_unittest.cc
deleted file mode 100644
index 3ba8c194cfa..00000000000
--- a/chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service_unittest.cc
+++ /dev/null
@@ -1,463 +0,0 @@
-// Copyright (c) 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 <utility>
-
-#include "base/json/json_writer.h"
-#include "base/macros.h"
-#include "base/metrics/field_trial.h"
-#include "base/test/histogram_tester.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "components/password_manager/core/browser/password_manager.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/password_manager/sync/browser/password_manager_setting_migrator_service.h"
-#include "components/prefs/pref_service.h"
-#include "components/sync/driver/fake_sync_service.h"
-#include "components/sync/model/attachments/attachment_service_proxy_for_test.h"
-#include "components/sync/model/fake_sync_change_processor.h"
-#include "components/sync/model/sync_error_factory.h"
-#include "components/sync/model/sync_error_factory_mock.h"
-#include "components/sync/protocol/sync.pb.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"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const char kFieldTrialName[] = "PasswordManagerSettingsMigration";
-const char kEnabledGroupName[] = "Enable";
-const char kDisabledGroupName[] = "Disable";
-
-const char kInitialValuesHistogramName[] =
- "PasswordManager.SettingsReconciliation.InitialValues";
-
-const char kInitialAndFinalValuesHistogramName[] =
- "PasswordManager.SettingsReconciliation.InitialAndFinalValues";
-
-enum BooleanPrefState {
- OFF,
- ON,
- EMPTY, // datatype bucket is empty
-};
-
-// Enum used for histogram tracking of the initial values for the legacy and new
-// preferences.
-enum PasswordManagerPreferencesInitialValues {
- N0L0,
- N0L1,
- N1L0,
- N1L1,
- NUM_INITIAL_VALUES,
-};
-
-// Enum used for histogram tracking of the combined initial values and final
-// values for the legacy and new preferences.
-enum PasswordManagerPreferencesInitialAndFinalValues {
- I00F00,
- I00F01,
- I00F10,
- I00F11,
- I01F00,
- I01F01,
- I01F10,
- I01F11,
- I10F00,
- I10F01,
- I10F10,
- I10F11,
- I11F00,
- I11F01,
- I11F10,
- I11F11,
- NUM_INITIAL_AND_FINAL_VALUES,
-};
-
-syncer::SyncData CreatePrefSyncData(const std::string& name, bool value) {
- base::Value bool_value(value);
- std::string serialized;
- base::JSONWriter::Write(bool_value, &serialized);
- sync_pb::EntitySpecifics specifics;
- sync_pb::PreferenceSpecifics* pref = nullptr;
- if (name == password_manager::prefs::kPasswordManagerSavingEnabled)
- pref = specifics.mutable_preference();
- else if (name == password_manager::prefs::kCredentialsEnableService)
- pref = specifics.mutable_priority_preference()->mutable_preference();
- else
- NOTREACHED() << "Wrong preference name: " << name;
- pref->set_name(name);
- pref->set_value(serialized);
- return syncer::SyncData::CreateRemoteData(
- 1, specifics, base::Time(), syncer::AttachmentIdList(),
- syncer::AttachmentServiceProxyForTest::Create());
-}
-
-// Emulates start of the syncing for the specific sync type. If |name| is
-// kPasswordManagerSavingEnabled preference, then it's PREFERENCE data type.
-// If |name| is kCredentialsEnableService pref, then it's PRIORITY_PREFERENCE
-// data type.
-void StartSyncingPref(sync_preferences::PrefServiceSyncable* prefs,
- const std::string& name,
- BooleanPrefState pref_state_in_sync) {
- syncer::SyncDataList sync_data_list;
- if (pref_state_in_sync == EMPTY) {
- sync_data_list = syncer::SyncDataList();
- } else {
- sync_data_list.push_back(
- CreatePrefSyncData(name, pref_state_in_sync == ON));
- }
-
- syncer::ModelType type = syncer::UNSPECIFIED;
- if (name == password_manager::prefs::kPasswordManagerSavingEnabled)
- type = syncer::PREFERENCES;
- else if (name == password_manager::prefs::kCredentialsEnableService)
- type = syncer::PRIORITY_PREFERENCES;
- ASSERT_NE(syncer::UNSPECIFIED, type) << "Wrong preference name: " << name;
- syncer::SyncableService* sync = prefs->GetSyncableService(type);
- sync->MergeDataAndStartSyncing(type, sync_data_list,
- std::unique_ptr<syncer::SyncChangeProcessor>(
- new syncer::FakeSyncChangeProcessor),
- std::unique_ptr<syncer::SyncErrorFactory>(
- new syncer::SyncErrorFactoryMock));
-}
-
-class SyncServiceMock : public syncer::FakeSyncService {
- public:
- bool IsFirstSetupComplete() const override { return true; }
-
- bool CanSyncStart() const override { return can_sync_start_; }
-
- void SetCanSyncStart(bool can_sync_start) {
- can_sync_start_ = can_sync_start;
- }
-
- private:
- bool can_sync_start_ = true;
-};
-
-class TestPrefModelAssociatorClient
- : public sync_preferences::PrefModelAssociatorClient {
- public:
- TestPrefModelAssociatorClient() {}
- ~TestPrefModelAssociatorClient() override {}
-
- // PrefModelAssociatorClient implementation.
- bool IsMergeableListPreference(const std::string& pref_name) const override {
- return false;
- }
-
- bool IsMergeableDictionaryPreference(
- const std::string& pref_name) const override {
- return false;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestPrefModelAssociatorClient);
-};
-
-} // namespace
-
-namespace password_manager {
-
-class PasswordManagerSettingMigratorServiceTest : public testing::Test {
- protected:
- PasswordManagerSettingMigratorServiceTest() {}
- ~PasswordManagerSettingMigratorServiceTest() override {}
-
- void SetUp() override {
- SetupPreferenceMigrationEnvironment();
- EnforcePasswordManagerSettingMigrationExperiment(kEnabledGroupName);
- }
-
- void SetupLocalPrefState(const std::string& name, BooleanPrefState state) {
- if (state == ON)
- prefs()->SetBoolean(name, true);
- else if (state == OFF)
- prefs()->SetBoolean(name, false);
- else if (state == EMPTY)
- ASSERT_TRUE(prefs()->FindPreference(name)->IsDefaultValue());
- }
-
- sync_preferences::PrefServiceSyncable* prefs() { return pref_service_.get(); }
-
- void SetupPreferenceMigrationEnvironment() {
- sync_preferences::PrefServiceMockFactory factory;
- factory.SetPrefModelAssociatorClient(&client_);
- scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry(
- new user_prefs::PrefRegistrySyncable);
- password_manager::PasswordManager::RegisterProfilePrefs(
- pref_registry.get());
- std::unique_ptr<sync_preferences::PrefServiceSyncable>
- pref_service_syncable = factory.CreateSyncable(pref_registry.get());
- migration_service_.reset(
- new PasswordManagerSettingMigratorService(pref_service_syncable.get()));
- pref_service_ = std::move(pref_service_syncable);
- }
-
- void ExpectValuesForBothPrefValues(bool new_pref_value, bool old_pref_value) {
- EXPECT_EQ(new_pref_value,
- prefs()->GetBoolean(prefs::kCredentialsEnableService));
- EXPECT_EQ(old_pref_value,
- prefs()->GetBoolean(prefs::kPasswordManagerSavingEnabled));
- }
-
- SyncServiceMock* profile_sync_service() { return &sync_service_; }
-
- void NotifyProfileAdded() {
- migration_service_->InitializeMigration(&sync_service_);
- }
-
- void EnforcePasswordManagerSettingMigrationExperiment(const char* name) {
- // The existing instance of FieldTrialList should be deleted before creating
- // new one, so reset() is called in order to do so.
- field_trial_list_.reset();
- field_trial_list_.reset(new base::FieldTrialList(nullptr));
- base::FieldTrialList::CreateFieldTrial(kFieldTrialName, name);
- }
-
- private:
- std::unique_ptr<base::FieldTrialList> field_trial_list_;
- TestPrefModelAssociatorClient client_;
- SyncServiceMock sync_service_;
- std::unique_ptr<sync_preferences::PrefServiceSyncable> pref_service_;
- std::unique_ptr<PasswordManagerSettingMigratorService> migration_service_;
-
- DISALLOW_COPY_AND_ASSIGN(PasswordManagerSettingMigratorServiceTest);
-};
-
-TEST_F(PasswordManagerSettingMigratorServiceTest, TestMigrationOnLocalChanges) {
- const struct {
- const char* group;
- const char* pref_name;
- bool pref_value;
- bool expected_new_pref_value;
- bool expected_old_pref_value;
- } kTestingTable[] = {
- {kEnabledGroupName, prefs::kPasswordManagerSavingEnabled, true, true,
- true},
- {kEnabledGroupName, prefs::kPasswordManagerSavingEnabled, false, false,
- false},
- {kEnabledGroupName, prefs::kCredentialsEnableService, true, true, true},
- {kEnabledGroupName, prefs::kCredentialsEnableService, false, false,
- false},
- {kDisabledGroupName, prefs::kPasswordManagerSavingEnabled, false, true,
- false},
- {kDisabledGroupName, prefs::kCredentialsEnableService, false, false,
- true}};
-
- for (const auto& test_case : kTestingTable) {
- SetupPreferenceMigrationEnvironment();
- EnforcePasswordManagerSettingMigrationExperiment(test_case.group);
- prefs()->SetBoolean(prefs::kCredentialsEnableService,
- !test_case.pref_value);
- prefs()->SetBoolean(prefs::kPasswordManagerSavingEnabled,
- !test_case.pref_value);
- NotifyProfileAdded();
- base::HistogramTester tester;
- prefs()->SetBoolean(test_case.pref_name, test_case.pref_value);
- ExpectValuesForBothPrefValues(test_case.expected_new_pref_value,
- test_case.expected_old_pref_value);
- EXPECT_THAT(tester.GetAllSamples(kInitialValuesHistogramName),
- testing::IsEmpty());
- }
-}
-
-TEST_F(PasswordManagerSettingMigratorServiceTest,
- ReconcileWhenWhenBothPrefsTypesArrivesFromSync) {
- const struct {
- BooleanPrefState new_pref_local_value;
- BooleanPrefState old_pref_local_value;
- BooleanPrefState new_pref_sync_value;
- BooleanPrefState old_pref_sync_value;
- bool result_value;
- PasswordManagerPreferencesInitialValues histogram_initial_value;
- PasswordManagerPreferencesInitialAndFinalValues histogram_initial_and_final;
- } kTestingTable[] = {
-#if defined(OS_ANDROID) || defined(OS_IOS)
- {ON, OFF, ON, EMPTY, false, N1L0, I10F00},
- {ON, OFF, OFF, EMPTY, false, N1L0, I10F00},
- {ON, OFF, EMPTY, EMPTY, false, N1L0, I10F00},
- {ON, ON, ON, EMPTY, true, N1L1, I11F11},
- {ON, ON, OFF, EMPTY, false, N1L1, I11F00},
- {OFF, OFF, ON, EMPTY, true, N0L0, I00F11},
- {OFF, OFF, OFF, EMPTY, false, N0L0, I00F00},
- {OFF, ON, ON, EMPTY, true, N0L1, I01F11},
- {OFF, ON, OFF, EMPTY, false, N0L1, I01F00},
- {OFF, ON, EMPTY, EMPTY, false, N0L1, I01F00},
-#else
- {EMPTY, EMPTY, EMPTY, EMPTY, true, N1L1, I11F11},
- {EMPTY, EMPTY, EMPTY, OFF, false, N1L1, I11F00},
- {EMPTY, EMPTY, EMPTY, ON, true, N1L1, I11F11},
- {EMPTY, EMPTY, OFF, EMPTY, false, N1L1, I11F00},
- {EMPTY, EMPTY, ON, EMPTY, true, N1L1, I11F11},
- {OFF, OFF, EMPTY, EMPTY, false, N0L0, I00F00},
- {OFF, OFF, OFF, OFF, false, N0L0, I00F00},
- {OFF, OFF, OFF, ON, true, N0L0, I00F11},
- {OFF, OFF, ON, OFF, true, N0L0, I00F11},
- {OFF, ON, OFF, ON, false, N0L1, I01F00},
- {OFF, ON, ON, OFF, false, N0L1, I01F00},
- {OFF, ON, ON, ON, true, N0L1, I01F11},
- {ON, OFF, EMPTY, EMPTY, false, N1L0, I10F00},
- {ON, OFF, OFF, ON, false, N1L0, I10F00},
- {ON, OFF, ON, OFF, false, N1L0, I10F00},
- {ON, OFF, ON, ON, true, N1L0, I10F11},
- {ON, ON, EMPTY, OFF, false, N1L1, I11F00},
- {ON, ON, EMPTY, ON, true, N1L1, I11F11},
- {ON, ON, OFF, EMPTY, false, N1L1, I11F00},
- {ON, ON, OFF, OFF, false, N1L1, I11F00},
- {ON, ON, OFF, ON, false, N1L1, I11F00},
- {ON, ON, ON, EMPTY, true, N1L1, I11F11},
- {ON, ON, ON, OFF, false, N1L1, I11F00},
- {ON, ON, ON, ON, true, N1L1, I11F11},
-#endif
- };
-
- for (const auto& test_case : kTestingTable) {
- SetupPreferenceMigrationEnvironment();
- EnforcePasswordManagerSettingMigrationExperiment(kEnabledGroupName);
- SCOPED_TRACE(testing::Message("Local data = ")
- << test_case.new_pref_local_value << " "
- << test_case.old_pref_local_value);
- SCOPED_TRACE(testing::Message("Sync data = ")
- << test_case.new_pref_sync_value << " "
- << test_case.old_pref_sync_value);
- SetupLocalPrefState(prefs::kPasswordManagerSavingEnabled,
- test_case.old_pref_local_value);
- SetupLocalPrefState(prefs::kCredentialsEnableService,
- test_case.new_pref_local_value);
- base::HistogramTester tester;
- NotifyProfileAdded();
- StartSyncingPref(prefs(), prefs::kCredentialsEnableService,
- test_case.new_pref_sync_value);
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
- StartSyncingPref(prefs(), prefs::kPasswordManagerSavingEnabled,
- test_case.old_pref_sync_value);
-#endif
- ExpectValuesForBothPrefValues(test_case.result_value,
- test_case.result_value);
- EXPECT_THAT(tester.GetAllSamples(kInitialValuesHistogramName),
- testing::ElementsAre(
- base::Bucket(test_case.histogram_initial_value, 1)));
- EXPECT_THAT(tester.GetAllSamples(kInitialAndFinalValuesHistogramName),
- testing::ElementsAre(
- base::Bucket(test_case.histogram_initial_and_final, 1)));
- }
-}
-
-TEST_F(PasswordManagerSettingMigratorServiceTest,
- DoNotReconcileWhenWhenBothPrefsTypesArrivesFromSync) {
- const struct {
- BooleanPrefState new_pref_local_value;
- BooleanPrefState old_pref_local_value;
- BooleanPrefState new_pref_sync_value;
- BooleanPrefState old_pref_sync_value;
- bool result_new_pref_value;
- bool result_old_pref_value;
- PasswordManagerPreferencesInitialValues histogram_initial_value;
- } kTestingTable[] = {
-#if defined(OS_ANDROID) || defined(OS_IOS)
- {ON, OFF, ON, EMPTY, true, false, N1L0},
- {ON, OFF, OFF, EMPTY, false, false, N1L0},
- {ON, OFF, EMPTY, EMPTY, true, false, N1L0},
- {ON, ON, ON, EMPTY, true, true, N1L1},
- {ON, ON, OFF, EMPTY, false, true, N1L1},
- {OFF, OFF, ON, EMPTY, true, false, N0L0},
- {OFF, OFF, OFF, EMPTY, false, false, N0L0},
- {OFF, ON, ON, EMPTY, true, true, N0L1},
- {OFF, ON, OFF, EMPTY, false, true, N0L1},
- {OFF, ON, EMPTY, EMPTY, false, true, N0L1},
-#else
- {OFF, OFF, OFF, ON, false, true, N0L0},
- {OFF, OFF, ON, OFF, true, false, N0L0},
- {OFF, OFF, ON, ON, true, true, N0L0},
- {OFF, ON, EMPTY, OFF, false, false, N0L1},
- {OFF, ON, EMPTY, ON, false, true, N0L1},
- {OFF, ON, OFF, EMPTY, false, true, N0L1},
- {OFF, ON, OFF, OFF, false, false, N0L1},
- {OFF, ON, OFF, ON, false, true, N0L1},
- {OFF, ON, ON, EMPTY, true, true, N0L1},
- {OFF, ON, ON, OFF, true, false, N0L1},
- {OFF, ON, ON, ON, true, true, N0L1},
- {ON, OFF, OFF, ON, false, true, N1L0},
- {ON, OFF, ON, OFF, true, false, N1L0},
- {ON, OFF, ON, ON, true, true, N1L0},
- {ON, ON, EMPTY, OFF, true, false, N1L1},
- {ON, ON, EMPTY, ON, true, true, N1L1},
- {ON, ON, OFF, EMPTY, false, true, N1L1},
- {ON, ON, OFF, OFF, false, false, N1L1},
- {ON, ON, OFF, ON, false, true, N1L1},
- {ON, ON, ON, EMPTY, true, true, N1L1},
- {ON, ON, ON, OFF, true, false, N1L1},
- {ON, ON, ON, ON, true, true, N1L1},
-#endif
- };
-
- for (const auto& test_case : kTestingTable) {
- SetupPreferenceMigrationEnvironment();
- EnforcePasswordManagerSettingMigrationExperiment(kDisabledGroupName);
- SCOPED_TRACE(testing::Message("Local data = ")
- << test_case.new_pref_local_value << " "
- << test_case.old_pref_local_value);
- SCOPED_TRACE(testing::Message("Sync data = ")
- << test_case.new_pref_sync_value << " "
- << test_case.old_pref_sync_value);
- SetupLocalPrefState(prefs::kPasswordManagerSavingEnabled,
- test_case.old_pref_local_value);
- SetupLocalPrefState(prefs::kCredentialsEnableService,
- test_case.new_pref_local_value);
- base::HistogramTester tester;
- NotifyProfileAdded();
- StartSyncingPref(prefs(), prefs::kCredentialsEnableService,
- test_case.new_pref_sync_value);
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
- StartSyncingPref(prefs(), prefs::kPasswordManagerSavingEnabled,
- test_case.old_pref_sync_value);
-#endif
- ExpectValuesForBothPrefValues(test_case.result_new_pref_value,
- test_case.result_old_pref_value);
- EXPECT_THAT(tester.GetAllSamples(kInitialValuesHistogramName),
- testing::ElementsAre(
- base::Bucket(test_case.histogram_initial_value, 1)));
- EXPECT_THAT(tester.GetAllSamples(kInitialAndFinalValuesHistogramName),
- testing::IsEmpty());
- }
-}
-
-TEST_F(PasswordManagerSettingMigratorServiceTest,
- ReconcileWhenSyncIsNotExpectedPasswordManagerEnabledOff) {
- prefs()->SetBoolean(prefs::kPasswordManagerSavingEnabled, false);
- profile_sync_service()->SetCanSyncStart(false);
- base::HistogramTester tester;
- NotifyProfileAdded();
- ExpectValuesForBothPrefValues(false, false);
- EXPECT_THAT(tester.GetAllSamples(kInitialAndFinalValuesHistogramName),
- testing::ElementsAre(base::Bucket(I10F00, 1)));
-}
-
-TEST_F(PasswordManagerSettingMigratorServiceTest,
- ReconcileWhenSyncIsNotExpectedPasswordManagerEnabledOn) {
- prefs()->SetBoolean(prefs::kPasswordManagerSavingEnabled, true);
- ASSERT_EQ(prefs()->GetBoolean(prefs::kCredentialsEnableService), true);
- profile_sync_service()->SetCanSyncStart(false);
- base::HistogramTester tester;
- NotifyProfileAdded();
- ExpectValuesForBothPrefValues(true, true);
- EXPECT_THAT(tester.GetAllSamples(kInitialAndFinalValuesHistogramName),
- testing::ElementsAre(base::Bucket(I11F11, 1)));
-}
-
-TEST_F(PasswordManagerSettingMigratorServiceTest,
- ReconcileWhenSyncIsNotExpectedDefaultValuesForPrefs) {
- ASSERT_EQ(prefs()->GetBoolean(prefs::kCredentialsEnableService), true);
- profile_sync_service()->SetCanSyncStart(false);
- base::HistogramTester tester;
- NotifyProfileAdded();
- ExpectValuesForBothPrefValues(true, true);
- EXPECT_THAT(tester.GetAllSamples(kInitialAndFinalValuesHistogramName),
- testing::ElementsAre(base::Bucket(I11F11, 1)));
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/sync/browser/password_model_worker.cc b/chromium/components/password_manager/sync/browser/password_model_worker.cc
index 997a0b1cbe1..d413adef87e 100644
--- a/chromium/components/password_manager/sync/browser/password_model_worker.cc
+++ b/chromium/components/password_manager/sync/browser/password_model_worker.cc
@@ -20,7 +20,7 @@ syncer::ModelSafeGroup PasswordModelWorker::GetModelSafeGroup() {
return syncer::GROUP_PASSWORD;
}
-bool PasswordModelWorker::IsOnModelThread() {
+bool PasswordModelWorker::IsOnModelSequence() {
// Ideally PasswordStore would expose a way to check whether this is the
// thread it does work on. Since it doesn't, just return true to bypass a
// CHECK in the sync code.
diff --git a/chromium/components/password_manager/sync/browser/password_model_worker.h b/chromium/components/password_manager/sync/browser/password_model_worker.h
index 08405739119..40a422bd605 100644
--- a/chromium/components/password_manager/sync/browser/password_model_worker.h
+++ b/chromium/components/password_manager/sync/browser/password_model_worker.h
@@ -25,7 +25,7 @@ class PasswordModelWorker : public syncer::ModelSafeWorker {
// syncer::ModelSafeWorker implementation.
syncer::ModelSafeGroup GetModelSafeGroup() override;
- bool IsOnModelThread() override;
+ bool IsOnModelSequence() override;
void RequestStop() override;
private:
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 2830c559af3..a20e470bbcf 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
@@ -107,6 +107,7 @@ class CredentialsFilterTest : public SyncUsernameTestBase {
base::Unretained(this)),
base::Bind(&SyncUsernameTestBase::signin_manager,
base::Unretained(this))) {
+ form_manager_.Init(nullptr);
fetcher_.Fetch();
}
diff --git a/chromium/components/payments/OWNERS b/chromium/components/payments/OWNERS
index 2ff5e3b38df..1a9e64dd0e2 100644
--- a/chromium/components/payments/OWNERS
+++ b/chromium/components/payments/OWNERS
@@ -1,4 +1,4 @@
mathp@chromium.org
rouslan@chromium.org
-# COMPONENT: UI>Browser>Autofill>Payments
+# COMPONENT: UI>Browser>Payments
diff --git a/chromium/components/payments/android/OWNERS b/chromium/components/payments/android/OWNERS
index e39fa27b510..55cc03e0e05 100644
--- a/chromium/components/payments/android/OWNERS
+++ b/chromium/components/payments/android/OWNERS
@@ -1,3 +1,3 @@
gogerald@chromium.org
-# COMPONENT: UI>Browser>Autofill>Payments
+# COMPONENT: UI>Browser>Payments
diff --git a/chromium/components/payments/android/web_app_manifest_section_table.cc b/chromium/components/payments/android/web_app_manifest_section_table.cc
index ad2529cce5f..f48db6fe2fb 100644
--- a/chromium/components/payments/android/web_app_manifest_section_table.cc
+++ b/chromium/components/payments/android/web_app_manifest_section_table.cc
@@ -114,9 +114,12 @@ bool WebAppManifestSectionTable::AddWebAppManifest(
sql::Statement s1(db_->GetUniqueStatement(
"DELETE FROM web_app_manifest_section WHERE id=? "));
- s1.BindString(0, manifest[0]->id);
- if (!s1.Run())
- return false;
+ for (const auto& section : manifest) {
+ s1.BindString(0, section->id);
+ if (!s1.Run())
+ return false;
+ s1.Reset(true);
+ }
sql::Statement s2(
db_->GetUniqueStatement("INSERT INTO web_app_manifest_section "
@@ -125,7 +128,6 @@ bool WebAppManifestSectionTable::AddWebAppManifest(
const time_t expire_date_in_seconds =
base::Time::NowFromSystemTime().ToTimeT() + DATA_VALID_TIME_IN_SECONDS;
for (const auto& section : manifest) {
- DCHECK_EQ(manifest[0]->id, section->id);
int index = 0;
s2.BindInt64(index++, expire_date_in_seconds);
s2.BindString(index++, section->id);
diff --git a/chromium/components/payments/android/web_app_manifest_section_table_unittest.cc b/chromium/components/payments/android/web_app_manifest_section_table_unittest.cc
index 97c82f2d262..3e1859d8329 100644
--- a/chromium/components/payments/android/web_app_manifest_section_table_unittest.cc
+++ b/chromium/components/payments/android/web_app_manifest_section_table_unittest.cc
@@ -140,6 +140,61 @@ TEST_F(WebAppManifestSectionTableTest, AddAndGetMultipleManifests) {
ASSERT_TRUE(alicepay_manifest[0]->fingerprints[1] == fingerprint_four);
}
+// A single manifest can have multiple package names, e.g., one for developer
+// and one for production version of the app. A package name is unique among all
+// the apps on Android, so this means we can define multiple apps in a single
+// manifest.
+TEST_F(WebAppManifestSectionTableTest, AddAndGetSingleManifestWithTwoIds) {
+ std::vector<uint8_t> fingerprint_dev = GenerateFingerprint(1);
+ std::vector<uint8_t> fingerprint_prod = GenerateFingerprint(32);
+
+ WebAppManifestSectionTable* web_app_manifest_section_table =
+ WebAppManifestSectionTable::FromWebDatabase(db_.get());
+
+ std::vector<mojom::WebAppManifestSectionPtr> manifest;
+ {
+ // Adds dev version to the manifest.
+ mojom::WebAppManifestSectionPtr manifest_dev_section =
+ mojom::WebAppManifestSection::New();
+ manifest_dev_section->id = "com.bobpay.dev";
+ manifest_dev_section->min_version = static_cast<int64_t>(2);
+ manifest_dev_section->fingerprints.push_back(fingerprint_dev);
+ manifest.emplace_back(std::move(manifest_dev_section));
+ }
+ {
+ // Adds prod version to the manifest.
+ mojom::WebAppManifestSectionPtr manifest_prod_section =
+ mojom::WebAppManifestSection::New();
+ manifest_prod_section->id = "com.bobpay.prod";
+ manifest_prod_section->min_version = static_cast<int64_t>(1);
+ manifest_prod_section->fingerprints.push_back(fingerprint_prod);
+ manifest.emplace_back(std::move(manifest_prod_section));
+ }
+ ASSERT_TRUE(web_app_manifest_section_table->AddWebAppManifest(manifest));
+
+ {
+ // Verify the dev manifest.
+ std::vector<mojom::WebAppManifestSectionPtr> actual_manifest =
+ web_app_manifest_section_table->GetWebAppManifest("com.bobpay.dev");
+ ASSERT_EQ(actual_manifest.size(), 1U);
+ EXPECT_EQ(actual_manifest[0]->id, "com.bobpay.dev");
+ EXPECT_EQ(actual_manifest[0]->min_version, 2);
+ ASSERT_EQ(actual_manifest[0]->fingerprints.size(), 1U);
+ EXPECT_TRUE(actual_manifest[0]->fingerprints[0] == fingerprint_dev);
+ }
+
+ {
+ // Verify the prod manifest.
+ std::vector<mojom::WebAppManifestSectionPtr> actual_manifest =
+ web_app_manifest_section_table->GetWebAppManifest("com.bobpay.prod");
+ ASSERT_EQ(actual_manifest.size(), 1U);
+ EXPECT_EQ(actual_manifest[0]->id, "com.bobpay.prod");
+ EXPECT_EQ(actual_manifest[0]->min_version, 1);
+ ASSERT_EQ(actual_manifest[0]->fingerprints.size(), 1U);
+ EXPECT_TRUE(actual_manifest[0]->fingerprints[0] == fingerprint_prod);
+ }
+}
+
} // namespace
} // namespace payments
diff --git a/chromium/components/payments/content/BUILD.gn b/chromium/components/payments/content/BUILD.gn
index 6396049b0ce..c856ae90c14 100644
--- a/chromium/components/payments/content/BUILD.gn
+++ b/chromium/components/payments/content/BUILD.gn
@@ -24,11 +24,11 @@ static_library("content") {
"//components/autofill/core/browser",
"//components/keyed_service/content",
"//components/payments/core",
- "//components/payments/mojom",
"//components/prefs",
"//components/strings:components_strings_grit",
"//content/public/browser",
"//mojo/public/cpp/bindings",
+ "//third_party/WebKit/public:blink_headers",
"//third_party/libphonenumber",
]
}
@@ -53,11 +53,11 @@ static_library("utils") {
"//components/data_use_measurement/core",
"//components/link_header_util",
"//components/payments/core",
- "//components/payments/mojom",
"//components/payments/mojom:mojom_parser",
"//components/strings",
"//content/public/browser",
"//net",
+ "//third_party/WebKit/public:blink_headers",
"//third_party/re2",
"//ui/base",
"//url",
@@ -88,11 +88,11 @@ source_set("unit_tests") {
"//components/autofill/core/browser:test_support",
"//components/payments/core",
"//components/payments/core:test_support",
- "//components/payments/mojom",
"//components/strings:components_strings_grit",
"//content/test:test_support",
"//net:test_support",
"//testing/gtest",
+ "//third_party/WebKit/public:blink_headers",
"//third_party/icu",
"//third_party/libaddressinput:test_support",
]
diff --git a/chromium/components/payments/content/DEPS b/chromium/components/payments/content/DEPS
index de79a74acf5..0d4155ddae1 100644
--- a/chromium/components/payments/content/DEPS
+++ b/chromium/components/payments/content/DEPS
@@ -10,6 +10,7 @@ include_rules = [
"+content/public",
"+mojo/public/cpp",
"+net",
+ "+third_party/WebKit/public/platform/modules/payments",
"+third_party/libphonenumber",
"+third_party/re2",
"+ui/base",
diff --git a/chromium/components/payments/content/OWNERS b/chromium/components/payments/content/OWNERS
index 913eb47bf91..b30e98fcb98 100644
--- a/chromium/components/payments/content/OWNERS
+++ b/chromium/components/payments/content/OWNERS
@@ -1,3 +1,3 @@
per-file payment_response_helper*=sebsg@chromium.org
-# COMPONENT: UI>Browser>Autofill>Payments
+# COMPONENT: UI>Browser>Payments
diff --git a/chromium/components/payments/content/android/BUILD.gn b/chromium/components/payments/content/android/BUILD.gn
index c11f428b301..89dbeabf154 100644
--- a/chromium/components/payments/content/android/BUILD.gn
+++ b/chromium/components/payments/content/android/BUILD.gn
@@ -8,16 +8,11 @@ import("//mojo/public/tools/bindings/mojom.gni")
static_library("android") {
sources = [
- "component_jni_registrar.cc",
- "component_jni_registrar.h",
"currency_formatter_android.cc",
"currency_formatter_android.h",
"origin_security_checker_android.cc",
- "origin_security_checker_android.h",
"payment_details_validation_android.cc",
- "payment_details_validation_android.h",
"payment_manifest_downloader_android.cc",
- "payment_manifest_downloader_android.h",
"payment_manifest_parser_android.cc",
"payment_manifest_parser_android.h",
]
@@ -26,7 +21,6 @@ static_library("android") {
"//base",
"//components/payments/content:utils",
"//components/payments/core",
- "//components/payments/mojom",
"//content/public/browser",
"//net",
]
@@ -53,9 +47,9 @@ android_library("java") {
]
deps = [
"//base:base_java",
- "//components/payments/mojom:mojom_java",
"//components/payments/mojom:mojom_parser_java",
"//content/public/android:content_java",
"//mojo/public/java:bindings_java",
+ "//third_party/WebKit/public:android_mojo_bindings_java",
]
}
diff --git a/chromium/components/payments/content/android/OWNERS b/chromium/components/payments/content/android/OWNERS
index e39fa27b510..55cc03e0e05 100644
--- a/chromium/components/payments/content/android/OWNERS
+++ b/chromium/components/payments/content/android/OWNERS
@@ -1,3 +1,3 @@
gogerald@chromium.org
-# COMPONENT: UI>Browser>Autofill>Payments
+# COMPONENT: UI>Browser>Payments
diff --git a/chromium/components/payments/content/android/currency_formatter_android.cc b/chromium/components/payments/content/android/currency_formatter_android.cc
index 3b3694e5c3d..4707088c750 100644
--- a/chromium/components/payments/content/android/currency_formatter_android.cc
+++ b/chromium/components/payments/content/android/currency_formatter_android.cc
@@ -55,11 +55,6 @@ CurrencyFormatterAndroid::GetFormattedCurrencyCode(
env, currency_formatter_->formatted_currency_code());
}
-// static
-bool CurrencyFormatterAndroid::Register(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
static jlong InitCurrencyFormatterAndroid(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
diff --git a/chromium/components/payments/content/android/currency_formatter_android.h b/chromium/components/payments/content/android/currency_formatter_android.h
index 7249e14d2c5..886388dc512 100644
--- a/chromium/components/payments/content/android/currency_formatter_android.h
+++ b/chromium/components/payments/content/android/currency_formatter_android.h
@@ -18,9 +18,6 @@ class CurrencyFormatter;
// Forwarding calls to payments::CurrencyFormatter.
class CurrencyFormatterAndroid {
public:
- // Registers the JNI bindings for this class.
- static bool Register(JNIEnv* env);
-
CurrencyFormatterAndroid(
JNIEnv* env,
jobject jcaller,
diff --git a/chromium/components/payments/content/android/origin_security_checker_android.cc b/chromium/components/payments/content/android/origin_security_checker_android.cc
index 1ca7d1f2a5c..856d8ac5d31 100644
--- a/chromium/components/payments/content/android/origin_security_checker_android.cc
+++ b/chromium/components/payments/content/android/origin_security_checker_android.cc
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/payments/content/android/origin_security_checker_android.h"
-
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "components/payments/content/origin_security_checker.h"
@@ -42,8 +40,4 @@ jboolean IsOriginLocalhostOrFile(JNIEnv* env,
GURL(ConvertJavaStringToUTF8(env, jurl)));
}
-bool RegisterOriginSecurityChecker(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
} // namespace payments
diff --git a/chromium/components/payments/content/android/origin_security_checker_android.h b/chromium/components/payments/content/android/origin_security_checker_android.h
deleted file mode 100644
index d44138984b5..00000000000
--- a/chromium/components/payments/content/android/origin_security_checker_android.h
+++ /dev/null
@@ -1,16 +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_PAYMENTS_CONTENT_ANDROID_ORIGIN_SECURITY_CHECKER_ANDROID_H_
-#define COMPONENTS_PAYMENTS_CONTENT_ANDROID_ORIGIN_SECURITY_CHECKER_ANDROID_H_
-
-#include <jni.h>
-
-namespace payments {
-
-bool RegisterOriginSecurityChecker(JNIEnv* env);
-
-} // namespace payments
-
-#endif // COMPONENTS_PAYMENTS_CONTENT_ANDROID_ORIGIN_SECURITY_CHECKER_ANDROID_H_
diff --git a/chromium/components/payments/content/android/payment_details_validation_android.cc b/chromium/components/payments/content/android/payment_details_validation_android.cc
index fa88d63a61d..7378c097137 100644
--- a/chromium/components/payments/content/android/payment_details_validation_android.cc
+++ b/chromium/components/payments/content/android/payment_details_validation_android.cc
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/payments/content/android/payment_details_validation_android.h"
-
#include <stdint.h>
#include <cstring>
@@ -13,8 +11,8 @@
#include "base/android/jni_android.h"
#include "components/payments/content/payment_details_validation.h"
-#include "components/payments/mojom/payment_request.mojom.h"
#include "jni/PaymentValidator_jni.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
@@ -33,8 +31,4 @@ jboolean ValidatePaymentDetails(
return validatePaymentDetails(details, &unused_error_message);
}
-bool RegisterPaymentValidator(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
} // namespace payments
diff --git a/chromium/components/payments/content/android/payment_details_validation_android.h b/chromium/components/payments/content/android/payment_details_validation_android.h
deleted file mode 100644
index 1ff1d6a9bb4..00000000000
--- a/chromium/components/payments/content/android/payment_details_validation_android.h
+++ /dev/null
@@ -1,16 +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_PAYMENTS_CONTENT_ANDROID_PAYMENT_DETAILS_VALIDATION_ANDROID_H_
-#define COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_DETAILS_VALIDATION_ANDROID_H_
-
-#include <jni.h>
-
-namespace payments {
-
-bool RegisterPaymentValidator(JNIEnv* env);
-
-} // namespace payments
-
-#endif // COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_DETAILS_VALIDATION_ANDROID_H_
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 41241e05b9a..d6503d98d59 100644
--- a/chromium/components/payments/content/android/payment_manifest_downloader_android.cc
+++ b/chromium/components/payments/content/android/payment_manifest_downloader_android.cc
@@ -4,137 +4,114 @@
#include "components/payments/content/android/payment_manifest_downloader_android.h"
-#include <memory>
-#include <utility>
-
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
-#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "components/payments/content/payment_manifest_downloader.h"
#include "content/public/browser/browser_context.h"
#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 "url/gurl.h"
-#include "url/url_constants.h"
namespace payments {
namespace {
-class SelfDeletingDownloadDelegate
- : public PaymentManifestDownloader::Delegate {
+class DownloadCallback {
public:
- explicit SelfDeletingDownloadDelegate(
- const base::android::JavaParamRef<jobject>& jcallback)
- : jcallback_(jcallback), is_downloading_payment_method_manifest_(true) {}
-
- void set_downloader(std::unique_ptr<PaymentManifestDownloader> downloader) {
- downloader_ = std::move(downloader);
- }
-
- void DownloadPaymentMethodManifest() {
- is_downloading_payment_method_manifest_ = true;
- downloader_->DownloadPaymentMethodManifest();
- }
+ DownloadCallback(const base::android::JavaParamRef<jobject>& jcallback)
+ : jcallback_(jcallback) {}
- void DownloadWebAppManifest() {
- is_downloading_payment_method_manifest_ = false;
- downloader_->DownloadWebAppManifest();
- }
+ ~DownloadCallback() {}
- // PaymentManifestDownloader::Delegate
- void OnManifestDownloadSuccess(const std::string& content) override {
+ void OnPaymentMethodManifestDownload(const std::string& content) {
JNIEnv* env = base::android::AttachCurrentThread();
- if (is_downloading_payment_method_manifest_) {
+
+ if (content.empty()) {
+ Java_ManifestDownloadCallback_onManifestDownloadFailure(env, jcallback_);
+ } else {
Java_ManifestDownloadCallback_onPaymentMethodManifestDownloadSuccess(
env, jcallback_,
base::android::ConvertUTF8ToJavaString(env, content));
+ }
+ }
+
+ void OnWebAppManifestDownload(const std::string& content) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+
+ if (content.empty()) {
+ Java_ManifestDownloadCallback_onManifestDownloadFailure(env, jcallback_);
} else {
Java_ManifestDownloadCallback_onWebAppManifestDownloadSuccess(
env, jcallback_,
base::android::ConvertUTF8ToJavaString(env, content));
}
- delete this;
- }
-
- // PaymentManifestDownloader::Delegate
- void OnManifestDownloadFailure() override {
- Java_ManifestDownloadCallback_onManifestDownloadFailure(
- base::android::AttachCurrentThread(), jcallback_);
- delete this;
}
private:
- ~SelfDeletingDownloadDelegate() override {}
-
base::android::ScopedJavaGlobalRef<jobject> jcallback_;
- bool is_downloading_payment_method_manifest_;
- std::unique_ptr<PaymentManifestDownloader> downloader_;
- DISALLOW_COPY_AND_ASSIGN(SelfDeletingDownloadDelegate);
+ DISALLOW_COPY_AND_ASSIGN(DownloadCallback);
};
-SelfDeletingDownloadDelegate* BuildSelfDeletingDownloadDelegate(
- JNIEnv* env,
- const base::android::JavaParamRef<jobject>& jweb_contents,
- const base::android::JavaParamRef<jobject>& juri,
- const base::android::JavaParamRef<jobject>& jcallback) {
- SelfDeletingDownloadDelegate* delegate =
- new SelfDeletingDownloadDelegate(jcallback);
-
- content::WebContents* web_contents =
- content::WebContents::FromJavaWebContents(jweb_contents);
- if (!web_contents) {
- delegate->OnManifestDownloadFailure();
- return nullptr;
- }
+} // namespace
- GURL url(base::android::ConvertJavaStringToUTF8(
- env, Java_PaymentManifestDownloader_getUriString(env, juri)));
- DCHECK(url.is_valid());
- DCHECK(url.SchemeIs(url::kHttpsScheme));
+PaymentManifestDownloaderAndroid::PaymentManifestDownloaderAndroid(
+ const scoped_refptr<net::URLRequestContextGetter>& context)
+ : downloader_(context) {}
- std::unique_ptr<PaymentManifestDownloader> downloader =
- base::MakeUnique<PaymentManifestDownloader>(
- content::BrowserContext::GetDefaultStoragePartition(
- web_contents->GetBrowserContext())
- ->GetURLRequestContext(),
- url, delegate);
- delegate->set_downloader(std::move(downloader));
+PaymentManifestDownloaderAndroid::~PaymentManifestDownloaderAndroid() {}
- return delegate;
+void PaymentManifestDownloaderAndroid::DownloadPaymentMethodManifest(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller,
+ const base::android::JavaParamRef<jobject>& juri,
+ const base::android::JavaParamRef<jobject>& jcallback) {
+ downloader_.DownloadPaymentMethodManifest(
+ GURL(base::android::ConvertJavaStringToUTF8(
+ env, Java_PaymentManifestDownloader_getUriString(env, juri))),
+ base::BindOnce(&DownloadCallback::OnPaymentMethodManifestDownload,
+ base::MakeUnique<DownloadCallback>(jcallback)));
}
-} // namespace
-
-bool RegisterPaymentManifestDownloader(JNIEnv* env) {
- return RegisterNativesImpl(env);
+void PaymentManifestDownloaderAndroid::DownloadWebAppManifest(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller,
+ const base::android::JavaParamRef<jobject>& juri,
+ const base::android::JavaParamRef<jobject>& jcallback) {
+ downloader_.DownloadWebAppManifest(
+ GURL(base::android::ConvertJavaStringToUTF8(
+ env, Java_PaymentManifestDownloader_getUriString(env, juri))),
+ base::BindOnce(&DownloadCallback::OnWebAppManifestDownload,
+ base::MakeUnique<DownloadCallback>(jcallback)));
}
-void DownloadPaymentMethodManifest(
+void PaymentManifestDownloaderAndroid::Destroy(
JNIEnv* env,
- const base::android::JavaParamRef<jclass>& jcaller,
- const base::android::JavaParamRef<jobject>& jweb_contents,
- const base::android::JavaParamRef<jobject>& jmethod_name,
- const base::android::JavaParamRef<jobject>& jcallback) {
- SelfDeletingDownloadDelegate* delegate = BuildSelfDeletingDownloadDelegate(
- env, jweb_contents, jmethod_name, jcallback);
- if (delegate)
- delegate->DownloadPaymentMethodManifest();
+ const base::android::JavaParamRef<jobject>& jcaller) {
+ delete this;
}
-void DownloadWebAppManifest(
+void PaymentManifestDownloaderAndroid::AllowHttpForTest(
JNIEnv* env,
- const base::android::JavaParamRef<jclass>& jcaller,
- const base::android::JavaParamRef<jobject>& jweb_contents,
- const base::android::JavaParamRef<jobject>& jweb_app_manifest_uri,
- const base::android::JavaParamRef<jobject>& jcallback) {
- SelfDeletingDownloadDelegate* delegate = BuildSelfDeletingDownloadDelegate(
- env, jweb_contents, jweb_app_manifest_uri, jcallback);
- if (delegate)
- delegate->DownloadWebAppManifest();
+ const base::android::JavaParamRef<jobject>& jcaller) {
+ downloader_.AllowHttpForTest();
+}
+
+// Static free function declared and called directly from java.
+// Caller owns the result. Returns 0 on error.
+static jlong Init(JNIEnv* env,
+ const base::android::JavaParamRef<jclass>& jcaller,
+ const base::android::JavaParamRef<jobject>& jweb_contents) {
+ content::WebContents* web_contents =
+ content::WebContents::FromJavaWebContents(jweb_contents);
+ if (!web_contents)
+ return 0;
+
+ return reinterpret_cast<jlong>(new PaymentManifestDownloaderAndroid(
+ content::BrowserContext::GetDefaultStoragePartition(
+ web_contents->GetBrowserContext())
+ ->GetURLRequestContext()));
}
} // 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 12f2306d646..cfbb4cbe908 100644
--- a/chromium/components/payments/content/android/payment_manifest_downloader_android.h
+++ b/chromium/components/payments/content/android/payment_manifest_downloader_android.h
@@ -7,9 +7,49 @@
#include <jni.h>
+#include "base/android/jni_android.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/payments/content/payment_manifest_downloader.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
namespace payments {
-bool RegisterPaymentManifestDownloader(JNIEnv* env);
+// Android wrapper for the payment manifest downloader.
+class PaymentManifestDownloaderAndroid {
+ public:
+ explicit PaymentManifestDownloaderAndroid(
+ const scoped_refptr<net::URLRequestContextGetter>& context);
+ ~PaymentManifestDownloaderAndroid();
+
+ void DownloadPaymentMethodManifest(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller,
+ const base::android::JavaParamRef<jobject>& juri,
+ const base::android::JavaParamRef<jobject>& jcallback);
+
+ void DownloadWebAppManifest(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller,
+ const base::android::JavaParamRef<jobject>& juri,
+ const base::android::JavaParamRef<jobject>& jcallback);
+
+ // Deletes this object.
+ void Destroy(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller);
+
+ // Allows HTTP URLs. Should be used for testing only.
+ void AllowHttpForTest(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller);
+
+ private:
+ PaymentManifestDownloader downloader_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaymentManifestDownloaderAndroid);
+};
} // namespace payments
diff --git a/chromium/components/payments/content/android/payment_manifest_parser_android.cc b/chromium/components/payments/content/android/payment_manifest_parser_android.cc
index 65a3e26e449..a740ecb4e5e 100644
--- a/chromium/components/payments/content/android/payment_manifest_parser_android.cc
+++ b/chromium/components/payments/content/android/payment_manifest_parser_android.cc
@@ -5,9 +5,11 @@
#include "components/payments/content/android/payment_manifest_parser_android.h"
#include <stddef.h>
+#include <vector>
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
@@ -26,7 +28,10 @@ class ParseCallback {
~ParseCallback() {}
// Copies payment method manifest into Java.
- void OnPaymentMethodManifestParsed(std::vector<GURL> web_app_manifest_urls) {
+ void OnPaymentMethodManifestParsed(
+ const std::vector<GURL>& web_app_manifest_urls,
+ const std::vector<url::Origin>& unused_supported_origins,
+ bool unused_all_origins_supported) {
DCHECK_GE(100U, web_app_manifest_urls.size());
JNIEnv* env = base::android::AttachCurrentThread();
@@ -138,10 +143,6 @@ void PaymentManifestParserAndroid::StopUtilityProcess(
delete this;
}
-bool RegisterPaymentManifestParser(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
// Caller owns the result.
jlong CreatePaymentManifestParserAndroid(
JNIEnv* env,
diff --git a/chromium/components/payments/content/android/payment_manifest_parser_android.h b/chromium/components/payments/content/android/payment_manifest_parser_android.h
index a2b7e22a5f6..65fc725da0c 100644
--- a/chromium/components/payments/content/android/payment_manifest_parser_android.h
+++ b/chromium/components/payments/content/android/payment_manifest_parser_android.h
@@ -7,11 +7,7 @@
#include <jni.h>
-#include <memory>
-#include <vector>
-
#include "base/android/jni_android.h"
-#include "base/android/scoped_java_ref.h"
#include "base/macros.h"
#include "components/payments/content/payment_manifest_parser_host.h"
@@ -49,8 +45,6 @@ class PaymentManifestParserAndroid {
DISALLOW_COPY_AND_ASSIGN(PaymentManifestParserAndroid);
};
-bool RegisterPaymentManifestParser(JNIEnv* env);
-
} // namespace payments
#endif // COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_MANIFEST_PARSER_ANDROID_H_
diff --git a/chromium/components/payments/content/origin_security_checker.cc b/chromium/components/payments/content/origin_security_checker.cc
index 20986082d47..e25f558f42d 100644
--- a/chromium/components/payments/content/origin_security_checker.cc
+++ b/chromium/components/payments/content/origin_security_checker.cc
@@ -23,7 +23,7 @@ bool OriginSecurityChecker::IsSchemeCryptographic(const GURL& url) {
// static
bool OriginSecurityChecker::IsOriginLocalhostOrFile(const GURL& url) {
return url.is_valid() &&
- (net::IsLocalhost(url.HostNoBrackets()) || url.SchemeIsFile());
+ (net::IsLocalhost(url.HostNoBracketsPiece()) || url.SchemeIsFile());
}
} // namespace payments
diff --git a/chromium/components/payments/content/payment_details_validation.cc b/chromium/components/payments/content/payment_details_validation.cc
index e953cfb70dc..c61946e216b 100644
--- a/chromium/components/payments/content/payment_details_validation.cc
+++ b/chromium/components/payments/content/payment_details_validation.cc
@@ -8,7 +8,7 @@
#include <vector>
#include "components/payments/content/payments_validators.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
namespace {
diff --git a/chromium/components/payments/content/payment_details_validation.h b/chromium/components/payments/content/payment_details_validation.h
index fda9990749b..358fa8efe15 100644
--- a/chromium/components/payments/content/payment_details_validation.h
+++ b/chromium/components/payments/content/payment_details_validation.h
@@ -7,7 +7,7 @@
#include <string>
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
diff --git a/chromium/components/payments/content/payment_manifest_downloader.cc b/chromium/components/payments/content/payment_manifest_downloader.cc
index 15a8783d71a..63c4e077d87 100644
--- a/chromium/components/payments/content/payment_manifest_downloader.cc
+++ b/chromium/components/payments/content/payment_manifest_downloader.cc
@@ -10,6 +10,8 @@
#include "base/bind.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/link_header_util/link_header_util.h"
@@ -24,44 +26,110 @@
namespace payments {
namespace {
-bool IsValidManifestUrl(const GURL& url) {
- return url.is_valid() && url.SchemeIs(url::kHttpsScheme);
+GURL ParseResponseHeader(const net::URLFetcher* source) {
+ if (source->GetResponseCode() != net::HTTP_OK &&
+ source->GetResponseCode() != net::HTTP_NO_CONTENT) {
+ return GURL();
+ }
+
+ net::HttpResponseHeaders* headers = source->GetResponseHeaders();
+ if (!headers)
+ return GURL();
+
+ std::string link_header;
+ headers->GetNormalizedHeader("link", &link_header);
+ if (link_header.empty())
+ return GURL();
+
+ std::string payment_method_manifest_url;
+ std::unordered_map<std::string, base::Optional<std::string>> params;
+ for (const auto& value : link_header_util::SplitLinkHeader(link_header)) {
+ if (!link_header_util::ParseLinkHeaderValue(
+ value.first, value.second, &payment_method_manifest_url, &params)) {
+ continue;
+ }
+
+ auto rel = params.find("rel");
+ if (rel == params.end())
+ continue;
+
+ std::vector<std::string> rel_parts =
+ 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 GURL();
+}
+
+std::string ParseResponseContent(const net::URLFetcher* source) {
+ std::string content;
+ if (source->GetResponseCode() != net::HTTP_OK)
+ return content;
+
+ bool success = source->GetResponseAsString(&content);
+ DCHECK(success); // Whether the fetcher was set to store result as string.
+
+ return content;
}
} // namespace
PaymentManifestDownloader::PaymentManifestDownloader(
- const scoped_refptr<net::URLRequestContextGetter>& context,
+ const scoped_refptr<net::URLRequestContextGetter>& context)
+ : context_(context), allow_http_for_test_(false) {}
+
+PaymentManifestDownloader::~PaymentManifestDownloader() {}
+
+void PaymentManifestDownloader::DownloadPaymentMethodManifest(
const GURL& url,
- Delegate* delegate)
- : context_(context),
- url_(url),
- delegate_(delegate),
- is_downloading_http_link_header_(true) {
- DCHECK(IsValidManifestUrl(url_));
+ DownloadCallback callback) {
+ DCHECK(IsValidManifestUrl(url));
+ InitiateDownload(url, net::URLFetcher::HEAD, std::move(callback));
}
-PaymentManifestDownloader::~PaymentManifestDownloader() {}
+void PaymentManifestDownloader::DownloadWebAppManifest(
+ const GURL& url,
+ DownloadCallback callback) {
+ DCHECK(IsValidManifestUrl(url));
+ InitiateDownload(url, net::URLFetcher::GET, std::move(callback));
+}
-void PaymentManifestDownloader::DownloadPaymentMethodManifest() {
- DCHECK(!fetcher_);
- is_downloading_http_link_header_ = true;
- InitiateDownload(url_, net::URLFetcher::HEAD);
+void PaymentManifestDownloader::AllowHttpForTest() {
+ allow_http_for_test_ = true;
}
-void PaymentManifestDownloader::DownloadWebAppManifest() {
- DCHECK(!fetcher_);
- is_downloading_http_link_header_ = false;
- InitiateDownload(url_, net::URLFetcher::GET);
+PaymentManifestDownloader::Download::Download() {}
+
+PaymentManifestDownloader::Download::~Download() {}
+
+void PaymentManifestDownloader::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ auto download_it = downloads_.find(source);
+ 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) {
+ GURL url = ParseResponseHeader(source);
+ if (IsValidManifestUrl(url)) {
+ InitiateDownload(url, net::URLFetcher::GET,
+ std::move(download->callback));
+ } else {
+ std::move(download->callback).Run(std::string());
+ }
+ } else {
+ std::move(download->callback).Run(ParseResponseContent(source));
+ }
}
void PaymentManifestDownloader::InitiateDownload(
const GURL& url,
- net::URLFetcher::RequestType request_type) {
- if (!IsValidManifestUrl(url)) {
- delegate_->OnManifestDownloadFailure();
- return;
- }
+ net::URLFetcher::RequestType request_type,
+ DownloadCallback callback) {
+ DCHECK(IsValidManifestUrl(url));
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("payment_manifest_downloader", R"(
@@ -83,69 +151,31 @@ void PaymentManifestDownloader::InitiateDownload(
"disable all payment apps to stop this feature."
policy_exception_justification: "Not implemented."
})");
- fetcher_ = net::URLFetcher::Create(0 /* id */, url, request_type, this,
- traffic_annotation);
+ 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();
+ 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 download = base::MakeUnique<Download>();
+ download->request_type = request_type;
+ download->fetcher = std::move(fetcher);
+ download->callback = std::move(callback);
+
+ const net::URLFetcher* identifier = download->fetcher.get();
+ auto insert_result =
+ downloads_.insert(std::make_pair(identifier, std::move(download)));
+ DCHECK(insert_result.second); // Whether the insert has succeeded.
}
-void PaymentManifestDownloader::OnURLFetchComplete(
- const net::URLFetcher* source) {
- if (source->GetResponseCode() != net::HTTP_OK) {
- delegate_->OnManifestDownloadFailure();
- return;
- }
-
- if (is_downloading_http_link_header_) {
- is_downloading_http_link_header_ = false;
-
- net::HttpResponseHeaders* headers = source->GetResponseHeaders();
- if (!headers) {
- delegate_->OnManifestDownloadFailure();
- return;
- }
-
- std::string link_header;
- headers->GetNormalizedHeader("link", &link_header);
- if (!link_header.empty()) {
- std::string payment_method_manifest_url;
- std::unordered_map<std::string, base::Optional<std::string>> params;
- for (const auto& value : link_header_util::SplitLinkHeader(link_header)) {
- if (!link_header_util::ParseLinkHeaderValue(
- value.first, value.second, &payment_method_manifest_url,
- &params)) {
- continue;
- }
-
- auto rel = params.find("rel");
- if (rel == params.end())
- continue;
-
- std::vector<std::string> rel_parts =
- base::SplitString(rel->second.value_or(""), HTTP_LWS,
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (std::find(rel_parts.begin(), rel_parts.end(),
- "payment-method-manifest") != rel_parts.end()) {
- InitiateDownload(url_.Resolve(payment_method_manifest_url),
- net::URLFetcher::GET);
- return;
- }
- }
- }
- } else {
- std::string content;
- if (source->GetResponseAsString(&content) && !content.empty()) {
- delegate_->OnManifestDownloadSuccess(content);
- return;
- }
- }
-
- delegate_->OnManifestDownloadFailure();
+bool PaymentManifestDownloader::IsValidManifestUrl(const GURL& url) {
+ return url.is_valid() &&
+ (url.SchemeIs(url::kHttpsScheme) ||
+ (url.SchemeIs(url::kHttpScheme) && allow_http_for_test_));
}
} // namespace payments
diff --git a/chromium/components/payments/content/payment_manifest_downloader.h b/chromium/components/payments/content/payment_manifest_downloader.h
index ac1a95ba59d..6ef80d3bff0 100644
--- a/chromium/components/payments/content/payment_manifest_downloader.h
+++ b/chromium/components/payments/content/payment_manifest_downloader.h
@@ -5,9 +5,11 @@
#ifndef COMPONENTS_PAYMENTS_CONTENT_PAYMENT_MANIFEST_DOWNLOADER_H_
#define COMPONENTS_PAYMENTS_CONTENT_PAYMENT_MANIFEST_DOWNLOADER_H_
+#include <map>
#include <memory>
#include <string>
+#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "net/url_request/url_fetcher.h"
@@ -28,34 +30,23 @@ namespace payments {
// HTTP response codes are 200.
class PaymentManifestDownloader : public net::URLFetcherDelegate {
public:
- // The interface for receiving the result of downloading a manifest.
- class Delegate {
- public:
- // Called when a manifest has been successfully downloaded.
- virtual void OnManifestDownloadSuccess(const std::string& content) = 0;
-
- // Called when failed to download the manifest for any reason:
- // - HTTP response code is not 200.
- // - HTTP GET on the manifest URL returns empty content.
- //
- // In the case of a payment method manifest download, can also be called
- // when:
- // - HTTP response headers are absent.
- // - HTTP response headers do not contain Link headers.
- // - Link header does not contain rel="payment-method-manifest".
- // - Link header does not contain a valid URL.
- virtual void OnManifestDownloadFailure() = 0;
-
- protected:
- virtual ~Delegate() {}
- };
-
- // |delegate| should not be null and must outlive this object. |url| should be
- // a valid URL with HTTPS scheme.
- PaymentManifestDownloader(
- const scoped_refptr<net::URLRequestContextGetter>& context,
- const GURL& url,
- Delegate* delegate);
+ // Called on completed download of a manifest. Download failure results in
+ // empty contents. Failure to download the manifest can happen because of the
+ // following reasons:
+ // - HTTP response code is not 200. (204 is also allowed for HEAD request.)
+ // - HTTP GET on the manifest URL returns empty content.
+ //
+ // In the case of a payment method manifest download, can also be called
+ // when:
+ // - HTTP response headers are absent.
+ // - HTTP response headers do not contain Link headers.
+ // - Link header does not contain rel="payment-method-manifest".
+ // - Link header does not contain a valid URL.
+ using DownloadCallback = base::OnceCallback<void(const std::string&)>;
+
+ // |delegate| should not be null and must outlive this object.
+ explicit PaymentManifestDownloader(
+ const scoped_refptr<net::URLRequestContextGetter>& context);
~PaymentManifestDownloader() override;
@@ -76,28 +67,48 @@ class PaymentManifestDownloader : public net::URLFetcherDelegate {
// The absolute location must use HTTPS scheme.
//
// 2) GET request for the payment method manifest file.
- void DownloadPaymentMethodManifest();
+ //
+ // |url| should be a valid URL with HTTPS scheme.
+ void DownloadPaymentMethodManifest(const GURL& url,
+ DownloadCallback callback);
// Download a web app manifest via a single HTTP request:
//
// 1) GET request for the payment method name.
- void DownloadWebAppManifest();
+ //
+ // |url| should be a valid URL with HTTPS scheme.
+ void DownloadWebAppManifest(const GURL& url, DownloadCallback callback);
+
+ // Allows HTTP URLs. Should be used only for testing.
+ void AllowHttpForTest();
private:
- void InitiateDownload(const GURL& url,
- net::URLFetcher::RequestType request_type);
+ // Information about an ongoing download request.
+ struct Download {
+ Download();
+ ~Download();
+
+ net::URLFetcher::RequestType request_type;
+ std::unique_ptr<net::URLFetcher> fetcher;
+ DownloadCallback callback;
+ };
// net::URLFetcherDelegate
void OnURLFetchComplete(const net::URLFetcher* source) override;
- scoped_refptr<net::URLRequestContextGetter> context_;
- const GURL url_;
+ void InitiateDownload(const GURL& url,
+ net::URLFetcher::RequestType request_type,
+ DownloadCallback callback);
+ bool IsValidManifestUrl(const GURL& url);
- // Non-owned. Never null. Outlives this object.
- Delegate* delegate_;
+ scoped_refptr<net::URLRequestContextGetter> context_;
+ bool allow_http_for_test_;
- bool is_downloading_http_link_header_;
- std::unique_ptr<net::URLFetcher> fetcher_;
+ // 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_;
DISALLOW_COPY_AND_ASSIGN(PaymentManifestDownloader);
};
diff --git a/chromium/components/payments/content/payment_manifest_downloader_unittest.cc b/chromium/components/payments/content/payment_manifest_downloader_unittest.cc
index bc7d26bcfb1..a1266601f58 100644
--- a/chromium/components/payments/content/payment_manifest_downloader_unittest.cc
+++ b/chromium/components/payments/content/payment_manifest_downloader_unittest.cc
@@ -15,22 +15,21 @@
namespace payments {
namespace {
-class PaymentMethodManifestDownloaderTest
- : public testing::Test,
- public PaymentManifestDownloader::Delegate {
+class PaymentMethodManifestDownloaderTest : public testing::Test {
public:
PaymentMethodManifestDownloaderTest()
: context_(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get())),
- downloader_(context_, GURL("https://bobpay.com"), this) {
- downloader_.DownloadPaymentMethodManifest();
+ downloader_(context_) {
+ downloader_.DownloadPaymentMethodManifest(
+ GURL("https://bobpay.com"),
+ base::BindOnce(&PaymentMethodManifestDownloaderTest::OnManifestDownload,
+ base::Unretained(this)));
}
~PaymentMethodManifestDownloaderTest() override {}
- // PaymentManifestDownloader::Delegate
- MOCK_METHOD1(OnManifestDownloadSuccess, void(const std::string& content));
- MOCK_METHOD0(OnManifestDownloadFailure, void());
+ MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
net::TestURLFetcher* fetcher() { return factory_.GetFetcherByID(0); }
@@ -46,7 +45,7 @@ class PaymentMethodManifestDownloaderTest
TEST_F(PaymentMethodManifestDownloaderTest, HttpHeadResponse404IsFailure) {
fetcher()->set_response_code(404);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -54,7 +53,7 @@ TEST_F(PaymentMethodManifestDownloaderTest, HttpHeadResponse404IsFailure) {
TEST_F(PaymentMethodManifestDownloaderTest, NoHttpHeadersIsFailure) {
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -65,7 +64,7 @@ TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpHeaderIsFailure) {
fetcher()->set_response_headers(headers);
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -77,7 +76,7 @@ TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpLinkHeaderIsFailure) {
fetcher()->set_response_headers(headers);
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -89,7 +88,7 @@ TEST_F(PaymentMethodManifestDownloaderTest, NoRelInHttpLinkHeaderIsFailure) {
fetcher()->set_response_headers(headers);
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -101,7 +100,7 @@ TEST_F(PaymentMethodManifestDownloaderTest, NoUrlInHttpLinkHeaderIsFailure) {
fetcher()->set_response_headers(headers);
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -114,7 +113,7 @@ TEST_F(PaymentMethodManifestDownloaderTest,
fetcher()->set_response_headers(headers);
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -128,7 +127,7 @@ TEST_F(PaymentMethodManifestDownloaderTest, HttpGetResponse404IsFailure) {
fetcher()->delegate()->OnURLFetchComplete(fetcher());
fetcher()->set_response_code(404);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -142,7 +141,7 @@ TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
fetcher()->delegate()->OnURLFetchComplete(fetcher());
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -157,7 +156,24 @@ TEST_F(PaymentMethodManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
fetcher()->SetResponseString("manifest content");
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadSuccess("manifest content"));
+ EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+
+ fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+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);
+
+ EXPECT_CALL(*this, OnManifestDownload("manifest content"));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -197,27 +213,26 @@ TEST_F(PaymentMethodManifestDownloaderTest, AbsoluteHttpHeaderLinkUrl) {
fetcher()->set_response_headers(headers);
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
-class WebAppManifestDownloaderTest
- : public testing::Test,
- public PaymentManifestDownloader::Delegate {
+class WebAppManifestDownloaderTest : public testing::Test {
public:
WebAppManifestDownloaderTest()
: context_(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get())),
- downloader_(context_, GURL("https://bobpay.com"), this) {
- downloader_.DownloadWebAppManifest();
+ downloader_(context_) {
+ downloader_.DownloadWebAppManifest(
+ GURL("https://bobpay.com"),
+ base::BindOnce(&WebAppManifestDownloaderTest::OnManifestDownload,
+ base::Unretained(this)));
}
~WebAppManifestDownloaderTest() override {}
- // PaymentManifestDownloader::Delegate
- MOCK_METHOD1(OnManifestDownloadSuccess, void(const std::string& content));
- MOCK_METHOD0(OnManifestDownloadFailure, void());
+ MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
net::TestURLFetcher* fetcher() { return factory_.GetFetcherByID(0); }
@@ -233,7 +248,7 @@ class WebAppManifestDownloaderTest
TEST_F(WebAppManifestDownloaderTest, HttpGetResponse404IsFailure) {
fetcher()->set_response_code(404);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -241,7 +256,7 @@ TEST_F(WebAppManifestDownloaderTest, HttpGetResponse404IsFailure) {
TEST_F(WebAppManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadFailure());
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
@@ -250,7 +265,7 @@ TEST_F(WebAppManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
fetcher()->SetResponseString("manifest content");
fetcher()->set_response_code(200);
- EXPECT_CALL(*this, OnManifestDownloadSuccess("manifest content"));
+ EXPECT_CALL(*this, OnManifestDownload("manifest content"));
fetcher()->delegate()->OnURLFetchComplete(fetcher());
}
diff --git a/chromium/components/payments/content/payment_manifest_parser_host.cc b/chromium/components/payments/content/payment_manifest_parser_host.cc
index 0af9a38c23b..aaea07c4aab 100644
--- a/chromium/components/payments/content/payment_manifest_parser_host.cc
+++ b/chromium/components/payments/content/payment_manifest_parser_host.cc
@@ -17,6 +17,11 @@
#include "url/url_constants.h"
namespace payments {
+namespace {
+
+const size_t kMaximumNumberOfItems = 100U;
+
+} // namespace
PaymentManifestParserHost::PaymentManifestParserHost() : callback_counter_(0) {}
@@ -37,7 +42,8 @@ void PaymentManifestParserHost::ParsePaymentMethodManifest(
const std::string& content,
PaymentMethodCallback callback) {
if (!mojo_client_) {
- std::move(callback).Run(std::vector<GURL>());
+ std::move(callback).Run(std::vector<GURL>(), std::vector<url::Origin>(),
+ false);
return;
}
@@ -72,7 +78,9 @@ void PaymentManifestParserHost::ParseWebAppManifest(const std::string& content,
void PaymentManifestParserHost::OnPaymentMethodParse(
int64_t callback_identifier,
- const std::vector<GURL>& web_app_manifest_urls) {
+ const std::vector<GURL>& web_app_manifest_urls,
+ const std::vector<url::Origin>& supported_origins,
+ bool all_origins_supported) {
const auto& pending_callback_it =
pending_payment_method_callbacks_.find(callback_identifier);
if (pending_callback_it == pending_payment_method_callbacks_.end()) {
@@ -82,8 +90,8 @@ void PaymentManifestParserHost::OnPaymentMethodParse(
return;
}
- const size_t kMaximumNumberOfWebAppUrls = 100U;
- if (web_app_manifest_urls.size() > kMaximumNumberOfWebAppUrls) {
+ if (web_app_manifest_urls.size() > kMaximumNumberOfItems ||
+ supported_origins.size() > kMaximumNumberOfItems) {
// If more than 100 items, then something went wrong in the utility
// process. Stop the utility process and notify all callbacks.
OnUtilityProcessStopped();
@@ -99,12 +107,30 @@ void PaymentManifestParserHost::OnPaymentMethodParse(
}
}
+ if (all_origins_supported && !supported_origins.empty()) {
+ // The format of the payment method manifest does not allow for both of
+ // these conditions to be true. Something went wrong in the utility process.
+ // Stop the utility process and notify all callbacks.
+ OnUtilityProcessStopped();
+ return;
+ }
+
+ for (const auto& origin : supported_origins) {
+ if (!origin.GetURL().is_valid() || origin.scheme() != url::kHttpsScheme) {
+ // If not a valid origin with HTTPS scheme, then something went wrong in
+ // the utility process. Stop the utility process and notify all callbacks.
+ OnUtilityProcessStopped();
+ return;
+ }
+ }
+
PaymentMethodCallback callback = std::move(pending_callback_it->second);
pending_payment_method_callbacks_.erase(pending_callback_it);
// Can trigger synchronous deletion of this object, so can't access any of
// the member variables after this block.
- std::move(callback).Run(web_app_manifest_urls);
+ std::move(callback).Run(web_app_manifest_urls, supported_origins,
+ all_origins_supported);
}
void PaymentManifestParserHost::OnWebAppParse(
@@ -119,8 +145,7 @@ void PaymentManifestParserHost::OnWebAppParse(
return;
}
- const size_t kMaximumNumberOfSections = 100U;
- if (manifest.size() > kMaximumNumberOfSections) {
+ if (manifest.size() > kMaximumNumberOfItems) {
// If more than 100 items, then something went wrong in the utility
// process. Stop the utility process and notify all callbacks.
OnUtilityProcessStopped();
@@ -128,8 +153,7 @@ void PaymentManifestParserHost::OnWebAppParse(
}
for (size_t i = 0; i < manifest.size(); ++i) {
- const size_t kMaximumNumberOfFingerprints = 100U;
- if (manifest[i]->fingerprints.size() > kMaximumNumberOfFingerprints) {
+ if (manifest[i]->fingerprints.size() > kMaximumNumberOfItems) {
// If more than 100 items, then something went wrong in the utility
// process. Stop the utility process and notify all callbacks.
OnUtilityProcessStopped();
@@ -148,15 +172,16 @@ void PaymentManifestParserHost::OnWebAppParse(
void PaymentManifestParserHost::OnUtilityProcessStopped() {
mojo_client_.reset();
- std::unordered_map<int64_t, PaymentMethodCallback> payment_method_callbacks =
+ std::map<int64_t, PaymentMethodCallback> payment_method_callbacks =
std::move(pending_payment_method_callbacks_);
- std::unordered_map<int64_t, WebAppCallback> web_app_callbacks =
+ std::map<int64_t, WebAppCallback> web_app_callbacks =
std::move(pending_web_app_callbacks_);
for (auto& callback : payment_method_callbacks) {
// Can trigger synchronous deletion of this object, so can't access any of
// the member variables after this line.
- std::move(callback.second).Run(std::vector<GURL>());
+ std::move(callback.second)
+ .Run(std::vector<GURL>(), std::vector<url::Origin>(), false);
}
for (auto& callback : web_app_callbacks) {
diff --git a/chromium/components/payments/content/payment_manifest_parser_host.h b/chromium/components/payments/content/payment_manifest_parser_host.h
index eb6887554c7..7d35832861a 100644
--- a/chromium/components/payments/content/payment_manifest_parser_host.h
+++ b/chromium/components/payments/content/payment_manifest_parser_host.h
@@ -7,14 +7,16 @@
#include <stdint.h>
+#include <map>
#include <memory>
-#include <unordered_map>
+#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "components/payments/mojom/payment_manifest_parser.mojom.h"
#include "url/gurl.h"
+#include "url/origin.h"
namespace content {
template <class MojoInterface>
@@ -23,12 +25,15 @@ class UtilityProcessMojoClient;
namespace payments {
+class PaymentManifestParserHostTest;
+
// Host of the utility process that parses manifest contents.
class PaymentManifestParserHost {
public:
- // Called on successful parsing of a payment method manifest. The result is a
- // move-only vector, which is empty on parse failure.
- using PaymentMethodCallback = base::OnceCallback<void(std::vector<GURL>)>;
+ // Called on successful parsing of a payment method manifest. Parse failure
+ // results in empty vectors and "false".
+ using PaymentMethodCallback = base::OnceCallback<
+ void(const std::vector<GURL>&, const std::vector<url::Origin>&, bool)>;
// Called on successful parsing of a web app manifest. The result is a
// move-only vector, which is empty on parse failure.
using WebAppCallback =
@@ -48,8 +53,12 @@ class PaymentManifestParserHost {
void ParseWebAppManifest(const std::string& content, WebAppCallback callback);
private:
+ friend class PaymentManifestParserHostTest;
+
void OnPaymentMethodParse(int64_t callback_identifier,
- const std::vector<GURL>& web_app_manifest_urls);
+ const std::vector<GURL>& web_app_manifest_urls,
+ const std::vector<url::Origin>& supported_origins,
+ bool all_origins_supported);
void OnWebAppParse(int64_t callback_identifier,
std::vector<mojom::WebAppManifestSectionPtr> manifest);
@@ -60,9 +69,8 @@ class PaymentManifestParserHost {
mojo_client_;
int64_t callback_counter_;
- std::unordered_map<int64_t, PaymentMethodCallback>
- pending_payment_method_callbacks_;
- std::unordered_map<int64_t, WebAppCallback> pending_web_app_callbacks_;
+ std::map<int64_t, PaymentMethodCallback> pending_payment_method_callbacks_;
+ std::map<int64_t, WebAppCallback> pending_web_app_callbacks_;
DISALLOW_COPY_AND_ASSIGN(PaymentManifestParserHost);
};
diff --git a/chromium/components/payments/content/payment_request.cc b/chromium/components/payments/content/payment_request.cc
index 1363bb50a08..1a6e063a587 100644
--- a/chromium/components/payments/content/payment_request.cc
+++ b/chromium/components/payments/content/payment_request.cc
@@ -36,14 +36,15 @@ PaymentRequest::PaymentRequest(
observer_for_testing_(observer_for_testing),
journey_logger_(delegate_->IsIncognito(),
web_contents_->GetLastCommittedURL(),
- delegate_->GetUkmRecorder()) {
+ delegate_->GetUkmRecorder()),
+ 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
// erroneous in nature).
// TODO(crbug.com/683636): Investigate using
// set_connection_error_with_reason_handler with Binding::CloseWithReason.
binding_.set_connection_error_handler(base::Bind(
- &PaymentRequest::OnConnectionTerminated, base::Unretained(this)));
+ &PaymentRequest::OnConnectionTerminated, weak_ptr_factory_.GetWeakPtr()));
}
PaymentRequest::~PaymentRequest() {}
@@ -85,7 +86,7 @@ void PaymentRequest::Init(mojom::PaymentRequestClientPtr client,
delegate_->GetApplicationLocale());
state_ = base::MakeUnique<PaymentRequestState>(
spec_.get(), this, delegate_->GetApplicationLocale(),
- delegate_->GetPersonalDataManager(), delegate_.get());
+ delegate_->GetPersonalDataManager(), delegate_.get(), &journey_logger_);
return;
}
@@ -107,7 +108,7 @@ void PaymentRequest::Init(mojom::PaymentRequestClientPtr client,
delegate_->GetApplicationLocale());
state_ = base::MakeUnique<PaymentRequestState>(
spec_.get(), this, delegate_->GetApplicationLocale(),
- delegate_->GetPersonalDataManager(), delegate_.get());
+ delegate_->GetPersonalDataManager(), delegate_.get(), &journey_logger_);
}
void PaymentRequest::Show() {
@@ -120,12 +121,18 @@ void PaymentRequest::Show() {
// A tab can display only one PaymentRequest UI at a time.
if (!manager_->CanShow(this)) {
LOG(ERROR) << "A PaymentRequest UI is already showing";
+ journey_logger_.SetNotShown(
+ JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
+ has_recorded_completion_ = true;
client_->OnError(mojom::PaymentErrorReason::USER_CANCEL);
OnConnectionTerminated();
return;
}
if (!state_->AreRequestedMethodsSupported()) {
+ journey_logger_.SetNotShown(
+ JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
+ has_recorded_completion_ = true;
client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED);
if (observer_for_testing_)
observer_for_testing_->OnNotSupportedError();
@@ -134,6 +141,11 @@ void PaymentRequest::Show() {
}
journey_logger_.SetShowCalled();
+ journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
+ journey_logger_.SetRequestedInformation(
+ spec_->request_shipping(), spec_->request_payer_email(),
+ spec_->request_payer_phone(), spec_->request_payer_name());
+
delegate_->ShowDialog(this);
}
@@ -144,6 +156,13 @@ void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
OnConnectionTerminated();
return;
}
+
+ if (!details->total) {
+ LOG(ERROR) << "Missing total";
+ OnConnectionTerminated();
+ return;
+ }
+
spec_->UpdateWith(std::move(details));
}
@@ -170,7 +189,9 @@ void PaymentRequest::Complete(mojom::PaymentComplete result) {
if (!client_.is_bound())
return;
- if (result != mojom::PaymentComplete::SUCCESS) {
+ // Failed transactions show an error. Successful and unknown-state
+ // transactions don't show an error.
+ if (result == mojom::PaymentComplete::FAIL) {
delegate_->ShowErrorMessage();
} else {
DCHECK(!has_recorded_completion_);
@@ -217,6 +238,8 @@ void PaymentRequest::CanMakePayment() {
void PaymentRequest::OnPaymentResponseAvailable(
mojom::PaymentResponsePtr response) {
+ journey_logger_.SetEventOccurred(
+ JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
client_->OnPaymentResponse(std::move(response));
}
@@ -272,6 +295,9 @@ void PaymentRequest::OnConnectionTerminated() {
}
void PaymentRequest::Pay() {
+ journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
+ journey_logger_.SetSelectedPaymentMethod(
+ JourneyLogger::SELECTED_PAYMENT_METHOD_CREDIT_CARD);
state_->GeneratePaymentResponse();
}
diff --git a/chromium/components/payments/content/payment_request.h b/chromium/components/payments/content/payment_request.h
index e0ab3fa4cd8..d4145f843a2 100644
--- a/chromium/components/payments/content/payment_request.h
+++ b/chromium/components/payments/content/payment_request.h
@@ -9,13 +9,14 @@
#include <vector>
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "components/payments/content/payment_request_spec.h"
#include "components/payments/content/payment_request_state.h"
#include "components/payments/core/journey_logger.h"
#include "components/payments/core/payment_request_delegate.h"
-#include "components/payments/mojom/payment_request.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
#include "url/gurl.h"
namespace content {
@@ -126,6 +127,8 @@ class PaymentRequest : public mojom::PaymentRequest,
// Whether a completion was already recorded for this Payment Request.
bool has_recorded_completion_ = false;
+ base::WeakPtrFactory<PaymentRequest> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(PaymentRequest);
};
diff --git a/chromium/components/payments/content/payment_request_spec.cc b/chromium/components/payments/content/payment_request_spec.cc
index 252fe150417..364214bc72f 100644
--- a/chromium/components/payments/content/payment_request_spec.cc
+++ b/chromium/components/payments/content/payment_request_spec.cc
@@ -6,8 +6,11 @@
#include <utility>
+#include "base/feature_list.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
+#include "components/payments/core/features.h"
+#include "components/payments/core/payment_instrument.h"
#include "components/payments/core/payment_method_data.h"
#include "components/payments/core/payment_request_data_util.h"
#include "components/strings/grit/components_strings.h"
@@ -42,6 +45,87 @@ std::string GetBasicCardNetworkName(const mojom::BasicCardNetwork& network) {
return std::string();
}
+// Returns the card type associated with the given BasicCardType.
+autofill::CreditCard::CardType GetBasicCardType(
+ const mojom::BasicCardType& type) {
+ switch (type) {
+ case mojom::BasicCardType::CREDIT:
+ return autofill::CreditCard::CARD_TYPE_CREDIT;
+ case mojom::BasicCardType::DEBIT:
+ return autofill::CreditCard::CARD_TYPE_DEBIT;
+ case mojom::BasicCardType::PREPAID:
+ return autofill::CreditCard::CARD_TYPE_PREPAID;
+ }
+ NOTREACHED();
+ return autofill::CreditCard::CARD_TYPE_UNKNOWN;
+}
+
+PaymentMethodData CreatePaymentMethodData(
+ const mojom::PaymentMethodDataPtr& method_data_entry) {
+ PaymentMethodData method_data;
+ method_data.supported_methods = method_data_entry->supported_methods;
+
+ // Transfer the supported basic card networks (visa, amex) and types
+ // (credit, debit).
+ for (const mojom::BasicCardNetwork& network :
+ method_data_entry->supported_networks) {
+ method_data.supported_networks.push_back(GetBasicCardNetworkName(network));
+ }
+ for (const mojom::BasicCardType& type : method_data_entry->supported_types) {
+ autofill::CreditCard::CardType card_type = GetBasicCardType(type);
+ method_data.supported_types.insert(card_type);
+ }
+ return method_data;
+}
+
+// Validates the |method_data| and fills |supported_card_networks_|,
+// |supported_card_networks_set_|, |basic_card_specified_networks_|,
+// and |url_payment_method_identifiers_|.
+void PopulateValidatedMethodData(
+ const std::vector<PaymentMethodData>& method_data_vector,
+ std::vector<std::string>* supported_card_networks,
+ std::set<std::string>* basic_card_specified_networks,
+ std::set<std::string>* supported_card_networks_set,
+ std::set<autofill::CreditCard::CardType>* supported_card_types_set,
+ std::vector<std::string>* url_payment_method_identifiers,
+ std::map<std::string, std::set<std::string>>* stringified_method_data) {
+ data_util::ParseSupportedMethods(method_data_vector, supported_card_networks,
+ basic_card_specified_networks,
+ url_payment_method_identifiers);
+ supported_card_networks_set->insert(supported_card_networks->begin(),
+ supported_card_networks->end());
+
+ data_util::ParseSupportedCardTypes(method_data_vector,
+ supported_card_types_set);
+}
+
+void PopulateValidatedMethodData(
+ const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom,
+ std::vector<std::string>* supported_card_networks,
+ std::set<std::string>* basic_card_specified_networks,
+ std::set<std::string>* supported_card_networks_set,
+ std::set<autofill::CreditCard::CardType>* supported_card_types_set,
+ std::vector<std::string>* url_payment_method_identifiers,
+ std::map<std::string, std::set<std::string>>* stringified_method_data) {
+ std::vector<PaymentMethodData> method_data_vector;
+ 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);
+ }
+
+ method_data_vector.push_back(CreatePaymentMethodData(method_data_entry));
+ }
+
+ PopulateValidatedMethodData(
+ method_data_vector, supported_card_networks,
+ basic_card_specified_networks, supported_card_networks_set,
+ supported_card_types_set, url_payment_method_identifiers,
+ stringified_method_data);
+}
+
} // namespace
const char kBasicCardMethodName[] = "basic-card";
@@ -59,7 +143,10 @@ PaymentRequestSpec::PaymentRequestSpec(
if (observer)
AddObserver(observer);
UpdateSelectedShippingOption(/*after_update=*/false);
- PopulateValidatedMethodData(method_data);
+ PopulateValidatedMethodData(
+ method_data, &supported_card_networks_, &basic_card_specified_networks_,
+ &supported_card_networks_set_, &supported_card_types_set_,
+ &url_payment_method_identifiers_, &stringified_method_data_);
}
PaymentRequestSpec::~PaymentRequestSpec() {}
@@ -146,37 +233,63 @@ bool PaymentRequestSpec::IsMixedCurrency() const {
});
}
-void PaymentRequestSpec::PopulateValidatedMethodData(
- const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom) {
- std::vector<PaymentMethodData> method_data_vector;
- 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);
+const mojom::PaymentItemPtr& PaymentRequestSpec::GetTotal(
+ PaymentInstrument* selected_instrument) const {
+ const mojom::PaymentDetailsModifierPtr* modifier =
+ GetApplicableModifier(selected_instrument);
+ return modifier ? (*modifier)->total : details().total;
+}
+
+std::vector<const mojom::PaymentItemPtr*> PaymentRequestSpec::GetDisplayItems(
+ PaymentInstrument* selected_instrument) const {
+ std::vector<const mojom::PaymentItemPtr*> display_items;
+ const mojom::PaymentDetailsModifierPtr* modifier =
+ GetApplicableModifier(selected_instrument);
+ for (const auto& item : details().display_items) {
+ display_items.push_back(&item);
+ }
+
+ if (modifier) {
+ for (const auto& additional_item : (*modifier)->additional_display_items) {
+ display_items.push_back(&additional_item);
}
+ }
+ return display_items;
+}
+
+const std::vector<mojom::PaymentShippingOptionPtr>&
+PaymentRequestSpec::GetShippingOptions() const {
+ return details().shipping_options;
+}
+
+const mojom::PaymentDetailsModifierPtr*
+PaymentRequestSpec::GetApplicableModifier(
+ PaymentInstrument* selected_instrument) const {
+ if (!selected_instrument ||
+ !base::FeatureList::IsEnabled(features::kWebPaymentsModifiers))
+ return nullptr;
- PaymentMethodData method_data;
- method_data.supported_methods = method_data_entry->supported_methods;
- // Transfer the supported basic card networks.
+ for (const auto& modifier : details().modifiers) {
std::vector<std::string> supported_networks;
- for (const mojom::BasicCardNetwork& network :
- method_data_entry->supported_networks) {
- supported_networks.push_back(GetBasicCardNetworkName(network));
- }
- method_data.supported_networks = std::move(supported_networks);
+ std::set<autofill::CreditCard::CardType> supported_types;
+ // The following 4 are unused but required by PopulateValidatedMethodData.
+ std::set<std::string> basic_card_specified_networks;
+ std::set<std::string> supported_card_networks_set;
+ std::vector<std::string> url_payment_method_identifiers;
+ std::map<std::string, std::set<std::string>> stringified_method_data;
+ PopulateValidatedMethodData(
+ {CreatePaymentMethodData(modifier->method_data)}, &supported_networks,
+ &basic_card_specified_networks, &supported_card_networks_set,
+ &supported_types, &url_payment_method_identifiers,
+ &stringified_method_data);
- // TODO(crbug.com/708603): Add browser-side support for
- // |method_data.supported_types|.
- method_data_vector.push_back(std::move(method_data));
+ if (selected_instrument->IsValidForModifier(
+ modifier->method_data->supported_methods, supported_types,
+ supported_networks)) {
+ return &modifier;
+ }
}
-
- data_util::ParseBasicCardSupportedNetworks(method_data_vector,
- &supported_card_networks_,
- &basic_card_specified_networks_);
- supported_card_networks_set_.insert(supported_card_networks_.begin(),
- supported_card_networks_.end());
+ return nullptr;
}
void PaymentRequestSpec::UpdateSelectedShippingOption(bool after_update) {
diff --git a/chromium/components/payments/content/payment_request_spec.h b/chromium/components/payments/content/payment_request_spec.h
index 1835c75ef06..798e31dd360 100644
--- a/chromium/components/payments/content/payment_request_spec.h
+++ b/chromium/components/payments/content/payment_request_spec.h
@@ -13,12 +13,15 @@
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
+#include "components/autofill/core/browser/credit_card.h"
#include "components/payments/core/currency_formatter.h"
#include "components/payments/core/payment_options_provider.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
+class PaymentInstrument;
+
// Identifier for the basic card payment method in the PaymentMethodData.
extern const char kBasicCardMethodName[];
@@ -82,6 +85,13 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
const {
return stringified_method_data_;
}
+ const std::set<autofill::CreditCard::CardType>& supported_card_types_set()
+ const {
+ return supported_card_types_set_;
+ }
+ const std::vector<std::string>& url_payment_method_identifiers() const {
+ return url_payment_method_identifiers_;
+ }
// Returns whether the |method_name| was specified as supported through the
// "basic-card" payment method. If false, it means either the |method_name| is
// not supported at all, or specified directly in supportedMethods.
@@ -107,19 +117,30 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
return selected_shipping_option_error_;
}
- const mojom::PaymentDetails& details() const { return *details_.get(); }
-
void StartWaitingForUpdateWith(UpdateReason reason);
-
bool IsMixedCurrency() const;
UpdateReason current_update_reason() const { return current_update_reason_; }
+ // Returns the total object of this payment request, taking into account the
+ // applicable modifier for |selected_instrument| if any.
+ const mojom::PaymentItemPtr& GetTotal(
+ PaymentInstrument* selected_instrument) const;
+ // Returns the display items for this payment request, taking into account the
+ // applicable modifier for |selected_instrument| if any.
+ std::vector<const mojom::PaymentItemPtr*> GetDisplayItems(
+ PaymentInstrument* selected_instrument) const;
+
+ const std::vector<mojom::PaymentShippingOptionPtr>& GetShippingOptions()
+ const;
+
private:
- // Validates the |method_data| and fills |supported_card_networks_|,
- // |supported_card_networks_set_| and |basic_card_specified_networks_|.
- void PopulateValidatedMethodData(
- const std::vector<mojom::PaymentMethodDataPtr>& method_data);
+ // Returns the first applicable modifier in the Payment Request for the
+ // |selected_instrument|.
+ const mojom::PaymentDetailsModifierPtr* GetApplicableModifier(
+ PaymentInstrument* selected_instrument) const;
+
+ const mojom::PaymentDetails& details() const { return *details_.get(); }
// Updates the |selected_shipping_option| based on the data passed to this
// payment request by the website. This will set selected_shipping_option_ to
@@ -157,10 +178,18 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
std::vector<std::string> supported_card_networks_;
std::set<std::string> supported_card_networks_set_;
+ std::set<autofill::CreditCard::CardType> supported_card_types_set_;
+
// Only the set of basic-card specified networks. NOTE: callers should use
// |supported_card_networks_set_| to check merchant support.
std::set<std::string> basic_card_specified_networks_;
+ // A list of supported url-based payment method identifers specified by the
+ // merchant. This encompasses one of the two types of payment method
+ // identifers, the other being standardized payment method identifiers i.e.,
+ // basic-card.
+ std::vector<std::string> url_payment_method_identifiers_;
+
// A mapping of the payment method names to the corresponding JSON-stringified
// payment method specific data.
std::map<std::string, std::set<std::string>> stringified_method_data_;
diff --git a/chromium/components/payments/content/payment_request_spec_unittest.cc b/chromium/components/payments/content/payment_request_spec_unittest.cc
index 235a17adffc..e5388c7886b 100644
--- a/chromium/components/payments/content/payment_request_spec_unittest.cc
+++ b/chromium/components/payments/content/payment_request_spec_unittest.cc
@@ -8,9 +8,9 @@
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
-#include "components/payments/mojom/payment_request.mojom.h"
#include "components/strings/grit/components_strings.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
#include "ui/base/l10n/l10n_util.h"
namespace payments {
diff --git a/chromium/components/payments/content/payment_request_state.cc b/chromium/components/payments/content/payment_request_state.cc
index 47a068ec878..b0a03ebac06 100644
--- a/chromium/components/payments/content/payment_request_state.cc
+++ b/chromium/components/payments/content/payment_request_state.cc
@@ -16,6 +16,7 @@
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/payments/content/payment_response_helper.h"
#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/journey_logger.h"
#include "components/payments/core/payment_instrument.h"
#include "components/payments/core/payment_request_data_util.h"
#include "components/payments/core/payment_request_delegate.h"
@@ -27,13 +28,15 @@ PaymentRequestState::PaymentRequestState(
Delegate* delegate,
const std::string& app_locale,
autofill::PersonalDataManager* personal_data_manager,
- PaymentRequestDelegate* payment_request_delegate)
+ PaymentRequestDelegate* payment_request_delegate,
+ JourneyLogger* journey_logger)
: is_ready_to_pay_(false),
is_waiting_for_merchant_validation_(false),
app_locale_(app_locale),
spec_(spec),
delegate_(delegate),
personal_data_manager_(personal_data_manager),
+ journey_logger_(journey_logger),
selected_shipping_profile_(nullptr),
selected_shipping_option_error_profile_(nullptr),
selected_contact_profile_(nullptr),
@@ -77,11 +80,10 @@ void PaymentRequestState::OnSpecUpdated() {
}
bool PaymentRequestState::CanMakePayment() const {
- for (const std::unique_ptr<PaymentInstrument>& instrument :
- available_instruments_) {
+ for (const auto& instrument : available_instruments_) {
if (instrument->IsValidForCanMakePayment()) {
// AddAutofillPaymentInstrument() filters out available instruments based
- // on supported card networks.
+ // on supported card networks (visa, amex) and types (credit, debit).
DCHECK(spec_->supported_card_networks_set().find(
instrument->method_name()) !=
spec_->supported_card_networks_set().end());
@@ -144,15 +146,26 @@ void PaymentRequestState::AddAutofillPaymentInstrument(
std::string basic_card_network =
autofill::data_util::GetPaymentRequestData(card.network())
.basic_card_issuer_network;
- if (!spec_->supported_card_networks_set().count(basic_card_network))
+ if (!spec_->supported_card_networks_set().count(basic_card_network) ||
+ !spec_->supported_card_types_set().count(card.card_type())) {
return;
+ }
+
+ // The total number of card types: credit, debit, prepaid, unknown.
+ constexpr size_t kTotalNumberOfCardTypes = 4U;
+
+ // Whether the card type (credit, debit, prepaid) matches thetype that the
+ // merchant has requested exactly. This should be false for unknown card
+ // types, if the merchant cannot accept some card types.
+ bool matches_merchant_card_type_exactly =
+ card.card_type() != autofill::CreditCard::CARD_TYPE_UNKNOWN ||
+ spec_->supported_card_types_set().size() == kTotalNumberOfCardTypes;
// AutofillPaymentInstrument makes a copy of |card| so it is effectively
// owned by this object.
- std::unique_ptr<PaymentInstrument> instrument =
- base::MakeUnique<AutofillPaymentInstrument>(
- basic_card_network, card, shipping_profiles_, app_locale_,
- payment_request_delegate_);
+ auto instrument = base::MakeUnique<AutofillPaymentInstrument>(
+ basic_card_network, card, matches_merchant_card_type_exactly,
+ shipping_profiles_, app_locale_, payment_request_delegate_);
available_instruments_.push_back(std::move(instrument));
if (selected)
@@ -241,6 +254,10 @@ bool PaymentRequestState::IsPaymentAppInvoked() const {
return !!response_helper_;
}
+AddressNormalizer* PaymentRequestState::GetAddressNormalizer() {
+ return payment_request_delegate_->GetAddressNormalizer();
+}
+
void PaymentRequestState::PopulateProfileCache() {
std::vector<autofill::AutofillProfile*> profiles =
personal_data_manager_->GetProfilesToSuggest();
@@ -262,12 +279,43 @@ void PaymentRequestState::PopulateProfileCache() {
shipping_profiles_ = profile_comparator()->FilterProfilesForShipping(
raw_profiles_for_filtering);
+ // Set the number of suggestions shown for the sections requested by the
+ // merchant.
+ if (spec_->request_payer_name() || spec_->request_payer_phone() ||
+ spec_->request_payer_email()) {
+ bool has_complete_contact =
+ contact_profiles_.empty()
+ ? false
+ : profile_comparator()->IsContactInfoComplete(contact_profiles_[0]);
+ journey_logger_->SetNumberOfSuggestionsShown(
+ JourneyLogger::Section::SECTION_CONTACT_INFO, contact_profiles_.size(),
+ has_complete_contact);
+ }
+ if (spec_->request_shipping()) {
+ bool has_complete_shipping =
+ shipping_profiles_.empty()
+ ? false
+ : profile_comparator()->IsShippingComplete(shipping_profiles_[0]);
+ journey_logger_->SetNumberOfSuggestionsShown(
+ JourneyLogger::Section::SECTION_SHIPPING_ADDRESS,
+ shipping_profiles_.size(), has_complete_shipping);
+ }
+
// Create the list of available instruments. A copy of each card will be made
// by their respective AutofillPaymentInstrument.
const std::vector<autofill::CreditCard*>& cards =
personal_data_manager_->GetCreditCardsToSuggest();
for (autofill::CreditCard* card : cards)
AddAutofillPaymentInstrument(/*selected=*/false, *card);
+
+ bool has_complete_instrument =
+ available_instruments().empty()
+ ? false
+ : available_instruments()[0]->IsCompleteForPayment();
+
+ journey_logger_->SetNumberOfSuggestionsShown(
+ JourneyLogger::Section::SECTION_PAYMENT_METHOD,
+ available_instruments().size(), has_complete_instrument);
}
void PaymentRequestState::SetDefaultProfileSelections() {
@@ -293,7 +341,8 @@ void PaymentRequestState::SetDefaultProfileSelections() {
auto first_complete_instrument =
std::find_if(instruments.begin(), instruments.end(),
[](const std::unique_ptr<PaymentInstrument>& instrument) {
- return instrument->IsCompleteForPayment();
+ return instrument->IsCompleteForPayment() &&
+ instrument->IsExactlyMatchingMerchantRequest();
});
selected_instrument_ = first_complete_instrument == instruments.end()
? nullptr
diff --git a/chromium/components/payments/content/payment_request_state.h b/chromium/components/payments/content/payment_request_state.h
index a62d2b84eae..2027490a2a6 100644
--- a/chromium/components/payments/content/payment_request_state.h
+++ b/chromium/components/payments/content/payment_request_state.h
@@ -15,7 +15,7 @@
#include "components/payments/content/payment_response_helper.h"
#include "components/payments/core/address_normalizer.h"
#include "components/payments/core/payments_profile_comparator.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace autofill {
class AutofillProfile;
@@ -26,6 +26,7 @@ class RegionDataLoader;
namespace payments {
+class JourneyLogger;
class PaymentInstrument;
class PaymentRequestDelegate;
@@ -70,7 +71,8 @@ class PaymentRequestState : public PaymentResponseHelper::Delegate,
Delegate* delegate,
const std::string& app_locale,
autofill::PersonalDataManager* personal_data_manager,
- PaymentRequestDelegate* payment_request_delegate);
+ PaymentRequestDelegate* payment_request_delegate,
+ JourneyLogger* journey_logger);
~PaymentRequestState() override;
// PaymentResponseHelper::Delegate
@@ -183,6 +185,8 @@ class PaymentRequestState : public PaymentResponseHelper::Delegate,
// generation has begun. False otherwise.
bool IsPaymentAppInvoked() const;
+ AddressNormalizer* GetAddressNormalizer();
+
private:
// Fetches the Autofill Profiles for this user from the PersonalDataManager,
// and stores copies of them, owned by this PaymentRequestState, in
@@ -215,10 +219,11 @@ class PaymentRequestState : public PaymentResponseHelper::Delegate,
const std::string app_locale_;
- // Not owned. Never null. Both outlive this object.
+ // Not owned. Never null. Will outlive this object.
PaymentRequestSpec* spec_;
Delegate* delegate_;
autofill::PersonalDataManager* personal_data_manager_;
+ JourneyLogger* journey_logger_;
autofill::AutofillProfile* selected_shipping_profile_;
autofill::AutofillProfile* selected_shipping_option_error_profile_;
diff --git a/chromium/components/payments/content/payment_request_state_unittest.cc b/chromium/components/payments/content/payment_request_state_unittest.cc
index 186fca9ea65..0e0a014ddae 100644
--- a/chromium/components/payments/content/payment_request_state_unittest.cc
+++ b/chromium/components/payments/content/payment_request_state_unittest.cc
@@ -13,9 +13,10 @@
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/payments/content/payment_request_spec.h"
+#include "components/payments/core/journey_logger.h"
#include "components/payments/core/test_payment_request_delegate.h"
-#include "components/payments/mojom/payment_request.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
@@ -26,6 +27,9 @@ class PaymentRequestStateTest : public testing::Test,
PaymentRequestStateTest()
: num_on_selected_information_changed_called_(0),
test_payment_request_delegate_(/*personal_data_manager=*/nullptr),
+ journey_logger_(test_payment_request_delegate_.IsIncognito(),
+ GURL("http://www.test.com"),
+ test_payment_request_delegate_.GetUkmRecorder()),
address_(autofill::test::GetFullProfile()),
credit_card_visa_(autofill::test::GetCreditCard()) {
test_personal_data_manager_.AddTestingProfile(&address_);
@@ -59,7 +63,7 @@ class PaymentRequestStateTest : public testing::Test,
/*observer=*/nullptr, "en-US");
state_ = base::MakeUnique<PaymentRequestState>(
spec_.get(), this, "en-US", &test_personal_data_manager_,
- &test_payment_request_delegate_);
+ &test_payment_request_delegate_, &journey_logger_);
state_->AddObserver(this);
}
@@ -115,6 +119,7 @@ class PaymentRequestStateTest : public testing::Test,
mojom::PaymentAddressPtr selected_shipping_address_;
autofill::TestPersonalDataManager test_personal_data_manager_;
TestPaymentRequestDelegate test_payment_request_delegate_;
+ JourneyLogger journey_logger_;
// Test data.
autofill::AutofillProfile address_;
diff --git a/chromium/components/payments/content/payment_request_web_contents_manager.h b/chromium/components/payments/content/payment_request_web_contents_manager.h
index 25a47effca1..8e35e70487c 100644
--- a/chromium/components/payments/content/payment_request_web_contents_manager.h
+++ b/chromium/components/payments/content/payment_request_web_contents_manager.h
@@ -10,10 +10,10 @@
#include "base/macros.h"
#include "components/payments/content/payment_request.h"
-#include "components/payments/mojom/payment_request.mojom.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace content {
class RenderFrameHost;
diff --git a/chromium/components/payments/content/payment_response_helper.h b/chromium/components/payments/content/payment_response_helper.h
index b9cf024718e..fb87bdbf8ef 100644
--- a/chromium/components/payments/content/payment_response_helper.h
+++ b/chromium/components/payments/content/payment_response_helper.h
@@ -9,7 +9,7 @@
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/payments/core/address_normalizer.h"
#include "components/payments/core/payment_instrument.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
diff --git a/chromium/components/payments/content/payment_response_helper_unittest.cc b/chromium/components/payments/content/payment_response_helper_unittest.cc
index 6279886ca02..706cbba08ee 100644
--- a/chromium/components/payments/content/payment_response_helper_unittest.cc
+++ b/chromium/components/payments/content/payment_response_helper_unittest.cc
@@ -18,8 +18,8 @@
#include "components/payments/content/payment_request_spec.h"
#include "components/payments/core/autofill_payment_instrument.h"
#include "components/payments/core/test_payment_request_delegate.h"
-#include "components/payments/mojom/payment_request.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
@@ -37,8 +37,8 @@ class PaymentResponseHelperTest : public testing::Test,
visa_card.set_billing_address_id(address_.guid());
visa_card.set_use_count(5u);
autofill_instrument_ = base::MakeUnique<AutofillPaymentInstrument>(
- "visa", visa_card, billing_addresses_, "en-US",
- &test_payment_request_delegate_);
+ "visa", visa_card, /*matches_merchant_card_type_exactly=*/true,
+ billing_addresses_, "en-US", &test_payment_request_delegate_);
}
~PaymentResponseHelperTest() override {}
@@ -120,11 +120,14 @@ TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_SupportedMethod) {
"{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
"\"city\":\"Elysium\","
"\"country\":\"US\","
+ "\"dependentLocality\":\"\","
+ "\"languageCode\":\"\","
"\"organization\":\"Underworld\","
"\"phone\":\"16502111111\","
"\"postalCode\":\"91111\","
"\"recipient\":\"John H. Doe\","
- "\"region\":\"CA\"},"
+ "\"region\":\"CA\","
+ "\"sortingCode\":\"\"},"
"\"cardNumber\":\"4111111111111111\","
"\"cardSecurityCode\":\"123\","
"\"cardholderName\":\"Test User\","
@@ -156,11 +159,14 @@ TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_BasicCard) {
"{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
"\"city\":\"Elysium\","
"\"country\":\"US\","
+ "\"dependentLocality\":\"\","
+ "\"languageCode\":\"\","
"\"organization\":\"Underworld\","
"\"phone\":\"16502111111\","
"\"postalCode\":\"91111\","
"\"recipient\":\"John H. Doe\","
- "\"region\":\"CA\"},"
+ "\"region\":\"CA\","
+ "\"sortingCode\":\"\"},"
"\"cardNumber\":\"4111111111111111\","
"\"cardSecurityCode\":\"123\","
"\"cardholderName\":\"Test User\","
diff --git a/chromium/components/payments/content/payments_validators.h b/chromium/components/payments/content/payments_validators.h
index 33af75925be..331e185e2b9 100644
--- a/chromium/components/payments/content/payments_validators.h
+++ b/chromium/components/payments/content/payments_validators.h
@@ -8,7 +8,7 @@
#include <string>
#include "base/macros.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
namespace payments {
diff --git a/chromium/components/payments/content/utility/fingerprint_parser.cc b/chromium/components/payments/content/utility/fingerprint_parser.cc
index 594d58908b6..98cb171fc3b 100644
--- a/chromium/components/payments/content/utility/fingerprint_parser.cc
+++ b/chromium/components/payments/content/utility/fingerprint_parser.cc
@@ -24,11 +24,17 @@ uint8_t HexDigitToByte(char c) {
std::vector<uint8_t> FingerprintStringToByteArray(const std::string& input) {
std::vector<uint8_t> output;
- if (input.size() != 32 * 3 - 1)
+ const size_t kLength = 32 * 3 - 1;
+ if (input.size() != kLength) {
+ LOG(ERROR) << "Fingerprint \"" << input << "\" should contain exactly "
+ << kLength << " characters.";
return output;
+ }
for (size_t i = 0; i < input.size(); i += 3) {
if (i < input.size() - 2 && input[i + 2] != ':') {
+ LOG(ERROR) << "Bytes in fingerprint \"" << input
+ << "\" should separated by \":\" characters.";
output.clear();
return output;
}
@@ -36,6 +42,8 @@ std::vector<uint8_t> FingerprintStringToByteArray(const std::string& input) {
char big_end = input[i];
char little_end = input[i + 1];
if (!IsUpperCaseHexDigit(big_end) || !IsUpperCaseHexDigit(little_end)) {
+ LOG(ERROR) << "Bytes in fingerprint \"" << input
+ << "\" should be upper case hex digits 0-9 and A-F.";
output.clear();
return output;
}
diff --git a/chromium/components/payments/content/utility/payment_manifest_parser.cc b/chromium/components/payments/content/utility/payment_manifest_parser.cc
index f3a4aaa4e1f..4ef5cf1a6ee 100644
--- a/chromium/components/payments/content/utility/payment_manifest_parser.cc
+++ b/chromium/components/payments/content/utility/payment_manifest_parser.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/json/json_reader.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -19,54 +20,161 @@
#include "url/url_constants.h"
namespace payments {
+namespace {
-// static
-void PaymentManifestParser::Create(
- const service_manager::BindSourceInfo& source_info,
- mojom::PaymentManifestParserRequest request) {
- mojo::MakeStrongBinding(base::MakeUnique<PaymentManifestParser>(),
- std::move(request));
-}
+const size_t kMaximumNumberOfEntries = 100U;
+const char* const kDefaultApplications = "default_applications";
+const char* const kSupportedOrigins = "supported_origins";
-// static
-std::vector<GURL> PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- const std::string& input) {
- std::vector<GURL> output;
- std::unique_ptr<base::Value> value(base::JSONReader::Read(input));
- if (!value)
- return output;
-
- std::unique_ptr<base::DictionaryValue> dict =
- base::DictionaryValue::From(std::move(value));
- if (!dict)
- return output;
+// Parses the "default_applications": ["https://some/url"] from |dict| into
+// |web_app_manifest_urls|. Returns 'false' for invalid data.
+bool ParseDefaultApplications(base::DictionaryValue* dict,
+ std::vector<GURL>* web_app_manifest_urls) {
+ DCHECK(dict);
+ DCHECK(web_app_manifest_urls);
base::ListValue* list = nullptr;
- if (!dict->GetList("default_applications", &list))
- return output;
+ if (!dict->GetList(kDefaultApplications, &list)) {
+ LOG(ERROR) << "\"" << kDefaultApplications << "\" must be a list.";
+ return false;
+ }
size_t apps_number = list->GetSize();
- const size_t kMaximumNumberOfApps = 100U;
- if (apps_number > kMaximumNumberOfApps)
- return output;
+ if (apps_number > kMaximumNumberOfEntries) {
+ LOG(ERROR) << "\"" << kDefaultApplications << "\" must contain at most "
+ << kMaximumNumberOfEntries << " entries.";
+ return false;
+ }
std::string item;
for (size_t i = 0; i < apps_number; ++i) {
if (!list->GetString(i, &item) && item.empty()) {
- output.clear();
- return output;
+ LOG(ERROR) << "Each entry in \"" << kDefaultApplications
+ << "\" must be a string.";
+ web_app_manifest_urls->clear();
+ return false;
}
GURL url(item);
if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme)) {
- output.clear();
- return output;
+ LOG(ERROR) << "\"" << item << "\" entry in \"" << kDefaultApplications
+ << "\" is not a valid URL with HTTPS scheme.";
+ web_app_manifest_urls->clear();
+ return false;
}
- output.push_back(url);
+ web_app_manifest_urls->push_back(url);
}
- return output;
+ return true;
+}
+
+// Parses the "supported_origins": "*" (or ["https://some.origin"]) from |dict|
+// into |supported_origins| and |all_origins_supported|. Returns 'false' for
+// invalid data.
+bool ParseSupportedOrigins(base::DictionaryValue* dict,
+ std::vector<url::Origin>* supported_origins,
+ bool* all_origins_supported) {
+ DCHECK(dict);
+ DCHECK(supported_origins);
+ DCHECK(all_origins_supported);
+
+ *all_origins_supported = false;
+
+ std::string item;
+ if (dict->GetString(kSupportedOrigins, &item)) {
+ if (item != "*") {
+ LOG(ERROR) << "\"" << item << "\" is not a valid value for \""
+ << kSupportedOrigins
+ << "\". Must be either \"*\" or a list of origins.";
+ return false;
+ }
+
+ *all_origins_supported = true;
+ return true;
+ }
+
+ base::ListValue* list = nullptr;
+ if (!dict->GetList(kSupportedOrigins, &list)) {
+ LOG(ERROR) << "\"" << kSupportedOrigins
+ << "\" must be either \"*\" or a list of origins.";
+ return false;
+ }
+
+ size_t supported_origins_number = list->GetSize();
+ if (supported_origins_number > kMaximumNumberOfEntries) {
+ LOG(ERROR) << "\"" << kSupportedOrigins << "\" must contain at most "
+ << kMaximumNumberOfEntries << " entires.";
+ return false;
+ }
+
+ for (size_t i = 0; i < supported_origins_number; ++i) {
+ if (!list->GetString(i, &item) && item.empty()) {
+ LOG(ERROR) << "Each entry in \"" << kSupportedOrigins
+ << "\" must be a string.";
+ supported_origins->clear();
+ return false;
+ }
+
+ GURL url(item);
+ if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme) ||
+ url.path() != "/" || url.has_query() || url.has_ref()) {
+ LOG(ERROR) << "\"" << item << "\" entry in \"" << kSupportedOrigins
+ << "\" is not a valid origin with HTTPS scheme.";
+ supported_origins->clear();
+ return false;
+ }
+
+ supported_origins->push_back(url::Origin(url));
+ }
+
+ return true;
+}
+
+} // namespace
+
+// static
+void PaymentManifestParser::Create(
+ mojom::PaymentManifestParserRequest request) {
+ mojo::MakeStrongBinding(base::MakeUnique<PaymentManifestParser>(),
+ std::move(request));
+}
+
+// static
+void PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
+ const std::string& input,
+ std::vector<GURL>* web_app_manifest_urls,
+ std::vector<url::Origin>* supported_origins,
+ bool* all_origins_supported) {
+ DCHECK(web_app_manifest_urls);
+ DCHECK(supported_origins);
+ DCHECK(all_origins_supported);
+
+ *all_origins_supported = false;
+
+ std::unique_ptr<base::Value> value(base::JSONReader::Read(input));
+ if (!value) {
+ LOG(ERROR) << "Payment method manifest must be in JSON format.";
+ return;
+ }
+
+ std::unique_ptr<base::DictionaryValue> dict =
+ base::DictionaryValue::From(std::move(value));
+ if (!dict) {
+ LOG(ERROR) << "Payment method manifest must be a JSON dictionary.";
+ return;
+ }
+
+ if (dict->HasKey(kDefaultApplications) &&
+ !ParseDefaultApplications(dict.get(), web_app_manifest_urls)) {
+ return;
+ }
+
+ if (dict->HasKey(kSupportedOrigins) &&
+ !ParseSupportedOrigins(dict.get(), supported_origins,
+ all_origins_supported)) {
+ web_app_manifest_urls->clear();
+ }
}
// static
@@ -74,22 +182,29 @@ std::vector<mojom::WebAppManifestSectionPtr>
PaymentManifestParser::ParseWebAppManifestIntoVector(const std::string& input) {
std::vector<mojom::WebAppManifestSectionPtr> output;
std::unique_ptr<base::Value> value(base::JSONReader::Read(input));
- if (!value)
+ if (!value) {
+ LOG(ERROR) << "Web app manifest must be in JSON format.";
return output;
+ }
std::unique_ptr<base::DictionaryValue> dict =
base::DictionaryValue::From(std::move(value));
- if (!dict)
+ if (!dict) {
+ LOG(ERROR) << "Web app manifest must be a JSON dictionary.";
return output;
+ }
base::ListValue* list = nullptr;
- if (!dict->GetList("related_applications", &list))
+ if (!dict->GetList("related_applications", &list)) {
+ LOG(ERROR) << "\"related_applications\" must be a list.";
return output;
+ }
size_t related_applications_size = list->GetSize();
for (size_t i = 0; i < related_applications_size; ++i) {
base::DictionaryValue* related_application = nullptr;
if (!list->GetDictionary(i, &related_application) || !related_application) {
+ LOG(ERROR) << "\"related_applications\" must be a list of dictionaries.";
output.clear();
return output;
}
@@ -100,8 +215,10 @@ PaymentManifestParser::ParseWebAppManifestIntoVector(const std::string& input) {
continue;
}
- const size_t kMaximumNumberOfRelatedApplications = 100U;
- if (output.size() >= kMaximumNumberOfRelatedApplications) {
+ if (output.size() >= kMaximumNumberOfEntries) {
+ LOG(ERROR) << "\"related_applications\" must contain at most "
+ << kMaximumNumberOfEntries
+ << " entries with \"platform\": \"play\".";
output.clear();
return output;
}
@@ -112,7 +229,10 @@ PaymentManifestParser::ParseWebAppManifestIntoVector(const std::string& input) {
if (!related_application->HasKey(kId) ||
!related_application->HasKey(kMinVersion) ||
!related_application->HasKey(kFingerprints)) {
- output.clear();
+ LOG(ERROR) << "Each \"platform\": \"play\" entry in "
+ "\"related_applications\" must contain \""
+ << kId << "\", \"" << kMinVersion << "\", and \""
+ << kFingerprints << "\".";
return output;
}
@@ -122,6 +242,7 @@ PaymentManifestParser::ParseWebAppManifestIntoVector(const std::string& input) {
if (!related_application->GetString(kId, &section->id) ||
section->id.empty() || !base::IsStringASCII(section->id)) {
+ LOG(ERROR) << "\"" << kId << "\" must be a non-empty ASCII string.";
output.clear();
return output;
}
@@ -130,15 +251,19 @@ PaymentManifestParser::ParseWebAppManifestIntoVector(const std::string& input) {
if (!related_application->GetString(kMinVersion, &min_version) ||
min_version.empty() || !base::IsStringASCII(min_version) ||
!base::StringToInt64(min_version, &section->min_version)) {
+ LOG(ERROR) << "\"" << kMinVersion
+ << "\" must be a string convertible into a number.";
output.clear();
return output;
}
- const size_t kMaximumNumberOfFingerprints = 100U;
base::ListValue* fingerprints_list = nullptr;
if (!related_application->GetList(kFingerprints, &fingerprints_list) ||
fingerprints_list->empty() ||
- fingerprints_list->GetSize() > kMaximumNumberOfFingerprints) {
+ fingerprints_list->GetSize() > kMaximumNumberOfEntries) {
+ LOG(ERROR) << "\"" << kFingerprints
+ << "\" must be a non-empty list of at most "
+ << kMaximumNumberOfEntries << " items.";
output.clear();
return output;
}
@@ -148,12 +273,16 @@ PaymentManifestParser::ParseWebAppManifestIntoVector(const std::string& input) {
base::DictionaryValue* fingerprint_dict = nullptr;
std::string fingerprint_type;
std::string fingerprint_value;
- if (!fingerprints_list->GetDictionary(i, &fingerprint_dict) ||
+ if (!fingerprints_list->GetDictionary(j, &fingerprint_dict) ||
!fingerprint_dict ||
!fingerprint_dict->GetString("type", &fingerprint_type) ||
fingerprint_type != "sha256_cert" ||
!fingerprint_dict->GetString("value", &fingerprint_value) ||
- fingerprint_value.empty()) {
+ fingerprint_value.empty() ||
+ !base::IsStringASCII(fingerprint_value)) {
+ LOG(ERROR) << "Each entry in \"" << kFingerprints
+ << "\" must be a dictionary with \"type\": "
+ "\"sha256_cert\" and a non-empty ASCII string \"value\".";
output.clear();
return output;
}
@@ -181,7 +310,16 @@ PaymentManifestParser::~PaymentManifestParser() {}
void PaymentManifestParser::ParsePaymentMethodManifest(
const std::string& content,
ParsePaymentMethodManifestCallback callback) {
- std::move(callback).Run(ParsePaymentMethodManifestIntoVector(content));
+ std::vector<GURL> web_app_manifest_urls;
+ std::vector<url::Origin> supported_origins;
+ bool all_origins_supported = false;
+
+ ParsePaymentMethodManifestIntoVectors(content, &web_app_manifest_urls,
+ &supported_origins,
+ &all_origins_supported);
+
+ std::move(callback).Run(web_app_manifest_urls, supported_origins,
+ all_origins_supported);
}
void PaymentManifestParser::ParseWebAppManifest(
diff --git a/chromium/components/payments/content/utility/payment_manifest_parser.h b/chromium/components/payments/content/utility/payment_manifest_parser.h
index cabdc15924c..34e0ce707bd 100644
--- a/chromium/components/payments/content/utility/payment_manifest_parser.h
+++ b/chromium/components/payments/content/utility/payment_manifest_parser.h
@@ -11,21 +11,25 @@
#include "base/macros.h"
#include "components/payments/mojom/payment_manifest_parser.mojom.h"
#include "url/gurl.h"
-
-namespace service_manager {
-struct BindSourceInfo;
-}
+#include "url/origin.h"
namespace payments {
// Parser for payment method manifests and web app manifests. Should be used
// only in a sandboxed utility process.
//
-// Example valid payment method manifest structure:
+// Example 1 of valid payment method manifest structure:
+//
+// {
+// "default_applications": ["https://bobpay.com/payment-app.json"],
+// "supported_origins": ["https://alicepay.com"]
+// }
+//
+// Example 2 of valid payment method manifest structure:
//
// {
// "default_applications": ["https://bobpay.com/payment-app.json"],
-// "supported_origins": ["https://alicepay.com"] // Not yet parsed or used.
+// "supported_origins": "*"
// }
//
// Example valid web app manifest structure:
@@ -46,11 +50,13 @@ namespace payments {
// https://docs.google.com/document/d/1izV4uC-tiRJG3JLooqY3YRLU22tYOsLTNq0P_InPJeE
class PaymentManifestParser : public mojom::PaymentManifestParser {
public:
- static void Create(const service_manager::BindSourceInfo& source_info,
- mojom::PaymentManifestParserRequest request);
+ static void Create(mojom::PaymentManifestParserRequest request);
- static std::vector<GURL> ParsePaymentMethodManifestIntoVector(
- const std::string& input);
+ static void ParsePaymentMethodManifestIntoVectors(
+ const std::string& input,
+ std::vector<GURL>* web_app_manifest_urls,
+ std::vector<url::Origin>* supported_origins,
+ bool* all_origins_supported);
// The return value is move-only, so no copying occurs.
static std::vector<mojom::WebAppManifestSectionPtr>
diff --git a/chromium/components/payments/content/utility/payment_manifest_parser_unittest.cc b/chromium/components/payments/content/utility/payment_manifest_parser_unittest.cc
index de804990ad4..d7893d7f820 100644
--- a/chromium/components/payments/content/utility/payment_manifest_parser_unittest.cc
+++ b/chromium/components/payments/content/utility/payment_manifest_parser_unittest.cc
@@ -11,109 +11,230 @@ namespace {
// Payment method manifest parsing:
+void ExpectUnableToParsePaymentMethodManifest(const std::string& input) {
+ std::vector<GURL> actual_web_app_urls;
+ std::vector<url::Origin> actual_supported_origins;
+ bool actual_all_origins_supported = false;
+
+ PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
+ input, &actual_web_app_urls, &actual_supported_origins,
+ &actual_all_origins_supported);
+
+ EXPECT_TRUE(actual_web_app_urls.empty());
+ EXPECT_TRUE(actual_supported_origins.empty());
+ EXPECT_FALSE(actual_all_origins_supported);
+}
+
+void ExpectParsedPaymentMethodManifest(
+ const std::string& input,
+ const std::vector<GURL>& expected_web_app_urls,
+ const std::vector<url::Origin>& expected_supported_origins,
+ bool expected_all_origins_supported) {
+ std::vector<GURL> actual_web_app_urls;
+ std::vector<url::Origin> actual_supported_origins;
+ bool actual_all_origins_supported = false;
+
+ PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
+ input, &actual_web_app_urls, &actual_supported_origins,
+ &actual_all_origins_supported);
+
+ EXPECT_EQ(expected_web_app_urls, actual_web_app_urls);
+ EXPECT_EQ(expected_supported_origins, actual_supported_origins);
+ EXPECT_EQ(expected_all_origins_supported, actual_all_origins_supported);
+}
+
TEST(PaymentManifestParserTest, NullPaymentMethodManifestIsMalformed) {
- EXPECT_TRUE(
- PaymentManifestParser::ParsePaymentMethodManifestIntoVector(std::string())
- .empty());
+ ExpectUnableToParsePaymentMethodManifest(std::string());
}
TEST(PaymentManifestParserTest, NonJsonPaymentMethodManifestIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "this is not json")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest("this is not json");
}
TEST(PaymentManifestParserTest, StringPaymentMethodManifestIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "\"this is a string\"")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest("\"this is a string\"");
}
TEST(PaymentManifestParserTest,
EmptyDictionaryPaymentMethodManifestIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector("{}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest("{}");
}
TEST(PaymentManifestParserTest, NullDefaultApplicationIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": null}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": null}");
}
TEST(PaymentManifestParserTest, NumberDefaultApplicationIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": 0}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": 0}");
}
TEST(PaymentManifestParserTest, ListOfNumbersDefaultApplicationIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": [0]}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": [0]}");
}
TEST(PaymentManifestParserTest, EmptyListOfDefaultApplicationsIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": []}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": []}");
}
TEST(PaymentManifestParserTest, ListOfEmptyDefaultApplicationsIsMalformed) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": [\"\"]}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"default_applications\": [\"\"]}");
}
TEST(PaymentManifestParserTest, DefaultApplicationsShouldNotHaveNulCharacters) {
- EXPECT_TRUE(
- PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": [\"https://bobpay.com/app\0json\"]}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"default_applications\": [\"https://bobpay.com/app\0json\"]}");
}
TEST(PaymentManifestParserTest, DefaultApplicationKeyShouldBeLowercase) {
- EXPECT_TRUE(
- PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"Default_Applications\": [\"https://bobpay.com/app.json\"]}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"Default_Applications\": [\"https://bobpay.com/app.json\"]}");
}
TEST(PaymentManifestParserTest, DefaultApplicationsShouldBeAbsoluteUrls) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": ["
- "\"https://bobpay.com/app.json\","
- "\"app.json\"]}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"default_applications\": ["
+ "\"https://bobpay.com/app.json\","
+ "\"app.json\"]}");
}
TEST(PaymentManifestParserTest, DefaultApplicationsShouldBeHttps) {
- EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": ["
- "\"https://bobpay.com/app.json\","
- "\"http://alicepay.com/app.json\"]}")
- .empty());
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"default_applications\": ["
+ "\"https://bobpay.com/app.json\","
+ "\"http://alicepay.com/app.json\"]}");
+}
+
+TEST(PaymentManifestParserTest, NullSupportedOriginsIsMalformed) {
+ ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": null}");
+}
+
+TEST(PaymentManifestParserTest, NumberSupportedOriginsIsMalformed) {
+ ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": 0}");
+}
+
+TEST(PaymentManifestParserTest, EmptyListSupportedOriginsIsMalformed) {
+ ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": []}");
}
-TEST(PaymentManifestParserTest, WellFormedPaymentMethodManifest) {
- std::vector<GURL> expected = {GURL("https://bobpay.com/app.json"),
- GURL("https://alicepay.com/app.json")};
- EXPECT_EQ(expected,
- PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
- "{\"default_applications\": ["
- "\"https://bobpay.com/app.json\","
- "\"https://alicepay.com/app.json\"]}"));
+TEST(PaymentManifestParserTest, ListOfNumbersSupportedOriginsIsMalformed) {
+ ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": [0]}");
+}
+
+TEST(PaymentManifestParserTest, ListOfEmptySupportedOriginsIsMalformed) {
+ ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": [\"\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHaveNulCharacters) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"supported_origins\": [\"https://bob\0pay.com\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldBeHttps) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"supported_origins\": [\"http://bobpay.com\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHavePath) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"supported_origins\": [\"https://bobpay.com/webpay\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHaveQuery) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"supported_origins\": [\"https://bobpay.com/?action=webpay\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHaveRef) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"supported_origins\": [\"https://bobpay.com/#webpay\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldBeList) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"supported_origins\": \"https://bobpay.com\"}");
+}
+
+TEST(PaymentManifestParserTest, WellFormedPaymentMethodManifestWithApps) {
+ ExpectParsedPaymentMethodManifest(
+ "{\"default_applications\": ["
+ "\"https://bobpay.com/app.json\","
+ "\"https://alicepay.com/app.json\"]}",
+ {GURL("https://bobpay.com/app.json"),
+ GURL("https://alicepay.com/app.json")},
+ std::vector<url::Origin>(), false);
+}
+
+TEST(PaymentManifestParserTest,
+ WellFormedPaymentMethodManifestWithAppsAndAllSupportedOrigins) {
+ ExpectParsedPaymentMethodManifest(
+ "{\"default_applications\": [\"https://bobpay.com/app.json\", "
+ "\"https://alicepay.com/app.json\"], \"supported_origins\": \"*\"}",
+ {GURL("https://bobpay.com/app.json"),
+ GURL("https://alicepay.com/app.json")},
+ std::vector<url::Origin>(), true);
+}
+
+TEST(PaymentManifestParserTest, AllOriginsSupported) {
+ ExpectParsedPaymentMethodManifest("{\"supported_origins\": \"*\"}",
+ std::vector<GURL>(),
+ std::vector<url::Origin>(), true);
+}
+
+TEST(PaymentManifestParserTest,
+ InvalidDefaultAppsWillPreventParsingSupportedOrigins) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"default_applications\": [\"http://bobpay.com/app.json\"], "
+ "\"supported_origins\": \"*\"}");
+}
+
+TEST(PaymentManifestParserTest,
+ InvalidSupportedOriginsWillPreventParsingDefaultApps) {
+ ExpectUnableToParsePaymentMethodManifest(
+ "{\"default_applications\": [\"https://bobpay.com/app.json\"], "
+ "\"supported_origins\": \"+\"}");
+}
+
+TEST(PaymentManifestParserTest,
+ WellFormedPaymentMethodManifestWithAppsAndSomeSupportedOrigins) {
+ ExpectParsedPaymentMethodManifest(
+ "{\"default_applications\": [\"https://bobpay.com/app.json\", "
+ "\"https://alicepay.com/app.json\"], \"supported_origins\": "
+ "[\"https://charliepay.com\", \"https://evepay.com\"]}",
+ {GURL("https://bobpay.com/app.json"),
+ GURL("https://alicepay.com/app.json")},
+ {url::Origin(GURL("https://charliepay.com")),
+ url::Origin(GURL("https://evepay.com"))},
+ false);
+}
+
+TEST(PaymentManifestParserTest,
+ WellFormedPaymentMethodManifestWithSomeSupportedOrigins) {
+ ExpectParsedPaymentMethodManifest(
+ "{\"supported_origins\": [\"https://charliepay.com\", "
+ "\"https://evepay.com\"]}",
+ std::vector<GURL>(),
+ {url::Origin(GURL("https://charliepay.com")),
+ url::Origin(GURL("https://evepay.com"))},
+ false);
+}
+
+TEST(PaymentManifestParserTest,
+ WellFormedPaymentMethodManifestWithAllSupportedOrigins) {
+ ExpectParsedPaymentMethodManifest("{\"supported_origins\": \"*\"}",
+ std::vector<GURL>(),
+ std::vector<url::Origin>(), true);
}
// Web app manifest parsing:
-void ExpectUnableToParse(const std::string& input) {
+void ExpectUnableToParseWebAppManifest(const std::string& input) {
std::vector<mojom::WebAppManifestSectionPtr> actual_output =
PaymentManifestParser::ParseWebAppManifestIntoVector(input);
EXPECT_TRUE(actual_output.empty());
}
-void ExpectParsed(
+void ExpectParsedWebAppManifest(
const std::string& input,
const std::string& expected_id,
int64_t expected_min_version,
@@ -127,41 +248,41 @@ void ExpectParsed(
}
TEST(PaymentManifestParserTest, NullContentIsMalformed) {
- ExpectUnableToParse(std::string());
+ ExpectUnableToParseWebAppManifest(std::string());
}
TEST(PaymentManifestParserTest, NonJsonContentIsMalformed) {
- ExpectUnableToParse("this is not json");
+ ExpectUnableToParseWebAppManifest("this is not json");
}
TEST(PaymentManifestParserTest, StringContentIsMalformed) {
- ExpectUnableToParse("\"this is a string\"");
+ ExpectUnableToParseWebAppManifest("\"this is a string\"");
}
TEST(PaymentManifestParserTest, EmptyDictionaryIsMalformed) {
- ExpectUnableToParse("{}");
+ ExpectUnableToParseWebAppManifest("{}");
}
TEST(PaymentManifestParserTest, NullRelatedApplicationsSectionIsMalformed) {
- ExpectUnableToParse("{\"related_applications\": null}");
+ ExpectUnableToParseWebAppManifest("{\"related_applications\": null}");
}
TEST(PaymentManifestParserTest, NumberRelatedApplicationsectionIsMalformed) {
- ExpectUnableToParse("{\"related_applications\": 0}");
+ ExpectUnableToParseWebAppManifest("{\"related_applications\": 0}");
}
TEST(PaymentManifestParserTest,
ListOfNumbersRelatedApplicationsSectionIsMalformed) {
- ExpectUnableToParse("{\"related_applications\": [0]}");
+ ExpectUnableToParseWebAppManifest("{\"related_applications\": [0]}");
}
TEST(PaymentManifestParserTest,
ListOfEmptyDictionariesRelatedApplicationsSectionIsMalformed) {
- ExpectUnableToParse("{\"related_applications\": [{}]}");
+ ExpectUnableToParseWebAppManifest("{\"related_applications\": [{}]}");
}
TEST(PaymentManifestParserTest, NoPlayPlatformIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"id\": \"com.bobpay.app\", "
@@ -176,7 +297,7 @@ TEST(PaymentManifestParserTest, NoPlayPlatformIsMalformed) {
}
TEST(PaymentManifestParserTest, NoPackageNameIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -191,7 +312,7 @@ TEST(PaymentManifestParserTest, NoPackageNameIsMalformed) {
}
TEST(PaymentManifestParserTest, NoVersionIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -206,7 +327,7 @@ TEST(PaymentManifestParserTest, NoVersionIsMalformed) {
}
TEST(PaymentManifestParserTest, NoFingerprintIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -217,7 +338,7 @@ TEST(PaymentManifestParserTest, NoFingerprintIsMalformed) {
}
TEST(PaymentManifestParserTest, EmptyFingerprintsIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -229,7 +350,7 @@ TEST(PaymentManifestParserTest, EmptyFingerprintsIsMalformed) {
}
TEST(PaymentManifestParserTest, EmptyFingerprintsDictionaryIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -241,7 +362,7 @@ TEST(PaymentManifestParserTest, EmptyFingerprintsDictionaryIsMalformed) {
}
TEST(PaymentManifestParserTest, NoFingerprintTypeIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -256,7 +377,7 @@ TEST(PaymentManifestParserTest, NoFingerprintTypeIsMalformed) {
}
TEST(PaymentManifestParserTest, NoFingerprintValueIsMalformed) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -270,7 +391,7 @@ TEST(PaymentManifestParserTest, NoFingerprintValueIsMalformed) {
}
TEST(PaymentManifestParserTest, PlatformShouldNotHaveNullCharacters) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"pl\0ay\", "
@@ -286,7 +407,7 @@ TEST(PaymentManifestParserTest, PlatformShouldNotHaveNullCharacters) {
}
TEST(PaymentManifestParserTest, PackageNameShouldNotHaveNullCharacters) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -302,7 +423,7 @@ TEST(PaymentManifestParserTest, PackageNameShouldNotHaveNullCharacters) {
}
TEST(PaymentManifestParserTest, VersionShouldNotHaveNullCharacters) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -318,7 +439,7 @@ TEST(PaymentManifestParserTest, VersionShouldNotHaveNullCharacters) {
}
TEST(PaymentManifestParserTest, FingerprintTypeShouldNotHaveNullCharacters) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -334,7 +455,7 @@ TEST(PaymentManifestParserTest, FingerprintTypeShouldNotHaveNullCharacters) {
}
TEST(PaymentManifestParserTest, FingerprintValueShouldNotHaveNullCharacters) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -351,7 +472,7 @@ TEST(PaymentManifestParserTest, FingerprintValueShouldNotHaveNullCharacters) {
}
TEST(PaymentManifestParserTest, KeysShouldBeLowerCase) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"Related_applications\": [{"
" \"Platform\": \"play\", "
@@ -367,7 +488,7 @@ TEST(PaymentManifestParserTest, KeysShouldBeLowerCase) {
}
TEST(PaymentManifestParserTest, FingerprintsShouldBeSha256) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -383,7 +504,7 @@ TEST(PaymentManifestParserTest, FingerprintsShouldBeSha256) {
}
TEST(PaymentManifestParserTest, FingerprintBytesShouldBeColonSeparated) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -391,15 +512,15 @@ TEST(PaymentManifestParserTest, FingerprintBytesShouldBeColonSeparated) {
" \"min_version\": \"1\", "
" \"fingerprints\": [{"
" \"type\": \"sha256_cert\", "
- " \"value\" \"00010203040506070809A0A1A2A3A4A5A6A7A8A9B0B1B2B3B4B5B6"
- "B7B8B9C0C1\""
+ " \"value\": \"000010020030040050060070080090A00A10A20A30A40A50A60A7"
+ "0A80A90B00B10B20B30B40B50B60B70B80B90C00C1\""
" }]"
" }]"
"}");
}
TEST(PaymentManifestParserTest, FingerprintBytesShouldBeUpperCase) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -415,7 +536,7 @@ TEST(PaymentManifestParserTest, FingerprintBytesShouldBeUpperCase) {
}
TEST(PaymentManifestParserTest, FingerprintsShouldContainsThirtyTwoBytes) {
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -428,7 +549,7 @@ TEST(PaymentManifestParserTest, FingerprintsShouldContainsThirtyTwoBytes) {
" }]"
" }]"
"}");
- ExpectUnableToParse(
+ ExpectUnableToParseWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -444,7 +565,7 @@ TEST(PaymentManifestParserTest, FingerprintsShouldContainsThirtyTwoBytes) {
}
TEST(PaymentManifestParserTest, WellFormed) {
- ExpectParsed(
+ ExpectParsedWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -464,7 +585,7 @@ TEST(PaymentManifestParserTest, WellFormed) {
}
TEST(PaymentManifestParserTest, DuplicateSignaturesWellFormed) {
- ExpectParsed(
+ ExpectParsedWebAppManifest(
"{"
" \"related_applications\": [{"
" \"platform\": \"play\", "
@@ -490,5 +611,80 @@ TEST(PaymentManifestParserTest, DuplicateSignaturesWellFormed) {
0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC0, 0xC1}});
}
+TEST(PaymentManifestParserTest, TwoDifferentSignaturesWellFormed) {
+ ExpectParsedWebAppManifest(
+ "{"
+ " \"related_applications\": [{"
+ " \"platform\": \"play\", "
+ " \"id\": \"com.bobpay.app\", "
+ " \"min_version\": \"1\", "
+ " \"fingerprints\": [{"
+ " \"type\": \"sha256_cert\", "
+ " \"value\": \"00:01:02:03:04:05:06:07:08:09:A0:A1:A2:A3:A4:A5:A6:A7"
+ ":A8:A9:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+ " }, {"
+ " \"type\": \"sha256_cert\", "
+ " \"value\": \"AA:01:02:03:04:05:06:07:08:09:A0:A1:A2:A3:A4:A5:A6:A7"
+ ":A8:A9:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+ " }]"
+ " }]"
+ "}",
+ "com.bobpay.app", 1,
+ {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xA0,
+ 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xB0, 0xB1,
+ 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC0, 0xC1},
+ {0xAA, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xA0,
+ 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xB0, 0xB1,
+ 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC0, 0xC1}});
+}
+
+TEST(PaymentManifestParserTest, TwoRelatedApplicationsWellFormed) {
+ std::vector<mojom::WebAppManifestSectionPtr> actual_output =
+ PaymentManifestParser::ParseWebAppManifestIntoVector(
+ "{"
+ " \"related_applications\": [{"
+ " \"platform\": \"play\", "
+ " \"id\": \"com.bobpay.app.dev\", "
+ " \"min_version\": \"2\", "
+ " \"fingerprints\": [{"
+ " \"type\": \"sha256_cert\", "
+ " \"value\": "
+ "\"00:01:02:03:04:05:06:07:08:09:A0:A1:A2:A3:A4:A5:A6:A7"
+ ":A8:A9:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+ " }]"
+ " }, {"
+ " \"platform\": \"play\", "
+ " \"id\": \"com.bobpay.app.prod\", "
+ " \"min_version\": \"1\", "
+ " \"fingerprints\": [{"
+ " \"type\": \"sha256_cert\", "
+ " \"value\": "
+ "\"AA:01:02:03:04:05:06:07:08:09:A0:A1:A2:A3:A4:A5:A6:A7"
+ ":A8:A9:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+ " }]"
+ " }]"
+ "}");
+
+ ASSERT_EQ(2U, actual_output.size());
+
+ EXPECT_EQ("com.bobpay.app.dev", actual_output.front()->id);
+ EXPECT_EQ(2, actual_output.front()->min_version);
+ EXPECT_EQ(
+ std::vector<std::vector<uint8_t>>(
+ {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xA0,
+ 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xB0, 0xB1,
+ 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC0, 0xC1}}),
+ actual_output.front()->fingerprints);
+
+ EXPECT_EQ("com.bobpay.app.prod", actual_output.back()->id);
+ EXPECT_EQ(1, actual_output.back()->min_version);
+ EXPECT_EQ(
+ std::vector<std::vector<uint8_t>>(
+ {{0xAA, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xA0,
+ 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xB0, 0xB1,
+ 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC0, 0xC1}}),
+ actual_output.back()->fingerprints);
+}
+
} // namespace
} // namespace payments
diff --git a/chromium/components/payments/core/BUILD.gn b/chromium/components/payments/core/BUILD.gn
index eaa3d417ba7..ed5343e61d5 100644
--- a/chromium/components/payments/core/BUILD.gn
+++ b/chromium/components/payments/core/BUILD.gn
@@ -17,6 +17,8 @@ static_library("core") {
"can_make_payment_query.h",
"currency_formatter.cc",
"currency_formatter.h",
+ "features.cc",
+ "features.h",
"journey_logger.cc",
"journey_logger.h",
"payment_address.cc",
@@ -28,6 +30,7 @@ static_library("core") {
"payment_options_provider.h",
"payment_prefs.cc",
"payment_prefs.h",
+ "payment_request_base_delegate.h",
"payment_request_data_util.cc",
"payment_request_data_util.h",
"payment_request_delegate.h",
@@ -60,6 +63,8 @@ static_library("core") {
static_library("test_support") {
testonly = true
sources = [
+ "payments_test_util.cc",
+ "payments_test_util.h",
"test_address_normalizer.cc",
"test_address_normalizer.h",
"test_payment_request_delegate.cc",
@@ -70,6 +75,9 @@ static_library("test_support") {
":core",
"//base",
"//components/autofill/core/browser",
+ "//components/autofill/core/browser:test_support",
+ "//components/pref_registry",
+ "//components/prefs",
]
}
@@ -86,6 +94,7 @@ source_set("unit_tests") {
"payment_method_data_unittest.cc",
"payment_request_data_util_unittest.cc",
"payments_profile_comparator_unittest.cc",
+ "strings_util_unittest.cc",
"subkey_requester_unittest.cc",
]
@@ -106,3 +115,99 @@ source_set("unit_tests") {
"//ui/base",
]
}
+
+bundle_data("payments_test_bundle_data") {
+ testonly = true
+
+ sources = [
+ "//components/test/data/payments/abort.js",
+ "//components/test/data/payments/alicepay_bobpay_charliepay_and_cards.js",
+ "//components/test/data/payments/app.json",
+ "//components/test/data/payments/blob_url.js",
+ "//components/test/data/payments/bobpay.js",
+ "//components/test/data/payments/bobpay_and_basic_card_with_basic_card_modifiers.js",
+ "//components/test/data/payments/bobpay_and_basic_card_with_modifiers.js",
+ "//components/test/data/payments/bobpay_and_cards.js",
+ "//components/test/data/payments/bobpay_ui_skip.js",
+ "//components/test/data/payments/bobpay_ui_skip_preload.js",
+ "//components/test/data/payments/can_make_payment_metrics.js",
+ "//components/test/data/payments/can_make_payment_query.js",
+ "//components/test/data/payments/can_make_payment_query_bobpay.js",
+ "//components/test/data/payments/can_make_payment_query_cc.js",
+ "//components/test/data/payments/contact_details.js",
+ "//components/test/data/payments/contact_details_and_free_shipping.js",
+ "//components/test/data/payments/debit.js",
+ "//components/test/data/payments/dynamic_shipping.js",
+ "//components/test/data/payments/email.js",
+ "//components/test/data/payments/email_and_free_shipping.js",
+ "//components/test/data/payments/email_and_phone.js",
+ "//components/test/data/payments/extra_shipping_options.js",
+ "//components/test/data/payments/fail_complete.js",
+ "//components/test/data/payments/free_shipping.js",
+ "//components/test/data/payments/initiated.js",
+ "//components/test/data/payments/initiated_test.html",
+ "//components/test/data/payments/long_id.js",
+ "//components/test/data/payments/metrics.js",
+ "//components/test/data/payments/modifier.js",
+ "//components/test/data/payments/multiple_show.js",
+ "//components/test/data/payments/name.js",
+ "//components/test/data/payments/name_and_free_shipping.js",
+ "//components/test/data/payments/no_shipping.js",
+ "//components/test/data/payments/payment-manifest.json",
+ "//components/test/data/payments/payment_method_identifier.js",
+ "//components/test/data/payments/payment_request.html",
+ "//components/test/data/payments/payment_request.js",
+ "//components/test/data/payments/payment_request_abort_test.html",
+ "//components/test/data/payments/payment_request_alicepay_bobpay_charliepay_and_cards_test.html",
+ "//components/test/data/payments/payment_request_blob_url_test.html",
+ "//components/test/data/payments/payment_request_bobpay_and_basic_card_with_basic_card_modifiers_test.html",
+ "//components/test/data/payments/payment_request_bobpay_and_basic_card_with_modifiers_test.html",
+ "//components/test/data/payments/payment_request_bobpay_and_cards_test.html",
+ "//components/test/data/payments/payment_request_bobpay_test.html",
+ "//components/test/data/payments/payment_request_bobpay_ui_skip_preload_test.html",
+ "//components/test/data/payments/payment_request_bobpay_ui_skip_test.html",
+ "//components/test/data/payments/payment_request_can_make_payment_metrics_test.html",
+ "//components/test/data/payments/payment_request_can_make_payment_query_bobpay_test.html",
+ "//components/test/data/payments/payment_request_can_make_payment_query_cc_test.html",
+ "//components/test/data/payments/payment_request_can_make_payment_query_test.html",
+ "//components/test/data/payments/payment_request_contact_details_and_free_shipping_test.html",
+ "//components/test/data/payments/payment_request_contact_details_test.html",
+ "//components/test/data/payments/payment_request_debit_test.html",
+ "//components/test/data/payments/payment_request_dynamic_shipping_test.html",
+ "//components/test/data/payments/payment_request_email_and_free_shipping_test.html",
+ "//components/test/data/payments/payment_request_email_and_phone_test.html",
+ "//components/test/data/payments/payment_request_email_test.html",
+ "//components/test/data/payments/payment_request_extra_shipping_options_test.html",
+ "//components/test/data/payments/payment_request_fail_complete_test.html",
+ "//components/test/data/payments/payment_request_free_shipping_test.html",
+ "//components/test/data/payments/payment_request_id.js",
+ "//components/test/data/payments/payment_request_id_test.html",
+ "//components/test/data/payments/payment_request_iframe.html",
+ "//components/test/data/payments/payment_request_long_id_test.html",
+ "//components/test/data/payments/payment_request_main.html",
+ "//components/test/data/payments/payment_request_metrics_test.html",
+ "//components/test/data/payments/payment_request_modifier_test.html",
+ "//components/test/data/payments/payment_request_multiple_requests.html",
+ "//components/test/data/payments/payment_request_multiple_show_test.html",
+ "//components/test/data/payments/payment_request_name_and_free_shipping_test.html",
+ "//components/test/data/payments/payment_request_name_test.html",
+ "//components/test/data/payments/payment_request_no_shipping_test.html",
+ "//components/test/data/payments/payment_request_payment_method_identifier_test.html",
+ "//components/test/data/payments/payment_request_phone_and_free_shipping_test.html",
+ "//components/test/data/payments/payment_request_phone_test.html",
+ "//components/test/data/payments/payment_request_shipping_address_change_test.html",
+ "//components/test/data/payments/payment_request_show_twice_test.html",
+ "//components/test/data/payments/phone.js",
+ "//components/test/data/payments/phone_and_free_shipping.js",
+ "//components/test/data/payments/shipping_address_change.js",
+ "//components/test/data/payments/show_twice.js",
+ "//components/test/data/payments/style.css",
+ "//components/test/data/payments/util.js",
+ "//components/test/data/payments/webpay",
+ "//components/test/data/payments/webpay.mock-http-headers",
+ ]
+ outputs = [
+ "{{bundle_resources_dir}}/" +
+ "{{source_root_relative_dir}}/{{source_file_part}}",
+ ]
+}
diff --git a/chromium/components/payments/core/DEPS b/chromium/components/payments/core/DEPS
index 20bc8d9c09b..8325aae1337 100644
--- a/chromium/components/payments/core/DEPS
+++ b/chromium/components/payments/core/DEPS
@@ -4,10 +4,17 @@ include_rules = [
"+components/autofill/core",
"+components/keyed_service/core",
"+components/metrics",
+ "+components/prefs",
"+components/pref_registry",
"+components/strings",
- "+components/ukm",
+ "+services/metrics/public",
"+third_party/libaddressinput",
"+third_party/libphonenumber",
"+ui/base",
]
+
+specific_include_rules = {
+ "journey_logger_unittest\.cc": [
+ "+components/ukm",
+ ],
+}
diff --git a/chromium/components/payments/core/OWNERS b/chromium/components/payments/core/OWNERS
index 396d240ce37..a54ecb4e8c0 100644
--- a/chromium/components/payments/core/OWNERS
+++ b/chromium/components/payments/core/OWNERS
@@ -1,4 +1,4 @@
per-file journey_logger*=sebsg@chromium.org
per-file address_normalizer*=sebsg@chromium.org
-# COMPONENT: UI>Browser>Autofill>Payments
+# COMPONENT: UI>Browser>Payments
diff --git a/chromium/components/payments/core/address_normalization_manager.cc b/chromium/components/payments/core/address_normalization_manager.cc
index 21475900670..240846b27b4 100644
--- a/chromium/components/payments/core/address_normalization_manager.cc
+++ b/chromium/components/payments/core/address_normalization_manager.cc
@@ -16,10 +16,10 @@ constexpr int kAddressNormalizationTimeoutSeconds = 5;
} // namespace
AddressNormalizationManager::AddressNormalizationManager(
- std::unique_ptr<AddressNormalizer> address_normalizer,
+ AddressNormalizer* address_normalizer,
const std::string& default_country_code)
: default_country_code_(default_country_code),
- address_normalizer_(std::move(address_normalizer)) {
+ address_normalizer_(address_normalizer) {
DCHECK(autofill::data_util::IsValidCountryCode(default_country_code));
DCHECK(address_normalizer_);
@@ -31,9 +31,10 @@ AddressNormalizationManager::AddressNormalizationManager(
AddressNormalizationManager::~AddressNormalizationManager() {}
-void AddressNormalizationManager::FinalizeWithCompletionCallback(
+void AddressNormalizationManager::FinalizePendingRequestsWithCompletionCallback(
base::OnceClosure completion_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!completion_callback_);
completion_callback_ = std::move(completion_callback);
accepting_requests_ = false;
MaybeRunCompletionCallback();
@@ -45,8 +46,8 @@ void AddressNormalizationManager::StartNormalizingAddress(
DCHECK(accepting_requests_) << "FinalizeWithCompletionCallback has been "
"called, cannot normalize more addresses";
- delegates_.push_back(base::MakeUnique<NormalizerDelegate>(
- this, address_normalizer_.get(), profile));
+ delegates_.push_back(
+ base::MakeUnique<NormalizerDelegate>(this, address_normalizer_, profile));
}
void AddressNormalizationManager::MaybeRunCompletionCallback() {
@@ -61,6 +62,10 @@ void AddressNormalizationManager::MaybeRunCompletionCallback() {
// We're no longer accepting requests, and all the delegates have completed.
// Now's the time to run the completion callback.
std::move(completion_callback_).Run();
+
+ // Start accepting requests after the completion callback has run.
+ delegates_.clear();
+ accepting_requests_ = true;
}
AddressNormalizationManager::NormalizerDelegate::NormalizerDelegate(
diff --git a/chromium/components/payments/core/address_normalization_manager.h b/chromium/components/payments/core/address_normalization_manager.h
index a838dbce183..63ff0f1eb79 100644
--- a/chromium/components/payments/core/address_normalization_manager.h
+++ b/chromium/components/payments/core/address_normalization_manager.h
@@ -27,18 +27,18 @@ class AddressNormalizationManager {
public:
// Initializes an AddressNormalizationManager. |default_country_code| will be
// used if the country code in an AutofillProfile to normalize is not valid.
- // The AddressNormalizationManager takes ownership of |address_normalizer|.
- AddressNormalizationManager(
- std::unique_ptr<AddressNormalizer> address_normalizer,
- const std::string& default_country_code);
+ // The AddressNormalizationManager does not own |address_normalizer|.
+ AddressNormalizationManager(AddressNormalizer* address_normalizer,
+ const std::string& default_country_code);
~AddressNormalizationManager();
- // Stops accepting normalization requests. If all the address normalization
- // requests have already completed, |completion_callback| will be called
- // before this method returns. Otherwise, it will be called as soon as the
- // last pending request completes.
- void FinalizeWithCompletionCallback(base::OnceClosure completion_callback);
+ // Stops accepting normalization requests until all pending requests complete.
+ // If all the address normalization requests have already completed,
+ // |completion_callback| will be called before this method returns. Otherwise,
+ // it will be called as soon as the last pending request completes.
+ void FinalizePendingRequestsWithCompletionCallback(
+ base::OnceClosure completion_callback);
// Normalizes the address in |profile|. This may or may not happen
// asynchronously. On completion, the address in |profile| will be updated
@@ -95,8 +95,8 @@ class AddressNormalizationManager {
// Storage for all the delegates that handle the normalization requests.
std::vector<std::unique_ptr<NormalizerDelegate>> delegates_;
- // The AddressNormalizer to use. Owned by this class.
- std::unique_ptr<AddressNormalizer> address_normalizer_;
+ // An unowned raw pointer to the AddressNormalizer to use.
+ AddressNormalizer* address_normalizer_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(AddressNormalizationManager);
diff --git a/chromium/components/payments/core/address_normalization_manager_unittest.cc b/chromium/components/payments/core/address_normalization_manager_unittest.cc
index c0a60fa2c80..4e58c247be2 100644
--- a/chromium/components/payments/core/address_normalization_manager_unittest.cc
+++ b/chromium/components/payments/core/address_normalization_manager_unittest.cc
@@ -17,23 +17,21 @@ class AddressNormalizationManagerTest : public testing::Test {
AddressNormalizationManagerTest() {}
void Initialize(const std::string& country_code) {
- std::unique_ptr<TestAddressNormalizer> address_normalizer =
- base::MakeUnique<TestAddressNormalizer>();
- address_normalizer_ = address_normalizer.get();
+ address_normalizer_ = base::MakeUnique<TestAddressNormalizer>();
manager_ = base::MakeUnique<AddressNormalizationManager>(
- std::move(address_normalizer), country_code);
+ address_normalizer_.get(), country_code);
}
void Finalize() {
- manager_->FinalizeWithCompletionCallback(
+ manager_->FinalizePendingRequestsWithCompletionCallback(
base::BindOnce(&AddressNormalizationManagerTest::CompletionCallback,
base::Unretained(this)));
}
void CompletionCallback() { completion_callback_called_ = true; }
+ std::unique_ptr<TestAddressNormalizer> address_normalizer_;
std::unique_ptr<AddressNormalizationManager> manager_;
- TestAddressNormalizer* address_normalizer_ = nullptr; // Weak.
bool completion_callback_called_ = false;
};
diff --git a/chromium/components/payments/core/autofill_payment_instrument.cc b/chromium/components/payments/core/autofill_payment_instrument.cc
index 82ff24d2c62..86d98f02771 100644
--- a/chromium/components/payments/core/autofill_payment_instrument.cc
+++ b/chromium/components/payments/core/autofill_payment_instrument.cc
@@ -4,6 +4,7 @@
#include "components/payments/core/autofill_payment_instrument.h"
+#include <algorithm>
#include <memory>
#include "base/json/json_writer.h"
@@ -17,23 +18,25 @@
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/payments/core/basic_card_response.h"
+#include "components/payments/core/payment_request_base_delegate.h"
#include "components/payments/core/payment_request_data_util.h"
-#include "components/payments/core/payment_request_delegate.h"
namespace payments {
AutofillPaymentInstrument::AutofillPaymentInstrument(
const std::string& method_name,
const autofill::CreditCard& card,
+ bool matches_merchant_card_type_exactly,
const std::vector<autofill::AutofillProfile*>& billing_profiles,
const std::string& app_locale,
- PaymentRequestDelegate* payment_request_delegate)
+ PaymentRequestBaseDelegate* payment_request_delegate)
: PaymentInstrument(
method_name,
autofill::data_util::GetPaymentRequestData(card.network())
.icon_resource_id,
PaymentInstrument::Type::AUTOFILL),
credit_card_(card),
+ matches_merchant_card_type_exactly_(matches_merchant_card_type_exactly),
billing_profiles_(billing_profiles),
app_locale_(app_locale),
delegate_(nullptr),
@@ -77,7 +80,7 @@ void AutofillPaymentInstrument::InvokePaymentApp(
weak_ptr_factory_.GetWeakPtr());
}
-bool AutofillPaymentInstrument::IsCompleteForPayment() {
+bool AutofillPaymentInstrument::IsCompleteForPayment() const {
// COMPLETE or EXPIRED cards are considered valid for payment. The user will
// be prompted to enter the new expiration at the CVC step.
return autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
@@ -85,13 +88,17 @@ bool AutofillPaymentInstrument::IsCompleteForPayment() {
autofill::CREDIT_CARD_EXPIRED;
}
-base::string16 AutofillPaymentInstrument::GetMissingInfoLabel() {
+bool AutofillPaymentInstrument::IsExactlyMatchingMerchantRequest() const {
+ return matches_merchant_card_type_exactly_;
+}
+
+base::string16 AutofillPaymentInstrument::GetMissingInfoLabel() const {
return autofill::GetCompletionMessageForCard(
autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
billing_profiles_));
}
-bool AutofillPaymentInstrument::IsValidForCanMakePayment() {
+bool AutofillPaymentInstrument::IsValidForCanMakePayment() const {
autofill::CreditCardCompletionStatus status =
autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
billing_profiles_);
@@ -116,6 +123,44 @@ base::string16 AutofillPaymentInstrument::GetSublabel() const {
autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL), app_locale_);
}
+bool AutofillPaymentInstrument::IsValidForModifier(
+ const std::vector<std::string>& method,
+ const std::set<autofill::CreditCard::CardType>& supported_types,
+ const std::vector<std::string>& supported_networks) const {
+ // This instrument only matches basic-card.
+ if (std::find(method.begin(), method.end(), "basic-card") == method.end())
+ return false;
+
+ // If supported_types is not specified and this instrument matches the method,
+ // the modifier is applicable. If supported_types is populated, it must
+ // contain this card's type to be applicable. The same is true for
+ // supported_networks.
+ bool is_supported_type =
+ supported_types.empty() ||
+ std::find(supported_types.begin(), supported_types.end(),
+ credit_card_.card_type()) != supported_types.end();
+
+ // supported_types may contain CARD_TYPE_UNKNOWN because of the parsing
+ // function but the modifiers shouldn't be applied since the website can't be
+ // sure that the instrument is an applicable card.
+ if (is_supported_type &&
+ credit_card_.card_type() ==
+ autofill::CreditCard::CardType::CARD_TYPE_UNKNOWN)
+ return false;
+
+ bool is_supported_network = supported_networks.empty();
+ if (!is_supported_network) {
+ std::string basic_card_network =
+ autofill::data_util::GetPaymentRequestData(credit_card_.network())
+ .basic_card_issuer_network;
+ is_supported_network =
+ std::find(supported_networks.begin(), supported_networks.end(),
+ basic_card_network) != supported_networks.end();
+ }
+
+ return is_supported_type && is_supported_network;
+}
+
void AutofillPaymentInstrument::OnFullCardRequestSucceeded(
const autofill::CreditCard& card,
const base::string16& cvc) {
diff --git a/chromium/components/payments/core/autofill_payment_instrument.h b/chromium/components/payments/core/autofill_payment_instrument.h
index 6df075f621a..3600918a79f 100644
--- a/chromium/components/payments/core/autofill_payment_instrument.h
+++ b/chromium/components/payments/core/autofill_payment_instrument.h
@@ -19,7 +19,7 @@
namespace payments {
-class PaymentRequestDelegate;
+class PaymentRequestBaseDelegate;
// Represents an Autofill/Payments credit card form of payment in Payment
// Request.
@@ -33,19 +33,25 @@ class AutofillPaymentInstrument
AutofillPaymentInstrument(
const std::string& method_name,
const autofill::CreditCard& card,
+ bool matches_merchant_card_type_exactly,
const std::vector<autofill::AutofillProfile*>& billing_profiles,
const std::string& app_locale,
- PaymentRequestDelegate* payment_request_delegate);
+ PaymentRequestBaseDelegate* payment_request_delegate);
~AutofillPaymentInstrument() override;
// PaymentInstrument:
void InvokePaymentApp(PaymentInstrument::Delegate* delegate) override;
- bool IsCompleteForPayment() override;
- base::string16 GetMissingInfoLabel() override;
- bool IsValidForCanMakePayment() override;
+ bool IsCompleteForPayment() const override;
+ bool IsExactlyMatchingMerchantRequest() const override;
+ base::string16 GetMissingInfoLabel() const override;
+ bool IsValidForCanMakePayment() const override;
void RecordUse() override;
base::string16 GetLabel() const override;
base::string16 GetSublabel() const override;
+ bool IsValidForModifier(
+ const std::vector<std::string>& method,
+ const std::set<autofill::CreditCard::CardType>& supported_types,
+ const std::vector<std::string>& supported_networks) const override;
// autofill::payments::FullCardRequest::ResultDelegate:
void OnFullCardRequestSucceeded(const autofill::CreditCard& card,
@@ -65,13 +71,20 @@ class AutofillPaymentInstrument
// A copy of the card is owned by this object.
autofill::CreditCard credit_card_;
+
+ // Whether the card type (credit, debit, prepaid) matches the merchant's
+ // requested card type exactly. If the merchant accepts all card types, then
+ // this variable is always "true". If the merchant accepts only a subset of
+ // the card types, then this variable is "false" for unknown card types.
+ const bool matches_merchant_card_type_exactly_;
+
// Not owned by this object, should outlive this.
const std::vector<autofill::AutofillProfile*>& billing_profiles_;
const std::string app_locale_;
PaymentInstrument::Delegate* delegate_;
- PaymentRequestDelegate* payment_request_delegate_;
+ PaymentRequestBaseDelegate* payment_request_delegate_;
autofill::AutofillProfile billing_address_;
base::string16 cvc_;
diff --git a/chromium/components/payments/core/autofill_payment_instrument_unittest.cc b/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
index 7f84726ad79..117d587bf30 100644
--- a/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
+++ b/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
@@ -164,8 +164,10 @@ class AutofillPaymentInstrumentTest : public testing::Test {
// A valid local credit card is a valid instrument for payment.
TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment) {
- AutofillPaymentInstrument instrument("visa", local_credit_card(),
- billing_profiles(), "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", local_credit_card(),
+ /*matches_merchant_card_type_exactly=*/true, billing_profiles(), "en-US",
+ nullptr);
EXPECT_TRUE(instrument.IsCompleteForPayment());
EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
}
@@ -174,8 +176,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment) {
TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_Expired) {
autofill::CreditCard& card = local_credit_card();
card.SetExpirationYear(2016); // Expired.
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_TRUE(instrument.IsCompleteForPayment());
EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
}
@@ -186,8 +189,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoName) {
card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
base::ASCIIToUTF16(""), "en-US");
base::string16 missing_info;
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_FALSE(instrument.IsCompleteForPayment());
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_ON_CARD_REQUIRED),
instrument.GetMissingInfoLabel());
@@ -198,8 +202,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoNumber) {
autofill::CreditCard& card = local_credit_card();
card.SetNumber(base::ASCIIToUTF16(""));
base::string16 missing_info;
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_FALSE(instrument.IsCompleteForPayment());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE),
@@ -212,8 +217,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoBillinbAddressId) {
autofill::CreditCard& card = local_credit_card();
card.set_billing_address_id("");
base::string16 missing_info;
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_FALSE(instrument.IsCompleteForPayment());
EXPECT_EQ(
l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
@@ -227,8 +233,9 @@ TEST_F(AutofillPaymentInstrumentTest,
autofill::CreditCard& card = local_credit_card();
card.set_billing_address_id("InvalidBillingAddressId");
base::string16 missing_info;
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_FALSE(instrument.IsCompleteForPayment());
EXPECT_EQ(
l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
@@ -243,8 +250,9 @@ TEST_F(AutofillPaymentInstrumentTest,
card.SetNumber(base::ASCIIToUTF16(""));
card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
base::ASCIIToUTF16(""), "en-US");
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_FALSE(instrument.IsCompleteForPayment());
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED),
instrument.GetMissingInfoLabel());
@@ -255,8 +263,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_MaskedCard) {
autofill::CreditCard card = autofill::test::GetMaskedServerCard();
ASSERT_GT(billing_profiles().size(), 0UL);
card.set_billing_address_id(billing_profiles()[0]->guid());
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_TRUE(instrument.IsCompleteForPayment());
EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
}
@@ -267,8 +276,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_ExpiredMaskedCard) {
ASSERT_GT(billing_profiles().size(), 0UL);
card.set_billing_address_id(billing_profiles()[0]->guid());
card.SetExpirationYear(2016); // Expired.
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_TRUE(instrument.IsCompleteForPayment());
EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
}
@@ -277,8 +287,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_ExpiredMaskedCard) {
TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_Minimal) {
autofill::CreditCard& card = local_credit_card();
card.SetExpirationYear(2016); // Expired.
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_TRUE(instrument.IsValidForCanMakePayment());
}
@@ -286,8 +297,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_Minimal) {
TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_MaskedCard) {
autofill::CreditCard card = autofill::test::GetMaskedServerCard();
card.SetExpirationYear(2016); // Expired.
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_TRUE(instrument.IsValidForCanMakePayment());
}
@@ -296,8 +308,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_NoName) {
autofill::CreditCard& card = local_credit_card();
card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
base::ASCIIToUTF16(""), "en-US");
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_FALSE(instrument.IsValidForCanMakePayment());
}
@@ -305,8 +318,9 @@ TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_NoName) {
TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_NoNumber) {
autofill::CreditCard& card = local_credit_card();
card.SetNumber(base::ASCIIToUTF16(""));
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", nullptr);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", nullptr);
EXPECT_FALSE(instrument.IsValidForCanMakePayment());
}
@@ -320,8 +334,9 @@ TEST_F(AutofillPaymentInstrumentTest,
autofill::CreditCard& card = local_credit_card();
card.SetNumber(base::ASCIIToUTF16(""));
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", &delegate);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", &delegate);
FakePaymentInstrumentDelegate instrument_delegate;
@@ -348,8 +363,9 @@ TEST_F(AutofillPaymentInstrumentTest,
autofill::CreditCard& card = local_credit_card();
card.SetNumber(base::ASCIIToUTF16(""));
- AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
- "en-US", &delegate);
+ AutofillPaymentInstrument instrument(
+ "visa", card, /*matches_merchant_card_type_exactly=*/true,
+ billing_profiles(), "en-US", &delegate);
FakePaymentInstrumentDelegate instrument_delegate;
diff --git a/chromium/components/payments/core/basic_card_response.cc b/chromium/components/payments/core/basic_card_response.cc
index 77921c1c294..57821b9d23d 100644
--- a/chromium/components/payments/core/basic_card_response.cc
+++ b/chromium/components/payments/core/basic_card_response.cc
@@ -45,17 +45,12 @@ std::unique_ptr<base::DictionaryValue> BasicCardResponse::ToDictionaryValue()
std::unique_ptr<base::DictionaryValue> result =
base::MakeUnique<base::DictionaryValue>();
- if (!this->cardholder_name.empty())
- result->SetString(kCardCardholderName, this->cardholder_name);
+ result->SetString(kCardCardholderName, this->cardholder_name);
result->SetString(kCardCardNumber, this->card_number);
- if (!this->expiry_month.empty())
- result->SetString(kCardExpiryMonth, this->expiry_month);
- if (!this->expiry_year.empty())
- result->SetString(kCardExpiryYear, this->expiry_year);
- if (!this->card_security_code.empty())
- result->SetString(kCardCardSecurityCode, this->card_security_code);
- if (!this->billing_address.ToDictionaryValue()->empty())
- result->Set(kCardBillingAddress, this->billing_address.ToDictionaryValue());
+ result->SetString(kCardExpiryMonth, this->expiry_month);
+ result->SetString(kCardExpiryYear, this->expiry_year);
+ result->SetString(kCardCardSecurityCode, this->card_security_code);
+ result->Set(kCardBillingAddress, this->billing_address.ToDictionaryValue());
return result;
}
diff --git a/chromium/components/payments/core/features.cc b/chromium/components/payments/core/features.cc
new file mode 100644
index 00000000000..e3694b24811
--- /dev/null
+++ b/chromium/components/payments/core/features.cc
@@ -0,0 +1,19 @@
+// 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/payments/core/features.h"
+
+namespace payments {
+namespace features {
+
+#if defined(OS_IOS)
+const base::Feature kWebPayments{"WebPayments",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
+const base::Feature kWebPaymentsModifiers{"WebPaymentsModifiers",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+} // namespace features
+} // namespace payments
diff --git a/chromium/components/payments/core/features.h b/chromium/components/payments/core/features.h
new file mode 100644
index 00000000000..926e267cef4
--- /dev/null
+++ b/chromium/components/payments/core/features.h
@@ -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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_FEATURES_H_
+#define COMPONENTS_PAYMENTS_CORE_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+
+namespace payments {
+namespace features {
+
+#if defined(OS_IOS)
+// Used to control the state of the Payment Request API feature.
+extern const base::Feature kWebPayments;
+#endif
+
+// Used to control the support for Payment Details modifiers.
+extern const base::Feature kWebPaymentsModifiers;
+
+} // namespace features
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_FEATURES_H_
diff --git a/chromium/components/payments/core/journey_logger.cc b/chromium/components/payments/core/journey_logger.cc
index 7eee988f1be..ffdd5b837d2 100644
--- a/chromium/components/payments/core/journey_logger.cc
+++ b/chromium/components/payments/core/journey_logger.cc
@@ -8,8 +8,8 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
-#include "components/ukm/public/ukm_entry_builder.h"
-#include "components/ukm/public/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_entry_builder.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
namespace payments {
@@ -36,8 +36,8 @@ std::string GetHistogramNameSuffix(
case JourneyLogger::SECTION_CONTACT_INFO:
name_suffix = "ContactInfo.";
break;
- case JourneyLogger::SECTION_CREDIT_CARDS:
- name_suffix = "CreditCards.";
+ case JourneyLogger::SECTION_PAYMENT_METHOD:
+ name_suffix = "PaymentMethod.";
break;
default:
break;
@@ -91,10 +91,13 @@ void JourneyLogger::IncrementSelectionEdits(Section section) {
sections_[section].number_selection_edits_++;
}
-void JourneyLogger::SetNumberOfSuggestionsShown(Section section, int number) {
+void JourneyLogger::SetNumberOfSuggestionsShown(Section section,
+ int number,
+ bool has_complete_suggestion) {
DCHECK_LT(section, SECTION_MAX);
sections_[section].number_suggestions_shown_ = number;
sections_[section].is_requested_ = true;
+ sections_[section].has_complete_suggestion_ = has_complete_suggestion;
}
void JourneyLogger::SetCanMakePaymentValue(bool value) {
@@ -106,6 +109,29 @@ void JourneyLogger::SetShowCalled() {
was_show_called_ = true;
}
+void JourneyLogger::SetEventOccurred(Event event) {
+ events_ |= event;
+}
+
+void JourneyLogger::SetSelectedPaymentMethod(
+ SelectedPaymentMethod payment_method) {
+ payment_method_ = payment_method;
+}
+
+void JourneyLogger::SetRequestedInformation(bool requested_shipping,
+ bool requested_email,
+ bool requested_phone,
+ bool requested_name) {
+ // This method should only be called once per Payment Request.
+ DCHECK(requested_information_ == REQUESTED_INFORMATION_MAX);
+
+ requested_information_ =
+ (requested_shipping ? REQUESTED_INFORMATION_SHIPPING : 0) |
+ (requested_email ? REQUESTED_INFORMATION_EMAIL : 0) |
+ (requested_phone ? REQUESTED_INFORMATION_PHONE : 0) |
+ (requested_name ? REQUESTED_INFORMATION_NAME : 0);
+}
+
void JourneyLogger::SetCompleted() {
UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Completed", true);
@@ -113,8 +139,11 @@ void JourneyLogger::SetCompleted() {
}
void JourneyLogger::SetAborted(AbortReason reason) {
- base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.Aborted", reason,
- ABORT_REASON_MAX);
+ // Don't log abort reasons if the Payment Request was not shown to the user.
+ if (was_show_called_) {
+ base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.Aborted",
+ reason, ABORT_REASON_MAX);
+ }
if (reason == ABORT_REASON_ABORTED_BY_USER ||
reason == ABORT_REASON_USER_NAVIGATION)
@@ -123,7 +152,6 @@ void JourneyLogger::SetAborted(AbortReason reason) {
RecordJourneyStatsHistograms(COMPLETION_STATUS_OTHER_ABORTED);
}
-#ifdef OS_ANDROID
void JourneyLogger::SetNotShown(NotShownReason reason) {
base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.NoShow", reason,
NOT_SHOWN_REASON_MAX);
@@ -132,30 +160,6 @@ void JourneyLogger::SetNotShown(NotShownReason reason) {
// will be recorded for a Payment Request that was not shown to the user.
UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Initiated", true);
}
-#endif
-
-void JourneyLogger::SetEventOccurred(Event event) {
- events_ |= event;
-}
-
-void JourneyLogger::SetSelectedPaymentMethod(
- SelectedPaymentMethod payment_method) {
- payment_method_ = payment_method;
-}
-
-void JourneyLogger::SetRequestedInformation(bool requested_shipping,
- bool requested_email,
- bool requested_phone,
- bool requested_name) {
- // This method should only be called once per Payment Request.
- DCHECK(requested_information_ == REQUESTED_INFORMATION_MAX);
-
- requested_information_ =
- (requested_shipping ? REQUESTED_INFORMATION_SHIPPING : 0) |
- (requested_email ? REQUESTED_INFORMATION_EMAIL : 0) |
- (requested_phone ? REQUESTED_INFORMATION_PHONE : 0) |
- (requested_name ? REQUESTED_INFORMATION_NAME : 0);
-}
void JourneyLogger::RecordJourneyStatsHistograms(
CompletionStatus completion_status) {
@@ -163,18 +167,16 @@ void JourneyLogger::RecordJourneyStatsHistograms(
has_recorded_ = true;
RecordCheckoutFlowMetrics();
-
- RecordPaymentMethodMetric();
-
- RecordRequestedInformationMetrics();
-
- RecordSectionSpecificStats(completion_status);
-
- // Record the CanMakePayment metrics based on whether the transaction was
- // completed or aborted by the user (UserAborted) or otherwise (OtherAborted).
RecordCanMakePaymentStats(completion_status);
-
RecordUrlKeyedMetrics(completion_status);
+ RecordEventsMetric(completion_status);
+
+ // These following metrics only make sense if the UI was shown to the user.
+ if (was_show_called_) {
+ RecordPaymentMethodMetric();
+ RecordRequestedInformationMetrics();
+ RecordSectionSpecificStats(completion_status);
+ }
}
void JourneyLogger::RecordCheckoutFlowMetrics() {
@@ -200,6 +202,7 @@ void JourneyLogger::RecordPaymentMethodMetric() {
}
void JourneyLogger::RecordRequestedInformationMetrics() {
+ DCHECK(requested_information_ != REQUESTED_INFORMATION_MAX);
UMA_HISTOGRAM_ENUMERATION("PaymentRequest.RequestedInformation",
requested_information_, REQUESTED_INFORMATION_MAX);
}
@@ -209,6 +212,10 @@ void JourneyLogger::RecordSectionSpecificStats(
// Record whether the user had suggestions for each requested information.
bool user_had_all_requested_information = true;
+ // Record whether the user had at least one complete suggestion for each
+ // requested section.
+ bool user_had_complete_suggestions_ = true;
+
for (int i = 0; i < NUMBER_OF_SECTIONS; ++i) {
std::string name_suffix = GetHistogramNameSuffix(i, completion_status);
@@ -233,6 +240,9 @@ void JourneyLogger::RecordSectionSpecificStats(
if (sections_[i].number_suggestions_shown_ == 0) {
user_had_all_requested_information = false;
+ user_had_complete_suggestions_ = false;
+ } else if (!sections_[i].has_complete_suggestion_) {
+ user_had_complete_suggestions_ = false;
}
}
}
@@ -250,6 +260,20 @@ void JourneyLogger::RecordSectionSpecificStats(
"EffectOnCompletion",
completion_status, COMPLETION_STATUS_MAX);
}
+
+ // Record metrics about completion based on whether the user had complete
+ // suggestions for each requested information.
+ if (user_had_complete_suggestions_) {
+ base::UmaHistogramEnumeration(
+ "PaymentRequest.UserHadCompleteSuggestionsForEverything."
+ "EffectOnCompletion",
+ completion_status, COMPLETION_STATUS_MAX);
+ } else {
+ base::UmaHistogramEnumeration(
+ "PaymentRequest.UserDidNotHaveCompleteSuggestionsForEverything."
+ "EffectOnCompletion",
+ completion_status, COMPLETION_STATUS_MAX);
+ }
}
void JourneyLogger::RecordCanMakePaymentStats(
@@ -302,6 +326,44 @@ void JourneyLogger::RecordCanMakePaymentEffectOnCompletion(
COMPLETION_STATUS_MAX);
}
+void JourneyLogger::RecordEventsMetric(CompletionStatus completion_status) {
+ // Add the completion status to the events.
+ switch (completion_status) {
+ case COMPLETION_STATUS_COMPLETED:
+ events_ |= EVENT_COMPLETED;
+ break;
+ case COMPLETION_STATUS_USER_ABORTED:
+ events_ |= EVENT_USER_ABORTED;
+ break;
+ case COMPLETION_STATUS_OTHER_ABORTED:
+ events_ |= EVENT_OTHER_ABORTED;
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // Add the whether the user had complete suggestions for all requested
+ // sections to the events.
+ bool user_had_complete_suggestions_for_requested_information = true;
+ for (int i = 0; i < NUMBER_OF_SECTIONS; ++i) {
+ if (sections_[i].is_requested_) {
+ if (sections_[i].number_suggestions_shown_ == 0 ||
+ !sections_[i].has_complete_suggestion_) {
+ user_had_complete_suggestions_for_requested_information = false;
+ }
+ }
+ }
+ if (user_had_complete_suggestions_for_requested_information)
+ events_ |= EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS;
+
+ // Add whether the user had and initial form of payment to the events.
+ if (sections_[SECTION_PAYMENT_METHOD].number_suggestions_shown_ > 0)
+ events_ |= EVENT_HAD_INITIAL_FORM_OF_PAYMENT;
+
+ // Record the events.
+ UMA_HISTOGRAM_SPARSE_SLOWLY("PaymentRequest.Events", events_);
+}
+
void JourneyLogger::RecordUrlKeyedMetrics(CompletionStatus completion_status) {
if (!ukm_recorder_ || !url_.is_valid())
return;
diff --git a/chromium/components/payments/core/journey_logger.h b/chromium/components/payments/core/journey_logger.h
index b37f7ae48dd..6db0e978ce1 100644
--- a/chromium/components/payments/core/journey_logger.h
+++ b/chromium/components/payments/core/journey_logger.h
@@ -38,7 +38,7 @@ class JourneyLogger {
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: Section
enum Section {
SECTION_CONTACT_INFO = 0,
- SECTION_CREDIT_CARDS = 1,
+ SECTION_PAYMENT_METHOD = 1,
SECTION_SHIPPING_ADDRESS = 2,
SECTION_MAX,
};
@@ -106,7 +106,12 @@ class JourneyLogger {
EVENT_PAY_CLICKED = 1 << 1,
EVENT_RECEIVED_INSTRUMENT_DETAILS = 1 << 2,
EVENT_SKIPPED_SHOW = 1 << 3,
- EVENT_ENUM_MAX = 16,
+ EVENT_COMPLETED = 1 << 4,
+ EVENT_USER_ABORTED = 1 << 5,
+ EVENT_OTHER_ABORTED = 1 << 6,
+ EVENT_HAD_INITIAL_FORM_OF_PAYMENT = 1 << 7,
+ EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS = 1 << 8,
+ EVENT_ENUM_MAX = 512,
};
// The reason why the Payment Request was aborted.
@@ -127,7 +132,6 @@ class JourneyLogger {
ABORT_REASON_MAX,
};
-#ifdef OS_ANDROID
// The reason why the Payment Request was not shown to the user.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: NotShownReason
@@ -138,7 +142,6 @@ class JourneyLogger {
NOT_SHOWN_REASON_OTHER = 3,
NOT_SHOWN_REASON_MAX = 4,
};
-#endif
JourneyLogger(bool is_incognito,
const GURL& url,
@@ -155,7 +158,9 @@ class JourneyLogger {
void IncrementSelectionEdits(Section section);
// Sets the number of suggestions shown for the specified section.
- void SetNumberOfSuggestionsShown(Section section, int number);
+ void SetNumberOfSuggestionsShown(Section section,
+ int number,
+ bool has_valid_suggestion);
// Records the fact that the merchant called CanMakePayment and records it's
// return value.
@@ -184,11 +189,9 @@ class JourneyLogger {
// starts the logging of all the journey metrics.
void SetAborted(AbortReason reason);
-#ifdef OS_ANDROID
// Records that the Payment Request was not shown to the user, along with the
// reason.
void SetNotShown(NotShownReason reason);
-#endif
private:
static const int NUMBER_OF_SECTIONS = 3;
@@ -208,13 +211,15 @@ class JourneyLogger {
number_selection_changes_(0),
number_selection_edits_(0),
number_suggestions_shown_(0),
- is_requested_(false) {}
+ is_requested_(false),
+ has_complete_suggestion_(false) {}
int number_selection_adds_;
int number_selection_changes_;
int number_selection_edits_;
int number_suggestions_shown_;
bool is_requested_;
+ bool has_complete_suggestion_;
};
// Records the histograms for all the sections that were requested by the
@@ -250,6 +255,10 @@ class JourneyLogger {
void RecordCanMakePaymentEffectOnCompletion(
CompletionStatus completion_status);
+ // Records the metric about the different events that happened during the
+ // Payment Request.
+ void RecordEventsMetric(CompletionStatus completion_status);
+
// Records the Payment Request Url Keyed Metrics.
void RecordUrlKeyedMetrics(CompletionStatus completion_status);
diff --git a/chromium/components/payments/core/journey_logger_unittest.cc b/chromium/components/payments/core/journey_logger_unittest.cc
index 941fccb580d..f62931072ab 100644
--- a/chromium/components/payments/core/journey_logger_unittest.cc
+++ b/chromium/components/payments/core/journey_logger_unittest.cc
@@ -54,6 +54,7 @@ TEST(JourneyLoggerTest,
// The merchant does not query CanMakePayment, show the PaymentRequest and the
// user aborts it.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
@@ -83,6 +84,7 @@ TEST(JourneyLoggerTest,
// The merchant does not query CanMakePayment, show the PaymentRequest and
// there is an abort not initiated by the user.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
@@ -112,6 +114,7 @@ TEST(JourneyLoggerTest,
// The merchant does not query CanMakePayment, show the PaymentRequest and the
// user completes it.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCompleted();
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
@@ -207,6 +210,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCanMakePaymentValue(false);
logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
@@ -240,6 +244,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCanMakePaymentValue(false);
logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
@@ -273,6 +278,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCanMakePaymentValue(false);
logger.SetCompleted();
@@ -307,6 +313,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCanMakePaymentValue(true);
logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
@@ -342,6 +349,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCanMakePaymentValue(true);
logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
@@ -377,6 +385,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCanMakePaymentValue(true);
logger.SetCompleted();
@@ -412,6 +421,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
+ logger.SetRequestedInformation(true, false, false, false);
logger.SetCanMakePaymentValue(true);
logger.SetCompleted();
@@ -429,20 +439,43 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
// Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the user completes the checkout.
logger.SetCompleted();
+ // Make sure that the expected metric was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+ // Make sure the opposite metric was not logged.
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
}
// Tests that the completion status metrics based on whether the user had
@@ -453,20 +486,43 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
// Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the user aborts the checkout.
logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
+ // Make sure the expected metric was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+ // Make sure the opposite metric was not logged.
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
}
// Tests that the completion status metrics based on whether the user had
@@ -477,20 +533,43 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
// Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the checkout is aborted.
logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+ // Make sure the expected metric was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+ // Make sure that the opposite metric was not logged.
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
}
// Tests that the completion status metrics based on whether the user had
@@ -502,20 +581,43 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
// Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the user completes the checkout.
logger.SetCompleted();
+ // Make sure the expected metric was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+ // Make sure the opposite metric was not logged.
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
}
// Tests that the completion status metrics based on whether the user had
@@ -526,21 +628,44 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
- // Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
+ // Simulate that the user had suggestions for none of the requested sections.
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the user completes the checkout.
logger.SetCompleted();
+ // Make sure the expected metrics was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+ // Make sure the opposite metrics was not logged.
EXPECT_THAT(
histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
}
// Tests that the completion status metrics based on whether the user had
@@ -551,21 +676,44 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
- // Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
+ // Simulate that the user had suggestions for none of the requested sections.
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the user aborts the checkout.
logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
+ // Make sure the metric was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+ // Make sure the opposite metric was not logged.
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserHadSuggestionsForEverything."
"EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
}
// Tests that the completion status metrics based on whether the user had
@@ -576,21 +724,44 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
- // Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
+ // Simulate that the user had suggestions for none of the requested sections.
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the the checkout is aborted.
logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+ // Make sure the expected metric was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+ // Make sure the opposite metric was not logged.
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserHadSuggestionsForEverything."
"EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
}
// Tests that the completion status metrics based on whether the user had
@@ -602,21 +773,199 @@ TEST(JourneyLoggerTest,
JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
/*ukm_recorder=*/nullptr);
- // Simulate that the user had suggestions for all the requested sections.
- logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
+ // Simulate that the user had suggestions for none of the requested sections.
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
// Simulate that the user aborts the checkout.
logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
+ // Make sure the expected metric was logged.
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
"EffectOnCompletion",
JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+ // Make sure the opposite metric was not logged.
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
"PaymentRequest.UserHadSuggestionsForEverything."
"EffectOnCompletion"),
testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(
+ JourneyLoggerTest,
+ RecordJourneyStatsHistograms_NoCompleteSuggestionsForEverything_OtherAborted) {
+ base::HistogramTester histogram_tester;
+ JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
+ /*ukm_recorder=*/nullptr);
+
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/false, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
+ // Simulate that the user had incomplete suggestions for the requested
+ // sections.
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 2,
+ /*has_complete_suggestion=*/false);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
+
+ // Simulate that the the checkout is aborted.
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+
+ // Make sure the expected metric was logged.
+ histogram_tester.ExpectBucketCount(
+ "PaymentRequest.UserDidNotHaveCompleteSuggestionsForEverything."
+ "EffectOnCompletion",
+ JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+
+ // Makes sure the opposite metric was not logged.
+ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+ "PaymentRequest.UserHadCompleteSuggestionsForEverything."
+ "EffectOnCompletion"),
+ testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(
+ JourneyLoggerTest,
+ RecordJourneyStatsHistograms_NoCompleteSuggestionsForEverything_SomeComplete_OtherAborted) {
+ base::HistogramTester histogram_tester;
+ JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
+ /*ukm_recorder=*/nullptr);
+
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/true, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
+ // Simulate that the user had incomplete suggestions for one of the requested
+ // sections.
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 2,
+ /*has_complete_suggestion=*/false);
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_SHIPPING_ADDRESS, 1,
+ /*has_complete_suggestion=*/true);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
+
+ // Simulate that the the checkout is aborted.
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+
+ // Make sure that the expected metric was logged.
+ histogram_tester.ExpectBucketCount(
+ "PaymentRequest.UserDidNotHaveCompleteSuggestionsForEverything."
+ "EffectOnCompletion",
+ JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+
+ // Make sure that the opposite metric was not logged.
+ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+ "PaymentRequest.UserHadCompleteSuggestionsForEverything."
+ "EffectOnCompletion"),
+ testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_FALSE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(
+ JourneyLoggerTest,
+ RecordJourneyStatsHistograms_CompleteSuggestionsForEverything_OtherAborted) {
+ base::HistogramTester histogram_tester;
+ JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
+ /*ukm_recorder=*/nullptr);
+
+ // The merchant only requests payment information.
+ logger.SetRequestedInformation(
+ /*requested_shipping=*/true, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
+
+ // Simulate that the user had incomplete suggestions for one of the requested
+ // sections.
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 2,
+ /*has_complete_suggestion=*/true);
+ logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_SHIPPING_ADDRESS, 1,
+ /*has_complete_suggestion=*/true);
+
+ // Simulate that the Payment Request was shown to the user.
+ logger.SetShowCalled();
+
+ // Simulate that the the checkout is aborted.
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+
+ // Make sure that the expected metric was logged.
+ histogram_tester.ExpectBucketCount(
+ "PaymentRequest.UserHadCompleteSuggestionsForEverything."
+ "EffectOnCompletion",
+ JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+
+ // Make sure that the opposite metric was not logged.
+ EXPECT_THAT(
+ histogram_tester.GetTotalCountsForPrefix(
+ "PaymentRequest.UserDidNotHaveCompleteSuggestionsForEverything."
+ "EffectOnCompletion"),
+ testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+ // Make sure the correct events were logged.
+ std::vector<base::Bucket> buckets =
+ histogram_tester.GetAllSamples("PaymentRequest.Events");
+ ASSERT_EQ(1U, buckets.size());
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+ EXPECT_TRUE(buckets[0].min &
+ JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+ EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+ EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
}
// Tests that the metrics are logged correctly for two simultaneous Payment
@@ -630,12 +979,20 @@ TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
// Make the two loggers have different data.
logger1.SetShowCalled();
+ logger1.SetRequestedInformation(
+ /*requested_shipping=*/true, /*requested_email=*/true,
+ /*requested_phone=*/false, /*requested_name=*/false);
logger2.SetShowCalled();
+ logger2.SetRequestedInformation(
+ /*requested_shipping=*/true, /*requested_email=*/false,
+ /*requested_phone=*/false, /*requested_name=*/false);
logger1.SetCanMakePaymentValue(true);
- logger1.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
- logger2.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+ logger1.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
+ /*has_complete_suggestion=*/false);
+ logger2.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
+ /*has_complete_suggestion=*/false);
// Simulate that the user completes one checkout and aborts the other.
logger1.SetCompleted();
@@ -664,9 +1021,11 @@ TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
}
-// Tests that the Payment Request UKMs are logged correctly.
-TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_CheckoutFunnelUkm) {
- ukm::TestUkmRecorder ukm_recorder;
+// Tests that the Payment Request UKMs are logged correctly when the user aborts
+// the Payment Request.
+TEST(JourneyLoggerTest,
+ RecordJourneyStatsHistograms_CheckoutFunnelUkm_UserAborted) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
char test_url[] = "http://www.google.com/";
base::HistogramTester histogram_tester;
@@ -703,4 +1062,41 @@ TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_CheckoutFunnelUkm) {
step_metric->value);
}
+// Tests that the Payment Request UKMs are logged correctly when the user
+// completes the Payment Request.
+TEST(JourneyLoggerTest,
+ RecordJourneyStatsHistograms_CheckoutFunnelUkm_Completed) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+ char test_url[] = "http://www.google.com/";
+
+ base::HistogramTester histogram_tester;
+ JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url),
+ /*ukm_recorder=*/&ukm_recorder);
+
+ // Simulate that the user aborts after being shown the Payment Request.
+ logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
+ logger.SetCompleted();
+
+ // Make sure the UKM was logged correctly.
+ ASSERT_EQ(1U, ukm_recorder.sources_count());
+ const ukm::UkmSource* source = ukm_recorder.GetSourceForUrl(test_url);
+ ASSERT_NE(nullptr, source);
+
+ ASSERT_EQ(1U, ukm_recorder.entries_count());
+ const ukm::mojom::UkmEntry* entry = ukm_recorder.GetEntry(0);
+ EXPECT_EQ(source->id(), entry->source_id);
+ EXPECT_EQ(base::HashMetricName(internal::kUKMCheckoutEventsEntryName),
+ entry->event_hash);
+
+ const ukm::mojom::UkmMetric* status_metric = ukm::TestUkmRecorder::FindMetric(
+ entry, internal::kUKMCompletionStatusMetricName);
+ ASSERT_NE(nullptr, status_metric);
+ EXPECT_EQ(JourneyLogger::COMPLETION_STATUS_COMPLETED, status_metric->value);
+
+ const ukm::mojom::UkmMetric* step_metric =
+ ukm::TestUkmRecorder::FindMetric(entry, internal::kUKMEventsMetricName);
+ ASSERT_NE(nullptr, step_metric);
+ EXPECT_EQ(JourneyLogger::EVENT_SHOWN, step_metric->value);
+}
+
} // namespace payments
diff --git a/chromium/components/payments/core/payment_address.cc b/chromium/components/payments/core/payment_address.cc
index 42b61c9ad8d..d5d72592013 100644
--- a/chromium/components/payments/core/payment_address.cc
+++ b/chromium/components/payments/core/payment_address.cc
@@ -52,37 +52,23 @@ std::unique_ptr<base::DictionaryValue> PaymentAddress::ToDictionaryValue()
const {
std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
- if (!this->country.empty())
- result->SetString(kAddressCountry, this->country);
-
- if (!this->address_line.empty()) {
- std::unique_ptr<base::ListValue> address_line =
- base::MakeUnique<base::ListValue>();
- for (const base::string16& address_line_string : this->address_line) {
- if (!address_line_string.empty())
- address_line->AppendString(address_line_string);
- }
- result->Set(kAddressAddressLine, std::move(address_line));
+ result->SetString(kAddressCountry, this->country);
+ std::unique_ptr<base::ListValue> address_line =
+ base::MakeUnique<base::ListValue>();
+ for (const base::string16& address_line_string : this->address_line) {
+ if (!address_line_string.empty())
+ address_line->AppendString(address_line_string);
}
-
- if (!this->region.empty())
- result->SetString(kAddressRegion, this->region);
- if (!this->city.empty())
- result->SetString(kAddressCity, this->city);
- if (!this->dependent_locality.empty())
- result->SetString(kAddressDependentLocality, this->dependent_locality);
- if (!this->postal_code.empty())
- result->SetString(kAddressPostalCode, this->postal_code);
- if (!this->sorting_code.empty())
- result->SetString(kAddressSortingCode, this->sorting_code);
- if (!this->language_code.empty())
- result->SetString(kAddressLanguageCode, this->language_code);
- if (!this->organization.empty())
- result->SetString(kAddressOrganization, this->organization);
- if (!this->recipient.empty())
- result->SetString(kAddressRecipient, this->recipient);
- if (!this->phone.empty())
- result->SetString(kAddressPhone, this->phone);
+ result->Set(kAddressAddressLine, std::move(address_line));
+ result->SetString(kAddressRegion, this->region);
+ result->SetString(kAddressCity, this->city);
+ result->SetString(kAddressDependentLocality, this->dependent_locality);
+ result->SetString(kAddressPostalCode, this->postal_code);
+ result->SetString(kAddressSortingCode, this->sorting_code);
+ result->SetString(kAddressLanguageCode, this->language_code);
+ result->SetString(kAddressOrganization, this->organization);
+ result->SetString(kAddressRecipient, this->recipient);
+ result->SetString(kAddressPhone, this->phone);
return result;
}
diff --git a/chromium/components/payments/core/payment_instrument.h b/chromium/components/payments/core/payment_instrument.h
index 6da07693d8e..24b1d14cac2 100644
--- a/chromium/components/payments/core/payment_instrument.h
+++ b/chromium/components/payments/core/payment_instrument.h
@@ -7,9 +7,11 @@
#include <set>
#include <string>
+#include <vector>
#include "base/macros.h"
#include "base/strings/string16.h"
+#include "components/autofill/core/browser/credit_card.h"
namespace payments {
@@ -38,19 +40,30 @@ class PaymentInstrument {
virtual void InvokePaymentApp(Delegate* delegate) = 0;
// Returns whether the instrument is complete to be used as a payment method
// without further editing.
- virtual bool IsCompleteForPayment() = 0;
+ virtual bool IsCompleteForPayment() const = 0;
+ // Returns whether the instrument is exactly matching all filters provided by
+ // the merchant. For example, this can return "false" for unknown card types,
+ // if the merchant requested only debit cards.
+ virtual bool IsExactlyMatchingMerchantRequest() const = 0;
// Returns a message to indicate to the user what's missing for the instrument
// to be complete for payment.
- virtual base::string16 GetMissingInfoLabel() = 0;
+ virtual base::string16 GetMissingInfoLabel() const = 0;
// Returns whether the instrument is valid for the purposes of responding to
// canMakePayment.
- virtual bool IsValidForCanMakePayment() = 0;
+ virtual bool IsValidForCanMakePayment() const = 0;
// Records the use of this payment instrument.
virtual void RecordUse() = 0;
// Return the sub/label of payment instrument, to be displayed to the user.
virtual base::string16 GetLabel() const = 0;
virtual base::string16 GetSublabel() const = 0;
+ // Returns true if this payment instrument can be used to fulfill a request
+ // specifying |method| as a supported method of payment, false otherwise.
+ virtual bool IsValidForModifier(
+ const std::vector<std::string>& method,
+ const std::set<autofill::CreditCard::CardType>& supported_types,
+ const std::vector<std::string>& supported_networks) const = 0;
+
const std::string& method_name() const { return method_name_; }
int icon_resource_id() const { return icon_resource_id_; }
Type type() { return type_; }
diff --git a/chromium/components/payments/core/payment_method_data.cc b/chromium/components/payments/core/payment_method_data.cc
index 2ea818e579f..b0bb839d699 100644
--- a/chromium/components/payments/core/payment_method_data.cc
+++ b/chromium/components/payments/core/payment_method_data.cc
@@ -20,6 +20,27 @@ static const char kSupportedMethods[] = "supportedMethods";
static const char kSupportedNetworks[] = "supportedNetworks";
static const char kSupportedTypes[] = "supportedTypes";
+// Converts |supported_type| to |card_type| and returns true on success.
+bool ConvertCardTypeStringToEnum(const std::string& supported_type,
+ autofill::CreditCard::CardType* card_type) {
+ if (supported_type == "credit") {
+ *card_type = autofill::CreditCard::CARD_TYPE_CREDIT;
+ return true;
+ }
+
+ if (supported_type == "debit") {
+ *card_type = autofill::CreditCard::CARD_TYPE_DEBIT;
+ return true;
+ }
+
+ if (supported_type == "prepaid") {
+ *card_type = autofill::CreditCard::CARD_TYPE_PREPAID;
+ return true;
+ }
+
+ return false;
+}
+
} // namespace
PaymentMethodData::PaymentMethodData() {}
@@ -84,7 +105,10 @@ bool PaymentMethodData::FromDictionaryValue(
!base::IsStringASCII(supported_type)) {
return false;
}
- this->supported_types.push_back(supported_type);
+ autofill::CreditCard::CardType card_type =
+ autofill::CreditCard::CARD_TYPE_UNKNOWN;
+ if (ConvertCardTypeStringToEnum(supported_type, &card_type))
+ this->supported_types.insert(card_type);
}
}
}
diff --git a/chromium/components/payments/core/payment_method_data.h b/chromium/components/payments/core/payment_method_data.h
index 5bc7241db27..f0ce9237db5 100644
--- a/chromium/components/payments/core/payment_method_data.h
+++ b/chromium/components/payments/core/payment_method_data.h
@@ -6,9 +6,12 @@
#define COMPONENTS_PAYMENTS_CORE_PAYMENT_METHOD_DATA_H_
#include <memory>
+#include <set>
#include <string>
#include <vector>
+#include "components/autofill/core/browser/credit_card.h"
+
namespace base {
class DictionaryValue;
}
@@ -41,7 +44,7 @@ class PaymentMethodData {
// When the methods include "basic-card", a list of networks and types that
// are supported.
std::vector<std::string> supported_networks;
- std::vector<std::string> supported_types;
+ std::set<autofill::CreditCard::CardType> supported_types;
};
} // namespace payments
diff --git a/chromium/components/payments/core/payment_method_data_unittest.cc b/chromium/components/payments/core/payment_method_data_unittest.cc
index a20e283a175..b44a23161d3 100644
--- a/chromium/components/payments/core/payment_method_data_unittest.cc
+++ b/chromium/components/payments/core/payment_method_data_unittest.cc
@@ -12,20 +12,14 @@ namespace payments {
// Tests the success case when populating a PaymentMethodData from a dictionary.
TEST(PaymentMethodData, FromDictionaryValueSuccess) {
PaymentMethodData expected;
- std::vector<std::string> supported_methods;
- supported_methods.push_back("visa");
- supported_methods.push_back("basic-card");
- expected.supported_methods = supported_methods;
+ expected.supported_methods.push_back("visa");
+ expected.supported_methods.push_back("basic-card");
expected.data =
"{\"supportedNetworks\":[\"mastercard\"],"
"\"supportedTypes\":[\"debit\",\"credit\"]}";
- std::vector<std::string> supported_networks;
- supported_networks.push_back("mastercard");
- expected.supported_networks = supported_networks;
- std::vector<std::string> supported_types;
- supported_types.push_back("debit");
- supported_types.push_back("credit");
- expected.supported_types = supported_types;
+ 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;
std::unique_ptr<base::ListValue> supported_methods_list(new base::ListValue);
@@ -97,13 +91,11 @@ TEST(PaymentMethodData, Equality) {
method_data2.supported_networks = supported_networks1;
EXPECT_EQ(method_data1, method_data2);
- std::vector<std::string> supported_types1{"credit"};
- method_data1.supported_types = supported_types1;
+ method_data1.supported_types = {autofill::CreditCard::CARD_TYPE_UNKNOWN};
EXPECT_NE(method_data1, method_data2);
- std::vector<std::string> supported_types2{"debit"};
- method_data2.supported_types = supported_types2;
+ method_data2.supported_types = {autofill::CreditCard::CARD_TYPE_DEBIT};
EXPECT_NE(method_data1, method_data2);
- method_data2.supported_types = supported_types1;
+ method_data2.supported_types = method_data1.supported_types;
EXPECT_EQ(method_data1, method_data2);
}
diff --git a/chromium/components/payments/core/payment_request_base_delegate.h b/chromium/components/payments/core/payment_request_base_delegate.h
new file mode 100644
index 00000000000..8b48dce8876
--- /dev/null
+++ b/chromium/components/payments/core/payment_request_base_delegate.h
@@ -0,0 +1,80 @@
+// 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_PAYMENTS_CORE_PAYMENT_REQUEST_BASE_DELEGATE_H_
+#define COMPONENTS_PAYMENTS_CORE_PAYMENT_REQUEST_BASE_DELEGATE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/core/browser/payments/full_card_request.h"
+
+class GURL;
+
+namespace autofill {
+class CreditCard;
+class PersonalDataManager;
+class RegionDataLoader;
+} // namespace autofill
+
+class PrefService;
+
+namespace ukm {
+class UkmRecorder;
+} // namespace ukm
+
+namespace payments {
+
+class AddressNormalizer;
+class PaymentRequest;
+
+class PaymentRequestBaseDelegate {
+ public:
+ virtual ~PaymentRequestBaseDelegate() {}
+
+ // Gets the PersonalDataManager associated with this PaymentRequest flow.
+ // Cannot be null.
+ virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
+
+ virtual const std::string& GetApplicationLocale() const = 0;
+
+ // Returns whether the user is in Incognito mode.
+ virtual bool IsIncognito() const = 0;
+
+ // Returns true if the SSL certificate is valid. Should be called only for
+ // cryptographic schemes.
+ virtual bool IsSslCertificateValid() = 0;
+
+ // Returns the URL of the page that is currently being displayed.
+ virtual const GURL& GetLastCommittedURL() const = 0;
+
+ // Starts a FullCardRequest to unmask |credit_card|.
+ virtual void DoFullCardRequest(
+ const autofill::CreditCard& credit_card,
+ base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+ result_delegate) = 0;
+
+ // Returns a pointer to the address normalizer to use for the duration of this
+ // Payment Request.
+ virtual AddressNormalizer* GetAddressNormalizer() = 0;
+
+ // Creates a new region data loader that will self delete, or a test mock.
+ virtual autofill::RegionDataLoader* GetRegionDataLoader() = 0;
+
+ // Returns a pointer to the UKM service.
+ virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
+
+ // Returns the user's signed-in email address, or empty string if not signed
+ // in.
+ virtual std::string GetAuthenticatedEmail() const = 0;
+
+ // Gets the pref service for the browser context associated with this
+ // PaymentRequest.
+ virtual PrefService* GetPrefService() = 0;
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_PAYMENT_REQUEST_BASE_DELEGATE_H_
diff --git a/chromium/components/payments/core/payment_request_data_util.cc b/chromium/components/payments/core/payment_request_data_util.cc
index 7f689419603..43e7f2c36d8 100644
--- a/chromium/components/payments/core/payment_request_data_util.cc
+++ b/chromium/components/payments/core/payment_request_data_util.cc
@@ -4,13 +4,14 @@
#include "components/payments/core/payment_request_data_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/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_country.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_profile.h"
-#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/validation.h"
@@ -18,6 +19,8 @@
#include "components/payments/core/payment_address.h"
#include "components/payments/core/payment_method_data.h"
#include "third_party/libphonenumber/phonenumber_api.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
namespace payments {
namespace data_util {
@@ -90,17 +93,22 @@ BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
return response;
}
-void ParseBasicCardSupportedNetworks(
+void ParseSupportedMethods(
const std::vector<PaymentMethodData>& method_data,
std::vector<std::string>* out_supported_networks,
- std::set<std::string>* out_basic_card_specified_networks) {
+ std::set<std::string>* out_basic_card_specified_networks,
+ std::vector<std::string>* out_url_payment_method_identifiers) {
DCHECK(out_supported_networks->empty());
DCHECK(out_basic_card_specified_networks->empty());
+ DCHECK(out_url_payment_method_identifiers->empty());
const std::set<std::string> kBasicCardNetworks{
"amex", "diners", "discover", "jcb",
"mastercard", "mir", "unionpay", "visa"};
std::set<std::string> remaining_card_networks(kBasicCardNetworks);
+
+ std::set<GURL> url_payment_method_identifiers;
+
for (const PaymentMethodData& method_data_entry : method_data) {
if (method_data_entry.supported_methods.empty())
return;
@@ -148,11 +156,55 @@ void ParseBasicCardSupportedNetworks(
}
}
}
+ } 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:
+ // https://www.w3.org/TR/payment-method-id/#dfn-validate-a-url-based-payment-method-identifier
+ // Avoid duplicate URLs.
+ GURL url(method);
+ if (url.is_valid() && url.SchemeIs(url::kHttpsScheme) &&
+ !url.has_username() && !url.has_password()) {
+ const auto result = url_payment_method_identifiers.insert(url);
+ if (result.second)
+ out_url_payment_method_identifiers->push_back(method);
+ }
}
}
}
}
+void ParseSupportedCardTypes(
+ const std::vector<PaymentMethodData>& method_data,
+ std::set<autofill::CreditCard::CardType>* out_supported_card_types_set) {
+ 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"))
+ continue;
+
+ for (const autofill::CreditCard::CardType& card_type :
+ method_data_entry.supported_types) {
+ out_supported_card_types_set->insert(card_type);
+ }
+ }
+
+ // Omitting the card types means all 3 card types are supported.
+ if (out_supported_card_types_set->empty()) {
+ out_supported_card_types_set->insert(
+ autofill::CreditCard::CARD_TYPE_CREDIT);
+ out_supported_card_types_set->insert(autofill::CreditCard::CARD_TYPE_DEBIT);
+ out_supported_card_types_set->insert(
+ autofill::CreditCard::CARD_TYPE_PREPAID);
+ }
+
+ // Let the user decide whether an unknown card type should be used.
+ out_supported_card_types_set->insert(autofill::CreditCard::CARD_TYPE_UNKNOWN);
+}
+
base::string16 GetFormattedPhoneNumberForDisplay(
const autofill::AutofillProfile& profile,
const std::string& locale) {
@@ -191,6 +243,26 @@ std::string FormatPhoneForResponse(const std::string& phone_number,
PhoneNumberUtil::PhoneNumberFormat::E164);
}
+base::string16 FormatCardNumberForDisplay(const base::string16& card_number) {
+ base::string16 number = autofill::CreditCard::StripSeparators(card_number);
+ if (number.empty() || !base::IsAsciiDigit(number[0]))
+ return card_number;
+
+ std::vector<size_t> positions = {4U, 9U, 14U};
+ if (autofill::CreditCard::GetCardNetwork(number) ==
+ autofill::kAmericanExpressCard) {
+ positions = {4U, 11U};
+ }
+
+ static const base::char16 kSeparator = base::ASCIIToUTF16(" ")[0];
+ for (size_t i : positions) {
+ if (number.size() > i)
+ number.insert(i, 1U, kSeparator);
+ }
+
+ return number;
+}
+
std::string GetCountryCodeWithFallback(const autofill::AutofillProfile* profile,
const std::string& app_locale) {
std::string country_code =
diff --git a/chromium/components/payments/core/payment_request_data_util.h b/chromium/components/payments/core/payment_request_data_util.h
index c301946d5e5..7ede39a384a 100644
--- a/chromium/components/payments/core/payment_request_data_util.h
+++ b/chromium/components/payments/core/payment_request_data_util.h
@@ -10,10 +10,10 @@
#include <vector>
#include "base/strings/string16.h"
+#include "components/autofill/core/browser/credit_card.h"
namespace autofill {
class AutofillProfile;
-class CreditCard;
} // namespace autofill
namespace payments {
@@ -38,8 +38,10 @@ BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
const autofill::AutofillProfile& billing_profile,
const std::string& app_locale);
-// Parse the supported card networks from supportedMethods and "basic-card"'s
-// supportedNetworks. |out_supported_networks| is filled with list of networks
+// Parse all the supported payment methods from the merchant including 1) the
+// supported card networks from supportedMethods and "basic-card"'s
+// supportedNetworks and 2) the url-based payment method identifiers.
+// |out_supported_networks| is filled with a list of networks
// in the order that they were specified by the merchant.
// |out_basic_card_supported_networks| is a subset of |out_supported_networks|
// that includes all networks that were specified as part of "basic-card". This
@@ -47,10 +49,21 @@ BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
// "basic-card" in the PaymentResponse. |method_data.supported_networks| is
// expected to only contain basic-card card network names (the list is at
// https://www.w3.org/Payments/card-network-ids).
-void ParseBasicCardSupportedNetworks(
+// |out_url_payment_method_identifiers| is filled with a list of all the
+// payment method identifiers specicied by the merchant that are URL-based.
+void ParseSupportedMethods(
const std::vector<PaymentMethodData>& method_data,
std::vector<std::string>* out_supported_networks,
- std::set<std::string>* out_basic_card_supported_networks);
+ std::set<std::string>* out_basic_card_supported_networks,
+ std::vector<std::string>* out_url_payment_method_identifiers);
+
+// Parses the supported card types (e.g., credit, debit, prepaid) from
+// supportedTypes. |out_supported_card_types_set| is expected to be empty. It
+// will always contain autofill::CreditCard::CARD_TYPE_UNKNOWN after the call.
+// Also, it gets filled with all of the card types if supportedTypes is empty.
+void ParseSupportedCardTypes(
+ const std::vector<PaymentMethodData>& method_data,
+ std::set<autofill::CreditCard::CardType>* out_supported_card_types_set);
// Returns the phone number from the given |profile| formatted for display.
base::string16 GetFormattedPhoneNumberForDisplay(
@@ -71,6 +84,11 @@ std::string FormatPhoneForDisplay(const std::string& phone_number,
std::string FormatPhoneForResponse(const std::string& phone_number,
const std::string& country_code);
+// Formats |card_number| for display. For example, "4111111111111111" is
+// formatted into "4111 1111 1111 1111". This method does not format masked card
+// numbers, which start with a letter.
+base::string16 FormatCardNumberForDisplay(const base::string16& card_number);
+
// Returns a country code to be used when validating this profile. If the
// profile has a valid country code set, it is returned. If not, a country code
// associated with |app_locale| is used as a fallback.
diff --git a/chromium/components/payments/core/payment_request_data_util_unittest.cc b/chromium/components/payments/core/payment_request_data_util_unittest.cc
index 3181a47d282..b815c0738e6 100644
--- a/chromium/components/payments/core/payment_request_data_util_unittest.cc
+++ b/chromium/components/payments/core/payment_request_data_util_unittest.cc
@@ -34,11 +34,14 @@ TEST(PaymentRequestDataUtilTest, GetPaymentAddressFromAutofillProfile) {
"{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
"\"city\":\"Elysium\","
"\"country\":\"US\","
+ "\"dependentLocality\":\"\","
+ "\"languageCode\":\"\","
"\"organization\":\"Underworld\","
"\"phone\":\"16502111111\","
"\"postalCode\":\"91111\","
"\"recipient\":\"John H. Doe\","
- "\"region\":\"CA\"}",
+ "\"region\":\"CA\","
+ "\"sortingCode\":\"\"}",
json_address);
}
@@ -59,11 +62,14 @@ TEST(PaymentRequestDataUtilTest, GetBasicCardResponseFromAutofillCreditCard) {
"{\"addressLine\":[\"666 Erebus St.\",\"Apt 8\"],"
"\"city\":\"Elysium\","
"\"country\":\"US\","
+ "\"dependentLocality\":\"\","
+ "\"languageCode\":\"\","
"\"organization\":\"Underworld\","
"\"phone\":\"16502111111\","
"\"postalCode\":\"91111\","
"\"recipient\":\"John H. Doe\","
- "\"region\":\"CA\"},"
+ "\"region\":\"CA\","
+ "\"sortingCode\":\"\"},"
"\"cardNumber\":\"4111111111111111\","
"\"cardSecurityCode\":\"123\","
"\"cardholderName\":\"Test User\","
diff --git a/chromium/components/payments/core/payment_request_delegate.h b/chromium/components/payments/core/payment_request_delegate.h
index 53a44c70626..dd5057554d6 100644
--- a/chromium/components/payments/core/payment_request_delegate.h
+++ b/chromium/components/payments/core/payment_request_delegate.h
@@ -5,35 +5,14 @@
#ifndef COMPONENTS_PAYMENTS_CORE_PAYMENT_REQUEST_DELEGATE_H_
#define COMPONENTS_PAYMENTS_CORE_PAYMENT_REQUEST_DELEGATE_H_
-#include <memory>
-#include <string>
-
-#include "base/memory/weak_ptr.h"
-#include "components/autofill/core/browser/payments/full_card_request.h"
-
-class GURL;
-
-namespace autofill {
-class CreditCard;
-class PersonalDataManager;
-class RegionDataLoader;
-} // namespace autofill
-
-class PrefService;
-
-namespace ukm {
-class UkmRecorder;
-} // namespace ukm
+#include "components/payments/core/payment_request_base_delegate.h"
namespace payments {
-class AddressNormalizer;
class PaymentRequest;
-class PaymentRequestDelegate {
+class PaymentRequestDelegate : public PaymentRequestBaseDelegate {
public:
- virtual ~PaymentRequestDelegate() {}
-
// Shows the Payment Request dialog for the given |request|.
virtual void ShowDialog(PaymentRequest* request) = 0;
@@ -44,46 +23,6 @@ class PaymentRequestDelegate {
// Disables the dialog and shows an error message that the transaction has
// failed.
virtual void ShowErrorMessage() = 0;
-
- // Gets the PersonalDataManager associated with this PaymentRequest flow.
- // Cannot be null.
- virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
-
- virtual const std::string& GetApplicationLocale() const = 0;
-
- // Returns whether the user is in Incognito mode.
- virtual bool IsIncognito() const = 0;
-
- // Returns true if the SSL certificate is valid. Should be called only for
- // cryptographic schemes.
- virtual bool IsSslCertificateValid() = 0;
-
- // Returns the URL of the page that is currently being displayed.
- virtual const GURL& GetLastCommittedURL() const = 0;
-
- // Starts a FullCardRequest to unmask |credit_card|.
- virtual void DoFullCardRequest(
- const autofill::CreditCard& credit_card,
- base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
- result_delegate) = 0;
-
- // Returns a pointer to the address normalizer to use for the duration of this
- // Payment Request.
- virtual AddressNormalizer* GetAddressNormalizer() = 0;
-
- // Creates a new region data loader that will self delete, or a test mock.
- virtual autofill::RegionDataLoader* GetRegionDataLoader() = 0;
-
- // Returns a pointer to the UKM service.
- virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
-
- // Returns the user's signed-in email address, or empty string if not signed
- // in.
- virtual std::string GetAuthenticatedEmail() const = 0;
-
- // Gets the pref service for the browser context associated with this
- // PaymentRequest.
- virtual PrefService* GetPrefService() = 0;
};
} // namespace payments
diff --git a/chromium/components/payments/core/payments_profile_comparator.cc b/chromium/components/payments/core/payments_profile_comparator.cc
index 3190bacf2a9..faeab2d7f10 100644
--- a/chromium/components/payments/core/payments_profile_comparator.cc
+++ b/chromium/components/payments/core/payments_profile_comparator.cc
@@ -56,8 +56,10 @@ PaymentsProfileComparator::FilterProfilesForContact(
// Stable sort, since profiles are expected to be passed in frecency order.
std::stable_sort(
processed.begin(), processed.end(),
- std::bind(&PaymentsProfileComparator::IsContactMoreComplete, this,
- std::placeholders::_1, std::placeholders::_2));
+ [this](autofill::AutofillProfile* p1, autofill::AutofillProfile* p2) {
+ return GetContactCompletenessScore(p1) >
+ GetContactCompletenessScore(p2);
+ });
auto it = processed.begin();
while (it != processed.end()) {
@@ -149,8 +151,10 @@ PaymentsProfileComparator::FilterProfilesForShipping(
std::stable_sort(
processed.begin(), processed.end(),
- std::bind(&PaymentsProfileComparator::IsShippingMoreComplete, this,
- std::placeholders::_1, std::placeholders::_2));
+ [this](autofill::AutofillProfile* p1, autofill::AutofillProfile* p2) {
+ return GetShippingCompletenessScore(p1) >
+ GetShippingCompletenessScore(p2);
+ });
// TODO(crbug.com/722949): Remove profiles with no relevant information, or
// which are subsets of more-complete profiles.
@@ -271,16 +275,4 @@ bool PaymentsProfileComparator::AreRequiredAddressFieldsPresent(
return autofill::addressinput::HasAllRequiredFields(*data);
}
-bool PaymentsProfileComparator::IsContactMoreComplete(
- const autofill::AutofillProfile* p1,
- const autofill::AutofillProfile* p2) const {
- return GetContactCompletenessScore(p1) > GetContactCompletenessScore(p2);
-}
-
-bool PaymentsProfileComparator::IsShippingMoreComplete(
- const autofill::AutofillProfile* p1,
- const autofill::AutofillProfile* p2) const {
- return GetShippingCompletenessScore(p1) > GetShippingCompletenessScore(p2);
-}
-
} // namespace payments
diff --git a/chromium/components/payments/core/payments_profile_comparator.h b/chromium/components/payments/core/payments_profile_comparator.h
index e186e05efad..61e76092bb7 100644
--- a/chromium/components/payments/core/payments_profile_comparator.h
+++ b/chromium/components/payments/core/payments_profile_comparator.h
@@ -97,7 +97,7 @@ class PaymentsProfileComparator : public autofill::AutofillProfileComparator {
// Clears the cached evaluation result for |profile|. Must be called when a
// profile is modified and saved during the course of a PaymentRequest.
- void Invalidate(const autofill::AutofillProfile& profile);
+ virtual void Invalidate(const autofill::AutofillProfile& profile);
private:
ProfileFields ComputeMissingFields(
@@ -108,17 +108,10 @@ class PaymentsProfileComparator : public autofill::AutofillProfileComparator {
bool AreRequiredAddressFieldsPresent(
const autofill::AutofillProfile& profile) const;
- // Comparison functions suitable for sorting profiles by completeness
- // score with std::sort.
- bool IsContactMoreComplete(const autofill::AutofillProfile* p1,
- const autofill::AutofillProfile* p2) const;
- bool IsShippingMoreComplete(const autofill::AutofillProfile* p1,
- const autofill::AutofillProfile* p2) const;
-
mutable std::map<std::string, ProfileFields> cache_;
const PaymentOptionsProvider& options_;
};
} // namespace payments
-#endif // COMPONENTS_PAYMENTS_CORE_PAYMENTS_PROFILE_COMPARATOR_H_ \ No newline at end of file
+#endif // COMPONENTS_PAYMENTS_CORE_PAYMENTS_PROFILE_COMPARATOR_H_
diff --git a/chromium/components/payments/core/payments_test_util.cc b/chromium/components/payments/core/payments_test_util.cc
new file mode 100644
index 00000000000..9ee4770c808
--- /dev/null
+++ b/chromium/components/payments/core/payments_test_util.cc
@@ -0,0 +1,24 @@
+// 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/payments/core/payments_test_util.h"
+
+#include "base/memory/ref_counted.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/payments/core/payment_prefs.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+
+namespace payments {
+namespace test {
+
+std::unique_ptr<PrefService> PrefServiceForTesting() {
+ scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
+ new user_prefs::PrefRegistrySyncable());
+ ::payments::RegisterProfilePrefs(registry.get());
+ return ::autofill::test::PrefServiceForTesting(registry.get());
+}
+
+} // namespace test
+} // namespace payments
diff --git a/chromium/components/payments/core/payments_test_util.h b/chromium/components/payments/core/payments_test_util.h
new file mode 100644
index 00000000000..811b3956e09
--- /dev/null
+++ b/chromium/components/payments/core/payments_test_util.h
@@ -0,0 +1,27 @@
+// 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_PAYMENTS_CORE_PAYMENTS_TEST_UTIL_H_
+#define COMPONENTS_PAYMENTS_CORE_PAYMENTS_TEST_UTIL_H_
+
+#include <memory>
+
+class PrefService;
+
+namespace payments {
+
+// Common utilities shared amongst Payments tests.
+namespace test {
+
+// Return a PrefService that can be used for Payments-related testing in
+// contexts where the PrefService would otherwise have to be constructed
+// manually (e.g., in unit tests within Autofill core code). The returned
+// PrefService has had Autofill preferences registered on its associated
+// registry.
+std::unique_ptr<PrefService> PrefServiceForTesting();
+
+} // namespace test
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_PAYMENTS_TEST_UTIL_H_
diff --git a/chromium/components/payments/core/strings_util.cc b/chromium/components/payments/core/strings_util.cc
index 165d2b225b7..bc5fad06c2b 100644
--- a/chromium/components/payments/core/strings_util.cc
+++ b/chromium/components/payments/core/strings_util.cc
@@ -13,6 +13,27 @@
#include "ui/base/l10n/l10n_util.h"
namespace payments {
+namespace {
+
+constexpr size_t kNone = 0;
+constexpr size_t kCredit = 1;
+constexpr size_t kDebit = 2;
+constexpr size_t kPrepaid = 4;
+
+size_t getCardTypeBitmask(
+ const std::set<autofill::CreditCard::CardType>& types) {
+ return (types.find(autofill::CreditCard::CARD_TYPE_CREDIT) != types.end()
+ ? kCredit
+ : kNone) |
+ (types.find(autofill::CreditCard::CARD_TYPE_DEBIT) != types.end()
+ ? kDebit
+ : kNone) |
+ (types.find(autofill::CreditCard::CARD_TYPE_PREPAID) != types.end()
+ ? kPrepaid
+ : kNone);
+}
+
+} // namespace
base::string16 GetShippingAddressLabelFormAutofillProfile(
const autofill::AutofillProfile& profile,
@@ -99,4 +120,47 @@ base::string16 GetShippingOptionSectionString(
}
}
+base::string16 GetAcceptedCardTypesText(
+ const std::set<autofill::CreditCard::CardType>& types) {
+ int string_ids[8];
+
+ string_ids[kNone] = IDS_PAYMENTS_ACCEPTED_CARDS_LABEL;
+ string_ids[kCredit | kDebit | kPrepaid] = IDS_PAYMENTS_ACCEPTED_CARDS_LABEL;
+
+ string_ids[kCredit] = IDS_PAYMENTS_ACCEPTED_CREDIT_CARDS_LABEL;
+ string_ids[kDebit] = IDS_PAYMENTS_ACCEPTED_DEBIT_CARDS_LABEL;
+ string_ids[kPrepaid] = IDS_PAYMENTS_ACCEPTED_PREPAID_CARDS_LABEL;
+
+ string_ids[kCredit | kDebit] = IDS_PAYMENTS_ACCEPTED_CREDIT_DEBIT_CARDS_LABEL;
+ string_ids[kCredit | kPrepaid] =
+ IDS_PAYMENTS_ACCEPTED_CREDIT_PREPAID_CARDS_LABEL;
+ string_ids[kDebit | kPrepaid] =
+ IDS_PAYMENTS_ACCEPTED_DEBIT_PREPAID_CARDS_LABEL;
+
+ return l10n_util::GetStringUTF16(string_ids[getCardTypeBitmask(types)]);
+}
+
+base::string16 GetCardTypesAreAcceptedText(
+ const std::set<autofill::CreditCard::CardType>& types) {
+ int string_ids[8];
+
+ string_ids[kNone] = 0;
+ string_ids[kCredit | kDebit | kPrepaid] = 0;
+
+ string_ids[kCredit] = IDS_PAYMENTS_CREDIT_CARDS_ARE_ACCEPTED_LABEL;
+ string_ids[kDebit] = IDS_PAYMENTS_DEBIT_CARDS_ARE_ACCEPTED_LABEL;
+ string_ids[kPrepaid] = IDS_PAYMENTS_PREPAID_CARDS_ARE_ACCEPTED_LABEL;
+
+ string_ids[kCredit | kDebit] =
+ IDS_PAYMENTS_CREDIT_DEBIT_CARDS_ARE_ACCEPTED_LABEL;
+ string_ids[kCredit | kPrepaid] =
+ IDS_PAYMENTS_CREDIT_PREPAID_CARDS_ARE_ACCEPTED_LABEL;
+ string_ids[kDebit | kPrepaid] =
+ IDS_PAYMENTS_DEBIT_PREPAID_CARDS_ARE_ACCEPTED_LABEL;
+
+ int string_id = string_ids[getCardTypeBitmask(types)];
+ return string_id == 0 ? base::string16()
+ : l10n_util::GetStringUTF16(string_id);
+}
+
} // namespace payments
diff --git a/chromium/components/payments/core/strings_util.h b/chromium/components/payments/core/strings_util.h
index c693d29fe6d..db084f40bb5 100644
--- a/chromium/components/payments/core/strings_util.h
+++ b/chromium/components/payments/core/strings_util.h
@@ -5,9 +5,11 @@
#ifndef COMPONENTS_PAYMENTS_CORE_STRINGS_UTIL_H_
#define COMPONENTS_PAYMENTS_CORE_STRINGS_UTIL_H_
+#include <set>
#include <string>
#include "base/strings/string16.h"
+#include "components/autofill/core/browser/credit_card.h"
#include "components/payments/core/payment_options_provider.h"
namespace autofill {
@@ -41,6 +43,19 @@ base::string16 GetShippingAddressSectionString(
base::string16 GetShippingOptionSectionString(
PaymentShippingType shipping_type);
+// Returns the label "Accepted cards" that is customized based on the
+// accepted card |types|. For example, "Accepted debit cards". If |types| is
+// empty or contains all possible values, then returns the generic "Accepted
+// cards" string.
+base::string16 GetAcceptedCardTypesText(
+ const std::set<autofill::CreditCard::CardType>& types);
+
+// Returns the label "Cards are accepted" that is customized based on the
+// accepted card |types|. For example, "Debit cards are accepted". If |types| is
+// empty or contains all possible values, then returns an empty string.
+base::string16 GetCardTypesAreAcceptedText(
+ const std::set<autofill::CreditCard::CardType>& types);
+
} // namespace payments
#endif // COMPONENTS_PAYMENTS_CORE_STRINGS_UTIL_H_
diff --git a/chromium/components/payments/core/strings_util_unittest.cc b/chromium/components/payments/core/strings_util_unittest.cc
new file mode 100644
index 00000000000..7aebf44e077
--- /dev/null
+++ b/chromium/components/payments/core/strings_util_unittest.cc
@@ -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.
+
+#include "components/payments/core/strings_util.h"
+
+#include <string>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments {
+namespace {
+
+using CardType = ::autofill::CreditCard::CardType;
+
+constexpr CardType CREDIT = ::autofill::CreditCard::CARD_TYPE_CREDIT;
+constexpr CardType DEBIT = ::autofill::CreditCard::CARD_TYPE_DEBIT;
+constexpr CardType PREPAID = ::autofill::CreditCard::CARD_TYPE_PREPAID;
+constexpr CardType UNKNOWN = ::autofill::CreditCard::CARD_TYPE_UNKNOWN;
+
+} // namespace
+
+TEST(StringsUtilTest, GetAcceptedCardTypesText) {
+ static const struct {
+ std::vector<CardType> card_types;
+ const char* const expected_text;
+ } kTestCases[] = {
+ {std::vector<CardType>(), "Accepted cards"},
+ {{UNKNOWN}, "Accepted cards"},
+ {{CREDIT}, "Accepted credit cards"},
+ {{DEBIT}, "Accepted debit cards"},
+ {{PREPAID}, "Accepted prepaid cards"},
+ {{CREDIT, DEBIT}, "Accepted credit and debit cards"},
+ {{CREDIT, PREPAID}, "Accepted credit and prepaid cards"},
+ {{DEBIT, PREPAID}, "Accepted debit and prepaid cards"},
+ {{CREDIT, DEBIT, PREPAID}, "Accepted cards"},
+ };
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ EXPECT_EQ(
+ base::UTF8ToUTF16(kTestCases[i].expected_text),
+ GetAcceptedCardTypesText(std::set<CardType>(
+ kTestCases[i].card_types.begin(), kTestCases[i].card_types.end())));
+ }
+}
+
+TEST(StringsUtilTest, GetCardTypesAreAcceptedText) {
+ static const struct {
+ std::vector<CardType> card_types;
+ const char* const expected_text;
+ } kTestCases[] = {
+ {std::vector<CardType>(), ""},
+ {{UNKNOWN}, ""},
+ {{CREDIT}, "Credit cards are accepted."},
+ {{DEBIT}, "Debit cards are accepted."},
+ {{PREPAID}, "Prepaid cards are accepted."},
+ {{CREDIT, DEBIT}, "Credit and debit cards are accepted."},
+ {{CREDIT, PREPAID}, "Credit and prepaid cards are accepted."},
+ {{DEBIT, PREPAID}, "Debit and prepaid cards are accepted."},
+ {{CREDIT, DEBIT, PREPAID}, ""},
+ };
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ EXPECT_EQ(
+ base::UTF8ToUTF16(kTestCases[i].expected_text),
+ GetCardTypesAreAcceptedText(std::set<CardType>(
+ kTestCases[i].card_types.begin(), kTestCases[i].card_types.end())));
+ }
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/subkey_requester.cc b/chromium/components/payments/core/subkey_requester.cc
index 513fc54ffc4..ae0bca05b61 100644
--- a/chromium/components/payments/core/subkey_requester.cc
+++ b/chromium/components/payments/core/subkey_requester.cc
@@ -29,10 +29,12 @@ class SubKeyRequest : public SubKeyRequester::Request {
public:
// The |delegate| and |address_validator| need to outlive this Request.
SubKeyRequest(const std::string& region_code,
+ const std::string& language,
int timeout_seconds,
autofill::AddressValidator* address_validator,
SubKeyReceiverCallback on_subkeys_received)
: region_code_(region_code),
+ language_(language),
address_validator_(address_validator),
on_subkeys_received_(std::move(on_subkeys_received)),
has_responded_(false),
@@ -43,7 +45,7 @@ class SubKeyRequest : public SubKeyRequester::Request {
base::TimeDelta::FromSeconds(timeout_seconds));
}
- ~SubKeyRequest() override {}
+ ~SubKeyRequest() override { on_timeout_.Cancel(); }
void OnRulesLoaded() override {
on_timeout_.Cancel();
@@ -52,12 +54,20 @@ class SubKeyRequest : public SubKeyRequester::Request {
return;
has_responded_ = true;
- std::move(on_subkeys_received_)
- .Run(address_validator_->GetRegionSubKeys(region_code_));
+ auto subkeys =
+ address_validator_->GetRegionSubKeys(region_code_, language_);
+ std::vector<std::string> subkeys_codes;
+ std::vector<std::string> subkeys_names;
+ for (auto s : subkeys) {
+ subkeys_codes.push_back(s.first);
+ subkeys_names.push_back(s.second);
+ }
+ std::move(on_subkeys_received_).Run(subkeys_codes, subkeys_names);
}
private:
std::string region_code_;
+ std::string language_;
// Not owned. Never null. Outlive this object.
autofill::AddressValidator* address_validator_;
@@ -78,12 +88,14 @@ SubKeyRequester::SubKeyRequester(std::unique_ptr<Source> source,
SubKeyRequester::~SubKeyRequester() {}
void SubKeyRequester::StartRegionSubKeysRequest(const std::string& region_code,
+ const std::string& language,
int timeout_seconds,
SubKeyReceiverCallback cb) {
DCHECK(timeout_seconds >= 0);
- std::unique_ptr<SubKeyRequest> request(base::MakeUnique<SubKeyRequest>(
- region_code, timeout_seconds, &address_validator_, std::move(cb)));
+ std::unique_ptr<SubKeyRequest> request(
+ base::MakeUnique<SubKeyRequest>(region_code, language, timeout_seconds,
+ &address_validator_, std::move(cb)));
if (AreRulesLoadedForRegion(region_code)) {
request->OnRulesLoaded();
@@ -113,10 +125,11 @@ void SubKeyRequester::OnAddressValidationRulesLoaded(
// The case for |success| == false is already handled. if |success| == false,
// AddressValidator::GetRegionSubKeys will return an empty list of subkeys.
// Therefore, here, we can ignore the value of |success|.
-
// Check if there is any subkey request for that region code.
- if (!pending_subkey_region_code_.compare(region_code))
+ if (pending_subkey_request_ &&
+ !pending_subkey_region_code_.compare(region_code)) {
pending_subkey_request_->OnRulesLoaded();
+ }
pending_subkey_region_code_.clear();
pending_subkey_request_.reset();
}
diff --git a/chromium/components/payments/core/subkey_requester.h b/chromium/components/payments/core/subkey_requester.h
index 1813781d5b4..a7d597fb3ea 100644
--- a/chromium/components/payments/core/subkey_requester.h
+++ b/chromium/components/payments/core/subkey_requester.h
@@ -10,8 +10,10 @@
namespace payments {
+// This receives a region code and the device's language.
using SubKeyReceiverCallback =
- base::OnceCallback<void(const std::vector<std::string>&)>;
+ base::OnceCallback<void(const std::vector<std::string>&,
+ const std::vector<std::string>&)>;
// SubKeyRequester Loads Rules from the server and extracts the subkeys.
// For a given key (region code for a country, such as US), the list of its
@@ -37,8 +39,9 @@ class SubKeyRequester : public autofill::LoadRulesListener {
// |region_code|. The received subkeys will be returned to the |requester|. If
// the subkeys are not received in |timeout_seconds|, then the requester will
// be informed and the request will be canceled. |requester| should never be
- // null.
+ // null. The requesting device language is set to |language|, ex:"fr".
void StartRegionSubKeysRequest(const std::string& region_code,
+ const std::string& language,
int timeout_seconds,
SubKeyReceiverCallback cb);
diff --git a/chromium/components/payments/core/subkey_requester_unittest.cc b/chromium/components/payments/core/subkey_requester_unittest.cc
index f679c16a6b0..474700c76df 100644
--- a/chromium/components/payments/core/subkey_requester_unittest.cc
+++ b/chromium/components/payments/core/subkey_requester_unittest.cc
@@ -25,6 +25,7 @@ using ::i18n::addressinput::Storage;
using ::i18n::addressinput::TestdataSource;
const char kLocale[] = "OZ";
+const char kLanguage[] = "en";
const int kInvalidSize = -1;
const int kCorrectSize = 2; // for subkeys = Do, Re
const int kEmptySize = 0;
@@ -33,8 +34,9 @@ class SubKeyReceiver : public base::RefCountedThreadSafe<SubKeyReceiver> {
public:
SubKeyReceiver() : subkeys_size_(kInvalidSize) {}
- void OnSubKeysReceived(const std::vector<std::string>& subkeys) {
- subkeys_size_ = subkeys.size();
+ void OnSubKeysReceived(const std::vector<std::string>& subkeys_codes,
+ const std::vector<std::string>& subkeys_names) {
+ subkeys_size_ = subkeys_codes.size();
}
int subkeys_size() const { return subkeys_size_; }
@@ -62,7 +64,13 @@ class ChromiumTestdataSource : public TestdataSource {
new std::string(
"{\"data/OZ\": "
"{\"id\":\"data/OZ\",\"key\":\"OZ\",\"name\":\"Oz \", "
- "\"lang\":\"en\",\"languages\":\"en\",\"sub_keys\":\"DO~Re\"}}"));
+ "\"lang\":\"en\",\"sub_keys\":\"DO~RE\", \"sub_names\":\"Do~Re\"},"
+ "\"data/OZ/DO\": "
+ "{\"id\":\"data/OZ/DO\",\"key\":\"DO\",\"name\":\"Do \", "
+ "\"lang\":\"en\"},"
+ "\"data/OZ/RE\": "
+ "{\"id\":\"data/OZ/RE\",\"key\":\"RE\",\"name\":\"Re \", "
+ "\"lang\":\"en\"}}"));
}
private:
@@ -138,7 +146,7 @@ TEST_F(SubKeyRequesterTest, StartRequest_RulesLoaded) {
EXPECT_TRUE(requester_->AreRulesLoadedForRegion(kLocale));
// Start the request.
- requester_->StartRegionSubKeysRequest(kLocale, 0, std::move(cb));
+ requester_->StartRegionSubKeysRequest(kLocale, kLanguage, 0, std::move(cb));
// Since the rules are already loaded, the subkeys should be received
// synchronously.
@@ -159,7 +167,7 @@ TEST_F(SubKeyRequesterTest, StartRequest_RulesNotLoaded_WillNotLoad) {
requester_->ShouldLoadRules(false);
// Start the normalization.
- requester_->StartRegionSubKeysRequest(kLocale, 0, std::move(cb));
+ requester_->StartRegionSubKeysRequest(kLocale, kLanguage, 0, std::move(cb));
// Let the timeout execute.
base::RunLoop().RunUntilIdle();
@@ -181,7 +189,7 @@ TEST_F(SubKeyRequesterTest, StartRequest_RulesNotLoaded_WillLoad) {
// call.
requester_->ShouldLoadRules(true);
// Start the request.
- requester_->StartRegionSubKeysRequest(kLocale, 0, std::move(cb));
+ requester_->StartRegionSubKeysRequest(kLocale, kLanguage, 0, std::move(cb));
// Even if the rules are not loaded before the call to
// StartRegionSubKeysRequest, they should get loaded in the call. Since our
diff --git a/chromium/components/payments/mojom/BUILD.gn b/chromium/components/payments/mojom/BUILD.gn
index 9268a078a11..740c592fba9 100644
--- a/chromium/components/payments/mojom/BUILD.gn
+++ b/chromium/components/payments/mojom/BUILD.gn
@@ -4,12 +4,6 @@
import("//mojo/public/tools/bindings/mojom.gni")
-mojom("mojom") {
- sources = [
- "payment_request.mojom",
- ]
-}
-
mojom("mojom_parser") {
sources = [
"payment_manifest_parser.mojom",
@@ -17,17 +11,6 @@ mojom("mojom_parser") {
public_deps = [
"//url/mojo:url_mojom_gurl",
- ]
-}
-
-mojom("mojom_payment_app") {
- sources = [
- "payment_app.mojom",
- ]
-
- public_deps = [
- ":mojom",
- "//mojo/common:common_custom_types",
- "//url/mojo:url_mojom_gurl",
+ "//url/mojo:url_mojom_origin",
]
}
diff --git a/chromium/components/payments/mojom/OWNERS b/chromium/components/payments/mojom/OWNERS
index 82559c92e72..503dfd23d24 100644
--- a/chromium/components/payments/mojom/OWNERS
+++ b/chromium/components/payments/mojom/OWNERS
@@ -1,4 +1,4 @@
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
-# COMPONENT: UI>Browser>Autofill>Payments
+# COMPONENT: UI>Browser>Payments
diff --git a/chromium/components/payments/mojom/payment_app.mojom b/chromium/components/payments/mojom/payment_app.mojom
deleted file mode 100644
index dbf1a0f4694..00000000000
--- a/chromium/components/payments/mojom/payment_app.mojom
+++ /dev/null
@@ -1,61 +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.
-
-module payments.mojom;
-
-import "components/payments/mojom/payment_request.mojom";
-import "mojo/common/time.mojom";
-import "url/mojo/url.mojom";
-
-enum PaymentHandlerStatus {
- SUCCESS,
- NOT_IMPLEMENTED,
- NOT_FOUND,
- NO_ACTIVE_WORKER,
- STORAGE_OPERATION_FAILED,
-};
-
-struct PaymentInstrument {
- string name;
- array<string> enabled_methods;
- string stringified_capabilities;
-};
-
-interface PaymentManager {
- Init(string service_worker_scope);
- DeletePaymentInstrument(string instrument_key)
- => (PaymentHandlerStatus status);
- GetPaymentInstrument(string instrument_key)
- => (PaymentInstrument instrument, PaymentHandlerStatus status);
- KeysOfPaymentInstruments()
- => (array<string> keys, PaymentHandlerStatus status);
- HasPaymentInstrument(string instrument_key)
- => (PaymentHandlerStatus status);
- SetPaymentInstrument(string instrument_key, PaymentInstrument instrument)
- => (PaymentHandlerStatus status);
- ClearPaymentInstruments()
- => (PaymentHandlerStatus status);
-};
-
-struct PaymentAppRequest {
- url.mojom.Url top_level_origin;
- url.mojom.Url payment_request_origin;
- string payment_request_id;
- array<PaymentMethodData> method_data;
- PaymentItem total;
- array<PaymentDetailsModifier> modifiers;
- string instrument_key;
-};
-
-struct PaymentAppResponse {
- string method_name;
- string stringified_details;
-};
-
-// This interface is provided to pass a payment app response from payment
-// request event in renderer side to browser side by calling respondWith().
-interface PaymentAppResponseCallback {
- OnPaymentAppResponse(PaymentAppResponse response,
- mojo.common.mojom.Time dispatch_event_time);
-};
diff --git a/chromium/components/payments/mojom/payment_manifest_parser.mojom b/chromium/components/payments/mojom/payment_manifest_parser.mojom
index 96c3d3cb6d3..b99828e8733 100644
--- a/chromium/components/payments/mojom/payment_manifest_parser.mojom
+++ b/chromium/components/payments/mojom/payment_manifest_parser.mojom
@@ -5,6 +5,7 @@
[JavaPackage="org.chromium.payments.mojom"]
module payments.mojom;
+import "url/mojo/origin.mojom";
import "url/mojo/url.mojom";
struct WebAppManifestSection {
@@ -19,7 +20,9 @@ struct WebAppManifestSection {
interface PaymentManifestParser {
ParsePaymentMethodManifest(string content)
- => (array<url.mojom.Url> webAppManifestUrls);
+ => (array<url.mojom.Url> web_app_manifest_urls,
+ array<url.mojom.Origin> supported_origins,
+ bool all_origins_supported);
ParseWebAppManifest(string content)
=> (array<WebAppManifestSection> manifest);
};
diff --git a/chromium/components/payments/mojom/payment_request.mojom b/chromium/components/payments/mojom/payment_request.mojom
deleted file mode 100644
index 72c3dac791b..00000000000
--- a/chromium/components/payments/mojom/payment_request.mojom
+++ /dev/null
@@ -1,234 +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.
-
-[JavaPackage="org.chromium.payments.mojom"]
-module payments.mojom;
-
-// The shipping address that the browser process provides to the renderer
-// process. Built either by the browser or a payment app.
-struct PaymentAddress {
- // ISO 3166 country code. Two upper case ASCII letters.
- string country;
-
- array<string> address_line;
- string region;
- string city;
- string dependent_locality;
- string postal_code;
- string sorting_code;
-
- // Optional shortest ISO 639 language code. Two or three lower case ASCII
- // letters.
- string language_code;
-
- // Optional ISO 15924 script code. Four ASCII letters. The first letter is
- // upper case; the rest are lower case.
- string script_code;
-
- string organization;
- string recipient;
- string phone;
-};
-
-// The currency amount that the renderer provides to the browser process. The
-// browser shows the amount in UI and forwards it on to the payment app, if it
-// requires the amount.
-struct PaymentCurrencyAmount {
- // The most common identifiers are three-letter alphabetic codes as defined
- // by [ISO4217] (for example, "USD" for US Dollars), however any string of at
- // most 2048 characters is considered valid.
- string currency;
-
- // ISO 20022 CurrencyAnd30Amount. Up to 30 total digits. Up to 10 fraction
- // digits. Separated by a dot.
- string value;
-
- // currency_system is a URL that indicates the currency system that the
- // currency identifier belongs to. By default, the value is
- // urn:iso:std:iso:4217 indicating that currency is defined by [ISO4217]
- // (for example, USD for US Dollars).
- string currency_system = "urn:iso:std:iso:4217";
-};
-
-struct PaymentResponse {
- string method_name;
-
- // Payment method specific JSON string that is built either by the browser or
- // a payment app, for example Android Pay. Browser ensures that the string can
- // be successfully parsed into base::JSONParser. Renderer parses this string
- // via v8::JSON::Parse() and hands off the result to the merchant website.
- // There's no one format for this object, so more specific types cannot be
- // used. A simple example:
- //
- // {"nameOnCard": "Jon Doe", "pan": "4111 1111 1111 1111"}
- string stringified_details;
-
- PaymentAddress? shipping_address;
- string? shipping_option;
- string? payer_name;
- string? payer_email;
- string? payer_phone;
-};
-
-enum PaymentErrorReason {
- UNKNOWN,
- USER_CANCEL,
- NOT_SUPPORTED
-};
-
-enum CanMakePaymentQueryResult {
- CAN_MAKE_PAYMENT,
- CANNOT_MAKE_PAYMENT,
- QUERY_QUOTA_EXCEEDED,
-
- // Used only on localhost and file:// schemes to warn web developer that the
- // query quota has exceeded, but Chrome is returning an answer anyway.
- WARNING_CAN_MAKE_PAYMENT,
- WARNING_CANNOT_MAKE_PAYMENT,
-};
-
-interface PaymentRequestClient {
- OnShippingAddressChange(PaymentAddress address);
- OnShippingOptionChange(string shipping_option_id);
- OnPaymentResponse(PaymentResponse response);
- OnError(PaymentErrorReason error);
- OnComplete();
- OnAbort(bool aborted_successfully);
- OnCanMakePayment(CanMakePaymentQueryResult result);
- WarnNoFavicon();
-};
-
-struct PaymentItem {
- string label;
- PaymentCurrencyAmount amount;
- bool pending;
-};
-
-struct PaymentShippingOption {
- string id;
- string label;
- PaymentCurrencyAmount amount;
- bool selected;
-};
-
-enum AndroidPayEnvironment {
- PRODUCTION,
- TEST
-};
-
-enum AndroidPayCardNetwork {
- AMEX,
- DISCOVER,
- MASTERCARD,
- VISA
-};
-
-enum AndroidPayTokenization {
- UNSPECIFIED,
- GATEWAY_TOKEN,
- NETWORK_TOKEN
-};
-
-struct AndroidPayTokenizationParameter {
- string? key;
- string? value;
-};
-
-enum BasicCardNetwork {
- AMEX,
- DINERS,
- DISCOVER,
- JCB,
- MASTERCARD,
- MIR,
- UNIONPAY,
- VISA
-};
-
-enum BasicCardType {
- CREDIT,
- DEBIT,
- PREPAID
-};
-
-struct PaymentMethodData {
- array<string> supported_methods;
-
- // A JSON string built by the renderer from a JavaScript object that the
- // merchant website provides. The renderer uses
- // blink::JSONObject::toJSONString() to generate this string. The browser does
- // not parse the string and passes it as-is directly to payment apps. There's
- // no one format for this object, so more specific types cannot be used. A
- // simple example:
- //
- // {"gateway": "stripe"}
- string stringified_data;
-
- // Android Pay specific method data is parsed in the renderer.
- // https://developers.google.com/web/fundamentals/getting-started/primers/payment-request/android-pay
- // TODO(rouslan): Stop parsing Android Pay data. http://crbug.com/620173
- AndroidPayEnvironment environment;
- string? merchant_name;
- string? merchant_id;
- array<AndroidPayCardNetwork> allowed_card_networks;
- AndroidPayTokenization tokenization_type;
- array<AndroidPayTokenizationParameter> parameters;
- // Value of 0 means the merchant did not specify or it was an invalid value.
- int32 min_google_play_services_version;
-
- // Basic card specific method data is parsed in the renderer.
- array<BasicCardNetwork> supported_networks;
- array<BasicCardType> supported_types;
-};
-
-struct PaymentDetailsModifier {
- PaymentItem? total;
- array<PaymentItem> additional_display_items;
- PaymentMethodData method_data;
-};
-
-struct PaymentDetails {
- PaymentItem? total;
- array<PaymentItem> display_items;
- array<PaymentShippingOption> shipping_options;
- array<PaymentDetailsModifier> modifiers;
- string error = "";
- // Identifier identifying the payment request, to be exposed
- // to payment apps. It is optional since this structure is used
- // by PaymentDetailsUpdate (next to PaymentDetailsInit) but
- // PaymentDetailsUpdate has no id.
- string? id;
-};
-
-enum PaymentShippingType {
- SHIPPING,
- DELIVERY,
- PICKUP
-};
-
-struct PaymentOptions {
- bool request_payer_name;
- bool request_payer_email;
- bool request_payer_phone;
- bool request_shipping;
- PaymentShippingType shipping_type;
-};
-
-enum PaymentComplete {
- SUCCESS,
- FAIL,
- UNKNOWN
-};
-
-interface PaymentRequest {
- Init(PaymentRequestClient client,
- array<PaymentMethodData> method_data,
- PaymentDetails details,
- PaymentOptions options);
- Show();
- UpdateWith(PaymentDetails details);
- Abort();
- Complete(PaymentComplete result);
- CanMakePayment();
-};
diff --git a/chromium/components/payments_strings.grdp b/chromium/components/payments_strings.grdp
index 974c7a760c4..f5b95551f8e 100644
--- a/chromium/components/payments_strings.grdp
+++ b/chromium/components/payments_strings.grdp
@@ -16,9 +16,45 @@
<message name="IDS_PAYMENTS_SAVE_CARD_TO_DEVICE_CHECKBOX" desc="The label for the checkbox that enables the user to save a credit card to their device, for example, on their phone." formatter_data="android_java">
Save this card to this device
</message>
- <message name="IDS_PAYMENTS_ACCEPTED_CARDS_LABEL" desc="The title for the section that displays the credit card types that the merchant accepts. Below the title, we show a row of icons indicating the accepted cards (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+ <message name="IDS_PAYMENTS_ACCEPTED_CARDS_LABEL" desc="The title for the section that displays the card networks that the merchant accepts. Below the title, we show a row of icons indicating the accepted networks (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
Accepted cards
</message>
+ <message name="IDS_PAYMENTS_ACCEPTED_CREDIT_CARDS_LABEL" desc="The title for the section that displays the credit card networks that the merchant accepts. Below the title, we show a row of icons indicating the accepted credit card networks (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+ Accepted credit cards
+ </message>
+ <message name="IDS_PAYMENTS_ACCEPTED_DEBIT_CARDS_LABEL" desc="The title for the section that displays the debit card networks that the merchant accepts. Below the title, we show a row of icons indicating the accepted debit card networks (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+ Accepted debit cards
+ </message>
+ <message name="IDS_PAYMENTS_ACCEPTED_PREPAID_CARDS_LABEL" desc="The title for the section that displays the prepaid card networks that the merchant accepts. Below the title, we show a row of icons indicating the accepted prepaid card networks (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+ Accepted prepaid cards
+ </message>
+ <message name="IDS_PAYMENTS_ACCEPTED_CREDIT_DEBIT_CARDS_LABEL" desc="The title for the section that displays the credit and debit card networks that the merchant accepts. Below the title, we show a row of icons indicating the accepted networks (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+ Accepted credit and debit cards
+ </message>
+ <message name="IDS_PAYMENTS_ACCEPTED_CREDIT_PREPAID_CARDS_LABEL" desc="The title for the section that displays the credit and prepaid card networks that the merchant accepts. Below the title, we show a row of icons indicating the accepted networks (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+ Accepted credit and prepaid cards
+ </message>
+ <message name="IDS_PAYMENTS_ACCEPTED_DEBIT_PREPAID_CARDS_LABEL" desc="The title for the section that displays the debit and prepaid card networks that the merchant accepts. Below the title, we show a row of icons indicating the accepted networks (Visa icon, Mastercard icon, etc.)." formatter_data="android_java">
+ Accepted debit and prepaid cards
+ </message>
+ <message name="IDS_PAYMENTS_CREDIT_CARDS_ARE_ACCEPTED_LABEL" desc="A message for the section that displays user's credit cards that the merchant accepts." formatter_data="android_java">
+ Credit cards are accepted.
+ </message>
+ <message name="IDS_PAYMENTS_DEBIT_CARDS_ARE_ACCEPTED_LABEL" desc="A message for the section that displays user's debit cards that the merchant accepts." formatter_data="android_java">
+ Debit cards are accepted.
+ </message>
+ <message name="IDS_PAYMENTS_PREPAID_CARDS_ARE_ACCEPTED_LABEL" desc="A message for the section that displays user's prepaid cards that the merchant accepts." formatter_data="android_java">
+ Prepaid cards are accepted.
+ </message>
+ <message name="IDS_PAYMENTS_CREDIT_DEBIT_CARDS_ARE_ACCEPTED_LABEL" desc="A message for the section that displays user's credit and debit cards that the merchant accepts." formatter_data="android_java">
+ Credit and debit cards are accepted.
+ </message>
+ <message name="IDS_PAYMENTS_CREDIT_PREPAID_CARDS_ARE_ACCEPTED_LABEL" desc="A message for the section that displays user's credit and prepaid cards that the merchant accepts." formatter_data="android_java">
+ Credit and prepaid cards are accepted.
+ </message>
+ <message name="IDS_PAYMENTS_DEBIT_PREPAID_CARDS_ARE_ACCEPTED_LABEL" desc="A message for the section that displays user's debit and prepaid cards that the merchant accepts." formatter_data="android_java">
+ Debit and prepaid cards are accepted.
+ </message>
<message name="IDS_PAYMENTS_METHOD_OF_PAYMENT_LABEL" desc="The title for the section that lets the user select the method of payment." formatter_data="android_java">
Payment method
</message>
@@ -134,11 +170,8 @@
<message name="IDS_PAYMENTS_NAME_ON_CARD" desc="Title of the field representing the full name of a credit card holder.">
Cardholder Name
</message>
- <message name="IDS_PAYMENTS_EXP_MONTH" desc="Title of the field representing the expiration month of a credit card.">
- Expiration Month
- </message>
- <message name="IDS_PAYMENTS_EXP_YEAR" desc="Title of the field representing the expiration year of a credit card.">
- Expiration Year
+ <message name="IDS_PAYMENTS_EXP_DATE" desc="Title of the field representing the expiration date of a credit card.">
+ Expiration Date
</message>
<message name="IDS_PAYMENTS_BILLING_ADDRESS" desc="Title of the field representing the billing address of a credit card.">
Billing Address
@@ -225,8 +258,8 @@
</message>
<message name="IDS_PAYMENT_REQUEST_ORDER_SUMMARY_MORE_ITEMS" desc="The label in the Order Summary section of the Payment Sheet that indicates how many display items are hidden. [ICU Syntax]">
{MORE_ITEMS, plural,
- =1 {<ph name="ITEM_COUNT">#<ex>1</ex></ph> item}
- other {<ph name="ITEM_COUNT">#<ex>2</ex></ph> items}}
+ =1 {<ph name="ITEM_COUNT">#<ex>1</ex></ph> more item}
+ other {<ph name="ITEM_COUNT">#<ex>2</ex></ph> more items}}
</message>
<message name="IDS_PAYMENT_REQUEST_SHIPPING_SECTION_NAME" desc="The name of the Shipping Address section in the Payment Sheet of the Payment Request dialog.">
Shipping address
@@ -314,23 +347,47 @@
<if expr="not is_android">
<message name="IDS_PAYMENT_REQUEST_PAYMENT_METHODS_PREVIEW" desc="This is a snippet of a payment method a user has saved to Chrome, plus an indication of the number of additional payment methods the user has saved. Its function is to show the user has payment methods that can be used to complete a payment, and thus doesn't have to type the entire payment method. [ICU Syntax]">
{PAYMENT_METHOD, plural,
+ =0 {<ph name="PAYMENT_METHOD_PREVIEW">{1}<ex>VISA ....1234</ex></ph>}
=1 {<ph name="PAYMENT_METHOD_PREVIEW">{1}<ex>VISA ....1234</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS">{2}<ex>1</ex></ph> more}
other {<ph name="PAYMENT_METHOD_PREVIEW">{1}<ex>VISA ....1234</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS">{2}<ex>2</ex></ph> more}}
</message>
<message name="IDS_PAYMENT_REQUEST_SHIPPING_ADDRESSES_PREVIEW" desc="This is a snippet of a shipping address a user has saved to Chrome, plus an indication of the number of additional shipping addresses the user has saved. Its function is to show the user has shipping addresses that can be used to complete a purchase, and thus doesn't have to type the entire address. [ICU Syntax]" formatter_data="android_java">
{SHIPPING_ADDRESS, plural,
+ =0 {<ph name="SHIPPING_ADDRESS_PREVIEW">{1}<ex>Jerry, 1253 Mcgill college</ex></ph>}
=1 {<ph name="SHIPPING_ADDRESS_PREVIEW">{1}<ex>Jerry, 1253 Mcgill college</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES">{2}<ex>1</ex></ph> more}
other {<ph name="SHIPPING_ADDRESS_PREVIEW">{1}<ex>Jerry, 1253 Mcgill college</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES">{2}<ex>2</ex></ph> more}}
</message>
<message name="IDS_PAYMENT_REQUEST_SHIPPING_OPTIONS_PREVIEW" desc="This is a snippet of a shipping option a merchant supports, plus an indication of the number of additional shipping options the merchant supports. Its function is to show the user can choose different shipping options to complete a purchase. [ICU Syntax]">
{SHIPPING_OPTIONS, plural,
+ =0 {<ph name="SHIPPING_OPTION_PREVIEW">{1}<ex>standard shipping</ex></ph>}
=1 {<ph name="SHIPPING_OPTION_PREVIEW">{1}<ex>standard shipping</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS">{2}<ex>1</ex></ph> more}
other {<ph name="SHIPPING_OPTION_PREVIEW">{1}<ex>standard shipping</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS">{2}<ex>2</ex></ph> more}}
</message>
<message name="IDS_PAYMENT_REQUEST_CONTACTS_PREVIEW" desc="This is a snippet of a contact a user has saved to Chrome, plus an indication of the number of additional contacts the user has saved. Its function is to show the user has contacts that can be used to complete a purchase, and thus doesn't have to type the entire contact info. [ICU Syntax]">
{CONTACT, plural,
+ =0 {<ph name="CONTACT_PREVIEW">{1}<ex>Jerry, 438-123-1922</ex></ph>}
=1 {<ph name="CONTACT_PREVIEW">{1}<ex>Jerry, 438-123-1922</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_CONTACTS">{2}<ex>1</ex></ph> more}
other {<ph name="CONTACT_PREVIEW">{1}<ex>Jerry, 438-123-1922</ex></ph> and <ph name="NUMBER_OF_ADDITIONAL_CONTACTS">{2}<ex>2</ex></ph> more}}
</message>
</if>
+
+ <!-- a11y strings -->
+ <message name="IDS_PAYMENTS_BACK" desc="The screen reader string for the back arrow button in the Desktop Payments dialog">
+ Back
+ </message>
+ <message name="IDS_PAYMENTS_EDIT" desc="The screen reader string for the pencil edit button in the Desktop Payments dialog">
+ Edit
+ </message>
+ <message name="IDS_PAYMENTS_ROW_ACCESSIBLE_NAME_FORMAT" desc="The format to build the screen reader string for Payment Sheet rows">
+ <ph name="ROW_NAME">$1<ex>Payment Method</ex></ph> <ph name="ROW_CONTENT">$2<ex>VISA ****1234</ex></ph>
+ </message>
+ <message name="IDS_PAYMENTS_ROW_ACCESSIBLE_NAME_SELECTED_FORMAT" desc="The format to build the screen reader string for Payment Sheet rows, with an added indication that this row has been selected">
+ <ph name="ROW_NAME">$1<ex>Payment Method</ex></ph>, currently selected. <ph name="ROW_CONTENT">$2<ex>VISA ****1234</ex></ph>
+ </message>
+ <message name="IDS_PAYMENTS_PROFILE_LABELS_ACCESSIBLE_FORMAT" desc="The format to build the screen reader string for profile labels">
+ <ph name="FIRST_LABEL">$1<ex>VISA ****1234</ex></ph> <ph name="SECOND_LABEL">$2<ex>Homer Simpson</ex></ph> <ph name="THIRD_LABEL">$3<ex>123 Fake Street</ex></ph>
+ </message>
+ <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>
</grit-part>
diff --git a/chromium/components/pdf/DEPS b/chromium/components/pdf/DEPS
index d0c3033c30f..ffff8fe796e 100644
--- a/chromium/components/pdf/DEPS
+++ b/chromium/components/pdf/DEPS
@@ -5,4 +5,7 @@ include_rules = [
"+third_party/skia/include",
"+third_party/WebKit/public",
"+ui/base",
+ "+ui/gfx",
+ "+ui/strings",
+ "+ui/touch_selection",
]
diff --git a/chromium/components/pdf/browser/BUILD.gn b/chromium/components/pdf/browser/BUILD.gn
index 30fff96eae4..f7db28bbfcb 100644
--- a/chromium/components/pdf/browser/BUILD.gn
+++ b/chromium/components/pdf/browser/BUILD.gn
@@ -15,6 +15,7 @@ static_library("browser") {
"//base",
"//components/pdf/common:interfaces",
"//content/public/browser",
+ "//ui/touch_selection",
]
public_deps = [
diff --git a/chromium/components/pdf/browser/pdf_web_contents_helper.cc b/chromium/components/pdf/browser/pdf_web_contents_helper.cc
index 64b814bfe29..25066d26952 100644
--- a/chromium/components/pdf/browser/pdf_web_contents_helper.cc
+++ b/chromium/components/pdf/browser/pdf_web_contents_helper.cc
@@ -10,6 +10,9 @@
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/pdf/browser/pdf_web_contents_helper_client.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/strings/grit/ui_strings.h"
DEFINE_WEB_CONTENTS_USER_DATA_KEY(pdf::PDFWebContentsHelper);
@@ -31,9 +34,113 @@ PDFWebContentsHelper::PDFWebContentsHelper(
std::unique_ptr<PDFWebContentsHelperClient> client)
: content::WebContentsObserver(web_contents),
pdf_service_bindings_(web_contents, this),
- client_(std::move(client)) {}
+ client_(std::move(client)),
+ touch_selection_controller_client_manager_(nullptr),
+ has_selection_(false) {}
PDFWebContentsHelper::~PDFWebContentsHelper() {
+ if (!touch_selection_controller_client_manager_)
+ return;
+
+ touch_selection_controller_client_manager_->InvalidateClient(this);
+ touch_selection_controller_client_manager_->RemoveObserver(this);
+}
+
+void PDFWebContentsHelper::SelectionChanged(const gfx::Point& left,
+ int32_t left_height,
+ const gfx::Point& right,
+ int32_t right_height) {
+ if (!touch_selection_controller_client_manager_)
+ InitTouchSelectionClientManager();
+
+ if (touch_selection_controller_client_manager_) {
+ gfx::SelectionBound start;
+ gfx::SelectionBound end;
+ start.SetEdgeTop(gfx::PointF(left.x(), left.y()));
+ start.SetEdgeBottom(gfx::PointF(left.x(), left.y() + left_height));
+ start.set_type(gfx::SelectionBound::LEFT);
+ start.set_visible(true);
+ end.SetEdgeTop(gfx::PointF(right.x(), right.y()));
+ end.SetEdgeBottom(gfx::PointF(right.x(), right.y() + right_height));
+ end.set_type(gfx::SelectionBound::RIGHT);
+ end.set_visible(true);
+
+ has_selection_ = start != end;
+
+ touch_selection_controller_client_manager_->UpdateClientSelectionBounds(
+ start, end, this, this);
+ }
+}
+
+bool PDFWebContentsHelper::SupportsAnimation() const {
+ return false;
+}
+
+void PDFWebContentsHelper::MoveCaret(const gfx::PointF& position) {
+ // TODO(wjmaclean, dsinclair): Implement connection to PDFium to implement.
+}
+
+void PDFWebContentsHelper::MoveRangeSelectionExtent(const gfx::PointF& extent) {
+ // TODO(wjmaclean, dsinclair): Implement connection to PDFium to implement.
+}
+
+void PDFWebContentsHelper::SelectBetweenCoordinates(const gfx::PointF& base,
+ const gfx::PointF& extent) {
+ // TODO(wjmaclean, dsinclair): Implement connection to PDFium to implement.
+}
+
+void PDFWebContentsHelper::OnSelectionEvent(ui::SelectionEventType event) {}
+
+std::unique_ptr<ui::TouchHandleDrawable>
+PDFWebContentsHelper::CreateDrawable() {
+ // We can return null here, as the manager will look after this.
+ return std::unique_ptr<ui::TouchHandleDrawable>();
+}
+
+void PDFWebContentsHelper::OnManagerWillDestroy(
+ content::TouchSelectionControllerClientManager* manager) {
+ DCHECK(manager == touch_selection_controller_client_manager_);
+ manager->RemoveObserver(this);
+ touch_selection_controller_client_manager_ = nullptr;
+}
+
+bool PDFWebContentsHelper::IsCommandIdEnabled(int command_id) const {
+ // TODO(wjmaclean|dsinclair): Make PDFium send readability information in the
+ // selection changed message?
+ bool readable = true;
+
+ switch (command_id) {
+ case IDS_APP_COPY:
+ return readable && has_selection_;
+ // TODO(wjmaclean): add logic for copy/paste as the information required
+ // from PDFium becomes available.
+ }
+ return false;
+}
+
+void PDFWebContentsHelper::ExecuteCommand(int command_id, int event_flags) {
+ // TODO(wjmaclean, dsinclair): Need to communicate to PDFium to get it to copy
+ // the selection onto the clipboard (and eventually accept cut/paste commands
+ // too).
+}
+
+void PDFWebContentsHelper::RunContextMenu() {
+ // TouchSelectionControllerClientAura will handle this for us.
+ NOTIMPLEMENTED();
+}
+
+void PDFWebContentsHelper::InitTouchSelectionClientManager() {
+ content::RenderWidgetHostView* view =
+ web_contents()->GetRenderWidgetHostView();
+ if (!view)
+ return;
+
+ touch_selection_controller_client_manager_ =
+ view->GetTouchSelectionControllerClientManager();
+ if (!touch_selection_controller_client_manager_)
+ return;
+
+ touch_selection_controller_client_manager_->AddObserver(this);
}
void PDFWebContentsHelper::HasUnsupportedFeature() {
diff --git a/chromium/components/pdf/browser/pdf_web_contents_helper.h b/chromium/components/pdf/browser/pdf_web_contents_helper.h
index 0d848c7bfc5..e91790f39b2 100644
--- a/chromium/components/pdf/browser/pdf_web_contents_helper.h
+++ b/chromium/components/pdf/browser/pdf_web_contents_helper.h
@@ -11,9 +11,13 @@
#include "base/callback.h"
#include "base/macros.h"
#include "components/pdf/common/pdf.mojom.h"
+#include "content/public/browser/touch_selection_controller_client_manager.h"
#include "content/public/browser/web_contents_binding_set.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
+#include "ui/touch_selection/selection_event_type.h"
+#include "ui/touch_selection/touch_selection_controller.h"
+#include "ui/touch_selection/touch_selection_menu_runner.h"
namespace content {
class WebContents;
@@ -27,7 +31,10 @@ class PDFWebContentsHelperClient;
class PDFWebContentsHelper
: public content::WebContentsObserver,
public content::WebContentsUserData<PDFWebContentsHelper>,
- public mojom::PdfService {
+ public mojom::PdfService,
+ public ui::TouchSelectionControllerClient,
+ public ui::TouchSelectionMenuClient,
+ public content::TouchSelectionControllerClientManager::Observer {
public:
~PDFWebContentsHelper() override;
@@ -35,10 +42,36 @@ class PDFWebContentsHelper
content::WebContents* contents,
std::unique_ptr<PDFWebContentsHelperClient> client);
+ void SelectionChanged(const gfx::Point& left,
+ int32_t left_height,
+ const gfx::Point& right,
+ int32_t right_height);
+
+ // ui::TouchSelectionControllerClient :
+ bool SupportsAnimation() const override;
+ void SetNeedsAnimate() override {}
+ void MoveCaret(const gfx::PointF& position) override;
+ void MoveRangeSelectionExtent(const gfx::PointF& extent) override;
+ void SelectBetweenCoordinates(const gfx::PointF& base,
+ const gfx::PointF& extent) override;
+ void OnSelectionEvent(ui::SelectionEventType event) override;
+ std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override;
+
+ // ui::TouchSelectionMenuRunner:
+ bool IsCommandIdEnabled(int command_id) const override;
+ void ExecuteCommand(int command_id, int event_flags) override;
+ void RunContextMenu() override;
+
+ // ui::TouchSelectionControllerClientManager::Observer:
+ void OnManagerWillDestroy(
+ content::TouchSelectionControllerClientManager* manager) override;
+
private:
PDFWebContentsHelper(content::WebContents* web_contents,
std::unique_ptr<PDFWebContentsHelperClient> client);
+ void InitTouchSelectionClientManager();
+
// mojom::PdfService:
void HasUnsupportedFeature() override;
void SaveUrlAs(const GURL& url, const content::Referrer& referrer) override;
@@ -46,6 +79,9 @@ class PDFWebContentsHelper
content::WebContentsFrameBindingSet<mojom::PdfService> pdf_service_bindings_;
std::unique_ptr<PDFWebContentsHelperClient> client_;
+ content::TouchSelectionControllerClientManager*
+ touch_selection_controller_client_manager_;
+ bool has_selection_;
DISALLOW_COPY_AND_ASSIGN(PDFWebContentsHelper);
};
diff --git a/chromium/components/pdf/renderer/pdf_accessibility_tree.cc b/chromium/components/pdf/renderer/pdf_accessibility_tree.cc
index 4512380c852..09ed47bb95a 100644
--- a/chromium/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/chromium/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -275,7 +275,7 @@ ui::AXNodeData* PdfAccessibilityTree::CreateNode(ui::AXRole role) {
ui::AXNodeData* node = new ui::AXNodeData();
node->id = render_accessibility->GenerateAXID();
node->role = role;
- node->AddState(ui::AX_STATE_READ_ONLY);
+ node->AddIntAttribute(ui::AX_ATTR_RESTRICTION, ui::AX_RESTRICTION_READ_ONLY);
// All nodes other than the first one have coordinates relative to
// the first node.
diff --git a/chromium/components/physical_web/webui/physical_web_base_message_handler.cc b/chromium/components/physical_web/webui/physical_web_base_message_handler.cc
index dd28dc3dd22..f68cfb08c74 100644
--- a/chromium/components/physical_web/webui/physical_web_base_message_handler.cc
+++ b/chromium/components/physical_web/webui/physical_web_base_message_handler.cc
@@ -4,6 +4,8 @@
#include "components/physical_web/webui/physical_web_base_message_handler.h"
+#include <utility>
+
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
@@ -88,7 +90,7 @@ void PhysicalWebBaseMessageHandler::PushNearbyURLs() {
++index;
}
- results.Set(physical_web_ui::kMetadata, metadata.release());
+ results.Set(physical_web_ui::kMetadata, std::move(metadata));
// Pass the list of Physical Web URL metadata to the WebUI. A jstemplate will
// create a list view with an item for each URL.
diff --git a/chromium/components/physical_web/webui/resources/physical_web.html b/chromium/components/physical_web/webui/resources/physical_web.html
index 034912ed743..a873271a69d 100644
--- a/chromium/components/physical_web/webui/resources/physical_web.html
+++ b/chromium/components/physical_web/webui/resources/physical_web.html
@@ -1,8 +1,8 @@
<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
-<title i18n-content="title"></title>
+<title>$i18n{title}</title>
<if expr="is_android or is_ios">
<meta name="viewport" content="width=device-width, user-scalable=no">
</if>
@@ -22,9 +22,9 @@
<body>
<div id="body-container" hidden>
- <h1 i18n-content="title"></h1>
+ <h1>$i18n{title}</h1>
<div id="render-container"></div>
- <div id="empty-list-container" i18n-content="emptyMessage"></div>
+ <div id="empty-list-container">$i18n{emptyMessage}</div>
</div>
<a hidden id="render-template" class="physicalWebTemplate" jsselect="metadata"
diff --git a/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc b/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
index 57678645368..c5b3b05876d 100644
--- a/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
+++ b/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
@@ -145,7 +145,7 @@ void LoadablePluginPlaceholder::UpdateMessage() {
return;
std::string script =
"window.setMessage(" + base::GetQuotedJSONString(message_) + ")";
- plugin()->web_view()->MainFrame()->ExecuteScript(
+ plugin()->main_frame()->ExecuteScript(
blink::WebScriptSource(blink::WebString::FromUTF8(script)));
}
@@ -252,7 +252,7 @@ void LoadablePluginPlaceholder::OnUnobscuredRectUpdate(
std::string script = base::StringPrintf(
"window.resizePoster('%dpx', '%dpx', '%dpx', '%dpx')", x, y, width,
height);
- plugin()->web_view()->MainFrame()->ExecuteScript(
+ plugin()->main_frame()->ExecuteScript(
blink::WebScriptSource(blink::WebString::FromUTF8(script)));
}
}
@@ -343,13 +343,11 @@ void LoadablePluginPlaceholder::DidFinishIconRepositionForTestingCallback() {
blink::WebElement element = plugin()->Container()->GetElement();
element.SetAttribute("placeholderReady", "true");
- std::unique_ptr<content::V8ValueConverter> converter(
- content::V8ValueConverter::create());
base::Value value("placeholderReady");
blink::WebSerializedScriptValue message_data =
blink::WebSerializedScriptValue::Serialize(
blink::MainThreadIsolate(),
- converter->ToV8Value(
+ content::V8ValueConverter::Create()->ToV8Value(
&value,
element.GetDocument().GetFrame()->MainWorldScriptContext()));
blink::WebDOMMessageEvent msg_event(message_data);
diff --git a/chromium/components/plugins/renderer/webview_plugin.cc b/chromium/components/plugins/renderer/webview_plugin.cc
index 80404cfe8a3..6b580b16f05 100644
--- a/chromium/components/plugins/renderer/webview_plugin.cc
+++ b/chromium/components/plugins/renderer/webview_plugin.cc
@@ -68,7 +68,7 @@ WebViewPlugin* WebViewPlugin::Create(content::RenderView* render_view,
const GURL& url) {
DCHECK(url.is_valid()) << "Blink requires the WebView to have a valid URL.";
WebViewPlugin* plugin = new WebViewPlugin(render_view, delegate, preferences);
- plugin->web_view()->MainFrame()->LoadHTMLString(html_data, url);
+ plugin->main_frame()->LoadHTMLString(html_data, url);
return plugin;
}
@@ -259,18 +259,22 @@ WebViewPlugin::WebViewHelper::WebViewHelper(
// ApplyWebPreferences before making a WebLocalFrame so that the frame sees a
// consistent view of our preferences.
content::RenderView::ApplyWebPreferences(preferences, web_view_);
- WebLocalFrame* web_frame = WebLocalFrame::Create(
- blink::WebTreeScopeType::kDocument, this, nullptr, nullptr);
- web_view_->SetMainFrame(web_frame);
- // TODO(dcheng): The main frame widget currently has a special case.
- // Eliminate this once WebView is no longer a WebWidget.
- WebFrameWidget::Create(this, web_view_, web_frame);
+ WebLocalFrame* web_frame =
+ WebLocalFrame::CreateMainFrame(web_view_, this, nullptr, nullptr);
+ WebFrameWidget::Create(this, web_frame);
}
WebViewPlugin::WebViewHelper::~WebViewHelper() {
web_view_->Close();
}
+blink::WebLocalFrame* WebViewPlugin::WebViewHelper::main_frame() {
+ // WebViewHelper doesn't support OOPIFs so the main frame will
+ // always be local.
+ DCHECK(web_view_->MainFrame()->IsWebLocalFrame());
+ return static_cast<WebLocalFrame*>(web_view_->MainFrame());
+}
+
bool WebViewPlugin::WebViewHelper::AcceptsLoadDrops() {
return false;
}
@@ -296,11 +300,7 @@ void WebViewPlugin::WebViewHelper::StartDragging(blink::WebReferrerPolicy,
const WebImage&,
const WebPoint&) {
// Immediately stop dragging.
- DCHECK(web_view_->MainFrame()->IsWebLocalFrame());
- web_view_->MainFrame()
- ->ToWebLocalFrame()
- ->FrameWidget()
- ->DragSourceSystemDragEnded();
+ main_frame()->FrameWidget()->DragSourceSystemDragEnded();
}
bool WebViewPlugin::WebViewHelper::AllowsBrokenNullLayerTreeView() const {
@@ -334,9 +334,11 @@ void WebViewPlugin::WebViewHelper::ScheduleAnimation() {
}
std::unique_ptr<blink::WebURLLoader>
-WebViewPlugin::WebViewHelper::CreateURLLoader() {
+WebViewPlugin::WebViewHelper::CreateURLLoader(
+ const blink::WebURLRequest& request,
+ base::SingleThreadTaskRunner* task_runner) {
// TODO(yhirano): Stop using Platform::CreateURLLoader() here.
- return blink::Platform::Current()->CreateURLLoader();
+ return blink::Platform::Current()->CreateURLLoader(request, task_runner);
}
void WebViewPlugin::WebViewHelper::DidClearWindowObject() {
@@ -345,8 +347,7 @@ void WebViewPlugin::WebViewHelper::DidClearWindowObject() {
v8::Isolate* isolate = blink::MainThreadIsolate();
v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Context> context =
- web_view_->MainFrame()->MainWorldScriptContext();
+ v8::Local<v8::Context> context = main_frame()->MainWorldScriptContext();
DCHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
diff --git a/chromium/components/plugins/renderer/webview_plugin.h b/chromium/components/plugins/renderer/webview_plugin.h
index 5e3693ea755..240dfe5e059 100644
--- a/chromium/components/plugins/renderer/webview_plugin.h
+++ b/chromium/components/plugins/renderer/webview_plugin.h
@@ -20,6 +20,7 @@
#include "third_party/WebKit/public/web/WebViewClient.h"
namespace blink {
+class WebLocalFrame;
class WebMouseEvent;
}
@@ -68,7 +69,7 @@ class WebViewPlugin : public blink::WebPlugin,
const std::string& html_data,
const GURL& url);
- blink::WebView* web_view() { return web_view_helper_.web_view(); }
+ blink::WebLocalFrame* main_frame() { return web_view_helper_.main_frame(); }
const blink::WebString& old_title() const { return old_title_; }
@@ -116,6 +117,8 @@ class WebViewPlugin : public blink::WebPlugin,
const content::WebPreferences& preferences);
~WebViewPlugin() override;
+ blink::WebView* web_view() { return web_view_helper_.web_view(); }
+
// content::RenderViewObserver methods:
void OnDestruct() override {}
void OnZoomLevelChanged() override;
@@ -151,6 +154,7 @@ class WebViewPlugin : public blink::WebPlugin,
~WebViewHelper() override;
blink::WebView* web_view() { return web_view_; }
+ blink::WebLocalFrame* main_frame();
// WebViewClient methods:
bool AcceptsLoadDrops() override;
@@ -171,7 +175,9 @@ class WebViewPlugin : public blink::WebPlugin,
void DidInvalidateRect(const blink::WebRect&) override;
void DidChangeCursor(const blink::WebCursorInfo& cursor) override;
void ScheduleAnimation() override;
- std::unique_ptr<blink::WebURLLoader> CreateURLLoader() override;
+ std::unique_ptr<blink::WebURLLoader> CreateURLLoader(
+ const blink::WebURLRequest& request,
+ base::SingleThreadTaskRunner* task_runner) override;
// WebFrameClient methods:
void DidClearWindowObject() override;
diff --git a/chromium/components/policy/core/browser/BUILD.gn b/chromium/components/policy/core/browser/BUILD.gn
index c3730e0e1dd..47957c237b1 100644
--- a/chromium/components/policy/core/browser/BUILD.gn
+++ b/chromium/components/policy/core/browser/BUILD.gn
@@ -69,8 +69,6 @@ source_set("internal") {
sources += [
"android/android_combined_policy_provider.cc",
"android/android_combined_policy_provider.h",
- "android/component_jni_registrar.cc",
- "android/component_jni_registrar.h",
"android/policy_converter.cc",
"android/policy_converter.h",
]
diff --git a/chromium/components/policy/core/common/BUILD.gn b/chromium/components/policy/core/common/BUILD.gn
index 17217b1ad87..d5093840495 100644
--- a/chromium/components/policy/core/common/BUILD.gn
+++ b/chromium/components/policy/core/common/BUILD.gn
@@ -157,6 +157,7 @@ source_set("internal") {
"ntdsapi.lib",
]
}
+
# Compile on Linux for fuzzer and since code is reused on Chrome OS.
if (is_win || is_linux) {
sources += [
@@ -188,6 +189,8 @@ source_set("internal") {
}
if (is_chromeos) {
sources += [
+ "policy_scheduler.cc",
+ "policy_scheduler.h",
"proxy_policy_provider.cc",
"proxy_policy_provider.h",
]
@@ -305,7 +308,10 @@ source_set("unit_tests") {
]
}
if (is_chromeos) {
- sources += [ "proxy_policy_provider_unittest.cc" ]
+ sources += [
+ "policy_scheduler_unittest.cc",
+ "proxy_policy_provider_unittest.cc",
+ ]
} else {
sources += [
"cloud/user_cloud_policy_manager_unittest.cc",
diff --git a/chromium/components/policy_strings.grdp b/chromium/components/policy_strings.grdp
index 5e093d1653c..4b96b8140df 100644
--- a/chromium/components/policy_strings.grdp
+++ b/chromium/components/policy_strings.grdp
@@ -246,9 +246,12 @@
<message name="IDS_POLICY_STATUS_USER" desc="Title for the user policy status box.">
User policies
</message>
- <message name="IDS_POLICY_LABEL_DOMAIN" desc="Label for the enrollment domain in the device policy status box.">
+ <message name="IDS_POLICY_LABEL_ENTERPRISE_ENROLLMENT_DOMAIN" desc="Label for the enrollment domain in the device policy status box.">
Enrollment domain:
</message>
+ <message name="IDS_POLICY_LABEL_ENTERPRISE_DISPLAY_DOMAIN" desc="Label for the display domain in the device policy status box.">
+ Display domain:
+ </message>
<message name="IDS_POLICY_LABEL_USERNAME" desc="Label for the username in the user policy status box.">
User:
</message>
@@ -334,8 +337,8 @@
<message name="IDS_POLICY_SOURCE_CLOUD" desc="Indicates that the policy originates from the cloud.">
Cloud
</message>
- <message name="IDS_POLICY_SOURCE_ACTIVE_DIRECTORY" desc="Indicates that the policy originates from Active Directory.">
- <ph name="MICROSOFT_ACTIVE_DIRECTORY">Microsoft® Active Directory®</ph>
+ <message name="IDS_POLICY_SOURCE_ACTIVE_DIRECTORY" desc="Indicates that the policy originates from a local server, e.g. Samba or Active Directory.">
+ <ph name="MICROSOFT_ACTIVE_DIRECTORY">Local Server</ph>
</message>
<message name="IDS_POLICY_SOURCE_PLATFORM" desc="Indicates that the policy is obtained from the local OS.">
Platform
diff --git a/chromium/components/precache/DEPS b/chromium/components/precache/DEPS
deleted file mode 100644
index 975943c97a9..00000000000
--- a/chromium/components/precache/DEPS
+++ /dev/null
@@ -1,11 +0,0 @@
-include_rules = [
- # Precache is a layered component; subdirectories must introduce the ability
- # to use the content layer explicitly as appropriate.
- # http://www.chromium.org/developers/design-documents/layered-components-design
- "+components/data_use_measurement/core",
- "-components/precache",
- "+components/precache/core",
-
- "+components/history/core/browser",
- "+components/prefs",
-]
diff --git a/chromium/components/precache/OWNERS b/chromium/components/precache/OWNERS
deleted file mode 100644
index 1bec579e45d..00000000000
--- a/chromium/components/precache/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-lizeb@chromium.org
-pasko@chromium.org
-twifkak@chromium.org
-
-# Emeritus:
-bengr@chromium.org
-rajendrant@chromium.org
-sclittle@chromium.org
diff --git a/chromium/components/precache/README b/chromium/components/precache/README
deleted file mode 100644
index 438ab7b781d..00000000000
--- a/chromium/components/precache/README
+++ /dev/null
@@ -1,11 +0,0 @@
-The precache component contains code for an experimental prototype to
-proactively load Web resources into the network stack's disk cache.
-
-To enable this feature use the command line flag --enable-precache.
-
-Precache is a layered component. See:
-http://www.chromium.org/developers/design-documents/layered-components-design
-
-Folder structure:
-
- core/ contains the core logic of the component.
diff --git a/chromium/components/precache/android/BUILD.gn b/chromium/components/precache/android/BUILD.gn
deleted file mode 100644
index af995624b56..00000000000
--- a/chromium/components/precache/android/BUILD.gn
+++ /dev/null
@@ -1,29 +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.
-
-import("//build/config/android/rules.gni")
-
-android_library("precache_java") {
- deps = [
- "//base:base_java",
- ]
- java_files = [
- "java/src/org/chromium/components/precache/DeviceState.java",
- "java/src/org/chromium/components/precache/NetworkInfoDelegate.java",
- "java/src/org/chromium/components/precache/NetworkInfoDelegateFactory.java",
- ]
-}
-
-android_library("precache_javatests") {
- testonly = true
- deps = [
- ":precache_java",
- "//base:base_java_test_support",
- "//third_party/android_support_test_runner:runner_java",
- ]
- java_files = [
- "javatests/src/org/chromium/components/precache/DeviceStateTest.java",
- "javatests/src/org/chromium/components/precache/MockDeviceState.java",
- ]
-}
diff --git a/chromium/components/precache/content/BUILD.gn b/chromium/components/precache/content/BUILD.gn
deleted file mode 100644
index bca9748e511..00000000000
--- a/chromium/components/precache/content/BUILD.gn
+++ /dev/null
@@ -1,50 +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.
-
-static_library("content") {
- sources = [
- "precache_manager.cc",
- "precache_manager.h",
- ]
-
- configs += [ "//components/precache/core:precache_config" ]
-
- deps = [
- "//base",
- "//components/data_reduction_proxy/core/browser",
- "//components/history/core/browser",
- "//components/keyed_service/core",
- "//components/precache/core",
- "//components/precache/core:proto",
- "//components/prefs",
- "//components/sync",
- "//components/variations",
- "//content/public/browser",
- "//net",
- "//sql",
- "//url",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "precache_manager_unittest.cc",
- ]
- deps = [
- ":content",
- "//base",
- "//base/test:test_support",
- "//components/history/core/browser",
- "//components/precache/core",
- "//components/precache/core:proto",
- "//components/variations:test_support",
- "//content/public/browser",
- "//content/test:test_support",
- "//net:test_support",
- "//sql",
- "//testing/gmock",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/precache/content/DEPS b/chromium/components/precache/content/DEPS
deleted file mode 100644
index 5a184772960..00000000000
--- a/chromium/components/precache/content/DEPS
+++ /dev/null
@@ -1,18 +0,0 @@
-include_rules = [
- "+components/data_reduction_proxy",
- "+components/keyed_service",
- "+components/sync/driver",
- "+components/variations",
- "+content/public/browser",
- "+net/base",
- "+net/disk_cache",
- "+net/http",
- "+net/url_request",
-]
-
-specific_include_rules = {
- '.*_[a-z]*test\.cc': [
- "+content/public/test",
- "+net/test",
- ],
-}
diff --git a/chromium/components/precache/content/precache_manager.cc b/chromium/components/precache/content/precache_manager.cc
deleted file mode 100644
index d9ad0ae9809..00000000000
--- a/chromium/components/precache/content/precache_manager.cc
+++ /dev/null
@@ -1,514 +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/precache/content/precache_manager.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/time/time.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/precache/core/precache_database.h"
-#include "components/precache/core/precache_switches.h"
-#include "components/precache/core/proto/precache.pb.h"
-#include "components/precache/core/proto/unfinished_work.pb.h"
-#include "components/prefs/pref_service.h"
-#include "components/sync/driver/sync_service.h"
-#include "components/variations/metrics_util.h"
-#include "components/variations/variations_associated_data.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/base/network_change_notifier.h"
-#include "net/http/http_cache.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_getter.h"
-
-using content::BrowserThread;
-
-namespace precache {
-
-const char kPrecacheFieldTrialName[] = "Precache";
-const char kMinCacheSizeParam[] = "min_cache_size";
-
-namespace {
-
-const char kPrecacheFieldTrialEnabledGroup[] = "Enabled";
-const char kPrecacheFieldTrialControlGroup[] = "Control";
-const char kConfigURLParam[] = "config_url";
-const char kManifestURLPrefixParam[] = "manifest_url_prefix";
-const char kDataReductionProxyParam[] = "disable_if_data_reduction_proxy";
-const size_t kNumTopHosts = 100;
-
-} // namespace
-
-size_t NumTopHosts() {
- return kNumTopHosts;
-}
-
-PrecacheManager::PrecacheManager(
- content::BrowserContext* browser_context,
- const syncer::SyncService* const sync_service,
- const history::HistoryService* const history_service,
- const data_reduction_proxy::DataReductionProxySettings*
- data_reduction_proxy_settings,
- Delegate* delegate,
- const base::FilePath& db_path,
- std::unique_ptr<PrecacheDatabase> precache_database)
- : browser_context_(browser_context),
- sync_service_(sync_service),
- history_service_(history_service),
- data_reduction_proxy_settings_(data_reduction_proxy_settings),
- delegate_(delegate),
- is_precaching_(false) {
- precache_database_ = std::move(precache_database);
- BrowserThread::PostTask(
- BrowserThread::DB, FROM_HERE,
- base::Bind(base::IgnoreResult(&PrecacheDatabase::Init),
- base::Unretained(precache_database_.get()), db_path));
-}
-
-PrecacheManager::~PrecacheManager() {
- // DeleteSoon posts a non-nestable task to the task runner, so any previously
- // posted tasks that rely on an Unretained precache_database_ will finish
- // before it is deleted.
- BrowserThread::DeleteSoon(BrowserThread::DB, FROM_HERE,
- precache_database_.release());
-}
-
-bool PrecacheManager::IsInExperimentGroup() const {
- // Verify IsPrecachingAllowed() before calling FieldTrialList::FindFullName().
- // This is because field trials are only assigned when requested. This allows
- // us to create Control and Experiment groups that are limited to users for
- // whom PrecachingAllowed() is true, thus accentuating the impact of
- // precaching.
- return IsPrecachingAllowed() &&
- (base::StartsWith(
- base::FieldTrialList::FindFullName(kPrecacheFieldTrialName),
- kPrecacheFieldTrialEnabledGroup, base::CompareCase::SENSITIVE) ||
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnablePrecache));
-}
-
-bool PrecacheManager::IsInControlGroup() const {
- // Verify IsPrecachingAllowed() before calling FindFullName(). See
- // PrecacheManager::IsInExperimentGroup() for an explanation of why.
- return IsPrecachingAllowed() &&
- base::StartsWith(
- base::FieldTrialList::FindFullName(kPrecacheFieldTrialName),
- kPrecacheFieldTrialControlGroup, base::CompareCase::SENSITIVE);
-}
-
-bool PrecacheManager::IsPrecachingAllowed() const {
- return PrecachingAllowed() == AllowedType::ALLOWED;
-}
-
-PrecacheManager::AllowedType PrecacheManager::PrecachingAllowed() const {
- bool disable_if_proxy = !variations::GetVariationParamValue(
- kPrecacheFieldTrialName, kDataReductionProxyParam).empty();
- if (disable_if_proxy &&
- (!data_reduction_proxy_settings_ ||
- data_reduction_proxy_settings_->IsDataReductionProxyEnabled()))
- return AllowedType::DISALLOWED;
-
- if (!(sync_service_ && sync_service_->IsEngineInitialized()))
- return AllowedType::PENDING;
-
- // SyncService delegates to SyncPrefs, which must be called on the UI thread.
- if (history_service_ && !sync_service_->IsLocalSyncEnabled() &&
- sync_service_->GetActiveDataTypes().Has(syncer::SESSIONS) &&
- !sync_service_->GetEncryptedDataTypes().Has(syncer::SESSIONS)) {
- return AllowedType::ALLOWED;
- }
-
- return AllowedType::DISALLOWED;
-}
-
-void PrecacheManager::OnCacheBackendReceived(int net_error_code) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- if (net_error_code != net::OK) {
- // Assume there is no cache.
- cache_backend_ = nullptr;
- OnCacheSizeReceived(0);
- return;
- }
- DCHECK(cache_backend_);
- int result = cache_backend_->CalculateSizeOfAllEntries(base::Bind(
- &PrecacheManager::OnCacheSizeReceived, base::Unretained(this)));
- if (result == net::ERR_IO_PENDING) {
- // Wait for the callback.
- } else if (result >= 0) {
- // The result is the expected bytes already.
- OnCacheSizeReceived(result);
- } else {
- // Error occurred. Couldn't get the size. Assume there is no cache.
- OnCacheSizeReceived(0);
- }
- cache_backend_ = nullptr;
-}
-
-void PrecacheManager::OnCacheSizeReceived(int cache_size_bytes) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&PrecacheManager::OnCacheSizeReceivedInUIThread,
- base::Unretained(this), cache_size_bytes));
-}
-
-void PrecacheManager::OnCacheSizeReceivedInUIThread(int cache_size_bytes) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- UMA_HISTOGRAM_MEMORY_KB("Precache.CacheSize.AllEntries",
- cache_size_bytes / 1024);
-
- if (cache_size_bytes < min_cache_size_bytes_) {
- OnDone(); // Do not continue.
- } else {
- BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::GetUnfinishedWork,
- base::Unretained(precache_database_.get())),
- base::Bind(&PrecacheManager::OnGetUnfinishedWorkDone, AsWeakPtr()));
- }
-}
-
-void PrecacheManager::PrecacheIfCacheIsBigEnough(
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- CHECK(url_request_context_getter);
-
- // Continue with OnGetUnfinishedWorkDone only if the size of the cache is
- // at least min_cache_size_bytes_.
- // Class disk_cache::Backend does not expose its maximum size. However, caches
- // are usually full, so we can use the size of all the entries stored in the
- // cache (via CalculateSizeOfAllEntries) as a proxy of its maximum size.
- net::URLRequestContext* context =
- url_request_context_getter->GetURLRequestContext();
- if (!context) {
- OnCacheSizeReceived(0);
- return;
- }
- net::HttpTransactionFactory* factory = context->http_transaction_factory();
- if (!factory) {
- OnCacheSizeReceived(0);
- return;
- }
- net::HttpCache* cache = factory->GetCache();
- if (!cache) {
- // There is no known cache. Assume that there is no cache.
- // TODO(jamartin): I'm not sure this can be an actual posibility. Consider
- // making this a CHECK(cache).
- OnCacheSizeReceived(0);
- return;
- }
- const int net_error_code = cache->GetBackend(
- &cache_backend_, base::Bind(&PrecacheManager::OnCacheBackendReceived,
- base::Unretained(this)));
- if (net_error_code != net::ERR_IO_PENDING) {
- // No need to wait for the callback. The callback hasn't been called with
- // the appropriate code, so we call it directly.
- OnCacheBackendReceived(net_error_code);
- }
-}
-
-void PrecacheManager::StartPrecaching(
- const PrecacheCompletionCallback& precache_completion_callback) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- if (is_precaching_) {
- DLOG(WARNING) << "Cannot start precaching because precaching is already "
- "in progress.";
- return;
- }
- precache_completion_callback_ = precache_completion_callback;
-
- is_precaching_ = true;
- BrowserThread::PostTask(
- BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::SetLastPrecacheTimestamp,
- base::Unretained(precache_database_.get()),
- base::Time::Now()));
-
- // Ignore boolean return value. In all documented failure cases, it sets the
- // int to a reasonable value.
- base::StringToInt(variations::GetVariationParamValue(kPrecacheFieldTrialName,
- kMinCacheSizeParam),
- &min_cache_size_bytes_);
- if (min_cache_size_bytes_ <= 0) {
- // Skip looking up the cache size, because it doesn't matter.
- OnCacheSizeReceivedInUIThread(0);
- return;
- }
-
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
- content::BrowserContext::GetDefaultStoragePartition(browser_context_)
- ->GetURLRequestContext());
- if (!url_request_context_getter) {
- OnCacheSizeReceivedInUIThread(0);
- return;
- }
-
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PrecacheManager::PrecacheIfCacheIsBigEnough, AsWeakPtr(),
- std::move(url_request_context_getter)));
-}
-
-void PrecacheManager::OnGetUnfinishedWorkDone(
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work) {
- // Reset progress on a prefetch that has taken too long to complete.
- if (unfinished_work->has_start_time() &&
- base::Time::Now() -
- base::Time::FromInternalValue(unfinished_work->start_time()) >
- base::TimeDelta::FromHours(6)) {
- PrecacheFetcher::RecordCompletionStatistics(
- *unfinished_work, unfinished_work->top_host_size(),
- unfinished_work->resource_size());
- unfinished_work.reset(new PrecacheUnfinishedWork);
- }
- // If this prefetch is new, set the start time.
- if (!unfinished_work->has_start_time())
- unfinished_work->set_start_time(base::Time::Now().ToInternalValue());
- unfinished_work_ = std::move(unfinished_work);
- bool needs_top_hosts = unfinished_work_->top_host_size() == 0;
-
- base::RecordAction(base::UserMetricsAction("Precache.Fetch.Begin"));
-
- if (IsInExperimentGroup()) {
- BrowserThread::PostTask(
- BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::DeleteExpiredPrecacheHistory,
- base::Unretained(precache_database_.get()),
- base::Time::Now()));
-
- // Request NumTopHosts() top hosts. Note that PrecacheFetcher is further
- // bound by the value of PrecacheConfigurationSettings.top_sites_count, as
- // retrieved from the server.
- if (needs_top_hosts) {
- history_service_->TopHosts(
- NumTopHosts(),
- base::Bind(&PrecacheManager::OnHostsReceived, AsWeakPtr()));
- } else {
- InitializeAndStartFetcher();
- }
- } else if (IsInControlGroup()) {
- // Calculate TopHosts solely for metrics purposes.
- if (needs_top_hosts) {
- history_service_->TopHosts(
- NumTopHosts(),
- base::Bind(&PrecacheManager::OnHostsReceivedThenDone, AsWeakPtr()));
- } else {
- OnDone();
- }
- } else {
- if (PrecachingAllowed() != AllowedType::PENDING) {
- // We are not waiting on the sync engine to be initialized. The user
- // either is not in the field trial, or does not have sync enabled.
- // Pretend that precaching started, so that the PrecacheServiceLauncher
- // doesn't try to start it again.
- }
-
- OnDone();
- }
-}
-
-void PrecacheManager::CancelPrecaching() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (!is_precaching_) {
- // Do nothing if precaching is not in progress.
- return;
- }
- is_precaching_ = false;
- // If cancellation occurs after StartPrecaching but before OnHostsReceived,
- // is_precaching will be true, but the precache_fetcher_ will not yet be
- // constructed.
- if (precache_fetcher_) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work =
- precache_fetcher_->CancelPrecaching();
- if (unfinished_work) {
- BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::SaveUnfinishedWork,
- precache_database_->GetWeakPtr(),
- base::Passed(&unfinished_work)));
- }
- // Destroying the |precache_fetcher_| will cancel any fetch in progress.
- precache_fetcher_.reset();
- }
- precache_completion_callback_.Reset();
-}
-
-bool PrecacheManager::IsPrecaching() const {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- return is_precaching_;
-}
-
-void PrecacheManager::UpdatePrecacheMetricsAndState(
- const GURL& url,
- const GURL& referrer,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- bool is_user_traffic,
- const base::Callback<void(base::Time)>& register_synthetic_trial) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::GetLastPrecacheTimestamp,
- base::Unretained(precache_database_.get())),
- base::Bind(&PrecacheManager::RecordStatsForFetch, AsWeakPtr(), url,
- referrer, fetch_time, info, size, register_synthetic_trial));
-
- if (is_user_traffic && IsPrecaching())
- CancelPrecaching();
-}
-
-void PrecacheManager::RecordStatsForFetch(
- const GURL& url,
- const GURL& referrer,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- const base::Callback<void(base::Time)>& register_synthetic_trial,
- base::Time last_precache_time) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- register_synthetic_trial.Run(last_precache_time);
-
- if (size == 0 || url.is_empty() || !url.SchemeIsHTTPOrHTTPS()) {
- // Ignore empty responses, empty URLs, or URLs that aren't HTTP or HTTPS.
- return;
- }
-
- if (!history_service_)
- return;
-
- history_service_->HostRankIfAvailable(
- referrer,
- base::Bind(&PrecacheManager::RecordStatsForFetchInternal, AsWeakPtr(),
- url, referrer.host(), fetch_time, info, size));
-}
-
-void PrecacheManager::RecordStatsForFetchInternal(
- const GURL& url,
- const std::string& referrer_host,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- int host_rank) {
- if (is_precaching_) {
- // Assume that precache is responsible for all requests made while
- // precaching is currently in progress.
- // TODO(sclittle): Make PrecacheFetcher explicitly mark precache-motivated
- // fetches, and use that to determine whether or not a fetch was motivated
- // by precaching.
- BrowserThread::PostTask(
- BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::RecordURLPrefetchMetrics,
- base::Unretained(precache_database_.get()), info));
- } else {
- bool is_connection_cellular =
- net::NetworkChangeNotifier::IsConnectionCellular(
- net::NetworkChangeNotifier::GetConnectionType());
-
- BrowserThread::PostTask(
- BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::RecordURLNonPrefetch,
- base::Unretained(precache_database_.get()), url, fetch_time,
- info, size, host_rank, is_connection_cellular));
- }
-}
-
-void PrecacheManager::ClearHistory() {
- // PrecacheDatabase::ClearHistory must run after PrecacheDatabase::Init has
- // finished. Using PostNonNestableTask guarantees this, by definition. See
- // base::SequencedTaskRunner for details.
- BrowserThread::PostNonNestableTask(
- BrowserThread::DB, FROM_HERE,
- base::Bind(&PrecacheDatabase::ClearHistory,
- base::Unretained(precache_database_.get())));
-}
-
-void PrecacheManager::Shutdown() {
- CancelPrecaching();
-}
-
-void PrecacheManager::OnDone() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- precache_fetcher_.reset();
-
- // Run completion callback if not null. It's null if the client is in the
- // Control group and CancelPrecaching is called before TopHosts computation
- // finishes.
- if (!precache_completion_callback_.is_null()) {
- precache_completion_callback_.Run(!is_precaching_);
- // Uninitialize the callback so that any scoped_refptrs in it are released.
- precache_completion_callback_.Reset();
- }
-
- is_precaching_ = false;
-}
-
-void PrecacheManager::OnManifestFetched(const std::string& host,
- const PrecacheManifest& manifest) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (delegate_)
- delegate_->OnManifestFetched(host, manifest);
-}
-
-void PrecacheManager::OnHostsReceived(
- const history::TopHostsList& host_counts) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- for (const auto& host_count : host_counts) {
- TopHost* top_host = unfinished_work_->add_top_host();
- top_host->set_hostname(host_count.first);
- top_host->set_visits(host_count.second);
- }
- InitializeAndStartFetcher();
-}
-
-void PrecacheManager::InitializeAndStartFetcher() {
- if (!is_precaching_) {
- // Don't start precaching if it was canceled while waiting for the list of
- // hosts.
- return;
- }
- // Start precaching.
- precache_fetcher_.reset(new PrecacheFetcher(
- content::BrowserContext::GetDefaultStoragePartition(browser_context_)
- ->GetURLRequestContext(),
- GURL(variations::GetVariationParamValue(kPrecacheFieldTrialName,
- kConfigURLParam)),
- variations::GetVariationParamValue(kPrecacheFieldTrialName,
- kManifestURLPrefixParam),
- std::move(unfinished_work_),
- metrics::HashName(
- base::FieldTrialList::FindFullName(kPrecacheFieldTrialName)),
- precache_database_->GetWeakPtr(),
- content::BrowserThread::GetTaskRunnerForThread(
- content::BrowserThread::DB),
- this));
- precache_fetcher_->Start();
-}
-
-void PrecacheManager::OnHostsReceivedThenDone(
- const history::TopHostsList& host_counts) {
- OnDone();
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/content/precache_manager.h b/chromium/components/precache/content/precache_manager.h
deleted file mode 100644
index 3f678e9fcab..00000000000
--- a/chromium/components/precache/content/precache_manager.h
+++ /dev/null
@@ -1,269 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PRECACHE_CONTENT_PRECACHE_MANAGER_H_
-#define COMPONENTS_PRECACHE_CONTENT_PRECACHE_MANAGER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <list>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "components/history/core/browser/history_types.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/precache/core/precache_fetcher.h"
-#include "net/disk_cache/disk_cache.h"
-#include "net/http/http_cache.h"
-#include "url/gurl.h"
-
-namespace base {
-class FilePath;
-class Time;
-}
-
-namespace content {
-class BrowserContext;
-}
-
-namespace data_reduction_proxy {
-class DataReductionProxySettings;
-}
-
-namespace history {
-class HistoryService;
-}
-
-namespace net {
-class HttpResponseInfo;
-}
-
-namespace syncer {
-class SyncService;
-}
-
-namespace precache {
-
-class PrecacheDatabase;
-class PrecacheUnfinishedWork;
-class PrecacheManifest;
-
-extern const char kPrecacheFieldTrialName[];
-
-// Visible for test.
-extern const char kMinCacheSizeParam[];
-size_t NumTopHosts();
-
-// Class that manages all precaching-related activities. Owned by the
-// BrowserContext that it is constructed for. Use
-// PrecacheManagerFactory::GetForBrowserContext to get an instance of this
-// class. All methods must be called on the UI thread unless indicated
-// otherwise.
-// TODO(sclittle): Delete precache history when browsing history is deleted.
-// http://crbug.com/326549
-class PrecacheManager : public KeyedService,
- public PrecacheFetcher::PrecacheDelegate,
- public base::SupportsWeakPtr<PrecacheManager> {
- public:
- class Delegate {
- public:
- // Called when a precache manifest has been successfully fetched and parsed.
- virtual void OnManifestFetched(const std::string& host,
- const PrecacheManifest& manifest) = 0;
- };
-
- typedef base::Callback<void(bool)> PrecacheCompletionCallback;
-
- PrecacheManager(content::BrowserContext* browser_context,
- const syncer::SyncService* sync_service,
- const history::HistoryService* history_service,
- const data_reduction_proxy::DataReductionProxySettings*
- data_reduction_proxy_settings,
- Delegate* delegate,
- const base::FilePath& db_path,
- std::unique_ptr<PrecacheDatabase> precache_database);
- ~PrecacheManager() override;
-
- // Returns true if the client is in the experiment group -- that is,
- // precaching is allowed based on user settings, and enabled as part of a
- // field trial or by commandline flag. Virtual for testing.
- virtual bool IsInExperimentGroup() const;
-
- // Returns true if the client is in the control group -- that is, precaching
- // is allowed based on user settings, and the browser is in the control group
- // of the field trial. Virtual for testing.
- virtual bool IsInControlGroup() const;
-
- // Returns true if precaching is allowed based on user settings. Virtual for
- // testing.
- virtual bool IsPrecachingAllowed() const;
-
- // Starts precaching resources that the user is predicted to fetch in the
- // future. If precaching is already currently in progress, then this method
- // does nothing. The |precache_completion_callback| will be passed true when
- // precaching finishes, and passed false when precaching abort due to failed
- // preconditions, but will not be run if precaching is canceled.
- void StartPrecaching(
- const PrecacheCompletionCallback& precache_completion_callback);
-
- // Cancels precaching if it is in progress.
- void CancelPrecaching();
-
- // Returns true if precaching is currently in progress, or false otherwise.
- bool IsPrecaching() const;
-
- // Posts a task to the DB thread to delete all history entries from the
- // database. Does not wait for completion of this task.
- void ClearHistory();
-
- // Update precache about an URL being fetched. Metrics related to precache are
- // updated and any ongoing precache will be cancelled if this is an user
- // initiated request. Should be called on UI thread.
- void UpdatePrecacheMetricsAndState(
- const GURL& url,
- const GURL& referrer,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- bool is_user_traffic,
- const base::Callback<void(base::Time)>& register_synthetic_trial);
-
- private:
- friend class PrecacheManagerTest;
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest, DeleteExpiredPrecacheHistory);
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest,
- RecordStatsForFetchDuringPrecaching);
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest, RecordStatsForFetchHTTP);
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest, RecordStatsForFetchHTTPS);
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest, RecordStatsForFetchInTopHosts);
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest,
- RecordStatsForFetchWithEmptyURL);
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest, RecordStatsForFetchWithNonHTTP);
- FRIEND_TEST_ALL_PREFIXES(PrecacheManagerTest,
- RecordStatsForFetchWithSizeZero);
-
- enum class AllowedType {
- ALLOWED,
- DISALLOWED,
- PENDING
- };
-
- // From KeyedService.
- void Shutdown() override;
-
- // From PrecacheFetcher::PrecacheDelegate.
- void OnDone() override;
- void OnManifestFetched(const std::string& host,
- const PrecacheManifest& manifest) override;
-
- // Registers the precache synthetic field trial for users whom the precache
- // task was run recently. |last_precache_time| is the last time precache task
- // was run.
- void RegisterSyntheticFieldTrial(const base::Time last_precache_time);
-
- // Callback when fetching unfinished work from storage is done.
- void OnGetUnfinishedWorkDone(
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work);
-
- // From history::HistoryService::TopHosts.
- void OnHostsReceived(const history::TopHostsList& host_counts);
-
- // Initializes and Starts a PrecacheFetcher with unfinished work.
- void InitializeAndStartFetcher();
-
- // From history::HistoryService::TopHosts. Used for the control group, which
- // gets the list of TopHosts for metrics purposes, but otherwise does nothing.
- void OnHostsReceivedThenDone(const history::TopHostsList& host_counts);
-
- // Chain of callbacks for StartPrecaching that make sure that we only precache
- // if there is a cache big enough.
- void PrecacheIfCacheIsBigEnough(
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
- void OnCacheBackendReceived(int net_error_code);
- void OnCacheSizeReceived(int cache_size_bytes);
- void OnCacheSizeReceivedInUIThread(int cache_size_bytes);
-
- // Returns true if precaching is allowed for the browser context.
- AllowedType PrecachingAllowed() const;
-
- // Update precache-related metrics in response to a URL being fetched.
- void RecordStatsForFetch(
- const GURL& url,
- const GURL& referrer,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- const base::Callback<void(base::Time)>& register_synthetic_trial,
- base::Time last_precache_time);
-
- // Update precache-related metrics in response to a URL being fetched. Called
- // by RecordStatsForFetch() by way of an asynchronous HistoryService callback.
- void RecordStatsForFetchInternal(const GURL& url,
- const std::string& referrer_host,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- int host_rank);
-
- // The browser context that owns this PrecacheManager.
- content::BrowserContext* const browser_context_;
-
- // The sync service corresponding to the browser context. Used to determine
- // whether precache can run. May be null.
- const syncer::SyncService* const sync_service_;
-
- // The history service corresponding to the browser context. Used to determine
- // the list of top hosts. May be null.
- const history::HistoryService* const history_service_;
-
- // The data reduction proxy settings object corresponding to the browser
- // context. Used to determine if the proxy is enabled.
- const data_reduction_proxy::DataReductionProxySettings* const
- data_reduction_proxy_settings_;
-
- // The Delegate corresponding to the browser context. Used to notify the
- // browser about a new available manifest. May be null.
- Delegate* delegate_;
-
- // The PrecacheFetcher used to precache resources. Should only be used on the
- // UI thread.
- std::unique_ptr<PrecacheFetcher> precache_fetcher_;
-
- // The callback that will be run if precaching finishes without being
- // canceled.
- PrecacheCompletionCallback precache_completion_callback_;
-
- // The PrecacheDatabase for tracking precache metrics. Should only be used on
- // the DB thread.
- std::unique_ptr<PrecacheDatabase> precache_database_;
-
- // Flag indicating whether or not precaching is currently in progress.
- bool is_precaching_;
-
- // Pointer to the backend of the cache. Required to get the size of the cache.
- // It is not owned and it is reset on demand via callbacks.
- // It should only be accessed from the IO thread.
- disk_cache::Backend* cache_backend_;
-
- // The minimum cache size allowed for precaching. Initialized by
- // StartPrecaching and read by OnCacheSizeReceivedInUIThread.
- int min_cache_size_bytes_;
-
- // Work that hasn't yet finished.
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work_;
-
- DISALLOW_COPY_AND_ASSIGN(PrecacheManager);
-};
-
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CONTENT_PRECACHE_MANAGER_H_
diff --git a/chromium/components/precache/content/precache_manager_unittest.cc b/chromium/components/precache/content/precache_manager_unittest.cc
deleted file mode 100644
index 4c3981260a8..00000000000
--- a/chromium/components/precache/content/precache_manager_unittest.cc
+++ /dev/null
@@ -1,692 +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/precache/content/precache_manager.h"
-
-#include <stddef.h>
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/command_line.h"
-#include "base/compiler_specific.h"
-#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/location.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/test/histogram_tester.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/history/core/browser/history_constants.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_types.h"
-#include "components/precache/core/precache_database.h"
-#include "components/precache/core/precache_switches.h"
-#include "components/precache/core/proto/unfinished_work.pb.h"
-#include "components/variations/variations_params_manager.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/simple/simple_backend_impl.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_response_info.h"
-#include "net/http/http_status_code.h"
-#include "net/test/gtest_util.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 "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace precache {
-
-namespace {
-
-using ::testing::_;
-using ::testing::ContainerEq;
-using ::testing::UnorderedElementsAre;
-using ::testing::Invoke;
-using ::testing::IsEmpty;
-using ::testing::Pair;
-using ::testing::SaveArg;
-
-const char kConfigURL[] = "http://config-url.com";
-const char kManifestURLPrefix[] = "http://manifest-url-prefix.com/";
-const char kGoodManifestURL[] =
- "http://manifest-url-prefix.com/good-manifest.com";
-const char kEvilManifestURL[] =
- "http://manifest-url-prefix.com/evil-manifest.com";
-
-class TestURLFetcherCallback {
- public:
- std::unique_ptr<net::FakeURLFetcher> CreateURLFetcher(
- const GURL& url,
- net::URLFetcherDelegate* delegate,
- const std::string& response_data,
- net::HttpStatusCode response_code,
- net::URLRequestStatus::Status status) {
- std::unique_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher(
- url, delegate, response_data, response_code, status));
-
- requested_urls_.insert(url);
- return fetcher;
- }
-
- const std::multiset<GURL>& requested_urls() const {
- return requested_urls_;
- }
-
- private:
- // Multiset with one entry for each URL requested.
- std::multiset<GURL> requested_urls_;
-};
-
-class MockHistoryService : public history::HistoryService {
- public:
- MockHistoryService() {
- ON_CALL(*this, HostRankIfAvailable(_, _))
- .WillByDefault(Invoke(
- [](const GURL& url, const base::Callback<void(int)>& callback) {
- callback.Run(history::kMaxTopHosts);
- }));
- }
-
- MOCK_CONST_METHOD2(TopHosts,
- void(size_t num_hosts, const TopHostsCallback& callback));
-
- MOCK_CONST_METHOD2(HostRankIfAvailable,
- void(const GURL& url,
- const base::Callback<void(int)>& callback));
-};
-
-class MockPrecacheManagerDelegate : public PrecacheManager::Delegate {
- public:
- MOCK_METHOD2(OnManifestFetched,
- void(const std::string& host, const PrecacheManifest& manifest));
-};
-
-ACTION_P(ReturnHosts, starting_hosts) {
- arg1.Run(starting_hosts);
-}
-
-class TestPrecacheCompletionCallback {
- public:
- TestPrecacheCompletionCallback() : was_on_done_called_(false) {}
-
- void OnDone(bool precaching_started) { was_on_done_called_ = true; }
-
- PrecacheManager::PrecacheCompletionCallback GetCallback() {
- return base::Bind(&TestPrecacheCompletionCallback::OnDone,
- base::Unretained(this));
- }
-
- bool was_on_done_called() const {
- return was_on_done_called_;
- }
-
- private:
- bool was_on_done_called_;
-};
-
-class PrecacheManagerUnderTest : public PrecacheManager {
- public:
- PrecacheManagerUnderTest(
- content::BrowserContext* browser_context,
- const syncer::SyncService* sync_service,
- const history::HistoryService* history_service,
- const data_reduction_proxy::DataReductionProxySettings*
- data_reduction_proxy_settings,
- Delegate* delegate_,
- const base::FilePath& db_path,
- std::unique_ptr<PrecacheDatabase> precache_database)
- : PrecacheManager(browser_context,
- sync_service,
- history_service,
- data_reduction_proxy_settings,
- delegate_,
- db_path,
- std::move(precache_database)),
- control_group_(false) {}
- bool IsInExperimentGroup() const override { return !control_group_; }
- bool IsInControlGroup() const override { return control_group_; }
- bool IsPrecachingAllowed() const override { return true; }
- void SetInControlGroup(bool in_control_group) {
- control_group_ = in_control_group;
- }
-
- private:
- bool control_group_;
-};
-
-} // namespace
-
-class PrecacheManagerTest : public testing::Test {
- public:
- PrecacheManagerTest()
- : factory_(nullptr,
- base::Bind(&TestURLFetcherCallback::CreateURLFetcher,
- base::Unretained(&url_callback_))) {}
-
- ~PrecacheManagerTest() {
- // precache_manager_'s constructor releases a PrecacheDatabase and deletes
- // it on the DB thread. PrecacheDatabase already has a pending Init call
- // which will assert in debug builds because the directory passed to it is
- // deleted. So manually ensure that the task is run before browser_context_
- // is destructed.
- base::RunLoop().RunUntilIdle();
- }
-
- protected:
- void SetUp() override {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kPrecacheConfigSettingsURL, kConfigURL);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kPrecacheManifestURLPrefix, kManifestURLPrefix);
-
- ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
- precache_database_ = new PrecacheDatabase;
- Reset(precache_database_);
- base::RunLoop().RunUntilIdle();
-
- // Make the fetch of the precache configuration settings fail. Precaching
- // should still complete normally in this case.
- factory_.SetFakeResponse(GURL(kConfigURL), "",
- net::HTTP_INTERNAL_SERVER_ERROR,
- net::URLRequestStatus::FAILED);
- info_.headers = new net::HttpResponseHeaders("");
- }
-
- // precache_manager_ assumes ownership of precache_database.
- void Reset(PrecacheDatabase* precache_database) {
- base::FilePath db_path = scoped_temp_dir_.GetPath().Append(
- base::FilePath(FILE_PATH_LITERAL("precache_database")));
- precache_manager_.reset(new PrecacheManagerUnderTest(
- &browser_context_, nullptr /* sync_service */, &history_service_,
- nullptr /* data_reduction_proxy_settings */,
- &precache_manager_delegate_, db_path,
- base::WrapUnique(precache_database)));
- }
-
- void Flush() { precache_database_->Flush(); }
-
- void RecordStatsForFetch(const GURL& url,
- const std::string& referrer_host,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- base::Time last_precache_time) {
- precache_manager_->RecordStatsForFetch(
- url, GURL(referrer_host), fetch_time, info, size,
- base::Bind(&PrecacheManagerTest::RegisterSyntheticFieldTrial,
- base::Unretained(this)),
- last_precache_time);
- }
-
- void RecordStatsForPrecacheFetch(const GURL& url,
- const std::string& referrer_host,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- base::Time last_precache_time) {
- RecordStatsForFetch(url, referrer_host, fetch_time, info, size,
- last_precache_time);
- precache_database_->RecordURLPrefetch(url, referrer_host, fetch_time,
- info.was_cached, size);
- }
-
- MOCK_METHOD1(RegisterSyntheticFieldTrial,
- void(base::Time last_precache_time));
-
- // Must be declared first so that it is destroyed last.
- content::TestBrowserThreadBundle test_browser_thread_bundle_;
- base::ScopedTempDir scoped_temp_dir_;
- PrecacheDatabase* precache_database_;
- content::TestBrowserContext browser_context_;
- std::unique_ptr<PrecacheManagerUnderTest> precache_manager_;
- TestURLFetcherCallback url_callback_;
- net::FakeURLFetcherFactory factory_;
- TestPrecacheCompletionCallback precache_callback_;
- testing::NiceMock<MockHistoryService> history_service_;
- testing::NiceMock<MockPrecacheManagerDelegate> precache_manager_delegate_;
- base::HistogramTester histograms_;
- net::HttpResponseInfo info_;
- variations::testing::VariationParamsManager variation_params_;
-};
-
-TEST_F(PrecacheManagerTest, StartAndFinishPrecaching) {
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- MockHistoryService::TopHostsCallback top_hosts_callback;
- EXPECT_CALL(history_service_, TopHosts(NumTopHosts(), _))
- .WillOnce(SaveArg<1>(&top_hosts_callback));
-
- factory_.SetFakeResponse(GURL(kConfigURL), "", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodManifestURL), "", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(precache_manager_->IsPrecaching());
-
- top_hosts_callback.Run(
- history::TopHostsList(1, std::make_pair("good-manifest.com", 1)));
- base::RunLoop().RunUntilIdle(); // For PrecacheFetcher.
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- EXPECT_TRUE(precache_callback_.was_on_done_called());
-
- std::multiset<GURL> expected_requested_urls;
- expected_requested_urls.insert(GURL(kConfigURL));
- expected_requested_urls.insert(GURL(kGoodManifestURL));
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-}
-
-TEST_F(PrecacheManagerTest, StartPrecachingWithGoodSizedCache) {
- variation_params_.SetVariationParams(kPrecacheFieldTrialName,
- {{kMinCacheSizeParam, "1"}});
-
- // Let's store something in the cache so we pass the min_cache_size threshold.
- disk_cache::Backend* cache_backend;
- {
- // Get the CacheBackend.
- net::TestCompletionCallback cb;
- net::HttpCache* cache =
- content::BrowserContext::GetDefaultStoragePartition(&browser_context_)
- ->GetURLRequestContext()
- ->GetURLRequestContext()
- ->http_transaction_factory()
- ->GetCache();
- CHECK_NE(nullptr, cache);
- int rv = cache->GetBackend(&cache_backend, cb.callback());
- CHECK_EQ(net::OK, cb.GetResult(rv));
- CHECK_NE(nullptr, cache_backend);
- CHECK_EQ(cache_backend, cache->GetCurrentBackend());
- }
- disk_cache::Entry* entry = nullptr;
- {
- // Create a cache Entry.
- net::TestCompletionCallback cb;
- int rv = cache_backend->CreateEntry("key", &entry, cb.callback());
- CHECK_EQ(net::OK, cb.GetResult(rv));
- CHECK_NE(nullptr, entry);
- }
- {
- // Store some data in the cache Entry.
- const std::string data(1, 'a');
- scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(data));
- net::TestCompletionCallback cb;
- int rv = entry->WriteData(0, 0, buffer.get(), buffer->size(), cb.callback(),
- false);
- entry->Close();
- CHECK_EQ(buffer->size(), cb.GetResult(rv));
- }
- {
- // Make sure everything went according to plan.
- net::TestCompletionCallback cb;
- int rv = cache_backend->CalculateSizeOfAllEntries(cb.callback());
- CHECK_LE(1, cb.GetResult(rv));
- }
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(precache_manager_->IsPrecaching());
- // Now it should be waiting for the top hosts.
-}
-
-TEST_F(PrecacheManagerTest, StartPrecachingStopsOnSmallCaches) {
- // We don't have any entry in the cache, so the reported cache_size = 0 and
- // thus it will fall below the threshold of 1.
- variation_params_.SetVariationParams(kPrecacheFieldTrialName,
- {{kMinCacheSizeParam, "1"}});
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- EXPECT_TRUE(precache_callback_.was_on_done_called());
- EXPECT_TRUE(url_callback_.requested_urls().empty());
-}
-
-TEST_F(PrecacheManagerTest, StartAndFinishPrecachingWithUnfinishedHosts) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("evil-manifest.com");
- unfinished_work->set_start_time(base::Time::Now().ToInternalValue());
- precache_database_->SaveUnfinishedWork(std::move(unfinished_work));
-
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- factory_.SetFakeResponse(GURL(kConfigURL), "", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(
- GURL(kEvilManifestURL), "",
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- ASSERT_TRUE(precache_database_->GetLastPrecacheTimestamp().is_null());
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- EXPECT_TRUE(precache_manager_->IsPrecaching());
-
- base::RunLoop().RunUntilIdle(); // For PrecacheFetcher.
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- EXPECT_TRUE(precache_callback_.was_on_done_called());
-
- // The LastPrecacheTimestamp has been set.
- EXPECT_FALSE(precache_database_->GetLastPrecacheTimestamp().is_null());
-
- std::multiset<GURL> expected_requested_urls;
- expected_requested_urls.insert(GURL(kConfigURL));
- expected_requested_urls.insert(GURL(kEvilManifestURL));
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-}
-
-TEST_F(PrecacheManagerTest,
- StartAndCancelPrecachingBeforeUnfinishedWorkRetrieved) {
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- EXPECT_TRUE(precache_manager_->IsPrecaching());
- precache_manager_->CancelPrecaching();
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(precache_callback_.was_on_done_called());
-}
-
-TEST_F(PrecacheManagerTest, StartAndCancelPrecachingBeforeTopHostsCompleted) {
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- MockHistoryService::TopHostsCallback top_hosts_callback;
- EXPECT_CALL(history_service_, TopHosts(NumTopHosts(), _))
- .WillOnce(SaveArg<1>(&top_hosts_callback));
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- EXPECT_TRUE(precache_manager_->IsPrecaching());
-
- precache_manager_->CancelPrecaching();
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- base::RunLoop().RunUntilIdle();
-
- top_hosts_callback.Run(
- history::TopHostsList(1, std::make_pair("starting-url.com", 1)));
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- EXPECT_FALSE(precache_callback_.was_on_done_called());
-}
-
-TEST_F(PrecacheManagerTest, StartAndCancelPrecachingBeforeURLsReceived) {
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- MockHistoryService::TopHostsCallback top_hosts_callback;
- EXPECT_CALL(history_service_, TopHosts(NumTopHosts(), _))
- .WillOnce(SaveArg<1>(&top_hosts_callback));
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- EXPECT_TRUE(precache_manager_->IsPrecaching());
-
- precache_manager_->CancelPrecaching();
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- top_hosts_callback.Run(
- history::TopHostsList(1, std::make_pair("starting-url.com", 1)));
- base::RunLoop().RunUntilIdle(); // For PrecacheFetcher.
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- EXPECT_FALSE(precache_callback_.was_on_done_called());
- EXPECT_TRUE(url_callback_.requested_urls().empty());
-}
-
-TEST_F(PrecacheManagerTest, StartAndCancelPrecachingAfterURLsReceived) {
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- PrecacheManifest good_manifest;
- good_manifest.add_resource()->set_url("http://good-resource.com");
-
- EXPECT_CALL(history_service_, TopHosts(NumTopHosts(), _))
- .WillOnce(ReturnHosts(
- history::TopHostsList(1, std::make_pair("starting-url.com", 1))));
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
-
- EXPECT_TRUE(precache_manager_->IsPrecaching());
- // Run a task to get unfinished work, and to get hosts.
- // We need to call run_loop.Run as many times as needed to go through the
- // chain of callbacks :-(.
- for (int i = 0; i < 4; ++i) {
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop.QuitClosure());
- run_loop.Run();
- }
- // base::RunLoop().RunUntilIdle();
- precache_manager_->CancelPrecaching();
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- base::RunLoop().RunUntilIdle(); // For PrecacheFetcher.
- EXPECT_FALSE(precache_manager_->IsPrecaching());
- EXPECT_FALSE(precache_callback_.was_on_done_called());
-
- std::multiset<GURL> expected_requested_urls;
- expected_requested_urls.insert(GURL(kConfigURL));
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-}
-
-// TODO(rajendrant): Add unittests for
-// PrecacheUtil::UpdatePrecacheMetricsAndState() for more test coverage.
-TEST_F(PrecacheManagerTest, RecordStatsForFetchWithSizeZero) {
- // Fetches with size 0 should be ignored.
- RecordStatsForPrecacheFetch(GURL("http://url.com"), "", base::Time(), info_,
- 0, base::Time());
- base::RunLoop().RunUntilIdle();
- histograms_.ExpectTotalCount("Precache.Freshness.Prefetch", 0);
-}
-
-TEST_F(PrecacheManagerTest, RecordStatsForFetchWithNonHTTP) {
- // Fetches for URLs with schemes other than HTTP or HTTPS should be ignored.
- RecordStatsForPrecacheFetch(GURL("ftp://ftp.com"), "", base::Time(), info_,
- 1000, base::Time());
- base::RunLoop().RunUntilIdle();
- histograms_.ExpectTotalCount("Precache.Freshness.Prefetch", 0);
-}
-
-TEST_F(PrecacheManagerTest, RecordStatsForFetchWithEmptyURL) {
- // Fetches for empty URLs should be ignored.
- RecordStatsForPrecacheFetch(GURL(), "", base::Time(), info_, 1000,
- base::Time());
- base::RunLoop().RunUntilIdle();
- histograms_.ExpectTotalCount("Precache.Freshness.Prefetch", 0);
-}
-
-TEST_F(PrecacheManagerTest, RecordStatsForFetchDuringPrecaching) {
- EXPECT_CALL(history_service_, TopHosts(NumTopHosts(), _))
- .WillOnce(ReturnHosts(history::TopHostsList()));
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
-
- EXPECT_TRUE(precache_manager_->IsPrecaching());
- RecordStatsForPrecacheFetch(GURL("http://url.com"), std::string(),
- base::Time(), info_, 1000, base::Time());
- base::RunLoop().RunUntilIdle();
- precache_manager_->CancelPrecaching();
-
- // For PrecacheFetcher and RecordURLPrecached.
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(
- histograms_.GetTotalCountsForPrefix("Precache."),
- UnorderedElementsAre(Pair("Precache.CacheStatus.Prefetch", 1),
- Pair("Precache.CacheSize.AllEntries", 1),
- Pair("Precache.DownloadedPrecacheMotivated", 1),
- Pair("Precache.Fetch.PercentCompleted", 1),
- Pair("Precache.Fetch.ResponseBytes.Network", 1),
- Pair("Precache.Fetch.ResponseBytes.Total", 1),
- Pair("Precache.Fetch.TimeToComplete", 1),
- Pair("Precache.Freshness.Prefetch", 1)));
-}
-
-TEST_F(PrecacheManagerTest, RegistersSyntheticFieldTrial) {
- base::Time now = base::Time::Now();
-
- EXPECT_CALL(history_service_, TopHosts(NumTopHosts(), _))
- .WillOnce(ReturnHosts(history::TopHostsList()));
- EXPECT_CALL(*this, RegisterSyntheticFieldTrial(now));
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
-
- EXPECT_TRUE(precache_manager_->IsPrecaching());
- RecordStatsForPrecacheFetch(GURL("http://url.com"), std::string(),
- base::Time(), info_, 1000,
- now /* last_precache_time */);
- base::RunLoop().RunUntilIdle();
- precache_manager_->CancelPrecaching();
-}
-
-TEST_F(PrecacheManagerTest, RecordStatsForFetchHTTP) {
- RecordStatsForFetch(GURL("http://http-url.com"), "", base::Time(), info_,
- 1000, base::Time());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
- UnorderedElementsAre(
- Pair("Precache.DownloadedNonPrecache", 1),
- Pair("Precache.CacheStatus.NonPrefetch", 1),
- Pair("Precache.CacheStatus.NonPrefetch.NonTopHosts", 1)));
-}
-
-TEST_F(PrecacheManagerTest, RecordStatsForFetchHTTPS) {
- RecordStatsForFetch(GURL("https://https-url.com"), "", base::Time(), info_,
- 1000, base::Time());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
- UnorderedElementsAre(
- Pair("Precache.DownloadedNonPrecache", 1),
- Pair("Precache.CacheStatus.NonPrefetch", 1),
- Pair("Precache.CacheStatus.NonPrefetch.NonTopHosts", 1)));
-}
-
-TEST_F(PrecacheManagerTest, RecordStatsForFetchInTopHosts) {
- EXPECT_CALL(history_service_,
- HostRankIfAvailable(GURL("http://referrer.com"), _))
- .WillOnce(Invoke(
- [](const GURL& url, const base::Callback<void(int)>& callback) {
- callback.Run(0);
- }));
- RecordStatsForFetch(GURL("http://http-url.com"), "http://referrer.com",
- base::Time(), info_, 1000, base::Time());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
- UnorderedElementsAre(
- Pair("Precache.DownloadedNonPrecache", 1),
- Pair("Precache.CacheStatus.NonPrefetch", 1),
- Pair("Precache.CacheStatus.NonPrefetch.TopHosts", 1)));
-}
-
-TEST_F(PrecacheManagerTest, DeleteExpiredPrecacheHistory) {
- // TODO(twifkak): Split this into multiple tests.
- base::HistogramTester::CountsMap expected_histogram_count_map;
-
- // This test has to use Time::Now() because StartPrecaching uses Time::Now().
- const base::Time kCurrentTime = base::Time::Now();
- EXPECT_CALL(history_service_, TopHosts(NumTopHosts(), _))
- .Times(2)
- .WillRepeatedly(ReturnHosts(history::TopHostsList()));
-
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- EXPECT_TRUE(precache_manager_->IsPrecaching());
-
- // Precache a bunch of URLs, with different fetch times.
- RecordStatsForPrecacheFetch(GURL("http://old-fetch.com"), std::string(),
- kCurrentTime - base::TimeDelta::FromDays(61),
- info_, 1000, base::Time());
- RecordStatsForPrecacheFetch(GURL("http://recent-fetch.com"), std::string(),
- kCurrentTime - base::TimeDelta::FromDays(59),
- info_, 1000, base::Time());
- RecordStatsForPrecacheFetch(GURL("http://yesterday-fetch.com"), std::string(),
- kCurrentTime - base::TimeDelta::FromDays(1),
- info_, 1000, base::Time());
- expected_histogram_count_map["Precache.CacheStatus.Prefetch"] += 3;
- expected_histogram_count_map["Precache.CacheSize.AllEntries"]++;
- expected_histogram_count_map["Precache.DownloadedPrecacheMotivated"] += 3;
- expected_histogram_count_map["Precache.Fetch.PercentCompleted"]++;
- expected_histogram_count_map["Precache.Fetch.ResponseBytes.Network"]++;
- expected_histogram_count_map["Precache.Fetch.ResponseBytes.Total"]++;
- expected_histogram_count_map["Precache.Fetch.TimeToComplete"]++;
- expected_histogram_count_map["Precache.Freshness.Prefetch"] += 3;
- base::RunLoop().RunUntilIdle();
-
- precache_manager_->CancelPrecaching();
- base::RunLoop().RunUntilIdle();
-
- // Disable pause-resume.
- precache_database_->DeleteUnfinishedWork();
- base::RunLoop().RunUntilIdle();
-
- // For PrecacheFetcher and RecordURLPrecached.
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
- ContainerEq(expected_histogram_count_map));
-
- // The expired precache will be deleted during precaching this time.
- precache_manager_->StartPrecaching(precache_callback_.GetCallback());
- EXPECT_TRUE(precache_manager_->IsPrecaching());
- base::RunLoop().RunUntilIdle();
-
- // The precache fetcher runs until done, which records these histograms,
- // and then cancels precaching, which records these histograms again.
- // In practice
- expected_histogram_count_map["Precache.CacheSize.AllEntries"]++;
- expected_histogram_count_map["Precache.Fetch.PercentCompleted"]++;
- expected_histogram_count_map["Precache.Fetch.ResponseBytes.Network"]++;
- expected_histogram_count_map["Precache.Fetch.ResponseBytes.Total"]++;
- // For PrecacheFetcher and RecordURLPrecached.
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(precache_manager_->IsPrecaching());
-
- // A fetch for the same URL as the expired precache was served from the cache,
- // but it isn't reported as saved bytes because it had expired in the precache
- // history.
- info_.was_cached = true; // From now on all fetches are cached.
- RecordStatsForFetch(GURL("http://old-fetch.com"), "", kCurrentTime, info_,
- 1000, base::Time());
- expected_histogram_count_map["Precache.Fetch.TimeToComplete"]++;
- expected_histogram_count_map["Precache.CacheStatus.NonPrefetch"]++;
- expected_histogram_count_map
- ["Precache.CacheStatus.NonPrefetch.NonTopHosts"]++;
- expected_histogram_count_map["Precache.TimeSinceLastPrecache"] += 1;
-
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
- ContainerEq(expected_histogram_count_map));
-
- // The other precaches should not have expired, so the following fetches from
- // the cache should count as saved bytes.
- RecordStatsForFetch(GURL("http://recent-fetch.com"), "", kCurrentTime, info_,
- 1000, base::Time());
- RecordStatsForFetch(GURL("http://yesterday-fetch.com"), "", kCurrentTime,
- info_, 1000, base::Time());
- expected_histogram_count_map["Precache.CacheStatus.NonPrefetch"] += 2;
- expected_histogram_count_map
- ["Precache.CacheStatus.NonPrefetch.FromPrecache"] += 2;
- expected_histogram_count_map
- ["Precache.CacheStatus.NonPrefetch.NonTopHosts"] += 2;
- expected_histogram_count_map["Precache.Saved"] += 2;
- expected_histogram_count_map["Precache.TimeSinceLastPrecache"] += 2;
- expected_histogram_count_map["Precache.Saved.Freshness"] = 2;
-
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
- ContainerEq(expected_histogram_count_map));
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/BUILD.gn b/chromium/components/precache/core/BUILD.gn
deleted file mode 100644
index 00bde867301..00000000000
--- a/chromium/components/precache/core/BUILD.gn
+++ /dev/null
@@ -1,90 +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.
-
-import("//third_party/protobuf/proto_library.gni")
-
-# These values are duplicated in the GYP build in:
-# //components/precache/precache_defines.gypi
-precache_config_settings_url =
- "https://www.gstatic.com/chrome/wifiprefetch/precache_config"
-precache_manifest_url_prefix =
- "https://www.gstatic.com/chrome/wifiprefetch/hosts/"
-
-config("precache_config") {
- defines = [
- "PRECACHE_CONFIG_SETTINGS_URL=\"$precache_config_settings_url\"",
- "PRECACHE_MANIFEST_URL_PREFIX=\"$precache_manifest_url_prefix\"",
- ]
-}
-
-static_library("core") {
- sources = [
- "fetcher_pool.h",
- "precache_database.cc",
- "precache_database.h",
- "precache_fetcher.cc",
- "precache_fetcher.h",
- "precache_manifest_util.cc",
- "precache_manifest_util.h",
- "precache_referrer_host_table.cc",
- "precache_referrer_host_table.h",
- "precache_session_table.cc",
- "precache_session_table.h",
- "precache_switches.cc",
- "precache_switches.h",
- "precache_url_table.cc",
- "precache_url_table.h",
- ]
-
- # Note the GYP build sets this as direct dependent settings, but this is
- # only used to share the settings with the unit tests. Instead, we just
- # set this config for the necessary targets manually.
- configs += [ ":precache_config" ]
-
- deps = [
- ":proto",
- "//base",
- "//components/data_use_measurement/core",
- "//components/history/core/browser",
- "//components/prefs",
- "//net",
- "//sql",
- "//url",
- ]
-}
-
-proto_library("proto") {
- sources = [
- "proto/precache.proto",
- "proto/quota.proto",
- "proto/timestamp.proto",
- "proto/unfinished_work.proto",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "fetcher_pool_unittest.cc",
- "precache_database_unittest.cc",
- "precache_fetcher_unittest.cc",
- "precache_referrer_host_table_unittest.cc",
- "precache_session_table_unittest.cc",
- "precache_url_table_unittest.cc",
- ]
-
- configs += [ ":precache_config" ]
-
- deps = [
- ":core",
- ":proto",
- "//base",
- "//base/test:test_support",
- "//components/history/core/browser",
- "//net:test_support",
- "//sql",
- "//testing/gmock",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/precache/core/DEPS b/chromium/components/precache/core/DEPS
deleted file mode 100644
index 952a6db9da3..00000000000
--- a/chromium/components/precache/core/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-include_rules = [
- "+components/data_use_measurement/core",
- "+net/base",
- "+net/http",
- "+net/url_request",
- "+sql",
-]
diff --git a/chromium/components/precache/core/fetcher_pool.h b/chromium/components/precache/core/fetcher_pool.h
deleted file mode 100644
index 97cf194c450..00000000000
--- a/chromium/components/precache/core/fetcher_pool.h
+++ /dev/null
@@ -1,87 +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_PRECACHE_CORE_FETCHER_POOL_H_
-#define COMPONENTS_PRECACHE_CORE_FETCHER_POOL_H_
-
-#include <memory>
-#include <unordered_map>
-
-#include "base/gtest_prod_util.h"
-#include "base/logging.h"
-
-namespace precache {
-
-// FetcherPool that accepts a limited number of elements.
-//
-// FetcherPool is particularly suited for having multiple URLFetchers running
-// in parallel.
-//
-// It doesn't enqueue the elements above a defined capacity. The callsite must
-// check for IsAvailable before calling Start.
-//
-// Example usage:
-// std::list<GURL> pending_urls = ...;
-// FetcherPool<net::URLFetcher> pool(max_parallel_fetches);
-// std::function<void()> start_next_batch =
-// [&pending_urls, &pool]() {
-// while (!pending_urls.empty() && pool.IsAvailable()) {
-// pool.Add(CreateAndStartUrlFetcher(pending_urls.front()));
-// pending_urls.pop_front();
-// }
-// };
-// // The URLFetcherDelegate of the created URLFetchers MUST call
-// // pool.Release(url_fetcher) and start_next_batch() as part of
-// // OnURLFetchComplete.
-// start_next_batch();
-template <typename T>
-class FetcherPool {
- public:
- explicit FetcherPool(size_t max_size) : max_size_(max_size){};
- virtual ~FetcherPool(){};
-
- // Takes ownership and adds the given |element| to the pool.
- // The element will live until its deletion.
- void Add(std::unique_ptr<T> element) {
- DCHECK(IsAvailable()) << "FetcherPool size exceeded. "
- "Did you check IsAvailable?";
- DCHECK(element) << "The element cannot be null.";
- DCHECK(elements_.find(element.get()) == elements_.end())
- << "The pool already contains the given element.";
- elements_[element.get()] = std::move(element);
- }
-
- // Deletes the given |element| from the pool.
- void Delete(const T& element) {
- DCHECK(elements_.find(&element) != elements_.end())
- << "The pool doesn't contain the given element.";
- elements_.erase(&element);
- }
-
- // Deletes all the elements in the pool.
- void DeleteAll() { elements_.clear(); }
-
- // Returns true iff the pool is empty.
- bool IsEmpty() const { return elements_.empty(); }
-
- // Returns true iff the pool can accept a new element.
- bool IsAvailable() const { return max_size_ > elements_.size(); }
-
- const std::unordered_map<const T*, std::unique_ptr<T>>& elements() const {
- return elements_;
- }
-
- // Returns the maximum size of the pool.
- size_t max_size() const { return max_size_; }
-
- private:
- const size_t max_size_;
- std::unordered_map<const T*, std::unique_ptr<T>> elements_;
-
- DISALLOW_COPY_AND_ASSIGN(FetcherPool);
-};
-
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CORE_FETCHER_POOL_H_
diff --git a/chromium/components/precache/core/fetcher_pool_unittest.cc b/chromium/components/precache/core/fetcher_pool_unittest.cc
deleted file mode 100644
index f3d9de48076..00000000000
--- a/chromium/components/precache/core/fetcher_pool_unittest.cc
+++ /dev/null
@@ -1,223 +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/precache/core/fetcher_pool.h"
-
-#include <algorithm>
-#include <array>
-#include <functional>
-#include <list>
-#include <memory>
-#include <string>
-
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_status.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace precache {
-
-namespace {
-
-using net::FakeURLFetcher;
-using net::HTTP_OK;
-using net::URLFetcher;
-using net::URLRequestStatus;
-using ::testing::_;
-using ::testing::Invoke;
-
-class MockURLFetcherDelegate : public net::URLFetcherDelegate {
- public:
- MockURLFetcherDelegate() {};
- virtual ~MockURLFetcherDelegate() {};
-
- MOCK_METHOD1(OnURLFetchComplete, void(const URLFetcher*));
- MOCK_METHOD4(OnURLFetchDownloadProgress,
- void(const URLFetcher* source,
- int64_t current,
- int64_t total,
- int64_t current_network_bytes));
- MOCK_METHOD3(OnURLFetchUploadProgress,
- void(const URLFetcher* source, int64_t current, int64_t total));
-};
-
-TEST(FetcherPoolTest, AddDelete) {
- // It also tests IsAvailable.
- base::MessageLoop loop;
- MockURLFetcherDelegate delegate;
- std::unique_ptr<URLFetcher> url_fetcher(
- new FakeURLFetcher(GURL("http://a.com"), &delegate, "irrelevant", HTTP_OK,
- URLRequestStatus::SUCCESS));
- URLFetcher* url_fetcher_ptr = url_fetcher.get();
-
- FetcherPool<URLFetcher> pool(1);
- EXPECT_TRUE(pool.IsAvailable());
- EXPECT_TRUE(pool.IsEmpty());
- pool.Add(std::move(url_fetcher));
- url_fetcher_ptr->Start();
- EXPECT_FALSE(pool.IsAvailable());
- EXPECT_FALSE(pool.IsEmpty());
- EXPECT_CALL(delegate, OnURLFetchComplete(url_fetcher_ptr));
-
- base::RunLoop().RunUntilIdle();
-
- pool.Delete(*url_fetcher_ptr);
- EXPECT_TRUE(pool.IsEmpty());
- EXPECT_TRUE(pool.IsAvailable());
-}
-
-TEST(FetcherPoolTest, Delete) {
- const size_t kSize = 42;
- base::MessageLoop loop;
- MockURLFetcherDelegate delegate;
- std::unique_ptr<URLFetcher> url_fetcher(
- new FakeURLFetcher(GURL("http://a.com"), &delegate, "irrelevant", HTTP_OK,
- URLRequestStatus::SUCCESS));
- URLFetcher* url_fetcher_ptr = url_fetcher.get();
-
- FetcherPool<URLFetcher> pool(kSize);
- pool.Add(std::move(url_fetcher));
- url_fetcher_ptr->Start();
- pool.Delete(*url_fetcher_ptr);
-
- EXPECT_TRUE(pool.IsEmpty());
-
- EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0);
- base::RunLoop().RunUntilIdle();
-}
-
-TEST(FetcherPoolTest, ParallelURLFetchers) {
- // It also tests IsEmpty.
- const size_t kSize = 42;
- base::MessageLoop loop;
- MockURLFetcherDelegate delegate;
- FetcherPool<URLFetcher> pool(kSize);
- std::string urls[] = {"http://a.com", "http://b.com", "http://c.com"};
- // To make sure that nothing slip through while setting the expectations.
- EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0);
- int num_requests_in_flight = 0;
- for (const auto& url : urls) {
- std::unique_ptr<URLFetcher> url_fetcher(
- new FakeURLFetcher(GURL(url), &delegate, "irrelevant", HTTP_OK,
- URLRequestStatus::SUCCESS));
- num_requests_in_flight++;
- url_fetcher->Start();
- pool.Add(std::move(url_fetcher));
- EXPECT_FALSE(pool.IsEmpty());
- EXPECT_TRUE(pool.IsAvailable());
- }
- EXPECT_CALL(delegate, OnURLFetchComplete(_))
- .Times(3)
- .WillRepeatedly(Invoke([&pool](const URLFetcher* fetcher) {
- EXPECT_TRUE(fetcher);
- pool.Delete(*fetcher);
- }));
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(pool.IsEmpty());
- EXPECT_TRUE(pool.IsAvailable());
-}
-
-TEST(FetcherPoolTest, DeleteAll) {
- const size_t kSize = 42;
- base::MessageLoop loop;
- MockURLFetcherDelegate delegate;
- FetcherPool<URLFetcher> pool(kSize);
- std::string urls[] = {"http://a.com", "http://b.com", "http://c.com"};
- EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0);
- for (const auto& url : urls) {
- std::unique_ptr<URLFetcher> url_fetcher(
- new FakeURLFetcher(GURL(url), &delegate, "irrelevant", HTTP_OK,
- URLRequestStatus::SUCCESS));
- url_fetcher->Start();
- pool.Add(std::move(url_fetcher));
- }
-
- pool.DeleteAll();
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(pool.IsEmpty());
- EXPECT_TRUE(pool.IsAvailable());
-}
-
-#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
-
-TEST(FetcherPoolTest, AddTooManyURLFetchers) {
- MockURLFetcherDelegate delegate;
- FetcherPool<URLFetcher> pool(0);
- std::unique_ptr<URLFetcher> url_fetcher(
- new FakeURLFetcher(GURL("http://queso.es"), &delegate, "irrelevant",
- HTTP_OK, URLRequestStatus::SUCCESS));
- EXPECT_DEBUG_DEATH(pool.Add(std::move(url_fetcher)),
- "FetcherPool size exceeded");
-}
-
-TEST(FetcherPoolTest, AddNullURLFetcher) {
- FetcherPool<URLFetcher> pool(1);
- std::unique_ptr<URLFetcher> null_ptr;
- EXPECT_DEBUG_DEATH(pool.Add(std::move(null_ptr)), "cannot be null");
-}
-
-TEST(FetcherPoolTest, DeleteUnregisteredURLFetcher) {
- MockURLFetcherDelegate delegate;
- FetcherPool<URLFetcher> pool(1);
- FakeURLFetcher url_fetcher(GURL("http://queso.es"), &delegate, "irrelevant",
- HTTP_OK, URLRequestStatus::SUCCESS);
- EXPECT_DEBUG_DEATH(pool.Delete(url_fetcher),
- "doesn't contain the given element");
-}
-
-#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
-
-TEST(FetcherPoolTest, ExampleUsage) {
- base::MessageLoop loop;
- FetcherPool<URLFetcher> pool(2);
- MockURLFetcherDelegate delegate;
-
- std::list<GURL> pending_urls{
- {GURL("http://a.com"), GURL("http://b.com"), GURL("http://c.com")}};
-
- std::function<void()> start_next_batch = [&pending_urls, &pool, &delegate]() {
- while (!pending_urls.empty() && pool.IsAvailable()) {
- // Called CreateAndStartUrlFetcher in the documentation.
- std::unique_ptr<URLFetcher> fetcher(
- new FakeURLFetcher(GURL(pending_urls.front()), &delegate,
- "irrelevant", HTTP_OK, URLRequestStatus::SUCCESS));
- fetcher->Start();
- pending_urls.pop_front();
- pool.Add(std::move(fetcher));
- }
- };
-
- EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0); // 3 and no more.
- EXPECT_CALL(delegate, OnURLFetchComplete(_))
- .Times(pending_urls.size())
- .WillRepeatedly(
- Invoke([&pool, &start_next_batch](const URLFetcher* fetcher) {
- EXPECT_TRUE(fetcher);
- pool.Delete(*fetcher);
- start_next_batch();
- }));
-
- start_next_batch();
- EXPECT_FALSE(pool.IsEmpty());
- EXPECT_FALSE(pool.IsAvailable());
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(pool.IsEmpty());
- EXPECT_TRUE(pool.IsAvailable());
-}
-
-} // namespace
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_database.cc b/chromium/components/precache/core/precache_database.cc
deleted file mode 100644
index f30d8cb9bea..00000000000
--- a/chromium/components/precache/core/precache_database.cc
+++ /dev/null
@@ -1,467 +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/precache/core/precache_database.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/location.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/history/core/browser/history_constants.h"
-#include "components/precache/core/proto/unfinished_work.pb.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_response_info.h"
-#include "sql/connection.h"
-#include "sql/transaction.h"
-#include "url/gurl.h"
-
-namespace {
-
-// The number of days old that an entry in the precache URL table can be before
-// it is considered "old" and is removed from the table.
-const int kPrecacheHistoryExpiryPeriodDays = 60;
-
-const int kSecondsInMinute = 60;
-const int kSecondsInHour = kSecondsInMinute * 60;
-
-} // namespace
-
-namespace precache {
-
-PrecacheDatabase::PrecacheDatabase()
- : is_flush_posted_(false), weak_factory_(this) {
- // A PrecacheDatabase can be constructed on any thread.
- thread_checker_.DetachFromThread();
-}
-
-PrecacheDatabase::~PrecacheDatabase() {
- // The destructor must not run on the UI thread, as it may trigger IO
- // operations via sql::Connection's destructor.
- DCHECK(thread_checker_.CalledOnValidThread());
-}
-
-bool PrecacheDatabase::Init(const base::FilePath& db_path) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!db_); // Init must only be called once.
-
- db_.reset(new sql::Connection());
- db_->set_histogram_tag("Precache");
-
- if (!db_->Open(db_path)) {
- // Don't initialize the URL table if unable to access
- // the database.
- return false;
- }
-
- if (!precache_url_table_.Init(db_.get()) ||
- !precache_referrer_host_table_.Init(db_.get()) ||
- !precache_session_table_.Init(db_.get())) {
- // Raze and close the database connection to indicate that it's not usable,
- // and so that the database will be created anew next time, in case it's
- // corrupted.
- db_->RazeAndClose();
- return false;
- }
- return true;
-}
-
-void PrecacheDatabase::DeleteExpiredPrecacheHistory(
- const base::Time& current_time) {
- if (!IsDatabaseAccessible()) {
- // Do nothing if unable to access the database.
- return;
- }
-
- // Delete old precache history that has expired.
- base::Time delete_end = current_time - base::TimeDelta::FromDays(
- kPrecacheHistoryExpiryPeriodDays);
- buffered_writes_.push_back(
- base::Bind(&PrecacheURLTable::DeleteAllPrecachedBefore,
- base::Unretained(&precache_url_table_), delete_end));
- buffered_writes_.push_back(
- base::Bind(&PrecacheReferrerHostTable::DeleteAllEntriesBefore,
- base::Unretained(&precache_referrer_host_table_), delete_end));
- Flush();
-}
-
-void PrecacheDatabase::ClearHistory() {
- if (!IsDatabaseAccessible()) {
- // Do nothing if unable to access the database.
- return;
- }
-
- buffered_writes_.push_back(base::Bind(
- &PrecacheURLTable::DeleteAll, base::Unretained(&precache_url_table_)));
- buffered_writes_.push_back(
- base::Bind(&PrecacheReferrerHostTable::DeleteAll,
- base::Unretained(&precache_referrer_host_table_)));
- Flush();
-}
-
-void PrecacheDatabase::SetLastPrecacheTimestamp(const base::Time& time) {
- last_precache_timestamp_ = time;
-
- if (!IsDatabaseAccessible()) {
- // Do nothing if unable to access the database.
- return;
- }
-
- buffered_writes_.push_back(
- base::Bind(&PrecacheSessionTable::SetLastPrecacheTimestamp,
- base::Unretained(&precache_session_table_), time));
- MaybePostFlush();
-}
-
-base::Time PrecacheDatabase::GetLastPrecacheTimestamp() {
- if (last_precache_timestamp_.is_null() && IsDatabaseAccessible()) {
- last_precache_timestamp_ =
- precache_session_table_.GetLastPrecacheTimestamp();
- }
- return last_precache_timestamp_;
-}
-
-PrecacheReferrerHostEntry PrecacheDatabase::GetReferrerHost(
- const std::string& referrer_host) {
- DCHECK(thread_checker_.CalledOnValidThread());
- return precache_referrer_host_table_.GetReferrerHost(referrer_host);
-}
-
-void PrecacheDatabase::GetURLListForReferrerHost(
- int64_t referrer_host_id,
- std::vector<GURL>* used_urls,
- std::vector<GURL>* downloaded_urls) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_NE(PrecacheReferrerHostEntry::kInvalidId, referrer_host_id);
-
- // Flush any pending writes to the URL and referrer host tables.
- Flush();
-
- precache_url_table_.GetURLListForReferrerHost(referrer_host_id, used_urls,
- downloaded_urls);
- precache_url_table_.SetDownloadReported(referrer_host_id);
-}
-
-void PrecacheDatabase::RecordURLPrefetchMetrics(
- const net::HttpResponseInfo& info) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.Prefetch",
- info.cache_entry_status,
- net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
-
- DCHECK(info.headers) << "The headers are required to get the freshness.";
- if (info.headers) {
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Precache.Freshness.Prefetch",
- info.headers->GetFreshnessLifetimes(info.response_time)
- .freshness.InSeconds(),
- base::TimeDelta::FromMinutes(5).InSeconds() /* min */,
- base::TimeDelta::FromDays(356).InSeconds() /* max */,
- 100 /* bucket_count */);
- }
-}
-
-void PrecacheDatabase::RecordURLPrefetch(const GURL& url,
- const std::string& referrer_host,
- const base::Time& fetch_time,
- bool was_cached,
- int64_t size) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (!IsDatabaseAccessible()) {
- // Don't track anything if unable to access the database.
- return;
- }
-
- if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) {
- // If the URL for this fetch is in the write buffer, then flush the write
- // buffer.
- Flush();
- }
-
- if (!was_cached) {
- // The precache only counts as overhead if it was downloaded over the
- // network.
- UMA_HISTOGRAM_COUNTS("Precache.DownloadedPrecacheMotivated",
- static_cast<base::HistogramBase::Sample>(size));
- }
-
- // Use the URL table to keep track of URLs. URLs that are fetched via network
- // or already in the cache due to prior precaching are recorded as
- // precache-motivated. URLs that came from the cache and not recorded as
- // precached previously, were already in the cache because of user browsing.
- // Therefore, this precache will not be considered as precache-motivated,
- // since it had no significant effect (besides a possible revalidation and a
- // change in the cache LRU priority). If a row for the URL already exists,
- // then the timestamp is updated.
- const PrecacheURLInfo info = precache_url_table_.GetURLInfo(url);
- bool is_download_reported = info.is_download_reported;
- if (info.is_precached && !was_cached) {
- is_download_reported = false;
- }
- buffered_writes_.push_back(
- base::Bind(&PrecacheDatabase::RecordURLPrefetchInternal, GetWeakPtr(),
- url, referrer_host, !was_cached || info.is_precached,
- fetch_time, is_download_reported));
- buffered_urls_.insert(url.spec());
- MaybePostFlush();
-}
-
-void PrecacheDatabase::RecordURLPrefetchInternal(
- const GURL& url,
- const std::string& referrer_host,
- bool is_precached,
- const base::Time& fetch_time,
- bool is_download_reported) {
- int64_t referrer_host_id =
- precache_referrer_host_table_.GetReferrerHost(referrer_host).id;
- if (referrer_host_id == PrecacheReferrerHostEntry::kInvalidId) {
- referrer_host_id = precache_referrer_host_table_.UpdateReferrerHost(
- referrer_host, 0, fetch_time);
- }
- DCHECK_NE(referrer_host_id, PrecacheReferrerHostEntry::kInvalidId);
- precache_url_table_.AddURL(url, referrer_host_id, is_precached, fetch_time,
- is_download_reported);
-}
-
-void PrecacheDatabase::RecordURLNonPrefetch(const GURL& url,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- int host_rank,
- bool is_connection_cellular) {
- UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.NonPrefetch",
- info.cache_entry_status,
- net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
-
- if (host_rank != history::kMaxTopHosts) {
- // The resource was loaded on a page that could have been affected by
- // precaching.
- UMA_HISTOGRAM_ENUMERATION(
- "Precache.CacheStatus.NonPrefetch.TopHosts", info.cache_entry_status,
- net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
- } else {
- // The resource was loaded on a page that could NOT have been affected by
- // precaching.
- UMA_HISTOGRAM_ENUMERATION(
- "Precache.CacheStatus.NonPrefetch.NonTopHosts", info.cache_entry_status,
- net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
- }
-
- if (!IsDatabaseAccessible()) {
- // Don't track anything if unable to access the database.
- return;
- }
-
- RecordTimeSinceLastPrecache(fetch_time);
-
- if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) {
- // If the URL for this fetch is in the write buffer, then flush the write
- // buffer.
- Flush();
- }
-
- const PrecacheURLInfo url_info = precache_url_table_.GetURLInfo(url);
-
- if (url_info.was_precached) {
- UMA_HISTOGRAM_ENUMERATION(
- "Precache.CacheStatus.NonPrefetch.FromPrecache",
- info.cache_entry_status,
- net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
- }
-
- base::HistogramBase::Sample size_sample =
- static_cast<base::HistogramBase::Sample>(size);
- if (!info.was_cached) {
- // The fetch was served over the network during user browsing, so count it
- // as downloaded non-precache bytes.
- UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache", size_sample);
- if (is_connection_cellular) {
- UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache.Cellular",
- size_sample);
- }
- // Since the resource has been fetched during user browsing, mark the URL as
- // used in the precache URL table, if any exists. The current fetch would
- // have put this resource in the cache regardless of whether or not it was
- // previously precached, so mark the URL as used.
- buffered_writes_.push_back(
- base::Bind(&PrecacheURLTable::SetURLAsNotPrecached,
- base::Unretained(&precache_url_table_), url));
- buffered_urls_.insert(url.spec());
- MaybePostFlush();
- } else if (/* info.was_cached && */ url_info.is_precached &&
- !url_info.was_used) {
- // The fetch was served from the cache, and since there's an entry for this
- // URL in the URL table, this means that the resource was served from the
- // cache only because precaching put it there. Thus, precaching was helpful,
- // so count the fetch as saved bytes.
- UMA_HISTOGRAM_COUNTS("Precache.Saved", size_sample);
- if (is_connection_cellular) {
- UMA_HISTOGRAM_COUNTS("Precache.Saved.Cellular", size_sample);
- }
-
- DCHECK(info.headers) << "The headers are required to get the freshness.";
- if (info.headers) {
- // TODO(jamartin): Maybe report stale_while_validate as well.
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Precache.Saved.Freshness",
- info.headers->GetFreshnessLifetimes(info.response_time)
- .freshness.InSeconds(),
- base::TimeDelta::FromMinutes(5).InSeconds() /* min */,
- base::TimeDelta::FromDays(356).InSeconds() /* max */,
- 100 /* bucket_count */);
- }
-
- buffered_writes_.push_back(
- base::Bind(&PrecacheURLTable::SetPrecachedURLAsUsed,
- base::Unretained(&precache_url_table_), url));
- buffered_urls_.insert(url.spec());
- MaybePostFlush();
- }
-}
-
-void PrecacheDatabase::UpdatePrecacheReferrerHost(
- const std::string& hostname,
- int64_t manifest_id,
- const base::Time& fetch_time) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (!IsDatabaseAccessible()) {
- // Don't track anything if unable to access the database.
- return;
- }
-
- buffered_writes_.push_back(
- base::Bind(&PrecacheDatabase::UpdatePrecacheReferrerHostInternal,
- GetWeakPtr(), hostname, manifest_id, fetch_time));
- MaybePostFlush();
-}
-
-void PrecacheDatabase::UpdatePrecacheReferrerHostInternal(
- const std::string& hostname,
- int64_t manifest_id,
- const base::Time& fetch_time) {
- int64_t referrer_host_id = precache_referrer_host_table_.UpdateReferrerHost(
- hostname, manifest_id, fetch_time);
-
- if (referrer_host_id != PrecacheReferrerHostEntry::kInvalidId) {
- precache_url_table_.ClearAllForReferrerHost(referrer_host_id);
- }
-}
-
-void PrecacheDatabase::RecordTimeSinceLastPrecache(
- const base::Time& fetch_time) {
- const base::Time& last_precache_timestamp = GetLastPrecacheTimestamp();
- // It could still be null if the DB was not accessible.
- if (!last_precache_timestamp.is_null()) {
- // This is the timespan (in seconds) between the last call to
- // PrecacheManager::StartPrecaching and the fetch time of a non-precache
- // URL. Please note that the session started by that call to
- // PrecacheManager::StartPrecaching may not have precached this particular
- // URL or even any URL for that matter.
- // The range was estimated to have the 95 percentile within the last bounded
- // bucket.
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Precache.TimeSinceLastPrecache",
- (fetch_time - last_precache_timestamp).InSeconds(), kSecondsInMinute,
- kSecondsInHour * 36, 100);
- }
-}
-
-bool PrecacheDatabase::IsDatabaseAccessible() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(db_);
-
- return db_->is_open();
-}
-
-void PrecacheDatabase::Flush() {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (buffered_writes_.empty()) {
- // Do nothing if there's nothing to flush.
- DCHECK(buffered_urls_.empty());
- return;
- }
-
- if (IsDatabaseAccessible()) {
- sql::Transaction transaction(db_.get());
- if (transaction.Begin()) {
- for (std::vector<base::Closure>::const_iterator it =
- buffered_writes_.begin();
- it != buffered_writes_.end(); ++it) {
- it->Run();
- }
-
- transaction.Commit();
- }
- }
-
- // Clear the buffer, even if the database is inaccessible or unable to begin a
- // transaction.
- buffered_writes_.clear();
- buffered_urls_.clear();
-}
-
-void PrecacheDatabase::PostedFlush() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(is_flush_posted_);
- is_flush_posted_ = false;
- Flush();
-}
-
-void PrecacheDatabase::MaybePostFlush() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (buffered_writes_.empty() || is_flush_posted_) {
- // There's no point in posting a flush if there's nothing to be flushed or
- // if a flush has already been posted.
- return;
- }
-
- // Post a delayed task to flush the buffer in 1 second, so that multiple
- // database writes can be buffered up and flushed together in the same
- // transaction.
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::Bind(&PrecacheDatabase::PostedFlush,
- weak_factory_.GetWeakPtr()),
- base::TimeDelta::FromSeconds(1));
- is_flush_posted_ = true;
-}
-
-std::unique_ptr<PrecacheUnfinishedWork>
-PrecacheDatabase::GetUnfinishedWork() {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work =
- precache_session_table_.GetUnfinishedWork();
- precache_session_table_.DeleteUnfinishedWork();
- return unfinished_work;
-}
-
-void PrecacheDatabase::SaveUnfinishedWork(
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work) {
- precache_session_table_.SaveUnfinishedWork(
- std::move(unfinished_work));
-}
-
-void PrecacheDatabase::DeleteUnfinishedWork() {
- precache_session_table_.DeleteUnfinishedWork();
-}
-
-void PrecacheDatabase::SaveQuota(const PrecacheQuota& quota) {
- precache_session_table_.SaveQuota(quota);
-}
-
-PrecacheQuota PrecacheDatabase::GetQuota() {
- return precache_session_table_.GetQuota();
-}
-
-base::WeakPtr<PrecacheDatabase> PrecacheDatabase::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_database.h b/chromium/components/precache/core/precache_database.h
deleted file mode 100644
index 0597516d7b0..00000000000
--- a/chromium/components/precache/core/precache_database.h
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PRECACHE_CORE_PRECACHE_DATABASE_H_
-#define COMPONENTS_PRECACHE_CORE_PRECACHE_DATABASE_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/containers/hash_tables.h"
-#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/precache/core/precache_fetcher.h"
-#include "components/precache/core/precache_referrer_host_table.h"
-#include "components/precache/core/precache_session_table.h"
-#include "components/precache/core/precache_url_table.h"
-
-class GURL;
-
-namespace base {
-class FilePath;
-}
-
-namespace net {
-class HttpResponseInfo;
-}
-
-namespace sql {
-class Connection;
-}
-
-namespace precache {
-
-class PrecacheUnfinishedWork;
-
-// Class that tracks information related to precaching. This class may be
-// constructed on any thread, but all calls to, and destruction of this class
-// must be done on the the DB thread.
-class PrecacheDatabase {
- public:
- // A PrecacheDatabase can be constructed on any thread.
- PrecacheDatabase();
-
- ~PrecacheDatabase();
-
- // Initializes the precache database, using the specified database file path.
- // Init must be called before any other methods.
- bool Init(const base::FilePath& db_path);
-
- // Deletes precache history from the precache URL table that is more than 60
- // days older than |current_time|.
- void DeleteExpiredPrecacheHistory(const base::Time& current_time);
-
- // Delete all history entries from the database.
- void ClearHistory();
-
- // Setter and getter for the last precache timestamp.
- void SetLastPrecacheTimestamp(const base::Time& time);
- base::Time GetLastPrecacheTimestamp();
-
- // Report precache-related metrics in response to a URL being fetched, where
- // the fetch was motivated by precaching. This is called from the network
- // delegate, via precache_util.
- void RecordURLPrefetchMetrics(const net::HttpResponseInfo& info);
-
- // Records the precache of an url |url| for top host |referrer_host|. This is
- // called from PrecacheFetcher.
- void RecordURLPrefetch(const GURL& url,
- const std::string& referrer_host,
- const base::Time& fetch_time,
- bool was_cached,
- int64_t size);
-
- // Report precache-related metrics in response to a URL being fetched, where
- // the fetch was not motivated by precaching. |is_connection_cellular|
- // indicates whether the current network connection is a cellular network.
- // This is called from the network delegate, via precache_util.
- void RecordURLNonPrefetch(const GURL& url,
- const base::Time& fetch_time,
- const net::HttpResponseInfo& info,
- int64_t size,
- int host_rank,
- bool is_connection_cellular);
-
- // Returns the referrer host entry for the |referrer_host|.
- PrecacheReferrerHostEntry GetReferrerHost(const std::string& referrer_host);
-
- // Populates the list of used and downloaded resources for referrer host with
- // id |referrer_host_id|. It will also clear the reported downloaded_urls.
- void GetURLListForReferrerHost(int64_t referrer_host_id,
- std::vector<GURL>* used_urls,
- std::vector<GURL>* downloaded_urls);
-
- // Updates the |manifest_id| and |fetch_time| for the referrer host
- // |hostname|, and deletes the precached subresource URLs for this top host.
- void UpdatePrecacheReferrerHost(const std::string& hostname,
- int64_t manifest_id,
- const base::Time& fetch_time);
-
- // Gets the state required to continue a precache session.
- std::unique_ptr<PrecacheUnfinishedWork> GetUnfinishedWork();
-
- // Stores the state required to continue a precache session so that the
- // session can be resumed later.
- void SaveUnfinishedWork(
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work);
-
- // Deletes unfinished work from the database.
- void DeleteUnfinishedWork();
-
- // Precache quota.
- void SaveQuota(const PrecacheQuota& quota);
- PrecacheQuota GetQuota();
-
- base::WeakPtr<PrecacheDatabase> GetWeakPtr();
-
- private:
- friend class PrecacheDatabaseTest;
- friend class PrecacheFetcherTest;
- friend class PrecacheManagerTest;
-
- bool IsDatabaseAccessible() const;
-
- // Flushes any buffered write operations. |buffered_writes_| will be empty
- // after calling this function. To maximize performance, all the buffered
- // writes are run in a single database transaction.
- void Flush();
-
- // Same as Flush(), but also updates the flag |is_flush_posted_| to indicate
- // that a flush is no longer posted.
- void PostedFlush();
-
- // Post a call to PostedFlush() on the current thread's MessageLoop, if
- // |buffered_writes_| is non-empty and there isn't already a flush call
- // posted.
- void MaybePostFlush();
-
- // Records the time since the last precache.
- void RecordTimeSinceLastPrecache(const base::Time& fetch_time);
-
- void RecordURLPrefetchInternal(const GURL& url,
- const std::string& referrer_host,
- bool is_precached,
- const base::Time& fetch_time,
- bool is_download_reported);
-
- void UpdatePrecacheReferrerHostInternal(const std::string& hostname,
- int64_t manifest_id,
- const base::Time& fetch_time);
-
- std::unique_ptr<sql::Connection> db_;
-
- // Table that keeps track of URLs that are in the cache because of precaching,
- // and wouldn't be in the cache otherwise. If |buffered_writes_| is non-empty,
- // then this table will not be up to date until the next call to Flush().
- PrecacheURLTable precache_url_table_;
-
- // If |buffered_writes_| is non-empty,
- // then this table will not be up to date until the next call to Flush().
- PrecacheReferrerHostTable precache_referrer_host_table_;
-
- // Table that persists state related to a precache session, including
- // unfinished work to be done.
- PrecacheSessionTable precache_session_table_;
-
- // A vector of write operations to be run on the database.
- std::vector<base::Closure> buffered_writes_;
-
- // Set of URLs that have been modified in |buffered_writes_|. It's a hash set
- // of strings, and not GURLs, because there is no hash function on GURL.
- base::hash_set<std::string> buffered_urls_;
-
- // Flag indicating whether or not a call to Flush() has been posted to run in
- // the future.
- bool is_flush_posted_;
-
- // ThreadChecker used to ensure that all methods other than the constructor
- // or destructor are called on the same thread.
- base::ThreadChecker thread_checker_;
-
- // Time of the last precache. This is a cached copy of
- // precache_session_table_.GetLastPrecacheTimestamp.
- base::Time last_precache_timestamp_;
-
- // This must be the last member of this class.
- base::WeakPtrFactory<PrecacheDatabase> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(PrecacheDatabase);
-};
-
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CORE_PRECACHE_DATABASE_H_
diff --git a/chromium/components/precache/core/precache_database_unittest.cc b/chromium/components/precache/core/precache_database_unittest.cc
deleted file mode 100644
index 4fca60e72e3..00000000000
--- a/chromium/components/precache/core/precache_database_unittest.cc
+++ /dev/null
@@ -1,685 +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/precache/core/precache_database.h"
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/containers/hash_tables.h"
-#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram_base.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/time/time.h"
-#include "components/history/core/browser/history_constants.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_response_info.h"
-#include "net/http/http_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace {
-
-using ::testing::ContainerEq;
-using ::testing::ElementsAre;
-using base::Bucket;
-using net::HttpResponseInfo;
-
-const GURL kURL("http://url.com");
-const int kReferrerID = 1;
-const base::Time kFetchTime = base::Time() + base::TimeDelta::FromHours(1000);
-const base::Time kOldFetchTime = kFetchTime - base::TimeDelta::FromDays(1);
-const base::Time kNewFetchTime =
- base::Time() + base::TimeDelta::FromHours(2000);
-const base::Time kPrecacheTime =
- base::Time() + base::TimeDelta::FromHours(3000);
-const int64_t kSize = 5000;
-const int64_t kFreshnessBucket10K = 9089;
-// One of the possible CacheEntryStatus for when the fetch was served from the
-// network.
-const HttpResponseInfo::CacheEntryStatus kFromNetwork =
- HttpResponseInfo::CacheEntryStatus::ENTRY_UPDATED;
-
-std::map<GURL, base::Time> BuildURLTableMap(const GURL& url,
- const base::Time& precache_time) {
- std::map<GURL, base::Time> url_table_map;
- url_table_map[url] = precache_time;
- return url_table_map;
-}
-
-HttpResponseInfo CreateHttpResponseInfo(bool was_cached,
- bool network_accessed) {
- HttpResponseInfo result;
- result.was_cached = was_cached;
- result.network_accessed = network_accessed;
- if (was_cached) {
- if (network_accessed) {
- result.cache_entry_status =
- HttpResponseInfo::CacheEntryStatus::ENTRY_VALIDATED;
- } else {
- result.cache_entry_status =
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED;
- }
- } else { // !was_cached.
- result.cache_entry_status = kFromNetwork;
- }
- std::string header(
- "HTTP/1.1 200 OK\n"
- "cache-control: max-age=10000\n\n");
- result.headers = new net::HttpResponseHeaders(
- net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()));
- DCHECK_EQ(
- 10000,
- result.headers->GetFreshnessLifetimes(base::Time()).freshness.InSeconds())
- << "Error parsing the test headers: " << header;
- return result;
-}
-
-} // namespace
-
-namespace precache {
-
-class PrecacheDatabaseTest : public testing::Test {
- public:
- PrecacheDatabaseTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
- ~PrecacheDatabaseTest() override {}
-
- protected:
- void SetUp() override {
- precache_database_.reset(new PrecacheDatabase());
-
- ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
- base::FilePath db_path = scoped_temp_dir_.GetPath().Append(
- base::FilePath(FILE_PATH_LITERAL("precache_database")));
- ASSERT_TRUE(precache_database_->Init(db_path));
- }
-
- void TearDown() override { precache_url_table()->DeleteAll(); }
-
- std::map<GURL, base::Time> GetActualURLTableMap() {
- // Flush any buffered writes so that the URL table will be up to date.
- precache_database_->Flush();
-
- std::map<GURL, base::Time> url_table_map;
- precache_url_table()->GetAllDataForTesting(&url_table_map);
- return url_table_map;
- }
-
- PrecacheURLTable* precache_url_table() {
- return &precache_database_->precache_url_table_;
- }
-
- void Flush() { precache_database_->Flush(); }
-
- // Convenience methods for recording different types of URL fetches. These
- // exist to improve the readability of the tests.
- void RecordPrecacheFromNetwork(const GURL& url,
- const base::Time& fetch_time,
- int64_t size);
- void RecordPrecacheFromCache(const GURL& url,
- const base::Time& fetch_time,
- int64_t size);
- void RecordFetchFromNetwork(const GURL& url,
- const base::Time& fetch_time,
- int64_t size);
- void RecordFetchFromNetwork(const GURL& url,
- const base::Time& fetch_time,
- int64_t size,
- int host_rank);
- void RecordFetchFromNetworkCellular(const GURL& url,
- const base::Time& fetch_time,
- int64_t size);
- void RecordFetchFromCache(const GURL& url,
- const base::Time& fetch_time,
- int64_t size);
- void RecordFetchFromCacheCellular(const GURL& url,
- const base::Time& fetch_time,
- int64_t size);
-
- // Must be declared first so that it is destroyed last.
- base::ScopedTempDir scoped_temp_dir_;
-
- base::test::ScopedTaskEnvironment scoped_task_environment_;
-
- std::unique_ptr<PrecacheDatabase> precache_database_;
- base::HistogramTester histograms_;
- base::HistogramTester::CountsMap expected_histogram_counts_;
-
- void ExpectNewSample(const std::string& histogram_name,
- base::HistogramBase::Sample sample) {
- histograms_.ExpectUniqueSample(histogram_name, sample, 1);
- expected_histogram_counts_[histogram_name]++;
- }
-
- void ExpectNoOtherSamples() {
- EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
- ContainerEq(expected_histogram_counts_));
- }
-};
-
-void PrecacheDatabaseTest::RecordPrecacheFromNetwork(
- const GURL& url,
- const base::Time& fetch_time,
- int64_t size) {
- const HttpResponseInfo info = CreateHttpResponseInfo(
- false /* was_cached */, false /* network_accessed */);
- precache_database_->RecordURLPrefetchMetrics(info);
- precache_database_->RecordURLPrefetch(url, std::string(), fetch_time,
- info.was_cached, size);
-}
-
-void PrecacheDatabaseTest::RecordPrecacheFromCache(const GURL& url,
- const base::Time& fetch_time,
- int64_t size) {
- const HttpResponseInfo info = CreateHttpResponseInfo(
- true /* was_cached */, false /* network_accessed */);
- precache_database_->RecordURLPrefetchMetrics(info);
- precache_database_->RecordURLPrefetch(url, std::string(), fetch_time,
- info.was_cached, size);
-}
-
-void PrecacheDatabaseTest::RecordFetchFromNetwork(const GURL& url,
- const base::Time& fetch_time,
- int64_t size) {
- const HttpResponseInfo info = CreateHttpResponseInfo(
- false /* was_cached */, false /* network_accessed */);
- precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
- history::kMaxTopHosts,
- false /* is_connection_cellular */);
-}
-
-void PrecacheDatabaseTest::RecordFetchFromNetwork(const GURL& url,
- const base::Time& fetch_time,
- int64_t size,
- int host_rank) {
- const HttpResponseInfo info = CreateHttpResponseInfo(
- false /* was_cached */, false /* network_accessed */);
- precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
- host_rank,
- false /* is_connection_cellular */);
-}
-
-void PrecacheDatabaseTest::RecordFetchFromNetworkCellular(
- const GURL& url,
- const base::Time& fetch_time,
- int64_t size) {
- const HttpResponseInfo info = CreateHttpResponseInfo(
- false /* was_cached */, false /* network_accessed */);
- precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
- history::kMaxTopHosts,
- true /* is_connection_cellular */);
-}
-
-void PrecacheDatabaseTest::RecordFetchFromCache(const GURL& url,
- const base::Time& fetch_time,
- int64_t size) {
- const HttpResponseInfo info = CreateHttpResponseInfo(
- true /* was_cached */, false /* network_accessed */);
- precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
- history::kMaxTopHosts,
- false /* is_connection_cellular */);
-}
-
-void PrecacheDatabaseTest::RecordFetchFromCacheCellular(
- const GURL& url,
- const base::Time& fetch_time,
- int64_t size) {
- const HttpResponseInfo info = CreateHttpResponseInfo(
- true /* was_cached */, false /* network_accessed */);
- precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
- history::kMaxTopHosts,
- true /* is_connection_cellular */);
-}
-
-namespace {
-
-TEST_F(PrecacheDatabaseTest, PrecacheOverNetwork) {
- RecordPrecacheFromNetwork(kURL, kFetchTime, kSize);
-
- EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap());
-
- ExpectNewSample("Precache.DownloadedPrecacheMotivated", kSize);
- ExpectNewSample("Precache.CacheStatus.Prefetch", kFromNetwork);
- ExpectNewSample("Precache.Freshness.Prefetch", kFreshnessBucket10K);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, PrecacheFromCacheWithURLTableEntry) {
- precache_url_table()->AddURL(kURL, kReferrerID, true, kOldFetchTime, false);
- RecordPrecacheFromCache(kURL, kFetchTime, kSize);
-
- // The URL table entry should have been updated to have |kFetchTime| as the
- // timestamp.
- EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap());
-
- ExpectNewSample("Precache.CacheStatus.Prefetch",
- net::HttpResponseInfo::ENTRY_USED);
- ExpectNewSample("Precache.Freshness.Prefetch", kFreshnessBucket10K);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, PrecacheFromCacheWithoutURLTableEntry) {
- RecordPrecacheFromCache(kURL, kFetchTime, kSize);
-
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.CacheStatus.Prefetch",
- net::HttpResponseInfo::ENTRY_USED);
- ExpectNewSample("Precache.Freshness.Prefetch", kFreshnessBucket10K);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, FetchOverNetwork_NonCellular) {
- RecordFetchFromNetwork(kURL, kFetchTime, kSize);
-
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts", kFromNetwork);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, FetchOverNetwork_NonCellular_TopHosts) {
- RecordFetchFromNetwork(kURL, kFetchTime, kSize, 0 /* host_rank */);
-
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.TopHosts", kFromNetwork);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, FetchOverNetwork_Cellular) {
- RecordFetchFromNetworkCellular(kURL, kFetchTime, kSize);
-
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
- ExpectNewSample("Precache.DownloadedNonPrecache.Cellular", kSize);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts", kFromNetwork);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, FetchOverNetworkWithURLTableEntry) {
- precache_url_table()->AddURL(kURL, kReferrerID, true, kOldFetchTime, false);
- RecordFetchFromNetwork(kURL, kFetchTime, kSize);
-
- // The URL table entry should have been deleted.
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.FromPrecache",
- kFromNetwork);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts", kFromNetwork);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, FetchFromCacheWithURLTableEntry_NonCellular) {
- precache_url_table()->AddURL(kURL, kReferrerID, true, kOldFetchTime, false);
- RecordFetchFromCache(kURL, kFetchTime, kSize);
-
- // The URL table entry should have been deleted.
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.CacheStatus.NonPrefetch",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.FromPrecache",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNewSample("Precache.Saved", kSize);
- ExpectNewSample("Precache.Saved.Freshness", kFreshnessBucket10K);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, FetchFromCacheWithURLTableEntry_Cellular) {
- precache_url_table()->AddURL(kURL, kReferrerID, true, kOldFetchTime, false);
- RecordFetchFromCacheCellular(kURL, kFetchTime, kSize);
-
- // The URL table entry should have been deleted.
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.CacheStatus.NonPrefetch",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.FromPrecache",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNewSample("Precache.Saved", kSize);
- ExpectNewSample("Precache.Saved.Cellular", kSize);
- ExpectNewSample("Precache.Saved.Freshness", kFreshnessBucket10K);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, FetchFromCacheWithoutURLTableEntry) {
- RecordFetchFromCache(kURL, kFetchTime, kSize);
-
- EXPECT_TRUE(GetActualURLTableMap().empty());
-
- ExpectNewSample("Precache.CacheStatus.NonPrefetch",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts",
- HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
- ExpectNoOtherSamples();
-}
-
-TEST_F(PrecacheDatabaseTest, DeleteExpiredPrecacheHistory) {
- const base::Time kToday = base::Time() + base::TimeDelta::FromDays(1000);
- const base::Time k59DaysAgo = kToday - base::TimeDelta::FromDays(59);
- const base::Time k61DaysAgo = kToday - base::TimeDelta::FromDays(61);
-
- precache_url_table()->AddURL(GURL("http://expired-precache.com"), kReferrerID,
- true, k61DaysAgo, false);
- precache_url_table()->AddURL(GURL("http://old-precache.com"), kReferrerID,
- true, k59DaysAgo, false);
-
- precache_database_->DeleteExpiredPrecacheHistory(kToday);
-
- EXPECT_EQ(BuildURLTableMap(GURL("http://old-precache.com"), k59DaysAgo),
- GetActualURLTableMap());
-}
-
-TEST_F(PrecacheDatabaseTest, SampleInteraction) {
- const GURL kURL1("http://url1.com");
- const int64_t kSize1 = 1;
- const GURL kURL2("http://url2.com");
- const int64_t kSize2 = 2;
- const GURL kURL3("http://url3.com");
- const int64_t kSize3 = 3;
- const GURL kURL4("http://url4.com");
- const int64_t kSize4 = 4;
- const GURL kURL5("http://url5.com");
- const int64_t kSize5 = 5;
-
- RecordPrecacheFromNetwork(kURL1, kFetchTime, kSize1);
- RecordPrecacheFromNetwork(kURL2, kFetchTime, kSize2);
- RecordPrecacheFromNetwork(kURL3, kFetchTime, kSize3);
- RecordPrecacheFromNetwork(kURL4, kFetchTime, kSize4);
-
- RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1);
- RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1);
- RecordFetchFromNetworkCellular(kURL2, kFetchTime, kSize2);
- RecordFetchFromNetworkCellular(kURL5, kFetchTime, kSize5);
- RecordFetchFromCacheCellular(kURL5, kFetchTime, kSize5);
-
- RecordPrecacheFromCache(kURL1, kFetchTime, kSize1);
- RecordPrecacheFromNetwork(kURL2, kFetchTime, kSize2);
- RecordPrecacheFromCache(kURL3, kFetchTime, kSize3);
- RecordPrecacheFromCache(kURL4, kFetchTime, kSize4);
-
- RecordFetchFromCache(kURL1, kFetchTime, kSize1);
- RecordFetchFromNetwork(kURL2, kFetchTime, kSize2);
- RecordFetchFromCache(kURL3, kFetchTime, kSize3);
- RecordFetchFromCache(kURL5, kFetchTime, kSize5);
-
- EXPECT_THAT(histograms_.GetAllSamples("Precache.DownloadedPrecacheMotivated"),
- ElementsAre(Bucket(kSize1, 1), Bucket(kSize2, 2),
- Bucket(kSize3, 1), Bucket(kSize4, 1)));
-
- EXPECT_THAT(histograms_.GetAllSamples("Precache.DownloadedNonPrecache"),
- ElementsAre(Bucket(kSize2, 2), Bucket(kSize5, 1)));
-
- EXPECT_THAT(
- histograms_.GetAllSamples("Precache.DownloadedNonPrecache.Cellular"),
- ElementsAre(Bucket(kSize2, 1), Bucket(kSize5, 1)));
-
- EXPECT_THAT(
- histograms_.GetAllSamples("Precache.CacheStatus.Prefetch"),
- ElementsAre(
- Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_USED, 3),
- Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_UPDATED, 5)));
-
- EXPECT_THAT(
- histograms_.GetAllSamples("Precache.CacheStatus.NonPrefetch"),
- ElementsAre(
- Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_USED, 6),
- Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_UPDATED, 3)));
-
- EXPECT_THAT(histograms_.GetAllSamples("Precache.Saved"),
- ElementsAre(Bucket(kSize1, 1), Bucket(kSize3, 1)));
-
- EXPECT_THAT(histograms_.GetAllSamples("Precache.Saved.Cellular"),
- ElementsAre(Bucket(kSize1, 1)));
-}
-
-TEST_F(PrecacheDatabaseTest, LastPrecacheTimestamp) {
- // So that it starts recording TimeSinceLastPrecache.
- const base::Time kStartTime =
- base::Time() + base::TimeDelta::FromSeconds(100);
- precache_database_->SetLastPrecacheTimestamp(kStartTime);
-
- RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
- RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
- RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
- RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
-
- EXPECT_THAT(histograms_.GetAllSamples("Precache.TimeSinceLastPrecache"),
- ElementsAre());
-
- const base::Time kTimeA = kStartTime + base::TimeDelta::FromSeconds(7);
- const base::Time kTimeB = kStartTime + base::TimeDelta::FromMinutes(42);
- const base::Time kTimeC = kStartTime + base::TimeDelta::FromHours(20);
-
- RecordFetchFromCacheCellular(kURL, kTimeA, kSize);
- RecordFetchFromCacheCellular(kURL, kTimeA, kSize);
- RecordFetchFromNetworkCellular(kURL, kTimeB, kSize);
- RecordFetchFromNetworkCellular(kURL, kTimeB, kSize);
- RecordFetchFromCacheCellular(kURL, kTimeB, kSize);
- RecordFetchFromCacheCellular(kURL, kTimeC, kSize);
-
- EXPECT_THAT(histograms_.GetAllSamples("Precache.TimeSinceLastPrecache"),
- ElementsAre(Bucket(0, 2), Bucket(2406, 3), Bucket(69347, 1)));
-}
-
-TEST_F(PrecacheDatabaseTest, PrecacheFreshnessPrefetch) {
- auto info = CreateHttpResponseInfo(false /* was_cached */,
- false /* network_accessed */);
- RecordPrecacheFromNetwork(kURL, kFetchTime, kSize);
-
- EXPECT_THAT(histograms_.GetAllSamples("Precache.Freshness.Prefetch"),
- ElementsAre(Bucket(kFreshnessBucket10K, 1)));
-}
-
-TEST_F(PrecacheDatabaseTest, UpdateAndGetReferrerHost) {
- // Invalid ID should be returned for referrer host that does not exist.
- EXPECT_EQ(PrecacheReferrerHostEntry::kInvalidId,
- precache_database_->GetReferrerHost(std::string()).id);
- EXPECT_EQ(PrecacheReferrerHostEntry::kInvalidId,
- precache_database_->GetReferrerHost("not_created_host.com").id);
-
- // Create a new entry.
- precache_database_->UpdatePrecacheReferrerHost("foo.com", 1, kFetchTime);
- Flush();
- auto actual_entry = precache_database_->GetReferrerHost("foo.com");
- EXPECT_EQ("foo.com", actual_entry.referrer_host);
- EXPECT_EQ(1, actual_entry.manifest_id);
- EXPECT_EQ(kFetchTime, actual_entry.time);
-
- // Update the existing entry.
- precache_database_->UpdatePrecacheReferrerHost("foo.com", 2, kNewFetchTime);
- Flush();
- actual_entry = precache_database_->GetReferrerHost("foo.com");
- EXPECT_EQ("foo.com", actual_entry.referrer_host);
- EXPECT_EQ(2, actual_entry.manifest_id);
- EXPECT_EQ(kNewFetchTime, actual_entry.time);
-}
-
-TEST_F(PrecacheDatabaseTest, GetURLListForReferrerHost) {
- precache_database_->UpdatePrecacheReferrerHost("foo.com", 1, kFetchTime);
- precache_database_->UpdatePrecacheReferrerHost("bar.com", 2, kNewFetchTime);
- precache_database_->UpdatePrecacheReferrerHost("foobar.com", 3,
- kNewFetchTime);
- precache_database_->UpdatePrecacheReferrerHost("empty.com", 3, kNewFetchTime);
-
- struct test_resource_info {
- std::string url;
- bool is_user_browsed;
- bool is_network_fetched;
- bool is_cellular_fetched;
- bool expected_in_used;
- };
-
- const struct {
- std::string hostname;
- std::vector<test_resource_info> resource_info;
- } tests[] = {
- {
- "foo.com",
- {
- {"http://foo.com/from-cache.js", true, false, false, true},
- {"http://some-cdn.com/from-network.js", true, true, false, false},
- {"http://foo.com/from-cache-cellular.js", true, false, true,
- true},
- {"http://foo.com/from-network-cellular.js", true, true, true,
- false},
- {"http://foo.com/not-browsed.js", false, false, false, false},
- },
- },
- {
- "bar.com",
- {
- {"http://bar.com/a.js", true, false, false, true},
- {"http://some-cdn.com/b.js", true, false, true, true},
- {"http://bar.com/not-browsed.js", false, false, false, false},
- },
- },
- {
- "foobar.com",
- {
- {"http://some-cdn.com/not-used.js", true, true, true, false},
- },
- },
- {
- "empty.com", std::vector<test_resource_info>{},
- },
- };
- // Add precached resources.
- for (const auto& test : tests) {
- for (const auto& resource : test.resource_info) {
- precache_database_->RecordURLPrefetch(GURL(resource.url), test.hostname,
- kPrecacheTime, false, kSize);
- }
- }
- // Update some resources as used due to user browsing.
- for (const auto& test : tests) {
- for (const auto& resource : test.resource_info) {
- if (!resource.is_user_browsed)
- continue;
- if (!resource.is_network_fetched && !resource.is_cellular_fetched) {
- RecordFetchFromCache(GURL(resource.url), kFetchTime, kSize);
- } else if (!resource.is_network_fetched && resource.is_cellular_fetched) {
- RecordFetchFromCacheCellular(GURL(resource.url), kFetchTime, kSize);
- } else if (resource.is_network_fetched && !resource.is_cellular_fetched) {
- RecordFetchFromNetwork(GURL(resource.url), kFetchTime, kSize);
- } else if (resource.is_network_fetched && resource.is_cellular_fetched) {
- RecordFetchFromNetworkCellular(GURL(resource.url), kFetchTime, kSize);
- }
- }
- }
- Flush();
- // Verify the used and downloaded resources.
- for (const auto& test : tests) {
- std::vector<GURL> expected_used_urls, expected_downloaded_urls;
- for (const auto& resource : test.resource_info) {
- if (resource.expected_in_used) {
- expected_used_urls.push_back(GURL(resource.url));
- }
- expected_downloaded_urls.push_back(GURL(resource.url));
- }
- std::vector<GURL> actual_used_urls, actual_downloaded_urls;
- auto referrer_id = precache_database_->GetReferrerHost(test.hostname).id;
- EXPECT_NE(PrecacheReferrerHostEntry::kInvalidId, referrer_id);
- precache_database_->GetURLListForReferrerHost(
- referrer_id, &actual_used_urls, &actual_downloaded_urls);
- EXPECT_THAT(actual_used_urls, ::testing::ContainerEq(expected_used_urls));
- EXPECT_THAT(actual_downloaded_urls,
- ::testing::ContainerEq(expected_downloaded_urls))
- << "Host: " << test.hostname;
- }
- // Subsequent manifest updates should clear the used and downloaded resources.
- for (const auto& test : tests) {
- precache_database_->UpdatePrecacheReferrerHost(test.hostname, 100,
- kNewFetchTime);
- Flush();
- std::vector<GURL> actual_used_urls, actual_downloaded_urls;
- auto referrer_id = precache_database_->GetReferrerHost(test.hostname).id;
- EXPECT_NE(PrecacheReferrerHostEntry::kInvalidId, referrer_id);
- precache_database_->GetURLListForReferrerHost(
- referrer_id, &actual_used_urls, &actual_downloaded_urls);
- EXPECT_THAT(actual_used_urls, ::testing::IsEmpty());
- EXPECT_THAT(actual_downloaded_urls, ::testing::IsEmpty());
- }
- // Resources that were precached previously and not seen in user browsing
- // should be still marked as precached.
- std::map<GURL, base::Time> expected_url_table_map;
- for (const auto& test : tests) {
- for (const auto& resource : test.resource_info) {
- if (!resource.is_user_browsed)
- expected_url_table_map[GURL(resource.url)] = kPrecacheTime;
- }
- }
- EXPECT_EQ(expected_url_table_map, GetActualURLTableMap());
-}
-
-TEST_F(PrecacheDatabaseTest, GetURLListForReferrerHostReportsDownloadedOnce) {
- precache_database_->UpdatePrecacheReferrerHost("foo.com", 1, kFetchTime);
- // Add two resources that shouldn't appear in downloaded.
- Flush(); // We need to write the referrer_host_id.
- const std::string already_reported_and_not_refetch =
- "http://foo.com/already-reported-and-not-refetch.js";
- precache_database_->RecordURLPrefetch(GURL(already_reported_and_not_refetch),
- "foo.com", kPrecacheTime, false, kSize);
- const std::string already_reported_and_in_cache =
- "http://foo.com/already-reported-and-in-cache.js";
- precache_database_->RecordURLPrefetch(GURL(already_reported_and_in_cache),
- "foo.com", kPrecacheTime, false, kSize);
- const std::string already_reported_but_refetch =
- "http://foo.com/already-reported-but-refetch.js";
- precache_database_->RecordURLPrefetch(GURL(already_reported_but_refetch),
- "foo.com", kPrecacheTime, false, kSize);
- {
- // Let's mark existing resources as is_download_reported = 1 by calling
- // GetURLListForReferrerHost.
- std::vector<GURL> unused_a, unused_b;
- auto id = precache_database_->GetReferrerHost("foo.com").id;
- ASSERT_NE(PrecacheReferrerHostEntry::kInvalidId, id);
- precache_database_->GetURLListForReferrerHost(id, &unused_a, &unused_b);
- }
-
- precache_database_->RecordURLPrefetch(GURL(already_reported_and_in_cache),
- "foo.com", kPrecacheTime,
- true /* was_cached */, kSize);
- precache_database_->RecordURLPrefetch(GURL(already_reported_but_refetch),
- "foo.com", kPrecacheTime,
- false /* was_cached */, kSize);
- Flush();
-
- // Only the refetch resource should be reported as downloaded this time
- // around.
- std::vector<GURL> _, actual_downloaded_urls;
- auto referrer_id = precache_database_->GetReferrerHost("foo.com").id;
- EXPECT_NE(PrecacheReferrerHostEntry::kInvalidId, referrer_id);
- precache_database_->GetURLListForReferrerHost(referrer_id, &_,
- &actual_downloaded_urls);
- EXPECT_THAT(actual_downloaded_urls,
- ElementsAre(GURL(already_reported_but_refetch)));
-}
-
-} // namespace
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_fetcher.cc b/chromium/components/precache/core/precache_fetcher.cc
deleted file mode 100644
index a17c6ea199c..00000000000
--- a/chromium/components/precache/core/precache_fetcher.cc
+++ /dev/null
@@ -1,972 +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/precache/core/precache_fetcher.h"
-
-#include <algorithm>
-#include <limits>
-#include <set>
-#include <utility>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/command_line.h"
-#include "base/compiler_specific.h"
-#include "base/containers/hash_tables.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/optional.h"
-#include "base/sha1.h"
-#include "base/strings/string_piece.h"
-#include "base/task_runner_util.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/precache/core/precache_database.h"
-#include "components/precache/core/precache_manifest_util.h"
-#include "components/precache/core/precache_switches.h"
-#include "components/precache/core/proto/quota.pb.h"
-#include "components/precache/core/proto/unfinished_work.pb.h"
-#include "net/base/completion_callback.h"
-#include "net/base/escape.h"
-#include "net/base/io_buffer.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
-#include "net/base/url_util.h"
-#include "net/http/http_response_headers.h"
-#include "net/url_request/url_fetcher_response_writer.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
-
-namespace precache {
-
-// The following flags are for privacy reasons. For example, if a user clears
-// their cookies, but a tracking beacon is prefetched and the beacon specifies
-// its source URL in a URL param, the beacon site would be able to rebuild a
-// profile of the user. All three flags should occur together, or not at all,
-// per
-// https://groups.google.com/a/chromium.org/d/topic/net-dev/vvcodRV6SdM/discussion.
-const int kNoTracking =
- net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA;
-
-// The maximum number of URLFetcher requests that can be in flight in parallel.
-// Note that OnManifestFetchComplete and OnResourceFetchComplete perform
-// remove_if operations which are O(kMaxParallelFetches). Those should be
-// optimized before increasing this value significantly.
-const int kMaxParallelFetches = 10;
-
-namespace {
-
-// The maximum for the Precache.Fetch.ResponseBytes.* histograms. We set this to
-// a number we expect to be in the 99th percentile for the histogram, give or
-// take.
-const int kMaxResponseBytes = 500 * 1024 * 1024;
-
-GURL GetDefaultConfigURL() {
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- if (command_line.HasSwitch(switches::kPrecacheConfigSettingsURL)) {
- return GURL(
- command_line.GetSwitchValueASCII(switches::kPrecacheConfigSettingsURL));
- }
-
-#if defined(PRECACHE_CONFIG_SETTINGS_URL)
- return GURL(PRECACHE_CONFIG_SETTINGS_URL);
-#else
- // The precache config settings URL could not be determined, so return an
- // empty, invalid GURL.
- return GURL();
-#endif
-}
-
-std::string GetDefaultManifestURLPrefix() {
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- if (command_line.HasSwitch(switches::kPrecacheManifestURLPrefix)) {
- return command_line.GetSwitchValueASCII(
- switches::kPrecacheManifestURLPrefix);
- }
-
-#if defined(PRECACHE_MANIFEST_URL_PREFIX)
- return PRECACHE_MANIFEST_URL_PREFIX;
-#else
- // The precache manifest URL prefix could not be determined, so return an
- // empty string.
- return std::string();
-#endif
-}
-
-// Attempts to parse a protobuf message from the response string of a
-// URLFetcher. If parsing is successful, the message parameter will contain the
-// parsed protobuf and this function will return true. Otherwise, returns false.
-bool ParseProtoFromFetchResponse(const net::URLFetcher& source,
- ::google::protobuf::MessageLite* message) {
- std::string response_string;
-
- if (!source.GetStatus().is_success()) {
- DLOG(WARNING) << "Fetch failed: " << source.GetOriginalURL().spec();
- return false;
- }
- if (!source.GetResponseAsString(&response_string)) {
- DLOG(WARNING) << "No response string present: "
- << source.GetOriginalURL().spec();
- return false;
- }
- if (!message->ParseFromString(response_string)) {
- DLOG(WARNING) << "Unable to parse proto served from "
- << source.GetOriginalURL().spec();
- return false;
- }
- return true;
-}
-
-// URLFetcherResponseWriter that ignores the response body, in order to avoid
-// the unnecessary memory usage. Use it rather than the default if you don't
-// care about parsing the response body. We use it below as a means to populate
-// the cache with requested resource URLs.
-class URLFetcherNullWriter : public net::URLFetcherResponseWriter {
- public:
- int Initialize(const net::CompletionCallback& callback) override {
- return net::OK;
- }
-
- int Write(net::IOBuffer* buffer,
- int num_bytes,
- const net::CompletionCallback& callback) override {
- return num_bytes;
- }
-
- int Finish(int net_error, const net::CompletionCallback& callback) override {
- return net::OK;
- }
-};
-
-// Returns the base64 encoded resource URL hashes. The resource URLs are hashed
-// individually, and 8 bytes of each hash is appended together, which is then
-// encoded to base64.
-std::string GetResourceURLBase64Hash(const std::vector<GURL>& urls) {
- // Each resource hash uses 8 bytes, instead of the 20 bytes of sha1 hash, as a
- // tradeoff between sending more bytes and reducing hash collisions.
- const size_t kHashBytesSize = 8;
- std::string hashes;
- hashes.reserve(urls.size() * kHashBytesSize);
-
- for (const auto& url : urls) {
- const std::string& url_spec = url.spec();
- unsigned char sha1_hash[base::kSHA1Length];
- base::SHA1HashBytes(
- reinterpret_cast<const unsigned char*>(url_spec.c_str()),
- url_spec.size(), sha1_hash);
- hashes.append(reinterpret_cast<const char*>(sha1_hash), kHashBytesSize);
- }
- base::Base64Encode(hashes, &hashes);
- return hashes;
-}
-
-// Retrieves the manifest info on the DB thread. Manifest info for each of the
-// hosts in |hosts_to_fetch|, is added to |hosts_info|.
-std::deque<ManifestHostInfo> RetrieveManifestInfo(
- const base::WeakPtr<PrecacheDatabase>& precache_database,
- std::vector<std::pair<std::string, int64_t>> hosts_to_fetch) {
- std::deque<ManifestHostInfo> hosts_info;
- if (!precache_database)
- return hosts_info;
-
- for (const auto& host : hosts_to_fetch) {
- auto referrer_host_info = precache_database->GetReferrerHost(host.first);
- if (referrer_host_info.id != PrecacheReferrerHostEntry::kInvalidId) {
- std::vector<GURL> used_urls, downloaded_urls;
- precache_database->GetURLListForReferrerHost(
- referrer_host_info.id, &used_urls, &downloaded_urls);
- hosts_info.push_back(
- ManifestHostInfo(referrer_host_info.manifest_id, host.first,
- host.second, GetResourceURLBase64Hash(used_urls),
- GetResourceURLBase64Hash(downloaded_urls)));
- } else {
- hosts_info.push_back(
- ManifestHostInfo(PrecacheReferrerHostEntry::kInvalidId, host.first,
- host.second, std::string(), std::string()));
- }
- }
- return hosts_info;
-}
-
-PrecacheQuota RetrieveQuotaInfo(
- const base::WeakPtr<PrecacheDatabase>& precache_database) {
- PrecacheQuota quota;
- if (precache_database) {
- quota = precache_database->GetQuota();
- }
- return quota;
-}
-
-// Returns true if the |quota| time has expired.
-bool IsQuotaTimeExpired(const PrecacheQuota& quota,
- const base::Time& time_now) {
- // Quota expires one day after the start time.
- base::Time start_time = base::Time::FromInternalValue(quota.start_time());
- return start_time > time_now ||
- start_time + base::TimeDelta::FromDays(1) < time_now;
-}
-
-// Models the expected number of requests for the resource, given that
-// resource_weight_ratio is the probability of a request given a visit to the
-// host, and host_visits is the number of visits to the host in 30 days.
-double NaiveResourceWeight(double resource_weight_ratio, int64_t host_visits) {
- return resource_weight_ratio * host_visits;
-}
-
-// Models the probability of at least one request for the resource, given that
-// resource_weight_ratio is the probability of a request given a visit to the
-// host, and host_visits is the number of visits to the host in 30 days.
-double GeometricResourceWeight(double resource_weight_ratio,
- int64_t host_visits) {
- return 1 - pow(1 - resource_weight_ratio, host_visits);
-}
-
-} // namespace
-
-// Returns the weight of the resource. When global ranking is enabled, the
-// fetches are sorted by descending weight. Parameters:
-// function: Which combination function to use.
-// resource_weight_ratio: The weight_ratio of the resource.
-// host_visits: The count of visits to the given host in the past 30 days.
-double ResourceWeight(
- PrecacheConfigurationSettings::ResourceWeightFunction function,
- double resource_weight_ratio,
- int64_t host_visits) {
- switch (function) {
- case PrecacheConfigurationSettings::FUNCTION_NAIVE:
- return NaiveResourceWeight(resource_weight_ratio, host_visits);
- case PrecacheConfigurationSettings::FUNCTION_GEOMETRIC:
- return GeometricResourceWeight(resource_weight_ratio, host_visits);
- default:
- DLOG(FATAL) << "Unknown function " << function;
- return 0;
- }
-}
-
-PrecacheFetcher::Fetcher::Fetcher(
- net::URLRequestContextGetter* request_context,
- const GURL& url,
- const std::string& referrer,
- const base::Callback<void(const Fetcher&)>& callback,
- bool is_resource_request,
- size_t max_bytes,
- bool revalidation_only)
- : request_context_(request_context),
- url_(url),
- referrer_(referrer),
- callback_(callback),
- is_resource_request_(is_resource_request),
- max_bytes_(max_bytes),
- revalidation_only_(revalidation_only),
- response_bytes_(0),
- network_response_bytes_(0),
- was_cached_(false) {
- DCHECK(url.is_valid());
- if (is_resource_request_)
- LoadFromCache();
- else
- LoadFromNetwork();
-}
-
-PrecacheFetcher::Fetcher::~Fetcher() {}
-
-void PrecacheFetcher::Fetcher::LoadFromCache() {
- fetch_stage_ = FetchStage::CACHE;
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("wifi_prefetch_from_cache", R"(
- semantics {
- sender: "Wifi Prefetch"
- description:
- "Speeds up mobile web page loads by downloading some common static "
- "assets (such as JS and CSS) for sites that the user browses "
- "frequently, in advance of the browser needing them. Only applies "
- "to users with tab sync enabled."
- trigger:
- "Background service that runs when the device is plugged into "
- "power, on unmetered wifi, and Chromium is not in the foreground."
- data:
- "Local cache fetches; no data is sent over the network."
- destination: OTHER
- }
- policy {
- cookies_allowed: false
- setting:
- "Users can disable this feature by several settings: Disabling tab "
- "sync via unchecking 'Open tabs' in Chromium settings under "
- "'Advanced sync settings'; Disabling predicting required downloads "
- "via unchecking 'Use a prediction service to load pages more "
- "quickly' in Chromium settings under Privacy; Enabling 'Data "
- "Saver' in Chromium settings on Android."
- chrome_policy {
- NetworkPredictionOptions {
- policy_options {mode: MANDATORY}
- NetworkPredictionOptions: 2
- }
- }
- })");
- cache_url_fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this,
- traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- cache_url_fetcher_.get(),
- data_use_measurement::DataUseUserData::PRECACHE);
- cache_url_fetcher_->SetRequestContext(request_context_);
- cache_url_fetcher_->SetLoadFlags(net::LOAD_ONLY_FROM_CACHE |
- net::LOAD_SKIP_CACHE_VALIDATION |
- kNoTracking);
- std::unique_ptr<URLFetcherNullWriter> null_writer(new URLFetcherNullWriter);
- cache_url_fetcher_->SaveResponseWithWriter(std::move(null_writer));
- cache_url_fetcher_->Start();
-}
-
-void PrecacheFetcher::Fetcher::LoadFromNetwork() {
- fetch_stage_ = FetchStage::NETWORK;
- if (is_resource_request_) {
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation(
- "wifi_prefetch_resource_from_network", R"(
- semantics {
- sender: "Wifi Prefetch"
- description:
- "Speeds up mobile web page loads by downloading common static "
- "assets (such as JS and CSS) for sites that the user browses "
- "frequently, in advance of the browser needing them. Only "
- "applies to users with tab sync enabled."
- trigger:
- "Background service that runs when the device is plugged into "
- "power, on unmetered wifi, and Chromium is not in the "
- "foreground."
- data: "Link to the requested resrouce."
- destination: WEBSITE
- }
- policy {
- cookies_allowed: false
- setting:
- "Users can disable this feature by several settings: Disabling "
- "tab sync via unchecking 'Open tabs' in Chromium settings "
- "under 'Advanced sync settings'; Disabling predicting required "
- "downloads via unchecking 'Use a prediction service to load "
- "pages more quickly' in Chromium settings under Privacy; "
- "Enabling 'Data Saver' in Chromium settings on Android."
- chrome_policy {
- NetworkPredictionOptions {
- policy_options {mode: MANDATORY}
- NetworkPredictionOptions: 2
- }
- }
- })");
- network_url_fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET,
- this, traffic_annotation);
- } else {
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("wifi_prefetch_sites_from_network",
- R"(
- semantics {
- sender: "Wifi Prefetch"
- description:
- "Speeds up mobile web page loads by downloading common static "
- "assets (such as JS and CSS) for sites that the user browses "
- "frequently, in advance of the browser needing them. The first "
- "step is to download the list of common static assets from "
- "Google. Only applies to users with tab sync enabled."
- trigger:
- "Background service that runs when the device is plugged into "
- "power, on unmetered wifi, and Chromium is not in the foreground."
- data: "A list of the top hosts that the user visits."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: false
- setting:
- "Users can disable this feature by several settings: Disabling "
- "tab sync via unchecking 'Open tabs' in Chromium settings under "
- "'Advanced sync settings'; Disabling predicting required "
- "downloads via unchecking 'Use a prediction service to load "
- "pages more quickly' in Chromium settings under Privacy; "
- "Enabling 'Data Saver' in Chromium settings on Android."
- chrome_policy {
- NetworkPredictionOptions {
- policy_options {mode: MANDATORY}
- NetworkPredictionOptions: 2
- }
- }
- })");
- network_url_fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET,
- this, traffic_annotation);
- }
-
- data_use_measurement::DataUseUserData::AttachToFetcher(
- network_url_fetcher_.get(),
- data_use_measurement::DataUseUserData::PRECACHE);
- network_url_fetcher_->SetRequestContext(request_context_);
- if (is_resource_request_) {
- // LOAD_VALIDATE_CACHE allows us to refresh Date headers for resources
- // already in the cache. The Date headers are updated from 304s as well as
- // 200s.
- network_url_fetcher_->SetLoadFlags(net::LOAD_VALIDATE_CACHE | kNoTracking);
- // We don't need a copy of the response body for resource requests. The
- // request is issued only to populate the browser cache.
- std::unique_ptr<URLFetcherNullWriter> null_writer(new URLFetcherNullWriter);
- network_url_fetcher_->SaveResponseWithWriter(std::move(null_writer));
- } else {
- // Config and manifest requests do not need to be revalidated. It's okay if
- // they expire from the cache minutes after we request them.
- network_url_fetcher_->SetLoadFlags(kNoTracking);
- }
- network_url_fetcher_->Start();
-}
-
-void PrecacheFetcher::Fetcher::OnURLFetchDownloadProgress(
- const net::URLFetcher* source,
- int64_t current,
- int64_t total,
- int64_t current_network_bytes) {
- // If network bytes going over the per-resource download cap.
- if (fetch_stage_ == FetchStage::NETWORK &&
- // |current_network_bytes| is guaranteed to be non-negative, so this cast
- // is safe.
- static_cast<size_t>(current_network_bytes) > max_bytes_) {
- // Call the completion callback, to attempt the next download, or to trigger
- // cleanup in precache_delegate_->OnDone().
- response_bytes_ = current;
- network_response_bytes_ = current_network_bytes;
- was_cached_ = source->WasCached();
-
- UMA_HISTOGRAM_CUSTOM_COUNTS("Precache.Fetch.ResponseBytes.NetworkWasted",
- network_response_bytes_, 1,
- 1024 * 1024 /* 1 MB */, 100);
- // Cancel the download.
- network_url_fetcher_.reset();
- callback_.Run(*this);
- }
-}
-
-void PrecacheFetcher::Fetcher::OnURLFetchComplete(
- const net::URLFetcher* source) {
- CHECK(source);
- if (fetch_stage_ == FetchStage::CACHE &&
- ((source->GetStatus().error() == net::ERR_CACHE_MISS &&
- !revalidation_only_) ||
- (source->GetResponseHeaders() &&
- source->GetResponseHeaders()->HasValidators()))) {
- // If the resource was not found in the cache, request it from the
- // network.
- //
- // If the resource was found in the cache, but contains validators,
- // request a refresh. The presence of validators increases the chance that
- // we get a 304 response rather than a full one, thus allowing us to
- // refresh the cache with minimal network load.
- LoadFromNetwork();
- return;
- }
-
- // If any of:
- // - The request was for a config or manifest.
- // - The resource was a cache hit without validators.
- // - The response came from the network.
- // Then Fetcher is done with this URL and can return control to the caller.
- response_bytes_ = source->GetReceivedResponseContentLength();
- network_response_bytes_ = source->GetTotalReceivedBytes();
- was_cached_ = source->WasCached();
- callback_.Run(*this);
-}
-
-// static
-void PrecacheFetcher::RecordCompletionStatistics(
- const PrecacheUnfinishedWork& unfinished_work,
- size_t remaining_manifest_urls_to_fetch,
- size_t remaining_resource_urls_to_fetch) {
- // These may be unset in tests.
- if (!unfinished_work.has_start_time())
- return;
- base::TimeDelta time_to_fetch =
- base::Time::Now() -
- base::Time::FromInternalValue(unfinished_work.start_time());
- UMA_HISTOGRAM_CUSTOM_TIMES("Precache.Fetch.TimeToComplete", time_to_fetch,
- base::TimeDelta::FromSeconds(1),
- base::TimeDelta::FromHours(4), 50);
-
- int num_total_resources = unfinished_work.num_resource_urls();
- int percent_completed =
- num_total_resources == 0
- ? 101 // Overflow bucket.
- : (100 * (static_cast<double>(num_total_resources -
- remaining_resource_urls_to_fetch) /
- num_total_resources));
-
- UMA_HISTOGRAM_PERCENTAGE("Precache.Fetch.PercentCompleted",
- percent_completed);
- UMA_HISTOGRAM_CUSTOM_COUNTS("Precache.Fetch.ResponseBytes.Total",
- unfinished_work.total_bytes(), 1,
- kMaxResponseBytes, 100);
- UMA_HISTOGRAM_CUSTOM_COUNTS("Precache.Fetch.ResponseBytes.Network",
- unfinished_work.network_bytes(), 1,
- kMaxResponseBytes, 100);
-
- if (unfinished_work.has_min_weight_fetched()) {
- UMA_HISTOGRAM_COUNTS_1000("Precache.Fetch.MinWeight",
- unfinished_work.min_weight_fetched() * 1000);
- }
-}
-
-// static
-std::string PrecacheFetcher::GetResourceURLBase64HashForTesting(
- const std::vector<GURL>& urls) {
- return GetResourceURLBase64Hash(urls);
-}
-
-PrecacheFetcher::PrecacheFetcher(
- net::URLRequestContextGetter* request_context,
- const GURL& config_url,
- const std::string& manifest_url_prefix,
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work,
- uint32_t experiment_id,
- const base::WeakPtr<PrecacheDatabase>& precache_database,
- const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner,
- PrecacheFetcher::PrecacheDelegate* precache_delegate)
- : request_context_(request_context),
- config_url_(config_url),
- manifest_url_prefix_(manifest_url_prefix),
- precache_database_(precache_database),
- db_task_runner_(std::move(db_task_runner)),
- precache_delegate_(precache_delegate),
- pool_(kMaxParallelFetches),
- experiment_id_(experiment_id) {
- DCHECK(request_context_.get()); // Request context must be non-NULL.
- DCHECK(precache_delegate_); // Precache delegate must be non-NULL.
-
- DCHECK_NE(GURL(), GetDefaultConfigURL())
- << "Could not determine the precache config settings URL.";
- DCHECK_NE(std::string(), GetDefaultManifestURLPrefix())
- << "Could not determine the default precache manifest URL prefix.";
- DCHECK(unfinished_work);
-
- // Copy resources to member variable as a convenience.
- // TODO(rajendrant): Consider accessing these directly from the proto, by
- // keeping track of the current resource index.
- for (const auto& resource : unfinished_work->resource()) {
- if (resource.has_url() && resource.has_top_host_name()) {
- resources_to_fetch_.emplace_back(
- GURL(resource.url()), resource.top_host_name(), resource.weight());
- }
- }
- unfinished_work_ = std::move(unfinished_work);
-}
-
-PrecacheFetcher::~PrecacheFetcher() {
-}
-
-std::unique_ptr<PrecacheUnfinishedWork> PrecacheFetcher::CancelPrecaching() {
- // This could get called multiple times, and it should be handled gracefully.
- if (!unfinished_work_)
- return nullptr;
-
- unfinished_work_->clear_resource();
- if (unfinished_work_->has_config_settings()) {
- // If config fetch is incomplete, |top_hosts_to_fetch_| will be empty and
- // top hosts should be left as is in |unfinished_work_|.
- unfinished_work_->clear_top_host();
- for (const auto& top_host : top_hosts_fetching_)
- unfinished_work_->add_top_host()->set_hostname(top_host.hostname);
- for (const auto& top_host : top_hosts_to_fetch_)
- unfinished_work_->add_top_host()->set_hostname(top_host.hostname);
- }
- for (const auto& resource : resources_fetching_) {
- auto* new_resource = unfinished_work_->add_resource();
- new_resource->set_url(resource.url.spec());
- new_resource->set_top_host_name(resource.referrer);
- new_resource->set_weight(resource.weight);
- }
- for (const auto& resource : resources_to_fetch_) {
- auto* new_resource = unfinished_work_->add_resource();
- new_resource->set_url(resource.url.spec());
- new_resource->set_top_host_name(resource.referrer);
- new_resource->set_weight(resource.weight);
- }
- top_hosts_fetching_.clear();
- top_hosts_to_fetch_.clear();
- resources_fetching_.clear();
- resources_to_fetch_.clear();
- pool_.DeleteAll();
- return std::move(unfinished_work_);
-}
-
-void PrecacheFetcher::Start() {
- if (unfinished_work_->has_config_settings()) {
- DCHECK(unfinished_work_->has_start_time());
- DetermineManifests();
- return;
- }
-
- GURL config_url =
- config_url_.is_empty() ? GetDefaultConfigURL() : config_url_;
-
- DCHECK(config_url.is_valid()) << "Config URL not valid: "
- << config_url.possibly_invalid_spec();
-
- // Fetch the precache configuration settings from the server.
- DCHECK(pool_.IsEmpty()) << "All parallel requests should be available";
- pool_.Add(base::MakeUnique<Fetcher>(
- request_context_.get(), config_url, std::string(),
- base::Bind(&PrecacheFetcher::OnConfigFetchComplete, AsWeakPtr()),
- false /* is_resource_request */, std::numeric_limits<int32_t>::max(),
- false /* revalidation_only */));
-}
-
-void PrecacheFetcher::StartNextResourceFetch() {
- DCHECK(unfinished_work_->has_config_settings());
- while (!resources_to_fetch_.empty() && pool_.IsAvailable()) {
- ResourceInfo& resource = resources_to_fetch_.front();
- const size_t max_bytes = std::min(
- quota_.remaining(),
- std::min(unfinished_work_->config_settings().max_bytes_per_resource(),
- unfinished_work_->config_settings().max_bytes_total() -
- unfinished_work_->total_bytes()));
- pool_.Add(base::MakeUnique<Fetcher>(
- request_context_.get(), resource.url, resource.referrer,
- base::Bind(&PrecacheFetcher::OnResourceFetchComplete, AsWeakPtr()),
- true /* is_resource_request */, max_bytes,
- unfinished_work_->config_settings().revalidation_only()));
-
- resources_fetching_.push_back(std::move(resource));
- resources_to_fetch_.pop_front();
- }
-}
-
-void PrecacheFetcher::StartNextManifestFetches() {
- // We fetch as many manifests at a time as possible, as we need all resource
- // URLs in memory in order to rank them.
- while (!top_hosts_to_fetch_.empty() && pool_.IsAvailable()) {
- ManifestHostInfo& top_host = top_hosts_to_fetch_.front();
- pool_.Add(base::MakeUnique<Fetcher>(
- request_context_.get(), top_host.manifest_url, top_host.hostname,
- base::Bind(&PrecacheFetcher::OnManifestFetchComplete, AsWeakPtr(),
- top_host.visits),
- false /* is_resource_request */, std::numeric_limits<int32_t>::max(),
- false /* revalidation_only */));
- top_hosts_fetching_.push_back(std::move(top_host));
- top_hosts_to_fetch_.pop_front();
- }
-}
-
-void PrecacheFetcher::NotifyDone(size_t remaining_manifest_urls_to_fetch,
- size_t remaining_resource_urls_to_fetch) {
- RecordCompletionStatistics(*unfinished_work_,
- remaining_manifest_urls_to_fetch,
- remaining_resource_urls_to_fetch);
- precache_delegate_->OnDone();
-}
-
-void PrecacheFetcher::StartNextFetch() {
- DCHECK(unfinished_work_->has_config_settings());
-
- // If over the precache total size cap or daily quota, then stop prefetching.
- if ((unfinished_work_->total_bytes() >
- unfinished_work_->config_settings().max_bytes_total()) ||
- quota_.remaining() == 0) {
- pool_.DeleteAll();
- NotifyDone(top_hosts_to_fetch_.size() + top_hosts_fetching_.size(),
- resources_to_fetch_.size() + resources_fetching_.size());
- return;
- }
-
- StartNextResourceFetch();
- StartNextManifestFetches();
- if (top_hosts_to_fetch_.empty() && resources_to_fetch_.empty() &&
- pool_.IsEmpty()) {
- // There are no more URLs to fetch, so end the precache cycle.
- NotifyDone(0, 0);
- // OnDone may have deleted this PrecacheFetcher, so don't do anything after
- // it is called.
- }
-}
-
-void PrecacheFetcher::OnConfigFetchComplete(const Fetcher& source) {
- UpdateStats(source.response_bytes(), source.network_response_bytes());
- if (source.network_url_fetcher() == nullptr) {
- pool_.DeleteAll(); // Cancel any other ongoing request.
- } else {
- // Attempt to parse the config proto. On failure, continue on with the
- // default configuration.
- ParseProtoFromFetchResponse(
- *source.network_url_fetcher(),
- unfinished_work_->mutable_config_settings());
- pool_.Delete(source);
- DetermineManifests();
- }
-}
-
-void PrecacheFetcher::DetermineManifests() {
- DCHECK(unfinished_work_->has_config_settings());
-
- std::vector<std::pair<std::string, int64_t>> top_hosts_to_fetch;
- // Keep track of manifest URLs that are being fetched, in order to elide
- // duplicates.
- std::set<base::StringPiece> seen_top_hosts;
- int64_t rank = 0;
-
- for (const auto& host : unfinished_work_->top_host()) {
- ++rank;
- if (rank > unfinished_work_->config_settings().top_sites_count())
- break;
- if (seen_top_hosts.insert(host.hostname()).second)
- top_hosts_to_fetch.emplace_back(host.hostname(), host.visits());
- }
-
- // Attempt to fetch manifests for starting hosts up to the maximum top sites
- // count. If a manifest does not exist for a particular starting host, then
- // the fetch will fail, and that starting host will be ignored. Starting
- // hosts are not added if this is a continuation from a previous precache
- // session.
- if (resources_to_fetch_.empty()) {
- for (const std::string& host :
- unfinished_work_->config_settings().forced_site()) {
- // We add a forced site with visits == 0, which means its resources will
- // be downloaded last. TODO(twifkak): Consider removing support for
- // forced_site.
- if (seen_top_hosts.insert(host).second)
- top_hosts_to_fetch.emplace_back(host, 0);
- }
- }
- // We retrieve manifest usage and quota info from the local database before
- // fetching the manifests.
- PostTaskAndReplyWithResult(
- db_task_runner_.get(), FROM_HERE,
- base::Bind(&RetrieveManifestInfo, precache_database_,
- std::move(top_hosts_to_fetch)),
- base::Bind(&PrecacheFetcher::OnManifestInfoRetrieved, AsWeakPtr()));
-}
-
-void PrecacheFetcher::OnManifestInfoRetrieved(
- std::deque<ManifestHostInfo> manifests_info) {
- const std::string prefix = manifest_url_prefix_.empty()
- ? GetDefaultManifestURLPrefix()
- : manifest_url_prefix_;
- if (!GURL(prefix).is_valid()) {
- // Don't attempt to fetch any manifests if the manifest URL prefix
- // is invalid.
- top_hosts_to_fetch_.clear();
- unfinished_work_->set_num_manifest_urls(manifests_info.size());
- NotifyDone(manifests_info.size(), resources_to_rank_.size());
- return;
- }
-
- top_hosts_to_fetch_ = std::move(manifests_info);
- for (auto& manifest : top_hosts_to_fetch_) {
- manifest.manifest_url =
- GURL(prefix +
- net::EscapeQueryParamValue(
- net::EscapeQueryParamValue(manifest.hostname, false), false));
- if (manifest.manifest_id != PrecacheReferrerHostEntry::kInvalidId) {
- manifest.manifest_url = net::AppendOrReplaceQueryParameter(
- manifest.manifest_url, "manifest",
- std::to_string(manifest.manifest_id));
- manifest.manifest_url = net::AppendOrReplaceQueryParameter(
- manifest.manifest_url, "used_resources", manifest.used_url_hash);
- manifest.manifest_url = net::AppendOrReplaceQueryParameter(
- manifest.manifest_url, "d", manifest.downloaded_url_hash);
- DCHECK(manifest.manifest_url.is_valid());
- }
- }
- unfinished_work_->set_num_manifest_urls(top_hosts_to_fetch_.size());
-
- PostTaskAndReplyWithResult(
- db_task_runner_.get(), FROM_HERE,
- base::Bind(&RetrieveQuotaInfo, precache_database_),
- base::Bind(&PrecacheFetcher::OnQuotaInfoRetrieved, AsWeakPtr()));
-}
-
-void PrecacheFetcher::OnQuotaInfoRetrieved(const PrecacheQuota& quota) {
- quota_ = quota;
- base::Time time_now = base::Time::Now();
- if (IsQuotaTimeExpired(quota_, time_now)) {
- // This is a new day. Update daily quota, that starts today and expires by
- // end of today.
-
- // If a previous day existed, report its usage.
- if (quota_.has_start_time()) {
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Precache.Fetch.ResponseBytes.Daily",
- unfinished_work_->config_settings().daily_quota_total() -
- quota_.remaining(),
- 1, kMaxResponseBytes, 100);
- }
-
- quota_.set_start_time(time_now.LocalMidnight().ToInternalValue());
- quota_.set_remaining(
- unfinished_work_->config_settings().daily_quota_total());
- db_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&PrecacheDatabase::SaveQuota, precache_database_, quota_));
- }
- StartNextFetch();
-}
-
-ManifestHostInfo::ManifestHostInfo(int64_t manifest_id,
- const std::string& hostname,
- int64_t visits,
- const std::string& used_url_hash,
- const std::string& downloaded_url_hash)
- : manifest_id(manifest_id),
- hostname(hostname),
- visits(visits),
- used_url_hash(used_url_hash),
- downloaded_url_hash(downloaded_url_hash) {}
-
-ManifestHostInfo::~ManifestHostInfo() {}
-
-ManifestHostInfo::ManifestHostInfo(ManifestHostInfo&&) = default;
-
-ManifestHostInfo& ManifestHostInfo::operator=(ManifestHostInfo&&) = default;
-
-ResourceInfo::ResourceInfo(const GURL& url,
- const std::string& referrer,
- double weight)
- : url(url), referrer(referrer), weight(weight) {}
-
-ResourceInfo::~ResourceInfo() {}
-
-ResourceInfo::ResourceInfo(ResourceInfo&&) = default;
-
-ResourceInfo& ResourceInfo::operator=(ResourceInfo&&) = default;
-
-void PrecacheFetcher::OnManifestFetchComplete(int64_t host_visits,
- const Fetcher& source) {
- DCHECK(unfinished_work_->has_config_settings());
- UpdateStats(source.response_bytes(), source.network_response_bytes());
- if (source.network_url_fetcher() == nullptr) {
- pool_.DeleteAll(); // Cancel any other ongoing request.
- } else {
- PrecacheManifest manifest;
-
- if (ParseProtoFromFetchResponse(*source.network_url_fetcher(), &manifest)) {
- precache_delegate_->OnManifestFetched(source.referrer(), manifest);
- const base::Optional<std::vector<bool>> resource_bitset =
- GetResourceBitset(manifest, experiment_id_);
- const int32_t included_resources_max =
- unfinished_work_->config_settings().top_resources_count();
- int32_t included_resources = 0;
- for (int i = 0; i < manifest.resource_size() &&
- included_resources < included_resources_max;
- ++i) {
- if ((!resource_bitset.has_value() || resource_bitset.value()[i]) &&
- manifest.resource(i).has_url()) {
- GURL url(manifest.resource(i).url());
- if (url.is_valid()) {
- double weight = ResourceWeight(
- unfinished_work_->config_settings().resource_weight_function(),
- manifest.resource(i).weight_ratio(), host_visits);
- if (weight >= unfinished_work_->config_settings().min_weight()) {
- resources_to_rank_.emplace_back(url, source.referrer(), weight);
- ++included_resources;
- }
- }
- }
- }
- db_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PrecacheDatabase::UpdatePrecacheReferrerHost,
- precache_database_, source.referrer(),
- manifest.id().id(), base::Time::Now()));
- }
- }
-
- top_hosts_fetching_.remove_if([&source](const ManifestHostInfo& top_host) {
- return top_host.manifest_url == source.url();
- });
-
- pool_.Delete(source);
-
- if (top_hosts_to_fetch_.empty() && top_hosts_fetching_.empty())
- QueueResourcesForFetch();
-
- StartNextFetch();
-}
-
-void PrecacheFetcher::QueueResourcesForFetch() {
- // Done fetching manifests. Now move resources_to_rank_ into
- // resources_to_fetch_, so that StartNextFetch will begin fetching resources.
- resources_to_fetch_ = std::move(resources_to_rank_);
-
- if (unfinished_work_->config_settings().global_ranking()) {
- // Sort resources_to_fetch_ by descending weight.
- std::stable_sort(resources_to_fetch_.begin(), resources_to_fetch_.end(),
- [](const ResourceInfo& first, const ResourceInfo& second) {
- return first.weight > second.weight;
- });
- }
-
- // Truncate to size |total_resources_count|.
- const size_t num_resources = std::min(
- resources_to_fetch_.size(),
- static_cast<size_t>(
- unfinished_work_->config_settings().total_resources_count()));
- resources_to_fetch_.erase(resources_to_fetch_.begin() + num_resources,
- resources_to_fetch_.end());
-
- // Save denominator for PercentCompleted UMA.
- unfinished_work_->set_num_resource_urls(resources_to_fetch_.size());
-}
-
-void PrecacheFetcher::OnResourceFetchComplete(const Fetcher& source) {
- UpdateStats(source.response_bytes(), source.network_response_bytes());
-
- db_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&PrecacheDatabase::RecordURLPrefetch, precache_database_,
- source.url(), source.referrer(), base::Time::Now(),
- source.was_cached(), source.response_bytes()));
-
- auto resource =
- std::find_if(resources_fetching_.begin(), resources_fetching_.end(),
- [&source](const ResourceInfo& resource) {
- return resource.url == source.url();
- });
- if (resource != resources_fetching_.end()) {
- if (unfinished_work_->config_settings().global_ranking() &&
- (!unfinished_work_->has_min_weight_fetched() ||
- resource->weight < unfinished_work_->min_weight_fetched()))
- unfinished_work_->set_min_weight_fetched(resource->weight);
-
- resources_fetching_.erase(resource);
- }
-
- pool_.Delete(source);
-
- // The resource has already been put in the cache during the fetch process, so
- // nothing more needs to be done for the resource.
- StartNextFetch();
-}
-
-void PrecacheFetcher::UpdateStats(int64_t response_bytes,
- int64_t network_response_bytes) {
- DCHECK_LE(0, response_bytes);
- DCHECK_LE(0, network_response_bytes);
-
- unfinished_work_->set_total_bytes(
- unfinished_work_->total_bytes() + response_bytes);
- unfinished_work_->set_network_bytes(
- unfinished_work_->network_bytes() + network_response_bytes);
-
- if (!IsQuotaTimeExpired(quota_, base::Time::Now())) {
- uint64_t used_bytes = static_cast<uint64_t>(network_response_bytes);
- int64_t remaining =
- static_cast<int64_t>(quota_.remaining()) - network_response_bytes;
- if (remaining < 0)
- remaining = 0;
- quota_.set_remaining(
- used_bytes > quota_.remaining() ? 0U : quota_.remaining() - used_bytes);
- db_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&PrecacheDatabase::SaveQuota, precache_database_, quota_));
- }
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_fetcher.h b/chromium/components/precache/core/precache_fetcher.h
deleted file mode 100644
index b8c937abb47..00000000000
--- a/chromium/components/precache/core/precache_fetcher.h
+++ /dev/null
@@ -1,369 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PRECACHE_CORE_PRECACHE_FETCHER_H_
-#define COMPONENTS_PRECACHE_CORE_PRECACHE_FETCHER_H_
-
-#include <stdint.h>
-
-#include <deque>
-#include <list>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.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 "base/time/time.h"
-#include "components/precache/core/fetcher_pool.h"
-#include "components/precache/core/proto/precache.pb.h"
-#include "components/precache/core/proto/quota.pb.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "url/gurl.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace net {
-class URLRequestContextGetter;
-}
-
-namespace precache {
-
-class PrecacheConfigurationSettings;
-class PrecacheDatabase;
-class PrecacheUnfinishedWork;
-
-// Visible for testing.
-extern const int kNoTracking;
-extern const int kMaxParallelFetches;
-
-// Information about the manifest for a host.
-struct ManifestHostInfo {
- ManifestHostInfo(int64_t manifest_id,
- const std::string& hostname,
- int64_t visits,
- const std::string& used_url_hash,
- const std::string& downloaded_url_hash);
- ~ManifestHostInfo();
- ManifestHostInfo(ManifestHostInfo&&);
- ManifestHostInfo& operator=(ManifestHostInfo&&);
- // Copy constructor and assignment operator are implicitly deleted.
-
- int64_t manifest_id;
- std::string hostname;
- GURL manifest_url;
- int64_t visits;
- std::string used_url_hash;
- std::string downloaded_url_hash;
-};
-
-// Information about a resource to be downloaded.
-struct ResourceInfo {
- ResourceInfo(const GURL& url, const std::string& referrer, double weight);
- ~ResourceInfo();
- ResourceInfo(ResourceInfo&&);
- ResourceInfo& operator=(ResourceInfo&&);
- // Copy constructor and assignment operator are implicitly deleted.
-
- GURL url; // The resource being requested.
- std::string referrer; // The host of the manifest requesting this resource.
- double weight; // Estimate of the expected utility of this resource.
-};
-
-// Public interface to code that fetches resources that the user is likely to
-// want to fetch in the future, putting them in the network stack disk cache.
-// Precaching is intended to be done when Chrome is not actively in use, likely
-// hours ahead of the time when the resources are actually needed.
-//
-// This class takes as input a prioritized list of URL domains that the user
-// commonly visits, referred to as starting hosts. This class interacts with a
-// server, sending it the list of starting hosts sequentially. For each starting
-// host, the server returns a manifest of resource URLs that are good candidates
-// for precaching. Every resource returned is fetched, and responses are cached
-// as they are received. Destroying the PrecacheFetcher while it is precaching
-// will cancel any fetch in progress and cancel precaching.
-//
-// The URLs of the server-side component must be specified in order for the
-// PrecacheFetcher to work. This includes the URL that the precache
-// configuration settings are fetched from and the prefix of URLs where precache
-// manifests are fetched from. These can be set by using command line switches
-// or by providing default values.
-//
-// Sample interaction:
-//
-// class MyPrecacheFetcherDelegate : public PrecacheFetcher::PrecacheDelegate {
-// public:
-// void PrecacheResourcesForTopURLs(
-// net::URLRequestContextGetter* request_context,
-// const std::list<GURL>& top_urls) {
-// fetcher_.reset(new PrecacheFetcher(...));
-// fetcher_->Start();
-// }
-//
-// void Cancel() {
-// std::unique_ptr<PrecacheUnfinishedWork> unfinished_work =
-// fetcher_->CancelPrecaching();
-// fetcher_.reset();
-// }
-//
-// virtual void OnDone() {
-// // Do something when precaching is done.
-// }
-//
-// private:
-// std::unique_ptr<PrecacheFetcher> fetcher_;
-// };
-class PrecacheFetcher : public base::SupportsWeakPtr<PrecacheFetcher> {
- public:
- class PrecacheDelegate {
- public:
- // Called when the fetching of resources has finished, whether the resources
- // were fetched or not. If the PrecacheFetcher is destroyed before OnDone is
- // called, then precaching will be canceled and OnDone will not be called.
- virtual void OnDone() = 0;
-
- // Called when a precache manifest has been successfully fetched and parsed.
- virtual void OnManifestFetched(const std::string& host,
- const PrecacheManifest& manifest) = 0;
- };
-
- // Visible for testing.
- class Fetcher;
-
- static void RecordCompletionStatistics(
- const PrecacheUnfinishedWork& unfinished_work,
- size_t remaining_manifest_urls_to_fetch,
- size_t remaining_resource_urls_to_fetch);
-
- static std::string GetResourceURLBase64HashForTesting(
- const std::vector<GURL>& urls);
-
- // Constructs a new PrecacheFetcher. The |unfinished_work| contains the
- // prioritized list of hosts that the user commonly visits. These hosts are
- // used by a server side component to construct a list of resource URLs that
- // the user is likely to fetch. Takes ownership of |unfinished_work|.
- // |precache_database| should be accessed only in |db_task_runner|.
- PrecacheFetcher(
- net::URLRequestContextGetter* request_context,
- const GURL& config_url,
- const std::string& manifest_url_prefix,
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work,
- uint32_t experiment_id,
- const base::WeakPtr<PrecacheDatabase>& precache_database,
- const scoped_refptr<base::SingleThreadTaskRunner>& db_task_runner,
- PrecacheDelegate* precache_delegate);
-
- virtual ~PrecacheFetcher();
-
- // Starts fetching resources to precache. URLs are fetched sequentially. Can
- // be called from any thread. Start should only be called once on a
- // PrecacheFetcher instance.
- void Start();
-
- // Stops all precaching work. The PreacheFetcher should not be used after
- // calling this method.
- std::unique_ptr<PrecacheUnfinishedWork> CancelPrecaching();
-
- private:
- friend class PrecacheFetcherTest;
- FRIEND_TEST_ALL_PREFIXES(PrecacheFetcherTest,
- GloballyRankResourcesAfterPauseResume);
- FRIEND_TEST_ALL_PREFIXES(PrecacheFetcherTest, FetcherPoolMaxLimitReached);
- FRIEND_TEST_ALL_PREFIXES(PrecacheFetcherTest,
- CancelPrecachingAfterAllManifestFetch);
- FRIEND_TEST_ALL_PREFIXES(PrecacheFetcherTest, DailyQuota);
-
- // Notifies the precache delete that precaching is done, and report
- // completion statistics.
- void NotifyDone(size_t remaining_manifest_urls_to_fetch,
- size_t remaining_resource_urls_to_fetch);
-
- // Fetches the next resource or manifest URL, if any remain. Fetching is done
- // sequentially and depth-first: all resources are fetched for a manifest
- // before the next manifest is fetched. This is done to limit the length of
- // the |resource_urls_to_fetch_| list, reducing the memory usage.
- void StartNextFetch();
-
- void StartNextManifestFetches();
- void StartNextResourceFetch();
-
- // Called when the precache configuration settings have been fetched.
- // Determines the list of manifest URLs to fetch according to the list of
- // |starting_hosts_| and information from the precache configuration settings.
- // If the fetch of the configuration settings fails, then precaching ends.
- void OnConfigFetchComplete(const Fetcher& source);
-
- // Constructs manifest URLs using a manifest URL prefix, and lists of hosts.
- void DetermineManifests();
-
- // Called when a precache manifest has been fetched. Builds the list of
- // resource URLs to fetch according to the URLs in the manifest. If the fetch
- // of a manifest fails, then it skips to the next manifest.
- void OnManifestFetchComplete(int64_t host_visits, const Fetcher& source);
-
- // Moves the pending resource URLs into the to-be-fetched queue, and sorts and
- // truncates if specified by the PrecacheConfigurationSettings. Called by
- // OnManifestFetchComplete after the last manifest is fetched, so that
- // StartNextFetch will begin fetching resource URLs.
- void QueueResourcesForFetch();
-
- // Called when a resource has been fetched.
- void OnResourceFetchComplete(const Fetcher& source);
-
- // Adds up the response sizes.
- void UpdateStats(int64_t response_bytes, int64_t network_response_bytes);
-
- // Callback invoked when the manifest info for all the top hosts is retrieved.
- void OnManifestInfoRetrieved(std::deque<ManifestHostInfo> manifests_info);
-
- // Callback invoked when the quota is retrieved.
- void OnQuotaInfoRetrieved(const PrecacheQuota& quota);
-
- // The request context used when fetching URLs.
- const scoped_refptr<net::URLRequestContextGetter> request_context_;
-
- // The custom URL to use when fetching the config. If not provided, the
- // default flag-specified URL will be used.
- const GURL config_url_;
-
- // The custom URL prefix to use when fetching manifests. If not provided, the
- // default flag-specified prefix will be used.
- const std::string manifest_url_prefix_;
-
- // PrecacheDatabase should be accessed on the DB thread.
- base::WeakPtr<PrecacheDatabase> precache_database_;
- scoped_refptr<base::SingleThreadTaskRunner> db_task_runner_;
-
- // Non-owning pointer. Should not be NULL.
- PrecacheDelegate* precache_delegate_;
-
- // Top hosts for which manifests still need to be fetched (i.e. no Fetcher has
- // been created yet).
- std::deque<ManifestHostInfo> top_hosts_to_fetch_;
-
- // Top hosts for which manifests are currently being fetched.
- std::list<ManifestHostInfo> top_hosts_fetching_;
-
- // Resources to be fetched, in desired fetch order. Populated only after
- // manifest fetching is complete.
- std::deque<ResourceInfo> resources_to_fetch_;
-
- // Resources currently being fetched, in the order requested.
- std::list<ResourceInfo> resources_fetching_;
-
- // Resources to be fetched, not yet ranked. Valid until manifest fetching is
- // done, after which resources are sorted and places in resources_to_fetch_.
- std::deque<ResourceInfo> resources_to_rank_;
-
- FetcherPool<Fetcher> pool_;
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work_;
-
- // Daily quota.
- PrecacheQuota quota_;
-
- // The fieldtrial experiment ID.
- uint32_t experiment_id_;
-
- DISALLOW_COPY_AND_ASSIGN(PrecacheFetcher);
-};
-
-// Visible for testing.
-double ResourceWeight(
- PrecacheConfigurationSettings::ResourceWeightFunction function,
- double resource_weight_ratio,
- int64_t host_visits);
-
-// Class that fetches a URL, and runs the specified callback when the fetch is
-// complete. This class exists so that a different method can be run in
-// response to different kinds of fetches, e.g. OnConfigFetchComplete when
-// configuration settings are fetched, OnManifestFetchComplete when a manifest
-// is fetched, etc.
-//
-// This class tries to increase freshness while limiting network usage, by using
-// the following strategy:
-// 1. Fetch the URL from the cache.
-// 2a. If it's present and lacks revalidation headers, then stop.
-// 2b. If it's not present, or it's present and has revalidation headers, then
-// refetch over the network.
-//
-// This allows the precache to "refresh" cache entries by increasing their
-// expiration date, but minimizes the network impact of doing so, by performing
-// only conditional GETs.
-//
-// On completion it calls the given callback. This class cancels requests whose
-// responses are or will be larger than max_bytes. In such cases,
-// network_url_fetcher() will return nullptr.
-class PrecacheFetcher::Fetcher : public net::URLFetcherDelegate {
- public:
- // Construct a new Fetcher. This will create and start a new URLFetcher
- // immediately. Parameters:
- // request_context: The request context to pass to the URLFetcher.
- // url: The URL to fetch.
- // referrer: The hostname of the manifest requesting this resource. Empty
- // for config fetches.
- // callback: Called when the fetch is finished or cancelled.
- // is_resource_request: If true, the URL may be refreshed using
- // LOAD_VALIDATE_CACHE.
- // max_bytes: The number of bytes to download before cancelling.
- // revalidation_only: If true, the URL is fetched only if it has an existing
- // cache entry with conditional headers.
- Fetcher(net::URLRequestContextGetter* request_context,
- const GURL& url,
- const std::string& referrer,
- const base::Callback<void(const Fetcher&)>& callback,
- bool is_resource_request,
- size_t max_bytes,
- bool revalidation_only);
- ~Fetcher() override;
- void OnURLFetchDownloadProgress(const net::URLFetcher* source,
- int64_t current,
- int64_t total,
- int64_t current_network_bytes) override;
- void OnURLFetchComplete(const net::URLFetcher* source) override;
- int64_t response_bytes() const { return response_bytes_; }
- int64_t network_response_bytes() const { return network_response_bytes_; }
- const net::URLFetcher* network_url_fetcher() const {
- return network_url_fetcher_.get();
- }
- const GURL& url() const { return url_; }
- const std::string& referrer() const { return referrer_; }
- bool is_resource_request() const { return is_resource_request_; }
- bool was_cached() const { return was_cached_; }
-
- private:
- enum class FetchStage { CACHE, NETWORK };
-
- void LoadFromCache();
- void LoadFromNetwork();
-
- // The arguments to this Fetcher's constructor.
- net::URLRequestContextGetter* const request_context_;
- const GURL url_;
- const std::string referrer_;
- const base::Callback<void(const Fetcher&)> callback_;
- const bool is_resource_request_;
- const size_t max_bytes_;
- const bool revalidation_only_;
-
- FetchStage fetch_stage_;
- // The cache_url_fetcher_ is kept alive until Fetcher destruction for testing.
- std::unique_ptr<net::URLFetcher> cache_url_fetcher_;
- std::unique_ptr<net::URLFetcher> network_url_fetcher_;
- int64_t response_bytes_;
- int64_t network_response_bytes_;
- bool was_cached_;
-
- DISALLOW_COPY_AND_ASSIGN(Fetcher);
-};
-
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CORE_PRECACHE_FETCHER_H_
diff --git a/chromium/components/precache/core/precache_fetcher_unittest.cc b/chromium/components/precache/core/precache_fetcher_unittest.cc
deleted file mode 100644
index e790f950656..00000000000
--- a/chromium/components/precache/core/precache_fetcher_unittest.cc
+++ /dev/null
@@ -1,2125 +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/precache/core/precache_fetcher.h"
-
-#include <stdint.h>
-
-#include <cstring>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/command_line.h"
-#include "base/compiler_specific.h"
-#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#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/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/precache/core/precache_database.h"
-#include "components/precache/core/precache_switches.h"
-#include "components/precache/core/proto/precache.pb.h"
-#include "components/precache/core/proto/unfinished_work.pb.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_response_info.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 "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace precache {
-
-namespace {
-
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::NotNull;
-using ::testing::Property;
-
-const char kConfigURL[] = "http://config-url.com";
-const char kManifestURLPrefix[] = "http://manifest-url-prefix.com/";
-const char kCustomConfigURL[] = "http://custom-config-url.com";
-const char kCustomManifestURLPrefix[] =
- "http://custom-manifest-url-prefix.com/";
-const char kManifestFetchFailureURL[] =
- "http://manifest-url-prefix.com/manifest-fetch-failure.com";
-const char kBadManifestURL[] =
- "http://manifest-url-prefix.com/bad-manifest.com";
-const char kGoodManifestURL[] =
- "http://manifest-url-prefix.com/good-manifest.com";
-const char kCustomGoodManifestURL[] =
- "http://custom-manifest-url-prefix.com/good-manifest.com";
-const char kResourceFetchFailureURL[] = "http://resource-fetch-failure.com";
-const char kGoodResourceURL[] = "http://good-resource.com";
-const char kGoodResourceURLA[] = "http://good-resource.com/a";
-const char kGoodResourceURLB[] = "http://good-resource.com/b";
-const char kGoodResourceURLC[] = "http://good-resource.com/c";
-const char kGoodResourceURLD[] = "http://good-resource.com/d";
-const char kForcedStartingURLManifestURL[] =
- "http://manifest-url-prefix.com/forced-starting-url.com";
-const uint32_t kExperimentID = 123;
-
-} // namespace
-
-class TestURLFetcherCallback {
- public:
- TestURLFetcherCallback() : total_response_bytes_(0) {}
-
- std::unique_ptr<net::FakeURLFetcher> CreateURLFetcher(
- const GURL& url,
- net::URLFetcherDelegate* delegate,
- const std::string& response_data,
- net::HttpStatusCode response_code,
- net::URLRequestStatus::Status status) {
- std::unique_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher(
- url, delegate, response_data, response_code, status));
-
- total_response_bytes_ += response_data.size();
- requested_urls_.push_back(url);
-
- return fetcher;
- }
-
- const std::vector<GURL>& requested_urls() const { return requested_urls_; }
-
- void clear_requested_urls() { requested_urls_.clear(); }
-
- int total_response_bytes() const { return total_response_bytes_; }
-
- private:
- std::vector<GURL> requested_urls_;
- int total_response_bytes_;
-};
-
-class TestPrecacheDelegate : public PrecacheFetcher::PrecacheDelegate {
- public:
- TestPrecacheDelegate()
- : on_done_was_called_(false) {}
-
- void OnDone() override {
- LOG(INFO) << "OnDone";
- on_done_was_called_ = true;
- }
-
- void OnManifestFetched(const std::string& host,
- const PrecacheManifest& manifest) override {
- hosts.push_back(host);
- }
-
- bool was_on_done_called() const {
- return on_done_was_called_;
- }
-
- void clear_manifest_hosts() { hosts.clear(); }
-
- std::vector<std::string> get_manifest_hosts() const { return hosts; }
-
- private:
- bool on_done_was_called_;
- std::vector<std::string> hosts;
-};
-
-class MockURLFetcherFactory : public net::URLFetcherFactory {
- public:
- typedef net::URLFetcher* DoURLFetcher(
- int id,
- const GURL& url,
- net::URLFetcher::RequestType request_type,
- net::URLFetcherDelegate* delegate);
-
- 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 base::WrapUnique(
- DoCreateURLFetcher(id, url, request_type, delegate));
- }
-
- // The method to mock out, instead of CreateURLFetcher. This is necessary
- // because gmock can't handle move-only types such as scoped_ptr.
- MOCK_METHOD4(DoCreateURLFetcher, DoURLFetcher);
-
- // A fake successful response. When the action runs, it saves off a pointer to
- // the FakeURLFetcher in its output parameter for later inspection.
- testing::Action<DoURLFetcher> RespondWith(const std::string& body,
- net::FakeURLFetcher** fetcher) {
- return RespondWith(body, [](net::FakeURLFetcher* fetcher) {
- fetcher->set_response_code(net::HTTP_OK);
- }, fetcher);
- }
-
- // A fake custom response. When the action runs, it runs the given modifier to
- // customize the FakeURLFetcher, and then saves off a pointer to the
- // FakeURLFetcher in its output parameter for later inspection. The modifier
- // should be a functor that takes a FakeURLFetcher* and returns void.
- template <typename F>
- testing::Action<DoURLFetcher> RespondWith(const std::string& body,
- F modifier,
- net::FakeURLFetcher** fetcher) {
- return testing::MakeAction(
- new FakeResponseAction<F>(body, modifier, fetcher));
- }
-
- private:
- template <typename F>
- class FakeResponseAction : public testing::ActionInterface<DoURLFetcher> {
- public:
- FakeResponseAction(const std::string& body,
- F modifier,
- net::FakeURLFetcher** fetcher)
- : body_(body), modifier_(modifier), fetcher_(fetcher) {}
-
- net::URLFetcher* Perform(
- const testing::tuple<int,
- const GURL&,
- net::URLFetcher::RequestType,
- net::URLFetcherDelegate*>& args) {
- auto* fetcher = new net::FakeURLFetcher(
- testing::get<1>(args), testing::get<3>(args), body_, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- modifier_(fetcher);
- if (fetcher_)
- *fetcher_ = fetcher;
- return fetcher;
- }
-
- private:
- std::string body_;
- F modifier_;
- net::FakeURLFetcher** fetcher_;
- };
-};
-
-class PrecacheFetcherFetcherTest : public testing::Test {
- public:
- PrecacheFetcherFetcherTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI),
- request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- scoped_url_fetcher_factory_(&factory_),
- callback_(base::Bind(&PrecacheFetcherFetcherTest::Callback,
- base::Unretained(this))) {}
-
- MOCK_METHOD1(Callback, void(const PrecacheFetcher::Fetcher&));
-
- protected:
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_;
- MockURLFetcherFactory factory_;
- net::ScopedURLFetcherFactory scoped_url_fetcher_factory_;
- base::Callback<void(const PrecacheFetcher::Fetcher&)> callback_;
-};
-
-void CacheMiss(net::FakeURLFetcher* fetcher) {
- fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_CACHE_MISS));
-}
-
-void HasETag(net::FakeURLFetcher* fetcher) {
- std::string raw_headers("HTTP/1.1 200 OK\0ETag: foo\0\0", 27);
- fetcher->set_response_headers(
- make_scoped_refptr(new net::HttpResponseHeaders(raw_headers)));
-}
-
-TEST_F(PrecacheFetcherFetcherTest, Config) {
- GURL url(kConfigURL);
-
- net::FakeURLFetcher* fetcher = nullptr;
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- .WillOnce(factory_.RespondWith("", &fetcher));
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- NotNull())));
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- false /* is_resource_request */, SIZE_MAX, false /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-
- ASSERT_NE(nullptr, fetcher);
- EXPECT_EQ(kNoTracking, fetcher->GetLoadFlags());
-}
-
-TEST_F(PrecacheFetcherFetcherTest, ResourceNotInCache) {
- GURL url(kGoodResourceURL);
-
- net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr;
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- .WillOnce(factory_.RespondWith("", CacheMiss, &fetcher1))
- .WillOnce(factory_.RespondWith("", &fetcher2));
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- NotNull())));
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- true /* is_resource_request */, SIZE_MAX, false /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-
- ASSERT_NE(nullptr, fetcher1);
- EXPECT_EQ(
- net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking,
- fetcher1->GetLoadFlags());
- ASSERT_NE(nullptr, fetcher2);
- EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags());
-}
-
-TEST_F(PrecacheFetcherFetcherTest, ResourceHasValidators) {
- GURL url(kGoodResourceURL);
-
- net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr;
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- .WillOnce(factory_.RespondWith("", HasETag, &fetcher1))
- .WillOnce(factory_.RespondWith("", &fetcher2));
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- NotNull())));
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- true /* is_resource_request */, SIZE_MAX, false /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-
- ASSERT_NE(nullptr, fetcher1);
- EXPECT_EQ(
- net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking,
- fetcher1->GetLoadFlags());
- ASSERT_NE(nullptr, fetcher2);
- EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags());
-}
-
-TEST_F(PrecacheFetcherFetcherTest, ResourceHasNoValidators) {
- GURL url(kGoodResourceURL);
-
- net::FakeURLFetcher* fetcher = nullptr;
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- .WillOnce(factory_.RespondWith("", &fetcher));
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- nullptr))); // It never reached the network.
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- true /* is_resource_request */, SIZE_MAX, false /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(
- net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking,
- fetcher->GetLoadFlags());
-}
-
-TEST_F(PrecacheFetcherFetcherTest, RevalidationOnlyResourceNotInCache) {
- GURL url(kGoodResourceURL);
-
- net::FakeURLFetcher* fetcher = nullptr;
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- .WillOnce(factory_.RespondWith("", CacheMiss, &fetcher));
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- nullptr))); // It never reached the network.
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- true /* is_resource_request */, SIZE_MAX, true /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-
- ASSERT_NE(nullptr, fetcher);
- EXPECT_EQ(
- net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking,
- fetcher->GetLoadFlags());
-}
-
-TEST_F(PrecacheFetcherFetcherTest, RevalidationOnlyResourceHasValidators) {
- GURL url(kGoodResourceURL);
-
- net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr;
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- .WillOnce(factory_.RespondWith("", HasETag, &fetcher1))
- .WillOnce(factory_.RespondWith("", &fetcher2));
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- NotNull())));
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- true /* is_resource_request */, SIZE_MAX, true /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-
- ASSERT_NE(nullptr, fetcher1);
- EXPECT_EQ(
- net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking,
- fetcher1->GetLoadFlags());
- ASSERT_NE(nullptr, fetcher2);
- EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags());
-}
-
-TEST_F(PrecacheFetcherFetcherTest, RevalidationOnlyResourceHasNoValidators) {
- GURL url(kGoodResourceURL);
-
- net::FakeURLFetcher* fetcher = nullptr;
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- .WillOnce(factory_.RespondWith("", &fetcher));
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- nullptr))); // It never reached the network.
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- true /* is_resource_request */, SIZE_MAX, true /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(
- net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking,
- fetcher->GetLoadFlags());
-}
-
-TEST_F(PrecacheFetcherFetcherTest, ResourceTooBig) {
- GURL url(kGoodResourceURL);
-
- EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _))
- // Cache request will fail, so that a network request is made. Only
- // network requests are byte-capped.
- .WillOnce(factory_.RespondWith("", CacheMiss, nullptr))
- .WillOnce(factory_.RespondWith(std::string(100, '.'), nullptr));
-
- // The callback should be called even though the download was cancelled, so
- // that the next download can start. The network_url_fetcher within should be
- // null, to signify that either the network was never reached (which will be
- // flagged as an error due to the expectation above) or it was requested but
- // cancelled (which is the desired behavior).
- EXPECT_CALL(*this,
- Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher,
- nullptr)));
-
- PrecacheFetcher::Fetcher precache_fetcher(
- request_context_.get(), url, url.host(), callback_,
- true /* is_resource_request */, 99 /* max_bytes */,
- false /* revalidation_only */);
-
- base::RunLoop().RunUntilIdle();
-}
-
-class PrecacheFetcherTest : public testing::Test {
- public:
- PrecacheFetcherTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI),
- task_runner_(base::ThreadTaskRunnerHandle::Get()),
- request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- factory_(NULL,
- base::Bind(&TestURLFetcherCallback::CreateURLFetcher,
- base::Unretained(&url_callback_))),
- expected_total_response_bytes_(0),
- parallel_fetches_beyond_capacity_(false) {}
-
- void UpdatePrecacheReferrerHost(const std::string& hostname,
- int64_t manifest_id) {
- precache_database_.UpdatePrecacheReferrerHost(hostname, manifest_id,
- base::Time());
- }
-
- void RecordURLPrefetch(const GURL& url, const std::string& referrer_host) {
- precache_database_.RecordURLPrefetch(url, referrer_host, base::Time::Now(),
- false /* was_cached */,
- 1000 /* size */);
- }
-
- void RecordURLNonPrefetch(const GURL& url) {
- net::HttpResponseInfo info;
- info.was_cached = true;
- info.headers = new net::HttpResponseHeaders(std::string());
- precache_database_.RecordURLNonPrefetch(url, base::Time::Now(), info,
- 1000 /* size */, 0 /* host_rank */,
- false /* is_connection_cellular */);
- }
-
- protected:
- void SetUp() override {
- ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
- base::FilePath db_path = scoped_temp_dir_.GetPath().Append(
- base::FilePath(FILE_PATH_LITERAL("precache_database")));
- precache_database_.Init(db_path);
- }
- void SetDefaultFlags() {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kPrecacheConfigSettingsURL, kConfigURL);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kPrecacheManifestURLPrefix, kManifestURLPrefix);
- }
-
- // Posts a task to check if more parallel fetches of precache manifest and
- // resource URLs were attempted beyond the fetcher pool maximum defined
- // capacity. The task will be posted repeatedly until such condition is met.
- void CheckUntilParallelFetchesBeyondCapacity(
- const PrecacheFetcher* precache_fetcher) {
- if (!precache_fetcher->pool_.IsAvailable() &&
- (!precache_fetcher->top_hosts_to_fetch_.empty() ||
- !precache_fetcher->resources_to_fetch_.empty())) {
- parallel_fetches_beyond_capacity_ = true;
- return;
- }
-
- // Check again after allowing the message loop to process some messages.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(
- &PrecacheFetcherTest::CheckUntilParallelFetchesBeyondCapacity,
- base::Unretained(this), precache_fetcher));
- }
-
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() const {
- return task_runner_;
- }
-
- // To allow friend access.
- void Flush() { precache_database_.Flush(); }
-
- // Must be declared first so that it is destroyed last.
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_;
- TestURLFetcherCallback url_callback_;
- net::FakeURLFetcherFactory factory_;
- TestPrecacheDelegate precache_delegate_;
- base::ScopedTempDir scoped_temp_dir_;
- PrecacheDatabase precache_database_;
- int expected_total_response_bytes_;
-
- // True if more parallel fetches were attempted beyond the fetcher pool
- // maximum capacity.
- bool parallel_fetches_beyond_capacity_;
-};
-
-TEST_F(PrecacheFetcherTest, FullPrecache) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
- unfinished_work->add_top_host()->set_hostname("manifest-fetch-failure.com");
- unfinished_work->add_top_host()->set_hostname("bad-manifest.com");
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
- unfinished_work->add_top_host()->set_hostname("not-in-top-3.com");
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(3);
- config.add_forced_site("forced-starting-url.com");
- // Duplicate starting URL, the manifest for this should only be fetched once.
- config.add_forced_site("good-manifest.com");
-
- PrecacheManifest good_manifest;
- good_manifest.add_resource()->set_url(kResourceFetchFailureURL);
- good_manifest.add_resource(); // Resource with no URL, should not be fetched.
- good_manifest.add_resource()->set_url(kGoodResourceURL);
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kManifestFetchFailureURL), "",
- net::HTTP_INTERNAL_SERVER_ERROR,
- net::URLRequestStatus::FAILED);
- factory_.SetFakeResponse(GURL(kBadManifestURL), "bad protobuf", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kResourceFetchFailureURL),
- "", net::HTTP_INTERNAL_SERVER_ERROR,
- net::URLRequestStatus::FAILED);
- factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kForcedStartingURLManifestURL),
- PrecacheManifest().SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- // Destroy the PrecacheFetcher after it has finished, to record metrics.
- }
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.emplace_back(kManifestFetchFailureURL);
- expected_requested_urls.emplace_back(kBadManifestURL);
- expected_requested_urls.emplace_back(kGoodManifestURL);
- expected_requested_urls.emplace_back(kForcedStartingURLManifestURL);
- expected_requested_urls.emplace_back(kResourceFetchFailureURL);
- expected_requested_urls.emplace_back(kGoodResourceURL);
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {
- "good-manifest.com", "forced-starting-url.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
- histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total",
- url_callback_.total_response_bytes(), 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-class PrecacheFetcherResourceSelectionTest
- : public PrecacheFetcherTest,
- public testing::WithParamInterface<PrecacheResourceSelection> {
- public:
- // These bitsets are asymmetric and multibyte, in order to test the orderings.
-
- // Set bits for kGoodResourceURL, kGoodResourceURLC and kGoodResourceURLD.
- static PrecacheResourceSelection DeprecatedBitset() {
- PrecacheResourceSelection ret;
- ret.set_deprecated_bitset(0b110000000001);
- return ret;
- }
-
- // Set bits for kGoodResourceURL, kGoodResourceURLC and kGoodResourceURLD.
- static PrecacheResourceSelection Bitset() {
- PrecacheResourceSelection ret;
- ret.set_bitset("\x01\x0c");
- return ret;
- }
-};
-
-TEST_P(PrecacheFetcherResourceSelectionTest, Basic) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- PrecacheConfigurationSettings config;
-
- PrecacheManifest good_manifest;
- PrecacheResourceSelection resource_selection;
- good_manifest.add_resource()->set_url(kGoodResourceURL);
- good_manifest.add_resource()->set_url(kGoodResourceURLA);
- for (int i = 0; i < 8; ++i)
- good_manifest.add_resource()->set_url(kGoodResourceURLB);
- good_manifest.add_resource()->set_url(kGoodResourceURLC);
- good_manifest.add_resource()->set_url(kGoodResourceURLD);
-
- (*good_manifest.mutable_experiments()
- ->mutable_resources_by_experiment_group())[kExperimentID] = GetParam();
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURLC), "good URL B", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURLD), "good URL D", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- // Destroy the PrecacheFetcher after it has finished, to record metrics.
- }
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.emplace_back(kGoodManifestURL);
- expected_requested_urls.emplace_back(kGoodResourceURL);
- expected_requested_urls.emplace_back(kGoodResourceURLC);
- expected_requested_urls.emplace_back(kGoodResourceURLD);
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
- histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total",
- url_callback_.total_response_bytes(), 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-TEST_P(PrecacheFetcherResourceSelectionTest, MissingBitset) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- PrecacheConfigurationSettings config;
-
- PrecacheManifest good_manifest;
- PrecacheResourceSelection resource_selection;
- good_manifest.add_resource()->set_url(kGoodResourceURL);
- good_manifest.add_resource()->set_url(kGoodResourceURLA);
- good_manifest.add_resource()->set_url(kGoodResourceURLB);
- good_manifest.add_resource()->set_url(kGoodResourceURLC);
- good_manifest.add_resource()->set_url(kGoodResourceURLD);
-
- // Set bits for a different experiment group.
- (*good_manifest.mutable_experiments()
- ->mutable_resources_by_experiment_group())[kExperimentID + 1] =
- GetParam();
-
- // Resource selection bitset for the experiment group will be missing and all
- // resources will be fetched.
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURLA), "good URL A", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURLB), "good URL B", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURLC), "good URL C", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURLD), "good URL D", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- // Destroy the PrecacheFetcher after it has finished, to record metrics.
- }
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.emplace_back(kGoodManifestURL);
- expected_requested_urls.emplace_back(kGoodResourceURL);
- expected_requested_urls.emplace_back(kGoodResourceURLA);
- expected_requested_urls.emplace_back(kGoodResourceURLB);
- expected_requested_urls.emplace_back(kGoodResourceURLC);
- expected_requested_urls.emplace_back(kGoodResourceURLD);
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
- histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total",
- url_callback_.total_response_bytes(), 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-INSTANTIATE_TEST_CASE_P(
- PrecacheFetcherResourceSelectionTest,
- PrecacheFetcherResourceSelectionTest,
- testing::Values(PrecacheFetcherResourceSelectionTest::DeprecatedBitset(),
- PrecacheFetcherResourceSelectionTest::Bitset()));
-
-TEST_F(PrecacheFetcherTest, PrecachePauseResume) {
- SetDefaultFlags();
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(3);
-
- std::unique_ptr<PrecacheUnfinishedWork> initial_work(
- new PrecacheUnfinishedWork());
- initial_work->add_top_host()->set_hostname("manifest1.com");
- initial_work->add_top_host()->set_hostname("manifest2.com");
- initial_work->set_start_time(
- (base::Time::Now() - base::TimeDelta::FromHours(1)).ToInternalValue());
-
- PrecacheFetcher first_fetcher(request_context_.get(), GURL(), std::string(),
- std::move(initial_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(),
- &precache_delegate_);
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- first_fetcher.Start();
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work =
- first_fetcher.CancelPrecaching();
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kBadManifestURL), "bad protobuf", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL("http://manifest-url-prefix.com/manifest1.com"),
- "bad protobuf", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL("http://manifest-url-prefix.com/manifest2.com"),
- "bad protobuf", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- url_callback_.clear_requested_urls();
- PrecacheFetcher second_fetcher(request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(),
- &precache_delegate_);
- second_fetcher.Start();
- base::RunLoop().RunUntilIdle();
- expected_requested_urls.emplace_back(
- "http://manifest-url-prefix.com/manifest1.com");
- expected_requested_urls.emplace_back(
- "http://manifest-url-prefix.com/manifest2.com");
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-TEST_F(PrecacheFetcherTest, ResumeWithConfigOnly) {
- SetDefaultFlags();
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->mutable_config_settings()->add_forced_site(
- "good-manifest.com");
- unfinished_work->set_start_time(base::Time::Now().ToInternalValue());
- PrecacheManifest good_manifest;
- good_manifest.add_resource()->set_url(kGoodResourceURL);
-
- factory_.SetFakeResponse(GURL(kGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
- }
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kGoodManifestURL);
- expected_requested_urls.emplace_back(kGoodResourceURL);
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-
-TEST_F(PrecacheFetcherTest, CustomURLs) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
-
- PrecacheConfigurationSettings config;
-
- PrecacheManifest good_manifest;
- good_manifest.add_resource()->set_url(kGoodResourceURL);
-
- factory_.SetFakeResponse(GURL(kCustomConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kCustomGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(kCustomConfigURL), kCustomManifestURLPrefix,
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kCustomConfigURL);
- expected_requested_urls.emplace_back(kCustomGoodManifestURL);
- expected_requested_urls.emplace_back(kGoodResourceURL);
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-TEST_F(PrecacheFetcherTest, ConfigFetchFailure) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
-
- factory_.SetFakeResponse(GURL(kConfigURL), "",
- net::HTTP_INTERNAL_SERVER_ERROR,
- net::URLRequestStatus::FAILED);
- factory_.SetFakeResponse(GURL(kGoodManifestURL), "", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(), std::move(unfinished_work),
- kExperimentID, precache_database_.GetWeakPtr(), task_runner(),
- &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.emplace_back(kGoodManifestURL);
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-TEST_F(PrecacheFetcherTest, BadConfig) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
-
- factory_.SetFakeResponse(GURL(kConfigURL), "bad protobuf", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodManifestURL), "", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(), std::move(unfinished_work),
- kExperimentID, precache_database_.GetWeakPtr(), task_runner(),
- &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.emplace_back(kGoodManifestURL);
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-TEST_F(PrecacheFetcherTest, Cancel) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("starting-url.com");
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(1);
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- // Destroy the PrecacheFetcher, to cancel precaching. No metrics
- // should be recorded because this should not cause OnDone to be
- // called on the precache delegate.
- }
-
- base::RunLoop().RunUntilIdle();
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
- EXPECT_FALSE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 0);
-}
-
-#if defined(PRECACHE_CONFIG_SETTINGS_URL)
-
-// If the default precache configuration settings URL is defined, then test that
-// it works with the PrecacheFetcher.
-TEST_F(PrecacheFetcherTest, PrecacheUsingDefaultConfigSettingsURL) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("starting-url.com");
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(0);
-
- factory_.SetFakeResponse(GURL(PRECACHE_CONFIG_SETTINGS_URL),
- config.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(), std::move(unfinished_work),
- kExperimentID, precache_database_.GetWeakPtr(), task_runner(),
- &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(PRECACHE_CONFIG_SETTINGS_URL);
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-#endif // PRECACHE_CONFIG_SETTINGS_URL
-
-#if defined(PRECACHE_MANIFEST_URL_PREFIX)
-
-// If the default precache manifest URL prefix is defined, then test that it
-// works with the PrecacheFetcher.
-TEST_F(PrecacheFetcherTest, PrecacheUsingDefaultManifestURLPrefix) {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kPrecacheConfigSettingsURL, kConfigURL);
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("starting-url.com");
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(1);
-
- GURL manifest_url(PRECACHE_MANIFEST_URL_PREFIX "starting-url.com");
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(manifest_url, PrecacheManifest().SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(), std::move(unfinished_work),
- kExperimentID, precache_database_.GetWeakPtr(), task_runner(),
- &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.push_back(manifest_url);
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"starting-url.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-#endif // PRECACHE_MANIFEST_URL_PREFIX
-
-TEST_F(PrecacheFetcherTest, TopResourcesCount) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
-
- PrecacheConfigurationSettings config;
- config.set_top_resources_count(3);
-
- PrecacheManifest good_manifest;
- good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved");
- good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved");
- good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved");
- good_manifest.add_resource()->set_url("http://good-manifest.com/skipped");
- good_manifest.add_resource()->set_url("http://good-manifest.com/skipped");
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL("http://good-manifest.com/retrieved"), "good",
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- // Destroy the PrecacheFetcher after it has finished, to record metrics.
- }
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.emplace_back(kGoodManifestURL);
- expected_requested_urls.emplace_back("http://good-manifest.com/retrieved");
- expected_requested_urls.emplace_back("http://good-manifest.com/retrieved");
- expected_requested_urls.emplace_back("http://good-manifest.com/retrieved");
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
- histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total",
- url_callback_.total_response_bytes(), 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-TEST_F(PrecacheFetcherTest, TopResourcesCount_ResourceBitset) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
- unfinished_work->add_top_host()->set_hostname("good-manifest.com");
-
- PrecacheConfigurationSettings config;
- config.set_top_resources_count(2);
-
- PrecacheManifest good_manifest;
- good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved");
- good_manifest.add_resource()->set_url("http://good-manifest.com/skipped");
- good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved");
- good_manifest.add_resource()->set_url("http://good-manifest.com/skipped");
- good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved");
- (*good_manifest.mutable_experiments()
- ->mutable_resources_by_experiment_group())[kExperimentID]
- .set_deprecated_bitset(0b10101);
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(kGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL("http://good-manifest.com/retrieved"), "good",
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- // Destroy the PrecacheFetcher after it has finished, to record metrics.
- }
-
- std::vector<GURL> expected_requested_urls;
- expected_requested_urls.emplace_back(kConfigURL);
- expected_requested_urls.emplace_back(kGoodManifestURL);
- expected_requested_urls.emplace_back("http://good-manifest.com/retrieved");
- expected_requested_urls.emplace_back("http://good-manifest.com/retrieved");
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
- histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total",
- url_callback_.total_response_bytes(), 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-// MaxBytesPerResource is impossible to test with net::FakeURLFetcherFactory:
-//
-// - The PrecacheFetcher::Fetcher's max_bytes logic only applies to network
-// requests, and not cached requests.
-// - Forcing PrecacheFetcher::Fetcher to do a network request (i.e. a second
-// request for the same URL) requires either setting a custom error of
-// ERR_CACHE_MISS or setting a custom ETag response header, neither of which
-// is possible under FakeURLFetcherFactory.
-//
-// PrecacheFetcherFetcherTest.ResourceTooBig tests the bulk of the code. We'll
-// assume that PrecacheFetcher passes the right max_bytes to the
-// PrecacheFetcher::Fetcher constructor.
-//
-// TODO(twifkak): Port these tests from FakeURLFetcherFactory to
-// MockURLFetcherFactory or EmbeddedTestServer, and add a test that fetches are
-// cancelled midstream.
-
-TEST_F(PrecacheFetcherTest, MaxBytesTotal) {
- SetDefaultFlags();
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- auto* top_host = unfinished_work->add_top_host();
- top_host->set_hostname("good-manifest.com");
- top_host->set_visits(1);
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- // Should be greater than kMaxParallelFetches, so that we can observe
- // PrecacheFetcher not fetching the remaining resources after max bytes is
- // exceeded.
- const size_t kNumResources = kMaxParallelFetches + 5;
- // Should be smaller than kNumResources - kMaxParallelFetches, such that the
- // max bytes is guaranteed to be exceeded before all fetches have been
- // requested. In this case, after 3 fetches have been completed, 3 more are
- // added to the fetcher pool, but 2 out of 5 still remain.
- const size_t kResourcesWithinMax = 3;
- // Should be big enough that the size of the config, manifest, and HTTP
- // headers are negligible for max bytes computation.
- const size_t kBytesPerResource = 500;
- const size_t kMaxBytesTotal = kResourcesWithinMax * kBytesPerResource;
-
- PrecacheConfigurationSettings config;
- config.set_max_bytes_total(kMaxBytesTotal);
- config.set_global_ranking(true);
-
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- PrecacheManifest good_manifest;
- for (size_t i = 0; i < kNumResources; ++i) {
- const std::string url = "http://good-manifest.com/" + std::to_string(i);
- auto* resource = good_manifest.add_resource();
- resource->set_url(url);
- resource->set_weight_ratio(static_cast<double>(i) / kNumResources);
- factory_.SetFakeResponse(GURL(url), std::string(kBytesPerResource, '.'),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- }
-
- factory_.SetFakeResponse(GURL(kGoodManifestURL),
- good_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
- }
-
- // Fetcher should request config, manifest, and all but 3 resources. For some
- // reason, we are seeing it fetch all but 4 resources. Meh, close enough.
- EXPECT_EQ(1 + 1 + kNumResources - 4, url_callback_.requested_urls().size());
-
- std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-
- const double expected_min_weight =
- good_manifest.resource(kNumResources - 3).weight_ratio() *
- 1 /* # of visits to good-manifest.com */;
- histogram.ExpectBucketCount("Precache.Fetch.MinWeight",
- 1000.0 * expected_min_weight, 1);
-}
-
-// Tests the parallel fetch behaviour when more precache resource and manifest
-// requests are available than the maximum capacity of fetcher pool.
-TEST_F(PrecacheFetcherTest, FetcherPoolMaxLimitReached) {
- SetDefaultFlags();
-
- const size_t kNumTopHosts = 5;
- const size_t kNumResources = kMaxParallelFetches + 5;
-
- PrecacheConfigurationSettings config;
- std::vector<GURL> expected_requested_urls;
- std::vector<std::string> expected_manifest_hosts;
-
- config.set_top_sites_count(kNumTopHosts);
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- expected_requested_urls.emplace_back(kConfigURL);
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url);
- expected_manifest_hosts.push_back(top_host_url);
- }
-
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- unfinished_work->add_top_host()->set_hostname(top_host_url);
-
- PrecacheManifest manifest;
- for (size_t j = 0; j < kNumResources; ++j) {
- const std::string resource_url =
- base::StringPrintf("http://top-host-%zu.com/resource-%zu", i, j);
- manifest.add_resource()->set_url(resource_url);
- factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- expected_requested_urls.emplace_back(resource_url);
- }
- factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url),
- manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- }
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- EXPECT_GT(kNumResources, precache_fetcher.pool_.max_size());
- CheckUntilParallelFetchesBeyondCapacity(&precache_fetcher);
-
- base::RunLoop().RunUntilIdle();
-
- // Destroy the PrecacheFetcher after it has finished, to record metrics.
- }
-
- EXPECT_TRUE(parallel_fetches_beyond_capacity_);
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
-
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1);
- histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total",
- url_callback_.total_response_bytes(), 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-TEST_F(PrecacheFetcherTest, FilterInvalidManifestUrls) {
- SetDefaultFlags();
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kPrecacheManifestURLPrefix, "invalid-manifest-prefix");
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("manifest.com");
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- factory_.SetFakeResponse(GURL(kConfigURL), "", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
- }
-
- // The config is fetched, but not the invalid manifest URL.
- EXPECT_EQ(1UL, url_callback_.requested_urls().size());
-
- EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- // manifest.com will have been failed to complete, in this case.
- EXPECT_THAT(histogram.GetAllSamples("Precache.Fetch.PercentCompleted"),
- ElementsAre(base::Bucket(101, 1)));
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-TEST_F(PrecacheFetcherTest, FilterInvalidResourceUrls) {
- SetDefaultFlags();
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("bad-manifest.com");
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- factory_.SetFakeResponse(GURL(kConfigURL), "", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- PrecacheManifest bad_manifest;
- bad_manifest.add_resource()->set_url("http://");
-
- factory_.SetFakeResponse(GURL(kBadManifestURL),
- bad_manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
- }
-
- // The config and manifest are fetched, but not the invalid resource URL.
- EXPECT_EQ(2UL, url_callback_.requested_urls().size());
-
- std::vector<std::string> expected_manifest_hosts = {"bad-manifest.com"};
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- // bad-manifest.com will have been completed.
- EXPECT_THAT(histogram.GetAllSamples("Precache.Fetch.PercentCompleted"),
- ElementsAre(base::Bucket(101, 1)));
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-}
-
-TEST(PrecacheFetcherStandaloneTest, GetResourceURLBase64Hash) {
- // Expected base64 hash for some selected URLs.
- EXPECT_EQ("dVSI/sC1cGk=", PrecacheFetcher::GetResourceURLBase64HashForTesting(
- {GURL("http://used-resource-1/a.js")}));
- EXPECT_EQ("B/Jc6JvusZQ=", PrecacheFetcher::GetResourceURLBase64HashForTesting(
- {GURL("http://used-resource-1/b.js")}));
- EXPECT_EQ("CmvACGJ4k08=", PrecacheFetcher::GetResourceURLBase64HashForTesting(
- {GURL("http://used-resource-1/c.js")}));
-
- EXPECT_EQ("dVSI/sC1cGkH8lzom+6xlA==",
- PrecacheFetcher::GetResourceURLBase64HashForTesting(
- {GURL("http://used-resource-1/a.js"),
- GURL("http://used-resource-1/b.js")}));
-}
-
-TEST_F(PrecacheFetcherTest, SendUsedDownloadedResourceHash) {
- SetDefaultFlags();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
- unfinished_work->add_top_host()->set_hostname("top-host-1.com");
- unfinished_work->add_top_host()->set_hostname("top-host-2.com");
- unfinished_work->add_top_host()->set_hostname("top-host-3.com");
-
- UpdatePrecacheReferrerHost("top-host-1.com", 1001);
- UpdatePrecacheReferrerHost("top-host-2.com", 1002);
- UpdatePrecacheReferrerHost("top-host-3.com", 1003);
-
- // Mark some resources as precached.
- RecordURLPrefetch(GURL("http://used-resource-1/a.js"), "top-host-1.com");
- RecordURLPrefetch(GURL("http://used-resource-1/b.js"), "top-host-1.com");
- RecordURLPrefetch(GURL("http://unused-resource-1/c.js"), "top-host-1.com");
- RecordURLPrefetch(GURL("http://unused-resource-2/a.js"), "top-host-2.com");
- RecordURLPrefetch(GURL("http://unused-resource-2/b.js"), "top-host-2.com");
- base::RunLoop().RunUntilIdle();
-
- // Mark some resources as used during user browsing.
- RecordURLNonPrefetch(GURL("http://used-resource-1/a.js"));
- RecordURLNonPrefetch(GURL("http://used-resource-1/b.js"));
- base::RunLoop().RunUntilIdle();
-
- factory_.SetFakeResponse(GURL(kConfigURL), std::string(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(
- GURL(std::string(kManifestURLPrefix) +
- "top-host-1.com?manifest=1001&used_resources=" +
- net::EscapeQueryParamValue(
- PrecacheFetcher::GetResourceURLBase64HashForTesting(
- {GURL("http://used-resource-1/a.js"),
- GURL("http://used-resource-1/b.js")}),
- true) +
- "&d=" + net::EscapeQueryParamValue(
- PrecacheFetcher::GetResourceURLBase64HashForTesting(
- {GURL("http://used-resource-1/a.js"),
- GURL("http://used-resource-1/b.js"),
- GURL("http://unused-resource-1/c.js")}),
- true)),
- std::string(), net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(
- GURL(std::string(kManifestURLPrefix) +
- "top-host-2.com?manifest=1002&used_resources=&d=" +
- net::EscapeQueryParamValue(
- PrecacheFetcher::GetResourceURLBase64HashForTesting(
- {GURL("http://unused-resource-2/a.js"),
- GURL("http://unused-resource-2/b.js")}),
- true)),
- std::string(), net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(
- GURL(std::string(kManifestURLPrefix) +
- "top-host-3.com?manifest=1003&used_resources=&d="),
- std::string(), net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
- }
-
- // If we run the precache again, no download should be reported.
- factory_.ClearFakeResponses();
- factory_.SetFakeResponse(GURL(kConfigURL), std::string(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- // Since we returned an empty proto, the manifest id was set to 0.
- // The d='s are empty because precache fetches are tried first solely from the
- // cache and, since any matching request to the fake factory succeeds, it is
- // hardcoded to be cached even though we didn't specify it as such in the fake
- // response.
- factory_.SetFakeResponse(GURL(std::string(kManifestURLPrefix) +
- "top-host-1.com?manifest=0&used_resources=&d="),
- std::string(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(std::string(kManifestURLPrefix) +
- "top-host-2.com?manifest=0&used_resources=&d="),
- std::string(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_.SetFakeResponse(GURL(std::string(kManifestURLPrefix) +
- "top-host-3.com?manifest=0&used_resources=&d="),
- std::string(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- // Flush so that previous UpdatePrecacheReferrerHost calls make it through.
- // Otherwise, manifest_id may be non 0 for some of the hosts.
- Flush();
- {
- std::unique_ptr<PrecacheUnfinishedWork> more_work(
- new PrecacheUnfinishedWork());
- more_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
- more_work->add_top_host()->set_hostname("top-host-1.com");
- more_work->add_top_host()->set_hostname("top-host-2.com");
- more_work->add_top_host()->set_hostname("top-host-3.com");
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(), std::move(more_work),
- kExperimentID, precache_database_.GetWeakPtr(), task_runner(),
- &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
- }
-}
-
-TEST(PrecacheFetcherResourceWeightTest, Naive) {
- ASSERT_EQ(
- 0, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_NAIVE, 0, 100));
- ASSERT_EQ(
- 4, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_NAIVE, 1, 4));
- ASSERT_EQ(8, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_NAIVE,
- 0.5, 16));
-}
-
-TEST(PrecacheFetcherResourceWeightTest, Geometric) {
- ASSERT_EQ(0, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_GEOMETRIC,
- 0, 100));
- ASSERT_EQ(1, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_GEOMETRIC,
- 1, 4));
- ASSERT_NEAR(0.9999847,
- ResourceWeight(PrecacheConfigurationSettings::FUNCTION_GEOMETRIC,
- 0.5, 16),
- 0.0000001);
-}
-
-class PrecacheFetcherGlobalRankingTest
- : public PrecacheFetcherTest,
- public testing::WithParamInterface<
- PrecacheConfigurationSettings::ResourceWeightFunction> {};
-
-TEST_P(PrecacheFetcherGlobalRankingTest, GloballyRankResources) {
- SetDefaultFlags();
-
- const size_t kNumTopHosts = 5;
- const size_t kNumResources = 5;
-
- std::vector<GURL> expected_requested_urls;
- std::vector<std::string> expected_manifest_hosts;
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(kNumTopHosts);
- config.set_global_ranking(true);
- config.set_resource_weight_function(GetParam());
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- expected_requested_urls.emplace_back(kConfigURL);
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url);
- expected_manifest_hosts.push_back(top_host_url);
- }
-
- // Visit counts and weights are chosen in such a way that resource requests
- // between different hosts will be interleaved.
- std::vector<std::pair<std::string, float>> resources;
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- TopHost* top_host = unfinished_work->add_top_host();
- top_host->set_hostname(top_host_url);
- top_host->set_visits(kNumTopHosts - i);
-
- PrecacheManifest manifest;
- for (size_t j = 0; j < kNumResources; ++j) {
- const float weight = 1 - static_cast<float>(j) / kNumResources;
- const std::string resource_url =
- base::StringPrintf("http://top-host-%zu.com/resource-%zu-weight-%.1f",
- i, j, top_host->visits() * weight);
- PrecacheResource* resource = manifest.add_resource();
- resource->set_url(resource_url);
- resource->set_weight_ratio(weight);
- factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- resources.emplace_back(
- resource_url, ResourceWeight(GetParam(), weight, top_host->visits()));
- }
- factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url),
- manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- }
- // Sort by descending weight.
- std::stable_sort(resources.begin(), resources.end(),
- [](const std::pair<std::string, float>& a,
- const std::pair<std::string, float>& b) {
- return a.second > b.second;
- });
- for (const auto& resource : resources)
- expected_requested_urls.emplace_back(resource.first);
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
- base::RunLoop().RunUntilIdle();
- }
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-INSTANTIATE_TEST_CASE_P(
- PrecacheFetcherGlobalRankingTest,
- PrecacheFetcherGlobalRankingTest,
- testing::Values(PrecacheConfigurationSettings::FUNCTION_NAIVE,
- PrecacheConfigurationSettings::FUNCTION_GEOMETRIC));
-
-TEST_F(PrecacheFetcherTest, GloballyRankResourcesAfterPauseResume) {
- SetDefaultFlags();
-
- const size_t kNumTopHosts = 5;
- const size_t kNumResources = 5;
-
- std::vector<GURL> expected_requested_urls;
- std::vector<std::string> expected_manifest_hosts;
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(kNumTopHosts);
- config.set_global_ranking(true);
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- // Visit counts and weights are chosen in such a way that resource requests
- // between different hosts will be interleaved.
- std::vector<std::pair<std::string, float>> resources;
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- expected_manifest_hosts.push_back(top_host_url);
- TopHost* top_host = unfinished_work->add_top_host();
- top_host->set_hostname(top_host_url);
- top_host->set_visits(kNumTopHosts - i);
-
- PrecacheManifest manifest;
- for (size_t j = 0; j < kNumResources; ++j) {
- const float weight = 1 - static_cast<float>(j) / kNumResources;
- const std::string resource_url =
- base::StringPrintf("http://top-host-%zu.com/resource-%zu-weight-%.1f",
- i, j, top_host->visits() * weight);
- PrecacheResource* resource = manifest.add_resource();
- resource->set_url(resource_url);
- resource->set_weight_ratio(weight);
- factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- resources.emplace_back(resource_url,
- top_host->visits() * resource->weight_ratio());
- }
- factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url),
- manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- }
- // Sort by descending weight.
- std::stable_sort(resources.begin(), resources.end(),
- [](const std::pair<std::string, float>& a,
- const std::pair<std::string, float>& b) {
- return a.second > b.second;
- });
- for (const auto& resource : resources)
- expected_requested_urls.emplace_back(resource.first);
-
- std::unique_ptr<PrecacheUnfinishedWork> cancelled_work;
- {
- uint32_t remaining_tries = 100;
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- // Run the loop until all tophost manifest fetches are complete, but some
- // resource fetches are pending.
- while (--remaining_tries != 0 &&
- (!precache_fetcher.top_hosts_to_fetch_.empty() ||
- !precache_fetcher.top_hosts_fetching_.empty() ||
- !precache_fetcher.unfinished_work_->has_config_settings() ||
- precache_fetcher.resources_to_fetch_.empty())) {
- LOG(INFO) << "remaining_tries: " << remaining_tries;
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop.QuitClosure());
- run_loop.Run();
- }
-
- // Cancel precaching.
- cancelled_work = precache_fetcher.CancelPrecaching();
- EXPECT_TRUE(precache_fetcher.top_hosts_to_fetch_.empty());
- EXPECT_TRUE(precache_fetcher.resources_to_fetch_.empty());
- }
- EXPECT_NE(cancelled_work, nullptr);
- EXPECT_TRUE(cancelled_work->top_host().empty());
- EXPECT_EQ(kNumTopHosts * kNumResources,
- static_cast<size_t>(cancelled_work->resource().size()));
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_FALSE(precache_delegate_.was_on_done_called());
-
- url_callback_.clear_requested_urls();
- precache_delegate_.clear_manifest_hosts();
-
- // Continuing with the precache should fetch all resources, as the previous
- // run was cancelled before any finished. They should be fetched in global
- // ranking order.
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(cancelled_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- LOG(INFO) << "Resuming prefetch.";
- precache_fetcher.Start();
- base::RunLoop().RunUntilIdle();
- }
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectBucketCount("Precache.Fetch.MinWeight",
- 1000.0 * resources.back().second, 1);
-}
-
-TEST_F(PrecacheFetcherTest, MaxTotalResources) {
- SetDefaultFlags();
-
- const size_t kNumResources = 5;
-
- std::vector<GURL> expected_requested_urls;
- std::vector<std::string> expected_manifest_hosts;
-
- PrecacheConfigurationSettings config;
- config.set_total_resources_count(2);
- config.set_global_ranking(true);
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- expected_requested_urls.emplace_back(kConfigURL);
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- TopHost* top_host = unfinished_work->add_top_host();
- top_host->set_hostname("top-host.com");
- top_host->set_visits(1);
-
- expected_requested_urls.emplace_back(kManifestURLPrefix +
- top_host->hostname());
- expected_manifest_hosts.push_back(top_host->hostname());
-
- PrecacheManifest manifest;
- for (size_t i = 0; i < kNumResources; ++i) {
- const float weight = 1 - static_cast<float>(i) / kNumResources;
- const std::string resource_url =
- base::StringPrintf("http://top-host.com/resource-%zu-weight-%.1f", i,
- top_host->visits() * weight);
- PrecacheResource* resource = manifest.add_resource();
- resource->set_url(resource_url);
- resource->set_weight_ratio(weight);
- factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- if (i < config.total_resources_count())
- expected_requested_urls.emplace_back(resource_url);
- }
- factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host->hostname()),
- manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
- base::RunLoop().RunUntilIdle();
- }
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- const float expected_min_weight =
- manifest.resource(config.total_resources_count() - 1).weight_ratio();
- histogram.ExpectUniqueSample("Precache.Fetch.MinWeight",
- 1000.0 * expected_min_weight, 1);
-}
-
-TEST_F(PrecacheFetcherTest, MinWeight) {
- SetDefaultFlags();
-
- const size_t kNumResources = 5;
-
- std::vector<GURL> expected_requested_urls;
- std::vector<std::string> expected_manifest_hosts;
-
- PrecacheConfigurationSettings config;
- config.set_min_weight(3);
- config.set_global_ranking(true);
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- expected_requested_urls.emplace_back(kConfigURL);
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- TopHost* top_host = unfinished_work->add_top_host();
- top_host->set_hostname("top-host.com");
- top_host->set_visits(5);
-
- expected_requested_urls.emplace_back(kManifestURLPrefix +
- top_host->hostname());
- expected_manifest_hosts.push_back(top_host->hostname());
-
- PrecacheManifest manifest;
- for (size_t i = 0; i < kNumResources; ++i) {
- const float weight = 1 - static_cast<float>(i) / kNumResources;
- const std::string resource_url =
- base::StringPrintf("http://top-host.com/resource-%zu-weight-%.1f", i,
- top_host->visits() * weight);
- PrecacheResource* resource = manifest.add_resource();
- resource->set_url(resource_url);
- resource->set_weight_ratio(weight);
- factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- // If top_host->visits() * weight > config.min_weight():
- if (i < 3)
- expected_requested_urls.emplace_back(resource_url);
- }
- factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host->hostname()),
- manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
- base::RunLoop().RunUntilIdle();
- }
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-// Tests cancel precaching when all tophost manifests are fetched, but some
-// resource fetches are pending.
-TEST_F(PrecacheFetcherTest, CancelPrecachingAfterAllManifestFetch) {
- SetDefaultFlags();
-
- const size_t kNumTopHosts = 5;
- const size_t kNumResources = 5;
-
- PrecacheConfigurationSettings config;
- std::vector<GURL> expected_requested_urls;
- std::vector<std::string> expected_manifest_hosts;
- std::unique_ptr<PrecacheUnfinishedWork> cancelled_work;
-
- config.set_top_sites_count(kNumTopHosts);
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- expected_requested_urls.emplace_back(kConfigURL);
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url);
- expected_manifest_hosts.push_back(top_host_url);
- }
-
- int num_resources = 0;
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- TopHost* top_host = unfinished_work->add_top_host();
- top_host->set_hostname(top_host_url);
- top_host->set_visits(kNumTopHosts - i);
-
- PrecacheManifest manifest;
- for (size_t j = 0; j < kNumResources; ++j) {
- const std::string resource_url =
- base::StringPrintf("http://top-host-%zu.com/resource-%zu", i, j);
- PrecacheResource* resource = manifest.add_resource();
- resource->set_url(resource_url);
- resource->set_weight_ratio(1);
- factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- if (++num_resources <= kMaxParallelFetches)
- expected_requested_urls.emplace_back(resource_url);
- }
- factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url),
- manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- }
-
- {
- uint32_t remaining_tries = 100;
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- // Run the loop until all tophost manifest fetches are complete, but some
- // resource fetches are pending.
- while (--remaining_tries != 0 &&
- (!precache_fetcher.top_hosts_to_fetch_.empty() ||
- !precache_fetcher.top_hosts_fetching_.empty() ||
- !precache_fetcher.unfinished_work_->has_config_settings() ||
- precache_fetcher.resources_to_fetch_.empty())) {
- LOG(INFO) << "remaining_tries: " << remaining_tries;
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop.QuitClosure());
- run_loop.Run();
- }
-
- // Cancel precaching.
- cancelled_work = precache_fetcher.CancelPrecaching();
- EXPECT_TRUE(precache_fetcher.top_hosts_to_fetch_.empty());
- EXPECT_TRUE(precache_fetcher.resources_to_fetch_.empty());
- }
- ASSERT_NE(nullptr, cancelled_work);
- EXPECT_TRUE(cancelled_work->top_host().empty());
- EXPECT_EQ(kNumTopHosts * kNumResources,
- static_cast<size_t>(cancelled_work->resource().size()));
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_FALSE(precache_delegate_.was_on_done_called());
-
- // Continuing with the precache should fetch all resources, as the previous
- // run was cancelled before any finished.
- expected_requested_urls.clear();
- url_callback_.clear_requested_urls();
- precache_delegate_.clear_manifest_hosts();
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- for (size_t j = 0; j < kNumResources; ++j) {
- expected_requested_urls.emplace_back(
- base::StringPrintf("http://top-host-%zu.com/resource-%zu", i, j));
- }
- }
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(cancelled_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- LOG(INFO) << "Resuming prefetch.";
- precache_fetcher.Start();
- base::RunLoop().RunUntilIdle();
- }
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-}
-
-TEST_F(PrecacheFetcherTest, DailyQuota) {
- SetDefaultFlags();
-
- const size_t kNumTopHosts = 3;
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue());
-
- PrecacheConfigurationSettings config;
- config.set_top_sites_count(kNumTopHosts);
- config.set_daily_quota_total(10000);
- factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
- std::vector<GURL> expected_requested_urls;
- std::vector<std::string> expected_manifest_hosts;
- expected_requested_urls.emplace_back(kConfigURL);
-
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- expected_requested_urls.emplace_back(std::string(kManifestURLPrefix) +
- top_host_url);
- expected_manifest_hosts.push_back(top_host_url);
- }
-
- for (size_t i = 0; i < kNumTopHosts; ++i) {
- const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i);
- const std::string resource_url =
- base::StringPrintf("http://top-host-%zu.com/resource.html", i);
- PrecacheManifest manifest;
- manifest.add_resource()->set_url(resource_url);
-
- unfinished_work->add_top_host()->set_hostname(top_host_url);
- factory_.SetFakeResponse(
- GURL(std::string(kManifestURLPrefix) + top_host_url),
- manifest.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- // Set a 5000 byte resource.
- factory_.SetFakeResponse(GURL(resource_url), std::string(5000, 'a'),
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-
- expected_requested_urls.emplace_back(resource_url);
- }
-
- base::HistogramTester histogram;
-
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(0U, precache_fetcher.quota_.remaining());
- unfinished_work = precache_fetcher.CancelPrecaching();
- }
-
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- EXPECT_EQ(0, unfinished_work->top_host_size());
- EXPECT_EQ(1, unfinished_work->resource_size());
-
- histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 1);
- histogram.ExpectTotalCount("Precache.Fetch.ResponseBytes.Total", 1);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1);
-
- // Continuing with the precache when quota limit is reached, will not fetch
- // any resources.
- expected_requested_urls.clear();
- url_callback_.clear_requested_urls();
- precache_delegate_.clear_manifest_hosts();
- {
- PrecacheFetcher precache_fetcher(
- request_context_.get(), GURL(), std::string(),
- std::move(unfinished_work), kExperimentID,
- precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_);
- precache_fetcher.Start();
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(0U, precache_fetcher.quota_.remaining());
- }
- EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
- EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty());
- EXPECT_TRUE(precache_delegate_.was_on_done_called());
-
- histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 2);
- histogram.ExpectTotalCount("Precache.Fetch.ResponseBytes.Total", 2);
- histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 2);
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_manifest_util.cc b/chromium/components/precache/core/precache_manifest_util.cc
deleted file mode 100644
index ab801f65163..00000000000
--- a/chromium/components/precache/core/precache_manifest_util.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/precache/core/precache_manifest_util.h"
-
-#include <string>
-
-#include "components/precache/core/proto/precache.pb.h"
-
-namespace precache {
-
-void RemoveUnknownFields(PrecacheManifest* manifest) {
- manifest->mutable_unknown_fields()->clear();
- for (auto& resource : *manifest->mutable_resource())
- resource.mutable_unknown_fields()->clear();
- if (manifest->has_experiments()) {
- manifest->mutable_experiments()->mutable_unknown_fields()->clear();
- for (auto& kv : *manifest->mutable_experiments()
- ->mutable_resources_by_experiment_group()) {
- kv.second.mutable_unknown_fields()->clear();
- }
- }
- if (manifest->has_id())
- manifest->mutable_id()->mutable_unknown_fields()->clear();
-}
-
-base::Optional<std::vector<bool>> GetResourceBitset(
- const PrecacheManifest& manifest,
- uint32_t experiment_id) {
- base::Optional<std::vector<bool>> ret;
- if (manifest.has_experiments()) {
- const auto& resource_bitset_map =
- manifest.experiments().resources_by_experiment_group();
- const auto& it = resource_bitset_map.find(experiment_id);
- if (it != resource_bitset_map.end()) {
- if (it->second.has_bitset()) {
- const std::string& bitset = it->second.bitset();
- const int bitset_size = bitset.size() * 8;
- DCHECK_GE(bitset_size, manifest.resource_size());
- if (bitset_size >= manifest.resource_size()) {
- ret.emplace(bitset_size);
- for (size_t i = 0; i < bitset.size(); ++i) {
- for (size_t j = 0; j < 8; ++j) {
- if ((1 << j) & bitset[i])
- ret.value()[i * 8 + j] = true;
- }
- }
- }
- } else if (it->second.has_deprecated_bitset()) {
- uint64_t bitset = it->second.deprecated_bitset();
- DCHECK_GE(64, manifest.resource_size());
- if (64 >= manifest.resource_size()) {
- ret.emplace(64);
- for (int i = 0; i < 64; ++i) {
- if ((0x1ULL << i) & bitset)
- ret.value()[i] = true;
- }
- }
- }
- }
- }
- // Only return one variable to ensure RVO triggers.
- return ret;
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_manifest_util.h b/chromium/components/precache/core/precache_manifest_util.h
deleted file mode 100644
index b3e2ec2f55b..00000000000
--- a/chromium/components/precache/core/precache_manifest_util.h
+++ /dev/null
@@ -1,30 +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_PRECACHE_CORE_PRECACHE_MANIFEST_UTIL_H_
-#define COMPONENTS_PRECACHE_CORE_PRECACHE_MANIFEST_UTIL_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/optional.h"
-
-namespace precache {
-
-class PrecacheManifest;
-
-// Removes unknown fields from the |manifest| including embedded messages.
-void RemoveUnknownFields(PrecacheManifest* manifest);
-
-// Returns the resource selection bitset from the |manifest| for the given
-// |experiment_id|. If the experiment group is not found, then this returns
-// nullopt, in which case all resources should be selected.
-base::Optional<std::vector<bool>> GetResourceBitset(
- const PrecacheManifest& manifest,
- uint32_t experiment_id);
-
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CORE_PRECACHE_MANIFEST_UTIL_H_
diff --git a/chromium/components/precache/core/precache_referrer_host_table.cc b/chromium/components/precache/core/precache_referrer_host_table.cc
deleted file mode 100644
index e6acbbad8aa..00000000000
--- a/chromium/components/precache/core/precache_referrer_host_table.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/precache/core/precache_referrer_host_table.h"
-
-#include "sql/connection.h"
-#include "sql/statement.h"
-
-using sql::Statement;
-
-namespace precache {
-
-const int64_t PrecacheReferrerHostEntry::kInvalidId = -1;
-
-bool PrecacheReferrerHostEntry::operator==(
- const PrecacheReferrerHostEntry& entry) const {
- return id == entry.id && referrer_host == entry.referrer_host &&
- manifest_id == entry.manifest_id && time == entry.time;
-}
-
-PrecacheReferrerHostTable::PrecacheReferrerHostTable() : db_(NULL) {}
-
-PrecacheReferrerHostTable::~PrecacheReferrerHostTable() {}
-
-bool PrecacheReferrerHostTable::Init(sql::Connection* db) {
- DCHECK(!db_); // Init must only be called once.
- DCHECK(db); // The database connection must be non-NULL.
- db_ = db;
- return CreateTableIfNonExistent();
-}
-
-PrecacheReferrerHostEntry PrecacheReferrerHostTable::GetReferrerHost(
- const std::string& referrer_host) {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT id, referrer_host, manifest_id, time "
- "FROM precache_referrer_hosts WHERE referrer_host=?"));
-
- statement.BindString(0, referrer_host);
- if (statement.Step()) {
- return PrecacheReferrerHostEntry(
- statement.ColumnInt64(0), statement.ColumnString(1),
- statement.ColumnInt64(2),
- base::Time::FromInternalValue(statement.ColumnInt64(3)));
- }
- return PrecacheReferrerHostEntry(PrecacheReferrerHostEntry::kInvalidId,
- std::string(), 0, base::Time());
-}
-
-int64_t PrecacheReferrerHostTable::UpdateReferrerHost(
- const std::string& referrer_host,
- int64_t manifest_id,
- const base::Time& time) {
- int64_t referrer_host_id = GetReferrerHost(referrer_host).id;
- if (referrer_host_id == PrecacheReferrerHostEntry::kInvalidId) {
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "INSERT INTO precache_referrer_hosts "
- "(id, referrer_host, manifest_id, time) "
- "VALUES(NULL, ?, ?, ?)"));
-
- statement.BindString(0, referrer_host);
- statement.BindInt64(1, manifest_id);
- statement.BindInt64(2, time.ToInternalValue());
- if (statement.Run())
- return db_->GetLastInsertRowId();
- } else {
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "UPDATE precache_referrer_hosts "
- "SET manifest_id=?, time=? "
- "WHERE id=?"));
-
- statement.BindInt64(0, manifest_id);
- statement.BindInt64(1, time.ToInternalValue());
- ;
- statement.BindInt64(2, referrer_host_id);
- if (statement.Run())
- return referrer_host_id;
- }
- return -1;
-}
-
-void PrecacheReferrerHostTable::DeleteAllEntriesBefore(
- const base::Time& delete_end) {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE, "DELETE FROM precache_referrer_hosts WHERE time < ?"));
- statement.BindInt64(0, delete_end.ToInternalValue());
- statement.Run();
-}
-
-void PrecacheReferrerHostTable::DeleteAll() {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE, "DELETE FROM precache_referrer_hosts"));
-
- statement.Run();
-}
-
-bool PrecacheReferrerHostTable::CreateTableIfNonExistent() {
- return db_->Execute(
- "CREATE TABLE IF NOT EXISTS precache_referrer_hosts "
- "(id INTEGER PRIMARY KEY, referrer_host TEXT KEY, manifest_id INTEGER, "
- "time INTEGER)");
-}
-
-std::map<std::string, PrecacheReferrerHostEntry>
-PrecacheReferrerHostTable::GetAllDataForTesting() {
- std::map<std::string, PrecacheReferrerHostEntry> all_data;
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "SELECT id, referrer_host, manifest_id, time "
- "FROM precache_referrer_hosts"));
- while (statement.Step()) {
- all_data[statement.ColumnString(1)] = PrecacheReferrerHostEntry(
- statement.ColumnInt64(0), statement.ColumnString(1),
- statement.ColumnInt64(2),
- base::Time::FromInternalValue(statement.ColumnInt64(3)));
- }
- return all_data;
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_referrer_host_table.h b/chromium/components/precache/core/precache_referrer_host_table.h
deleted file mode 100644
index e95624551ab..00000000000
--- a/chromium/components/precache/core/precache_referrer_host_table.h
+++ /dev/null
@@ -1,82 +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_PRECACHE_CORE_PRECACHE_REFERRER_HOST_TABLE_H_
-#define COMPONENTS_PRECACHE_CORE_PRECACHE_REFERRER_HOST_TABLE_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <string>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-
-namespace sql {
-class Connection;
-}
-
-namespace precache {
-
-struct PrecacheReferrerHostEntry {
- static const int64_t kInvalidId;
-
- PrecacheReferrerHostEntry() : id(kInvalidId) {}
- PrecacheReferrerHostEntry(int64_t id,
- const std::string& referrer_host,
- int64_t manifest_id,
- const base::Time& time)
- : id(id),
- referrer_host(referrer_host),
- manifest_id(manifest_id),
- time(time) {}
-
- // Comparison for testing.
- bool operator==(const PrecacheReferrerHostEntry& entry) const;
-
- int64_t id;
- std::string referrer_host;
- int64_t manifest_id;
- base::Time time;
-};
-
-class PrecacheReferrerHostTable {
- public:
- PrecacheReferrerHostTable();
- ~PrecacheReferrerHostTable();
-
- // Initialize the precache referrer host table for use with the specified
- // database connection. The caller keeps ownership of |db|, and |db| must not
- // be NULL. Init must be called before any other methods.
- bool Init(sql::Connection* db);
-
- // Returns the referrer host information about |referrer_host|.
- PrecacheReferrerHostEntry GetReferrerHost(const std::string& referrer_host);
-
- // Updates the referrer host information about |referrer_host|.
- int64_t UpdateReferrerHost(const std::string& referrer_host,
- int64_t manifest_id,
- const base::Time& time);
-
- // Deletes entries that were created before the time of |delete_end|.
- void DeleteAllEntriesBefore(const base::Time& delete_end);
-
- // Delete all entries.
- void DeleteAll();
-
- // Used by tests to get the contents of the table.
- std::map<std::string, PrecacheReferrerHostEntry> GetAllDataForTesting();
-
- private:
- bool CreateTableIfNonExistent();
-
- // Not owned by |this|.
- sql::Connection* db_;
-
- DISALLOW_COPY_AND_ASSIGN(PrecacheReferrerHostTable);
-};
-
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CORE_PRECACHE_REFERRER_HOST_TABLE_H_
diff --git a/chromium/components/precache/core/precache_referrer_host_table_unittest.cc b/chromium/components/precache/core/precache_referrer_host_table_unittest.cc
deleted file mode 100644
index 624f1e26b9d..00000000000
--- a/chromium/components/precache/core/precache_referrer_host_table_unittest.cc
+++ /dev/null
@@ -1,154 +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/precache/core/precache_referrer_host_table.h"
-
-#include <map>
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/time/time.h"
-#include "sql/connection.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace precache {
-
-namespace {
-
-const char* kReffererHostFoo = "foo.com";
-const char* kReffererHostBar = "bar.com";
-const int64_t kManifestIdFoo = 1001;
-const int64_t kManifestIdBar = 1002;
-
-class PrecacheReferrerHostTableTest : public testing::Test {
- public:
- PrecacheReferrerHostTableTest() {}
- ~PrecacheReferrerHostTableTest() override {}
-
- protected:
- void SetUp() override {
- precache_referrer_host_table_.reset(new PrecacheReferrerHostTable());
- db_.reset(new sql::Connection());
- ASSERT_TRUE(db_->OpenInMemory());
- precache_referrer_host_table_->Init(db_.get());
- }
-
- std::unique_ptr<PrecacheReferrerHostTable> precache_referrer_host_table_;
- std::unique_ptr<sql::Connection> db_;
-};
-
-TEST_F(PrecacheReferrerHostTableTest, GetReferrerHost) {
- const base::Time kTimeFoo = base::Time::FromInternalValue(100);
- const base::Time kTimeBar = base::Time::FromInternalValue(200);
- std::map<std::string, PrecacheReferrerHostEntry> expected_entries;
-
- // Add new referrer hosts.
- int64_t foo_id = precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostFoo, kManifestIdFoo, kTimeFoo);
- int64_t bar_id = precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostBar, kManifestIdBar, kTimeBar);
-
- EXPECT_NE(-1, foo_id);
- EXPECT_NE(-1, bar_id);
-
- EXPECT_EQ(PrecacheReferrerHostEntry(foo_id, kReffererHostFoo, kManifestIdFoo,
- kTimeFoo),
- precache_referrer_host_table_->GetReferrerHost(kReffererHostFoo));
- EXPECT_EQ(PrecacheReferrerHostEntry(bar_id, kReffererHostBar, kManifestIdBar,
- kTimeBar),
- precache_referrer_host_table_->GetReferrerHost(kReffererHostBar));
-
- expected_entries[kReffererHostFoo] = PrecacheReferrerHostEntry(
- foo_id, kReffererHostFoo, kManifestIdFoo, kTimeFoo);
- expected_entries[kReffererHostBar] = PrecacheReferrerHostEntry(
- bar_id, kReffererHostBar, kManifestIdBar, kTimeBar);
- EXPECT_THAT(expected_entries,
- ::testing::ContainerEq(
- precache_referrer_host_table_->GetAllDataForTesting()));
-}
-
-TEST_F(PrecacheReferrerHostTableTest, UpdateReferrerHost) {
- const base::Time kTimeFoo = base::Time::FromInternalValue(100);
- const base::Time kTimeBar = base::Time::FromInternalValue(200);
- std::map<std::string, PrecacheReferrerHostEntry> expected_entries;
-
- // Add new referrer hosts.
- int64_t foo_id = precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostFoo, kManifestIdFoo, kTimeFoo);
- int64_t bar_id = precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostBar, kManifestIdBar, kTimeBar);
-
- EXPECT_NE(-1, foo_id);
- EXPECT_NE(-1, bar_id);
-
- expected_entries[kReffererHostFoo] = PrecacheReferrerHostEntry(
- foo_id, kReffererHostFoo, kManifestIdFoo, kTimeFoo);
- expected_entries[kReffererHostBar] = PrecacheReferrerHostEntry(
- bar_id, kReffererHostBar, kManifestIdBar, kTimeBar);
- EXPECT_THAT(expected_entries,
- ::testing::ContainerEq(
- precache_referrer_host_table_->GetAllDataForTesting()));
-
- // Updating referrer hosts should return the same ID.
- EXPECT_EQ(foo_id, precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostFoo, kManifestIdFoo, kTimeFoo));
- EXPECT_EQ(bar_id, precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostBar, kManifestIdBar, kTimeBar));
- EXPECT_THAT(expected_entries,
- ::testing::ContainerEq(
- precache_referrer_host_table_->GetAllDataForTesting()));
-}
-
-TEST_F(PrecacheReferrerHostTableTest, DeleteAll) {
- const base::Time kTimeFoo = base::Time::FromInternalValue(100);
- const base::Time kTimeBar = base::Time::FromInternalValue(200);
- std::map<std::string, PrecacheReferrerHostEntry> expected_entries;
-
- // Add new referrer hosts.
- int64_t foo_id = precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostFoo, kManifestIdFoo, kTimeFoo);
- int64_t bar_id = precache_referrer_host_table_->UpdateReferrerHost(
- kReffererHostBar, kManifestIdBar, kTimeBar);
-
- EXPECT_NE(-1, foo_id);
- EXPECT_NE(-1, bar_id);
-
- expected_entries[kReffererHostFoo] = PrecacheReferrerHostEntry(
- foo_id, kReffererHostFoo, kManifestIdFoo, kTimeFoo);
- expected_entries[kReffererHostBar] = PrecacheReferrerHostEntry(
- bar_id, kReffererHostBar, kManifestIdBar, kTimeBar);
- EXPECT_THAT(expected_entries,
- ::testing::ContainerEq(
- precache_referrer_host_table_->GetAllDataForTesting()));
-
- precache_referrer_host_table_->DeleteAll();
-
- EXPECT_EQ(0UL, precache_referrer_host_table_->GetAllDataForTesting().size());
-}
-
-TEST_F(PrecacheReferrerHostTableTest, DeleteAllEntriesBefore) {
- const base::Time kOldTime = base::Time::FromInternalValue(10);
- const base::Time kBeforeTime = base::Time::FromInternalValue(20);
- const base::Time kEndTime = base::Time::FromInternalValue(30);
- const base::Time kAfterTime = base::Time::FromInternalValue(40);
-
- precache_referrer_host_table_->UpdateReferrerHost("old.com", 1, kOldTime);
- precache_referrer_host_table_->UpdateReferrerHost("before.com", 2,
- kBeforeTime);
- precache_referrer_host_table_->UpdateReferrerHost("end.com", 3, kEndTime);
- precache_referrer_host_table_->UpdateReferrerHost("after.com", 4, kAfterTime);
-
- precache_referrer_host_table_->DeleteAllEntriesBefore(kEndTime);
-
- const auto actual_entries =
- precache_referrer_host_table_->GetAllDataForTesting();
- EXPECT_EQ(2UL, actual_entries.size());
- EXPECT_NE(actual_entries.end(), actual_entries.find("end.com"));
- EXPECT_NE(actual_entries.end(), actual_entries.find("after.com"));
-}
-
-} // namespace
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_session_table.cc b/chromium/components/precache/core/precache_session_table.cc
deleted file mode 100644
index f7ac55c7a0e..00000000000
--- a/chromium/components/precache/core/precache_session_table.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/precache/core/precache_session_table.h"
-
-#include <stdint.h>
-#include <string>
-
-#include "base/logging.h"
-#include "base/time/time.h"
-#include "components/precache/core/proto/timestamp.pb.h"
-#include "components/precache/core/proto/unfinished_work.pb.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
-
-using sql::Statement;
-
-namespace precache {
-
-PrecacheSessionTable::PrecacheSessionTable() : db_(nullptr) {}
-
-PrecacheSessionTable::~PrecacheSessionTable() {}
-
-bool PrecacheSessionTable::Init(sql::Connection* db) {
- DCHECK(!db_); // Init must only be called once.
- DCHECK(db); // The database connection must be non-NULL.
- db_ = db;
- return CreateTableIfNonExistent();
-}
-
-void PrecacheSessionTable::SetSessionDataType(SessionDataType id,
- const std::string& data) {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE,
- "INSERT OR REPLACE INTO precache_session (type, value) VALUES(?,?)"));
- statement.BindInt(0, static_cast<int>(id));
- statement.BindString(1, data);
- statement.Run();
-}
-
-std::string PrecacheSessionTable::GetSessionDataType(SessionDataType id) {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE, "SELECT value from precache_session where type=?"));
- statement.BindInt(0, static_cast<int>(id));
- return statement.Step() ? statement.ColumnString(0) : std::string();
-}
-
-void PrecacheSessionTable::SetLastPrecacheTimestamp(const base::Time& time) {
- DCHECK(!time.is_null());
- Timestamp timestamp;
- timestamp.set_seconds((time - base::Time::UnixEpoch()).InSeconds());
- SetSessionDataType(SessionDataType::LAST_PRECACHE_TIMESTAMP,
- timestamp.SerializeAsString());
-}
-
-base::Time PrecacheSessionTable::GetLastPrecacheTimestamp() {
- Timestamp timestamp;
- const std::string data =
- GetSessionDataType(SessionDataType::LAST_PRECACHE_TIMESTAMP);
- if (!data.empty())
- timestamp.ParseFromString(data);
- return timestamp.has_seconds()
- ? base::Time::UnixEpoch() +
- base::TimeDelta::FromSeconds(timestamp.seconds())
- : base::Time();
-}
-
-void PrecacheSessionTable::DeleteLastPrecacheTimestamp() {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE, "DELETE FROM precache_session where type=?"));
- statement.BindInt(0,
- static_cast<int>(SessionDataType::LAST_PRECACHE_TIMESTAMP));
- statement.Run();
-}
-
-// Store unfinished work.
-void PrecacheSessionTable::SaveUnfinishedWork(
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work) {
- SetSessionDataType(SessionDataType::UNFINISHED_WORK,
- unfinished_work->SerializeAsString());
-}
-
-// Retrieve unfinished work.
-std::unique_ptr<PrecacheUnfinishedWork>
-PrecacheSessionTable::GetUnfinishedWork() {
- const std::string data = GetSessionDataType(SessionDataType::UNFINISHED_WORK);
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- if (!data.empty())
- unfinished_work->ParseFromString(data);
- return unfinished_work;
-}
-
-void PrecacheSessionTable::DeleteUnfinishedWork() {
- Statement statement(
- db_->GetCachedStatement(
- SQL_FROM_HERE, "DELETE FROM precache_session where type=?"));
- statement.BindInt(0, static_cast<int>(SessionDataType::UNFINISHED_WORK));
- statement.Run();
-}
-
-void PrecacheSessionTable::SaveQuota(const PrecacheQuota& quota) {
- SetSessionDataType(SessionDataType::QUOTA, quota.SerializeAsString());
-}
-
-PrecacheQuota PrecacheSessionTable::GetQuota() {
- PrecacheQuota quota;
- const std::string data = GetSessionDataType(SessionDataType::QUOTA);
- if (!data.empty())
- quota.ParseFromString(data);
- return quota;
-}
-
-bool PrecacheSessionTable::CreateTableIfNonExistent() {
- return db_->Execute(
- "CREATE TABLE IF NOT EXISTS precache_session (type INTEGER PRIMARY KEY, "
- "value STRING)");
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_session_table.h b/chromium/components/precache/core/precache_session_table.h
deleted file mode 100644
index 4ddeda91ce8..00000000000
--- a/chromium/components/precache/core/precache_session_table.h
+++ /dev/null
@@ -1,83 +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_PRECACHE_CORE_PRECACHE_SESSION_TABLE_H_
-#define COMPONENTS_PRECACHE_CORE_PRECACHE_SESSION_TABLE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "components/precache/core/proto/quota.pb.h"
-
-namespace sql {
-class Connection;
-}
-
-namespace precache {
-
-class PrecacheUnfinishedWork;
-
-// Denotes the type of session information being stored.
-enum class SessionDataType {
- // Unfinished work to do sometime later.
- UNFINISHED_WORK = 0,
-
- // Timestamp of the last precache.
- LAST_PRECACHE_TIMESTAMP = 1,
-
- // Remaining quota limits.
- QUOTA = 2,
-};
-
-class PrecacheSessionTable {
- public:
- PrecacheSessionTable();
- virtual ~PrecacheSessionTable();
-
- // Initializes the precache task URL table for use with the specified database
- // connection. The caller keeps ownership of |db|, and |db| must not be null.
- // Init must be called before any other methods.
- bool Init(sql::Connection* db);
-
- // -- Time since last precache --
-
- void SetLastPrecacheTimestamp(const base::Time& time);
-
- // If none present, it will return base::Time(), so it can be checked via
- // is_null().
- base::Time GetLastPrecacheTimestamp();
-
- void DeleteLastPrecacheTimestamp();
-
- // Precache quota.
- void SaveQuota(const PrecacheQuota& quota);
- PrecacheQuota GetQuota();
-
- // -- Unfinished work --
-
- // Stores unfinished work.
- void SaveUnfinishedWork(
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work);
-
- // Retrieves unfinished work.
- std::unique_ptr<PrecacheUnfinishedWork> GetUnfinishedWork();
-
- // Removes all unfinished work from the database.
- void DeleteUnfinishedWork();
-
- private:
- bool CreateTableIfNonExistent();
-
- void SetSessionDataType(SessionDataType id, const std::string& data);
- std::string GetSessionDataType(SessionDataType id);
-
- // Non-owned pointer.
- sql::Connection* db_;
-
- DISALLOW_COPY_AND_ASSIGN(PrecacheSessionTable);
-};
-
-} // namespace precache
-#endif // COMPONENTS_PRECACHE_CORE_PRECACHE_SESSION_TABLE_H_
diff --git a/chromium/components/precache/core/precache_session_table_unittest.cc b/chromium/components/precache/core/precache_session_table_unittest.cc
deleted file mode 100644
index dee5c0995d9..00000000000
--- a/chromium/components/precache/core/precache_session_table_unittest.cc
+++ /dev/null
@@ -1,177 +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 <stdint.h>
-
-#include "base/compiler_specific.h"
-#include "base/time/time.h"
-#include "components/precache/core/precache_session_table.h"
-#include "components/precache/core/proto/quota.pb.h"
-#include "components/precache/core/proto/unfinished_work.pb.h"
-#include "sql/connection.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace precache {
-
-namespace {
-
-class PrecacheSessionTableTest : public testing::Test {
- public:
- PrecacheSessionTableTest() {}
- ~PrecacheSessionTableTest() override {}
-
- protected:
- void SetUp() override {
- precache_session_table_.reset(new PrecacheSessionTable());
- db_.reset(new sql::Connection());
- ASSERT_TRUE(db_->OpenInMemory());
- precache_session_table_->Init(db_.get());
- }
-
- std::unique_ptr<PrecacheSessionTable> precache_session_table_;
- std::unique_ptr<sql::Connection> db_;
-};
-
-TEST_F(PrecacheSessionTableTest, LastPrecacheTimestamp) {
- const base::Time sometime = base::Time::FromDoubleT(42);
-
- precache_session_table_->SetLastPrecacheTimestamp(sometime);
-
- EXPECT_EQ(sometime, precache_session_table_->GetLastPrecacheTimestamp());
-
- precache_session_table_->DeleteLastPrecacheTimestamp();
-
- EXPECT_EQ(base::Time(), precache_session_table_->GetLastPrecacheTimestamp());
-}
-
-TEST_F(PrecacheSessionTableTest, SaveAndGetUnfinishedWork) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("foo.com");
- unfinished_work->add_top_host()->set_hostname("bar.com");
- auto* s = unfinished_work->mutable_config_settings();
- s->set_top_sites_count(11);
- s->add_forced_site("baz.com");
- s->set_top_resources_count(12);
- s->set_max_bytes_per_resource(501);
- s->set_max_bytes_total(1001);
- unfinished_work->add_resource()->set_url("http://x.com/");
- unfinished_work->add_resource()->set_url("http://y.com/");
- unfinished_work->add_resource()->set_url("http://z.com/");
- unfinished_work->set_total_bytes(13);
- unfinished_work->set_network_bytes(14);
- unfinished_work->set_num_manifest_urls(15);
- base::Time sometime = base::Time::UnixEpoch();
- unfinished_work->set_start_time(sometime.ToInternalValue());
-
- precache_session_table_->SaveUnfinishedWork(std::move(unfinished_work));
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work2 =
- precache_session_table_->GetUnfinishedWork();
-
- EXPECT_EQ(2, unfinished_work2->top_host_size());
- EXPECT_EQ("foo.com", unfinished_work2->top_host(0).hostname());
- EXPECT_EQ("bar.com", unfinished_work2->top_host(1).hostname());
- EXPECT_EQ(11, unfinished_work2->config_settings().top_sites_count());
- EXPECT_EQ(1, unfinished_work2->config_settings().forced_site_size());
- EXPECT_EQ("baz.com", unfinished_work2->config_settings().forced_site(0));
- EXPECT_EQ(12, unfinished_work2->config_settings().top_resources_count());
- EXPECT_EQ(501ul,
- unfinished_work2->config_settings().max_bytes_per_resource());
- EXPECT_EQ(1001ul, unfinished_work2->config_settings().max_bytes_total());
- EXPECT_EQ(3, unfinished_work2->resource_size());
- EXPECT_EQ("http://x.com/", unfinished_work2->resource(0).url());
- EXPECT_EQ("http://y.com/", unfinished_work2->resource(1).url());
- EXPECT_EQ("http://z.com/", unfinished_work2->resource(2).url());
- EXPECT_EQ(13ul, unfinished_work2->total_bytes());
- EXPECT_EQ(14ul, unfinished_work2->network_bytes());
- EXPECT_EQ(15ul, unfinished_work2->num_manifest_urls());
- EXPECT_EQ(base::Time::UnixEpoch(),
- base::Time::FromInternalValue(unfinished_work2->start_time()));
-}
-
-// Test that storing overwrites previous unfinished work.
-TEST_F(PrecacheSessionTableTest, SaveAgainAndGet) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("a.com");
- precache_session_table_->SaveUnfinishedWork(std::move(unfinished_work));
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work2(
- new PrecacheUnfinishedWork());
- unfinished_work2->add_top_host()->set_hostname("b.com");
- precache_session_table_->SaveUnfinishedWork(std::move(unfinished_work2));
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work3 =
- precache_session_table_->GetUnfinishedWork();
- EXPECT_EQ("b.com", unfinished_work3->top_host(0).hostname());
-}
-
-// Test that reading does not remove unfinished work from storage.
-TEST_F(PrecacheSessionTableTest, SaveAndGetAgain) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("a.com");
- precache_session_table_->SaveUnfinishedWork(std::move(unfinished_work));
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work2 =
- precache_session_table_->GetUnfinishedWork();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work3 =
- precache_session_table_->GetUnfinishedWork();
-
- EXPECT_EQ("a.com", unfinished_work3->top_host(0).hostname());
-}
-
-// Test that storing a large proto works.
-TEST_F(PrecacheSessionTableTest, SaveManyURLs) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- for (int i = 0; i < 1000; ++i)
- unfinished_work->add_top_host()->set_hostname("a.com");
- precache_session_table_->SaveUnfinishedWork(std::move(unfinished_work));
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work2 =
- precache_session_table_->GetUnfinishedWork();
-
- EXPECT_EQ(1000, unfinished_work2->top_host_size());
- for (int i = 0; i < 1000; ++i)
- EXPECT_EQ("a.com", unfinished_work2->top_host(i).hostname());
-}
-
-// Test that reading after deletion returns no unfinished work.
-TEST_F(PrecacheSessionTableTest, SaveDeleteGet) {
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
- new PrecacheUnfinishedWork());
- unfinished_work->add_top_host()->set_hostname("a.com");
- precache_session_table_->SaveUnfinishedWork(std::move(unfinished_work));
- precache_session_table_->DeleteUnfinishedWork();
-
- std::unique_ptr<PrecacheUnfinishedWork> unfinished_work2 =
- precache_session_table_->GetUnfinishedWork();
-
- EXPECT_EQ(0, unfinished_work2->top_host_size());
-}
-
-TEST_F(PrecacheSessionTableTest, SaveAndGetQuota) {
- // Initial quota, should have expired.
- EXPECT_LT(base::Time::FromInternalValue(
- precache_session_table_->GetQuota().start_time()),
- base::Time::Now());
-
- PrecacheQuota quota;
- quota.set_start_time(base::Time::Now().ToInternalValue());
- quota.set_remaining(1000U);
-
- PrecacheQuota expected_quota = quota;
- precache_session_table_->SaveQuota(quota);
- EXPECT_EQ(expected_quota.start_time(),
- precache_session_table_->GetQuota().start_time());
- EXPECT_EQ(expected_quota.remaining(),
- precache_session_table_->GetQuota().remaining());
-}
-
-} // namespace
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_switches.cc b/chromium/components/precache/core/precache_switches.cc
deleted file mode 100644
index a0ea84a9c9c..00000000000
--- a/chromium/components/precache/core/precache_switches.cc
+++ /dev/null
@@ -1,21 +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/precache/core/precache_switches.h"
-
-namespace precache {
-namespace switches {
-
-// Enables the proactive populating of the disk cache with Web resources that
-// are likely to be needed in future page fetches.
-const char kEnablePrecache[] = "enable-precache";
-
-// The URL that provides the PrecacheConfigurationSettings proto.
-const char kPrecacheConfigSettingsURL[] = "precache-config-settings-url";
-
-// Precache manifests will be served from URLs with this prefix.
-const char kPrecacheManifestURLPrefix[] = "precache-manifest-url-prefix";
-
-} // namespace switches
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_switches.h b/chromium/components/precache/core/precache_switches.h
deleted file mode 100644
index 5ed0c438a30..00000000000
--- a/chromium/components/precache/core/precache_switches.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PRECACHE_CORE_PRECACHE_SWITCHES_H_
-#define COMPONENTS_PRECACHE_CORE_PRECACHE_SWITCHES_H_
-
-namespace precache {
-namespace switches {
-
-// All switches in alphabetical order. The switches should be documented
-// alongside the definition of their values in the .cc file.
-extern const char kEnablePrecache[];
-extern const char kPrecacheConfigSettingsURL[];
-extern const char kPrecacheManifestURLPrefix[];
-
-} // namespace switches
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CORE_PRECACHE_SWITCHES_H_
diff --git a/chromium/components/precache/core/precache_url_table.cc b/chromium/components/precache/core/precache_url_table.cc
deleted file mode 100644
index 43ae9ebc36e..00000000000
--- a/chromium/components/precache/core/precache_url_table.cc
+++ /dev/null
@@ -1,205 +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/precache/core/precache_url_table.h"
-
-#include <string>
-
-#include "base/logging.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
-
-using sql::Statement;
-
-namespace {
-
-// Returns the spec of the given URL.
-std::string GetKey(const GURL& url) {
- return url.spec();
-}
-
-} // namespace
-
-namespace precache {
-
-bool PrecacheURLInfo::operator==(const PrecacheURLInfo& other) const {
- return was_precached == other.was_precached &&
- is_precached == other.is_precached && was_used == other.was_used &&
- is_download_reported == other.is_download_reported;
-}
-
-PrecacheURLTable::PrecacheURLTable() : db_(NULL) {}
-
-PrecacheURLTable::~PrecacheURLTable() {}
-
-bool PrecacheURLTable::Init(sql::Connection* db) {
- DCHECK(!db_); // Init must only be called once.
- DCHECK(db); // The database connection must be non-NULL.
- db_ = db;
- return CreateTableIfNonExistent();
-}
-
-void PrecacheURLTable::AddURL(const GURL& url,
- int64_t referrer_host_id,
- bool is_precached,
- const base::Time& precache_time,
- bool is_download_reported) {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE,
- "INSERT OR REPLACE INTO precache_urls "
- "(url, referrer_host_id, was_used, is_precached, time, "
- " is_download_reported)"
- "VALUES(?, ?, 0, ?, ?, ?)"));
- statement.BindString(0, GetKey(url));
- statement.BindInt64(1, referrer_host_id);
- statement.BindInt64(2, is_precached ? 1 : 0);
- statement.BindInt64(3, precache_time.ToInternalValue());
- statement.BindInt64(4, is_download_reported);
- statement.Run();
-}
-
-PrecacheURLInfo PrecacheURLTable::GetURLInfo(const GURL& url) {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT is_precached, was_used, is_download_reported "
- "FROM precache_urls WHERE url=?"));
- statement.BindString(0, GetKey(url));
-
- if (statement.Step()) {
- return {/*present=*/true, /*is_precached=*/statement.ColumnBool(0),
- /*was_used==*/statement.ColumnBool(1),
- /*is_download_reported=*/statement.ColumnBool(2)};
- } else {
- return {/*present=*/false, /*is_precached=*/false, /*was_used=*/false,
- /*is_download_reported=*/false};
- }
-}
-
-void PrecacheURLTable::SetPrecachedURLAsUsed(const GURL& url) {
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "UPDATE precache_urls SET was_used=1, "
- "is_precached=0 "
- "WHERE url=? and was_used=0 and is_precached=1"));
-
- statement.BindString(0, GetKey(url));
- statement.Run();
-}
-
-void PrecacheURLTable::SetURLAsNotPrecached(const GURL& url) {
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "UPDATE precache_urls SET is_precached=0 "
- "WHERE url=? and is_precached=1"));
- statement.BindString(0, GetKey(url));
- statement.Run();
-}
-
-void PrecacheURLTable::GetURLListForReferrerHost(
- int64_t referrer_host_id,
- std::vector<GURL>* used_urls,
- std::vector<GURL>* downloaded_urls) {
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "SELECT url, was_used, is_download_reported "
- "from precache_urls where referrer_host_id=?"));
- statement.BindInt64(0, referrer_host_id);
- while (statement.Step()) {
- GURL url(statement.ColumnString(0));
- if (statement.ColumnInt(1))
- used_urls->push_back(url);
- if (!statement.ColumnInt(2))
- downloaded_urls->push_back(url);
- }
-}
-
-void PrecacheURLTable::SetDownloadReported(int64_t referrer_host_id) {
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "UPDATE precache_urls SET is_download_reported=1 "
- "WHERE referrer_host_id=?"));
- statement.BindInt64(0, referrer_host_id);
- statement.Run();
-}
-
-void PrecacheURLTable::ClearAllForReferrerHost(int64_t referrer_host_id) {
- // Delete the URLs that are not precached.
- Statement delete_statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "DELETE FROM precache_urls WHERE "
- "referrer_host_id=? AND is_precached=0"));
- delete_statement.BindInt64(0, referrer_host_id);
- delete_statement.Run();
-
- // Clear was_used for precached URLs.
- Statement update_statement(db_->GetCachedStatement(
- SQL_FROM_HERE,
- "UPDATE precache_urls SET was_used=0 WHERE referrer_host_id=?"));
- update_statement.BindInt64(0, referrer_host_id);
- update_statement.Run();
-}
-
-void PrecacheURLTable::DeleteAllPrecachedBefore(const base::Time& delete_end) {
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE, "DELETE FROM precache_urls WHERE time < ?"));
-
- statement.BindInt64(0, delete_end.ToInternalValue());
- statement.Run();
-}
-
-void PrecacheURLTable::DeleteAll() {
- Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE, "DELETE FROM precache_urls"));
-
- statement.Run();
-}
-
-void PrecacheURLTable::GetAllDataForTesting(std::map<GURL, base::Time>* map) {
- map->clear();
-
- Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT url, time FROM precache_urls where is_precached=1"));
-
- while (statement.Step()) {
- GURL url = GURL(statement.ColumnString(0));
- (*map)[url] = base::Time::FromInternalValue(statement.ColumnInt64(1));
- }
-}
-
-bool PrecacheURLTable::CreateTableIfNonExistent() {
- // TODO(jamartin): The PRIMARY KEY should be (url, referrer_host_id).
- if (!db_->DoesTableExist("precache_urls")) {
- return db_->Execute(
- "CREATE TABLE precache_urls "
- "(url TEXT PRIMARY KEY, referrer_host_id INTEGER, was_used INTEGER, "
- "is_precached INTEGER, "
- "time INTEGER, is_download_reported INTEGER)");
- } else {
- // Migrate the table by creating the missing columns.
- if (!db_->DoesColumnExist("precache_urls", "was_used") &&
- !db_->Execute("ALTER TABLE precache_urls "
- "ADD COLUMN was_used INTEGER")) {
- return false;
- }
- if (!db_->DoesColumnExist("precache_urls", "is_precached") &&
- !db_->Execute("ALTER TABLE precache_urls ADD COLUMN is_precached "
- "INTEGER default 1")) {
- return false;
- }
- if (!db_->DoesColumnExist("precache_urls", "referrer_host_id") &&
- !db_->Execute(
- "ALTER TABLE precache_urls ADD COLUMN referrer_host_id INTEGER")) {
- return false;
- }
- if (!db_->DoesColumnExist("precache_urls", "is_download_reported") &&
- !db_->Execute("ALTER TABLE precache_urls "
- "ADD COLUMN is_download_reported INTEGER")) {
- return false;
- }
- }
- return true;
-}
-
-} // namespace precache
diff --git a/chromium/components/precache/core/precache_url_table.h b/chromium/components/precache/core/precache_url_table.h
deleted file mode 100644
index 4f4effd3631..00000000000
--- a/chromium/components/precache/core/precache_url_table.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PRECACHE_CORE_PRECACHE_URL_TABLE_H_
-#define COMPONENTS_PRECACHE_CORE_PRECACHE_URL_TABLE_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "url/gurl.h"
-
-namespace sql {
-class Connection;
-}
-
-namespace precache {
-
-// Information about a given URL with respect to the PrecacheURLTable.
-struct PrecacheURLInfo {
- // The url has been prefetched in the past 60 days. (This number comes from
- // kPrecacheHistoryExpiryPeriodDays in precache_database.cc.)
- bool was_precached;
-
- // True if the cache entry is the one fetched by PrecacheFetcher. False if a
- // new network fetch overwrote the cache entry since the prefetch.
- bool is_precached;
-
- // The prefetched copy of the URL was used in browsing (i.e. while
- // is_precached was true).
- bool was_used;
-
- // It was already reported that this resources was downloaded.
- bool is_download_reported;
-
- bool operator==(const PrecacheURLInfo& other) const;
-};
-
-// Interface for database table that keeps track of the URLs that have been
-// precached but not used. This table is used to count how many bytes were saved
-// by precached resources.
-// Each row in this table represents a URL that was precached over the network,
-// and has not been fetched through user browsing since then.
-// Manages one table { URL (primary key), precache timestamp }.
-class PrecacheURLTable {
- public:
- PrecacheURLTable();
- ~PrecacheURLTable();
-
- // Initialize the precache URL table for use with the specified database
- // connection. The caller keeps ownership of |db|, and |db| must not be NULL.
- // Init must be called before any other methods.
- bool Init(sql::Connection* db);
-
- // Adds an URL to the table, |referrer_host_id| is the id of the referrer host
- // in PrecacheReferrerHostTable, |is_precached| indicates if the URL is
- // precached, |time| is the timestamp, |is_download_reported| indicates if
- // this the download of this URL was already reported. Replaces the row if one
- // already exists.
- void AddURL(const GURL& url,
- int64_t referrer_host_id,
- bool is_precached,
- const base::Time& precache_time,
- bool is_download_reported);
-
- // Returns information about the URL's status with respect to prefetching.
- PrecacheURLInfo GetURLInfo(const GURL& url);
-
- // Sets the precached URL as used.
- void SetPrecachedURLAsUsed(const GURL& url);
-
- // Set the previously precached URL as not precached, during user browsing.
- void SetURLAsNotPrecached(const GURL& url);
-
- // Populates the used and downloaded resource URLs for the referrer host with
- // id |referrer_host_id|.
- void GetURLListForReferrerHost(int64_t referrer_host_id,
- std::vector<GURL>* used_urls,
- std::vector<GURL>* downloaded_urls);
-
- // Sets all the URLs of the given referrer_host_id as is_download_reported.
- void SetDownloadReported(int64_t referrer_host_id);
-
- // Clears all URL entries for the referrer host |referrer_host_id|.
- void ClearAllForReferrerHost(int64_t referrer_host_id);
-
- // Deletes entries that were precached before the time of |delete_end|.
- void DeleteAllPrecachedBefore(const base::Time& delete_end);
-
- // Delete all entries.
- void DeleteAll();
-
- // Used by tests to get the contents of the table.
- void GetAllDataForTesting(std::map<GURL, base::Time>* map);
-
- private:
- bool CreateTableIfNonExistent();
-
- // Non-owned pointer.
- sql::Connection* db_;
-
- DISALLOW_COPY_AND_ASSIGN(PrecacheURLTable);
-};
-
-} // namespace precache
-
-#endif // COMPONENTS_PRECACHE_CORE_PRECACHE_URL_TABLE_H_
diff --git a/chromium/components/precache/core/precache_url_table_unittest.cc b/chromium/components/precache/core/precache_url_table_unittest.cc
deleted file mode 100644
index ab9fc406d29..00000000000
--- a/chromium/components/precache/core/precache_url_table_unittest.cc
+++ /dev/null
@@ -1,202 +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/precache/core/precache_url_table.h"
-
-#include <map>
-#include <memory>
-#include <set>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/compiler_specific.h"
-#include "base/time/time.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace precache {
-
-void PrintTo(const PrecacheURLInfo& url_info, ::std::ostream* os) {
- *os << "{" << url_info.was_precached << ", " << url_info.is_precached << ", "
- << url_info.was_used << ", " << url_info.is_download_reported << "}";
-}
-
-namespace {
-
-class PrecacheURLTableTest : public testing::Test {
- public:
- PrecacheURLTableTest() {}
- ~PrecacheURLTableTest() override {}
-
- protected:
- void SetUp() override {
- precache_url_table_.reset(new PrecacheURLTable());
- db_.reset(new sql::Connection());
- ASSERT_TRUE(db_->OpenInMemory());
- ASSERT_TRUE(precache_url_table_->Init(db_.get()));
- }
-
- std::unique_ptr<PrecacheURLTable> precache_url_table_;
- std::unique_ptr<sql::Connection> db_;
-};
-
-TEST_F(PrecacheURLTableTest, AddURLWithNoExistingRow) {
- const base::Time kTime = base::Time::FromInternalValue(100);
- precache_url_table_->AddURL(GURL("http://url.com"), 1, true, kTime, false);
-
- std::map<GURL, base::Time> expected_map;
- expected_map[GURL("http://url.com")] = kTime;
-
- std::map<GURL, base::Time> actual_map;
- precache_url_table_->GetAllDataForTesting(&actual_map);
- EXPECT_EQ(expected_map, actual_map);
-}
-
-TEST_F(PrecacheURLTableTest, AddURLWithExistingRow) {
- const base::Time kOldTime = base::Time::FromInternalValue(50);
- const base::Time kNewTime = base::Time::FromInternalValue(100);
- precache_url_table_->AddURL(GURL("http://url.com"), 1, true, kOldTime, false);
- precache_url_table_->AddURL(GURL("http://url.com"), 1, true, kNewTime, false);
-
- std::map<GURL, base::Time> expected_map;
- expected_map[GURL("http://url.com")] = kNewTime;
-
- std::map<GURL, base::Time> actual_map;
- precache_url_table_->GetAllDataForTesting(&actual_map);
- EXPECT_EQ(expected_map, actual_map);
-}
-
-TEST_F(PrecacheURLTableTest, SetURLAsNotPrecached) {
- const base::Time kStaysTime = base::Time::FromInternalValue(50);
- const base::Time kDeletedTime = base::Time::FromInternalValue(100);
-
- precache_url_table_->AddURL(GURL("http://stays.com"), 1, true, kStaysTime,
- false);
- precache_url_table_->AddURL(GURL("http://deleted.com"), 1, true, kDeletedTime,
- false);
-
- precache_url_table_->SetURLAsNotPrecached(GURL("http://deleted.com"));
-
- std::map<GURL, base::Time> expected_map;
- expected_map[GURL("http://stays.com")] = kStaysTime;
-
- std::map<GURL, base::Time> actual_map;
- precache_url_table_->GetAllDataForTesting(&actual_map);
- EXPECT_EQ(expected_map, actual_map);
-}
-
-TEST_F(PrecacheURLTableTest, SetDownloadReported) {
- const GURL url("http://stays.com");
- precache_url_table_->AddURL(url, 1, true, base::Time::FromInternalValue(50),
- false);
-
- precache_url_table_->SetDownloadReported(1);
-
- EXPECT_EQ((PrecacheURLInfo{true, true, false, true}),
- precache_url_table_->GetURLInfo(url));
-}
-
-TEST_F(PrecacheURLTableTest, GetURLInfo) {
- const GURL url("http://url.com");
-
- EXPECT_EQ((PrecacheURLInfo{false, false, false, false}),
- precache_url_table_->GetURLInfo(url));
-
- precache_url_table_->AddURL(url, 1, true, base::Time::FromInternalValue(100),
- true);
-
- EXPECT_EQ((PrecacheURLInfo{true, true, false, true}),
- precache_url_table_->GetURLInfo(url));
-
- precache_url_table_->SetPrecachedURLAsUsed(url);
-
- EXPECT_EQ((PrecacheURLInfo{true, false, true, true}),
- precache_url_table_->GetURLInfo(url));
-
- precache_url_table_->AddURL(url, 1, true, base::Time::FromInternalValue(100),
- false);
-
- EXPECT_EQ((PrecacheURLInfo{true, true, false, false}),
- precache_url_table_->GetURLInfo(url));
-
- precache_url_table_->SetURLAsNotPrecached(url);
-
- EXPECT_EQ((PrecacheURLInfo{true, false, false, false}),
- precache_url_table_->GetURLInfo(url));
-
- precache_url_table_->SetDownloadReported(1);
-
- EXPECT_EQ((PrecacheURLInfo{true, false, false, true}),
- precache_url_table_->GetURLInfo(url));
-}
-
-TEST_F(PrecacheURLTableTest, DeleteAllPrecachedBefore) {
- const base::Time kOldTime = base::Time::FromInternalValue(10);
- const base::Time kBeforeTime = base::Time::FromInternalValue(20);
- const base::Time kEndTime = base::Time::FromInternalValue(30);
- const base::Time kAfterTime = base::Time::FromInternalValue(40);
-
- precache_url_table_->AddURL(GURL("http://old.com"), 1, true, kOldTime, false);
- precache_url_table_->AddURL(GURL("http://before.com"), 1, true, kBeforeTime,
- false);
- precache_url_table_->AddURL(GURL("http://end.com"), 1, true, kEndTime, false);
- precache_url_table_->AddURL(GURL("http://after.com"), 1, true, kAfterTime,
- false);
-
- precache_url_table_->DeleteAllPrecachedBefore(kEndTime);
-
- std::map<GURL, base::Time> expected_map;
- expected_map[GURL("http://end.com")] = kEndTime;
- expected_map[GURL("http://after.com")] = kAfterTime;
-
- std::map<GURL, base::Time> actual_map;
- precache_url_table_->GetAllDataForTesting(&actual_map);
- EXPECT_EQ(expected_map, actual_map);
-}
-
-TEST_F(PrecacheURLTableTest, TableMigration) {
- // Create the previous version of the URL table.
- precache_url_table_.reset(new PrecacheURLTable());
- db_.reset(new sql::Connection());
- ASSERT_TRUE(db_->OpenInMemory());
- ASSERT_TRUE(db_->Execute(
- "CREATE TABLE IF NOT EXISTS precache_urls (url TEXT PRIMARY KEY, time "
- "INTEGER)"));
-
- // Populate data for the previous version.
- const std::string old_urls[] = {"http://foo.com", "http://bar.com",
- "http://foobar.com"};
- for (const auto& url : old_urls) {
- sql::Statement statement(db_->GetCachedStatement(
- SQL_FROM_HERE, "INSERT INTO precache_urls (url, time) VALUES(?,100)"));
- statement.BindString(0, url);
- statement.Run();
- }
-
- // Verify the migration.
- ASSERT_TRUE(precache_url_table_->Init(db_.get()));
- EXPECT_TRUE(db_->DoesColumnExist("precache_urls", "was_used"));
- EXPECT_TRUE(db_->DoesColumnExist("precache_urls", "is_precached"));
- EXPECT_TRUE(db_->DoesColumnExist("precache_urls", "referrer_host_id"));
-
- std::set<std::string> actual_urls;
- sql::Statement statement(
- db_->GetCachedStatement(SQL_FROM_HERE,
- "select url, referrer_host_id, was_used, "
- "is_precached from precache_urls"));
- while (statement.Step()) {
- actual_urls.insert(statement.ColumnString(0));
- EXPECT_EQ(0, statement.ColumnInt(1));
- EXPECT_EQ(0, statement.ColumnInt(2));
- EXPECT_EQ(1, statement.ColumnInt(3));
- }
- EXPECT_THAT(std::set<std::string>(begin(old_urls), end(old_urls)),
- ::testing::ContainerEq(actual_urls));
-}
-
-} // namespace
-
-} // namespace precache
diff --git a/chromium/components/precache/core/proto/precache.proto b/chromium/components/precache/core/proto/precache.proto
deleted file mode 100644
index f8a10c0e729..00000000000
--- a/chromium/components/precache/core/proto/precache.proto
+++ /dev/null
@@ -1,160 +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.
-
-syntax = "proto2";
-
-package precache;
-
-// Chrome requires this.
-option optimize_for = LITE_RUNTIME;
-
-// Information about a cacheable resource to be precached.
-message PrecacheResource {
- // The URL of the resource. This field must always be present.
- optional string url = 1;
-
- // The tophost this resource corresponds to.
- optional string top_host_name = 2;
-
- // How important this resource is for the host. It ranges from 0.0 to 1.0.
- // Higher values mean more important.
- optional double weight_ratio = 3;
-
- // How important this resource is for the client; a combination of
- // weight_ratio and TopHost.visits. Populated only in PrecacheUnfinishedWork.
- // This is a non-negative number, with higher being more important. Its value
- // depends on PrecacheConfigurationSettings.resource_weight_function.
- optional double weight = 4;
-
- enum Type {
- RESOURCE_TYPE_UNKNOWN = 0;
-
- RESOURCE_TYPE_IMAGE = 1;
- RESOURCE_TYPE_FONT = 2;
- RESOURCE_TYPE_STYLESHEET = 3;
- RESOURCE_TYPE_SCRIPT = 4;
-
- RESOURCE_TYPE_OTHER = 7;
- }
-
- // The type of resource.
- optional Type type = 5;
-};
-
-message PrecacheManifestId {
- optional int64 id = 1;
-};
-
-// A manifest of cacheable resources to be precached for a specific host.
-// CAUTION: When any change is done here, bump kDatabaseVersion in
-// chrome/browser/predictors/resource_prefetch_predictor_tables.h
-message PrecacheManifest {
- // List of resources that we predict that the user will need if they are
- // likely to fetch the host.
- repeated PrecacheResource resource = 1;
-
- // Experiments running on this manifest.
- optional PrecacheExperiments experiments = 2;
-
- // Identifier for the manifest sent by the server.
- optional PrecacheManifestId id = 3;
-};
-
-message PrecacheExperiments {
- // A mapping between experiment groups and the resources that should be
- // considered for the experiment.
- map<fixed32, PrecacheResourceSelection> resources_by_experiment_group = 1;
-};
-
-// Determines which of the resources in the manifest should be selected.
-message PrecacheResourceSelection {
- // A bitset over the resources listed in the manifest. Bits correspond to
- // resource position in LSB-to-MSB order, as in:
- //
- // if ((0x1ULL << i) && DEPRECATED_bitset) IncludeResource(i);
- //
- // Deprecated because it only supports up to 64 resources.
- optional fixed64 DEPRECATED_bitset = 1
- [default = 0xFFFFFFFFFFFFFFFF, deprecated = true];
-
- // A bitset over the resources listed in the manifest. Bits correspond to
- // resource position. Bytes are ordered little-endian, and bits within each
- // byte are ordered LSB-to-MSB. The resulting bitstream is of mixed order,
- // but easy to test:
- //
- // if ((1 << (i % 8)) & bitset[i / 8]) IncludeResource(i);
- //
- // Takes precedence over DEPRECATED_bitset, if both are present.
- optional bytes bitset = 2;
-
- // A PrecacheResourceSelection without DEPRECATED_bitset or bitset means that
- // all resources should be selected.
-};
-
-message PrecacheConfigurationSettings {
- // The maximum rank of the user's most visited hosts to consider precaching
- // resources for, starting from 1. For example, a value of 10 means that only
- // hosts that are in the user's top 10 most visited hosts will be considered
- // as starting URLs for resource precaching. This is specified by the server
- // for testing purposes, so that it's easy to adjust how aggressively
- // resources are precached.
- // Values that are zero or lower indicate that none of the user's top sites
- // will be used for precaching.
- optional int64 top_sites_count = 1 [default = 100];
-
- // List of additional hosts that resources will be precached for.
- // These are hosts that the server predicts that the user will visit, as a
- // result of server-side analytics.
- repeated string forced_site = 2;
-
- // The number of resources to fetch for each site. Only the top
- // |top_resources_count| URLs from each manifest are fetched.
- optional int32 top_resources_count = 3 [default = 100];
-
- // The maximum number of bytes to download per resource. Downloads of
- // resources larger than this will be cancelled. This max applies only to new
- // downloads; cached resources are not capped.
- optional uint64 max_bytes_per_resource = 4 [default = 500000 /* 500 KB */];
-
- // The maximum number of bytes per precache run. While precaching, the total
- // number of bytes used for resources is tallied -- this includes new
- // downloads as well as cached resources. After this limit is reached, no
- // other resources will be downloaded.
- optional uint64 max_bytes_total = 5 [default = 10000000 /* 10 MB */];
-
- // The maximum number of bytes that can be fetched by precache on a single
- // day. After this limit is reached, no more resources will be downloaded,
- // until the quota gets replenished the next day.
- optional uint64 daily_quota_total = 6 [default = 40000000 /* 40 MB */];
-
- // The number of resources to fetch per precache run. Only the first
- // |total_resources_count| resource URLs are fetched.
- optional uint32 total_resources_count = 7 [default = 999999];
-
- // The minimum visit-adjusted weight for which a resource will be downloaded.
- optional double min_weight = 8 [default = 0];
-
- // Whether to sort resources by weight, descending, before fetching. This
- // affects the fetcher's behavior with respect to max_bytes_total and
- // total_resources_count.
- optional bool global_ranking = 9 [default = false];
-
- // If true, resource fetches are only made over the network for a given URL if
- // an existing cache entry exists and has revalidation headers.
- optional bool revalidation_only = 10 [default = false];
-
- // The function to use to combine a resource's weight_ratio with its
- // referring manifest's host_visits count to produce a final score.
- enum ResourceWeightFunction {
- // Models the expected number of requests for the resource in the next 30
- // days, given that weight_ratio is a probability that a visit to the host
- // will request a resource, and host_visits is an estimate of the number of
- // visits to the host in the next 30 days.
- FUNCTION_NAIVE = 0;
- // Models the probability of at least one request, given the same.
- FUNCTION_GEOMETRIC = 1;
- };
- optional ResourceWeightFunction resource_weight_function = 11
- [default = FUNCTION_NAIVE];
-};
diff --git a/chromium/components/precache/core/proto/quota.proto b/chromium/components/precache/core/proto/quota.proto
deleted file mode 100644
index 6a60e7c54c6..00000000000
--- a/chromium/components/precache/core/proto/quota.proto
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto2";
-
-package precache;
-
-// Chrome requires this.
-option optimize_for = LITE_RUNTIME;
-
-// Quota limit and expiry time. This is stored in a database, and not
-// transferred via network.
-message PrecacheQuota {
- // Represents the start time of this quota. After enough time has elapsed
- // since the start time (as defined in PrecacheFetcher::IsQuotaTimeExpired), a
- // new quota is created.
- optional int64 start_time = 1;
-
- // Maximum number of bytes that can be fetched until this quota expires.
- // Initialized to PrecacheConfigurationSettings.daily_quota_total and
- // decremented for every byte downloaded. After this reaches zero, the
- // PrecacheFetcher will not download any more until a new quota window starts.
- optional uint64 remaining = 2;
-};
diff --git a/chromium/components/precache/core/proto/timestamp.proto b/chromium/components/precache/core/proto/timestamp.proto
deleted file mode 100644
index 7d7efaf3e9c..00000000000
--- a/chromium/components/precache/core/proto/timestamp.proto
+++ /dev/null
@@ -1,17 +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.
-
-syntax = "proto2";
-
-package precache;
-
-// Chrome requires this.
-option optimize_for = LITE_RUNTIME;
-
-message Timestamp {
- // Represents seconds of UTC time since Unix epoch
- // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
- // 9999-12-31T23:59:59Z inclusive.
- optional int64 seconds = 1;
-}
diff --git a/chromium/components/precache/core/proto/unfinished_work.proto b/chromium/components/precache/core/proto/unfinished_work.proto
deleted file mode 100644
index cd07cbafc40..00000000000
--- a/chromium/components/precache/core/proto/unfinished_work.proto
+++ /dev/null
@@ -1,61 +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.
-
-syntax = "proto2";
-
-import "precache.proto";
-
-package precache;
-
-// Chrome requires this.
-option optimize_for = LITE_RUNTIME;
-
-message TopHost {
- // The host name of a top host.
- optional string hostname = 1;
-
- // The number of visits it had by this user.
- optional int64 visits = 2;
-};
-
-// Information about the precache work that needs to be completed.
-message PrecacheUnfinishedWork {
- // Top hosts for which to fetch manifests.
- repeated TopHost top_host = 1;
-
- optional PrecacheConfigurationSettings config_settings = 2;
-
- // DEPRECATED: Manifest URLs remaining to be fetched.
- // repeated DeprecatedPrecacheManifestURL deprecated_manifest = 3
- // [deprecated = true];
-
- // Resource URLs remaining to be fetched.
- repeated PrecacheResource resource = 4;
-
- // Tally of the total number of bytes contained in URL fetches, including
- // config, manifests, and resources. This the number of bytes as they would be
- // compressed over the network.
- optional uint64 total_bytes = 5;
-
- // Tally of the total number of bytes received over the network from URL
- // fetches (the same ones as in total_response_bytes_). This includes response
- // headers and intermediate responses such as 30xs.
- optional uint64 network_bytes = 6;
-
- // The total number of manifest URLs that the precache session started with.
- optional uint64 num_manifest_urls = 7;
-
- // The total number of resource URLs that the precache session gathered from
- // the manifests.
- optional uint64 num_resource_urls = 9;
-
- // The internal value of a base::Time object representing the precache
- // session start time. The start time is the time just before when top hosts
- // are requested.
- optional int64 start_time = 8;
-
- // The minimum resource weight that has been fetched so far. Populated only if
- // global ranking is enabled.
- optional double min_weight_fetched = 10;
-};
diff --git a/chromium/components/prefs/BUILD.gn b/chromium/components/prefs/BUILD.gn
index 029c712bb78..5387ac4e311 100644
--- a/chromium/components/prefs/BUILD.gn
+++ b/chromium/components/prefs/BUILD.gn
@@ -15,6 +15,7 @@ component("prefs") {
"json_pref_store.h",
"overlay_user_pref_store.cc",
"overlay_user_pref_store.h",
+ "persistent_pref_store.cc",
"persistent_pref_store.h",
"pref_change_registrar.cc",
"pref_change_registrar.h",
@@ -84,6 +85,8 @@ source_set("unit_tests") {
"in_memory_pref_store_unittest.cc",
"json_pref_store_unittest.cc",
"overlay_user_pref_store_unittest.cc",
+ "persistent_pref_store_unittest.cc",
+ "persistent_pref_store_unittest.h",
"pref_change_registrar_unittest.cc",
"pref_member_unittest.cc",
"pref_notifier_impl_unittest.cc",
diff --git a/chromium/components/prefs/in_memory_pref_store.h b/chromium/components/prefs/in_memory_pref_store.h
index 5529622fdd0..8d49f6c0458 100644
--- a/chromium/components/prefs/in_memory_pref_store.h
+++ b/chromium/components/prefs/in_memory_pref_store.h
@@ -46,7 +46,6 @@ class COMPONENTS_PREFS_EXPORT InMemoryPrefStore : public PersistentPrefStore {
PrefReadError GetReadError() const override;
PersistentPrefStore::PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override {}
- void CommitPendingWrite() override {}
void SchedulePendingLossyWrites() override {}
void ClearMutableValues() override {}
diff --git a/chromium/components/prefs/in_memory_pref_store_unittest.cc b/chromium/components/prefs/in_memory_pref_store_unittest.cc
index 822aa8f0f0c..14da3ef0b64 100644
--- a/chromium/components/prefs/in_memory_pref_store_unittest.cc
+++ b/chromium/components/prefs/in_memory_pref_store_unittest.cc
@@ -5,10 +5,13 @@
#include "components/prefs/in_memory_pref_store.h"
#include "base/memory/ptr_util.h"
+#include "base/test/scoped_task_environment.h"
#include "base/values.h"
+#include "components/prefs/persistent_pref_store_unittest.h"
#include "components/prefs/pref_store_observer_mock.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+
namespace {
const char kTestPref[] = "test.pref";
@@ -18,6 +21,7 @@ class InMemoryPrefStoreTest : public testing::Test {
void SetUp() override { store_ = new InMemoryPrefStore(); }
protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<InMemoryPrefStore> store_;
PrefStoreObserverMock observer_;
};
@@ -101,4 +105,8 @@ TEST_F(InMemoryPrefStoreTest, ReadPrefs) {
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, store_->ReadPrefs());
}
+TEST_F(InMemoryPrefStoreTest, CommitPendingWriteWithCallback) {
+ TestCommitPendingWriteWithCallback(store_.get());
+}
+
} // namespace
diff --git a/chromium/components/prefs/json_pref_store.cc b/chromium/components/prefs/json_pref_store.cc
index 240f1daf563..0b6b0f1803e 100644
--- a/chromium/components/prefs/json_pref_store.cc
+++ b/chromium/components/prefs/json_pref_store.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
@@ -143,13 +144,13 @@ scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
JsonPrefStore::JsonPrefStore(
const base::FilePath& pref_filename,
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> file_task_runner,
std::unique_ptr<PrefFilter> pref_filter)
: path_(pref_filename),
- sequenced_task_runner_(std::move(sequenced_task_runner)),
+ file_task_runner_(std::move(file_task_runner)),
prefs_(new base::DictionaryValue()),
read_only_(false),
- writer_(pref_filename, sequenced_task_runner_),
+ writer_(pref_filename, file_task_runner_),
pref_filter_(std::move(pref_filter)),
initialized_(false),
filtering_in_progress_(false),
@@ -162,7 +163,7 @@ JsonPrefStore::JsonPrefStore(
bool JsonPrefStore::GetValue(const std::string& key,
const base::Value** result) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Value* tmp = nullptr;
if (!prefs_->Get(key, &tmp))
@@ -178,32 +179,32 @@ std::unique_ptr<base::DictionaryValue> JsonPrefStore::GetValues() const {
}
void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.AddObserver(observer);
}
void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.RemoveObserver(observer);
}
bool JsonPrefStore::HasObservers() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return observers_.might_have_observers();
}
bool JsonPrefStore::IsInitializationComplete() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return initialized_;
}
bool JsonPrefStore::GetMutableValue(const std::string& key,
base::Value** result) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return prefs_->Get(key, result);
}
@@ -211,7 +212,7 @@ bool JsonPrefStore::GetMutableValue(const std::string& key,
void JsonPrefStore::SetValue(const std::string& key,
std::unique_ptr<base::Value> value,
uint32_t flags) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(value);
base::Value* old_value = nullptr;
@@ -225,7 +226,7 @@ void JsonPrefStore::SetValue(const std::string& key,
void JsonPrefStore::SetValueSilently(const std::string& key,
std::unique_ptr<base::Value> value,
uint32_t flags) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(value);
base::Value* old_value = nullptr;
@@ -237,7 +238,7 @@ void JsonPrefStore::SetValueSilently(const std::string& key,
}
void JsonPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (prefs_->RemovePath(key, nullptr))
ReportValueChanged(key, flags);
@@ -245,26 +246,26 @@ void JsonPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
void JsonPrefStore::RemoveValueSilently(const std::string& key,
uint32_t flags) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
prefs_->RemovePath(key, nullptr);
ScheduleWrite(flags);
}
bool JsonPrefStore::ReadOnly() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return read_only_;
}
PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return read_error_;
}
PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
OnFileRead(ReadPrefsFromDisk(path_));
return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE
@@ -272,20 +273,19 @@ PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
}
void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
initialized_ = false;
error_delegate_.reset(error_delegate);
// Weakly binds the read task so that it doesn't kick in during shutdown.
base::PostTaskAndReplyWithResult(
- sequenced_task_runner_.get(), FROM_HERE,
- base::Bind(&ReadPrefsFromDisk, path_),
+ file_task_runner_.get(), FROM_HERE, base::Bind(&ReadPrefsFromDisk, path_),
base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr()));
}
-void JsonPrefStore::CommitPendingWrite() {
- DCHECK(CalledOnValidThread());
+void JsonPrefStore::CommitPendingWrite(base::OnceClosure done_callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Schedule a write for any lossy writes that are outstanding to ensure that
// they get flushed when this function is called.
@@ -293,6 +293,15 @@ void JsonPrefStore::CommitPendingWrite() {
if (writer_.HasPendingWrite() && !read_only_)
writer_.DoScheduledWrite();
+
+ if (done_callback) {
+ // Since disk operations occur on |file_task_runner_|, the reply of a task
+ // posted to |file_task_runner_| will run after currently pending disk
+ // operations. Also, by definition of PostTaskAndReply(), the reply will run
+ // on the current sequence.
+ file_task_runner_->PostTaskAndReply(
+ FROM_HERE, base::BindOnce(&base::DoNothing), std::move(done_callback));
+ }
}
void JsonPrefStore::SchedulePendingLossyWrites() {
@@ -301,7 +310,7 @@ void JsonPrefStore::SchedulePendingLossyWrites() {
}
void JsonPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (pref_filter_)
pref_filter_->FilterUpdate(key);
@@ -314,7 +323,7 @@ void JsonPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) {
void JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback(
bool write_success) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
has_pending_write_reply_ = false;
if (!on_next_successful_write_reply_.is_null()) {
@@ -345,7 +354,7 @@ void JsonPrefStore::PostWriteCallback(
void JsonPrefStore::RegisterOnNextSuccessfulWriteReply(
const base::Closure& on_next_successful_write_reply) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(on_next_successful_write_reply_.is_null());
on_next_successful_write_reply_ = on_next_successful_write_reply;
@@ -367,7 +376,7 @@ void JsonPrefStore::RegisterOnNextSuccessfulWriteReply(
void JsonPrefStore::RegisterOnNextWriteSynchronousCallbacks(
OnWriteCallbackPair callbacks) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
has_pending_write_reply_ = true;
@@ -385,7 +394,7 @@ void JsonPrefStore::ClearMutableValues() {
}
void JsonPrefStore::OnFileRead(std::unique_ptr<ReadResult> read_result) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(read_result);
@@ -441,11 +450,12 @@ void JsonPrefStore::OnFileRead(std::unique_ptr<ReadResult> read_result) {
}
JsonPrefStore::~JsonPrefStore() {
- CommitPendingWrite();
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ CommitPendingWrite(base::OnceClosure());
}
bool JsonPrefStore::SerializeData(std::string* output) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_lossy_write_ = false;
@@ -472,7 +482,7 @@ void JsonPrefStore::FinalizeFileRead(
bool initialization_successful,
std::unique_ptr<base::DictionaryValue> prefs,
bool schedule_write) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
filtering_in_progress_ = false;
diff --git a/chromium/components/prefs/json_pref_store.h b/chromium/components/prefs/json_pref_store.h
index b298d30e691..1321a1e19dc 100644
--- a/chromium/components/prefs/json_pref_store.h
+++ b/chromium/components/prefs/json_pref_store.h
@@ -19,8 +19,8 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "base/sequence_checker.h"
#include "base/task_scheduler/post_task.h"
-#include "base/threading/non_thread_safe.h"
#include "components/prefs/base_prefs_export.h"
#include "components/prefs/persistent_pref_store.h"
#include "components/prefs/pref_filter.h"
@@ -48,8 +48,7 @@ FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps);
class COMPONENTS_PREFS_EXPORT JsonPrefStore
: public PersistentPrefStore,
public base::ImportantFileWriter::DataSerializer,
- public base::SupportsWeakPtr<JsonPrefStore>,
- public base::NonThreadSafe {
+ public base::SupportsWeakPtr<JsonPrefStore> {
public:
struct ReadResult;
@@ -66,12 +65,12 @@ class COMPONENTS_PREFS_EXPORT JsonPrefStore
// |pref_filename| is the path to the file to read prefs from. It is incorrect
// to create multiple JsonPrefStore with the same |pref_filename|.
- // |sequenced_task_runner| is used for asynchronous reads and writes. It must
+ // |file_task_runner| is used for asynchronous reads and writes. It must
// have the base::TaskShutdownBehavior::BLOCK_SHUTDOWN and base::MayBlock()
// traits. Unless external tasks need to run on the same sequence as
// JsonPrefStore tasks, keep the default value.
JsonPrefStore(const base::FilePath& pref_filename,
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner =
+ scoped_refptr<base::SequencedTaskRunner> file_task_runner =
base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(),
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
@@ -102,7 +101,7 @@ class COMPONENTS_PREFS_EXPORT JsonPrefStore
// See details in pref_filter.h.
PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
- void CommitPendingWrite() override;
+ void CommitPendingWrite(base::OnceClosure done_callback) override;
void SchedulePendingLossyWrites() override;
void ReportValueChanged(const std::string& key, uint32_t flags) override;
@@ -227,7 +226,7 @@ class COMPONENTS_PREFS_EXPORT JsonPrefStore
void ScheduleWrite(uint32_t flags);
const base::FilePath path_;
- const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
+ const scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
std::unique_ptr<base::DictionaryValue> prefs_;
@@ -253,6 +252,8 @@ class COMPONENTS_PREFS_EXPORT JsonPrefStore
WriteCountHistogram write_count_histogram_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(JsonPrefStore);
};
diff --git a/chromium/components/prefs/json_pref_store_unittest.cc b/chromium/components/prefs/json_pref_store_unittest.cc
index cae79d34143..54f878445b4 100644
--- a/chromium/components/prefs/json_pref_store_unittest.cc
+++ b/chromium/components/prefs/json_pref_store_unittest.cc
@@ -16,7 +16,6 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram_samples.h"
#include "base/path_service.h"
#include "base/run_loop.h"
@@ -25,11 +24,13 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/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"
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread.h"
#include "base/values.h"
+#include "components/prefs/persistent_pref_store_unittest.h"
#include "components/prefs/pref_filter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -129,9 +130,25 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
};
-} // namespace
+enum class CommitPendingWriteMode {
+ WITHOUT_CALLBACK,
+ WITH_CALLBACK,
+};
+
+void CommitPendingWrite(
+ JsonPrefStore* pref_store,
+ CommitPendingWriteMode commit_pending_write_mode,
+ base::test::ScopedTaskEnvironment* scoped_task_environment) {
+ if (commit_pending_write_mode == CommitPendingWriteMode::WITHOUT_CALLBACK) {
+ pref_store->CommitPendingWrite(OnceClosure());
+ scoped_task_environment->RunUntilIdle();
+ } else {
+ TestCommitPendingWriteWithCallback(pref_store);
+ }
+}
-class JsonPrefStoreTest : public testing::Test {
+class JsonPrefStoreTest
+ : public testing::TestWithParam<CommitPendingWriteMode> {
public:
JsonPrefStoreTest() = default;
@@ -140,27 +157,21 @@ class JsonPrefStoreTest : public testing::Test {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
- void TearDown() override {
- // Make sure all pending tasks have been processed (e.g., deleting the
- // JsonPrefStore may post write tasks).
- RunLoop().RunUntilIdle();
- }
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
// The path to temporary directory used to contain the test operations.
base::ScopedTempDir temp_dir_;
- // A message loop that we can use as the file thread message loop.
- MessageLoop message_loop_;
DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreTest);
};
+} // namespace
+
// Test fallback behavior for a nonexistent file.
TEST_F(JsonPrefStoreTest, NonExistentFile) {
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(bogus_input_file, message_loop_.task_runner(),
- std::unique_ptr<PrefFilter>());
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly());
@@ -172,8 +183,7 @@ TEST_F(JsonPrefStoreTest, InvalidFile) {
ASSERT_LT(0, base::WriteFile(invalid_file,
kInvalidJson, arraysize(kInvalidJson) - 1));
- scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- invalid_file, message_loop_.task_runner(), std::unique_ptr<PrefFilter>());
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(invalid_file);
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly());
@@ -191,8 +201,11 @@ TEST_F(JsonPrefStoreTest, InvalidFile) {
// This function is used to avoid code duplication while testing synchronous
// and asynchronous version of the JsonPrefStore loading. It validates that the
// given output file's contents matches kWriteGolden.
-void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
- const base::FilePath& output_file) {
+void RunBasicJsonPrefStoreTest(
+ JsonPrefStore* pref_store,
+ const base::FilePath& output_file,
+ CommitPendingWriteMode commit_pending_write_mode,
+ base::test::ScopedTaskEnvironment* scoped_task_environment) {
const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
const char kMaxTabs[] = "tabs.max_tabs";
const char kLongIntPref[] = "long_int.pref";
@@ -253,8 +266,8 @@ void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
EXPECT_EQ(214748364842LL, value);
// Serialize and compare to expected output.
- pref_store->CommitPendingWrite();
- RunLoop().RunUntilIdle();
+ CommitPendingWrite(pref_store, commit_pending_write_mode,
+ scoped_task_environment);
std::string output_contents;
ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents));
@@ -262,15 +275,14 @@ void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
ASSERT_TRUE(base::DeleteFile(output_file, false));
}
-TEST_F(JsonPrefStoreTest, Basic) {
+TEST_P(JsonPrefStoreTest, Basic) {
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
ASSERT_LT(0, base::WriteFile(input_file,
kReadJson, arraysize(kReadJson) - 1));
// Test that the persistent value can be loaded.
ASSERT_TRUE(PathExists(input_file));
- scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- input_file, message_loop_.task_runner(), std::unique_ptr<PrefFilter>());
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly());
EXPECT_TRUE(pref_store->IsInitializationComplete());
@@ -285,17 +297,17 @@ TEST_F(JsonPrefStoreTest, Basic) {
// }
// }
- RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+ &scoped_task_environment_);
}
-TEST_F(JsonPrefStoreTest, BasicAsync) {
+TEST_P(JsonPrefStoreTest, BasicAsync) {
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
ASSERT_LT(0, base::WriteFile(input_file,
kReadJson, arraysize(kReadJson) - 1));
// Test that the persistent value can be loaded.
- scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- input_file, message_loop_.task_runner(), std::unique_ptr<PrefFilter>());
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
{
MockPrefStoreObserver mock_observer;
@@ -307,7 +319,7 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
EXPECT_CALL(*mock_error_delegate,
OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
pref_store->RemoveObserver(&mock_observer);
EXPECT_FALSE(pref_store->ReadOnly());
@@ -324,28 +336,26 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
// }
// }
- RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+ &scoped_task_environment_);
}
-TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
+TEST_P(JsonPrefStoreTest, PreserveEmptyValues) {
FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
- scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- pref_file, message_loop_.task_runner(), std::unique_ptr<PrefFilter>());
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
// Set some keys with empty values.
- pref_store->SetValue("list", base::WrapUnique(new base::ListValue),
+ pref_store->SetValue("list", base::MakeUnique<base::ListValue>(),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- pref_store->SetValue("dict", base::WrapUnique(new base::DictionaryValue),
+ pref_store->SetValue("dict", base::MakeUnique<base::DictionaryValue>(),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
// Write to file.
- pref_store->CommitPendingWrite();
- RunLoop().RunUntilIdle();
+ CommitPendingWrite(pref_store.get(), GetParam(), &scoped_task_environment_);
// Reload.
- pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(),
- std::unique_ptr<PrefFilter>());
+ pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
ASSERT_FALSE(pref_store->ReadOnly());
@@ -362,8 +372,7 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
- scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- pref_file, message_loop_.task_runner(), std::unique_ptr<PrefFilter>());
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
dict->SetString("key", "value");
@@ -382,9 +391,7 @@ TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(bogus_input_file, message_loop_.task_runner(),
- std::unique_ptr<PrefFilter>());
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
MockPrefStoreObserver mock_observer;
pref_store->AddObserver(&mock_observer);
@@ -394,13 +401,13 @@ TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
EXPECT_CALL(*mock_error_delegate,
OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
pref_store->RemoveObserver(&mock_observer);
EXPECT_FALSE(pref_store->ReadOnly());
}
-TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
+TEST_P(JsonPrefStoreTest, ReadWithInterceptor) {
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
ASSERT_LT(0, base::WriteFile(input_file,
kReadJson, arraysize(kReadJson) - 1));
@@ -409,9 +416,9 @@ TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
new InterceptingPrefFilter());
InterceptingPrefFilter* raw_intercepting_pref_filter_ =
intercepting_pref_filter.get();
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(input_file, message_loop_.task_runner(),
- std::move(intercepting_pref_filter));
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(
+ input_file, base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}),
+ std::move(intercepting_pref_filter));
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
pref_store->ReadPrefs());
@@ -439,10 +446,11 @@ TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
// }
// }
- RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+ &scoped_task_environment_);
}
-TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
+TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
ASSERT_LT(0, base::WriteFile(input_file,
kReadJson, arraysize(kReadJson) - 1));
@@ -451,9 +459,9 @@ TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
new InterceptingPrefFilter());
InterceptingPrefFilter* raw_intercepting_pref_filter_ =
intercepting_pref_filter.get();
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(input_file, message_loop_.task_runner(),
- std::move(intercepting_pref_filter));
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(
+ input_file, base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}),
+ std::move(intercepting_pref_filter));
MockPrefStoreObserver mock_observer;
pref_store->AddObserver(&mock_observer);
@@ -467,7 +475,7 @@ TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
// EXPECT_CALL(*mock_error_delegate,
// OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(pref_store->ReadOnly());
EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
@@ -500,7 +508,8 @@ TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
// }
// }
- RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+ &scoped_task_environment_);
}
TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) {
@@ -652,6 +661,15 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) {
histogram_tester.ExpectTotalCount(histogram_name, 6);
}
+INSTANTIATE_TEST_CASE_P(
+ WithoutCallback,
+ JsonPrefStoreTest,
+ ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK));
+INSTANTIATE_TEST_CASE_P(
+ WithCallback,
+ JsonPrefStoreTest,
+ ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK));
+
class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
public:
JsonPrefStoreLossyWriteTest() = default;
@@ -663,8 +681,7 @@ class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
}
scoped_refptr<JsonPrefStore> CreatePrefStore() {
- return new JsonPrefStore(test_file_, message_loop_.task_runner(),
- std::unique_ptr<PrefFilter>());
+ return base::MakeRefCounted<JsonPrefStore>(test_file_);
}
// Return the ImportantFileWriter for a given JsonPrefStore.
@@ -675,7 +692,7 @@ class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
// Get the contents of kTestFile. Pumps the message loop before returning the
// result.
std::string GetTestFileContents() {
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
std::string file_contents;
ReadFileToString(test_file_, &file_contents);
return file_contents;
@@ -726,7 +743,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
// Call CommitPendingWrite and check that the lossy pref and the normal pref
// are there with the last values set above.
- pref_store->CommitPendingWrite();
+ pref_store->CommitPendingWrite(base::OnceClosure());
ASSERT_FALSE(file_writer->HasPendingWrite());
ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
GetTestFileContents());
@@ -791,7 +808,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
// Call CommitPendingWrite and check that the lossy pref is there with the
// last value set above.
- pref_store->CommitPendingWrite();
+ pref_store->CommitPendingWrite(base::OnceClosure());
ASSERT_FALSE(file_writer->HasPendingWrite());
ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
}
@@ -904,8 +921,7 @@ class JsonPrefStoreCallbackTest : public JsonPrefStoreTest {
}
scoped_refptr<JsonPrefStore> CreatePrefStore() {
- return new JsonPrefStore(test_file_, message_loop_.task_runner(),
- std::unique_ptr<PrefFilter>());
+ return base::MakeRefCounted<JsonPrefStore>(test_file_);
}
// Return the ImportantFileWriter for a given JsonPrefStore.
@@ -931,16 +947,22 @@ class JsonPrefStoreCallbackTest : public JsonPrefStoreTest {
DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreCallbackTest);
};
-TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) {
+// Flaky on Linux TSAN. http://crbug.com/732445.
+#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+#define MAYBE_TestSerializeDataCallbacks DISABLED_TestSerializeDataCallbacks
+#else
+#define MAYBE_TestSerializeDataCallbacks TestSerializeDataCallbacks
+#endif
+TEST_F(JsonPrefStoreCallbackTest, MAYBE_TestSerializeDataCallbacks) {
base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
ASSERT_LT(0,
base::WriteFile(input_file, kReadJson, arraysize(kReadJson) - 1));
std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
new InterceptingPrefFilter(write_callback_observer_.GetCallbackPair()));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(input_file, message_loop_.task_runner(),
- std::move(intercepting_pref_filter));
+ auto pref_store = base::MakeRefCounted<JsonPrefStore>(
+ input_file, base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}),
+ std::move(intercepting_pref_filter));
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
EXPECT_EQ(NOT_CALLED,
@@ -954,7 +976,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) {
EXPECT_EQ(NOT_CALLED,
write_callback_observer_.GetAndResetPostWriteObservationState());
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
EXPECT_EQ(CALLED_WITH_SUCCESS,
@@ -970,7 +992,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
file_writer->WriteNow(MakeUnique<std::string>("foo"));
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
EXPECT_EQ(CALLED_WITH_SUCCESS,
@@ -981,7 +1003,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
file_writer->WriteNow(MakeUnique<std::string>("foo"));
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
EXPECT_EQ(CALLED_WITH_SUCCESS,
@@ -990,7 +1012,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
// Test RegisterOnNextSuccessfulWriteReply only.
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
file_writer->WriteNow(MakeUnique<std::string>("foo"));
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
EXPECT_EQ(NOT_CALLED,
@@ -999,7 +1021,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
// Test RegisterOnNextWriteSynchronousCallbacks only.
write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
file_writer->WriteNow(MakeUnique<std::string>("foo"));
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
EXPECT_EQ(CALLED_WITH_SUCCESS,
@@ -1012,7 +1034,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
// Confirm that the observers are invoked.
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
TriggerFakeWriteForCallback(pref_store.get(), true);
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_EQ(CALLED_WITH_SUCCESS,
write_callback_observer_.GetAndResetPostWriteObservationState());
@@ -1025,7 +1047,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
// Confirm that re-installing the observers works for another write.
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
TriggerFakeWriteForCallback(pref_store.get(), true);
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_EQ(CALLED_WITH_SUCCESS,
write_callback_observer_.GetAndResetPostWriteObservationState());
@@ -1034,7 +1056,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
// write, and that the synchronous observer is invoked.
successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
TriggerFakeWriteForCallback(pref_store.get(), false);
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_EQ(CALLED_WITH_ERROR,
write_callback_observer_.GetAndResetPostWriteObservationState());
@@ -1043,7 +1065,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
// being set by |PostWriteCallback| by the last TriggerFakeWriteCallback.
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
file_writer->WriteNow(MakeUnique<std::string>("foo"));
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_EQ(NOT_CALLED,
write_callback_observer_.GetAndResetPostWriteObservationState());
@@ -1063,7 +1085,7 @@ TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksDuringProfileDeath) {
soon_out_of_scope_pref_store.get());
file_writer->WriteNow(MakeUnique<std::string>("foo"));
}
- RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
EXPECT_EQ(CALLED_WITH_SUCCESS,
diff --git a/chromium/components/prefs/overlay_user_pref_store.cc b/chromium/components/prefs/overlay_user_pref_store.cc
index 48271fb01eb..55f3f687e2e 100644
--- a/chromium/components/prefs/overlay_user_pref_store.cc
+++ b/chromium/components/prefs/overlay_user_pref_store.cc
@@ -9,15 +9,48 @@
#include "base/memory/ptr_util.h"
#include "base/values.h"
+#include "components/prefs/in_memory_pref_store.h"
+
+// Allows us to monitor two pref stores and tell updates from them apart. It
+// essentially mimics a Callback for the Observer interface (e.g. it allows
+// binding additional arguments).
+class OverlayUserPrefStore::ObserverAdapter : public PrefStore::Observer {
+ public:
+ ObserverAdapter(bool overlay, OverlayUserPrefStore* parent)
+ : overlay_(overlay), parent_(parent) {}
+
+ // Methods of PrefStore::Observer.
+ void OnPrefValueChanged(const std::string& key) override {
+ parent_->OnPrefValueChanged(overlay_, key);
+ }
+ void OnInitializationCompleted(bool succeeded) override {
+ parent_->OnInitializationCompleted(overlay_, succeeded);
+ }
+
+ private:
+ // Is the update for the overlay?
+ const bool overlay_;
+ OverlayUserPrefStore* const parent_;
+};
-OverlayUserPrefStore::OverlayUserPrefStore(
- PersistentPrefStore* underlay)
- : underlay_(underlay) {
- underlay_->AddObserver(this);
+OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* underlay)
+ : OverlayUserPrefStore(new InMemoryPrefStore(), underlay) {}
+
+OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* overlay,
+ PersistentPrefStore* underlay)
+ : overlay_observer_(
+ base::MakeUnique<OverlayUserPrefStore::ObserverAdapter>(true, this)),
+ underlay_observer_(
+ base::MakeUnique<OverlayUserPrefStore::ObserverAdapter>(false, this)),
+ overlay_(overlay),
+ underlay_(underlay) {
+ DCHECK(overlay->IsInitializationComplete());
+ overlay_->AddObserver(overlay_observer_.get());
+ underlay_->AddObserver(underlay_observer_.get());
}
bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const {
- return overlay_.GetValue(key, NULL);
+ return overlay_->GetValue(key, NULL);
}
void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) {
@@ -33,23 +66,24 @@ bool OverlayUserPrefStore::HasObservers() const {
}
bool OverlayUserPrefStore::IsInitializationComplete() const {
- return underlay_->IsInitializationComplete();
+ return underlay_->IsInitializationComplete() &&
+ overlay_->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
// be an entry.
- DCHECK(ShallBeStoredInOverlay(key) || !overlay_.GetValue(key, NULL));
+ DCHECK(ShallBeStoredInOverlay(key) || !overlay_->GetValue(key, NULL));
- if (overlay_.GetValue(key, result))
+ if (overlay_->GetValue(key, result))
return true;
return underlay_->GetValue(key, result);
}
std::unique_ptr<base::DictionaryValue> OverlayUserPrefStore::GetValues() const {
auto values = underlay_->GetValues();
- auto overlay_values = overlay_.AsDictionaryValue();
+ auto overlay_values = overlay_->GetValues();
for (const auto& key : overlay_names_set_) {
std::unique_ptr<base::Value> out_value;
overlay_values->Remove(key, &out_value);
@@ -65,7 +99,8 @@ bool OverlayUserPrefStore::GetMutableValue(const std::string& key,
if (!ShallBeStoredInOverlay(key))
return underlay_->GetMutableValue(key, result);
- if (overlay_.GetValue(key, result))
+ written_overlay_names_.insert(key);
+ if (overlay_->GetMutableValue(key, result))
return true;
// Try to create copy of underlay if the overlay does not contain a value.
@@ -74,7 +109,8 @@ bool OverlayUserPrefStore::GetMutableValue(const std::string& key,
return false;
*result = underlay_value->DeepCopy();
- overlay_.SetValue(key, base::WrapUnique(*result));
+ overlay_->SetValue(key, base::WrapUnique(*result),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
return true;
}
@@ -86,8 +122,8 @@ void OverlayUserPrefStore::SetValue(const std::string& key,
return;
}
- if (overlay_.SetValue(key, std::move(value)))
- ReportValueChanged(key, flags);
+ written_overlay_names_.insert(key);
+ overlay_->SetValue(key, std::move(value), flags);
}
void OverlayUserPrefStore::SetValueSilently(const std::string& key,
@@ -98,7 +134,8 @@ void OverlayUserPrefStore::SetValueSilently(const std::string& key,
return;
}
- overlay_.SetValue(key, std::move(value));
+ written_overlay_names_.insert(key);
+ overlay_->SetValueSilently(key, std::move(value), flags);
}
void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
@@ -107,8 +144,8 @@ void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
return;
}
- if (overlay_.RemoveValue(key))
- ReportValueChanged(key, flags);
+ written_overlay_names_.insert(key);
+ overlay_->RemoveValue(key, flags);
}
bool OverlayUserPrefStore::ReadOnly() const {
@@ -121,7 +158,7 @@ PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const {
PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() {
// We do not read intentionally.
- OnInitializationCompleted(true);
+ OnInitializationCompleted(/* overlay */ false, true);
return PersistentPrefStore::PREF_READ_ERROR_NONE;
}
@@ -129,11 +166,11 @@ void OverlayUserPrefStore::ReadPrefsAsync(
ReadErrorDelegate* error_delegate_raw) {
std::unique_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
// We do not read intentionally.
- OnInitializationCompleted(true);
+ OnInitializationCompleted(/* overlay */ false, true);
}
-void OverlayUserPrefStore::CommitPendingWrite() {
- underlay_->CommitPendingWrite();
+void OverlayUserPrefStore::CommitPendingWrite(base::OnceClosure done_callback) {
+ underlay_->CommitPendingWrite(std::move(done_callback));
// We do not write our content intentionally.
}
@@ -147,16 +184,6 @@ void OverlayUserPrefStore::ReportValueChanged(const std::string& key,
observer.OnPrefValueChanged(key);
}
-void OverlayUserPrefStore::OnPrefValueChanged(const std::string& key) {
- if (!overlay_.GetValue(key, NULL))
- ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
-}
-
-void OverlayUserPrefStore::OnInitializationCompleted(bool succeeded) {
- for (PrefStore::Observer& observer : observers_)
- observer.OnInitializationCompleted(succeeded);
-}
-
void OverlayUserPrefStore::RegisterOverlayPref(const std::string& key) {
DCHECK(!key.empty()) << "Key is empty";
DCHECK(overlay_names_set_.find(key) == overlay_names_set_.end())
@@ -165,11 +192,32 @@ void OverlayUserPrefStore::RegisterOverlayPref(const std::string& key) {
}
void OverlayUserPrefStore::ClearMutableValues() {
- overlay_.Clear();
+ for (const auto& key : written_overlay_names_) {
+ overlay_->RemoveValue(key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ }
}
OverlayUserPrefStore::~OverlayUserPrefStore() {
- underlay_->RemoveObserver(this);
+ overlay_->RemoveObserver(overlay_observer_.get());
+ underlay_->RemoveObserver(underlay_observer_.get());
+}
+
+void OverlayUserPrefStore::OnPrefValueChanged(bool overlay,
+ const std::string& key) {
+ if (overlay) {
+ ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
+ } else {
+ if (!overlay_->GetValue(key, NULL))
+ ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
+ }
+}
+
+void OverlayUserPrefStore::OnInitializationCompleted(bool overlay,
+ bool succeeded) {
+ if (!IsInitializationComplete())
+ return;
+ for (PrefStore::Observer& observer : observers_)
+ observer.OnInitializationCompleted(succeeded);
}
bool OverlayUserPrefStore::ShallBeStoredInOverlay(
diff --git a/chromium/components/prefs/overlay_user_pref_store.h b/chromium/components/prefs/overlay_user_pref_store.h
index 7766dcdbaed..23cace53f80 100644
--- a/chromium/components/prefs/overlay_user_pref_store.h
+++ b/chromium/components/prefs/overlay_user_pref_store.h
@@ -21,10 +21,13 @@
// 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_|.
-class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore : public PersistentPrefStore,
- public PrefStore::Observer {
+class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore
+ : public PersistentPrefStore {
public:
explicit OverlayUserPrefStore(PersistentPrefStore* underlay);
+ // The |overlay| must already be initialized.
+ OverlayUserPrefStore(PersistentPrefStore* overlay,
+ PersistentPrefStore* underlay);
// Returns true if a value has been set for the |key| in this
// OverlayUserPrefStore, i.e. if it potentially overrides a value
@@ -53,14 +56,10 @@ class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore : public PersistentPrefStore,
PrefReadError GetReadError() const override;
PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* delegate) override;
- void CommitPendingWrite() override;
+ void CommitPendingWrite(base::OnceClosure done_callback) override;
void SchedulePendingLossyWrites() override;
void ReportValueChanged(const std::string& key, uint32_t flags) override;
- // Methods of PrefStore::Observer.
- void OnPrefValueChanged(const std::string& key) override;
- void OnInitializationCompleted(bool succeeded) override;
-
void RegisterOverlayPref(const std::string& key);
void ClearMutableValues() override;
@@ -70,15 +69,22 @@ class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore : public PersistentPrefStore,
private:
typedef std::set<std::string> NamesSet;
+ class ObserverAdapter;
+
+ void OnPrefValueChanged(bool overlay, const std::string& key);
+ void OnInitializationCompleted(bool overlay, 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;
base::ObserverList<PrefStore::Observer, true> observers_;
- PrefValueMap overlay_;
+ 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_;
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 e32bf1e35ae..26241494b8c 100644
--- a/chromium/components/prefs/overlay_user_pref_store_unittest.cc
+++ b/chromium/components/prefs/overlay_user_pref_store_unittest.cc
@@ -5,7 +5,9 @@
#include "components/prefs/overlay_user_pref_store.h"
#include "base/memory/ptr_util.h"
+#include "base/test/scoped_task_environment.h"
#include "base/values.h"
+#include "components/prefs/persistent_pref_store_unittest.h"
#include "components/prefs/pref_store_observer_mock.h"
#include "components/prefs/testing_pref_store.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -38,6 +40,7 @@ class OverlayUserPrefStoreTest : public testing::Test {
~OverlayUserPrefStoreTest() override {}
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<TestingPrefStore> underlay_;
scoped_refptr<OverlayUserPrefStore> overlay_;
};
@@ -128,7 +131,7 @@ TEST_F(OverlayUserPrefStoreTest, GetAndSet) {
// Check that GetMutableValue does not return the dictionary of the underlay.
TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) {
- underlay_->SetValue(overlay_key, base::WrapUnique(new DictionaryValue),
+ underlay_->SetValue(overlay_key, base::MakeUnique<DictionaryValue>(),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
Value* modify = NULL;
@@ -158,12 +161,12 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
const Value* value = NULL;
// Check that underlay first value is reported.
- underlay_->SetValue(regular_key, base::WrapUnique(new Value(42)),
+ underlay_->SetValue(regular_key, base::MakeUnique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(regular_key);
// Check that underlay overwriting is reported.
- underlay_->SetValue(regular_key, base::WrapUnique(new Value(43)),
+ underlay_->SetValue(regular_key, base::MakeUnique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(regular_key);
@@ -172,7 +175,7 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
EXPECT_TRUE(base::Value(43).Equals(value));
// Check that overwriting change in overlay is reported.
- overlay_->SetValue(regular_key, base::WrapUnique(new Value(44)),
+ overlay_->SetValue(regular_key, base::MakeUnique<Value>(44),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(regular_key);
@@ -192,16 +195,16 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
// Check respecting of silence.
- overlay_->SetValueSilently(regular_key, base::WrapUnique(new Value(46)),
+ overlay_->SetValueSilently(regular_key, base::MakeUnique<Value>(46),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- underlay_->SetValue(regular_key, base::WrapUnique(new Value(47)),
+ underlay_->SetValue(regular_key, base::MakeUnique<Value>(47),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(regular_key, base::WrapUnique(new Value(48)),
+ overlay_->SetValue(regular_key, base::MakeUnique<Value>(48),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
}
@@ -209,9 +212,9 @@ 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, base::WrapUnique(new Value(42)),
+ underlay_->SetValue(overlay_key, base::MakeUnique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(overlay_key, base::WrapUnique(new Value(43)),
+ overlay_->SetValue(overlay_key, base::MakeUnique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
const Value* value = nullptr;
@@ -225,16 +228,35 @@ TEST_F(OverlayUserPrefStoreTest, ClearMutableValues) {
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, base::MakeUnique<Value>(42),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ overlay_->SetValueSilently(overlay_key, base::MakeUnique<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(base::Value(43).Equals(value));
+ overlay_->ClearMutableValues();
+
+ // Check that an underlay preference is returned.
+ EXPECT_TRUE(overlay_->GetValue(overlay_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, base::WrapUnique(new Value(42)),
+ underlay_->SetValue(regular_key, base::MakeUnique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(overlay_key, base::WrapUnique(new Value(43)),
+ overlay_->SetValue(overlay_key, base::MakeUnique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- underlay_->SetValue(shared_key, base::WrapUnique(new Value(42)),
+ underlay_->SetValue(shared_key, base::MakeUnique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(shared_key, base::WrapUnique(new Value(43)),
+ overlay_->SetValue(shared_key, base::MakeUnique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
auto values = overlay_->GetValues();
@@ -252,4 +274,8 @@ TEST_F(OverlayUserPrefStoreTest, GetValues) {
EXPECT_TRUE(base::Value(43).Equals(value));
}
+TEST_F(OverlayUserPrefStoreTest, CommitPendingWriteWithCallback) {
+ TestCommitPendingWriteWithCallback(overlay_.get());
+}
+
} // namespace base
diff --git a/chromium/components/prefs/persistent_pref_store.cc b/chromium/components/prefs/persistent_pref_store.cc
new file mode 100644
index 00000000000..9d3a42af6c5
--- /dev/null
+++ b/chromium/components/prefs/persistent_pref_store.cc
@@ -0,0 +1,18 @@
+// 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/prefs/persistent_pref_store.h"
+
+#include <utility>
+
+#include "base/threading/sequenced_task_runner_handle.h"
+
+void PersistentPrefStore::CommitPendingWrite(base::OnceClosure done_callback) {
+ // Default behavior for PersistentPrefStore implementation that don't issue
+ // disk operations: schedule the callback immediately.
+ if (done_callback) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ std::move(done_callback));
+ }
+}
diff --git a/chromium/components/prefs/persistent_pref_store.h b/chromium/components/prefs/persistent_pref_store.h
index 7bc28278be6..2eba3dcaf56 100644
--- a/chromium/components/prefs/persistent_pref_store.h
+++ b/chromium/components/prefs/persistent_pref_store.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/callback.h"
#include "components/prefs/base_prefs_export.h"
#include "components/prefs/writeable_pref_store.h"
@@ -61,8 +62,10 @@ class COMPONENTS_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore {
// Owns |error_delegate|.
virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0;
- // Lands any pending writes to disk.
- virtual void CommitPendingWrite() = 0;
+ // Starts an asynchronous attempt to commit pending writes to disk. Posts a
+ // task to run |done_callback| on the current sequence when disk operations,
+ // if any, are complete (even if they are unsuccessful).
+ virtual void CommitPendingWrite(base::OnceClosure done_callback);
// Schedule a write if there is any lossy data pending. Unlike
// CommitPendingWrite() this does not immediately sync to disk, instead it
diff --git a/chromium/components/prefs/persistent_pref_store_unittest.cc b/chromium/components/prefs/persistent_pref_store_unittest.cc
new file mode 100644
index 00000000000..e2a65416453
--- /dev/null
+++ b/chromium/components/prefs/persistent_pref_store_unittest.cc
@@ -0,0 +1,22 @@
+// 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/prefs/persistent_pref_store.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/sequence_checker_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+void TestCommitPendingWriteWithCallback(PersistentPrefStore* store) {
+ base::RunLoop run_loop;
+ base::SequenceCheckerImpl sequence_checker;
+ store->CommitPendingWrite(base::BindOnce(
+ [](base::SequenceCheckerImpl* sequence_checker, base::RunLoop* run_loop) {
+ EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
+ run_loop->Quit();
+ },
+ base::Unretained(&sequence_checker), base::Unretained(&run_loop)));
+ run_loop.Run();
+}
diff --git a/chromium/components/prefs/persistent_pref_store_unittest.h b/chromium/components/prefs/persistent_pref_store_unittest.h
new file mode 100644
index 00000000000..99442336c7b
--- /dev/null
+++ b/chromium/components/prefs/persistent_pref_store_unittest.h
@@ -0,0 +1,15 @@
+// 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_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_
+#define COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_
+
+class PersistentPrefStore;
+
+// Calls CommitPendingWrite() on |store| with a callback. Verifies that the
+// callback runs on the appropriate sequence. This function is meant to be
+// reused in the tests of various PersistentPrefStore implementations.
+void TestCommitPendingWriteWithCallback(PersistentPrefStore* store);
+
+#endif // COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_
diff --git a/chromium/components/prefs/pref_registry.cc b/chromium/components/prefs/pref_registry.cc
index e80d03c46d3..ccc4a374206 100644
--- a/chromium/components/prefs/pref_registry.cc
+++ b/chromium/components/prefs/pref_registry.cc
@@ -52,6 +52,15 @@ void PrefRegistry::SetDefaultPrefValue(const std::string& pref_name,
defaults_->ReplaceDefaultValue(pref_name, base::WrapUnique(value));
}
+void PrefRegistry::SetDefaultForeignPrefValue(
+ const std::string& path,
+ std::unique_ptr<base::Value> default_value,
+ uint32_t flags) {
+ auto erased = foreign_pref_keys_.erase(path);
+ DCHECK_EQ(1u, erased);
+ RegisterPreference(path, std::move(default_value), flags);
+}
+
void PrefRegistry::RegisterPreference(
const std::string& path,
std::unique_ptr<base::Value> default_value,
@@ -69,3 +78,8 @@ void PrefRegistry::RegisterPreference(
if (flags != NO_REGISTRATION_FLAGS)
registration_flags_[path] = flags;
}
+
+void PrefRegistry::RegisterForeignPref(const std::string& path) {
+ bool inserted = foreign_pref_keys_.insert(path).second;
+ DCHECK(inserted);
+}
diff --git a/chromium/components/prefs/pref_registry.h b/chromium/components/prefs/pref_registry.h
index bdb8af55a04..d88fdfc9bbb 100644
--- a/chromium/components/prefs/pref_registry.h
+++ b/chromium/components/prefs/pref_registry.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <memory>
+#include <set>
#include "base/containers/hash_tables.h"
#include "base/macros.h"
@@ -47,6 +48,9 @@ class COMPONENTS_PREFS_EXPORT PrefRegistry
// This marks the pref as "lossy". There is no strict time guarantee on when
// a lossy pref will be persisted to permanent storage when it is modified.
LOSSY_PREF = 1 << 8,
+
+ // Registering a pref as public allows other services to access it.
+ PUBLIC = 1 << 9,
};
typedef PrefValueMap::const_iterator const_iterator;
@@ -70,6 +74,20 @@ class COMPONENTS_PREFS_EXPORT PrefRegistry
// |pref_name| must be a previously registered preference.
void SetDefaultPrefValue(const std::string& pref_name, base::Value* value);
+ // Registers a pref owned by another service for use with the current service.
+ // The owning service must register that pref with the |PUBLIC| flag.
+ void RegisterForeignPref(const std::string& path);
+
+ // Sets the default value and flags of a previously-registered foreign pref
+ // value.
+ void SetDefaultForeignPrefValue(const std::string& path,
+ std::unique_ptr<base::Value> default_value,
+ uint32_t flags);
+
+ const std::set<std::string>& foreign_pref_keys() const {
+ return foreign_pref_keys_;
+ }
+
protected:
friend class base::RefCounted<PrefRegistry>;
virtual ~PrefRegistry();
@@ -85,6 +103,8 @@ class COMPONENTS_PREFS_EXPORT PrefRegistry
// A map of pref name to a bitmask of PrefRegistrationFlags.
PrefRegistrationFlagsMap registration_flags_;
+ std::set<std::string> foreign_pref_keys_;
+
private:
DISALLOW_COPY_AND_ASSIGN(PrefRegistry);
};
diff --git a/chromium/components/prefs/pref_service.cc b/chromium/components/prefs/pref_service.cc
index dccebf458a2..57eb09d87f4 100644
--- a/chromium/components/prefs/pref_service.cc
+++ b/chromium/components/prefs/pref_service.cc
@@ -80,7 +80,7 @@ PrefService::PrefService(
}
PrefService::~PrefService() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Reset pointers so accesses after destruction reliably crash.
pref_value_store_.reset();
@@ -104,17 +104,21 @@ void PrefService::InitFromStorage(bool async) {
}
void PrefService::CommitPendingWrite() {
- DCHECK(CalledOnValidThread());
- user_pref_store_->CommitPendingWrite();
+ CommitPendingWrite(base::OnceClosure());
+}
+
+void PrefService::CommitPendingWrite(base::OnceClosure done_callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ user_pref_store_->CommitPendingWrite(std::move(done_callback));
}
void PrefService::SchedulePendingLossyWrites() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
user_pref_store_->SchedulePendingLossyWrites();
}
bool PrefService::GetBoolean(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool result = false;
@@ -129,7 +133,7 @@ bool PrefService::GetBoolean(const std::string& path) const {
}
int PrefService::GetInteger(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
int result = 0;
@@ -144,7 +148,7 @@ int PrefService::GetInteger(const std::string& path) const {
}
double PrefService::GetDouble(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
double result = 0.0;
@@ -159,7 +163,7 @@ double PrefService::GetDouble(const std::string& path) const {
}
std::string PrefService::GetString(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string result;
@@ -174,7 +178,7 @@ std::string PrefService::GetString(const std::string& path) const {
}
base::FilePath PrefService::GetFilePath(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::FilePath result;
@@ -196,14 +200,14 @@ bool PrefService::HasPrefPath(const std::string& path) const {
void PrefService::IteratePreferenceValues(
base::RepeatingCallback<void(const std::string& key,
const base::Value& value)> callback) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (const auto& it : *pref_registry_)
callback.Run(it.first, *GetPreferenceValue(it.first));
}
std::unique_ptr<base::DictionaryValue> PrefService::GetPreferenceValues(
IncludeDefaults include_defaults) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<base::DictionaryValue> out(new base::DictionaryValue);
for (const auto& it : *pref_registry_) {
if (include_defaults == INCLUDE_DEFAULTS) {
@@ -220,7 +224,7 @@ std::unique_ptr<base::DictionaryValue> PrefService::GetPreferenceValues(
const PrefService::Preference* PrefService::FindPreference(
const std::string& pref_name) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PreferenceMap::iterator it = prefs_map_.find(pref_name);
if (it != prefs_map_.end())
return &(it->second);
@@ -271,7 +275,7 @@ bool PrefService::IsUserModifiablePreference(
const base::DictionaryValue* PrefService::GetDictionary(
const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const base::Value* value = GetPreferenceValue(path);
if (!value) {
@@ -287,7 +291,7 @@ const base::DictionaryValue* PrefService::GetDictionary(
const base::Value* PrefService::GetUserPrefValue(
const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const Preference* pref = FindPreference(path);
if (!pref) {
@@ -311,13 +315,13 @@ const base::Value* PrefService::GetUserPrefValue(
void PrefService::SetDefaultPrefValue(const std::string& path,
base::Value* value) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pref_registry_->SetDefaultPrefValue(path, value);
}
const base::Value* PrefService::GetDefaultPrefValue(
const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Lookup the preference in the default store.
const base::Value* value = NULL;
if (!pref_registry_->defaults()->GetValue(path, &value)) {
@@ -328,7 +332,7 @@ const base::Value* PrefService::GetDefaultPrefValue(
}
const base::ListValue* PrefService::GetList(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const base::Value* value = GetPreferenceValue(path);
if (!value) {
@@ -360,7 +364,7 @@ PrefRegistry* PrefService::DeprecatedGetPrefRegistry() {
}
void PrefService::ClearPref(const std::string& path) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const Preference* pref = FindPreference(path);
if (!pref) {
@@ -405,7 +409,7 @@ void PrefService::SetInt64(const std::string& path, int64_t value) {
}
int64_t PrefService::GetInt64(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const base::Value* value = GetPreferenceValue(path);
if (!value) {
@@ -427,7 +431,7 @@ void PrefService::SetUint64(const std::string& path, uint64_t value) {
}
uint64_t PrefService::GetUint64(const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const base::Value* value = GetPreferenceValue(path);
if (!value) {
@@ -447,7 +451,7 @@ base::Value* PrefService::GetMutableUserPref(const std::string& path,
base::Value::Type type) {
CHECK(type == base::Value::Type::DICTIONARY ||
type == base::Value::Type::LIST);
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const Preference* pref = FindPreference(path);
if (!pref) {
@@ -478,21 +482,21 @@ base::Value* PrefService::GetMutableUserPref(const std::string& path,
}
void PrefService::ReportUserPrefChanged(const std::string& key) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
user_pref_store_->ReportValueChanged(key, GetWriteFlags(FindPreference(key)));
}
void PrefService::ReportUserPrefChanged(
const std::string& key,
std::set<std::vector<std::string>> path_components) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
user_pref_store_->ReportSubValuesChanged(key, std::move(path_components),
GetWriteFlags(FindPreference(key)));
}
void PrefService::SetUserPrefValue(const std::string& path,
std::unique_ptr<base::Value> new_value) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const Preference* pref = FindPreference(path);
if (!pref) {
@@ -596,7 +600,7 @@ bool PrefService::Preference::IsExtensionModifiable() const {
const base::Value* PrefService::GetPreferenceValue(
const std::string& path) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(battre): This is a check for crbug.com/435208. After analyzing some
// crash dumps it looks like the PrefService is accessed even though it has
diff --git a/chromium/components/prefs/pref_service.h b/chromium/components/prefs/pref_service.h
index a360afa44ac..ca7ad8fa0c3 100644
--- a/chromium/components/prefs/pref_service.h
+++ b/chromium/components/prefs/pref_service.h
@@ -23,7 +23,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "base/values.h"
#include "components/prefs/base_prefs_export.h"
#include "components/prefs/persistent_pref_store.h"
@@ -55,7 +55,7 @@ class ScopedUserPrefUpdateBase;
// Settings and storage accessed through this class represent
// user-selected preferences and information and MUST not be
// extracted, overwritten or modified except through the defined APIs.
-class COMPONENTS_PREFS_EXPORT PrefService : public base::NonThreadSafe {
+class COMPONENTS_PREFS_EXPORT PrefService {
public:
enum PrefInitializationStatus {
INITIALIZATION_STATUS_WAITING,
@@ -177,6 +177,11 @@ class COMPONENTS_PREFS_EXPORT PrefService : public base::NonThreadSafe {
// immediately (basically, during shutdown).
void CommitPendingWrite();
+ // Lands pending writes to disk. This should only be used if we need to save
+ // immediately. |done_callback| will be invoked when changes have been
+ // written.
+ void CommitPendingWrite(base::OnceClosure done_callback);
+
// Schedule a write if there is any lossy data pending. Unlike
// CommitPendingWrite() this does not immediately sync to disk, instead it
// triggers an eventual write if there is lossy data pending and if there
@@ -397,6 +402,8 @@ class COMPONENTS_PREFS_EXPORT PrefService : public base::NonThreadSafe {
// of registered preferences are.
mutable PreferenceMap prefs_map_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(PrefService);
};
diff --git a/chromium/components/prefs/pref_service_factory.cc b/chromium/components/prefs/pref_service_factory.cc
index 7b21ae0f164..8e5d80753e8 100644
--- a/chromium/components/prefs/pref_service_factory.cc
+++ b/chromium/components/prefs/pref_service_factory.cc
@@ -41,14 +41,16 @@ void PrefServiceFactory::SetUserPrefsFile(
}
std::unique_ptr<PrefService> PrefServiceFactory::Create(
- PrefRegistry* pref_registry) {
+ PrefRegistry* pref_registry,
+ std::unique_ptr<PrefValueStore::Delegate> delegate) {
PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
std::unique_ptr<PrefService> pref_service(new PrefService(
pref_notifier,
new PrefValueStore(managed_prefs_.get(), supervised_user_prefs_.get(),
extension_prefs_.get(), command_line_prefs_.get(),
user_prefs_.get(), recommended_prefs_.get(),
- pref_registry->defaults().get(), pref_notifier),
+ pref_registry->defaults().get(), pref_notifier,
+ std::move(delegate)),
user_prefs_.get(), pref_registry, read_error_callback_, async_));
return pref_service;
}
diff --git a/chromium/components/prefs/pref_service_factory.h b/chromium/components/prefs/pref_service_factory.h
index 0427543b460..c9781c812f4 100644
--- a/chromium/components/prefs/pref_service_factory.h
+++ b/chromium/components/prefs/pref_service_factory.h
@@ -12,6 +12,7 @@
#include "components/prefs/persistent_pref_store.h"
#include "components/prefs/pref_registry.h"
#include "components/prefs/pref_store.h"
+#include "components/prefs/pref_value_store.h"
class PrefService;
@@ -67,7 +68,9 @@ class COMPONENTS_PREFS_EXPORT PrefServiceFactory {
// Creates a PrefService object initialized with the parameters from
// this factory.
- std::unique_ptr<PrefService> Create(PrefRegistry* registry);
+ std::unique_ptr<PrefService> Create(
+ PrefRegistry* registry,
+ std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr);
protected:
scoped_refptr<PrefStore> managed_prefs_;
diff --git a/chromium/components/prefs/pref_value_store.cc b/chromium/components/prefs/pref_value_store.cc
index 4c7a4e5d9cf..da58b231840 100644
--- a/chromium/components/prefs/pref_value_store.cc
+++ b/chromium/components/prefs/pref_value_store.cc
@@ -55,9 +55,11 @@ PrefValueStore::PrefValueStore(PrefStore* managed_prefs,
PrefStore* user_prefs,
PrefStore* recommended_prefs,
PrefStore* default_prefs,
- PrefNotifier* pref_notifier)
+ PrefNotifier* pref_notifier,
+ std::unique_ptr<Delegate> delegate)
: pref_notifier_(pref_notifier),
- initialization_failed_(false) {
+ initialization_failed_(false),
+ delegate_(std::move(delegate)) {
InitPrefStore(MANAGED_STORE, managed_prefs);
InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs);
InitPrefStore(EXTENSION_STORE, extension_prefs);
@@ -67,6 +69,11 @@ PrefValueStore::PrefValueStore(PrefStore* managed_prefs,
InitPrefStore(DEFAULT_STORE, default_prefs);
CheckInitializationCompleted();
+ if (delegate_) {
+ delegate_->Init(managed_prefs, supervised_user_prefs, extension_prefs,
+ command_line_prefs, user_prefs, recommended_prefs,
+ default_prefs, pref_notifier);
+ }
}
PrefValueStore::~PrefValueStore() {}
@@ -79,7 +86,8 @@ PrefValueStore* PrefValueStore::CloneAndSpecialize(
PrefStore* user_prefs,
PrefStore* recommended_prefs,
PrefStore* default_prefs,
- PrefNotifier* pref_notifier) {
+ PrefNotifier* pref_notifier,
+ std::unique_ptr<Delegate> delegate) {
DCHECK(pref_notifier);
if (!managed_prefs)
managed_prefs = GetPrefStore(MANAGED_STORE);
@@ -96,9 +104,10 @@ PrefValueStore* PrefValueStore::CloneAndSpecialize(
if (!default_prefs)
default_prefs = GetPrefStore(DEFAULT_STORE);
- return new PrefValueStore(
- managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs,
- user_prefs, recommended_prefs, default_prefs, pref_notifier);
+ return new PrefValueStore(managed_prefs, supervised_user_prefs,
+ extension_prefs, command_line_prefs, user_prefs,
+ recommended_prefs, default_prefs, pref_notifier,
+ std::move(delegate));
}
void PrefValueStore::set_callback(const PrefChangedCallback& callback) {
@@ -185,6 +194,8 @@ bool PrefValueStore::PrefValueExtensionModifiable(
void PrefValueStore::UpdateCommandLinePrefStore(PrefStore* command_line_prefs) {
InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
+ if (delegate_)
+ delegate_->UpdateCommandLinePrefStore(command_line_prefs);
}
bool PrefValueStore::PrefValueInStore(
diff --git a/chromium/components/prefs/pref_value_store.h b/chromium/components/prefs/pref_value_store.h
index eeab3062fbe..9fb9ed10996 100644
--- a/chromium/components/prefs/pref_value_store.h
+++ b/chromium/components/prefs/pref_value_store.h
@@ -17,7 +17,9 @@
#include "components/prefs/base_prefs_export.h"
#include "components/prefs/pref_store.h"
+class PersistentPrefStore;
class PrefNotifier;
+class PrefRegistry;
class PrefStore;
// The PrefValueStore manages various sources of values for Preferences
@@ -31,6 +33,32 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore {
public:
typedef base::Callback<void(const std::string&)> PrefChangedCallback;
+ // Delegate used to observe certain events in the |PrefValueStore|'s lifetime.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called by the PrefValueStore constructor with the PrefStores passed to
+ // it.
+ virtual void Init(PrefStore* managed_prefs,
+ PrefStore* supervised_user_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs,
+ PrefStore* default_prefs,
+ PrefNotifier* pref_notifier) = 0;
+
+ virtual void InitIncognitoUnderlay(
+ PersistentPrefStore* incognito_user_prefs_underlay) = 0;
+
+ virtual void InitPrefRegistry(PrefRegistry* pref_registry) = 0;
+
+ // Called whenever PrefValueStore::UpdateCommandLinePrefStore is called,
+ // with the same argument.
+ virtual void UpdateCommandLinePrefStore(PrefStore* command_line_prefs) = 0;
+ };
+
// PrefStores must be listed here in order from highest to lowest priority.
// MANAGED contains all managed preference values that are provided by
// mandatory policies (e.g. Windows Group Policy or cloud policy).
@@ -77,19 +105,24 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore {
PrefStore* user_prefs,
PrefStore* recommended_prefs,
PrefStore* default_prefs,
- PrefNotifier* pref_notifier);
+ PrefNotifier* pref_notifier,
+ std::unique_ptr<Delegate> delegate = nullptr);
virtual ~PrefValueStore();
// Creates a clone of this PrefValueStore with PrefStores overwritten
// by the parameters passed, if unequal NULL.
- PrefValueStore* CloneAndSpecialize(PrefStore* managed_prefs,
- PrefStore* supervised_user_prefs,
- PrefStore* extension_prefs,
- PrefStore* command_line_prefs,
- PrefStore* user_prefs,
- PrefStore* recommended_prefs,
- PrefStore* default_prefs,
- PrefNotifier* pref_notifier);
+ //
+ // The new PrefValueStore is passed the |delegate| in its constructor.
+ PrefValueStore* CloneAndSpecialize(
+ PrefStore* managed_prefs,
+ PrefStore* supervised_user_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs,
+ PrefStore* default_prefs,
+ PrefNotifier* pref_notifier,
+ std::unique_ptr<Delegate> delegate = nullptr);
// A PrefValueStore can have exactly one callback that is directly
// notified of preferences changing in the store. This does not
@@ -255,6 +288,9 @@ class COMPONENTS_PREFS_EXPORT PrefValueStore {
// True if not all of the PrefStores were initialized successfully.
bool initialization_failed_;
+ // Might be null.
+ std::unique_ptr<Delegate> delegate_;
+
DISALLOW_COPY_AND_ASSIGN(PrefValueStore);
};
diff --git a/chromium/components/prefs/scoped_user_pref_update.cc b/chromium/components/prefs/scoped_user_pref_update.cc
index dfba3349a2d..bec88eef228 100644
--- a/chromium/components/prefs/scoped_user_pref_update.cc
+++ b/chromium/components/prefs/scoped_user_pref_update.cc
@@ -13,15 +13,16 @@ namespace subtle {
ScopedUserPrefUpdateBase::ScopedUserPrefUpdateBase(PrefService* service,
const std::string& path)
: service_(service), path_(path), value_(NULL) {
- DCHECK(service_->CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(service_->sequence_checker_);
}
ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Notify();
}
base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!value_)
value_ = service_->GetMutableUserPref(path_, type);
return value_;
diff --git a/chromium/components/prefs/scoped_user_pref_update.h b/chromium/components/prefs/scoped_user_pref_update.h
index 29859d65b33..f8f45fc6841 100644
--- a/chromium/components/prefs/scoped_user_pref_update.h
+++ b/chromium/components/prefs/scoped_user_pref_update.h
@@ -11,7 +11,7 @@
#include <string>
#include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "base/values.h"
#include "components/prefs/base_prefs_export.h"
#include "components/prefs/pref_service.h"
@@ -31,8 +31,7 @@ namespace subtle {
// We need this base class mostly for making it a friend of PrefService
// and getting access to PrefService::GetMutableUserPref and
// PrefService::ReportUserPrefChanged.
-class COMPONENTS_PREFS_EXPORT ScopedUserPrefUpdateBase
- : public base::NonThreadSafe {
+class COMPONENTS_PREFS_EXPORT ScopedUserPrefUpdateBase {
protected:
ScopedUserPrefUpdateBase(PrefService* service, const std::string& path);
@@ -54,6 +53,8 @@ class COMPONENTS_PREFS_EXPORT ScopedUserPrefUpdateBase
// Cache of value from user pref store (set between Get() and Notify() calls).
base::Value* value_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdateBase);
};
diff --git a/chromium/components/prefs/testing_pref_store.cc b/chromium/components/prefs/testing_pref_store.cc
index efdc83fcc77..f214da8f2ad 100644
--- a/chromium/components/prefs/testing_pref_store.cc
+++ b/chromium/components/prefs/testing_pref_store.cc
@@ -9,6 +9,7 @@
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -98,7 +99,10 @@ void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
NotifyInitializationCompleted();
}
-void TestingPrefStore::CommitPendingWrite() { committed_ = true; }
+void TestingPrefStore::CommitPendingWrite(base::OnceClosure done_callback) {
+ committed_ = true;
+ PersistentPrefStore::CommitPendingWrite(std::move(done_callback));
+}
void TestingPrefStore::SchedulePendingLossyWrites() {}
diff --git a/chromium/components/prefs/testing_pref_store.h b/chromium/components/prefs/testing_pref_store.h
index 4e0afa46a0c..cc40c1d0da9 100644
--- a/chromium/components/prefs/testing_pref_store.h
+++ b/chromium/components/prefs/testing_pref_store.h
@@ -45,7 +45,7 @@ class TestingPrefStore : public PersistentPrefStore {
PrefReadError GetReadError() const override;
PersistentPrefStore::PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
- void CommitPendingWrite() override;
+ void CommitPendingWrite(base::OnceClosure done_callback) override;
void SchedulePendingLossyWrites() override;
// Marks the store as having completed initialization.
diff --git a/chromium/components/previews/OWNERS b/chromium/components/previews/OWNERS
index 2783dea1c6e..9a86a9f4bc3 100644
--- a/chromium/components/previews/OWNERS
+++ b/chromium/components/previews/OWNERS
@@ -1 +1,3 @@
file://components/data_reduction_proxy/OWNERS
+
+# COMPONENT: UI>Browser>Previews \ No newline at end of file
diff --git a/chromium/components/previews/core/previews_black_list.h b/chromium/components/previews/core/previews_black_list.h
index f45aa7770d0..9d8c98500d2 100644
--- a/chromium/components/previews/core/previews_black_list.h
+++ b/chromium/components/previews/core/previews_black_list.h
@@ -51,6 +51,9 @@ enum class PreviewsEligibilityReason {
NETWORK_NOT_SLOW = 7,
// If the page was reloaded, the user should not be shown a stale preview.
RELOAD_DISALLOWED = 8,
+ // The host is explicitly blacklisted by the server, so the user was not shown
+ // a preview.
+ HOST_BLACKLISTED_BY_SERVER = 9,
LAST = 9,
};
diff --git a/chromium/components/previews/core/previews_decider.h b/chromium/components/previews/core/previews_decider.h
index 2c518fe3d94..d3cbf9b59e6 100644
--- a/chromium/components/previews/core/previews_decider.h
+++ b/chromium/components/previews/core/previews_decider.h
@@ -7,6 +7,7 @@
#include "components/previews/core/previews_experiments.h"
+#include "base/strings/string_piece.h"
#include "net/nqe/effective_connection_type.h"
namespace net {
@@ -25,11 +26,11 @@ class PreviewsDecider {
virtual bool ShouldAllowPreviewAtECT(
const net::URLRequest& request,
PreviewsType type,
- net::EffectiveConnectionType effective_connection_type_threshold)
- const = 0;
+ net::EffectiveConnectionType effective_connection_type_threshold,
+ const std::vector<std::string>& host_blacklist_from_server) const = 0;
// Same as ShouldAllowPreviewAtECT, but uses the previews default
- // EffectiveConnectionType.
+ // EffectiveConnectionType and no blacklisted hosts from the server.
virtual bool ShouldAllowPreview(const net::URLRequest& request,
PreviewsType type) const = 0;
diff --git a/chromium/components/previews/core/previews_experiments.cc b/chromium/components/previews/core/previews_experiments.cc
index ccab155619a..b654c75a2ff 100644
--- a/chromium/components/previews/core/previews_experiments.cc
+++ b/chromium/components/previews/core/previews_experiments.cc
@@ -9,6 +9,7 @@
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/previews/core/previews_features.h"
@@ -173,6 +174,13 @@ net::EffectiveConnectionType EffectiveConnectionTypeThresholdForClientLoFi() {
net::EFFECTIVE_CONNECTION_TYPE_2G);
}
+std::vector<std::string> GetBlackListedHostsForClientLoFiFieldTrial() {
+ return base::SplitString(
+ base::GetFieldTrialParamValue(kClientLoFiExperimentName,
+ "short_host_blacklist"),
+ ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+}
+
} // namespace params
std::string GetStringNameForType(PreviewsType type) {
diff --git a/chromium/components/previews/core/previews_experiments.h b/chromium/components/previews/core/previews_experiments.h
index b7dd59954a5..509269c11be 100644
--- a/chromium/components/previews/core/previews_experiments.h
+++ b/chromium/components/previews/core/previews_experiments.h
@@ -58,16 +58,19 @@ bool IsOfflinePreviewsEnabled();
// The blacklist version for offline previews.
int OfflinePreviewsVersion();
-// Whether Client LoFi previews are enabled.
+// Whether Client Lo-Fi previews are enabled.
bool IsClientLoFiEnabled();
-// The blacklist version for Client LoFi previews.
+// The blacklist version for Client Lo-Fi previews.
int ClientLoFiVersion();
-// The threshold of EffectiveConnectionType above which Client LoFi previews
+// The threshold of EffectiveConnectionType above which Client Lo-Fi previews
// should not be served.
net::EffectiveConnectionType EffectiveConnectionTypeThresholdForClientLoFi();
+// Returns the hosts that are blacklisted by the Client Lo-Fi field trial.
+std::vector<std::string> GetBlackListedHostsForClientLoFiFieldTrial();
+
} // namespace params
enum class PreviewsType {
diff --git a/chromium/components/previews/core/previews_experiments_unittest.cc b/chromium/components/previews/core/previews_experiments_unittest.cc
index bc5efb819cf..eb71c6d52e4 100644
--- a/chromium/components/previews/core/previews_experiments_unittest.cc
+++ b/chromium/components/previews/core/previews_experiments_unittest.cc
@@ -144,6 +144,8 @@ TEST(PreviewsExperimentsTest, TestEnableClientLoFiWithDefaultParams) {
EXPECT_EQ(0, params::ClientLoFiVersion());
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G,
params::EffectiveConnectionTypeThresholdForClientLoFi());
+ EXPECT_EQ(std::vector<std::string>(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial());
}
TEST(PreviewsExperimentsTest, TestEnableClientLoFiWithCustomParams) {
@@ -151,7 +153,9 @@ TEST(PreviewsExperimentsTest, TestEnableClientLoFiWithCustomParams) {
// Set some custom params for Client LoFi.
std::map<std::string, std::string> custom_params = {
- {"version", "10"}, {"max_allowed_effective_connection_type", "3G"},
+ {"version", "10"},
+ {"max_allowed_effective_connection_type", "3G"},
+ {"short_host_blacklist", "some,hosts, to-blacklist ,,"},
};
EXPECT_TRUE(base::AssociateFieldTrialParams(kClientLoFiFieldTrial, kEnabled,
custom_params));
@@ -162,6 +166,8 @@ TEST(PreviewsExperimentsTest, TestEnableClientLoFiWithCustomParams) {
EXPECT_EQ(10, params::ClientLoFiVersion());
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_3G,
params::EffectiveConnectionTypeThresholdForClientLoFi());
+ EXPECT_EQ(std::vector<std::string>({"some", "hosts", "to-blacklist"}),
+ params::GetBlackListedHostsForClientLoFiFieldTrial());
variations::testing::ClearAllVariationParams();
}
diff --git a/chromium/components/previews/core/previews_features.cc b/chromium/components/previews/core/previews_features.cc
index d88577c41f2..01f15dae36f 100644
--- a/chromium/components/previews/core/previews_features.cc
+++ b/chromium/components/previews/core/previews_features.cc
@@ -15,5 +15,9 @@ const base::Feature kOfflinePreviews{"OfflinePreviews",
const base::Feature kClientLoFi{"ClientLoFi",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables the Stale Previews timestamp on Previews infobars.
+const base::Feature kStalePreviewsTimestamp{"StalePreviewsTimestamp",
+ base::FEATURE_ENABLED_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 2225bfd5e41..e83f96059ea 100644
--- a/chromium/components/previews/core/previews_features.h
+++ b/chromium/components/previews/core/previews_features.h
@@ -12,6 +12,7 @@ namespace features {
extern const base::Feature kOfflinePreviews;
extern const base::Feature kClientLoFi;
+extern const base::Feature kStalePreviewsTimestamp;
} // namespace features
} // namespace previews
diff --git a/chromium/components/previews/core/previews_io_data.cc b/chromium/components/previews/core/previews_io_data.cc
index d2f8ed1361d..232f71cb225 100644
--- a/chromium/components/previews/core/previews_io_data.cc
+++ b/chromium/components/previews/core/previews_io_data.cc
@@ -4,6 +4,8 @@
#include "components/previews/core/previews_io_data.h"
+#include <algorithm>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
@@ -108,13 +110,15 @@ void PreviewsIOData::ClearBlackList(base::Time begin_time,
bool PreviewsIOData::ShouldAllowPreview(const net::URLRequest& request,
PreviewsType type) const {
return ShouldAllowPreviewAtECT(
- request, type, params::DefaultEffectiveConnectionTypeThreshold());
+ request, type, params::DefaultEffectiveConnectionTypeThreshold(),
+ std::vector<std::string>());
}
bool PreviewsIOData::ShouldAllowPreviewAtECT(
const net::URLRequest& request,
PreviewsType type,
- net::EffectiveConnectionType effective_connection_type_threshold) const {
+ net::EffectiveConnectionType effective_connection_type_threshold,
+ const std::vector<std::string>& host_blacklist_from_server) const {
if (is_enabled_callback_.is_null() || !previews_black_list_) {
LogPreviewsEligibilityReason(
PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE, type);
@@ -131,6 +135,7 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
LogPreviewsEligibilityReason(status, type);
return false;
}
+
net::NetworkQualityEstimator* network_quality_estimator =
request.context()->network_quality_estimator();
if (!network_quality_estimator ||
@@ -157,6 +162,14 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
return false;
}
+ if (std::find(host_blacklist_from_server.begin(),
+ host_blacklist_from_server.end(), request.url().host_piece()) !=
+ host_blacklist_from_server.end()) {
+ LogPreviewsEligibilityReason(
+ PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER, type);
+ return false;
+ }
+
LogPreviewsEligibilityReason(PreviewsEligibilityReason::ALLOWED, type);
return true;
}
diff --git a/chromium/components/previews/core/previews_io_data.h b/chromium/components/previews/core/previews_io_data.h
index f44fa304736..42e73495de8 100644
--- a/chromium/components/previews/core/previews_io_data.h
+++ b/chromium/components/previews/core/previews_io_data.h
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
+#include <vector>
#include "base/callback.h"
#include "base/macros.h"
@@ -64,7 +65,8 @@ class PreviewsIOData : public PreviewsDecider {
bool ShouldAllowPreviewAtECT(
const net::URLRequest& request,
PreviewsType type,
- net::EffectiveConnectionType effective_connection_type_threshold)
+ net::EffectiveConnectionType effective_connection_type_threshold,
+ const std::vector<std::string>& host_blacklist_from_server)
const override;
protected:
diff --git a/chromium/components/previews/core/previews_io_data_unittest.cc b/chromium/components/previews/core/previews_io_data_unittest.cc
index 2de0ade9039..101092284bd 100644
--- a/chromium/components/previews/core/previews_io_data_unittest.cc
+++ b/chromium/components/previews/core/previews_io_data_unittest.cc
@@ -288,7 +288,7 @@ TEST_F(PreviewsIODataTest, TestAllowLitePageWhenNetworkQualityFast) {
base::HistogramTester histogram_tester;
EXPECT_TRUE(io_data()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LITE_PAGE,
- net::EFFECTIVE_CONNECTION_TYPE_4G));
+ net::EFFECTIVE_CONNECTION_TYPE_4G, std::vector<std::string>()));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LitePage",
static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
@@ -353,7 +353,8 @@ TEST_F(PreviewsIODataTest, ClientLoFiDisallowedByDefault) {
base::HistogramTester histogram_tester;
EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
- params::EffectiveConnectionTypeThresholdForClientLoFi()));
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial()));
histogram_tester.ExpectTotalCount("Previews.EligibilityReason.LoFi", 0);
}
@@ -364,7 +365,8 @@ TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenFieldTrialDisabled) {
base::HistogramTester histogram_tester;
EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
- params::EffectiveConnectionTypeThresholdForClientLoFi()));
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial()));
histogram_tester.ExpectTotalCount("Previews.EligibilityReason.LoFi", 0);
}
@@ -378,7 +380,8 @@ TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenNetworkQualityUnavailable) {
base::HistogramTester histogram_tester;
EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
- params::EffectiveConnectionTypeThresholdForClientLoFi()));
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial()));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
static_cast<int>(PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE),
@@ -396,7 +399,8 @@ TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenNetworkFast) {
base::HistogramTester histogram_tester;
EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
- params::EffectiveConnectionTypeThresholdForClientLoFi()));
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial()));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
static_cast<int>(PreviewsEligibilityReason::NETWORK_NOT_SLOW), 1);
@@ -413,7 +417,8 @@ TEST_F(PreviewsIODataTest, ClientLoFiAllowed) {
base::HistogramTester histogram_tester;
EXPECT_TRUE(io_data()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
- params::EffectiveConnectionTypeThresholdForClientLoFi()));
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial()));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
@@ -433,12 +438,56 @@ TEST_F(PreviewsIODataTest, ClientLoFiAllowedOnReload) {
base::HistogramTester histogram_tester;
EXPECT_TRUE(io_data()->ShouldAllowPreviewAtECT(
*request, PreviewsType::LOFI,
- params::EffectiveConnectionTypeThresholdForClientLoFi()));
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial()));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
}
+TEST_F(PreviewsIODataTest, ClientLoFiObeysHostBlackListFromServer) {
+ InitializeUIService();
+ CreateFieldTrialWithParams("PreviewsClientLoFi", "Enabled",
+ {{"max_allowed_effective_connection_type", "2G"},
+ {"short_host_blacklist", "foo.com, ,bar.net "}});
+
+ network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+ const struct {
+ const char* url;
+ bool expected_client_lofi_allowed;
+ } tests[] = {
+ {"http://example.com", true}, {"http://foo.com", false},
+ {"https://foo.com", false}, {"http://www.foo.com", true},
+ {"http://m.foo.com", true}, {"http://foo.net", true},
+ {"http://foo.com/example", false}, {"http://bar.net", false},
+ {"http://bar.net.tld", true},
+ };
+
+ for (const auto& test : tests) {
+ base::HistogramTester histogram_tester;
+
+ std::unique_ptr<net::URLRequest> request =
+ context()->CreateRequest(GURL(test.url), net::DEFAULT_PRIORITY, nullptr,
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ EXPECT_EQ(test.expected_client_lofi_allowed,
+ io_data()->ShouldAllowPreviewAtECT(
+ *request, PreviewsType::LOFI,
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial()));
+
+ histogram_tester.ExpectUniqueSample(
+ "Previews.EligibilityReason.LoFi",
+ static_cast<int>(
+ test.expected_client_lofi_allowed
+ ? PreviewsEligibilityReason::ALLOWED
+ : PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER),
+ 1);
+ }
+}
+
} // namespace
} // namespace previews
diff --git a/chromium/components/printing/browser/print_manager.cc b/chromium/components/printing/browser/print_manager.cc
index 1094a260bb6..6dbe709f01f 100644
--- a/chromium/components/printing/browser/print_manager.cc
+++ b/chromium/components/printing/browser/print_manager.cc
@@ -50,20 +50,20 @@ void PrintManager::OnPrintingFailed(int cookie) {
return;
}
#if defined(OS_ANDROID)
- PdfWritingDone(false);
+ PdfWritingDone(0);
#endif
}
void PrintManager::PrintingRenderFrameDeleted() {
#if defined(OS_ANDROID)
- PdfWritingDone(false);
+ PdfWritingDone(0);
#endif
}
#if defined(OS_ANDROID)
-void PrintManager::PdfWritingDone(bool result) {
+void PrintManager::PdfWritingDone(int page_count) {
if (!pdf_writing_done_callback_.is_null())
- pdf_writing_done_callback_.Run(file_descriptor().fd, result);
+ pdf_writing_done_callback_.Run(file_descriptor().fd, page_count);
// Invalidate the file descriptor so it doesn't get reused.
file_descriptor_ = base::FileDescriptor(-1, false);
}
diff --git a/chromium/components/printing/browser/print_manager.h b/chromium/components/printing/browser/print_manager.h
index 8fdba958d58..1635d36825c 100644
--- a/chromium/components/printing/browser/print_manager.h
+++ b/chromium/components/printing/browser/print_manager.h
@@ -22,9 +22,9 @@ class PrintManager : public content::WebContentsObserver {
#if defined(OS_ANDROID)
// TODO(timvolodine): consider introducing PrintManagerAndroid (crbug/500960)
- typedef base::Callback<void(int, bool)> PdfWritingDoneCallback;
+ using PdfWritingDoneCallback = base::Callback<void(int, int)>;
- void PdfWritingDone(bool result);
+ void PdfWritingDone(int page_count);
// Sets the file descriptor into which the PDF will be written.
void set_file_descriptor(const base::FileDescriptor& file_descriptor) {
diff --git a/chromium/components/printing/common/print_messages.h b/chromium/components/printing/common/print_messages.h
index 32b0451b60d..fd395915885 100644
--- a/chromium/components/printing/common/print_messages.h
+++ b/chromium/components/printing/common/print_messages.h
@@ -398,9 +398,10 @@ IPC_SYNC_MESSAGE_CONTROL1_2(PrintHostMsg_AllocateTempFileForPrinting,
int /* render_frame_id */,
base::FileDescriptor /* temp file fd */,
int /* fd in browser*/)
-IPC_MESSAGE_CONTROL2(PrintHostMsg_TempFileForPrintingWritten,
+IPC_MESSAGE_CONTROL3(PrintHostMsg_TempFileForPrintingWritten,
int /* render_frame_id */,
- int /* fd in browser */)
+ int /* fd in browser */,
+ int /* page count */)
#endif // defined(OS_ANDROID)
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chromium/components/printing/renderer/print_web_view_helper.cc b/chromium/components/printing/renderer/print_web_view_helper.cc
index 829b2dc1c65..3c9dfc0d1ed 100644
--- a/chromium/components/printing/renderer/print_web_view_helper.cc
+++ b/chromium/components/printing/renderer/print_web_view_helper.cc
@@ -99,7 +99,7 @@ const char kPageLoadScriptFormat[] =
const char kPageSetupScriptFormat[] = "setup(%s);";
-void ExecuteScript(blink::WebFrame* frame,
+void ExecuteScript(blink::WebLocalFrame* frame,
const char* script_format,
const base::Value& parameters) {
std::string json;
@@ -565,7 +565,7 @@ void PrintWebViewHelper::PrintHeaderAndFooter(
blink::WebCanvas* canvas,
int page_number,
int total_pages,
- const blink::WebFrame& source_frame,
+ const blink::WebLocalFrame& source_frame,
float webkit_scale_factor,
const PageSizeMargins& page_layout,
const PrintMsg_Print_Params& params) {
@@ -590,11 +590,10 @@ void PrintWebViewHelper::PrintHeaderAndFooter(
}
};
HeaderAndFooterClient frame_client;
- blink::WebLocalFrame* frame = blink::WebLocalFrame::Create(
- blink::WebTreeScopeType::kDocument, &frame_client, nullptr, nullptr);
- web_view->SetMainFrame(frame);
+ blink::WebLocalFrame* frame = blink::WebLocalFrame::CreateMainFrame(
+ web_view, &frame_client, nullptr, nullptr);
blink::WebWidgetClient web_widget_client;
- blink::WebFrameWidget::Create(&web_widget_client, web_view, frame);
+ blink::WebFrameWidget::Create(&web_widget_client, frame);
base::Value html(
base::UTF8ToUTF16(ResourceBundle::GetSharedInstance().GetRawDataResource(
@@ -628,7 +627,7 @@ void PrintWebViewHelper::PrintHeaderAndFooter(
}
// static - Not anonymous so that platform implementations can use it.
-float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame,
+float PrintWebViewHelper::RenderPageContent(blink::WebLocalFrame* frame,
int page_number,
const gfx::Rect& canvas_area,
const gfx::Rect& content_area,
@@ -690,7 +689,9 @@ class PrepareFrameAndViewForPrint : public blink::WebViewClient,
const blink::WebFrameOwnerProperties& frame_owner_properties) override;
void FrameDetached(blink::WebLocalFrame* frame,
DetachType detach_type) override;
- std::unique_ptr<blink::WebURLLoader> CreateURLLoader() override;
+ std::unique_ptr<blink::WebURLLoader> CreateURLLoader(
+ const blink::WebURLRequest& request,
+ base::SingleThreadTaskRunner* task_runner) override;
void CallOnReady();
void ResizeForPrinting();
@@ -769,8 +770,10 @@ void PrepareFrameAndViewForPrint::ResizeForPrinting() {
// Backup size and offset if it's a local frame.
blink::WebView* web_view = frame_.view();
if (blink::WebFrame* web_frame = web_view->MainFrame()) {
+ // TODO(lukasza, weili): Support restoring scroll offset of a remote main
+ // frame - https://crbug.com/734815.
if (web_frame->IsWebLocalFrame())
- prev_scroll_offset_ = web_frame->GetScrollOffset();
+ prev_scroll_offset_ = web_frame->ToWebLocalFrame()->GetScrollOffset();
}
prev_view_size_ = web_view->Size();
@@ -815,10 +818,9 @@ void PrepareFrameAndViewForPrint::CopySelection(
blink::WebView::Create(this, blink::kWebPageVisibilityStateVisible);
owns_web_view_ = true;
content::RenderView::ApplyWebPreferences(prefs, web_view);
- blink::WebLocalFrame* main_frame = blink::WebLocalFrame::Create(
- blink::WebTreeScopeType::kDocument, this, nullptr, nullptr);
- web_view->SetMainFrame(main_frame);
- blink::WebFrameWidget::Create(this, web_view, main_frame);
+ blink::WebLocalFrame* main_frame =
+ blink::WebLocalFrame::CreateMainFrame(web_view, this, nullptr, nullptr);
+ blink::WebFrameWidget::Create(this, main_frame);
frame_.Reset(web_view->MainFrame()->ToWebLocalFrame());
node_to_print_.Reset();
@@ -863,9 +865,11 @@ void PrepareFrameAndViewForPrint::FrameDetached(blink::WebLocalFrame* frame,
}
std::unique_ptr<blink::WebURLLoader>
-PrepareFrameAndViewForPrint::CreateURLLoader() {
+PrepareFrameAndViewForPrint::CreateURLLoader(
+ const blink::WebURLRequest& request,
+ base::SingleThreadTaskRunner* task_runner) {
// TODO(yhirano): Stop using Platform::CreateURLLoader() here.
- return blink::Platform::Current()->CreateURLLoader();
+ return blink::Platform::Current()->CreateURLLoader(request, task_runner);
}
void PrepareFrameAndViewForPrint::CallOnReady() {
@@ -879,8 +883,10 @@ void PrepareFrameAndViewForPrint::RestoreSize() {
blink::WebView* web_view = frame_.GetFrame()->View();
web_view->Resize(prev_view_size_);
if (blink::WebFrame* web_frame = web_view->MainFrame()) {
+ // TODO(lukasza, weili): Support restoring scroll offset of a remote main
+ // frame - https://crbug.com/734815.
if (web_frame->IsWebLocalFrame())
- web_frame->SetScrollOffset(prev_scroll_offset_);
+ web_frame->ToWebLocalFrame()->SetScrollOffset(prev_scroll_offset_);
}
}
@@ -1181,13 +1187,10 @@ void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) {
if (!UpdatePrintSettings(print_preview_context_.source_frame(),
print_preview_context_.source_node(), settings)) {
if (print_preview_context_.last_error() != PREVIEW_ERROR_BAD_SETTING) {
- Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
- routing_id(), print_pages_params_
- ? print_pages_params_->params.document_cookie
- : 0));
- notify_browser_of_print_failure_ = false; // Already sent.
+ DidFinishPrinting(INVALID_SETTINGS);
+ } else {
+ DidFinishPrinting(FAIL_PREVIEW);
}
- DidFinishPrinting(FAIL_PREVIEW);
return;
}
@@ -1551,6 +1554,8 @@ void PrintWebViewHelper::Print(blink::WebLocalFrame* frame,
#endif // BUILDFLAG(ENABLE_BASIC_PRINTING)
void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) {
+ int cookie =
+ print_pages_params_ ? print_pages_params_->params.document_cookie : 0;
switch (result) {
case OK:
break;
@@ -1561,23 +1566,27 @@ void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) {
case FAIL_PRINT:
if (notify_browser_of_print_failure_ && print_pages_params_) {
- int cookie = print_pages_params_->params.document_cookie;
Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie));
}
break;
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
case FAIL_PREVIEW:
- int cookie =
- print_pages_params_ ? print_pages_params_->params.document_cookie : 0;
- if (notify_browser_of_print_failure_) {
- LOG(ERROR) << "CreatePreviewDocument failed";
- Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
- } else {
- Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
+ if (!is_print_ready_metafile_sent_) {
+ if (notify_browser_of_print_failure_) {
+ LOG(ERROR) << "CreatePreviewDocument failed";
+ Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
+ } else {
+ Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
+ }
}
print_preview_context_.Failed(notify_browser_of_print_failure_);
break;
+ case INVALID_SETTINGS:
+ Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(routing_id(),
+ cookie));
+ print_preview_context_.Failed(false);
+ break;
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
}
prep_frame_view_.reset();
diff --git a/chromium/components/printing/renderer/print_web_view_helper.h b/chromium/components/printing/renderer/print_web_view_helper.h
index c4339d474ad..fc5e9648a33 100644
--- a/chromium/components/printing/renderer/print_web_view_helper.h
+++ b/chromium/components/printing/renderer/print_web_view_helper.h
@@ -44,7 +44,6 @@ class DictionaryValue;
}
namespace blink {
-class WebFrame;
class WebLocalFrame;
class WebView;
}
@@ -155,6 +154,7 @@ class PrintWebViewHelper
FAIL_PRINT,
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
FAIL_PREVIEW,
+ INVALID_SETTINGS,
#endif
};
@@ -322,7 +322,7 @@ class PrintWebViewHelper
// |page_number| is zero-based.
// When method is called, canvas should be setup to draw to |canvas_area|
// with |scale_factor|.
- static float RenderPageContent(blink::WebFrame* frame,
+ static float RenderPageContent(blink::WebLocalFrame* frame,
int page_number,
const gfx::Rect& canvas_area,
const gfx::Rect& content_area,
@@ -354,7 +354,7 @@ class PrintWebViewHelper
static void PrintHeaderAndFooter(blink::WebCanvas* canvas,
int page_number,
int total_pages,
- const blink::WebFrame& source_frame,
+ const blink::WebLocalFrame& source_frame,
float webkit_scale_factor,
const PageSizeMargins& page_layout_in_points,
const PrintMsg_Print_Params& params);
diff --git a/chromium/components/printing/renderer/print_web_view_helper_linux.cc b/chromium/components/printing/renderer/print_web_view_helper_linux.cc
index 910c471105f..024ff6a1755 100644
--- a/chromium/components/printing/renderer/print_web_view_helper_linux.cc
+++ b/chromium/components/printing/renderer/print_web_view_helper_linux.cc
@@ -76,8 +76,8 @@ bool PrintWebViewHelper::PrintPagesNative(blink::WebLocalFrame* frame,
return false;
// Tell the browser we've finished writing the file.
- Send(new PrintHostMsg_TempFileForPrintingWritten(routing_id(),
- sequence_number));
+ Send(new PrintHostMsg_TempFileForPrintingWritten(
+ routing_id(), sequence_number, printed_pages.size()));
return true;
#else
PrintHostMsg_DidPrintPage_Params printed_page_params;
diff --git a/chromium/components/printing/service/pdf_compositor_service.cc b/chromium/components/printing/service/pdf_compositor_service.cc
index d00f3ed76bd..40456fe47aa 100644
--- a/chromium/components/printing/service/pdf_compositor_service.cc
+++ b/chromium/components/printing/service/pdf_compositor_service.cc
@@ -22,7 +22,6 @@ namespace {
void OnPdfCompositorRequest(
const std::string& creator,
service_manager::ServiceContextRefFactory* ref_factory,
- const service_manager::BindSourceInfo& source_info,
printing::mojom::PdfCompositorRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<printing::PdfCompositorImpl>(
creator, ref_factory->CreateRef()),
@@ -66,8 +65,7 @@ void PdfCompositorService::OnBindInterface(
const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
- registry_.BindInterface(source_info, interface_name,
- std::move(interface_pipe));
+ registry_.BindInterface(interface_name, std::move(interface_pipe));
}
} // namespace printing
diff --git a/chromium/components/printing/service/pdf_compositor_service.h b/chromium/components/printing/service/pdf_compositor_service.h
index 31672988b5c..6f7bdb8c2c9 100644
--- a/chromium/components/printing/service/pdf_compositor_service.h
+++ b/chromium/components/printing/service/pdf_compositor_service.h
@@ -12,7 +12,6 @@
#include "base/memory/weak_ptr.h"
#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
#include "components/printing/service/public/interfaces/pdf_compositor.mojom.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/cpp/service_context_ref.h"
diff --git a/chromium/components/proximity_auth/BUILD.gn b/chromium/components/proximity_auth/BUILD.gn
index 3f4a2a4bf9a..fba8334afe4 100644
--- a/chromium/components/proximity_auth/BUILD.gn
+++ b/chromium/components/proximity_auth/BUILD.gn
@@ -23,11 +23,17 @@ static_library("proximity_auth") {
"messenger_observer.h",
"metrics.cc",
"metrics.h",
+ "notification_controller.h",
+ "promotion_manager.cc",
+ "promotion_manager.h",
"proximity_auth_client.h",
- "proximity_auth_pref_manager.cc",
+ "proximity_auth_local_state_pref_manager.cc",
+ "proximity_auth_local_state_pref_manager.h",
"proximity_auth_pref_manager.h",
"proximity_auth_pref_names.cc",
"proximity_auth_pref_names.h",
+ "proximity_auth_profile_pref_manager.cc",
+ "proximity_auth_profile_pref_manager.h",
"proximity_auth_system.cc",
"proximity_auth_system.h",
"proximity_monitor.h",
@@ -44,8 +50,6 @@ static_library("proximity_auth") {
"screenlock_state.h",
"switches.cc",
"switches.h",
- "throttled_bluetooth_connection_finder.cc",
- "throttled_bluetooth_connection_finder.h",
"unlock_manager.h",
"unlock_manager_impl.cc",
"unlock_manager_impl.h",
@@ -55,8 +59,10 @@ static_library("proximity_auth") {
"//base",
"//components/cryptauth",
"//components/cryptauth/ble",
+ "//components/pref_registry:pref_registry",
"//components/prefs",
"//components/proximity_auth/logging",
+ "//components/proximity_auth/public/interfaces",
"//components/signin/core/account_id:account_id",
"//device/bluetooth",
"//net",
@@ -98,12 +104,13 @@ source_set("unit_tests") {
"bluetooth_connection_unittest.cc",
"bluetooth_low_energy_connection_finder_unittest.cc",
"messenger_impl_unittest.cc",
- "proximity_auth_pref_manager_unittest.cc",
+ "promotion_manager_unittests.cc",
+ "proximity_auth_local_state_pref_manager_unittest.cc",
+ "proximity_auth_profile_pref_manager_unittest.cc",
"proximity_auth_system_unittest.cc",
"proximity_monitor_impl_unittest.cc",
"remote_device_life_cycle_impl_unittest.cc",
"remote_status_update_unittest.cc",
- "throttled_bluetooth_connection_finder_unittest.cc",
"unlock_manager_impl_unittest.cc",
]
@@ -120,6 +127,7 @@ source_set("unit_tests") {
"//components/prefs:test_support",
"//components/proximity_auth/logging",
"//components/proximity_auth/logging:unit_tests",
+ "//components/sync_preferences:test_support",
"//device/bluetooth:mocks",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/proximity_auth/public/interfaces/BUILD.gn b/chromium/components/proximity_auth/public/interfaces/BUILD.gn
new file mode 100644
index 00000000000..00e2400845f
--- /dev/null
+++ b/chromium/components/proximity_auth/public/interfaces/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+ sources = [
+ "auth_type.mojom",
+ ]
+}
diff --git a/chromium/components/proximity_auth/public/interfaces/auth_type.mojom b/chromium/components/proximity_auth/public/interfaces/auth_type.mojom
new file mode 100644
index 00000000000..12ad61f72b8
--- /dev/null
+++ b/chromium/components/proximity_auth/public/interfaces/auth_type.mojom
@@ -0,0 +1,28 @@
+// 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.
+
+module proximity_auth.mojom;
+
+// Supported authentication types.
+enum AuthType {
+ // Password is used to authenticate.
+ OFFLINE_PASSWORD,
+
+ // Online authentication against GAIA.
+ ONLINE_SIGN_IN,
+
+ // Pin is used to authenticate.
+ NUMERIC_PIN,
+
+ // Click on the user pod to unlock/sign-in.
+ USER_CLICK,
+
+ // Used for public session. Click on the user pod would expand the pod and
+ // allow a click to sign-in.
+ EXPAND_THEN_USER_CLICK,
+
+ // Forced to use password to authenticate.
+ // Unlike OFFLINE_PASSWORD, this can't be changed to any other.
+ FORCE_OFFLINE_PASSWORD,
+};
diff --git a/chromium/components/proxy_config/ios/proxy_service_factory.cc b/chromium/components/proxy_config/ios/proxy_service_factory.cc
index 600191ae7f8..e0e0775a558 100644
--- a/chromium/components/proxy_config/ios/proxy_service_factory.cc
+++ b/chromium/components/proxy_config/ios/proxy_service_factory.cc
@@ -17,8 +17,7 @@ std::unique_ptr<net::ProxyConfigService>
ProxyServiceFactory::CreateProxyConfigService(PrefProxyConfigTracker* tracker) {
std::unique_ptr<net::ProxyConfigService> base_service(
net::ProxyService::CreateSystemProxyConfigService(
- web::WebThread::GetTaskRunnerForThread(web::WebThread::IO),
- web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE)));
+ web::WebThread::GetTaskRunnerForThread(web::WebThread::IO)));
return tracker->CreateTrackingProxyConfigService(std::move(base_service));
}
diff --git a/chromium/components/proxy_config/pref_proxy_config_tracker.h b/chromium/components/proxy_config/pref_proxy_config_tracker.h
index d51abc8ae94..77e2e36ccfe 100644
--- a/chromium/components/proxy_config/pref_proxy_config_tracker.h
+++ b/chromium/components/proxy_config/pref_proxy_config_tracker.h
@@ -29,7 +29,7 @@ class PROXY_CONFIG_EXPORT PrefProxyConfigTracker {
// before DetachFromPrefService was called. Takes ownership of the passed
// |base_service|, which can be NULL. This |base_service| provides the proxy
// settings of the OS (except of ChromeOS). This must be called on the
- // UI thread.
+ // UI thread. May only be called once on a PrefProxyConfigTracker.
virtual std::unique_ptr<net::ProxyConfigService>
CreateTrackingProxyConfigService(
std::unique_ptr<net::ProxyConfigService> base_service) = 0;
diff --git a/chromium/components/proxy_config/pref_proxy_config_tracker_impl.cc b/chromium/components/proxy_config/pref_proxy_config_tracker_impl.cc
index f8077aa9032..6c4f03d9124 100644
--- a/chromium/components/proxy_config/pref_proxy_config_tracker_impl.cc
+++ b/chromium/components/proxy_config/pref_proxy_config_tracker_impl.cc
@@ -13,7 +13,6 @@
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/values.h"
-#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/proxy_config_dictionary.h"
@@ -24,10 +23,12 @@
//============================= ProxyConfigServiceImpl =======================
ProxyConfigServiceImpl::ProxyConfigServiceImpl(
- net::ProxyConfigService* base_service)
- : base_service_(base_service),
- pref_config_state_(ProxyPrefs::CONFIG_UNSET),
- pref_config_read_pending_(true),
+ std::unique_ptr<net::ProxyConfigService> base_service,
+ ProxyPrefs::ConfigState initial_config_state,
+ const net::ProxyConfig& initial_config)
+ : base_service_(std::move(base_service)),
+ pref_config_state_(initial_config_state),
+ pref_config_(initial_config),
registered_observer_(false) {
// ProxyConfigServiceImpl is created on the UI thread, but used on the network
// thread.
@@ -54,9 +55,6 @@ net::ProxyConfigService::ConfigAvailability
ProxyConfigServiceImpl::GetLatestProxyConfig(net::ProxyConfig* config) {
RegisterObserver();
- if (pref_config_read_pending_)
- return net::ProxyConfigService::CONFIG_PENDING;
-
// Ask the base service if available.
net::ProxyConfig system_config;
ConfigAvailability system_availability =
@@ -79,7 +77,6 @@ void ProxyConfigServiceImpl::UpdateProxyConfig(
ProxyPrefs::ConfigState config_state,
const net::ProxyConfig& config) {
DCHECK(thread_checker_.CalledOnValidThread());
- pref_config_read_pending_ = false;
pref_config_state_ = config_state;
pref_config_ = config;
@@ -109,7 +106,7 @@ void ProxyConfigServiceImpl::OnProxyConfigChanged(
// Check whether there is a proxy configuration defined by preferences. In
// this case that proxy configuration takes precedence and the change event
- // from the delegate proxy service can be disregarded.
+ // from the delegate proxy config service can be disregarded.
if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_)) {
net::ProxyConfig actual_config;
availability = GetLatestProxyConfig(&actual_config);
@@ -133,9 +130,11 @@ PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: pref_service_(pref_service),
proxy_config_service_impl_(NULL),
- update_pending_(true),
io_task_runner_(io_task_runner) {
- config_state_ = ReadPrefConfig(pref_service_, &pref_config_);
+ pref_config_state_ = ReadPrefConfig(pref_service_, &pref_config_);
+ active_config_state_ = pref_config_state_;
+ active_config_ = pref_config_;
+
proxy_prefs_.Init(pref_service);
proxy_prefs_.Add(proxy_config::prefs::kProxy,
base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged,
@@ -149,12 +148,11 @@ PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
std::unique_ptr<net::ProxyConfigService>
PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService(
std::unique_ptr<net::ProxyConfigService> base_service) {
- proxy_config_service_impl_ =
- new ProxyConfigServiceImpl(base_service.release());
+ DCHECK(!proxy_config_service_impl_);
+ proxy_config_service_impl_ = new ProxyConfigServiceImpl(
+ std::move(base_service), active_config_state_, active_config_);
VLOG(1) << this << ": set chrome proxy config service to "
<< proxy_config_service_impl_;
- if (proxy_config_service_impl_ && update_pending_)
- OnProxyConfigChanged(config_state_, pref_config_);
return std::unique_ptr<net::ProxyConfigService>(proxy_config_service_impl_);
}
@@ -216,13 +214,12 @@ void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
// static
void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
- user_prefs::PrefRegistrySyncable* pref_service) {
+ PrefRegistrySimple* registry) {
std::unique_ptr<base::DictionaryValue> default_settings =
ProxyConfigDictionary::CreateSystem();
- pref_service->RegisterDictionaryPref(proxy_config::prefs::kProxy,
- std::move(default_settings));
- pref_service->RegisterBooleanPref(proxy_config::prefs::kUseSharedProxies,
- false);
+ registry->RegisterDictionaryPref(proxy_config::prefs::kProxy,
+ std::move(default_settings));
+ registry->RegisterBooleanPref(proxy_config::prefs::kUseSharedProxies, false);
}
// static
@@ -261,25 +258,31 @@ ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::ReadPrefConfig(
ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::GetProxyConfig(
net::ProxyConfig* config) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (config_state_ != ProxyPrefs::CONFIG_UNSET)
+ if (pref_config_state_ != ProxyPrefs::CONFIG_UNSET)
*config = pref_config_;
- return config_state_;
+ return pref_config_state_;
}
void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
ProxyPrefs::ConfigState config_state,
const net::ProxyConfig& config) {
- if (!proxy_config_service_impl_) {
- VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
- update_pending_ = true;
+ // If the configuration hasn't changed, do nothing.
+ if (active_config_state_ == config_state &&
+ (active_config_state_ == ProxyPrefs::CONFIG_UNSET ||
+ active_config_.Equals(config))) {
return;
}
- update_pending_ = !io_task_runner_->PostTask(
+
+ active_config_state_ = config_state;
+ if (active_config_state_ != ProxyPrefs::CONFIG_UNSET)
+ active_config_ = config;
+
+ if (!proxy_config_service_impl_)
+ return;
+ io_task_runner_->PostTask(
FROM_HERE, base::Bind(&ProxyConfigServiceImpl::UpdateProxyConfig,
base::Unretained(proxy_config_service_impl_),
config_state, config));
- VLOG(1) << this << (update_pending_ ? ": Error" : ": Done")
- << " pushing proxy to UpdateProxyConfig";
}
bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
@@ -348,14 +351,12 @@ void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
net::ProxyConfig new_config;
ProxyPrefs::ConfigState config_state =
ReadPrefConfig(pref_service_, &new_config);
- if (config_state_ != config_state ||
- (config_state_ != ProxyPrefs::CONFIG_UNSET &&
+ if (pref_config_state_ != config_state ||
+ (pref_config_state_ != ProxyPrefs::CONFIG_UNSET &&
!pref_config_.Equals(new_config))) {
- config_state_ = config_state;
- if (config_state_ != ProxyPrefs::CONFIG_UNSET)
+ pref_config_state_ = config_state;
+ if (pref_config_state_ != ProxyPrefs::CONFIG_UNSET)
pref_config_ = new_config;
- update_pending_ = true;
- }
- if (update_pending_)
OnProxyConfigChanged(config_state, new_config);
+ }
}
diff --git a/chromium/components/proxy_config/pref_proxy_config_tracker_impl.h b/chromium/components/proxy_config/pref_proxy_config_tracker_impl.h
index ec42a792217..b86fb3271f0 100644
--- a/chromium/components/proxy_config/pref_proxy_config_tracker_impl.h
+++ b/chromium/components/proxy_config/pref_proxy_config_tracker_impl.h
@@ -24,10 +24,6 @@ namespace base {
class SingleThreadTaskRunner;
}
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
// A net::ProxyConfigService implementation that applies preference proxy
// settings (pushed from PrefProxyConfigTrackerImpl) as overrides to the proxy
// configuration determined by a baseline delegate ProxyConfigService on
@@ -36,10 +32,9 @@ class PrefRegistrySyncable;
class ProxyConfigServiceImpl : public net::ProxyConfigService,
public net::ProxyConfigService::Observer {
public:
- // Takes ownership of the passed |base_service|.
- // GetLatestProxyConfig returns ConfigAvailability::CONFIG_PENDING until
- // UpdateProxyConfig has been called.
- explicit ProxyConfigServiceImpl(net::ProxyConfigService* base_service);
+ ProxyConfigServiceImpl(std::unique_ptr<net::ProxyConfigService> base_service,
+ ProxyPrefs::ConfigState initial_config_state,
+ const net::ProxyConfig& initial_config);
~ProxyConfigServiceImpl() override;
// ProxyConfigService implementation:
@@ -71,10 +66,6 @@ class ProxyConfigServiceImpl : public net::ProxyConfigService,
// Configuration as defined by prefs.
net::ProxyConfig pref_config_;
- // Flag that indicates that a PrefProxyConfigTracker needs to inform us
- // about a proxy configuration before we may return any configuration.
- bool pref_config_read_pending_;
-
// Indicates whether the base service registration is done.
bool registered_observer_;
@@ -131,7 +122,7 @@ class PROXY_CONFIG_EXPORT PrefProxyConfigTrackerImpl
// Registers the proxy preferences. These are actually registered
// the same way in local state and in user prefs.
static void RegisterPrefs(PrefRegistrySimple* registry);
- static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// Creates a proxy configuration from proxy-related preferences of
// |pref_service|. Configuration is stored in |config|, return value indicates
@@ -148,27 +139,34 @@ class PROXY_CONFIG_EXPORT PrefProxyConfigTrackerImpl
// Called when there's a change in prefs proxy config.
// Subclasses can extend it for changes in other sources of proxy config.
+ // Checks new config against old config, and if there was no change, does
+ // nothing.
virtual void OnProxyConfigChanged(ProxyPrefs::ConfigState config_state,
const net::ProxyConfig& config);
void OnProxyPrefChanged();
const PrefService* prefs() const { return pref_service_; }
- bool update_pending() const { return update_pending_; }
private:
// Tracks configuration state. |pref_config_| is valid only if |config_state_|
// is not CONFIG_UNSET.
- ProxyPrefs::ConfigState config_state_;
+ ProxyPrefs::ConfigState pref_config_state_;
// Configuration as defined by prefs.
net::ProxyConfig pref_config_;
PrefService* pref_service_;
ProxyConfigServiceImpl* proxy_config_service_impl_; // Weak ptr.
- bool update_pending_; // True if config has not been pushed to network stack.
PrefChangeRegistrar proxy_prefs_;
+ // State of |active_config_|. |active_config_| is only valid if
+ // |active_config_state_| is not ProxyPrefs::CONFIG_UNSET.
+ ProxyPrefs::ConfigState active_config_state_;
+
+ // Active proxy configuration, last received from OnProxyConfigChanged.
+ net::ProxyConfig active_config_;
+
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
base::ThreadChecker thread_checker_;
diff --git a/chromium/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc b/chromium/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc
index b0fd11edca0..b0fd3b51326 100644
--- a/chromium/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc
+++ b/chromium/components/proxy_config/pref_proxy_config_tracker_impl_unittest.cc
@@ -74,22 +74,22 @@ class MockObserver : public net::ProxyConfigService::Observer {
class PrefProxyConfigTrackerImplTest : public testing::Test {
protected:
- PrefProxyConfigTrackerImplTest() {
+ PrefProxyConfigTrackerImplTest() {}
+
+ // Initializes the proxy config service. The delegate config service has the
+ // specified initial config availability.
+ void InitConfigService(net::ProxyConfigService::ConfigAvailability
+ delegate_config_availability) {
pref_service_.reset(new TestingPrefServiceSimple());
PrefProxyConfigTrackerImpl::RegisterPrefs(pref_service_->registry());
fixed_config_.set_pac_url(GURL(kFixedPacUrl));
delegate_service_ =
- new TestProxyConfigService(fixed_config_,
- net::ProxyConfigService::CONFIG_VALID);
+ new TestProxyConfigService(fixed_config_, delegate_config_availability);
proxy_config_tracker_.reset(new PrefProxyConfigTrackerImpl(
pref_service_.get(), base::ThreadTaskRunnerHandle::Get()));
proxy_config_service_ =
proxy_config_tracker_->CreateTrackingProxyConfigService(
std::unique_ptr<net::ProxyConfigService>(delegate_service_));
- // SetProxyConfigServiceImpl triggers update of initial prefs proxy
- // config by tracker to chrome proxy config service, so flush all pending
- // tasks so that tests start fresh.
- base::RunLoop().RunUntilIdle();
}
~PrefProxyConfigTrackerImplTest() override {
@@ -104,12 +104,11 @@ class PrefProxyConfigTrackerImplTest : public testing::Test {
TestProxyConfigService* delegate_service_; // weak
std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
net::ProxyConfig fixed_config_;
-
- private:
std::unique_ptr<PrefProxyConfigTrackerImpl> proxy_config_tracker_;
};
TEST_F(PrefProxyConfigTrackerImplTest, BaseConfiguration) {
+ InitConfigService(net::ProxyConfigService::CONFIG_VALID);
net::ProxyConfig actual_config;
EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID,
proxy_config_service_->GetLatestProxyConfig(&actual_config));
@@ -117,6 +116,7 @@ TEST_F(PrefProxyConfigTrackerImplTest, BaseConfiguration) {
}
TEST_F(PrefProxyConfigTrackerImplTest, DynamicPrefOverrides) {
+ InitConfigService(net::ProxyConfigService::CONFIG_VALID);
pref_service_->SetManagedPref(proxy_config::prefs::kProxy,
ProxyConfigDictionary::CreateFixedServers(
"http://example.com:3128", std::string()));
@@ -149,6 +149,7 @@ MATCHER_P(ProxyConfigMatches, config, "") {
}
TEST_F(PrefProxyConfigTrackerImplTest, Observers) {
+ InitConfigService(net::ProxyConfigService::CONFIG_VALID);
const net::ProxyConfigService::ConfigAvailability CONFIG_VALID =
net::ProxyConfigService::CONFIG_VALID;
MockObserver observer;
@@ -204,6 +205,7 @@ TEST_F(PrefProxyConfigTrackerImplTest, Observers) {
}
TEST_F(PrefProxyConfigTrackerImplTest, Fallback) {
+ InitConfigService(net::ProxyConfigService::CONFIG_VALID);
const net::ProxyConfigService::ConfigAvailability CONFIG_VALID =
net::ProxyConfigService::CONFIG_VALID;
MockObserver observer;
@@ -257,6 +259,7 @@ TEST_F(PrefProxyConfigTrackerImplTest, Fallback) {
}
TEST_F(PrefProxyConfigTrackerImplTest, ExplicitSystemSettings) {
+ InitConfigService(net::ProxyConfigService::CONFIG_VALID);
pref_service_->SetRecommendedPref(proxy_config::prefs::kProxy,
ProxyConfigDictionary::CreateAutoDetect());
pref_service_->SetUserPref(proxy_config::prefs::kProxy,
@@ -270,4 +273,34 @@ TEST_F(PrefProxyConfigTrackerImplTest, ExplicitSystemSettings) {
EXPECT_EQ(GURL(kFixedPacUrl), actual_config.pac_url());
}
+// Test the case where the delegate service gets a config only after the service
+// is created.
+TEST_F(PrefProxyConfigTrackerImplTest, DelegateConfigServiceGetsConfigLate) {
+ InitConfigService(net::ProxyConfigService::CONFIG_PENDING);
+
+ testing::StrictMock<MockObserver> observer;
+ proxy_config_service_->AddObserver(&observer);
+
+ net::ProxyConfig actual_config;
+ EXPECT_EQ(net::ProxyConfigService::CONFIG_PENDING,
+ proxy_config_service_->GetLatestProxyConfig(&actual_config));
+
+ // When the delegate service gets the config, the other service should update
+ // its observers.
+ EXPECT_CALL(observer,
+ OnProxyConfigChanged(ProxyConfigMatches(fixed_config_),
+ net::ProxyConfigService::CONFIG_VALID))
+ .Times(1);
+ delegate_service_->SetProxyConfig(fixed_config_,
+ net::ProxyConfigService::CONFIG_VALID);
+
+ // Since no prefs were set, should just use the delegated config service's
+ // settings.
+ EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID,
+ proxy_config_service_->GetLatestProxyConfig(&actual_config));
+ EXPECT_EQ(GURL(kFixedPacUrl), actual_config.pac_url());
+
+ proxy_config_service_->RemoveObserver(&observer);
+}
+
} // namespace
diff --git a/chromium/components/quirks/quirks_client.cc b/chromium/components/quirks/quirks_client.cc
index b50f4526c4a..13eea620eab 100644
--- a/chromium/components/quirks/quirks_client.cc
+++ b/chromium/components/quirks/quirks_client.cc
@@ -9,7 +9,6 @@
#include "base/json/json_reader.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/quirks/quirks_manager.h"
#include "components/version_info/version_info.h"
@@ -143,7 +142,7 @@ void QuirksClient::OnURLFetchComplete(const net::URLFetcher* source) {
}
base::PostTaskAndReplyWithResult(
- manager_->blocking_pool(), FROM_HERE,
+ manager_->task_runner(), FROM_HERE,
base::Bind(&WriteIccFile, icc_path_, data),
base::Bind(&QuirksClient::Shutdown, weak_ptr_factory_.GetWeakPtr()));
}
diff --git a/chromium/components/quirks/quirks_manager.cc b/chromium/components/quirks/quirks_manager.cc
index d5d81bb63b3..e569bbcd8c2 100644
--- a/chromium/components/quirks/quirks_manager.cc
+++ b/chromium/components/quirks/quirks_manager.cc
@@ -11,8 +11,8 @@
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
+#include "base/task_runner.h"
#include "base/task_runner_util.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/quirks/pref_names.h"
@@ -56,12 +56,12 @@ std::string IdToFileName(int64_t product_id) {
QuirksManager::QuirksManager(
std::unique_ptr<Delegate> delegate,
- scoped_refptr<base::SequencedWorkerPool> blocking_pool,
+ scoped_refptr<base::TaskRunner> task_runner,
PrefService* local_state,
scoped_refptr<net::URLRequestContextGetter> url_context_getter)
: waiting_for_login_(true),
delegate_(std::move(delegate)),
- blocking_pool_(blocking_pool),
+ task_runner_(task_runner),
local_state_(local_state),
url_context_getter_(url_context_getter),
weak_ptr_factory_(this) {}
@@ -74,10 +74,10 @@ QuirksManager::~QuirksManager() {
// static
void QuirksManager::Initialize(
std::unique_ptr<Delegate> delegate,
- scoped_refptr<base::SequencedWorkerPool> blocking_pool,
+ scoped_refptr<base::TaskRunner> task_runner,
PrefService* local_state,
scoped_refptr<net::URLRequestContextGetter> url_context_getter) {
- manager_ = new QuirksManager(std::move(delegate), blocking_pool, local_state,
+ manager_ = new QuirksManager(std::move(delegate), task_runner, local_state,
url_context_getter);
}
@@ -132,7 +132,7 @@ void QuirksManager::RequestIccProfilePath(
std::string name = IdToFileName(product_id);
base::PostTaskAndReplyWithResult(
- blocking_pool_.get(), FROM_HERE,
+ task_runner_.get(), FROM_HERE,
base::Bind(&CheckForIccFile,
delegate_->GetDisplayProfileDirectory().Append(name)),
base::Bind(&QuirksManager::OnIccFilePathRequestCompleted,
diff --git a/chromium/components/quirks/quirks_manager.h b/chromium/components/quirks/quirks_manager.h
index 52902730a5b..d5a90a7fd98 100644
--- a/chromium/components/quirks/quirks_manager.h
+++ b/chromium/components/quirks/quirks_manager.h
@@ -21,7 +21,7 @@ class PrefRegistrySimple;
class PrefService;
namespace base {
-class SequencedWorkerPool;
+class TaskRunner;
}
namespace net {
@@ -78,7 +78,7 @@ class QUIRKS_EXPORT QuirksManager {
static void Initialize(
std::unique_ptr<Delegate> delegate,
- scoped_refptr<base::SequencedWorkerPool> blocking_pool,
+ scoped_refptr<base::TaskRunner> task_runner,
PrefService* local_state,
scoped_refptr<net::URLRequestContextGetter> url_context_getter);
static void Shutdown();
@@ -103,7 +103,7 @@ class QUIRKS_EXPORT QuirksManager {
net::URLFetcherDelegate* delegate);
Delegate* delegate() { return delegate_.get(); }
- base::SequencedWorkerPool* blocking_pool() { return blocking_pool_.get(); }
+ base::TaskRunner* task_runner() { return task_runner_.get(); }
net::URLRequestContextGetter* url_context_getter() {
return url_context_getter_.get();
}
@@ -118,7 +118,7 @@ class QUIRKS_EXPORT QuirksManager {
private:
QuirksManager(std::unique_ptr<Delegate> delegate,
- scoped_refptr<base::SequencedWorkerPool> blocking_pool,
+ scoped_refptr<base::TaskRunner> task_runner,
PrefService* local_state,
scoped_refptr<net::URLRequestContextGetter> url_context_getter);
~QuirksManager();
@@ -147,7 +147,7 @@ class QUIRKS_EXPORT QuirksManager {
// These objects provide resources from the browser.
std::unique_ptr<Delegate> delegate_; // Impl runs from chrome/browser.
- scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
+ scoped_refptr<base::TaskRunner> task_runner_;
PrefService* local_state_; // For local prefs.
scoped_refptr<net::URLRequestContextGetter> url_context_getter_;
diff --git a/chromium/components/rappor/rappor_recorder_impl.cc b/chromium/components/rappor/rappor_recorder_impl.cc
index 4a2a23d3d78..52a281138fd 100644
--- a/chromium/components/rappor/rappor_recorder_impl.cc
+++ b/chromium/components/rappor/rappor_recorder_impl.cc
@@ -19,7 +19,6 @@ RapporRecorderImpl::~RapporRecorderImpl() = default;
// static
void RapporRecorderImpl::Create(
RapporServiceImpl* rappor_service,
- const service_manager::BindSourceInfo& source_info,
mojom::RapporRecorderRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<RapporRecorderImpl>(rappor_service),
std::move(request));
diff --git a/chromium/components/rappor/rappor_recorder_impl.h b/chromium/components/rappor/rappor_recorder_impl.h
index c6da8a9d25d..103b4cd0358 100644
--- a/chromium/components/rappor/rappor_recorder_impl.h
+++ b/chromium/components/rappor/rappor_recorder_impl.h
@@ -10,10 +10,6 @@
class GURL;
-namespace service_manager {
-struct BindSourceInfo;
-}
-
namespace rappor {
class RapporServiceImpl;
@@ -26,7 +22,6 @@ class RapporRecorderImpl : public mojom::RapporRecorder {
~RapporRecorderImpl() override;
static void Create(RapporServiceImpl* rappor_service,
- const service_manager::BindSourceInfo& source_info,
mojom::RapporRecorderRequest request);
private:
diff --git a/chromium/components/reading_list/core/BUILD.gn b/chromium/components/reading_list/core/BUILD.gn
index a5d4f131998..f40368b667f 100644
--- a/chromium/components/reading_list/core/BUILD.gn
+++ b/chromium/components/reading_list/core/BUILD.gn
@@ -2,8 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/buildflag_header.gni")
-import("//components/reading_list/core/reading_list.gni")
+import("//components/reading_list/features/reading_list.gni")
source_set("core") {
sources = [
@@ -25,10 +24,10 @@ source_set("core") {
"reading_list_store_delegate.h",
]
deps = [
- ":flags",
"//base",
"//components/keyed_service/core",
"//components/prefs",
+ "//components/reading_list/features:flags",
"//components/sync",
"//net",
"//url",
@@ -56,22 +55,3 @@ source_set("unit_tests") {
"//url",
]
}
-
-source_set("flags") {
- sources = [
- "reading_list_switches.cc",
- "reading_list_switches.h",
- ]
- deps = [
- "//base",
- ]
- public_deps = [
- ":reading_list_enable_flags",
- ]
-}
-
-buildflag_header("reading_list_enable_flags") {
- header = "reading_list_enable_flags.h"
- _enabled = is_ios && enable_reading_list
- flags = [ "ENABLE_READING_LIST=$_enabled" ]
-}
diff --git a/chromium/components/reading_list/core/reading_list_model.cc b/chromium/components/reading_list/core/reading_list_model.cc
index d3d2829a553..04252bc6873 100644
--- a/chromium/components/reading_list/core/reading_list_model.cc
+++ b/chromium/components/reading_list/core/reading_list_model.cc
@@ -10,6 +10,7 @@
ReadingListModel::ReadingListModel() : current_batch_updates_count_(0) {}
ReadingListModel::~ReadingListModel() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_) {
observer.ReadingListModelBeingDeleted(this);
}
@@ -17,7 +18,7 @@ ReadingListModel::~ReadingListModel() {
// Observer methods.
void ReadingListModel::AddObserver(ReadingListModelObserver* observer) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(observer);
observers_.AddObserver(observer);
if (loaded()) {
@@ -26,13 +27,13 @@ void ReadingListModel::AddObserver(ReadingListModelObserver* observer) {
}
void ReadingListModel::RemoveObserver(ReadingListModelObserver* observer) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.RemoveObserver(observer);
}
// Batch update methods.
bool ReadingListModel::IsPerformingBatchUpdates() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return current_batch_updates_count_ > 0;
}
@@ -43,7 +44,7 @@ ReadingListModel::CreateBatchToken() {
std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
ReadingListModel::BeginBatchUpdates() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto token = CreateBatchToken();
++current_batch_updates_count_;
@@ -54,13 +55,13 @@ ReadingListModel::BeginBatchUpdates() {
}
void ReadingListModel::EnteringBatchUpdates() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_)
observer.ReadingListModelBeganBatchUpdates(this);
}
void ReadingListModel::EndBatchUpdates() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsPerformingBatchUpdates());
DCHECK(current_batch_updates_count_ > 0);
--current_batch_updates_count_;
@@ -70,7 +71,7 @@ void ReadingListModel::EndBatchUpdates() {
}
void ReadingListModel::LeavingBatchUpdates() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_)
observer.ReadingListModelCompletedBatchUpdates(this);
}
diff --git a/chromium/components/reading_list/core/reading_list_model.h b/chromium/components/reading_list/core/reading_list_model.h
index d0031588b7f..c51e0161328 100644
--- a/chromium/components/reading_list/core/reading_list_model.h
+++ b/chromium/components/reading_list/core/reading_list_model.h
@@ -11,7 +11,7 @@
#include "base/callback.h"
#include "base/observer_list.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "components/reading_list/core/reading_list_entry.h"
#include "components/reading_list/core/reading_list_model_observer.h"
@@ -27,7 +27,7 @@ class ModelTypeSyncBridge;
// other of read ones. This object should only be accessed from one thread
// (Usually the main thread). The observers callbacks are also sent on the main
// thread.
-class ReadingListModel : public base::NonThreadSafe {
+class ReadingListModel {
public:
class ScopedReadingListBatchUpdate;
@@ -170,6 +170,8 @@ class ReadingListModel : public base::NonThreadSafe {
// Called when model is leaving batch update mode.
virtual void LeavingBatchUpdates();
+ SEQUENCE_CHECKER(sequence_checker_);
+
private:
unsigned int current_batch_updates_count_;
diff --git a/chromium/components/reading_list/core/reading_list_model_impl.cc b/chromium/components/reading_list/core/reading_list_model_impl.cc
index 92713dbd2cf..8a0b440c025 100644
--- a/chromium/components/reading_list/core/reading_list_model_impl.cc
+++ b/chromium/components/reading_list/core/reading_list_model_impl.cc
@@ -27,7 +27,7 @@ ReadingListModelImpl::ReadingListModelImpl(
has_unseen_(false),
loaded_(false),
weak_ptr_factory_(this) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(clock_);
if (storage) {
storage_layer_ = std::move(storage);
@@ -42,7 +42,7 @@ ReadingListModelImpl::~ReadingListModelImpl() {}
void ReadingListModelImpl::StoreLoaded(
std::unique_ptr<ReadingListEntries> entries) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(entries);
entries_ = std::move(entries);
for (auto& iterator : *entries_) {
@@ -55,19 +55,19 @@ void ReadingListModelImpl::StoreLoaded(
}
void ReadingListModelImpl::Shutdown() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_)
observer.ReadingListModelBeingShutdown(this);
loaded_ = false;
}
bool ReadingListModelImpl::loaded() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return loaded_;
}
size_t ReadingListModelImpl::size() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(read_entry_count_ + unread_entry_count_ == entries_->size());
if (!loaded())
return 0;
@@ -75,7 +75,7 @@ size_t ReadingListModelImpl::size() const {
}
size_t ReadingListModelImpl::unread_size() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(read_entry_count_ + unread_entry_count_ == entries_->size());
if (!loaded())
return 0;
@@ -83,7 +83,7 @@ size_t ReadingListModelImpl::unread_size() const {
}
size_t ReadingListModelImpl::unseen_size() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!loaded())
return 0;
return unseen_entry_count_;
@@ -99,7 +99,7 @@ void ReadingListModelImpl::SetUnseenFlag() {
}
bool ReadingListModelImpl::GetLocalUnseenFlag() const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!loaded())
return false;
// If there are currently no unseen entries, return false even if has_unseen_
@@ -109,7 +109,7 @@ bool ReadingListModelImpl::GetLocalUnseenFlag() const {
}
void ReadingListModelImpl::ResetLocalUnseenFlag() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!loaded()) {
return;
}
@@ -119,7 +119,7 @@ void ReadingListModelImpl::ResetLocalUnseenFlag() {
}
void ReadingListModelImpl::MarkAllSeen() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
if (unseen_entry_count_ == 0) {
return;
@@ -148,7 +148,7 @@ void ReadingListModelImpl::MarkAllSeen() {
}
bool ReadingListModelImpl::DeleteAllEntries() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!loaded()) {
return false;
}
@@ -193,14 +193,14 @@ const std::vector<GURL> ReadingListModelImpl::Keys() const {
const ReadingListEntry* ReadingListModelImpl::GetEntryByURL(
const GURL& gurl) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
return GetMutableEntryFromURL(gurl);
}
const ReadingListEntry* ReadingListModelImpl::GetFirstUnreadEntry(
bool distilled) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
if (unread_entry_count_ == 0) {
return nullptr;
@@ -234,7 +234,7 @@ const ReadingListEntry* ReadingListModelImpl::GetFirstUnreadEntry(
ReadingListEntry* ReadingListModelImpl::GetMutableEntryFromURL(
const GURL& url) const {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
auto iterator = entries_->find(url);
if (iterator == entries_->end()) {
@@ -245,7 +245,7 @@ ReadingListEntry* ReadingListModelImpl::GetMutableEntryFromURL(
void ReadingListModelImpl::SyncAddEntry(
std::unique_ptr<ReadingListEntry> entry) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
// entry must not already exist.
DCHECK(GetMutableEntryFromURL(entry->URL()) == nullptr);
@@ -265,7 +265,7 @@ void ReadingListModelImpl::SyncAddEntry(
ReadingListEntry* ReadingListModelImpl::SyncMergeEntry(
std::unique_ptr<ReadingListEntry> entry) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
ReadingListEntry* existing_entry = GetMutableEntryFromURL(entry->URL());
DCHECK(existing_entry);
@@ -301,7 +301,7 @@ void ReadingListModelImpl::RemoveEntryByURL(const GURL& url) {
void ReadingListModelImpl::RemoveEntryByURLImpl(const GURL& url,
bool from_sync) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
const ReadingListEntry* entry = GetEntryByURL(url);
if (!entry)
@@ -324,7 +324,7 @@ const ReadingListEntry& ReadingListModelImpl::AddEntry(
const GURL& url,
const std::string& title,
reading_list::EntrySource source) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
DCHECK(url.SchemeIsHTTPOrHTTPS());
RemoveEntryByURL(url);
@@ -351,7 +351,7 @@ const ReadingListEntry& ReadingListModelImpl::AddEntry(
}
void ReadingListModelImpl::SetReadStatus(const GURL& url, bool read) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
auto iterator = entries_->find(url);
if (iterator == entries_->end()) {
@@ -380,7 +380,7 @@ void ReadingListModelImpl::SetReadStatus(const GURL& url, bool read) {
void ReadingListModelImpl::SetEntryTitle(const GURL& url,
const std::string& title) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
auto iterator = entries_->find(url);
if (iterator == entries_->end()) {
@@ -410,7 +410,7 @@ void ReadingListModelImpl::SetEntryDistilledInfo(
const GURL& distilled_url,
int64_t distillation_size,
const base::Time& distillation_date) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
auto iterator = entries_->find(url);
if (iterator == entries_->end()) {
@@ -438,7 +438,7 @@ void ReadingListModelImpl::SetEntryDistilledInfo(
void ReadingListModelImpl::SetEntryDistilledState(
const GURL& url,
ReadingListEntry::DistillationState state) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
auto iterator = entries_->find(url);
if (iterator == entries_->end()) {
@@ -464,7 +464,7 @@ void ReadingListModelImpl::SetEntryDistilledState(
void ReadingListModelImpl::SetContentSuggestionsExtra(
const GURL& url,
const reading_list::ContentSuggestionsExtra& extra) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(loaded());
ReadingListEntry* entry = GetMutableEntryFromURL(url);
if (!entry) {
@@ -504,7 +504,7 @@ ReadingListModelImpl::ScopedReadingListBatchUpdate::
}
void ReadingListModelImpl::LeavingBatchUpdates() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (storage_layer_) {
SetPersistentHasUnseen(has_unseen_);
}
@@ -512,12 +512,12 @@ void ReadingListModelImpl::LeavingBatchUpdates() {
}
void ReadingListModelImpl::EnteringBatchUpdates() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ReadingListModel::EnteringBatchUpdates();
}
void ReadingListModelImpl::SetPersistentHasUnseen(bool has_unseen) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!pref_service_) {
return;
}
@@ -526,7 +526,7 @@ void ReadingListModelImpl::SetPersistentHasUnseen(bool has_unseen) {
}
bool ReadingListModelImpl::GetPersistentHasUnseen() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!pref_service_) {
return false;
}
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 c79a63578dc..c71c731eed4 100644
--- a/chromium/components/reading_list/core/reading_list_model_unittest.cc
+++ b/chromium/components/reading_list/core/reading_list_model_unittest.cc
@@ -115,7 +115,7 @@ class TestReadingListStorage : public ReadingListModelStorage {
base::Optional<syncer::ModelError> MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityDataMap entity_data_map) override {
+ syncer::EntityChangeList entity_data) 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 a9af371a3f0..015c7c3ac16 100644
--- a/chromium/components/reading_list/core/reading_list_store.cc
+++ b/chromium/components/reading_list/core/reading_list_store.cc
@@ -29,13 +29,14 @@ ReadingListStore::ReadingListStore(
pending_transaction_count_(0) {}
ReadingListStore::~ReadingListStore() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(0, pending_transaction_count_);
}
void ReadingListStore::SetReadingListModel(ReadingListModel* model,
ReadingListStoreDelegate* delegate,
base::Clock* clock) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
model_ = model;
delegate_ = delegate;
clock_ = clock;
@@ -59,7 +60,7 @@ ReadingListStore::ScopedBatchUpdate::~ScopedBatchUpdate() {
}
void ReadingListStore::BeginTransaction() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_transaction_count_++;
if (pending_transaction_count_ == 1) {
batch_ = store_->CreateWriteBatch();
@@ -67,7 +68,7 @@ void ReadingListStore::BeginTransaction() {
}
void ReadingListStore::CommitTransaction() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_transaction_count_--;
if (pending_transaction_count_ == 0) {
store_->CommitWriteBatch(
@@ -78,7 +79,7 @@ void ReadingListStore::CommitTransaction() {
}
void ReadingListStore::SaveEntry(const ReadingListEntry& entry) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto token = EnsureBatchCreated();
std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
@@ -105,7 +106,7 @@ void ReadingListStore::SaveEntry(const ReadingListEntry& entry) {
}
void ReadingListStore::RemoveEntry(const ReadingListEntry& entry) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto token = EnsureBatchCreated();
batch_->DeleteData(entry.URL().spec());
@@ -122,7 +123,7 @@ void ReadingListStore::RemoveEntry(const ReadingListEntry& entry) {
void ReadingListStore::OnDatabaseLoad(
syncer::ModelTypeStore::Result result,
std::unique_ptr<syncer::ModelTypeStore::RecordList> entries) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result != syncer::ModelTypeStore::Result::SUCCESS) {
change_processor()->ReportError(FROM_HERE,
"Cannot load Reading List Database.");
@@ -158,7 +159,7 @@ void ReadingListStore::OnDatabaseLoad(
void ReadingListStore::OnReadAllMetadata(
base::Optional<syncer::ModelError> error,
std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error) {
change_processor()->ReportError(FROM_HERE, "Failed to read metadata.");
} else {
@@ -173,7 +174,7 @@ void ReadingListStore::OnDatabaseSave(syncer::ModelTypeStore::Result result) {
void ReadingListStore::OnStoreCreated(
syncer::ModelTypeStore::Result result,
std::unique_ptr<syncer::ModelTypeStore> store) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result != syncer::ModelTypeStore::Result::SUCCESS) {
// TODO(crbug.com/664926): handle store creation error.
return;
@@ -194,20 +195,19 @@ ReadingListStore::CreateMetadataChangeList() {
// Perform the initial merge between local and sync data. This should only be
// called when a data type is first enabled to start syncing, and there is no
// sync metadata. Best effort should be made to match local and sync data. The
-// keys in the |entity_data_map| will have been created via GetClientTag(...),
-// and if a local and sync data should match/merge but disagree on tags, the
-// service should use the sync data's tag. Any local pieces of data that are
-// not present in sync should immediately be Put(...) to the processor before
-// returning. The same MetadataChangeList that was passed into this function
-// can be passed to Put(...) calls. Delete(...) can also be called but should
-// not be needed for most model types. Durable storage writes, if not able to
-// combine all change atomically, should save the metadata after the data
-// changes, so that this merge will be re-driven by sync if is not completely
-// saved during the current run.
+// storage keys in the |entity_data| are populated with GetStorageKey(...),
+// local and sync copies of the same entity should resolve to the same storage
+// key. Any local pieces of data that are not present in sync should immediately
+// be Put(...) to the processor before returning. The same MetadataChangeList
+// that was passed into this function can be passed to Put(...) calls.
+// Delete(...) can also be called but should not be needed for most model types.
+// Durable storage writes, if not able to combine all change atomically, should
+// save the metadata after the data changes, so that this merge will be re-
+// driven by sync if is not completely saved during the current run.
base::Optional<syncer::ModelError> ReadingListStore::MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityDataMap entity_data_map) {
- DCHECK(CalledOnValidThread());
+ syncer::EntityChangeList entity_data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto token = EnsureBatchCreated();
// Keep track of the last update of each item.
std::set<std::string> synced_entries;
@@ -215,10 +215,10 @@ base::Optional<syncer::ModelError> ReadingListStore::MergeSyncData(
model_batch_updates = model_->BeginBatchUpdates();
// Merge sync to local data.
- for (const auto& kv : entity_data_map) {
- synced_entries.insert(kv.first);
+ for (const auto& change : entity_data) {
+ synced_entries.insert(change.storage_key());
const sync_pb::ReadingListSpecifics& specifics =
- kv.second.value().specifics.reading_list();
+ change.data().specifics.reading_list();
// Deserialize entry.
std::unique_ptr<ReadingListEntry> entry(
ReadingListEntry::FromReadingListSpecifics(specifics, clock_->Now()));
@@ -293,7 +293,7 @@ base::Optional<syncer::ModelError> ReadingListStore::MergeSyncData(
base::Optional<syncer::ModelError> ReadingListStore::ApplySyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
model_->BeginBatchUpdates();
auto token = EnsureBatchCreated();
@@ -356,7 +356,7 @@ base::Optional<syncer::ModelError> ReadingListStore::ApplySyncChanges(
void ReadingListStore::GetData(StorageKeyList storage_keys,
DataCallback callback) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto batch = base::MakeUnique<syncer::MutableDataBatch>();
for (const std::string& url_string : storage_keys) {
const ReadingListEntry* entry = model_->GetEntryByURL(GURL(url_string));
@@ -369,7 +369,7 @@ void ReadingListStore::GetData(StorageKeyList storage_keys,
}
void ReadingListStore::GetAllData(DataCallback callback) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto batch = base::MakeUnique<syncer::MutableDataBatch>();
for (const auto& url : model_->Keys()) {
@@ -382,7 +382,7 @@ void ReadingListStore::GetAllData(DataCallback callback) {
void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
const ReadingListEntry& entry) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
entry.AsReadingListSpecifics();
@@ -402,7 +402,7 @@ void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
// GetStorageKey().
std::string ReadingListStore::GetClientTag(
const syncer::EntityData& entity_data) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return GetStorageKey(entity_data);
}
@@ -414,7 +414,7 @@ std::string ReadingListStore::GetClientTag(
// should be.
std::string ReadingListStore::GetStorageKey(
const syncer::EntityData& entity_data) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return entity_data.specifics.reading_list().entry_id();
}
diff --git a/chromium/components/reading_list/core/reading_list_store.h b/chromium/components/reading_list/core/reading_list_store.h
index 9f6eef8abfd..d3016f692a6 100644
--- a/chromium/components/reading_list/core/reading_list_store.h
+++ b/chromium/components/reading_list/core/reading_list_store.h
@@ -8,7 +8,7 @@
#include <memory>
#include <string>
-#include "base/threading/non_thread_safe.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"
#include "components/sync/model/model_error.h"
@@ -21,8 +21,7 @@ class MutableDataBatch;
class ReadingListModel;
// A ReadingListModelStorage storing and syncing data in protobufs.
-class ReadingListStore : public ReadingListModelStorage,
- public base::NonThreadSafe {
+class ReadingListStore : public ReadingListModelStorage {
using StoreFactoryFunction = base::Callback<void(
const syncer::ModelTypeStore::InitCallback& callback)>;
@@ -49,19 +48,19 @@ class ReadingListStore : public ReadingListModelStorage,
// Perform the initial merge between local and sync data. This should only be
// called when a data type is first enabled to start syncing, and there is no
// sync metadata. Best effort should be made to match local and sync data. The
- // keys in the |entity_data_map| will have been created via GetClientTag(...),
- // and if a local and sync data should match/merge but disagree on tags, the
- // service should use the sync data's tag. Any local pieces of data that are
- // not present in sync should immediately be Put(...) to the processor before
- // returning. The same MetadataChangeList that was passed into this function
- // can be passed to Put(...) calls. Delete(...) can also be called but should
- // not be needed for most model types. Durable storage writes, if not able to
- // combine all change atomically, should save the metadata after the data
- // changes, so that this merge will be re-driven by sync if is not completely
- // saved during the current run.
+ // storage keys in the |entity_data| are populated with GetStorageKey(...),
+ // local and sync copies of the same entity should resolve to the same storage
+ // key. Any local pieces of data that are not present in sync should
+ // immediately be Put(...) to the processor before returning. The same
+ // MetadataChangeList that was passed into this function can be passed to
+ // Put(...) calls. Delete(...) can also be called but should not be needed for
+ // most model types. Durable storage writes, if not able to combine all change
+ // atomically, should save the metadata after the data changes, so that this
+ // merge will be re-driven by sync if is not completely saved during the
+ // current run.
base::Optional<syncer::ModelError> MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityDataMap entity_data_map) override;
+ syncer::EntityChangeList entity_data) override;
// Apply changes from the sync server locally.
// Please note that |entity_changes| might have fewer entries than
@@ -168,6 +167,8 @@ class ReadingListStore : public ReadingListModelStorage,
base::Clock* clock_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
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 7006ed358c6..dc872d7cde3 100644
--- a/chromium/components/reading_list/core/reading_list_store_unittest.cc
+++ b/chromium/components/reading_list/core/reading_list_store_unittest.cc
@@ -224,7 +224,7 @@ TEST_F(ReadingListStoreTest, SaveOneUnread) {
}
TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
- syncer::EntityDataMap remote_input;
+ syncer::EntityChangeList remote_input;
ReadingListEntry entry(GURL("http://read.example.com/"), "read title",
AdvanceAndGetTime(clock_));
entry.SetRead(true, AdvanceAndGetTime(clock_));
@@ -235,7 +235,8 @@ TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
data.client_tag_hash = "http://read.example.com/";
*data.specifics.mutable_reading_list() = *specifics;
- remote_input["http://read.example.com/"] = data.PassToPtr();
+ remote_input.push_back(syncer::EntityChange::CreateAdd(
+ "http://read.example.com/", data.PassToPtr()));
std::unique_ptr<syncer::MetadataChangeList> metadata_changes(
reading_list_store_->CreateMetadataChangeList());
@@ -248,7 +249,6 @@ TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
}
TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) {
- syncer::EntityDataMap remote_input;
ReadingListEntry entry(GURL("http://read.example.com/"), "read title",
AdvanceAndGetTime(clock_));
entry.SetRead(true, AdvanceAndGetTime(clock_));
@@ -271,7 +271,6 @@ TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) {
}
TEST_F(ReadingListStoreTest, ApplySyncChangesOneMerge) {
- syncer::EntityDataMap remote_input;
AdvanceAndGetTime(clock_);
model_->AddEntry(GURL("http://unread.example.com/"), "unread title",
reading_list::ADDED_VIA_CURRENT_APP);
@@ -302,7 +301,6 @@ TEST_F(ReadingListStoreTest, ApplySyncChangesOneIgnored) {
"old unread title", AdvanceAndGetTime(clock_));
old_entry.SetRead(true, AdvanceAndGetTime(clock_));
- syncer::EntityDataMap remote_input;
AdvanceAndGetTime(clock_);
model_->AddEntry(GURL("http://unread.example.com/"), "new unread title",
reading_list::ADDED_VIA_CURRENT_APP);
diff --git a/chromium/components/reading_list/core/reading_list_switches.cc b/chromium/components/reading_list/core/reading_list_switches.cc
deleted file mode 100644
index d6aba34c584..00000000000
--- a/chromium/components/reading_list/core/reading_list_switches.cc
+++ /dev/null
@@ -1,17 +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/reading_list/core/reading_list_switches.h"
-
-#include "build/build_config.h"
-#include "base/command_line.h"
-#include "components/reading_list/core/reading_list_enable_flags.h"
-
-namespace reading_list {
-namespace switches {
-bool IsReadingListEnabled() {
- return BUILDFLAG(ENABLE_READING_LIST);
-}
-} // namespace switches
-} // namespace reading_list
diff --git a/chromium/components/reading_list/core/reading_list_switches.h b/chromium/components/reading_list/core/reading_list_switches.h
deleted file mode 100644
index c99231c5def..00000000000
--- a/chromium/components/reading_list/core/reading_list_switches.h
+++ /dev/null
@@ -1,15 +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_READING_LIST_CORE_READING_LIST_SWITCHES_H_
-#define COMPONENTS_READING_LIST_CORE_READING_LIST_SWITCHES_H_
-
-namespace reading_list {
-namespace switches {
-// Whether Reading List is enabled on this device.
-bool IsReadingListEnabled();
-} // namespace switches
-} // namespace reading_list
-
-#endif // COMPONENTS_READING_LIST_CORE_READING_LIST_SWITCHES_H_
diff --git a/chromium/components/reading_list/features/BUILD.gn b/chromium/components/reading_list/features/BUILD.gn
new file mode 100644
index 00000000000..948c8be01fd
--- /dev/null
+++ b/chromium/components/reading_list/features/BUILD.gn
@@ -0,0 +1,21 @@
+import("//build/buildflag_header.gni")
+import("//components/reading_list/features/reading_list.gni")
+
+buildflag_header("reading_list_enable_flags") {
+ header = "reading_list_enable_flags.h"
+ _enabled = is_ios && enable_reading_list
+ flags = [ "ENABLE_READING_LIST=$_enabled" ]
+}
+
+source_set("flags") {
+ sources = [
+ "reading_list_switches.cc",
+ "reading_list_switches.h",
+ ]
+ deps = [
+ "//base",
+ ]
+ public_deps = [
+ "//components/reading_list/features:reading_list_enable_flags",
+ ]
+}
diff --git a/chromium/components/reading_list/core/reading_list.gni b/chromium/components/reading_list/features/reading_list.gni
index 26c4e1e7cb5..26c4e1e7cb5 100644
--- a/chromium/components/reading_list/core/reading_list.gni
+++ b/chromium/components/reading_list/features/reading_list.gni
diff --git a/chromium/components/reading_list/features/reading_list_switches.cc b/chromium/components/reading_list/features/reading_list_switches.cc
new file mode 100644
index 00000000000..93007e1c045
--- /dev/null
+++ b/chromium/components/reading_list/features/reading_list_switches.cc
@@ -0,0 +1,17 @@
+// 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/reading_list/features/reading_list_switches.h"
+
+#include "base/command_line.h"
+#include "build/build_config.h"
+#include "components/reading_list/features/reading_list_enable_flags.h"
+
+namespace reading_list {
+namespace switches {
+bool IsReadingListEnabled() {
+ return BUILDFLAG(ENABLE_READING_LIST);
+}
+} // namespace switches
+} // namespace reading_list
diff --git a/chromium/components/reading_list/features/reading_list_switches.h b/chromium/components/reading_list/features/reading_list_switches.h
new file mode 100644
index 00000000000..1681ddc46a9
--- /dev/null
+++ b/chromium/components/reading_list/features/reading_list_switches.h
@@ -0,0 +1,15 @@
+// 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_READING_LIST_FEATURES_READING_LIST_SWITCHES_H_
+#define COMPONENTS_READING_LIST_FEATURES_READING_LIST_SWITCHES_H_
+
+namespace reading_list {
+namespace switches {
+// Whether Reading List is enabled on this device.
+bool IsReadingListEnabled();
+} // namespace switches
+} // namespace reading_list
+
+#endif // COMPONENTS_READING_LIST_FEATURES_READING_LIST_SWITCHES_H_
diff --git a/chromium/components/reading_list/ios/BUILD.gn b/chromium/components/reading_list/ios/BUILD.gn
index 5d605fbb7b2..eb4ed058401 100644
--- a/chromium/components/reading_list/ios/BUILD.gn
+++ b/chromium/components/reading_list/ios/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
source_set("ios") {
+ configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"favicon_web_state_dispatcher.h",
"reading_list_model_bridge_observer.h",
diff --git a/chromium/components/reading_list/ios/reading_list_model_bridge_observer.h b/chromium/components/reading_list/ios/reading_list_model_bridge_observer.h
index 33a433ec5b2..dde8f6ca942 100644
--- a/chromium/components/reading_list/ios/reading_list_model_bridge_observer.h
+++ b/chromium/components/reading_list/ios/reading_list_model_bridge_observer.h
@@ -75,6 +75,8 @@ class ReadingListModelBridge : public ReadingListModelObserver {
const GURL& url) override;
__unsafe_unretained id<ReadingListModelBridgeObserver> observer_;
+
+ // TODO(crbug.com/729015): Refactor to remove the naked pointer.
ReadingListModel* model_; // weak
DISALLOW_COPY_AND_ASSIGN(ReadingListModelBridge);
diff --git a/chromium/components/reading_list/ios/reading_list_model_bridge_observer.mm b/chromium/components/reading_list/ios/reading_list_model_bridge_observer.mm
index a54a1a471f1..ff5836ee033 100644
--- a/chromium/components/reading_list/ios/reading_list_model_bridge_observer.mm
+++ b/chromium/components/reading_list/ios/reading_list_model_bridge_observer.mm
@@ -7,6 +7,10 @@
#include "components/reading_list/core/reading_list_entry.h"
#include "components/reading_list/core/reading_list_model.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
ReadingListModelBridge::ReadingListModelBridge(
id<ReadingListModelBridgeObserver> observer,
ReadingListModel* model)
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 d62e040cf78..95dae075636 100644
--- a/chromium/components/renderer_context_menu/context_menu_content_type.cc
+++ b/chromium/components/renderer_context_menu/context_menu_content_type.cc
@@ -77,6 +77,8 @@ bool ContextMenuContentType::SupportsGroup(int group) {
bool ContextMenuContentType::SupportsGroupInternal(int group) {
const bool has_link = !params_.unfiltered_link_url.is_empty();
const bool has_selection = !params_.selection_text.empty();
+ const bool is_password =
+ params_.input_field_type == WebContextMenuData::kInputFieldTypePassword;
switch (group) {
case ITEM_GROUP_CUSTOM:
@@ -136,7 +138,7 @@ bool ContextMenuContentType::SupportsGroupInternal(int group) {
return !params_.is_editable && has_selection;
case ITEM_GROUP_SEARCH_PROVIDER:
- return has_selection;
+ return has_selection && !is_password;
case ITEM_GROUP_PRINT: {
// Image menu items also imply print items.
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 f9663f333e3..709931ac804 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
@@ -333,10 +333,7 @@ void RenderViewContextMenuBase::MenuWillShow(ui::SimpleMenuModel* source) {
if (source != &menu_model_)
return;
- content::RenderWidgetHostView* view =
- source_web_contents_->GetRenderWidgetHostView();
- if (view)
- view->SetShowingContextMenu(true);
+ source_web_contents_->SetShowingContextMenu(true);
NotifyMenuShown();
}
@@ -346,10 +343,7 @@ void RenderViewContextMenuBase::MenuClosed(ui::SimpleMenuModel* source) {
if (source != &menu_model_)
return;
- content::RenderWidgetHostView* view =
- source_web_contents_->GetRenderWidgetHostView();
- if (view)
- view->SetShowingContextMenu(false);
+ source_web_contents_->SetShowingContextMenu(false);
source_web_contents_->NotifyContextMenuClosed(params_.custom_context);
}
diff --git a/chromium/components/resources/OWNERS b/chromium/components/resources/OWNERS
index f846201c619..eed6b23f6bd 100644
--- a/chromium/components/resources/OWNERS
+++ b/chromium/components/resources/OWNERS
@@ -20,11 +20,6 @@ per-file neterror*=mmenke@chromium.org
per-file ntp_tiles_resources.grdp=file://components/ntp_tiles/OWNERS
per-file proximity_auth*=tengs@chromium.org
per-file security_interstitials_resources.grdp=file://components/security_interstitials/OWNERS
-per-file supervised_user_error_page.grpd=file://components/supervised_user_error_page/OWNERS
-per-file sync_driver_resources.grdp=file:components/sync/OWNERS
-per-file version_ui*=achuith@chromium.org
-per-file version_ui*=bauerb@chromium.org
-per-file version_ui*=dbeam@chromium.org
-per-file version_ui*=estade@chromium.org
-per-file version_ui*=pam@chromium.org
-per-file version_ui*=xiyuan@chromium.org
+per-file supervised_user_error_page_resources.grdp=file://components/supervised_user_error_page/OWNERS
+per-file sync_driver_resources.grdp=file://components/sync/OWNERS
+per-file version_ui*=file://ui/webui/PLATFORM_OWNERS
diff --git a/chromium/components/resources/components_resources.grd b/chromium/components/resources/components_resources.grd
index cf622dcbc4c..ec0dffdd4bd 100644
--- a/chromium/components/resources/components_resources.grd
+++ b/chromium/components/resources/components_resources.grd
@@ -19,6 +19,7 @@
<part file="physical_web_ui_resources.grdp" />
<part file="printing_resources.grdp" />
<part file="proximity_auth_resources.grdp" />
+ <part file="safe_browsing_resources.grdp" />
<part file="security_interstitials_resources.grdp" />
<part file="signin_resources.grdp" />
<part file="supervised_user_error_page_resources.grdp" />
diff --git a/chromium/components/resources/flags_ui_resources.grdp b/chromium/components/resources/flags_ui_resources.grdp
index 383f224e528..f5e599c8fa7 100644
--- a/chromium/components/resources/flags_ui_resources.grdp
+++ b/chromium/components/resources/flags_ui_resources.grdp
@@ -2,7 +2,4 @@
<grit-part>
<include name="IDR_FLAGS_UI_FLAGS_HTML" file="../flags_ui/resources/flags.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_FLAGS_UI_FLAGS_JS" file="../flags_ui/resources/flags.js" type="BINDATA" />
- <if expr="is_ios">
- <include name="IDR_APPLE_FLAGS_HTML" file="../flags_ui/resources/apple_flags.html" flattenhtml="true" type="BINDATA" />
- </if>
</grit-part>
diff --git a/chromium/components/resources/ntp_tiles_resources.grdp b/chromium/components/resources/ntp_tiles_resources.grdp
index 7d8d6c0a63c..9367081c3b1 100644
--- a/chromium/components/resources/ntp_tiles_resources.grdp
+++ b/chromium/components/resources/ntp_tiles_resources.grdp
@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
<if expr="is_android or is_ios">
- <include name="IDR_POPULAR_SITES_INTERNALS_HTML" file="../ntp_tiles/webui/resources/popular_sites_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
- <include name="IDR_POPULAR_SITES_INTERNALS_JS" file="../ntp_tiles/webui/resources/popular_sites_internals.js" type="BINDATA" />
- <include name="IDR_POPULAR_SITES_INTERNALS_CSS" file="../ntp_tiles/webui/resources/popular_sites_internals.css" type="BINDATA" />
<if expr="_google_chrome">
<then>
<include name="IDR_DEFAULT_POPULAR_SITES_JSON" file="../ntp_tiles/resources/internal/default_popular_sites.json" type="BINDATA" />
diff --git a/chromium/components/resources/safe_browsing_resources.grdp b/chromium/components/resources/safe_browsing_resources.grdp
new file mode 100644
index 00000000000..1b2d2115da3
--- /dev/null
+++ b/chromium/components/resources/safe_browsing_resources.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <include name="IDR_SAFE_BROWSING_HTML" file="..\..\components\safe_browsing\web_ui\resources\safe_browsing.html" type="BINDATA" />
+ <include name="IDR_SAFE_BROWSING_CSS" file="..\..\components\safe_browsing\web_ui\resources\safe_browsing.css" type="BINDATA" />
+ <include name="IDR_SAFE_BROWSING_JS" file="..\..\components\safe_browsing\web_ui\resources\safe_browsing.js" type="BINDATA" />
+</grit-part>
diff --git a/chromium/components/resources/security_interstitials_resources.grdp b/chromium/components/resources/security_interstitials_resources.grdp
index 2fb0f05ce92..4d72526b941 100644
--- a/chromium/components/resources/security_interstitials_resources.grdp
+++ b/chromium/components/resources/security_interstitials_resources.grdp
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
- <include name="IDR_SECURITY_INTERSTITIAL_UI_HTML" file="../security_interstitials/core/browser/resources/interstitial_ui.html" flattenhtml="true" type="BINDATA" />
- <include name="IDR_SECURITY_INTERSTITIAL_HTML" file="../security_interstitials/core/browser/resources/interstitial_v2.html" flattenhtml="true" type="BINDATA" />
+ <include name="IDR_SECURITY_INTERSTITIAL_UI_HTML" file="../security_interstitials/core/browser/resources/list_of_interstitials.html" flattenhtml="true" type="BINDATA" />
+ <include name="IDR_SECURITY_INTERSTITIAL_HTML" file="../security_interstitials/core/browser/resources/interstitial_large.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_SECURITY_INTERSTITIAL_QUIET_HTML" file="../security_interstitials/core/browser/resources/interstitial_webview_quiet.html" flattenhtml="true" type="BINDATA" />
-</grit-part> \ No newline at end of file
+</grit-part>
diff --git a/chromium/components/safe_browsing/BUILD.gn b/chromium/components/safe_browsing/BUILD.gn
index 06bf6321c2c..f09e7c62cff 100644
--- a/chromium/components/safe_browsing/BUILD.gn
+++ b/chromium/components/safe_browsing/BUILD.gn
@@ -19,14 +19,17 @@ source_set("safe_browsing") {
"base_ui_manager.cc",
"base_ui_manager.h",
]
-
+ public_deps = [
+ "//components/security_interstitials/content:security_interstitial_page",
+ ]
deps = [
":base_ping_manager",
+ ":features",
"//base:base",
"//base:i18n",
"//components/safe_browsing/common:safe_browsing_prefs",
+ "//components/safe_browsing/web_ui:constants",
"//components/safe_browsing_db:database_manager",
- "//components/security_interstitials/content:security_interstitial_page",
"//components/security_interstitials/core:core",
"//content/public/browser:browser",
"//content/public/common:common",
@@ -68,3 +71,14 @@ source_set("base_ping_manager_unittest") {
"//testing/gtest",
]
}
+
+static_library("features") {
+ sources = [
+ "features.cc",
+ "features.h",
+ ]
+
+ deps = [
+ "//base:base",
+ ]
+}
diff --git a/chromium/components/safe_browsing/DEPS b/chromium/components/safe_browsing/DEPS
index ccceca98f91..aa87f2cc685 100644
--- a/chromium/components/safe_browsing/DEPS
+++ b/chromium/components/safe_browsing/DEPS
@@ -6,6 +6,7 @@ include_rules = [
"+content/public/browser",
"+content/public/common",
"+google_apis",
+ "+mojo/public/cpp",
"+net/base",
"+net/log",
"+net/traffic_annotation",
diff --git a/chromium/components/safe_browsing/OWNERS b/chromium/components/safe_browsing/OWNERS
index 599d7d6b457..3f03255af9e 100644
--- a/chromium/components/safe_browsing/OWNERS
+++ b/chromium/components/safe_browsing/OWNERS
@@ -1,6 +1,6 @@
jialiul@chromium.org
-mattm@chromium.org
nparker@chromium.org
+vakh@chromium.org
# For componentization
timvolodine@chromium.org
diff --git a/chromium/components/safe_browsing/base_blocking_page.cc b/chromium/components/safe_browsing/base_blocking_page.cc
index 9ddae815dc1..6c169759e6f 100644
--- a/chromium/components/safe_browsing/base_blocking_page.cc
+++ b/chromium/components/safe_browsing/base_blocking_page.cc
@@ -79,6 +79,7 @@ BaseBlockingPage::CreateDefaultDisplayOptions(
false, // is_extended_reporting
false, // is_scout
false, // kSafeBrowsingProceedAnywayDisabled
+ false, // should_open_links_in_new_tab
"cpn_safe_browsing"); // help_center_article_link
}
@@ -103,7 +104,8 @@ void BaseBlockingPage::ShowBlockingPage(
BaseBlockingPage* blocking_page = new BaseBlockingPage(
ui_manager, web_contents, entry ? entry->GetURL() : GURL(),
unsafe_resources,
- CreateControllerClient(web_contents, unsafe_resources, ui_manager),
+ CreateControllerClient(web_contents, unsafe_resources, ui_manager,
+ nullptr),
CreateDefaultDisplayOptions(unsafe_resources));
blocking_page->Show();
}
@@ -264,6 +266,8 @@ std::string BaseBlockingPage::GetExtraMetricsSuffix(
return "from_device_v4";
case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
return "from_client_side_detection";
+ case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
+ return "from_password_protection_service";
case safe_browsing::ThreatSource::UNKNOWN:
break;
}
@@ -281,13 +285,14 @@ BaseBlockingPage::GetInterstitialReason(
const BaseUIManager::UnsafeResource& resource = *iter;
safe_browsing::SBThreatType threat_type = resource.threat_type;
if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
- threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
+ threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE) {
return BaseSafeBrowsingErrorUI::SB_REASON_MALWARE;
} else if (threat_type == SB_THREAT_TYPE_URL_UNWANTED) {
harmful = true;
} else {
DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
- threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL);
+ threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
+ threat_type == SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING);
}
}
@@ -335,7 +340,8 @@ std::unique_ptr<SecurityInterstitialControllerClient>
BaseBlockingPage::CreateControllerClient(
content::WebContents* web_contents,
const UnsafeResourceList& unsafe_resources,
- BaseUIManager* ui_manager) {
+ BaseUIManager* ui_manager,
+ PrefService* pref_service) {
history::HistoryService* history_service =
ui_manager->history_service(web_contents);
@@ -345,7 +351,7 @@ BaseBlockingPage::CreateControllerClient(
history_service);
return base::MakeUnique<SecurityInterstitialControllerClient>(
- web_contents, std::move(metrics_helper), nullptr, /* prefs */
+ web_contents, std::move(metrics_helper), pref_service,
ui_manager->app_locale(), ui_manager->default_safe_page());
}
@@ -358,4 +364,14 @@ void BaseBlockingPage::set_sb_error_ui(
sb_error_ui_ = std::move(sb_error_ui);
}
+// static
+bool BaseBlockingPage::ShouldReportThreatDetails(SBThreatType threat_type) {
+ return threat_type == SB_THREAT_TYPE_URL_PHISHING ||
+ threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+ threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
+ threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
+ threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE ||
+ threat_type == SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING;
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/base_blocking_page.h b/chromium/components/safe_browsing/base_blocking_page.h
index d4a8c674cdd..837498b9e5a 100644
--- a/chromium/components/safe_browsing/base_blocking_page.h
+++ b/chromium/components/safe_browsing/base_blocking_page.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "components/security_interstitials/content/security_interstitial_page.h"
#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
#include "components/security_interstitials/core/metrics_helper.h"
@@ -52,6 +53,9 @@ class BaseBlockingPage
void OnDontProceed() override;
void CommandReceived(const std::string& command) override;
+ // Checks the threat type to decide if we should report ThreatDetails.
+ static bool ShouldReportThreatDetails(SBThreatType threat_type);
+
protected:
// Don't instantiate this class directly, use ShowBlockingPage instead.
BaseBlockingPage(
@@ -115,7 +119,8 @@ class BaseBlockingPage
security_interstitials::SecurityInterstitialControllerClient>
CreateControllerClient(content::WebContents* web_contents,
const UnsafeResourceList& unsafe_resources,
- BaseUIManager* ui_manager);
+ BaseUIManager* ui_manager,
+ PrefService* pref_service);
int GetHTMLTemplateId() override;
diff --git a/chromium/components/safe_browsing/base_ping_manager.cc b/chromium/components/safe_browsing/base_ping_manager.cc
index 30fdf1c079b..cc89412fd98 100644
--- a/chromium/components/safe_browsing/base_ping_manager.cc
+++ b/chromium/components/safe_browsing/base_ping_manager.cc
@@ -199,9 +199,11 @@ GURL BasePingManager::SafeBrowsingHitUrl(
DCHECK(hit_report.threat_type == SB_THREAT_TYPE_URL_MALWARE ||
hit_report.threat_type == SB_THREAT_TYPE_URL_PHISHING ||
hit_report.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
- hit_report.threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL ||
- hit_report.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL ||
- hit_report.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL);
+ hit_report.threat_type == SB_THREAT_TYPE_URL_BINARY_MALWARE ||
+ hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
+ hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE ||
+ hit_report.threat_type ==
+ SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING);
std::string url = ProtocolManagerHelper::ComposeUrl(
url_prefix_, "report", client_name_, version_, std::string(),
hit_report.extended_reporting_level);
@@ -217,15 +219,18 @@ GURL BasePingManager::SafeBrowsingHitUrl(
case SB_THREAT_TYPE_URL_UNWANTED:
threat_list = "uwsblhit";
break;
- case SB_THREAT_TYPE_BINARY_MALWARE_URL:
+ case SB_THREAT_TYPE_URL_BINARY_MALWARE:
threat_list = "binurlhit";
break;
- case SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL:
+ case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
threat_list = "phishcsdhit";
break;
- case SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL:
+ case SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
threat_list = "malcsdhit";
break;
+ case SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING:
+ threat_list = "phishpphit";
+ break;
default:
NOTREACHED();
}
@@ -247,6 +252,9 @@ GURL BasePingManager::SafeBrowsingHitUrl(
case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
threat_source = "csd";
break;
+ case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
+ threat_source = "pps";
+ break;
case safe_browsing::ThreatSource::UNKNOWN:
NOTREACHED();
}
diff --git a/chromium/components/safe_browsing/base_ping_manager_unittest.cc b/chromium/components/safe_browsing/base_ping_manager_unittest.cc
index 467611af9ae..f65676e3ef8 100644
--- a/chromium/components/safe_browsing/base_ping_manager_unittest.cc
+++ b/chromium/components/safe_browsing/base_ping_manager_unittest.cc
@@ -125,7 +125,7 @@ TEST_F(BasePingManagerTest, TestSafeBrowsingHitUrl) {
{
HitReport hp(base_hp);
- hp.threat_type = SB_THREAT_TYPE_BINARY_MALWARE_URL;
+ hp.threat_type = SB_THREAT_TYPE_URL_BINARY_MALWARE;
hp.threat_source = ThreatSource::REMOTE;
hp.extended_reporting_level = SBER_LEVEL_OFF;
hp.is_metrics_reporting_active = true;
@@ -143,7 +143,7 @@ TEST_F(BasePingManagerTest, TestSafeBrowsingHitUrl) {
{
HitReport hp(base_hp);
- hp.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL;
+ hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING;
hp.threat_source = ThreatSource::LOCAL_PVER4;
hp.extended_reporting_level = SBER_LEVEL_OFF;
hp.is_metrics_reporting_active = false;
@@ -161,7 +161,7 @@ TEST_F(BasePingManagerTest, TestSafeBrowsingHitUrl) {
{
HitReport hp(base_hp);
- hp.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL;
+ hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
hp.threat_source = ThreatSource::LOCAL_PVER4;
hp.extended_reporting_level = SBER_LEVEL_OFF;
hp.is_metrics_reporting_active = false;
@@ -180,7 +180,7 @@ TEST_F(BasePingManagerTest, TestSafeBrowsingHitUrl) {
// Same as above, but add population_id
{
HitReport hp(base_hp);
- hp.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL;
+ hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
hp.threat_source = ThreatSource::LOCAL_PVER4;
hp.extended_reporting_level = SBER_LEVEL_OFF;
hp.is_metrics_reporting_active = false;
@@ -270,7 +270,7 @@ TEST_F(BasePingManagerTest, TestReportSafeBrowsingHit) {
hp.malicious_url = GURL("http://malicious.url.com");
hp.page_url = GURL("http://page.url.com");
hp.referrer_url = GURL("http://referrer.url.com");
- hp.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL;
+ hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
hp.threat_source = ThreatSource::LOCAL_PVER4;
hp.extended_reporting_level = SBER_LEVEL_OFF;
hp.is_metrics_reporting_active = false;
diff --git a/chromium/components/safe_browsing/base_resource_throttle.cc b/chromium/components/safe_browsing/base_resource_throttle.cc
index 87a8812e2bf..8de25547cfe 100644
--- a/chromium/components/safe_browsing/base_resource_throttle.cc
+++ b/chromium/components/safe_browsing/base_resource_throttle.cc
@@ -4,10 +4,13 @@
#include "components/safe_browsing/base_resource_throttle.h"
+#include <utility>
+
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/web_ui/constants.h"
#include "components/safe_browsing_db/util.h"
#include "components/security_interstitials/content/unsafe_resource.h"
#include "content/public/browser/browser_thread.h"
@@ -69,10 +72,12 @@ std::unique_ptr<base::Value> NetLogStringCallback(const char* name,
BaseResourceThrottle::BaseResourceThrottle(
const net::URLRequest* request,
content::ResourceType resource_type,
+ SBThreatTypeSet threat_types,
scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
scoped_refptr<BaseUIManager> ui_manager)
: ui_manager_(ui_manager),
threat_type_(SB_THREAT_TYPE_SAFE),
+ threat_types_(std::move(threat_types)),
database_manager_(database_manager),
request_(request),
state_(STATE_NONE),
@@ -82,19 +87,6 @@ BaseResourceThrottle::BaseResourceThrottle(
net::NetLogWithSource::Make(request->net_log().net_log(),
NetLogSourceType::SAFE_BROWSING)) {}
-// static
-BaseResourceThrottle* BaseResourceThrottle::MaybeCreate(
- net::URLRequest* request,
- content::ResourceType resource_type,
- scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
- scoped_refptr<BaseUIManager> ui_manager) {
- if (database_manager->IsSupported()) {
- return new BaseResourceThrottle(request, resource_type,
- database_manager, ui_manager);
- }
- return nullptr;
-}
-
BaseResourceThrottle::~BaseResourceThrottle() {
if (defer_state_ != DEFERRED_NONE) {
EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr);
@@ -358,7 +350,11 @@ bool BaseResourceThrottle::CheckUrl(const GURL& url) {
UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_,
content::RESOURCE_TYPE_LAST_TYPE);
- if (database_manager_->CheckBrowseUrl(url, this)) {
+ if (CheckWebUIUrls(url)) {
+ return false;
+ }
+
+ if (database_manager_->CheckBrowseUrl(url, threat_types_, this)) {
threat_type_ = SB_THREAT_TYPE_SAFE;
ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay.
return true;
@@ -378,6 +374,28 @@ bool BaseResourceThrottle::CheckUrl(const GURL& url) {
return false;
}
+bool BaseResourceThrottle::CheckWebUIUrls(const GURL& url) {
+ DCHECK(threat_type_ == safe_browsing::SB_THREAT_TYPE_SAFE);
+ if (url == kChromeUISafeBrowsingMatchMalwareUrl) {
+ threat_type_ = safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+ } else if (url == kChromeUISafeBrowsingMatchPhishingUrl) {
+ threat_type_ = safe_browsing::SB_THREAT_TYPE_URL_PHISHING;
+ } else if (url == kChromeUISafeBrowsingMatchUnwantedUrl) {
+ threat_type_ = safe_browsing::SB_THREAT_TYPE_URL_UNWANTED;
+ }
+
+ if (threat_type_ != safe_browsing::SB_THREAT_TYPE_SAFE) {
+ state_ = STATE_CHECKING_URL;
+ url_being_checked_ = url;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&BaseResourceThrottle::OnCheckBrowseUrlResult, AsWeakPtr(),
+ url, threat_type_, ThreatMetadata()));
+ return true;
+ }
+ return false;
+}
+
void BaseResourceThrottle::OnCheckUrlTimeout() {
CHECK_EQ(state_, STATE_CHECKING_URL);
diff --git a/chromium/components/safe_browsing/base_resource_throttle.h b/chromium/components/safe_browsing/base_resource_throttle.h
index 1fe92f31603..38de36f5d28 100644
--- a/chromium/components/safe_browsing/base_resource_throttle.h
+++ b/chromium/components/safe_browsing/base_resource_throttle.h
@@ -13,6 +13,7 @@
#include "base/timer/timer.h"
#include "components/safe_browsing/base_ui_manager.h"
#include "components/safe_browsing_db/database_manager.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "components/security_interstitials/content/unsafe_resource.h"
#include "content/public/browser/resource_throttle.h"
#include "content/public/common/resource_type.h"
@@ -50,15 +51,6 @@ class BaseResourceThrottle
public SafeBrowsingDatabaseManager::Client,
public base::SupportsWeakPtr<BaseResourceThrottle> {
public:
- // Construct a BaseResourceThrottle, or return nullptr if we
- // cannot access the safe browsing API on Android
- static BaseResourceThrottle* MaybeCreate(
- net::URLRequest* request,
- content::ResourceType resource_type,
- scoped_refptr<SafeBrowsingDatabaseManager>
- database_manager,
- scoped_refptr<BaseUIManager> ui_manager);
-
// content::ResourceThrottle implementation (called on IO thread):
void WillStartRequest(bool* defer) override;
void WillRedirectRequest(const net::RedirectInfo& redirect_info,
@@ -74,12 +66,16 @@ class BaseResourceThrottle
SBThreatType threat_type,
const ThreatMetadata& metadata) override;
+ // Called on the IO thread when the user has decided to proceed with the
+ // current request, or go back.
+ void OnBlockingPageComplete(bool proceed);
+
protected:
BaseResourceThrottle(
const net::URLRequest* request,
content::ResourceType resource_type,
- scoped_refptr<SafeBrowsingDatabaseManager>
- database_manager,
+ SBThreatTypeSet threat_types,
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
scoped_refptr<BaseUIManager> ui_manager);
~BaseResourceThrottle() override;
@@ -104,6 +100,18 @@ class BaseResourceThrottle
// page, but may be overridden in a subclass.
virtual void CancelResourceLoad();
+ // Starts displaying the safe browsing interstitial page. Called on the UI
+ // thread.
+ static void StartDisplayingBlockingPage(
+ const base::WeakPtr<BaseResourceThrottle>& throttle,
+ scoped_refptr<BaseUIManager> ui_manager,
+ const security_interstitials::UnsafeResource& resource);
+
+ // Starts running |url| through the safe browsing check. Returns true if the
+ // URL is safe to visit. Otherwise returns false and will call
+ // OnBrowseUrlResult() when the check has completed.
+ virtual bool CheckUrl(const GURL& url);
+
scoped_refptr<BaseUIManager> ui_manager();
private:
@@ -126,28 +134,17 @@ class BaseResourceThrottle
DEFERRED_PROCESSING,
};
- scoped_refptr<BaseUIManager> ui_manager_;
+ // Checks if |url| is one of the hardcoded WebUI match URLs. Returns true if
+ // the URL is one of the hardcoded URLs and will post a task to
+ // OnCheckBrowseUrlResult.
+ bool CheckWebUIUrls(const GURL& url);
- // Called on the IO thread when the user has decided to proceed with the
- // current request, or go back.
- void OnBlockingPageComplete(bool proceed);
-
- // Starts running |url| through the safe browsing check. Returns true if the
- // URL is safe to visit. Otherwise returns false and will call
- // OnBrowseUrlResult() when the check has completed.
- bool CheckUrl(const GURL& url);
+ scoped_refptr<BaseUIManager> ui_manager_;
// Callback for when the safe browsing check (which was initiated by
// StartCheckingUrl()) has taken longer than kCheckUrlTimeoutMs.
void OnCheckUrlTimeout();
- // Starts displaying the safe browsing interstitial page. Called on the UI
- // thread.
- static void StartDisplayingBlockingPage(
- const base::WeakPtr<BaseResourceThrottle>& throttle,
- scoped_refptr<BaseUIManager> ui_manager,
- const security_interstitials::UnsafeResource& resource);
-
void ResumeRequest();
// For marking network events. |name| and |value| can be null.
@@ -177,6 +174,7 @@ class BaseResourceThrottle
GURL unchecked_redirect_url_;
GURL url_being_checked_;
+ const SBThreatTypeSet threat_types_;
scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
const net::URLRequest* request_;
diff --git a/chromium/components/safe_browsing/browser/BUILD.gn b/chromium/components/safe_browsing/browser/BUILD.gn
index 8d7b1880c50..1ad4965da44 100644
--- a/chromium/components/safe_browsing/browser/BUILD.gn
+++ b/chromium/components/safe_browsing/browser/BUILD.gn
@@ -6,6 +6,12 @@ import("//build/config/features.gni")
source_set("browser") {
sources = [
+ "browser_url_loader_throttle.cc",
+ "browser_url_loader_throttle.h",
+ "mojo_safe_browsing_impl.cc",
+ "mojo_safe_browsing_impl.h",
+ "safe_browsing_url_checker_impl.cc",
+ "safe_browsing_url_checker_impl.h",
"safe_browsing_url_request_context_getter.cc",
"safe_browsing_url_request_context_getter.h",
"threat_details.cc",
@@ -14,6 +20,7 @@ source_set("browser") {
"threat_details_cache.h",
"threat_details_history.cc",
"threat_details_history.h",
+ "url_checker_delegate.h",
]
deps = [
@@ -22,6 +29,7 @@ source_set("browser") {
"//components/safe_browsing:csd_proto",
"//components/safe_browsing:safe_browsing",
"//components/safe_browsing/common:common",
+ "//components/safe_browsing_db:database_manager",
"//components/security_interstitials/content:security_interstitial_page",
"//content/public/browser:browser",
"//net:extras",
diff --git a/chromium/components/safe_browsing/browser/DEPS b/chromium/components/safe_browsing/browser/DEPS
index 37c91123491..d98a2a4dfa1 100644
--- a/chromium/components/safe_browsing/browser/DEPS
+++ b/chromium/components/safe_browsing/browser/DEPS
@@ -2,9 +2,10 @@ include_rules = [
"+components/history/core/browser",
"+components/safe_browsing/csd.pb.h",
"+content/public/browser",
+ "+ipc/ipc_message.h",
"+net/cookies",
"+net/extras",
"+net/http",
"+net/ssl",
"+net/traffic_annotation",
-] \ No newline at end of file
+]
diff --git a/chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc
new file mode 100644
index 00000000000..f841da38bc5
--- /dev/null
+++ b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc
@@ -0,0 +1,98 @@
+// 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/safe_browsing/browser/browser_url_loader_throttle.h"
+
+#include "base/logging.h"
+#include "components/safe_browsing/browser/safe_browsing_url_checker_impl.h"
+#include "components/safe_browsing/browser/url_checker_delegate.h"
+#include "net/url_request/redirect_info.h"
+
+namespace safe_browsing {
+
+// static
+std::unique_ptr<BrowserURLLoaderThrottle> BrowserURLLoaderThrottle::MaybeCreate(
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+ const base::Callback<content::WebContents*()>& web_contents_getter) {
+ if (!url_checker_delegate ||
+ !url_checker_delegate->GetDatabaseManager()->IsSupported()) {
+ return nullptr;
+ }
+
+ return base::WrapUnique<BrowserURLLoaderThrottle>(
+ new BrowserURLLoaderThrottle(std::move(url_checker_delegate),
+ web_contents_getter));
+}
+
+BrowserURLLoaderThrottle::BrowserURLLoaderThrottle(
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+ const base::Callback<content::WebContents*()>& web_contents_getter)
+ : url_checker_delegate_(std::move(url_checker_delegate)),
+ web_contents_getter_(web_contents_getter) {}
+
+BrowserURLLoaderThrottle::~BrowserURLLoaderThrottle() = default;
+
+void BrowserURLLoaderThrottle::WillStartRequest(
+ const GURL& url,
+ int load_flags,
+ content::ResourceType resource_type,
+ bool* defer) {
+ DCHECK_EQ(0u, pending_checks_);
+ DCHECK(!blocked_);
+ DCHECK(!url_checker_);
+
+ pending_checks_++;
+ url_checker_ = base::MakeUnique<SafeBrowsingUrlCheckerImpl>(
+ load_flags, resource_type, std::move(url_checker_delegate_),
+ web_contents_getter_);
+ url_checker_->CheckUrl(
+ url, base::BindOnce(&BrowserURLLoaderThrottle::OnCheckUrlResult,
+ base::Unretained(this)));
+}
+
+void BrowserURLLoaderThrottle::WillRedirectRequest(
+ const net::RedirectInfo& redirect_info,
+ bool* defer) {
+ // If |blocked_| is true, the resource load has been canceled and there
+ // shouldn't be such a notification.
+ DCHECK(!blocked_);
+
+ pending_checks_++;
+ url_checker_->CheckUrl(
+ redirect_info.new_url,
+ base::BindOnce(&BrowserURLLoaderThrottle::OnCheckUrlResult,
+ base::Unretained(this)));
+}
+
+void BrowserURLLoaderThrottle::WillProcessResponse(bool* defer) {
+ // If |blocked_| is true, the resource load has been canceled and there
+ // shouldn't be such a notification.
+ DCHECK(!blocked_);
+
+ if (pending_checks_ > 0)
+ *defer = true;
+}
+
+void BrowserURLLoaderThrottle::OnCheckUrlResult(bool safe) {
+ if (blocked_)
+ return;
+
+ DCHECK_LT(0u, pending_checks_);
+ pending_checks_--;
+
+ if (safe) {
+ if (pending_checks_ == 0) {
+ // The resource load is not necessarily deferred, in that case Resume() is
+ // a no-op.
+ delegate_->Resume();
+ }
+ } else {
+ url_checker_.reset();
+ blocked_ = true;
+ pending_checks_ = 0;
+ delegate_->CancelWithError(net::ERR_ABORTED);
+ }
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/browser/browser_url_loader_throttle.h b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.h
new file mode 100644
index 00000000000..f57ce492fd6
--- /dev/null
+++ b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.h
@@ -0,0 +1,70 @@
+// 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_SAFE_BROWSING_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
+#define COMPONENTS_SAFE_BROWSING_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/common/url_loader_throttle.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace safe_browsing {
+
+class UrlCheckerDelegate;
+class SafeBrowsingUrlCheckerImpl;
+
+// BrowserURLLoaderThrottle is used in the browser process to query
+// SafeBrowsing to determine whether a URL and also its redirect URLs are safe
+// to load. It defers response processing until all URL checks are completed;
+// cancels the load if any URLs turn out to be bad.
+// Used when --enable-network-service is in effect.
+class BrowserURLLoaderThrottle : public content::URLLoaderThrottle {
+ public:
+ static std::unique_ptr<BrowserURLLoaderThrottle> MaybeCreate(
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+ const base::Callback<content::WebContents*()>& web_contents_getter);
+
+ ~BrowserURLLoaderThrottle() override;
+
+ // content::URLLoaderThrottle implementation.
+ void WillStartRequest(const GURL& url,
+ int load_flags,
+ content::ResourceType resource_type,
+ bool* defer) override;
+ void WillRedirectRequest(const net::RedirectInfo& redirect_info,
+ bool* defer) override;
+ void WillProcessResponse(bool* defer) override;
+
+ private:
+ // |web_contents_getter| is used for displaying SafeBrowsing UI when
+ // necessary.
+ BrowserURLLoaderThrottle(
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+ const base::Callback<content::WebContents*()>& web_contents_getter);
+
+ void OnCheckUrlResult(bool safe);
+
+ // The following member stays valid until |url_checker_| is created.
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate_;
+
+ base::Callback<content::WebContents*()> web_contents_getter_;
+
+ std::unique_ptr<SafeBrowsingUrlCheckerImpl> url_checker_;
+
+ size_t pending_checks_ = 0;
+ bool blocked_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserURLLoaderThrottle);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
diff --git a/chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.cc b/chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.cc
new file mode 100644
index 00000000000..19195203517
--- /dev/null
+++ b/chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.cc
@@ -0,0 +1,99 @@
+// 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/safe_browsing/browser/mojo_safe_browsing_impl.h"
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "components/safe_browsing/browser/safe_browsing_url_checker_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/resource_type.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "net/base/load_flags.h"
+
+namespace safe_browsing {
+namespace {
+
+content::WebContents* GetWebContentsFromID(int render_process_id,
+ int render_frame_id) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ content::RenderFrameHost* render_frame_host =
+ content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (!render_frame_host)
+ return nullptr;
+
+ return content::WebContents::FromRenderFrameHost(render_frame_host);
+}
+
+// This class wraps a base::OnceCallback<void(bool)> and runs it on destruction,
+// if it hasn't been run yet.
+class BooleanCallbackWrapper {
+ public:
+ using BooleanCallback = base::OnceCallback<void(bool)>;
+
+ explicit BooleanCallbackWrapper(BooleanCallback callback)
+ : callback_(std::move(callback)) {}
+ ~BooleanCallbackWrapper() {
+ if (callback_)
+ Run(false);
+ }
+
+ void Run(bool value) { std::move(callback_).Run(value); }
+
+ private:
+ BooleanCallback callback_;
+};
+
+} // namespace
+
+MojoSafeBrowsingImpl::MojoSafeBrowsingImpl(
+ scoped_refptr<UrlCheckerDelegate> delegate,
+ int render_process_id)
+ : delegate_(std::move(delegate)), render_process_id_(render_process_id) {}
+
+MojoSafeBrowsingImpl::~MojoSafeBrowsingImpl() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+// static
+void MojoSafeBrowsingImpl::MaybeCreate(
+ int render_process_id,
+ const base::Callback<UrlCheckerDelegate*()>& delegate_getter,
+ mojom::SafeBrowsingRequest request) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ scoped_refptr<UrlCheckerDelegate> delegate = delegate_getter.Run();
+
+ if (!delegate || !delegate->GetDatabaseManager()->IsSupported())
+ return;
+
+ mojo::MakeStrongBinding(base::WrapUnique(new MojoSafeBrowsingImpl(
+ std::move(delegate), render_process_id)),
+ std::move(request));
+}
+
+void MojoSafeBrowsingImpl::CreateCheckerAndCheck(
+ int32_t render_frame_id,
+ mojom::SafeBrowsingUrlCheckerRequest request,
+ const GURL& url,
+ int32_t load_flags,
+ content::ResourceType resource_type,
+ CreateCheckerAndCheckCallback callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ auto checker_impl = base::MakeUnique<SafeBrowsingUrlCheckerImpl>(
+ static_cast<int>(load_flags), resource_type, delegate_,
+ base::Bind(&GetWebContentsFromID, render_process_id_,
+ static_cast<int>(render_frame_id)));
+
+ checker_impl->CheckUrl(
+ url, base::BindOnce(
+ &BooleanCallbackWrapper::Run,
+ base::Owned(new BooleanCallbackWrapper(std::move(callback)))));
+ mojo::MakeStrongBinding(std::move(checker_impl), std::move(request));
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.h b/chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.h
new file mode 100644
index 00000000000..fb0416a009c
--- /dev/null
+++ b/chromium/components/safe_browsing/browser/mojo_safe_browsing_impl.h
@@ -0,0 +1,47 @@
+// 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_SAFE_BROWSING_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
+#define COMPONENTS_SAFE_BROWSING_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/browser/url_checker_delegate.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "ipc/ipc_message.h"
+
+namespace safe_browsing {
+
+// This class implements the Mojo interface for renderers to perform
+// SafeBrowsing URL checks.
+class MojoSafeBrowsingImpl : public mojom::SafeBrowsing {
+ public:
+ ~MojoSafeBrowsingImpl() override;
+
+ static void MaybeCreate(
+ int render_process_id,
+ const base::Callback<UrlCheckerDelegate*()>& delegate_getter,
+ mojom::SafeBrowsingRequest request);
+
+ private:
+ MojoSafeBrowsingImpl(scoped_refptr<UrlCheckerDelegate> delegate,
+ int render_process_id);
+
+ // mojom::SafeBrowsing implementation.
+ void CreateCheckerAndCheck(int32_t render_frame_id,
+ mojom::SafeBrowsingUrlCheckerRequest request,
+ const GURL& url,
+ int32_t load_flags,
+ content::ResourceType resource_type,
+ CreateCheckerAndCheckCallback callback) override;
+
+ scoped_refptr<UrlCheckerDelegate> delegate_;
+ int render_process_id_ = MSG_ROUTING_NONE;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoSafeBrowsingImpl);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
diff --git a/chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc b/chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
new file mode 100644
index 00000000000..174e75e939b
--- /dev/null
+++ b/chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
@@ -0,0 +1,163 @@
+// 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/safe_browsing/browser/safe_browsing_url_checker_impl.h"
+
+#include "components/safe_browsing/browser/url_checker_delegate.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/load_flags.h"
+
+namespace safe_browsing {
+namespace {
+
+// TODO(yzshen): Share such value with safe_browsing::BaseResourceThrottle.
+// Maximum time in milliseconds to wait for the SafeBrowsing service reputation
+// check. After this amount of time the outstanding check will be aborted, and
+// the resource will be treated as if it were safe.
+const int kCheckUrlTimeoutMs = 5000;
+
+} // namespace
+
+SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
+ int load_flags,
+ content::ResourceType resource_type,
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+ const base::Callback<content::WebContents*()>& web_contents_getter)
+ : load_flags_(load_flags),
+ resource_type_(resource_type),
+ web_contents_getter_(web_contents_getter),
+ url_checker_delegate_(std::move(url_checker_delegate)),
+ database_manager_(url_checker_delegate_->GetDatabaseManager()),
+ weak_factory_(this) {}
+
+SafeBrowsingUrlCheckerImpl::~SafeBrowsingUrlCheckerImpl() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ if (state_ == STATE_CHECKING_URL)
+ database_manager_->CancelCheck(this);
+}
+
+void SafeBrowsingUrlCheckerImpl::CheckUrl(const GURL& url,
+ CheckUrlCallback callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url;
+ urls_.push_back(url);
+ callbacks_.push_back(std::move(callback));
+
+ ProcessUrls();
+}
+
+void SafeBrowsingUrlCheckerImpl::OnCheckBrowseUrlResult(
+ const GURL& url,
+ SBThreatType threat_type,
+ const ThreatMetadata& metadata) {
+ DCHECK_EQ(STATE_CHECKING_URL, state_);
+ DCHECK_LT(next_index_, urls_.size());
+ DCHECK_EQ(urls_[next_index_], url);
+
+ timer_.Stop();
+ if (threat_type == SB_THREAT_TYPE_SAFE) {
+ state_ = STATE_NONE;
+ std::move(callbacks_[next_index_]).Run(true);
+ next_index_++;
+ ProcessUrls();
+ return;
+ }
+
+ if (load_flags_ & net::LOAD_PREFETCH) {
+ // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING.
+ if (resource_type_ == content::RESOURCE_TYPE_MAIN_FRAME)
+ url_checker_delegate_->MaybeDestroyPrerenderContents(
+ web_contents_getter_);
+ BlockAndProcessUrls();
+ return;
+ }
+
+ security_interstitials::UnsafeResource resource;
+ resource.url = url;
+ resource.original_url = urls_[0];
+ if (urls_.size() > 1)
+ resource.redirect_urls = std::vector<GURL>(urls_.begin() + 1, urls_.end());
+ resource.is_subresource = resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME;
+ resource.is_subframe = resource_type_ == content::RESOURCE_TYPE_SUB_FRAME;
+ resource.threat_type = threat_type;
+ resource.threat_metadata = metadata;
+ resource.callback =
+ base::Bind(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete,
+ weak_factory_.GetWeakPtr());
+ resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::IO);
+ resource.web_contents_getter = web_contents_getter_;
+ resource.threat_source = database_manager_->GetThreatSource();
+
+ state_ = STATE_DISPLAYING_BLOCKING_PAGE;
+ url_checker_delegate_->StartDisplayingBlockingPageHelper(resource);
+}
+
+void SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout() {
+ database_manager_->CancelCheck(this);
+
+ OnCheckBrowseUrlResult(urls_[next_index_], safe_browsing::SB_THREAT_TYPE_SAFE,
+ ThreatMetadata());
+}
+
+void SafeBrowsingUrlCheckerImpl::ProcessUrls() {
+ DCHECK_NE(STATE_BLOCKED, state_);
+
+ if (state_ == STATE_CHECKING_URL ||
+ state_ == STATE_DISPLAYING_BLOCKING_PAGE) {
+ return;
+ }
+
+ while (next_index_ < urls_.size()) {
+ DCHECK_EQ(STATE_NONE, state_);
+ // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer
+ // side. That would save some IPCs. It requires a method on the
+ // SafeBrowsing mojo interface to query all supported resource types.
+ if (!database_manager_->CanCheckResourceType(resource_type_) ||
+ database_manager_->CheckBrowseUrl(
+ urls_[next_index_], url_checker_delegate_->GetThreatTypes(),
+ this)) {
+ std::move(callbacks_[next_index_]).Run(true);
+ next_index_++;
+ continue;
+ }
+
+ state_ = STATE_CHECKING_URL;
+ // Start a timer to abort the check if it takes too long.
+ timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this,
+ &SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout);
+
+ break;
+ }
+}
+
+void SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls() {
+ DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: " << urls_[next_index_];
+ state_ = STATE_BLOCKED;
+
+ // If user decided to not proceed through a warning, mark all the remaining
+ // redirects as "bad".
+ for (; next_index_ < callbacks_.size(); ++next_index_)
+ std::move(callbacks_[next_index_]).Run(false);
+}
+
+void SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete(bool proceed) {
+ DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_);
+
+ if (proceed) {
+ state_ = STATE_NONE;
+ std::move(callbacks_[next_index_]).Run(true);
+ next_index_++;
+ ProcessUrls();
+ } else {
+ BlockAndProcessUrls();
+ }
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.h b/chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.h
new file mode 100644
index 00000000000..b037d8dcfd0
--- /dev/null
+++ b/chromium/components/safe_browsing/browser/safe_browsing_url_checker_impl.h
@@ -0,0 +1,102 @@
+// 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_SAFE_BROWSING_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
+#define COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing_db/database_manager.h"
+#include "content/public/common/resource_type.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace safe_browsing {
+
+class UrlCheckerDelegate;
+
+// A SafeBrowsingUrlCheckerImpl instance is used to perform SafeBrowsing check
+// for a URL and its redirect URLs. It implements Mojo interface so that it can
+// be used to handle queries from renderers. But it is also used to handle
+// queries from the browser. In that case, the public methods are called
+// directly instead of through Mojo.
+// Used when --enable-network-service is in effect.
+//
+// TODO(yzshen): Do all the logging like what BaseResourceThrottle does.
+class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
+ public SafeBrowsingDatabaseManager::Client {
+ public:
+ SafeBrowsingUrlCheckerImpl(
+ int load_flags,
+ content::ResourceType resource_type,
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+ const base::Callback<content::WebContents*()>& web_contents_getter);
+
+ ~SafeBrowsingUrlCheckerImpl() override;
+
+ // mojom::SafeBrowsingUrlChecker implementation.
+ void CheckUrl(const GURL& url, CheckUrlCallback callback) override;
+
+ private:
+ // SafeBrowsingDatabaseManager::Client implementation:
+ void OnCheckBrowseUrlResult(const GURL& url,
+ SBThreatType threat_type,
+ const ThreatMetadata& metadata) override;
+
+ void OnCheckUrlTimeout();
+
+ void ProcessUrls();
+
+ void BlockAndProcessUrls();
+
+ void OnBlockingPageComplete(bool proceed);
+
+ enum State {
+ // Haven't started checking or checking is complete.
+ STATE_NONE,
+ // We have one outstanding URL-check.
+ STATE_CHECKING_URL,
+ // We're displaying a blocking page.
+ STATE_DISPLAYING_BLOCKING_PAGE,
+ // The blocking page has returned *not* to proceed.
+ STATE_BLOCKED
+ };
+
+ const int load_flags_;
+ const content::ResourceType resource_type_;
+ base::Callback<content::WebContents*()> web_contents_getter_;
+ scoped_refptr<UrlCheckerDelegate> url_checker_delegate_;
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+
+ // The redirect chain for this resource, including the original URL and
+ // subsequent redirect URLs.
+ std::vector<GURL> urls_;
+ // Callbacks corresponding to |urls_| to report check results. |urls_| and
+ // |callbacks_| are always of the same size.
+ std::vector<CheckUrlCallback> callbacks_;
+ // |urls_| before |next_index_| have been checked. If |next_index_| is smaller
+ // than the size of |urls_|, the URL at |next_index_| is being processed.
+ size_t next_index_ = 0;
+
+ State state_ = STATE_NONE;
+
+ // Timer to abort the SafeBrowsing check if it takes too long.
+ base::OneShotTimer timer_;
+
+ base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
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 513698fde86..7c5ce4a4073 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
@@ -78,13 +78,18 @@ SafeBrowsingURLRequestContextGetter::GetURLRequestContext() {
if (safe_browsing_request_context_->http_transaction_factory() &&
safe_browsing_request_context_->http_transaction_factory()
->GetSession()) {
- net::HttpNetworkSession::Params safe_browsing_params =
+ net::HttpNetworkSession::Params safe_browsing_session_params =
safe_browsing_request_context_->http_transaction_factory()
->GetSession()
->params();
- safe_browsing_params.channel_id_service = channel_id_service_.get();
- http_network_session_.reset(
- new net::HttpNetworkSession(safe_browsing_params));
+ net::HttpNetworkSession::Context safe_browsing_session_context =
+ safe_browsing_request_context_->http_transaction_factory()
+ ->GetSession()
+ ->context();
+ safe_browsing_session_context.channel_id_service =
+ channel_id_service_.get();
+ http_network_session_.reset(new net::HttpNetworkSession(
+ safe_browsing_session_params, safe_browsing_session_context));
http_transaction_factory_.reset(
new net::HttpNetworkLayer(http_network_session_.get()));
safe_browsing_request_context_->set_http_transaction_factory(
diff --git a/chromium/components/safe_browsing/browser/threat_details.cc b/chromium/components/safe_browsing/browser/threat_details.cc
index 0e6cfb8cf11..b1fe8d6ee44 100644
--- a/chromium/components/safe_browsing/browser/threat_details.cc
+++ b/chromium/components/safe_browsing/browser/threat_details.cc
@@ -18,6 +18,7 @@
#include "components/safe_browsing/browser/threat_details_cache.h"
#include "components/safe_browsing/browser/threat_details_history.h"
#include "components/safe_browsing/common/safebrowsing_messages.h"
+#include "components/safe_browsing_db/hit_report.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
@@ -72,10 +73,12 @@ ClientSafeBrowsingReportRequest::ReportType GetReportTypeFromSBThreatType(
return ClientSafeBrowsingReportRequest::URL_MALWARE;
case SB_THREAT_TYPE_URL_UNWANTED:
return ClientSafeBrowsingReportRequest::URL_UNWANTED;
- case SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL:
- return ClientSafeBrowsingReportRequest::CLIENT_SIDE_PHISHING_URL;
- case SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL:
- return ClientSafeBrowsingReportRequest::CLIENT_SIDE_MALWARE_URL;
+ case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
+ return ClientSafeBrowsingReportRequest::URL_CLIENT_SIDE_PHISHING;
+ case SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
+ return ClientSafeBrowsingReportRequest::URL_CLIENT_SIDE_MALWARE;
+ case SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING:
+ return ClientSafeBrowsingReportRequest::URL_PASSWORD_PROTECTION_PHISHING;
default: // Gated by SafeBrowsingBlockingPage::ShouldReportThreatDetails.
NOTREACHED() << "We should not send report for threat type "
<< threat_type;
@@ -129,6 +132,25 @@ std::string GetElementKey(const int frame_tree_node_id,
return base::StringPrintf("%d-%d", frame_tree_node_id, element_node_id);
}
+using CSBRR = safe_browsing::ClientSafeBrowsingReportRequest;
+CSBRR::SafeBrowsingUrlApiType GetUrlApiTypeForThreatSource(
+ safe_browsing::ThreatSource source) {
+ switch (source) {
+ case safe_browsing::ThreatSource::DATA_SAVER:
+ return CSBRR::FLYWHEEL;
+ case safe_browsing::ThreatSource::LOCAL_PVER3:
+ return CSBRR::PVER3_NATIVE;
+ case safe_browsing::ThreatSource::LOCAL_PVER4:
+ return CSBRR::PVER4_NATIVE;
+ case safe_browsing::ThreatSource::REMOTE:
+ return CSBRR::ANDROID_SAFETYNET;
+ case safe_browsing::ThreatSource::UNKNOWN:
+ case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
+ case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
+ break;
+ }
+ return CSBRR::SAFE_BROWSING_URL_API_TYPE_UNSPECIFIED;
+}
} // namespace
// The default ThreatDetailsFactory. Global, made a singleton so we
@@ -194,6 +216,12 @@ ThreatDetails::ThreatDetails(
StartCollection();
}
+ThreatDetails::ThreatDetails()
+ : cache_result_(false),
+ did_proceed_(false),
+ num_visits_(0),
+ ambiguous_dom_(false) {}
+
ThreatDetails::~ThreatDetails() {}
bool ThreatDetails::OnMessageReceived(const IPC::Message& message,
@@ -359,6 +387,9 @@ void ThreatDetails::StartCollection() {
report_->set_type(GetReportTypeFromSBThreatType(resource_.threat_type));
}
+ if (resource_.threat_type == SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING)
+ report_->set_token(resource_.token);
+
GURL referrer_url;
NavigationEntry* nav_entry = resource_.GetNavigationEntryForResource();
if (nav_entry) {
@@ -578,6 +609,9 @@ void ThreatDetails::OnCacheCollectionReady() {
}
report_->set_complete(cache_result_);
+ report_->mutable_client_properties()->set_url_api_type(
+ GetUrlApiTypeForThreatSource(resource_.threat_source));
+
// Send the report, using the SafeBrowsingService.
std::string serialized;
if (!report_->SerializeToString(&serialized)) {
diff --git a/chromium/components/safe_browsing/browser/threat_details.h b/chromium/components/safe_browsing/browser/threat_details.h
index 16a244f9e22..adbda2996a7 100644
--- a/chromium/components/safe_browsing/browser/threat_details.h
+++ b/chromium/components/safe_browsing/browser/threat_details.h
@@ -91,7 +91,7 @@ class ThreatDetails : public base::RefCountedThreadSafe<
// in UI thread; then do cache collection back in IO thread. We also record
// if the user did proceed with the warning page, and how many times user
// visited this page before. When we are done, we send the report.
- void FinishCollection(bool did_proceed, int num_visits);
+ virtual void FinishCollection(bool did_proceed, int num_visits);
void OnCacheCollectionReady();
@@ -110,6 +110,8 @@ class ThreatDetails : public base::RefCountedThreadSafe<
const UnsafeResource& resource,
net::URLRequestContextGetter* request_context_getter,
history::HistoryService* history_service);
+ // Default constructor for testing only.
+ ThreatDetails();
~ThreatDetails() override;
diff --git a/chromium/components/safe_browsing/browser/url_checker_delegate.h b/chromium/components/safe_browsing/browser/url_checker_delegate.h
new file mode 100644
index 00000000000..02f442e0405
--- /dev/null
+++ b/chromium/components/safe_browsing/browser/url_checker_delegate.h
@@ -0,0 +1,52 @@
+// 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_SAFE_BROWSING_BROWSER_URL_CHECKER_DELEGATE_H_
+#define COMPONENTS_SAFE_BROWSING_BROWSER_URL_CHECKER_DELEGATE_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace security_interstitials {
+struct UnsafeResource;
+}
+
+namespace safe_browsing {
+
+class BaseUIManager;
+class SafeBrowsingDatabaseManager;
+
+// Delegate interface for SafeBrowsingUrlCheckerImpl. SafeBrowsingUrlCheckerImpl
+// is embedder-independent. It delegates to this interface those operations that
+// different embedders (Chrome and Android WebView) handle differently.
+//
+// All methods should only be called from the IO thread.
+class UrlCheckerDelegate
+ : public base::RefCountedThreadSafe<UrlCheckerDelegate> {
+ public:
+ // Destroys prerender contents if necessary.
+ virtual void MaybeDestroyPrerenderContents(
+ const base::Callback<content::WebContents*()>& web_contents_getter) = 0;
+
+ // Starts displaying the SafeBrowsing interstitial page.
+ virtual void StartDisplayingBlockingPageHelper(
+ const security_interstitials::UnsafeResource& resource) = 0;
+
+ virtual const SBThreatTypeSet& GetThreatTypes() = 0;
+ virtual SafeBrowsingDatabaseManager* GetDatabaseManager() = 0;
+ virtual BaseUIManager* GetUIManager() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<UrlCheckerDelegate>;
+ virtual ~UrlCheckerDelegate() {}
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_BROWSER_URL_CHECKER_DELEGATE_H_
diff --git a/chromium/components/safe_browsing/common/BUILD.gn b/chromium/components/safe_browsing/common/BUILD.gn
index 212b21df0d3..6de5638d5bc 100644
--- a/chromium/components/safe_browsing/common/BUILD.gn
+++ b/chromium/components/safe_browsing/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("//mojo/public/tools/bindings/mojom.gni")
+
source_set("common") {
sources = [
"safebrowsing_constants.cc",
@@ -12,13 +14,20 @@ source_set("common") {
"safebrowsing_switches.cc",
"safebrowsing_switches.h",
"safebrowsing_types.h",
+ "utils.cc",
+ "utils.h",
]
deps = [
"//base",
+ "//crypto:crypto",
"//ipc",
"//url/ipc:url_ipc",
]
+
+ public_deps = [
+ ":interfaces",
+ ]
}
static_library("safe_browsing_prefs") {
@@ -28,6 +37,7 @@ static_library("safe_browsing_prefs") {
]
deps = [
"//base:base",
+ "//components/pref_registry:pref_registry",
"//components/prefs",
]
}
@@ -45,3 +55,14 @@ source_set("unit_tests") {
"//testing/gtest",
]
}
+
+mojom("interfaces") {
+ sources = [
+ "safe_browsing.mojom",
+ ]
+
+ public_deps = [
+ "//content/public/common:resource_type_bindings",
+ "//url/mojo:url_mojom_gurl",
+ ]
+}
diff --git a/chromium/components/safe_browsing/common/DEPS b/chromium/components/safe_browsing/common/DEPS
index 61eff4271b8..60d09aaed9f 100644
--- a/chromium/components/safe_browsing/common/DEPS
+++ b/chromium/components/safe_browsing/common/DEPS
@@ -1,5 +1,7 @@
include_rules = [
+ "+components/pref_registry",
"+components/prefs",
+ "+crypto/sha2.h",
"+ipc",
"+url"
]
diff --git a/chromium/components/safe_browsing/common/OWNERS b/chromium/components/safe_browsing/common/OWNERS
index 6734395b373..599596c1259 100644
--- a/chromium/components/safe_browsing/common/OWNERS
+++ b/chromium/components/safe_browsing/common/OWNERS
@@ -2,3 +2,5 @@ jialiul@chromium.org
per-file *_messages*.h=set noparent
per-file *_messages*.h=file://ipc/SECURITY_OWNERS
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/safe_browsing/common/safe_browsing.mojom b/chromium/components/safe_browsing/common/safe_browsing.mojom
new file mode 100644
index 00000000000..5b7ae9544c7
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safe_browsing.mojom
@@ -0,0 +1,31 @@
+// 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.
+
+module safe_browsing.mojom;
+
+import "content/public/common/resource_type.mojom";
+import "url/mojo/url.mojom";
+
+interface SafeBrowsing {
+ // Queries SafeBrowsing whether |url| is safe to load, and creates a
+ // SafeBrowsingUrlChecker interface.
+ // This checker interface should be used (and only used) for subsequent checks
+ // of redirects, so that the server side could keep track of the redirect
+ // chain. Disconnecting the checker interface cancels on-going URL checks.
+ // Please note that in that case if the check started by this method hasn't
+ // completed yet, it will also be canceled and return true as if the URL is
+ // safe.
+ // The check and (subsequent checks performed using SafeBrowsingUrlChecker)
+ // checks against SafeBrowsing's Malware, Phishing, and UwS blacklists.
+ CreateCheckerAndCheck(
+ int32 render_frame_id,
+ SafeBrowsingUrlChecker& request,
+ url.mojom.Url url,
+ int32 load_flags,
+ content.mojom.ResourceType resource_type) => (bool safe);
+};
+
+interface SafeBrowsingUrlChecker {
+ CheckUrl(url.mojom.Url url) => (bool safe);
+};
diff --git a/chromium/components/safe_browsing/common/safe_browsing_prefs.cc b/chromium/components/safe_browsing/common/safe_browsing_prefs.cc
index 83f75881b64..6747a0d50dc 100644
--- a/chromium/components/safe_browsing/common/safe_browsing_prefs.cc
+++ b/chromium/components/safe_browsing/common/safe_browsing_prefs.cc
@@ -7,6 +7,8 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace {
@@ -139,16 +141,22 @@ void RecordExtendedReportingPrefChanged(
} // namespace
namespace prefs {
+const char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
const char kSafeBrowsingExtendedReportingEnabled[] =
"safebrowsing.extended_reporting_enabled";
-const char kSafeBrowsingScoutReportingEnabled[] =
- "safebrowsing.scout_reporting_enabled";
-const char kSafeBrowsingScoutGroupSelected[] =
- "safebrowsing.scout_group_selected";
+const char kSafeBrowsingExtendedReportingOptInAllowed[] =
+ "safebrowsing.extended_reporting_opt_in_allowed";
+const char kSafeBrowsingIncidentsSent[] = "safebrowsing.incidents_sent";
+const char kSafeBrowsingProceedAnywayDisabled[] =
+ "safebrowsing.proceed_anyway_disabled";
const char kSafeBrowsingSawInterstitialExtendedReporting[] =
"safebrowsing.saw_interstitial_sber1";
const char kSafeBrowsingSawInterstitialScoutReporting[] =
"safebrowsing.saw_interstitial_sber2";
+const char kSafeBrowsingScoutGroupSelected[] =
+ "safebrowsing.scout_group_selected";
+const char kSafeBrowsingScoutReportingEnabled[] =
+ "safebrowsing.scout_reporting_enabled";
} // namespace prefs
namespace safe_browsing {
@@ -156,7 +164,7 @@ namespace safe_browsing {
const char kSwitchForceScoutGroup[] = "force-scout-group";
const base::Feature kCanShowScoutOptIn{"CanShowScoutOptIn",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOnlyShowScoutOptIn{"OnlyShowScoutOptIn",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -294,6 +302,10 @@ void InitializeSafeBrowsingPrefs(PrefService* prefs) {
}
}
+bool IsExtendedReportingOptInAllowed(const PrefService& prefs) {
+ return prefs.GetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed);
+}
+
bool IsExtendedReportingEnabled(const PrefService& prefs) {
return prefs.GetBoolean(GetExtendedReportingPrefName(prefs));
}
@@ -345,6 +357,26 @@ void RecordExtendedReportingMetrics(const PrefService& prefs) {
}
}
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(prefs::kSafeBrowsingExtendedReportingEnabled,
+ false);
+ registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutReportingEnabled,
+ false);
+ registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutGroupSelected, false);
+ registry->RegisterBooleanPref(
+ prefs::kSafeBrowsingSawInterstitialExtendedReporting, false);
+ registry->RegisterBooleanPref(
+ prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
+ registry->RegisterBooleanPref(
+ prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+ registry->RegisterBooleanPref(
+ prefs::kSafeBrowsingEnabled, true,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+ registry->RegisterBooleanPref(prefs::kSafeBrowsingProceedAnywayDisabled,
+ false);
+ registry->RegisterDictionaryPref(prefs::kSafeBrowsingIncidentsSent);
+}
+
void SetExtendedReportingPrefAndMetric(
PrefService* prefs,
bool value,
@@ -438,4 +470,24 @@ void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs) {
true);
}
+base::ListValue GetSafeBrowsingPreferencesList(PrefService* prefs) {
+ base::ListValue preferences_list;
+
+ 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
+ // user.
+ for (const char* preference : safe_browsing_preferences) {
+ preferences_list.GetList().push_back(base::Value(preference));
+ bool enabled = prefs->GetBoolean(preference);
+ preferences_list.GetList().push_back(
+ base::Value(enabled ? "Enabled" : "Disabled"));
+ }
+ return preferences_list;
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/common/safe_browsing_prefs.h b/chromium/components/safe_browsing/common/safe_browsing_prefs.h
index ec87b39be1a..76c7913b02f 100644
--- a/chromium/components/safe_browsing/common/safe_browsing_prefs.h
+++ b/chromium/components/safe_browsing/common/safe_browsing_prefs.h
@@ -8,21 +8,29 @@
#define COMPONENTS_SAFE_BROWSING_COMMON_SAFE_BROWSING_PREFS_H_
#include "base/feature_list.h"
+#include "base/values.h"
+class PrefRegistrySimple;
class PrefService;
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 indicating whether Safe Browsing Scout reporting is enabled, which
-// collects data for malware detection.
-extern const char kSafeBrowsingScoutReportingEnabled[];
+// 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.
+extern const char kSafeBrowsingExtendedReportingOptInAllowed[];
-// Boolean indicating whether the Scout reporting workflow is enabled. This
-// affects which of SafeBrowsingExtendedReporting or SafeBrowsingScoutReporting
-// is used.
-extern const char kSafeBrowsingScoutGroupSelected[];
+// A dictionary mapping incident types to a dict of incident key:digest pairs.
+extern const char kSafeBrowsingIncidentsSent[];
+
+// Boolean that is true when the SafeBrowsing interstitial should not allow
+// users to proceed anyway.
+extern const char kSafeBrowsingProceedAnywayDisabled[];
// Boolean indicating whether the user has ever seen a security interstitial
// containing the legacy Extended Reporting opt-in.
@@ -31,6 +39,15 @@ extern const char kSafeBrowsingSawInterstitialExtendedReporting[];
// Boolean indicating whether the user has ever seen a security interstitial
// containing the new Scout opt-in.
extern const char kSafeBrowsingSawInterstitialScoutReporting[];
+
+// Boolean indicating whether the Scout reporting workflow is enabled. This
+// affects which of SafeBrowsingExtendedReporting or SafeBrowsingScoutReporting
+// is used.
+extern const char kSafeBrowsingScoutGroupSelected[];
+
+// Boolean indicating whether Safe Browsing Scout reporting is enabled, which
+// collects data for malware detection.
+extern const char kSafeBrowsingScoutReportingEnabled[];
}
namespace safe_browsing {
@@ -113,6 +130,10 @@ const char* GetExtendedReportingPrefName(const PrefService& prefs);
// 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);
+
// Returns whether Safe Browsing Extended Reporting is currently enabled.
// This should be used to decide if any of the reporting preferences are set,
// regardless of which specific one is set.
@@ -124,6 +145,9 @@ bool IsScout(const PrefService& prefs);
// Updates UMA metrics about Safe Browsing Extended Reporting states.
void RecordExtendedReportingMetrics(const PrefService& prefs);
+// Registers user preferences related to Safe Browsing.
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
// Sets the currently active Safe Browsing Extended Reporting preference to the
// specified value. The |location| indicates the UI where the change was
// made.
@@ -146,6 +170,11 @@ void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
// depending on their experiment state.
void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs);
+// Returns a list of preferences to be shown in chrome://safe-browsing. The
+// preferences are passed as an alternating sequence of preference names and
+// values represented as strings.
+base::ListValue GetSafeBrowsingPreferencesList(PrefService* prefs);
+
} // namespace safe_browsing
#endif // COMPONENTS_SAFE_BROWSING_COMMON_SAFE_BROWSING_PREFS_H_
diff --git a/chromium/components/safe_browsing/common/safebrowsing_constants.cc b/chromium/components/safe_browsing/common/safebrowsing_constants.cc
index 22e6de0cdf3..65c2b54bb0e 100644
--- a/chromium/components/safe_browsing/common/safebrowsing_constants.cc
+++ b/chromium/components/safe_browsing/common/safebrowsing_constants.cc
@@ -13,4 +13,23 @@ const base::FilePath::CharType kCookiesFile[] = FILE_PATH_LITERAL(" Cookies");
const base::FilePath::CharType kChannelIDFile[] =
FILE_PATH_LITERAL(" Channel IDs");
+// The default URL prefix where browser fetches chunk updates, hashes,
+// and reports safe browsing hits and malware details.
+const char kSbDefaultURLPrefix[] =
+ "https://safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are issues establishing a connection
+// with the server at the primary URL.
+const char kSbBackupConnectErrorURLPrefix[] =
+ "https://alt1-safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are HTTP-specific issues with the
+// server at the primary URL.
+const char kSbBackupHttpErrorURLPrefix[] =
+ "https://alt2-safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are local network specific issues.
+const char kSbBackupNetworkErrorURLPrefix[] =
+ "https://alt3-safebrowsing.google.com/safebrowsing";
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/common/safebrowsing_constants.h b/chromium/components/safe_browsing/common/safebrowsing_constants.h
index e6776b56274..598352e3997 100644
--- a/chromium/components/safe_browsing/common/safebrowsing_constants.h
+++ b/chromium/components/safe_browsing/common/safebrowsing_constants.h
@@ -14,6 +14,21 @@ extern const base::FilePath::CharType kSafeBrowsingBaseFilename[];
// Filename suffix for the cookie database.
extern const base::FilePath::CharType kCookiesFile[];
extern const base::FilePath::CharType kChannelIDFile[];
+
+// The default URL prefix where browser fetches chunk updates, hashes,
+// and reports safe browsing hits and malware details.
+extern const char kSbDefaultURLPrefix[];
+
+// The backup URL prefix used when there are issues establishing a connection
+// with the server at the primary URL.
+extern const char kSbBackupConnectErrorURLPrefix[];
+
+// The backup URL prefix used when there are HTTP-specific issues with the
+// server at the primary URL.
+extern const char kSbBackupHttpErrorURLPrefix[];
+
+// The backup URL prefix used when there are local network specific issues.
+extern const char kSbBackupNetworkErrorURLPrefix[];
}
#endif // COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
diff --git a/chromium/components/safe_browsing/common/utils.cc b/chromium/components/safe_browsing/common/utils.cc
new file mode 100644
index 00000000000..67baa8c13cc
--- /dev/null
+++ b/chromium/components/safe_browsing/common/utils.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/safe_browsing/common/utils.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "crypto/sha2.h"
+
+namespace safe_browsing {
+
+std::string ShortURLForReporting(const GURL& url) {
+ std::string spec(url.spec());
+ if (url.SchemeIs(url::kDataScheme)) {
+ size_t comma_pos = spec.find(',');
+ if (comma_pos != std::string::npos && comma_pos != spec.size() - 1) {
+ std::string hash_value = crypto::SHA256HashString(spec);
+ spec.erase(comma_pos + 1);
+ spec += base::HexEncode(hash_value.data(), hash_value.size());
+ }
+ }
+ return spec;
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/common/utils.h b/chromium/components/safe_browsing/common/utils.h
new file mode 100644
index 00000000000..fa3b8a771d0
--- /dev/null
+++ b/chromium/components/safe_browsing/common/utils.h
@@ -0,0 +1,20 @@
+// Copyrights 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.
+//
+// Safe Browsing utility functions.
+
+#ifndef COMPONENTS_SAFE_BROWSING_COMMON_UTILS_H_
+#define COMPONENTS_SAFE_BROWSING_COMMON_UTILS_H_
+
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+// Shorten URL by replacing its contents with its SHA256 hash if it has data
+// scheme.
+std::string ShortURLForReporting(const GURL& url);
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_COMMON_UTILS_H_
diff --git a/chromium/components/safe_browsing/csd.proto b/chromium/components/safe_browsing/csd.proto
index 7bb5920bd7c..088d7b5bde3 100644
--- a/chromium/components/safe_browsing/csd.proto
+++ b/chromium/components/safe_browsing/csd.proto
@@ -161,7 +161,6 @@ message ClientMalwareRequest {
// is safe for the purposes of entering user credentials for logging in.
message LoginReputationClientRequest {
// The top level frame URL of the webpage that hosts the login form.
- // The client will strip CGI parameters.
optional string page_url = 1;
// Type for the request.
@@ -217,6 +216,19 @@ message LoginReputationClientRequest {
// Whether the reused password is used for Chrome signin.
optional bool is_chrome_signin_password = 3;
+
+ // Sync account type. Only set if |is_chrome_signin_password| is true.
+ enum SyncAccountType {
+ // Not a sign-in user.
+ NOT_SIGNED_IN = 0;
+
+ // User signed in with @gmail.com, or @googlemail.com account.
+ GMAIL = 1;
+
+ // User signed in with a G Suite account.
+ GSUITE = 2;
+ }
+ optional SyncAccountType sync_account_type = 4;
}
optional PasswordReuseEvent password_reuse_event = 4;
@@ -262,6 +274,10 @@ message LoginReputationClientResponse {
// Deprecated.
optional bool DEPRECATED_cache_expression_exact_match = 4 [deprecated = true];
+
+ // A token unique to each request which correlates response and post-warning
+ // actions.
+ optional bytes verdict_token = 5;
}
message ClientMalwareResponse {
@@ -467,7 +483,8 @@ message ClientDownloadRequest {
// Fields 19-21 are reserved for server-side use and are never sent by the
// client.
- // A binary contained in an archive (e.g., a .zip archive).
+ // A binary or archive contained in an archive (e.g., a .exe in a .zip
+ // archive, or a .zip inside a .zip).
message ArchivedBinary {
optional string file_basename = 1;
optional DownloadType download_type = 2;
@@ -504,8 +521,15 @@ message ClientDownloadRequest {
// Deprecated.
optional bool DEPRECATED_download_attribution_finch_enabled = 39
[deprecated = true];
+
+ // The Mac disk image code signature.
+ // The underlying structure of code signature is defined at
+ // https://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/codesign.h
+ optional bytes udif_code_signature = 40;
}
+// Please update SafeBrowsingNavigationObserverManager::SanitizeReferrerChain()
+// if you're adding more fields to this message.
message ReferrerChainEntry {
enum URLType {
// URL of safe browsing events that are at the end of the referrer chain.
@@ -899,11 +923,12 @@ message ClientSafeBrowsingReportRequest {
URL_PHISHING = 1;
URL_MALWARE = 2;
URL_UNWANTED = 3;
- CLIENT_SIDE_PHISHING_URL = 4;
- CLIENT_SIDE_MALWARE_URL = 5;
+ URL_CLIENT_SIDE_PHISHING = 4;
+ URL_CLIENT_SIDE_MALWARE = 5;
DANGEROUS_DOWNLOAD_RECOVERY = 6;
DANGEROUS_DOWNLOAD_WARNING = 7;
DANGEROUS_DOWNLOAD_BY_API = 10;
+ URL_PASSWORD_PROTECTION_PHISHING = 12;
}
message HTTPHeader {
@@ -984,10 +1009,34 @@ message ClientSafeBrowsingReportRequest {
// Whether user visited this origin before.
optional bool repeat_visit = 9;
- // The same token in ClientDownloadResponse. This field is only set if its
- // report type is DANGEROUS_DOWNLOAD_RECOVERY, DANGEROUS_DOWNLOAD_WARNING or
- // DANGEROUS_DOWNLOAD_BY_API.
+ // The same token in ClientDownloadResponse or LoginReputationClientResponse.
+ // This field is only set if its report type is DANGEROUS_DOWNLOAD_RECOVERY,
+ // DANGEROUS_DOWNLOAD_WARNING, DANGEROUS_DOWNLOAD_BY_API or
+ // PASSWORD_PROTECTION_PHISHING_URL.
optional bytes token = 15;
+
+ enum SafeBrowsingUrlApiType {
+ SAFE_BROWSING_URL_API_TYPE_UNSPECIFIED = 0;
+ // Native implementation of Safe Browsing API v3 protocol.
+ PVER3_NATIVE = 1;
+ // Native implementation of Safe Browsing API v4 protocol.
+ PVER4_NATIVE = 2;
+ // Android SafetyNet API.
+ // https://developer.android.com/training/safetynet/safebrowsing.html
+ ANDROID_SAFETYNET = 3;
+ // Flywheel (data compression service).
+ FLYWHEEL = 4;
+ }
+
+ // The information propagated from the client about various environment
+ // variables including SDK version, Google Play Services version and so on.
+ message SafeBrowsingClientProperties {
+ optional string client_version = 1;
+ optional int64 google_play_services_version = 2;
+ optional bool is_instant_apps = 3;
+ optional SafeBrowsingUrlApiType url_api_type = 4;
+ }
+ optional SafeBrowsingClientProperties client_properties = 17;
}
// An HTML Element on the page (eg: iframe, div, script, etc).
diff --git a/chromium/components/safe_browsing/features.cc b/chromium/components/safe_browsing/features.cc
new file mode 100644
index 00000000000..0e5c74c858b
--- /dev/null
+++ b/chromium/components/safe_browsing/features.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 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/safe_browsing/features.h"
+
+#include <stddef.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include "base/feature_list.h"
+
+#include "base/macros.h"
+#include "base/values.h"
+namespace safe_browsing {
+// Please define any new SafeBrowsing related features in this file, and add
+// them to the ExperimentalFeaturesList below to start displaying their status
+// on the chrome://safe-browsing page.
+
+// Controls various parameters related to occasionally collecting ad samples,
+// for example to control how often collection should occur.
+const base::Feature kAdSamplerTriggerFeature{"SafeBrowsingAdSamplerTrigger",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kLocalDatabaseManagerEnabled{
+ "SafeBrowsingV4LocalDatabaseManagerEnabled",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Specifies which non-resource HTML Elements to collect based on their tag and
+// attributes. It's a single param containing a comma-separated list of pairs.
+// For example: "tag1,id,tag1,height,tag2,foo" - this will collect elements with
+// tag "tag1" that have attribute "id" or "height" set, and elements of tag
+// "tag2" if they have attribute "foo" set. All tag names and attributes should
+// be lower case.
+const base::Feature kThreatDomDetailsTagAndAttributeFeature{
+ "ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kV4OnlyEnabled{"SafeBrowsingV4OnlyEnabled",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+namespace {
+// List of experimental features. Boolean value for each list member should be
+// set to true if the experiment is currently running at a probability other
+// than 1 or 0, or to false otherwise.
+constexpr struct {
+ const base::Feature* feature;
+ // True if the feature is running at a probability other than 1 or 0.
+ bool probabilistically_enabled;
+} kExperimentalFeatures[]{
+ {&kAdSamplerTriggerFeature, false},
+ {&kLocalDatabaseManagerEnabled, true},
+ {&kThreatDomDetailsTagAndAttributeFeature, false},
+ {&kV4OnlyEnabled, true},
+};
+
+// Adds the name and the enabled/disabled status of a given feature.
+void AddFeatureAndAvailability(const base::Feature* exp_feature,
+ base::ListValue* param_list) {
+ param_list->GetList().push_back(base::Value(exp_feature->name));
+ if (base::FeatureList::IsEnabled(*exp_feature)) {
+ param_list->GetList().push_back(base::Value("Enabled"));
+ } else {
+ param_list->GetList().push_back(base::Value("Disabled"));
+ }
+}
+} // namespace
+
+// Returns the list of the experimental features that are enabled or disabled,
+// as part of currently running Safe Browsing experiments.
+base::ListValue GetFeatureStatusList() {
+ base::ListValue param_list;
+ for (const auto& feature_status : kExperimentalFeatures) {
+ if (feature_status.probabilistically_enabled)
+ AddFeatureAndAvailability(feature_status.feature, &param_list);
+ }
+ return param_list;
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/features.h b/chromium/components/safe_browsing/features.h
new file mode 100644
index 00000000000..baa6763d675
--- /dev/null
+++ b/chromium/components/safe_browsing/features.h
@@ -0,0 +1,30 @@
+// Copyright (c) 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_SAFE_BROWSING_FEATURES_H_
+#define COMPONENTS_SAFE_BROWSING_FEATURES_H_
+
+#include <stddef.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/values.h"
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace safe_browsing {
+// Features list
+extern const base::Feature kAdSamplerTriggerFeature;
+extern const base::Feature kLocalDatabaseManagerEnabled;
+extern const base::Feature kThreatDomDetailsTagAndAttributeFeature;
+extern const base::Feature kV4OnlyEnabled;
+
+base::ListValue GetFeatureStatusList();
+
+#endif // COMPONENTS_SAFE_BROWSING_FEATURES_H_
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/password_protection/BUILD.gn b/chromium/components/safe_browsing/password_protection/BUILD.gn
index a7be564812e..f2e9509917b 100644
--- a/chromium/components/safe_browsing/password_protection/BUILD.gn
+++ b/chromium/components/safe_browsing/password_protection/BUILD.gn
@@ -3,48 +3,56 @@
# found in the LICENSE file.
source_set("password_protection") {
- sources = [
- "password_protection_request.cc",
- "password_protection_request.h",
- "password_protection_service.cc",
- "password_protection_service.h",
- ]
+ if (!is_android && !is_ios) {
+ sources = [
+ "password_protection_request.cc",
+ "password_protection_request.h",
+ "password_protection_service.cc",
+ "password_protection_service.h",
+ ]
- public_deps = [
- "//google_apis:google_apis",
- ]
+ public_deps = [
+ "//google_apis:google_apis",
+ ]
- deps = [
- "//base:base",
- "//components/content_settings/core/browser:browser",
- "//components/data_use_measurement/core:core",
- "//components/history/core/browser:browser",
- "//components/safe_browsing:csd_proto",
- "//components/safe_browsing_db:database_manager",
- "//components/safe_browsing_db:v4_protocol_manager_util",
- "//content/public/browser:browser",
- "//net:net",
- "//third_party/protobuf:protobuf_lite",
- ]
+ deps = [
+ "//base:base",
+ "//components/content_settings/core/browser:browser",
+ "//components/data_use_measurement/core:core",
+ "//components/history/core/browser:browser",
+ "//components/password_manager/core/browser:browser",
+ "//components/safe_browsing:csd_proto",
+ "//components/safe_browsing_db:database_manager",
+ "//components/safe_browsing_db:v4_protocol_manager_util",
+ "//components/safe_browsing_db:whitelist_checker_client",
+ "//content/public/browser:browser",
+ "//net:net",
+ "//third_party/protobuf:protobuf_lite",
+ ]
+ }
}
source_set("password_protection_unittest") {
testonly = true
- sources = [
- "password_protection_service_unittest.cc",
- ]
- deps = [
- ":password_protection",
- "//base",
- "//base/test:test_support",
- "//components/content_settings/core/browser:browser",
- "//components/history/core/browser:browser",
- "//components/safe_browsing_db:test_database_manager",
- "//components/sync_preferences:test_support",
- "//content/test:test_support",
- "//net:test_support",
- "//testing/gmock",
- "//testing/gtest",
- "//third_party/protobuf:protobuf_lite",
- ]
+ if (!is_android && !is_ios) {
+ sources = [
+ "password_protection_service_unittest.cc",
+ ]
+ deps = [
+ ":password_protection",
+ "//base",
+ "//base/test:test_support",
+ "//components/content_settings/core/browser:browser",
+ "//components/history/core/browser:browser",
+ "//components/password_manager/core/browser:browser",
+ "//components/safe_browsing:features",
+ "//components/safe_browsing_db:test_database_manager",
+ "//components/sync_preferences:test_support",
+ "//content/test:test_support",
+ "//net:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/protobuf:protobuf_lite",
+ ]
+ }
}
diff --git a/chromium/components/safe_browsing/password_protection/DEPS b/chromium/components/safe_browsing/password_protection/DEPS
index f04e7f6794a..1752444f8c8 100644
--- a/chromium/components/safe_browsing/password_protection/DEPS
+++ b/chromium/components/safe_browsing/password_protection/DEPS
@@ -1,11 +1,8 @@
include_rules = [
- "-content",
"+components/content_settings/core/browser",
"+components/history/core/browser",
- "+components/safe_browsing/csd.pb.h",
- "+components/safe_browsing_db",
+ "+components/password_manager/core/browser/password_reuse_detector.h",
"+components/sync_preferences/testing_pref_service_syncable.h",
- "+content/public/browser",
"+content/public/test",
"+net",
]
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 182070830a2..3c5ac791456 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/chromium/components/safe_browsing/password_protection/password_protection_request.cc
@@ -1,18 +1,22 @@
// 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/safe_browsing/password_protection/password_protection_request.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing_db/whitelist_checker_client.h"
#include "content/public/browser/web_contents.h"
#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "url/origin.h"
using content::BrowserThread;
using content::WebContents;
@@ -26,6 +30,7 @@ PasswordProtectionRequest::PasswordProtectionRequest(
const GURL& password_form_frame_url,
const std::string& saved_domain,
LoginReputationClientRequest::TriggerType type,
+ bool password_field_exists,
PasswordProtectionService* pps,
int request_timeout_in_ms)
: web_contents_(web_contents),
@@ -33,11 +38,14 @@ PasswordProtectionRequest::PasswordProtectionRequest(
password_form_action_(password_form_action),
password_form_frame_url_(password_form_frame_url),
saved_domain_(saved_domain),
- request_type_(type),
+ trigger_type_(type),
+ password_field_exists_(password_field_exists),
password_protection_service_(pps),
request_timeout_in_ms_(request_timeout_in_ms),
weakptr_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+ trigger_type_ == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
}
PasswordProtectionRequest::~PasswordProtectionRequest() {
@@ -46,26 +54,37 @@ PasswordProtectionRequest::~PasswordProtectionRequest() {
void PasswordProtectionRequest::Start() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- CheckWhitelistOnUIThread();
+ CheckWhitelist();
}
-void PasswordProtectionRequest::CheckWhitelistOnUIThread() {
+void PasswordProtectionRequest::CheckWhitelist() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- bool* match_whitelist = new bool(false);
- tracker_.PostTaskAndReply(
+ // Start a task on the IO thread to check the whitelist. It may
+ // callback immediately on the IO thread or take some time if a full-hash-
+ // check is required.
+ auto result_callback = base::Bind(&OnWhitelistCheckDoneOnIO, GetWeakPtr());
+ tracker_.PostTask(
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO).get(), FROM_HERE,
- base::Bind(&PasswordProtectionService::CheckCsdWhitelistOnIOThread,
- base::Unretained(password_protection_service_),
- main_frame_url_, match_whitelist),
- base::Bind(&PasswordProtectionRequest::OnWhitelistCheckDone, this,
- base::Owned(match_whitelist)));
+ base::Bind(&WhitelistCheckerClient::StartCheckCsdWhitelist,
+ password_protection_service_->database_manager(),
+ main_frame_url_, result_callback));
+}
+
+// static
+void PasswordProtectionRequest::OnWhitelistCheckDoneOnIO(
+ base::WeakPtr<PasswordProtectionRequest> weak_request,
+ bool match_whitelist) {
+ // Don't access weak_request on IO thread. Move it back to UI thread first.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&PasswordProtectionRequest::OnWhitelistCheckDone, weak_request,
+ match_whitelist));
}
-void PasswordProtectionRequest::OnWhitelistCheckDone(
- const bool* match_whitelist) {
+void PasswordProtectionRequest::OnWhitelistCheckDone(bool match_whitelist) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (*match_whitelist)
+ if (match_whitelist)
Finish(PasswordProtectionService::MATCHED_WHITELIST, nullptr);
else
CheckCachedVerdicts();
@@ -81,7 +100,7 @@ void PasswordProtectionRequest::CheckCachedVerdicts() {
std::unique_ptr<LoginReputationClientResponse> cached_response =
base::MakeUnique<LoginReputationClientResponse>();
auto verdict = password_protection_service_->GetCachedVerdict(
- main_frame_url_, cached_response.get());
+ main_frame_url_, trigger_type_, cached_response.get());
if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED)
Finish(PasswordProtectionService::RESPONSE_ALREADY_CACHED,
std::move(cached_response));
@@ -92,11 +111,11 @@ void PasswordProtectionRequest::CheckCachedVerdicts() {
void PasswordProtectionRequest::FillRequestProto() {
request_proto_ = base::MakeUnique<LoginReputationClientRequest>();
request_proto_->set_page_url(main_frame_url_.spec());
- request_proto_->set_trigger_type(request_type_);
- password_protection_service_->FillUserPopulation(request_type_,
+ request_proto_->set_trigger_type(trigger_type_);
+ password_protection_service_->FillUserPopulation(trigger_type_,
request_proto_.get());
request_proto_->set_stored_verdict_cnt(
- password_protection_service_->GetStoredVerdictCount());
+ password_protection_service_->GetStoredVerdictCount(trigger_type_));
LoginReputationClientRequest::Frame* main_frame =
request_proto_->add_frames();
main_frame->set_url(main_frame_url_.spec());
@@ -104,7 +123,7 @@ void PasswordProtectionRequest::FillRequestProto() {
password_protection_service_->FillReferrerChain(
main_frame_url_, -1 /* tab id not available */, main_frame);
- switch (request_type_) {
+ switch (trigger_type_) {
case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: {
LoginReputationClientRequest::Frame::Form* password_form;
if (password_form_frame_url_ == main_frame_url_) {
@@ -124,7 +143,21 @@ void PasswordProtectionRequest::FillRequestProto() {
break;
}
case LoginReputationClientRequest::PASSWORD_REUSE_EVENT: {
- // TODO(jialiul): Fill more password reuse related information when ready.
+ 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(
+ saved_domain_ == std::string(password_manager::kSyncPasswordDomain));
+ if (reuse_event->is_chrome_signin_password()) {
+ reuse_event->set_sync_account_type(
+ password_protection_service_->GetSyncAccountType());
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordProtection.PasswordReuseSyncAccountType",
+ reuse_event->sync_account_type(),
+ LoginReputationClientRequest::PasswordReuseEvent::
+ SyncAccountType_MAX +
+ 1);
+ }
break;
}
default:
@@ -238,17 +271,21 @@ void PasswordProtectionRequest::Finish(
std::unique_ptr<LoginReputationClientResponse> response) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
tracker_.TryCancelAll();
-
- if (request_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+ bool is_sync_password =
+ saved_domain_ == std::string(password_manager::kSyncPasswordDomain);
+ if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName,
outcome, PasswordProtectionService::MAX_OUTCOME);
+ } else if (is_sync_password) {
+ UMA_HISTOGRAM_ENUMERATION(kSyncPasswordEntryRequestOutcomeHistogramName,
+ outcome, PasswordProtectionService::MAX_OUTCOME);
} else {
UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName,
outcome, PasswordProtectionService::MAX_OUTCOME);
}
- if (response) {
- switch (request_type_) {
+ if (outcome == PasswordProtectionService::SUCCEEDED && response) {
+ switch (trigger_type_) {
case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
UMA_HISTOGRAM_ENUMERATION(
"PasswordProtection.Verdict.PasswordFieldOnFocus",
@@ -256,10 +293,17 @@ void PasswordProtectionRequest::Finish(
LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
break;
case LoginReputationClientRequest::PASSWORD_REUSE_EVENT:
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordProtection.Verdict.ProtectedPasswordEntry",
- response->verdict_type(),
- LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
+ if (is_sync_password) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordProtection.Verdict.SyncProtectedPasswordEntry",
+ response->verdict_type(),
+ LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordProtection.Verdict.ProtectedPasswordEntry",
+ response->verdict_type(),
+ LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
+ }
break;
default:
NOTREACHED();
@@ -267,7 +311,9 @@ void PasswordProtectionRequest::Finish(
}
DCHECK(password_protection_service_);
- password_protection_service_->RequestFinished(this, std::move(response));
+ password_protection_service_->RequestFinished(
+ this, outcome == PasswordProtectionService::RESPONSE_ALREADY_CACHED,
+ std::move(response));
}
void PasswordProtectionRequest::Cancel(bool timed_out) {
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 89fcf772f41..7593ca74027 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_request.h
+++ b/chromium/components/safe_browsing/password_protection/password_protection_request.h
@@ -46,6 +46,7 @@ class PasswordProtectionRequest : public base::RefCountedThreadSafe<
const GURL& password_form_frame_url,
const std::string& saved_domain,
LoginReputationClientRequest::TriggerType type,
+ bool password_field_exists,
PasswordProtectionService* pps,
int request_timeout_in_ms);
@@ -67,7 +68,15 @@ class PasswordProtectionRequest : public base::RefCountedThreadSafe<
GURL main_frame_url() const { return main_frame_url_; }
- LoginReputationClientRequest* request_proto() { return request_proto_.get(); }
+ const LoginReputationClientRequest* request_proto() const {
+ return request_proto_.get();
+ }
+
+ content::WebContents* web_contents() const { return web_contents_; }
+
+ LoginReputationClientRequest::TriggerType trigger_type() const {
+ return trigger_type_;
+ }
private:
friend class base::RefCountedThreadSafe<PasswordProtectionRequest>;
@@ -76,16 +85,16 @@ class PasswordProtectionRequest : public base::RefCountedThreadSafe<
friend class base::DeleteHelper<PasswordProtectionRequest>;
~PasswordProtectionRequest() override;
- void CheckWhitelistOnUIThread();
+ // Start checking the whitelist.
+ void CheckWhitelist();
+
+ static void OnWhitelistCheckDoneOnIO(
+ base::WeakPtr<PasswordProtectionRequest> weak_request,
+ bool match_whitelist);
// If |main_frame_url_| matches whitelist, call Finish() immediately;
- // otherwise call CheckCachedVerdicts(). It is the task posted back to UI
- // thread by the PostTaskAndReply() in CheckWhitelistOnUIThread().
- // |match_whitelist| boolean pointer is used to pass whitelist checking result
- // between UI and IO thread. The object it points to will be deleted at the
- // end of OnWhitelistCheckDone(), since base::Owned() transfers its ownership
- // to this callback function.
- void OnWhitelistCheckDone(const bool* match_whitelist);
+ // otherwise call CheckCachedVerdicts().
+ void OnWhitelistCheckDone(bool match_whitelist);
// Looks up cached verdicts. If verdict is already cached, call SendRequest();
// otherwise call Finish().
@@ -120,7 +129,10 @@ class PasswordProtectionRequest : public base::RefCountedThreadSafe<
const std::string saved_domain_;
// If this request is for unfamiliar login page or for a password reuse event.
- const LoginReputationClientRequest::TriggerType request_type_;
+ const LoginReputationClientRequest::TriggerType trigger_type_;
+
+ // If there is a password field on the page.
+ const bool password_field_exists_;
// When request is sent.
base::TimeTicks request_start_time_;
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 69630426e8b..646c1762f19 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/chromium/components/safe_browsing/password_protection/password_protection_service.cc
@@ -4,6 +4,9 @@
#include "components/safe_browsing/password_protection/password_protection_service.h"
+#include <stddef.h>
+#include <string>
+
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
@@ -15,9 +18,11 @@
#include "base/strings/string_util.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/history/core/browser/history_service.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/safe_browsing/password_protection/password_protection_request.h"
#include "components/safe_browsing_db/database_manager.h"
#include "components/safe_browsing_db/v4_protocol_manager_util.h"
+#include "components/safe_browsing_db/whitelist_checker_client.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/google_api_keys.h"
@@ -38,6 +43,7 @@ const char kVerdictProto[] = "verdict_proto";
const int kRequestTimeoutMs = 10000;
const char kPasswordProtectionRequestUrl[] =
"https://sb-ssl.google.com/safebrowsing/clientreport/login";
+const char kPasswordOnFocusCacheKey[] = "password_on_focus_cache_key";
// Helper function to determine if the given origin matches content settings
// map's patterns.
@@ -57,12 +63,12 @@ size_t GetPathDepth(const std::string& cache_expression_path) {
.size();
}
-// Given a URL of either http or https scheme, return its scheme://hostname.
+// Given a URL of either http or https scheme, return its http://hostname.
// e.g., "https://www.foo.com:80/bar/test.cgi" -> "http://www.foo.com".
GURL GetHostNameWithHTTPScheme(const GURL& url) {
DCHECK(url.SchemeIsHTTPOrHTTPS());
std::string result(url::kHttpScheme);
- result.append(url::kStandardSchemeSeparator).append(url.HostNoBrackets());
+ result.append(url::kStandardSchemeSeparator).append(url.host());
return GURL(result);
}
@@ -74,17 +80,23 @@ const base::Feature kPasswordFieldOnFocusPinging{
const base::Feature kProtectedPasswordEntryPinging{
"ProtectedPasswordEntryPinging", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kPasswordProtectionInterstitial{
+ "PasswordProtectionInterstitial", base::FEATURE_DISABLED_BY_DEFAULT};
+
const char kPasswordOnFocusRequestOutcomeHistogramName[] =
"PasswordProtection.RequestOutcome.PasswordFieldOnFocus";
const char kPasswordEntryRequestOutcomeHistogramName[] =
"PasswordProtection.RequestOutcome.ProtectedPasswordEntry";
+const char kSyncPasswordEntryRequestOutcomeHistogramName[] =
+ "PasswordProtection.RequestOutcome.SyncPasswordEntry";
PasswordProtectionService::PasswordProtectionService(
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
HistoryService* history_service,
HostContentSettingsMap* host_content_settings_map)
- : stored_verdict_count_(-1),
+ : stored_verdict_count_password_on_focus_(-1),
+ stored_verdict_count_password_entry_(-1),
database_manager_(database_manager),
request_context_getter_(request_context_getter),
history_service_observer_(this),
@@ -102,41 +114,57 @@ PasswordProtectionService::~PasswordProtectionService() {
weak_factory_.InvalidateWeakPtrs();
}
-void PasswordProtectionService::CheckCsdWhitelistOnIOThread(
- const GURL& url,
- bool* check_result) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- *check_result =
- url.is_valid() ? database_manager_->MatchCsdWhitelistUrl(url) : true;
-}
-
bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) {
if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS())
return false;
- const std::string& hostname = url.HostNoBrackets();
+ const std::string hostname = url.HostNoBrackets();
return !net::IsLocalhost(hostname) && !net::IsHostnameNonUnique(hostname) &&
hostname.find('.') != std::string::npos;
}
+// We cache both types of pings under the same content settings type (
+// CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION). Since UNFAMILIAR_LOGING_PAGE
+// 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:
+// (1) origin, (2) 2nd level key is always |kPasswordOnFocusCacheKey|,
+// (3) cache expression.
LoginReputationClientResponse::VerdictType
PasswordProtectionService::GetCachedVerdict(
const GURL& url,
+ TriggerType trigger_type,
LoginReputationClientResponse* out_response) {
+ DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+ trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+
if (!url.is_valid())
return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
- DCHECK(content_settings_);
-
GURL hostname = GetHostNameWithHTTPScheme(url);
- std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+ std::unique_ptr<base::DictionaryValue> cache_dictionary =
base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
std::string(), nullptr));
- // Return early if there is no verdict cached for this origin.
- if (!verdict_dictionary.get() || verdict_dictionary->empty())
+
+ if (!cache_dictionary.get() || cache_dictionary->empty())
return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+ base::DictionaryValue* 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)) {
+ return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+ }
+ } else {
+ verdict_dictionary = cache_dictionary.get();
+ }
+
std::vector<std::string> paths;
GeneratePathVariantsWithoutQuery(url, &paths);
int max_path_depth = -1;
@@ -145,8 +173,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.get());
- !it.IsAtEnd(); it.Advance()) {
+ 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);
@@ -180,39 +212,65 @@ PasswordProtectionService::GetCachedVerdict(
void PasswordProtectionService::CacheVerdict(
const GURL& url,
+ TriggerType trigger_type,
LoginReputationClientResponse* verdict,
const base::Time& receive_time) {
DCHECK(verdict);
DCHECK(content_settings_);
+ DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+ trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
GURL hostname = GetHostNameWithHTTPScheme(url);
- std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+ int* stored_verdict_count =
+ trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+ ? &stored_verdict_count_password_on_focus_
+ : &stored_verdict_count_password_entry_;
+ std::unique_ptr<base::DictionaryValue> cache_dictionary =
base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
std::string(), nullptr));
- if (!verdict_dictionary.get())
- verdict_dictionary = base::MakeUnique<base::DictionaryValue>();
-
- std::unique_ptr<base::DictionaryValue> verdict_entry =
- CreateDictionaryFromVerdict(verdict, receive_time);
+ if (!cache_dictionary || !cache_dictionary.get())
+ cache_dictionary = base::MakeUnique<base::DictionaryValue>();
+
+ std::unique_ptr<base::DictionaryValue> verdict_entry(
+ CreateDictionaryFromVerdict(verdict, receive_time));
+
+ base::DictionaryValue* 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->SetDictionaryWithoutPathExpansion(
+ base::StringPiece(kPasswordOnFocusCacheKey),
+ base::MakeUnique<base::DictionaryValue>());
+ }
+ } else {
+ verdict_dictionary = cache_dictionary.get();
+ }
// Increases stored verdict count if we haven't seen this cache expression
// before.
if (!verdict_dictionary->HasKey(verdict->cache_expression()))
- stored_verdict_count_ = GetStoredVerdictCount() + 1;
+ *stored_verdict_count = GetStoredVerdictCount(trigger_type) + 1;
+
// If same cache_expression is already in this verdict_dictionary, we simply
// override it.
verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
std::move(verdict_entry));
content_settings_->SetWebsiteSettingDefaultScope(
hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
- std::string(), std::move(verdict_dictionary));
+ std::string(), std::move(cache_dictionary));
}
void PasswordProtectionService::CleanUpExpiredVerdicts() {
DCHECK(content_settings_);
- if (GetStoredVerdictCount() <= 0)
+
+ if (GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
+ GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
return;
ContentSettingsForOneType password_protection_settings;
@@ -224,44 +282,29 @@ void PasswordProtectionService::CleanUpExpiredVerdicts() {
password_protection_settings) {
GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
// Find all verdicts associated with this origin.
- std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+ 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> expired_keys;
- for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
- !it.IsAtEnd(); it.Advance()) {
- base::DictionaryValue* verdict_entry = nullptr;
- verdict_dictionary->GetDictionaryWithoutPathExpansion(it.key(),
- &verdict_entry);
- 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());
- }
- }
-
- for (const std::string& key : expired_keys) {
- verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
- stored_verdict_count_--;
- }
-
- if (verdict_dictionary->size() == 0u) {
+ bool has_expired_password_on_focus_entry = RemoveExpiredVerdicts(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ cache_dictionary.get());
+ bool has_expired_password_reuse_entry = RemoveExpiredVerdicts(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ cache_dictionary.get());
+
+ if (cache_dictionary->size() == 0u) {
content_settings_->ClearSettingsForOneTypeWithPredicate(
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url));
- } else if (expired_keys.size() > 0u) {
+ } 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|.
content_settings_->SetWebsiteSettingDefaultScope(
primary_pattern_url, GURL(),
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
- std::move(verdict_dictionary));
+ std::move(cache_dictionary));
}
}
}
@@ -272,13 +315,15 @@ void PasswordProtectionService::StartRequest(
const GURL& password_form_action,
const GURL& password_form_frame_url,
const std::string& saved_domain,
- LoginReputationClientRequest::TriggerType type) {
+ TriggerType trigger_type,
+ bool password_field_exists) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
scoped_refptr<PasswordProtectionRequest> request(
- new PasswordProtectionRequest(web_contents, main_frame_url,
- password_form_action,
- password_form_frame_url, saved_domain, type,
- this, GetRequestTimeoutInMS()));
+ new PasswordProtectionRequest(
+ web_contents, main_frame_url, password_form_action,
+ password_form_frame_url, saved_domain, trigger_type,
+ password_field_exists, this, GetRequestTimeoutInMS()));
+
DCHECK(request);
request->Start();
requests_.insert(std::move(request));
@@ -290,44 +335,63 @@ void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
const GURL& password_form_action,
const GURL& password_form_frame_url) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (CanSendPing(kPasswordFieldOnFocusPinging, main_frame_url)) {
+ if (CanSendPing(kPasswordFieldOnFocusPinging, main_frame_url, false)) {
StartRequest(web_contents, main_frame_url, password_form_action,
password_form_frame_url,
std::string(), /* saved_domain: not used for this type */
- LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
}
}
void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
WebContents* web_contents,
const GURL& main_frame_url,
- const std::string& saved_domain) {
+ const std::string& saved_domain,
+ bool password_field_exists) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (CanSendPing(kProtectedPasswordEntryPinging, main_frame_url)) {
+ if (CanSendPing(
+ kProtectedPasswordEntryPinging, main_frame_url,
+ saved_domain == std::string(password_manager::kSyncPasswordDomain))) {
StartRequest(web_contents, main_frame_url, GURL(), GURL(), saved_domain,
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ password_field_exists);
}
}
bool PasswordProtectionService::CanSendPing(const base::Feature& feature,
- const GURL& main_frame_url) {
+ const GURL& main_frame_url,
+ bool is_sync_password) {
RequestOutcome request_outcome = URL_NOT_VALID_FOR_REPUTATION_COMPUTING;
- if (IsPingingEnabled(kPasswordFieldOnFocusPinging, &request_outcome) &&
+ if (IsPingingEnabled(feature, &request_outcome) &&
CanGetReputationOfURL(main_frame_url)) {
return true;
}
- RecordNoPingingReason(feature, request_outcome);
+ RecordNoPingingReason(feature, request_outcome, is_sync_password);
return false;
}
void PasswordProtectionService::RequestFinished(
PasswordProtectionRequest* request,
+ bool already_cached,
std::unique_ptr<LoginReputationClientResponse> response) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
DCHECK(request);
- if (response)
- CacheVerdict(request->main_frame_url(), response.get(), base::Time::Now());
+
+ if (response) {
+ if (!already_cached) {
+ CacheVerdict(request->main_frame_url(), request->trigger_type(),
+ response.get(), base::Time::Now());
+ }
+
+ if (request->trigger_type() ==
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
+ response->verdict_type() == LoginReputationClientResponse::PHISHING &&
+ base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) {
+ ShowPhishingInterstitial(request->main_frame_url(),
+ response->verdict_token(),
+ request->web_contents());
+ }
+ }
// Finished processing this request. Remove it from pending list.
for (auto it = requests_.begin(); it != requests_.end(); it++) {
@@ -362,30 +426,49 @@ GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() {
return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
}
-int PasswordProtectionService::GetStoredVerdictCount() {
+int PasswordProtectionService::GetStoredVerdictCount(TriggerType trigger_type) {
DCHECK(content_settings_);
+ DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+ trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+ int* stored_verdict_count =
+ trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+ ? &stored_verdict_count_password_on_focus_
+ : &stored_verdict_count_password_entry_;
// If we have already computed this, return its value.
- if (stored_verdict_count_ >= 0)
- return stored_verdict_count_;
+ if (*stored_verdict_count >= 0)
+ return *stored_verdict_count;
ContentSettingsForOneType password_protection_settings;
content_settings_->GetSettingsForOneType(
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
&password_protection_settings);
- stored_verdict_count_ = 0;
+ stored_verdict_count_password_on_focus_ = 0;
+ stored_verdict_count_password_entry_ = 0;
if (password_protection_settings.empty())
return 0;
for (const ContentSettingPatternSource& source :
password_protection_settings) {
- std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+ std::unique_ptr<base::DictionaryValue> cache_dictionary =
base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
GURL(source.primary_pattern.ToString()), GURL(),
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
- if (verdict_dictionary.get() && !verdict_dictionary->empty())
- stored_verdict_count_ += static_cast<int>(verdict_dictionary->size());
+ 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());
+ }
+ }
}
- return stored_verdict_count_;
+ return *stored_verdict_count;
}
int PasswordProtectionService::GetRequestTimeoutInMS() {
@@ -393,7 +476,7 @@ int PasswordProtectionService::GetRequestTimeoutInMS() {
}
void PasswordProtectionService::FillUserPopulation(
- const LoginReputationClientRequest::TriggerType& request_type,
+ TriggerType trigger_type,
LoginReputationClientRequest* request_proto) {
ChromeUserPopulation* user_population = request_proto->mutable_population();
user_population->set_user_population(
@@ -423,8 +506,10 @@ void PasswordProtectionService::OnURLsDeleted(
bool expired,
const history::URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) {
- if (stored_verdict_count_ <= 0)
+ if (stored_verdict_count_password_on_focus_ <= 0 &&
+ stored_verdict_count_password_entry_ <= 0) {
return;
+ }
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
@@ -446,7 +531,8 @@ void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
if (all_history) {
content_settings_->ClearSettingsForOneType(
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
- stored_verdict_count_ = 0;
+ stored_verdict_count_password_on_focus_ = 0;
+ stored_verdict_count_password_entry_ = 0;
return;
}
@@ -457,24 +543,106 @@ void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
for (const history::URLRow& row : deleted_rows) {
if (!row.url().SchemeIsHTTPOrHTTPS())
continue;
- GURL url_key = GetHostNameWithHTTPScheme(row.url());
- std::unique_ptr<base::DictionaryValue> verdict_dictionary =
- base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
- url_key, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
- std::string(), nullptr));
-
- // Move on if we have no cached verdict for this deleted history row.
- if (!verdict_dictionary.get() || verdict_dictionary->empty())
- continue;
- int verdict_count = static_cast<int>(verdict_dictionary->size());
- stored_verdict_count_ = GetStoredVerdictCount() - verdict_count;
+ GURL url_key = GetHostNameWithHTTPScheme(row.url());
+ stored_verdict_count_password_on_focus_ =
+ GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
+ GetVerdictCountForURL(
+ url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
+ stored_verdict_count_password_entry_ =
+ GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
+ GetVerdictCountForURL(
+ url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
content_settings_->ClearSettingsForOneTypeWithPredicate(
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
base::Bind(&OriginMatchPrimaryPattern, url_key));
}
}
+int PasswordProtectionService::GetVerdictCountForURL(const GURL& url,
+ TriggerType trigger_type) {
+ DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+ trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+ std::unique_ptr<base::DictionaryValue> cache_dictionary =
+ base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+ url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
+ nullptr));
+ if (!cache_dictionary.get() || cache_dictionary->empty())
+ return 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;
+ } else {
+ return cache_dictionary->HasKey(base::StringPiece(kPasswordOnFocusCacheKey))
+ ? cache_dictionary->size() - 1
+ : cache_dictionary->size();
+ }
+}
+
+bool PasswordProtectionService::RemoveExpiredVerdicts(
+ TriggerType trigger_type,
+ 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 (!verdict_dictionary || verdict_dictionary->empty())
+ return false;
+
+ 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);
+ 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());
+ }
+ }
+
+ 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);
+ }
+
+ return expired_keys.size() > 0U;
+}
+
// static
bool PasswordProtectionService::ParseVerdictEntry(
base::DictionaryValue* verdict_entry,
@@ -558,7 +726,8 @@ PasswordProtectionService::CreateDictionaryFromVerdict(
void PasswordProtectionService::RecordNoPingingReason(
const base::Feature& feature,
- RequestOutcome reason) {
+ RequestOutcome reason,
+ bool is_sync_password) {
DCHECK(feature.name == kProtectedPasswordEntryPinging.name ||
feature.name == kPasswordFieldOnFocusPinging.name);
@@ -566,8 +735,13 @@ void PasswordProtectionService::RecordNoPingingReason(
feature.name == kProtectedPasswordEntryPinging.name;
if (is_password_entry_ping) {
- UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName, reason,
- MAX_OUTCOME);
+ if (is_sync_password) {
+ UMA_HISTOGRAM_ENUMERATION(kSyncPasswordEntryRequestOutcomeHistogramName,
+ reason, MAX_OUTCOME);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName,
+ reason, MAX_OUTCOME);
+ }
} else {
UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName,
reason, MAX_OUTCOME);
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 7060bb20c2f..a5b24bf1744 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_service.h
+++ b/chromium/components/safe_browsing/password_protection/password_protection_service.h
@@ -39,8 +39,10 @@ class PasswordProtectionRequest;
extern const base::Feature kPasswordFieldOnFocusPinging;
extern const base::Feature kProtectedPasswordEntryPinging;
+extern const base::Feature kPasswordProtectionInterstitial;
extern const char kPasswordOnFocusRequestOutcomeHistogramName[];
extern const char kPasswordEntryRequestOutcomeHistogramName[];
+extern const char kSyncPasswordEntryRequestOutcomeHistogramName[];
// Manage password protection pings and verdicts. There is one instance of this
// class per profile. Therefore, every PasswordProtectionService instance is
@@ -48,6 +50,10 @@ extern const char kPasswordEntryRequestOutcomeHistogramName[];
// HostContentSettingsMap instance.
class PasswordProtectionService : public history::HistoryServiceObserver {
public:
+ using TriggerType = LoginReputationClientRequest::TriggerType;
+ using SyncAccountType =
+ LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType;
+
// The outcome of the request. These values are used for UMA.
// DO NOT CHANGE THE ORDERING OF THESE VALUES.
enum RequestOutcome {
@@ -68,6 +74,7 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
URL_NOT_VALID_FOR_REPUTATION_COMPUTING = 14,
MAX_OUTCOME
};
+
PasswordProtectionService(
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
@@ -85,13 +92,15 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
// any thread.
LoginReputationClientResponse::VerdictType GetCachedVerdict(
const GURL& url,
+ TriggerType trigger_type,
LoginReputationClientResponse* out_response);
- // Stores |verdict| in |settings| based on |url|, |verdict| and
- // |receive_time|.
- void CacheVerdict(const GURL& url,
- LoginReputationClientResponse* verdict,
- const base::Time& receive_time);
+ // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
+ // |verdict| and |receive_time|.
+ virtual void CacheVerdict(const GURL& url,
+ TriggerType trigger_type,
+ LoginReputationClientResponse* verdict,
+ const base::Time& receive_time);
// Removes all the expired verdicts from cache.
void CleanUpExpiredVerdicts();
@@ -104,7 +113,8 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
const GURL& password_form_action,
const GURL& password_form_frame_url,
const std::string& saved_domain,
- LoginReputationClientRequest::TriggerType type);
+ TriggerType trigger_type,
+ bool password_field_exists);
virtual void MaybeStartPasswordFieldOnFocusRequest(
content::WebContents* web_contents,
@@ -115,7 +125,8 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
virtual void MaybeStartProtectedPasswordEntryRequest(
content::WebContents* web_contents,
const GURL& main_frame_url,
- const std::string& saved_domain);
+ const std::string& saved_domain,
+ bool password_field_exists);
scoped_refptr<SafeBrowsingDatabaseManager> database_manager();
@@ -130,26 +141,29 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
protected:
friend class PasswordProtectionRequest;
-
+ FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest, VerifyCanSendPing);
// 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.)
- bool CanSendPing(const base::Feature& feature, const GURL& main_frame_url);
+ // a local host). |is_sync_password| is used for UMA metric recording.
+ bool CanSendPing(const base::Feature& feature,
+ const GURL& main_frame_url,
+ bool is_sync_password);
// Called by a PasswordProtectionRequest instance when it finishes to remove
// itself from |requests_|.
virtual void RequestFinished(
PasswordProtectionRequest* request,
+ bool already_cached,
std::unique_ptr<LoginReputationClientResponse> response);
// Cancels all requests in |requests_|, empties it, and releases references to
// the requests.
void CancelPendingRequests();
- // Gets the total number of verdict (no matter expired or not) we cached for
- // current active profile.
- virtual int GetStoredVerdictCount();
+ // Gets the total number of verdicts of the specified |trigger_type| we cached
+ // for this profile. This counts both expired and active verdicts.
+ virtual int GetStoredVerdictCount(TriggerType trigger_type);
scoped_refptr<net::URLRequestContextGetter> request_context_getter() {
return request_context_getter_;
@@ -168,9 +182,8 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
int event_tab_id, // -1 if tab id is not available.
LoginReputationClientRequest::Frame* frame) = 0;
- void FillUserPopulation(
- const LoginReputationClientRequest::TriggerType& request_type,
- LoginReputationClientRequest* request_proto);
+ void FillUserPopulation(TriggerType trigger_type,
+ LoginReputationClientRequest* request_proto);
virtual bool IsExtendedReporting() = 0;
@@ -181,6 +194,14 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
virtual bool IsHistorySyncEnabled() = 0;
+ virtual void ShowPhishingInterstitial(const GURL& phishing_url,
+ const std::string& token,
+ content::WebContents* web_contents) = 0;
+
+ // Gets the type of sync account associated with current profile or
+ // |NOT_SIGNED_IN|.
+ virtual SyncAccountType GetSyncAccountType() = 0;
+
void CheckCsdWhitelistOnIOThread(const GURL& url, bool* check_result);
HostContentSettingsMap* content_settings() const { return content_settings_; }
@@ -214,6 +235,15 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
void RemoveContentSettingsOnURLsDeleted(bool all_history,
const history::URLRows& deleted_rows);
+ // Helper function called by RemoveContentSettingsOnURLsDeleted(..). It
+ // calculate the number of verdicts of |type| that associate with |url|.
+ int GetVerdictCountForURL(const GURL& url, TriggerType type);
+
+ // Remove verdict of |type| from |cache_dictionary|. Return false if no
+ // verdict removed, true otherwise.
+ bool RemoveExpiredVerdicts(TriggerType type,
+ base::DictionaryValue* cache_dictionary);
+
static bool ParseVerdictEntry(base::DictionaryValue* verdict_entry,
int* out_verdict_received_time,
LoginReputationClientResponse* out_verdict);
@@ -235,9 +265,14 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
const base::Time& receive_time);
static void RecordNoPingingReason(const base::Feature& feature,
- RequestOutcome reason);
- // Number of verdict stored for this profile.
- int stored_verdict_count_;
+ RequestOutcome reason,
+ bool is_sync_password);
+ // Number of verdict stored for this profile for password on focus pings.
+ int stored_verdict_count_password_on_focus_;
+
+ // Number of verdict stored for this profile for protected password entry
+ // pings.
+ int stored_verdict_count_password_entry_;
scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
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 9748541fecb..eb9a5971ddc 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
@@ -10,6 +10,8 @@
#include "base/test/histogram_tester.h"
#include "base/test/null_task_runner.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing/features.h"
#include "components/safe_browsing/password_protection/password_protection_request.h"
#include "components/safe_browsing_db/test_database_manager.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -18,14 +20,16 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using testing::_;
+using testing::ElementsAre;
+using testing::Return;
+
namespace {
const char kFormActionUrl[] = "https://form_action.com/";
const char kPasswordFrameUrl[] = "https://password_frame.com/";
const char kSavedDomain[] = "saved_domain.com";
const char kTargetUrl[] = "http://foo.com/";
-const char kVerdictHistogramName[] =
- "PasswordProtection.Verdict.PasswordFieldOnFocus";
} // namespace
@@ -35,7 +39,8 @@ class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
public:
MockSafeBrowsingDatabaseManager() {}
- MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL&));
+ MOCK_METHOD2(CheckCsdWhitelistUrl,
+ AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
protected:
~MockSafeBrowsingDatabaseManager() override {}
@@ -78,6 +83,7 @@ class TestPasswordProtectionService : public PasswordProtectionService {
void RequestFinished(
PasswordProtectionRequest* request,
+ bool already_cached_unused,
std::unique_ptr<LoginReputationClientResponse> response) override {
latest_request_ = request;
latest_response_ = std::move(response);
@@ -102,11 +108,21 @@ class TestPasswordProtectionService : public PasswordProtectionService {
bool IsPingingEnabled(const base::Feature& feature,
RequestOutcome* reason) override {
+ checked_feature_name_ = feature.name;
return true;
}
+ void ShowPhishingInterstitial(const GURL& phishing_url,
+ const std::string& token,
+ content::WebContents* web_contents) override {}
+
bool IsHistorySyncEnabled() override { return false; }
+ LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
+ GetSyncAccountType() override {
+ return LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN;
+ }
+
LoginReputationClientResponse* latest_response() {
return latest_response_.get();
}
@@ -115,15 +131,18 @@ class TestPasswordProtectionService : public PasswordProtectionService {
size_t GetPendingRequestsCount() { return requests_.size(); }
- LoginReputationClientRequest* GetLatestRequestProto() {
+ const LoginReputationClientRequest* GetLatestRequestProto() {
return latest_request_ ? latest_request_->request_proto() : nullptr;
}
+ std::string checked_feature_name() { return checked_feature_name_; }
+
private:
bool is_extended_reporting_;
bool is_incognito_;
PasswordProtectionRequest* latest_request_;
std::unique_ptr<LoginReputationClientResponse> latest_response_;
+ std::string checked_feature_name_;
DISALLOW_COPY_AND_ASSIGN(TestPasswordProtectionService);
};
@@ -161,25 +180,28 @@ class PasswordProtectionServiceTest : public testing::Test {
void InitializeAndStartPasswordOnFocusRequest(bool match_whitelist,
int timeout_in_ms) {
GURL target_url(kTargetUrl);
- EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(target_url))
- .WillRepeatedly(testing::Return(match_whitelist));
+ EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url, _))
+ .WillRepeatedly(
+ Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequest(
nullptr, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
std::string(), LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- password_protection_service_.get(), timeout_in_ms);
+ true, password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
- void InitializeAndStartPasswordEntryRequest(bool match_whitelist,
+ void InitializeAndStartPasswordEntryRequest(const std::string& saved_domain,
+ bool match_whitelist,
int timeout_in_ms) {
GURL target_url(kTargetUrl);
- EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(target_url))
- .WillRepeatedly(testing::Return(match_whitelist));
+ EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url, _))
+ .WillRepeatedly(
+ Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequest(
- nullptr, target_url, GURL(), GURL(), std::string(kSavedDomain),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ nullptr, target_url, GURL(), GURL(), saved_domain,
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT, true,
password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
@@ -194,20 +216,17 @@ class PasswordProtectionServiceTest : public testing::Test {
}
void CacheVerdict(const GURL& url,
+ LoginReputationClientRequest::TriggerType trigger,
LoginReputationClientResponse::VerdictType verdict,
int cache_duration_sec,
const std::string& cache_expression,
const base::Time& verdict_received_time) {
LoginReputationClientResponse response(
CreateVerdictProto(verdict, cache_duration_sec, cache_expression));
- password_protection_service_->CacheVerdict(url, &response,
+ password_protection_service_->CacheVerdict(url, trigger, &response,
verdict_received_time);
}
- size_t GetStoredVerdictCount() {
- return password_protection_service_->GetStoredVerdictCount();
- }
-
void CacheInvalidVerdict() {
GURL invalid_hostname("http://invalid.com");
std::unique_ptr<base::DictionaryValue> verdict_dictionary =
@@ -229,6 +248,10 @@ class PasswordProtectionServiceTest : public testing::Test {
std::string(), std::move(verdict_dictionary));
}
+ size_t GetStoredVerdictCount(LoginReputationClientRequest::TriggerType type) {
+ return password_protection_service_->GetStoredVerdictCount(type);
+ }
+
protected:
// |thread_bundle_| is needed here because this test involves both UI and IO
// threads.
@@ -318,80 +341,173 @@ TEST_F(PasswordProtectionServiceTest, TestPathVariantsMatchCacheExpression) {
GURL("http://evil.com/worse/index.html"), cache_expression_with_slash));
}
-TEST_F(PasswordProtectionServiceTest, TestCachedVerdicts) {
- ASSERT_EQ(0U, GetStoredVerdictCount());
+TEST_F(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
+ ASSERT_EQ(0U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
// Assume each verdict has a TTL of 10 minutes.
// Cache a verdict for http://www.test.com/foo/index.html
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foo",
base::Time::Now());
- EXPECT_EQ(1U, GetStoredVerdictCount());
+ EXPECT_EQ(1U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// Cache another verdict with the some origin and cache_expression should
// override the cache.
CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/foo",
base::Time::Now());
- EXPECT_EQ(1U, GetStoredVerdictCount());
+ 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"), &out_verdict));
+ EXPECT_EQ(
+ LoginReputationClientResponse::PHISHING,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://www.test.com/foo/index2.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &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,
LoginReputationClientResponse::SAFE, 10 * 60, "test.com/bar",
base::Time::Now());
- EXPECT_EQ(2U, GetStoredVerdictCount());
+ EXPECT_EQ(2U, 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,
+ LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foobar",
+ base::Time::Now());
+ EXPECT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+ EXPECT_EQ(1U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+}
+
+TEST_F(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
+ ASSERT_EQ(0U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+ // Assume each verdict has a TTL of 10 minutes.
+ // Cache a verdict for http://www.test.com/foo/index.html
+ CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foo",
+ base::Time::Now());
+
+ EXPECT_EQ(1U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+ // 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::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::SAFE, 10 * 60, "test.com/bar",
+ base::Time::Now());
+ EXPECT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+ // Now cache a PASSWORD_REUSE_EVENT verdict, stored verdict count for
+ // UNFAMILIAR_LOGIN_PAGE should be the same.
+ CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foobar",
+ base::Time::Now());
+ EXPECT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+ EXPECT_EQ(1U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
}
TEST_F(PasswordProtectionServiceTest, TestGetCachedVerdicts) {
- ASSERT_EQ(0U, GetStoredVerdictCount());
- // Prepare 2 verdicts of the same origin with different cache expressions,
- // one is expired, the other is not.
+ ASSERT_EQ(0U, GetStoredVerdictCount(
+ 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.
base::Time now = base::Time::Now();
CacheVerdict(GURL("http://test.com/login.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::SAFE, 10 * 60, "test.com", now);
CacheVerdict(
GURL("http://test.com/def/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/def",
base::Time::FromDoubleT(now.ToDoubleT() -
24.0 * 60.0 * 60.0)); // Yesterday, expired.
- ASSERT_EQ(2U, GetStoredVerdictCount());
+ CacheVerdict(GURL("http://test.com/bar/login.html"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/bar",
+ now);
+
+ ASSERT_EQ(2U, 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/"), &actual_verdict));
+ EXPECT_EQ(
+ LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://www.unknown.com/"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &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"), &actual_verdict));
+ EXPECT_EQ(
+ LoginReputationClientResponse::SAFE,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://test.com/xyz/foo.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &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"), &actual_verdict));
+ EXPECT_EQ(
+ LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://test.com/def/ghi/index.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
}
TEST_F(PasswordProtectionServiceTest, TestRemoveCachedVerdictOnURLsDeleted) {
- ASSERT_EQ(0U, GetStoredVerdictCount());
+ ASSERT_EQ(0U, GetStoredVerdictCount(
+ 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".
base::Time now = base::Time::Now();
CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
"foo.com/abc", now);
CacheVerdict(GURL("http://bar.com/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::PHISHING, 10 * 60, "bar.com",
now);
- ASSERT_EQ(2U, GetStoredVerdictCount());
+ ASSERT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+ CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
+ "foo.com/abc", now);
+ CacheVerdict(GURL("http://bar.com/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::PHISHING, 10 * 60, "bar.com",
+ now);
+ ASSERT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// Delete a bar.com URL. Corresponding content setting keyed on
// origin "http://bar.com" should be removed,
@@ -404,17 +520,31 @@ TEST_F(PasswordProtectionServiceTest, TestRemoveCachedVerdictOnURLsDeleted) {
password_protection_service_->RemoveContentSettingsOnURLsDeleted(
false /* all_history */, deleted_urls);
- EXPECT_EQ(1U, GetStoredVerdictCount());
+ EXPECT_EQ(1U, 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, &actual_verdict));
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
- GURL("http://bar.com"), &actual_verdict));
+ GURL("http://bar.com"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ &actual_verdict));
// If delete all history. All password protection content settings should be
// gone.
password_protection_service_->RemoveContentSettingsOnURLsDeleted(
true /* all_history */, history::URLRows());
- EXPECT_EQ(0U, GetStoredVerdictCount());
+ EXPECT_EQ(0U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+ EXPECT_EQ(0U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
}
TEST_F(PasswordProtectionServiceTest, VerifyCanGetReputationOfURL) {
@@ -438,7 +568,7 @@ TEST_F(PasswordProtectionServiceTest, VerifyCanGetReputationOfURL) {
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://10.0.1.0/")));
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
- GURL("http://FEED::BEEF")));
+ GURL("http://[FEED::BEEF]")));
// Main frame URL is a no-yet-assigned y ICANN gTLD.
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
@@ -463,19 +593,21 @@ TEST_F(PasswordProtectionServiceTest, TestNoRequestSentForWhitelistedURL) {
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
- testing::ElementsAre(base::Bucket(4 /* MATCHED_WHITELIST */, 1)));
+ ElementsAre(base::Bucket(4 /* MATCHED_WHITELIST */, 1)));
}
TEST_F(PasswordProtectionServiceTest, TestNoRequestSentIfVerdictAlreadyCached) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogramName, 0);
- CacheVerdict(GURL(kTargetUrl), LoginReputationClientResponse::LOW_REPUTATION,
- 600, GURL(kTargetUrl).host(), base::Time::Now());
+ CacheVerdict(GURL(kTargetUrl),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::LOW_REPUTATION, 600,
+ GURL(kTargetUrl).host(), base::Time::Now());
InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
10000 /* timeout in ms*/);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
- testing::ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
+ ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
password_protection_service_->latest_response()->verdict_type());
}
@@ -494,7 +626,7 @@ TEST_F(PasswordProtectionServiceTest, TestResponseFetchFailed) {
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
- testing::ElementsAre(base::Bucket(9 /* FETCH_FAILED */, 1)));
+ ElementsAre(base::Bucket(9 /* FETCH_FAILED */, 1)));
}
TEST_F(PasswordProtectionServiceTest, TestMalformedResponse) {
@@ -513,7 +645,7 @@ TEST_F(PasswordProtectionServiceTest, TestMalformedResponse) {
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
- testing::ElementsAre(base::Bucket(10 /* RESPONSE_MALFORMED */, 1)));
+ ElementsAre(base::Bucket(10 /* RESPONSE_MALFORMED */, 1)));
}
TEST_F(PasswordProtectionServiceTest, TestRequestTimedout) {
@@ -524,10 +656,11 @@ TEST_F(PasswordProtectionServiceTest, TestRequestTimedout) {
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
- testing::ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
+ ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
}
-TEST_F(PasswordProtectionServiceTest, TestRequestAndResponseSuccessfull) {
+TEST_F(PasswordProtectionServiceTest,
+ TestPasswordOnFocusRequestAndResponseSuccessfull) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogramName, 0);
// Set up valid response.
net::TestURLFetcher fetcher(0, GURL("http://bar.com"), nullptr);
@@ -544,9 +677,10 @@ TEST_F(PasswordProtectionServiceTest, TestRequestAndResponseSuccessfull) {
base::RunLoop().RunUntilIdle();
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
- testing::ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
- EXPECT_THAT(histograms_.GetAllSamples(kVerdictHistogramName),
- testing::ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+ ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+ EXPECT_THAT(histograms_.GetAllSamples(
+ "PasswordProtection.Verdict.PasswordFieldOnFocus"),
+ ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
LoginReputationClientResponse* actual_response =
password_protection_service_->latest_response();
EXPECT_EQ(expected_response.verdict_type(), actual_response->verdict_type());
@@ -556,15 +690,63 @@ TEST_F(PasswordProtectionServiceTest, TestRequestAndResponseSuccessfull) {
actual_response->cache_duration_sec());
}
+TEST_F(PasswordProtectionServiceTest,
+ TestPasswordEntryRequestAndResponseSuccessfull) {
+ histograms_.ExpectTotalCount(kPasswordEntryRequestOutcomeHistogramName, 0);
+ histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogramName,
+ 0);
+ // Set up valid response.
+ net::TestURLFetcher fetcher(0, GURL("http://bar.com"), nullptr);
+ fetcher.set_status(
+ net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK));
+ fetcher.set_response_code(200);
+ LoginReputationClientResponse expected_response = CreateVerdictProto(
+ LoginReputationClientResponse::PHISHING, 600, GURL(kTargetUrl).host());
+ fetcher.SetResponseString(expected_response.SerializeAsString());
+
+ // Initiate a saved password entry request.
+ InitializeAndStartPasswordEntryRequest(
+ "example.com", false /* match whitelist */, 10000 /* timeout in ms*/);
+ request_->OnURLFetchComplete(&fetcher);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(
+ histograms_.GetAllSamples(kPasswordEntryRequestOutcomeHistogramName),
+ ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+ EXPECT_THAT(histograms_.GetAllSamples(
+ "PasswordProtection.Verdict.ProtectedPasswordEntry"),
+ ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+ histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogramName,
+ 0);
+
+ // Initiate a sync password entry request.
+ InitializeAndStartPasswordEntryRequest(password_manager::kSyncPasswordDomain,
+ false /* match whitelist */,
+ 10000 /* timeout in ms*/);
+ request_->OnURLFetchComplete(&fetcher);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(
+ histograms_.GetAllSamples(kSyncPasswordEntryRequestOutcomeHistogramName),
+ ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+ EXPECT_THAT(
+ histograms_.GetAllSamples(kPasswordEntryRequestOutcomeHistogramName),
+ ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+ EXPECT_THAT(histograms_.GetAllSamples(
+ "PasswordProtection.Verdict.SyncProtectedPasswordEntry"),
+ ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+ EXPECT_THAT(histograms_.GetAllSamples(
+ "PasswordProtection.Verdict.ProtectedPasswordEntry"),
+ ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+}
+
TEST_F(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogramName, 0);
GURL target_url(kTargetUrl);
- EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(target_url))
- .WillRepeatedly(testing::Return(false));
+ EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url, _))
+ .WillRepeatedly(Return(AsyncMatch::NO_MATCH));
password_protection_service_->StartRequest(
nullptr, target_url, GURL("http://foo.com/submit"),
GURL("http://foo.com/frame"), std::string(),
- LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
// Destroy password_protection_service_ while there is one request pending.
password_protection_service_.reset();
@@ -572,46 +754,91 @@ TEST_F(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogramName),
- testing::ElementsAre(base::Bucket(2 /* CANCELED */, 1)));
+ ElementsAre(base::Bucket(2 /* CANCELED */, 1)));
}
TEST_F(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
- ASSERT_EQ(0U, GetStoredVerdictCount());
- // Prepare 4 verdicts:
+ // Prepare 4 verdicts for PASSWORD_REUSE_EVENT:
// (1) "foo.com/abc" valid
// (2) "foo.com/def" expired
// (3) "bar.com/abc" expired
// (4) "bar.com/def" expired
base::Time now = base::Time::Now();
CacheVerdict(GURL("https://foo.com/abc/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
"foo.com/abc", now);
CacheVerdict(GURL("https://foo.com/def/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::LOW_REPUTATION, 0, "foo.com/def",
now);
CacheVerdict(GURL("https://bar.com/abc/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::PHISHING, 0, "bar.com/abc", now);
CacheVerdict(GURL("https://bar.com/def/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
LoginReputationClientResponse::PHISHING, 0, "bar.com/def", now);
- ASSERT_EQ(4U, GetStoredVerdictCount());
+ ASSERT_EQ(4U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+ // Prepare 2 verdicts for UNFAMILIAR_LOGIN_PAGE:
+ // (1) "bar.com/def" valid
+ // (2) "bar.com/xyz" expired
+ CacheVerdict(GURL("https://bar.com/def/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::SAFE, 10 * 60, "bar.com/def",
+ now);
+ CacheVerdict(GURL("https://bar.com/xyz/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ LoginReputationClientResponse::PHISHING, 0, "bar.com/xyz", now);
+ ASSERT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
password_protection_service_->CleanUpExpiredVerdicts();
- ASSERT_EQ(1U, GetStoredVerdictCount());
+ ASSERT_EQ(1U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+ ASSERT_EQ(1U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
LoginReputationClientResponse actual_verdict;
- // Has cached verdict for foo.com/abc.
- EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
+ // 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));
+ // 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));
+ // 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));
+ // 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, &actual_verdict));
+
+ // Has cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/def.
+ EXPECT_EQ(LoginReputationClientResponse::SAFE,
password_protection_service_->GetCachedVerdict(
- GURL("https://foo.com/abc/test.jsp"), &actual_verdict));
- // No cached verdict for foo.com/def.
+ GURL("https://bar.com/def/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ &actual_verdict));
+
+ // No cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/xyz.
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
- GURL("https://foo.com/def/index.jsp"), &actual_verdict));
- // Nothing in content setting for bar.com.
- EXPECT_EQ(nullptr, content_setting_map_->GetWebsiteSetting(
- GURL("https://bar.com"), GURL(),
- CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
- std::string(), nullptr));
+ GURL("https://bar.com/xyz/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ &actual_verdict));
}
TEST_F(PasswordProtectionServiceTest,
@@ -647,7 +874,7 @@ TEST_F(PasswordProtectionServiceTest, VerifyPasswordOnFocusRequestProto) {
request_->OnURLFetchComplete(&fetcher);
base::RunLoop().RunUntilIdle();
- LoginReputationClientRequest* actual_request =
+ const LoginReputationClientRequest* actual_request =
password_protection_service_->GetLatestRequestProto();
EXPECT_EQ(kTargetUrl, actual_request->page_url());
EXPECT_EQ(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
@@ -660,8 +887,7 @@ TEST_F(PasswordProtectionServiceTest, VerifyPasswordOnFocusRequestProto) {
EXPECT_EQ(kFormActionUrl, actual_request->frames(1).forms(0).action_url());
}
-TEST_F(PasswordProtectionServiceTest,
- VerifyProtectedPasswordEntryRequestProto) {
+TEST_F(PasswordProtectionServiceTest, VerifyPasswordProtectionRequestProto) {
// Set up valid response.
net::TestURLFetcher fetcher(0, GURL("http://bar.com"), nullptr);
fetcher.set_status(
@@ -670,21 +896,53 @@ TEST_F(PasswordProtectionServiceTest,
LoginReputationClientResponse expected_response = CreateVerdictProto(
LoginReputationClientResponse::PHISHING, 600, GURL(kTargetUrl).host());
fetcher.SetResponseString(expected_response.SerializeAsString());
- InitializeAndStartPasswordEntryRequest(false /* match whitelist */,
- 100000 /* timeout in ms*/);
+ // Initialize request triggered by chrome sync password reuse.
+ InitializeAndStartPasswordEntryRequest(
+ std::string(password_manager::kSyncPasswordDomain),
+ false /* match whitelist */, 100000 /* timeout in ms*/);
base::RunLoop().RunUntilIdle();
request_->OnURLFetchComplete(&fetcher);
base::RunLoop().RunUntilIdle();
- LoginReputationClientRequest* actual_request =
+ const LoginReputationClientRequest* actual_request =
password_protection_service_->GetLatestRequestProto();
EXPECT_EQ(kTargetUrl, actual_request->page_url());
EXPECT_EQ(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
actual_request->trigger_type());
EXPECT_EQ(1, actual_request->frames_size());
EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
- // TODO(jialiul): Update this test when we're ready to fill more fields.
- ASSERT_FALSE(actual_request->has_password_reuse_event());
+ EXPECT_TRUE(actual_request->frames(0).has_password_field());
+ ASSERT_TRUE(actual_request->has_password_reuse_event());
+ ASSERT_TRUE(
+ actual_request->password_reuse_event().is_chrome_signin_password());
+
+ // Initialize request triggered by saved password reuse.
+ InitializeAndStartPasswordEntryRequest(std::string(kSavedDomain),
+ false /* match whitelist */,
+ 100000 /* timeout in ms*/);
+ base::RunLoop().RunUntilIdle();
+ request_->OnURLFetchComplete(&fetcher);
+ base::RunLoop().RunUntilIdle();
+
+ actual_request = password_protection_service_->GetLatestRequestProto();
+ ASSERT_TRUE(actual_request->has_password_reuse_event());
+ ASSERT_FALSE(
+ actual_request->password_reuse_event().is_chrome_signin_password());
+}
+
+TEST_F(PasswordProtectionServiceTest, VerifyCanSendPing) {
+ GURL suspicious_url("http://phishing.com");
+ EXPECT_TRUE(password_protection_service_->CanSendPing(
+ kProtectedPasswordEntryPinging, suspicious_url,
+ true /* is_sync_password */));
+ EXPECT_EQ(kProtectedPasswordEntryPinging.name,
+ password_protection_service_->checked_feature_name());
+
+ EXPECT_TRUE(password_protection_service_->CanSendPing(
+ kPasswordFieldOnFocusPinging, suspicious_url,
+ false /* is_sync_password */));
+ EXPECT_EQ(kPasswordFieldOnFocusPinging.name,
+ password_protection_service_->checked_feature_name());
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/renderer/BUILD.gn b/chromium/components/safe_browsing/renderer/BUILD.gn
index 03dbfe67f5b..02239b48b2f 100644
--- a/chromium/components/safe_browsing/renderer/BUILD.gn
+++ b/chromium/components/safe_browsing/renderer/BUILD.gn
@@ -12,6 +12,7 @@ source_set("renderer") {
]
deps = [
"//base",
+ "//components/safe_browsing:features",
"//components/safe_browsing/common:common",
"//content/public/renderer",
"//ipc",
@@ -20,3 +21,39 @@ source_set("renderer") {
]
}
}
+
+source_set("websocket_sb_handshake_throttle") {
+ sources = [
+ "websocket_sb_handshake_throttle.cc",
+ "websocket_sb_handshake_throttle.h",
+ ]
+
+ deps = [
+ "//base:base",
+ "//components/safe_browsing/common:interfaces",
+ "//content/public/common:common",
+ "//content/public/renderer:renderer",
+ "//ipc",
+ "//services/service_manager/public/cpp:cpp",
+ "//third_party/WebKit/public:blink",
+ "//url:url",
+ ]
+}
+
+source_set("websocket_sb_handshake_throttle_unittest") {
+ testonly = true
+ sources = [
+ "websocket_sb_handshake_throttle_unittest.cc",
+ ]
+
+ deps = [
+ ":websocket_sb_handshake_throttle",
+ "//base:base",
+ "//base/test:test_support",
+ "//components/safe_browsing/common:interfaces",
+ "//ipc",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/WebKit/public:blink",
+ ]
+}
diff --git a/chromium/components/safe_browsing/renderer/DEPS b/chromium/components/safe_browsing/renderer/DEPS
index 04ab6a902c4..15dc39ee340 100644
--- a/chromium/components/safe_browsing/renderer/DEPS
+++ b/chromium/components/safe_browsing/renderer/DEPS
@@ -1,5 +1,8 @@
include_rules = [
"+content/public/renderer",
+ "+components/safe_browsing",
"+third_party/WebKit/public/platform",
"+third_party/WebKit/public/web",
-] \ No newline at end of file
+ "+ipc",
+ "+mojo/public/cpp",
+]
diff --git a/chromium/components/safe_browsing/renderer/threat_dom_details.cc b/chromium/components/safe_browsing/renderer/threat_dom_details.cc
index 0a36545b6c5..823b36a2c92 100644
--- a/chromium/components/safe_browsing/renderer/threat_dom_details.cc
+++ b/chromium/components/safe_browsing/renderer/threat_dom_details.cc
@@ -6,6 +6,7 @@
#include <algorithm>
#include <map>
+#include <string>
#include <unordered_set>
#include "base/compiler_specific.h"
@@ -15,6 +16,7 @@
#include "base/strings/stringprintf.h"
#include "components/safe_browsing/common/safebrowsing_messages.h"
#include "components/safe_browsing/common/safebrowsing_types.h"
+#include "components/safe_browsing/features.h"
#include "content/public/renderer/render_frame.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/web/WebDocument.h"
@@ -34,15 +36,6 @@ namespace safe_browsing {
// messages generated by ThreatDOMDetails.
using ElementToNodeMap = std::map<blink::WebNode, size_t>;
-// This Feature specifies which non-resource HTML Elements to collect based on
-// their tag and attributes. It's a single param containing a comma-separated
-// list of pairs. For example: "tag1,id,tag1,height,tag2,foo" - this will
-// collect elements with tag "tag1" that have attribute "id" or "height" set,
-// and elements of tag "tag2" if they have attribute "foo" set. All tag names
-// and attributes should be lower case.
-const base::Feature kThreatDomDetailsTagAndAttributeFeature{
- "ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
-
// The name of the param containing the tags and attributes list.
const char kTagAndAttributeParamName[] = "tag_attribute_csv";
@@ -60,10 +53,24 @@ class TagNameIs {
std::string tag_;
};
+void GetDefaultTagAndAttributeList(
+ std::vector<TagAndAttributesItem>* tag_and_attributes_list) {
+ tag_and_attributes_list->clear();
+
+ // These entries must be sorted by tag name.
+ // These tags are related to identifying Google ads.
+ tag_and_attributes_list->push_back(
+ TagAndAttributesItem("div", {"data-google-query-id", "id"}));
+ tag_and_attributes_list->push_back(TagAndAttributesItem("iframe", {"id"}));
+}
+
void ParseTagAndAttributeParams(
std::vector<TagAndAttributesItem>* tag_and_attributes_list) {
DCHECK(tag_and_attributes_list);
+ // If the feature is disabled we just use the default list. Otherwise the list
+ // from the Finch param will be the one used.
if (!base::FeatureList::IsEnabled(kThreatDomDetailsTagAndAttributeFeature)) {
+ GetDefaultTagAndAttributeList(tag_and_attributes_list);
return;
}
tag_and_attributes_list->clear();
@@ -108,7 +115,7 @@ SafeBrowsingHostMsg_ThreatDOMDetails_Node* GetNodeForElement(
const blink::WebNode& element,
const safe_browsing::ElementToNodeMap& element_to_node_map,
std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node>* resources) {
- DCHECK(element_to_node_map.count(element) > 0);
+ DCHECK_GT(element_to_node_map.count(element), 0u);
size_t resource_index = element_to_node_map.at(element);
return &(resources->at(resource_index));
}
@@ -239,8 +246,15 @@ bool ShouldHandleElement(
} // namespace
TagAndAttributesItem::TagAndAttributesItem() {}
+
+TagAndAttributesItem::TagAndAttributesItem(
+ const std::string& tag_name_param,
+ const std::vector<std::string>& attributes_param)
+ : tag_name(tag_name_param), attributes(attributes_param) {}
+
TagAndAttributesItem::TagAndAttributesItem(const TagAndAttributesItem& item)
: tag_name(item.tag_name), attributes(item.attributes) {}
+
TagAndAttributesItem::~TagAndAttributesItem() {}
uint32_t ThreatDOMDetails::kMaxNodes = 500;
@@ -280,7 +294,7 @@ void ThreatDOMDetails::OnGetThreatDOMDetails() {
void ThreatDOMDetails::ExtractResources(
std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node>* resources) {
- blink::WebFrame* frame = render_frame()->GetWebFrame();
+ blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
if (!frame)
return;
SafeBrowsingHostMsg_ThreatDOMDetails_Node details_node;
diff --git a/chromium/components/safe_browsing/renderer/threat_dom_details.h b/chromium/components/safe_browsing/renderer/threat_dom_details.h
index 0f48d0b16f6..1b1e3933cac 100644
--- a/chromium/components/safe_browsing/renderer/threat_dom_details.h
+++ b/chromium/components/safe_browsing/renderer/threat_dom_details.h
@@ -19,7 +19,6 @@ struct SafeBrowsingHostMsg_ThreatDOMDetails_Node;
namespace safe_browsing {
-extern const base::Feature kThreatDomDetailsTagAndAttributeFeature;
extern const char kTagAndAttributeParamName[];
// Represents the tag name of an HTML Element and its associated attributes.
@@ -28,6 +27,8 @@ extern const char kTagAndAttributeParamName[];
class TagAndAttributesItem {
public:
TagAndAttributesItem();
+ TagAndAttributesItem(const std::string& tag_name_param,
+ const std::vector<std::string>& attributes_param);
TagAndAttributesItem(const TagAndAttributesItem& item);
~TagAndAttributesItem();
diff --git a/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.cc b/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.cc
new file mode 100644
index 00000000000..7e7bfefcbf2
--- /dev/null
+++ b/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.cc
@@ -0,0 +1,85 @@
+// 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/safe_browsing/renderer/websocket_sb_handshake_throttle.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/renderer/render_frame.h"
+#include "ipc/ipc_message.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+
+namespace safe_browsing {
+
+WebSocketSBHandshakeThrottle::WebSocketSBHandshakeThrottle(
+ mojom::SafeBrowsing* safe_browsing)
+ : callbacks_(nullptr),
+ safe_browsing_(safe_browsing),
+ result_(Result::UNKNOWN),
+ weak_factory_(this) {}
+
+WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() {
+ // ThrottleHandshake() should always be called, but since that is done all the
+ // way over in Blink, just avoid logging if it is not called rather than
+ // DCHECK()ing.
+ if (start_time_.is_null())
+ return;
+ if (result_ == Result::UNKNOWN) {
+ result_ = Result::ABANDONED;
+ UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Abandoned",
+ base::TimeTicks::Now() - start_time_);
+ }
+ UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.WebSocket.Result", result_,
+ Result::RESULT_COUNT);
+}
+
+void WebSocketSBHandshakeThrottle::ThrottleHandshake(
+ const blink::WebURL& url,
+ blink::WebLocalFrame* web_local_frame,
+ blink::WebCallbacks<void, const blink::WebString&>* callbacks) {
+ DCHECK(!callbacks_);
+ DCHECK(!url_checker_);
+ callbacks_ = callbacks;
+ url_ = url;
+ int render_frame_id = MSG_ROUTING_NONE;
+ if (web_local_frame) {
+ render_frame_id =
+ content::RenderFrame::FromWebFrame(web_local_frame)->GetRoutingID();
+ }
+ int load_flags = 0;
+ start_time_ = base::TimeTicks::Now();
+ safe_browsing_->CreateCheckerAndCheck(
+ render_frame_id, mojo::MakeRequest(&url_checker_), url, load_flags,
+ content::RESOURCE_TYPE_SUB_RESOURCE,
+ base::BindOnce(&WebSocketSBHandshakeThrottle::OnCheckResult,
+ weak_factory_.GetWeakPtr()));
+}
+
+void WebSocketSBHandshakeThrottle::OnCheckResult(bool safe) {
+ DCHECK(!start_time_.is_null());
+ base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_;
+ if (safe) {
+ result_ = Result::SAFE;
+ UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Safe", elapsed);
+ callbacks_->OnSuccess();
+ } else {
+ // When the insterstitial is dismissed the page is navigated and this object
+ // is destroyed before reaching here.
+ result_ = Result::BLOCKED;
+ UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Blocked", elapsed);
+ callbacks_->OnError(blink::WebString::FromUTF8(base::StringPrintf(
+ "WebSocket connection to %s failed safe browsing check",
+ url_.spec().c_str())));
+ }
+ // |this| is destroyed here.
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.h b/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.h
new file mode 100644
index 00000000000..71a2c73832c
--- /dev/null
+++ b/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle.h
@@ -0,0 +1,58 @@
+// 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.
+
+// Implementation of SafeBrowsing for WebSockets. This code runs inside the
+// render process, calling the interface defined in safe_browsing.mojom to
+// communicate with the SafeBrowsing service.
+
+#ifndef COMPONENTS_SAFE_BROWSING_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
+#define COMPONENTS_SAFE_BROWSING_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "third_party/WebKit/public/platform/WebCallbacks.h"
+#include "third_party/WebKit/public/platform/WebSocketHandshakeThrottle.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+class WebSocketSBHandshakeThrottle : public blink::WebSocketHandshakeThrottle {
+ public:
+ explicit WebSocketSBHandshakeThrottle(mojom::SafeBrowsing* safe_browsing);
+ ~WebSocketSBHandshakeThrottle() override;
+
+ void ThrottleHandshake(
+ const blink::WebURL& url,
+ blink::WebLocalFrame* web_local_frame,
+ blink::WebCallbacks<void, const blink::WebString&>* callbacks) override;
+
+ private:
+ // These values are logged to UMA so do not renumber or reuse.
+ enum class Result {
+ UNKNOWN = 0,
+ SAFE = 1,
+ BLOCKED = 2,
+ ABANDONED = 3,
+ RESULT_COUNT
+ };
+ void OnCheckResult(bool safe);
+
+ GURL url_;
+ blink::WebCallbacks<void, const blink::WebString&>* callbacks_;
+ mojom::SafeBrowsingUrlCheckerPtr url_checker_;
+ mojom::SafeBrowsing* safe_browsing_;
+ base::TimeTicks start_time_;
+ Result result_;
+
+ base::WeakPtrFactory<WebSocketSBHandshakeThrottle> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketSBHandshakeThrottle);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
diff --git a/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle_unittest.cc b/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle_unittest.cc
new file mode 100644
index 00000000000..806768d3c7e
--- /dev/null
+++ b/chromium/components/safe_browsing/renderer/websocket_sb_handshake_throttle_unittest.cc
@@ -0,0 +1,135 @@
+// 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/safe_browsing/renderer/websocket_sb_handshake_throttle.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "content/public/common/resource_type.h"
+#include "ipc/ipc_message.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+
+namespace safe_browsing {
+
+namespace {
+
+constexpr char kTestUrl[] = "wss://test/";
+
+class FakeSafeBrowsing : public mojom::SafeBrowsing {
+ public:
+ FakeSafeBrowsing() : render_frame_id_(), load_flags_(-1), resource_type_() {}
+
+ void CreateCheckerAndCheck(int32_t render_frame_id,
+ mojom::SafeBrowsingUrlCheckerRequest request,
+ const GURL& url,
+ int32_t load_flags,
+ content::ResourceType resource_type,
+ CreateCheckerAndCheckCallback callback) override {
+ render_frame_id_ = render_frame_id;
+ request_ = std::move(request);
+ url_ = url;
+ load_flags_ = load_flags;
+ resource_type_ = resource_type;
+ callback_ = std::move(callback);
+ run_loop_.Quit();
+ }
+
+ void RunUntilCalled() { run_loop_.Run(); }
+
+ int32_t render_frame_id_;
+ mojom::SafeBrowsingUrlCheckerRequest request_;
+ GURL url_;
+ int32_t load_flags_;
+ content::ResourceType resource_type_;
+ CreateCheckerAndCheckCallback callback_;
+ base::RunLoop run_loop_;
+};
+
+class FakeWebCallbacks
+ : public blink::WebCallbacks<void, const blink::WebString&> {
+ public:
+ enum Result { RESULT_NOT_CALLED, RESULT_SUCCESS, RESULT_ERROR };
+
+ FakeWebCallbacks() : result_(RESULT_NOT_CALLED) {}
+
+ void OnSuccess() override {
+ result_ = RESULT_SUCCESS;
+ run_loop_.Quit();
+ }
+
+ void OnError(const blink::WebString& message) override {
+ result_ = RESULT_ERROR;
+ message_ = message;
+ run_loop_.Quit();
+ }
+
+ void RunUntilCalled() { run_loop_.Run(); }
+
+ Result result_;
+ blink::WebString message_;
+ base::RunLoop run_loop_;
+};
+
+class WebSocketSBHandshakeThrottleTest : public ::testing::Test {
+ protected:
+ WebSocketSBHandshakeThrottleTest() : mojo_binding_(&safe_browsing_) {
+ mojo_binding_.Bind(mojo::MakeRequest(&safe_browsing_ptr_));
+ throttle_ = base::MakeUnique<WebSocketSBHandshakeThrottle>(
+ safe_browsing_ptr_.get());
+ }
+
+ base::test::ScopedTaskEnvironment message_loop_;
+ FakeSafeBrowsing safe_browsing_;
+ mojo::Binding<mojom::SafeBrowsing> mojo_binding_;
+ mojom::SafeBrowsingPtr safe_browsing_ptr_;
+ std::unique_ptr<WebSocketSBHandshakeThrottle> throttle_;
+ FakeWebCallbacks fake_callbacks_;
+};
+
+TEST_F(WebSocketSBHandshakeThrottleTest, Construction) {}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, CheckArguments) {
+ throttle_->ThrottleHandshake(GURL(kTestUrl), nullptr, &fake_callbacks_);
+ safe_browsing_.RunUntilCalled();
+ // TODO(ricea): Find a way to create a WebLocalFrame in a unit test so that
+ // the code that looks up the render_frame_id can be tested.
+ EXPECT_EQ(MSG_ROUTING_NONE, safe_browsing_.render_frame_id_);
+ EXPECT_EQ(GURL(kTestUrl), safe_browsing_.url_);
+ EXPECT_EQ(0, safe_browsing_.load_flags_);
+ EXPECT_EQ(content::RESOURCE_TYPE_SUB_RESOURCE, safe_browsing_.resource_type_);
+ EXPECT_TRUE(safe_browsing_.callback_);
+}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, Safe) {
+ throttle_->ThrottleHandshake(GURL(kTestUrl), nullptr, &fake_callbacks_);
+ safe_browsing_.RunUntilCalled();
+ std::move(safe_browsing_.callback_).Run(true);
+ fake_callbacks_.RunUntilCalled();
+ EXPECT_EQ(FakeWebCallbacks::RESULT_SUCCESS, fake_callbacks_.result_);
+}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, Unsafe) {
+ throttle_->ThrottleHandshake(GURL(kTestUrl), nullptr, &fake_callbacks_);
+ safe_browsing_.RunUntilCalled();
+ std::move(safe_browsing_.callback_).Run(false);
+ fake_callbacks_.RunUntilCalled();
+ EXPECT_EQ(FakeWebCallbacks::RESULT_ERROR, fake_callbacks_.result_);
+ EXPECT_EQ(
+ blink::WebString(
+ "WebSocket connection to wss://test/ failed safe browsing check"),
+ fake_callbacks_.message_);
+}
+
+} // namespace
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/triggers/BUILD.gn b/chromium/components/safe_browsing/triggers/BUILD.gn
index aef9c9ab961..32721f0838d 100644
--- a/chromium/components/safe_browsing/triggers/BUILD.gn
+++ b/chromium/components/safe_browsing/triggers/BUILD.gn
@@ -5,18 +5,60 @@
import("//build/config/features.gni")
source_set("triggers") {
- if (safe_browsing_mode != 0) {
- sources = [
- "trigger_manager.cc",
- "trigger_manager.h",
- ]
+ sources = [
+ "trigger_manager.cc",
+ "trigger_manager.h",
+ ]
+ public_deps = [
+ "//components/security_interstitials/content:security_interstitial_page",
+ "//components/security_interstitials/core:core",
+ "//content/public/browser:browser",
+ ]
+ deps = [
+ ":trigger_throttler",
+ "//base:base",
+ "//components/prefs:prefs",
+ "//components/safe_browsing:safe_browsing",
+ "//components/safe_browsing/browser:browser",
+ ]
+}
+
+source_set("trigger_throttler") {
+ sources = [
+ "trigger_throttler.cc",
+ "trigger_throttler.h",
+ ]
+ deps = [
+ "//base:base",
+ ]
+}
+
+source_set("ad_sampler_trigger") {
+ sources = [
+ "ad_sampler_trigger.cc",
+ "ad_sampler_trigger.h",
+ ]
+ deps = [
+ "//base:base",
+ "//components/safe_browsing:features",
+ "//content/public/browser",
+ ]
+}
- deps = [
- "//base:base",
- "//components/safe_browsing:safe_browsing",
- "//components/safe_browsing/browser:browser",
- "//components/security_interstitials/content:security_interstitial_page",
- "//content/public/browser:browser",
- ]
- }
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "trigger_manager_unittest.cc",
+ "trigger_throttler_unittest.cc",
+ ]
+ deps = [
+ ":trigger_throttler",
+ ":triggers",
+ "//base",
+ "//chrome/test:test_support",
+ "//components/prefs:test_support",
+ "//components/safe_browsing/browser:browser",
+ "//content/test:test_support",
+ "//testing/gtest",
+ ]
}
diff --git a/chromium/components/safe_browsing/triggers/DEPS b/chromium/components/safe_browsing/triggers/DEPS
new file mode 100644
index 00000000000..d25d0af40be
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/test",
+ "+components/prefs",
+]
diff --git a/chromium/components/safe_browsing/triggers/OWNERS b/chromium/components/safe_browsing/triggers/OWNERS
new file mode 100644
index 00000000000..c83e5cafe05
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/OWNERS
@@ -0,0 +1 @@
+lpz@chromium.org
diff --git a/chromium/components/safe_browsing/triggers/ad_sampler_trigger.cc b/chromium/components/safe_browsing/triggers/ad_sampler_trigger.cc
new file mode 100644
index 00000000000..8ca19eaa080
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/ad_sampler_trigger.cc
@@ -0,0 +1,96 @@
+// 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/safe_browsing/triggers/ad_sampler_trigger.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/safe_browsing/features.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(safe_browsing::AdSamplerTrigger);
+
+namespace safe_browsing {
+
+// Param name of the denominator for controlling sampling frequency.
+const char kAdSamplerFrequencyDenominatorParam[] =
+ "safe_browsing_ad_sampler_frequency_denominator";
+
+// A frequency denominator with this value indicates sampling is disabled.
+const size_t kSamplerFrequencyDisabled = 0;
+
+namespace {
+
+size_t GetSamplerFrequencyDenominator() {
+ if (!base::FeatureList::IsEnabled(kAdSamplerTriggerFeature))
+ return kSamplerFrequencyDisabled;
+
+ const std::string sampler_frequency_denominator =
+ base::GetFieldTrialParamValueByFeature(
+ kAdSamplerTriggerFeature, kAdSamplerFrequencyDenominatorParam);
+ int result;
+ return base::StringToInt(sampler_frequency_denominator, &result)
+ ? result
+ : kSamplerFrequencyDisabled;
+}
+
+bool DetectGoogleAd(content::NavigationHandle* navigation_handle) {
+ // TODO(crbug.com/742397): This function is temporarily copied from
+ // c/b/page_load_metrics/observers/ads_page_load_metrics_observer.cc
+ // This code should be updated to use shared infrastructure when available.
+
+ // In case the navigation aborted, look up the RFH by the Frame Tree Node
+ // ID. It returns the committed frame host or the initial frame host for the
+ // frame if no committed host exists. Using a previous host is fine because
+ // once a frame has an ad we always consider it to have an ad.
+ // We use the unsafe method of FindFrameByFrameTreeNodeId because we're not
+ // concerned with which process the frame lives on (we only want to know if an
+ // ad could be present on the page right now).
+ content::RenderFrameHost* current_frame_host =
+ navigation_handle->GetWebContents()->UnsafeFindFrameByFrameTreeNodeId(
+ navigation_handle->GetFrameTreeNodeId());
+ if (current_frame_host) {
+ const std::string& frame_name = current_frame_host->GetFrameName();
+ if (base::StartsWith(frame_name, "google_ads_iframe",
+ base::CompareCase::SENSITIVE) ||
+ base::StartsWith(frame_name, "google_ads_frame",
+ base::CompareCase::SENSITIVE)) {
+ return true;
+ }
+ }
+
+ const GURL& url = navigation_handle->GetURL();
+ return url.host_piece() == "tpc.googlesyndication.com" &&
+ base::StartsWith(url.path_piece(), "/safeframe",
+ base::CompareCase::SENSITIVE);
+}
+
+bool ShouldCheckForAd(const size_t frequency_denominator) {
+ return frequency_denominator != kSamplerFrequencyDisabled &&
+ (base::RandUint64() % frequency_denominator) == 0;
+}
+
+} // namespace
+
+AdSamplerTrigger::AdSamplerTrigger(content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ sampler_frequency_denominator_(GetSamplerFrequencyDenominator()) {}
+
+AdSamplerTrigger::~AdSamplerTrigger() {}
+
+void AdSamplerTrigger::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ // TODO(lpz): Add UMA metrics for how often we skip checking, find nothing, or
+ // take a sample.
+ if (ShouldCheckForAd(sampler_frequency_denominator_) &&
+ DetectGoogleAd(navigation_handle)) {
+ // TODO(lpz): Call into TriggerManager to sample the ad.
+ }
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/triggers/ad_sampler_trigger.h b/chromium/components/safe_browsing/triggers/ad_sampler_trigger.h
new file mode 100644
index 00000000000..ab4c87c2595
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/ad_sampler_trigger.h
@@ -0,0 +1,49 @@
+// 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_SAFE_BROWSING_TRIGGERS_AD_SAMPLER_TRIGGER_H_
+#define COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_SAMPLER_TRIGGER_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_contents_observer.h"
+
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class NavigationHandle;
+}
+
+namespace safe_browsing {
+
+// Param name of the denominator for controlling sampling frequency.
+extern const char kAdSamplerFrequencyDenominatorParam[];
+
+// A frequency denominator with this value indicates sampling is disabled.
+extern const size_t kSamplerFrequencyDisabled;
+
+// This class periodically checks for Google ads on the page and may decide to
+// send a report to Google with the ad's structure for further analysis.
+class AdSamplerTrigger : public content::WebContentsObserver,
+ public content::WebContentsUserData<AdSamplerTrigger> {
+ public:
+ ~AdSamplerTrigger() override;
+
+ // content::WebContentsObserver implementation.
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ private:
+ explicit AdSamplerTrigger(content::WebContents* contents);
+ friend class content::WebContentsUserData<AdSamplerTrigger>;
+
+ // Ad samples will be collected with frequency
+ // 1/|sampler_frequency_denominator_|
+ size_t sampler_frequency_denominator_;
+
+ DISALLOW_COPY_AND_ASSIGN(AdSamplerTrigger);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_SAMPLER_TRIGGER_H_
diff --git a/chromium/components/safe_browsing/triggers/trigger_manager.cc b/chromium/components/safe_browsing/triggers/trigger_manager.cc
index d453630f8ff..eeec916a499 100644
--- a/chromium/components/safe_browsing/triggers/trigger_manager.cc
+++ b/chromium/components/safe_browsing/triggers/trigger_manager.cc
@@ -4,26 +4,133 @@
#include "components/safe_browsing/triggers/trigger_manager.h"
+#include "components/prefs/pref_service.h"
#include "components/safe_browsing/base_ui_manager.h"
#include "components/safe_browsing/browser/threat_details.h"
-#include "content/public/browser/web_contents.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
#include "net/url_request/url_request_context_getter.h"
namespace safe_browsing {
+namespace {
+
+bool CanStartDataCollection(const SBErrorOptions& error_display_options,
+ const TriggerThrottler& throttler,
+ const TriggerType trigger_type) {
+ // We start data collection as long as user is not incognito and is able to
+ // change the Extended Reporting opt-in, and the |trigger_type| has available
+ // quota. We don't require users to be opted-in to SBER to begin collecting
+ // data, since they may be able to change the setting while data collection is
+ // running (eg: on a security interstitial).
+ return !error_display_options.is_off_the_record &&
+ error_display_options.is_extended_reporting_opt_in_allowed &&
+ throttler.TriggerCanFire(trigger_type);
+}
+
+bool CanSendReport(const SBErrorOptions& error_display_options) {
+ // Reports are only sent for non-incoginito users who are allowed to modify
+ // the Extended Reporting setting and have opted-in to Extended Reporting.
+ return !error_display_options.is_off_the_record &&
+ error_display_options.is_extended_reporting_opt_in_allowed &&
+ error_display_options.is_extended_reporting_enabled;
+}
+
+} // namespace
+
+DataCollectorsContainer::DataCollectorsContainer() {}
+DataCollectorsContainer::~DataCollectorsContainer() {}
+
TriggerManager::TriggerManager(BaseUIManager* ui_manager)
: ui_manager_(ui_manager) {}
TriggerManager::~TriggerManager() {}
-ThreatDetails* TriggerManager::StartCollectingThreatDetails(
+// static
+SBErrorOptions TriggerManager::GetSBErrorDisplayOptions(
+ const PrefService& pref_service,
+ const content::WebContents& web_contents) {
+ return SBErrorOptions(/*is_main_frame_load_blocked=*/false,
+ IsExtendedReportingOptInAllowed(pref_service),
+ web_contents.GetBrowserContext()->IsOffTheRecord(),
+ IsExtendedReportingEnabled(pref_service),
+ /*is_scout_reporting_enabled=*/false,
+ /*is_proceed_anyway_disabled=*/false,
+ /*should_open_links_in_new_tab=*/false,
+ /*help_center_article_link=*/std::string());
+}
+
+bool TriggerManager::StartCollectingThreatDetails(
+ const TriggerType trigger_type,
content::WebContents* web_contents,
const security_interstitials::UnsafeResource& resource,
net::URLRequestContextGetter* request_context_getter,
- history::HistoryService* history_service) {
- return ThreatDetails::NewThreatDetails(ui_manager_, web_contents, resource,
- request_context_getter,
- history_service);
+ history::HistoryService* history_service,
+ const SBErrorOptions& error_display_options) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (!CanStartDataCollection(error_display_options, trigger_throttler_,
+ trigger_type))
+ return false;
+
+ // Ensure we're not already collecting data on this tab.
+ // TODO(lpz): this check should be more specific to check that this type of
+ // data collector is not running on this tab (once additional data collectors
+ // exist).
+ if (base::ContainsKey(data_collectors_map_, web_contents))
+ return false;
+
+ DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
+ collectors->threat_details = scoped_refptr<ThreatDetails>(
+ ThreatDetails::NewThreatDetails(ui_manager_, web_contents, resource,
+ request_context_getter, history_service));
+ return true;
+}
+
+bool TriggerManager::FinishCollectingThreatDetails(
+ const TriggerType trigger_type,
+ content::WebContents* web_contents,
+ const base::TimeDelta& delay,
+ bool did_proceed,
+ int num_visits,
+ const SBErrorOptions& error_display_options) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ // Make sure there's a data collector running on this tab.
+ // TODO(lpz): this check should be more specific to check that the right type
+ // of data collector is running on this tab (once additional data collectors
+ // exist).
+ if (!base::ContainsKey(data_collectors_map_, web_contents))
+ return false;
+
+ // Determine whether a report should be sent.
+ bool should_send_report = CanSendReport(error_display_options);
+
+ DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
+ // Find the data collector and tell it to finish collecting data, and then
+ // remove it from our map. We release ownership of the data collector here but
+ // it will live until the end of the FinishCollection call because it
+ // implements RefCountedThreadSafe.
+ if (should_send_report) {
+ scoped_refptr<ThreatDetails> threat_details = collectors->threat_details;
+ content::BrowserThread::PostDelayedTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&ThreatDetails::FinishCollection, threat_details,
+ did_proceed, num_visits),
+ delay);
+
+ // Record that this trigger fired and collected data.
+ trigger_throttler_.TriggerFired(trigger_type);
+ }
+
+ // Regardless of whether the report got sent, clean up the data collector on
+ // this tab.
+ collectors->threat_details = nullptr;
+ data_collectors_map_.erase(web_contents);
+
+ return should_send_report;
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/triggers/trigger_manager.h b/chromium/components/safe_browsing/triggers/trigger_manager.h
index 1e66b0cd871..fcc0df62b06 100644
--- a/chromium/components/safe_browsing/triggers/trigger_manager.h
+++ b/chromium/components/safe_browsing/triggers/trigger_manager.h
@@ -5,8 +5,16 @@
#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_MANAGER_H_
#define COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_MANAGER_H_
+#include <unordered_map>
+
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/triggers/trigger_throttler.h"
#include "components/security_interstitials/content/unsafe_resource.h"
+#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
+#include "content/public/browser/web_contents.h"
+
+class PrefService;
namespace history {
class HistoryService;
@@ -21,6 +29,31 @@ namespace safe_browsing {
class BaseUIManager;
class ThreatDetails;
+// A wrapper around different kinds of data collectors that can be active on a
+// given browser tab. Any given field can be null or empty if the associated
+// data is not being collected.
+struct DataCollectorsContainer {
+ public:
+ DataCollectorsContainer();
+ ~DataCollectorsContainer();
+
+ // Note: new data collection types should be added below as additional fields.
+
+ // Collects ThreatDetails which contains resource URLs and partial DOM.
+ scoped_refptr<ThreatDetails> threat_details;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DataCollectorsContainer);
+};
+
+// Stores the data collectors that are active on each WebContents (ie: browser
+// tab).
+using DataCollectorsMap =
+ std::unordered_map<content::WebContents*, DataCollectorsContainer>;
+
+using SBErrorOptions =
+ security_interstitials::BaseSafeBrowsingErrorUI::SBErrorDisplayOptions;
+
// This class manages SafeBrowsing data-reporting triggers. Triggers are
// activated for users opted-in to Extended Reporting and when security-related
// data collection is required.
@@ -33,17 +66,61 @@ class TriggerManager {
TriggerManager(BaseUIManager* ui_manager);
~TriggerManager();
- ThreatDetails* StartCollectingThreatDetails(
+ // Returns a SBErrorDisplayOptions struct containing user state that is
+ // relevant for TriggerManager to decide whether to start/finish data
+ // collection. Looks at incognito state from |web_contents|, and opt-ins from
+ // |pref_service|. Only the fields needed by TriggerManager will be set.
+ static SBErrorOptions GetSBErrorDisplayOptions(
+ const PrefService& pref_service,
+ const content::WebContents& web_contents);
+
+ // Begins collecting a ThreatDetails report on the specified |web_contents|.
+ // |resource| is the unsafe resource that cause the collection to occur.
+ // |request_context_getter| is used to retrieve data from the HTTP cache.
+ // |history_service| is used to get data about redirects.
+ // |error_display_options| contains the current state of relevant user
+ // preferences. We use this object for interop with WebView, in Chrome it
+ // should be created by TriggerManager::GetSBErrorDisplayOptions().
+ // Returns true if the collection began, or false if it didn't.
+ bool StartCollectingThreatDetails(
+ const TriggerType trigger_type,
content::WebContents* web_contents,
const security_interstitials::UnsafeResource& resource,
net::URLRequestContextGetter* request_context_getter,
- history::HistoryService* history_service);
+ history::HistoryService* history_service,
+ const SBErrorOptions& error_display_options);
+
+ // Completes the collection of a ThreatDetails report on the specified
+ // |web_contents| and sends the report. |delay| can be used to wait a period
+ // of time before finishing the report. |did_proceed| indicates whether the
+ // user proceeded through the security interstitial associated with this
+ // report. |num_visits| is how many times the user has visited the site
+ // before. |error_display_options| contains the current state of relevant user
+ // preferences. We use this object for interop with WebView, in Chrome it
+ // should be created by TriggerManager::GetSBErrorDisplayOptions().
+ // Returns true if the report was completed and sent, or false otherwise (eg:
+ // the user was not opted-in to extended reporting after collection began).
+ bool FinishCollectingThreatDetails(
+ const TriggerType trigger_type,
+ content::WebContents* web_contents,
+ const base::TimeDelta& delay,
+ bool did_proceed,
+ int num_visits,
+ const SBErrorOptions& error_display_options);
private:
+ friend class TriggerManagerTest;
+
// The UI manager is used to send reports to Google. Not owned.
// TODO(lpz): we may only need a the PingManager here.
BaseUIManager* ui_manager_;
+ // Map of the data collectors running on each tabs.
+ DataCollectorsMap data_collectors_map_;
+
+ // Keeps track of how often triggers fire and throttles them when needed.
+ TriggerThrottler trigger_throttler_;
+
DISALLOW_COPY_AND_ASSIGN(TriggerManager);
};
diff --git a/chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc b/chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc
new file mode 100644
index 00000000000..662f446aff9
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 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/safe_browsing/triggers/trigger_manager.h"
+
+#include "base/stl_util.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/browser/threat_details.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_contents_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Key;
+using testing::UnorderedElementsAre;
+
+namespace safe_browsing {
+
+// Mock ThreatDetails class that makes FinishCollection a no-op.
+class MockThreatDetails : public ThreatDetails {
+ public:
+ MockThreatDetails() : ThreatDetails() {}
+ MOCK_METHOD2(FinishCollection, void(bool did_proceed, int num_visits));
+
+ private:
+ ~MockThreatDetails() override {}
+ DISALLOW_COPY_AND_ASSIGN(MockThreatDetails);
+};
+
+class MockThreatDetailsFactory : public ThreatDetailsFactory {
+ public:
+ ~MockThreatDetailsFactory() override {}
+
+ ThreatDetails* CreateThreatDetails(
+ BaseUIManager* ui_manager,
+ content::WebContents* web_contents,
+ const security_interstitials::UnsafeResource& unsafe_resource,
+ net::URLRequestContextGetter* request_context_getter,
+ history::HistoryService* history_service) override {
+ MockThreatDetails* threat_details = new MockThreatDetails();
+ return threat_details;
+ }
+};
+
+class TriggerManagerTest : public ::testing::Test {
+ public:
+ TriggerManagerTest() : trigger_manager_(/*ui_manager=*/nullptr) {}
+ ~TriggerManagerTest() override {}
+
+ void SetUp() override {
+ ThreatDetails::RegisterFactory(&mock_threat_details_factory_);
+
+ // Register any prefs that are needed by the trigger manager. By default,
+ // enable Safe Browsing and Extended Reporting.
+ safe_browsing::RegisterProfilePrefs(pref_service_.registry());
+ SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+ SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
+ SetPref(prefs::kSafeBrowsingScoutGroupSelected, true);
+ }
+
+ void SetPref(const std::string& pref, bool value) {
+ pref_service_.SetBoolean(pref, value);
+ }
+
+ content::WebContents* CreateWebContents() {
+ DCHECK(!browser_context_.IsOffTheRecord())
+ << "CreateWebContents() should not be called after "
+ "CreateIncognitoWebContents()";
+ return web_contents_factory_.CreateWebContents(&browser_context_);
+ }
+
+ content::WebContents* CreateIncognitoWebContents() {
+ browser_context_.set_is_off_the_record(true);
+ return web_contents_factory_.CreateWebContents(&browser_context_);
+ }
+
+ bool StartCollectingThreatDetails(content::WebContents* web_contents) {
+ SBErrorOptions options =
+ TriggerManager::GetSBErrorDisplayOptions(pref_service_, *web_contents);
+ return trigger_manager_.StartCollectingThreatDetails(
+ TriggerType::SECURITY_INTERSTITIAL, web_contents,
+ security_interstitials::UnsafeResource(), nullptr, nullptr, options);
+ }
+
+ bool FinishCollectingThreatDetails(content::WebContents* web_contents,
+ bool expect_report_sent) {
+ if (expect_report_sent) {
+ MockThreatDetails* threat_details = static_cast<MockThreatDetails*>(
+ trigger_manager_.data_collectors_map_[web_contents]
+ .threat_details.get());
+ EXPECT_CALL(*threat_details, FinishCollection(_, _)).Times(1);
+ }
+ SBErrorOptions options =
+ TriggerManager::GetSBErrorDisplayOptions(pref_service_, *web_contents);
+ return trigger_manager_.FinishCollectingThreatDetails(
+ TriggerType::SECURITY_INTERSTITIAL, web_contents, base::TimeDelta(),
+ false, 0, options);
+ }
+
+ const DataCollectorsMap& data_collectors_map() {
+ return trigger_manager_.data_collectors_map_;
+ }
+
+ private:
+ TriggerManager trigger_manager_;
+ MockThreatDetailsFactory mock_threat_details_factory_;
+ content::TestBrowserThreadBundle thread_bundle_;
+ content::TestBrowserContext browser_context_;
+ content::TestWebContentsFactory web_contents_factory_;
+ TestingPrefServiceSimple pref_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(TriggerManagerTest);
+};
+
+TEST_F(TriggerManagerTest, StartAndFinishCollectingThreatDetails) {
+ // Basic workflow is to start and finish data collection with a single
+ // WebContents.
+ content::WebContents* web_contents1 = CreateWebContents();
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents1));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents1)));
+ EXPECT_TRUE(FinishCollectingThreatDetails(web_contents1, true));
+ EXPECT_TRUE(data_collectors_map().empty());
+
+ // More complex scenarios can happen, where collection happens on two
+ // WebContents at the same time, possibly starting and completing in different
+ // order.
+ content::WebContents* web_contents2 = CreateWebContents();
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents1));
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents2));
+ EXPECT_THAT(data_collectors_map(),
+ UnorderedElementsAre(Key(web_contents1), Key(web_contents2)));
+ EXPECT_TRUE(FinishCollectingThreatDetails(web_contents2, true));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents1)));
+ EXPECT_TRUE(FinishCollectingThreatDetails(web_contents1, true));
+ EXPECT_TRUE(data_collectors_map().empty());
+
+ // Calling Start twice with the same WebContents is an error, and will return
+ // false the second time. But it can still be completed normally.
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents1));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents1)));
+ EXPECT_FALSE(StartCollectingThreatDetails(web_contents1));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents1)));
+ EXPECT_TRUE(FinishCollectingThreatDetails(web_contents1, true));
+ EXPECT_TRUE(data_collectors_map().empty());
+
+ // Calling Finish twice with the same WebContents is an error, and will return
+ // false the second time. It's basically a no-op.
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents1));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents1)));
+ EXPECT_TRUE(FinishCollectingThreatDetails(web_contents1, true));
+ EXPECT_TRUE(data_collectors_map().empty());
+ EXPECT_FALSE(FinishCollectingThreatDetails(web_contents1, false));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, NoDataCollection_Incognito) {
+ // Data collection will not begin and no reports will be sent when incognito.
+ content::WebContents* web_contents = CreateIncognitoWebContents();
+ EXPECT_FALSE(StartCollectingThreatDetails(web_contents));
+ EXPECT_TRUE(data_collectors_map().empty());
+ EXPECT_FALSE(FinishCollectingThreatDetails(web_contents, false));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, NoDataCollection_SBEROptInDisallowed) {
+ // Data collection will not begin and no reports will be sent when the user is
+ // not allowed to opt-in to SBER.
+ SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+ content::WebContents* web_contents = CreateWebContents();
+ EXPECT_FALSE(StartCollectingThreatDetails(web_contents));
+ EXPECT_TRUE(data_collectors_map().empty());
+ EXPECT_FALSE(FinishCollectingThreatDetails(web_contents, false));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, NoDataCollection_IncognitoAndSBEROptInDisallowed) {
+ // Data collection will not begin and no reports will be sent when the user is
+ // not allowed to opt-in to SBER and is also incognito.
+ SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+ content::WebContents* web_contents = CreateIncognitoWebContents();
+ EXPECT_FALSE(StartCollectingThreatDetails(web_contents));
+ EXPECT_TRUE(data_collectors_map().empty());
+ EXPECT_FALSE(FinishCollectingThreatDetails(web_contents, false));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, UserOptedOutOfSBER_DataCollected_NoReportSent) {
+ // When the user is opted-out of SBER then data collection will begin but no
+ // report will be sent when data collection ends.
+ SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+ content::WebContents* web_contents = CreateWebContents();
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+ EXPECT_FALSE(FinishCollectingThreatDetails(web_contents, false));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, UserOptsOutOfSBER_DataCollected_NoReportSent) {
+ // If the user opts-out of Extended Reporting while data is being collected
+ // then no report is sent. Note that the test fixture opts the user into
+ // Extended Reporting by default.
+ content::WebContents* web_contents = CreateWebContents();
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+
+ SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+
+ EXPECT_FALSE(FinishCollectingThreatDetails(web_contents, false));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, UserOptsInToSBER_DataCollected_ReportSent) {
+ // When the user is opted-out of SBER then data collection will begin. If they
+ // opt-in to SBER while data collection is in progress then the report will
+ // also be sent.
+ SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+ content::WebContents* web_contents = CreateWebContents();
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+
+ SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
+
+ EXPECT_TRUE(FinishCollectingThreatDetails(web_contents, true));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest,
+ SBEROptInBecomesDisallowed_DataCollected_NoReportSent) {
+ // If the user loses the ability to opt-in to SBER in the middle of data
+ // collection then the report will not be sent.
+ content::WebContents* web_contents = CreateWebContents();
+ EXPECT_TRUE(StartCollectingThreatDetails(web_contents));
+ EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+
+ // Remove the ability to opt-in to SBER.
+ SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+
+ EXPECT_FALSE(FinishCollectingThreatDetails(web_contents, false));
+ EXPECT_TRUE(data_collectors_map().empty());
+}
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/triggers/trigger_throttler.cc b/chromium/components/safe_browsing/triggers/trigger_throttler.cc
new file mode 100644
index 00000000000..cb00346e4c3
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/trigger_throttler.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 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/safe_browsing/triggers/trigger_throttler.h"
+
+#include "base/stl_util.h"
+#include "base/time/time.h"
+
+namespace safe_browsing {
+
+namespace {
+const size_t kUnlimitedTriggerQuota = std::numeric_limits<size_t>::max();
+constexpr base::TimeDelta kOneDayTimeDelta = base::TimeDelta::FromDays(1);
+
+size_t GetDailyQuotaForTrigger(const TriggerType trigger_type) {
+ switch (trigger_type) {
+ case TriggerType::SECURITY_INTERSTITIAL:
+ return kUnlimitedTriggerQuota;
+ }
+ // By default, unhandled trigger types have no quota.
+ return 0;
+}
+
+} // namespace
+
+TriggerThrottler::TriggerThrottler() {}
+
+TriggerThrottler::~TriggerThrottler() {}
+
+bool TriggerThrottler::TriggerCanFire(const TriggerType trigger_type) const {
+ // Lookup how many times this trigger is allowed to fire each day.
+ const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+
+ // Some basic corner cases for triggers that always fire, or disabled
+ // triggers that never fire.
+ if (trigger_quota == kUnlimitedTriggerQuota)
+ return true;
+ if (trigger_quota == 0)
+ return false;
+
+ // Other triggers are capped, see how many times this trigger has already
+ // fired.
+ if (!base::ContainsKey(trigger_events_, trigger_type))
+ return true;
+
+ const std::vector<time_t>& timestamps = trigger_events_.at(trigger_type);
+ // More quota is available, so the trigger can fire again.
+ if (trigger_quota > timestamps.size())
+ return true;
+
+ // Otherwise, we have more events than quota, check which day they occured on.
+ // Newest events are at the end of vector so we can simply look at the
+ // Nth-from-last entry (where N is the quota) to see if it happened within
+ // the current day or earlier.
+ base::Time min_timestamp = base::Time::Now() - kOneDayTimeDelta;
+ const size_t pos = timestamps.size() - trigger_quota + 1;
+ return timestamps[pos] < min_timestamp.ToTimeT();
+}
+
+void TriggerThrottler::TriggerFired(const TriggerType trigger_type) {
+ // Lookup how many times this trigger is allowed to fire each day.
+ const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+
+ // For triggers that always fire, don't bother tracking quota.
+ if (trigger_quota == kUnlimitedTriggerQuota)
+ return;
+
+ // Otherwise, record that the trigger fired.
+ std::vector<time_t>* timestamps = &trigger_events_[trigger_type];
+ timestamps->push_back(base::Time::Now().ToTimeT());
+
+ // Clean up the trigger events map.
+ CleanupOldEvents();
+}
+
+void TriggerThrottler::CleanupOldEvents() {
+ for (const auto& map_iter : trigger_events_) {
+ const TriggerType trigger_type = map_iter.first;
+ const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+ const std::vector<time_t>& trigger_times = map_iter.second;
+
+ // Skip the cleanup if we have quota room, quotas should generally be small.
+ if (trigger_times.size() < trigger_quota)
+ return;
+
+ std::vector<time_t> tmp_trigger_times;
+ base::Time min_timestamp = base::Time::Now() - kOneDayTimeDelta;
+ // Go over the event times for this trigger and keep timestamps which are
+ // newer than |min_timestamp|. We put timestamps in a temp vector that will
+ // get swapped into the map in place of the existing vector.
+ for (const time_t timestamp : trigger_times) {
+ if (timestamp > min_timestamp.ToTimeT())
+ tmp_trigger_times.push_back(timestamp);
+ }
+
+ trigger_events_[trigger_type].swap(tmp_trigger_times);
+ }
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/triggers/trigger_throttler.h b/chromium/components/safe_browsing/triggers/trigger_throttler.h
new file mode 100644
index 00000000000..33b588a2983
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/trigger_throttler.h
@@ -0,0 +1,58 @@
+// Copyright (c) 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_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
+#define COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
+
+#include <unordered_map>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+
+namespace safe_browsing {
+
+enum class TriggerType {
+ SECURITY_INTERSTITIAL = 1,
+};
+
+struct TriggerTypeHash {
+ std::size_t operator()(TriggerType trigger_type) const {
+ return static_cast<std::size_t>(trigger_type);
+ };
+};
+
+// A map for storing a list of event timestamps for different trigger types.
+using TriggerTimestampMap =
+ std::unordered_map<TriggerType, std::vector<time_t>, TriggerTypeHash>;
+
+// TriggerThrottler keeps track of how often each type of trigger gets fired
+// and throttles them if they fire too often.
+class TriggerThrottler {
+ public:
+ TriggerThrottler();
+ virtual ~TriggerThrottler();
+
+ // Check if the the specified |trigger_type| has quota available and is
+ // allowed to fire at this time.
+ bool TriggerCanFire(const TriggerType trigger_type) const;
+
+ // Called to notify the throttler that a trigger has just fired and quota
+ // should be updated.
+ void TriggerFired(const TriggerType trigger_type);
+
+ private:
+ // Called to periodically clean-up the list of event timestamps.
+ void CleanupOldEvents();
+
+ // Stores each trigger type that fired along with the timestamps of when it
+ // fired.
+ TriggerTimestampMap trigger_events_;
+
+ DISALLOW_COPY_AND_ASSIGN(TriggerThrottler);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
diff --git a/chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc b/chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc
new file mode 100644
index 00000000000..1b01e7295d4
--- /dev/null
+++ b/chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 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/safe_browsing/triggers/trigger_throttler.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+namespace {
+
+TEST(TriggerThrottler, SecurityInterstitialsHaveUnlimitedQuota) {
+ // Make sure that security interstitials never run out of quota.
+ TriggerThrottler throttler;
+ for (int i = 0; i < 1000; ++i) {
+ throttler.TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
+ EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
+ }
+}
+
+} // namespace
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/web_ui/BUILD.gn b/chromium/components/safe_browsing/web_ui/BUILD.gn
new file mode 100644
index 00000000000..d695729c39b
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/BUILD.gn
@@ -0,0 +1,31 @@
+# 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.
+
+static_library("web_ui") {
+ sources = [
+ "safe_browsing_ui.cc",
+ "safe_browsing_ui.h",
+ ]
+
+ deps = [
+ ":constants",
+ "//base",
+ "//components/resources:components_resources_grit",
+ "//components/resources:components_scaled_resources_grit",
+ "//components/safe_browsing:features",
+ "//components/safe_browsing/common:safe_browsing_prefs",
+ "//components/strings:components_strings_grit",
+ "//components/user_prefs:user_prefs",
+ "//content/public/browser",
+ "//net",
+ "//url",
+ ]
+}
+
+static_library("constants") {
+ sources = [
+ "constants.cc",
+ "constants.h",
+ ]
+}
diff --git a/chromium/components/safe_browsing/web_ui/DEPS b/chromium/components/safe_browsing/web_ui/DEPS
new file mode 100644
index 00000000000..09eb5158069
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+components/grit/components_resources.h",
+ "+components/user_prefs",
+ "+components/strings/grit/components_strings.h",
+ "+components/grit/components_scaled_resources.h"
+]
diff --git a/chromium/components/safe_browsing/web_ui/constants.cc b/chromium/components/safe_browsing/web_ui/constants.cc
new file mode 100644
index 00000000000..4088770f5c5
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/constants.cc
@@ -0,0 +1,20 @@
+// 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/safe_browsing/web_ui/constants.h"
+
+namespace safe_browsing {
+
+const char kChromeUISafeBrowsingURL[] = "chrome://safe-browsing/";
+const char kChromeUISafeBrowsingHost[] = "safe-browsing";
+const char kSbUnderConstruction[] =
+ "The safe browsing page is under construction.";
+extern const char kChromeUISafeBrowsingMatchMalwareUrl[] =
+ "chrome://safe-browsing/match?type=malware";
+extern const char kChromeUISafeBrowsingMatchPhishingUrl[] =
+ "chrome://safe-browsing/match?type=phishing";
+extern const char kChromeUISafeBrowsingMatchUnwantedUrl[] =
+ "chrome://safe-browsing/match?type=unwanted";
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/web_ui/constants.h b/chromium/components/safe_browsing/web_ui/constants.h
new file mode 100644
index 00000000000..fc1cf9bebeb
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/constants.h
@@ -0,0 +1,19 @@
+// 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_SAFE_BROWSING_WEB_UI_CONSTANTS_H_
+#define COMPONENTS_SAFE_BROWSING_WEB_UI_CONSTANTS_H_
+
+namespace safe_browsing {
+
+extern const char kChromeUISafeBrowsingURL[];
+extern const char kChromeUISafeBrowsingHost[];
+extern const char kSbUnderConstruction[];
+extern const char kChromeUISafeBrowsingMatchMalwareUrl[];
+extern const char kChromeUISafeBrowsingMatchPhishingUrl[];
+extern const char kChromeUISafeBrowsingMatchUnwantedUrl[];
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_WEB_UI_CONSTANTS_H_
diff --git a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.css b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.css
new file mode 100644
index 00000000000..6f8f80b504d
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.css
@@ -0,0 +1,26 @@
+/* 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. */
+
+body {
+ color: rgb(48, 57, 66);
+ margin:15px;
+}
+p {
+ white-space: pre-wrap;
+}
+.sbPageContent {
+ background-color: #fbfbfb;
+ border: 1px solid #cecece;
+ border-radius: 3px;
+ padding: 19px;
+ line-height: 1.5;
+}
+#sbTitle {
+ font-size: 2em;
+ margin-bottom: 0.8em;
+}
+h1, h2, h3, p {
+ font-weight: normal;
+ line-height: 1;
+}
diff --git a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.html b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.html
new file mode 100644
index 00000000000..bf5863f70e4
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Safe Browsing</title>
+ <link rel="stylesheet" href="safe_browsing.css">
+ <link rel="stylesheet" href="chrome://resources/css/text_defaults.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>
+ </head>
+ <body>
+ <div id="header">
+ <h1 id="sbTitle">Safe Browsing</h1>
+ </div>
+ <p>$i18n{sbUnderConstruction}</p>
+ <h2>Experiments</h2>
+ <div class="sbPageContent">
+ <p id="experimentsList"></p>
+ </div>
+ <h2>Preferences</h2>
+ <div class="sbPageContent">
+ <p id="preferencesList"></p>
+ </div>
+ <script src="chrome://resources/js/i18n_template.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
new file mode 100644
index 00000000000..f7ed029e3c9
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.js
@@ -0,0 +1,48 @@
+/* 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. */
+
+cr.define('safe_browsing', function() {
+ 'use strict';
+ /**
+ * Asks the C++ SafeBrowsingUIHandler to get the lists of Safe Browsing
+ * ongoing experiments and preferences.
+ * The SafeBrowsingUIHandler should reply to addExperiment() and
+ * addPreferences() (below).
+ */
+ function initialize() {
+ cr.sendWithPromise('getExperiments', []).then((experiments) =>
+ addExperiments(experiments));
+ cr.sendWithPromise('getPrefs', []).then(
+ prefs=>addPrefs(prefs));
+ }
+
+ function addExperiments(result) {
+ var resLength = result.length;
+ var experimentsListFormatted = "";
+
+ for (var i = 0; i < resLength; i += 2) {
+ experimentsListFormatted += "<div><b>" + result[i + 1] +
+ "</b>: " + result[i] + "</div>";
+ }
+
+ $('experimentsList').innerHTML = experimentsListFormatted;
+ }
+
+ function addPrefs(result) {
+ var resLength = result.length;
+ var preferencesListFormatted = "";
+
+ for (var i = 0; i < resLength; i += 2) {
+ preferencesListFormatted += "<div><b>" + result[i + 1] + "</b>: " +
+ result[i] + "</div>";
+ }
+ $('preferencesList').innerHTML = preferencesListFormatted;
+ }
+
+ return {
+ initialize: initialize,
+ };
+});
+
+document.addEventListener('DOMContentLoaded', safe_browsing.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
new file mode 100644
index 00000000000..dcc522f6ecd
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/safe_browsing_ui.cc
@@ -0,0 +1,77 @@
+// 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/safe_browsing/web_ui/safe_browsing_ui.h"
+
+#include "base/values.h"
+#include "components/grit/components_resources.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/web_ui/constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+
+namespace safe_browsing {
+
+SafeBrowsingUI::SafeBrowsingUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Set up the chrome://safe-browsing source.
+
+ content::WebUIDataSource* html_source = content::WebUIDataSource::Create(
+ safe_browsing::kChromeUISafeBrowsingHost);
+
+ content::BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+
+ // Register callback handler.
+ // Handles messages from JavaScript to C++ via chrome.send().
+ web_ui->AddMessageHandler(
+ base::MakeUnique<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);
+ html_source->SetDefaultResource(IDR_SAFE_BROWSING_HTML);
+
+ content::WebUIDataSource::Add(browser_context, html_source);
+}
+
+SafeBrowsingUI::~SafeBrowsingUI() {}
+
+SafeBrowsingUIHandler::SafeBrowsingUIHandler(content::BrowserContext* context)
+ : browser_context_(context) {}
+
+void SafeBrowsingUIHandler::GetExperiments(const base::ListValue* args) {
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), GetFeatureStatusList());
+}
+
+void SafeBrowsingUIHandler::GetPrefs(const base::ListValue* args) {
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id),
+ safe_browsing::GetSafeBrowsingPreferencesList(
+ user_prefs::UserPrefs::Get(browser_context_)));
+}
+SafeBrowsingUIHandler::~SafeBrowsingUIHandler() {}
+
+void SafeBrowsingUIHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getExperiments", base::Bind(&SafeBrowsingUIHandler::GetExperiments,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPrefs",
+ base::Bind(&SafeBrowsingUIHandler::GetPrefs, base::Unretained(this)));
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/web_ui/safe_browsing_ui.h b/chromium/components/safe_browsing/web_ui/safe_browsing_ui.h
new file mode 100644
index 00000000000..632b82dc1cd
--- /dev/null
+++ b/chromium/components/safe_browsing/web_ui/safe_browsing_ui.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_WEBUI_SAFE_BROWSING_UI_H_
+#define COMPONENTS_SAFE_BROWSING_WEBUI_SAFE_BROWSING_UI_H_
+
+#include "base/macros.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"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace safe_browsing {
+class SafeBrowsingUIHandler : public content::WebUIMessageHandler {
+ public:
+ SafeBrowsingUIHandler(content::BrowserContext*);
+ ~SafeBrowsingUIHandler() override;
+ void GetExperiments(const base::ListValue* args);
+ void GetPrefs(const base::ListValue* args);
+ void RegisterMessages() override;
+
+ private:
+ content::BrowserContext* browser_context_;
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIHandler);
+};
+
+// The WebUI for chrome://safe-browsing
+class SafeBrowsingUI : public content::WebUIController {
+ public:
+ explicit SafeBrowsingUI(content::WebUI* web_ui);
+ ~SafeBrowsingUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUI);
+};
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_WEBUI_SAFE_BROWSING_UI_H_
diff --git a/chromium/components/safe_browsing_db/BUILD.gn b/chromium/components/safe_browsing_db/BUILD.gn
index fb9286f8e4c..c3f376dcbeb 100644
--- a/chromium/components/safe_browsing_db/BUILD.gn
+++ b/chromium/components/safe_browsing_db/BUILD.gn
@@ -33,6 +33,7 @@ group("safe_browsing_db_shared") {
":prefix_set",
":safebrowsing_proto",
":util",
+ ":v4_feature_list", # Used by SafeBrowsingService
"//components/safe_browsing/common:safe_browsing_prefs",
]
}
@@ -71,6 +72,10 @@ static_library("database_manager") {
"//net",
"//url",
]
+
+ public_deps = [
+ ":safebrowsing_proto",
+ ]
}
static_library("hit_report") {
@@ -108,6 +113,7 @@ static_library("remote_database_manager") {
":database_manager",
":safe_browsing_api_handler",
":v4_get_hash_protocol_manager",
+ ":v4_protocol_manager_util",
"//base:base",
"//components/variations",
"//content/public/browser",
@@ -159,6 +165,7 @@ static_library("test_database_manager") {
]
deps = [
":database_manager",
+ ":v4_protocol_manager_util",
"//base:base",
"//net",
]
@@ -210,6 +217,7 @@ static_library("v4_feature_list") {
]
deps = [
"//base",
+ "//components/safe_browsing:features",
]
}
@@ -268,6 +276,15 @@ source_set("v4_protocol_manager_util") {
]
}
+if (is_android) {
+ import("//build/config/android/rules.gni")
+ java_cpp_enum("sb_threat_values") {
+ sources = [
+ "v4_protocol_manager_util.h",
+ ]
+ }
+}
+
source_set("v4_rice") {
sources = [
"v4_rice.cc",
@@ -369,6 +386,7 @@ source_set("v4_local_database_manager_unittest") {
deps = [
":v4_database",
":v4_local_database_manager",
+ ":v4_protocol_manager_util",
":v4_test_util",
"//base",
"//base/test:test_support",
@@ -397,19 +415,15 @@ source_set("v4_update_protocol_manager_unittest") {
]
}
-source_set("unit_tests") {
+source_set("unit_tests_shared") {
testonly = true
sources = [
"database_manager_unittest.cc",
"prefix_set_unittest.cc",
"util_unittest.cc",
- "v4_database_unittest.cc",
"v4_get_hash_protocol_manager_unittest.cc",
- "v4_local_database_manager_unittest.cc",
"v4_protocol_manager_util_unittest.cc",
- "v4_rice_unittest.cc",
- "v4_store_unittest.cc",
- "v4_update_protocol_manager_unittest.cc",
+ "whitelist_checker_client_unittest.cc",
]
deps = [
":database_manager",
@@ -417,8 +431,36 @@ source_set("unit_tests") {
":safebrowsing_proto",
":test_database_manager",
":util",
- ":v4_database",
":v4_get_hash_protocol_manager",
+ ":v4_protocol_manager_util",
+ ":v4_test_util",
+ ":whitelist_checker_client",
+ "//base",
+ "//content/public/browser",
+ "//content/test:test_support",
+ "//net",
+ "//net:test_support",
+ "//testing/gtest",
+ ]
+ if (is_win) {
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ cflags = [ "/wd4267" ] # Conversion from size_t to 'type'.
+ }
+}
+
+source_set("unit_tests_desktop") {
+ testonly = true
+ sources = [
+ "v4_database_unittest.cc",
+ "v4_local_database_manager_unittest.cc",
+ "v4_rice_unittest.cc",
+ "v4_store_unittest.cc",
+ "v4_update_protocol_manager_unittest.cc",
+ ]
+ deps = [
+ ":unit_tests_shared",
+ ":util",
+ ":v4_database",
":v4_local_database_manager",
":v4_protocol_manager_util",
":v4_rice",
@@ -453,10 +495,12 @@ source_set("unit_tests_mobile") {
":remote_database_manager",
":safe_browsing_api_handler",
":safe_browsing_api_handler_util",
+ ":unit_tests_shared",
":util",
":v4_test_util",
"//base",
"//components/variations",
+ "//content/test:test_support",
"//testing/gtest",
"//url",
]
@@ -465,3 +509,32 @@ source_set("unit_tests_mobile") {
cflags = [ "/wd4267" ] # Conversion from size_t to 'type'.
}
}
+
+static_library("whitelist_checker_client") {
+ sources = [
+ "whitelist_checker_client.cc",
+ "whitelist_checker_client.h",
+ ]
+ deps = [
+ ":database_manager",
+ "//base:base",
+ ]
+}
+
+source_set("whitelist_checker_client_unittest") {
+ testonly = true
+ sources = [
+ "whitelist_checker_client_unittest.cc",
+ ]
+ deps = [
+ ":database_manager",
+ ":test_database_manager",
+ ":whitelist_checker_client",
+ "//base:base",
+ "//base/test:test_support",
+ "//content/public/browser",
+ "//content/test:test_support",
+ "//testing/gmock:gmock",
+ "//testing/gtest:gtest",
+ ]
+}
diff --git a/chromium/components/safe_browsing_db/DEPS b/chromium/components/safe_browsing_db/DEPS
index 55dc17a90ee..341bbf0b8ca 100644
--- a/chromium/components/safe_browsing_db/DEPS
+++ b/chromium/components/safe_browsing_db/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"+components/data_use_measurement/core",
"+components/safe_browsing/common/safe_browsing_prefs.h",
+ "+components/safe_browsing/features.h",
"+components/variations",
"+components/version_info",
"+content/public/browser",
diff --git a/chromium/components/safe_browsing_db/OWNERS b/chromium/components/safe_browsing_db/OWNERS
index 2f08295890d..50851251692 100644
--- a/chromium/components/safe_browsing_db/OWNERS
+++ b/chromium/components/safe_browsing_db/OWNERS
@@ -1,6 +1,3 @@
-mattm@chromium.org
+jialiul@chromium.org
nparker@chromium.org
vakh@chromium.org
-
-# For reporting-related changes:
-jialiul@chromium.org
diff --git a/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.cc b/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.cc
index 8040c2caa50..3acd9a4153a 100644
--- a/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.cc
+++ b/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.cc
@@ -10,8 +10,10 @@
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
+#include "base/containers/flat_set.h"
#include "base/metrics/histogram_macros.h"
#include "components/safe_browsing_db/safe_browsing_api_handler_util.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "content/public/browser/browser_thread.h"
#include "jni/SafeBrowsingApiBridge_jni.h"
@@ -48,6 +50,8 @@ int SBThreatTypeToJavaThreatType(const SBThreatType& sb_threat_type) {
return safe_browsing::JAVA_THREAT_TYPE_POTENTIALLY_HARMFUL_APPLICATION;
case SB_THREAT_TYPE_URL_UNWANTED:
return safe_browsing::JAVA_THREAT_TYPE_UNWANTED_SOFTWARE;
+ case SB_THREAT_TYPE_SUBRESOURCE_FILTER:
+ return safe_browsing::JAVA_THREAT_TYPE_SUBRESOURCE_FILTER;
default:
NOTREACHED();
return 0;
@@ -55,9 +59,9 @@ int SBThreatTypeToJavaThreatType(const SBThreatType& sb_threat_type) {
}
// Convert a vector of SBThreatTypes to JavaIntArray of Java threat types.
-ScopedJavaLocalRef<jintArray> SBThreatTypesToJavaArray(
+ScopedJavaLocalRef<jintArray> SBThreatTypeSetToJavaArray(
JNIEnv* env,
- const std::vector<SBThreatType>& threat_types) {
+ const SBThreatTypeSet& threat_types) {
DCHECK(threat_types.size() > 0);
int int_threat_types[threat_types.size()];
int* itr = &int_threat_types[0];
@@ -149,7 +153,7 @@ bool SafeBrowsingApiHandlerBridge::CheckApiIsSupported() {
void SafeBrowsingApiHandlerBridge::StartURLCheck(
const SafeBrowsingApiHandler::URLCheckCallbackMeta& callback,
const GURL& url,
- const std::vector<SBThreatType>& threat_types) {
+ const SBThreatTypeSet& threat_types) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!CheckApiIsSupported()) {
@@ -166,14 +170,12 @@ void SafeBrowsingApiHandlerBridge::StartURLCheck(
DVLOG(1) << "Starting check " << callback_id << " for URL " << url;
- // Default threat types, to support upstream code that doesn't yet set them.
- std::vector<SBThreatType> local_threat_types(threat_types);
- DCHECK(!local_threat_types.empty());
+ DCHECK(!threat_types.empty());
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec());
ScopedJavaLocalRef<jintArray> j_threat_types =
- SBThreatTypesToJavaArray(env, local_threat_types);
+ SBThreatTypeSetToJavaArray(env, threat_types);
Java_SafeBrowsingApiBridge_startUriLookup(env, j_api_handler_, callback_id,
j_url, j_threat_types);
diff --git a/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.h b/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.h
index 678437affbe..40eda4d2149 100644
--- a/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.h
+++ b/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.h
@@ -15,6 +15,7 @@
#include "base/android/jni_android.h"
#include "base/macros.h"
#include "components/safe_browsing_db/safe_browsing_api_handler.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "url/gurl.h"
namespace safe_browsing {
@@ -28,7 +29,7 @@ class SafeBrowsingApiHandlerBridge : public SafeBrowsingApiHandler {
// Makes Native->Java call to check the URL against Safe Browsing lists.
void StartURLCheck(const URLCheckCallbackMeta& callback,
const GURL& url,
- const std::vector<SBThreatType>& threat_types) override;
+ const SBThreatTypeSet& threat_types) override;
private:
// Creates the j_api_handler_ if it hasn't been already. If the API is not
diff --git a/chromium/components/safe_browsing_db/database_manager.cc b/chromium/components/safe_browsing_db/database_manager.cc
index 0155efc9f8f..6633c417ced 100644
--- a/chromium/components/safe_browsing_db/database_manager.cc
+++ b/chromium/components/safe_browsing_db/database_manager.cc
@@ -15,7 +15,11 @@ using content::BrowserThread;
namespace safe_browsing {
-SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager() : enabled_(false) {}
+SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager()
+ : base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>(
+ content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::IO)),
+ enabled_(false) {}
SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
DCHECK(!v4_get_hash_protocol_manager_);
diff --git a/chromium/components/safe_browsing_db/database_manager.h b/chromium/components/safe_browsing_db/database_manager.h
index 01ae7caa105..c50ef687f99 100644
--- a/chromium/components/safe_browsing_db/database_manager.h
+++ b/chromium/components/safe_browsing_db/database_manager.h
@@ -15,9 +15,10 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "components/safe_browsing_db/hit_report.h"
#include "components/safe_browsing_db/util.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "content/public/common/resource_type.h"
#include "url/gurl.h"
@@ -27,12 +28,20 @@ class URLRequestContextGetter;
namespace safe_browsing {
+// Value returned by some Check*Whitelist() calls that may or may not have an
+// immediate answer.
+enum class AsyncMatch {
+ ASYNC, // No answer yet -- Client will get a callback
+ MATCH, // URL matches the list. No callback.
+ NO_MATCH, // URL doesn't match. No callback.
+};
+
struct V4ProtocolConfig;
class V4GetHashProtocolManager;
// Base class to either the locally-managed or a remotely-managed database.
class SafeBrowsingDatabaseManager
- : public base::RefCountedThreadSafe<SafeBrowsingDatabaseManager> {
+ : public base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager> {
public:
// Callers requesting a result should derive from this class.
// The destructor should call db_manager->CancelCheck(client) if a
@@ -64,6 +73,10 @@ class SafeBrowsingDatabaseManager
virtual void OnCheckResourceUrlResult(const GURL& url,
SBThreatType threat_type,
const std::string& threat_hash) {}
+
+ // Called when the result of checking a whitelist is known.
+ // Currently only used for CSD whitelist.
+ virtual void OnCheckWhitelistUrlResult(bool is_whitelisted) {}
};
//
@@ -88,6 +101,8 @@ 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;
@@ -112,20 +127,19 @@ class SafeBrowsingDatabaseManager
// and "client" is called asynchronously with the result when it is ready.
virtual bool CheckApiBlacklistUrl(const GURL& url, Client* client);
+ // Check if the |url| matches any of the full-length hashes from the client-
+ // side phishing detection whitelist. The 3-state return value indicates
+ // the result or that the Client will get a callback later with the result.
+ virtual AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) = 0;
+
// Called on the IO thread to check if the given url is safe or not. If we
// can synchronously determine that the url is safe, CheckUrl returns true.
// Otherwise it returns false, and "client" is called asynchronously with the
- // result when it is ready.
- virtual bool CheckBrowseUrl(const GURL& url, Client* client) = 0;
-
- // Called on the IO thread to check if the given url belongs to the
- // subresource filter list. If the url doesn't belong to the list, the check
- // happens synchronously, otherwise it returns false, and "client" is called
- // asynchronously with the result when it is ready.
- // Currently supported only on desktop. Returns TRUE if the list is not yet
- // available.
- virtual bool CheckUrlForSubresourceFilter(const GURL& url,
- Client* client) = 0;
+ // result when it is ready. The URL will only be checked for the threat types
+ // in |threat_types|.
+ virtual bool CheckBrowseUrl(const GURL& url,
+ const SBThreatTypeSet& threat_types,
+ Client* client) = 0;
// Check if the prefix for |url| is in safebrowsing download add lists.
// Result will be passed to callback in |client|.
@@ -143,15 +157,24 @@ class SafeBrowsingDatabaseManager
// to callback in |client|.
virtual bool CheckResourceUrl(const GURL& url, Client* client) = 0;
+ // Called on the IO thread to check if the given url belongs to a list the
+ // subresource cares about. If the url doesn't belong to any such list and the
+ // check can happen synchronously, returns true. Otherwise it returns false,
+ // and "client" is called asynchronously with the result when it is ready.
+ // Returns true if the list is not yet available.
+ virtual bool CheckUrlForSubresourceFilter(const GURL& url,
+ Client* client) = 0;
+
//
- // Methods to synchronously check whether a URL, or full hash, or IP address
- // or a DLL file is safe.
+ // Match*(): Methods to synchronously check if various types are safe.
//
// Check if the |url| matches any of the full-length hashes from the client-
// side phishing detection whitelist. Returns true if there was a match and
// false otherwise. To make sure we are conservative we will return true if
// an error occurs. This method must be called on the IO thread.
+ //
+ // DEPRECATED. ref: http://crbug.com/714300
virtual bool MatchCsdWhitelistUrl(const GURL& url) = 0;
// Check if |str| matches any of the full-length hashes from the download
@@ -241,7 +264,8 @@ class SafeBrowsingDatabaseManager
virtual ~SafeBrowsingDatabaseManager();
- friend class base::RefCountedThreadSafe<SafeBrowsingDatabaseManager>;
+ friend class base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>;
+ friend class base::DeleteHelper<SafeBrowsingDatabaseManager>;
FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
CheckApiBlacklistUrlPrefixes);
diff --git a/chromium/components/safe_browsing_db/database_manager_unittest.cc b/chromium/components/safe_browsing_db/database_manager_unittest.cc
index 5180687b6f3..5669978dcef 100644
--- a/chromium/components/safe_browsing_db/database_manager_unittest.cc
+++ b/chromium/components/safe_browsing_db/database_manager_unittest.cc
@@ -17,6 +17,7 @@
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/safe_browsing_db/test_database_manager.h"
#include "components/safe_browsing_db/v4_protocol_manager_util.h"
@@ -68,8 +69,9 @@ class SafeBrowsingDatabaseManagerTest : public testing::Test {
}
void TearDown() override {
- base::RunLoop().RunUntilIdle();
db_manager_->StopOnIOThread(false);
+ db_manager_ = nullptr;
+ base::RunLoop().RunUntilIdle();
}
std::string GetStockV4GetHashResponse() {
diff --git a/chromium/components/safe_browsing_db/hit_report.h b/chromium/components/safe_browsing_db/hit_report.h
index 4fecc44c9fa..9df6a900bf8 100644
--- a/chromium/components/safe_browsing_db/hit_report.h
+++ b/chromium/components/safe_browsing_db/hit_report.h
@@ -21,6 +21,7 @@ enum class ThreatSource {
LOCAL_PVER4, // From V4LocalDatabaseManager, protocol v4
REMOTE, // From RemoteSafeBrowsingDatabaseManager
CLIENT_SIDE_DETECTION, // From ClientSideDetectionHost
+ PASSWORD_PROTECTION_SERVICE, // From PasswordProtectionService
};
// Data to report about the contents of a particular threat (malware, phishing,
diff --git a/chromium/components/safe_browsing_db/remote_database_manager.cc b/chromium/components/safe_browsing_db/remote_database_manager.cc
index b620426b711..5762d41831c 100644
--- a/chromium/components/safe_browsing_db/remote_database_manager.cc
+++ b/chromium/components/safe_browsing_db/remote_database_manager.cc
@@ -13,6 +13,7 @@
#include "base/timer/elapsed_timer.h"
#include "components/safe_browsing_db/safe_browsing_api_handler.h"
#include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_thread.h"
@@ -172,18 +173,26 @@ 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.SchemeIs(url::kHttpsScheme) || url.SchemeIs(url::kHttpScheme) ||
- url.SchemeIs(url::kFtpScheme);
+ return url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kFtpScheme) ||
+ url.SchemeIsWSOrWSS();
}
bool RemoteSafeBrowsingDatabaseManager::ChecksAreAlwaysAsync() const {
return true;
}
-bool RemoteSafeBrowsingDatabaseManager::CheckBrowseUrl(const GURL& url,
- Client* client) {
+bool RemoteSafeBrowsingDatabaseManager::CheckBrowseUrl(
+ const GURL& url,
+ const SBThreatTypeSet& threat_types,
+ Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(!threat_types.empty());
+ DCHECK(SBThreatTypeSetIsValidForCheckBrowseUrl(threat_types));
if (!enabled_)
return true;
@@ -202,8 +211,7 @@ bool RemoteSafeBrowsingDatabaseManager::CheckBrowseUrl(const GURL& url,
DCHECK(api_handler) << "SafeBrowsingApiHandler was never constructed";
api_handler->StartURLCheck(
base::Bind(&ClientRequest::OnRequestDoneWeak, req->GetWeakPtr()), url,
- {SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_URL_PHISHING,
- SB_THREAT_TYPE_URL_UNWANTED});
+ threat_types);
LogPendingChecks(current_requests_.size());
current_requests_.push_back(req.release());
@@ -236,6 +244,8 @@ bool RemoteSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
const GURL& url,
Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(CanCheckSubresourceFilter());
+
if (!enabled_ || !CanCheckUrl(url))
return true;
@@ -249,7 +259,8 @@ bool RemoteSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
DCHECK(api_handler) << "SafeBrowsingApiHandler was never constructed";
api_handler->StartURLCheck(
base::Bind(&ClientRequest::OnRequestDoneWeak, req->GetWeakPtr()), url,
- {SB_THREAT_TYPE_SUBRESOURCE_FILTER});
+ CreateSBThreatTypeSet(
+ {SB_THREAT_TYPE_SUBRESOURCE_FILTER, SB_THREAT_TYPE_URL_PHISHING}));
LogPendingChecks(current_requests_.size());
current_requests_.push_back(req.release());
@@ -258,6 +269,13 @@ bool RemoteSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
return false;
}
+AsyncMatch RemoteSafeBrowsingDatabaseManager::CheckCsdWhitelistUrl(
+ const GURL& url,
+ Client* client) {
+ NOTREACHED();
+ return AsyncMatch::MATCH;
+}
+
bool RemoteSafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) {
NOTREACHED();
return true;
diff --git a/chromium/components/safe_browsing_db/remote_database_manager.h b/chromium/components/safe_browsing_db/remote_database_manager.h
index 1f13ca391fb..c2a30ff2016 100644
--- a/chromium/components/safe_browsing_db/remote_database_manager.h
+++ b/chromium/components/safe_browsing_db/remote_database_manager.h
@@ -41,13 +41,17 @@ 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, Client* client) override;
+ bool CheckBrowseUrl(const GURL& url,
+ const SBThreatTypeSet& threat_types,
+ Client* client) override;
bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
Client* client) override;
bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
Client* client) override;
+ AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) override;
bool CheckResourceUrl(const GURL& url, Client* client) override;
bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override;
bool MatchCsdWhitelistUrl(const GURL& url) override;
diff --git a/chromium/components/safe_browsing_db/remote_database_manager_unittest.cc b/chromium/components/safe_browsing_db/remote_database_manager_unittest.cc
index 284b3358c88..4a2b49a99d7 100644
--- a/chromium/components/safe_browsing_db/remote_database_manager_unittest.cc
+++ b/chromium/components/safe_browsing_db/remote_database_manager_unittest.cc
@@ -8,10 +8,12 @@
#include "base/logging.h"
#include "base/metrics/field_trial.h"
+#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "components/safe_browsing_db/safe_browsing_api_handler.h"
#include "components/variations/variations_associated_data.h"
+#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace safe_browsing {
@@ -22,7 +24,7 @@ class TestSafeBrowsingApiHandler : public SafeBrowsingApiHandler {
public:
void StartURLCheck(const URLCheckCallbackMeta& callback,
const GURL& url,
- const std::vector<SBThreatType>& threat_types) override {}
+ const SBThreatTypeSet& threat_types) override {}
};
} // namespace
@@ -37,6 +39,11 @@ class RemoteDatabaseManagerTest : public testing::Test {
db_ = new RemoteSafeBrowsingDatabaseManager();
}
+ void TearDown() override {
+ db_ = nullptr;
+ base::RunLoop().RunUntilIdle();
+ }
+
// Setup the two field trial params. These are read in db_'s ctor.
void SetFieldTrialParams(const std::string types_to_check_val) {
// Destroy the existing FieldTrialList before creating a new one to avoid
@@ -59,6 +66,7 @@ class RemoteDatabaseManagerTest : public testing::Test {
group_name, params));
}
+ content::TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<base::FieldTrialList> field_trials_;
TestSafeBrowsingApiHandler api_handler_;
scoped_refptr<RemoteSafeBrowsingDatabaseManager> db_;
diff --git a/chromium/components/safe_browsing_db/safe_browsing_api_handler.h b/chromium/components/safe_browsing_db/safe_browsing_api_handler.h
index fa01a6077af..f2e7abaf63c 100644
--- a/chromium/components/safe_browsing_db/safe_browsing_api_handler.h
+++ b/chromium/components/safe_browsing_db/safe_browsing_api_handler.h
@@ -13,6 +13,7 @@
#include "base/callback.h"
#include "components/safe_browsing_db/util.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "url/gurl.h"
namespace safe_browsing {
@@ -30,7 +31,7 @@ class SafeBrowsingApiHandler {
// Makes Native->Java call and invokes callback when check is done.
virtual void StartURLCheck(const URLCheckCallbackMeta& callback,
const GURL& url,
- const std::vector<SBThreatType>& threat_types) = 0;
+ const SBThreatTypeSet& threat_types) = 0;
virtual ~SafeBrowsingApiHandler() {}
diff --git a/chromium/components/safe_browsing_db/test_database_manager.cc b/chromium/components/safe_browsing_db/test_database_manager.cc
index 423097e1318..343ce155d65 100644
--- a/chromium/components/safe_browsing_db/test_database_manager.cc
+++ b/chromium/components/safe_browsing_db/test_database_manager.cc
@@ -23,6 +23,10 @@ bool TestSafeBrowsingDatabaseManager::CanCheckResourceType(
return false;
}
+bool TestSafeBrowsingDatabaseManager::CanCheckSubresourceFilter() const {
+ return false;
+}
+
bool TestSafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
NOTIMPLEMENTED();
return false;
@@ -33,8 +37,10 @@ bool TestSafeBrowsingDatabaseManager::ChecksAreAlwaysAsync() const {
return false;
}
-bool TestSafeBrowsingDatabaseManager::CheckBrowseUrl(const GURL& url,
- Client* client) {
+bool TestSafeBrowsingDatabaseManager::CheckBrowseUrl(
+ const GURL& url,
+ const SBThreatTypeSet& threat_types,
+ Client* client) {
NOTIMPLEMENTED();
return true;
}
@@ -66,6 +72,13 @@ bool TestSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
return true;
}
+AsyncMatch TestSafeBrowsingDatabaseManager::CheckCsdWhitelistUrl(
+ const GURL& url,
+ Client* client) {
+ NOTIMPLEMENTED();
+ return AsyncMatch::MATCH;
+}
+
bool TestSafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) {
NOTIMPLEMENTED();
return true;
diff --git a/chromium/components/safe_browsing_db/test_database_manager.h b/chromium/components/safe_browsing_db/test_database_manager.h
index c2cd803fa9a..51183720336 100644
--- a/chromium/components/safe_browsing_db/test_database_manager.h
+++ b/chromium/components/safe_browsing_db/test_database_manager.h
@@ -10,6 +10,7 @@
#include <vector>
#include "components/safe_browsing_db/database_manager.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
namespace safe_browsing {
@@ -24,8 +25,12 @@ class TestSafeBrowsingDatabaseManager
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, Client* client) override;
+ bool CheckBrowseUrl(const GURL& url,
+ const SBThreatTypeSet& threat_types,
+ Client* client) override;
+ AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) override;
bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
Client* client) override;
bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
@@ -47,7 +52,7 @@ class TestSafeBrowsingDatabaseManager
void StopOnIOThread(bool shutdown) override;
protected:
- ~TestSafeBrowsingDatabaseManager() override {};
+ ~TestSafeBrowsingDatabaseManager() override {}
};
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing_db/util.h b/chromium/components/safe_browsing_db/util.h
index dd03f5fe03f..ff47d837205 100644
--- a/chromium/components/safe_browsing_db/util.h
+++ b/chromium/components/safe_browsing_db/util.h
@@ -24,7 +24,7 @@ class GURL;
namespace safe_browsing {
// Metadata that indicates what kind of URL match this is.
-enum class ThreatPatternType {
+enum class ThreatPatternType : int {
NONE = 0, // Pattern type didn't appear in the metadata
MALWARE_LANDING = 1, // The match is a malware landing page
MALWARE_DISTRIBUTION = 2, // The match is a malware distribution page
diff --git a/chromium/components/safe_browsing_db/v4_database.cc b/chromium/components/safe_browsing_db/v4_database.cc
index 9164951b2fb..fc23c9b68d2 100644
--- a/chromium/components/safe_browsing_db/v4_database.cc
+++ b/chromium/components/safe_browsing_db/v4_database.cc
@@ -9,6 +9,7 @@
#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
+#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/safe_browsing_db/v4_database.h"
#include "content/public/browser/browser_thread.h"
@@ -30,6 +31,19 @@ base::LazyInstance<std::unique_ptr<V4DatabaseFactory>>::Leaky g_db_factory =
base::LazyInstance<std::unique_ptr<V4StoreFactory>>::Leaky g_store_factory =
LAZY_INSTANCE_INITIALIZER;
+// Verifies the checksums on a collection of stores.
+// Returns the IDs of stores whose checksums failed to verify.
+std::vector<ListIdentifier> VerifyChecksums(
+ std::vector<std::pair<ListIdentifier, V4Store*>> stores) {
+ std::vector<ListIdentifier> stores_to_reset;
+ for (const auto& store_map_iter : stores) {
+ if (!store_map_iter.second->VerifyChecksum()) {
+ stores_to_reset.push_back(store_map_iter.first);
+ }
+ }
+ return stores_to_reset;
+}
+
} // namespace
std::unique_ptr<V4Database> V4DatabaseFactory::Create(
@@ -65,7 +79,7 @@ void V4Database::CreateOnTaskRunner(
const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
NewDatabaseReadyCallback new_db_callback,
const TimeTicks create_start_time) {
- DCHECK(db_task_runner->RunsTasksOnCurrentThread());
+ DCHECK(db_task_runner->RunsTasksInCurrentSequence());
if (!g_store_factory.Get())
g_store_factory.Get() = base::MakeUnique<V4StoreFactory>();
@@ -119,7 +133,7 @@ V4Database::V4Database(
db_task_runner_(db_task_runner),
pending_store_updates_(0),
weak_factory_on_io_(this) {
- DCHECK(db_task_runner->RunsTasksOnCurrentThread());
+ DCHECK(db_task_runner->RunsTasksInCurrentSequence());
}
// static
@@ -133,7 +147,7 @@ void V4Database::Destroy(std::unique_ptr<V4Database> v4_database) {
}
V4Database::~V4Database() {
- DCHECK(db_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
}
void V4Database::ApplyUpdate(
@@ -254,28 +268,19 @@ void V4Database::ResetStores(
void V4Database::VerifyChecksum(
DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- // TODO(vakh): Consider using PostTaskAndReply instead.
- const scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner =
- base::ThreadTaskRunnerHandle::Get();
- db_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&V4Database::VerifyChecksumOnTaskRunner,
- weak_factory_on_io_.GetWeakPtr(), callback_task_runner,
- db_ready_for_updates_callback));
-}
-void V4Database::VerifyChecksumOnTaskRunner(
- const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
- DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) {
- std::vector<ListIdentifier> stores_to_reset;
- for (const auto& store_map_iter : *store_map_) {
- if (!store_map_iter.second->VerifyChecksum()) {
- stores_to_reset.push_back(store_map_iter.first);
- }
+ // Make a threadsafe copy of store_map_ w/raw pointers that we can hand to
+ // the DB thread. The V4Stores ptrs are guaranteed to be valid because their
+ // deletion would be sequenced on the DB thread, after this posted task is
+ // serviced.
+ std::vector<std::pair<ListIdentifier, V4Store*>> stores;
+ for (const auto& next_store : *store_map_) {
+ stores.push_back(std::make_pair(next_store.first, next_store.second.get()));
}
- callback_task_runner->PostTask(
- FROM_HERE, base::Bind(db_ready_for_updates_callback, stores_to_reset));
+ base::PostTaskAndReplyWithResult(db_task_runner_.get(), FROM_HERE,
+ base::Bind(&VerifyChecksums, stores),
+ db_ready_for_updates_callback);
}
bool V4Database::IsStoreAvailable(const ListIdentifier& identifier) const {
diff --git a/chromium/components/safe_browsing_db/v4_database.h b/chromium/components/safe_browsing_db/v4_database.h
index fd8b3af8148..0146830f08c 100644
--- a/chromium/components/safe_browsing_db/v4_database.h
+++ b/chromium/components/safe_browsing_db/v4_database.h
@@ -167,7 +167,11 @@ class V4Database {
V4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
std::unique_ptr<StoreMap> store_map);
- // Map of ListIdentifier to the V4Store.
+ // The collection of V4Stores, keyed by ListIdentifier.
+ // The map itself lives on the V4Database's parent thread, but its V4Store
+ // objects live on the db_task_runner_thread.
+ // TODO(vakh): Consider writing a container object which encapsulates or
+ // harmonizes thread affinity for the associative container and the data.
const std::unique_ptr<StoreMap> store_map_;
private:
diff --git a/chromium/components/safe_browsing_db/v4_database_unittest.cc b/chromium/components/safe_browsing_db/v4_database_unittest.cc
index 66db6fa5b95..de42e1a3bf9 100644
--- a/chromium/components/safe_browsing_db/v4_database_unittest.cc
+++ b/chromium/components/safe_browsing_db/v4_database_unittest.cc
@@ -24,7 +24,7 @@ class FakeV4Store : public V4Store {
const bool hash_prefix_matches)
: V4Store(
task_runner,
- base::FilePath(store_path.value() + FILE_PATH_LITERAL(".fake"))),
+ base::FilePath(store_path.value() + FILE_PATH_LITERAL(".store"))),
hash_prefix_should_match_(hash_prefix_matches) {}
HashPrefix GetMatchingHashPrefix(const FullHash& full_hash) override {
@@ -103,13 +103,13 @@ class V4DatabaseTest : public PlatformTest {
SB_THREAT_TYPE_URL_MALWARE);
expected_identifiers_.push_back(win_malware_id_);
expected_store_paths_.push_back(
- database_dirname_.AppendASCII("win_url_malware.fake"));
+ database_dirname_.AppendASCII("win_url_malware.store"));
list_infos_.emplace_back(true, "linux_url_malware", linux_malware_id_,
SB_THREAT_TYPE_URL_MALWARE);
expected_identifiers_.push_back(linux_malware_id_);
expected_store_paths_.push_back(
- database_dirname_.AppendASCII("linux_url_malware.fake"));
+ database_dirname_.AppendASCII("linux_url_malware.store"));
}
void DatabaseUpdated() {}
diff --git a/chromium/components/safe_browsing_db/v4_feature_list.cc b/chromium/components/safe_browsing_db/v4_feature_list.cc
index 9d166e9f15b..8185e6fc16d 100644
--- a/chromium/components/safe_browsing_db/v4_feature_list.cc
+++ b/chromium/components/safe_browsing_db/v4_feature_list.cc
@@ -2,22 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/feature_list.h"
#include "components/safe_browsing_db/v4_feature_list.h"
+#include "base/feature_list.h"
+#include "components/safe_browsing/features.h"
+
namespace safe_browsing {
namespace V4FeatureList {
-namespace {
-
-const base::Feature kLocalDatabaseManagerEnabled{
- "SafeBrowsingV4LocalDatabaseManagerEnabled",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kV4OnlyEnabled{"SafeBrowsingV4OnlyEnabled",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
bool IsV4OnlyEnabled() {
return base::FeatureList::IsEnabled(kV4OnlyEnabled);
}
@@ -27,8 +20,6 @@ bool IsLocalDatabaseManagerEnabled() {
IsV4OnlyEnabled();
}
-} // namespace
-
V4UsageStatus GetV4UsageStatus() {
V4UsageStatus v4_usage_status;
if (safe_browsing::V4FeatureList::IsV4OnlyEnabled()) {
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 77488c2c422..9cfd8ffa13e 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
@@ -255,10 +255,12 @@ V4GetHashProtocolManager::V4GetHashProtocolManager(
threat_types_.assign(threat_types.begin(), threat_types.end());
}
-V4GetHashProtocolManager::~V4GetHashProtocolManager() {}
+V4GetHashProtocolManager::~V4GetHashProtocolManager() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
void V4GetHashProtocolManager::ClearCache() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
full_hash_cache_.clear();
}
@@ -267,7 +269,7 @@ void V4GetHashProtocolManager::GetFullHashes(
full_hash_to_store_and_hash_prefixes,
FullHashCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!full_hash_to_store_and_hash_prefixes.empty());
std::vector<HashPrefix> prefixes_to_request;
@@ -506,7 +508,7 @@ void V4GetHashProtocolManager::GetHashUrlAndHeaders(
}
void V4GetHashProtocolManager::HandleGetHashError(const Time& now) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval(
&gethash_error_count_, &gethash_back_off_mult_);
next_gethash_time_ = now + next;
@@ -738,7 +740,7 @@ void V4GetHashProtocolManager::MergeResults(
// SafeBrowsing request responses are handled here.
void V4GetHashProtocolManager::OnURLFetchComplete(
const net::URLFetcher* source) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_CURRENTLY_ON(BrowserThread::IO);
PendingHashRequests::iterator it = pending_hash_requests_.find(source);
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 7d9c2c2aff1..6e43e8897c9 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
@@ -20,7 +20,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
@@ -139,8 +139,7 @@ struct FullHashCallbackInfo {
class V4GetHashProtocolManagerFactory;
-class V4GetHashProtocolManager : public net::URLFetcherDelegate,
- public base::NonThreadSafe {
+class V4GetHashProtocolManager : public net::URLFetcherDelegate {
public:
// Invoked when GetFullHashesWithApis completes.
// Parameters:
@@ -341,6 +340,8 @@ class V4GetHashProtocolManager : public net::URLFetcherDelegate,
std::vector<ThreatEntryType> threat_entry_types_;
std::vector<ThreatType> threat_types_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManager);
};
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 02617ce96cd..326ed5b0fdb 100644
--- a/chromium/components/safe_browsing_db/v4_local_database_manager.cc
+++ b/chromium/components/safe_browsing_db/v4_local_database_manager.cc
@@ -11,10 +11,12 @@
#include "base/bind_helpers.h"
#include "base/callback.h"
+#include "base/files/file_util.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/post_task.h"
#include "components/safe_browsing_db/v4_feature_list.h"
#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "content/public/browser/browser_thread.h"
@@ -30,11 +32,14 @@ namespace {
const ThreatSeverity kLeastSeverity =
std::numeric_limits<ThreatSeverity>::max();
+// The list of the name of any store files that are no longer used and can be
+// safely deleted from the disk. There's no overlap allowed between the files
+// on this list and the list returned by GetListInfos().
+const char* const kStoreFileNamesToDelete[] = {"AnyIpMalware.store"};
+
ListInfos GetListInfos() {
// NOTE(vakh): When adding a store here, add the corresponding store-specific
// histograms also.
- // NOTE(vakh): Delete file "AnyIpMalware.store". It has been renamed to
- // "IpMalware.store". If it exists, it should be 75 bytes long.
// The first argument to ListInfo specifies whether to sync hash prefixes for
// that list. This can be false for two reasons:
// - The server doesn't support that list yet. Once the server adds support
@@ -59,7 +64,7 @@ ListInfos GetListInfos() {
ListInfo(kSyncOnlyOnChromeBuilds, "UrlCsdDownloadWhitelist.store",
GetUrlCsdDownloadWhitelistId(), SB_THREAT_TYPE_UNUSED),
ListInfo(kSyncOnlyOnChromeBuilds, "UrlCsdWhitelist.store",
- GetUrlCsdWhitelistId(), SB_THREAT_TYPE_UNUSED),
+ GetUrlCsdWhitelistId(), SB_THREAT_TYPE_CSD_WHITELIST),
ListInfo(kSyncAlways, "UrlSoceng.store", GetUrlSocEngId(),
SB_THREAT_TYPE_URL_PHISHING),
ListInfo(kSyncAlways, "UrlMalware.store", GetUrlMalwareId(),
@@ -67,7 +72,7 @@ ListInfos GetListInfos() {
ListInfo(kSyncAlways, "UrlUws.store", GetUrlUwsId(),
SB_THREAT_TYPE_URL_UNWANTED),
ListInfo(kSyncAlways, "UrlMalBin.store", GetUrlMalBinId(),
- SB_THREAT_TYPE_BINARY_MALWARE_URL),
+ SB_THREAT_TYPE_URL_BINARY_MALWARE),
ListInfo(kSyncAlways, "ChromeExtMalware.store", GetChromeExtMalwareId(),
SB_THREAT_TYPE_EXTENSION),
ListInfo(kSyncOnlyOnChromeBuilds, "ChromeUrlClientIncident.store",
@@ -96,6 +101,8 @@ ThreatSeverity GetThreatSeverity(const ListIdentifier& list_id) {
case CLIENT_INCIDENT:
case SUBRESOURCE_FILTER:
return 2;
+ case CSD_WHITELIST:
+ return 3;
default:
NOTREACHED() << "Unexpected ThreatType encountered: "
<< list_id.threat_type();
@@ -103,6 +110,34 @@ ThreatSeverity GetThreatSeverity(const ListIdentifier& list_id) {
}
}
+// This is only valid for types that are passed to GetBrowseUrl().
+ListIdentifier GetUrlIdFromSBThreatType(SBThreatType sb_threat_type) {
+ switch (sb_threat_type) {
+ case SB_THREAT_TYPE_URL_MALWARE:
+ return GetUrlMalwareId();
+
+ case SB_THREAT_TYPE_URL_PHISHING:
+ return GetUrlSocEngId();
+
+ case SB_THREAT_TYPE_URL_UNWANTED:
+ return GetUrlUwsId();
+
+ default:
+ NOTREACHED();
+ // Compiler requires a return statement here.
+ return GetUrlMalwareId();
+ }
+}
+
+StoresToCheck CreateStoresToCheckFromSBThreatTypeSet(
+ const SBThreatTypeSet& threat_types) {
+ StoresToCheck stores_to_check;
+ for (SBThreatType sb_threat_type : threat_types) {
+ stores_to_check.insert(GetUrlIdFromSBThreatType(sb_threat_type));
+ }
+ return stores_to_check;
+}
+
} // namespace
V4LocalDatabaseManager::PendingCheck::PendingCheck(
@@ -118,7 +153,6 @@ V4LocalDatabaseManager::PendingCheck::PendingCheck(
for (const auto& url : urls) {
V4ProtocolManagerUtil::UrlToFullHashes(url, &full_hashes);
}
- DCHECK(full_hashes.size());
full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE);
}
@@ -152,10 +186,14 @@ V4LocalDatabaseManager::V4LocalDatabaseManager(
: base_path_(base_path),
extended_reporting_level_callback_(extended_reporting_level_callback),
list_infos_(GetListInfos()),
+ task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
weak_factory_(this) {
DCHECK(!base_path_.empty());
DCHECK(!list_infos_.empty());
+ DeleteUnusedStoreFiles();
+
DVLOG(1) << "V4LocalDatabaseManager::V4LocalDatabaseManager: "
<< "base_path_: " << base_path_.AsUTF8Unsafe();
}
@@ -195,17 +233,25 @@ bool V4LocalDatabaseManager::CanCheckResourceType(
return true;
}
+bool V4LocalDatabaseManager::CanCheckSubresourceFilter() const {
+ return true;
+}
+
bool V4LocalDatabaseManager::CanCheckUrl(const GURL& url) const {
- return url.SchemeIs(url::kHttpsScheme) || url.SchemeIs(url::kHttpScheme) ||
- url.SchemeIs(url::kFtpScheme);
+ return url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kFtpScheme) ||
+ url.SchemeIsWSOrWSS();
}
bool V4LocalDatabaseManager::ChecksAreAlwaysAsync() const {
return false;
}
-bool V4LocalDatabaseManager::CheckBrowseUrl(const GURL& url, Client* client) {
+bool V4LocalDatabaseManager::CheckBrowseUrl(const GURL& url,
+ const SBThreatTypeSet& threat_types,
+ Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(!threat_types.empty());
+ DCHECK(SBThreatTypeSetIsValidForCheckBrowseUrl(threat_types));
if (!enabled_ || !CanCheckUrl(url)) {
return true;
@@ -213,7 +259,7 @@ bool V4LocalDatabaseManager::CheckBrowseUrl(const GURL& url, Client* client) {
std::unique_ptr<PendingCheck> check = base::MakeUnique<PendingCheck>(
client, ClientCallbackType::CHECK_BROWSE_URL,
- StoresToCheck({GetUrlMalwareId(), GetUrlSocEngId(), GetUrlUwsId()}),
+ CreateStoresToCheckFromSBThreatTypeSet(threat_types),
std::vector<GURL>(1, url));
return HandleCheck(std::move(check));
@@ -275,6 +321,7 @@ 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()});
@@ -289,15 +336,34 @@ bool V4LocalDatabaseManager::CheckUrlForSubresourceFilter(const GURL& url,
return HandleCheck(std::move(check));
}
-bool V4LocalDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) {
+AsyncMatch V4LocalDatabaseManager::CheckCsdWhitelistUrl(const GURL& url,
+ Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
StoresToCheck stores_to_check({GetUrlCsdWhitelistId()});
- if (!AreAllStoresAvailableNow(stores_to_check)) {
+ if (!AreAllStoresAvailableNow(stores_to_check) || !CanCheckUrl(url)) {
// Fail open: Whitelist everything. Otherwise we may run the
// CSD phishing/malware detector on popular domains and generate
- // undue load on the client and server. This has the effect of disabling
- // CSD phishing/malware detection until the store is first synced.
+ // undue load on the client and server, or send Password Reputation
+ // requests on popular sites. This has the effect of disabling
+ // CSD phishing/malware detection and password reputation service
+ // until the store is first synced and/or loaded from disk.
+ return AsyncMatch::MATCH;
+ }
+
+ std::unique_ptr<PendingCheck> check = base::MakeUnique<PendingCheck>(
+ client, ClientCallbackType::CHECK_CSD_WHITELIST, stores_to_check,
+ std::vector<GURL>(1, url));
+
+ return HandleWhitelistCheck(std::move(check));
+}
+
+bool V4LocalDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ StoresToCheck stores_to_check({GetUrlCsdWhitelistId()});
+ if (!AreAllStoresAvailableNow(stores_to_check)) {
+ // Fail open: Whitelist everything. See CheckCsdWhitelistUrl.
return true;
}
@@ -473,6 +539,33 @@ void V4LocalDatabaseManager::DatabaseUpdated() {
}
}
+void V4LocalDatabaseManager::DeleteUnusedStoreFiles() {
+ for (auto* const store_filename_to_delete : kStoreFileNamesToDelete) {
+ // Is the file marked for deletion also being used for a valid V4Store?
+ auto it = std::find_if(std::begin(list_infos_), std::end(list_infos_),
+ [&store_filename_to_delete](ListInfo const& li) {
+ return li.filename() == store_filename_to_delete;
+ });
+ if (list_infos_.end() == it) {
+ const base::FilePath store_path =
+ base_path_.AppendASCII(store_filename_to_delete);
+ bool path_exists = base::PathExists(store_path);
+ base::UmaHistogramBoolean("SafeBrowsing.V4UnusedStoreFileExists" +
+ GetUmaSuffixForStore(store_path),
+ path_exists);
+ if (!path_exists) {
+ continue;
+ }
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(base::IgnoreResult(&base::DeleteFile),
+ store_path, false /* recursive */));
+ } else {
+ NOTREACHED() << "Trying to delete a store file that's in use: "
+ << store_filename_to_delete;
+ }
+ }
+}
+
bool V4LocalDatabaseManager::GetPrefixMatches(
const std::unique_ptr<PendingCheck>& check,
FullHashToStoreAndHashPrefixesMap* full_hash_to_store_and_hash_prefixes) {
@@ -482,7 +575,6 @@ bool V4LocalDatabaseManager::GetPrefixMatches(
DCHECK(v4_database_);
const base::TimeTicks before = TimeTicks::Now();
- DCHECK(!check->full_hashes.empty());
full_hash_to_store_and_hash_prefixes->clear();
for (const auto& full_hash : check->full_hashes) {
@@ -554,6 +646,34 @@ SBThreatType V4LocalDatabaseManager::GetSBThreatTypeForList(
return it->sb_threat_type();
}
+AsyncMatch V4LocalDatabaseManager::HandleWhitelistCheck(
+ std::unique_ptr<PendingCheck> check) {
+ // We don't bother queuing whitelist checks since the DB will
+ // normally be available already -- whitelists are used after page load,
+ // and navigations are blocked until the DB is ready and dequeues checks.
+ // The caller should have already checked that the DB is ready.
+ DCHECK(v4_database_);
+
+ FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
+ if (!GetPrefixMatches(check, &full_hash_to_store_and_hash_prefixes)) {
+ return AsyncMatch::NO_MATCH;
+ }
+
+ // Look for any full-length hash in the matches. If there is one,
+ // there's no need for a full-hash check. This saves bandwidth for
+ // very popular sites since they'll have full-length hashes locally.
+ // These loops will have exactly 1 entry most of the time.
+ for (const auto& entry : full_hash_to_store_and_hash_prefixes) {
+ for (const auto& store_and_prefix : entry.second) {
+ if (store_and_prefix.hash_prefix.size() == kMaxHashPrefixLength)
+ return AsyncMatch::MATCH;
+ }
+ }
+
+ ScheduleFullHashCheck(std::move(check), full_hash_to_store_and_hash_prefixes);
+ return AsyncMatch::ASYNC;
+}
+
bool V4LocalDatabaseManager::HandleCheck(std::unique_ptr<PendingCheck> check) {
if (!v4_database_) {
queued_checks_.push_back(std::move(check));
@@ -565,6 +685,14 @@ bool V4LocalDatabaseManager::HandleCheck(std::unique_ptr<PendingCheck> check) {
return true;
}
+ ScheduleFullHashCheck(std::move(check), full_hash_to_store_and_hash_prefixes);
+ return false;
+}
+
+void V4LocalDatabaseManager::ScheduleFullHashCheck(
+ std::unique_ptr<PendingCheck> check,
+ const FullHashToStoreAndHashPrefixesMap&
+ full_hash_to_store_and_hash_prefixes) {
// Add check to pending_checks_ before scheduling PerformFullHashCheck so that
// even if the client calls CancelCheck before PerformFullHashCheck gets
// called, the check can be found in pending_checks_.
@@ -576,8 +704,6 @@ bool V4LocalDatabaseManager::HandleCheck(std::unique_ptr<PendingCheck> check) {
base::Bind(&V4LocalDatabaseManager::PerformFullHashCheck,
weak_factory_.GetWeakPtr(), base::Passed(std::move(check)),
full_hash_to_store_and_hash_prefixes));
-
- return false;
}
bool V4LocalDatabaseManager::HandleHashSynchronously(
@@ -700,6 +826,16 @@ void V4LocalDatabaseManager::RespondToClient(
check->matching_full_hash);
break;
+ case ClientCallbackType::CHECK_CSD_WHITELIST: {
+ DCHECK_EQ(1u, check->urls.size());
+ bool did_match_whitelist =
+ check->most_severe_threat_type == SB_THREAT_TYPE_CSD_WHITELIST;
+ DCHECK(did_match_whitelist ||
+ check->most_severe_threat_type == SB_THREAT_TYPE_SAFE);
+ check->client->OnCheckWhitelistUrlResult(did_match_whitelist);
+ break;
+ }
+
case ClientCallbackType::CHECK_EXTENSION_IDS: {
DCHECK_EQ(check->full_hash_threat_types.size(),
check->full_hashes.size());
@@ -722,14 +858,6 @@ void V4LocalDatabaseManager::SetupDatabase() {
DCHECK(!list_infos_.empty());
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- // Only get a new task runner if there isn't one already. If the service has
- // previously been started and stopped, a task runner could already exist.
- if (!task_runner_) {
- base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
- task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
- pool->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
- }
-
// Do not create the database on the IO thread since this may be an expensive
// operation. Instead, do that on the task_runner and when the new database
// has been created, swap it out on the IO thread.
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 9e3ea4fa00f..c35296e0f18 100644
--- a/chromium/components/safe_browsing_db/v4_local_database_manager.h
+++ b/chromium/components/safe_browsing_db/v4_local_database_manager.h
@@ -40,9 +40,13 @@ 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, Client* client) override;
+ bool CheckBrowseUrl(const GURL& url,
+ const SBThreatTypeSet& threat_types,
+ Client* client) override;
+ AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) override;
bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
Client* client) override;
// TODO(vakh): |CheckExtensionIDs| in the base class accepts a set of
@@ -88,28 +92,32 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
enum class ClientCallbackType {
// This represents the case when we're trying to determine if a URL is
// unsafe from the following perspectives: Malware, Phishing, UwS.
- CHECK_BROWSE_URL = 0,
+ CHECK_BROWSE_URL,
// This represents the case when we're trying to determine if any of the
// URLs in a vector of URLs is unsafe for downloading binaries.
- CHECK_DOWNLOAD_URLS = 1,
+ CHECK_DOWNLOAD_URLS,
// This represents the case when we're trying to determine if a URL is an
// unsafe resource.
- CHECK_RESOURCE_URL = 2,
+ CHECK_RESOURCE_URL,
// This represents the case when we're trying to determine if a Chrome
// extension is a unsafe.
- CHECK_EXTENSION_IDS = 3,
+ CHECK_EXTENSION_IDS,
// This respresents the case when we're trying to determine if a URL belongs
// to the list where subresource filter should be active.
- CHECK_URL_FOR_SUBRESOURCE_FILTER = 4,
+ CHECK_URL_FOR_SUBRESOURCE_FILTER,
+
+ // This respresents the case when we're trying to determine if a URL is
+ // part of the CSD whitelist.
+ CHECK_CSD_WHITELIST,
// This represents the other cases when a check is being performed
// synchronously so a client callback isn't required. For instance, when
// trying to determing if an IP address is unsafe due to hosting Malware.
- CHECK_OTHER = 5,
+ CHECK_OTHER,
};
// The information we need to process a URL safety reputation request and
@@ -193,6 +201,9 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
// Called when the database has been updated and schedules the next update.
void DatabaseUpdated();
+ // Delete any *.store files from disk that are no longer used.
+ void DeleteUnusedStoreFiles();
+
// Identifies the prefixes and the store they matched in, for a given |check|.
// Returns true if one or more hash prefix matches are found; false otherwise.
bool GetPrefixMatches(
@@ -221,6 +232,15 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
// schedules a task to perform full hash check and returns false.
bool HandleCheck(std::unique_ptr<PendingCheck> check);
+ // Like HandleCheck, but for whitelists that have both full-hashes and
+ // partial hashes in the DB. Returns MATCH, NO_MATCH, or ASYNC.
+ AsyncMatch HandleWhitelistCheck(std::unique_ptr<PendingCheck> check);
+
+ // Schedules a full-hash check for a given set of prefixes.
+ void ScheduleFullHashCheck(std::unique_ptr<PendingCheck> check,
+ const FullHashToStoreAndHashPrefixesMap&
+ full_hash_to_store_and_hash_prefixes);
+
// Checks |stores_to_check| in database synchronously for hash prefixes
// matching |hash|. Returns true if there's a match; false otherwise. This is
// used for lists that have full hash information in the database.
@@ -314,7 +334,6 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
base::WeakPtrFactory<V4LocalDatabaseManager> weak_factory_;
- friend class base::RefCountedThreadSafe<V4LocalDatabaseManager>;
DISALLOW_COPY_AND_ASSIGN(V4LocalDatabaseManager);
}; // class V4LocalDatabaseManager
diff --git a/chromium/components/safe_browsing_db/v4_local_database_manager_unittest.cc b/chromium/components/safe_browsing_db/v4_local_database_manager_unittest.cc
index 34cb4ac5887..f40d3410470 100644
--- a/chromium/components/safe_browsing_db/v4_local_database_manager_unittest.cc
+++ b/chromium/components/safe_browsing_db/v4_local_database_manager_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "components/safe_browsing_db/v4_local_database_manager.h"
+#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
@@ -11,6 +12,7 @@
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/safe_browsing_db/v4_database.h"
+#include "components/safe_browsing_db/v4_protocol_manager_util.h"
#include "components/safe_browsing_db/v4_test_util.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "crypto/sha2.h"
@@ -31,7 +33,7 @@ FullHash HashForUrl(const GURL& url) {
return full_hashes[0];
}
-// Always returns misses from GetFullHashes().
+// Use this if you want GetFullHashes() to always return prescribed results.
class FakeGetHashProtocolManager : public V4GetHashProtocolManager {
public:
FakeGetHashProtocolManager(
@@ -74,6 +76,7 @@ class FakeGetHashProtocolManagerFactory
};
// Use FakeGetHashProtocolManagerFactory in scope, then reset.
+// You should make sure the DatabaseManager is created _after_ this.
class ScopedFakeGetHashProtocolManagerFactory {
public:
ScopedFakeGetHashProtocolManagerFactory(
@@ -113,6 +116,8 @@ class FakeV4Database : public V4Database {
StoreAndHashPrefixes* store_and_hash_prefixes) override {
store_and_hash_prefixes->clear();
for (const StoreAndHashPrefix& stored_sahp : store_and_hash_prefixes_) {
+ if (stores_to_check.count(stored_sahp.list_id) == 0)
+ continue;
const PrefixSize& prefix_size = stored_sahp.hash_prefix.size();
if (!full_hash.compare(0, prefix_size, stored_sahp.hash_prefix)) {
store_and_hash_prefixes->push_back(stored_sahp);
@@ -159,6 +164,8 @@ class FakeV4Database : public V4Database {
const bool stores_available_;
};
+// TODO(nparker): This might be simpler with a mock and EXPECT calls.
+// That would also catch unexpected calls.
class TestClient : public SafeBrowsingDatabaseManager::Client {
public:
TestClient(SBThreatType sb_threat_type,
@@ -166,17 +173,10 @@ class TestClient : public SafeBrowsingDatabaseManager::Client {
V4LocalDatabaseManager* manager_to_cancel = nullptr)
: expected_sb_threat_type(sb_threat_type),
expected_urls(1, url),
- on_check_browse_url_result_called_(false),
- on_check_download_urls_result_called_(false),
- on_check_resource_url_result_called_(false),
manager_to_cancel_(manager_to_cancel) {}
TestClient(SBThreatType sb_threat_type, const std::vector<GURL>& url_chain)
- : expected_sb_threat_type(sb_threat_type),
- expected_urls(url_chain),
- on_check_browse_url_result_called_(false),
- on_check_download_urls_result_called_(false),
- on_check_resource_url_result_called_(false) {}
+ : expected_sb_threat_type(sb_threat_type), expected_urls(url_chain) {}
void OnCheckBrowseUrlResult(const GURL& url,
SBThreatType threat_type,
@@ -197,6 +197,7 @@ class TestClient : public SafeBrowsingDatabaseManager::Client {
ASSERT_EQ(threat_type == SB_THREAT_TYPE_SAFE, threat_hash.empty());
on_check_resource_url_result_called_ = true;
}
+
void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
SBThreatType threat_type) override {
ASSERT_EQ(expected_urls, url_chain);
@@ -206,12 +207,26 @@ class TestClient : public SafeBrowsingDatabaseManager::Client {
SBThreatType expected_sb_threat_type;
std::vector<GURL> expected_urls;
- bool on_check_browse_url_result_called_;
- bool on_check_download_urls_result_called_;
- bool on_check_resource_url_result_called_;
+ bool on_check_browse_url_result_called_ = false;
+ bool on_check_download_urls_result_called_ = false;
+ bool on_check_resource_url_result_called_ = false;
V4LocalDatabaseManager* manager_to_cancel_;
};
+class TestWhitelistClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+ explicit TestWhitelistClient(bool whitelist_expected)
+ : whitelist_expected_(whitelist_expected) {}
+
+ void OnCheckWhitelistUrlResult(bool is_whitelisted) override {
+ EXPECT_EQ(whitelist_expected_, is_whitelisted);
+ callback_called_ = true;
+ }
+
+ const bool whitelist_expected_;
+ bool callback_called_ = false;
+};
+
class TestExtensionClient : public SafeBrowsingDatabaseManager::Client {
public:
TestExtensionClient(const std::set<FullHash>& expected_bad_crxs)
@@ -229,18 +244,19 @@ class TestExtensionClient : public SafeBrowsingDatabaseManager::Client {
class FakeV4LocalDatabaseManager : public V4LocalDatabaseManager {
public:
- void PerformFullHashCheck(std::unique_ptr<PendingCheck> check,
- const FullHashToStoreAndHashPrefixesMap&
- full_hash_to_store_and_hash_prefixes) override {
- perform_full_hash_check_called_ = true;
- }
-
FakeV4LocalDatabaseManager(
const base::FilePath& base_path,
ExtendedReportingLevelCallback extended_reporting_level_callback)
: V4LocalDatabaseManager(base_path, extended_reporting_level_callback),
perform_full_hash_check_called_(false) {}
+ // V4LocalDatabaseManager impl:
+ void PerformFullHashCheck(std::unique_ptr<PendingCheck> check,
+ const FullHashToStoreAndHashPrefixesMap&
+ full_hash_to_store_and_hash_prefixes) override {
+ perform_full_hash_check_called_ = true;
+ }
+
static bool PerformFullHashCheckCalled(
scoped_refptr<safe_browsing::V4LocalDatabaseManager>& v4_ldbm) {
FakeV4LocalDatabaseManager* fake =
@@ -369,6 +385,10 @@ class V4LocalDatabaseManagerTest : public PlatformTest {
WaitForTasksOnTaskRunner();
}
+ const SBThreatTypeSet usual_threat_types_ = CreateSBThreatTypeSet(
+ {SB_THREAT_TYPE_URL_PHISHING, SB_THREAT_TYPE_URL_MALWARE,
+ SB_THREAT_TYPE_URL_UNWANTED});
+
base::ScopedTempDir base_dir_;
ExtendedReportingLevel extended_reporting_level_;
ExtendedReportingLevelCallback erl_callback_;
@@ -405,7 +425,7 @@ TEST_F(V4LocalDatabaseManagerTest,
WaitForTasksOnTaskRunner();
// Both the stores are empty right now so CheckBrowseUrl should return true.
EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
- GURL("http://example.com/a/"), nullptr));
+ GURL("http://example.com/a/"), usual_threat_types_, nullptr));
}
TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithFakeDbReturnsMatch) {
@@ -420,10 +440,132 @@ TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithFakeDbReturnsMatch) {
ReplaceV4Database(store_and_hash_prefixes);
const GURL url_bad("https://" + url_bad_no_scheme);
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(url_bad, nullptr));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ url_bad, usual_threat_types_, nullptr));
+
+ // Wait for PerformFullHashCheck to complete.
+ WaitForTasksOnTaskRunner();
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithPrefixMatch) {
+ // Setup to receive full-hash misses. We won't make URL requests.
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+
+ std::string url_white_no_scheme("example.com/white/");
+ FullHash white_full_hash(crypto::SHA256HashString(url_white_no_scheme));
+ const HashPrefix white_hash_prefix(white_full_hash.substr(0, 5));
+ StoreAndHashPrefixes store_and_hash_prefixes;
+ store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(),
+ white_hash_prefix);
+ ReplaceV4Database(store_and_hash_prefixes, true /* stores_available */);
+
+ TestWhitelistClient client(false /* whitelist_expected */);
+ const GURL url_check("https://" + url_white_no_scheme);
+ EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdWhitelistUrl(
+ url_check, &client));
+
+ EXPECT_FALSE(client.callback_called_);
// Wait for PerformFullHashCheck to complete.
WaitForTasksOnTaskRunner();
+ EXPECT_TRUE(client.callback_called_);
+}
+
+// This is like CsdWhitelistWithPrefixMatch, but we also verify the
+// full-hash-match results in an appropriate callback value.
+TEST_F(V4LocalDatabaseManagerTest,
+ TestCheckCsdWhitelistWithPrefixTheFullMatch) {
+ std::string url_white_no_scheme("example.com/white/");
+ FullHash white_full_hash(crypto::SHA256HashString(url_white_no_scheme));
+
+ // Setup to receive full-hash hit. We won't make URL requests.
+ FullHashInfos infos(
+ {{white_full_hash, GetUrlCsdWhitelistId(), base::Time::Now()}});
+ ScopedFakeGetHashProtocolManagerFactory pin(infos);
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+
+ const HashPrefix white_hash_prefix(white_full_hash.substr(0, 5));
+ StoreAndHashPrefixes store_and_hash_prefixes;
+ store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(),
+ white_hash_prefix);
+ ReplaceV4Database(store_and_hash_prefixes, true /* stores_available */);
+
+ TestWhitelistClient client(true /* whitelist_expected */);
+ const GURL url_check("https://" + url_white_no_scheme);
+ EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdWhitelistUrl(
+ url_check, &client));
+
+ EXPECT_FALSE(client.callback_called_);
+
+ // Wait for PerformFullHashCheck to complete.
+ WaitForTasksOnTaskRunner();
+ EXPECT_TRUE(client.callback_called_);
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithFullMatch) {
+ // Setup to receive full-hash misses. We won't make URL requests.
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+
+ std::string url_white_no_scheme("example.com/white/");
+ FullHash white_full_hash(crypto::SHA256HashString(url_white_no_scheme));
+ StoreAndHashPrefixes store_and_hash_prefixes;
+ store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(), white_full_hash);
+ ReplaceV4Database(store_and_hash_prefixes, true /* stores_available */);
+
+ TestWhitelistClient client(false /* whitelist_expected */);
+ const GURL url_check("https://" + url_white_no_scheme);
+ EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdWhitelistUrl(
+ url_check, &client));
+
+ WaitForTasksOnTaskRunner();
+ EXPECT_FALSE(client.callback_called_);
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithNoMatch) {
+ // Setup to receive full-hash misses. We won't make URL requests.
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+
+ // Add a full hash that won't match the URL we check.
+ std::string url_white_no_scheme("example.com/white/");
+ FullHash white_full_hash(crypto::SHA256HashString(url_white_no_scheme));
+ StoreAndHashPrefixes store_and_hash_prefixes;
+ store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), white_full_hash);
+ ReplaceV4Database(store_and_hash_prefixes, true /* stores_available */);
+
+ TestWhitelistClient client(true /* whitelist_expected */);
+ const GURL url_check("https://other.com/");
+ EXPECT_EQ(
+ AsyncMatch::NO_MATCH,
+ v4_local_database_manager_->CheckCsdWhitelistUrl(url_check, &client));
+
+ WaitForTasksOnTaskRunner();
+ EXPECT_FALSE(client.callback_called_);
+}
+
+// When whitelist is unavailable, all URLS should be whitelisted.
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistUnavailable) {
+ // Setup to receive full-hash misses. We won't make URL requests.
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+
+ StoreAndHashPrefixes store_and_hash_prefixes;
+ ReplaceV4Database(store_and_hash_prefixes, false /* stores_available */);
+
+ TestWhitelistClient client(false /* whitelist_expected */);
+ const GURL url_check("https://other.com/");
+ EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdWhitelistUrl(
+ url_check, &client));
+
+ WaitForTasksOnTaskRunner();
+ EXPECT_FALSE(client.callback_called_);
}
TEST_F(V4LocalDatabaseManagerTest,
@@ -435,7 +577,7 @@ TEST_F(V4LocalDatabaseManagerTest,
ForceDisableLocalDatabaseManager();
EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
- GURL("http://example.com/a/"), nullptr));
+ GURL("http://example.com/a/"), usual_threat_types_, nullptr));
}
TEST_F(V4LocalDatabaseManagerTest, TestGetSeverestThreatTypeAndMetadata) {
@@ -489,7 +631,7 @@ TEST_F(V4LocalDatabaseManagerTest, TestChecksAreQueued) {
const GURL url("https://www.example.com/");
TestClient client(SB_THREAT_TYPE_SAFE, url);
EXPECT_TRUE(GetQueuedChecks().empty());
- v4_local_database_manager_->CheckBrowseUrl(url, &client);
+ v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client);
// The database is unavailable so the check should get queued.
EXPECT_EQ(1ul, GetQueuedChecks().size());
@@ -498,7 +640,7 @@ TEST_F(V4LocalDatabaseManagerTest, TestChecksAreQueued) {
EXPECT_TRUE(GetQueuedChecks().empty());
ResetV4Database();
- v4_local_database_manager_->CheckBrowseUrl(url, &client);
+ v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client);
// The database is unavailable so the check should get queued.
EXPECT_EQ(1ul, GetQueuedChecks().size());
@@ -527,7 +669,8 @@ TEST_F(V4LocalDatabaseManagerTest, CancelPending) {
// Test that a request flows through to the callback.
{
TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(url_bad, &client));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ url_bad, usual_threat_types_, &client));
EXPECT_FALSE(client.on_check_browse_url_result_called_);
WaitForTasksOnTaskRunner();
EXPECT_TRUE(client.on_check_browse_url_result_called_);
@@ -536,7 +679,8 @@ TEST_F(V4LocalDatabaseManagerTest, CancelPending) {
// Test that cancel prevents the callback from being called.
{
TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(url_bad, &client));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ url_bad, usual_threat_types_, &client));
v4_local_database_manager_->CancelCheck(&client);
EXPECT_FALSE(client.on_check_browse_url_result_called_);
WaitForTasksOnTaskRunner();
@@ -552,8 +696,10 @@ TEST_F(V4LocalDatabaseManagerTest, CancelQueued) {
TestClient client1(SB_THREAT_TYPE_SAFE, url,
v4_local_database_manager_.get());
TestClient client2(SB_THREAT_TYPE_SAFE, url);
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(url, &client1));
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(url, &client2));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ url, usual_threat_types_, &client1));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ url, usual_threat_types_, &client2));
EXPECT_EQ(2ul, GetQueuedChecks().size());
EXPECT_FALSE(client1.on_check_browse_url_result_called_);
EXPECT_FALSE(client2.on_check_browse_url_result_called_);
@@ -578,7 +724,8 @@ TEST_F(V4LocalDatabaseManagerTest, PerformFullHashCheckCalledAsync) {
const GURL url_bad("https://" + url_bad_no_scheme);
// The fake database returns a matched hash prefix.
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(url_bad, nullptr));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ url_bad, usual_threat_types_, nullptr));
EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
v4_local_database_manager_));
@@ -602,7 +749,8 @@ TEST_F(V4LocalDatabaseManagerTest, UsingWeakPtrDropsCallback) {
ReplaceV4Database(store_and_hash_prefixes);
const GURL url_bad("https://" + url_bad_no_scheme);
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(url_bad, nullptr));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ url_bad, usual_threat_types_, nullptr));
v4_local_database_manager_->StopOnIOThread(true);
// Release the V4LocalDatabaseManager object right away before the callback
@@ -671,7 +819,7 @@ TEST_F(V4LocalDatabaseManagerTest, TestMatchDownloadWhitelistUrl) {
GURL other_url("http://iffy.com");
StoreAndHashPrefixes store_and_hash_prefixes;
- store_and_hash_prefixes.emplace_back(GetCertCsdDownloadWhitelistId(),
+ store_and_hash_prefixes.emplace_back(GetUrlCsdDownloadWhitelistId(),
HashForUrl(good_url));
ReplaceV4Database(store_and_hash_prefixes, false /* not available */);
@@ -753,7 +901,8 @@ TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithSameClientAndCancel) {
GURL second_url("http://example.com/");
TestClient client(SB_THREAT_TYPE_SAFE, first_url);
// The fake database returns a matched hash prefix.
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(first_url, &client));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ first_url, usual_threat_types_, &client));
// That check gets queued. Now, let's cancel the check. After this, we should
// not receive a call for |OnCheckBrowseUrlResult| with |first_url|.
@@ -761,7 +910,8 @@ TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithSameClientAndCancel) {
// Now, re-use that client but for |second_url|.
client.expected_urls.assign(1, second_url);
- EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(second_url, &client));
+ EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+ second_url, usual_threat_types_, &client));
// Wait for PerformFullHashCheck to complete.
WaitForTasksOnTaskRunner();
@@ -958,7 +1108,7 @@ TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlWithOneBlacklisted) {
store_and_hash_prefixes.emplace_back(GetUrlMalBinId(), bad_hash_prefix);
ReplaceV4Database(store_and_hash_prefixes, true /* stores_available */);
- TestClient client(SB_THREAT_TYPE_BINARY_MALWARE_URL, url_chain);
+ TestClient client(SB_THREAT_TYPE_URL_BINARY_MALWARE, url_chain);
EXPECT_FALSE(
v4_local_database_manager_->CheckDownloadUrl(url_chain, &client));
EXPECT_FALSE(client.on_check_download_urls_result_called_);
@@ -966,4 +1116,45 @@ TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlWithOneBlacklisted) {
EXPECT_TRUE(client.on_check_download_urls_result_called_);
}
+TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileDoesNotExist) {
+ auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
+ ASSERT_FALSE(base::PathExists(store_file_path));
+
+ // Reset the database manager so that DeleteUnusedStoreFiles is called.
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+ ASSERT_FALSE(base::PathExists(store_file_path));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileSuccess) {
+ auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
+ ASSERT_FALSE(base::PathExists(store_file_path));
+
+ // Now write an empty file.
+ base::WriteFile(store_file_path, "", 0);
+ ASSERT_TRUE(base::PathExists(store_file_path));
+
+ // Reset the database manager so that DeleteUnusedStoreFiles is called.
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+ ASSERT_FALSE(base::PathExists(store_file_path));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileRandomFileNotDeleted) {
+ auto random_store_file_path = base_dir_.GetPath().AppendASCII("random.store");
+ ASSERT_FALSE(base::PathExists(random_store_file_path));
+
+ // Now write an empty file.
+ base::WriteFile(random_store_file_path, "", 0);
+ ASSERT_TRUE(base::PathExists(random_store_file_path));
+
+ // Reset the database manager so that DeleteUnusedStoreFiles is called.
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+ ASSERT_TRUE(base::PathExists(random_store_file_path));
+
+ // Cleanup
+ base::DeleteFile(random_store_file_path, false /* recursive */);
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing_db/v4_protocol_manager_util.cc b/chromium/components/safe_browsing_db/v4_protocol_manager_util.cc
index bd097a72b58..5153d4a5a98 100644
--- a/chromium/components/safe_browsing_db/v4_protocol_manager_util.cc
+++ b/chromium/components/safe_browsing_db/v4_protocol_manager_util.cc
@@ -21,6 +21,10 @@ using base::Time;
using base::TimeDelta;
namespace safe_browsing {
+const base::FilePath::CharType kStoreSuffix[] = FILE_PATH_LITERAL(".store");
+
+// The Safe Browsing V4 server URL prefix.
+const char kSbV4UrlPrefix[] = "https://safebrowsing.googleapis.com/v4";
namespace {
@@ -141,8 +145,11 @@ ListIdentifier GetUrlUwsId() {
return ListIdentifier(GetCurrentPlatformType(), URL, UNWANTED_SOFTWARE);
}
-// The Safe Browsing V4 server URL prefix.
-const char kSbV4UrlPrefix[] = "https://safebrowsing.googleapis.com/v4";
+std::string GetUmaSuffixForStore(const base::FilePath& file_path) {
+ DCHECK_EQ(kStoreSuffix, file_path.BaseName().Extension());
+ return base::StringPrintf(
+ ".%" PRIsFP, file_path.BaseName().RemoveExtension().value().c_str());
+}
StoreAndHashPrefix::StoreAndHashPrefix(ListIdentifier list_id,
const HashPrefix& hash_prefix)
@@ -165,6 +172,21 @@ size_t StoreAndHashPrefix::hash() const {
return base::HashInts(first, second);
}
+bool SBThreatTypeSetIsValidForCheckBrowseUrl(const SBThreatTypeSet& set) {
+ for (SBThreatType type : set) {
+ switch (type) {
+ case SB_THREAT_TYPE_URL_PHISHING:
+ case SB_THREAT_TYPE_URL_MALWARE:
+ case SB_THREAT_TYPE_URL_UNWANTED:
+ break;
+
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+
bool ListIdentifier::operator==(const ListIdentifier& other) const {
return platform_type_ == other.platform_type_ &&
threat_entry_type_ == other.threat_entry_type_ &&
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 882fb4a45cc..9c083542bdd 100644
--- a/chromium/components/safe_browsing_db/v4_protocol_manager_util.h
+++ b/chromium/components/safe_browsing_db/v4_protocol_manager_util.h
@@ -8,6 +8,7 @@
// A class that implements the stateless methods used by the GetHashUpdate and
// GetFullHash stubby calls made by Chrome using the SafeBrowsing V4 protocol.
+#include <initializer_list>
#include <memory>
#include <ostream>
#include <string>
@@ -15,6 +16,7 @@
#include <unordered_set>
#include <vector>
+#include "base/containers/flat_set.h"
#include "base/gtest_prod_util.h"
#include "base/strings/string_piece.h"
#include "components/safe_browsing_db/safebrowsing.pb.h"
@@ -75,6 +77,8 @@ struct V4ProtocolConfig {
// Different types of threats that SafeBrowsing protects against. This is the
// type that's returned to the clients of SafeBrowsing in Chromium.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.safe_browsing
+// GENERATED_JAVA_PREFIX_TO_STRIP: SB_THREAT_TYPE_
enum SBThreatType {
// This type can be used for lists that can be checked synchronously so a
// client callback isn't required, or for whitelists.
@@ -93,18 +97,18 @@ enum SBThreatType {
SB_THREAT_TYPE_URL_UNWANTED,
// The download URL is malware.
- SB_THREAT_TYPE_BINARY_MALWARE_URL,
+ SB_THREAT_TYPE_URL_BINARY_MALWARE,
// Url detected by the client-side phishing model. Note that unlike the
// above values, this does not correspond to a downloaded list.
- SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL,
+ SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING,
// The Chrome extension or app (given by its ID) is malware.
SB_THREAT_TYPE_EXTENSION,
// Url detected by the client-side malware IP list. This IP list is part
// of the client side detection model.
- SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL,
+ SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE,
// Url leads to a blacklisted resource script. Note that no warnings should be
// shown on this threat type, but an incident report might be sent.
@@ -115,8 +119,28 @@ enum SBThreatType {
// Activation patterns for the Subresource Filter.
SB_THREAT_TYPE_SUBRESOURCE_FILTER,
+
+ // CSD Phishing whitelist. This "threat" means a URL matched the whitelist.
+ SB_THREAT_TYPE_CSD_WHITELIST,
+
+ // Url detected by password protection service.
+ SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING,
};
+using SBThreatTypeSet = base::flat_set<SBThreatType>;
+
+// Return true if |set| only contains types that are valid for CheckBrowseUrl().
+// Intended for use in DCHECK().
+bool SBThreatTypeSetIsValidForCheckBrowseUrl(const SBThreatTypeSet& set);
+
+// Shorthand for creating an SBThreatTypeSet from a list of SBThreatTypes. Use
+// like CreateSBThreatTypeSet({SB_THREAT_TYPE_URL_PHISHING,
+// SB_THREAT_TYPE_URL_MALWARE})
+inline SBThreatTypeSet CreateSBThreatTypeSet(
+ std::initializer_list<SBThreatType> set) {
+ return SBThreatTypeSet(set, base::KEEP_FIRST_OF_DUPES);
+}
+
// The information required to uniquely identify each list the client is
// interested in maintaining and downloading from the SafeBrowsing servers.
// For example, for digests of Malware binaries on Windows:
@@ -163,6 +187,9 @@ ListIdentifier GetUrlSocEngId();
ListIdentifier GetUrlSubresourceFilterId();
ListIdentifier GetUrlUwsId();
+// Returns the basename of the store file, without the ".store" extension.
+std::string GetUmaSuffixForStore(const base::FilePath& file_path);
+
// Represents the state of each store.
using StoreStateMap = std::unordered_map<ListIdentifier, std::string>;
diff --git a/chromium/components/safe_browsing_db/v4_store.cc b/chromium/components/safe_browsing_db/v4_store.cc
index fe3220a1f2b..a1bc3070143 100644
--- a/chromium/components/safe_browsing_db/v4_store.cc
+++ b/chromium/components/safe_browsing_db/v4_store.cc
@@ -46,11 +46,6 @@ const char kTime[] = ".Time";
const uint32_t kFileMagic = 0x600D71FE;
const uint32_t kFileVersion = 9;
-std::string GetUmaSuffixForStore(const base::FilePath& file_path) {
- return base::StringPrintf(
- ".%" PRIsFP, file_path.BaseName().RemoveExtension().value().c_str());
-}
-
void RecordTimeWithAndWithoutSuffix(const std::string& metric,
base::TimeDelta time,
const base::FilePath& file_path) {
@@ -208,7 +203,7 @@ V4Store::V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
task_runner_(task_runner) {}
V4Store::~V4Store() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
}
std::string V4Store::DebugString() const {
@@ -533,7 +528,7 @@ ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
const HashPrefixMap& additions_map,
const RepeatedField<int32>* raw_removals,
const std::string& expected_checksum) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK(hash_prefix_map_.empty());
bool calculate_checksum = !expected_checksum.empty();
@@ -665,7 +660,7 @@ ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
}
StoreReadResult V4Store::ReadFromDisk() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
V4StoreFileFormat file_format;
int64_t file_size;
@@ -801,7 +796,7 @@ bool V4Store::HashPrefixMatches(const HashPrefix& hash_prefix,
}
bool V4Store::VerifyChecksum() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (expected_checksum_.empty()) {
// Nothing to check here folks!
diff --git a/chromium/components/safe_browsing_db/v4_update_protocol_manager.cc b/chromium/components/safe_browsing_db/v4_update_protocol_manager.cc
index 3d485bdd6bf..ddd08f588b6 100644
--- a/chromium/components/safe_browsing_db/v4_update_protocol_manager.cc
+++ b/chromium/components/safe_browsing_db/v4_update_protocol_manager.cc
@@ -153,7 +153,9 @@ V4UpdateProtocolManager::V4UpdateProtocolManager(
// when it is ready to process updates.
}
-V4UpdateProtocolManager::~V4UpdateProtocolManager() {}
+V4UpdateProtocolManager::~V4UpdateProtocolManager() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
bool V4UpdateProtocolManager::IsUpdateScheduled() const {
return update_timer_.IsRunning();
@@ -166,7 +168,7 @@ void V4UpdateProtocolManager::ScheduleNextUpdate(
}
void V4UpdateProtocolManager::ScheduleNextUpdateWithBackoff(bool back_off) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (config_.disable_auto_update) {
DCHECK(!IsUpdateScheduled());
@@ -181,7 +183,7 @@ void V4UpdateProtocolManager::ScheduleNextUpdateWithBackoff(bool back_off) {
// According to section 5 of the SafeBrowsing protocol specification, we must
// back off after a certain number of errors.
base::TimeDelta V4UpdateProtocolManager::GetNextUpdateInterval(bool back_off) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(next_update_interval_ > base::TimeDelta());
base::TimeDelta next = next_update_interval_;
@@ -208,7 +210,7 @@ base::TimeDelta V4UpdateProtocolManager::GetNextUpdateInterval(bool back_off) {
void V4UpdateProtocolManager::ScheduleNextUpdateAfterInterval(
base::TimeDelta interval) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(interval >= base::TimeDelta());
// Unschedule any current timer.
@@ -299,7 +301,7 @@ bool V4UpdateProtocolManager::ParseUpdateResponse(
}
void V4UpdateProtocolManager::IssueUpdateRequest() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If an update request is already pending, record and return silently.
if (request_.get()) {
@@ -370,7 +372,7 @@ void V4UpdateProtocolManager::HandleTimeout() {
// SafeBrowsing request responses are handled here.
void V4UpdateProtocolManager::OnURLFetchComplete(
const net::URLFetcher* source) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
timeout_timer_.Stop();
diff --git a/chromium/components/safe_browsing_db/v4_update_protocol_manager.h b/chromium/components/safe_browsing_db/v4_update_protocol_manager.h
index 5c60a0c398d..706c7d4c331 100644
--- a/chromium/components/safe_browsing_db/v4_update_protocol_manager.h
+++ b/chromium/components/safe_browsing_db/v4_update_protocol_manager.h
@@ -18,7 +18,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
@@ -47,8 +47,7 @@ typedef base::Callback<void(std::unique_ptr<ParsedServerResponse>)>
typedef base::Callback<ExtendedReportingLevel()> ExtendedReportingLevelCallback;
-class V4UpdateProtocolManager : public net::URLFetcherDelegate,
- public base::NonThreadSafe {
+class V4UpdateProtocolManager : public net::URLFetcherDelegate {
public:
~V4UpdateProtocolManager() override;
@@ -192,6 +191,8 @@ class V4UpdateProtocolManager : public net::URLFetcherDelegate,
ExtendedReportingLevelCallback extended_reporting_level_callback_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManager);
};
diff --git a/chromium/components/safe_browsing_db/whitelist_checker_client.cc b/chromium/components/safe_browsing_db/whitelist_checker_client.cc
new file mode 100644
index 00000000000..4691ddd17e1
--- /dev/null
+++ b/chromium/components/safe_browsing_db/whitelist_checker_client.cc
@@ -0,0 +1,74 @@
+// 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/safe_browsing_db/whitelist_checker_client.h"
+
+#include "base/bind.h"
+
+namespace safe_browsing {
+
+// Static
+void WhitelistCheckerClient::StartCheckCsdWhitelist(
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+ const GURL& url,
+ base::Callback<void(bool)> callback_for_result) {
+ // TODO(nparker): Maybe also call SafeBrowsingDatabaseManager::CanCheckUrl()
+ if (!url.is_valid()) {
+ callback_for_result.Run(true /* is_whitelisted */);
+ return;
+ }
+
+ // Make a client for each request. The caller could have several in
+ // flight at once.
+ std::unique_ptr<WhitelistCheckerClient> client =
+ base::MakeUnique<WhitelistCheckerClient>(callback_for_result,
+ database_manager);
+ AsyncMatch match = database_manager->CheckCsdWhitelistUrl(url, client.get());
+
+ switch (match) {
+ case AsyncMatch::MATCH:
+ callback_for_result.Run(true /* is_whitelisted */);
+ break;
+ case AsyncMatch::NO_MATCH:
+ callback_for_result.Run(false /* is_whitelisted */);
+ break;
+ case AsyncMatch::ASYNC:
+ // Client is now self-owned. When it gets called back with the result,
+ // it will delete itself.
+ client.release();
+ break;
+ }
+}
+
+WhitelistCheckerClient::WhitelistCheckerClient(
+ base::Callback<void(bool)> callback_for_result,
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
+ : callback_for_result_(callback_for_result),
+ database_manager_(database_manager),
+ weak_factory_(this) {
+ // Set a timer to fail open, i.e. call it "whitelisted", if the full
+ // check takes too long.
+ auto timeout_callback =
+ base::Bind(&WhitelistCheckerClient::OnCheckWhitelistUrlTimeout,
+ weak_factory_.GetWeakPtr());
+ timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeoutMsec),
+ timeout_callback);
+}
+
+WhitelistCheckerClient::~WhitelistCheckerClient() {}
+
+// SafeBrowsingDatabaseMananger::Client impl
+void WhitelistCheckerClient::OnCheckWhitelistUrlResult(bool is_whitelisted) {
+ timer_.Stop();
+ callback_for_result_.Run(is_whitelisted);
+ // This method is invoked only if we're already self-owned.
+ delete this;
+}
+
+void WhitelistCheckerClient::OnCheckWhitelistUrlTimeout() {
+ database_manager_->CancelCheck(this);
+ this->OnCheckWhitelistUrlResult(true /* is_whitelisted */);
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing_db/whitelist_checker_client.h b/chromium/components/safe_browsing_db/whitelist_checker_client.h
new file mode 100644
index 00000000000..c28782618d3
--- /dev/null
+++ b/chromium/components/safe_browsing_db/whitelist_checker_client.h
@@ -0,0 +1,55 @@
+// 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_SAFE_BROWSING_DB_WHITELIST_CHECKER_CLIENT_H_
+#define COMPONENTS_SAFE_BROWSING_DB_WHITELIST_CHECKER_CLIENT_H_
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing_db/database_manager.h"
+
+namespace safe_browsing {
+
+// This provides a simpler interface to
+// SafeBrowsingDatabaseManager::CheckCsdWhitelistUrl() for callers that
+// don't want to track their own clients.
+
+class WhitelistCheckerClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+ using BoolCallback = base::Callback<void(bool /* is_whitelisted */)>;
+
+ // Static method to instantiate and start a check. The callback will
+ // be invoked when it's done, times out, or if database_manager gets
+ // shut down. Must be called on IO thread.
+ static void StartCheckCsdWhitelist(
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+ const GURL& url,
+ BoolCallback callback_for_result);
+
+ WhitelistCheckerClient(
+ BoolCallback callback_for_result,
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager);
+ ~WhitelistCheckerClient() override;
+
+ // SafeBrowsingDatabaseMananger::Client impl
+ void OnCheckWhitelistUrlResult(bool is_whitelisted) override;
+
+ protected:
+ static const int kTimeoutMsec = 5000;
+ base::OneShotTimer timer_;
+ BoolCallback callback_for_result_;
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+ base::WeakPtrFactory<WhitelistCheckerClient> weak_factory_;
+
+ private:
+ WhitelistCheckerClient();
+
+ // Called when the call to CheckCsdWhitelistUrl times out.
+ void OnCheckWhitelistUrlTimeout();
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_DB_WHITELIST_CHECKER_CLIENT_H_
diff --git a/chromium/components/safe_browsing_db/whitelist_checker_client_unittest.cc b/chromium/components/safe_browsing_db/whitelist_checker_client_unittest.cc
new file mode 100644
index 00000000000..0b78df22d9a
--- /dev/null
+++ b/chromium/components/safe_browsing_db/whitelist_checker_client_unittest.cc
@@ -0,0 +1,132 @@
+// 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/safe_browsing_db/whitelist_checker_client.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/safe_browsing_db/test_database_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+using base::TimeDelta;
+using testing::_;
+using testing::Return;
+using testing::SaveArg;
+
+using BoolCallback = base::Callback<void(bool /* is_whitelisted */)>;
+using MockBoolCallback = testing::StrictMock<base::MockCallback<BoolCallback>>;
+
+namespace {
+class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
+ public:
+ MockSafeBrowsingDatabaseManager() {}
+
+ MOCK_METHOD1(CancelCheck, void(SafeBrowsingDatabaseManager::Client*));
+
+ MOCK_METHOD2(CheckCsdWhitelistUrl,
+ AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
+
+ protected:
+ ~MockSafeBrowsingDatabaseManager() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
+};
+} // namespace
+
+class WhitelistCheckerClientTest : public testing::Test {
+ public:
+ WhitelistCheckerClientTest() : target_url_("http://foo.bar") {}
+
+ void SetUp() override {
+ database_manager_ = new MockSafeBrowsingDatabaseManager;
+ task_runner_ = new base::TestMockTimeTaskRunner(base::Time::Now(),
+ base::TimeTicks::Now());
+ message_loop_.reset(new base::MessageLoop);
+ io_thread_ = base::MakeUnique<content::TestBrowserThread>(
+ content::BrowserThread::IO, base::MessageLoop::current());
+ message_loop_->SetTaskRunner(task_runner_);
+ }
+
+ void TearDown() override {
+ database_manager_ = nullptr;
+ base::RunLoop().RunUntilIdle();
+
+ // Verify no callback is remaining.
+ // TODO(nparker): We should somehow EXPECT that no entry is remaining,
+ // rather than just invoking it.
+ task_runner_->FastForwardUntilNoTasksRemain();
+ }
+
+ protected:
+ GURL target_url_;
+ scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
+
+ // Needed for |database_manager_| teardown tasks.
+ std::unique_ptr<content::TestBrowserThread> io_thread_;
+
+ std::unique_ptr<base::MessageLoop> message_loop_;
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+};
+
+TEST_F(WhitelistCheckerClientTest, TestMatch) {
+ EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+ .WillOnce(Return(AsyncMatch::MATCH));
+
+ MockBoolCallback callback;
+ EXPECT_CALL(callback, Run(true /* is_whitelisted */));
+ WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+ callback.Get());
+}
+
+TEST_F(WhitelistCheckerClientTest, TestNoMatch) {
+ EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+ .WillOnce(Return(AsyncMatch::NO_MATCH));
+
+ MockBoolCallback callback;
+ EXPECT_CALL(callback, Run(false /* is_whitelisted */));
+ WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+ callback.Get());
+}
+
+TEST_F(WhitelistCheckerClientTest, TestAsyncNoMatch) {
+ SafeBrowsingDatabaseManager::Client* client;
+ EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+ .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+
+ MockBoolCallback callback;
+ WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+ callback.Get());
+ // Callback should not be called yet.
+
+ EXPECT_CALL(callback, Run(false /* is_whitelisted */));
+ // The self-owned client deletes itself here.
+ client->OnCheckWhitelistUrlResult(false);
+}
+
+TEST_F(WhitelistCheckerClientTest, TestAsyncTimeout) {
+ SafeBrowsingDatabaseManager::Client* client;
+ EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(target_url_, _))
+ .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+ EXPECT_CALL(*database_manager_.get(), CancelCheck(_)).Times(1);
+
+ MockBoolCallback callback;
+ WhitelistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+ callback.Get());
+ task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
+ // No callback yet.
+
+ EXPECT_CALL(callback, Run(true /* is_whitelisted */));
+ task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(5));
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing_strings.grdp b/chromium/components/safe_browsing_strings.grdp
new file mode 100644
index 00000000000..92bb0ae0518
--- /dev/null
+++ b/chromium/components/safe_browsing_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <message name="IDS_SB_UNDER_CONSTRUCTION" desc="A message that the Safe Browsing page is under construction.">
+ The Safe Browsing page is under construction.
+ </message>
+</grit-part>
diff --git a/chromium/components/safe_json/BUILD.gn b/chromium/components/safe_json/BUILD.gn
index 3040ba02cbf..37befe20d69 100644
--- a/chromium/components/safe_json/BUILD.gn
+++ b/chromium/components/safe_json/BUILD.gn
@@ -8,8 +8,6 @@ if (is_android) {
static_library("safe_json") {
sources = [
- "android/component_jni_registrar.cc",
- "android/component_jni_registrar.h",
"json_sanitizer.cc",
"json_sanitizer.h",
"json_sanitizer_android.cc",
diff --git a/chromium/components/safe_json/json_sanitizer.h b/chromium/components/safe_json/json_sanitizer.h
index a1c9fc3b705..434f4be6d1a 100644
--- a/chromium/components/safe_json/json_sanitizer.h
+++ b/chromium/components/safe_json/json_sanitizer.h
@@ -36,10 +36,6 @@ class JsonSanitizer {
const StringCallback& success_callback,
const StringCallback& error_callback);
-#if defined(OS_ANDROID)
- static bool Register(JNIEnv* env);
-#endif
-
protected:
JsonSanitizer() {}
~JsonSanitizer() {}
diff --git a/chromium/components/safe_json/json_sanitizer_android.cc b/chromium/components/safe_json/json_sanitizer_android.cc
index b69b8d2022e..f79ea7f5e99 100644
--- a/chromium/components/safe_json/json_sanitizer_android.cc
+++ b/chromium/components/safe_json/json_sanitizer_android.cc
@@ -51,6 +51,11 @@ class JsonSanitizerAndroid : public JsonSanitizer {
StringCallback success_callback_;
StringCallback error_callback_;
+ // Runs the callbacks. Stored as a member because grabbing it when posting
+ // the callbacks (being called from Java) fails under certain circumstances
+ // (https://crbug.com/739510).
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
DISALLOW_COPY_AND_ASSIGN(JsonSanitizerAndroid);
};
@@ -58,7 +63,8 @@ JsonSanitizerAndroid::JsonSanitizerAndroid(
const StringCallback& success_callback,
const StringCallback& error_callback)
: success_callback_(success_callback),
- error_callback_(error_callback) {}
+ error_callback_(error_callback),
+ task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
void JsonSanitizerAndroid::Sanitize(const std::string& unsafe_json) {
// The JSON parser only accepts wellformed UTF-8.
@@ -77,13 +83,11 @@ void JsonSanitizerAndroid::Sanitize(const std::string& unsafe_json) {
}
void JsonSanitizerAndroid::OnSuccess(const std::string& json) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(success_callback_, json));
+ task_runner_->PostTask(FROM_HERE, base::Bind(success_callback_, json));
}
void JsonSanitizerAndroid::OnError(const std::string& error) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(error_callback_, error));
+ task_runner_->PostTask(FROM_HERE, base::Bind(error_callback_, error));
}
} // namespace
@@ -111,15 +115,10 @@ void JsonSanitizer::Sanitize(const std::string& unsafe_json,
const StringCallback& success_callback,
const StringCallback& error_callback) {
// JsonSanitizerAndroid does all its work synchronously, but posts any
- // callbacks to the current message loop. This means it can be destroyed at
+ // callbacks to the current sequence. This means it can be destroyed at
// the end of this method.
JsonSanitizerAndroid sanitizer(success_callback, error_callback);
sanitizer.Sanitize(unsafe_json);
}
-// static
-bool JsonSanitizer::Register(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
} // namespace safe_json
diff --git a/chromium/components/safe_json/safe_json_parser_impl.cc b/chromium/components/safe_json/safe_json_parser_impl.cc
index 8f0ea483ed9..9694dad8643 100644
--- a/chromium/components/safe_json/safe_json_parser_impl.cc
+++ b/chromium/components/safe_json/safe_json_parser_impl.cc
@@ -19,23 +19,12 @@ SafeJsonParserImpl::SafeJsonParserImpl(const std::string& unsafe_json,
const ErrorCallback& error_callback)
: unsafe_json_(unsafe_json),
success_callback_(success_callback),
- error_callback_(error_callback) {
- io_thread_checker_.DetachFromThread();
-}
+ error_callback_(error_callback) {}
SafeJsonParserImpl::~SafeJsonParserImpl() = default;
void SafeJsonParserImpl::Start() {
- caller_task_runner_ = base::SequencedTaskRunnerHandle::Get();
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO, FROM_HERE,
- base::Bind(&SafeJsonParserImpl::StartOnIOThread, base::Unretained(this)));
-}
-
-void SafeJsonParserImpl::StartOnIOThread() {
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- DCHECK(io_thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mojo_json_parser_.reset(
new content::UtilityProcessMojoClient<mojom::SafeJsonParser>(
l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_JSON_PARSER_NAME)));
@@ -50,34 +39,27 @@ void SafeJsonParserImpl::StartOnIOThread() {
}
void SafeJsonParserImpl::OnConnectionError() {
- DCHECK(io_thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Shut down the utility process.
mojo_json_parser_.reset();
- caller_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&SafeJsonParserImpl::ReportResults, base::Unretained(this),
- nullptr, "Connection error with the json parser process."));
+ ReportResults(nullptr, "Connection error with the json parser process.");
}
void SafeJsonParserImpl::OnParseDone(std::unique_ptr<base::Value> result,
const base::Optional<std::string>& error) {
- DCHECK(io_thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Shut down the utility process.
mojo_json_parser_.reset();
- // Call ReportResults() on caller's thread.
- caller_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&SafeJsonParserImpl::ReportResults, base::Unretained(this),
- base::Passed(&result), error.value_or("")));
+ ReportResults(std::move(result), error.value_or(""));
}
void SafeJsonParserImpl::ReportResults(std::unique_ptr<base::Value> parsed_json,
const std::string& error) {
- DCHECK(caller_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error.empty() && parsed_json) {
if (!success_callback_.is_null())
success_callback_.Run(std::move(parsed_json));
diff --git a/chromium/components/safe_json/safe_json_parser_impl.h b/chromium/components/safe_json/safe_json_parser_impl.h
index 03ac99563f5..54da8dcffaa 100644
--- a/chromium/components/safe_json/safe_json_parser_impl.h
+++ b/chromium/components/safe_json/safe_json_parser_impl.h
@@ -17,7 +17,6 @@
#include "content/public/browser/utility_process_mojo_client.h"
namespace base {
-class SequencedTaskRunner;
class Value;
}
@@ -50,16 +49,11 @@ class SafeJsonParserImpl : public SafeJsonParser {
const std::string unsafe_json_;
SuccessCallback success_callback_;
ErrorCallback error_callback_;
- scoped_refptr<base::SequencedTaskRunner> caller_task_runner_;
std::unique_ptr<content::UtilityProcessMojoClient<mojom::SafeJsonParser>>
mojo_json_parser_;
- // Used instead of DCHECK_CURRENTLY_ON(BrowserThread::IO) because it's
- // posssible that it fails when the IO thread message loop is shutting down.
- // This happens after the IO thread has unregistered from the BrowserThread
- // list.
- base::ThreadChecker io_thread_checker_;
+ SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(SafeJsonParserImpl);
};
diff --git a/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.cc b/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.cc
index 2cb3bb00b20..559955eab49 100644
--- a/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.cc
+++ b/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.cc
@@ -20,7 +20,6 @@ SafeJsonParserMojoImpl::~SafeJsonParserMojoImpl() = default;
// static
void SafeJsonParserMojoImpl::Create(
- const service_manager::BindSourceInfo& source_info,
mojom::SafeJsonParserRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<SafeJsonParserMojoImpl>(),
std::move(request));
diff --git a/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.h b/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.h
index cfe18d0ab37..ab4194daaf4 100644
--- a/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.h
+++ b/chromium/components/safe_json/utility/safe_json_parser_mojo_impl.h
@@ -10,10 +10,6 @@
#include "base/macros.h"
#include "components/safe_json/public/interfaces/safe_json.mojom.h"
-namespace service_manager {
-struct BindSourceInfo;
-}
-
namespace safe_json {
class SafeJsonParserMojoImpl : public mojom::SafeJsonParser {
@@ -21,8 +17,7 @@ class SafeJsonParserMojoImpl : public mojom::SafeJsonParser {
SafeJsonParserMojoImpl();
~SafeJsonParserMojoImpl() override;
- static void Create(const service_manager::BindSourceInfo& source_info,
- mojom::SafeJsonParserRequest request);
+ static void Create(mojom::SafeJsonParserRequest request);
private:
// mojom::SafeJsonParser implementation.
diff --git a/chromium/components/search_engines/default_search_manager.cc b/chromium/components/search_engines/default_search_manager.cc
index a69aa74a9d5..ebe9a875aaa 100644
--- a/chromium/components/search_engines/default_search_manager.cc
+++ b/chromium/components/search_engines/default_search_manager.cc
@@ -53,6 +53,7 @@ const char DefaultSearchManager::kNewTabURL[] = "new_tab_url";
const char DefaultSearchManager::kContextualSearchURL[] =
"contextual_search_url";
const char DefaultSearchManager::kFaviconURL[] = "favicon_url";
+const char DefaultSearchManager::kLogoURL[] = "logo_url";
const char DefaultSearchManager::kOriginatingURL[] = "originating_url";
const char DefaultSearchManager::kSearchURLPostParams[] =
diff --git a/chromium/components/search_engines/default_search_manager.h b/chromium/components/search_engines/default_search_manager.h
index 975c70e142c..7f0853ad898 100644
--- a/chromium/components/search_engines/default_search_manager.h
+++ b/chromium/components/search_engines/default_search_manager.h
@@ -42,6 +42,7 @@ class DefaultSearchManager {
static const char kNewTabURL[];
static const char kContextualSearchURL[];
static const char kFaviconURL[];
+ static const char kLogoURL[];
static const char kOriginatingURL[];
static const char kSearchURLPostParams[];
diff --git a/chromium/components/search_engines/default_search_manager_unittest.cc b/chromium/components/search_engines/default_search_manager_unittest.cc
index 5e72128e705..8b2c54b42c4 100644
--- a/chromium/components/search_engines/default_search_manager_unittest.cc
+++ b/chromium/components/search_engines/default_search_manager_unittest.cc
@@ -34,7 +34,7 @@ void SetOverrides(sync_preferences::TestingPrefServiceSyncable* prefs,
prefs->SetUserPref(prefs::kSearchProviderOverridesVersion,
base::MakeUnique<base::Value>(1));
auto overrides = base::MakeUnique<base::ListValue>();
- std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
+ auto entry = base::MakeUnique<base::DictionaryValue>();
entry->SetString("name", update ? "new_foo" : "foo");
entry->SetString("keyword", update ? "new_fook" : "fook");
@@ -44,23 +44,23 @@ void SetOverrides(sync_preferences::TestingPrefServiceSyncable* prefs,
entry->SetInteger("id", 1001);
entry->SetString("suggest_url", "http://foo.com/suggest?q={searchTerms}");
entry->SetString("instant_url", "http://foo.com/instant?q={searchTerms}");
- base::ListValue* alternate_urls = new base::ListValue;
+ auto alternate_urls = base::MakeUnique<base::ListValue>();
alternate_urls->AppendString("http://foo.com/alternate?q={searchTerms}");
- entry->Set("alternate_urls", alternate_urls);
+ entry->Set("alternate_urls", std::move(alternate_urls));
entry->SetString("search_terms_replacement_key", "espv");
- overrides->Append(entry->CreateDeepCopy());
+ overrides->Append(std::move(entry));
- entry.reset(new base::DictionaryValue);
+ entry = base::MakeUnique<base::DictionaryValue>();
entry->SetInteger("id", 1002);
entry->SetString("name", update ? "new_bar" : "bar");
entry->SetString("keyword", update ? "new_bark" : "bark");
entry->SetString("encoding", std::string());
- overrides->Append(entry->CreateDeepCopy());
+ overrides->Append(base::MakeUnique<base::Value>(*entry));
entry->SetInteger("id", 1003);
entry->SetString("name", "baz");
entry->SetString("keyword", "bazk");
entry->SetString("encoding", "UTF-8");
- overrides->Append(entry->CreateDeepCopy());
+ overrides->Append(std::move(entry));
prefs->SetUserPref(prefs::kSearchProviderOverrides, std::move(overrides));
}
diff --git a/chromium/components/search_engines/default_search_policy_handler.cc b/chromium/components/search_engines/default_search_policy_handler.cc
index 83a356515b0..295334d2d0f 100644
--- a/chromium/components/search_engines/default_search_policy_handler.cc
+++ b/chromium/components/search_engines/default_search_policy_handler.cc
@@ -36,7 +36,8 @@ void SetListInPref(const PolicyMap& policies,
bool is_list = policy_value->GetAsList(&policy_list);
DCHECK(is_list);
}
- dict->Set(key, policy_list ? policy_list->DeepCopy() : new base::ListValue());
+ dict->Set(key, policy_list ? base::MakeUnique<base::ListValue>(*policy_list)
+ : base::MakeUnique<base::ListValue>());
}
// Extracts a string from a policy value and adds it to a pref dictionary.
diff --git a/chromium/components/search_engines/prepopulated_engines.json b/chromium/components/search_engines/prepopulated_engines.json
index 58f4ae0dcc9..02999b593bc 100644
--- a/chromium/components/search_engines/prepopulated_engines.json
+++ b/chromium/components/search_engines/prepopulated_engines.json
@@ -21,6 +21,8 @@
// - NOTIFY the Chrome Webstore team if you add/delete a search engine or
// change domain of an existing one (send email to webstore-eng@google.com).
// They need to know the mapping between an engine's "id" and its URLs.
+// - Use HTTPS URLs when adding new search engines or adding URLs to existing
+// ones. It's not the 90s anymore.
{
"int_variables": {
@@ -111,7 +113,7 @@
"google": {
"name": "Google",
"keyword": "google.com",
- "favicon_url": "http://www.google.com/favicon.ico",
+ "favicon_url": "https://www.google.com/favicon.ico",
"search_url": "{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}{google:contextualSearchVersion}ie={inputEncoding}",
"suggest_url": "{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}",
"instant_url": "{google:baseURL}webhp?sourceid=chrome-instant&{google:RLZ}{google:forceInstantResults}{google:instantExtendedEnabledParameter}ie={inputEncoding}",
@@ -590,6 +592,7 @@
"name": "\u042f\u043d\u0434\u0435\u043a\u0441",
"keyword": "yandex.by",
"favicon_url": "https://yastatic.net/lego/_/pDu9OWAQKB0s2J9IojKpiS_Eho.ico",
+ "logo_url": "https://storage.ape.yandex.net/get/browser/Doodles/yandex/drawable-xxhdpi/yandex.png",
"search_url": "https://yandex.by/{yandex:searchPath}?text={searchTerms}",
"suggest_url": "https://suggest.yandex.by/suggest-ff.cgi?part={searchTerms}",
"image_url": "https://yandex.by/images/search/?rpt=imageview",
@@ -603,6 +606,7 @@
"name": "\u042f\u043d\u0434\u0435\u043a\u0441",
"keyword": "yandex.kz",
"favicon_url": "https://yastatic.net/lego/_/pDu9OWAQKB0s2J9IojKpiS_Eho.ico",
+ "logo_url": "https://storage.ape.yandex.net/get/browser/Doodles/yandex/drawable-xxhdpi/yandex.png",
"search_url": "https://yandex.kz/{yandex:searchPath}?text={searchTerms}",
"suggest_url": "https://suggest.yandex.kz/suggest-ff.cgi?part={searchTerms}",
"image_url": "https://yandex.kz/images/search/?rpt=imageview",
@@ -616,6 +620,7 @@
"name": "\u042f\u043d\u0434\u0435\u043a\u0441",
"keyword": "yandex.ru",
"favicon_url": "https://yastatic.net/lego/_/pDu9OWAQKB0s2J9IojKpiS_Eho.ico",
+ "logo_url": "https://storage.ape.yandex.net/get/browser/Doodles/yandex/drawable-xxhdpi/yandex.png",
"search_url": "https://yandex.ru/{yandex:searchPath}?text={searchTerms}&{yandex:referralID}",
"suggest_url": "https://suggest.yandex.ru/suggest-ff.cgi?part={searchTerms}",
"image_url": "https://yandex.ru/images/search/?rpt=imageview",
@@ -642,6 +647,7 @@
"name": "\u042f\u043d\u0434\u0435\u043a\u0441",
"keyword": "yandex.ua",
"favicon_url": "https://yastatic.net/lego/_/pDu9OWAQKB0s2J9IojKpiS_Eho.ico",
+ "logo_url": "https://storage.ape.yandex.net/get/browser/Doodles/yandex/drawable-xxhdpi/yandex.png",
"search_url": "https://yandex.ua/{yandex:searchPath}?text={searchTerms}",
"suggest_url": "https://suggest.yandex.ua/suggest-ff.cgi?part={searchTerms}",
"image_url": "https://yandex.ua/images/search/?rpt=imageview",
diff --git a/chromium/components/search_engines/prepopulated_engines_schema.json b/chromium/components/search_engines/prepopulated_engines_schema.json
index ffe3810f6b1..b27caf2bd38 100644
--- a/chromium/components/search_engines/prepopulated_engines_schema.json
+++ b/chromium/components/search_engines/prepopulated_engines_schema.json
@@ -32,6 +32,7 @@
{ "field": "new_tab_url", "type": "string", "optional": true },
// If omitted, this engine does not support contextual search.
{ "field": "contextual_search_url", "type": "string", "optional": true },
+ { "field": "logo_url", "type": "string", "optional": true },
// The followings are post parameters for the corresponding search URL.
// If omitted, a GET request will be sent when using the corresponding
// search URL. Otherwise, a POST request will be sent.
diff --git a/chromium/components/search_engines/template_url.h b/chromium/components/search_engines/template_url.h
index 169997d71bd..7fd245b1e67 100644
--- a/chromium/components/search_engines/template_url.h
+++ b/chromium/components/search_engines/template_url.h
@@ -568,6 +568,8 @@ class TemplateURL {
}
const GURL& favicon_url() const { return data_.favicon_url; }
+ const GURL& logo_url() const { return data_.logo_url; }
+
const GURL& originating_url() const { return data_.originating_url; }
bool safe_for_autoreplace() const { return data_.safe_for_autoreplace; }
diff --git a/chromium/components/search_engines/template_url_data.cc b/chromium/components/search_engines/template_url_data.cc
index 187d0a277c6..84adb1aa87d 100644
--- a/chromium/components/search_engines/template_url_data.cc
+++ b/chromium/components/search_engines/template_url_data.cc
@@ -34,6 +34,7 @@ TemplateURLData::TemplateURLData(const base::string16& name,
base::StringPiece image_url,
base::StringPiece new_tab_url,
base::StringPiece contextual_search_url,
+ base::StringPiece logo_url,
base::StringPiece search_url_post_params,
base::StringPiece suggest_url_post_params,
base::StringPiece instant_url_post_params,
@@ -48,6 +49,7 @@ TemplateURLData::TemplateURLData(const base::string16& name,
image_url(image_url.as_string()),
new_tab_url(new_tab_url.as_string()),
contextual_search_url(contextual_search_url.as_string()),
+ logo_url(GURL(logo_url)),
search_url_post_params(search_url_post_params.as_string()),
suggestions_url_post_params(suggest_url_post_params.as_string()),
instant_url_post_params(instant_url_post_params.as_string()),
@@ -78,8 +80,6 @@ TemplateURLData::~TemplateURLData() {
}
void TemplateURLData::SetShortName(const base::string16& short_name) {
- DCHECK(!short_name.empty());
-
// Remove tabs, carriage returns, and the like, as they can corrupt
// how the short name is displayed.
short_name_ = base::CollapseWhitespace(short_name, true);
diff --git a/chromium/components/search_engines/template_url_data.h b/chromium/components/search_engines/template_url_data.h
index 93088a8dbdf..6ad8be2817d 100644
--- a/chromium/components/search_engines/template_url_data.h
+++ b/chromium/components/search_engines/template_url_data.h
@@ -37,6 +37,7 @@ struct TemplateURLData {
base::StringPiece image_url,
base::StringPiece new_tab_url,
base::StringPiece contextual_search_url,
+ base::StringPiece logo_url,
base::StringPiece search_url_post_params,
base::StringPiece suggest_url_post_params,
base::StringPiece instant_url_post_params,
@@ -71,6 +72,9 @@ struct TemplateURLData {
std::string new_tab_url;
std::string contextual_search_url;
+ // Optional URL for the logo.
+ GURL logo_url;
+
// The following post_params are comma-separated lists used to specify the
// post parameters for the corresponding URL.
std::string search_url_post_params;
@@ -78,7 +82,7 @@ struct TemplateURLData {
std::string instant_url_post_params;
std::string image_url_post_params;
- // Optional favicon for the TemplateURL.
+ // Favicon for the TemplateURL.
GURL favicon_url;
// URL to the OSD file this came from. May be empty.
diff --git a/chromium/components/search_engines/template_url_data_util.cc b/chromium/components/search_engines/template_url_data_util.cc
index fd3138b7bc7..8712307e23e 100644
--- a/chromium/components/search_engines/template_url_data_util.cc
+++ b/chromium/components/search_engines/template_url_data_util.cc
@@ -49,10 +49,13 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromDictionary(
&result->contextual_search_url);
std::string favicon_url;
std::string originating_url;
+ std::string logo_url;
dict.GetString(DefaultSearchManager::kFaviconURL, &favicon_url);
dict.GetString(DefaultSearchManager::kOriginatingURL, &originating_url);
+ dict.GetString(DefaultSearchManager::kLogoURL, &logo_url);
result->favicon_url = GURL(favicon_url);
result->originating_url = GURL(originating_url);
+ result->logo_url = GURL(logo_url);
dict.GetString(DefaultSearchManager::kSearchURLPostParams,
&result->search_url_post_params);
@@ -133,6 +136,7 @@ std::unique_ptr<base::DictionaryValue> TemplateURLDataToDictionary(
data.favicon_url.spec());
url_dict->SetString(DefaultSearchManager::kOriginatingURL,
data.originating_url.spec());
+ url_dict->SetString(DefaultSearchManager::kLogoURL, data.logo_url.spec());
url_dict->SetString(DefaultSearchManager::kSearchURLPostParams,
data.search_url_post_params);
@@ -187,10 +191,10 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromPrepopulatedEngine(
base::WideToUTF16(engine.name), base::WideToUTF16(engine.keyword),
engine.search_url, engine.suggest_url, engine.instant_url,
engine.image_url, engine.new_tab_url, engine.contextual_search_url,
- engine.search_url_post_params, engine.suggest_url_post_params,
- engine.instant_url_post_params, engine.image_url_post_params,
- engine.favicon_url, engine.encoding, alternate_urls,
- engine.search_terms_replacement_key, engine.id);
+ engine.logo_url, engine.search_url_post_params,
+ engine.suggest_url_post_params, engine.instant_url_post_params,
+ engine.image_url_post_params, engine.favicon_url, engine.encoding,
+ alternate_urls, engine.search_terms_replacement_key, engine.id);
}
std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
@@ -214,6 +218,7 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
std::string image_url;
std::string new_tab_url;
std::string contextual_search_url;
+ std::string logo_url;
std::string search_url_post_params;
std::string suggest_url_post_params;
std::string instant_url_post_params;
@@ -226,6 +231,7 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
engine.GetString("image_url", &image_url);
engine.GetString("new_tab_url", &new_tab_url);
engine.GetString("contextual_search_url", &contextual_search_url);
+ engine.GetString("logo_url", &logo_url);
engine.GetString("search_url_post_params", &search_url_post_params);
engine.GetString("suggest_url_post_params", &suggest_url_post_params);
engine.GetString("instant_url_post_params", &instant_url_post_params);
@@ -235,7 +241,7 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
&search_terms_replacement_key);
return base::MakeUnique<TemplateURLData>(
name, keyword, search_url, suggest_url, instant_url, image_url,
- new_tab_url, contextual_search_url, search_url_post_params,
+ new_tab_url, contextual_search_url, logo_url, search_url_post_params,
suggest_url_post_params, instant_url_post_params, image_url_post_params,
favicon_url, encoding, *alternate_urls, search_terms_replacement_key,
id);
diff --git a/chromium/components/search_engines/template_url_fetcher.h b/chromium/components/search_engines/template_url_fetcher.h
index c2c05ed4bd5..896c755f027 100644
--- a/chromium/components/search_engines/template_url_fetcher.h
+++ b/chromium/components/search_engines/template_url_fetcher.h
@@ -6,11 +6,11 @@
#define COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_FETCHER_H_
#include <memory>
+#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
#include "base/strings/string16.h"
#include "components/keyed_service/core/keyed_service.h"
diff --git a/chromium/components/search_engines/template_url_parser.cc b/chromium/components/search_engines/template_url_parser.cc
index 9d051056be5..64b1385e05d 100644
--- a/chromium/components/search_engines/template_url_parser.cc
+++ b/chromium/components/search_engines/template_url_parser.cc
@@ -280,8 +280,10 @@ void TemplateURLParsingContext::EndElementImpl(void* ctx, const xmlChar* name) {
break;
}
case TemplateURLParsingContext::ALIAS: {
- context->data_.SetKeyword(context->string_);
- context->has_custom_keyword_ = true;
+ if (!context->string_.empty()) {
+ context->data_.SetKeyword(context->string_);
+ context->has_custom_keyword_ = true;
+ }
break;
}
default:
@@ -303,8 +305,7 @@ void TemplateURLParsingContext::CharactersImpl(void* ctx,
std::unique_ptr<TemplateURL> TemplateURLParsingContext::GetTemplateURL(
const SearchTermsData& search_terms_data) {
// TODO(jcampan): Support engines that use POST; see http://crbug.com/18107
- if (method_ == TemplateURLParsingContext::POST ||
- data_.short_name().empty() || !IsHTTPRef(data_.url()) ||
+ if (method_ == TemplateURLParsingContext::POST || !IsHTTPRef(data_.url()) ||
!IsHTTPRef(data_.suggestions_url))
return nullptr;
if (suggestion_method_ == TemplateURLParsingContext::POST)
@@ -321,6 +322,10 @@ std::unique_ptr<TemplateURL> TemplateURLParsingContext::GetTemplateURL(
if (!has_custom_keyword_)
data_.SetKeyword(TemplateURL::GenerateKeyword(search_url));
+ // If the OSDD omits or has an empty short name, use the keyword.
+ if (data_.short_name().empty())
+ data_.SetShortName(data_.keyword());
+
// Bail if the search URL is empty or if either TemplateURLRef is invalid.
std::unique_ptr<TemplateURL> template_url =
base::MakeUnique<TemplateURL>(data_);
diff --git a/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc b/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
index 08c323e07c3..77e1c585963 100644
--- a/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
+++ b/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
@@ -12,7 +12,9 @@
#include "base/command_line.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
#include "components/google/core/browser/google_switches.h"
#include "components/search_engines/prepopulated_engines.h"
#include "components/search_engines/search_engines_pref_names.h"
@@ -20,6 +22,7 @@
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_data_util.h"
#include "components/search_engines/template_url_service.h"
+#include "components/search_engines/testing_search_terms_data.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -159,9 +162,9 @@ TEST_F(TemplateURLPrepopulateDataTest, ProvidersFromPrefs) {
// Test the optional settings too.
entry->SetString("suggest_url", "http://foo.com/suggest?q={searchTerms}");
entry->SetString("instant_url", "http://foo.com/instant?q={searchTerms}");
- base::ListValue* alternate_urls = new base::ListValue;
+ auto alternate_urls = base::MakeUnique<base::ListValue>();
alternate_urls->AppendString("http://foo.com/alternate?q={searchTerms}");
- entry->Set("alternate_urls", alternate_urls);
+ entry->Set("alternate_urls", std::move(alternate_urls));
entry->SetString("search_terms_replacement_key", "espv");
overrides = base::MakeUnique<base::ListValue>();
overrides->Append(entry->CreateDeepCopy());
@@ -367,3 +370,58 @@ TEST_F(TemplateURLPrepopulateDataTest, GetEngineTypeForAllPrepopulatedEngines) {
TemplateURL(*data).GetEngineType(SearchTermsData()));
}
}
+
+namespace {
+
+void CheckTemplateUrlRefIsCryptographic(const TemplateURLRef& url_ref) {
+ TestingSearchTermsData search_terms_data("https://www.google.com/");
+ if (!url_ref.IsValid(search_terms_data)) {
+ ADD_FAILURE() << url_ref.GetURL();
+ return;
+ }
+
+ // Double parentheses around the string16 constructor to prevent the compiler
+ // from parsing it as a function declaration.
+ TemplateURLRef::SearchTermsArgs search_term_args((base::string16()));
+ GURL url(url_ref.ReplaceSearchTerms(search_term_args, search_terms_data));
+ EXPECT_TRUE(url.is_empty() || url.SchemeIsCryptographic()) << url;
+}
+
+} // namespace
+
+TEST_F(TemplateURLPrepopulateDataTest, HttpsUrls) {
+ // Preexisting search engines that don't use HTTPS URLs.
+ // Don't add new entries to this list!
+ std::set<int> exceptions{
+ 4, 6, 16, 17, 21, 27, 35, 36, 43, 44, 45, 50, 54, 55, 56, 60, 61,
+ 62, 63, 64, 65, 66, 68, 70, 74, 75, 76, 77, 78, 79, 80, 81, 85, 90,
+ };
+ using PrepopulatedEngine = TemplateURLPrepopulateData::PrepopulatedEngine;
+ const std::vector<const PrepopulatedEngine*> all_engines =
+ TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
+ for (const PrepopulatedEngine* engine : all_engines) {
+ std::unique_ptr<TemplateURLData> data =
+ TemplateURLDataFromPrepopulatedEngine(*engine);
+ if (base::ContainsKey(exceptions, data->prepopulate_id))
+ continue;
+
+ GURL logo_url = data->logo_url;
+ EXPECT_TRUE(logo_url.is_empty() || logo_url.SchemeIsCryptographic())
+ << logo_url;
+ GURL favicon_url = data->favicon_url;
+ EXPECT_TRUE(favicon_url.is_empty() || favicon_url.SchemeIsCryptographic())
+ << favicon_url;
+
+ TemplateURL template_url(*data);
+
+ // Intentionally don't check alternate URLs, because those are only used
+ // for matching.
+ CheckTemplateUrlRefIsCryptographic(template_url.url_ref());
+ CheckTemplateUrlRefIsCryptographic(template_url.suggestions_url_ref());
+ CheckTemplateUrlRefIsCryptographic(template_url.instant_url_ref());
+ CheckTemplateUrlRefIsCryptographic(template_url.image_url_ref());
+ CheckTemplateUrlRefIsCryptographic(template_url.new_tab_url_ref());
+ CheckTemplateUrlRefIsCryptographic(
+ template_url.contextual_search_url_ref());
+ }
+}
diff --git a/chromium/components/search_provider_logos/BUILD.gn b/chromium/components/search_provider_logos/BUILD.gn
index 8b3c839a85d..7b9909e15c6 100644
--- a/chromium/components/search_provider_logos/BUILD.gn
+++ b/chromium/components/search_provider_logos/BUILD.gn
@@ -4,6 +4,10 @@
static_library("search_provider_logos") {
sources = [
+ "features.cc",
+ "features.h",
+ "fixed_logo_api.cc",
+ "fixed_logo_api.h",
"google_logo_api.cc",
"google_logo_api.h",
"logo_cache.cc",
@@ -32,6 +36,7 @@ static_library("search_provider_logos") {
source_set("unit_tests") {
testonly = true
sources = [
+ "google_logo_api_unittest.cc",
"logo_cache_unittest.cc",
"logo_tracker_unittest.cc",
]
diff --git a/chromium/components/search_provider_logos/OWNERS b/chromium/components/search_provider_logos/OWNERS
index 7bd15d57a36..db0aa591a2c 100644
--- a/chromium/components/search_provider_logos/OWNERS
+++ b/chromium/components/search_provider_logos/OWNERS
@@ -1 +1,3 @@
-justincohen@chromium.org \ No newline at end of file
+bauerb@chromium.org
+justincohen@chromium.org
+treib@chromium.org
diff --git a/chromium/components/search_provider_logos/features.cc b/chromium/components/search_provider_logos/features.cc
new file mode 100644
index 00000000000..1326c7f639a
--- /dev/null
+++ b/chromium/components/search_provider_logos/features.cc
@@ -0,0 +1,14 @@
+// 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/search_provider_logos/features.h"
+
+namespace search_provider_logos {
+namespace features {
+
+const base::Feature kUseDdljsonApi{"UseDdljsonApi",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+} // namespace features
+} // namespace search_provider_logos
diff --git a/chromium/components/search_provider_logos/features.h b/chromium/components/search_provider_logos/features.h
new file mode 100644
index 00000000000..05c46e5e65c
--- /dev/null
+++ b/chromium/components/search_provider_logos/features.h
@@ -0,0 +1,18 @@
+// 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_SEARCH_PROVIDER_LOGOS_FEATURES_H_
+#define COMPONENTS_SEARCH_PROVIDER_LOGOS_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace search_provider_logos {
+namespace features {
+
+extern const base::Feature kUseDdljsonApi;
+
+} // namespace features
+} // namespace search_provider_logos
+
+#endif // COMPONENTS_SEARCH_PROVIDER_LOGOS_FEATURES_H_
diff --git a/chromium/components/search_provider_logos/fixed_logo_api.cc b/chromium/components/search_provider_logos/fixed_logo_api.cc
new file mode 100644
index 00000000000..f9a5f5e6362
--- /dev/null
+++ b/chromium/components/search_provider_logos/fixed_logo_api.cc
@@ -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.
+
+#include "components/search_provider_logos/fixed_logo_api.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "components/search_provider_logos/logo_common.h"
+#include "url/gurl.h"
+
+namespace search_provider_logos {
+
+std::unique_ptr<EncodedLogo> ParseFixedLogoResponse(
+ std::unique_ptr<std::string> response,
+ base::Time response_time,
+ bool* parsing_failed) {
+ auto logo = base::MakeUnique<EncodedLogo>();
+ logo->encoded_image = base::RefCountedString::TakeString(response.get());
+
+ // If |can_show_after_expiration| is true, the |expiration_time| has little
+ // effect. Set it as far as possible in the future just as an approximation.
+ logo->metadata.expiration_time =
+ response_time + base::TimeDelta::FromMilliseconds(kMaxTimeToLiveMS);
+ logo->metadata.can_show_after_expiration = true;
+
+ *parsing_failed = false;
+ return logo;
+}
+
+GURL UseFixedLogoUrl(const GURL& logo_url, const std::string& fingerprint) {
+ return logo_url;
+}
+
+} // namespace search_provider_logos
diff --git a/chromium/components/search_provider_logos/fixed_logo_api.h b/chromium/components/search_provider_logos/fixed_logo_api.h
new file mode 100644
index 00000000000..7801357502d
--- /dev/null
+++ b/chromium/components/search_provider_logos/fixed_logo_api.h
@@ -0,0 +1,31 @@
+// 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_SEARCH_PROVIDER_LOGOS_FIXED_LOGO_API_H_
+#define COMPONENTS_SEARCH_PROVIDER_LOGOS_FIXED_LOGO_API_H_
+
+#include <memory>
+#include <string>
+
+#include "base/time/time.h"
+
+class GURL;
+
+namespace search_provider_logos {
+
+struct EncodedLogo;
+
+// Implements AppendFingerprintToLogoURL, defined in logo_tracker.h,
+// for static logos.
+GURL UseFixedLogoUrl(const GURL& logo_url, const std::string& fingerprint);
+
+// Implements ParseLogoResponse, defined in logo_tracker.h, for static logos.
+std::unique_ptr<EncodedLogo> ParseFixedLogoResponse(
+ std::unique_ptr<std::string> response,
+ base::Time response_time,
+ bool* parsing_failed);
+
+} // namespace search_provider_logos
+
+#endif // COMPONENTS_SEARCH_PROVIDER_LOGOS_FIXED_LOGO_API_H_
diff --git a/chromium/components/search_provider_logos/google_logo_api.cc b/chromium/components/search_provider_logos/google_logo_api.cc
index f0ab64f4c5e..2750c90df8d 100644
--- a/chromium/components/search_provider_logos/google_logo_api.cc
+++ b/chromium/components/search_provider_logos/google_logo_api.cc
@@ -9,60 +9,84 @@
#include <algorithm>
#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/feature_list.h"
#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/values.h"
+#include "components/search_provider_logos/features.h"
+#include "url/url_constants.h"
namespace search_provider_logos {
+GURL GetGoogleDoodleURL(const GURL& google_base_url) {
+ GURL::Replacements replacements;
+ replacements.SetPathStr(base::FeatureList::IsEnabled(features::kUseDdljsonApi)
+ ? "async/ddljson"
+ : "async/newtab_mobile");
+ return google_base_url.ReplaceComponents(replacements);
+}
+
+AppendQueryparamsToLogoURL GetGoogleAppendQueryparamsCallback(
+ bool gray_background) {
+ if (base::FeatureList::IsEnabled(features::kUseDdljsonApi))
+ return base::Bind(&GoogleNewAppendQueryparamsToLogoURL, gray_background);
+
+ return base::Bind(&GoogleLegacyAppendQueryparamsToLogoURL, gray_background);
+}
+
+ParseLogoResponse GetGoogleParseLogoResponseCallback(const GURL& base_url) {
+ if (base::FeatureList::IsEnabled(features::kUseDdljsonApi))
+ return base::Bind(&GoogleNewParseLogoResponse, base_url);
+
+ return base::Bind(&GoogleLegacyParseLogoResponse);
+}
+
namespace {
const char kResponsePreamble[] = ")]}'";
}
-GURL GoogleAppendQueryparamsToLogoURL(const GURL& logo_url,
- const std::string& fingerprint,
- bool wants_cta,
- bool gray_background) {
+GURL GoogleLegacyAppendQueryparamsToLogoURL(bool gray_background,
+ const GURL& logo_url,
+ const std::string& fingerprint) {
// Note: we can't just use net::AppendQueryParameter() because it escapes
// ":" to "%3A", but the server requires the colon not to be escaped.
// See: http://crbug.com/413845
// TODO(newt): Switch to using net::AppendQueryParameter once it no longer
// escapes ":"
- if (!fingerprint.empty() || wants_cta) {
- std::string query(logo_url.query());
- if (!query.empty())
- query += "&";
-
- query += "async=";
- std::vector<base::StringPiece> params;
- std::string fingerprint_param;
- if (!fingerprint.empty()) {
- fingerprint_param = "es_dfp:" + fingerprint;
- params.push_back(fingerprint_param);
- }
+ std::string query(logo_url.query());
+ if (!query.empty())
+ query += "&";
- if (wants_cta)
- params.push_back("cta:1");
+ query += "async=";
+ std::vector<base::StringPiece> params;
+ std::string fingerprint_param;
+ if (!fingerprint.empty()) {
+ fingerprint_param = "es_dfp:" + fingerprint;
+ params.push_back(fingerprint_param);
+ }
- if (gray_background) {
- params.push_back("transp:1");
- params.push_back("graybg:1");
- }
+ params.push_back("cta:1");
- query += base::JoinString(params, ",");
- GURL::Replacements replacements;
- replacements.SetQueryStr(query);
- return logo_url.ReplaceComponents(replacements);
+ if (gray_background) {
+ params.push_back("transp:1");
+ params.push_back("graybg:1");
}
- return logo_url;
+ query += base::JoinString(params, ",");
+ GURL::Replacements replacements;
+ replacements.SetQueryStr(query);
+ return logo_url.ReplaceComponents(replacements);
}
-std::unique_ptr<EncodedLogo> GoogleParseLogoResponse(
- const std::unique_ptr<std::string>& response,
+std::unique_ptr<EncodedLogo> GoogleLegacyParseLogoResponse(
+ std::unique_ptr<std::string> response,
base::Time response_time,
bool* parsing_failed) {
// Google doodles are sent as JSON with a prefix. Example:
@@ -153,4 +177,158 @@ std::unique_ptr<EncodedLogo> GoogleParseLogoResponse(
return logo;
}
+GURL GoogleNewAppendQueryparamsToLogoURL(bool gray_background,
+ const GURL& logo_url,
+ const std::string& fingerprint) {
+ // Note: we can't just use net::AppendQueryParameter() because it escapes
+ // ":" to "%3A", but the server requires the colon not to be escaped.
+ // See: http://crbug.com/413845
+
+ std::string query(logo_url.query());
+ if (!query.empty())
+ query += "&";
+
+ query += "async=";
+
+ std::vector<base::StringPiece> params;
+ params.push_back("ntp:1");
+ if (gray_background) {
+ params.push_back("graybg:1");
+ }
+ if (!fingerprint.empty()) {
+ params.push_back("es_dfp:" + fingerprint);
+ }
+ query += base::JoinString(params, ",");
+
+ GURL::Replacements replacements;
+ replacements.SetQueryStr(query);
+ return logo_url.ReplaceComponents(replacements);
+}
+
+namespace {
+
+GURL ParseUrl(const base::DictionaryValue& parent_dict,
+ const std::string& key,
+ const GURL& base_url) {
+ std::string url_str;
+ if (!parent_dict.GetString(key, &url_str) || url_str.empty()) {
+ return GURL();
+ }
+ return base_url.Resolve(url_str);
+}
+
+} // namespace
+
+std::unique_ptr<EncodedLogo> GoogleNewParseLogoResponse(
+ const GURL& base_url,
+ std::unique_ptr<std::string> response,
+ base::Time response_time,
+ bool* parsing_failed) {
+ // The response may start with )]}'. Ignore this.
+ base::StringPiece response_sp(*response);
+ if (response_sp.starts_with(kResponsePreamble))
+ response_sp.remove_prefix(strlen(kResponsePreamble));
+
+ // Default parsing failure to be true.
+ *parsing_failed = true;
+
+ int error_code;
+ std::string error_string;
+ int error_line;
+ int error_col;
+ std::unique_ptr<base::Value> value = base::JSONReader::ReadAndReturnError(
+ response_sp, 0, &error_code, &error_string, &error_line, &error_col);
+ if (!value) {
+ LOG(WARNING) << error_string << " at " << error_line << ":" << error_col;
+ return nullptr;
+ }
+
+ std::unique_ptr<base::DictionaryValue> config =
+ base::DictionaryValue::From(std::move(value));
+ if (!config)
+ return nullptr;
+
+ const base::DictionaryValue* ddljson = nullptr;
+ if (!config->GetDictionary("ddljson", &ddljson))
+ return nullptr;
+
+ // If there is no logo today, the "ddljson" dictionary will be empty.
+ if (ddljson->empty()) {
+ *parsing_failed = false;
+ return nullptr;
+ }
+
+ auto logo = base::MakeUnique<EncodedLogo>();
+
+ // Check if the main image is animated.
+ bool is_animated = false;
+ const base::DictionaryValue* image = nullptr;
+ if (ddljson->GetDictionary("large_image", &image)) {
+ image->GetBoolean("is_animated", &is_animated);
+
+ // If animated, get the URL for the animated image.
+ if (is_animated) {
+ GURL animated_url = ParseUrl(*image, "url", base_url);
+ if (!animated_url.is_valid())
+ return nullptr;
+ logo->metadata.animated_url = animated_url.spec();
+ }
+ }
+
+ // Data is optional, since we may be revalidating a cached logo.
+ // If this is an animated doodle, get the CTA image data.
+ std::string encoded_image_data;
+ if (ddljson->GetString(is_animated ? "cta_data_uri" : "data_uri",
+ &encoded_image_data)) {
+ GURL encoded_image_uri(encoded_image_data);
+ if (!encoded_image_uri.is_valid() ||
+ !encoded_image_uri.SchemeIs(url::kDataScheme)) {
+ return nullptr;
+ }
+ std::string content = encoded_image_uri.GetContent();
+ // The content should look like this: "image/png;base64,aaa..." (where
+ // "aaa..." is the base64-encoded image data).
+ size_t mime_type_end = content.find_first_of(';');
+ if (mime_type_end == std::string::npos)
+ return nullptr;
+ logo->metadata.mime_type = content.substr(0, mime_type_end);
+
+ size_t base64_begin = mime_type_end + 1;
+ size_t base64_end = content.find_first_of(',', base64_begin);
+ if (base64_end == std::string::npos)
+ return nullptr;
+ base::StringPiece base64(content.begin() + base64_begin,
+ content.begin() + base64_end);
+ if (base64 != "base64")
+ return nullptr;
+
+ size_t data_begin = base64_end + 1;
+ base::StringPiece data(content.begin() + data_begin, content.end());
+ logo->encoded_image = base::MakeRefCounted<base::RefCountedString>();
+ if (!base::Base64Decode(data, &logo->encoded_image->data()))
+ return nullptr;
+ }
+
+ logo->metadata.on_click_url =
+ ParseUrl(*ddljson, "target_url", base_url).spec();
+ ddljson->GetString("alt_text", &logo->metadata.alt_text);
+
+ ddljson->GetString("fingerprint", &logo->metadata.fingerprint);
+
+ base::TimeDelta time_to_live;
+ // The JSON doesn't guarantee the number to fit into an int.
+ double ttl_ms = 0; // Expires immediately if the parameter is missing.
+ if (ddljson->GetDouble("time_to_live_ms", &ttl_ms)) {
+ time_to_live = base::TimeDelta::FromMillisecondsD(ttl_ms);
+ logo->metadata.can_show_after_expiration = false;
+ } else {
+ time_to_live = base::TimeDelta::FromMilliseconds(kMaxTimeToLiveMS);
+ logo->metadata.can_show_after_expiration = true;
+ }
+ logo->metadata.expiration_time = response_time + time_to_live;
+
+ *parsing_failed = false;
+ return logo;
+}
+
} // namespace search_provider_logos
diff --git a/chromium/components/search_provider_logos/google_logo_api.h b/chromium/components/search_provider_logos/google_logo_api.h
index d1d85b9ba6f..3c3929eba4e 100644
--- a/chromium/components/search_provider_logos/google_logo_api.h
+++ b/chromium/components/search_provider_logos/google_logo_api.h
@@ -14,16 +14,41 @@
namespace search_provider_logos {
-// Implements AppendFingerprintToLogoURL, defined in logo_tracker.h, for Google
-// doodles.
-GURL GoogleAppendQueryparamsToLogoURL(const GURL& logo_url,
- const std::string& fingerprint,
- bool wants_cta,
- bool gray_background);
-
-// Implements ParseLogoResponse, defined in logo_tracker.h, for Google doodles.
-std::unique_ptr<EncodedLogo> GoogleParseLogoResponse(
- const std::unique_ptr<std::string>& response,
+// Returns the URL where the doodle can be downloaded, e.g.
+// https://www.google.com/async/newtab_mobile. This depends on the user's
+// Google domain.
+GURL GetGoogleDoodleURL(const GURL& google_base_url);
+
+// These return the correct callbacks for appending queryparams and parsing the
+// response ("Legacy" or "New"), based on the value of features::kUseDdljsonApi.
+AppendQueryparamsToLogoURL GetGoogleAppendQueryparamsCallback(
+ bool gray_background);
+ParseLogoResponse GetGoogleParseLogoResponseCallback(const GURL& base_url);
+
+// Implements AppendQueryparamsToLogoURL, defined in logo_tracker.h, for Google
+// doodles (old newtab_mobile API).
+GURL GoogleLegacyAppendQueryparamsToLogoURL(bool gray_background,
+ const GURL& logo_url,
+ const std::string& fingerprint);
+
+// Implements ParseLogoResponse, defined in logo_tracker.h, for Google doodles
+// (old newtab_mobile API).
+std::unique_ptr<EncodedLogo> GoogleLegacyParseLogoResponse(
+ std::unique_ptr<std::string> response,
+ base::Time response_time,
+ bool* parsing_failed);
+
+// Implements AppendQueryparamsToLogoURL, defined in logo_tracker.h, for Google
+// doodles (new ddljson API).
+GURL GoogleNewAppendQueryparamsToLogoURL(bool gray_background,
+ const GURL& logo_url,
+ const std::string& fingerprint);
+
+// Implements ParseLogoResponse, defined in logo_tracker.h, for Google doodles
+// (new ddljson API).
+std::unique_ptr<EncodedLogo> GoogleNewParseLogoResponse(
+ const GURL& base_url,
+ std::unique_ptr<std::string> response,
base::Time response_time,
bool* parsing_failed);
diff --git a/chromium/components/search_provider_logos/google_logo_api_unittest.cc b/chromium/components/search_provider_logos/google_logo_api_unittest.cc
new file mode 100644
index 00000000000..221ee31afe1
--- /dev/null
+++ b/chromium/components/search_provider_logos/google_logo_api_unittest.cc
@@ -0,0 +1,100 @@
+// 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/search_provider_logos/google_logo_api.h"
+
+#include <memory>
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using testing::Eq;
+
+namespace search_provider_logos {
+
+TEST(GoogleNewLogoApiTest, ResolvesRelativeUrl) {
+ const GURL base_url("https://base.doo/");
+ const std::string json = R"json()]}'
+{
+ "ddljson": {
+ "target_url": "/target"
+ }
+})json";
+
+ bool failed = false;
+ std::unique_ptr<EncodedLogo> logo = GoogleNewParseLogoResponse(
+ base_url, base::MakeUnique<std::string>(json), base::Time(), &failed);
+
+ ASSERT_FALSE(failed);
+ ASSERT_TRUE(logo);
+ EXPECT_EQ("https://base.doo/target", logo->metadata.on_click_url);
+}
+
+TEST(GoogleNewLogoApiTest, DoesNotResolveAbsoluteUrl) {
+ const GURL base_url("https://base.doo/");
+ const std::string json = R"json()]}'
+{
+ "ddljson": {
+ "target_url": "https://www.doodle.com/target"
+ }
+})json";
+
+ bool failed = false;
+ std::unique_ptr<EncodedLogo> logo = GoogleNewParseLogoResponse(
+ base_url, base::MakeUnique<std::string>(json), base::Time(), &failed);
+
+ ASSERT_FALSE(failed);
+ ASSERT_TRUE(logo);
+ EXPECT_EQ("https://www.doodle.com/target", logo->metadata.on_click_url);
+}
+
+TEST(GoogleNewLogoApiTest, ParsesStaticImage) {
+ const GURL base_url("https://base.doo/");
+ // Note: The base64 encoding of "abc" is "YWJj".
+ const std::string json = R"json()]}'
+{
+ "ddljson": {
+ "target_url": "/target",
+ "data_uri": "data:image/png;base64,YWJj"
+ }
+})json";
+
+ bool failed = false;
+ std::unique_ptr<EncodedLogo> logo = GoogleNewParseLogoResponse(
+ base_url, base::MakeUnique<std::string>(json), base::Time(), &failed);
+
+ ASSERT_FALSE(failed);
+ ASSERT_TRUE(logo);
+ EXPECT_EQ("abc", logo->encoded_image->data());
+}
+
+TEST(GoogleNewLogoApiTest, ParsesAnimatedImage) {
+ const GURL base_url("https://base.doo/");
+ // Note: The base64 encoding of "abc" is "YWJj".
+ const std::string json = R"json()]}'
+{
+ "ddljson": {
+ "target_url": "/target",
+ "large_image": {
+ "is_animated": true,
+ "url": "https://www.doodle.com/image.gif"
+ },
+ "cta_data_uri": "data:image/png;base64,YWJj"
+ }
+})json";
+
+ bool failed = false;
+ std::unique_ptr<EncodedLogo> logo = GoogleNewParseLogoResponse(
+ base_url, base::MakeUnique<std::string>(json), base::Time(), &failed);
+
+ ASSERT_FALSE(failed);
+ ASSERT_TRUE(logo);
+ EXPECT_EQ("https://www.doodle.com/image.gif", logo->metadata.animated_url);
+ EXPECT_EQ("abc", logo->encoded_image->data());
+}
+
+} // namespace search_provider_logos
diff --git a/chromium/components/search_provider_logos/logo_cache.cc b/chromium/components/search_provider_logos/logo_cache.cc
index 70fbe7b939d..3c4b2728eb9 100644
--- a/chromium/components/search_provider_logos/logo_cache.cc
+++ b/chromium/components/search_provider_logos/logo_cache.cc
@@ -57,16 +57,16 @@ LogoCache::LogoCache(const base::FilePath& cache_directory)
: cache_directory_(cache_directory),
metadata_is_valid_(false) {
// The LogoCache can be constructed on any thread, as long as it's used
- // on a single thread after construction.
- thread_checker_.DetachFromThread();
+ // on a single sequence after construction.
+ DETACH_FROM_SEQUENCE(sequence_checker_);
}
LogoCache::~LogoCache() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void LogoCache::UpdateCachedLogoMetadata(const LogoMetadata& metadata) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(metadata_);
DCHECK_EQ(metadata_->fingerprint, metadata.fingerprint);
@@ -75,24 +75,24 @@ void LogoCache::UpdateCachedLogoMetadata(const LogoMetadata& metadata) {
}
const LogoMetadata* LogoCache::GetCachedLogoMetadata() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ReadMetadataIfNeeded();
return metadata_.get();
}
void LogoCache::SetCachedLogo(const EncodedLogo* logo) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<LogoMetadata> metadata;
if (logo) {
- metadata.reset(new LogoMetadata(logo->metadata));
+ metadata = base::MakeUnique<LogoMetadata>(logo->metadata);
logo_num_bytes_ = static_cast<int>(logo->encoded_image->size());
}
UpdateMetadata(std::move(metadata));
- WriteLogo(logo ? logo->encoded_image : NULL);
+ WriteLogo(logo ? logo->encoded_image : nullptr);
}
std::unique_ptr<EncodedLogo> LogoCache::GetCachedLogo() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ReadMetadataIfNeeded();
if (!metadata_)
diff --git a/chromium/components/search_provider_logos/logo_cache.h b/chromium/components/search_provider_logos/logo_cache.h
index e4a19af3e90..da421c4200e 100644
--- a/chromium/components/search_provider_logos/logo_cache.h
+++ b/chromium/components/search_provider_logos/logo_cache.h
@@ -43,14 +43,14 @@ class LogoCache {
// Updates the metadata for the cached logo.
virtual void UpdateCachedLogoMetadata(const LogoMetadata& metadata);
- // Returns metadata for the cached logo, or NULL if logo is cached.
+ // Returns metadata for the cached logo, or null if logo is cached.
virtual const LogoMetadata* GetCachedLogoMetadata();
- // Sets the cached logo and metadata. |logo| may be NULL, in which case the
+ // Sets the cached logo and metadata. |logo| may be null, in which case the
// cached logo and metadata will be cleared.
virtual void SetCachedLogo(const EncodedLogo* logo);
- // Returns the cached logo, or NULL if no logo is cached or the cached logo is
+ // Returns the cached logo, or null if no logo is cached or the cached logo is
// corrupt.
virtual std::unique_ptr<EncodedLogo> GetCachedLogo();
@@ -62,7 +62,7 @@ class LogoCache {
FRIEND_TEST_ALL_PREFIXES(LogoCacheTest, RetrieveCorruptMetadata);
FRIEND_TEST_ALL_PREFIXES(LogoCacheTest, RetrieveCorruptLogo);
- // Converts string |str| to a LogoMetadata object and returns it. Returns NULL
+ // Converts string |str| to a LogoMetadata object and returns it. Returns null
// if |str| cannot be converted.
static std::unique_ptr<LogoMetadata> LogoMetadataFromString(
const std::string& str,
@@ -84,7 +84,7 @@ class LogoCache {
// If the cached logo's metadata isn't available in memory (i.e.
// |metadata_is_valid_| is false), reads it from disk and stores it in
- // |metadata_|. If no logo is cached, |metadata_| will be updated to NULL.
+ // |metadata_|. If no logo is cached, |metadata_| will be updated to null.
void ReadMetadataIfNeeded();
// Writes the metadata for the cached logo to disk.
@@ -104,9 +104,9 @@ class LogoCache {
// The directory in which the cached logo and metadata will be saved.
base::FilePath cache_directory_;
- // The metadata describing the cached logo, or NULL if no logo is cached. This
+ // The metadata describing the cached logo, or null if no logo is cached. This
// value is meaningful iff |metadata_is_valid_| is true; otherwise, the
- // metadata must be read from file and |metadata_| will be NULL.
+ // metadata must be read from file and |metadata_| will be null.
// Note: Once read from file, metadata will be stored in memory indefinitely.
std::unique_ptr<LogoMetadata> metadata_;
bool metadata_is_valid_;
@@ -116,8 +116,8 @@ class LogoCache {
// is complete and corresponds to the current metadata file.
int logo_num_bytes_;
- // Ensure LogoCache is only used on a single thread.
- base::ThreadChecker thread_checker_;
+ // Ensure LogoCache is only used sequentially.
+ SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(LogoCache);
};
diff --git a/chromium/components/search_provider_logos/logo_cache_unittest.cc b/chromium/components/search_provider_logos/logo_cache_unittest.cc
index 0de7e3897d3..2e0ca57f949 100644
--- a/chromium/components/search_provider_logos/logo_cache_unittest.cc
+++ b/chromium/components/search_provider_logos/logo_cache_unittest.cc
@@ -13,6 +13,7 @@
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -55,17 +56,17 @@ base::RefCountedString* CreateExampleImage(size_t num_bytes) {
return encoded_image_str;
}
-EncodedLogo GetExampleLogo() {
- EncodedLogo logo;
- logo.encoded_image = CreateExampleImage(837);
- logo.metadata = GetExampleMetadata();
+std::unique_ptr<EncodedLogo> GetExampleLogo() {
+ auto logo = base::MakeUnique<EncodedLogo>();
+ logo->encoded_image = CreateExampleImage(837);
+ logo->metadata = GetExampleMetadata();
return logo;
}
-EncodedLogo GetExampleLogo2() {
- EncodedLogo logo;
- logo.encoded_image = CreateExampleImage(345);
- logo.metadata = GetExampleMetadata2();
+std::unique_ptr<EncodedLogo> GetExampleLogo2() {
+ auto logo = base::MakeUnique<EncodedLogo>();
+ logo->encoded_image = CreateExampleImage(345);
+ logo->metadata = GetExampleMetadata2();
return logo;
}
@@ -106,27 +107,27 @@ class LogoCacheTest : public ::testing::Test {
}
void InitCache() {
- cache_.reset(new LogoCache(
- cache_parent_dir_.GetPath().Append(FILE_PATH_LITERAL("cache"))));
+ cache_ = base::MakeUnique<LogoCache>(
+ cache_parent_dir_.GetPath().Append(FILE_PATH_LITERAL("cache")));
}
void ExpectMetadata(const LogoMetadata* expected_metadata) {
const LogoMetadata* retrieved_metadata = cache_->GetCachedLogoMetadata();
if (expected_metadata) {
- ASSERT_TRUE(retrieved_metadata != NULL);
+ ASSERT_TRUE(retrieved_metadata);
ExpectMetadataEqual(*expected_metadata, *retrieved_metadata);
} else {
- ASSERT_TRUE(retrieved_metadata == NULL);
+ ASSERT_FALSE(retrieved_metadata);
}
}
void ExpectLogo(const EncodedLogo* expected_logo) {
std::unique_ptr<EncodedLogo> retrieved_logo(cache_->GetCachedLogo());
if (expected_logo) {
- ASSERT_TRUE(retrieved_logo.get() != NULL);
+ ASSERT_TRUE(retrieved_logo.get());
ExpectLogosEqual(*expected_logo, *retrieved_logo);
} else {
- ASSERT_TRUE(retrieved_logo.get() == NULL);
+ ASSERT_FALSE(retrieved_logo.get());
}
}
@@ -157,7 +158,7 @@ TEST(LogoCacheSerializationTest, DeserializeCorruptMetadata) {
int logo_num_bytes = 33;
std::unique_ptr<LogoMetadata> metadata =
LogoCache::LogoMetadataFromString("", &logo_num_bytes);
- ASSERT_TRUE(metadata.get() == NULL);
+ ASSERT_FALSE(metadata);
LogoMetadata example_metadata = GetExampleMetadata2();
std::string corrupt_str;
@@ -165,17 +166,17 @@ TEST(LogoCacheSerializationTest, DeserializeCorruptMetadata) {
example_metadata, logo_num_bytes, &corrupt_str);
corrupt_str.append("@");
metadata = LogoCache::LogoMetadataFromString(corrupt_str, &logo_num_bytes);
- ASSERT_TRUE(metadata.get() == NULL);
+ ASSERT_FALSE(metadata);
}
TEST_F(LogoCacheTest, StoreAndRetrieveMetadata) {
// Expect no metadata at first.
- ExpectMetadata(NULL);
+ ExpectMetadata(nullptr);
// Set initial metadata.
- EncodedLogo logo = GetExampleLogo();
- LogoMetadata& metadata = logo.metadata;
- cache_->SetCachedLogo(&logo);
+ std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
+ LogoMetadata& metadata = logo->metadata;
+ cache_->SetCachedLogo(logo.get());
ExpectMetadata(&metadata);
// Update metadata.
@@ -194,42 +195,42 @@ TEST_F(LogoCacheTest, StoreAndRetrieveMetadata) {
TEST_F(LogoCacheTest, StoreAndRetrieveLogo) {
// Expect no metadata at first.
- ExpectLogo(NULL);
+ ExpectLogo(nullptr);
// Set initial logo.
- EncodedLogo logo = GetExampleLogo();
- cache_->SetCachedLogo(&logo);
- ExpectLogo(&logo);
+ std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
+ cache_->SetCachedLogo(logo.get());
+ ExpectLogo(logo.get());
- // Update logo to NULL.
- cache_->SetCachedLogo(NULL);
- ExpectLogo(NULL);
+ // Update logo to null.
+ cache_->SetCachedLogo(nullptr);
+ ExpectLogo(nullptr);
// Read logo back from disk.
SimulateRestart();
- ExpectLogo(NULL);
+ ExpectLogo(nullptr);
// Update logo.
logo = GetExampleLogo2();
- cache_->SetCachedLogo(&logo);
- ExpectLogo(&logo);
+ cache_->SetCachedLogo(logo.get());
+ ExpectLogo(logo.get());
// Read logo back from disk.
SimulateRestart();
- ExpectLogo(&logo);
+ ExpectLogo(logo.get());
}
TEST_F(LogoCacheTest, RetrieveCorruptMetadata) {
// Set initial logo.
- EncodedLogo logo = GetExampleLogo2();
- cache_->SetCachedLogo(&logo);
- ExpectLogo(&logo);
+ std::unique_ptr<EncodedLogo> logo = GetExampleLogo2();
+ cache_->SetCachedLogo(logo.get());
+ ExpectLogo(logo.get());
- // Corrupt metadata and expect NULL for both logo and metadata.
+ // Corrupt metadata and expect null for both logo and metadata.
SimulateRestart();
ShortenFile(cache_->GetMetadataPath());
- ExpectMetadata(NULL);
- ExpectLogo(NULL);
+ ExpectMetadata(nullptr);
+ ExpectLogo(nullptr);
// Ensure corrupt cache files are deleted.
EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
@@ -238,16 +239,17 @@ TEST_F(LogoCacheTest, RetrieveCorruptMetadata) {
TEST_F(LogoCacheTest, RetrieveCorruptLogo) {
// Set initial logo.
- EncodedLogo logo = GetExampleLogo();
- cache_->SetCachedLogo(&logo);
- ExpectLogo(&logo);
+ std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
+ cache_->SetCachedLogo(logo.get());
+ ExpectLogo(logo.get());
- // Corrupt logo and expect NULL.
+ // Corrupt logo and expect nullptr.
SimulateRestart();
ShortenFile(cache_->GetLogoPath());
- ExpectLogo(NULL);
- // Once the logo is noticed to be NULL, the metadata should also be cleared.
- ExpectMetadata(NULL);
+ ExpectLogo(nullptr);
+
+ // Once the logo is noticed to be null, the metadata should also be cleared.
+ ExpectMetadata(nullptr);
// Ensure corrupt cache files are deleted.
EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
diff --git a/chromium/components/search_provider_logos/logo_common.h b/chromium/components/search_provider_logos/logo_common.h
index ce94005f904..fbb3a9c64a0 100644
--- a/chromium/components/search_provider_logos/logo_common.h
+++ b/chromium/components/search_provider_logos/logo_common.h
@@ -7,13 +7,17 @@
#include <stdint.h>
+#include <memory>
#include <string>
+#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_memory.h"
#include "base/time/time.h"
#include "third_party/skia/include/core/SkBitmap.h"
+class GURL;
+
namespace search_provider_logos {
// The maximum number of milliseconds that a logo can be cached.
@@ -73,6 +77,18 @@ struct Logo {
LogoMetadata metadata;
};
+// Parses the response from the server and returns it as an EncodedLogo. Returns
+// null if the response is invalid.
+using ParseLogoResponse = base::Callback<std::unique_ptr<EncodedLogo>(
+ std::unique_ptr<std::string> response,
+ base::Time response_time,
+ bool* parsing_failed)>;
+
+// Encodes the fingerprint of the cached logo in the logo URL. This enables the
+// server to verify whether the cached logo is up to date.
+using AppendQueryparamsToLogoURL =
+ base::Callback<GURL(const GURL& logo_url, const std::string& fingerprint)>;
+
} // namespace search_provider_logos
#endif // COMPONENTS_SEARCH_PROVIDER_LOGOS_LOGO_COMMON_H_
diff --git a/chromium/components/search_provider_logos/logo_tracker.cc b/chromium/components/search_provider_logos/logo_tracker.cc
index a2fc7f9e9e4..021e52e2ba9 100644
--- a/chromium/components/search_provider_logos/logo_tracker.cc
+++ b/chromium/components/search_provider_logos/logo_tracker.cc
@@ -7,10 +7,12 @@
#include <algorithm>
#include <utility>
+#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram_macros.h"
#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
@@ -62,41 +64,32 @@ std::unique_ptr<EncodedLogo> GetLogoFromCacheOnFileThread(LogoCache* logo_cache,
return logo_cache->GetCachedLogo();
}
-void DeleteLogoCacheOnFileThread(LogoCache* logo_cache) {
- delete logo_cache;
-}
-
} // namespace
LogoTracker::LogoTracker(
base::FilePath cached_logo_directory,
- scoped_refptr<base::SequencedTaskRunner> file_task_runner,
- scoped_refptr<base::TaskRunner> background_task_runner,
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
std::unique_ptr<LogoDelegate> delegate)
: is_idle_(true),
is_cached_logo_valid_(false),
logo_delegate_(std::move(delegate)),
- logo_cache_(new LogoCache(cached_logo_directory)),
- clock_(new base::DefaultClock()),
- file_task_runner_(file_task_runner),
- background_task_runner_(background_task_runner),
+ cache_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+ logo_cache_(new LogoCache(cached_logo_directory),
+ base::OnTaskRunnerDeleter(cache_task_runner_)),
+ clock_(base::MakeUnique<base::DefaultClock>()),
request_context_getter_(request_context_getter),
weak_ptr_factory_(this) {}
LogoTracker::~LogoTracker() {
ReturnToIdle(kDownloadOutcomeNotTracked);
- file_task_runner_->PostTask(
- FROM_HERE, base::Bind(&DeleteLogoCacheOnFileThread, logo_cache_));
- logo_cache_ = NULL;
}
void LogoTracker::SetServerAPI(
const GURL& logo_url,
const ParseLogoResponse& parse_logo_response_func,
- const AppendQueryparamsToLogoURL& append_queryparams_func,
- bool wants_cta,
- bool gray_background) {
+ const AppendQueryparamsToLogoURL& append_queryparams_func) {
if (logo_url == logo_url_)
return;
@@ -105,8 +98,6 @@ void LogoTracker::SetServerAPI(
logo_url_ = logo_url;
parse_logo_response_func_ = parse_logo_response_func;
append_queryparams_func_ = append_queryparams_func;
- wants_cta_ = wants_cta;
- gray_background_ = gray_background;
}
void LogoTracker::GetLogo(LogoObserver* observer) {
@@ -116,11 +107,9 @@ void LogoTracker::GetLogo(LogoObserver* observer) {
if (is_idle_) {
is_idle_ = false;
base::PostTaskAndReplyWithResult(
- file_task_runner_.get(),
- FROM_HERE,
+ cache_task_runner_.get(), FROM_HERE,
base::Bind(&GetLogoFromCacheOnFileThread,
- logo_cache_,
- logo_url_,
+ base::Unretained(logo_cache_.get()), logo_url_,
clock_->Now()),
base::Bind(&LogoTracker::OnCachedLogoRead,
weak_ptr_factory_.GetWeakPtr()));
@@ -135,9 +124,9 @@ void LogoTracker::RemoveObserver(LogoObserver* observer) {
void LogoTracker::SetLogoCacheForTests(std::unique_ptr<LogoCache> cache) {
DCHECK(cache);
- file_task_runner_->PostTask(
- FROM_HERE, base::Bind(&DeleteLogoCacheOnFileThread, logo_cache_));
- logo_cache_ = cache.release();
+ // Call reset() and release() to keep the deleter of the |logo_cache_| member
+ // and run it on the old value.
+ logo_cache_.reset(cache.release());
}
void LogoTracker::SetClockForTests(std::unique_ptr<base::Clock> clock) {
@@ -195,18 +184,16 @@ void LogoTracker::OnCachedLogoAvailable(const LogoMetadata& metadata,
}
void LogoTracker::SetCachedLogo(std::unique_ptr<EncodedLogo> logo) {
- file_task_runner_->PostTask(
+ cache_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&LogoCache::SetCachedLogo,
- base::Unretained(logo_cache_),
+ base::Bind(&LogoCache::SetCachedLogo, base::Unretained(logo_cache_.get()),
base::Owned(logo.release())));
}
void LogoTracker::SetCachedMetadata(const LogoMetadata& metadata) {
- file_task_runner_->PostTask(FROM_HERE,
- base::Bind(&LogoCache::UpdateCachedLogoMetadata,
- base::Unretained(logo_cache_),
- metadata));
+ cache_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&LogoCache::UpdateCachedLogoMetadata,
+ base::Unretained(logo_cache_.get()), metadata));
}
void LogoTracker::FetchLogo() {
@@ -223,8 +210,7 @@ void LogoTracker::FetchLogo() {
if (command_line->HasSwitch(switches::kGoogleDoodleUrl)) {
url = GURL(command_line->GetSwitchValueASCII(switches::kGoogleDoodleUrl));
} else {
- url = append_queryparams_func_.Run(logo_url_, fingerprint, wants_cta_,
- gray_background_);
+ url = append_queryparams_func_.Run(logo_url_, fingerprint);
}
net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -367,8 +353,10 @@ void LogoTracker::OnURLFetchComplete(const net::URLFetcher* source) {
bool from_http_cache = source->WasCached();
bool* parsing_failed = new bool(false);
- base::PostTaskAndReplyWithResult(
- background_task_runner_.get(), FROM_HERE,
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE,
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::Bind(parse_logo_response_func_, base::Passed(&response),
response_time, parsing_failed),
base::Bind(&LogoTracker::OnFreshLogoParsed,
diff --git a/chromium/components/search_provider_logos/logo_tracker.h b/chromium/components/search_provider_logos/logo_tracker.h
index 402b6401ecb..7a05504e1d4 100644
--- a/chromium/components/search_provider_logos/logo_tracker.h
+++ b/chromium/components/search_provider_logos/logo_tracker.h
@@ -67,22 +67,6 @@ class LogoDelegate {
base::Callback<void(const SkBitmap&)> image_decoded_callback) = 0;
};
-// Parses the response from the server and returns it as an EncodedLogo. Returns
-// NULL if the response is invalid.
-typedef base::Callback<std::unique_ptr<EncodedLogo>(
- const std::unique_ptr<std::string>& response,
- base::Time response_time,
- bool* parsing_failed)>
- ParseLogoResponse;
-
-// Encodes the fingerprint of the cached logo in the logo URL. This enables the
-// server to verify whether the cached logo is up to date.
-typedef base::Callback<GURL(const GURL& logo_url,
- const std::string& fingerprint,
- bool wants_cta,
- bool gray_background)>
- AppendQueryparamsToLogoURL;
-
// This class provides the logo for a search provider. Logos are downloaded from
// the search provider's logo URL and cached on disk.
//
@@ -97,9 +81,6 @@ class LogoTracker : public net::URLFetcherDelegate {
// |cached_logo_directory| is the directory in which the cached logo and its
// metadata should be saved.
//
- // |file_task_runner| is the SequencedTaskRunner that should be used to run
- // file system operations.
- //
// |background_task_runner| is the TaskRunner that should be used to for
// CPU-intensive background operations.
//
@@ -107,8 +88,6 @@ class LogoTracker : public net::URLFetcherDelegate {
// the logo.
explicit LogoTracker(
base::FilePath cached_logo_directory,
- scoped_refptr<base::SequencedTaskRunner> file_task_runner,
- scoped_refptr<base::TaskRunner> background_task_runner,
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
std::unique_ptr<LogoDelegate> delegate);
@@ -124,17 +103,9 @@ class LogoTracker : public net::URLFetcherDelegate {
// |parse_logo_response_func| is a callback that will be used to parse the
// server's response into a EncodedLogo object. |append_queryparams_func| is a
// callback that will return the URL from which to download the logo.
- // |wants_cta| determines if the url should return a call to action image.
- // |gray_background| determines whether to request a logo with a gray
- // background. The gray will match the NTP background color.
- // Note: |parse_logo_response_func| and |append_queryparams_func| must be
- // suitable for running multiple times, concurrently, and on multiple threads.
- // TODO(ianwen): remove wants_cta from parameter.
void SetServerAPI(const GURL& logo_url,
const ParseLogoResponse& parse_logo_response_func,
- const AppendQueryparamsToLogoURL& append_queryparams_func,
- bool wants_cta,
- bool gray_background);
+ const AppendQueryparamsToLogoURL& append_queryparams_func);
// Retrieves the current search provider's logo from the local cache and/or
// over the network, and registers |observer| to be called when the cached
@@ -221,12 +192,6 @@ class LogoTracker : public net::URLFetcherDelegate {
// action request in the logo URL.
AppendQueryparamsToLogoURL append_queryparams_func_;
- // If |true| request call to action in server API.
- bool wants_cta_;
-
- // If |true| request a doodle with a gray background.
- bool gray_background_;
-
// False if an asynchronous task is currently running.
bool is_idle_;
@@ -252,18 +217,16 @@ class LogoTracker : public net::URLFetcherDelegate {
std::unique_ptr<LogoDelegate> logo_delegate_;
- // The cache used to persist the logo on disk. Used only on the file thread.
- LogoCache* logo_cache_;
+ // The SequencedTaskRunner on which the cache lives.
+ scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
+
+ // The cache used to persist the logo on disk. Used only on a background
+ // SequencedTaskRunner.
+ std::unique_ptr<LogoCache, base::OnTaskRunnerDeleter> logo_cache_;
// Clock used to determine current time. Can be overridden in tests.
std::unique_ptr<base::Clock> clock_;
- // The SequencedTaskRunner on which file system operations will be run.
- scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
-
- // The TaskRunner on which the server's response will be parsed.
- scoped_refptr<base::TaskRunner> background_task_runner_;
-
// The URLRequestContextGetter used for network requests.
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
diff --git a/chromium/components/search_provider_logos/logo_tracker_unittest.cc b/chromium/components/search_provider_logos/logo_tracker_unittest.cc
index 926f485feed..1b634c396cc 100644
--- a/chromium/components/search_provider_logos/logo_tracker_unittest.cc
+++ b/chromium/components/search_provider_logos/logo_tracker_unittest.cc
@@ -21,6 +21,7 @@
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.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/time.h"
@@ -188,7 +189,7 @@ void ExpectLogosEqual(const Logo* expected_logo,
Logo actual_logo;
if (actual_encoded_logo)
actual_logo = DecodeLogo(*actual_encoded_logo);
- ExpectLogosEqual(expected_logo, actual_encoded_logo ? &actual_logo : NULL);
+ ExpectLogosEqual(expected_logo, actual_encoded_logo ? &actual_logo : nullptr);
}
ACTION_P(ExpectLogosEqualAction, expected_logo) {
@@ -234,18 +235,18 @@ class MockLogoCache : public LogoCache {
logo_->metadata = metadata;
}
- virtual const LogoMetadata* GetCachedLogoMetadataInternal() {
+ const LogoMetadata* GetCachedLogoMetadataInternal() {
return metadata_.get();
}
- virtual void SetCachedLogoInternal(const EncodedLogo* logo) {
- logo_.reset(logo ? new EncodedLogo(*logo) : NULL);
- metadata_.reset(logo ? new LogoMetadata(logo->metadata) : NULL);
+ void SetCachedLogoInternal(const EncodedLogo* logo) {
+ logo_ = logo ? base::MakeUnique<EncodedLogo>(*logo) : nullptr;
+ metadata_ = logo ? base::MakeUnique<LogoMetadata>(logo->metadata) : nullptr;
}
std::unique_ptr<EncodedLogo> GetCachedLogo() override {
OnGetCachedLogo();
- return base::WrapUnique(logo_ ? new EncodedLogo(*logo_) : NULL);
+ return logo_ ? base::MakeUnique<EncodedLogo>(*logo_) : nullptr;
}
private:
@@ -274,7 +275,7 @@ class MockLogoObserver : public LogoObserver {
void ExpectFreshLogo(const Logo* expected_fresh_logo) {
Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*this, OnLogoAvailable(_, true)).Times(0);
- EXPECT_CALL(*this, OnLogoAvailable(NULL, true));
+ EXPECT_CALL(*this, OnLogoAvailable(nullptr, true));
EXPECT_CALL(*this, OnLogoAvailable(_, false))
.WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
@@ -314,33 +315,29 @@ class TestLogoDelegate : public LogoDelegate {
class LogoTrackerTest : public ::testing::Test {
protected:
LogoTrackerTest()
- : message_loop_(new base::MessageLoop()),
- logo_url_("https://google.com/doodleoftheday?size=hp"),
+ : logo_url_("https://google.com/doodleoftheday?size=hp"),
test_clock_(new base::SimpleTestClock()),
logo_cache_(new NiceMock<MockLogoCache>()),
- fake_url_fetcher_factory_(NULL) {
+ fake_url_fetcher_factory_(nullptr) {
test_clock_->SetNow(base::Time::FromJsTime(INT64_C(1388686828000)));
logo_tracker_ =
- new LogoTracker(base::FilePath(), base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get(),
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()),
- std::unique_ptr<LogoDelegate>(new TestLogoDelegate()));
- logo_tracker_->SetServerAPI(logo_url_, base::Bind(&GoogleParseLogoResponse),
- base::Bind(&GoogleAppendQueryparamsToLogoURL),
- false,
- false);
- logo_tracker_->SetClockForTests(std::unique_ptr<base::Clock>(test_clock_));
- logo_tracker_->SetLogoCacheForTests(
- std::unique_ptr<LogoCache>(logo_cache_));
+ base::MakeUnique<LogoTracker>(base::FilePath(),
+ new net::TestURLRequestContextGetter(
+ base::ThreadTaskRunnerHandle::Get()),
+ base::MakeUnique<TestLogoDelegate>());
+ logo_tracker_->SetServerAPI(
+ logo_url_, base::Bind(&GoogleLegacyParseLogoResponse),
+ base::Bind(&GoogleLegacyAppendQueryparamsToLogoURL, false));
+ logo_tracker_->SetClockForTests(base::WrapUnique(test_clock_));
+ logo_tracker_->SetLogoCacheForTests(base::WrapUnique(logo_cache_));
}
- virtual void TearDown() {
- // logo_tracker_ owns logo_cache_, which gets destructed on the file thread
- // after logo_tracker_'s destruction. Ensure that logo_cache_ is actually
- // destructed before the test ends to make gmock happy.
- delete logo_tracker_;
- base::RunLoop().RunUntilIdle();
+ void TearDown() override {
+ // |logo_tracker_| owns |logo_cache_|, which gets destroyed on a background
+ // sequence after |logo_tracker_|s destruction. Ensure that |logo_cache_| is
+ // actually destroyed before the test ends to make gmock happy.
+ logo_tracker_.reset();
+ task_environment_.RunUntilIdle();
}
// Returns the response that the server would send for the given logo.
@@ -361,16 +358,16 @@ class LogoTrackerTest : public ::testing::Test {
net::URLRequestStatus::SUCCESS,
net::HttpStatusCode response_code = net::HTTP_OK);
- // Calls logo_tracker_->GetLogo() with listener_ and waits for the
+ // Calls logo_tracker_->GetLogo() with |observer_| and waits for the
// asynchronous response(s).
void GetLogo();
- std::unique_ptr<base::MessageLoop> message_loop_;
+ base::test::ScopedTaskEnvironment task_environment_;
GURL logo_url_;
base::SimpleTestClock* test_clock_;
NiceMock<MockLogoCache>* logo_cache_;
net::FakeURLFetcherFactory fake_url_fetcher_factory_;
- LogoTracker* logo_tracker_;
+ std::unique_ptr<LogoTracker> logo_tracker_;
NiceMock<MockLogoObserver> observer_;
};
@@ -385,8 +382,8 @@ void LogoTrackerTest::SetServerResponse(
const std::string& response,
net::URLRequestStatus::Status request_status,
net::HttpStatusCode response_code) {
- fake_url_fetcher_factory_.SetFakeResponse(
- logo_url_, response, response_code, request_status);
+ SetServerResponseWhenFingerprint(std::string(), response, request_status,
+ response_code);
}
void LogoTrackerTest::SetServerResponseWhenFingerprint(
@@ -395,48 +392,38 @@ void LogoTrackerTest::SetServerResponseWhenFingerprint(
net::URLRequestStatus::Status request_status,
net::HttpStatusCode response_code) {
GURL url_with_fp =
- GoogleAppendQueryparamsToLogoURL(logo_url_, fingerprint, false, false);
+ GoogleLegacyAppendQueryparamsToLogoURL(false, logo_url_, fingerprint);
fake_url_fetcher_factory_.SetFakeResponse(
url_with_fp, response_when_fingerprint, response_code, request_status);
}
void LogoTrackerTest::GetLogo() {
logo_tracker_->GetLogo(&observer_);
- base::RunLoop().RunUntilIdle();
+ task_environment_.RunUntilIdle();
}
// Tests -----------------------------------------------------------------------
-TEST_F(LogoTrackerTest, FingerprintURLHasColon) {
- GURL url_with_fp = GoogleAppendQueryparamsToLogoURL(
- GURL("http://logourl.com/path"), "abc123", false, false);
- EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123", url_with_fp.spec());
-
- url_with_fp = GoogleAppendQueryparamsToLogoURL(
- GURL("http://logourl.com/?a=b"), "cafe0", false, false);
- EXPECT_EQ("http://logourl.com/?a=b&async=es_dfp:cafe0", url_with_fp.spec());
-}
-
TEST_F(LogoTrackerTest, CTAURLHasComma) {
- GURL url_with_fp = GoogleAppendQueryparamsToLogoURL(
- GURL("http://logourl.com/path"), "abc123", true, false);
+ GURL url_with_fp = GoogleLegacyAppendQueryparamsToLogoURL(
+ false, GURL("http://logourl.com/path"), "abc123");
EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123,cta:1",
url_with_fp.spec());
- url_with_fp = GoogleAppendQueryparamsToLogoURL(
- GURL("http://logourl.com/?a=b"), "", true, false);
+ url_with_fp = GoogleLegacyAppendQueryparamsToLogoURL(
+ false, GURL("http://logourl.com/?a=b"), "");
EXPECT_EQ("http://logourl.com/?a=b&async=cta:1", url_with_fp.spec());
}
TEST_F(LogoTrackerTest, CTAGrayBackgroundHasCommas) {
- GURL url_with_fp = GoogleAppendQueryparamsToLogoURL(
- GURL("http://logourl.com/path"), "abc123", true, true);
+ GURL url_with_fp = GoogleLegacyAppendQueryparamsToLogoURL(
+ true, GURL("http://logourl.com/path"), "abc123");
EXPECT_EQ(
"http://logourl.com/path?async=es_dfp:abc123,cta:1,transp:1,graybg:1",
url_with_fp.spec());
- url_with_fp = GoogleAppendQueryparamsToLogoURL(
- GURL("http://logourl.com/?a=b"), "", true, true);
+ url_with_fp = GoogleLegacyAppendQueryparamsToLogoURL(
+ true, GURL("http://logourl.com/?a=b"), "");
EXPECT_EQ("http://logourl.com/?a=b&async=cta:1,transp:1,graybg:1",
url_with_fp.spec());
}
@@ -452,18 +439,18 @@ TEST_F(LogoTrackerTest, DownloadAndCacheLogo) {
TEST_F(LogoTrackerTest, EmptyCacheAndFailedDownload) {
EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
- EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+ EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
SetServerResponse("server is borked");
- observer_.ExpectCachedLogo(NULL);
+ observer_.ExpectCachedLogo(nullptr);
GetLogo();
SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
- observer_.ExpectCachedLogo(NULL);
+ observer_.ExpectCachedLogo(nullptr);
GetLogo();
SetServerResponse("", net::URLRequestStatus::SUCCESS, net::HTTP_BAD_GATEWAY);
- observer_.ExpectCachedLogo(NULL);
+ observer_.ExpectCachedLogo(nullptr);
GetLogo();
}
@@ -517,7 +504,7 @@ TEST_F(LogoTrackerTest, ValidateCachedLogo) {
observer_.ExpectCachedLogo(&cached_logo);
GetLogo();
- EXPECT_TRUE(logo_cache_->GetCachedLogoMetadata() != NULL);
+ ASSERT_TRUE(logo_cache_->GetCachedLogoMetadata());
EXPECT_EQ(fresh_logo.metadata.expiration_time,
logo_cache_->GetCachedLogoMetadata()->expiration_time);
@@ -582,10 +569,10 @@ TEST_F(LogoTrackerTest, InvalidateCachedLogo) {
SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
")]}' {\"update\":{}}");
- logo_cache_->ExpectSetCachedLogo(NULL);
+ logo_cache_->ExpectSetCachedLogo(nullptr);
EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
- observer_.ExpectCachedAndFreshLogos(&cached_logo, NULL);
+ observer_.ExpectCachedAndFreshLogos(&cached_logo, nullptr);
GetLogo();
}
@@ -598,9 +585,9 @@ TEST_F(LogoTrackerTest, DeleteCachedLogoFromOldUrl) {
EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
- EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+ EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
- observer_.ExpectCachedLogo(NULL);
+ observer_.ExpectCachedLogo(nullptr);
GetLogo();
}
@@ -613,7 +600,7 @@ TEST_F(LogoTrackerTest, LogoWithTTLCannotBeShownAfterExpiration) {
const LogoMetadata* cached_metadata =
logo_cache_->GetCachedLogoMetadata();
- EXPECT_TRUE(cached_metadata != NULL);
+ ASSERT_TRUE(cached_metadata);
EXPECT_FALSE(cached_metadata->can_show_after_expiration);
EXPECT_EQ(test_clock_->Now() + time_to_live,
cached_metadata->expiration_time);
@@ -627,7 +614,7 @@ TEST_F(LogoTrackerTest, LogoWithoutTTLCanBeShownAfterExpiration) {
const LogoMetadata* cached_metadata =
logo_cache_->GetCachedLogoMetadata();
- EXPECT_TRUE(cached_metadata != NULL);
+ ASSERT_TRUE(cached_metadata);
EXPECT_TRUE(cached_metadata->can_show_after_expiration);
EXPECT_EQ(test_clock_->Now() + base::TimeDelta::FromDays(30),
cached_metadata->expiration_time);
@@ -676,9 +663,9 @@ TEST_F(LogoTrackerTest, DeleteAncientCachedLogo) {
EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
- EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+ EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
- observer_.ExpectCachedLogo(NULL);
+ observer_.ExpectCachedLogo(nullptr);
GetLogo();
}
@@ -692,9 +679,9 @@ TEST_F(LogoTrackerTest, DeleteExpiredCachedLogo) {
EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
- EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+ EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
- observer_.ExpectCachedLogo(NULL);
+ observer_.ExpectCachedLogo(nullptr);
GetLogo();
}
@@ -726,16 +713,16 @@ TEST_F(LogoTrackerTest, SupportOverlappingLogoRequests) {
const int kNumListeners = 10;
std::vector<std::unique_ptr<MockLogoObserver>> listeners;
for (int i = 0; i < kNumListeners; ++i) {
- MockLogoObserver* listener = new MockLogoObserver();
+ auto listener = base::MakeUnique<MockLogoObserver>();
listener->ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
- listeners.push_back(base::WrapUnique(listener));
+ listeners.push_back(std::move(listener));
}
- EnqueueObservers(logo_tracker_, listeners, 0);
+ EnqueueObservers(logo_tracker_.get(), listeners, 0);
EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(AtMost(3));
EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(3));
- base::RunLoop().RunUntilIdle();
+ task_environment_.RunUntilIdle();
}
TEST_F(LogoTrackerTest, DeleteObserversWhenLogoURLChanged) {
@@ -744,10 +731,9 @@ TEST_F(LogoTrackerTest, DeleteObserversWhenLogoURLChanged) {
logo_tracker_->GetLogo(&listener1);
logo_url_ = GURL("http://example.com/new-logo-url");
- logo_tracker_->SetServerAPI(logo_url_, base::Bind(&GoogleParseLogoResponse),
- base::Bind(&GoogleAppendQueryparamsToLogoURL),
- false,
- false);
+ logo_tracker_->SetServerAPI(
+ logo_url_, base::Bind(&GoogleLegacyParseLogoResponse),
+ base::Bind(&GoogleLegacyAppendQueryparamsToLogoURL, false));
Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
SetServerResponse(ServerResponse(logo));
@@ -755,7 +741,7 @@ TEST_F(LogoTrackerTest, DeleteObserversWhenLogoURLChanged) {
listener2.ExpectFreshLogo(&logo);
logo_tracker_->GetLogo(&listener2);
- base::RunLoop().RunUntilIdle();
+ task_environment_.RunUntilIdle();
}
} // namespace
diff --git a/chromium/components/security_interstitials/content/security_interstitial_controller_client.cc b/chromium/components/security_interstitials/content/security_interstitial_controller_client.cc
index 87d0789a391..49eba9c05d4 100644
--- a/chromium/components/security_interstitials/content/security_interstitial_controller_client.cc
+++ b/chromium/components/security_interstitials/content/security_interstitial_controller_client.cc
@@ -79,6 +79,14 @@ void SecurityInterstitialControllerClient::OpenUrlInCurrentTab(
web_contents_->OpenURL(params);
}
+void SecurityInterstitialControllerClient::OpenUrlInNewForegroundTab(
+ const GURL& url) {
+ content::OpenURLParams params(url, Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ web_contents_->OpenURL(params);
+}
+
const std::string&
SecurityInterstitialControllerClient::GetApplicationLocale() const {
return app_locale_;
diff --git a/chromium/components/security_interstitials/content/security_interstitial_controller_client.h b/chromium/components/security_interstitials/content/security_interstitial_controller_client.h
index f10d26b08c4..29aa10f08f6 100644
--- a/chromium/components/security_interstitials/content/security_interstitial_controller_client.h
+++ b/chromium/components/security_interstitials/content/security_interstitial_controller_client.h
@@ -45,6 +45,7 @@ class SecurityInterstitialControllerClient
void Proceed() override;
void Reload() override;
void OpenUrlInCurrentTab(const GURL& url) override;
+ void OpenUrlInNewForegroundTab(const GURL& url) override;
PrefService* GetPrefService() override;
const std::string& GetApplicationLocale() const override;
bool CanLaunchDateAndTimeSettings() override;
diff --git a/chromium/components/security_interstitials/content/security_interstitial_page.cc b/chromium/components/security_interstitials/content/security_interstitial_page.cc
index db371a66c52..eaa95eb351a 100644
--- a/chromium/components/security_interstitials/content/security_interstitial_page.cc
+++ b/chromium/components/security_interstitials/content/security_interstitial_page.cc
@@ -88,7 +88,8 @@ void SecurityInterstitialPage::Show() {
AfterShow();
}
-SecurityInterstitialControllerClient* SecurityInterstitialPage::controller() {
+SecurityInterstitialControllerClient* SecurityInterstitialPage::controller()
+ const {
return controller_.get();
}
diff --git a/chromium/components/security_interstitials/content/security_interstitial_page.h b/chromium/components/security_interstitials/content/security_interstitial_page.h
index 710945cada9..7cf5fc20857 100644
--- a/chromium/components/security_interstitials/content/security_interstitial_page.h
+++ b/chromium/components/security_interstitials/content/security_interstitial_page.h
@@ -65,7 +65,7 @@ class SecurityInterstitialPage : public content::InterstitialPageDelegate {
content::WebContents* web_contents() const;
GURL request_url() const;
- SecurityInterstitialControllerClient* controller();
+ SecurityInterstitialControllerClient* controller() const;
// Update metrics when the interstitial is closed.
void UpdateMetricsAfterSecurityInterstitial();
diff --git a/chromium/components/security_interstitials/content/unsafe_resource.cc b/chromium/components/security_interstitials/content/unsafe_resource.cc
index c6204cb9652..410c3c7e46a 100644
--- a/chromium/components/security_interstitials/content/unsafe_resource.cc
+++ b/chromium/components/security_interstitials/content/unsafe_resource.cc
@@ -40,10 +40,13 @@ bool UnsafeResource::IsMainPageLoadBlocked() const {
if (is_subresource)
return false;
- // Client-side phishing detection interstitials never block the main frame
- // load, since they happen after the page is finished loading.
- if (threat_type == safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL ||
- threat_type == safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
+ // Client-side phishing/malware detection and password protection phishing
+ // interstitials never block the main frame load, since they happen after the
+ // page is finished loading.
+ if (threat_type == safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
+ threat_type == safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE ||
+ threat_type ==
+ safe_browsing::SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING) {
return false;
}
diff --git a/chromium/components/security_interstitials/content/unsafe_resource.h b/chromium/components/security_interstitials/content/unsafe_resource.h
index b26da33d121..fdb31b4e6f9 100644
--- a/chromium/components/security_interstitials/content/unsafe_resource.h
+++ b/chromium/components/security_interstitials/content/unsafe_resource.h
@@ -67,6 +67,9 @@ struct UnsafeResource {
scoped_refptr<base::SingleThreadTaskRunner> callback_thread;
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_URL_PASSWORD_PROTECTION_PHISHING
+ std::string token;
};
} // security_interstitials
diff --git a/chromium/components/security_interstitials/core/BUILD.gn b/chromium/components/security_interstitials/core/BUILD.gn
index 364f681b6cc..ec859cf01d4 100644
--- a/chromium/components/security_interstitials/core/BUILD.gn
+++ b/chromium/components/security_interstitials/core/BUILD.gn
@@ -20,6 +20,10 @@ static_library("core") {
"safe_browsing_quiet_error_ui.h",
"ssl_error_ui.cc",
"ssl_error_ui.h",
+ "superfish_error_ui.cc",
+ "superfish_error_ui.h",
+ "urls.cc",
+ "urls.h",
]
deps = [
diff --git a/chromium/components/security_interstitials/core/bad_clock_ui.cc b/chromium/components/security_interstitials/core/bad_clock_ui.cc
index dafdd148ab9..0bbd4510d28 100644
--- a/chromium/components/security_interstitials/core/bad_clock_ui.cc
+++ b/chromium/components/security_interstitials/core/bad_clock_ui.cc
@@ -105,10 +105,10 @@ void BadClockUI::HandleCommand(SecurityInterstitialCommands command) {
controller_->LaunchDateAndTimeSettings();
break;
case CMD_OPEN_REPORTING_PRIVACY:
- controller_->OpenExtendedReportingPrivacyPolicy();
+ controller_->OpenExtendedReportingPrivacyPolicy(true);
break;
case CMD_OPEN_WHITEPAPER:
- controller_->OpenExtendedReportingWhitepaper();
+ controller_->OpenExtendedReportingWhitepaper(true);
break;
case CMD_PROCEED:
case CMD_OPEN_HELP_CENTER:
diff --git a/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.cc b/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.cc
index ad7c340e53c..7a7ca5290b1 100644
--- a/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.cc
+++ b/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.cc
@@ -31,6 +31,7 @@ BaseSafeBrowsingErrorUI::SBErrorDisplayOptions::SBErrorDisplayOptions(
bool is_extended_reporting_enabled,
bool is_scout_reporting_enabled,
bool is_proceed_anyway_disabled,
+ bool should_open_links_in_new_tab,
const std::string& help_center_article_link)
: is_main_frame_load_blocked(is_main_frame_load_blocked),
is_extended_reporting_opt_in_allowed(
@@ -39,6 +40,7 @@ BaseSafeBrowsingErrorUI::SBErrorDisplayOptions::SBErrorDisplayOptions(
is_extended_reporting_enabled(is_extended_reporting_enabled),
is_scout_reporting_enabled(is_scout_reporting_enabled),
is_proceed_anyway_disabled(is_proceed_anyway_disabled),
+ should_open_links_in_new_tab(should_open_links_in_new_tab),
help_center_article_link(help_center_article_link) {}
BaseSafeBrowsingErrorUI::SBErrorDisplayOptions::SBErrorDisplayOptions(
@@ -50,6 +52,7 @@ BaseSafeBrowsingErrorUI::SBErrorDisplayOptions::SBErrorDisplayOptions(
is_extended_reporting_enabled(other.is_extended_reporting_enabled),
is_scout_reporting_enabled(other.is_scout_reporting_enabled),
is_proceed_anyway_disabled(other.is_proceed_anyway_disabled),
+ should_open_links_in_new_tab(other.should_open_links_in_new_tab),
help_center_article_link(other.help_center_article_link) {}
} // security_interstitials
diff --git a/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.h b/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.h
index 1306fbdca8a..ccbf9f90429 100644
--- a/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.h
+++ b/chromium/components/security_interstitials/core/base_safe_browsing_error_ui.h
@@ -32,6 +32,7 @@ class BaseSafeBrowsingErrorUI {
bool is_extended_reporting_enabled,
bool is_scout_reporting_enabled,
bool is_proceed_anyway_disabled,
+ bool should_open_links_in_new_tab,
const std::string& help_center_article_link);
SBErrorDisplayOptions(const SBErrorDisplayOptions& other);
@@ -54,6 +55,9 @@ class BaseSafeBrowsingErrorUI {
// Indicates if kSafeBrowsingProceedAnywayDisabled preference is set.
bool is_proceed_anyway_disabled;
+ // Indicates if links should use a new foreground tab or the current tab.
+ bool should_open_links_in_new_tab;
+
// The p= query parameter used when visiting the Help Center. If this is
// nullptr, then a default value will be used for the SafeBrowsing article.
std::string help_center_article_link;
@@ -95,10 +99,18 @@ class BaseSafeBrowsingErrorUI {
return display_options_.is_proceed_anyway_disabled;
}
+ bool should_open_links_in_new_tab() const {
+ return display_options_.should_open_links_in_new_tab;
+ }
+
const std::string& get_help_center_article_link() const {
return display_options_.help_center_article_link;
}
+ const SBErrorDisplayOptions& get_error_display_options() const {
+ return display_options_;
+ }
+
// Checks if we should even show the extended reporting option. We don't show
// it in incognito mode or if kSafeBrowsingExtendedReportingOptInAllowed
// preference is disabled.
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_badclock.css b/chromium/components/security_interstitials/core/browser/resources/interstitial_badclock.css
new file mode 100644
index 00000000000..5795edfef8c
--- /dev/null
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_badclock.css
@@ -0,0 +1,9 @@
+/* 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. */
+
+.bad-clock .icon {
+ background-image: -webkit-image-set(
+ url(images/1x/clock.png) 1x,
+ url(images/2x/clock.png) 2x);
+}
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_captiveportal.css b/chromium/components/security_interstitials/core/browser/resources/interstitial_captiveportal.css
new file mode 100644
index 00000000000..4a46002141e
--- /dev/null
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_captiveportal.css
@@ -0,0 +1,9 @@
+/* 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. */
+
+.captive-portal .icon {
+ background-image: -webkit-image-set(
+ url(images/1x/captive_portal_page_icon.png) 1x,
+ url(images/2x/captive_portal_page_icon.png) 2x);
+}
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_common.js b/chromium/components/security_interstitials/core/browser/resources/interstitial_common.js
deleted file mode 100644
index e826a149075..00000000000
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_common.js
+++ /dev/null
@@ -1,46 +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 is the shared code for security interstitials. It is used for both SSL
-// interstitials and Safe Browsing interstitials.
-
-// Should match security_interstitials::SecurityInterstitialCommands
-/** @enum| {string} */
-var SecurityInterstitialCommandId = {
- CMD_DONT_PROCEED: 0,
- CMD_PROCEED: 1,
- // Ways for user to get more information
- CMD_SHOW_MORE_SECTION: 2,
- CMD_OPEN_HELP_CENTER: 3,
- CMD_OPEN_DIAGNOSTIC: 4,
- // Primary button actions
- CMD_RELOAD: 5,
- CMD_OPEN_DATE_SETTINGS: 6,
- CMD_OPEN_LOGIN: 7,
- // Safe Browsing Extended Reporting
- CMD_DO_REPORT: 8,
- CMD_DONT_REPORT: 9,
- CMD_OPEN_REPORTING_PRIVACY: 10,
- CMD_OPEN_WHITEPAPER: 11,
- // Report a phishing error.
- CMD_REPORT_PHISHING_ERROR: 12
-};
-
-/**
- * A convenience method for sending commands to the parent page.
- * @param {string} cmd The command to send.
- */
-function sendCommand(cmd) {
-// <if expr="not is_ios">
- window.domAutomationController.setAutomationId(1);
- window.domAutomationController.send(cmd);
-// </if>
-// <if expr="is_ios">
- // TODO(crbug.com/565877): Revisit message passing for WKWebView.
- var iframe = document.createElement('IFRAME');
- iframe.setAttribute('src', 'js-command:' + cmd);
- document.documentElement.appendChild(iframe);
- iframe.parentNode.removeChild(iframe);
-// </if>
-} \ No newline at end of file
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_large.html b/chromium/components/security_interstitials/core/browser/resources/interstitial_large.html
new file mode 100644
index 00000000000..e60f2253ea5
--- /dev/null
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_large.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport"
+ content="initial-scale=1, minimum-scale=1, width=device-width">
+ <title>$i18n{tabTitle}</title>
+ <link rel="stylesheet" href="../../common/resources/interstitial_core.css">
+ <link rel="stylesheet" href="../../common/resources/interstitial_common.css">
+ <link rel="stylesheet" href="interstitial_badclock.css">
+ <link rel="stylesheet" href="interstitial_captiveportal.css">
+ <link rel="stylesheet" href="interstitial_safebrowsing.css">
+ <link rel="stylesheet" href="interstitial_ssl.css">
+ <script src="../../../../../ui/webui/resources/js/util.js"></script>
+ <script src="captive_portal.js"></script>
+ <script src="ssl.js"></script>
+ <script src="extended_reporting.js"></script>
+ <script src="../../common/resources/interstitial_mobile_nav.js"></script>
+ <script src="../../common/resources/interstitial_common.js"></script>
+ <script src="interstitial_large.js"></script>
+</head>
+<body id="body">
+ <div class="interstitial-wrapper">
+ <div id="main-content">
+ <div class="icon" id="icon"></div>
+ <div id="main-message">
+ <h1>$i18n{heading}</h1>
+ <p>$i18nRaw{primaryParagraph}</p>
+ <div id="debugging">
+ <div id="error-code" class="error-code"></div>
+ <div id="error-debugging-info" class="hidden"></div>
+ </div>
+ </div>
+ <div id="extended-reporting-opt-in" class="hidden">
+ <label>
+ <div class="checkboxes">
+ <input type="checkbox" id="opt-in-checkbox">
+ <span class="checkbox"></span>
+ </div>
+ <span id="opt-in-label"></span>
+ </label>
+ </div>
+ </div>
+ <div class="nav-wrapper">
+ <button id="primary-button">$i18n{primaryButtonText}</button>
+ <button id="details-button" class="small-link">
+ $i18n{openDetails}
+ </button>
+ </div>
+ <div id="details" class="hidden">
+ <p>$i18nRaw{explanationParagraph}</p>
+ <p id="final-paragraph">$i18nRaw{finalParagraph}</p>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_large.js b/chromium/components/security_interstitials/core/browser/resources/interstitial_large.js
new file mode 100644
index 00000000000..3deac633c8a
--- /dev/null
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_large.js
@@ -0,0 +1,172 @@
+// 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.
+
+// This is the shared code for the new (Chrome 37) security interstitials. It is
+// used for both SSL interstitials and Safe Browsing interstitials.
+
+var expandedDetails = false;
+var keyPressState = 0;
+
+/**
+ * This allows errors to be skippped by typing a secret phrase into the page.
+ * @param {string} e The key that was just pressed.
+ */
+function handleKeypress(e) {
+ var BYPASS_SEQUENCE = 'badidea';
+ if (BYPASS_SEQUENCE.charCodeAt(keyPressState) == e.keyCode) {
+ keyPressState++;
+ if (keyPressState == BYPASS_SEQUENCE.length) {
+ sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
+ keyPressState = 0;
+ }
+ } else {
+ keyPressState = 0;
+ }
+}
+
+/**
+ * This appends a piece of debugging information to the end of the warning.
+ * When complete, the caller must also make the debugging div
+ * (error-debugging-info) visible.
+ * @param {string} title The name of this debugging field.
+ * @param {string} value The value of the debugging field.
+ * @param {boolean=} fixedWidth If true, the value field is displayed fixed
+ * width.
+ */
+function appendDebuggingField(title, value, fixedWidth) {
+ // The values input here are not trusted. Never use innerHTML on these
+ // values!
+ var spanTitle = document.createElement('span');
+ spanTitle.classList.add('debugging-title');
+ spanTitle.innerText = title + ': ';
+
+ var spanValue = document.createElement('span');
+ spanValue.classList.add('debugging-content');
+ if (fixedWidth) {
+ spanValue.classList.add('debugging-content-fixed-width');
+ }
+ spanValue.innerText = value;
+
+ var pElem = document.createElement('p');
+ pElem.classList.add('debugging-content');
+ pElem.appendChild(spanTitle);
+ pElem.appendChild(spanValue);
+ $('error-debugging-info').appendChild(pElem);
+}
+
+function toggleDebuggingInfo() {
+ $('error-debugging-info').classList.toggle(HIDDEN_CLASS);
+}
+
+function setupEvents() {
+ var overridable = loadTimeData.getBoolean('overridable');
+ var interstitialType = loadTimeData.getString('type');
+ var ssl = interstitialType == 'SSL';
+ var captivePortal = interstitialType == 'CAPTIVE_PORTAL';
+ var badClock = ssl && loadTimeData.getBoolean('bad_clock');
+ var hidePrimaryButton = loadTimeData.getBoolean('hide_primary_button');
+
+ if (ssl) {
+ $('body').classList.add(badClock ? 'bad-clock' : 'ssl');
+ $('error-code').textContent = loadTimeData.getString('errorCode');
+ $('error-code').classList.remove(HIDDEN_CLASS);
+ } else if (captivePortal) {
+ $('body').classList.add('captive-portal');
+ } else {
+ $('body').classList.add('safe-browsing');
+ }
+
+ $('icon').classList.add('icon');
+
+ if (hidePrimaryButton) {
+ $('primary-button').classList.add(HIDDEN_CLASS);
+ } else {
+ $('primary-button').addEventListener('click', function() {
+ switch (interstitialType) {
+ case 'CAPTIVE_PORTAL':
+ sendCommand(SecurityInterstitialCommandId.CMD_OPEN_LOGIN);
+ break;
+
+ case 'SSL':
+ if (badClock)
+ sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DATE_SETTINGS);
+ else if (overridable)
+ sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
+ else
+ sendCommand(SecurityInterstitialCommandId.CMD_RELOAD);
+ break;
+
+ case 'SAFEBROWSING':
+ sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
+ break;
+
+ default:
+ throw 'Invalid interstitial type';
+ }
+ });
+ }
+
+ if (overridable) {
+ // Captive portal page isn't overridable.
+ $('proceed-link').addEventListener('click', function(event) {
+ sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
+ });
+ } else if (!ssl) {
+ $('final-paragraph').classList.add(HIDDEN_CLASS);
+ }
+
+ if (ssl && overridable) {
+ $('proceed-link').classList.add('small-link');
+ }
+
+ if ($('diagnostic-link')) {
+ $('diagnostic-link').addEventListener('click', function(event) {
+ sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DIAGNOSTIC);
+ });
+ }
+
+ if ($('learn-more-link')) {
+ $('learn-more-link').addEventListener('click', function(event) {
+ sendCommand(SecurityInterstitialCommandId.CMD_OPEN_HELP_CENTER);
+ });
+ }
+
+ if (captivePortal) {
+ // Captive portal page doesn't have details button.
+ $('details-button').classList.add('hidden');
+ } else {
+ $('details-button').addEventListener('click', function(event) {
+ var hiddenDetails = $('details').classList.toggle(HIDDEN_CLASS);
+
+ if (mobileNav) {
+ // Details appear over the main content on small screens.
+ $('main-content').classList.toggle(HIDDEN_CLASS, !hiddenDetails);
+ } else {
+ $('main-content').classList.remove(HIDDEN_CLASS);
+ }
+
+ $('details-button').innerText = hiddenDetails ?
+ loadTimeData.getString('openDetails') :
+ loadTimeData.getString('closeDetails');
+ if (!expandedDetails) {
+ // Record a histogram entry only the first time that details is opened.
+ sendCommand(SecurityInterstitialCommandId.CMD_SHOW_MORE_SECTION);
+ expandedDetails = true;
+ }
+ });
+ }
+
+ if ($('report-error-link')) {
+ $('report-error-link').addEventListener('click', function(event) {
+ sendCommand(SecurityInterstitialCommandId.CMD_REPORT_PHISHING_ERROR);
+ });
+ }
+
+ preventDefaultOnPoundLinkClicks();
+ setupExtendedReportingCheckbox();
+ setupSSLDebuggingInfo();
+ document.addEventListener('keypress', handleKeypress);
+}
+
+document.addEventListener('DOMContentLoaded', setupEvents);
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css b/chromium/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css
new file mode 100644
index 00000000000..c274cd4d7f3
--- /dev/null
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css
@@ -0,0 +1,45 @@
+/* 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. */
+
+body.safe-browsing {
+ background-color: rgb(206, 52, 38);
+ color: white;
+}
+
+.safe-browsing :-webkit-any(
+ a, #details, #details-button, h1, h2, p, .small-link) {
+ color: white;
+}
+
+.safe-browsing button {
+ background-color: rgba(255, 255, 255, .15);
+}
+
+.safe-browsing button:active {
+ background-color: rgba(255, 255, 255, .25);
+}
+
+.safe-browsing button:hover {
+ box-shadow: 0 2px 3px rgba(0, 0, 0, .5);
+}
+
+.safe-browsing .error-code {
+ display: none;
+}
+
+.safe-browsing .icon {
+ background-image: -webkit-image-set(
+ url(images/1x/triangle_white.png) 1x,
+ url(images/2x/triangle_white.png) 2x);
+}
+
+@media (min-width: 240px) and (max-width: 420px) and
+ (min-height: 401px),
+ (min-width: 421px) and (min-height: 240px) and
+ (max-height: 560px) {
+ body.safe-browsing .nav-wrapper {
+ background: rgb(206, 52, 38);
+ box-shadow: 0 -22px 40px rgb(206, 52, 38);
+ }
+} \ No newline at end of file
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_ssl.css b/chromium/components/security_interstitials/core/browser/resources/interstitial_ssl.css
new file mode 100644
index 00000000000..97fff2260a3
--- /dev/null
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_ssl.css
@@ -0,0 +1,17 @@
+/* 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. */
+
+.ssl .icon {
+ background-image: -webkit-image-set(
+ url(images/1x/triangle_red.png) 1x,
+ url(images/2x/triangle_red.png) 2x);
+}
+
+.ssl-opt-in .checkbox {
+ border-color: #696969;
+}
+
+.ssl-opt-in .checkbox::before {
+ border-color: #696969;
+}
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_ui.html b/chromium/components/security_interstitials/core/browser/resources/interstitial_ui.html
deleted file mode 100644
index a759c82be28..00000000000
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_ui.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<html>
-<head>
- <title>Interstitials</title>
- <meta name="viewport" content="width=device-width">
- <style>
- body {
- font-family: sans-serif;
- line-height: 1.4;
- }
-
- h3, h4 {
- margin-bottom: 0.5em;
- }
-
- ul {
- margin-top: 0.5em;
- }
- </style>
-</head>
-<body>
- <h2>Choose an interstitial</h2>
- <h3>SSL</h3>
- <ul>
- <li>
- <a href="ssl?overridable=1&strict_enforcement=0">example.com (generic, overridable)</a>
- </li>
- <li>
- <a href="ssl?overridable=0&strict_enforcement=0">
- example.com (generic, non-overridable)
- </a>
- </li>
- <li>
- <a href="ssl?overridable=0&strict_enforcement=1">
- example.com (HSTS, non-overridable)
- </a>
- </li>
- <li>
- <a href="clock?clock_manipulation=2">Clock is ahead</a>
- </li>
- <li>
- <a href="clock?clock_manipulation=-2">Clock is behind</a>
- </li>
- </ul>
- <h3>SafeBrowsing</h3>
- <h4>Loud</h4>
- <ul>
- <li>
- <a href="safebrowsing?type=malware">Malware</a>
- </li>
- <li>
- <a href="safebrowsing?type=phishing">Phishing</a>
- </li>
- <li>
- <a href="safebrowsing?type=clientside_malware">Client Side Malware</a>
- </li>
- <li>
- <a href="safebrowsing?type=clientside_phishing">Client Side Phishing</a>
- </li>
- </ul>
- <h4>Quiet (WebView)</h4>
- <ul>
- <li>
- <a href="quietsafebrowsing?type=malware">Malware</a>
- </li>
- <li>
- <a href="quietsafebrowsing?type=phishing">Phishing</a>
- </li>
- <li>
- <a href="quietsafebrowsing?type=giant">Giant</a>
- </li>
- </ul>
- <h3>Captive Portal</h3>
- <ul>
- <li>
- <a href="captiveportal">Captive Portal, Non-WiFi</a>
- </li>
- <li>
- <a href="captiveportal?is_wifi=1">
- Captive Portal, WiFi
- </a>
- </li>
- <li>
- <a href="captiveportal?is_wifi=1&wifi_name=CoffeeShopWiFi">
- Captive Portal, WiFi with network name "CoffeeShopWiFi"
- </a>
- </li>
- </ul>
- <h3>Supervised Users</h3>
- <ul>
- <li>
- <a href="supervised_user">
- Supervised User
- </a>
- </li>
- </ul>
-</body>
-</html>
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css
deleted file mode 100644
index fdda65dff27..00000000000
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css
+++ /dev/null
@@ -1,505 +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. */
-
-.bad-clock .icon {
- background-image: -webkit-image-set(
- url(images/1x/clock.png) 1x,
- url(images/2x/clock.png) 2x);
-}
-
-body.safe-browsing {
- background-color: rgb(206, 52, 38);
- color: white;
-}
-
-button {
- -webkit-user-select: none;
- background: rgb(66, 133, 244);
- border: 0;
- border-radius: 2px;
- box-sizing: border-box;
- color: #fff;
- cursor: pointer;
- float: right;
- font-size: .875em;
- margin: 0;
- padding: 10px 24px;
- transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-[dir='rtl'] button {
- float: left;
-}
-
-button:active {
- background: rgb(50, 102, 213);
- outline: 0;
-}
-
-button:hover {
- box-shadow: 0 1px 3px rgba(0, 0, 0, .50);
-}
-
-#debugging {
- display: inline;
- overflow: auto;
-}
-
-.debugging-content {
- line-height: 1em;
- margin-bottom: 0;
- margin-top: 1em;
-}
-
-.debugging-content-fixed-width {
- display: block;
- font-family: monospace;
- font-size: 1.2em;
- margin-top: 0.5em;
-}
-
-.debugging-title {
- font-weight: bold;
-}
-
-#details {
- color: #696969;
- margin: 0 0 50px;
-}
-
-#details p:not(:first-of-type) {
- margin-top: 20px;
-}
-
-#details-button:hover {
- box-shadow: inherit;
- text-decoration: underline;
-}
-
-.error-code {
- color: #646464;
- font-size: .86667em;
- text-transform: uppercase;
-}
-
-#error-debugging-info {
- font-size: 0.8em;
-}
-
-h1 {
- color: #333;
- font-size: 1.6em;
- font-weight: normal;
- line-height: 1.25em;
- margin-bottom: 16px;
-}
-
-h2 {
- font-size: 1.2em;
- font-weight: normal;
-}
-
-.icon {
- height: 72px;
- margin: 0 0 40px;
- width: 72px;
-}
-
-input[type=checkbox] {
- opacity: 0;
-}
-
-input[type=checkbox]:focus ~ .checkbox {
- outline: -webkit-focus-ring-color auto 5px;
-}
-
-.interstitial-wrapper {
- box-sizing: border-box;
- font-size: 1em;
- line-height: 1.6em;
- margin: 100px auto 0;
- max-width: 600px;
- width: 100%;
-}
-
-#main-message > p {
- display: inline;
-}
-
-#extended-reporting-opt-in {
- font-size: .875em;
- margin-top: 39px;
-}
-
-#extended-reporting-opt-in label {
- position: relative;
- display: flex;
- align-items: flex-start;
-}
-
-.nav-wrapper {
- margin-top: 51px;
-}
-
-.nav-wrapper::after {
- clear: both;
- content: '';
- display: table;
- width: 100%;
-}
-
-.safe-browsing :-webkit-any(
- a, #details, #details-button, h1, h2, p, .small-link) {
- color: white;
-}
-
-.safe-browsing button {
- background-color: rgba(255, 255, 255, .15);
-}
-
-.safe-browsing button:active {
- background-color: rgba(255, 255, 255, .25);
-}
-
-.safe-browsing button:hover {
- box-shadow: 0 2px 3px rgba(0, 0, 0, .5);
-}
-
-.safe-browsing .error-code {
- display: none;
-}
-
-.safe-browsing .icon {
- background-image: -webkit-image-set(
- url(images/1x/triangle_white.png) 1x,
- url(images/2x/triangle_white.png) 2x);
-}
-
-.small-link {
- color: #696969;
- font-size: .875em;
-}
-
-.ssl .icon {
- background-image: -webkit-image-set(
- url(images/1x/triangle_red.png) 1x,
- url(images/2x/triangle_red.png) 2x);
-}
-
-.captive-portal .icon {
- background-image: -webkit-image-set(
- url(images/1x/captive_portal_page_icon.png) 1x,
- url(images/2x/captive_portal_page_icon.png) 2x);
-}
-
-.checkboxes {
- flex: 0 0 24px;
-}
-
-.checkbox {
- background: transparent;
- border: 1px solid white;
- border-radius: 2px;
- display: block;
- height: 14px;
- left: 0;
- position: absolute;
- right: 0;
- top: 3px;
- width: 14px;
-}
-
-.checkbox::before {
- background: transparent;
- border: 2px solid white;
- border-right-width: 0;
- border-top-width: 0;
- content: '';
- height: 4px;
- left: 2px;
- opacity: 0;
- position: absolute;
- top: 3px;
- transform: rotate(-45deg);
- width: 9px;
-}
-
-.ssl-opt-in .checkbox {
- border-color: #696969;
-}
-
-.ssl-opt-in .checkbox::before {
- border-color: #696969;
-}
-
-input[type=checkbox]:checked ~ .checkbox::before {
- opacity: 1;
-}
-
-@media (max-width: 700px) {
- .interstitial-wrapper {
- padding: 0 10%;
- }
-
- #error-debugging-info {
- overflow: auto;
- }
-}
-
-@media (max-height: 600px) {
- .error-code {
- margin-top: 10px;
- }
-}
-
-@media (max-width: 420px) {
- button,
- [dir='rtl'] button,
- .small-link {
- float: none;
- font-size: .825em;
- font-weight: 400;
- margin: 0;
- text-transform: uppercase;
- width: 100%;
- }
-
- #details {
- margin: 20px 0 20px 0;
- }
-
- #details p:not(:first-of-type) {
- margin-top: 10px;
- }
-
- #details-button {
- display: block;
- margin-top: 20px;
- text-align: center;
- width: 100%;
- }
-
- .interstitial-wrapper {
- padding: 0 5%;
- }
-
- #extended-reporting-opt-in {
- margin-top: 24px;
- }
-
- .nav-wrapper {
- margin-top: 30px;
- }
-}
-
-/**
- * Mobile specific styling.
- * Navigation buttons are anchored to the bottom of the screen.
- * Details message replaces the top content in its own scrollable area.
- */
-
-@media (max-width: 420px) {
- #details-button {
- border: 0;
- margin: 28px 0 0;
- }
-
- .secondary-button {
- -webkit-margin-end: 0;
- margin-top: 16px;
- }
-}
-
-/* Fixed nav. */
-@media (min-width: 240px) and (max-width: 420px) and
- (min-height: 401px),
- (min-width: 421px) and (min-height: 240px) and
- (max-height: 560px) {
- body .nav-wrapper {
- background: #f7f7f7;
- bottom: 0;
- box-shadow: 0 -22px 40px rgb(247, 247, 247);
- margin: 0;
- max-width: 736px;
- padding-left: 0px;
- padding-right: 48px;
- position: fixed;
- z-index: 2;
- }
-
- body.safe-browsing .nav-wrapper {
- background: rgb(206, 52, 38);
- box-shadow: 0 -22px 40px rgb(206, 52, 38);
- }
-
- .interstitial-wrapper {
- max-width: 736px;
- }
-
- #details,
- #main-content {
- padding-bottom: 40px;
- }
-
- #details {
- padding-top: 5.5vh;
- }
-}
-
-@media (max-width: 420px) and (orientation: portrait),
- (max-height: 560px) {
- body {
- margin: 0 auto;
- }
-
- button,
- [dir='rtl'] button,
- button.small-link {
- font-family: Roboto-Regular,Helvetica;
- font-size: .933em;
- font-weight: 600;
- margin: 6px 0;
- text-transform: uppercase;
- transform: translatez(0);
- }
-
- .nav-wrapper {
- box-sizing: border-box;
- padding-bottom: 8px;
- width: 100%;
- }
-
- .error-code {
- margin-top: 0;
- }
-
- #details {
- box-sizing: border-box;
- height: auto;
- margin: 0;
- opacity: 1;
- transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);
- }
-
- #details.hidden,
- #main-content.hidden {
- display: block;
- height: 0;
- opacity: 0;
- overflow: hidden;
- padding-bottom: 0;
- transition: none;
- }
-
- #details-button {
- padding-bottom: 16px;
- padding-top: 16px;
- }
-
- h1 {
- font-size: 1.5em;
- margin-bottom: 8px;
- }
-
- .icon {
- margin-bottom: 5.69vh;
- }
-
- .interstitial-wrapper {
- box-sizing: border-box;
- margin: 7vh auto 12px;
- padding: 0 24px;
- position: relative;
- }
-
- .interstitial-wrapper p {
- font-size: .95em;
- line-height: 1.61em;
- margin-top: 8px;
- }
-
- #main-content {
- margin: 0;
- transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1);
- }
-
- .small-link {
- border: 0;
- }
-
- .suggested-left > #control-buttons,
- .suggested-right > #control-buttons {
- float: none;
- margin: 0;
- }
-}
-
-@media (min-width: 421px) and (min-height: 500px) and (max-height: 560px) {
- .interstitial-wrapper {
- margin-top: 10vh;
- }
-}
-
-@media (min-height: 400px) and (orientation:portrait) {
- .interstitial-wrapper {
- margin-bottom: 145px;
- }
-}
-
-@media (min-height: 299px) {
- .nav-wrapper {
- padding-bottom: 16px;
- }
-}
-
-@media (min-height: 500px) and (max-height: 650px) and (max-width: 414px) and
- (orientation: portrait) {
- .interstitial-wrapper {
- margin-top: 7vh;
- }
-}
-
-@media (min-height: 650px) and (max-width: 414px) and (orientation: portrait) {
- .interstitial-wrapper {
- margin-top: 10vh;
- }
-}
-
-/* Small mobile screens. No fixed nav. */
-@media (max-height: 400px) and (orientation: portrait),
- (max-height: 239px) and (orientation: landscape),
- (max-width: 419px) and (max-height: 399px) {
- .interstitial-wrapper {
- display: flex;
- flex-direction: column;
- margin-bottom: 0;
- }
-
- #details {
- flex: 1 1 auto;
- order: 0;
- }
-
- #main-content {
- flex: 1 1 auto;
- order: 0;
- }
-
- .nav-wrapper {
- flex: 0 1 auto;
- margin-top: 8px;
- order: 1;
- padding-left: 0;
- padding-right: 0;
- position: relative;
- width: 100%;
- }
-}
-
-@media (max-width: 239px) and (orientation: portrait) {
- .nav-wrapper {
- padding-left: 0;
- padding-right: 0;
- }
-}
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.html b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.html
deleted file mode 100644
index f382689f3d3..00000000000
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
-<head>
- <meta charset="utf-8">
- <meta name="viewport"
- content="initial-scale=1, minimum-scale=1, width=device-width">
- <title i18n-content="tabTitle"></title>
- <link rel="stylesheet" href="interstitial_common.css">
- <link rel="stylesheet" href="interstitial_v2.css">
- <script src="../../../../../ui/webui/resources/js/util.js"></script>
- <script src="captive_portal.js"></script>
- <script src="ssl.js"></script>
- <script src="extended_reporting.js"></script>
- <script src="interstitial_v2_mobile.js"></script>
- <script src="interstitial_common.js"></script>
- <script src="interstitial_v2.js"></script>
-</head>
-<body id="body">
- <div class="interstitial-wrapper">
- <div id="main-content">
- <div class="icon" id="icon"></div>
- <div id="main-message">
- <h1>$i18n{heading}</h1>
- <p i18n-values=".innerHTML:primaryParagraph"></p>
- <div id="debugging">
- <div id="error-code" class="error-code"></div>
- <div id="error-debugging-info" class="hidden"></div>
- </div>
- </div>
- <div id="extended-reporting-opt-in" class="hidden">
- <label>
- <div class="checkboxes">
- <input type="checkbox" id="opt-in-checkbox">
- <span class="checkbox"></span>
- </div>
- <span id="opt-in-label"></span>
- </label>
- </div>
- </div>
- <div class="nav-wrapper">
- <button id="primary-button">$i18n{primaryButtonText}</button>
- <button id="details-button" class="small-link">$i18n{openDetails}</button>
- </div>
- <div id="details" class="hidden">
- <p i18n-values=".innerHTML:explanationParagraph"></p>
- <p i18n-values=".innerHTML:finalParagraph" id="final-paragraph"></p>
- </div>
- </div>
-</body>
-</html>
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js
deleted file mode 100644
index 815ba69fcae..00000000000
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js
+++ /dev/null
@@ -1,172 +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.
-
-// This is the shared code for the new (Chrome 37) security interstitials. It is
-// used for both SSL interstitials and Safe Browsing interstitials.
-
-var expandedDetails = false;
-var keyPressState = 0;
-
-/**
- * This allows errors to be skippped by typing a secret phrase into the page.
- * @param {string} e The key that was just pressed.
- */
-function handleKeypress(e) {
- var BYPASS_SEQUENCE = 'badidea';
- if (BYPASS_SEQUENCE.charCodeAt(keyPressState) == e.keyCode) {
- keyPressState++;
- if (keyPressState == BYPASS_SEQUENCE.length) {
- sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
- keyPressState = 0;
- }
- } else {
- keyPressState = 0;
- }
-}
-
-/**
- * This appends a piece of debugging information to the end of the warning.
- * When complete, the caller must also make the debugging div
- * (error-debugging-info) visible.
- * @param {string} title The name of this debugging field.
- * @param {string} value The value of the debugging field.
- * @param {boolean=} fixedWidth If true, the value field is displayed fixed
- * width.
- */
-function appendDebuggingField(title, value, fixedWidth) {
- // The values input here are not trusted. Never use innerHTML on these
- // values!
- var spanTitle = document.createElement('span');
- spanTitle.classList.add('debugging-title');
- spanTitle.innerText = title + ': ';
-
- var spanValue = document.createElement('span');
- spanValue.classList.add('debugging-content');
- if (fixedWidth) {
- spanValue.classList.add('debugging-content-fixed-width');
- }
- spanValue.innerText = value;
-
- var pElem = document.createElement('p');
- pElem.classList.add('debugging-content');
- pElem.appendChild(spanTitle);
- pElem.appendChild(spanValue);
- $('error-debugging-info').appendChild(pElem);
-}
-
-function toggleDebuggingInfo() {
- $('error-debugging-info').classList.toggle('hidden');
-}
-
-function setupEvents() {
- var overridable = loadTimeData.getBoolean('overridable');
- var interstitialType = loadTimeData.getString('type');
- var ssl = interstitialType == 'SSL';
- var captivePortal = interstitialType == 'CAPTIVE_PORTAL';
- var badClock = ssl && loadTimeData.getBoolean('bad_clock');
- var hidePrimaryButton = loadTimeData.getBoolean('hide_primary_button');
-
- if (ssl) {
- $('body').classList.add(badClock ? 'bad-clock' : 'ssl');
- $('error-code').textContent = loadTimeData.getString('errorCode');
- $('error-code').classList.remove('hidden');
- } else if (captivePortal) {
- $('body').classList.add('captive-portal');
- } else {
- $('body').classList.add('safe-browsing');
- }
-
- $('icon').classList.add('icon');
-
- if (hidePrimaryButton) {
- $('primary-button').classList.add('hidden');
- } else {
- $('primary-button').addEventListener('click', function() {
- switch (interstitialType) {
- case 'CAPTIVE_PORTAL':
- sendCommand(SecurityInterstitialCommandId.CMD_OPEN_LOGIN);
- break;
-
- case 'SSL':
- if (badClock)
- sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DATE_SETTINGS);
- else if (overridable)
- sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
- else
- sendCommand(SecurityInterstitialCommandId.CMD_RELOAD);
- break;
-
- case 'SAFEBROWSING':
- sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
- break;
-
- default:
- throw 'Invalid interstitial type';
- }
- });
- }
-
- if (overridable) {
- // Captive portal page isn't overridable.
- $('proceed-link').addEventListener('click', function(event) {
- sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
- });
- } else if (!ssl) {
- $('final-paragraph').classList.add('hidden');
- }
-
- if (ssl && overridable) {
- $('proceed-link').classList.add('small-link');
- }
-
- if ($('diagnostic-link')) {
- $('diagnostic-link').addEventListener('click', function(event) {
- sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DIAGNOSTIC);
- });
- }
-
- if ($('learn-more-link')) {
- $('learn-more-link').addEventListener('click', function(event) {
- sendCommand(SecurityInterstitialCommandId.CMD_OPEN_HELP_CENTER);
- });
- }
-
- if (captivePortal) {
- // Captive portal page doesn't have details button.
- $('details-button').classList.add('hidden');
- } else {
- $('details-button').addEventListener('click', function(event) {
- var hiddenDetails = $('details').classList.toggle('hidden');
-
- if (mobileNav) {
- // Details appear over the main content on small screens.
- $('main-content').classList.toggle('hidden', !hiddenDetails);
- } else {
- $('main-content').classList.remove('hidden');
- }
-
- $('details-button').innerText = hiddenDetails ?
- loadTimeData.getString('openDetails') :
- loadTimeData.getString('closeDetails');
- if (!expandedDetails) {
- // Record a histogram entry only the first time that details is opened.
- sendCommand(SecurityInterstitialCommandId.CMD_SHOW_MORE_SECTION);
- expandedDetails = true;
- }
- });
- }
-
- if ($('report-error-link')) {
- $('report-error-link').addEventListener('click', function(event) {
- sendCommand(SecurityInterstitialCommandId.CMD_REPORT_PHISHING_ERROR);
- });
- }
-
- preventDefaultOnPoundLinkClicks();
- setupExtendedReportingCheckbox();
- setupSSLDebuggingInfo();
- document.addEventListener('keypress', handleKeypress);
-}
-
-document.addEventListener('DOMContentLoaded', setupEvents);
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js
deleted file mode 100644
index 655d91f33be..00000000000
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var mobileNav = false;
-
-/**
- * For small screen mobile the navigation buttons are moved
- * below the advanced text.
- */
-function onResize() {
- var helpOuterBox = document.querySelector('#details');
- var mainContent = document.querySelector('#main-content');
- var mediaQuery = '(min-width: 240px) and (max-width: 420px) and ' +
- '(min-height: 401px), ' +
- '(max-height: 560px) and (min-height: 240px) and ' +
- '(min-width: 421px)';
-
- var detailsHidden = helpOuterBox.classList.contains('hidden');
- var runnerContainer = document.querySelector('.runner-container');
-
- // Check for change in nav status.
- if (mobileNav != window.matchMedia(mediaQuery).matches) {
- mobileNav = !mobileNav;
-
- // Handle showing the top content / details sections according to state.
- if (mobileNav) {
- mainContent.classList.toggle('hidden', !detailsHidden);
- helpOuterBox.classList.toggle('hidden', detailsHidden);
- if (runnerContainer) {
- runnerContainer.classList.toggle('hidden', !detailsHidden);
- }
- } else if (!detailsHidden) {
- // Non mobile nav with visible details.
- mainContent.classList.remove('hidden');
- helpOuterBox.classList.remove('hidden');
- if (runnerContainer) {
- runnerContainer.classList.remove('hidden');
- }
- }
- }
-}
-
-function setupMobileNav() {
- window.addEventListener('resize', onResize);
- onResize();
-}
-
-document.addEventListener('DOMContentLoaded', setupMobileNav);
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css b/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
index 80a87648cb6..50c858c297c 100644
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
@@ -8,6 +8,9 @@ body {
#details {
box-sizing: border-box;
+ color: rgba(0, 0, 0, .54);
+ font-family: Roboto-Regular, sans-serif;
+ font-size: 0.93333em;
height: auto;
line-height: 1.48em;
margin: 0;
@@ -25,16 +28,16 @@ body {
}
#details-link {
- color: rgba(0,0,0,.38);
- /* For V1, the details link is hidden. */
- display: none;
+ color: rgba(0, 0, 0, .38);
text-decoration: underline;
text-transform: none;
}
h1 {
- color: rgba(0,0,0,.38);
- font-size: 1.037037em;
+ color: rgba(0, 0, 0, .38);
+ font-family: Roboto-Regular, sans-serif;
+ font-size: 0.93333em;
+ font-weight: normal;
line-height: 1.4em;
margin: 8px 0 8px;
}
@@ -55,10 +58,11 @@ h1 {
.icon {
background-image: url(images/blocked.svg);
+ background-position: center;
height: 20vh;
margin: 0 auto;
- max-height: 36px;
- max-width: 36px;
+ max-height: 24px;
+ max-width: 24px;
min-height: 18px;
min-width: 18px;
opacity: .54;
@@ -70,19 +74,17 @@ h1 {
box-sizing: border-box;
display: flex;
flex-direction: column;
- font-size: 0.9em;
height: 100vh;
justify-content: center;
line-height: 1.6em;
margin: 0 auto;
- max-width: 640px;
+ max-width: 480px;
padding: 16px;
width: 100%;
}
#main-content {
align-self: auto;
- color: rgba(0, 0, 0, .54);
flex: 0 1 auto;
text-align: center;
}
@@ -96,8 +98,12 @@ h1 {
@media (min-height:25em) and (min-width:37.5em),
(min-height:37.5em) and (min-width:25em) {
.icon {
- height: 36px;
- width: 36px;
+ max-height: 36px;
+ max-width: 36px;
+ }
+
+ .interstitial-wrapper {
+ padding: 48px;
}
}
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html b/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html
index 63de8891ab1..64cc0d25933 100644
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html
@@ -5,9 +5,10 @@
<meta name="viewport"
content="initial-scale=1, minimum-scale=1, width=device-width">
<title>$i18n{tabTitle}</title>
- <link rel="stylesheet" href="interstitial_common.css">
+ <link rel="stylesheet" href="../../common/resources/interstitial_core.css">
<link rel="stylesheet" href="interstitial_webview_quiet.css">
<script src="../../../../../ui/webui/resources/js/util.js"></script>
+ <script src="../../common/resources/interstitial_common.js"></script>
<script src="interstitial_webview_quiet.js"></script>
</head>
<body id="body">
@@ -17,13 +18,13 @@
<div id="main-message">
<h1>
<span>$i18n{heading}</span>
- <a id="details-link">$i18n{openDetails}</a>
+ <a href="#" id="details-link">$i18n{openDetails}</a>
</h1>
</div>
</div>
<div id="details" class="hidden">
<p>
- $i18Raw{explanationParagraph}
+ $i18nRaw{explanationParagraph}
</p>
</div>
</div>
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js b/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js
index cb189d7128a..e59990a73d4 100644
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js
@@ -2,9 +2,37 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+/**
+ * Restores the interstitial content to the initial state if the window size
+ * switches to a small view.
+ */
+function onResize() {
+ var mediaQuery = '(max-height:11.25em) and (max-width:18.75em),' +
+ '(max-height:18.75em) and (max-width:11.25em),' +
+ '(max-height:5em), (max-width:5em)';
+
+ // Check for change in window size.
+ if (window.matchMedia(mediaQuery).matches) {
+ var hiddenDetails = $('details').classList.add(HIDDEN_CLASS);
+ $('main-content').classList.remove(HIDDEN_CLASS);
+ }
+}
+
function initPage() {
var isGiantWebView = loadTimeData.getBoolean('is_giant');
document.body.className = isGiantWebView ? 'giant' : '';
+ preventDefaultOnPoundLinkClicks();
+
+ $('details-link').addEventListener('click', function(event) {
+ var hiddenDetails = $('details').classList.toggle(HIDDEN_CLASS);
+ $('main-content').classList.toggle(HIDDEN_CLASS, !hiddenDetails);
+ });
+
+ $('proceed-link').addEventListener('click', function(event) {
+ sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
+ });
+
+ window.addEventListener('resize', onResize);
}
document.addEventListener('DOMContentLoaded', initPage);
diff --git a/chromium/components/security_interstitials/core/browser/resources/list_of_interstitials.html b/chromium/components/security_interstitials/core/browser/resources/list_of_interstitials.html
new file mode 100644
index 00000000000..d9a13f8bf35
--- /dev/null
+++ b/chromium/components/security_interstitials/core/browser/resources/list_of_interstitials.html
@@ -0,0 +1,103 @@
+<html>
+<head>
+ <title>Interstitials</title>
+ <meta name="viewport" content="width=device-width">
+ <style>
+ body {
+ font-family: sans-serif;
+ line-height: 1.4;
+ }
+
+ h3, h4 {
+ margin-bottom: 0.5em;
+ }
+
+ ul {
+ margin-top: 0.5em;
+ }
+ </style>
+</head>
+<body>
+ <h2>Choose an interstitial</h2>
+ <h3>SSL</h3>
+ <ul>
+ <li>
+ <a href="ssl?overridable=1&strict_enforcement=0">example.com (generic, overridable)</a>
+ </li>
+ <li>
+ <a href="ssl?overridable=0&strict_enforcement=0">
+ example.com (generic, non-overridable)
+ </a>
+ </li>
+ <li>
+ <a href="ssl?overridable=0&strict_enforcement=1">
+ example.com (HSTS, non-overridable)
+ </a>
+ </li>
+ <li>
+ <a href="clock?clock_manipulation=2">Clock is ahead</a>
+ </li>
+ <li>
+ <a href="clock?clock_manipulation=-2">Clock is behind</a>
+ </li>
+ <li>
+ <a href="ssl?type=hpkp_failure">Pinned certificate error</a>
+ </li>
+ <li>
+ <a href="superfish-ssl">Superfish</a>
+ </li>
+ </ul>
+ <h3>SafeBrowsing</h3>
+ <h4>Loud</h4>
+ <ul>
+ <li>
+ <a href="safebrowsing?type=malware">Malware</a>
+ </li>
+ <li>
+ <a href="safebrowsing?type=phishing">Phishing</a>
+ </li>
+ <li>
+ <a href="safebrowsing?type=clientside_malware">Client Side Malware</a>
+ </li>
+ <li>
+ <a href="safebrowsing?type=clientside_phishing">Client Side Phishing</a>
+ </li>
+ </ul>
+ <h4>Quiet (WebView)</h4>
+ <ul>
+ <li>
+ <a href="quietsafebrowsing?type=malware">Malware</a>
+ </li>
+ <li>
+ <a href="quietsafebrowsing?type=phishing">Phishing</a>
+ </li>
+ <li>
+ <a href="quietsafebrowsing?type=giant">Giant</a>
+ </li>
+ </ul>
+ <h3>Captive Portal</h3>
+ <ul>
+ <li>
+ <a href="captiveportal">Captive Portal, Non-WiFi</a>
+ </li>
+ <li>
+ <a href="captiveportal?is_wifi=1">
+ Captive Portal, WiFi
+ </a>
+ </li>
+ <li>
+ <a href="captiveportal?is_wifi=1&wifi_name=CoffeeShopWiFi">
+ Captive Portal, WiFi with network name "CoffeeShopWiFi"
+ </a>
+ </li>
+ </ul>
+ <h3>Supervised Users</h3>
+ <ul>
+ <li>
+ <a href="supervised_user">
+ Supervised User
+ </a>
+ </li>
+ </ul>
+</body>
+</html>
diff --git a/chromium/components/security_interstitials/core/common/resources/interstitial_common.css b/chromium/components/security_interstitials/core/common/resources/interstitial_common.css
new file mode 100644
index 00000000000..764d5f7c3e8
--- /dev/null
+++ b/chromium/components/security_interstitials/core/common/resources/interstitial_common.css
@@ -0,0 +1,450 @@
+/* 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. */
+
+button {
+ border: 0;
+ border-radius: 2px;
+ box-sizing: border-box;
+ color: #fff;
+ cursor: pointer;
+ float: right;
+ font-size: .875em;
+ margin: 0;
+ padding: 10px 24px;
+ transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
+ user-select: none;
+}
+
+[dir='rtl'] button {
+ float: left;
+}
+
+.bad-clock button,
+.captive-portal button,
+.main-frame-blocked button,
+.neterror button,
+.offline button,
+.ssl button {
+ background: rgb(66, 133, 244);
+}
+
+button:active {
+ background: rgb(50, 102, 213);
+ outline: 0;
+}
+
+button:hover {
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .50);
+}
+
+#debugging {
+ display: inline;
+ overflow: auto;
+}
+
+.debugging-content {
+ line-height: 1em;
+ margin-bottom: 0;
+ margin-top: 1em;
+}
+
+.debugging-content-fixed-width {
+ display: block;
+ font-family: monospace;
+ font-size: 1.2em;
+ margin-top: 0.5em;
+}
+
+.debugging-title {
+ font-weight: bold;
+}
+
+#details {
+ color: #696969;
+ margin: 0 0 50px;
+}
+
+#details p:not(:first-of-type) {
+ margin-top: 20px;
+}
+
+#details-button:hover {
+ box-shadow: inherit;
+ text-decoration: underline;
+}
+
+.error-code {
+ color: #646464;
+ font-size: .86667em;
+ text-transform: uppercase;
+}
+
+#error-debugging-info {
+ font-size: 0.8em;
+}
+
+h1 {
+ color: #333;
+ font-size: 1.6em;
+ font-weight: normal;
+ line-height: 1.25em;
+ margin-bottom: 16px;
+}
+
+h2 {
+ font-size: 1.2em;
+ font-weight: normal;
+}
+
+.icon {
+ height: 72px;
+ margin: 0 0 40px;
+ width: 72px;
+}
+
+input[type=checkbox] {
+ opacity: 0;
+}
+
+input[type=checkbox]:focus ~ .checkbox {
+ outline: -webkit-focus-ring-color auto 5px;
+}
+
+.interstitial-wrapper {
+ box-sizing: border-box;
+ font-size: 1em;
+ line-height: 1.6em;
+ margin: 14vh auto 0;
+ max-width: 600px;
+ width: 100%;
+}
+
+#main-message > p {
+ display: inline;
+}
+
+#extended-reporting-opt-in {
+ font-size: .875em;
+ margin-top: 39px;
+}
+
+#extended-reporting-opt-in label {
+ position: relative;
+ display: flex;
+ align-items: flex-start;
+}
+
+.nav-wrapper {
+ margin-top: 51px;
+}
+
+.nav-wrapper::after {
+ clear: both;
+ content: '';
+ display: table;
+ width: 100%;
+}
+
+.small-link {
+ color: #696969;
+ font-size: .875em;
+}
+
+.checkboxes {
+ flex: 0 0 24px;
+}
+
+.checkbox {
+ background: transparent;
+ border: 1px solid white;
+ border-radius: 2px;
+ display: block;
+ height: 14px;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 3px;
+ width: 14px;
+}
+
+.checkbox::before {
+ background: transparent;
+ border: 2px solid white;
+ border-right-width: 0;
+ border-top-width: 0;
+ content: '';
+ height: 4px;
+ left: 2px;
+ opacity: 0;
+ position: absolute;
+ top: 3px;
+ transform: rotate(-45deg);
+ width: 9px;
+}
+
+input[type=checkbox]:checked ~ .checkbox::before {
+ opacity: 1;
+}
+
+@media (max-width: 700px) {
+ .interstitial-wrapper {
+ padding: 0 10%;
+ }
+
+ #error-debugging-info {
+ overflow: auto;
+ }
+}
+
+@media (max-height: 600px) {
+ .error-code {
+ margin-top: 10px;
+ }
+}
+
+@media (max-width: 420px) {
+ button,
+ [dir='rtl'] button,
+ .small-link {
+ float: none;
+ font-size: .825em;
+ font-weight: 400;
+ margin: 0;
+ text-transform: uppercase;
+ width: 100%;
+ }
+
+ #details {
+ margin: 20px 0 20px 0;
+ }
+
+ #details p:not(:first-of-type) {
+ margin-top: 10px;
+ }
+
+ #details-button {
+ display: block;
+ margin-top: 20px;
+ text-align: center;
+ width: 100%;
+ }
+
+ .interstitial-wrapper {
+ padding: 0 5%;
+ }
+
+ #extended-reporting-opt-in {
+ margin-top: 24px;
+ }
+
+ .nav-wrapper {
+ margin-top: 30px;
+ }
+}
+
+/**
+ * Mobile specific styling.
+ * Navigation buttons are anchored to the bottom of the screen.
+ * Details message replaces the top content in its own scrollable area.
+ */
+
+@media (max-width: 420px) {
+ #details-button {
+ border: 0;
+ margin: 28px 0 0;
+ }
+
+ .secondary-button {
+ -webkit-margin-end: 0;
+ margin-top: 16px;
+ }
+}
+
+/* Fixed nav. */
+@media (min-width: 240px) and (max-width: 420px) and
+ (min-height: 401px),
+ (min-width: 421px) and (min-height: 240px) and
+ (max-height: 560px) {
+ body .nav-wrapper {
+ background: #f7f7f7;
+ bottom: 0;
+ box-shadow: 0 -22px 40px rgb(247, 247, 247);
+ margin: 0;
+ max-width: 736px;
+ padding-left: 0px;
+ padding-right: 48px;
+ position: fixed;
+ z-index: 2;
+ }
+
+ .interstitial-wrapper {
+ max-width: 736px;
+ }
+
+ #details,
+ #main-content {
+ padding-bottom: 40px;
+ }
+
+ #details {
+ padding-top: 5.5vh;
+ }
+}
+
+@media (max-width: 420px) and (orientation: portrait),
+ (max-height: 560px) {
+ body {
+ margin: 0 auto;
+ }
+
+ button,
+ [dir='rtl'] button,
+ button.small-link {
+ font-family: Roboto-Regular,Helvetica;
+ font-size: .933em;
+ font-weight: 600;
+ margin: 6px 0;
+ text-transform: uppercase;
+ transform: translatez(0);
+ }
+
+ .nav-wrapper {
+ box-sizing: border-box;
+ padding-bottom: 8px;
+ width: 100%;
+ }
+
+ .error-code {
+ margin-top: 0;
+ }
+
+ #details {
+ box-sizing: border-box;
+ height: auto;
+ margin: 0;
+ opacity: 1;
+ transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);
+ }
+
+ #details.hidden,
+ #main-content.hidden {
+ display: block;
+ height: 0;
+ opacity: 0;
+ overflow: hidden;
+ padding-bottom: 0;
+ transition: none;
+ }
+
+ #details-button {
+ padding-bottom: 16px;
+ padding-top: 16px;
+ }
+
+ h1 {
+ font-size: 1.5em;
+ margin-bottom: 8px;
+ }
+
+ .icon {
+ margin-bottom: 5.69vh;
+ }
+
+ .interstitial-wrapper {
+ box-sizing: border-box;
+ margin: 7vh auto 12px;
+ padding: 0 24px;
+ position: relative;
+ }
+
+ .interstitial-wrapper p {
+ font-size: .95em;
+ line-height: 1.61em;
+ margin-top: 8px;
+ }
+
+ #main-content {
+ margin: 0;
+ transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1);
+ }
+
+ .small-link {
+ border: 0;
+ }
+
+ .suggested-left > #control-buttons,
+ .suggested-right > #control-buttons {
+ float: none;
+ margin: 0;
+ }
+}
+
+@media (min-width: 421px) and (min-height: 500px) and (max-height: 560px) {
+ .interstitial-wrapper {
+ margin-top: 10vh;
+ }
+}
+
+@media (min-height: 400px) and (orientation:portrait) {
+ .interstitial-wrapper {
+ margin-bottom: 145px;
+ }
+}
+
+@media (min-height: 299px) {
+ .nav-wrapper {
+ padding-bottom: 16px;
+ }
+}
+
+@media (min-height: 500px) and (max-height: 650px) and (max-width: 414px) and
+ (orientation: portrait) {
+ .interstitial-wrapper {
+ margin-top: 7vh;
+ }
+}
+
+@media (min-height: 650px) and (max-width: 414px) and (orientation: portrait) {
+ .interstitial-wrapper {
+ margin-top: 10vh;
+ }
+}
+
+/* Small mobile screens. No fixed nav. */
+@media (max-height: 400px) and (orientation: portrait),
+ (max-height: 239px) and (orientation: landscape),
+ (max-width: 419px) and (max-height: 399px) {
+ .interstitial-wrapper {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 0;
+ }
+
+ #details {
+ flex: 1 1 auto;
+ order: 0;
+ }
+
+ #main-content {
+ flex: 1 1 auto;
+ order: 0;
+ }
+
+ .nav-wrapper {
+ flex: 0 1 auto;
+ margin-top: 8px;
+ order: 1;
+ padding-left: 0;
+ padding-right: 0;
+ position: relative;
+ width: 100%;
+ }
+}
+
+@media (max-width: 239px) and (orientation: portrait) {
+ .nav-wrapper {
+ padding-left: 0;
+ padding-right: 0;
+ }
+}
diff --git a/chromium/components/security_interstitials/core/common/resources/interstitial_common.js b/chromium/components/security_interstitials/core/common/resources/interstitial_common.js
new file mode 100644
index 00000000000..8a4bf05ce61
--- /dev/null
+++ b/chromium/components/security_interstitials/core/common/resources/interstitial_common.js
@@ -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.
+
+// This is the shared code for security interstitials. It is used for both SSL
+// interstitials and Safe Browsing interstitials.
+
+// Should match security_interstitials::SecurityInterstitialCommands
+/** @enum| {string} */
+var SecurityInterstitialCommandId = {
+ CMD_DONT_PROCEED: 0,
+ CMD_PROCEED: 1,
+ // Ways for user to get more information
+ CMD_SHOW_MORE_SECTION: 2,
+ CMD_OPEN_HELP_CENTER: 3,
+ CMD_OPEN_DIAGNOSTIC: 4,
+ // Primary button actions
+ CMD_RELOAD: 5,
+ CMD_OPEN_DATE_SETTINGS: 6,
+ CMD_OPEN_LOGIN: 7,
+ // Safe Browsing Extended Reporting
+ CMD_DO_REPORT: 8,
+ CMD_DONT_REPORT: 9,
+ CMD_OPEN_REPORTING_PRIVACY: 10,
+ CMD_OPEN_WHITEPAPER: 11,
+ // Report a phishing error.
+ CMD_REPORT_PHISHING_ERROR: 12
+};
+
+var HIDDEN_CLASS = 'hidden';
+
+/**
+ * A convenience method for sending commands to the parent page.
+ * @param {string} cmd The command to send.
+ */
+function sendCommand(cmd) {
+// <if expr="not is_ios">
+ window.domAutomationController.send(cmd);
+// </if>
+// <if expr="is_ios">
+ // TODO(crbug.com/565877): Revisit message passing for WKWebView.
+ var iframe = document.createElement('IFRAME');
+ iframe.setAttribute('src', 'js-command:' + cmd);
+ document.documentElement.appendChild(iframe);
+ iframe.parentNode.removeChild(iframe);
+// </if>
+}
+
+/**
+ * Call this to stop clicks on <a href="#"> links from scrolling to the top of
+ * the page (and possibly showing a # in the link).
+ */
+function preventDefaultOnPoundLinkClicks() {
+ document.addEventListener('click', function(e) {
+ var anchor = findAncestor(/** @type {Node} */ (e.target), function(el) {
+ return el.tagName == 'A';
+ });
+ // Use getAttribute() to prevent URL normalization.
+ if (anchor && anchor.getAttribute('href') == '#')
+ e.preventDefault();
+ });
+}
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_common.css b/chromium/components/security_interstitials/core/common/resources/interstitial_core.css
index 32a27b997ff..32a27b997ff 100644
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_common.css
+++ b/chromium/components/security_interstitials/core/common/resources/interstitial_core.css
diff --git a/chromium/components/security_interstitials/core/common/resources/interstitial_mobile_nav.js b/chromium/components/security_interstitials/core/common/resources/interstitial_mobile_nav.js
new file mode 100644
index 00000000000..433977e2a78
--- /dev/null
+++ b/chromium/components/security_interstitials/core/common/resources/interstitial_mobile_nav.js
@@ -0,0 +1,49 @@
+// 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.
+
+var mobileNav = false;
+
+/**
+ * For small screen mobile the navigation buttons are moved
+ * below the advanced text.
+ */
+function onResize() {
+ var helpOuterBox = document.querySelector('#details');
+ var mainContent = document.querySelector('#main-content');
+ var mediaQuery = '(min-width: 240px) and (max-width: 420px) and ' +
+ '(min-height: 401px), ' +
+ '(max-height: 560px) and (min-height: 240px) and ' +
+ '(min-width: 421px)';
+
+ var detailsHidden = helpOuterBox.classList.contains(HIDDEN_CLASS);
+ var runnerContainer = document.querySelector('.runner-container');
+
+ // Check for change in nav status.
+ if (mobileNav != window.matchMedia(mediaQuery).matches) {
+ mobileNav = !mobileNav;
+
+ // Handle showing the top content / details sections according to state.
+ if (mobileNav) {
+ mainContent.classList.toggle(HIDDEN_CLASS, !detailsHidden);
+ helpOuterBox.classList.toggle(HIDDEN_CLASS, detailsHidden);
+ if (runnerContainer) {
+ runnerContainer.classList.toggle(HIDDEN_CLASS, !detailsHidden);
+ }
+ } else if (!detailsHidden) {
+ // Non mobile nav with visible details.
+ mainContent.classList.remove(HIDDEN_CLASS);
+ helpOuterBox.classList.remove(HIDDEN_CLASS);
+ if (runnerContainer) {
+ runnerContainer.classList.remove(HIDDEN_CLASS);
+ }
+ }
+ }
+}
+
+function setupMobileNav() {
+ window.addEventListener('resize', onResize);
+ onResize();
+}
+
+document.addEventListener('DOMContentLoaded', setupMobileNav);
diff --git a/chromium/components/security_interstitials/core/controller_client.cc b/chromium/components/security_interstitials/core/controller_client.cc
index 58ff99d23e9..09fa690c303 100644
--- a/chromium/components/security_interstitials/core/controller_client.cc
+++ b/chromium/components/security_interstitials/core/controller_client.cc
@@ -9,9 +9,9 @@
#include "components/google/core/browser/google_util.h"
#include "components/prefs/pref_service.h"
#include "components/security_interstitials/core/metrics_helper.h"
+#include "components/security_interstitials/core/urls.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
-#include "url/gurl.h"
namespace security_interstitials {
@@ -21,10 +21,12 @@ const char kOptInLink[] = "optInLink";
const char kPrivacyLinkHtml[] =
"<a id=\"privacy-link\" href=\"#\" onclick=\"sendCommand(%d); "
"return false;\" onmousedown=\"return false;\">%s</a>";
+const char kHelpCenterUrl[] = "https://support.google.com/chrome/";
ControllerClient::ControllerClient(
std::unique_ptr<MetricsHelper> metrics_helper)
- : metrics_helper_(std::move(metrics_helper)) {}
+ : metrics_helper_(std::move(metrics_helper)),
+ help_center_url_(kHelpCenterUrl) {}
ControllerClient::~ControllerClient() {}
@@ -33,28 +35,45 @@ MetricsHelper* ControllerClient::metrics_helper() const {
}
void ControllerClient::SetReportingPreference(bool report) {
+ DCHECK(GetPrefService());
GetPrefService()->SetBoolean(GetExtendedReportingPrefName(), report);
metrics_helper_->RecordUserInteraction(
report ? MetricsHelper::SET_EXTENDED_REPORTING_ENABLED
: MetricsHelper::SET_EXTENDED_REPORTING_DISABLED);
}
-void ControllerClient::OpenExtendedReportingPrivacyPolicy() {
+void ControllerClient::OpenExtendedReportingPrivacyPolicy(
+ bool open_links_in_new_tab) {
metrics_helper_->RecordUserInteraction(MetricsHelper::SHOW_PRIVACY_POLICY);
- GURL privacy_url(
- l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_URL));
+ GURL privacy_url(kSafeBrowsingPrivacyPolicyUrl);
privacy_url =
google_util::AppendGoogleLocaleParam(privacy_url, GetApplicationLocale());
- OpenUrlInCurrentTab(privacy_url);
+ OpenURL(open_links_in_new_tab, privacy_url);
}
-void ControllerClient::OpenExtendedReportingWhitepaper() {
+void ControllerClient::OpenExtendedReportingWhitepaper(
+ bool open_links_in_new_tab) {
metrics_helper_->RecordUserInteraction(MetricsHelper::SHOW_WHITEPAPER);
- GURL whitepaper_url(
- l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_WHITEPAPER_URL));
+ GURL whitepaper_url(kSafeBrowsingWhitePaperUrl);
whitepaper_url = google_util::AppendGoogleLocaleParam(whitepaper_url,
GetApplicationLocale());
- OpenUrlInCurrentTab(whitepaper_url);
+ OpenURL(open_links_in_new_tab, whitepaper_url);
+}
+
+void ControllerClient::OpenURL(bool open_links_in_new_tab, const GURL& url) {
+ if (open_links_in_new_tab) {
+ OpenUrlInNewForegroundTab(url);
+ } else {
+ OpenUrlInCurrentTab(url);
+ }
+}
+
+GURL ControllerClient::GetBaseHelpCenterUrl() const {
+ return help_center_url_;
+}
+
+void ControllerClient::SetBaseHelpCenterUrlForTesting(const GURL& test_url) {
+ help_center_url_ = test_url;
}
} // namespace security_interstitials
diff --git a/chromium/components/security_interstitials/core/controller_client.h b/chromium/components/security_interstitials/core/controller_client.h
index 613c6bc9f7f..c9a0daa10ec 100644
--- a/chromium/components/security_interstitials/core/controller_client.h
+++ b/chromium/components/security_interstitials/core/controller_client.h
@@ -9,8 +9,8 @@
#include <string>
#include "base/macros.h"
+#include "url/gurl.h"
-class GURL;
class PrefService;
namespace security_interstitials {
@@ -61,8 +61,13 @@ class ControllerClient {
// Handle the user's reporting preferences.
void SetReportingPreference(bool report);
- void OpenExtendedReportingPrivacyPolicy();
- void OpenExtendedReportingWhitepaper();
+
+ void OpenExtendedReportingPrivacyPolicy(bool open_links_in_new_tab);
+ void OpenExtendedReportingWhitepaper(bool open_links_in_new_tab);
+
+ // Helper method which either opens a URL in a new tab or a the current tab
+ // based on the display options setting.
+ void OpenURL(bool open_links_in_new_tab, const GURL& url);
// If available, open the operating system's date/time settings.
virtual bool CanLaunchDateAndTimeSettings() = 0;
@@ -89,15 +94,23 @@ class ControllerClient {
virtual void OpenUrlInCurrentTab(const GURL& url) = 0;
+ virtual void OpenUrlInNewForegroundTab(const GURL& url) = 0;
+
virtual PrefService* GetPrefService() = 0;
virtual const std::string& GetApplicationLocale() const = 0;
+ GURL GetBaseHelpCenterUrl() const;
+
+ void SetBaseHelpCenterUrlForTesting(const GURL& test_url);
+
protected:
virtual const std::string GetExtendedReportingPrefName() const = 0;
private:
std::unique_ptr<MetricsHelper> metrics_helper_;
+ // Link to the help center.
+ GURL help_center_url_;
DISALLOW_COPY_AND_ASSIGN(ControllerClient);
};
diff --git a/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc b/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
index 12e67e3f66a..f34abee5104 100644
--- a/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
+++ b/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
@@ -20,9 +20,6 @@
namespace security_interstitials {
namespace {
-// URL for the Help Center
-const char kLearnMore[] = "https://support.google.com/chrome/";
-
// For malware interstitial pages, we link the problematic URL to Google's
// diagnostic page.
#if defined(GOOGLE_CHROME_BUILD)
@@ -70,9 +67,10 @@ SafeBrowsingLoudErrorUI::SafeBrowsingLoudErrorUI(
controller->metrics_helper()->RecordUserDecision(MetricsHelper::SHOW);
controller->metrics_helper()->RecordUserInteraction(
MetricsHelper::TOTAL_VISITS);
- if (is_proceed_anyway_disabled())
+ if (is_proceed_anyway_disabled()) {
controller->metrics_helper()->RecordUserDecision(
security_interstitials::MetricsHelper::PROCEEDING_DISABLED);
+ }
}
SafeBrowsingLoudErrorUI::~SafeBrowsingLoudErrorUI() {
@@ -165,12 +163,13 @@ void SafeBrowsingLoudErrorUI::HandleCommand(
// User pressed "Learn more".
controller()->metrics_helper()->RecordUserInteraction(
security_interstitials::MetricsHelper::SHOW_LEARN_MORE);
- GURL learn_more_url(kLearnMore);
+
+ GURL learn_more_url = controller()->GetBaseHelpCenterUrl();
learn_more_url = net::AppendQueryParameter(
learn_more_url, "p", get_help_center_article_link());
learn_more_url =
google_util::AppendGoogleLocaleParam(learn_more_url, app_locale());
- controller()->OpenUrlInCurrentTab(learn_more_url);
+ controller()->OpenURL(should_open_links_in_new_tab(), learn_more_url);
break;
}
case CMD_RELOAD: {
@@ -181,11 +180,13 @@ void SafeBrowsingLoudErrorUI::HandleCommand(
}
case CMD_OPEN_REPORTING_PRIVACY: {
// User pressed on the SB Extended Reporting "privacy policy" link.
- controller()->OpenExtendedReportingPrivacyPolicy();
+ controller()->OpenExtendedReportingPrivacyPolicy(
+ should_open_links_in_new_tab());
break;
}
case CMD_OPEN_WHITEPAPER: {
- controller()->OpenExtendedReportingWhitepaper();
+ controller()->OpenExtendedReportingWhitepaper(
+ should_open_links_in_new_tab());
break;
}
case CMD_OPEN_DIAGNOSTIC: {
@@ -197,7 +198,7 @@ void SafeBrowsingLoudErrorUI::HandleCommand(
GURL diagnostic_url(diagnostic);
diagnostic_url =
google_util::AppendGoogleLocaleParam(diagnostic_url, app_locale());
- controller()->OpenUrlInCurrentTab(diagnostic_url);
+ controller()->OpenURL(should_open_links_in_new_tab(), diagnostic_url);
break;
}
case CMD_REPORT_PHISHING_ERROR: {
@@ -206,7 +207,7 @@ void SafeBrowsingLoudErrorUI::HandleCommand(
GURL phishing_error_url(kReportPhishingErrorUrl);
phishing_error_url = google_util::AppendGoogleLocaleParam(
phishing_error_url, app_locale());
- controller()->OpenUrlInCurrentTab(phishing_error_url);
+ controller()->OpenURL(should_open_links_in_new_tab(), phishing_error_url);
break;
}
case CMD_OPEN_DATE_SETTINGS:
@@ -288,8 +289,9 @@ void SafeBrowsingLoudErrorUI::PopulateExtendedReportingOption(
bool can_show_extended_reporting_option = CanShowExtendedReportingOption();
load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox,
can_show_extended_reporting_option);
- if (!can_show_extended_reporting_option)
+ if (!can_show_extended_reporting_option) {
return;
+ }
const std::string privacy_link = base::StringPrintf(
security_interstitials::kPrivacyLinkHtml,
diff --git a/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc b/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
index 6afb802058e..c7e9a8006a4 100644
--- a/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
+++ b/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
@@ -90,7 +90,34 @@ void SafeBrowsingQuietErrorUI::SetGiantWebViewForTesting(
void SafeBrowsingQuietErrorUI::HandleCommand(
SecurityInterstitialCommands command) {
- NOTREACHED();
+ switch (command) {
+ case CMD_PROCEED: {
+ // User pressed on the button to proceed.
+ if (!is_proceed_anyway_disabled()) {
+ controller()->metrics_helper()->RecordUserDecision(
+ MetricsHelper::PROCEED);
+ controller()->Proceed();
+ }
+ break;
+ }
+ case CMD_DONT_PROCEED:
+ case CMD_DO_REPORT:
+ case CMD_DONT_REPORT:
+ case CMD_SHOW_MORE_SECTION:
+ case CMD_OPEN_HELP_CENTER:
+ case CMD_RELOAD:
+ case CMD_OPEN_REPORTING_PRIVACY:
+ case CMD_OPEN_WHITEPAPER:
+ case CMD_OPEN_DIAGNOSTIC:
+ case CMD_REPORT_PHISHING_ERROR:
+ case CMD_OPEN_DATE_SETTINGS:
+ case CMD_OPEN_LOGIN:
+ case CMD_ERROR:
+ case CMD_TEXT_FOUND:
+ case CMD_TEXT_NOT_FOUND:
+ NOTREACHED();
+ break;
+ }
}
int SafeBrowsingQuietErrorUI::GetHTMLTemplateId() const {
diff --git a/chromium/components/security_interstitials/core/ssl_error_ui.cc b/chromium/components/security_interstitials/core/ssl_error_ui.cc
index 1cde12396de..90734a04a4f 100644
--- a/chromium/components/security_interstitials/core/ssl_error_ui.cc
+++ b/chromium/components/security_interstitials/core/ssl_error_ui.cc
@@ -15,8 +15,8 @@
namespace security_interstitials {
namespace {
-// URL for help page.
-const char kHelpURL[] = "https://support.google.com/chrome/answer/6098869";
+// Path to the relevant help center page.
+const char kHelpPath[] = "answer/6098869";
bool IsMasked(int options, SSLErrorUI::SSLErrorOptionsMask mask) {
return ((options & mask) != 0);
@@ -86,6 +86,22 @@ void SSLErrorUI::PopulateStringsForHTML(base::DictionaryValue* load_time_data) {
PopulateNonOverridableStrings(load_time_data);
}
+const net::SSLInfo& SSLErrorUI::ssl_info() const {
+ return ssl_info_;
+}
+
+const base::Time& SSLErrorUI::time_triggered() const {
+ return time_triggered_;
+}
+
+ControllerClient* SSLErrorUI::controller() const {
+ return controller_;
+}
+
+int SSLErrorUI::cert_error() const {
+ return cert_error_;
+}
+
void SSLErrorUI::PopulateOverridableStrings(
base::DictionaryValue* load_time_data) {
DCHECK(soft_override_enabled_);
@@ -174,7 +190,8 @@ void SSLErrorUI::HandleCommand(SecurityInterstitialCommands command) {
case CMD_OPEN_HELP_CENTER:
controller_->metrics_helper()->RecordUserInteraction(
security_interstitials::MetricsHelper::SHOW_LEARN_MORE);
- controller_->OpenUrlInCurrentTab(GURL(kHelpURL));
+ controller_->OpenUrlInNewForegroundTab(
+ controller_->GetBaseHelpCenterUrl().Resolve(kHelpPath));
break;
case CMD_RELOAD:
controller_->metrics_helper()->RecordUserInteraction(
@@ -182,10 +199,10 @@ void SSLErrorUI::HandleCommand(SecurityInterstitialCommands command) {
controller_->Reload();
break;
case CMD_OPEN_REPORTING_PRIVACY:
- controller_->OpenExtendedReportingPrivacyPolicy();
+ controller_->OpenExtendedReportingPrivacyPolicy(true);
break;
case CMD_OPEN_WHITEPAPER:
- controller_->OpenExtendedReportingWhitepaper();
+ controller_->OpenExtendedReportingWhitepaper(true);
break;
case CMD_OPEN_DATE_SETTINGS:
case CMD_OPEN_DIAGNOSTIC:
diff --git a/chromium/components/security_interstitials/core/ssl_error_ui.h b/chromium/components/security_interstitials/core/ssl_error_ui.h
index d18127bb1a3..36b766c42e9 100644
--- a/chromium/components/security_interstitials/core/ssl_error_ui.h
+++ b/chromium/components/security_interstitials/core/ssl_error_ui.h
@@ -45,10 +45,16 @@ class SSLErrorUI {
int display_options, // Bitmask of SSLErrorOptionsMask values.
const base::Time& time_triggered,
ControllerClient* controller);
- ~SSLErrorUI();
+ virtual ~SSLErrorUI();
- void PopulateStringsForHTML(base::DictionaryValue* load_time_data);
- void HandleCommand(SecurityInterstitialCommands command);
+ virtual void PopulateStringsForHTML(base::DictionaryValue* load_time_data);
+ virtual void HandleCommand(SecurityInterstitialCommands command);
+
+ protected:
+ const net::SSLInfo& ssl_info() const;
+ const base::Time& time_triggered() const;
+ ControllerClient* controller() const;
+ int cert_error() const;
private:
void PopulateOverridableStrings(base::DictionaryValue* load_time_data);
diff --git a/chromium/components/security_interstitials/core/superfish_error_ui.cc b/chromium/components/security_interstitials/core/superfish_error_ui.cc
new file mode 100644
index 00000000000..2124319deab
--- /dev/null
+++ b/chromium/components/security_interstitials/core/superfish_error_ui.cc
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/security_interstitials/core/superfish_error_ui.h"
+
+#include "components/security_interstitials/core/common_string_util.h"
+#include "components/security_interstitials/core/controller_client.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace security_interstitials {
+
+namespace {
+
+// URL for Superfish-specific help page.
+const char kHelpURL[] = "https://support.google.com/chrome/?p=superfish";
+
+} // namespace
+
+SuperfishErrorUI::SuperfishErrorUI(
+ const GURL& request_url,
+ int cert_error,
+ const net::SSLInfo& ssl_info,
+ int display_options, // Bitmask of SSLErrorOptionsMask values.
+ const base::Time& time_triggered,
+ ControllerClient* controller)
+ : SSLErrorUI(request_url,
+ cert_error,
+ ssl_info,
+ display_options,
+ time_triggered,
+ controller) {}
+
+void SuperfishErrorUI::PopulateStringsForHTML(
+ base::DictionaryValue* load_time_data) {
+ common_string_util::PopulateSSLDebuggingStrings(ssl_info(), time_triggered(),
+ load_time_data);
+
+ load_time_data->SetString("type", "SSL");
+ load_time_data->SetString("errorCode", net::ErrorToString(cert_error()));
+ load_time_data->SetBoolean("overridable", false);
+ load_time_data->SetBoolean("bad_clock", false);
+ load_time_data->SetBoolean("hide_primary_button", true);
+ load_time_data->SetString("tabTitle",
+ l10n_util::GetStringUTF16(IDS_SSL_V2_TITLE));
+ load_time_data->SetString(
+ "heading", l10n_util::GetStringUTF16(IDS_SSL_SUPERFISH_HEADING));
+ load_time_data->SetString(
+ "primaryParagraph",
+ l10n_util::GetStringUTF16(IDS_SSL_SUPERFISH_PRIMARY_PARAGRAPH));
+
+ // Fill in empty values for normal SSL error strings that aren't used on this
+ // interstitial.
+ load_time_data->SetString("explanationParagraph", std::string());
+ load_time_data->SetString("primaryButtonText", std::string());
+ load_time_data->SetString("finalParagraph", std::string());
+ load_time_data->SetString("openDetails", base::string16());
+ load_time_data->SetString("closeDetails", base::string16());
+}
+
+void SuperfishErrorUI::HandleCommand(SecurityInterstitialCommands command) {
+ // Override the Help Center link to point to a Superfish-specific page.
+ if (command == CMD_OPEN_HELP_CENTER) {
+ controller()->metrics_helper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::SHOW_LEARN_MORE);
+ controller()->OpenUrlInNewForegroundTab(GURL(kHelpURL));
+ return;
+ }
+ SSLErrorUI::HandleCommand(command);
+}
+
+} // namespace security_interstitials
diff --git a/chromium/components/security_interstitials/core/superfish_error_ui.h b/chromium/components/security_interstitials/core/superfish_error_ui.h
new file mode 100644
index 00000000000..efd984acb5e
--- /dev/null
+++ b/chromium/components/security_interstitials/core/superfish_error_ui.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CORE_SUPERFISH_ERROR_UI_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CORE_SUPERFISH_ERROR_UI_H_
+
+#include "components/security_interstitials/core/ssl_error_ui.h"
+
+namespace security_interstitials {
+
+class ControllerClient;
+
+// A subclass of SSLErrorUI that customizes the error UI with instructions for
+// uninstalling the Superfish software.
+class SuperfishErrorUI : public SSLErrorUI {
+ public:
+ SuperfishErrorUI(
+ const GURL& request_url,
+ int cert_error,
+ const net::SSLInfo& ssl_info,
+ int display_options, // Bitmask of SSLErrorOptionsMask values.
+ const base::Time& time_triggered,
+ ControllerClient* controller);
+ ~SuperfishErrorUI() override {}
+
+ void PopulateStringsForHTML(base::DictionaryValue* load_time_data) override;
+ void HandleCommand(SecurityInterstitialCommands command) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SuperfishErrorUI);
+};
+
+} // namespace security_interstitials
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CORE_SUPERFISH_ERROR_UI_H_
diff --git a/chromium/components/security_interstitials/core/urls.cc b/chromium/components/security_interstitials/core/urls.cc
new file mode 100644
index 00000000000..8e74bb3eb87
--- /dev/null
+++ b/chromium/components/security_interstitials/core/urls.cc
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/security_interstitials/core/urls.h"
+
+namespace security_interstitials {
+
+const char kSafeBrowsingPrivacyPolicyUrl[] =
+ "https://www.google.com/chrome/browser/privacy/#safe-browsing-policies";
+const char kSafeBrowsingWhitePaperUrl[] =
+ "https://www.google.com/chrome/browser/privacy/"
+ "whitepaper.html#extendedreport";
+
+} // namespace security_interstitials
diff --git a/chromium/components/security_interstitials/core/urls.h b/chromium/components/security_interstitials/core/urls.h
new file mode 100644
index 00000000000..3951e6004d3
--- /dev/null
+++ b/chromium/components/security_interstitials/core/urls.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CORE_URLS_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CORE_URLS_H_
+
+namespace security_interstitials {
+
+// Using strings in code for non-translatable strings decreases binary size
+extern const char kSafeBrowsingPrivacyPolicyUrl[];
+extern const char kSafeBrowsingWhitePaperUrl[];
+
+} // namespace security_interstitials
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CORE_URLS_H_
diff --git a/chromium/components/security_interstitials_strings.grdp b/chromium/components/security_interstitials_strings.grdp
index abe7d06f4a6..8d44d5de458 100644
--- a/chromium/components/security_interstitials_strings.grdp
+++ b/chromium/components/security_interstitials_strings.grdp
@@ -49,14 +49,6 @@
</message>
</if>
- <!-- Extended reporting strings -->
- <message name="IDS_SAFE_BROWSING_PRIVACY_POLICY_URL" translateable="false">
- https://www.google.com/chrome/browser/privacy/#safe-browsing-policies
- </message>
- <message name="IDS_SAFE_BROWSING_WHITEPAPER_URL" translateable="false">
- https://www.google.com/chrome/browser/privacy/whitepaper.html#extendedreport
- </message>
-
<!-- SSL error page -->
<message name="IDS_SSL_V2_TITLE" desc="The tab title for the SSL interstitial.">
Privacy error
@@ -68,6 +60,24 @@
Attackers might be trying to steal your information from <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>google.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> (for example, passwords, messages, or credit cards). <ph name="BEGIN_LEARN_MORE_LINK">&lt;a href="#" id="learn-more-link"&gt;</ph>Learn more<ph name="END_LEARN_MORE_LINK">&lt;/a&gt;</ph>
</message>
+ <!-- SSL error page: Superfish-specific -->
+ <message name="IDS_SSL_SUPERFISH_HEADING" desc="The large heading at the top of the Superfish-specific SSL interstitial.">
+ Software on your computer is stopping Chrome from safely connecting to the web
+ </message>
+ <message name="IDS_SSL_SUPERFISH_PRIMARY_PARAGRAPH" desc="The primary explanatory paragraph for the Superfish-specific SSL interstitial.">
+ <ph name="BEGIN_PARAGRAPH">&lt;p&gt;</ph>Follow these steps to temporarily disable the software so you can get on the web. You'll need administrator privileges.<ph name="END_PARAGRAPH">&lt;/p&gt;</ph>
+
+ <ph name="BEGIN_LIST">&lt;ol&gt;</ph>
+ <ph name="LIST_ITEM">&lt;li&gt;</ph>Click <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>Start<ph name="END_BOLD">&lt;/strong&gt;</ph>, then search for and select <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>"View local services"<ph name="END_BOLD">&lt;/strong&gt;</ph>
+ <ph name="LIST_ITEM">&lt;li&gt;</ph>Select <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>VisualDiscovery<ph name="END_BOLD">&lt;/strong&gt;</ph>
+ <ph name="LIST_ITEM">&lt;li&gt;</ph>Under <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>Startup type<ph name="END_BOLD">&lt;/strong&gt;</ph>, select <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>Disabled<ph name="END_BOLD">&lt;/strong&gt;</ph>
+ <ph name="LIST_ITEM">&lt;li&gt;</ph>Under <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>Service status<ph name="END_BOLD">&lt;/strong&gt;</ph>, click <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>Stop<ph name="END_BOLD">&lt;/strong&gt;</ph>
+ <ph name="LIST_ITEM">&lt;li&gt;</ph>Click <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>Apply<ph name="END_BOLD">&lt;/strong&gt;</ph>, then click <ph name="BEGIN_BOLD">&lt;strong&gt;</ph>OK<ph name="END_BOLD">&lt;/strong&gt;</ph>
+ <ph name="LIST_ITEM">&lt;li&gt;</ph>Visit the <ph name="BEGIN_LEARN_MORE_LINK">&lt;a href="#" id="learn-more-link"&gt;</ph>Chrome help center<ph name="END_LEARN_MORE_LINK">&lt;/a&gt;</ph> to learn how to permanently remove the software from your computer
+ <ph name="END_LIST">&lt;/ol&gt;</ph>
+
+ </message>
+
<!-- SSL error page: overridable -->
<message name="IDS_SSL_OVERRIDABLE_SAFETY_BUTTON" desc="The text for the button that takes the user back to the previous page.">
Back to safety
@@ -218,16 +228,16 @@
<!-- WebView Safe Browsing quiet interstitals medium sized -->
<message name="IDS_MALWARE_WEBVIEW_HEADING" desc="The heading of the malware interstitial on medium sized Webview.">
- Dangerous content blocked
+ Dangerous content blocked.
</message>
<message name="IDS_MALWARE_WEBVIEW_EXPLANATION_PARAGRAPH" desc="The explanation of why Safe Browsing has blocked the page. Allows the user to proceed using a link.">
- This content might try to install dangerous software on your device that steals or deletes your information. <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>Show anyway<ph name="END_LINK">&lt;/a&gt;</ph>.
+ This content might try to install dangerous software on your device that steals or deletes your information. <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>Show anyway<ph name="END_LINK">&lt;/a&gt;</ph>
</message>
<message name="IDS_PHISHING_WEBVIEW_HEADING" desc="The heading of the phishing interstitial on medium sized Webview.">
- Deceptive content blocked
+ Deceptive content blocked.
</message>
<message name="IDS_PHISHING_WEBVIEW_EXPLANATION_PARAGRAPH" desc="The explanation of why Safe Browsing has blocked the page. Allows the user to proceed using a link.">
- This content might try to trick you into installing software or revealing personal information. <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>Show anyway<ph name="END_LINK">&lt;/a&gt;</ph>.
+ This content might try to trick you into installing software or revealing personal information. <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>Show anyway<ph name="END_LINK">&lt;/a&gt;</ph>
</message>
</grit-part>
diff --git a/chromium/components/security_state/DEPS b/chromium/components/security_state/DEPS
index b2f6f8ed02e..f0420255e92 100644
--- a/chromium/components/security_state/DEPS
+++ b/chromium/components/security_state/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+net",
+ "+third_party/WebKit/public/platform",
] \ No newline at end of file
diff --git a/chromium/components/security_state/content/content_utils.cc b/chromium/components/security_state/content/content_utils.cc
index 27986e01d39..2832bf0c95f 100644
--- a/chromium/components/security_state/content/content_utils.cc
+++ b/chromium/components/security_state/content/content_utils.cc
@@ -23,6 +23,7 @@
#include "net/cert/x509_certificate.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
+#include "third_party/WebKit/public/platform/WebMixedContentContextType.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "ui/base/l10n/l10n_util.h"
@@ -52,7 +53,117 @@ blink::WebSecurityStyle SecurityLevelToSecurityStyle(
return blink::kWebSecurityStyleUnknown;
}
-void AddConnectionExplanation(
+void ExplainHTTPSecurity(
+ const security_state::SecurityInfo& security_info,
+ content::SecurityStyleExplanations* security_style_explanations) {
+ if (security_info.security_level == security_state::HTTP_SHOW_WARNING) {
+ if (security_info.displayed_password_field_on_http ||
+ security_info.displayed_credit_card_field_on_http) {
+ security_style_explanations->neutral_explanations.push_back(
+ content::SecurityStyleExplanation(
+ l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT),
+ l10n_util::GetStringUTF8(
+ IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION)));
+ }
+ if (security_info.incognito_downgraded_security_level) {
+ security_style_explanations->neutral_explanations.push_back(
+ content::SecurityStyleExplanation(
+ l10n_util::GetStringUTF8(IDS_INCOGNITO_NONSECURE),
+ l10n_util::GetStringUTF8(IDS_INCOGNITO_NONSECURE_DESCRIPTION)));
+ }
+ }
+}
+
+void ExplainSafeBrowsingSecurity(
+ const security_state::SecurityInfo& security_info,
+ content::SecurityStyleExplanations* security_style_explanations) {
+ if (security_info.malicious_content_status !=
+ security_state::MALICIOUS_CONTENT_STATUS_NONE) {
+ security_style_explanations->summary =
+ l10n_util::GetStringUTF8(IDS_SAFEBROWSING_WARNING);
+ }
+}
+
+void ExplainCertificateSecurity(
+ const security_state::SecurityInfo& security_info,
+ content::SecurityStyleExplanations* security_style_explanations) {
+ if (security_info.sha1_in_chain) {
+ security_style_explanations->neutral_explanations.push_back(
+ content::SecurityStyleExplanation(
+ l10n_util::GetStringUTF8(IDS_SHA1),
+ l10n_util::GetStringUTF8(IDS_SHA1_DESCRIPTION),
+ !!security_info.certificate,
+ blink::WebMixedContentContextType::kNotMixedContent));
+ }
+
+ if (security_info.cert_missing_subject_alt_name) {
+ security_style_explanations->insecure_explanations.push_back(
+ content::SecurityStyleExplanation(
+ l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING),
+ l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING_DESCRIPTION),
+ !!security_info.certificate,
+ blink::WebMixedContentContextType::kNotMixedContent));
+ }
+
+ bool is_cert_status_error = net::IsCertStatusError(security_info.cert_status);
+ bool is_cert_status_minor_error =
+ net::IsCertStatusMinorError(security_info.cert_status);
+
+ if (is_cert_status_error) {
+ base::string16 error_string = base::UTF8ToUTF16(net::ErrorToString(
+ net::MapCertStatusToNetError(security_info.cert_status)));
+
+ content::SecurityStyleExplanation explanation(
+ l10n_util::GetStringUTF8(IDS_CERTIFICATE_CHAIN_ERROR),
+ l10n_util::GetStringFUTF8(
+ IDS_CERTIFICATE_CHAIN_ERROR_DESCRIPTION_FORMAT, error_string),
+ !!security_info.certificate,
+ blink::WebMixedContentContextType::kNotMixedContent);
+
+ if (is_cert_status_minor_error) {
+ security_style_explanations->neutral_explanations.push_back(explanation);
+ } else {
+ security_style_explanations->insecure_explanations.push_back(explanation);
+ }
+ } else {
+ // If the certificate does not have errors and is not using SHA1, then add
+ // an explanation that the certificate is valid.
+
+ base::string16 issuer_name;
+ if (security_info.certificate) {
+ // This results in the empty string if there is no relevant display name.
+ issuer_name = base::UTF8ToUTF16(
+ security_info.certificate->issuer().GetDisplayName());
+ } else {
+ issuer_name = base::string16();
+ }
+ if (issuer_name.empty()) {
+ issuer_name.assign(
+ l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY));
+ }
+
+ if (!security_info.sha1_in_chain) {
+ security_style_explanations->secure_explanations.push_back(
+ content::SecurityStyleExplanation(
+ l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE),
+ l10n_util::GetStringFUTF8(
+ IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION, issuer_name),
+ !!security_info.certificate,
+ blink::WebMixedContentContextType::kNotMixedContent));
+ }
+ }
+
+ security_style_explanations->pkp_bypassed = security_info.pkp_bypassed;
+ if (security_info.pkp_bypassed) {
+ security_style_explanations->info_explanations.push_back(
+ content::SecurityStyleExplanation(
+ l10n_util::GetStringUTF8(IDS_PRIVATE_KEY_PINNING_BYPASSED),
+ l10n_util::GetStringUTF8(
+ IDS_PRIVATE_KEY_PINNING_BYPASSED_DESCRIPTION)));
+ }
+}
+
+void ExplainConnectionSecurity(
const security_state::SecurityInfo& security_info,
content::SecurityStyleExplanations* security_style_explanations) {
// Avoid showing TLS details when we couldn't even establish a TLS connection
@@ -112,19 +223,19 @@ void AddConnectionExplanation(
str_id = (status & net::OBSOLETE_SSL_MASK_PROTOCOL)
? IDS_SSL_AN_OBSOLETE_PROTOCOL
: IDS_SSL_A_STRONG_PROTOCOL;
- description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
description_replacements.push_back(protocol_name);
+ description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
str_id = (status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE)
? IDS_SSL_AN_OBSOLETE_KEY_EXCHANGE
: IDS_SSL_A_STRONG_KEY_EXCHANGE;
- description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
description_replacements.push_back(key_exchange_name);
+ description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
str_id = (status & net::OBSOLETE_SSL_MASK_CIPHER) ? IDS_SSL_AN_OBSOLETE_CIPHER
: IDS_SSL_A_STRONG_CIPHER;
- description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
description_replacements.push_back(cipher_name);
+ description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
security_style_explanations->info_explanations.push_back(
content::SecurityStyleExplanation(
@@ -134,6 +245,54 @@ void AddConnectionExplanation(
description_replacements, nullptr))));
}
+void ExplainContentSecurity(
+ const security_state::SecurityInfo& security_info,
+ content::SecurityStyleExplanations* security_style_explanations) {
+ security_style_explanations->ran_insecure_content_style =
+ SecurityLevelToSecurityStyle(security_state::kRanInsecureContentLevel);
+ security_style_explanations->displayed_insecure_content_style =
+ SecurityLevelToSecurityStyle(
+ security_state::kDisplayedInsecureContentLevel);
+
+ // Record the presence of mixed content (HTTP subresources on an HTTPS
+ // page).
+ security_style_explanations->ran_mixed_content =
+ security_info.mixed_content_status ==
+ security_state::CONTENT_STATUS_RAN ||
+ security_info.mixed_content_status ==
+ security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
+ security_style_explanations->displayed_mixed_content =
+ security_info.mixed_content_status ==
+ security_state::CONTENT_STATUS_DISPLAYED ||
+ security_info.mixed_content_status ==
+ security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
+
+ security_style_explanations->contained_mixed_form =
+ security_info.contained_mixed_form;
+
+ // If the main resource was loaded with no certificate errors or only minor
+ // certificate errors, then record the presence of subresources with
+ // certificate errors. Subresource certificate errors aren't recorded when the
+ // main resource was loaded with major certificate errors because, in the
+ // common case, these subresource certificate errors would be duplicative with
+ // the main resource's error.
+ bool is_cert_status_error = net::IsCertStatusError(security_info.cert_status);
+ bool is_cert_status_minor_error =
+ net::IsCertStatusMinorError(security_info.cert_status);
+ if (!is_cert_status_error || is_cert_status_minor_error) {
+ security_style_explanations->ran_content_with_cert_errors =
+ security_info.content_with_cert_errors_status ==
+ security_state::CONTENT_STATUS_RAN ||
+ security_info.content_with_cert_errors_status ==
+ security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
+ security_style_explanations->displayed_content_with_cert_errors =
+ security_info.content_with_cert_errors_status ==
+ security_state::CONTENT_STATUS_DISPLAYED ||
+ security_info.content_with_cert_errors_status ==
+ security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
+ }
+}
+
} // namespace
std::unique_ptr<security_state::VisibleSecurityState> GetVisibleSecurityState(
@@ -182,27 +341,8 @@ blink::WebSecurityStyle GetSecurityStyle(
const blink::WebSecurityStyle security_style =
SecurityLevelToSecurityStyle(security_info.security_level);
- // The HTTP_SHOW_WARNING state may occur if the page is served as a data: URI
- // or if it is served non-securely AND contains a sensitive form field.
- if (security_info.security_level == security_state::HTTP_SHOW_WARNING &&
- (security_info.displayed_password_field_on_http ||
- security_info.displayed_credit_card_field_on_http)) {
- security_style_explanations->neutral_explanations.push_back(
- content::SecurityStyleExplanation(
- l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT),
- l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION)));
- }
- security_style_explanations->ran_insecure_content_style =
- SecurityLevelToSecurityStyle(security_state::kRanInsecureContentLevel);
- security_style_explanations->displayed_insecure_content_style =
- SecurityLevelToSecurityStyle(
- security_state::kDisplayedInsecureContentLevel);
-
- if (security_info.malicious_content_status !=
- security_state::MALICIOUS_CONTENT_STATUS_NONE) {
- security_style_explanations->summary =
- l10n_util::GetStringUTF8(IDS_SAFEBROWSING_WARNING);
- }
+ ExplainHTTPSecurity(security_info, security_style_explanations);
+ ExplainSafeBrowsingSecurity(security_info, security_style_explanations);
// Check if the page is HTTP; if so, no more explanations are needed. Note
// that SecurityStyleUnauthenticated does not necessarily mean that
@@ -217,99 +357,9 @@ blink::WebSecurityStyle GetSecurityStyle(
return security_style;
}
- if (security_info.sha1_in_chain) {
- security_style_explanations->neutral_explanations.push_back(
- content::SecurityStyleExplanation(
- l10n_util::GetStringUTF8(IDS_SHA1),
- l10n_util::GetStringUTF8(IDS_SHA1_DESCRIPTION),
- !!security_info.certificate));
- }
-
- if (security_info.cert_missing_subject_alt_name) {
- security_style_explanations->insecure_explanations.push_back(
- content::SecurityStyleExplanation(
- l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING),
- l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING_DESCRIPTION),
- !!security_info.certificate));
- }
-
- // Record the presence of mixed content (HTTP subresources on an HTTPS
- // page).
- security_style_explanations->ran_mixed_content =
- security_info.mixed_content_status ==
- security_state::CONTENT_STATUS_RAN ||
- security_info.mixed_content_status ==
- security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
- security_style_explanations->displayed_mixed_content =
- security_info.mixed_content_status ==
- security_state::CONTENT_STATUS_DISPLAYED ||
- security_info.mixed_content_status ==
- security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
-
- security_style_explanations->contained_mixed_form =
- security_info.contained_mixed_form;
-
- bool is_cert_status_error = net::IsCertStatusError(security_info.cert_status);
- bool is_cert_status_minor_error =
- net::IsCertStatusMinorError(security_info.cert_status);
-
- // If the main resource was loaded no certificate errors or only minor
- // certificate errors, then record the presence of subresources with
- // certificate errors. Subresource certificate errors aren't recorded
- // when the main resource was loaded with major certificate errors
- // because, in the common case, these subresource certificate errors
- // would be duplicative with the main resource's error.
- if (!is_cert_status_error || is_cert_status_minor_error) {
- security_style_explanations->ran_content_with_cert_errors =
- security_info.content_with_cert_errors_status ==
- security_state::CONTENT_STATUS_RAN ||
- security_info.content_with_cert_errors_status ==
- security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
- security_style_explanations->displayed_content_with_cert_errors =
- security_info.content_with_cert_errors_status ==
- security_state::CONTENT_STATUS_DISPLAYED ||
- security_info.content_with_cert_errors_status ==
- security_state::CONTENT_STATUS_DISPLAYED_AND_RAN;
- }
-
- if (is_cert_status_error) {
- base::string16 error_string = base::UTF8ToUTF16(net::ErrorToString(
- net::MapCertStatusToNetError(security_info.cert_status)));
-
- content::SecurityStyleExplanation explanation(
- l10n_util::GetStringUTF8(IDS_CERTIFICATE_CHAIN_ERROR),
- l10n_util::GetStringFUTF8(
- IDS_CERTIFICATE_CHAIN_ERROR_DESCRIPTION_FORMAT, error_string),
- !!security_info.certificate);
-
- if (is_cert_status_minor_error) {
- security_style_explanations->neutral_explanations.push_back(explanation);
- } else {
- security_style_explanations->insecure_explanations.push_back(explanation);
- }
- } else {
- // If the certificate does not have errors and is not using SHA1, then add
- // an explanation that the certificate is valid.
- if (!security_info.sha1_in_chain) {
- security_style_explanations->secure_explanations.push_back(
- content::SecurityStyleExplanation(
- l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE),
- l10n_util::GetStringUTF8(
- IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION),
- !!security_info.certificate));
- }
- }
-
- AddConnectionExplanation(security_info, security_style_explanations);
-
- security_style_explanations->pkp_bypassed = security_info.pkp_bypassed;
- if (security_info.pkp_bypassed) {
- security_style_explanations->info_explanations.push_back(
- content::SecurityStyleExplanation(
- l10n_util::GetStringUTF8(IDS_PRIVATE_KEY_PINNING_BYPASSED),
- l10n_util::GetStringUTF8(
- IDS_PRIVATE_KEY_PINNING_BYPASSED_DESCRIPTION)));
- }
+ ExplainCertificateSecurity(security_info, security_style_explanations);
+ ExplainConnectionSecurity(security_info, security_style_explanations);
+ ExplainContentSecurity(security_info, security_style_explanations);
return security_style;
}
diff --git a/chromium/components/security_state/content/content_utils_unittest.cc b/chromium/components/security_state/content/content_utils_unittest.cc
index e0e37e0a45f..e031970a874 100644
--- a/chromium/components/security_state/content/content_utils_unittest.cc
+++ b/chromium/components/security_state/content/content_utils_unittest.cc
@@ -4,6 +4,8 @@
#include "components/security_state/content/content_utils.h"
+#include <vector>
+
#include "base/command_line.h"
#include "base/test/histogram_tester.h"
#include "components/security_state/core/security_state.h"
@@ -16,6 +18,7 @@
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebMixedContentContextType.h"
namespace {
@@ -162,7 +165,7 @@ bool FindSecurityStyleExplanation(
return false;
}
-// Test that connection explanations are formated as expected. Note the strings
+// Test that connection explanations are formatted as expected. Note the strings
// are not translated and so will be the same in any locale.
TEST(SecurityStateContentUtilsTest, ConnectionExplanation) {
// Test a modern configuration with a key exchange group.
@@ -183,9 +186,9 @@ TEST(SecurityStateContentUtilsTest, ConnectionExplanation) {
ASSERT_TRUE(FindSecurityStyleExplanation(
explanations.secure_explanations, "Secure connection", &explanation));
EXPECT_EQ(
- "The connection to this site is encrypted and authenticated using a "
- "strong protocol (TLS 1.2), a strong key exchange (ECDHE_RSA with "
- "X25519), and a strong cipher (CHACHA20_POLY1305).",
+ "The connection to this site is encrypted and authenticated using TLS "
+ "1.2 (a strong protocol), ECDHE_RSA with X25519 (a strong key "
+ "exchange), and CHACHA20_POLY1305 (a strong cipher).",
explanation.description);
}
@@ -199,9 +202,9 @@ TEST(SecurityStateContentUtilsTest, ConnectionExplanation) {
ASSERT_TRUE(FindSecurityStyleExplanation(
explanations.secure_explanations, "Secure connection", &explanation));
EXPECT_EQ(
- "The connection to this site is encrypted and authenticated using a "
- "strong protocol (TLS 1.2), a strong key exchange (ECDHE_RSA), and a "
- "strong cipher (CHACHA20_POLY1305).",
+ "The connection to this site is encrypted and authenticated using TLS "
+ "1.2 (a strong protocol), ECDHE_RSA (a strong key exchange), and "
+ "CHACHA20_POLY1305 (a strong cipher).",
explanation.description);
}
@@ -218,9 +221,38 @@ TEST(SecurityStateContentUtilsTest, ConnectionExplanation) {
ASSERT_TRUE(FindSecurityStyleExplanation(
explanations.secure_explanations, "Secure connection", &explanation));
EXPECT_EQ(
- "The connection to this site is encrypted and authenticated using a "
- "strong protocol (TLS 1.3), a strong key exchange (X25519), and a "
- "strong cipher (AES_128_GCM).",
+ "The connection to this site is encrypted and authenticated using TLS "
+ "1.3 (a strong protocol), X25519 (a strong key exchange), and "
+ "AES_128_GCM (a strong cipher).",
+ explanation.description);
+ }
+}
+
+// Test that obsolete connection explanations are formatted as expected.
+TEST(SecurityStateContentUtilsTest, ObsoleteConnectionExplanation) {
+ security_state::SecurityInfo security_info;
+ security_info.cert_status = net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+ security_info.scheme_is_cryptographic = true;
+ net::SSLConnectionStatusSetCipherSuite(
+ 0xc013 /* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA */,
+ &security_info.connection_status);
+ net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2,
+ &security_info.connection_status);
+ security_info.key_exchange_group = 29; // X25519
+ security_info.obsolete_ssl_status =
+ net::ObsoleteSSLMask::OBSOLETE_SSL_MASK_CIPHER;
+
+ {
+ content::SecurityStyleExplanations explanations;
+ GetSecurityStyle(security_info, &explanations);
+ content::SecurityStyleExplanation explanation;
+ ASSERT_TRUE(FindSecurityStyleExplanation(explanations.info_explanations,
+ "Obsolete connection settings",
+ &explanation));
+ EXPECT_EQ(
+ "The connection to this site uses TLS 1.2 (a strong protocol), "
+ "ECDHE_RSA with X25519 (a strong key exchange), and AES_128_CBC with "
+ "HMAC-SHA1 (an obsolete cipher).",
explanation.description);
}
}
@@ -253,6 +285,15 @@ TEST(SecurityStateContentUtilsTest, HTTPWarning) {
EXPECT_EQ(blink::kWebSecurityStyleNeutral, security_style);
// Verify only one explanation was shown when Form Not Secure is triggered.
EXPECT_EQ(1u, explanations.neutral_explanations.size());
+
+ // Verify that two explanations are shown when the Incognito and
+ // FormNotSecure flags are both set.
+ explanations.neutral_explanations.clear();
+ security_info.displayed_credit_card_field_on_http = true;
+ security_info.incognito_downgraded_security_level = true;
+ security_style = GetSecurityStyle(security_info, &explanations);
+ EXPECT_EQ(blink::kWebSecurityStyleNeutral, security_style);
+ EXPECT_EQ(2u, explanations.neutral_explanations.size());
}
// Tests that an explanation is provided if a certificate is missing a
@@ -279,4 +320,14 @@ TEST(SecurityStateContentUtilsTest, SubjectAltNameWarning) {
EXPECT_EQ(0u, explanations.insecure_explanations.size());
}
+// Tests that an explanation using the shorter constructor sets the correct
+// default values for other fields.
+TEST(SecurityStateContentUtilsTest, DefaultSecurityStyleExplanation) {
+ content::SecurityStyleExplanation explanation("summary", "description");
+
+ EXPECT_EQ(false, explanation.has_certificate);
+ EXPECT_EQ(blink::WebMixedContentContextType::kNotMixedContent,
+ explanation.mixed_content_type);
+}
+
} // namespace
diff --git a/chromium/components/security_state/core/security_state.cc b/chromium/components/security_state/core/security_state.cc
index aed4e42d2bf..c7d40bf2b11 100644
--- a/chromium/components/security_state/core/security_state.cc
+++ b/chromium/components/security_state/core/security_state.cc
@@ -5,6 +5,7 @@
#include "components/security_state/core/security_state.h"
#include <stdint.h>
+#include <string>
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
@@ -17,32 +18,55 @@ namespace security_state {
namespace {
-// Do not change or reorder this enum, and add new values at the end. It is used
-// in the MarkHttpAs histogram.
+// These values are written to logs. New enum values can be added, but existing
+// enums must never be renumbered or deleted and reused.
enum MarkHttpStatus {
- NEUTRAL /* deprecated */,
- NON_SECURE,
- HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS,
+ NEUTRAL = 0, // Deprecated
+ NON_SECURE = 1,
+ HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS = 2,
+ NON_SECURE_AFTER_EDITING = 3,
+ NON_SECURE_WHILE_INCOGNITO = 4,
+ NON_SECURE_WHILE_INCOGNITO_OR_EDITING = 5,
LAST_STATUS
};
// If |switch_or_field_trial_group| corresponds to a valid
-// MarkHttpAs group, sets |*level| and |*histogram_status| to the
+// MarkHttpAs setting, sets |*level| and |*histogram_status| to the
// appropriate values and returns true. Otherwise, returns false.
bool GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
std::string switch_or_field_trial_group,
bool displayed_sensitive_input_on_http,
+ bool is_incognito,
SecurityLevel* level,
MarkHttpStatus* histogram_status) {
- if (switch_or_field_trial_group != switches::kMarkHttpAsDangerous)
- return false;
- *level = DANGEROUS;
- *histogram_status = NON_SECURE;
- return true;
+ if (switch_or_field_trial_group ==
+ switches::kMarkHttpAsNonSecureWhileIncognito) {
+ *histogram_status = NON_SECURE_WHILE_INCOGNITO;
+ *level = (is_incognito || displayed_sensitive_input_on_http)
+ ? security_state::HTTP_SHOW_WARNING
+ : NONE;
+ return true;
+ }
+ if (switch_or_field_trial_group ==
+ switches::kMarkHttpAsNonSecureWhileIncognitoOrEditing) {
+ *histogram_status = NON_SECURE_WHILE_INCOGNITO_OR_EDITING;
+ *level = (is_incognito || displayed_sensitive_input_on_http)
+ ? security_state::HTTP_SHOW_WARNING
+ : NONE;
+ return true;
+ }
+ if (switch_or_field_trial_group == switches::kMarkHttpAsDangerous) {
+ *histogram_status = NON_SECURE;
+ *level = DANGEROUS;
+ return true;
+ }
+
+ return false;
}
SecurityLevel GetSecurityLevelForNonSecureFieldTrial(
- bool displayed_sensitive_input_on_http) {
+ bool displayed_sensitive_input_on_http,
+ bool is_incognito) {
std::string choice =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kMarkHttpAs);
@@ -56,9 +80,11 @@ SecurityLevel GetSecurityLevelForNonSecureFieldTrial(
// If the command-line switch is set, then it takes precedence over
// the field trial group.
if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
- choice, displayed_sensitive_input_on_http, &level, &status)) {
+ choice, displayed_sensitive_input_on_http, is_incognito, &level,
+ &status)) {
if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
- group, displayed_sensitive_input_on_http, &level, &status)) {
+ group, displayed_sensitive_input_on_http, is_incognito, &level,
+ &status)) {
status = HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS;
level = displayed_sensitive_input_on_http
? security_state::HTTP_SHOW_WARNING
@@ -125,7 +151,8 @@ SecurityLevel GetSecurityLevelForRequest(
(url.IsStandard() || url.SchemeIs(url::kBlobScheme))) {
return GetSecurityLevelForNonSecureFieldTrial(
visible_security_state.displayed_password_field_on_http ||
- visible_security_state.displayed_credit_card_field_on_http);
+ visible_security_state.displayed_credit_card_field_on_http,
+ visible_security_state.is_incognito);
}
return NONE;
}
@@ -221,11 +248,9 @@ void SecurityInfoForRequest(
visible_security_state.displayed_password_field_on_http;
security_info->displayed_credit_card_field_on_http =
visible_security_state.displayed_credit_card_field_on_http;
- if (visible_security_state.certificate) {
- security_info->cert_missing_subject_alt_name =
- !visible_security_state.certificate->GetSubjectAltName(nullptr,
- nullptr);
- }
+ security_info->cert_missing_subject_alt_name =
+ visible_security_state.certificate &&
+ !visible_security_state.certificate->GetSubjectAltName(nullptr, nullptr);
security_info->contained_mixed_form =
visible_security_state.contained_mixed_form;
@@ -235,10 +260,37 @@ void SecurityInfoForRequest(
is_origin_secure_callback, security_info->sha1_in_chain,
security_info->mixed_content_status,
security_info->content_with_cert_errors_status);
+
+ security_info->incognito_downgraded_security_level =
+ (visible_security_state.is_incognito &&
+ security_info->security_level == HTTP_SHOW_WARNING &&
+ security_state::IsHttpWarningForIncognitoEnabled());
}
} // namespace
+bool IsHttpWarningForIncognitoEnabled() {
+ std::string choice =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kMarkHttpAs);
+ std::string group = base::FieldTrialList::FindFullName("MarkNonSecureAs");
+ SecurityLevel level = NONE;
+ MarkHttpStatus status;
+
+ // If the command-line switch is set, then it takes precedence over
+ // the field trial group.
+ if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
+ choice, false, true, &level, &status)) {
+ if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
+ group, false, true, &level, &status)) {
+ return false;
+ }
+ }
+
+ return (status == NON_SECURE_WHILE_INCOGNITO ||
+ status == NON_SECURE_WHILE_INCOGNITO_OR_EDITING);
+}
+
const base::Feature kHttpFormWarningFeature{"HttpFormWarning",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -258,7 +310,8 @@ SecurityInfo::SecurityInfo()
displayed_password_field_on_http(false),
displayed_credit_card_field_on_http(false),
contained_mixed_form(false),
- cert_missing_subject_alt_name(false) {}
+ cert_missing_subject_alt_name(false),
+ incognito_downgraded_security_level(false) {}
SecurityInfo::~SecurityInfo() {}
@@ -290,7 +343,8 @@ VisibleSecurityState::VisibleSecurityState()
ran_content_with_cert_errors(false),
pkp_bypassed(false),
displayed_password_field_on_http(false),
- displayed_credit_card_field_on_http(false) {}
+ displayed_credit_card_field_on_http(false),
+ is_incognito(false) {}
VisibleSecurityState::~VisibleSecurityState() {}
@@ -312,7 +366,8 @@ bool VisibleSecurityState::operator==(const VisibleSecurityState& other) const {
other.displayed_password_field_on_http &&
displayed_credit_card_field_on_http ==
other.displayed_credit_card_field_on_http &&
- contained_mixed_form == other.contained_mixed_form);
+ contained_mixed_form == other.contained_mixed_form &&
+ is_incognito == other.is_incognito);
}
} // namespace security_state
diff --git a/chromium/components/security_state/core/security_state.h b/chromium/components/security_state/core/security_state.h
index 4d1ec3f90ce..0e737f9d2ce 100644
--- a/chromium/components/security_state/core/security_state.h
+++ b/chromium/components/security_state/core/security_state.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_SECURITY_STATE_SECURITY_STATE_H_
-#define COMPONENTS_SECURITY_STATE_SECURITY_STATE_H_
+#ifndef COMPONENTS_SECURITY_STATE_CORE_SECURITY_STATE_H_
+#define COMPONENTS_SECURITY_STATE_CORE_SECURITY_STATE_H_
#include <stdint.h>
#include <memory>
@@ -141,6 +141,9 @@ struct SecurityInfo {
// True if the server's certificate does not contain a
// subjectAltName extension with a domain name or IP address.
bool cert_missing_subject_alt_name;
+ // True if the |security_level| was downgraded to HTTP_SHOW_WARNING because
+ // the page was loaded while Incognito.
+ bool incognito_downgraded_security_level;
};
// Contains the security state relevant to computing the SecurityInfo
@@ -181,6 +184,8 @@ struct VisibleSecurityState {
bool displayed_password_field_on_http;
// True if the page was an HTTP page that displayed a credit card field.
bool displayed_credit_card_field_on_http;
+ // True if the page was displayed in an Incognito context.
+ bool is_incognito;
};
// These security levels describe the treatment given to pages that
@@ -209,6 +214,10 @@ void GetSecurityInfo(
// |kHttpFormWarningFeature| feature.
bool IsHttpWarningInFormEnabled();
+// Returns true if the MarkHttpAs setting indicates that a warning
+// should be shown for HTTP pages loaded while in Incognito mode.
+bool IsHttpWarningForIncognitoEnabled();
+
} // namespace security_state
-#endif // COMPONENTS_SECURITY_STATE_SECURITY_STATE_H_
+#endif // COMPONENTS_SECURITY_STATE_CORE_SECURITY_STATE_H_
diff --git a/chromium/components/security_state/core/security_state_unittest.cc b/chromium/components/security_state/core/security_state_unittest.cc
index 201a91d0b64..85df06e2560 100644
--- a/chromium/components/security_state/core/security_state_unittest.cc
+++ b/chromium/components/security_state/core/security_state_unittest.cc
@@ -5,11 +5,14 @@
#include "components/security_state/core/security_state.h"
#include <stdint.h>
+#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/test/histogram_tester.h"
+#include "base/test/scoped_command_line.h"
+#include "components/security_state/core/switches.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
@@ -49,7 +52,8 @@ class TestSecurityStateHelper {
ran_mixed_content_(false),
malicious_content_status_(MALICIOUS_CONTENT_STATUS_NONE),
displayed_password_field_on_http_(false),
- displayed_credit_card_field_on_http_(false) {}
+ displayed_credit_card_field_on_http_(false),
+ is_incognito_(false) {}
virtual ~TestSecurityStateHelper() {}
void SetCertificate(scoped_refptr<net::X509Certificate> cert) {
@@ -85,6 +89,7 @@ class TestSecurityStateHelper {
bool displayed_credit_card_field_on_http) {
displayed_credit_card_field_on_http_ = displayed_credit_card_field_on_http;
}
+ void set_is_incognito(bool is_incognito) { is_incognito_ = is_incognito; }
void SetUrl(const GURL& url) { url_ = url; }
@@ -103,6 +108,7 @@ class TestSecurityStateHelper {
state->displayed_password_field_on_http = displayed_password_field_on_http_;
state->displayed_credit_card_field_on_http =
displayed_credit_card_field_on_http_;
+ state->is_incognito = is_incognito_;
return state;
}
@@ -124,6 +130,7 @@ class TestSecurityStateHelper {
MaliciousContentStatus malicious_content_status_;
bool displayed_password_field_on_http_;
bool displayed_credit_card_field_on_http_;
+ bool is_incognito_;
};
} // namespace
@@ -353,6 +360,30 @@ TEST(SecurityStateTest, PrivateUserDataNotSetOnPseudoUrls) {
}
}
+// Tests that |incognito_downgraded_security_level| is set only when the
+// corresponding VisibleSecurityState flag is set and the HTTPBad Phase 2
+// experiment is enabled.
+TEST(SecurityStateTest, IncognitoFlagPropagates) {
+ TestSecurityStateHelper helper;
+ helper.SetUrl(GURL(kHttpUrl));
+ SecurityInfo security_info;
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+
+ helper.set_is_incognito(true);
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+ {
+ // Enable the "non-secure-while-incognito" configuration.
+ base::test::ScopedCommandLine scoped_command_line;
+ scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+ security_state::switches::kMarkHttpAs,
+ security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+ }
+}
+
// Tests that SSL.MarkHttpAsStatus histogram is updated when security state is
// computed for a page.
TEST(SecurityStateTest, MarkHttpAsStatusHistogram) {
@@ -367,12 +398,70 @@ TEST(SecurityStateTest, MarkHttpAsStatusHistogram) {
SecurityInfo security_info;
histograms.ExpectTotalCount(kHistogramName, 0);
helper.GetSecurityInfo(&security_info);
- histograms.ExpectUniqueSample(kHistogramName, 2 /* HTTP_SHOW_WARNING */, 1);
+ histograms.ExpectUniqueSample(
+ kHistogramName, 2 /* HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS */, 1);
// Ensure histogram recorded correctly even without a password input.
helper.set_displayed_password_field_on_http(false);
helper.GetSecurityInfo(&security_info);
- histograms.ExpectUniqueSample(kHistogramName, 2 /* HTTP_SHOW_WARNING */, 2);
+ histograms.ExpectUniqueSample(
+ kHistogramName, 2 /* HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS */, 2);
+
+ {
+ // Test the "non-secure-while-incognito" configuration.
+ base::test::ScopedCommandLine scoped_command_line;
+ scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+ security_state::switches::kMarkHttpAs,
+ security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+
+ base::HistogramTester histograms;
+ TestSecurityStateHelper helper;
+ helper.SetUrl(GURL(kHttpUrl));
+
+ // Ensure histogram recorded correctly when the Incognito flag is present.
+ helper.set_is_incognito(true);
+ SecurityInfo security_info;
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+ histograms.ExpectUniqueSample(kHistogramName,
+ 4 /* NON_SECURE_WHILE_INCOGNITO */, 1);
+
+ // Ensure histogram recorded correctly even without the Incognito flag.
+ helper.set_is_incognito(false);
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+ histograms.ExpectUniqueSample(kHistogramName,
+ 4 /* NON_SECURE_WHILE_INCOGNITO */, 2);
+ }
+
+ {
+ // Test the "non-secure-while-incognito-or-editing" configuration.
+ base::test::ScopedCommandLine scoped_command_line;
+ scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+ security_state::switches::kMarkHttpAs,
+ security_state::switches::kMarkHttpAsNonSecureWhileIncognitoOrEditing);
+
+ base::HistogramTester histograms;
+ TestSecurityStateHelper helper;
+ helper.SetUrl(GURL(kHttpUrl));
+
+ // Ensure histogram recorded correctly when the Incognito flag is present.
+ helper.set_is_incognito(true);
+ SecurityInfo security_info;
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+ histograms.ExpectUniqueSample(
+ kHistogramName, 5 /* NON_SECURE_WHILE_INCOGNITO_OR_EDITING */, 1);
+
+ // Ensure histogram recorded correctly even without the Incognito flag.
+ helper.set_is_incognito(false);
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+ histograms.ExpectUniqueSample(
+ kHistogramName, 5 /* NON_SECURE_WHILE_INCOGNITO_OR_EDITING */, 2);
+ }
}
TEST(SecurityStateTest, DetectSubjectAltName) {
diff --git a/chromium/components/security_state/core/switches.cc b/chromium/components/security_state/core/switches.cc
index c8542f83961..dddd8ae4e61 100644
--- a/chromium/components/security_state/core/switches.cc
+++ b/chromium/components/security_state/core/switches.cc
@@ -10,6 +10,9 @@ namespace switches {
// Use to opt-in to marking HTTP as non-secure.
const char kMarkHttpAs[] = "mark-non-secure-as";
const char kMarkHttpAsDangerous[] = "non-secure";
-
+const char kMarkHttpAsNonSecureAfterEditing[] = "non-secure-after-editing";
+const char kMarkHttpAsNonSecureWhileIncognito[] = "non-secure-while-incognito";
+const char kMarkHttpAsNonSecureWhileIncognitoOrEditing[] =
+ "non-secure-while-incognito-or-editing";
} // namespace switches
} // namespace security_state
diff --git a/chromium/components/security_state/core/switches.h b/chromium/components/security_state/core/switches.h
index 4216cd27f60..df272389c08 100644
--- a/chromium/components/security_state/core/switches.h
+++ b/chromium/components/security_state/core/switches.h
@@ -10,6 +10,9 @@ namespace switches {
extern const char kMarkHttpAs[];
extern const char kMarkHttpAsDangerous[];
+extern const char kMarkHttpAsNonSecureAfterEditing[];
+extern const char kMarkHttpAsNonSecureWhileIncognito[];
+extern const char kMarkHttpAsNonSecureWhileIncognitoOrEditing[];
}
} // namespace security_state
diff --git a/chromium/components/security_state_strings.grdp b/chromium/components/security_state_strings.grdp
index 1982f9112ba..6094c157812 100644
--- a/chromium/components/security_state_strings.grdp
+++ b/chromium/components/security_state_strings.grdp
@@ -7,6 +7,12 @@
<message name="IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION" desc="Description of a security problem where the site collects private user data on an insecure page, which results in an omnibox warning." translateable="false">
This page includes a password or credit card input over HTTP. A warning has been added to the URL bar.
</message>
+ <message name="IDS_INCOGNITO_NONSECURE" desc="Summary phrase for a security problem where the site is non-secure (HTTP) and the user is browsing Incognito." translateable="false">
+ Non-secure page loaded in incognito mode
+ </message>
+ <message name="IDS_INCOGNITO_NONSECURE_DESCRIPTION" desc="Description of a security problem where the site is non-secure (HTTP) and the user is browsing Incognito, which results in an omnibox warning." translateable="false">
+ This page was loaded non-securely in an incognito window. A warning has been added to the URL bar.
+ </message>
<message name="IDS_SAFEBROWSING_WARNING" desc="Summary phrase for a security problem where the site is deemed unsafe by the SafeBrowsing service." translateable="false">
This page is dangerous (flagged by Google Safe Browsing).
</message>
@@ -32,7 +38,7 @@
Valid certificate
</message>
<message name="IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION" desc="Description of a site that has a valid server certificate." translateable="false">
- The connection to this site is using a valid, trusted server certificate.
+ The connection to this site is using a valid, trusted server certificate issued by <ph name="ISSUER">$1<ex>Let's Encrypt Authority X3</ex></ph>.
</message>
<message name="IDS_STRONG_SSL_SUMMARY" desc="Summary phrase for a site that uses a modern, secure TLS protocol and cipher." translateable="false">
Secure connection
@@ -44,13 +50,13 @@
Public-Key-Pinning was bypassed by a local root certificate.
</message>
<message name="IDS_STRONG_SSL_DESCRIPTION" desc="Description of a site that uses a modern, secure TLS protocol and cipher." translateable="false">
- The connection to this site is encrypted and authenticated using a strong protocol (<ph name="PROTOCOL_VERSION">$1<ex>TLS 1.2</ex></ph>), a strong key exchange (<ph name="KEY_EXCHANGE">$2<ex>ECDHE_RSA</ex></ph>), and a strong cipher (<ph name="CIPHER_SUTE">$3<ex>AES_128_GCM</ex></ph>).
+ The connection to this site is encrypted and authenticated using <ph name="PROTOCOL_VERSION">$1<ex>TLS 1.2</ex></ph> (a strong protocol), <ph name="KEY_EXCHANGE">$2<ex>ECDHE_RSA</ex></ph> (a strong key exchange), and <ph name="CIPHER_SUTE">$3<ex>AES_128_GCM</ex></ph> (a strong cipher).
</message>
<message name="IDS_OBSOLETE_SSL_SUMMARY" desc="Summary phrase for a site that uses an outdated SSL settings (protocol, key exchange, or cipher)." translateable="false">
Obsolete connection settings
</message>
<message name="IDS_OBSOLETE_SSL_DESCRIPTION" desc="Description of a site that uses an outdated TLS protocol or cipher." translateable="false">
- The connection to this site uses <ph name="A_PROTOCOL">$1<ex>an obsolete protocol</ex></ph> (<ph name="PROTOCOL">$2<ex>TLS 1.0</ex></ph>), <ph name="A_KEY_EXCHANGE">$3<ex>an obsolete key exchange</ex></ph> (<ph name="KEY_EXCHANGE">$4<ex>ECDHE_RSA</ex></ph>), and <ph name="A_CIPHER">$5<ex>an obsolete cipher</ex></ph> (<ph name="CIPHER">$6<ex>AES_256_CBC with HMAC-SHA1</ex></ph>).
+ The connection to this site uses <ph name="PROTOCOL">$1<ex>TLS 1.0</ex></ph> (<ph name="A_PROTOCOL">$2<ex>an obsolete protocol</ex></ph>), <ph name="KEY_EXCHANGE">$3<ex>ECDHE_RSA</ex></ph> (<ph name="A_KEY_EXCHANGE">$4<ex>an obsolete key exchange</ex></ph>), and <ph name="CIPHER">$5<ex>AES_256_CBC with HMAC-SHA1</ex></ph> (<ph name="A_CIPHER">$6<ex>an obsolete cipher</ex></ph>).
</message>
<message name="IDS_CIPHER_WITH_MAC" desc="Description of an SSL cipher that contains a separate (bulk) cipher and MAC." translateable="false">
<ph name="CIPHER">$1<ex>AES_256_CBC</ex></ph> with <ph name="MAC">$2<ex>HMAC-SHA1</ex></ph>
diff --git a/chromium/components/sessions/BUILD.gn b/chromium/components/sessions/BUILD.gn
index fc20eacf6da..5ec556c3e16 100644
--- a/chromium/components/sessions/BUILD.gn
+++ b/chromium/components/sessions/BUILD.gn
@@ -159,6 +159,9 @@ static_library("test_support") {
}
source_set("unit_tests") {
+ if (is_ios) {
+ configs += [ "//build/config/compiler:enable_arc" ]
+ }
testonly = true
sources = [
"core/serialized_navigation_entry_unittest.cc",
diff --git a/chromium/components/sessions/content/content_serialized_navigation_builder.cc b/chromium/components/sessions/content/content_serialized_navigation_builder.cc
index 7acfa8ba572..94f20a05b96 100644
--- a/chromium/components/sessions/content/content_serialized_navigation_builder.cc
+++ b/chromium/components/sessions/content/content_serialized_navigation_builder.cc
@@ -22,7 +22,8 @@ namespace sessions {
SerializedNavigationEntry
ContentSerializedNavigationBuilder::FromNavigationEntry(
int index,
- const content::NavigationEntry& entry) {
+ const content::NavigationEntry& entry,
+ SerializationOptions serialization_options) {
SerializedNavigationEntry navigation;
navigation.index_ = index;
navigation.unique_id_ = entry.GetUniqueID();
@@ -30,7 +31,8 @@ ContentSerializedNavigationBuilder::FromNavigationEntry(
navigation.referrer_policy_ = entry.GetReferrer().policy;
navigation.virtual_url_ = entry.GetVirtualURL();
navigation.title_ = entry.GetTitle();
- navigation.encoded_page_state_ = entry.GetPageState().ToEncodedData();
+ if (!(serialization_options & SerializationOptions::EXCLUDE_PAGE_STATE))
+ navigation.encoded_page_state_ = entry.GetPageState().ToEncodedData();
navigation.transition_type_ = entry.GetTransitionType();
navigation.has_post_data_ = entry.GetHasPostData();
navigation.post_id_ = entry.GetPostID();
diff --git a/chromium/components/sessions/content/content_serialized_navigation_builder.h b/chromium/components/sessions/content/content_serialized_navigation_builder.h
index b8cd76d58cb..ebb9afb0e94 100644
--- a/chromium/components/sessions/content/content_serialized_navigation_builder.h
+++ b/chromium/components/sessions/content/content_serialized_navigation_builder.h
@@ -22,11 +22,25 @@ class SerializedNavigationEntry;
// classes.
class SESSIONS_EXPORT ContentSerializedNavigationBuilder {
public:
+ // Set of options for serializing a navigation. Multiple options can be
+ // combined by bit masking.
+ enum SerializationOptions {
+ // Serialized all available navigation data.
+ DEFAULT = 0x0,
+
+ // Exclude page state data. Serializing page state data can involve heavy
+ // processing on pages with deep iframe trees, so should be avoided if not
+ // necessary.
+ EXCLUDE_PAGE_STATE = 0x1,
+ };
+
// Construct a SerializedNavigationEntry for a particular index from the given
// NavigationEntry.
static SerializedNavigationEntry FromNavigationEntry(
int index,
- const content::NavigationEntry& entry);
+ const content::NavigationEntry& entry,
+ SerializationOptions serialization_options =
+ SerializationOptions::DEFAULT);
// Convert the given SerializedNavigationEntry into a NavigationEntry with the
// given context. The NavigationEntry will have a transition type of
diff --git a/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc b/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc
index 585c41d9a46..45f365a2416 100644
--- a/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc
+++ b/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc
@@ -161,6 +161,26 @@ TEST_F(ContentSerializedNavigationBuilderTest, FromNavigationEntry) {
navigation.extended_info_map().at(kExtendedInfoKey2));
}
+// Test effect of the navigation serialization options.
+TEST_F(ContentSerializedNavigationBuilderTest,
+ FromNavigationEntrySerializationOptions) {
+ const std::unique_ptr<content::NavigationEntry> navigation_entry(
+ MakeNavigationEntryForTest());
+
+ const SerializedNavigationEntry& default_navigation =
+ ContentSerializedNavigationBuilder::FromNavigationEntry(
+ test_data::kIndex, *navigation_entry,
+ ContentSerializedNavigationBuilder::DEFAULT);
+ EXPECT_EQ(test_data::kEncodedPageState,
+ default_navigation.encoded_page_state());
+
+ const SerializedNavigationEntry& excluded_page_state_navigation =
+ ContentSerializedNavigationBuilder::FromNavigationEntry(
+ test_data::kIndex, *navigation_entry,
+ ContentSerializedNavigationBuilder::EXCLUDE_PAGE_STATE);
+ EXPECT_TRUE(excluded_page_state_navigation.encoded_page_state().empty());
+}
+
// Create a NavigationEntry, then create another one by converting to
// a SerializedNavigationEntry and back. The new one should match the old one
// except for fields that aren't preserved, which should be set to
diff --git a/chromium/components/sessions/content/content_serialized_navigation_driver.cc b/chromium/components/sessions/content/content_serialized_navigation_driver.cc
index 20ec2d02090..abf02b4862e 100644
--- a/chromium/components/sessions/content/content_serialized_navigation_driver.cc
+++ b/chromium/components/sessions/content/content_serialized_navigation_driver.cc
@@ -15,11 +15,6 @@ namespace sessions {
namespace {
-const int kObsoleteReferrerPolicyAlways = 0;
-const int kObsoleteReferrerPolicyDefault = 1;
-const int kObsoleteReferrerPolicyNever = 2;
-const int kObsoleteReferrerPolicyOrigin = 3;
-
ContentSerializedNavigationDriver* g_instance = nullptr;
} // namespace
@@ -59,45 +54,6 @@ int ContentSerializedNavigationDriver::GetDefaultReferrerPolicy() const {
return blink::kWebReferrerPolicyDefault;
}
-bool ContentSerializedNavigationDriver::MapReferrerPolicyToOldValues(
- int referrer_policy,
- int* mapped_referrer_policy) const {
- switch (referrer_policy) {
- case blink::kWebReferrerPolicyAlways:
- case blink::kWebReferrerPolicyDefault:
- // "always" and "default" are the same value in all versions.
- *mapped_referrer_policy = referrer_policy;
- return true;
-
- case blink::kWebReferrerPolicyOrigin:
- // "origin" exists in the old encoding.
- *mapped_referrer_policy = kObsoleteReferrerPolicyOrigin;
- return true;
-
- default:
- // Everything else is mapped to never.
- *mapped_referrer_policy = kObsoleteReferrerPolicyNever;
- return false;
- }
-}
-
-bool ContentSerializedNavigationDriver::MapReferrerPolicyToNewValues(
- int referrer_policy,
- int* mapped_referrer_policy) const {
- switch (referrer_policy) {
- case kObsoleteReferrerPolicyAlways:
- case kObsoleteReferrerPolicyDefault:
- // "always" and "default" are the same value in all versions.
- *mapped_referrer_policy = referrer_policy;
- return true;
-
- default:
- // Since we don't know what encoding was used, we map the rest to "never".
- *mapped_referrer_policy = blink::kWebReferrerPolicyNever;
- return false;
- }
-}
-
std::string
ContentSerializedNavigationDriver::GetSanitizedPageStateForPickle(
const SerializedNavigationEntry* navigation) const {
diff --git a/chromium/components/sessions/content/content_serialized_navigation_driver.h b/chromium/components/sessions/content/content_serialized_navigation_driver.h
index ec732a70766..82a9b81aa50 100644
--- a/chromium/components/sessions/content/content_serialized_navigation_driver.h
+++ b/chromium/components/sessions/content/content_serialized_navigation_driver.h
@@ -36,10 +36,6 @@ class SESSIONS_EXPORT ContentSerializedNavigationDriver
// SerializedNavigationDriver implementation.
int GetDefaultReferrerPolicy() const override;
- bool MapReferrerPolicyToOldValues(int referrer_policy,
- int* mapped_referrer_policy) const override;
- bool MapReferrerPolicyToNewValues(int referrer_policy,
- int* mapped_referrer_policy) const override;
std::string GetSanitizedPageStateForPickle(
const SerializedNavigationEntry* navigation) const override;
void Sanitize(SerializedNavigationEntry* navigation) const override;
diff --git a/chromium/components/sessions/core/base_session_service.cc b/chromium/components/sessions/core/base_session_service.cc
index 21726d042bd..e40809f472a 100644
--- a/chromium/components/sessions/core/base_session_service.cc
+++ b/chromium/components/sessions/core/base_session_service.cc
@@ -34,7 +34,7 @@ void PostOrRunInternalGetCommandsCallback(
base::TaskRunner* task_runner,
const BaseSessionService::GetCommandsCallback& callback,
std::vector<std::unique_ptr<SessionCommand>> commands) {
- if (task_runner->RunsTasksOnCurrentThread()) {
+ if (task_runner->RunsTasksInCurrentSequence()) {
callback.Run(std::move(commands));
} else {
task_runner->PostTask(FROM_HERE,
diff --git a/chromium/components/sessions/core/serialized_navigation_driver.h b/chromium/components/sessions/core/serialized_navigation_driver.h
index 74bbfefe1cb..2a9f74f8230 100644
--- a/chromium/components/sessions/core/serialized_navigation_driver.h
+++ b/chromium/components/sessions/core/serialized_navigation_driver.h
@@ -24,18 +24,6 @@ class SESSIONS_EXPORT SerializedNavigationDriver {
// Returns the default referrer policy.
virtual int GetDefaultReferrerPolicy() const = 0;
- // Maps current referrer policies to old values to work around
- // crbug.com/450589. Returns false if the referrer should be stripped.
- virtual bool MapReferrerPolicyToOldValues(
- int referrer_policy,
- int* mapped_referrer_policy) const = 0;
-
- // Sanitizes a referrer policy that might either be in the old or the new
- // format. Returns false if the referrer should be stripped.
- virtual bool MapReferrerPolicyToNewValues(
- int referrer_policy,
- int* mapped_referrer_policy) const = 0;
-
// Returns a sanitized version of the given |navigation|'s encoded_page_state
// suitable for writing to disk.
virtual std::string GetSanitizedPageStateForPickle(
diff --git a/chromium/components/sessions/core/serialized_navigation_entry.cc b/chromium/components/sessions/core/serialized_navigation_entry.cc
index ca686b0add4..3dda4ae452b 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry.cc
+++ b/chromium/components/sessions/core/serialized_navigation_entry.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include "base/macros.h"
#include "base/pickle.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/memory_usage_estimator.h"
@@ -18,6 +19,9 @@ namespace sessions {
// TODO(treib): Remove, not needed anymore. crbug.com/627747
const char kSearchTermsKey[] = "search_terms";
+// The previous referrer policy value corresponding to |Never|.
+const int kObsoleteReferrerPolicyNever = 2;
+
SerializedNavigationEntry::SerializedNavigationEntry()
: index_(-1),
unique_id_(0),
@@ -48,14 +52,8 @@ SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
navigation.referrer_url_ = GURL(sync_data.referrer());
navigation.referrer_policy_ = sync_data.correct_referrer_policy();
} else {
- int mapped_referrer_policy;
- if (SerializedNavigationDriver::Get()->MapReferrerPolicyToNewValues(
- sync_data.obsolete_referrer_policy(), &mapped_referrer_policy)) {
- navigation.referrer_url_ = GURL(sync_data.referrer());
- } else {
- navigation.referrer_url_ = GURL();
- }
- navigation.referrer_policy_ = mapped_referrer_policy;
+ navigation.referrer_url_ = GURL();
+ navigation.referrer_policy_ = kObsoleteReferrerPolicyNever;
}
navigation.virtual_url_ = GURL(sync_data.virtual_url());
navigation.title_ = base::UTF8ToUTF16(sync_data.title());
@@ -236,15 +234,11 @@ void SerializedNavigationEntry::WriteToPickle(int max_size,
const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
pickle->WriteInt(type_mask);
- int mapped_referrer_policy;
- if (SerializedNavigationDriver::Get()->MapReferrerPolicyToOldValues(
- referrer_policy_, &mapped_referrer_policy) &&
- referrer_url_.is_valid()) {
- WriteStringToPickle(pickle, &bytes_written, max_size, referrer_url_.spec());
- } else {
- WriteStringToPickle(pickle, &bytes_written, max_size, std::string());
- }
- pickle->WriteInt(mapped_referrer_policy);
+ WriteStringToPickle(pickle, &bytes_written, max_size, referrer_url_.spec());
+
+ // This field was deprecated in m61, but we still write it to the pickle for
+ // forwards compatibility.
+ pickle->WriteInt(kObsoleteReferrerPolicyNever);
// Save info required to override the user agent.
WriteStringToPickle(
@@ -294,15 +288,11 @@ bool SerializedNavigationEntry::ReadFromPickle(base::PickleIterator* iterator) {
referrer_spec = std::string();
referrer_url_ = GURL(referrer_spec);
- // The "referrer policy" property was added even later, so we fall back to
- // the default policy if the property is not present.
- //
- // Note: due to crbug.com/450589 this value might be incorrect, and a
- // corrected version is stored later in the pickle.
- if (!iterator->ReadInt(&referrer_policy_)) {
- referrer_policy_ =
- SerializedNavigationDriver::Get()->GetDefaultReferrerPolicy();
- }
+ // Note: due to crbug.com/450589 the initial referrer policy is incorrect,
+ // and ignored. A correct referrer policy is extracted later (see
+ // |correct_referrer_policy| below).
+ int ignored_referrer_policy;
+ ignore_result(iterator->ReadInt(&ignored_referrer_policy));
// If the original URL can't be found, leave it empty.
std::string original_request_url_spec;
@@ -333,12 +323,6 @@ bool SerializedNavigationEntry::ReadFromPickle(base::PickleIterator* iterator) {
if (iterator->ReadInt(&correct_referrer_policy)) {
referrer_policy_ = correct_referrer_policy;
} else {
- int mapped_referrer_policy;
- if (!SerializedNavigationDriver::Get()->MapReferrerPolicyToNewValues(
- referrer_policy_, &mapped_referrer_policy)) {
- referrer_url_ = GURL();
- }
- referrer_policy_ = mapped_referrer_policy;
encoded_page_state_ =
SerializedNavigationDriver::Get()->StripReferrerFromPageState(
encoded_page_state_);
@@ -368,14 +352,7 @@ bool SerializedNavigationEntry::ReadFromPickle(base::PickleIterator* iterator) {
sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
sync_pb::TabNavigation sync_data;
sync_data.set_virtual_url(virtual_url_.spec());
- int mapped_referrer_policy;
- if (SerializedNavigationDriver::Get()->MapReferrerPolicyToOldValues(
- referrer_policy_, &mapped_referrer_policy)) {
- sync_data.set_referrer(referrer_url_.spec());
- } else {
- sync_data.set_referrer(std::string());
- }
- sync_data.set_obsolete_referrer_policy(mapped_referrer_policy);
+ sync_data.set_referrer(referrer_url_.spec());
sync_data.set_correct_referrer_policy(referrer_policy_);
sync_data.set_title(base::UTF16ToUTF8(title_));
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm b/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm
index c119b54745e..da0dcb61b4a 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm
@@ -11,6 +11,10 @@
#include "ios/web/public/referrer.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace sessions {
namespace {
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_driver.cc b/chromium/components/sessions/ios/ios_serialized_navigation_driver.cc
index ba195b52d15..5c53c21a898 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_driver.cc
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_driver.cc
@@ -7,16 +7,10 @@
#include "base/memory/singleton.h"
#include "components/sessions/core/serialized_navigation_entry.h"
#include "ios/web/public/referrer.h"
+#include "ios/web/public/referrer_util.h"
namespace sessions {
-namespace {
-const int kObsoleteReferrerPolicyAlways = 0;
-const int kObsoleteReferrerPolicyDefault = 1;
-const int kObsoleteReferrerPolicyNever = 2;
-const int kObsoleteReferrerPolicyOrigin = 3;
-} // namespace
-
// static
SerializedNavigationDriver* SerializedNavigationDriver::Get() {
return IOSSerializedNavigationDriver::GetInstance();
@@ -40,45 +34,6 @@ int IOSSerializedNavigationDriver::GetDefaultReferrerPolicy() const {
return web::ReferrerPolicyDefault;
}
-bool IOSSerializedNavigationDriver::MapReferrerPolicyToOldValues(
- int referrer_policy,
- int* mapped_referrer_policy) const {
- switch (referrer_policy) {
- case web::ReferrerPolicyAlways:
- case web::ReferrerPolicyDefault:
- // "always" and "default" are the same value in all versions.
- *mapped_referrer_policy = referrer_policy;
- return true;
-
- case web::ReferrerPolicyOrigin:
- // "origin" exists in the old encoding.
- *mapped_referrer_policy = kObsoleteReferrerPolicyOrigin;
- return true;
-
- default:
- // Everything else is mapped to never.
- *mapped_referrer_policy = kObsoleteReferrerPolicyNever;
- return false;
- }
-}
-
-bool IOSSerializedNavigationDriver::MapReferrerPolicyToNewValues(
- int referrer_policy,
- int* mapped_referrer_policy) const {
- switch (referrer_policy) {
- case kObsoleteReferrerPolicyAlways:
- case kObsoleteReferrerPolicyDefault:
- // "always" and "default" are the same value in all versions.
- *mapped_referrer_policy = referrer_policy;
- return true;
-
- default:
- // Since we don't know what encoding was used, we map the rest to "never".
- *mapped_referrer_policy = web::ReferrerPolicyNever;
- return false;
- }
-}
-
std::string
IOSSerializedNavigationDriver::GetSanitizedPageStateForPickle(
const SerializedNavigationEntry* navigation) const {
@@ -99,29 +54,8 @@ void IOSSerializedNavigationDriver::Sanitize(
NOTREACHED();
referrer.policy = web::ReferrerPolicyNever;
}
- bool is_downgrade = referrer.url.SchemeIsCryptographic() &&
- !navigation->virtual_url_.SchemeIsCryptographic();
- switch (referrer.policy) {
- case web::ReferrerPolicyDefault:
- if (is_downgrade)
- referrer.url = GURL();
- break;
- case web::ReferrerPolicyNoReferrerWhenDowngrade:
- if (is_downgrade)
- referrer.url = GURL();
- case web::ReferrerPolicyAlways:
- break;
- case web::ReferrerPolicyNever:
- referrer.url = GURL();
- break;
- case web::ReferrerPolicyOrigin:
- referrer.url = referrer.url.GetOrigin();
- break;
- case web::ReferrerPolicyOriginWhenCrossOrigin:
- if (navigation->virtual_url_.GetOrigin() != referrer.url.GetOrigin())
- referrer.url = referrer.url.GetOrigin();
- break;
- }
+ referrer.url = GURL(
+ ReferrerHeaderValueForNavigation(navigation->virtual_url_, referrer));
}
// Reset the referrer if it has changed.
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_driver.h b/chromium/components/sessions/ios/ios_serialized_navigation_driver.h
index 7982ad3145e..a589585eadc 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_driver.h
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_driver.h
@@ -26,10 +26,6 @@ class IOSSerializedNavigationDriver
// SerializedNavigationDriver implementation.
int GetDefaultReferrerPolicy() const override;
- bool MapReferrerPolicyToOldValues(int referrer_policy,
- int* mapped_referrer_policy) const override;
- bool MapReferrerPolicyToNewValues(int referrer_policy,
- int* mapped_referrer_policy) const override;
std::string GetSanitizedPageStateForPickle(
const SerializedNavigationEntry* navigation) const override;
void Sanitize(SerializedNavigationEntry* navigation) const override;
diff --git a/chromium/components/signin/core/account_id/account_id.cc b/chromium/components/signin/core/account_id/account_id.cc
index 1ffac80db1e..e232ecb965c 100644
--- a/chromium/components/signin/core/account_id/account_id.cc
+++ b/chromium/components/signin/core/account_id/account_id.cc
@@ -319,6 +319,16 @@ bool AccountId::Deserialize(const std::string& serialized,
return false;
}
+std::ostream& operator<<(std::ostream& stream, const AccountId& account_id) {
+ stream << "{id: " << account_id.id_ << ", email: " << account_id.user_email_
+ << ", type: "
+ << static_cast<
+ std::underlying_type<decltype(account_id.account_type_)>::type>(
+ account_id.account_type_)
+ << "}";
+ return stream;
+}
+
const AccountId& EmptyAccountId() {
return AccountId::EmptyAccountId::GetInstance()->user_id;
}
diff --git a/chromium/components/signin/core/account_id/account_id.h b/chromium/components/signin/core/account_id/account_id.h
index 81082b5fe04..a263178d13d 100644
--- a/chromium/components/signin/core/account_id/account_id.h
+++ b/chromium/components/signin/core/account_id/account_id.h
@@ -7,7 +7,9 @@
#include <stddef.h>
+#include <ostream>
#include <string>
+
#include "base/containers/hash_tables.h"
enum class AccountType { UNKNOWN, GOOGLE, ACTIVE_DIRECTORY };
@@ -97,6 +99,8 @@ class AccountId {
AccountId* out_account_id);
private:
+ friend std::ostream& operator<<(std::ostream&, const AccountId&);
+
AccountId(const std::string& id,
const std::string& user_email,
const AccountType& account_type);
@@ -106,6 +110,9 @@ class AccountId {
AccountType account_type_ = AccountType::UNKNOWN;
};
+// Overload << operator to allow logging of AccountIds.
+std::ostream& operator<<(std::ostream& stream, const AccountId& account_id);
+
// Returns a reference to a singleton.
const AccountId& EmptyAccountId();
diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn
index 5265026b314..b900638e0cf 100644
--- a/chromium/components/signin/core/browser/BUILD.gn
+++ b/chromium/components/signin/core/browser/BUILD.gn
@@ -2,10 +2,23 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//components/signin/features.gni")
+
if (is_android) {
import("//build/config/android/rules.gni")
}
+# Split into its own target to allow the Identity Service to depend on it in
+# typemaps without introducing a dependency on all of
+# //components/signin/core/browser, which is undesirable. In the long term
+# this file will move to be part of the Identity Service client library.
+static_library("account_info") {
+ sources = [
+ "account_info.cc",
+ "account_info.h",
+ ]
+}
+
static_library("browser") {
sources = [
"about_signin_internals.cc",
@@ -14,8 +27,6 @@ static_library("browser") {
"access_token_fetcher.h",
"account_fetcher_service.cc",
"account_fetcher_service.h",
- "account_info.cc",
- "account_info.h",
"account_info_fetcher.cc",
"account_info_fetcher.h",
"account_investigator.cc",
@@ -24,14 +35,16 @@ static_library("browser") {
"account_reconcilor.h",
"account_tracker_service.cc",
"account_tracker_service.h",
- "android/component_jni_registrar.cc",
- "android/component_jni_registrar.h",
"child_account_info_fetcher.cc",
"child_account_info_fetcher.h",
"child_account_info_fetcher_android.cc",
"child_account_info_fetcher_android.h",
"child_account_info_fetcher_impl.cc",
"child_account_info_fetcher_impl.h",
+ "chrome_connected_header_helper.cc",
+ "chrome_connected_header_helper.h",
+ "dice_header_helper.cc",
+ "dice_header_helper.h",
"gaia_cookie_manager_service.cc",
"gaia_cookie_manager_service.h",
"profile_identity_provider.cc",
@@ -75,26 +88,29 @@ static_library("browser") {
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
public_deps = [
+ ":account_info",
+ "//base",
+ "//components/content_settings/core/browser",
+ "//components/content_settings/core/common",
+ "//components/invalidation/public",
+ "//components/keyed_service/core",
+ "//components/prefs",
"//components/signin/core/account_id",
"//components/signin/core/common",
+ "//components/signin/core/common:signin_features",
+ "//google_apis",
+ "//net",
+ "//url",
]
deps = [
- "//base",
"//base:i18n",
- "//components/content_settings/core/browser",
- "//components/content_settings/core/common",
"//components/data_use_measurement/core",
"//components/google/core/browser",
- "//components/invalidation/public",
- "//components/keyed_service/core",
"//components/metrics",
"//components/os_crypt",
"//components/pref_registry",
- "//components/prefs",
"//components/webdata/common",
"//crypto",
- "//google_apis",
- "//net",
"//sql",
"//third_party/icu",
]
@@ -107,6 +123,13 @@ static_library("browser") {
]
}
+ if (!enable_dice_support) {
+ sources -= [
+ "dice_header_helper.cc",
+ "dice_header_helper.h",
+ ]
+ }
+
if (is_android) {
sources -= [
"child_account_info_fetcher_impl.cc",
@@ -167,6 +190,7 @@ source_set("unit_tests") {
"//components/os_crypt:test_support",
"//components/pref_registry:pref_registry",
"//components/signin/core/common",
+ "//components/signin/core/common:signin_features",
"//components/sync_preferences:test_support",
"//testing/gmock",
]
diff --git a/chromium/components/signin/core/browser/about_signin_internals.cc b/chromium/components/signin/core/browser/about_signin_internals.cc
index da7be07b13c..fca6fbe2507 100644
--- a/chromium/components/signin/core/browser/about_signin_internals.cc
+++ b/chromium/components/signin/core/browser/about_signin_internals.cc
@@ -42,11 +42,11 @@ std::string GetTimeStr(base::Time time) {
base::ListValue* AddSection(base::ListValue* parent_list,
const std::string& title) {
- std::unique_ptr<base::DictionaryValue> section(new base::DictionaryValue());
- base::ListValue* section_contents = new base::ListValue();
+ auto section = base::MakeUnique<base::DictionaryValue>();
section->SetString("title", title);
- section->Set("data", section_contents);
+ base::ListValue* section_contents =
+ section->SetList("data", base::MakeUnique<base::ListValue>());
parent_list->Append(std::move(section));
return section_contents;
}
@@ -393,8 +393,7 @@ void AboutSigninInternals::GoogleSigninFailed(
}
void AboutSigninInternals::GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
NotifyObservers();
}
@@ -410,24 +409,21 @@ void AboutSigninInternals::OnGaiaAccountsInCookieUpdated(
if (error.state() != GoogleServiceAuthError::NONE)
return;
- base::DictionaryValue cookie_status;
- base::ListValue* cookie_info = new base::ListValue();
- cookie_status.Set("cookie_info", cookie_info);
+ auto cookie_info = base::MakeUnique<base::ListValue>();
for (size_t i = 0; i < gaia_accounts.size(); ++i) {
- AddCookieEntry(cookie_info,
- gaia_accounts[i].raw_email,
+ AddCookieEntry(cookie_info.get(), gaia_accounts[i].raw_email,
gaia_accounts[i].gaia_id,
gaia_accounts[i].valid ? "Valid" : "Invalid");
}
if (gaia_accounts.size() == 0) {
- AddCookieEntry(cookie_info,
- "No Accounts Present.",
- std::string(),
+ AddCookieEntry(cookie_info.get(), "No Accounts Present.", std::string(),
std::string());
}
+ base::DictionaryValue cookie_status;
+ cookie_status.Set("cookie_info", std::move(cookie_info));
// Update the observers that the cookie's accounts are updated.
for (auto& observer : signin_observers_)
observer.OnCookieAccountsFetched(&cookie_status);
@@ -529,16 +525,16 @@ AboutSigninInternals::SigninStatus::ToValue(
FROM_HERE_WITH_EXPLICIT_FUNCTION(
"422460 AboutSigninInternals::SigninStatus::ToValue1"));
- std::unique_ptr<base::DictionaryValue> signin_status(
- new base::DictionaryValue());
- base::ListValue* signin_info = new base::ListValue();
- signin_status->Set("signin_info", signin_info);
+ auto signin_status = base::MakeUnique<base::DictionaryValue>();
+ auto signin_info = base::MakeUnique<base::ListValue>();
// A summary of signin related info first.
- base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
+ base::ListValue* basic_info =
+ AddSection(signin_info.get(), "Basic Information");
AddSectionEntry(basic_info, "Chrome Version", product_version);
- AddSectionEntry(basic_info, "Account Consistency?",
- switches::IsEnableAccountConsistency() == true ? "On" : "Off");
+ AddSectionEntry(
+ basic_info, "Account Consistency?",
+ switches::IsAccountConsistencyMirrorEnabled() == true ? "On" : "Off");
AddSectionEntry(basic_info, "Signin Status",
signin_manager->IsAuthenticated() ? "Signed In" : "Not Signed In");
OAuth2TokenServiceDelegate::LoadCredentialsState load_tokens_state =
@@ -589,7 +585,8 @@ AboutSigninInternals::SigninStatus::ToValue(
// Time and status information of the possible sign in types.
base::ListValue* detailed_info =
- AddSection(signin_info, "Last Signin Details");
+ AddSection(signin_info.get(), "Last Signin Details");
+ signin_status->Set("signin_info", std::move(signin_info));
for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
const std::string status_field_label =
SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i));
@@ -644,8 +641,7 @@ AboutSigninInternals::SigninStatus::ToValue(
"422460 AboutSigninInternals::SigninStatus::ToValue4"));
// Token information for all services.
- base::ListValue* token_info = new base::ListValue();
- signin_status->Set("token_info", token_info);
+ auto token_info = base::MakeUnique<base::ListValue>();
for (auto it = token_info_map.begin(); it != token_info_map.end(); ++it) {
// TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
// is fixed.
@@ -653,7 +649,7 @@ AboutSigninInternals::SigninStatus::ToValue(
FROM_HERE_WITH_EXPLICIT_FUNCTION(
"422460 AboutSigninInternals::SigninStatus::ToValue41"));
- base::ListValue* token_details = AddSection(token_info, it->first);
+ base::ListValue* token_details = AddSection(token_info.get(), it->first);
// TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
// is fixed.
@@ -674,24 +670,24 @@ AboutSigninInternals::SigninStatus::ToValue(
token_details->Append(token->ToValue());
}
}
+ signin_status->Set("token_info", std::move(token_info));
- base::ListValue* account_info = new base::ListValue();
- signin_status->Set("accountInfo", account_info);
+ auto account_info = base::MakeUnique<base::ListValue>();
const std::vector<std::string>& accounts_in_token_service =
token_service->GetAccounts();
- if(accounts_in_token_service.size() == 0) {
- std::unique_ptr<base::DictionaryValue> no_token_entry(
- new base::DictionaryValue());
+ if (accounts_in_token_service.size() == 0) {
+ auto no_token_entry = base::MakeUnique<base::DictionaryValue>();
no_token_entry->SetString("accountId", "No token in Token Service.");
account_info->Append(std::move(no_token_entry));
}
- for(const std::string& account_id : accounts_in_token_service) {
- std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ for (const std::string& account_id : accounts_in_token_service) {
+ auto entry = base::MakeUnique<base::DictionaryValue>();
entry->SetString("accountId", account_id);
account_info->Append(std::move(entry));
}
+ signin_status->Set("accountInfo", std::move(account_info));
return signin_status;
}
diff --git a/chromium/components/signin/core/browser/about_signin_internals.h b/chromium/components/signin/core/browser/about_signin_internals.h
index 8e42feab409..46d11da5fe1 100644
--- a/chromium/components/signin/core/browser/about_signin_internals.h
+++ b/chromium/components/signin/core/browser/about_signin_internals.h
@@ -191,8 +191,7 @@ class AboutSigninInternals
// SigninManagerBase::Observer implementations.
void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSignedOut(const std::string& account_id,
const std::string& username) override;
diff --git a/chromium/components/signin/core/browser/access_token_fetcher.cc b/chromium/components/signin/core/browser/access_token_fetcher.cc
index d301d3708da..de834072eed 100644
--- a/chromium/components/signin/core/browser/access_token_fetcher.cc
+++ b/chromium/components/signin/core/browser/access_token_fetcher.cc
@@ -76,8 +76,7 @@ void AccessTokenFetcher::StartAccessTokenRequest() {
}
void AccessTokenFetcher::GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
DCHECK(waiting_for_sign_in_);
DCHECK(!waiting_for_refresh_token_);
DCHECK(signin_manager_->IsAuthenticated());
diff --git a/chromium/components/signin/core/browser/access_token_fetcher.h b/chromium/components/signin/core/browser/access_token_fetcher.h
index 2963ab8b797..cfa84724e48 100644
--- a/chromium/components/signin/core/browser/access_token_fetcher.h
+++ b/chromium/components/signin/core/browser/access_token_fetcher.h
@@ -51,8 +51,7 @@ class AccessTokenFetcher : public SigninManagerBase::Observer,
// SigninManagerBase::Observer implementation.
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
// OAuth2TokenService::Observer implementation.
diff --git a/chromium/components/signin/core/browser/account_fetcher_service.cc b/chromium/components/signin/core/browser/account_fetcher_service.cc
index 7f25b2d377c..6203942dac4 100644
--- a/chromium/components/signin/core/browser/account_fetcher_service.cc
+++ b/chromium/components/signin/core/browser/account_fetcher_service.cc
@@ -65,6 +65,7 @@ AccountFetcherService::AccountFetcherService()
child_info_request_(nullptr) {}
AccountFetcherService::~AccountFetcherService() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(shutdown_called_);
}
@@ -142,7 +143,7 @@ void AccountFetcherService::RefreshAllAccountInfo(bool only_fetch_if_invalid) {
// account. This is possible since we only support a single account to be a
// child anyway.
void AccountFetcherService::UpdateChildInfo() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<std::string> accounts = token_service_->GetAccounts();
if (accounts.size() == 1) {
const std::string& candidate = accounts[0];
@@ -160,7 +161,7 @@ void AccountFetcherService::UpdateChildInfo() {
}
void AccountFetcherService::MaybeEnableNetworkFetches() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!profile_loaded_ || !refresh_tokens_loaded_)
return;
if (!network_fetches_enabled_) {
@@ -199,7 +200,7 @@ void AccountFetcherService::ScheduleNextRefresh() {
// Starts fetching user information. This is called periodically to refresh.
void AccountFetcherService::StartFetchingUserInfo(
const std::string& account_id) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(network_fetches_enabled_);
std::unique_ptr<AccountInfoFetcher>& request =
@@ -341,7 +342,7 @@ void AccountFetcherService::OnRefreshTokenRevoked(
}
void AccountFetcherService::OnRefreshTokensLoaded() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
refresh_tokens_loaded_ = true;
MaybeEnableNetworkFetches();
}
diff --git a/chromium/components/signin/core/browser/account_fetcher_service.h b/chromium/components/signin/core/browser/account_fetcher_service.h
index 5ca34f1fc8a..aefdcdd3a92 100644
--- a/chromium/components/signin/core/browser/account_fetcher_service.h
+++ b/chromium/components/signin/core/browser/account_fetcher_service.h
@@ -11,7 +11,7 @@
#include <unordered_map>
#include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "base/timer/timer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "google_apis/gaia/oauth2_token_service.h"
@@ -32,8 +32,7 @@ class PrefRegistrySyncable;
}
class AccountFetcherService : public KeyedService,
- public OAuth2TokenService::Observer,
- public base::NonThreadSafe {
+ public OAuth2TokenService::Observer {
public:
// Name of the preference that tracks the int64_t representation of the last
// time the AccountTrackerService was updated.
@@ -139,6 +138,8 @@ class AccountFetcherService : public KeyedService,
std::unique_ptr<RefreshTokenAnnotationRequest>>
refresh_token_annotation_requests_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(AccountFetcherService);
};
diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc
index 5a0170eb3f6..3393ef7333e 100644
--- a/chromium/components/signin/core/browser/account_reconcilor.cc
+++ b/chromium/components/signin/core/browser/account_reconcilor.cc
@@ -222,8 +222,7 @@ void AccountReconcilor::OnEndBatchChanges() {
}
void AccountReconcilor::GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
VLOG(1) << "AccountReconcilor::GoogleSigninSucceeded: signed in";
RegisterWithCookieManagerService();
RegisterWithContentSettings();
@@ -241,7 +240,7 @@ void AccountReconcilor::GoogleSignedOut(const std::string& account_id,
}
void AccountReconcilor::PerformMergeAction(const std::string& account_id) {
- if (!switches::IsEnableAccountConsistency()) {
+ if (!switches::IsAccountConsistencyMirrorEnabled()) {
MarkAccountAsAddedToCookie(account_id);
return;
}
@@ -250,7 +249,7 @@ void AccountReconcilor::PerformMergeAction(const std::string& account_id) {
}
void AccountReconcilor::PerformLogoutAllAccountsAction() {
- if (!switches::IsEnableAccountConsistency())
+ if (!switches::IsAccountConsistencyMirrorEnabled())
return;
VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction";
cookie_manager_service_->LogOutAllAccounts(kSource);
diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h
index 6217588d974..285e6f51cf1 100644
--- a/chromium/components/signin/core/browser/account_reconcilor.h
+++ b/chromium/components/signin/core/browser/account_reconcilor.h
@@ -151,8 +151,7 @@ class AccountReconcilor : public KeyedService,
// Overriden from SigninManagerBase::Observer.
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSignedOut(const std::string& account_id,
const std::string& username) override;
diff --git a/chromium/components/signin/core/browser/account_tracker_service.cc b/chromium/components/signin/core/browser/account_tracker_service.cc
index 129b124288c..dc4dba0b394 100644
--- a/chromium/components/signin/core/browser/account_tracker_service.cc
+++ b/chromium/components/signin/core/browser/account_tracker_service.cc
@@ -60,6 +60,7 @@ const char AccountTrackerService::kNoPictureURLFound[] = "NO_PICTURE_URL";
AccountTrackerService::AccountTrackerService() : signin_client_(nullptr) {}
AccountTrackerService::~AccountTrackerService() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// static
diff --git a/chromium/components/signin/core/browser/account_tracker_service.h b/chromium/components/signin/core/browser/account_tracker_service.h
index b92e0a9515d..cc4035c4db4 100644
--- a/chromium/components/signin/core/browser/account_tracker_service.h
+++ b/chromium/components/signin/core/browser/account_tracker_service.h
@@ -13,13 +13,12 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "base/timer/timer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/signin/core/browser/account_info.h"
#include "google_apis/gaia/gaia_auth_util.h"
-
class PrefService;
class SigninClient;
@@ -33,8 +32,7 @@ class PrefRegistrySyncable;
// AccountTrackerService is a KeyedService that retrieves and caches GAIA
// information about Google Accounts.
-class AccountTrackerService : public KeyedService,
- public base::NonThreadSafe {
+class AccountTrackerService : public KeyedService {
public:
// Name of the preference property that persists the account information
// tracked by this service.
@@ -157,6 +155,8 @@ class AccountTrackerService : public KeyedService,
std::map<std::string, AccountState> accounts_;
base::ObserverList<Observer> observer_list_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(AccountTrackerService);
};
diff --git a/chromium/components/signin/core/browser/android/BUILD.gn b/chromium/components/signin/core/browser/android/BUILD.gn
index f9f482b6a91..d7d0c958299 100644
--- a/chromium/components/signin/core/browser/android/BUILD.gn
+++ b/chromium/components/signin/core/browser/android/BUILD.gn
@@ -21,10 +21,13 @@ android_library("java") {
java_files = [
"java/src/org/chromium/components/signin/AccountManagerDelegate.java",
+ "java/src/org/chromium/components/signin/AccountManagerFacade.java",
"java/src/org/chromium/components/signin/AccountManagerHelper.java",
+ "java/src/org/chromium/components/signin/AccountsChangeObserver.java",
"java/src/org/chromium/components/signin/AuthException.java",
"java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java",
"java/src/org/chromium/components/signin/ChromeSigninController.java",
+ "java/src/org/chromium/components/signin/AccountManagerDelegateException.java",
"java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java",
]
}
@@ -39,7 +42,7 @@ android_library("javatests") {
"//third_party/android_support_test_runner:runner_java",
]
- java_files = [ "javatests/src/org/chromium/components/signin/test/AccountManagerHelperTest.java" ]
+ java_files = [ "javatests/src/org/chromium/components/signin/test/AccountManagerFacadeTest.java" ]
}
android_library("signin_java_test_support") {
@@ -50,6 +53,7 @@ android_library("signin_java_test_support") {
"//base:base_java_test_support",
"//third_party/android_tools:android_support_annotations_java",
"//third_party/jsr-305:jsr_305_javalib",
+ "//third_party/junit",
]
java_files = [
diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher_android.cc b/chromium/components/signin/core/browser/child_account_info_fetcher_android.cc
index 34446dad77e..2fbdd2a0076 100644
--- a/chromium/components/signin/core/browser/child_account_info_fetcher_android.cc
+++ b/chromium/components/signin/core/browser/child_account_info_fetcher_android.cc
@@ -50,11 +50,6 @@ ChildAccountInfoFetcherAndroid::~ChildAccountInfoFetcherAndroid() {
j_child_account_info_fetcher_.obj());
}
-// static
-bool ChildAccountInfoFetcherAndroid::Register(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
void SetIsChildAccount(JNIEnv* env,
const JavaParamRef<jclass>& caller,
jlong native_service,
diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher_android.h b/chromium/components/signin/core/browser/child_account_info_fetcher_android.h
index 05f30a177fc..aee7ed9a473 100644
--- a/chromium/components/signin/core/browser/child_account_info_fetcher_android.h
+++ b/chromium/components/signin/core/browser/child_account_info_fetcher_android.h
@@ -22,9 +22,6 @@ class ChildAccountInfoFetcherAndroid : public ChildAccountInfoFetcher {
static void InitializeForTests();
- // Register JNI methods.
- static bool Register(JNIEnv* env);
-
private:
ChildAccountInfoFetcherAndroid(AccountFetcherService* 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
new file mode 100644
index 00000000000..12486b67317
--- /dev/null
+++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc
@@ -0,0 +1,171 @@
+// 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/chrome_connected_header_helper.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "url/gurl.h"
+
+namespace signin {
+
+namespace {
+
+const char kContinueUrlAttrName[] = "continue_url";
+const char kEmailAttrName[] = "email";
+const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";
+const char kGaiaIdAttrName[] = "id";
+const char kIsSameTabAttrName[] = "is_same_tab";
+const char kIsSamlAttrName[] = "is_saml";
+const char kProfileModeAttrName[] = "mode";
+const char kServiceTypeAttrName[] = "action";
+
+// Determines the service type that has been passed from Gaia in the header.
+GAIAServiceType GetGAIAServiceTypeFromHeader(const std::string& header_value) {
+ if (header_value == "SIGNOUT")
+ return GAIA_SERVICE_TYPE_SIGNOUT;
+ else if (header_value == "INCOGNITO")
+ return GAIA_SERVICE_TYPE_INCOGNITO;
+ else if (header_value == "ADDSESSION")
+ return GAIA_SERVICE_TYPE_ADDSESSION;
+ else if (header_value == "REAUTH")
+ return GAIA_SERVICE_TYPE_REAUTH;
+ else if (header_value == "SIGNUP")
+ return GAIA_SERVICE_TYPE_SIGNUP;
+ else if (header_value == "DEFAULT")
+ return GAIA_SERVICE_TYPE_DEFAULT;
+ else
+ return GAIA_SERVICE_TYPE_NONE;
+}
+
+} // namespace
+
+// static
+std::string ChromeConnectedHeaderHelper::BuildRequestCookieIfPossible(
+ const GURL& url,
+ const std::string& account_id,
+ const content_settings::CookieSettings* cookie_settings,
+ int profile_mode_mask) {
+ ChromeConnectedHeaderHelper chrome_connected_helper;
+ if (!chrome_connected_helper.ShouldBuildRequestHeader(url, cookie_settings))
+ return "";
+ return chrome_connected_helper.BuildRequestHeader(
+ false /* is_header_request */, url, account_id, profile_mode_mask);
+}
+
+// static
+ManageAccountsParams ChromeConnectedHeaderHelper::BuildManageAccountsParams(
+ const std::string& header_value) {
+ DCHECK(!header_value.empty());
+ ManageAccountsParams params;
+ ResponseHeaderDictionary header_dictionary =
+ ParseAccountConsistencyResponseHeader(header_value);
+ ResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
+ for (; it != header_dictionary.end(); ++it) {
+ const std::string key_name(it->first);
+ const std::string value(it->second);
+ if (key_name == kServiceTypeAttrName) {
+ params.service_type = GetGAIAServiceTypeFromHeader(value);
+ } else if (key_name == kEmailAttrName) {
+ params.email = value;
+ } else if (key_name == kIsSamlAttrName) {
+ params.is_saml = value == "true";
+ } else if (key_name == kContinueUrlAttrName) {
+ params.continue_url = value;
+ } else if (key_name == kIsSameTabAttrName) {
+ params.is_same_tab = value == "true";
+ } else {
+ DLOG(WARNING) << "Unexpected Gaia header attribute '" << key_name << "'.";
+ }
+ }
+ return params;
+}
+
+bool ChromeConnectedHeaderHelper::IsUrlEligibleToIncludeGaiaId(
+ const GURL& url,
+ bool is_header_request) {
+ if (is_header_request) {
+ // Gaia ID is only necessary for Drive. Don't set it otherwise.
+ return IsDriveOrigin(url.GetOrigin());
+ }
+
+ // Cookie requests don't have the granularity to only include the Gaia ID for
+ // Drive origin. Set it on all google.com instead.
+ if (!url.SchemeIsCryptographic())
+ return false;
+
+ const std::string kGoogleDomain = "google.com";
+ std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
+ url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ return domain == kGoogleDomain;
+}
+
+bool ChromeConnectedHeaderHelper::IsDriveOrigin(const GURL& url) {
+ if (!url.SchemeIsCryptographic())
+ return false;
+
+ const GURL kGoogleDriveURL("https://drive.google.com");
+ const GURL kGoogleDocsURL("https://docs.google.com");
+ return url == kGoogleDriveURL || url == kGoogleDocsURL;
+}
+
+bool ChromeConnectedHeaderHelper::IsUrlEligibleForRequestHeader(
+ const GURL& url) {
+ // Only set the header for Drive and Gaia always, and other Google properties
+ // if account consistency is enabled. Vasquette, which is integrated with most
+ // Google properties, needs the header to redirect certain user actions to
+ // Chrome native UI. Drive and Gaia need the header to tell if the current
+ // user is connected.
+
+ // Consider the account ID sensitive and limit it to secure domains.
+ if (!url.SchemeIsCryptographic())
+ return false;
+
+ GURL origin(url.GetOrigin());
+ bool is_enable_account_consistency =
+ switches::IsAccountConsistencyMirrorEnabled();
+ bool is_google_url = is_enable_account_consistency &&
+ (google_util::IsGoogleDomainUrl(
+ url, google_util::ALLOW_SUBDOMAIN,
+ google_util::DISALLOW_NON_STANDARD_PORTS) ||
+ google_util::IsYoutubeDomainUrl(
+ url, google_util::ALLOW_SUBDOMAIN,
+ google_util::DISALLOW_NON_STANDARD_PORTS));
+ return is_google_url || IsDriveOrigin(origin) ||
+ gaia::IsGaiaSignonRealm(origin);
+}
+
+std::string ChromeConnectedHeaderHelper::BuildRequestHeader(
+ bool is_header_request,
+ const GURL& url,
+ const std::string& account_id,
+ int profile_mode_mask) {
+ if (account_id.empty())
+ return std::string();
+
+ std::vector<std::string> parts;
+ if (IsUrlEligibleToIncludeGaiaId(url, is_header_request)) {
+ // Only set the Gaia ID on domains that actually requires it.
+ parts.push_back(
+ base::StringPrintf("%s=%s", kGaiaIdAttrName, account_id.c_str()));
+ }
+ parts.push_back(
+ base::StringPrintf("%s=%s", kProfileModeAttrName,
+ base::IntToString(profile_mode_mask).c_str()));
+ parts.push_back(base::StringPrintf(
+ "%s=%s", kEnableAccountConsistencyAttrName,
+ switches::IsAccountConsistencyMirrorEnabled() ? "true" : "false"));
+
+ return base::JoinString(parts, is_header_request ? "," : ":");
+}
+
+} // namespace signin
diff --git a/chromium/components/signin/core/browser/chrome_connected_header_helper.h b/chromium/components/signin/core/browser/chrome_connected_header_helper.h
new file mode 100644
index 00000000000..72ea404e3e0
--- /dev/null
+++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.h
@@ -0,0 +1,55 @@
+// 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_CHROME_CONNECTED_HEADER_HELPER_H_
+#define COMPONENTS_SIGNIN_CORE_BROWSER_CHROME_CONNECTED_HEADER_HELPER_H_
+
+#include <string>
+
+#include "components/signin/core/browser/signin_header_helper.h"
+
+class GURL;
+
+namespace signin {
+
+// SigninHeaderHelper implementation managing the "X-Chrome-Connected" header.
+class ChromeConnectedHeaderHelper : public SigninHeaderHelper {
+ public:
+ ChromeConnectedHeaderHelper() {}
+ ~ChromeConnectedHeaderHelper() override {}
+
+ // Returns the Chrome-Connected cookie, or an empty string if it should not be
+ // added to the request to |url|.
+ static std::string BuildRequestCookieIfPossible(
+ const GURL& url,
+ const std::string& account_id,
+ const content_settings::CookieSettings* cookie_settings,
+ int profile_mode_mask);
+
+ // Returns the parameters contained in the X-Chrome-Manage-Accounts response
+ // header.
+ static ManageAccountsParams BuildManageAccountsParams(
+ const std::string& header_value);
+
+ // Returns the value for the Chrome-Connected request header. May return the
+ // empty string, in this case the header must not be added.
+ std::string BuildRequestHeader(bool is_header_request,
+ const GURL& url,
+ const std::string& account_id,
+ int profile_mode_mask);
+
+ private:
+ // Returns whether the URL is eligible for the Gaia ID parameter.
+ bool IsUrlEligibleToIncludeGaiaId(const GURL& url, bool is_header_request);
+
+ // Returns whether the URL has a Google Drive origin.
+ bool IsDriveOrigin(const GURL& url);
+
+ // SigninHeaderHelper implementation:
+ bool IsUrlEligibleForRequestHeader(const GURL& url) override;
+};
+
+} // namespace signin
+
+#endif // COMPONENTS_SIGNIN_CORE_BROWSER_CHROME_CONNECTED_HEADER_HELPER_H_
diff --git a/chromium/components/signin/core/browser/dice_header_helper.cc b/chromium/components/signin/core/browser/dice_header_helper.cc
new file mode 100644
index 00000000000..4d8aade04f2
--- /dev/null
+++ b/chromium/components/signin/core/browser/dice_header_helper.cc
@@ -0,0 +1,147 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/core/browser/dice_header_helper.h"
+
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_urls.h"
+
+namespace signin {
+
+namespace {
+
+const char kSigninActionAttrName[] = "action";
+const char kSigninAuthUserAttrName[] = "authuser";
+const char kSigninAuthorizationCodeAttrName[] = "authorization_code";
+const char kSigninEmailAttrName[] = "email";
+const char kSigninIdAttrName[] = "id";
+const char kSignoutEmailAttrName[] = "email";
+const char kSignoutSessionIndexAttrName[] = "sessionindex";
+const char kSignoutObfuscatedIDAttrName[] = "obfuscatedid";
+
+// Determines the Dice action that has been passed from Gaia in the header.
+DiceAction GetDiceActionFromHeader(const std::string& value) {
+ if (value == "SIGNIN")
+ return DiceAction::SIGNIN;
+ else if (value == "SIGNOUT")
+ return DiceAction::SIGNOUT;
+ else
+ return DiceAction::NONE;
+}
+
+} // namespace
+
+// static
+DiceResponseParams DiceHeaderHelper::BuildDiceSigninResponseParams(
+ const std::string& header_value) {
+ DCHECK(!header_value.empty());
+ DiceResponseParams params;
+ DiceResponseParams::SigninInfo& info = params.signin_info;
+ ResponseHeaderDictionary header_dictionary =
+ ParseAccountConsistencyResponseHeader(header_value);
+ ResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
+ for (; it != header_dictionary.end(); ++it) {
+ const std::string key_name(it->first);
+ const std::string value(it->second);
+ if (key_name == kSigninActionAttrName) {
+ params.user_intention = GetDiceActionFromHeader(value);
+ } else if (key_name == kSigninIdAttrName) {
+ info.gaia_id = value;
+ } else if (key_name == kSigninEmailAttrName) {
+ info.email = value;
+ } else if (key_name == kSigninAuthUserAttrName) {
+ bool parse_success = base::StringToInt(value, &info.session_index);
+ if (!parse_success)
+ info.session_index = -1;
+ } else if (key_name == kSigninAuthorizationCodeAttrName) {
+ info.authorization_code = value;
+ } else {
+ DLOG(WARNING) << "Unexpected Gaia header attribute '" << key_name << "'.";
+ }
+ }
+
+ if (params.user_intention != DiceAction::SIGNIN) {
+ DLOG(WARNING)
+ << "Only SIGNIN is supported through X-Chrome-ID-Consistency-Response :"
+ << header_value;
+ params.user_intention = DiceAction::NONE;
+ }
+
+ if (info.gaia_id.empty() || info.email.empty() || info.session_index == -1 ||
+ info.authorization_code.empty()) {
+ DLOG(WARNING) << "Missing header parameters for Dice SIGNIN: "
+ << header_value;
+ params.user_intention = DiceAction::NONE;
+ }
+
+ return params;
+}
+
+// static
+DiceResponseParams DiceHeaderHelper::BuildDiceSignoutResponseParams(
+ const std::string& header_value) {
+ // Google internal documentation of this header at:
+ // http://go/gaia-response-headers
+ DCHECK(!header_value.empty());
+ DiceResponseParams params;
+ params.user_intention = DiceAction::SIGNOUT;
+ DiceResponseParams::SignoutInfo& info = params.signout_info;
+ ResponseHeaderDictionary header_dictionary =
+ ParseAccountConsistencyResponseHeader(header_value);
+ ResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
+ for (; it != header_dictionary.end(); ++it) {
+ const std::string key_name(it->first);
+ const std::string value(it->second);
+ if (key_name == kSignoutObfuscatedIDAttrName) {
+ info.gaia_id.push_back(value);
+ // The Gaia ID is wrapped in quotes.
+ base::TrimString(value, "\"", &info.gaia_id.back());
+ } else if (key_name == kSignoutEmailAttrName) {
+ // The email is wrapped in quotes.
+ info.email.push_back(value);
+ base::TrimString(value, "\"", &info.email.back());
+ } else if (key_name == kSignoutSessionIndexAttrName) {
+ int session_index = -1;
+ bool parse_success = base::StringToInt(value, &session_index);
+ if (parse_success)
+ info.session_index.push_back(session_index);
+ } else {
+ DLOG(WARNING) << "Unexpected Gaia header attribute '" << key_name << "'.";
+ }
+ }
+
+ if ((info.gaia_id.size() != info.email.size()) ||
+ (info.gaia_id.size() != info.session_index.size())) {
+ DLOG(WARNING) << "Invalid parameter count for Dice SIGNOUT header: "
+ << header_value;
+ params.user_intention = DiceAction::NONE;
+ }
+
+ return params;
+}
+
+bool DiceHeaderHelper::IsUrlEligibleForRequestHeader(const GURL& url) {
+ if (switches::GetAccountConsistencyMethod() !=
+ switches::AccountConsistencyMethod::kDice) {
+ return false;
+ }
+ return gaia::IsGaiaSignonRealm(url.GetOrigin());
+}
+
+std::string DiceHeaderHelper::BuildRequestHeader(const std::string& account_id,
+ bool sync_enabled) {
+ std::vector<std::string> parts;
+ parts.push_back("client_id=" +
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id());
+ if (sync_enabled)
+ parts.push_back("sync_account_id=" + account_id);
+ return base::JoinString(parts, ",");
+}
+
+} // namespace signin
diff --git a/chromium/components/signin/core/browser/dice_header_helper.h b/chromium/components/signin/core/browser/dice_header_helper.h
new file mode 100644
index 00000000000..d83fa037693
--- /dev/null
+++ b/chromium/components/signin/core/browser/dice_header_helper.h
@@ -0,0 +1,44 @@
+// 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_DICE_HEADER_HELPER_H_
+#define COMPONENTS_SIGNIN_CORE_BROWSER_DICE_HEADER_HELPER_H_
+
+#include <string>
+
+#include "components/signin/core/browser/signin_header_helper.h"
+
+class GURL;
+
+namespace signin {
+
+// SigninHeaderHelper implementation managing the Dice header.
+class DiceHeaderHelper : public SigninHeaderHelper {
+ public:
+ DiceHeaderHelper() {}
+ ~DiceHeaderHelper() override {}
+
+ // Returns the parameters contained in the X-Chrome-ID-Consistency-Response
+ // response header.
+ static DiceResponseParams BuildDiceSigninResponseParams(
+ const std::string& header_value);
+
+ // Returns the parameters contained in the Google-Accounts-SignOut response
+ // header.
+ static DiceResponseParams BuildDiceSignoutResponseParams(
+ const std::string& header_value);
+
+ // Returns the header value for Dice requests. Returns the empty string when
+ // the header must not be added.
+ std::string BuildRequestHeader(const std::string& account_id,
+ bool sync_enabled);
+
+ private:
+ // SigninHeaderHelper implementation:
+ bool IsUrlEligibleForRequestHeader(const GURL& url) override;
+};
+
+} // namespace signin
+
+#endif // COMPONENTS_SIGNIN_CORE_BROWSER_DICE_HEADER_HELPER_H_
diff --git a/chromium/components/signin/core/browser/fake_signin_manager.cc b/chromium/components/signin/core/browser/fake_signin_manager.cc
index 32caba99d7b..104d8ea429c 100644
--- a/chromium/components/signin/core/browser/fake_signin_manager.cc
+++ b/chromium/components/signin/core/browser/fake_signin_manager.cc
@@ -31,7 +31,8 @@ FakeSigninManager::FakeSigninManager(
: SigninManager(client,
token_service,
account_tracker_service,
- cookie_manager_service) {}
+ cookie_manager_service),
+ token_service_(token_service) {}
FakeSigninManager::~FakeSigninManager() {}
@@ -57,8 +58,9 @@ void FakeSigninManager::CompletePendingSignin() {
SetAuthenticatedAccountId(GetAccountIdForAuthInProgress());
set_auth_in_progress(std::string());
for (auto& observer : observer_list_) {
- observer.GoogleSigninSucceeded(authenticated_account_id_, username_,
- password_);
+ observer.GoogleSigninSucceeded(authenticated_account_id_, username_);
+ observer.GoogleSigninSucceededWithPassword(authenticated_account_id_,
+ username_, password_);
}
}
@@ -91,6 +93,8 @@ void FakeSigninManager::SignOut(
const std::string account_id = GetAuthenticatedAccountId();
const std::string username = GetAuthenticatedAccountInfo().email;
authenticated_account_id_.clear();
+ if (token_service_)
+ token_service_->RevokeAllCredentials();
for (auto& observer : observer_list_)
observer.GoogleSignedOut(account_id, username);
diff --git a/chromium/components/signin/core/browser/fake_signin_manager.h b/chromium/components/signin/core/browser/fake_signin_manager.h
index 5ba63b81e53..b3943e0f256 100644
--- a/chromium/components/signin/core/browser/fake_signin_manager.h
+++ b/chromium/components/signin/core/browser/fake_signin_manager.h
@@ -13,9 +13,7 @@
#include "components/signin/core/browser/signin_manager.h"
#include "components/signin/core/browser/signin_metrics.h"
-// SigninManager to use for testing. Tests should use the type
-// SigninManagerForTesting to ensure that the right type for their platform is
-// used.
+// SigninManager to use for testing.
class FakeSigninManagerBase : public SigninManagerBase {
public:
@@ -44,7 +42,7 @@ class FakeSigninManager : public SigninManager {
void set_password(const std::string& password) { password_ = password; }
- void SignIn(const std::string& account_id,
+ void SignIn(const std::string& gaia_id,
const std::string& username,
const std::string& password);
@@ -66,6 +64,8 @@ class FakeSigninManager : public SigninManager {
// Username specified in StartSignInWithRefreshToken() call.
std::string username_;
+
+ ProfileOAuth2TokenService* token_service_;
};
#endif // !defined (OS_CHROMEOS)
diff --git a/chromium/components/signin/core/browser/profile_identity_provider.cc b/chromium/components/signin/core/browser/profile_identity_provider.cc
index 3be796e2d64..a537d4b718a 100644
--- a/chromium/components/signin/core/browser/profile_identity_provider.cc
+++ b/chromium/components/signin/core/browser/profile_identity_provider.cc
@@ -42,8 +42,7 @@ bool ProfileIdentityProvider::RequestLogin() {
void ProfileIdentityProvider::GoogleSigninSucceeded(
const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
FireOnActiveAccountLogin();
}
diff --git a/chromium/components/signin/core/browser/profile_identity_provider.h b/chromium/components/signin/core/browser/profile_identity_provider.h
index ca3e1a590a3..eef7e346787 100644
--- a/chromium/components/signin/core/browser/profile_identity_provider.h
+++ b/chromium/components/signin/core/browser/profile_identity_provider.h
@@ -32,8 +32,7 @@ class ProfileIdentityProvider : public IdentityProvider,
// SigninManagerBase::Observer:
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSignedOut(const std::string& account_id,
const std::string& username) override;
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 7e8eb2a22c9..8cf2fd90b28 100644
--- a/chromium/components/signin/core/browser/profile_oauth2_token_service.cc
+++ b/chromium/components/signin/core/browser/profile_oauth2_token_service.cc
@@ -6,7 +6,7 @@
ProfileOAuth2TokenService::ProfileOAuth2TokenService(
std::unique_ptr<OAuth2TokenServiceDelegate> delegate)
- : OAuth2TokenService(std::move(delegate)) {
+ : OAuth2TokenService(std::move(delegate)), all_credentials_loaded_(false) {
AddObserver(this);
}
@@ -24,6 +24,10 @@ void ProfileOAuth2TokenService::LoadCredentials(
GetDelegate()->LoadCredentials(primary_account_id);
}
+bool ProfileOAuth2TokenService::AreAllCredentialsLoaded() {
+ return all_credentials_loaded_;
+}
+
void ProfileOAuth2TokenService::UpdateCredentials(
const std::string& account_id,
const std::string& refresh_token) {
@@ -50,3 +54,7 @@ void ProfileOAuth2TokenService::OnRefreshTokenRevoked(
CancelRequestsForAccount(account_id);
ClearCacheForAccount(account_id);
}
+
+void ProfileOAuth2TokenService::OnRefreshTokensLoaded() {
+ all_credentials_loaded_ = true;
+}
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 fadf1417bd1..3372a924c99 100644
--- a/chromium/components/signin/core/browser/profile_oauth2_token_service.h
+++ b/chromium/components/signin/core/browser/profile_oauth2_token_service.h
@@ -42,13 +42,13 @@ class ProfileOAuth2TokenService : public OAuth2TokenService,
// Loads credentials from a backing persistent store to make them available
// after service is used between profile restarts.
//
- // Only call this method if there is at least one account connected to the
- // profile, otherwise startup will cause unneeded work on the IO thread. The
- // primary account is specified with the |primary_account_id| argument. If
- // empty, no credentials will be loaded. For a regular profile, the primary
- // account id comes from SigninManager. For a supervised user, the id comes
- // from SupervisedUserService.
- virtual void LoadCredentials(const std::string& primary_account_id);
+ // The primary account is specified with the |primary_account_id| argument.
+ // For a regular profile, the primary account id comes from SigninManager.
+ // For a supervised user, the id comes from SupervisedUserService.
+ void LoadCredentials(const std::string& primary_account_id);
+
+ // Returns true iff all credentials have been loaded from disk.
+ bool AreAllCredentialsLoaded();
// Updates a |refresh_token| for an |account_id|. Credentials are persisted,
// and available through |LoadCredentials| after service is restarted.
@@ -64,6 +64,10 @@ class ProfileOAuth2TokenService : public OAuth2TokenService,
private:
void OnRefreshTokenAvailable(const std::string& account_id) override;
void OnRefreshTokenRevoked(const std::string& account_id) override;
+ void OnRefreshTokensLoaded() override;
+
+ // Whether all credentials have been loaded.
+ bool all_credentials_loaded_;
DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenService);
};
diff --git a/chromium/components/signin/core/browser/refresh_token_annotation_request.cc b/chromium/components/signin/core/browser/refresh_token_annotation_request.cc
index 191dab8ebf4..15c43148f56 100644
--- a/chromium/components/signin/core/browser/refresh_token_annotation_request.cc
+++ b/chromium/components/signin/core/browser/refresh_token_annotation_request.cc
@@ -17,6 +17,7 @@
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/base/escape.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_request_context_getter.h"
namespace {
@@ -41,7 +42,7 @@ RefreshTokenAnnotationRequest::RefreshTokenAnnotationRequest(
}
RefreshTokenAnnotationRequest::~RefreshTokenAnnotationRequest() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// static
@@ -102,7 +103,7 @@ bool RefreshTokenAnnotationRequest::ShouldSendNow(PrefService* pref_service) {
void RefreshTokenAnnotationRequest::RequestAccessToken(
OAuth2TokenService* token_service,
const std::string& account_id) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
OAuth2TokenService::ScopeSet scopes;
scopes.insert(GaiaConstants::kOAuth1LoginScope);
access_token_request_ = token_service->StartRequest(account_id, scopes, this);
@@ -112,7 +113,7 @@ void RefreshTokenAnnotationRequest::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Got access token";
Start(request_context_getter_.get(), access_token);
}
@@ -120,7 +121,7 @@ void RefreshTokenAnnotationRequest::OnGetTokenSuccess(
void RefreshTokenAnnotationRequest::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Failed to get access token";
RecordRequestStatusHistogram(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, request_callback_);
@@ -156,7 +157,7 @@ std::string RefreshTokenAnnotationRequest::CreateApiCallBody() {
void RefreshTokenAnnotationRequest::ProcessApiCallSuccess(
const net::URLFetcher* source) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Request succeeded";
RecordRequestStatusHistogram(true);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, request_callback_);
@@ -165,9 +166,40 @@ void RefreshTokenAnnotationRequest::ProcessApiCallSuccess(
void RefreshTokenAnnotationRequest::ProcessApiCallFailure(
const net::URLFetcher* source) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Request failed";
RecordRequestStatusHistogram(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, request_callback_);
request_callback_.Reset();
}
+
+net::PartialNetworkTrafficAnnotationTag
+RefreshTokenAnnotationRequest::GetNetworkTrafficAnnotationTag() {
+ return net::DefinePartialNetworkTrafficAnnotation(
+ "refresh_token_annotation_request", "oauth2_api_call_flow", R"(
+ semantics {
+ sender: "Account Fetcher Service"
+ description:
+ "Sends request to /IssueToken endpoint with device_id to backfill "
+ "device info for refresh tokens issued pre-M38."
+ trigger:
+ "When refreshing account information in AccountFetcherService. On "
+ "chrome startup and at most once per day."
+ data:
+ "OAuth 2.0 access token, Chrome's client id and version number, and "
+ "a single signin scoped, randomly generated device id for Chrome "
+ "profile."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ setting:
+ "This feature cannot be disabled by settings, however the request is "
+ "made only for signed-in users."
+ chrome_policy {
+ SigninAllowed {
+ policy_options {mode: MANDATORY}
+ SigninAllowed: false
+ }
+ }
+ })");
+}
diff --git a/chromium/components/signin/core/browser/refresh_token_annotation_request.h b/chromium/components/signin/core/browser/refresh_token_annotation_request.h
index 483830bfea4..2e0868c43ca 100644
--- a/chromium/components/signin/core/browser/refresh_token_annotation_request.h
+++ b/chromium/components/signin/core/browser/refresh_token_annotation_request.h
@@ -10,7 +10,7 @@
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
#include "google_apis/gaia/oauth2_api_call_flow.h"
#include "google_apis/gaia/oauth2_token_service.h"
@@ -22,8 +22,7 @@ class SigninClient;
// important to keep server QPS low therefore this request is sent on average
// once per 10 days per profile.
// This code shold be removed once majority of refresh tokens are updated.
-class RefreshTokenAnnotationRequest : public base::NonThreadSafe,
- public OAuth2TokenService::Consumer,
+class RefreshTokenAnnotationRequest : public OAuth2TokenService::Consumer,
public OAuth2ApiCallFlow {
public:
~RefreshTokenAnnotationRequest() override;
@@ -59,6 +58,8 @@ class RefreshTokenAnnotationRequest : public base::NonThreadSafe,
std::string CreateApiCallBody() override;
void ProcessApiCallSuccess(const net::URLFetcher* source) override;
void ProcessApiCallFailure(const net::URLFetcher* source) override;
+ net::PartialNetworkTrafficAnnotationTag GetNetworkTrafficAnnotationTag()
+ override;
private:
FRIEND_TEST_ALL_PREFIXES(RefreshTokenAnnotationRequestTest, ShouldSendNow);
@@ -84,6 +85,8 @@ class RefreshTokenAnnotationRequest : public base::NonThreadSafe,
std::unique_ptr<OAuth2TokenService::Request> access_token_request_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(RefreshTokenAnnotationRequest);
};
diff --git a/chromium/components/signin/core/browser/resources/signin_index.html b/chromium/components/signin/core/browser/resources/signin_index.html
index 7965ffe6e38..513a934658d 100644
--- a/chromium/components/signin/core/browser/resources/signin_index.html
+++ b/chromium/components/signin/core/browser/resources/signin_index.html
@@ -1,5 +1,5 @@
<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<title>Signin Internals</title>
diff --git a/chromium/components/signin/core/browser/signin_header_helper.cc b/chromium/components/signin/core/browser/signin_header_helper.cc
index 668d25d4492..0a326c1f807 100644
--- a/chromium/components/signin/core/browser/signin_header_helper.cc
+++ b/chromium/components/signin/core/browser/signin_header_helper.cc
@@ -6,143 +6,26 @@
#include <stddef.h>
+#include "base/logging.h"
#include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "build/build_config.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/google/core/browser/google_util.h"
+#include "components/signin/core/browser/chrome_connected_header_helper.h"
#include "components/signin/core/common/profile_management_switches.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/base/escape.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
-#include "url/gurl.h"
-namespace {
-
-// Dictionary of fields in a mirror response header.
-typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary;
-
-const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts";
-const char kContinueUrlAttrName[] = "continue_url";
-const char kEmailAttrName[] = "email";
-const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";
-const char kGaiaIdAttrName[] = "id";
-const char kProfileModeAttrName[] = "mode";
-const char kIsSameTabAttrName[] = "is_same_tab";
-const char kIsSamlAttrName[] = "is_saml";
-const char kServiceTypeAttrName[] = "action";
-
-bool IsDriveOrigin(const GURL& url) {
- if (!url.SchemeIsCryptographic())
- return false;
-
- const GURL kGoogleDriveURL("https://drive.google.com");
- const GURL kGoogleDocsURL("https://docs.google.com");
- return url == kGoogleDriveURL || url == kGoogleDocsURL;
-}
-
-bool IsUrlEligibleToIncludeGaiaId(const GURL& url, bool is_header_request) {
- if (is_header_request) {
- // GAIA Id is only necessary for Drive. Don't set it otherwise.
- return IsDriveOrigin(url.GetOrigin());
- }
-
- // Cookie requests don't have the granularity to only include the GAIA Id for
- // Drive origin. Set it on all google.com instead.
- if (!url.SchemeIsCryptographic())
- return false;
-
- const std::string kGoogleDomain = "google.com";
- std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
- url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
- return domain == kGoogleDomain;
-}
-
-// Determines the service type that has been passed from GAIA in the header.
-signin::GAIAServiceType GetGAIAServiceTypeFromHeader(
- const std::string& header_value) {
- if (header_value == "SIGNOUT")
- return signin::GAIA_SERVICE_TYPE_SIGNOUT;
- else if (header_value == "INCOGNITO")
- return signin::GAIA_SERVICE_TYPE_INCOGNITO;
- else if (header_value == "ADDSESSION")
- return signin::GAIA_SERVICE_TYPE_ADDSESSION;
- else if (header_value == "REAUTH")
- return signin::GAIA_SERVICE_TYPE_REAUTH;
- else if (header_value == "SIGNUP")
- return signin::GAIA_SERVICE_TYPE_SIGNUP;
- else if (header_value == "DEFAULT")
- return signin::GAIA_SERVICE_TYPE_DEFAULT;
- else
- return signin::GAIA_SERVICE_TYPE_NONE;
-}
-
-// Parses the mirror response header. Its expected format is
-// "key1=value1,key2=value2,...".
-MirrorResponseHeaderDictionary ParseMirrorResponseHeader(
- const std::string& header_value) {
- MirrorResponseHeaderDictionary dictionary;
- for (const base::StringPiece& field :
- base::SplitStringPiece(header_value, ",", base::KEEP_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY)) {
- size_t delim = field.find_first_of('=');
- if (delim == std::string::npos) {
- DLOG(WARNING) << "Unexpected GAIA header field '" << field << "'.";
- continue;
- }
- dictionary[field.substr(0, delim).as_string()] = net::UnescapeURLComponent(
- field.substr(delim + 1).as_string(),
- net::UnescapeRule::PATH_SEPARATORS |
- net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
- }
- return dictionary;
-}
-
-std::string BuildMirrorRequestIfPossible(
- bool is_header_request,
- const GURL& url,
- const std::string& account_id,
- const content_settings::CookieSettings* cookie_settings,
- int profile_mode_mask) {
- if (account_id.empty())
- return std::string();
-
- // If signin cookies are not allowed, don't add the header.
- if (!signin::SettingsAllowSigninCookies(cookie_settings)) {
- return std::string();
- }
-
- // Check if url is elligible for the header.
- if (!signin::IsUrlEligibleForXChromeConnectedHeader(url))
- return std::string();
-
- std::vector<std::string> parts;
- if (IsUrlEligibleToIncludeGaiaId(url, is_header_request)) {
- // Only set the GAIA Id on domains that actually requires it.
- parts.push_back(
- base::StringPrintf("%s=%s", kGaiaIdAttrName, account_id.c_str()));
- }
- parts.push_back(
- base::StringPrintf("%s=%s", kProfileModeAttrName,
- base::IntToString(profile_mode_mask).c_str()));
- parts.push_back(base::StringPrintf(
- "%s=%s", kEnableAccountConsistencyAttrName,
- switches::IsEnableAccountConsistency() ? "true" : "false"));
-
- return base::JoinString(parts, is_header_request ? "," : ":");
-}
-
-} // namespace
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#include "components/signin/core/browser/dice_header_helper.h"
+#endif
namespace signin {
extern const char kChromeConnectedHeader[] = "X-Chrome-Connected";
+extern const char kDiceRequestHeader[] = "X-Chrome-ID-Consistency-Request";
ManageAccountsParams::ManageAccountsParams()
: service_type(GAIA_SERVICE_TYPE_NONE),
@@ -150,15 +33,22 @@ ManageAccountsParams::ManageAccountsParams()
is_saml(false),
continue_url(""),
is_same_tab(false) {
-#if !defined(OS_IOS)
- child_id = 0;
- route_id = 0;
-#endif // !defined(OS_IOS)
}
-ManageAccountsParams::ManageAccountsParams(const ManageAccountsParams& other) =
+ManageAccountsParams::ManageAccountsParams(const ManageAccountsParams&) =
default;
+// Trivial constructors and destructors.
+DiceResponseParams::DiceResponseParams() : user_intention(DiceAction::NONE) {}
+DiceResponseParams::~DiceResponseParams() {}
+DiceResponseParams::DiceResponseParams(const DiceResponseParams&) = default;
+DiceResponseParams::SigninInfo::SigninInfo() {}
+DiceResponseParams::SigninInfo::~SigninInfo() {}
+DiceResponseParams::SigninInfo::SigninInfo(const SigninInfo&) = default;
+DiceResponseParams::SignoutInfo::SignoutInfo() {}
+DiceResponseParams::SignoutInfo::~SignoutInfo() {}
+DiceResponseParams::SignoutInfo::SignoutInfo(const SignoutInfo&) = default;
+
bool SettingsAllowSigninCookies(
const content_settings::CookieSettings* cookie_settings) {
GURL gaia_url = GaiaUrls::GetInstance()->gaia_url();
@@ -173,110 +63,113 @@ std::string BuildMirrorRequestCookieIfPossible(
const std::string& account_id,
const content_settings::CookieSettings* cookie_settings,
int profile_mode_mask) {
- return BuildMirrorRequestIfPossible(false /* is_header_request */, url,
- account_id, cookie_settings,
- profile_mode_mask);
+ return signin::ChromeConnectedHeaderHelper::BuildRequestCookieIfPossible(
+ url, account_id, cookie_settings, profile_mode_mask);
}
-bool AppendOrRemoveMirrorRequestHeaderIfPossible(
+bool SigninHeaderHelper::AppendOrRemoveRequestHeader(
net::URLRequest* request,
const GURL& redirect_url,
- const std::string& account_id,
- const content_settings::CookieSettings* cookie_settings,
- int profile_mode_mask) {
- const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
- std::string header_value = BuildMirrorRequestIfPossible(
- true /* is_header_request */, url, account_id, cookie_settings,
- profile_mode_mask);
+ const char* header_name,
+ const std::string& header_value) {
if (header_value.empty()) {
- // If the request is being redirected, and it has the x-chrome-connected
+ // 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(
- signin::kChromeConnectedHeader) &&
- signin::IsUrlEligibleForXChromeConnectedHeader(request->url()) &&
- !signin::IsUrlEligibleForXChromeConnectedHeader(redirect_url)) {
- request->RemoveRequestHeaderByName(signin::kChromeConnectedHeader);
+ request->extra_request_headers().HasHeader(header_name) &&
+ IsUrlEligibleForRequestHeader(request->url()) &&
+ !IsUrlEligibleForRequestHeader(redirect_url)) {
+ request->RemoveRequestHeaderByName(header_name);
}
return false;
}
- request->SetExtraRequestHeaderByName(kChromeConnectedHeader, header_value,
- false);
+ request->SetExtraRequestHeaderByName(header_name, header_value, false);
return true;
}
-ManageAccountsParams BuildManageAccountsParams(
+// static
+SigninHeaderHelper::ResponseHeaderDictionary
+SigninHeaderHelper::ParseAccountConsistencyResponseHeader(
const std::string& header_value) {
- signin::ManageAccountsParams params;
- MirrorResponseHeaderDictionary header_dictionary =
- ParseMirrorResponseHeader(header_value);
- MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
- for (; it != header_dictionary.end(); ++it) {
- const std::string key_name(it->first);
- if (key_name == kServiceTypeAttrName) {
- params.service_type =
- GetGAIAServiceTypeFromHeader(header_dictionary[kServiceTypeAttrName]);
- } else if (key_name == kEmailAttrName) {
- params.email = header_dictionary[kEmailAttrName];
- } else if (key_name == kIsSamlAttrName) {
- params.is_saml = header_dictionary[kIsSamlAttrName] == "true";
- } else if (key_name == kContinueUrlAttrName) {
- params.continue_url = header_dictionary[kContinueUrlAttrName];
- } else if (key_name == kIsSameTabAttrName) {
- params.is_same_tab = header_dictionary[kIsSameTabAttrName] == "true";
- } else {
- DLOG(WARNING) << "Unexpected GAIA header attribute '" << key_name << "'.";
+ ResponseHeaderDictionary dictionary;
+ for (const base::StringPiece& field :
+ base::SplitStringPiece(header_value, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ size_t delim = field.find_first_of('=');
+ if (delim == std::string::npos) {
+ DLOG(WARNING) << "Unexpected Gaia header field '" << field << "'.";
+ continue;
}
+ dictionary.insert(
+ {field.substr(0, delim).as_string(),
+ net::UnescapeURLComponent(
+ field.substr(delim + 1).as_string(),
+ net::UnescapeRule::PATH_SEPARATORS |
+ net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)});
}
- return params;
+ return dictionary;
}
-ManageAccountsParams BuildManageAccountsParamsIfExists(net::URLRequest* request,
- bool is_off_the_record) {
- ManageAccountsParams empty_params;
- empty_params.service_type = GAIA_SERVICE_TYPE_NONE;
- if (!gaia::IsGaiaSignonRealm(request->url().GetOrigin()))
- return empty_params;
+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;
- std::string header_value;
- net::HttpResponseHeaders* response_headers = request->response_headers();
- if (!response_headers ||
- !response_headers->GetNormalizedHeader(
- kChromeManageAccountsHeader, &header_value)) {
- return empty_params;
- }
+ // Check if url is eligible for the header.
+ if (!IsUrlEligibleForRequestHeader(url))
+ return false;
- DCHECK(switches::IsEnableAccountConsistency() && !is_off_the_record);
- return BuildManageAccountsParams(header_value);
+ return true;
}
-// Checks if the url has the required properties to have an
-// X-Chrome-Connected header.
-bool IsUrlEligibleForXChromeConnectedHeader(const GURL& url) {
- // Only set the header for Drive and Gaia always, and other Google properties
- // if account consistency is enabled.
- // Vasquette, which is integrated with most Google properties, needs the
- // header to redirect certain user actions to Chrome native UI. Drive and Gaia
- // need the header to tell if the current user is connected. The drive path is
- // a temporary workaround until the more generic chrome.principals API is
- // available.
+void AppendOrRemoveAccountConsistentyRequestHeader(
+ net::URLRequest* request,
+ const GURL& redirect_url,
+ const std::string& account_id,
+ bool sync_enabled,
+ const content_settings::CookieSettings* cookie_settings,
+ int profile_mode_mask) {
+ const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ DiceHeaderHelper dice_helper;
+ std::string dice_header_value;
+ if (dice_helper.ShouldBuildRequestHeader(url, cookie_settings)) {
+ dice_header_value =
+ dice_helper.BuildRequestHeader(account_id, sync_enabled);
+ }
+ dice_helper.AppendOrRemoveRequestHeader(
+ request, redirect_url, kDiceRequestHeader, dice_header_value);
+#endif
+
+ ChromeConnectedHeaderHelper chrome_connected_helper;
+ std::string chrome_connected_header_value;
+ if (chrome_connected_helper.ShouldBuildRequestHeader(url, cookie_settings)) {
+ chrome_connected_header_value = chrome_connected_helper.BuildRequestHeader(
+ true /* is_header_request */, url, account_id, profile_mode_mask);
+ }
+ chrome_connected_helper.AppendOrRemoveRequestHeader(
+ request, redirect_url, kChromeConnectedHeader,
+ chrome_connected_header_value);
+}
- // Consider the account id sensitive and limit it to secure domains.
- if (!url.SchemeIsCryptographic())
- return false;
+ManageAccountsParams BuildManageAccountsParams(
+ const std::string& header_value) {
+ return ChromeConnectedHeaderHelper::BuildManageAccountsParams(header_value);
+}
- GURL origin(url.GetOrigin());
- bool is_enable_account_consistency = switches::IsEnableAccountConsistency();
- bool is_google_url = is_enable_account_consistency &&
- (google_util::IsGoogleDomainUrl(
- url, google_util::ALLOW_SUBDOMAIN,
- google_util::DISALLOW_NON_STANDARD_PORTS) ||
- google_util::IsYoutubeDomainUrl(
- url, google_util::ALLOW_SUBDOMAIN,
- google_util::DISALLOW_NON_STANDARD_PORTS));
- return is_google_url || IsDriveOrigin(origin) ||
- gaia::IsGaiaSignonRealm(origin);
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+DiceResponseParams BuildDiceSigninResponseParams(
+ const std::string& header_value) {
+ return DiceHeaderHelper::BuildDiceSigninResponseParams(header_value);
+}
+
+DiceResponseParams BuildDiceSignoutResponseParams(
+ const std::string& header_value) {
+ return DiceHeaderHelper::BuildDiceSignoutResponseParams(header_value);
}
+#endif
} // namespace signin
diff --git a/chromium/components/signin/core/browser/signin_header_helper.h b/chromium/components/signin/core/browser/signin_header_helper.h
index 9f000bc6411..06b72cb3ad6 100644
--- a/chromium/components/signin/core/browser/signin_header_helper.h
+++ b/chromium/components/signin/core/browser/signin_header_helper.h
@@ -5,9 +5,12 @@
#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_HEADER_HELPER_H_
#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_HEADER_HELPER_H_
+#include <map>
#include <string>
+#include <vector>
-#include "build/build_config.h" // For OS_IOS
+#include "components/signin/core/common/signin_features.h"
+#include "url/gurl.h"
namespace content_settings {
class CookieSettings;
@@ -17,8 +20,6 @@ namespace net {
class URLRequest;
}
-class GURL;
-
namespace signin {
// Profile mode flags.
@@ -31,12 +32,13 @@ enum ProfileMode {
};
extern const char kChromeConnectedHeader[];
+extern const char kDiceRequestHeader[];
-// The ServiceType specified by GAIA in the response header accompanying the 204
+// The ServiceType specified by Gaia in the response header accompanying the 204
// response. This indicates the action Chrome is supposed to lead the user to
// perform.
enum GAIAServiceType {
- GAIA_SERVICE_TYPE_NONE = 0, // No GAIA response header.
+ GAIA_SERVICE_TYPE_NONE = 0, // No Gaia response header.
GAIA_SERVICE_TYPE_SIGNOUT, // Logout all existing sessions.
GAIA_SERVICE_TYPE_INCOGNITO, // Open an incognito tab.
GAIA_SERVICE_TYPE_ADDSESSION, // Add a secondary account.
@@ -45,7 +47,13 @@ enum GAIAServiceType {
GAIA_SERVICE_TYPE_DEFAULT, // All other cases.
};
-// Struct describing the paramters received in the manage account header.
+enum class DiceAction {
+ NONE,
+ SIGNIN, // Sign in an account.
+ SIGNOUT, // Sign out of all sessions.
+};
+
+// Struct describing the parameters received in the manage account header.
struct ManageAccountsParams {
// The requested service type such as "ADDSESSION".
GAIAServiceType service_type;
@@ -59,26 +67,87 @@ struct ManageAccountsParams {
// Whether the continue URL should be loaded in the same tab.
bool is_same_tab;
-// iOS has no notion of route and child IDs.
-#if !defined(OS_IOS)
- // The child id associated with the web content of the request.
- int child_id;
- // The route id associated with the web content of the request.
- int route_id;
-#endif // !defined(OS_IOS)
-
ManageAccountsParams();
ManageAccountsParams(const ManageAccountsParams& other);
};
+// Struct describing the parameters received in the Dice response header.
+struct DiceResponseParams {
+ struct SigninInfo {
+ SigninInfo();
+ SigninInfo(const SigninInfo&);
+ ~SigninInfo();
+ // Gaia ID of the account signed in.
+ std::string gaia_id;
+ // Email of the account signed in.
+ std::string email;
+ // Session index for the account signed in.
+ int session_index;
+ // Authorization code to fetch a refresh token.
+ std::string authorization_code;
+ };
+
+ struct SignoutInfo {
+ SignoutInfo();
+ SignoutInfo(const SignoutInfo&);
+ ~SignoutInfo();
+ // Gaia IDs of the accounts signed out.
+ std::vector<std::string> gaia_id;
+ // Emails of the accounts signed out.
+ std::vector<std::string> email;
+ // Session indices for the accounts signed out.
+ std::vector<int> session_index;
+ };
+
+ DiceResponseParams();
+ ~DiceResponseParams();
+ DiceResponseParams(const DiceResponseParams& other);
+
+ DiceAction user_intention;
+
+ // Populated when |user_intention| is SIGNIN.
+ SigninInfo signin_info;
+
+ // Populated when |user_intention| is SIGNOUT.
+ SignoutInfo signout_info;
+};
+
+// 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.
+ bool AppendOrRemoveRequestHeader(net::URLRequest* 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(
+ const GURL& url,
+ const content_settings::CookieSettings* cookie_settings);
+
+ protected:
+ SigninHeaderHelper() {}
+ virtual ~SigninHeaderHelper() {}
+
+ // Dictionary of fields in a account consistency response header.
+ using ResponseHeaderDictionary = std::multimap<std::string, std::string>;
+
+ // Parses the account consistency response header. Its expected format is
+ // "key1=value1,key2=value2,...".
+ static ResponseHeaderDictionary ParseAccountConsistencyResponseHeader(
+ const std::string& header_value);
+
+ private:
+ // Returns whether the url is eligible for the request header.
+ virtual bool IsUrlEligibleForRequestHeader(const GURL& url) = 0;
+};
+
// Returns true if signin cookies are allowed.
bool SettingsAllowSigninCookies(
const content_settings::CookieSettings* cookie_settings);
-// Checks if the url has the required properties to have an
-// X-Chrome-Connected header.
-bool IsUrlEligibleForXChromeConnectedHeader(const GURL& url);
-
// Returns the CHROME_CONNECTED cookie, or an empty string if it should not be
// added to the request to |url|.
std::string BuildMirrorRequestCookieIfPossible(
@@ -87,13 +156,14 @@ std::string BuildMirrorRequestCookieIfPossible(
const content_settings::CookieSettings* cookie_settings,
int profile_mode_mask);
-// Adds X-Chrome-Connected header to all Gaia requests from a connected profile,
-// with the exception of requests from gaia webview.
+// Adds account consistency header to all Gaia requests from a connected
+// profile, with the exception of requests from gaia webview.
// Removes the header in case it should not be transfered to a redirected url.
-bool AppendOrRemoveMirrorRequestHeaderIfPossible(
+void AppendOrRemoveAccountConsistentyRequestHeader(
net::URLRequest* request,
const GURL& redirect_url,
const std::string& account_id,
+ bool sync_enabled,
const content_settings::CookieSettings* cookie_settings,
int profile_mode_mask);
@@ -101,12 +171,21 @@ bool AppendOrRemoveMirrorRequestHeaderIfPossible(
// header.
ManageAccountsParams BuildManageAccountsParams(const std::string& header_value);
-// Returns the parameters contained in the X-Chrome-Manage-Accounts response
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+// Returns the parameters contained in the X-Chrome-ID-Consistency-Response
+// response header.
+// Returns DiceAction::NONE in case of error (such as missing or malformed
+// parameters).
+DiceResponseParams BuildDiceSigninResponseParams(
+ const std::string& header_value);
+
+// Returns the parameters contained in the Google-Accounts-SignOut response
// header.
-// If the request does not have a response header or if the header contains
-// garbage, then |service_type| is set to |GAIA_SERVICE_TYPE_NONE|.
-ManageAccountsParams BuildManageAccountsParamsIfExists(net::URLRequest* request,
- bool is_off_the_record);
+// Returns DiceAction::NONE in case of error (such as missing or malformed
+// parameters).
+DiceResponseParams BuildDiceSignoutResponseParams(
+ const std::string& header_value);
+#endif
} // namespace signin
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 5141e3d3f0b..bade766e6fb 100644
--- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -2,19 +2,31 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/signin/core/browser/signin_header_helper.h"
+
#include <memory>
+#include <string>
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
#include "components/content_settings/core/browser/cookie_settings.h"
-#include "components/signin/core/browser/signin_header_helper.h"
+#include "components/signin/core/browser/chrome_connected_header_helper.h"
#include "components/signin/core/common/profile_management_switches.h"
+#include "components/signin/core/common/signin_features.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "google_apis/gaia/gaia_urls.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#include "components/signin/core/browser/dice_header_helper.h"
+#endif
+
+namespace signin {
+
class SigninHeaderHelperTest : public testing::Test {
protected:
void SetUp() override {
@@ -33,32 +45,62 @@ class SigninHeaderHelperTest : public testing::Test {
void CheckMirrorCookieRequest(const GURL& url,
const std::string& account_id,
const std::string& expected_request) {
- EXPECT_EQ(signin::BuildMirrorRequestCookieIfPossible(
- url, account_id, cookie_settings_.get(),
- signin::PROFILE_MODE_DEFAULT),
- expected_request);
+ EXPECT_EQ(
+ BuildMirrorRequestCookieIfPossible(
+ url, account_id, cookie_settings_.get(), PROFILE_MODE_DEFAULT),
+ expected_request);
}
- void CheckMirrorHeaderRequest(const GURL& url,
- const std::string& account_id,
- const std::string& expected_request) {
- bool expected_result = !expected_request.empty();
+ std::unique_ptr<net::URLRequest> CreateRequest(const GURL& url,
+ const std::string& account_id,
+ bool sync_enabled) {
std::unique_ptr<net::URLRequest> url_request =
url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_EQ(signin::AppendOrRemoveMirrorRequestHeaderIfPossible(
- url_request.get(), GURL(), account_id, cookie_settings_.get(),
- signin::PROFILE_MODE_DEFAULT),
- expected_result);
+ AppendOrRemoveAccountConsistentyRequestHeader(
+ url_request.get(), GURL(), account_id, sync_enabled,
+ cookie_settings_.get(), PROFILE_MODE_DEFAULT);
+ return url_request;
+ }
+
+ void CheckAccountConsistencyHeaderRequest(
+ net::URLRequest* url_request,
+ const char* header_name,
+ const std::string& expected_request) {
+ bool expected_result = !expected_request.empty();
std::string request;
- EXPECT_EQ(url_request->extra_request_headers().GetHeader(
- signin::kChromeConnectedHeader, &request),
- expected_result);
+ EXPECT_EQ(
+ url_request->extra_request_headers().GetHeader(header_name, &request),
+ expected_result);
if (expected_result) {
EXPECT_EQ(expected_request, request);
}
}
+ void CheckMirrorHeaderRequest(const GURL& url,
+ const std::string& account_id,
+ const std::string& expected_request) {
+ std::unique_ptr<net::URLRequest> url_request =
+ CreateRequest(url, account_id, false /* sync_enabled */);
+ CheckAccountConsistencyHeaderRequest(
+ url_request.get(), kChromeConnectedHeader, expected_request);
+ }
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ void CheckDiceHeaderRequest(const GURL& url,
+ const std::string& account_id,
+ bool sync_enabled,
+ const std::string& expected_mirror_request,
+ const std::string& expected_dice_request) {
+ std::unique_ptr<net::URLRequest> url_request =
+ CreateRequest(url, account_id, sync_enabled);
+ CheckAccountConsistencyHeaderRequest(
+ url_request.get(), kChromeConnectedHeader, expected_mirror_request);
+ CheckAccountConsistencyHeaderRequest(url_request.get(), kDiceRequestHeader,
+ expected_dice_request);
+ }
+#endif
+
base::MessageLoop loop_;
sync_preferences::TestingPrefServiceSyncable prefs_;
@@ -71,7 +113,7 @@ class SigninHeaderHelperTest : public testing::Test {
// Tests that no Mirror request is returned when the user is not signed in (no
// account id).
TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestNoAccountId) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
CheckMirrorHeaderRequest(GURL("https://docs.google.com"), "", "");
CheckMirrorCookieRequest(GURL("https://docs.google.com"), "", "");
@@ -80,7 +122,7 @@ TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestNoAccountId) {
// Tests that no Mirror request is returned when the cookies aren't allowed to
// be set.
TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestCookieSettingBlocked) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
cookie_settings_->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
CheckMirrorHeaderRequest(GURL("https://docs.google.com"), "0123456789", "");
@@ -89,7 +131,7 @@ TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestCookieSettingBlocked) {
// Tests that no Mirror request is returned when the target is a non-Google URL.
TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestExternalURL) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
CheckMirrorHeaderRequest(GURL("https://foo.com"), "0123456789", "");
CheckMirrorCookieRequest(GURL("https://foo.com"), "0123456789", "");
@@ -98,7 +140,7 @@ TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestExternalURL) {
// Tests that the Mirror request is returned without the GAIA Id when the target
// is a google TLD domain.
TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleTLD) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
CheckMirrorHeaderRequest(GURL("https://google.fr"), "0123456789",
"mode=0,enable_account_consistency=true");
@@ -109,7 +151,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleTLD) {
// Tests that the Mirror request is returned when the target is the domain
// google.com, and that the GAIA Id is only attached for the cookie.
TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleCom) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
CheckMirrorHeaderRequest(GURL("https://www.google.com"), "0123456789",
"mode=0,enable_account_consistency=true");
@@ -118,14 +160,63 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleCom) {
"id=0123456789:mode=0:enable_account_consistency=true");
}
+// Mirror is always enabled on Android and iOS, so these tests are only relevant
+// on Desktop.
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+
+// Tests that the Mirror request is returned when the target is a Gaia URL, even
+// if account consistency is disabled.
+TEST_F(SigninHeaderHelperTest, TestMirrorRequestGaiaURL) {
+ ASSERT_FALSE(switches::IsAccountConsistencyMirrorEnabled());
+ CheckMirrorHeaderRequest(GURL("https://accounts.google.com"), "0123456789",
+ "mode=0,enable_account_consistency=false");
+ CheckMirrorCookieRequest(
+ GURL("https://accounts.google.com"), "0123456789",
+ "id=0123456789:mode=0:enable_account_consistency=false");
+}
+
+// Tests Dice requests.
+TEST_F(SigninHeaderHelperTest, TestDiceRequest) {
+ switches::EnableAccountConsistencyDiceForTesting(
+ base::CommandLine::ForCurrentProcess());
+ // ChromeConnected but no Dice for Docs URLs.
+ CheckDiceHeaderRequest(
+ GURL("https://docs.google.com"), "0123456789", false /* sync_enabled */,
+ "id=0123456789,mode=0,enable_account_consistency=false", "");
+
+ // ChromeConnected and Dice for Gaia URLs.
+ // Sync disabled.
+ std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
+ ASSERT_FALSE(client_id.empty());
+ CheckDiceHeaderRequest(GURL("https://accounts.google.com"), "0123456789",
+ false /* sync_enabled */,
+ "mode=0,enable_account_consistency=false",
+ "client_id=" + client_id);
+ // Sync enabled: check that the Dice header has the Sync account ID and that
+ // the mirror header is not modified.
+ CheckDiceHeaderRequest(
+ GURL("https://accounts.google.com"), "0123456789",
+ true /* sync_enabled */, "mode=0,enable_account_consistency=false",
+ "client_id=" + client_id + ",sync_account_id=0123456789");
+
+ // No ChromeConnected and no Dice for other URLs.
+ CheckDiceHeaderRequest(GURL("https://www.google.com"), "0123456789",
+ false /* sync_enabled */, "", "");
+}
+
+// Tests that no Dice request is returned when Dice is not enabled.
+TEST_F(SigninHeaderHelperTest, TestNoDiceRequestWhenDisabled) {
+ switches::EnableAccountConsistencyMirrorForTesting(
+ base::CommandLine::ForCurrentProcess());
+ CheckDiceHeaderRequest(GURL("https://accounts.google.com"), "0123456789",
+ false /* sync_enabled */,
+ "mode=0,enable_account_consistency=true", "");
+}
+
// Tests that the Mirror request is returned with the GAIA Id on Drive origin,
// even if account consistency is disabled.
-//
-// Account consistency if always enabled on Android and iOS, so this test is
-// only relevant on Desktop.
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
TEST_F(SigninHeaderHelperTest, TestMirrorRequestDrive) {
- DCHECK(!switches::IsEnableAccountConsistency());
+ ASSERT_FALSE(switches::IsAccountConsistencyMirrorEnabled());
CheckMirrorHeaderRequest(
GURL("https://docs.google.com/document"), "0123456789",
"id=0123456789,mode=0,enable_account_consistency=false");
@@ -134,7 +225,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestDrive) {
"id=0123456789:mode=0:enable_account_consistency=false");
// Enable Account Consistency will override the disable.
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
CheckMirrorHeaderRequest(
GURL("https://docs.google.com/document"), "0123456789",
@@ -143,12 +234,102 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestDrive) {
GURL("https://drive.google.com/drive"), "0123456789",
"id=0123456789:mode=0:enable_account_consistency=true");
}
-#endif
+
+TEST_F(SigninHeaderHelperTest, TestDiceInvalidResponseParams) {
+ DiceResponseParams params = BuildDiceSigninResponseParams("blah");
+ EXPECT_EQ(DiceAction::NONE, params.user_intention);
+}
+
+TEST_F(SigninHeaderHelperTest, TestBuildDiceResponseParams) {
+ const char kAuthorizationCode[] = "authorization_code";
+ const char kEmail[] = "foo@example.com";
+ const char kGaiaID[] = "gaia_id";
+ const int kSessionIndex = 42;
+
+ {
+ // Signin response.
+ DiceResponseParams params =
+ BuildDiceSigninResponseParams(base::StringPrintf(
+ "action=SIGNIN,id=%s,email=%s,authuser=%i,authorization_code=%s",
+ kGaiaID, kEmail, kSessionIndex, kAuthorizationCode));
+ EXPECT_EQ(DiceAction::SIGNIN, params.user_intention);
+ EXPECT_EQ(kGaiaID, params.signin_info.gaia_id);
+ EXPECT_EQ(kEmail, params.signin_info.email);
+ EXPECT_EQ(kSessionIndex, params.signin_info.session_index);
+ EXPECT_EQ(kAuthorizationCode, params.signin_info.authorization_code);
+ }
+
+ {
+ // Signout response.
+ // Note: Gaia responses typically have a whitespace after the commas, and
+ // some fields are wrapped in quotes.
+ DiceResponseParams params = BuildDiceSignoutResponseParams(
+ base::StringPrintf("email=\"%s\", sessionindex=%i, obfuscatedid=\"%s\"",
+ kEmail, kSessionIndex, kGaiaID));
+ ASSERT_EQ(DiceAction::SIGNOUT, params.user_intention);
+ EXPECT_EQ(1u, params.signout_info.gaia_id.size());
+ EXPECT_EQ(1u, params.signout_info.email.size());
+ EXPECT_EQ(1u, params.signout_info.session_index.size());
+ EXPECT_EQ(kGaiaID, params.signout_info.gaia_id[0]);
+ EXPECT_EQ(kEmail, params.signout_info.email[0]);
+ EXPECT_EQ(kSessionIndex, params.signout_info.session_index[0]);
+ }
+
+ {
+ // Multi-Signout response.
+ const char kEmail2[] = "bar@example.com";
+ const char kGaiaID2[] = "gaia_id_2";
+ const int kSessionIndex2 = 2;
+ DiceResponseParams params =
+ BuildDiceSignoutResponseParams(base::StringPrintf(
+ "email=\"%s\", sessionindex=%i, obfuscatedid=\"%s\", "
+ "email=\"%s\", sessionindex=%i, obfuscatedid=\"%s\"",
+ kEmail, kSessionIndex, kGaiaID, kEmail2, kSessionIndex2, kGaiaID2));
+ ASSERT_EQ(DiceAction::SIGNOUT, params.user_intention);
+ EXPECT_EQ(2u, params.signout_info.gaia_id.size());
+ EXPECT_EQ(2u, params.signout_info.email.size());
+ EXPECT_EQ(2u, params.signout_info.session_index.size());
+ EXPECT_EQ(kGaiaID, params.signout_info.gaia_id[0]);
+ EXPECT_EQ(kEmail, params.signout_info.email[0]);
+ EXPECT_EQ(kSessionIndex, params.signout_info.session_index[0]);
+ EXPECT_EQ(kGaiaID2, params.signout_info.gaia_id[1]);
+ EXPECT_EQ(kEmail2, params.signout_info.email[1]);
+ EXPECT_EQ(kSessionIndex2, params.signout_info.session_index[1]);
+ }
+
+ {
+ // Missing authorization code.
+ DiceResponseParams params = BuildDiceSigninResponseParams(
+ base::StringPrintf("action=SIGNIN,id=%s,email=%s,authuser=%i", kGaiaID,
+ kEmail, kSessionIndex));
+ EXPECT_EQ(DiceAction::NONE, params.user_intention);
+ }
+
+ {
+ // Missing email in SIGNIN.
+ DiceResponseParams params =
+ BuildDiceSigninResponseParams(base::StringPrintf(
+ "action=SIGNIN,id=%s,authuser=%i,authorization_code=%s", kGaiaID,
+ kSessionIndex, kAuthorizationCode));
+ EXPECT_EQ(DiceAction::NONE, params.user_intention);
+ }
+
+ {
+ // Missing email in signout.
+ DiceResponseParams params = BuildDiceSignoutResponseParams(
+ base::StringPrintf("email=%s, sessionindex=%i, obfuscatedid=%s, "
+ "sessionindex=2, obfuscatedid=bar",
+ kEmail, kSessionIndex, kGaiaID));
+ EXPECT_EQ(DiceAction::NONE, params.user_intention);
+ }
+}
+
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
// Tests that the Mirror header request is returned normally when the redirect
// URL is eligible.
TEST_F(SigninHeaderHelperTest, TestMirrorHeaderEligibleRedirectURL) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
const GURL url("https://docs.google.com/document");
const GURL redirect_url("https://www.google.com");
@@ -156,17 +337,17 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderEligibleRedirectURL) {
std::unique_ptr<net::URLRequest> url_request =
url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_TRUE(signin::AppendOrRemoveMirrorRequestHeaderIfPossible(
- url_request.get(), redirect_url, account_id, cookie_settings_.get(),
- signin::PROFILE_MODE_DEFAULT));
- EXPECT_TRUE(url_request->extra_request_headers().HasHeader(
- signin::kChromeConnectedHeader));
+ AppendOrRemoveAccountConsistentyRequestHeader(
+ url_request.get(), redirect_url, account_id, false /* sync_enabled */,
+ cookie_settings_.get(), PROFILE_MODE_DEFAULT);
+ EXPECT_TRUE(
+ url_request->extra_request_headers().HasHeader(kChromeConnectedHeader));
}
// Tests that the Mirror header request is stripped when the redirect URL is not
// eligible.
TEST_F(SigninHeaderHelperTest, TestMirrorHeaderNonEligibleRedirectURL) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
const GURL url("https://docs.google.com/document");
const GURL redirect_url("http://www.foo.com");
@@ -174,17 +355,17 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderNonEligibleRedirectURL) {
std::unique_ptr<net::URLRequest> url_request =
url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_FALSE(signin::AppendOrRemoveMirrorRequestHeaderIfPossible(
- url_request.get(), redirect_url, account_id, cookie_settings_.get(),
- signin::PROFILE_MODE_DEFAULT));
- EXPECT_FALSE(url_request->extra_request_headers().HasHeader(
- signin::kChromeConnectedHeader));
+ AppendOrRemoveAccountConsistentyRequestHeader(
+ url_request.get(), redirect_url, account_id, false /* sync_enabled */,
+ cookie_settings_.get(), PROFILE_MODE_DEFAULT);
+ EXPECT_FALSE(
+ url_request->extra_request_headers().HasHeader(kChromeConnectedHeader));
}
// Tests that the Mirror header, whatever its value is, is untouched when both
// the current and the redirect URL are non-eligible.
TEST_F(SigninHeaderHelperTest, TestIgnoreMirrorHeaderNonEligibleURLs) {
- switches::EnableAccountConsistencyForTesting(
+ switches::EnableAccountConsistencyMirrorForTesting(
base::CommandLine::ForCurrentProcess());
const GURL url("https://www.bar.com");
const GURL redirect_url("http://www.foo.com");
@@ -193,13 +374,34 @@ TEST_F(SigninHeaderHelperTest, TestIgnoreMirrorHeaderNonEligibleURLs) {
std::unique_ptr<net::URLRequest> url_request =
url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
- url_request->SetExtraRequestHeaderByName(signin::kChromeConnectedHeader,
- fake_header, false);
- EXPECT_FALSE(signin::AppendOrRemoveMirrorRequestHeaderIfPossible(
- url_request.get(), redirect_url, account_id, cookie_settings_.get(),
- signin::PROFILE_MODE_DEFAULT));
+ url_request->SetExtraRequestHeaderByName(kChromeConnectedHeader, fake_header,
+ false);
+ AppendOrRemoveAccountConsistentyRequestHeader(
+ url_request.get(), redirect_url, account_id, false /* sync_enabled */,
+ cookie_settings_.get(), PROFILE_MODE_DEFAULT);
std::string header;
EXPECT_TRUE(url_request->extra_request_headers().GetHeader(
- signin::kChromeConnectedHeader, &header));
+ kChromeConnectedHeader, &header));
EXPECT_EQ(fake_header, header);
}
+
+TEST_F(SigninHeaderHelperTest, TestInvalidManageAccountsParams) {
+ ManageAccountsParams params = BuildManageAccountsParams("blah");
+ EXPECT_EQ(GAIA_SERVICE_TYPE_NONE, params.service_type);
+}
+
+TEST_F(SigninHeaderHelperTest, TestBuildManageAccountsParams) {
+ const char kContinueURL[] = "https://www.example.com/continue";
+ const char kEmail[] = "foo@example.com";
+
+ ManageAccountsParams params = BuildManageAccountsParams(base::StringPrintf(
+ "action=REAUTH,email=%s,is_saml=true,is_same_tab=true,continue_url=%s",
+ kEmail, kContinueURL));
+ EXPECT_EQ(GAIA_SERVICE_TYPE_REAUTH, params.service_type);
+ EXPECT_EQ(kEmail, params.email);
+ EXPECT_EQ(true, params.is_saml);
+ EXPECT_EQ(true, params.is_same_tab);
+ EXPECT_EQ(GURL(kContinueURL), params.continue_url);
+}
+
+} // namespace signin
diff --git a/chromium/components/signin/core/browser/signin_manager.cc b/chromium/components/signin/core/browser/signin_manager.cc
index 9e588dc9879..5ced3207823 100644
--- a/chromium/components/signin/core/browser/signin_manager.cc
+++ b/chromium/components/signin/core/browser/signin_manager.cc
@@ -54,6 +54,8 @@ std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
return "No Signin";
case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
return "With refresh token";
+ case SIGNIN_TYPE_WITHOUT_REFRESH_TOKEN:
+ return "Without refresh token";
}
NOTREACHED();
@@ -66,9 +68,9 @@ bool SigninManager::PrepareForSignin(SigninType type,
const std::string& password) {
std::string account_id =
account_tracker_service()->PickAccountIdForAccount(gaia_id, username);
+ DCHECK(!account_id.empty());
DCHECK(possibly_invalid_account_id_.empty() ||
possibly_invalid_account_id_ == account_id);
- DCHECK(!account_id.empty());
if (!IsAllowedUsername(username)) {
// Account is not allowed by admin policy.
@@ -101,19 +103,21 @@ void SigninManager::StartSignInWithRefreshToken(
const std::string& password,
const OAuthTokenFetchedCallback& callback) {
DCHECK(!IsAuthenticated());
-
- if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, gaia_id, username,
- password)) {
+ SigninType signin_type = refresh_token.empty()
+ ? SIGNIN_TYPE_WITHOUT_REFRESH_TOKEN
+ : SIGNIN_TYPE_WITH_REFRESH_TOKEN;
+ if (!PrepareForSignin(signin_type, gaia_id, username, password)) {
return;
}
- // Store our token.
+ // Store the refresh token.
temp_refresh_token_ = refresh_token;
- if (!callback.is_null() && !temp_refresh_token_.empty()) {
+ if (!callback.is_null()) {
+ // Callback present, let the caller complete the pending sign-in.
callback.Run(temp_refresh_token_);
} else {
- // No oauth token or callback, so just complete our pending signin.
+ // No callback, so just complete the pending signin.
CompletePendingSignin();
}
}
@@ -350,13 +354,13 @@ void SigninManager::CompletePendingSignin() {
DCHECK(!possibly_invalid_account_id_.empty());
OnSignedIn();
- DCHECK(!temp_refresh_token_.empty());
DCHECK(IsAuthenticated());
- std::string account_id = GetAuthenticatedAccountId();
- token_service_->UpdateCredentials(account_id, temp_refresh_token_);
- temp_refresh_token_.clear();
-
+ if (!temp_refresh_token_.empty()) {
+ std::string account_id = GetAuthenticatedAccountId();
+ token_service_->UpdateCredentials(account_id, temp_refresh_token_);
+ temp_refresh_token_.clear();
+ }
MergeSigninCredentialIntoCookieJar();
}
@@ -385,8 +389,11 @@ void SigninManager::OnSignedIn() {
for (auto& observer : observer_list_) {
observer.GoogleSigninSucceeded(GetAuthenticatedAccountId(),
- GetAuthenticatedAccountInfo().email,
- password_);
+ GetAuthenticatedAccountInfo().email);
+
+ observer.GoogleSigninSucceededWithPassword(
+ GetAuthenticatedAccountId(), GetAuthenticatedAccountInfo().email,
+ password_);
}
client_->OnSignedIn(GetAuthenticatedAccountId(), gaia_id,
diff --git a/chromium/components/signin/core/browser/signin_manager.h b/chromium/components/signin/core/browser/signin_manager.h
index 92e9ff587ec..bae32cf78e4 100644
--- a/chromium/components/signin/core/browser/signin_manager.h
+++ b/chromium/components/signin/core/browser/signin_manager.h
@@ -74,8 +74,10 @@ class SigninManager : public SigninManagerBase,
const std::string& policy);
// Attempt to sign in this user with a refresh token.
+ // If |refresh_token| is not empty, then SigninManager will add it to the
+ // |token_service_| when the sign-in flow is completed.
// If non-null, the passed |oauth_fetched_callback| callback is invoked once
- // signin has been completed.
+ // sign-in has been completed.
// The callback should invoke SignOut() or CompletePendingSignin() to either
// continue or cancel the in-process signin.
virtual void StartSignInWithRefreshToken(
@@ -152,7 +154,11 @@ class SigninManager : public SigninManagerBase,
signin_metrics::SignoutDelete signout_delete_metric);
private:
- enum SigninType { SIGNIN_TYPE_NONE, SIGNIN_TYPE_WITH_REFRESH_TOKEN };
+ enum SigninType {
+ SIGNIN_TYPE_NONE,
+ SIGNIN_TYPE_WITH_REFRESH_TOKEN,
+ SIGNIN_TYPE_WITHOUT_REFRESH_TOKEN
+ };
std::string SigninTypeToString(SigninType type);
friend class FakeSigninManager;
diff --git a/chromium/components/signin/core/browser/signin_manager_base.cc b/chromium/components/signin/core/browser/signin_manager_base.cc
index df125732094..14ad0380e7a 100644
--- a/chromium/components/signin/core/browser/signin_manager_base.cc
+++ b/chromium/components/signin/core/browser/signin_manager_base.cc
@@ -232,7 +232,9 @@ bool SigninManagerBase::AuthInProgress() const {
return false;
}
-void SigninManagerBase::Shutdown() {}
+void SigninManagerBase::Shutdown() {
+ on_shutdown_callback_list_.Notify();
+}
void SigninManagerBase::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
diff --git a/chromium/components/signin/core/browser/signin_manager_base.h b/chromium/components/signin/core/browser/signin_manager_base.h
index ddf20d85567..a7c08678e66 100644
--- a/chromium/components/signin/core/browser/signin_manager_base.h
+++ b/chromium/components/signin/core/browser/signin_manager_base.h
@@ -26,6 +26,7 @@
#include <memory>
#include <string>
+#include "base/callback_list.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"
@@ -42,6 +43,10 @@ class PrefRegistrySimple;
class PrefService;
class SigninClient;
+namespace password_manager {
+class PasswordStoreSigninNotifierImpl;
+}
+
namespace user_prefs {
class PrefRegistrySyncable;
}
@@ -55,8 +60,7 @@ class SigninManagerBase : public KeyedService {
// Called when a user signs into Google services such as sync.
virtual void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) {}
+ const std::string& username) {}
// Called when the currently signed-in user for a user has been signed out.
virtual void GoogleSignedOut(const std::string& account_id,
@@ -64,6 +68,30 @@ class SigninManagerBase : public KeyedService {
protected:
virtual ~Observer() {}
+
+ private:
+ // Observers that can observer the password of the Google account after a
+ // successful sign-in.
+ friend class PasswordStoreSigninNotifierImpl;
+
+ // SigninManagers that fire |GoogleSigninSucceededWithPassword|
+ // notifications.
+ friend class SigninManager;
+ friend class FakeSigninManager;
+
+ // Called when a user signs into Google services such as sync. Also passes
+ // the password of the Google account that was used to sign in.
+ //
+ // Observers should override |GoogleSigninSucceeded| if they are not
+ // interested in the password thas was used during the sign-in.
+ //
+ // Note: The password is always empty on mobile as the user signs in to
+ // Chrome with accounts that were added to the device, so Chrome does not
+ // have access to the password.
+ virtual void GoogleSigninSucceededWithPassword(
+ const std::string& account_id,
+ const std::string& username,
+ const std::string& password) {}
};
SigninManagerBase(SigninClient* client,
@@ -132,6 +160,15 @@ class SigninManagerBase : public KeyedService {
// Gives access to the SigninClient instance associated with this instance.
SigninClient* signin_client() const { return client_; }
+ // Adds a callback that will be called when this instance is shut down.Not
+ // intended for general usage, but rather for usage only by the Identity
+ // Service implementation during the time period of conversion of Chrome to
+ // use the Identity Service.
+ std::unique_ptr<base::CallbackList<void()>::Subscription>
+ RegisterOnShutdownCallback(const base::Closure& cb) {
+ return on_shutdown_callback_list_.Add(cb);
+ }
+
protected:
AccountTrackerService* account_tracker_service() const {
return account_tracker_service_;
@@ -169,6 +206,9 @@ class SigninManagerBase : public KeyedService {
base::ObserverList<signin_internals_util::SigninDiagnosticsObserver, true>
signin_diagnostics_observers_;
+ // The list of callbacks notified on shutdown.
+ base::CallbackList<void()> on_shutdown_callback_list_;
+
base::WeakPtrFactory<SigninManagerBase> weak_pointer_factory_;
DISALLOW_COPY_AND_ASSIGN(SigninManagerBase);
diff --git a/chromium/components/signin/core/browser/signin_metrics.h b/chromium/components/signin/core/browser/signin_metrics.h
index 177ed1fae62..cc6709e2958 100644
--- a/chromium/components/signin/core/browser/signin_metrics.h
+++ b/chromium/components/signin/core/browser/signin_metrics.h
@@ -143,6 +143,7 @@ enum class AccessPoint : int {
ACCESS_POINT_NTP_CONTENT_SUGGESTIONS,
ACCESS_POINT_RESIGNIN_INFOBAR,
ACCESS_POINT_TAB_SWITCHER,
+ ACCESS_POINT_FORCE_SIGNIN_WARNING,
ACCESS_POINT_MAX, // This must be last.
};
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider.cc
index 699e61d6190..8ead61c4a97 100644
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider.cc
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider.cc
@@ -77,8 +77,7 @@ void SigninStatusMetricsProvider::OnSigninManagerShutdown(
void SigninStatusMetricsProvider::GoogleSigninSucceeded(
const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
SigninStatus recorded_signin_status = signin_status();
if (recorded_signin_status == ALL_PROFILES_NOT_SIGNED_IN) {
UpdateSigninStatus(MIXED_SIGNIN_STATUS);
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider.h b/chromium/components/signin/core/browser/signin_status_metrics_provider.h
index 9d6e663d126..ee6f0adf6ba 100644
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider.h
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider.h
@@ -70,8 +70,7 @@ class SigninStatusMetricsProvider : public SigninStatusMetricsProviderBase,
// SigninManagerBase::Observer:
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSignedOut(const std::string& account_id,
const std::string& username) override;
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
index 13f54ab77e4..ed1a87129a9 100644
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
@@ -27,15 +27,13 @@ TEST(SigninStatusMetricsProviderTest, GoogleSigninSucceeded) {
// Initial status is all signed out and then one of the profiles is signed in.
metrics_provider.UpdateInitialSigninStatus(2, 0);
- metrics_provider.GoogleSigninSucceeded(std::string(), std::string(),
- std::string());
+ metrics_provider.GoogleSigninSucceeded(std::string(), std::string());
EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
metrics_provider.GetSigninStatusForTesting());
// Initial status is mixed and then one of the profiles is signed in.
metrics_provider.UpdateInitialSigninStatus(2, 1);
- metrics_provider.GoogleSigninSucceeded(std::string(), std::string(),
- std::string());
+ metrics_provider.GoogleSigninSucceeded(std::string(), std::string());
EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
metrics_provider.GetSigninStatusForTesting());
}
diff --git a/chromium/components/signin/core/browser/signin_tracker.cc b/chromium/components/signin/core/browser/signin_tracker.cc
index b13365dcab3..24e23983344 100644
--- a/chromium/components/signin/core/browser/signin_tracker.cc
+++ b/chromium/components/signin/core/browser/signin_tracker.cc
@@ -32,6 +32,12 @@ void SigninTracker::Initialize() {
cookie_manager_service_->AddObserver(this);
}
+void SigninTracker::GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username) {
+ if (token_service_->RefreshTokenIsAvailable(account_id))
+ observer_->SigninSuccess();
+}
+
void SigninTracker::GoogleSigninFailed(const GoogleServiceAuthError& error) {
observer_->SigninFailed(error);
}
diff --git a/chromium/components/signin/core/browser/signin_tracker.h b/chromium/components/signin/core/browser/signin_tracker.h
index cabbedf02ca..e433df58722 100644
--- a/chromium/components/signin/core/browser/signin_tracker.h
+++ b/chromium/components/signin/core/browser/signin_tracker.h
@@ -80,6 +80,8 @@ class SigninTracker : public SigninManagerBase::Observer,
~SigninTracker() override;
// SigninManagerBase::Observer implementation.
+ void GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username) override;
void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
// OAuth2TokenService::Observer implementation.
diff --git a/chromium/components/signin/core/common/BUILD.gn b/chromium/components/signin/core/common/BUILD.gn
index 670bde9c185..c1a5c09b5e9 100644
--- a/chromium/components/signin/core/common/BUILD.gn
+++ b/chromium/components/signin/core/common/BUILD.gn
@@ -2,6 +2,17 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/buildflag_header.gni")
+import("//components/signin/features.gni")
+
+buildflag_header("signin_features") {
+ header = "signin_features.h"
+ flags = [
+ "ENABLE_DICE_SUPPORT=$enable_dice_support",
+ "ENABLE_MIRROR=$enable_mirror",
+ ]
+}
+
static_library("common") {
sources = [
"profile_management_switches.cc",
@@ -15,4 +26,8 @@ static_library("common") {
deps = [
"//base",
]
+
+ public_deps = [
+ ":signin_features",
+ ]
}
diff --git a/chromium/components/signin/core/common/profile_management_switches.cc b/chromium/components/signin/core/common/profile_management_switches.cc
index 4b14f6c0331..61dd356e6c0 100644
--- a/chromium/components/signin/core/common/profile_management_switches.cc
+++ b/chromium/components/signin/core/common/profile_management_switches.cc
@@ -14,14 +14,31 @@
namespace switches {
-bool IsEnableAccountConsistency() {
-#if defined(OS_ANDROID) || defined(OS_IOS)
- // Account consistency is enabled on Android and iOS.
- return true;
+AccountConsistencyMethod GetAccountConsistencyMethod() {
+#if BUILDFLAG(ENABLE_MIRROR)
+ // Mirror is enabled on Android and iOS.
+ return AccountConsistencyMethod::kMirror;
#else
- return base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableAccountConsistency);
-#endif // defined(OS_ANDROID) || defined(OS_IOS)
+ base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+ std::string method = cmd->GetSwitchValueASCII(switches::kAccountConsistency);
+ if (method == switches::kAccountConsistencyMirror)
+ return AccountConsistencyMethod::kMirror;
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ if (method == switches::kAccountConsistencyDice)
+ return AccountConsistencyMethod::kDice;
+#endif
+
+ return AccountConsistencyMethod::kDisabled;
+#endif // BUILDFLAG(ENABLE_MIRROR)
+}
+
+bool IsAccountConsistencyMirrorEnabled() {
+ return GetAccountConsistencyMethod() == AccountConsistencyMethod::kMirror;
+}
+
+bool IsAccountConsistencyDiceEnabled() {
+ return GetAccountConsistencyMethod() == AccountConsistencyMethod::kDice;
}
bool IsExtensionsMultiAccount() {
@@ -33,13 +50,21 @@ bool IsExtensionsMultiAccount() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kExtensionsMultiAccount) ||
- IsEnableAccountConsistency();
+ GetAccountConsistencyMethod() == AccountConsistencyMethod::kMirror;
}
-void EnableAccountConsistencyForTesting(base::CommandLine* command_line) {
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
- command_line->AppendSwitch(switches::kEnableAccountConsistency);
+void EnableAccountConsistencyMirrorForTesting(base::CommandLine* command_line) {
+#if !BUILDFLAG(ENABLE_MIRROR)
+ command_line->AppendSwitchASCII(switches::kAccountConsistency,
+ switches::kAccountConsistencyMirror);
#endif
}
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+void EnableAccountConsistencyDiceForTesting(base::CommandLine* command_line) {
+ command_line->AppendSwitchASCII(switches::kAccountConsistency,
+ switches::kAccountConsistencyDice);
+}
+#endif
+
} // namespace switches
diff --git a/chromium/components/signin/core/common/profile_management_switches.h b/chromium/components/signin/core/common/profile_management_switches.h
index b3080e961d0..9642ef5b0ce 100644
--- a/chromium/components/signin/core/common/profile_management_switches.h
+++ b/chromium/components/signin/core/common/profile_management_switches.h
@@ -9,21 +9,41 @@
#ifndef COMPONENTS_SIGNIN_CORE_COMMON_PROFILE_MANAGEMENT_SWITCHES_H_
#define COMPONENTS_SIGNIN_CORE_COMMON_PROFILE_MANAGEMENT_SWITCHES_H_
+#include "components/signin/core/common/signin_features.h"
+
namespace base {
class CommandLine;
}
namespace switches {
-// Checks whether account consistency is enabled. If enabled, the account
+enum class AccountConsistencyMethod {
+ kDisabled, // No account consistency.
+ kMirror, // Account management UI in the avatar bubble.
+ kDice // Account management UI on Gaia webpages.
+};
+
+// 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 IsEnableAccountConsistency();
+bool IsAccountConsistencyMirrorEnabled();
+
+// Checks whether Dice account consistency is enabled. If enabled, then account
+// management UI is available on the Gaia webpages.
+bool IsAccountConsistencyDiceEnabled();
// Whether the chrome.identity API should be multi-account.
bool IsExtensionsMultiAccount();
-// Called in tests to force enable account consistency.
-void EnableAccountConsistencyForTesting(base::CommandLine* command_line);
+// Called in tests to force enable Mirror account consistency.
+void EnableAccountConsistencyMirrorForTesting(base::CommandLine* command_line);
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+// Called in tests to force enable Dice account consistency.
+void EnableAccountConsistencyDiceForTesting(base::CommandLine* command_line);
+#endif
} // namespace switches
diff --git a/chromium/components/signin/core/common/signin_switches.cc b/chromium/components/signin/core/common/signin_switches.cc
index ef1d865c333..5ef25b778b3 100644
--- a/chromium/components/signin/core/common/signin_switches.cc
+++ b/chromium/components/signin/core/common/signin_switches.cc
@@ -16,9 +16,17 @@ const char kDisableSigninPromo[] = "disable-signin-promo";
// Disables sending signin scoped device id to LSO with refresh token request.
const char kDisableSigninScopedDeviceId[] = "disable-signin-scoped-device-id";
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
-// Enables consistent identity features.
-const char kEnableAccountConsistency[] = "enable-account-consistency";
+#if !BUILDFLAG(ENABLE_MIRROR)
+// Command line flag for enabling account consistency. Default mode is disabled.
+// Mirror is a legacy mode in which Google accounts are always addded to Chrome,
+// and Chrome then adds them to the Google authentication cookies.
+// Dice is a new experiment in which Chrome is aware of the accounts in the
+// Google authentication cookies.
+const char kAccountConsistency[] = "account-consistency";
+
+// Values for the kAccountConsistency flag.
+const char kAccountConsistencyMirror[] = "mirror";
+const char kAccountConsistencyDice[] = "dice";
#endif
// Enables sending EnableRefreshTokenAnnotationRequest.
diff --git a/chromium/components/signin/core/common/signin_switches.h b/chromium/components/signin/core/common/signin_switches.h
index 8c0ecc2db9b..5038a1e4fe3 100644
--- a/chromium/components/signin/core/common/signin_switches.h
+++ b/chromium/components/signin/core/common/signin_switches.h
@@ -5,7 +5,7 @@
#ifndef COMPONENTS_SIGNIN_CORE_COMMON_SIGNIN_SWITCHES_H_
#define COMPONENTS_SIGNIN_CORE_COMMON_SIGNIN_SWITCHES_H_
-#include "base/feature_list.h"
+#include "components/signin/core/common/signin_features.h"
namespace switches {
@@ -22,10 +22,12 @@ extern const char kEnableRefreshTokenAnnotationRequest[];
extern const char kEnableSigninPromo[];
extern const char kExtensionsMultiAccount[];
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
-// Note: Account consistency is already enabled on mobile platforms, so this
-// switch only exist on desktop platforms.
-extern const char kEnableAccountConsistency[];
+#if !BUILDFLAG(ENABLE_MIRROR)
+// Note: Account consistency (Mirror) is already enabled on mobile platforms, so
+// this switch only exist on desktop platforms.
+extern const char kAccountConsistency[];
+extern const char kAccountConsistencyMirror[];
+extern const char kAccountConsistencyDice[];
#endif
} // namespace switches
diff --git a/chromium/components/signin/features.gni b/chromium/components/signin/features.gni
new file mode 100644
index 00000000000..f4636abdb3f
--- /dev/null
+++ b/chromium/components/signin/features.gni
@@ -0,0 +1,9 @@
+# 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.
+
+# Dice is supported on the platform (but not necessarily enabled).
+enable_dice_support = (is_linux && !is_chromeos) || is_mac || is_win
+
+# Mirror is enabled and other account consistency mechanisms are not available.
+enable_mirror = is_android || is_ios
diff --git a/chromium/components/signin/ios/browser/BUILD.gn b/chromium/components/signin/ios/browser/BUILD.gn
index b914497f1d0..f34f92eec9a 100644
--- a/chromium/components/signin/ios/browser/BUILD.gn
+++ b/chromium/components/signin/ios/browser/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
source_set("browser") {
+ configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"account_consistency_service.h",
"account_consistency_service.mm",
@@ -48,6 +49,7 @@ source_set("test_support") {
}
source_set("unit_tests") {
+ configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [
"account_consistency_service_unittest.mm",
@@ -62,7 +64,8 @@ source_set("unit_tests") {
"//components/signin/core/common",
"//components/sync_preferences:test_support",
"//ios/web",
- "//ios/web:test_support",
+ "//ios/web/public/test",
+ "//ios/web/public/test/fakes",
"//third_party/ocmock",
]
}
diff --git a/chromium/components/signin/ios/browser/account_consistency_service.h b/chromium/components/signin/ios/browser/account_consistency_service.h
index 1f7a1e6eb3f..8df4874f76b 100644
--- a/chromium/components/signin/ios/browser/account_consistency_service.h
+++ b/chromium/components/signin/ios/browser/account_consistency_service.h
@@ -11,7 +11,6 @@
#include <set>
#include <string>
-#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
@@ -144,8 +143,7 @@ class AccountConsistencyService : public KeyedService,
// SigninManagerBase::Observer implementation.
void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
+ const std::string& username) override;
void GoogleSignedOut(const std::string& account_id,
const std::string& username) override;
@@ -178,11 +176,10 @@ class AccountConsistencyService : public KeyedService,
std::map<std::string, base::Time> last_cookie_update_map_;
// Web view used to apply the CHROME_CONNECTED cookie requests.
- base::scoped_nsobject<WKWebView> web_view_;
+ __strong WKWebView* web_view_;
// Navigation delegate of |web_view_| that informs the service when a cookie
// request has been applied.
- base::scoped_nsobject<AccountConsistencyNavigationDelegate>
- navigation_delegate_;
+ AccountConsistencyNavigationDelegate* navigation_delegate_;
// Handlers reacting on GAIA responses with the X-Chrome-Manage-Accounts
// header set.
diff --git a/chromium/components/signin/ios/browser/account_consistency_service.mm b/chromium/components/signin/ios/browser/account_consistency_service.mm
index 5f15df39e15..a56cc1b8626 100644
--- a/chromium/components/signin/ios/browser/account_consistency_service.mm
+++ b/chromium/components/signin/ios/browser/account_consistency_service.mm
@@ -6,7 +6,6 @@
#import <WebKit/WebKit.h>
-#import "base/ios/weak_nsobject.h"
#include "base/logging.h"
#import "base/mac/foundation_util.h"
#include "base/macros.h"
@@ -24,6 +23,10 @@
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace {
// Threshold (in hours) used to control whether the CHROME_CONNECTED cookie
@@ -58,7 +61,7 @@ class AccountConsistencyHandler : public web::WebStatePolicyDecider {
AccountConsistencyService* account_consistency_service_; // Weak.
AccountReconcilor* account_reconcilor_; // Weak.
- base::WeakNSProtocol<id<ManageAccountsDelegate>> delegate_;
+ __weak id<ManageAccountsDelegate> delegate_;
};
}
@@ -390,14 +393,14 @@ WKWebView* AccountConsistencyService::GetWKWebView() {
return nil;
}
if (!web_view_) {
- web_view_.reset([BuildWKWebView() retain]);
- navigation_delegate_.reset([[AccountConsistencyNavigationDelegate alloc]
+ web_view_ = BuildWKWebView();
+ navigation_delegate_ = [[AccountConsistencyNavigationDelegate alloc]
initWithCallback:base::Bind(&AccountConsistencyService::
FinishedApplyingCookieRequest,
- base::Unretained(this), true)]);
+ base::Unretained(this), true)];
[web_view_ setNavigationDelegate:navigation_delegate_];
}
- return web_view_.get();
+ return web_view_;
}
WKWebView* AccountConsistencyService::BuildWKWebView() {
@@ -407,8 +410,8 @@ WKWebView* AccountConsistencyService::BuildWKWebView() {
void AccountConsistencyService::ResetWKWebView() {
[web_view_ setNavigationDelegate:nil];
[web_view_ stopLoading];
- web_view_.reset();
- navigation_delegate_.reset();
+ web_view_ = nil;
+ navigation_delegate_ = nil;
applying_cookie_requests_ = false;
}
@@ -459,8 +462,7 @@ void AccountConsistencyService::OnGaiaAccountsInCookieUpdated(
void AccountConsistencyService::GoogleSigninSucceeded(
const std::string& account_id,
- const std::string& username,
- const std::string& password) {
+ const std::string& username) {
AddChromeConnectedCookies();
}
diff --git a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm
index 4dcfccf52ca..f61cebd655c 100644
--- a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm
+++ b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -8,7 +8,6 @@
#include <memory>
-#import "base/mac/scoped_nsobject.h"
#include "components/signin/core/browser/account_reconcilor.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/fake_signin_manager.h"
@@ -27,6 +26,10 @@
#include "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace {
// URL of the Google domain where the CHROME_CONNECTED cookie is set/removed.
NSURL* const kGoogleUrl = [NSURL URLWithString:@"https://google.com/"];
@@ -63,12 +66,11 @@ class FakeAccountConsistencyService : public AccountConsistencyService {
private:
WKWebView* BuildWKWebView() override {
if (!mock_web_view_) {
- mock_web_view_.reset(
- [[OCMockObject niceMockForClass:[WKWebView class]] retain]);
+ mock_web_view_ = [OCMockObject niceMockForClass:[WKWebView class]];
}
return mock_web_view_;
}
- base::scoped_nsobject<id> mock_web_view_;
+ id mock_web_view_;
};
// Mock AccountReconcilor to catch call to OnReceivedManageAccountsResponse.
@@ -155,7 +157,7 @@ class AccountConsistencyServiceTest : public PlatformTest {
void (^continueBlock)(NSInvocation*) = ^(NSInvocation* invocation) {
if (!continue_navigation)
return;
- WKWebView* web_view = nil;
+ __unsafe_unretained WKWebView* web_view = nil;
[invocation getArgument:&web_view atIndex:0];
[GetNavigationDelegate() webView:web_view didFinishNavigation:nil];
};
@@ -236,11 +238,11 @@ TEST_F(AccountConsistencyServiceTest, SignInSignOut) {
id delegate =
[OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
NSDictionary* headers = [NSDictionary dictionary];
- base::scoped_nsobject<NSHTTPURLResponse> response([[NSHTTPURLResponse alloc]
- initWithURL:kCountryGoogleUrl
- statusCode:200
- HTTPVersion:@"HTTP/1.1"
- headerFields:headers]);
+ NSHTTPURLResponse* response =
+ [[NSHTTPURLResponse alloc] initWithURL:kCountryGoogleUrl
+ statusCode:200
+ HTTPVersion:@"HTTP/1.1"
+ headerFields:headers];
account_consistency_service_->SetWebStateHandler(&web_state_, delegate);
EXPECT_TRUE(web_state_.ShouldAllowResponse(response));
web_state_.WebStateDestroyed();
@@ -292,11 +294,11 @@ TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsNotOnGaia) {
NSDictionary* headers =
[NSDictionary dictionaryWithObject:@"action=DEFAULT"
forKey:@"X-Chrome-Manage-Accounts"];
- base::scoped_nsobject<NSHTTPURLResponse> response([[NSHTTPURLResponse alloc]
+ NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
initWithURL:[NSURL URLWithString:@"https://google.com"]
statusCode:200
HTTPVersion:@"HTTP/1.1"
- headerFields:headers]);
+ headerFields:headers];
account_consistency_service_->SetWebStateHandler(&web_state_, delegate);
EXPECT_TRUE(web_state_.ShouldAllowResponse(response));
web_state_.WebStateDestroyed();
@@ -311,11 +313,11 @@ TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsNoHeader) {
[OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
NSDictionary* headers = [NSDictionary dictionary];
- base::scoped_nsobject<NSHTTPURLResponse> response([[NSHTTPURLResponse alloc]
+ NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"]
statusCode:200
HTTPVersion:@"HTTP/1.1"
- headerFields:headers]);
+ headerFields:headers];
account_consistency_service_->SetWebStateHandler(&web_state_, delegate);
EXPECT_TRUE(web_state_.ShouldAllowResponse(response));
web_state_.WebStateDestroyed();
@@ -335,11 +337,11 @@ TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsDefault) {
NSDictionary* headers =
[NSDictionary dictionaryWithObject:@"action=DEFAULT"
forKey:@"X-Chrome-Manage-Accounts"];
- base::scoped_nsobject<NSHTTPURLResponse> response([[NSHTTPURLResponse alloc]
+ NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"]
statusCode:200
HTTPVersion:@"HTTP/1.1"
- headerFields:headers]);
+ headerFields:headers];
account_consistency_service_->SetWebStateHandler(&web_state_, delegate);
EXPECT_CALL(account_reconcilor_, OnReceivedManageAccountsResponse(
signin::GAIA_SERVICE_TYPE_DEFAULT))
diff --git a/chromium/components/signin/ios/browser/merge_session_observer_bridge.h b/chromium/components/signin/ios/browser/merge_session_observer_bridge.h
index 8137dbbf1d0..ec9b10439d1 100644
--- a/chromium/components/signin/ios/browser/merge_session_observer_bridge.h
+++ b/chromium/components/signin/ios/browser/merge_session_observer_bridge.h
@@ -35,7 +35,7 @@ class MergeSessionObserverBridge : public GaiaCookieManagerService::Observer {
const GoogleServiceAuthError& error) override;
private:
- id<MergeSessionObserverBridgeDelegate> delegate_;
+ __weak id<MergeSessionObserverBridgeDelegate> delegate_;
GaiaCookieManagerService* cookie_manager_service_;
DISALLOW_COPY_AND_ASSIGN(MergeSessionObserverBridge);
diff --git a/chromium/components/signin/ios/browser/merge_session_observer_bridge.mm b/chromium/components/signin/ios/browser/merge_session_observer_bridge.mm
index 0237be1eb61..ee30039d09a 100644
--- a/chromium/components/signin/ios/browser/merge_session_observer_bridge.mm
+++ b/chromium/components/signin/ios/browser/merge_session_observer_bridge.mm
@@ -7,6 +7,10 @@
#include "base/logging.h"
#include "google_apis/gaia/google_service_auth_error.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
MergeSessionObserverBridge::MergeSessionObserverBridge(
id<MergeSessionObserverBridgeDelegate> delegate,
GaiaCookieManagerService* cookie_manager_service)
diff --git a/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.h b/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.h
index 04d38962310..425a9b3e90e 100644
--- a/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.h
+++ b/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.h
@@ -7,6 +7,7 @@
#import <Foundation/Foundation.h>
+#import "base/ios/weak_nsobject.h"
#include "base/macros.h"
#include "google_apis/gaia/oauth2_token_service.h"
@@ -49,7 +50,7 @@ class OAuth2TokenServiceObserverBridge : public OAuth2TokenService::Observer {
private:
OAuth2TokenService* token_service_; // weak
- id<OAuth2TokenServiceObserverBridgeDelegate> delegate_;
+ base::WeakNSProtocol<id<OAuth2TokenServiceObserverBridgeDelegate>> delegate_;
DISALLOW_COPY_AND_ASSIGN(OAuth2TokenServiceObserverBridge);
};
diff --git a/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm b/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm
index 7d649a939e9..ddaddb068d3 100644
--- a/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm
+++ b/chromium/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm
@@ -4,6 +4,10 @@
#include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
OAuth2TokenServiceObserverBridge::OAuth2TokenServiceObserverBridge(
OAuth2TokenService* token_service,
id<OAuth2TokenServiceObserverBridgeDelegate> delegate)
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 49606f24e79..57b10f8d544 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
@@ -27,6 +27,10 @@
#include "google_apis/gaia/oauth2_access_token_fetcher.h"
#include "net/url_request/url_request_status.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace {
// Match the way Chromium handles authentication errors in
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 a4043b3f022..ecfdd2c1a79 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
@@ -20,6 +20,10 @@
#include "net/url_request/test_url_fetcher_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
typedef ProfileOAuth2TokenServiceIOSProvider::AccountInfo ProviderAccount;
class ProfileOAuth2TokenServiceIOSDelegateTest
diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm
index 4144c6f2ec7..0ddcca0b5fa 100644
--- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm
+++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm
@@ -4,6 +4,10 @@
#include "components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
std::vector<ProfileOAuth2TokenServiceIOSProvider::AccountInfo>
ProfileOAuth2TokenServiceIOSProvider::GetAllAccounts() const {
return std::vector<ProfileOAuth2TokenServiceIOSProvider::AccountInfo>();
diff --git a/chromium/components/spellcheck/browser/spellcheck_message_filter_platform.h b/chromium/components/spellcheck/browser/spellcheck_message_filter_platform.h
index ce4a28de891..75e538838c8 100644
--- a/chromium/components/spellcheck/browser/spellcheck_message_filter_platform.h
+++ b/chromium/components/spellcheck/browser/spellcheck_message_filter_platform.h
@@ -42,8 +42,6 @@ class SpellCheckMessageFilterPlatform : public content::BrowserMessageFilter {
void OnCheckSpelling(const base::string16& word, int route_id, bool* correct);
void OnFillSuggestionList(const base::string16& word,
std::vector<base::string16>* suggestions);
- void OnShowSpellingPanel(bool show);
- void OnUpdateSpellingPanelWithMisspelledWord(const base::string16& word);
void OnRequestTextCheck(int route_id,
int identifier,
const base::string16& text);
diff --git a/chromium/components/spellcheck/browser/spellcheck_message_filter_platform_android.cc b/chromium/components/spellcheck/browser/spellcheck_message_filter_platform_android.cc
index 483bb7df51f..f68a73d99da 100644
--- a/chromium/components/spellcheck/browser/spellcheck_message_filter_platform_android.cc
+++ b/chromium/components/spellcheck/browser/spellcheck_message_filter_platform_android.cc
@@ -62,15 +62,6 @@ void SpellCheckMessageFilterPlatform::OnFillSuggestionList(
NOTREACHED();
}
-void SpellCheckMessageFilterPlatform::OnShowSpellingPanel(bool show) {
- NOTREACHED();
-}
-
-void SpellCheckMessageFilterPlatform::OnUpdateSpellingPanelWithMisspelledWord(
- const base::string16& word) {
- NOTREACHED();
-}
-
void SpellCheckMessageFilterPlatform::OnRequestTextCheck(
int route_id,
int identifier,
diff --git a/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc b/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc
index de8b3f92550..68d20a3d1fd 100644
--- a/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc
+++ b/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc
@@ -94,7 +94,8 @@ void SpellCheckerSessionBridge::ProcessSpellCheckResults(
JNIEnv* env,
const JavaParamRef<jobject>& jobj,
const JavaParamRef<jintArray>& offset_array,
- const JavaParamRef<jintArray>& length_array) {
+ const JavaParamRef<jintArray>& length_array,
+ const JavaParamRef<jobjectArray>& suggestions_array) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::vector<int> offsets;
std::vector<int> lengths;
@@ -104,8 +105,14 @@ void SpellCheckerSessionBridge::ProcessSpellCheckResults(
std::vector<SpellCheckResult> results;
for (size_t i = 0; i < offsets.size(); i++) {
- results.push_back(
- SpellCheckResult(SpellCheckResult::SPELLING, offsets[i], lengths[i]));
+ base::android::ScopedJavaLocalRef<jobjectArray> suggestions_for_word_array(
+ env, static_cast<jobjectArray>(
+ env->GetObjectArrayElement(suggestions_array, i)));
+ std::vector<base::string16> suggestions_for_word;
+ base::android::AppendJavaStringArrayToStringVector(
+ env, suggestions_for_word_array.obj(), &suggestions_for_word);
+ results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, offsets[i],
+ lengths[i], suggestions_for_word));
}
content::RenderProcessHost* sender =
diff --git a/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.h b/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.h
index c849d32ecff..a9118dcf417 100644
--- a/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.h
+++ b/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.h
@@ -35,7 +35,8 @@ class SpellCheckerSessionBridge {
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jobj,
const base::android::JavaParamRef<jintArray>& offset_array,
- const base::android::JavaParamRef<jintArray>& length_array);
+ const base::android::JavaParamRef<jintArray>& length_array,
+ const base::android::JavaParamRef<jobjectArray>& suggestions_array);
// Sets the handle to the Java SpellCheckerSessionBridge object to null,
// marking the Java object for garbage collection.
diff --git a/chromium/components/spellcheck/common/BUILD.gn b/chromium/components/spellcheck/common/BUILD.gn
index 4806ceef916..6c7c548f802 100644
--- a/chromium/components/spellcheck/common/BUILD.gn
+++ b/chromium/components/spellcheck/common/BUILD.gn
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//components/spellcheck/spellcheck_build_features.gni")
import("//mojo/public/tools/bindings/mojom.gni")
source_set("common") {
@@ -13,6 +14,7 @@ source_set("common") {
"spellcheck_message_generator.cc",
"spellcheck_message_generator.h",
"spellcheck_messages.h",
+ "spellcheck_result.cc",
"spellcheck_result.h",
"spellcheck_switches.cc",
"spellcheck_switches.h",
@@ -35,6 +37,10 @@ mojom("interfaces") {
"spellcheck.mojom",
]
+ if (has_spellcheck_panel) {
+ sources += [ "spellcheck_panel.mojom" ]
+ }
+
public_deps = [
"//mojo/common:common_custom_types",
]
diff --git a/chromium/components/spellcheck/common/OWNERS b/chromium/components/spellcheck/common/OWNERS
index ef88cc78b08..ed454dad2fd 100644
--- a/chromium/components/spellcheck/common/OWNERS
+++ b/chromium/components/spellcheck/common/OWNERS
@@ -2,3 +2,5 @@ per-file *_messages*.h=set noparent
per-file *_messages*.h=file://ipc/SECURITY_OWNERS
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/spellcheck/common/spellcheck.mojom b/chromium/components/spellcheck/common/spellcheck.mojom
index 91952616731..33b818f7d26 100644
--- a/chromium/components/spellcheck/common/spellcheck.mojom
+++ b/chromium/components/spellcheck/common/spellcheck.mojom
@@ -5,13 +5,15 @@
module spellcheck.mojom;
import "mojo/common/file.mojom";
+import "mojo/common/string16.mojom";
// Render process interface exposed to the browser for receiving process-
// wide spellcheck control and updates from the browser process.
//
interface SpellChecker {
// Initialize the render process spellchecker. Called after startup and
- // also in response to a render process RequestDictionary request.
+ // also in response to a renderer's spellcheck::mojom::SpellCheckHost
+ // RequestDictionary request.
Initialize(array<SpellCheckBDictLanguage> dictionaries,
array<string> custom_words,
bool enable);
@@ -26,3 +28,27 @@ struct SpellCheckBDictLanguage {
mojo.common.mojom.File? file;
string language;
};
+
+// Browser process interface exposed to the renderer for requesting spell-
+// check host services.
+//
+interface SpellCheckHost {
+ // Asks the browser to initialize the renderer's spellcheck system. The
+ // initialize call arrives on interface spellcheck::mojom::SpellChecker
+ // in async response to this request.
+ RequestDictionary();
+
+ // Tracks spell checking occurrences to collect histograms, where |word|
+ // was checked, and |misspelled| is true if |word| was misspelt.
+ NotifyChecked(mojo.common.mojom.String16 word, bool misspelled);
+
+ // Asks the host to spellcheck the |text| using a remote Spelling server
+ // to do the spellchecking. If the remote Spelling server is available,
+ // returns |success| true, and the spellchecked |results|. Note this API
+ // requires a !BUILDFLAG(USE_BROWSER_SPELLCHECKER) build.
+ CallSpellingService(mojo.common.mojom.String16 text) =>
+ (bool success, array<SpellCheckResult> results);
+};
+
+[Native]
+struct SpellCheckResult;
diff --git a/chromium/components/spellcheck/common/spellcheck.typemap b/chromium/components/spellcheck/common/spellcheck.typemap
new file mode 100644
index 00000000000..468241ca106
--- /dev/null
+++ b/chromium/components/spellcheck/common/spellcheck.typemap
@@ -0,0 +1,15 @@
+# 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.
+
+mojom = "//components/spellcheck/common/spellcheck.mojom"
+
+public_headers = [ "//components/spellcheck/common/spellcheck_result.h" ]
+
+traits_headers = [ "//components/spellcheck/common/spellcheck_messages.h" ]
+
+deps = [
+ "//components/spellcheck:build_features",
+]
+
+type_mappings = [ "spellcheck.mojom.SpellCheckResult=::SpellCheckResult" ]
diff --git a/chromium/components/spellcheck/common/spellcheck_messages.h b/chromium/components/spellcheck/common/spellcheck_messages.h
index e8e83a8f026..616315c1c0c 100644
--- a/chromium/components/spellcheck/common/spellcheck_messages.h
+++ b/chromium/components/spellcheck/common/spellcheck_messages.h
@@ -12,7 +12,7 @@
#include "ipc/ipc_message_macros.h"
#if !BUILDFLAG(ENABLE_SPELLCHECK)
-#error "Spellcheck should be enabled"
+#error "Spellcheck should be enabled."
#endif
#define IPC_MESSAGE_START SpellCheckMsgStart
@@ -23,23 +23,11 @@ IPC_STRUCT_TRAITS_BEGIN(SpellCheckResult)
IPC_STRUCT_TRAITS_MEMBER(decoration)
IPC_STRUCT_TRAITS_MEMBER(location)
IPC_STRUCT_TRAITS_MEMBER(length)
- IPC_STRUCT_TRAITS_MEMBER(replacement)
+ IPC_STRUCT_TRAITS_MEMBER(replacements)
IPC_STRUCT_TRAITS_END()
// Messages sent from the browser to the renderer.
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-// Sends text-check results from the Spelling service when the service finishes
-// checking text received by a SpellCheckHostMsg_CallSpellingService message.
-// If the service is not available, the 4th parameter should be false and the
-// 5th parameter should contain the requested sentence.
-IPC_MESSAGE_ROUTED4(SpellCheckMsg_RespondSpellingService,
- int /* request identifier given by WebKit */,
- bool /* succeeded calling service */,
- base::string16 /* sentence */,
- std::vector<SpellCheckResult>)
-#endif
-
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
// Sends when NSSpellChecker finishes checking text received by a preceding
// SpellCheckHostMsg_RequestTextCheck message.
@@ -49,36 +37,8 @@ IPC_MESSAGE_ROUTED3(SpellCheckMsg_RespondTextCheck,
std::vector<SpellCheckResult>)
#endif
-#if BUILDFLAG(HAS_SPELLCHECK_PANEL)
-// This message tells the renderer to advance to the next misspelling. It is
-// sent when the user clicks the "Find Next" button on the spelling panel.
-IPC_MESSAGE_ROUTED0(SpellCheckMsg_AdvanceToNextMisspelling)
-
-IPC_MESSAGE_ROUTED1(SpellCheckMsg_ToggleSpellPanel, bool)
-#endif
-
// Messages sent from the renderer to the browser.
-// The renderer has tried to spell check a word, but couldn't because no
-// dictionary was available to load. Request that the browser find an
-// appropriate dictionary and return it.
-IPC_MESSAGE_CONTROL0(SpellCheckHostMsg_RequestDictionary)
-
-// Tracks spell checking occurrence to collect histogram.
-IPC_MESSAGE_ROUTED2(SpellCheckHostMsg_NotifyChecked,
- base::string16 /* word */,
- bool /* true if checked word is misspelled */)
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-// Asks the Spelling service to check text. When the service finishes checking
-// the input text, it sends a SpellingCheckMsg_RespondSpellingService with
-// text-check results.
-IPC_MESSAGE_CONTROL3(SpellCheckHostMsg_CallSpellingService,
- int /* route_id for response */,
- int /* request identifier given by WebKit */,
- base::string16 /* sentence */)
-#endif
-
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
// TODO(groby): This needs to originate from SpellcheckProvider.
IPC_SYNC_MESSAGE_CONTROL2_1(SpellCheckHostMsg_CheckSpelling,
@@ -99,13 +59,3 @@ IPC_MESSAGE_ROUTED2(SpellCheckHostMsg_ToggleSpellCheck,
bool /* enabled */,
bool /* checked */)
#endif // USE_BROWSER_SPELLCHECKER
-
-#if BUILDFLAG(HAS_SPELLCHECK_PANEL)
-// Tells the browser to display or not display the SpellingPanel
-IPC_MESSAGE_ROUTED1(SpellCheckHostMsg_ShowSpellingPanel,
- bool /* if true, then show it, otherwise hide it*/)
-
-// Tells the browser to update the spelling panel with the given word.
-IPC_MESSAGE_ROUTED1(SpellCheckHostMsg_UpdateSpellingPanelWithMisspelledWord,
- base::string16 /* the word to update the panel with */)
-#endif
diff --git a/chromium/components/spellcheck/common/spellcheck_panel.mojom b/chromium/components/spellcheck/common/spellcheck_panel.mojom
new file mode 100644
index 00000000000..f50096bceb6
--- /dev/null
+++ b/chromium/components/spellcheck/common/spellcheck_panel.mojom
@@ -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.
+
+module spellcheck.mojom;
+
+import "mojo/common/string16.mojom";
+
+// Render frame interface exposed to the browser for sending messages when
+// the OSX BUILDFLAG(HAS_SPELLCHECK_PANEL) spelling and grammar checking
+// panel is active. The browser selects the currently focused render frame
+// and sends the following messages to that frame.
+//
+interface SpellCheckPanel {
+ // Informs the focused renderer frame if the OSX spelling and grammar
+ // checking panel is visible or not.
+ ToggleSpellPanel(bool visible);
+
+ // Tells the focused renderer frame to advance to the next misspelling.
+ // Sent when the user clicks the "Find Next" button on the OSX spelling
+ // and grammar checking panel.
+ AdvanceToNextMisspelling();
+};
+
+// Browser process interface exposed to the renderer for requesting spell-
+// check panel host services when the OSX BUILDFLAG(HAS_SPELLCHECK_PANEL)
+// spelling and grammar checking panel is active.
+//
+interface SpellCheckPanelHost {
+ // Tells the browser to display (or not display) the OSX spelling panel.
+ ShowSpellingPanel(bool show);
+
+ // Tells the browser to update the spelling panel with the given |word|.
+ UpdateSpellingPanelWithMisspelledWord(mojo.common.mojom.String16 word);
+};
diff --git a/chromium/components/spellcheck/common/spellcheck_result.cc b/chromium/components/spellcheck/common/spellcheck_result.cc
new file mode 100644
index 00000000000..ba5e0c922c1
--- /dev/null
+++ b/chromium/components/spellcheck/common/spellcheck_result.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 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/spellcheck/common/spellcheck_result.h"
+#include <vector>
+
+SpellCheckResult::SpellCheckResult(Decoration d,
+ int loc,
+ int len,
+ const std::vector<base::string16>& rep)
+ : decoration(d), location(loc), length(len), replacements(rep) {}
+
+SpellCheckResult::SpellCheckResult(Decoration d,
+ int loc,
+ int len,
+ const base::string16& rep)
+ : decoration(d),
+ location(loc),
+ length(len),
+ replacements(std::vector<base::string16>({rep})) {}
+
+SpellCheckResult::~SpellCheckResult() = default;
+
+SpellCheckResult::SpellCheckResult(const SpellCheckResult&) = default;
diff --git a/chromium/components/spellcheck/common/spellcheck_result.h b/chromium/components/spellcheck/common/spellcheck_result.h
index 0c4fb45eff7..1937a8f21d8 100644
--- a/chromium/components/spellcheck/common/spellcheck_result.h
+++ b/chromium/components/spellcheck/common/spellcheck_result.h
@@ -6,6 +6,7 @@
#define COMPONENTS_SPELLCHECK_COMMON_SPELLCHECK_RESULT_H_
#include <stdint.h>
+#include <vector>
#include "base/strings/string16.h"
@@ -22,16 +23,25 @@ struct SpellCheckResult {
GRAMMAR = 1 << 2,
};
- explicit SpellCheckResult(Decoration d = SPELLING,
- int loc = 0,
- int len = 0,
- const base::string16& rep = base::string16())
- : decoration(d), location(loc), length(len), replacement(rep) {}
+ // Default values are so we have a default constructor for IPC::ReadParam()
+ explicit SpellCheckResult(
+ Decoration d = SPELLING,
+ int loc = 0,
+ int len = 0,
+ const std::vector<base::string16>& rep = std::vector<base::string16>());
+
+ explicit SpellCheckResult(Decoration d,
+ int loc,
+ int len,
+ const base::string16& rep);
+
+ ~SpellCheckResult();
+ SpellCheckResult(const SpellCheckResult&);
Decoration decoration;
int location;
int length;
- base::string16 replacement;
+ std::vector<base::string16> replacements;
};
#endif // COMPONENTS_SPELLCHECK_COMMON_SPELLCHECK_RESULT_H_
diff --git a/chromium/components/spellcheck/renderer/hunspell_engine.cc b/chromium/components/spellcheck/renderer/hunspell_engine.cc
index c3853483105..4ef02676059 100644
--- a/chromium/components/spellcheck/renderer/hunspell_engine.cc
+++ b/chromium/components/spellcheck/renderer/hunspell_engine.cc
@@ -11,10 +11,12 @@
#include "base/files/memory_mapped_file.h"
#include "base/time/time.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/common/spellcheck_common.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
#include "components/spellcheck/spellcheck_build_features.h"
+#include "content/public/common/service_names.mojom.h"
#include "content/public/renderer/render_thread.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "third_party/hunspell/src/hunspell/hunspell.hxx"
using content::RenderThread;
@@ -119,8 +121,12 @@ void HunspellEngine::FillSuggestionList(
bool HunspellEngine::InitializeIfNeeded() {
if (!initialized_ && !dictionary_requested_) {
// RenderThread will not exist in test.
- if (RenderThread::Get())
- RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary);
+ if (RenderThread::Get()) {
+ spellcheck::mojom::SpellCheckHostPtr spell_check_host;
+ RenderThread::Get()->GetConnector()->BindInterface(
+ content::mojom::kBrowserServiceName, &spell_check_host);
+ spell_check_host->RequestDictionary();
+ }
dictionary_requested_ = true;
return true;
}
diff --git a/chromium/components/spellcheck/renderer/spellcheck.cc b/chromium/components/spellcheck/renderer/spellcheck.cc
index 4ef53a067a6..262a47063cd 100644
--- a/chromium/components/spellcheck/renderer/spellcheck.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck.cc
@@ -30,7 +30,6 @@
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_visitor.h"
#include "content/public/renderer/render_thread.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebVector.h"
@@ -113,6 +112,25 @@ void PreserveOriginalApostropheTypes(const base::string16& misspelled_word,
}
}
+std::vector<WebString> FilterReplacementSuggestions(
+ const base::string16& misspelled_word,
+ const std::vector<base::string16>& replacements) {
+ std::vector<WebString> replacements_filtered;
+ for (base::string16 replacement : replacements) {
+ // Use the same types of apostrophes as in the mispelled word.
+ PreserveOriginalApostropheTypes(misspelled_word, &replacement);
+
+ // Ignore suggestions that are just changing the apostrophe type
+ // (straight vs. typographical)
+ if (replacement == misspelled_word)
+ continue;
+
+ replacements_filtered.push_back(WebString::FromUTF16(replacement));
+ }
+
+ return replacements_filtered;
+}
+
} // namespace
class SpellCheck::SpellcheckRequest {
@@ -201,9 +219,8 @@ void SpellCheck::FillSuggestions(
}
void SpellCheck::SpellCheckerRequest(
- const service_manager::BindSourceInfo& source_info,
spellcheck::mojom::SpellCheckerRequest request) {
- spellchecker_bindings_.AddBinding(this, std::move(request));
+ bindings_.AddBinding(this, std::move(request));
}
void SpellCheck::Initialize(
@@ -473,7 +490,8 @@ void SpellCheck::CreateTextCheckingResults(
const base::string16& misspelled_word =
line_text.substr(spellcheck_result.location, spellcheck_result.length);
- base::string16 replacement = spellcheck_result.replacement;
+ const std::vector<base::string16>& replacements =
+ spellcheck_result.replacements;
SpellCheckResult::Decoration decoration = spellcheck_result.decoration;
// Ignore words in custom dictionary.
@@ -482,11 +500,13 @@ void SpellCheck::CreateTextCheckingResults(
continue;
}
- // Use the same types of appostrophes as in the mispelled word.
- PreserveOriginalApostropheTypes(misspelled_word, &replacement);
+ std::vector<WebString> replacements_filtered =
+ FilterReplacementSuggestions(misspelled_word, replacements);
- // Ignore misspellings due the typographical apostrophe.
- if (misspelled_word == replacement)
+ // If the spellchecker suggested replacements, but they were all just
+ // changing apostrophe styles, ignore this misspelling. If there were never
+ // any suggested replacements, keep the misspelling.
+ if (replacements_filtered.empty() && !replacements.empty())
continue;
if (filter == USE_NATIVE_CHECKER) {
@@ -504,10 +524,10 @@ void SpellCheck::CreateTextCheckingResults(
}
}
- results.push_back(WebTextCheckingResult(
- static_cast<WebTextDecorationType>(decoration),
- line_offset + spellcheck_result.location, spellcheck_result.length,
- blink::WebString::FromUTF16(replacement)));
+ results.push_back(
+ WebTextCheckingResult(static_cast<WebTextDecorationType>(decoration),
+ line_offset + spellcheck_result.location,
+ spellcheck_result.length, replacements_filtered));
}
textcheck_results->Assign(results);
diff --git a/chromium/components/spellcheck/renderer/spellcheck.h b/chromium/components/spellcheck/renderer/spellcheck.h
index c5a839f7ba6..79bc547b2c9 100644
--- a/chromium/components/spellcheck/renderer/spellcheck.h
+++ b/chromium/components/spellcheck/renderer/spellcheck.h
@@ -30,10 +30,6 @@ struct WebTextCheckingResult;
template <typename T> class WebVector;
}
-namespace service_manager {
-struct BindSourceInfo;
-}
-
// TODO(morrita): Needs reorg with SpellCheckProvider.
// See http://crbug.com/73699.
// Shared spellchecking logic/data for a RenderProcess. All RenderViews use
@@ -124,8 +120,7 @@ class SpellCheck : public content::RenderThreadObserver,
std::vector<base::string16>* optional_suggestions);
// Binds requests for the SpellChecker interface.
- void SpellCheckerRequest(const service_manager::BindSourceInfo& source_info,
- spellcheck::mojom::SpellCheckerRequest request);
+ void SpellCheckerRequest(spellcheck::mojom::SpellCheckerRequest request);
// spellcheck::mojom::SpellChecker:
void Initialize(
@@ -153,7 +148,7 @@ class SpellCheck : public content::RenderThreadObserver,
#endif
// Bindings for SpellChecker clients.
- mojo::BindingSet<spellcheck::mojom::SpellChecker> spellchecker_bindings_;
+ mojo::BindingSet<spellcheck::mojom::SpellChecker> bindings_;
// A vector of objects used to actually check spelling, one for each enabled
// language.
diff --git a/chromium/components/spellcheck/renderer/spellcheck_panel.cc b/chromium/components/spellcheck/renderer/spellcheck_panel.cc
index e2b0373d402..545f1cf52b8 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_panel.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_panel.cc
@@ -4,66 +4,77 @@
#include "components/spellcheck/renderer/spellcheck_panel.h"
+#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "content/public/renderer/render_view.h"
+#include "content/public/common/service_names.mojom.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
-#include "third_party/WebKit/public/web/WebView.h"
-using blink::WebString;
+namespace {
+// Returns the browser's SpellCheckPanelHost interface.
+spellcheck::mojom::SpellCheckPanelHostPtr GetSpellCheckPanelHost() {
+ spellcheck::mojom::SpellCheckPanelHostPtr spell_check_panel_host;
+ DCHECK(content::RenderThread::Get());
+ content::RenderThread::Get()->GetConnector()->BindInterface(
+ content::mojom::kBrowserServiceName, &spell_check_panel_host);
+ return spell_check_panel_host;
+}
+}
-SpellCheckPanel::SpellCheckPanel(content::RenderView* render_view)
- : content::RenderViewObserver(render_view),
- content::RenderViewObserverTracker<SpellCheckPanel>(render_view),
+SpellCheckPanel::SpellCheckPanel(content::RenderFrame* render_frame)
+ : content::RenderFrameObserver(render_frame),
spelling_panel_visible_(false) {
- render_view->GetWebView()->SetSpellCheckClient(this);
+ DCHECK(render_frame);
+ render_frame->GetInterfaceRegistry()->AddInterface(base::Bind(
+ &SpellCheckPanel::SpellCheckPanelRequest, base::Unretained(this)));
+ render_frame->GetWebFrame()->SetSpellCheckPanelHostClient(this);
}
SpellCheckPanel::~SpellCheckPanel() = default;
-void SpellCheckPanel::ShowSpellingUI(bool show) {
- UMA_HISTOGRAM_BOOLEAN("SpellCheck.api.showUI", show);
- Send(new SpellCheckHostMsg_ShowSpellingPanel(routing_id(), show));
+void SpellCheckPanel::OnDestruct() {
+ delete this;
}
bool SpellCheckPanel::IsShowingSpellingUI() {
return spelling_panel_visible_;
}
+void SpellCheckPanel::ShowSpellingUI(bool show) {
+ UMA_HISTOGRAM_BOOLEAN("SpellCheck.api.showUI", show);
+ GetSpellCheckPanelHost()->ShowSpellingPanel(show);
+}
+
void SpellCheckPanel::UpdateSpellingUIWithMisspelledWord(
- const WebString& word) {
- Send(new SpellCheckHostMsg_UpdateSpellingPanelWithMisspelledWord(
- routing_id(), word.Utf16()));
+ const blink::WebString& word) {
+ GetSpellCheckPanelHost()->UpdateSpellingPanelWithMisspelledWord(word.Utf16());
}
-bool SpellCheckPanel::OnMessageReceived(const IPC::Message& message) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(SpellCheckPanel, message)
- IPC_MESSAGE_HANDLER(SpellCheckMsg_AdvanceToNextMisspelling,
- OnAdvanceToNextMisspelling)
- IPC_MESSAGE_HANDLER(SpellCheckMsg_ToggleSpellPanel, OnToggleSpellPanel)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
+void SpellCheckPanel::SpellCheckPanelRequest(
+ spellcheck::mojom::SpellCheckPanelRequest request) {
+ bindings_.AddBinding(this, std::move(request));
}
-void SpellCheckPanel::OnAdvanceToNextMisspelling() {
- if (!render_view()->GetWebView())
- return;
- render_view()->GetWebView()->FocusedFrame()->ExecuteCommand(
- WebString::FromUTF8("AdvanceToNextMisspelling"));
+void SpellCheckPanel::AdvanceToNextMisspelling() {
+ auto* render_frame = content::RenderFrameObserver::render_frame();
+ DCHECK(render_frame->GetWebFrame());
+
+ render_frame->GetWebFrame()->ExecuteCommand(
+ blink::WebString::FromUTF8("AdvanceToNextMisspelling"));
}
-void SpellCheckPanel::OnToggleSpellPanel(bool is_currently_visible) {
- if (!render_view()->GetWebView())
- return;
- // We need to tell the webView whether the spelling panel is visible or not so
+void SpellCheckPanel::ToggleSpellPanel(bool visible) {
+ auto* render_frame = content::RenderFrameObserver::render_frame();
+ DCHECK(render_frame->GetWebFrame());
+
+ // Tell our frame whether the spelling panel is visible or not so
// that it won't need to make ipc calls later.
- spelling_panel_visible_ = is_currently_visible;
- render_view()->GetWebView()->FocusedFrame()->ExecuteCommand(
- WebString::FromUTF8("ToggleSpellPanel"));
-}
+ spelling_panel_visible_ = visible;
-void SpellCheckPanel::OnDestruct() {
- delete this;
+ render_frame->GetWebFrame()->ExecuteCommand(
+ blink::WebString::FromUTF8("ToggleSpellPanel"));
}
diff --git a/chromium/components/spellcheck/renderer/spellcheck_panel.h b/chromium/components/spellcheck/renderer/spellcheck_panel.h
index 18c9c4e2f15..8b3f63f41fb 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_panel.h
+++ b/chromium/components/spellcheck/renderer/spellcheck_panel.h
@@ -6,40 +6,45 @@
#define COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PANEL_H
#include "base/macros.h"
+#include "components/spellcheck/common/spellcheck_panel.mojom.h"
#include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/renderer/render_view_observer.h"
-#include "content/public/renderer/render_view_observer_tracker.h"
-#include "third_party/WebKit/public/web/WebSpellCheckClient.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "third_party/WebKit/public/platform/WebSpellCheckPanelHostClient.h"
#if !BUILDFLAG(HAS_SPELLCHECK_PANEL)
-#error "This file shouldn't be compiled without spellcheck panel."
+#error "Spellcheck panel should be enabled."
#endif
-class SpellCheckPanel
- : public content::RenderViewObserver,
- public content::RenderViewObserverTracker<SpellCheckPanel>,
- public blink::WebSpellCheckClient {
+class SpellCheckPanel : public content::RenderFrameObserver,
+ public blink::WebSpellCheckPanelHostClient,
+ public spellcheck::mojom::SpellCheckPanel {
public:
- explicit SpellCheckPanel(content::RenderView* render_view);
+ explicit SpellCheckPanel(content::RenderFrame* render_frame);
~SpellCheckPanel() override;
- // RenderViewObserver implementation.
- bool OnMessageReceived(const IPC::Message& message) override;
-
private:
- // RenderViewObserver implementation.
+ // content::RenderFrameObserver:
void OnDestruct() override;
- // blink::WebSpellCheckClient implementation.
- void ShowSpellingUI(bool show) override;
+ // blink::WebSpellCheckPanelHostClient:
bool IsShowingSpellingUI() override;
+ void ShowSpellingUI(bool show) override;
void UpdateSpellingUIWithMisspelledWord(
const blink::WebString& word) override;
- void OnAdvanceToNextMisspelling();
- void OnToggleSpellPanel(bool is_currently_visible);
+ // Binds browser requests for the frame SpellCheckPanel interface.
+ void SpellCheckPanelRequest(
+ spellcheck::mojom::SpellCheckPanelRequest request);
+
+ // spellcheck::mojom::SpellCheckPanel:
+ void ToggleSpellPanel(bool visible) override;
+ void AdvanceToNextMisspelling() override;
+
+ // SpellCheckPanel bindings.
+ mojo::BindingSet<spellcheck::mojom::SpellCheckPanel> bindings_;
- // True if the browser is showing the spelling panel for us.
+ // True if the browser is showing the spelling panel.
bool spelling_panel_visible_;
DISALLOW_COPY_AND_ASSIGN(SpellCheckPanel);
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider.cc b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
index abd2cd5422f..1807349ea85 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
@@ -4,13 +4,18 @@
#include "components/spellcheck/renderer/spellcheck_provider.h"
+#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/common/spellcheck_messages.h"
#include "components/spellcheck/common/spellcheck_result.h"
#include "components/spellcheck/renderer/spellcheck.h"
#include "components/spellcheck/renderer/spellcheck_language.h"
#include "components/spellcheck/spellcheck_build_features.h"
+#include "content/public/common/service_names.mojom.h"
#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "third_party/WebKit/public/platform/WebVector.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebElement.h"
@@ -47,6 +52,16 @@ SpellCheckProvider::SpellCheckProvider(content::RenderFrame* render_frame,
SpellCheckProvider::~SpellCheckProvider() {
}
+spellcheck::mojom::SpellCheckHost& SpellCheckProvider::GetSpellCheckHost() {
+ if (spell_check_host_)
+ return *spell_check_host_;
+
+ DCHECK(content::RenderThread::Get());
+ content::RenderThread::Get()->GetConnector()->BindInterface(
+ content::mojom::kBrowserServiceName, &spell_check_host_);
+ return *spell_check_host_;
+}
+
void SpellCheckProvider::RequestTextChecking(
const base::string16& text,
WebTextCheckingCompletion* completion) {
@@ -64,34 +79,36 @@ void SpellCheckProvider::RequestTextChecking(
// this text to the Spelling service only if a user enables this feature.
last_request_.clear();
last_results_.Assign(blink::WebVector<blink::WebTextCheckingResult>());
+ last_identifier_ = text_check_completions_.Add(completion);
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+ // TODO(crbug.com/714480): convert the RequestTextCheck IPC to mojo.
// Text check (unified request for grammar and spell check) is only
// available for browser process, so we ask the system spellchecker
- // over IPC or return an empty result if the checker is not
- // available.
- Send(new SpellCheckHostMsg_RequestTextCheck(
- routing_id(), text_check_completions_.Add(completion), text));
+ // over IPC or return an empty result if the checker is not available.
+ Send(new SpellCheckHostMsg_RequestTextCheck(routing_id(), last_identifier_,
+ text));
#else
- Send(new SpellCheckHostMsg_CallSpellingService(
- routing_id(), text_check_completions_.Add(completion),
- base::string16(text)));
+ if (!spell_check_host_ && !content::RenderThread::Get())
+ return; // NULL in tests that do not provide a spell_check_host_.
+ GetSpellCheckHost().CallSpellingService(
+ text, base::Bind(&SpellCheckProvider::OnRespondSpellingService,
+ base::Unretained(this), last_identifier_, text));
#endif // !USE_BROWSER_SPELLCHECKER
}
bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) {
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message)
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
- IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService,
- OnRespondSpellingService)
-#endif
-#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+ // TODO(crbug.com/714480): convert the RequestTextCheck IPC to mojo.
IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondTextCheck, OnRespondTextCheck)
-#endif
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
+#else
+ return false;
+#endif
}
void SpellCheckProvider::FocusedNodeChanged(const blink::WebNode& unused) {
@@ -103,6 +120,7 @@ void SpellCheckProvider::FocusedNodeChanged(const blink::WebNode& unused) {
bool enabled = !element.IsNull() && element.IsEditable();
bool checked = enabled && frame->IsSpellCheckingEnabled();
+ // TODO(crbug.com/714480): convert the ToggleSpellCheck IPC to mojo.
Send(new SpellCheckHostMsg_ToggleSpellCheck(routing_id(), enabled, checked));
#endif // USE_BROWSER_SPELLCHECKER
}
@@ -129,7 +147,9 @@ void SpellCheckProvider::CheckSpelling(
UMA_HISTOGRAM_COUNTS("SpellCheck.api.check", word.size());
// If optional_suggestions is not requested, the API is called
// for marking. So we use this for counting markable words.
- Send(new SpellCheckHostMsg_NotifyChecked(routing_id(), word, 0 < length));
+ if (!spell_check_host_ && !content::RenderThread::Get())
+ return; // NULL in tests that do not provide a spell_check_host_.
+ GetSpellCheckHost().NotifyChecked(word, 0 < length);
}
}
@@ -151,8 +171,8 @@ void SpellCheckProvider::CancelAllPendingRequests() {
#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
void SpellCheckProvider::OnRespondSpellingService(
int identifier,
- bool succeeded,
const base::string16& line,
+ bool success,
const std::vector<SpellCheckResult>& results) {
WebTextCheckingCompletion* completion =
text_check_completions_.Lookup(identifier);
@@ -160,8 +180,8 @@ void SpellCheckProvider::OnRespondSpellingService(
return;
text_check_completions_.Remove(identifier);
- // If |succeeded| is false, we use local spellcheck as a fallback.
- if (!succeeded) {
+ // If |success| is false, we use local spellcheck as a fallback.
+ if (!success) {
spellcheck_->RequestTextChecking(line, completion);
return;
}
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider.h b/chromium/components/spellcheck/renderer/spellcheck_provider.h
index cead10758dc..d31e1107a0d 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider.h
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider.h
@@ -5,13 +5,11 @@
#ifndef COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_H_
#define COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_H_
-#include <stddef.h>
-#include <stdint.h>
-
#include <vector>
#include "base/id_map.h"
#include "base/macros.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/spellcheck_build_features.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_observer_tracker.h"
@@ -25,8 +23,8 @@ class WebTextCheckingCompletion;
struct WebTextCheckingResult;
}
-// This class deals with invoking browser-side spellcheck mechanism
-// which is done asynchronously.
+// This class deals with asynchronously invoking text spelling and grammar
+// checking services provided by the browser process (host).
class SpellCheckProvider
: public content::RenderFrameObserver,
public content::RenderFrameObserverTracker<SpellCheckProvider>,
@@ -38,14 +36,14 @@ class SpellCheckProvider
SpellCheck* spellcheck);
~SpellCheckProvider() override;
- // Requests async spell and grammar checker to the platform text
- // checker, which is available on the browser process. The function does not
- // have special handling for partial words, as Blink guarantees that no
- // request is made when typing in the middle of a word.
+ // Requests async spell and grammar checks from the platform text checker
+ // available in the browser process. The function does not have special
+ // handling for partial words, as Blink guarantees that no request is made
+ // when typing in the middle of a word.
void RequestTextChecking(const base::string16& text,
blink::WebTextCheckingCompletion* completion);
- // The number of ongoing IPC requests.
+ // The number of ongoing spell check host requests.
size_t pending_text_request_size() const {
return text_check_completions_.size();
}
@@ -56,23 +54,31 @@ class SpellCheckProvider
// Enables document-wide spellchecking.
void EnableSpellcheck(bool enabled);
- // RenderFrameObserver implementation.
+ // content::RenderFrameObserver:
bool OnMessageReceived(const IPC::Message& message) override;
void FocusedNodeChanged(const blink::WebNode& node) override;
private:
friend class TestingSpellCheckProvider;
- // Tries to satisfy a spell check request from the cache in |last_request_|.
+ // Sets the SpellCheckHost (for unit tests).
+ void SetSpellCheckHostForTesting(spellcheck::mojom::SpellCheckHostPtr host) {
+ spell_check_host_ = std::move(host);
+ }
+
+ // Returns the SpellCheckHost.
+ spellcheck::mojom::SpellCheckHost& GetSpellCheckHost();
+
+ // Tries to satisfy a spellcheck request from the cache in |last_request_|.
// Returns true (and cancels/finishes the completion) if it can, false
// if the provider should forward the query on.
bool SatisfyRequestFromCache(const base::string16& text,
blink::WebTextCheckingCompletion* completion);
- // RenderFrameObserver implementation.
+ // content::RenderFrameObserver:
void OnDestruct() override;
- // blink::WebTextCheckClient implementation.
+ // blink::WebTextCheckClient:
void CheckSpelling(
const blink::WebString& text,
int& offset,
@@ -84,11 +90,10 @@ class SpellCheckProvider
void CancelAllPendingRequests() override;
#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
- void OnRespondSpellingService(
- int identifier,
- bool succeeded,
- const base::string16& text,
- const std::vector<SpellCheckResult>& results);
+ void OnRespondSpellingService(int identifier,
+ const base::string16& text,
+ bool success,
+ const std::vector<SpellCheckResult>& results);
#endif
// Returns whether |text| has word characters, i.e. whether a spellchecker
@@ -102,17 +107,21 @@ class SpellCheckProvider
const std::vector<SpellCheckResult>& results);
#endif
- // Holds ongoing spellchecking operations, assigns IDs for the IPC routing.
+ // Holds ongoing spellchecking operations.
WebTextCheckCompletions text_check_completions_;
- // The last text sent to the browser process to spellcheck it and its
- // spellchecking results.
+ // The last text sent to the browser process for spellchecking, and its
+ // spellcheck results and WebTextCheckCompletions identifier.
base::string16 last_request_;
blink::WebVector<blink::WebTextCheckingResult> last_results_;
+ int last_identifier_;
- // Weak pointer to shared (per RenderView) spellcheck data.
+ // Weak pointer to shared (per renderer) spellcheck data.
SpellCheck* spellcheck_;
+ // Interface to the SpellCheckHost.
+ spellcheck::mojom::SpellCheckHostPtr spell_check_host_;
+
DISALLOW_COPY_AND_ASSIGN(SpellCheckProvider);
};
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc b/chromium/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc
index 34ab0a66224..40269b820f4 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc
@@ -2,13 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <vector>
-
-#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/spellcheck/renderer/spellcheck_provider_test.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/platform/WebString.h"
// Tests for Hunspell functionality in SpellcheckingProvider
@@ -17,14 +13,6 @@ using base::WideToUTF16;
namespace {
-TEST_F(SpellCheckProviderTest, UsingHunspell) {
- FakeTextCheckingCompletion completion;
- provider_.RequestTextChecking(ASCIIToUTF16("hello"), &completion);
- EXPECT_EQ(completion.completion_count_, 1U);
- EXPECT_EQ(provider_.messages_.size(), 0U);
- EXPECT_EQ(provider_.pending_text_request_size(), 0U);
-}
-
// Tests that the SpellCheckProvider object sends a spellcheck request when a
// user finishes typing a word. Also this test verifies that this object checks
// only a line being edited by the user.
@@ -35,18 +23,21 @@ TEST_F(SpellCheckProviderTest, MultiLineText) {
provider_.ResetResult();
provider_.RequestTextChecking(base::string16(), &completion);
EXPECT_TRUE(provider_.text_.empty());
+ EXPECT_EQ(provider_.spelling_service_call_count_, 0U);
// Verify that the SpellCheckProvider class spellcheck the first word when we
// stop typing after finishing the first word.
provider_.ResetResult();
provider_.RequestTextChecking(ASCIIToUTF16("First"), &completion);
EXPECT_EQ(ASCIIToUTF16("First"), provider_.text_);
+ EXPECT_EQ(provider_.spelling_service_call_count_, 1U);
// Verify that the SpellCheckProvider class spellcheck the first line when we
// type a return key, i.e. when we finish typing a line.
provider_.ResetResult();
provider_.RequestTextChecking(ASCIIToUTF16("First Second\n"), &completion);
EXPECT_EQ(ASCIIToUTF16("First Second\n"), provider_.text_);
+ EXPECT_EQ(provider_.spelling_service_call_count_, 2U);
// Verify that the SpellCheckProvider class spellcheck the lines when we
// finish typing a word "Third" to the second line.
@@ -54,6 +45,7 @@ TEST_F(SpellCheckProviderTest, MultiLineText) {
provider_.RequestTextChecking(ASCIIToUTF16("First Second\nThird "),
&completion);
EXPECT_EQ(ASCIIToUTF16("First Second\nThird "), provider_.text_);
+ EXPECT_EQ(provider_.spelling_service_call_count_, 3U);
// Verify that the SpellCheckProvider class does not send a spellcheck request
// when a user inserts whitespace characters.
@@ -61,6 +53,7 @@ TEST_F(SpellCheckProviderTest, MultiLineText) {
provider_.RequestTextChecking(ASCIIToUTF16("First Second\nThird "),
&completion);
EXPECT_TRUE(provider_.text_.empty());
+ EXPECT_EQ(provider_.spelling_service_call_count_, 3U);
// Verify that the SpellCheckProvider class spellcheck the lines when we type
// a period.
@@ -68,6 +61,7 @@ TEST_F(SpellCheckProviderTest, MultiLineText) {
provider_.RequestTextChecking(ASCIIToUTF16("First Second\nThird Fourth."),
&completion);
EXPECT_EQ(ASCIIToUTF16("First Second\nThird Fourth."), provider_.text_);
+ EXPECT_EQ(provider_.spelling_service_call_count_, 4U);
}
// Tests that the SpellCheckProvider class does not send requests to the
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc b/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc
index ffba9d14b0c..8e78c70ea59 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc
@@ -5,19 +5,16 @@
#include "components/spellcheck/renderer/spellcheck_provider_test.h"
#include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
+#include "components/spellcheck/common/spellcheck_result.h"
#include "components/spellcheck/renderer/spellcheck.h"
#include "components/spellcheck/spellcheck_build_features.h"
-#include "ipc/ipc_message_macros.h"
-
-class MockSpellcheck: public SpellCheck {
-};
+#include "ipc/ipc_message.h"
FakeTextCheckingCompletion::FakeTextCheckingCompletion()
-: completion_count_(0),
- cancellation_count_(0) {
-}
+ : completion_count_(0), cancellation_count_(0) {}
FakeTextCheckingCompletion::~FakeTextCheckingCompletion() {}
@@ -32,62 +29,80 @@ void FakeTextCheckingCompletion::DidCancelCheckingText() {
}
TestingSpellCheckProvider::TestingSpellCheckProvider()
- : SpellCheckProvider(NULL, new MockSpellcheck),
- spelling_service_call_count_(0) {
-}
+ : SpellCheckProvider(nullptr, new SpellCheck),
+ spelling_service_call_count_(0),
+ binding_(this) {}
-TestingSpellCheckProvider::TestingSpellCheckProvider(
- SpellCheck* spellcheck)
+TestingSpellCheckProvider::TestingSpellCheckProvider(SpellCheck* spellcheck)
: SpellCheckProvider(nullptr, spellcheck),
- spelling_service_call_count_(0) {
-}
+ spelling_service_call_count_(0),
+ binding_(this) {}
TestingSpellCheckProvider::~TestingSpellCheckProvider() {
+ binding_.Close();
delete spellcheck_;
}
-bool TestingSpellCheckProvider::Send(IPC::Message* message) {
+void TestingSpellCheckProvider::RequestTextChecking(
+ const base::string16& text,
+ blink::WebTextCheckingCompletion* completion) {
#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
- // Call our mock message handlers.
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(TestingSpellCheckProvider, *message)
- IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService,
- OnCallSpellingService)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
-
- if (handled) {
- delete message;
- return true;
+ if (!loop_ && !base::MessageLoop::current())
+ loop_ = base::MakeUnique<base::MessageLoop>();
+ if (!binding_.is_bound()) {
+ spellcheck::mojom::SpellCheckHostPtr host_proxy;
+ binding_.Bind(mojo::MakeRequest(&host_proxy));
+ SetSpellCheckHostForTesting(std::move(host_proxy));
}
+ SpellCheckProvider::RequestTextChecking(text, completion);
+ base::RunLoop().RunUntilIdle();
+#else
+ SpellCheckProvider::RequestTextChecking(text, completion);
#endif
+}
+bool TestingSpellCheckProvider::Send(IPC::Message* message) {
messages_.push_back(base::WrapUnique<IPC::Message>(message));
return true;
}
+void TestingSpellCheckProvider::RequestDictionary() {}
+
+void TestingSpellCheckProvider::NotifyChecked(const base::string16& word,
+ bool misspelled) {}
+
+void TestingSpellCheckProvider::CallSpellingService(
+ const base::string16& text,
+ CallSpellingServiceCallback callback) {
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+ OnCallSpellingService(text);
+ std::move(callback).Run(true, std::vector<SpellCheckResult>());
+#else
+ NOTREACHED();
+#endif
+}
+
void TestingSpellCheckProvider::OnCallSpellingService(
- int route_id,
- int identifier,
const base::string16& text) {
-#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
- NOTREACHED();
-#else
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
++spelling_service_call_count_;
blink::WebTextCheckingCompletion* completion =
- text_check_completions_.Lookup(identifier);
+ text_check_completions_.Lookup(last_identifier_);
if (!completion) {
ResetResult();
return;
}
text_.assign(text);
- text_check_completions_.Remove(identifier);
+ text_check_completions_.Remove(last_identifier_);
std::vector<blink::WebTextCheckingResult> results;
- results.push_back(blink::WebTextCheckingResult(
- blink::kWebTextDecorationTypeSpelling, 0, 5, blink::WebString("hello")));
+ results.push_back(
+ blink::WebTextCheckingResult(blink::kWebTextDecorationTypeSpelling, 0, 5,
+ std::vector<blink::WebString>({"hello"})));
completion->DidFinishCheckingText(results);
last_request_ = text;
last_results_ = results;
+#else
+ NOTREACHED();
#endif
}
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider_test.h b/chromium/components/spellcheck/renderer/spellcheck_provider_test.h
index 89a354e1662..aa2cb77058f 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider_test.h
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider_test.h
@@ -5,19 +5,23 @@
#ifndef COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_TEST_H_
#define COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_TEST_H_
-#include <stddef.h>
-
+#include <memory>
#include <vector>
#include "base/strings/string16.h"
#include "components/spellcheck/renderer/spellcheck_provider.h"
+#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebVector.h"
#include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
#include "third_party/WebKit/public/web/WebTextCheckingResult.h"
+namespace base {
+class MessageLoop;
+}
+
namespace IPC {
- class Message;
+class Message;
}
// A fake completion object for verification.
@@ -35,17 +39,20 @@ class FakeTextCheckingCompletion : public blink::WebTextCheckingCompletion {
};
// Faked test target, which stores sent message for verification.
-class TestingSpellCheckProvider : public SpellCheckProvider {
+class TestingSpellCheckProvider : public SpellCheckProvider,
+ public spellcheck::mojom::SpellCheckHost {
public:
TestingSpellCheckProvider();
// Takes ownership of |spellcheck|.
explicit TestingSpellCheckProvider(SpellCheck* spellcheck);
~TestingSpellCheckProvider() override;
+
+ void RequestTextChecking(const base::string16& text,
+ blink::WebTextCheckingCompletion* completion);
+
bool Send(IPC::Message* message) override;
- void OnCallSpellingService(int route_id,
- int identifier,
- const base::string16& text);
+ void OnCallSpellingService(const base::string16& text);
void ResetResult();
void SetLastResults(
@@ -57,6 +64,19 @@ class TestingSpellCheckProvider : public SpellCheckProvider {
base::string16 text_;
std::vector<std::unique_ptr<IPC::Message>> messages_;
size_t spelling_service_call_count_;
+
+ private:
+ // spellcheck::mojom::SpellCheckerHost:
+ void RequestDictionary() override;
+ void NotifyChecked(const base::string16& word, bool misspelled) override;
+ void CallSpellingService(const base::string16& text,
+ CallSpellingServiceCallback callback) override;
+
+ // Message loop (if needed) to deliver the SpellCheckHost request flow.
+ std::unique_ptr<base::MessageLoop> loop_;
+
+ // Binding to receive the SpellCheckHost request flow.
+ mojo::Binding<spellcheck::mojom::SpellCheckHost> binding_;
};
// SpellCheckProvider test fixture.
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider_unittest.cc b/chromium/components/spellcheck/renderer/spellcheck_provider_unittest.cc
index 478fe21a132..02684c0b1e4 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider_unittest.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider_unittest.cc
@@ -29,8 +29,9 @@ TEST_F(SpellCheckProviderCacheTest, SubstringWithMisspellings) {
blink::WebVector<blink::WebTextCheckingResult> last_results;
std::vector<blink::WebTextCheckingResult> results;
- results.push_back(blink::WebTextCheckingResult(
- blink::kWebTextDecorationTypeSpelling, 5, 3, blink::WebString("isq")));
+ results.push_back(
+ blink::WebTextCheckingResult(blink::kWebTextDecorationTypeSpelling, 5, 3,
+ std::vector<blink::WebString>({"isq"})));
last_results.Assign(results);
provider_.SetLastResults(base::ASCIIToUTF16("This isq a test"), last_results);
EXPECT_TRUE(provider_.SatisfyRequestFromCache(
diff --git a/chromium/components/spellcheck/renderer/spellcheck_unittest.cc b/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
index ef73f2d36d1..8e14c44fa75 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
@@ -1201,9 +1201,10 @@ TEST_F(SpellCheckTest, CreateTextCheckingResultsKeepsTypographicalApostrophe) {
SpellCheckResult::SPELLING, 6, 6,
base::WideToUTF16(L"haven" TYPOGRAPHICAL_APOSTROPHE L"t")));
spellcheck_results.push_back(SpellCheckResult(
- SpellCheckResult::SPELLING, 13, 10, base::WideToUTF16(
- L"in" TYPOGRAPHICAL_APOSTROPHE L"n" TYPOGRAPHICAL_APOSTROPHE L"out"
- TYPOGRAPHICAL_APOSTROPHE L"s")));
+ SpellCheckResult::SPELLING, 13, 10,
+ base::WideToUTF16(
+ L"in" TYPOGRAPHICAL_APOSTROPHE L"n" TYPOGRAPHICAL_APOSTROPHE L"ou"
+ L"t" TYPOGRAPHICAL_APOSTROPHE L"s")));
// Replacements that differ only by apostrophe type should be ignored.
spellcheck_results.push_back(
@@ -1213,26 +1214,71 @@ TEST_F(SpellCheckTest, CreateTextCheckingResultsKeepsTypographicalApostrophe) {
SpellCheckResult(SpellCheckResult::SPELLING, 29, 4,
base::WideToUTF16(L"I" TYPOGRAPHICAL_APOSTROPHE L"ve")));
+ // If we have no suggested replacements, we should keep this misspelling.
+ spellcheck_results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 0, 5, std::vector<base::string16>()));
+
+ // If we have multiple replacements that all differ only by apostrophe type,
+ // we should ignore this misspelling.
+ spellcheck_results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 0, 11,
+ std::vector<base::string16>(
+ {base::UTF8ToUTF16("Ik've havn'"),
+ base::WideToUTF16(L"Ik" TYPOGRAPHICAL_APOSTROPHE
+ "ve havn" TYPOGRAPHICAL_APOSTROPHE)})));
+
+ // If we have multiple replacements where some only differ by apostrophe type
+ // and some don't, we should keep this misspelling, but remove the
+ // replacements that only differ by apostrophe type.
+ spellcheck_results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 0, 5,
+ std::vector<base::string16>(
+ {base::UTF8ToUTF16("I've"), base::UTF8ToUTF16("Ive"),
+ base::WideToUTF16(L"Ik" TYPOGRAPHICAL_APOSTROPHE "ve")})));
+
+ // Similar to the previous case except with the apostrophe changing from
+ // typographical to straight instead of the other direction
+ spellcheck_results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 6, 6,
+ std::vector<base::string16>({base::UTF8ToUTF16("havn't"),
+ base::UTF8ToUTF16("havnt"),
+ base::UTF8ToUTF16("haven't")})));
+
+ // If we have multiple replacements, none of which differ only by apostrophe
+ // type, we should keep this misspelling.
+ spellcheck_results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 6, 6,
+ std::vector<base::string16>(
+ {base::UTF8ToUTF16("have"), base::UTF8ToUTF16("haven't")})));
+
blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
spell_check()->CreateTextCheckingResults(SpellCheck::USE_NATIVE_CHECKER, 0,
text, spellcheck_results,
&textcheck_results);
- static const wchar_t* kExpectedReplacements[] = {
- L"I've",
- L"haven" TYPOGRAPHICAL_APOSTROPHE L"t",
- L"in'n" TYPOGRAPHICAL_APOSTROPHE L"out's",
- L"I've",
- L"haven" TYPOGRAPHICAL_APOSTROPHE L"t",
- L"in'n" TYPOGRAPHICAL_APOSTROPHE L"out" TYPOGRAPHICAL_APOSTROPHE L"s",
+ static std::vector<std::vector<const wchar_t*>> kExpectedReplacements = {
+ {L"I've"},
+ {L"haven" TYPOGRAPHICAL_APOSTROPHE L"t"},
+ {L"in'n" TYPOGRAPHICAL_APOSTROPHE L"out's"},
+ {L"I've"},
+ {L"haven" TYPOGRAPHICAL_APOSTROPHE L"t"},
+ {L"in'n" TYPOGRAPHICAL_APOSTROPHE L"out" TYPOGRAPHICAL_APOSTROPHE L"s"},
+ std::vector<const wchar_t*>(),
+ {L"I've", L"Ive"},
+ {L"havnt", L"haven" TYPOGRAPHICAL_APOSTROPHE "t"},
+ {L"have", L"haven" TYPOGRAPHICAL_APOSTROPHE "t"},
};
- ASSERT_EQ(arraysize(kExpectedReplacements), textcheck_results.size());
- for (size_t i = 0; i < arraysize(kExpectedReplacements); ++i) {
- EXPECT_EQ(base::WideToUTF16(kExpectedReplacements[i]),
- textcheck_results[i].replacement.Utf16())
- << "i=" << i << "\nactual: \""
- << textcheck_results[i].replacement.Utf16() << "\"";
+ ASSERT_EQ(kExpectedReplacements.size(), textcheck_results.size());
+ for (size_t i = 0; i < kExpectedReplacements.size(); ++i) {
+ EXPECT_EQ(kExpectedReplacements[i].size(),
+ textcheck_results[i].replacements.size());
+ for (size_t j = 0; j < kExpectedReplacements[i].size(); ++j) {
+ EXPECT_EQ(base::WideToUTF16(kExpectedReplacements[i][j]),
+ textcheck_results[i].replacements[j].Utf16())
+ << "i=" << i << "\nj=" << j << "\nactual: \""
+ << textcheck_results[i].replacements[j].Utf16() << "\"";
+ }
}
}
diff --git a/chromium/components/ssl_config/BUILD.gn b/chromium/components/ssl_config/BUILD.gn
index d514ca633d5..4f3bca7933f 100644
--- a/chromium/components/ssl_config/BUILD.gn
+++ b/chromium/components/ssl_config/BUILD.gn
@@ -31,6 +31,7 @@ source_set("unit_tests") {
"//base",
"//base/test:test_support",
"//components/prefs:test_support",
+ "//components/variations:test_support",
"//net",
"//testing/gtest",
]
diff --git a/chromium/components/ssl_config/DEPS b/chromium/components/ssl_config/DEPS
index 0fa195e2d9e..35579bbf62b 100644
--- a/chromium/components/ssl_config/DEPS
+++ b/chromium/components/ssl_config/DEPS
@@ -2,6 +2,7 @@ include_rules = [
"+components/content_settings/core/browser",
"+components/content_settings/core/common",
"+components/prefs",
+ "+components/variations",
"+net/socket",
"+net/ssl",
]
diff --git a/chromium/components/ssl_config/ssl_config_prefs.cc b/chromium/components/ssl_config/ssl_config_prefs.cc
index c2552869b5e..978ee3ae6da 100644
--- a/chromium/components/ssl_config/ssl_config_prefs.cc
+++ b/chromium/components/ssl_config/ssl_config_prefs.cc
@@ -16,6 +16,7 @@ const char kCertEnableCommonNameFallbackLocalAnchors[] =
"ssl.common_name_fallback_enabled_for_local_anchors";
const char kSSLVersionMin[] = "ssl.version_min";
const char kSSLVersionMax[] = "ssl.version_max";
+const char kTLS13Variant[] = "ssl.tls13_variant";
const char kCipherSuiteBlacklist[] = "ssl.cipher_suites.blacklist";
} // namespace prefs
diff --git a/chromium/components/ssl_config/ssl_config_prefs.h b/chromium/components/ssl_config/ssl_config_prefs.h
index 2f386524575..55121fc5af8 100644
--- a/chromium/components/ssl_config/ssl_config_prefs.h
+++ b/chromium/components/ssl_config/ssl_config_prefs.h
@@ -14,6 +14,7 @@ extern const char kCertEnableSha1LocalAnchors[];
extern const char kCertEnableCommonNameFallbackLocalAnchors[];
extern const char kSSLVersionMin[];
extern const char kSSLVersionMax[];
+extern const char kTLS13Variant[];
extern const char kCipherSuiteBlacklist[];
} // namespace prefs
diff --git a/chromium/components/ssl_config/ssl_config_service_manager_pref.cc b/chromium/components/ssl_config/ssl_config_service_manager_pref.cc
index df78e589992..41925a9a63c 100644
--- a/chromium/components/ssl_config/ssl_config_service_manager_pref.cc
+++ b/chromium/components/ssl_config/ssl_config_service_manager_pref.cc
@@ -10,9 +10,9 @@
#include <vector>
#include "base/bind.h"
-#include "base/feature_list.h"
#include "base/location.h"
#include "base/macros.h"
+#include "base/metrics/field_trial_params.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/values.h"
@@ -85,9 +85,7 @@ uint16_t SSLProtocolVersionFromString(const std::string& version_str) {
return version;
}
-const base::Feature kTLS13Feature{
- "NegotiateTLS13", base::FEATURE_DISABLED_BY_DEFAULT,
-};
+const char kTLS13VariantExperimentName[] = "TLS13Variant";
} // namespace
@@ -176,6 +174,7 @@ class SSLConfigServiceManagerPref : public ssl_config::SSLConfigServiceManager {
BooleanPrefMember common_name_fallback_local_anchors_enabled_;
StringPrefMember ssl_version_min_;
StringPrefMember ssl_version_max_;
+ StringPrefMember tls13_variant_;
// The cached list of disabled SSL cipher suites.
std::vector<uint16_t> disabled_cipher_suites_;
@@ -194,7 +193,37 @@ SSLConfigServiceManagerPref::SSLConfigServiceManagerPref(
io_task_runner_(io_task_runner) {
DCHECK(local_state);
- if (base::FeatureList::IsEnabled(kTLS13Feature)) {
+ const std::string tls13_variant =
+ base::GetFieldTrialParamValue(kTLS13VariantExperimentName, "variant");
+ if (tls13_variant == "disabled") {
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kTLS13Variant,
+ new base::Value(switches::kTLS13VariantDisabled));
+ } else if (tls13_variant == "draft") {
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kTLS13Variant,
+ new base::Value(switches::kTLS13VariantDraft));
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kSSLVersionMax,
+ new base::Value(switches::kSSLVersionTLSv13));
+ } else if (tls13_variant == "experiment") {
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kTLS13Variant,
+ new base::Value(switches::kTLS13VariantExperiment));
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kSSLVersionMax,
+ new base::Value(switches::kSSLVersionTLSv13));
+ } else if (tls13_variant == "record-type") {
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kTLS13Variant,
+ new base::Value(switches::kTLS13VariantRecordTypeExperiment));
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kSSLVersionMax,
+ new base::Value(switches::kSSLVersionTLSv13));
+ } else if (tls13_variant == "no-session-id") {
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kTLS13Variant,
+ new base::Value(switches::kTLS13VariantNoSessionIDExperiment));
local_state->SetDefaultPrefValue(
ssl_config::prefs::kSSLVersionMax,
new base::Value(switches::kSSLVersionTLSv13));
@@ -219,6 +248,8 @@ SSLConfigServiceManagerPref::SSLConfigServiceManagerPref(
local_state_callback);
ssl_version_max_.Init(ssl_config::prefs::kSSLVersionMax, local_state,
local_state_callback);
+ tls13_variant_.Init(ssl_config::prefs::kTLS13Variant, local_state,
+ local_state_callback);
local_state_change_registrar_.Init(local_state);
local_state_change_registrar_.Add(ssl_config::prefs::kCipherSuiteBlacklist,
@@ -248,6 +279,7 @@ void SSLConfigServiceManagerPref::RegisterPrefs(PrefRegistrySimple* registry) {
std::string());
registry->RegisterStringPref(ssl_config::prefs::kSSLVersionMax,
std::string());
+ registry->RegisterStringPref(ssl_config::prefs::kTLS13Variant, std::string());
registry->RegisterListPref(ssl_config::prefs::kCipherSuiteBlacklist);
}
@@ -287,6 +319,7 @@ void SSLConfigServiceManagerPref::GetSSLConfigFromPrefs(
common_name_fallback_local_anchors_enabled_.GetValue();
std::string version_min_str = ssl_version_min_.GetValue();
std::string version_max_str = ssl_version_max_.GetValue();
+ std::string tls13_variant_str = tls13_variant_.GetValue();
config->version_min = net::kDefaultSSLVersionMin;
config->version_max = net::kDefaultSSLVersionMax;
uint16_t version_min = SSLProtocolVersionFromString(version_min_str);
@@ -297,6 +330,21 @@ void SSLConfigServiceManagerPref::GetSSLConfigFromPrefs(
if (version_max && version_max >= net::SSL_PROTOCOL_VERSION_TLS1_2) {
config->version_max = version_max;
}
+
+ if (tls13_variant_str == switches::kTLS13VariantDisabled) {
+ if (config->version_max > net::SSL_PROTOCOL_VERSION_TLS1_2)
+ config->version_max = net::SSL_PROTOCOL_VERSION_TLS1_2;
+ } else if (tls13_variant_str == switches::kTLS13VariantDraft) {
+ config->tls13_variant = net::kTLS13VariantDraft;
+ } else if (tls13_variant_str == switches::kTLS13VariantExperiment) {
+ config->tls13_variant = net::kTLS13VariantExperiment;
+ } else if (tls13_variant_str == switches::kTLS13VariantRecordTypeExperiment) {
+ config->tls13_variant = net::kTLS13VariantRecordTypeExperiment;
+ } else if (tls13_variant_str ==
+ switches::kTLS13VariantNoSessionIDExperiment) {
+ config->tls13_variant = net::kTLS13VariantNoSessionIDExperiment;
+ }
+
config->disabled_cipher_suites = disabled_cipher_suites_;
}
diff --git a/chromium/components/ssl_config/ssl_config_service_manager_pref_unittest.cc b/chromium/components/ssl_config/ssl_config_service_manager_pref_unittest.cc
index bb94f68f3f0..6e8f454d5ed 100644
--- a/chromium/components/ssl_config/ssl_config_service_manager_pref_unittest.cc
+++ b/chromium/components/ssl_config/ssl_config_service_manager_pref_unittest.cc
@@ -5,18 +5,18 @@
#include <memory>
#include <utility>
-#include "base/feature_list.h"
+#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "components/prefs/testing_pref_service.h"
#include "components/ssl_config/ssl_config_prefs.h"
#include "components/ssl_config/ssl_config_service_manager.h"
#include "components/ssl_config/ssl_config_switches.h"
+#include "components/variations/variations_params_manager.h"
#include "net/ssl/ssl_config.h"
#include "net/ssl/ssl_config_service.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -146,18 +146,23 @@ TEST_F(SSLConfigServiceManagerPrefTest, NoCommandLinePrefs) {
// enabled.
EXPECT_EQ(net::kDefaultSSLVersionMin, ssl_config.version_min);
EXPECT_EQ(net::kDefaultSSLVersionMax, ssl_config.version_max);
+ EXPECT_EQ(net::kDefaultTLS13Variant, ssl_config.tls13_variant);
// The settings should not be added to the local_state.
EXPECT_FALSE(local_state.HasPrefPath(ssl_config::prefs::kSSLVersionMin));
EXPECT_FALSE(local_state.HasPrefPath(ssl_config::prefs::kSSLVersionMax));
+ EXPECT_FALSE(local_state.HasPrefPath(ssl_config::prefs::kTLS13Variant));
// Explicitly double-check the settings are not in the preference store.
std::string version_min_str;
std::string version_max_str;
+ std::string tls13_variant_str;
EXPECT_FALSE(local_state_store->GetString(ssl_config::prefs::kSSLVersionMin,
&version_min_str));
EXPECT_FALSE(local_state_store->GetString(ssl_config::prefs::kSSLVersionMax,
&version_max_str));
+ EXPECT_FALSE(local_state_store->GetString(ssl_config::prefs::kTLS13Variant,
+ &tls13_variant_str));
}
// Tests that "ssl3" is not treated as a valid minimum version.
@@ -225,11 +230,96 @@ TEST_F(SSLConfigServiceManagerPrefTest, NoTLS11Max) {
EXPECT_LE(net::SSL_PROTOCOL_VERSION_TLS1_2, ssl_config.version_max);
}
-// Tests that TLS 1.3 may be enabled via features.
-TEST_F(SSLConfigServiceManagerPrefTest, TLS13Feature) {
- // Toggle the feature.
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitFromCommandLine("NegotiateTLS13", std::string());
+// Tests that TLS 1.3 can be disabled via field trials.
+TEST_F(SSLConfigServiceManagerPrefTest, TLS13VariantFeatureDisabled) {
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "disabled"}});
+
+ TestingPrefServiceSimple local_state;
+ SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+ std::unique_ptr<SSLConfigServiceManager> config_manager(
+ SSLConfigServiceManager::CreateDefaultManager(
+ &local_state, base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+ ASSERT_TRUE(config_service.get());
+
+ SSLConfig ssl_config;
+ config_service->GetSSLConfig(&ssl_config);
+ EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_2, ssl_config.version_max);
+}
+
+// Tests that Draft TLS 1.3 can be enabled via field trials.
+TEST_F(SSLConfigServiceManagerPrefTest, TLS13VariantFeatureDraft) {
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "draft"}});
+
+ TestingPrefServiceSimple local_state;
+ SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+ std::unique_ptr<SSLConfigServiceManager> config_manager(
+ SSLConfigServiceManager::CreateDefaultManager(
+ &local_state, base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+ ASSERT_TRUE(config_service.get());
+
+ SSLConfig ssl_config;
+ config_service->GetSSLConfig(&ssl_config);
+ EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_3, ssl_config.version_max);
+ EXPECT_EQ(net::kTLS13VariantDraft, ssl_config.tls13_variant);
+}
+
+// Tests that Experiment TLS 1.3 can be enabled via field trials.
+TEST_F(SSLConfigServiceManagerPrefTest, TLS13VariantFeatureExperiment) {
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "experiment"}});
+
+ TestingPrefServiceSimple local_state;
+ SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+ std::unique_ptr<SSLConfigServiceManager> config_manager(
+ SSLConfigServiceManager::CreateDefaultManager(
+ &local_state, base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+ ASSERT_TRUE(config_service.get());
+
+ SSLConfig ssl_config;
+ config_service->GetSSLConfig(&ssl_config);
+ EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_3, ssl_config.version_max);
+ EXPECT_EQ(net::kTLS13VariantExperiment, ssl_config.tls13_variant);
+}
+
+// Tests that Record Type Experiment TLS 1.3 can be enabled via field trials.
+TEST_F(SSLConfigServiceManagerPrefTest,
+ TLS13VariantFeatureRecordTypeExperiment) {
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "record-type"}});
+
+ TestingPrefServiceSimple local_state;
+ SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+ std::unique_ptr<SSLConfigServiceManager> config_manager(
+ SSLConfigServiceManager::CreateDefaultManager(
+ &local_state, base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+ ASSERT_TRUE(config_service.get());
+
+ SSLConfig ssl_config;
+ config_service->GetSSLConfig(&ssl_config);
+ EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_3, ssl_config.version_max);
+ EXPECT_EQ(net::kTLS13VariantRecordTypeExperiment, ssl_config.tls13_variant);
+}
+
+// Tests that No SessionID Experiment TLS 1.3 can be enabled via field trials.
+TEST_F(SSLConfigServiceManagerPrefTest,
+ TLS13VariantFeatureNoSessionIDExperiment) {
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "no-session-id"}});
TestingPrefServiceSimple local_state;
SSLConfigServiceManager::RegisterPrefs(local_state.registry());
@@ -243,15 +333,17 @@ TEST_F(SSLConfigServiceManagerPrefTest, TLS13Feature) {
SSLConfig ssl_config;
config_service->GetSSLConfig(&ssl_config);
EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_3, ssl_config.version_max);
+ EXPECT_EQ(net::kTLS13VariantNoSessionIDExperiment, ssl_config.tls13_variant);
}
-// Tests that the SSLVersionMax preference overwites the TLS 1.3 feature.
+// Tests that the SSLVersionMax preference overwites the TLS 1.3 variant
+// field trial.
TEST_F(SSLConfigServiceManagerPrefTest, TLS13SSLVersionMax) {
scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
- // Toggle the feature.
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitFromCommandLine("NegotiateTLS13", std::string());
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "experiment"}});
TestingPrefServiceSimple local_state;
local_state.SetUserPref(ssl_config::prefs::kSSLVersionMax,
@@ -270,6 +362,60 @@ TEST_F(SSLConfigServiceManagerPrefTest, TLS13SSLVersionMax) {
EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_2, ssl_config.version_max);
}
+// Tests that disabling TLS 1.3 by preference overwrites the TLS 1.3 field
+// trial.
+TEST_F(SSLConfigServiceManagerPrefTest, TLS13VariantOverrideDisable) {
+ scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
+
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "experiment"}});
+
+ TestingPrefServiceSimple local_state;
+ local_state.SetUserPref(ssl_config::prefs::kTLS13Variant,
+ base::MakeUnique<base::Value>("disabled"));
+ SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+ std::unique_ptr<SSLConfigServiceManager> config_manager(
+ SSLConfigServiceManager::CreateDefaultManager(
+ &local_state, base::ThreadTaskRunnerHandle::Get()));
+ ASSERT_TRUE(config_manager.get());
+ scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+ ASSERT_TRUE(config_service.get());
+
+ SSLConfig ssl_config;
+ config_service->GetSSLConfig(&ssl_config);
+ EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_2, ssl_config.version_max);
+}
+
+// Tests that enabling TLS 1.3 by preference overwrites the TLS 1.3 field trial.
+TEST_F(SSLConfigServiceManagerPrefTest, TLS13VariantOverrideEnable) {
+ scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
+
+ // Toggle the field trial.
+ variations::testing::VariationParamsManager variation_params(
+ "TLS13Variant", {{"variant", "disabled"}});
+
+ TestingPrefServiceSimple local_state;
+ local_state.SetUserPref(ssl_config::prefs::kSSLVersionMax,
+ base::MakeUnique<base::Value>("tls1.3"));
+ local_state.SetUserPref(ssl_config::prefs::kTLS13Variant,
+ base::MakeUnique<base::Value>("experiment"));
+ SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+ std::unique_ptr<SSLConfigServiceManager> config_manager(
+ SSLConfigServiceManager::CreateDefaultManager(
+ &local_state, base::ThreadTaskRunnerHandle::Get()));
+ ASSERT_TRUE(config_manager.get());
+ scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+ ASSERT_TRUE(config_service.get());
+
+ SSLConfig ssl_config;
+ config_service->GetSSLConfig(&ssl_config);
+ EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_3, ssl_config.version_max);
+ EXPECT_EQ(net::kTLS13VariantExperiment, ssl_config.tls13_variant);
+}
+
// Tests that SHA-1 signatures for local trust anchors can be enabled.
TEST_F(SSLConfigServiceManagerPrefTest, SHA1ForLocalAnchors) {
scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
diff --git a/chromium/components/ssl_config/ssl_config_switches.cc b/chromium/components/ssl_config/ssl_config_switches.cc
index 2152f87e7bb..b4e6456b147 100644
--- a/chromium/components/ssl_config/ssl_config_switches.cc
+++ b/chromium/components/ssl_config/ssl_config_switches.cc
@@ -14,6 +14,9 @@ const char kSSLVersionMax[] = "ssl-version-max";
// "tls1.3").
const char kSSLVersionMin[] = "ssl-version-min";
+// Specifies the enabled TLS 1.3 variant ("disabled", "draft", "experiment").
+const char kTLS13Variant[] = "tls13-variant";
+
// These values aren't switches, but rather the values that kSSLVersionMax and
// kSSLVersionMin can have.
const char kSSLVersionTLSv1[] = "tls1";
@@ -21,4 +24,10 @@ const char kSSLVersionTLSv11[] = "tls1.1";
const char kSSLVersionTLSv12[] = "tls1.2";
const char kSSLVersionTLSv13[] = "tls1.3";
+const char kTLS13VariantDisabled[] = "disabled";
+const char kTLS13VariantDraft[] = "draft";
+const char kTLS13VariantExperiment[] = "experiment";
+const char kTLS13VariantRecordTypeExperiment[] = "record-type";
+const char kTLS13VariantNoSessionIDExperiment[] = "no-session-id";
+
} // namespace switches
diff --git a/chromium/components/ssl_config/ssl_config_switches.h b/chromium/components/ssl_config/ssl_config_switches.h
index 34c7d24ca67..6ea39034b44 100644
--- a/chromium/components/ssl_config/ssl_config_switches.h
+++ b/chromium/components/ssl_config/ssl_config_switches.h
@@ -9,10 +9,16 @@ namespace switches {
extern const char kSSLVersionMax[];
extern const char kSSLVersionMin[];
+extern const char kTLS13Variant[];
extern const char kSSLVersionTLSv1[];
extern const char kSSLVersionTLSv11[];
extern const char kSSLVersionTLSv12[];
extern const char kSSLVersionTLSv13[];
+extern const char kTLS13VariantDisabled[];
+extern const char kTLS13VariantDraft[];
+extern const char kTLS13VariantExperiment[];
+extern const char kTLS13VariantRecordTypeExperiment[];
+extern const char kTLS13VariantNoSessionIDExperiment[];
} // namespace switches
diff --git a/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.cc b/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.cc
index 2edb795765b..236442328b5 100644
--- a/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.cc
+++ b/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.cc
@@ -16,7 +16,6 @@ StartupMetricHostImpl::~StartupMetricHostImpl() = default;
// static
void StartupMetricHostImpl::Create(
- const service_manager::BindSourceInfo& source_info,
mojom::StartupMetricHostRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<StartupMetricHostImpl>(),
std::move(request));
diff --git a/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.h b/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.h
index cba0d723a4a..6901e13bf56 100644
--- a/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.h
+++ b/chromium/components/startup_metric_utils/browser/startup_metric_host_impl.h
@@ -11,10 +11,6 @@
#include "base/time/time.h"
#include "components/startup_metric_utils/common/startup_metric.mojom.h"
-namespace service_manager {
-struct BindSourceInfo;
-}
-
namespace startup_metric_utils {
class StartupMetricHostImpl : public mojom::StartupMetricHost {
@@ -22,8 +18,7 @@ class StartupMetricHostImpl : public mojom::StartupMetricHost {
StartupMetricHostImpl();
~StartupMetricHostImpl() override;
- static void Create(const service_manager::BindSourceInfo& source_info,
- mojom::StartupMetricHostRequest request);
+ static void Create(mojom::StartupMetricHostRequest request);
private:
void RecordRendererMainEntryTime(
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 8d9e4669e7c..f55f8907e00 100644
--- a/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc
+++ b/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -718,9 +718,11 @@ void RecordFirstWebContentsMainFrameLoad(base::TimeTicks ticks) {
g_process_creation_ticks.Get(), ticks);
}
-void RecordFirstWebContentsNonEmptyPaint(base::TimeTicks ticks) {
+void RecordFirstWebContentsNonEmptyPaint(
+ base::TimeTicks now,
+ base::TimeTicks render_process_host_init_time) {
static bool is_first_call = true;
- if (!is_first_call || ticks.is_null())
+ if (!is_first_call || now.is_null())
return;
is_first_call = false;
@@ -735,11 +737,16 @@ void RecordFirstWebContentsNonEmptyPaint(base::TimeTicks ticks) {
metrics::CallStackProfileMetricsProvider::FIRST_NONEMPTY_PAINT);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100, "Startup.FirstWebContents.NonEmptyPaint2",
- g_process_creation_ticks.Get(), ticks);
+ g_process_creation_ticks.Get(), now);
UMA_HISTOGRAM_WITH_TEMPERATURE(
UMA_HISTOGRAM_LONG_TIMES_100,
"Startup.BrowserMessageLoopStart.To.NonEmptyPaint2",
- ticks - g_message_loop_start_ticks.Get());
+ now - g_message_loop_start_ticks.Get());
+
+ UMA_HISTOGRAM_WITH_TEMPERATURE(
+ UMA_HISTOGRAM_LONG_TIMES_100,
+ "Startup.FirstWebContents.RenderProcessHostInit.ToNonEmptyPaint",
+ now - render_process_host_init_time);
}
void RecordFirstWebContentsMainNavigationStart(base::TimeTicks ticks,
diff --git a/chromium/components/startup_metric_utils/browser/startup_metric_utils.h b/chromium/components/startup_metric_utils/browser/startup_metric_utils.h
index 2e6fd0d3108..6066d716056 100644
--- a/chromium/components/startup_metric_utils/browser/startup_metric_utils.h
+++ b/chromium/components/startup_metric_utils/browser/startup_metric_utils.h
@@ -83,8 +83,10 @@ void RecordRendererMainEntryTime(base::TimeTicks ticks);
void RecordFirstWebContentsMainFrameLoad(base::TimeTicks ticks);
// Call this with the time when the first web contents had a non-empty paint,
-// only if the first web contents was unimpended in its attempt to do so.
-void RecordFirstWebContentsNonEmptyPaint(base::TimeTicks ticks);
+// only if the first web contents was unimpeded in its attempt to do so.
+void RecordFirstWebContentsNonEmptyPaint(
+ base::TimeTicks now,
+ base::TimeTicks render_process_host_init_time);
// Call this with the time when the first web contents began navigating its main
// frame. Adds a suffix to its metrics according to |workload|.
diff --git a/chromium/components/storage_monitor/BUILD.gn b/chromium/components/storage_monitor/BUILD.gn
index c8559d2c99d..6c565d3f65f 100644
--- a/chromium/components/storage_monitor/BUILD.gn
+++ b/chromium/components/storage_monitor/BUILD.gn
@@ -140,6 +140,7 @@ source_set("unit_tests") {
deps = [
":test_support",
+ "//base/test:test_support",
"//content/test:test_support",
"//testing/gtest",
]
diff --git a/chromium/components/storage_monitor/OWNERS b/chromium/components/storage_monitor/OWNERS
index 4cbc8a41099..ed5b39e3398 100644
--- a/chromium/components/storage_monitor/OWNERS
+++ b/chromium/components/storage_monitor/OWNERS
@@ -1,3 +1,2 @@
thestig@chromium.org
-vandebo@chromium.org
-gbillock@chromium.org
+tommycli@chromium.org
diff --git a/chromium/components/storage_monitor/image_capture_device.h b/chromium/components/storage_monitor/image_capture_device.h
index 55dc1fe5613..15246efdd40 100644
--- a/chromium/components/storage_monitor/image_capture_device.h
+++ b/chromium/components/storage_monitor/image_capture_device.h
@@ -10,7 +10,6 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
-#include "base/mac/cocoa_protocols.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/memory/ref_counted.h"
diff --git a/chromium/components/storage_monitor/mtab_watcher_linux.cc b/chromium/components/storage_monitor/mtab_watcher_linux.cc
index 2de7369d0bc..31ad9c12fa9 100644
--- a/chromium/components/storage_monitor/mtab_watcher_linux.cc
+++ b/chromium/components/storage_monitor/mtab_watcher_linux.cc
@@ -12,7 +12,7 @@
#include "base/bind.h"
#include "base/macros.h"
-#include "content/public/browser/browser_thread.h"
+#include "base/threading/thread_restrictions.h"
namespace {
@@ -36,11 +36,10 @@ const char* const kKnownFileSystems[] = {
namespace storage_monitor {
MtabWatcherLinux::MtabWatcherLinux(const base::FilePath& mtab_path,
- base::WeakPtr<Delegate> delegate)
- : mtab_path_(mtab_path),
- delegate_(delegate),
- weak_ptr_factory_(this) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+ const UpdateMtabCallback& callback)
+ : mtab_path_(mtab_path), callback_(callback), weak_ptr_factory_(this) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ base::ThreadRestrictions::AssertIOAllowed();
bool ret = file_watcher_.Watch(
mtab_path_, false,
base::Bind(&MtabWatcherLinux::OnFilePathChanged,
@@ -54,11 +53,13 @@ MtabWatcherLinux::MtabWatcherLinux(const base::FilePath& mtab_path,
}
MtabWatcherLinux::~MtabWatcherLinux() {
- DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ base::ThreadRestrictions::AssertIOAllowed();
}
void MtabWatcherLinux::ReadMtab() const {
- DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ base::ThreadRestrictions::AssertIOAllowed();
FILE* fp = setmntent(mtab_path_.value().c_str(), "r");
if (!fp)
@@ -82,14 +83,13 @@ void MtabWatcherLinux::ReadMtab() const {
}
endmntent(fp);
- content::BrowserThread::PostTask(
- content::BrowserThread::UI, FROM_HERE,
- base::Bind(&Delegate::UpdateMtab, delegate_, device_map));
+ callback_.Run(device_map);
}
void MtabWatcherLinux::OnFilePathChanged(
const base::FilePath& path, bool error) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ base::ThreadRestrictions::AssertIOAllowed();
if (path != mtab_path_) {
// This cannot happen unless FilePathWatcher is buggy. Just ignore this
diff --git a/chromium/components/storage_monitor/mtab_watcher_linux.h b/chromium/components/storage_monitor/mtab_watcher_linux.h
index 0a39fe3d743..8e779219870 100644
--- a/chromium/components/storage_monitor/mtab_watcher_linux.h
+++ b/chromium/components/storage_monitor/mtab_watcher_linux.h
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// MtabWatcherLinux listens for mount point changes from a mtab file and
-// notifies a StorageMonitorLinux about them.
-// MtabWatcherLinux lives on the FILE thread.
-
#ifndef COMPONENTS_STORAGE_MONITOR_MTAB_WATCHER_LINUX_H_
#define COMPONENTS_STORAGE_MONITOR_MTAB_WATCHER_LINUX_H_
@@ -19,27 +15,28 @@
#include "base/files/file_path_watcher.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
#include "build/build_config.h"
namespace storage_monitor {
+// MtabWatcherLinux listens for mount point changes from a mtab file and
+// notifies a StorageMonitorLinux about them. This class should be created and
+// destroyed on a single sequence suitable for file IO.
class MtabWatcherLinux {
public:
// (mount point, mount device)
// A mapping from mount point to mount device, as extracted from the mtab
// file.
- typedef std::map<base::FilePath, base::FilePath> MountPointDeviceMap;
+ using MountPointDeviceMap = std::map<base::FilePath, base::FilePath>;
- class Delegate {
- public:
- virtual ~Delegate() {}
-
- // Parses |new_mtab| and find all changes. Called on the UI thread.
- virtual void UpdateMtab(const MountPointDeviceMap& new_mtab) = 0;
- };
+ using UpdateMtabCallback =
+ base::Callback<void(const MountPointDeviceMap& new_mtab)>;
+ // |callback| is called on the same sequence as the rest of the class.
+ // Caller is responsible for bouncing to the correct sequence.
MtabWatcherLinux(const base::FilePath& mtab_path,
- base::WeakPtr<Delegate> delegate);
+ const UpdateMtabCallback& callback);
~MtabWatcherLinux();
private:
@@ -55,7 +52,9 @@ class MtabWatcherLinux {
// Watcher for |mtab_path_|.
base::FilePathWatcher file_watcher_;
- base::WeakPtr<Delegate> delegate_;
+ UpdateMtabCallback callback_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<MtabWatcherLinux> weak_ptr_factory_;
diff --git a/chromium/components/storage_monitor/portable_device_watcher_win.cc b/chromium/components/storage_monitor/portable_device_watcher_win.cc
index 62a08a0c39a..d6911e2c655 100644
--- a/chromium/components/storage_monitor/portable_device_watcher_win.cc
+++ b/chromium/components/storage_monitor/portable_device_watcher_win.cc
@@ -17,7 +17,8 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_restrictions.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_propvariant.h"
@@ -32,9 +33,6 @@ namespace {
// Name of the client application that communicates with the MTP device.
const base::char16 kClientName[] = L"Chromium";
-// Name of the sequenced task runner.
-const char kMediaTaskRunnerName[] = "media-task-runner";
-
// Returns true if |data| represents a class of portable devices.
bool IsPortableDeviceStructure(LPARAM data) {
DEV_BROADCAST_HDR* broadcast_hdr =
@@ -317,8 +315,8 @@ bool IsMassStoragePortableDevice(const base::string16& pnp_device_id,
base::string16 GetDeviceNameOnBlockingThread(
IPortableDeviceManager* portable_device_manager,
const base::string16& pnp_device_id) {
- DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
DCHECK(portable_device_manager);
+ base::ThreadRestrictions::AssertIOAllowed();
base::string16 name;
GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
GetDeviceDescription(pnp_device_id, portable_device_manager, &name) ||
@@ -331,8 +329,8 @@ base::string16 GetDeviceNameOnBlockingThread(
bool GetDeviceStorageObjectsOnBlockingThread(
const base::string16& pnp_device_id,
PortableDeviceWatcherWin::StorageObjects* storage_objects) {
- DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
DCHECK(storage_objects);
+ base::ThreadRestrictions::AssertIOAllowed();
base::win::ScopedComPtr<IPortableDevice> device;
if (!SetUp(pnp_device_id, &device))
return false;
@@ -369,10 +367,10 @@ bool GetDeviceInfoOnBlockingThread(
IPortableDeviceManager* portable_device_manager,
const base::string16& pnp_device_id,
PortableDeviceWatcherWin::DeviceDetails* device_details) {
- DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
DCHECK(portable_device_manager);
DCHECK(device_details);
DCHECK(!pnp_device_id.empty());
+ base::ThreadRestrictions::AssertIOAllowed();
device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager,
pnp_device_id);
if (IsMassStoragePortableDevice(pnp_device_id, device_details->name))
@@ -388,7 +386,7 @@ bool GetDeviceInfoOnBlockingThread(
// returns true and fills in |portable_device_mgr|. On failure, returns false.
bool GetPortableDeviceManager(
base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) {
- DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
HRESULT hr = ::CoCreateInstance(
__uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(portable_device_mgr->GetAddressOf()));
@@ -406,8 +404,8 @@ bool GetPortableDeviceManager(
// false.
bool EnumerateAttachedDevicesOnBlockingThread(
PortableDeviceWatcherWin::Devices* devices) {
- DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
DCHECK(devices);
+ base::ThreadRestrictions::AssertIOAllowed();
base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
if (!GetPortableDeviceManager(&portable_device_mgr))
return false;
@@ -441,8 +439,8 @@ bool EnumerateAttachedDevicesOnBlockingThread(
bool HandleDeviceAttachedEventOnBlockingThread(
const base::string16& pnp_device_id,
PortableDeviceWatcherWin::DeviceDetails* device_details) {
- DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
DCHECK(device_details);
+ base::ThreadRestrictions::AssertIOAllowed();
base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
if (!GetPortableDeviceManager(&portable_device_mgr))
return false;
@@ -503,10 +501,9 @@ PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
void PortableDeviceWatcherWin::Init(HWND hwnd) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
notifications_ = RegisterPortableDeviceNotification(hwnd);
- base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
- media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
- pool->GetNamedSequenceToken(kMediaTaskRunnerName),
- base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
+ media_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
EnumerateAttachedDevices();
}
diff --git a/chromium/components/storage_monitor/storage_monitor.cc b/chromium/components/storage_monitor/storage_monitor.cc
index beb45fd2c33..85c37e5df0c 100644
--- a/chromium/components/storage_monitor/storage_monitor.cc
+++ b/chromium/components/storage_monitor/storage_monitor.cc
@@ -84,7 +84,7 @@ std::vector<StorageInfo> StorageMonitor::GetAllAvailableStorages() const {
}
void StorageMonitor::EnsureInitialized(base::Closure callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (initialized_) {
if (!callback.is_null())
callback.Run();
diff --git a/chromium/components/storage_monitor/storage_monitor.h b/chromium/components/storage_monitor/storage_monitor.h
index 5c58581d605..ade2f2e5d80 100644
--- a/chromium/components/storage_monitor/storage_monitor.h
+++ b/chromium/components/storage_monitor/storage_monitor.h
@@ -13,9 +13,9 @@
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/observer_list_threadsafe.h"
+#include "base/sequence_checker.h"
#include "base/strings/string16.h"
#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "components/storage_monitor/storage_info.h"
@@ -93,7 +93,7 @@ class StorageMonitor {
// stack. Before the callback is run, calls to |GetAllAvailableStorages| and
// |GetStorageInfoForPath| may not return the correct results. In addition,
// registered observers will not be notified on device attachment/detachment.
- // Should be invoked on the UI thread; callbacks will be run on the UI thread.
+ // Callbacks will run on the same sequence as the rest of the class.
void EnsureInitialized(base::Closure callback);
// Return true if the storage monitor has already been initialized.
@@ -152,8 +152,7 @@ class StorageMonitor {
// Called to initialize the storage monitor.
virtual void Init() = 0;
- // Called by subclasses to mark the storage monitor as
- // fully initialized. Must be called on the UI thread.
+ // Called by subclasses to mark the storage monitor as fully initialized.
void MarkInitialized();
private:
@@ -174,8 +173,8 @@ class StorageMonitor {
scoped_refptr<base::ObserverListThreadSafe<RemovableStorageObserver>>
observer_list_;
- // Used to make sure we call initialize from the same thread as creation.
- base::ThreadChecker thread_checker_;
+ // Used to make sure we call initialize from the same sequence as creation.
+ SEQUENCE_CHECKER(sequence_checker_);
bool initializing_;
bool initialized_;
diff --git a/chromium/components/storage_monitor/storage_monitor_linux.cc b/chromium/components/storage_monitor/storage_monitor_linux.cc
index f0f53d70182..b7e335e1548 100644
--- a/chromium/components/storage_monitor/storage_monitor_linux.cc
+++ b/chromium/components/storage_monitor/storage_monitor_linux.cc
@@ -24,6 +24,10 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_restrictions.h"
#include "components/storage_monitor/media_storage_util.h"
#include "components/storage_monitor/removable_device_constants.h"
#include "components/storage_monitor/storage_info.h"
@@ -31,8 +35,6 @@
#include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
#include "device/udev_linux/scoped_udev.h"
-using content::BrowserThread;
-
namespace storage_monitor {
typedef MtabWatcherLinux::MountPointDeviceMap MountPointDeviceMap;
@@ -111,7 +113,7 @@ uint64_t GetDeviceStorageSize(const base::FilePath& device_path,
// Gets the device information using udev library.
std::unique_ptr<StorageInfo> GetDeviceInfo(const base::FilePath& device_path,
const base::FilePath& mount_point) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ base::ThreadRestrictions::AssertIOAllowed();
DCHECK(!device_path.empty());
std::unique_ptr<StorageInfo> storage_info;
@@ -179,18 +181,30 @@ std::unique_ptr<StorageInfo> GetDeviceInfo(const base::FilePath& device_path,
return storage_info;
}
-MtabWatcherLinux* CreateMtabWatcherLinuxOnFileThread(
+// Runs |callback| with the |new_mtab| on |storage_monitor_task_runner|.
+void BounceMtabUpdateToStorageMonitorTaskRunner(
+ scoped_refptr<base::SequencedTaskRunner> storage_monitor_task_runner,
+ const MtabWatcherLinux::UpdateMtabCallback& callback,
+ const MtabWatcherLinux::MountPointDeviceMap& new_mtab) {
+ storage_monitor_task_runner->PostTask(FROM_HERE,
+ base::Bind(callback, new_mtab));
+}
+
+MtabWatcherLinux* CreateMtabWatcherLinuxOnMtabWatcherTaskRunner(
const base::FilePath& mtab_path,
- base::WeakPtr<MtabWatcherLinux::Delegate> delegate) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ scoped_refptr<base::SequencedTaskRunner> storage_monitor_task_runner,
+ const MtabWatcherLinux::UpdateMtabCallback& callback) {
+ base::ThreadRestrictions::AssertIOAllowed();
// Owned by caller.
- return new MtabWatcherLinux(mtab_path, delegate);
+ return new MtabWatcherLinux(
+ mtab_path, base::Bind(&BounceMtabUpdateToStorageMonitorTaskRunner,
+ storage_monitor_task_runner, callback));
}
-StorageMonitor::EjectStatus EjectPathOnFileThread(
+StorageMonitor::EjectStatus EjectPathOnBlockingTaskRunner(
const base::FilePath& path,
const base::FilePath& device) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ base::ThreadRestrictions::AssertIOAllowed();
// Note: Linux LSB says umount should exist in /bin.
static const char kUmountBinary[] = "/bin/umount";
@@ -227,22 +241,25 @@ StorageMonitor::EjectStatus EjectPathOnFileThread(
StorageMonitorLinux::StorageMonitorLinux(const base::FilePath& path)
: mtab_path_(path),
get_device_info_callback_(base::Bind(&GetDeviceInfo)),
- weak_ptr_factory_(this) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-}
+ mtab_watcher_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND})),
+ weak_ptr_factory_(this) {}
StorageMonitorLinux::~StorageMonitorLinux() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ mtab_watcher_task_runner_->DeleteSoon(FROM_HERE, mtab_watcher_.release());
}
void StorageMonitorLinux::Init() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!mtab_path_.empty());
- BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&CreateMtabWatcherLinuxOnFileThread,
- mtab_path_,
- weak_ptr_factory_.GetWeakPtr()),
+ base::PostTaskAndReplyWithResult(
+ mtab_watcher_task_runner_.get(), FROM_HERE,
+ base::Bind(&CreateMtabWatcherLinuxOnMtabWatcherTaskRunner, mtab_path_,
+ base::SequencedTaskRunnerHandle::Get(),
+ base::Bind(&StorageMonitorLinux::UpdateMtab,
+ weak_ptr_factory_.GetWeakPtr())),
base::Bind(&StorageMonitorLinux::OnMtabWatcherCreated,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -250,8 +267,8 @@ void StorageMonitorLinux::Init() {
bool StorageMonitorLinux::GetStorageInfoForPath(
const base::FilePath& path,
StorageInfo* device_info) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(device_info);
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!path.IsAbsolute())
return false;
@@ -270,12 +287,14 @@ bool StorageMonitorLinux::GetStorageInfoForPath(
void StorageMonitorLinux::SetGetDeviceInfoCallbackForTest(
const GetDeviceInfoCallback& get_device_info_callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
get_device_info_callback_ = get_device_info_callback;
}
void StorageMonitorLinux::EjectDevice(
const std::string& device_id,
base::Callback<void(EjectStatus)> callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
StorageInfo::Type type;
if (!StorageInfo::CrackDeviceId(device_id, &type, NULL)) {
callback.Run(EJECT_FAILURE);
@@ -304,20 +323,18 @@ void StorageMonitorLinux::EjectDevice(
receiver()->ProcessDetach(device_id);
- BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&EjectPathOnFileThread, path, device),
- callback);
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&EjectPathOnBlockingTaskRunner, path, device), callback);
}
void StorageMonitorLinux::OnMtabWatcherCreated(MtabWatcherLinux* watcher) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mtab_watcher_.reset(watcher);
}
void StorageMonitorLinux::UpdateMtab(const MountPointDeviceMap& new_mtab) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Check existing mtab entries for unaccounted mount points.
// These mount points must have been removed in the new mtab.
std::list<base::FilePath> mount_points_to_erase;
@@ -380,6 +397,9 @@ void StorageMonitorLinux::UpdateMtab(const MountPointDeviceMap& new_mtab) {
}
// Check new mtab entries against existing ones.
+ scoped_refptr<base::SequencedTaskRunner> mounting_task_runner =
+ base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND});
for (MountPointDeviceMap::const_iterator new_iter = new_mtab.begin();
new_iter != new_mtab.end(); ++new_iter) {
const base::FilePath& mount_point = new_iter->first;
@@ -392,24 +412,23 @@ void StorageMonitorLinux::UpdateMtab(const MountPointDeviceMap& new_mtab) {
if (IsDeviceAlreadyMounted(mount_device)) {
HandleDeviceMountedMultipleTimes(mount_device, mount_point);
} else {
- BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::FILE, FROM_HERE,
+ base::PostTaskAndReplyWithResult(
+ mounting_task_runner.get(), FROM_HERE,
base::Bind(get_device_info_callback_, mount_device, mount_point),
base::Bind(&StorageMonitorLinux::AddNewMount,
- weak_ptr_factory_.GetWeakPtr(),
- mount_device));
+ weak_ptr_factory_.GetWeakPtr(), mount_device));
}
}
}
- // Note: relies on scheduled tasks on the file thread being sequential. This
- // block needs to follow the for loop, so that the DoNothing call on the FILE
- // thread happens after the scheduled metadata retrievals, meaning that the
- // reply callback will then happen after all the AddNewMount calls.
+ // Note: Relies on scheduled tasks on the |mounting_task_runner| being
+ // sequential. This block needs to follow the for loop, so that the DoNothing
+ // call on the |mounting_task_runner| happens after the scheduled metadata
+ // retrievals, meaning that the reply callback will then happen after all the
+ // AddNewMount calls.
if (!IsInitialized()) {
- BrowserThread::PostTaskAndReply(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&base::DoNothing),
+ mounting_task_runner->PostTaskAndReply(
+ FROM_HERE, base::Bind(&base::DoNothing),
base::Bind(&StorageMonitorLinux::MarkInitialized,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -417,14 +436,14 @@ void StorageMonitorLinux::UpdateMtab(const MountPointDeviceMap& new_mtab) {
bool StorageMonitorLinux::IsDeviceAlreadyMounted(
const base::FilePath& mount_device) const {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::ContainsKey(mount_priority_map_, mount_device);
}
void StorageMonitorLinux::HandleDeviceMountedMultipleTimes(
const base::FilePath& mount_device,
const base::FilePath& mount_point) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
MountPriorityMap::iterator priority = mount_priority_map_.find(mount_device);
DCHECK(priority != mount_priority_map_.end());
@@ -437,7 +456,7 @@ void StorageMonitorLinux::HandleDeviceMountedMultipleTimes(
void StorageMonitorLinux::AddNewMount(
const base::FilePath& mount_device,
std::unique_ptr<StorageInfo> storage_info) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!storage_info)
return;
diff --git a/chromium/components/storage_monitor/storage_monitor_linux.h b/chromium/components/storage_monitor/storage_monitor_linux.h
index a007bf9c7a6..bf38ebbb864 100644
--- a/chromium/components/storage_monitor/storage_monitor_linux.h
+++ b/chromium/components/storage_monitor/storage_monitor_linux.h
@@ -4,9 +4,8 @@
// StorageMonitorLinux processes mount point change events, notifies listeners
// about the addition and deletion of media devices, and answers queries about
-// mounted devices.
-// StorageMonitorLinux lives on the UI thread, and uses a MtabWatcherLinux on
-// the FILE thread to get mount point change events.
+// mounted devices. StorageMonitorLinux uses a MtabWatcherLinux on a separate
+// background sequence that is file IO capable.
#ifndef COMPONENTS_STORAGE_MONITOR_STORAGE_MONITOR_LINUX_H_
#define COMPONENTS_STORAGE_MONITOR_STORAGE_MONITOR_LINUX_H_
@@ -23,16 +22,20 @@
#include "base/files/file_path.h"
#include "base/files/file_path_watcher.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
#include "build/build_config.h"
#include "components/storage_monitor/mtab_watcher_linux.h"
#include "components/storage_monitor/storage_monitor.h"
-#include "content/public/browser/browser_thread.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
namespace storage_monitor {
-class StorageMonitorLinux : public StorageMonitor,
- public MtabWatcherLinux::Delegate {
+class StorageMonitorLinux : public StorageMonitor {
public:
// Should only be called by browser start up code.
// Use StorageMonitor::GetInstance() instead.
@@ -52,9 +55,9 @@ class StorageMonitorLinux : public StorageMonitor,
void SetGetDeviceInfoCallbackForTest(
const GetDeviceInfoCallback& get_device_info_callback);
- // MtabWatcherLinux::Delegate implementation.
- void UpdateMtab(
- const MtabWatcherLinux::MountPointDeviceMap& new_mtab) override;
+ // Parses |new_mtab| and find all changes.
+ virtual void UpdateMtab(
+ const MtabWatcherLinux::MountPointDeviceMap& new_mtab);
private:
// Structure to save mounted device information such as device path, unique
@@ -64,14 +67,6 @@ class StorageMonitorLinux : public StorageMonitor,
StorageInfo storage_info;
};
- // For use with std::unique_ptr.
- struct MtabWatcherLinuxDeleter {
- void operator()(MtabWatcherLinux* mtab_watcher) {
- content::BrowserThread::DeleteSoon(content::BrowserThread::FILE,
- FROM_HERE, mtab_watcher);
- }
- };
-
// Mapping of mount points to MountPointInfo.
typedef std::map<base::FilePath, MountPointInfo> MountMap;
@@ -123,7 +118,12 @@ class StorageMonitorLinux : public StorageMonitor,
// points.
MountPriorityMap mount_priority_map_;
- std::unique_ptr<MtabWatcherLinux, MtabWatcherLinuxDeleter> mtab_watcher_;
+ // Must be created and destroyed on |mtab_watcher_task_runner_|.
+ std::unique_ptr<MtabWatcherLinux> mtab_watcher_;
+
+ scoped_refptr<base::SequencedTaskRunner> mtab_watcher_task_runner_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<StorageMonitorLinux> weak_ptr_factory_;
diff --git a/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc b/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc
index 7c3b223c87b..15118afb15a 100644
--- a/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc
+++ b/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc
@@ -21,13 +21,14 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/task_scheduler.h"
+#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/storage_monitor/mock_removable_storage_observer.h"
#include "components/storage_monitor/removable_device_constants.h"
#include "components/storage_monitor/storage_info.h"
#include "components/storage_monitor/storage_monitor.h"
#include "components/storage_monitor/test_storage_monitor.h"
-#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace storage_monitor {
@@ -134,6 +135,13 @@ class TestStorageMonitorLinux : public StorageMonitorLinux {
void UpdateMtab(
const MtabWatcherLinux::MountPointDeviceMap& new_mtab) override {
StorageMonitorLinux::UpdateMtab(new_mtab);
+
+ // The UpdateMtab call performs the actual mounting by posting tasks
+ // to the task scheduler. This also needs to be flushed.
+ base::TaskScheduler::GetInstance()->FlushForTesting();
+
+ // Once the storage monitor picks up the changes to the fake mtab file,
+ // exit the RunLoop that should be blocking the main test thread.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
}
@@ -157,8 +165,7 @@ class StorageMonitorLinuxTest : public testing::Test {
const std::string mount_type;
};
- StorageMonitorLinuxTest()
- : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
+ StorageMonitorLinuxTest() {}
~StorageMonitorLinuxTest() override {}
protected:
@@ -182,13 +189,13 @@ class StorageMonitorLinuxTest : public testing::Test {
monitor_->AddObserver(mock_storage_observer_.get());
monitor_->Init();
- base::RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
}
void TearDown() override {
- base::RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
monitor_->RemoveObserver(mock_storage_observer_.get());
- base::RunLoop().RunUntilIdle();
+ scoped_task_environment_.RunUntilIdle();
// Linux storage monitor must be destroyed on the UI thread, so do it here.
monitor_.reset();
@@ -198,6 +205,7 @@ class StorageMonitorLinuxTest : public testing::Test {
// file, and run the message loop.
void AppendToMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
WriteToMtab(data, data_size, false /* do not overwrite */);
+ // Block until the mtab changes are detected by the file watcher.
base::RunLoop().Run();
}
@@ -205,6 +213,7 @@ class StorageMonitorLinuxTest : public testing::Test {
// |data_size|, and run the message loop.
void OverwriteMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
WriteToMtab(data, data_size, true /* overwrite */);
+ // Block until the mtab changes are detected by the file watcher.
base::RunLoop().Run();
}
@@ -301,7 +310,7 @@ class StorageMonitorLinuxTest : public testing::Test {
ASSERT_EQ(1, endmntent(file));
}
- content::TestBrowserThreadBundle thread_bundle_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<MockRemovableStorageObserver> mock_storage_observer_;
diff --git a/chromium/components/strings/components_chromium_strings_mr.xtb b/chromium/components/strings/components_chromium_strings_mr.xtb
index 2bd19779f1e..f3e7061d2ff 100644
--- a/chromium/components/strings/components_chromium_strings_mr.xtb
+++ b/chromium/components/strings/components_chromium_strings_mr.xtb
@@ -34,7 +34,7 @@
हे समस्‍येचे निराकरण करीत नसल्‍यास, आम्‍ही
सुधारित कार्यप्रदर्शनासाठी पुन्हा
हा पर्याय निवडण्याची शिफारस करतो.</translation>
-<translation id="8187289872471304532">अनुप्रयोग &gt; सिस्टीम प्राधान्ये &gt; नेटवर्क &gt; प्रगत &gt; प्रॉक्सी
+<translation id="8187289872471304532">अॅप्लिकेशन &gt; सिस्टम प्राधान्ये &gt; नेटवर्क &gt; प्रगत &gt; प्रॉक्सी
वर जा आणि निवडलेल्या कोणत्याही प्रॉक्सींची निवड रद्द करा.</translation>
<translation id="8684913864886094367">Chromium योग्यरित्या बंद केले नव्‍हते.</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_google_chrome_strings_mr.xtb b/chromium/components/strings/components_google_chrome_strings_mr.xtb
index 0e0ff230e79..488a54bfeae 100644
--- a/chromium/components/strings/components_google_chrome_strings_mr.xtb
+++ b/chromium/components/strings/components_google_chrome_strings_mr.xtb
@@ -34,6 +34,6 @@
&gt;
LAN सेटिंग्ज
वर जा आणि "आपल्या LAN साठी एक प्रॉक्सी सर्व्हर वापरा" निवड रद्द करा.</translation>
-<translation id="8187289872471304532">अनुप्रयोग &gt; सिस्टीम प्राधान्ये &gt; नेटवर्क &gt; प्रगत &gt; प्रॉक्सी
+<translation id="8187289872471304532">अॅप्लिकेशन &gt; सिस्टम प्राधान्ये &gt; नेटवर्क &gt; प्रगत &gt; प्रॉक्सी
वर जा आणि निवडलेल्या कोणत्याही प्रॉक्सींची निवड रद्द करा.</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_google_chrome_strings_sk.xtb b/chromium/components/strings/components_google_chrome_strings_sk.xtb
index 5a27ab6acc9..197d43fb9be 100644
--- a/chromium/components/strings/components_google_chrome_strings_sk.xtb
+++ b/chromium/components/strings/components_google_chrome_strings_sk.xtb
@@ -4,7 +4,7 @@
<translation id="1016765312371154165">Chrome nebol správne vypnutý.</translation>
<translation id="130631256467250065">Zmeny sa prejavia po ďalšom reštartovaní zariadenia.</translation>
<translation id="2147651015520127414">Chrome overil, že certifikát tohto webu vydal vydavateľ <ph name="ISSUER" />.</translation>
-<translation id="2874156562296220396">Vytvorenie prehliadača Google Chrome bolo umožnené prostredníctvom projektu otvoreného zdrojového kódu <ph name="BEGIN_LINK_CHROMIUM" />Chromium<ph name="END_LINK_CHROMIUM" /> a ďalšieho <ph name="BEGIN_LINK_OSS" />softvéru s otvoreným zdrojovým kódom<ph name="END_LINK_OSS" />.</translation>
+<translation id="2874156562296220396">Google Chrome je založený na open source projekte <ph name="BEGIN_LINK_CHROMIUM" />Chromium<ph name="END_LINK_CHROMIUM" /> a ďalšom <ph name="BEGIN_LINK_OSS" />open source softvéri<ph name="END_LINK_OSS" />.</translation>
<translation id="3140883423282498090">Zmeny sa prejavia pri nasledujúcom reštartovaní prehliadača Google Chrome.</translation>
<translation id="3444832043240812445">Táto stránka zobrazuje informácie o nedávnych zlyhaniach, len ak ste <ph name="BEGIN_LINK" />povolili hlásenia zlyhaní<ph name="END_LINK" />.</translation>
<translation id="3875312571075912821">Chromu povoľte prístup k sieti v nastaveniach brány firewall alebo antivírusového softvéru.</translation>
diff --git a/chromium/components/strings/components_strings_am.xtb b/chromium/components/strings/components_strings_am.xtb
index 012d0c91d91..1d4e6b9cffe 100644
--- a/chromium/components/strings/components_strings_am.xtb
+++ b/chromium/components/strings/components_strings_am.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">ሌሎች መተግበሪያዎችን ይዝጉ</translation>
<translation id="1055184225775184556">&amp;አክልን ቀልብስ</translation>
<translation id="10614374240317010">በጭራሽ አልተቀመጠም</translation>
+<translation id="1066396345355680611">ከ<ph name="SITE" /> እና ሌሎች አንዳንድ ጣቢያዎች የመጣ ጥበቃ የሚደረግለት የይዘት መዳረሻ ሊያጡ ይችላሉ።</translation>
<translation id="106701514854093668">የዴስክቶፕ ዕልባቶች</translation>
<translation id="1074497978438210769">ደህንነቱ አልተጠበቀም</translation>
<translation id="1080116354587839789">ከስፋቱ ጋር አመጣጥን</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">የንባብ ዝርዝር</translation>
<translation id="1264126396475825575">በ<ph name="CRASH_TIME" /> ላይ የብልሽት ሪፖርት ተይዟል (እስካሁን አልተሰቀለም ወይም ችላ አልተባለም)</translation>
<translation id="1281526147609854549">በ<ph name="ISSUER" /> የተሰጠ</translation>
-<translation id="1283919782143846010">አደገኛ ይዘት ታግዷል</translation>
<translation id="1285320974508926690">ይህን ጣቢያ በጭራሽ አትተርጉም</translation>
<translation id="129553762522093515">በቅርብ ጊዜ የተዘጉ</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />ኩኪዎችዎን ማጽዳት ይሞክሩ<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">የምዝገባ ጎራ፦</translation>
<translation id="1340482604681802745">የመውሰጃ አድራሻ</translation>
-<translation id="1344211575059133124">ይህን ጣቢያ ለመጎብኘት ፈቃድ የሚያስፈልገዎት ይመስላል</translation>
<translation id="1344588688991793829">የChromium ራስ-ሙላ ቅንብሮች...</translation>
<translation id="1348198688976932919">ቀጥሎ እየመጣ ያለው ጣቢያ አደገኛ መተግበሪያዎችን በውስጡ ይዟል</translation>
<translation id="1374468813861204354">የቀረቡ የጥቆማ አስተያየቶች</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">የ<ph name="ORGANIZATION" /> በ<ph name="LOCALITY" /> ማንነቱ የተረጋገጠው በ<ph name="ISSUER" /> ነው።</translation>
<translation id="1426410128494586442">አዎ</translation>
<translation id="1430915738399379752">አትም</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ተጨማሪ}one{<ph name="PAYMENT_METHOD_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ተጨማሪ}other{<ph name="PAYMENT_METHOD_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ተጨማሪ}}</translation>
<translation id="1506687042165942984">የዚህን ገጽ የተቀመጠ (ለምሳሌ ቀኑ ያለፈበት እንደሆነ የታወቀ) ቅጂን አሳይ።</translation>
<translation id="1517433312004943670">ስልክ ቁጥር ያስፈልጋል</translation>
+<translation id="1517500485252541695">ተቀባይነት ያላቸው ክሬዲት እና ዴቢት ካርዶች</translation>
<translation id="1519264250979466059">የግንብ ቀን</translation>
+<translation id="1527263332363067270">ግንኙነትን በመጠበቅ ላይ…</translation>
<translation id="153384715582417236">ለአሁን ያለው ይኸው ነው</translation>
<translation id="1549470594296187301">ይህን ባህሪ ለመጠቀም ጃቫስክሪፕት መንቃት አለበት።</translation>
<translation id="1555130319947370107">ሰማያዊ</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">የሥርዓት አስተዳዳሪውን ለማነጋገር ይሞክሩ።</translation>
<translation id="1740951997222943430">ትክክለኛ የአገልግሎት ማብቂያ ወር ያስገቡ</translation>
-<translation id="1745358365027406341">ገጹን በኋላ አውርድ</translation>
<translation id="17513872634828108">ትሮችን ክፈት</translation>
<translation id="1753706481035618306">የገጽ ቁጥር</translation>
<translation id="1763864636252898013">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በመሣሪያዎ ስርዓተ ክወና የሚታመን አይደለም። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">የሚያስፈልግ መስክ</translation>
<translation id="187918866476621466">የመነሻ ገጾችን ክፈት</translation>
<translation id="1883255238294161206">ዝርዝር ሰብስብ</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ተጨማሪ}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ተጨማሪ}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ተጨማሪ}}</translation>
<translation id="1898423065542865115">በማጣራት ላይ</translation>
+<translation id="1916770123977586577">የተዘመኑ ቅንብሮችዎን በዚህ ጣቢያ ላይ ለመተግበር ይህን ገጽ እንደገና ይጫኑት</translation>
+<translation id="1919345977826869612">ማስታወቂያዎች</translation>
<translation id="192020519938775529">{COUNT,plural, =0{ምንም}=1{1 ጣቢያ}one{# ጣቢያዎች}other{# ጣቢያዎች}}</translation>
<translation id="194030505837763158">ወደ <ph name="LINK" /> ሂድ</translation>
+<translation id="1948773908305951926">ተቀባይነት ያላቸው የቅድመ-ክፍያ ካርዶች</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> እልባቶች</translation>
<translation id="1973335181906896915">የመለያ ቁጥር መስጠት ላይ ስህተት</translation>
<translation id="1974060860693918893">የላቀ</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">የኤች ቲ ቲ ፒ ስህተት</translation>
<translation id="2270484714375784793">ስልክ ቁጥር</translation>
<translation id="2282872951544483773">የማይገኙ ሙከራዎች</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ንጥል}one{<ph name="ITEM_COUNT" /> ንጥሎች}other{<ph name="ITEM_COUNT" /> ንጥሎች}}</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>
<translation id="2337852623177822836">ቅንብር በአስተዳዳሪዎ ነው ቁጥጥር የሚደረግበት</translation>
<translation id="2354001756790975382">ሌላ እልባቶች</translation>
<translation id="2354430244986887761">Google የጥንቃቄ አሰሳ በቅርብ ጊዜ በ<ph name="SITE" /> ላይ <ph name="BEGIN_LINK" />ጎጂ መተግበሪያዎችን አግኝቷል<ph name="END_LINK" />።</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">ቀጥል</translation>
<translation id="2365563543831475020">በ<ph name="CRASH_TIME" /> ላይ የተያዘው የብልሽት ሪፖርት አልተሰቀለም</translation>
<translation id="2367567093518048410">ደረጃ</translation>
-<translation id="237718015863234333">ምንም የበይነገጽ አማራጮች አይገኙም</translation>
<translation id="2384307209577226199">የንግድ ድርጅት ነባሪ</translation>
<translation id="2386255080630008482">የአገልጋይ እውቅና ማረጋገጫ ተሽሯል።</translation>
<translation id="2392959068659972793">ምንም እሴት ያልተዋቀረላቸው መምሪያዎችን አሳይ</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">ምልክት ከተደረገበት Chromium ለተሻለ የቅጽ አሞላል ፍጥነት የካርድዎን ቅጂ በዚህ መሣሪያ ላይ ያከማቻል።</translation>
<translation id="2498091847651709837">አዲስ ካርድ ቃኝ</translation>
<translation id="2501278716633472235">ወደ ኋላ ተመለስ</translation>
+<translation id="2503184589641749290">ተቀባይነት ያላቸው የዴቢት እና የቅድመ-ክፍያ ካርዶች</translation>
<translation id="2515629240566999685">በእርስዎ አካባቢ ያለውን ሲግናል መፈተሽ</translation>
-<translation id="2516305470678292029">የበይነገጽ አማራጮች</translation>
<translation id="2539524384386349900">አግኝ</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> ልክ ያልኾነ ምላሽ ልኳል።</translation>
<translation id="2556876185419854533">&amp;አርትዕን ቀልብስ</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">የመላኪያ ዘዴ</translation>
<translation id="277499241957683684">የሚጎድል የመሣሪያ መዝገብ</translation>
<translation id="2784949926578158345">ግንኙነቱ ዳግም እንዲጀምር ተደርጓል።</translation>
+<translation id="2788784517760473862">ተቀባይነት ያላቸው ክሬዲት ካርዶች</translation>
<translation id="2794233252405721443">ጣቢያ ታግዷል</translation>
<translation id="2799020568854403057">ቀጥሎ ያለው ጣቢያ ጎጂ መተግበሪያዎች አሉት</translation>
<translation id="2803306138276472711">Google የጥንቃቄ አሰሳ በቅርብ ጊዜ <ph name="SITE" /> ላይ <ph name="BEGIN_LINK" />ተንኮል-አዘል ዌር<ph name="END_LINK" /> አግኝቷል። በመደበኛ ጊዜ ደህንነታቸው የተጠበቁ ድር ጣቢያዎች አንዳንድ ጊዜ በተንኮል-አዘል ዌር ይጠቃሉ።</translation>
<translation id="2824775600643448204">የአድራሻ እና ፍለጋ አሞሌ</translation>
<translation id="2826760142808435982">ግንኙነቱ የተመሰጠረ እና <ph name="CIPHER" />ን በመጠቀም የተረጋገጠ ነው፣ እና <ph name="KX" />ን እንደ የቁልፍ መቀያየሪያ ስልት ይጠቀምበታል።</translation>
<translation id="2835170189407361413">ቅጽ አጽዳ</translation>
+<translation id="2851634818064021665">ይህን ጣቢያ ለመጎብኘት ፈቃድ ያስፈልገዎታል</translation>
<translation id="2856444702002559011">አጥቂዎች የእርስዎን መረጃ (ለምሳሌ፦ የይለፍ ቃላትን፣ መልዕክቶችን፣ ወይም የክሬዲት ካርዶችን የመሳሰሉ) ከ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ለመስረቅ እየሞከሩ ሊሆኑ ይችላሉ። <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">ዳግም አትጫን</translation>
-<translation id="2900469785430194048">Google Chrome ይህን ድረ-ገጽ ለማሳየት በሚሞክርበት ጊዜ ማኅደረ ትውስታ አልቆበታል።</translation>
<translation id="2909946352844186028">የአውታረ መረብ ለውጥ ተገኝቷል።</translation>
<translation id="2916038427272391327">ሌሎች ፕሮግራሞችን ይዝጉ</translation>
<translation id="2922350208395188000">የአገልጋይ እውቅና ማረጋገጫ ሊረጋገጥ አልቻለም።</translation>
<translation id="2928905813689894207">ክፍያ የሚጠየቅበት አድራሻ</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ተጨማሪ}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ተጨማሪ}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ተጨማሪ}}</translation>
<translation id="2941952326391522266">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በ<ph name="DOMAIN2" /> ነው የተሰጠው። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
<translation id="2948083400971632585">ከቅንብሮች ገጽ ሆነው ማናቸውንም ለግንኙነት የተዋቀሩ ተኪዎችን ማሰናከል ይችላሉ።</translation>
<translation id="2955913368246107853">አግኝ አሞሌን ዝጋ</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">ከነቃ Chromium ለተሻለ የቅጽ አሞላል ፍጥነት ሲባል በዚህ መሣሪያ ላይ ያለው የካርድዎን ቅጂ ያከማቻል።</translation>
<translation id="2985398929374701810">የሚሰራ አድራሻ ያስገቡ</translation>
<translation id="2986368408720340940">ይህ የመውሰጃ ዘዴ አይደገፍም። የተለየ ዘዴ ይምረጡ።</translation>
@@ -277,11 +281,9 @@
<translation id="3167968892399408617">ማንነትን በማያሳውቅ ትሮች ውስጥ የሚያዩዋቸው ገጾች ሁሉንም ማንነት የማያሳውቁ ትሮችዎን ከዘጉ በኋላ በአሳሽዎ ታሪክ፣ የኩኪ ማከማቻ፣ ወይም የፍለጋ ታሪክ ውስጥ አይቀመጡም። ማንነት በማያስውቅ ሁነታ መሥራት የሌሎች ሰዎች፣ አገልጋዮች፣ ሶፍትዌሮች ወይም ከጀርባዎ የቆሙ የሌሎች ሰዎች ባህሪይ ላይ ለውጥ አያመጣም።</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">ደሴት</translation>
-<translation id="317583078218509884">ገጹ ዳግም ከተጫነ በኋላ አዲስ የጣቢያ ፍቃዶች ቅንብሮቹ ይተገበራሉ።</translation>
<translation id="3176929007561373547">ተኪ አገልጋዩ በአግባቡ እየሰራ መሆኑን ለማረጋገጥ የተኪ ቅንብሮችዎን ይፈትሹ ወይም የአውታረ
መረብዎ አስተዳዳሪን ያግኙ። ተኪ አገልጋይ መጠቀም እንደሌለብዎት የሚያምኑ ከሆኑ፦
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">ገጽ ማንነት በማያሳውቅ ሁነታ ውስጥ ይክፈቱ</translation>
<translation id="320323717674993345">ክፍያን ሰርዝ</translation>
<translation id="3207960819495026254">ዕልባት ተደርጎበታል</translation>
<translation id="3225919329040284222">አገልጋዩ አብረው የተሰሩ የሚጠበቁ ማሟያዎችን የማያሟላ የእውቅና ማረጋገጫ ነው ያቀረበው። እነዚህ የሚጠበቁ ማሟያዎች እርስዎን ለመጠበቅ ለተረጋገጡ ከፍተኛ ደህንነት ላላቸው ድር ጣቢያዎች ተካትተዋል።</translation>
@@ -294,6 +296,7 @@
<translation id="3270847123878663523">&amp;ዳግም ደርድርን ቀልብስ</translation>
<translation id="3282497668470633863">በካርድ ላይ ስም ያክሉ</translation>
<translation id="3286538390144397061">አሁን ዳግም አስጀምር</translation>
+<translation id="3287510313208355388">መስመር ላይ ሲሆኑ ያውርዱ</translation>
<translation id="3303855915957856445">ምንም የፍለጋ ውጤቶች አልተገኙም</translation>
<translation id="3305707030755673451">የእርስዎ ውሂብ <ph name="TIME" /> ላይ በእርስዎ የስምረት የይለፍ ቃል ተመስጥሯል። ስምረትን ለመጀመር ያስገቡት።</translation>
<translation id="3320021301628644560">የመክፈያ አድራሻ አክል</translation>
@@ -326,7 +329,6 @@
<translation id="3452404311384756672">የሚመጣው በየ፦</translation>
<translation id="3462200631372590220">የላቁ ደብቅ</translation>
<translation id="3467763166455606212">የካርድ ያዥ ስም ያስፈልጋል</translation>
-<translation id="3478058380795961209">ጊዜው የሚያልፍበት ወር</translation>
<translation id="3479539252931486093">ይህ ያልተጠበቀ ነበር? <ph name="BEGIN_LINK" />ያሳውቁን<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">አሁን አይደለም</translation>
<translation id="3498215018399854026">በዚህ ጊዜ ላይ ወላጅህን ማግኘት አልቻልንም። እባክህ እንደገና ሞክር።</translation>
@@ -342,6 +344,7 @@
&lt;p&gt;እባክዎ ቀኑን እና ሰዓቱን ከ&lt;strong&gt;አጠቃላይ&lt;/strong&gt; ክፍል የሚለው የ&lt;strong&gt;ቅንብሮች&lt;/strong&gt; መተግበሪያ ላይ ያስተካክሉ።&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />፣ <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">የክሬዲት እና የቅድመ-ክፍያ ካርዶች ተቀባይነት አላቸው።</translation>
<translation id="3582930987043644930">ስም ያክሉ</translation>
<translation id="3583757800736429874">&amp;ውሰድን ድገም</translation>
<translation id="3586931643579894722">ዝርዝር ደብቅ</translation>
@@ -357,10 +360,10 @@
<translation id="3655670868607891010">ይህንን በተደጋጋሚነት የሚያዩ ከሆኑ <ph name="HELP_LINK" />ን ይሞክሩ።</translation>
<translation id="3658742229777143148">ክለሳ</translation>
<translation id="3678029195006412963">ጥያቄ ሊፈረም አልተቻለም</translation>
+<translation id="3678529606614285348">ገጹን ማንነት በማያሳውቅ አዲስ መስኮት ውስጥ ይክፈቱ (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">የብልሽት ሪፖርት <ph name="CRASH_TIME" /> ላይ ተይዟል፣ <ph name="UPLOAD_TIME" /> ላይ ተሰቅሏል</translation>
<translation id="3681007416295224113">የሰርቲፊኬት መረጃ</translation>
<translation id="3690164694835360974">መግቢያ ደህንነቱ የተጠበቀ አይደለም</translation>
-<translation id="3693415264595406141">የይለፍ ቃል፦</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">በመጫን ላይ…</translation>
<translation id="3712624925041724820">ሁሉም ፍቃዶች ተሞክረዋል</translation>
@@ -372,6 +375,7 @@
<translation id="3748148204939282805">በ<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="375403751935624634">በአገልጋይ ስህተት ምክንያት የትርጉም ስራው ተሰናክሏል።</translation>
<translation id="3759461132968374835">በቅርብ ጊዜ ሪፖርት የተደረጉ ብልሽቶች የለዎትም። የብልሽት ሪፖርት ማድረግ ተሰናክሎ ሳለ የተከሰቱ ብልሽቶች እዚህ አይታዩም።</translation>
+<translation id="3765032636089507299">የSafe Browsing ገጽ በግንባታ ላይ ነው።</translation>
<translation id="3778403066972421603">ይህን ካርድ በእርስዎ የGoogle መለያ እና በዚህ መሣሪያ ላይ ማስቀመጥ ይፈልጋሉ?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">በ<ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /> ላይ የአገልግሎት ጊዜው ያበቃል</translation>
@@ -384,8 +388,10 @@
<translation id="3886446263141354045">ይህን ጣቢያ የመድረስ ጥያቄዎ ለ<ph name="NAME" /> ተልኳል</translation>
<translation id="3890664840433101773">ኢሜይል ያክሉ</translation>
<translation id="3901925938762663762">ካርዱ አገልግሎት ጊዜው አብቅቷል</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">የተሰቀለ የብልሽት ሪፖርት መታወቂያ <ph name="CRASH_ID" /> (የአካባቢ የብልሽት መታወቂያ፦ <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት ዕውቅና ማረጋገጫው የርዕሰ ጒዳይ አማራጭ ስሞችን አይጠቅስም። ይህ በተሳሳተ ውቅረት የተከሰተ ወይም አጥቂ የእርስዎን ግንኙነት አቋርጦ እየገባ ስለሆነ ሊሆን ይችላል።</translation>
+<translation id="3949601375789751990">የአሰሳ ታሪክዎ እዚህ ይመጣል</translation>
<translation id="3963721102035795474">የአንባቢ ሁነታ</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{ምንም}=1{ከ1 ጣቢያ }one{ከ# ጣቢያዎች }other{ከ# ጣቢያዎች }}</translation>
<translation id="397105322502079400">በማስላት ላይ...</translation>
@@ -414,6 +420,8 @@
<translation id="4165986682804962316">የጣቢያ ቅንብሮች</translation>
<translation id="4169947484918424451">Chromium ይህን ካርድ እንዲያስቀምጥልዎት ይፈልጋሉ?</translation>
<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="4196861286325780578">&amp;ውሰድን ድገም</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />የኬላ እና የጸረ-ቫይረስ ውቅረቶችን መፈተሽ<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ብልሽቶች</translation>
@@ -437,12 +445,12 @@
<translation id="4356973930735388585">በዚህ ጣቢያ ላይ ያሉ አጥቂዎች መረጃዎን (ለምሳሌ፦ ፎቶዎች፣ የይለፍ ቃላት፣ መልዕክቶች እና ክሬዲት ካርዶች) ሊሰርቁ ወይም ሊሰርዙ የሚችሉ አደገኛ ፕሮግራሞችን በኮምፒውተርዎ ላይ ለመጫን ሊሞክሩ ይችላሉ።</translation>
<translation id="4372948949327679948">የተጠበቀው የ<ph name="VALUE_TYPE" /> ዋጋ ነው።</translation>
<translation id="4377125064752653719"><ph name="DOMAIN" />ን ለመድረስ ሞክረዋል፣ ነገር ግን አገልጋዩ ያቀረበው የእውቅና ማረጋገጫ በሰጪው ተሽሯል። ይህ ማለት አገልጋዩ ያቀረበው የደህንነት ምስክርነቶች ፈጽሞ ሊታመኑ አይገባም። ከአጥቂ ጋር እየተገናኙ ሊሆን ይችላል።</translation>
-<translation id="4381091992796011497">የተጣቃሚ ስም፦</translation>
<translation id="4394049700291259645">አሰናክል</translation>
<translation id="4406896451731180161">የፍለጋ ውጤቶች</translation>
<translation id="4424024547088906515">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በChrome የሚታመን አይደለም። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> የመግቢያ እውቅና ማረጋገጫዎን አልተቀበለም፣ ወይም ገና አልተሰጠዎት ይሆናል።</translation>
<translation id="443673843213245140">የተኪ መጠቀም ተሰናክሏል ግን ግልጽ የሆነ የተኪ ውቅር ተገልጿል።</translation>
+<translation id="445100540951337728">ተቀባይነት ያላቸው ዴቢት ካርዶች</translation>
<translation id="4506176782989081258">የማረጋገጥ ስህተት፦ <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">የሥርዓት አስተዳዳሪውን ማነጋገር</translation>
<translation id="450710068430902550">ከአስተዳዳሪ ጋር ማጋራት</translation>
@@ -454,12 +462,14 @@
<translation id="4587425331216688090">አድራሻ ከChrome ይወገድ?</translation>
<translation id="4592951414987517459">ወደ የእርስዎ <ph name="DOMAIN" /> ግንኙነት ዘመናዊ የምስጠራ ጥቅል በመጠቀም ተመስጥሯል።</translation>
<translation id="4594403342090139922">&amp;ሰርዝን ቀልብስ</translation>
+<translation id="4611292653554630842">ግባ</translation>
<translation id="4619615317237390068">ከሌሎች መሣሪያዎች የመጡ ትሮች</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው ስህተቶች አሉበት። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
<translation id="4690462567478992370">ልክ ያልሆነ የእውቅና ማረጋገጫ መጠቀም አቁም</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">የእርስዎ ግንኙነት ተቋርጧል</translation>
+<translation id="471880041731876836">ይህን ጣቢያ የመጎብኘት ፈቃድ የለዎትም</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />የWindows አውታረ መረብ መመርመሪያን በማሄድ ላይ<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">መምሪያዎችን ዳግም ጫን</translation>
<translation id="4728558894243024398">የመሣሪያ ስርዓት</translation>
@@ -481,14 +491,15 @@
<translation id="4850886885716139402">አሳይ</translation>
<translation id="4854362297993841467">የማድረሻ ዘዴው አይገኝም። የተለየ ዘዴ ይሞክሩ።</translation>
<translation id="4858792381671956233">ይህን ገጽ መጎብኘት ችግር ካለው ወላጆችዎንጠይቀዋል</translation>
-<translation id="4863764087567530506">ይህ ይዘት ሶፍትዌር እንዲጭኑ ወይም የግል መረጃ ገልጸው እንዲያሳዩ ሊያሳስትዎት ሊሞክር ይችል ይሆናል። <ph name="BEGIN_LINK" />የሆነው ሆኖ አሳይ<ph name="END_LINK" />።</translation>
<translation id="4880827082731008257">የፍለጋ ታሪክ</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />፣ <ph name="TYPE_2" />፣, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">ማረጋገጫ ያስፈልጋል</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{እና 1 ተጨማሪ ድረ-ገጽ}one{እና # ተጨማሪ ድረ-ገጾች}other{እና # ተጨማሪ ድረ-ገጾች}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">ገጹ ከማይታወቅ ቋንቋ ወደ <ph name="LANGUAGE_LANGUAGE" /> ተተርጉሟል</translation>
<translation id="4923459931733593730">ክፍያ</translation>
<translation id="4926049483395192435">መገለጽ አለበት።</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">እርምጃዎች</translation>
<translation id="4958444002117714549">ዝርዝሩን ዘርጋ</translation>
<translation id="4974590756084640048">ማስጠንቀቂያዎችን ዳግም አንቃ</translation>
@@ -504,6 +515,7 @@
<translation id="5045550434625856497">ትክክል ያልሆነ የይለፍ ቃል</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>
<translation id="5087580092889165836">ካርድ አክል</translation>
<translation id="5089810972385038852">ግዛት</translation>
@@ -511,18 +523,27 @@
<translation id="5095208057601539847">ጠቅላይ ግዛት</translation>
<translation id="5115563688576182185">(64-ቢት)</translation>
<translation id="5141240743006678641">የተመሳሰሉ የይለፍ ቃላት ከGoogle ምስክርነቶችዎ ጋር ያመሳስሉ</translation>
-<translation id="514421653919133810">ገጽን ማንነት በማያሳውቅ ሁነታ ውስጥ ይክፈቱ (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">የስህተት ኮድ በመምሪያው ምላሽ ውስጥ አለ</translation>
+<translation id="5159010409087891077">ገጹን ማንነት በማያሳውቅ አዲስ መስኮት ውስጥ ይክፈቱ (⇧⌘N)</translation>
<translation id="5171045022955879922">ይፈልጉ ወይም ዩአርኤል ይጻፉ</translation>
<translation id="5172758083709347301">ማሽን</translation>
<translation id="5179510805599951267">በ<ph name="ORIGINAL_LANGUAGE" /> አይደለም? ይህን ስህተት ሪፖርት ያድርጉ</translation>
-<translation id="5181140330217080051">በማውረድ ላይ</translation>
<translation id="5190835502935405962">የዕልባቶች አሞሌ</translation>
<translation id="5199729219167945352">ሙከራዎች</translation>
<translation id="5205222826937269299">ስም ያስፈልጋል</translation>
<translation id="5222812217790122047">ኢሜይል ያስፈልጋል</translation>
<translation id="5251803541071282808">ደመና</translation>
<translation id="5277279256032773186">በሥራ ላይ Chrome እየተጠቀሙ ነዎት? ንግድ ሥራዎች ለሠራተኞቻቸው የChrome ቅንብሮችን ማስተዳደር ይችላሉ። የበለጠ ይረዱ</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />እርስዎ ድር ላይ መሆን እንዲችሉ ሶፍትዌሩን በጊዜያዊነት ለማሰናከል የሚከተሉትን ደረጃዎች ይከተሉ። የአስተዳዳሪ ልዩ መብቶች ሊኖርዎት ይገባል።<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />ጀምር<ph name="END_BOLD" />ን ጠቅ ያድርጉ፣ በመቀጠል ይፈልጉ እና <ph name="BEGIN_BOLD" />«አካባቢያዊ አገልግሎቶችን አሳይ»<ph name="END_BOLD" />ን ፈልገው ይምረጡ
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />ን ይምረጡ
+ <ph name="LIST_ITEM" />ከ<ph name="BEGIN_BOLD" />ማስጀመሪያ ዓይነት<ph name="END_BOLD" /> ሥር <ph name="BEGIN_BOLD" />ተሰናክሏል<ph name="END_BOLD" />ን ይምረጡ
+ <ph name="LIST_ITEM" />ከ<ph name="BEGIN_BOLD" />አገልግሎት ሁኔታ<ph name="END_BOLD" /> ሥር <ph name="BEGIN_BOLD" />አስቁም<ph name="END_BOLD" />ን ጠቅ ያድርጉ
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />ተግብር<ph name="END_BOLD" />ን ጠቅ ያድርጉ፣ ከዚያ <ph name="BEGIN_BOLD" />እሺ<ph name="END_BOLD" />ን ጠቅ ያድርጉ
+ <ph name="LIST_ITEM" />ሶፍትዌሩን እንዴት በቋሚነት ከእርስዎ ኮምፒውተር ማስወገድ እንደሚችሉ ለመረዳት <ph name="BEGIN_LEARN_MORE_LINK" />የChrome እገዛ ማዕከል<ph name="END_LEARN_MORE_LINK" />ን ይጎብኙ
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">ከዚህ ጣቢያ ጋር ያለዎት ግንኙነት ግላዊ አይደለም። ከምናባዊ ዕውነታ ሁነታ በማናቸውም ጊዜ ለመውጣት፣ የጆሮ ማዳመጫን ያስወግዱ እና ተመለስ የሚለውን ይጫኑ።</translation>
<translation id="5299298092464848405">መምሪያን መተንተን ላይ ስህተት</translation>
<translation id="5308689395849655368">የብልሽት ሪፖርት ማድረግ ተሰናክሏል።</translation>
@@ -539,11 +560,10 @@
<translation id="5439770059721715174">«<ph name="ERROR_PATH" />» ላይ የብያኔ ማረጋገጥ ስህተት፦ <ph name="ERROR" /></translation>
<translation id="5452270690849572955">ይህ <ph name="HOST_NAME" /> ገጽ ሊገኝ አይችልም</translation>
<translation id="5455374756549232013">መጥፎ የመምሪያ ጊዜ ማህተም</translation>
-<translation id="5455790498993699893">'<ph name="ACTIVE_MATCH" /> ከ
-<ph name="TOTAL_MATCHCOUNT" />'
-nil</translation>
<translation id="5457113250005438886">ልክ ያልሆነ</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ተጨማሪ}one{<ph name="CONTACT_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ተጨማሪ}other{<ph name="CONTACT_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ተጨማሪ}}</translation>
<translation id="5470861586879999274">&amp;አርትዕን ድገም</translation>
+<translation id="5481076368049295676">ይህ ይዘት በእርስዎ መሣሪያ ላይ መረጃዎን የሚሰርቅ ወይም የሚሰርዝ አደገኛ ሶፍትዌርን ለመጫን ሊሞክር ይችል ይሆናል። <ph name="BEGIN_LINK" />የሆነው ሆኖ አሳይ<ph name="END_LINK" /></translation>
<translation id="54817484435770891">የሚሰራ አድራሻ ያስገቡ</translation>
<translation id="5492298309214877701">በኩባንያ፣ በድርጅት ወይም በትምህርት ቤት ውስጠ መረብ ውስጥ ያለው ይህ ጣቢያ እንደ ውጫዊ የድር ጣቢያ ተመሳሳይ ዩአርኤል አለው።
<ph name="LINE_BREAK" />
@@ -555,6 +575,7 @@ nil</translation>
<translation id="5540224163453853">የተጠየቀውን ጽሑፍ ማግኘት አልተቻለም።</translation>
<translation id="5544037170328430102"><ph name="SITE" /> ላይ የተካተተ ገጽ እንዲህ ይላል፦</translation>
<translation id="5556459405103347317">ዳግም ጫን</translation>
+<translation id="5560088892362098740">አገልግሎቱ የሚያበቃበት ቀን</translation>
<translation id="5565735124758917034">ገባሪ</translation>
<translation id="5571083550517324815">ከዚህ አድራሻ ላይ መውሰድ አይቻልም። የተለየ አድራሻ ይምረጡ።</translation>
<translation id="5572851009514199876">Chrome እርስዎ ይህን ጣቢያ እንዲደርሱ የተፈቀደልዎ መሆኑን ወይም አለመሆኑን እንዲያረጋግጥ እባክዎ ይጀምሩና ወደ Chrome ይግቡ።</translation>
@@ -569,12 +590,13 @@ nil</translation>
<translation id="5629630648637658800">የመምሪያ ቅንብሮችን መጫን አልተሳካም</translation>
<translation id="5631439013527180824">ልክ ያልሆነ የመሣሪያ አስተዳደር ማስመሰያ</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="5646376287012673985">አካባቢ</translation>
<translation id="5659593005791499971">ኢሜይል</translation>
<translation id="5669703222995421982">ግላዊነት የተላበሰ ይዘት ያግኙ</translation>
<translation id="5675650730144413517">ይህ ገጽ እየሠራ አይደለም</translation>
<translation id="5710435578057952990">የዚህ ድረ-ገጽ ማንነት አልተረጋገጠም።</translation>
-<translation id="5713016350996637505">አታላይ ይዘት ታግዷል</translation>
+<translation id="5719499550583120431">የቅድመ-ክፍያ ካርዶች ተቀባይነት አላቸው።</translation>
<translation id="5720705177508910913">የአሁኑ ተጠቃሚ</translation>
<translation id="5732392974455271431">የእርስዎ ወላጆች እገዳውን ሊያነሱልዎ ይችላሉ</translation>
<translation id="5763042198335101085">ትክክለኛ የኢሜይል አድራሻ ያስገቡ</translation>
@@ -586,15 +608,14 @@ nil</translation>
<translation id="5803412860119678065">የእርስዎን <ph name="CARD_DETAIL" /> መሙላት ይፈልጋሉ?</translation>
<translation id="5810442152076338065">ወደ <ph name="DOMAIN" /> ግንኙነትዎ ፍጹማዊ ስነ መሰውር ጥቅል በመጠቀም የተመሳጠረ ነው።</translation>
<translation id="5813119285467412249">&amp;አክልን ድገም</translation>
-<translation id="5814352347845180253">ከ<ph name="SITE" /> እና ሌሎች አንዳንድ ጣቢያዎች የመጣ የፕሪሚየም ይዘት መዳረሻ ሊያጡ ይችላሉ።</translation>
<translation id="5838278095973806738">በአጥቂዎች ሊሰረቅ ስለሚችል በዚህ ጣቢያ ላይ ማናቸውም አደጋን ሊያስከትል የሚችል መረጃ (ለምሳሌ፦ የይለፍ ቃሎች ወይም የክሬዲት ካርዶች) ማስገባት የለብዎትም።</translation>
<translation id="5869405914158311789">ይህ ጣቢያ ሊደረስበት አይችልም</translation>
<translation id="5869522115854928033">የተቀመጡ የይለፍ ቃሎች</translation>
<translation id="5872918882028971132">የወላጅ አስተያየት ጥቆማዎች</translation>
+<translation id="5893752035575986141">ክሬዲት ካርዶች ተቀባይነት አላቸው።</translation>
<translation id="5901630391730855834">ቢጫ</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ሰምሯል)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 ጥቅም ላይ ያለ}one{# ጥቅም ላይ ያለ}other{# ጥቅም ላይ}}</translation>
-<translation id="5926846154125914413">ከአንዳንድ ጣቢያዎች የመጣ የፕሪሚየም ይዘት መዳረሻ ሊያጡ ይችላሉ።</translation>
<translation id="5959728338436674663">አደገኛ መተግበሪያዎችን እና ጣቢያዎችን ማግኘት እንዲያግዝ አንዳንድ <ph name="BEGIN_WHITEPAPER_LINK" />የሥርዓት መረጃ እና የገጽ ይዘት<ph name="END_WHITEPAPER_LINK" />ን በራስ-ሰር ወደ Google ይላኩ። <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">ከታሪክ አስወግድ</translation>
<translation id="5975083100439434680">አሳንስ</translation>
@@ -610,6 +631,7 @@ nil</translation>
<translation id="6040143037577758943">ዝጋ</translation>
<translation id="6042308850641462728">ተጨማሪ</translation>
<translation id="6047233362582046994">በእርስዎ ደህንነት ላይ የሚያመጣቸውን ስጋቶች ከተረዱ አደገኛ መተግበሪያዎቹ ከመወገዳቸው በፊት <ph name="BEGIN_LINK" />ይህን ጣቢያ መጎብኘት<ph name="END_LINK" /> ይችላሉ።</translation>
+<translation id="6047927260846328439">ይህ ይዘት ሶፍትዌር እንዲጭኑ ወይም የግል መረጃ ገልጸው እንዲያሳዩ እርስዎን ለማሳሳት ሊሞክር ይችል ይሆናል። <ph name="BEGIN_LINK" />የሆነው ሆኖ አሳይ<ph name="END_LINK" /></translation>
<translation id="6051221802930200923"><ph name="SITE" /> የዕውቅና ማረጋገጫ ሚስማር መሰካትን ስለሚጠቀም ድር ጣቢያውን አሁን መጎብኘት አይችሉም። የአውታረ መረብ ስህተቶች እና ጥቃቶች ብዙውን ጊዜ ጊዜያዊ ስለሆኑ ይህ ገጽ በኋላ ላይ ሊሠራ ይችላል።</translation>
<translation id="6060685159320643512">ይጠንቀቁ፣ እነዚህ ሙከራዎች ሊያስቸግሩ ይችላሉ</translation>
<translation id="6080696365213338172">በአስተዳዳሪ የቀረበ የእውቅና ማረጋገጫ በመጠቀም ይዘት ደርሰዋል። ለ<ph name="DOMAIN" /> የሚያቀርቡት ውሂብ በአስተዳዳሪዎ ሊያዝ ይችላል።</translation>
@@ -623,7 +645,6 @@ nil</translation>
<translation id="6165508094623778733">ተጨማሪ ለመረዳት</translation>
<translation id="6169916984152623906">አሁን በግል ማሰስ ይችላሉ፣ እና ይህን መሣሪያ የሚጠቀሙ ሰዎች የእርስዎን እንቅስቃሴ አይመለከቱም። ይሁንና፣ ውርዶች እና ዕልባቶች ይቀመጣሉ።</translation>
<translation id="6177128806592000436">ወደዚህ ጣቢያ ያልዎት ግንኙነት ደህንነቱ አስተማማኝ አይደለም</translation>
-<translation id="6184817833369986695">(የተመሳሳይ ሰዎች ስብስብ፦ <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">የበይነመረብ ግኑኝነትዎን ያረጋግጡ</translation>
<translation id="6218753634732582820">ከChromium ላይ አድራሻ ይወገድ?</translation>
<translation id="6221345481584921695">Google የጥንቃቄ አሰሳ በቅርብ ጊዜ <ph name="SITE" /> ላይ <ph name="BEGIN_LINK" />ተንኮል-አዘል ዌር<ph name="END_LINK" /> አግኝቷል። በመደበኛ ጊዜ ደህንነታቸው የተጠበቁ ድር ጣቢያዎች አንዳንድ ጊዜ በተንኮል-አዘል ዌር ሊጠቁ ይችላሉ። ተንኮል-አዘል ይዘቱ የሚታወቅ የተንኮል-አዘል ዌር አሰራጭ ከሆነው <ph name="SUBRESOURCE_HOST" /> ነው የመጣው።</translation>
@@ -642,13 +663,14 @@ nil</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>
<translation id="6387478394221739770">አዲስ የChrome ባህሪያትን ይፈልጋሉ? የቅድመ ይሁንታ ሰርጣችንን በchrome.com/beta ላይ ይሞክሩት።</translation>
-<translation id="6389758589412724634">Chromium ይህን ድረ-ገጽ ለማሳየት በሚሞክርበት ጊዜ ማኅደረ ትውስታ አልቆበታል።</translation>
+<translation id="6397451950548600259">በእርስዎ ኮምፒውተር ላይ ያለ ሶፍትዌር Chrome ደህንነቱ በተጠበቀ ሁኔታ ከድር ጋር እንዳይገናኝ እያስቆመው ነው</translation>
<translation id="6404511346730675251">ዕልባት አርትዕ</translation>
<translation id="6410264514553301377">የ<ph name="CREDIT_CARD" /> የአገልግሎት ማብቂያ ቀን እና ሲቪሲ ያስገቡ</translation>
<translation id="6414888972213066896">ይህን ጣቢያ መጎብኘት ችግር ካለው ወላጅዎን ጠይቀዋል</translation>
@@ -663,6 +685,7 @@ nil</translation>
<translation id="647261751007945333">የመሣሪያ መምሪያዎች</translation>
<translation id="6477321094435799029">Chrome በዚህ ገጽ ላይ ያልተለመደ ኮድ አግኝቷል፣ እና የእርስዎን የግል መረጃ (ለምሳሌ፦ የይለፍ ቃላት፣ ስልክ ቁጥሮች እና ክሬዲት ካርዶች) ለመጠበቅ ሲባል አግዶታል።</translation>
<translation id="6489534406876378309">ድምስሶችን መስቀል ጀምር</translation>
+<translation id="6507833130742554667">ክሬዲት እና ዴቢት ካርዶች ተቀባይነት አላቸው።</translation>
<translation id="6508722015517270189">Chromeን ዳግም ያስጀምሩት</translation>
<translation id="6529602333819889595">&amp;ሰርዝን ድገም</translation>
<translation id="6534179046333460208">የአካላዊ ድር ጥቆማዎች</translation>
@@ -671,13 +694,13 @@ nil</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="657639383826808334">ይህ ይዘት በእርስዎ መሣሪያ ላይ የእርስዎን መረጃ የሚሰርቅ ወይም የሚሰርዝ አደገኛ ሶፍትዌርን ለመጫን ሊሞክር ይችል ይሆናል። <ph name="BEGIN_LINK" />የሆነው ሆኖ አሳይ<ph name="END_LINK" />።</translation>
<translation id="6596325263575161958">የምስጠራ አማራጮች</translation>
<translation id="662080504995468778">ቆይ</translation>
<translation id="6626291197371920147">የሚሰራ የካርድ ቁጥር ያክሉ</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> ፍለጋ</translation>
<translation id="6630809736994426279">አሁን <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ የሚገኙ አጥቂዎች የእርስዎን መረጃ (ለምሳሌ፦ ፎቶዎች፣ የይለፍ ቃላት፣ መልዕክቶች፣ እና ክሬዲት ካርዶች የመሳሰሉ) የሚሰርቁ ወይም የሚሰርዙ አደገኛ ፕሮግራሞችን በእርስዎ Mac ላይ ለመጫን እየሞከሩ ሊሆኑ ይችላሉ። <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">ይህ መመሪያ ተቋርጧል።</translation>
+<translation id="6657585470893396449">የይለፍ ቃል፦</translation>
<translation id="6671697161687535275">የአስተያየት ጥቆማ ከChromium ይወገድ?</translation>
<translation id="6685834062052613830">ዘግተው ይውጡ እና ቅንብርን ያጠናቅቁ</translation>
<translation id="6710213216561001401">ቀዳሚ</translation>
@@ -708,7 +731,6 @@ nil</translation>
<translation id="6970216967273061347">ወረዳ</translation>
<translation id="6973656660372572881">ሁለቱም ቋሚ ተኪ አገልጋዮች እና የ.pac ስክሪፕት ዩአርኤል ተገልጸዋል።</translation>
<translation id="6989763994942163495">የላቁ ቅንብሮችን አሳይ...</translation>
-<translation id="7000990526846637657">ምንም የታሪክ ግቤቶች አልተገኙም</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">የእርስዎ Google መለያ <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> ላይ ሌሎች የአሰሳ ታሪክ ዓይነቶች ሊኖረው ይችላል</translation>
<translation id="7029809446516969842">የይለፍ ቃላት</translation>
@@ -723,7 +745,9 @@ nil</translation>
<translation id="7129409597930077180">ወደዚህ አድራሻ መላክ አይቻልም። የተለየ አድራሻ ይምረጡ።</translation>
<translation id="7138472120740807366">የማድረሻ ስልት</translation>
<translation id="7139724024395191329">ኤሚሬት</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ተጨማሪ}one{<ph name="PAYMENT_METHOD_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ተጨማሪ}other{<ph name="PAYMENT_METHOD_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ተጨማሪ}}</translation>
<translation id="7155487117670177674">ክፍያ ደህንነቱ የተጠበቀ አይደለም</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ተጨማሪ}one{<ph name="SHIPPING_OPTION_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ተጨማሪ}other{<ph name="SHIPPING_OPTION_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ተጨማሪ}}</translation>
<translation id="7179921470347911571">አሁን ዳግም ያስጀምሩ</translation>
<translation id="7180611975245234373">አድስ</translation>
<translation id="7182878459783632708">ምንም መምሪያዎች አልተዋቀሩም</translation>
@@ -764,6 +788,7 @@ nil</translation>
<translation id="7514365320538308">አውርድ</translation>
<translation id="7518003948725431193">ለዚህ የድር አድራሻ ምንም ድረ-ገጽ አልተገኘም፦ <ph name="URL" /></translation>
<translation id="7521387064766892559">ጃቫስክሪፕት</translation>
+<translation id="7526934274050461096">ከዚሃ ጣቢያ ጋር ያለዎት ግንኙነት የግል አይደለም</translation>
<translation id="7535087603100972091">እሴት</translation>
<translation id="7537536606612762813">ግዴታ</translation>
<translation id="7542403920425041731">አንዴ ካረጋገጡ በኋላ የካርድ ዝርዝሮችዎ ለዚህ ጣቢያ ይጋራሉ።</translation>
@@ -773,7 +798,6 @@ nil</translation>
<translation id="7552846755917812628">የሚከተሉትን ጠቃሚ ምክሮች ይሞክሩ፦</translation>
<translation id="7554791636758816595">አዲስ ትር</translation>
<translation id="7567204685887185387">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በተጭበረበረ ሁኔታ ተሰጥቶ ሊሆን ይችላል። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ተጨማሪ}one{<ph name="CONTACT_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ተጨማሪ}other{<ph name="CONTACT_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ተጨማሪ}}</translation>
<translation id="7568593326407688803">ይህ ገጽ በ<ph name="ORIGINAL_LANGUAGE" />ነው። መተርጎም ይፈልጋሉ?</translation>
<translation id="7569952961197462199">ክሬዲት ካርድ ከChrome ይወገድ?</translation>
<translation id="7569983096843329377">ጥቁር</translation>
@@ -800,9 +824,11 @@ nil</translation>
<translation id="7714464543167945231">ሰርቲፊኬት</translation>
<translation id="7716147886133743102">በእርስዎ አስተዳዳሪ ታግዷል</translation>
<translation id="7716424297397655342">ይህ ጣቢያ ከመሸጎጫው ላይ ሊጫን አይችልም</translation>
+<translation id="774634243536837715">አደገኛ ይዘት ታግዷል።</translation>
<translation id="7752995774971033316">አይቀናበርም</translation>
<translation id="7755287808199759310">የእርስዎ ወላጅ እገዳውን ሊያነሱልዎ ይችላሉ</translation>
<translation id="7758069387465995638">የኬላ ወይም የፀረ-ቫይረስ ሶፍትዌር ግንኙነቱን አግዶት ሊሆን ይችላል።</translation>
+<translation id="7759163816903619567">የማሳያ ጎራ፦</translation>
<translation id="7761701407923456692">የአገልጋይ እውቅና ማረጋገጫ ከዩ አር ኤሉ ጋር አይዛመድም።</translation>
<translation id="7763386264682878361">የክፍያ ዝርዝር ሰነድ ተንታኝ</translation>
<translation id="7764225426217299476">አድራሻ አክል</translation>
@@ -817,6 +843,7 @@ nil</translation>
<translation id="7815407501681723534">ለ«<ph name="SEARCH_STRING" />» <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> ተገኝተዋል።</translation>
<translation id="785549533363645510">ሆኖም ግን የማይታዩ አይደሉም። ማንነት የማያሳውቅ ሁነታ መጠቀም የእርስዎን አሰሳ፣ የበይነመረብ አገልግሎት አቅራቢ ወይም የሚጎበኟቸው ድር ጣቢያዎች ከአሰሪዎ አይደብቃቸውም።</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">የዴቢት እና የቅድመ-ክፍያ ካርዶች ተቀባይነት አላቸው።</translation>
<translation id="7887683347370398519">የእርስዎን CVC ይፈትሹ እና እንደገና ይሞክሩ</translation>
<translation id="79338296614623784">የሚሰራ ስልክ ቁጥር ያስገቡ</translation>
<translation id="7935318582918952113">የDOM ማጣሪያ</translation>
@@ -836,7 +863,6 @@ nil</translation>
<translation id="8041089156583427627">ግብረ መልስ ላክ</translation>
<translation id="8041940743680923270">ሁለንተናዊ ነባሪውን ተጠቀሙ (ጠይቅ)</translation>
<translation id="8088680233425245692">ጽሑፉን ማየት አልተቻለም።</translation>
-<translation id="8089520772729574115">ከ1 ሜባ ያነሰ</translation>
<translation id="8091372947890762290">ማግበር በአገልጋዩ ላይ በመጠባበቅ ላይ ነው</translation>
<translation id="8118489163946903409">የመክፈያ ዘዴ</translation>
<translation id="8131740175452115882">አረጋግጥ</translation>
@@ -848,10 +874,13 @@ nil</translation>
<translation id="8194797478851900357">&amp;ውሰድን ቀልብስ</translation>
<translation id="8201077131113104583">የማይሰራ የURL ዝማኔ ለቅጥያ ከመታወቂያ «<ph name="EXTENSION_ID" />» ጋር።</translation>
<translation id="8202097416529803614">የትዕዛዝ ማጠቃለያ</translation>
+<translation id="8205463626947051446">ጣቢያ ጣልቃ ገቢ ማስታወቂያዎችን የማሳየት አዝማሚያ አለው</translation>
<translation id="8218327578424803826">የተመደበ መገኛ አካባቢ፦</translation>
<translation id="8225771182978767009">ይህን ኮምፒውተር ያቀናበረው ሰው ይህን ጣቢያ ለማገድ መርጧል።</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />፣ <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">ገጹን ማንነት በማያሳውቅ አዲስ ትር ውስጥ ይክፈቱ</translation>
<translation id="8241707690549784388">እየፈለጉት ያሉት ገጽ ያስገቡትን መረጃ ተጥቅሟል። ወደዛ ገጽ መመለስ ወስደውት የነበረውን ርምጃ እንዲደገም ሊያደርግ ይችላል። ለመቀጠል ይፈልጋሉ?</translation>
+<translation id="8241712895048303527">በዚህ ጣቢያ ላይ አግድ</translation>
<translation id="8249320324621329438">ለመጨረሻ ጊዜ የመጣው፦</translation>
<translation id="8253091569723639551">የማስከፈያ አድራሻ ያስፈልጋል</translation>
<translation id="8261506727792406068">ሰርዝ</translation>
@@ -862,7 +891,6 @@ nil</translation>
<translation id="8308427013383895095">በአውታረመረብ ግንኙነት ችግር ምክንያት የትርጉም ስራው ተሰናክሏል።</translation>
<translation id="8332188693563227489">የ<ph name="HOST_NAME" /> መዳረሻ ተከልክሏል</translation>
<translation id="834457929814110454">በእርስዎ ደህንነት ላይ የሚያመጣቸውን ስጋቶች ከተረዱ አደገኛ ፕሮግራሞቹ ከመወገዳቸው በፊት <ph name="BEGIN_LINK" />ይህን ጣቢያ መጎብኘት<ph name="END_LINK" /> ይችላሉ።</translation>
-<translation id="8344669043927012510">ገጽ ማንነት በማያሳውቅ ሁነታ ውስጥ ይክፈቱ (⇧⌘N)</translation>
<translation id="8349305172487531364">የዕልባቶች አሞሌ</translation>
<translation id="8363502534493474904">የአውሮፕላን ሁነታን ማጥፋት</translation>
<translation id="8364627913115013041">አልተዋቀረም።</translation>
@@ -872,6 +900,7 @@ nil</translation>
<translation id="8398259832188219207">የብልሽት ሪፖርት <ph name="UPLOAD_TIME" /> ላይ ተሰቅሏል</translation>
<translation id="8412145213513410671">ብልሽቶች (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">ተመሳሳዩ የይለፍ ሐረጉን ሁለት ጊዜ ማስገባት አለብዎት።</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ቅንብሮች</translation>
<translation id="8433057134996913067">ይህ ከአብዛኛዎቹ የድር ጣቢያዎች ዘግቶ ያስወጣዎታል።</translation>
<translation id="8437238597147034694">&amp;ውሰድን ቀልብስ</translation>
@@ -879,8 +908,9 @@ nil</translation>
<translation id="8483780878231876732">ከእርስዎ የGoogle መለያ ካርዶችን ለመጠቀም ወደ Chrome በመለያ ይግቡ</translation>
<translation id="8488350697529856933">የሚመለከተው ለ</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ምላሽ ለመስጠት ከልክ በላይ ረዥም ጊዜ ወስዷል።</translation>
-<translation id="8532105204136943229">ጊዜው የሚያልፍበት ዓመት</translation>
+<translation id="8503813439785031346">የተጣቃሚ ስም</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="8571890674111243710">ገጽ ወደ <ph name="LANGUAGE" /> በመተርጎም ላይ...</translation>
<translation id="858637041960032120">ስልክ ቁጥር ያክሉ
@@ -888,6 +918,7 @@ nil</translation>
<translation id="859285277496340001">የእውቅና ማረጋገጫው ተሽሮ እንደሆነ የሚታይበት ምንም ስልት አይገልጽም።</translation>
<translation id="8620436878122366504">የእርስዎ ወላጆች ገና አላጸደቁትም</translation>
<translation id="8647750283161643317">ሁሉንም ወደ ነባሪ ዳግም አስጀምር</translation>
+<translation id="8660471606262461360">ከGoogle ክፍያዎች</translation>
<translation id="8703575177326907206">ከ<ph name="DOMAIN" /> ጋር ያለዎት ግንኙነት አልተመሰጠረም</translation>
<translation id="8718314106902482036">ክፍያ አልተጠናቀቀም</translation>
<translation id="8725066075913043281">እንደገና ይሞክሩ</translation>
@@ -915,6 +946,7 @@ nil</translation>
<translation id="8903921497873541725">አጉላ</translation>
<translation id="8931333241327730545">ይህን ካርድ በእርስዎ የGoogle መለያ ላይ ማስቀመጥ ይፈልጋሉ?</translation>
<translation id="8932102934695377596">የእርስዎ ሰዓት ወደ ኋላ ቀርቷል</translation>
+<translation id="8938939909778640821">ተቀባይነት ያላቸው የክሬዲት እና የቅድመ-ክፍያ ካርዶች</translation>
<translation id="8971063699422889582">የአገልጋይ እውቅና ማረጋገጫ ጊዜው አልፎበታል።</translation>
<translation id="8986494364107987395">የአጠቃቀም ስታስቲክስ እና የብልሽት ሪፖርቶች በራስ ሰር ወደ Google ይላኩ።</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -944,7 +976,6 @@ nil</translation>
<translation id="9169664750068251925">ሁልጊዜ በዚህ ጣቢያ ላይ አግድ</translation>
<translation id="9170848237812810038">&amp;ቀልብስ</translation>
<translation id="917450738466192189">የአገልጋይ እውቅና ማረጋገጫ ልክ ያልኾነ ነው።</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ተጨማሪ}one{<ph name="SHIPPING_OPTION_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ተጨማሪ}other{<ph name="SHIPPING_OPTION_PREVIEW" /> እና <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ተጨማሪ}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> የማይጠቀም ፕሮቶኮል ይጠቀማል።</translation>
<translation id="9205078245616868884">የእርስዎ ውሂብ በእርስዎ የስምረት የይለፍ ቃል ተመስጥሯል። ስምረትን ለመጀመር ያስገቡት።</translation>
<translation id="9207861905230894330">ጽሑፍ ማከል አልተቻለም።</translation>
@@ -953,9 +984,9 @@ nil</translation>
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ቅጽን አጽዳ</translation>
<translation id="939736085109172342">አዲስ ዓቃፊ</translation>
-<translation id="941721044073577244">ይህን ጣቢያ የመጎብኘት ፈቃድ የሌለዎት ይመስላሉ</translation>
<translation id="969892804517981540">ይፋ ግንባታ</translation>
<translation id="975560348586398090">{COUNT,plural, =0{ምንም}=1{1 ንጥል}one{# ንጥሎች}other{# ንጥሎች}}</translation>
+<translation id="981121421437150478">ከመስመር ውጪ</translation>
<translation id="988159990683914416">የገንቢዎች ግንባታ</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ar.xtb b/chromium/components/strings/components_strings_ar.xtb
index fc18a7380fd..618aa84fb12 100644
--- a/chromium/components/strings/components_strings_ar.xtb
+++ b/chromium/components/strings/components_strings_ar.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">إغلاق التطبيقات الأخرى</translation>
<translation id="1055184225775184556">تراجع عن الإ&amp;ضافة</translation>
<translation id="10614374240317010">لم يتم الحفظ مطلقًا</translation>
+<translation id="1066396345355680611">قد تفقد إمكانية الوصول إلى محتوى محمي من <ph name="SITE" /> وبعض المواقع الأخرى.</translation>
<translation id="106701514854093668">الإشارات المرجعية على سطح المكتب</translation>
<translation id="1074497978438210769">غير آمن</translation>
<translation id="1080116354587839789">ملاءمة مع العرض</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">قائمة القراءة</translation>
<translation id="1264126396475825575">تقرير الأعطال الذي تم الحصول عليه في <ph name="CRASH_TIME" /> (لم يتم تحميله بعد أو تجاهله)</translation>
<translation id="1281526147609854549">تم الإصدار بواسطة <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">تم حظر المحتوى الخطير</translation>
<translation id="1285320974508926690">عدم ترجمة هذا الموقع مطلقًا</translation>
<translation id="129553762522093515">المغلقة حديثًا</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />جرّب محو ملفات تعريف الارتباط<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">نطاق التسجيل:</translation>
<translation id="1340482604681802745">عنوان الاستلام</translation>
-<translation id="1344211575059133124">يبدو أنك بحاجة إلى الحصول على إذن لزيارة هذا الموقع</translation>
<translation id="1344588688991793829">‏إعدادات الملء التلقائي في Chromium...</translation>
<translation id="1348198688976932919">يحتوي موقع الويب المقصود على تطبيقات خطيرة</translation>
<translation id="1374468813861204354">اقتراحات</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">تمّ التحقق من هوية <ph name="ORGANIZATION" /> في <ph name="LOCALITY" /> من قِبل <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">نعم</translation>
<translation id="1430915738399379752">طباعة</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> وطريقة دفع واحدة (<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />) أخرى}zero{<ph name="PAYMENT_METHOD_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> طريقة دفع أخرى}two{<ph name="PAYMENT_METHOD_PREVIEW" /> وطريقتا دفع (<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />) أخريان}few{<ph name="PAYMENT_METHOD_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> طرق دفع أخرى}many{<ph name="PAYMENT_METHOD_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> طريقة دفع أخرى}other{<ph name="PAYMENT_METHOD_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> طريقة دفع أخرى}}</translation>
<translation id="1506687042165942984">عرض نسخة محفوظة (أي معروف أنها منتهية) من هذه الصفحة.</translation>
<translation id="1517433312004943670">رقم الهاتف مطلوب</translation>
+<translation id="1517500485252541695">بطاقات الائتمان وبطاقات السحب الآلي المقبولة</translation>
<translation id="1519264250979466059">تاريخ الإصدار</translation>
+<translation id="1527263332363067270">في انتظار الاتصال…</translation>
<translation id="153384715582417236">هذا كل شيء الآن</translation>
<translation id="1549470594296187301">‏يجب تمكين JavaScript لاستخدام هذه الميزة.</translation>
<translation id="1555130319947370107">أزرق</translation>
@@ -101,12 +101,11 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">جرّب الاتصال بمشرف النظام.</translation>
<translation id="1740951997222943430">أدخِل شهر انتهاء صلاحية صحيح</translation>
-<translation id="1745358365027406341">تنزيل الصفحة لاحقًا</translation>
<translation id="17513872634828108">علامات التبويب المفتوحة</translation>
<translation id="1753706481035618306">رقم الصفحة</translation>
<translation id="1763864636252898013">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان غير موثوقة من خلال نظام تشغيل جهازك. وربما يكون السبب في ذلك خطأ في التكوين أو مهاجمًا يعترض الاتصال.</translation>
<translation id="1768211456781949159">‏<ph name="BEGIN_LINK" />تجربة تشغيل بيانات التشخيص لشبكة Windows<ph name="END_LINK" />.</translation>
-<translation id="1783075131180517613">الرجاء تحديث عبارة مرور المزامنة.</translation>
+<translation id="1783075131180517613">يُرجى تحديث عبارة مرور المزامنة.</translation>
<translation id="1787142507584202372">تظهر علامات التبويب المفتوحة هنا</translation>
<translation id="1789575671122666129">النوافذ المنبثقة</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">حقل مطلوب</translation>
<translation id="187918866476621466">فتح صفحات بدء التشغيل</translation>
<translation id="1883255238294161206">تصغير القائمة</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> وعنوان واحد (<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />) آخر}zero{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> عنوان آخر}two{<ph name="SHIPPING_ADDRESS_PREVIEW" /> وعنوانان (<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />) آخران}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> عناوين أخرى}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> عنوانًا آخرًا}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> عنوان آخر}}</translation>
<translation id="1898423065542865115">التصفية</translation>
+<translation id="1916770123977586577">لتطبيق الإعدادات المُحدَّثة على موقع الويب هذا، أعِد تحميل هذه الصفحة</translation>
+<translation id="1919345977826869612">إعلانات</translation>
<translation id="192020519938775529">{COUNT,plural, =0{بدون}=1{موقع واحد}two{ موقعان (#)}few{# مواقع}many{# موقعًا}other{# موقع}}</translation>
<translation id="194030505837763158">الانتقال إلى <ph name="LINK" /></translation>
+<translation id="1948773908305951926">بطاقات الدفع المُسبق المقبولة</translation>
<translation id="1962204205936693436">إشارات <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">خطأ أثناء التسلسل</translation>
<translation id="1974060860693918893">إعدادات متقدمة</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">‏خطأ HTTP</translation>
<translation id="2270484714375784793">رقم الهاتف</translation>
<translation id="2282872951544483773">التجارب غير المتاحة</translation>
-<translation id="2283595600756901917">{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="2292556288342944218">تم حظر دخولك إلى الإنترنت</translation>
<translation id="230155334948463882">بطاقة جديدة؟</translation>
+<translation id="2316887270356262533">يوفِّر أقل من 1 ميغابايت. وقد يتم تحميل بعض المواقع بشكل أبطأ عند زيارتها في المرة القادمة.</translation>
<translation id="2317259163369394535">يتطلَّب <ph name="DOMAIN" /> اسم مستخدم وكلمة مرور.</translation>
+<translation id="2317583587496011522">يتم قبول بطاقات السحب الآلي.</translation>
<translation id="2337852623177822836">يتم التحكّم في الإعداد من قبل المشرف</translation>
<translation id="2354001756790975382">الإشارات الأخرى</translation>
<translation id="2354430244986887761">‏عثر ‏‫التصفح الآمن من Google‬ مؤخرًا <ph name="BEGIN_LINK" />على تطبيقات ضارة<ph name="END_LINK" /> في <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">المتابعة</translation>
<translation id="2365563543831475020">لم يتم تحميل تقرير الأعطال الذي تم الحصول عليه في <ph name="CRASH_TIME" /></translation>
<translation id="2367567093518048410">المستوى</translation>
-<translation id="237718015863234333">لا توجد بدائل لعناصر واجهة المستخدم</translation>
<translation id="2384307209577226199">السياسة افتراضية في المؤسسة ويمكن إلغاؤها</translation>
<translation id="2386255080630008482">تم إبطال شهادة الخادم.</translation>
<translation id="2392959068659972793">عرض السياسات التي لم يتم تعيين قيم لها</translation>
@@ -194,14 +195,14 @@
<translation id="2495093607237746763">‏عند وضع علامة على هذا الخيار، سيخزّن Chromium نسخة من بطاقتك على هذا الجهاز لتعبئة النماذج بشكل أسرع.</translation>
<translation id="2498091847651709837">فحص بطاقة جديدة</translation>
<translation id="2501278716633472235">الرجوع للخلف</translation>
+<translation id="2503184589641749290">بطاقات السحب الآلي وبطاقات الدفع المسبق المقبولة</translation>
<translation id="2515629240566999685">التحقق من الإشارة في منطقتك</translation>
-<translation id="2516305470678292029">بدائل عناصر واجهة المستخدم</translation>
<translation id="2539524384386349900">اكتشاف</translation>
<translation id="255002559098805027">أرسل <ph name="HOST_NAME" /> استجابة غير صالحة.</translation>
<translation id="2556876185419854533">تراجع عن ا&amp;لتحرير</translation>
<translation id="2587730715158995865">من <ph name="ARTICLE_PUBLISHER" />. يمكنك قراءة هذه المقالة و<ph name="OTHER_ARTICLE_COUNT" /> قصص أخرى.</translation>
<translation id="2587841377698384444">رقم تعريف واجهة برمجة التطبيقات الدليل:</translation>
-<translation id="2597378329261239068">هذا المستند محمي بكلمة المرور. الرجاء إدخال كلمة مرور.</translation>
+<translation id="2597378329261239068">هذا المستند محمي بكلمة المرور. يُرجى إدخال كلمة مرور.</translation>
<translation id="2609632851001447353">الاختلافات</translation>
<translation id="262424810616849754">{COUNT,plural, =0{بدون}=1{تطبيق واحد ($1)}=2{تطبيقان (2) ($1، $2)}few{# تطبيقات ($1، $2، $3)}many{# تطبيقًا ($1، $2، $3)}other{# تطبيق ($1، $2، $3)}}</translation>
<translation id="2625385379895617796">توقيت ساعتك متقدم عن الوقت الحالي</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">طريقة الشحن</translation>
<translation id="277499241957683684">سجِلّ الجهاز مفقود</translation>
<translation id="2784949926578158345">تمت إعادة تعيين الاتصال.</translation>
+<translation id="2788784517760473862">بطاقات الائتمان المقبولة</translation>
<translation id="2794233252405721443">تم حظر الموقع</translation>
<translation id="2799020568854403057">يحتوي موقع الويب المقصود على تطبيقات ضارة</translation>
<translation id="2803306138276472711">‏اكتشف التصفح الآمن من Google مؤخرًا <ph name="BEGIN_LINK" />برامج ضارة<ph name="END_LINK" /> على <ph name="SITE" />. أحيانًا تصاب مواقع الويب الآمنة في الوضع العادي ببرامج ضارة.</translation>
<translation id="2824775600643448204">شريط العناوين والبحث</translation>
<translation id="2826760142808435982">تم تشفير الاتصال ومصادقته باستخدام <ph name="CIPHER" />، ويستخدم <ph name="KX" /> كآلية التبادل الرئيسية.</translation>
<translation id="2835170189407361413">محو النموذج</translation>
+<translation id="2851634818064021665">يلزمك الحصول على إذن لزيارة هذا الموقع</translation>
<translation id="2856444702002559011">قد يحاول المهاجمون سرقة معلوماتك من <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (على سبيل المثال، كلمات المرور، أو الرسائل، أو بطاقات الائتمان). <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">إلغاء إعادة التحميل</translation>
-<translation id="2900469785430194048">‏نفدت ذاكرة Google Chrome أثناء محاولة عرض صفحة الويب هذه.</translation>
<translation id="2909946352844186028">تم اكتشاف حدوث تغيير في الشبكة.</translation>
<translation id="2916038427272391327">إغلاق البرامج الأخرى</translation>
<translation id="2922350208395188000">لا يمكن التحقق من شهادة الخادم.</translation>
<translation id="2928905813689894207">عنوان إرسال الفواتير</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> وعنوان <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> آخر}two{<ph name="SHIPPING_ADDRESS_PREVIEW" /> وعنوانان (<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />) آخران}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> عناوين أخرى}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> عنوانًا آخر}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> عنوان آخر}}</translation>
<translation id="2941952326391522266">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان من <ph name="DOMAIN2" />. وربما سبب ذلك خطأ في التكوين أو مهاجمًا يعترض اتصالك.</translation>
<translation id="2948083400971632585">يمكنك تعطيل أي خوادم وكيلة تمت تهيئتها لاتصال من صفحة الإعدادات.</translation>
<translation id="2955913368246107853">إغلاق شريط البحث</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">‏عند التمكين، سيُخزن Chromium نسخة من بطاقتك على هذا الجهاز لتعبئة النماذج بشكل أسرع.</translation>
<translation id="2985398929374701810">أدخِل عنوانًا صحيحًا</translation>
<translation id="2986368408720340940">طريقة الاستلام هذه غير متاحة. جرِّب طريقة أخرى.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">لن يتم تسجيل الصفحات التي تعرضها في علامات تبويب التصفح المتخفي في سجل المتصفح أو مخزن ملفات تعريف الارتباط أو سجل البحث بعد إغلاق جميع علامات التبويب في وضع التصفح المتخفي. ولكن سيتم الاحتفاظ بأي ملفات تنزلها أو إشارات مرجعية تنشئها.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">جزيرة</translation>
-<translation id="317583078218509884">ستسري إعدادات أذونات الموقع بعد إعادة تحميل الصفحة.</translation>
<translation id="3176929007561373547">تحقق من إعدادات الخادم الوكيل أو اتصل بمشرف الشبكة
للتأكد من عمل الخادم الوكيل. فإذا كنت لا تعتقد أنه يجب عليك استخدام
خادم وكيل:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">فتح صفحة في وضع التصفح المتخفي</translation>
<translation id="320323717674993345">إلغاء الدفع</translation>
<translation id="3207960819495026254">محدد بعلامة متابعة القراءة</translation>
<translation id="3225919329040284222">قدم الخادم شهادة لا تتطابق مع التوقعات المضمّنة. تم تضمين هذه التوقعات للحصول على مواقع ويب موثوقة وآمنة جدًا لتوفير الحماية لك.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">تراجع عن إعادة الت&amp;رتيب</translation>
<translation id="3282497668470633863">إضافة الاسم الوارد في البطاقة</translation>
<translation id="3286538390144397061">إعادة التشغيل الآن</translation>
+<translation id="3287510313208355388">التنزيل عند الاتصال</translation>
<translation id="3303855915957856445">لم يتم العثور على أي نتائج بحث</translation>
<translation id="3305707030755673451">تم تشفير بياناتك باستخدام عبارة مرور المزامنة في <ph name="TIME" />. أدخلها لبدء المزامنة.</translation>
<translation id="3320021301628644560">إضافة عنوان إرسال الفواتير</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">الفاصل الزمني للجلب:</translation>
<translation id="3462200631372590220">الإخفاء (خيار متقدم)</translation>
<translation id="3467763166455606212">اسم حامل البطاقة مطلوب</translation>
-<translation id="3478058380795961209">شهر انتهاء الصلاحية</translation>
<translation id="3479539252931486093">ألم تتوقَّع هذا؟ <ph name="BEGIN_LINK" />أطلِعنا على الأمر<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">ليس الآن</translation>
<translation id="3498215018399854026">لم نتمكن من الوصول إلى أحد والديك في الوقت الحالي. يُرجى إعادة المحاولة مرة أخرى.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;يُرجى تعديل التاريخ والوقت من القسم &lt;strong&gt;عام&lt;/strong&gt; في تطبيق &lt;strong&gt;الإعدادات&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">يتم قبول بطاقات الائتمان وبطاقات الدفع المسبق.</translation>
<translation id="3582930987043644930">إضافة اسم</translation>
<translation id="3583757800736429874">إ&amp;عادة النقل</translation>
<translation id="3586931643579894722">إخفاء التفاصيل</translation>
@@ -353,15 +356,15 @@
<translation id="3623476034248543066">عرض القيمة</translation>
<translation id="3630155396527302611">إذا تم بالفعل إدراج صفحة الويب كبرنامج مسموح له بالدخول إلى الشبكة، فجرّب
إزالتها من القائمة وإضافتها مرةً أخرى.</translation>
-<translation id="3648607100222897006">ربما تتغير أو تتعطل أو تختفي هذه الميزات التجريبية في أي وقت. ولا نعطي أي ضمانات حيال ما يمكن حدوثه إذا شغلت إحدى هذه الميزات التجريبية، وقد يتعطل المتصفح بشكل مفاجئ. وبشكل جدي، قد يحذف المتصفح جميع بياناتك، أو قد تتعرض خصوصيتك وأمانك للاختراق بطرق غير متوقعة. أي ميزات تجريبية تمكّنها سيتم تمكينها لجميع مستخدمي هذا المتصفح. الرجاء المتابعة بحذر.</translation>
+<translation id="3648607100222897006">ربما تتغير أو تتعطل أو تختفي هذه الميزات التجريبية في أي وقت. ولا نعطي أي ضمانات حيال ما يمكن حدوثه إذا شغلت إحدى هذه الميزات التجريبية، وقد يتعطل المتصفح بشكل مفاجئ. وبشكل جدي، قد يحذف المتصفح جميع بياناتك، أو قد تتعرض خصوصيتك وأمانك للاختراق بطرق غير متوقعة. أي ميزات تجريبية تمكّنها سيتم تمكينها لجميع مستخدمي هذا المتصفح. يُرجى المتابعة بحذر.</translation>
<translation id="3650584904733503804">تم التحقق بنجاح</translation>
<translation id="3655670868607891010">إذا كنت تشاهد هذا بكثرة، فجرّب <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">التعديل</translation>
<translation id="3678029195006412963">تعذر توقيع الطلب</translation>
+<translation id="3678529606614285348">‏يمكنك فتح الصفحة في نافذة جديدة للتصفح المتخفي (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">تم تسجيل تقرير الأعطال في <ph name="CRASH_TIME" />، وتم تحميله في <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">معلومات الشهادة</translation>
<translation id="3690164694835360974">عملية تسجيل الدخول غير آمنة</translation>
-<translation id="3693415264595406141">كلمة المرور:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">جارٍ التحميل...</translation>
<translation id="3712624925041724820">التراخيص مستنفذة</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">قد يخدعك المهاجمون على <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="375403751935624634">أخفقت الترجمة بسبب حدوث خطأ في الخادم.</translation>
<translation id="3759461132968374835">ليس لديك أي أعطال تم الإبلاغ عنها مؤخرًا. الأعطال التي حدثت عندما تم تعطيل الإبلاغ عن الأعطال لن تظهر هنا.</translation>
+<translation id="3765032636089507299">صفحة التصفح الآمن تحت الإنشاء.</translation>
<translation id="3778403066972421603">‏هل تريد حفظ هذه البطاقة في حسابك في Google وعلى هذا الجهاز؟</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">تنتهي في <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">تمّ إرسال طلبك للدخول إلى هذا الموقع إلى <ph name="NAME" /></translation>
<translation id="3890664840433101773">إضافة بريد إلكتروني</translation>
<translation id="3901925938762663762">انتهت صلاحية البطاقة</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">تحميل مُعرّف تقارير الأعطال <ph name="CRASH_ID" /> (مُعرّف الأعطال المحلية: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">لم يتمكن هذا الخادم من إثبات أنه <ph name="DOMAIN" />؛ بل إن شهادة الأمان التابعة له لا تُحدّد الأسماء البديلة للمضيفات. وربما يكون السبب في ذلك وجود خطأ في التهيئة أو اعتراض أحد المهاجمين للاتصال.</translation>
+<translation id="3949601375789751990">يظهر سجلّ التصفّح هنا</translation>
<translation id="3963721102035795474">وضع القارئ</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{بدون}=1{من موقع واحد }two{من موقعين (#) }few{من # مواقع }many{من # موقعًا }other{من # موقع }}</translation>
<translation id="397105322502079400">جارٍ الحساب...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">إعدادات الموقع</translation>
<translation id="4169947484918424451">‏هل تريد من Chromium حفظ هذه البطاقة؟</translation>
<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="4196861286325780578">إ&amp;عادة النقل</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />التحقق من عمليات تهيئة الجدار الناري وبرامج مكافحة الفيروسات<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">الأعطال</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">قد يحاول المهاجمون الموجودون على هذا الموقع تثبيت برامج خطيرة على الكمبيوتر التابع لك تسرق معلوماتك أو تحذفها (على سبيل المثال، الصور وكلمات المرور والرسائل وبطاقات الائتمان).</translation>
<translation id="4372948949327679948">القيمة <ph name="VALUE_TYPE" /> المتوقعة.</translation>
<translation id="4377125064752653719">لقد حاولت الوصول إلى <ph name="DOMAIN" />، ولكن جهة إصدار الشهادة التي قدمها الخادم قد أبطلت الشهادة. وهذا يعني أن بيانات اعتماد الأمان التي قدمها الخادم يجب عدم الوثوق بها مطلقًا. فقد تكون على اتصال بأحد المهاجمين.</translation>
-<translation id="4381091992796011497">اسم المستخدم:</translation>
<translation id="4394049700291259645">تعطيل</translation>
<translation id="4406896451731180161">نتائج البحث</translation>
<translation id="4424024547088906515">‏هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان غير موثوقة من قبل Chrome. وربما يكون السبب في ذلك خطأ في التكوين أو مهاجمًا يعترض الاتصال.</translation>
<translation id="4432688616882109544">لم يقبل <ph name="HOST_NAME" /> شهادة تسجيل الدخول أو من المحتمل ألا يكون قد تم تقديم واحدة.</translation>
<translation id="443673843213245140">تم تعطيل استخدام الخادم الوكيل ولكن تم تحديد تهيئة صريحة للخادم الوكيل.</translation>
+<translation id="445100540951337728">بطاقات السحب الآلي المقبولة</translation>
<translation id="4506176782989081258">خطأ في عملية التحقق: <ph name="VALIDATION_ERROR" />.</translation>
<translation id="4506599922270137252">الاتصال بمشرف النظام</translation>
<translation id="450710068430902550">المشاركة مع المشرف</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">‏هل تريد إزالة العنوان من Chrome؟</translation>
<translation id="4592951414987517459">يتم ترميز اتصالك بالنطاق <ph name="DOMAIN" /> باستخدام مجموعة تشفير حديثة.</translation>
<translation id="4594403342090139922">تراجع عن الحذ&amp;ف</translation>
+<translation id="4611292653554630842">تسجيل الدخول</translation>
<translation id="4619615317237390068">علامات التبويب من الأجهزة الأخرى</translation>
<translation id="4668929960204016307">،</translation>
<translation id="467662567472608290">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان تحتوي على أخطاء. وربما يكون السبب في ذلك خطأ في التكوين أو مهاجمًا يعترض اتصالك.</translation>
<translation id="4690462567478992370">التوقف عن استخدام شهادة غير صالحة</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" />‏ <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">تم قطع اتصالك</translation>
+<translation id="471880041731876836">ليس لديك إذن لزيارة هذا الموقع</translation>
<translation id="4722547256916164131">‏<ph name="BEGIN_LINK" />تشغيل بيانات تشخيص شبكة Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">إعادة تحميل السياسات</translation>
<translation id="4728558894243024398">النظام الأساسي</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">عرض</translation>
<translation id="4854362297993841467">طريقة التسليم هذه غير متاحة. جرِّب طريقة أخرى.</translation>
<translation id="4858792381671956233">لقد سألت والديك ما إذا كانت زيارة هذا الموقع مناسبةً لك</translation>
-<translation id="4863764087567530506">قد يحاول هذا المحتوى خداعك لتنزيل برامج أو الكشف عن معلومات شخصية. <ph name="BEGIN_LINK" />عرض على أي حال<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">سجلّ البحث</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />، <ph name="TYPE_2" />، <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">المصادقة مطلوبة</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{وصفحة ويب واحدة إضافية}zero{و# صفحة ويب إضافية}two{وصفحتا ويب (#) إضافيتان}few{و# صفحات ويب إضافية}many{و# صفحة ويب إضافية}other{و# صفحة ويب إضافية}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">تمت ترجمة هذه الصفحة من لغة غير معروفة إلى اللغة <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">الدفع</translation>
<translation id="4926049483395192435">يجب تحديدها.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">إجراءات</translation>
<translation id="4958444002117714549">توسيع القائمة</translation>
<translation id="4974590756084640048">إعادة تمكين التحذيرات</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">كلمة مرور غير صحيحة</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>
<translation id="5087580092889165836">إضافة بطاقة</translation>
<translation id="5089810972385038852">بلد/دولة</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">الإقليم</translation>
<translation id="5115563688576182185">(64 بت)</translation>
<translation id="5141240743006678641">‏تشفير كلمات المرور المتزامنة باستخدام بيانات اعتماد Google</translation>
-<translation id="514421653919133810">‏فتح صفحة في وضع التصفح المتخفي (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">يوجد رمز خطأ في استجابة السياسة</translation>
+<translation id="5159010409087891077">‏يمكنك فتح الصفحة في نافذة جديدة للتصفح المتخفي (⇧⌘N)</translation>
<translation id="5171045022955879922">‏البحث أو إدخال عنوان URL</translation>
<translation id="5172758083709347301">الجهاز</translation>
<translation id="5179510805599951267">هل الصفحة ليست باللغة <ph name="ORIGINAL_LANGUAGE" />؟ الإبلاغ عن هذا الخطأ</translation>
-<translation id="5181140330217080051">تنزيل</translation>
<translation id="5190835502935405962">شريط الإشارات</translation>
<translation id="5199729219167945352">التجارب</translation>
<translation id="5205222826937269299">الاسم مطلوب</translation>
<translation id="5222812217790122047">البريد الإلكتروني مطلوب</translation>
<translation id="5251803541071282808">السحاب</translation>
<translation id="5277279256032773186">‏هل تستخدم Chrome في العمل؟ يمكن للأنشطة التجارية إدارة إعدادات Chrome لموظفيها. تعرَّف على المزيد</translation>
+<translation id="5281113152797308730">‏<ph name="BEGIN_PARAGRAPH" />اتبع هذه الخطوات لتعطيل البرامج مؤقتًا حتى يتسنى لك الوصول إلى الويب. سيلزمك الحصول على امتيازات المشرف.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />انقر على <ph name="BEGIN_BOLD" />البدء<ph name="END_BOLD" />، ثم ابحث عن <ph name="BEGIN_BOLD" />"عرض الخدمات المحلية"<ph name="END_BOLD" /> وحدِّده
+ <ph name="LIST_ITEM" />حدِّد <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />ضمن <ph name="BEGIN_BOLD" />نوع بدء التشغيل<ph name="END_BOLD" />، حدِّد <ph name="BEGIN_BOLD" />معطَّل<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />ضمن <ph name="BEGIN_BOLD" />حالة التشغيل<ph name="END_BOLD" />، انقر على <ph name="BEGIN_BOLD" />إيقاف<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />انقر على <ph name="BEGIN_BOLD" />تطبيق<ph name="END_BOLD" />، ثم انقر على <ph name="BEGIN_BOLD" />موافق<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />تفضَّل بزيارة <ph name="BEGIN_LEARN_MORE_LINK" />مركز مساعدة Chrome<ph name="END_LEARN_MORE_LINK" /> للتعرُّف على كيفية إزالة البرامج من جهاز الكمبيوتر نهائيًا
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">‏اتصالك بهذا الموقع ليس له خصوصية. للخروج من وضع الواقع الافتراضي (VR) في أي وقت، أزل سماعة الرأس واضغط على رجوع.</translation>
<translation id="5299298092464848405">خطأ في تحليل السياسة</translation>
<translation id="5308689395849655368">ميزة الإبلاغ عن الأعطال معطلة.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">حدث خطأ في مصادقة المخطط على "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">لا يمكن العثور على صفحة <ph name="HOST_NAME" /> هذه.</translation>
<translation id="5455374756549232013">الطابع الزمني للسياسة سيئ</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> من <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">غير صالحة</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> وجهة اتصال <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> أخرى}two{<ph name="CONTACT_PREVIEW" /> وجهتا اتصال (<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />) أخريان}few{<ph name="CONTACT_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> جهات اتصال أخرى}many{<ph name="CONTACT_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> جهة اتصال أخرى}other{<ph name="CONTACT_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> جهة اتصال أخرى}}</translation>
<translation id="5470861586879999274">إعادة الت&amp;حرير</translation>
+<translation id="5481076368049295676">قد يحاول هذا المحتوى تثبيت برامج خطيرة على جهازك تسرق معلوماتك أو تحذفها. <ph name="BEGIN_LINK" />عرض على أي حال<ph name="END_LINK" /></translation>
<translation id="54817484435770891">إضافة عنوان صالح</translation>
<translation id="5492298309214877701">‏يكون لموقع الويب هذا على الشبكة الداخلية للشركة أو المؤسسة أو المدرسة نفس عنوان URL لأحد مواقع الويب الخارجية.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">تعذر العثور على المقالة المطلوبة</translation>
<translation id="5544037170328430102">صفحة مُضمنة في <ph name="SITE" /> تعرض:</translation>
<translation id="5556459405103347317">إعادة تحميل</translation>
+<translation id="5560088892362098740">تاريخ انتهاء الصلاحية</translation>
<translation id="5565735124758917034">نشط</translation>
<translation id="5571083550517324815">لا يمكن الاستلام من هذا العنوان. حدِّد عنوانًا آخر.</translation>
<translation id="5572851009514199876">‏يُرجى البدء وتسجيل الدخول إلى Chrome لكي يتأكد Chrome مما إذا كان مسموحًا لك الوصول إلى موقع الويب هذا أم لا.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">تعذّر تحميل إعدادات السياسة</translation>
<translation id="5631439013527180824">الرمز المميز لإدارة الجهاز غير صالح</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="5646376287012673985">الموقع</translation>
<translation id="5659593005791499971">البريد الإلكتروني</translation>
<translation id="5669703222995421982">الحصول على محتوى مخصص</translation>
<translation id="5675650730144413517">يتعذّر على هذه الصفحة العمل</translation>
<translation id="5710435578057952990">لم يتمّ التحقق من هوية هذا الموقع.</translation>
-<translation id="5713016350996637505">تم حظر المحتوى المُضلل</translation>
+<translation id="5719499550583120431">يتم قبول بطاقات الدفع المسبق.</translation>
<translation id="5720705177508910913">المستخدم الحالي</translation>
<translation id="5732392974455271431">يمكن لوالديك إلغاء الحظر لك</translation>
<translation id="5763042198335101085">أدخِل عنوان بريد إلكتروني صحيحًا</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">هل تريد ملء <ph name="CARD_DETAIL" />؟</translation>
<translation id="5810442152076338065">يتم ترميز اتصالك بالنطاق <ph name="DOMAIN" /> باستخدام مجموعة تشفير قديمة.</translation>
<translation id="5813119285467412249">إعا&amp;دة الإضافة</translation>
-<translation id="5814352347845180253">قد تفقد إمكانية الدخول إلى محتوى متميز من <ph name="SITE" /> وبعض المواقع الأخرى.</translation>
<translation id="5838278095973806738">يجب عدم إدخال معلومات حسّاسة على هذا الموقع (على سبيل المثال، كلمات المرور أو بطاقات الائتمان)، نظرًا لأنه قد تتم سرقتها من قِبل المهاجمين.</translation>
<translation id="5869405914158311789">لا يمكن الوصول إلى موقع الويب هذا</translation>
<translation id="5869522115854928033">كلمات المرور المحفوظة</translation>
<translation id="5872918882028971132">اقتراحات الآباء</translation>
+<translation id="5893752035575986141">يتم قبول بطاقات الائتمان.</translation>
<translation id="5901630391730855834">أصفر</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="5926846154125914413">قد تفقد إمكانية الدخول إلى محتوى متميز من بعض المواقع.</translation>
<translation id="5959728338436674663">‏يمكنك إرسال بعض <ph name="BEGIN_WHITEPAPER_LINK" />معلومات النظام ومحتوى الصفحة<ph name="END_WHITEPAPER_LINK" /> إلى Google تلقائيًا للمساعدة في اكتشاف التطبيقات والمواقع الضارة. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">إزالة من السجل</translation>
<translation id="5975083100439434680">تصغير</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">إغلاق</translation>
<translation id="6042308850641462728">المزيد</translation>
<translation id="6047233362582046994">إذا كنت على دراية بالمخاطر التي تهدد أمانك، يمكنك <ph name="BEGIN_LINK" />زيارة موقع الويب هذا<ph name="END_LINK" /> قبل أن تتم إزالة التطبيقات الضارة.</translation>
+<translation id="6047927260846328439">قد يحاول هذا المحتوى خداعك لتثبيت برامج أو الكشف عن معلومات شخصية. <ph name="BEGIN_LINK" />عرض على أي حال<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">لا يمكنك زيارة <ph name="SITE" /> في الوقت الحالي لأن الموقع يستخدم أداة التحقق من صحة الشهادات. أخطاء الشبكة والهجمات عليها عادةً ما تكون مؤقتة، لذا ستعمل هذه الصفحة في وقت لاحق على الأرجح.</translation>
<translation id="6060685159320643512">احذر، هذه التجارب غير مضمونة النتائج</translation>
<translation id="6080696365213338172">لقد دخلت إلى المحتوى باستخدام شهادة وفرها المشرف. ويمكن أن يعترض المشرف طريق البيانات التي تقدمها إلى <ph name="DOMAIN" />.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">مزيد من المعلومات</translation>
<translation id="6169916984152623906">يمكنك الآن التصفح بشكلٍ سري، ولن يتمكّن الأشخاص الآخرون الذين يستخدمون هذا الجهاز من مشاهدة نشاطك. ولكن، سيتم حفظ عمليات التنزيل والإشارات المرجعيّة.</translation>
<translation id="6177128806592000436">إن اتصالك بهذا الموقع غير آمن</translation>
-<translation id="6184817833369986695">(مجموعة نموذجية: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">التحقق من اتصالك بالإنترنت</translation>
<translation id="6218753634732582820">‏هل تريد إزالة العنوان من Chromium؟</translation>
<translation id="6221345481584921695">‏اكتشف التصفح الآمن من Google‬ مؤخرًا <ph name="BEGIN_LINK" />برامج ضارة<ph name="END_LINK" /> على <ph name="SITE" />. أحيانًا تصاب مواقع الويب الآمنة في الوضع العادي ببرامج ضارة. مصدر محتوى البرامج الضارة <ph name="SUBRESOURCE_HOST" />، وهو ناشر معروف للبرامج الضارة.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">‏إذا كنت مهتمًا بميزات Google الجديدة والرائعة، فيُمكنك تجربة القناة التجريبية على chrome.com/beta.</translation>
-<translation id="6389758589412724634">‏نفدت ذاكرة Chromium أثناء محاولة عرض صفحة الويب هذه.</translation>
+<translation id="6397451950548600259">‏تعمل البرامج على جهاز الكمبيوتر على منع اتصال Chrome بأمان بالويب</translation>
<translation id="6404511346730675251">تعديل إشارة مرجعية</translation>
<translation id="6410264514553301377">‏أدخِل تاريخ انتهاء الصلاحية ورمز التحقق من البطاقة (CVC) لـ <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">لقد سألت والديك ما إذا كانت زيارة هذا الموقع مناسبةً لك</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">سياسات الأجهزة</translation>
<translation id="6477321094435799029">‏اكتشف Chrome وجود رمز غير عادي على هذه الصفحة وأجرى حظرًا لهذا الرمز لحماية معلوماتك الشخصية (على سبيل المثال، كلمات المرور، وأرقام الهواتف، وبطاقات الائتمان).</translation>
<translation id="6489534406876378309">بدء تحميل الأعطال</translation>
+<translation id="6507833130742554667">يتم قبول بطاقات الائتمان وبطاقات السحب الآلي.</translation>
<translation id="6508722015517270189">‏إعادة تشغيل Chrome</translation>
<translation id="6529602333819889595">إعادة الح&amp;ذف</translation>
<translation id="6534179046333460208">اقتراحات الشبكة المادية</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">قد يحاول هذا المحتوى تثبيت برامج خطيرة على جهازك تسرق معلوماتك أو تحذفها. <ph name="BEGIN_LINK" />عرض على أي حال<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">خيارات التشفير</translation>
<translation id="662080504995468778">البقاء</translation>
<translation id="6626291197371920147">إضافة رقم بطاقة صالح</translation>
<translation id="6628463337424475685">بحث <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">‏قد يحاول المهاجمون حاليًا على <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> تثبيت برامج خطيرة على جهاز Mac تؤدي إلى سرقة أو حذف معلوماتك (على سبيل المثال، الصور، وكلمات المرور، والرسائل، وبطاقات الائتمان) <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">تم تجاهل هذه السياسة.</translation>
+<translation id="6657585470893396449">كلمة المرور</translation>
<translation id="6671697161687535275">‏هل تريد إزالة اقتراح النموذج من Chromium؟</translation>
<translation id="6685834062052613830">الخروج وإكمال الإعداد</translation>
<translation id="6710213216561001401">السابق</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">المنطقة</translation>
<translation id="6973656660372572881">‏تم تحديد كل من الخوادم الوكيلة الثابتة وعنوان URL للنص البرمجي pac.</translation>
<translation id="6989763994942163495">عرض الإعدادات المتقدمة...</translation>
-<translation id="7000990526846637657">لم يتم العثور على أي إدخالات في السجلّ</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">‏قد يتضمن حسابك في Google نماذج أخرى من سجلّ التصفح في <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">كلمات المرور</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">لا يمكن الشحن على هذا العنوان. حدِّد عنوانًا آخر.</translation>
<translation id="7138472120740807366">طريقة التسليم</translation>
<translation id="7139724024395191329">الإمارة</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> وطريقة دفع <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> أخرى}two{<ph name="PAYMENT_METHOD_PREVIEW" /> وطريقتا دفع <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> أخريان}few{<ph name="PAYMENT_METHOD_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> طرق دفع أخرى}many{<ph name="PAYMENT_METHOD_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> طريقة دفع أخرى}other{<ph name="PAYMENT_METHOD_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> طريقة دفع أخرى}}</translation>
<translation id="7155487117670177674">عملية الدفع غير آمنة</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> وخيار <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> آخر}two{<ph name="SHIPPING_OPTION_PREVIEW" /> وخياران (<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />) آخران}few{<ph name="SHIPPING_OPTION_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> خيارات أخرى}many{<ph name="SHIPPING_OPTION_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> خيارًا آخر}other{<ph name="SHIPPING_OPTION_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> خيار آخر}}</translation>
<translation id="7179921470347911571">إعادة التشغيل الآن</translation>
<translation id="7180611975245234373">تحديث</translation>
<translation id="7182878459783632708">لم يتم تعيين أية سياسات</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">تنزيل</translation>
<translation id="7518003948725431193">لم يتم العثور على أي صفحة ويب لعنوان الويب:<ph name="URL" /></translation>
<translation id="7521387064766892559">جافا سكريبت</translation>
+<translation id="7526934274050461096">اتصالك بموقع الويب هذا لا يتمتع بخصوصية</translation>
<translation id="7535087603100972091">القيمة</translation>
<translation id="7537536606612762813">إلزامية</translation>
<translation id="7542403920425041731">بعد تأكيدك، ستتم مشاركة تفاصيل بطاقتك مع موقع الويب هذا.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">جرّب النصائح التالية:</translation>
<translation id="7554791636758816595">علامة تبويب جديدة</translation>
<translation id="7567204685887185387">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان تم إصدارها عن طريق الاحتيال. وربما يكون سبب ذلك خطأ في التكوين أو مهاجمًا يعترض اتصالك.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> وجهة اتصال واحدة (<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />) أخرى}zero{<ph name="CONTACT_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> جهة اتصال أخرى}two{<ph name="CONTACT_PREVIEW" /> وجهتا اتصال (<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />) أخريان}few{<ph name="CONTACT_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> جهات اتصال أخرى}many{<ph name="CONTACT_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> جهة اتصال أخرى}other{<ph name="CONTACT_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> جهة اتصال أخرى}}</translation>
<translation id="7568593326407688803">تتوفر هذه الصفحة باللغة<ph name="ORIGINAL_LANGUAGE" />فهل تريد ترجمتها؟</translation>
<translation id="7569952961197462199">‏هل تريد إزالة بطاقة الائتمان من Chrome؟</translation>
<translation id="7569983096843329377">أسود</translation>
@@ -784,7 +810,7 @@
<translation id="7600965453749440009">عدم الترجمة مطلقًا من اللغة <ph name="LANGUAGE" /></translation>
<translation id="7610193165460212391">القيمة خارج النطاق <ph name="VALUE" />.</translation>
<translation id="7613889955535752492">تاريخ انتهاء الصلاحية: <ph name="EXPIRATION_MONTH" /> / <ph name="EXPIRATION_YEAR" /></translation>
-<translation id="7615602087246926389">‏لديك فعلاً بيانات تم تشفيرها باستخدام نسخة أخرى من كلمة مرور حسابك في Google. الرجاء إدخالها أدناه.</translation>
+<translation id="7615602087246926389">‏لديك فعلاً بيانات تم تشفيرها باستخدام نسخة أخرى من كلمة مرور حسابك في Google. يُرجى إدخالها أدناه.</translation>
<translation id="7637571805876720304">‏هل تريد إزالة بطاقة الائتمان من Chromium؟</translation>
<translation id="765676359832457558">إخفاء الإعدادات المتقدمة...</translation>
<translation id="7658239707568436148">إلغاء</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">شهادة</translation>
<translation id="7716147886133743102">تم الحظر من قبل المشرف</translation>
<translation id="7716424297397655342">لا يمكن تحميل موقع الويب هذا من ذاكرة التخزين المؤقت</translation>
+<translation id="774634243536837715">تم حظر المحتوى الخطير.</translation>
<translation id="7752995774971033316">غير مُدار</translation>
<translation id="7755287808199759310">قد يلغي والداك الحظر لك</translation>
<translation id="7758069387465995638">ربما حظر الجدار الناري أو برامج مكافحة الفيروسات الاتصال.</translation>
+<translation id="7759163816903619567">نطاق العرض:</translation>
<translation id="7761701407923456692">‏لا تتطابق شهادة الخادم مع عنوان URL.</translation>
<translation id="7763386264682878361">المحلل اللغوي لبيان الدفع</translation>
<translation id="7764225426217299476">إضافة عنوان</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">تم العثور على <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> لـ "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">ومع ذلك، أنت غير مرئي. لا يخفي الانتقال إلى وضع التخفي التصفح من صاحب العمل أو مزود خدمة الإنترنت أو المواقع التي تزورها.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">يتم قبول بطاقات السحب الآلي وبطاقات الدفع المسبق.</translation>
<translation id="7887683347370398519">‏تحقق من رمز التحقق من البطاقة (CVC) ثم أعد المحاولة.</translation>
<translation id="79338296614623784">أدخِل رقم هاتف صحيحًا</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">إرسال تعليقات</translation>
<translation id="8041940743680923270">استخدام الإعداد الافتراضي العمومي (طلب)</translation>
<translation id="8088680233425245692">تعذّر عرض المقالة.</translation>
-<translation id="8089520772729574115">أقل من ميغابايت واحدة</translation>
<translation id="8091372947890762290">التنشيط قيد الانتظار في الخادم</translation>
<translation id="8118489163946903409">طريقة الدفع</translation>
<translation id="8131740175452115882">التأكيد</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">تراجع عن ال&amp;نقل</translation>
<translation id="8201077131113104583">‏عنوان URL لتحديث الإضافة التي تحتوي على رقم التعريف "<ph name="EXTENSION_ID" />" غير صالح.</translation>
<translation id="8202097416529803614">ملخص الطلب</translation>
+<translation id="8205463626947051446">يميل الموقع إلى عرض إعلانات متداخلة</translation>
<translation id="8218327578424803826">الموقع الذي تم تعيينه:</translation>
<translation id="8225771182978767009">اختار الشخص الذي أعد جهاز الكمبيوتر حظر موقع الويب هذا.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />، <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">يمكنك فتح الصفحة في علامة تبويب جديدة للتصفح المتخفي</translation>
<translation id="8241707690549784388">استخدمت الصفحة التي تبحث عنها المعلومات التي أدخلتها وقد يؤدّي الرجوع إليها إلى تكرار جميع الإجراءات السابقة. هل تريد المتابعة؟</translation>
+<translation id="8241712895048303527">الحظر على هذا الموقع</translation>
<translation id="8249320324621329438">تاريخ آخر عملية جلب:</translation>
<translation id="8253091569723639551">عنوان إرسال الفواتير مطلوب</translation>
<translation id="8261506727792406068">حذف</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">أخفقت الترجمة بسبب حدوث مشكلة في الاتصال بالشبكة.</translation>
<translation id="8332188693563227489">تم رفض الدخول إلى <ph name="HOST_NAME" />.</translation>
<translation id="834457929814110454">إذا كنت على دراية بالمخاطر التي تهدد أمانك، يمكنك <ph name="BEGIN_LINK" />زيارة هذا الموقع<ph name="END_LINK" /> قبل أن تتم إزالة البرامج الضارة.</translation>
-<translation id="8344669043927012510">‏فتح صفحة في وضع التصفح المتخفي (‎⇧⌘N)</translation>
<translation id="8349305172487531364">شريط الإشارات</translation>
<translation id="8363502534493474904">إيقاف تشغيل وضع الطائرة</translation>
<translation id="8364627913115013041">لم يتم تعيينها.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">تم تحميل تقرير الأعطال في <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">الأعطال (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">يجب إدخال عبارة المرور نفسها مرتين.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">إعدادات</translation>
<translation id="8433057134996913067">سيؤدي هذا إلى خروجك من معظم مواقع الويب.</translation>
<translation id="8437238597147034694">تراجع عن ال&amp;نقل</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">‏لاستخدام البطاقات من حسابك في Google، يُرجى تسجيل الدخول إلى Chrome</translation>
<translation id="8488350697529856933">تنطبق على</translation>
<translation id="8498891568109133222">استغرق <ph name="HOST_NAME" /> وقتًا أطول مما يجب للاستجابة.</translation>
-<translation id="8532105204136943229">عام انتهاء الصلاحية</translation>
+<translation id="8503813439785031346">اسم المستخدم</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="8571890674111243710">جارٍ ترجمة الصفحة إلى <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">إضافة رقم هاتف
@@ -887,6 +919,7 @@
<translation id="859285277496340001">لا تحدد الشهادة آلية للتحقق مما إذا كانت الشهادة قد تم إبطالها.</translation>
<translation id="8620436878122366504">لم يوافق عليه والداك حتى الآن</translation>
<translation id="8647750283161643317">إعادة التعيين على الإعدادات الافتراضية</translation>
+<translation id="8660471606262461360">‏من Google Payments</translation>
<translation id="8703575177326907206">الاتصال بالموقع <ph name="DOMAIN" /> غير محميّ بنظام تشفير.</translation>
<translation id="8718314106902482036">لم تكتمل عملية الدفع</translation>
<translation id="8725066075913043281">أعد المحاولة</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">تكبير</translation>
<translation id="8931333241327730545">‏هل تريد حفظ هذه البطاقة إلى حسابك في Google؟</translation>
<translation id="8932102934695377596">توقيت ساعتك متأخر عن الوقت الحالي</translation>
+<translation id="8938939909778640821">بطاقات الائتمان وبطاقات الدفع المسبق المقبولة</translation>
<translation id="8971063699422889582">انتهت صلاحية شهادة الخادم.</translation>
<translation id="8986494364107987395">‏إرسال إحصائيات الاستخدام وتقارير الأعطال إلى Google تلقائيًا</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" />‏ [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">الحظر دومًا على هذا الموقع</translation>
<translation id="9170848237812810038">&amp;إلغاء</translation>
<translation id="917450738466192189">شهادة الخادم غير صالحة.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> وخيار واحد (<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />) آخر}zero{<ph name="SHIPPING_OPTION_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> خيار آخر}two{<ph name="SHIPPING_OPTION_PREVIEW" /> وخياران (<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />) آخران}few{<ph name="SHIPPING_OPTION_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> خيارات أخرى}many{<ph name="SHIPPING_OPTION_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> خيارًا آخرًا}other{<ph name="SHIPPING_OPTION_PREVIEW" /> و<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> خيار آخر}}</translation>
<translation id="9183425211371246419">يستخدم <ph name="HOST_NAME" /> بروتوكول غير مدعوم.</translation>
<translation id="9205078245616868884">يتم تشفير بياناتك باستخدام عبارة مرور المزامنة. أدخلها لبدء المزامنة.</translation>
<translation id="9207861905230894330">أخفقت إضافة مقالة.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">محو النموذج</translation>
<translation id="939736085109172342">مجلد جديد</translation>
-<translation id="941721044073577244">يبدو أنه ليس لديك إذن بالدخول إلى هذا الموقع</translation>
<translation id="969892804517981540">البنية الرسمية</translation>
<translation id="975560348586398090">{COUNT,plural, =0{بدون}=1{عنصر واحد}two{عنصران (#)}few{# عناصر}many{# عنصرًا}other{# عنصر}}</translation>
+<translation id="981121421437150478">بلا اتصال</translation>
<translation id="988159990683914416">بنية المطوِّر</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_bg.xtb b/chromium/components/strings/components_strings_bg.xtb
index acf9186f0db..c8b2f8f687a 100644
--- a/chromium/components/strings/components_strings_bg.xtb
+++ b/chromium/components/strings/components_strings_bg.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Затворете другите приложения.</translation>
<translation id="1055184225775184556">&amp;Отмяна на добавянето</translation>
<translation id="10614374240317010">Незапазвани никога</translation>
+<translation id="1066396345355680611">Може да загубите достъп до защитено съдържание от <ph name="SITE" /> и някои други сайтове.</translation>
<translation id="106701514854093668">Настолни отметки</translation>
<translation id="1074497978438210769">Няма защита</translation>
<translation id="1080116354587839789">Побиране на ширина</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Списък за четене</translation>
<translation id="1264126396475825575">Сигнал за срив, записан в/ъв <ph name="CRASH_TIME" /> (още не е качен или пренебрегнат)</translation>
<translation id="1281526147609854549">Издадено от <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Блокирахме опасно съдържание</translation>
<translation id="1285320974508926690">Този сайт да не се превежда никога</translation>
<translation id="129553762522093515">Наскоро затворени</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Опитайте да изчистите „бисквитките“ си<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Домейн за записване:</translation>
<translation id="1340482604681802745">Адрес за вземане</translation>
-<translation id="1344211575059133124">Изглежда, че се нуждаете от разрешение, за да посетите този сайт</translation>
<translation id="1344588688991793829">Настройки за Автоматично попълване в Chromium...</translation>
<translation id="1348198688976932919">На хоризонта се задава сайт с опасни приложения</translation>
<translation id="1374468813861204354">предложения</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Самоличността на <ph name="ORGANIZATION" /> от <ph name="LOCALITY" /> е проверена от <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Да</translation>
<translation id="1430915738399379752">Печат</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Показване на запазено копие на тази страница (за което е известно, че не е актуално).</translation>
<translation id="1517433312004943670">Телефонният номер е задължителен</translation>
+<translation id="1517500485252541695">Приемани кредитни и дебитни карти</translation>
<translation id="1519264250979466059">Дата на версията</translation>
+<translation id="1527263332363067270">Изчаква се връзка…</translation>
<translation id="153384715582417236">Това е всичко засега</translation>
<translation id="1549470594296187301">Трябва да активирате JavaScript, за да използвате тази функция.</translation>
<translation id="1555130319947370107">синьо</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Свържете се със системния администратор.</translation>
<translation id="1740951997222943430">Въведете валиден месец на изтичане</translation>
-<translation id="1745358365027406341">Изтегляне на страницата по-късно</translation>
<translation id="17513872634828108">Отворени раздели</translation>
<translation id="1753706481035618306">Номер на страницата</translation>
<translation id="1763864636252898013">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; операционната система на устройството ви няма доверие на сертификата му за сигурност. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Задължително поле</translation>
<translation id="187918866476621466">Отваряне на страниците при стартиране</translation>
<translation id="1883255238294161206">Свиване на списъка</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Филтриране</translation>
+<translation id="1916770123977586577">Презаредете страницата, за да приложите актуализираните си настройки към този сайт</translation>
+<translation id="1919345977826869612">Реклами</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Няма}=1{1 сайт}other{# сайта}}</translation>
<translation id="194030505837763158">Към <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Приемани предплатени карти</translation>
<translation id="1962204205936693436">Отметки от <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Грешка при сериализирането</translation>
<translation id="1974060860693918893">Разширени</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP грешка</translation>
<translation id="2270484714375784793">Телефонен номер</translation>
<translation id="2282872951544483773">Експерименти, които не са налице</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> елемент}other{<ph name="ITEM_COUNT" /> елемента}}</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>
<translation id="2337852623177822836">Настройката се контролира от администратора ви</translation>
<translation id="2354001756790975382">Други отметки</translation>
<translation id="2354430244986887761">Google Безопасно сърфиране наскоро <ph name="BEGIN_LINK" />откри опасни приложения<ph name="END_LINK" /> на <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Напред</translation>
<translation id="2365563543831475020">Сигналът за срив, записан в/ъв <ph name="CRASH_TIME" />, не бе качен</translation>
<translation id="2367567093518048410">Ниво</translation>
-<translation id="237718015863234333">Няма алтернативи на потребителския интерфейс</translation>
<translation id="2384307209577226199">Зададено по подразбиране в корпоративна среда</translation>
<translation id="2386255080630008482">Сертификатът на сървъра е анулиран.</translation>
<translation id="2392959068659972793">Да се показват правилата без зададена стойност</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Ако поставите отметка, Chromium ще съхранява на това устройство копие на картата ви с цел по-бързо попълване на формуляри.</translation>
<translation id="2498091847651709837">Сканиране на нова карта</translation>
<translation id="2501278716633472235">Назад</translation>
+<translation id="2503184589641749290">Приемани дебитни и предплатени карти</translation>
<translation id="2515629240566999685">Проверете сигнала в района.</translation>
-<translation id="2516305470678292029">Алтернативи на потребителския интерфейс</translation>
<translation id="2539524384386349900">Откриване</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> изпрати невалиден отговор.</translation>
<translation id="2556876185419854533">&amp;Отмяна на редактирането</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Начин на доставка</translation>
<translation id="277499241957683684">Липсващ запис за устройството</translation>
<translation id="2784949926578158345">Връзката бе възстановена.</translation>
+<translation id="2788784517760473862">Приемани кредитни карти</translation>
<translation id="2794233252405721443">Сайтът е блокиран</translation>
<translation id="2799020568854403057">На хоризонта се задава сайт с опасни приложения</translation>
<translation id="2803306138276472711">Google Безопасно сърфиране наскоро <ph name="BEGIN_LINK" />откри злонамерен софтуер<ph name="END_LINK" /> на <ph name="SITE" />. Уебсайтовете, които обикновено са надеждни, понякога се заразяват с опасен софтуер.</translation>
<translation id="2824775600643448204">Лента за адреси и за търсене</translation>
<translation id="2826760142808435982">Връзката е шифрована и удостоверена посредством <ph name="CIPHER" /> и използва <ph name="KX" /> като механизъм за обмен на ключове.</translation>
<translation id="2835170189407361413">Изчистване на формуляра</translation>
+<translation id="2851634818064021665">Необходимо ви е разрешение, за да посетите този сайт</translation>
<translation id="2856444702002559011">Възможно е извършители на атака да опитват да откраднат информацията ви от <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (например пароли, съобщения или номера на кредитни карти). <ph name="BEGIN_LEARN_MORE_LINK" />Научете повече<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Без презареждане</translation>
-<translation id="2900469785430194048">Паметта на Google Chrome се изчерпа, докато браузърът опитваше да покаже тази уеб страница.</translation>
<translation id="2909946352844186028">Установена бе промяна в мрежата.</translation>
<translation id="2916038427272391327">Затворете другите програми.</translation>
<translation id="2922350208395188000">Сертификатът на сървъра не може да бъде проверен.</translation>
<translation id="2928905813689894207">Адрес за фактуриране</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; сертификатът му за сигурност е от <ph name="DOMAIN2" />. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
<translation id="2948083400971632585">Можете да деактивирате всички конфигурирани за дадена връзка прокси сървъри от страницата „Настройки“.</translation>
<translation id="2955913368246107853">Затваряне на лентата за търсене</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Ако настройката е активирана, Chromium ще съхранява на това устройство копие на картата ви с цел по-бързо попълване на формуляри.</translation>
<translation id="2985398929374701810">Въведете валиден адрес</translation>
<translation id="2986368408720340940">Този начин на вземане не се поддържа. Опитайте с друг.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Страниците, които преглеждате в разделите в режим „инкогнито“, няма да останат в историята на браузъра, хранилището за „бисквитки“ или историята на търсенето, след като затворите всички раздели в този режим. Изтеглените от вас файлове или създадените от вас отметки обаче ще бъдат запазени.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Остров</translation>
-<translation id="317583078218509884">Новите настройки за разрешенията за сайтове ще влязат в сила след презареждането на страницата.</translation>
<translation id="3176929007561373547">За да се уверите, че прокси сървърът работи,
проверете настройките му или се свържете със системния си администратор. Ако смятате, че не трябва
да използвате прокси сървър:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Отворете страницата в режим „инкогнито“.</translation>
<translation id="320323717674993345">Анулиране на плащането</translation>
<translation id="3207960819495026254">С отметка</translation>
<translation id="3225919329040284222">Сървърът предостави сертификат, който не съответства на вградените очаквания. Те са включени за определени уебсайтове с голяма степен на сигурност, за да ви предпазим.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Отмяна на пренареждането</translation>
<translation id="3282497668470633863">Добавяне на името на картодържателя</translation>
<translation id="3286538390144397061">Рестартиране сега</translation>
+<translation id="3287510313208355388">Изтегляне, когато съм онлайн</translation>
<translation id="3303855915957856445">Няма намерени резултати от търсенето</translation>
<translation id="3305707030755673451">На <ph name="TIME" /> данните ви бяха шифровани с пропуска ви за синхронизиране. Въведете го, за да стартирате синхронизирането.</translation>
<translation id="3320021301628644560">Добавяне на адреса за фактуриране</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Интервал на извличане:</translation>
<translation id="3462200631372590220">Скриване на подробностите</translation>
<translation id="3467763166455606212">Трябва да въведете името на титуляря на картата</translation>
-<translation id="3478058380795961209">Месец на валидност</translation>
<translation id="3479539252931486093">Това неочаквано ли беше? <ph name="BEGIN_LINK" />Уведомете ни<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Не сега</translation>
<translation id="3498215018399854026">Не можахме да се свържем с родителя ви. Моля, опитайте отново.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Моля, коригирайте датата и часа от секцията &lt;strong&gt;General&lt;/strong&gt; на приложението &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Научете повече<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Приемат се кредитни и предплатени карти.</translation>
<translation id="3582930987043644930">Добавете име</translation>
<translation id="3583757800736429874">&amp;Възстановяване на преместването</translation>
<translation id="3586931643579894722">Скриване на подробностите</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Ако виждате това често, опитайте <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Ревизия</translation>
<translation id="3678029195006412963">Заявката не можа да бъде подписана</translation>
+<translation id="3678529606614285348">Отворете страницата в нов прозорец в режим „инкогнито“ (Ctrl-Shift-N).</translation>
<translation id="3679803492151881375">Сигналът за срив е записан в/ъв <ph name="CRASH_TIME" /> и качен в/ъв <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Информация за сертификата</translation>
<translation id="3690164694835360974">Страницата за вход не е защитена</translation>
-<translation id="3693415264595406141">Парола:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> „<ph name="TITLE" />“ <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Зарежда се...</translation>
<translation id="3712624925041724820">Лицензите са изчерпани</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Извършители на атака, използващи <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="375403751935624634">Преводът не бе успешен поради грешка в сървъра.</translation>
<translation id="3759461132968374835">Наскоро не сте съобщавали за сривове. Тези, възникнали при деактивирано изпращане на сигнали за сривове, не се показват тук.</translation>
+<translation id="3765032636089507299">Страницата на Безопасно сърфиране е в процес на разработка.</translation>
<translation id="3778403066972421603">Искате ли тази карта да се запази на устройството и в профила ви в Google?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Изтича на <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Заявката ви за достъп до този сайт бе изпратена до <ph name="NAME" /></translation>
<translation id="3890664840433101773">Добавяне на имейл адрес</translation>
<translation id="3901925938762663762">Картата е с изтекла валидност</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> – <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Идентификатор на качения сигнал за срив: <ph name="CRASH_ID" /> (локален идентификатор на срива: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Сървърът не можа да докаже, че е <ph name="DOMAIN" />. В сертификата му за сигурност не са посочени алтернативни имена на обекта. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от извършител на атака.</translation>
+<translation id="3949601375789751990">Тук се показва историята ви на сърфиране</translation>
<translation id="3963721102035795474">Режим за четене</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Няма}=1{От 1 сайт }other{От # сайта }}</translation>
<translation id="397105322502079400">Изчислява се...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Настройки за сайта</translation>
<translation id="4169947484918424451">Искате ли Chromium да запази тази карта?</translation>
<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="4196861286325780578">&amp;Възстановяване на преместването</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Проверете конфигурацията на защитната стена и антивирусния софтуер<ph name="END_LINK" />.</translation>
<translation id="4220128509585149162">Сривове</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Извършители на атака, използващи този сайт, може да опитат да инсталират опасни програми на компютъра ви, които крадат или изтриват информацията ви (например снимки, пароли, съобщения и номера на кредитни карти).</translation>
<translation id="4372948949327679948">Очаквана е стойност от тип <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Направихте опит да се свържете с/ъс <ph name="DOMAIN" />, но сървърът предoстави сертификат, анулиран от издателя си. Това означава, че в никакъв случай не трябва да се доверявате на представените от сървъра идентификационни данни за сигурност. Възможно е да сте се свързали с извършител на атака.</translation>
-<translation id="4381091992796011497">Потребителско име:</translation>
<translation id="4394049700291259645">Деактивиране</translation>
<translation id="4406896451731180161">резултата от търсенето</translation>
<translation id="4424024547088906515">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; Chrome няма доверие на сертификата му за сигурност. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> не прие сертификата ви за вход или е възможно да не е предоставен такъв.</translation>
<translation id="443673843213245140">Използването на прокси сървър е деактивирано, но е посочена изрична негова конфигурация.</translation>
+<translation id="445100540951337728">Приемани дебитни карти</translation>
<translation id="4506176782989081258">Грешка при потвърждаването: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Свържете се със системния администратор.</translation>
<translation id="450710068430902550">Споделяне с администратор</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Адресът да се премахне ли от Chrome?</translation>
<translation id="4592951414987517459">Връзката ви с/ъс <ph name="DOMAIN" /> е шифрована със съвременен криптографски пакет.</translation>
<translation id="4594403342090139922">&amp;Отмяна на изтриването</translation>
+<translation id="4611292653554630842">Вход</translation>
<translation id="4619615317237390068">Раздели от други устройства</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; сертификатът му за сигурност съдържа грешки. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
<translation id="4690462567478992370">Спиране на използването на невалиден сертификат</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Връзката ви бе прекъсната</translation>
+<translation id="471880041731876836">Нямате разрешение да посетите този сайт</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Стартирайте мрежова диагностика в Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Презареждане на правилата</translation>
<translation id="4728558894243024398">Платформа</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">Изглед</translation>
<translation id="4854362297993841467">Този начин на бърза доставка не се поддържа. Опитайте с друг.</translation>
<translation id="4858792381671956233">Попитахте родителите си дали може да посетите този сайт</translation>
-<translation id="4863764087567530506">Въпросното съдържание може да се опита да ви подведе да инсталирате софтуер или да разкриете лична информация. <ph name="BEGIN_LINK" />Показване въпреки това<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Търсене в историята</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> и <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Изисква се удостоверяване</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{и още 1 уеб страница}other{и още # уеб страници}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Тази страница е преведена от непознат език на <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Плащане</translation>
<translation id="4926049483395192435">Трябва да се посочи.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Действия</translation>
<translation id="4958444002117714549">Разгъване на списъка</translation>
<translation id="4974590756084640048">Повторно активиране на предупрежденията</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Грешна парола</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>
<translation id="5087580092889165836">Добавяне на карта</translation>
<translation id="5089810972385038852">Щат</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Провинция</translation>
<translation id="5115563688576182185">(64 бита)</translation>
<translation id="5141240743006678641">Синхронизираните пароли да се шифроват с идентификационните ви данни за Google</translation>
-<translation id="514421653919133810">Отворете страницата в режим „инкогнито“ (Ctrl-Shift-N).</translation>
<translation id="5145883236150621069">В отговора за правилото присъства код на грешка</translation>
+<translation id="5159010409087891077">Отворете страницата в нов прозорец в режим „инкогнито“ (⇧⌘N).</translation>
<translation id="5171045022955879922">Търсете или въведете URL адрес</translation>
<translation id="5172758083709347301">Машината</translation>
<translation id="5179510805599951267">Не е на <ph name="ORIGINAL_LANGUAGE" />? Подайте сигнал за тази грешка</translation>
-<translation id="5181140330217080051">Изтегля се</translation>
<translation id="5190835502935405962">Лента на отметките</translation>
<translation id="5199729219167945352">Експерименти</translation>
<translation id="5205222826937269299">Името е задължително</translation>
<translation id="5222812217790122047">Имейл адресът е задължителен</translation>
<translation id="5251803541071282808">Облак</translation>
<translation id="5277279256032773186">Използвате Chrome на работното си място? Бизнесите могат да управляват настройките на браузъра за служителите си. Научете повече</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Изпълнете следните стъпки, за да деактивирате временно софтуера, така че да можете да се свържете с мрежата. Ще са ви необходими администраторски права.<ph name="END_PARAGRAPH" />
+
+<ph name="BEGIN_LIST" />
+<ph name="LIST_ITEM" />Кликнете върху <ph name="BEGIN_BOLD" />Старт<ph name="END_BOLD" />, след което потърсете и изберете <ph name="BEGIN_BOLD" />Преглед на локални услуги<ph name="END_BOLD" />.
+<ph name="LIST_ITEM" />Изберете <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+<ph name="LIST_ITEM" />За <ph name="BEGIN_BOLD" />Startup type<ph name="END_BOLD" /> изберете <ph name="BEGIN_BOLD" />Disabled<ph name="END_BOLD" />.
+<ph name="LIST_ITEM" />Под <ph name="BEGIN_BOLD" />Service status<ph name="END_BOLD" /> кликнете върху <ph name="BEGIN_BOLD" />Stop<ph name="END_BOLD" />.
+<ph name="LIST_ITEM" />Кликнете върху <ph name="BEGIN_BOLD" />Приложи<ph name="END_BOLD" /> и след това – върху <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+<ph name="LIST_ITEM" />Посетете <ph name="BEGIN_LEARN_MORE_LINK" />Помощния център на Chrome<ph name="END_LEARN_MORE_LINK" />, за да научите как да премахнете за постоянно софтуера от компютъра си.
+<ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Връзката ви с този сайт не е частна. За да излезете от режима на VR, премахнете очилата и натиснете бутона за назад.</translation>
<translation id="5299298092464848405">Грешка при синтактичния анализ на правилото</translation>
<translation id="5308689395849655368">Изпращането на сигнали за сривове е деактивирано.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">При потвърждаване на схемата възникна грешка в/ъв „<ph name="ERROR_PATH" />“: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Тази страница на <ph name="HOST_NAME" /> не може да бъде намерена</translation>
<translation id="5455374756549232013">Невалидно клеймо за дата и час на правилото</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> от <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Невалидно</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Възстановяване на редактирането</translation>
+<translation id="5481076368049295676">Въпросното съдържание може да се опита да инсталира опасен софтуер на устройството ви, който да открадне или изтрие информацията ви. <ph name="BEGIN_LINK" />Показване въпреки това<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Добавяне на валиден адрес</translation>
<translation id="5492298309214877701">Този интранет сайт на фирмата, организацията или училището има същия URL адрес като на външен уебсайт.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Заявената статия не можа да бъде намерена.</translation>
<translation id="5544037170328430102">Вградена страница на адрес <ph name="SITE" /> изпраща подкана:</translation>
<translation id="5556459405103347317">Повторно зареждане</translation>
+<translation id="5560088892362098740">Дата на изтичане</translation>
<translation id="5565735124758917034">Активно</translation>
<translation id="5571083550517324815">Този адрес за вземане не се поддържа. Изберете друг.</translation>
<translation id="5572851009514199876">Моля, стартирайте браузъра Chrome и влезте в него, за да се провери дали имате достъп до този сайт.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Зареждането на настройките за правилото не бе успешно</translation>
<translation id="5631439013527180824">Невалидно означение за управление на устройството</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="5646376287012673985">Местоположение</translation>
<translation id="5659593005791499971">Имейл</translation>
<translation id="5669703222995421982">Получаване на персонализирано съдържание</translation>
<translation id="5675650730144413517">Тази страница не работи</translation>
<translation id="5710435578057952990">Самоличността на този уебсайт не е потвърдена.</translation>
-<translation id="5713016350996637505">Блокирахме измамно съдържание</translation>
+<translation id="5719499550583120431">Приемат се предплатени карти.</translation>
<translation id="5720705177508910913">Текущият потребител</translation>
<translation id="5732392974455271431">Родителите ви могат да го отблокират за вас</translation>
<translation id="5763042198335101085">Въведете валиден имейл адрес</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Искате ли да се попълнят данните за <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Връзката ви с/ъс <ph name="DOMAIN" /> е шифрована с остарял криптографски пакет.</translation>
<translation id="5813119285467412249">&amp;Възстановяване на добавянето</translation>
-<translation id="5814352347845180253">Може да загубите достъп до платено съдържание от <ph name="SITE" /> и някои други сайтове.</translation>
<translation id="5838278095973806738">Не ви препоръчваме да въвеждате поверителна информация в този сайт (например пароли или номера на кредитни карти), тъй като може да бъде открадната от извършители на атаки.</translation>
<translation id="5869405914158311789">Няма достъп до този сайт</translation>
<translation id="5869522115854928033">Запазени пароли</translation>
<translation id="5872918882028971132">Основни предложения</translation>
+<translation id="5893752035575986141">Приемат се кредитни карти.</translation>
<translation id="5901630391730855834">жълто</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (синхронизирано)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Използва се 1}other{Използват се #}}</translation>
-<translation id="5926846154125914413">Може да загубите достъп до платено съдържание от някои сайтове.</translation>
<translation id="5959728338436674663">Автоматично изпращане до Google на <ph name="BEGIN_WHITEPAPER_LINK" />системна информация и част от съдържанието на страниците<ph name="END_WHITEPAPER_LINK" /> с цел по-лесно откриване на опасни приложения и сайтове. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">Премахване от историята</translation>
<translation id="5975083100439434680">Намаляване на мащаба</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Затваряне</translation>
<translation id="6042308850641462728">Още</translation>
<translation id="6047233362582046994">Ако разбирате рисковете за сигурността си, може <ph name="BEGIN_LINK" />да посетите този сайт<ph name="END_LINK" /> преди премахването на опасните приложения.</translation>
+<translation id="6047927260846328439">Въпросното съдържание може да се опита да ви подведе да инсталирате софтуер или да разкриете лична информация. <ph name="BEGIN_LINK" />Показване въпреки това<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">В момента не можете да посетите <ph name="SITE" />, защото уебсайтът използва метод за допълнително потвърждаване на сертификатите. Обикновено грешките в мрежата и атаките срещу нея са временни, така че тази страница вероятно ще работи по-късно.</translation>
<translation id="6060685159320643512">Внимавайте, тези експерименти може да са опасни</translation>
<translation id="6080696365213338172">Осъществихте достъп до съдържанието посредством осигурен от администратора сертификат. Данните, които предоставите на <ph name="DOMAIN" />, могат да бъдат прихванати от администратора ви.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Научете повече</translation>
<translation id="6169916984152623906">Вече можете да сърфирате частно. Така другите хора, които използват това устройство, няма да виждат активността ви. Изтеглянията и отметките обаче ще се запазват.</translation>
<translation id="6177128806592000436">Връзката ви с този сайт не е защитена</translation>
-<translation id="6184817833369986695">(кохорта: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Проверете връзката си с интернет</translation>
<translation id="6218753634732582820">Адресът да се премахне ли от Chromium?</translation>
<translation id="6221345481584921695">Google Безопасно сърфиране наскоро <ph name="BEGIN_LINK" />откри злонамерен софтуер<ph name="END_LINK" /> на <ph name="SITE" />. Уебсайтовете, които обикновено са надеждни, понякога се заразяват с опасен софтуер. Въпросното съдържание произлиза от <ph name="SUBRESOURCE_HOST" /> – известен разпространител на злонамерени програми.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Търсите интересни нови функции на Chrome? Изпробвайте бета канала на адрес chrome.com/beta.</translation>
-<translation id="6389758589412724634">Паметта на Chromium се изчерпа, докато браузърът опитваше да покаже тази уеб страница.</translation>
+<translation id="6397451950548600259">Софтуер на компютъра ви пречи на Chrome да се свърже безопасно с мрежата</translation>
<translation id="6404511346730675251">Редактиране на отметката</translation>
<translation id="6410264514553301377">Въвеждане на датата на валидност и кода за проверка за <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Попитахте родителя си дали може да посетите този сайт</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Правила за устройството</translation>
<translation id="6477321094435799029">Chrome откри необичаен код на тази страница и я блокира, за да защити личната ви информация (например пароли, телефонни номера и номера на кредитни карти).</translation>
<translation id="6489534406876378309">Стартиране на качването на сривове</translation>
+<translation id="6507833130742554667">Приемат се кредитни и дебитни карти.</translation>
<translation id="6508722015517270189">Рестартирайте Chrome.</translation>
<translation id="6529602333819889595">&amp;Възстановяване на изтриването</translation>
<translation id="6534179046333460208">Предложения от Физическата мрежа</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Въпросното съдържание може да се опита да инсталира опасен софтуер на устройството ви, който да открадне или изтрие информацията ви. <ph name="BEGIN_LINK" />Показване въпреки това<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Опции за шифроване</translation>
<translation id="662080504995468778">Оставане</translation>
<translation id="6626291197371920147">Добавяне на валиден номер на карта</translation>
<translation id="6628463337424475685">Търсене с/ъс <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Извършители на атака, понастоящем използващи <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, може да опитат да инсталират опасни програми на компютъра ви Mac, които крадат или изтриват информацията ви (например снимки, пароли, съобщения и номера на кредитни карти). <ph name="BEGIN_LEARN_MORE_LINK" />Научете повече<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Това правило е оттеглено.</translation>
+<translation id="6657585470893396449">Парола</translation>
<translation id="6671697161687535275">Предложението за формуляри да се премахне ли от Chromium?</translation>
<translation id="6685834062052613830">Излизане от профила и завършване на настройването</translation>
<translation id="6710213216561001401">Предишна</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Окръг</translation>
<translation id="6973656660372572881">Посочени са както фиксирани прокси сървъри, така и URL адрес на скрипт във формат .pac.</translation>
<translation id="6989763994942163495">Показване на разширените настройки...</translation>
-<translation id="7000990526846637657">Няма намерени записи в историята</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Възможно е в профила ви в Google да има други видове история на сърфиране, съхранявани на адрес <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Пароли</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Този адрес за доставка не се поддържа. Изберете друг.</translation>
<translation id="7138472120740807366">Начин на бърза доставка</translation>
<translation id="7139724024395191329">Емирство</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Страницата за плащане не е защитена</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Стартиране отново сега</translation>
<translation id="7180611975245234373">Опресняване</translation>
<translation id="7182878459783632708">Няма зададени правила</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Изтегляне</translation>
<translation id="7518003948725431193">Не е намерена уеб страница за уеб адреса: <ph name="URL" /></translation>
<translation id="7521387064766892559">Javascript</translation>
+<translation id="7526934274050461096">Връзката ви с този сайт не е частна</translation>
<translation id="7535087603100972091">Стойност</translation>
<translation id="7537536606612762813">Задължително</translation>
<translation id="7542403920425041731">След като потвърдите картата си, данните за нея ще бъдат споделени с този сайт.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Изпробвайте следните съвети:</translation>
<translation id="7554791636758816595">Нов раздел</translation>
<translation id="7567204685887185387">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; възможно е сертификатът му за сигурност да е издаден измамнически. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Тази страница е на<ph name="ORIGINAL_LANGUAGE" />Искате ли да я преведете?</translation>
<translation id="7569952961197462199">Кредитната карта да се премахне ли от Chrome?</translation>
<translation id="7569983096843329377">черно</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Сертификат</translation>
<translation id="7716147886133743102">Блокирано от администратора ви</translation>
<translation id="7716424297397655342">Този сайт не може да се зареди от кеш паметта</translation>
+<translation id="774634243536837715">Блокирахме опасно съдържание.</translation>
<translation id="7752995774971033316">Не се управлява</translation>
<translation id="7755287808199759310">Родителят ви може да го отблокира за вас</translation>
<translation id="7758069387465995638">Възможно е връзката да е блокирана от защитна стена или антивирусен софтуер.</translation>
+<translation id="7759163816903619567">Показван домейн:</translation>
<translation id="7761701407923456692">Сертификатът на сървъра не съответства на URL адреса.</translation>
<translation id="7763386264682878361">Инструмент за синтактичен анализ на манифести за плащания</translation>
<translation id="7764225426217299476">Добавяне на адрес</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534"><ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> за „<ph name="SEARCH_STRING" />“</translation>
<translation id="785549533363645510">Сърфирането ви обаче не е невидимо. При преминаване в режим „инкогнито“ то не се скрива от работодателя ви и от доставчика ви на интернет услуги, нито от уебсайтовете, които посещавате.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Приемат се дебитни и предплатени карти.</translation>
<translation id="7887683347370398519">Прегледайте кода за проверка и опитайте отново</translation>
<translation id="79338296614623784">Въведете валиден телефонен номер</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Изпращане на отзивите</translation>
<translation id="8041940743680923270">Използване на глобалната стандартна стойност (запитване)</translation>
<translation id="8088680233425245692">Преглеждането на статията не бе успешно.</translation>
-<translation id="8089520772729574115">по-малко от 1 МБ</translation>
<translation id="8091372947890762290">В сървъра се изчаква активиране</translation>
<translation id="8118489163946903409">Начин на плащане</translation>
<translation id="8131740175452115882">Потвърждаване</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Отмяна на преместването</translation>
<translation id="8201077131113104583">Невалиден URL адрес за актуализиране на разширението с идентификационен номер <ph name="EXTENSION_ID" />.</translation>
<translation id="8202097416529803614">Обобщена информация за поръчката</translation>
+<translation id="8205463626947051446">На сайта обикновено се показват натрапчиви реклами</translation>
<translation id="8218327578424803826">Зададено местоположение:</translation>
<translation id="8225771182978767009">Човекът, който е настроил компютъра, е блокирал този сайт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" /> и <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Отворете страницата в нов раздел в режим „инкогнито“.</translation>
<translation id="8241707690549784388">Страницата, която търсите, използва въведената от вас информация. Ако се върнете на тази страница, действията, които вече сте изпълнили, може да бъдат повторени. Искате ли да продължите?</translation>
+<translation id="8241712895048303527">Блокиране за този сайт</translation>
<translation id="8249320324621329438">Последно извличане:</translation>
<translation id="8253091569723639551">Адресът за фактуриране е задължителен</translation>
<translation id="8261506727792406068">Изтриване</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Преводът не бе успешен поради проблем с връзката към мрежата.</translation>
<translation id="8332188693563227489">Достъпът до <ph name="HOST_NAME" /> бе отказан</translation>
<translation id="834457929814110454">Ако разбирате рисковете за сигурността си, може <ph name="BEGIN_LINK" />да посетите този сайт<ph name="END_LINK" /> преди премахването на опасните програми.</translation>
-<translation id="8344669043927012510">Отворете страницата в режим „инкогнито“ (⇧⌘N).</translation>
<translation id="8349305172487531364">Лента на отметките</translation>
<translation id="8363502534493474904">Изключете самолетния режим.</translation>
<translation id="8364627913115013041">Не е зададено.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Сигналът за срив е качен в/ъв <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Сривове (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Трябва да въведете един и същи пропуск два пъти.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Настройки</translation>
<translation id="8433057134996913067">Ще излезете от повечето уебсайтове.</translation>
<translation id="8437238597147034694">&amp;Отмяна на преместването</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">За да използвате картите от профила си в Google, влезте в Chrome</translation>
<translation id="8488350697529856933">Прилага се към</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> не отговаря твърде дълго време.</translation>
-<translation id="8532105204136943229">Година на валидност</translation>
+<translation id="8503813439785031346">Потребителско име</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="8571890674111243710">Превод на страницата на <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">+ тел. номер</translation>
<translation id="859285277496340001">Сертификатът не посочва механизъм за проверка дали е бил анулиран.</translation>
<translation id="8620436878122366504">Родителите ви все още не са одобрили заявката</translation>
<translation id="8647750283161643317">Възстановяване на всичко към стандартното състояние</translation>
+<translation id="8660471606262461360">От Google Payments</translation>
<translation id="8703575177326907206">Връзката ви с <ph name="DOMAIN" /> не е шифрована.</translation>
<translation id="8718314106902482036">Плащането не е завършено</translation>
<translation id="8725066075913043281">Опитайте отново</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Увеличаване на мащаба</translation>
<translation id="8931333241327730545">Искате ли да запазите тази карта в профила си в Google?</translation>
<translation id="8932102934695377596">Часовникът ви е назад</translation>
+<translation id="8938939909778640821">Приемани кредитни и предплатени карти</translation>
<translation id="8971063699422889582">Сертификатът на сървъра е с изтекла валидност.</translation>
<translation id="8986494364107987395">Автоматично изпращане до Google на статистически данни за използването на Chrome и сигнали за сривове</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Блокиране винаги на този сайт</translation>
<translation id="9170848237812810038">&amp;Отмяна</translation>
<translation id="917450738466192189">Сертификатът на сървъра е невалиден.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> и още <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> използва неподдържан протокол.</translation>
<translation id="9205078245616868884">Данните ви са шифровани с пропуска ви за синхронизиране. Въведете го, за да стартирате синхронизирането.</translation>
<translation id="9207861905230894330">Добавянето на статията не бе успешно.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ИЗЧИСТВАНЕ НА ФОРМУЛЯРА</translation>
<translation id="939736085109172342">Нова папка</translation>
-<translation id="941721044073577244">Изглежда, че нямате разрешение да посетите този сайт</translation>
<translation id="969892804517981540">Официално издание</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Няма}=1{1 елемент}other{# елемента}}</translation>
+<translation id="981121421437150478">Офлайн</translation>
<translation id="988159990683914416">Компилирана програма за програмисти</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_bn.xtb b/chromium/components/strings/components_strings_bn.xtb
index d2952543bac..19298eafd69 100644
--- a/chromium/components/strings/components_strings_bn.xtb
+++ b/chromium/components/strings/components_strings_bn.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">অন্যান্য অ্যাপ্লিকেশানগুলি বন্ধ করুন</translation>
<translation id="1055184225775184556">&amp;যোগ করাকে পূর্বাবস্থায় ফেরান</translation>
<translation id="10614374240317010">কখনও সংরক্ষিত হয়নি</translation>
+<translation id="1066396345355680611">আপনি <ph name="SITE" /> এবং অন্যান্য সাইট থেকে প্রিমিয়াম কন্টেন্টে অ্যাক্সেস হারাতে পারেন।</translation>
<translation id="106701514854093668">ডেস্কটপ বুকমার্ক</translation>
<translation id="1074497978438210769">সুরক্ষিত নয়</translation>
<translation id="1080116354587839789">প্রস্থের মধ্যে আঁটান</translation>
@@ -30,7 +31,7 @@
<translation id="1181037720776840403">সরান</translation>
<translation id="1184214524891303587">নিরাপত্তার সম্ভাব্য লঙ্ঘনের ঘটনাগুলির বিস্তারিত বিবরণ Google এর কাছে <ph name="BEGIN_WHITEPAPER_LINK" />স্বয়ংক্রিয়ভাবে প্রতিবেদন করুন<ph name="END_WHITEPAPER_LINK" />। <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">পরবর্তী</translation>
-<translation id="1201895884277373915">এই সাইট থেকে আরো</translation>
+<translation id="1201895884277373915">এই সাইট থেকে আরও</translation>
<translation id="1206967143813997005">নষ্ট প্রাথমিক স্বাক্ষর</translation>
<translation id="1209206284964581585">এখনকার মতো লুকান</translation>
<translation id="121201262018556460">আপনি <ph name="DOMAIN" />-এ পৌঁছানোর প্রচেষ্টা করেছেন, কিন্তু সার্ভার একটি দুর্বল কী সম্বলিত শংসাপত্র উপস্থাপন করেছে৷ কোনো আক্রমণকারী ব্যক্তিগত কী ভঙ্গ করে থাকতে পারে এবং সার্ভারটি আপনার প্রত্যাশিত সার্ভার নাও হতে পারে (হতে পারে আপনি একজন আক্রমণকারীর সাথে যোগাযোগ করছেন)৷</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">পড়ার তালিকা</translation>
<translation id="1264126396475825575">ক্র্যাশ প্রতিবেদন <ph name="CRASH_TIME" /> এ ক্যাপচার করা হয়েছে (এখনো আপলোড করা বা উপেক্ষা করা হয়নি)</translation>
<translation id="1281526147609854549">জারি করেছে <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">বিপজ্জনক কন্টেন্ট ব্লক করা হয়েছে</translation>
<translation id="1285320974508926690">কখনই এই সাইটটিকে অনুবাদ করবেন না</translation>
<translation id="129553762522093515">সম্প্রতি বন্ধ হয়েছে</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />আপনার কুকিজ সাফ করে দেখুন<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">ডোমেন নথিভুক্ত করুন:</translation>
<translation id="1340482604681802745">যে ঠিকানা থেকে নিতে হবে</translation>
-<translation id="1344211575059133124">মনে হচ্ছে এই সাইটটি ঘুরে দেখতে আপনাকে অনুমতি নিতে হবে</translation>
<translation id="1344588688991793829">Chromium স্বতঃপূর্ণ সেটিংস...</translation>
<translation id="1348198688976932919">যে সাইট খুলতে চলেছেন সেটিতে বিপজ্জনক অ্যাপ আছে</translation>
<translation id="1374468813861204354">প্রস্তাবনাগুলি</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" />-তে <ph name="ORGANIZATION" />-এর পরিচয় <ph name="ISSUER" /> যাচাই করেছে৷</translation>
<translation id="1426410128494586442">হ্যাঁ</translation>
<translation id="1430915738399379752">প্রিন্ট</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />টি}one{<ph name="PAYMENT_METHOD_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />টি}other{<ph name="PAYMENT_METHOD_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />টি}}</translation>
<translation id="1506687042165942984">এই পৃষ্ঠার একটি সংরক্ষিত প্রতিলিপি (অর্থাৎ,তারিখ সীমার বাইরে হিসাবে পরিচিত) প্রদর্শন করুন৷</translation>
<translation id="1517433312004943670">ফোন নম্বর আবশ্যক</translation>
+<translation id="1517500485252541695">ক্রেডিট এবং ডেবিট কার্ড গ্রহণ করা হয়</translation>
<translation id="1519264250979466059">নির্মাণের তারিখ</translation>
+<translation id="1527263332363067270">সংযোগের জন্য অপেক্ষা করা হচ্ছে...</translation>
<translation id="153384715582417236">এখন এই পর্যন্তই</translation>
<translation id="1549470594296187301">এই বৈশিষ্ট্যটি ব্যবহার করার জন্য JavaScript সক্ষম করা প্রয়োজন।</translation>
<translation id="1555130319947370107">নীল</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">সিস্টেম প্রশাসকের সাথে যোগাযোগ করে দেখুন।</translation>
<translation id="1740951997222943430">মেয়াদ শেষ হওয়ার মাসের সঠিক মান লিখুন</translation>
-<translation id="1745358365027406341">পৃষ্ঠাটি পরে ডাউনলোড করুন</translation>
<translation id="17513872634828108">খোলা ট্যাব</translation>
<translation id="1753706481035618306">পৃষ্ঠা সংখ্যা</translation>
<translation id="1763864636252898013">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্রটি আপনার ডিভােইসের নিকট বিশ্বাসযোগ্য নয়। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
@@ -122,16 +121,18 @@
<translation id="1871284979644508959">আবশ্যক ক্ষেত্র</translation>
<translation id="187918866476621466">শুরুর পৃষ্ঠাগুলি খুলুন</translation>
<translation id="1883255238294161206">তালিকা সঙ্কুচিত করুন</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />টি}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />টি}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />টি}}</translation>
<translation id="1898423065542865115">ফিল্টার হচ্ছে</translation>
+<translation id="1916770123977586577">আপডেট করা সেটিংস এই সাইটে প্রয়োগ করতে পৃষ্ঠাটি আবার লোড করুন</translation>
+<translation id="1919345977826869612">বিজ্ঞাপন</translation>
<translation id="192020519938775529">{COUNT,plural, =0{কিছুই নয়}=1{১টি সাইট}one{#টি সাইট}other{#টি সাইট}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> এ যান</translation>
+<translation id="1948773908305951926">প্রিপেড কার্ড গ্রহণ করা হয়</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> বুকমার্কগুলি</translation>
<translation id="1973335181906896915">ধারাবাহিকতাতে ত্রুটি</translation>
<translation id="1974060860693918893">উন্নত</translation>
<translation id="1978555033938440688">ফার্মওয়ের সংস্করণ</translation>
-<translation id="1995859865337580572">অনুগ্রহ করে আপনার CVC যাচাই করুন</translation>
-<translation id="2001146170449793414">{COUNT,plural, =1{এবং আরো ১টি}one{এবং আরো #টি}other{এবং আরো #টি}}</translation>
+<translation id="1995859865337580572">Please verify your CVC</translation>
+<translation id="2001146170449793414">{COUNT,plural, =1{এবং আরও ১টি}one{এবং আরও #টি}other{এবং আরও #টি}}</translation>
<translation id="2025186561304664664">স্বতঃ কনফিগার করতে প্রক্সি সেট করা হয়৷</translation>
<translation id="2030481566774242610">আপনি কি <ph name="LINK" /> বোঝাতে চেয়েছিলেন?</translation>
<translation id="2032962459168915086"><ph name="BEGIN_LINK" />প্রক্সি এবং ফায়ারওয়াল পরীক্ষা করে দেখুন<ph name="END_LINK" /></translation>
@@ -166,10 +167,11 @@
<translation id="2262243747453050782">HTTP ত্রুটি</translation>
<translation id="2270484714375784793">ফোন নম্বর</translation>
<translation id="2282872951544483773">অনুপলব্ধ পরীক্ষানিরীক্ষাগুলি</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" />টি আইটেম}one{<ph name="ITEM_COUNT" />টি আইটেম}other{<ph name="ITEM_COUNT" />টি আইটেম}}</translation>
<translation id="2292556288342944218">আপনার ইন্টারনেট অ্যাক্সেস অবরুদ্ধ করা হয়েছে</translation>
<translation id="230155334948463882">নতুন কার্ড?</translation>
-<translation id="2317259163369394535"><ph name="DOMAIN" /> এর জন্য একটি ব্যবহারকারীর নাম এবং পাসওয়ার্ড প্রয়োজন।</translation>
+<translation id="2316887270356262533">১ MB এর চেয়ে কম জায়গা খালি করে। পরের বার যখন দেখবেন তখন কিছু সাইট লোড হতে দেরি হতে পারে।</translation>
+<translation id="2317259163369394535"><ph name="DOMAIN" /> এর জন্য একটি ইউজারনেম এবং পাসওয়ার্ড প্রয়োজন।</translation>
+<translation id="2317583587496011522">ডেবিট কার্ড গ্রহণ করা হয়।</translation>
<translation id="2337852623177822836">সেটিংস আপনার প্রশাসক নিয়ন্ত্রণ করে</translation>
<translation id="2354001756790975382">অন্য বুকমার্কস</translation>
<translation id="2354430244986887761">Google নিরাপদ ব্রাউজিং সম্প্রতি <ph name="SITE" /> এ <ph name="BEGIN_LINK" />ক্ষতিকারক অ্যাপ<ph name="END_LINK" /> খুঁজে পেয়েছে।</translation>
@@ -179,7 +181,6 @@
<translation id="2359808026110333948">অবিরত</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> এ ক্যাপচার করা ক্র্যাশ প্রতিবেদন আপলোড করা হয়নি</translation>
<translation id="2367567093518048410">স্তর</translation>
-<translation id="237718015863234333">কোনো UI বিকল্প উপলব্ধ নেই</translation>
<translation id="2384307209577226199">এন্টারপ্রাইজ ডিফল্ট</translation>
<translation id="2386255080630008482">সার্ভারের শংসাপত্রটি প্রত্যাহার করা হয়েছে৷</translation>
<translation id="2392959068659972793">কোনো মান সেট করা নেই এমন নীতিগুলি দেখান</translation>
@@ -195,12 +196,12 @@
<translation id="2495093607237746763">টিক চিহ্ণ দেওয়া থাকলে, ফর্ম পূরনের কাজ দ্রুত করতে Chromium এই ডিভাইসে আপনার কার্ডের একটি প্রতিলিপি সঞ্চয় করবে।</translation>
<translation id="2498091847651709837">নতুন কার্ড স্ক্যান করুন</translation>
<translation id="2501278716633472235">ফিরে যান</translation>
+<translation id="2503184589641749290">ডেবিট ও প্রিপেড কার্ড গ্রহণ করা হয়</translation>
<translation id="2515629240566999685">আপনার এলাকায় সংকেত পরীক্ষা করে দেখুন</translation>
-<translation id="2516305470678292029">UI বিকল্পগুলি</translation>
<translation id="2539524384386349900">সনাক্ত করুন</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> একটি অবৈধ প্রতিক্রিয়া পাঠিয়েছে।</translation>
<translation id="2556876185419854533">&amp;সম্পাদনাকে পূর্বাবস্থায় ফেরান</translation>
-<translation id="2587730715158995865"><ph name="ARTICLE_PUBLISHER" /> থেকে পাওয়া। এটি এবং আরো <ph name="OTHER_ARTICLE_COUNT" />টি গল্প পড়ুন।</translation>
+<translation id="2587730715158995865"><ph name="ARTICLE_PUBLISHER" /> থেকে পাওয়া। এটি এবং আরও <ph name="OTHER_ARTICLE_COUNT" />টি গল্প পড়ুন।</translation>
<translation id="2587841377698384444">ডিরেক্টরি API আইডি:</translation>
<translation id="2597378329261239068">এই দস্তাবেজটি পাসওয়ার্ড সুরক্ষিত৷ দয়া করে একটি পাসওয়ার্ড লিখুন৷</translation>
<translation id="2609632851001447353">বৈচিত্রতা</translation>
@@ -230,19 +231,21 @@
<translation id="277133753123645258">শিপিংয়ের পদ্ধতি</translation>
<translation id="277499241957683684">ডিভাইস রেকর্ড অনুপস্থিত</translation>
<translation id="2784949926578158345">সংযোগ পুনঃসেট করা হয়েছে৷</translation>
+<translation id="2788784517760473862">ক্রেডিট কার্ড গ্রহণ করা হয়</translation>
<translation id="2794233252405721443">সাইট অবরুদ্ধ করা হয়েছে</translation>
<translation id="2799020568854403057">যে সাইট খুলতে চলেছেন সেটিতে ক্ষতিকারক অ্যাপ আছে</translation>
<translation id="2803306138276472711">Google নিরাপদ ব্রাউজিং সাম্প্রতিক <ph name="SITE" /> এ <ph name="BEGIN_LINK" />ম্যালওয়্যার শনাক্ত করেছে<ph name="END_LINK" />। যেসব ওয়েবসাইট সাধারণত নিরাপদ থাকে, সেগুলি কখনও কখনও ম্যালওয়্যার দ্বারা আক্রান্ত হয়।</translation>
<translation id="2824775600643448204">ঠিকানা এবং অনুসন্ধান দণ্ড</translation>
<translation id="2826760142808435982"><ph name="CIPHER" /> ব্যবহার করে এই সংযোগটি এনক্রিপ্টেড এবং প্রমাণীকৃত করা হয়েছে এবং কী এক্সচেঞ্জ প্রক্রিয়া হিসাবে <ph name="KX" /> ব্যবহার করে৷</translation>
<translation id="2835170189407361413">ফর্ম সাফ করুন</translation>
+<translation id="2851634818064021665">এই সাইট দেখার জন্য আপনার অনুমতির প্রয়োজন</translation>
<translation id="2856444702002559011">আক্রমণকারীরা হয়ত <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> থেকে আপনার তথ্য (যেমন পাসওয়ার্ড, মেসেজ বা ক্রেডিট কার্ড) চুরি করার চেষ্টা করছে। <ph name="BEGIN_LEARN_MORE_LINK" />আরও জানুন<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">আবার লোড করবেন না</translation>
-<translation id="2900469785430194048">এই ওয়েবপৃষ্ঠা প্রদর্শন করার সময় Google Chrome এর মেমরি শেষ হয়ে গেছে।</translation>
<translation id="2909946352844186028">একটি নেটওয়ার্ক পরিবর্তন সনাক্ত হয়েছে৷</translation>
<translation id="2916038427272391327">অন্যান্য প্রোগ্রামগুলি বন্ধ করুন</translation>
<translation id="2922350208395188000">সার্ভারের শংসাপত্র চেক করা যাবে না৷</translation>
<translation id="2928905813689894207">বিলিংয়ের ঠিকানা</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />টি}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />টি}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />টি}}</translation>
<translation id="2941952326391522266">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্র <ph name="DOMAIN2" /> থেকে পাওয়া। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
<translation id="2948083400971632585">আপনি সেটিংস পৃষ্ঠা থেকে সংযোগের জন্য কনফিগার করা যেকোনো প্রক্সি নিষ্ক্রিয় করতে পারেন৷</translation>
<translation id="2955913368246107853">খোঁজ দণ্ড বন্ধ করুন</translation>
@@ -250,13 +253,14 @@
<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>
<translation id="2985306909656435243">সক্ষম করা হলে, ফর্ম পূরনের কাজ দ্রুত করতে Chromium এই ডিভাইসে আপনার কার্ডের একটি প্রতিলিপি সংরক্ষণ করবে।</translation>
<translation id="2985398929374701810">একটি সঠিক ঠিকানা লিখুন</translation>
<translation id="2986368408720340940">এই পদ্ধতিতে পিক-আপ করা যাবে না। অন্য পদ্ধতি ব্যবহার করুন।</translation>
<translation id="2991174974383378012">ওয়েবসাইটের সাথে ভাগ করছে</translation>
<translation id="2991571918955627853">ওয়েবসাইটটি HSTS ব্যবহার করার কারণে আপনি এখন <ph name="SITE" /> এ যেতে পারবেন না। নেটওয়ার্ক ত্রুটি এবং আক্রমণ সাধারণত সাময়িকভাবে হয়, তাই এই পৃষ্ঠাটি সম্ভবত পরে কাজ করবে।</translation>
<translation id="3005723025932146533">সংরক্ষিত প্রতিলিপি দেখান</translation>
-<translation id="3008447029300691911"><ph name="CREDIT_CARD" /> এর CVC লিখুন। আপনি নিশ্চিত করলে, আপনার কার্ডের বিবরণ এই সাইটের সাথে শেয়ার করা হবে।</translation>
+<translation id="3008447029300691911">Enter the CVC for <ph name="CREDIT_CARD" />. Once you confirm, your card details will be shared with this site.</translation>
<translation id="3010559122411665027">তালিকার এন্ট্রি " <ph name="ENTRY_INDEX" /> ": <ph name="ERROR" /></translation>
<translation id="301521992641321250">স্বয়ংক্রিয়ভাবে ব্লক করা হয়েছে</translation>
<translation id="3024663005179499861">নীতির ভুল প্রকার</translation>
@@ -278,24 +282,23 @@
<translation id="3167968892399408617">ছদ্মবেশী ট্যাবগুলিতে আপনি যে পৃষ্ঠাগুলি দেখেন সেগুলি আপনার সব ছদ্মবেশী ট্যাব বন্ধ করে দেওয়ার পর ব্রাউজারের ইতিহাস, কুকি স্টোর বা অনুসন্ধান ইতিহাসে থাকবে না। আপনি ডাউনলোড করেছেন এমন ফাইল বা বুকমার্ক তৈরি করছেন এমন সবগুলি রেখে দেওয়া হবে।</translation>
<translation id="3169472444629675720">আবিষ্কার করুন</translation>
<translation id="3174168572213147020">দ্বীপ</translation>
-<translation id="317583078218509884">পৃষ্ঠাটি পুনঃলোডের পরে নতুন সাইট অনুমতি সেটিংস প্রভাবী হবে৷</translation>
<translation id="3176929007561373547">প্রক্সী সার্ভার কাজ করছে কি না, তা নিশ্চিত করতে আপনার প্রক্সী সেটিংস পরীক্ষা করুন
বা আপনার নেটওয়ার্ক প্রশাসকের সাথে যোগাযোগ করুন৷ আপনি কোনো প্রক্সী সার্ভার
ব্যবহার করবেন না বলে মনে করলে:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">ছদ্মবেশী মোডে পৃষ্ঠা খুলুন</translation>
<translation id="320323717674993345">পেমেন্ট বাতিল করুন</translation>
<translation id="3207960819495026254">বুকমার্ক করা হয়েছে</translation>
<translation id="3225919329040284222">সার্ভারটি এমন একটি শংসাপত্র উপস্থাপনা করেছে যা বিল্ট-ইন প্রত্যাশাগুলির সাথে মেলে না৷ এই প্রত্যাশাগুলি আপনাকে সুরক্ষিত করতে কিছু নিশ্চিত, উচ্চ সুরক্ষার ওয়েবসাইটের জন্য অন্তর্ভুক্ত৷</translation>
<translation id="3226128629678568754">পৃষ্ঠাটি লোড করতে প্রয়োজনীয় ডেটেটি আবার জমা দিতে আবার লোড করার বোতামটি টিপুন৷</translation>
<translation id="3227137524299004712">মাইক্রোফোন</translation>
<translation id="3228969707346345236">পৃষ্ঠাটি ইতিমধ্যে <ph name="LANGUAGE" />-এ থাকার কারণে অনুবাদ ব্যর্থ হয়েছে৷</translation>
-<translation id="323107829343500871"><ph name="CREDIT_CARD" /> এর CVC লিখুন</translation>
+<translation id="323107829343500871">Enter the CVC for <ph name="CREDIT_CARD" /></translation>
<translation id="3234666976984236645">এই সাইটে সর্বদা গুরুত্বপূর্ণ সামগ্রী সনাক্ত করুন</translation>
<translation id="3254409185687681395">এই পৃষ্ঠাটি বুকমার্ক করুন</translation>
<translation id="3270847123878663523">&amp;পুনর্বিন্যাসকে পূর্বাবস্থায় ফেরান</translation>
<translation id="3282497668470633863">কার্ডে থাকা নাম যোগ করুন</translation>
<translation id="3286538390144397061">এখন পুর্নসূচনা করবেন</translation>
+<translation id="3287510313208355388">যখন অনলাইন হবেন তখন ডাউনলোড করবেন</translation>
<translation id="3303855915957856445">কোনো অনুসন্ধান ফলাফল পাওয়া যায়নি</translation>
<translation id="3305707030755673451">আপনার ডেটা আপনার সিঙ্ক পাসফ্রেজ দিয়ে <ph name="TIME" /> এ এনক্রিপ্ট করা হয়েছে। সিঙ্ক শুরু করার জন্য এটি লিখুন।</translation>
<translation id="3320021301628644560">বিলিংয়ের ঠিকানা যোগ করুন</translation>
@@ -322,13 +325,12 @@
<translation id="3422472998109090673">বর্তমানে <ph name="HOST_NAME" /> পাওয়া যাচ্ছে না।</translation>
<translation id="3427092606871434483">মঞ্জুরি দিন (ডিফল্ট)</translation>
<translation id="3427342743765426898">&amp;সম্পাদনাকে আবার করুন</translation>
-<translation id="3431636764301398940">এই ডিভাইসে এই কার্ডটি সংরক্ষণ করুন</translation>
+<translation id="3431636764301398940">এই ডিভাইসে এই কার্ডটি সেভ করুন</translation>
<translation id="3435896845095436175">সক্ষম করুন</translation>
<translation id="3447661539832366887">এই ডিভাইসের মালিক ডাইনোসর গেমটি বন্ধ করেছেন৷</translation>
<translation id="3452404311384756672">বিরামকাল প্রাপ্ত করুন:</translation>
<translation id="3462200631372590220">উন্নত করার বিশদ বিবরণ, লুকান</translation>
<translation id="3467763166455606212">কার্ডহোল্ডারের নাম প্রয়োজন</translation>
-<translation id="3478058380795961209">মেয়াদপূর্তির মাস</translation>
<translation id="3479539252931486093">এটি কি অপ্রত্যাশিত ছিল? <ph name="BEGIN_LINK" />আমাদেরকে জানান<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">এখনই নয়</translation>
<translation id="3498215018399854026">আমরা এই মুহূর্তে আপনার পিতামাতার সাথে যোগাযোগ করতে পারিনি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷</translation>
@@ -344,6 +346,7 @@
&lt;p&gt;অনুগ্রহ করে &lt;strong&gt;সেটিংস&lt;/strong&gt; অ্যাপের &lt;strong&gt;সাধারণ&lt;/strong&gt; বিভাগ থেকে তারিখ এবং সময় সংশোধন করুন।&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />আরও জানুন<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">ক্রেডিট এবং প্রিপেড কার্ড গ্রহণ করা হয়।</translation>
<translation id="3582930987043644930">নাম যোগ করুন</translation>
<translation id="3583757800736429874">&amp;সরানোর কাজটি আবার করুন</translation>
<translation id="3586931643579894722">বিশদ বিবরণ লুকান</translation>
@@ -359,10 +362,10 @@
<translation id="3655670868607891010">আপনি যদি এটি প্রায়শই দেখতে পান, তাহলে <ph name="HELP_LINK" /> চেষ্টা করে দেখুন৷</translation>
<translation id="3658742229777143148">পুনর্বিবেচনা</translation>
<translation id="3678029195006412963">অনুরোধটি স্বাক্ষরিত করা যায়নি</translation>
+<translation id="3678529606614285348">নতুন ছদ্মবেশী উইন্ডোতে (Ctrl-Shift-N) করে একটি পৃষ্ঠা খুলুন</translation>
<translation id="3679803492151881375">ক্র্যাশ প্রতিবেদন <ph name="CRASH_TIME" /> এ ক্যাপচার করা হয়েছে, <ph name="UPLOAD_TIME" /> এ আপলোড করা হয়েছে</translation>
<translation id="3681007416295224113">শংসাপত্র তথ্য</translation>
<translation id="3690164694835360974">লগইন সুরক্ষিত নয়</translation>
-<translation id="3693415264595406141">পাসওয়ার্ড:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">লোড হচ্ছে...</translation>
<translation id="3712624925041724820">লাইসেন্সগুলির মেয়াদ শেষ হয়ে গেছে</translation>
@@ -374,6 +377,7 @@
<translation id="3748148204939282805"><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="375403751935624634">একটি সার্ভার ত্রুটির কারণে অনুবাদ ব্যর্থ হয়েছে৷</translation>
<translation id="3759461132968374835">আপনার কাছে সাম্প্রতিক প্রতিবেদন করা কোনও ক্র্যাশ নেই৷ ক্র্যাশ প্রতিবেদন অক্ষম থাকাকালীন ঘটা ক্র্যাশ এখানে উপস্থিত হবে না৷</translation>
+<translation id="3765032636089507299">নিরাপদ ব্রাউজিং পৃষ্ঠা তৈরি করা হচ্ছে।</translation>
<translation id="3778403066972421603">আপনি কি এই কার্ডটি আপনার Google অ্যাকাউন্টে ও এই ডিভাইসে সেভ করতে চান?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /> এ মেয়াদ শেষ হবে</translation>
@@ -386,14 +390,16 @@
<translation id="3886446263141354045">এই সাইটটি অ্যাক্সেস করার জন্য আপনার অনুরোধ <ph name="NAME" />কে পাঠানো হয়েছে</translation>
<translation id="3890664840433101773">ইমেল যোগ করুন</translation>
<translation id="3901925938762663762">কার্ডটির মেয়াদ শেষ হয়েছে</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ক্র্যাশ রিপোর্ট আইডি <ph name="CRASH_ID" /> আপলোড করা হয়েছে (স্থানীয় ক্র্যাশ আইডি: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">এই সার্ভারটিকে <ph name="DOMAIN" /> হিসাবে প্রমাণ করা যায়নি; এটির নিরাপত্তা শংসাপত্রে সাবজেক্ট অল্টারনেটিভ নেম্স নির্দিষ্ট করা নেই। কনফিগারেশনের কোনও সমস্যা অথবা আপনার সংযোগে কোনও আক্রমণকারী আড়ি পাতার কারণে এটি হয়ে থাকতে পারে।</translation>
+<translation id="3949601375789751990">আপনার ব্রাউজিং এর ইতিহাস এখানে দেখা যায়</translation>
<translation id="3963721102035795474">পাঠক মোড</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{কিছুই নয়}=1{১টি সাইট থেকে }one{#টি সাইট থেকে }other{#টি সাইট থেকে }}</translation>
<translation id="397105322502079400">গণনা করা হচ্ছে...</translation>
<translation id="3973234410852337861"><ph name="HOST_NAME" /> অবরুদ্ধ হয়ে রয়েছে</translation>
<translation id="3987940399970879459">১ MB এর কম</translation>
-<translation id="40103911065039147">{URL_count,plural, =1{আশেপাশের ১টি ওয়েবপৃষ্ঠা}one{আশেপাশের #টি ওয়েবপৃষ্ঠা}other{আশেপাশের #টি ওয়েবপৃষ্ঠা}}</translation>
+<translation id="40103911065039147">{URL_count,plural, =1{1 web page nearby}one{# web pages nearby}other{# web pages nearby}}</translation>
<translation id="4021036232240155012">DNS সেই নেটওয়ার্ক পরিষেবা যা কোনো ওয়েবসাইটের নামকে ইন্টারনেট ঠিকানায় রুপান্তরিত করে।</translation>
<translation id="4030383055268325496">&amp;যোগ করাকে পূর্বাবস্থায় ফেরান</translation>
<translation id="404928562651467259">সতর্কতা</translation>
@@ -408,7 +414,7 @@
<translation id="4115378294792113321">ম্যাজেন্টা</translation>
<translation id="4116663294526079822">এই সাইটে সর্বদা অনুমতি দিন</translation>
<translation id="4117700440116928470">নীতির সুযোগটি সমর্থিত নয়৷</translation>
-<translation id="4129401438321186435">{COUNT,plural, =1{আরো ১টি}one{অন্যান্য #টি}other{অন্যান্য #টি}}</translation>
+<translation id="4129401438321186435">{COUNT,plural, =1{আরও ১টি}one{অন্যান্য #টি}other{অন্যান্য #টি}}</translation>
<translation id="4130226655945681476">নেটওয়ার্কের তার, মডেম, এবং রাউটার পরীক্ষা করুন</translation>
<translation id="413544239732274901">আরও জানুন</translation>
<translation id="4148925816941278100">আমেরিকান এক্সপ্রেস</translation>
@@ -416,6 +422,8 @@
<translation id="4165986682804962316">সাইটের সেটিংস</translation>
<translation id="4169947484918424451">আপনি কি চান যে Chromium এই কার্ড সংরক্ষণ করুক?</translation>
<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="4196861286325780578">&amp;সরানোর কাজটি আবার করুন</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ফায়ারওয়াল এবং অ্যান্টিভাইরাস কনফিগারেশন পরীক্ষা করে দেখুন<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ক্র্যাশেস</translation>
@@ -427,7 +435,7 @@
<translation id="425582637250725228">আপনার করা পরিবর্তনগুলি সংরক্ষণ নাও করা হতে পারে।</translation>
<translation id="4258748452823770588">ত্রুটিপূর্ণ স্বাক্ষর</translation>
<translation id="4265872034478892965">আপনার প্রশাসক অনুমতি দিয়েছে</translation>
-<translation id="4269787794583293679">(কোনো ব্যবহারকারীর নাম নেই)</translation>
+<translation id="4269787794583293679">(কোনো ইউজারনেম নেই)</translation>
<translation id="4275830172053184480">আপনার ডিভাইস বন্ধ করে চালু করুন</translation>
<translation id="4280429058323657511">, মেয়াদ শেষ <ph name="EXPIRATION_DATE_ABBR" /></translation>
<translation id="4300246636397505754">মূল প্রস্তাবনাগুলি</translation>
@@ -439,12 +447,12 @@
<translation id="4356973930735388585">এই সাইটে আক্রমণকারীরা আপনার কম্পিউটারে ক্ষতিকারক প্রোগ্রাম ইনস্টল করতে পারে যা আপনার তথ্য (উদাহরণস্বরুপ, ফটো, পাসওয়ার্ড, বার্তা এবং ক্রেডিট কার্ড) চুরি করতে বা মুছে দিতে পারে।</translation>
<translation id="4372948949327679948">প্রত্যাশিত <ph name="VALUE_TYPE" /> মান৷</translation>
<translation id="4377125064752653719">আপনি <ph name="DOMAIN" />-এ পৌঁছানোর প্রচেষ্টা করেছেন, তবে সার্ভারটি যে শংসাপত্রটি উপস্থাপন করেছে সেটির জারিকর্তা সেটিকে প্রত্যাহার করেছে৷ এর অর্থ হ'ল সার্ভারটি যে সুরক্ষা প্রমানপত্র উপস্থাপন করেছে তা কোনওমতেই বিশ্বাসযোগ্য নয়৷ হতে পারে আপনি একজন আক্রমণকারীর সাথে যোগাযোগ করছেন৷</translation>
-<translation id="4381091992796011497">ব্যবহারকারী নাম:</translation>
<translation id="4394049700291259645">অক্ষম</translation>
<translation id="4406896451731180161">অনুসন্ধানের ফলাফলগুলি</translation>
<translation id="4424024547088906515">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্র Chrome এর নিকট বিশ্বাসযোগ্য নয়। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> আপনার লগইন শংসাপত্রটি স্বীকার করেনি, অথবা কোনো শংসাপত্র দেওয়া হয়নি।</translation>
<translation id="443673843213245140">প্রক্সির ব্যবহার অক্ষম করা হয়েছে কিন্তু কোনো স্পষ্ট প্রক্সি কনফিগারেশান নির্দিষ্ট করা হয়েছে৷</translation>
+<translation id="445100540951337728">ডেবিট কার্ড গ্রহণ করা হয়</translation>
<translation id="4506176782989081258">যাচাইকরণের ত্রুটি: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">সিস্টেম প্রশাসকের সাথে যোগাযোগ করে দেখুন</translation>
<translation id="450710068430902550">প্রশাসকের সাথে ভাগ করছে</translation>
@@ -456,12 +464,14 @@
<translation id="4587425331216688090">Chrome থেকে ঠিকানা সরাবেন?</translation>
<translation id="4592951414987517459">একটি আধুনিক সাইফার স্যুট ব্যবহার করে <ph name="DOMAIN" />-এ আপনার সংযোগ এনক্রিপ্ট করা হয়েছে।</translation>
<translation id="4594403342090139922">&amp;মুছে ফেলাকে পূর্বাবস্থায় ফেরান</translation>
+<translation id="4611292653554630842">লগ-ইন করুন</translation>
<translation id="4619615317237390068">অন্যান্য ডিভাইসগুলি থেকে ট্যাব</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্রে কিছু ত্রুটি আছে। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
<translation id="4690462567478992370">কোনো অবৈধ শংসাপত্র ব্যবহার করা বন্ধ করুন</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">আপনার সংযোগ বাধাপ্রাপ্ত হয়েছে</translation>
+<translation id="471880041731876836">এই সাইট দেখার অনুমতি আপনার নেই</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows নেটওয়ার্ক ডায়গনিস্টিক্স চালান<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">নীতিগুলি পুনঃলোড করুন</translation>
<translation id="4728558894243024398">প্ল্যাটফর্ম</translation>
@@ -474,7 +484,7 @@
<translation id="4759118997339041434">পেমেন্ট স্বতঃপূর্ণকরণ অক্ষম করা হয়েছে</translation>
<translation id="4764776831041365478"><ph name="URL" />-এ ওয়েবপৃষ্ঠাটি হতে পারে অস্থায়ীভাবে ডাউন আছে অথবা হতে পারে এটি স্থায়ীভাবে কোনো নতুন ওয়েব ঠিকানাতে সরানো হয়েছে৷</translation>
<translation id="4771973620359291008">একটি অজানা ত্রুটি ঘটেছে৷</translation>
-<translation id="4800132727771399293">আপনার মেয়াদ শেষের তারিখ এবং CVC পরীক্ষা করুন এবং আবার চেষ্টা করুন</translation>
+<translation id="4800132727771399293">Check your expiration date and CVC and try again</translation>
<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4807049035289105102">এই মুহূর্তে আপনি <ph name="SITE" /> এ যেতে পারবেন না কারণ ওয়েবসাইটটি অবোধ্য শংসাপত্র পাঠিয়েছে যেটি Google Chrome প্রক্রিয়া করতে পারছে না। নেটওয়ার্ক ত্রুটি এবং আক্রমণ সাধারণত সাময়িকভাবে হয়, তাই এই পৃষ্ঠা সম্ভবত পরে কাজ করবে।</translation>
<translation id="4813512666221746211">নেটওয়ার্ক ত্রুটি</translation>
@@ -483,14 +493,15 @@
<translation id="4850886885716139402">দেখুন</translation>
<translation id="4854362297993841467">এই পদ্ধতিতে ডেলিভারি করা যাবে না। অন্য পদ্ধতি ব্যবহার করুন।</translation>
<translation id="4858792381671956233">এই সাইটটি দেখার জন্য উপযুক্ত কিনা তা আপনি আপনার পিতামাতাকে জিজ্ঞাসা করেছেন</translation>
-<translation id="4863764087567530506">এই কন্টেন্ট প্রতারণার মাধ্যমে আপনাকে দিয়ে কোনও সফ্টওয়্যার ইনস্টল করাতে অথবা আপনার ব্যক্তিগত তথ্য জেনে নেওয়ার চেষ্টা করতে পারে। <ph name="BEGIN_LINK" />তবুও এটি দেখতে চাই<ph name="END_LINK" />।</translation>
<translation id="4880827082731008257">ইতিহাস অনুসন্ধান</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
-<translation id="4914479371620770914">{URL_count,plural, =1{এবং আরো ১টি ওয়েব পৃষ্ঠা}one{এবং আরো #টি ওয়েব পৃষ্ঠা}other{এবং আরো #টি ওয়েব পৃষ্ঠা}}</translation>
+<translation id="4913131542719409934">প্রমাণীকরণ প্রয়োজন</translation>
+<translation id="4914479371620770914">{URL_count,plural, =1{এবং আরও ১টি ওয়েব পৃষ্ঠা}one{এবং আরও #টি ওয়েব পৃষ্ঠা}other{এবং আরও #টি ওয়েব পৃষ্ঠা}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">এই পৃষ্ঠাটি কোন অজানা ভাষা থেকে <ph name="LANGUAGE_LANGUAGE" />-এ অনুবাদ করা হয়েছে</translation>
<translation id="4923459931733593730">অর্থপ্রদান</translation>
<translation id="4926049483395192435">নির্দিষ্ট করা উচিত৷</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">ক্রিয়াসমূহ</translation>
<translation id="4958444002117714549">তালিকা প্রসারিত করুন</translation>
<translation id="4974590756084640048">সতর্কবার্তাগুলি পুনঃসক্ষম করুন</translation>
@@ -506,6 +517,7 @@
<translation id="5045550434625856497">ভুল পাসওয়ার্ড</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>
<translation id="5087580092889165836">কার্ড জুড়ুন</translation>
<translation id="5089810972385038852">রাজ্য</translation>
@@ -513,22 +525,31 @@
<translation id="5095208057601539847">প্রদেশ</translation>
<translation id="5115563688576182185">(৬৪-বিট)</translation>
<translation id="5141240743006678641">আপনার Google শংসাপত্রের সাথে সিঙ্ক করা পাসওয়ার্ডগুলি এনক্রিপ্ট করুন৷</translation>
-<translation id="514421653919133810">ছদ্মবেশী মোডে পৃষ্ঠা খুলুন (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">নীতি প্রতিক্রিয়ার মধ্যে ত্রুটি কোড উপস্থিত</translation>
-<translation id="5171045022955879922">অনুসন্ধান করুন বা URL লিখুন</translation>
+<translation id="5159010409087891077">নতুন ছদ্মবেশী উইন্ডোতে (⇧⌘N) করে একটি পৃষ্ঠা খুলুন</translation>
+<translation id="5171045022955879922">খুঁজুন বা URL লিখুন</translation>
<translation id="5172758083709347301">যন্ত্র</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />-এ নেই? এই ত্রুটি রিপোর্ট করুন</translation>
-<translation id="5181140330217080051">ডাউনলোড হচ্ছে</translation>
<translation id="5190835502935405962">বুকমার্ক দণ্ড</translation>
<translation id="5199729219167945352">পরীক্ষাদি</translation>
<translation id="5205222826937269299">নাম প্রয়োজন</translation>
<translation id="5222812217790122047">ইমেল প্রয়োজন</translation>
<translation id="5251803541071282808">ক্লাউড</translation>
-<translation id="5277279256032773186">কর্মক্ষেত্রে Chrome ব্যবহার করছেন? ব্যবসাগুলো তাদের কর্মচারীদের জন্য Chrome সেটিংস পরিচালনা করতে পারে। আরো জানুন</translation>
+<translation id="5277279256032773186">কর্মক্ষেত্রে Chrome ব্যবহার করছেন? ব্যবসাগুলো তাদের কর্মচারীদের জন্য Chrome সেটিংস পরিচালনা করতে পারে। আরও জানুন</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />সফ্টওয়্যারটি সাময়িকভাবে নিষ্ক্রিয় করার জন্য এই পদক্ষেপগুলি অনুসরণ করুন যাতে আপনি ওয়েবে যেতে পারেন। আপনার প্রশাসকের অধিকার প্রয়োজন হবে।<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />শুরু করুন<ph name="END_BOLD" /> এ ক্লিক করুন, এবং তারপর <ph name="BEGIN_BOLD" />"স্থানীয় পরিষেবা দেখুন"<ph name="END_BOLD" /> অনুসন্ধান করে বেছে নিন
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" /> বেছে নিন
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />শুরু করার ধরন<ph name="END_BOLD" /> এর অধীনে <ph name="BEGIN_BOLD" />অক্ষম করুন <ph name="END_BOLD" /> বেছে নিন
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />পরিষেবা স্থিতির<ph name="END_BOLD" /> অধীনে <ph name="BEGIN_BOLD" />বন্ধ করুন <ph name="END_BOLD" /> এ ক্লিক করুন
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />প্রয়োগ করুন <ph name="END_BOLD" />এ ক্লিক করুন তারপর <ph name="BEGIN_BOLD" />ঠিক আছে<ph name="END_BOLD" /> তে ক্লিক করুন
+ <ph name="LIST_ITEM" />আপনার কম্পিউটার থেকে কিভাবে স্থায়ীভাবে সফ্টওয়্যার সরিয়ে ফেলতে হয় সেটি জানতে <ph name="BEGIN_LEARN_MORE_LINK" />Chrome সহায়তা কেন্দ্রে<ph name="END_LEARN_MORE_LINK" /> দেখুন
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">এই সাইটে আপনার সংযোগ ব্যক্তিগত নয়। যেকোনো সময় (ভিআর)VR মোড থেকে বেরিয়ে যাওয়ার জন্য, হেডসেট সরান এবং 'পিছনে' চাপুন।</translation>
<translation id="5299298092464848405">নীতি বিশ্লেষণ করার সময় ত্রুটি</translation>
<translation id="5308689395849655368">ক্র্যাশ প্রতিবেদন অক্ষম আছে৷</translation>
-<translation id="5317780077021120954">সংরক্ষণ করুন</translation>
+<translation id="5317780077021120954">সেভ করুন</translation>
<translation id="5327248766486351172">নাম</translation>
<translation id="5355557959165512791">ওয়েবসাইটটির শংসাপত্র তুলে নেওয়ার কারণে আপনি এখন <ph name="SITE" /> এ যেতে পারবেন না। নেটওয়ার্ক ত্রুটি এবং আক্রমণ সাধারণত সাময়িকভাবে হয়, তাই এই পৃষ্ঠাটি সম্ভবত পরে কাজ করবে।</translation>
<translation id="536296301121032821">নীতি সেটিংস সংরক্ষণ করতে ব্যর্থ হয়েছে</translation>
@@ -541,9 +562,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" এ স্কিমা বৈধতার ত্রুটি হয়েছে: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">এই <ph name="HOST_NAME" /> পৃষ্ঠাটি পাওয়া যাচ্ছে না</translation>
<translation id="5455374756549232013">ত্রুটিপূর্ণ নীতি টাইমস্ট্যাম্প</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" /> এর <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">অবৈধ</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />টি}one{<ph name="CONTACT_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />টি}other{<ph name="CONTACT_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />টি}}</translation>
<translation id="5470861586879999274">&amp;সম্পাদনাকে আবার করুন</translation>
+<translation id="5481076368049295676">এই কন্টেন্ট আপনার ডিভাইসে এমন বিপজ্জনক সফ্টওয়্যার ইনস্টল করার চেষ্টা করতে পারে যা আপনার তথ্য চুরি করে বা মুছে দেয়। <ph name="BEGIN_LINK" />তবুও এটি দেখতে চাই<ph name="END_LINK" /></translation>
<translation id="54817484435770891">বৈধ ঠিকানা যোগ করুন</translation>
<translation id="5492298309214877701">কোম্পানী, সংস্থা বা স্কুল ইন্ট্রানেটে এই সাইটটির একটি বাহ্যিক ওয়েবসাইটের মতো একই URL আছে।
<ph name="LINE_BREAK" />
@@ -555,6 +577,7 @@
<translation id="5540224163453853">অনুরোধকৃত নিবন্ধ খুঁজে পাওয়া যায়নি৷</translation>
<translation id="5544037170328430102"><ph name="SITE" /> এ একটি এম্বেডেড পৃষ্ঠা বলছে:</translation>
<translation id="5556459405103347317">আবার লোড করুন</translation>
+<translation id="5560088892362098740">মেয়াদ শেষের তারিখ</translation>
<translation id="5565735124758917034">সক্রিয়</translation>
<translation id="5571083550517324815">এই ঠিকানা থেকে পিক-আপ করা যাবে না। অন্য ঠিকানা বেছে নিন।</translation>
<translation id="5572851009514199876">আপনার এই সাইটে অ্যাক্সেস করার অনুমতি আছে কিনা তা Chrome পরীক্ষা করার জন্য অনুগ্রহ করে শুরু করুন এবং Chrome এ প্রবেশ করুন।</translation>
@@ -569,12 +592,13 @@
<translation id="5629630648637658800">নীতি সেটিংস লোড করতে ব্যর্থ হয়েছে</translation>
<translation id="5631439013527180824">অবৈধ ডিভাইস পরিচালনা টোকেন</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="5646376287012673985">অবস্থান</translation>
<translation id="5659593005791499971">ইমেল</translation>
<translation id="5669703222995421982">ব্যক্তিগতকৃত সামগ্রী পান</translation>
<translation id="5675650730144413517">এই পৃষ্ঠাটি কাজ করছে না</translation>
<translation id="5710435578057952990">এই ওয়েবসাইটির পরিচয় যাচাই করা হয় নি৷</translation>
-<translation id="5713016350996637505">প্রতারণামূলক কন্টেন্ট ব্লক করা হয়েছে</translation>
+<translation id="5719499550583120431">প্রিপেড কার্ড গ্রহণ করা হয়।</translation>
<translation id="5720705177508910913">বর্তমান ব্যবহারকারী</translation>
<translation id="5732392974455271431">আপনার পিতামাতা এটি আপনার জন্য অবরোধ মুক্ত করতে পারবেন</translation>
<translation id="5763042198335101085">একটি সঠিক ইমেল ঠিকানা লিখুন</translation>
@@ -586,15 +610,14 @@
<translation id="5803412860119678065">আপনি কি আপনার <ph name="CARD_DETAIL" /> এর তথ্য পূরণ করতে চান?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" />-এ আপনার সংযোগ একটি অপ্রচলিত সাইফার স্যুট ব্যবহার করে এনক্রিপ্ট করা হয়েছে৷</translation>
<translation id="5813119285467412249">&amp;যোগ করাকে পুনরায় করুন</translation>
-<translation id="5814352347845180253">আপনি <ph name="SITE" /> এবং অন্যান্য সাইট থেকে প্রিমিয়াম সামগ্রীতে অ্যাক্সেস হারাতে পারেন।</translation>
<translation id="5838278095973806738">এই সাইটে আপনার কোনো সংবেদনশীল তথ্য দেওয়া উচিত হবে না (উদাহরণস্বরূপ, পাসওয়ার্ড বা ক্রেডিট কার্ড) কারণ আক্রমণকারীরা এগুলি চুরি করতে পারে।</translation>
<translation id="5869405914158311789">এই সাইটটিতে পৌছানো যাচ্ছে না</translation>
<translation id="5869522115854928033">সংরক্ষিত পাসওয়ার্ড</translation>
<translation id="5872918882028971132">মূল প্রস্তাবনাগুলি</translation>
+<translation id="5893752035575986141">ক্রেডিট কার্ড গ্রহণ করা হয়।</translation>
<translation id="5901630391730855834">হলুদ</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (সিঙ্ক হয়েছে)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{১টি ব্যবহৃত হচ্ছে}one{#টি ব্যবহৃত হচ্ছে}other{#টি ব্যবহৃত হচ্ছে}}</translation>
-<translation id="5926846154125914413">আপনি কিছু সাইট থেকে প্রিমিয়াম সামগ্রীতে অ্যাক্সেস হারাতে পারেন।</translation>
<translation id="5959728338436674663">বিপজ্জনক অ্যাপ্লিকেশান ও সাইটগুলি সনাক্ত করতে Google এর কাছে কিছু<ph name="BEGIN_WHITEPAPER_LINK" /> সিস্টেম তথ্য ও পৃষ্ঠার সামগ্রী<ph name="END_WHITEPAPER_LINK" /> স্বয়ংক্রিয়ভাবে পাঠান। <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">ইতিহাস থেকে সরান</translation>
<translation id="5975083100439434680">জুম কমান</translation>
@@ -608,8 +631,9 @@
<translation id="6025416945513303461"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /> (সিঙ্ক হয়েছে)</translation>
<translation id="6027201098523975773">একটি নাম লিখুন</translation>
<translation id="6040143037577758943">বন্ধ</translation>
-<translation id="6042308850641462728">আরো</translation>
+<translation id="6042308850641462728">আরও</translation>
<translation id="6047233362582046994">আপনি যদি আপনার নিরাপত্তার ঝুঁকিগুলি বুঝে নিয়ে থাকেন, তাহলে ক্ষতিকারক অ্যাপগুলি সরানোর আগে <ph name="BEGIN_LINK" />এই সাইটে যেতে পারেন<ph name="END_LINK" />৷</translation>
+<translation id="6047927260846328439">এই কন্টেন্ট প্রতারণার মাধ্যমে আপনাকে দিয়ে কোনও সফ্টওয়্যার ইনস্টল করাতে অথবা আপনার ব্যক্তিগত তথ্য জেনে নেওয়ার চেষ্টা করতে পারে। <ph name="BEGIN_LINK" />তবুও এটি দেখতে চাই<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">ওয়েবসাইটটি পিন করা শংসাপত্র ব্যবহার করার কারণে আপনি এখন <ph name="SITE" /> এ যেতে পারবেন না। নেটওয়ার্ক ত্রুটি এবং আক্রমণ সাধারণত সাময়িকভাবে হয়, তাই এই পৃষ্ঠাটি সম্ভবত পরে কাজ করবে।</translation>
<translation id="6060685159320643512">সাবধান হন, এই পরীক্ষাগুলি সমস্যা সৃষ্টি করতে পারে</translation>
<translation id="6080696365213338172">প্রশাসকের দ্বারা সরবরাহ করা শংসাপত্রের ব্যবহার করে আপনি সামগ্রী ব্যবহার করেছেন৷ <ph name="DOMAIN" /> কে আপনি যে ডেটা সরবরাহ করেন তা আপনার প্রশাসক বাধা দিতে পারে৷</translation>
@@ -619,10 +643,9 @@
<translation id="6151417162996330722">সার্ভারের শংসাপত্রের বৈধতার সময়সীমা আছে যা খুবই দীর্ঘ।</translation>
<translation id="6157877588268064908">শিপিং এর পদ্ধতি এবং প্রয়োজনীয়তা দেখতে একটি ঠিকানা বেছে নিন</translation>
<translation id="6158003235852588289">Google এর নিরাপদ ব্রাউজিং সম্প্রতি <ph name="SITE" /> এ ফিশিং শনাক্ত করেছে। ফিশিং সাইটগুলি আপনাকে প্রতারিত করার জন্য অন্যান্য সাইট যেমন হয় সেইরকম ভান করে।</translation>
-<translation id="6165508094623778733">আরো জানুন</translation>
+<translation id="6165508094623778733">আরও জানুন</translation>
<translation id="6169916984152623906">এখন আপনি গোপনভাবে ব্রাউজ করতে পারেন, এবং অন্য যেসব বক্তি এই ডিভাইস ব্যবহার করেন তারা আপনার কার্যকলাপ দেখতে পাবেন না। তবে, আপনার ডাউনলোড এবং বুকমার্কগুলি সংরক্ষণ করা হবে।</translation>
<translation id="6177128806592000436">এই সাইটে আপনার সংযোগ নিরাপদ নয়</translation>
-<translation id="6184817833369986695">(দল: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">আপনার ইন্টারনেট সংযোগ পরীক্ষা করুন</translation>
<translation id="6218753634732582820">Chromium থেকে ঠিকানা সরাবেন?</translation>
<translation id="6221345481584921695">Google নিরাপদ ব্রাউজিং সাম্প্রতিক <ph name="SITE" /> এ <ph name="BEGIN_LINK" />ম্যালওয়্যার শনাক্ত করেছে<ph name="END_LINK" />। যেসব ওয়েবসাইট সাধারণত নিরাপদ থাকে, সেগুলি কখনও কখনও ম্যালওয়্যার দ্বারা আক্রান্ত হয়। <ph name="SUBRESOURCE_HOST" />, একটি পরিচিত ম্যালওয়্যার বিতরণকারী, থেকে ক্ষতিকারক সামগ্রী আসে। কয়েক ঘণ্টা পরে আপনার ফিরে আসা উচিত।</translation>
@@ -641,20 +664,21 @@
<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>
+<translation id="6386120369904791316">{COUNT,plural, =1{আরও ১টি প্রস্তাব}one{অন্যান্য #টি প্রস্তাব}other{অন্যান্য #টি প্রস্তাব}}</translation>
<translation id="6387478394221739770">Chrome এর নতুন দুর্দান্ত বৈশিষ্ট্যগুলিতে আগ্রহী? chrome.com/beta এ আমাদের বিটা চ্যানেল ব্যবহার করে দেখুন৷</translation>
-<translation id="6389758589412724634">এই ওয়েবপৃষ্ঠা প্রদর্শন করার সময় Chromium এর মেমরি শেষ হয়ে গেছে।</translation>
+<translation id="6397451950548600259">আপনার কম্পিউটারে সফ্টওয়্যারটি নিরাপদে ওয়েব সংযোগের করতে Chrome কে বাধা দিচ্ছে</translation>
<translation id="6404511346730675251">বুকমার্ক সম্পাদনা করুন</translation>
-<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> এর মেয়াদ শেষের তারিখ এবং CVC লিখুন</translation>
+<translation id="6410264514553301377">Enter the expiration date and CVC for <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">এই সাইটটি ঘুরে দেখা ঠিক হবে কিনা সেই বিষয়ে আপনি আপনার পিতামাতাকে জিজ্ঞাসা করেছেন</translation>
<translation id="6417515091412812850">শংসাপত্রকরণটি প্রত্যাহার করা হয়েছে কিনা তা যাচাইয়ে অক্ষম৷</translation>
<translation id="6433490469411711332">পরিচিতি তথ্য সম্পাদনা করুন</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> সংযোগ করতে প্রত্যাখ্যান করেছে।</translation>
-<translation id="6446608382365791566">আরো তথ্য যোগ করুন</translation>
+<translation id="6446608382365791566">আরও তথ্য যোগ করুন</translation>
<translation id="6447842834002726250">কুকিজ</translation>
<translation id="6451458296329894277">ফর্ম পুনঃজমা নিশ্চিত করুন</translation>
<translation id="6456339708790392414">আপনার অর্থপ্রদান</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">ডিভাইস নীতিগুলি</translation>
<translation id="6477321094435799029">Chrome এই পৃষ্ঠাতে অস্বাভাবিক কোড পেয়েছে এবং আপনার ব্যক্তিগত তথ্যের (উদাহরণস্বরূপ, পাসওয়ার্ড, ফোন নম্বর, এবং ক্রেডিট কার্ড) সুরক্ষার জন্য এটি অবরুদ্ধ করেছে।</translation>
<translation id="6489534406876378309">ক্র্যাশগুলি আপলোড করা শুরু করুন</translation>
+<translation id="6507833130742554667">ক্রেডিট ও ডেবিট কার্ড গ্রহণ করা হয়।</translation>
<translation id="6508722015517270189">Chrome পুনরায় চালু করুন</translation>
<translation id="6529602333819889595">&amp;মুছে ফেলাকে আবার করুন</translation>
<translation id="6534179046333460208">বাস্তবিক ওয়েব প্রস্তাবনাগুলি</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">এই কন্টেন্ট আপনার ডিভাইসে এমন বিপজ্জনক সফ্টওয়্যার ইনস্টল করার চেষ্টা করতে পারে যা আপনার তথ্য চুরি করে বা মুছে দেয়। <ph name="BEGIN_LINK" />তবুও এটি দেখতে চাই<ph name="END_LINK" />।</translation>
<translation id="6596325263575161958">এনক্রিপশন বিকল্পগুলি</translation>
<translation id="662080504995468778">থাকুন</translation>
<translation id="6626291197371920147">বৈধ কার্ড নম্বর যোগ করুন</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> অনুসন্ধান</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> এ যে আক্রমণকারীরা এই মুহূর্তে সক্রিয় আছে, তারা আপনার Mac এ এমন বিপজ্জনক প্রোগ্রাম ইনস্টল করে দিতে পারে যেগুলি আপনার তথ্যের (যেমন ফটো, পাসওয়ার্ড, মেসেজ এবং ক্রেডিট কার্ড) ক্ষতি করতে বা সেগুলি চুরি করতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরও জানুন<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">এই নীতিটি অসমর্থিত হয়েছে৷</translation>
+<translation id="6657585470893396449">পাসওয়ার্ড</translation>
<translation id="6671697161687535275">Chromium থেকে ফর্ম প্রস্তাবনা সরাবেন?</translation>
<translation id="6685834062052613830">প্রস্থান করুন করে সেটআপ সম্পূর্ণ করুন</translation>
<translation id="6710213216561001401">পূর্ববর্তী</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">জেলা</translation>
<translation id="6973656660372572881">স্থির প্রক্সি সার্ভার এবং .pac স্ক্রিপ্ট URL-এর উভয়ই নির্দিষ্ট আছে৷</translation>
<translation id="6989763994942163495">উন্নত সেটিংস দেখান ...</translation>
-<translation id="7000990526846637657">কোনো ইতিহাস এন্ট্রি পাওয়া যায়নি</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985"><ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> এ আপনার Google অ্যাকাউন্টের অন্যান্য ধরনের ব্রাউজিং ইতিহাস থাকতে পারে</translation>
<translation id="7029809446516969842">পাসওয়ার্ড</translation>
@@ -716,13 +740,15 @@
<translation id="7064851114919012435">পরিচিতি তথ্য</translation>
<translation id="7079718277001814089">এই সাইটে মালওয়ের আছে</translation>
<translation id="7087282848513945231">দেশ</translation>
-<translation id="7090678807593890770">Google এ <ph name="LINK" /> এর অনুসন্ধান করুন</translation>
+<translation id="7090678807593890770">Google এ <ph name="LINK" /> এর খুঁজুন</translation>
<translation id="7108819624672055576">একটি এক্সটেনশন দ্বারা অনুমোদিত</translation>
<translation id="7119414471315195487">অন্যান্য ট্যাব বা প্রোগ্রাম বন্ধ করুন</translation>
<translation id="7129409597930077180">এই ঠিকানায় শিপিং করা যাবে না। অন্য ঠিকানা বেছে নিন।</translation>
<translation id="7138472120740807366">ডেলিভারির পদ্ধতি</translation>
<translation id="7139724024395191329">এমিরেট</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />টি}one{<ph name="PAYMENT_METHOD_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />টি}other{<ph name="PAYMENT_METHOD_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />টি}}</translation>
<translation id="7155487117670177674">পেমেন্ট সুরক্ষিত নয়</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />টি}one{<ph name="SHIPPING_OPTION_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />টি}other{<ph name="SHIPPING_OPTION_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />টি}}</translation>
<translation id="7179921470347911571">এখনই পুনঃলঞ্চ করুন</translation>
<translation id="7180611975245234373">রিফ্রেশ করুন</translation>
<translation id="7182878459783632708">কোন নীতি সেট করা নেই</translation>
@@ -730,11 +756,11 @@
<translation id="7192203810768312527"><ph name="SIZE" /> জায়গা খালি করে। পরের বার যখন দেখবেন তখন কিছু সাইট লোড হতে দেরি হতে পারে।</translation>
<translation id="719464814642662924">Visa</translation>
<translation id="7210863904660874423"><ph name="HOST_NAME" /> নিরাপত্তা মান মেনে চলে না।</translation>
-<translation id="721197778055552897">এই সমস্যাটি সম্পর্কে <ph name="BEGIN_LINK" />আরো জানুন<ph name="END_LINK" />৷</translation>
+<translation id="721197778055552897">এই সমস্যাটি সম্পর্কে <ph name="BEGIN_LINK" />আরও জানুন<ph name="END_LINK" />৷</translation>
<translation id="7219179957768738017">এই সংযোগটি <ph name="SSL_VERSION" />টি ব্যবহার করে</translation>
<translation id="7220786058474068424">প্রক্রিয়ায় রয়েছে</translation>
<translation id="724691107663265825">এই সাইটটিতে ম্যালওয়্যার আছে</translation>
-<translation id="724975217298816891">আপনার কার্ডের বিবরণ আপডেট করার জন্য মেয়াদ শেষের তারিখ এবং <ph name="CREDIT_CARD" /> এর CVC লিখুন। আপনি নিশ্চিত করলে, আপনার কার্ডের বিবরণ এই সাইটের সাথে শেয়ার করা হবে।</translation>
+<translation id="724975217298816891">Enter the expiration date and CVC for <ph name="CREDIT_CARD" /> to update your card details. Once you confirm, your card details will be shared with this site.</translation>
<translation id="7260504762447901703">অ্যাক্সেস প্রত্যাহার করুন</translation>
<translation id="7275334191706090484">পরিচালিত বুকমার্কগুলি</translation>
<translation id="7298195798382681320">প্রস্তাবিত</translation>
@@ -742,7 +768,7 @@
<translation id="7334320624316649418">&amp;পুনর্বিন্যাসকে আবার করুন</translation>
<translation id="733923710415886693">সার্ভারের শংসাপত্রটি শংসাপত্রের স্বচ্ছতার মাধ্যমে প্রকাশ করা হয়নি।</translation>
<translation id="7353601530677266744">কম্যান্ড লাইন</translation>
-<translation id="7372973238305370288">ফলাফল অনুসন্ধান করুন</translation>
+<translation id="7372973238305370288">ফলাফল খুঁজুন</translation>
<translation id="7377249249140280793"><ph name="RELATIVE_DATE" /> - <ph name="FULL_DATE" /></translation>
<translation id="7378627244592794276">না</translation>
<translation id="7378810950367401542">/</translation>
@@ -753,7 +779,7 @@
<translation id="7441627299479586546">ভুল বিষয় বিশিষ্ট নীতি</translation>
<translation id="7444046173054089907">সাইটটি অবরুদ্ধ</translation>
<translation id="7445762425076701745">আপনি যে সার্ভারে সংযুক্ত রয়েছে সেটিকে সম্পূর্ণ যাচাই করতে পারা যায় না৷ আপনি নামগুলি দিয়ে এমন একটি সার্ভারে সংযুক্ত রয়েছেন যা আপনার নেটওয়ার্কে বৈধ, যেটি একটি বাহ্যিক শংসাকরণ কর্তৃপক্ষ যার এটির মালিকানা যাচাই করার কোনও উপায় নেই৷ কিছু শংসাপত্র কর্তৃপক্ষ এই নামগুলি নির্বিচারে শংসাপত্রগুলি ইস্যু করবে, আপনি উদ্দিষ্ট ওয়েবসাইটে সংযুক্ত রয়েছেন কোনও আক্রমণকারীতে নয় তা নিশ্চিত করার কোনও উপায় নেই৷</translation>
-<translation id="7451311239929941790">এই সমস্যা সম্পর্কে <ph name="BEGIN_LINK" />আরো জানুন<ph name="END_LINK" />।</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>
@@ -761,8 +787,9 @@
<translation id="7485870689360869515">কোনো ডেটা পাওয়া যায়নি৷</translation>
<translation id="7508255263130623398">ফিরে পাওয়া নীতির ডিভাইস আইডি খালি অথবা বর্তমান ডিভাইস আইডির সাথে মিলছে না</translation>
<translation id="7514365320538308">ডাউনলোড করুন</translation>
-<translation id="7518003948725431193">এই ওয়েব ঠিকানার কোনও ওয়েবপৃষ্ঠা পাওয়া যায় নি: <ph name="URL" /></translation>
+<translation id="7518003948725431193">No webpage was found for the web address: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">এই সাইটে আপনার সংযোগ ব্যক্তিগত নয়</translation>
<translation id="7535087603100972091">মান</translation>
<translation id="7537536606612762813">বাধ্যতামূলক</translation>
<translation id="7542403920425041731">আপনি নিশ্চিত করলে আপনার কার্ডের বিবরণ এই সাইটের সাথে শেয়ার করা হবে।</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">নিম্নোল্লিখিত টিপ্স ব্যবহার করে দেখুন:</translation>
<translation id="7554791636758816595">নতুন ট্যাব</translation>
<translation id="7567204685887185387">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্র প্রতারণাপূর্ণভাবে ইস্যু করা হয়ে থাকতে পারে। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />টি}one{<ph name="CONTACT_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />টি}other{<ph name="CONTACT_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />টি}}</translation>
<translation id="7568593326407688803">এই পৃষ্ঠাটি<ph name="ORIGINAL_LANGUAGE" />ভাষাতে আছে আপনি কি এটিকে অনুবাদ করতে চাইবেন?</translation>
<translation id="7569952961197462199">Chrome থেকে ক্রেডিট কার্ড সরাবেন?</translation>
<translation id="7569983096843329377">কালো</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">শংসাপত্র</translation>
<translation id="7716147886133743102">আপনার প্রশাসক ব্লক করেছে</translation>
<translation id="7716424297397655342">এই সাইটটি ক্যাশে থেকে লোড করা যাবে না</translation>
+<translation id="774634243536837715">বিপজ্জনক কন্টেন্ট ব্লক করা হয়েছে।</translation>
<translation id="7752995774971033316">অপরিচালিত</translation>
<translation id="7755287808199759310">আপনার পিতামাতা এটি আপনার জন্য অবরোধ মুক্ত করতে পারবেন</translation>
<translation id="7758069387465995638">ফায়ারওয়াল বা অ্যান্টিভাইরাস সফটওয়্যার সংযোগকে অবরুদ্ধ করে থাকতে পারে।</translation>
+<translation id="7759163816903619567">ডিসপ্লে ডোমেন:</translation>
<translation id="7761701407923456692">সার্ভারের শংসাপত্র URL-এর সাথে মেলে না৷</translation>
<translation id="7763386264682878361">পেমেন্ট ম্যানিফেস্ট বিশ্লেষক</translation>
<translation id="7764225426217299476">ঠিকানা যোগ করুন</translation>
@@ -816,7 +844,8 @@
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />' এর জন্য <ph name="NUMBER_OF_RESULTS" />টি <ph name="SEARCH_RESULTS" /> খুঁজে পাওয়া গেছে</translation>
<translation id="785549533363645510">আপনি অবশ্য অদৃশ্য থাকবেন না। ছদ্মবেশী মোডে গেলেও তা আপনার নিয়োগকর্তা, আপনার ইন্টারনেট পরিষেবা প্রদানকারী অথবা আপনার পরিদর্শন করা ওয়েবসাইট থেকে আপনার ব্রাউজিংকে আড়াল করবে না।</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
-<translation id="7887683347370398519">আপনার CVC পরীক্ষা করুন এবং আবার চেষ্টা করুন</translation>
+<translation id="7878176543348854470">ডেবিট ও প্রিপেড কার্ড গ্রহণ করা হয়।</translation>
+<translation id="7887683347370398519">Check your CVC and try again</translation>
<translation id="79338296614623784">একটি সঠিক ফোন নম্বর লিখুন</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">সার্ভারের শংসাপত্র এখনও কার্যকর নয়.</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">প্রতিক্রিয়া পাঠান</translation>
<translation id="8041940743680923270">বিশ্বব্যাপী ডিফল্ট ব্যবহার করুন (জানতে চান)</translation>
<translation id="8088680233425245692">নিবন্ধ দেখতে ব্যর্থ হয়েছে৷</translation>
-<translation id="8089520772729574115">১ মেগাবাইটের কম</translation>
<translation id="8091372947890762290">সার্ভারে সক্রিয়করণ বাকি আছে</translation>
<translation id="8118489163946903409">পেমেন্টের পদ্ধতি</translation>
<translation id="8131740175452115882">নিশ্চিত হন</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;সরানোকে পূর্বাবস্থায় ফেরান</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" ID যুক্ত এক্সটেনশানের অবৈধ আপডেট URL।</translation>
<translation id="8202097416529803614">অর্ডারের সারসংক্ষেপ</translation>
+<translation id="8205463626947051446">সাইটটিতে বিরক্তিকরভাবে বিজ্ঞাপন দেখানো হয়</translation>
<translation id="8218327578424803826">নির্ধারিত অবস্থান:</translation>
<translation id="8225771182978767009">এই কম্পিউটার যিনি সেট আপ করেছেন তিনি এই সাইটটি অবরুদ্ধ করার বিষয়টি চয়ন করেছেন।</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">নতুন ছদ্মবেশী ট্যাবে একটি পৃষ্ঠা খুলুন</translation>
<translation id="8241707690549784388">আপনি যে পৃষ্ঠাটি খুঁজছেন সেটি আপনার প্রবেশ করানো তথ্য ব্যবহার করছে৷ এই পৃষ্ঠাতে ফিরে এলে কোনো ক্রিয়া আবার করতে হতে পারে৷ আপনি কি অবিরত করতে চান?</translation>
+<translation id="8241712895048303527">এই সাইটে ব্লক করুন</translation>
<translation id="8249320324621329438">সর্বশেষ প্রাপ্ত করেছে:</translation>
<translation id="8253091569723639551">বিলিং ঠিকানা প্রয়োজন</translation>
<translation id="8261506727792406068">মুছুন</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">নেটওয়ার্ক সংযোগে কোন সমস্যা হওয়ার কারণে অনুবাদ ব্যর্থ হয়েছে৷</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> এ অ্যাক্সেস অস্বীকার করা হয়েছে</translation>
<translation id="834457929814110454">আপনি যদি আপনার নিরাপত্তার ঝুঁকিগুলি বুঝতে পারেন, তাহলে ক্ষতিকারক প্রোগ্রাম সরানোর আগে আপনি <ph name="BEGIN_LINK" />এই সাইটে যেতে পারেন<ph name="END_LINK" />৷</translation>
-<translation id="8344669043927012510">ছদ্মবেশী মোডে পৃষ্ঠা খুলুন (⇧⌘N)</translation>
<translation id="8349305172487531364">বুকমার্কস দণ্ড</translation>
<translation id="8363502534493474904">বিমান মোড বন্ধ করে দেখুন</translation>
<translation id="8364627913115013041">সেট করা নেই৷</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">ক্র্যাশ প্রতিবেদন <ph name="UPLOAD_TIME" /> এ আপলোড করা হয়েছে</translation>
<translation id="8412145213513410671">ক্র্যাশ (<ph name="CRASH_COUNT" />টি)</translation>
<translation id="8412392972487953978">আপনাকে একই পাসফ্রেজ অবশ্যই দু'বার প্রবেশ করাতে হবে৷</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">সেটিংস</translation>
<translation id="8433057134996913067">এটি বেশিরভাগ ওয়েবসাইট থেকে আপনাকে প্রস্থান করুন করবে।</translation>
<translation id="8437238597147034694">&amp;সরানোকে পূর্বাবস্থায় ফেরান</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">আপনার Google অ্যাকাউন্ট থেকে কার্ড ব্যবহার করার জন্য Chrome এ সাইন ইন করুন</translation>
<translation id="8488350697529856933">এতে প্রয়োগ হয়</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> সাড়া দিতে খুবে বেশি সময় নিয়েছে।</translation>
-<translation id="8532105204136943229">মেয়াদপূর্তির বছর</translation>
+<translation id="8503813439785031346">ইউজারনেম</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="8571890674111243710">পৃষ্ঠাটি<ph name="LANGUAGE" />তে অনুবাদ করুন...</translation>
<translation id="858637041960032120">ফোননম্বর জুড়ুন</translation>
<translation id="859285277496340001">শংসাপত্রটি প্রত্যাহার করা হয়েছে কিনা তা যাচাই করতে শংসাপত্রটি কোনও কারিগরীকে নির্দিষ্ট করে না৷</translation>
<translation id="8620436878122366504">আপনার পিতামাতা এখনও এটি অনুমোদন করেন নি</translation>
<translation id="8647750283161643317">সবগুলিকে ডিফল্টে আবার সেট করুন</translation>
+<translation id="8660471606262461360">Google Payments থেকে</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" />-এ আপনার কানেকশন এনক্রিপ্ট হয় নি৷</translation>
<translation id="8718314106902482036">পেমেন্ট করা যায়নি</translation>
<translation id="8725066075913043281">আবার চেষ্টা করুন</translation>
@@ -913,26 +946,27 @@
<translation id="8903921497873541725">জুম বাড়ান</translation>
<translation id="8931333241327730545">আপনি কি আপনার Google অ্যাকাউন্টে এই কার্ড সংরক্ষণ করতে চান?</translation>
<translation id="8932102934695377596">আপনার ঘড়ির সময় পিছিয়ে রয়েছে</translation>
+<translation id="8938939909778640821">ক্রেডিট ও প্রিপেড কার্ড গ্রহণ করা হয়</translation>
<translation id="8971063699422889582">সার্ভারের শংসাপত্রের মেয়াদ ফুরিয়েছে৷</translation>
<translation id="8986494364107987395">ব্যবহারের পরিসংখ্যান এবং ক্র্যাশ প্রতিবেদনগুলি স্বয়ংক্রিয়ভাবে Google-এ পাঠান</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
<translation id="8996941253935762404">সামনের সাইটটিতে ক্ষতিকর প্রোগ্রামগুলি রয়েছে</translation>
<translation id="8997023839087525404">সার্ভারটি এমন একটি শংসাপত্র উপস্থাপন করেছে যেটি শংসাপত্রের স্বচ্ছতার নীতি ব্যবহার করে সর্বজনীনভাবে প্রকাশ করা হয়নি। এটা কিছু শংসাপত্রের জন্য একটি আবশ্যকতা, যাতে করে সেগুলির বিশ্বাসযোগ্যত করা যায় এবং আক্রমণকারীদের বিরুদ্ধে সুরক্ষা নেওয়া যায়।</translation>
-<translation id="9001074447101275817"><ph name="DOMAIN" /> প্রক্সীটির একটি ব্যবহারকারী নাম এবং পাসওয়ার্ড প্রয়োজন।</translation>
+<translation id="9001074447101275817"><ph name="DOMAIN" /> প্রক্সীটির একটি ইউজারনেম এবং পাসওয়ার্ড প্রয়োজন।</translation>
<translation id="9005998258318286617">PDF ডকুমেন্ট লোড করা যায়নি।</translation>
<translation id="901974403500617787">যে ফ্ল্যাগগুলি সমস্ত সিস্টেম জুড়ে প্রয়োগ করা হয় সেগুলি শুধুমাত্র মালিকের দ্বারা সেট করা যেতে পারে: <ph name="OWNER_EMAIL" />৷</translation>
<translation id="9020200922353704812">কার্ডের বিলিং ঠিকানা প্রয়োজন</translation>
<translation id="9020542370529661692">এই পৃষ্ঠাটি <ph name="TARGET_LANGUAGE" /> এ অনুবাদ করা হয়েছে</translation>
<translation id="9035022520814077154">নিরাপত্তা ত্রুটি</translation>
-<translation id="9038649477754266430">পৃষ্ঠা আরো দ্রুত লোড করার জন্য কোনো পূর্বাভাষ পরিষেবা ব্যবহার করুন</translation>
+<translation id="9038649477754266430">পৃষ্ঠা আরও দ্রুত লোড করার জন্য কোনো পূর্বাভাষ পরিষেবা ব্যবহার করুন</translation>
<translation id="9039213469156557790">উপরন্তু, এই পৃষ্ঠাতে অন্যান্য সংস্থান অন্তর্ভুক্ত রয়েছে যা নিরাপদ নয়৷ এই সংস্থানগুলি ট্রানজিটের সময় অন্যরা দেখতে পাবে এবং পৃষ্ঠাটির আচরণ পরিবর্তন করার জন্য কোনো আক্রমণকারী এর পরিবর্তন করতে পারেন৷</translation>
<translation id="9049981332609050619">আপনি <ph name="DOMAIN" />-এ পৌছানোর প্রয়াস করছেন, কিন্তু সার্ভার একটি অবৈধ শংসাপত্র উপস্থাপন করেছে|</translation>
<translation id="9050666287014529139">পাসফ্রেজ</translation>
<translation id="9065203028668620118">সম্পাদনা</translation>
-<translation id="9068849894565669697">রঙ নির্বাচন করুন</translation>
+<translation id="9068849894565669697">রঙ বেছে নিন</translation>
<translation id="9069693763241529744">একটি এক্সটেনশন ব্লক করেছে</translation>
<translation id="9076283476770535406">এতে প্রাপ্তবয়স্কদের সামগ্রী থাকতে পারে</translation>
-<translation id="9078964945751709336">আরো তথ্য প্রয়োজন</translation>
+<translation id="9078964945751709336">আরও তথ্য প্রয়োজন</translation>
<translation id="9103872766612412690"><ph name="SITE" /> সাধারণত আপনার তথ্য সুরক্ষিত রাখতে এনক্রিপশান ব্যবহার করে। এইবার যখন Chromium <ph name="SITE" /> এর সাথে সংযোগ স্থাপন করার চেষ্টা করেছে, তখন ওয়েবসাইটটি অস্বাভাবিক এবং ভুল শংসাপত্র পাঠিয়েছে। হয় একজন আক্রমণকারী <ph name="SITE" /> হওয়ার ভান করছে, অথবা একটি ওয়াই-ফাই প্রবেশ করুন স্ক্রীণ সংযোগকে বাধাপ্রদান করেছে। আপনার তথ্য এখনো নিরাপদ আছে কারণ কোনো ডেটা আদানপ্রদানের আগেই Chromium সংযোগটিকে বন্ধ করে দিয়েছে।</translation>
<translation id="9137013805542155359">প্রকৃত রূপ দেখান</translation>
<translation id="9137248913990643158">এই অ্যাপ্লিকেশানটি ব্যবহার করার আগে অনুগ্রহ করে শুরু করুন এবং Chrome এ প্রবেশ করুন।</translation>
@@ -942,18 +976,17 @@
<translation id="9169664750068251925">এই সাইটে সর্বদা অবরোধ করুন</translation>
<translation id="9170848237812810038">&amp;পূর্বাবস্থায় ফিরুন</translation>
<translation id="917450738466192189">সার্ভারের শংসাপত্র অকার্যকর৷</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />টি}one{<ph name="SHIPPING_OPTION_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />টি}other{<ph name="SHIPPING_OPTION_PREVIEW" /> এবং আরও <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />টি}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> একটি অসমর্থিত প্রোটোকল ব্যবহার করে।</translation>
<translation id="9205078245616868884">আপনার ডেটা আপনার সিঙ্ক পাসফ্রেজ দিয়ে এনক্রিপ্ট করা হয়েছে। সিঙ্ক শুরু করার জন্য এটি লিখুন।</translation>
<translation id="9207861905230894330">নিবন্ধ যোগ করতে ব্যর্থ হয়েছে৷</translation>
-<translation id="9219103736887031265">চিত্রগুলি</translation>
+<translation id="9219103736887031265">ছবিগুলি</translation>
<translation id="933612690413056017">কোনো ইন্টারনেট সংযোগ নেই</translation>
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ফর্ম সাফ করুন</translation>
<translation id="939736085109172342">নতুন ফোল্ডার</translation>
-<translation id="941721044073577244">মনে হচ্ছে এই সাইটটি ঘুরে দেখার অনুমতি আপনার নেই</translation>
<translation id="969892804517981540">অফিসিয়াল বিল্ড</translation>
<translation id="975560348586398090">{COUNT,plural, =0{কিছুই নয়}=1{১টি আইটেম}one{#টি আইটেম}other{#টি আইটেম}}</translation>
+<translation id="981121421437150478">অফলাইন</translation>
<translation id="988159990683914416">বিকাশকারী বিল্ড</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ca.xtb b/chromium/components/strings/components_strings_ca.xtb
index bce15d3ef69..49d3e62eb47 100644
--- a/chromium/components/strings/components_strings_ca.xtb
+++ b/chromium/components/strings/components_strings_ca.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Tanca altres aplicacions</translation>
<translation id="1055184225775184556">&amp;Desfés l'addició</translation>
<translation id="10614374240317010">Contrasenyes que no es desen mai</translation>
+<translation id="1066396345355680611">Pot ser que deixis de tenir accés al contingut protegit del lloc web <ph name="SITE" /> i d'altres llocs web.</translation>
<translation id="106701514854093668">Adreces d'interès d'escriptori</translation>
<translation id="1074497978438210769">No segur</translation>
<translation id="1080116354587839789">Ajusta a l'amplada</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Llista de lectura</translation>
<translation id="1264126396475825575">S'ha capturat un informe d'error (<ph name="CRASH_TIME" />) (encara no s'ha penjat ni ignorat)</translation>
<translation id="1281526147609854549">Emès per <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Contingut perillós bloquejat</translation>
<translation id="1285320974508926690">No tradueixis mai aquest lloc</translation>
<translation id="129553762522093515">Tancades recentment</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Esborreu les galetes<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domini d'inscripció:</translation>
<translation id="1340482604681802745">Adreça de recollida</translation>
-<translation id="1344211575059133124">Sembla que necessites permís per visitar aquest lloc</translation>
<translation id="1344588688991793829">Configuració d'Emplenament automàtic de Chromium...</translation>
<translation id="1348198688976932919">Aquest lloc web conté aplicacions perilloses</translation>
<translation id="1374468813861204354">suggeriments</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">La identitat de <ph name="ORGANIZATION" /> a <ph name="LOCALITY" /> ha estat verificada per <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Sí</translation>
<translation id="1430915738399379752">Impressió</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> més}other{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> més}}</translation>
<translation id="1506687042165942984">Mostra una còpia desada (és a dir, no actualitzada) d'aquesta pàgina.</translation>
<translation id="1517433312004943670">El número de telèfon és obligatori</translation>
+<translation id="1517500485252541695">Targetes de crèdit i de dèbit acceptades</translation>
<translation id="1519264250979466059">Data de creació</translation>
+<translation id="1527263332363067270">S'està esperant que hi hagi connexió…</translation>
<translation id="153384715582417236">De moment, això és tot</translation>
<translation id="1549470594296187301">Heu d'activar el JavaScript per utilitzar aquesta funció.</translation>
<translation id="1555130319947370107">Blau</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Baixa la pàgina més tard</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Camp obligatori</translation>
<translation id="187918866476621466">Obre les pàgines d'inici</translation>
<translation id="1883255238294161206">Redueix la llista</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> més}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> més}}</translation>
<translation id="1898423065542865115">Filtratge</translation>
+<translation id="1916770123977586577">Per aplicar la configuració actualitzada a aquest lloc web, torna a carregar la pàgina</translation>
+<translation id="1919345977826869612">Anuncis</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Cap}=1{1 lloc}other{# llocs}}</translation>
<translation id="194030505837763158">Ves a <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Targetes de prepagament acceptades</translation>
<translation id="1962204205936693436">Adreces d'interès de <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Error de serialització</translation>
<translation id="1974060860693918893">Configuració avançada</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Error d'HTTP</translation>
<translation id="2270484714375784793">Número de telèfon</translation>
<translation id="2282872951544483773">Experiments no disponibles</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element}other{<ph name="ITEM_COUNT" /> elements}}</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>
<translation id="2337852623177822836">L'administrador controla l'opció de configuració</translation>
<translation id="2354001756790975382">Altres adreces d'interès</translation>
<translation id="2354430244986887761">Recentment, Navegació segura de Google <ph name="BEGIN_LINK" />ha trobat aplicacions perjudicials<ph name="END_LINK" /> a <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continua</translation>
<translation id="2365563543831475020">L'informe d'error capturat (<ph name="CRASH_TIME" />) no s'ha penjat</translation>
<translation id="2367567093518048410">Nivell</translation>
-<translation id="237718015863234333">No hi ha cap alternativa a la IU disponible.</translation>
<translation id="2384307209577226199">Predeterminada de l'empresa</translation>
<translation id="2386255080630008482">El certificat del servidor s'ha revocat.</translation>
<translation id="2392959068659972793">Mostra les polítiques sense valors definits</translation>
@@ -194,8 +195,8 @@
<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="2503184589641749290">Targetes de dèbit i de prepagament acceptades</translation>
<translation id="2515629240566999685">Comproveu el senyal a la vostra zona</translation>
-<translation id="2516305470678292029">Alternatives a la IU</translation>
<translation id="2539524384386349900">Detecta</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> ha enviat una resposta que no és vàlida.</translation>
<translation id="2556876185419854533">&amp;Desfés la modificació</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Mètode d'enviament</translation>
<translation id="277499241957683684">Falta el registre del dispositiu</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>
<translation id="2799020568854403057">Aquest lloc web conté aplicacions perjudicials</translation>
<translation id="2803306138276472711">Navegació segura de Google ha <ph name="BEGIN_LINK" />detectat programari maliciós<ph name="END_LINK" /> recentment a la pàgina <ph name="SITE" />. De vegades, els llocs web que acostumen a ser segurs s'infecten amb programari maliciós.</translation>
<translation id="2824775600643448204">Barra d'adreces i de cerca</translation>
<translation id="2826760142808435982">La connexió s'ha encriptat i autenticat mitjançant <ph name="CIPHER" /> i fa servir <ph name="KX" /> com a mecanisme d'intercanvi clau.</translation>
<translation id="2835170189407361413">Esborra el formulari</translation>
+<translation id="2851634818064021665">Necessites permís per visitar aquest lloc web</translation>
<translation id="2856444702002559011">Pot ser que els atacants provin de robar la teva informació del lloc web <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (per exemple, contrasenyes, missatges o targetes de crèdit). <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">No tornis a carregar</translation>
-<translation id="2900469785430194048">Google Chrome s'ha quedat sense memòria en provar de mostrar aquesta pàgina web.</translation>
<translation id="2909946352844186028">S'ha detectat un canvi a la xarxa.</translation>
<translation id="2916038427272391327">Tanca altres programes</translation>
<translation id="2922350208395188000">No es pot comprovar el certificat del servidor.</translation>
<translation id="2928905813689894207">Adreça de facturació</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> més}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> més}}</translation>
<translation id="2941952326391522266">Aquest servidor no ha pogut comprovar que sigui <ph name="DOMAIN" /> perquè el seu certificat de seguretat prové del domini <ph name="DOMAIN2" />. Això pot ser a causa d'una configuració incorrecta o d'un atacant que intercepta la vostra connexió.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Si s'activa aquesta opció, Chromium desa una còpia de la vostra targeta en aquest dispositiu per agilitzar l'emplenament de formularis.</translation>
<translation id="2985398929374701810">Introdueix una adreça vàlida</translation>
<translation id="2986368408720340940">Aquest mètode de recollida no està disponible. Prova'n un altre.</translation>
@@ -274,15 +278,13 @@
<translation id="3150653042067488994">Error temporal del servidor</translation>
<translation id="3154506275960390542">Aquesta pàgina inclou un formulari que pot ser que no s'enviï de manera segura. La resta d'usuaris poden veure les dades que enviïs mentre estiguin en trànsit o un atacant podria modificar-les per canviar el contingut que rep el servidor.</translation>
<translation id="3157931365184549694">Restaura</translation>
-<translation id="3167968892399408617">Les pàgines que consulteu en pestanyes d'incògnit no s'emmagatzemaran a l'historial del navegador, al magatzem de galetes ni a l'historial de cerca després d'haver tancat totes les pestanyes d'incògnit. Els fitxers que baixeu i les adreces d'interès que creeu sí que es desaran.</translation>
+<translation id="3167968892399408617">Les pàgines que consulteu en pestanyes d'incògnit no s'emmagatzemaran a l'historial del navegador, al magatzem de galetes ni a l'historial de cerques després d'haver tancat totes les pestanyes d'incògnit. Els fitxers que baixeu i les adreces d'interès que creeu sí que es desaran.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Illa</translation>
-<translation id="317583078218509884">La nova configuració dels permisos del lloc es farà efectiva quan torneu a carregar la pàgina.</translation>
<translation id="3176929007561373547">Comproveu la configuració del servidor intermediari o contacteu amb l'administrador de la xarxa per
assegurar-vos que el servidor intermediari funcioni correctament. Si creieu que no és necessari
utilitzar un servidor intermediari:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Obre la pàgina en mode d'incògnit</translation>
<translation id="320323717674993345">Cancel·la el pagament</translation>
<translation id="3207960819495026254">S'ha afegit a les adreces d'interès.</translation>
<translation id="3225919329040284222">El servidor ha presentat un certificat que no coincideix amb les expectatives integrades. Les expectatives s'inclouen perquè determinats llocs web d'alta seguretat us protegeixin.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Desfés el canvi d'ordre</translation>
<translation id="3282497668470633863">Afegeix el titular de la targeta</translation>
<translation id="3286538390144397061">Reinicia ara</translation>
+<translation id="3287510313208355388">Baixa quan estigui en línia</translation>
<translation id="3303855915957856445">No s'ha trobat cap resultat de la cerca</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Obtén l'interval:</translation>
<translation id="3462200631372590220">Amaga la configuració avançada</translation>
<translation id="3467763166455606212">El nom del titular de la targeta és obligatori</translation>
-<translation id="3478058380795961209">Mes de caducitat</translation>
<translation id="3479539252931486093">Heu trobat el lloc bloquejat de manera inesperada? <ph name="BEGIN_LINK" />Informeu-nos-en<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ara no</translation>
<translation id="3498215018399854026">En aquests moments no ens hem pogut posar en contacte amb els pares. Torneu-ho a provar.</translation>
@@ -343,12 +345,13 @@
&lt;p&gt;Canvia 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; <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">S'accepten targetes de crèdit i de prepagament.</translation>
<translation id="3582930987043644930">Afegeix el nom</translation>
<translation id="3583757800736429874">&amp;Refés el moviment</translation>
<translation id="3586931643579894722">Oculta els detalls</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Introdueix una data de caducitat vàlida</translation>
-<translation id="36224234498066874">Esborra les dades de navegació</translation>
+<translation id="36224234498066874">Esborra dades de navegació</translation>
<translation id="362276910939193118">Mostra l'historial complet</translation>
<translation id="3623476034248543066">Mostra el valor</translation>
<translation id="3630155396527302611">Si ja està inclòs a la llista de programes autoritzats per accedir a la xarxa, proveu
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Si aquest missatge apareix sovint, proveu aquests <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisió</translation>
<translation id="3678029195006412963">La sol·licitud no s'ha pogut signar</translation>
+<translation id="3678529606614285348">Obre la pàgina en una finestra d'incògnit nova (Ctrl+Maj+N)</translation>
<translation id="3679803492151881375">Informe d'error generat el <ph name="CRASH_TIME" /> i enviat el <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informació del certificat</translation>
<translation id="3690164694835360974">L'inici de sessió no és segur</translation>
-<translation id="3693415264595406141">Contrasenya:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">S'està carregant...</translation>
<translation id="3712624925041724820">Llicències exhaurides</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Els atacants del lloc web <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> et poden enganyar perquè facis alguna acció perillosa, com ara instal·lar programari o revelar informació personal (per exemple, contrasenyes, números de telèfon o targetes de crèdit). <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">S'ha produït un error en el procés de traducció a causa d'un error del servidor.</translation>
<translation id="3759461132968374835">No heu informat de cap bloqueig recentment. Els bloquejos que es van produir mentre la creació d'informes de bloqueig estava desactivada no apareixeran aquí.</translation>
+<translation id="3765032636089507299">La pàgina Navegació segura està en construcció.</translation>
<translation id="3778403066972421603">Vols desar aquesta targeta al teu compte de Google i en aquest dispositiu?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Data de caducitat: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">La teva sol·licitud per accedir a aquest lloc s'ha enviat a <ph name="NAME" /></translation>
<translation id="3890664840433101773">Afegeix un correu electrònic</translation>
<translation id="3901925938762663762">La targeta ha caducat</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">S'ha penjat l'identificador <ph name="CRASH_ID" /> de l'informe d'error (identificador d'error local: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Aquest servidor no ha pogut demostrar que sigui <ph name="DOMAIN" />, perquè el seu certificat de seguretat no especifica noms alternatius per a l'assumpte. Això pot ser a causa d'una configuració incorrecta o d'un atacant que intercepta la connexió.</translation>
+<translation id="3949601375789751990">Aquí es mostra l'historial de navegació</translation>
<translation id="3963721102035795474">Mode de lector</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Cap}=1{D'1 lloc }other{De # llocs }}</translation>
<translation id="397105322502079400">S’està calculant...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Configuració del lloc</translation>
<translation id="4169947484918424451">Voleu que Chromium desi aquesta targeta?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Els atacants d'aquest lloc poden provar 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).</translation>
<translation id="4372948949327679948">Valor <ph name="VALUE_TYPE" /> esperat.</translation>
<translation id="4377125064752653719">Heu provat d'accedir a <ph name="DOMAIN" />, però l'emissor ha revocat el certificat que ha presentat el servidor. Això vol dir que no heu de confiar gens en les credencials de seguretat que ha presentat el servidor. És possible que us estigueu comunicant amb un atacant.</translation>
-<translation id="4381091992796011497">Nom d'usuari:</translation>
<translation id="4394049700291259645">Desactiva</translation>
<translation id="4406896451731180161">resultats de la cerca</translation>
<translation id="4424024547088906515">Aquest servidor no ha pogut comprovar que sigui <ph name="DOMAIN" /> perquè Chrome 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="4432688616882109544"><ph name="HOST_NAME" /> no ha acceptat el certificat d'inici de sessió o pot ser que no se n'hagi proporcionat cap.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Voleu suprimir l'adreça de Chrome?</translation>
<translation id="4592951414987517459">La connexió a <ph name="DOMAIN" /> s'ha encriptat amb un sistema de xifratge modern.</translation>
<translation id="4594403342090139922">&amp;Desfés la supressió</translation>
+<translation id="4611292653554630842">Inicia la sessió</translation>
<translation id="4619615317237390068">Pestanyes d'altres dispositius</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">El servidor no ha pogut comprovar que sigui <ph name="DOMAIN" /> perquè el seu certificat de seguretat conté errors. Això pot ser a causa d'una configuració incorrecta o d'un atacant que intercepta la vostra connexió.</translation>
<translation id="4690462567478992370">Deixa de fer servir un certificat no vàlid</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">La connexió s'ha interromput</translation>
+<translation id="471880041731876836">No tens permís per visitar aquest lloc web</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Executar el Diagnòstic de xarxa de Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Torna a carregar les polítiques</translation>
<translation id="4728558894243024398">Plataforma</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Aquest contingut pot provar d'enganyar-te perquè instal·lis programari o proporcionis informació personal. <ph name="BEGIN_LINK" />Mostra igualment<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Cerca a l'historial</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Cal autenticació</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{i 1 pàgina web més}other{i # pàgines web més}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Aquesta pàgina s'ha traduït des d'un idioma desconegut a: <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pagament</translation>
<translation id="4926049483395192435">S'ha d'especificar.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Accions</translation>
<translation id="4958444002117714549">Desplega la llista</translation>
<translation id="4974590756084640048">Torna a activar els advertiments</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Contrasenya incorrecta</translation>
<translation id="5056549851600133418">Articles que us 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>
<translation id="5087580092889165836">Afegiu una targeta</translation>
<translation id="5089810972385038852">Estat</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Província</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5141240743006678641">Encripta contrasenyes sincronitzades amb les vostres credencials de Google</translation>
-<translation id="514421653919133810">Obre la pàgina en mode d'incògnit (Ctrl+Maj+N)</translation>
<translation id="5145883236150621069">Hi ha un codi d'error en la resposta a la política</translation>
+<translation id="5159010409087891077">Obre la pàgina en una finestra d'incògnit nova (⇧⌘N)</translation>
<translation id="5171045022955879922">Cerqueu o escriviu l'URL</translation>
<translation id="5172758083709347301">Automàtica</translation>
<translation id="5179510805599951267">No està escrita en <ph name="ORIGINAL_LANGUAGE" />? Informa d'aquest error</translation>
-<translation id="5181140330217080051">S'està baixant</translation>
<translation id="5190835502935405962">Barra d'adreces d'interès</translation>
<translation id="5199729219167945352">Experiments</translation>
<translation id="5205222826937269299">El nom és obligatori</translation>
<translation id="5222812217790122047">El correu electrònic és obligatori</translation>
<translation id="5251803541071282808">Núvol</translation>
<translation id="5277279256032773186">Fas servir Chrome a la feina? Les empreses poden gestionar la configuració de Chrome dels empleats. Més informació</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Segueix aquests passos per desactivar temporalment el programari perquè puguis accedir al web. Per fer-ho, necessitaràs privilegis d'administrador.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Fes clic a <ph name="BEGIN_BOLD" />Inicia<ph name="END_BOLD" /> i, a continuació, cerca i selecciona <ph name="BEGIN_BOLD" />Mostra serveis locals<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Selecciona <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />A <ph name="BEGIN_BOLD" />Tipus d'inici<ph name="END_BOLD" />, selecciona <ph name="BEGIN_BOLD" />Desactivat<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />A <ph name="BEGIN_BOLD" />Estat del servei<ph name="END_BOLD" />, fes clic a <ph name="BEGIN_BOLD" />Atura<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Fes clic a <ph name="BEGIN_BOLD" />Aplica<ph name="END_BOLD" /> i, a continuació, a <ph name="BEGIN_BOLD" />D'acord<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Visita el <ph name="BEGIN_LEARN_MORE_LINK" />Centre d'ajuda de Chrome<ph name="END_LEARN_MORE_LINK" /> per obtenir informació sobre com suprimir permanentment el programari de l'ordinador.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">La teva connexió amb aquest lloc no és privada. Per sortir del mode RV en qualsevol moment, treu-te el visor i prem Enrere.</translation>
<translation id="5299298092464848405">S'ha produït un error en analitzar la política</translation>
<translation id="5308689395849655368">La creació d'informes de bloqueig està desactivada.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Error de validació de l'esquema a "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">No s'ha trobat aquesta pàgina de <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Marca de temps de la política incorrecta</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> de <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">No vàlides</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> més}other{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> més}}</translation>
<translation id="5470861586879999274">&amp;Refés la modificació</translation>
+<translation id="5481076368049295676">Aquest contingut pot provar d'instal·lar programari maliciós al teu dispositiu per suprimir-ne informació o robar-te'n. <ph name="BEGIN_LINK" />Mostra igualment<ph name="END_LINK" />.</translation>
<translation id="54817484435770891">Afegeix una adreça vàlida</translation>
<translation id="5492298309214877701">Aquest lloc de la intranet de l'empresa, l'organització o el centre educatiu té el mateix URL que un lloc web extern.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">No s'ha pogut trobar l'article sol·licitat.</translation>
<translation id="5544037170328430102">Una pàgina inserida a <ph name="SITE" /> diu:</translation>
<translation id="5556459405103347317">Torna a carregar</translation>
+<translation id="5560088892362098740">Data de caducitat</translation>
<translation id="5565735124758917034">Actiu</translation>
<translation id="5571083550517324815">No es pot fer la recollida en aquesta adreça. Selecciona'n una altra.</translation>
<translation id="5572851009514199876">Obre Chrome i inicia-hi la sessió perquè Chrome pugui comprovar si tens permís per accedir a aquest lloc.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">No s'ha pogut carregar la configuració de la política</translation>
<translation id="5631439013527180824">Testimoni de gestió del dispositiu no vàlid</translation>
<translation id="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="5646376287012673985">Ubicació</translation>
<translation id="5659593005791499971">Correu electrònic</translation>
<translation id="5669703222995421982">Obtén contingut personalitzat</translation>
<translation id="5675650730144413517">Aquesta pàgina no funciona</translation>
<translation id="5710435578057952990">La identitat d'aquest lloc web no ha estat verificada.</translation>
-<translation id="5713016350996637505">Contingut enganyós bloquejat</translation>
+<translation id="5719499550583120431">S'accepten targetes de prepagament.</translation>
<translation id="5720705177508910913">Usuari actual</translation>
<translation id="5732392974455271431">Els teus pares te'l poden desbloquejar</translation>
<translation id="5763042198335101085">Introdueix una adreça electrònica vàlida</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Vols emplenar la informació de la teva targeta <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">La connexió a <ph name="DOMAIN" /> s'ha encriptat amb un sistema de xifratge obsolet.</translation>
<translation id="5813119285467412249">&amp;Refés l'addició</translation>
-<translation id="5814352347845180253">Pot ser que perdis l'accés al contingut prèmium de <ph name="SITE" /> i d'altres llocs.</translation>
<translation id="5838278095973806738">No introdueixis informació confidencial en aquest lloc (com ara contrasenyes o targetes de crèdit), ja que alguns atacants podrien robar-la.</translation>
<translation id="5869405914158311789">No es pot accedir a aquest lloc</translation>
<translation id="5869522115854928033">Contrasenyes desades</translation>
<translation id="5872918882028971132">Suggeriments per als responsables</translation>
+<translation id="5893752035575986141">S'accepten targetes de crèdit.</translation>
<translation id="5901630391730855834">Groc</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="5926846154125914413">Pot ser que perdis l'accés al contingut prèmium d'alguns llocs.</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 perillosos. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">Elimina de l'historial</translation>
<translation id="5975083100439434680">Redueix</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Tanca</translation>
<translation id="6042308850641462728">Més</translation>
<translation id="6047233362582046994">Si entens el risc que suposa per a la teva seguretat, pots <ph name="BEGIN_LINK" />visitar aquest lloc web<ph name="END_LINK" /> abans que no s'hagin suprimit les aplicacions perjudicials.</translation>
+<translation id="6047927260846328439">Aquest contingut pot provar d'enganyar-te perquè instal·lis programari o proporcionis informació personal. <ph name="BEGIN_LINK" />Mostra igualment<ph name="END_LINK" />.</translation>
<translation id="6051221802930200923">En aquests moments no pots visitar <ph name="SITE" /> perquè el lloc web fa servir una fixació de 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="6060685159320643512">Compte! Aquests experiments mosseguen</translation>
<translation id="6080696365213338172">Heu accedit a contingut mitjançant un certificat proporcionat per l'administrador. Per tant, l'administrador por interceptar les dades que proporcioneu a <ph name="DOMAIN" />.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Més informació</translation>
<translation id="6169916984152623906">Ara pots navegar de manera privada i les altres persones que utilitzin aquest dispositiu no veuran la teva activitat. Tanmateix, les baixades i les adreces d'interès sí que es desaran.</translation>
<translation id="6177128806592000436">La teva connexió amb aquest lloc no és segura</translation>
-<translation id="6184817833369986695">(cohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Comprovació de la connexió a Internet</translation>
<translation id="6218753634732582820">Voleu suprimir l'adreça de Chromium?</translation>
<translation id="6221345481584921695">Navegació segura de Google ha <ph name="BEGIN_LINK" />detectat programari maliciós<ph name="END_LINK" /> recentment a <ph name="SITE" />. De vegades, els llocs web que acostumen a ser segurs s'infecten amb programari maliciós. El contingut maliciós prové de l'amfitrió <ph name="SUBRESOURCE_HOST" />, un distribuïdor conegut de programari maliciós.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Esteu interessat en les funcions de Chrome noves i interessants? Proveu el nostre canal beta a la pàgina chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium s'ha quedat sense memòria en provar de mostrar aquesta pàgina web.</translation>
+<translation id="6397451950548600259">L'ordinador conté programari que impedeix que Chrome es connecti de manera segura al web</translation>
<translation id="6404511346730675251">Edita l'adreça d'interès</translation>
<translation id="6410264514553301377">Introdueix la data de caducitat i el CVC de la targeta <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Has demanat permís als teus pares per visitar aquest lloc</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Polítiques de dispositius</translation>
<translation id="6477321094435799029">Chrome ha detectat codi poc comú en aquesta pàgina i, per tant, l'ha bloquejat per protegir la teva informació personal (per exemple, contrasenyes, números de telèfon i targetes de crèdit).</translation>
<translation id="6489534406876378309">Comença a penjar els errors</translation>
+<translation id="6507833130742554667">S'accepten targetes de crèdit i de dèbit.</translation>
<translation id="6508722015517270189">Reinicia Chrome</translation>
<translation id="6529602333819889595">&amp;Refés la supressió</translation>
<translation id="6534179046333460208">Suggeriments del Web físic</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Aquest contingut pot provar d'instal·lar programari maliciós al teu dispositiu per suprimir-ne informació o robar-te'n. <ph name="BEGIN_LINK" />Mostra igualment<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opcions d'encriptació</translation>
<translation id="662080504995468778">No surtis</translation>
<translation id="6626291197371920147">Afegeix un número de targeta vàlid</translation>
<translation id="6628463337424475685">Cerca de <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">És possible que els atacants que es troben a <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> provin d'instal·lar programes perillosos a l'ordinador Mac per robar-te o suprimir-te informació (per exemple, fotos, contrasenyes, missatges i targetes de crèdit). <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Aquesta política ha quedat obsoleta.</translation>
+<translation id="6657585470893396449">Contrasenya</translation>
<translation id="6671697161687535275">Voleu suprimir el suggeriment de formulari de Chromium?</translation>
<translation id="6685834062052613830">Tanqueu la sessió i completeu la configuració</translation>
<translation id="6710213216561001401">Anterior</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Districte</translation>
<translation id="6973656660372572881">S'especifiquen tant els servidors intermediaris fixos com un URL d'script .pac.</translation>
<translation id="6989763994942163495">Mostra la configuració avançada...</translation>
-<translation id="7000990526846637657">No s'ha trobat cap entrada a l'historial</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">És possible que el teu compte de Google tingui altres formes de l'historial de navegació a <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Contrasenyes</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">No es pot enviar a aquesta adreça. Selecciona'n una altra.</translation>
<translation id="7138472120740807366">Mètode d'entrega</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> més}other{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> més}}</translation>
<translation id="7155487117670177674">El pagament no és segur</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> més}other{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> més}}</translation>
<translation id="7179921470347911571">Torna'l a iniciar ara</translation>
<translation id="7180611975245234373">Actualitza</translation>
<translation id="7182878459783632708">Cap política definida</translation>
@@ -733,7 +759,7 @@
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Més informació<ph name="END_LINK" /> sobre aquest problema.</translation>
<translation id="7219179957768738017">La connexió utilitza <ph name="SSL_VERSION" />.</translation>
<translation id="7220786058474068424">S'està processant</translation>
-<translation id="724691107663265825">Aquest lloc conté programari maliciós</translation>
+<translation id="724691107663265825">Aquest lloc web conté programari maliciós</translation>
<translation id="724975217298816891">Introdueix la data de caducitat i el CVC de la targeta <ph name="CREDIT_CARD" /> per actualitzar-ne els detalls. Un cop confirmada, els detalls de la targeta es compartiran amb aquest lloc.</translation>
<translation id="7260504762447901703">Revoca l'accés</translation>
<translation id="7275334191706090484">Adreces d'interès gestionades</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Baixa</translation>
<translation id="7518003948725431193">No s'ha trobat cap pàgina web per a l'adreça: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">La connexió amb aquest lloc no és privada</translation>
<translation id="7535087603100972091">Valor</translation>
<translation id="7537536606612762813">Obligatòria</translation>
<translation id="7542403920425041731">Un cop confirmada, els detalls de la targeta es compartiran amb aquest lloc web.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Prova els consells següents:</translation>
<translation id="7554791636758816595">Pestanya nova</translation>
<translation id="7567204685887185387">Aquest servidor no ha pogut comprovar que sigui <ph name="DOMAIN" /> perquè és possible que el seu certificat de seguretat s'hagi emès de manera fraudulenta. Això pot ser a causa d'una configuració incorrecta o d'un atacant que intercepta la vostra connexió.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> més}other{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> més}}</translation>
<translation id="7568593326407688803">Aquesta pàgina està en<ph name="ORIGINAL_LANGUAGE" />Voleu traduir-la?</translation>
<translation id="7569952961197462199">Voleu suprimir la targeta de crèdit de Chrome?</translation>
<translation id="7569983096843329377">Negre</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificat</translation>
<translation id="7716147886133743102">Bloquejat per l'administrador</translation>
<translation id="7716424297397655342">Aquest lloc no es pot carregar des de la memòria cau</translation>
+<translation id="774634243536837715">S'ha bloquejat el contingut perillós.</translation>
<translation id="7752995774971033316">Sense gestionar</translation>
<translation id="7755287808199759310">El teu pare o la teva mare et poden desbloquejar el lloc</translation>
<translation id="7758069387465995638">És possible que l'antivirus o el tallafoc hagi bloquejat la connexió.</translation>
+<translation id="7759163816903619567">Domini de publicació:</translation>
<translation id="7761701407923456692">El certificat del servidor no coincideix amb l'URL.</translation>
<translation id="7763386264682878361">Analitzador de fitxers de manifest de pagament</translation>
<translation id="7764225426217299476">Afegeix una adreça</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">S'han trobat <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> per a "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">Tanmateix, no sou invisible. La vostra empresa, el vostre proveïdor de serveis d'Internet i els llocs web que visiteu poden veure la vostra navegació d'incògnit.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" />: <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">S'accepten targetes de dèbit i de prepagament.</translation>
<translation id="7887683347370398519">Comproveu el CVC i torneu-ho a provar</translation>
<translation id="79338296614623784">Introdueix un número de telèfon vàlid</translation>
<translation id="7935318582918952113">Destil·lador DOM</translation>
@@ -832,10 +861,9 @@
<translation id="8025119109950072390">Els atacants d'aquest lloc et poden enganyar perquè facis alguna acció perillosa, com ara instal·lar programari o revelar informació personal (per exemple, contrasenyes, números de telèfon o targetes de crèdit).</translation>
<translation id="8034522405403831421">Aquesta pàgina està escrita en <ph name="SOURCE_LANGUAGE" />. Vols traduir-la a <ph name="TARGET_LANGUAGE" />?</translation>
<translation id="8037357227543935929">Pregunta (opció predeterminada)</translation>
-<translation id="8041089156583427627">Envia comentaris</translation>
+<translation id="8041089156583427627">Envia suggeriments</translation>
<translation id="8041940743680923270">Utilitza l'opció predeterminada global (Pregunta)</translation>
<translation id="8088680233425245692">No s'ha pogut consultar l'article.</translation>
-<translation id="8089520772729574115">menys d'1 MB</translation>
<translation id="8091372947890762290">L'activació està pendent al servidor</translation>
<translation id="8118489163946903409">Mètode de pagament</translation>
<translation id="8131740175452115882">Confirma</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Desfés el moviment</translation>
<translation id="8201077131113104583">L'URL d'actualització per a l'extensió amb identificador "<ph name="EXTENSION_ID" />" no és vàlid.</translation>
<translation id="8202097416529803614">Resum de la comanda</translation>
+<translation id="8205463626947051446">El lloc web acostuma a mostrar anuncis intrusius</translation>
<translation id="8218327578424803826">Ubicació assignada:</translation>
<translation id="8225771182978767009">La persona que ha configurat l'ordinador ha bloquejat aquest lloc.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Obre la pàgina en una pestanya d'incògnit nova</translation>
<translation id="8241707690549784388">La pàgina que busques utilitzava informació que vas introduir. Tornar a aquesta pàgina podria provocar una repetició de qualsevol acció realitzada. Vols continuar?</translation>
+<translation id="8241712895048303527">Bloqueja en aquest lloc web</translation>
<translation id="8249320324621329438">Última obtenció:</translation>
<translation id="8253091569723639551">Cal indicar l'adreça de facturació</translation>
<translation id="8261506727792406068">Suprimeix</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">No s'ha pogut executar la traducció a causa d'un problema amb la connexió de xarxa.</translation>
<translation id="8332188693563227489">S'ha rebutjat l'accés a <ph name="HOST_NAME" /></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="8344669043927012510">Obre la pàgina en mode d'incògnit (⇧⌘N)</translation>
<translation id="8349305172487531364">Barra d'adreces d'interès</translation>
<translation id="8363502534493474904">Desactiveu el mode d'avió</translation>
<translation id="8364627913115013041">No s'ha definit.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Informe d'error penjat el <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Bloqueigs (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Heu d'introduir la mateixa frase de contrasenya dues vegades.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configuració</translation>
<translation id="8433057134996913067">Amb aquesta acció es tancarà la sessió a la majoria de llocs web.</translation>
<translation id="8437238597147034694">&amp;Desfés el moviment</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Per poder utilitzar targetes del teu compte de Google, inicia la sessió a Chrome</translation>
<translation id="8488350697529856933">Objectiu d'aplicació</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha tardat massa a respondre.</translation>
-<translation id="8532105204136943229">Any de caducitat</translation>
+<translation id="8503813439785031346">Nom d'usuari</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="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="8620436878122366504">Els teus pares encara no ho han aprovat</translation>
<translation id="8647750283161643317">Restableix-ho tot als valors predeterminats</translation>
+<translation id="8660471606262461360">De Google Payments</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="8725066075913043281">Torna-ho a provar</translation>
@@ -913,10 +946,11 @@
<translation id="8903921497873541725">Amplia</translation>
<translation id="8931333241327730545">Voleu desar aquesta targeta al vostre compte de Google?</translation>
<translation id="8932102934695377596">El rellotge està endarrerit</translation>
+<translation id="8938939909778640821">Targetes de crèdit i de prepagament acceptades</translation>
<translation id="8971063699422889582">El certificat del servidor ha caducat.</translation>
<translation id="8986494364107987395">Envia automàticament estadístiques d'ús i informes d'error a Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
-<translation id="8996941253935762404">Aquest lloc conté programes perjudicials</translation>
+<translation id="8996941253935762404">Aquest lloc web conté programes perjudicials</translation>
<translation id="8997023839087525404">El servidor ha presentat un certificat que no s'ha divulgat públicament mitjançant la política Transparència de certificats. Això és un requisit d'alguns certificats per garantir que són de confiança i una mesura de protecció contra els atacants.</translation>
<translation id="9001074447101275817">El servidor intermediari del domini <ph name="DOMAIN" /> requereix un nom d'usuari i una contrasenya.</translation>
<translation id="9005998258318286617">No es pot carregar el document PDF.</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Bloqueja sempre en aquest lloc</translation>
<translation id="9170848237812810038">&amp;Desfés</translation>
<translation id="917450738466192189">El certificat del servidor no és vàlid.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> més}other{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> més}}</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>
<translation id="9207861905230894330">No s'ha pogut afegir l'article.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ESBORRA EL FORMULARI</translation>
<translation id="939736085109172342">Carpeta nova</translation>
-<translation id="941721044073577244">Sembla que no tens permís per visitar aquest lloc</translation>
<translation id="969892804517981540">Muntatge oficial</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Cap}=1{1 element}other{# elements}}</translation>
+<translation id="981121421437150478">Sense connexió</translation>
<translation id="988159990683914416">Muntatge del desenvolupador</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_cs.xtb b/chromium/components/strings/components_strings_cs.xtb
index 04fd935ffdf..3d00483e122 100644
--- a/chromium/components/strings/components_strings_cs.xtb
+++ b/chromium/components/strings/components_strings_cs.xtb
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="cs">
-<translation id="1008557486741366299">Nyní ne</translation>
+<translation id="1008557486741366299">Teď ne</translation>
<translation id="1015730422737071372">Zadejte další podrobnosti</translation>
<translation id="1021110881106174305">Přijímané karty</translation>
<translation id="1032854598605920125">Otočit ve směru hodinových ručiček</translation>
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Zavřete ostatní aplikace</translation>
<translation id="1055184225775184556">&amp;Vrátit přidání zpět</translation>
<translation id="10614374240317010">Nikdy se neukládají</translation>
+<translation id="1066396345355680611">Můžete ztratit přístup k chráněnému obsahu z webu <ph name="SITE" /> a některých dalších webů.</translation>
<translation id="106701514854093668">Záložky v PC</translation>
<translation id="1074497978438210769">Nezabezpečeno</translation>
<translation id="1080116354587839789">Přizpůsobit na šířku</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Seznam četby</translation>
<translation id="1264126396475825575">Zpráva o selhání pořízená <ph name="CRASH_TIME" /> (dosud nenahrána nebo ignorována)</translation>
<translation id="1281526147609854549">Vydavatel: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Byl zablokován nebezpečný obsah</translation>
<translation id="1285320974508926690">Tento web nikdy nepřekládat</translation>
<translation id="129553762522093515">Nedávno zavřené</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Zkuste vymazat soubory cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Doména registrace:</translation>
<translation id="1340482604681802745">Adresa vyzvednutí</translation>
-<translation id="1344211575059133124">Zdá se, že k návštěvě tohoto webu potřebuješ povolení</translation>
<translation id="1344588688991793829">Nastavení Automatického vyplňování v prohlížeči Chromium...</translation>
<translation id="1348198688976932919">Web, na který se chystáte přejít, obsahuje nebezpečné aplikace</translation>
<translation id="1374468813861204354">návrhy</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identitu <ph name="ORGANIZATION" />v <ph name="LOCALITY" /> ověřil/a <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ano</translation>
<translation id="1430915738399379752">Tisk</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> další}few{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> další}many{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> dalšího}other{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> dalších}}</translation>
<translation id="1506687042165942984">Zobrazí uloženou (tj. neaktuální) kopii této stránky</translation>
<translation id="1517433312004943670">Je vyžadováno telefonní číslo</translation>
+<translation id="1517500485252541695">Přijímané kreditní a debetní karty</translation>
<translation id="1519264250979466059">Datum sestavení</translation>
+<translation id="1527263332363067270">Čeká se na připojení…</translation>
<translation id="153384715582417236">To je prozatím vše</translation>
<translation id="1549470594296187301">Chcete-li tuto funkci použít, musí být aktivován JavaScript.</translation>
<translation id="1555130319947370107">Modrá</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Stáhnout stránku později</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Povinné pole</translation>
<translation id="187918866476621466">Otevřít počáteční stránky</translation>
<translation id="1883255238294161206">Sbalit seznam</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> další}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> další}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> dalšího}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> dalších}}</translation>
<translation id="1898423065542865115">Filtrování</translation>
+<translation id="1916770123977586577">Chcete-li pro tento web použít aktualizovaná nastavení, načtěte tuto stránku znovu</translation>
+<translation id="1919345977826869612">Reklamy</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Žádné}=1{1 web}few{# weby}many{# webu}other{# webů}}</translation>
<translation id="194030505837763158">Přejít na odkaz <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Přijímané předplacené karty</translation>
<translation id="1962204205936693436">Záložky domény <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Chyba serializace</translation>
<translation id="1974060860693918893">Rozšířená nastavení</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Chyba protokolu HTTP</translation>
<translation id="2270484714375784793">Telefonní číslo</translation>
<translation id="2282872951544483773">Nedostupné experimenty</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> položka}few{<ph name="ITEM_COUNT" /> položky}many{<ph name="ITEM_COUNT" /> položky}other{<ph name="ITEM_COUNT" /> položek}}</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>
<translation id="2337852623177822836">Nastavení je spravováno administrátorem</translation>
<translation id="2354001756790975382">Ostatní záložky</translation>
<translation id="2354430244986887761">Bezpečné prohlížení Google na webu <ph name="SITE" /> nedávno <ph name="BEGIN_LINK" />nalezlo škodlivé aplikace<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Pokračovat</translation>
<translation id="2365563543831475020">Zpráva o selhání pořízená <ph name="CRASH_TIME" /> nebyla nahrána</translation>
<translation id="2367567093518048410">Úroveň</translation>
-<translation id="237718015863234333">Žádné alternativy uživatelského rozhraní nejsou k dispozici</translation>
<translation id="2384307209577226199">Výchozí podnikové nastavení</translation>
<translation id="2386255080630008482">Certifikát serveru byl zamítnut.</translation>
<translation id="2392959068659972793">Zobrazit zásady bez nastavených hodnot</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Pokud je tato možnost zaškrtnuta, prohlížeč Chromium do zařízení uloží kopii karty za účelem rychlejšího vyplňování formulářů.</translation>
<translation id="2498091847651709837">Naskenovat novou kartu</translation>
<translation id="2501278716633472235">Zpět</translation>
+<translation id="2503184589641749290">Přijímané debetní a předplacené karty</translation>
<translation id="2515629240566999685">Zkontrolovat, zda máte dostatečně silný signál</translation>
-<translation id="2516305470678292029">Alternativy uživatelského rozhraní</translation>
<translation id="2539524384386349900">Rozpoznat</translation>
<translation id="255002559098805027">Web <ph name="HOST_NAME" /> vrátil neplatnou odpověď.</translation>
<translation id="2556876185419854533">&amp;Vrátit úpravy zpět</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Způsob dopravy</translation>
<translation id="277499241957683684">Chybějící záznam zařízení</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>
<translation id="2799020568854403057">Web, na který se chystáte přejít, obsahuje škodlivé aplikace</translation>
<translation id="2803306138276472711">Služba Bezpečné prohlížení Google na webu <ph name="SITE" /> nedávno <ph name="BEGIN_LINK" />zjistila malware<ph name="END_LINK" />. Někdy mohou být malwarem nakaženy i weby, které jsou obvykle bezpečné.</translation>
<translation id="2824775600643448204">Adresní a vyhledávací řádek</translation>
<translation id="2826760142808435982">Připojení je šifrováno a ověřeno pomocí šifry <ph name="CIPHER" /> a jako mechanismus výměny klíčů používá <ph name="KX" />.</translation>
<translation id="2835170189407361413">Vymazat formulář</translation>
+<translation id="2851634818064021665">K návštěvě tohoto webu potřebujete povolení</translation>
<translation id="2856444702002559011">Útočníci se mohou pokusit odcizit vaše údaje na webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (například hesla, zprávy nebo informace o platebních kartách). <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Nenačítat znovu</translation>
-<translation id="2900469785430194048">Prohlížeč Google Chrome k zobrazení této webové stránky neměl dostatek paměti.</translation>
<translation id="2909946352844186028">Byla zjištěna změna sítě.</translation>
<translation id="2916038427272391327">Zavřete ostatní programy</translation>
<translation id="2922350208395188000">Certifikát serveru nelze zkontrolovat.</translation>
<translation id="2928905813689894207">Fakturační adresa</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> další}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> další}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> další}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> dalších}}</translation>
<translation id="2941952326391522266">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />. Jeho bezpečnostní certifikát pochází z domény <ph name="DOMAIN2" />. 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="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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Pokud je tato možnost aktivována, prohlížeč Chromium do zařízení uloží kopii karty za účelem rychlejšího vyplňování formulářů.</translation>
<translation id="2985398929374701810">Zadejte platnou adresu</translation>
<translation id="2986368408720340940">Tento způsob vyzvednutí není k dispozici. Zkuste použít jiný způsob.</translation>
@@ -277,9 +281,7 @@
<translation id="3167968892399408617">Stránky, které otevřete na anonymních kartách, po zavření všech anonymních karet nezanechají žádné stopy v historii prohlížeče, v úložišti souborů cookie ani v historii vyhledávání. Zachovány však zůstanou všechny stažené soubory a vytvořené záložky.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Ostrov</translation>
-<translation id="317583078218509884">Nové nastavení oprávnění webových stránek se projeví po opětovném načtení stránky.</translation>
<translation id="3176929007561373547">Zkontrolujte nastavení proxy serveru nebo se obraťte na správce sítě, aby ověřil, zda proxy server funguje. Pokud se domníváte, že by proxy server neměl být používán: <ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Otevřete stránku v anonymním režimu</translation>
<translation id="320323717674993345">Zrušit platbu</translation>
<translation id="3207960819495026254">Přidáno do záložek</translation>
<translation id="3225919329040284222">Server se prokázal certifikátem, který neodpovídá integrovaným očekáváním. Tato očekávaní jsou zahrnuta u určitých webových stránek s vysokou úrovní zabezpečení kvůli vaší ochraně.</translation>
@@ -292,6 +294,7 @@
<translation id="3270847123878663523">&amp;Vrátit změnu uspořádání zpět</translation>
<translation id="3282497668470633863">Přidání jména na kartě</translation>
<translation id="3286538390144397061">Restartovat</translation>
+<translation id="3287510313208355388">Stáhnout, až bude zařízení online</translation>
<translation id="3303855915957856445">Nebyly nalezeny žádné výsledky</translation>
<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>
@@ -324,9 +327,8 @@
<translation id="3452404311384756672">Interval načtení:</translation>
<translation id="3462200631372590220">Skrýt rozšířené</translation>
<translation id="3467763166455606212">Je nutné zadat jméno držitele karty</translation>
-<translation id="3478058380795961209">Měsíc vypršení platnosti</translation>
<translation id="3479539252931486093">Nebylo toto očekáváno? <ph name="BEGIN_LINK" />Informujte nás<ph name="END_LINK" />.</translation>
-<translation id="3479552764303398839">Nyní ne</translation>
+<translation id="3479552764303398839">Teď ne</translation>
<translation id="3498215018399854026">V tuto chvíli se nám s vaším rodičem nepodařilo spojit. Zkuste to prosím znovu.</translation>
<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>
@@ -340,6 +342,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Obchodník přijímá kreditní a předplacené karty.</translation>
<translation id="3582930987043644930">Přidejte jméno</translation>
<translation id="3583757800736429874">&amp;Opakovat přesunutí</translation>
<translation id="3586931643579894722">Skrýt podrobnosti</translation>
@@ -354,10 +357,10 @@
<translation id="3655670868607891010">Pokud se vám tato stránka zobrazuje často, zkuste využít tyto <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Verze</translation>
<translation id="3678029195006412963">Požadavek nebylo možné podepsat</translation>
+<translation id="3678529606614285348">Otevřete stránku v novém anonymním okně (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Zpráva o selhání pořízená <ph name="CRASH_TIME" /> byla nahrána <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informace o certifikátu</translation>
<translation id="3690164694835360974">Přihlášení není zabezpečené</translation>
-<translation id="3693415264595406141">Heslo:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Načítání...</translation>
<translation id="3712624925041724820">Byly vyčerpány licence</translation>
@@ -369,6 +372,7 @@
<translation id="3748148204939282805">Útočníci na webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> vás mohou podvodem přimět k nebezpečnému chování, jako je instalace softwaru nebo vyzrazení osobních údajů (například hesel, telefonních čísel nebo platebních karet). <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Z důvodu chyby serveru se překlad nezdařil.</translation>
<translation id="3759461132968374835">Nemáte žádná nedávno hlášená selhání. Selhání, ke kterým došlo, když byla služba hlášení o selháních vypnutá, se zde nezobrazují.</translation>
+<translation id="3765032636089507299">Stránka Bezpečného procházení je ve výstavbě.</translation>
<translation id="3778403066972421603">Chcete tuto kartu uložit do svého účtu Google a do tohoto zařízení?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Platnost do: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -381,8 +385,10 @@
<translation id="3886446263141354045">Vaše žádost o přístup k tomuto webu byla odeslána uživateli <ph name="NAME" />.</translation>
<translation id="3890664840433101773">Přidat e-mail</translation>
<translation id="3901925938762663762">Karta vypršela</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID nahrané zprávy o selhání <ph name="CRASH_ID" /> (místní ID selhání: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />. V jeho bezpečnostním certifikátu nejsou uvedeny alternativní názvy subjektu. 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="3949601375789751990">Zde se zobrazuje vaše historie prohlížení</translation>
<translation id="3963721102035795474">Režim čtečky</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Žádné}=1{Z 1 webu }few{Z # webů }many{Z # webu }other{Z # webů }}</translation>
<translation id="397105322502079400">Probíhá výpočet…</translation>
@@ -411,6 +417,8 @@
<translation id="4165986682804962316">Nastavení webu</translation>
<translation id="4169947484918424451">Chcete, aby prohlížeč Chromium tuto kartu uložil?</translation>
<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="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>
@@ -434,12 +442,12 @@
<translation id="4356973930735388585">Útočníci, kteří se aktuálně nacházejí na tomto webu, 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).</translation>
<translation id="4372948949327679948">Očekávána hodnota <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Pokusili jste se přejít na web <ph name="DOMAIN" />, ale certifikát prezentovaný tímto webem byl vydavatelem certifikátu zrušen. To znamená, že bezpečnostním pověřením, která web prezentoval, nelze zcela důvěřovat. Je možné, že komunikujete s útočníkem.</translation>
-<translation id="4381091992796011497">Jméno uživatele:</translation>
<translation id="4394049700291259645">Deaktivovat</translation>
<translation id="4406896451731180161">výsledky vyhledávání</translation>
<translation id="4424024547088906515">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />. Chrome 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="4432688616882109544">Web <ph name="HOST_NAME" /> nepřijal přihlašovací certifikát, případně žádný certifikát nebyl poskytnut.</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="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>
@@ -451,12 +459,14 @@
<translation id="4587425331216688090">Odstranit adresu z Chromu?</translation>
<translation id="4592951414987517459">Vaše připojení k doméně <ph name="DOMAIN" /> je šifrováno za použití moderní šifrovací sady.</translation>
<translation id="4594403342090139922">&amp;Vrátit smazání zpět</translation>
+<translation id="4611292653554630842">Přihlásit se</translation>
<translation id="4619615317237390068">Karty z ostatních zařízení</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />, protože jeho bezpečnostní certifikát obsahuje chyby. 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="4690462567478992370">Přestat používat neplatný certifikát</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Připojení bylo přerušeno</translation>
+<translation id="471880041731876836">K návštěvě tohoto webu nemáte povolení</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Spustit Diagnostiku sítě systému Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Znovu načíst zásady</translation>
<translation id="4728558894243024398">Platforma</translation>
@@ -478,14 +488,15 @@
<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="4863764087567530506">Tento obsah by se vás podvodem mohl pokusit přimět k instalaci softwaru nebo odhalení osobních údajů. <ph name="BEGIN_LINK" />Přesto zobrazit<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Hledat v historii</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Je vyžadováno ověření</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{a 1 další webová stránka}few{a # další webové stránky}many{a # další webové stránky}other{a # dalších webových stránek}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Tato stránka byla přeložena z neznámého jazyka do jazyka <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Platba</translation>
<translation id="4926049483395192435">Musí být uvedeno</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Akce</translation>
<translation id="4958444002117714549">Rozbalit seznam</translation>
<translation id="4974590756084640048">Znovu zapnout upozornění</translation>
@@ -501,6 +512,7 @@
<translation id="5045550434625856497">Nesprávné heslo</translation>
<translation id="5056549851600133418">Články pro vás</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Zkontrolovat adresu proxy serveru<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Můžete ztratit přístup k chráněnému obsahu z některých webů.</translation>
<translation id="5087286274860437796">Certifikát serveru v tuto chvíli není platný.</translation>
<translation id="5087580092889165836">Přidat kartu</translation>
<translation id="5089810972385038852">Stát/kraj</translation>
@@ -508,18 +520,27 @@
<translation id="5095208057601539847">Provincie</translation>
<translation id="5115563688576182185">(64bitový)</translation>
<translation id="5141240743006678641">Šifrovat synchronizovaná hesla pomocí hesla k účtu Google</translation>
-<translation id="514421653919133810">Otevřete stránku v anonymním režimu (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Odpověď zásady obsahuje kód chyby</translation>
+<translation id="5159010409087891077">Otevřete stránku v novém anonymním okně (⇧⌘N)</translation>
<translation id="5171045022955879922">Vyhledávejte či zadejte URL</translation>
<translation id="5172758083709347301">Počítač</translation>
<translation id="5179510805599951267">Nejedná se o jazyk <ph name="ORIGINAL_LANGUAGE" />? Nahlaste tuto chybu.</translation>
-<translation id="5181140330217080051">Stahování</translation>
<translation id="5190835502935405962">Lišta záložek</translation>
<translation id="5199729219167945352">Experimenty</translation>
<translation id="5205222826937269299">Je nutné zadat jméno</translation>
<translation id="5222812217790122047">Je nutné zadat e-mail</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Používáte Chrome v práci? Firmy mohou spravovat nastavení prohlížeče Chrome pro své zaměstnance. Další informace</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Abyste se mohli dostat na web, podle těchto pokynů software dočasně zakažte. Budete potřebovat administrátorská práva.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klikněte na <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, vyhledejte položku <ph name="BEGIN_BOLD" />Zobrazit místní služby<ph name="END_BOLD" /> a klikněte na ni.
+ <ph name="LIST_ITEM" />Vyberte službu <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />V rozbalovací nabídce <ph name="BEGIN_BOLD" />Typ spouštění<ph name="END_BOLD" /> vyberte <ph name="BEGIN_BOLD" />Zakázáno<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />V sekci <ph name="BEGIN_BOLD" />Stav služby<ph name="END_BOLD" /> klikněte na <ph name="BEGIN_BOLD" />Zastavit<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Klikněte na <ph name="BEGIN_BOLD" />Použít<ph name="END_BOLD" /> a poté na <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Informace o tom, jak tento software trvale odstranit z počítače, naleznete v <ph name="BEGIN_LEARN_MORE_LINK" />centru nápovědy prohlížeče Chrome<ph name="END_LEARN_MORE_LINK" />.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Připojení k tomuto webu není soukromé. Režim VR můžete kdykoliv ukončit tím, že sejmete náhlavní soupravu a stisknete tlačítko Zpět.</translation>
<translation id="5299298092464848405">Při analýze zásady došlo k chybě</translation>
<translation id="5308689395849655368">Zprávy o selhání jsou zakázány.</translation>
@@ -536,9 +557,10 @@
<translation id="5439770059721715174">Chyba validace schématu v místě <ph name="ERROR_PATH" />: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Tuto stránku na webu <ph name="HOST_NAME" /> nelze najít</translation>
<translation id="5455374756549232013">Chybné časové razítko zásady</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> z <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Neplatné</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> další}few{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> další}many{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> dalšího}other{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> dalších}}</translation>
<translation id="5470861586879999274">&amp;Opakovat úpravy</translation>
+<translation id="5481076368049295676">Tento obsah by vám do zařízení mohl zkusit nainstalovat nebezpečný software, který odcizuje nebo maže informace. <ph name="BEGIN_LINK" />Přesto zobrazit<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Přidejte platnou adresu</translation>
<translation id="5492298309214877701">Tato stránka v intranetu společnosti, organizace nebo školy má stejnou adresu URL jako externí web.
<ph name="LINE_BREAK" />
@@ -550,6 +572,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="5540224163453853">Požadovaný článek nebyl nalezen.</translation>
<translation id="5544037170328430102">Vložená stránka na webu <ph name="SITE" /> říká:</translation>
<translation id="5556459405103347317">Načíst znovu</translation>
+<translation id="5560088892362098740">Datum vypršení platnosti</translation>
<translation id="5565735124758917034">Aktivní</translation>
<translation id="5571083550517324815">Vyzvednutí na této adrese není možné. Vyberte jinou adresu.</translation>
<translation id="5572851009514199876">Přihlaste se do Chromu, aby bylo možné ověřit, zda máte povolení tento web navštívit.</translation>
@@ -564,12 +587,13 @@ Kontaktujte administrátora systému.</translation>
<translation id="5629630648637658800">Načítání nastavení zásady se nezdařilo</translation>
<translation id="5631439013527180824">Neplatný token správy zařízení</translation>
<translation id="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="5646376287012673985">Poloha</translation>
<translation id="5659593005791499971">E-mail</translation>
<translation id="5669703222995421982">Získejte personalizovaný obsah</translation>
<translation id="5675650730144413517">Tato stránka nefunguje</translation>
<translation id="5710435578057952990">Identita těchto webových stránek nebyla ověřena.</translation>
-<translation id="5713016350996637505">Byl zablokován klamavý obsah</translation>
+<translation id="5719499550583120431">Obchodník přijímá předplacené karty.</translation>
<translation id="5720705177508910913">Aktuální uživatel</translation>
<translation id="5732392974455271431">Rodiče ti jej mohou odblokovat.</translation>
<translation id="5763042198335101085">Zadejte platnou e-mailovou adresu</translation>
@@ -581,15 +605,14 @@ Kontaktujte administrátora systému.</translation>
<translation id="5803412860119678065">Chcete vyplnit informace o kartě <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Vaše připojení k doméně <ph name="DOMAIN" /> je šifrováno za použití zastaralé šifrovací sady.</translation>
<translation id="5813119285467412249">&amp;Opakovat přidání</translation>
-<translation id="5814352347845180253">Můžete ztratit přístup k prémiovému obsahu z webu <ph name="SITE" /> a některých dalších webů.</translation>
<translation id="5838278095973806738">Na tento web byste neměli zadávat citlivé údaje (například hesla nebo čísla platebních karet), protože by je mohli odcizit útočníci.</translation>
<translation id="5869405914158311789">Tento web není dostupný</translation>
<translation id="5869522115854928033">Uložená hesla</translation>
<translation id="5872918882028971132">Návrhy rodičů</translation>
+<translation id="5893752035575986141">Obchodník přijímá kreditní karty.</translation>
<translation id="5901630391730855834">Žlutá</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="5926846154125914413">Můžete ztratit přístup k prémiovému obsahu z některých webů.</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="5967867314010545767">Odstranit z historie</translation>
<translation id="5975083100439434680">Oddálit</translation>
@@ -605,6 +628,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="6040143037577758943">Zavřít</translation>
<translation id="6042308850641462728">Více</translation>
<translation id="6047233362582046994">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é aplikace odstraněny.</translation>
+<translation id="6047927260846328439">Tento obsah by se vás podvodem mohl pokusit přimět k instalaci softwaru nebo odhalení osobních údajů. <ph name="BEGIN_LINK" />Přesto zobrazit<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Web <ph name="SITE" /> nyní nemůžete navštívit, protože používá připínání certifikátů. Síťové chyby a útoky jsou obvykle dočasné, tato stránka pravděpodobně později bude fungovat.</translation>
<translation id="6060685159320643512">Pozor, tyto experimenty mohou skončit všelijak.</translation>
<translation id="6080696365213338172">Získali jste přístup k obsahu pomocí certifikátu poskytnutého správcem. Údaje poskytovaná doméně <ph name="DOMAIN" /> bude správce moci zachytit.</translation>
@@ -617,7 +641,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="6165508094623778733">Další informace</translation>
<translation id="6169916984152623906">Nyní můžete procházet internet v soukromí a ostatní uživatelé tohoto zařízení vaši aktivitu neuvidí. Stažené soubory a záložky budou uloženy.</translation>
<translation id="6177128806592000436">Spojení s tímto webem není bezpečné</translation>
-<translation id="6184817833369986695">(kohorta: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Zkontrolujte připojení k internetu</translation>
<translation id="6218753634732582820">Odstranit adresu z prohlížeče Chromium?</translation>
<translation id="6221345481584921695">Služba Bezpečné prohlížení Google na webu <ph name="SITE" /> nedávno <ph name="BEGIN_LINK" />zjistila malware<ph name="END_LINK" />. Někdy mohou být malwarem nakaženy i weby, které jsou obvykle bezpečné. Tento škodlivý obsah pochází z webu <ph name="SUBRESOURCE_HOST" />, který je distribucí malwaru známý.</translation>
@@ -636,13 +659,14 @@ Kontaktujte administrátora systému.</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>
<translation id="6387478394221739770">Zajímají vás nové funkce Chromu? Vyzkoušejte kanál beta na adrese chrome.com/beta.</translation>
-<translation id="6389758589412724634">Prohlížeč Chromium k zobrazení této webové stránky neměl dostatek paměti.</translation>
+<translation id="6397451950548600259">Software na počítači Chromu brání v bezpečném připojení k webu.</translation>
<translation id="6404511346730675251">Upravit záložku</translation>
<translation id="6410264514553301377">Zadejte datum vypršení platnosti a kód CVC karty <ph name="CREDIT_CARD" />.</translation>
<translation id="6414888972213066896">Zeptal(a) ses rodiče, zda můžeš navštívit tento web.</translation>
@@ -657,6 +681,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="647261751007945333">Zásady zařízení</translation>
<translation id="6477321094435799029">Chrome na této stránce zjistil neobvyklý kód a z důvodu ochrany vašich osobních údajů (například hesel, telefonních čísel a platebních karet) ji zablokoval.</translation>
<translation id="6489534406876378309">Začít nahrávat zprávy o selhání</translation>
+<translation id="6507833130742554667">Obchodník přijímá kreditní a debetní karty.</translation>
<translation id="6508722015517270189">Restartujte Chrome</translation>
<translation id="6529602333819889595">&amp;Opakovat smazání</translation>
<translation id="6534179046333460208">Návrhy fyzického webu</translation>
@@ -665,13 +690,13 @@ Kontaktujte administrátora systému.</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="657639383826808334">Tento obsah by vám do zařízení mohl zkusit nainstalovat nebezpečný software, který odcizuje nebo maže informace. <ph name="BEGIN_LINK" />Přesto zobrazit<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Možnosti šifrování</translation>
<translation id="662080504995468778">Zůstat</translation>
<translation id="6626291197371920147">Přidání platného čísla karty</translation>
<translation id="6628463337424475685">Vyhledávání <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Ú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 do vašeho počítače Mac 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="6644283850729428850">Tato zásada se již nepoužívá.</translation>
+<translation id="6657585470893396449">Heslo</translation>
<translation id="6671697161687535275">Odstranit návrh položky formuláře z prohlížeče Chromium?</translation>
<translation id="6685834062052613830">Odhlaste se a dokončete nastavení</translation>
<translation id="6710213216561001401">Předchozí</translation>
@@ -702,7 +727,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="6970216967273061347">Obvod</translation>
<translation id="6973656660372572881">Určeny jsou pevně dané servery proxy i adresa URL skriptu PAC.</translation>
<translation id="6989763994942163495">Zobrazit rozšířená nastavení...</translation>
-<translation id="7000990526846637657">Nebyly nalezeny žádné historické záznamy</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Na adrese <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> mohou být k dispozici další formy historie prohlížení zaznamenané ve vašem účtu Google</translation>
<translation id="7029809446516969842">Hesla</translation>
@@ -717,7 +741,9 @@ Kontaktujte administrátora systému.</translation>
<translation id="7129409597930077180">Dodání na tuto adresu není možné. Vyberte jinou adresu.</translation>
<translation id="7138472120740807366">Způsob doručení</translation>
<translation id="7139724024395191329">Emirát</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> další}few{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> další}many{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> další}other{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> dalších}}</translation>
<translation id="7155487117670177674">Platba není zabezpečená</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> další}few{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> další}many{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> další}other{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> dalších}}</translation>
<translation id="7179921470347911571">Spustit znovu</translation>
<translation id="7180611975245234373">Obnovit</translation>
<translation id="7182878459783632708">Nebyly nastaveny žádné zásady</translation>
@@ -758,6 +784,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="7514365320538308">Stáhnout</translation>
<translation id="7518003948725431193">Na webové adrese <ph name="URL" /> se nepodařilo nalézt žádnou webovou stránku.</translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Připojení k tomuto webu není soukromé</translation>
<translation id="7535087603100972091">Hodnota</translation>
<translation id="7537536606612762813">Povinná</translation>
<translation id="7542403920425041731">Po ověření budou údaje o kartě sdíleny s tímto webem.</translation>
@@ -767,7 +794,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="7552846755917812628">Vyzkoušejte následující tipy:</translation>
<translation id="7554791636758816595">Nová karta</translation>
<translation id="7567204685887185387">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />. Jeho bezpečnostní certifikát byl zřejmě vydán podvodně. 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="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> další}few{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> další}many{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> dalšího}other{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> dalších}}</translation>
<translation id="7568593326407688803">Tato stránka je v jazyce<ph name="ORIGINAL_LANGUAGE" />Chcete ji přeložit?</translation>
<translation id="7569952961197462199">Odstranit platební kartu z Chromu?</translation>
<translation id="7569983096843329377">Černá</translation>
@@ -794,9 +820,11 @@ Kontaktujte administrátora systému.</translation>
<translation id="7714464543167945231">Certifikát</translation>
<translation id="7716147886133743102">Blokováno administrátorem</translation>
<translation id="7716424297397655342">Tento web nelze načíst z mezipaměti</translation>
+<translation id="774634243536837715">Byl zablokován nebezpečný obsah.</translation>
<translation id="7752995774971033316">Nespravováno</translation>
<translation id="7755287808199759310">Rodič ti jej může odblokovat.</translation>
<translation id="7758069387465995638">Připojení mohlo být zablokováno firewallem nebo antivirovým softwarem.</translation>
+<translation id="7759163816903619567">Zobrazovaná doména:</translation>
<translation id="7761701407923456692">Certifikát serveru neodpovídá adrese URL.</translation>
<translation id="7763386264682878361">Analyzátor manifestů plateb</translation>
<translation id="7764225426217299476">Přidat adresu</translation>
@@ -811,6 +839,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="7815407501681723534">Nalezené <ph name="SEARCH_RESULTS" /> pro dotaz „<ph name="SEARCH_STRING" />“: <ph name="NUMBER_OF_RESULTS" /></translation>
<translation id="785549533363645510">To neznamená, že jste neviditelní. Anonymní režim neskryje vaši aktivitu před vaším zaměstnavatelem, poskytovatelem internetových služeb ani webovými stránkami, které navštívíte.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Obchodník přijímá debetní a předplacené karty.</translation>
<translation id="7887683347370398519">Zkontrolujte kód CVC a zkuste to znovu</translation>
<translation id="79338296614623784">Zadejte platné telefonní číslo</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -830,7 +859,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="8041089156583427627">Odeslat svůj názor</translation>
<translation id="8041940743680923270">Použít výchozí globální hodnotu (Dotázat se)</translation>
<translation id="8088680233425245692">Zobrazení článku se nezdařilo.</translation>
-<translation id="8089520772729574115">méně než 1 MB</translation>
<translation id="8091372947890762290">Čeká se na aktivaci na serveru</translation>
<translation id="8118489163946903409">Platební metoda</translation>
<translation id="8131740175452115882">Potvrdit</translation>
@@ -842,13 +870,16 @@ Kontaktujte administrátora systému.</translation>
<translation id="8194797478851900357">&amp;Vrátit přesunutí zpět</translation>
<translation id="8201077131113104583">Neplatná adresa URL aktualizace rozšíření s ID <ph name="EXTENSION_ID" />.</translation>
<translation id="8202097416529803614">Shrnutí objednávky</translation>
+<translation id="8205463626947051446">Tento web často zobrazuje rušivé reklamy</translation>
<translation id="8218327578424803826">Přiřazené místo:</translation>
<translation id="8225771182978767009">Uživatel, který tento počítač nastavoval, se rozhodl tento web blokovat.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Otevřete stránku na nové anonymní kartě</translation>
<translation id="8241707690549784388">Stránka, kterou hledáte, používala vámi zadané informace. Návrat na tuto stránku by mohl způsobit, že akce, kterou jste provedli, bude opakována. Přejete si pokračovat?</translation>
+<translation id="8241712895048303527">Na tomto webu blokovat</translation>
<translation id="8249320324621329438">Naposledy načteno:</translation>
<translation id="8253091569723639551">Fakturační adresa je povinná</translation>
-<translation id="8261506727792406068">Vymazat</translation>
+<translation id="8261506727792406068">Smazat</translation>
<translation id="8289355894181816810">Pokud nevíte, co dělat, obraťte se na svého správce sítě.</translation>
<translation id="8293206222192510085">Přidat záložku</translation>
<translation id="8294431847097064396">Zdroj</translation>
@@ -856,7 +887,6 @@ 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="8332188693563227489">Přístup k webu <ph name="HOST_NAME" /> byl odepřen</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="8344669043927012510">Otevřete stránku v anonymním režimu (⇧⌘N)</translation>
<translation id="8349305172487531364">Lišta záložek</translation>
<translation id="8363502534493474904">Vypnout režim Letadlo</translation>
<translation id="8364627913115013041">Nenastaveno.</translation>
@@ -866,6 +896,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="8398259832188219207">Zpráva o selhání nahraná <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Selhání (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Stejnou heslovou frázi musíte zadat dvakrát.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nastavení</translation>
<translation id="8433057134996913067">Budete odhlášeni z většiny webů.</translation>
<translation id="8437238597147034694">&amp;Vrátit přesunutí zpět</translation>
@@ -873,8 +904,9 @@ Kontaktujte administrátora systému.</translation>
<translation id="8483780878231876732">Chcete-li používat karty z účtu Google, přihlaste se do Chromu.</translation>
<translation id="8488350697529856933">Platí pro</translation>
<translation id="8498891568109133222">Odpověď webu <ph name="HOST_NAME" /> trvala příliš dlouho.</translation>
-<translation id="8532105204136943229">Rok vypršení platnosti</translation>
+<translation id="8503813439785031346">Uživatelské jméno</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="8571890674111243710">Překlad stránky do jazyka: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Přidat telefon
@@ -882,6 +914,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="859285277496340001">V certifikátu není uvedeno, jakým způsobem lze zkontrolovat, zda nebyl zrušen.</translation>
<translation id="8620436878122366504">Rodiče přístup dosud neschválili.</translation>
<translation id="8647750283161643317">Obnovit u všech experimentů výchozí nastavení</translation>
+<translation id="8660471606262461360">Od služby Google Payments</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="8725066075913043281">Zkusit znovu</translation>
@@ -909,6 +942,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="8903921497873541725">Přiblížit</translation>
<translation id="8931333241327730545">Chcete tuto kartu uložit do účtu Google?</translation>
<translation id="8932102934695377596">Vaše hodiny se zpožďují</translation>
+<translation id="8938939909778640821">Přijímané kreditní a předplacené karty</translation>
<translation id="8971063699422889582">Platnost certifikátu serveru vypršela.</translation>
<translation id="8986494364107987395">Automaticky posílat společnosti Google statistiky používání a zprávy o selhání</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -938,7 +972,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="9169664750068251925">Blokovat vždy na tomto webu</translation>
<translation id="9170848237812810038">Z&amp;pět</translation>
<translation id="917450738466192189">Certifikát serveru je neplatný.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> další}few{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> další}many{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> dalšího}other{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> dalších}}</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>
<translation id="9207861905230894330">Přidání článku se nezdařilo.</translation>
@@ -947,9 +980,9 @@ Kontaktujte administrátora systému.</translation>
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">VYMAZAT FORMULÁŘ</translation>
<translation id="939736085109172342">Nová složka</translation>
-<translation id="941721044073577244">Zdá se, že k přístupu na tento web nemáte oprávnění</translation>
<translation id="969892804517981540">Oficiální sestavení</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Žádné}=1{1 položka}few{# položky}many{# položky}other{# položek}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Vývojářské sestavení</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_da.xtb b/chromium/components/strings/components_strings_da.xtb
index 3e54384ac0a..558e82a9601 100644
--- a/chromium/components/strings/components_strings_da.xtb
+++ b/chromium/components/strings/components_strings_da.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Luk andre apps</translation>
<translation id="1055184225775184556">&amp;Fortryd tilføjelse</translation>
<translation id="10614374240317010">Aldrig gemt</translation>
+<translation id="1066396345355680611">Du mister muligvis adgang til beskyttet adgang fra <ph name="SITE" /> og visse andre websites.</translation>
<translation id="106701514854093668">Bogmærker på pc</translation>
<translation id="1074497978438210769">Ikke sikker</translation>
<translation id="1080116354587839789">Tilpas til bredden</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Læseliste</translation>
<translation id="1264126396475825575">Der blev registreret en nedbrudsrapport <ph name="CRASH_TIME" /> (endnu ikke uploadet eller ignoreret)</translation>
<translation id="1281526147609854549">Udgivet af <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Farligt indhold er blokeret</translation>
<translation id="1285320974508926690">Oversæt aldrig dette website</translation>
<translation id="129553762522093515">Senest lukkede</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Prøv at rydde dine cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Registreringsdomæne:</translation>
<translation id="1340482604681802745">Afhentningsadresse</translation>
-<translation id="1344211575059133124">Det ser ud til, at du skal have tilladelse til at besøge dette website</translation>
<translation id="1344588688991793829">Indstillinger for AutoFyld i Chromium...</translation>
<translation id="1348198688976932919">Det website, du er på vej til, indeholder farlige apps</translation>
<translation id="1374468813861204354">forslag</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identiteten for <ph name="ORGANIZATION" /> på <ph name="LOCALITY" /> er bekræftet af <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ja</translation>
<translation id="1430915738399379752">Udskriv</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> anden}one{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> anden}other{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> andre}}</translation>
<translation id="1506687042165942984">Vis en gemt (dvs. forældet) kopi af denne side.</translation>
<translation id="1517433312004943670">Telefonnummer er påkrævet</translation>
+<translation id="1517500485252541695">Accepterede betalingskort</translation>
<translation id="1519264250979466059">Versionsdato</translation>
+<translation id="1527263332363067270">Venter på forbindelse…</translation>
<translation id="153384715582417236">Det var det hele indtil videre.</translation>
<translation id="1549470594296187301">JavaScript skal være aktiveret, før du kan bruge denne funktion.</translation>
<translation id="1555130319947370107">Blå</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Download siden senere</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Obligatorisk felt</translation>
<translation id="187918866476621466">Åbn opstartssider</translation>
<translation id="1883255238294161206">Skjul liste</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> anden}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> anden}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> andre}}</translation>
<translation id="1898423065542865115">Filtrering</translation>
+<translation id="1916770123977586577">Genindlæs denne side for at anvende dine opdaterede indstillinger på dette website</translation>
+<translation id="1919345977826869612">Annoncer</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Ingen}=1{1 website}one{# website}other{# websites}}</translation>
<translation id="194030505837763158">Gå til <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Accepterede forudbetalte kort</translation>
<translation id="1962204205936693436">Bogmærker fra <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Serialiseringsfejl</translation>
<translation id="1974060860693918893">Avanceret</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP-fejl</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2282872951544483773">Utilgængelige eksperimenter</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element}one{<ph name="ITEM_COUNT" /> element}other{<ph name="ITEM_COUNT" /> elementer}}</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>
<translation id="2337852623177822836">Indstillingen styres af din administrator</translation>
<translation id="2354001756790975382">Andre bogmærker</translation>
<translation id="2354430244986887761">Google Beskyttet browsing har for nylig <ph name="BEGIN_LINK" />fundet skadelige apps<ph name="END_LINK" /> på <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Fortsæt</translation>
<translation id="2365563543831475020">Nedbrudsrapporten, der blev registreret <ph name="CRASH_TIME" />, blev ikke uploadet</translation>
<translation id="2367567093518048410">Niveau</translation>
-<translation id="237718015863234333">Der er ingen alternative grænseflader</translation>
<translation id="2384307209577226199">Virksomhedsstandard</translation>
<translation id="2386255080630008482">Serverens certifikat er blevet tilbagekaldt.</translation>
<translation id="2392959068659972793">Vis politikker uden nogen værdier</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Hvis dette felt er markeret, gemmer Chromium en kopi af dit kort på denne enhed for at gøre det hurtigere at udfylde formularer.</translation>
<translation id="2498091847651709837">Scan et nyt kort</translation>
<translation id="2501278716633472235">Gå tilbage</translation>
+<translation id="2503184589641749290">Accepterede debetkort og forudbetalte kort</translation>
<translation id="2515629240566999685">Kontrollere signalet i dit område</translation>
-<translation id="2516305470678292029">Alternative grænseflader</translation>
<translation id="2539524384386349900">Registrer</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> sendte et ugyldigt svar.</translation>
<translation id="2556876185419854533">&amp;Fortryd redigering</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Forsendelsesmetode</translation>
<translation id="277499241957683684">Manglende enhedsregistrering</translation>
<translation id="2784949926578158345">Forbindelsen blev nulstillet.</translation>
+<translation id="2788784517760473862">Accepterede kreditkort</translation>
<translation id="2794233252405721443">Websitet er blokeret</translation>
<translation id="2799020568854403057">Det website, du er på vej til, indeholder skadelige apps</translation>
<translation id="2803306138276472711">Google Beskyttet browsing <ph name="BEGIN_LINK" />registrerede malware<ph name="END_LINK" /> på <ph name="SITE" />. Websites, der normalt er sikre, bliver undertiden inficeret med malware.</translation>
<translation id="2824775600643448204">Adresse og søgelinje</translation>
<translation id="2826760142808435982">Forbindelsen er krypteret og godkendt ved hjælp af <ph name="CIPHER" />, og den anvender <ph name="KX" /> som primær udvekslingsmekanisme.</translation>
<translation id="2835170189407361413">Ryd formular</translation>
+<translation id="2851634818064021665">Du skal have tilladelse til at besøge dette website</translation>
<translation id="2856444702002559011">Brugere med ondsindede hensigter kan forsøge at stjæle dine oplysninger fra <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (f.eks. adgangskoder, beskeder eller kreditkort). <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Genindlæs ikke siden</translation>
-<translation id="2900469785430194048">Google Chrome løb tør for hukommelse, da websiden skulle vises.</translation>
<translation id="2909946352844186028">Der blev registreret en netværksændring.</translation>
<translation id="2916038427272391327">Luk andre programmer</translation>
<translation id="2922350208395188000">Serverens certifikat kan ikke kontrolleres.</translation>
<translation id="2928905813689894207">Faktureringsadresse</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> anden}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> anden}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> andre}}</translation>
<translation id="2941952326391522266">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da sikkerhedscertifikatet er fra <ph name="DOMAIN2" />. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Hvis denne indstilling er slået til, gemmer Chromium en kopi af dit kort på denne enhed for at gøre det hurtigere at udfylde formularer.</translation>
<translation id="2985398929374701810">Angiv en gyldig adresse</translation>
<translation id="2986368408720340940">Denne afhentningsmetode er ikke tilgængelig. Prøv en anden metode.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Når du har lukket alle dine inkognitofaner, gemmes der hverken cookies, browser- eller søgehistorik for de sider, du besøger i inkognitotilstand. Dog gemmes alle de filer, du downloader, og bogmærker, du opretter.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Ø</translation>
-<translation id="317583078218509884">De nye indstillinger for websitetilladelser træder i kraft, når du har genindlæst siden.</translation>
<translation id="3176929007561373547">Kontrollér dine proxyindstillinger, eller kontakt din netværksadministrator
for at sikre, at proxyserveren fungerer. Hvis du ikke mener,
at du skal bruge en proxyserver, skal du:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Åbn side i inkognitotilstand</translation>
<translation id="320323717674993345">Annuller betaling</translation>
<translation id="3207960819495026254">Bogmærket</translation>
<translation id="3225919329040284222">Serveren præsenterede et certifikat, der ikke svarer til de indbyggede forventninger. Disse forventninger medtages for bestemte websites med høj sikkerhed for at beskytte dig.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Fortryd omarrangering</translation>
<translation id="3282497668470633863">Tilføj navn på kort</translation>
<translation id="3286538390144397061">Genstart nu</translation>
+<translation id="3287510313208355388">Download, når du er online</translation>
<translation id="3303855915957856445">Der blev ikke fundet nogen søgeresultater</translation>
<translation id="3305707030755673451">Dine data blev krypteret med din adgangssætning til synkronisering d. <ph name="TIME" />. Indtast adgangssætningen for at starte synkroniseringen.</translation>
<translation id="3320021301628644560">Tilføj faktureringsadresse</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Hent interval:</translation>
<translation id="3462200631372590220">Skjul avanceret</translation>
<translation id="3467763166455606212">Kortindehavers navn skal angives</translation>
-<translation id="3478058380795961209">Udløbsmåned:</translation>
<translation id="3479539252931486093">Var dette uventet? <ph name="BEGIN_LINK" />Giv os gerne feedback<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ikke nu</translation>
<translation id="3498215018399854026">Vi kan ikke få fat i din forælder på nuværende tidspunkt. Prøv igen.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Tilpas dato og klokkeslæt i sektionen &lt;strong&gt;Generelt&lt;/strong&gt; i appen &lt;strong&gt;Indstillinger&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kreditkort og forudbetalte kort accepteres.</translation>
<translation id="3582930987043644930">Tilføj navn</translation>
<translation id="3583757800736429874">&amp;Annuller fortryd flytning</translation>
<translation id="3586931643579894722">Skjul oplysninger</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Hvis du ser dette jævnligt, kan du prøve <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revision</translation>
<translation id="3678029195006412963">Anmodningen kunne ikke signeres</translation>
+<translation id="3678529606614285348">Åbn siden i et nyt inkognitovindue (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Nedbrud registreret <ph name="CRASH_TIME" />, uploadet <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Certifikatoplysninger</translation>
<translation id="3690164694835360974">Login er ikke sikkert</translation>
-<translation id="3693415264595406141">Adgangskode:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Indlæser...</translation>
<translation id="3712624925041724820">Licenserne er opbrugt</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Brugere med ondsindede hensigter <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan narre dig til at gøre noget farligt såsom at installere software eller afsløre dine personlige oplysninger (f.eks. adgangskoder, telefonnumre eller kreditkort). <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Oversættelsen mislykkedes på grund af en serverfejl.</translation>
<translation id="3759461132968374835">Du har ingen nyligt rapporterede nedbrud. Nedbrud, der opstod, mens rapportering om nedbrud var deaktiveret, vises ikke her.</translation>
+<translation id="3765032636089507299">Siden Beskyttet browsing er under opførelse.</translation>
<translation id="3778403066972421603">Vil du gemme dette kort på din Google-konto og på denne enhed?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Udløber <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Din anmodning om adgang til dette website er blevet sendt til <ph name="NAME" />.</translation>
<translation id="3890664840433101773">Tilføj e-mail</translation>
<translation id="3901925938762663762">Kortet er udløbet</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Uploadet nedbruds-id <ph name="CRASH_ID" /> (lokalt nedbruds-id: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da sikkerhedscertifikatet ikke angiver alternative navne på emner. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
+<translation id="3949601375789751990">Din browserhistorik vises her</translation>
<translation id="3963721102035795474">Læser-tilstand</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Ingen}=1{1 website }one{# website }other{# websites }}</translation>
<translation id="397105322502079400">Beregner...</translation>
@@ -400,7 +406,7 @@
<translation id="4072486802667267160">Der opstod en fejl under behandlingen af din ordre. Prøv igen.</translation>
<translation id="4075732493274867456">Klienten og serveren understøtter ikke en fælles SSL-protokolversion eller et fælles krypteringsprogram.</translation>
<translation id="4079302484614802869">Proxykonfiguration er angivet til at anvende en webadresse for .pac-script, ikke faste proxyservere.</translation>
-<translation id="4098354747657067197">Misvisende website forude</translation>
+<translation id="4098354747657067197">Vildledende website forude</translation>
<translation id="4103249731201008433">Enhedens serienummer er ugyldigt</translation>
<translation id="410351446219883937">Autoplay</translation>
<translation id="4103763322291513355">Gå til &lt;strong&gt;chrome://policy&lt;/strong&gt; for at se listen over sortlistede webadresser og andre politikker, din systemadministrator har igangsat.</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Indstillinger for website</translation>
<translation id="4169947484918424451">Skal Chromium gemme dette kort?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Hackere på dette website vil muligvis forsøge at installere skadelige programmer på din computer, som stjæler eller sletter dine oplysninger (f.eks. billeder, adgangskoder, beskeder og kreditkortoplysninger).</translation>
<translation id="4372948949327679948">Forventet <ph name="VALUE_TYPE" />-værdi.</translation>
<translation id="4377125064752653719">Du har forsøgt at få fat på <ph name="DOMAIN" />, men serverens certifikat er blevet tilbagekaldt af udgiveren. Det betyder, at du bestemt ikke bør have tillid til serverens sikkerhedsoplysninger. Du kommunikerer muligvis med en hacker.</translation>
-<translation id="4381091992796011497">Brugernavn:</translation>
<translation id="4394049700291259645">Deaktiver</translation>
<translation id="4406896451731180161">søgeresultater</translation>
<translation id="4424024547088906515">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da Chrome ikke har tillid til sikkerhedscertifikatet. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> accepterede ikke dit logincertifikat, eller der er ikke angivet et.</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="4506176782989081258">Valideringsfejl: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kontakte systemadministratoren</translation>
<translation id="450710068430902550">Deling med administrator</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Vil du fjerne adressen fra Chrome?</translation>
<translation id="4592951414987517459">Din forbindelse til <ph name="DOMAIN" /> er krypteret ved hjælp af en moderne krypteringspakke.</translation>
<translation id="4594403342090139922">&amp;Fortryd sletning</translation>
+<translation id="4611292653554630842">Log ind</translation>
<translation id="4619615317237390068">Faner fra andre enheder</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da sikkerhedscertifikatet indeholder fejl. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
<translation id="4690462567478992370">Stop med at bruge et ugyldigt certifikat</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Din forbindelse blev afbrudt</translation>
+<translation id="471880041731876836">Du har ikke tilladelse til at besøge dette website</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Køre Windows Netværksdiagnosticering<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Opdater politikker</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Dette indhold forsøger muligvis at narre dig til at installere software eller afsløre personlige oplysninger. <ph name="BEGIN_LINK" />Vis alligevel<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Søg i historikken</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Godkendelse er påkrævet</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{og 1 anden webside}one{og # anden webside}other{og # andre websider}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Denne side er oversat fra et ukendt sprog til <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Betaling</translation>
<translation id="4926049483395192435">Skal angives.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Handlinger</translation>
<translation id="4958444002117714549">Udvid liste</translation>
<translation id="4974590756084640048">Genaktiver advarsler</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Ugyldig adgangskode</translation>
<translation id="5056549851600133418">Artikler til dig</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Tjekke proxy-adressen<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Du kan miste adgang til beskyttet indhold fra visse websites.</translation>
<translation id="5087286274860437796">Serverens certifikatet er ikke gyldigt i øjeblikket.</translation>
<translation id="5087580092889165836">Tilføj kort</translation>
<translation id="5089810972385038852">Delstat</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provins</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5141240743006678641">Krypter synkroniserede adgangskoder med dine Google-loginoplysninger</translation>
-<translation id="514421653919133810">Åbn side i inkognitotilstand (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Fejlkode til stede i politiksvar</translation>
+<translation id="5159010409087891077">Åbn siden i et nyt inkognitovindue (⇧⌘N)</translation>
<translation id="5171045022955879922">Søg, eller indtast webadresse</translation>
<translation id="5172758083709347301">Maskine</translation>
<translation id="5179510805599951267">Ikke på <ph name="ORIGINAL_LANGUAGE" />? Rapporter denne fejl</translation>
-<translation id="5181140330217080051">Downloader</translation>
<translation id="5190835502935405962">Bogmærkelinje</translation>
<translation id="5199729219167945352">Eksperimenter</translation>
<translation id="5205222826937269299">Navn påkrævet</translation>
<translation id="5222812217790122047">E-mail påkrævet</translation>
<translation id="5251803541071282808">Skyen</translation>
<translation id="5277279256032773186">Bruger du Chrome på arbejdet? Virksomheder kan administrere Chrome-indstillinger for deres medarbejdere. Få flere oplysninger</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Følg denne vejledning for at deaktivere softwaren midlertidigt, så du kan komme på nettet. Du skal have administratorrettigheder.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klik på <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, og søg efter og vælg <ph name="BEGIN_BOLD" />"Vis lokale tjenester"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Vælg <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Under <ph name="BEGIN_BOLD" />Starttype<ph name="END_BOLD" /> skal du vælge <ph name="BEGIN_BOLD" />Deaktiveret<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Under <ph name="BEGIN_BOLD" />Tjenestestatus<ph name="END_BOLD" /> skal du klikke på <ph name="BEGIN_BOLD" />Stop<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Klik på <ph name="BEGIN_BOLD" />Anvend<ph name="END_BOLD" /> og derefter på <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Besøg <ph name="BEGIN_LEARN_MORE_LINK" />Hjælp til Chrome<ph name="END_LEARN_MORE_LINK" /> for at få flere oplysninger om, hvordan du permanent fjerner softwaren fra din computer
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Din forbindelse til dette website er ikke privat. Du kan til enhver tid afslutte VR-tilstanden ved at tage headsettet af og trykke på Tilbage.</translation>
<translation id="5299298092464848405">Der opstod en fejl ved parsing af politik</translation>
<translation id="5308689395849655368">Rapportering af nedbrud er deaktiveret.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Skemavalideringsfejl på "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Denne side fra <ph name="HOST_NAME" /> blev ikke fundet</translation>
<translation id="5455374756549232013">Forkert tidsstempel for politik</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> af <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Ugyldig</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> anden}one{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> anden}other{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> andre}}</translation>
<translation id="5470861586879999274">&amp;Annuller fortryd redigering</translation>
+<translation id="5481076368049295676">Dette indhold forsøger muligvis at installere farlig software på din enhed, der kan stjæle eller slette dine oplysninger. <ph name="BEGIN_LINK" />Vis alligevel<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Tilføj gyldig adresse</translation>
<translation id="5492298309214877701">Dette website på virksomhedens, organisationens eller skolens intranet har samme webadresse som et eksternt website.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Den anmodede artikel blev ikke fundet.</translation>
<translation id="5544037170328430102">En integreret side på <ph name="SITE" /> siger:</translation>
<translation id="5556459405103347317">Genindlæs</translation>
+<translation id="5560088892362098740">Udløbsdato</translation>
<translation id="5565735124758917034">Aktiv</translation>
<translation id="5571083550517324815">Der kan ikke afhentes på denne adresse. Vælg en anden adresse.</translation>
<translation id="5572851009514199876">Start og log ind på Chrome, så Chrome kan kontrollere, om du har adgang til dette website.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Der kunne ikke indlæses indstillinger for politik</translation>
<translation id="5631439013527180824">Ugyldigt token for enhedsadministration</translation>
<translation id="5633066919399395251">Brugere med ondsindede hensigter, der i øjeblikket er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, kan forsøge at installere farlige programmer på din computer, der stjæler eller sletter dine oplysninger (f.eks. fotos, adgangskoder, beskeder og kreditkort). <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Vildledende indhold er blokeret.</translation>
<translation id="5646376287012673985">Placering</translation>
<translation id="5659593005791499971">E-mail</translation>
<translation id="5669703222995421982">Få tilpasset indhold</translation>
<translation id="5675650730144413517">Denne side virker ikke</translation>
<translation id="5710435578057952990">Dette websites identitet er ikke blevet bekræftet.</translation>
-<translation id="5713016350996637505">Der er blevet blokeret vildledende indhold</translation>
+<translation id="5719499550583120431">Forudbetalte kort accepteres.</translation>
<translation id="5720705177508910913">Aktuel bruger</translation>
<translation id="5732392974455271431">Dine forældre kan fjerne blokeringen for dig</translation>
<translation id="5763042198335101085">Angiv en gyldig mailadresse</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Skal <ph name="CARD_DETAIL" /> udfyldes?</translation>
<translation id="5810442152076338065">Din forbindelse til <ph name="DOMAIN" /> er krypteret ved hjælp af en forældet krypteringspakke.</translation>
<translation id="5813119285467412249">&amp;Annuller fortryd tilføjelse</translation>
-<translation id="5814352347845180253">Du kan miste adgangen til Premium-indhold på <ph name="SITE" /> og visse andre websites.</translation>
<translation id="5838278095973806738">Du bør ikke indtaste følsomme oplysninger på dette website (f.eks. adgangskoder eller kreditkortoplysninger), da de kan blive stjålet af hackere.</translation>
<translation id="5869405914158311789">Der kan ikke oprettes forbindelse til dette website</translation>
<translation id="5869522115854928033">Gemte adgangskoder</translation>
<translation id="5872918882028971132">Forslag fra forældre</translation>
+<translation id="5893752035575986141">Kreditkort accepteres.</translation>
<translation id="5901630391730855834">Gul</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="5926846154125914413">Du kan miste adgangen til Premium-indhold på visse websites.</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="5967867314010545767">Fjern fra historik</translation>
<translation id="5975083100439434680">Zoom ud</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Luk</translation>
<translation id="6042308850641462728">Mere</translation>
<translation id="6047233362582046994">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 apps fjernes.</translation>
+<translation id="6047927260846328439">Dette indhold forsøger muligvis at narre dig til at installere software eller afsløre personlige oplysninger. <ph name="BEGIN_LINK" />Vis alligevel<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Du kan ikke besøge <ph name="SITE" /> lige nu, da websitet bruger certifikatlåsning. Netværksfejl og angreb er normalt midlertidige, så siden vil sandsynligvis fungere igen senere.</translation>
<translation id="6060685159320643512">Vær forsigtig. Disse eksperimenter kan være farlige</translation>
<translation id="6080696365213338172">Du har opnår adgang til indhold vha. et administratorcertifikat. De data, du angiver til <ph name="DOMAIN" />, kan indhentes af din administrator.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Flere oplysninger</translation>
<translation id="6169916984152623906">Nu kan du gå på nettet privat, og andre brugere på denne enhed kan ikke se din aktivitet. Downloads og bogmærker gemmes dog stadig.</translation>
<translation id="6177128806592000436">Din forbindelse til dette website er ikke sikker.</translation>
-<translation id="6184817833369986695">(kohorte: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Kontrollér din internetforbindelse</translation>
<translation id="6218753634732582820">Vil du fjerne adressen fra Chromium?</translation>
<translation id="6221345481584921695">Google Beskyttet browsing <ph name="BEGIN_LINK" />registrerede malware<ph name="END_LINK" /> på <ph name="SITE" /> for nylig. Websites, der normalt er sikre, inficeres undertiden med malware. Det skadelige indhold kommer fra <ph name="SUBRESOURCE_HOST" />, som er en kendt malwaredistributør.</translation>
@@ -641,13 +664,14 @@
<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">Filtrer 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>
<translation id="6387478394221739770">Er du interesseret i smarte nye Chrome-funktioner? Prøv vores betakanal på chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium løb tør for hukommelse, da websiden skulle vises.</translation>
+<translation id="6397451950548600259">Der er software på computeren, som forhindrer, at Chrome kan oprette en sikker forbindelse til nettet</translation>
<translation id="6404511346730675251">Rediger bogmærke</translation>
<translation id="6410264514553301377">Indtast udløbsdatoen og kontrolkoden for <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Du har spurgt en af dine forældre, om det er i orden, at du besøger dette website.</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Enhedspolitikker</translation>
<translation id="6477321094435799029">Chrome registrerede usædvanlig kode på denne side og blokerede den for at beskytte dine personlige oplysninger (f.eks. adgangskoder, telefonnumre eller kreditkort).</translation>
<translation id="6489534406876378309">Start upload af nedbrud</translation>
+<translation id="6507833130742554667">Betalingskort accepteres.</translation>
<translation id="6508722015517270189">Genstart Chrome</translation>
<translation id="6529602333819889595">&amp;Annuller fortryd slet</translation>
<translation id="6534179046333460208">Forslag til Fysisk web</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Dette indhold forsøger muligvis at installere farlig software på din enhed, der kan stjæle eller slette dine oplysninger. <ph name="BEGIN_LINK" />Vis alligevel<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Krypteringsmuligheder</translation>
<translation id="662080504995468778">Bliv her</translation>
<translation id="6626291197371920147">Tilføj gyldigt kortnummer</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> -søgning</translation>
<translation id="6630809736994426279">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 Mac, som 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="6644283850729428850">Denne politik er forældet.</translation>
+<translation id="6657585470893396449">Adgangskode</translation>
<translation id="6671697161687535275">Vil du fjerne formularforslag fra Chromium?</translation>
<translation id="6685834062052613830">Log ud, og fuldfør konfigurationen</translation>
<translation id="6710213216561001401">Forrige</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Distrikt</translation>
<translation id="6973656660372572881">Både faste proxyservere og en webadresse for .pac-script angives.</translation>
<translation id="6989763994942163495">Vis avancerede indstillinger...</translation>
-<translation id="7000990526846637657">Der blev ikke fundet nogen poster i historikken</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Din Google-konto kan have andre former for browserhistorik på <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />.</translation>
<translation id="7029809446516969842">Adgangskoder</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Der kan ikke sendes til denne adresse. Vælg en anden adresse.</translation>
<translation id="7138472120740807366">Leveringsmetode</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> anden}one{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> anden}other{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> andre}}</translation>
<translation id="7155487117670177674">Betaling er ikke sikkert</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> anden}one{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> anden}other{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> andre}}</translation>
<translation id="7179921470347911571">Genstart nu</translation>
<translation id="7180611975245234373">Opdater</translation>
<translation id="7182878459783632708">Ingen politikker er indstillet</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Download</translation>
<translation id="7518003948725431193">Ingen webside fundet på webadressen: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Din forbindelse til dette website er ikke privat</translation>
<translation id="7535087603100972091">Værdi</translation>
<translation id="7537536606612762813">Obligatorisk</translation>
<translation id="7542403920425041731">Når du har bekræftet, deles dine kortoplysninger med dette website.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Prøv de følgende tips:</translation>
<translation id="7554791636758816595">Ny fane</translation>
<translation id="7567204685887185387">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da sikkerhedscertifikatet er udstedt på ulovlig vis. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> anden}one{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> anden}other{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> andre}}</translation>
<translation id="7568593326407688803">Denne side er på<ph name="ORIGINAL_LANGUAGE" />Vil du oversætte den?</translation>
<translation id="7569952961197462199">Vil du fjerne kreditkortet fra Chrome?</translation>
<translation id="7569983096843329377">Sort</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certifikat</translation>
<translation id="7716147886133743102">Blokeret af din administrator</translation>
<translation id="7716424297397655342">Dette website kan ikke indlæses fra cachen</translation>
+<translation id="774634243536837715">Farligt indhold er blokeret.</translation>
<translation id="7752995774971033316">Administreres ikke</translation>
<translation id="7755287808199759310">Din forælder kan fjerne blokeringen for dig</translation>
<translation id="7758069387465995638">Firewall- eller antivirussoftware kan have blokeret forbindelsen.</translation>
+<translation id="7759163816903619567">Vist domæne:</translation>
<translation id="7761701407923456692">Serverens certifikat passer ikke til webadressen.</translation>
<translation id="7763386264682878361">Værktøj til parsing af betalingsmanifester</translation>
<translation id="7764225426217299476">Tilføj adresse</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Der blev fundet <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> for "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">Du er dog ikke usynlig. Inkognitotilstand skjuler ikke din browserhistorik over for din arbejdsgiver, din internetudbyder eller de websites, du besøger.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Betalingskort accepteres.</translation>
<translation id="7887683347370398519">Kontrollér, om din kontrolkode er korrekt, og prøv igen.</translation>
<translation id="79338296614623784">Angiv et gyldigt telefonnummer</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Send feedback</translation>
<translation id="8041940743680923270">Brug global standard (spørg)</translation>
<translation id="8088680233425245692">Artiklen kunne ikke vises.</translation>
-<translation id="8089520772729574115">mindre end 1 MB</translation>
<translation id="8091372947890762290">Aktivering afventer serveren</translation>
<translation id="8118489163946903409">Betalingsmetode</translation>
<translation id="8131740175452115882">Bekræft</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Fortryd flytning</translation>
<translation id="8201077131113104583">Ugyldig webadresse til opdatering for udvidelse med id'et "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Ordreoversigt</translation>
+<translation id="8205463626947051446">Websitet har tendens til at vise påtrængende annoncer</translation>
<translation id="8218327578424803826">Tildelt placering:</translation>
<translation id="8225771182978767009">Den person, der har konfigureret denne computer, har valgt at blokere dette website.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Åbn siden på en ny inkognitofane</translation>
<translation id="8241707690549784388">Siden, du søger, benyttede oplysninger, du har indtastet. Vender du tilbage til denne side kan det betyde, at enhver handling, du har foretaget, skal gentages. Vil du fortsætte?</translation>
+<translation id="8241712895048303527">Bloker på dette website</translation>
<translation id="8249320324621329438">Sidste hentet:</translation>
<translation id="8253091569723639551">Faktureringsadresse skal angives</translation>
<translation id="8261506727792406068">Slet</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Oversættelsen mislykkedes på grund af problemer med netværksforbindelsen.</translation>
<translation id="8332188693563227489">Adgangen til <ph name="HOST_NAME" /> blev nægtet</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="8344669043927012510">Åbn side i inkognitotilstand (⇧⌘N)</translation>
<translation id="8349305172487531364">Bogmærkelinje</translation>
<translation id="8363502534493474904">Deaktivere flytilstand</translation>
<translation id="8364627913115013041">Ikke angivet.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Nedbrudsrapporten blev uploadet <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Nedbrud (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Du skal angive den samme adgangssætning to gange.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Indstillinger</translation>
<translation id="8433057134996913067">Dette logger dig ud af de fleste websites.</translation>
<translation id="8437238597147034694">&amp;Fortryd flytning</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Hvis du vil bruge kort fra din Google-konto, skal du logge ind i Chrome</translation>
<translation id="8488350697529856933">Gælder for</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> var for lang tid om at svare.</translation>
-<translation id="8532105204136943229">Udløbsår</translation>
+<translation id="8503813439785031346">Brugernavn</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="8571890674111243710">Oversætter siden til <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Tilføj tlf.nr.
@@ -887,6 +919,7 @@
<translation id="859285277496340001">Certifikatet angiver ikke en mekanisme, der kontrollerer, om den har været tilbagekaldt.</translation>
<translation id="8620436878122366504">Dine forældre har ikke godkendt det endnu</translation>
<translation id="8647750283161643317">Nulstil alle til standard</translation>
+<translation id="8660471606262461360">Fra Google Payments</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="8725066075913043281">Forsøg igen</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Zoom ind</translation>
<translation id="8931333241327730545">Vil du gemme dette kort på din Google-konto?</translation>
<translation id="8932102934695377596">Dit ur er bagud</translation>
+<translation id="8938939909778640821">Accepterede betalingskort</translation>
<translation id="8971063699422889582">Serverens certifikat er udløbet.</translation>
<translation id="8986494364107987395">Send automatisk brugsstatistikker og nedbrudsrapporter til Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Bloker altid på dette website</translation>
<translation id="9170848237812810038">&amp;Fortryd</translation>
<translation id="917450738466192189">Serverens certifikat er ugyldigt.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> anden}one{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> anden}other{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> andre}}</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. Indtast den for at starte synkroniseringen.</translation>
<translation id="9207861905230894330">Artiklen kunne ikke tilføjes.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">RYD FORMULAREN</translation>
<translation id="939736085109172342">Ny mappe</translation>
-<translation id="941721044073577244">Det ser ud til, at du ikke har tilladelse til at se dette website</translation>
<translation id="969892804517981540">Officiel version</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Ingen}=1{1 element}one{# element}other{# elementer}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Udviklerversion</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_de.xtb b/chromium/components/strings/components_strings_de.xtb
index b570217645e..8e46b1e4735 100644
--- a/chromium/components/strings/components_strings_de.xtb
+++ b/chromium/components/strings/components_strings_de.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Andere Apps schließen</translation>
<translation id="1055184225775184556">&amp;Hinzufügen rückgängig machen</translation>
<translation id="10614374240317010">Nie speichern für…</translation>
+<translation id="1066396345355680611">Eventuell verlieren Sie den Zugriff auf geschützte Inhalte von <ph name="SITE" /> und einigen anderen Websites.</translation>
<translation id="106701514854093668">Desktop-Lesezeichen</translation>
<translation id="1074497978438210769">Nicht sicher</translation>
<translation id="1080116354587839789">An Breite anpassen</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Leseliste</translation>
<translation id="1264126396475825575">Absturzbericht erfasst am <ph name="CRASH_TIME" />, wurde noch nicht hochgeladen oder wurde ignoriert</translation>
<translation id="1281526147609854549">Ausgestellt von <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Gefährliche Inhalte blockiert</translation>
<translation id="1285320974508926690">Diese Website nie übersetzen</translation>
<translation id="129553762522093515">Kürzlich geschlossen</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Löschen Sie Ihre Cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Anmeldedomain:</translation>
<translation id="1340482604681802745">Abholadresse</translation>
-<translation id="1344211575059133124">Offenbar benötigst du eine Berechtigung, um diese Website zu besuchen</translation>
<translation id="1344588688991793829">AutoFill-Einstellungen für Chromium...</translation>
<translation id="1348198688976932919">Die Website, die Sie aufrufen möchten, enthält gefährliche Apps</translation>
<translation id="1374468813861204354">Vorschläge</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Die Identität von <ph name="ORGANIZATION" /> bei <ph name="LOCALITY" /> wurde von <ph name="ISSUER" /> verifiziert.</translation>
<translation id="1426410128494586442">Ja</translation>
<translation id="1430915738399379752">Drucken</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> weitere}other{<ph name="PAYMENT_METHOD_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> weitere}}</translation>
<translation id="1506687042165942984">Zeigt eine gespeicherte (veraltete) Kopie dieser Seite an</translation>
<translation id="1517433312004943670">Telefonnummer erforderlich</translation>
+<translation id="1517500485252541695">Akzeptierte Kredit- und Debitkarten</translation>
<translation id="1519264250979466059">Build-Datum</translation>
+<translation id="1527263332363067270">Warten auf Verbindung…</translation>
<translation id="153384715582417236">Das ist im Moment alles</translation>
<translation id="1549470594296187301">Für diese Funktion muss JavaScript aktiviert sein.</translation>
<translation id="1555130319947370107">Blau</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Seite später herunterladen</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Pflichtfeld</translation>
<translation id="187918866476621466">"Beim Start"-Seiten öffnen</translation>
<translation id="1883255238294161206">Liste ausblenden</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> weitere}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> weitere}}</translation>
<translation id="1898423065542865115">Filtern</translation>
+<translation id="1916770123977586577">Laden Sie diese Seite neu, um die aktualisierten Einstellungen für diese Website zu übernehmen</translation>
+<translation id="1919345977826869612">Werbung</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Keine}=1{1 Website}other{# Websites}}</translation>
<translation id="194030505837763158">Besuchen Sie die Seite <ph name="LINK" />.</translation>
+<translation id="1948773908305951926">Akzeptierte Prepaidkarten</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" />-Lesezeichen</translation>
<translation id="1973335181906896915">Fehler bei der Serialisierung</translation>
<translation id="1974060860693918893">Erweitert</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP-Fehler</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2282872951544483773">Nicht verfügbare Experimente</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> Element}other{<ph name="ITEM_COUNT" /> Elemente}}</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>
<translation id="2337852623177822836">Einstellung wird von Ihrem Administrator gesteuert</translation>
<translation id="2354001756790975382">Weitere Lesezeichen</translation>
<translation id="2354430244986887761">Google Safe Browsing hat kürzlich <ph name="BEGIN_LINK" />schädliche Apps<ph name="END_LINK" /> auf der Website <ph name="SITE" /> gefunden.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Weiter</translation>
<translation id="2365563543831475020">Absturzbericht erfasst am <ph name="CRASH_TIME" />, wurde nicht hochgeladen</translation>
<translation id="2367567093518048410">Ebene</translation>
-<translation id="237718015863234333">Keine Alternativen für die Benutzeroberfläche verfügbar</translation>
<translation id="2384307209577226199">Standardeinstellung durch Unternehmen</translation>
<translation id="2386255080630008482">Das Serverzertifikat wurde aufgehoben.</translation>
<translation id="2392959068659972793">Richtlinien ohne Wert zeigen</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Wenn Sie diese Option auswählen, speichert Chromium eine Kopie Ihrer Karte auf diesem Gerät, damit Formulare schneller ausgefüllt werden können.</translation>
<translation id="2498091847651709837">Neue Karte scannen</translation>
<translation id="2501278716633472235">Zurück</translation>
+<translation id="2503184589641749290">Akzeptierte Debit- und Prepaidkarten</translation>
<translation id="2515629240566999685">Signal an Ihrem Standort prüfen</translation>
-<translation id="2516305470678292029">Alternativen für die Benutzeroberfläche</translation>
<translation id="2539524384386349900">Erkennen</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> hat eine ungültige Antwort gesendet.</translation>
<translation id="2556876185419854533">&amp;Bearbeiten rückgängig machen</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Versandart</translation>
<translation id="277499241957683684">Fehlender Gerätedatensatz</translation>
<translation id="2784949926578158345">Verbindung wurde zurückgesetzt.</translation>
+<translation id="2788784517760473862">Akzeptierte Kreditkarten</translation>
<translation id="2794233252405721443">Website blockiert</translation>
<translation id="2799020568854403057">Die Website, die Sie aufrufen möchten, enthält schädliche Apps</translation>
<translation id="2803306138276472711">Google Safe Browsing hat kürzlich <ph name="BEGIN_LINK" />Malware<ph name="END_LINK" /> auf <ph name="SITE" /> gefunden. Websites, die in der Regel sicher sind, können gelegentlich mit Malware infiziert sein.</translation>
<translation id="2824775600643448204">Adress- und Suchleiste</translation>
<translation id="2826760142808435982">Die Verbindung ist mit <ph name="CIPHER" /> verschlüsselt und authentifiziert und verwendet <ph name="KX" /> als Mechanismus für den Schlüsselaustausch.</translation>
<translation id="2835170189407361413">Formular leeren</translation>
+<translation id="2851634818064021665">Sie benötigen eine Berechtigung, um auf diese Website zuzugreifen</translation>
<translation id="2856444702002559011">Hacker könnten versuchen, Ihre Daten von <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> zu stehen, zum Beispiel Passwörter, Nachrichten oder Kreditkartendaten. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Nicht neu laden</translation>
-<translation id="2900469785430194048">Beim Versuch, diese Webseite aufzurufen, hat Google Chrome das Arbeitsspeicherlimit erreicht.</translation>
<translation id="2909946352844186028">Eine Netzwerkänderung ist aufgetreten.</translation>
<translation id="2916038427272391327">Andere Programme schließen</translation>
<translation id="2922350208395188000">Das Serverzertifikat kann nicht überprüft werden.</translation>
<translation id="2928905813689894207">Rechnungsadresse</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> weitere}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> weitere}}</translation>
<translation id="2941952326391522266">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat stammt von <ph name="DOMAIN2" />. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Wenn Sie diese Option auswählen, speichert Chromium eine Kopie Ihrer Karte auf diesem Gerät, damit Formulare schneller ausgefüllt werden können.</translation>
<translation id="2985398929374701810">Gültige Adresse eingeben</translation>
<translation id="2986368408720340940">Diese Abholoption ist nicht verfügbar. Bitte wählen Sie eine andere Option aus.</translation>
@@ -265,7 +269,7 @@
<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="3093245981617870298">Sie sind offline.</translation>
+<translation id="3093245981617870298">Sie sind offline</translation>
<translation id="3105172416063519923">Geräte-ID:</translation>
<translation id="3109728660330352905">Sie sind nicht zum Aufrufen dieser Seite autorisiert.</translation>
<translation id="3120730422813725195">Elo</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Seiten, die Sie sich auf Inkognito-Tabs ansehen, werden nach dem Schließen aller Inkognito-Tabs nicht in Ihrem Browserverlauf, Cookiespeicher oder Suchverlauf gespeichert. Ihre Lesezeichen und heruntergeladenen Dateien bleiben erhalten.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Insel</translation>
-<translation id="317583078218509884">Neue Einstellungen zu Websiteberechtigungen werden nach dem Aktualisieren der Seite wirksam.</translation>
<translation id="3176929007561373547">Vergewissern Sie sich, dass der Proxyserver funktioniert. Überprüfen Sie die
Proxyeinstellungen oder wenden Sie sich an Ihren Netzwerkadministrator.
Falls Sie keinen Proxyserver verwenden möchten, deaktivieren Sie ihn wie
folgt: <ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Seite im Inkognitomodus öffnen</translation>
<translation id="320323717674993345">Zahlung abbrechen</translation>
<translation id="3207960819495026254">Mit einem Lesezeichen versehen</translation>
<translation id="3225919329040284222">Der Server hat ein Zertifikat übermittelt, das nicht mit den integrierten Erwartungen übereinstimmt. Diese Erwartungen sind zu Ihrem Schutz in bestimmten Websites mit hohen Sicherheitsstandards enthalten.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Neu anordnen rückgängig machen</translation>
<translation id="3282497668470633863">Angabe für "Name auf der Karte" hinzufügen</translation>
<translation id="3286538390144397061">Jetzt neu starten</translation>
+<translation id="3287510313208355388">Herunterladen, sobald eine Internetverbindung besteht</translation>
<translation id="3303855915957856445">Keine Suchergebnisse gefunden</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Abrufintervall:</translation>
<translation id="3462200631372590220">Erweiterte Informationen ausblenden</translation>
<translation id="3467763166455606212">Name des Karteninhabers erforderlich</translation>
-<translation id="3478058380795961209">Ablaufmonat</translation>
<translation id="3479539252931486093">Geschah dies unerwartet? <ph name="BEGIN_LINK" />Informieren Sie uns<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Jetzt nicht</translation>
<translation id="3498215018399854026">Wir können deinen Vater bzw. deine Mutter momentan nicht erreichen. Bitte versuche es später erneut.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Passen Sie Datum und Uhrzeit im Abschnitt &lt;strong&gt;Allgemein&lt;/strong&gt; der App &lt;strong&gt;Einstellungen&lt;/strong&gt; an.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kredit- und Prepaidkarten werden akzeptiert.</translation>
<translation id="3582930987043644930">Namen hinzufügen</translation>
<translation id="3583757800736429874">&amp;Verschieben wiederholen</translation>
<translation id="3586931643579894722">Details ausblenden</translation>
@@ -357,10 +360,10 @@
<translation id="3655670868607891010">Sollte Ihnen diese Meldung häufiger angezeigt werden, sehen Sie sich unsere <ph name="HELP_LINK" /> an.</translation>
<translation id="3658742229777143148">Überarbeitung</translation>
<translation id="3678029195006412963">Anfrage konnte nicht signiert werden</translation>
+<translation id="3678529606614285348">Seite in einem neuen Inkognitofenster öffnen (Strg + Shift + N)</translation>
<translation id="3679803492151881375">Absturz am <ph name="CRASH_TIME" /> erfasst und am <ph name="UPLOAD_TIME" /> hochgeladen</translation>
<translation id="3681007416295224113">Zertifikatinformationen</translation>
<translation id="3690164694835360974">Log-in nicht sicher</translation>
-<translation id="3693415264595406141">Passwort:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Wird geladen...</translation>
<translation id="3712624925041724820">Lizenzen aufgebraucht</translation>
@@ -372,6 +375,7 @@
<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="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>
<translation id="3778403066972421603">Möchten Sie diese Karte in Ihrem Google-Konto und auf diesem Gerät speichern?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Ablaufdatum: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -384,8 +388,10 @@
<translation id="3886446263141354045">Deine Anfrage zum Zugriff auf diese Website wurde an <ph name="NAME" /> gesendet</translation>
<translation id="3890664840433101773">E-Mail-Adresse hinzufügen</translation>
<translation id="3901925938762663762">Die Karte ist abgelaufen.</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> – <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID des hochgeladenen Absturzberichts: <ph name="CRASH_ID" /> (lokale Absturz-ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat gibt keine alternativen Namen an. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation>
+<translation id="3949601375789751990">Hier wird Ihr Browserverlauf angezeigt</translation>
<translation id="3963721102035795474">Lesemodus</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Keine}=1{Von 1 Website }other{Von # Websites }}</translation>
<translation id="397105322502079400">Wird berechnet...</translation>
@@ -414,6 +420,8 @@
<translation id="4165986682804962316">Website-Einstellungen</translation>
<translation id="4169947484918424451">Möchten Sie, dass Chromium diese Karte speichert?</translation>
<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="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>
@@ -437,12 +445,12 @@
<translation id="4356973930735388585">Unbefugte Dritte auf dieser Website versuchen unter Umständen, gefährliche Programme auf Ihrem Computer zu installieren, um Ihre Daten zu stehlen oder zu löschen, zum Beispiel Fotos, Passwörter, Nachrichten und Kreditkartendaten.</translation>
<translation id="4372948949327679948">Erwarteter <ph name="VALUE_TYPE" />-Wert</translation>
<translation id="4377125064752653719">Sie haben versucht, auf <ph name="DOMAIN" /> zuzugreifen, das vom Server übermittelte Zertifikat wurde jedoch vom entsprechenden Aussteller widerrufen. Das bedeutet, dass die vom Server übermittelten Sicherheitsinformationen nicht vertrauenswürdig sind. Möglicherweise kommunizieren Sie mit einem Hacker.</translation>
-<translation id="4381091992796011497">Nutzername:</translation>
<translation id="4394049700291259645">Deaktivieren</translation>
<translation id="4406896451731180161">Suchergebnisse</translation>
<translation id="4424024547088906515">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wird von Chrome als nicht vertrauenswürdig eingestuft. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> hat Ihr Anmeldezertifikat nicht akzeptiert oder es wurde keines bereitgestellt.</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="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>
@@ -454,12 +462,14 @@
<translation id="4587425331216688090">Adresse aus Chrome entfernen?</translation>
<translation id="4592951414987517459">Ihre Verbindung zu <ph name="DOMAIN" /> ist mit einer modernen Codier-Suite verschlüsselt.</translation>
<translation id="4594403342090139922">&amp;Löschen rückgängig machen</translation>
+<translation id="4611292653554630842">Anmelden</translation>
<translation id="4619615317237390068">Tabs von anderen Geräten</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat enthält Fehler. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation>
<translation id="4690462567478992370">Ungültiges Zertifikat nicht mehr verwenden</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Ihre Verbindung wurde unterbrochen</translation>
+<translation id="471880041731876836">Sie sind nicht berechtigt, auf diese Website zuzugreifen</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows-Netzwerkdiagnose ausführen<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Richtlinien neu laden</translation>
<translation id="4728558894243024398">Plattform</translation>
@@ -481,14 +491,15 @@
<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="4863764087567530506">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>
<translation id="4880827082731008257">Im Verlauf suchen</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Authentifizierung erforderlich</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{und 1 weitere Webseite}other{und # weitere Webseiten}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Diese Seite wurde von einer unbekannten Sprache in <ph name="LANGUAGE_LANGUAGE" /> übersetzt.</translation>
<translation id="4923459931733593730">Zahlung</translation>
<translation id="4926049483395192435">Angabe erforderlich</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Aktionen</translation>
<translation id="4958444002117714549">Liste einblenden</translation>
<translation id="4974590756084640048">Warnmeldungen wieder aktivieren</translation>
@@ -504,6 +515,7 @@
<translation id="5045550434625856497">Falsches Passwort</translation>
<translation id="5056549851600133418">Artikel für Sie</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>
<translation id="5087580092889165836">Karte hinzufügen</translation>
<translation id="5089810972385038852">Bundesstaat/-land</translation>
@@ -511,18 +523,27 @@
<translation id="5095208057601539847">Provinz</translation>
<translation id="5115563688576182185">(64-Bit)</translation>
<translation id="5141240743006678641">Synchronisierte Passwörter mit Ihren Google-Anmeldeinformationen verschlüsseln</translation>
-<translation id="514421653919133810">Seite im Inkognitomodus öffnen (Strg + Shift + N)</translation>
<translation id="5145883236150621069">Fehlercode in der Richtlinienantwort</translation>
+<translation id="5159010409087891077">Seite in einem neuen Inkognitofenster öffnen (⇧⌘N)</translation>
<translation id="5171045022955879922">Suchen oder URL eingeben</translation>
<translation id="5172758083709347301">Computer</translation>
<translation id="5179510805599951267">Nicht auf <ph name="ORIGINAL_LANGUAGE" />? Diesen Fehler melden</translation>
-<translation id="5181140330217080051">Download wird ausgeführt...</translation>
<translation id="5190835502935405962">Lesezeichenleiste</translation>
<translation id="5199729219167945352">Experimente</translation>
<translation id="5205222826937269299">Name erforderlich</translation>
<translation id="5222812217790122047">E-Mail-Adresse erforderlich</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Nutzen Sie Chrome bei der Arbeit? Unternehmen können Chrome-Einstellungen für ihre Mitarbeiter verwalten. Weitere Informationen</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Führen Sie die folgenden Schritte aus, um die Software vorübergehend zu deaktivieren und dann auf das Internet zuzugreifen. Zum Deaktivieren der Software benötigen Sie Administratorrechte.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klicken Sie auf <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, suchen Sie nach <ph name="BEGIN_BOLD" />Lokale Dienste anzeigen<ph name="END_BOLD" /> und wählen Sie die Option aus.
+ <ph name="LIST_ITEM" />Wählen Sie <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" /> aus.
+ <ph name="LIST_ITEM" />Wählen Sie unter <ph name="BEGIN_BOLD" />Starttyp<ph name="END_BOLD" /> die Option <ph name="BEGIN_BOLD" />Deaktiviert<ph name="END_BOLD" /> aus.
+ <ph name="LIST_ITEM" />Klicken Sie unter <ph name="BEGIN_BOLD" />Dienststatus<ph name="END_BOLD" /> auf <ph name="BEGIN_BOLD" />Beenden<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Klicken Sie auf <ph name="BEGIN_BOLD" />Übernehmen<ph name="END_BOLD" /> und dann auf <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />In der <ph name="BEGIN_LEARN_MORE_LINK" />Chrome-Hilfe<ph name="END_LEARN_MORE_LINK" /> finden Sie eine Anleitung, wie Sie die Software endgültig von Ihrem Computer entfernen.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Ihre Verbindung zu dieser Website ist nicht sicher. Sie können den VR-Mode jederzeit verlassen, indem Sie das Headset abnehmen und auf "Zurück" klicken.</translation>
<translation id="5299298092464848405">Fehler beim Parsen der Richtlinie</translation>
<translation id="5308689395849655368">Die Absturzberichtsfunktion ist deaktiviert.</translation>
@@ -539,9 +560,10 @@
<translation id="5439770059721715174">Schemavalidierungsfehler in "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Diese <ph name="HOST_NAME" />-Seite wurde nicht gefunden</translation>
<translation id="5455374756549232013">Zeitstempel der Richtlinie ist fehlerhaft.</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> von <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Ungültig</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> weiterer}other{<ph name="CONTACT_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> weitere}}</translation>
<translation id="5470861586879999274">&amp;Bearbeiten wiederholen</translation>
+<translation id="5481076368049295676">Diese Inhalte versuchen möglicherweise, gefährliche Software auf Ihrem Gerät zu installieren, durch die Ihre Informationen gestohlen oder gelöscht werden. <ph name="BEGIN_LINK" />Trotzdem anzeigen<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Gültige Adresse hinzufügen</translation>
<translation id="5492298309214877701">Diese Website im Intranet des Unternehmens, der Organisation oder der Schule hat die gleiche URL wie eine externe Website.
<ph name="LINE_BREAK" />
@@ -553,6 +575,7 @@
<translation id="5540224163453853">Der gewünschte Artikel wurde nicht gefunden.</translation>
<translation id="5544037170328430102">Auf einer in <ph name="SITE" /> eingebetteten Seite wird Folgendes angezeigt:</translation>
<translation id="5556459405103347317">Neu laden</translation>
+<translation id="5560088892362098740">Ablaufdatum</translation>
<translation id="5565735124758917034">Aktiv</translation>
<translation id="5571083550517324815">Diese Abholadresse wird nicht unterstützt. Bitte wählen Sie eine andere Adresse aus.</translation>
<translation id="5572851009514199876">Melden Sie sich zuerst in Chrome an, damit überprüft werden kann, ob Sie auf diese Website zugreifen dürfen.</translation>
@@ -567,12 +590,13 @@
<translation id="5629630648637658800">Fehler beim Laden der Richtlinieneinstellungen</translation>
<translation id="5631439013527180824">Ungültiges Management-Token für das Gerät</translation>
<translation id="5633066919399395251">Zurzeit auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> befindliche Hacker könnten versuchen, gefährliche Programme auf 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="5646376287012673985">Standort</translation>
+<translation id="563324245173044180">Betrügerische Inhalte blockiert.</translation>
+<translation id="5646376287012673985">Speicherort</translation>
<translation id="5659593005791499971">E-Mail-Adresse</translation>
<translation id="5669703222995421982">Personalisierte Inhalte erhalten</translation>
<translation id="5675650730144413517">Diese Seite funktioniert nicht</translation>
<translation id="5710435578057952990">Die Identität dieser Website wurde nicht verifiziert.</translation>
-<translation id="5713016350996637505">Betrügerische Inhalte blockiert</translation>
+<translation id="5719499550583120431">Prepaidkarten werden akzeptiert.</translation>
<translation id="5720705177508910913">Aktueller Nutzer</translation>
<translation id="5732392974455271431">Deine Eltern können die Blockierung aufheben</translation>
<translation id="5763042198335101085">Geben Sie eine gültige E-Mail-Adresse ein</translation>
@@ -584,15 +608,14 @@
<translation id="5803412860119678065">Möchten Sie die Daten Ihrer <ph name="CARD_DETAIL" /> eingeben?</translation>
<translation id="5810442152076338065">Ihre Verbindung zu <ph name="DOMAIN" /> ist mit einer veralteten Codier-Suite verschlüsselt.</translation>
<translation id="5813119285467412249">&amp;Hinzufügen wiederholen</translation>
-<translation id="5814352347845180253">Eventuell verlieren Sie den Zugriff auf Premiuminhalte von <ph name="SITE" /> und einigen anderen Websites.</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="5869405914158311789">Diese Website ist nicht erreichbar</translation>
<translation id="5869522115854928033">Gespeicherte Passwörter</translation>
<translation id="5872918882028971132">Vorschläge für Eltern</translation>
+<translation id="5893752035575986141">Kreditkarten werden akzeptiert.</translation>
<translation id="5901630391730855834">Gelb</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="5926846154125914413">Eventuell verlieren Sie den Zugriff auf Premiuminhalte von einigen Websites.</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="5967867314010545767">Aus Verlauf entfernen</translation>
<translation id="5975083100439434680">Verkleinern</translation>
@@ -608,6 +631,7 @@
<translation id="6040143037577758943">Schließen</translation>
<translation id="6042308850641462728">Mehr</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>
<translation id="6051221802930200923">Sie können <ph name="SITE" /> zurzeit nicht aufrufen, weil die Website das Zertifikats-Pinning nutzt. Netzwerkfehler und Angriffe sind in der Regel nur vorübergehend, sodass die Seite wahrscheinlich später wieder funktioniert.</translation>
<translation id="6060685159320643512">Vorsichtig, diese Experimente können gefährlich sein!</translation>
<translation id="6080696365213338172">Sie haben über ein vom Administrator bereitgestelltes Zertifikat auf Inhalte zugegriffen. Die Daten, die Sie innerhalb von <ph name="DOMAIN" /> bereitstellen, können von Ihrem Administrator abgefangen werden.</translation>
@@ -621,7 +645,6 @@
<translation id="6165508094623778733">Weitere Informationen</translation>
<translation id="6169916984152623906">Sie können jetzt privat surfen. Für andere Personen, die dieses Gerät nutzen, sind Ihre Aktivitäten nicht sichtbar. Ihre Downloads und Lesezeichen werden jedoch gespeichert.</translation>
<translation id="6177128806592000436">Die Verbindung zu dieser Website ist nicht sicher</translation>
-<translation id="6184817833369986695">(Kohorte: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Bitte überprüfen Sie Ihre Internetverbindung.</translation>
<translation id="6218753634732582820">Adresse aus Chromium entfernen?</translation>
<translation id="6221345481584921695">Google Safe Browsing hat kürzlich <ph name="BEGIN_LINK" />Malware<ph name="END_LINK" /> auf <ph name="SITE" /> gefunden. Websites, die in der Regel sicher sind, können gelegentlich mit Malware infiziert sein. Der schädliche Inhalt stammt von <ph name="SUBRESOURCE_HOST" />, einem bekannten Verteiler von Malware.</translation>
@@ -638,15 +661,16 @@
<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 Netzwerkvervollständigung.</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>
<translation id="6387478394221739770">Interessiert an coolen neuen Chrome-Funktionen? Testen Sie unsere Betaversion unter chrome.com/beta.</translation>
-<translation id="6389758589412724634">Beim Versuch, diese Webseite aufzurufen, hat Chromium das Arbeitsspeicherlimit erreicht.</translation>
+<translation id="6397451950548600259">Software auf Ihrem Computer verhindert, dass Chrome eine sichere Internetverbindung herstellt</translation>
<translation id="6404511346730675251">Lesezeichen bearbeiten</translation>
<translation id="6410264514553301377">Ablaufdatum und CVC für <ph name="CREDIT_CARD" /> eingeben</translation>
<translation id="6414888972213066896">Du hast ein Elternteil gefragt, ob du diese Website besuchen darfst</translation>
@@ -661,6 +685,7 @@
<translation id="647261751007945333">Geräterichtlinien</translation>
<translation id="6477321094435799029">Chrome hat auf dieser Seite ungewöhnlichen Code erfasst und diese Seite daher blockiert, um Ihre personenbezogenen Daten wie Passwörter, Telefonnummern oder Kreditkarteninformationen zu schützen.</translation>
<translation id="6489534406876378309">Hochladen von Abstürzen starten</translation>
+<translation id="6507833130742554667">Kredit- und Debitkarten werden akzeptiert.</translation>
<translation id="6508722015517270189">Chrome neu starten</translation>
<translation id="6529602333819889595">&amp;Löschen wiederholen</translation>
<translation id="6534179046333460208">Physical Web-Vorschläge</translation>
@@ -669,13 +694,13 @@
<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="657639383826808334">Diese Inhalte versuchen möglicherweise, gefährliche Software auf Ihrem Gerät zu installieren, durch die Ihre Informationen gestohlen oder gelöscht werden. <ph name="BEGIN_LINK" />Trotzdem anzeigen<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">Verschlüsselungsoptionen</translation>
<translation id="662080504995468778">Bleiben</translation>
<translation id="6626291197371920147">Gültige Kartennummer hinzufügen</translation>
<translation id="6628463337424475685"><ph name="ENGINE" />-Suche</translation>
<translation id="6630809736994426279">Zurzeit auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> befindliche Hacker könnten versuchen, gefährliche Programme auf Ihrem Mac 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="6644283850729428850">Diese Richtlinie ist veraltet.</translation>
+<translation id="6657585470893396449">Passwort</translation>
<translation id="6671697161687535275">Vorschlag für das Formular aus Chromium entfernen?</translation>
<translation id="6685834062052613830">Abmelden und Einrichtung abschließen</translation>
<translation id="6710213216561001401">Zurück</translation>
@@ -706,7 +731,6 @@
<translation id="6970216967273061347">Bezirk</translation>
<translation id="6973656660372572881">Sowohl feste Proxyserver als auch eine PAC-Skript-URL sind festgelegt.</translation>
<translation id="6989763994942163495">Erweiterte Einstellungen anzeigen</translation>
-<translation id="7000990526846637657">Keine Verlaufseinträge gefunden</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Möglicherweise sind in Ihrem Google-Konto noch andere Formen des Browserverlaufs unter <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> vorhanden</translation>
<translation id="7029809446516969842">Passwörter</translation>
@@ -721,7 +745,9 @@
<translation id="7129409597930077180">Der Versand an diese Adresse ist nicht möglich. Bitte wählen Sie eine andere Adresse aus.</translation>
<translation id="7138472120740807366">Lieferoption</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> weitere}other{<ph name="PAYMENT_METHOD_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> weitere}}</translation>
<translation id="7155487117670177674">Zahlung nicht sicher</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> weitere}other{<ph name="SHIPPING_OPTION_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> weitere}}</translation>
<translation id="7179921470347911571">Jetzt neu starten</translation>
<translation id="7180611975245234373">Aktualisieren</translation>
<translation id="7182878459783632708">Keine Richtlinien festgelegt</translation>
@@ -762,6 +788,7 @@
<translation id="7514365320538308">Herunterladen</translation>
<translation id="7518003948725431193">Für folgende Webadresse wurde keine Webseite gefunden: <ph name="URL" />.</translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Die Verbindung zu dieser Website ist nicht sicher</translation>
<translation id="7535087603100972091">Wert</translation>
<translation id="7537536606612762813">Verbindlich</translation>
<translation id="7542403920425041731">Nach erfolgter Bestätigung werden die Kartendetails an diese Website weitergegeben.</translation>
@@ -771,7 +798,6 @@
<translation id="7552846755917812628">Probieren Sie folgende Tipps aus:</translation>
<translation id="7554791636758816595">Neuer Tab</translation>
<translation id="7567204685887185387">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wurde möglicherweise in betrügerischer Absicht ausgegeben. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> weiterer}other{<ph name="CONTACT_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> weitere}}</translation>
<translation id="7568593326407688803">Diese Seite ist auf<ph name="ORIGINAL_LANGUAGE" />Soll sie übersetzt werden?</translation>
<translation id="7569952961197462199">Kreditkarte aus Chrome entfernen?</translation>
<translation id="7569983096843329377">Schwarz</translation>
@@ -798,9 +824,11 @@
<translation id="7714464543167945231">Zertifikat</translation>
<translation id="7716147886133743102">Von Ihrem Administrator blockiert</translation>
<translation id="7716424297397655342">Diese Website kann nicht aus dem Cache geladen werden</translation>
+<translation id="774634243536837715">Gefährliche Inhalte blockiert.</translation>
<translation id="7752995774971033316">Nicht verwaltet</translation>
<translation id="7755287808199759310">Deine Eltern können die Blockierung aufheben</translation>
<translation id="7758069387465995638">Möglicherweise wurde die Verbindung von einer Firewall oder Antivirensoftware blockiert.</translation>
+<translation id="7759163816903619567">Anzeigebereich</translation>
<translation id="7761701407923456692">Das Serverzertifikat stimmt nicht mit der URL überein.</translation>
<translation id="7763386264682878361">Zahlungsmanifest-Parser</translation>
<translation id="7764225426217299476">Adresse hinzufügen</translation>
@@ -815,6 +843,7 @@
<translation id="7815407501681723534"><ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> für "<ph name="SEARCH_STRING" />" gefunden</translation>
<translation id="785549533363645510">Sie sind jedoch nicht unsichtbar. Der Inkognitomodus verhindert nicht, dass Informationen zu Ihren Webaktivitäten von Ihrem Arbeitgeber, Ihrem Internetanbieter oder den von Ihnen besuchten Websites erfasst werden.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Debit- und Prepaidkarten werden akzeptiert.</translation>
<translation id="7887683347370398519">Prüfen Sie Ihren CVC und versuchen Sie es dann erneut.</translation>
<translation id="79338296614623784">Geben Sie eine gültige Telefonnummer ein</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -834,7 +863,6 @@
<translation id="8041089156583427627">Feedback geben</translation>
<translation id="8041940743680923270">Globalen Standard verwenden (Fragen)</translation>
<translation id="8088680233425245692">Der Artikel kann nicht angezeigt werden.</translation>
-<translation id="8089520772729574115">weniger als 1 MB</translation>
<translation id="8091372947890762290">Aktivierung auf dem Server steht noch aus.</translation>
<translation id="8118489163946903409">Zahlungsmethode</translation>
<translation id="8131740175452115882">Bestätigen</translation>
@@ -846,10 +874,13 @@
<translation id="8194797478851900357">&amp;Verschieben rückgängig machen</translation>
<translation id="8201077131113104583">Ungültige Update-URL für Erweiterung mit der ID "<ph name="EXTENSION_ID" />"</translation>
<translation id="8202097416529803614">Zusammenfassung der Bestellung</translation>
+<translation id="8205463626947051446">Website zeigt für gewöhnlich aufdringliche Werbeanzeigen an</translation>
<translation id="8218327578424803826">Zugewiesener Standort:</translation>
<translation id="8225771182978767009">Die Person, die diesen Computer eingerichtet hat, hat diese Website gesperrt.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Seite in einem neuen Inkognito-Tab öffnen</translation>
<translation id="8241707690549784388">Die gesuchte Seite hat die von Ihnen eingegebenen Informationen verwendet bzw. verarbeitet. Wenn Sie zu dieser Seite zurückgehen, wird möglicherweise eine bereits ausgeführte Aktion wiederholt. Möchten Sie fortfahren?</translation>
+<translation id="8241712895048303527">Auf dieser Website blockieren</translation>
<translation id="8249320324621329438">Letzter Abruf:</translation>
<translation id="8253091569723639551">Rechnungsadresse ist erforderlich</translation>
<translation id="8261506727792406068">Löschen</translation>
@@ -860,7 +891,6 @@
<translation id="8308427013383895095">Die Übersetzung ist aufgrund eines Problems mit der Netzwerkverbindung fehlgeschlagen.</translation>
<translation id="8332188693563227489">Der Zugriff auf <ph name="HOST_NAME" /> wurde verweigert</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="8344669043927012510">Seite im Inkognitomodus öffnen (⇧⌘N)</translation>
<translation id="8349305172487531364">Lesezeichenleiste</translation>
<translation id="8363502534493474904">Flugmodus ausschalten</translation>
<translation id="8364627913115013041">Nicht eingerichtet</translation>
@@ -870,6 +900,7 @@
<translation id="8398259832188219207">Absturzbericht hochgeladen am <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Abstürze (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Sie müssen zweimal dieselbe Passphrase eingeben.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Einstellungen</translation>
<translation id="8433057134996913067">Dadurch werden Sie von den meisten Websites abgemeldet.</translation>
<translation id="8437238597147034694">&amp;Verschieben rückgängig machen</translation>
@@ -877,8 +908,9 @@
<translation id="8483780878231876732">Melden Sie sich in Chrome an, um in Ihrem Google-Konto gespeicherte Kreditkarten zu verwenden</translation>
<translation id="8488350697529856933">Gilt für</translation>
<translation id="8498891568109133222">Die Antwort von <ph name="HOST_NAME" /> hat zu lange gedauert.</translation>
-<translation id="8532105204136943229">Ablaufjahr</translation>
+<translation id="8503813439785031346">Nutzername</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="8571890674111243710">Seite wird in folgende Sprache übersetzt: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Weitere Nummer
@@ -886,6 +918,7 @@
<translation id="859285277496340001">In dem Zertifikat ist kein Mechanismus angegeben, mit dem geprüft werden kann, ob es zurückgerufen wurde.</translation>
<translation id="8620436878122366504">Deine Eltern haben die Berechtigung noch nicht erteilt</translation>
<translation id="8647750283161643317">Alle auf Standardeinstellung zurücksetzen</translation>
+<translation id="8660471606262461360">Aus Google Payments</translation>
<translation id="8703575177326907206">Die Verbindung zu <ph name="DOMAIN" /> ist nicht verschlüsselt.</translation>
<translation id="8718314106902482036">Zahlung nicht abgeschlossen</translation>
<translation id="8725066075913043281">Erneut versuchen</translation>
@@ -913,10 +946,11 @@
<translation id="8903921497873541725">Vergrößern</translation>
<translation id="8931333241327730545">Möchten Sie diese Karte in Ihrem Google-Konto speichern?</translation>
<translation id="8932102934695377596">Ihre Uhr geht nach.</translation>
+<translation id="8938939909778640821">Akzeptierte Kredit- und Prepaidkarten</translation>
<translation id="8971063699422889582">Das Serverzertifikat ist abgelaufen.</translation>
<translation id="8986494364107987395">Nutzungsstatistiken und Absturzberichte automatisch an Google senden</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
-<translation id="8996941253935762404">Die Website, die Sie aufrufen möchten, enthält schädliche Programme.</translation>
+<translation id="8996941253935762404">Die Website, die Sie aufrufen möchten, enthält schädliche Programme</translation>
<translation id="8997023839087525404">Der Server präsentierte ein Zertifikat, das nicht gemäß der Richtlinie zur Zertifikatstransparenz öffentlich offengelegt wurde. Dies ist für einige Zertifikate jedoch eine Voraussetzung, mit der sichergestellt wird, dass sie vertrauenswürdig sind und vor Angriffen schützen.</translation>
<translation id="9001074447101275817">Für den Proxy <ph name="DOMAIN" /> sind ein Nutzername und ein Passwort erforderlich.</translation>
<translation id="9005998258318286617">Fehler beim Laden des PDF-Dokuments.</translation>
@@ -924,7 +958,8 @@
<translation id="9020200922353704812">Rechnungsadresse für Kreditkarte erforderlich</translation>
<translation id="9020542370529661692">Die Seite wurde übersetzt und liegt nun auf <ph name="TARGET_LANGUAGE" /> vor.</translation>
<translation id="9035022520814077154">Sicherheitsfehler</translation>
-<translation id="9038649477754266430">Vervollständigungsfunktion zum schnelleren Laden von Seiten verwenden</translation>
+<translation id="9038649477754266430">Vorhersagefunktion zum schnelleren Laden von Seiten verwenden
+</translation>
<translation id="9039213469156557790">Außerdem enthält diese Seite andere, nicht sichere Ressourcen. Diese Ressourcen können während der Übertragung von anderen Nutzern angezeigt und von Angreifern bearbeitet werden, die das Verhalten der Seite verändern.</translation>
<translation id="9049981332609050619">Sie haben versucht, auf <ph name="DOMAIN" /> zuzugreifen, der Server hat sich jedoch mit einem ungültigen Zertifikat ausgewiesen.</translation>
<translation id="9050666287014529139">Passphrase</translation>
@@ -942,7 +977,6 @@
<translation id="9169664750068251925">Auf dieser Website immer blockieren</translation>
<translation id="9170848237812810038">&amp;Rückgängig</translation>
<translation id="917450738466192189">Das Serverzertifikat ist ungültig.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> weitere}other{<ph name="SHIPPING_OPTION_PREVIEW" /> und <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> weitere}}</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>
<translation id="9207861905230894330">Der Artikel konnte nicht hinzugefügt werden.</translation>
@@ -951,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">Formular leeren</translation>
<translation id="939736085109172342">Neuer Ordner</translation>
-<translation id="941721044073577244">Du bist offenbar nicht berechtigt, auf diese Website zuzugreifen</translation>
<translation id="969892804517981540">Offizieller Build</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Keine}=1{1 Eintrag}other{# Einträge}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Entwickler-Build</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_el.xtb b/chromium/components/strings/components_strings_el.xtb
index 882bde64fd6..93aebe3b316 100644
--- a/chromium/components/strings/components_strings_el.xtb
+++ b/chromium/components/strings/components_strings_el.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Κλείστε τις άλλες εφαρμογές</translation>
<translation id="1055184225775184556">&amp;Αναίρεση προσθήκης</translation>
<translation id="10614374240317010">Δεν έχει αποθηκευθεί ποτέ</translation>
+<translation id="1066396345355680611">Ενδέχεται να χάσετε την πρόσβαση σε προστατευμένο περιεχόμενο από τον ιστότοπο <ph name="SITE" /> και ορισμένους άλλους ιστοτόπους.</translation>
<translation id="106701514854093668">Σελιδοδείκτες επιτραπέζιου υπολογιστή</translation>
<translation id="1074497978438210769">Μη ασφαλής</translation>
<translation id="1080116354587839789">Προσαρμογή στο πλάτος</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Λίστα ανάγνωσης</translation>
<translation id="1264126396475825575">Καταγράφηκαν αναφορές σφαλμάτων <ph name="CRASH_TIME" /> (δεν έχουν ακόμη μεταφορτωθεί ή παραβλεφθεί)</translation>
<translation id="1281526147609854549">Εκδόθηκε από <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Το επικίνδυνο περιεχόμενο αποκλείστηκε</translation>
<translation id="1285320974508926690">Να μην γίνεται ποτέ μετάφραση αυτού του ιστότοπου</translation>
<translation id="129553762522093515">Έκλεισαν πρόσφατα</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Δοκιμάστε να διαγράψετε τα cookie σας<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Τομέας εγγραφής:</translation>
<translation id="1340482604681802745">Διεύθυνση παραλαβής</translation>
-<translation id="1344211575059133124">Φαίνεται πως χρειάζεστε άδεια για να επισκεφτείτε αυτόν τον ιστότοπο</translation>
<translation id="1344588688991793829">Ρυθμίσεις αυτόματης συμπλήρωσης Chromium…</translation>
<translation id="1348198688976932919">Ο ιστότοπος που πρόκειται να επισκεφτείτε περιέχει επικίνδυνες εφαρμογές</translation>
<translation id="1374468813861204354">προτάσεις</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Η ταυτότητα του <ph name="ORGANIZATION" /> στο <ph name="LOCALITY" /> επαληθεύτηκε από <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ναι</translation>
<translation id="1430915738399379752">Εκτύπωση</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ακόμη}other{<ph name="PAYMENT_METHOD_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ακόμη}}</translation>
<translation id="1506687042165942984">Εμφάνιση ενός αποθηκευμένου αντιγράφου (π.χ. επιβεβαιωμένες μη ενημερωμένες εκδόσεις) αυτής της σελίδας.</translation>
<translation id="1517433312004943670">Απαιτείται αριθμός τηλεφώνου</translation>
+<translation id="1517500485252541695">Αποδεκτές πιστωτικές και χρεωστικές κάρτες</translation>
<translation id="1519264250979466059">Ημερομηνία κατασκευής</translation>
+<translation id="1527263332363067270">Αναμονή για σύνδεση…</translation>
<translation id="153384715582417236">Αυτά προς το παρόν</translation>
<translation id="1549470594296187301">Θα πρέπει να ενεργοποιηθεί η JavaScript για τη χρήση αυτής της λειτουργίας.</translation>
<translation id="1555130319947370107">Μπλε</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Προσπαθήστε να επικοινωνήσετε με το διαχειριστή συστήματος.</translation>
<translation id="1740951997222943430">Εισαγάγετε έναν έγκυρο μήνα λήξης</translation>
-<translation id="1745358365027406341">Λήψη σελίδας αργότερα</translation>
<translation id="17513872634828108">Ανοικτές καρτέλες</translation>
<translation id="1753706481035618306">Αριθμός σελίδας</translation>
<translation id="1763864636252898013">Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν θεωρείται έμπιστο από το λειτουργικό σύστημα της συσκευής σας. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Απαιτούμενο πεδίο</translation>
<translation id="187918866476621466">Άνοιγμα σελίδων εκκίνησης</translation>
<translation id="1883255238294161206">Σύμπτυξη λίστας</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ακόμη}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ακόμη}}</translation>
<translation id="1898423065542865115">Φιλτράρισμα</translation>
+<translation id="1916770123977586577">Προκειμένου να εφαρμοστούν οι ενημερωμένες ρυθμίσεις σας σε αυτόν τον ιστότοπο, επαναλάβετε τη φόρτωση αυτής της σελίδες</translation>
+<translation id="1919345977826869612">Διαφημίσεις</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Κανένας}=1{1 ιστότοπος}other{# ιστότοποι}}</translation>
<translation id="194030505837763158">Μετάβαση στο σύνδεσμο <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Αποδεκτές προπληρωμένες κάρτες</translation>
<translation id="1962204205936693436">Σελιδοδείκτες <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Σφάλμα σειριοποίησης</translation>
<translation id="1974060860693918893">Σύνθετες</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Σφάλμα HTTP</translation>
<translation id="2270484714375784793">Αριθμός τηλεφώνου</translation>
<translation id="2282872951544483773">Μη διαθέσιμα πειράματα</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> στοιχείο}other{<ph name="ITEM_COUNT" /> στοιχεία}}</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>
<translation id="2337852623177822836">Η ρύθμιση ελέγχεται από τον διαχειριστή σας</translation>
<translation id="2354001756790975382">Άλλοι σελιδοδείκτες</translation>
<translation id="2354430244986887761">Η Ασφαλής περιήγηση Google πρόσφατα <ph name="BEGIN_LINK" />εντόπισε επιβλαβείς εφαρμογές<ph name="END_LINK" /> στον ιστότοπο <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Συνέχεια</translation>
<translation id="2365563543831475020">Οι αναφορές σφαλμάτων που καταγράφηκαν <ph name="CRASH_TIME" /> δεν έχουν μεταφορτωθεί</translation>
<translation id="2367567093518048410">Επίπεδο</translation>
-<translation id="237718015863234333">Δεν διατίθενται εναλλακτικές διεπαφές</translation>
<translation id="2384307209577226199">Προεπιλογή επιχείρησης</translation>
<translation id="2386255080630008482">Το πιστοποιητικό του διακομιστή ανακλήθηκε.</translation>
<translation id="2392959068659972793">Εμφάνιση πολιτικών χωρίς τιμή που να έχει οριστεί.</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Εάν επιλεγεί, το Chromium θα αποθηκεύσει ένα αντίγραφο της κάρτας σας σε αυτήν τη συσκευή για ταχύτερη συμπλήρωση φορμών.</translation>
<translation id="2498091847651709837">Σάρωση νέας κάρτας</translation>
<translation id="2501278716633472235">Επιστροφή</translation>
+<translation id="2503184589641749290">Αποδεκτές χρεωστικές και προπληρωμένες κάρτες</translation>
<translation id="2515629240566999685">Ελέγξτε το σήμα στην περιοχή σας</translation>
-<translation id="2516305470678292029">Εναλλακτικές διεπαφές</translation>
<translation id="2539524384386349900">Αναγνώριση</translation>
<translation id="255002559098805027">Ο κεντρικός υπολογιστής <ph name="HOST_NAME" /> έστειλε μια μη έγκυρη απόκριση.</translation>
<translation id="2556876185419854533">&amp;Αναίρεση επεξεργασίας</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Τρόπος αποστολής</translation>
<translation id="277499241957683684">Λείπει κάποιο αρχείο συσκευής</translation>
<translation id="2784949926578158345">Έγινε επαναφορά της σύνδεσης.</translation>
+<translation id="2788784517760473862">Αποδεκτές πιστωτικές κάρτες</translation>
<translation id="2794233252405721443">Ο ιστότοπος έχει αποκλειστεί</translation>
<translation id="2799020568854403057">Ο ιστότοπος που πρόκειται να επισκεφτείτε περιέχει επιβλαβείς εφαρμογές</translation>
<translation id="2803306138276472711">Πρόσφατα η Ασφαλής περιήγηση Google <ph name="BEGIN_LINK" />εντόπισε κακόβουλο λογισμικό<ph name="END_LINK" /> στον ιστότοπο <ph name="SITE" />. Οι ιστότοποι που είναι ασφαλείς υπό φυσιολογικές συνθήκες μερικές φορές προσβάλλονται από κακόβουλα λογισμικά.</translation>
<translation id="2824775600643448204">Γραμμή διευθύνσεων και αναζήτησης</translation>
<translation id="2826760142808435982">Η κρυπτογράφηση και ο έλεγχος ταυτότητας της σύνδεσης γίνονται με <ph name="CIPHER" /> και χρησιμοποιεί το <ph name="KX" /> ως μηχανισμό ανταλλαγής κλειδιών.</translation>
<translation id="2835170189407361413">Διαγραφή φόρμας</translation>
+<translation id="2851634818064021665">Απαιτείται άδεια για να επισκεφτείτε αυτόν τον ιστότοπο</translation>
<translation id="2856444702002559011">Οι εισβολείς ενδέχεται να προσπαθήσουν να υποκλέψουν τα στοιχεία σας από τον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (για παράδειγμα, κωδικούς πρόσβασης, μηνύματα ή πιστωτικές κάρτες). <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε περισσότερα<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Να μην γίνει επανάληψη φόρτωσης</translation>
-<translation id="2900469785430194048">Η διαθέσιμη μνήμη του Google Chrome εξαντλήθηκε, κατά την προσπάθεια προβολής αυτής της ιστοσελίδας.</translation>
<translation id="2909946352844186028">Εντοπίστηκε μια αλλαγή δικτύου.</translation>
<translation id="2916038427272391327">Κλείστε τα άλλα προγράμματα</translation>
<translation id="2922350208395188000">Δεν είναι δυνατός ο έλεγχος του πιστοποιητικού του διακομιστή.</translation>
<translation id="2928905813689894207">Διεύθυνση χρέωσης</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ακόμη}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ακόμη}}</translation>
<translation id="2941952326391522266">Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του είναι από το <ph name="DOMAIN2" />. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
<translation id="2948083400971632585">Μπορείτε να απενεργοποιήσετε τυχόν διακομιστές μεσολάβησης που έχουν διαμορφωθεί για μια σύνδεση από τη σελίδα ρυθμίσεων.</translation>
<translation id="2955913368246107853">Κλείσιμο γραμμής εύρεσης</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Εάν ενεργοποιηθεί, το Chromium θα αποθηκεύσει ένα αντίγραφο της κάρτας σας σε αυτήν τη συσκευή για ταχύτερη συμπλήρωση φορμών.</translation>
<translation id="2985398929374701810">Εισαγάγετε μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου</translation>
<translation id="2986368408720340940">Ο τρόπος παραλαβής δεν είναι διαθέσιμος. Δοκιμάστε έναν άλλο τρόπο.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Οι σελίδες που προβάλλετε στις καρτέλες της ανώνυμης περιήγησης δεν διατηρούνται στο ιστορικό του προγράμματος περιήγησης, στα cookie ή στο ιστορικό αναζήτησης, αφού κλείσετε όλες τις καρτέλες της ανώνυμης περιήγησης. Τα αρχεία που κατεβάζετε ή οι σελιδοδείκτες που δημιουργείτε θα διατηρούνται.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Νησί</translation>
-<translation id="317583078218509884">Οι νέες ρυθμίσεις αδειών ιστότοπου θα τεθούν σε ισχύ μετά την επανάληψη φόρτωσης της σελίδας.</translation>
<translation id="3176929007561373547">Ελέγξτε τις ρυθμίσεις του διακομιστή μεσολάβησης ή επικοινωνήστε με το διαχειριστή του δικτύου σας, για
να βεβαιωθείτε ότι ο διακομιστής μεσολάβησης λειτουργεί. Εάν δεν πιστεύετε ότι
απαιτείται η χρήση διακομιστή μεσολάβησης:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Ανοίξτε τη σελίδα σε κατάσταση ανώνυμης περιήγησης</translation>
<translation id="320323717674993345">Ακύρωση πληρωμής</translation>
<translation id="3207960819495026254">Προστέθηκε στους σελιδοδείκτες</translation>
<translation id="3225919329040284222">Ο διακομιστής παρουσίασε ένα πιστοποιητικό που δεν αντιστοιχεί στις ενσωματωμένες προϋποθέσεις. Αυτές οι προϋποθέσεις συμπεριλαμβάνονται σε συγκεκριμένους ιστότοπους υψηλής ασφάλειας για την προστασία σας.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Αναίρεση αναδιάταξης</translation>
<translation id="3282497668470633863">Προσθήκη ονόματος στην κάρτα</translation>
<translation id="3286538390144397061">Άμεση επανεκκίνηση</translation>
+<translation id="3287510313208355388">Να γίνει λήψη όταν πραγματοποιηθεί σύνδεση</translation>
<translation id="3303855915957856445">Δεν βρέθηκαν αποτελέσματα αναζήτησης</translation>
<translation id="3305707030755673451">Τα δεδομένα σας κρυπτογραφήθηκαν με τη δική σας φράση πρόσβασης συγχρονισμού στις <ph name="TIME" />. Πληκτρολογήστε την για να ξεκινήσει ο συγχρονισμός.</translation>
<translation id="3320021301628644560">Προσθήκη διεύθυνσης χρέωσης</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Διάστημα ανάκτησης:</translation>
<translation id="3462200631372590220">Απόκρυψη σύνθετων</translation>
<translation id="3467763166455606212">Απαιτείται το όνομα κατόχου κάρτας</translation>
-<translation id="3478058380795961209">Μήνας λήξης</translation>
<translation id="3479539252931486093">Δεν το περιμένατε; <ph name="BEGIN_LINK" />Ενημερώστε μας<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Όχι τώρα</translation>
<translation id="3498215018399854026">Δεν ήταν δυνατή η επικοινωνία με τον γονέα σας αυτήν τη στιγμή. Δοκιμάστε ξανά.</translation>
@@ -343,12 +345,13 @@
&lt;p&gt;Προσαρμόστε την ημερομηνία και την ώρα από την ενότητα &lt;strong&gt;Γενικές&lt;/strong&gt; της εφαρμογής &lt;strong&gt;Ρυθμίσεις&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε περισσότερα<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Οι πιστωτικές και προπληρωμένες κάρτες γίνονται δεκτές.</translation>
<translation id="3582930987043644930">Προσθήκη ονόματος</translation>
<translation id="3583757800736429874">&amp;Επανάληψη μετακίνησης</translation>
<translation id="3586931643579894722">Απόκρυψη λεπτομερειών</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Εισαγάγετε μια έγκυρη ημερομηνία λήξης</translation>
-<translation id="36224234498066874">Διαγραφή Δεδομένων Περιήγησης...</translation>
+<translation id="36224234498066874">Διαγραφή δεδομένων περιήγησης…</translation>
<translation id="362276910939193118">Εμφάνιση πλήρους ιστορικού</translation>
<translation id="3623476034248543066">Εμφάνιση τιμής</translation>
<translation id="3630155396527302611">Εάν έχει ήδη καταχωριστεί ως πρόγραμμα στο οποίο επιτρέπεται η πρόσβαση στο δίκτυο, δοκιμάστε
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Αν αυτό το μήνυμα εμφανίζεται συχνά, μπορείτε να βρείτε βοήθεια εδώ <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Αναθεώρηση</translation>
<translation id="3678029195006412963">Δεν ήταν δυνατή η έγκριση του αιτήματος</translation>
+<translation id="3678529606614285348">Ανοίξτε τη σελίδα σε ένα νέο παράθυρο ανώνυμης περιήγησης (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Η αναφορά σφαλμάτων καταγράφηκε στις <ph name="CRASH_TIME" /> και μεταφορτώθηκε στις <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Πληροφορίες πιστοποιητικού</translation>
<translation id="3690164694835360974">Μη ασφαλής σύνδεση</translation>
-<translation id="3693415264595406141">Κωδικός πρόσβασης:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Φόρτωση...</translation>
<translation id="3712624925041724820">Οι άδειες έχουν εξαντληθεί</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Οι εισβολείς σε αυτόν τον ιστότοπο <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="375403751935624634">Η μετάφραση απέτυχε λόγω σφάλματος διακομιστή.</translation>
<translation id="3759461132968374835">Δεν έχετε πρόσφατα αναφερθέντα σφάλματα. Τα σφάλματα που προέκυψαν όταν η αναφορά σφαλμάτων ήταν απενεργοποιημένη δεν θα εμφανιστούν εδώ.</translation>
+<translation id="3765032636089507299">Η σελίδα Ασφαλούς περιήγησης είναι υπό κατασκευή.</translation>
<translation id="3778403066972421603">Θέλετε να αποθηκεύσετε αυτήν την κάρτα στον Λογαριασμό σας Google και σε αυτήν τη συσκευή;</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Λήγει <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Το αίτημά σας για να αποκτήσετε πρόσβαση σε αυτόν τον ιστότοπο έχει σταλεί στον/η(ν) <ph name="NAME" /></translation>
<translation id="3890664840433101773">Προσθήκη διεύθυνσης ηλεκτρονικού ταχυδρομείου</translation>
<translation id="3901925938762663762">Η κάρτα έχει λήξει</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Αναγνωριστικό μεταφορτωμένης αναφοράς σφαλμάτων <ph name="CRASH_ID" /> (Αναγνωριστικό τοπικού σφάλματος: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Αυτός ο διακομιστής δεν μπορεί να αποδείξει ότι είναι ο τομέας <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας δεν προσδιορίζει Εναλλακτικά ονόματα θέματος. Αυτό μπορεί να οφείλεται σε εσφαλμένη διαμόρφωση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
+<translation id="3949601375789751990">Το ιστορικό περιήγησής σας εμφανίζεται εδώ</translation>
<translation id="3963721102035795474">Λειτουργία αναγνώστη</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Κανένα}=1{Από 1 ιστότοπο }other{Από # ιστοτόπους }}</translation>
<translation id="397105322502079400">Υπολογισμός…</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Ρυθμίσεις ιστότοπου</translation>
<translation id="4169947484918424451">Θέλετε το Chromium να αποθηκεύσει αυτήν την κάρτα;</translation>
<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="4196861286325780578">&amp;Επανάληψη μετακίνησης</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Ελέγξτε τις διαμορφώσεις του τείχους προστασίας και της προστασίας από ιούς<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Απότομες διακοπές λειτουργίας</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Οι εισβολείς σε αυτόν τον ιστότοπο μπορεί να επιχειρήσουν να εγκαταστήσουν επικίνδυνα προγράμματα στον υπολογιστή σας, τα οποία μπορούν να υποκλέψουν ή να διαγράψουν τα δεδομένα σας (για παράδειγμα, φωτογραφίες, κωδικούς πρόσβασης, μηνύματα και στοιχεία πιστωτικών καρτών).</translation>
<translation id="4372948949327679948">Αναμενόμενη τιμή <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Προσπαθήσατε να μεταβείτε στον τομέα <ph name="DOMAIN" />, όμως το πιστοποιητικό που παρουσιάστηκε από το διακομιστή ανακλήθηκε από τον εκδότη του. Αυτό σημαίνει ότι τα διαπιστευτήρια ασφαλείας που παρουσιάστηκαν από το διακομιστή δεν πρέπει σε καμία περίπτωση να θεωρηθούν αξιόπιστα. Ενδέχεται να επικοινωνείτε με κάποιον εισβολέα.</translation>
-<translation id="4381091992796011497">Όνομα χρήστη:</translation>
<translation id="4394049700291259645">Απενεργοποίηση</translation>
<translation id="4406896451731180161">αποτελέσματα αναζήτησης</translation>
<translation id="4424024547088906515">Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν θεωρείται έμπιστο από τον Chrome. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
<translation id="4432688616882109544">Ο κεντρικός υπολογιστής <ph name="HOST_NAME" /> δεν αποδέχτηκε το πιστοποιητικό σύνδεσής σας ή μπορεί να μην διατέθηκε πιστοποιητικό σύνδεσης.</translation>
<translation id="443673843213245140">Η χρήση ενός διακομιστή μεσολάβησης είναι απενεργοποιημένη, αλλά έχει καθοριστεί μια ρητή διαμόρφωση διακομιστή μεσολάβησης.</translation>
+<translation id="445100540951337728">Αποδεκτές χρεωστικές κάρτες</translation>
<translation id="4506176782989081258">Σφάλμα επικύρωσης: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Επικοινωνήστε με το διαχειριστή συστήματος</translation>
<translation id="450710068430902550">Κοινοποίηση στο διαχειριστή</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Κατάργηση διεύθυνσης από το Chrome;</translation>
<translation id="4592951414987517459">Η σύνδεσή σας στο <ph name="DOMAIN" /> κρυπτογραφείται χρησιμοποιώντας ένα σύγχρονο πρόγραμμα κρυπτογράφησης.</translation>
<translation id="4594403342090139922">&amp;Αναίρεση διαγραφής</translation>
+<translation id="4611292653554630842">Σύνδεση</translation>
<translation id="4619615317237390068">Καρτέλες από άλλες συσκευές</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Ο διακομιστής δεν κατάφερε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του περιέχει σφάλματα. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
<translation id="4690462567478992370">Διακοπή χρήσης μη έγκυρου πιστοποιητικού</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Η σύνδεσή σας διακόπηκε</translation>
+<translation id="471880041731876836">Δεν έχετε άδεια να επισκεφτείτε αυτόν τον ιστότοπο</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Να εκτελέσετε τον Διαγνωστικό έλεγχο δικτύου των Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Επανάληψη φόρτωσης πολιτικών</translation>
<translation id="4728558894243024398">Πλατφόρμα</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">Προβολή</translation>
<translation id="4854362297993841467">Αυτός ο τρόπος παράδοσης δεν είναι διαθέσιμος. Δοκιμάστε έναν άλλο τρόπο.</translation>
<translation id="4858792381671956233">Ρώτησες τους γονείς σου εάν σου επιτρέπουν να επισκεφτείς αυτόν τον ιστότοπο</translation>
-<translation id="4863764087567530506">Αυτό το περιεχόμενο μπορεί να προσπαθήσει να σας εξαπατήσει έτσι ώστε να εγκαταστήσετε λογισμικό ή να αποκαλύψετε προσωπικά στοιχεία. <ph name="BEGIN_LINK" />Εμφάνιση ούτως ή άλλως<ph name="END_LINK" />.</translation>
-<translation id="4880827082731008257">Ιστορικό αναζήτησης</translation>
+<translation id="4880827082731008257">Αναζήτηση ιστορικού</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Απαιτείται έλεγχος ταυτότητας</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{και 1 ακόμη ιστοσελίδα}other{και # ακόμη ιστοσελίδες}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Αυτή η σελίδα έχει μεταφραστεί από μια άγνωστη γλώσσας στα <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Πληρωμή</translation>
<translation id="4926049483395192435">Πρέπει να καθοριστεί.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Ενέργειες</translation>
<translation id="4958444002117714549">Ανάπτυξη λίστας</translation>
<translation id="4974590756084640048">Επανενεργοποίηση προειδοποιήσεων</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Λανθασμένος κωδικός πρόσβασης</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>
<translation id="5087580092889165836">Προσθήκη κάρτας</translation>
<translation id="5089810972385038852">Πολιτεία</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Επαρχία</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5141240743006678641">Κρυπτογραφήστε συγχρονισμένους κωδικούς πρόσβασης με τα διαπιστευτήριά σας Google.</translation>
-<translation id="514421653919133810">Ανοίξτε τη σελίδα σε κατάσταση ανώνυμης περιήγησης (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Βρέθηκε κωδικός σφάλματος στην απόκριση πολιτικής</translation>
+<translation id="5159010409087891077">Ανοίξτε τη σελίδα σε ένα νέο παράθυρο ανώνυμης περιήγησης (⇧⌘N)</translation>
<translation id="5171045022955879922">Αναζήτηση ή πληκτρολόγηση διεύθυνσης URL</translation>
<translation id="5172758083709347301">Υπολογιστής</translation>
<translation id="5179510805599951267">Δεν είναι στα <ph name="ORIGINAL_LANGUAGE" />; Αναφέρετε αυτό το σφάλμα</translation>
-<translation id="5181140330217080051">Λήψη</translation>
<translation id="5190835502935405962">Γραμμή σελιδοδεικτών</translation>
<translation id="5199729219167945352">Πειράματα</translation>
<translation id="5205222826937269299">Απαιτείται όνομα</translation>
<translation id="5222812217790122047">Απαιτείται διεύθυνση ηλεκτρονικού ταχυδρομείου</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Χρησιμοποιείτε το Chrome στη δουλειά σας; Οι επιχειρήσεις μπορούν να διαχειρίζονται τις ρυθμίσεις του Chrome για τους εργαζόμενούς τους. Μάθετε περισσότερα</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Ακολουθήστε αυτά τα βήματα, για να απενεργοποιήσετε προσωρινά το λογισμικό, προκειμένου να συνδεθείτε στον ιστό. Θα πρέπει να έχετε προνόμια προγραμματιστή.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Κάντε κλικ στο στοιχείο <ph name="BEGIN_BOLD" />Έναρξη<ph name="END_BOLD" /> και έπειτα αναζητήστε και επιλέξτε το στοιχείο <ph name="BEGIN_BOLD" />"Προβολή τοπικών υπηρεσιών"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Επιλέξτε <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Στην ενότητα <ph name="BEGIN_BOLD" />Τύπος εκκίνησης<ph name="END_BOLD" />, επιλέξτε <ph name="BEGIN_BOLD" />Απενεργοποιημένη<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Στην ενότητα <ph name="BEGIN_BOLD" />Κατάσταση υπηρεσίας<ph name="END_BOLD" />, κάντε κλικ στο στοιχείο <ph name="BEGIN_BOLD" />Διακοπή<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Κάντε κλικ στο στοιχείο <ph name="BEGIN_BOLD" />Εφαρμογή<ph name="END_BOLD" /> και έπειτα στο στοιχείο <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Επισκεφτείτε το <ph name="BEGIN_LEARN_MORE_LINK" />Κέντρο βοήθειας του Chrome<ph name="END_LEARN_MORE_LINK" />, για να μάθετε πώς μπορείτε να καταργήσετε οριστικά το λογισμικό από τον υπολογιστή σας
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Η σύνδεσή σας σε αυτόν τον ιστότοπο δεν είναι ιδιωτική. Για έξοδο από τη λειτουργία VR, ανά πάσα στιγμή, αφαιρέστε το ακουστικό και πιέστε πίσω.</translation>
<translation id="5299298092464848405">Σφάλμα ανάλυσης πολιτικής</translation>
<translation id="5308689395849655368">Η αναφορά σφαλμάτων είναι απενεργοποιημένη.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Σφάλμα επαλήθευσης σχήματος σε "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Δεν είναι δυνατός ο εντοπισμός αυτής της σελίδας του κεντρικού υπολογιστή <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Εσφαλμένη χρονική σήμανση πολιτικής</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> από <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Μη έγκυρο</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ακόμη}other{<ph name="CONTACT_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ακόμη}}</translation>
<translation id="5470861586879999274">&amp;Επανάληψη επεξεργασίας</translation>
+<translation id="5481076368049295676">Αυτό το περιεχόμενο μπορεί να προσπαθήσει να εγκαταστήσει επικίνδυνο λογισμικό στη συσκευή σας το οποίο κλέβει ή διαγράφει τα στοιχεία σας. <ph name="BEGIN_LINK" />Εμφάνιση ούτως ή άλλως<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Προσθήκη έγκυρης διεύθυνσης</translation>
<translation id="5492298309214877701">Αυτός ο ιστότοπος που βρίσκεται στο εσωτερικό δίκτυο της εταιρείας, του οργανισμού ή του σχολείου έχει το ίδιο URL με έναν εξωτερικό ιστότοπο.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Δεν ήταν δυνατή η εύρεση του άρθρου που ζητήσατε.</translation>
<translation id="5544037170328430102">Μια ενσωματωμένη σελίδα στον ιστότοπο <ph name="SITE" /> λέει:</translation>
<translation id="5556459405103347317">Επαναφόρτωση</translation>
+<translation id="5560088892362098740">Ημερομηνία λήξης</translation>
<translation id="5565735124758917034">Ενεργό</translation>
<translation id="5571083550517324815">Δεν είναι δυνατή η παραλαβή από αυτήν τη διεύθυνση. Επιλέξτε μια άλλη διεύθυνση.</translation>
<translation id="5572851009514199876">Εκκινήστε και συνδεθείτε στο Chrome, έτσι ώστε το Chrome να μπορεί να ελέγξει εάν έχετε δικαίωμα πρόσβασης σε αυτόν τον ιστότοπο.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Αποτυχία φόρτωσης ρυθμίσεων πολιτικής</translation>
<translation id="5631439013527180824">Μη έγκυρο διακριτικό διαχείρισης συσκευής</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="5646376287012673985">Τοποθεσία</translation>
<translation id="5659593005791499971">Διεύθυνση ηλεκτρονικού ταχυδρομείου</translation>
<translation id="5669703222995421982">Λήψη εξατομικευμένου περιεχομένου</translation>
<translation id="5675650730144413517">Αυτή η σελίδα δεν λειτουργεί</translation>
<translation id="5710435578057952990">Η ταυτότητα αυτού του ιστότοπου δεν έχει επαληθευτεί.</translation>
-<translation id="5713016350996637505">Το παραπλανητικό περιεχόμενο αποκλείστηκε</translation>
+<translation id="5719499550583120431">Οι προπληρωμένες κάρτες γίνονται δεκτές.</translation>
<translation id="5720705177508910913">Τρέχων χρήστης</translation>
<translation id="5732392974455271431">Οι γονείς σου μπορούν να καταργήσουν τον αποκλεισμό του</translation>
<translation id="5763042198335101085">Εισαγάγετε μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Θέλετε να συμπληρωθούν τα στοιχεία της κάρτας <ph name="CARD_DETAIL" />;</translation>
<translation id="5810442152076338065">Η σύνδεσή σας στο <ph name="DOMAIN" /> κρυπτογραφείται χρησιμοποιώντας ένα απαρχαιωμένο πρόγραμμα κρυπτογράφησης.</translation>
<translation id="5813119285467412249">&amp;Επανάληψη προσθήκης</translation>
-<translation id="5814352347845180253">Ενδέχεται να χάσετε την πρόσβαση σε προνομιακό περιεχόμενο από τον ιστότοπο <ph name="SITE" /> και ορισμένους άλλους ιστότοπους.</translation>
<translation id="5838278095973806738">Δεν θα πρέπει να εισαγάγετε ευαίσθητες πληροφορίες σε αυτόν τον ιστότοπο (για παράδειγμα, κωδικούς πρόσβασης ή πιστωτικές κάρτες), επειδή ενδέχεται να υποκλαπούν από εισβολείς.</translation>
<translation id="5869405914158311789">Δεν είναι δυνατή η πρόσβαση σε αυτόν τον ιστότοπο</translation>
<translation id="5869522115854928033">Αποθηκευμένοι κωδικοί πρόσβασης</translation>
<translation id="5872918882028971132">Γονικές προτάσεις</translation>
+<translation id="5893752035575986141">Οι πιστωτικές κάρτες γίνονται δεκτές.</translation>
<translation id="5901630391730855834">Κίτρινο</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (συγχρονισμένο)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 σε χρήση}other{# σε χρήση}}</translation>
-<translation id="5926846154125914413">Ενδέχεται να χάσετε την πρόσβαση σε προνομιακό περιεχόμενο από ορισμένους ιστότοπους.</translation>
<translation id="5959728338436674663">Αυτόματη αποστολή ορισμένων <ph name="BEGIN_WHITEPAPER_LINK" />πληροφοριών συστήματος και περιεχομένου σελίδων<ph name="END_WHITEPAPER_LINK" /> στην Google για διευκόλυνση του εντοπισμού επικίνδυνων εφαρμογών και ιστοτόπων<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">Κατάργηση από το ιστορικό</translation>
<translation id="5975083100439434680">Σμίκρυνση</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Κλείσιμο</translation>
<translation id="6042308850641462728">Περισσότερα</translation>
<translation id="6047233362582046994">Εάν κατανοείτε τους κινδύνους για την ασφάλειά σας, μπορείτε να <ph name="BEGIN_LINK" />επισκεφτείτε αυτόν τον ιστότοπο<ph name="END_LINK" /> πριν από την κατάργηση των επιβλαβών εφαρμογών.</translation>
+<translation id="6047927260846328439">Αυτό το περιεχόμενο μπορεί να προσπαθήσει να σας εξαπατήσει έτσι ώστε να εγκαταστήσετε λογισμικό ή να αποκαλύψετε προσωπικά στοιχεία. <ph name="BEGIN_LINK" />Εμφάνιση ούτως ή άλλως<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Δεν μπορείτε να επισκεφτείτε το <ph name="SITE" /> αυτήν τη στιγμή επειδή ο ιστότοπος χρησιμοποιεί certificate pinning (κλείδωμα πιστοποιητικών). Τα σφάλματα δικτύου και οι επιθέσεις είναι συνήθως προσωρινά, συνεπώς αυτή η σελίδα πιθανότατα θα λειτουργήσει αργότερα.</translation>
<translation id="6060685159320643512">Προσοχή, τέτοια πειράματα είναι επικύνδυνα</translation>
<translation id="6080696365213338172">Η πρόσβασή σας στο περιεχόμενο πραγματοποιήθηκε με τη χρήση ενός πιστοποιητικού που παρασχέθηκε από διαχειριστή. Τα δεδομένα που παρέχετε στο <ph name="DOMAIN" /> μπορεί να ελέγχονται από το διαχειριστή σας.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Μάθετε περισσότερα</translation>
<translation id="6169916984152623906">Στο εξής μπορείτε να περιηγηθείτε ιδιωτικά και η δραστηριότητά σας δεν θα είναι ορατή στα άλλα άτομα που χρησιμοποιούν αυτήν τη συσκευή. Ωστόσο, οι λήψεις και οι σελιδοδείκτες θα αποθηκεύονται</translation>
<translation id="6177128806592000436">Η σύνδεσή σας σε αυτόν τον ιστότοπο δεν είναι ασφαλής</translation>
-<translation id="6184817833369986695">(κοόρτη: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Ελέγξτε τη σύνδεσή σας στο Internet</translation>
<translation id="6218753634732582820">Να καταργηθεί η διεύθυνση από το Chromium;</translation>
<translation id="6221345481584921695">Πρόσφατα η Ασφαλής περιήγηση Google <ph name="BEGIN_LINK" />εντόπισε κακόβουλο λογισμικό<ph name="END_LINK" /> στον ιστότοπο <ph name="SITE" />. Οι ιστότοποι που είναι ασφαλείς υπό φυσιολογικές συνθήκες μερικές φορές προσβάλλονται από κακόβουλα λογισμικά. Το κακόβουλο περιεχόμενο προέρχεται από το <ph name="SUBRESOURCE_HOST" />, έναν γνωστό διανομέα κακόβουλου λογισμικού.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Θέλετε να ενημερώνεστε για τις συναρπαστικές νέες δυνατότητες του Chrome; Επισκεφτείτε το κανάλι beta στη διεύθυνση chrome.com/beta.</translation>
-<translation id="6389758589412724634">Η διαθέσιμη μνήμη του Chromium εξαντλήθηκε, κατά την προσπάθεια προβολής αυτής της ιστοσελίδας.</translation>
+<translation id="6397451950548600259">Κάποιο λογισμικό στον υπολογιστή σας παρεμποδίζει την ασφαλή σύνδεση του Chrome στον ιστό</translation>
<translation id="6404511346730675251">Επεξεργασία σελιδοδείκτη</translation>
<translation id="6410264514553301377">Εισαγάγετε την ημερομηνία λήξης και τον κωδικό CVC για την κάρτα <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Ρώτησες τους γονείς σου εάν σου επιτρέπουν να επισκεφτείς αυτόν τον ιστότοπο</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Πολιτικές συσκευών </translation>
<translation id="6477321094435799029">Το Chrome εντόπισε ασυνήθιστο κώδικα σε αυτήν τη σελίδα και τον απέκλεισε για να προστατεύσει τα προσωπικά σας στοιχεία (για παράδειγμα, κωδικούς πρόσβασης, αριθμούς τηλεφώνου ή πιστωτικές κάρτες).</translation>
<translation id="6489534406876378309">Έναρξη μεταφόρτωσης σφαλμάτων</translation>
+<translation id="6507833130742554667">Οι πιστωτικές και οι χρεωστικές κάρτες γίνονται δεκτές.</translation>
<translation id="6508722015517270189">Επανεκκινήστε το Chrome</translation>
<translation id="6529602333819889595">&amp;Επανάληψη διαγραφής</translation>
<translation id="6534179046333460208">Προτάσεις Φυσικού δικτύου</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Αυτό το περιεχόμενο μπορεί να προσπαθήσει να εγκαταστήσει επικίνδυνο λογισμικό στη συσκευή σας το οποίο κλέβει ή διαγράφει τα στοιχεία σας. <ph name="BEGIN_LINK" />Εμφάνιση ούτως ή άλλως<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Επιλογές κρυπτογράφησης</translation>
<translation id="662080504995468778">Παραμονή</translation>
<translation id="6626291197371920147">Προσθήκη έγκυρου αριθμού κάρτας</translation>
<translation id="6628463337424475685">Αναζήτηση <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Οι εισβολείς που βρίσκονται αυτήν τη στιγμή στον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ενδέχεται να επιχειρήσουν να εγκαταστήσουν επικίνδυνα προγράμματα στον υπολογιστή σας Mac, για να υποκλέψουν ή να διαγράψουν τα δεδομένα σας (για παράδειγμα, φωτογραφίες, κωδικούς πρόσβασης, μηνύματα και πιστωτικές κάρτες). <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε περισσότερα<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Αυτή η πολιτική έχει αποσυρθεί.</translation>
+<translation id="6657585470893396449">Κωδικός πρόσβασης</translation>
<translation id="6671697161687535275">Να καταργηθεί η πρόταση φόρμας από το Chromium;</translation>
<translation id="6685834062052613830">Αποσυνδεθείτε και ολοκληρώστε την εγκατάσταση</translation>
<translation id="6710213216561001401">Προηγούμενο</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Περιοχή</translation>
<translation id="6973656660372572881">Καθορίζονται τόσο οι σταθεροί διακομιστές μεσολάβησης όσο και μια διεύθυνση URL σεναρίου .pac.</translation>
<translation id="6989763994942163495">Εμφάνιση σύνθετων ρυθμίσεων…</translation>
-<translation id="7000990526846637657">Δεν βρέθηκαν καταχωρίσεις ιστορικού</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Ο Λογαριασμός σας Google ενδέχεται να διαθέτει άλλες μορφές ιστορικού περιήγησης στη διεύθυνση <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Κωδικοί πρόσβασης</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Δεν είναι δυνατή η αποστολή σε αυτήν τη διεύθυνση. Επιλέξτε μια άλλη διεύθυνση.</translation>
<translation id="7138472120740807366">Μέθοδος προβολής</translation>
<translation id="7139724024395191329">Εμιράτο</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ακόμη}other{<ph name="PAYMENT_METHOD_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ακόμη}}</translation>
<translation id="7155487117670177674">Μη ασφαλής πληρωμή</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ακόμη}other{<ph name="SHIPPING_OPTION_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ακόμη}}</translation>
<translation id="7179921470347911571">Επανεκκίνηση τώρα</translation>
<translation id="7180611975245234373">Ανανέωση</translation>
<translation id="7182878459783632708">Δεν έχουν οριστεί πολιτικές</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Λήψη</translation>
<translation id="7518003948725431193">Δεν βρέθηκε καμία ιστοσελίδα για τη διεύθυνση ιστού:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Η σύνδεσή σας σε αυτόν τον ιστότοπο δεν είναι ιδιωτική</translation>
<translation id="7535087603100972091">Τιμή</translation>
<translation id="7537536606612762813">Υποχρεωτική</translation>
<translation id="7542403920425041731">Μετά την επιβεβαίωση, τα στοιχεία της κάρτας θα κοινοποιηθούν σε αυτόν τον ιστότοπο.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Δοκιμάστε τις παρακάτω συμβουλές:</translation>
<translation id="7554791636758816595">Νέα καρτέλα</translation>
<translation id="7567204685887185387">Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του μπορεί να εκδόθηκε παράνομα. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ακόμη}other{<ph name="CONTACT_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ακόμη}}</translation>
<translation id="7568593326407688803">Αυτή η σελίδα είναι στα<ph name="ORIGINAL_LANGUAGE" />Θέλετε να τη μεταφράσετε;</translation>
<translation id="7569952961197462199">Κατάργηση πιστωτικής κάρτας από το Chrome;</translation>
<translation id="7569983096843329377">Μαύρο</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Πιστοποιητικό</translation>
<translation id="7716147886133743102">Αποκλείστηκε από τον διαχειριστή σας</translation>
<translation id="7716424297397655342">Δεν είναι δυνατή η φόρτωση αυτού του ιστότοπου από την κρυφή μνήμη</translation>
+<translation id="774634243536837715">Το επικίνδυνο περιεχόμενο αποκλείστηκε.</translation>
<translation id="7752995774971033316">Χωρίς διαχείριση</translation>
<translation id="7755287808199759310">Ο γονέας σας μπορεί να καταργήσει τον αποκλεισμό του για εσάς</translation>
<translation id="7758069387465995638">Το τείχος προστασίας ή το λογισμικό προστασίας από ιούς ενδέχεται να έχει αποκλείσει τη σύνδεση.</translation>
+<translation id="7759163816903619567">Τομέας προβολής:</translation>
<translation id="7761701407923456692">Το πιστοποιητικό του διακομιστή δεν συμφωνεί με τη διεύθυνση URL.</translation>
<translation id="7763386264682878361">Συντακτικός αναλυτής διακήρυξης πληρωμών</translation>
<translation id="7764225426217299476">Προσθήκη διεύθυνσης</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Βρέθηκαν <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> για τον όρο αναζήτησης "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">Ωστόσο, δεν είστε αόρατος/η. Με την κατάσταση ανώνυμης περιήγησης δεν μπορείτε να αποκρύψετε τα στοιχεία της περιήγησής σας από τους εργοδότες σας, τον πάροχο υπηρεσιών διαδικτύου που χρησιμοποιείτε ή τους ιστότοπους που επισκέπτεστε.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Οι χρεωστικές και οι προπληρωμένες κάρτες γίνονται δεκτές.</translation>
<translation id="7887683347370398519">Ελέγξτε τον κωδικό σας CVC και δοκιμάστε ξανά</translation>
<translation id="79338296614623784">Εισαγάγετε έναν έγκυρο αριθμό τηλεφώνου</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Αποστολή σχολίων</translation>
<translation id="8041940743680923270">Χρήση καθολικής προεπιλεγμένης ρύθμισης (Ερώτηση)</translation>
<translation id="8088680233425245692">Αποτυχία προβολής άρθρου.</translation>
-<translation id="8089520772729574115">λιγότερο από 1 MB</translation>
<translation id="8091372947890762290">Η ενεργοποίηση στο διακομιστή εκκρεμεί</translation>
<translation id="8118489163946903409">Τρόπος πληρωμής</translation>
<translation id="8131740175452115882">Επιβεβαίωση</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Αναίρεση μετακίνησης</translation>
<translation id="8201077131113104583">Η διεύθυνση URL ενημέρωσης για την επέκταση με αναγνωριστικό ID "<ph name="EXTENSION_ID" />", δεν είναι έγκυρη.</translation>
<translation id="8202097416529803614">Σύνοψη παραγγελίας</translation>
+<translation id="8205463626947051446">Ο ιστότοπος τείνει να εμφανίζει παρεμβατικές διαφημίσεις</translation>
<translation id="8218327578424803826">Εκχωρημένη τοποθεσία:</translation>
<translation id="8225771182978767009">Το άτομο που ρύθμισε αυτόν τον υπολογιστή επέλεξε να αποκλείσει αυτόν τον ιστότοπο.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Ανοίξτε τη σελίδα σε μια νέα καρτέλα ανώνυμης περιήγησης</translation>
<translation id="8241707690549784388">Στη σελίδα που αναζητάτε χρησιμοποποιήθηκαν πληροφορίες που καταχωρίσατε. Αν επιστρέψετε σε αυτή τη σελίδα ίσως επαναληφθούν ενέργειες που εκτελέσατε. Θέλετε να συνεχίσετε;</translation>
+<translation id="8241712895048303527">Αποκλεισμός σε αυτόν τον ιστότοπο</translation>
<translation id="8249320324621329438">Τελευταία ανάκτηση:</translation>
<translation id="8253091569723639551">Απαιτείται διεύθυνση χρέωσης</translation>
<translation id="8261506727792406068">Διαγραφή</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Η μετάφραση απέτυχε λόγω προβλήματος με τη σύνδεση δικτύου.</translation>
<translation id="8332188693563227489">Απορρίφθηκε η πρόσβαση στο κεντρικό υπολογιστή <ph name="HOST_NAME" /></translation>
<translation id="834457929814110454">Εάν κατανοείτε τους κινδύνους για την ασφάλειά σας, μπορείτε να <ph name="BEGIN_LINK" />επισκεφτείτε αυτόν τον ιστότοπο<ph name="END_LINK" /> πριν από την κατάργηση των επικίνδυνων προγραμμάτων.</translation>
-<translation id="8344669043927012510">Ανοίξτε τη σελίδα σε κατάσταση ανώνυμης περιήγησης (⇧⌘N)</translation>
<translation id="8349305172487531364">Γραμμή σελιδοδεικτών</translation>
<translation id="8363502534493474904">Απενεργοποιήστε τη λειτουργία πτήσης</translation>
<translation id="8364627913115013041">Δεν έχει οριστεί.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Η αναφορά σφαλμάτων μεταφορτώθηκε στις <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Σφάλματα (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Πρέπει να εισαγάγετε δύο φορές την ίδια φράση πρόσβασης.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ρυθμίσεις</translation>
<translation id="8433057134996913067">Αυτή η ενέργεια θα σας αποσυνδέσει από τους περισσότερους ιστότοπους.</translation>
<translation id="8437238597147034694">&amp;Αναίρεση μετακίνησης</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Για να χρησιμοποιήσετε κάρτες από τον Λογαριασμό σας Google, συνδεθείτε στο Chrome</translation>
<translation id="8488350697529856933">Ισχύει για</translation>
<translation id="8498891568109133222">Ο κεντρικός υπολογιστής <ph name="HOST_NAME" /> άργησε πολύ να ανταποκριθεί.</translation>
-<translation id="8532105204136943229">Έτος λήξης</translation>
+<translation id="8503813439785031346">Όνομα χρήστη</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="8571890674111243710">Μετάφραση σελίδας σε <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Προσθ. τηλεφ.
@@ -887,6 +919,7 @@
<translation id="859285277496340001">Αυτό το πιστοποιητικό δεν καθορίζει κάποιο μηχανισμό για να ελέγχεται εάν έχει ανακληθεί.</translation>
<translation id="8620436878122366504">Οι γονείς σας δεν τον έχουν εγκρίνει ακόμα</translation>
<translation id="8647750283161643317">Επαναφορά προεπιλογών</translation>
+<translation id="8660471606262461360">Από το Google Payments</translation>
<translation id="8703575177326907206">Η σύνδεσή σας με τον τομέα <ph name="DOMAIN" /> δεν είναι κρυπτογραφημένη.</translation>
<translation id="8718314106902482036">Η πληρωμή δεν ολοκληρώθηκε</translation>
<translation id="8725066075913043281">Προσπαθήστε ξανά</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Μεγέθυνση</translation>
<translation id="8931333241327730545">Θέλετε να αποθηκεύσετε αυτήν την κάρτα στο Λογαριασμό σας Google;</translation>
<translation id="8932102934695377596">Το ρολόι σας πάει πίσω</translation>
+<translation id="8938939909778640821">Αποδεκτές πιστωτικές και προπληρωμένες κάρτες</translation>
<translation id="8971063699422889582">Το πιστοποιητικό του διακομιστή έχει λήξει.</translation>
<translation id="8986494364107987395">Αυτόματη αποστολή στατιστικών στοιχείων χρήσης και αναφορών σφαλμάτων στην Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Να γίνεται πάντα αποκλεισμός σε αυτόν τον ιστότοπο</translation>
<translation id="9170848237812810038">Αναί&amp;ρεση</translation>
<translation id="917450738466192189">Το πιστοποιητικό του διακομιστή δεν είναι έγκυρο.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ακόμη}other{<ph name="SHIPPING_OPTION_PREVIEW" /> και <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ακόμη}}</translation>
<translation id="9183425211371246419">Ο κεντρικός υπολογιστής <ph name="HOST_NAME" /> χρησιμοποιεί μη υποστηριζόμενο πρωτόκολλο.</translation>
<translation id="9205078245616868884">Τα δεδομένα σας είναι κρυπτογραφημένα με τη δική σας φράση πρόσβασης συγχρονισμού. Πληκτρολογήστε την για να ξεκινήσει ο συγχρονισμός.</translation>
<translation id="9207861905230894330">Αποτυχία προσθήκης άρθρου.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ΔΙΑΓΡΑΦΗ ΦΟΡΜΑΣ</translation>
<translation id="939736085109172342">Νέος φάκελος</translation>
-<translation id="941721044073577244">Φαίνεται ότι δεν έχετε άδεια να επισκεφτείτε αυτόν τον ιστότοπο</translation>
<translation id="969892804517981540">Επίσημη έκδοση</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Κανένα}=1{1 στοιχείο}other{# στοιχεία}}</translation>
+<translation id="981121421437150478">Εκτός σύνδεσης</translation>
<translation id="988159990683914416">Έκδοση προγραμματιστή</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_en-GB.xtb b/chromium/components/strings/components_strings_en-GB.xtb
index 266e93d1968..395afc0996e 100644
--- a/chromium/components/strings/components_strings_en-GB.xtb
+++ b/chromium/components/strings/components_strings_en-GB.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Close other apps</translation>
<translation id="1055184225775184556">&amp;Undo Add</translation>
<translation id="10614374240317010">Never saved</translation>
+<translation id="1066396345355680611">You may lose access to protected content from <ph name="SITE" /> and some other sites.</translation>
<translation id="106701514854093668">Desktop Bookmarks</translation>
<translation id="1074497978438210769">Not secure</translation>
<translation id="1080116354587839789">Fit to width</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Reading list</translation>
<translation id="1264126396475825575">Crash report captured on <ph name="CRASH_TIME" /> (not yet uploaded or ignored)</translation>
<translation id="1281526147609854549">Issued by <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Dangerous content blocked</translation>
<translation id="1285320974508926690">Never translate this site</translation>
<translation id="129553762522093515">Recently closed</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Try clearing your cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Enrolment domain:</translation>
<translation id="1340482604681802745">Pickup address</translation>
-<translation id="1344211575059133124">Looks like you need permission to visit this site</translation>
<translation id="1344588688991793829">Chromium Auto-fill settings...</translation>
<translation id="1348198688976932919">The site ahead contains dangerous apps</translation>
<translation id="1374468813861204354">suggestions</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">The identity of <ph name="ORGANIZATION" /> at <ph name="LOCALITY" /> has been verified by <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Yes</translation>
<translation id="1430915738399379752">Print</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> more}other{<ph name="PAYMENT_METHOD_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> more}}</translation>
<translation id="1506687042165942984">Show a saved (i.e. known to be out of date) copy of this page.</translation>
<translation id="1517433312004943670">Phone number required</translation>
+<translation id="1517500485252541695">Accepted credit and debit cards</translation>
<translation id="1519264250979466059">Build Date</translation>
+<translation id="1527263332363067270">Waiting for connection…</translation>
<translation id="153384715582417236">That’s all for now</translation>
<translation id="1549470594296187301">JavaScript must be enabled to use this feature.</translation>
<translation id="1555130319947370107">Blue</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Download page later</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Required field</translation>
<translation id="187918866476621466">Open startup pages</translation>
<translation id="1883255238294161206">Collapse list</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> more}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> more}}</translation>
<translation id="1898423065542865115">Filtering</translation>
+<translation id="1916770123977586577">To apply your updated settings to this site, reload this page</translation>
+<translation id="1919345977826869612">Ads</translation>
<translation id="192020519938775529">{COUNT,plural, =0{None}=1{1 site}other{# sites}}</translation>
<translation id="194030505837763158">Go to <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Accepted prepaid cards</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> Bookmarks</translation>
<translation id="1973335181906896915">Serialisation error</translation>
<translation id="1974060860693918893">Advanced</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP error</translation>
<translation id="2270484714375784793">Phone number</translation>
<translation id="2282872951544483773">Unavailable Experiments</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item}other{<ph name="ITEM_COUNT" /> items}}</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>
<translation id="2337852623177822836">Setting controlled by your administrator</translation>
<translation id="2354001756790975382">Other bookmarks</translation>
<translation id="2354430244986887761">Google Safe Browsing recently <ph name="BEGIN_LINK" />found harmful apps<ph name="END_LINK" /> on <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continue</translation>
<translation id="2365563543831475020">Crash report captured on <ph name="CRASH_TIME" /> was not uploaded</translation>
<translation id="2367567093518048410">Level</translation>
-<translation id="237718015863234333">No UI alternatives available</translation>
<translation id="2384307209577226199">Enterprise default</translation>
<translation id="2386255080630008482">Server's certificate has been revoked</translation>
<translation id="2392959068659972793">Show policies with no value set</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">If ticked, Chromium will store a copy of your card on this device for faster form filling.</translation>
<translation id="2498091847651709837">Scan new card</translation>
<translation id="2501278716633472235">Go back</translation>
+<translation id="2503184589641749290">Accepted debit and prepaid cards</translation>
<translation id="2515629240566999685">Checking the signal in your area</translation>
-<translation id="2516305470678292029">UI Alternatives</translation>
<translation id="2539524384386349900">Detect</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> sent an invalid response.</translation>
<translation id="2556876185419854533">&amp;Undo Edit</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Delivery method</translation>
<translation id="277499241957683684">Missing device record</translation>
<translation id="2784949926578158345">The connection was reset.</translation>
+<translation id="2788784517760473862">Accepted credit cards</translation>
<translation id="2794233252405721443">Site blocked</translation>
<translation id="2799020568854403057">The site ahead contains harmful apps</translation>
<translation id="2803306138276472711">Google Safe Browsing recently <ph name="BEGIN_LINK" />detected malware<ph name="END_LINK" /> on <ph name="SITE" />. Websites that are normally safe are sometimes infected with malware.</translation>
<translation id="2824775600643448204">Address and search bar</translation>
<translation id="2826760142808435982">The connection is encrypted and authenticated using <ph name="CIPHER" /> and uses <ph name="KX" /> as the key exchange mechanism.</translation>
<translation id="2835170189407361413">Clear form</translation>
+<translation id="2851634818064021665">You need permission to visit this site</translation>
<translation id="2856444702002559011">Attackers might be trying to steal your information from <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (for example, passwords, messages or credit cards). <ph name="BEGIN_LEARN_MORE_LINK" />Learn more<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Don't reload</translation>
-<translation id="2900469785430194048">Google Chrome ran out of memory while trying to display this web page.</translation>
<translation id="2909946352844186028">A network change was detected.</translation>
<translation id="2916038427272391327">Close other programmes</translation>
<translation id="2922350208395188000">Server's certificate cannot be checked.</translation>
<translation id="2928905813689894207">Billing Address</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> more}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> more}}</translation>
<translation id="2941952326391522266">This server could not prove that it is <ph name="DOMAIN" />; its security certificate is from <ph name="DOMAIN2" />. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
<translation id="2948083400971632585">You can disable any proxies configured for a connection from the settings page.</translation>
<translation id="2955913368246107853">Close find bar</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">If enabled, Chromium will store a copy of your card on this device for faster form filling.</translation>
<translation id="2985398929374701810">Enter a valid address</translation>
<translation id="2986368408720340940">This pickup method isn’t available. Try a different method.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Pages that you view in incognito tabs won’t stick around in your browser’s history, cookie store or search history after you’ve closed all of your incognito tabs. Any files you download or bookmarks you create will be kept.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Island</translation>
-<translation id="317583078218509884">New site permissions settings will take effect after reloading the page.</translation>
<translation id="3176929007561373547">Check your proxy settings or contact your network administrator to
make sure that the proxy server is working. If you don't believe you should
be using a proxy server:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Open page in Incognito mode</translation>
<translation id="320323717674993345">Cancel payment</translation>
<translation id="3207960819495026254">Bookmarked</translation>
<translation id="3225919329040284222">The server presented a certificate that doesn't match built-in expectations. These expectations are included for certain, high-security websites in order to protect you.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Undo Reorder</translation>
<translation id="3282497668470633863">Add name on card</translation>
<translation id="3286538390144397061">Restart Now</translation>
+<translation id="3287510313208355388">Download when online</translation>
<translation id="3303855915957856445">No search results found</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Fetch interval:</translation>
<translation id="3462200631372590220">Hide advanced</translation>
<translation id="3467763166455606212">Cardholder name required</translation>
-<translation id="3478058380795961209">Month of Expiry</translation>
<translation id="3479539252931486093">Was this unexpected? <ph name="BEGIN_LINK" />Let us know<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Not now</translation>
<translation id="3498215018399854026">We could not reach your parent at the moment. Please try again.</translation>
@@ -343,6 +345,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Learn more<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Credit and prepaid cards are accepted.</translation>
<translation id="3582930987043644930">Add name</translation>
<translation id="3583757800736429874">&amp;Redo Move</translation>
<translation id="3586931643579894722">Hide details</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">If you're seeing this frequently, try these <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revision</translation>
<translation id="3678029195006412963">Request could not be signed</translation>
+<translation id="3678529606614285348">Open page in a new Incognito window (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Crash report captured on <ph name="CRASH_TIME" />, uploaded on <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Certificate information</translation>
<translation id="3690164694835360974">Login not secure</translation>
-<translation id="3693415264595406141">Password:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Loading...</translation>
<translation id="3712624925041724820">Licenses exhausted</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Attackers on <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> may trick you into doing something dangerous like installing software or revealing your personal information (for example, passwords, phone numbers or credit cards). <ph name="BEGIN_LEARN_MORE_LINK" />Learn more<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">The translation failed because of a server error.</translation>
<translation id="3759461132968374835">You have no recently reported crashes. Crashes that occurred when crash reporting was disabled will not appear here.</translation>
+<translation id="3765032636089507299">The Safe Browsing page is under construction.</translation>
<translation id="3778403066972421603">Do you want to save this card to your Google account and on this device?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Expires <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Your request to access this site has been sent to <ph name="NAME" /></translation>
<translation id="3890664840433101773">Add email</translation>
<translation id="3901925938762663762">The card is expired</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Uploaded Crash Report ID <ph name="CRASH_ID" /> (Local Crash ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">This server could not prove that it is <ph name="DOMAIN" />; its security certificate does not specify Subject Alternative Names. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
+<translation id="3949601375789751990">Your browsing history appears here</translation>
<translation id="3963721102035795474">Reader Mode</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{None}=1{From 1 site }other{From # sites }}</translation>
<translation id="397105322502079400">Calculating...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Site settings</translation>
<translation id="4169947484918424451">Do you want Chromium to save this card?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Attackers on this site might attempt to install dangerous programs on your computer that steal or delete your information (for example, photos, passwords, messages and credit cards).</translation>
<translation id="4372948949327679948">Expected <ph name="VALUE_TYPE" /> value.</translation>
<translation id="4377125064752653719">You attempted to reach <ph name="DOMAIN" />, but the certificate that the server presented has been revoked by its issuer. This means that the security credentials the server presented absolutely should not be trusted. You may be communicating with an attacker.</translation>
-<translation id="4381091992796011497">User Name:</translation>
<translation id="4394049700291259645">Disable</translation>
<translation id="4406896451731180161">search results</translation>
<translation id="4424024547088906515">This server could not prove that it is <ph name="DOMAIN" />; its security certificate is not trusted by Chrome. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> didn’t accept your login certificate, or one may not have been provided.</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="4506176782989081258">Validation error: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Contacting the system admin</translation>
<translation id="450710068430902550">Sharing with Administrator</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Remove address from Chrome?</translation>
<translation id="4592951414987517459">Your connection to <ph name="DOMAIN" /> is encrypted using a modern cipher suite.</translation>
<translation id="4594403342090139922">&amp;Undo Delete</translation>
+<translation id="4611292653554630842">Log in</translation>
<translation id="4619615317237390068">Tabs from other devices</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">This server could not prove that it is <ph name="DOMAIN" />; its security certificate contains errors. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
<translation id="4690462567478992370">Stop using an invalid certificate</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Your connection was interrupted</translation>
+<translation id="471880041731876836">You don't have permission to visit this site</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Running Windows Network Diagnostics<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Reload policies</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">This content might try to trick you into installing software or revealing personal information. <ph name="BEGIN_LINK" />Show anyway<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Search history</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Authentication required</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{and 1 more web page}other{and # more web pages}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">This page has been translated from an unknown language into <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Payment</translation>
<translation id="4926049483395192435">Must be specified.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Actions</translation>
<translation id="4958444002117714549">Expand list</translation>
<translation id="4974590756084640048">Re-enable warnings</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Incorrect password</translation>
<translation id="5056549851600133418">Articles for you</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Checking the proxy address<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">You may lose access to protected content from some sites.</translation>
<translation id="5087286274860437796">Server's certificate is not valid at this time.</translation>
<translation id="5087580092889165836">Add card</translation>
<translation id="5089810972385038852">County</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Province</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5141240743006678641">Encrypt synced passwords with your Google credentials</translation>
-<translation id="514421653919133810">Open page in Incognito mode (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Error code present in the policy response</translation>
+<translation id="5159010409087891077">Open page in a new Incognito window (⇧⌘N)</translation>
<translation id="5171045022955879922">Search or type URL</translation>
<translation id="5172758083709347301">Machine</translation>
<translation id="5179510805599951267">Not in <ph name="ORIGINAL_LANGUAGE" />? Report this error</translation>
-<translation id="5181140330217080051">Downloading</translation>
<translation id="5190835502935405962">Bookmarks Bar</translation>
<translation id="5199729219167945352">Experiments</translation>
<translation id="5205222826937269299">Name required</translation>
<translation id="5222812217790122047">Email (required)</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Using Chrome at work? Businesses can manage Chrome settings for their employees. Find out more</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Follow these steps to temporarily disable the software so that you can get on the web. You'll need administrator privileges.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Click <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, then search for and select <ph name="BEGIN_BOLD" />'View local services'<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Select <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Under <ph name="BEGIN_BOLD" />Startup type<ph name="END_BOLD" />, select <ph name="BEGIN_BOLD" />Disabled<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Under <ph name="BEGIN_BOLD" />Service status<ph name="END_BOLD" />, click <ph name="BEGIN_BOLD" />Stop<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Click <ph name="BEGIN_BOLD" />Apply<ph name="END_BOLD" />, then click <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Visit the <ph name="BEGIN_LEARN_MORE_LINK" />Chrome help centre<ph name="END_LEARN_MORE_LINK" /> to learn how to permanently remove the software from your computer
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Your connection to this site is not private. To exit VR mode at any time, remove headset and press back.</translation>
<translation id="5299298092464848405">Error parsing policy</translation>
<translation id="5308689395849655368">Crash reporting is disabled.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Schema validation error at "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">This <ph name="HOST_NAME" /> page can’t be found</translation>
<translation id="5455374756549232013">Bad policy timestamp</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> of <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Invalid</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> more}other{<ph name="CONTACT_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> more}}</translation>
<translation id="5470861586879999274">&amp;Redo edit</translation>
+<translation id="5481076368049295676">This content might try to install dangerous software on your device that steals or deletes your information. <ph name="BEGIN_LINK" />Show anyway<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Add valid address</translation>
<translation id="5492298309214877701">This site on the company, organisation or school intranet has the same URL as an external website.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Could not find the requested article.</translation>
<translation id="5544037170328430102">An embedded page at <ph name="SITE" /> says:</translation>
<translation id="5556459405103347317">Reload</translation>
+<translation id="5560088892362098740">Expiry Date</translation>
<translation id="5565735124758917034">Active</translation>
<translation id="5571083550517324815">Can’t pick up from this address. Select a different address.</translation>
<translation id="5572851009514199876">Please start and sign in to Chrome so that Chrome can check whether you are allowed to access this site.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Failed to load policy settings</translation>
<translation id="5631439013527180824">Invalid device management token</translation>
<translation id="5633066919399395251">Attackers currently on <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> might attempt to install dangerous programs on your computer that steal or delete your information (for example, photos, passwords, messages and credit cards). <ph name="BEGIN_LEARN_MORE_LINK" />Learn more<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Deceptive content blocked.</translation>
<translation id="5646376287012673985">Location</translation>
<translation id="5659593005791499971">Email</translation>
<translation id="5669703222995421982">Get personalised content</translation>
<translation id="5675650730144413517">This page isn’t working</translation>
<translation id="5710435578057952990">The identity of this website has not been verified.</translation>
-<translation id="5713016350996637505">Deceptive content blocked</translation>
+<translation id="5719499550583120431">Prepaid cards are accepted.</translation>
<translation id="5720705177508910913">Current user</translation>
<translation id="5732392974455271431">Your parents can unblock it for you</translation>
<translation id="5763042198335101085">Enter a valid email address</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Do you want to fill in your <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Your connection to <ph name="DOMAIN" /> is encrypted using an obsolete cipher suite.</translation>
<translation id="5813119285467412249">&amp;Redo Add</translation>
-<translation id="5814352347845180253">You may lose access to premium content from <ph name="SITE" /> and some other sites.</translation>
<translation id="5838278095973806738">You should not enter any sensitive information on this site (for example, passwords or credit cards), because it could be stolen by attackers.</translation>
<translation id="5869405914158311789">This site can’t be reached</translation>
<translation id="5869522115854928033">Saved passwords</translation>
<translation id="5872918882028971132">Parent Suggestions</translation>
+<translation id="5893752035575986141">Credit cards are accepted.</translation>
<translation id="5901630391730855834">Yellow</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="5926846154125914413">You may lose access to premium content from some sites.</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="5967867314010545767">Remove from history</translation>
<translation id="5975083100439434680">Zoom out</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Close</translation>
<translation id="6042308850641462728">More</translation>
<translation id="6047233362582046994">If you understand the risks to your security, you may <ph name="BEGIN_LINK" />visit this site<ph name="END_LINK" /> before the harmful apps have been removed.</translation>
+<translation id="6047927260846328439">This content might try to trick you into installing software or revealing personal information. <ph name="BEGIN_LINK" />Show anyway<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">You cannot visit <ph name="SITE" /> right now because the website uses certificate pinning. Network errors and attacks are usually temporary, so this page will probably work later.</translation>
<translation id="6060685159320643512">Careful, these experiments may bite</translation>
<translation id="6080696365213338172">You have accessed content using an administrator-provided certificate. Data you provide to <ph name="DOMAIN" /> can be intercepted by your administrator.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Learn more</translation>
<translation id="6169916984152623906">Now you can browse privately, and other people who use this device won’t see your activity. However, downloads and bookmarks will be saved.</translation>
<translation id="6177128806592000436">Your connection to this site is not secure</translation>
-<translation id="6184817833369986695">(cohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Check your Internet connection</translation>
<translation id="6218753634732582820">Remove address from Chromium?</translation>
<translation id="6221345481584921695">Google Safe Browsing recently <ph name="BEGIN_LINK" />detected malware<ph name="END_LINK" /> on <ph name="SITE" />. Websites that are normally safe are sometimes infected with malware. The malicious content comes from <ph name="SUBRESOURCE_HOST" />, a known malware distributor.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Interested in cool new Chrome features? Try our beta channel at chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium ran out of memory while trying to display this web page.</translation>
+<translation id="6397451950548600259">Software on your computer is stopping Chrome from safely connecting to the web</translation>
<translation id="6404511346730675251">Edit bookmark</translation>
<translation id="6410264514553301377">Enter the expiry date and CVC for <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">You asked your parent if it's OK to visit this site</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Device policies</translation>
<translation id="6477321094435799029">Chrome detected unusual code on this page and blocked it to protect your personal information (for example, passwords, phone numbers and credit cards).</translation>
<translation id="6489534406876378309">Start uploading crashes</translation>
+<translation id="6507833130742554667">Credit and debit cards are accepted.</translation>
<translation id="6508722015517270189">Restart Chrome</translation>
<translation id="6529602333819889595">&amp;Redo Delete</translation>
<translation id="6534179046333460208">Physical Web suggestions</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">This content might try to install dangerous software on your device that steals or deletes your information. <ph name="BEGIN_LINK" />Show anyway<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Encryption options</translation>
<translation id="662080504995468778">Stay</translation>
<translation id="6626291197371920147">Add valid card number</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Search</translation>
<translation id="6630809736994426279">Attackers currently on <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> might attempt to install dangerous programs on your Mac 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="6644283850729428850">This policy has been deprecated.</translation>
+<translation id="6657585470893396449">Password</translation>
<translation id="6671697161687535275">Remove form suggestion from Chromium?</translation>
<translation id="6685834062052613830">Sign out and complete setup</translation>
<translation id="6710213216561001401">Previous</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">District</translation>
<translation id="6973656660372572881">Both fixed proxy servers and a .pac script URL are specified.</translation>
<translation id="6989763994942163495">+ Show advanced settings</translation>
-<translation id="7000990526846637657">No history entries found</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Your Google account may have other forms of browsing history at <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Passwords</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Can’t deliver to this address. Select a different address.</translation>
<translation id="7138472120740807366">Delivery method</translation>
<translation id="7139724024395191329">Emirate</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> more}other{<ph name="PAYMENT_METHOD_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> more}}</translation>
<translation id="7155487117670177674">Payment not secure</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> more}other{<ph name="SHIPPING_OPTION_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> more}}</translation>
<translation id="7179921470347911571">Relaunch Now</translation>
<translation id="7180611975245234373">Refresh</translation>
<translation id="7182878459783632708">No policies set</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Download</translation>
<translation id="7518003948725431193">No web page was found for the web address: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Your connection to this site is not private</translation>
<translation id="7535087603100972091">Value</translation>
<translation id="7537536606612762813">Mandatory</translation>
<translation id="7542403920425041731">Once you confirm, your card details will be shared with this site.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Try the following tips:</translation>
<translation id="7554791636758816595">New Tab</translation>
<translation id="7567204685887185387">This server could not prove that it is <ph name="DOMAIN" />; its security certificate might have been issued fraudulently. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> more}other{<ph name="CONTACT_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> more}}</translation>
<translation id="7568593326407688803">This page is in<ph name="ORIGINAL_LANGUAGE" />Would you like to translate it?</translation>
<translation id="7569952961197462199">Remove credit card from Chrome?</translation>
<translation id="7569983096843329377">Black</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificate</translation>
<translation id="7716147886133743102">Blocked by your administrator</translation>
<translation id="7716424297397655342">This site can’t be loaded from the cache</translation>
+<translation id="774634243536837715">Dangerous content blocked.</translation>
<translation id="7752995774971033316">Unmanaged</translation>
<translation id="7755287808199759310">Your parent can unblock it for you</translation>
<translation id="7758069387465995638">Firewall or antivirus software may have blocked the connection.</translation>
+<translation id="7759163816903619567">Display domain:</translation>
<translation id="7761701407923456692">Server's certificate does not match the URL.</translation>
<translation id="7763386264682878361">Payment Manifest Parser</translation>
<translation id="7764225426217299476">Add address</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Found <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> for '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">However, you aren’t invisible. Going incognito doesn’t hide your browsing from your employer, your Internet service provider or the websites that you visit.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Debit and prepaid cards are accepted.</translation>
<translation id="7887683347370398519">Check your CVC and try again</translation>
<translation id="79338296614623784">Enter a valid phone number</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Send Feedback</translation>
<translation id="8041940743680923270">Use global default (Ask)</translation>
<translation id="8088680233425245692">Failed to view article.</translation>
-<translation id="8089520772729574115">less than 1 MB</translation>
<translation id="8091372947890762290">Activation is pending on the server</translation>
<translation id="8118489163946903409">Payment method</translation>
<translation id="8131740175452115882">Confirm</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Undo Move</translation>
<translation id="8201077131113104583">Invalid update URL for extension with ID "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Order summary</translation>
+<translation id="8205463626947051446">Site tends to show intrusive ads</translation>
<translation id="8218327578424803826">Assigned Location:</translation>
<translation id="8225771182978767009">The person who set up this computer has chosen to block this site.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Open page in a new Incognito tab</translation>
<translation id="8241707690549784388">The page that you're looking for used information that you entered. Returning to that page might cause any action that you took to be repeated. Do you want to continue?</translation>
+<translation id="8241712895048303527">Block on this site</translation>
<translation id="8249320324621329438">Last fetched:</translation>
<translation id="8253091569723639551">Billing address required</translation>
<translation id="8261506727792406068">Delete</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">The translation failed because of a problem with the network connection.</translation>
<translation id="8332188693563227489">Access to <ph name="HOST_NAME" /> was denied</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="8344669043927012510">Open page in Incognito mode (⇧⌘N)</translation>
<translation id="8349305172487531364">Bookmarks bar</translation>
<translation id="8363502534493474904">Turning off aeroplane mode</translation>
<translation id="8364627913115013041">Not set.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Crash report uploaded on <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Crashes (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">You must enter the same passphrase twice.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Settings</translation>
<translation id="8433057134996913067">This will sign you out of most websites.</translation>
<translation id="8437238597147034694">&amp;Undo move</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">To use cards from your Google account, sign in to Chrome</translation>
<translation id="8488350697529856933">Applies to</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> took too long to respond.</translation>
-<translation id="8532105204136943229">Year of Expiry</translation>
+<translation id="8503813439785031346">Username</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="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="8620436878122366504">Your parents haven't approved it yet</translation>
<translation id="8647750283161643317">Reset all to default</translation>
+<translation id="8660471606262461360">From Google Payments</translation>
<translation id="8703575177326907206">Your connection to <ph name="DOMAIN" /> is not encrypted.</translation>
<translation id="8718314106902482036">Payment not completed</translation>
<translation id="8725066075913043281">Try again</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Zoom in</translation>
<translation id="8931333241327730545">Do you want to save this card to your Google Account?</translation>
<translation id="8932102934695377596">Your clock is behind</translation>
+<translation id="8938939909778640821">Accepted credit and prepaid cards</translation>
<translation id="8971063699422889582">Server's certificate has expired.</translation>
<translation id="8986494364107987395">Automatically send usage statistics and crash reports to Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Always block on this site</translation>
<translation id="9170848237812810038">&amp;Undo</translation>
<translation id="917450738466192189">Server's certificate is invalid.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> more}other{<ph name="SHIPPING_OPTION_PREVIEW" /> and <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> more}}</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>
<translation id="9207861905230894330">Failed to add article.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">CLEAR FORM</translation>
<translation id="939736085109172342">New folder</translation>
-<translation id="941721044073577244">Looks like you don't have permission to visit this site</translation>
<translation id="969892804517981540">Official Build</translation>
<translation id="975560348586398090">{COUNT,plural, =0{None}=1{1 item}other{# items}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Developer Build</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_es-419.xtb b/chromium/components/strings/components_strings_es-419.xtb
index 5021842842d..ae2ffce9c04 100644
--- a/chromium/components/strings/components_strings_es-419.xtb
+++ b/chromium/components/strings/components_strings_es-419.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Cierra las demás apps.</translation>
<translation id="1055184225775184556">&amp;Deshacer Agregar</translation>
<translation id="10614374240317010">Nunca guardado</translation>
+<translation id="1066396345355680611">Es posible que ya no puedas acceder al contenido protegido de <ph name="SITE" /> y otros sitios.</translation>
<translation id="106701514854093668">Favoritos de escritorio</translation>
<translation id="1074497978438210769">No seguro</translation>
<translation id="1080116354587839789">Ajustar al ancho</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lista de lectura</translation>
<translation id="1264126396475825575">El informe de fallos se capturó el <ph name="CRASH_TIME" /> (todavía no se cargó ni se ignoró)</translation>
<translation id="1281526147609854549">Emitido por <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Se bloqueó contenido peligroso</translation>
<translation id="1285320974508926690">Nunca traducir este sitio</translation>
<translation id="129553762522093515">Cerrado recientemente</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Intenta borrar tus cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Inscripción de dominio:</translation>
<translation id="1340482604681802745">Dirección de retiro</translation>
-<translation id="1344211575059133124">Al parecer, necesitas permiso para visitar este sitio</translation>
<translation id="1344588688991793829">Configuración de la función Autocompletar de Chromium…</translation>
<translation id="1348198688976932919">El siguiente sitio incluye apps peligrosas</translation>
<translation id="1374468813861204354">sugerencias</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">La identidad de <ph name="ORGANIZATION" /> en <ph name="LOCALITY" /> ha sido verificada por <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Sí</translation>
<translation id="1430915738399379752">Imprimir</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}other{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}}</translation>
<translation id="1506687042165942984">Se muestra una copia guardada (es decir, desactualizada) de la página.</translation>
<translation id="1517433312004943670">Se requiere el número de teléfono</translation>
+<translation id="1517500485252541695">Tarjetas de débito y crédito aceptadas</translation>
<translation id="1519264250979466059">Fecha de compilación</translation>
+<translation id="1527263332363067270">Esperando conexión…</translation>
<translation id="153384715582417236">Eso es todo por ahora</translation>
<translation id="1549470594296187301">JavaScript debe estar habilitado para usar esta función.</translation>
<translation id="1555130319947370107">Azul</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Descargar la página más tarde</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Campo obligatorio</translation>
<translation id="187918866476621466">Abrir páginas de inicio</translation>
<translation id="1883255238294161206">Ocultar lista</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}}</translation>
<translation id="1898423065542865115">Filtrado</translation>
+<translation id="1916770123977586577">Para aplicar la configuración actualizada en este sitio, vuelve a cargar la página</translation>
+<translation id="1919345977826869612">Anuncios</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Ninguno}=1{1 sitio}other{# sitios}}</translation>
<translation id="194030505837763158">Ir a <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Tarjetas de prepago aceptadas</translation>
<translation id="1962204205936693436">Favoritos de <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Error de serialización</translation>
<translation id="1974060860693918893">Avanzada</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Error de HTTP</translation>
<translation id="2270484714375784793">Número de teléfono</translation>
<translation id="2282872951544483773">Experimentos no disponibles</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> elemento}other{<ph name="ITEM_COUNT" /> elementos}}</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>
<translation id="2337852623177822836">Configuración controlada por tu administrador</translation>
<translation id="2354001756790975382">Otros favoritos</translation>
<translation id="2354430244986887761">Navegación segura de Google recientemente <ph name="BEGIN_LINK" />encontró apps dañinas<ph name="END_LINK" /> en <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continuar</translation>
<translation id="2365563543831475020">El informe de fallos que se capturó a las <ph name="CRASH_TIME" /> no se cargó</translation>
<translation id="2367567093518048410">Nivel</translation>
-<translation id="237718015863234333">No hay alternativas de interfaz de usuario disponibles.</translation>
<translation id="2384307209577226199">Empresa (predeterminada)</translation>
<translation id="2386255080630008482">Se ha revocado el certificado del servidor.</translation>
<translation id="2392959068659972793">Mostrar políticas sin valor establecido</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Si marcas esta opción, Chromium almacenará una copia de la tarjeta en el dispositivo para completar más rápidamente los formularios.</translation>
<translation id="2498091847651709837">Escanear tarjeta nueva</translation>
<translation id="2501278716633472235">Ir atrás</translation>
+<translation id="2503184589641749290">Tarjetas de débito y prepago aceptadas</translation>
<translation id="2515629240566999685">Verificar la señal en tu área.</translation>
-<translation id="2516305470678292029">Alternativas de interfaz de usuario</translation>
<translation id="2539524384386349900">Detectar</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> envió una respuesta no válida.</translation>
<translation id="2556876185419854533">&amp;Deshacer Editar</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Método de envío</translation>
<translation id="277499241957683684">Falta un registro de dispositivo.</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>
<translation id="2799020568854403057">El siguiente sitio incluye apps dañinas</translation>
<translation id="2803306138276472711">La Navegación segura de Google <ph name="BEGIN_LINK" />detectó software malicioso<ph name="END_LINK" /> en <ph name="SITE" /> recientemente. A veces, los sitios web que suelen ser seguros contienen software malicioso.</translation>
<translation id="2824775600643448204">Barra de direcciones y de búsqueda</translation>
<translation id="2826760142808435982">La conexión se encriptó y autenticó con <ph name="CIPHER" />, y utiliza <ph name="KX" /> como el mecanismo de intercambio de claves.</translation>
<translation id="2835170189407361413">Eliminar formulario</translation>
+<translation id="2851634818064021665">Necesitas permiso para visitar este sitio</translation>
<translation id="2856444702002559011">Es posible que algunos atacantes intenten robar tu información de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (p. ej., contraseñas, mensajes o tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">No volver a cargar</translation>
-<translation id="2900469785430194048">Google Chrome se quedó sin memoria cuando intentaba mostrar esta página web.</translation>
<translation id="2909946352844186028">Se detectó un cambio de red.</translation>
<translation id="2916038427272391327">Cierra los demás programas.</translation>
<translation id="2922350208395188000">No se puede comprobar el certificado del servidor.</translation>
<translation id="2928905813689894207">Dirección de facturación</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}}</translation>
<translation id="2941952326391522266">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; el certificado de seguridad proviene de <ph name="DOMAIN2" />. Es posible que esto se deba a una configuración incorrecta o a que un atacante interceptó la conexión.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Si se habilita esta opción, Chromium almacenará una copia de la tarjeta en el dispositivo para completar más rápidamente los formularios.</translation>
<translation id="2985398929374701810">Ingresa una dirección válida</translation>
<translation id="2986368408720340940">El método de retiro no está disponible. Prueba otro método.</translation>
@@ -277,13 +281,11 @@
<translation id="3167968892399408617">Una vez que cierres todas las pestañas de incógnito, las páginas que hayas visitado en ese modo no se guardarán en el historial del navegador, en la lista de cookies ni en el historial de búsquedas. Se guardarán los archivos que descargues y los favoritos que crees.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Isla</translation>
-<translation id="317583078218509884">La nueva configuración de permisos del sitio se aplicará al volver a cargar la página.</translation>
<translation id="3176929007561373547">Comprueba la configuración del proxy o comunícate con tu
administrador de red para asegurarte de que el
servidor proxy esté funcionando. Si consideras
que no necesitas usar un servidor proxy:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Abre la página en modo de navegación incógnito.</translation>
<translation id="320323717674993345">Cancelar pago</translation>
<translation id="3207960819495026254">Agregada a favoritos</translation>
<translation id="3225919329040284222">El servidor mostró un certificado que no coincide con lo esperado. Estas expectativas se incluyen en determinados sitios web con un alto nivel de seguridad para garantizar tu protección.</translation>
@@ -296,6 +298,7 @@
<translation id="3270847123878663523">&amp;Deshacer Reorganizar</translation>
<translation id="3282497668470633863">Agregar el nombre en la tarjeta</translation>
<translation id="3286538390144397061">Reiniciar ahora</translation>
+<translation id="3287510313208355388">Descargar cuando haya conexión</translation>
<translation id="3303855915957856445">No se encontraron resultados en la búsqueda</translation>
<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>
@@ -328,7 +331,6 @@
<translation id="3452404311384756672">Obtener intervalo:</translation>
<translation id="3462200631372590220">Ocultar detalles avanzados</translation>
<translation id="3467763166455606212">Se requiere el nombre del titular de la tarjeta</translation>
-<translation id="3478058380795961209">Mes vencimiento</translation>
<translation id="3479539252931486093">¿Ocurrió algo inesperado? <ph name="BEGIN_LINK" />Cuéntanos<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ahora no</translation>
<translation id="3498215018399854026">No pudimos comunicarnos con ninguno de tus padres. Vuelve a intentarlo.</translation>
@@ -344,12 +346,13 @@
&lt;p&gt;Ajusta la fecha y 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; <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Se aceptan tarjetas de crédito y prepago.</translation>
<translation id="3582930987043644930">Agregar nombre</translation>
<translation id="3583757800736429874">&amp;Rehacer Mover</translation>
<translation id="3586931643579894722">Ocultar detalles</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Ingresa una fecha de vencimiento válida</translation>
-<translation id="36224234498066874">Eliminar datos de navegación...</translation>
+<translation id="36224234498066874">Borrar datos de navegación...</translation>
<translation id="362276910939193118">Mostrar historial completo</translation>
<translation id="3623476034248543066">Mostrar valor</translation>
<translation id="3630155396527302611">Si ya está incluido como un programa con permiso para acceder a la red, intenta
@@ -359,10 +362,10 @@
<translation id="3655670868607891010">Si este mensaje aparece con frecuencia, haz clic aquí: <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisión</translation>
<translation id="3678029195006412963">La solicitud no se pudo firmar</translation>
+<translation id="3678529606614285348">Abre la página en una nueva ventana de incógnito (Ctrl-mayúscula-N).</translation>
<translation id="3679803492151881375">El informe de fallos se capturó el <ph name="CRASH_TIME" /> y se cargó el <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Información sobre el certificado</translation>
<translation id="3690164694835360974">Acceso no seguro</translation>
-<translation id="3693415264595406141">Contraseña:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Cargando...</translation>
<translation id="3712624925041724820">Licencias agotadas</translation>
@@ -374,6 +377,7 @@
<translation id="3748148204939282805">Es posible que los atacantes en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> intenten engañarte para que realices alguna acción peligrosa, como instalar software o revelar información personal (p. ej., contraseñas, números de teléfono o tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Falló la traducción debido a un error de servidor.</translation>
<translation id="3759461132968374835">No has notificado ningún bloqueo recientemente. Los bloqueos que se hayan producido mientras la función de notificación de bloqueos estaba desactivada no aparecerán en esta página.</translation>
+<translation id="3765032636089507299">La página de Navegación segura está en construcción.</translation>
<translation id="3778403066972421603">¿Deseas guardar esta tarjeta en tu cuenta de Google y en este dispositivo?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Vencimiento: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -386,8 +390,10 @@
<translation id="3886446263141354045">Tu solicitud de acceso a este sitio se envió a <ph name="NAME" /></translation>
<translation id="3890664840433101773">Agregar correo electrónico</translation>
<translation id="3901925938762663762">Caducó la tarjeta.</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID del informe de fallos <ph name="CRASH_ID" /> cargado (ID de fallo local: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; su certificado de seguridad no especifica la extensión Nombres alternativos del asunto. Es posible que se deba a un error en la configuración o a que haya un atacante que está interceptando tu conexión.</translation>
+<translation id="3949601375789751990">Tu historial de navegación aparece aquí</translation>
<translation id="3963721102035795474">Modo de lectura</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Ninguno}=1{De 1 sitio }other{De # sitios }}</translation>
<translation id="397105322502079400">Calculando...</translation>
@@ -416,6 +422,8 @@
<translation id="4165986682804962316">Configuración del sitio</translation>
<translation id="4169947484918424451">¿Quieres que Chromium guarde esta tarjeta?</translation>
<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="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>
@@ -439,12 +447,12 @@
<translation id="4356973930735388585">Es posible que los atacantes que se encuentren en este sitio intenten instalar programas peligrosos en tu computadora con el fin de robarte información o borrarla (p. ej., fotos, contraseñas, mensajes y tarjetas de crédito).</translation>
<translation id="4372948949327679948">Valor <ph name="VALUE_TYPE" /> esperado.</translation>
<translation id="4377125064752653719">Intentaste acceder a <ph name="DOMAIN" />, pero el emisor anuló el certificado que presentó el servidor. Esto significa que no se debe confiar en absoluto en las credenciales de seguridad que presentó el servidor. Te puedes estar comunicando con un atacante.</translation>
-<translation id="4381091992796011497">Nombre de usuario:</translation>
<translation id="4394049700291259645">Inhabilitar</translation>
<translation id="4406896451731180161">resultados de búsqueda</translation>
<translation id="4424024547088906515">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; Chrome 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="4432688616882109544"><ph name="HOST_NAME" /> no aceptó tu certificado de acceso o es posible que no se haya proporcionado.</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="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>
@@ -456,12 +464,14 @@
<translation id="4587425331216688090">¿Confirmas que quieres quitar la dirección de Chrome?</translation>
<translation id="4592951414987517459">Tu conexión a <ph name="DOMAIN" /> está encriptada con un conjunto de cifrado moderno.</translation>
<translation id="4594403342090139922">&amp;Deshacer Eliminar</translation>
+<translation id="4611292653554630842">Acceder</translation>
<translation id="4619615317237390068">Pestañas de otros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; el certificado de seguridad contiene errores. Es posible que esto se deba a una configuración incorrecta o a que un atacante interceptó la conexión.</translation>
<translation id="4690462567478992370">Dejar de usar un certificado no válido</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Se interrumpió la conexión</translation>
+<translation id="471880041731876836">No tienes permiso para visitar este sitio</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Ejecución del Diagnóstico de red de Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Volver a cargar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
@@ -483,14 +493,15 @@
<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="4863764087567530506">Es posible que este contenido trate de engañarte para que instales software o reveles información personal. <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" /></translation>
<translation id="4880827082731008257">Buscar historial</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Se requiere una autenticación</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{y 1 página web más}other{y # páginas web más}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Esta página ha sido traducida desde un idioma desconocido a <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pago</translation>
<translation id="4926049483395192435">Debe especificarse un valor.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Acciones</translation>
<translation id="4958444002117714549">Mostrar lista</translation>
<translation id="4974590756084640048">Volver a habilitar las advertencias</translation>
@@ -506,6 +517,7 @@
<translation id="5045550434625856497">Contraseña incorrecta</translation>
<translation id="5056549851600133418">Artículos para ti</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Comprobar la dirección de proxy<ph name="END_LINK" />.</translation>
+<translation id="5086888986931078152">Es posible que ya no puedas acceder al contenido protegido de otros sitios.</translation>
<translation id="5087286274860437796">El certificado del servidor no es válido en este momento.</translation>
<translation id="5087580092889165836">Agregar tarjeta</translation>
<translation id="5089810972385038852">Estado</translation>
@@ -513,18 +525,27 @@
<translation id="5095208057601539847">Provincia</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5141240743006678641">Encriptar contraseñas sincronizadas con tus credenciales de Google</translation>
-<translation id="514421653919133810">Abre la página en modo de navegación incógnito (Ctrl-mayúscula-N).</translation>
<translation id="5145883236150621069">Código de error en la respuesta de la política</translation>
+<translation id="5159010409087891077">Abre la página en una nueva ventana de incógnito (⇧⌘N).</translation>
<translation id="5171045022955879922">Buscar o escribir URL</translation>
<translation id="5172758083709347301">Equipo</translation>
<translation id="5179510805599951267">¿No está en <ph name="ORIGINAL_LANGUAGE" />? Informa este error</translation>
-<translation id="5181140330217080051">Descargando...</translation>
<translation id="5190835502935405962">Barra de favoritos</translation>
<translation id="5199729219167945352">Experimentos</translation>
<translation id="5205222826937269299">Nombre (obligatorio)</translation>
<translation id="5222812217790122047">Correo electrónico (obligatorio)</translation>
<translation id="5251803541071282808">Nube</translation>
<translation id="5277279256032773186">¿Usas Chrome en el trabajo? Las empresas pueden administrar la configuración de Chrome para sus empleados. Más información</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Realiza estos pasos para inhabilitar el software de forma temporal, para que puedas acceder a la Web. Necesitarás privilegios de administrador.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Haz clic en <ph name="BEGIN_BOLD" />Inicio<ph name="END_BOLD" />; luego, busca <ph name="BEGIN_BOLD" />"Ver servicios locales"<ph name="END_BOLD" /> y selecciónalo.
+ <ph name="LIST_ITEM" />Selecciona <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />En <ph name="BEGIN_BOLD" />Tipo de inicio<ph name="END_BOLD" />, selecciona <ph name="BEGIN_BOLD" />Inhabilitado<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />En <ph name="BEGIN_BOLD" />Estado del servicio<ph name="END_BOLD" />, haz clic en <ph name="BEGIN_BOLD" />Detener<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Haz clic en <ph name="BEGIN_BOLD" />Aplicar<ph name="END_BOLD" /> y, luego, en <ph name="BEGIN_BOLD" />Aceptar<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Visita el <ph name="BEGIN_LEARN_MORE_LINK" />Centro de ayuda de Chrome<ph name="END_LEARN_MORE_LINK" /> para obtener información sobre cómo quitar el software de forma permanente de tu computadora.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Tu conexión a este sitio no es privada. Para salir del modo RV en cualquier momento, quita los auriculares y presiona Atrás.</translation>
<translation id="5299298092464848405">Error al analizar la política</translation>
<translation id="5308689395849655368">Notificación de fallas desactivada.</translation>
@@ -541,9 +562,10 @@
<translation id="5439770059721715174">Error de validación de esquema en "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">No se encuentra esta página <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Marca de tiempo de política incorrecta</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> de <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Sin validez</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}other{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}}</translation>
<translation id="5470861586879999274">&amp;Rehacer Editar</translation>
+<translation id="5481076368049295676">Es posible que este contenido intente instalar software peligroso que robe o borre tu información en tu dispositivo . <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Agregar una dirección válida</translation>
<translation id="5492298309214877701">En la intranet de la compañía, organización o escuela, este sitio tiene la misma URL que un sitio web externo
<ph name="LINE_BREAK" />
@@ -555,6 +577,7 @@
<translation id="5540224163453853">No se pudo encontrar el artículo solicitado.</translation>
<translation id="5544037170328430102">Una página incrustada en <ph name="SITE" /> dice:</translation>
<translation id="5556459405103347317">Cargar de nuevo</translation>
+<translation id="5560088892362098740">Fecha de vencimiento</translation>
<translation id="5565735124758917034">Activo</translation>
<translation id="5571083550517324815">No se puede retirar el artículo en esta dirección. Selecciona una diferente.</translation>
<translation id="5572851009514199876">Abre Chrome y accede a tu cuenta para que el programa pueda comprobar si puedes acceder a este sitio.</translation>
@@ -569,12 +592,13 @@
<translation id="5629630648637658800">Error al cargar la configuración de la política</translation>
<translation id="5631439013527180824">Token de administración de dispositivos no válido</translation>
<translation id="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="5646376287012673985">Ubicación</translation>
<translation id="5659593005791499971">Correo electrónico</translation>
<translation id="5669703222995421982">Obtener contenido personalizado</translation>
<translation id="5675650730144413517">Esta página no funciona</translation>
<translation id="5710435578057952990">No se ha verificado la identidad de este sitio web.</translation>
-<translation id="5713016350996637505">Se bloqueó contenido engañoso</translation>
+<translation id="5719499550583120431">Se aceptan tarjetas de prepago.</translation>
<translation id="5720705177508910913">Usuario actual</translation>
<translation id="5732392974455271431">Tus padres pueden desbloquearlo por ti</translation>
<translation id="5763042198335101085">Escribe una dirección de correo electrónico válida</translation>
@@ -586,15 +610,14 @@
<translation id="5803412860119678065">¿Deseas llenar los campos con la información de tu tarjeta <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Tu conexión a <ph name="DOMAIN" /> está encriptada con un conjunto de cifrado obsoleto.</translation>
<translation id="5813119285467412249">&amp;Rehacer Agregar</translation>
-<translation id="5814352347845180253">Es posible que ya no puedas acceder al contenido premium de <ph name="SITE" /> y otros sitios.</translation>
<translation id="5838278095973806738">No debes ingresar información confidencial en este sitio (p. ej., contraseñas o tarjetas de crédito), ya que los atacantes podrían robarla.</translation>
<translation id="5869405914158311789">No se puede acceder a este sitio</translation>
<translation id="5869522115854928033">Contraseñas almacenadas</translation>
<translation id="5872918882028971132">Sugerencias para padres</translation>
+<translation id="5893752035575986141">Se aceptan tarjetas de crédito.</translation>
<translation id="5901630391730855834">Amarillo</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="5926846154125914413">Es posible que ya no puedas acceder al contenido premium de otros sitios.</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="5967867314010545767">Eliminar del historial</translation>
<translation id="5975083100439434680">Alejar</translation>
@@ -610,6 +633,7 @@
<translation id="6040143037577758943">Cerrar</translation>
<translation id="6042308850641462728">Más</translation>
<translation id="6047233362582046994">Si comprendes los riesgos de seguridad, puedes <ph name="BEGIN_LINK" />visitar este sitio<ph name="END_LINK" /> antes de que se hayan quitado las apps dañinas.</translation>
+<translation id="6047927260846328439">Es posible que este contenido trate de engañarte para que instales software o reveles información personal. <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">No puedes visitar <ph name="SITE" /> ahora porque el sitio web usa la fijación de certificados. 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="6060685159320643512">Cuidado, estos experimentos pueden dañarte</translation>
<translation id="6080696365213338172">Accediste al contenido mediante un certificado proporcionado por el administrador. Los datos que proporciones a <ph name="DOMAIN" /> pueden ser interceptados por tu administrador.</translation>
@@ -623,7 +647,6 @@
<translation id="6165508094623778733">Más información</translation>
<translation id="6169916984152623906">Ahora puedes navegar con privacidad. Si otras personas usan este dispositivo, no verán tu actividad. Sin embargo, se guardarán las descargas y los favoritos.</translation>
<translation id="6177128806592000436">Tu conexión con este sitio no es segura</translation>
-<translation id="6184817833369986695">(cohorte: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Comprueba tu conexión a Internet.</translation>
<translation id="6218753634732582820">¿Confirmas que quieres quitar la dirección de Chromium?</translation>
<translation id="6221345481584921695">La Navegación segura de Google <ph name="BEGIN_LINK" />detectó software malicioso<ph name="END_LINK" /> en <ph name="SITE" /> recientemente. A veces, los sitios web que suelen ser seguros contienen software malicioso. El contenido malicioso proviene de <ph name="SUBRESOURCE_HOST" />, un conocido distribuidor de software malicioso.</translation>
@@ -642,13 +665,14 @@
<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>
<translation id="6387478394221739770">Si estás interesado en probar nuevas e interesantes funciones de Chrome, visita nuestro canal beta en chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium se quedó sin memoria cuando intentaba mostrar esta página web.</translation>
+<translation id="6397451950548600259">Un software en tu computadora evita que Chrome se conecte de forma segura a la Web</translation>
<translation id="6404511346730675251">Editar marcador</translation>
<translation id="6410264514553301377">Ingresar la fecha de vencimiento y el CVC para <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Les preguntaste a tus padres si puedes visitar este sitio</translation>
@@ -663,6 +687,7 @@
<translation id="647261751007945333">Políticas de dispositivos</translation>
<translation id="6477321094435799029">Chrome detectó código inusual en esta página y la bloqueó para proteger tu información personal (p. ej.: contraseñas, números de teléfono y tarjetas de crédito).</translation>
<translation id="6489534406876378309">Comenzar a cargar fallos</translation>
+<translation id="6507833130742554667">Se aceptan tarjetas de crédito y débito.</translation>
<translation id="6508722015517270189">Reinicia Chrome.</translation>
<translation id="6529602333819889595">&amp;Rehacer Eliminar</translation>
<translation id="6534179046333460208">Sugerencias de la Web física</translation>
@@ -671,13 +696,13 @@
<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="657639383826808334">Es posible que este contenido intente instalar software peligroso en tu dispositivo que robe o borre tu información. <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">Opciones de encriptación</translation>
<translation id="662080504995468778">Permanecer aquí</translation>
<translation id="6626291197371920147">Agregar un número de tarjeta válido</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Búsqueda</translation>
<translation id="6630809736994426279">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 Mac 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="6644283850729428850">Esta política no ha sido aprobada.</translation>
+<translation id="6657585470893396449">Contraseña</translation>
<translation id="6671697161687535275">¿Confirmas que quieres quitar la sugerencia de formulario de Chromium?</translation>
<translation id="6685834062052613830">Salir y completar la configuración</translation>
<translation id="6710213216561001401">Anterior</translation>
@@ -708,7 +733,6 @@
<translation id="6970216967273061347">Distrito</translation>
<translation id="6973656660372572881">Se especifican servidores proxy fijos y URL de secuencias de comandos .pac.</translation>
<translation id="6989763994942163495">Mostrar configuración avanzada...</translation>
-<translation id="7000990526846637657">No se encontraron entradas en el historial</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Es posible que tu cuenta de Google tenga otros formularios del historial de navegación en <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Contraseñas</translation>
@@ -723,7 +747,9 @@
<translation id="7129409597930077180">No se pueden realizar envíos a esa dirección. Selecciona una dirección diferente.</translation>
<translation id="7138472120740807366">Método de entrega</translation>
<translation id="7139724024395191329">Emirato</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}other{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}}</translation>
<translation id="7155487117670177674">Pago no seguro</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}other{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}}</translation>
<translation id="7179921470347911571">Reiniciar ahora</translation>
<translation id="7180611975245234373">Actualizar</translation>
<translation id="7182878459783632708">No hay políticas establecidas.</translation>
@@ -764,6 +790,7 @@
<translation id="7514365320538308">Descargar</translation>
<translation id="7518003948725431193">No se encontró una página web para la siguiente dirección web: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Tu conexión con este sitio no es privada</translation>
<translation id="7535087603100972091">Valor</translation>
<translation id="7537536606612762813">Obligatoria</translation>
<translation id="7542403920425041731">Después de que se confirme, los datos de tu tarjeta se compartirán con este sitio.</translation>
@@ -773,7 +800,6 @@
<translation id="7552846755917812628">Intenta las siguientes sugerencias:</translation>
<translation id="7554791636758816595">Nueva pestaña</translation>
<translation id="7567204685887185387">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; el certificado de seguridad podría haberse emitido de forma fraudulenta. Es posible que esto se deba a una configuración incorrecta o a que un atacante interceptó la conexión.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}other{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}}</translation>
<translation id="7568593326407688803">Esta página está en<ph name="ORIGINAL_LANGUAGE" />¿Quieres traducirla?</translation>
<translation id="7569952961197462199">¿Confirmas que quieres quitar la tarjeta de crédito de Chrome?</translation>
<translation id="7569983096843329377">Negro</translation>
@@ -800,9 +826,11 @@
<translation id="7714464543167945231">Certificado</translation>
<translation id="7716147886133743102">Bloqueado por tu administrador</translation>
<translation id="7716424297397655342">No se puede cargar este sitio desde la caché</translation>
+<translation id="774634243536837715">Se bloqueó contenido peligroso.</translation>
<translation id="7752995774971033316">Sin administrar</translation>
<translation id="7755287808199759310">Uno de tus padres puede desbloquearlo por ti</translation>
-<translation id="7758069387465995638">Es posible que un software antivirus o un firewarll hayan bloqueado la conexión.</translation>
+<translation id="7758069387465995638">Es posible que un software antivirus o un firewall hayan bloqueado la conexión.</translation>
+<translation id="7759163816903619567">Dominio para mostrar:</translation>
<translation id="7761701407923456692">El certificado del servidor no coincide con la dirección URL.</translation>
<translation id="7763386264682878361">Analizador del manifiesto de pagos</translation>
<translation id="7764225426217299476">Agregar dirección</translation>
@@ -817,6 +845,7 @@
<translation id="7815407501681723534">Se encontraron <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> para "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">Sin embargo, no eres invisible. El modo de navegación de incógnito no oculta tu navegación de tu empleador, de tu proveedor de servicios de Internet, ni de los sitios web que visitas.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Se aceptan tarjetas de débito y prepago.</translation>
<translation id="7887683347370398519">Verifica tu CVC y vuelve a intentarlo.</translation>
<translation id="79338296614623784">Ingresa un número de teléfono válido</translation>
<translation id="7935318582918952113">Filtro de DOM</translation>
@@ -836,7 +865,6 @@
<translation id="8041089156583427627">Enviar comentario</translation>
<translation id="8041940743680923270">Usar configuración global predeterminada (Preguntar)</translation>
<translation id="8088680233425245692">Error al visualizar artículo</translation>
-<translation id="8089520772729574115">menos de 1 Mb</translation>
<translation id="8091372947890762290">La activación está pendiente en el servidor.</translation>
<translation id="8118489163946903409">Forma de pago</translation>
<translation id="8131740175452115882">Confirmar</translation>
@@ -848,13 +876,16 @@
<translation id="8194797478851900357">&amp;Deshacer Mover</translation>
<translation id="8201077131113104583">URL de actualización no válida para la extensión con ID "<ph name="EXTENSION_ID" />"</translation>
<translation id="8202097416529803614">Resumen del pedido</translation>
+<translation id="8205463626947051446">El sitio tiende a mostrar anuncios intrusivos</translation>
<translation id="8218327578424803826">Ubicación asignada:</translation>
<translation id="8225771182978767009">La persona que configuró esta computadora decidió bloquear este sitio.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Abre la página en una nueva pestaña de incógnito.</translation>
<translation id="8241707690549784388">La página que buscas ha utilizado la información que has especificado. Volver a la página podría provocar la repetición de alguna acción. ¿Deseas continuar?</translation>
+<translation id="8241712895048303527">Bloquear en este sitio</translation>
<translation id="8249320324621329438">Se obtuvo por última vez:</translation>
<translation id="8253091569723639551">Se requiere una dirección de facturación</translation>
-<translation id="8261506727792406068">Eliminar</translation>
+<translation id="8261506727792406068">Borrar</translation>
<translation id="8289355894181816810">Comunícate con el administrador de red si no entiendes bien lo que significa.</translation>
<translation id="8293206222192510085">Agregar Marcador</translation>
<translation id="8294431847097064396">Fuente</translation>
@@ -862,7 +893,6 @@
<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="8332188693563227489">Se denegó el acceso a <ph name="HOST_NAME" /></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="8344669043927012510">Abre la página en modo de navegación incógnito (⇧⌘N).</translation>
<translation id="8349305172487531364">Barra de favoritos</translation>
<translation id="8363502534493474904">Desactivar el modo de avión.</translation>
<translation id="8364627913115013041">Sin establecer</translation>
@@ -872,6 +902,7 @@
<translation id="8398259832188219207">El informe de fallos se cargó el <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Bloqueos (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Debes ingresar la misma frase de contraseña dos veces.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configuración</translation>
<translation id="8433057134996913067">Si realizas esta acción, saldrás de la mayoría de los sitios web.</translation>
<translation id="8437238597147034694">&amp;Deshacer Mover</translation>
@@ -879,8 +910,9 @@
<translation id="8483780878231876732">Para usar tarjetas de tu cuenta de Google, accede a tu cuenta en Chrome</translation>
<translation id="8488350697529856933">Se aplica a</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> tardó demasiado en responder.</translation>
-<translation id="8532105204136943229">Año vencimiento</translation>
+<translation id="8503813439785031346">Nombre de usuario</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="8571890674111243710">Traduciendo página a <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Agregar teléfono
@@ -888,6 +920,7 @@
<translation id="859285277496340001">El certificado no especifica un mecanismo para verificar si ha sido revocado.</translation>
<translation id="8620436878122366504">Tus padres aún no lo aprobaron</translation>
<translation id="8647750283161643317">Restablecer todos los valores predeterminados</translation>
+<translation id="8660471606262461360">De Google Payments</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="8725066075913043281">Intentar nuevamente</translation>
@@ -915,6 +948,7 @@
<translation id="8903921497873541725">Acercar</translation>
<translation id="8931333241327730545">¿Quieres guardar esta tarjeta en tu cuenta de Google?</translation>
<translation id="8932102934695377596">El reloj está atrasado</translation>
+<translation id="8938939909778640821">Tarjetas de crédito y prepago aceptadas</translation>
<translation id="8971063699422889582">El certificado del servidor ha caducado.</translation>
<translation id="8986494364107987395">Enviar automáticamente estadísticas de uso e informes sobre fallos a Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -944,7 +978,6 @@
<translation id="9169664750068251925">Bloquear siempre en este sitio</translation>
<translation id="9170848237812810038">&amp;Deshacer</translation>
<translation id="917450738466192189">El certificado del servidor no es válido.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}other{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}}</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>
<translation id="9207861905230894330">Error al agregar artículo</translation>
@@ -953,9 +986,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">BORRAR FORMULARIO</translation>
<translation id="939736085109172342">Nueva carpeta</translation>
-<translation id="941721044073577244">Al parecer, no tienes permiso para visitar este sitio</translation>
<translation id="969892804517981540">Build oficial</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Ninguno}=1{1 elemento}other{# elementos}}</translation>
+<translation id="981121421437150478">Sin conexión</translation>
<translation id="988159990683914416">Build para desarrolladores</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_es.xtb b/chromium/components/strings/components_strings_es.xtb
index 14d9f646a0f..378dce1c291 100644
--- a/chromium/components/strings/components_strings_es.xtb
+++ b/chromium/components/strings/components_strings_es.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Cierra otras aplicaciones</translation>
<translation id="1055184225775184556">&amp;Deshacer acción de añadir</translation>
<translation id="10614374240317010">Contraseñas que nunca se guardan</translation>
+<translation id="1066396345355680611">Es posible que dejes de tener acceso al contenido protegido de <ph name="SITE" /> y a otros sitios web.</translation>
<translation id="106701514854093668">Marcadores del ordenador</translation>
<translation id="1074497978438210769">No es seguro</translation>
<translation id="1080116354587839789">Se ajusta al ancho</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lista de lectura</translation>
<translation id="1264126396475825575">Informe sobre fallos registrado el <ph name="CRASH_TIME" /> (todavía no se ha subido ni ignorado)</translation>
<translation id="1281526147609854549">Emitido por <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Contenido peligroso bloqueado</translation>
<translation id="1285320974508926690">No traducir nunca este sitio</translation>
<translation id="129553762522093515">Cerrado recientemente</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Borrar las cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Dominio de registro:</translation>
<translation id="1340482604681802745">Dirección de recogida</translation>
-<translation id="1344211575059133124">Parece que necesitas permiso para acceder a este sitio web</translation>
<translation id="1344588688991793829">Configuración de la función Autocompletar de Chromium...</translation>
<translation id="1348198688976932919">El sitio web al que vas a acceder contiene aplicaciones peligrosas</translation>
<translation id="1374468813861204354">sugerencias</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">La identidad de <ph name="ORGANIZATION" /> en <ph name="LOCALITY" /> ha sido verificada por <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Sí</translation>
<translation id="1430915738399379752">Imprimir</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}other{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}}</translation>
<translation id="1506687042165942984">Muestra una copia guardada (es decir, no actualizada) de esta página.</translation>
<translation id="1517433312004943670">Número de teléfono requerido</translation>
+<translation id="1517500485252541695">Tarjetas de débito y crédito aceptadas</translation>
<translation id="1519264250979466059">Fecha de compilación</translation>
+<translation id="1527263332363067270">Esperando conexión…</translation>
<translation id="153384715582417236">Eso es todo por ahora</translation>
<translation id="1549470594296187301">JavaScript debe estar habilitado para utilizar esta función.</translation>
<translation id="1555130319947370107">Azul</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Descargar la página más tarde</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Campo obligatorio</translation>
<translation id="187918866476621466">Abrir páginas de inicio</translation>
<translation id="1883255238294161206">Contraer lista</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}}</translation>
<translation id="1898423065542865115">Filtrado</translation>
+<translation id="1916770123977586577">Vuelve a cargar esta página para aplicar la configuración actualizada a este sitio web</translation>
+<translation id="1919345977826869612">Anuncios</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Ninguno}=1{1 sitio web}other{# sitios web}}</translation>
<translation id="194030505837763158">Ir a <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Tarjetas prepago aceptadas</translation>
<translation id="1962204205936693436">Marcadores de <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Error de serialización</translation>
<translation id="1974060860693918893">Configuración avanzada</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Error de HTTP</translation>
<translation id="2270484714375784793">Número de teléfono</translation>
<translation id="2282872951544483773">Experimentos no disponibles</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> elemento}other{<ph name="ITEM_COUNT" /> elementos}}</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>
<translation id="2337852623177822836">Configuración controlada por el administrador</translation>
<translation id="2354001756790975382">Otros marcadores</translation>
<translation id="2354430244986887761">La función Navegación Segura de Google <ph name="BEGIN_LINK" />encontró aplicaciones dañinas<ph name="END_LINK" /> recientemente en <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continuar</translation>
<translation id="2365563543831475020">No se ha subido el informe sobre fallos registrado el <ph name="CRASH_TIME" /></translation>
<translation id="2367567093518048410">Nivel</translation>
-<translation id="237718015863234333">No hay alternativas de IU disponibles</translation>
<translation id="2384307209577226199">Empresa (con valores predeterminados)</translation>
<translation id="2386255080630008482">Se ha revocado el certificado de servidor.</translation>
<translation id="2392959068659972793">Mostrar políticas sin valores establecidos</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Si se activa esta opción, Chromium guardará una copia de tu tarjeta en este dispositivo para completar formularios más rápidamente.</translation>
<translation id="2498091847651709837">Escanear nueva tarjeta</translation>
<translation id="2501278716633472235">Volver</translation>
+<translation id="2503184589641749290">Tarjetas prepago y de débito aceptadas</translation>
<translation id="2515629240566999685">Comprobar la señal en tu zona</translation>
-<translation id="2516305470678292029">Alternativas de IU</translation>
<translation id="2539524384386349900">Detectar</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> ha enviado una respuesta no válida.</translation>
<translation id="2556876185419854533">&amp;Deshacer edición</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Método de envío</translation>
<translation id="277499241957683684">Falta un registro de dispositivo.</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>
<translation id="2799020568854403057">El sitio web al que vas a acceder contiene aplicaciones dañinas</translation>
<translation id="2803306138276472711">Recientemente, la función de Navegación Segura de Google <ph name="BEGIN_LINK" />ha detectado software malicioso<ph name="END_LINK" /> en <ph name="SITE" />. En ocasiones, los sitios web que normalmente son seguros contienen software malicioso.</translation>
<translation id="2824775600643448204">Barra de direcciones y de búsqueda </translation>
<translation id="2826760142808435982">La conexión se ha encriptado y autenticado con <ph name="CIPHER" />, y utiliza <ph name="KX" /> como el mecanismo de intercambio clave.</translation>
<translation id="2835170189407361413">Eliminar formulario</translation>
+<translation id="2851634818064021665">Necesitas permiso para acceder a este sitio web</translation>
<translation id="2856444702002559011">Es posible que los atacantes estén intentando robar tu información de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (por ejemplo, contraseñas, mensajes o tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">No volver a cargar</translation>
-<translation id="2900469785430194048">Google Chrome se ha quedado sin memoria al intentar mostrar esta página web.</translation>
<translation id="2909946352844186028">Se ha detectado un cambio de red.</translation>
<translation id="2916038427272391327">Cierra otros programas</translation>
<translation id="2922350208395188000">No es posible comprobar el certificado del servidor.</translation>
<translation id="2928905813689894207">Dirección de facturación</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> más}}</translation>
<translation id="2941952326391522266">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, su certificado de seguridad procede de <ph name="DOMAIN2" />. Este problema puede deberse a una configuración incorrecta o a que un atacante haya interceptado la conexión.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Si se habilita esta opción, Chromium guardará una copia de tu tarjeta en este dispositivo para completar formularios más rápidamente.</translation>
<translation id="2985398929374701810">Introduce una dirección válida</translation>
<translation id="2986368408720340940">Este método de recogida no está disponible. Selecciona otro.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Las páginas que aparezcan en las pestañas de incógnito no se guardarán en el historial del navegador, en el almacén de cookies ni en el historial de búsquedas una vez que hayas cerrado todas tus pestañas de incógnito. Se mantendrán los archivos que descargues o los marcadores que crees.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Isla</translation>
-<translation id="317583078218509884">La nueva configuración de permisos del sitio se aplicará una vez que la página se haya vuelto a cargar.</translation>
<translation id="3176929007561373547">Comprueba la configuración del proxy o ponte en contacto con el administrador de red para
asegurarte de que el servidor proxy funcione correctamente. Si consideras que no necesitas utilizar
un servidor proxy, sigue estas instrucciones:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Abre la página en modo incógnito</translation>
<translation id="320323717674993345">Cancelar pago</translation>
<translation id="3207960819495026254">Añadido a marcadores</translation>
<translation id="3225919329040284222">El servidor ha mostrado un certificado que no coincide con lo que se esperaba. Algunos sitios web tienen un alto nivel de seguridad para garantizar tu protección y esperan ciertas características de los certificados.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Deshacer reorganización</translation>
<translation id="3282497668470633863">Añadir un nombre de la tarjeta</translation>
<translation id="3286538390144397061">Reiniciar ahora</translation>
+<translation id="3287510313208355388">Descargar cuando haya conexión</translation>
<translation id="3303855915957856445">No se han encontrado resultados de búsqueda</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Intervalo de comprobación:</translation>
<translation id="3462200631372590220">Ocultar configuración avanzada</translation>
<translation id="3467763166455606212">El nombre del titular de la tarjeta es obligatorio</translation>
-<translation id="3478058380795961209">Mes de caducidad</translation>
<translation id="3479539252931486093">¿No te lo esperabas? <ph name="BEGIN_LINK" />Notifícanoslo<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ahora no</translation>
<translation id="3498215018399854026">No hemos podido contactar con tu padre/madre/tutor. Vuelve a intentarlo.</translation>
@@ -343,6 +345,7 @@
&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;Ajustes&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Se aceptan tarjetas prepago y de crédito.</translation>
<translation id="3582930987043644930">Añade un nombre</translation>
<translation id="3583757800736429874">&amp;Rehacer movimiento</translation>
<translation id="3586931643579894722">Ocultar detalles</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Si este mensaje aparece con frecuencia, prueba a solucionarlo con estas <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisión</translation>
<translation id="3678029195006412963">No se ha podido firmar la solicitud</translation>
+<translation id="3678529606614285348">Abre una página en una nueva ventana de incógnito (Ctrl + Mayús + N).</translation>
<translation id="3679803492151881375">Informe sobre fallos registrado el <ph name="CRASH_TIME" /> y subido el <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Datos del certificado</translation>
<translation id="3690164694835360974">Inicio de sesión no seguro</translation>
-<translation id="3693415264595406141">Contraseña:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Cargando...</translation>
<translation id="3712624925041724820">Licencias agotadas</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Es posible que los atacantes que se encuentren en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> intenten engañarte para que realices una acción peligrosa, como instalar software o revelar tu información personal (por ejemplo, contraseñas, números de teléfono o tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Se ha producido un error de traducción debido a un problema con el servidor.</translation>
<translation id="3759461132968374835">No se ha notificado ningún fallo recientemente. Los fallos que se hayan producido cuando la función de notificación de fallos estaba inhabilitada no aparecerán en esta página.</translation>
+<translation id="3765032636089507299">La página Navegación Segura está en construcción.</translation>
<translation id="3778403066972421603">¿Quieres guardar esta tarjeta en tu cuenta de Google y en este dispositivo?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Vencimiento: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Tu solicitud de acceso a este sitio web se ha enviado a <ph name="NAME" /></translation>
<translation id="3890664840433101773">Añadir correo electrónico</translation>
<translation id="3901925938762663762">La tarjeta ha caducado</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID del informe sobre fallos subido: <ph name="CRASH_ID" /> (ID del fallo local: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Este servidor no ha podido demostrar que es <ph name="DOMAIN" />; su certificado de seguridad no especifica nombres alternativos del sujeto. Este problema puede deberse a una configuración incorrecta o a que un atacante ha interceptado la conexión.</translation>
+<translation id="3949601375789751990">Tu historial de navegación aparece aquí</translation>
<translation id="3963721102035795474">Modo de lectura</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Ninguno}=1{De 1 sitio web }other{De # sitios web }}</translation>
<translation id="397105322502079400">Calculando...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Configuración de sitios web</translation>
<translation id="4169947484918424451">¿Quieres que Chromium guarde esta tarjeta?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Es posible que los atacantes que se encuentren en este sitio web 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).</translation>
<translation id="4372948949327679948">Se esperaba un valor <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Has intentado acceder a <ph name="DOMAIN" />, pero el emisor ha revocado el certificado mostrado por el servidor, lo que significa que las credenciales de seguridad presentadas por el servidor no son de confianza. Es posible que hayas accedido a la página de un atacante.</translation>
-<translation id="4381091992796011497">Nombre de usuario:</translation>
<translation id="4394049700291259645">Inhabilitar</translation>
<translation id="4406896451731180161">resultados de la búsqueda</translation>
<translation id="4424024547088906515">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, Chrome 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="4432688616882109544"><ph name="HOST_NAME" /> no ha aceptado el certificado de inicio de sesión o es posible que no se haya proporcionado ninguno.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">¿Eliminar dirección de Chrome?</translation>
<translation id="4592951414987517459">Tu conexión con <ph name="DOMAIN" /> está cifrada con un conjunto de cifrado moderno.</translation>
<translation id="4594403342090139922">&amp;Deshacer eliminación</translation>
+<translation id="4611292653554630842">Iniciar sesión</translation>
<translation id="4619615317237390068">Pestañas de otros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, su certificado de seguridad contiene errores. El problema puede deberse a una configuración incorrecta o a que un atacante haya interceptado la conexión.</translation>
<translation id="4690462567478992370">Dejar de utilizar un certificado no válido</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Se ha interrumpido la conexión</translation>
+<translation id="471880041731876836">No tienes permiso para acceder a este sitio web</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Ejecutar Diagnósticos de red de Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Volver a cargar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Es posible que este contenido intente engañarte para que instales software o reveles información personal. <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Buscar en el historial</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> y <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Autenticación obligatoria</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ y 1 página web más}other{ y # páginas web más}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Esta página se ha traducido de un idioma desconocido al <ph name="LANGUAGE_LANGUAGE" />.</translation>
<translation id="4923459931733593730">Pago</translation>
<translation id="4926049483395192435">Se debe especificar un valor.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Acciones</translation>
<translation id="4958444002117714549">Expandir lista</translation>
<translation id="4974590756084640048">Volver a habilitar advertencias</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Contraseña incorrecta</translation>
<translation id="5056549851600133418">Artículos recomendados para ti</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Comprobar la dirección del proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Es posible que dejes de tener acceso al contenido protegido de algunos sitios web.</translation>
<translation id="5087286274860437796">El certificado del servidor no es válido en este momento.</translation>
<translation id="5087580092889165836">Añadir tarjeta</translation>
<translation id="5089810972385038852">Estado</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provincia</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5141240743006678641">Cifrar contraseñas sincronizadas con tus credenciales de Google</translation>
-<translation id="514421653919133810">Abre la página en modo incógnito (Ctrl + Mayús + N)</translation>
<translation id="5145883236150621069">Código de error presente en respuesta de la política</translation>
+<translation id="5159010409087891077">Abre una página en una nueva ventana de incógnito (⇧ + ⌘ + N)</translation>
<translation id="5171045022955879922">Busca o escribe una URL</translation>
<translation id="5172758083709347301">Equipo</translation>
<translation id="5179510805599951267">¿Esta página no está escrita en <ph name="ORIGINAL_LANGUAGE" />? Informa de este error.</translation>
-<translation id="5181140330217080051">Descargando</translation>
<translation id="5190835502935405962">Barra de marcadores</translation>
<translation id="5199729219167945352">Experimentos</translation>
<translation id="5205222826937269299">Nombre obligatorio</translation>
<translation id="5222812217790122047">Correo electrónico obligatorio</translation>
<translation id="5251803541071282808">Nube</translation>
<translation id="5277279256032773186">¿Usas Chrome en el trabajo? Las empresas pueden administrar la configuración de Chrome de sus empleados. Más información</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Sigue estos pasos para inhabilitar temporalmente el software y poder acceder a la Web. Necesitarás privilegios de administrador.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Haz clic en <ph name="BEGIN_BOLD" />Inicio<ph name="END_BOLD" /> y selecciona <ph name="BEGIN_BOLD" />Ver servicios locales<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Selecciona <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />En <ph name="BEGIN_BOLD" />Tipo de inicio<ph name="END_BOLD" />, selecciona <ph name="BEGIN_BOLD" />Deshabilitado<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />En <ph name="BEGIN_BOLD" />Estado del servicio<ph name="END_BOLD" />, haz clic en <ph name="BEGIN_BOLD" />Detener<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Haz clic en <ph name="BEGIN_BOLD" />Aplicar<ph name="END_BOLD" /> y, a continuación, haz clic en <ph name="BEGIN_BOLD" />Aceptar<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Accede al <ph name="BEGIN_LEARN_MORE_LINK" />Centro de Ayuda de Chrome<ph name="END_LEARN_MORE_LINK" /> para obtener más información sobre cómo eliminar el software de tu ordenador de forma permanente.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Tu conexión a este sitio web no es privada. Para salir del modo RV en cualquier momento, quita el visor y pulsa Atrás.</translation>
<translation id="5299298092464848405">Error al analizar la política</translation>
<translation id="5308689395849655368">Notificación de fallos inhabilitada</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Error de validación de esquema en "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">No se puede encontrar esta página (<ph name="HOST_NAME" />)</translation>
<translation id="5455374756549232013">Marca de tiempo de política incorrecta</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> de <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">No válido</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}other{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}}</translation>
<translation id="5470861586879999274">&amp;Rehacer edición</translation>
+<translation id="5481076368049295676">Es posible que este contenido intente instalar software peligroso que robe o elimine tu información en el dispositivo. <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Añadir dirección válida</translation>
<translation id="5492298309214877701">Este sitio web de la intranet del centro educativo, de la organización o de la empresa tiene la misma URL que un sitio web externo.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">No se ha podido encontrar el artículo solicitado.</translation>
<translation id="5544037170328430102">Una página insertada en <ph name="SITE" /> dice:</translation>
<translation id="5556459405103347317">Volver a cargar</translation>
+<translation id="5560088892362098740">Fecha de caducidad</translation>
<translation id="5565735124758917034">Activo</translation>
<translation id="5571083550517324815">Los pedidos no se pueden recoger en esta dirección. Selecciona otra.</translation>
<translation id="5572851009514199876">Abre Chrome e inicia sesión en el navegador para que compruebe si tienes permiso para acceder a este sitio web.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Error al cargar la configuración de la política</translation>
<translation id="5631439013527180824">Token de administración de dispositivos no válido</translation>
<translation id="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="5646376287012673985">Ubicación</translation>
<translation id="5659593005791499971">Correo electrónico</translation>
<translation id="5669703222995421982">Obtener contenido personalizado</translation>
<translation id="5675650730144413517">Esta página no funciona</translation>
<translation id="5710435578057952990">No se ha verificado la identidad de este sitio web.</translation>
-<translation id="5713016350996637505">Contenido engañoso bloqueado</translation>
+<translation id="5719499550583120431">Se aceptan tarjetas prepago.</translation>
<translation id="5720705177508910913">Usuario actual</translation>
<translation id="5732392974455271431">Tus padres pueden desbloquearlo</translation>
<translation id="5763042198335101085">Introduce una dirección de correo electrónico válida</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">¿Quieres rellenar la información de <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Tu conexión con <ph name="DOMAIN" /> está cifrada con un conjunto de cifrado obsoleto.</translation>
<translation id="5813119285467412249">&amp;Rehacer acción de añadir</translation>
-<translation id="5814352347845180253">Es posible que dejes de tener acceso al contenido premium de <ph name="SITE" /> y algunos otros sitios web.</translation>
<translation id="5838278095973806738">No deberías introducir información confidencial en este sitio web (por ejemplo, contraseñas o tarjetas de crédito) porque los atacantes podrían robarla.</translation>
<translation id="5869405914158311789">No se puede acceder a este sitio web</translation>
<translation id="5869522115854928033">Contraseñas guardadas</translation>
<translation id="5872918882028971132">Sugerencias de padres</translation>
+<translation id="5893752035575986141">Se aceptan tarjetas de crédito.</translation>
<translation id="5901630391730855834">Amarillo</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="5926846154125914413">Es posible que dejes de tener acceso al contenido premium de algunos sitios web.</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="5967867314010545767">Eliminar del historial</translation>
<translation id="5975083100439434680">Reducir</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Cerrar</translation>
<translation id="6042308850641462728">Más</translation>
<translation id="6047233362582046994">Si entiendes los riesgos para tu seguridad, puedes <ph name="BEGIN_LINK" />acceder a este sitio web<ph name="END_LINK" /> antes de que se hayan eliminado las aplicaciones dañinas.</translation>
+<translation id="6047927260846328439">Es posible que este contenido intente engañarte para que instales software o reveles información personal. <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">No puedes acceder a <ph name="SITE" /> en este momento porque el sitio web utiliza la fijación de certificados. 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="6060685159320643512">¡Atención! Estos experimentos pueden ser peligrosos</translation>
<translation id="6080696365213338172">Has accedido al contenido mediante un certificado proporcionado por el administrador. Los datos que proporciones a <ph name="DOMAIN" /> pueden ser interceptados por tu administrador.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Más información</translation>
<translation id="6169916984152623906">Ahora puedes navegar de forma privada; las otras personas que usen este dispositivo no verán tu actividad. No obstante, se guardarán las descargas y los marcadores.</translation>
<translation id="6177128806592000436">Tu conexión con este sitio web no es segura</translation>
-<translation id="6184817833369986695">(cohorte: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Comprueba tu conexión a Internet</translation>
<translation id="6218753634732582820">¿Quitar dirección de Chromium?</translation>
<translation id="6221345481584921695">Recientemente, la función de Navegación Segura de Google <ph name="BEGIN_LINK" />ha detectado software malicioso<ph name="END_LINK" /> en <ph name="SITE" />. En ocasiones, los sitios web que normalmente son seguros contienen software malicioso. Este contenido malintencionado procede de <ph name="SUBRESOURCE_HOST" />, un conocido distribuidor de software malicioso.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Si estás interesado en probar nuevas e interesantes funciones de Chrome, prueba nuestro canal beta en la página chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium se ha quedado sin memoria al intentar mostrar esta página web.</translation>
+<translation id="6397451950548600259">Hay software en tu ordenador que impide que Chrome se conecte a la Web de forma segura</translation>
<translation id="6404511346730675251">Editar marcador</translation>
<translation id="6410264514553301377">Introduce la fecha de vencimiento y el código CVC de la tarjeta <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Has solicitado permiso a uno de tus padres para poder acceder a este sitio web</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Políticas de dispositivos</translation>
<translation id="6477321094435799029">Chrome ha detectado un código inusual en esta página y lo ha bloqueado para proteger tu información personal (por ejemplo, contraseñas, números de teléfono y tarjetas de crédito).</translation>
<translation id="6489534406876378309">Empezar a subir errores</translation>
+<translation id="6507833130742554667">Se aceptan tarjetas de crédito y débito.</translation>
<translation id="6508722015517270189">Reinicia Chrome</translation>
<translation id="6529602333819889595">&amp;Rehacer eliminación</translation>
<translation id="6534179046333460208">Sugerencias de la Web física</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Es posible que este contenido intente instalar software peligroso que robe o elimine tu información en el dispositivo. <ph name="BEGIN_LINK" />Mostrar de todos modos<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opciones de cifrado</translation>
<translation id="662080504995468778">Seguir aquí</translation>
<translation id="6626291197371920147">Añadir un número de tarjeta válido</translation>
<translation id="6628463337424475685">Búsqueda de <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">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 Mac 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="6644283850729428850">Esta política está obsoleta.</translation>
+<translation id="6657585470893396449">Contraseña</translation>
<translation id="6671697161687535275">¿Quitar sugerencia de formulario de Chromium?</translation>
<translation id="6685834062052613830">Cierra sesión y completa la configuración</translation>
<translation id="6710213216561001401">Anterior</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Distrito</translation>
<translation id="6973656660372572881">Se especifican tanto servidores proxy fijos como una URL de secuencia de comandos .pac.</translation>
<translation id="6989763994942163495">Mostrar configuración avanzada...</translation>
-<translation id="7000990526846637657">No se han encontrado entradas del historial</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Es posible que en tu cuenta de Google haya otros datos de navegación registrados, que puedes consultar en la página <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Contraseñas</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Los pedidos no se pueden enviar a esta dirección. Selecciona otra.</translation>
<translation id="7138472120740807366">Método de entrega</translation>
<translation id="7139724024395191329">Emirato</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}other{<ph name="PAYMENT_METHOD_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> más}}</translation>
<translation id="7155487117670177674">Pago no seguro</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}other{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}}</translation>
<translation id="7179921470347911571">Reiniciar ahora</translation>
<translation id="7180611975245234373">Actualizar</translation>
<translation id="7182878459783632708">No hay políticas establecidas.</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Descargar</translation>
<translation id="7518003948725431193">No se ha encontrado ninguna página web para la dirección <ph name="URL" />.</translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Tu conexión con este sitio web no es privada</translation>
<translation id="7535087603100972091">Valor</translation>
<translation id="7537536606612762813">Obligatoria</translation>
<translation id="7542403920425041731">Cuando la confirmes, la información de la tarjeta se compartirá con este sitio web.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Prueba los siguientes consejos:</translation>
<translation id="7554791636758816595">Nueva pestaña</translation>
<translation id="7567204685887185387">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, su certificado de seguridad podría haberse emitido de forma fraudulenta. El problema puede deberse a una configuración incorrecta o a que un atacante haya interceptado la conexión.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}other{<ph name="CONTACT_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> más}}</translation>
<translation id="7568593326407688803">Esta página está escrita en<ph name="ORIGINAL_LANGUAGE" />¿Quieres traducirla?</translation>
<translation id="7569952961197462199">¿Eliminar tarjeta de crédito de Chrome?</translation>
<translation id="7569983096843329377">Negro</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificado</translation>
<translation id="7716147886133743102">Bloqueado por el administrador</translation>
<translation id="7716424297397655342">No se puede cargar este sitio web desde la caché</translation>
+<translation id="774634243536837715">Contenido peligroso bloqueado.</translation>
<translation id="7752995774971033316">No administrado</translation>
<translation id="7755287808199759310">Uno de tus padres puede desbloquearlo</translation>
<translation id="7758069387465995638">Puede que el cortafuegos o el software antivirus hayan bloqueado la conexión.</translation>
+<translation id="7759163816903619567">Dominio visible:</translation>
<translation id="7761701407923456692">El certificado del servidor no coincide con la URL.</translation>
<translation id="7763386264682878361">Analizador de archivos de manifiesto de pagos</translation>
<translation id="7764225426217299476">Añadir dirección</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Se han encontrado <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> de <ph name="SEARCH_STRING" /></translation>
<translation id="785549533363645510">Ten en cuenta que tus acciones no serán totalmente invisibles. El uso del modo incógnito no te permite ocultar tu actividad de navegación a tu empresa, a tu proveedor de servicios de Internet o a los sitios web que visites.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Se aceptan tarjetas prepago y de débito.</translation>
<translation id="7887683347370398519">Comprueba el código CVC y vuelve a intentarlo</translation>
<translation id="79338296614623784">Introduce un número de teléfono válido</translation>
<translation id="7935318582918952113">Extractor de DOM</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Enviar</translation>
<translation id="8041940743680923270">Utilizar valor predeterminado global (Preguntar)</translation>
<translation id="8088680233425245692">Se ha producido un error al ver el artículo.</translation>
-<translation id="8089520772729574115">menos de 1 MB</translation>
<translation id="8091372947890762290">La activación está pendiente en el servidor.</translation>
<translation id="8118489163946903409">Método de pago</translation>
<translation id="8131740175452115882">Confirmar</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Deshacer movimiento</translation>
<translation id="8201077131113104583">URL de actualización no válida para la extensión <ph name="EXTENSION_ID" />.</translation>
<translation id="8202097416529803614">Resumen del pedido</translation>
+<translation id="8205463626947051446">El sitio web suele mostrar anuncios invasivos</translation>
<translation id="8218327578424803826">Ubicación asignada:</translation>
<translation id="8225771182978767009">La persona que ha configurado este ordenador ha elegido bloquear este sitio web.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" /> o <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Abre una página en una nueva pestaña de incógnito</translation>
<translation id="8241707690549784388">La página que buscas ha utilizado la información que has especificado. Volver a la página podría provocar la repetición de alguna acción. ¿Quieres continuar?</translation>
+<translation id="8241712895048303527">Bloquear en este sitio web</translation>
<translation id="8249320324621329438">Última comprobación:</translation>
<translation id="8253091569723639551">Se necesita una dirección de facturación</translation>
<translation id="8261506727792406068">Eliminar</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Se ha producido un error de traducción debido a un problema con la conexión de red.</translation>
<translation id="8332188693563227489">Se ha denegado el acceso a <ph name="HOST_NAME" /></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="8344669043927012510">Abre la página en modo incógnito (⇧ + ⌘ + N)</translation>
<translation id="8349305172487531364">Barra de marcadores</translation>
<translation id="8363502534493474904">Desactivar el modo avión</translation>
<translation id="8364627913115013041">No establecida</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Informe sobre fallos subido el <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Fallos (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Debes introducir la misma frase de contraseña dos veces.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ajustes</translation>
<translation id="8433057134996913067">Con esta opción, tu sesión se cerrará en la mayoría de sitios web.</translation>
<translation id="8437238597147034694">&amp;Deshacer movimiento</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Inicia sesión en Chrome para utilizar tarjetas de tu cuenta de Google</translation>
<translation id="8488350697529856933">Aplicable a</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha tardado demasiado tiempo en responder.</translation>
-<translation id="8532105204136943229">Año de caducidad</translation>
+<translation id="8503813439785031346">Nombre de usuario</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="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="8620436878122366504">Tus padres aún no lo han aprobado</translation>
<translation id="8647750283161643317">Restablecer todo a su estado predeterminado</translation>
+<translation id="8660471606262461360">De Google Payments</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="8725066075913043281">Volver a intentarlo</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Acercar</translation>
<translation id="8931333241327730545">¿Quieres guardar esta tarjeta en tu cuenta de Google?</translation>
<translation id="8932102934695377596">Tu reloj está atrasado</translation>
+<translation id="8938939909778640821">Tarjetas prepago y de crédito aceptadas</translation>
<translation id="8971063699422889582">El certificado del servidor ha caducado.</translation>
<translation id="8986494364107987395">Enviar automáticamente estadísticas de uso e informes sobre fallos a Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Bloquear siempre en este sitio</translation>
<translation id="9170848237812810038">&amp;Deshacer</translation>
<translation id="917450738466192189">El certificado del servidor no es válido.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}other{<ph name="SHIPPING_OPTION_PREVIEW" /> y <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> más}}</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>
<translation id="9207861905230894330">Se ha producido un error al añadir el artículo.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">BORRAR FORMULARIO</translation>
<translation id="939736085109172342">Nueva carpeta</translation>
-<translation id="941721044073577244">Parece que no tienes permiso para acceder a este sitio web</translation>
<translation id="969892804517981540">Build oficial</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Ninguno}=1{1 elemento}other{# elementos}}</translation>
+<translation id="981121421437150478">Sin conexión</translation>
<translation id="988159990683914416">Build para desarrolladores</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_et.xtb b/chromium/components/strings/components_strings_et.xtb
index 9ba2036501d..fa05fdfb245 100644
--- a/chromium/components/strings/components_strings_et.xtb
+++ b/chromium/components/strings/components_strings_et.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Sulgege muud rakendused</translation>
<translation id="1055184225775184556">&amp;Võta lisamine tagasi</translation>
<translation id="10614374240317010">Ei ole kunagi salvestatud</translation>
+<translation id="1066396345355680611">Võite kaotada juurdepääsu saidi <ph name="SITE" /> ja mõne muu saidi kaitstud sisule.</translation>
<translation id="106701514854093668">Töölaua järjehoidjad</translation>
<translation id="1074497978438210769">Pole turvaline</translation>
<translation id="1080116354587839789">Sobita laiusesse</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lugemisloend</translation>
<translation id="1264126396475825575">Krahhiaruanne talletati <ph name="CRASH_TIME" /> (ei ole veel üles laaditud ega eiratud)</translation>
<translation id="1281526147609854549">Väljaandja: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Ohtlik sisu blokeeriti</translation>
<translation id="1285320974508926690">Ära kunagi seda saiti tõlgi</translation>
<translation id="129553762522093515">Viimati suletud</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Kustutage küpsisefailid<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Registreerimisdomeen:</translation>
<translation id="1340482604681802745">Kättesaamise aadress</translation>
-<translation id="1344211575059133124">Näib, et vajate saidi külastamiseks luba</translation>
<translation id="1344588688991793829">Chromiumi automaattäite seaded ...</translation>
<translation id="1348198688976932919">Sait, mille soovite avada, sisaldab ohtlikke rakendusi</translation>
<translation id="1374468813861204354">soovitused</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="ORGANIZATION" /> identiteeti leheküljel <ph name="LOCALITY" /> kinnitas <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Jah</translation>
<translation id="1430915738399379752">Printimine</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Lehe salvestatud (s.t teadaolevalt aegunud) koopia kuvamine.</translation>
<translation id="1517433312004943670">Telefoninumber on nõutav</translation>
+<translation id="1517500485252541695">Aktsepteeritavad krediit- ja deebetkaardid</translation>
<translation id="1519264250979466059">Järgu kuupäev</translation>
+<translation id="1527263332363067270">Ühenduse ootamine …</translation>
<translation id="153384715582417236">See on praeguseks kõik</translation>
<translation id="1549470594296187301">Selle funktsiooni kasutamiseks peab JavaScript olema lubatud.</translation>
<translation id="1555130319947370107">Sinine</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Proovige ühendust võtta süsteemiadministraatoriga.</translation>
<translation id="1740951997222943430">Sisestage kehtiv aegumiskuu</translation>
-<translation id="1745358365027406341">Laadi leht hiljem alla</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Kohustuslik väli</translation>
<translation id="187918866476621466">Ava käivitamisel avatavad lehed</translation>
<translation id="1883255238294161206">Ahenda loend</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtreerimine</translation>
+<translation id="1916770123977586577">Laadige leht uuesti, et rakendada värskendatud seaded saidile</translation>
+<translation id="1919345977826869612">Reklaamid</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Ühtegi}=1{1 sait}other{# saiti}}</translation>
<translation id="194030505837763158">Avage <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Aktsepteeritavad ettemakstud kaardid</translation>
<translation id="1962204205936693436">Domeeni <ph name="DOMAIN" /> järjehoidjad</translation>
<translation id="1973335181906896915">Viga jadaks teisendamisel</translation>
<translation id="1974060860693918893">Täpsemad</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP viga</translation>
<translation id="2270484714375784793">Telefoninumber</translation>
<translation id="2282872951544483773">Kättesaamatud katsetused</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> üksus}other{<ph name="ITEM_COUNT" /> üksust}}</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>
<translation id="2337852623177822836">Seadet juhib administraator</translation>
<translation id="2354001756790975382">Muud järjehoidjad</translation>
<translation id="2354430244986887761">Google'i ohutu sirvimine leidis hiljuti saidilt <ph name="SITE" /> <ph name="BEGIN_LINK" />ohtlikke rakendusi<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Jätka</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> talletatud krahhiaruannet ei laaditud üles</translation>
<translation id="2367567093518048410">Tase</translation>
-<translation id="237718015863234333">Kasutajaliidese alternatiive pole saadaval</translation>
<translation id="2384307209577226199">Ettevõtte vaikeseade</translation>
<translation id="2386255080630008482">Serveri sertifikaat on tühistatud.</translation>
<translation id="2392959068659972793">Kuva reeglid, mille väärtusi pole määratud</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Kui see on märgitud, salvestab Chromium teie kaardi koopia vormide kiiremaks täitmiseks sellesse seadmesse.</translation>
<translation id="2498091847651709837">Uue kaardi skannimine</translation>
<translation id="2501278716633472235">Mine tagasi</translation>
+<translation id="2503184589641749290">Aktsepteeritavad ettemakstud ja deebetkaardid</translation>
<translation id="2515629240566999685">Kontrollige oma piirkonna signaali</translation>
-<translation id="2516305470678292029">Kasutajaliidese alternatiivid</translation>
<translation id="2539524384386349900">Tuvasta</translation>
<translation id="255002559098805027">Host <ph name="HOST_NAME" /> saatis sobimatu vastuse.</translation>
<translation id="2556876185419854533">&amp;Võta muudatus tagasi</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Tarneviis</translation>
<translation id="277499241957683684">Seadme kirje puudub</translation>
<translation id="2784949926578158345">Ühendus lähtestati.</translation>
+<translation id="2788784517760473862">Aktsepteeritavad krediitkaardid</translation>
<translation id="2794233252405721443">Sait on blokeeritud</translation>
<translation id="2799020568854403057">Sait, mille soovite avada, sisaldab kahjulikke rakendusi</translation>
<translation id="2803306138276472711">Google'i ohutu sirvimise teenus <ph name="BEGIN_LINK" />tuvastas hiljuti pahavara<ph name="END_LINK" /> saidil <ph name="SITE" />. Tavaliselt turvalisi veebisaite võidakse mõnikord nakatada pahavaraga.</translation>
<translation id="2824775600643448204">Aadressi- ja otsinguriba</translation>
<translation id="2826760142808435982">Ühendus on krüptitud ja autenditud üksusega <ph name="CIPHER" /> ning kasutab peamise vahetusmehhanismina üksust <ph name="KX" />.</translation>
<translation id="2835170189407361413">Tühjenda vorm</translation>
+<translation id="2851634818064021665">Vajate selle saidi külastamiseks luba</translation>
<translation id="2856444702002559011">Ründajad võivad üritada varastada teie teavet (nt paroole, sõnumeid või krediitkaarditeavet) saidilt <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />. <ph name="BEGIN_LEARN_MORE_LINK" />Lisateave<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Ära laadi uuesti</translation>
-<translation id="2900469785430194048">Google Chrome'il pole selle veebilehe kuvamiseks piisavalt mälu.</translation>
<translation id="2909946352844186028">Tuvastati võrgumuudatus.</translation>
<translation id="2916038427272391327">Sulgege muud programmid</translation>
<translation id="2922350208395188000">Serveri sertifikaati ei saa kontrollida.</translation>
<translation id="2928905813689894207">Arveldusaadress</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />, selle turvasertifikaati pärineb domeenilt <ph name="DOMAIN2" />. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
<translation id="2948083400971632585">Seadete lehel saate keelata kõik ühenduse jaoks konfigureeritud puhverserverid.</translation>
<translation id="2955913368246107853">Sule leiuriba</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Kui see on lubatud, salvestab Chromium teie kaardi koopia vormide kiiremaks täitmiseks sellesse seadmesse.</translation>
<translation id="2985398929374701810">Sisestage kehtiv aadress</translation>
<translation id="2986368408720340940">See kättesaamisviis pole saadaval. Proovige mõnda teist kättesaamisviisi.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Inkognito vahelehtedel kuvatavaid lehti ei talletata pärast vahelehtede sulgemist brauseri ajalukku, küpsistefailide salve ega otsinguajalukku. Allalaaditavad failid ja järjehoidjatesse lisatud sisu säilitatakse.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Saar</translation>
-<translation id="317583078218509884">Saidi uute lubade seaded jõustuvad pärast lehe uuesti laadimist.</translation>
<translation id="3176929007561373547">Kontrollige puhverserveri seadeid või võtke ühendust võrguadministraatoriga
ja veenduge, et puhverserver töötaks. Kui arvate, et teil ei ole vaja
puhverserverit kasutada:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Avage leht inkognito režiimis</translation>
<translation id="320323717674993345">Tühista makse</translation>
<translation id="3207960819495026254">Järjehoidjatesse lisatud</translation>
<translation id="3225919329040284222">Serveri esitatud sertifikaat ei vasta sisseehitatud ootustele. Ootused on teie kaitsmiseks kaasatud kindlate kõrge turvalisusega veebisaitide jaoks.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Võta korrastamine tagasi</translation>
<translation id="3282497668470633863">Kaardil oleva nime lisamine</translation>
<translation id="3286538390144397061">Taaskäivitada kohe</translation>
+<translation id="3287510313208355388">Laadi alla, kui ühendus on saadaval</translation>
<translation id="3303855915957856445">Otsingutulemusi ei leitud</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Hankimise intervall:</translation>
<translation id="3462200631372590220">Peida täpsemad üksikasjad</translation>
<translation id="3467763166455606212">Kaardiomaniku nimi on kohustuslik</translation>
-<translation id="3478058380795961209">Aegumiskuu</translation>
<translation id="3479539252931486093">Kas see oli ootamatu? <ph name="BEGIN_LINK" />Andke meile sellest teada<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Mitte praegu</translation>
<translation id="3498215018399854026">Teie vanemaga ei õnnestunud praegu ühendust võtta. Proovige hiljem uuesti.</translation>
@@ -343,6 +345,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Lisateave<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kaupmees aktsepteerib ettemakstud ja krediitkaarte.</translation>
<translation id="3582930987043644930">Lisage nimi</translation>
<translation id="3583757800736429874">&amp;Teisalda uuesti</translation>
<translation id="3586931643579894722">Peida üksikasjad</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Kui näete seda sageli, proovige järgmist: <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Redaktsioon</translation>
<translation id="3678029195006412963">Taotlust ei saanud allkirjastada</translation>
+<translation id="3678529606614285348">Avage leht uues inkognito aknas (Ctrl + tõstuklahv + N)</translation>
<translation id="3679803492151881375">Krahhiaruanne jäädvustati ajal <ph name="CRASH_TIME" />, see laaditi üles ajal <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Sertifikaadi andmed</translation>
<translation id="3690164694835360974">Sisselogimine pole turvaline</translation>
-<translation id="3693415264595406141">Parool:</translation>
<translation id="3704609568417268905"><ph name="TIME" />, <ph name="BOOKMARKED" />, <ph name="TITLE" />, <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Laadimine...</translation>
<translation id="3712624925041724820">Litsentsid on ammendunud</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> asuvad ründajad võivad teid meelitada ohtlikele tegevustele, nagu tarkvara installimine või isiklike andmete (nt paroolid, telefoninumbrid või krediitkaarditeave) avaldamine. <ph name="BEGIN_LEARN_MORE_LINK" />Lisateave<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Tõlkimine ebaõnnestus serverivea tõttu.</translation>
<translation id="3759461132968374835">Hiljuti teatatud krahhe ei ole. Siin ei ilmu krahhid, mis toimusid siis, kui krahhide aruandlus oli keelatud.</translation>
+<translation id="3765032636089507299">Google'i ohutu sirvimise leht on loomisel.</translation>
<translation id="3778403066972421603">Kas soovite selle kaardi salvestada oma Google'i kontole ja sellesse seadmesse?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Aegub: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Teie juurdepääsutaotlus sellele saidile saadeti kasutajale <ph name="NAME" /></translation>
<translation id="3890664840433101773">E-posti aadressi lisamine</translation>
<translation id="3901925938762663762">Kaart on aegunud</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Üleslaaditud krahhiaruande ID <ph name="CRASH_ID" /> (kohaliku krahhi ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />; selle turvasertifikaat ei määra laiendust Subject Alternative Names. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
+<translation id="3949601375789751990">Siin kuvatakse teie sirvimisajalugu</translation>
<translation id="3963721102035795474">Lugejarežiim</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Ükski}=1{1 saidilt }other{# saidilt }}</translation>
<translation id="397105322502079400">Arvutamine ...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Saidi seaded</translation>
<translation id="4169947484918424451">Kas soovite, et Chromium salvestaks selle kaardi?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Saidil 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 krediitkaardiandmed).</translation>
<translation id="4372948949327679948">Oodatud väärtus: <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Püüdsite jõuda saidile <ph name="DOMAIN" />, kuid sertifikaadi väljaandja on serveri esitatud sertifikaadi tagasi võtnud. See tähendab, et serveri esitatud turvamandaate ei tohiks mingil juhul usaldada. Võimalik, et suhtlete ründajaga.</translation>
-<translation id="4381091992796011497">Kasutajanimi:</translation>
<translation id="4394049700291259645">Keela</translation>
<translation id="4406896451731180161">otsingutulemused</translation>
<translation id="4424024547088906515">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />, Chrome ei usalda selle turvasertifikaati. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
<translation id="4432688616882109544">Host <ph name="HOST_NAME" /> ei aktsepteerinud teie sisselogimise sertifikaati või te ei esitanud seda.</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="4506176782989081258">Valideerimisviga: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Võtke ühendust süsteemiadministraatoriga</translation>
<translation id="450710068430902550">Administraatoriga jagamine</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Kas eemaldada Chrome'ist aadress?</translation>
<translation id="4592951414987517459">Teie ühendus domeeniga <ph name="DOMAIN" /> on krüpteeritud tänapäevase šifreerimiskomplektiga.</translation>
<translation id="4594403342090139922">&amp;Võta kustutamine tagasi</translation>
+<translation id="4611292653554630842">Logi sisse</translation>
<translation id="4619615317237390068">Muudest seadmetest pärinevad vahelehed</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />, selle turvasertifikaat sisaldab vigu. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
<translation id="4690462567478992370">Lõpeta kehtetu sertifikaadi kasutamine</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Teie ühendus katkes</translation>
+<translation id="471880041731876836">Teil ei ole selle saidi külastamiseks luba</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windowsi võrgudiagnostika käitamine<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Laadi reeglid uuesti</translation>
<translation id="4728558894243024398">Platvorm</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">See sisu võib meelitada teid installima tarkvara või avaldama isiklikke andmeid. <ph name="BEGIN_LINK" />Kuva ikkagi<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Otsi ajaloost</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Vajalik on autentimine</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ja veel 1 veebileht}other{ja veel # veebilehte}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Leht on tõlgitud teadmata keelest <ph name="LANGUAGE_LANGUAGE" /> keelde</translation>
<translation id="4923459931733593730">Makse</translation>
<translation id="4926049483395192435">Tuleb määrata.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Toimingud</translation>
<translation id="4958444002117714549">Laienda loendit</translation>
<translation id="4974590756084640048">Luba hoiatused uuesti</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Vale salasõna</translation>
<translation id="5056549851600133418">Teile soovitatud artiklid</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Kontrollige puhverserveri aadressi<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Võite kaotada juurdepääsu mõne saidi kaitstud sisule.</translation>
<translation id="5087286274860437796">Serveri sertifikaat pole praegu kehtiv.</translation>
<translation id="5087580092889165836">Lisa kaart</translation>
<translation id="5089810972385038852">Osariik</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provints</translation>
<translation id="5115563688576182185">(64-bitine)</translation>
<translation id="5141240743006678641">Krüpteerige sünkroonitud paroolid oma Google'i mandaadiga</translation>
-<translation id="514421653919133810">Avage leht inkognito režiimis (Ctrl + Tõstuklahv + N)</translation>
<translation id="5145883236150621069">Reegli vastuses sisaldus veakood</translation>
+<translation id="5159010409087891077">Avage leht uues inkognito aknas (⇧ ⌘ N)</translation>
<translation id="5171045022955879922">Otsige või sisestage URL</translation>
<translation id="5172758083709347301">Masin</translation>
<translation id="5179510805599951267">Tegu ei ole <ph name="ORIGINAL_LANGUAGE" /> keelega? Andke veast teada</translation>
-<translation id="5181140330217080051">Allalaadimine</translation>
<translation id="5190835502935405962">Järjehoidjariba</translation>
<translation id="5199729219167945352">Katsed</translation>
<translation id="5205222826937269299">Nimi on nõutav</translation>
<translation id="5222812217790122047">E-posti aadress on nõutav</translation>
<translation id="5251803541071282808">Pilv</translation>
<translation id="5277279256032773186">Kas kasutate Chrome'i tööl? Ettevõtted võivad hallata töötajate Chrome'i seadeid. Lisateave</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Tarkvara ajutiseks keelamiseks ja veebi pääsemiseks järgige neid toiminguid. Teil on vaja administraatoriõigusi.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klõpsake valikul <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, seejärel otsige üksust <ph name="BEGIN_BOLD" />„View local services”<ph name="END_BOLD" /> ja valige see
+ <ph name="LIST_ITEM" />Valige <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Jaotises <ph name="BEGIN_BOLD" />Startup type<ph name="END_BOLD" /> valige <ph name="BEGIN_BOLD" />Disabled<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Jaotises <ph name="BEGIN_BOLD" />Service status<ph name="END_BOLD" /> klõpsake käsul <ph name="BEGIN_BOLD" />Stop<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Klõpsake käsul <ph name="BEGIN_BOLD" />Apply<ph name="END_BOLD" /> ja seejärel <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Vaadake <ph name="BEGIN_LEARN_MORE_LINK" />Chrome'i abikeskusest<ph name="END_LEARN_MORE_LINK" /> teavet selle kohta, kuidas tarkvara arvutist jäädavalt eemaldada
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Teie ühendus saidiga ei ole privaatne. VR-režiimist väljumiseks võite igal ajal eemaldada peakomplekti ja vajutada tagasinuppu.</translation>
<translation id="5299298092464848405">Reegli sõelumisel ilmnes viga</translation>
<translation id="5308689395849655368">Krahhide aruandlus on keelatud.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Skeemi valideerimise viga asukohas „<ph name="ERROR_PATH" />”: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Hosti <ph name="HOST_NAME" /> lehte ei leita</translation>
<translation id="5455374756549232013">Reegli ajatempel on sobimatu</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> / <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Kehtetu</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Muuda uuesti</translation>
+<translation id="5481076368049295676">See sisu võib üritada installida teie seadmesse ohtlikku tarkvara, mis varastab või kustutab teie teavet. <ph name="BEGIN_LINK" />Kuva ikkagi<ph name="END_LINK" />.</translation>
<translation id="54817484435770891">Sisestage sobiv aadress</translation>
<translation id="5492298309214877701">Sellel ettevõtte, organisatsiooni või kooli intranetis oleval saidil on sama URL mis välisel veebisaidil.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Taotletud artiklit ei õnnestunud leida.</translation>
<translation id="5544037170328430102">Manustatud leht saidil <ph name="SITE" /> ütleb:</translation>
<translation id="5556459405103347317">Laadi uuesti</translation>
+<translation id="5560088892362098740">Aegumiskuupäev</translation>
<translation id="5565735124758917034">Aktiivne</translation>
<translation id="5571083550517324815">Sellelt aadressilt ei saa kaupa kätte. Valige mõni teine aadress.</translation>
<translation id="5572851009514199876">Alustage ja logige Chrome'i sisse, et Chrome saaks kontrollida, kas teil on luba sellele saidile juurdepääsemiseks.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Reegli seadete laadimine ebaõnnestus</translation>
<translation id="5631439013527180824">Seadme halduse luba on kehtetu</translation>
<translation id="5633066919399395251">Saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> olevad ründajad võivad proovida installida teie arvutisse ohtlikke programme, mis varastavad teie teavet või kustutavad selle (nt fotod, paroolid, sõnumid ja krediitkaarditeave). <ph name="BEGIN_LEARN_MORE_LINK" />Lisateave<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Petlik sisu blokeeriti.</translation>
<translation id="5646376287012673985">Asukoht</translation>
<translation id="5659593005791499971">Meil</translation>
<translation id="5669703222995421982">Isikupärastatud sisu hankimine</translation>
<translation id="5675650730144413517">See leht ei tööta</translation>
<translation id="5710435578057952990">Selle veebisaidi identiteeti pole kinnitanud.</translation>
-<translation id="5713016350996637505">Petlik sisu blokeeriti</translation>
+<translation id="5719499550583120431">Kaupmees aktsepteerib ettemakstud kaarte.</translation>
<translation id="5720705177508910913">Praegune kasutaja</translation>
<translation id="5732392974455271431">Vanemad saavad blokeeringu teie eest tühistada</translation>
<translation id="5763042198335101085">Sisestage kehtiv e-posti aadress</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Kas soovite sisestada kirje <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Teie ühendus domeeniga <ph name="DOMAIN" /> on krüpteeritud aegunud šifreerimiskomplektiga.</translation>
<translation id="5813119285467412249">&amp;Lisa uuesti</translation>
-<translation id="5814352347845180253">Võite kaotada juurdepääsu saidi <ph name="SITE" /> ja mõnede muude saitide tasulisele sisule.</translation>
<translation id="5838278095973806738">Te ei tohiks sellele saidile sisestada tundlikku teavet (nt paroolid või krediitkaardid), kuna ründajad võivad selle varastada.</translation>
<translation id="5869405914158311789">Selle saidiga ei saa ühendust</translation>
<translation id="5869522115854928033">Salvestatud paroolid</translation>
<translation id="5872918882028971132">Vanema soovitused</translation>
+<translation id="5893752035575986141">Kaupmees aktsepteerib krediitkaarte.</translation>
<translation id="5901630391730855834">Kollane</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="5926846154125914413">Võite kaotada juurdepääsu mõnede saitide tasulisele sisule.</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="5967867314010545767">Eemalda ajaloost</translation>
<translation id="5975083100439434680">Suumib välja</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Sulge</translation>
<translation id="6042308850641462728">Rohkem</translation>
<translation id="6047233362582046994">Kui mõistate, kuidas see teie turvalisust ohustab, siis võite <ph name="BEGIN_LINK" />seda saiti külastada<ph name="END_LINK" /> enne, kui kahjulikud rakendused on eemaldatud.</translation>
+<translation id="6047927260846328439">See sisu võib meelitada teid installima tarkvara või avaldama isiklikke andmeid. <ph name="BEGIN_LINK" />Kuva ikkagi<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Te ei saa saiti <ph name="SITE" /> praegu külastada, sest veebisait kasutab sertifikaadi kinnitamist. Võrguvead ja -rünnakud on tavaliselt ajutised, nii et leht tõenäoliselt hiljem töötab.</translation>
<translation id="6060685159320643512">Ettevaatust, need katsed võivad hammustada.</translation>
<translation id="6080696365213338172">Olete sisule juurde pääsenud administraatori antud sertifikaadiga. Administraator saab domeenile <ph name="DOMAIN" /> edastatavaid andmeid kinni pidada.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Lisateave</translation>
<translation id="6169916984152623906">Nüüd saate sirvida privaatselt ja teised seadme kasutajad ei näe teie tegevusi. Allalaaditud failid ja järjehoidjad siiski salvestatakse.</translation>
<translation id="6177128806592000436">Teie ühendus selle saidiga pole turvaline</translation>
-<translation id="6184817833369986695">(rühm: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Kontrollige Interneti-ühendust</translation>
<translation id="6218753634732582820">Kas eemaldada Chromiumist aadress?</translation>
<translation id="6221345481584921695">Google'i ohutu sirvimise teenus <ph name="BEGIN_LINK" />tuvastas hiljuti pahavara<ph name="END_LINK" /> saidil <ph name="SITE" />. Tavaliselt turvalisi veebisaite võidakse mõnikord nakatada pahavaraga. Pahatahtlik sisu pärineb hostilt <ph name="SUBRESOURCE_HOST" />, mis on tuntud pahavara levitaja.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Kas olete huvitatud Chrome'i uutest lahedatest funktsioonidest? Proovige meie beetakanalit aadressil chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromiumil pole selle veebilehe kuvamiseks piisavalt mälu.</translation>
+<translation id="6397451950548600259">Teie arvutis olev tarkvara ei luba Chrome'il veebiga ohutult ühendust luua</translation>
<translation id="6404511346730675251">Muuda järjehoidjat</translation>
<translation id="6410264514553301377">Sisestage krediitkaardi <ph name="CREDIT_CARD" /> aegumiskuupäev ja CVC</translation>
<translation id="6414888972213066896">Küsisite vanemalt, kas võite seda saiti külastada</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Seadme reeglid</translation>
<translation id="6477321094435799029">Chrome tuvastas sellel lehel ebatavalise koodi ja blokeeris selle, et teie isiklikke andmeid (nt paroolid, telefoninumbrid ja krediitkaardiandmed) kaitsta.</translation>
<translation id="6489534406876378309">Krahhide üleslaadimise alustamine</translation>
+<translation id="6507833130742554667">Kaupmees aktsepteerib krediit- ja deebetkaarte.</translation>
<translation id="6508722015517270189">Taaskäivitage Chrome</translation>
<translation id="6529602333819889595">&amp;Kustuta uuesti</translation>
<translation id="6534179046333460208">Füüsilise veebi soovitused</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">See sisu võib üritada installida teie seadmesse ohtlikku tarkvara, mis teie teavet varastab või selle kustutab. <ph name="BEGIN_LINK" />Kuva ikkagi<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Krüpteerimise valikud</translation>
<translation id="662080504995468778">Jää siia</translation>
<translation id="6626291197371920147">Kehtiva kaardinumbri lisamine</translation>
<translation id="6628463337424475685"><ph name="ENGINE" />'i otsing</translation>
<translation id="6630809736994426279">Saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> olevad ründajad võivad proovida installida teie Maci 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="6644283850729428850">See reegel on aegunud.</translation>
+<translation id="6657585470893396449">Parool</translation>
<translation id="6671697161687535275">Kas eemaldada Chromiumist vormi soovitus?</translation>
<translation id="6685834062052613830">Logige välja ja viige seadistus lõpule</translation>
<translation id="6710213216561001401">Eelmine</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Ringkond</translation>
<translation id="6973656660372572881">Määratud on nii fikseeritud puhverserverid kui ka pac-skriptiga URL.</translation>
<translation id="6989763994942163495">Kuva täpsemad seaded ...</translation>
-<translation id="7000990526846637657">Ajalookirjeid ei leitud</translation>
<translation id="7012363358306927923">Hiina UnionPay</translation>
<translation id="7012372675181957985">Aadressil <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> võib teie Google'i kontol olla muus vormis sirvimisajalugu</translation>
<translation id="7029809446516969842">Paroolid</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Sellele aadressile ei saa tarnida. Valige mõni teine aadress.</translation>
<translation id="7138472120740807366">Kohaletoimetamisviis</translation>
<translation id="7139724024395191329">Emiraat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Makse pole turvaline</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Taaskäivita kohe</translation>
<translation id="7180611975245234373">Värskenda</translation>
<translation id="7182878459783632708">Reegleid pole määratud</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Laadi alla</translation>
<translation id="7518003948725431193">Järgneval veebiaadressil ei olnud ühtegi veebilehte: <ph name="URL" /></translation>
<translation id="7521387064766892559">Javascript</translation>
+<translation id="7526934274050461096">Teie ühendus selle saidiga pole privaatne</translation>
<translation id="7535087603100972091">Väärtus</translation>
<translation id="7537536606612762813">Kohustuslik</translation>
<translation id="7542403920425041731">Kui selle kinnitate, jagatakse teie kaardi üksikasju selle saidiga.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Proovige järgmiseid nõuandeid.</translation>
<translation id="7554791636758816595">Uus vaheleht</translation>
<translation id="7567204685887185387">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />, selle turvasertifikaat võib olla väljastatud pettuse teel. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">See leht on keeles<ph name="ORIGINAL_LANGUAGE" />Kas soovite seda tõlkida?</translation>
<translation id="7569952961197462199">Kas eemaldada Chrome'ist krediitkaart?</translation>
<translation id="7569983096843329377">Must</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Sertifikaat</translation>
<translation id="7716147886133743102">Blokeeris administraator</translation>
<translation id="7716424297397655342">Seda saiti ei saa vahemälust laadida</translation>
+<translation id="774634243536837715">Ohtlik sisu blokeeriti.</translation>
<translation id="7752995774971033316">Haldamata</translation>
<translation id="7755287808199759310">Vanem saab blokeeringu teie eest tühistada</translation>
<translation id="7758069387465995638">Tulemüür või viirusetõrjetarkvara võis ühenduse blokeerida.</translation>
+<translation id="7759163816903619567">Kuvatav domeen:</translation>
<translation id="7761701407923456692">Serveri sertifikaat ei vasta URL-ile.</translation>
<translation id="7763386264682878361">Makse manifesti parser</translation>
<translation id="7764225426217299476">Lisage aadress</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Otsingule „<ph name="SEARCH_STRING" />” leiti <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" />.</translation>
<translation id="785549533363645510">Te pole siiski nähtamatu. Inkognito režiimi kasutamine ei varja teie sirvimist tööandja, Interneti-teenuse pakkuja ega külastatavate veebisaitide eest.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Kaupmees aktsepteerib ettemakstud ja deebetkaarte.</translation>
<translation id="7887683347370398519">Kontrollige CVC-d ja proovige uuesti</translation>
<translation id="79338296614623784">Sisestage kehtiv telefoninumber</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Saada tagasiside</translation>
<translation id="8041940743680923270">Kasuta globaalset vaikeseadet (küsi)</translation>
<translation id="8088680233425245692">Artikli kuvamine ebaõnnestus.</translation>
-<translation id="8089520772729574115">vähem kui 1 MB</translation>
<translation id="8091372947890762290">Aktiveerimine on serveris ootel</translation>
<translation id="8118489163946903409">Makseviis</translation>
<translation id="8131740175452115882">Kinnita</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Võta teisaldamine tagasi</translation>
<translation id="8201077131113104583">ID-ga „<ph name="EXTENSION_ID" />” laienduse kehtetu värskendamise URL.</translation>
<translation id="8202097416529803614">Tellimuse kokkuvõte</translation>
+<translation id="8205463626947051446">Sait näitab sekkuvaid reklaame</translation>
<translation id="8218327578424803826">Määratud asukoht:</translation>
<translation id="8225771182978767009">Arvuti seadistanud inimene blokeeris selle saidi.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Avage leht uuel inkognito vahekaardil</translation>
<translation id="8241707690549784388">Teie poolt otsitav lehekülg kasutas teie sisestatud andmeid. Sellele leheküljele naasmine võib kaasa tuua kõikide sooritatud tegevuste kordamise. Soovite jätkata?</translation>
+<translation id="8241712895048303527">Blokeeri sellel saidil</translation>
<translation id="8249320324621329438">Viimati toodud:</translation>
<translation id="8253091569723639551">Arveldusaadress on nõutav</translation>
<translation id="8261506727792406068">Kustuta</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Tõlkimine ebaõnnestus võrguühenduse probleemi tõttu.</translation>
<translation id="8332188693563227489">Juurdepääs hostile <ph name="HOST_NAME" /> blokeeriti</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="8344669043927012510">Avage leht inkognito režiimis (⇧ + ⌘ + N)</translation>
<translation id="8349305172487531364">Järjehoidjariba</translation>
<translation id="8363502534493474904">Lülitage lennurežiim välja</translation>
<translation id="8364627913115013041">Määramata.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Krahhiaruanne laaditi üles ajal <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Krahhid (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Peate sisestama sama parooli kaks korda.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Seaded</translation>
<translation id="8433057134996913067">See logib teid välja enamikult veebisaitidelt.</translation>
<translation id="8437238597147034694">&amp;Võta teisaldamine tagasi</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Google'i konto kaartide kasutamiseks logige sisse Chrome'i</translation>
<translation id="8488350697529856933">Kehtib:</translation>
<translation id="8498891568109133222">Hostil <ph name="HOST_NAME" /> kulus vastamiseks liiga kaua aega.</translation>
-<translation id="8532105204136943229">Aegumisaasta</translation>
+<translation id="8503813439785031346">Kasutajanimi</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="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="8620436878122366504">Vanemad ei ole seda veel kinnitanud</translation>
<translation id="8647750283161643317">Lähtesta kõik vaikeolekusse</translation>
+<translation id="8660471606262461360">Teenusest Google Payments</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="8725066075913043281">Proovi uuesti</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Suurendab</translation>
<translation id="8931333241327730545">Kas soovite selle kaardi salvestada oma Google'i kontole?</translation>
<translation id="8932102934695377596">Teie kell on taga</translation>
+<translation id="8938939909778640821">Aktsepteeritavad ettemakstud ja krediitkaardid</translation>
<translation id="8971063699422889582">Serveri sertifikaat on aegunud.</translation>
<translation id="8986494364107987395">Saada kasutusstatistika ja krahhiaruanded automaatselt Google'ile</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Blokeeri sellel saidil alati</translation>
<translation id="9170848237812810038">&amp;Võta tagasi</translation>
<translation id="917450738466192189">Serveri sertifikaat on kehtetu.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ja veel <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Artikli lisamine ebaõnnestus.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">TÜHJENDA VORM</translation>
<translation id="939736085109172342">Uus kaust</translation>
-<translation id="941721044073577244">Näib, et teil pole luba selle saidi külastamiseks</translation>
<translation id="969892804517981540">Ametlik järk</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Ühtegi}=1{1 üksus}other{# üksust}}</translation>
+<translation id="981121421437150478">Võrguühenduseta</translation>
<translation id="988159990683914416">Arendaja järk</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_fa.xtb b/chromium/components/strings/components_strings_fa.xtb
index 77036746cf5..d0b5a95e6de 100644
--- a/chromium/components/strings/components_strings_fa.xtb
+++ b/chromium/components/strings/components_strings_fa.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">برنامه‌های دیگر را ببندید</translation>
<translation id="1055184225775184556">&amp;واگرد افزودن</translation>
<translation id="10614374240317010">هرگز ذخیره نمی‌شود</translation>
+<translation id="1066396345355680611">ممکن است دسترسی به محتوای محافظت‌شده <ph name="SITE" /> و برخی دیگر از سایت‌ها را از دست بدهید.</translation>
<translation id="106701514854093668">نشانک‌های دسک‌تاپ</translation>
<translation id="1074497978438210769">امن نیست</translation>
<translation id="1080116354587839789">متناسب با پهنا</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">فهرست خواندن</translation>
<translation id="1264126396475825575">گزارش خرابی ثبت‌شده در <ph name="CRASH_TIME" /> (هنوز بارگذاری نشده است یا نادیده‌ گرفته شده است)</translation>
<translation id="1281526147609854549">صادرشده توسط <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">محتوای خطرناک مسدود شد</translation>
<translation id="1285320974508926690">این سایت هرگز ترجمه نشود</translation>
<translation id="129553762522093515">اخیراً بسته‌شده</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />کوکی‌ها را پاک کنید<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">دامنه ثبت‌نام:</translation>
<translation id="1340482604681802745">نشانی تحویل گرفتن کالا</translation>
-<translation id="1344211575059133124">گویا برای بازدید این سایت به اجازه نیاز دارید</translation>
<translation id="1344588688991793829">‏تنظیمات تکمیل خودکار Chromium...</translation>
<translation id="1348198688976932919">سایت پیش‌رو حاوی برنامه‌های خطرناک است</translation>
<translation id="1374468813861204354">پیشنهادات</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">هویت <ph name="ORGANIZATION" /> در <ph name="LOCALITY" /> توسط <ph name="ISSUER" /> تأیید شده است.</translation>
<translation id="1426410128494586442">بله</translation>
<translation id="1430915738399379752">چاپ</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> روش دیگر}one{<ph name="PAYMENT_METHOD_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> روش دیگر}other{<ph name="PAYMENT_METHOD_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> روش دیگر}}</translation>
<translation id="1506687042165942984">یک نسخه ذخیره‌شده (یعنی قدیمی) از این صفحه نشان داده شود.</translation>
<translation id="1517433312004943670">شماره تلفن ضروری است</translation>
+<translation id="1517500485252541695">کارت‌های اعتباری و نقدی قابل‌قبول</translation>
<translation id="1519264250979466059">تاریخ ساخت</translation>
+<translation id="1527263332363067270">درانتظار برقراری اتصال…‏</translation>
<translation id="153384715582417236">درحال‌حاضر مورد دیگری وجود ندارد</translation>
<translation id="1549470594296187301">برای استفاده از این قابلیت، جاوا اسکریپت باید فعال باشد.</translation>
<translation id="1555130319947370107">آبی</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">با سرپرست سیستم تماس بگیرید.</translation>
<translation id="1740951997222943430">ماه انقضای معتبری وارد کنید</translation>
-<translation id="1745358365027406341">بارگیری صفحه در فرصت دیگری</translation>
<translation id="17513872634828108">بازکردن برگه‌ها</translation>
<translation id="1753706481035618306">شماره صفحه</translation>
<translation id="1763864636252898013">این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ گواهی امنیتی آن مورداعتماد سیستم عامل دستگاه شما نیست. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجی اتصال شما را قطع کرده است.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">قسمت الزامی</translation>
<translation id="187918866476621466">باز کردن صفحه‌های شروع</translation>
<translation id="1883255238294161206">کوچک کردن فهرست</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> نشانی دیگر}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> نشانی دیگر}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> نشانی دیگر}}</translation>
<translation id="1898423065542865115">فیلتر کردن</translation>
+<translation id="1916770123977586577">برای اعمال کردن تنظیمات به‌روزرسانی‌شده خود در این سایت، این صفحه را تازه‌سازی کنید</translation>
+<translation id="1919345977826869612">آگهی‌ها</translation>
<translation id="192020519938775529">{COUNT,plural, =0{هیچ‌کدام}=1{۱ سایت}one{# سایت}other{# سایت}}</translation>
<translation id="194030505837763158">رفتن به <ph name="LINK" /></translation>
+<translation id="1948773908305951926">کارت‌های پیش‌پرداخت قابل‌قبول</translation>
<translation id="1962204205936693436">نشانک‌های <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">خطای ترتیب</translation>
<translation id="1974060860693918893">پیشرفته</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">‏خطای HTTP</translation>
<translation id="2270484714375784793">شماره تلفن</translation>
<translation id="2282872951544483773">آزمایش‌های غیرقابل دسترس</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> مورد}one{<ph name="ITEM_COUNT" /> مورد}other{<ph name="ITEM_COUNT" /> مورد}}</translation>
<translation id="2292556288342944218">دسترسی شما به اینترنت مسدود است</translation>
<translation id="230155334948463882">کارت جدید؟</translation>
+<translation id="2316887270356262533">کمتر از ۱ مگابایت از فضا را آزاد می‌کند. ممکن است برخی از سایت‌ها در بازدیدهای بعدی کندتر بارگیری شوند.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> به نام کاربری و گذرواژه نیاز دارد.</translation>
+<translation id="2317583587496011522">کارت‌های نقدی پذیرفته می‌شوند.</translation>
<translation id="2337852623177822836">تنظیم توسط سرپرست کنترل می‌شود</translation>
<translation id="2354001756790975382">نشانک‌های دیگر</translation>
<translation id="2354430244986887761">‏مرور ایمن Google، اخیراً <ph name="BEGIN_LINK" />برنامه‌های خطرناکی<ph name="END_LINK" /> در <ph name="SITE" /> پیدا کرده است.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">ادامه</translation>
<translation id="2365563543831475020">گزارش خرابی ثبت‌شده در <ph name="CRASH_TIME" /> بارگذاری نشد</translation>
<translation id="2367567093518048410">سطح</translation>
-<translation id="237718015863234333">هیچ گزینه واسط کاربری موجود نیست</translation>
<translation id="2384307209577226199">پیش‌فرض شرکتی</translation>
<translation id="2386255080630008482">گواهی سرور باطل شده است.</translation>
<translation id="2392959068659972793">نمایش خط‌مشی‌ها با مقدار تنظیم نشده</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">‏اگر علامت زده شود، Chromium برای پر کردن سریع‌تر فرم، یک کپی از کارت شما در این دستگاه ذخیره می‌کند.</translation>
<translation id="2498091847651709837">اسکن کارت جدید</translation>
<translation id="2501278716633472235">بازگشت</translation>
+<translation id="2503184589641749290">کارت‌های نقدی و پیش‌پرداخت قابل‌قبول</translation>
<translation id="2515629240566999685">بررسی سیگنال در منطقه‌تان</translation>
-<translation id="2516305470678292029">گزینه‌های واسط کاربر</translation>
<translation id="2539524384386349900">تشخیص</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> پاسخی نامعتبر ارسال کرد.</translation>
<translation id="2556876185419854533">&amp;واگرد ویرایش</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">روش ارسال</translation>
<translation id="277499241957683684">ثبت دستگاه موجود نیست</translation>
<translation id="2784949926578158345">اتصال مجدداً برقرار شد.</translation>
+<translation id="2788784517760473862">کارت‌های اعتباری قابل‌قبول</translation>
<translation id="2794233252405721443">سایت مسدودشده</translation>
<translation id="2799020568854403057">سایت پیش‌رو حاوی برنامه‌های خطرناک است</translation>
<translation id="2803306138276472711">‏Google Safe Browsing به تازگی در <ph name="SITE" />، ‏<ph name="BEGIN_LINK" />بدافزار شناسایی کرده است<ph name="END_LINK" />. گاهی اوقات وب‌سایت‌هایی که معمولاً ایمن هستند با بدافزار آلوده می‌شوند.</translation>
<translation id="2824775600643448204">نوار جستجو و آدرس</translation>
<translation id="2826760142808435982">اتصال با استفاده از <ph name="CIPHER" /> رمزگذاری و راستی‌آزمایی شده است و از <ph name="KX" /> به عنوان مکانیسم تبادل کلید استفاده می‌کند.</translation>
<translation id="2835170189407361413">پاک کردن فرم</translation>
+<translation id="2851634818064021665">برای بازدید کردن از این سایت، مجوز لازم دارید</translation>
<translation id="2856444702002559011">شاید مهاجم‌ها در تلاش باشند اطلاعات شما (مانند گذرواژه‌ها، پیام‌ها یا کارت‌های اعتباری) را از <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> سرقت کنند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">تازه‌سازی نشود</translation>
-<translation id="2900469785430194048">‏Google Chrome هنگام تلاش برای نمایش این صفحه با کمبود حافظه روبرو شد.</translation>
<translation id="2909946352844186028">تغییر شبکه تشخیص داده شد.</translation>
<translation id="2916038427272391327">برنامه‌های دیگر را ببندید</translation>
<translation id="2922350208395188000">گواهی سرور بررسی نمی‌شود.</translation>
<translation id="2928905813689894207">نشانی صورت‌حساب</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> نشانی دیگر}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> نشانی دیگر}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> نشانی دیگر}}</translation>
<translation id="2941952326391522266">این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ گواهی امنیتی آن از <ph name="DOMAIN2" /> است. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجی اتصال شما را قطع کرده است.</translation>
<translation id="2948083400971632585">در صفحه تنظیمات می‌توانید همه پراکسی‌های پیکربندی شده برای هر اتصال را از کار بیاندازید.</translation>
<translation id="2955913368246107853">بستن نوار یافتن</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">‏اگر فعال شود، Chromium برای پر کردن سریع‌تر فرم، یک کپی از کارت شما در این دستگاه ذخیره می‌کند.</translation>
<translation id="2985398929374701810">نشانی معتبری وارد کنید</translation>
<translation id="2986368408720340940">این روش تحویل گرفتن در دسترس نیست. روش دیگری را امتحان کنید.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">صفحه‌هایی که در برگه‌های حالت ناشناس مرور می‌کنید، بعد از بستن همه برگه‌های حالت ناشناس در سابقه مرورگر، فضای ذخیره کوکی یا سابقه جستجو باقی نمی‌مانند. فایل‌هایی که بارگیری می‌کنید یا نشانک‌هایی که ایجاد می‌کنید حفظ می‌شود.</translation>
<translation id="3169472444629675720">کشف کردن</translation>
<translation id="3174168572213147020">جزیره</translation>
-<translation id="317583078218509884">تنظیمات مجوزهای جدید سایت پس از بارگیری مجدد صفحه اعمال می‌شوند.</translation>
<translation id="3176929007561373547">تنظیمات پروکسی‌ را بررسی کنید یا با سرپرست شبکه‌‌تان تماس بگیرید تا
مطمئن شوید سرور پروکسی کار می‌کند. اگر مطمئن نیستید که باید از سرور
پروکسی استفاده کنید:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">صفحه را در حالت «ناشناس» باز کنید</translation>
<translation id="320323717674993345">لغو پرداخت</translation>
<translation id="3207960819495026254">نشانک‌گذاری شده</translation>
<translation id="3225919329040284222">سرور گواهی را نشان می‌دهد که با موارد پیش‌بینی‌شده داخلی مطابقت ندارد. این پیش‌بینی‌ها به‌طور حتم وب‌سایتهای دارای امنیت بالا را جهت محافظت از شما در بر می‌گیرند.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;واگرد ترتیب‌بندی مجدد</translation>
<translation id="3282497668470633863">افزودن نام روی کارت</translation>
<translation id="3286538390144397061">راه‌اندازی مجدد اکنون</translation>
+<translation id="3287510313208355388">بارگیری بعد از آنلاین شدن</translation>
<translation id="3303855915957856445">هیچ نتیجه‌ای برای جستجو یافت نشد</translation>
<translation id="3305707030755673451">داده‌های شما در تاریخ <ph name="TIME" /> با عبارت عبور همگام‌سازی‌تان رمزگذاری شد. برای شروع همگام‌سازی آن را وارد کنید.</translation>
<translation id="3320021301628644560">افزودن نشانی صورتحساب</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">فاصله زمانی واکشی:</translation>
<translation id="3462200631372590220">پنهان کردن پیشرفته</translation>
<translation id="3467763166455606212">نام صاحب حساب الزامی است</translation>
-<translation id="3478058380795961209">ماه انقضا</translation>
<translation id="3479539252931486093">غیرمنتظره بود؟ <ph name="BEGIN_LINK" />به ما اطلاع دهید<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">اکنون نه</translation>
<translation id="3498215018399854026">در حال حاضر نمی‌توانیم با والدینتان تماس برقرار کنیم. لطفاً دوباره امتحان کنید.</translation>
@@ -343,12 +345,13 @@
&lt;p&gt;لطفاً تاریخ و زمان را در بخش &lt;strong&gt;«موارد کلی»&lt;/strong&gt; برنامه &lt;strong&gt;«تنظیمات»&lt;/strong&gt; تنظیم کنید.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />، <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">کارت‌های اعتباری و پیش‌پرداخت پذیرفته می‌شوند.</translation>
<translation id="3582930987043644930">افزودن نام</translation>
<translation id="3583757800736429874">&amp;انجام مجدد انتقال</translation>
<translation id="3586931643579894722">عدم نمایش جزئیات</translation>
<translation id="3600246354004376029"><ph name="TITLE" />، <ph name="DOMAIN" />، <ph name="TIME" /></translation>
<translation id="3615877443314183785">تاریخ انقضای معتبری وارد کنید</translation>
-<translation id="36224234498066874">پاک کردن داده‌های مرور...</translation>
+<translation id="36224234498066874">پاک کردن داده‌های محصول مرور…</translation>
<translation id="362276910939193118">نمایش کل سابقه</translation>
<translation id="3623476034248543066">نشان دادن مقدار</translation>
<translation id="3630155396527302611">اگر درحال حاضر به‌عنوان برنامه‌ای برای دسترسی به شبکه در فهرست قرار دارد،
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">اگر مکرراً با این مشکل مواجه می‌شوید، <ph name="HELP_LINK" /> را امتحان کنید.</translation>
<translation id="3658742229777143148">ویرایش</translation>
<translation id="3678029195006412963">درخواست امضا نشد</translation>
+<translation id="3678529606614285348">‏صفحه را در پنجره‌ «ناشناس» جدیدی باز کنید (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">گزارش خرابی در <ph name="CRASH_TIME" /> ثبت شد، در <ph name="UPLOAD_TIME" /> بارگذاری شد</translation>
<translation id="3681007416295224113">اطلاعات گواهی</translation>
<translation id="3690164694835360974">ورود به سیستم امن نیست</translation>
-<translation id="3693415264595406141">گذرواژه:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> ‏<ph name="BOOKMARKED" /> ‏<ph name="TITLE" /> ‏<ph name="DOMAIN" /></translation>
<translation id="370665806235115550">در حال بارکردن…</translation>
<translation id="3712624925041724820">مجوزها دیگر معتبر نیستند</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">ممکن است مهاجم‌ها در <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="375403751935624634">بدلیل خطای سرور ترجمه انجام نشد.</translation>
<translation id="3759461132968374835">شما اخیراً گزارش خرابی ارسال نکرده‌اید. مشکلاتی که در هنگام غیرفعال بودن ویژگی ارائه گزارش خرابی ایجاد شده است، در اینجا نمایش داده نمی‌شود.</translation>
+<translation id="3765032636089507299">صفحه «مرور ایمن» در دست ساخت است.</translation>
<translation id="3778403066972421603">‏آیا می‌خواهید این کارت در حساب Google شما و این دستگاه ذخیره شود؟</translation>
<translation id="3783418713923659662">مسترکارت</translation>
<translation id="3787705759683870569">تاریخ انقضا <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">درخواست شما برای دسترسی به این سایت برای <ph name="NAME" /> ارسال شده است</translation>
<translation id="3890664840433101773">افزودن رایانامه</translation>
<translation id="3901925938762663762">کارت منقضی شده است</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">شناسه گزارش خرابی بارگذاری‌شده <ph name="CRASH_ID" /> (شناسه خرابی محلی: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">‏این سرور نتوانست ثابت کند که <ph name="DOMAIN" /> است؛ در گواهی امنیتی آن، Subject Alternative Names مشخص نشده است. ممکن است این مشکل به دلیل پیکربندی نادرست یا قطع اتصال شما توسط مهاجم ایجاد شده باشد.</translation>
+<translation id="3949601375789751990">سابقه مرورتان در اینجا نشان داده می‌شود</translation>
<translation id="3963721102035795474">حالت «خواننده»</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{هیچ‌کدام}=1{از ۱ سایت }one{از # سایت }other{از # سایت }}</translation>
<translation id="397105322502079400">در حال محاسبه…</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">تنظیمات سایت</translation>
<translation id="4169947484918424451">‏می‌خواهید Chromium این کارت را ذخیره کند؟</translation>
<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="4196861286325780578">&amp;انجام مجدد انتقال</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />بررسی پیکربندی آنتی‌ویروس و دیوار آتش<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">خرابی ها</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">مهاجم‌ها در این سایت ممکن است تلاش کنند برنامه‌های خطرناکی در رایانه شما نصب کنند که اطلاعات شما (مانند عکس‌ها، گذرواژه‌ها، پیام‌ها و کارت‌های اعتباری) را به سرقت می‌برند یا حذف می‌کنند.</translation>
<translation id="4372948949327679948">مقدار مورد انتظار <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">شما سعی در دسترسی به <ph name="DOMAIN" /> را داشتید، اما صادر کننده، گواهی ارائه شده از سوی سرور را باطل کرده است. یعنی اصلاً نباید به اطلاعات کاربری که این سرور ارائه می‌کند اطمینان کرد. ممکن است شما با مهاجمی در ارتباط باشید.</translation>
-<translation id="4381091992796011497">نام کاربری:</translation>
<translation id="4394049700291259645">غیر فعال کردن</translation>
<translation id="4406896451731180161">نتایج جستجو</translation>
<translation id="4424024547088906515">‏این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ گواهی امنیتی آن مورداعتماد Chrome نیست. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجی اتصال شما را قطع کرده است.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> گواهی ورود به سیستمتان را نپذیرفت یا ممکن است گواهی‌ای ارائه نشده باشد.</translation>
<translation id="443673843213245140">استفاده از پروکسی غیرفعال است اما یک پیکربندی خاص برای پروکسی تعیین شده است.</translation>
+<translation id="445100540951337728">کارت‌های نقدی قابل‌قبول</translation>
<translation id="4506176782989081258">خطای ارزیابی: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">تماس با سرپرست سیستم</translation>
<translation id="450710068430902550">اشتراک‌گذاری با سرپرست سیستم</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">‏آدرس از Chrome پاک شود؟</translation>
<translation id="4592951414987517459">اتصال شما به <ph name="DOMAIN" /> با استفاده از یک مجموعه رمز مدرن، رمزگذاری شده است.</translation>
<translation id="4594403342090139922">&amp;واگرد حذف</translation>
+<translation id="4611292653554630842">ورود به سیستم</translation>
<translation id="4619615317237390068">برگه‌ها از دستگاه‌های دیگر</translation>
<translation id="4668929960204016307">،</translation>
<translation id="467662567472608290">این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ گواهی امنیتی آن خطاهایی دارد. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجمی اتصال شما را قطع کرده است.</translation>
<translation id="4690462567478992370">توقف استفاده از گواهینامه نامعتبر</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">اتصال شما قطع شد</translation>
+<translation id="471880041731876836">برای بازدید کردن از این سایت، مجوز لازم ندارید</translation>
<translation id="4722547256916164131">‏<ph name="BEGIN_LINK" />در حال اجرای Windows Network Diagnostics<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">تازه‌سازی خط مشی‌ها</translation>
<translation id="4728558894243024398">پلت فورم</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">نما</translation>
<translation id="4854362297993841467">این روش تحویل در دسترس نیست. روش دیگری را امتحان کنید.</translation>
<translation id="4858792381671956233">از والدینتان پرسیدید آیا اجازه بازدید از این سایت را دارید</translation>
-<translation id="4863764087567530506">شاید محتوای این صفحه تلاش کند شما را فریب دهد تا نرم‌افزاری نصب کنید یا اطلاعات شخصی را افشا سازید. <ph name="BEGIN_LINK" />درهرصورت نمایش داده شود<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">سابقه جستجو</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />،‏ <ph name="TYPE_2" />،‏ <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">راستی‌آزمایی لازم است</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ و ۱ صفحه وب دیگر}one{ و # صفحه وب دیگر}other{ و # صفحه وب دیگر}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">این صفحه از یک زبان ناشناس به <ph name="LANGUAGE_LANGUAGE" /> ترجمه شده است.</translation>
<translation id="4923459931733593730">پرداخت</translation>
<translation id="4926049483395192435">باید مشخص شود.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">عملکردها</translation>
<translation id="4958444002117714549">بزرگ کردن فهرست</translation>
<translation id="4974590756084640048">فعال کردن مجدد اخطارها</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">گذرواژه نادرست</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>
<translation id="5087580092889165836">افزودن کارت</translation>
<translation id="5089810972385038852">ایالت</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">استان</translation>
<translation id="5115563688576182185">(۶۴ بیت)</translation>
<translation id="5141240743006678641">‏رمزگذاری گذرواژه‌های همگام‌سازی شده با اطلاعات کاربری Google شما</translation>
-<translation id="514421653919133810">‏صفحه را در حالت «ناشناس» باز کنید (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">کد خطا در پاسخ خط‌مشی موجود است</translation>
+<translation id="5159010409087891077">‏صفحه را در پنجره «ناشناس» جدیدی باز کنید (N⌘⇧)</translation>
<translation id="5171045022955879922">‏جستجو یا تایپ URL</translation>
<translation id="5172758083709347301">دستگاه</translation>
<translation id="5179510805599951267">به زبان <ph name="ORIGINAL_LANGUAGE" /> نیست؟ گزارش این خطا</translation>
-<translation id="5181140330217080051">در حال بارگیری</translation>
<translation id="5190835502935405962">نوار نشانک‌ها</translation>
<translation id="5199729219167945352">آزمایشات</translation>
<translation id="5205222826937269299">نام ضروری است</translation>
<translation id="5222812217790122047">رایانامه ضروری است</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">‏از Chrome در محل کار استفاده می‌کنید؟ کسب و کارها می‌توانند تنظیمات Chrome را برای کارمندانشان مدیریت کنند. بیشتر بدانید</translation>
+<translation id="5281113152797308730">‏<ph name="BEGIN_PARAGRAPH" />با دنبال کردن این مراحل، نرم‌افزار را موقتاً غیرفعال کنید تا بتوانید به وب دسترسی داشته باشید. انجام این مراحل به امتیازهای سرپرستی نیاز دارد.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />روی <ph name="BEGIN_BOLD" />شروع<ph name="END_BOLD" /> کلیک کنید، سپس <ph name="BEGIN_BOLD" />«مشاهده سرویس‌های محلی»<ph name="END_BOLD" /> را جستجو و انتخاب کنید
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" /> را انتخاب کنید
+ <ph name="LIST_ITEM" />در بخش <ph name="BEGIN_BOLD" />نوع راه‌اندازی<ph name="END_BOLD" />، <ph name="BEGIN_BOLD" />غیرفعال‌<ph name="END_BOLD" /> را انتخاب کنید
+ <ph name="LIST_ITEM" />در بخش <ph name="BEGIN_BOLD" />وضعیت سرویس<ph name="END_BOLD" />، روی <ph name="BEGIN_BOLD" />توقف<ph name="END_BOLD" /> کلیک کنید
+ <ph name="LIST_ITEM" />روی <ph name="BEGIN_BOLD" />اعمال<ph name="END_BOLD" />، سپس <ph name="BEGIN_BOLD" />تأیید<ph name="END_BOLD" /> کلیک کنید
+ <ph name="LIST_ITEM" />برای آشنایی با نحوه برداشتن دائم نرم‌افزار از رایانه، به <ph name="BEGIN_LEARN_MORE_LINK" />مرکز راهنمایی Chrome<ph name="END_LEARN_MORE_LINK" /> بروید
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">‏اتصال شما به این سایت خصوصی نیست. درهرزمانی برای خروج از حالت VR، هدست را جدا کنید و «برگشت» را فشار دهید.</translation>
<translation id="5299298092464848405">خطا در تجزیه خط‌‌مشی</translation>
<translation id="5308689395849655368">گزارش خرابی غیر فعال است.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">خطای تأیید طرح در «<ph name="ERROR_PATH" />»:‏ <ph name="ERROR" /></translation>
<translation id="5452270690849572955">صفحه <ph name="HOST_NAME" /> پیدا نمی‌شود</translation>
<translation id="5455374756549232013">مهر زمان خط‌مشی نادرست است</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> از <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">نامعتبر</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> مخاطب دیگر}one{<ph name="CONTACT_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> مخاطب دیگر}other{<ph name="CONTACT_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> مخاطب دیگر}}</translation>
<translation id="5470861586879999274">&amp;انجام مجدد ویرایش</translation>
+<translation id="5481076368049295676">شاید این محتوا تلاش کند نرم‌افزار خطرناکی در دستگاهتان نصب کند که اطلاعاتتان را به سرقت برد یا حذف کند. <ph name="BEGIN_LINK" />درهرصورت نمایش داده شود<ph name="END_LINK" /></translation>
<translation id="54817484435770891">نشانی معتبری اضافه کنید</translation>
<translation id="5492298309214877701">نشانی وب این سایت در اینترانت شرکت، سازمان یا محل تحصیل با نشانی وب یک وب‌سایت خارجی یکسان است.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">مقاله درخواستی یافت نشد.</translation>
<translation id="5544037170328430102">صفحه جاسازی‌شده‌ای در <ph name="SITE" /> می‌گوید:</translation>
<translation id="5556459405103347317">تازه‌سازی</translation>
+<translation id="5560088892362098740">تاریخ انقضا</translation>
<translation id="5565735124758917034">فعال</translation>
<translation id="5571083550517324815">تحویل گرفتن از این نشانی ممکن نیست. نشانی دیگری را انتخاب کنید.</translation>
<translation id="5572851009514199876">‏لطفاً Chrome را باز کنید و به سیستم آن وارد شوید تا Chrome بتواند بررسی کند آیا مجاز به دسترسی به این سایت هستید یا خیر.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">تنظیمات خط‌مشی بارگیری نشد</translation>
<translation id="5631439013527180824">نشانه مدیریت دستگاه نامعتبر است</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="5646376287012673985">مکان</translation>
<translation id="5659593005791499971">رایانامه</translation>
<translation id="5669703222995421982">دریافت محتوای شخصی‌سازی‌شده</translation>
<translation id="5675650730144413517">این صفحه کار نمی‌کند</translation>
<translation id="5710435578057952990">هویت این وب سایت تأیید نشده است.</translation>
-<translation id="5713016350996637505">محتوای فریب‌دهنده مسدود شد</translation>
+<translation id="5719499550583120431">کارت‌های پیش‌پرداخت پذیرفته می‌شوند.</translation>
<translation id="5720705177508910913">کاربر کنونی</translation>
<translation id="5732392974455271431">والدینتان می‌توانند این سایت را برای شما بگشایند</translation>
<translation id="5763042198335101085">نشانی رایانامه معتبری وارد کنید</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">می‌خواهید <ph name="CARD_DETAIL" /> خود را وارد کنید؟</translation>
<translation id="5810442152076338065">اتصال شما به <ph name="DOMAIN" /> با استفاده از یک مجموعه رمز منسوخ، رمزگذاری شده است.</translation>
<translation id="5813119285467412249">&amp;انجام مجدد افزودن</translation>
-<translation id="5814352347845180253">ممکن است دسترسی به محتوای ممتاز از <ph name="SITE" /> و برخی دیگر سایت‌ها را از دست بدهید.</translation>
<translation id="5838278095973806738">نباید هیچ اطلاعات حساسی (مثل گذرواژه یا کارت اعتباری) را در این سایت وارد کنید، زیرا ممکن است مهاجمین آن‌ها را سرقت کنند.</translation>
<translation id="5869405914158311789">دسترسی به این سایت امکان‌پذیر نیست</translation>
<translation id="5869522115854928033">گذرواژه‌های ذخیره‌شده</translation>
<translation id="5872918882028971132">پیشنهادات والدین</translation>
+<translation id="5893752035575986141">کارت‌های اعتباری پذیرفته می‌شوند.</translation>
<translation id="5901630391730855834">زرد</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (همگام‌سازی‌شده)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{۱ کوکی درحال استفاده}one{# کوکی درحال استفاده}other{# کوکی درحال استفاده}}</translation>
-<translation id="5926846154125914413">ممکن است دسترسی به محتوای ممتاز برخی از سایت‌ها را از دست بدهید.</translation>
<translation id="5959728338436674663">‏ارسال خودکار برخی از <ph name="BEGIN_WHITEPAPER_LINK" />اطلاعات سیستم و محتوای صفحه<ph name="END_WHITEPAPER_LINK" /> به Google برای کمک به شناسایی برنامه‌ها و سایت‌های خطرناک. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">حذف از سابقه</translation>
<translation id="5975083100439434680">کوچک نمایی</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">بستن</translation>
<translation id="6042308850641462728">بیشتر</translation>
<translation id="6047233362582046994">اگر خطری را که امنیتتان را تهدید می‌کند درک می‌کنید، می‌توانید قبل از حذف برنامه‌های مضر، <ph name="BEGIN_LINK" />از این سایت بازدید کنید<ph name="END_LINK" />.</translation>
+<translation id="6047927260846328439">شاید محتوای این صفحه تلاش کند شما را فریب دهد تا نرم‌افزاری نصب کنید یا اطلاعات شخصی را افشا سازید. <ph name="BEGIN_LINK" />درهرصورت نمایش داده شود<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">درحال‌حاضر نمی‌توانید از <ph name="SITE" /> دیدن کنید، زیرا وب‌سایت از پین کردن گواهینامه استفاده می‌کند. خطاهای شبکه و حمله‌ها موقتی هستند، بنابراین احتمالاً این صفحه بعداً کار خواهد کرد.</translation>
<translation id="6060685159320643512">مراقب باشید، این آزمایشات ممکن است خطرناک باشند</translation>
<translation id="6080696365213338172">شما با استفاده از گواهی ارائه شده توسط سرپرست سیستم به محتوا دسترسی پیدا کرده‌اید. داده‌هایی که به <ph name="DOMAIN" /> ارائه می‌کنید ممکن است توسط سرپرست سیستم رهگیری شوند.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">بیشتر بدانید</translation>
<translation id="6169916984152623906">اکنون می‌توانید به‌طور خصوصی مرور کنید و سایر افرادی که از این دستگاه استفاده می‌کنند فعالیت شما را نخواهند دید. بااین‌وجود بارگیری‌ها و نشانک‌ها ذخیره خواهند شد.</translation>
<translation id="6177128806592000436">اتصال شما به این سایت امن نیست</translation>
-<translation id="6184817833369986695">(هم‌گروه: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">اتصال اینترنتتان را بررسی کنید</translation>
<translation id="6218753634732582820">‏آدرس از Chromium پاک شود؟</translation>
<translation id="6221345481584921695">‏Google Safe Browsing به تازگی در <ph name="SITE" />، ‏<ph name="BEGIN_LINK" />بدافزار شناسایی کرده است<ph name="END_LINK" />. گاهی اوقات وب‌سایت‌هایی که معمولاً امن هستند، با بدافزار آلوده می‌شوند. منبع محتوای مخرب <ph name="SUBRESOURCE_HOST" /> است که یک توزیع‌کننده بدافزار شناخته شده، می‌باشد.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">‏علاقه‌مند به قابلیت‌های جدید و جالب Chrome هستید؟ کانال بتای ما را در chrome.com/beta امتحان کنید.</translation>
-<translation id="6389758589412724634">‏Chromium هنگام تلاش برای نمایش این صفحه با کمبود حافظه روبرو شد.</translation>
+<translation id="6397451950548600259">‏نرم‌افزاری در رایانه شما مانع از اتصال ایمن Chrome به وب می‌شود</translation>
<translation id="6404511346730675251">ویرایش نشانک</translation>
<translation id="6410264514553301377">‏CVC‏ و تاریخ انقضای <ph name="CREDIT_CARD" /> را وارد کنید</translation>
<translation id="6414888972213066896">از والدینتان پرسیدید آیا اجازه بازدید از این سایت را دارید</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">خط‌‌مشی‌های دستگاه</translation>
<translation id="6477321094435799029">‏Chrome کد نامعمول در این این صفحه شناسایی کرده و برای محافظت از اطلاعات شخصی‌تان (مثلاً گذرواژه‌ها، شماره تلفن‌‌ها و کارت‌های اعتباری) آن را مسدود کرده است.</translation>
<translation id="6489534406876378309">شروع بارگذاری کردن خرابی‌ها</translation>
+<translation id="6507833130742554667">کارت‌های اعتباری و نقدی پذیرفته می‌شوند.</translation>
<translation id="6508722015517270189">‏Chrome را راه‌اندازی مجدد کنید</translation>
<translation id="6529602333819889595">&amp;انجام مجدد حذف</translation>
<translation id="6534179046333460208">پیشنهادهای «وب فیزیکی»</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">شاید این محتوا تلاش کند نرم‌افزار خطرناکی در دستگاهتان نصب کند که اطلاعاتتان را سرقت یا حذف می‌کند. <ph name="BEGIN_LINK" />درهرصورت نمایش داده شود<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">گزینه‌های رمزگذاری</translation>
<translation id="662080504995468778">ماندن</translation>
<translation id="6626291197371920147">افزودن شماره کارت معتبر</translation>
<translation id="6628463337424475685">جستجوی <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">‏شاید درحال‌حاضر مهاجم‌ها در <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> در تلاش باشند برنامه‌های خطرناکی در Mac شما نصب کنند که اطلاعاتتان (مانند عکس‌ها، گذرواژه‌ها، پیام‌ها و کارت‌های اعتباری) را به سرقت می‌برند یا حذف می‌کنند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">این قانون قدیمی شده است.</translation>
+<translation id="6657585470893396449">گذرواژه</translation>
<translation id="6671697161687535275">‏پیشنهاد فرم از Chromium پاک شود؟</translation>
<translation id="6685834062052613830">خروج از سیستم و تکمیل راه‌اندازی</translation>
<translation id="6710213216561001401">قبلی</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">حوزه</translation>
<translation id="6973656660372572881">‏هم سرورهای پروکسی ثابت و هم آدرس اسکریپت pac. مشخص شده‌اند.</translation>
<translation id="6989763994942163495">نمایش تنظیمات پیشرفته ...</translation>
-<translation id="7000990526846637657">هیچ ورودی سابقه‌ای پیدا نشد</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">‏ممکن است «حساب Google» شما سابقه مروری به شکل دیگری در <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> داشته باشد</translation>
<translation id="7029809446516969842">گذرواژه‌ها</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">ارسال به این نشانی ممکن نیست. نشانی دیگری را انتخاب کنید.</translation>
<translation id="7138472120740807366">روش تحویل</translation>
<translation id="7139724024395191329">امارت</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> روش دیگر}one{<ph name="PAYMENT_METHOD_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> روش دیگر}other{<ph name="PAYMENT_METHOD_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> روش دیگر}}</translation>
<translation id="7155487117670177674">پرداخت امن نیست</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> گزینه دیگر}one{<ph name="SHIPPING_OPTION_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> گزینه دیگر}other{<ph name="SHIPPING_OPTION_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> گزینه دیگر}}</translation>
<translation id="7179921470347911571">اکنون راه‌اندازی مجدد شود</translation>
<translation id="7180611975245234373">بازخوانی</translation>
<translation id="7182878459783632708">هیچ خط‌مشی‌ای تنظیم نشده است</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">بارگیری</translation>
<translation id="7518003948725431193">صفحه وبی با این آدرس وب یافت نشد: <ph name="URL" /></translation>
<translation id="7521387064766892559">جاوا اسکریپت</translation>
+<translation id="7526934274050461096">اتصال شما به این سایت خصوصی نیست</translation>
<translation id="7535087603100972091">مقدار</translation>
<translation id="7537536606612762813">اجباری</translation>
<translation id="7542403920425041731">بعد از تأیید شما، جزئیات کارتتان با این سایت به اشتراک گذاشته می‌شود.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">نکته‌های زیر را امتحان کنید:</translation>
<translation id="7554791636758816595">برگهٔ جدید</translation>
<translation id="7567204685887185387">این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ ممکن است گواهی امنیتی آن به صورت تقلبی صادر شده باشد. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجمی اتصال شما را قطع کرده است.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> مخاطب دیگر}one{<ph name="CONTACT_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> مخاطب دیگر}other{<ph name="CONTACT_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> مخاطب دیگر}}</translation>
<translation id="7568593326407688803">این صفحه به <ph name="ORIGINAL_LANGUAGE" /> است، آیا مایلید ترجمه شود؟</translation>
<translation id="7569952961197462199">‏کارت اعتباری از Chrome پاک شود؟</translation>
<translation id="7569983096843329377">سیاه</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">گواهی</translation>
<translation id="7716147886133743102">توسط سرپرست مسدود شده است</translation>
<translation id="7716424297397655342">این سایت نمی‌تواند از حافظه پنهان بار شود</translation>
+<translation id="774634243536837715">محتوای خطرناک مسدود شد.</translation>
<translation id="7752995774971033316">مدیریت نشده</translation>
<translation id="7755287808199759310">والدینتان می‌توانند این سایت را برای شما بگشایند</translation>
<translation id="7758069387465995638">دیوار آتش یا نرم‌افزار ضدویروس ممکن است مانع اتصال شده باشد.</translation>
+<translation id="7759163816903619567">نمایش دامنه:</translation>
<translation id="7761701407923456692">گواهی سرور با نشانی وب مطابقت ندارد.</translation>
<translation id="7763386264682878361">تجزیه‌کننده مانیفست پرداخت</translation>
<translation id="7764225426217299476">افزودن آدرس</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534"><ph name="NUMBER_OF_RESULTS" /><ph name="SEARCH_RESULTS" /> برای «<ph name="SEARCH_STRING" />» پیدا شد</translation>
<translation id="785549533363645510">اما، شما نامرئی نیستید. با استفاده از حالت ناشناس، مرورتان از چشمان کارفرمای شما، ارائه‌دهنده خدمات اینترنت یا وب‌‌سایت‌هایی که بازدید می‌کنید پنهان نمی‌ماند.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">کارت‌های نقدی و پیش‌پرداخت پذیرفته می‌شوند.</translation>
<translation id="7887683347370398519">‏CVC را بررسی کرده و دوباره امتحان کنید</translation>
<translation id="79338296614623784">شماره تلفن معتبری وارد کنید</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">ارسال بازخورد</translation>
<translation id="8041940743680923270">استفاده از پیش‌فرض جهانی (سؤال شود)</translation>
<translation id="8088680233425245692">مشاهده مقاله ناموفق بود.</translation>
-<translation id="8089520772729574115">کمتر از ۱ مگابایت</translation>
<translation id="8091372947890762290">فعال‌سازی در سرور در حالت تعلیق است</translation>
<translation id="8118489163946903409">روش پرداخت</translation>
<translation id="8131740175452115882">تأیید</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;واگرد انتقال</translation>
<translation id="8201077131113104583">نشانی وب به‌روزرسانی نامعتبر برای برنامه افزودنی با شناسه «<ph name="EXTENSION_ID" />».</translation>
<translation id="8202097416529803614">خلاصه سفارش</translation>
+<translation id="8205463626947051446">سایت تمایل دارد آگهی‌ها مزاحم نشان دهد</translation>
<translation id="8218327578424803826">مکان اختصاص یافته:</translation>
<translation id="8225771182978767009">شخصی که این رایانه را راه‌اندازی کرده این سایت را مسدود کرده است.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />،‏ <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">صفحه را در برگه «ناشناس» جدیدی باز کنید</translation>
<translation id="8241707690549784388">صفحه‌ای که جستجو می‌کنید از اطلاعاتی استفاده می‌کند که شما وارد کرده‌اید. بازگشت به آن صفحه ممکن است باعث شود اقدامی را که قبلاً انجام دادید دوباره تکرار کنید. آیا می‌خواهید ادامه دهید؟</translation>
+<translation id="8241712895048303527">مسدود کردن در این سایت</translation>
<translation id="8249320324621329438">آخرین واکشی شده:</translation>
<translation id="8253091569723639551">نشانی صورت‌حساب لازم است</translation>
<translation id="8261506727792406068">حذف</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">ترجمه انجام نشد چون مشکلی در اتصال به شبکه رخ داد.</translation>
<translation id="8332188693563227489">دسترسی به <ph name="HOST_NAME" /> رد شد</translation>
<translation id="834457929814110454">اگر خطرات امنیتی‌ای که متوجه شما هستند را درک می‌کنید، می‌توانید قبل از حذف شدن برنامه‌های خطرناک، <ph name="BEGIN_LINK" />از این سایت بازدید کنید<ph name="END_LINK" />.</translation>
-<translation id="8344669043927012510">‏صفحه را در حالت «ناشناس» باز کنید (‎⇧⌘N)</translation>
<translation id="8349305172487531364">نوار نشانک‌ها</translation>
<translation id="8363502534493474904">خاموش کردن حالت هواپیما</translation>
<translation id="8364627913115013041">تنظیم نشده است.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">گزارش خرابی در <ph name="UPLOAD_TIME" /> بارگذاری شد</translation>
<translation id="8412145213513410671">خرابی ها (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">شما باید همان عبارت عبور را دوبار وارد کنید.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">تنظیمات</translation>
<translation id="8433057134996913067">با این کار از سیستم بیشتر وب‌سایت‌ها خارج می‌شوید.</translation>
<translation id="8437238597147034694">&amp;واگرد انتقال</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">‏برای استفاده از کارت‌ها از حساب Google خود، به سیستم Chrome وارد شوید</translation>
<translation id="8488350697529856933">اعمال برای</translation>
<translation id="8498891568109133222">پاسخ <ph name="HOST_NAME" /> بیش از حد طول کشیده است.</translation>
-<translation id="8532105204136943229">سال انقضا</translation>
+<translation id="8503813439785031346">نام کاربری</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="8571890674111243710">ترجمه صفحه به <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">افزودن شماره تلفن</translation>
<translation id="859285277496340001">این مجوز هیچ مکانیزمی را برای بررسی اینکه آیا باطل شده یا نه مشخص نمی‌کند.</translation>
<translation id="8620436878122366504">والدینتان هنوز این سایت را تأیید نکرده‌اند</translation>
<translation id="8647750283161643317">بازنشانی همه به موارد پیش‌فرض</translation>
+<translation id="8660471606262461360">‏از Google Payments</translation>
<translation id="8703575177326907206">اتصال شما به <ph name="DOMAIN" /> رمزگذاری نشده است.</translation>
<translation id="8718314106902482036">پرداخت کامل نشد</translation>
<translation id="8725066075913043281">سعی مجدد</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">بزرگ‌نمایی</translation>
<translation id="8931333241327730545">‏می‌خواهید این کارت را در حساب Google خود ذخیره کنید؟</translation>
<translation id="8932102934695377596">ساعت شما عقب است</translation>
+<translation id="8938939909778640821">کارت‌های اعتباری و پیش‌پرداخت قابل‌قبول</translation>
<translation id="8971063699422889582">گواهی سرور منقضی شده است.</translation>
<translation id="8986494364107987395">‏ارسال خودکار آمار کاربرد و گزارش‌های خرابی به Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">همیشه مسدود در این سایت</translation>
<translation id="9170848237812810038">&amp;واگرد</translation>
<translation id="917450738466192189">گواهی سرور نامعتبر است.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> گزینه دیگر}one{<ph name="SHIPPING_OPTION_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> گزینه دیگر}other{<ph name="SHIPPING_OPTION_PREVIEW" /> و <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> گزینه دیگر}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> از یک پروتکل پشتیبانی‌نشده استفاده می‌کند.</translation>
<translation id="9205078245616868884">داده‌های شما با عبارت عبور همگام‌سازی رمزگذاری می‌شود. برای شروع همگام‌سازی آن را وارد کنید.</translation>
<translation id="9207861905230894330">افزودن مقاله ناموفق بود.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">پاک کردن فرم</translation>
<translation id="939736085109172342">پوشهٔ جدید</translation>
-<translation id="941721044073577244">گویا اجازه بازدید از این سایت را ندارید</translation>
<translation id="969892804517981540">ساخت رسمی</translation>
<translation id="975560348586398090">{COUNT,plural, =0{هیچ‌کدام}=1{۱ مورد}one{# مورد}other{# مورد}}</translation>
+<translation id="981121421437150478">آفلاین</translation>
<translation id="988159990683914416">ساخت برنامه‌نویس</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_fi.xtb b/chromium/components/strings/components_strings_fi.xtb
index 9085de7e6a8..56a2cccadc8 100644
--- a/chromium/components/strings/components_strings_fi.xtb
+++ b/chromium/components/strings/components_strings_fi.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Sulje muita sovelluksia.</translation>
<translation id="1055184225775184556">K&amp;umoa lisäys</translation>
<translation id="10614374240317010">Ei tallenneta</translation>
+<translation id="1066396345355680611">Saatat menettää esim. sivuston <ph name="SITE" /> suojatun sisällön käyttöoikeuden.</translation>
<translation id="106701514854093668">Työpöytäkirjanmerkit</translation>
<translation id="1074497978438210769">Ei turvallinen</translation>
<translation id="1080116354587839789">Sovita leveyteen</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lukulista</translation>
<translation id="1264126396475825575">Kaatumisraportti tallennettu <ph name="CRASH_TIME" /> (ei vielä lähetetty tai ohitettu)</translation>
<translation id="1281526147609854549">Myöntäjä: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Vaarallinen sisältö estetty</translation>
<translation id="1285320974508926690">Älä käännä tätä sivustoa</translation>
<translation id="129553762522093515">Hiljattain suljetut välilehdet</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Tyhjennä evästeet.<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Käyttöönoton verkkotunnus:</translation>
<translation id="1340482604681802745">Nouto-osoite</translation>
-<translation id="1344211575059133124">Vaikuttaa siltä, että tarvitset luvan tämän sivuston käyttämiseen.</translation>
<translation id="1344588688991793829">Chromiumin automaattisen täytön asetukset…</translation>
<translation id="1348198688976932919">Sivusto sisältää vaarallisia sovelluksia</translation>
<translation id="1374468813861204354">ehdotuksia</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Organisaation <ph name="ORGANIZATION" /> identiteetin sijainnissa <ph name="LOCALITY" /> on vahvistanut <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Kyllä</translation>
<translation id="1430915738399379752">Tulosta</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> toinen}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> muuta}}</translation>
<translation id="1506687042165942984">Näytä tallennettu (eli vanhentuneeksi tiedetty) kopio tästä sivusta.</translation>
<translation id="1517433312004943670">Puhelinnumero vaaditaan</translation>
+<translation id="1517500485252541695">Hyväksytyt luotto- ja maksukortit</translation>
<translation id="1519264250979466059">Koontipäivä</translation>
+<translation id="1527263332363067270">Odotetaan yhteyttä…</translation>
<translation id="153384715582417236">Siinä kaikki toistaiseksi</translation>
<translation id="1549470594296187301">Tämän ominaisuuden käyttö edellyttää JavaScriptiä.</translation>
<translation id="1555130319947370107">Sininen</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Ota yhteyttä järjestelmänvalvojaan.</translation>
<translation id="1740951997222943430">Anna kelvollinen viimeinen voimassaolokuukausi.</translation>
-<translation id="1745358365027406341">Lataa sivu myöhemmin</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Pakollinen tieto</translation>
<translation id="187918866476621466">Avaa aloitussivut</translation>
<translation id="1883255238294161206">Tiivistä luettelo</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> toinen}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> muuta}}</translation>
<translation id="1898423065542865115">Suodatus</translation>
+<translation id="1916770123977586577">Päivitä tämä sivu, niin päivitetyt asetuksesi otetaan käyttöön tällä sivustolla.</translation>
+<translation id="1919345977826869612">Mainokset</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Ei mitään}=1{1 sivusto}other{# sivustoa}}</translation>
<translation id="194030505837763158">Siirry osoitteeseen <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Hyväksytyt prepaid-kortit</translation>
<translation id="1962204205936693436">Verkkotunnuksen <ph name="DOMAIN" /> kirjanmerkit</translation>
<translation id="1973335181906896915">Sarjaesittämisen virhe</translation>
<translation id="1974060860693918893">Lisäasetukset</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP-virhe</translation>
<translation id="2270484714375784793">Puhelinnumero</translation>
<translation id="2282872951544483773">Ei käytettävissä olevat kokeilut</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> kohde}other{<ph name="ITEM_COUNT" /> kohdetta}}</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>
<translation id="2337852623177822836">Järjestelmänvalvojan hallinnoima asetus</translation>
<translation id="2354001756790975382">Muut kirjanmerkit</translation>
<translation id="2354430244986887761">Google-selaussuoja <ph name="BEGIN_LINK" />löysi äskettäin haitallisia sovelluksia<ph name="END_LINK" /> sivustolta <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Jatka</translation>
<translation id="2365563543831475020">Kaatumisraportti tallennettu <ph name="CRASH_TIME" />, ei vielä lähetetty</translation>
<translation id="2367567093518048410">Taso</translation>
-<translation id="237718015863234333">Käyttöliittymävaihtoehtoja ei ole saatavilla</translation>
<translation id="2384307209577226199">Yrityksen oletus</translation>
<translation id="2386255080630008482">Palvelimen varmenne on kumottu.</translation>
<translation id="2392959068659972793">Näytä käytännöt, joille ei ole asetettu arvoa</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Jos tämä on valittu, Chromium tallentaa kortin kopion tälle laitteelle nopeuttaakseen lomakkeiden täyttöä.</translation>
<translation id="2498091847651709837">Skannaa uusi kortti</translation>
<translation id="2501278716633472235">Takaisin</translation>
+<translation id="2503184589641749290">Hyväksytyt maksu- ja prepaid-kortit</translation>
<translation id="2515629240566999685">Tarkista alueesi mobiilisignaali.</translation>
-<translation id="2516305470678292029">Käyttöliittymävaihtoehdot</translation>
<translation id="2539524384386349900">Tunnista</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> lähetti virheellisen vastauksen.</translation>
<translation id="2556876185419854533">K&amp;umoa muokkaus</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Lähetystapa</translation>
<translation id="277499241957683684">Laitetallenne puuttuu</translation>
<translation id="2784949926578158345">Yhteys katkaistiin.</translation>
+<translation id="2788784517760473862">Hyväksytyt luottokortit</translation>
<translation id="2794233252405721443">Sivusto estetty</translation>
<translation id="2799020568854403057">Sivusto sisältää haitallisia sovelluksia</translation>
<translation id="2803306138276472711">Google-selaussuoja havaitsi sivustossa <ph name="SITE" /> äskettäin <ph name="BEGIN_LINK" />haittaohjelmia<ph name="END_LINK" />. Tavallisesti turvalliset sivustot voivat joskus saada haittaohjelmatartunnan.</translation>
<translation id="2824775600643448204">Osoite- ja hakupalkki</translation>
<translation id="2826760142808435982">Yhteys on salattu ja todennettu <ph name="CIPHER" />:n avulla ja se käyttää menetelmää <ph name="KX" /> avainvaihtomekanismina.</translation>
<translation id="2835170189407361413">Tyhjennä lomake</translation>
+<translation id="2851634818064021665">Tarvitset luvan tälle sivustolle siirtymiseen.</translation>
<translation id="2856444702002559011">Sivustolle <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää varastaa tietojasi (esimerkiksi salasanoja, viestejä tai luottokorttitietoja). <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Älä päivitä</translation>
-<translation id="2900469785430194048">Google Chromen muisti loppui verkkosivua näytettäessä.</translation>
<translation id="2909946352844186028">Verkossa havaittiin muutos.</translation>
<translation id="2916038427272391327">Sulje muita ohjelmia.</translation>
<translation id="2922350208395188000">Palvelimen varmennetta ei voi tarkistaa.</translation>
<translation id="2928905813689894207">Laskutusosoite</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" /><ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{ ja <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> toinen}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> muuta}}</translation>
<translation id="2941952326391522266">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; sen suojausvarmenne on verkkotunnuksesta <ph name="DOMAIN2" />. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
<translation id="2948083400971632585">Voit poistaa yhteydelle määritetyt välityspalvelimet käytöstä asetussivulla.</translation>
<translation id="2955913368246107853">Sulje hakupalkki</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Jos tämä on käytössä, Chromium tallentaa kortin kopion tälle laitteelle nopeuttaakseen lomakkeiden täyttöä.</translation>
<translation id="2985398929374701810">Anna kelvollinen osoite.</translation>
<translation id="2986368408720340940">Tämä noutotapa ei ole käytettävissä. Kokeile toista tapaa.</translation>
@@ -277,13 +281,11 @@
<translation id="3167968892399408617">Incognito-välilehdillä avattujen sivujen tietoja ei säilytetä selaimen historiassa, evästeissä tai hakuhistoriassa, kun kaikki incognito-välilehdet suljetaan. Ladatut tiedostot ja luodut kirjanmerkit säilytetään.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Saari</translation>
-<translation id="317583078218509884">Uudet sivuston käyttölupa-asetukset tulevat voimaan päivitettyäsi sivun.</translation>
<translation id="3176929007561373547">Tarkista välityspalvelinasetukset tai ota yhteyttä verkon järjestelmänvalvojaan
varmistaaksesi, että välityspalvelin toimii. Jos välityspalvelimen ei pitäisi olla
käytössä:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Avaa sivu incognito-tilassa.</translation>
<translation id="320323717674993345">Peruuta maksu</translation>
<translation id="3207960819495026254">Kirjanmerkeissä</translation>
<translation id="3225919329040284222">Palvelin esitti varmenteen, joka ei vastaa sisäänrakennettuja odotuksia. Tietyillä tehokkaasti suojatuilla sivustoilla on odotuksia, joilla suojataan käyttäjiä.</translation>
@@ -296,6 +298,7 @@
<translation id="3270847123878663523">K&amp;umoa uudelleenjärjestely</translation>
<translation id="3282497668470633863">Lisää kortissa oleva nimi</translation>
<translation id="3286538390144397061">Käynnistä uudelleen</translation>
+<translation id="3287510313208355388">Lataa, kun verkkoyhteys on muodostettu</translation>
<translation id="3303855915957856445">Ei hakutuloksia</translation>
<translation id="3305707030755673451">Tietosi salattiin synkronoinnin tunnuslauseesi avulla <ph name="TIME" />. Aloita synkronointi antamalla tunnuslause.</translation>
<translation id="3320021301628644560">Lisää laskutusosoite</translation>
@@ -328,7 +331,6 @@
<translation id="3452404311384756672">Hakuväli:</translation>
<translation id="3462200631372590220">Piilota lisäasetukset</translation>
<translation id="3467763166455606212">Kortinhaltijan nimi on pakollinen.</translation>
-<translation id="3478058380795961209">Voimassa (kk)</translation>
<translation id="3479539252931486093">Etkö odottanut tätä? <ph name="BEGIN_LINK" />Kerro siitä meille.<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ei nyt</translation>
<translation id="3498215018399854026">Emme tavoittaneet vanhempaasi. Yritä uudelleen.</translation>
@@ -344,6 +346,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Luotto- ja prepaid-kortit hyväksytään.</translation>
<translation id="3582930987043644930">Lisää nimi</translation>
<translation id="3583757800736429874">&amp;Toista siirto</translation>
<translation id="3586931643579894722">Piilota lisätiedot</translation>
@@ -359,10 +362,10 @@
<translation id="3655670868607891010">Jos näet tämän usein, kokeile näitä <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Päivitetty versio</translation>
<translation id="3678029195006412963">Pyynnön allekirjoittaminen epäonnistui.</translation>
+<translation id="3678529606614285348">Avaa sivu uudessa incognito-ikkunassa (Ctrl + Vaihto + N).</translation>
<translation id="3679803492151881375">Kaatumisraportti tallennettu <ph name="CRASH_TIME" />, lähetetty <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Varmenteen tiedot</translation>
<translation id="3690164694835360974">Sisäänkirjautuminen ei ole turvallinen</translation>
-<translation id="3693415264595406141">Salasana:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Ladataan...</translation>
<translation id="3712624925041724820">Käyttöluvat ovat lopussa</translation>
@@ -374,6 +377,7 @@
<translation id="3748148204939282805">Sivustolle <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää huijata sinua tekemään jotain vaarallista, kuten asentamaan ohjelmia tai paljastamaan henkilötietojasi (esimerkiksi salasanoja, puhelinnumeroita tai luottokorttitietoja). <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Käännös epäonnistui palvelinvirheen vuoksi.</translation>
<translation id="3759461132968374835">Ei viimeaikaisia kaatumisilmoituksia. Jos selain kaatui kaatumisilmoitusten ollessa pois käytöstä, ilmoituksia ei näytetä täällä.</translation>
+<translation id="3765032636089507299">Selaussuojasivu on työn alla.</translation>
<translation id="3778403066972421603">Haluatko tallentaa tämän kortin Google-tilille ja tälle laitteelle?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Vanhenee <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -386,8 +390,10 @@
<translation id="3886446263141354045">Pyyntösi päästä tälle sivustolle on lähetetty henkilölle <ph name="NAME" />.</translation>
<translation id="3890664840433101773">Lisää sähköposti</translation>
<translation id="3901925938762663762">Kortti on vanhentunut.</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Kaatumisraportti lähetetty, raporttitunnus: <ph name="CRASH_ID" /> (paikallinen kaatumistunnus: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; sen suojausvarmenteessa ei määritetä kohteen vaihtoehtoisia nimiä. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
+<translation id="3949601375789751990">Selaushistoriasi näkyy tässä.</translation>
<translation id="3963721102035795474">Lukijatila</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Ei mitään}=1{1 sivustolta }other{# sivustolta }}</translation>
<translation id="397105322502079400">Lasketaan...</translation>
@@ -416,6 +422,8 @@
<translation id="4165986682804962316">Sivustoasetukset</translation>
<translation id="4169947484918424451">Haluatko, että Chromium tallentaa tämän kortin?</translation>
<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="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>
@@ -439,12 +447,12 @@
<translation id="4356973930735388585">Tälle sivustolle hyökännyt taho voi yrittää asentaa tietokoneellesi vaarallisia ohjelmia, jotka varastavat tai poistavat tietojasi, kuten kuviasi, salasanojasi, viestejäsi ja luottokorttiesi tietoja.</translation>
<translation id="4372948949327679948">Odotettu <ph name="VALUE_TYPE" />-arvo.</translation>
<translation id="4377125064752653719">Yritit yhdistää sivustoon <ph name="DOMAIN" />, mutta varmenteen myöntäjä on kumonnut palvelimen esittämän varmenteen. Palvelimen esittämiin suojaustietoihin ei siis tule luottaa. Saatat olla tekemisissä hakkerin kanssa.</translation>
-<translation id="4381091992796011497">Käyttäjätunnus:</translation>
<translation id="4394049700291259645">Poista käytöstä</translation>
<translation id="4406896451731180161">hakutulokset</translation>
<translation id="4424024547088906515">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; Chrome ei luota sen suojausvarmenteeseen. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> ei hyväksynyt kirjautumisvarmennettasi, tai varmennetta ei annettu.</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="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>
@@ -456,12 +464,14 @@
<translation id="4587425331216688090">Poistetaanko osoite Chromen tiedoista?</translation>
<translation id="4592951414987517459">Yhteytesi kohteeseen <ph name="DOMAIN" /> on salattu nykyaikaisella salaustekniikalla.</translation>
<translation id="4594403342090139922">K&amp;umoa poisto</translation>
+<translation id="4611292653554630842">Kirjaudu sisään</translation>
<translation id="4619615317237390068">Välilehdet muista laitteista</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; sen suojausvarmenne sisältää virheitä. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
<translation id="4690462567478992370">Lopeta virheellisten varmenteiden käyttö</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Yhteys keskeytyi</translation>
+<translation id="471880041731876836">Sinulla ei ole lupaa siirtyä tälle sivustolle.</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windowsin verkon diagnostiikkaa<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Päivitä käytännöt</translation>
<translation id="4728558894243024398">Käyttöympäristö</translation>
@@ -483,14 +493,15 @@
<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="4863764087567530506">Tämä sisältö saattaa yrittää huijata sinua asentamaan ohjelmistoja tai paljastamaan henkilökohtaisia tietoja. <ph name="BEGIN_LINK" />Näytä silti<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Haku historiasta</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Todennus vaaditaan</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ja 1 muu verkkosivu}other{ja # muuta verkkosivua}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Tämä sivu on käännetty tuntemattomasta kielestä kielelle <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Maksu</translation>
<translation id="4926049483395192435">On määritettävä.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Toiminnot</translation>
<translation id="4958444002117714549">Laajenna luettelo</translation>
<translation id="4974590756084640048">Ota varoitukset uudelleen käyttöön</translation>
@@ -506,6 +517,7 @@
<translation id="5045550434625856497">Väärä salasana</translation>
<translation id="5056549851600133418">Sinulle valitut artikkelit</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Tarkista välityspalvelimen osoite.<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Saatat menettää joidenkin sivustojen suojatun sisällön käyttöoikeuden.</translation>
<translation id="5087286274860437796">Palvelimen varmenne ei ole tällä hetkellä kelvollinen.</translation>
<translation id="5087580092889165836">Lisää kortti</translation>
<translation id="5089810972385038852">Osavaltio/alue</translation>
@@ -513,18 +525,27 @@
<translation id="5095208057601539847">Provinssi</translation>
<translation id="5115563688576182185">(64-bittinen)</translation>
<translation id="5141240743006678641">Salaa synkronoidut salasanat Google-kirjautumistietojesi avulla</translation>
-<translation id="514421653919133810">Avaa sivu incognito-tilassa (Ctrl + Vaihto + N).</translation>
<translation id="5145883236150621069">Käytäntövastaus sisältää virhekoodin</translation>
+<translation id="5159010409087891077">Avaa sivu uudessa incognito-ikkunassa (⇧ + ⌘ + N).</translation>
<translation id="5171045022955879922">Kirjoita hakusanoja tai URL-osoite</translation>
<translation id="5172758083709347301">Kaikki tietokoneen käyttäjät</translation>
<translation id="5179510805599951267">Eikö kieli ole <ph name="ORIGINAL_LANGUAGE" />? Ilmoita virheestä</translation>
-<translation id="5181140330217080051">Ladataan</translation>
<translation id="5190835502935405962">Kirjanmerkkipalkki</translation>
<translation id="5199729219167945352">Kokeilut</translation>
<translation id="5205222826937269299">Nimi vaaditaan</translation>
<translation id="5222812217790122047">Sähköposti vaaditaan</translation>
<translation id="5251803541071282808">Pilvi</translation>
<translation id="5277279256032773186">Käytätkö Chromea töissä? Yritykset voivat hallita Chromen asetuksia työntekijöidensä puolesta. Lisätietoja</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Voit poistaa ohjelmiston väliaikaisesti käytöstä ja päästä verkkoon noudattamalla näitä ohjeita. Nämä vaiheet edellyttävät järjestelmänvalvojan käyttöoikeuksia.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Valitse <ph name="BEGIN_BOLD" />Käynnistä<ph name="END_BOLD" /> ja hae ja valitse sen jälkeen <ph name="BEGIN_BOLD" />Näytä paikalliset palvelut<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Valitse <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Siirry <ph name="BEGIN_BOLD" />Käynnistystapa<ph name="END_BOLD" />-kohtaan ja valitse <ph name="BEGIN_BOLD" />Ei käytössä<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Siirry <ph name="BEGIN_BOLD" />Palvelun tila<ph name="END_BOLD" /> ‑kohtaan ja valitse <ph name="BEGIN_BOLD" />Pysäytä<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Valitse <ph name="BEGIN_BOLD" />Käytä<ph name="END_BOLD" /> ja sen jälkeen <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Saat ohjeita ohjelmiston pysyvään poistamiseen tietokoneelta siirtymällä <ph name="BEGIN_LEARN_MORE_LINK" />Chrome-ohjekeskukseen<ph name="END_LEARN_MORE_LINK" />.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Yhteytesi tähän sivustoon ei ole yksityinen. Voit poistua VR-tilasta milloin tahansa ottamalla headsetin pois ja valitsemalla Takaisin.</translation>
<translation id="5299298092464848405">Virhe jäsennettäessä käytäntöä</translation>
<translation id="5308689395849655368">Kaatumisraportit on poistettu käytöstä.</translation>
@@ -541,9 +562,10 @@
<translation id="5439770059721715174">Mallin todennusvirhe kohdassa <ph name="ERROR_PATH" />: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Tätä sivuston <ph name="HOST_NAME" /> sivua ei löydy.</translation>
<translation id="5455374756549232013">Virheellinen käytännön aikaleima</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> / <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Virheellinen</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> toinen}other{<ph name="CONTACT_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> muuta}}</translation>
<translation id="5470861586879999274">&amp;Toista muokkaus</translation>
+<translation id="5481076368049295676">Tämä sisältö saattaa yrittää asentaa vaarallisia ohjelmistoja laitteellesi tai varastaa tai poistaa tietojasi. <ph name="BEGIN_LINK" />Näytä silti<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Lisää kelvollinen osoite</translation>
<translation id="5492298309214877701">Tällä yrityksen, organisaation tai oppilaitoksen intranetissä olevalla sivustolla on sama URL-osoite kuin ulkoisella verkkosivustolla.
<ph name="LINE_BREAK" />
@@ -555,6 +577,7 @@
<translation id="5540224163453853">Pyydettyä artikkelia ei löydy.</translation>
<translation id="5544037170328430102">Viesti upotetulta sivulta osoitteessa <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Lataa uudelleen</translation>
+<translation id="5560088892362098740">Viimeinen voimassaolopäivä</translation>
<translation id="5565735124758917034">Aktiivinen</translation>
<translation id="5571083550517324815">Nouto tästä osoitteesta ei onnistu. Valitse eri osoite.</translation>
<translation id="5572851009514199876">Aloita ja kirjaudu sisään, jotta Chrome voi tarkistaa, onko sinulla oikeus käyttää tätä sivustoa.</translation>
@@ -569,12 +592,13 @@
<translation id="5629630648637658800">Käytännön asetuksien lataaminen epäonnistui</translation>
<translation id="5631439013527180824">Laitteenhallintatunnus on virheellinen</translation>
<translation id="5633066919399395251">Sivustolle <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää asentaa tietokoneellesi vaarallisia ohjelmia, jotka varastavat tai poistavat tietojasi, esimerkiksi kuvia, salasanoja, viestejä tai luottokorttitietoja. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Harhaanjohtava sisältö estetty</translation>
<translation id="5646376287012673985">Sijainti</translation>
<translation id="5659593005791499971">Sähköposti</translation>
<translation id="5669703222995421982">Hanki räätälöityä sisältöä</translation>
<translation id="5675650730144413517">Sivu ei toimi</translation>
<translation id="5710435578057952990">Tämän sivuston identiteettiä ei ole vahvistettu.</translation>
-<translation id="5713016350996637505">Harhaanjohtava sisältö estetty</translation>
+<translation id="5719499550583120431">Prepaid-kortit hyväksytään.</translation>
<translation id="5720705177508910913">Nykyinen käyttäjä</translation>
<translation id="5732392974455271431">Vanhempasi voivat kumota eston puolestasi.</translation>
<translation id="5763042198335101085">Anna voimassa oleva sähköpostiosoite.</translation>
@@ -586,15 +610,14 @@
<translation id="5803412860119678065">Täytetäänkö kortin <ph name="CARD_DETAIL" /> tiedot?</translation>
<translation id="5810442152076338065">Yhteytesi kohteeseen <ph name="DOMAIN" /> on salattu vanhentuneella salaustekniikalla.</translation>
<translation id="5813119285467412249">&amp;Toista lisäys</translation>
-<translation id="5814352347845180253">Saatat menettää esim. sivuston <ph name="SITE" /> maksullisen sisällön käyttöoikeuden.</translation>
<translation id="5838278095973806738">Älä anna tälle sivustolle salasanoja, luottokorttinumeroita tai muita arkaluonteisia tietoja, sillä hyökkääjät saattavat varastaa ne.</translation>
<translation id="5869405914158311789">Sivustoon ei saada yhteyttä</translation>
<translation id="5869522115854928033">Tallennetut salasanat</translation>
<translation id="5872918882028971132">Ylätason ehdotukset</translation>
+<translation id="5893752035575986141">Luottokortit hyväksytään.</translation>
<translation id="5901630391730855834">Keltainen</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="5926846154125914413">Saatat menettää joidenkin sivustojen maksullisen sisällön käyttöoikeuden.</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="5967867314010545767">Poista historiasta</translation>
<translation id="5975083100439434680">Loitonna</translation>
@@ -610,6 +633,7 @@
<translation id="6040143037577758943">Sulje</translation>
<translation id="6042308850641462728">Lisää</translation>
<translation id="6047233362582046994">Jos ymmärrät käyntiä koskevat turvallisuusriskit, voit <ph name="BEGIN_LINK" />siirtyä tälle sivustolle<ph name="END_LINK" /> jo ennen haitallisten sovellusten poistamista.</translation>
+<translation id="6047927260846328439">Tämä sisältö saattaa yrittää huijata sinua asentamaan ohjelmistoja tai paljastamaan henkilökohtaisia tietoja. <ph name="BEGIN_LINK" />Näytä silti<ph name="END_LINK" /></translation>
<translation id="6051221802930200923"><ph name="SITE" /> ei juuri nyt ole käytettävissä, koska se käyttää varmenteiden kiinnittämistä. Verkkovirheet ja hyökkäykset ovat yleensä väliaikaisia, joten sivu luultavasti toimii myöhemmin.</translation>
<translation id="6060685159320643512">Varoitus, nämä kokeilut saattavat puraista</translation>
<translation id="6080696365213338172">Käytät sisältöä järjestelmänvalvojan myöntämällä varmenteella. Järjestelmänvalvoja voi käyttää verkkotunnukselle <ph name="DOMAIN" /> lähettämiäsi tietoja.</translation>
@@ -623,7 +647,6 @@
<translation id="6165508094623778733">Lisätietoja</translation>
<translation id="6169916984152623906">Nyt voit selata verkkoa yksityisesti. Laitteen muut käyttäjät eivät näe selaushistoriaasi. Lataukset ja kirjanmerkit kuitenkin tallennetaan.</translation>
<translation id="6177128806592000436">Sivustoon ei ole muodostettu turvallista yhteyttä.</translation>
-<translation id="6184817833369986695">(kohortti: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Tarkista internetyhteytesi</translation>
<translation id="6218753634732582820">Poistetaanko osoite Chromiumista?</translation>
<translation id="6221345481584921695">Google-selaussuoja havaitsi sivustossa <ph name="SITE" /> äskettäin <ph name="BEGIN_LINK" />haittaohjelmia<ph name="END_LINK" />. Tavallisesti turvalliset sivustot voivat joskus saada haittaohjelmatartunnan. Haitallinen sisältö on peräisin tunnetusta haittaohjelmien jakelusivustosta nimeltä <ph name="SUBRESOURCE_HOST" />.</translation>
@@ -642,13 +665,14 @@
<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>
<translation id="6387478394221739770">Oletko kiinnostunut Chromen uusista jännittävistä ominaisuuksista? Kokeile beta-kanavaa osoitteessa chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromiumin muisti loppui verkkosivua näytettäessä.</translation>
+<translation id="6397451950548600259">Tietokoneelle asennettu ohjelmisto estää Chromea muodostamasta turvallista yhteyttä verkkoon</translation>
<translation id="6404511346730675251">Muokkaa kirjanmerkkiä</translation>
<translation id="6410264514553301377">Anna kortin <ph name="CREDIT_CARD" /> vanhentumispäivä ja CVC</translation>
<translation id="6414888972213066896">Pyysit vanhemmiltasi lupaa käydä tällä sivustolla.</translation>
@@ -663,6 +687,7 @@
<translation id="647261751007945333">Laitekäytännöt</translation>
<translation id="6477321094435799029">Chrome havaitsi tällä sivulla epätavallista koodia ja esti sen suojellakseen henkilötietojasi (esimerkiksi salasanoja, puhelinnumeroita tai luottokorttitietoja).</translation>
<translation id="6489534406876378309">Aloita kaatumistietojen lähettäminen</translation>
+<translation id="6507833130742554667">Luotto- ja maksukortit hyväksytään.</translation>
<translation id="6508722015517270189">Käynnistä Chrome uudelleen.</translation>
<translation id="6529602333819889595">&amp;Toista poisto</translation>
<translation id="6534179046333460208">Fyysisen webin ehdotukset</translation>
@@ -671,13 +696,13 @@
<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="657639383826808334">Tämä sisältö saattaa yrittää asentaa vaarallisia ohjelmistoja laitteellesi tai varastaa tai poistaa tietojasi. <ph name="BEGIN_LINK" />Näytä silti<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Salausasetukset</translation>
<translation id="662080504995468778">Jää</translation>
<translation id="6626291197371920147">Lisää kelvollinen kortin numero</translation>
<translation id="6628463337424475685"><ph name="ENGINE" />-haku</translation>
<translation id="6630809736994426279">Sivustolle <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää asentaa Maciisi 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="6644283850729428850">Tämä käytäntö on vanhentunut.</translation>
+<translation id="6657585470893396449">Salasana</translation>
<translation id="6671697161687535275">Poistetaanko lomake-ehdotus Chromiumista?</translation>
<translation id="6685834062052613830">Kirjaudu ulos ja suorita määritys loppuun.</translation>
<translation id="6710213216561001401">Edellinen</translation>
@@ -708,7 +733,6 @@
<translation id="6970216967273061347">Alue</translation>
<translation id="6973656660372572881">Sekä kiinteät välityspalvelimet että .pac-URL-osoite on määritetty.</translation>
<translation id="6989763994942163495">Näytä lisäasetukset...</translation>
-<translation id="7000990526846637657">Historiamerkintöjä ei löytynyt</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Google-tililläsi voi olla muita selaushistoriatietoja osoitteessa <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />.</translation>
<translation id="7029809446516969842">Salasanat</translation>
@@ -723,7 +747,9 @@
<translation id="7129409597930077180">Lähetys tähän osoitteeseen ei onnistu. Valitse eri osoite.</translation>
<translation id="7138472120740807366">Toimitustapa</translation>
<translation id="7139724024395191329">Emiirikunta</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> toinen}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> muuta}}</translation>
<translation id="7155487117670177674">Maksu ei ole turvallinen</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> toinen}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> muuta}}</translation>
<translation id="7179921470347911571">Käynnistä uudelleen</translation>
<translation id="7180611975245234373">Päivitä</translation>
<translation id="7182878459783632708">Käytäntöjä ei ole asetettu</translation>
@@ -764,6 +790,7 @@
<translation id="7514365320538308">Lataa</translation>
<translation id="7518003948725431193">Verkkosivua ei löytynyt osoitteelle: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Sivustoon muodostamasi yhteys ei ole yksityinen.</translation>
<translation id="7535087603100972091">Arvo</translation>
<translation id="7537536606612762813">Pakollinen</translation>
<translation id="7542403920425041731">Vahvistamisen jälkeen korttisi tiedot jaetaan sivuston kanssa.</translation>
@@ -773,7 +800,6 @@
<translation id="7552846755917812628">Kokeile seuraavia keinoja:</translation>
<translation id="7554791636758816595">Uusi välilehti</translation>
<translation id="7567204685887185387">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; sen suojausvarmenne on ehkä luotu vilpillisesti. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> toinen}other{<ph name="CONTACT_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> muuta}}</translation>
<translation id="7568593326407688803">Tämä sivu on kirjoitettu kielellä<ph name="ORIGINAL_LANGUAGE" />Haluatko kääntää sen?</translation>
<translation id="7569952961197462199">Poistetaanko luottokortti Chromen tiedoista?</translation>
<translation id="7569983096843329377">Musta</translation>
@@ -800,9 +826,11 @@
<translation id="7714464543167945231">Varmenne</translation>
<translation id="7716147886133743102">Järjestelmänvalvojan estämä</translation>
<translation id="7716424297397655342">Tätä sivustoa ei voi ladata välimuistista</translation>
+<translation id="774634243536837715">Vaarallinen sisältö estetty</translation>
<translation id="7752995774971033316">Ei hallinnoida</translation>
<translation id="7755287808199759310">Vanhempasi voi kumota eston puolestasi.</translation>
<translation id="7758069387465995638">Palomuuri tai virustorjuntaohjelmisto on saattanut estää yhteyden.</translation>
+<translation id="7759163816903619567">Näytä verkkotunnus:</translation>
<translation id="7761701407923456692">Palvelimen varmenne ei vastaa URL-osoitetta.</translation>
<translation id="7763386264682878361">Maksuluettelon jäsentäjä</translation>
<translation id="7764225426217299476">Lisää osoite</translation>
@@ -817,6 +845,7 @@
<translation id="7815407501681723534">Haku <ph name="SEARCH_STRING" /> tuotti <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" />.</translation>
<translation id="785549533363645510">Et ole kuitenkaan näkymätön. Incognito-tilan käyttäminen ei kätke selaamistasi työnantajaltasi, internetpalveluntarjoajaltasi tai käyttämiltäsi sivustoilta.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Maksu- ja prepaid-kortit hyväksytään.</translation>
<translation id="7887683347370398519">Tarkista CVC ja yritä uudelleen.</translation>
<translation id="79338296614623784">Anna kelvollinen puhelinnumero.</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -836,7 +865,6 @@
<translation id="8041089156583427627">Lähetä palautetta</translation>
<translation id="8041940743680923270">Käytä yleistä oletusasetusta (kysy)</translation>
<translation id="8088680233425245692">Artikkelin näyttäminen epäonnistui.</translation>
-<translation id="8089520772729574115">alle 1 Mt</translation>
<translation id="8091372947890762290">Aktivointi odottaa palvelimella</translation>
<translation id="8118489163946903409">Maksutapa</translation>
<translation id="8131740175452115882">Vahvista</translation>
@@ -848,10 +876,13 @@
<translation id="8194797478851900357">K&amp;umoa siirto</translation>
<translation id="8201077131113104583">Laajennuksella, jonka tunnus on <ph name="EXTENSION_ID" />, on virheellinen päivitys-URL-osoite.</translation>
<translation id="8202097416529803614">Tilauksen yhteenveto</translation>
+<translation id="8205463626947051446">Sivusto näyttää häiritseviä mainoksia.</translation>
<translation id="8218327578424803826">Määrätty sijainti:</translation>
<translation id="8225771182978767009">Tämän tietokoneen määrittänyt henkilö on estänyt tämän sivuston.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Avaa sivu uudessa incognito-välilehdessä.</translation>
<translation id="8241707690549784388">Etsimäsi sivu käytti antamiasi tietoja. Sivulle palaaminen voi johtaa jokaisen tekemäsi toiminnon toistamiseen. Haluatko jatkaa?</translation>
+<translation id="8241712895048303527">Estä tällä sivustolla</translation>
<translation id="8249320324621329438">Viimeksi haettu:</translation>
<translation id="8253091569723639551">Laskutusosoite tarvitaan</translation>
<translation id="8261506727792406068">Poista</translation>
@@ -862,7 +893,6 @@
<translation id="8308427013383895095">Käännös epäonnistui, koska verkkoyhteydessä esiintyi ongelmia.</translation>
<translation id="8332188693563227489">Sivuston <ph name="HOST_NAME" /> käyttöoikeus evättiin</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="8344669043927012510">Avaa sivu incognito-tilassa (⇧ + ⌘ + N).</translation>
<translation id="8349305172487531364">Kirjanmerkkipalkki</translation>
<translation id="8363502534493474904">Poista lentokonetila käytöstä.</translation>
<translation id="8364627913115013041">Ei määritetty.</translation>
@@ -872,6 +902,7 @@
<translation id="8398259832188219207">Kaatumisraportti lähetetty <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Kaatumiset (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Kirjoita sama tunnuslause kahdesti.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Asetukset</translation>
<translation id="8433057134996913067">Sinut kirjataan ulos useimmilta verkkosivustoilta.</translation>
<translation id="8437238597147034694">K&amp;umoa siirto</translation>
@@ -879,8 +910,9 @@
<translation id="8483780878231876732">Jos haluat käyttää Google-tilillesi tallennettuja kortteja, kirjaudu Chromeen</translation>
<translation id="8488350697529856933">Käyttöalue</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ei vastannut riittävän nopeasti.</translation>
-<translation id="8532105204136943229">Voimassa (vuosi)</translation>
+<translation id="8503813439785031346">Käyttäjätunnus</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="8571890674111243710">Käännetään sivua kielelle <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Lisää puh.nro
@@ -888,6 +920,7 @@
<translation id="859285277496340001">Varmenne ei määritä mekanismia, jonka avulla voitaisiin tarkistaa, onko varmenne kumottu.</translation>
<translation id="8620436878122366504">Vanhempasi eivät ole hyväksyneet sitä vielä.</translation>
<translation id="8647750283161643317">Palauta kaikki oletusarvoon</translation>
+<translation id="8660471606262461360">Google Paymentsista</translation>
<translation id="8703575177326907206">Yhteyttäsi verkkotunnukseen <ph name="DOMAIN" /> ei ole salattu.</translation>
<translation id="8718314106902482036">Maksua ei suoritettu loppuun</translation>
<translation id="8725066075913043281">Yritä uudelleen</translation>
@@ -915,6 +948,7 @@
<translation id="8903921497873541725">Lähennä</translation>
<translation id="8931333241327730545">Haluatko tallentaa tämän kortin Google-tilillesi?</translation>
<translation id="8932102934695377596">Kellosi jätättää</translation>
+<translation id="8938939909778640821">Hyväksytyt luotto- ja prepaid-kortit</translation>
<translation id="8971063699422889582">Palvelimen varmenne on vanhentunut.</translation>
<translation id="8986494364107987395">Lähetä Googlelle käyttötilastoja ja virheraportteja automaattisesti</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -944,7 +978,6 @@
<translation id="9169664750068251925">Estä aina tämä sivusto</translation>
<translation id="9170848237812810038">K&amp;umoa</translation>
<translation id="917450738466192189">Palvelimen varmenne ei kelpaa.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> toinen}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ja <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> muuta}}</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>
<translation id="9207861905230894330">Artikkelin lisääminen epäonnistui.</translation>
@@ -953,9 +986,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">TYHJENNÄ LOMAKE</translation>
<translation id="939736085109172342">Uusi kansio</translation>
-<translation id="941721044073577244">Vaikuttaa siltä, että sinulla ei ole lupaa käyttää tätä sivustoa.</translation>
<translation id="969892804517981540">Virallinen koontiversio</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Ei mitään}=1{1 kohde}other{# kohdetta}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Kehittäjän koontiversio</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_fil.xtb b/chromium/components/strings/components_strings_fil.xtb
index ef699ea5996..cbf3fd95fc0 100644
--- a/chromium/components/strings/components_strings_fil.xtb
+++ b/chromium/components/strings/components_strings_fil.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Isara ang iba pang app</translation>
<translation id="1055184225775184556">&amp;I-undo ang Pagdagdag</translation>
<translation id="10614374240317010">Hindi kailanman nag-save</translation>
+<translation id="1066396345355680611">Maaari kang mawalan ng access sa protektadong content mula sa <ph name="SITE" /> at ilang iba pang site.</translation>
<translation id="106701514854093668">Mga Bookmark sa Desktop</translation>
<translation id="1074497978438210769">Hindi secure</translation>
<translation id="1080116354587839789">Pagkasyahin sa lapad</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Listahan ng babasahin</translation>
<translation id="1264126396475825575">Ulat ng pag-crash na nakuha noong <ph name="CRASH_TIME" /> (hindi pa naa-upload o nababalewala)</translation>
<translation id="1281526147609854549">Inisyu ng <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Na-block ang mapanganib na content</translation>
<translation id="1285320974508926690">Huwag isalin kailanman ang site na ito</translation>
<translation id="129553762522093515">Kamakailang isinara</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Subukang i-clear ang iyong cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domain ng pagpapatala:</translation>
<translation id="1340482604681802745">Address sa pag-pick up</translation>
-<translation id="1344211575059133124">Mukhang kailangan mo ng pahintulot upang mabisita ang site na ito</translation>
<translation id="1344588688991793829">Mga setting ng Autofill ng Chromium...</translation>
<translation id="1348198688976932919">Naglalaman ng mga mapanganib na app ang pupuntahang site</translation>
<translation id="1374468813861204354">mga suhestiyon</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Ang pagkilanlan ng <ph name="ORGANIZATION" /> sa <ph name="LOCALITY" /> ay napatotohanan ng <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Oo</translation>
<translation id="1430915738399379752">I-print</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> pa}one{<ph name="PAYMENT_METHOD_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> pa}other{<ph name="PAYMENT_METHOD_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> pa}}</translation>
<translation id="1506687042165942984">Magpakita ng naka-save (ibigsabihin alam na hindi napapanahon) na kopya ng page na ito.</translation>
<translation id="1517433312004943670">Kinakailangan ang numero ng telepono</translation>
+<translation id="1517500485252541695">Mga tinatanggap na credit at debit card</translation>
<translation id="1519264250979466059">Petsa ng Build</translation>
+<translation id="1527263332363067270">Naghihintay ng koneksyon…</translation>
<translation id="153384715582417236">'Yan na muna sa ngayon</translation>
<translation id="1549470594296187301">Dapat naka-enable ang JavaScript upang magamit ang feature na ito.</translation>
<translation id="1555130319947370107">Asul</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">I-download ang page sa ibang pagkakataon</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Kinakailangang field</translation>
<translation id="187918866476621466">Buksan ang mga page sa pagsisimula</translation>
<translation id="1883255238294161206">Tiklupin ang listahan</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> pa}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> pa}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> pa}}</translation>
<translation id="1898423065542865115">Pagfi-filter</translation>
+<translation id="1916770123977586577">Upang ilapat ang iyong mga na-update na setting sa site na ito, i-reload ang page</translation>
+<translation id="1919345977826869612">Mga Ad</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Wala}=1{1 site}one{# site}other{# na site}}</translation>
<translation id="194030505837763158">Pumunta sa <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Mga tinatanggap na prepaid card</translation>
<translation id="1962204205936693436">Mga Bookmark ng <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Error sa serialization</translation>
<translation id="1974060860693918893">Advanced</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Error sa HTTP</translation>
<translation id="2270484714375784793">Numero ng telepono</translation>
<translation id="2282872951544483773">Mga Eksperimentong Hindi Available</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item}one{<ph name="ITEM_COUNT" /> item}other{<ph name="ITEM_COUNT" /> na item}}</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>
<translation id="2337852623177822836">Kinokontrol ng iyong administrator ang setting</translation>
<translation id="2354001756790975382">Iba pang mga bookmark</translation>
<translation id="2354430244986887761"><ph name="BEGIN_LINK" />May nahanap na mga mapanganib na app<ph name="END_LINK" /> kamakailan ang Ligtas na Pag-browse ng Google sa <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Magpatuloy</translation>
<translation id="2365563543831475020">Hindi na-upload ang nakuhang ulat ng pag-crash noong <ph name="CRASH_TIME" /></translation>
<translation id="2367567093518048410">Antas</translation>
-<translation id="237718015863234333">Walang mga available na alternatibo sa UI</translation>
<translation id="2384307209577226199">Default ng enterprise</translation>
<translation id="2386255080630008482">Nabawi ang certificate ng server.</translation>
<translation id="2392959068659972793">Ipakita ang mga patakarang walang nakatakdang halaga</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Kung may check, mag-iimbak ang Chromium ng kopya ng iyong card sa device na ito para sa mas mabilis na pagsagot sa form.</translation>
<translation id="2498091847651709837">Mag-scan ng bagong card</translation>
<translation id="2501278716633472235">Bumalik</translation>
+<translation id="2503184589641749290">Mga tinatanggap na debit at prepaid card</translation>
<translation id="2515629240566999685">Suriin ang signal sa iyong lugar</translation>
-<translation id="2516305470678292029">Mga Alternatibo sa UI</translation>
<translation id="2539524384386349900">Tukuyin</translation>
<translation id="255002559098805027">Nagpadala ng di-wastong tugon ang <ph name="HOST_NAME" />.</translation>
<translation id="2556876185419854533">&amp;I-undo ang Pag-e-edit</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Pamamaraan ng pagpapadala</translation>
<translation id="277499241957683684">Nawawalang tala ng device</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>
<translation id="2799020568854403057">Naglalaman ng mga mapaminsalang app ang pupuntahang site</translation>
<translation id="2803306138276472711">Kamakailan lang, ang Google Safe Browsing ay <ph name="BEGIN_LINK" />nakakita ng malware<ph name="END_LINK" /> sa <ph name="SITE" />. Paminsan-minsan, nagkakaroon ng malware ang mga website na karaniwang ligtas.</translation>
<translation id="2824775600643448204">Address bar at bar sa paghahanap</translation>
<translation id="2826760142808435982">Ine-encrypt at pinapatotoo ang koneksyon gamit ang <ph name="CIPHER" /> at ginagamit ang <ph name="KX" /> bilang key exchange mechanism.</translation>
<translation id="2835170189407361413">I-clear ang form</translation>
+<translation id="2851634818064021665">Kailangan mo ng pahintulot upang mabisita ang site na ito</translation>
<translation id="2856444702002559011">Maaaring sinusubukang nakawin ng mga attacker ang iyong impormasyon mula sa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (halimbawa, mga password, mensahe, o credit card). <ph name="BEGIN_LEARN_MORE_LINK" />Matuto pa<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Huwag I-reload</translation>
-<translation id="2900469785430194048">Naubusan ng memory ang Google Chrome habang sinusubukang ipakita ang webpage na ito.</translation>
<translation id="2909946352844186028">May nakitang pagbabago sa network.</translation>
<translation id="2916038427272391327">Isara ang iba pang program</translation>
<translation id="2922350208395188000">Hindi masuri ang certificate ng server.</translation>
<translation id="2928905813689894207">Billing Address</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> pa}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> pa}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> pa}}</translation>
<translation id="2941952326391522266">Hindi mapatunayan ng server na ito na ito ay <ph name="DOMAIN" />; mula sa <ph name="DOMAIN2" /> ang certificate ng seguridad nito. Maaaring dulot ito ng maling configuration o isang umaatake na hinahadlangan ang iyong koneksyon.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Kung naka-enable, mag-iimbak ang Chromium ng kopya ng iyong card sa device na ito para sa mas mabilis na pagsagot sa form.</translation>
<translation id="2985398929374701810">Maglagay ng wastong address</translation>
<translation id="2986368408720340940">Hindi available ang pamamaraan ng pag-pick up na ito. Sumubok ng ibang pamamaraan.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Ang mga page na titingnan mo sa mga tab na incognito ay hindi mananatili sa history, cookie store o history ng paghahanap ng iyong browser kapag naisara mo na ang lahat ng iyong tab na incognito. Papanatilihin ang anumang mga file na ida-download mo o mga bookmark na gagawin mo.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Pulo</translation>
-<translation id="317583078218509884">Magkakaroon ng bisa ang mga bagong setting ng pahintulot sa site pagkatapos i-reload ang pahina.</translation>
<translation id="3176929007561373547">Tingnan ang mga setting ng iyong proxy o makipag-ugnayan sa iyong network
administrator upang matiyak na gumagana ang proxy server. Kung sa palagay mo
ay hindi ka dapat gumagamit ng proxy server:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Buksan ang page sa Incognito mode</translation>
<translation id="320323717674993345">Kanselahin ang Pagbabayad</translation>
<translation id="3207960819495026254">Naka-bookmark</translation>
<translation id="3225919329040284222">Nagpakita ang server ng certificate na hindi tumutugma sa mga built-in na inaasahan. Ang mga inaasahang ito ay isinama para sa ilang partikular na website na may mataas na antas ng seguridad upang maprotektahan ka.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;I-undo ang Pagbabago sa Ayos</translation>
<translation id="3282497668470633863">Magdagdag ng pangalan sa card</translation>
<translation id="3286538390144397061">I-restart Ngayon</translation>
+<translation id="3287510313208355388">I-download kapag online</translation>
<translation id="3303855915957856445">Walang nakitang resulta ng paghahanap</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Kunin ang agwat:</translation>
<translation id="3462200631372590220">Itago ang advanced</translation>
<translation id="3467763166455606212">Kinakailangan ang pangalan ng cardholder</translation>
-<translation id="3478058380795961209">Buwan ng Pag-expire</translation>
<translation id="3479539252931486093">Hindi mo ba ito inaasahan? <ph name="BEGIN_LINK" />Ipaalam sa amin<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Hindi ngayon</translation>
<translation id="3498215018399854026">Hindi namin makaugnayan ang iyong magulang sa sandaling ito. Pakisubukang muli.</translation>
@@ -343,12 +345,13 @@
&lt;p&gt;Pakiayos 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; <ph name="BEGIN_LEARN_MORE_LINK" />Matuto pa<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Tinatanggap ang mga credit at prepaid card.</translation>
<translation id="3582930987043644930">Magdagdag ng pangalan</translation>
<translation id="3583757800736429874">&amp;Gawing Muli ang Paglilipat</translation>
<translation id="3586931643579894722">Magtago ng mga detalye</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Maglagay ng wastong petsa ng pag-expire</translation>
-<translation id="36224234498066874">Clear Browsing Data...</translation>
+<translation id="36224234498066874">I-clear ang Data sa Pag-browse...</translation>
<translation id="362276910939193118">Ipakita ang Buong History</translation>
<translation id="3623476034248543066">Ipakita ang halaga</translation>
<translation id="3630155396527302611">Kung nakalista na ito bilang isang programang pinapahintulutang i-access ang network, subukang
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Kung madalas mo itong nakikita, subukan ang mga ito <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Rebisyon</translation>
<translation id="3678029195006412963">Hindi malagdaan ang kahilingan</translation>
+<translation id="3678529606614285348">Buksan ang page sa bagong Incognito window (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Nakuha ang ulat ng pag-crash noong <ph name="CRASH_TIME" />, na-upload noong <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Impormasyon sa certificate</translation>
<translation id="3690164694835360974">Hindi ligtas ang pag-log in</translation>
-<translation id="3693415264595406141">Password:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Naglo-load...</translation>
<translation id="3712624925041724820">Naubos na ang mga lisensya</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Maaaring linlangin ka ng mga attacker sa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> na gawin ang isang mapanganib na bagay tulad ng pag-install ng software o paghayag ng iyong personal na impormasyon (halimbawa, mga password, numero ng telepono, o credit card). <ph name="BEGIN_LEARN_MORE_LINK" />Matuto pa<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Nabigo ang translation dahil sa error sa server.</translation>
<translation id="3759461132968374835">Wala kang kamakailang iniulat na mga pag-crash. Hindi lilitaw dito ang mga pag-crash na naganap kapag hindi pinagana ang pag-uulat ng pag-crash.</translation>
+<translation id="3765032636089507299">Kasalukuyang ginagawa ang Ligtas na Pag-browse.</translation>
<translation id="3778403066972421603">Gusto mo bang i-save ang card na ito sa iyong Google Account at sa device na ito?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Mag-e-expire sa <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Ipinadala ang iyong kahilingang i-access ang site na ito kay <ph name="NAME" /></translation>
<translation id="3890664840433101773">Magdagdag ng email</translation>
<translation id="3901925938762663762">Na-expire na ang card</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID ng Na-upload na Ulat ng Pag-crash <ph name="CRASH_ID" /> (ID ng Lokal na Pag-crash: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Hindi mapatunayan ng server na ito na <ph name="DOMAIN" /> ito; hindi tinutukoy ng certificate ng seguridad nito ang Mga Alternatibong Pangalan ng Subject. Maaaring dahil ito sa isang maling configuration o sa isang umaatake na humahadlang sa iyong koneksyon.</translation>
+<translation id="3949601375789751990">Lalabas dito ang iyong history ng pag-browse</translation>
<translation id="3963721102035795474">Reader Mode</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Wala}=1{Mula sa 1 site }one{Mula sa # site }other{Mula sa # na site }}</translation>
<translation id="397105322502079400">Kinakalkula...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Mga setting ng site</translation>
<translation id="4169947484918424451">Gusto mo bang i-save ng Chromium ang card na ito?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Maaaring subukan ng mga attacker sa site na ito 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).</translation>
<translation id="4372948949327679948">Inaasahang <ph name="VALUE_TYPE" /> na halaga.</translation>
<translation id="4377125064752653719">Tinangka mong maabot ang <ph name="DOMAIN" />, subalit ang certificate na ipinakita ng server ay binawi ng nagbigay nito. Nangangahulugan ito na ang mga kredensyal sa seguridad na ipinakita ng server ay talagang hindi dapat pagkatiwalaan. Maaaring nakikipag-ugnay ka sa isang nang-aatake.</translation>
-<translation id="4381091992796011497">User Name:</translation>
<translation id="4394049700291259645">Huwag paganahin</translation>
<translation id="4406896451731180161">mga resulta ng paghahanap</translation>
<translation id="4424024547088906515">Hindi mapatunayan ng server na ito na ito ay <ph name="DOMAIN" />; hindi pinagkakatiwalaan ng Chrome ang certificate ng seguridad nito. Maaaring dulot ito ng maling configuration o isang umaatake na hinahadlangan ang iyong koneksyon.</translation>
<translation id="4432688616882109544">Hindi tinanggap ng <ph name="HOST_NAME" /> ang iyong certificate sa pag-log in o maaaring walang ibinigay.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Alisin ang address sa Chrome?</translation>
<translation id="4592951414987517459">Naka-encrypt ang iyong koneksyon sa <ph name="DOMAIN" /> gamit ang isang makabagong cipher suite.</translation>
<translation id="4594403342090139922">&amp;I-undo ang Pagtanggal</translation>
+<translation id="4611292653554630842">Mag-log in</translation>
<translation id="4619615317237390068">Mga tab mula sa iba pang mga device</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Hindi mapatunayan ng server na ito na ito ay <ph name="DOMAIN" />; naglalaman ng mga error ang certificate ng seguridad nito. Maaaring dulot ito ng maling configuration o isang umaatake na hinahadlangan ang iyong koneksyon.</translation>
<translation id="4690462567478992370">Ihinto ang paggamit ng di-wastong certificate</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Naputol ang iyong koneksyon</translation>
+<translation id="471880041731876836">Wala kang pahintulot na bisitahin ang site na ito</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Magpatakbo ng Windows Network Diagnostics<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">I-reload ang mga patakaran</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Maaaring subukan kang linlangin ng content na ito na mag-install ng software o maghayag ng personal na impormasyon. <ph name="BEGIN_LINK" />Ipakita pa rin<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">History ng paghahanap</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Kinakailangan ng pag-authenticate</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{at 1 pang web page}one{at # pang web page}other{at # pang web page}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Na-translate ang pahinang ito mula sa hindi kilalang wika patungo sa <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pagbabayad</translation>
<translation id="4926049483395192435">Dapat na tukuyin.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Mga Pagkilos</translation>
<translation id="4958444002117714549">Palawakin ang listahan</translation>
<translation id="4974590756084640048">Muling i-enable ang mga babala</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Hindi wastong password</translation>
<translation id="5056549851600133418">Mga artikulo para sa iyo</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Suriin ang proxy address<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Mawawalan ka ng access sa protektadong content mula sa ilang site.</translation>
<translation id="5087286274860437796">Hindi angkop ang certificate ng server sa oras na ito.</translation>
<translation id="5087580092889165836">Magdagdag ng card</translation>
<translation id="5089810972385038852">Estado</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Probinsya</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5141240743006678641">I-encrypt ang mga naka-sync na password gamit ang iyong mga kredensyal sa Google</translation>
-<translation id="514421653919133810">Buksan ang page sa Incognito mode (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">May code ng error sa tugon sa patakaran</translation>
+<translation id="5159010409087891077">Buksan ang page sa bagong Incognito window (⇧⌘N)</translation>
<translation id="5171045022955879922">Hanapin o i-type ang URL</translation>
<translation id="5172758083709347301">Computer</translation>
<translation id="5179510805599951267">Wala sa <ph name="ORIGINAL_LANGUAGE" />? Iulat ang error na ito</translation>
-<translation id="5181140330217080051">Nagda-download</translation>
<translation id="5190835502935405962">Bar ng Mga Bookmark</translation>
<translation id="5199729219167945352">Mga Eksperimento</translation>
<translation id="5205222826937269299">Kailangan ng pangalan</translation>
<translation id="5222812217790122047">Kailangan ng email</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Ginagamit mo ba ang Chrome sa trabaho? Maaaring pamahalaan ng mga negosyo ang mga setting ng Chrome para sa kanilang mga empleyado. Matuto pa</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Sundin ang mga hakbang na ito upang pansamantalang i-disable ang software upang makapunta ka sa web. Kakailanganin mo ang mga pribilehiyong pang-administrator.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />I-click ang <ph name="BEGIN_BOLD" />Magsimula<ph name="END_BOLD" />, pagkatapos ay hanapin at piliin ang <ph name="BEGIN_BOLD" />"Tingnan ang mga lokal na serbisyo"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Piliin ang <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Sa ilalim ng <ph name="BEGIN_BOLD" />Uri ng startup<ph name="END_BOLD" />, piliin ang <ph name="BEGIN_BOLD" />Naka-disable<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Sa ilalim ng <ph name="BEGIN_BOLD" />Status ng serbisyo<ph name="END_BOLD" />, i-click ang <ph name="BEGIN_BOLD" />Ihinto<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />I-click ang <ph name="BEGIN_BOLD" />Ilapat<ph name="END_BOLD" />, pagkatapos ay i-click ang <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Bisitahin ang <ph name="BEGIN_LEARN_MORE_LINK" />help center ng Chrome<ph name="END_LEARN_MORE_LINK" /> upang alamin kung paano permanenteng alisin ang software sa iyong computer
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Hindi pribado ang iyong koneksyon sa site na ito. Upang lumabas sa VR mode anumang oras, alisin ang headset at pindutin ang bumalik.</translation>
<translation id="5299298092464848405">Error sa pag-parse ng patakaran</translation>
<translation id="5308689395849655368">Hindi pinagana ang pag-uulat ng pag-crash.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Error sa pagpapatunay ng schema sa "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Hindi makita ang <ph name="HOST_NAME" /> page na ito</translation>
<translation id="5455374756549232013">Maling timestamp ng patakaran</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> ng <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Di-wasto</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> pa}one{<ph name="CONTACT_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> pa}other{<ph name="CONTACT_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> pa}}</translation>
<translation id="5470861586879999274">&amp;Gawing muli ang pag-e-edit</translation>
+<translation id="5481076368049295676">Maaaring subukan ng content na ito na mag-install ng mapanganib na software sa iyong device na magnanakaw o magde-delete ng impormasyon mo. <ph name="BEGIN_LINK" />Ipakita pa rin<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Magdagdag ng wastong address</translation>
<translation id="5492298309214877701">Ang URL ng site na ito sa intranet ng kumpanya, organisasyon o paaralan ay kapareho ng URL ng isang external na website.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Hindi mahanap ang hiniling na artikulo.</translation>
<translation id="5544037170328430102">Isinasaad ng isang naka-embed na page sa <ph name="SITE" /> na:</translation>
<translation id="5556459405103347317">I-reload</translation>
+<translation id="5560088892362098740">Petsa ng Pag-expire</translation>
<translation id="5565735124758917034">Aktibo</translation>
<translation id="5571083550517324815">Hindi maaaring mag-pick up mula sa address na ito. Pumili ng ibang address.</translation>
<translation id="5572851009514199876">Magsimula at mag-sign in sa Chrome upang masuri ng Chrome kung pinapayagan kang i-access ang site na ito.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Nabigong i-load ang mga setting ng patakaran</translation>
<translation id="5631439013527180824">Di-wastong token sa pamamahala ng device</translation>
<translation id="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="5646376287012673985">Lokasyon</translation>
<translation id="5659593005791499971">Email</translation>
<translation id="5669703222995421982">Makakuha ng naka-personalize na content</translation>
<translation id="5675650730144413517">Hindi gumagana ang page na ito</translation>
<translation id="5710435578057952990">Ang pagkilala ng website na ito ay hindi natukoy.</translation>
-<translation id="5713016350996637505">Na-block ang mapanlinlang na content</translation>
+<translation id="5719499550583120431">Tinatanggap ang mga prepaid card.</translation>
<translation id="5720705177508910913">Kasalukuyang user</translation>
<translation id="5732392974455271431">Maaari itong alisin sa pagkaka-block ng iyong mga magulang para sa iyo</translation>
<translation id="5763042198335101085">Maglagay ng wastong email address</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Gusto mo bang ilagay ang iyong <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Naka-encrypt ang iyong koneksyon sa <ph name="DOMAIN" /> gamit ang isang hindi na ginagamit na cipher suite.</translation>
<translation id="5813119285467412249">&amp;Gawing Muli ang Pagdagdag</translation>
-<translation id="5814352347845180253">Maaari kang mawalan ng access sa premium na content mula sa <ph name="SITE" /> at ilan pang ibang site.</translation>
<translation id="5838278095973806738">Hindi ka dapat maglagay ng anumang sensitibong impormasyon sa site na ito (halimbawa, mga password o credit card), dahil maaari itong nakawin ng mga umaatake.</translation>
<translation id="5869405914158311789">Hindi makakonekta sa site na ito</translation>
<translation id="5869522115854928033">Mga naka-save na password</translation>
<translation id="5872918882028971132">Mga Suhestyon ng Magulang</translation>
+<translation id="5893752035575986141">Tinatanggap ang mga credit card.</translation>
<translation id="5901630391730855834">Dilaw</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="5926846154125914413">Maaari kang mawalan ng access sa premium na content mula sa ilang site.</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="5967867314010545767">Alisin sa history</translation>
<translation id="5975083100439434680">Mag-zoom out</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Isara</translation>
<translation id="6042308850641462728">Higit pa</translation>
<translation id="6047233362582046994">Kung nauunawaan mo ang mga panganib sa iyong seguridad, maaari mong <ph name="BEGIN_LINK" />bisitahin ang site na ito<ph name="END_LINK" /> bago maalis ang mga mapaminsalang app.</translation>
+<translation id="6047927260846328439">Maaaring subukan ng content na ito na linlangin kang mag-install ng software o maghayag ng personal na impormasyon. <ph name="BEGIN_LINK" />Ipakita pa rin<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Hindi mo maaaring bisitahin ang <ph name="SITE" /> sa ngayon dahil gumagamit ng pag-pin ng certificate ang website. 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="6060685159320643512">Mag-ingat, maaaring makahamak ang mga eksperimentong ito</translation>
<translation id="6080696365213338172">Nag-access ka ng nilalaman gamit ang isang certificate na ibinigay ng administrator. Maaaring harangin ng iyong administrator ang data na ibibigay mo sa <ph name="DOMAIN" />.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Matuto nang higit pa</translation>
<translation id="6169916984152623906">Makakapag-browse ka na ngayon nang pribado, at hindi makikita ng ibang taong gumagamit sa device na ito ang iyong aktibidad. Gayunpaman, mase-save ang mga download at bookmark.</translation>
<translation id="6177128806592000436">Hindi ligtas ang iyong koneksyon sa site na ito</translation>
-<translation id="6184817833369986695">(cohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Suriin ang iyong koneksyon sa Internet</translation>
<translation id="6218753634732582820">Gusto mo bang alisin ang address sa Chromium?</translation>
<translation id="6221345481584921695">Kamakailan lang, ang Google Safe Browsing ay <ph name="BEGIN_LINK" />nakakita ng malware<ph name="END_LINK" /> sa <ph name="SITE" />. Paminsan-minsan, nagkakaroon ng malware ang mga website na karaniwang ligtas. Ang nakakahamak na content ay galing sa <ph name="SUBRESOURCE_HOST" />, isang kilalang nagkakalat ng malware.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Interesado sa mga astig at bagong tampok sa Chrome? Subukan ang aming beta channel sa chrome.com/beta.</translation>
-<translation id="6389758589412724634">Naubusan ng memory ang Chromium habang sinusubukang ipakita ang webpage na ito.</translation>
+<translation id="6397451950548600259">Pinipigilan ng software sa iyong computer na makakonekta nang ligtas ang Chrome sa web</translation>
<translation id="6404511346730675251">I-edit ang bookmark</translation>
<translation id="6410264514553301377">Ilagay ang petsa ng pag-expire at CVC para sa <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Tinanong mo ang iyong magulang kung maaari mong bisitahin ang site na ito</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Mga patakaran sa device</translation>
<translation id="6477321094435799029">May natukoy na kakaibang code ang Chrome sa page na ito at na-block ito upang protektahan ang iyong personal na impormasyon (halimbawa, mga password, numero ng telepono at credit card).</translation>
<translation id="6489534406876378309">Simulang mag-upload ng mga pag-crash</translation>
+<translation id="6507833130742554667">Tinatanggap ang mga credit at debit card.</translation>
<translation id="6508722015517270189">I-restart ang Chrome</translation>
<translation id="6529602333819889595">&amp;Gawing Muli ang Pagtanggal</translation>
<translation id="6534179046333460208">Mga suhestyon sa Pisikal na Web</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Maaaring subukan ng content na ito na mag-install ng mapanganib na software sa iyong device na magnanakaw o magde-delete ng impormasyon mo. <ph name="BEGIN_LINK" />Ipakita pa rin<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Mga pagpipilian sa pag-encrypt</translation>
<translation id="662080504995468778">Manatili</translation>
<translation id="6626291197371920147">Magdagdag ng wastong card number</translation>
<translation id="6628463337424475685">Paghahanap ng <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">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 Mac 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="6644283850729428850">Hindi na ginagamit ang patakarang ito.</translation>
+<translation id="6657585470893396449">Password</translation>
<translation id="6671697161687535275">Gusto mo bang alisin ang form para sa suhestyon sa Chromium?</translation>
<translation id="6685834062052613830">Mag-sign out at kumpletuhin ang setup</translation>
<translation id="6710213216561001401">Nakaraan</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Distrito</translation>
<translation id="6973656660372572881">Tinukoy ang parehong mga hindi nababagong proxy server at isang .pac script URL.</translation>
<translation id="6989763994942163495">Ipakita ang mga advanced na setting...</translation>
-<translation id="7000990526846637657">Walang nakitang mga entry sa history</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Ang iyong Google Account ay maaaring may iba pang mga form ng history ng pagba-browse sa <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Mga Password</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Hindi maaaring magpadala sa address na ito. Pumili ng ibang address.</translation>
<translation id="7138472120740807366">Pamamaraan ng paghahatid</translation>
<translation id="7139724024395191329">Emirate</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> pa}one{<ph name="PAYMENT_METHOD_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> pa}other{<ph name="PAYMENT_METHOD_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> pa}}</translation>
<translation id="7155487117670177674">Hindi ligtas ang pagbabayad</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> pa}one{<ph name="SHIPPING_OPTION_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> pa}other{<ph name="SHIPPING_OPTION_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> pa}}</translation>
<translation id="7179921470347911571">Ilunsad Muli Ngayon</translation>
<translation id="7180611975245234373">I-refresh</translation>
<translation id="7182878459783632708">Walang nakatakdang mga patakaran</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">I-download</translation>
<translation id="7518003948725431193">Walang webpage na nahanap para sa web address:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Hindi pribado ang iyong koneksyon sa site na ito</translation>
<translation id="7535087603100972091">Value</translation>
<translation id="7537536606612762813">Kinakailangan</translation>
<translation id="7542403920425041731">Kapag nagkumpirma ka, ibabahagi ang mga detalye ng iyong card sa site na ito.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Subukan ang mga sumusunod na tip:</translation>
<translation id="7554791636758816595">Bagong Tab</translation>
<translation id="7567204685887185387">Hindi mapatunayan ng server na ito na ito ay <ph name="DOMAIN" />; maaaring mapanlokong ibinigay ang certificate ng seguridad nito. Maaaring dulot ito ng maling configuration o isang umaatake na hinahadlangan ang iyong koneksyon.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> pa}one{<ph name="CONTACT_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> pa}other{<ph name="CONTACT_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> pa}}</translation>
<translation id="7568593326407688803">Ang pahinang ito ay nasa<ph name="ORIGINAL_LANGUAGE" />Gusto mong isalin ito?</translation>
<translation id="7569952961197462199">Alisin ang credit card sa Chrome?</translation>
<translation id="7569983096843329377">Itim</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificate</translation>
<translation id="7716147886133743102">Na-block ng iyong administrator</translation>
<translation id="7716424297397655342">Hindi maaaring i-load ang site na ito mula sa cache</translation>
+<translation id="774634243536837715">Na-block ang mapanganib na content.</translation>
<translation id="7752995774971033316">Hindi pinamamahalaan</translation>
<translation id="7755287808199759310">Maaari itong alisin sa pagkaka-block ng iyong magulang para sa iyo</translation>
<translation id="7758069387465995638">Maaaring na-block ng firewall o antivirus software ang koneksyon.</translation>
+<translation id="7759163816903619567">Display domain:</translation>
<translation id="7761701407923456692">Hindi tumutugma sa URL ang certificate ng server.</translation>
<translation id="7763386264682878361">Pang-parse ng Manifest ng Pagbabayad</translation>
<translation id="7764225426217299476">Magdagdag ng address</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Nakakita ng <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> para sa '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">Gayunpaman, hindi ka invisible. Kahit mag-incognito ka, hindi matatago ang iyong pagba-browse mula sa iyong employer, sa iyong internet service provider o sa mga website na binibisita mo.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Tinatanggap ang mga debit at prepaid card.</translation>
<translation id="7887683347370398519">Tingnan ang iyong CVC at subukang muli</translation>
<translation id="79338296614623784">Maglagay ng wastong numero ng telepono</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Magpadala ng Feedback</translation>
<translation id="8041940743680923270">Gamitin ang pangkalahatang default (Tanungin)</translation>
<translation id="8088680233425245692">Hindi natingnan ang artikulo.</translation>
-<translation id="8089520772729574115">wala pang 1 MB</translation>
<translation id="8091372947890762290">Nakabinbin sa server ang pag-activate</translation>
<translation id="8118489163946903409">Paraan ng pagbabayad</translation>
<translation id="8131740175452115882">Kumpirmahin</translation>
@@ -847,13 +875,16 @@
<translation id="8194797478851900357">&amp;I-undo ang Paglilipat</translation>
<translation id="8201077131113104583">Di-wastong URL ng update para sa extension na may ID na "<ph name="EXTENSION_ID" />."</translation>
<translation id="8202097416529803614">Buod ng order</translation>
+<translation id="8205463626947051446">Malamang na magpakita ang site ng mga nakakasagabal na ad</translation>
<translation id="8218327578424803826">Itinakdang Lokasyon:</translation>
<translation id="8225771182978767009">Pinili ng taong nag-set up ng computer na ito na i-block ang site na ito.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Buksan ang page sa bagong Incognito tab</translation>
<translation id="8241707690549784388">Ang pahina na hinahanap mo para sa paggamit ng impormasyon na ipinasok mo. Ang pagbalik sa pahinang iyon maaaring magsanhi ng anumang aksyon na akalo mo ay naulit. Nais mo bang ipagpatuloy?</translation>
+<translation id="8241712895048303527">I-block sa site na ito</translation>
<translation id="8249320324621329438">Huling kinuha:</translation>
<translation id="8253091569723639551">Kinakailangan ang billing address</translation>
-<translation id="8261506727792406068">Burahin</translation>
+<translation id="8261506727792406068">I-delete</translation>
<translation id="8289355894181816810">Makipag-ugnay sa administrator ng iyong network kung hindi ka sigurado kung ano ang ibig sabihin nito.</translation>
<translation id="8293206222192510085">Magdagdag ng Bookmark</translation>
<translation id="8294431847097064396">Pinagmulan</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Nabigo ang translation dahil sa problema sa koneksyon sa network.</translation>
<translation id="8332188693563227489">Tinanggihan ang access sa <ph name="HOST_NAME" /></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="8344669043927012510">Buksan ang page sa Incognito mode (⇧⌘N)</translation>
<translation id="8349305172487531364">Bookmarks bar</translation>
<translation id="8363502534493474904">I-off ang airplane mode</translation>
<translation id="8364627913115013041">Hindi nakatakda.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Na-upload ang ulat ng pag-crash noong <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Mga Pag-crash (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Dapat mong ilagay ang parehong passphrase nang dalawang beses.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Mga Setting</translation>
<translation id="8433057134996913067">Masa-sign out ka sa karamihan ng mga website.</translation>
<translation id="8437238597147034694">&amp;I-undo ang paglilipat</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Upang gumamit ng mga card mula sa iyong Google Account, mag-sign in sa Chrome</translation>
<translation id="8488350697529856933">Nalalapat sa</translation>
<translation id="8498891568109133222">Masyadong matagal bago nakatugon ang <ph name="HOST_NAME" />.</translation>
-<translation id="8532105204136943229">Taon ng Pag-expire</translation>
+<translation id="8503813439785031346">Username</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="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="8620436878122366504">Hindi pa ito inaaprubahan ng iyong mga magulang</translation>
<translation id="8647750283161643317">I-reset ang lahat sa default</translation>
+<translation id="8660471606262461360">Mula sa Google Payments</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="8725066075913043281">Muling subukan</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Mag-zoom in</translation>
<translation id="8931333241327730545">Gusto mo bang i-save ang card na ito sa iyong Google Account?</translation>
<translation id="8932102934695377596">Nahuhuli ang iyong orasan</translation>
+<translation id="8938939909778640821">Mga tinatanggap na credit at prepaid card</translation>
<translation id="8971063699422889582">Nag-expire na ang certificate ng server.</translation>
<translation id="8986494364107987395">Awtomatikong ipadala ang mga istatistika ng paggamit at mga ulat ng pag-crash sa Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Palaging i-block sa site na ito</translation>
<translation id="9170848237812810038">&amp;I-undo</translation>
<translation id="917450738466192189">Di-wastong certificate ng server.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> pa}one{<ph name="SHIPPING_OPTION_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> pa}other{<ph name="SHIPPING_OPTION_PREVIEW" /> at <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> pa}}</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>
<translation id="9207861905230894330">Hindi naidagdag ang artikulo.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">CLEAR FORM</translation>
<translation id="939736085109172342">Bagong folder</translation>
-<translation id="941721044073577244">Mukhang wala kang pahintulot na bisitahin ang site na ito</translation>
<translation id="969892804517981540">Official Build</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Wala}=1{1 item}one{# item}other{# na item}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Bumuo ang Developer</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_fr.xtb b/chromium/components/strings/components_strings_fr.xtb
index a7dbc20c6ae..4abea50140e 100644
--- a/chromium/components/strings/components_strings_fr.xtb
+++ b/chromium/components/strings/components_strings_fr.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Fermez les autres applications</translation>
<translation id="1055184225775184556">&amp;Annuler l'ajout</translation>
<translation id="10614374240317010">Jamais enregistrés</translation>
+<translation id="1066396345355680611">Vous risquez de ne plus avoir accès au contenu protégé de <ph name="SITE" /> et de certains autres sites.</translation>
<translation id="106701514854093668">Favoris sur l'ordinateur</translation>
<translation id="1074497978438210769">Non sécurisé</translation>
<translation id="1080116354587839789">Ajuster à la largeur</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Liste de lecture</translation>
<translation id="1264126396475825575">Rapport d'erreur enregistré le <ph name="CRASH_TIME" /> (pas encore importé ou ignoré)</translation>
<translation id="1281526147609854549">Émis par <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Contenu dangereux bloqué</translation>
<translation id="1285320974508926690">Ne jamais traduire ce site</translation>
<translation id="129553762522093515">Récemment fermés</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Essayez de supprimer les cookies.<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domaine d'enregistrement :</translation>
<translation id="1340482604681802745">Adresse d'enlèvement</translation>
-<translation id="1344211575059133124">Tu n'es pas autorisé à consulter ce site</translation>
<translation id="1344588688991793829">Paramètres de saisie automatique de Chromium…</translation>
<translation id="1348198688976932919">Ce site contient des applications dangereuses</translation>
<translation id="1374468813861204354">suggestions</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">L'identité de <ph name="ORGANIZATION" /> situé à <ph name="LOCALITY" /> a été vérifiée par <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Oui</translation>
<translation id="1430915738399379752">Imprimer</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> autre}one{<ph name="PAYMENT_METHOD_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> autre}other{<ph name="PAYMENT_METHOD_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> autres}}</translation>
<translation id="1506687042165942984">Afficher une copie enregistrée (non actualisée) de cette page</translation>
<translation id="1517433312004943670">Veuillez saisir le numéro de téléphone.</translation>
+<translation id="1517500485252541695">Cartes de crédit et de débit acceptées</translation>
<translation id="1519264250979466059">Date de création</translation>
+<translation id="1527263332363067270">En attente de connexion…</translation>
<translation id="153384715582417236">C'est tout !</translation>
<translation id="1549470594296187301">Vous devez activer JavaScript pour utiliser cette fonctionnalité.</translation>
<translation id="1555130319947370107">Bleu</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Télécharger la page plus tard</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Veuillez compléter ce champ.</translation>
<translation id="187918866476621466">Ouvrir les pages de démarrage</translation>
<translation id="1883255238294161206">Réduire la liste</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> autre}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> autre}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> autres}}</translation>
<translation id="1898423065542865115">Filtrage</translation>
+<translation id="1916770123977586577">Pour appliquer vos paramètres mis à jour à ce site, actualisez la page</translation>
+<translation id="1919345977826869612">Annonces</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Aucun}=1{1 site}one{# site}other{# sites}}</translation>
<translation id="194030505837763158">Accédez à <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Cartes prépayées acceptées</translation>
<translation id="1962204205936693436">Favoris du domaine <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Erreur de sérialisation.</translation>
<translation id="1974060860693918893">Paramètres avancés</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Erreur HTTP.</translation>
<translation id="2270484714375784793">N° de téléphone</translation>
<translation id="2282872951544483773">Tests non disponibles.</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> article}one{<ph name="ITEM_COUNT" /> article}other{<ph name="ITEM_COUNT" /> articles}}</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>
<translation id="2337852623177822836">Paramètre contrôlé par votre administrateur</translation>
<translation id="2354001756790975382">Autres favoris</translation>
<translation id="2354430244986887761">La navigation sécurisée de Google a récemment <ph name="BEGIN_LINK" />détecté des applications malveillantes<ph name="END_LINK" /> sur le site <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continuer</translation>
<translation id="2365563543831475020">Le rapport d'erreur enregistré le <ph name="CRASH_TIME" /> n'a pas été importé.</translation>
<translation id="2367567093518048410">Niveau</translation>
-<translation id="237718015863234333">Aucune alternative disponible pour l'interface utilisateur</translation>
<translation id="2384307209577226199">Valeur par défaut définie par l'entreprise</translation>
<translation id="2386255080630008482">Le certificat du serveur a été révoqué.</translation>
<translation id="2392959068659972793">Afficher les règles non paramétrées</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Si cette case est cochée, Chromium enregistre une copie de votre carte sur cet appareil pour vous permettre de remplir plus rapidement les formulaires.</translation>
<translation id="2498091847651709837">Lire une nouvelle carte</translation>
<translation id="2501278716633472235">Retour</translation>
+<translation id="2503184589641749290">Cartes de débit et cartes prépayées acceptées</translation>
<translation id="2515629240566999685">Vérifier le signal dans votre zone</translation>
-<translation id="2516305470678292029">Alternatives pour l'interface utilisateur</translation>
<translation id="2539524384386349900">Détecter</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> a envoyé une réponse incorrecte.</translation>
<translation id="2556876185419854533">&amp;Annuler la modification</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Mode d'expédition</translation>
<translation id="277499241957683684">Enregistrement de l'appareil manquant.</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>
<translation id="2799020568854403057">Ce site contient des applications malveillantes</translation>
<translation id="2803306138276472711">La fonctionnalité de navigation sécurisée Google a récemment permis de <ph name="BEGIN_LINK" />détecter des logiciels malveillants<ph name="END_LINK" /> sur le site <ph name="SITE" />. Un site Web qui est normalement sans danger peut parfois être infecté par des logiciels malveillants.</translation>
<translation id="2824775600643448204">Barre d'adresse et de recherche</translation>
<translation id="2826760142808435982">La connexion est chiffrée et authentifiée avec la clé <ph name="CIPHER" />. La méthode d'échange de clés utilisé est <ph name="KX" />.</translation>
<translation id="2835170189407361413">Effacer le formulaire</translation>
+<translation id="2851634818064021665">Tu n'es pas autorisé à consulter ce site</translation>
<translation id="2856444702002559011">Des individus malveillants tentent peut-être de subtiliser vos informations personnelles sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (mots de passe, messages ou numéros de carte de crédit, par exemple). <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Ne pas actualiser</translation>
-<translation id="2900469785430194048">Google Chrome n'avait pas suffisamment de mémoire pour afficher cette page Web.</translation>
<translation id="2909946352844186028">Un changement de réseau a été détecté.</translation>
<translation id="2916038427272391327">Fermez les autres programmes</translation>
<translation id="2922350208395188000">Impossible de vérifier le certificat du serveur.</translation>
<translation id="2928905813689894207">Adresse de facturation</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> autre}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> autre}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> autres}}</translation>
<translation id="2941952326391522266">Impossible de vérifier sur le serveur qu'il s'agit bien du domaine <ph name="DOMAIN" />, car son certificat de sécurité provient du domaine <ph name="DOMAIN2" />. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Si cette option est activée, Chromium enregistre une copie de votre carte sur cet appareil pour vous permettre de remplir plus rapidement les formulaires.</translation>
<translation id="2985398929374701810">Saisissez une adresse valide</translation>
<translation id="2986368408720340940">Mode d'enlèvement non disponible. Choisissez-en un autre.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Les pages consultées dans les onglets de navigation privée ne sont pas enregistrées dans l'historique de votre navigateur, dans les cookies ni dans l'historique des recherches une fois que vous avez fermé tous les onglets de navigation privée. Les fichiers téléchargés et les favoris ajoutés sont conservés.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Île</translation>
-<translation id="317583078218509884">Les nouveaux paramètres des autorisations de site seront appliqués lorsque vous actualiserez la page.</translation>
<translation id="3176929007561373547">Vérifiez vos paramètres de proxy ou contactez votre administrateur réseau pour
vous assurer que le serveur proxy fonctionne. Si vous
ne pensez pas devoir utiliser de serveur proxy, procédez comme suit :
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Ouvrez la page en mode navigation privée</translation>
<translation id="320323717674993345">Annuler le paiement</translation>
<translation id="3207960819495026254">Favori</translation>
<translation id="3225919329040284222">Le serveur dispose d'un certificat qui ne répond pas aux exigences intégrées. Celles-ci sont incluses dans certains sites Web très sécurisés afin de vous protéger.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Annuler la réorganisation</translation>
<translation id="3282497668470633863">Ajouter le nom sur la carte</translation>
<translation id="3286538390144397061">Redémarrer maintenant</translation>
+<translation id="3287510313208355388">Télécharger une fois en ligne</translation>
<translation id="3303855915957856445">Aucun résultat de recherche n'a été trouvé.</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Intervalle de récupération :</translation>
<translation id="3462200631372590220">Masquer les paramètres avancés</translation>
<translation id="3467763166455606212">Nom du titulaire de la carte obligatoire</translation>
-<translation id="3478058380795961209">Mois d'expiration</translation>
<translation id="3479539252931486093">S'agit-il d'une erreur inattendue ? <ph name="BEGIN_LINK" />Signalez-nous ce problème.<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Pas maintenant</translation>
<translation id="3498215018399854026">Impossible de joindre votre parent pour le moment. Veuillez réessayer.</translation>
@@ -343,6 +345,7 @@
&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;Paramètres&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Les cartes de crédit et les cartes prépayées sont acceptées.</translation>
<translation id="3582930987043644930">Ajouter un nom</translation>
<translation id="3583757800736429874">&amp;Rétablir le déplacement</translation>
<translation id="3586931643579894722">Masquer les détails</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Si ce message s'affiche régulièrement, essayez ces <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Révision</translation>
<translation id="3678029195006412963">Impossible de signer la demande</translation>
+<translation id="3678529606614285348">Ouvrir la page dans une nouvelle fenêtre de navigation privée (Ctrl+Maj+N)</translation>
<translation id="3679803492151881375">Rapport d'erreur enregistré le <ph name="CRASH_TIME" /> et importé le <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informations relatives au certificat</translation>
<translation id="3690164694835360974">Connexion non sécurisée</translation>
-<translation id="3693415264595406141">Mot de passe :</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Chargement en cours...</translation>
<translation id="3712624925041724820">Licences épuisées.</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Des individus malveillants à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pourraient vous inciter à effectuer des opérations dangereuses, telles que l'installation d'un logiciel ou la divulgation d'informations personnelles (mots de passe, numéros de téléphone 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="375403751935624634">Échec de la traduction en raison d'une erreur de serveur</translation>
<translation id="3759461132968374835">Aucune erreur n'a été signalée récemment. Les erreurs n'apparaissent ici que lorsque l'envoi de rapports d'erreur est activé.</translation>
+<translation id="3765032636089507299">La page de navigation sécurisée est en cours de création.</translation>
<translation id="3778403066972421603">Voulez-vous enregistrer cette carte dans votre compte Google et sur cet appareil ?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Expire en <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Votre demande d'accès à ce site a bien été envoyée à <ph name="NAME" /></translation>
<translation id="3890664840433101773">Ajouter une adresse e-mail</translation>
<translation id="3901925938762663762">La carte a expiré.</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID du rapport d'erreur importé : <ph name="CRASH_ID" /> (ID de plantage local : <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Impossible de vérifier que ce serveur est bien <ph name="DOMAIN" />, car son certificat de sécurité ne contient pas l'extension "Subject Alternative Names" (Autres noms de l'objet). Cela peut être dû à une mauvaise configuration ou à l'interception de votre connexion par un pirate informatique.</translation>
+<translation id="3949601375789751990">Votre historique de navigation s'affiche ici</translation>
<translation id="3963721102035795474">Mode lecture</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Aucun}=1{De 1 site }one{De # site }other{De # sites }}</translation>
<translation id="397105322502079400">Calcul en cours…</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Paramètres du site</translation>
<translation id="4169947484918424451">Voulez-vous que cette carte soit enregistrée dans Chromium ?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Des individus malveillants à l'œuvre sur ce site pourraient tenter d'installer des applications dangereuses sur votre ordinateur afin de récupérer ou de supprimer certaines informations : photos, mots de passe, messages, numéros de carte de paiement, etc.</translation>
<translation id="4372948949327679948">Valeur attendue : <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Vous avez tenté d'accéder à <ph name="DOMAIN" />, mais le certificat présenté par le serveur a été révoqué par son émetteur. Cela signifie que le certificat présenté par le serveur ne doit pas être approuvé. Il est donc possible que vous communiquiez avec un pirate informatique.</translation>
-<translation id="4381091992796011497">Nom d'utilisateur :</translation>
<translation id="4394049700291259645">Désactiver</translation>
<translation id="4406896451731180161">résultats de recherche</translation>
<translation id="4424024547088906515">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 Chrome. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> n'a pas accepté votre certificat de connexion, ou vous ne l'avez pas fourni.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Supprimer l'adresse de Chrome ?</translation>
<translation id="4592951414987517459">Votre connexion à <ph name="DOMAIN" /> est chiffrée à l'aide d'une méthode de chiffrement récente.</translation>
<translation id="4594403342090139922">&amp;Annuler la suppression</translation>
+<translation id="4611292653554630842">Connexion</translation>
<translation id="4619615317237390068">Onglets d'autres appareils</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Impossible de vérifier sur le serveur qu'il s'agit bien du domaine <ph name="DOMAIN" />, car son certificat de sécurité contient des erreurs. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
<translation id="4690462567478992370">Arrêter d'utiliser un certificat non valide</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Votre connexion a été interrompue</translation>
+<translation id="471880041731876836">Vous n'êtes pas autorisé à consulter ce site</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Exécuter les diagnostics réseau de Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Actualiser les règles</translation>
<translation id="4728558894243024398">Plate-forme</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Ce contenu peut vous inciter à installer un logiciel ou vous soutirer des informations personnelles. <ph name="BEGIN_LINK" />Je souhaite y accéder malgré tout.<ph name="END_LINK" /></translation>
<translation id="4880827082731008257">Rechercher dans l'historique</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Authentification requise</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{et 1 autre page Web}one{et # autre page Web}other{et # autres pages Web}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Cette page rédigée dans une langue non identifiée a été traduite en <ph name="LANGUAGE_LANGUAGE" />.</translation>
<translation id="4923459931733593730">Paiement</translation>
<translation id="4926049483395192435">Doit être spécifié.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Actions</translation>
<translation id="4958444002117714549">Développer la liste</translation>
<translation id="4974590756084640048">Réactiver les avertissements</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Mot de passe incorrect</translation>
<translation id="5056549851600133418">Articles pour vous</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Vérifier l'adresse du proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Vous risquez de ne plus avoir accès au contenu protégé de certains sites.</translation>
<translation id="5087286274860437796">Le certificat actuel du serveur n'est pas valide.</translation>
<translation id="5087580092889165836">Ajouter une carte</translation>
<translation id="5089810972385038852">État</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Province</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5141240743006678641">Chiffrer les mots de passe synchronisés avec vos informations de connexion Google</translation>
-<translation id="514421653919133810">Ouvrez la page en mode navigation privée (Ctrl+Maj+N)</translation>
<translation id="5145883236150621069">Code d'erreur présent dans la réponse de la règle.</translation>
+<translation id="5159010409087891077">Ouvrir la page dans une nouvelle fenêtre de navigation privée (⇧⌘N)</translation>
<translation id="5171045022955879922">Rechercher ou saisir une URL</translation>
<translation id="5172758083709347301">Ordinateur</translation>
<translation id="5179510805599951267">Cette page n'est pas rédigée en <ph name="ORIGINAL_LANGUAGE" /> ? Signaler l'erreur</translation>
-<translation id="5181140330217080051">Téléchargement</translation>
<translation id="5190835502935405962">Barre de favoris</translation>
<translation id="5199729219167945352">Prototypes</translation>
<translation id="5205222826937269299">Veuillez saisir un nom</translation>
<translation id="5222812217790122047">Veuillez saisir une adresse e-mail</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Vous utilisez Chrome au travail ? Les entreprises peuvent gérer les paramètres Chrome de leurs employés. En savoir plus</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Suivez ces étapes pour désactiver temporairement le logiciel afin d'accéder au Web. Vous devez disposer des droits d'administrateur.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Cliquez sur <ph name="BEGIN_BOLD" />Démarrer<ph name="END_BOLD" />, puis recherchez et sélectionnez l'option <ph name="BEGIN_BOLD" />Afficher les services locaux<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Sélectionnez <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Sous <ph name="BEGIN_BOLD" />Type de démarrage<ph name="END_BOLD" />, sélectionnez <ph name="BEGIN_BOLD" />Désactivé<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Sous <ph name="BEGIN_BOLD" />État du service<ph name="END_BOLD" />, cliquez sur <ph name="BEGIN_BOLD" />Arrêter<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Cliquez sur <ph name="BEGIN_BOLD" />Appliquer<ph name="END_BOLD" />, puis sur <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Accédez au <ph name="BEGIN_LEARN_MORE_LINK" />Centre d'aide Chrome<ph name="END_LEARN_MORE_LINK" /> pour découvrir comment supprimer définitivement le logiciel de votre ordinateur.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Votre connexion à ce site n'est pas privée. Pour quitter le mode Réalité virtuelle à tout moment, retirez le casque, puis appuyez sur Retour.</translation>
<translation id="5299298092464848405">Erreur d'analyse de la règle.</translation>
<translation id="5308689395849655368">L'envoi de rapports d'erreur est désactivé.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Erreur de validation du schéma au niveau de "<ph name="ERROR_PATH" />" : <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Cette page du site <ph name="HOST_NAME" /> est introuvable</translation>
<translation id="5455374756549232013">Horodatage de la règle incorrect.</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> sur <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Non valide</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> autre}one{<ph name="CONTACT_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> autre}other{<ph name="CONTACT_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> autres}}</translation>
<translation id="5470861586879999274">&amp;Rétablir la modification</translation>
+<translation id="5481076368049295676">Ce contenu peut essayer d'installer un logiciel dangereux sur votre appareil pour dérober ou supprimer vos informations. <ph name="BEGIN_LINK" />Je souhaite y accéder malgré tout.<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Ajouter une adresse valide</translation>
<translation id="5492298309214877701">Ce site situé sur l'intranet de l'entreprise, de l'organisation ou de l'établissement scolaire possède la même URL qu'un site Web externe.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Impossible de trouver l'article demandé.</translation>
<translation id="5544037170328430102">Une page intégrée à l'adresse <ph name="SITE" /> indique :</translation>
<translation id="5556459405103347317">Actualiser</translation>
+<translation id="5560088892362098740">Date d'expiration</translation>
<translation id="5565735124758917034">Actif</translation>
<translation id="5571083550517324815">Enlèvement impossible à cette adresse. Sélectionnez-en une autre.</translation>
<translation id="5572851009514199876">Veuillez démarrer Chrome et vous connecter à votre compte pour que le navigateur puisse vérifier que vous êtes autorisé à accéder à ce site.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Échec du chargement des paramètres de la règle.</translation>
<translation id="5631439013527180824">Jeton de gestion de l'appareil non valide.</translation>
<translation id="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="5646376287012673985">Localisation</translation>
<translation id="5659593005791499971">E-mail</translation>
<translation id="5669703222995421982">Obtenir une recommandation de contenu personnalisé</translation>
<translation id="5675650730144413517">Cette page ne fonctionne pas</translation>
<translation id="5710435578057952990">L'identité de ce site Web n'a pas été vérifiée.</translation>
-<translation id="5713016350996637505">Contenu trompeur bloqué</translation>
+<translation id="5719499550583120431">Les cartes prépayées sont acceptées.</translation>
<translation id="5720705177508910913">Utilisateur actuel</translation>
<translation id="5732392974455271431">Tes parents peuvent te le débloquer</translation>
<translation id="5763042198335101085">Saisissez une adresse e-mail valide</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Souhaitez-vous indiquer les informations "<ph name="CARD_DETAIL" />" ?</translation>
<translation id="5810442152076338065">Votre connexion à <ph name="DOMAIN" /> est chiffrée à l'aide d'une méthode de chiffrement obsolète.</translation>
<translation id="5813119285467412249">&amp;Rétablir l'ajout</translation>
-<translation id="5814352347845180253">Vous risquez de ne plus avoir accès au contenu premium de <ph name="SITE" /> et de certains autres sites.</translation>
<translation id="5838278095973806738">Vous ne devriez pas saisir d'informations sensibles sur ce site (par exemple, vos mots de passe ou les informations de votre carte de paiement), car elles risquent d'être dérobées par des pirates informatiques.</translation>
<translation id="5869405914158311789">Ce site est inaccessible</translation>
<translation id="5869522115854928033">Mots de passe enregistrés</translation>
<translation id="5872918882028971132">Suggestions des parents</translation>
+<translation id="5893752035575986141">Les cartes de crédit sont acceptées.</translation>
<translation id="5901630391730855834">Jaune</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="5926846154125914413">Vous risquez de ne plus avoir accès au contenu premium de certains sites.</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="5967867314010545767">Supprimer de l'historique</translation>
<translation id="5975083100439434680">Zoom arrière</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Fermer</translation>
<translation id="6042308850641462728">Plus</translation>
<translation id="6047233362582046994">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 applications dangereuses aient été supprimées.</translation>
+<translation id="6047927260846328439">Ce contenu peut vous inciter à installer un logiciel ou vous soutirer des informations personnelles. <ph name="BEGIN_LINK" />Je souhaite y accéder malgré tout.<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Le site <ph name="SITE" /> est actuellement inaccessible, car il utilise l'épinglage des certificats. 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="6060685159320643512">Attention, ces fonctionnalités expérimentales peuvent mordre.</translation>
<translation id="6080696365213338172">Vous avez accédé à du contenu à l'aide d'un certificat fourni par l'administrateur. Les données que vous fournissez à <ph name="DOMAIN" /> peuvent être interceptées par votre administrateur.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">En savoir plus</translation>
<translation id="6169916984152623906">Vous pouvez désormais naviguer de façon privée. Les autres utilisateurs de cet appareil ne verront pas votre activité. Les téléchargements et les favoris seront cependant enregistrés.</translation>
<translation id="6177128806592000436">Votre connexion à ce site n'est pas sécurisée.</translation>
-<translation id="6184817833369986695">(cohorte : <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Vérifiez votre connexion Internet</translation>
<translation id="6218753634732582820">Supprimer l'adresse de Chromium ?</translation>
<translation id="6221345481584921695">La fonctionnalité de navigation sécurisée Google a récemment permis de <ph name="BEGIN_LINK" />détecter des logiciels malveillants<ph name="END_LINK" /> sur le site <ph name="SITE" />. Un site Web qui est normalement sans danger peut parfois être infecté par des logiciels malveillants. Le contenu en cause provient de l'hôte "<ph name="SUBRESOURCE_HOST" />", une source de logiciels malveillants connue.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Vous souhaitez bénéficier de nouvelles fonctionnalités Chrome passionnantes ? Essayez notre version bêta à l'adresse chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium n'avait pas suffisamment de mémoire pour afficher cette page Web.</translation>
+<translation id="6397451950548600259">Un logiciel installé sur votre ordinateur empêche Chrome de se connecter au Web de manière sécurisée</translation>
<translation id="6404511346730675251">Modifier le favori</translation>
<translation id="6410264514553301377">Saisissez la date d'expiration et le code CVC pour <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Une demande d'autorisation a été envoyée à tes parents pour la consultation de ce site</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Règles relatives aux appareils</translation>
<translation id="6477321094435799029">Chrome a détecté un code inhabituel sur cette page et a bloqué cette dernière pour protéger vos informations personnelles (mots de passe, numéros de téléphone et de cartes de paiement).</translation>
<translation id="6489534406876378309">Lancer l'importation des plantages</translation>
+<translation id="6507833130742554667">Les cartes de crédit et de débit sont acceptées.</translation>
<translation id="6508722015517270189">Relancez Chrome</translation>
<translation id="6529602333819889595">&amp;Rétablir la suppression</translation>
<translation id="6534179046333460208">Suggestions pour le Web physique</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Ce contenu peut essayer d'installer un logiciel dangereux sur votre appareil pour dérober ou supprimer vos informations. <ph name="BEGIN_LINK" />Je souhaite y accéder malgré tout.<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">Options de chiffrement</translation>
<translation id="662080504995468778">Rester</translation>
<translation id="6626291197371920147">Ajouter un numéro de carte valide</translation>
<translation id="6628463337424475685">Recherche <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">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 Mac 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="6644283850729428850">Cette règle est obsolète.</translation>
+<translation id="6657585470893396449">Mot de passe</translation>
<translation id="6671697161687535275">Supprimer la suggestion de saisie de formulaire de Chromium ?</translation>
<translation id="6685834062052613830">Déconnectez-vous et complétez la configuration.</translation>
<translation id="6710213216561001401">Précédent</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">District</translation>
<translation id="6973656660372572881">Les serveurs proxy déterminés et une URL de script .pac sont spécifiés tous les deux.</translation>
<translation id="6989763994942163495">Afficher les paramètres avancés…</translation>
-<translation id="7000990526846637657">Aucune entrée d'historique trouvée</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Votre compte Google conserve peut-être d'autres formes d'historique de navigation sur la page <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />.</translation>
<translation id="7029809446516969842">Mots de passe</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Impossible d'expédier à cette adresse. Sélectionnez-en une autre.</translation>
<translation id="7138472120740807366">Mode de livraison</translation>
<translation id="7139724024395191329">Émirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> autre}one{<ph name="PAYMENT_METHOD_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> autre}other{<ph name="PAYMENT_METHOD_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> autres}}</translation>
<translation id="7155487117670177674">Paiement non sécurisé</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> autre}one{<ph name="SHIPPING_OPTION_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> autre}other{<ph name="SHIPPING_OPTION_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> autres}}</translation>
<translation id="7179921470347911571">Relancer maintenant</translation>
<translation id="7180611975245234373">Actualiser</translation>
<translation id="7182878459783632708">Aucune règle n'est définie</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Télécharger</translation>
<translation id="7518003948725431193">Aucune page Web trouvée à l'adresse :<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Votre connexion à ce site n'est pas privée</translation>
<translation id="7535087603100972091">Valeur</translation>
<translation id="7537536606612762813">Obligatoire</translation>
<translation id="7542403920425041731">Une fois la validation terminée, les informations relatives à votre carte seront partagées avec ce site.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Essayez les astuces suivantes :</translation>
<translation id="7554791636758816595">Nouvel onglet</translation>
<translation id="7567204685887185387">Impossible de vérifier sur le serveur qu'il s'agit bien du domaine <ph name="DOMAIN" />. Il se peut que son certificat de sécurité ait été émis de manière frauduleuse. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> autre}one{<ph name="CONTACT_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> autre}other{<ph name="CONTACT_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> autres}}</translation>
<translation id="7568593326407688803">Cette page est en<ph name="ORIGINAL_LANGUAGE" />Voulez-vous la traduire ?</translation>
<translation id="7569952961197462199">Supprimer les données de carte de paiement de Chrome ?</translation>
<translation id="7569983096843329377">Noir</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificat</translation>
<translation id="7716147886133743102">Bloqué par votre administrateur</translation>
<translation id="7716424297397655342">Impossible de charger ce site à partir du cache</translation>
+<translation id="774634243536837715">Contenu dangereux bloqué.</translation>
<translation id="7752995774971033316">Non géré</translation>
<translation id="7755287808199759310">Ton parent peut te le débloquer</translation>
<translation id="7758069387465995638">Il est possible qu'un pare-feu ou un logiciel antivirus ait bloqué la connexion.</translation>
+<translation id="7759163816903619567">Domaine d'affichage :</translation>
<translation id="7761701407923456692">Le certificat du serveur ne correspond pas à l'URL.</translation>
<translation id="7763386264682878361">Analyseur du fichier manifeste du paiement</translation>
<translation id="7764225426217299476">Ajouter une adresse</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534"><ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> trouvé(s) pour "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">Cependant, cela ne vous rend pas invisible. Si vous passez en mode navigation privée, votre employeur, votre fournisseur d'accès à Internet ou les sites Web que vous consultez pourront toujours avoir accès à votre historique de navigation.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> : <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Les cartes de débit et les cartes prépayées sont acceptées.</translation>
<translation id="7887683347370398519">Veuillez vérifier votre code CVC et réessayer.</translation>
<translation id="79338296614623784">Saisissez un numéro de téléphone valide</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Envoyer</translation>
<translation id="8041940743680923270">Utiliser le paramètre global par défaut ("Demander")</translation>
<translation id="8088680233425245692">Échec de l'affichage de l'article.</translation>
-<translation id="8089520772729574115">moins de 1 Mo</translation>
<translation id="8091372947890762290">Activation en attente sur le serveur.</translation>
<translation id="8118489163946903409">Mode de paiement</translation>
<translation id="8131740175452115882">Confirmer</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Annuler le déplacement</translation>
<translation id="8201077131113104583">URL de mise à jour non valide pour l'extension associée à l'identifiant "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Récapitulatif de la commande</translation>
+<translation id="8205463626947051446">Le site a tendance à afficher des annonces intrusives</translation>
<translation id="8218327578424803826">Position attribuée :</translation>
<translation id="8225771182978767009">La personne qui a configuré cet ordinateur a choisi de bloquer ce site.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Ouvrir la page dans un nouvel onglet de navigation privée</translation>
<translation id="8241707690549784388">La page que vous recherchez a utilisé des informations que vous avez envoyées. Si vous revenez sur cette page, chaque action précédemment effectuée sera répétée. Souhaitez-vous continuer ?</translation>
+<translation id="8241712895048303527">Bloquer sur ce site</translation>
<translation id="8249320324621329438">Dernière récupération :</translation>
<translation id="8253091569723639551">Adresse de facturation obligatoire</translation>
<translation id="8261506727792406068">Supprimer</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Échec de la traduction en raison d'un problème de connexion réseau</translation>
<translation id="8332188693563227489">L'accès à <ph name="HOST_NAME" /> a été refusé</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="8344669043927012510">Ouvrez la page en mode navigation privée (⇧⌘N)</translation>
<translation id="8349305172487531364">Barre de favoris</translation>
<translation id="8363502534493474904">Désactiver le mode Avion</translation>
<translation id="8364627913115013041">Non définie</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Rapport d'erreur importé le <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Erreurs (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Vous devez saisir deux fois la même phrase secrète.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Paramètres</translation>
<translation id="8433057134996913067">Vous serez déconnecté de la plupart des sites.</translation>
<translation id="8437238597147034694">&amp;Annuler le déplacement</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Pour utiliser les cartes de votre compte Google, connectez-vous à Chrome.</translation>
<translation id="8488350697529856933">S'applique à</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> a mis trop de temps à répondre.</translation>
-<translation id="8532105204136943229">Année d'expiration</translation>
+<translation id="8503813439785031346">Nom d'utilisateur</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="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="8620436878122366504">Tes parents ne l'ont pas encore autorisé</translation>
<translation id="8647750283161643317">Rétablir tous les tests par défaut</translation>
+<translation id="8660471606262461360">Informations issues de Google Payments</translation>
<translation id="8703575177326907206">Votre connexion à <ph name="DOMAIN" /> n'est pas chiffrée.</translation>
<translation id="8718314106902482036">Paiement non finalisé</translation>
<translation id="8725066075913043281">Réessayer</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Zoom avant</translation>
<translation id="8931333241327730545">Voulez-vous enregistrer cette carte dans votre compte Google ?</translation>
<translation id="8932102934695377596">Votre horloge est en retard</translation>
+<translation id="8938939909778640821">Cartes de crédit et cartes prépayées acceptées</translation>
<translation id="8971063699422889582">Le certificat du serveur a expiré.</translation>
<translation id="8986494364107987395">Envoyer automatiquement les statistiques d'utilisation et les rapports d'erreur à Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Toujours bloquer sur ce site</translation>
<translation id="9170848237812810038">Ann&amp;uler</translation>
<translation id="917450738466192189">Le certificat du serveur n'est pas valide.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> autre}one{<ph name="SHIPPING_OPTION_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> autre}other{<ph name="SHIPPING_OPTION_PREVIEW" /> et <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> autres}}</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>
<translation id="9207861905230894330">Échec de l'ajout de l'article.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">EFFACER LE FORMULAIRE</translation>
<translation id="939736085109172342">Nouveau dossier</translation>
-<translation id="941721044073577244">Vous n'êtes pas autorisé à consulter ce site</translation>
<translation id="969892804517981540">Build officiel</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Aucun}=1{1 élément}one{# élément}other{# éléments}}</translation>
+<translation id="981121421437150478">Hors connexion</translation>
<translation id="988159990683914416">Build de développement</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_gu.xtb b/chromium/components/strings/components_strings_gu.xtb
index 3b3e194d072..7a3b1129791 100644
--- a/chromium/components/strings/components_strings_gu.xtb
+++ b/chromium/components/strings/components_strings_gu.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">અન્ય ઍપ્લિકેશનો બંધ કરો</translation>
<translation id="1055184225775184556">&amp;ઉમેરવું પૂર્વવત્ કરો</translation>
<translation id="10614374240317010">ક્યારેય ન સચવાયેલું</translation>
+<translation id="1066396345355680611">તમે <ph name="SITE" /> અને કેટલીક અન્ય સાઇટના સંરક્ષિત કન્ટેન્ટનો ઍક્સેસ ગુમાવી શકો છો.</translation>
<translation id="106701514854093668">ડેસ્કટૉપ બુકમાર્ક્સ</translation>
<translation id="1074497978438210769">સુરક્ષિત નથી</translation>
<translation id="1080116354587839789">પહોળાઈ પ્રમાણે ફિટ કરો</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">વાચન સૂચિ</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" /> એ ક્રેશ રિપોર્ટ કૅપ્ચર કરવામાં આવી (હજી સુધી અપલોડ કરવામાં કે અવગણવામાં આવેલ નથી)</translation>
<translation id="1281526147609854549"><ph name="ISSUER" /> દ્વારા જારી કરાયેલ</translation>
-<translation id="1283919782143846010">જોખમકારક કન્ટેન્ટ અવરોધિત કર્યું</translation>
<translation id="1285320974508926690">આ સાઇટનું ક્યારેય ભાષાંતર કરશો નહીં</translation>
<translation id="129553762522093515">તાજેતરમાં બંધ કરેલા</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />તમારી કૂકીઝને સાફ કરવાનો પ્રયાસ કરો<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">નોંધણી ડોમેન:</translation>
<translation id="1340482604681802745">પિકઅપ માટેનું સરનામું</translation>
-<translation id="1344211575059133124">એવું લાગે છે કે આ સાઇટની મુલાકાત લેવા માટે તમને પરવાનગીની જરૂર છે</translation>
<translation id="1344588688991793829">Chromium સ્વતઃભરણ સેટિંગ્સ...</translation>
<translation id="1348198688976932919">સાઇટમાં આગળ જોખમકારક ઍપ્લિકેશનો છે</translation>
<translation id="1374468813861204354">સૂચનો</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" /> માં <ph name="ORGANIZATION" /> ની ઓળખાણ <ph name="ISSUER" /> દ્વારા ચકાસવામાં આવી છે.</translation>
<translation id="1426410128494586442">હા</translation>
<translation id="1430915738399379752">છાપો</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> વધુ}one{<ph name="PAYMENT_METHOD_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> વધુ}other{<ph name="PAYMENT_METHOD_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> વધુ}}</translation>
<translation id="1506687042165942984">આ પૃષ્ઠની સાચવેલી (એટલે કે જૂની થઈ ગયેલી તરીકે ઓળખાતી) કૉપિ દર્શાવો.</translation>
<translation id="1517433312004943670">ફોન નંબર આવશ્યક</translation>
+<translation id="1517500485252541695">સ્વીકૃત ક્રેડિટ અને ડેબિટ કાર્ડ</translation>
<translation id="1519264250979466059">નિર્માણ તારીખ</translation>
+<translation id="1527263332363067270">કનેક્શનની રાહ જોઈ રહ્યાં છીએ...</translation>
<translation id="153384715582417236">હમણાં માટે બસ આટલું પૂરતું છે</translation>
<translation id="1549470594296187301">આ સુવિધાનો ઉપયોગ કરવા માટે JavaScript સક્ષમ કરેલ હોવી આવશ્યક છે.</translation>
<translation id="1555130319947370107">વાદળી</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">સિસ્ટમ વ્યવસ્થાપકનો સંપર્ક કરવાનો પ્રયાસ કરો.</translation>
<translation id="1740951997222943430">એક માન્ય સમાપ્તિ મહિનો દાખલ કરો</translation>
-<translation id="1745358365027406341">પૃષ્ઠને પછીથી ડાઉનલોડ કરો</translation>
<translation id="17513872634828108">ટેબ્સ ખોલો</translation>
<translation id="1753706481035618306">પૃષ્ઠ નંબર</translation>
<translation id="1763864636252898013">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર તમારા ઉપકરણની ઑપરેટિંગ સિસ્ટમ દ્વારા વિશ્વસનીય નથી. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">આવશ્યક ફીલ્ડ</translation>
<translation id="187918866476621466">સ્ટાર્ટઅપ પૃષ્ઠો ખોલો</translation>
<translation id="1883255238294161206">સૂચિ સંકુચિત કરો</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> વધુ}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> વધુ}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> વધુ}}</translation>
<translation id="1898423065542865115">ફિલ્ટરિંગ</translation>
+<translation id="1916770123977586577">આ સાઇટ પર તમારી અપડેટ કરેલ સેટિંગ્સ લાગુ પાડવા માટે, આ પેજને ફરીથી લોડ કરો</translation>
+<translation id="1919345977826869612">જાહેરાતો</translation>
<translation id="192020519938775529">{COUNT,plural, =0{કોઈ નહીં}=1{1 સાઇટ}one{# સાઇટ}other{# સાઇટ}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> પર જાઓ</translation>
+<translation id="1948773908305951926">સ્વીકૃત પ્રીપેઇડ કાર્ડ</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> બુકમાર્ક્સ</translation>
<translation id="1973335181906896915">અનુક્રમાંકન ભૂલ</translation>
<translation id="1974060860693918893">વિગતવાર</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP ભૂલ</translation>
<translation id="2270484714375784793">ફોન નંબર</translation>
<translation id="2282872951544483773">અનુપલબ્ધ પ્રયોગો</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> આઇટમ}one{<ph name="ITEM_COUNT" /> આઇટમ}other{<ph name="ITEM_COUNT" /> આઇટમ}}</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>
<translation id="2337852623177822836">તમારા વ્યવસ્થાપકે નિયંત્રિત કરેલ સેટિંગ</translation>
<translation id="2354001756790975382">અન્ય બુકમાર્ક્સ</translation>
<translation id="2354430244986887761">Google સલામત બ્રાઉઝિંગને તાજેતરમાં <ph name="SITE" /> પર <ph name="BEGIN_LINK" />હાનિકારક ઍપ્લિકેશન મળી<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">ચાલુ રાખો</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> એ કૅપ્ચર કરેલ ક્રેશ રિપોર્ટ અપલોડ કરી ન હતી</translation>
<translation id="2367567093518048410">સ્તર</translation>
-<translation id="237718015863234333">કોઈ UI વિકલ્પો ઉપલબ્ધ નથી</translation>
<translation id="2384307209577226199">એન્ટરપ્રાઇઝ ડિફોલ્ટ</translation>
<translation id="2386255080630008482">સર્વરનું પ્રમાણપત્ર રદ કરવામાં આવ્યું છે.</translation>
<translation id="2392959068659972793">કોઈ કિંમત સેટ નહીં સાથે નીતિઓ બતાવો</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">જો ચેક કરેલું હોય, તો ઝડપથી ફોર્મ ભરવા માટે Chromium આ ઉપકરણ પર તમારા કાર્ડની એક કૉપિ સંગ્રહિત કરશે.</translation>
<translation id="2498091847651709837">નવું કાર્ડ સ્કૅન કરો</translation>
<translation id="2501278716633472235">પાછા જાઓ</translation>
+<translation id="2503184589641749290">સ્વીકૃત ડેબિટ અને પ્રીપેઇડ કાર્ડ</translation>
<translation id="2515629240566999685">તમારા વિસ્તારમાં સિગ્નલ તપાસીને</translation>
-<translation id="2516305470678292029">UI વિકલ્પો</translation>
<translation id="2539524384386349900">શોધો</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> એ અમાન્ય પ્રતિસાદ મોકલ્યો.</translation>
<translation id="2556876185419854533">&amp;સંપાદિત કરવું પૂર્વવત્‌ કરો</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">વિતરણ પદ્ધતિ</translation>
<translation id="277499241957683684">ઉપકરણ રેકોર્ડ ખૂટે છે</translation>
<translation id="2784949926578158345">કનેક્શન ફરીથી સેટ થયું.</translation>
+<translation id="2788784517760473862">સ્વીકૃત ક્રેડિટ કાર્ડ</translation>
<translation id="2794233252405721443">સાઇટ અવરોધિત કરી</translation>
<translation id="2799020568854403057">સાઇટ આગળ હાનિકારક ઍપ્લિકેશનો ધરાવે છે</translation>
<translation id="2803306138276472711">Google Safe Browsing ને તાજેતરમાં <ph name="SITE" /> પર <ph name="BEGIN_LINK" />મૉલવેર મળ્યું<ph name="END_LINK" />. વેબસાઇટ્સ કે જે સામાન્ય રીતે સુરક્ષિત છે તે ક્યારેક મૉલવેરથી દૂષિત હોય છે.</translation>
<translation id="2824775600643448204">સરનામું અને શોધ બાર</translation>
<translation id="2826760142808435982">કનેક્શન <ph name="CIPHER" /> નો ઉપયોગ કરીને એન્ક્રિપ્ટ અને પ્રમાણિત કરેલુ છે અને મુખ્ય એક્સચેન્જ મેકેનિઝ્મ તરીકે <ph name="KX" /> નો ઉપયોગ કરે છે.</translation>
<translation id="2835170189407361413">ફોર્મ સાફ કરો</translation>
+<translation id="2851634818064021665">આ સાઇટની મુલાકાત લેવા માટે તમને પરનાવગીની જરૂર છે</translation>
<translation id="2856444702002559011">હુમલાખોરો કદાચ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />માંથી તમારી માહિતી (ઉદાહરણ તરીકે, પાસવર્ડ, સંદેશા અથવા ક્રેડિટ કાર્ડ) ચોરવાનો પ્રયાસ કરી રહ્યાં હોઈ શકે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધુ જાણો<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">ફરીથી લોડ કરશો નહીં</translation>
-<translation id="2900469785430194048">આ વેબ પૃષ્ઠ પ્રદર્શિત કરવાનો પ્રયાસ કરતી વખતે Google Chrome ની મેમરી સમાપ્ત થઈ ગઈ.</translation>
<translation id="2909946352844186028">નેટવર્ક ફેરફાર મળ્યો હતો.</translation>
<translation id="2916038427272391327">અન્ય પ્રોગ્રામ બંધ કરો</translation>
<translation id="2922350208395188000">સર્વરનું પ્રમાણપત્ર તપાસી શકાતું નથી.</translation>
<translation id="2928905813689894207">બિલિંગનું સરનામું</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> વધુ}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> વધુ}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> વધુ}}</translation>
<translation id="2941952326391522266">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર <ph name="DOMAIN2" /> નું છે. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation>
<translation id="2948083400971632585">તમે સેટિંગ્સ પૃષ્ઠમાંથી કનેક્શન માટે ગોઠવવામાં આવેલ કોઇપણ પ્રોક્સીઓ અક્ષમ કરી શકો છો.</translation>
<translation id="2955913368246107853">શોધ બાર બંધ કરો</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">જો સક્ષમ કરેલું હોય, તો ઝડપથી ફોર્મ ભરવા માટે Chromium આ ઉપકરણ પર તમારા કાર્ડની એક કૉપિ સંગ્રહિત કરશે.</translation>
<translation id="2985398929374701810">એક માન્ય સરનામું દાખલ કરો</translation>
<translation id="2986368408720340940">આ પિકઅપ પદ્ધતિ ઉપલબ્ધ નથી. કોઈ ભિન્ન પદ્ધતિ અજમાવો.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">તમે છુપા ટેબ્સમાં જુઓ છો તે પૃષ્ઠો તમે તમારા બધા છુપા ટેબ્સ બંધ કરી દો તે પછી તમારા બ્રાઉઝરના ઇતિહાસ, કુકી સ્ટોર અથવા શોધ ઇતિહાસમાં રહેશે નહીં. તમે ડાઉનલોડ કરો છો તે કોઈપણ ફાઇલો અથવા તમે બનાવો છો તે બુકમાર્ક્સ રાખવામાં આવશે.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">આઇલેન્ડ</translation>
-<translation id="317583078218509884">નવી સાઇટ પરવાનગીઓ સેટિંગ્સ પૃષ્ઠ ફરીથી લોડ કર્યા પછી પ્રભાવમાં આવશે.</translation>
<translation id="3176929007561373547">પ્રોક્સી સર્વર કાર્ય કરી રહ્યું છે તેની ખાતરી કરવા માટે તમારી પ્રોક્સી સેટિંગ્સ તપાસો
અથવા તમારા નેટવર્ક વ્યવસ્થાપકનો સંપર્ક કરો. જો તમે પ્રોક્સી સર્વરનો ઉપયોગ કરવો 
જોઇએ એવું ન માનતા હો:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">છુપા મોડમાં પૃષ્ઠ ખોલો</translation>
<translation id="320323717674993345">ચુકવણી રદ કરો</translation>
<translation id="3207960819495026254">બુકમાર્ક કરેલ</translation>
<translation id="3225919329040284222">સર્વર એક પ્રમાણપત્ર પ્રસ્તુત કરે છે જે બિલ્ટ-ઇન અપેક્ષાઓ સાથે મેળ ખાતું નથી. આ અપેક્ષાઓમાં તમને સુરક્ષિત રાખવા માટે અમુક ચોક્કસ, ઉચ્ચ-સુરક્ષા વેબસાઇટ્સનો સમાવેશ થાય છે.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;પુનઃક્રમાંકિત કરવું પૂર્વવત્ કરો</translation>
<translation id="3282497668470633863">કાર્ડ પર નામ ઉમેરો</translation>
<translation id="3286538390144397061">હવે ફરીથી પ્રારંભ કરો</translation>
+<translation id="3287510313208355388">ઑનલાઇન હોય ત્યારે ડાઉનલોડ કરો</translation>
<translation id="3303855915957856445">કોઈ શોધ પરિણામો મળ્યાં નથી</translation>
<translation id="3305707030755673451">તમારો ડેટા <ph name="TIME" /> ના રોજ તમારા સમન્વયન પાસફ્રેઝ સાથે એન્ક્રિપ્ટ કરવામાં આવ્યો હતો. સમન્વયન શરૂ કરવા માટે તે દાખલ કરો.</translation>
<translation id="3320021301628644560">બિલિંગ સરનામું ઉમેરો</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">આનયન અંતરાલ:</translation>
<translation id="3462200631372590220">વિગતવાર છુપાવો</translation>
<translation id="3467763166455606212">કાર્ડધારકનું નામ આવશ્યક છે</translation>
-<translation id="3478058380795961209">સમય સમાપ્તિ મહિનો</translation>
<translation id="3479539252931486093">શું આ અનપેક્ષિત હતું? <ph name="BEGIN_LINK" />અમને જણાવો<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">હમણાં નહીં</translation>
<translation id="3498215018399854026">અમે આ પળે તમારા વાલી સુધી પહોંચી શક્યાં નથી. કૃપા કરીને ફરી પ્રયાસ કરો.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;કૃપા કરીને &lt;strong&gt;સેટિંગ્સ&lt;/strong&gt; ઍપ્લિકેશનના &lt;strong&gt;સામાન્ય&lt;/strong&gt; વિભાગમાંથી તારીખ અને સમય સમાયોજિત કરો.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />વધુ જાણો<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">ક્રેડિટ અને પ્રીપેઇડ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
<translation id="3582930987043644930">નામ ઉમેરો</translation>
<translation id="3583757800736429874">&amp;ખસેડવું ફરી કરો</translation>
<translation id="3586931643579894722">વિગતો છુપાવો</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">જો તમે આ વારંવાર જોઈ રહ્યાં છો, તો આ <ph name="HELP_LINK" /> અજમાવી જુઓ.</translation>
<translation id="3658742229777143148">પુનરાવર્તન</translation>
<translation id="3678029195006412963">વિનંતી પર સહી કરી શક્યાં નથી</translation>
+<translation id="3678529606614285348">એક નવી છુપી વિંડોમાં પેજ ખોલો (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375"><ph name="CRASH_TIME" /> એ ક્રેશ રિપોર્ટ કૅપ્ચર કરી અને <ph name="UPLOAD_TIME" /> એ અપલોડ કર્યો હતો</translation>
<translation id="3681007416295224113">પ્રમાણપત્ર માહિતી</translation>
<translation id="3690164694835360974">લોગિન સુરક્ષિત નથી</translation>
-<translation id="3693415264595406141">પાસવર્ડ:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">લોડ કરી રહ્યું છે...</translation>
<translation id="3712624925041724820">લાઇસેંસીસ પૂર્ણ</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805"><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="375403751935624634">સર્વર ભૂલને કારણે ભાષાંતર નિષ્ફળ રહ્યું.</translation>
<translation id="3759461132968374835">તમે હાલમાં ક્રેશની જાણ કરી નથી. ક્રેશની જાણ કરવાનું અક્ષમ હતું ત્યારે થયેલા ક્રેશ અહીં દેખાશે નહીં.</translation>
+<translation id="3765032636089507299">Safe Browsing પેજ રચના હેઠળ છે.</translation>
<translation id="3778403066972421603">શું તમે આ કાર્ડને તમારા Google એકાઉન્ટ અને આ ઉપકરણ પર સાચવવા માગો છો?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /> માં સમાપ્ત થાય છે</translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">આ સાઇટને ઍક્સેસ કરવાની તમારી વિનંતી <ph name="NAME" /> ને મોકલવામાં આવી છે</translation>
<translation id="3890664840433101773">ઇમેઇલ ઉમેરો</translation>
<translation id="3901925938762663762">કાર્ડની સમયસીમા સમાપ્ત થઇ ગઈ છે</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ક્રેશ રિપોર્ટ ID <ph name="CRASH_ID" /> (સ્થાનિક ક્રેશ ID: <ph name="CRASH_LOCAL_ID" />) અપલોડ કર્યું</translation>
<translation id="3949571496842715403">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર વિષય વૈકલ્પિક નામનો ઉલ્લેખ કરતું નથી. આ કોઈ ખોટી ગોઠવણીને કારણે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવતો હોવાને કારણે બન્યું હોઈ શકે.</translation>
+<translation id="3949601375789751990">તમારો બ્રાઉઝિંગ ઇતિહાસ અહીં દેખાય છે</translation>
<translation id="3963721102035795474">રીડર મોડ</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{કોઈ નહીં}=1{1 સાઇટમાંથી }one{# સાઇટમાંથી }other{# સાઇટમાંથી }}</translation>
<translation id="397105322502079400">ગણના કરી રહ્યું છે...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">સાઇટ સેટિંગ્સ</translation>
<translation id="4169947484918424451">શું તમે ઇચ્છો છો કે Chromium આ કાર્ડ સાચવે?</translation>
<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="4196861286325780578">&amp;ખસેડવું ફરી કરો</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ફાયરવોલ અને એન્ટીવાઇરસ ગોઠવણી તપાસીને<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ક્રેશેસ</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">આ સાઇટ પરના હુમલાખોરો તમારા કમ્પ્યુટર પર તમારી માહિતી (ઉદાહરણ તરીકે, ફોટા, પાસવર્ડ્સ, સંદેશા અને ક્રેડિટ કાર્ડ્સ) ને ચોરી શકે કે કાઢી નાખે તેવા જોખમી પ્રોગ્રામ્સને ઇન્સ્ટોલ કરવાનો પ્રયાસ કરી શકે છે.</translation>
<translation id="4372948949327679948">અપેક્ષિત <ph name="VALUE_TYPE" /> મૂલ્ય.</translation>
<translation id="4377125064752653719">તમે <ph name="DOMAIN" /> પર પહોંચવાનો પ્રયાસ કર્યો, પણ સર્વર દ્વારા પ્રસ્તુત કરવામાં આવેલું પ્રમાણપત્ર તેના રજૂકર્તા દ્વારા જ રદ કરવામાં આવ્યું છે. આનો અર્થ છે કે સર્વરે પ્રસ્તુત કરેલા સુરક્ષા પ્રમાણપત્રો પૂર્ણપણે વિશ્વસનીય નથી. તમે કોઈ હુમલાખોર જોડે વાત કરતા હોઈ શકો છો.</translation>
-<translation id="4381091992796011497">વપરાશકર્તાનું નામ:</translation>
<translation id="4394049700291259645">અક્ષમ કરો</translation>
<translation id="4406896451731180161">શોધ પરિણામો</translation>
<translation id="4424024547088906515">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર Chrome દ્વારા વિશ્વસનીય નથી. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> એ તમારું લોગિન પ્રમાણપત્ર સ્વીકાર્યું ન હતું અથવા કદાચ કોઈ એક પ્રદાન કરવામાં આવ્યું નથી.</translation>
<translation id="443673843213245140">પ્રોક્સીનો ઉપયોગ અક્ષમ કરેલો છે પણ એક સ્પષ્ટ પ્રોક્સી ગોઠવણી ઉલ્લેખિત છે.</translation>
+<translation id="445100540951337728">સ્વીકૃત ડેબિટ કાર્ડ</translation>
<translation id="4506176782989081258">માન્યતા ભૂલ: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">સિસ્ટમ વ્યવસ્થાપકનો સંપર્ક કરીને</translation>
<translation id="450710068430902550">વ્યવસ્થાપક સાથે શેર કરવું</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Chrome માંથી સરનામું દૂર કરીએ?</translation>
<translation id="4592951414987517459">આધુનિક સાઇફર સ્યૂટનો ઉપયોગ કરીને <ph name="DOMAIN" /> સાથેનું તમારું કનેક્શન એન્ક્રિપ્ટ કરાયું છે.</translation>
<translation id="4594403342090139922">&amp;કાઢી નાખવું પૂર્વવત્‌ કરો</translation>
+<translation id="4611292653554630842">લૉગ ઇન કરો</translation>
<translation id="4619615317237390068">અન્ય ઉપકરણોમાંથી ટૅબ્સ</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેના સુરક્ષા પ્રમાણપત્રમાં ભૂલો છે. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation>
<translation id="4690462567478992370">અમાન્ય પ્રમાણપત્રનો ઉપયોગ કરવાનું બંધ કરો</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">તમારું કનેક્શન અવરોધાયું હતું</translation>
+<translation id="471880041731876836">તમારી પાસે આ સાઇટની મુલાકાત લેવાની પરવાનગી નથી</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows નેટવર્ક ડાયગ્નોસ્ટિક્સ ચલાવી રહ્યાં છે<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">નીતિઓ ફરીથી લોડ કરો</translation>
<translation id="4728558894243024398">પ્લેટફોર્મ</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">જુઓ</translation>
<translation id="4854362297993841467">વિતરણની આ પદ્ધતિ ઉપલબ્ધ નથી. કોઈ ભિન્ન પદ્ધતિ અજમાવો.</translation>
<translation id="4858792381671956233">તમે આ સાઇટની મુલાકાત લો છો તે ઠીક છે કે કેમ તેવું તમે તમારા માતાપિતાને પૂછ્યું</translation>
-<translation id="4863764087567530506">આ કન્ટેન્ટ કદાચ સૉફ્ટવેર ઇન્સ્ટૉલ કરવા માટે અથવા વ્યક્તિગત માહિતી કઢાવવા માટે તમારી સાથે કપટ કરવાનો પ્રયાસ કરી શકે છે. <ph name="BEGIN_LINK" />છતાં પણ બતાવો<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">ઇતિહાસ શોધ</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">પ્રમાણીકરણ આવશ્યક છે</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{અને 1 વધુ વેબ પૃષ્ઠ}one{અને # વધુ વેબ પૃષ્ઠ}other{અને # વધુ વેબ પૃષ્ઠ}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">આ પૃષ્ઠ કોઈ અજ્ઞાત ભાષામાંથી <ph name="LANGUAGE_LANGUAGE" /> માં અનુવાદિત કરવામાં આવ્યું છે</translation>
<translation id="4923459931733593730">ચુકવણી</translation>
<translation id="4926049483395192435">ઉલ્લેખિત હોવું આવશ્યક છે.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">ક્રિયાઓ</translation>
<translation id="4958444002117714549">સૂચિ વિસ્તૃત કરો</translation>
<translation id="4974590756084640048">ચેતવણીઓ ફરીથી સક્ષમ કરો</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">ખોટો પાસવર્ડ</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>
<translation id="5087580092889165836">કાર્ડ ઉમેરો</translation>
<translation id="5089810972385038852">રાજ્ય</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">પ્રાંત</translation>
<translation id="5115563688576182185">(64-બિટ)</translation>
<translation id="5141240743006678641">તમારા Google ઓળખપત્રો સાથે સમન્વયિત પાસવર્ડ્સને એન્ક્રિપ્ટ કરો</translation>
-<translation id="514421653919133810">છુપા મોડમાં પૃષ્ઠ ખોલો (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">નીતિ પ્રતિક્રિયામાં ભૂલ કોડ હાજર</translation>
+<translation id="5159010409087891077">એક નવી છુપી વિંડોમાં પેજ ખોલો (⇧⌘N)</translation>
<translation id="5171045022955879922">URL શોધો અથવા લખો</translation>
<translation id="5172758083709347301">મશીન</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> માં નથી? આ ભૂલની જાણ કરો </translation>
-<translation id="5181140330217080051">ડાઉનલોડ કરી રહ્યું છે</translation>
<translation id="5190835502935405962">બુકમાર્ક્સ બાર</translation>
<translation id="5199729219167945352">પ્રયોગો</translation>
<translation id="5205222826937269299">નામ આવશ્યક છે</translation>
<translation id="5222812217790122047">ઇમેઇલ આવશ્યક છે</translation>
<translation id="5251803541071282808">મેઘ</translation>
<translation id="5277279256032773186">કાર્ય પર Chrome નો ઉપયોગ કરી રહ્યાં છો? વ્યવસાયો તેમના કર્મચારીઓ માટે Chrome સેટિંગ્સને સંચાલિત કરી શકે છે. વધુ જાણો</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />સૉફ્ટવેરને અસ્થાયીરૂપે અક્ષમ કરવા માટે આ પગલાં અનુસરો જેથી તમે વેબ પર જઈ શકો. તમને વ્યવસ્થાપકના વિશેષાધિકારની જરૂર પડશે.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />પ્રારંભ કરો<ph name="END_BOLD" /> પર ક્લિક કરો, પછી માટે શોધો અને <ph name="BEGIN_BOLD" />"સ્થાનિક સેવાઓ જુઓ"<ph name="END_BOLD" /> પસંદ કરો
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" /> પસંદ કરો
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />પ્રારંભ કરવાનો પ્રકાર<ph name="END_BOLD" /> હેઠળ, <ph name="BEGIN_BOLD" />અક્ષમ કરેલ<ph name="END_BOLD" /> પસંદ કરો
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />સેવાની સ્થિતિ<ph name="END_BOLD" /> હેઠળ, <ph name="BEGIN_BOLD" />બંધ કરો<ph name="END_BOLD" /> પર ક્લિક કરો
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />લાગુ કરો<ph name="END_BOLD" /> પર ક્લિક કરો, પછી <ph name="BEGIN_BOLD" />ઓકે<ph name="END_BOLD" /> પર ક્લિક કરો
+ <ph name="LIST_ITEM" />તમારા કમ્પ્યુટરમાંથી કાયમ માટે સૉફ્ટવેરને દૂર કરવા માટે <ph name="BEGIN_LEARN_MORE_LINK" />Chrome સહાય કેન્દ્ર<ph name="END_LEARN_MORE_LINK" />ની મુલાકાત લો
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">આ સાઇટ પરનું તમારું કનેક્શન ખાનગી નથી. કોઈપણ સમયે VR મોડથી બહાર નીકળવા માટે, હેડસેટ દૂર કરો અને પાછળ દબાવો.</translation>
<translation id="5299298092464848405">ભૂલ વિશ્લેષણ નીતિ</translation>
<translation id="5308689395849655368">ક્રેશની જાણ કરવાનું અક્ષમ કર્યું છે.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" પર સ્કીમા માન્યતા ભૂલ: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">આ <ph name="HOST_NAME" /> પૃષ્ઠ શોધી શકાતું નથી</translation>
<translation id="5455374756549232013">ખરાબ નીતિ સમયનોંધ</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" /> માંથી <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">અમાન્ય</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> વધુ}one{<ph name="CONTACT_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> વધુ}other{<ph name="CONTACT_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> વધુ}}</translation>
<translation id="5470861586879999274">&amp;સંપાદિત કરવું ફરી કરો</translation>
+<translation id="5481076368049295676">આ કન્ટેન્ટ કદાચ તમારા ઉપકરણ પર જોખમકારક સૉફ્ટવેર ઇન્સ્ટૉલ કરવાનો પ્રયાસ કરી શકે છે કે જે તમારી માહિતીની ચોરી કરે અથવા કાઢી નાખે છે. <ph name="BEGIN_LINK" />છતાં પણ બતાવો<ph name="END_LINK" /></translation>
<translation id="54817484435770891">માન્ય સરનામું ઉમેરો</translation>
<translation id="5492298309214877701">કંપની, સંસ્થા અથવા શાળા ઇન્ટ્રાનેટ પર આ સાઇટ બાહ્ય વેબસાઇટ જેવું જ URL ધરાવે છે.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">વિનંતી કરેલ લેખ શોધી શકાયો નથી.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> પરનું એમ્બેડ કરેલ પૃષ્ઠ આ કહે છે:</translation>
<translation id="5556459405103347317">ફરિથી લોડ કરો</translation>
+<translation id="5560088892362098740">સમાપ્તિ તારીખ</translation>
<translation id="5565735124758917034">સક્રિય</translation>
<translation id="5571083550517324815">આ સરનામેથી પિકઅપ કરી શકતા નથી. કોઈ ભિન્ન સરનામું પસંદ કરો.</translation>
<translation id="5572851009514199876">કૃપા કરીને Chrome ને પ્રારંભ કરો અને સાઇન ઇન કરો જેથી કરીને Chrome તપાસી શકે કે તમને આ સાઇટની ઍક્સેસની મંજૂરી છે કે કેમ.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">નીતિ સેટિંગ્સ લોડ કરવામાં નિષ્ફળ થયાં</translation>
<translation id="5631439013527180824">અમાન્ય ઉપકરણ સંચાલન ટોકન</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="5646376287012673985">સ્થાન</translation>
<translation id="5659593005791499971">ઇમેઇલ</translation>
<translation id="5669703222995421982">વ્યક્તિગત કરેલ સામગ્રી મેળવો</translation>
<translation id="5675650730144413517">આ પૃષ્ઠ કામ કરી રહ્યું નથી</translation>
<translation id="5710435578057952990">આ વેબસાઇટની ઓળખ ચકાસવામાં આવી નથી.</translation>
-<translation id="5713016350996637505">ભ્રામક કન્ટેન્ટ અવરોધિત કર્યું</translation>
+<translation id="5719499550583120431">પ્રીપેઇડ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
<translation id="5720705177508910913">વર્તમાન વપરાશકર્તા</translation>
<translation id="5732392974455271431">તમારા માટે તમારા માતાપિતા તેને અનાવરોધિત કરી શકે છે</translation>
<translation id="5763042198335101085">એક માન્ય ઇમેઇલ ઍડ્રેસ ઉમેરો</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">શું તમે તમારી <ph name="CARD_DETAIL" /> માહિતી ભરવા માગો છો?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" /> સાથેના તમારા કનેક્શનને ઑબ્સોલિટ સાઇફર સ્યૂટનો ઉપયોગ કરીને એન્ક્રિપ્ટ કરાયું છે.</translation>
<translation id="5813119285467412249">&amp;ઉમેરવું ફરી કરો</translation>
-<translation id="5814352347845180253">તમે <ph name="SITE" /> અને કેટલીક અન્ય સાઇટ્સની પ્રીમિયમ સામગ્રીની ઍક્સેસ ગુમાવી શકો છો.</translation>
<translation id="5838278095973806738">તમારે આ સાઇટ પર કોઈપણ સંવેદનશીલ માહિતી (ઉદાહરણ તરીકે, પાસવર્ડ્સ અથવા ક્રેડિટ કાર્ડ્સ) દાખલ કરવી જોઈએ નહીં, કારણ કે તે હુમલાખોર દ્વારા ચોરવામાં આવી શકે છે.</translation>
<translation id="5869405914158311789">આ સાઇટ પર પહોંચી શકાતું નથી</translation>
<translation id="5869522115854928033">સાચવેલા પાસવર્ડ્સ</translation>
<translation id="5872918882028971132">પેરેન્ટ સૂચનો</translation>
+<translation id="5893752035575986141">ક્રેડિટ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
<translation id="5901630391730855834">પીળો</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (સમન્વયિત)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 ઉપયોગમાં છે}one{# ઉપયોગમાં છે}other{# ઉપયોગમાં છે}}</translation>
-<translation id="5926846154125914413">તમે કેટલીક સાઇટ્સની પ્રીમિયમ સામગ્રીની ઍક્સેસ ગુમાવી શકો છો.</translation>
<translation id="5959728338436674663">જોખમી અ‍ૅપ્લિકેશનો અને સાઇટ્સ શોધવામાં સહાય કરવા માટે Google ને કેટલીક <ph name="BEGIN_WHITEPAPER_LINK" />સિસ્ટમ માહિતી અને પૃષ્ઠ સામગ્રી<ph name="END_WHITEPAPER_LINK" /> આપમેળે મોકલો. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">ઇતિહાસમાંથી દૂર કરો</translation>
<translation id="5975083100439434680">ઝૂમ ઘટાડો</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">બંધ કરો</translation>
<translation id="6042308850641462728">વધુ</translation>
<translation id="6047233362582046994">જો તમે તમારી સુરક્ષાના જોખમોને સમજો છો, તો તમે જોખમકારક ઍપ્લિકેશનો દૂર કરતા પહેલા <ph name="BEGIN_LINK" />આ સાઇટની મુલાકાત<ph name="END_LINK" /> લઈ શકો છો.</translation>
+<translation id="6047927260846328439">આ કન્ટેન્ટ કદાચ સૉફ્ટવેર ઇન્સ્ટૉલ કરવા માટે અથવા વ્યક્તિગત માહિતી કઢાવવા માટે તમારી સાથે કપટ કરવાનો પ્રયાસ કરી શકે છે. <ph name="BEGIN_LINK" />છતાં પણ બતાવો<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">તમે અત્યારે આ <ph name="SITE" />ની મુલાકાત લઈ શકતાં નથી કારણ કે આ વેબસાઇટ પ્રમાણપત્ર પિનિંગનો ઉપયોગ કરે છે. નેટવર્કમાં ભૂલ આવવી અને હુમલા થવા સામાન્ય રીતે અસ્થાયી હોય છે, તેથી આ પેજ સંભવિત રૂપે થોડા સમય પછી કાર્ય કરશે.</translation>
<translation id="6060685159320643512">સાવચેતી રાખો, આ પ્રયોગો નુકસાનકારક હોઈ શકે છે</translation>
<translation id="6080696365213338172">તમે વ્યવસ્થાપક-પ્રદત્ત પ્રમાણપત્રનો ઉપયોગ કરીને સામગ્રી ઍક્સેસ કરી છે. તમે <ph name="DOMAIN" /> ને પ્રદાન કરેલ ડેટા તમારા વ્યવસ્થાપક દ્વારા ઇન્ટરસેપ્ટ થઈ શકે છે.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">વધુ જાણો</translation>
<translation id="6169916984152623906">હવે તમે ખાનગીમાં બ્રાઉઝ કરી શકો છો અને અન્ય લોકો જે આ ઉપકરણનો ઉપયોગ કરે છે તે પણ તમારી પ્રવૃત્તિ જોઇ શકશે નહીં. જો કે ડાઉનલોડ અને બુકમાર્ક સાચવવામાં આવશે.</translation>
<translation id="6177128806592000436">આ સાઇટ પરનું તમારું કનેક્શન સુરક્ષિત નથી</translation>
-<translation id="6184817833369986695">(સાથી: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">તમારું ઇન્ટરનેટ કનેક્શન તપાસો</translation>
<translation id="6218753634732582820">Chromium માંથી સરનામું દૂર કરીએ?</translation>
<translation id="6221345481584921695">Google Safe Browsing ને તાજેતરમાં <ph name="SITE" /> પર <ph name="BEGIN_LINK" />મૉલવેર મળ્યું<ph name="END_LINK" />. વેબસાઇટ્સ કે જે સામાન્ય રીતે સુરક્ષિત છે તે ક્યારેક મૉલવેરથી દૂષિત હોય છે. દુર્ભાવનાપૂર્ણ સામગ્રી એક જ્ઞાત મૉલવેર વિક્રેતા એવા, <ph name="SUBRESOURCE_HOST" /> થી આવે છે.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">શું કૂલ નવી Chrome સુવિધાઓમાં રુચિ ધરાવો છો? chrome.com/beta પર અમારી બીટા ચેનલ અજમાવી જુઓ.</translation>
-<translation id="6389758589412724634">આ વેબ પૃષ્ઠ પ્રદર્શિત કરવાનો પ્રયાસ કરતી વખતે Chromium ની મેમરી સમાપ્ત થઈ ગઈ.</translation>
+<translation id="6397451950548600259">તમારા કમ્પ્યુટરમાંનું સૉફ્ટવેર Chromeને સુરક્ષિત રીતે વેબ સાથે કનેક્ટ થવાથી રોકી રહ્યું છે</translation>
<translation id="6404511346730675251">બુકમાર્ક સંપાદિત કરો</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> માટે સમાપ્તિ તારીખ અને CVC દાખલ કરો</translation>
<translation id="6414888972213066896">આ પૃષ્ઠની મુલાકાત લો છો તે ઠીક છે કે કેમ તેવું તમે તમારા માતાપિતાને પૂછ્યું</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">ઉપકરણ નીતિઓ</translation>
<translation id="6477321094435799029">Chrome ને આ પૃષ્ઠ પર અસામાન્ય કોડ મળ્યો અને તમારી વ્યક્તિગત માહિતી (ઉદાહરણ તરીકે, પાસવર્ડ્સ, ફોન નંબર્સ અને ક્રેડિટ કાર્ડ્સ)ની સુરક્ષા કરવા માટે તેને અવરોધિત કરેલ છે.</translation>
<translation id="6489534406876378309">ક્રેશ અપલોડ કરવાનું શરૂ કરો</translation>
+<translation id="6507833130742554667">ક્રેડિટ અને ડેબિટ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
<translation id="6508722015517270189">Chrome ને પુનઃપ્રારંભ કરો</translation>
<translation id="6529602333819889595">&amp;કાઢી નાખવું ફરી કરો</translation>
<translation id="6534179046333460208">વાસ્તવિક વેબ સૂચનો</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">આ કન્ટેન્ટ તમારા ઉપકરણ પર જોખમી સૉફ્ટવેર ઇન્સ્ટૉલ કરવાનો પ્રયાસ કરી શકે છે જે તમારી માહિતીની ચોરી કરે અથવા કાઢી નાખે છે. <ph name="BEGIN_LINK" />છતાં પણ બતાવો<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">એન્ક્રિપ્શન વિકલ્પો</translation>
<translation id="662080504995468778">પૃષ્ઠ પર રહો</translation>
<translation id="6626291197371920147">માન્ય કાર્ડ નંબર ઉમેરો</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> શોધ</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પરના હુમલાખોરો કદાચ હાલમાં તમારા Mac પર જોખમી પ્રોગ્રામ ઇન્સ્ટૉલ કરવાનો પ્રયાસ કરે છે કે જે તમારી માહિતી (ઉદાહરણ તરીકે, ફોટા, પાસવર્ડ, સંદેશા અને ક્રેડિટ કાર્ડ) ચોરી અથવા કાઢી નાખી શકે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધુ જાણો<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">આ નીતિ દૂર કરવામાં આવેલી છે.</translation>
+<translation id="6657585470893396449">પાસવર્ડ</translation>
<translation id="6671697161687535275">Chromium માંથી ફોર્મ સૂચન દૂર કરીએ?</translation>
<translation id="6685834062052613830">સાઇન આઉટ કરો અને સેટઅપ પૂર્ણ કરો</translation>
<translation id="6710213216561001401">પહેલાનું</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">જીલ્લો</translation>
<translation id="6973656660372572881">નિયત પ્રોક્સી સર્વર્સ અને .pac script URL બન્નેનો ઉલ્લેખ કરેલો છે.</translation>
<translation id="6989763994942163495">વિગતવાર સેટિંગ્સ બતાવો...</translation>
-<translation id="7000990526846637657">કોઈ ઇતિહાસ પ્રવિષ્ટિઓ મળી નથી</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">તમારા Google એકાઉન્ટમાં <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> પર બ્રાઉઝિંગ ઇતિહાસના અન્ય સ્વરૂપો હોઇ શકે છે.</translation>
<translation id="7029809446516969842">પાસવર્ડ્સ</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">આ સરનામે વિતરણ કરી શકાતું નથી. કોઈ ભિન્ન સરનામું પસંદ કરો.</translation>
<translation id="7138472120740807366">વિતરણ પદ્ધતિ</translation>
<translation id="7139724024395191329">એમિરાત</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> વધુ}one{<ph name="PAYMENT_METHOD_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> વધુ}other{<ph name="PAYMENT_METHOD_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> વધુ}}</translation>
<translation id="7155487117670177674">ચુકવણી સુરક્ષિત નથી</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> વધુ}one{<ph name="SHIPPING_OPTION_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> વધુ}other{<ph name="SHIPPING_OPTION_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> વધુ}}</translation>
<translation id="7179921470347911571">હમણાં ફરીથી લોંચ કરો</translation>
<translation id="7180611975245234373">તાજું કરો</translation>
<translation id="7182878459783632708">કોઈ નીતિઓ સેટ નથી</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">ડાઉનલોડ કરો</translation>
<translation id="7518003948725431193">વેબ સરનામાં માટે કોઈ વેબપેજ મળ્યું નથી: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">આ સાઇટ પરનું તમારું કનેક્શન ખાનગી નથી</translation>
<translation id="7535087603100972091">મૂલ્ય</translation>
<translation id="7537536606612762813">ફરજિયાત</translation>
<translation id="7542403920425041731">એકવાર તમે પુષ્ટિ કરી લો તે પછી, આ સાઇટ સાથે તમારા કાર્ડની વિગતો શેર કરવામાં આવશે.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">નીચેની ટિપને અજમાવો:</translation>
<translation id="7554791636758816595">નવું ટૅબ</translation>
<translation id="7567204685887185387">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર કપટપૂર્વક રજૂ કરવામાં આવેલ હોઈ શકે છે. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> વધુ}one{<ph name="CONTACT_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> વધુ}other{<ph name="CONTACT_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> વધુ}}</translation>
<translation id="7568593326407688803">આ પૃષ્ઠ<ph name="ORIGINAL_LANGUAGE" />માં છે શું તમે તેને અનુવાદિત કરવા માંગો છો?</translation>
<translation id="7569952961197462199">Chrome માંથી ક્રેડિટ કાર્ડ દૂર કરીએ?</translation>
<translation id="7569983096843329377">શ્યામ</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">પ્રમાણપત્ર</translation>
<translation id="7716147886133743102">વ્યવસ્થાપક દ્વારા અવરોધિત કરેલ છે</translation>
<translation id="7716424297397655342">કૅશમાંથી આ સાઇટ લોડ કરી શકાતી નથી</translation>
+<translation id="774634243536837715">જોખમકારક કન્ટેન્ટ અવરોધિત કર્યુ.</translation>
<translation id="7752995774971033316">બિનસંચાલિત</translation>
<translation id="7755287808199759310">તમારા માટે તમારા માતાપિતા તેને અનાવરોધિત કરી શકે છે</translation>
<translation id="7758069387465995638">ફાયરવોલ અથવા એન્ટિવાયરસ સોફ્ટવેર એ કનેક્શન અવરોધિત કર્યું હોઈ શકે છે.</translation>
+<translation id="7759163816903619567">પ્રદર્શનનું ડોમેન:</translation>
<translation id="7761701407923456692">સર્વરનું પ્રમાણપત્ર URL સાથે મેળ ખાતું નથી.</translation>
<translation id="7763386264682878361">ચુકવણી સ્પષ્ટ વિશ્લેષક</translation>
<translation id="7764225426217299476">સરનામું ઉમેરો</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />' માટે <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> મળ્યાં</translation>
<translation id="785549533363645510">જો કે, તમે અદૃશ્ય નથી. છુપામાં જવું તમારા નિયોક્તા, તમારા ઇન્ટરનેટ સેવા પ્રદાતા અથવા તમે મુલાકાત લો છો તે વેબસાઇટ્સથી તમારા બ્રાઉઝિંગને છુપાવતું નથી.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">ડેબિટ અને પ્રીપેઇડ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
<translation id="7887683347370398519">તમારું CVC તપાસો અને ફરીથી પ્રયાસ કરો</translation>
<translation id="79338296614623784">એક માન્ય ફોન નંબર દાખલ કરો</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">પ્રતિસાદ મોકલો</translation>
<translation id="8041940743680923270">વૈશ્વિક ડિફોલ્ટનો ઉપયોગ કરો (કહો)</translation>
<translation id="8088680233425245692">લેખ જોવામાં નિષ્ફળ થયાં.</translation>
-<translation id="8089520772729574115">1 MB કરતાં ઓછું</translation>
<translation id="8091372947890762290">સક્રિયતા સર્વર પર બાકી છે</translation>
<translation id="8118489163946903409">ચુકવણી પદ્ધતિ</translation>
<translation id="8131740175452115882">પુષ્ટિ કરો</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;ખસેડવું પૂર્વવત્‌ કરો</translation>
<translation id="8201077131113104583">ID "<ph name="EXTENSION_ID" />" સાથેના એક્સટેન્શન માટે અમાન્ય અપડેટ URL.</translation>
<translation id="8202097416529803614">ઓર્ડરનો સારાંશ</translation>
+<translation id="8205463626947051446">ખલેલ પાડતી જાહેરાતો બતાવવાનું વલણ ધરાવતી સાઇટ</translation>
<translation id="8218327578424803826">સોંપાયેલ સ્થાન:</translation>
<translation id="8225771182978767009">આ કમ્પ્યુટરને સેટ કરનાર વ્યક્તિએ આ સાઇટને અવરોધિત કરવાનું પસંદ કર્યું છે.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">એક નવી છુપી વિંડોમાં પેજ ખોલો</translation>
<translation id="8241707690549784388">તમારા દ્વારા દાખલ કરાયેલી વપરાયેલી માહિતી માટે આ પાનું તમે જોઈ રહ્યા છો. તે પૃષ્ઠ પર પાછા જવાથી એવી કોઈપણ ક્રિયા ફરીથી થઈ શકે છે જે તમે પહેલા કરી હતી. શું તમે ચાલુ રાખવા માંગો છો?</translation>
+<translation id="8241712895048303527">આ સાઇટ પર અવરોધિત કરો</translation>
<translation id="8249320324621329438">છેલ્લું આનયન:</translation>
<translation id="8253091569723639551">બિલિંગ સરનામું આવશ્યક</translation>
<translation id="8261506727792406068">કાઢી નાખો</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">નેટવર્ક કનેક્શનમાં સમસ્યાને કારણે ભાષાંતર નિષ્ફળ રહ્યું.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ની ઍક્સેસ નકારાઈ હતી</translation>
<translation id="834457929814110454">જો તમે તમારી સુરક્ષાના જોખમોને સમજો છો, તો તમે જોખમી પ્રોગ્રામ્સ દૂર કરી દેવામાં આવે તે પહેલાં <ph name="BEGIN_LINK" />આ સાઇટની મુલાકાત<ph name="END_LINK" /> લઈ શકો છો.</translation>
-<translation id="8344669043927012510">છુપા મોડમાં પૃષ્ઠ ખોલો (⇧⌘N)</translation>
<translation id="8349305172487531364">બુકમાર્ક્સ બાર</translation>
<translation id="8363502534493474904">એરપ્લેન મોડ બંધ કરીને</translation>
<translation id="8364627913115013041">સેટ નથી.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207"><ph name="UPLOAD_TIME" /> એ ક્રેશ રિપોર્ટ અપલોડ કર્યો હતો</translation>
<translation id="8412145213513410671">ક્રેશેસ (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">તમારે તે જ પાસફ્રેઝ બે વાર દાખલ કરવો આવશ્યક છે.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">સેટિંગ્સ</translation>
<translation id="8433057134996913067">આ તમને મોટાભાગની વેબસાઇટ્સમાંથી સાઇન આઉટ કરશે.</translation>
<translation id="8437238597147034694">&amp;ખસેડવું પૂર્વવત્‌ કરો</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">તમારા Google એકાઉન્ટમાંથી કાર્ડ્સનો ઉપયોગ કરવા માટે, Chrome માં સાઇન ઇન કરો</translation>
<translation id="8488350697529856933">આમને લાગુ</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> એ પ્રતિસાદ આપવા માટે ઘણો સમય લીધો.</translation>
-<translation id="8532105204136943229">સમય સમાપ્તિ વર્ષ</translation>
+<translation id="8503813439785031346">વપરાશકર્તાનામ</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="8571890674111243710">પૃષ્ઠને <ph name="LANGUAGE" /> માં અનુવાદિત કરી રહ્યું છે...</translation>
<translation id="858637041960032120">ફોન નંબર ઉમેરો</translation>
<translation id="859285277496340001">પ્રમાણપત્રને રદ કરવામાં આવ્યું છે કે નહિ તે તપાસવા માટે કોઈપણ મેકેનિઝમ નિર્દિષ્ટ કરતું નથી.</translation>
<translation id="8620436878122366504">તમારા માતાપિતાએ તેને હજી સુધી મંજૂર કરેલ નથી</translation>
<translation id="8647750283161643317">બધાને ડિફોલ્ટ પર ફરીથી સેટ કરો</translation>
+<translation id="8660471606262461360">Google Payments તરફથી</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> સાથેનું તમારું કનેક્શન એન્ક્રિપ્ટેડ નથી.</translation>
<translation id="8718314106902482036">ચુકવણી પૂર્ણ થઈ નથી</translation>
<translation id="8725066075913043281">ફરી પ્રયાસ કરો</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">ઝૂમ વધારો</translation>
<translation id="8931333241327730545">શું તમે આ કાર્ડને તમારા Google એકાઉન્ટમાં સાચવવા માગો છો?</translation>
<translation id="8932102934695377596">તમારી ઘડિયાળ પાછળ છે</translation>
+<translation id="8938939909778640821">સ્વીકૃત ક્રેડિટ અને પ્રીપેઇડ કાર્ડ</translation>
<translation id="8971063699422889582">સર્વરના પ્રમાણપત્રની સમયસીમા સમાપ્ત થઈ છે.</translation>
<translation id="8986494364107987395">ઉપયોગિતા આંકડાઓ અને ક્રેશ રિપોર્ટ્સ Google ને આપમેળે મોકલો</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">હંમેશા આ સાઇટ પર અવરોધિત કરો</translation>
<translation id="9170848237812810038">&amp;પૂર્વવત્ કરો</translation>
<translation id="917450738466192189">સર્વરનું પ્રમાણપત્ર અમાન્ય છે.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> વધુ}one{<ph name="SHIPPING_OPTION_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> વધુ}other{<ph name="SHIPPING_OPTION_PREVIEW" /> અને <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> વધુ}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" />, એક અસમર્થિત પ્રોટોકોલનો ઉપયોગ કરે છે.</translation>
<translation id="9205078245616868884">તમારો ડેટા તમારા સમન્વયન પાસફ્રેઝ સાથે એન્ક્રિપ્ટ કરવામાં આવ્યો છે. સમન્વયન શરૂ કરવા માટે તે દાખલ કરો.</translation>
<translation id="9207861905230894330">લેખ ઉમેરવામાં નિષ્ફળ થયાં.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">ડાઇનર્સ ક્લબ</translation>
<translation id="935608979562296692">ફોર્મ સાફ કરો</translation>
<translation id="939736085109172342">નવું ફોલ્ડર</translation>
-<translation id="941721044073577244">એવું લાગે છે કે તમને આ સાઇટની મુલાકાત લેવા માટેની પરવાનગી નથી</translation>
<translation id="969892804517981540">આધિકારિક બિલ્ડ</translation>
<translation id="975560348586398090">{COUNT,plural, =0{કોઈ નહીં}=1{1 આઇટમ}one{# આઇટમ}other{# આઇટમ}}</translation>
+<translation id="981121421437150478">ઑફલાઇન</translation>
<translation id="988159990683914416">વિકાસકર્તા બિલ્ડ</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_hi.xtb b/chromium/components/strings/components_strings_hi.xtb
index f44ffd1e4c7..d04ed52c226 100644
--- a/chromium/components/strings/components_strings_hi.xtb
+++ b/chromium/components/strings/components_strings_hi.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">दूूूूसरे ऐप्लिकेशन बंद करें</translation>
<translation id="1055184225775184556">&amp;जोड़ना वापस लाएं</translation>
<translation id="10614374240317010">कभी नहीं सहेजा गया</translation>
+<translation id="1066396345355680611">आप <ph name="SITE" /> से और कुछ अन्य साइटों से सुरक्षित सामग्री का एक्सेस खो सकते हैं.</translation>
<translation id="106701514854093668">डेस्कटॉप बुकमार्क</translation>
<translation id="1074497978438210769">सुरक्षित नहीं है</translation>
<translation id="1080116354587839789">चौड़ाई में फ़िट करें</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">पठन सूची</translation>
<translation id="1264126396475825575">ख़राबी रिपोर्ट <ph name="CRASH_TIME" /> पर कैप्चर की गई (अभी तक अपलोड नहीं की गई या उसे अनदेखा किया गया)</translation>
<translation id="1281526147609854549"><ph name="ISSUER" /> ने जारी किया है</translation>
-<translation id="1283919782143846010">खतरनाक सामग्री ब्लॉक की गई</translation>
<translation id="1285320974508926690">कभी भी इस साइट का अनुवाद न करें</translation>
<translation id="129553762522093515">हाल ही में बंद किए गए</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />अपनी कुकी साफ़ करके देखें<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">नामांकन डोमेन:</translation>
<translation id="1340482604681802745">पिकअप पता</translation>
-<translation id="1344211575059133124">ऐसा लगता है कि आपको इस साइट पर जाने की अनुमति लेनी होगी</translation>
<translation id="1344588688991793829">क्रोमियम ऑटोमैटिक भरने वाली सेटिंग...</translation>
<translation id="1348198688976932919">आगे आने वाली साइट में खतरनाक ऐप्लिकेशन हैं</translation>
<translation id="1374468813861204354">सुझाव</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="ORGANIZATION" /> की पहचान <ph name="LOCALITY" /> में <ph name="ISSUER" /> द्वारा सत्यापित की गई है.</translation>
<translation id="1426410128494586442">हां</translation>
<translation id="1430915738399379752">प्रिंट करें</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> अन्य}one{<ph name="PAYMENT_METHOD_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> अन्य}other{<ph name="PAYMENT_METHOD_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> अन्य}}</translation>
<translation id="1506687042165942984">इस पृष्‍ठ की सहेजी गई (अर्थात जिसे पुराना माना जाता है) कॉपी दिखाएं.</translation>
<translation id="1517433312004943670">फ़ोन नंबर आवश्यक है</translation>
+<translation id="1517500485252541695">स्वीकृत क्रेडिट और डेबिट कार्ड</translation>
<translation id="1519264250979466059">बिल्ड दिनांक</translation>
+<translation id="1527263332363067270">कनेक्‍शन के इंतज़ार में…</translation>
<translation id="153384715582417236">अभी के लिए हो गया</translation>
<translation id="1549470594296187301">इस सुविधा का उपयोग करने के लिए JavaScript को सक्षम किया जाना चाहिए.</translation>
<translation id="1555130319947370107">नीला</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">सिस्टम व्यवस्थापक से संपर्क करने का प्रयास करें.</translation>
<translation id="1740951997222943430">खत्म होने का मान्य महीना डालें</translation>
-<translation id="1745358365027406341">पेज को बाद में डाउनलोड करें</translation>
<translation id="17513872634828108">टैब खोलें</translation>
<translation id="1753706481035618306">पृष्‍ठ संख्‍या</translation>
<translation id="1763864636252898013">यह सर्वर यह नहीं प्रमाणित कर सका कि यह <ph name="DOMAIN" /> है; इसका सुरक्षा प्रमाणपत्र आपके डिवाइस के ऑपरेटिंग सिस्टम द्वारा विश्वसनीय नहीं है. ऐसा गलत कॉन्फ़िगरेशन या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">आवश्यक फ़ील्ड</translation>
<translation id="187918866476621466">शुरुआती पन्ना खोलें</translation>
<translation id="1883255238294161206">सूची संक्षिप्त करें</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> अन्य}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> अन्य}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> अन्य}}</translation>
<translation id="1898423065542865115">फ़िल्टर किया जा रहा है</translation>
+<translation id="1916770123977586577">इस साइट पर अपनी अपडेट की गई सेटिंग लागू करने के लिए, इस पेज को फिर से लोड करें</translation>
+<translation id="1919345977826869612">विज्ञापन</translation>
<translation id="192020519938775529">{COUNT,plural, =0{कुछ नहीं}=1{1 साइट}one{# साइट}other{# साइट}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> पर जाएं</translation>
+<translation id="1948773908305951926">स्वीकृत प्रीपेड कार्ड</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> बुकमार्क</translation>
<translation id="1973335181906896915">क्रमबद्ध करने में गड़बड़ी</translation>
<translation id="1974060860693918893">उन्नत</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP गड़बड़ी</translation>
<translation id="2270484714375784793">फ़ोन नंबर</translation>
<translation id="2282872951544483773">अनुपलब्ध प्रयोग</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> आइटम}one{<ph name="ITEM_COUNT" /> आइटम}other{<ph name="ITEM_COUNT" /> आइटम}}</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>
<translation id="2337852623177822836">वह सेटिंग जिसे आपका व्यवस्थापक नियंत्रित करता है</translation>
<translation id="2354001756790975382">अन्य बुकमार्क</translation>
<translation id="2354430244986887761">Google सुरक्षित ब्राउज़िंग को हाल में <ph name="SITE" /> पर <ph name="BEGIN_LINK" />नुकसान पहुंचाने वाले ऐप्लिकेशन मिले हैं<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">जारी रखें</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> पर कैप्चर की गई ख़राबी रिपोर्ट अपलोड नहीं की गई</translation>
<translation id="2367567093518048410">स्तर</translation>
-<translation id="237718015863234333">कोई UI विकल्प उपलब्ध नहीं है</translation>
<translation id="2384307209577226199">एंटरप्राइज़ डिफ़ॉल्ट</translation>
<translation id="2386255080630008482">सर्वर का प्रमाणपत्र निरस्त कर दिया गया है.</translation>
<translation id="2392959068659972793">कोई भी मान सेट नहीं की गई नीतियां दिखाएं</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">यदि चेक किया गया हो, तो अधिक तेज़ी से फ़ॉर्म भरने के लिए क्रोमियम इस डिवाइस पर आपके कार्ड की कॉपी संग्रहित करेगा.</translation>
<translation id="2498091847651709837">नया कार्ड स्‍कैन करें</translation>
<translation id="2501278716633472235">वापस जाएं</translation>
+<translation id="2503184589641749290">स्वीकृत डेबिट और प्रीपेड कार्ड</translation>
<translation id="2515629240566999685">अपने क्षेत्र में सिग्नल की जांच करें</translation>
-<translation id="2516305470678292029">UI विकल्प</translation>
<translation id="2539524384386349900">पता लगाएं</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> ने एक अमान्य प्रतिसाद भेजा है.</translation>
<translation id="2556876185419854533">&amp;संपादन वापस लाएं</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">शिपिंग का तरीका</translation>
<translation id="277499241957683684">डिवाइस का रिकॉर्ड लापता है</translation>
<translation id="2784949926578158345">कनेक्‍शन रीसेट किया गया था.</translation>
+<translation id="2788784517760473862">स्वीकृत क्रेडिट कार्ड</translation>
<translation id="2794233252405721443">साइट अवरोधित है</translation>
<translation id="2799020568854403057">आगे आने वाली साइट में नुकसान पहुंचाने वाले ऐप्लिकेशन हैं</translation>
<translation id="2803306138276472711">Google सुरक्षित ब्राउज़िंग को <ph name="SITE" /> पर हाल ही में <ph name="BEGIN_LINK" />मैलवेयर का पता चला<ph name="END_LINK" /> है. आमतौर पर सुरक्षित रहने वाली वेबसाइटें कभी-कभी मैलवेयर से संक्रमित हो जाती हैं.</translation>
<translation id="2824775600643448204">पता और खोज बार</translation>
<translation id="2826760142808435982">कनेक्शन को <ph name="CIPHER" /> का उपयोग करके एन्क्रिप्ट और प्रमाणित किया गया है और यह कुंजी विनिमय तकनीक के रूप में <ph name="KX" /> का उपयोग करता है.</translation>
<translation id="2835170189407361413">फ़ॉर्म साफ़ करें</translation>
+<translation id="2851634818064021665">आपको इस साइट पर जाने की अनुमति लेनी होगी</translation>
<translation id="2856444702002559011">हो सकता है कि हमलावर <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> से आपकी जानकारी (उदाहरण के लिए, पासवर्ड, संदेश या क्रेडिट कार्ड) चुराने की कोशिश कर रहे हों. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">पुन: लोड ना करें</translation>
-<translation id="2900469785430194048">यह वेबपेज दिखाते समय Google Chrome में जगह नहीं बची.</translation>
<translation id="2909946352844186028">नेटवर्क में बदलाव का पता चला.</translation>
<translation id="2916038427272391327">दूसरे प्रोग्राम बंद करें</translation>
<translation id="2922350208395188000">सर्वर प्रमाणपत्र की जांच नहीं की जा सकती.</translation>
<translation id="2928905813689894207">बिलिंग पता</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> अन्य}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> अन्य}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> अन्य}}</translation>
<translation id="2941952326391522266">यह सर्वर यह प्रमाणित नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसका सुरक्षा प्रमाणपत्र <ph name="DOMAIN2" /> की ओर से है. ऐसा गलत कॉन्फ़िगरेशन के कारण या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
<translation id="2948083400971632585">आप किसी कनेक्शन के लिए कॉन्फ़िगर की गई किसी भी प्रॉक्सी को सेटिंग पेज से अक्षम कर सकते हैं.</translation>
<translation id="2955913368246107853">खोज बार बंद करें</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">यदि सक्षम किया गया हो, तो अधिक तेज़ी से फ़ॉर्म भरने के लिए क्रोमियम इस डिवाइस पर आपके कार्ड की एक कॉपी संग्रहित करेगा.</translation>
<translation id="2985398929374701810">मान्य पता डालें</translation>
<translation id="2986368408720340940">पिकअप का यह तरीका उपलब्ध नहीं है. कोई दूसरा तरीका आज़माएं.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">आपके द्वारा गुप्त टैब में देखे जाने वाले पेज, आपके द्वारा अपने सभी गुप्त टैब बंद कर देने के बाद आपके ब्राउज़र के इतिहास, कुकी संग्रह, या खोज इतिहास में नहीं रहेंगे. आपके द्वारा डाउनलोड की गईं सभी फ़ाइलें या बनाए गए बुकमार्क रख लिए जाएंगे.</translation>
<translation id="3169472444629675720">तलाश करें</translation>
<translation id="3174168572213147020">द्वीप</translation>
-<translation id="317583078218509884">पेज को पुन: लोड करने के बाद नई साइट अनुमतियां सेटिंग प्रभावी हो जाएंगी.</translation>
<translation id="3176929007561373547">यह सुनिश्चित करने के लिए कि प्रॉक्सी सर्वर काम कर रहा है,
अपनी प्रॉक्सी सेटिंग जांचें या अपने नेटवर्क व्यवस्थापक से संपर्क करें. यदि आपको विश्वास नहीं हो
कि आप किसी प्रॉक्सी सर्वर का उपयोग कर रहे हैं, तो:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">पेज को गुप्त मोड में खोलें</translation>
<translation id="320323717674993345">भुगतान रद्द करें</translation>
<translation id="3207960819495026254">बुकमार्क किया गया</translation>
<translation id="3225919329040284222">सर्वर द्वारा कोई प्रमाणपत्र प्रस्‍तुत किया गया, जो बिल्‍ट-इन अपेक्षाओं से मिलान नहीं करता. इन अपेक्षाओं को आपकी सुरक्षा करने के लिए कुछ, उच्‍च-सुरक्षा वेबसाइटों के लिए शामिल किया गया है.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;पुन: क्रमित करना वापस लाएं</translation>
<translation id="3282497668470633863">कार्ड पर नाम जोड़ें</translation>
<translation id="3286538390144397061">अभी फिर से प्रारंभ करें</translation>
+<translation id="3287510313208355388">ऑनलाइन होने पर डाउनलोड करें</translation>
<translation id="3303855915957856445">कोई खोज परिणाम नहीं मिला</translation>
<translation id="3305707030755673451">आपका डेटा आपके समन्वयन पासफ़्रेज़ के साथ <ph name="TIME" /> को एन्क्रिप्ट किया गया था. समन्वयन शुरू करने के लिए इसे डालें.</translation>
<translation id="3320021301628644560">बिलिंग पता जोड़ें</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">प्राप्ति अंतराल:</translation>
<translation id="3462200631372590220">उन्नत को छिपाएं</translation>
<translation id="3467763166455606212">कार्ड मालिक का नाम ज़रूरी है</translation>
-<translation id="3478058380795961209">समाप्ति माह</translation>
<translation id="3479539252931486093">क्या यह अनपेक्षित था? <ph name="BEGIN_LINK" />हमें बताएं<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">अभी नहीं</translation>
<translation id="3498215018399854026">हम इस समय आपके अभिभावक तक नहीं पहुंच पा रहे हैं. कृपया पुन: प्रयास करें.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;कृपया &lt;strong&gt;सेटिंग&lt;/strong&gt; ऐप्लिकेशन के &lt;strong&gt;सामान्य&lt;/strong&gt; अनुभाग से दिनांक और समय एडजस्ट करें.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">क्रेडिट और प्रीपेड कार्ड स्वीकार किए जाते हैं.</translation>
<translation id="3582930987043644930">नाम जोड़ें</translation>
<translation id="3583757800736429874">&amp;ले जाना फिर से करें</translation>
<translation id="3586931643579894722">विवरण छुपाएं</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">यदि आपको यह बार-बार दिखाई दे रहा हो, तो इन <ph name="HELP_LINK" /> को आज़माएं.</translation>
<translation id="3658742229777143148">पुनरीक्षण</translation>
<translation id="3678029195006412963">अनुरोध पर हस्ताक्षर नहीं किया जा सका</translation>
+<translation id="3678529606614285348">पेज को नई गुप्त विंडो में खोलें (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">ख़राबी रिपोर्ट <ph name="CRASH_TIME" /> पर कैप्चर की गई, <ph name="UPLOAD_TIME" /> पर अपलोड की गई</translation>
<translation id="3681007416295224113">प्रमाणपत्र जानकारी</translation>
<translation id="3690164694835360974">लॉगिन सुरक्षित नहीं है</translation>
-<translation id="3693415264595406141">पासवर्ड:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">लोड हो रही हैं...</translation>
<translation id="3712624925041724820">लाइसेंस समाप्त हो गए</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805"><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="375403751935624634">सर्वर गड़बड़ी के कारण अनुवाद विफल.</translation>
<translation id="3759461132968374835">आपके पास हाल ही में रिपोर्ट किए गए क्रैश नहीं हैं. क्रैश रिपोर्टिंग अक्षम होने के दौरान होने वाले क्रैश यहां दिखाई नहीं देंगे.</translation>
+<translation id="3765032636089507299">सुरक्षित ब्राउज़िंग पेज अभी बन रहा है.</translation>
<translation id="3778403066972421603">क्या आप इस कार्ड को अपने Google खाते में और इस डिवाइस पर सहेजना चाहते हैं?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /> में खत्म होगा</translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">यह साइट एक्सेस करने का आपका अनुरोध <ph name="NAME" /> को भेज दिया गया है</translation>
<translation id="3890664840433101773">ईमेल जोड़ें</translation>
<translation id="3901925938762663762">इस कार्ड की समय सीमा समाप्‍त हो गई है</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">अपलोड की गई ख़राबी रिपोर्ट आईडी <ph name="CRASH_ID" /> (स्थानीय क्रैश आईडी: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">यह सर्वर प्रमाणित नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसके सुरक्षा प्रमाणपत्र में विषय के वैकल्पिक नाम नहीं बताए गए हैं. ऐसा गलत कॉन्फ़िगरेशन के कारण या किसी आक्रमणकर्ता की ओर से आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
+<translation id="3949601375789751990">आपका ब्राउज़िंग इतिहास यहां दिखाई देता है</translation>
<translation id="3963721102035795474">रीडर मोड</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{कुछ नहीं}=1{1 साइट से }one{# साइटों से }other{# साइटों से }}</translation>
<translation id="397105322502079400">गणना की जा रही है...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">साइट सेटिंग</translation>
<translation id="4169947484918424451">क्या आप चाहते हैं कि क्रोमियम इस कार्ड को सहेजे?</translation>
<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="4196861286325780578">&amp;ले जाना फिर से करें</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />फायरवॉल और एंटीवायरस कॉन्फ़िगरेशन की जांच करें<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">क्रैश</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">इस साइट पर मौजूद हमलावर आपके कंप्यूटर पर ऐसे खतरनाक प्रोग्राम इंस्टॉल करने की कोशिश कर सकते हैं जो आपकी जानकारी (उदाहरण के लिए, फ़ोटो, पासवर्ड, संदेश और क्रेडिट कार्ड) चुरा लेते हैं या उसे हटा देते हैं.</translation>
<translation id="4372948949327679948">अपेक्षित <ph name="VALUE_TYPE" /> मान.</translation>
<translation id="4377125064752653719">आपने <ph name="DOMAIN" /> तक पहुंचने का प्रयास किया, लेकिन सर्वर द्वारा प्रस्तुत प्रमाणपत्र को उसके जारीकर्ता द्वारा रद्द कर दिया गया है. इसका अर्थ है कि सर्वर द्वारा प्रस्तुत सुरक्षा प्रमाणिकता पर पूर्णतया विश्वास नहीं करना चाहिए. हो सकता है कि आप किसी हमलावर से बातचीत कर रहे हों.</translation>
-<translation id="4381091992796011497">उपयोगकर्ता नाम:</translation>
<translation id="4394049700291259645">अक्षम करें</translation>
<translation id="4406896451731180161">खोज परिणाम</translation>
<translation id="4424024547088906515">यह सर्वर यह नहीं प्रमाणित कर सका कि यह <ph name="DOMAIN" /> है; इसका सुरक्षा प्रमाणपत्र Chrome द्वारा विश्वसनीय नहीं है. ऐसा गलत कॉन्फ़िगरेशन या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> ने आपका लॉगिन प्रमाणपत्र स्वीकार नहीं किया है या हो सकता है कि प्रमाणपत्र उपलब्ध नहीं कराया गया हो.</translation>
<translation id="443673843213245140">प्रॉक्‍सी का उपयोग अक्षम है लेकिन कोई स्‍पष्ट प्रॉक्‍सी कॉन्फ़िगरेशन निर्दिष्ट किया गया है.</translation>
+<translation id="445100540951337728">स्वीकृत डेबिट कार्ड</translation>
<translation id="4506176782989081258">सत्‍यापन गड़बड़ी: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">सिस्टम व्यवस्थापक से संपर्क करें</translation>
<translation id="450710068430902550">व्यवस्थापक के साथ साझा करना</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Chrome से पता निकालें?</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" /> से आपके कनेक्शन को किसी आधुनिक सिफ़र सुइट का उपयोग करके एन्‍क्रिप्‍ट किया गया है.</translation>
<translation id="4594403342090139922">&amp;हटाना वापस लाएं</translation>
+<translation id="4611292653554630842">लॉग इन करें</translation>
<translation id="4619615317237390068">अन्य डिवाइस के टैब</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">यह सर्वर यह प्रमाणित नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसके सुरक्षा प्रमाणपत्र में त्रुटियां हैं. ऐसा गलत कॉन्फ़िगरेशन के कारण या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
<translation id="4690462567478992370">अमान्य प्रमाणपत्र का उपयोग करना बंद करें</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">आपका कनेक्शन बाधित था</translation>
+<translation id="471880041731876836">आपको इस साइट पर जाने की अनुमति नहीं है</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows नेटवर्क निदान चलाकर देखें<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">नीतियां फिर से लोड करें</translation>
<translation id="4728558894243024398">प्लेटफ़ॉर्म</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">देखें</translation>
<translation id="4854362297993841467">वितरण का यह तरीका उपलब्ध नहीं है. कोई दूसरा तरीका आज़माएं.</translation>
<translation id="4858792381671956233">आपने अपने अभिभावकों से पूछा था कि इस साइट पर जाना ठीक है या नहीं</translation>
-<translation id="4863764087567530506">यह सामग्री आपको सॉफ़्टवेयर इंस्‍टॉल करने या व्यक्तिगत जानकारी प्रकट करने के लिए भ्रमित कर सकती है. <ph name="BEGIN_LINK" />फिर भी दिखाएं<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">खोज इतिहास</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">प्रमाणीकरण ज़रूरी है</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{1 और वेब पेज}one{# और वेब पेज}other{# और वेब पेज}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">इस पेज का एक अज्ञात भाषा से <ph name="LANGUAGE_LANGUAGE" /> में अनुवाद किया गया है</translation>
<translation id="4923459931733593730">भुगतान</translation>
<translation id="4926049483395192435">निर्दिष्ट किया जाना चाहिए.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">क्रियाएं</translation>
<translation id="4958444002117714549">सूची विस्तृत करें</translation>
<translation id="4974590756084640048">चेतावनियां फिर से सक्षम करें</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">ग़लत पासवर्ड</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>
<translation id="5087580092889165836">कार्ड जोड़ें</translation>
<translation id="5089810972385038852">राज्य</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">प्रांत</translation>
<translation id="5115563688576182185">(64-बिट)</translation>
<translation id="5141240743006678641">समन्वयित पासवर्ड अपने Google प्रमाणिकता के साथ एन्क्रिप्ट करें</translation>
-<translation id="514421653919133810">पेज को गुप्त मोड में खोलें (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">नीति प्रतिसाद में गड़बड़ी कोड मौजूद है</translation>
+<translation id="5159010409087891077">पेज को नई गुप्त विंडो में खोलें (⇧⌘N)</translation>
<translation id="5171045022955879922">URL खोजें या लिखें</translation>
<translation id="5172758083709347301">मशीन</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> में नहीं है? इस गड़बड़ी की रिपोर्ट करें</translation>
-<translation id="5181140330217080051">डाउनलोड हो रहा है</translation>
<translation id="5190835502935405962">बुकमार्क बार</translation>
<translation id="5199729219167945352">प्रयोग</translation>
<translation id="5205222826937269299">नाम आवश्यक है</translation>
<translation id="5222812217790122047">ईमेल आवश्यक है</translation>
<translation id="5251803541071282808">क्लाउड</translation>
<translation id="5277279256032773186">कार्यस्थल पर Chrome का उपयोग कर रहे हैं? कंपनियां अपने कर्मचारियों के लिए Chrome सेटिंग प्रबंधित कर सकती हैं. और जानें</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />इन चरणों का पालन करके सॉफ़्टवेयर को कुछ समय के लिए अक्षम करें ताकि आप वेब पर जा सकें. आपको व्यावस्थापकीय विशेषाधिकारों की ज़रूरत होगी.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />शुरू करें<ph name="END_BOLD" /> को क्लिक करें, फिर <ph name="BEGIN_BOLD" />"स्थानीय सेवाएं देखें"<ph name="END_BOLD" /> को खोजें और चुनें
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" /> चुनें
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />स्टार्टअप प्रकार<ph name="END_BOLD" /> के अंतर्गत, <ph name="BEGIN_BOLD" />अक्षम<ph name="END_BOLD" /> चुनें
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />सेवा स्थिति<ph name="END_BOLD" /> के अंतर्गत, <ph name="BEGIN_BOLD" />रोकें<ph name="END_BOLD" /> को क्लिक करें
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />लागू करें<ph name="END_BOLD" /> को क्लिक करें, फिर <ph name="BEGIN_BOLD" />ठीक है<ph name="END_BOLD" /> क्लिक करें
+ <ph name="LIST_ITEM" />अपने कंप्यूटर से हमेशा के लिए सॉफ़्टवेयर को निकालने का तरीका जानने के लिए <ph name="BEGIN_LEARN_MORE_LINK" />Chrome सहायता केंद्र<ph name="END_LEARN_MORE_LINK" /> पर जाएं
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">इस साइट से आपका कनेक्शन निजी नहीं है. VR मोड से किसी भी समय बाहर निकलने के लिए, हेडसेट निकालें और वापस जाएं दबाएं.</translation>
<translation id="5299298092464848405">नीति पार्स करने में गड़बड़ी</translation>
<translation id="5308689395849655368">क्रैश की रिपोर्ट करना अक्षम कर दिया गया है.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" पर स्कीमा सत्यापन गड़बड़ी: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">यह <ph name="HOST_NAME" /> पेज प्राप्त नहीं किया जा सकता</translation>
<translation id="5455374756549232013">खराब नीति टाइमस्टैम्प</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" /> में से <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">अमान्य</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> अन्य}one{<ph name="CONTACT_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> अन्य}other{<ph name="CONTACT_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> अन्य}}</translation>
<translation id="5470861586879999274">&amp;संपादित करना फिर से करें</translation>
+<translation id="5481076368049295676">यह सामग्री आपके डिवाइस पर ऐसा खतरनाक सॉफ़्टवेयर इंस्टॉल करने की कोशिश कर सकती है जो आपकी जानकारी चुरा सकता है या उसे हटा सकता है. <ph name="BEGIN_LINK" />फिर भी दिखाएं<ph name="END_LINK" /></translation>
<translation id="54817484435770891">मान्य पता जोड़ें</translation>
<translation id="5492298309214877701">कंपनी, संगठन या विद्यालय के इंट्रानेट पर इस साइट का URL एक बाहरी वेबसाइट जैसा है.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">अनुरोध किया गया लेख नहीं ढूंढा जा सका.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> पर एम्‍बेड किए गए पृष्‍ठ का कहना है:</translation>
<translation id="5556459405103347317">पुन: लोड करें</translation>
+<translation id="5560088892362098740">समयसीमा समाप्ति दिनांक</translation>
<translation id="5565735124758917034">सक्रिय</translation>
<translation id="5571083550517324815">इस पते से पिक अप नहीं किया जा सकता. कोई दूसरा पता चुनें.</translation>
<translation id="5572851009514199876">कृपया Chrome शुरू करके उसमें प्रवेश करें ताकि Chrome देख सके कि क्या आपके पास यह साइट एक्सेस करने की अनुमति है.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">नीति सेटिंग लोड करने में विफल</translation>
<translation id="5631439013527180824">अमान्य डिवाइस प्रबंधन टोकन</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="5646376287012673985">स्थान</translation>
<translation id="5659593005791499971">ईमेल</translation>
<translation id="5669703222995421982">खास आपके लिए बनी सामग्री पाएं</translation>
<translation id="5675650730144413517">यह पेज काम नहीं कर रहा है</translation>
<translation id="5710435578057952990">इस वेबसाइट की पहचान सत्यापित नहीं की गई है.</translation>
-<translation id="5713016350996637505">भ्रामक सामग्री ब्लॉक की गई</translation>
+<translation id="5719499550583120431">प्रीपेड कार्ड स्वीकार किए जाते हैं.</translation>
<translation id="5720705177508910913">वर्तमान उपयोगकर्ता</translation>
<translation id="5732392974455271431">आपके अभिभावक इसे आपके लिए अनवरोधित कर सकते हैं</translation>
<translation id="5763042198335101085">मान्य ईमेल पता डालें</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">क्या आप अपनी <ph name="CARD_DETAIL" /> भरना चाहते हैं?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" /> से आपके कनेक्शन को किसी अप्रचलित सिफ़र सुइट का उपयोग करके एन्‍क्रिप्‍ट किया गया है.</translation>
<translation id="5813119285467412249">&amp;जोड़ना फिर से करें</translation>
-<translation id="5814352347845180253">आप <ph name="SITE" /> और कुछ अन्य साइटों से प्रीमियम सामग्री की एक्सेस खो सकते हैं.</translation>
<translation id="5838278095973806738">आपको इस साइट पर कोई भी संवेदनशील जानकारी (उदाहरण के लिए, पासवर्ड या क्रेडिट कार्ड) नहीं डालनी चाहिए, क्योंकि उसे हमलावर चुरा सकते हैं.</translation>
<translation id="5869405914158311789">इस साइट तक नहीं पहुंचा जा सकता</translation>
<translation id="5869522115854928033">सहेजे गए पासवर्ड</translation>
<translation id="5872918882028971132">अभिभावक सुझाव</translation>
+<translation id="5893752035575986141">क्रेडिट कार्ड स्वीकार किए जाते हैं.</translation>
<translation id="5901630391730855834">पीला</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (सिंक किया गया)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 उपयोग में है}one{# उपयोग में हैं}other{# उपयोग में हैं}}</translation>
-<translation id="5926846154125914413">आप कुछ साइटों से प्रीमियम सामग्री की एक्सेस खो सकते हैं.</translation>
<translation id="5959728338436674663">Google को कुछ <ph name="BEGIN_WHITEPAPER_LINK" />सिस्टम संबंधी जानकारी और पेज सामग्री<ph name="END_WHITEPAPER_LINK" /> अपने आप भेजें ताकि खतरनाक ऐप्लिकेशन और साइटों का पता लगाने में सहायता मिल सके. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">इतिहास से निकालें</translation>
<translation id="5975083100439434680">ज़ूम आउट</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">बंद करें</translation>
<translation id="6042308850641462728">अधिक</translation>
<translation id="6047233362582046994">अगर आप अपनी सुरक्षा में होने वाले जोखिमों को समझते हैं, तो खतरनाक ऐप्लिकेशन निकाले जाने से पहले आप <ph name="BEGIN_LINK" />इस साइट पर जा<ph name="END_LINK" /> सकते हैं.</translation>
+<translation id="6047927260846328439">यह सामग्री आपसे धोखे से सॉफ़्टवेयर इंस्‍टॉल करवाने या व्यक्तिगत जानकारी का खुलासा करवाने की कोशिश कर सकती है. <ph name="BEGIN_LINK" />फिर भी दिखाएं<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">आप इस समय <ph name="SITE" /> पर नहीं जा सकते हैं क्योंकि वेबसाइट प्रमाणपत्र पिनिंग का उपयोग करती है. नेटवर्क की गड़बड़ियां और हमले आमतौर पर कुछ देर के लिए होते हैं, इसलिए यह पेज शायद बाद में ठीक से काम करेगा.</translation>
<translation id="6060685159320643512">सावधान, ये प्रयोग नुकसान पहुंचा सकते हैं</translation>
<translation id="6080696365213338172">आपने व्यवस्थापक द्वारा प्रदत्त प्रमाणपत्र के उपयोग से सामग्री एक्सेस की है. आपके द्वारा <ph name="DOMAIN" /> को प्रदान किया गया डेटा आपके व्यवस्थापक द्वारा बीच में रोका जा सकता है.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">अधिक जानें</translation>
<translation id="6169916984152623906">अब आप निजी रूप से ब्राउज़ कर सकते हैं और इस डिवाइस का उपयोग करने वाले दूसरे लोगों को आपकी गतिविधि दिखाई नहीं देगी. लेकिन डाउनलोड और बुकमार्क सेव होंगे.</translation>
<translation id="6177128806592000436">इस साइट से आपका कनेक्‍शन सुरक्षित नहीं है</translation>
-<translation id="6184817833369986695">(समानता रखने वाले लोग: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">अपना इंटरनेट कनेक्शन जांचें</translation>
<translation id="6218753634732582820">क्रोमियम से पता निकालें?</translation>
<translation id="6221345481584921695">Google सुरक्षित ब्राउज़िंग को <ph name="SITE" /> पर हाल ही में <ph name="BEGIN_LINK" />मैलवेयर का पता चला<ph name="END_LINK" /> है. आमतौर पर सुरक्षित रहने वाली वेबसाइटें कभी-कभी मैलेवयर से संक्रमित हो जाती हैं. दुर्भावनापूर्ण सामग्री <ph name="SUBRESOURCE_HOST" /> से आती है, जो कि एक ज्ञात मैलवेयर वितरक है.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">बेहतरीन नई Chrome सुविधाओं में रूचि है? chrome.com/beta पर हमारा बीटा चैनल आज़माएं.</translation>
-<translation id="6389758589412724634">यह वेबपेज दिखाते समय क्रोमियम में जगह नहीं बची.</translation>
+<translation id="6397451950548600259">आपके कंप्यूटर पर मौजूद सॉफ़्टवेयर Chrome को सुरक्षित रूप से वेब से कनेक्ट होने से रोक रहा है</translation>
<translation id="6404511346730675251">बुकमार्क संपादित करें</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> का समाप्ति दिनांक और CVC डालें</translation>
<translation id="6414888972213066896">आपने अपने अभिभावक से पूछा था कि इस साइट पर जाना ठीक है या नहीं</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">डिवाइस नीतियां</translation>
<translation id="6477321094435799029">Chrome को इस पेज पर असामान्य कोड मिला था और उसने आपकी व्यक्तिगत जानकारी (उदाहरण के लिए, पासवर्ड, फ़ोन नंबर और क्रेडिट कार्ड) की सुरक्षा करने के लिए उसे अवरुद्ध कर दिया है.</translation>
<translation id="6489534406876378309">क्रैश अपलोड करना प्रारंभ करें</translation>
+<translation id="6507833130742554667">क्रेडिट और डेबिट कार्ड स्वीकार किए जाते हैं.</translation>
<translation id="6508722015517270189">Chrome को फिर से शुरू करें</translation>
<translation id="6529602333819889595">&amp;हटाना फिर से करें</translation>
<translation id="6534179046333460208">जीता-जागता वेब के सुझाव</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">यह सामग्री आपके डिवाइस पर ऐसा खतरनाक सॉफ़्टवेयर इंस्टॉल करने की कोशिश कर सकती है जो आपकी जानकारी चुरा सकता है या उसे हटा सकता है. <ph name="BEGIN_LINK" />फिर भी दिखाएं<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">सुरक्षित तरीका विकल्प</translation>
<translation id="662080504995468778">इसपर रहें</translation>
<translation id="6626291197371920147">मान्य कार्ड नंबर जोड़ें</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> खोज</translation>
<translation id="6630809736994426279">इस समय <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर मौजूद हमलावर आपके Mac पर ऐसे खतरनाक प्रोग्राम इंस्टॉल करने की कोशिश कर सकते हैं जो आपकी जानकारी (उदाहरण के लिए, फ़ोटो, पासवर्ड, संदेश और क्रेडिट कार्ड) चुराते हैं या उसे हटा देते हैं. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">यह नीति हटा दी गई है.</translation>
+<translation id="6657585470893396449">पासवर्ड</translation>
<translation id="6671697161687535275">क्रोमियम से फ़ॉर्म सुझाव निकालें?</translation>
<translation id="6685834062052613830">प्रस्थान करें और सेटअप पूरा करें</translation>
<translation id="6710213216561001401">पिछला</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">जिला</translation>
<translation id="6973656660372572881">फ़िक्‍स्‍ड प्रॉक्‍सी सर्वर और .pac स्‍क्रिप्‍ट URL दोनों ही निर्दिष्ट हैं.</translation>
<translation id="6989763994942163495">अतिरिक्त सेटिंग दिखाएं...</translation>
-<translation id="7000990526846637657">कोई इतिहास प्रविष्टि नहीं मिली</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">आपके Google खाते में <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> पर ब्राउज़िंग इतिहास के अन्य रूप उपलब्ध हो सकते हैं</translation>
<translation id="7029809446516969842">पासवर्ड</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">इस पते पर शिप नहीं किया जा सकता. कोई दूसरा पता चुनें.</translation>
<translation id="7138472120740807366">वितरण का तरीका</translation>
<translation id="7139724024395191329">अमीरात</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> अन्य}one{<ph name="PAYMENT_METHOD_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> अन्य}other{<ph name="PAYMENT_METHOD_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> अन्य}}</translation>
<translation id="7155487117670177674">भुगतान सुरक्षित नहीं है</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> अन्य}one{<ph name="SHIPPING_OPTION_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> अन्य}other{<ph name="SHIPPING_OPTION_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> अन्य}}</translation>
<translation id="7179921470347911571">अभी पुन: लॉन्‍च करें</translation>
<translation id="7180611975245234373">रीफ्रेश करें</translation>
<translation id="7182878459783632708">कोई नीति सेट नहीं की गई है</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">डाउनलोड करें</translation>
<translation id="7518003948725431193">इस वेब पते के लिए कोई वेब पेज नहीं मिला था: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">इस साइट से आपका कनेक्‍शन निजी नहीं है</translation>
<translation id="7535087603100972091">मान</translation>
<translation id="7537536606612762813">आवश्यक</translation>
<translation id="7542403920425041731">आपकी तरफ से पुष्टि हो जाने पर, आपके कार्ड के विवरण इस साइट के साथ साझा किए जाएंगे.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">ये टिप्स आज़माएं:</translation>
<translation id="7554791636758816595">नया टैब</translation>
<translation id="7567204685887185387">यह सर्वर यह प्रमाणित नहीं कर सका कि यह <ph name="DOMAIN" /> है; हो सकता है इसका सुरक्षा प्रमाणपत्र धोखे से जारी किया गया हो. ऐसा गलत कॉन्फ़िगरेशन के कारण या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> अन्य}one{<ph name="CONTACT_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> अन्य}other{<ph name="CONTACT_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> अन्य}}</translation>
<translation id="7568593326407688803">यह पृष्ठ<ph name="ORIGINAL_LANGUAGE" />में है क्या आप इसका अनुवाद करना चाहेंगे?</translation>
<translation id="7569952961197462199">Chrome से क्रेडिट कार्ड निकालें?</translation>
<translation id="7569983096843329377">काला</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">प्रमाणपत्र</translation>
<translation id="7716147886133743102">आपके व्यवस्थापक की ओर से अवरोधित है</translation>
<translation id="7716424297397655342">इस साइट को संचय से लोड नहीं किया जा सकता</translation>
+<translation id="774634243536837715">खतरनाक सामग्री ब्लॉक की गई.</translation>
<translation id="7752995774971033316">अप्रबंधित</translation>
<translation id="7755287808199759310">आपका अभिभावक इसे आपके लिए अनवरोधित कर सकता है</translation>
<translation id="7758069387465995638">हो सकता है कि फायरवॉल या एंटीवायरस द्वारा कनेक्शन अवरुद्ध हो.</translation>
+<translation id="7759163816903619567">डोमेन प्रदर्शित करें:</translation>
<translation id="7761701407923456692">सर्वर का प्रमाणपत्र URL से मेल नहीं हो रहा है.</translation>
<translation id="7763386264682878361">भुगतान मेनिफ़ेस्ट पार्सर</translation>
<translation id="7764225426217299476">पता जोड़ें</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />' के लिए <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> मिले</translation>
<translation id="785549533363645510">हालांकि, आप अदृश्य नहीं हैं. गुप्त मोड में रहने से आपकी ब्राउज़िंग आपके नियोक्ता, आपके इंटरनेट सेवा प्रदाता या आपके द्वारा देखी जाने वाली वेबसाइट से छिपती नहीं है.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">डेबिट और प्रीपेड कार्ड स्वीकार किए जाते हैं.</translation>
<translation id="7887683347370398519">अपना CVC जांचें और पुन: प्रयास करें</translation>
<translation id="79338296614623784">मान्य फ़ोन नंबर डालें</translation>
<translation id="7935318582918952113">DOM डिस्टिलर</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">सुझाव भेजें</translation>
<translation id="8041940743680923270">वैश्विक डिफ़ॉल्ट का उपयोग करें (पूछें)</translation>
<translation id="8088680233425245692">लेख देखने में विफल रहा.</translation>
-<translation id="8089520772729574115">1 MB से कम</translation>
<translation id="8091372947890762290">सर्वर पर सक्रियण लंबित है</translation>
<translation id="8118489163946903409">भुगतान विधि</translation>
<translation id="8131740175452115882">दुबारा पूछें</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;ले जाना वापस लाएं</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" आईडी वाले एक्‍सटेंशन का अमान्‍य अपडेट URL.</translation>
<translation id="8202097416529803614">आदेश सारांश</translation>
+<translation id="8205463626947051446">साइट तंग करने वाले विज्ञापन दिखाने के लिए जानी जाती है</translation>
<translation id="8218327578424803826">सौंपा गया स्‍थान:</translation>
<translation id="8225771182978767009">जिस व्यक्ति ने इस कंप्यूटर को सेट किया है, उसने इस साइट को अवरुद्ध करना चुना है.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">पेज को नए गुप्त टैब में खोलें</translation>
<translation id="8241707690549784388">आपके द्वारा खोजे जा रहे पेज ने आपके द्वारा प्रविष्ट की गई जानकारी का उपयोग किया है. उस पेज पर वापस जाने से आपके द्वारा की गई किसी क्रिया को दोहराने की आवश्यकता हो सकती है. क्या आप जारी रखना चाहते हैं?</translation>
+<translation id="8241712895048303527">इस साइट पर ब्लॉक करें</translation>
<translation id="8249320324621329438">पिछली बार प्राप्त किया गया:</translation>
<translation id="8253091569723639551">बिलिंग पता आवश्यक</translation>
<translation id="8261506727792406068">हटाएं</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">नेटवर्क कनेक्शन में कोई समस्या होने के कारण अनुवाद विफल हुआ.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> का एक्सेस अस्वीकृत किया गया</translation>
<translation id="834457929814110454">यदि आप अपनी सुरक्षा में होने वाले जोखिमों को समझते हैं, तो खतरनाक प्रोग्राम निकाले जाने से पहले आप <ph name="BEGIN_LINK" />इस साइट पर विज़िट<ph name="END_LINK" /> कर सकते हैं.</translation>
-<translation id="8344669043927012510">पेज को गुप्त मोड में खोलें (⇧⌘N)</translation>
<translation id="8349305172487531364">बुकमार्क बार</translation>
<translation id="8363502534493474904">हवाई जहाज़ मोड बंद करें</translation>
<translation id="8364627913115013041">सेट नहीं है.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">ख़राबी रिपोर्ट <ph name="UPLOAD_TIME" /> पर अपलोड की गई</translation>
<translation id="8412145213513410671">क्रैश (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">आपको वही पासफ़्रेज़ दोबारा दर्ज करना होगा.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">सेटिंग्स</translation>
<translation id="8433057134996913067">इससे आप अधिकांश वेबसाइट से प्रस्थान कर जाएंगे.</translation>
<translation id="8437238597147034694">&amp;ले जाना वापस लाएं</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">कार्ड का उपयोग अपने Google खाते से करने के लिए, Chrome में प्रवेश करें</translation>
<translation id="8488350697529856933">इस पर लागू होती है</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ने प्रतिसाद देने में अत्यधिक समय लिया.</translation>
-<translation id="8532105204136943229">समाप्‍ति वर्ष</translation>
+<translation id="8503813439785031346">उपयोगकर्ता नाम</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="8571890674111243710">पेज का अनुवाद <ph name="LANGUAGE" /> में कर रहा है...</translation>
<translation id="858637041960032120">फ़ोन नंबर जोड़ें
@@ -887,6 +919,7 @@
<translation id="859285277496340001">प्रमाणपत्र यह जांचने के लिए कोई तंत्र निर्दिष्‍ट नहीं करता कि इसे रद्द कर दिया गया है या नहीं.</translation>
<translation id="8620436878122366504">आपके अभिभावकों ने अभी तक इसकी स्वीकृति नहीं दी है</translation>
<translation id="8647750283161643317">सभी को डिफ़ॉल्ट पर रीसेट करें</translation>
+<translation id="8660471606262461360">Google पेमेंट्स से</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> से आपके कनेक्शन को एन्क्रिप्ट नहीं किया गया है.</translation>
<translation id="8718314106902482036">भुगतान पूरा नहीं हुआ</translation>
<translation id="8725066075913043281">पुन: प्रयास करें</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">ज़ूम इन करें</translation>
<translation id="8931333241327730545">क्या आप इस कार्ड को अपने Google खाते में सहेजना चाहते हैं?</translation>
<translation id="8932102934695377596">आपकी घड़ी पीछे है</translation>
+<translation id="8938939909778640821">स्वीकृत क्रेडिट और प्रीपेड कार्ड</translation>
<translation id="8971063699422889582">सर्वर के प्रमाणपत्र की समय-सीमा समाप्त हो चुकी है.</translation>
<translation id="8986494364107987395">उपयोग संबंधी आंकड़े और क्रैश रिपोर्ट अपने आप Google को भेजने की अनुमति दें</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">इस साइट पर हमेशा अवरोधित करें</translation>
<translation id="9170848237812810038">&amp;पूर्ववत् करें</translation>
<translation id="917450738466192189">सर्वर का प्रमाणपत्र अमान्य है.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> अन्य}one{<ph name="SHIPPING_OPTION_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> अन्य}other{<ph name="SHIPPING_OPTION_PREVIEW" /> और <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> अन्य}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> एक असमर्थित प्रोटोकॉल का उपयोग करता है.</translation>
<translation id="9205078245616868884">आपका डेटा आपके समन्वयन पासफ़्रेज़ के साथ एन्क्रिप्ट किया गया है. समन्वयन शुरू करने के लिए इसे डालें.</translation>
<translation id="9207861905230894330">लेख जोड़ने में विफल रहा.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">फ़ॉर्म साफ़ करें</translation>
<translation id="939736085109172342">नया फ़ोल्डर</translation>
-<translation id="941721044073577244">ऐसा लगता है कि आपको इस साइट पर जाने की अनुमति नहीं है</translation>
<translation id="969892804517981540">आधिकारिक बिल्ड</translation>
<translation id="975560348586398090">{COUNT,plural, =0{कुछ नहीं}=1{1 आइटम}one{# आइटम}other{# आइटम}}</translation>
+<translation id="981121421437150478">ऑफ़लाइन</translation>
<translation id="988159990683914416">डेवलपर बिल्ड</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_hr.xtb b/chromium/components/strings/components_strings_hr.xtb
index bf989497dc9..36438525a71 100644
--- a/chromium/components/strings/components_strings_hr.xtb
+++ b/chromium/components/strings/components_strings_hr.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Zatvorite ostale aplikacije</translation>
<translation id="1055184225775184556">&amp;Poništi dodavanje</translation>
<translation id="10614374240317010">Zaporke se nikad ne spremaju</translation>
+<translation id="1066396345355680611">Možda ćete izgubiti pristup zaštićenom sadržaju s web-lokacije <ph name="SITE" /> i nekih drugih web-lokacija.</translation>
<translation id="106701514854093668">Oznake radne površine</translation>
<translation id="1074497978438210769">Nije sigurno</translation>
<translation id="1080116354587839789">Prilagođavanje širini</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Popis za čitanje</translation>
<translation id="1264126396475825575">Izvješća o rušenju programa generirana <ph name="CRASH_TIME" /> (još nisu prenesena ili zanemarena)</translation>
<translation id="1281526147609854549">Izdavač: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Blokiran je opasan sadržaj</translation>
<translation id="1285320974508926690">Nikad nemoj prevoditi ovu web-lokaciju</translation>
<translation id="129553762522093515">Nedavno zatvoreno</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Izbrišite kolačiće<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domena upisa:</translation>
<translation id="1340482604681802745">Adresa preuzimanja</translation>
-<translation id="1344211575059133124">Izgleda da ti je za posjet toj web-lokaciji potrebno dopuštenje</translation>
<translation id="1344588688991793829">Postavke automatskog popunjavanja u Chromiumu...</translation>
<translation id="1348198688976932919">Sljedeća web-lokacija sadrži opasne aplikacije</translation>
<translation id="1374468813861204354">prijedlozi</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identitet za <ph name="ORGANIZATION" />na <ph name="LOCALITY" /> ovjerio je <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Da</translation>
<translation id="1430915738399379752">Ispis</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Prikaži spremljenu kopiju te stranice (tj., onu za koju se zna da je zastarjela).</translation>
<translation id="1517433312004943670">Telefonski je broj obavezan</translation>
+<translation id="1517500485252541695">Prihvaćene kreditne i debitne kartice</translation>
<translation id="1519264250979466059">Datum međuverzije</translation>
+<translation id="1527263332363067270">Čekanje na uspostavu veze…</translation>
<translation id="153384715582417236">To je zasad sve</translation>
<translation id="1549470594296187301">Za upotrebu te značajke mora biti omogućen JavaScript.</translation>
<translation id="1555130319947370107">Plava</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Preuzmi stranicu kasnije</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Obavezno polje</translation>
<translation id="187918866476621466">Otvori polazne stranice</translation>
<translation id="1883255238294161206">Sažmi popis</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtriranje</translation>
+<translation id="1916770123977586577">Ponovo učitajte stranicu da bi se ažurirane postavke primijenile na ovu web-lokaciju</translation>
+<translation id="1919345977826869612">Oglasi</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Nijedna}=1{1 web-lokacija}one{# web-lokacija}few{# web-lokacije}other{# web-lokacija}}</translation>
<translation id="194030505837763158">Posjetite <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Prihvaćene pretplatne kartice</translation>
<translation id="1962204205936693436">Oznake s domene <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Pogreška postavljanja u seriju</translation>
<translation id="1974060860693918893">Napredno</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP pogreška</translation>
<translation id="2270484714375784793">Telefonski broj</translation>
<translation id="2282872951544483773">Nedostupni eksperimenti</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> stavka}one{<ph name="ITEM_COUNT" /> stavka}few{<ph name="ITEM_COUNT" /> stavke}other{<ph name="ITEM_COUNT" /> stavki}}</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>
<translation id="2337852623177822836">Postavkom upravlja administrator</translation>
<translation id="2354001756790975382">Druge oznake</translation>
<translation id="2354430244986887761">Google sigurno pregledavanje nedavno je <ph name="BEGIN_LINK" />pronašlo štetne aplikacije<ph name="END_LINK" /> na web-lokaciji <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Nastavi</translation>
<translation id="2365563543831475020">Izvješće o rušenju programa generirano <ph name="CRASH_TIME" /> još nije preneseno</translation>
<translation id="2367567093518048410">Razina</translation>
-<translation id="237718015863234333">Nema zamjenskih korisničkih sučelja</translation>
<translation id="2384307209577226199">Zadano pravilo organizacije</translation>
<translation id="2386255080630008482">Opozvan je certifikat poslužitelja.</translation>
<translation id="2392959068659972793">Prikaži pravila bez postavljenih vrijednosti</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Ako je potvrđen taj okvir, Chromium će pohraniti kopiju vaše kartice na uređaj radi bržeg ispunjavanja obrazaca.</translation>
<translation id="2498091847651709837">Skeniraj novu karticu</translation>
<translation id="2501278716633472235">Natrag</translation>
+<translation id="2503184589641749290">Prihvaćene debitne i pretplatne kartice</translation>
<translation id="2515629240566999685">provjerite jačinu signala na svom području</translation>
-<translation id="2516305470678292029">Zamjenska korisnička sučelja</translation>
<translation id="2539524384386349900">Otkrij</translation>
<translation id="255002559098805027">Host <ph name="HOST_NAME" /> poslao je nevažeći odgovor.</translation>
<translation id="2556876185419854533">&amp;Poništi uređivanje</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Način dostave</translation>
<translation id="277499241957683684">Zapis uređaja nije prisutan</translation>
<translation id="2784949926578158345">Veza je ponovo uspostavljena.</translation>
+<translation id="2788784517760473862">Prihvaćene kreditne kartice</translation>
<translation id="2794233252405721443">Web-lokacija blokirana</translation>
<translation id="2799020568854403057">Sljedeća web-lokacija sadrži štetne aplikacije</translation>
<translation id="2803306138276472711">Google sigurno pregledavanje nedavno je <ph name="BEGIN_LINK" />otkrilo zlonamjerni softver<ph name="END_LINK" /> na <ph name="SITE" />. Web-lokacije koje su inače sigurne ponekad mogu biti zaražene zlonamjernim softverom.</translation>
<translation id="2824775600643448204">Adresna traka i traka za pretraživanje</translation>
<translation id="2826760142808435982">Veza je kriptirana i autentificirana šifrom <ph name="CIPHER" />, a <ph name="KX" /> služi za mehanizam razmjene ključeva.</translation>
<translation id="2835170189407361413">Obriši obrazac</translation>
+<translation id="2851634818064021665">Potrebno ti je dopuštenje za posjet toj web-lokaciji</translation>
<translation id="2856444702002559011">Napadači možda pokušavaju ukrasti vaše podatke s web-lokacije <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (na primjer zaporke, poruke ili brojeve kreditnih kartica). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Ne učitavaj ponovo</translation>
-<translation id="2900469785430194048">Google Chrome ostao je bez memorije dok je pokušavao prikazati ovu web-stranicu.</translation>
<translation id="2909946352844186028">Otkrivena je promjena mreže.</translation>
<translation id="2916038427272391327">Zatvorite ostale programe</translation>
<translation id="2922350208395188000">Certifikat poslužitelja nije moguće provjeriti.</translation>
<translation id="2928905813689894207">Adresa za naplatu</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; njegov je sigurnosni certifikat s domene <ph name="DOMAIN2" />. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Ako je to omogućeno, Chromium će pohraniti kopiju vaše kartice na uređaj radi bržeg ispunjavanja obrazaca.</translation>
<translation id="2985398929374701810">Unesite važeću adresu</translation>
<translation id="2986368408720340940">Taj način preuzimanja nije dostupan. Pokušajte s nekim drugim načinom.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Stranice koje pregledavate na anonimnim karticama ne zadržavaju se u povijesti preglednika, pohrani kolačića ili povijesti pretraživanja nakon što zatvorite sve anonimne kartice, ali će se zadržati sve datoteke koje preuzmete ili oznake koje napravite.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Otok</translation>
-<translation id="317583078218509884">Nove postavke dozvola za web-lokacije postat će aktivne nakon ponovnog učitavanja stranice.</translation>
<translation id="3176929007561373547">Provjerite postavke proxyja ili se obratite mrežnom administratoru da
biste provjerili je li proxy poslužitelj u funkciji. Ako mislite da ne
biste trebali upotrebljavati proxy poslužitelj:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Otvorite stranicu anonimno</translation>
<translation id="320323717674993345">Otkaži plaćanje</translation>
<translation id="3207960819495026254">Označeno</translation>
<translation id="3225919329040284222">Poslužitelj je pokazao certifikat koji ne odgovara ugrađenim očekivanjima. Ta su očekivanja uključena za određene web-lokacije s visokim stupnjem sigurnosti radi vaše zaštite.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Poništi promjenu rasporeda</translation>
<translation id="3282497668470633863">Dodajte ime na kartici</translation>
<translation id="3286538390144397061">Ponovo pokreni sad</translation>
+<translation id="3287510313208355388">Preuzmi online</translation>
<translation id="3303855915957856445">Nisu pronađeni rezultati pretraživanja</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Dohvati interval:</translation>
<translation id="3462200631372590220">Sakrij napredno</translation>
<translation id="3467763166455606212">Potrebno je unijeti ime nositelja kartice</translation>
-<translation id="3478058380795961209">Mjesec isteka</translation>
<translation id="3479539252931486093">Niste to očekivali? <ph name="BEGIN_LINK" />Javite nam<ph name="END_LINK" />.</translation>
<translation id="3479552764303398839">Ne sada</translation>
<translation id="3498215018399854026">Nismo uspjeli stupiti u kontakt s tvojim roditeljem. Pokušaj ponovo.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Prilagodite datum i vrijeme u odjeljku &lt;strong&gt;General&lt;/strong&gt; u aplikaciji &lt;strong&gt;Postavke&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Prihvaćaju se kreditne i pretplatne kartice.</translation>
<translation id="3582930987043644930">Dodajte ime</translation>
<translation id="3583757800736429874">&amp;Ponovi premještanje</translation>
<translation id="3586931643579894722">Sakrij detalje</translation>
@@ -357,10 +360,10 @@
<translation id="3655670868607891010">Ako se to često prikazuje, pokušajte <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Izmjena</translation>
<translation id="3678029195006412963">Zahtjev nije bilo moguće potpisati</translation>
+<translation id="3678529606614285348">Otvorite stranicu u novom anonimnom prozoru (Ctrl – Shift – N)</translation>
<translation id="3679803492151881375">Izvješće o rušenju programa generirano u <ph name="CRASH_TIME" />, preneseno u <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Podaci o certifikatu</translation>
<translation id="3690164694835360974">Prijava nije sigurna</translation>
-<translation id="3693415264595406141">Zaporka:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Učitavanje...</translation>
<translation id="3712624925041724820">Licence su potrošene</translation>
@@ -372,6 +375,7 @@
<translation id="3748148204939282805">Napadači na web-lokaciji <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogu vas na prijevaru pokušati navesti da napravite nešto opasno kao što je instaliranje softvera ili otkrivanje osobnih podataka (npr. zaporki, telefonskih brojeva ili brojeva kreditnih kartica). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Prijevod nije uspio zbog poslužiteljske pogreške.</translation>
<translation id="3759461132968374835">Nemate nedavnih izvješća o padu. Ovdje se neće prikazati padovi do kojih je došlo kada je izvješćivanje o padovima onemogućeno.</translation>
+<translation id="3765032636089507299">Stranica Sigurno pregledavanje trenutačno je u izradi.</translation>
<translation id="3778403066972421603">Želite li spremiti ovu karticu na svoj Google račun i na ovaj uređaj?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Istječe <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -384,8 +388,10 @@
<translation id="3886446263141354045">Zahtjev za pristup toj web-lokaciji poslan je korisniku <ph name="NAME" /></translation>
<translation id="3890664840433101773">Dodajte e-adresu</translation>
<translation id="3901925938762663762">Kartica je istekla</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Preneseno je izvješće o rušenju programa s ID-om <ph name="CRASH_ID" /> (lokalni ID rušenja: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; njegov sigurnosni certifikat ne navodi alternativne nazive predmeta. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation>
+<translation id="3949601375789751990">Ovdje se prikazuje vaša povijest pregledavanja</translation>
<translation id="3963721102035795474">Način čitača</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Nijedna}=1{S 1 web-lokacije }one{S/sa # web-lokacije }few{S/sa # web-lokacije }other{S/sa # web-lokacija }}</translation>
<translation id="397105322502079400">Izračun u tijeku…</translation>
@@ -414,6 +420,8 @@
<translation id="4165986682804962316">Postavke web-lokacije</translation>
<translation id="4169947484918424451">Želite li da Chromium spremi tu karticu?</translation>
<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="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>
@@ -437,12 +445,12 @@
<translation id="4356973930735388585">Napadači na ovoj web-lokaciji 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).</translation>
<translation id="4372948949327679948">Očekivana vrijednost vrste <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Pokušali ste doseći domenu <ph name="DOMAIN" />, ali certifikat koji je poslužitelj predstavio povučen je od strane izdavača. Prema tome nikako ne biste trebali vjerovati sigurnosnim certifikatima koje predstavlja poslužitelj. Možda komunicirate s napadačem.</translation>
-<translation id="4381091992796011497">Ime korisnika:</translation>
<translation id="4394049700291259645">Onemogući</translation>
<translation id="4406896451731180161">rezultati pretraživanja</translation>
<translation id="4424024547088906515">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; Chrome smatra da njegov sigurnosni certifikat nije pouzdan. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation>
<translation id="4432688616882109544">Host <ph name="HOST_NAME" /> nije prihvatio vaš certifikat za prijavu ili certifikat nije poslan.</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="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>
@@ -454,12 +462,14 @@
<translation id="4587425331216688090">Želite li s Chromea ukloniti adresu?</translation>
<translation id="4592951414987517459">Vaša veza s domenom <ph name="DOMAIN" /> kriptirana je modernim kriptografskim paketom.</translation>
<translation id="4594403342090139922">&amp;Poništi brisanje</translation>
+<translation id="4611292653554630842">Prijava</translation>
<translation id="4619615317237390068">Kartice s drugih uređaja</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; njegov sigurnosni certifikat sadrži pogreške. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation>
<translation id="4690462567478992370">Prestani upotrebljavati nevažeći certifikat</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Veza je prekinuta</translation>
+<translation id="471880041731876836">Nemaš dopuštenje za posjet toj web-lokaciji</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />pokrenuti Mrežnu dijagnostiku sustava Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Ponovo učitaj pravila</translation>
<translation id="4728558894243024398">Platforma</translation>
@@ -481,14 +491,15 @@
<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="4863764087567530506">Ovaj vas sadržaj može na prijevaru pokušati navesti da instalirate softver ili odate svoje osobne podatke. <ph name="BEGIN_LINK" />Ipak prikaži<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Pretraži povijest</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Potrebna je autentifikacija</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{i još 1 web-stranica}one{i još # web-stranica}few{i još # web-stranice}other{i još # web-stranica}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Ova je stranica prevedena s nepoznatog jezika na <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Plaćanje</translation>
<translation id="4926049483395192435">Mora biti određeno.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Radnje</translation>
<translation id="4958444002117714549">Proširi popis</translation>
<translation id="4974590756084640048">Ponovo omogući upozorenja</translation>
@@ -504,6 +515,7 @@
<translation id="5045550434625856497">Pogrešna zaporka</translation>
<translation id="5056549851600133418">Preporučeni članci</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />provjerite proxy adresu<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Možda ćete izgubiti pristup zaštićenom sadržaju s nekih web-lokacija.</translation>
<translation id="5087286274860437796">Certifikat poslužitelja trenutačno nije važeći.</translation>
<translation id="5087580092889165836">Dodaj karticu</translation>
<translation id="5089810972385038852">Država</translation>
@@ -511,18 +523,27 @@
<translation id="5095208057601539847">Provincija</translation>
<translation id="5115563688576182185">(64-bitni)</translation>
<translation id="5141240743006678641">Šifriranje sinkroniziranih zaporki s vjerodajnicama za Google</translation>
-<translation id="514421653919133810">Otvorite stranicu anonimno (Ctrl – Shift – N)</translation>
<translation id="5145883236150621069">Odgovor na pravilo sadrži kôd pogreške</translation>
+<translation id="5159010409087891077">Otvorite stranicu u novom anonimnom prozoru (⇧⌘N)</translation>
<translation id="5171045022955879922">Pretražite ili upišite URL</translation>
<translation id="5172758083709347301">Strojno</translation>
<translation id="5179510805599951267">Nije <ph name="ORIGINAL_LANGUAGE" /> jezik? Prijavite tu pogrešku</translation>
-<translation id="5181140330217080051">Preuzimanje</translation>
<translation id="5190835502935405962">Traka oznaka</translation>
<translation id="5199729219167945352">Eksperimenti</translation>
<translation id="5205222826937269299">Ime je obavezno</translation>
<translation id="5222812217790122047">E-pošta (obavezno)</translation>
<translation id="5251803541071282808">Oblak</translation>
<translation id="5277279256032773186">Upotrebljavate li Chrome na poslu? Tvrtke mogu upravljati Chromeovim postavkama za svoje zaposlenike. Saznajte više</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Privremeno onemogućite softver prema ovim uputama da biste se povezali s webom. Potrebne su vam administratorske povlastice.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Kliknite <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, a zatim potražite i odaberite <ph name="BEGIN_BOLD" />"Prikaz lokalnih servisa".<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Odaberite <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />U odjeljku <ph name="BEGIN_BOLD" />Vrsta pokretanja<ph name="END_BOLD" /> odaberite <ph name="BEGIN_BOLD" />Onemogućeno<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />U odjeljku <ph name="BEGIN_BOLD" />Status servisa<ph name="END_BOLD" /> kliknite <ph name="BEGIN_BOLD" />Zaustavi<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Kliknite <ph name="BEGIN_BOLD" />Primijeni<ph name="END_BOLD" />, a zatim kliknite <ph name="BEGIN_BOLD" />U redu<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />U <ph name="BEGIN_LEARN_MORE_LINK" />Chromeovu centru za pomoć<ph name="END_LEARN_MORE_LINK" /> možete saznati kako trajno ukloniti softver s računala.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Veza s tom web-lokacijom nije privatna. Da biste zatvorili VR način, skinite masku i pritisnite Natrag.</translation>
<translation id="5299298092464848405">Pogreška u pravilu analize</translation>
<translation id="5308689395849655368">Onemogućeno je izvješćivanje o padu.</translation>
@@ -539,9 +560,10 @@
<translation id="5439770059721715174">Pogreška provjere sheme na lokaciji "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Tu stranicu na <ph name="HOST_NAME" /> nije moguće pronaći</translation>
<translation id="5455374756549232013">Vremenska oznaka pravila koje nije valjano</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> od <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Nije važeće</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Ponovi uređivanje</translation>
+<translation id="5481076368049295676">Ovaj sadržaj može pokušati na vaš uređaj instalirati opasan softver koji krade ili briše podatke. <ph name="BEGIN_LINK" />Ipak prikaži<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Dodajte važeću adresu</translation>
<translation id="5492298309214877701">Web-lokacija na intranetu tvrtke, organizacije ili škole ima isti URL kao i vanjska web-lokacija.
<ph name="LINE_BREAK" />
@@ -553,6 +575,7 @@
<translation id="5540224163453853">Traženi članak nije pronađen.</translation>
<translation id="5544037170328430102">Ugrađena stranica na web-lokaciji <ph name="SITE" /> navodi sljedeće:</translation>
<translation id="5556459405103347317">Ponovno učitaj</translation>
+<translation id="5560088892362098740">Datum isteka</translation>
<translation id="5565735124758917034">Aktivno</translation>
<translation id="5571083550517324815">Preuzimanje na toj adresi nije moguće. Odaberite drugu adresu.</translation>
<translation id="5572851009514199876">Pokrenite Chrome i prijavite se na njega kako bi mogao provjeriti imate li dopuštenje za pristup toj web-lokaciji.</translation>
@@ -567,12 +590,13 @@
<translation id="5629630648637658800">Učitavanje postavki pravila nije uspjelo</translation>
<translation id="5631439013527180824">Token za upravljanje uređajem nije važeći</translation>
<translation id="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="5646376287012673985">Lokacija</translation>
<translation id="5659593005791499971">E-pošta</translation>
<translation id="5669703222995421982">Predlaganje sadržaja</translation>
<translation id="5675650730144413517">Stranica ne funkcionira</translation>
<translation id="5710435578057952990">Identitet ove web lokacije nije ovjeren.</translation>
-<translation id="5713016350996637505">Blokiran je obmanjujući sadržaj</translation>
+<translation id="5719499550583120431">Prihvaćaju se pretplatne kartice.</translation>
<translation id="5720705177508910913">Trenutačni korisnik:</translation>
<translation id="5732392974455271431">Tvoji je roditelji mogu deblokirati</translation>
<translation id="5763042198335101085">Unesite važeću e-adresu</translation>
@@ -584,15 +608,14 @@
<translation id="5803412860119678065">Želite li ispuniti podatke o kartici <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Vaša veza s domenom <ph name="DOMAIN" /> kriptirana je zastarjelim kriptografskim paketom.</translation>
<translation id="5813119285467412249">&amp;Ponovi dodavanje</translation>
-<translation id="5814352347845180253">Možda ćete izgubiti pristup premium sadržaju s web-lokacije <ph name="SITE" /> i nekih drugih web-lokacija.</translation>
<translation id="5838278095973806738">Na ovu web-lokaciju nemojte unositi osjetljive podatke (na primjer, zaporke ili kreditne kartice) jer su je možda ukrali napadači.</translation>
<translation id="5869405914158311789">Web-lokacija ne može se dohvatiti</translation>
<translation id="5869522115854928033">Spremljene zaporke</translation>
<translation id="5872918882028971132">Nadređeni prijedlozi</translation>
+<translation id="5893752035575986141">Prihvaćaju se kreditne kartice.</translation>
<translation id="5901630391730855834">Žuta</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="5926846154125914413">Možda ćete izgubiti pristup premium sadržaju s nekih web-lokacija.</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="5967867314010545767">Ukloni iz povijesti</translation>
<translation id="5975083100439434680">Smanji</translation>
@@ -608,6 +631,7 @@
<translation id="6040143037577758943">Zatvori</translation>
<translation id="6042308850641462728">Više</translation>
<translation id="6047233362582046994">Ako ste svjesni sigurnosnih rizika, možete <ph name="BEGIN_LINK" />posjetiti ovu web-lokaciju<ph name="END_LINK" /> prije uklanjanja štetnih aplikacija.</translation>
+<translation id="6047927260846328439">Ovaj vas sadržaj može na prijevaru pokušati navesti da instalirate softver ili odate svoje osobne podatke. <ph name="BEGIN_LINK" />Ipak prikaži<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Trenutačno ne možete otvoriti <ph name="SITE" /> jer web-lokacija upotrebljava prikvačivanje certifikata. Mrežne pogreške i napadi obično su privremeni, tako da će stranica kasnije vjerojatno funkcionirati.</translation>
<translation id="6060685159320643512">Oprez, ovi eksperimenti mogu ugristi</translation>
<translation id="6080696365213338172">Pristupili ste sadržaju pomoću certifikata koji je izdao administrator. Administrator može presresti podatke koje dostavljate domeni <ph name="DOMAIN" />.</translation>
@@ -621,7 +645,6 @@
<translation id="6165508094623778733">Saznajte više</translation>
<translation id="6169916984152623906">Sada možete pregledavati privatno i ostali korisnici ovog uređaja neće vidjeti vaše aktivnosti. No spremit će se preuzimanja i oznake.</translation>
<translation id="6177128806592000436">Veza s web-lokacijom nije sigurna</translation>
-<translation id="6184817833369986695">(skupina: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Provjerite internetsku vezu</translation>
<translation id="6218753634732582820">Želite li ukloniti adresu iz Chromiuma?</translation>
<translation id="6221345481584921695">Google sigurno pregledavanje nedavno je <ph name="BEGIN_LINK" />otkrilo zlonamjerni sadržaj<ph name="END_LINK" /> na <ph name="SITE" />. Web-lokacije koje su inače sigurne ponekad mogu biti zaražene zlonamjernim softverom. Zlonamjerni sadržaj potječe s hosta <ph name="SUBRESOURCE_HOST" /> koji je poznat po distribuciji zlonamjernog softvera.</translation>
@@ -640,13 +663,14 @@
<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>
<translation id="6387478394221739770">Zanimaju li vas nove, kul značajke preglednika Chrome? Isprobajte naš beta kanal na stranici chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium je ostao bez memorije dok je pokušavao prikazati ovu web-stranicu.</translation>
+<translation id="6397451950548600259">Softver na vašem računalu sprječava sigurno povezivanje Chromea s webom</translation>
<translation id="6404511346730675251">Uredi oznaku</translation>
<translation id="6410264514553301377">Unesite datum isteka i CVC za karticu <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Pitao si roditelja smiješ li otvoriti tu web-lokaciju</translation>
@@ -661,6 +685,7 @@
<translation id="647261751007945333">Pravila uređaja</translation>
<translation id="6477321094435799029">Chrome je otkrio neuobičajeni kôd na toj stranici i blokirao ju je radi zaštite vaših osobnih podataka (primjerice zaporki, telefonskih brojeva i kreditnih kartica).</translation>
<translation id="6489534406876378309">Pokreni prijenos rušenja</translation>
+<translation id="6507833130742554667">Prihvaćaju se kreditne i debitne kartice.</translation>
<translation id="6508722015517270189">Ponovo pokrenite Chrome</translation>
<translation id="6529602333819889595">&amp;Ponovi brisanje</translation>
<translation id="6534179046333460208">Prijedlozi Fizičkog weba</translation>
@@ -669,13 +694,13 @@
<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="657639383826808334">Ovaj sadržaj može pokušati na vaš uređaj instalirati opasan softver koji krade ili briše podatke. <ph name="BEGIN_LINK" />Ipak prikaži<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opcije šifriranja</translation>
<translation id="662080504995468778">Ostani</translation>
<translation id="6626291197371920147">Dodajte važeći broj kartice</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Pretraživanje</translation>
<translation id="6630809736994426279">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š Mac 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="6644283850729428850">Ovo je pravilo zastarjelo.</translation>
+<translation id="6657585470893396449">Zaporka</translation>
<translation id="6671697161687535275">Želite li ukloniti prijedlog iz Chromiuma?</translation>
<translation id="6685834062052613830">Odjavite se i dovršite postavljanje</translation>
<translation id="6710213216561001401">Prethodno</translation>
@@ -706,7 +731,6 @@
<translation id="6970216967273061347">Distrikt</translation>
<translation id="6973656660372572881">Određeni su fiksni proxy poslužitelji i URL .pac skripte.</translation>
<translation id="6989763994942163495">Pokaži napredne postavke...</translation>
-<translation id="7000990526846637657">Unosi povijesti nisu pronađeni</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Na Google računu možda postoje drugi oblici povijesti pregledavanja na stranici <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Zaporke</translation>
@@ -721,7 +745,9 @@
<translation id="7129409597930077180">Dostava na tu adresu nije moguća. Odaberite drugu adresu.</translation>
<translation id="7138472120740807366">Način isporuke</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Plaćanje nije sigurno</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Ponovo pokreni sada</translation>
<translation id="7180611975245234373">Osvježi</translation>
<translation id="7182878459783632708">Nije postavljeno nijedno pravilo</translation>
@@ -762,6 +788,7 @@
<translation id="7514365320538308">Preuzmi</translation>
<translation id="7518003948725431193">Za web-adresu nije pronađena web-stranica:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Veza s web-lokacijom nije privatna</translation>
<translation id="7535087603100972091">Vrijednost</translation>
<translation id="7537536606612762813">Obavezno</translation>
<translation id="7542403920425041731">Nakon što ih potvrdite, podaci o kartici dijelit će se s ovom web-lokacijom.</translation>
@@ -771,7 +798,6 @@
<translation id="7552846755917812628">Pokušajte učiniti sljedeće:</translation>
<translation id="7554791636758816595">Nova kartica</translation>
<translation id="7567204685887185387">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; njegov sigurnosni certifikat možda je lažan. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Ova je stranica na ovom jeziku:<ph name="ORIGINAL_LANGUAGE" />Želite li je prevesti?</translation>
<translation id="7569952961197462199">Želite li s Chromea ukloniti kreditnu karticu?</translation>
<translation id="7569983096843329377">Crna</translation>
@@ -798,9 +824,11 @@
<translation id="7714464543167945231">Certifikat</translation>
<translation id="7716147886133743102">Blokirao administrator</translation>
<translation id="7716424297397655342">Web-lokacija se ne može učitati iz predmemorije</translation>
+<translation id="774634243536837715">Blokiran je opasan sadržaj.</translation>
<translation id="7752995774971033316">Nema upravitelja</translation>
<translation id="7755287808199759310">Roditelj je može deblokirati</translation>
<translation id="7758069387465995638">Vezu možda blokira vatrozid ili antivirusni softver.</translation>
+<translation id="7759163816903619567">Prikaži domenu:</translation>
<translation id="7761701407923456692">Certifikat poslužitelja ne podudara se s URL-om.</translation>
<translation id="7763386264682878361">Raščlanjivač manifesta za plaćanje</translation>
<translation id="7764225426217299476">Dodaj adresu</translation>
@@ -815,6 +843,7 @@
<translation id="7815407501681723534">Pronađeno <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> za "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">Niste nevidljivi. Anonimni način ne sakriva vaše pregledavanje od poslodavca, davatelja internetskih usluga ili posjećenih web-lokacija.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Prihvaćaju se debitne i pretplatne kartice.</translation>
<translation id="7887683347370398519">Provjerite CVC i pokušajte ponovo</translation>
<translation id="79338296614623784">Unesite važeći telefonski broj</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -834,7 +863,6 @@
<translation id="8041089156583427627">Slanje povratnih informacija</translation>
<translation id="8041940743680923270">Upotrijebi globalnu zadanu vrijednost (pitaj)</translation>
<translation id="8088680233425245692">Prikaz članka nije uspio.</translation>
-<translation id="8089520772729574115">manje od 1 MB</translation>
<translation id="8091372947890762290">Aktivacija je na čekanju na poslužitelju</translation>
<translation id="8118489163946903409">Način plaćanja</translation>
<translation id="8131740175452115882">Potvrdi</translation>
@@ -846,10 +874,13 @@
<translation id="8194797478851900357">&amp;Poništi premještanje</translation>
<translation id="8201077131113104583">Nevažeći URL ažuriranja za proširenje s ID-om "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Sažetak narudžbe</translation>
+<translation id="8205463626947051446">Web-lokacija često prikazuje ometajuće oglase</translation>
<translation id="8218327578424803826">Dodijeljena lokacija:</translation>
<translation id="8225771182978767009">Osoba koja je postavila računalo blokirala je tu web-lokaciju.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Otvorite stranicu u novoj anonimnoj kartici</translation>
<translation id="8241707690549784388">Stranica koju ste tražili koristila je podatke koje ste unijeli. Vraćanje na tu stranicu može dovesti do ponavljanja poduzete radnje. Želite li nastaviti?</translation>
+<translation id="8241712895048303527">Blokiraj na ovoj web-lokaciji</translation>
<translation id="8249320324621329438">Zadnje dohvaćanje:</translation>
<translation id="8253091569723639551">Potrebna je adresa za naplatu</translation>
<translation id="8261506727792406068">Izbriši</translation>
@@ -860,7 +891,6 @@
<translation id="8308427013383895095">Prijevod nije uspio zbog problema s mrežnom vezom.</translation>
<translation id="8332188693563227489">Pristup hostu <ph name="HOST_NAME" /> je odbijen</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="8344669043927012510">Otvorite stranicu anonimno (⇧⌘N)</translation>
<translation id="8349305172487531364">Traka oznaka</translation>
<translation id="8363502534493474904">isključite način rada u zrakoplovu</translation>
<translation id="8364627913115013041">Nije postavljeno.</translation>
@@ -870,6 +900,7 @@
<translation id="8398259832188219207">Izvješće o rušenju programa preneseno u <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Padovi (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Morate dvaput unijeti istu zaporku.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Postavke</translation>
<translation id="8433057134996913067">Time ćete se odjaviti s većine web-lokacija.</translation>
<translation id="8437238597147034694">&amp;Poništi premještanje</translation>
@@ -877,8 +908,9 @@
<translation id="8483780878231876732">Da biste upotrebljavali kartice sa svojeg Google računa, prijavite se na Chrome</translation>
<translation id="8488350697529856933">Primjenjuje se na</translation>
<translation id="8498891568109133222">Hostu <ph name="HOST_NAME" /> bilo je potrebno previše vremena za odgovor.</translation>
-<translation id="8532105204136943229">Godina isteka</translation>
+<translation id="8503813439785031346">Korisničko ime</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="8571890674111243710">Prijevod stranice na <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Dodaj tel. broj
@@ -886,6 +918,7 @@
<translation id="859285277496340001">Certifikat ne navodi mehanizam za provjeru svojeg opoziva.</translation>
<translation id="8620436878122366504">Roditelji je još nisu odobrili</translation>
<translation id="8647750283161643317">Vrati sve na zadano</translation>
+<translation id="8660471606262461360">S Google Paymentsa</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="8725066075913043281">Pokušajte ponovo</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Povećaj</translation>
<translation id="8931333241327730545">Želite li spremiti tu karticu na Google račun?</translation>
<translation id="8932102934695377596">Sat kasni</translation>
+<translation id="8938939909778640821">Prihvaćene kreditne i pretplatne kartice</translation>
<translation id="8971063699422889582">Istekao je certifikat poslužitelja.</translation>
<translation id="8986494364107987395">Automatski šalji Googleu statistiku o upotrebi i izvješća o padu programa</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@ i netočne vjerodajnice. To može značiti da se neki napadač pokušava predsta
<translation id="9169664750068251925">Uvijek blokiraj na ovoj web-lokaciji</translation>
<translation id="9170848237812810038">&amp;Poništi</translation>
<translation id="917450738466192189">Certifikat poslužitelja nije valjan.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> i još <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Dodavanje članka nije uspjelo.</translation>
@@ -952,9 +985,9 @@ i netočne vjerodajnice. To može značiti da se neki napadač pokušava predsta
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">IZBRIŠI OBRAZAC</translation>
<translation id="939736085109172342">Nova mapa</translation>
-<translation id="941721044073577244">Izgleda da nemate dopuštenje za posjet toj web-lokaciji</translation>
<translation id="969892804517981540">Službeni sastavak</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Nijedna}=1{1 stavka}one{# stavka}few{# stavke}other{# stavki}}</translation>
+<translation id="981121421437150478">Izvanmrežno</translation>
<translation id="988159990683914416">Sastavak razvojnog programera</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_hu.xtb b/chromium/components/strings/components_strings_hu.xtb
index 8a35caea656..11eb909f56c 100644
--- a/chromium/components/strings/components_strings_hu.xtb
+++ b/chromium/components/strings/components_strings_hu.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Zárja be a többi alkalmazást</translation>
<translation id="1055184225775184556">&amp;Hozzáadás visszavonása</translation>
<translation id="10614374240317010">Az alábbi oldalakról soha ne mentsen jelszavakat</translation>
+<translation id="1066396345355680611">Elveszítheti hozzáférését a(z) <ph name="SITE" /> és más webhelyek védett tartalmaihoz.</translation>
<translation id="106701514854093668">Asztali könyvjelzők</translation>
<translation id="1074497978438210769">Nem biztonságos</translation>
<translation id="1080116354587839789">Szélességhez igazítás</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Olvasási lista</translation>
<translation id="1264126396475825575">Hibajelentés készült: <ph name="CRASH_TIME" /> (még nincs feltöltve vagy mellőzték)</translation>
<translation id="1281526147609854549">Kibocsátó: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Veszélyes tartalom letiltva</translation>
<translation id="1285320974508926690">Ezt a webhelyet soha ne fordítsa le</translation>
<translation id="129553762522093515">Mostanában bezárt</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Próbálkozzon a cookie-k törlésével<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Regisztrációs domain:</translation>
<translation id="1340482604681802745">Átvételi cím</translation>
-<translation id="1344211575059133124">Úgy tűnik, hogy a webhely felkereséséhez engedélyére van szükség</translation>
<translation id="1344588688991793829">A Chromium Automatikus kitöltési beállításai…</translation>
<translation id="1348198688976932919">A felkeresni kívánt webhely veszélyes alkalmazásokat tartalmaz</translation>
<translation id="1374468813861204354">javaslatok</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="ORGANIZATION" /> identitását <ph name="LOCALITY" /> régióban <ph name="ISSUER" /> ellenőrizte.</translation>
<translation id="1426410128494586442">Igen</translation>
<translation id="1430915738399379752">Nyomtatás</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Az oldal mentett (pl. köztudottan régi) változatának megjelenítése.</translation>
<translation id="1517433312004943670">Telefonszám szükséges</translation>
+<translation id="1517500485252541695">Elfogadott hitel- és bankkártyák</translation>
<translation id="1519264250979466059">Build dátuma</translation>
+<translation id="1527263332363067270">Várakozás a kapcsolódásra…</translation>
<translation id="153384715582417236">Egyelőre ennyi</translation>
<translation id="1549470594296187301">A funkció használatához engedélyezni kell a JavaScriptet.</translation>
<translation id="1555130319947370107">Kék</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Az oldal letöltése később</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Kötelező mező</translation>
<translation id="187918866476621466">Kezdőoldalak megnyitása</translation>
<translation id="1883255238294161206">Lista bezárása</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Szűrés</translation>
+<translation id="1916770123977586577">A frissített beállításoknak a webhelyen történő alkalmazásához töltse be újra az oldalt</translation>
+<translation id="1919345977826869612">Hirdetések</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Nincs}=1{1 webhely}other{# webhely}}</translation>
<translation id="194030505837763158">Ugrás ide: <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Elfogadott feltöltőkártyák</translation>
<translation id="1962204205936693436">A(z) <ph name="DOMAIN" /> könyvjelzői</translation>
<translation id="1973335181906896915">Szerializálási hiba</translation>
<translation id="1974060860693918893">Speciális</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP hiba</translation>
<translation id="2270484714375784793">Telefonszám</translation>
<translation id="2282872951544483773">Nem elérhető kísérletek</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> elem}other{<ph name="ITEM_COUNT" /> elem}}</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>
<translation id="2337852623177822836">A beállítást a rendszergazda szabályozza</translation>
<translation id="2354001756790975382">További könyvjelzők</translation>
<translation id="2354430244986887761">A Google Biztonságos Böngészés funkciója nemrég <ph name="BEGIN_LINK" />kártékony alkalmazásokat talált<ph name="END_LINK" /> a(z) <ph name="SITE" /> webhelyen.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Folytatás</translation>
<translation id="2365563543831475020">A(z) <ph name="CRASH_TIME" /> időpontban készült hibajelentés nincs feltöltve</translation>
<translation id="2367567093518048410">Szint</translation>
-<translation id="237718015863234333">Nincsenek alternatív kezelőfelületek</translation>
<translation id="2384307209577226199">Vállalati alapértelmezett</translation>
<translation id="2386255080630008482">A szerver tanúsítványát visszavonták.</translation>
<translation id="2392959068659972793">Beállított értékkel nem rendelkező házirendek megjelenítése</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Ha be van jelölve, a Chromium megőrzi a kártya másolatát ezen az eszközön a gyorsabb űrlapkitöltés érdekében.</translation>
<translation id="2498091847651709837">Új kártya beolvasása</translation>
<translation id="2501278716633472235">Visszalépés</translation>
+<translation id="2503184589641749290">Elfogadott bank- és feltöltőkártyák</translation>
<translation id="2515629240566999685">A térerő ellenőrzése tartózkodási helyén</translation>
-<translation id="2516305470678292029">Alternatív kezelőfelületek</translation>
<translation id="2539524384386349900">Felismerés</translation>
<translation id="255002559098805027">A(z) <ph name="HOST_NAME" /> érvénytelen választ küldött.</translation>
<translation id="2556876185419854533">&amp;Szerkesztés visszavonása</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Szállítási mód</translation>
<translation id="277499241957683684">Hiányzó eszközrekord</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>
<translation id="2799020568854403057">A felkeresni kívánt webhely káros alkalmazásokat tartalmaz</translation>
<translation id="2803306138276472711">A Google – Biztonságos böngészés nemrég <ph name="BEGIN_LINK" />rosszindulatú programot<ph name="END_LINK" /> észlelt a(z) <ph name="SITE" /> webhelyen. A rendes esetben biztonságos webhelyek néha rosszindulatú programokkal fertőzöttek.</translation>
<translation id="2824775600643448204">Cím- és keresősáv</translation>
<translation id="2826760142808435982">A kapcsolat <ph name="KX" /> algoritmust használ kulcscserélő mechanizmusként, kódolása pedig <ph name="CIPHER" /> használatával történt.</translation>
<translation id="2835170189407361413">Űrlap törlése</translation>
+<translation id="2851634818064021665">A webhely felkereséséhez jogosultságra van szüksége</translation>
<translation id="2856444702002559011">A támadók megpróbálhatják ellopni a(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhelyen lévő adatait (például jelszavait, üzeneteit és hitelkártyaadatait). <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2889159643044928134">Ne töltse újra</translation>
-<translation id="2900469785430194048">Elfogyott a memória, miközben a Google Chrome megpróbálta megjeleníteni ezt a weboldalt.</translation>
<translation id="2909946352844186028">Változást érzékeltünk a hálózatban.</translation>
<translation id="2916038427272391327">Zárja be a többi programot</translation>
<translation id="2922350208395188000">A szerver tanúsítványát nem sikerült leellenőrizni.</translation>
<translation id="2928905813689894207">Számlázási cím</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa a következőről származik: <ph name="DOMAIN2" />. Ennek oka lehet konfigurációs hiba, vagy hogy egy támadó eltérítette az Ön kapcsolódását.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Ha engedélyezi, a Chromium megőrzi a kártya másolatát ezen az eszközön a gyorsabb űrlapkitöltés érdekében.</translation>
<translation id="2985398929374701810">Érvényes címet adjon meg</translation>
<translation id="2986368408720340940">Ez az átvételi mód nem áll rendelkezésre. Próbálkozzon másik móddal.</translation>
@@ -277,10 +281,8 @@
<translation id="3167968892399408617">Az inkognitólapon megtekintett oldalak az összes inkognitólap bezárását követően nem szerepelnek majd böngészési előzményei között, a cookie-k gyűjtőhelyén, illetve a keresési előzményekben. A letöltött fájlok és a könyvjelzők azonban megmaradnak.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Sziget</translation>
-<translation id="317583078218509884">Az új webhelyengedélyek az oldal ismételt betöltése után lépnek életbe.</translation>
<translation id="3176929007561373547">Ellenőrizze a proxybeállításokat, vagy kérdezze meg a rendszergazdájától, hogy a proxyszerver működik-e. Ha úgy gondolja, hogy nem használ proxyszervert:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Nyissa meg az oldalt inkognitómódban</translation>
<translation id="320323717674993345">Fizetés visszavonása</translation>
<translation id="3207960819495026254">Könyvjelzőzött</translation>
<translation id="3225919329040284222">A szerver tanúsítványa nem felel meg a beépített elvárásoknak. Ezek a beépített elvárások bizonyos nagy biztonságú webhelyekre vonatkoznak az Ön védelme érdekében.</translation>
@@ -293,6 +295,7 @@
<translation id="3270847123878663523">&amp;Átrendezés visszavonása</translation>
<translation id="3282497668470633863">Adja meg a kártyán szereplő nevet</translation>
<translation id="3286538390144397061">Újraindítás most</translation>
+<translation id="3287510313208355388">Letöltés online állapotban</translation>
<translation id="3303855915957856445">Nincs találat</translation>
<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>
@@ -325,7 +328,6 @@
<translation id="3452404311384756672">Lekérési intervallum:</translation>
<translation id="3462200631372590220">Speciális beállítások elrejtése</translation>
<translation id="3467763166455606212">A kártyatulajdonos nevének megadása kötelező</translation>
-<translation id="3478058380795961209">Lejárat hónapja</translation>
<translation id="3479539252931486093">Ez váratlanul érte Önt? <ph name="BEGIN_LINK" />Tudassa velünk.<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ne most</translation>
<translation id="3498215018399854026">Jelenleg nem tudjuk elérni szüleidet. Próbálkozz újra.</translation>
@@ -341,12 +343,13 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Elfogadott hitel- és feltöltőkártyák.</translation>
<translation id="3582930987043644930">Név hozzáadása</translation>
<translation id="3583757800736429874">&amp;Áthelyezés újra</translation>
<translation id="3586931643579894722">Részletek elrejtése</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Érvényes lejárati dátumot kell megadnia</translation>
-<translation id="36224234498066874">Böngészési adatok törlése...</translation>
+<translation id="36224234498066874">Böngészési adatok törlése…</translation>
<translation id="362276910939193118">Minden előzmény megjelenítése</translation>
<translation id="3623476034248543066">Érték megjelenítése</translation>
<translation id="3630155396527302611">Ha már megjelenik a hálózat elérésére engedélyezett programként, próbálkozzon
@@ -356,10 +359,10 @@
<translation id="3655670868607891010">Ha gyakran látja ezt, próbálja ki a következőt: <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Ellenőrzés</translation>
<translation id="3678029195006412963">A kérést nem lehetett aláírni</translation>
+<translation id="3678529606614285348">Oldal megnyitása új inkognitóablakban (Ctrl+Shift+N)</translation>
<translation id="3679803492151881375">A hibajelentés elkészítésének ideje: <ph name="CRASH_TIME" />; a feltöltés ideje: <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Tanúsítvány adatai</translation>
<translation id="3690164694835360974">A bejelentkezés nem biztonságos</translation>
-<translation id="3693415264595406141">Jelszó:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Betöltés...</translation>
<translation id="3712624925041724820">Az engedélyek elfogytak</translation>
@@ -371,6 +374,7 @@
<translation id="3748148204939282805">A(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhely támadói megpróbálhatják csellel rávenni Önt például arra, hogy veszélyes szoftvert telepítsen, vagy felfedje személyes adatait (jelszavakat, telefonszámokat, hitelkártyaadatokat stb). <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="375403751935624634">A fordítás a szerver hibája miatt nem sikerült.</translation>
<translation id="3759461132968374835">Nincs a közelmúltban bejelentett rendszerösszeomlás. A kikapcsolt jelentésküldés során történt összeomlások nem jelennek meg itt.</translation>
+<translation id="3765032636089507299">A Biztonságos Böngészés oldal fejlesztés alatt áll</translation>
<translation id="3778403066972421603">Szeretné menteni a kártyát Google-fiókjába és az eszközre?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Lejárat dátuma: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -383,8 +387,10 @@
<translation id="3886446263141354045">Webhely-hozzáférési kérelme elküldve a következő személynek: <ph name="NAME" /></translation>
<translation id="3890664840433101773">E-mail-cím hozzáadása</translation>
<translation id="3901925938762663762">A kártya lejárt</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Feltöltött hibajelentés azonosítója: <ph name="CRASH_ID" /> (helyi hibaazonosító: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványában nincs meghatározva a „Subject Alternative Names” mező. Ezt okozhatja konfigurációs hiba, vagy az, hogy egy támadó eltérítette az Ön kapcsolatát.</translation>
+<translation id="3949601375789751990">A böngészési előzmények itt jelennek meg</translation>
<translation id="3963721102035795474">Olvasási mód</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Nincs}=1{ 1 webhelytől }other{# webhelytől }}</translation>
<translation id="397105322502079400">Számítás…</translation>
@@ -413,6 +419,8 @@
<translation id="4165986682804962316">Webhelybeállítások</translation>
<translation id="4169947484918424451">Szeretné, hogy a Chromium mentse ezt a kártyát?</translation>
<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="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>
@@ -436,12 +444,12 @@
<translation id="4356973930735388585">Előfordulhat, hogy a webhely támadói olyan veszélyes programokat próbálnak telepíteni számítógépére, amelyek ellopják vagy törlik adatait (például fotóit, jelszavait, üzeneteit vagy hitelkártyaadatait).</translation>
<translation id="4372948949327679948">Várt <ph name="VALUE_TYPE" /> érték.</translation>
<translation id="4377125064752653719">A(z) <ph name="DOMAIN" /> webhelyet próbálta megnyitni, de a kiállító visszavonta a szerver által bemutatott tanúsítványt. Ez azt jelenti, hogy a szerver biztonsági igazolásaiban egyáltalán nem lehet megbízni. Lehet, hogy egy támadóval áll kapcsolatban.</translation>
-<translation id="4381091992796011497">Felhasználónév:</translation>
<translation id="4394049700291259645">Kikapcsolás</translation>
<translation id="4406896451731180161">keresési találat</translation>
<translation id="4424024547088906515">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa a Chrome 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="4432688616882109544">A(z) <ph name="HOST_NAME" /> nem fogadta el az Ön bejelentkezési tanúsítványát, vagy nem talált ilyet.</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="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>
@@ -453,12 +461,14 @@
<translation id="4587425331216688090">Eltávolítja a címet a Chrome-ból?</translation>
<translation id="4592951414987517459">A(z) <ph name="DOMAIN" /> domainnel való kapcsolata modern kriptográfiával van titkosítva.</translation>
<translation id="4594403342090139922">&amp;Törlés visszavonása</translation>
+<translation id="4611292653554630842">Bejelentkezés</translation>
<translation id="4619615317237390068">Más eszközök lapjai</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa hibákat tartalmaz. Ennek oka lehet konfigurációs hiba, vagy hogy egy támadó eltérítette az Ön kapcsolódását.</translation>
<translation id="4690462567478992370">Érvénytelen tanúsítvány használatának befejezése</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Kapcsolata megszakadt</translation>
+<translation id="471880041731876836">Nincs jogosultsága a webhely felkereséséhez</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />A Windows Hálózati diagnosztika futtatása<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Házirendek újratöltése</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -480,14 +490,15 @@
<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="4863764087567530506">Lehet, hogy ez a tartalom megpróbálja rávenni Önt valamilyen szoftver telepítésére vagy személyes adatok kiadására. <ph name="BEGIN_LINK" />Megjelenítés mindenképpen<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Keresés az előzmények között</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> és <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Azonosítás szükséges</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{és egy további weboldal}other{és # további weboldal}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Ezt az oldalt lefordították egy ismeretlen nyelvről <ph name="LANGUAGE_LANGUAGE" /> nyelvre</translation>
<translation id="4923459931733593730">Fizetés</translation>
<translation id="4926049483395192435">Meg kell határozni.</translation>
+<translation id="4926340098269537727"><ph name="TOTAL_MATCHCOUNT" />/<ph name="ACTIVE_MATCH" /></translation>
<translation id="495170559598752135">Műveletek</translation>
<translation id="4958444002117714549">Lista részletes nézete</translation>
<translation id="4974590756084640048">Figyelmeztetések újbóli engedélyezése</translation>
@@ -503,6 +514,7 @@
<translation id="5045550434625856497">Helytelen jelszó</translation>
<translation id="5056549851600133418">Cikkek Önnek</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />A proxy címének ellenőrzése<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Elveszítheti hozzáférését egyes webhelyek védett tartalmaihoz.</translation>
<translation id="5087286274860437796">A szerver tanúsítványa jelenleg nem érvényes.</translation>
<translation id="5087580092889165836">Kártya hozzáadása</translation>
<translation id="5089810972385038852">Állam</translation>
@@ -510,18 +522,27 @@
<translation id="5095208057601539847">Tartomány</translation>
<translation id="5115563688576182185">(64 bites)</translation>
<translation id="5141240743006678641">A szinkronizált jelszavak titkosítása a Google hitelesítési adataival</translation>
-<translation id="514421653919133810">Nyissa meg az oldalt inkognitómódban (Ctrl+Shift+N)</translation>
<translation id="5145883236150621069">Az irányelv válasza hibakódot tartalmaz</translation>
+<translation id="5159010409087891077">Oldal megnyitása új inkognitóablakban (⇧⌘N)</translation>
<translation id="5171045022955879922">Keressen vagy írjon be egy URL-t</translation>
<translation id="5172758083709347301">Számítógép</translation>
<translation id="5179510805599951267">Nem <ph name="ORIGINAL_LANGUAGE" /> nyelven van? Hiba bejelentése</translation>
-<translation id="5181140330217080051">Letöltés</translation>
<translation id="5190835502935405962">Könyvjelzősáv</translation>
<translation id="5199729219167945352">Kísérletek</translation>
<translation id="5205222826937269299">A név megadása kötelező</translation>
<translation id="5222812217790122047">Az e-mail-cím megadása kötelező</translation>
<translation id="5251803541071282808">Felhő</translation>
<translation id="5277279256032773186">A munkahelyén használja a Chrome-ot? A cégek kezelhetik a Chrome-beállításokat alkalmazottaik számára. További információ.</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Kövesse ezeket a lépéseket, ha szeretné átmenetileg letiltani a szoftvert, hogy hozzáférhessen az internethez. Rendszergazdai jogosultságokra lesz szüksége.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Kattintson a <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" /> menüre, majd keresse meg, és válassza ki a <ph name="BEGIN_BOLD" />„Helyi szolgáltatások megjelenítése”<ph name="END_BOLD" /> lehetőséget.
+ <ph name="LIST_ITEM" />Válassza a következőt: <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />A <ph name="BEGIN_BOLD" />Kezdési típus<ph name="END_BOLD" /> részben válassza a <ph name="BEGIN_BOLD" />Letiltva<ph name="END_BOLD" /> lehetőséget.
+ <ph name="LIST_ITEM" />A <ph name="BEGIN_BOLD" />Szolgáltatás állapota<ph name="END_BOLD" /> részben kattintson a <ph name="BEGIN_BOLD" />Leállítás<ph name="END_BOLD" /> gombra.
+ <ph name="LIST_ITEM" />Kattintson az <ph name="BEGIN_BOLD" />Alkalmaz<ph name="END_BOLD" />, majd az <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" /> gombra.
+ <ph name="LIST_ITEM" />Keresse fel a <ph name="BEGIN_LEARN_MORE_LINK" />Chrome súgóját<ph name="END_LEARN_MORE_LINK" />, 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.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Kapcsolata a webhellyel nem privát. Bármikor kiléphet a VR módból, ha leveszi a headsetet, és megnyomja a Vissza gombot.</translation>
<translation id="5299298092464848405">Irányelv-előfeldolgozási hiba</translation>
<translation id="5308689395849655368">A hibabejelentés ki van kapcsolva.</translation>
@@ -538,9 +559,10 @@
<translation id="5439770059721715174">Sémaérvényesítési hiba a következőnél: „<ph name="ERROR_PATH" />”: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Ez a(z) <ph name="HOST_NAME" /> oldal nem található</translation>
<translation id="5455374756549232013">Hibás az irányelv időbélyege</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" />, összesen: <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Érvénytelen</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Szerkesztés újra</translation>
+<translation id="5481076368049295676">Lehet, hogy ez a tartalom megpróbál olyan veszélyes szoftvert telepíteni az eszközre, amely ellophatja vagy törölheti az Ön adatait. <ph name="BEGIN_LINK" />Megjelenítés mindenképpen<ph name="END_LINK" />.</translation>
<translation id="54817484435770891">Érvényes címet adjon meg</translation>
<translation id="5492298309214877701">Ennek a vállalati, szervezeti vagy iskolai intraneten található webhelynek az URL-címe megegyezik egy külső webhely URL-címével.
<ph name="LINE_BREAK" />
@@ -552,6 +574,7 @@
<translation id="5540224163453853">A kért cikk nem található.</translation>
<translation id="5544037170328430102">A(z) <ph name="SITE" /> egy beágyazott oldalának közlendője:</translation>
<translation id="5556459405103347317">Újratöltés</translation>
+<translation id="5560088892362098740">Lejárati dátum</translation>
<translation id="5565735124758917034">Aktív</translation>
<translation id="5571083550517324815">Ezen a címen nem lehetséges az átvétel. Válasszon másik címet.</translation>
<translation id="5572851009514199876">Indítsa el a Chrome böngészőt és jelentkezzen be, hogy a Chrome ellenőrizni tudja, engedélyezték-e a hozzáférést ehhez a webhelyhez.</translation>
@@ -566,12 +589,13 @@
<translation id="5629630648637658800">Az irányelv-beállítások betöltése sikertelen</translation>
<translation id="5631439013527180824">Érvénytelen eszközkezelési token</translation>
<translation id="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="5646376287012673985">Tartózkodási hely</translation>
<translation id="5659593005791499971">E-mail</translation>
<translation id="5669703222995421982">Személyre szabott tartalmak fogadása</translation>
<translation id="5675650730144413517">Az oldal nem működik</translation>
<translation id="5710435578057952990">A webhely valódiságát nem ellenőriztük.</translation>
-<translation id="5713016350996637505">Megtévesztő tartalom letiltva</translation>
+<translation id="5719499550583120431">Elfogadott feltöltőkártyák.</translation>
<translation id="5720705177508910913">Jelenlegi felhasználó</translation>
<translation id="5732392974455271431">A letiltást a szüleid oldhatják fel</translation>
<translation id="5763042198335101085">Érvényes e-mail-címet adjon meg</translation>
@@ -583,15 +607,14 @@
<translation id="5803412860119678065">Ki szeretné tölteni <ph name="CARD_DETAIL" /> kártyájának adataival?</translation>
<translation id="5810442152076338065">A(z) <ph name="DOMAIN" /> domainnel való kapcsolata elavult kriptográfiával van titkosítva.</translation>
<translation id="5813119285467412249">&amp;Hozzáadás újra</translation>
-<translation id="5814352347845180253">Elveszítheti hozzáférését a(z) <ph name="SITE" /> és más webhelyek prémium tartalmaihoz.</translation>
<translation id="5838278095973806738">Ne írjon be semmilyen bizalmas adatot (például jelszót vagy hitelkártyaadatot) a webhelyen, mivel a támadók ellophatják.</translation>
<translation id="5869405914158311789">A webhely nem érhető el</translation>
<translation id="5869522115854928033">Mentett jelszavak</translation>
<translation id="5872918882028971132">Szülői javaslatok</translation>
+<translation id="5893752035575986141">Elfogadott hitelkártyák.</translation>
<translation id="5901630391730855834">Sárga</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="5926846154125914413">Elveszítheti hozzáférését az egyes webhelyek prémium tartalmaihoz.</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="5967867314010545767">Eltávolítás az előzmények közül</translation>
<translation id="5975083100439434680">Kicsinyítés</translation>
@@ -607,6 +630,7 @@
<translation id="6040143037577758943">Bezárás</translation>
<translation id="6042308850641462728">Hosszabban</translation>
<translation id="6047233362582046994">Ha tisztában van a biztonságát fenyegető kockázatokkal, a káros alkalmazások eltávolítása előtt is <ph name="BEGIN_LINK" />felkeresheti ezt a webhelyet<ph name="END_LINK" />.</translation>
+<translation id="6047927260846328439">Lehet, hogy ez a tartalom megpróbálja rávenni Önt szoftver telepítésére vagy személyes adatok kiadására. <ph name="BEGIN_LINK" />Megjelenítés mindenképpen<ph name="END_LINK" />.</translation>
<translation id="6051221802930200923">Pillanatnyilag nem tudja felkeresni a(z) <ph name="SITE" /> webhelyet, mivel a webhely tanúsítványrögzítést használ. 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="6060685159320643512">Óvatosan, ezek a kísérletek haraphatnak</translation>
<translation id="6080696365213338172">A tartalmat egy rendszergazda által biztosított tanúsítványon keresztül érte el. A(z) <ph name="DOMAIN" /> számára megadott adatok a rendszergazda számára is elérhetők.</translation>
@@ -620,7 +644,6 @@
<translation id="6165508094623778733">További információ</translation>
<translation id="6169916984152623906">Most privát módon böngészhet, így az eszközt használó többi személy nem láthatja az Ön tevékenységeit. A letöltéseket és a könyvjelzőket azonban menti a rendszer.</translation>
<translation id="6177128806592000436">Kapcsolata a webhellyel nem biztonságos</translation>
-<translation id="6184817833369986695">(kohorsz: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Ellenőrizze az internetkapcsolatot</translation>
<translation id="6218753634732582820">Eltávolítja a címet a Chromiumból?</translation>
<translation id="6221345481584921695">A Google – Biztonságos böngészés nemrég <ph name="BEGIN_LINK" />rosszindulatú programokat<ph name="END_LINK" /> észlelt a következő webhelyen: <ph name="SITE" />. A rendes esetben biztonságos webhelyek néha rosszindulatú programokkal fertőződnek. A rosszindulatú tartalom az ilyen programok következő ismert terjesztőjétől származik: <ph name="SUBRESOURCE_HOST" />.</translation>
@@ -639,13 +662,14 @@
<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>
<translation id="6387478394221739770">Érdekli néhány remek új Chrome-funkció? Próbálja ki a Béta csatornánkat a chrome.com/beta webhelyen.</translation>
-<translation id="6389758589412724634">Elfogyott a memória, miközben a Chromium megpróbálta megjeleníteni ezt a weboldalt.</translation>
+<translation id="6397451950548600259">A számítógépen található valamelyik szoftver megakadályozza a Chrome-ot abban, hogy biztonságosan csatlakozzon az internetre</translation>
<translation id="6404511346730675251">Könyvjelző szerkesztése</translation>
<translation id="6410264514553301377">Írja be a(z) <ph name="CREDIT_CARD" /> kártyán szereplő lejárati dátumot és CVC-t</translation>
<translation id="6414888972213066896">Megkérdezted a szülőt, hogy meg szabad-e látogatnod ezt a webhelyet</translation>
@@ -660,6 +684,7 @@
<translation id="647261751007945333">Eszközházirendek</translation>
<translation id="6477321094435799029">A Chrome szokatlan kódot észlelt az oldalon, ezért letiltotta az Ön személyes adatainak (például jelszavak, telefonszámok és hitelkártyaszámok) védelme érdekében.</translation>
<translation id="6489534406876378309">Feltöltési összeomlások indítása</translation>
+<translation id="6507833130742554667">Elfogadott hitel- és bankkártyák.</translation>
<translation id="6508722015517270189">Indítsa újra a Chrome-ot</translation>
<translation id="6529602333819889595">&amp;Törlés újra</translation>
<translation id="6534179046333460208">Fizikai web – javaslatok</translation>
@@ -668,13 +693,13 @@
<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="657639383826808334">Lehet, hogy ez a tartalom megpróbál valamilyen veszélyes szoftvert telepíteni az eszközére, amely ellophatja vagy törölheti az Ön adatait. <ph name="BEGIN_LINK" />Megjelenítés mindenképpen<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Titkosítási lehetőségek</translation>
<translation id="662080504995468778">Mégse</translation>
<translation id="6626291197371920147">Adjon meg érvényes kártyaszámot</translation>
<translation id="6628463337424475685">Keresés: <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Előfordulhat, hogy a(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhely támadói olyan veszélyes programokat kísérelnek meg telepíteni az Ön Mac típusú számítógépére, amelyek ellopják vagy törlik 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="6644283850729428850">Ez a házirend már elavult.</translation>
+<translation id="6657585470893396449">Jelszó</translation>
<translation id="6671697161687535275">Eltávolítja az űrlapjavaslatot a Chromiumból?</translation>
<translation id="6685834062052613830">Kijelentkezés és a beállítás befejezése</translation>
<translation id="6710213216561001401">Előző</translation>
@@ -705,7 +730,6 @@
<translation id="6970216967273061347">Kerület</translation>
<translation id="6973656660372572881">Mindkét fix proxyszerver és egy .Pac típusú szkript URL-címe meg van adva.</translation>
<translation id="6989763994942163495">Speciális beállítások megjelenítése...</translation>
-<translation id="7000990526846637657">Nincsenek előzménybejegyzések</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Előfordulhat, hogy a böngészési előzmények más formái megtalálhatók Google-fiókjában a <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> címen</translation>
<translation id="7029809446516969842">Jelszavak</translation>
@@ -720,7 +744,9 @@
<translation id="7129409597930077180">Erre a címre nem lehetséges a szállítás. Válasszon másik címet.</translation>
<translation id="7138472120740807366">Kézbesítési mód</translation>
<translation id="7139724024395191329">Emírség</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">A fizetés nem biztonságos</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Újraindítás most</translation>
<translation id="7180611975245234373">Frissítés</translation>
<translation id="7182878459783632708">Nincsenek beállított házirendek</translation>
@@ -761,6 +787,7 @@
<translation id="7514365320538308">Letöltés</translation>
<translation id="7518003948725431193">Nem található weboldal az internetcímen:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">A webhellyel való kapcsolata nem privát</translation>
<translation id="7535087603100972091">Érték</translation>
<translation id="7537536606612762813">Kötelező</translation>
<translation id="7542403920425041731">Az igazolást követően a böngésző megosztja kártyaadatait a webhellyel.</translation>
@@ -770,7 +797,6 @@
<translation id="7552846755917812628">Próbálja ki az alábbi tippeket:</translation>
<translation id="7554791636758816595">Új lap</translation>
<translation id="7567204685887185387">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványát csalással állíthatták ki. Ennek oka lehet konfigurációs hiba, vagy hogy egy támadó eltérítette az Ön kapcsolódását.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Az oldal nyelve<ph name="ORIGINAL_LANGUAGE" />Kívánja lefordítani?</translation>
<translation id="7569952961197462199">Eltávolítja a hitelkártyát a Chrome-ból?</translation>
<translation id="7569983096843329377">Fekete</translation>
@@ -797,9 +823,11 @@
<translation id="7714464543167945231">Tanúsítvány</translation>
<translation id="7716147886133743102">Rendszergazda tiltja</translation>
<translation id="7716424297397655342">A webhely nem tölthető be a gyorsítótárból</translation>
+<translation id="774634243536837715">Veszélyes tartalom letiltva.</translation>
<translation id="7752995774971033316">Nem kezelt</translation>
<translation id="7755287808199759310">A letiltást a szülő oldhatja fel</translation>
<translation id="7758069387465995638">Előfordulhat, hogy a tűzfal vagy a vírusirtó szoftver tiltotta le a kapcsolatot.</translation>
+<translation id="7759163816903619567">Megjelenített domain:</translation>
<translation id="7761701407923456692">A szerver tanúsítványa nem egyezik az URL-lel</translation>
<translation id="7763386264682878361">Fizetésijegyzék-elemző</translation>
<translation id="7764225426217299476">Cím hozzáadása</translation>
@@ -814,6 +842,7 @@
<translation id="7815407501681723534"><ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> található a(z) „<ph name="SEARCH_STRING" />” kifejezésre</translation>
<translation id="785549533363645510">Azonban Ön nem teljesen láthatatlan. Az inkognitómód használata nem rejti el böngészési műveleteit munkáltatója, az internetszolgáltatója és a felkeresett webhelyek elől.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" />: <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Elfogadott bank- és feltöltőkártyák.</translation>
<translation id="7887683347370398519">Ellenőrizze a CVC-t, majd próbálja újra</translation>
<translation id="79338296614623784">Érvényes telefonszámot adjon meg</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -833,7 +862,6 @@
<translation id="8041089156583427627">Visszajelzés küldése</translation>
<translation id="8041940743680923270">Globális alapértelmezés használata (Megkérdezés)</translation>
<translation id="8088680233425245692">Nem sikerült megtekinteni a cikket.</translation>
-<translation id="8089520772729574115">kevesebb mint 1 MB</translation>
<translation id="8091372947890762290">Az aktiválás függőben van a szerveren</translation>
<translation id="8118489163946903409">Fizetési mód</translation>
<translation id="8131740175452115882">Megerősítés</translation>
@@ -845,10 +873,13 @@
<translation id="8194797478851900357">&amp;Áthelyezés visszavonása</translation>
<translation id="8201077131113104583">A(z) „<ph name="EXTENSION_ID" />” azonosítójú bővítmény frissítési URL-je érvénytelen.</translation>
<translation id="8202097416529803614">Rendelés összegzése</translation>
+<translation id="8205463626947051446">A webhely jellemzően tolakodó hirdetéseket jelenít meg</translation>
<translation id="8218327578424803826">Hozzárendelt helyszín:</translation>
<translation id="8225771182978767009">A számítógépet beállító személy a webhely letiltása mellett döntött.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" /> és <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Oldal megnyitása új inkognitóablakban</translation>
<translation id="8241707690549784388">A keresett oldal a megadott információt használta. Ha visszatér arra az oldalra, akkor lehet, hogy az egyszer már megtett mozdulatok ismétlésre kerülnek. Mégis továbblép?</translation>
+<translation id="8241712895048303527">Letiltás ezen a webhelyen</translation>
<translation id="8249320324621329438">Utolsó lekérés:</translation>
<translation id="8253091569723639551">A számlázási cím megadása kötelező</translation>
<translation id="8261506727792406068">Törlés</translation>
@@ -859,7 +890,6 @@
<translation id="8308427013383895095">A fordítás a hálózati kapcsolat problémája miatt nem sikerült.</translation>
<translation id="8332188693563227489">A hozzáférés megtagadva a következőhöz: <ph name="HOST_NAME" /></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="8344669043927012510">Nyissa meg az oldalt inkognitómódban (⇧⌘N)</translation>
<translation id="8349305172487531364">Könyvjelzősáv</translation>
<translation id="8363502534493474904">Repülős üzemmód kikapcsolása</translation>
<translation id="8364627913115013041">Nincs beállítva.</translation>
@@ -869,6 +899,7 @@
<translation id="8398259832188219207">A hibajelentés feltöltésének ideje: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Rendszerösszeomlások ( <ph name="CRASH_COUNT" /> )</translation>
<translation id="8412392972487953978">Mindkét alkalommal ugyanazt az összetett jelszót kell megadnia.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Beállítások</translation>
<translation id="8433057134996913067">Ezzel kijelentkezik a legtöbb webhelyről.</translation>
<translation id="8437238597147034694">&amp;Áthelyezés visszavonása</translation>
@@ -876,8 +907,9 @@
<translation id="8483780878231876732">Jelentkezzen be a Chrome-ba, hogy használhassa a kártyákat Google-fiókjából.</translation>
<translation id="8488350697529856933">A következőre érvényes</translation>
<translation id="8498891568109133222">A(z) <ph name="HOST_NAME" /> túl hosszú ideje nem válaszol.</translation>
-<translation id="8532105204136943229">Lejárat éve</translation>
+<translation id="8503813439785031346">Felhasználónév</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="8571890674111243710">Oldal fordítása erre a nyelvre: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Szám hozzáadása
@@ -885,6 +917,7 @@
<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="8620436878122366504">A szüleid még nem hagyták jóvá</translation>
<translation id="8647750283161643317">Minden visszaállítása az alapértékre</translation>
+<translation id="8660471606262461360">A Google Payments szolgáltatásból</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="8725066075913043281">Újrapróbálás</translation>
@@ -912,6 +945,7 @@
<translation id="8903921497873541725">Nagyítás</translation>
<translation id="8931333241327730545">Menti ezt a kártyát a Google-fiókjába?</translation>
<translation id="8932102934695377596">Késik az órája</translation>
+<translation id="8938939909778640821">Elfogadott hitel- és feltöltőkártyák</translation>
<translation id="8971063699422889582">A szerver tanúsítványa lejárt.</translation>
<translation id="8986494364107987395">Használati statisztikák és hibajelentések automatikus küldése a Google-nak</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -941,7 +975,6 @@
<translation id="9169664750068251925">Mindig tiltsa ezen az oldalon</translation>
<translation id="9170848237812810038">&amp;Visszavonás</translation>
<translation id="917450738466192189">A szerver tanúsítványa érvénytelen.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> és további <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">A cikk hozzáadása sikertelen.</translation>
@@ -950,9 +983,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ŰRLAP TÖRLÉSE</translation>
<translation id="939736085109172342">Új mappa</translation>
-<translation id="941721044073577244">Úgy tűnik, nincs jogosultsága a webhely felkeresésére</translation>
<translation id="969892804517981540">Hivatalos verzió</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Nincs}=1{1 elem}other{# elem}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Fejlesztői változat</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_id.xtb b/chromium/components/strings/components_strings_id.xtb
index 4bde6a8b75a..b0575a6ceb1 100644
--- a/chromium/components/strings/components_strings_id.xtb
+++ b/chromium/components/strings/components_strings_id.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Tutup aplikasi lain</translation>
<translation id="1055184225775184556">&amp;Urungkan Penambahan</translation>
<translation id="10614374240317010">Jangan pernah disimpan</translation>
+<translation id="1066396345355680611">Anda dapat kehilangan akses ke konten yang dilindungi dari <ph name="SITE" /> dan beberapa situs lain.</translation>
<translation id="106701514854093668">Bookmark Desktop</translation>
<translation id="1074497978438210769">Tidak aman</translation>
<translation id="1080116354587839789">Paskan dengan lebar</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Daftar bacaan</translation>
<translation id="1264126396475825575">Laporan kerusakan diambil pada pukul <ph name="CRASH_TIME" /> (belum diupload atau diabaikan)</translation>
<translation id="1281526147609854549">Diterbitkan oleh <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Konten berbahaya diblokir</translation>
<translation id="1285320974508926690">Jangan pernah terjemahkan situs ini</translation>
<translation id="129553762522093515">Barusan ditutup</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Coba hapus cookie Anda<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domain pendaftaran:</translation>
<translation id="1340482604681802745">Alamat pengambilan</translation>
-<translation id="1344211575059133124">Tampaknya Anda memerlukan izin untuk mengunjungi situs ini</translation>
<translation id="1344588688991793829">Setelan IsiOtomatis Chromium...</translation>
<translation id="1348198688976932919">Situs yang akan dibuka berisi aplikasi berbahaya</translation>
<translation id="1374468813861204354">saran</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identitas <ph name="ORGANIZATION" /> di <ph name="LOCALITY" /> telah diverifikasi oleh <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ya</translation>
<translation id="1430915738399379752">Cetak</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> metode lainnya}other{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> metode lainnya}}</translation>
<translation id="1506687042165942984">Tampilkan salinan tersimpan (yang diketahui telah habis masa berlakunya) dari laman ini.</translation>
<translation id="1517433312004943670">Perlu nomor telepon</translation>
+<translation id="1517500485252541695">Kartu kredit dan debit yang diterima</translation>
<translation id="1519264250979466059">Tanggal Dibuat</translation>
+<translation id="1527263332363067270">Menunggu sambungan internet...</translation>
<translation id="153384715582417236">Itu saja untuk sekarang</translation>
<translation id="1549470594296187301">JavaScript harus diaktifkan untuk menggunakan fitur ini.</translation>
<translation id="1555130319947370107">Biru</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Download halaman nanti</translation>
<translation id="17513872634828108">Buka tab</translation>
<translation id="1753706481035618306">Nomor laman</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Bidang wajib diisi</translation>
<translation id="187918866476621466">Buka halaman awal</translation>
<translation id="1883255238294161206">Ciutkan daftar</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> alamat lainnya}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> alamat lainnya}}</translation>
<translation id="1898423065542865115">Pemfilteran</translation>
+<translation id="1916770123977586577">Untuk menerapkan setelan yang telah diupdate pada situs ini, muat ulang halaman ini</translation>
+<translation id="1919345977826869612">Iklan</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Tidak ada}=1{1 situs}other{# situs}}</translation>
<translation id="194030505837763158">Buka <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Kartu prabayar yang diterima</translation>
<translation id="1962204205936693436">Bookmark <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Kesalahan serialisasi</translation>
<translation id="1974060860693918893">Lanjutan</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Kesalahan HTTP</translation>
<translation id="2270484714375784793">Nomor telepon</translation>
<translation id="2282872951544483773">Eksperimen Tidak Tersedia</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item}other{<ph name="ITEM_COUNT" /> item}}</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>
<translation id="2337852623177822836">Setelan dikontrol oleh administrator Anda</translation>
<translation id="2354001756790975382">Bookmark lain</translation>
<translation id="2354430244986887761">Baru-baru ini, Google Safe Browsing <ph name="BEGIN_LINK" />menemukan aplikasi berbahaya<ph name="END_LINK" /> di <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Lanjut</translation>
<translation id="2365563543831475020">Laporan kerusakan yang diambil pada pukul <ph name="CRASH_TIME" /> tidak diupload</translation>
<translation id="2367567093518048410">Tingkat</translation>
-<translation id="237718015863234333">Tidak tersedia alternatif UI</translation>
<translation id="2384307209577226199">Default perusahaan</translation>
<translation id="2386255080630008482">Sertifikat server telah dicabut.</translation>
<translation id="2392959068659972793">Tampilkan kebijakan tanpa nilai yang disetel</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Jika dicentang, Chromium akan menyimpan salinan kartu Anda di perangkat ini untuk pengisian formulir yang lebih cepat.</translation>
<translation id="2498091847651709837">Pindai kartu baru</translation>
<translation id="2501278716633472235">Kembali</translation>
+<translation id="2503184589641749290">Kartu prabayar dan debit yang diterima</translation>
<translation id="2515629240566999685">Periksa sinyal di area Anda</translation>
-<translation id="2516305470678292029">Alternatif UI</translation>
<translation id="2539524384386349900">Deteksi</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> mengirimkan tanggapan yang tidak valid.</translation>
<translation id="2556876185419854533">&amp;Urungkan Pengeditan</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Metode pengiriman</translation>
<translation id="277499241957683684">Catatan perangkat hilang</translation>
<translation id="2784949926578158345">Sambungan disetel ulang.</translation>
+<translation id="2788784517760473862">Kartu kredit yang diterima</translation>
<translation id="2794233252405721443">Situs diblokir</translation>
<translation id="2799020568854403057">Situs yang akan dibuka berisi aplikasi berbahaya</translation>
<translation id="2803306138276472711">Google Safe Browsing baru saja <ph name="BEGIN_LINK" />mendeteksi software perusak<ph name="END_LINK" /> di <ph name="SITE" />. Situs web yang umumnya aman terkadang terinfeksi software perusak.</translation>
<translation id="2824775600643448204">Bilah penelusuran dan alamat</translation>
<translation id="2826760142808435982">Sambungan dienkripsi dan diautentikasi menggunakan <ph name="CIPHER" /> dan menggunakan <ph name="KX" /> sebagai mekanisme pertukaran kunci.</translation>
<translation id="2835170189407361413">Hapus formulir</translation>
+<translation id="2851634818064021665">Anda memerlukan izin untuk membuka situs ini</translation>
<translation id="2856444702002559011">Penyerang mungkin berusaha mencuri informasi Anda dari <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (misalnya, sandi, pesan, atau kartu kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Jangan Muat Ulang</translation>
-<translation id="2900469785430194048">Google Chrome kehabisan memori saat mencoba menampilkan laman web ini.</translation>
<translation id="2909946352844186028">Perubahan jaringan terdeteksi.</translation>
<translation id="2916038427272391327">Tutup program lain</translation>
<translation id="2922350208395188000">Sertifikat server tidak dapat diperiksa.</translation>
<translation id="2928905813689894207">Alamat Penagihan</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> lainnya}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> lainnya}}</translation>
<translation id="2941952326391522266">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya dari <ph name="DOMAIN2" />. Hal ini disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation>
<translation id="2948083400971632585">Anda dapat menonaktifkan proxy apa pun yang dikonfigurasi untuk sambungan dari laman setelan.</translation>
<translation id="2955913368246107853">Tutup bilah cari</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Jika diaktifkan, Chromium akan menyimpan salinan kartu Anda di perangkat ini untuk pengisian formulir yang lebih cepat.</translation>
<translation id="2985398929374701810">Masukkan alamat yang valid</translation>
<translation id="2986368408720340940">Metode pengambilan tidak tersedia. Coba metode lain.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Halaman yang Anda lihat di tab penyamaran tidak akan disimpan dalam riwayat browser, penyimpanan cookie, atau riwayat penelusuran setelah Anda menutup semua tab penyamaran. File apa pun yang didownload atau bookmark yang dibuat akan tetap tersimpan.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Pulau</translation>
-<translation id="317583078218509884">Setelan izin situs baru akan berlaku setelah laman dimuat ulang.</translation>
<translation id="3176929007561373547">Periksa setelan proxy atau hubungi administrator jaringan untuk
memastikan bahwa server proxy bekerja. Jika Anda tidak yakin harus
menggunakan server proxy:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Buka halaman dalam Mode penyamaran</translation>
<translation id="320323717674993345">Batalkan Pembayaran</translation>
<translation id="3207960819495026254">Diberi bookmark</translation>
<translation id="3225919329040284222">Server menunjukkan sertifikat yang tidak sesuai dengan harapan terpasang. Harapan ini disertakan untuk situs web tertentu dengan keamanan tinggi guna melindungi Anda.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Urungkan Pengaturan Ulang</translation>
<translation id="3282497668470633863">Tambahkan nama di kartu</translation>
<translation id="3286538390144397061">Mulai Ulang Sekarang</translation>
+<translation id="3287510313208355388">Download saat online</translation>
<translation id="3303855915957856445">Hasil penelusuran tidak ditemukan</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Interval pengambilan:</translation>
<translation id="3462200631372590220">Sembunyikan lanjutan</translation>
<translation id="3467763166455606212">Diperlukan nama pemegang kartu</translation>
-<translation id="3478058380795961209">Bulan Masa Berlaku Habis</translation>
<translation id="3479539252931486093">Apakah hal ini tidak diharapkan? <ph name="BEGIN_LINK" />Beri tahu kami<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Jangan sekarang</translation>
<translation id="3498215018399854026">Orang tua Anda saat ini tidak dapat dihubungi. Coba lagi.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Harap sesuaikan tanggal dan waktu di bagian &lt;strong&gt;Umum&lt;/strong&gt; pada aplikasi &lt;strong&gt;Setelan&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kartu kredit dan prabayar diterima.</translation>
<translation id="3582930987043644930">Tambahkan nama</translation>
<translation id="3583757800736429874">&amp;Ulangi Pemindahan</translation>
<translation id="3586931643579894722">Sembunyikan detail</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Jika Anda sering melihatnya, coba <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisi</translation>
<translation id="3678029195006412963">Permintaan tidak dapat ditandatangani</translation>
+<translation id="3678529606614285348">Buka halaman dalam Jendela samaran baru (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Laporan kerusakan direkam pada <ph name="CRASH_TIME" />, diupload pada <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informasi sertifikat</translation>
<translation id="3690164694835360974">Proses masuk tidak aman</translation>
-<translation id="3693415264595406141">Sandi:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Memuat...</translation>
<translation id="3712624925041724820">Lisensi habis</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Penyerang di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> dapat mengelabui Anda agar melakukan tindakan yang berbahaya seperti menginstal software atau mengungkap informasi pribadi Anda (misalnya sandi, nomor telepon, atau kartu kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Terjemahan gagal karena kesalahan server.</translation>
<translation id="3759461132968374835">Tidak ada laporan kondisi ngadat saat ini. Kondisi ngadat yang terjadi saat pelaporan kondisi ngadat tidak diaktifkan tidak akan tampil di sini.</translation>
+<translation id="3765032636089507299">Halaman Safe Browsing sedang diperbaiki.</translation>
<translation id="3778403066972421603">Ingin menyimpan kartu ini di Akun Google dan perangkat ini?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Masa berlaku <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Permintaan Anda untuk mengakses situs ini telah dikirim ke <ph name="NAME" /></translation>
<translation id="3890664840433101773">Tambahkan email</translation>
<translation id="3901925938762663762">Kartu telah habis masa berlakunya</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID Laporan Error yang Diupload <ph name="CRASH_ID" /> (ID Error Lokal: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya tidak menyebutkan Nama Alternatif Subjek. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation>
+<translation id="3949601375789751990">Histori browsing Anda muncul di sini</translation>
<translation id="3963721102035795474">Mode Pembaca</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Tidak ada}=1{Dari 1 situs }other{Dari # situs }}</translation>
<translation id="397105322502079400">Menghitung...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Setelan situs</translation>
<translation id="4169947484918424451">Ingin Chromium menyimpan kartu ini?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Penyerang di situs ini mungkin berusaha memasang program berbahaya di komputer Anda yang dapat mencuri atau menghapus informasi (misalnya, foto, sandi, pesan, dan kartu kredit).</translation>
<translation id="4372948949327679948">Nilai <ph name="VALUE_TYPE" /> yang diharapkan.</translation>
<translation id="4377125064752653719">Anda berusaha menjangkau <ph name="DOMAIN" />, tetapi sertifikat yang disajikan oleh server telah dibatalkan oleh penerbitnya. Artinya informasi rahasia keamanan yang disajikan tidak dapat dipercaya. Anda mungkin sedang berkomunikasi dengan penyerang.</translation>
-<translation id="4381091992796011497">Nama Pengguna:</translation>
<translation id="4394049700291259645">Nonaktifkan</translation>
<translation id="4406896451731180161">hasil penelusuran</translation>
<translation id="4424024547088906515">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya tidak dipercaya oleh Chrome. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> tidak menerima sertifikat masuk Anda, atau sertifikat masuk mungkin tidak diberikan.</translation>
<translation id="443673843213245140">Penggunaan proxy dinonaktifkan tetapi konfigurasi proxy yang eksplisit ditentukan.</translation>
+<translation id="445100540951337728">Kartu kredit yang diterima</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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Hapus alamat dari Chrome?</translation>
<translation id="4592951414987517459">Sambungan Anda ke <ph name="DOMAIN" /> dienkripsi menggunakan cipher suite modern.</translation>
<translation id="4594403342090139922">&amp;Urungkan Penghapusan</translation>
+<translation id="4611292653554630842">Login</translation>
<translation id="4619615317237390068">Tab dari perangkat lain</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya berisi kesalahan. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation>
<translation id="4690462567478992370">Berhenti menggunakan sertifikat yang tidak valid</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Sambungan Anda terganggu</translation>
+<translation id="471880041731876836">Anda tidak memiliki izin untuk membuka situs ini</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Jalankan Diagnostik Jaringan Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Muat ulang kebijakan</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Konten ini mungkin mencoba mengelabui Anda agar menginstal software atau mengungkap informasi pribadi. <ph name="BEGIN_LINK" />Tampilkan saja<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Telusuri riwayat</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Perlu autentikasi</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{dan 1 laman web lainnya}other{dan # laman web lainnya}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Laman ini telah diterjemahkan dari bahasa yang tidak diketahui ke bahasa <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pembayaran</translation>
<translation id="4926049483395192435">Harus ditentukan.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Tindakan</translation>
<translation id="4958444002117714549">Luaskan daftar</translation>
<translation id="4974590756084640048">Aktifkan kembali peringatan</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Sandi salah</translation>
<translation id="5056549851600133418">Artikel untuk Anda</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Periksa alamat proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Anda dapat kehilangan akses ke konten yang dilindungi dari beberapa situs.</translation>
<translation id="5087286274860437796">Sertifikat server saat ini tidak valid.</translation>
<translation id="5087580092889165836">Tambahkan kartu</translation>
<translation id="5089810972385038852">Negara bagian</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provinsi</translation>
<translation id="5115563688576182185">(64 bit)</translation>
<translation id="5141240743006678641">Enkripsikan sandi yang disinkronkan dengan kredensial Google Anda</translation>
-<translation id="514421653919133810">Buka halaman dalam Mode penyamaran (Ctrl+Shift+N)</translation>
<translation id="5145883236150621069">Ada kode kesalahan dalam tanggapan kebijakan</translation>
+<translation id="5159010409087891077">Buka halaman dalam Jendela samaran baru (⇧⌘N)</translation>
<translation id="5171045022955879922">Telusuri atau ketik URL</translation>
<translation id="5172758083709347301">Mesin</translation>
<translation id="5179510805599951267">Bukan <ph name="ORIGINAL_LANGUAGE" />? Laporkan kesalahan deteksi ini</translation>
-<translation id="5181140330217080051">Mendownload</translation>
<translation id="5190835502935405962">Bilah Bookmark</translation>
<translation id="5199729219167945352">Eksperimen</translation>
<translation id="5205222826937269299">Nama wajib diisi</translation>
<translation id="5222812217790122047">Email wajib diisi</translation>
<translation id="5251803541071282808">Awan</translation>
<translation id="5277279256032773186">Menggunakan Chrome di kantor? Perusahaan dapat mengelola setelan Chrome untuk karyawan mereka. Pelajari lebih lanjut</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Ikuti langkah-langkah berikut untuk menonaktifkan software sementara waktu, sehingga Anda dapat online. Anda memerlukan hak istimewa administrator.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klik <ph name="BEGIN_BOLD" />Mulai<ph name="END_BOLD" />, lalu telusuri dan pilih <ph name="BEGIN_BOLD" />"Tampilkan layanan lokal"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Pilih <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Pada <ph name="BEGIN_BOLD" />Jenis startup<ph name="END_BOLD" />, pilih <ph name="BEGIN_BOLD" />Nonaktif<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Pada <ph name="BEGIN_BOLD" />Status layanan<ph name="END_BOLD" />, klik <ph name="BEGIN_BOLD" />Berhenti<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Klik <ph name="BEGIN_BOLD" />Terapkan<ph name="END_BOLD" />, lalu klik <ph name="BEGIN_BOLD" />Oke<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Buka <ph name="BEGIN_LEARN_MORE_LINK" />pusat bantuan Chrome<ph name="END_LEARN_MORE_LINK" /> untuk mempelajari cara menghapus software secara permanen dari komputer
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Sambungan Anda ke situs ini tidak bersifat pribadi. Untuk keluar dari mode VR setiap saat, copot headset dan tekan kembali.</translation>
<translation id="5299298092464848405">Kebijakan kesalahan penguraian</translation>
<translation id="5308689395849655368">Pelaporan kondisi ngadat dinonaktifkan.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Kesalahan validasi skema di "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Laman <ph name="HOST_NAME" /> ini tidak dapat ditemukan</translation>
<translation id="5455374756549232013">Stempel waktu kebijakan salah</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> dari <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Tidak valid</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> lainnya}other{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> lainnya}}</translation>
<translation id="5470861586879999274">&amp;Ulangi pengeditan</translation>
+<translation id="5481076368049295676">Konten ini mungkin mencoba menginstal software berbahaya yang mencuri atau menghapus informasi Anda di perangkat. <ph name="BEGIN_LINK" />Tetap tampilkan<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Tambahkan alamat yang valid</translation>
<translation id="5492298309214877701">Situs yang ada di intranet perusahaan, organisasi, atau sekolah ini memiliki URL yang sama dengan situs web eksternal.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Tidak dapat menemukan artikel yang diminta.</translation>
<translation id="5544037170328430102">Laman tersemat di <ph name="SITE" /> menyatakan:</translation>
<translation id="5556459405103347317">Muat ulang</translation>
+<translation id="5560088892362098740">Tanggal Habis Masa Berlaku</translation>
<translation id="5565735124758917034">Aktif</translation>
<translation id="5571083550517324815">Tidak dapat mengambil dari alamat ini. Pilih alamat lain.</translation>
<translation id="5572851009514199876">Mulai dan login ke Chrome agar Chrome dapat memeriksa apakah Anda diizinkan untuk mengakses situs ini atau tidak.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Gagal memuat setelan kebijakan</translation>
<translation id="5631439013527180824">Token pengelolaan perangkat tidak valid</translation>
<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="5646376287012673985">Lokasi</translation>
<translation id="5659593005791499971">Email</translation>
<translation id="5669703222995421982">Mendapatkan konten hasil personalisasi</translation>
<translation id="5675650730144413517">Halaman ini tidak berfungsi</translation>
<translation id="5710435578057952990">Identitas situs Web ini belum diverifikasi.</translation>
-<translation id="5713016350996637505">Konten penipuan diblokir</translation>
+<translation id="5719499550583120431">Kartu prabayar diterima.</translation>
<translation id="5720705177508910913">Pengguna saat ini</translation>
<translation id="5732392974455271431">Orang tua dapat membuka blokirnya untukmu</translation>
<translation id="5763042198335101085">Masukkan alamat email yang valid</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Ingin mengisi <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Sambungan Anda ke <ph name="DOMAIN" /> dienkripsi menggunakan cipher suite yang sudah usang.</translation>
<translation id="5813119285467412249">&amp;Ulangi Penambahan</translation>
-<translation id="5814352347845180253">Anda dapat kehilangan akses ke konten premium dari <ph name="SITE" /> dan beberapa situs lain.</translation>
<translation id="5838278095973806738">Jangan masukkan informasi sensitif apa pun di situs ini (misalnya, sandi atau kartu kredit), karena penyerang dapat mencurinya.</translation>
<translation id="5869405914158311789">Situs ini tidak dapat dijangkau</translation>
<translation id="5869522115854928033">Sandi tersimpan</translation>
<translation id="5872918882028971132">Saran Induk</translation>
+<translation id="5893752035575986141">Kartu kredit diterima.</translation>
<translation id="5901630391730855834">Kuning</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (disinkronkan)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 terpakai}other{# terpakai}}</translation>
-<translation id="5926846154125914413">Anda dapat kehilangan akses ke konten premium dari beberapa situs.</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="5967867314010545767">Hapus dari riwayat</translation>
<translation id="5975083100439434680">Perkecil</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Tutup</translation>
<translation id="6042308850641462728">Lainnya</translation>
<translation id="6047233362582046994">Jika Anda memahami risiko keamanan tersebut, Anda dapat <ph name="BEGIN_LINK" />mengunjungi situs ini<ph name="END_LINK" /> sebelum aplikasi berbahaya tersebut dihapus.</translation>
+<translation id="6047927260846328439">Konten ini mungkin mencoba mengelabui Anda agar menginstal software atau mengungkapkan informasi pribadi. <ph name="BEGIN_LINK" />Tetap tampilkan<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Anda tidak dapat membuka <ph name="SITE" /> sekarang karena situs menggunakan penyematan sertifikat. Error jaringan dan serangan biasanya bersifat sementara, sehingga halaman ini mungkin akan berfungsi nanti.</translation>
<translation id="6060685159320643512">Hati-hati, eksperimen ini dapat menimbulkan masalah</translation>
<translation id="6080696365213338172">Anda telah mengakses konten menggunakan sertifikat yang diberikan oleh administrator. Data yang diberikan ke <ph name="DOMAIN" /> dapat dicegat oleh administrator Anda.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Pelajari lebih lanjut</translation>
<translation id="6169916984152623906">Anda kini dapat mengakses secara rahasia, dan orang lain yang menggunakan perangkat ini tidak akan melihat aktivitas Anda. Namun, hasil download dan bookmark akan disimpan.</translation>
<translation id="6177128806592000436">Sambungan Anda ke situs ini tidak aman</translation>
-<translation id="6184817833369986695">(kelompok: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Periksa sambungan internet Anda</translation>
<translation id="6218753634732582820">Hapus alamat dari Chromium?</translation>
<translation id="6221345481584921695">Google Safe Browsing baru saja <ph name="BEGIN_LINK" />mendeteksi software perusak<ph name="END_LINK" /> di <ph name="SITE" />. Situs web yang biasanya aman terkadang terinfeksi software perusak. Konten berbahaya datang dari <ph name="SUBRESOURCE_HOST" />, yang dikenal luas sebagai distributor software perusak.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Tertarik dengan fitur Chrome baru yang keren? Coba saluran beta kami di chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium kehabisan memori saat mencoba menampilkan laman web ini.</translation>
+<translation id="6397451950548600259">Software di komputer menghentikan Chrome agar tidak terhubung dengan aman ke web</translation>
<translation id="6404511346730675251">Edit bookmark</translation>
<translation id="6410264514553301377">Masukkan tanggal kedaluwarsa dan CVC <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Kamu telah meminta izin kepada orang tua untuk mengunjungi situs ini</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Kebijakan perangkat</translation>
<translation id="6477321094435799029">Chrome mendeteksi kode yang tidak biasa pada halaman ini dan memblokirnya untuk melindungi informasi pribadi Anda (misalnya, sandi, nomor telepon, dan kartu kredit).</translation>
<translation id="6489534406876378309">Mulai mengupload kerusakan</translation>
+<translation id="6507833130742554667">Kartu kredit dan debit diterima.</translation>
<translation id="6508722015517270189">Buka Ulang Chrome</translation>
<translation id="6529602333819889595">&amp;Ulangi Penghapusan</translation>
<translation id="6534179046333460208">Saran Web Fisik</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Konten ini mungkin mencoba menginstal software berbahaya di perangkat yang mencuri atau menghapus informasi Anda. <ph name="BEGIN_LINK" />Tampilkan saja<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opsi enkripsi</translation>
<translation id="662080504995468778">Tinggal</translation>
<translation id="6626291197371920147">Tambahkan kartu yang valid</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Penelusuran</translation>
<translation id="6630809736994426279">Saat ini, penyerang di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha menginstal program berbahaya di Mac 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="6644283850729428850">Kebijakan ini telah usang.</translation>
+<translation id="6657585470893396449">Sandi</translation>
<translation id="6671697161687535275">Hapus saran formulir dari Chromium?</translation>
<translation id="6685834062052613830">Keluar dan selesaikan penyiapan</translation>
<translation id="6710213216561001401">Sebelumnya</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Distrik</translation>
<translation id="6973656660372572881">Server proxy tetap dan URL skrip .pac telah ditentukan.</translation>
<translation id="6989763994942163495">Tampilkan setelan lanjutan...</translation>
-<translation id="7000990526846637657">Entri riwayat tidak ditemukan</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Akun Google Anda mungkin memiliki bentuk riwayat penjelajahan lainnya di <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Sandi</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Tidak dapat mengirim ke alamat ini. Pilih alamat lain.</translation>
<translation id="7138472120740807366">Metode pengiriman</translation>
<translation id="7139724024395191329">Emirate</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> lainnya}other{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> lainnya}}</translation>
<translation id="7155487117670177674">Pembayaran tidak aman</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> lainnya}other{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> lainnya}}</translation>
<translation id="7179921470347911571">Luncurkan Ulang Sekarang</translation>
<translation id="7180611975245234373">Segarkan</translation>
<translation id="7182878459783632708">Tidak ada kebijakan yang disetel</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Download</translation>
<translation id="7518003948725431193">Tidak ada laman web yang ditemukan untuk alamat web:<ph name="URL" /></translation>
<translation id="7521387064766892559">Javascript</translation>
+<translation id="7526934274050461096">Koneksi Anda ke situs ini tidak bersifat pribadi</translation>
<translation id="7535087603100972091">Nilai</translation>
<translation id="7537536606612762813">Wajib</translation>
<translation id="7542403920425041731">Setelah mengonfirmasi, detail kartu Anda akan dibagikan dengan situs ini.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Coba tips berikut:</translation>
<translation id="7554791636758816595">Tab Baru</translation>
<translation id="7567204685887185387">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya mungkin telah dikeluarkan dengan curang. Hal ini disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> kontak lainnya}other{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> kontak lainnya}}</translation>
<translation id="7568593326407688803">Laman ini dalam bahasa<ph name="ORIGINAL_LANGUAGE" />Ingin diterjemahkan?</translation>
<translation id="7569952961197462199">Hapus kartu kredit dari Chrome?</translation>
<translation id="7569983096843329377">Hitam</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Sertifikat</translation>
<translation id="7716147886133743102">Diblokir oleh administrator</translation>
<translation id="7716424297397655342">Situs ini tidak dapat dimuat dari cache</translation>
+<translation id="774634243536837715">Konten berbahaya diblokir.</translation>
<translation id="7752995774971033316">Tidak terkelola</translation>
<translation id="7755287808199759310">Orang tua dapat membuka blokirnya untukmu</translation>
<translation id="7758069387465995638">Software antivirus atau firewall mungkin memblokir sambungan.</translation>
+<translation id="7759163816903619567">Tampilkan domain:</translation>
<translation id="7761701407923456692">Sertifikat server tidak cocok dengan URL.</translation>
<translation id="7763386264682878361">Parser Manifes Pembayaran</translation>
<translation id="7764225426217299476">Tambahkan alamat</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Ditemukan <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> hasil untuk '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">Namun, Anda masih dapat terlihat. Masuk ke mode penyamaran tidak menyembunyikan penjelajahan Anda dari atasan, penyedia layanan internet, atau situs web yang Anda kunjungi.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Kartu debit dan prabayar diterima.</translation>
<translation id="7887683347370398519">Periksa CVC dan coba lagi</translation>
<translation id="79338296614623784">Masukkan nomor telepon yang valid</translation>
<translation id="7935318582918952113">Penyaring DOM</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Kirim Masukan</translation>
<translation id="8041940743680923270">Gunakan default global (Tanyakan)</translation>
<translation id="8088680233425245692">Gagal melihat artikel.</translation>
-<translation id="8089520772729574115">kurang dari 1 MB</translation>
<translation id="8091372947890762290">Aktivasi ditunda di server</translation>
<translation id="8118489163946903409">Metode pembayaran</translation>
<translation id="8131740175452115882">Konfirmasi</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Urungkan Pemindahan</translation>
<translation id="8201077131113104583">URL pembaruan tidak valid untuk ekstensi dengan ID "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Ringkasan pesanan</translation>
+<translation id="8205463626947051446">Situs cenderung menampilkan iklan mengganggu</translation>
<translation id="8218327578424803826">Lokasi yang Ditetapkan:</translation>
<translation id="8225771182978767009">Orang yang menyiapkan komputer ini telah memilih untuk memblokir situs ini.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Buka halaman dalam tab Penyamaran baru</translation>
<translation id="8241707690549784388">Laman yang dicari menggunakan informasi yang Anda masukkan. Kembali ke laman tersebut dapat menyebabkan pengulangan tindakan apa pun yang Anda lakukan. Apakah Anda ingin melanjutkan?</translation>
+<translation id="8241712895048303527">Blokir di situs ini</translation>
<translation id="8249320324621329438">Terakhir diambil:</translation>
<translation id="8253091569723639551">Perlu alamat penagihan</translation>
<translation id="8261506727792406068">Hapus</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Terjemahan gagal karena ada masalah dengan koneksi jaringan.</translation>
<translation id="8332188693563227489">Akses ke <ph name="HOST_NAME" /> ditolak</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="8344669043927012510">Buka halaman dalam Mode penyamaran (⇧⌘N)</translation>
<translation id="8349305172487531364">Bilah bookmark</translation>
<translation id="8363502534493474904">Nonaktifkan mode pesawat</translation>
<translation id="8364627913115013041">Tidak disetel.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Laporan kerusakan diupload pada <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Ngadat (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Anda harus memasukkan frasa sandi yang sama dua kali.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Setelan</translation>
<translation id="8433057134996913067">Tindakan ini akan mengeluarkan Anda dari sebagian besar situs web.</translation>
<translation id="8437238597147034694">&amp;Urungkan pemindahan</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Untuk menggunakan kartu dari Akun Google Anda, masuk ke Chrome</translation>
<translation id="8488350697529856933">Berlaku untuk</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> membutuhkan terlalu banyak waktu untuk merespons.</translation>
-<translation id="8532105204136943229">Tahun Masa Berlaku Habis</translation>
+<translation id="8503813439785031346">Nama Pengguna</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 laman tidak dapat ditentukan.</translation>
<translation id="8571890674111243710">Menerjemahkan laman 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="8620436878122366504">Orang tuamu belum menyetujuinya</translation>
<translation id="8647750283161643317">Setel ulang semua ke default</translation>
+<translation id="8660471606262461360">Dari Google Payments</translation>
<translation id="8703575177326907206">Sambungan ke <ph name="DOMAIN" /> tidak dienkripsi.</translation>
<translation id="8718314106902482036">Pembayaran belum selesai</translation>
<translation id="8725066075913043281">Coba lagi</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Perbesar</translation>
<translation id="8931333241327730545">Ingin menyimpan kartu ini ke Akun Google Anda?</translation>
<translation id="8932102934695377596">Setelan waktu Anda terlalu lambat</translation>
+<translation id="8938939909778640821">Kartu kredit dan prabayar yang diterima</translation>
<translation id="8971063699422889582">Sertifikat server telah kedaluwarsa.</translation>
<translation id="8986494364107987395">Kirim statistik penggunaan dan laporan kerusakan ke Google secara otomatis</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Selalu cekal di situs ini</translation>
<translation id="9170848237812810038">&amp;Urung</translation>
<translation id="917450738466192189">Sertifikat server tidak valid.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> opsi pengiriman lainnya}other{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> opsi pengiriman lainnya}}</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>
<translation id="9207861905230894330">Gagal menambahkan artikel.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">HAPUS FORMULIR</translation>
<translation id="939736085109172342">Folder baru</translation>
-<translation id="941721044073577244">Tampaknya Anda tidak memiliki izin untuk mengunjungi situs ini</translation>
<translation id="969892804517981540">Pembuatan Resmi</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Tidak ada}=1{1 item}other{# item}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Buatan Pengembang</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_it.xtb b/chromium/components/strings/components_strings_it.xtb
index 2ccc2d35bd2..92268665c0d 100644
--- a/chromium/components/strings/components_strings_it.xtb
+++ b/chromium/components/strings/components_strings_it.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Chiudi altre app</translation>
<translation id="1055184225775184556">&amp;Annulla aggiunta</translation>
<translation id="10614374240317010">Mai salvate</translation>
+<translation id="1066396345355680611">Potresti non riuscire più ad accedere ai contenuti protetti del sito <ph name="SITE" /> e di altri siti.</translation>
<translation id="106701514854093668">Preferiti desktop</translation>
<translation id="1074497978438210769">Non sicuro</translation>
<translation id="1080116354587839789">Adatta in larghezza</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Elenco di lettura</translation>
<translation id="1264126396475825575">Rapporto sugli arresti anomali generato il giorno <ph name="CRASH_TIME" /> (non ancora caricato o ignorato)</translation>
<translation id="1281526147609854549">Emesso da <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Contenuti pericolosi bloccati</translation>
<translation id="1285320974508926690">Non tradurre mai questo sito</translation>
<translation id="129553762522093515">Chiuse di recente</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Prova a cancellare i cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Dominio registrazione:</translation>
<translation id="1340482604681802745">Indirizzo di ritiro</translation>
-<translation id="1344211575059133124">Sembra che ti occorra l'autorizzazione per poter visitare il sito</translation>
<translation id="1344588688991793829">Impostazioni di Compilazione automatica Chromium...</translation>
<translation id="1348198688976932919">Il sito che stai per visitare contiene app pericolose</translation>
<translation id="1374468813861204354">suggerimenti</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">L'identità di <ph name="ORGANIZATION" /> con sede a <ph name="LOCALITY" /> è stata verifica da <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Sì</translation>
<translation id="1430915738399379752">Stampa</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> altro}other{<ph name="PAYMENT_METHOD_PREVIEW" /> e altri <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Mostra una copia salvata (cioè che è noto sia obsoleta) di questa pagina.</translation>
<translation id="1517433312004943670">Numero di telefono obbligatorio</translation>
+<translation id="1517500485252541695">Carte di credito e di debito accettate</translation>
<translation id="1519264250979466059">Data build</translation>
+<translation id="1527263332363067270">In attesa di connessione…</translation>
<translation id="153384715582417236">Per il momento è tutto</translation>
<translation id="1549470594296187301">JavaScript deve essere attivato per utilizzare questa funzione.</translation>
<translation id="1555130319947370107">Blu</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Scarica la pagina più tardi</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Campo obbligatorio</translation>
<translation id="187918866476621466">Apri pagine iniziali</translation>
<translation id="1883255238294161206">Comprimi elenco</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> altro}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e altri <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtri</translation>
+<translation id="1916770123977586577">Ricarica la pagina per applicare le impostazioni aggiornate a questo sito</translation>
+<translation id="1919345977826869612">Annunci</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Nessuno}=1{1 sito}other{# siti}}</translation>
<translation id="194030505837763158">Vai al link: <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Carte prepagate accettate</translation>
<translation id="1962204205936693436">Segnalibri di <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Errore di serializzazione</translation>
<translation id="1974060860693918893">Avanzate</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Errore HTTP</translation>
<translation id="2270484714375784793">Numero di telefono</translation>
<translation id="2282872951544483773">Esperimenti non disponibili</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> elemento}other{<ph name="ITEM_COUNT" /> elementi}}</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>
<translation id="2337852623177822836">Impostazione controllata dall'amministratore</translation>
<translation id="2354001756790975382">Altri Preferiti</translation>
<translation id="2354430244986887761">La funzione Navigazione sicura di Google ha recentemente <ph name="BEGIN_LINK" />rilevato app dannose<ph name="END_LINK" /> sul sito <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continua</translation>
<translation id="2365563543831475020">Il rapporto sugli arresti anomali generato il giorno <ph name="CRASH_TIME" /> non è stato caricato</translation>
<translation id="2367567093518048410">Livello</translation>
-<translation id="237718015863234333">Nessuna alternativa UI disponibile</translation>
<translation id="2384307209577226199">Valore predefinito aziendale</translation>
<translation id="2386255080630008482">Il certificato del server è stato revocato.</translation>
<translation id="2392959068659972793">Mostra norme senza valori</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Se questa opzione viene selezionata, Chromium memorizza una copia della carta sul dispositivo per velocizzare la compilazione dei moduli.</translation>
<translation id="2498091847651709837">Esegui scansione nuova carta</translation>
<translation id="2501278716633472235">Indietro</translation>
+<translation id="2503184589641749290">Carte di debito e prepagate accettate</translation>
<translation id="2515629240566999685">Controllare il segnale nella tua area</translation>
-<translation id="2516305470678292029">Alternative UI</translation>
<translation id="2539524384386349900">Rileva</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> ha inviato una risposta non valida.</translation>
<translation id="2556876185419854533">&amp;Annulla modifica</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Metodo di spedizione</translation>
<translation id="277499241957683684">Record del dispositivo mancante</translation>
<translation id="2784949926578158345">La connessione è stata reimpostata.</translation>
+<translation id="2788784517760473862">Carte di credito accettate</translation>
<translation id="2794233252405721443">Sito bloccato</translation>
<translation id="2799020568854403057">Il sito che stai per visitare contiene app dannose</translation>
<translation id="2803306138276472711">La funzione Navigazione sicura di Google ha <ph name="BEGIN_LINK" />rilevato malware<ph name="END_LINK" /> di recente sul sito <ph name="SITE" />. I siti web che in genere sono sicuri a volte vengono infettati da malware.</translation>
<translation id="2824775600643448204">Barra degli indirizzi e di ricerca</translation>
<translation id="2826760142808435982">La connessione è stata criptata e autenticata utilizzando <ph name="CIPHER" /> e <ph name="KX" /> come meccanismo di scambio delle chiavi.</translation>
<translation id="2835170189407361413">Cancella modulo</translation>
+<translation id="2851634818064021665">Ti occorre l'autorizzazione per poter visitare questo sito</translation>
<translation id="2856444702002559011">Gli utenti malintenzionati potrebbero provare a carpire le tue informazioni da <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ad esempio, password, messaggi o carte di credito). <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Non ricaricare</translation>
-<translation id="2900469785430194048">Google Chrome ha esaurito la memoria mentre cercava di visualizzare questa pagina web.</translation>
<translation id="2909946352844186028">È stato rilevato un cambio di rete.</translation>
<translation id="2916038427272391327">Chiudi altri programmi</translation>
<translation id="2922350208395188000">Il certificato del server non può essere verificato.</translation>
<translation id="2928905813689894207">Indirizzo di fatturazione</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> altro}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e altri <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza proviene da <ph name="DOMAIN2" />. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Se questa opzione viene attivata, Chromium memorizza una copia della carta sul dispositivo per velocizzare la compilazione dei moduli.</translation>
<translation id="2985398929374701810">Inserisci un indirizzo valido</translation>
<translation id="2986368408720340940">Questo metodo di ritiro non è disponibile. Prova un metodo diverso.</translation>
@@ -277,9 +281,7 @@
<translation id="3167968892399408617">Le pagine visualizzate nelle schede in incognito non vengono memorizzate nella cronologia del browser, nell'archivio di cookie o nella cronologia delle ricerche dopo avere chiuso tutte le schede in incognito. I file scaricati o i preferiti creati verranno conservati.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Isola</translation>
-<translation id="317583078218509884">Le nuove impostazioni delle autorizzazioni per i siti avranno effetto una volta ricaricata la pagina.</translation>
<translation id="3176929007561373547">Controlla le impostazioni del proxy o contatta il tuo amministratore di rete per verificare che il server proxy funzioni. Se non ritieni di dover utilizzare un server proxy: <ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Apri la pagina in modalità di navigazione in incognito</translation>
<translation id="320323717674993345">Annulla pagamento</translation>
<translation id="3207960819495026254">Aggiunto ai Preferiti</translation>
<translation id="3225919329040284222">Il server ha presentato un certificato che non corrisponde alle previsioni integrate. Queste previsioni sono incluse per determinati siti web con protezione elevata allo scopo di proteggerti.</translation>
@@ -292,6 +294,7 @@
<translation id="3270847123878663523">&amp;Annulla ridisposizione</translation>
<translation id="3282497668470633863">Aggiungi il nome indicato sulla carta</translation>
<translation id="3286538390144397061">Riavvia adesso</translation>
+<translation id="3287510313208355388">Scarica quando è online</translation>
<translation id="3303855915957856445">Nessun risultato di ricerca trovato</translation>
<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>
@@ -324,7 +327,6 @@
<translation id="3452404311384756672">Intervallo recupero:</translation>
<translation id="3462200631372590220">Nascondi avanzate</translation>
<translation id="3467763166455606212">Nome titolare carta obbligatorio</translation>
-<translation id="3478058380795961209">Mese di scadenza</translation>
<translation id="3479539252931486093">Non era previsto? <ph name="BEGIN_LINK" />Contattaci<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Non adesso</translation>
<translation id="3498215018399854026">In questo momento, non è possibile raggiungere il tuo genitore. Riprova.</translation>
@@ -340,6 +342,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Le carte di credito e prepagate sono accettate.</translation>
<translation id="3582930987043644930">Aggiungi nome</translation>
<translation id="3583757800736429874">&amp;Ripeti spostamento</translation>
<translation id="3586931643579894722">Nascondi dettagli</translation>
@@ -355,10 +358,10 @@
<translation id="3655670868607891010">Se questo problema si verifica spesso, prova questi <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisione</translation>
<translation id="3678029195006412963">Impossibile firmare la richiesta</translation>
+<translation id="3678529606614285348">Apri la pagina in un'altra finestra di navigazione in incognito (CTRL-MAIUSC-N)</translation>
<translation id="3679803492151881375">Rapporto sugli arresti anomali generato in data <ph name="CRASH_TIME" />, caricato in data <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informazioni certificato</translation>
<translation id="3690164694835360974">Accesso non sicuro</translation>
-<translation id="3693415264595406141">Password:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Caricamento in corso...</translation>
<translation id="3712624925041724820">Licenze esaurite</translation>
@@ -370,6 +373,7 @@
<translation id="3748148204939282805">Gli utenti malintenzionati presenti sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero indurti con l'inganno a effettuare operazioni pericolose, ad esempio a installare software o a rivelare informazioni personali (ad esempio, password, numeri di telefono o carte di credito). <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">La traduzione non è riuscita a causa di un errore del server.</translation>
<translation id="3759461132968374835">Non hai segnalato arresti anomali di recente. Quelli che si sono verificati quando la segnalazione degli arresti anomali era disabilitata non verranno visualizzati qui.</translation>
+<translation id="3765032636089507299">La pagina Navigazione sicura è in fase di realizzazione.</translation>
<translation id="3778403066972421603">Vuoi salvare questa carta nel tuo account Google e su questo dispositivo?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Data di scadenza: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -382,8 +386,10 @@
<translation id="3886446263141354045">La tua richiesta di accesso al sito è stata inviata a <ph name="NAME" /></translation>
<translation id="3890664840433101773">Aggiungi email</translation>
<translation id="3901925938762663762">La carta è scaduta</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID rapporto sugli arresti anomali caricato <ph name="CRASH_ID" /> (ID arresto anomalo locale: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza non contiene nomi alternativi del soggetto. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation>
+<translation id="3949601375789751990">La cronologia di navigazione viene mostrata qui</translation>
<translation id="3963721102035795474">Modalità Reader</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Nessuno}=1{Di 1 sito }other{Di # siti }}</translation>
<translation id="397105322502079400">Calcolo in corso...</translation>
@@ -412,6 +418,8 @@
<translation id="4165986682804962316">Impostazioni sito</translation>
<translation id="4169947484918424451">Vuoi che Chromium salvi questa carta?</translation>
<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="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>
@@ -435,12 +443,12 @@
<translation id="4356973930735388585">I malintenzionati su questo sito potrebbero tentare di installare sul tuo computer programmi pericolosi che scoprono o eliminano i tuoi dati (ad esempio foto, password, messaggi e carte di credito).</translation>
<translation id="4372948949327679948">È previsto il valore <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Hai tentato di accedere a <ph name="DOMAIN" /> ma il server ha presentato un certificato revocato dall'autorità di certificazione. Ciò significa che le credenziali di sicurezza presentate dal server non sono assolutamente attendibili. Potresti avere stabilito una comunicazione con un utente malintenzionato.</translation>
-<translation id="4381091992796011497">Nome utente:</translation>
<translation id="4394049700291259645">Disabilita</translation>
<translation id="4406896451731180161">risultati di ricerca</translation>
<translation id="4424024547088906515">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza non è considerato attendibile da Chrome. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> non ha accettato il certificato di accesso oppure non ne è stato fornito uno.</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="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>
@@ -452,12 +460,14 @@
<translation id="4587425331216688090">Rimuovere l'indirizzo da Chrome?</translation>
<translation id="4592951414987517459">La connessione a <ph name="DOMAIN" /> è criptata tramite un pacchetto di crittografia moderno.</translation>
<translation id="4594403342090139922">&amp;Annulla eliminazione</translation>
+<translation id="4611292653554630842">Accedi</translation>
<translation id="4619615317237390068">Schede di altri dispositivi</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza contiene errori. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation>
<translation id="4690462567478992370">Interrompi l'utilizzo di un certificato non valido</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">La connessione è stata interrotta</translation>
+<translation id="471880041731876836">Non sei autorizzato a visitare questo sito</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Eseguire lo strumento Diagnostica di rete Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Ricarica norme</translation>
<translation id="4728558894243024398">Piattaforma</translation>
@@ -479,14 +489,15 @@
<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="4863764087567530506">Questi contenuti potrebbero cercare di indurti con l'inganno a installare software o a rivelare informazioni personali. <ph name="BEGIN_LINK" />Mostra comunque<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Cerca nella cronologia</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Autenticazione richiesta</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{e un'altra pagina web}other{e altre # pagine web}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Questa pagina è stata tradotta da una lingua sconosciuta in <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pagamento</translation>
<translation id="4926049483395192435">Deve essere specificato.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Azioni</translation>
<translation id="4958444002117714549">Espandi elenco</translation>
<translation id="4974590756084640048">Riattiva avvisi</translation>
@@ -502,6 +513,7 @@
<translation id="5045550434625856497">Password non corretta</translation>
<translation id="5056549851600133418">Articoli per te</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Controllare l'indirizzo proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Potresti non riuscire più ad accedere ai contenuti protetti di alcuni siti.</translation>
<translation id="5087286274860437796">Il certificato del server non è valido in questa fase.</translation>
<translation id="5087580092889165836">Aggiungi carta</translation>
<translation id="5089810972385038852">Provincia</translation>
@@ -509,18 +521,27 @@
<translation id="5095208057601539847">Provincia</translation>
<translation id="5115563688576182185">(a 64 bit)</translation>
<translation id="5141240743006678641">Cripta le password sincronizzate con le tue credenziali Google</translation>
-<translation id="514421653919133810">Apri la pagina in modalità di navigazione in incognito (CTRL-MAIUSC-N)</translation>
<translation id="5145883236150621069">Codice di errore presente nella risposta del criterio</translation>
+<translation id="5159010409087891077">Apri la pagina in un'altra finestra di navigazione in incognito (⇧⌘N)</translation>
<translation id="5171045022955879922">Cerca o digita un URL</translation>
<translation id="5172758083709347301">Computer</translation>
<translation id="5179510805599951267">Non in <ph name="ORIGINAL_LANGUAGE" />? Segnala questo errore</translation>
-<translation id="5181140330217080051">Download in corso</translation>
<translation id="5190835502935405962">Barra dei Preferiti</translation>
<translation id="5199729219167945352">Esperimenti</translation>
<translation id="5205222826937269299">Nome obbligatorio</translation>
<translation id="5222812217790122047">Email obbligatoria</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Utilizzi Chrome al lavoro? Le aziende possono gestire le impostazioni di Chrome per conto dei propri dipendenti. Ulteriori informazioni</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Svolgi i seguenti passaggi per disattivare temporaneamente il software e accedere così al Web. Devi disporre di privilegi di amministratore.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Fai clic su <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, quindi cerca e seleziona <ph name="BEGIN_BOLD" />"Visualizza servizi locali"<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Seleziona <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Nella sezione <ph name="BEGIN_BOLD" />Tipo di avvio<ph name="END_BOLD" />, seleziona <ph name="BEGIN_BOLD" />Disabilitato<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Nella sezione <ph name="BEGIN_BOLD" />Stato del servizio<ph name="END_BOLD" />, fai clic su <ph name="BEGIN_BOLD" />Interrompi<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Fai clic su <ph name="BEGIN_BOLD" />Applica<ph name="END_BOLD" />, quindi su <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Visita il <ph name="BEGIN_LEARN_MORE_LINK" />Centro assistenza di Chrome<ph name="END_LEARN_MORE_LINK" /> per avere informazioni su come rimuovere definitivamente il software dal computer.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">La tua connessione a questo sito non è privata. Per uscire dalla modalità VR in qualsiasi momento, rimuovi l'auricolare e premi Indietro.</translation>
<translation id="5299298092464848405">Errore durante l'analisi del criterio</translation>
<translation id="5308689395849655368">La segnalazione degli arresti anomali è disattivata.</translation>
@@ -537,9 +558,10 @@
<translation id="5439770059721715174">Errore di convalida dello schema in "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Impossibile trovare la pagina <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Timestamp del criterio errato</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> di <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Non validi</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> altro}other{<ph name="CONTACT_PREVIEW" /> e altri <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Ripeti modifica</translation>
+<translation id="5481076368049295676">Questi contenuti potrebbero cercare di installare sul tuo dispositivo software pericoloso che si impossessa delle tue informazioni o le elimina. <ph name="BEGIN_LINK" />Mostra comunque<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Aggiungi un indirizzo valido</translation>
<translation id="5492298309214877701">Il sito nell'Intranet dell'azienda, dell'organizzazione o della scuola ha lo stesso URL del sito web esterno.
<ph name="LINE_BREAK" />
@@ -551,6 +573,7 @@
<translation id="5540224163453853">Impossibile trovare l'articolo richiesto.</translation>
<translation id="5544037170328430102">Una pagina incorporata in <ph name="SITE" /> dice:</translation>
<translation id="5556459405103347317">Ricarica</translation>
+<translation id="5560088892362098740">Data di scadenza</translation>
<translation id="5565735124758917034">Attivo</translation>
<translation id="5571083550517324815">Impossibile ritirare dall'indirizzo specificato. Seleziona un indirizzo diverso.</translation>
<translation id="5572851009514199876">Accedi a Chrome per consentire al browser di verificare che tu sia autorizzato ad accedere a questo sito.</translation>
@@ -565,12 +588,13 @@
<translation id="5629630648637658800">Caricamento delle impostazioni criterio non riuscito</translation>
<translation id="5631439013527180824">Token di gestione del dispositivo non valido</translation>
<translation id="5633066919399395251">Gli utenti malintenzionati attualmente presenti sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero cercare di installare sul tuo computer programmi pericolosi che carpiscono o eliminano le tue informazioni (ad esempio, foto, password, messaggi e carte di credito). <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Contenuti ingannevoli bloccati.</translation>
<translation id="5646376287012673985">Posizione</translation>
<translation id="5659593005791499971">Email</translation>
<translation id="5669703222995421982">Ricevi contenuti suggeriti appositamente per te</translation>
<translation id="5675650730144413517">La pagina non funziona</translation>
<translation id="5710435578057952990">L'identità di questo sito web non è stata verificata.</translation>
-<translation id="5713016350996637505">Contenuti ingannevoli bloccati</translation>
+<translation id="5719499550583120431">Le carte prepagate sono accettate.</translation>
<translation id="5720705177508910913">Utente corrente</translation>
<translation id="5732392974455271431">I tuoi genitori possono sbloccarlo per te</translation>
<translation id="5763042198335101085">Inserisci un indirizzo email valido</translation>
@@ -582,15 +606,14 @@
<translation id="5803412860119678065">Vuoi inserire automaticamente <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">La connessione a <ph name="DOMAIN" /> è criptata tramite un pacchetto di crittografia obsoleto.</translation>
<translation id="5813119285467412249">&amp;Ripeti aggiunta</translation>
-<translation id="5814352347845180253">Potresti perdere l'accesso ai contenuti premium del sito <ph name="SITE" /> e di altri siti.</translation>
<translation id="5838278095973806738">Non dovresti inserire dati sensibili in questo sito (ad esempio password o carte di credito) perché potrebbero essere intercettati da utenti malintenzionati.</translation>
<translation id="5869405914158311789">Impossibile raggiungere il sito</translation>
<translation id="5869522115854928033">Password salvate</translation>
<translation id="5872918882028971132">Suggerimenti per i genitori</translation>
+<translation id="5893752035575986141">Le carte di credito sono accettate.</translation>
<translation id="5901630391730855834">Giallo</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="5926846154125914413">Potresti perdere l'accesso ai contenuti premium di alcuni siti.</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="5967867314010545767">Rimuovi da cronologia</translation>
<translation id="5975083100439434680">Diminuisci lo zoom</translation>
@@ -606,6 +629,7 @@
<translation id="6040143037577758943">Chiudi</translation>
<translation id="6042308850641462728">Più</translation>
<translation id="6047233362582046994">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 rimosse le app dannose.</translation>
+<translation id="6047927260846328439">Questi contenuti potrebbero cercare di indurti con l'inganno a installare software o a rivelare informazioni personali. <ph name="BEGIN_LINK" />Mostra comunque<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Al momento non puoi visitare il sito web <ph name="SITE" /> perché utilizza il blocco dei certificati. In genere gli errori di rete e gli attacchi sono temporanei, pertanto questa pagina potrebbe funzionare più tardi.</translation>
<translation id="6060685159320643512">Attenzione, prova questi esperimenti a tuo rischio e pericolo</translation>
<translation id="6080696365213338172">Hai raggiunto i contenuti utilizzando un certificato fornito dall'amministratore. I dati che fornisci a <ph name="DOMAIN" /> possono essere intercettati dal tuo amministratore.</translation>
@@ -618,7 +642,6 @@
<translation id="6165508094623778733">Ulteriori informazioni</translation>
<translation id="6169916984152623906">Ora puoi navigare in privato. Le altre persone che usano questo dispositivo non vedranno le tue attività, ma i download e i preferiti verranno salvati.</translation>
<translation id="6177128806592000436">La tua connessione a questo sito non è protetta</translation>
-<translation id="6184817833369986695">(coorte: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Controlla la connessione a Internet</translation>
<translation id="6218753634732582820">Rimuovere l'indirizzo da Chromium?</translation>
<translation id="6221345481584921695">La funzione Navigazione sicura di Google ha <ph name="BEGIN_LINK" />rilevato malware<ph name="END_LINK" /> di recente sul sito <ph name="SITE" />. I siti web che in genere sono sicuri a volte vengono infettati da malware. I contenuti dannosi provengono da <ph name="SUBRESOURCE_HOST" />, un noto distributore di malware.</translation>
@@ -637,13 +660,14 @@
<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>
<translation id="6387478394221739770">Ti interessano le nuove e straordinarie funzioni di Chrome? Prova il nostro canale beta all'indirizzo chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium ha esaurito la memoria mentre cercava di visualizzare questa pagina web.</translation>
+<translation id="6397451950548600259">Il software installato sul computer sta impedendo a Chrome di connettersi in sicurezza a Internet.</translation>
<translation id="6404511346730675251">Modifica preferito</translation>
<translation id="6410264514553301377">Inserisci la data di scadenza e il codice CVC della carta <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Hai chiesto ai tuoi genitori l'autorizzazione per visitare questo sito</translation>
@@ -658,6 +682,7 @@
<translation id="647261751007945333">Norme dispositivo</translation>
<translation id="6477321094435799029">Chrome ha rilevato un codice insolito su questa pagina e l'ha bloccata per proteggere le tue informazioni personali (ad esempio password, numeri di telefono e carte di credito).</translation>
<translation id="6489534406876378309">Avvia caricamento arresti anomali</translation>
+<translation id="6507833130742554667">Le carte di credito e di debito sono accettate.</translation>
<translation id="6508722015517270189">Riavvia Chrome</translation>
<translation id="6529602333819889595">&amp;Ripeti eliminazione</translation>
<translation id="6534179046333460208">Suggerimenti relativi al Physical Web</translation>
@@ -666,13 +691,13 @@
<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="657639383826808334">Questi contenuti potrebbero cercare di installare sul tuo dispositivo software pericoloso che si impossessa delle tue informazioni o le elimina. <ph name="BEGIN_LINK" />Mostra comunque<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opzioni di crittografia</translation>
<translation id="662080504995468778">Rimani</translation>
<translation id="6626291197371920147">Aggiungi un numero di carta valido</translation>
<translation id="6628463337424475685">Ricerca <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Gli utenti malintenzionati attualmente presenti sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero cercare di installare sul tuo Mac 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="6644283850729428850">Questa norma è obsoleta.</translation>
+<translation id="6657585470893396449">Password</translation>
<translation id="6671697161687535275">Rimuovere il suggerimento per i moduli da Chromium?</translation>
<translation id="6685834062052613830">Esci e completa la configurazione</translation>
<translation id="6710213216561001401">Indietro</translation>
@@ -703,7 +728,6 @@
<translation id="6970216967273061347">Distretto</translation>
<translation id="6973656660372572881">Sono stati specificati sia i server proxy fissi che un URL script .pac.</translation>
<translation id="6989763994942163495">Mostra impostazioni avanzate...</translation>
-<translation id="7000990526846637657">Nessuna voce della cronologia trovata</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Il tuo account Google potrebbe avere altre forme di cronologia di navigazione all'indirizzo <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Password</translation>
@@ -718,7 +742,9 @@
<translation id="7129409597930077180">Impossibile spedire all'indirizzo specificato. Seleziona un indirizzo diverso.</translation>
<translation id="7138472120740807366">Metodo di consegna</translation>
<translation id="7139724024395191329">Emirato</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> altro}other{<ph name="PAYMENT_METHOD_PREVIEW" /> e altri <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Pagamento non sicuro</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> altra}other{<ph name="SHIPPING_OPTION_PREVIEW" /> e altre <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Riavvia ora</translation>
<translation id="7180611975245234373">Aggiorna</translation>
<translation id="7182878459783632708">Nessuna norma impostata</translation>
@@ -759,6 +785,7 @@
<translation id="7514365320538308">Scarica</translation>
<translation id="7518003948725431193">Nessuna pagina web trovata per l'indirizzo web: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">La connessione a questo sito non è privata</translation>
<translation id="7535087603100972091">Valore</translation>
<translation id="7537536606612762813">Obbligatoria</translation>
<translation id="7542403920425041731">Dopo essere stati confermati, i dati della carta saranno condivisi con questo sito.</translation>
@@ -768,7 +795,6 @@
<translation id="7552846755917812628">Prova i seguenti suggerimenti:</translation>
<translation id="7554791636758816595">Nuova scheda</translation>
<translation id="7567204685887185387">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza potrebbe essere stato emesso in modo fraudolento. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> altro}other{<ph name="CONTACT_PREVIEW" /> e altri <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Questa pagina è in<ph name="ORIGINAL_LANGUAGE" />Vuoi tradurla?</translation>
<translation id="7569952961197462199">Rimuovere la carta di credito da Chrome?</translation>
<translation id="7569983096843329377">Nero</translation>
@@ -795,9 +821,11 @@
<translation id="7714464543167945231">Certificato</translation>
<translation id="7716147886133743102">Bloccata dall'amministratore</translation>
<translation id="7716424297397655342">Impossibile caricare il sito dalla cache</translation>
+<translation id="774634243536837715">Contenuti pericolosi bloccati.</translation>
<translation id="7752995774971033316">Non gestito</translation>
<translation id="7755287808199759310">Il tuo genitore può sbloccarlo per te</translation>
<translation id="7758069387465995638">Il software antivirus o il firewall potrebbe avere bloccato la connessione.</translation>
+<translation id="7759163816903619567">Visualizza dominio:</translation>
<translation id="7761701407923456692">Il certificato del server non corrisponde all'URL.</translation>
<translation id="7763386264682878361">Analizzatore sintattico dei file manifest dei pagamenti</translation>
<translation id="7764225426217299476">Aggiungi indirizzo</translation>
@@ -812,6 +840,7 @@
<translation id="7815407501681723534"><ph name="SEARCH_RESULTS" /> per "<ph name="SEARCH_STRING" />": <ph name="NUMBER_OF_RESULTS" /></translation>
<translation id="785549533363645510">Non sei completamente invisibile: se navighi in incognito, la tua navigazione non viene nascosta al tuo datore di lavoro, al provider di servizi Internet o ai siti web che visiti.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Le carte di debito e prepagate sono accettate.</translation>
<translation id="7887683347370398519">Controlla il tuo codice CVC e riprova</translation>
<translation id="79338296614623784">Inserisci un numero di telefono valido</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -831,7 +860,6 @@
<translation id="8041089156583427627">Invia feedback</translation>
<translation id="8041940743680923270">Usa predefinita globale (Chiedi)</translation>
<translation id="8088680233425245692">Impossibile visualizzare l'articolo.</translation>
-<translation id="8089520772729574115">meno di 1 MB</translation>
<translation id="8091372947890762290">Attivazione in attesa sul server</translation>
<translation id="8118489163946903409">Metodo di pagamento</translation>
<translation id="8131740175452115882">Conferma</translation>
@@ -843,10 +871,13 @@
<translation id="8194797478851900357">&amp;Annulla spostamento</translation>
<translation id="8201077131113104583">URL di aggiornamento non valido per l'estensione con ID "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Riepilogo dell’ordine</translation>
+<translation id="8205463626947051446">Il sito tende a mostrare annunci invasivi</translation>
<translation id="8218327578424803826">Posizione assegnata:</translation>
<translation id="8225771182978767009">La persona che ha configurato il computer ha deciso di bloccare questo sito.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Apri la pagina in una nuova scheda di navigazione in incognito</translation>
<translation id="8241707690549784388">La pagina a cui stai tentando di accedere utilizzava informazioni inserite da te. Tornando a quella pagina, è possibile che eventuali azioni che hai eseguito vengano ripetute. Continuare?</translation>
+<translation id="8241712895048303527">Blocca su questo sito</translation>
<translation id="8249320324621329438">Ultimo recupero:</translation>
<translation id="8253091569723639551">Indirizzo di fatturazione obbligatorio</translation>
<translation id="8261506727792406068">Elimina</translation>
@@ -857,7 +888,6 @@
<translation id="8308427013383895095">La traduzione non è riuscita a causa di un problema con la connessione di rete.</translation>
<translation id="8332188693563227489">Accesso a <ph name="HOST_NAME" /> negato</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="8344669043927012510">Apri la pagina in modalità di navigazione in incognito (⇧⌘N)</translation>
<translation id="8349305172487531364">Barra dei Preferiti</translation>
<translation id="8363502534493474904">Disattivare la modalità aereo</translation>
<translation id="8364627913115013041">Non impostata.</translation>
@@ -867,6 +897,7 @@
<translation id="8398259832188219207">Rapporto sugli arresti anomali caricato in data <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Arresti anomali (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Devi inserire la stessa passphrase due volte.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Impostazioni</translation>
<translation id="8433057134996913067">In questo modo uscirai dalla maggior parte dei siti web.</translation>
<translation id="8437238597147034694">&amp;Annulla spostamento</translation>
@@ -874,14 +905,16 @@
<translation id="8483780878231876732">Accedi a Chrome per usare le carte memorizzate nel tuo account Google</translation>
<translation id="8488350697529856933">Si applica a</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha impiegato troppo tempo a rispondere.</translation>
-<translation id="8532105204136943229">Anno di scadenza</translation>
+<translation id="8503813439785031346">Nome utente</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="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="8620436878122366504">I tuoi genitori non hanno ancora approvato la richiesta</translation>
<translation id="8647750283161643317">Ripristina i valori predefiniti per tutto</translation>
+<translation id="8660471606262461360">Da Google Payments</translation>
<translation id="8703575177326907206">La connessione a <ph name="DOMAIN" /> non è criptata.</translation>
<translation id="8718314106902482036">Pagamento non completato</translation>
<translation id="8725066075913043281">Riprova</translation>
@@ -909,6 +942,7 @@
<translation id="8903921497873541725">Aumenta lo zoom</translation>
<translation id="8931333241327730545">Vuoi salvare la scheda nel tuo account Google?</translation>
<translation id="8932102934695377596">L'orologio è indietro</translation>
+<translation id="8938939909778640821">Carte di credito e prepagate accettate</translation>
<translation id="8971063699422889582">Il certificato del server è scaduto.</translation>
<translation id="8986494364107987395">Invia automaticamente a Google statistiche sull'utilizzo e rapporti sugli arresti anomali</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -938,7 +972,6 @@
<translation id="9169664750068251925">Blocca sempre su questo sito</translation>
<translation id="9170848237812810038">&amp;Annulla</translation>
<translation id="917450738466192189">Il certificato del server non è valido.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> e <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> altra}other{<ph name="SHIPPING_OPTION_PREVIEW" /> e altre <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Impossibile aggiungere l'articolo.</translation>
@@ -947,9 +980,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">CANCELLA MODULO</translation>
<translation id="939736085109172342">Nuova cartella</translation>
-<translation id="941721044073577244">Sembra che tu non sia autorizzato a visitare questo sito</translation>
<translation id="969892804517981540">Build ufficiale</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Nessuno}=1{1 elemento}other{# elementi}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Build</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_iw.xtb b/chromium/components/strings/components_strings_iw.xtb
index 291fded5a0b..9c49fb858a4 100644
--- a/chromium/components/strings/components_strings_iw.xtb
+++ b/chromium/components/strings/components_strings_iw.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">סגירת יישומים אחרים</translation>
<translation id="1055184225775184556">&amp;ביטול הוספה</translation>
<translation id="10614374240317010">פריטים שאף פעם לא נשמרו</translation>
+<translation id="1066396345355680611">ייתכן שלא תהיה לך יותר גישה לתוכן מוגן מ-<ph name="SITE" /> ומאתרים אחרים.</translation>
<translation id="106701514854093668">סימניות שולחן עבודה</translation>
<translation id="1074497978438210769">לא מאובטח</translation>
<translation id="1080116354587839789">התאם לרוחב</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">רשימת קריאה</translation>
<translation id="1264126396475825575">דוח הקריסה תועד ב-<ph name="CRASH_TIME" /> (עדיין לא העלית אותו או בחרת להתעלם ממנו)</translation>
<translation id="1281526147609854549">הונפק על-ידי <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">תוכן מסוכן נחסם</translation>
<translation id="1285320974508926690">לעולם אל תתרגם אתר זה</translation>
<translation id="129553762522093515">נסגרו לאחרונה</translation>
<translation id="129863573139666797">‏<ph name="BEGIN_LINK" />נסה לנקות את קובצי ה-Cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">דומיין הרשמה:</translation>
<translation id="1340482604681802745">כתובת איסוף</translation>
-<translation id="1344211575059133124">נראה שאתה זקוק להרשאה כדי לבקר באתר הזה</translation>
<translation id="1344588688991793829">‏הגדרות מילוי אוטומטי של Chromium...</translation>
<translation id="1348198688976932919">האתר שאתה עומד לעבור אליו מכיל יישומים מסוכנים</translation>
<translation id="1374468813861204354">הצעות</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">זהות <ph name="ORGANIZATION" /> ב-<ph name="LOCALITY" /> אומתה על ידי <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">כן</translation>
<translation id="1430915738399379752">הדפס</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> נוסף}two{<ph name="PAYMENT_METHOD_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> נוספים}many{<ph name="PAYMENT_METHOD_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> נוספים}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> נוספים}}</translation>
<translation id="1506687042165942984">הצג עותק שמור (כלומר, ידוע שהוא עדכני) של הדף הזה.</translation>
<translation id="1517433312004943670">יש צורך במספר טלפון</translation>
+<translation id="1517500485252541695">כרטיסי אשראי וחיוב שהסוחר מקבל</translation>
<translation id="1519264250979466059">‏תאריך ה-Build</translation>
+<translation id="1527263332363067270">ממתין לחיבור...</translation>
<translation id="153384715582417236">זה הכול בינתיים</translation>
<translation id="1549470594296187301">‏JavaScript צריך להיות מופעל כדי להשתמש בתכונה זו.</translation>
<translation id="1555130319947370107">כחול</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">נסה לפנות אל מנהל המערכת.</translation>
<translation id="1740951997222943430">עליך להזין חודש תפוגה חוקי</translation>
-<translation id="1745358365027406341">הורד את הדף מאוחר יותר</translation>
<translation id="17513872634828108">כרטיסיות פתוחות</translation>
<translation id="1753706481035618306">מספר דף</translation>
<translation id="1763864636252898013">השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />. אישור האבטחה שלו לא נחשב כמהימן על ידי מערכת ההפעלה של המכשיר. ייתכן שהסיבה לכך היא תצורה שגויה או תוקף המיירט את החיבור שלך.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">שדה חובה</translation>
<translation id="187918866476621466">פתח דפי פתיחה</translation>
<translation id="1883255238294161206">כווץ רשימה</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> נוספת}two{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> נוספות}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> נוספות}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> נוספות}}</translation>
<translation id="1898423065542865115">סינון</translation>
+<translation id="1916770123977586577">כדי להחיל על האתר את ההגדרות המעודכנות, טען מחדש את הדף</translation>
+<translation id="1919345977826869612">מודעות</translation>
<translation id="192020519938775529">{COUNT,plural, =0{ללא}=1{אתר אחד}two{שני אתרים}many{# אתרים}other{# אתרים}}</translation>
<translation id="194030505837763158">עבור ל-<ph name="LINK" /></translation>
+<translation id="1948773908305951926">כרטיסים משולמים מראש שהסוחר מקבל</translation>
<translation id="1962204205936693436">סימניות של <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">שגיאה בעריכה בסידרה</translation>
<translation id="1974060860693918893">מתקדם</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">‏שגיאת HTTP</translation>
<translation id="2270484714375784793">מספר טלפון</translation>
<translation id="2282872951544483773">ניסויים לא זמינים</translation>
-<translation id="2283595600756901917">{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="2292556288342944218">הגישה לאינטרנט חסומה</translation>
<translation id="230155334948463882">כרטיס חדש?</translation>
+<translation id="2316887270356262533">‏פינוי של פחות מ-‎1 MB‎ מהשטח. ייתכן שחלק מהאתרים ייטענו לאט יותר בביקור הבא שלך.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> דורש שם משתמש וסיסמה.</translation>
+<translation id="2317583587496011522">אפשר לשלם בכרטיסי חיוב.</translation>
<translation id="2337852623177822836">ההגדרה נשלטת על-ידי מנהל המערכת</translation>
<translation id="2354001756790975382">סימניות אחרות</translation>
<translation id="2354430244986887761">‏התכונה 'גלישה בטוחה' של Google <ph name="BEGIN_LINK" />מצאה לאחרונה יישומים מזיקים<ph name="END_LINK" /> באתר <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">המשך</translation>
<translation id="2365563543831475020">דוח הקריסה שתועד ב-<ph name="CRASH_TIME" /> לא הועלה</translation>
<translation id="2367567093518048410">רמה</translation>
-<translation id="237718015863234333">אין חלופות זמינות עבור ממשק המשתמש</translation>
<translation id="2384307209577226199">ברירת מחדל של ארגון</translation>
<translation id="2386255080630008482">אישור השרת נשלל.</translation>
<translation id="2392959068659972793">הצגת מדיניות ללא ערך מוגדר</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">‏אם האפשרות תסומן, Chromium ישמור עותק של הכרטיס במכשיר הזה כדי למלא טפסים במהירות רבה יותר.</translation>
<translation id="2498091847651709837">סרוק כרטיס חדש</translation>
<translation id="2501278716633472235">חזור</translation>
+<translation id="2503184589641749290">כרטיסי חיוב וכרטיסים משולמים מראש שהסוחר מקבל</translation>
<translation id="2515629240566999685">לבדוק את האות באזור שלך</translation>
-<translation id="2516305470678292029">חלופות לממשק משתמש</translation>
<translation id="2539524384386349900">זהה</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> שלח תגובה לא חוקית.</translation>
<translation id="2556876185419854533">&amp;ביטול עריכה</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">שיטת משלוח</translation>
<translation id="277499241957683684">חסרה רשומת מכשיר</translation>
<translation id="2784949926578158345">החיבור עבר איפוס.</translation>
+<translation id="2788784517760473862">כרטיסי אשראי שהסוחר מקבל</translation>
<translation id="2794233252405721443">אתר חסום</translation>
<translation id="2799020568854403057">האתר שאתה עומד לעבור אליו מכיל יישומים מזיקים</translation>
<translation id="2803306138276472711">‏לאחרונה, 'גלישה בטוחה של Google‏' <ph name="BEGIN_LINK" />זיהתה תוכנה זדונית<ph name="END_LINK" /> באתר <ph name="SITE" />. אתרים שבדרך כלל נחשבים לבטוחים נדבקים לעתים בתוכנה זדונית.</translation>
<translation id="2824775600643448204">סרגל חיפוש וכתובות</translation>
<translation id="2826760142808435982">החיבור מוצפן ומאומת באמצעות <ph name="CIPHER" /> ומשתמש ב-<ph name="KX" /> כמנגנון להחלפת מפתחות.</translation>
<translation id="2835170189407361413">נקה טופס</translation>
+<translation id="2851634818064021665">יש צורך בהרשאה כדי להיכנס אל האתר הזה</translation>
<translation id="2856444702002559011">ייתכן שתוקפים מנסים לגנוב את הפרטים שלך מהאתר <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (לדוגמה, סיסמאות, הודעות או כרטיסי אשראי). <ph name="BEGIN_LEARN_MORE_LINK" />מידע נוסף<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">אל תטען מחדש</translation>
-<translation id="2900469785430194048">‏אזל הזיכרון הזמין ל-Google Chrome בניסיון להציג את דף האינטרנט הזה.</translation>
<translation id="2909946352844186028">אותר שינוי ברשת.</translation>
<translation id="2916038427272391327">סגירת תוכניות אחרות</translation>
<translation id="2922350208395188000">לא ניתן לבדוק את אישור השרת.</translation>
<translation id="2928905813689894207">כתובת לחיוב</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}two{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />. אישור האבטחה שלו הוא מ-<ph name="DOMAIN2" />. ייתכן שהסיבה לכך היא תצורה שגויה או תוקף המיירט את החיבור שלך.</translation>
<translation id="2948083400971632585">‏ניתן להשבית כל שרת proxy המוגדר לחיבור מדף ההגדרות.</translation>
<translation id="2955913368246107853">סגור את חלונית החיפוש</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">‏אם האפשרות תופעל, Chromium יאחסן עותק של הכרטיס שלך במכשיר הזה למילוי מהיר יותר של טפסים.</translation>
<translation id="2985398929374701810">עליך להזין כתובת חוקית</translation>
<translation id="2986368408720340940">שיטת האיסוף הזו אינה זמינה. עליך לבחור שיטה אחרת.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">‏דפים שאתה מציג בכרטיסיות גלישה בסתר לא יישארו בהיסטוריית הדפדפן, באחסון קובצי ה-Cookie או בהיסטוריית החיפושים לאחר שתסגור את כל כרטיסיות הגלישה בסתר. קבצים שהורדת או סימניות שיצרת יישמרו.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">אי</translation>
-<translation id="317583078218509884">הגדרות חדשות של אישורי אתר ייכנסו לתוקף לאחר טעינה מחדש של הדף.</translation>
<translation id="3176929007561373547">‏בדוק את הגדרות שרת ה-proxy או פנה למנהל הרשת
כדי לוודא ששרת ה-proxy פועל. אם אינך סבור שעליך
להשתמש בשרת proxy:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">פתיחת דף במצב גלישה בסתר</translation>
<translation id="320323717674993345">בטל תשלום</translation>
<translation id="3207960819495026254">מסומן בסימניה</translation>
<translation id="3225919329040284222">השרת הציג אישור שאינו תואם את הציפיות המובנות. ציפיות אלה נכללות עבור אתרי אינטרנט מסוימים בעלי אבטחה גבוהה כדי להגן עליך.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;ביטול של שינוי סדר</translation>
<translation id="3282497668470633863">הוספה של השם המופיע בכרטיס</translation>
<translation id="3286538390144397061">הפעל מחדש כעת</translation>
+<translation id="3287510313208355388">הורד לאחר התחברות</translation>
<translation id="3303855915957856445">לא נמצאו תוצאות חיפוש</translation>
<translation id="3305707030755673451">הנתונים שלך הוצפנו ב-<ph name="TIME" /> באמצעות ביטוי הסיסמה לסינכרון. הזן אותו כדי להתחיל בסינכרון.</translation>
<translation id="3320021301628644560">הוספה של כתובת לחיוב</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">מרווח אחזור:</translation>
<translation id="3462200631372590220">הסתר פרטים מתקדמים</translation>
<translation id="3467763166455606212">עליך לציין את שם בעל הכרטיס</translation>
-<translation id="3478058380795961209">חודש פקיעת התוקף</translation>
<translation id="3479539252931486093">האם זה קרה באופן בלתי צפוי? <ph name="BEGIN_LINK" />ספר לנו על כך<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">לא עכשיו</translation>
<translation id="3498215018399854026">לא הצלחנו ליצור קשר עם ההורה שלך. נסה שוב מאוחר יותר.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;התאם את התאריך והשעה מהקטע &lt;strong&gt;כללי&lt;/strong&gt; של אפליקציית ה&lt;strong&gt;הגדרות&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />מידע נוסף<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">אפשר לשלם באמצעות כרטיסי אשראי וכרטיסים משולמים מראש.</translation>
<translation id="3582930987043644930">הוסף שם</translation>
<translation id="3583757800736429874">&amp;ביצוע מחדש של העברה</translation>
<translation id="3586931643579894722">הסתר פרטים</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">אם אתה רואה זאת לעתים קרובות, נסה את ה<ph name="HELP_LINK" /> האלה.</translation>
<translation id="3658742229777143148">גרסה קודמת</translation>
<translation id="3678029195006412963">לא ניתן היה לחתום על הבקשה</translation>
+<translation id="3678529606614285348">‏פתח את הדף בחלון חדש של גלישה בסתר (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">דוח קריסה תועד ב-<ph name="CRASH_TIME" />, הועלה ב-<ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">פרטי אישור</translation>
<translation id="3690164694835360974">ההתחברות אינה מאובטחת</translation>
-<translation id="3693415264595406141">סיסמה:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">טוען...</translation>
<translation id="3712624925041724820">אין מספיק רישיונות</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">תוקפים באתר <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="375403751935624634">התרגום נכשל עקב שגיאת שרת.</translation>
<translation id="3759461132968374835">לא התקבלו דיווחים על קריסות לאחרונה. קריסות שהתרחשו בזמן שאפשרות הדיווח על קריסות היתה מושבתת לא יופיעו כאן.</translation>
+<translation id="3765032636089507299">הדף 'גלישה בטוחה' נמצא כרגע בבנייה.</translation>
<translation id="3778403066972421603">‏האם ברצונך לשמור את הכרטיס בחשבון Google שלך ובמכשיר הזה?</translation>
<translation id="3783418713923659662">מאסטרקארד</translation>
<translation id="3787705759683870569">תאריך תפוגה: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">הבקשה שלך לגשת לאתר הזה נשלחה אל <ph name="NAME" /></translation>
<translation id="3890664840433101773">הוספת כתובת אימייל</translation>
<translation id="3901925938762663762">תוקף הכרטיס פג</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">מזהה דוח הקריסה שהועלה <ph name="CRASH_ID" /> (מזהה קריסה מקומי: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">‏השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />; אישור האבטחה שלו לא מציין ערכי Subject Alternative Name. ייתכן שהסיבה לכך היא תצורה שגויה או תוקף המיירט את החיבור שלך.</translation>
+<translation id="3949601375789751990">היסטוריית הגלישה שלך מופיעה כאן</translation>
<translation id="3963721102035795474">מצב קורא</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{ללא}=1{מאתר אחד }two{משני אתרים }many{מ-# אתרים }other{מ-# אתרים }}</translation>
<translation id="397105322502079400">מחשב...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">הגדרות אתרים</translation>
<translation id="4169947484918424451">‏האם תרצה ש-Chromium ישמור את הכרטיס הזה?</translation>
<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="4196861286325780578">&amp;ביצוע מחדש של העברה</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />לבדוק את תצורת האנטי-וירוס וחומת האש<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">קריסה</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">תוקפים באתר הזה עשויים לנסות להתקין במחשב שלך תוכניות מסוכנות שגונבות או מוחקות מידע שלך (לדוגמה: תמונות, סיסמאות, הודעות ופרטי כרטיסי אשראי).</translation>
<translation id="4372948949327679948">צפוי ערך מסוג <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">ניסית להשיג את <ph name="DOMAIN" />, אך האישור שהשרת הציג בוטל על ידי המנפיק שלו. פירוש הדבר שאין כל אפשרות לתת אמון באישורי האבטחה שהשרת הציג. ייתכן שאתה מתקשר עם תוקף.</translation>
-<translation id="4381091992796011497">שם משתמש:</translation>
<translation id="4394049700291259645">השבת</translation>
<translation id="4406896451731180161">תוצאות חיפוש</translation>
<translation id="4424024547088906515">‏השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />. אישור האבטחה שלו לא נחשב כמהימן על ידי Chrome. ייתכן שהסיבה לכך היא תצורה שגויה או תוקף המיירט את החיבור שלך.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> לא אישר את אישור ההתחברות שלך, או שלא סופק אישור התחברות.</translation>
<translation id="443673843213245140">‏השימוש בשרת Proxy הושבת, אך צויינה תצורת שרת Proxy מפורשת.</translation>
+<translation id="445100540951337728">כרטיסי חיוב שהסוחר מקבל</translation>
<translation id="4506176782989081258">שגיאת אימות: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">לפנות אל מנהל המערכת</translation>
<translation id="450710068430902550">שיתוף עם מנהל מערכת</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">‏האם להסיר את הכתובת מ-Chrome?</translation>
<translation id="4592951414987517459">החיבור שלך אל <ph name="DOMAIN" /> מוצפן באמצעות חבילת צופן מתקדמת.</translation>
<translation id="4594403342090139922">&amp;ביטול מחיקה</translation>
+<translation id="4611292653554630842">התחבר</translation>
<translation id="4619615317237390068">כרטיסיות ממכשירים אחרים</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />; אישור האבטחה שלו מכיל שגיאות. ייתכן שהסיבה לכך היא הגדרה שגויה או תוקף המיירט את החיבור שלך.</translation>
<translation id="4690462567478992370">הפסק להשתמש באישור לא חוקי</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">החיבור נקטע</translation>
+<translation id="471880041731876836">אין לך הרשאה להיכנס אל האתר הזה</translation>
<translation id="4722547256916164131">‏<ph name="BEGIN_LINK" />מפעיל את אבחון הרשת של Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">טען מדיניות מחדש</translation>
<translation id="4728558894243024398">פלטפורמה</translation>
@@ -486,14 +496,15 @@ Del</translation>
<translation id="4850886885716139402">הצג</translation>
<translation id="4854362297993841467">שיטת המסירה הזו אינה זמינה. עליך לבחור שיטה אחרת.</translation>
<translation id="4858792381671956233">שאלת את ההורים שלך אם אתה יכול לגשת לאתר הזה</translation>
-<translation id="4863764087567530506">ייתכן שהתוכן הזה ינסה להטעות אותך כדי לגרום לך להתקין תוכנות או לחשוף מידע אישי. <ph name="BEGIN_LINK" />הצג בכל זאת<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">חפש בהיסטוריה</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">נדרש אימות</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ועוד דף אינטרנט אחד}two{ועוד # דפי אינטרנט}many{ועוד # דפי אינטרנט}other{ועוד # דפי אינטרנט}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">דף זה תורגם משפה לא ידועה ל<ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">תשלום</translation>
<translation id="4926049483395192435">יש לציין ערך זה.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">פעולות</translation>
<translation id="4958444002117714549">הרחב רשימה</translation>
<translation id="4974590756084640048">הפעל מחדש אזהרות</translation>
@@ -509,6 +520,7 @@ Del</translation>
<translation id="5045550434625856497">סיסמה שגויה</translation>
<translation id="5056549851600133418">מאמרים שעשויים לעניין אותך</translation>
<translation id="5070335125961472645">‏<ph name="BEGIN_LINK" />לבדוק את כתובת שרת ה-Proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">ייתכן שלא תהיה לך יותר גישה לתוכן מוגן מאתרים מסוימים.</translation>
<translation id="5087286274860437796">האישור של השרת אינו תקף כעת.</translation>
<translation id="5087580092889165836">הוסף כרטיס</translation>
<translation id="5089810972385038852">מדינה</translation>
@@ -516,18 +528,27 @@ Del</translation>
<translation id="5095208057601539847">פרובינציה</translation>
<translation id="5115563688576182185">(64 סיביות)</translation>
<translation id="5141240743006678641">‏הצפן סיסמאות מסונכרנות באמצעות פרטי הכניסה שלך ל-Google</translation>
-<translation id="514421653919133810">‏פתיחת הדף במצב גלישה בסתר (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">קיים קוד שגיאה בתגובת המדיניות</translation>
+<translation id="5159010409087891077">‏פתח את הדף בחלון חדש של גלישה בסתר (‎⇧⌘N)</translation>
<translation id="5171045022955879922">חפש או הקלד כתובת אתר</translation>
<translation id="5172758083709347301">מכונה</translation>
<translation id="5179510805599951267">לא ב<ph name="ORIGINAL_LANGUAGE" />? דווח על שגיאה זו</translation>
-<translation id="5181140330217080051">מוריד</translation>
<translation id="5190835502935405962">סרגל הסימניות</translation>
<translation id="5199729219167945352">ניסויים</translation>
<translation id="5205222826937269299">שם (חובה)</translation>
<translation id="5222812217790122047">אימייל (חובה)</translation>
<translation id="5251803541071282808">ענן</translation>
<translation id="5277279256032773186">‏משתמש ב-Chrome בעבודה? עסקים יכולים לנהל את ההגדרות של Chrome עבור העובדים. למידע נוסף</translation>
+<translation id="5281113152797308730">‏<ph name="BEGIN_PARAGRAPH" />בצע את השלבים הבאים על מנת להשבית את התוכנה באופן זמני, כדי שתוכל להתחבר לאינטרנט. יש צורך בהרשאות מנהל מערכת.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />לחץ על <ph name="BEGIN_BOLD" />התחל<ph name="END_BOLD" />, ולאחר מכן חפש ובחר את האפשרות <ph name="BEGIN_BOLD" />הצג שירותים מקומיים<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />בחר באפשרות <ph name="BEGIN_BOLD" />גילוי חזותי<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />בקטע <ph name="BEGIN_BOLD" />סוג אתחול<ph name="END_BOLD" />, בחר באפשרות <ph name="BEGIN_BOLD" />מושבת<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />בקטע <ph name="BEGIN_BOLD" />סטטוס שירות<ph name="END_BOLD" />, לחץ על <ph name="BEGIN_BOLD" />הפסק<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />לחץ על <ph name="BEGIN_BOLD" />החל<ph name="END_BOLD" />, ולאחר מכן על <ph name="BEGIN_BOLD" />אישור<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />היכנס אל <ph name="BEGIN_LEARN_MORE_LINK" />מרכז העזרה של Chrome<ph name="END_LEARN_MORE_LINK" /> כדי ללמוד איך להסיר לצמיתות את התוכנה מהמחשב
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">‏החיבור שלך לאתר זה אינו פרטי. כדי לצאת ממצב VR בכל שלב, הסר את האוזניות ולחץ על 'הקודם'.</translation>
<translation id="5299298092464848405">שגיאה בניתוח המדיניות</translation>
<translation id="5308689395849655368">דיווח קריסות מושבת.</translation>
@@ -544,9 +565,10 @@ Del</translation>
<translation id="5439770059721715174">שגיאת אימות סכימה ב-"<ph name="ERROR_PATH" />"‏: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">לא ניתן למצוא את הדף של <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">חותמת הזמן של המדיניות שגויה</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> מתוך <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">לא חוקי</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}two{<ph name="CONTACT_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}many{<ph name="CONTACT_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;ביצוע מחדש של עריכה</translation>
+<translation id="5481076368049295676">ייתכן שהתוכן הזה מכיל אמצעים שינסו להתקין תוכנה מסוכנת, שגונבת או מוחקת את המידע שלך. <ph name="BEGIN_LINK" />הצג בכל זאת<ph name="END_LINK" /></translation>
<translation id="54817484435770891">הוספה של כתובת חוקית</translation>
<translation id="5492298309214877701">כתובת האתר הזה באינטראנט של החברה, הארגון או מוסד הלימודים, זהה לכתובת
של אתר חיצוני.
@@ -559,6 +581,7 @@ Del</translation>
<translation id="5540224163453853">לא ניתן היה למצוא את הפריט המבוקש.</translation>
<translation id="5544037170328430102">דף המוטמע ב-<ph name="SITE" /> אומר:</translation>
<translation id="5556459405103347317">טען שוב</translation>
+<translation id="5560088892362098740">תאריך תפוגה</translation>
<translation id="5565735124758917034">פעיל</translation>
<translation id="5571083550517324815">לא ניתן לבצע איסוף מהכתובת הזו. עליך לבחור כתובת אחרת.</translation>
<translation id="5572851009514199876">‏תחילה היכנס לחשבונך ב-Chrome כדי לאפשר ל-Chrome לבדוק אם יש לך הרשאה לגשת לאתר הזה.</translation>
@@ -573,12 +596,13 @@ Del</translation>
<translation id="5629630648637658800">טעינת הגדרות המדיניות נכשלה</translation>
<translation id="5631439013527180824">אסימון ניהול המכשיר אינו חוקי</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="5646376287012673985">מיקום</translation>
<translation id="5659593005791499971">אימייל</translation>
<translation id="5669703222995421982">התאמה אישית של תוכן</translation>
<translation id="5675650730144413517">הדף הזה לא עובד</translation>
<translation id="5710435578057952990">הזהות של אתר זה לא אומתה.</translation>
-<translation id="5713016350996637505">תוכן מטעה נחסם</translation>
+<translation id="5719499550583120431">אפשר לשלם באמצעות כרטיסים משולמים מראש.</translation>
<translation id="5720705177508910913">משתמש נוכחי:</translation>
<translation id="5732392974455271431">ההורים שלך יכולים לבטל בשבילך את החסימה</translation>
<translation id="5763042198335101085">עליך להזין כתובת אימייל חוקית</translation>
@@ -590,15 +614,14 @@ Del</translation>
<translation id="5803412860119678065">האם ברצונך למלא את פרטי הכרטיס <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">החיבור שלך אל <ph name="DOMAIN" /> מוצפן באמצעות חבילת צופן מיושנת.</translation>
<translation id="5813119285467412249">&amp;ביצוע מחדש של הוספה</translation>
-<translation id="5814352347845180253">אתה עשוי לאבד גישה אל תוכן פרמיום מ-<ph name="SITE" /> ומאתרים אחרים.</translation>
<translation id="5838278095973806738">אין להזין מידע רגיש באתר הזה (כמו סיסמאות או מספרי כרטיסי אשראי), מאחר שתוקפים עלולים לקבל אליו גישה.</translation>
<translation id="5869405914158311789">לא ניתן לגשת לאתר הזה</translation>
<translation id="5869522115854928033">סיסמאות שמורות</translation>
<translation id="5872918882028971132">הצעות להורים</translation>
+<translation id="5893752035575986141">אפשר לשלם באמצעות כרטיסי אשראי.</translation>
<translation id="5901630391730855834">צהוב</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (מסונכרנים)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{אחד נמצא בשימוש}two{שניים נמצאים בשימוש}many{# נמצאים בשימוש}other{# נמצאים בשימוש}}</translation>
-<translation id="5926846154125914413">אתה עשוי לאבד גישה אל תוכן פרמיום מאתרים מסוימים.</translation>
<translation id="5959728338436674663">‏שלח באופן אוטומטי <ph name="BEGIN_WHITEPAPER_LINK" />חלק מפרטי המערכת ותוכן הדף<ph name="END_WHITEPAPER_LINK" /> אל Google כדי לעזור בזיהוי של אפליקציות ואתרים מסוכנים. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">הסר מההיסטוריה</translation>
<translation id="5975083100439434680">התרחק</translation>
@@ -614,6 +637,7 @@ Del</translation>
<translation id="6040143037577758943">סגור</translation>
<translation id="6042308850641462728">עוד</translation>
<translation id="6047233362582046994">אם אתה מבין את סיכוני האבטחה, תוכל <ph name="BEGIN_LINK" />להיכנס לאתר הזה<ph name="END_LINK" /> לפני הסרת היישומים המזיקים.</translation>
+<translation id="6047927260846328439">ייתכן שבתוכן הזה יש מידע מטעה שנועד לגרום לך להתקין תוכנות או לחשוף מידע אישי. <ph name="BEGIN_LINK" />הצג בכל זאת<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">נכון לעכשיו אי אפשר לבקר באתר <ph name="SITE" />, מאחר שבאתר הזה נעשה שימוש בנעיצת אישורים. שגיאות רשת ומתקפות הן בדרך כלל זמניות, כך שהדף הזה יחזור כנראה לפעול מאוחר יותר.</translation>
<translation id="6060685159320643512">זהירות, ניסויים אלה עלולים לנשוך</translation>
<translation id="6080696365213338172">ניגשת לתוכן באמצעות אישור שהוענק על ידי מנהל מערכת. מנהל המערכת שלך עשוי ליירט נתונים שתספק ל-<ph name="DOMAIN" />.</translation>
@@ -627,7 +651,6 @@ Del</translation>
<translation id="6165508094623778733">למידע נוסף</translation>
<translation id="6169916984152623906">עכשיו באפשרותך לגלוש באופן פרטי, ואנשים אחרים שמשתמשים במכשיר הזה לא יראו את הפעילות שלך. עם זאת, עדיין תתבצע שמירה של הורדות וסימניות.</translation>
<translation id="6177128806592000436">החיבור שלך לאתר הזה לא מאובטח</translation>
-<translation id="6184817833369986695">(קבוצה: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">בדוק את חיבור האינטרנט</translation>
<translation id="6218753634732582820">‏האם להסיר מ-Chromium את הכתובת?</translation>
<translation id="6221345481584921695">‏לאחרונה, 'גלישה בטוחה של Google' ‏<ph name="BEGIN_LINK" />זיהתה תוכנה זדונית<ph name="END_LINK" /> ב-<ph name="SITE" />. אתרים שבדרך כלל נחשבים לבטוחים נדבקים לעתים בתוכנה זדונית. התוכן הזדוני מגיע מ-<ph name="SUBRESOURCE_HOST" />, מפיץ ידוע של תוכנות זדוניות.</translation>
@@ -646,13 +669,14 @@ Del</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>
<translation id="6387478394221739770">‏מעוניין בתכונות חדשות ומגניבות של Chrome? נסה את ערוץ הביטא שלנו בכתובת chrome.com/beta.</translation>
-<translation id="6389758589412724634">‏אזל הזיכרון הזמין ל-Chromium בניסיון להציג את דף האינטרנט הזה.</translation>
+<translation id="6397451950548600259">‏יש תוכנה במחשב שלך שמונעת מ-Chrome להתחבר באופן מאובטח אל האינטרנט</translation>
<translation id="6404511346730675251">ערוך סימניה</translation>
<translation id="6410264514553301377">הזן את תאריך התפוגה ואת קוד האימות של <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">שאלת אחד מהוריך אם אתה יכול לבקר באתר הזה</translation>
@@ -667,6 +691,7 @@ Del</translation>
<translation id="647261751007945333">מדיניות המכשיר</translation>
<translation id="6477321094435799029">‏Chrome זיהה קוד חריג בדף הזה וחסם אותו כדי להגן על המידע הפרטי שלך (כגון סיסמאות, מספרי טלפון ומספרי כרטיסי אשראי).</translation>
<translation id="6489534406876378309">התחל להעלות קריסות</translation>
+<translation id="6507833130742554667">אפשר לשלם באמצעות כרטיסי אשראי וחיוב.</translation>
<translation id="6508722015517270189">‏הפעלה מחדש של Chrome</translation>
<translation id="6529602333819889595">&amp;ביצוע מחדש של מחיקה</translation>
<translation id="6534179046333460208">הצעות לאינטרנט הווירטופיזי</translation>
@@ -675,13 +700,13 @@ Del</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="657639383826808334">ייתכן שהתוכן הזה מכיל אמצעים שינסו להתקין תוכנה מסוכנת, שגונבת או מוחקת את המידע שלך. <ph name="BEGIN_LINK" />הצג בכל זאת<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">אפשרויות הצפנה</translation>
<translation id="662080504995468778">הישאר</translation>
<translation id="6626291197371920147">הוסף מספר כרטיס חוקי</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> חיפוש</translation>
<translation id="6630809736994426279">‏תוקפים שנמצאים כרגע באתר <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> עלולים להתקין במחשב ה-MAC שלך תוכנות מסוכנות שגונבות מידע או מוחקות אותו (לדוגמה, תמונות, סיסמאות, הודעות וכרטיסי אשראי). <ph name="BEGIN_LEARN_MORE_LINK" />מידע נוסף<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">מדיניות זו אינה בתוקף.</translation>
+<translation id="6657585470893396449">סיסמה</translation>
<translation id="6671697161687535275">‏האם להסיר מ-Chromium הצעות לטפסים?</translation>
<translation id="6685834062052613830">צא והשלם את ההגדרה</translation>
<translation id="6710213216561001401">הקודם</translation>
@@ -712,7 +737,6 @@ Del</translation>
<translation id="6970216967273061347">מחוז</translation>
<translation id="6973656660372572881">‏צוינו שרתי Proxy קבועים וכתובת אתר של הסקריפט מסוג ‎.Pac</translation>
<translation id="6989763994942163495">הצג הגדרות מתקדמות...</translation>
-<translation id="7000990526846637657">לא נמצאו רשומות היסטוריה</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">‏ייתכן שלחשבון Google שלך יש סוגים אחרים של היסטוריית גלישה שזמינים בכתובת <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">סיסמאות</translation>
@@ -727,7 +751,9 @@ Del</translation>
<translation id="7129409597930077180">לא ניתן לבצע משלוח לכתובת הזו. עליך לבחור כתובת אחרת.</translation>
<translation id="7138472120740807366">שיטת מסירה</translation>
<translation id="7139724024395191329">אמירות</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}two{<ph name="PAYMENT_METHOD_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}many{<ph name="PAYMENT_METHOD_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">התשלום אינו מאובטח</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}two{<ph name="SHIPPING_OPTION_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}many{<ph name="SHIPPING_OPTION_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ועוד <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">הפעל מחדש עכשיו</translation>
<translation id="7180611975245234373">רענן</translation>
<translation id="7182878459783632708">לא הוגדרה מדיניות</translation>
@@ -768,6 +794,7 @@ Del</translation>
<translation id="7514365320538308">הורד</translation>
<translation id="7518003948725431193">לא נמצא דף אינטרנט עבור כתובת האינטרנט: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">החיבור שלך לאתר זה אינו פרטי</translation>
<translation id="7535087603100972091">ערך</translation>
<translation id="7537536606612762813">הכרחי</translation>
<translation id="7542403920425041731">ברגע שתאשר, פרטי הכרטיס שלך ישותפו עם האתר הזה.</translation>
@@ -777,7 +804,6 @@ Del</translation>
<translation id="7552846755917812628">אולי הטיפים האלו יעזרו לך:</translation>
<translation id="7554791636758816595">כרטיסייה חדשה</translation>
<translation id="7567204685887185387">השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />. ייתכן שאישור האבטחה שלו נופק כהונאה. הסיבה לכך עשויה להיות הגדרה שגויה או תוקף המיירט את החיבור שלך.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ונוסף}two{<ph name="CONTACT_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ונוספים}many{<ph name="CONTACT_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ונוספים}other{<ph name="CONTACT_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ונוספים}}</translation>
<translation id="7568593326407688803">זהו דף ב<ph name="ORIGINAL_LANGUAGE" />האם ברצונך לתרגם אותו?</translation>
<translation id="7569952961197462199">‏האם להסיר את כרטיס האשראי מ-Chrome?</translation>
<translation id="7569983096843329377">שחור</translation>
@@ -804,9 +830,11 @@ Del</translation>
<translation id="7714464543167945231">אישור</translation>
<translation id="7716147886133743102">נחסמה על-ידי מנהל המערכת</translation>
<translation id="7716424297397655342">לא ניתן לטעון את האתר הזה מהמטמון</translation>
+<translation id="774634243536837715">תוכן מסוכן נחסם.</translation>
<translation id="7752995774971033316">ללא ניהול</translation>
<translation id="7755287808199759310">אחד מההורים שלך יכול לבטל בשבילך את החסימה</translation>
<translation id="7758069387465995638">ייתכן שחומת אש או תוכנת אנטי-וירוס חסמו את החיבור.</translation>
+<translation id="7759163816903619567">דומיין תצוגה:</translation>
<translation id="7761701407923456692">אישור השרת אינו תואם לכתובת האתר.</translation>
<translation id="7763386264682878361">מנתח מניפסט התשלום</translation>
<translation id="7764225426217299476">הוסף כתובת</translation>
@@ -821,6 +849,7 @@ Del</translation>
<translation id="7815407501681723534">נמצאו <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> בנושא '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">עם זאת, אינך בלתי נראה. המעבר למצב גלישה בסתר לא מסתיר את הגלישה שלך מהמעסיק, מספק האינטרנט או מהאתרים שאליהם אתה נכנס.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">אפשר לשלם באמצעות כרטיסי חיוב וכרטיסים משולמים מראש.</translation>
<translation id="7887683347370398519">‏בדוק את ה-CVC ונסה שוב</translation>
<translation id="79338296614623784">עליך להזין מספר טלפון חוקי</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -840,7 +869,6 @@ Del</translation>
<translation id="8041089156583427627">שלח משוב</translation>
<translation id="8041940743680923270">השתמש בברירת המחדל הכללית (שאל)</translation>
<translation id="8088680233425245692">הצגת הפריט נכשלה.</translation>
-<translation id="8089520772729574115">‏פחות מ-‎1 MB</translation>
<translation id="8091372947890762290">ההפעלה ממתינה בשרת</translation>
<translation id="8118489163946903409">אמצעי תשלום</translation>
<translation id="8131740175452115882">אישור</translation>
@@ -852,13 +880,16 @@ Del</translation>
<translation id="8194797478851900357">&amp;ביטול העברה</translation>
<translation id="8201077131113104583">כתובת אתר לא חוקית לעדכון עבור תוסף עם המזהה "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">סיכום הזמנה</translation>
+<translation id="8205463626947051446">האתר נוטה להציג מודעות שמפריעות</translation>
<translation id="8218327578424803826">מיקום מוקצה:</translation>
<translation id="8225771182978767009">האדם שהגדיר את המחשב הזה בחר לחסום את האתר הזה.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">פתח את הדף בכרטיסייה חדשה של גלישה בסתר</translation>
<translation id="8241707690549784388">הדף שאתה מחפש השתמש במידע שהזנת. החזרה לדף זה עלולה לגרום לכפילות בפעולות שביצעת. האם ברצונך להמשיך?</translation>
+<translation id="8241712895048303527">חסום באתר הזה</translation>
<translation id="8249320324621329438">אוחזר לאחרונה:</translation>
<translation id="8253091569723639551">יש להזין כתובת לחיוב</translation>
-<translation id="8261506727792406068">מחק</translation>
+<translation id="8261506727792406068">מחיקה</translation>
<translation id="8289355894181816810">פנה אל מנהל הרשת אם אינך יודע מה זה אומר.</translation>
<translation id="8293206222192510085">הוסף סימניה</translation>
<translation id="8294431847097064396">מקור</translation>
@@ -866,7 +897,6 @@ Del</translation>
<translation id="8308427013383895095">התרגום נכשל עקב בעיה בחיבור הרשת.</translation>
<translation id="8332188693563227489">הגישה ל-<ph name="HOST_NAME" /> נדחתה</translation>
<translation id="834457929814110454">אם אתה מבין את סיכוני האבטחה, תוכל <ph name="BEGIN_LINK" />להיכנס לאתר לא בטוח זה<ph name="END_LINK" /> לפני הסרת התכניות המסוכנות.</translation>
-<translation id="8344669043927012510">‏פתיחת הדף במצב גלישה בסתר (‎⇧⌘N‎)</translation>
<translation id="8349305172487531364">סרגל סימניות</translation>
<translation id="8363502534493474904">לכבות את מצב הטיסה</translation>
<translation id="8364627913115013041">לא מוגדר.</translation>
@@ -876,6 +906,7 @@ Del</translation>
<translation id="8398259832188219207">דוח קריסה הועלה ב-<ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">קריסות (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">עליך להזין את אותו משפט-סיסמה פעמיים.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">הגדרות</translation>
<translation id="8433057134996913067">פעולה זו תוציא אותך מהחשבון ברוב האתרים.</translation>
<translation id="8437238597147034694">&amp;ביטול העברה</translation>
@@ -883,8 +914,9 @@ Del</translation>
<translation id="8483780878231876732">‏כדי להשתמש בכרטיסים מחשבון Google, היכנס אל Chrome</translation>
<translation id="8488350697529856933">חל על</translation>
<translation id="8498891568109133222">ל-<ph name="HOST_NAME" /> נדרש זמן רב מדי להגיב.</translation>
-<translation id="8532105204136943229">שנת פקיעת התוקף</translation>
+<translation id="8503813439785031346">שם משתמש</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="8571890674111243710">מתרגם דף ל<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">הוסף מספר טלפון
@@ -892,6 +924,7 @@ Del</translation>
<translation id="859285277496340001">האישור אינו מציין מנגנון הבודק אם הוא נשלל.</translation>
<translation id="8620436878122366504">ההורים שלך עדיין לא אישרו זאת</translation>
<translation id="8647750283161643317">אפס הכל לברירת המחדל</translation>
+<translation id="8660471606262461360">‏מ-Google Payments</translation>
<translation id="8703575177326907206">ההתחברות שלך אל <ph name="DOMAIN" /> אינה מוצפנת.</translation>
<translation id="8718314106902482036">התשלום לא הושלם</translation>
<translation id="8725066075913043281">נסה שוב</translation>
@@ -919,6 +952,7 @@ Del</translation>
<translation id="8903921497873541725">התקרב</translation>
<translation id="8931333241327730545">‏האם ברצונך לשמור את הכרטיס הזה בחשבון Google שלך?</translation>
<translation id="8932102934695377596">השעון שלך מאחר</translation>
+<translation id="8938939909778640821">כרטיסי אשראי וכרטיסים משולמים מראש שהסוחר מקבל</translation>
<translation id="8971063699422889582">פג תוקפו של אישור השרת.</translation>
<translation id="8986494364107987395">‏שלח ל-Google דוחות קריסה וסטטיסטיקת שימוש באופן אוטומטי</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -937,7 +971,7 @@ Del</translation>
<translation id="9065203028668620118">ערוך</translation>
<translation id="9068849894565669697">בחירת צבע</translation>
<translation id="9069693763241529744">נחסמה על-ידי תוסף</translation>
-<translation id="9076283476770535406">ייתכן שהאתר מכיל תוכן למבוגרים</translation>
+<translation id="9076283476770535406">ייתכן שהאתר מכיל תוכן למבוגרים בלבד</translation>
<translation id="9078964945751709336">נדרש מידע נוסף</translation>
<translation id="9103872766612412690">‏האתר <ph name="SITE" /> משתמש בדרך כלל בהצפנה כדי להגן על המידע שלך. כאשר Chromium ניסה הפעם להתחבר ל-<ph name="SITE" />, האתר שלח חזרה אישורים חריגים ושגויים. ייתכן שתוקף מנסה להתחזות לאתר <ph name="SITE" />, או שמסך כניסה ל-Wi-Fi הפריע לחיבור. המידע שלך עדיין מאובטח מכיוון ש-Chromium הפסיק את החיבור לפני חילופי הנתונים.</translation>
<translation id="9137013805542155359">הצג מקור</translation>
@@ -948,7 +982,6 @@ Del</translation>
<translation id="9169664750068251925">חסום תמיד באתר זה</translation>
<translation id="9170848237812810038">&amp;ביטול</translation>
<translation id="917450738466192189">אישור השרת לא חוקי.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> נוספת}two{<ph name="SHIPPING_OPTION_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> נוספות}many{<ph name="SHIPPING_OPTION_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> נוספות}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ו-<ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> נוספות}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> משתמש בפרוטוקול שאינו נתמך.</translation>
<translation id="9205078245616868884">הנתונים שלך מוצפנים באמצעות ביטוי הסיסמה לסינכרון. הזן אותו כדי להתחיל בסינכרון.</translation>
<translation id="9207861905230894330">הוספת הפריט נכשלה.</translation>
@@ -957,9 +990,9 @@ Del</translation>
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">נקה את הטופס</translation>
<translation id="939736085109172342">תיקייה חדשה</translation>
-<translation id="941721044073577244">נראה שאין לך הרשאה לבקר באתר הזה</translation>
<translation id="969892804517981540">גירסה רשמית</translation>
<translation id="975560348586398090">{COUNT,plural, =0{ללא}=1{פריט אחד}two{שני פריטים}many{# פריטים}other{# פריטים}}</translation>
+<translation id="981121421437150478">לא מקוון</translation>
<translation id="988159990683914416">גירסת מפתחים</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ja.xtb b/chromium/components/strings/components_strings_ja.xtb
index 7a3d37dd1c9..fe09f7abf73 100644
--- a/chromium/components/strings/components_strings_ja.xtb
+++ b/chromium/components/strings/components_strings_ja.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">他のアプリを終了する</translation>
<translation id="1055184225775184556">追加の取り消し(&amp;U)</translation>
<translation id="10614374240317010">常に保存しない</translation>
+<translation id="1066396345355680611"><ph name="SITE" /> と他の一部のサイトで、保護されたコンテンツにアクセスできなくなる可能性があります。</translation>
<translation id="106701514854093668">パソコンのブックマーク</translation>
<translation id="1074497978438210769">保護されていません</translation>
<translation id="1080116354587839789">ウィンドウ幅に合わせる</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">リーディング リスト</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" /> にクラッシュ レポートが作成されました(まだアップロードされておらず、無視の指定もありません)</translation>
<translation id="1281526147609854549">発行元: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">危険なコンテンツがブロックされました</translation>
<translation id="1285320974508926690">このサイトは翻訳しない</translation>
<translation id="129553762522093515">最近閉じたタブ</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Cookie を消去してみてください<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">登録ドメイン:</translation>
<translation id="1340482604681802745">引取先住所</translation>
-<translation id="1344211575059133124">このサイトにアクセスするには許可が必要です</translation>
<translation id="1344588688991793829">Chromium の自動入力設定...</translation>
<translation id="1348198688976932919">アクセス先のサイトには危険なアプリがあります</translation>
<translation id="1374468813861204354">ヒント</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" /> の <ph name="ORGANIZATION" /> の ID は、<ph name="ISSUER" /> によって確認済みです。</translation>
<translation id="1426410128494586442">はい</translation>
<translation id="1430915738399379752">印刷</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 件)}other{<ph name="PAYMENT_METHOD_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 件)}}</translation>
<translation id="1506687042165942984">このページの保存されているコピー(古いバージョン)を表示します。</translation>
<translation id="1517433312004943670">電話番号が必要です</translation>
+<translation id="1517500485252541695">利用可能なクレジット カードとデビットカード</translation>
<translation id="1519264250979466059">ビルド日</translation>
+<translation id="1527263332363067270">接続を待機しています…</translation>
<translation id="153384715582417236">現時点では他にありません</translation>
<translation id="1549470594296187301">この機能を使用するには JavaScript を有効にする必要があります。</translation>
<translation id="1555130319947370107">青</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">AMEX</translation>
<translation id="1734878702283171397">システム管理者にお問い合わせください。</translation>
<translation id="1740951997222943430">有効期限(月)を正しい形式で入力してください</translation>
-<translation id="1745358365027406341">後でページをダウンロード</translation>
<translation id="17513872634828108">開いているタブ</translation>
<translation id="1753706481035618306">ページ番号</translation>
<translation id="1763864636252898013">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は、ご使用のデバイスのオペレーティング システムによって信頼されているものではありません。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">必須フィールド</translation>
<translation id="187918866476621466">起動ページを開く</translation>
<translation id="1883255238294161206">リストを折りたたむ</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 件)}other{<ph name="SHIPPING_ADDRESS_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 件)}}</translation>
<translation id="1898423065542865115">フィルタ</translation>
+<translation id="1916770123977586577">更新後の設定をこのサイトに適用するには、このページを再読み込みしてください</translation>
+<translation id="1919345977826869612">広告</translation>
<translation id="192020519938775529">{COUNT,plural, =0{なし}=1{1 件のサイト}other{# 件のサイト}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> に移動します</translation>
+<translation id="1948773908305951926">利用可能なプリペイド カード</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> のブックマーク</translation>
<translation id="1973335181906896915">シリアル化エラーです</translation>
<translation id="1974060860693918893">詳細設定</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP エラーです</translation>
<translation id="2270484714375784793">電話番号</translation>
<translation id="2282872951544483773">利用できない試験運用機能</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> 件のアイテム}other{<ph name="ITEM_COUNT" /> 件のアイテム}}</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>
<translation id="2337852623177822836">管理者が指定する設定</translation>
<translation id="2354001756790975382">その他のブックマーク</translation>
<translation id="2354430244986887761"><ph name="SITE" /> では最近、Google セーフ ブラウジングにより<ph name="BEGIN_LINK" />有害なアプリが検出<ph name="END_LINK" />されました。</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">続行</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> に作成されたクラッシュ レポートはアップロードされませんでした</translation>
<translation id="2367567093518048410">レベル</translation>
-<translation id="237718015863234333">代替ユーザーインターフェースはありません</translation>
<translation id="2384307209577226199">企業のデフォルト</translation>
<translation id="2386255080630008482">サーバーの証明書は取り消されています。</translation>
<translation id="2392959068659972793">値が設定されていないポリシーを表示する</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">チェックボックスをオンにすると、Chromium がカード情報をこの端末に保存するためフォームにすばやく入力できるようになります。</translation>
<translation id="2498091847651709837">新しいカードをスキャン</translation>
<translation id="2501278716633472235">戻る</translation>
+<translation id="2503184589641749290">利用可能なデビットカードとプリペイド カード</translation>
<translation id="2515629240566999685">電波状況を確認する</translation>
-<translation id="2516305470678292029">代替ユーザーインターフェース</translation>
<translation id="2539524384386349900">検出</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> から無効な応答が送信されました。</translation>
<translation id="2556876185419854533">編集の取り消し(&amp;U)</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">配送方法</translation>
<translation id="277499241957683684">デバイス レコードがありません</translation>
<translation id="2784949926578158345">接続がリセットされました。</translation>
+<translation id="2788784517760473862">利用可能なクレジット カード</translation>
<translation id="2794233252405721443">サイトがブロックされています</translation>
<translation id="2799020568854403057">アクセス先のサイトには有害なアプリがあります</translation>
<translation id="2803306138276472711"><ph name="SITE" /> では最近、Google セーフ ブラウジングにより、<ph name="BEGIN_LINK" />不正なソフトウェアが検出されました<ph name="END_LINK" />。通常は安全なウェブサイトであっても、不正なソフトウェアに感染している場合があります。</translation>
<translation id="2824775600643448204">アドレス検索バー</translation>
<translation id="2826760142808435982">接続は <ph name="CIPHER" /> を使用して暗号化および認証されており、<ph name="KX" /> が鍵交換メカニズムとして使用されています。</translation>
<translation id="2835170189407361413">フォームをクリア</translation>
+<translation id="2851634818064021665">このサイトにアクセスするには許可が必要です</translation>
<translation id="2856444702002559011"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> では、悪意のあるユーザーによって、パスワード、メッセージ、クレジット カードなどの情報が盗まれる可能性があります。<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">再読み込みを中止</translation>
-<translation id="2900469785430194048">このウェブページを表示しようとしましたが、Google Chrome のメモリが不足しています。</translation>
<translation id="2909946352844186028">ネットワークの変更が検出されました。</translation>
<translation id="2916038427272391327">他のプログラムを終了する</translation>
<translation id="2922350208395188000">サーバーの証明書を確認できません。</translation>
<translation id="2928905813689894207">請求先住所</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 件)}other{<ph name="SHIPPING_ADDRESS_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 件)}}</translation>
<translation id="2941952326391522266">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は <ph name="DOMAIN2" /> から発行されています。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation>
<translation id="2948083400971632585">接続用に設定されたプロキシは、設定ページで無効にできます。</translation>
<translation id="2955913368246107853">検索バーを閉じる</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">有効にすると、Chromium がカード情報をこの端末に保存するためフォームにすばやく入力できるようになります。</translation>
<translation id="2985398929374701810">有効な住所を入力してください</translation>
<translation id="2986368408720340940">この受け取り方法はご利用いただけません。別の方法を選択してください。</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">シークレット タブで表示したページの記録は、シークレット タブをすべて閉じた後、ブラウザの履歴、Cookie の保存場所、検索履歴から消去されます。ただし、ダウンロードしたファイルや作成したブックマークは保存されます。</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">島</translation>
-<translation id="317583078218509884">サイトの新しい権限設定はページの再読み込み後に有効になります。</translation>
<translation id="3176929007561373547">プロキシの設定を確認するか、ネットワーク管理者に問い合わせて、プロキシ サーバーが正常に
動作しているかどうかを確認してください。プロキシ サーバーを使用していない場合は
次の方法をお試しください。
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">ページをシークレット モードで開く</translation>
<translation id="320323717674993345">支払いをキャンセル</translation>
<translation id="3207960819495026254">ブックマークしました</translation>
<translation id="3225919329040284222">サーバーの提示した証明書が、組み込まれている想定の証明書と一致しません。これらの想定の証明書は、ユーザー保護のため、特定の安全性の高いウェブサイトについて用意されています。</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">順序変更の取り消し(&amp;U)</translation>
<translation id="3282497668470633863">名義人名を追加</translation>
<translation id="3286538390144397061">今すぐ再起動</translation>
+<translation id="3287510313208355388">オンライン時にダウンロード</translation>
<translation id="3303855915957856445">一致する結果は見つかりませんでした</translation>
<translation id="3305707030755673451">データは <ph name="TIME" /> に同期パスフレーズで暗号化されました。同期を開始するには、同期パスフレーズを入力してください。</translation>
<translation id="3320021301628644560">請求先住所を追加</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">取得間隔:</translation>
<translation id="3462200631372590220">詳細情報を表示しない</translation>
<translation id="3467763166455606212">カード名義人は必須です</translation>
-<translation id="3478058380795961209">有効期限(月)</translation>
<translation id="3479539252931486093">想定外の動作である場合は、<ph name="BEGIN_LINK" />問題を報告<ph name="END_LINK" />してください。</translation>
<translation id="3479552764303398839">後で</translation>
<translation id="3498215018399854026">現在、保護者にたずねることができません。もう一度お試しください。</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;&lt;strong&gt;設定&lt;/strong&gt;アプリの [&lt;strong&gt;全般&lt;/strong&gt;] で日時を調整してください。&lt;/p&gt;<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="COUNTRY" /> <ph name="STATE" /> <ph name="CITY" /></translation>
+<translation id="358285529439630156">クレジット カードとプリペイド カードをご利用いただけます。</translation>
<translation id="3582930987043644930">名前を追加</translation>
<translation id="3583757800736429874">移動のやり直し(&amp;R)</translation>
<translation id="3586931643579894722">詳細を非表示</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">このエラーが頻繁に表示される場合は、こちらの<ph name="HELP_LINK" />をお試しください。</translation>
<translation id="3658742229777143148">変更履歴</translation>
<translation id="3678029195006412963">リクエストに署名できませんでした</translation>
+<translation id="3678529606614285348">ページを新しいシークレット ウィンドウで開く(Ctrl-Shift-N)</translation>
<translation id="3679803492151881375"><ph name="CRASH_TIME" /> にクラッシュ レポートが作成され、<ph name="UPLOAD_TIME" /> にアップロードされました</translation>
<translation id="3681007416295224113">証明書情報</translation>
<translation id="3690164694835360974">ログイン情報は保護されません</translation>
-<translation id="3693415264595406141">パスワード:</translation>
<translation id="3704609568417268905"><ph name="TIME" />、<ph name="TITLE" />(<ph name="DOMAIN" />)を<ph name="BOOKMARKED" /></translation>
<translation id="370665806235115550">読み込んでいます...</translation>
<translation id="3712624925041724820">ライセンスを使い切りました</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805"><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="375403751935624634">サーバー エラーのため翻訳できませんでした。</translation>
<translation id="3759461132968374835">最近発生した障害はありません。障害レポートが無効になっているときに発生した障害は、ここには表示されません。</translation>
+<translation id="3765032636089507299">セーフ ブラウジング ページは準備中です。</translation>
<translation id="3778403066972421603">このカードを Google アカウントとこの端末に保存しますか?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">有効期限: <ph name="EXPIRATION_MONTH" /> / <ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">このサイトへのアクセス リクエストを <ph name="NAME" /> さんに送信しました</translation>
<translation id="3890664840433101773">メールを追加</translation>
<translation id="3901925938762663762">カードの有効期限が切れています</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">クラッシュ レポート ID <ph name="CRASH_ID" /> がアップロードされました(ローカルのクラッシュ ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書で SAN(サブジェクトの別名)が指定されていません。設定が不適切であるか、悪意のあるユーザーによって接続が妨害されている可能性があります。</translation>
+<translation id="3949601375789751990">閲覧履歴がここに表示されます</translation>
<translation id="3963721102035795474">リーダーモード</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{なし}=1{1 件のサイトから}other{# 件のサイトから}}</translation>
<translation id="397105322502079400">計算しています...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">サイトの設定</translation>
<translation id="4169947484918424451">Chromium にこのカードを保存しますか?</translation>
<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="4196861286325780578">移動のやり直し(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ファイアウォールとウイルス対策の設定を確認する<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">クラッシュ</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">このサイトを利用すると、悪意のあるユーザーによって、危険なプログラム(写真、パスワード、メッセージ、クレジット カード番号などの情報を盗み取るか削除するプログラム)がお使いのパソコンにインストールされる可能性があります。</translation>
<translation id="4372948949327679948"><ph name="VALUE_TYPE" /> 値が想定されます。</translation>
<translation id="4377125064752653719"><ph name="DOMAIN" /> にアクセスしようとしましたが、サーバーから提示された証明書は発行元により取り消されています。これは、サーバーから提示されたセキュリティ認証情報が信頼できないことを示しており、悪意のあるユーザーと通信しようとしている可能性があります。</translation>
-<translation id="4381091992796011497">ユーザー名:</translation>
<translation id="4394049700291259645">無効にする</translation>
<translation id="4406896451731180161">検索結果</translation>
<translation id="4424024547088906515">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は Chrome によって信頼されているものではありません。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> でログイン証明書が承認されなかったか、ログイン証明書が提示されていない可能性があります。</translation>
<translation id="443673843213245140">プロキシの使用は無効ですが、プロキシの設定が明示的に指定されています。</translation>
+<translation id="445100540951337728">利用可能なデビットカード</translation>
<translation id="4506176782989081258">検証エラー: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">システム管理者に問い合わせる</translation>
<translation id="450710068430902550">管理者との共有</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Chrome からアドレスを削除してもよろしいですか?</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" /> への接続は新しい暗号スイートにより暗号化されています。</translation>
<translation id="4594403342090139922">削除の取り消し(&amp;U)</translation>
+<translation id="4611292653554630842">ログイン</translation>
<translation id="4619615317237390068">他のデバイスからのタブ</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書にはエラーがあります。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation>
<translation id="4690462567478992370">無効な証明書の使用をやめる</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">接続が中断されました</translation>
+<translation id="471880041731876836">このサイトへのアクセスが許可されていません</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows ネットワーク診断ツールを実行する<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">ポリシーを再読み込み</translation>
<translation id="4728558894243024398">プラットフォーム</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">表示</translation>
<translation id="4854362297993841467">この配達方法はご利用いただけません。別の方法を選択してください。</translation>
<translation id="4858792381671956233">このサイトを開いてもよいかの問い合わせを保護者に送信しました</translation>
-<translation id="4863764087567530506">アクセス先のコンテンツは、ユーザーをだましてソフトウェアをインストールさせようとしたり、個人情報を危険にさらしたりする可能性があります。<ph name="BEGIN_LINK" />危険性を理解したうえで表示する<ph name="END_LINK" /></translation>
<translation id="4880827082731008257">履歴を検索</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />、<ph name="TYPE_2" />、<ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">認証が必要です</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{他 1 件のウェブページ}other{他 # 件のウェブページ}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">このページは、不明な言語から<ph name="LANGUAGE_LANGUAGE" />に翻訳されました。</translation>
<translation id="4923459931733593730">お支払い</translation>
<translation id="4926049483395192435">指定する必要があります。</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" /> / <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">操作</translation>
<translation id="4958444002117714549">リストを展開する</translation>
<translation id="4974590756084640048">警告を再度有効にする</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">パスワードが正しくありません</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>
<translation id="5087580092889165836">カードを追加</translation>
<translation id="5089810972385038852">都道府県 / 州</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">地方</translation>
<translation id="5115563688576182185">(64 ビット)</translation>
<translation id="5141240743006678641">Google の認証情報で同期パスワードを暗号化する</translation>
-<translation id="514421653919133810">ページをシークレット モードで開く(Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">ポリシー応答内にエラー コードがあります</translation>
+<translation id="5159010409087891077">ページを新しいシークレット ウィンドウで開く(⇧⌘N)</translation>
<translation id="5171045022955879922">検索または URL を入力</translation>
<translation id="5172758083709347301">マシン</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />でない場合はこのエラーを報告する</translation>
-<translation id="5181140330217080051">ダウンロードしています</translation>
<translation id="5190835502935405962">ブックマーク バー</translation>
<translation id="5199729219167945352">試験運用機能</translation>
<translation id="5205222826937269299">名前は必須です</translation>
<translation id="5222812217790122047">メールアドレスは必須です</translation>
<translation id="5251803541071282808">クラウド</translation>
<translation id="5277279256032773186">会社で Chrome を使用する場合は、従業員用に Chrome の設定を管理できます。詳細</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />このソフトウェアを一時的に無効にしてインターネットに接続できるようにするには、次の手順を行います。なお、この作業には管理者権限が必要です。<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />スタート<ph name="END_BOLD" /> メニューをクリックし、[<ph name="BEGIN_BOLD" />ローカル サービスの表示<ph name="END_BOLD" />] を検索して選択します
+ <ph name="LIST_ITEM" />[<ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />] を選択します
+ <ph name="LIST_ITEM" />[<ph name="BEGIN_BOLD" />スタートアップの種類<ph name="END_BOLD" />] の下で [<ph name="BEGIN_BOLD" />無効<ph name="END_BOLD" />] を選択します
+ <ph name="LIST_ITEM" />[<ph name="BEGIN_BOLD" />サービスの状態<ph name="END_BOLD" />] の下で [<ph name="BEGIN_BOLD" />停止<ph name="END_BOLD" />] をクリックします
+ <ph name="LIST_ITEM" />[<ph name="BEGIN_BOLD" />適用<ph name="END_BOLD" />] をクリックし、[<ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />] をクリックします
+ <ph name="LIST_ITEM" />このソフトウェアをパソコンから完全に削除する方法については、<ph name="BEGIN_LEARN_MORE_LINK" />Chrome ヘルプセンター記事<ph name="END_LEARN_MORE_LINK" />をご覧ください
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">このサイトへの接続ではプライバシーが保護されません。VR モードを終了するには、ヘッドセットを外して「戻る」を押します。</translation>
<translation id="5299298092464848405">ポリシーの解析中にエラーが発生しました</translation>
<translation id="5308689395849655368">障害レポートが無効になっています。</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">「<ph name="ERROR_PATH" />」でスキーマ確認エラー: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">この <ph name="HOST_NAME" /> ページが見つかりません</translation>
<translation id="5455374756549232013">ポリシーのタイムスタンプが不適切です</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> / <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">無効</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 件)}other{<ph name="CONTACT_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 件)}}</translation>
<translation id="5470861586879999274">編集のやり直し(&amp;R)</translation>
+<translation id="5481076368049295676">アクセス先のコンテンツによって、個人情報を入手したり削除したりする危険なソフトウェアがインストールされる可能性があります。<ph name="BEGIN_LINK" />危険性を理解したうえで表示する<ph name="END_LINK" /></translation>
<translation id="54817484435770891">有効なアドレスを追加</translation>
<translation id="5492298309214877701">このサイトは企業、団体、または学校のイントラネット上にありますが、外部のウェブサイトと同じ URL が使用されています。
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">リクエストされた記事が見つかりませんでした。</translation>
<translation id="5544037170328430102"><ph name="SITE" /> に埋め込まれているページの内容:</translation>
<translation id="5556459405103347317">再読み込み</translation>
+<translation id="5560088892362098740">有効期限</translation>
<translation id="5565735124758917034">有効</translation>
<translation id="5571083550517324815">この住所での受け取りはできません。別の住所を選択してください。</translation>
<translation id="5572851009514199876">このサイトへのアクセス権があるかどうかを Chrome で確認できるように、Chrome を起動してログインしてください。</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">ポリシー設定を読み込めませんでした</translation>
<translation id="5631439013527180824">無効なデバイス管理トークンです</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="5646376287012673985">現在地</translation>
<translation id="5659593005791499971">メール</translation>
<translation id="5669703222995421982">自分向けのコンテンツを表示</translation>
<translation id="5675650730144413517">このページは動作していません</translation>
<translation id="5710435578057952990">このウェブサイトの ID は確認されていません。</translation>
-<translation id="5713016350996637505">不正の可能性があるコンテンツがブロックされました</translation>
+<translation id="5719499550583120431">プリペイド カードをご利用いただけます。</translation>
<translation id="5720705177508910913">現在のユーザー</translation>
<translation id="5732392974455271431">ブロックの解除は保護者が行うことができます</translation>
<translation id="5763042198335101085">有効なメールアドレスを入力してください</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065"><ph name="CARD_DETAIL" /> を入力しますか?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" /> への接続は古い暗号スイートにより暗号化されています。</translation>
<translation id="5813119285467412249">追加のやり直し(&amp;R)</translation>
-<translation id="5814352347845180253"><ph name="SITE" /> と他の一部のサイトのプレミアム コンテンツにアクセスできなくなる可能性があります。</translation>
<translation id="5838278095973806738">このサイトでは機密情報(パスワード、クレジット カードなど)を入力しないでください。悪意のあるユーザーに情報が盗まれる恐れがあります。</translation>
<translation id="5869405914158311789">このサイトにアクセスできません</translation>
<translation id="5869522115854928033">保存したパスワード</translation>
<translation id="5872918882028971132">保護者からのおすすめ</translation>
+<translation id="5893752035575986141">クレジット カードをご利用いただけます。</translation>
<translation id="5901630391730855834">黄</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" />(同期済み)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 個が使用中}other{# 個が使用中}}</translation>
-<translation id="5926846154125914413">一部のサイトのプレミアム コンテンツにアクセスできなくなる可能性があります。</translation>
<translation id="5959728338436674663">危険なアプリやサイトの検出に役立てるために一部の<ph name="BEGIN_WHITEPAPER_LINK" />システム情報やページのコンテンツ<ph name="END_WHITEPAPER_LINK" />を Google に自動送信する。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">履歴から削除</translation>
<translation id="5975083100439434680">縮小する</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">閉じる</translation>
<translation id="6042308850641462728">もっと見る</translation>
<translation id="6047233362582046994">有害なアプリがまだ存在する可能性があるにもかかわらず<ph name="BEGIN_LINK" />このサイトにアクセスする<ph name="END_LINK" />場合は、セキュリティ上の危険性をあらかじめご認識ください。</translation>
+<translation id="6047927260846328439">アクセス先のコンテンツは、ユーザーをだましてソフトウェアをインストールさせようとしたり、個人情報を危険にさらしたりする可能性があります。<ph name="BEGIN_LINK" />危険性を理解したうえで表示する<ph name="END_LINK" /></translation>
<translation id="6051221802930200923"><ph name="SITE" /> では証明書ピンニングが使用されているため、現在アクセスできません。通常、ネットワーク エラーやネットワークへの攻撃は一時的なものです。しばらくするとページにアクセスできるようになります。</translation>
<translation id="6060685159320643512">これらの試験運用版は問題が発生する可能性があるため、ご利用の際には十分ご注意ください</translation>
<translation id="6080696365213338172">管理者が提供する証明書を使用してコンテンツにアクセスしています。<ph name="DOMAIN" /> に提供するデータは管理者によって傍受される可能性があります。</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">詳しく見る</translation>
<translation id="6169916984152623906">現在、シークレット モードで閲覧しています。あなたのアクティビティは、この端末を利用する他のユーザーには表示されません。ただし、ダウンロードしたファイルとブックマークは通常どおり保存されます。</translation>
<translation id="6177128806592000436">このサイトへの接続は保護されていません</translation>
-<translation id="6184817833369986695">(コホート: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">インターネット接続を確認してください</translation>
<translation id="6218753634732582820">Chromium からアドレスを削除してもよろしいですか?</translation>
<translation id="6221345481584921695"><ph name="BEGIN_LINK" /> では最近、Google セーフ ブラウジングにより、<ph name="END_LINK" />不正なソフトウェアが検出されました<ph name="SITE" />。通常は安全なウェブサイトであっても、不正なソフトウェアに感染している場合があります。今回の悪意のあるコンテンツは、不正なソフトウェアの既知の配布元である「<ph name="SUBRESOURCE_HOST" />」からもたらされました。</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Chrome の新しい機能に関心をお持ちでしたら、chrome.com/beta から Beta チャンネルをお試しください。</translation>
-<translation id="6389758589412724634">このウェブページを表示しようとしましたが、Chromium のメモリが不足しています。</translation>
+<translation id="6397451950548600259">パソコンにインストールされているソフトウェアが原因で、Chrome からインターネットに安全に接続することができません</translation>
<translation id="6404511346730675251">ブックマークを編集</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> の有効期限と CVC を入力</translation>
<translation id="6414888972213066896">このサイトを開いてもよいかの問い合わせを保護者に送信しました</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">デバイス ポリシー</translation>
<translation id="6477321094435799029">このページで通常と異なるコードを検出したため、個人情報(例: パスワード、電話番号、クレジット カード番号)を保護するために、ページをブロックしました。</translation>
<translation id="6489534406876378309">クラッシュのアップロードを開始</translation>
+<translation id="6507833130742554667">クレジット カードとデビットカードをご利用いただけます。</translation>
<translation id="6508722015517270189">Chrome を再起動する</translation>
<translation id="6529602333819889595">削除のやり直し(&amp;R)</translation>
<translation id="6534179046333460208">フィジカル ウェブからの URL</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">アクセス先のコンテンツによって、個人情報を入手したり削除したりする危険なソフトウェアがインストールされる可能性があります。<ph name="BEGIN_LINK" />危険性を理解したうえで表示する<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">暗号化オプション</translation>
<translation id="662080504995468778">とどまる</translation>
<translation id="6626291197371920147">有効なカード番号を追加</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> 検索</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> では現在、悪意のあるユーザーによって、お使いの Mac 上に危険なプログラム(写真、パスワード、メッセージ、クレジット カードなどの情報を盗んだり削除したりするプログラム)がインストールされる可能性があります。<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">このポリシーは廃止されました。</translation>
+<translation id="6657585470893396449">パスワード</translation>
<translation id="6671697161687535275">Chromium から候補を削除してもよろしいですか?</translation>
<translation id="6685834062052613830">ログアウトして設定を完了してください</translation>
<translation id="6710213216561001401">前へ</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">地区</translation>
<translation id="6973656660372572881">固定プロキシ サーバーと .pac スクリプト URL の両方が指定されています。</translation>
<translation id="6989763994942163495">詳細設定を表示...</translation>
-<translation id="7000990526846637657">履歴項目が見つかりません</translation>
<translation id="7012363358306927923">中国銀聯</translation>
<translation id="7012372675181957985">Google アカウントでの他の形式の閲覧履歴が <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> に残ることがあります</translation>
<translation id="7029809446516969842">パスワード</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">この住所には配送できません。別の住所を選択してください。</translation>
<translation id="7138472120740807366">配達方法</translation>
<translation id="7139724024395191329">管轄区域</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 件)}other{<ph name="PAYMENT_METHOD_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 件)}}</translation>
<translation id="7155487117670177674">お支払い情報は保護されません</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 件)}other{<ph name="SHIPPING_OPTION_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 件)}}</translation>
<translation id="7179921470347911571">今すぐ再起動</translation>
<translation id="7180611975245234373">更新</translation>
<translation id="7182878459783632708">ポリシーが設定されていません</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">ダウンロード</translation>
<translation id="7518003948725431193">次の URL のウェブページは見つかりませんでした:<ph name="URL" /></translation>
<translation id="7521387064766892559">Javascript</translation>
+<translation id="7526934274050461096">このサイトへの接続ではプライバシーが保護されません</translation>
<translation id="7535087603100972091">値</translation>
<translation id="7537536606612762813">必須</translation>
<translation id="7542403920425041731">確認すると、カードの情報がこのサイトに共有されます。</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">次のことをお試しください。</translation>
<translation id="7554791636758816595">新しいタブ</translation>
<translation id="7567204685887185387">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は不正に発行されたものである可能性があります。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 件)}other{<ph name="CONTACT_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 件)}}</translation>
<translation id="7568593326407688803">これは<ph name="ORIGINAL_LANGUAGE" />のページです。翻訳しますか?</translation>
<translation id="7569952961197462199">Chrome からクレジット カードを削除してもよろしいですか?</translation>
<translation id="7569983096843329377">黒</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">証明書</translation>
<translation id="7716147886133743102">管理者によってブロック</translation>
<translation id="7716424297397655342">このサイトをキャッシュから読み込むことができません</translation>
+<translation id="774634243536837715">危険なコンテンツがブロックされました。</translation>
<translation id="7752995774971033316">管理されていません</translation>
<translation id="7755287808199759310">ブロックの解除は保護者が行うことができます</translation>
<translation id="7758069387465995638">ファイアウォールまたはウイルス対策ソフトウェアによって接続がブロックされた可能性があります。</translation>
+<translation id="7759163816903619567">表示ドメイン:</translation>
<translation id="7761701407923456692">サーバーの証明書が URL と一致しません。</translation>
<translation id="7763386264682878361">Payment Manifest Parser</translation>
<translation id="7764225426217299476">住所を追加</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">「<ph name="SEARCH_STRING" />」に対し <ph name="NUMBER_OF_RESULTS" /> 件の <ph name="SEARCH_RESULTS" />が見つかりました</translation>
<translation id="785549533363645510">あらゆる場所に記録が一切残らないわけではありません。シークレット モードを使っても、雇用主、インターネット サービス プロバイダ、訪問先のウェブサイトに閲覧内容が知られる可能性はあります。</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">デビットカードとプリペイド カードをご利用いただけます。</translation>
<translation id="7887683347370398519">CVC を確認してからもう一度お試しください</translation>
<translation id="79338296614623784">有効な電話番号を入力してください</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">フィードバックを送信</translation>
<translation id="8041940743680923270">グローバルのデフォルト値([確認])を使用</translation>
<translation id="8088680233425245692">記事を表示できませんでした。</translation>
-<translation id="8089520772729574115">1 MB 未満</translation>
<translation id="8091372947890762290">サーバーで有効化が保留になっています</translation>
<translation id="8118489163946903409">お支払い方法</translation>
<translation id="8131740175452115882">確認</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">移動の取り消し(&amp;U)</translation>
<translation id="8201077131113104583">ID「<ph name="EXTENSION_ID" />」の拡張機能に対する無効な更新 URL です。</translation>
<translation id="8202097416529803614">ご注文概要</translation>
+<translation id="8205463626947051446">煩わしい広告がよく表示されるサイト</translation>
<translation id="8218327578424803826">割り当てられた場所:</translation>
<translation id="8225771182978767009">このサイトは、このパソコンを設定したユーザーによってブロックされています。</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />、<ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">ページを新しいシークレット タブで開く</translation>
<translation id="8241707690549784388">検索しているページは、入力した情報を使用しています。このページに戻った場合、操作のやり直しが発生する可能性があります。続行しますか?</translation>
+<translation id="8241712895048303527">このサイトでブロック</translation>
<translation id="8249320324621329438">前回の取得:</translation>
<translation id="8253091569723639551">請求先住所が必要です</translation>
<translation id="8261506727792406068">削除</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">ネットワーク接続に問題があったため翻訳できませんでした。</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> へのアクセスが拒否されました</translation>
<translation id="834457929814110454">有害なプログラムが削除されるより前に<ph name="BEGIN_LINK" />このサイトにアクセスする<ph name="END_LINK" />場合は、セキュリティ上のリスクについてご承知おきください。</translation>
-<translation id="8344669043927012510">ページをシークレット モードで開く(⇧⌘N)</translation>
<translation id="8349305172487531364">ブックマーク バー</translation>
<translation id="8363502534493474904">機内モードをオフにする</translation>
<translation id="8364627913115013041">未設定</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">クラッシュ レポートが <ph name="UPLOAD_TIME" /> にアップロードされました</translation>
<translation id="8412145213513410671">障害数(<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">同じパスフレーズを 2 回入力する必要があります。</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">設定</translation>
<translation id="8433057134996913067">ほとんどのウェブサイトからログアウトします。</translation>
<translation id="8437238597147034694">移動の取り消し(&amp;U)</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Google アカウントに保存したカードを使用するには Chrome にログインしてください</translation>
<translation id="8488350697529856933">適用先</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> からの応答時間が長すぎます。</translation>
-<translation id="8532105204136943229">有効期限(年)</translation>
+<translation id="8503813439785031346">ユーザー名</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="8571890674111243710">ページを<ph name="LANGUAGE" />に翻訳しています...</translation>
<translation id="858637041960032120">電話番号を追加
@@ -887,6 +919,7 @@
<translation id="859285277496340001">この証明書には、取り消されたかどうかを確認する方法が指定されていません。</translation>
<translation id="8620436878122366504">保護者がまだサイトを開くことを許可していません</translation>
<translation id="8647750283161643317">すべてデフォルトに戻す</translation>
+<translation id="8660471606262461360">Google ペイメントから取得</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> への接続は暗号化されていません。</translation>
<translation id="8718314106902482036">支払い処理を完了できませんでした</translation>
<translation id="8725066075913043281">やり直し</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">拡大する</translation>
<translation id="8931333241327730545">このカードを Google アカウントに保存しますか?</translation>
<translation id="8932102934695377596">時計が遅れています</translation>
+<translation id="8938939909778640821">利用可能なクレジット カードとプリペイド カード</translation>
<translation id="8971063699422889582">サーバーの証明書の有効期限が切れています。</translation>
<translation id="8986494364107987395">使用統計データと障害レポートを Google に自動送信する</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">このサイトでは常にブロック</translation>
<translation id="9170848237812810038">取消(&amp;U)</translation>
<translation id="917450738466192189">サーバーの証明書が無効です。</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 件)}other{<ph name="SHIPPING_OPTION_PREVIEW" />(他 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 件)}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ではサポートされていないプロトコルが使用されています。</translation>
<translation id="9205078245616868884">データは同期パスフレーズで暗号化されます。同期を開始するには、同期パスフレーズを入力してください。</translation>
<translation id="9207861905230894330">記事を追加できませんでした。</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">フォームをクリア</translation>
<translation id="939736085109172342">新しいフォルダ</translation>
-<translation id="941721044073577244">このサイトへのアクセスが許可されていないようです</translation>
<translation id="969892804517981540">Official Build</translation>
<translation id="975560348586398090">{COUNT,plural, =0{なし}=1{1 件のアイテム}other{# 件のアイテム}}</translation>
+<translation id="981121421437150478">オフライン</translation>
<translation id="988159990683914416">Developer Build</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_kn.xtb b/chromium/components/strings/components_strings_kn.xtb
index e6156d7a658..affa60b927a 100644
--- a/chromium/components/strings/components_strings_kn.xtb
+++ b/chromium/components/strings/components_strings_kn.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">ಇತರ ಅಪ್ಲಿಕೇಶನ್‍ಗಳನ್ನು ಮುಚ್ಚಿ</translation>
<translation id="1055184225775184556">&amp;ಸೇರಿಸುವುದನ್ನು ರದ್ದುಗೊಳಿಸಿ</translation>
<translation id="10614374240317010">ಉಳಿಸಿಯೇ ಇಲ್ಲ</translation>
+<translation id="1066396345355680611"><ph name="SITE" /> ಮತ್ತು ಕೆಲವು ಇತರ ಸೈಟ್‌ಗಳಿಂದ ಸುರಕ್ಷಸಿತ ವಿಷಯಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ನೀವು ಕಳೆದುಕೊಳ್ಳಬಹುದು.</translation>
<translation id="106701514854093668">ಡೆಸ್ಕ್‌ಟಾಪ್ ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳು</translation>
<translation id="1074497978438210769">ಸುರಕ್ಷಿತವಾಗಿಲ್ಲ</translation>
<translation id="1080116354587839789">ಅಗಲಕ್ಕೆ ಹೊಂದಿಸಿ</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">ಓದುವ ಪಟ್ಟಿ</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" /> ನಲ್ಲಿ ಕ್ರ್ಯಾಶ್ ವರದಿಯನ್ನು ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ (ಇನ್ನೂ ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿಲ್ಲ ಅಥವಾ ನಿರ್ಲಕ್ಷಿಸಲಾಗಿಲ್ಲ)</translation>
<translation id="1281526147609854549"><ph name="ISSUER" /> ಇವರಿಂದ ನೀಡಲಾಗಿದೆ</translation>
-<translation id="1283919782143846010">ಅಪಾಯಕಾರಿ ವಿಷಯವನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ</translation>
<translation id="1285320974508926690">ಈ ಸೈಟ್ ಅನ್ನು ಎಂದಿಗೂ ಭಾಷಾಂತರಿಸದಿರಿ</translation>
<translation id="129553762522093515">ಇತ್ತೀಚೆಗೆ ಮುಚ್ಚಲಾಗಿರುವುದು</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />ನಿಮ್ಮ ಕುಕೀಗಳನ್ನು ತೆರವುಗೊಳಿಸಲು ಪ್ರಯತ್ನಿಸಿ<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">ಡೊಮೇನ್ ದಾಖಲಾತಿ:</translation>
<translation id="1340482604681802745">ಪಿಕಪ್ ವಿಳಾಸ</translation>
-<translation id="1344211575059133124">ಈ ಸೈಟ್‌ಗೆ ಭೇಟಿ ನೀಡಲು ನೀವು ಅನುಮತಿ ಪಡೆಯಬೇಕೆಂದು ತೋರುತ್ತಿದೆ</translation>
<translation id="1344588688991793829">Chromium ಸ್ವಯಂತುಂಬುವಿಕೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳು...</translation>
<translation id="1348198688976932919">ಮುಂದಿರುವ ಸೈಟ್‌ನಲ್ಲಿ ಅಪಾಯಕಾರಿ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿವೆ</translation>
<translation id="1374468813861204354">ಸಲಹೆಗಳು</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" /> ಯಲ್ಲಿರುವ <ph name="ORGANIZATION" /> ಗುರುತನ್ನು <ph name="ISSUER" /> ನಿಂದ ಪರಿಶೀಲಿಸಲಾಗಿದೆ.</translation>
<translation id="1426410128494586442">ಹೌದು</translation>
<translation id="1430915738399379752">ಮುದ್ರಿಸು</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ಇನ್ನಷ್ಟು}one{<ph name="PAYMENT_METHOD_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ಇನ್ನಷ್ಟು}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="1506687042165942984">ಈ ಪುಟದ ಉಳಿಸಲಾದ (ಉದಾ. ಹಳೆಯದು ಎಂದು ಕರೆಯಲಾಗುವ) ನಕಲನ್ನು ತೋರಿಸಿ.</translation>
<translation id="1517433312004943670">ಫೋನ್ ಸಂಖ್ಯೆ ಅಗತ್ಯವಿದೆ</translation>
+<translation id="1517500485252541695">ಸಮ್ಮತಿಸಲಾದ ಕ್ರೆಡಿಟ್ ಮತ್ತು ಡೆಬಿಟ್ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="1519264250979466059">ಬಿಲ್ಡ್ ಡೇಟಾ</translation>
+<translation id="1527263332363067270">ಸಂಪರ್ಕಕ್ಕೆ ಕಾಯಲಾಗುತ್ತಿದೆ...</translation>
<translation id="153384715582417236">ಇದುವರೆಗೂ ಇಷ್ಟೇ</translation>
<translation id="1549470594296187301">ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬಳಸಲು JavaScript ಸಕ್ರಿಯಗೊಳಿಸಬೇಕು.</translation>
<translation id="1555130319947370107">ನೀಲಿ</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">ಸಿಸ್ಟಂ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಲು ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="1740951997222943430">ಮಾನ್ಯವಾದ ಅವಧಿ-ಮುಕ್ತಾಯ ತಿಂಗಳನ್ನು ನಮೂದಿಸಿ</translation>
-<translation id="1745358365027406341">ನಂತರ ಪುಟ ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ</translation>
<translation id="17513872634828108">ತೆರೆದ ಟ್ಯಾಬ್‌ಗಳು</translation>
<translation id="1753706481035618306">ಪುಟ ಸಂಖ್ಯೆ</translation>
<translation id="1763864636252898013">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವು ನಿಮ್ಮ ಸಾಧನದ ಆಪರೇಟಿಂಗ್ ಸಿಸ್ಟಂ‌ ಪ್ರಕಾರ ವಿಶ್ವಾಸಾರ್ಹವಾಗಿಲ್ಲ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">ಅಗತ್ಯ ಕ್ಷೇತ್ರ</translation>
<translation id="187918866476621466">ಆರಂಭಿಕ ಪುಟಗಳನ್ನು ತೆರೆಯಿರಿ</translation>
<translation id="1883255238294161206">ಪಟ್ಟಿಯನ್ನು ಸಂಕುಚಿಸಿ</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ಇನ್ನಷ್ಟು}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ಇನ್ನಷ್ಟು}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="1898423065542865115">ಫಿಲ್ಟರಿಂಗ್</translation>
+<translation id="1916770123977586577">ನಿಮ್ಮ ಅಪ್‌ಡೇಟ್‌ ಮಾಡಲಾದ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಈ ಸೈಟ್‌ಗೆ ಅನ್ವಯಿಸಲು, ಈ ಪುಟವನ್ನು ಪುನಃ ಲೋಡ್‌ ಮಾಡಿ</translation>
+<translation id="1919345977826869612">ಜಾಹೀರಾತುಗಳು</translation>
<translation id="192020519938775529">{COUNT,plural, =0{ಯಾವುದೂ ಇಲ್ಲ}=1{1 ಸೈಟ್}one{# ಸೈಟ್‌ಗಳು}other{# ಸೈಟ್‌ಗಳು}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> ಗೆ ಹೋಗಿ</translation>
+<translation id="1948773908305951926">ಸಮ್ಮತಿಸಲಾದ ಪ್ರೀಪೇಯ್ಡ್ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳು</translation>
<translation id="1973335181906896915">ಅನುಕ್ರಮಗೊಳಿಸುವಿಕೆಯ ದೋಷ</translation>
<translation id="1974060860693918893">ಸುಧಾರಿತ</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP ದೋಷ</translation>
<translation id="2270484714375784793">ಫೋನ್ ಸಂಖ್ಯೆ</translation>
<translation id="2282872951544483773">ಲಭ್ಯವಿಲ್ಲದ ಪ್ರಯೋಗಗಳು</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ಐಟಂ}one{<ph name="ITEM_COUNT" /> ಐಟಂಗಳು}other{<ph name="ITEM_COUNT" /> ಐಟಂಗಳು}}</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>
<translation id="2337852623177822836">ನಿಮ್ಮ ನಿರ್ವಾಹಕದಿಂದ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನಿಯಂತ್ರಿಸಲಾಗಿದೆ</translation>
<translation id="2354001756790975382">ಇತರ ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳು</translation>
<translation id="2354430244986887761">Google ಸುರಕ್ಷಿತ ಬ್ರೌಸಿಂಗ್‌ ಇತ್ತೀಚಿಗೆ <ph name="SITE" /> ನಲ್ಲಿ <ph name="BEGIN_LINK" />ಹಾನಿಕಾರಕ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಕಂಡುಹಿಡಿದಿದೆ<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">ಮುಂದುವರಿಸು</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> ನಲ್ಲಿ ಸೆರೆಹಿಡಿಯಲಾದ ಕ್ರ್ಯಾಶ್ ವರದಿಯನ್ನು ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿಲ್ಲ</translation>
<translation id="2367567093518048410">ಹಂತ</translation>
-<translation id="237718015863234333">ಯಾವುದೇ UI ಪರ್ಯಾಯಗಳು ಲಭ್ಯವಿಲ್ಲ</translation>
<translation id="2384307209577226199">ಎಂಟರ್‌ಪ್ರೈಸ್ ಡಿಫಾಲ್ಟ್</translation>
<translation id="2386255080630008482">ಸರ್ವರ್‌ನ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಹಿಂಪಡೆಯಲಾಗಿದೆ.</translation>
<translation id="2392959068659972793">ಯಾವುದೇ ಮೌಲ್ಯ ಹೊಂದಿಸಿಲ್ಲದ ನೀತಿಗಳನ್ನು ತೋರಿಸಿ</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">ಪರಿಶೀಲಿಸಿದರೆ, ವೇಗವಾಗಿ ಫಾರ್ಮ್ ಭರ್ತಿ ಮಾಡಲು Chromium ಈ ಸಾಧನದಲ್ಲಿ ನಿಮ್ಮ ಕಾರ್ಡ್‌ನ ಪ್ರತಿಯನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ.</translation>
<translation id="2498091847651709837">ಹೊಸ ಕಾರ್ಡ್ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ</translation>
<translation id="2501278716633472235">ಹಿಂದಿರುಗಿ</translation>
+<translation id="2503184589641749290">ಸಮ್ಮತಿಸಲಾದ ಡೆಬಿಟ್ ಮತ್ತು ಪ್ರೀಪೇಯ್ಡ್ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="2515629240566999685">ನಿಮ್ಮ ಪ್ರದೇಶದಲ್ಲಿನ ಸಿಗ್ನಲ್ ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ</translation>
-<translation id="2516305470678292029">UI ಪರ್ಯಾಯಗಳು</translation>
<translation id="2539524384386349900">ಪತ್ತೆ ಮಾಡು</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> ಅಮಾನ್ಯ ಪ್ರತಿಕ್ರಿಯೆ ಕಳುಹಿಸಿದೆ.</translation>
<translation id="2556876185419854533">&amp;ಸಂಪಾದಿಸುವುದನ್ನು ರದ್ದುಗೊಳಿಸಿ</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">ಶಿಪ್ಪಿಂಗ್ ವಿಧಾನ</translation>
<translation id="277499241957683684">ಸಾಧನದ ರೆಕಾರ್ಡ್ ಕಾಣೆಯಾಗಿದೆ</translation>
<translation id="2784949926578158345">ಸಂಪರ್ಕವನ್ನು ರೀಸೆಟ್ ಮಾಡಲಾಗಿದೆ.</translation>
+<translation id="2788784517760473862">ಸ್ವೀಕೃತ ಕ್ರೆಡಿಟ್‌ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="2794233252405721443">ಸೈಟ್ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ</translation>
<translation id="2799020568854403057">ಮುಂದಿರುವ ಸೈಟ್‌ನಲ್ಲಿ ಹಾನಿಕಾರಕ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿವೆ</translation>
<translation id="2803306138276472711">Google ಸುರಕ್ಷಿತ ಬ್ರೌಸಿಂಗ್ ಇತ್ತೀಚೆಗೆ <ph name="SITE" /> ನಲ್ಲಿ <ph name="BEGIN_LINK" />ಮಾಲ್‌ವೇರ್ ಪತ್ತೆಹಚ್ಚಿದೆ<ph name="END_LINK" />. ವೆಬ್‌ಸೈಟ್‌ಗಳು ಸಾಮಾನ್ಯವಾಗಿ ಸುರಕ್ಷಿತವಾಗಿದ್ದರೂ, ಕೆಲವೊಮ್ಮೆ ಮಾಲ್‌ವೇರ್‌ಗೆ ತುತ್ತಾಗಿರುತ್ತವೆ.</translation>
<translation id="2824775600643448204">ವಿಳಾಸ ಹಾಗೂ ಹುಡುಕಾಟ ಪಟ್ಟಿ</translation>
<translation id="2826760142808435982"><ph name="CIPHER" /> ಬಳಸಿಕೊಂಡು ಸಂಪರ್ಕವನ್ನು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ ಮತ್ತು ದೃಢೀಕರಿಸಲಾಗಿದೆ ಮತ್ತು <ph name="KX" /> ಅನ್ನು ಕೀ ವಿನಿಮಯ ಯಾಂತ್ರಿಕತೆಯಂತೆ ಬಳಸುತ್ತದೆ.</translation>
<translation id="2835170189407361413">ಫಾರ್ಮ್ ತೆರವುಗೊಳಿಸು</translation>
+<translation id="2851634818064021665">ಈ ಸೈಟ್‌ ಗೆ ಭೇಟಿ ನೀಡಲು ನಿಮ್ಮಗೆ ಅನುಮತಿಯ ಅಗತ್ಯವಿದೆ</translation>
<translation id="2856444702002559011">ದಾಳಿಕೋರರು <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನಿಂದ ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಕದಿಯಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರಬಹುದು (ಉದಾಹರಣೆಗೆ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಸಂದೇಶಗಳು ಅಥವಾ ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್‌ಗಳು). <ph name="BEGIN_LEARN_MORE_LINK" />ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">ಮರುಲೋಡ್ ಮಾಡಬೇಡ</translation>
-<translation id="2900469785430194048">ಈ ವೆಬ್‌ಪುಟ ಪ್ರದರ್ಶಿಸಲು ಪ್ರಯತ್ನಿಸುವಾಗ Google Chrome ಮೆಮೊರಿ ಖಾಲಿಯಾಗಿದೆ.</translation>
<translation id="2909946352844186028">ನೆಟ್‌ವರ್ಕ್ ಬದಲಾವಣೆಯನ್ನು ಪತ್ತೆ ಮಾಡಲಾಗಿದೆ.</translation>
<translation id="2916038427272391327">ಇತರ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಮುಚ್ಚಿ</translation>
<translation id="2922350208395188000">ಸರ್ವರ್‌ನ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಪರಿಶೀಲಿಸಲಾಗುವುದಿಲ್ಲ.</translation>
<translation id="2928905813689894207">ಬಿಲ್ಲಿಂಗ್ ವಿಳಾಸ</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ಇನ್ನಷ್ಟು}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ಇನ್ನಷ್ಟು}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="2941952326391522266">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವು <ph name="DOMAIN2" /> ದಿಂದ ಆಗಿದೆ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
<translation id="2948083400971632585">ಸಂಪರ್ಕಕ್ಕಾಗಿ ಕಾನ್ಫಿಗರ್ ಮಾಡಲಾಗಿರುವ ಯಾವುದೇ ಪ್ರಾಕ್ಸಿಗಳನ್ನು ನೀವು ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಪುಟದಿಂದ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಬಹುದು.</translation>
<translation id="2955913368246107853">ಹುಡುಕಿ ಬಾರ್ ಅನ್ನು ಮುಚ್ಚಿ</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">ಸಕ್ರಿಯಗೊಳಿಸಿದ್ದರೆ, ವೇಗವಾಗಿ ಫಾರ್ಮ್ ಭರ್ತಿ ಮಾಡಲು Chromium ಈ ಸಾಧನದಲ್ಲಿ ನಿಮ್ಮ ಕಾರ್ಡ್‌ನ ಪ್ರತಿಯನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ.</translation>
<translation id="2985398929374701810">ಮಾನ್ಯ ವಿಳಾಸವನ್ನು ನಮೂದಿಸಿ</translation>
<translation id="2986368408720340940">ಈ ಪಿಕಪ್ ವಿಧಾನ ಲಭ್ಯವಿಲ್ಲ. ಬೇರೊಂದು ವಿಧಾನವನ್ನು ಪ್ರಯತ್ನಿಸಿ.</translation>
@@ -277,10 +281,8 @@
<translation id="3167968892399408617">ನಿಮ್ಮ ಎಲ್ಲಾ ಅಪರಿಚಿತ ಟ್ಯಾಬ್‌ಗಳನ್ನು ಮುಚ್ಚಿದ ಬಳಿಕ ನೀವು ಅಪರಿಚಿತ ಮೋಡ್‌ನಲ್ಲಿ ವೀಕ್ಷಿಸಿದ ಪುಟಗಳು ಬ್ರೌಸರ್ ಇತಿಹಾಸದಲ್ಲಿ, ಕುಕೀ ಸಂಗ್ರಹದಲ್ಲಿ ಅಥವಾ ಹುಡುಕಾಟ ಇತಿಹಾಸದಲ್ಲಿ ಉಳಿಯುವುದಿಲ್ಲ. ನೀವು ಡೌನ್‌ಲೋಡ್ ಮಾಡುವ ಯಾವುದೇ ಫೈಲ್‌ಗಳು ಇಲ್ಲವೇ ನೀವು ರಚಿಸುವ ಯಾವುದೇ ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳನ್ನು ಹಾಗೆಯೇ ಇರಿಸಲಾಗುತ್ತದೆ.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">ದ್ವೀಪ</translation>
-<translation id="317583078218509884">ಪುಟವನ್ನು ಮರುಲೋಡ್ ಮಾಡಿದ ನಂತರ ಹೊಸ ಸೈಟ್ ಅನುಮತಿಗಳ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಪರಿಣಾಮಬೀರುತ್ತದೆ.</translation>
<translation id="3176929007561373547">ಪ್ರಾಕ್ಸಿ ಸರ್ವರ್ ಕಾರ್ಯವನಿರ್ವಹಿಸುತ್ತಿದೆಯೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು ನಿಮ್ಮ ಪ್ರಾಕ್ಸಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ. ನೀವು ಪ್ರಾಕ್ಸಿ ಸರ್ವರ್ ಅನ್ನು ಬಳಸುತ್ತಿಲ್ಲ ಎಂಬ ಅನುಮಾನ ನಿಮಗಿದ್ದರೆ:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">ಪುಟವನ್ನು ಅದೃಶ್ಯ ಮೋಡ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ</translation>
<translation id="320323717674993345">ಪಾವತಿಯನ್ನು ರದ್ದುಮಾಡಿ</translation>
<translation id="3207960819495026254">ಬುಕ್‌ಮಾರ್ಕ್‌ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="3225919329040284222">ಆಂತರಿಕ ಮಾನದಂಡಗಳಿಗೆ ಹೊಂದಿಕೆಯಾಗದ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಸರ್ವರ್ ಹಾಜರಿಪಡಿಸಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷತೆಯ ಸಲುವಾಗಿ ಕೆಲವು ಹೆಚ್ಚು ಸುರಕ್ಷಿತ ವೆಬ್ ಸೈಟ್‌ಗಳಲ್ಲಿ ಈ ಮಾನದಂಡಗಳನ್ನು ಸೇರ್ಪಡೆಗೊಳಿಸಲಾಗಿದೆ.</translation>
@@ -293,6 +295,7 @@
<translation id="3270847123878663523">&amp;ಮರುಕ್ರಮಗೊಳಿಸುವುದನ್ನು ರದ್ದುಗೊಳಿಸು</translation>
<translation id="3282497668470633863">ಕಾರ್ಡ್‌ನಲ್ಲಿರುವ ಹೆಸರನ್ನು ಸೇರಿಸಿ</translation>
<translation id="3286538390144397061">ಈಗ ಮರುಪ್ರಾರಂಭಿಸಿ</translation>
+<translation id="3287510313208355388">ಆನ್‌ಲೈನ್‌ನಲ್ಲಿರುವಾಗ ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ</translation>
<translation id="3303855915957856445">ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳು ಕಂಡುಬಂದಿಲ್ಲ</translation>
<translation id="3305707030755673451">ನಿಮ್ಮ ಡೇಟಾವನ್ನು <ph name="TIME" /> ರಂದು ನಿಮ್ಮ ಸಿಂಕ್ ಪಾಸ್‌ಫ್ರೇಸ್‌ನೊಂದಿಗೆ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಸಿಂಕ್ ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ನಮೂದಿಸಿ.</translation>
<translation id="3320021301628644560">ಬಿಲ್ಲಿಂಗ್ ವಿಳಾಸವನ್ನು ಸೇರಿಸಿ</translation>
@@ -325,7 +328,6 @@
<translation id="3452404311384756672">ವಿರಾಮವನ್ನು ಪಡೆಯಿರಿ:</translation>
<translation id="3462200631372590220">ಸುಧಾರಿತ ಆಯ್ಕೆಮಾಡಿ</translation>
<translation id="3467763166455606212">ಕಾರ್ಡ್‌ಹೋಲ್ಡರ್ ಹೆಸರು ಅಗತ್ಯವಿದೆ</translation>
-<translation id="3478058380795961209">ಮುಕ್ತಾಯದ ತಿಂಗಳು</translation>
<translation id="3479539252931486093">ಇದು ಅನಿರೀಕ್ಷಿತವೇ? <ph name="BEGIN_LINK" />ನಮಗೆ ತಿಳಿಸಿ<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">ಈಗ ಬೇಡ</translation>
<translation id="3498215018399854026">ಈ ಕ್ಷಣದಲ್ಲಿ ನಿಮ್ಮ ಪೋಷಕರನ್ನು ತಲುಪಲು ನಮಗೆ ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.</translation>
@@ -339,6 +341,7 @@
<translation id="3566021033012934673">ನಿಮ್ಮ ಸಂಪರ್ಕವು ಖಾಸಗಿಯಲ್ಲ</translation>
<translation id="3569145463236695319">&lt;p&gt;ನಿಮ್ಮ ಸಾಧನದ ದಿನಾಂಕ ಮತ್ತು ಸಮಯ (<ph name="DATE_AND_TIME" />) ತಪ್ಪಾಗಿರುವ ಕಾರಣದಿಂದ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ಗೆ ಖಾಸಗಿ ಸಂಪರ್ಕವನ್ನು ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;ಸಾಮಾನ್ಯ&lt;/strong&gt; ವಿಭಾಗದ &lt;strong&gt;ಸೆಟ್ಟಿಂಗ್‌ಗಳ&lt;/strong&gt; ಅಪ್ಲಿಕೇಶನ್‌ನಿಂದ ದಿನಾಂಕ ಮತ್ತು ಸಮಯವನ್ನು ಹೊಂದಿಸಿ.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">ಡೆಬಿಟ್‌ ಮತ್ತು ಪ್ರೀಪೇಯ್ಡ್‌ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಮ್ಮತಿಸಲಾಗಿದೆ.</translation>
<translation id="3582930987043644930">ಹೆಸರು ಸೇರಿಸಿ</translation>
<translation id="3583757800736429874">&amp;ಸರಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
<translation id="3586931643579894722">ವಿವರಗಳನ್ನು ಮರೆಮಾಡಿ</translation>
@@ -353,10 +356,10 @@
<translation id="3655670868607891010">ಇದು ನಿಮಗೆ ಪದೇ ಪದೇ ಎದುರಾಗುತ್ತಿದ್ದರೆ, <ph name="HELP_LINK" /> ಇವುಗಳನ್ನು ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="3658742229777143148">ಪರಿಷ್ಕರಣೆ</translation>
<translation id="3678029195006412963">ವಿನಂತಿಗೆ ಸಹಿ ಮಾಡಲಾಗುವುದಿಲ್ಲ</translation>
+<translation id="3678529606614285348">ಪುಟವನ್ನು ಹೊಸ ಅದೃಶ್ಯ ವಿಂಡೋದಲ್ಲಿ ತೆರೆಯಿರಿ (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375"><ph name="CRASH_TIME" /> ನಲ್ಲಿ ಕ್ರ್ಯಾಶ್ ವರದಿಯನ್ನು ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ, <ph name="UPLOAD_TIME" /> ಸಮಯಕ್ಕೆ ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="3681007416295224113">ಪ್ರಮಾಣಪತ್ರ ಮಾಹಿತಿ</translation>
<translation id="3690164694835360974">ಲಾಗಿನ್ ಸುರಕ್ಷಿತವಾಗಿಲ್ಲ</translation>
-<translation id="3693415264595406141">ಪಾಸ್‌ವರ್ಡ್:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">ಲೋಡ್ ಆಗುತ್ತಿದೆ...</translation>
<translation id="3712624925041724820">ಪರವಾನಗಿಗಳು ಬರಿದಾಗಿವೆ</translation>
@@ -368,6 +371,7 @@
<translation id="3748148204939282805"><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="375403751935624634">ಸರ್ವರ್ ದೋಷದ ಕಾರಣ ಅನುವಾದವು ವಿಫಲವಾಗಿದೆ.</translation>
<translation id="3759461132968374835">ಇತ್ತೀಚೆಗೆ ನೀವು ಯಾವುದೇ ಕ್ರ‍್ಯಾಶ್‌ಗಳನ್ನು ವರದಿ ಮಾಡಿಲ್ಲ. ಕ್ರ‍್ಯಾಶ್‌‌ ಅನ್ನು ವರದಿಮಾಡುವಿಕೆಯನ್ನು ಉಂಟಾಗಿರುವ ಕ್ರ‍್ಯಾಶ್‌ಗಳು ಇಲ್ಲಿ ಗೋಚರಿಸುವುದಿಲ್ಲ.</translation>
+<translation id="3765032636089507299">ಸುರಕ್ಷಿತ ಬ್ರೌಸಿಂಗ್ ಪುಟವು ನಿರ್ಮಾಣ ಹಂತದಲ್ಲಿದೆ.</translation>
<translation id="3778403066972421603">ಈ ಕಾರ್ಡ್‌ ಅನ್ನು ನಿಮ್ಮ Google ಖಾತೆ ಮತ್ತು ಈ ಸಾಧನದಲ್ಲಿ ಉಳಿಸಲು ಬಯಸುವಿರಾ?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">ಅವಧಿ-ಮುಕ್ತಾಯ <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -380,8 +384,10 @@
<translation id="3886446263141354045">ಈ ಸೈಟ್‌ಗೆ ಪ್ರವೇಶಿಸುವ ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು <ph name="NAME" /> ಅವರಿಗೆ ಕಳುಹಿಸಲಾಗಿದೆ</translation>
<translation id="3890664840433101773">ಇಮೇಲ್ ಸೇರಿಸಿ</translation>
<translation id="3901925938762663762">ಕಾರ್ಡ್ ಅವಧಿಯು ಮುಗಿದಿದೆ</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ಕ್ರ್ಯಾಷ್ ವರದಿ ಐಡಿ <ph name="CRASH_ID" />(ಸ್ಥಳೀಯ ಕ್ರ್ಯಾಷ್ ಐಡಿ: <ph name="CRASH_LOCAL_ID" />) ಅನ್ನು ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="3949571496842715403">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣ ಪತ್ರವು ವಿಷಯವಸ್ತು ಪರ್ಯಾಯ ಹೆಸರುಗಳನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸಿಲ್ಲ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
+<translation id="3949601375789751990">ನಿಮ್ಮ ಬ್ರೌಸಿಂಗ್ ಇತಿಹಾಸ ಇಲ್ಲಿ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತದೆ</translation>
<translation id="3963721102035795474">ರೀಡರ್‌ ಮೋಡ್‌</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{ಯಾವುದೂ ಇಲ್ಲ}=1{1 ಸೈಟ್‌ನಿಂದ }one{# ಸೈಟ್‌ಗಳಿಂದ }other{# ಸೈಟ್‌ಗಳಿಂದ }}</translation>
<translation id="397105322502079400">ಎಣಿಸಲಾಗುತ್ತಿದೆ...</translation>
@@ -410,6 +416,8 @@
<translation id="4165986682804962316">ಸೈಟ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು</translation>
<translation id="4169947484918424451">ಈ ಕಾರ್ಡ್ ಅನ್ನು Chromium ಉಳಿಸಬೇಕೆಂದು ನೀವು ಬಯಸುವಿರಾ?</translation>
<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="4196861286325780578">&amp;ಸರಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ಫೈರ್‌ವಾಲ್ ಮತ್ತು ಆಂಟಿವೈರಸ್ ಕಾನ್ಫಿಗರೇಶನ್‌‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ವಿಫಲತೆಗಳು</translation>
@@ -433,12 +441,12 @@
<translation id="4356973930735388585">ಈ ಸೈಟ್‌ನಲ್ಲಿರುವ ದಾಳಿಕೋರರು ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್‌ನಲ್ಲಿರುವ ಮಾಹಿತಿ (ಉದಾಹರಣೆಗೆ, ಫೋಟೋಗಳು, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಮಾಹಿತಿಗಳು) ಕದಿಯಲು ಇಲ್ಲವೇ ಅಳಿಸಲು ಅಪಾಯಕಾರಿ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರಬಹುದು.</translation>
<translation id="4372948949327679948">ನಿರೀಕ್ಷಿತ <ph name="VALUE_TYPE" /> ಮೌಲ್ಯ.</translation>
<translation id="4377125064752653719">ನೀವು <ph name="DOMAIN" /> ಅನ್ನು ತಲುಪಲು ಪ್ರಯತ್ನಿಸಿದಿರಿ, ಆದರೆ ಸರ್ವರ್ ನೀಡಿದ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಅದರ ನೀಡುವವರು ಹಿಂತೆಗೆದುಕೊಂಡಿದ್ದಾರೆ. ಇದರರ್ಥ ಸರ್ವರ್ ನೀಡಿದ ಸುರಕ್ಷತೆ ರುಜುವಾತುಗಳನ್ನು ಖಂಡಿತವಾಗಿ ನಂಬಲಾಗುವುದಿಲ್ಲ. ನೀವು ಆಕ್ರಮಣಕಾರರೊಂದಿಗೆ ಸಂವಹಿಸುತ್ತಿರಬಹುದು.</translation>
-<translation id="4381091992796011497">ಬಳಕೆದಾರ ಹೆಸರು :</translation>
<translation id="4394049700291259645">ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ</translation>
<translation id="4406896451731180161">ಹುಡುಕಾಟದ ಫಲಿತಾಂಶಗಳು</translation>
<translation id="4424024547088906515">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವು Chrome ಪಾಲಿಗೆ ವಿಶ್ವಾಸಾರ್ಹವಾಗಿಲ್ಲ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> ನಿಮ್ಮ ಲಾಗಿನ್ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಸ್ವೀಕರಿಸಲಿಲ್ಲ ಅಥವಾ ಅದನ್ನು ಒದಗಿಸದೆ ಇರಬಹುದು.</translation>
<translation id="443673843213245140">ಪ್ರಾಕ್ಸಿಯ ಬಳಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಆದರೆ ಬಹಿರಂಗ ಪ್ರಾಕ್ಸಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸಲಾಗಿದೆ.</translation>
+<translation id="445100540951337728">ಸಮ್ಮತಿಸಲಾದ ಡೆಬಿಟ್ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="4506176782989081258">ಮೌಲ್ಯೀಕರಿಸುವಿಕೆಯ ದೋಷ: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">ಸಿಸ್ಟಂ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ</translation>
<translation id="450710068430902550">ನಿರ್ವಾಹಕರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುವುದು</translation>
@@ -450,12 +458,14 @@
<translation id="4587425331216688090">Chrome ನಿಂದ ವಿಳಾಸವನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?</translation>
<translation id="4592951414987517459">ಆಧುನಿಕ ಸೈಫರ್ ಸೂಟ್ ಬಳಸುವ ಮೂಲಕ <ph name="DOMAIN" /> ಗೆ ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ.</translation>
<translation id="4594403342090139922">&amp;ಅಳಿಸುವುದನ್ನು ರದ್ದುಗೊಳಿಸಿ</translation>
+<translation id="4611292653554630842">ಲಾಗ್ ಇನ್</translation>
<translation id="4619615317237390068">ಇತರ ಸಾಧನಗಳಿಂದ ಟ್ಯಾಬ್‌ಗಳು</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರದಲ್ಲಿ ಸಾಕಷ್ಟು ದೋಷಗಳಿವೆ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
<translation id="4690462567478992370">ಅಮಾನ್ಯ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಬಳಸಿಕೊಂಡು ನಿಲ್ಲಿಸಿ</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">ನಿಮ್ಮ ಸಂಪರ್ಕಕ್ಕೆ ಅಡ್ಡಿಯಾಗಿದೆ</translation>
+<translation id="471880041731876836">ಈ ಸೈಟ್‌ ಗೆ ಭೇಟಿ ನೀಡಲು ನೀವು ಅನುಮತಿ ಹೊಂದಿಲ್ಲ</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows ನೆಟ್‌ವರ್ಕ್ ಡಯಾಗ್ನಾಸ್ಟಿಕ್ಸ್ ರನ್ ಮಾಡಲಾಗುತ್ತಿದೆ<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">ನೀತಿಗಳನ್ನು ಮರುಲೋಡ್ ಮಾಡಿ</translation>
<translation id="4728558894243024398">ಪ್ಲಾಟ್‌ಫಾರ್ಮ್</translation>
@@ -477,14 +487,15 @@
<translation id="4850886885716139402">ವೀಕ್ಷಣೆ</translation>
<translation id="4854362297993841467">ಈ ವಿತರಣೆಯ ವಿಧಾನ ಲಭ್ಯವಿಲ್ಲ. ಬೇರೊಂದು ವಿಧಾನವನ್ನು ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="4858792381671956233">ಈ ಸೈಟ್ ಅನ್ನು ಭೇಟಿ ಮಾಡಬಹುದು ಎಂದು ನಿಮ್ಮ ಪೋಷಕರಿಗೆ ನೀವು ಕೇಳಿರುವಿರಿ.</translation>
-<translation id="4863764087567530506">ಈ ಕಂಟೆಂಟ್‌ ಸಾಫ್ಟ್‌ವೇರ್ ಸ್ಥಾಪಿಸುವಿಕೆ ಅಥವಾ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಬಹಿರಂಗ ಪಡಿಸಿ ಮೋಸಗೊಳಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರಬಹುದು. <ph name="BEGIN_LINK" />ಹೇಗಿದ್ದರೂ ತೋರಿಸಿ<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">ಹುಡುಕಾಟ ಇತಿಹಾಸ</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">ಪ್ರಮಾಣೀಕರಣದ ಅವಶ್ಯಕತೆಯಿದೆ</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ಮತ್ತು 1 ಹೆಚ್ಚಿನ ವೆಬ್ ಪುಟ}one{ಮತ್ತು # ಹೆಚ್ಚಿನ ವೆಬ್ ಪುಟಗಳು}other{ಮತ್ತು # ಹೆಚ್ಚಿನ ವೆಬ್ ಪುಟಗಳು}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">ಗೊತ್ತಿಲ್ಲದ ಭಾಷೆಯಿಂದ <ph name="LANGUAGE_LANGUAGE" /> ಗೆ ಈ ಪುಟವನ್ನು ಭಾಷಾಂತರಿಸಲಾಗಿದೆ</translation>
<translation id="4923459931733593730">ಪಾವತಿ</translation>
<translation id="4926049483395192435">ನಿರ್ದಿಷ್ಟಪಡಿಸಬೇಕಾಗಿದೆ.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">ಕ್ರಿಯೆಗಳು</translation>
<translation id="4958444002117714549">ಪಟ್ಟಿಯನ್ನು ವಿಸ್ತರಿಸಿ</translation>
<translation id="4974590756084640048">ಎಚ್ಚರಿಕೆಗಳನ್ನು ಮರುಸಕ್ರಿಯಗೊಳಿಸಿ</translation>
@@ -500,6 +511,7 @@
<translation id="5045550434625856497">ತಪ್ಪು ಪಾಸ್‌ವರ್ಡ್</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>
<translation id="5087580092889165836">ಕಾರ್ಡ್ ಸೇರಿಸಿ</translation>
<translation id="5089810972385038852">ರಾಜ್ಯ</translation>
@@ -507,18 +519,26 @@
<translation id="5095208057601539847">ಪ್ರಾಂತ್ಯ</translation>
<translation id="5115563688576182185">(64-ಬಿಟ್)</translation>
<translation id="5141240743006678641">ನಿಮ್ಮ Google ರುಜುವಾತುಗಳ ಜೊತೆಗೆ ಸಿಂಕ್ ಮಾಡಿದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ಎನ್‌ಕ್ರಿಫ್ಟ್ ಮಾಡಿ</translation>
-<translation id="514421653919133810">ಪುಟವನ್ನು ಅದೃಶ್ಯ ಮೋಡ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">ನೀತಿ ಪ್ರತಿಕ್ರಿಯೆಯಲ್ಲಿ ದೋಷದ ಕೋಡ್ ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ</translation>
+<translation id="5159010409087891077">ಪುಟವನ್ನು ಹೊಸ ಅದೃಶ್ಯ ವಿಂಡೋದಲ್ಲಿ ತೆರೆಯಿರಿ (⇧⌘N)</translation>
<translation id="5171045022955879922">ಹುಡುಕಾಟ ನಡೆಸಿ ಅಥವಾ URL ಅನ್ನು ಟೈಪ್‌ ಮಾಡಿ</translation>
<translation id="5172758083709347301">ಯಂತ್ರ</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> ರಲ್ಲಿ ಇಲ್ಲವೆ? ಈ ದೋಷವನ್ನು ವರದಿ ಮಾಡಿ</translation>
-<translation id="5181140330217080051">ಡೌನ್‌ಲೋಡ್ ಆಗುತ್ತಿದೆ</translation>
<translation id="5190835502935405962">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಬಾರ್</translation>
<translation id="5199729219167945352">ಪ್ರಯೋಗಗಳು</translation>
<translation id="5205222826937269299">ಹೆಸರು ಅವಶ್ಯವಾಗಿದೆ</translation>
<translation id="5222812217790122047">ಇಮೇಲ್ ಅಗತ್ಯವಿದೆ</translation>
<translation id="5251803541071282808">ಮೇಘ</translation>
<translation id="5277279256032773186">ಕೆಲಸದಲ್ಲಿ Chrome ಬಳಸುತ್ತಿರುವಿರಾ? ತಮ್ಮ ಉದ್ಯೋಗಿಗಳಿಗಾಗಿ ವ್ಯವಹಾರಗಳಲ್ಲಿ Chrome ಸೆಟ್ಟಿಂಗ್‌ಗಳು ನಿರ್ವಹಿಸಬಹುದು. ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />ಈ ಹಂತಗಳನ್ನು ಅನುಸರಿಸಿ ತಾತ್ಕಾಲಿಕವಾಗಿ ಸಾಫ್ಟ್‌ವೇರ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದರೆ ನೀವು ವೆಬ್‌ ಅನ್ನು ಪಡೆದುಕೊಳ್ಳಬಹುದಾಗಿದೆ. ನಿಮಗೆ ನಿರ್ವಾಹಕರ ಸವಲತ್ತುಗಳ ಅಗತ್ಯವಿದೆ.<ph name="END_PARAGRAPH" />
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />ಕ್ಲಿಕ್ ಮಾಡಿ <ph name="BEGIN_BOLD" />ಪ್ರಾರಂಭಿಸಿ<ph name="END_BOLD" />, ನಂತರ ಹುಡುಕಿ ಮತ್ತು <ph name="BEGIN_BOLD" />"ಸ್ಥಳೀಯ ಸೇವೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ"<ph name="END_BOLD" /> ಆಯ್ಕೆಮಾಡಿ
+ <ph name="LIST_ITEM" />ಆಯ್ಕೆಮಾಡಿ <ph name="BEGIN_BOLD" />ದೃಶ್ಯಾವಳಿಅನ್ವೇಷಣೆ<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />ಕೆಳಗಿನ <ph name="BEGIN_BOLD" />ಸ್ಟಾರ್ಟ್ಅಪ್ ಪ್ರಕಾರ<ph name="END_BOLD" />, ಆಯ್ಕೆಮಾಡಿ <ph name="BEGIN_BOLD" />ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />ಕೆಳಗಿನ <ph name="BEGIN_BOLD" />ಸೇವೆ ಸ್ಥಿತಿಗಳು<ph name="END_BOLD" />, ಕ್ಲಿಕ್ ಮಾಡಿ <ph name="BEGIN_BOLD" />ನಿಲ್ಲಿಸಿ<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />ಕ್ಲಿಕ್ ಮಾಡಿ<ph name="BEGIN_BOLD" />ಅನ್ವಯಿಸು<ph name="END_BOLD" />, ನಂತರ <ph name="BEGIN_BOLD" />ಹೌದು<ph name="END_BOLD" /> ಎಂದು ಕ್ಲಿಕ್‌ ಮಾಡು
+ <ph name="LIST_ITEM" />ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್ ಮೂಲಕ ಶಾಶ್ವತವಾಗಿ ಸಾಫ್ಟ್‌ವೇರ್ ತೆಗೆಯುವುದು ಹೇಗೆ ಎಂದು ತಿಳಿಯಲು <ph name="BEGIN_LEARN_MORE_LINK" />Chrome ಸಹಾಯ ಕೇಂದ್ರಕ್ಕೆ<ph name="END_LEARN_MORE_LINK" /> ಭೇಟಿ ನೀಡಿ
+<ph name="END_LIST" /></translation>
<translation id="5297526204711817721">ಈ ಸೈಟ್‌ಗೆ ನಿಮ್ಮ ಸಂಪರ್ಕವು ಖಾಸಗಿಯಾಗಿಲ್ಲ. ಯಾವ ಸಮಯದಲ್ಲಾದರೂ VR ಮೋಡ್‌‌ನಿಂದ ನಿರ್ಗಮಿಸಲು, ಹೆಡ್‌ಸೆಟ್ ತೆಗೆದುಹಾಕಿ ಮತ್ತು ಹಿಂದೆ ಒತ್ತಿರಿ.</translation>
<translation id="5299298092464848405">ನೀತಿಯ ಪಾರ್ಸಿಂಗ್‌ನಲ್ಲಿ ದೋಷ</translation>
<translation id="5308689395849655368">ಕ್ರ‍್ಯಾಶ್‌‌ ವರದಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ.</translation>
@@ -535,9 +555,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" ನಲ್ಲಿ ಸ್ಕೀಮಾ ಮೌಲ್ವೀಕರಣ ದೋಷ: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">ಈ <ph name="HOST_NAME" /> ಪುಟ ಕಂಡುಬರುವುದಿಲ್ಲ</translation>
<translation id="5455374756549232013">ತಪ್ಪಾದ ನೀತಿಯ ಸಮಯಸ್ಟ್ಯಾಂಪ್</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" /> ರಲ್ಲಿ <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">ಅಮಾನ್ಯ</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ಇನ್ನಷ್ಟು}one{<ph name="CONTACT_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ಇನ್ನಷ್ಟು}other{<ph name="CONTACT_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="5470861586879999274">&amp;ಸಂಪಾದಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
+<translation id="5481076368049295676">ಈ ಕಂಟೆಂಟ್‌ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿರುವ ಮಾಹಿತಿಯನ್ನು ಕದಿಯುವ ಅಥವಾ ಅಳಿಸುವ ಅಪಾಯಕಾರಿ ಸಾಫ್ಟ್‌ವೇರ್ ಅನ್ನು ಸ್ಥಾಪಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರಬಹುದು. <ph name="BEGIN_LINK" />ಹೇಗಿದ್ದರೂ ತೋರಿಸಿ<ph name="END_LINK" />.</translation>
<translation id="54817484435770891">ಮಾನ್ಯವಾದ ವಿಳಾಸ ಸೇರಿಸಿ</translation>
<translation id="5492298309214877701">ಬಾಹ್ಯ ವೆಬ್‌ಸೈಟ್‌‌ನ URL ಅನ್ನೇ ಕಂಪನಿ, ಸಂಸ್ಥೆ ಅಥವಾ ಶಾಲೆಯ ಇಂಟ್ರಾನೆಟ್‌ನಲ್ಲಿನ ಈ ಸೈಟ್ ಹೊಂದಿದೆ.
<ph name="LINE_BREAK" />
@@ -549,6 +570,7 @@
<translation id="5540224163453853">ವಿನಂತಿಸಿದ ಲೇಖನವನ್ನು ಹುಡುಕಲು ಸಾಧವಾಗಲಿಲ್ಲ.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> ನಲ್ಲಿ ಎಂಬೆಡ್ ಮಾಡಲಾದ ಪುಟವು ಹೀಗೆ ಹೇಳುತ್ತದೆ:</translation>
<translation id="5556459405103347317">ಮರುಲೋಡ್‌</translation>
+<translation id="5560088892362098740">ಅವಧಿ ಮುಗಿಯುವ ದಿನಾಂಕ</translation>
<translation id="5565735124758917034">ಸಕ್ರಿಯ</translation>
<translation id="5571083550517324815">ಈ ವಿಳಾಸದಿಂದ ಪಿಕಪ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಬೇರೊಂದು ವಿಳಾಸವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ.</translation>
<translation id="5572851009514199876">ದಯವಿಟ್ಟು Chrome ಪ್ರಾರಂಭಿಸಿ ಮತ್ತು ಸೈನ್ ಇನ್ ಮಾಡಿ ಈ ಮೂಲಕ ಈ ಸೈಟ್‌ಗೆ ಪ್ರವೇಶಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು Chrome ಪರಿಶೀಲಿಸಬಹುದು.</translation>
@@ -563,12 +585,13 @@
<translation id="5629630648637658800">ನೀತಿಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲವಾಗಿದೆ</translation>
<translation id="5631439013527180824">ಅಮಾನ್ಯವಾದ ಸಾಧನ ನಿರ್ವಹಣೆ ಟೋಕನ್</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="5646376287012673985">ಸ್ಥಳ</translation>
<translation id="5659593005791499971">ಇಮೇಲ್</translation>
<translation id="5669703222995421982">ವೈಯಕ್ತೀಕರಿಸಲಾದ ವಿಷಯವನ್ನು ಪಡೆಯಿರಿ</translation>
<translation id="5675650730144413517">ಈ ಪುಟ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿಲ್ಲ</translation>
<translation id="5710435578057952990">ಈ ವೆಬ್‌ಸೈಟ್‌ನ ಗುರುತಿಸುವಿಕೆಯನ್ನು ಇನ್ನೂ ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ.</translation>
-<translation id="5713016350996637505">ವಂಚನೀಯ ವಿಷಯವನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ</translation>
+<translation id="5719499550583120431">ಪ್ರೀಪೇಯ್ಡ್ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಮ್ಮತಿಸಲಾಗಿದೆ.</translation>
<translation id="5720705177508910913">ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ</translation>
<translation id="5732392974455271431">ನಿಮ್ಮ ಪೋಷಕರು ನಿಮಗಾಗಿ ಅದನ್ನು ಅನಿರ್ಬಂಧಿಸಬಹುದಾಗಿದೆ</translation>
<translation id="5763042198335101085">ಮಾನ್ಯವಾದ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ನಮೂದಿಸಿ</translation>
@@ -580,15 +603,14 @@
<translation id="5803412860119678065">ನಿಮ್ಮ <ph name="CARD_DETAIL" /> ಭರ್ತಿ ಮಾಡಲು ನೀವು ಬಯಸುವಿರಾ?</translation>
<translation id="5810442152076338065">ಬಳಕೆಯಲ್ಲಿಲ್ಲದ ಸೈಫರ್ ಸೂಟ್ ಬಳಸುವ ಮೂಲಕ <ph name="DOMAIN" /> ಗೆ ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ.</translation>
<translation id="5813119285467412249">&amp;ಸೇರಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
-<translation id="5814352347845180253"><ph name="SITE" /> ಮತ್ತು ಕೆಲವು ಇತರ ಸೈಟ್‌ಗಳಿಂದ ಪ್ರೀಮಿಯಂ ವಿಷಯಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ನೀವು ಕಳೆದುಕೊಳ್ಳಬಹುದು.</translation>
<translation id="5838278095973806738">ಈ ಸೈಟ್‌ನಲ್ಲಿ ನೀವು ಯಾವುದೇ ಸೂಕ್ಷ್ಮ ಮಾಹಿತಿಯನ್ನು (ಉದಾಹರಣೆಗೆ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಅಥವಾ ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್‌ಗಳು) ನಮೂದಿಸಬಾರದು, ಏಕೆಂದರೆ ಅದು ದಾಳಿಕೋರರ ಮೂಲಕ ಕಳುವಾಗಬಹುದು.</translation>
<translation id="5869405914158311789">ಈ ಸೈಟ್ ತಲುಪಲಾಗುವುದಿಲ್ಲ</translation>
<translation id="5869522115854928033">ಉಳಿಸಲಾದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು</translation>
<translation id="5872918882028971132">ಪೋಷಕ ಸಲಹೆಗಳು</translation>
+<translation id="5893752035575986141">ಕ್ರೆಡಿಟ್‌ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಮ್ಮತಿಸಲಾಗಿದೆ.</translation>
<translation id="5901630391730855834">ಹಳದಿ</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ಸಿಂಕ್‌ ಮಾಡಲಾಗಿದೆ)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 ಬಳಕೆಯಲ್ಲಿದೆ}one{# ಬಳಕೆಯಲ್ಲಿದೆ}other{# ಬಳಕೆಯಲ್ಲಿದೆ}}</translation>
-<translation id="5926846154125914413">ಕೆಲವು ಸೈಟ್‌ಗಳ ಪ್ರೀಮಿಯಂ ವಿಷಯಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ನೀವು ಕಳೆದುಕೊಳ್ಳಬಹುದು.</translation>
<translation id="5959728338436674663">ಅಪಾಯಕಾರಿ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಸೈಟ್‌ಗಳ ಪತ್ತೆಗೆ ಸಹಾಯ ಮಾಡಲು Google ಗೆ ಕೆಲವು <ph name="BEGIN_WHITEPAPER_LINK" />ಸಿಸ್ಟಂ ಮಾಹಿತಿ ಮತ್ತು ಪುಟ ವಿಷಯ<ph name="END_WHITEPAPER_LINK" />ವನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಳುಹಿಸಿ. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">ಇತಿಹಾಸದಿಂದ ತೆಗೆದುಹಾಕಿ</translation>
<translation id="5975083100439434680">ಝೂಮ್ ಔಟ್</translation>
@@ -604,6 +626,7 @@
<translation id="6040143037577758943">ಮುಚ್ಚಿರಿ</translation>
<translation id="6042308850641462728">ಇನ್ನಷ್ಟು</translation>
<translation id="6047233362582046994">ನಿಮ್ಮ ಸುರಕ್ಷತೆ ಅಪಾಯಗಳು ನಿಮಗೆ ಅರ್ಥವಾಗಿದ್ದರೆ, ಅಪಾಯಕಾರಿ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕುವುದಕ್ಕೂ ಮೊದಲು ನೀವು <ph name="BEGIN_LINK" />ಈ ಸೈಟ್‌ಗೆ ಭೇಟಿ<ph name="END_LINK" /> ನೀಡಬಹುದು.</translation>
+<translation id="6047927260846328439">ಈ ಕಂಟೆಂಟ್‌ ಸಾಫ್ಟ್‌ವೇರ್ ಸ್ಥಾಪಿಸುವಂತೆ ಅಥವಾ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಬಹಿರಂಗಪಡಿಸುವಂತೆ ನಿಮ್ಮನ್ನು ಮೋಸಗೊಳಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರಬಹುದು. <ph name="BEGIN_LINK" />ಹೇಗಿದ್ದರೂ ತೋರಿಸಿ<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">ನೀವು ಸದ್ಯಕ್ಕೆ <ph name="SITE" /> ಗೆ ಭೇಟಿ ನೀಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಏಕೆಂದರೆ, ವೆಬ್‌ಸೈಟ್ ಪ್ರಮಾಣಪತ್ರ ಪಿನ್ ಮಾಡುವಿಕೆಯನ್ನು ಬಳಸುತ್ತದೆ. ನೆಟ್‌ವರ್ಕ್ ದೋಷಗಳು ಮತ್ತು ಆಕ್ರಮಣಗಳು ತಾತ್ಕಾಲಿಕವಾಗಿರುತ್ತವೆ, ಹೀಗಾಗಿ ಈ ಪುಟವು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಕಾರ್ಯ ನಿರ್ವಹಿಸಬಹುದು.</translation>
<translation id="6060685159320643512">ಜಾಗ್ರತೆ, ಈ ಪ್ರಯೋಗಗಳು ವಿಫಲವಾಗಬಹುದು</translation>
<translation id="6080696365213338172">ನಿರ್ವಾಹಕರು-ಒದಗಿಸಿದ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಬಳಸಿಕೊಂಡು ನೀವು ವಿಷಯವನ್ನು ಪ್ರವೇಶಿಸಿರುವಿರಿ. <ph name="DOMAIN" /> ಗೆ ನೀವು ಒದಗಿಸುವ ಡೇಟಾವನ್ನು ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ತಡೆಹಿಡಿಯಬಹುದಾಗಿದೆ.</translation>
@@ -616,7 +639,6 @@
<translation id="6165508094623778733">ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ</translation>
<translation id="6169916984152623906">ನೀವೀಗ ಖಾಸಗಿಯಾಗಿ ಬ್ರೌಸ್ ಮಾಡಬಹುದು. ಈ ಸಾಧನವನ್ನು ಬಳಸುವ ಬೇರೆ ಯಾರಿಗೂ ನಿಮ್ಮ ಚಟುವಟಿಕೆ ಕಾಣಿಸುವುದಿಲ್ಲ. ಆದರೂ, ಡೌನ್‌ಲೋಡ್‌ಗಳು ಮತ್ತು ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳು ಉಳಿದಿರುತ್ತವೆ.</translation>
<translation id="6177128806592000436">ಈ ಸೈಟ್‌ಗೆ ನಿಮ್ಮ ಸಂಪರ್ಕವು ಸುರಕ್ಷಿತವಾಗಿಲ್ಲ</translation>
-<translation id="6184817833369986695">(ಸಂಘ: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">ನಿಮ್ಮ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ</translation>
<translation id="6218753634732582820">Chromium ನಿಂದ ವಿಳಾಸವನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?</translation>
<translation id="6221345481584921695">Google ಸುರಕ್ಷಿತ ಬ್ರೌಸಿಂಗ್ ಇತ್ತೀಚೆಗೆ <ph name="SITE" /> ನಲ್ಲಿ <ph name="BEGIN_LINK" />ಮಾಲ್‌ವೇರ್ ಪತ್ತೆಹಚ್ಚಿದೆ<ph name="END_LINK" />. ಸಾಮಾನ್ಯವಾಗಿ ಸುರಕ್ಷಿತವಾಗಿರುವ ವೆಬ್‌ಸೈಟ್‌ಗಳು ಕೆಲವೊಮ್ಮೆ ಮಾಲ್‌ವೇರ್ ಸೋಂಕಿಗೆ ತುತ್ತಾಗಿರುತ್ತವೆ. ದುರುದ್ದೇಶಪೂರಿತ ಸಂಗತಿಗಳು ಮಾಲ್‌ವೇರ್ ವಿತರಕರಾದ <ph name="SUBRESOURCE_HOST" /> ರಿಂದ ಬರುತ್ತವೆ.</translation>
@@ -635,13 +657,14 @@
<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>
<translation id="6387478394221739770">ಉತ್ತಮವಾದ ಹೊಸ Chrome ವೈಶಿಷ್ಟ್ಯಗಳಲ್ಲಿ ಆಸಕ್ತಿ ಇದೆಯೇ? chrome.com/beta ನಲ್ಲಿ ನಮ್ಮ ಬೀಟಾ ಚಾನಲ್ ಪ್ರಯತ್ನಿಸಿ.</translation>
-<translation id="6389758589412724634">ಈ ವೆಬ್‌ಪುಟ ಪ್ರದರ್ಶಿಸಲು ಪ್ರಯತ್ನಿಸುವಾಗ Chromium ಮೆಮೊರಿ ಖಾಲಿಯಾಗಿದೆ.</translation>
+<translation id="6397451950548600259">ಸುರಕ್ಷಿತವಾಗಿ ವೆಬ್‌ಗೆ ಸಂಪರ್ಕಿಸುವ Chromeನ ಕಾರ್ಯವನ್ನು ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್‌ನಲ್ಲಿರುವ ಸಾಫ್ಟ್‌ವೇರ್‌ ಸ್ಥಗಿತಗೊಳಿಸಿದೆ</translation>
<translation id="6404511346730675251">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> ಗೆ ಮುಕ್ತಾಯ ದಿನಾಂಕ ಮತ್ತು ಸಿವಿಸಿ ಅನ್ನು ನಮೂದಿಸಿ</translation>
<translation id="6414888972213066896">ಈ ಸೈಟ್‌ಗೆ ಭೇಟಿ ನೀಡುವುದು ಸರಿಯೇ ಎಂದು ನೀವು ನಿಮ್ಮ ಪೋಷಕರನ್ನು ಕೇಳಿರುವಿರಿ</translation>
@@ -656,6 +679,7 @@
<translation id="647261751007945333">ಸಾಧನ ನೀತಿಗಳು</translation>
<translation id="6477321094435799029">ಈ ಪುಟದಲ್ಲಿ ಅಸಹಜ ಕೋಡ್ ಅನ್ನು Chrome ಪತ್ತೆಹಚ್ಚಿದೆ ಮತ್ತು ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು (ಉದಾಹರಣೆಗೆ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಫೋನ್‌ ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್‌ಗಳು) ರಕ್ಷಿಸಲು ಅದನ್ನು ನಿರ್ಬಂಧಿಸಿದೆ.</translation>
<translation id="6489534406876378309">ವಿಫಲತೆಗಳನ್ನು ಅಪ್‌ಲೋಡ್‌ ಮಾಡುವುದನ್ನು ಪ್ರಾರಂಭಿಸು</translation>
+<translation id="6507833130742554667">ಕ್ರೆಡಿಟ್‌ ಮತ್ತು ಡೆಬಿಟ್ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲಾಗುತ್ತದೆ.</translation>
<translation id="6508722015517270189">Chrome ಮರುಪ್ರಾರಂಭಿಸಿ</translation>
<translation id="6529602333819889595">&amp;ಅಳಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
<translation id="6534179046333460208">ಭೌತಿಕ ವೆಬ್ ಸಲಹೆಗಳು</translation>
@@ -664,13 +688,13 @@
<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="657639383826808334">ಈ ಕಂಟೆಂಟ್‌ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿರುವ ಮಾಹಿತಿಯನ್ನು ಕದಿಯಲು ಇಲ್ಲವೇ ಅಳಿಸಲು ಅಪಾಯಕಾರಿ ಸಾಫ್ಟ್‌ವೇರ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರಬಹುದು. <ph name="BEGIN_LINK" />ಹೇಗಿದ್ದರೂ ತೋರಿಸಿ<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">ಎನ್‌ಕ್ರಿಫ್ಶನ್ ಆಯ್ಕೆಗಳು</translation>
<translation id="662080504995468778">ಉಳಿಯಿರಿ</translation>
<translation id="6626291197371920147">ಮಾನ್ಯವಾದ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಿ</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> ಹುಡುಕಾಟ</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನ ದಾಳಿಕೋರರು ನಿಮ್ಮ Macನಲ್ಲಿ ಮಾಹಿತಿಯನ್ನು (ಉದಾಹರಣೆಗೆ, ಫೋಟೋಗಳು, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಮಾಹಿತಿಗಳು) ಕದಿಯಲು ಇಲ್ಲವೇ ಅಳಿಸಲು ಅಪಾಯಕಾರಿ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರಬಹುದು. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">ಈ ನೀತಿಯನ್ನು ವಿನಂತಿಸಲಾಗಿದೆ.</translation>
+<translation id="6657585470893396449">ಪಾಸ್‌ವರ್ಡ್</translation>
<translation id="6671697161687535275">Chromium ನಿಂದ ಫಾರ್ಮ್ ಸಲಹೆಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?</translation>
<translation id="6685834062052613830">ಸೈನ್‌ ಔಟ್‌ ಮಾಡಿ ಹಾಗೂ ಸೆಟಪ್ ಪೂರ್ಣಗೊಳಿಸಿ</translation>
<translation id="6710213216561001401">ಹಿಂದೆ</translation>
@@ -701,7 +725,6 @@
<translation id="6970216967273061347">ಜಿಲ್ಲೆ</translation>
<translation id="6973656660372572881">ಹೊಂದಿಸಿದ ಪ್ರಾಕ್ಸಿ ಸರ್ವರ್‌ಗಳು ಮತ್ತು .pac ಸ್ಕ್ರಿಪ್ಟ್ URL ಎರಡನ್ನೂ ನಿರ್ದಿಷ್ಟಪಡಿಸಲಾಗಿದೆ.</translation>
<translation id="6989763994942163495">ಸುಧಾರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೋರಿಸು...</translation>
-<translation id="7000990526846637657">ಯಾವುದೇ ಇತಿಹಾಸ ದಾಖಲೆಗಳು ಕಂಡುಬಂದಿಲ್ಲ</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">ನಿಮ್ಮ Google ಖಾತೆಯು <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> ನಲ್ಲಿ ಬ್ರೌಸಿಂಗ್ ಇತಿಹಾಸದ ಇತರ ಪ್ರಕಾರಗಳನ್ನು ಹೊಂದಿರಬಹುದು</translation>
<translation id="7029809446516969842">ಪಾಸ್‌ವರ್ಡ್‌ಗಳು</translation>
@@ -716,9 +739,11 @@
<translation id="7129409597930077180">ಈ ವಿಳಾಸಕ್ಕೆ ರವಾನಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಬೇರೊಂದು ವಿಳಾಸವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ.</translation>
<translation id="7138472120740807366">ವಿತರಣೆ ವಿಧಾನ</translation>
<translation id="7139724024395191329">ಎಮಿರೇಟ್</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ಇನ್ನಷ್ಟು}one{<ph name="PAYMENT_METHOD_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ಇನ್ನಷ್ಟು}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="7155487117670177674">ಪಾವತಿ ಸುರಕ್ಷಿತವಾಗಿಲ್ಲ</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ಇನ್ನಷ್ಟು}one{<ph name="SHIPPING_OPTION_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ಇನ್ನಷ್ಟು}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="7179921470347911571">ಇದೀಗ ಮರುಪ್ರಾರಂಭಿಸು</translation>
-<translation id="7180611975245234373">ರೀಫ್ರೆಶ್ ಮಾಡಿ</translation>
+<translation id="7180611975245234373">ರಿಫ್ರೆಶ್ ಮಾಡಿ</translation>
<translation id="7182878459783632708">ಯಾವುದೇ ನೀತಿಗಳನ್ನು ಹೊಂದಿಸಿಲ್ಲ</translation>
<translation id="7186367841673660872">ಈ ಪುಟವನ್ನು<ph name="ORIGINAL_LANGUAGE" />ನಿಂದ<ph name="LANGUAGE_LANGUAGE" />ಗೆ ಭಾಷಾಂತರಿಸಲಾಗಿದೆ</translation>
<translation id="7192203810768312527"><ph name="SIZE" /> ತೆಗೆದುಹಾಕಿ. ನಿಮ್ಮ ನಂತರದ ಭೇಟಿಯ ಸಮಯದಲ್ಲಿ ಕೆಲವು ಸೈಟ್‌ಗಳು ನಿಧಾನವಾಗಿ ಲೋಡ್ ಆಗಬಹುದು.</translation>
@@ -757,6 +782,7 @@
<translation id="7514365320538308">ಡೌನ್‌ಲೋಡ್</translation>
<translation id="7518003948725431193">ಈ ವೆಬ್ ವಿಳಾಸಕ್ಕಾಗಿ ಯಾವುದೇ ವೆಬ್ ಪುಟವು ಕಂಡುಬರಲಿಲ್ಲ: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">ಈ ಸೈಟ್‌ಗೆ ನಿಮ್ಮ ಸಂಪರ್ಕವು ಖಾಸಗಿಯಾಗಿಲ್ಲ.</translation>
<translation id="7535087603100972091">ಮೌಲ್ಯ</translation>
<translation id="7537536606612762813">ಕಡ್ಡಾಯ</translation>
<translation id="7542403920425041731">ನೀವು ಒಮ್ಮೆ ಖಚಿತಪಡಿಸಿದರೆ, ನಿಮ್ಮ ಕಾರ್ಡ್ ವಿವರಗಳನ್ನು ಈ ಸೈಟ್ ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.</translation>
@@ -766,7 +792,6 @@
<translation id="7552846755917812628">ಕೆಳಗಿನ ಸಲಹೆಗಳನ್ನು ಪ್ರಯತ್ನಿಸಿ:</translation>
<translation id="7554791636758816595">ಹೊಸ ಟ್ಯಾಬ್</translation>
<translation id="7567204685887185387">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವನ್ನು ವಂಚನೆಯಿಂದ ನೀಡಿರಬಹುದು. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ಇನ್ನಷ್ಟು}one{<ph name="CONTACT_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ಇನ್ನಷ್ಟು}other{<ph name="CONTACT_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="7568593326407688803">ಈ ಪುಟವು<ph name="ORIGINAL_LANGUAGE" />ನಲ್ಲಿದೆ ನೀವು ಅದನ್ನು ಭಾಷಾಂತರಿಸಲು ಬಯಸುವಿರಾ?</translation>
<translation id="7569952961197462199">Chrome ನಿಂದ ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ತೆಗೆದುಹಾಕುವುದೇ?</translation>
<translation id="7569983096843329377">ಕಪ್ಪು</translation>
@@ -793,9 +818,11 @@
<translation id="7714464543167945231">ಪ್ರಮಾಣಪತ್ರ</translation>
<translation id="7716147886133743102">ನಿಮ್ಮ ನಿರ್ವಾಹಕರಿಂದ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ</translation>
<translation id="7716424297397655342">ಸಂಗ್ರಹದಿಂದ ಈ ಸೈಟ್ ಲೋಡ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ</translation>
+<translation id="774634243536837715">ಅಪಾಯಕಾರಿ ವಿಷಯವನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ.</translation>
<translation id="7752995774971033316">ನಿರ್ವಹಣೆಯಲ್ಲಿಲ್ಲ</translation>
<translation id="7755287808199759310">ನಿಮ್ಮ ಪೋಷಕರು ನಿಮಗಾಗಿ ಅದನ್ನು ಅನಿರ್ಬಂಧಿಸಬಹುದಾಗಿದೆ</translation>
<translation id="7758069387465995638">ಸಂಪರ್ಕವನ್ನು ಫೈರ್‌ವಾಲ್ ಅಥವಾ ಆಂಟಿವೈರಸ್ ಸಾಫ್ಟ್‌ವೇರ್ ನಿರ್ಬಂಧಿಸಿರಬಹುದು.</translation>
+<translation id="7759163816903619567">ಪ್ರದರ್ಶನ ಡೊಮೇನ್:</translation>
<translation id="7761701407923456692">ಸರ್ವರ್‌ನ ಪ್ರಮಾಣಪತ್ರವು URL ಗೆ ಸರಿ ಹೊಂದುವುದಿಲ್ಲ.</translation>
<translation id="7763386264682878361">ಪಾವತಿ ಮ್ಯಾನಿಫೆಸ್ಟ್ ವಿಶ್ಲೇಷಕರು</translation>
<translation id="7764225426217299476">ವಿಳಾಸ ಸೇರಿಸಿ</translation>
@@ -810,6 +837,7 @@
<translation id="7815407501681723534"><ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> ಗೆ '<ph name="SEARCH_STRING" />' ಕಂಡುಬಂದಿದೆ</translation>
<translation id="785549533363645510">ಆದರೆ, ನೀವು ಅದೃಶ್ಯರಾಗಿರುವುದಿಲ್ಲ. ಅಜ್ಞಾತವಾಗಿ ಹೋಗುವುದರಿಂದ ನಿಮ್ಮ ಉದ್ಯೋಗದಾತರು, ನಿಮ್ಮ ಇಂಟರ್ನೆಟ್ ಸೇವಾ ಪೂರೈಕೆದಾರರು ಇಲ್ಲವೇ ನೀವು ಭೇಟಿ ನೀಡುವ ವೆಬ್‌ಸೈಟ್‌ಗಳಿಂದ ನಿಮ್ಮ ಬ್ರೌಸಿಂಗ್ ಮರೆ ಮಾಡಲಾಗುವುದಿಲ್ಲ.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">ಡೆಬಿಟ್ ಮತ್ತು ಪ್ರೀಪೇಯ್ಡ್ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಮ್ಮತಿಸಲಾಗಿದೆ.</translation>
<translation id="7887683347370398519">ನಿಮ್ಮ CVC ಅನ್ನು ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ</translation>
<translation id="79338296614623784">ಮಾನ್ಯವಾದ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ</translation>
<translation id="7935318582918952113">DOM ಡಿಸ್ಟಿಲರ್</translation>
@@ -829,7 +857,6 @@
<translation id="8041089156583427627">ಪ್ರತಿಕ್ರಿಯೆ ಕಳುಹಿಸಿ</translation>
<translation id="8041940743680923270">ಜಾಗತಿಕ ಡಿಫಾಲ್ಟ್ ಬಳಸಿ (ಕೇಳಿ)</translation>
<translation id="8088680233425245692">ಲೇಖನವನ್ನು ವೀಕ್ಷಿಸಲು ವಿಫಲವಾಗಿದೆ.</translation>
-<translation id="8089520772729574115">1 MB ಗಿಂತ ಕಡಿಮೆ</translation>
<translation id="8091372947890762290">ಸರ್ವರ್‌ನಲ್ಲಿ ಸಕ್ರಿಯತೆ ಬಾಕಿ ಉಳಿದಿದೆ</translation>
<translation id="8118489163946903409">ಪಾವತಿ ವಿಧಾನ</translation>
<translation id="8131740175452115882">ದೃಢೀಕರಿಸು</translation>
@@ -841,10 +868,13 @@
<translation id="8194797478851900357">&amp;ಸರಿಸುವುದನ್ನು ರದ್ದುಗೊಳಿಸು</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" ID ಜೊತೆಗಿನ ವಿಸ್ತರಣೆಗೆ ಅಮಾನ್ಯವಾದ ಅಪ್‌ಡೇಟ್‌‌ URL.</translation>
<translation id="8202097416529803614">ಆರ್ಡರ್ ಸಾರಾಂಶ</translation>
+<translation id="8205463626947051446">ಈ ಸೈಟ್ ಸಾಮಾನ್ಯವಾಗಿ ಅತಿಕ್ರಮಣಕಾರಿಯಾಗಿರುವ ಜಾಹೀರಾತುಗಳನ್ನು ತೋರಿಸುತ್ತದೆ</translation>
<translation id="8218327578424803826">ನಿಯೋಜಿಸಲಾದ ಸ್ಥಳ:</translation>
<translation id="8225771182978767009">ಈ ಕಂಪ್ಯೂಟರ್ ಹೊಂದಿಸಿರುವ ವ್ಯಕ್ತಿಯು ಈ ಸೈಟ್ ನಿರ್ಬಂಧಿಸಲು ಆಯ್ಕೆಮಾಡಿದ್ದಾರೆ.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">ಪುಟವನ್ನು ಹೊಸ ಅದೃಶ್ಯ ಟ್ಯಾಬ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ</translation>
<translation id="8241707690549784388">ನೀವು ಎದುರು ನೋಡುತ್ತಿರುವ ಪುಟ ನೀವು ನಮೂದಿಸಿದ ಮಾಹಿತಿಯನ್ನು ಬಳಸಿದೆ. ಆ ಪುಟಕ್ಕೆ ಹಿಂದಿರುಗುವುದರಿಂದ ನೀವು ಮಾಡಿದ ಯಾವುದೇ ಕ್ರಿಯೆ ಪುನರಾವರ್ತಿಸುವಂತೆ ಮಾಡುತ್ತದೆ. ನೀವು ಮುಂದುವರಿಸಲು ಬಯಸುತ್ತೀರಾ?</translation>
+<translation id="8241712895048303527">ಈ ಸೈಟ್‌ನಲ್ಲಿ ನಿರ್ಬಂಧಿಸಿ</translation>
<translation id="8249320324621329438">ಕಳೆದ ಬಾರಿ ಪಡೆದಿರುವುದು:</translation>
<translation id="8253091569723639551">ಬಿಲ್ಲಿಂಗ್ ವಿಳಾಸ ಅಗತ್ಯವಿದೆ</translation>
<translation id="8261506727792406068">ಅಳಿಸಿ</translation>
@@ -855,7 +885,6 @@
<translation id="8308427013383895095">ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕದಲ್ಲಿನ ಸಮಸ್ಯೆಯಿಂದಾಗಿ ಭಾಷಾಂತರವು ವಿಫಲವಾಗಿದೆ.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ಗೆ ಪ್ರವೇಶವನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ</translation>
<translation id="834457929814110454">ನಿಮ್ಮ ಸುರಕ್ಷತೆ ಅಪಾಯಗಳು ನಿಮಗೆ ಅರ್ಥವಾಗಿದ್ದರೆ, ಅಪಾಯಕಾರಿ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ತೆಗೆದುಹಾಕುವುದಕ್ಕೂ ಮೊದಲು ನೀವು <ph name="BEGIN_LINK" />ಈ ಸೈಟ್‌ಗೆ ಭೇಟಿ<ph name="END_LINK" /> ನೀಡಬಹುದು.</translation>
-<translation id="8344669043927012510">ಪುಟವನ್ನು ಅದೃಶ್ಯ ಮೋಡ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ (⇧⌘N)</translation>
<translation id="8349305172487531364">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಬಾರ್</translation>
<translation id="8363502534493474904">ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್ ಆಫ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ</translation>
<translation id="8364627913115013041">ಹೊಂದಿಸಿಲ್ಲ.</translation>
@@ -865,6 +894,7 @@
<translation id="8398259832188219207"><ph name="UPLOAD_TIME" /> ಸಮಯಕ್ಕೆ ಕ್ರ್ಯಾಶ್ ವರದಿಯನ್ನು ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="8412145213513410671">(<ph name="CRASH_COUNT" />) ಕ್ರ‍್ಯಾಶ್‌ಗಳು </translation>
<translation id="8412392972487953978">ನೀವು ಒಂದೇ ರೀತಿಯ ಪಾಸ್‌ಫ್ರೇಸ್ ಅನ್ನು ಎರಡು ಬಾರಿ ನಮೂದಿಸಬೇಕು.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ಸೆಟ್ಟಿಂಗ್‌ಗಳು</translation>
<translation id="8433057134996913067">ಇದು ನಿಮ್ಮನ್ನು ಹೆಚ್ಚಿನ ವೆಬ್‌ಸೈಟ್‌ಗಳಿಂದ ಸೈನ್‌ ಔಟ್‌ ಮಾಡುತ್ತದೆ.</translation>
<translation id="8437238597147034694">&amp;ಸರಿಸುವುದನ್ನು ರದ್ದುಗೊಳಿಸಿ</translation>
@@ -872,8 +902,9 @@
<translation id="8483780878231876732">ನಿಮ್ಮ Google ಖಾತೆಯಿಂದ ಕಾರ್ಡ್‌ಗಳನ್ನು ಬಳಸಲು, Chrome ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ</translation>
<translation id="8488350697529856933">ಇದಕ್ಕೆ ಅನ್ವಯಿಸಲಾಗುತ್ತದೆ</translation>
<translation id="8498891568109133222">ಪ್ರತಿಕ್ರಿಯಿಸಲು <ph name="HOST_NAME" /> ಹೆಚ್ಚು ಸಮಯ ತೆಗೆದುಕೊಂಡಿದೆ.</translation>
-<translation id="8532105204136943229">ಮುಕ್ತಾಯದ ವರ್ಷ</translation>
+<translation id="8503813439785031346">ಬಳಕೆದಾರಹೆಸರು</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="8571890674111243710"><ph name="LANGUAGE" /> ಗೆ ಪುಟವನ್ನು ಭಾಷಾಂತರಿಸಲಾಗುತ್ತಿದೆ...</translation>
<translation id="858637041960032120">ಫೋನ್ ಸಂ. ಸೇರಿಸಿ
@@ -881,6 +912,7 @@
<translation id="859285277496340001">ಇದನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಲು ಪ್ರಮಾಣಪತ್ರವು ಯಾಂತ್ರೀಕರಣವನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸಿಲ್ಲ.</translation>
<translation id="8620436878122366504">ನಿಮ್ಮ ಪೋಷಕರು ಇನ್ನೂ ಇದನ್ನು ಅಂಗೀಕರಿಸಿಲ್ಲ</translation>
<translation id="8647750283161643317">ಎಲ್ಲವನ್ನೂ ಡೀಫಾಲ್ಟ್‌ಗೆ ಮರುಹೊಂದಿಸಿ</translation>
+<translation id="8660471606262461360">Google Payments ನಿಂದ</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> ಗೆ ನಿಮ್ಮ ಸಂಪರ್ಕವು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಆಗಿಲ್ಲ.</translation>
<translation id="8718314106902482036">ಪಾವತಿ ಪೂರ್ಣಗೊಂಡಿಲ್ಲ</translation>
<translation id="8725066075913043281">ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ</translation>
@@ -908,6 +940,7 @@
<translation id="8903921497873541725">ಝೂಮ್ ಇನ್</translation>
<translation id="8931333241327730545">ಈ ಕಾರ್ಡನ್ನು ನಿಮ್ಮ Google ಖಾತೆನಲ್ಲಿ ಉಳಿಸಲು ಬಯಸುವಿರಾ?</translation>
<translation id="8932102934695377596">ನಿಮ್ಮ ಗಡಿಯಾರ ಹಿಂದೆ ಇದೆ</translation>
+<translation id="8938939909778640821">ಸಮ್ಮತಿಸಲಾದ ಕ್ರೆಡಿಟ್ ಮತ್ತು ಪ್ರೀಪೇಯ್ಡ್ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="8971063699422889582">ಸರ್ವರ್‌ನ ಪ್ರಕಮಾಣಪತ್ರದ ಅವಧಿ ಮುಕ್ತಾಯಗೊಂಡಿದೆ.</translation>
<translation id="8986494364107987395">ಬಳಕೆಯ ಅಂಕಿಅಂಶಗಳನ್ನು ಮತ್ತು ಕ್ರಾಶ್ ವರದಿಗಳನ್ನು Google ಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ರವಾನಿಸು</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -937,7 +970,6 @@
<translation id="9169664750068251925">ಈ ಸೈಟ್ ಅನ್ನು ಯಾವಾಗಲೂ ನಿರ್ಬಂಧಿಸು</translation>
<translation id="9170848237812810038">&amp;ರದ್ದುಮಾಡು</translation>
<translation id="917450738466192189">ಸರ್ವರ್‌ನ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾಗಿದೆ.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ಇನ್ನಷ್ಟು}one{<ph name="SHIPPING_OPTION_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ಇನ್ನಷ್ಟು}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ಮತ್ತು <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ಇನ್ನಷ್ಟು}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ಬೆಂಬಲಿತವಲ್ಲದ ಪ್ರೋಟೋಕಾಲ್ ಬಳಸುತ್ತಿದೆ.</translation>
<translation id="9205078245616868884">ನಿಮ್ಮ ಡೇಟಾವನ್ನು ನಿಮ್ಮ ಸಿಂಕ್ ಪಾಸ್‌ಫ್ರೇಸ್‌ನೊಂದಿಗೆ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಸಿಂಕ್ ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ನಮೂದಿಸಿ.</translation>
<translation id="9207861905230894330">ಲೇಖನವನ್ನು ಸೇರಿಸಲು ವಿಫಲವಾಗಿದೆ.</translation>
@@ -946,9 +978,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ಫಾರ್ಮ್ ತೆರವುಗೊಳಿಸಿ</translation>
<translation id="939736085109172342">ಹೊಸ ಫೋಲ್ಡರ್</translation>
-<translation id="941721044073577244">ಈ ಸೈಟ್‌ಗೆ ಭೇಟಿ ನೀಡಲು ನಿಮಗೆ ಅನುಮತಿ ಇಲ್ಲ ಎಂದು ತೋರುತ್ತಿದೆ</translation>
<translation id="969892804517981540">ಅಧಿಕೃತವಾಗಿ ನಿರ್ಮಿಸಿ</translation>
<translation id="975560348586398090">{COUNT,plural, =0{ಯಾವುದೂ ಇಲ್ಲ}=1{1 ಐಟಂ}one{# ಐಟಂಗಳು}other{# ಐಟಂಗಳು}}</translation>
+<translation id="981121421437150478">ಆಫ್‌ಲೈನ್</translation>
<translation id="988159990683914416">ಡೆವಲಪರ್ ಬಿಲ್ಡ್</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ko.xtb b/chromium/components/strings/components_strings_ko.xtb
index fa984e4970e..ecf93f068db 100644
--- a/chromium/components/strings/components_strings_ko.xtb
+++ b/chromium/components/strings/components_strings_ko.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">다른 앱 닫기</translation>
<translation id="1055184225775184556">추가 실행 취소(&amp;U)</translation>
<translation id="10614374240317010">저장되지 않음</translation>
+<translation id="1066396345355680611"><ph name="SITE" /> 및 기타 일부 사이트의 보호된 콘텐츠에 액세스하지 못할 수 있습니다.</translation>
<translation id="106701514854093668">데스크톱 북마크</translation>
<translation id="1074497978438210769">안전하지 않음</translation>
<translation id="1080116354587839789">너비에 맞춤</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">읽기 목록</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" />에 캡처된 비정상 종료 보고서(아직 업로드 또는 무시되지 않음)</translation>
<translation id="1281526147609854549">발급 기관: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">위험한 콘텐츠가 차단됨</translation>
<translation id="1285320974508926690">이 사이트 번역 안함</translation>
<translation id="129553762522093515">최근에 닫은 탭</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />쿠키 삭제해 보기<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">등록 도메인:</translation>
<translation id="1340482604681802745">수령 주소</translation>
-<translation id="1344211575059133124">이 사이트를 방문하려면 권한이 필요한 것 같습니다.</translation>
<translation id="1344588688991793829">Chromium 자동완성 설정...</translation>
<translation id="1348198688976932919">방문하려는 사이트에 위험한 앱이 있습니다.</translation>
<translation id="1374468813861204354">제안사항</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" />에 위치한 <ph name="ORGANIZATION" />의 주소를 <ph name="ISSUER" />이(가) 확인했습니다.</translation>
<translation id="1426410128494586442">예</translation>
<translation id="1430915738399379752">인쇄</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />개}other{<ph name="PAYMENT_METHOD_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />개}}</translation>
<translation id="1506687042165942984">오래된 것으로 알려진 이 페이지의 저장된 사본을 표시합니다.</translation>
<translation id="1517433312004943670">전화번호 필요</translation>
+<translation id="1517500485252541695">사용 가능한 신용카드 및 직불카드</translation>
<translation id="1519264250979466059">생성 날짜</translation>
+<translation id="1527263332363067270">연결 대기 중...</translation>
<translation id="153384715582417236">새 콘텐츠가 없습니다.</translation>
<translation id="1549470594296187301">이 기능을 이용하려면 자바스크립트를 사용하도록 설정해야 합니다.</translation>
<translation id="1555130319947370107">파란색</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">시스템 관리자에게 문의하세요.</translation>
<translation id="1740951997222943430">올바른 만료 월을 입력하세요.</translation>
-<translation id="1745358365027406341">페이지 나중에 다운로드</translation>
<translation id="17513872634828108">열린 탭</translation>
<translation id="1753706481035618306">페이지 번호</translation>
<translation id="1763864636252898013">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 기기의 운영체제에서 신뢰하는 보안 인증서가 아닙니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">필수 입력란</translation>
<translation id="187918866476621466">시작 페이지 열기</translation>
<translation id="1883255238294161206">접기 목록</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />개}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />개}}</translation>
<translation id="1898423065542865115">필터링</translation>
+<translation id="1916770123977586577">이 사이트에 업데이트된 설정을 적용하려면 이 페이지를 다시 로드하세요.</translation>
+<translation id="1919345977826869612">광고</translation>
<translation id="192020519938775529">{COUNT,plural, =0{없음}=1{사이트 1개}other{사이트 #개}}</translation>
<translation id="194030505837763158"><ph name="LINK" />(으)로 이동</translation>
+<translation id="1948773908305951926">사용 가능한 선불카드</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> 북마크</translation>
<translation id="1973335181906896915">일련화 오류</translation>
<translation id="1974060860693918893">고급</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP 오류</translation>
<translation id="2270484714375784793">전화번호</translation>
<translation id="2282872951544483773">사용할 수 없는 실험</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{항목 <ph name="ITEM_COUNT" />개}other{항목 <ph name="ITEM_COUNT" />개}}</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>
<translation id="2337852623177822836">관리자가 제어하는 설정</translation>
<translation id="2354001756790975382">기타 북마크</translation>
<translation id="2354430244986887761">Google 세이프 브라우징이 최근 <ph name="SITE" />에서 <ph name="BEGIN_LINK" />유해한 앱을 발견<ph name="END_LINK" />했습니다.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">계속</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />에 캡처된 비정상 종료 보고서가 업로드되지 않았습니다.</translation>
<translation id="2367567093518048410">수준</translation>
-<translation id="237718015863234333">사용 가능한 대체 UI 없음</translation>
<translation id="2384307209577226199">엔터프라이즈 기본값</translation>
<translation id="2386255080630008482">서버 인증서가 폐기되었습니다.</translation>
<translation id="2392959068659972793">값이 설정되지 않은 정책 표시</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">선택하면 이 기기에 카드 사본이 저장되어 Chromium에서 양식을 더 빠르게 입력할 수 있습니다.</translation>
<translation id="2498091847651709837">새 카드 스캔</translation>
<translation id="2501278716633472235">뒤로 이동</translation>
+<translation id="2503184589641749290">사용 가능한 직불카드 및 선불카드</translation>
<translation id="2515629240566999685">현재 지역의 신호 확인</translation>
-<translation id="2516305470678292029">대체 UI</translation>
<translation id="2539524384386349900">감지</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" />에서 잘못된 응답을 전송했습니다.</translation>
<translation id="2556876185419854533">수정 실행 취소(&amp;U)</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">배송 방법</translation>
<translation id="277499241957683684">기기 기록 없음</translation>
<translation id="2784949926578158345">연결이 재설정되었습니다.</translation>
+<translation id="2788784517760473862">사용 가능한 신용카드</translation>
<translation id="2794233252405721443">차단된 사이트</translation>
<translation id="2799020568854403057">방문하려는 사이트에 유해한 앱이 있습니다.</translation>
<translation id="2803306138276472711">최근 Google 세이프 브라우징이 <ph name="SITE" />에서 <ph name="BEGIN_LINK" />멀웨어를 감지<ph name="END_LINK" />했습니다. 평소에 안전한 웹사이트도 멀웨어에 감염될 때가 있습니다.</translation>
<translation id="2824775600643448204">주소창 및 검색창</translation>
<translation id="2826760142808435982">이 연결은 <ph name="CIPHER" />을(를) 사용하여 암호화되고 인증되며 <ph name="KX" />을(를) 키 교환 매커니즘으로 사용합니다.</translation>
<translation id="2835170189407361413">서식 지우기</translation>
+<translation id="2851634818064021665">이 사이트에 방문하려면 권한이 필요합니다.</translation>
<translation id="2856444702002559011">해커가 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />에서 정보(예: 비밀번호, 메시지, 신용카드 등)를 도용하려고 시도 중일 수 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />자세히 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">새로고침 안함</translation>
-<translation id="2900469785430194048">이 웹페이지를 표시하려고 했으나 Chrome 메모리가 부족합니다.</translation>
<translation id="2909946352844186028">네트워크 변경이 감지되었습니다.</translation>
<translation id="2916038427272391327">다른 프로그램 닫기</translation>
<translation id="2922350208395188000">서버 인증서를 확인할 수 없습니다.</translation>
<translation id="2928905813689894207">청구서 수신 주소</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />개}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />개}}</translation>
<translation id="2941952326391522266">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 서버의 보안 인증서가 <ph name="DOMAIN2" />에서 제공한 것입니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
<translation id="2948083400971632585">설정 페이지의 연결을 구성하는 프록시를 사용 중지할 수 있습니다.</translation>
<translation id="2955913368246107853">검색 바 닫기</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">선택하면 이 기기에 카드 사본이 저장되어 Chromium에서 양식을 더 빠르게 작성할 수 있습니다.</translation>
<translation id="2985398929374701810">올바른 주소를 입력하세요.</translation>
<translation id="2986368408720340940">사용할 수 없는 수령 방법입니다. 다른 방법을 선택하세요.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">시크릿 탭을 모두 닫으면 시크릿 탭에서 보는 페이지는 브라우저의 방문 기록, 쿠키 저장소, 검색 기록 어디에도 남지 않습니다. 단, 다운로드한 파일이나 생성한 북마크는 유지됩니다.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">섬</translation>
-<translation id="317583078218509884">새 사이트 권한 설정은 페이지를 새로고침한 다음에 적용됩니다.</translation>
<translation id="3176929007561373547">프록시 설정을 확인하거나 네트워크 관리자에게 문의하여
프록시 서버가 작동하는지 확인하세요. 프록시 서버를 사용하지 않으려면
다음 단계를 따르세요.
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">시크릿 모드에서 페이지 열기</translation>
<translation id="320323717674993345">결제 취소</translation>
<translation id="3207960819495026254">북마크됨</translation>
<translation id="3225919329040284222">서버가 내장된 기대치와 일치하지 않는 인증서를 전달했습니다. 이러한 기대치는 사용자를 보호하기 위해 보안이 엄격한 특정 웹사이트에 포함됩니다.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">재정렬 실행 취소(&amp;U)</translation>
<translation id="3282497668470633863">카드 명의 추가</translation>
<translation id="3286538390144397061">지금 다시 시작</translation>
+<translation id="3287510313208355388">온라인 상태일 때 다운로드</translation>
<translation id="3303855915957856445">검색결과 없음</translation>
<translation id="3305707030755673451"><ph name="TIME" />에 동기화 암호로 데이터가 암호화되었습니다. 동기화를 시작하려면 입력하세요.</translation>
<translation id="3320021301628644560">청구지 주소 추가</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">가져오기 간격:</translation>
<translation id="3462200631372590220">세부정보 숨기기</translation>
<translation id="3467763166455606212">카드 소유자 이름을 입력해야 합니다.</translation>
-<translation id="3478058380795961209">만료 월</translation>
<translation id="3479539252931486093">예기치 않은 문제가 발생했나요? <ph name="BEGIN_LINK" />Google에 알리기<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">나중에</translation>
<translation id="3498215018399854026">현재 부모님께 연락할 수 없습니다. 나중에 다시 시도해 주세요.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;&lt;strong&gt;설정&lt;/strong&gt; 앱의 &lt;strong&gt;일반&lt;/strong&gt; 섹션에서 날짜와 시간을 맞추세요.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />자세히 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="COUNTRY" /> <ph name="STATE" />, <ph name="CITY" /></translation>
+<translation id="358285529439630156">신용카드 및 선불카드를 사용할 수 있습니다.</translation>
<translation id="3582930987043644930">이름 추가</translation>
<translation id="3583757800736429874">이동 다시 실행(&amp;R)</translation>
<translation id="3586931643579894722">세부정보 숨기기</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">이 메시지가 자주 표시된다면 다음을 시도해 보세요. <ph name="HELP_LINK" /></translation>
<translation id="3658742229777143148">개정</translation>
<translation id="3678029195006412963">요청을 서명할 수 없음</translation>
+<translation id="3678529606614285348">새 시크릿 창에서 페이지 열기(Ctrl-Shift-N)</translation>
<translation id="3679803492151881375"><ph name="CRASH_TIME" />에 캡처된 비정상 종료 보고서가 <ph name="UPLOAD_TIME" />에 업로드됨</translation>
<translation id="3681007416295224113">인증서 정보</translation>
<translation id="3690164694835360974">로그인이 안전하지 않음</translation>
-<translation id="3693415264595406141">비밀번호:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">로드 중...</translation>
<translation id="3712624925041724820">라이선스 만료됨</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805"><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="375403751935624634">서버 오류가 발생하여 번역하지 못했습니다.</translation>
<translation id="3759461132968374835">최근에 보고된 비정상 종료가 없습니다. 비정상 종료 보고를 사용 중지했을 때 발생한 비정상 종료는 여기에 표시되지 않습니다.</translation>
+<translation id="3765032636089507299">세이프 브라우징 페이지는 현재 준비 중입니다.</translation>
<translation id="3778403066972421603">이 카드를 Google 계정과 이 기기에 저장하시겠습니까?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">만료: <ph name="EXPIRATION_YEAR" />년 <ph name="EXPIRATION_MONTH" />월</translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">사용자의 사이트 액세스 요청이 <ph name="NAME" />님에게 전송되었습니다.</translation>
<translation id="3890664840433101773">이메일 추가</translation>
<translation id="3901925938762663762">만료된 카드입니다.</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">비정상 종료 보고서 ID <ph name="CRASH_ID" /> 업로드됨(로컬 비정상 종료 ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">보안 인증서에 주체 대체 이름(SAN)이 지정되어 있지 않아 이 서버가 <ph name="DOMAIN" />임을 입증할 수 없습니다. 서버를 잘못 설정했거나 공격자가 연결을 가로채고 있기 때문일 수도 있습니다.</translation>
+<translation id="3949601375789751990">인터넷 사용 기록이 여기에 표시됩니다.</translation>
<translation id="3963721102035795474">리더 모드</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{없음}=1{사이트 1개 }other{사이트 #개 }}</translation>
<translation id="397105322502079400">계산 중...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">사이트 설정</translation>
<translation id="4169947484918424451">Chromium에서 이 카드를 저장하도록 하시겠습니까?</translation>
<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="4196861286325780578">이동 다시 실행(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />방화벽 및 바이러스 백신 소프트웨어 설정 확인<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">비정상 종료</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">이 사이트의 공격자가 사용자 정보(예: 사진, 비밀번호, 메시지, 신용카드)를 도용하거나 삭제하는 위험한 프로그램을 컴퓨터에 설치하려고 시도할 수 있습니다.</translation>
<translation id="4372948949327679948">예상 <ph name="VALUE_TYPE" /> 값입니다.</translation>
<translation id="4377125064752653719"><ph name="DOMAIN" />에 접속하려 했으나 발행기관에서 서버가 전달한 인증서를 폐기했습니다. 이는 서버가 제시한 보안 자격증명 정보를 신뢰할 수 없음을 의미합니다. 사용자는 현재 공격자와 통신 중일 수도 있습니다.</translation>
-<translation id="4381091992796011497">사용자 이름:</translation>
<translation id="4394049700291259645">사용 중지</translation>
<translation id="4406896451731180161">검색결과</translation>
<translation id="4424024547088906515">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 Chrome에서 신뢰하는 보안 인증서가 아닙니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" />에서 로그인 인증서를 승인하지 않았거나 로그인 인증서가 제공되지 않았을 수 있습니다.</translation>
<translation id="443673843213245140">프록시 사용은 중지되었지만 명시적 프록시 설정이 지정되어 있습니다.</translation>
+<translation id="445100540951337728">사용 가능한 직불카드</translation>
<translation id="4506176782989081258">유효성 검사 오류 <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">시스템 관리자에게 문의</translation>
<translation id="450710068430902550">관리자와 공유</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Chrome에서 주소를 삭제하시겠습니까?</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" />에 대한 연결은 최신 암호화 기술을 사용하여 암호화됩니다.</translation>
<translation id="4594403342090139922">삭제 실행 취소(&amp;U)</translation>
+<translation id="4611292653554630842">로그인</translation>
<translation id="4619615317237390068">다른 기기의 탭</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 서버의 보안 인증서에 오류가 있습니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
<translation id="4690462567478992370">유효하지 않은 인증서 사용 중단</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">연결이 끊김</translation>
+<translation id="471880041731876836">이 사이트에 방문할 수 있는 권한이 없습니다.</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows 네트워크 진단 프로그램 실행<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">정책 새로고침</translation>
<translation id="4728558894243024398">플랫폼</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">보기</translation>
<translation id="4854362297993841467">사용할 수 없는 배달 방법입니다. 다른 방법을 선택하세요.</translation>
<translation id="4858792381671956233">이 사이트를 방문해도 괜찮은지 부모님께 문의했습니다.</translation>
-<translation id="4863764087567530506">이 콘텐츠는 사용자를 속여 소프트웨어를 설치하거나 개인정보를 유출할 수도 있습니다. <ph name="BEGIN_LINK" />표시하기<ph name="END_LINK" /></translation>
<translation id="4880827082731008257">기록 검색</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">인증 필요</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{외 웹페이지 1개}other{외 웹페이지 #개}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">페이지가 알 수 없는 언어에서 <ph name="LANGUAGE_LANGUAGE" />(으)로 번역되었습니다.</translation>
<translation id="4923459931733593730">결제</translation>
<translation id="4926049483395192435">지정해야 합니다.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">작업</translation>
<translation id="4958444002117714549">펼치기 목록</translation>
<translation id="4974590756084640048">경고 다시 사용</translation>
@@ -505,25 +516,35 @@
<translation id="5045550434625856497">비밀번호가 잘못되었습니다.</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>
<translation id="5087580092889165836">카드 추가</translation>
-<translation id="5089810972385038852">주</translation>
+<translation id="5089810972385038852">시/도</translation>
<translation id="5094747076828555589">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 Chromium에서 신뢰하는 보안 인증서가 아닙니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
<translation id="5095208057601539847">주/도</translation>
<translation id="5115563688576182185">(64비트)</translation>
<translation id="5141240743006678641">동기화 비밀번호를 Google 자격증명으로 암호화</translation>
-<translation id="514421653919133810">시크릿 모드에서 페이지 열기(Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">정책 응답에 오류 코드가 포함되어 있음</translation>
+<translation id="5159010409087891077">새 시크릿 창에서 페이지 열기(⇧⌘N)</translation>
<translation id="5171045022955879922">검색 또는 URL 입력</translation>
<translation id="5172758083709347301">컴퓨터</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />가 아닙니까? 오류 신고</translation>
-<translation id="5181140330217080051">다운로드 중</translation>
<translation id="5190835502935405962">북마크바</translation>
<translation id="5199729219167945352">실험실 기능</translation>
<translation id="5205222826937269299">이름은 필수입니다.</translation>
<translation id="5222812217790122047">이메일은 필수입니다.</translation>
<translation id="5251803541071282808">클라우드</translation>
<translation id="5277279256032773186">직장에서 Chrome을 사용하시나요? 기업은 직원의 Chrome 설정을 관리할 수 있습니다. 자세히 알아보기</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />소프트웨어를 일시적으로 사용 중지하여 웹에 접속하려면 다음 단계를 따르세요. 관리자 권한이 필요합니다.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />시작<ph name="END_BOLD" />을 클릭한 다음 <ph name="BEGIN_BOLD" />'로컬 서비스 보기'<ph name="END_BOLD" />를 검색하여 선택합니다.
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />를 선택합니다.
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />시작 유형<ph name="END_BOLD" />에서 <ph name="BEGIN_BOLD" />사용 중지됨<ph name="END_BOLD" />을 선택합니다.
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />서비스 상태<ph name="END_BOLD" />에서 <ph name="BEGIN_BOLD" />중지<ph name="END_BOLD" />를 클릭합니다.
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />적용<ph name="END_BOLD" />을 클릭한 다음 <ph name="BEGIN_BOLD" />확인<ph name="END_BOLD" />을 클릭합니다.
+ <ph name="LIST_ITEM" /><ph name="BEGIN_LEARN_MORE_LINK" />Chrome 고객센터<ph name="END_LEARN_MORE_LINK" />를 방문하여 컴퓨터에서 소프트웨어를 영구적으로 삭제하는 방법을 알아보세요.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">이 사이트에 대한 연결은 비공개가 아닙니다. 언제든지 VR 모드를 종료하려면 헤드셋을 제거한 후 뒤로를 누르세요.</translation>
<translation id="5299298092464848405">정책을 파싱하는 중 오류 발생</translation>
<translation id="5308689395849655368">비정상 종료 보고가 사용 중지되었습니다.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">'<ph name="ERROR_PATH" />'에서 스키마 유효성 검사 오류: <ph name="ERROR" /></translation>
<translation id="5452270690849572955"><ph name="HOST_NAME" /> 페이지를 찾을 수 없음</translation>
<translation id="5455374756549232013">잘못된 정책 타임스탬프</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">잘못된 데이터</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />개}other{<ph name="CONTACT_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />개}}</translation>
<translation id="5470861586879999274">수정 다시 실행(&amp;R)</translation>
+<translation id="5481076368049295676">이 콘텐츠는 사용자의 정보를 도용하거나 삭제하는 위험한 소프트웨어를 기기에 설치하려 할 수도 있습니다. <ph name="BEGIN_LINK" />표시하기<ph name="END_LINK" /></translation>
<translation id="54817484435770891">유효한 주소 추가</translation>
<translation id="5492298309214877701">회사, 조직, 학교 인트라넷의 해당 사이트가 외부 웹사이트와 동일한 URL을 갖고 있습니다.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">요청한 글을 찾지 못했습니다.</translation>
<translation id="5544037170328430102"><ph name="SITE" />에 삽입된 페이지 내용:</translation>
<translation id="5556459405103347317">새로고침</translation>
+<translation id="5560088892362098740">만료일</translation>
<translation id="5565735124758917034">활성</translation>
<translation id="5571083550517324815">이 주소에서 수령할 수 없습니다. 다른 주소를 선택하세요.</translation>
<translation id="5572851009514199876">이 사이트에 액세스할 수 있는지 확인할 수 있도록 Chrome을 시작하고 로그인하세요.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">정책 설정 로드 실패</translation>
<translation id="5631439013527180824">잘못된 기기 관리 토큰</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="5646376287012673985">위치</translation>
<translation id="5659593005791499971">이메일</translation>
<translation id="5669703222995421982">내게 맞는 콘텐츠 추천 받기</translation>
<translation id="5675650730144413517">페이지가 작동하지 않습니다.</translation>
<translation id="5710435578057952990">이 웹사이트의 주소가 확인되지 않았습니다.</translation>
-<translation id="5713016350996637505">사기성 콘텐츠 차단됨</translation>
+<translation id="5719499550583120431">선불카드를 사용할 수 있습니다.</translation>
<translation id="5720705177508910913">현재 사용자</translation>
<translation id="5732392974455271431">부모님이 차단 해제할 수 있습니다.</translation>
<translation id="5763042198335101085">올바른 이메일 주소를 입력하세요.</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065"><ph name="CARD_DETAIL" />을(를) 입력하시겠습니까?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" />에 대한 연결이 더 이상 사용되지 않는 암호화 기술을 사용하여 암호화됩니다.</translation>
<translation id="5813119285467412249">추가 다시 실행(&amp;R)</translation>
-<translation id="5814352347845180253"><ph name="SITE" /> 및 기타 일부 사이트의 프리미엄 콘텐츠를 액세스하지 못할 수 있습니다.</translation>
<translation id="5838278095973806738">공격자에 의해 도난당할 수 있으므로 이 사이트에 비밀번호나 신용카드 등 민감한 정보를 입력해서는 안 됩니다.</translation>
<translation id="5869405914158311789">사이트에 연결할 수 없음</translation>
<translation id="5869522115854928033">저장된 비밀번호</translation>
<translation id="5872918882028971132">상위 추천</translation>
+<translation id="5893752035575986141">신용카드를 사용할 수 있습니다.</translation>
<translation id="5901630391730855834">노란색</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" />(동기화됨)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1개 사용 중}other{#개 사용 중}}</translation>
-<translation id="5926846154125914413">일부 사이트의 프리미엄 콘텐츠를 액세스하지 못할 수 있습니다.</translation>
<translation id="5959728338436674663">위험한 앱과 사이트를 감지할 수 있도록 일부 <ph name="BEGIN_WHITEPAPER_LINK" />시스템 정보와 페이지 콘텐츠<ph name="END_WHITEPAPER_LINK" />를 Google로 자동 전송합니다. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">기록에서 삭제</translation>
<translation id="5975083100439434680">축소</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">닫기</translation>
<translation id="6042308850641462728">더보기</translation>
<translation id="6047233362582046994">유해한 앱이 삭제되기 전에 <ph name="BEGIN_LINK" />이 사이트를 방문<ph name="END_LINK" />하는 경우 보안상 위험을 반드시 이해하시기 바랍니다.</translation>
+<translation id="6047927260846328439">이 콘텐츠는 사용자를 속여 소프트웨어를 설치하거나 개인정보를 유출할 수도 있습니다. <ph name="BEGIN_LINK" />표시하기<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">현재 <ph name="SITE" />에서 인증서 고정을 사용하기 때문에 방문할 수 없습니다. 네트워크 오류와 공격은 대부분 일시적이므로 나중에 이 페이지가 정상적으로 작동할 수 있습니다.</translation>
<translation id="6060685159320643512">이러한 실험실 기능은 문제를 일으킬 수 있습니다.</translation>
<translation id="6080696365213338172">관리자 제공 인증서를 사용하여 콘텐츠에 액세스했습니다. 사용자가 <ph name="DOMAIN" />에 제공한 데이터가 관리자에 의해 차단될 수 있습니다.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">자세히 알아보기</translation>
<translation id="6169916984152623906">이제 비공개로 인터넷을 사용할 수 있으며, 이 기기를 사용하는 다른 사용자가 내 활동을 볼 수 없습니다. 하지만 다운로드한 항목과 북마크는 저장됩니다.</translation>
<translation id="6177128806592000436">이 사이트에 대한 연결은 안전하지 않습니다.</translation>
-<translation id="6184817833369986695">(집단: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">인터넷 연결을 확인하세요.</translation>
<translation id="6218753634732582820">Chromium에서 주소를 삭제하시겠습니까?</translation>
<translation id="6221345481584921695">Google 세이프 브라우징이 최근 <ph name="SITE" />에서 <ph name="BEGIN_LINK" />멀웨어를 감지<ph name="END_LINK" />했습니다. 평소에 안전한 웹사이트도 멀웨어에 감염될 수가 있습니다. 악성 콘텐츠의 출처는 알려진 멀웨어 배포자인 <ph name="SUBRESOURCE_HOST" />입니다.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Chrome의 멋진 새 기능에 관심이 있으십니까? chrome.com/beta 페이지에서 베타 채널을 방문해 보세요.</translation>
-<translation id="6389758589412724634">이 웹페이지를 표시하려고 했으나 Chromium 메모리가 부족합니다.</translation>
+<translation id="6397451950548600259">컴퓨터의 소프트웨어로 인해 Chrome이 안전하게 웹에 접속할 수 없습니다.</translation>
<translation id="6404511346730675251">북마크 수정</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" />의 만료일과 CVC를 입력하세요.</translation>
<translation id="6414888972213066896">이 사이트를 방문해도 괜찮은지 부모님께 문의했습니다.</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">기기 정책</translation>
<translation id="6477321094435799029">Chrome이 이 페이지에서 비정상적인 코드를 감지했으며 개인정보(예: 비밀번호, 전화번호, 신용카드) 보호를 위해 차단했습니다.</translation>
<translation id="6489534406876378309">비정상 종료 업로드 시작하기</translation>
+<translation id="6507833130742554667">신용카드 및 직불카드를 사용할 수 있습니다.</translation>
<translation id="6508722015517270189">Chrome 다시 시작하기</translation>
<translation id="6529602333819889595">삭제 다시 실행(&amp;R)</translation>
<translation id="6534179046333460208">피지컬 웹 제안</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">이 콘텐츠는 사용자의 정보를 도용하거나 삭제하는 위험한 소프트웨어를 기기에 설치하려 할 수도 있습니다. <ph name="BEGIN_LINK" />표시하기<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">암호화 옵션</translation>
<translation id="662080504995468778">머무르기</translation>
<translation id="6626291197371920147">유효한 카드 번호 추가</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> 검색</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />의 공격자가 사용자 정보(예: 사진, 비밀번호, 메시지, 신용카드)를 도용하거나 삭제하는 위험한 프로그램을 Mac에 설치하려고 시도할 수 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />자세히 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">이 정책은 사용되지 않습니다.</translation>
+<translation id="6657585470893396449">비밀번호</translation>
<translation id="6671697161687535275">Chromium에서 자동완성 항목 추천을 삭제하시겠습니까?</translation>
<translation id="6685834062052613830">로그아웃 후 설정 완료</translation>
<translation id="6710213216561001401">이전</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">구</translation>
<translation id="6973656660372572881">고정 프록시 서버와 .pac 스크립트 URL이 모두 지정되어 있습니다.</translation>
<translation id="6989763994942163495">고급 설정 표시</translation>
-<translation id="7000990526846637657">기록이 없습니다.</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Google 계정에 다른 형식의 탐색 기록이 <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />에 남아있을 수 있습니다.</translation>
<translation id="7029809446516969842">비밀번호</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">이 주소로 배송할 수 없습니다. 다른 주소를 선택하세요.</translation>
<translation id="7138472120740807366">배달 방법</translation>
<translation id="7139724024395191329">에미리트</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />개}other{<ph name="PAYMENT_METHOD_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />개}}</translation>
<translation id="7155487117670177674">결제가 안전하지 않음</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />개}other{<ph name="SHIPPING_OPTION_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />개}}</translation>
<translation id="7179921470347911571">지금 다시 시작</translation>
<translation id="7180611975245234373">새로고침</translation>
<translation id="7182878459783632708">설정된 정책 없음</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">다운로드</translation>
<translation id="7518003948725431193">다음 웹 주소(<ph name="URL" />)에 대해 발견된 웹페이지가 없습니다.</translation>
<translation id="7521387064766892559">자바스크립트</translation>
+<translation id="7526934274050461096">이 사이트로의 연결은 비공개가 아닙니다.</translation>
<translation id="7535087603100972091">값</translation>
<translation id="7537536606612762813">필수</translation>
<translation id="7542403920425041731">확인하면 카드 세부정보가 이 사이트와 공유됩니다.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">다음 도움말을 확인해 보세요.</translation>
<translation id="7554791636758816595">새 탭</translation>
<translation id="7567204685887185387">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 서버의 보안 인증서가 부정한 방식으로 발행되었을 수 있습니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />개}other{<ph name="CONTACT_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />개}}</translation>
<translation id="7568593326407688803">이 페이지는<ph name="ORIGINAL_LANGUAGE" />로 되어 있습니다. 번역하시겠습니까?</translation>
<translation id="7569952961197462199">Chrome에서 신용카드를 삭제하시겠습니까?</translation>
<translation id="7569983096843329377">검정색</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">인증서</translation>
<translation id="7716147886133743102">관리자가 차단함</translation>
<translation id="7716424297397655342">이 사이트를 캐시에서 로드할 수 없음</translation>
+<translation id="774634243536837715">위험한 콘텐츠 차단됨</translation>
<translation id="7752995774971033316">관리되지 않음</translation>
<translation id="7755287808199759310">부모님이 차단 해제할 수 있습니다.</translation>
<translation id="7758069387465995638">방화벽이나 바이러스 백신 소프트웨어가 연결을 차단했을 수 있습니다.</translation>
+<translation id="7759163816903619567">표시 도메인:</translation>
<translation id="7761701407923456692">서버의 인증서가 URL과 일치하지 않습니다.</translation>
<translation id="7763386264682878361">결제 매니페스트 파서</translation>
<translation id="7764225426217299476">주소 추가</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />'에 대해 <ph name="SEARCH_RESULTS" /> <ph name="NUMBER_OF_RESULTS" />개의 검색 결과를 찾았습니다.</translation>
<translation id="785549533363645510">하지만 흔적이 아예 남지 않는 것은 아닙니다. 시크릿 모드로 탐색해도 회사, 인터넷 서비스 제공업체 또는 방문한 웹사이트에 저장된 흔적까지 없앨 수는 없습니다.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">직불카드 및 선불카드를 사용할 수 있습니다.</translation>
<translation id="7887683347370398519">CVC를 확인한 후 다시 시도하세요.</translation>
<translation id="79338296614623784">올바른 전화번호를 입력하세요.</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">의견 보내기</translation>
<translation id="8041940743680923270">전체 기본값 사용(요청)</translation>
<translation id="8088680233425245692">글을 조회하지 못했습니다.</translation>
-<translation id="8089520772729574115">1MB 미만</translation>
<translation id="8091372947890762290">활성화 요청이 서버에서 대기 중</translation>
<translation id="8118489163946903409">결제 수단</translation>
<translation id="8131740175452115882">확인</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">이동 실행 취소(&amp;U)</translation>
<translation id="8201077131113104583">ID가 '<ph name="EXTENSION_ID" />'인 확장 프로그램에 대한 잘못된 업데이트 URL</translation>
<translation id="8202097416529803614">주문 요약</translation>
+<translation id="8205463626947051446">사이트에 방해가 되는 광고를 표시하는 경향이 있음</translation>
<translation id="8218327578424803826">지정된 위치:</translation>
<translation id="8225771182978767009">컴퓨터를 설정한 사용자가 이 사이트를 차단했습니다.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">새 시크릿 창에서 페이지 열기</translation>
<translation id="8241707690549784388">찾고 있는 페이지에서 사용자가 입력한 정보를 사용했습니다. 해당 페이지로 돌아가면 기존 작업을 반복할 수 있습니다. 계속하시겠습니까?</translation>
+<translation id="8241712895048303527">이 사이트에서 차단</translation>
<translation id="8249320324621329438">마지막으로 가져온 시간:</translation>
<translation id="8253091569723639551">청구지 주소 필요</translation>
<translation id="8261506727792406068">삭제</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">네트워크 연결 문제로 인해 번역에 실패했습니다.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" />에 대한 액세스가 거부됨</translation>
<translation id="834457929814110454">보안 관련 위험을 이해한다면 악성 프로그램이 삭제되기 전에 <ph name="BEGIN_LINK" />이 사이트를 방문<ph name="END_LINK" />해도 됩니다.</translation>
-<translation id="8344669043927012510">시크릿 모드에서 페이지 열기(⇧⌘N)</translation>
<translation id="8349305172487531364">북마크바</translation>
<translation id="8363502534493474904">비행기 모드 사용 중지</translation>
<translation id="8364627913115013041">설정 안됨</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">비정상 종료 보고서가 <ph name="UPLOAD_TIME" />에 업로드됨</translation>
<translation id="8412145213513410671">비정상 종료(<ph name="CRASH_COUNT" />회)</translation>
<translation id="8412392972487953978">동일한 암호를 두 번 입력해야 합니다.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">설정</translation>
<translation id="8433057134996913067">이 작업을 수행하면 대부분의 웹사이트에서 로그아웃됩니다.</translation>
<translation id="8437238597147034694">이동 실행 취소(&amp;U)</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Google 계정에서 카드를 사용하려면 Chrome에 로그인하세요.</translation>
<translation id="8488350697529856933">적용 대상</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" />에서 응답하는 데 시간이 너무 오래 걸립니다.</translation>
-<translation id="8532105204136943229">만료 연도</translation>
+<translation id="8503813439785031346">사용자이름</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="8571890674111243710">페이지를 <ph name="LANGUAGE" />(으)로 번역 중...</translation>
<translation id="858637041960032120">번호 추가</translation>
<translation id="859285277496340001">인증서는 취소 여부를 확인하는 매커니즘을 지정하지 않습니다.</translation>
<translation id="8620436878122366504">부모님이 아직 승인하지 않았습니다.</translation>
<translation id="8647750283161643317">기본값으로 재설정</translation>
+<translation id="8660471606262461360">출처: Google Payments</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" />로의 연결은 암호화되지 않습니다.</translation>
<translation id="8718314106902482036">결제가 완료되지 않음</translation>
<translation id="8725066075913043281">다시 시도하세요</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">확대</translation>
<translation id="8931333241327730545">이 카드를 Google 계정에 저장하시겠습니까?</translation>
<translation id="8932102934695377596">시간이 너무 먼 과거로 설정되어 있습니다.</translation>
+<translation id="8938939909778640821">사용 가능한 신용카드 및 선불카드</translation>
<translation id="8971063699422889582">서버 인증서가 만료되었습니다.</translation>
<translation id="8986494364107987395">사용 통계 및 비정상 종료 보고서를 Google에 자동으로 보내기</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">이 사이트에서 항상 차단</translation>
<translation id="9170848237812810038">실행 취소(&amp;U)</translation>
<translation id="917450738466192189">서버의 인증서가 유효하지 않습니다.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />개}other{<ph name="SHIPPING_OPTION_PREVIEW" /> 외 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />개}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" />에서 지원되지 않는 프로토콜을 사용합니다.</translation>
<translation id="9205078245616868884">동기화 암호로 데이터가 암호화되어 있습니다. 동기화를 시작하려면 입력하세요.</translation>
<translation id="9207861905230894330">글을 추가하지 못했습니다.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">양식 지우기</translation>
<translation id="939736085109172342">새 폴더</translation>
-<translation id="941721044073577244">이 사이트를 방문할 수 있는 권한이 없습니다.</translation>
<translation id="969892804517981540">공식 빌드</translation>
<translation id="975560348586398090">{COUNT,plural, =0{없음}=1{항목 1개}other{항목 #개}}</translation>
+<translation id="981121421437150478">오프라인</translation>
<translation id="988159990683914416">개발자 빌드</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_lt.xtb b/chromium/components/strings/components_strings_lt.xtb
index d21a7e1925d..82a7e6d142b 100644
--- a/chromium/components/strings/components_strings_lt.xtb
+++ b/chromium/components/strings/components_strings_lt.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Uždarykite kitas programas</translation>
<translation id="1055184225775184556">&amp;Anuliuoti pridėjimą</translation>
<translation id="10614374240317010">Niekada neišsaugota</translation>
+<translation id="1066396345355680611">Galite prarasti prieigą prie saugomo turinio iš <ph name="SITE" /> ir kelių kitų svetainių.</translation>
<translation id="106701514854093668">Žymės staliniame kompiuteryje</translation>
<translation id="1074497978438210769">Nesaugi</translation>
<translation id="1080116354587839789">Pritaikyti pagal plotį</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Skaitymo sąrašas</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" /> užfiksuota strigčių ataskaita (dar neįkelta ar nepaisoma)</translation>
<translation id="1281526147609854549">Išdavė „<ph name="ISSUER" />“</translation>
-<translation id="1283919782143846010">Pavojingas turinys užblokuotas</translation>
<translation id="1285320974508926690">Niekada neversti šios svetainės</translation>
<translation id="129553762522093515">Neseniai uždarytas</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Pabandykite išvalyti slapukus<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Registracijos domenas:</translation>
<translation id="1340482604681802745">Paėmimo adresas</translation>
-<translation id="1344211575059133124">Panašu, kad jums reikia leidimo, jei norite apsilankyti šioje svetainėje</translation>
<translation id="1344588688991793829">„Chromium“ automatinio pildymo nustatymai...</translation>
<translation id="1348198688976932919">Pateiktoje svetainėje yra pavojingų programų</translation>
<translation id="1374468813861204354">pasiūlymai</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="ORGANIZATION" /> identiškumą <ph name="LOCALITY" /> patikrino <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Taip</translation>
<translation id="1430915738399379752">Spausdinti</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}many{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Rodyti išsaugotą šio puslapio kopiją (kuri yra pasenusi).</translation>
<translation id="1517433312004943670">Būtinas telefono numeris</translation>
+<translation id="1517500485252541695">Tinkamos kredito ir debeto kortelės</translation>
<translation id="1519264250979466059">Sukūrimo data</translation>
+<translation id="1527263332363067270">Laukiama ryšio…</translation>
<translation id="153384715582417236">Kol kas tiek</translation>
<translation id="1549470594296187301">Norint naudoti šią funkciją, reikia įgalinti „JavaScript“.</translation>
<translation id="1555130319947370107">Mėlyna</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Atsisiųsti puslapį vėliau</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Būtinas laukas</translation>
<translation id="187918866476621466">Atidaryti paleidimo puslapius</translation>
<translation id="1883255238294161206">Sutraukti sąrašą</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtravimas</translation>
+<translation id="1916770123977586577">Kad pritaikytumėte atnaujintus nustatymus šioje svetainėje, iš naujo įkelkite šį puslapį</translation>
+<translation id="1919345977826869612">Skelbimai</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Nėra}=1{1 svetainė}one{# svetainė}few{# svetainės}many{# svetainės}other{# svetainių}}</translation>
<translation id="194030505837763158">Apsilankykite <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Tinkamos išankstinio mokėjimo kortelės</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> žymės</translation>
<translation id="1973335181906896915">Serijinio rengimo klaida</translation>
<translation id="1974060860693918893">Išplėstiniai</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP klaida</translation>
<translation id="2270484714375784793">Telefono numeris</translation>
<translation id="2282872951544483773">Nepasiekiami eksperimentai</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> elementas}one{<ph name="ITEM_COUNT" /> elementas}few{<ph name="ITEM_COUNT" /> elementai}many{<ph name="ITEM_COUNT" /> elemento}other{<ph name="ITEM_COUNT" /> elementų}}</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>
<translation id="2337852623177822836">Nustatymą valdo administratorius</translation>
<translation id="2354001756790975382">Kitos žymės</translation>
<translation id="2354430244986887761">„Google“ saugaus naršymo technologija svetainėje <ph name="SITE" /> neseniai <ph name="BEGIN_LINK" />aptiko kenkėjiškų programų<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Tęsti</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> užfiksuota strigčių ataskaita nebuvo įkelta</translation>
<translation id="2367567093518048410">Lygis</translation>
-<translation id="237718015863234333">Nėra jokių galimų NS alternatyvų</translation>
<translation id="2384307209577226199">Numatytieji įmonės nustatymai</translation>
<translation id="2386255080630008482">Serverio sertifikatas panaikintas.</translation>
<translation id="2392959068659972793">Rodyti politiką su nenustatyta verte</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Jei pažymėta, „Chromium“ išsaugos kortelės kopiją įrenginyje, kad galėtumėte greičiau užpildyti formas.</translation>
<translation id="2498091847651709837">Nuskaityti naują kortelę</translation>
<translation id="2501278716633472235">Grįžti</translation>
+<translation id="2503184589641749290">Tinkamos debeto ir išankstinio mokėjimo kortelės</translation>
<translation id="2515629240566999685">Patikrinti signalo stiprumą savo srityje</translation>
-<translation id="2516305470678292029">NS alternatyvos</translation>
<translation id="2539524384386349900">Aptikti</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> išsiuntė netinkamą atsaką.</translation>
<translation id="2556876185419854533">&amp;Anuliuoti redagavimą</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Pristatymo metodas</translation>
<translation id="277499241957683684">Trūksta įrenginio įrašo</translation>
<translation id="2784949926578158345">Ryšys atkurtas.</translation>
+<translation id="2788784517760473862">Tinkamos kredito kortelės</translation>
<translation id="2794233252405721443">Svetainė užblokuota</translation>
<translation id="2799020568854403057">Pateiktoje svetainėje yra kenkėjiškų programų</translation>
<translation id="2803306138276472711">„Google“ saugaus naršymo sistema neseniai <ph name="BEGIN_LINK" />aptiko kenkėjiškų programų<ph name="END_LINK" /> svetainėje <ph name="SITE" />. Kartais svetainės, kurios paprastai yra saugios, užkrečiamos kenkėjiškomis programomis.</translation>
<translation id="2824775600643448204">Adreso ir paieškos juosta</translation>
<translation id="2826760142808435982">Ryšys užšifruotas ir tapatybė nustatyta naudojant <ph name="CIPHER" />. <ph name="KX" /> naudojamas kaip pagrindinis mainų mechanizmas.</translation>
<translation id="2835170189407361413">Valyti formą</translation>
+<translation id="2851634818064021665">Jums reikalingas leidimas, kad galėtumėte apsilankykite šioje svetainėje</translation>
<translation id="2856444702002559011">Užpuolikai gali bandyti pavogti jūsų informaciją iš <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (pvz., slaptažodžius, pranešimus ar kredito kortelių duomenis). <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Neįkelti iš naujo</translation>
-<translation id="2900469785430194048">„Google Chrome“ trūksta atminties šiam tinklalapiui pateikti.</translation>
<translation id="2909946352844186028">Aptiktas tinklo pasikeitimas.</translation>
<translation id="2916038427272391327">Uždarykite kitas programas</translation>
<translation id="2922350208395188000">Neįmanoma patikrinti serverio sertifikato.</translation>
<translation id="2928905813689894207">Atsiskaitymo adresas</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikatas yra iš <ph name="DOMAIN2" />. Taip gali nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užgrobėjo.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Jei šis nustatymas įgalintas, „Chromium“ išsaugos kortelės kopiją įrenginyje, kad galėtumėte greičiau užpildyti formas.</translation>
<translation id="2985398929374701810">Įveskite tinkamą adresą</translation>
<translation id="2986368408720340940">Šis paėmimo metodas nepasiekiamas. Išbandykite kitą metodą.</translation>
@@ -277,13 +281,11 @@
<translation id="3167968892399408617">Puslapiai, kuriuos peržiūrite inkognito skirtukų lapuose, nebus rodomi naršyklės istorijoje, slapukų saugykloje ar paieškos istorijoje, kai uždarysite visus inkognito skirtukų lapus. Visi atsisiųsti failai ar sukurtos žymės išliks.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Sala</translation>
-<translation id="317583078218509884">Nauji svetainės leidimų nustatymai pradės galioti iš naujo įkėlus puslapį.</translation>
<translation id="3176929007561373547">Patikrinkite tarpinio serverio nustatymus arba susisiekite su tinklo
administratoriumi, kad įsitikintumėte, jog tarpinis
serveris veikia. Jei manote, kad tarpinio serverio
naudoti nereikia:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Atidarykite puslapį inkognito režimu</translation>
<translation id="320323717674993345">Atšaukti mokėjimą</translation>
<translation id="3207960819495026254">Pažymėta</translation>
<translation id="3225919329040284222">Serveris pateikė sertifikatą, kuris neatitinka numatytų reikalavimų. Šie reikalavimai taikomi tam tikrose itin saugiose svetainėse, kad jūs būtumėte saugūs.</translation>
@@ -296,6 +298,7 @@
<translation id="3270847123878663523">&amp;Anuliuoti pertvarkymą</translation>
<translation id="3282497668470633863">Ant kortelės pateikto vardo pridėjimas</translation>
<translation id="3286538390144397061">Paleisti iš naujo dabar</translation>
+<translation id="3287510313208355388">Atsisiųsti prisijungus</translation>
<translation id="3303855915957856445">Nerasta jokių paieškos rezultatų</translation>
<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>
@@ -328,7 +331,6 @@
<translation id="3452404311384756672">Gauti intervalą:</translation>
<translation id="3462200631372590220">Slėpti išsamią informaciją</translation>
<translation id="3467763166455606212">Būtina nurodyti kortelės savininko vardą ir pavardę</translation>
-<translation id="3478058380795961209">Gal. pab. mėnuo</translation>
<translation id="3479539252931486093">Ar tai buvo netikėta? <ph name="BEGIN_LINK" />Praneškime mums apie tai<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ne dabar</translation>
<translation id="3498215018399854026">Šiuo metu nepavyko susisiekti su jūsų tėvu. Bandykite dar kartą.</translation>
@@ -344,6 +346,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kredito ir išankstinio mokėjimo kortelės tinkamos.</translation>
<translation id="3582930987043644930">Pridėti vardą</translation>
<translation id="3583757800736429874">&amp;Perkelti dar kartą</translation>
<translation id="3586931643579894722">Slėpti išsamią informaciją</translation>
@@ -359,10 +362,10 @@
<translation id="3655670868607891010">Jei tai rodoma dažnai, išbandykite <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Peržiūrėtas ir pataisytas leidimas</translation>
<translation id="3678029195006412963">Nepavyko pasirašyti užklausos</translation>
+<translation id="3678529606614285348">Atidarykite puslapį naujame inkognito lange („Ctrl“ – „Shift“ – N)</translation>
<translation id="3679803492151881375">Strigčių ataskaita užfiksuota <ph name="CRASH_TIME" />, įkelta <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Sertifikato informacija</translation>
<translation id="3690164694835360974">Prisijungimas nesaugus</translation>
-<translation id="3693415264595406141">Slaptažodis:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Įkeliama...</translation>
<translation id="3712624925041724820">Licencijos baigėsi</translation>
@@ -374,6 +377,7 @@
<translation id="3748148204939282805"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> užpuolikai gali bandyti apgaule priversti jus atlikti pavojingus veiksmus, pvz., įdiegti programinę įrangą ar atskleisti asmens informaciją (pvz., slaptažodžius, telefonų numerius ar kredito kortelių duomenis). <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Vertimas nepavyko dėl serverio klaidos.</translation>
<translation id="3759461132968374835">Nėra strigčių, apie kurias buvo neseniai pranešta. Strigtys, įvykusios tuo metu, kai strigčių ataskaitų teikimas buvo išjungtas, čia rodomos nebus.</translation>
+<translation id="3765032636089507299">Saugaus naršymo puslapis kuriamas.</translation>
<translation id="3778403066972421603">Ar norite išsaugoti šią kortelę „Google“ paskyroje ir šiame įrenginyje?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Galiojimo laikas baigiasi <ph name="EXPIRATION_MONTH" /> / <ph name="EXPIRATION_YEAR" /></translation>
@@ -386,8 +390,10 @@
<translation id="3886446263141354045">Jūsų užklausa pasiekti šią svetainę išsiųsta <ph name="NAME" />.</translation>
<translation id="3890664840433101773">Pridėti el. pašto adresą</translation>
<translation id="3901925938762663762">Kortelė nebegalioja</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Įkeltos strigties ataskaitos ID: <ph name="CRASH_ID" /> (vietinės strigties ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikate nenurodomi temos alternatyvūs pavadinimai. Tai gali būti dėl netinkamos konfigūracijos arba dėl ryšį pertraukusio užgrobėjo.</translation>
+<translation id="3949601375789751990">Naršymo istorija rodoma čia</translation>
<translation id="3963721102035795474">Skaitytojo režimas</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Nėra}=1{Iš 1 svetainės }one{Iš # svetainės }few{Iš # svetainių }many{Iš # svetainės }other{Iš # svetainių }}</translation>
<translation id="397105322502079400">Skaičiuojama...</translation>
@@ -416,6 +422,8 @@
<translation id="4165986682804962316">Svetainės nustatymai</translation>
<translation id="4169947484918424451">Ar norite, kad „Chromium“ išsaugotų šią kortelę?</translation>
<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="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>
@@ -439,12 +447,12 @@
<translation id="4356973930735388585">Šios svetainės užpuolėjai gali jūsų kompiuteryje bandyti įdiegti pavojingas programas, kurios vagia arba ištrina informaciją (pvz., nuotraukas, slaptažodžius, pranešimus ir kredito kortelių numerius).</translation>
<translation id="4372948949327679948">Numatyta „<ph name="VALUE_TYPE" />“ vertė.</translation>
<translation id="4377125064752653719">Bandėte pasiekti svetainę „<ph name="DOMAIN" />“, bet sertifikatą, kurį pateikė serveris, anuliavo jo išdavėjas. Tai reiškia, kad saugos kredencialais, kuriuos pateikė serveris, visiškai negalima pasitikėti. Galbūt bendraujate su užpuoliku.</translation>
-<translation id="4381091992796011497">Naudotojo vardas:</translation>
<translation id="4394049700291259645">Neleisti</translation>
<translation id="4406896451731180161">paieškos rezultatai</translation>
<translation id="4424024547088906515">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikatas nėra patikimas „Chrome“. Taip gali nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užgrobėjo.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> nepriėmė jūsų prisijungimo sertifikato arba prisijungimo sertifikatas nebuvo pateiktas.</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="4506176782989081258">Tikrinimo klaida: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Susisiekti su sistemos administratoriumi</translation>
<translation id="450710068430902550">Bendrinimas su administratoriumi</translation>
@@ -456,12 +464,14 @@
<translation id="4587425331216688090">Pašalinti adresą iš „Chrome“?</translation>
<translation id="4592951414987517459">Ryšys su <ph name="DOMAIN" /> užšifruotas naudojant modernų šifravimo paketą.</translation>
<translation id="4594403342090139922">&amp;Anuliuoti ištrynimą</translation>
+<translation id="4611292653554630842">Prisijungti</translation>
<translation id="4619615317237390068">Skirtukai iš kitų įrenginių</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikate yra klaidų. Taip gali nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užgrobėjo.</translation>
<translation id="4690462567478992370">Nebenaudoti negaliojančio sertifikato</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Ryšys nutrauktas</translation>
+<translation id="471880041731876836">Neturite leidimo, kad galėtumėte apsilankykite šioje svetainėje</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Paleistas įrankis „Windows Network Diagnostics“<ph name="END_LINK" />.</translation>
<translation id="4726672564094551039">Iš naujo įkelti politiką</translation>
<translation id="4728558894243024398">Platforma</translation>
@@ -483,14 +493,15 @@
<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="4863764087567530506">Šiuo turiniu gali būti bandoma apgaule priversti jus įdiegti programinę įrangą arba atskleisti asmens informaciją. <ph name="BEGIN_LINK" />Rodyti vis tiek<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Ieškoti istorijoje</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Autentifikavimas yra būtinas</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ir dar 1 tinklalapis}one{ir dar # tinklalapis}few{ir dar # tinklalapiai}many{ir dar # tinklalapio}other{ir dar # tinklalapių}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Šis puslapis išverstas iš nežinomos kalbos į <ph name="LANGUAGE_LANGUAGE" /> k.</translation>
<translation id="4923459931733593730">Mokėjimas</translation>
<translation id="4926049483395192435">Turi būti nurodyta.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" /> / <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Veiksmai</translation>
<translation id="4958444002117714549">Išskleisti sąrašą</translation>
<translation id="4974590756084640048">Iš naujo įgalinti įspėjimus</translation>
@@ -506,6 +517,7 @@
<translation id="5045550434625856497">Neteisingas slaptažodis</translation>
<translation id="5056549851600133418">Jums skirti straipsniai</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Patikrinti tarpinio serverio adresą<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Galite prarasti prieigą prie saugomo turinio iš kelių svetainių.</translation>
<translation id="5087286274860437796">Šiuo metu serverio sertifikatas negalioja.</translation>
<translation id="5087580092889165836">Pridėti kortelę</translation>
<translation id="5089810972385038852">Valstija</translation>
@@ -513,18 +525,27 @@
<translation id="5095208057601539847">Provincija</translation>
<translation id="5115563688576182185">(64 bitų)</translation>
<translation id="5141240743006678641">Šifruoti sinchronizuotus slaptažodžius naudojant „Google“ prisijungimo duomenis</translation>
-<translation id="514421653919133810">Atidarykite puslapį inkognito režimu („Ctrl“ – „Shift“ – N)</translation>
<translation id="5145883236150621069">Politikos atsakyme yra klaidos kodas</translation>
+<translation id="5159010409087891077">Atidarykite puslapį naujame inkognito lange (⇧⌘N)</translation>
<translation id="5171045022955879922">Ieškokite ar įveskite URL</translation>
<translation id="5172758083709347301">Įrenginys</translation>
<translation id="5179510805599951267">Ne <ph name="ORIGINAL_LANGUAGE" /> k.? Pranešti apie šią klaidą</translation>
-<translation id="5181140330217080051">Atsisiunčiama</translation>
<translation id="5190835502935405962">Žymių juosta</translation>
<translation id="5199729219167945352">Bandymai</translation>
<translation id="5205222826937269299">Būtina nurodyti pavadinimą</translation>
<translation id="5222812217790122047">Būtina nurodyti el. paštą</translation>
<translation id="5251803541071282808">Debesis</translation>
<translation id="5277279256032773186">Naudojate „Chrome“ darbe? Įmonės gali tvarkyti darbuotojų „Chrome“ nustatymus. Sužinokite daugiau</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Jei norite laikinai išjungti programinę įrangą, kad galėtumėte pasiekti žiniatinklį, atlikite nurodytus veiksmus. Jums reikės administratoriaus teisių.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Spustelėkite <ph name="BEGIN_BOLD" />Pradėti<ph name="END_BOLD" />, tada suraskite ir pasirinkite <ph name="BEGIN_BOLD" />Peržiūrėti vietines paslaugas<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Pasirinkite <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Skiltyje <ph name="BEGIN_BOLD" />Paleisties tipas<ph name="END_BOLD" /> pasirinkite <ph name="BEGIN_BOLD" />Išjungta<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Skiltyje <ph name="BEGIN_BOLD" />Paslaugos būsena<ph name="END_BOLD" /> spustelėkite <ph name="BEGIN_BOLD" />Stabdyti<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Spustelėkite <ph name="BEGIN_BOLD" />Taikyti<ph name="END_BOLD" />, tada – <ph name="BEGIN_BOLD" />Gerai<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Apsilankykite <ph name="BEGIN_LEARN_MORE_LINK" />„Chrome“ pagalbos centre<ph name="END_LEARN_MORE_LINK" />, kad sužinotumėte, kaip visam laikui pašalinti programinę įrangą iš kompiuterio
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Ryšys su šia svetaine nėra privatus. Kad galėtumėte bet kada išeiti iš virtualiosios realybės režimo, pašalinkite ausines ir paspauskite „Atgal“.</translation>
<translation id="5299298092464848405">Analizuojant politiką įvyko klaida</translation>
<translation id="5308689395849655368">Strigčių ataskaitų teikimas neleidžiamas.</translation>
@@ -541,9 +562,10 @@
<translation id="5439770059721715174">Schemos patvirtinimo klaida „<ph name="ERROR_PATH" />“: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Nepavyksta rasti šio <ph name="HOST_NAME" /> puslapio</translation>
<translation id="5455374756549232013">Bloga politikos laiko žymė</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> iš <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Netinkama</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}many{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Redaguoti dar kartą</translation>
+<translation id="5481076368049295676">Šiuo turiniu gali būti bandoma priversti jus įrenginyje įdiegti pavojingą programinę įrangą, kuri vagia arba ištrina jūsų informaciją. <ph name="BEGIN_LINK" />Rodyti vis tiek<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Tinkamo adreso pridėjimas</translation>
<translation id="5492298309214877701">Šios svetainės URL įmonės, organizacijos ar mokyklos intranete toks pat kaip išorinės svetainės URL.
<ph name="LINE_BREAK" />
@@ -555,6 +577,7 @@
<translation id="5540224163453853">Nepavyko rasti straipsnio, dėl kurio pateikta užklausa.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> įterptame puslapyje sakoma:</translation>
<translation id="5556459405103347317">Įkelti iš naujo</translation>
+<translation id="5560088892362098740">Galiojimo laiko pabaigos data</translation>
<translation id="5565735124758917034">Aktyvus</translation>
<translation id="5571083550517324815">Negalima paimti šiuo adresu. Pasirinkite kitą adresą.</translation>
<translation id="5572851009514199876">Pirmiausia prisijunkite prie „Chrome“, kad „Chrome“ galėtų patikrinti, ar jums leidžiama pasiekti šią svetainę.</translation>
@@ -569,12 +592,13 @@
<translation id="5629630648637658800">Įkeliant politikos nustatymus įvyko klaida</translation>
<translation id="5631439013527180824">Netinkamas įrenginio tvarkymo prieigos raktas</translation>
<translation id="5633066919399395251">Šiuo metu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> užpuolikai gali jūsų kompiuteryje bandyti įdiegti pavojingas programas, kurios vagia arba ištrina informaciją (pvz., nuotraukas, slaptažodžius, pranešimus ir kredito kortelių duomenis). <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Klaidinantis turinys užblokuotas.</translation>
<translation id="5646376287012673985">Vietovė</translation>
<translation id="5659593005791499971">El. paštas</translation>
<translation id="5669703222995421982">Suasmeninto turinio gavimas</translation>
<translation id="5675650730144413517">Šis puslapis neveikia</translation>
<translation id="5710435578057952990">Šio tinklalapio tapatybė nenustatyta.</translation>
-<translation id="5713016350996637505">Klaidinantis turinys užblokuotas</translation>
+<translation id="5719499550583120431">Išankstinio mokėjimo kortelės tinkamos.</translation>
<translation id="5720705177508910913">Dabartinis naudotojas</translation>
<translation id="5732392974455271431">Jūsų tėvai gali atblokuoti ją už jus</translation>
<translation id="5763042198335101085">Įveskite galiojantį el. pašto adresą</translation>
@@ -586,15 +610,14 @@
<translation id="5803412860119678065">Ar norite, kad būtų įvesta <ph name="CARD_DETAIL" /> informacija?</translation>
<translation id="5810442152076338065">Ryšys su <ph name="DOMAIN" /> užšifruotas naudojant pasenusį šifravimo paketą.</translation>
<translation id="5813119285467412249">&amp;Pridėti dar kartą</translation>
-<translation id="5814352347845180253">Galite prarasti prieigą prie aukščiausios kokybės turinio iš <ph name="SITE" /> ir kelių kitų svetainių.</translation>
<translation id="5838278095973806738">Šioje svetainėje neturėtumėte pateikti neskelbtinos informacijos (pvz., slaptažodžių ar kredito kortelių numerių), nes ją gali pavogti užpuolikai.</translation>
<translation id="5869405914158311789">Nepavyksta pasiekti šios svetainės</translation>
<translation id="5869522115854928033">Išsaugoti slaptažodžiai</translation>
<translation id="5872918882028971132">Pasiūlymai tėvams</translation>
+<translation id="5893752035575986141">Kredito kortelės tinkamos.</translation>
<translation id="5901630391730855834">Geltona</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="5926846154125914413">Galite prarasti prieigą prie aukščiausios kokybės turinio iš kelių svetainių.</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="5967867314010545767">Pašalinti iš istorijos</translation>
<translation id="5975083100439434680">Tolinti</translation>
@@ -610,6 +633,7 @@
<translation id="6040143037577758943">Uždaryti</translation>
<translation id="6042308850641462728">Daugiau</translation>
<translation id="6047233362582046994">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="6047927260846328439">Šiuo turiniu gali būti bandoma apgaule priversti jus įdiegti programinę įrangą arba atskleisti asmens informaciją. <ph name="BEGIN_LINK" />Rodyti vis tiek<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Šiuo metu negalite apsilankyti <ph name="SITE" />, nes svetainėje naudojamas sertifikatų prisegimas. Tinklo klaidos ir užpuolimai dažniausiai yra laikini, todėl šis puslapis vėliau tikriausiai veiks.</translation>
<translation id="6060685159320643512">Atsargiai, šie bandymai gali būti pavojingi</translation>
<translation id="6080696365213338172">Pasiekėte turinį naudodami administratoriaus pateiktą sertifikatą. Duomenys, kuriuos pateikiate <ph name="DOMAIN" />, gali būti perimti administratoriaus.</translation>
@@ -623,7 +647,6 @@
<translation id="6165508094623778733">Sužinokite daugiau</translation>
<translation id="6169916984152623906">Dabar galite naršyti privačiai, o kiti šį įrenginį naudojantys žmonės nematys jūsų veiklos. Tačiau atsisiuntimai ir žymės bus išsaugoti.</translation>
<translation id="6177128806592000436">Ryšys su šia svetaine nėra saugus</translation>
-<translation id="6184817833369986695">(grupė: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Patikrinkite interneto ryšį</translation>
<translation id="6218753634732582820">Pašalinti adresą iš „Chromium“?</translation>
<translation id="6221345481584921695">„Google“ saugaus naršymo sistema neseniai <ph name="BEGIN_LINK" />aptiko kenkėjišką programą<ph name="END_LINK" /> svetainėje <ph name="SITE" />. Svetainės, kurios paprastai yra saugios, kartais užkrečiamos kenkėjiškomis programomis. Kenkėjiškas turinys gautas iš <ph name="SUBRESOURCE_HOST" />, žinomo kenkėjiškų programų platintojo.</translation>
@@ -642,13 +665,14 @@
<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>
<translation id="6387478394221739770">Domina naujos „Chrome“ funkcijos? Išbandykite mūsų beta kanalą adresu chrome.com/beta.</translation>
-<translation id="6389758589412724634">„Chromium“ trūksta atminties šiam tinklalapiui pateikti.</translation>
+<translation id="6397451950548600259">Programinė įranga jūsų kompiuteryje neleidžia „Chrome“ saugiai prisijungti prie žiniatinklio</translation>
<translation id="6404511346730675251">Redaguoti žymę</translation>
<translation id="6410264514553301377">įveskite „<ph name="CREDIT_CARD" />“ galiojimo pabaigos datą ir kortelės saugos kodą (CVC)</translation>
<translation id="6414888972213066896">Paprašėte vieno iš tėvų leidimo apsilankyti šiame puslapyje</translation>
@@ -663,6 +687,7 @@
<translation id="647261751007945333">Įrenginio politika</translation>
<translation id="6477321094435799029">„Chrome“ šiame puslapyje aptiko neįprastą kodą ir jį užblokavo, kad apsaugotų asmens informaciją (pvz., slaptažodžius, telefonų numerius ir kredito korteles).</translation>
<translation id="6489534406876378309">Pradėti įkelti strigtis</translation>
+<translation id="6507833130742554667">Kredito ir debeto kortelės tinkamos.</translation>
<translation id="6508722015517270189">Iš naujo paleiskite „Chrome“</translation>
<translation id="6529602333819889595">&amp;Ištrinti dar kartą</translation>
<translation id="6534179046333460208">Fizinio žiniatinklio pasiūlymai</translation>
@@ -671,13 +696,13 @@
<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="657639383826808334">Šiuo turiniu gali būti bandoma priversti jus įrenginyje įdiegti pavojingą programinę įrangą, kuri vagia arba ištrina jūsų informaciją. <ph name="BEGIN_LINK" />Rodyti vis tiek<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Šifravimo parinktys</translation>
<translation id="662080504995468778">Likti</translation>
<translation id="6626291197371920147">Galiojančios kortelės numerio pridėjimas</translation>
<translation id="6628463337424475685">„<ph name="ENGINE" />“ paieška</translation>
<translation id="6630809736994426279">Šiuo metu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> užpuolikai gali jūsų „Mac“ įrenginyje 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="6644283850729428850">Ši politika nepatvirtinta.</translation>
+<translation id="6657585470893396449">Slaptažodis</translation>
<translation id="6671697161687535275">Pašalinti formos pasiūlymą iš „Chromium“?</translation>
<translation id="6685834062052613830">Atsijunkite ir užbaikite sąranką</translation>
<translation id="6710213216561001401">Ankstesnis</translation>
@@ -708,7 +733,6 @@
<translation id="6970216967273061347">Rajonas</translation>
<translation id="6973656660372572881">Nurodyti fiksuoti įgaliotieji serveriai ir .pac scenarijaus URL.</translation>
<translation id="6989763994942163495">Rodyti išplėstinius nustatymus...</translation>
-<translation id="7000990526846637657">Nerasta jokių istorijos įrašų</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Adresu <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> gali būti pateikta kitų formų jūsų „Google“ paskyros naršymo istorija</translation>
<translation id="7029809446516969842">Slaptažodžiai</translation>
@@ -723,7 +747,9 @@
<translation id="7129409597930077180">Negalima pristatyti šiuo adresu. Pasirinkite kitą adresą.</translation>
<translation id="7138472120740807366">Pristatymo metodas</translation>
<translation id="7139724024395191329">Emyratas</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}many{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Mokėjimas nesaugus</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}many{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Paleisti iš naujo dabar</translation>
<translation id="7180611975245234373">Atnaujinti</translation>
<translation id="7182878459783632708">Nenustatyta jokia politika</translation>
@@ -764,6 +790,7 @@
<translation id="7514365320538308">Atsisiųsti</translation>
<translation id="7518003948725431193">Nerasta nė vieno tinklalapio šiuo žiniatinklio adresu: <ph name="URL" /></translation>
<translation id="7521387064766892559">„JavaScript“</translation>
+<translation id="7526934274050461096">Jūsų ryšys su šia svetaine nėra privatus</translation>
<translation id="7535087603100972091">Reikšmė</translation>
<translation id="7537536606612762813">Privaloma</translation>
<translation id="7542403920425041731">Kai patvirtinsite, išsami kortelės informacija bus bendrinama su šia svetaine.</translation>
@@ -773,7 +800,6 @@
<translation id="7552846755917812628">Išbandykite toliau pateiktus patarimus.</translation>
<translation id="7554791636758816595">Naujas skirtukas</translation>
<translation id="7567204685887185387">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikatas gali būti neteisėtai išduotas. Taip gali nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užgrobėjo.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}many{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Šis puslapis yra<ph name="ORIGINAL_LANGUAGE" />Ar norėtumėte jį išversti?</translation>
<translation id="7569952961197462199">Pašalinti kredito kortelės informaciją iš „Chrome“?</translation>
<translation id="7569983096843329377">Juoda</translation>
@@ -800,9 +826,11 @@
<translation id="7714464543167945231">Sertifikatas</translation>
<translation id="7716147886133743102">Užblokavo jūsų administratorius</translation>
<translation id="7716424297397655342">Nepavyksta įkelti šios svetainės iš talpyklos</translation>
+<translation id="774634243536837715">Pavojingas turinys užblokuotas.</translation>
<translation id="7752995774971033316">Netvarkoma</translation>
<translation id="7755287808199759310">Jūsų tėtis ar mama gali ją atblokuoti už jus</translation>
<translation id="7758069387465995638">Gali būti, kad užkarda arba antivirusinė programinė įranga užblokavo ryšį.</translation>
+<translation id="7759163816903619567">Pateikties domenas:</translation>
<translation id="7761701407923456692">Serverio sertifikatas neatitinka URL.</translation>
<translation id="7763386264682878361">Mokėjimo aprašo analizavimo įrankis</translation>
<translation id="7764225426217299476">Pridėti adresą</translation>
@@ -817,6 +845,7 @@
<translation id="7815407501681723534">Pagal terminą „<ph name="SEARCH_STRING" />“ surasta tiek <ph name="SEARCH_RESULTS" />: <ph name="NUMBER_OF_RESULTS" /></translation>
<translation id="785549533363645510">Tačiau nesate nematomi. Įjungus inkognito režimą, naršymo veiksmai vis tiek matomi darbdaviui, interneto paslaugų teikėjui ar svetainėms, kuriose lankotės.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Debeto ir išankstinio mokėjimo kortelės tinkamos.</translation>
<translation id="7887683347370398519">Patikrinkite kortelės saugos kodą (CVC) ir bandykite dar kartą</translation>
<translation id="79338296614623784">Įveskite tinkamą telefono numerį</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -836,7 +865,6 @@
<translation id="8041089156583427627">Siųsti atsiliepimą</translation>
<translation id="8041940743680923270">Naudoti visuotinį numatytąjį nustatymą (klausti)</translation>
<translation id="8088680233425245692">Nepavyko peržiūrėti straipsnio.</translation>
-<translation id="8089520772729574115">mažiau nei 1 MB</translation>
<translation id="8091372947890762290">Laukiama aktyvinimo serveryje</translation>
<translation id="8118489163946903409">Mokėjimo metodas</translation>
<translation id="8131740175452115882">Patvirtinti</translation>
@@ -848,13 +876,16 @@
<translation id="8194797478851900357">&amp;Anuliuoti perkėlimą</translation>
<translation id="8201077131113104583">Netinkamas plėtinio, kurio ID „<ph name="EXTENSION_ID" />“, atnaujinimo URL.</translation>
<translation id="8202097416529803614">Užsakymo suvestinė</translation>
+<translation id="8205463626947051446">Svetainėje bandoma rodyti nepageidaujamus skelbimus</translation>
<translation id="8218327578424803826">Priskirta vieta:</translation>
<translation id="8225771182978767009">Šį kompiuterį nustatęs asmuo pasirinko blokuoti šią svetainę.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Atidarykite puslapį naujame inkognito skirtuke</translation>
<translation id="8241707690549784388">Jūsų ieškomas puslapis ieškojo informacijos, kurią įvedėte. Grįžus į tą puslapį bet kokie jūsų atliekami veiksmai gali būti kartojami. Ar norite tęsti?</translation>
+<translation id="8241712895048303527">Blokuoti šioje svetainėje</translation>
<translation id="8249320324621329438">Paskutinį kartą gauta:</translation>
<translation id="8253091569723639551">Būtina pateikti atsiskaitymo adresą</translation>
-<translation id="8261506727792406068">Panaikinti</translation>
+<translation id="8261506727792406068">Ištrinti</translation>
<translation id="8289355894181816810">Jei nesate tikri, ką tai reiškia, susisiekite su tinklo administratoriumi.</translation>
<translation id="8293206222192510085">Pridėti žymę</translation>
<translation id="8294431847097064396">Šaltinis</translation>
@@ -862,7 +893,6 @@
<translation id="8308427013383895095">Vertimas nepavyko dėl tinklo ryšio problemos.</translation>
<translation id="8332188693563227489">Prieiga prie <ph name="HOST_NAME" /> atmesta</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="8344669043927012510">Atidarykite puslapį inkognito režimu (⇧ ⌘ N)</translation>
<translation id="8349305172487531364">Žymių juosta</translation>
<translation id="8363502534493474904">Išjungti lėktuvo režimą</translation>
<translation id="8364627913115013041">Nenustatyta.</translation>
@@ -872,6 +902,7 @@
<translation id="8398259832188219207">Strigčių ataskaita įkelta <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Strigtys (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Reikia du kartus įvesti tą pačią slaptafrazę.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nustatymai</translation>
<translation id="8433057134996913067">Tai atlikę atsijungsite nuo daugumos svetainių.</translation>
<translation id="8437238597147034694">&amp;Anuliuoti perkėlimą</translation>
@@ -879,8 +910,9 @@
<translation id="8483780878231876732">Jei norite naudoti korteles iš „Google“ paskyros, prisijunkite prie „Chrome“</translation>
<translation id="8488350697529856933">Taikoma</translation>
<translation id="8498891568109133222">Per ilgai laukta <ph name="HOST_NAME" /> atsako.</translation>
-<translation id="8532105204136943229">Gal. pab. metai</translation>
+<translation id="8503813439785031346">Vartotojo vardas</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="8571890674111243710">Puslapis verčiamas į <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Pridėti tel. nr.
@@ -888,6 +920,7 @@
<translation id="859285277496340001">Sertifikatas nenurodo mechanizmo, skirto patikrinti, ar jis buvo panaikintas.</translation>
<translation id="8620436878122366504">Jūsų tėvai dar jos nepatvirtino</translation>
<translation id="8647750283161643317">Viską nustatyti į numatytuosius nustatymus</translation>
+<translation id="8660471606262461360">Iš „Google Payments“</translation>
<translation id="8703575177326907206">Jūsų ryšys su <ph name="DOMAIN" /> nekoduotas.</translation>
<translation id="8718314106902482036">Mokėjimas neužbaigtas</translation>
<translation id="8725066075913043281">Bandyti dar kartą</translation>
@@ -915,6 +948,7 @@
<translation id="8903921497873541725">Artinti</translation>
<translation id="8931333241327730545">Ar norite išsaugoti šios kortelės informaciją „Google“ paskyroje?</translation>
<translation id="8932102934695377596">Jūsų laikrodis atsilieka</translation>
+<translation id="8938939909778640821">Tinkamos kredito ir išankstinio mokėjimo kortelės</translation>
<translation id="8971063699422889582">Baigėsi serverio sertifikato galiojimo laikas.</translation>
<translation id="8986494364107987395">Automatiškai siųsti naudojimo statistiką ir strigčių ataskaitas „Google“</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -944,7 +978,6 @@
<translation id="9169664750068251925">Visada blokuoti šioje svetainėje</translation>
<translation id="9170848237812810038">&amp;Atšaukti</translation>
<translation id="917450738466192189">Serverio sertifikatas negalioja.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}many{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ir dar <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Nepavyko pridėti straipsnio.</translation>
@@ -953,9 +986,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">CLEAR FORM</translation>
<translation id="939736085109172342">Naujas aplankas</translation>
-<translation id="941721044073577244">Panašu, kad neturite leidimo apsilankyti šioje svetainėje</translation>
<translation id="969892804517981540">Oficialiai pagaminta</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Nėra}=1{1 elementas}one{# elementas}few{# elementai}many{# elemento}other{# elementų}}</translation>
+<translation id="981121421437150478">Neprisijungus</translation>
<translation id="988159990683914416">Vykdymo programa sukurta</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_lv.xtb b/chromium/components/strings/components_strings_lv.xtb
index eeccee59448..766fb396fd6 100644
--- a/chromium/components/strings/components_strings_lv.xtb
+++ b/chromium/components/strings/components_strings_lv.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Aizveriet citas lietotnes</translation>
<translation id="1055184225775184556">&amp;Pievienošanas atsaukšana</translation>
<translation id="10614374240317010">Netiek saglabātas</translation>
+<translation id="1066396345355680611">Varat zaudēt piekļuvi aizsargātam saturam no vietnes <ph name="SITE" /> un dažām citām vietnēm.</translation>
<translation id="106701514854093668">Datora grāmatzīmes</translation>
<translation id="1074497978438210769">Nav droša</translation>
<translation id="1080116354587839789">Ietilpināt pēc platuma</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lasīšanas saraksts</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" /> notverts ziņojums par avāriju (vēl nav augšupielādēts vai ignorēts)</translation>
<translation id="1281526147609854549">Izsniedza <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Bloķēts bīstams saturs</translation>
<translation id="1285320974508926690">Nekad netulkot šo vietni</translation>
<translation id="129553762522093515">Nesen aizvērtas</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Dzēsiet sīkfailus<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Reģistrācijas domēns:</translation>
<translation id="1340482604681802745">Saņemšanas adrese</translation>
-<translation id="1344211575059133124">Šķiet, ka jums ir nepieciešama atļauja apmeklēt šo vietni</translation>
<translation id="1344588688991793829">Chromium automātiskās aizpildes iestatījumi...</translation>
<translation id="1348198688976932919">Vietnē, kas tiks atvērta, ir bīstamas lietotnes</translation>
<translation id="1374468813861204354">ieteikumus</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="ORGANIZATION" />identitāti<ph name="LOCALITY" />apstiprināja<ph name="ISSUER" /></translation>
<translation id="1426410128494586442">Jā</translation>
<translation id="1430915738399379752">Drukāt</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}zero{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Rādīt saglabātu (t.i., novecojušu) šīs lapas versiju.</translation>
<translation id="1517433312004943670">Jānorāda tālruņa numurs</translation>
+<translation id="1517500485252541695">Atbalstītās kredītkartes un debetkartes</translation>
<translation id="1519264250979466059">Būvējuma datums</translation>
+<translation id="1527263332363067270">Tiek gaidīta savienojuma izveide...</translation>
<translation id="153384715582417236">Pagaidām tas arī viss!</translation>
<translation id="1549470594296187301">Lai izmantotu šo funkciju, ir jābūt iespējotai valodai JavaScript.</translation>
<translation id="1555130319947370107">Zila</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Lejupielādēt lapu vēlāk</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Obligātais lauks</translation>
<translation id="187918866476621466">Atvērt sākumlapas</translation>
<translation id="1883255238294161206">Sakļaut sarakstu</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}zero{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtrēšana</translation>
+<translation id="1916770123977586577">Lai lietotu atjauninātos iestatījumus šai vietnei, atkārtoti ielādējiet šo lapu</translation>
+<translation id="1919345977826869612">Reklāmas</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Nav}=1{1 vietne}zero{# vietnes}one{# vietne}other{# vietnes}}</translation>
<translation id="194030505837763158">Apmeklējiet vietni <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Atbalstītās priekšapmaksas kartes</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> grāmatzīmes</translation>
<translation id="1973335181906896915">Radās serializēšanas kļūda.</translation>
<translation id="1974060860693918893">Papildu</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP kļūda</translation>
<translation id="2270484714375784793">Tālruņa numurs</translation>
<translation id="2282872951544483773">Nepieejamie eksperimenti</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> vienums}zero{<ph name="ITEM_COUNT" /> vienumi}one{<ph name="ITEM_COUNT" /> vienums}other{<ph name="ITEM_COUNT" /> vienumi}}</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>
<translation id="2337852623177822836">Iestatījumu kontrolē jūsu administrators</translation>
<translation id="2354001756790975382">Citas grāmatzīmes</translation>
<translation id="2354430244986887761">Izmantojot Google drošo pārlūkošanu, nesen <ph name="BEGIN_LINK" />tika atrastas kaitīgas lietotnes<ph name="END_LINK" /> vietnē <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Turpināt</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> notverts ziņojums par avāriju; netika lejupielādēts</translation>
<translation id="2367567093518048410">Līmenis</translation>
-<translation id="237718015863234333">Nav pieejamas alternatīvas lietotāja saskarnes</translation>
<translation id="2384307209577226199">Uzņēmuma noklusējuma politika</translation>
<translation id="2386255080630008482">Servera sertifikāts ir atsaukts.</translation>
<translation id="2392959068659972793">Rādīt politikas, kuru vērtība nav iestatīta</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Ja šī izvēles rūtiņa ir atzīmēta, pārlūks Chromium saglabās jūsu kartes informāciju šajā ierīcē, lai nodrošinātu ātrāku veidlapu aizpildi.</translation>
<translation id="2498091847651709837">Skenēt jaunu karti</translation>
<translation id="2501278716633472235">Doties atpakaļ</translation>
+<translation id="2503184589641749290">Atbalstītās debetkartes un priekšapmaksas kartes</translation>
<translation id="2515629240566999685">Pārbaudiet signālu savā apkaimē.</translation>
-<translation id="2516305470678292029">Alternatīvas lietotāja saskarnes</translation>
<translation id="2539524384386349900">Noteikt</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> nosūtīja nederīgu atbildi.</translation>
<translation id="2556876185419854533">&amp;Labojuma atsaukšana</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Nosūtīšanas veids</translation>
<translation id="277499241957683684">Trūkst ierīces ieraksta.</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>
<translation id="2799020568854403057">Šajā vietnē, kas tiks atvērta, ir kaitīgas lietotnes</translation>
<translation id="2803306138276472711">Google drošās pārlūkošanas tehnoloģija vietnē <ph name="SITE" /> nesen <ph name="BEGIN_LINK" />konstatēja ļaunprātīgu programmatūru<ph name="END_LINK" />. Vietnes, kuras parasti ir drošas, dažkārt tiek inficētas ar ļaunprātīgu programmatūru.</translation>
<translation id="2824775600643448204">Adreses un meklēšanas josla</translation>
<translation id="2826760142808435982">Savienojums ir šifrēts un autentificēts, izmantojot <ph name="CIPHER" />, un tajā tiek izmantots <ph name="KX" /> kā atslēgu apmaiņas mehānisms.</translation>
<translation id="2835170189407361413">Notīrīt veidlapu</translation>
+<translation id="2851634818064021665">Jums nepieciešama atļauja, lai apmeklētu šo vietni</translation>
<translation id="2856444702002559011">Iespējams, uzbrucēji mēģina nozagt jūsu informāciju no vietnes <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (piemēram, paroles, ziņojumus vai kredītkaršu datus). <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairāk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2889159643044928134">Neielādēt atkārtoti</translation>
-<translation id="2900469785430194048">Mēģinot parādīt šo tīmekļa lapu, pārlūka Google Chrome atmiņā nepietika vietas.</translation>
<translation id="2909946352844186028">Konstatētas tīkla izmaiņas.</translation>
<translation id="2916038427272391327">Aizveriet citas programmas</translation>
<translation id="2922350208395188000">Servera sertifikātu nevar pārbaudīt.</translation>
<translation id="2928905813689894207">Norēķinu adrese</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}zero{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tās drošības sertifikāts ir saistīts ar domēnu <ph name="DOMAIN2" />. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ir ļaunprātīgi izmantojis jūsu savienojumu.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Ja šī opcija ir iespējota, Chromium saglabās jūsu kartes informāciju šajā ierīcē, lai nodrošinātu ātrāku veidlapu aizpildi.</translation>
<translation id="2985398929374701810">Ievadiet derīgu adresi</translation>
<translation id="2986368408720340940">Šis saņemšanas veids nav pieejams. Izmēģiniet citu veidu.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Lapas, ko skatāt inkognito režīma cilnēs, nebūs redzamas pārlūka vēsturē, sīkfailu krātuvē vai meklēšanas vēsturē, kad aizvērsiet visas inkognito režīma cilnes. Tomēr tiks saglabāti visi lejupielādētie faili un izveidotās grāmatzīmes.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Sala</translation>
-<translation id="317583078218509884">Jaunie vietnes atļauju iestatījumi stāsies spēkā, atkārtoti ielādējot šo lapu.</translation>
<translation id="3176929007561373547">Pārbaudiet starpniekservera iestatījumus vai sazinieties ar tīkla administratoru, lai
pārliecinātos, vai starpniekserveris darbojas. Ja uzskatāt, ka jums nav jāizmanto
starpniekserveris,
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Atveriet lapu inkognito režīmā</translation>
<translation id="320323717674993345">Atcelt maksājumu</translation>
<translation id="3207960819495026254">Atzīmēts kā grāmatzīme</translation>
<translation id="3225919329040284222">Serveris uzrādīja sertifikātu, kas neatbilst iebūvētajām cerībām. Šīs cerības ir ietvertas konkrētām, augstas drošības vietnēm, lai aizsargātu jūs.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Pārkārtošanas atsaukšana</translation>
<translation id="3282497668470633863">Pievienot vārdu un uzvārdu uz kartes</translation>
<translation id="3286538390144397061">Restartēt tūlīt</translation>
+<translation id="3287510313208355388">Lejupielādēt, kad pieejama tiešsaiste</translation>
<translation id="3303855915957856445">Netika atrasts neviens meklēšanas rezultāts.</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Pirmsielādes intervāls:</translation>
<translation id="3462200631372590220">Slēpt papildu informāciju</translation>
<translation id="3467763166455606212">Jānorāda kartes īpašnieka vārds un uzvārds</translation>
-<translation id="3478058380795961209">Der. term. mēn.</translation>
<translation id="3479539252931486093">Vai tas bija negaidīti? <ph name="BEGIN_LINK" />Informējiet mūs<ph name="END_LINK" />!</translation>
<translation id="3479552764303398839">Vēlāk</translation>
<translation id="3498215018399854026">Mēs nevarējām sasniegt jūsu māti/tēvu. Lūdzu, mēģiniet vēlreiz.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Lūdzu, mainiet 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; <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairāk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Tiek pieņemtas kredītkartes un priekšapmaksas kartes.</translation>
<translation id="3582930987043644930">Pievienojiet vārdu.</translation>
<translation id="3583757800736429874">&amp;Pārvietošanas atsaukuma atcelšana</translation>
<translation id="3586931643579894722">Slēpt detaļas</translation>
@@ -357,10 +360,10 @@
<translation id="3655670868607891010">Ja šo redzat bieži, izmēģiniet šos <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Pārskatīšana</translation>
<translation id="3678029195006412963">Pieprasījumu nevarēja parakstīt.</translation>
+<translation id="3678529606614285348">Atvērt lapu jaunā inkognito režīma logā (Ctrl+Shift+N)</translation>
<translation id="3679803492151881375">Avāriju pārskats tverts: <ph name="CRASH_TIME" />; augšupielādēts: <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Sertifikāta informācija</translation>
<translation id="3690164694835360974">Pieteikšanās nav droša</translation>
-<translation id="3693415264595406141">Parole:</translation>
<translation id="3704609568417268905"><ph name="TIME" />, <ph name="BOOKMARKED" />, <ph name="TITLE" />, <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Notiek ielāde...</translation>
<translation id="3712624925041724820">Nav pietiekami daudz licenču.</translation>
@@ -372,6 +375,7 @@
<translation id="3748148204939282805">Uzbrucēji vietnē <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> var mudināt jūs veikt bīstamas darbības, piemēram, instalēt programmatūru vai atklāt personas informāciju (piemēram, paroles, tālruņa numurus vai kredītkaršu datus). <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairāk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="375403751935624634">Tulkojums neizdevās servera kļūdas dēļ.</translation>
<translation id="3759461132968374835">Pēdējā laikā neesat ziņojis par avārijām. Šeit nebūs redzamas avārijas, kas radās laikā, kad avāriju pārskatu izveide bija atspējota.</translation>
+<translation id="3765032636089507299">Drošās pārlūkošanas lapa pašlaik nav pieejama</translation>
<translation id="3778403066972421603">Vai vēlaties saglabāt šo karti savā Google kontā un šajā ierīcē?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Derīguma termiņš: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -384,8 +388,10 @@
<translation id="3886446263141354045">Pieprasījums piekļūt šai vietnei ir nosūtīts lietotājam <ph name="NAME" /></translation>
<translation id="3890664840433101773">E-pasta adreses pievienošana</translation>
<translation id="3901925938762663762">Kartes derīguma termiņš ir beidzies.</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Augšupielādētā avārijas ziņojuma ID <ph name="CRASH_ID" /> (vietējais avārijas ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tā drošības sertifikātā nav norādīti temata citi nosaukumi. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ļaunprātīgi izmanto jūsu savienojumu.</translation>
+<translation id="3949601375789751990">Šeit ir redzama jūsu pārlūkošanas vēsture.</translation>
<translation id="3963721102035795474">Lasītāja režīms</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Nav}=1{No 1 vietnes }zero{No # vietnēm }one{No # vietnes }other{No # vietnēm }}</translation>
<translation id="397105322502079400">Aprēķina...</translation>
@@ -414,6 +420,8 @@
<translation id="4165986682804962316">Vietnes iestatījumi</translation>
<translation id="4169947484918424451">Vai vēlaties, lai Chromium saglabātu šo karti?</translation>
<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="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>
@@ -437,12 +445,12 @@
<translation id="4356973930735388585">Šajā vietnē esošie uzbrucēji jūsu datorā var mēģināt instalēt bīstamas programmas, kuras var nozagt vai dzēst jūsu informāciju (piemēram, fotoattēlus, paroles, ziņojumus un informāciju par kredītkartēm).</translation>
<translation id="4372948949327679948">Tika gaidīta vērtība <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Jūs mēģinājāt sasniegt <ph name="DOMAIN" />, bet izdevējs atsauca servera uzrādīto sertifikātu. Tas nozīmē, ka servera uzrādītie drošības akreditācijas dati itin nemaz nav uzticami. Iespējams, jūs sazināties ar uzbrucēju.</translation>
-<translation id="4381091992796011497">Lietotājvārds:</translation>
<translation id="4394049700291259645">Atspējot</translation>
<translation id="4406896451731180161">meklēšanas rezultāti</translation>
<translation id="4424024547088906515">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tās drošības sertifikāts netiek uzskatīts par uzticamu pārlūkā Chrome. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ir ļaunprātīgi izmantojis jūsu savienojumu.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> nepieņēma jūsu pieteikšanās sertifikātu, vai arī pieteikšanās sertifikāts netika iesniegts.</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="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>
@@ -454,12 +462,14 @@
<translation id="4587425331216688090">Vai noņemt adresi no pārlūka Chrome?</translation>
<translation id="4592951414987517459">Savienojums ar domēnu <ph name="DOMAIN" /> ir šifrēts, izmantojot mūsdienīgu šifra komplektu.</translation>
<translation id="4594403342090139922">&amp;Dzēšanas atsaukšana</translation>
+<translation id="4611292653554630842">Pieteikties</translation>
<translation id="4619615317237390068">Cilnes no citām ierīcēm</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tās drošības sertifikātā ir kļūdas. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ir ļaunprātīgi izmantojis jūsu savienojumu.</translation>
<translation id="4690462567478992370">Pārtraukt nederīga sertifikāta izmantošanu</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Savienojums tika pārtraukts</translation>
+<translation id="471880041731876836">Jums nav atļaujas apmeklēt šo vietni</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Palaist Windows tīkla diagnostiku<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Atkārtoti ielādēt politikas</translation>
<translation id="4728558894243024398">Platforma</translation>
@@ -481,14 +491,15 @@
<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="4863764087567530506">Ar šo saturu jūs var maldināt, lai jūs instalētu programmatūru vai atklātu personas informāciju. <ph name="BEGIN_LINK" />Tāpat rādīt<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Meklēšanas vēsture</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Nepieciešama autentifikācija</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{un vēl 1 tīmekļa lapa}zero{un vēl # tīmekļa lapas}one{un vēl # tīmekļa lapa}other{un vēl # tīmekļa lapas}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Šī lapa ir tulkota no nezināmas valodas valodā: <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Maksājums</translation>
<translation id="4926049483395192435">Jābūt norādītai.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Darbības</translation>
<translation id="4958444002117714549">Izvērst sarakstu</translation>
<translation id="4974590756084640048">Atkārtoti iespējot brīdinājumus</translation>
@@ -504,6 +515,7 @@
<translation id="5045550434625856497">Nepareiza parole</translation>
<translation id="5056549851600133418">Jums piemeklēti raksti</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Pārbaudiet starpniekservera adresi<ph name="END_LINK" />.</translation>
+<translation id="5086888986931078152">Varat zaudēt piekļuvi aizsargātam saturam no noteiktām vietnēm.</translation>
<translation id="5087286274860437796">Servera sertifikāts šobrīd nav derīgs.</translation>
<translation id="5087580092889165836">Pievienot karti</translation>
<translation id="5089810972385038852">Štats</translation>
@@ -511,18 +523,27 @@
<translation id="5095208057601539847">Province</translation>
<translation id="5115563688576182185">(64 bitu)</translation>
<translation id="5141240743006678641">Šifrēt sinhronizētās paroles, izmantojot Google akreditācijas datus</translation>
-<translation id="514421653919133810">Atveriet lapu inkognito režīmā (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Politikas atbildē ir kļūdas kods.</translation>
+<translation id="5159010409087891077">Atvērt lapu jaunā inkognito režīma logā (⇧⌘N)</translation>
<translation id="5171045022955879922">Meklējiet vai ievadiet URL.</translation>
<translation id="5172758083709347301">Ierīce</translation>
<translation id="5179510805599951267">Vai nav valodā: <ph name="ORIGINAL_LANGUAGE" />? Ziņot par šo kļūdu</translation>
-<translation id="5181140330217080051">Notiek lejupielāde</translation>
<translation id="5190835502935405962">Grāmatzīmju josla</translation>
<translation id="5199729219167945352">Eksperimenti</translation>
<translation id="5205222826937269299">Jānorāda vārds vai nosaukums.</translation>
<translation id="5222812217790122047">Jānorāda e-pasta adrese.</translation>
<translation id="5251803541071282808">Mākonis</translation>
<translation id="5277279256032773186">Vai izmantojat Chrome darbā? Uzņēmumi var pārvaldīt darbinieku Chrome iestatījumus. Uzziniet vairāk.</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Lai īslaicīgi atspējotu programmatūru un piekļūtu tīmeklim, veiciet tālāk norādītās darbības. Nepieciešamas administratora privilēģijas.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Noklikšķiniet uz pogas <ph name="BEGIN_BOLD" />Sākums<ph name="END_BOLD" /> un pēc tam atrodiet un atlasiet opciju <ph name="BEGIN_BOLD" />Skatīt vietējos pakalpojumus<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Atlasiet vienumu <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Slejā <ph name="BEGIN_BOLD" />Startēšanas veids<ph name="END_BOLD" /> atlasiet <ph name="BEGIN_BOLD" />Atspējots<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Slejā <ph name="BEGIN_BOLD" />Pakalpojuma statuss<ph name="END_BOLD" /> noklikšķiniet uz <ph name="BEGIN_BOLD" />Apturēt<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Noklikšķiniet uz <ph name="BEGIN_BOLD" />Lietot<ph name="END_BOLD" /> un pēc tam — uz <ph name="BEGIN_BOLD" />Labi<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Lai uzzinātu, kā neatgriezeniski noņemt programmatūru no datora, apmeklējiet <ph name="BEGIN_LEARN_MORE_LINK" />Chrome palīdzības centru<ph name="END_LEARN_MORE_LINK" />.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Savienojums ar šo vietni nav privāts. Lai izietu no virtuālās realitātes režīma, noņemiet austiņas un nospiediet Atpakaļ.</translation>
<translation id="5299298092464848405">Parsējot politiku, radās kļūda.</translation>
<translation id="5308689395849655368">Avāriju pārskatu izveide ir atspējota.</translation>
@@ -539,9 +560,10 @@
<translation id="5439770059721715174">Šeit tika atklāta shēmas validēšanas kļūda: <ph name="ERROR_PATH" />. Kļūdas ziņojums: <ph name="ERROR" />.</translation>
<translation id="5452270690849572955">Šo vietnes <ph name="HOST_NAME" /> lapu nevar atrast</translation>
<translation id="5455374756549232013">Politikas laikspiedols nav derīgs.</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> no <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Nav derīgi</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}zero{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Atcelt labojuma atsaukšanu</translation>
+<translation id="5481076368049295676">Šis saturs jūsu ierīcē var mēģināt instalēt bīstamu programmatūru, kas var nozagt vai izdzēst jūsu informāciju. <ph name="BEGIN_LINK" />Tāpat rādīt<ph name="END_LINK" />.</translation>
<translation id="54817484435770891">Derīgas adreses pievienošana</translation>
<translation id="5492298309214877701">Šai vietnei uzņēmuma, organizācijas vai skolas iekštīklā ir tāds pats URL kā ārējai vietnei.
<ph name="LINE_BREAK" />
@@ -553,6 +575,7 @@
<translation id="5540224163453853">Pieprasīto rakstu nevarēja atrast.</translation>
<translation id="5544037170328430102">Vietnē <ph name="SITE" /> iegultā lapā ir rakstīts:</translation>
<translation id="5556459405103347317">Pārlādēt</translation>
+<translation id="5560088892362098740">Derīguma termiņš</translation>
<translation id="5565735124758917034">Aktīvs</translation>
<translation id="5571083550517324815">Nevar saņemt sūtījumu šajā adresē. Atlasiet citu adresi.</translation>
<translation id="5572851009514199876">Lūdzu, palaidiet pārlūku Chrome un pierakstieties tajā, lai pārlūkā Chrome varētu pārbaudīt, vai jums ir atļauja piekļūt šai vietnei.</translation>
@@ -567,12 +590,13 @@
<translation id="5629630648637658800">Neizdevās ielādēt politikas iestatījumus.</translation>
<translation id="5631439013527180824">Ierīces pārvaldības marķieris nav derīgs.</translation>
<translation id="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="5646376287012673985">Atrašanās vieta</translation>
<translation id="5659593005791499971">E-pasts</translation>
<translation id="5669703222995421982">Saņemiet personalizētu saturu</translation>
<translation id="5675650730144413517">Šī lapa nedarbojas</translation>
<translation id="5710435578057952990">Tīmekļa vietnes identitāte nav apstiprināta.</translation>
-<translation id="5713016350996637505">Maldinošs saturs bloķēts</translation>
+<translation id="5719499550583120431">Tiek pieņemtas priekšapmaksas kartes.</translation>
<translation id="5720705177508910913">Pašreizējais lietotājs</translation>
<translation id="5732392974455271431">Lai atbloķētu, vērsieties pie vecākiem</translation>
<translation id="5763042198335101085">Ievadiet derīgu e-pasta adresi</translation>
@@ -584,15 +608,14 @@
<translation id="5803412860119678065">Vai vēlaties aizpildīt lauku ar informāciju “<ph name="CARD_DETAIL" />”?</translation>
<translation id="5810442152076338065">Savienojums ar domēnu <ph name="DOMAIN" /> ir šifrēts, izmantojot novecojušu šifra komplektu.</translation>
<translation id="5813119285467412249">&amp;Pievienošanas atsaukuma atcelšana</translation>
-<translation id="5814352347845180253">Varat zaudēt piekļuvi maksas saturam no vietnes <ph name="SITE" /> un dažām citām vietnēm.</translation>
<translation id="5838278095973806738">Neievadiet šajā vietnē sensitīvu informāciju (piemēram, paroles vai kredītkartes), jo to var nozagt uzbrucēji.</translation>
<translation id="5869405914158311789">Šī vietne nav sasniedzama</translation>
<translation id="5869522115854928033">Saglabātās paroles</translation>
<translation id="5872918882028971132">Vecāku ieteikumi</translation>
+<translation id="5893752035575986141">Tiek pieņemtas kredītkartes.</translation>
<translation id="5901630391730855834">Dzeltena</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="5926846154125914413">Varat zaudēt piekļuvi maksas saturam no noteiktām vietnēm.</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="5967867314010545767">Noņemt no vēstures</translation>
<translation id="5975083100439434680">Tālināt</translation>
@@ -608,6 +631,7 @@
<translation id="6040143037577758943">Aizvērt</translation>
<translation id="6042308850641462728">Vairāk</translation>
<translation id="6047233362582046994">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 lietotnes.</translation>
+<translation id="6047927260846328439">Ar šo saturu jūs var maldināt un panākt, ka instalējat programmatūru vai atklājat personas informāciju. <ph name="BEGIN_LINK" />Tāpat rādīt<ph name="END_LINK" />.</translation>
<translation id="6051221802930200923">Pašlaik nevarat apmeklēt vietni <ph name="SITE" />, jo tajā tiek izmantota sertifikātu piespraušana. Tā kā tīkla kļūdas un uzbrukumi parasti ir īslaicīgi, visticamāk, šī lapa vēlāk darbosies.</translation>
<translation id="6060685159320643512">Esiet uzmanīgs! Šie eksperimenti var jums kaitēt</translation>
<translation id="6080696365213338172">Jūs esat piekļuvis saturam, izmantojot administratora izsniegtu sertifikātu. Datus, kurus sniedzat domēnā <ph name="DOMAIN" />, var pārtvert jūsu administrators.</translation>
@@ -621,7 +645,6 @@
<translation id="6165508094623778733">Uzziniet vairāk</translation>
<translation id="6169916984152623906">Tagad varat privāti pārlūkot saturu, un citas personas, kas izmanto šo ierīci, nevarēs redzēt jūsu darbības. Tomēr lejupielādes un grāmatzīmes tiks saglabātas.</translation>
<translation id="6177128806592000436">Savienojums ar šo vietni nav drošs.</translation>
-<translation id="6184817833369986695">(personu grupa: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Interneta savienojuma pārbaude</translation>
<translation id="6218753634732582820">Vai noņemt adresi no pārlūka Chromium?</translation>
<translation id="6221345481584921695">Google drošās pārlūkošanas tehnoloģija vietnē <ph name="SITE" /> nesen <ph name="BEGIN_LINK" />konstatēja ļaunprātīgu programmatūru<ph name="END_LINK" />. Vietnes, kuras parasti ir drošas, dažkārt tiek inficētas ar ļaunprātīgu programmatūru. Ļaunprātīgā satura avots ir <ph name="SUBRESOURCE_HOST" /> — plaši zināms ļaunprātīgās programmatūras izplatītājs.</translation>
@@ -640,13 +663,14 @@
<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>
<translation id="6387478394221739770">Vai jūs interesē jaunas Chrome funkcijas? Izmēģiniet mūsu Beta versiju, kas pieejama vietnē chrome.com/beta.</translation>
-<translation id="6389758589412724634">Mēģinot parādīt šo tīmekļa lapu, pārlūka Chromium atmiņā nepietika vietas.</translation>
+<translation id="6397451950548600259">Programmatūra jūsu datorā, kuras dēļ pārlūkā Chrome nevar izveidot drošu tīmekļa savienojumu</translation>
<translation id="6404511346730675251">Rediģēt grāmatzīmi</translation>
<translation id="6410264514553301377">Ievadiet kredītkartes <ph name="CREDIT_CARD" /> derīguma termiņu un CVC.</translation>
<translation id="6414888972213066896">Jūs lūdzāt vienam no vecākiem atļauju apmeklēt šo vietni</translation>
@@ -661,6 +685,7 @@
<translation id="647261751007945333">Ierīces politikas</translation>
<translation id="6477321094435799029">Pārlūkā Chrome tika noteikts, ka šajā lapā ir neparasts kods. Lapa tika bloķēta, lai aizsargātu jūsu personas informāciju (piemēram, paroles, tālruņa numurus un kredītkaršu informāciju).</translation>
<translation id="6489534406876378309">Sākt avāriju datu augšupielādi</translation>
+<translation id="6507833130742554667">Tiek pieņemtas kredītkartes un debetkartes.</translation>
<translation id="6508722015517270189">Restartējiet pārlūku Chrome</translation>
<translation id="6529602333819889595">&amp;Dzēšanas atsaukuma atcelšana</translation>
<translation id="6534179046333460208">Fiziskā tīmekļa ieteikumi</translation>
@@ -669,13 +694,13 @@
<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="657639383826808334">Ar šo saturu var jūs maldināt, lai jūs instalētu bīstamu programmatūru savā ierīcē, vai var tikt nozagta vai izdzēsta jūsu informācija. <ph name="BEGIN_LINK" />Tāpat rādīt<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Šifrēšanas opcijas</translation>
<translation id="662080504995468778">Palikt</translation>
<translation id="6626291197371920147">Derīga kartes numura pievienošana</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> meklēšana</translation>
<translation id="6630809736994426279">Uzbrucēji vietnē <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> var mēģināt jūsu Mac 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="6644283850729428850">Šī politika ir izbeigta.</translation>
+<translation id="6657585470893396449">Parole</translation>
<translation id="6671697161687535275">Vai noņemt veidlapas ieteikumu no pārlūka Chromium?</translation>
<translation id="6685834062052613830">Izrakstieties un pabeidziet iestatīšanu</translation>
<translation id="6710213216561001401">Iepriekšējais</translation>
@@ -706,7 +731,6 @@
<translation id="6970216967273061347">Rajons</translation>
<translation id="6973656660372572881">Ir norādīti gan fiksēti starpniekserveri, gan .pac skripta URL.</translation>
<translation id="6989763994942163495">Rādīt papildu iestatījumus...</translation>
-<translation id="7000990526846637657">Netika atrasts neviens vēstures ieraksts.</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Jūsu Google kontam var būt cita veida pārlūkošanas vēstures dati vietnē <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />.</translation>
<translation id="7029809446516969842">Paroles</translation>
@@ -721,7 +745,9 @@
<translation id="7129409597930077180">Nevar nosūtīt uz šo adresi. Atlasiet citu adresi.</translation>
<translation id="7138472120740807366">Piegādes veids</translation>
<translation id="7139724024395191329">Emirāts</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}zero{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Maksājums nav drošs</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}zero{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Restartēt tūlīt</translation>
<translation id="7180611975245234373">Atsvaidzināt</translation>
<translation id="7182878459783632708">Nav iestatīta neviena politika</translation>
@@ -762,6 +788,7 @@
<translation id="7514365320538308">Lejupielādēt</translation>
<translation id="7518003948725431193">Šādā tīmekļa adresē netika atrasta neviena tīmekļa lapa: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Jūsu savienojums ar šo vietni nav privāts.</translation>
<translation id="7535087603100972091">Vērtība</translation>
<translation id="7537536606612762813">Obligāti</translation>
<translation id="7542403920425041731">Pēc apstiprinājuma jūsu kartes informācija tiks kopīgota ar šo vietni.</translation>
@@ -771,7 +798,6 @@
<translation id="7552846755917812628">Izmantojiet tālāk sniegtos padomus.</translation>
<translation id="7554791636758816595">Jauna cilne</translation>
<translation id="7567204685887185387">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tās drošības sertifikāts, iespējams, ir izveidots krāpnieciski. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ir ļaunprātīgi izmantojis jūsu savienojumu.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}zero{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Šī lapa ir rakstīta<ph name="ORIGINAL_LANGUAGE" />valodā. Vai vēlaties to tulkot?</translation>
<translation id="7569952961197462199">Vai noņemt kredītkarti no pārlūka Chrome?</translation>
<translation id="7569983096843329377">Melna</translation>
@@ -798,9 +824,11 @@
<translation id="7714464543167945231">Sertifikāts</translation>
<translation id="7716147886133743102">Bloķēja jūsu administrators</translation>
<translation id="7716424297397655342">Šo vietni nevar ielādēt no kešatmiņas</translation>
+<translation id="774634243536837715">Bloķēts bīstams saturs</translation>
<translation id="7752995774971033316">Netiek pārvaldīts</translation>
<translation id="7755287808199759310">Lai atbloķētu, vēsieties pie vecāka</translation>
<translation id="7758069387465995638">Iespējams, savienojumu ir bloķējis ugunsmūris vai pretvīrusu programmatūra.</translation>
+<translation id="7759163816903619567">Parādāmais domēns:</translation>
<translation id="7761701407923456692">Servera sertifikāts neatbilst URL.</translation>
<translation id="7763386264682878361">Maksājumu manifestu parsētājs</translation>
<translation id="7764225426217299476">Pievienot adresi</translation>
@@ -815,6 +843,7 @@
<translation id="7815407501681723534">Meklējot pēc virknes “<ph name="SEARCH_STRING" />”, tika atrasti <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /></translation>
<translation id="785549533363645510">Tomēr jūs neesat neredzams. Pārlūkojot inkognito režīmā, jūsu pārlūkošanas darbības netiek slēptas no jūsu darba devēja, interneta pakalpojumu sniedzēja vai apmeklētajām vietnēm.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" />: <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Tiek pieņemtas debetkartes un priekšapmaksas kartes.</translation>
<translation id="7887683347370398519">Pārbaudiet CVC kodu un mēģiniet vēlreiz.</translation>
<translation id="79338296614623784">Ievadiet derīgu tālruņa numuru</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -834,7 +863,6 @@
<translation id="8041089156583427627">Sūtīt atsauksmes</translation>
<translation id="8041940743680923270">Izmantot globālo noklusējumu (Vaicāt)</translation>
<translation id="8088680233425245692">Rakstu neizdevās skatīt.</translation>
-<translation id="8089520772729574115">mazāk nekā 1 MB</translation>
<translation id="8091372947890762290">Aktivizācija vēl nav apstiprināta serverī.</translation>
<translation id="8118489163946903409">Maksājuma veids</translation>
<translation id="8131740175452115882">Apstiprināt</translation>
@@ -846,10 +874,13 @@
<translation id="8194797478851900357">&amp;Pārvietošanas atsaukšana</translation>
<translation id="8201077131113104583">Nederīgs atjaunināšanas URL paplašinājumam ar ID “<ph name="EXTENSION_ID" />”.</translation>
<translation id="8202097416529803614">Pasūtījuma kopsavilkums</translation>
+<translation id="8205463626947051446">Vietnē tiek rādītas traucējošas reklāmas.</translation>
<translation id="8218327578424803826">Piešķirtā atrašanās vieta:</translation>
<translation id="8225771182978767009">Persona, kura iestatīja šo datoru, izvēlējās bloķēt šo vietni.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Atvērt lapu jaunā inkognito režīma cilnē</translation>
<translation id="8241707690549784388">Lapa, ko meklējāt, izmantoja jūsu ievadīto informāciju. Atgriešanās lapā var radīt jebkuras jūsu darbības atkārtojumu. Vai vēlaties turpināt?</translation>
+<translation id="8241712895048303527">Bloķēt šajā vietnē</translation>
<translation id="8249320324621329438">Pēdējās pirmsielādes laiks:</translation>
<translation id="8253091569723639551">Jānorāda norēķinu adrese.</translation>
<translation id="8261506727792406068">Dzēst</translation>
@@ -860,7 +891,6 @@
<translation id="8308427013383895095">Tulkošana neizdevās, jo radās problēma ar tīkla savienojumu.</translation>
<translation id="8332188693563227489">Piekļuve vietnei <ph name="HOST_NAME" /> tika noraidīta</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="8344669043927012510">Atveriet lapas inkognito režīmā (⇧⌘N)</translation>
<translation id="8349305172487531364">Grāmatzīmju josla</translation>
<translation id="8363502534493474904">Izslēdziet lidojuma režīmu.</translation>
<translation id="8364627913115013041">Nav iestatīta.</translation>
@@ -870,6 +900,7 @@
<translation id="8398259832188219207">Avāriju pārskats augšupielādēts: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Avārijas (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Viena un tā pati ieejas frāze jāievada divreiz.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Iestatījumi</translation>
<translation id="8433057134996913067">Šādi tiksiet izrakstīts no lielākās daļas vietņu.</translation>
<translation id="8437238597147034694">&amp;Atsaukt pārvietošanu</translation>
@@ -877,8 +908,9 @@
<translation id="8483780878231876732">Lai izmantotu kartes no sava Google konta, pierakstieties pārlūkā Chrome!</translation>
<translation id="8488350697529856933">Attiecas uz</translation>
<translation id="8498891568109133222">Vietne <ph name="HOST_NAME" /> pārāk ilgi nereaģēja.</translation>
-<translation id="8532105204136943229">Der. term. gads</translation>
+<translation id="8503813439785031346">Lietotājvārds</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="8571890674111243710">Notiek lapas tulkošana uz <ph name="LANGUAGE" /> valodu...</translation>
<translation id="858637041960032120">Piev. tālr. nr.
@@ -886,6 +918,7 @@
<translation id="859285277496340001">Sertifikāts nenorāda mehānismu, ar kuru pārbaudīt, vai tas nav atsaukts.</translation>
<translation id="8620436878122366504">Jūsu vecāki vēl nav to apstiprinājuši</translation>
<translation id="8647750283161643317">Atiestatīt visiem to noklusējuma iestatījumus</translation>
+<translation id="8660471606262461360">No Google Payments</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="8725066075913043281">Mēģināt vēlreiz</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Tuvināt</translation>
<translation id="8931333241327730545">Vai vēlaties saglabāt šo karti savā Google kontā?</translation>
<translation id="8932102934695377596">Norādītais laiks ir pārāk tālu pagātnē</translation>
+<translation id="8938939909778640821">Atbalstītās kredītkartes un priekšapmaksas kartes</translation>
<translation id="8971063699422889582">Servera sertifikātam ir beidzies derīguma termiņš.</translation>
<translation id="8986494364107987395">Automātiski sūtīt lietošanas statistiku un avāriju pārskatus uzņēmumam Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Vienmēr bloķēt šajā vietnē</translation>
<translation id="9170848237812810038">&amp;Atsaukt</translation>
<translation id="917450738466192189">Servera sertifikāts ir nederīgs.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}zero{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> un vēl <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Rakstu neizdevās pievienot.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">NOTĪRĪT VEIDLAPU</translation>
<translation id="939736085109172342">Jauna mape</translation>
-<translation id="941721044073577244">Šķiet, ka jums nav atļaujas apmeklēt šo vietni</translation>
<translation id="969892804517981540">Oficiālā Uzbūve</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Nav}=1{1 vienums}zero{# vienumi}one{# vienums}other{# vienumi}}</translation>
+<translation id="981121421437150478">Bezsaistē</translation>
<translation id="988159990683914416">Attīstītāja konstrukcija</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ml.xtb b/chromium/components/strings/components_strings_ml.xtb
index fffe401c9f8..69d7d4e6469 100644
--- a/chromium/components/strings/components_strings_ml.xtb
+++ b/chromium/components/strings/components_strings_ml.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">മറ്റ് ആപ്പുകൾ അടയ്‌ക്കുക</translation>
<translation id="1055184225775184556">&amp;ചേർക്കുന്നത് പഴയപടിയാക്കുക</translation>
<translation id="10614374240317010">ഒരിക്കലും സംരക്ഷിച്ചില്ല</translation>
+<translation id="1066396345355680611">നിങ്ങൾക്ക്, <ph name="SITE" /> എന്ന സൈറ്റിൽ നിന്നും മറ്റുസൈറ്റുകളിൽ നിന്നുമുള്ള സംരക്ഷിത ഉള്ളടക്കത്തിലേക്കുള്ള ആക്‌സസ് നഷ്‌ടമായേക്കാം.</translation>
<translation id="106701514854093668">ഡെസ്‌ക്‌ടോപ്പ് ബുക്ക്‌മാർക്കുകൾ</translation>
<translation id="1074497978438210769">സുരക്ഷിതമല്ല</translation>
<translation id="1080116354587839789">അനുയോജ്യമായ വീതിയിലാക്കുക</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">വായനാ ലിസ്റ്റ്</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" />-ന് ക്യാപ്‌ച്ചർ ചെയ്‌ത ക്രാഷ് റിപ്പോർട്ട് (ഇതുവരെ അപ്‌ലോഡുചെയ്‌തിട്ടോ അവഗണിച്ചിട്ടോ ഇല്ല)</translation>
<translation id="1281526147609854549"><ph name="ISSUER" /> ഇഷ്യൂ ചെയ്‌തത്</translation>
-<translation id="1283919782143846010">അപകടകരമായ ഉള്ളടക്കം ബ്ലോക്കുചെയ്‌തു</translation>
<translation id="1285320974508926690">ഈ സൈറ്റ് ഒരിക്കലും വിവര്‍‌ത്തനം ചെയ്യരുത്</translation>
<translation id="129553762522093515">സമീപകാലത്ത് അടച്ചവ</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />നിങ്ങളുടെ കുക്കികൾ മായ്‌ക്കുന്നത് പരീക്ഷിക്കുക<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">എൻറോൾമെന്റ് ഡൊമെയ്ൻ:</translation>
<translation id="1340482604681802745">പിക്കപ്പ് വിലാസം</translation>
-<translation id="1344211575059133124">ഈ സൈറ്റ് സന്ദർശിക്കാൻ നിങ്ങൾക്ക് അനുമതി ആവശ്യമാണെന്ന് തോന്നുന്നു</translation>
<translation id="1344588688991793829">Chromium ഓട്ടോഫിൽ ക്രമീകരണങ്ങൾ...</translation>
<translation id="1348198688976932919">നിങ്ങൾ പോകുന്ന സൈറ്റിൽ അപകടകരമായ ആപ്പുകൾ ഉണ്ട്</translation>
<translation id="1374468813861204354">നിർദ്ദേശങ്ങൾ</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" /> ലെ <ph name="ORGANIZATION" /> ന്റെ വ്യക്തിത്വം <ph name="ISSUER" /> പരിശോധിച്ചു.</translation>
<translation id="1426410128494586442">അതെ</translation>
<translation id="1430915738399379752">അച്ചടിക്കുക</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> എണ്ണവും}other{<ph name="PAYMENT_METHOD_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> എണ്ണവും}}</translation>
<translation id="1506687042165942984">ഈ പേജിന്റെ സംരക്ഷിച്ച (അതായത്. അത് കാലഹരണപ്പെട്ടതായി അറിയിക്കുന്നു) പകർപ്പ് കാണിക്കുക</translation>
<translation id="1517433312004943670">ഫോൺ നമ്പർ ആവശ്യമാണ്</translation>
+<translation id="1517500485252541695">ക്രെഡിറ്റ് കാർഡുകളും ഡെബിറ്റ് കാർഡുകളും സ്വീകരിക്കുന്നു</translation>
<translation id="1519264250979466059">ബിൽഡ് തീയതി</translation>
+<translation id="1527263332363067270">കണക്ഷനുവേണ്ടി കാക്കുന്നു…</translation>
<translation id="153384715582417236">ഇപ്പോൾ ഇത്രമാത്രമേ ലഭ്യമായിട്ടുള്ളൂ</translation>
<translation id="1549470594296187301">ഈ ഫീച്ചർ ഉപയോഗിക്കാൻ JavaScript പ്രവർത്തനക്ഷമമാക്കിയിരിക്കണം.</translation>
<translation id="1555130319947370107">നീല</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">സിസ്റ്റം അഡ്‌മിനെ ബന്ധപ്പെടാൻ ശ്രമിക്കുക.</translation>
<translation id="1740951997222943430">കാലഹരണപ്പെടുന്ന ശരിയായ മാസം നല്‍കുക</translation>
-<translation id="1745358365027406341">പേജ് പിന്നീട് ഡൗൺലോഡുചെയ്യുക</translation>
<translation id="17513872634828108">ഓപ്പൺ ടാബുകൾ</translation>
<translation id="1753706481035618306">പേജ് നമ്പർ</translation>
<translation id="1763864636252898013">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; അതിന്റെ സുരക്ഷ സർട്ടിഫിക്കറ്റിനെ നിങ്ങളുടെ ഉപകരണത്തിന്റെ ഓപ്പറേറ്റിംഗ് സിസ്‌റ്റത്തിന് പരിചയമില്ല. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">നിർബന്ധമായും പൂരിപ്പിക്കണം</translation>
<translation id="187918866476621466">ആരംഭ പേജുകൾ തുറക്കുക</translation>
<translation id="1883255238294161206">ലിസ്റ്റ് ചുരുക്കുക</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> എണ്ണവും}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> എണ്ണവും}}</translation>
<translation id="1898423065542865115">ഫിൽട്ടർ ചെയ്യുന്നു</translation>
+<translation id="1916770123977586577">ഈ സൈറ്റിലേക്ക് നിങ്ങളുടെ അപ്‌ഡേറ്റുചെയ്ത ക്രമീകരണം ഉപയോഗിക്കുന്നതിന്, ഈ പേജ് റീലോഡുചെയ്യുക</translation>
+<translation id="1919345977826869612">പരസ്യങ്ങള്‍</translation>
<translation id="192020519938775529">{COUNT,plural, =0{ഒന്നുമില്ല}=1{ഒരു സൈറ്റ്}other{# സൈറ്റുകൾ}}</translation>
<translation id="194030505837763158"><ph name="LINK" />-ലേക്ക് പോകുക</translation>
+<translation id="1948773908305951926">പ്രീപെയ്ഡ് കാർഡുകൾ സ്വീകരിക്കുന്നു</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> ബുക്ക്‌മാർക്കുകൾ</translation>
<translation id="1973335181906896915">സീരിയലൈസേഷൻ പിശക്</translation>
<translation id="1974060860693918893">നൂതനം</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP പിശക്</translation>
<translation id="2270484714375784793">ഫോൺ നമ്പർ</translation>
<translation id="2282872951544483773">ലഭ്യമല്ലാത്ത പരീക്ഷണങ്ങൾ</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ഇനം}other{<ph name="ITEM_COUNT" /> ഇനങ്ങൾ}}</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>
<translation id="2337852623177822836">ക്രമീകരണം നിയന്ത്രിക്കുന്നത് നിങ്ങളുടെ അഡ്‌മിനിസ്‌ട്രേറ്ററാണ്</translation>
<translation id="2354001756790975382">മറ്റ് ബുക്‌മാര്‍ക്കുകള്‍</translation>
<translation id="2354430244986887761">Google സുരക്ഷിത ബ്രൗസിംഗ് ഈയിടെ <ph name="SITE" /> എന്നതിൽ <ph name="BEGIN_LINK" />ദോഷകരമായ ആപ്പുകൾ കണ്ടെത്തി<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">തുടരൂ</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />-ന് ക്യാപ്‌ച്ചർ ചെയ്‌ത ക്രാഷ് റിപ്പോർട്ട് അപ്‌ലോഡുചെയ്‌തിട്ടില്ല</translation>
<translation id="2367567093518048410">നില</translation>
-<translation id="237718015863234333">UI ഇതരമാർഗങ്ങളൊന്നും ലഭ്യമല്ല</translation>
<translation id="2384307209577226199">എന്റര്‍പ്രൈസ് ഡിഫോൾട്ട്</translation>
<translation id="2386255080630008482">സെര്‍വറിന്‍റെ സര്‍ട്ടിഫിക്കറ്റ് അസാധുവാക്കി.</translation>
<translation id="2392959068659972793">മൂല്യമൊന്നും സജ്ജമാക്കാത്ത നയങ്ങൾ കാണിക്കുക</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">പരിശോധിച്ചെങ്കിൽ, വേഗത്തിൽ ഫോം പൂരിപ്പിക്കാൻ Chromium ഈ ഉപകരണത്തിൽ നിങ്ങളുടെ കാർഡിന്റെ ഒരു പകർപ്പ് സൂക്ഷിക്കും.</translation>
<translation id="2498091847651709837">പുതിയ കാർഡ് സ്‌കാൻ ചെയ്യുക</translation>
<translation id="2501278716633472235">പിന്നിലേക്ക് പോകുക</translation>
+<translation id="2503184589641749290">ഡെബിറ്റ് കാർഡുകളും പ്രീപെയ്ഡ് കാർഡുകളും സ്വീകരിക്കുന്നു</translation>
<translation id="2515629240566999685">നിങ്ങളുടെ ഏരിയയിലെ സിഗ്‌നൽ പരിശോധിക്കുന്നു</translation>
-<translation id="2516305470678292029">UI ഇതരമാർഗങ്ങൾ</translation>
<translation id="2539524384386349900">കണ്ടെത്തുക</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" />, അസാധുവായ ഒരു പ്രതികരണം അയച്ചു.</translation>
<translation id="2556876185419854533">&amp;എഡിറ്റുചെയ്യുന്നത് പഴയപടിയാക്കുക</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">ഷിപ്പിംഗ് രീതി</translation>
<translation id="277499241957683684">ഉപകരണ റെക്കോർഡ് കാണുന്നില്ല</translation>
<translation id="2784949926578158345">കണക്ഷന്‍ പുനഃസജ്ജമാക്കിയതാണ്.</translation>
+<translation id="2788784517760473862">ക്രെഡിറ്റ് കാർഡുകൾ സ്വീകരിക്കുന്നു</translation>
<translation id="2794233252405721443">സൈറ്റ് ബ്ലോക്കുചെയ്‌തു</translation>
<translation id="2799020568854403057">നിങ്ങൾ പോകുന്ന സൈറ്റിൽ ദോഷകരമായ ആപ്പുകളുണ്ട്</translation>
<translation id="2803306138276472711"><ph name="SITE" /> എന്നതിൽ Google സുരക്ഷിത ബ്രൗസിംഗ് ഈയിടെ <ph name="BEGIN_LINK" />മാൽവെയർ കണ്ടെത്തി<ph name="END_LINK" />. സാധാരണ നിലയിൽ സുരക്ഷിതമായ വെബ്സൈറ്റുകളിൽ ചിലപ്പോൾ മാൽവെയർ ഉണ്ടായേക്കാം.</translation>
<translation id="2824775600643448204">വിലാസവും തിരയൽ ബാറും</translation>
<translation id="2826760142808435982"><ph name="CIPHER" /> ഉപയോഗിച്ച് കണക്ഷൻ എൻക്രിപ്‌റ്റുചെയ്‌ത് പ്രാമാണീകരിക്കുന്നു, ഒപ്പം പ്രധാന എക്‌സേഞ്ച് മെക്കാനിസമായി <ph name="KX" /> ഉപയോഗിക്കുന്നു.</translation>
<translation id="2835170189407361413">ഫോം മായ്‌ക്കുക</translation>
+<translation id="2851634818064021665">ഈ സൈറ്റ് സന്ദർശിക്കാൻ നിങ്ങൾക്ക് അനുമതി ആവശ്യമാണ്</translation>
<translation id="2856444702002559011"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> എന്നതിൽ നിന്ന് നിങ്ങളുടെ വിവരം മോഷ്‌ടിക്കാൻ അക്രമികൾ ശ്രമിക്കുന്നുണ്ടാവാം (ഉദാഹരണത്തിന്, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ, അല്ലെങ്കിൽ ക്രെഡിറ്റ് കാർഡുകൾ). <ph name="BEGIN_LEARN_MORE_LINK" />കൂടുതലറിയുക<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">റീലോഡുചെയ്യരുത്</translation>
-<translation id="2900469785430194048">ഈ വെബ്‌പേജ് പ്രദർശിപ്പിക്കാൻ ശ്രമിക്കുന്നതിനിടയിൽ Google Chrome-ന്റെ മെമ്മറി നിറഞ്ഞു.</translation>
<translation id="2909946352844186028">ഒരു നെറ്റ്‌വർക്ക് മാറ്റം കണ്ടെത്തി.</translation>
<translation id="2916038427272391327">മറ്റ് പ്രോഗ്രാമുകൾ അടയ്‌ക്കുക</translation>
<translation id="2922350208395188000">സെര്‍വറിന്‍റെ സര്‍ട്ടിഫിക്കറ്റ് പരിശോധിക്കാന്‍ കഴിയില്ല.</translation>
<translation id="2928905813689894207">ബില്ലിംഗ് വിലാസം</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> എണ്ണവും}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> എണ്ണവും}}</translation>
<translation id="2941952326391522266">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; <ph name="DOMAIN2" /> എന്നതിൽ നിന്നുള്ളതാണ് അതിന്റെ സുരക്ഷാ സർട്ടിഫിക്കറ്റ്. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
<translation id="2948083400971632585">കണക്ഷനായി കോൺഫിഗർ ചെയ്‌ത ഏതൊരു പ്രോക്‌സികളും ക്രമീകരണങ്ങൾ പേജിൽ നിന്ന് നിങ്ങൾക്ക് പ്രവർത്തനരഹിതമാക്കാനാകും.</translation>
<translation id="2955913368246107853">ഫൈന്‍ഡ് ബാര്‍ അടയ്ക്കുക</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">പ്രവർത്തനക്ഷമമാക്കിയെങ്കിൽ, വേഗത്തിൽ ഫോം പൂരിപ്പിക്കാൻ Chromium ഈ ഉപകരണത്തിൽ നിങ്ങളുടെ കാർഡിന്റെ ഒരു പകർപ്പ് സൂക്ഷിക്കും.</translation>
<translation id="2985398929374701810">ശരിയായ വിലാസം നല്‍കുക</translation>
<translation id="2986368408720340940">ഈ പിക്കപ്പ് രീതി ലഭ്യമല്ല. മറ്റൊരു രീതി പരീക്ഷിക്കുക.</translation>
@@ -277,11 +281,9 @@
<translation id="3167968892399408617">ആൾമാറാട്ട ടാബുകളിൽ നിങ്ങൾ കാണുന്ന പേജുകൾ, ആൾമാറാട്ട ടാബുകൾ എല്ലാം അടച്ചതിനുശേഷം ബ്രൗസർ ചരിത്രത്തിലോ കുക്കി സ്റ്റോറിലോ തിരയൽ ചരിത്രത്തിലോ ഉണ്ടാകില്ല. നിങ്ങൾ ഡൗൺലോഡുചെയ്യുന്ന ഫയലുകളോ സൃഷ്‌ടിക്കുന്ന ബുക്ക്‌മാർക്കുകളോ എല്ലാം സൂക്ഷിക്കും.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">ഐലന്‍ഡ്</translation>
-<translation id="317583078218509884">പുതിയ സൈറ്റ് അനുമതി ക്രമീകരണങ്ങൾ പേജ് വീണ്ടും ലോഡുചെയ്‌തതിനുശേഷം പ്രാബല്യത്തിൽ വരും.</translation>
<translation id="3176929007561373547">പ്രോക്‌സി സെർവർ പ്രവർത്തിക്കുന്നുവെന്ന് ഉറപ്പാക്കാൻ
നിങ്ങളുടെ പ്രോക്‌സി ക്രമീകരണം പരിശോധിക്കുകയോ നെറ്റ്‌വർക്ക് അഡ്‌മിനിസ്‌ട്രേറ്ററെ ബന്ധപ്പെടുകയോ ചെയ്യുക. നിങ്ങൾ ഉപയോഗിക്കുന്നത് ഒരു പ്രോക്‌സി സെർവറാണെന്ന് കരുതുന്നില്ലെങ്കിൽ:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">അദൃശ്യതാ സംവിധാനത്തിൽ പേജ് തുറക്കുക</translation>
<translation id="320323717674993345">പേയ്‌മെന്റ് റദ്ദാക്കുക</translation>
<translation id="3207960819495026254">ബുക്ക്‌മാർക്കുചെയ്‌തു</translation>
<translation id="3225919329040284222">ബിൽട്ട്-ഇൻ പ്രതീക്ഷകള്‍ക്ക് പൊരുത്തപ്പെടാത്ത സര്‍ട്ടിഫിക്കറ്റാണ് സെര്‍വര്‍ അവതരിപ്പിച്ചത്. നിങ്ങളെ സംരക്ഷിക്കുന്നതിലേക്കായുള്ള നിശ്ചിത, ഉന്നത-സുരക്ഷാ വെബ്‌സൈറ്റുകൾക്കായാണ് ഈ പ്രതീക്ഷകൾ ഉൾപ്പെടുത്തിയിരിക്കുന്നത്.</translation>
@@ -294,6 +296,7 @@
<translation id="3270847123878663523">&amp;പുനഃക്രമീകരിക്കുന്നത് പഴയപടിയാക്കുക</translation>
<translation id="3282497668470633863">കാർഡിലെ പേര് ചേർക്കുക</translation>
<translation id="3286538390144397061">ഇപ്പോള്‍ പുനരാരംഭിക്കുക</translation>
+<translation id="3287510313208355388">ഓൺലൈൻ ആയിരിക്കുന്ന സമയത്ത് ഡൗൺലോഡുചെയ്യുക</translation>
<translation id="3303855915957856445">തിരയൽ ഫലങ്ങളൊന്നും കണ്ടെത്തിയില്ല</translation>
<translation id="3305707030755673451"><ph name="TIME" />-ന് നിങ്ങളുടെ സമന്വയ പാസ്‌ഫ്രെയ്‌സ് ഉപയോഗിച്ച് ഡാറ്റ എൻക്രിപ്‌റ്റുചെയ്‌തു. സമന്വയം ആരംഭിക്കുന്നതിന് ഇത് നൽകുക.</translation>
<translation id="3320021301628644560">ബില്ലിംഗ് വിലാസം ചേർക്കുക</translation>
@@ -326,7 +329,6 @@
<translation id="3452404311384756672">ഇടവേള ലഭ്യമാക്കുക:</translation>
<translation id="3462200631372590220">വിപുലമായവ മറയ്ക്കുക</translation>
<translation id="3467763166455606212">കാർഡ് ഉടമയുടെ പേര് ആവശ്യമാണ്</translation>
-<translation id="3478058380795961209">കാലഹരണപ്പെടുന്ന മാസം</translation>
<translation id="3479539252931486093">ഇത് അപ്രതീക്ഷിതമായിരുന്നോ? <ph name="BEGIN_LINK" />ഞങ്ങളെ അറിയിക്കുക<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">ഇപ്പോഴല്ല</translation>
<translation id="3498215018399854026">ഇപ്പോൾ ഞങ്ങൾക്ക് നിങ്ങളുടെ രക്ഷകർത്താവിനെ ബന്ധപ്പെടാനായില്ല. വീണ്ടും ശ്രമിക്കുക.</translation>
@@ -342,6 +344,7 @@
&lt;p&gt;&lt;strong&gt;ക്രമീകരണ&lt;/strong&gt; ആപ്പിന്റെ &lt;strong&gt;പൊതുവായ&lt;/strong&gt; വിഭാഗത്തിൽ നിന്ന് തീയതിയും സമയവും ക്രമീകരിക്കുക.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />കൂടുതലറിയുക<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">ക്രെഡിറ്റ് കാർഡുകളും പ്രീപെയ്ഡ് കാർഡുകളും സ്വീകരിക്കുന്നു.</translation>
<translation id="3582930987043644930">പേര് ചേർക്കുക</translation>
<translation id="3583757800736429874">&amp;നീക്കുന്നത് വീണ്ടും ചെയ്യുക</translation>
<translation id="3586931643579894722">വിശദാംശങ്ങൾ മറയ്‌ക്കുക‍‌</translation>
@@ -357,10 +360,10 @@
<translation id="3655670868607891010">നിങ്ങൾ ഇത് പതിവായി കാണുന്നുണ്ടെങ്കിൽ, ഈ <ph name="HELP_LINK" /> പരീക്ഷിക്കൂ.</translation>
<translation id="3658742229777143148">പുനരവലോകനം</translation>
<translation id="3678029195006412963">അഭ്യർത്ഥന സൈൻ ചെയ്യാനായില്ല</translation>
+<translation id="3678529606614285348">പുതിയ അദൃശ്യ വിൻഡോയിൽ പേജ് തുറക്കുക (കൺട്രോൾ-ഷിഫ്റ്റ്-N)</translation>
<translation id="3679803492151881375">ക്രാഷ് റിപ്പോർട്ട് <ph name="CRASH_TIME" />-ന് ക്യാപ്‌ചർ ചെയ്‌ത്, <ph name="UPLOAD_TIME" />-ന് അപ്‌ലോഡുചെയ്‌തു</translation>
<translation id="3681007416295224113">സര്‍‌ട്ടിഫിക്കറ്റ് വിവരങ്ങള്‍‌</translation>
<translation id="3690164694835360974">ലോഗിൻ ചെയ്യുന്നത് സുരക്ഷിതമല്ല</translation>
-<translation id="3693415264595406141">പാസ്‌വേഡ്:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">ലോഡ്ചെയ്യുന്നു...</translation>
<translation id="3712624925041724820">ലൈസൻസുകൾ കാലഹരണപ്പെട്ടു</translation>
@@ -372,6 +375,7 @@
<translation id="3748148204939282805"><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="375403751935624634">ഒരു സെര്‍വര്‍ പിശക് കാരണം വിവര്‍‌ത്തനം പരാജയപ്പെട്ടു.</translation>
<translation id="3759461132968374835">നിങ്ങള്‍ക്ക് സമീപകാലത്ത് റിപ്പോര്‍ട്ടുചെയ്ത ക്രാഷുകളൊന്നുമില്ല. ക്രാഷ് റിപ്പോര്‍ട്ടുചെയ്യുന്ന സമയത്ത് സംഭവിച്ച ക്രാഷുകളെ അപ്രാപ്തമാക്കി, ഇവിടെ ദൃശ്യമാകില്ല.</translation>
+<translation id="3765032636089507299">സുരക്ഷിത ബ്രൗസിംഗ് പേജ് നിർമ്മാണത്തിലാണ്.</translation>
<translation id="3778403066972421603">ഈ കാർഡ് നിങ്ങളുടെ Google അക്കൗണ്ടിലും ഈ ഉപകരണത്തിലും സംരക്ഷിക്കണോ?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" />-ൽ അവസാനിക്കുന്നു</translation>
@@ -384,8 +388,10 @@
<translation id="3886446263141354045"><ph name="NAME" /> എന്നയാൾക്ക് ഈ സൈറ്റ് ആക്‌സസ് ചെയ്യാനുള്ള നിങ്ങളുടെ അഭ്യർത്ഥന അയച്ചു</translation>
<translation id="3890664840433101773">ഇമെയില്‍‌ ചേര്‍‌ക്കുക</translation>
<translation id="3901925938762663762">കാർഡ് കാലഹരണപ്പെട്ടു</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686"><ph name="CRASH_ID" /> എന്ന ക്രാഷ് റിപ്പോർട്ട് ഐഡി അപ്‌ലോഡുചെയ്‌തു (ലോക്കൽ ക്രാഷ് ഐഡി: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">ഈ സെർവറിന് ഇത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; അതിന്റെ സുരക്ഷാ സർട്ടിഫിക്കറ്റ് വിഷയേതര നാമങ്ങൾ വ്യക്തമാക്കുന്നില്ല. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
+<translation id="3949601375789751990">നിങ്ങളുടെ ബ്രൗസിംഗ് ചരിത്രം ഇവിടെ ദൃശ്യമാകും</translation>
<translation id="3963721102035795474">റീഡർ മോഡ്</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{ഒന്നുമില്ല}=1{ഒരു സൈറ്റിൽ നിന്ന് }other{# സൈറ്റുകളിൽ നിന്ന് }}</translation>
<translation id="397105322502079400">കണക്കാക്കുന്നു...</translation>
@@ -414,6 +420,8 @@
<translation id="4165986682804962316">സൈറ്റ് ക്രമീകരണങ്ങൾ</translation>
<translation id="4169947484918424451">Chromium ഈ കാർഡ് സംരക്ഷിക്കണോ?</translation>
<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="4196861286325780578">&amp;നീക്കുന്നത് വീണ്ടും ചെയ്യുക</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ഫയർവാളും ആന്റിവൈറസ് കോൺഫിഗറേഷനുകളും പരിശോധിക്കുന്നു<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ക്രാഷുകള്‍</translation>
@@ -437,12 +445,12 @@
<translation id="4356973930735388585">ഈ സൈറ്റിലെ ആക്രമണകാരികൾ നിങ്ങളുടെ വിവരങ്ങൾ മോഷ്‌ടിക്കാനോ ഇല്ലാതാക്കാനോ ഇടയുള്ള (ഉദാഹരണത്തിന്, ഫോട്ടോകൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ, ക്രെഡിറ്റ് കാർഡുകൾ എന്നിവ) അപകടകരമായ പ്രോഗ്രാമുകൾ കമ്പ്യൂട്ടറിൽ ഇൻസ്‌റ്റാൾ ചെയ്യാൻ ശ്രമിച്ചേക്കാം.</translation>
<translation id="4372948949327679948">പ്രതീക്ഷിച്ച <ph name="VALUE_TYPE" /> മൂല്യം.</translation>
<translation id="4377125064752653719">നിങ്ങള്‍‌ <ph name="DOMAIN" /> എന്നതില്‍‌ എത്താന്‍‌ ശ്രമിച്ചു, പക്ഷേ സെര്‍‌വര്‍‌ നൽകിയ സര്‍‌ട്ടിഫിക്കറ്റ് അത് നല്‍‌കിയ ആള്‍‌ അസാധുവാക്കി. സെര്‍‌വര്‍‌ നല്‍‌കിയ സുരക്ഷാ ക്രെഡന്‍‌ഷ്യലുകള്‍‌ തികച്ചും വിശ്വാ‍സയോഗ്യമല്ല എന്നാണ് ഇതിനര്‍‌ത്ഥം. നിങ്ങള്‍‌ ഒരു ആക്രമണകാരിയുമായിട്ടാകാം ആശയവിനിമയം നടത്തുന്നത്.</translation>
-<translation id="4381091992796011497">ഉപയോക്തൃ നാമം:</translation>
<translation id="4394049700291259645">അപ്രാപ്‌തമാക്കുക</translation>
<translation id="4406896451731180161">തിരയൽ ഫലങ്ങൾ</translation>
<translation id="4424024547088906515">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; അതിന്റെ സുരക്ഷാ സർട്ടിഫിക്കറ്റ് Chrome-ന് പരിചയമില്ലാത്തതാണ്. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" />, നിങ്ങളുടെ ലോഗിൻ സർട്ടിഫിക്കറ്റിന് അംഗീകരിച്ചിട്ടില്ല, അല്ലെങ്കിൽ അങ്ങനെയൊരെണ്ണം നൽകിയിട്ടില്ലായിരിക്കാം.</translation>
<translation id="443673843213245140">പ്രോക്‌സി ഉപയോഗം അപ്രാപ്‌തമാക്കി പക്ഷെ ഒരു വ്യക്തമായ പ്രോക്‌സി കോൺഫിഗറേഷൻ നിർദ്ദേശിച്ചു.</translation>
+<translation id="445100540951337728">ഡെബിറ്റ് കാർഡുകൾ സ്വീകരിക്കുന്നു</translation>
<translation id="4506176782989081258">മൂല്യനിർണ്ണയ പിശക്: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">സിസ്റ്റം അഡ്‌മിനെ ബന്ധപ്പെടുന്നു</translation>
<translation id="450710068430902550">അഡ്‌മിനിസ്‌ട്രേറ്ററുമായി പങ്കിടുന്നു</translation>
@@ -454,12 +462,14 @@
<translation id="4587425331216688090">Chrome-ൽ നിന്ന് വിലാസം നീക്കംചെയ്യണോ?</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" /> എന്നതിലേക്കുള്ള നിങ്ങളുടെ കണക്ഷനെ ആധുനിക സൈഫർ സ്യൂട്ട് ഉപയോഗിച്ച് എൻക്രിപ്റ്റുചെയ്‌തിരിക്കുന്നു.</translation>
<translation id="4594403342090139922">&amp;ഇല്ലാതാക്കുന്നത് പഴയപടിയാക്കുക</translation>
+<translation id="4611292653554630842">ലോഗ് ഇന്‍ ചെയ്യുക</translation>
<translation id="4619615317237390068">മറ്റ് ഉപകരണങ്ങളിൽ നിന്നുള്ള ടാബുകൾ</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; അതിന്റെ സുരക്ഷാ സർട്ടിഫിക്കറ്റിൽ പിശകുകൾ അടങ്ങിയിരിക്കുന്നു. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
<translation id="4690462567478992370">അസാധുവായ ഒരു സർട്ടിഫിക്കറ്റ് ഉപയോഗിക്കുന്നത് നിർത്തുക</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">നിങ്ങളുടെ കണക്ഷൻ തടസ്സപ്പെട്ടു</translation>
+<translation id="471880041731876836">ഈ സൈറ്റ് സന്ദർശിക്കാൻ നിങ്ങൾക്ക് അനുമതി ആവശ്യമില്ല</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows നെറ്റ്‌വർക്ക് ഡയഗണോസ്‌റ്റിക്‌സ് റൺ ചെയ്യുന്നു<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">നയങ്ങൾ വീണ്ടും ലോഡുചെയ്യുക</translation>
<translation id="4728558894243024398">പ്ലാറ്റ്ഫോം</translation>
@@ -481,14 +491,15 @@
<translation id="4850886885716139402">കാണുക</translation>
<translation id="4854362297993841467">ഈ ഡെലിവറി രീതി ലഭ്യമല്ല. മറ്റൊരു രീതി പരീക്ഷിക്കുക.</translation>
<translation id="4858792381671956233">ഈ സൈറ്റ് സന്ദർശിക്കുന്നതിന് നിങ്ങൾ രക്ഷിതാക്കളോട് അനുമതി ആവശ്യപ്പെട്ടു</translation>
-<translation id="4863764087567530506">നിങ്ങൾ സോഫ്‌റ്റ്‌വെയറുകൾ ഇൻസ്‌റ്റാൾ ചെയ്യാനോ വ്യക്തിഗത വിവരങ്ങൾ വെളിപ്പെടുത്താനോ വേണ്ടി, ഈ ഉള്ളടക്കം നിങ്ങളെ കബളിപ്പിക്കാൻ ശ്രമിച്ചേക്കാം. <ph name="BEGIN_LINK" />എന്തായാലും കാണിക്കുക<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">തിരയൽ ചരിത്രം</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">പരിശോധിച്ചുറപ്പിക്കൽ ആവശ്യമുണ്ട്</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ഒരു വെബ്‌പേജ് കൂടിയുണ്ട്}other{# വെബ്‌പേജുകൾ കൂടിയുണ്ട്}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">ഈ പേജിനെ അറിയപ്പെടാത്ത ഒരു ഭാഷയില്‍‌ നിന്നും <ph name="LANGUAGE_LANGUAGE" /> എന്നതിലേക്ക് വിവര്‍‌ത്തനം ചെയ്തു</translation>
<translation id="4923459931733593730">പേയ്‌മെന്റ് രീതി</translation>
<translation id="4926049483395192435">വ്യക്തമാക്കേണ്ടതാണ്.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">പ്രവര്‍ത്തനങ്ങള്‍</translation>
<translation id="4958444002117714549">ലിസ്റ്റ് വിപുലീകരിക്കുക</translation>
<translation id="4974590756084640048">മുന്നറിയിപ്പുകൾ വീണ്ടും പ്രവർത്തനക്ഷമമാക്കുക</translation>
@@ -504,6 +515,7 @@
<translation id="5045550434625856497">ശരിയല്ലാത്ത രഹസ്യവാക്ക്</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>
<translation id="5087580092889165836">കാർഡ് ചേർക്കുക</translation>
<translation id="5089810972385038852">സ്റ്റേറ്റ്</translation>
@@ -511,18 +523,27 @@
<translation id="5095208057601539847">പ്രവിശ്യ</translation>
<translation id="5115563688576182185">(64-ബിറ്റ്)</translation>
<translation id="5141240743006678641">Google ക്രെഡൻഷ്യലുകൾ ഉപയോഗിച്ച് സമന്വിത പാസ്‌വേഡുകൾ എൻക്രിപ്റ്റുചെയ്യുക</translation>
-<translation id="514421653919133810">അദൃശ്യതാ സംവിധാനത്തിൽ പേജ് തുറക്കുക (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">നയ പ്രതികരണത്തിൽ പിശക് കോഡ് ഉണ്ട്</translation>
+<translation id="5159010409087891077">പുതിയൊരു അദൃശ്യ വിൻഡോയിൽ പേജ് തുറക്കുക (⇧⌘N)</translation>
<translation id="5171045022955879922">തിരയുക അല്ലെങ്കിൽ URL ടൈപ്പുചെയ്യുക</translation>
<translation id="5172758083709347301">മെഷീൻ</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> എന്നതില്‍‌ ഇല്ലേ? ഈ പിശക് റിപ്പോര്‍‌ട്ടുചെയ്യുക</translation>
-<translation id="5181140330217080051">ഡൗൺലോഡുചെയ്യുന്നു</translation>
<translation id="5190835502935405962">ബുക്ക്‌മാര്‍‌ക്കുകള്‍‌ ബാര്‍‌</translation>
<translation id="5199729219167945352">പരീക്ഷണങ്ങള്‍</translation>
<translation id="5205222826937269299">പേര് ആവശ്യമാണ്</translation>
<translation id="5222812217790122047">ഇമെയിൽ ആവശ്യമാണ്</translation>
<translation id="5251803541071282808">ക്ലൗഡ്</translation>
<translation id="5277279256032773186">ജോലിസ്ഥലത്തുള്ള Chrome ഉപയോഗിക്കുകയാണോ? ബിസിനസ് സ്ഥാപനങ്ങൾക്ക് അവരുടെ ജീവനക്കാർക്ക് വേണ്ടി Chrome ക്രമീകരണം മാനേജുചെയ്യാനാകും. കൂടുതലറിയുക</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />നിങ്ങൾക്ക് വെബിൽ കണക്റ്റുചെയ്യാൻ കഴിയുന്ന തരത്തിൽ താൽക്കാലികമായി സോഫ്റ്റ്‌വെയർ പ്രവർത്തനരഹിതമാക്കുന്നതിന് ഈ ഘട്ടങ്ങൾ പിന്തുടരുക. നിങ്ങൾക്ക് അഡ്‌മിനിസ്ട്രേറ്ററുടെ സവിശേഷാധികാരങ്ങൾ ആവശ്യമായിവരും.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />ആരംഭിക്കുക<ph name="END_BOLD" /> ക്ലിക്കുചെയ്യുക, തുടർന്ന് <ph name="BEGIN_BOLD" />"പ്രാദേശിക സേവനങ്ങൾ കാണുക"<ph name="END_BOLD" /> തിരയുകയും തിരഞ്ഞെടുക്കുകയും ചെയ്യുക
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />വിഷ്വൽ ഡിസ്കവറി<ph name="END_BOLD" /> തിരഞ്ഞെടുക്കുക
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />സ്റ്റാർട്ടപ്പ് തരം<ph name="END_BOLD" /> എന്നതിന് താഴെ, <ph name="BEGIN_BOLD" />പ്രവർത്തനരഹിതമാക്കി<ph name="END_BOLD" /> തിരഞ്ഞെടുക്കുക
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />സേവന നില<ph name="END_BOLD" /> എന്നതിന് താഴെ, <ph name="BEGIN_BOLD" />നിർത്തുക<ph name="END_BOLD" /> ക്ലിക്കുചെയ്യുക
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />പ്രയോഗിക്കുക<ph name="END_BOLD" /> ക്ലിക്കുചെയ്യുക, തുടർന്ന് <ph name="BEGIN_BOLD" />ശരി<ph name="END_BOLD" /> ക്ലിക്കുചെയ്യുക
+ <ph name="LIST_ITEM" />നിങ്ങളുടെ കമ്പ്യൂട്ടറിൽ നിന്ന് എങ്ങനെയാണ് സോഫ്റ്റ്‌വെയർ ശാശ്വതമായി നീക്കംചെയ്യുന്നത് എന്നറിയാൻ <ph name="BEGIN_LEARN_MORE_LINK" />Chrome സഹായ കേന്ദ്രം<ph name="END_LEARN_MORE_LINK" /> സന്ദർശിക്കുക
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">ഈ സൈറ്റിലെ നിങ്ങളുടെ കണക്ഷൻ സ്വകാര്യമല്ല. എപ്പോൾ വേണമെങ്കിലും VR മോഡിൽ നിന്ന് പുറത്തുകടക്കാൻ, ഹെഡ്‌സെറ്റ് നീക്കംചെയ്‌ത ശേഷം 'തിരികെ പോകുക' അമർത്തുക.</translation>
<translation id="5299298092464848405">നയം പാഴ്‌സുചെയ്യുന്നതിൽ പിശക്</translation>
<translation id="5308689395849655368">ക്രാഷ് റിപ്പോര്‍ട്ടുചെയ്യല്‍ അപ്രാപ്തമാക്കി.</translation>
@@ -539,9 +560,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" എന്നതിൽ സ്‌കീമ മൂല്ല്യനിർണ്ണയ പിശക്: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">ഈ <ph name="HOST_NAME" /> പേജ് കണ്ടെത്താനായില്ല</translation>
<translation id="5455374756549232013">മോശം നയ ടൈംസ്റ്റാമ്പ്</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" /> ന്റെ <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">അസാധുവാണ്</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> എണ്ണവും}other{<ph name="CONTACT_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> എണ്ണവും}}</translation>
<translation id="5470861586879999274">&amp;എഡിറ്റുചെയ്യുന്നത് വീണ്ടും ചെയ്യുക</translation>
+<translation id="5481076368049295676">ഈ ഉള്ളടക്കം, നിങ്ങളുടെ ഉപകരണത്തിൽ വിവരങ്ങൾ മോഷ്‌ടിക്കുകയോ ഇല്ലാതാക്കുയോ ചെയ്യുന്ന തരത്തിലുള്ള സോഫ്‌റ്റ്‌വെയർ ഇൻസ്‌റ്റാൾ ചെയ്യാൻ ശ്രമിച്ചേക്കാം. <ph name="BEGIN_LINK" />എന്തായാലും കാണിക്കുക<ph name="END_LINK" /></translation>
<translation id="54817484435770891">ശരിയായ വിലാസം ചേർക്കുക</translation>
<translation id="5492298309214877701">കമ്പനി, ഓർഗനൈസേഷൻ അല്ലെങ്കിൽ സ്‌കൂൾ ഇൻട്രാനെറ്റിലെ ഈ സൈറ്റിന്, ബാഹ്യ വെബ്‌സൈറ്റിന് സമാനമായ URL ആണുള്ളത്.
<ph name="LINE_BREAK" />
@@ -553,6 +575,7 @@
<translation id="5540224163453853">അഭ്യർത്ഥിച്ച ലേഖനം കണ്ടെത്താനായില്ല.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> സൈറ്റിലെ ഒരു എംബഡ് ചെയ്‌ത പേജ് പറയുന്നത്:</translation>
<translation id="5556459405103347317">വീണ്ടും ലോഡുചെയ്യുക</translation>
+<translation id="5560088892362098740">കാലഹരണപ്പെടല്‍ തീയതി</translation>
<translation id="5565735124758917034">സജീവമാണ്</translation>
<translation id="5571083550517324815">ഈ വിലാസത്തിൽ നിന്ന് പിക്ക്അപ്പ് ചെയ്യാൻ കഴിയില്ല. മറ്റൊരു വിലാസം തിരഞ്ഞെടുക്കുക.</translation>
<translation id="5572851009514199876">ആരംഭിച്ച് Chrome-ൽ സൈൻ ഇൻ ചെയ്യുന്നതിലൂടെ, നിങ്ങൾക്ക് ഈ സൈറ്റ് ആക്‌സസ് ചെയ്യാൻ അനുവാദമുണ്ടോയെന്ന് Chrome-ന് പരിശോധിക്കാനാവും.</translation>
@@ -567,12 +590,13 @@
<translation id="5629630648637658800">നയ ക്രമീകരണങ്ങൾ ലോഡുചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു</translation>
<translation id="5631439013527180824">ഉപകരണ മാനേജുമെന്റ് ടോക്കൺ അസാധുവാണ്</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="5646376287012673985">സ്ഥാനം</translation>
<translation id="5659593005791499971">ഇമെയില്‍</translation>
<translation id="5669703222995421982">വ്യക്തിപരമാക്കിയ ഉള്ളടക്കം സ്വന്തമാക്കുക</translation>
<translation id="5675650730144413517">ഈ പേജ് പ്രവർത്തിക്കുന്നില്ല</translation>
<translation id="5710435578057952990">ഈ വെബ്സൈറ്റിന്റെ വ്യക്തിത്വം പരിശോധിച്ചിട്ടില്ല.</translation>
-<translation id="5713016350996637505">തെറ്റിദ്ധരിപ്പിക്കുന്ന ഉള്ളടക്കം ബ്ലോക്കുചെയ്‌തു</translation>
+<translation id="5719499550583120431">പ്രീപെയ്ഡ് കാർഡുകൾ സ്വീകരിക്കുന്നു.</translation>
<translation id="5720705177508910913">നിലവിലെ ഉപയോക്താവ്</translation>
<translation id="5732392974455271431">നിങ്ങൾക്ക് വേണ്ടി ഇത് അൺബ്ലോക്കുചെയ്യാൻ രക്ഷിതാക്കൾക്ക് കഴിയും</translation>
<translation id="5763042198335101085">ശരിയായ ഇമെയിൽ വിലാസം നൽകുക</translation>
@@ -584,15 +608,14 @@
<translation id="5803412860119678065">നിങ്ങളുടെ <ph name="CARD_DETAIL" /> പൂരിപ്പിക്കണോ?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" /> എന്നതിലേക്കുള്ള നിങ്ങളുടെ കണക്ഷൻ കാലഹരണപ്പെട്ട സൈഫർ സ്യൂട്ട് ഉപയോഗിച്ച് എൻക്രിപ്റ്റുചെയ്‌തിരിക്കുന്നു.</translation>
<translation id="5813119285467412249">&amp;ചേർക്കുന്നത് വീണ്ടും ചെയ്യുക</translation>
-<translation id="5814352347845180253">നിങ്ങൾക്ക്, <ph name="SITE" /> സൈറ്റിൽ നിന്നും മറ്റുസൈറ്റുകളിൽ നിന്നുമുള്ള പ്രീമിയം ഉള്ളടക്കത്തിലേക്കുള്ള ആക്‌സസ് നഷ്‌ടമായേക്കാം.</translation>
<translation id="5838278095973806738">അക്രമകാരികൾ മോഷ്‌ടിക്കാൻ സാധ്യതയുള്ളതിനാൽ ഈ സൈറ്റിൽ നിങ്ങളുടെ രഹസ്യ വിവരങ്ങളൊന്നും (ഉദാഹരണത്തിന്, പാസ്‌വേഡുകളോ ക്രെഡിറ്റ് കാർഡുകളോ പോലുള്ളവ) നൽകരുത്.</translation>
<translation id="5869405914158311789">ഈ സൈറ്റ് ലഭ്യമാക്കാനാകുന്നില്ല</translation>
<translation id="5869522115854928033">സംരക്ഷിച്ച പാസ്‌വേഡുകള്‍</translation>
<translation id="5872918882028971132">പാരന്റ് നിർദ്ദേശങ്ങൾ</translation>
+<translation id="5893752035575986141">ക്രെഡിറ്റ് കാർഡുകൾ സ്വീകരിക്കുന്നു.</translation>
<translation id="5901630391730855834">മഞ്ഞ</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (സമന്വയിപ്പിച്ചത്)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{ഒരെണ്ണം ഉപയോഗത്തിലുണ്ട്}other{# എണ്ണം ഉപയോഗത്തിലുണ്ട്}}</translation>
-<translation id="5926846154125914413">നിങ്ങൾക്ക് ചില സൈറ്റുകളിൽ നിന്നുള്ള പ്രീമിയം ഉള്ളടക്കത്തിലേക്കുള്ള ആക്‌സസ് നഷ്‌ടമായേക്കാം.</translation>
<translation id="5959728338436674663">അപകടകരമായ ആപ്‌സുകളും സൈറ്റുകളും കണ്ടെത്താൻ സഹായിക്കുന്നതിന്‌ ചില <ph name="BEGIN_WHITEPAPER_LINK" />സിസ്‌റ്റം വിവരങ്ങളും പേജ്‌ ഉള്ളടക്കവും<ph name="END_WHITEPAPER_LINK" /> Google-ന്‌ സ്വയമേവ അയയ്‌ക്കുക. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">ചരിത്രത്തിൽ നിന്നും നീക്കംചെയ്യുക</translation>
<translation id="5975083100439434680">സൂം ഔട്ട്</translation>
@@ -608,6 +631,7 @@
<translation id="6040143037577758943">അടയ്ക്കുക</translation>
<translation id="6042308850641462728">കൂടുതൽ</translation>
<translation id="6047233362582046994">നിങ്ങളുടെ സുരക്ഷയെ ബാധിക്കാനിടയുണ്ടെന്ന് മനസ്സിലാക്കുകയാണെങ്കിൽ, ദോഷകരമായ ആപ്പുകൾ നീക്കംചെയ്യുന്നതിനു മുമ്പ് <ph name="BEGIN_LINK" />ഈ സൈറ്റ് സന്ദർശിക്കുക<ph name="END_LINK" />.</translation>
+<translation id="6047927260846328439">സോഫ്‌റ്റ്‌വെയർ ഇൻസ്‌റ്റാൾ ചെയ്യുന്നതിലേക്കോ വ്യക്തിഗത വിവരങ്ങൾ വെളിപ്പെടുത്തുന്നതിലേക്കോ നിങ്ങളെ തന്ത്രപൂർവ്വം നയിച്ചുകൊണ്ട്, ഈ ഉള്ളടക്കം നിങ്ങളെ കബളിപ്പിക്കാൻ ശ്രമിച്ചേക്കാം. <ph name="BEGIN_LINK" />എന്തായാലും കാണിക്കുക<ph name="END_LINK" /></translation>
<translation id="6051221802930200923"><ph name="SITE" /> എന്ന വെബ്‌സൈറ്റ് സർട്ടിഫിക്കറ്റ് പിന്നിംഗ് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങൾക്കിപ്പോൾ അത് സന്ദർശിക്കാനാകില്ല. നെറ്റ്‌വർക്ക് പിശകുകളും ആക്രമണങ്ങളും സാധാരണയായി താൽക്കാലികമായിരിക്കും, അതിനാൽ ഈ പേജ് മിക്കവാറും പിന്നീട് പ്രവർത്തിക്കും.</translation>
<translation id="6060685159320643512">ശ്രദ്ധിക്കൂ, ഈ പരീക്ഷണങ്ങള്‍ പാളിയേക്കാം‍ </translation>
<translation id="6080696365213338172">അഡ്‌മിനിസ്‌ട്രേറ്റർ നൽകിയ സർട്ടിഫിക്കറ്റ് ഉപയോഗിച്ച് നിങ്ങൾ ഉള്ളടക്കം ആക്‌സസ്സുചെയ്‌തു. നിങ്ങൾ <ph name="DOMAIN" /> എന്നതിലേക്ക് നൽകുന്ന ഡാറ്റ അഡ്‌മിനിസ്‌ട്രേറ്റർക്ക് തടയാനാവും.</translation>
@@ -621,7 +645,6 @@
<translation id="6165508094623778733">കൂടുതൽ‍ മനസിലാക്കുക</translation>
<translation id="6169916984152623906">നിങ്ങൾക്കിപ്പോൾ സ്വകാര്യമായി ബ്രൗസ് ചെയ്യാം, ഈ ഉപകരണം ഉപയോഗിക്കുന്ന മറ്റ് ആളുകൾക്ക് നിങ്ങളുടെ ആക്‌റ്റിവിറ്റി കാണാനാവില്ല. എന്നാൽ ഡൗൺലോഡുകളും ബുക്ക്‌മാർക്കുകളും സംരക്ഷിക്കപ്പെടും.</translation>
<translation id="6177128806592000436">ഈ സൈറ്റിലേക്കുള്ള നിങ്ങളുടെ കണക്ഷൻ സുരക്ഷിതമല്ല</translation>
-<translation id="6184817833369986695">(സമാന വിഭാഗത്തിൽപ്പെട്ടവ: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">നിങ്ങളുടെ ഇന്റർനെറ്റ് കണക്ഷൻ പരിശോധിക്കുക</translation>
<translation id="6218753634732582820">Chromium-ത്തിൽ നിന്ന് വിലാസം നീക്കംചെയ്യണോ?</translation>
<translation id="6221345481584921695"><ph name="BEGIN_LINK" /> എന്നതിൽ Google സുരക്ഷിത ബ്രൗസിംഗ് ഈയിടെ <ph name="END_LINK" />മാൽവെയർ കണ്ടെത്തി<ph name="SITE" /> . സാധാരണ നിലയിൽ സുരക്ഷിതമായ വെബ്സൈറ്റുകളിൽ ചിലപ്പോൾ മാൽവെയർ ഉണ്ടായിരിക്കാം. ഒരു അറിയപ്പെടുന്ന മാൽ‌വെയർ വിതരണക്കാരായ <ph name="SUBRESOURCE_HOST" /> എന്നതിൽ നിന്നാണ് ദോഷകരമായ ഉള്ളടക്കം വരുന്നത്.</translation>
@@ -640,13 +663,14 @@
<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>
<translation id="6387478394221739770">Chrome-ന്റെ രസകരമായ പുതിയ സവിശേഷതകളിൽ താൽപ്പര്യമുണ്ടോ? chrome.com/beta-യിൽ ഞങ്ങളുടെ ബീറ്റ ചാനൽ പരീക്ഷിക്കുക.</translation>
-<translation id="6389758589412724634">ഈ വെബ്‌പേജ് പ്രദർശിപ്പിക്കാൻ ശ്രമിക്കുന്നതിനിടയിൽ Chromium-ത്തിന്റെ മെമ്മറി നിറഞ്ഞു.</translation>
+<translation id="6397451950548600259">വെബിലേക്ക് സുരക്ഷിതമായി കണക്റ്റുചെയ്യുന്നതിൽ നിന്ന് Chrome-നെ നിങ്ങളുടെ കമ്പ്യൂട്ടറിലെ സോഫ്റ്റ്‌വെയർ തടയുന്നു</translation>
<translation id="6404511346730675251">ബുക്ക്‌മാർക്ക് എഡിറ്റുചെയ്യുക</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" />-ന്റെ കാലാവധി തീരുന്ന തീയതിയും CVC-യും നൽകുക</translation>
<translation id="6414888972213066896">ഈ സൈറ്റ് സന്ദർശിക്കുന്നതിന് നിങ്ങൾ രക്ഷിതാവിനോട് അനുമതി ആവശ്യപ്പെട്ടു</translation>
@@ -661,6 +685,7 @@
<translation id="647261751007945333">ഉപകരണ നയങ്ങൾ</translation>
<translation id="6477321094435799029">Chrome, ഈ പേജിൽ അസാധാരണമായ കോഡ് കണ്ടെത്തിയതിനാൽ നിങ്ങളുടെ വ്യക്തിഗത വിവരങ്ങൾ (ഉദാഹരണത്തിന്, പാസ്‌വേഡുകളും ഫോൺ നമ്പറുകളും ക്രെഡിറ്റ് കാർഡുകളും പോലുള്ളവ) പരിരക്ഷിക്കുന്നതിന് അതിനെ ബ്ലോക്കുചെയ്‌തു.</translation>
<translation id="6489534406876378309">ക്രാഷുകൾ അപ്‌ലോഡുചെയ്യുന്നത് ആരംഭിക്കുക</translation>
+<translation id="6507833130742554667">ക്രെഡിറ്റ് കാർഡുകളും ഡെബിറ്റ് കാർഡുകളും സ്വീകരിക്കുന്നു.</translation>
<translation id="6508722015517270189">Chrome റീസ്‌റ്റാർട്ടുചെയ്യുക</translation>
<translation id="6529602333819889595">&amp;ഇല്ലാതാക്കുന്നത് വീണ്ടും ചെയ്യുക</translation>
<translation id="6534179046333460208">ഫിസിക്കൽ വെബ് നിർദ്ദേശങ്ങൾ</translation>
@@ -669,13 +694,13 @@
<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="657639383826808334">ഈ ഉള്ളടക്കം, നിങ്ങളുടെ വിവരങ്ങൾ മോഷ്‌ടിക്കുകയോ ഇല്ലാതാക്കുയോ ചെയ്യുന്ന തരത്തിലുള്ള സോഫ്‌റ്റ്‌വെയറുകൾ ഉപകരണത്തിൽ ഇൻസ്‌റ്റാൾചെയ്യാൻ ശ്രമിക്കാനിടയുണ്ട്. <ph name="BEGIN_LINK" />എന്തായാലും കാണിക്കുക<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">എൻക്രിപ്‌ഷൻ ഓപ്‌ഷനുകൾ</translation>
<translation id="662080504995468778">തുടരുക</translation>
<translation id="6626291197371920147">ശരിയായ കാർഡ് നമ്പർ ചേർക്കുക</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> തിരയൽ</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> എന്ന സൈറ്റിലെ നിലവിലുള്ള ആക്രമികൾ നിങ്ങളുടെ വിവരങ്ങൾ ഇല്ലാതാക്കാനോ മോഷ്‌ടിക്കാനോ ഇടയുള്ള (ഉദാഹരണത്തിന്, ഫോട്ടോകൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ, ക്രെഡിറ്റ് കാർഡുകൾ മുതലായവ) അപകടകരമായ പ്രോഗ്രാമുകൾ Mac-ൽ ഇൻസ്‌റ്റാളുചെയ്യാൻ ശ്രമിച്ചേക്കാം. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടുതലറിയുക<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">ഈ നയം ഒഴിവാക്കി.</translation>
+<translation id="6657585470893396449">പാസ്‌വേഡ്</translation>
<translation id="6671697161687535275">Chromium-ത്തിൽ നിന്ന് ഫോം നിർദ്ദേശം നീക്കംചെയ്യണോ?</translation>
<translation id="6685834062052613830">സൈൻ ഔട്ട് ചെയ്‌ത്, സജ്ജമാക്കൽ പൂർത്തിയാക്കുക</translation>
<translation id="6710213216561001401">കഴിഞ്ഞ</translation>
@@ -706,7 +731,6 @@
<translation id="6970216967273061347">ജില്ല</translation>
<translation id="6973656660372572881">സ്ഥിരമായ പ്രോക്‌സി സെർവറുകളും ഒരു സ്‌ക്രിപ്റ്റ് URL-ഉം വ്യക്തമാക്കിയിരിക്കുന്നു.</translation>
<translation id="6989763994942163495">വിപുലമായ ക്രമീകരണങ്ങൾ കാണിക്കുക...</translation>
-<translation id="7000990526846637657">ചരിത്ര എൻട്രികളൊന്നും കണ്ടില്ല</translation>
<translation id="7012363358306927923">ചൈന UnionPay</translation>
<translation id="7012372675181957985">നിങ്ങളുടെ Google അക്കൗണ്ടിന് <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> എന്നതിൽ മറ്റ് തരത്തിലുള്ള ബ്രൗസിംഗ് ചരിത്രമുണ്ടായിരിക്കാം</translation>
<translation id="7029809446516969842">പാസ്‌വേഡുകള്‍</translation>
@@ -721,7 +745,9 @@
<translation id="7129409597930077180">ഈ വിലാസത്തിലേക്ക് ഷിപ്പ് ചെയ്യാൻ കഴിയില്ല. മറ്റൊരു വിലാസം തിരഞ്ഞെടുക്കുക.</translation>
<translation id="7138472120740807366">ഡെലിവറി രീതി</translation>
<translation id="7139724024395191329">എമിറേറ്റ്</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> എണ്ണവും}other{<ph name="PAYMENT_METHOD_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> എണ്ണവും}}</translation>
<translation id="7155487117670177674">പേയ്‌മെന്റ് സുരക്ഷിതമല്ല</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> എണ്ണവും}other{<ph name="SHIPPING_OPTION_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> എണ്ണവും}}</translation>
<translation id="7179921470347911571">ഇപ്പോള്‍ വീണ്ടും സമാരംഭിക്കുക</translation>
<translation id="7180611975245234373">പുതുക്കുക</translation>
<translation id="7182878459783632708">നയങ്ങളൊന്നും സജ്ജമാക്കിയിട്ടില്ല</translation>
@@ -762,6 +788,7 @@
<translation id="7514365320538308">ഡൗൺലോഡുചെയ്യുക</translation>
<translation id="7518003948725431193">വെബ് വിലാസത്തിനായി വെബ്‌പേജൊന്നും കണ്ടെത്തിയില്ല: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">ഈ സൈറ്റിലേക്കുള്ള നിങ്ങളുടെ കണക്ഷൻ സ്വകാര്യമല്ല</translation>
<translation id="7535087603100972091">മൂല്യം</translation>
<translation id="7537536606612762813">നിർബന്ധിതം</translation>
<translation id="7542403920425041731">സ്ഥിരീകരിച്ചുകഴിഞ്ഞാൽ, നിങ്ങളുടെ കാർഡ് വിശദാംശങ്ങൾ ഈ സൈറ്റുമായി പങ്കിടും.</translation>
@@ -771,7 +798,6 @@
<translation id="7552846755917812628">ഇനിപ്പറയുന്ന നുറുങ്ങുകൾ പരീക്ഷിക്കൂ:</translation>
<translation id="7554791636758816595">പുതിയ ടാബ്</translation>
<translation id="7567204685887185387">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; സെർവറിന്റെ സുരക്ഷ സർട്ടിഫിക്കറ്റ് വഞ്ചനാപരമായി ഇഷ്യൂ ചെയ്‌തിരിക്കാം. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> എണ്ണവും}other{<ph name="CONTACT_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> എണ്ണവും}}</translation>
<translation id="7568593326407688803">ഈ പേജ്<ph name="ORIGINAL_LANGUAGE" />ലാണ് നിങ്ങളത് വിവര്‍‌ത്തനം ചെയ്യാന്‍‌ താല്‍‌പ്പര്യപ്പെടുന്നോ?</translation>
<translation id="7569952961197462199">Chrome-ൽ നിന്ന് ക്രെഡിറ്റ് കാർഡ് നീക്കംചെയ്യണോ?</translation>
<translation id="7569983096843329377">കറുപ്പ്</translation>
@@ -798,9 +824,11 @@
<translation id="7714464543167945231">സര്‍‌ട്ടിഫിക്കറ്റ്</translation>
<translation id="7716147886133743102">നിങ്ങളുടെ അ‌ഡ്‌മിനി‌സ്‌ട്രേറ്റർ ബ്ലോക്കുചെയ്‌തു</translation>
<translation id="7716424297397655342">ഈ സൈറ്റ് കാഷെയിൽ നിന്ന് ലോഡുചെയ്യാനാകില്ല</translation>
+<translation id="774634243536837715">അപകടകരമായ ഉള്ളടക്കം ബ്ലോക്കുചെയ്‌തു.</translation>
<translation id="7752995774971033316">നിയന്ത്രിക്കാനാകാത്തത്</translation>
<translation id="7755287808199759310">നിങ്ങൾക്ക് വേണ്ടി ഇത് അൺബ്ലോക്കുചെയ്യാൻ രക്ഷിതാവിന് കഴിയും</translation>
<translation id="7758069387465995638">ഫയർവാളോ ആന്റി വൈറസ് സോഫ്‌റ്റ്‌വെയറോ കണക്ഷൻ ബ്ലോക്കുചെയ്‌തിട്ടുണ്ടാകാം.</translation>
+<translation id="7759163816903619567">പ്രദർശിപ്പിച്ച ഡൊമെയ്ന്‍:</translation>
<translation id="7761701407923456692">സെര്‍വറിന്‍റെ സര്‍ട്ടിഫിക്കറ്റ് URL മായി പൊരുത്തപ്പെടുന്നില്ല.</translation>
<translation id="7763386264682878361">പേയ്‌മെന്റ് മാനിഫെസ്‌റ്റ് പാർസർ</translation>
<translation id="7764225426217299476">വിലാസം ചേർക്കുക</translation>
@@ -815,6 +843,7 @@
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />' എന്നതിന്റെ <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> കണ്ടു</translation>
<translation id="785549533363645510">എന്നിരുന്നാലും നിങ്ങൾ അദൃശ്യനല്ല. ആൾമാറാട്ടത്തിലേയ്‌ക്ക് പോകുന്നത്, നിങ്ങളുടെ തൊഴിൽ ദാതാവിൽ നിന്നോ ഇന്റർനെറ്റ് സേവന ദാതാവിൽ നിന്നോ നിങ്ങൾ സന്ദർശിക്കുന്ന വെബ്‌സൈറ്റുകളിൽ നിന്നോ ഉള്ള ബ്രൗസിംഗിനെ മറയ്‌ക്കില്ല.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">ഡെബിറ്റ് കാർഡുകളും പ്രീപെയ്ഡ് കാർഡുകളും സ്വീകരിക്കുന്നു.</translation>
<translation id="7887683347370398519">നിങ്ങളുടെ CVC പരിശോധിച്ച് വീണ്ടും ശ്രമിക്കുക</translation>
<translation id="79338296614623784">ശരിയായ ഒരു ഫോൺ നമ്പർ നൽകുക</translation>
<translation id="7935318582918952113">DOM ഡിസ്‌റ്റിലർ</translation>
@@ -834,7 +863,6 @@
<translation id="8041089156583427627">ഫീഡ്ബാക്ക് അയയ്ക്കുക</translation>
<translation id="8041940743680923270">ഗ്ലോബൽ ഡിഫോൾട്ട് ഉപയോഗിക്കുക (ചോദിക്കുക)</translation>
<translation id="8088680233425245692">ലേഖനം കാണുന്നത് പരാജയപ്പെട്ടു.</translation>
-<translation id="8089520772729574115">ഒരു MB-യിൽ കുറവാണ്</translation>
<translation id="8091372947890762290">സെർവറിൽ സജീവമാക്കൽ തീർപ്പാക്കിയിട്ടില്ല</translation>
<translation id="8118489163946903409">പേയ്‌മെന്റ് രീതി</translation>
<translation id="8131740175452115882">സ്ഥിരീകരിക്കുക</translation>
@@ -846,13 +874,16 @@
<translation id="8194797478851900357">&amp;നീക്കുന്നത് പഴയപടിയാക്കുക</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" എന്ന ഐഡിയുള്ള വിപുലീകരണത്തിന്റെ അപ്‌ഡേറ്റ് URL അസാധുവാണ്.</translation>
<translation id="8202097416529803614">ഓർഡർ സംഗ്രഹം</translation>
+<translation id="8205463626947051446">അനാവശ്യമായ പരസ്യങ്ങൾ സൈറ്റ് കാണിക്കാൻ സാധ്യതയുണ്ട്</translation>
<translation id="8218327578424803826">നൽകിയിരിക്കുന്ന ലൊക്കേഷൻ:</translation>
<translation id="8225771182978767009">ഈ കമ്പ്യൂട്ടർ സജ്ജമാക്കിയ വ്യക്തി, ഈ സൈറ്റ് ബ്ലോക്കുചെയ്യാൻ തീരുമാനിച്ചിരുന്നു.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">പുതിയൊരു അദൃശ്യ ടാബിൽ പേജ് തുറക്കുക</translation>
<translation id="8241707690549784388">നിങ്ങള്‍ അന്വേഷിക്കുന്ന പേജ് നിങ്ങള്‍ രേഖപ്പെടുത്തിയ വിവരങ്ങള്‍ ഉപയോഗിക്കുന്നു. ആ പേജിലേക്ക് മടങ്ങുന്നത് നിങ്ങള്‍ ആവര്‍ത്തിക്കാവുന്ന ഏതെങ്കിലും പ്രവൃത്തിക്ക് കാരണമായേക്കും. തുടരാന്‍ നിങ്ങള്‍ ആഗ്രഹിക്കുന്നുണ്ടോ?</translation>
+<translation id="8241712895048303527">ഈ സൈറ്റിൽ ബ്ലോക്കുചെയ്യുക</translation>
<translation id="8249320324621329438">അവസാനം ലഭ്യമായത്:</translation>
<translation id="8253091569723639551">ബില്ലിംഗ് വിലാസം ആവശ്യമാണ്</translation>
-<translation id="8261506727792406068">ഇല്ലാതാക്കൂ</translation>
+<translation id="8261506727792406068">ഇല്ലാതാക്കുക</translation>
<translation id="8289355894181816810">ഇത് അർത്ഥമാക്കുന്നത് എന്താണെന്ന് നിങ്ങൾക്ക് ഉറപ്പില്ലെങ്കിൽ നെറ്റ്‌വർക്ക് അഡ്‌മിനിസ്‌ട്രേറ്ററെ ബന്ധപ്പെടുക.</translation>
<translation id="8293206222192510085">ബുക്ക്‌മാര്‍‌ക്ക് ചേര്‍‌ക്കുക</translation>
<translation id="8294431847097064396">ഉറവിടം</translation>
@@ -860,7 +891,6 @@
<translation id="8308427013383895095">നെറ്റ്വര്‍ക്ക് കണക്ഷനിലെ ഒരു പിശക് കാരണം വിവര്‍ത്തനം പരാജയപ്പെട്ടു.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ഹോസ്‌റ്റിലേക്കുള്ള ആക്‌സസ്സ് നിരസിച്ചു</translation>
<translation id="834457929814110454">നിങ്ങളുടെ സുരക്ഷയെ ബാധിക്കാനിടയുണ്ടെന്ന് മനസ്സിലാക്കുകയാണെങ്കിൽ, ദോഷകരമായ പ്രോഗ്രാമുകൾ നീക്കംചെയ്യുന്നതിനു മുമ്പ് <ph name="BEGIN_LINK" />ഈ സൈറ്റ് നിങ്ങൾക്ക് സന്ദർശിക്കാം<ph name="END_LINK" />.</translation>
-<translation id="8344669043927012510">അദൃശ്യതാ സംവിധാനത്തിൽ പേജ് തുറക്കുക (⇧⌘N)</translation>
<translation id="8349305172487531364">ബുക്മാര്‍ക്ക് ബാര്‍</translation>
<translation id="8363502534493474904">ഫ്ലൈറ്റ് മോഡ് ഓഫാക്കുന്നു</translation>
<translation id="8364627913115013041">സജ്ജമാക്കിയിട്ടില്ല.</translation>
@@ -870,6 +900,7 @@
<translation id="8398259832188219207">ക്രാഷ് റിപ്പോർട്ട് <ph name="UPLOAD_TIME" />-ന് അപ്‌ലോഡുചെയ്‌തു</translation>
<translation id="8412145213513410671">ക്രാഷുകള്‍ <ph name="CRASH_COUNT" /></translation>
<translation id="8412392972487953978">നിങ്ങള്‍ സമാന പാസ്ഫ്രെയ്സ് രണ്ടുതവണ നല്‍കണം.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ക്രമീകരണങ്ങള്‍</translation>
<translation id="8433057134996913067">ഇത് നിങ്ങളെ മിക്ക വെബ്‌സൈറ്റുകളിൽ നിന്നും സൈൻ ഔട്ട് ചെയ്യിക്കും.</translation>
<translation id="8437238597147034694">&amp;നീക്കുന്നത് പഴയപടിയാക്കുക</translation>
@@ -877,8 +908,9 @@
<translation id="8483780878231876732">നിങ്ങളുടെ Google അക്കൗണ്ടിൽ നിന്ന് കാർഡുകൾ ഉപയോഗിക്കാൻ, Chrome-ൽ സൈൻ ഇൻ ചെയ്യുക</translation>
<translation id="8488350697529856933">ഇതിന് ബാധകമാക്കുന്നു</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> പ്രതികരിക്കാൻ കൂടുതൽ സമയമെടുത്തു.</translation>
-<translation id="8532105204136943229">കാലഹരണപ്പെടുന്ന വർഷം</translation>
+<translation id="8503813439785031346">ഉപയോക്തൃനാമം</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="8571890674111243710"><ph name="LANGUAGE" /> ലേക്ക് പേജ് വിവര്‍‌ത്തനം ചെയ്യുന്നു...</translation>
<translation id="858637041960032120">ഫോൺ നം. ചേർക്കൂ
@@ -886,6 +918,7 @@
<translation id="859285277496340001">സാക്‍ഷ്യപത്രം അസാധുവാക്കിയോ എന്ന് പരിശോധിക്കുന്നതിന് അത് ഒരു മെക്കാനിസത്തെയും സൂചിപ്പിക്കുന്നില്ല.</translation>
<translation id="8620436878122366504">നിങ്ങളുടെ രക്ഷിതാക്കൾ ഇതുവരെ അംഗീകാരം നൽകിയിട്ടില്ല</translation>
<translation id="8647750283161643317">എല്ലാം സ്ഥിരമായി പുനഃസജ്ജമാക്കുക</translation>
+<translation id="8660471606262461360">Google പേയ്‌മെന്റിൽ നിന്ന്</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> ലേക്കുള്ള നിങ്ങളുടെ കണക്ഷന്‍ എന്‍‌ക്രിപ്റ്റ് ചെയ്തിട്ടില്ല.</translation>
<translation id="8718314106902482036">പേയ്‌മെന്റ് പൂർത്തിയായിട്ടില്ല</translation>
<translation id="8725066075913043281">വീണ്ടും ശ്രമിക്കുക</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">സൂം ഇന്‍</translation>
<translation id="8931333241327730545">ഈ കാർഡ് നിങ്ങളുടെ Google അക്കൗണ്ടിൽ സംരക്ഷിക്കണോ?</translation>
<translation id="8932102934695377596">നിങ്ങളുടെ ക്ലോക്ക് വളരെ പിന്നിലാണ്</translation>
+<translation id="8938939909778640821">ക്രെഡിറ്റ് കാർഡുകളും പ്രീപെയ്ഡ് കാർഡുകളും സ്വീകരിക്കുന്നു</translation>
<translation id="8971063699422889582">സെര്‍വറിന്‍റെ സര്‍ട്ടിഫിക്കറ്റ് കാലഹരണപ്പെട്ടു.</translation>
<translation id="8986494364107987395">Google ലേക്ക് സ്വപ്രേരിതമായി ഉപയോഗ സ്ഥിതിവിവരക്കണക്കുകളും ക്രാഷ് റിപ്പോര്‍ട്ടുകളും അയയ്ക്കുക</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">ഈ സൈറ്റിൽ എല്ലായ്‌പ്പോഴും തടയുക</translation>
<translation id="9170848237812810038">‍&amp;പൂര്‍വാവസ്ഥയിലാക്കുക</translation>
<translation id="917450738466192189">സെര്‍വറിന്‍റെ സര്‍ട്ടിഫിക്കറ്റ് അസാധുവാണ്.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> എണ്ണവും}other{<ph name="SHIPPING_OPTION_PREVIEW" /> എന്നതും മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> എണ്ണവും}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> പിന്തുണയ്‌ക്കാത്ത ഒരു പ്രോട്ടോക്കോളാണ് ഉപയോഗിക്കുന്നത്.</translation>
<translation id="9205078245616868884">നിങ്ങളുടെ സമന്വയ പാസ്‌ഫ്രെയ്‌സ് ഉപയോഗിച്ച് ഡാറ്റ എൻക്രിപ്‌റ്റുചെയ്‌തു. സമന്വയം ആരംഭിക്കുന്നതിന് ഇത് നൽകുക.</translation>
<translation id="9207861905230894330">ലേഖനം ചേർക്കുന്നത് പരാജയപ്പെട്ടു.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ഫോം മായ്‌ക്കുക</translation>
<translation id="939736085109172342">പുതിയ ഫോള്‍ഡര്‍</translation>
-<translation id="941721044073577244">ഈ സൈറ്റ് സന്ദർശിക്കാൻ നിങ്ങൾക്ക് അനുമതിയില്ലെന്ന് തോന്നുന്നു</translation>
<translation id="969892804517981540">ഔദ്യോഗിക ബില്‍ഡ്</translation>
<translation id="975560348586398090">{COUNT,plural, =0{ഒന്നുമില്ല}=1{ഒരു ഇനം}other{# ഇനങ്ങൾ}}</translation>
+<translation id="981121421437150478">ഓഫ്‌ലൈൻ</translation>
<translation id="988159990683914416">വികാസക പതിപ്പ്</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_mr.xtb b/chromium/components/strings/components_strings_mr.xtb
index d60c4b80d0b..4364c895636 100644
--- a/chromium/components/strings/components_strings_mr.xtb
+++ b/chromium/components/strings/components_strings_mr.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">अन्य अॅप्स बंद करा</translation>
<translation id="1055184225775184556">&amp;जोडा पूर्ववत करा</translation>
<translation id="10614374240317010">कधीही जतन न केलेले</translation>
+<translation id="1066396345355680611">तुम्ही <ph name="SITE" /> आणि काही इतर साइट मधील प्रतिबंधित आशयामधील अॅक्सेस गमावू शकाल.</translation>
<translation id="106701514854093668">डेस्‍कटॉप बुकमार्क</translation>
<translation id="1074497978438210769">सुरक्षित नाही</translation>
<translation id="1080116354587839789">रूंदीत फिट करा</translation>
@@ -34,7 +35,7 @@
<translation id="1206967143813997005">खराब प्रारंभिक स्वाक्षरी</translation>
<translation id="1209206284964581585">आतासाठी लपवा</translation>
<translation id="121201262018556460">आपण <ph name="DOMAIN" /> वर पोहोचण्याचा प्रयत्न केलात, परंतु सर्व्हरने एक कमकुवत की असलेले प्रमाणपत्र सादर केले. आक्रमणकर्त्याने गोपनीय की तोडलेली असू शकते आणि सर्व्हर हे आपल्याला अपेक्षित असणारे सर्व्हर नसू शकते (आपण कदाचित आक्रमणकर्त्याशी संप्रेषण करत असाल).</translation>
-<translation id="1219129156119358924">सिस्टीम सुरक्षा</translation>
+<translation id="1219129156119358924">सिस्टम सुरक्षा</translation>
<translation id="1227224963052638717">अज्ञात धोरण.</translation>
<translation id="1227633850867390598">मूल्य लपवा</translation>
<translation id="1228893227497259893">चुकीचा अस्तित्व ओळखकर्ता</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">वाचन सूची</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" /> वाजता क्रॅश अहवाल कॅप्चर केला (अद्याप अपलोड केलेला नाही किंवा दुर्लक्ष केले)</translation>
<translation id="1281526147609854549"><ph name="ISSUER" /> ने दिलेले</translation>
-<translation id="1283919782143846010">धोकादायक आशय ब्लॉक केला</translation>
<translation id="1285320974508926690">या साइटचा कधीही भाषांतर करु नका</translation>
<translation id="129553762522093515">अलीकडे बंद केलेले</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />आपल्या कुकीज साफ करून पहा<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">नावनोंदणी डोमेन:</translation>
<translation id="1340482604681802745">घेण्याचा पत्ता</translation>
-<translation id="1344211575059133124">या साइटला भेट देण्यासाठी आपल्याला परवानगीची आवश्यकता आहे असे दिसते</translation>
<translation id="1344588688991793829">Chromium स्वयंभरण सेटिंग्ज...</translation>
<translation id="1348198688976932919">पुढील साइटमध्ये धोकादायक अॅप आहे</translation>
<translation id="1374468813861204354">सूचना</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="ISSUER" /> द्वारे <ph name="LOCALITY" /> स्थित <ph name="ORGANIZATION" /> ची ओळख सत्यापित केली गेली आहे.</translation>
<translation id="1426410128494586442">होय</translation>
<translation id="1430915738399379752">मुद्रण</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">या पृष्ठाची जतन केलेली (उदा. कालबाह्य होणारे ज्ञात) प्रत दर्शवा.</translation>
<translation id="1517433312004943670">फोन नंबर आवश्यक आहे</translation>
+<translation id="1517500485252541695">स्वीकारली जाणारी क्रेडिट आणि डेबिट कार्डे</translation>
<translation id="1519264250979466059">बिल्ड तारीख</translation>
+<translation id="1527263332363067270">कनेक्शनची वाट पाहत आहे...</translation>
<translation id="153384715582417236">सध्या इतकेच</translation>
<translation id="1549470594296187301">हे वैशिष्‍ट्य वापरण्‍यासाठी JavaScript सक्षम करणे आवश्‍यक आहे.</translation>
<translation id="1555130319947370107">निळा</translation>
@@ -99,14 +99,13 @@
<translation id="1728677426644403582">आपण वेब पृष्ठाचा स्रोत पाहत आहात</translation>
<translation id="173080396488393970">या प्रकारच्या कार्डला सहाय्य नाही</translation>
<translation id="1734864079702812349">Amex</translation>
-<translation id="1734878702283171397">सिस्टीम प्रशासकाशी संपर्क साधण्याचा प्रयत्न करा.</translation>
+<translation id="1734878702283171397">सिस्टम प्रशासकाशी संपर्क साधण्याचा प्रयत्न करा.</translation>
<translation id="1740951997222943430">वैध समाप्ती महिना प्रविष्ट करा</translation>
-<translation id="1745358365027406341">पृष्ठ नंतर डाउनलोड करा</translation>
<translation id="17513872634828108">खुले टॅब</translation>
<translation id="1753706481035618306">पृष्ठ क्रमांक</translation>
<translation id="1763864636252898013">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र आपल्या डिव्हाइसच्या ऑपरेटिंग प्रणालीद्वारे विश्वसनीय नाही. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows नेटवर्क निदान चालवून पहा<ph name="END_LINK" />.</translation>
-<translation id="1783075131180517613">कृपया आपले संंकालित सांकेतिक वाक्यांश अद्यतनित करा.</translation>
+<translation id="1783075131180517613">कृपया आपले संंकालित सांकेतिक वाक्यांश अपडेट करा.</translation>
<translation id="1787142507584202372">आपले खुले टॅब येथे दिसतात</translation>
<translation id="1789575671122666129">पॉपअप</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">आवश्यक फील्ड</translation>
<translation id="187918866476621466">प्रारंभ पृष्ठ उघडा</translation>
<translation id="1883255238294161206">सूची संकुचित करा</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">फिल्टर करणे</translation>
+<translation id="1916770123977586577">तुमच्या सेटिंग्ज या साइटवर लागू करण्यासाठी, हे पेज रीलोड करा</translation>
+<translation id="1919345977826869612">जाहिराती</translation>
<translation id="192020519938775529">{COUNT,plural, =0{काहीही नाही}=1{1 साइट}one{# साइट}other{# साइट}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> दुव्याकडे जा</translation>
+<translation id="1948773908305951926">स्वीकारली जाणारी प्रीपेड कार्डे</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> बुकमार्क</translation>
<translation id="1973335181906896915">क्रमीकरण त्रुटी</translation>
<translation id="1974060860693918893">प्रगत</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP त्रुटी</translation>
<translation id="2270484714375784793">फोन नंबर</translation>
<translation id="2282872951544483773">अनुपलब्ध प्रयोग</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> आयटम}one{<ph name="ITEM_COUNT" /> आयटम}other{<ph name="ITEM_COUNT" /> आयटम}}</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>
<translation id="2337852623177822836">सेटिंग तुमच्या प्रशासकाने नियंत्रित केलेली आहे</translation>
<translation id="2354001756790975382">इतर बुकमार्क</translation>
<translation id="2354430244986887761">Google सुरक्षित ब्राउझिंगला अलीकडे <ph name="SITE" />मध्ये <ph name="BEGIN_LINK" />हानिकारक अॅप्स सापडले<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">सुरू ठेवा</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> वाजता कॅप्चर केलेला क्रॅश अहवाल अपलोड केला नाही</translation>
<translation id="2367567093518048410">दर्जा</translation>
-<translation id="237718015863234333">कोणतेही UI विकल्प उपलब्ध नाहीत</translation>
<translation id="2384307209577226199">एंटरप्राइझ डीफॉल्ट</translation>
<translation id="2386255080630008482">सर्व्हरचे प्रमाणपत्र निरस्त केले गेले.</translation>
<translation id="2392959068659972793">कोणतेही मूल्य सेट केल्याशिवाय धोरणे दर्शवा</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">चेक केल्यास, अधिक जलद फॉर्म भरण्यासाठी या डिव्हाइसवर Chromium आपल्या कार्डची एक प्रत संचयित करेल.</translation>
<translation id="2498091847651709837">नवीन कार्ड स्कॅन करा</translation>
<translation id="2501278716633472235">परत जा</translation>
+<translation id="2503184589641749290">स्वीकारली जाणारी डेबिट आणि प्रीपेड कार्डे</translation>
<translation id="2515629240566999685">आपल्या क्षेत्रातील सिग्नल तपासणे</translation>
-<translation id="2516305470678292029">UI विकल्प</translation>
<translation id="2539524384386349900">शोधा</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> नी एक अवैध प्रतिसाद पाठविला.</translation>
<translation id="2556876185419854533">&amp; संपादित करा पूर्ववत करा</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">शिपिंग पद्धत</translation>
<translation id="277499241957683684">डिव्हाइस रेकॉर्ड गहाळ</translation>
<translation id="2784949926578158345">कनेक्शन रीसेट केले.</translation>
+<translation id="2788784517760473862">क्रेडिट कार्डे स्वीकारली जातात</translation>
<translation id="2794233252405721443">साइट अवरोधित केली</translation>
<translation id="2799020568854403057">पुढील साइटमध्ये हानिकारक अॅप आहे</translation>
<translation id="2803306138276472711">Google सुरक्षित ब्राउझिंगला अलीकडे <ph name="SITE" /> वर <ph name="BEGIN_LINK" />मालवेअर आढळले आहे<ph name="END_LINK" />. सामान्यतः सुरक्षित असलेल्या वेबसाइट काहीवेळा मालवेअरमुळे संक्रमित झालेल्या असतात.</translation>
<translation id="2824775600643448204">पत्ता आणि शोध बार</translation>
<translation id="2826760142808435982">कनेक्शन <ph name="CIPHER" /> वापरून आणि की विनिमय तंत्र म्हणून <ph name="KX" /> वापर कूटबद्ध आणि प्रमाणीकृत केले आहे.</translation>
<translation id="2835170189407361413">फॉर्म क्लिअर करा</translation>
+<translation id="2851634818064021665">या साइटला भेट देण्यासाठी तुम्हाला परवानगीची गरज आहे</translation>
<translation id="2856444702002559011">हल्लेखोर कदाचित तुमची माहिती (उदाहरणार्थ पासवर्ड, संदेश किंवा क्रेडिट कार्ड) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> मधून चोरण्याचा प्रयत्न करत असतील. <ph name="BEGIN_LEARN_MORE_LINK" />आणखी जाणून घ्या<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">रीलोड करु नका</translation>
-<translation id="2900469785430194048">हे वेबपृष्ठ प्रदर्शित करण्याचा प्रयत्न करताना Google Chrome ची मेमरी संपली आहे.</translation>
<translation id="2909946352844186028">एक नेटवर्क बदल आढळला.</translation>
<translation id="2916038427272391327">अन्य प्रोग्राम बंद करा</translation>
<translation id="2922350208395188000">सर्व्हरचे प्रमाणपत्र तपासणे शक्य नाही.</translation>
<translation id="2928905813689894207">बिलिंग पत्ता</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> आणखी}one{<ph name="SHIPPING_ADDRESS_PREVIEW" />आणि <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> आणखी}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> आणखी}}</translation>
<translation id="2941952326391522266">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र <ph name="DOMAIN2" /> वरील आहे. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation>
<translation id="2948083400971632585">आपण सेटिंग्ज पृष्ठावरून एका कनेक्शनसाठी कॉन्फिगर केलेले कोणतेही प्रॉक्सी अक्षम करू शकता.</translation>
<translation id="2955913368246107853">शोध बार बंद करा</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">सक्षम केल्‍यास, Chromium जलदपणे फॉर्म भरण्‍यासाठी आपल्‍या कार्डची एक प्रत या डिव्‍हाइसवर संग्रहित करेल.</translation>
<translation id="2985398929374701810">वैध पत्ता प्रविष्ट करा</translation>
<translation id="2986368408720340940">ही पिकअप पद्धत उपलब्ध नाही. वेगळी पद्धत वापरून पहा.</translation>
@@ -263,7 +267,7 @@
<translation id="3037605927509011580">च्चक!</translation>
<translation id="3041612393474885105">प्रमाणपत्र माहिती...</translation>
<translation id="3063697135517575841">Chrome यावेळी आपल्या कार्डची पुष्टी करण्यात अक्षम होते. कृपया नंतर पुन्हा प्रयत्न करा.</translation>
-<translation id="3064966200440839136">बाह्य अनुप्रयोग द्वारे देय देण्यासाठी गुप्त मोड सोडत आहे. सुरु ठेवायचे?</translation>
+<translation id="3064966200440839136">बाह्य अॅप्लिकेशन द्वारे देय देण्यासाठी गुप्त मोड सोडत आहे. सुरु ठेवायचे?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{काहीही नाही}=1{1 पासवर्ड}one{# पासवर्ड}other{# पासवर्ड}}</translation>
<translation id="3093245981617870298">आपण ऑफलाइन आहात.</translation>
<translation id="3105172416063519923">मालमत्ता आयडी:</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">गुप्त मोडमध्‍ये आपण पाहता ती पृष्‍ठे आपण आपले सर्व गुप्त टॅब बंद केल्‍यानंतर आपला ब्राउझर इतिहास, कुकी स्टोअर किंवा शोध इतिहासामध्‍ये असणार नाहीत. आपण डाउनलोड करता त्या कोणत्याही फायली किंवा आपण केलेले बुकमार्क ठेवले जातील.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">बेट</translation>
-<translation id="317583078218509884">पृष्‍ठ रीलोड केल्‍यानंतर नवीन साइट परवानग्या सेटिंग्‍ज प्रभावी होतील.</translation>
<translation id="3176929007561373547">प्रॉक्सी सर्व्हर कार्य करीत आहे हे सुनिश्चित करण्यासाठी आपल्या प्रॉक्सी सेटिंग्ज तपासा
किंवा आपल्या नेटवर्क प्रशासकाशी संपर्क साधा. आपण प्रॉक्सी सर्व्हर वापरत
आहात यावर आपला विश्वास नसल्यास:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">गुप्त मोडमध्‍ये पृष्ठ उघडा</translation>
<translation id="320323717674993345">पेमेंट रद्द करा</translation>
<translation id="3207960819495026254">बुकमार्क केलेली</translation>
<translation id="3225919329040284222">सर्व्हरने असे प्रमाणपत्र सादर केले आहे जे अंगभूत अपेक्षांशी जुळत नाही. या अपेक्षा आपल्याला संरक्षित करण्यासाठी विशिष्ट, उच्च-सुरक्षिततेच्या वेबसाइटसाठी समाविष्ट केल्या आहेत.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;पुनर्क्रमित करा पूर्ववत करा</translation>
<translation id="3282497668470633863">कार्डवर नाव जोडा</translation>
<translation id="3286538390144397061">त्वरित रीस्टार्ट करा</translation>
+<translation id="3287510313208355388">ऑनलाइन असताना डाउनलोड करा</translation>
<translation id="3303855915957856445">कोणतेही शोध परिणाम आढळले नाहीत</translation>
<translation id="3305707030755673451">आपला डेटा आपल्या संकालन सांकेतिक वाक्यांशासह <ph name="TIME" /> वाजता कूटबद्ध केला होता. संकालन सुरु करण्यासाठी तो प्रविष्ट करा.</translation>
<translation id="3320021301628644560">बिलिंग पत्ता जोडा</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">मध्यंतर प्राप्त करा:</translation>
<translation id="3462200631372590220">प्रगत लपवा</translation>
<translation id="3467763166455606212">कार्डधारकाचे नाव आवश्यक</translation>
-<translation id="3478058380795961209">कालबाह्यता महिना:</translation>
<translation id="3479539252931486093">हे अनपेक्षित होते? <ph name="BEGIN_LINK" />आम्हाला कळवा<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">सध्या नाही</translation>
<translation id="3498215018399854026">याक्षणी आम्ही आपल्या पालकांपर्यंत पोहोचू शकलो नाही. कृपया पुन्हा प्रयत्न करा.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;कृपया अ‍ॅपच्या &lt;strong&gt;सेटिंग्ज&lt;/strong&gt; मधील &lt;strong&gt;सामान्य&lt;/strong&gt; विभागातून तारीख आणि वेळ नीट करा.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />आणखी जाणून घ्या<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">क्रेडिट आणि प्रीपेड कार्डे स्वीकारली जातात.</translation>
<translation id="3582930987043644930">नाव जोडा</translation>
<translation id="3583757800736429874">&amp;हलवा पुन्हा करा</translation>
<translation id="3586931643579894722">तपशील लपवा</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">आपण हे वारंवार पहात असल्यास, हे वापरून पहा <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">पुनरावृत्ती</translation>
<translation id="3678029195006412963">विनंती स्वाक्षरीकृत करणे शक्य झाले नाही</translation>
+<translation id="3678529606614285348">पेज एका नवीन गुप्त विंडोमध्ये उघडा (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">क्रॅश अहवाल <ph name="CRASH_TIME" /> वाजता कॅप्चर केला, <ph name="UPLOAD_TIME" /> वाजता अपलोड केला</translation>
<translation id="3681007416295224113">प्रमाणपत्र माहिती...</translation>
<translation id="3690164694835360974">लॉग इन सुरक्षित नाही</translation>
-<translation id="3693415264595406141">संकेतशब्द:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">लोड करीत आहे...</translation>
<translation id="3712624925041724820">परवाने संपुष्टात</translation>
@@ -373,20 +376,23 @@
<translation id="3748148204939282805"><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="375403751935624634">सर्व्हर त्रुटीमुळे भाषांतर अयशस्वी झाला.</translation>
<translation id="3759461132968374835">आपण अलीकडे कोणतेही क्रॅश नोंदवले नाहीत. क्रॅश नोंदवणे अक्षम असताना झालेले क्रॅश येथे दिसून येणार नाहीत.</translation>
+<translation id="3765032636089507299">सुरक्षित ब्राउझिंग पेज तयार होत आहे.</translation>
<translation id="3778403066972421603">तुम्हाला हे कार्ड तुमच्या Google खात्यामध्ये आणि या डिव्हाइसवर सेव्ह करायचे आहे का?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">समाप्त होते: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="382518646247711829">आपण प्रॉक्सी सर्व्हर वापरत असल्यास...</translation>
<translation id="3828924085048779000">रिक्त सांकेतिक वाक्यांशाची परवानगी नाही.</translation>
<translation id="385051799172605136">मागील</translation>
-<translation id="3858027520442213535">तारीख आणि वेळ अद्यतनित करा</translation>
+<translation id="3858027520442213535">तारीख आणि वेळ अपडेट करा</translation>
<translation id="3884278016824448484">संघर्ष करणारा डिव्हाइस अभिज्ञापक</translation>
<translation id="3885155851504623709">पॅरिश</translation>
<translation id="3886446263141354045">या साइटवर प्रवेश करण्याची आपली विनंती <ph name="NAME" /> कडे पाठविली गेली आहे</translation>
<translation id="3890664840433101773">ईमेल जोडा</translation>
<translation id="3901925938762663762">कार्ड कालबाह्य झाले आहे</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">क्रॅश तक्रार आयडी अपलोड केला<ph name="CRASH_ID" /> (स्थानिक क्रॅश आयडी: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">हा सर्व्हर <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षा प्रमाणपत्र विषय पर्यायी नावांचा उल्लेख करत नाही. हे कदाचित चुकीच्या कॉंफिगरेशनमुळे होत आहे किंवा आक्रमणकर्ता तुमच्या कनेक्शनमध्ये अडथळा आणत आहे.</translation>
+<translation id="3949601375789751990">तुमचा ब्राउझिंग इतिहास येथे दिसतो</translation>
<translation id="3963721102035795474">वाचक मोड</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{काहीही नाही}=1{1 साइटकडून }one{# साइटकडून }other{# साइटकडून }}</translation>
<translation id="397105322502079400">गणना करत आहे...</translation>
@@ -403,7 +409,7 @@
<translation id="4098354747657067197">भ्रामक साइट पुढे आहे</translation>
<translation id="4103249731201008433">डिव्हाइस सिरीयल क्रमांक अवैध आहे</translation>
<translation id="410351446219883937">ऑटोप्ले</translation>
-<translation id="4103763322291513355">आपल्या सिस्टीम प्रशासकाद्वारे प्रवर्तित काळ्यासूचीतील URLs आणि अन्य धोरणांची सूची पाहण्यासाठी &lt;strong&gt;chrome://policy&lt;/strong&gt; ला भेट द्या.</translation>
+<translation id="4103763322291513355">आपल्या सिस्टम प्रशासकाद्वारे प्रवर्तित काळ्यासूचीतील URLs आणि अन्य धोरणांची सूची पाहण्यासाठी &lt;strong&gt;chrome://policy&lt;/strong&gt; ला भेट द्या.</translation>
<translation id="4115378294792113321">किरमिजी</translation>
<translation id="4116663294526079822">या साइटवर नेहमी अनुमती द्या</translation>
<translation id="4117700440116928470">धोरण कक्षा समर्थित नाही.</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">साइट सेटिंग्ज</translation>
<translation id="4169947484918424451">Chromium ने हे कार्ड जतन करावे असे आपण इच्छिता?</translation>
<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="4196861286325780578">&amp;हलवा पुन्हा करा</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />फायरवॉल आणि अँटीव्हायरस कॉन्फिगरेशन तपासणे<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">क्रॅश होते</translation>
@@ -435,17 +443,17 @@
<translation id="4325863107915753736">लेख शोधण्यात अयशस्वी</translation>
<translation id="4326324639298822553">आपली कालबाह्यता तारीख तपासा आणि पुन्हा प्रयत्न करा</translation>
<translation id="4331708818696583467">सुरक्षित नाही</translation>
-<translation id="4356973930735388585">या साइट वरील आक्रमणकर्ते आपली माहिती (उदाहरणार्थ, फोटो, संकेतशब्द, संदेश आणि क्रेडिट कार्ड) चोरणारे किंवा हटविणारे धोकादायक प्रोग्राम आपल्या संगणकावर स्थापित करण्‍याचा प्रयत्न करू शकतात.</translation>
+<translation id="4356973930735388585">या साइट वरील आक्रमणकर्ते आपली माहिती (उदाहरणार्थ, फोटो, संकेतशब्द, संदेश आणि क्रेडिट कार्ड) चोरणारे किंवा हटविणारे धोकादायक प्रोग्राम आपल्या संगणकावर इंस्टॉल करण्‍याचा प्रयत्न करू शकतात.</translation>
<translation id="4372948949327679948">अपेक्षित <ph name="VALUE_TYPE" /> मूल्य.</translation>
<translation id="4377125064752653719">आपण <ph name="DOMAIN" /> वर पोहोचण्याचा प्रयत्न केला, परंतु सर्व्हरने सादर केलेले प्रमाणपत्र त्याच्या जारीकर्त्याद्वारे मागे घेतले गेले आहे. याचा अर्थ सर्व्हरने सादर केलेल्या सुरक्षा क्रेडेन्शियलवर अजिबात ठेवला जाऊ नये. आपण कदाचित आक्रमणकर्त्याशी संप्रेषण करत आहात.</translation>
-<translation id="4381091992796011497">वापरकर्ता नाव:</translation>
<translation id="4394049700291259645">अक्षम करा</translation>
<translation id="4406896451731180161">शोध परिणाम</translation>
<translation id="4424024547088906515">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र Chrome द्वारे विश्वसनीय नाही. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> ने आपले लॉग इन प्रमाणपत्र स्वीकारले नाही किंवा कदाचित प्रदान केले गेले नसावे.</translation>
<translation id="443673843213245140">प्रॉक्सीचा वापर अक्षम करण्‍यात आला आहे पण एक सुस्पष्‍ट प्रॉक्सी कॉन्‍फिगरेशन निर्दिष्‍ट करण्‍यात आले आहे.</translation>
+<translation id="445100540951337728">डेबिट कार्डे स्वीकारली जातात</translation>
<translation id="4506176782989081258">प्रमाणीकरण त्रुटी: <ph name="VALIDATION_ERROR" /></translation>
-<translation id="4506599922270137252">सिस्टीम प्रशासकाशी संपर्क साधणे</translation>
+<translation id="4506599922270137252">सिस्टम प्रशासकाशी संपर्क साधणे</translation>
<translation id="450710068430902550">प्रशासकासह सामायिक करीत आहे</translation>
<translation id="4515275063822566619">कार्ड आणि पत्ते Chrome आणि आपल्या Google खात्याकडील (<ph name="ACCOUNT_EMAIL" />) आहेत. आपण त्यांना <ph name="BEGIN_LINK" />सेटिंग्‍ज<ph name="END_LINK" /> मधून व्यवस्थापित करू शकता.</translation>
<translation id="4522570452068850558">तपशील</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Chrome मधून पत्ता काढायचा?</translation>
<translation id="4592951414987517459">आपले <ph name="DOMAIN" /> वरील कनेक्शन आधुनिक सायफर सूट वापरून कूटबद्ध केलेले आहे.</translation>
<translation id="4594403342090139922">&amp;हटवा पूर्ववत करा</translation>
+<translation id="4611292653554630842">लॉग इन करा</translation>
<translation id="4619615317237390068">अन्य डिव्हाइसेसमधील टॅब</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याच्या सुरक्षितता प्रमाणपत्रात त्रुटी आहेत. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation>
<translation id="4690462567478992370">अवैध प्रमाणपत्र वापरणे थांबवा</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">आपल्या कनेक्शनमध्ये व्यत्यय आला</translation>
+<translation id="471880041731876836">या साइटला भेट देण्याची तुम्हाला परवानगी नाही</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows नेटवर्क निदान चालविणे<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">धोरणे रीलोड करा</translation>
<translation id="4728558894243024398">प्लॅटफॉर्म</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">पहा</translation>
<translation id="4854362297993841467">ही वितरण पद्धत उपलब्ध नाही. वेगळी पद्धत वापरून पहा.</translation>
<translation id="4858792381671956233">या साइटला भेट देणे ठीक आहे का ते आपण आपल्‍या पालकांना विचारले</translation>
-<translation id="4863764087567530506">हा आशय सॉफ्टवेअर इंस्टॉल करणे किंवा वैयक्तिक माहिती उघड करण्यासाठी फसवण्याचा प्रयत्न करू शकते. <ph name="BEGIN_LINK" />तरीही दाखवा<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">इतिहास शोध</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">प्रमाणीकरण आवश्यक</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{आणि 1 आणखी वेब पृष्ठ}one{आणि # आणखी वेब पृष्‍ठ}other{आणि # आणखी वेब पृष्ठे}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">हे पृष्ठ अज्ञात भाषेतून <ph name="LANGUAGE_LANGUAGE" /> मध्ये अनुवादित करण्यात आले</translation>
<translation id="4923459931733593730">देयक</translation>
<translation id="4926049483395192435">निर्दिष्‍ट केले जाणे आवश्‍यक आहे.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">क्रिया</translation>
<translation id="4958444002117714549">सूची विस्तृत करा</translation>
<translation id="4974590756084640048">चेतावण्या पुन्हा सक्षम करा</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">अयोग्य संकेतशब्द</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>
<translation id="5087580092889165836">कार्ड जोडा</translation>
<translation id="5089810972385038852">राज्य</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">प्रांत</translation>
<translation id="5115563688576182185">(64-बिट)</translation>
<translation id="5141240743006678641">आपल्या Google क्रेडेन्शियलसह संंकालित केलेले संकेतशब्द कूटबद्ध करा</translation>
-<translation id="514421653919133810">गुप्त मोडमध्‍ये पृष्‍ठ उघडा (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">धोरण प्रतिसादामध्ये त्रुटी कोड अस्तित्वात आहे</translation>
+<translation id="5159010409087891077">नवीन गुप्त विंडोमध्ये पेज उघडा (⇧⌘N)</translation>
<translation id="5171045022955879922">URL शोधा किंवा टाइप करा</translation>
<translation id="5172758083709347301">मशीन</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> मध्ये नाही? या त्रुटीचा अहवाल नोंदवा</translation>
-<translation id="5181140330217080051">डाउनलोड करीत आहे</translation>
<translation id="5190835502935405962">बुकमार्क बार</translation>
<translation id="5199729219167945352">प्रयोग</translation>
<translation id="5205222826937269299">नाव आवश्यक आहे</translation>
<translation id="5222812217790122047">ईमेल आवश्यक आहे</translation>
<translation id="5251803541071282808">क्लाउड</translation>
<translation id="5277279256032773186">कार्यस्थानी Chrome वापरत आहात? व्यवसाय त्यांच्या कर्मचार्‍यांंसाठी Chrome सेटिंग्ज व्यवस्थापित करू शकतात. अधिक जाणनू घ्या</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />सॉफ्टवेअर तात्पुरते बंद करून ठेवण्यासाठी खालील पायर्‍यांचे अनुसरण करा, ज्यामुळे तुम्ही वेबशी कनेक्ट होऊ शकाल. तुम्हाला प्रशासकीय हक्क असणे आवश्यक असेल.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />सुरुवात<ph name="BEGIN_BOLD" /> करा<ph name="END_BOLD" /> वर क्लिक करा, त्यानंतर<ph name="BEGIN_BOLD" />"स्थानिक सेवा पहा"<ph name="END_BOLD" /> शोधा आणि निवडा
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />दृश्यशोध<ph name="END_BOLD" /> निवडा
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />सुरू होण्याचा प्रकार<ph name="END_BOLD" /> यामध्ये, <ph name="BEGIN_BOLD" />बंद केले<ph name="END_BOLD" /> निवडा
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />सेवा स्थिती<ph name="END_BOLD" /> मध्ये, <ph name="BEGIN_BOLD" />थांबवा<ph name="END_BOLD" /> वर क्लिक करा
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />लागू करा<ph name="END_BOLD" /> वर क्लिक करा, त्यानंतर <ph name="BEGIN_BOLD" />ठीक आहे<ph name="END_BOLD" /> वर क्लिक करा
+ <ph name="LIST_ITEM" /> सॉफ्टवेअर तुमच्या काँप्युटरवरून काढून कसे टाकावे हे माहीत करण्यासाठी <ph name="BEGIN_LEARN_MORE_LINK" />Chrome मदत केंद्र<ph name="END_LEARN_MORE_LINK" /> वर जा
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">या साइटवरील आपले कनेक्शन खाजगी नाही. VR मोड मधून बाहेर पडण्‍यासाठी, हेडसेट काढा आणि मागे दाबा.</translation>
<translation id="5299298092464848405">धोरण विश्लेषित करताना त्रुटी</translation>
<translation id="5308689395849655368">क्रॅश अहवाल अक्षम केला गेला आहे.</translation>
@@ -540,13 +561,14 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" वर स्कीमा प्रमाणीकरण त्रुटी: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">हे <ph name="HOST_NAME" /> पृष्ठ शोधले जाऊ शकत नाही</translation>
<translation id="5455374756549232013">खराब धोरण टाइमस्टँप</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" /> पैकी <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">अवैध</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> आणखी}one{<ph name="CONTACT_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> आणखी}other{<ph name="CONTACT_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> आणखी}}</translation>
<translation id="5470861586879999274">&amp;संपादित करा पुन्हा करा</translation>
+<translation id="5481076368049295676">हा आशय तुमच्या डिव्हाइसवर तुमची माहिती चोरू किंवा मिटवू शकणारे धोकादायक सॉफ्टवेअर इंस्टॉल करण्याचा कदाचित प्रयत्न करेल. <ph name="BEGIN_LINK" />तरीही दाखवा<ph name="END_LINK" /></translation>
<translation id="54817484435770891">वैध पत्ता जोडा</translation>
<translation id="5492298309214877701">कंपनी, संस्था किंवा शाळा इंट्रानेट वरील या साइटची URL बाह्य वेबसाइटसारखीच आहे.
<ph name="LINE_BREAK" />
- सिस्टीम प्रशासकाशी संपर्क साधण्याचा प्रयत्न करा.</translation>
+ सिस्टम प्रशासकाशी संपर्क साधण्याचा प्रयत्न करा.</translation>
<translation id="5509780412636533143">व्यवस्थापित केलेले बुकमार्क</translation>
<translation id="5510766032865166053">ती कदाचित हलविली किंवा हटविली गेली आहे.</translation>
<translation id="5523118979700054094">धोरणाचे नाव</translation>
@@ -554,6 +576,7 @@
<translation id="5540224163453853">विनंती केलेला लेख शोधू शकलो नाही.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> वरील एम्बेड केलेले पृष्ठ म्हणते:</translation>
<translation id="5556459405103347317">रीलोड करा</translation>
+<translation id="5560088892362098740">एक्सपायर होण्याची तारीख</translation>
<translation id="5565735124758917034">सक्रिय</translation>
<translation id="5571083550517324815">या पत्त्यावरून पिक अप करू शकत नाही. वेगळा पत्ता निवडा.</translation>
<translation id="5572851009514199876">कृपया प्रारंभ करा आणि Chrome मध्‍ये साइन इन करा जेणेकरून आपल्याला या साइटमध्ये प्रवेश करण्‍याची अनुमती आहे किंवा नाही ते Chrome तपासू शकेल.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">धोरण सेटिंग्ज लोड करण्यात अयशस्वी</translation>
<translation id="5631439013527180824">अवैध डिव्हाइस व्यवस्थापन टोकन</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="5646376287012673985">स्थान</translation>
<translation id="5659593005791499971">ईमेल</translation>
<translation id="5669703222995421982">वैयक्तीकृत सामग्री मिळवा</translation>
<translation id="5675650730144413517">हे पृष्ठ कार्य करीत नाही</translation>
<translation id="5710435578057952990">या वेबसाइटची ओळख सत्यापित केली गेली नाही.</translation>
-<translation id="5713016350996637505">फसवणारा आशय ब्लॉक केला</translation>
+<translation id="5719499550583120431">प्रीपेड कार्डे स्वीकारली जातात.</translation>
<translation id="5720705177508910913">वर्तमान वापरकर्ता</translation>
<translation id="5732392974455271431">आपले पालक आपल्यासाठी ती अनावरोधित करू शकतात</translation>
<translation id="5763042198335101085">वैध ईमेल पत्ता प्रविष्ट करा</translation>
@@ -585,16 +609,15 @@
<translation id="5803412860119678065">आपण आपले <ph name="CARD_DETAIL" /> भरू इच्छित आहात?</translation>
<translation id="5810442152076338065">आपले <ph name="DOMAIN" /> वरील कनेक्शन अप्रचलित सायफर सूट वापरून कूटबद्ध केलेले आहे.</translation>
<translation id="5813119285467412249">&amp;जोडा पुन्हा करा</translation>
-<translation id="5814352347845180253">आपण <ph name="SITE" /> आणि काही अन्य साइट मधील प्रीमियम सामग्री मधील प्रवेश कदाचित गमवाल.</translation>
<translation id="5838278095973806738">या साइटवर कोणतीही संवेदनशील माहिती (उदाहरणार्थ, संकेतशब्द किंवा क्रेडिट कार्ड) प्रविष्ट करू नका, कारण आक्रमणकर्ते ती चोरू शकतात.</translation>
<translation id="5869405914158311789">या साइटवर पोहचणे शक्य नाही</translation>
<translation id="5869522115854928033">जतन केलेले संकेतशब्द</translation>
<translation id="5872918882028971132">पालक सूचना</translation>
+<translation id="5893752035575986141">क्रेडिट कार्डे स्वीकारली जातात.</translation>
<translation id="5901630391730855834">पिवळा</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (सिंक केलेले)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 वापरात आहे}one{# वापरात आहे}other{# वापरात आहेत}}</translation>
-<translation id="5926846154125914413">आपण काही साइट मधील प्रीमियम सामग्री मधील प्रवेश कदाचित गमवाल.</translation>
-<translation id="5959728338436674663">धोकादायक अॅप्स आणि साइट शोधण्यात मदत करण्‍यासाठी काही <ph name="BEGIN_WHITEPAPER_LINK" />सिस्टीम माहिती आणि पृष्ठ सामग्री<ph name="END_WHITEPAPER_LINK" /> स्वयंचलितपणे Google कडे पाठवा. <ph name="PRIVACY_PAGE_LINK" /></translation>
+<translation id="5959728338436674663">धोकादायक अॅप्स आणि साइट शोधण्यात मदत करण्‍यासाठी काही <ph name="BEGIN_WHITEPAPER_LINK" />सिस्टम माहिती आणि पृष्ठ सामग्री<ph name="END_WHITEPAPER_LINK" /> स्वयंचलितपणे Google कडे पाठवा. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">इतिहासातून काढा</translation>
<translation id="5975083100439434680">झूम कमी करा</translation>
<translation id="598637245381783098">पेमेंट अॅप उघडू शकत नाही</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">बंद करा</translation>
<translation id="6042308850641462728">अधिक</translation>
<translation id="6047233362582046994">तुम्हाला तुमच्या सुरक्षेला असणारा धोका समजत असल्यास, हानिकारक अॅप्स काढले जाण्यापूर्वी तुम्ही <ph name="BEGIN_LINK" />या साइटला भेट देऊ शकता<ph name="END_LINK" />.</translation>
+<translation id="6047927260846328439">हा आशय तुम्हाला सॉफ्टवेअर इंस्टॉल करण्याचा किंवा वैयक्तिक माहिती उघड करण्याचा फसवा प्रयत्न करू शकेल. <ph name="BEGIN_LINK" />तरीही दाखवा<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">ही वेबसाइट प्रमाणपत्र पिनिंग वापरत असल्यामुळे तुम्ही आत्ता <ph name="SITE" /> पाहू शकणार नाही. नेटवर्क एरर आणि आक्रमणे शक्यतो तात्पुरती असतात, त्यामुळे हे पेज नंतर पाहता येईल.</translation>
<translation id="6060685159320643512">सावधगिरी बाळगा, या प्रयोगांमुळे हानी होऊ शकते</translation>
<translation id="6080696365213338172">आपण प्रशासकाने-प्रदान केलेले प्रमाणपत्र वापरून सामग्रीमध्ये प्रवेश केला. आपण <ph name="DOMAIN" /> वर प्रदान करता तो डेटा आपल्या प्रशासकाद्वारे अंतःखंडित केला जाऊ शकतो.</translation>
@@ -623,7 +647,6 @@
<translation id="6165508094623778733">अधिक जाणून घ्या</translation>
<translation id="6169916984152623906">आता आपण खाजगीरित्या ब्राउझ करू शकता आणि हे डिव्हाइस वापरणारे इतर लोक आपले क्रियाकलाप पाहू शकणार नाहीत. तथापि, डाउनलोड आणि बुकमार्क सेव्ह केले जातील.</translation>
<translation id="6177128806592000436">या साइटवरील आपले कनेक्शन सुरक्षित नाही</translation>
-<translation id="6184817833369986695">(cohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">आपले इंटरनेट कनेक्शन तपासा</translation>
<translation id="6218753634732582820">Chromium वरून पत्ता काढायचा?</translation>
<translation id="6221345481584921695">Google सुरक्षित ब्राउझिंगला अलीकडे <ph name="SITE" /> वर <ph name="BEGIN_LINK" />मालवेअर आढळले आहे<ph name="END_LINK" />. सामान्यतः सुरक्षित असलेल्या वेबसाइट काहीवेळा मालवेअरमुळे संक्रमित झालेल्या असतात. एक ज्ञात मालवेअर वितरक असलेल्या, <ph name="SUBRESOURCE_HOST" /> कडून दुर्भावनापूर्ण सामग्री येते.</translation>
@@ -642,13 +665,14 @@
<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>
<translation id="6387478394221739770">छान नवीन Chrome वैशिष्ट्यांमध्ये स्वारस्य आहे? chrome.com/beta वरील आमचे बीटा चॅनेल वापरून पहा.</translation>
-<translation id="6389758589412724634">हे वेबपृष्ठ प्रदर्शित करण्याचा प्रयत्न करताना Chromium ची मेमरी संपली आहे.</translation>
+<translation id="6397451950548600259">तुमच्या काँप्युटरवरील सॉफ्टवेअर Chrome ला वेबशी सुरक्षितपणे कनेक्ट होण्यापासून थांबवत आहे</translation>
<translation id="6404511346730675251">बुकमार्क संपादित करा</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> साठी कालबाह्यता तारीख आणि CVC प्रविष्‍ट करा</translation>
<translation id="6414888972213066896">या साइटला भेट देणे ठीक आहे का ते आपण आपल्‍या पालकास विचारले</translation>
@@ -660,9 +684,10 @@
<translation id="6451458296329894277">फॉर्म रीसबमिशनची पुष्टी करा</translation>
<translation id="6456339708790392414">आपले देयक</translation>
<translation id="6458467102616083041">धोरणानुसार डीफॉल्ट शोध अक्षम केल्याने दुर्लक्ष करण्‍यात आले.</translation>
-<translation id="647261751007945333">डिव्हाइस धोरणे</translation>
+<translation id="647261751007945333">डीव्हाइस धोरणे</translation>
<translation id="6477321094435799029">Chrome ला या पृष्‍ठावर असमान्य कोड सापडला आहे आणि आपली वैयक्तिक माहिती (उदा, संकेतशब्द, फोन नंबर आणि क्रेडिट कार्ड) संरक्षित करण्‍यासाठी अवरोधित केला आहे.</translation>
<translation id="6489534406876378309">क्रॅश अपलोड करणे प्रारंभ करा</translation>
+<translation id="6507833130742554667">क्रेडिट आणि डेबिट कार्डे स्वीकरली जातात.</translation>
<translation id="6508722015517270189">Chrome रीस्टार्ट करा</translation>
<translation id="6529602333819889595">&amp;पुन्हा करा हटवा</translation>
<translation id="6534179046333460208">वास्तविक वेब सूचना</translation>
@@ -671,13 +696,13 @@
<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="657639383826808334">हा आशय तुमच्या डिव्हाइसवर धोकादायक सॉफ्टवेअर इंस्टॉल करण्याचा प्रयत्न करेल जे तुमची माहिती चोरेल किंवा मिटवेल. <ph name="BEGIN_LINK" />तरीही दाखवा<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">कूटबद्धता पर्याय</translation>
<translation id="662080504995468778">यावर रहा</translation>
<translation id="6626291197371920147">वैध कार्ड नंबर जोडा</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> शोध</translation>
<translation id="6630809736994426279">सध्या <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="6644283850729428850">हे धोरण नापसंत आहे.</translation>
+<translation id="6657585470893396449">संकेतशब्द</translation>
<translation id="6671697161687535275">Chromium वरून फॉर्म सूचना काढायच्या?</translation>
<translation id="6685834062052613830">साइन आउट करा आणि सेटअप पूर्ण करा</translation>
<translation id="6710213216561001401">मागील</translation>
@@ -708,7 +733,6 @@
<translation id="6970216967273061347">‍जिल्‍हा</translation>
<translation id="6973656660372572881">निश्चित प्रॉक्सी सर्व्हर आणि .pac स्क्रिप्ट URL निर्दिष्‍ट करण्‍यात आले आहेत.</translation>
<translation id="6989763994942163495">प्रगत सेटिंग्ज दर्शवा...</translation>
-<translation id="7000990526846637657">कोणत्याही इतिहास प्रविष्टी सापडल्या नाहीत</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">आपल्या Google खात्यात <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> वर ब्राउझिंग इतिहासाची अन्य स्वरूपे असू शकतात</translation>
<translation id="7029809446516969842">संकेतशब्द</translation>
@@ -723,7 +747,9 @@
<translation id="7129409597930077180">या पत्त्यावर पाठवू शकत नाही. वेगळा पत्ता निवडा.</translation>
<translation id="7138472120740807366">वितरण पद्धत</translation>
<translation id="7139724024395191329">अमिरात</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> आणखी}one{<ph name="PAYMENT_METHOD_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> आणखी}other{<ph name="PAYMENT_METHOD_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> आणखी}}</translation>
<translation id="7155487117670177674">देयक सुरक्षित नाही</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> आणखी}one{<ph name="SHIPPING_OPTION_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> आणखी}other{<ph name="SHIPPING_OPTION_PREVIEW" /> आणि <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> आणखी}}</translation>
<translation id="7179921470347911571">आत्ता पुन्हा लाँच करा </translation>
<translation id="7180611975245234373">रीफ्रेश करा</translation>
<translation id="7182878459783632708">कोणतीही धोरणे सेट नाहीत</translation>
@@ -735,7 +761,7 @@
<translation id="7219179957768738017">कनेक्शन <ph name="SSL_VERSION" /> वापरते.</translation>
<translation id="7220786058474068424">प्रक्रिया करत आहे</translation>
<translation id="724691107663265825">साइटमध्ये पुढे मालवेअर आहे</translation>
-<translation id="724975217298816891">आपले कार्ड तपशील अद्यतनित करण्‍यासाठी <ph name="CREDIT_CARD" /> करिता कालबाह्यता तारीख आणि CVC प्रविष्‍ट करा. आपण पुष्टी केल्यावर, आपले कार्ड तपशील या साइटसह सामायिक केले जातील.</translation>
+<translation id="724975217298816891">आपले कार्ड तपशील अपडेट करण्‍यासाठी <ph name="CREDIT_CARD" /> करिता कालबाह्यता तारीख आणि CVC प्रविष्‍ट करा. आपण पुष्टी केल्यावर, आपले कार्ड तपशील या साइटसह सामायिक केले जातील.</translation>
<translation id="7260504762447901703">प्रवेश रद्द करा</translation>
<translation id="7275334191706090484">व्यवस्थापित केलेले बुकमार्क</translation>
<translation id="7298195798382681320">शिफारस केलेले</translation>
@@ -764,6 +790,7 @@
<translation id="7514365320538308">डाउनलोड करा</translation>
<translation id="7518003948725431193">या वेबपत्त्यासाठी वेबपृष्ठ आढळले नाही: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">या साइटवर तुमचे कनेक्शन खाजगी नाही</translation>
<translation id="7535087603100972091">मूल्य</translation>
<translation id="7537536606612762813">अनिवार्य</translation>
<translation id="7542403920425041731">तुम्ही निश्चित केल्यावर, तुमचे कार्ड तपशील या साइटसह शेअर केले जातील.</translation>
@@ -773,7 +800,6 @@
<translation id="7552846755917812628">खालील टिपा वापरून पहा:</translation>
<translation id="7554791636758816595">नवीन टॅब</translation>
<translation id="7567204685887185387">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र कदाचित लबाडीने जारी केले असावे. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">हे पृष्ठ<ph name="ORIGINAL_LANGUAGE" />मध्ये आहे आपण याचा भाषांतर करु इच्छिता?</translation>
<translation id="7569952961197462199">Chrome मधून क्रेडिट कार्ड काढायचे?</translation>
<translation id="7569983096843329377">काळा</translation>
@@ -792,7 +818,7 @@
<translation id="7662298039739062396">सेटिंग एका विस्ताराद्वारे नियंत्रित केली आहेत</translation>
<translation id="7667346355482952095">परत केलेले धोरण टोकन रिक्त आहे किंवा वर्तमान टोकनशी जुळत नाही</translation>
<translation id="7668654391829183341">अज्ञात डिव्हाइस</translation>
-<translation id="7669271284792375604">या साइट वरील आक्रमणकर्ते कदाचित आपल्या ब्राउझिंग अनुभवास हानी पोहोचविणारे प्रोग्राम (उदाहरणार्थ, आपले मुख्यपृष्ठ बदलून किंवा आपण भेट देता त्या साइटवर अतिरिक्त जाहिराती दर्शवून) स्थापित करून आपली फसवणूक करण्‍याचा प्रयत्न करू शकतात.</translation>
+<translation id="7669271284792375604">या साइट वरील आक्रमणकर्ते कदाचित आपल्या ब्राउझिंग अनुभवास हानी पोहोचविणारे प्रोग्राम (उदाहरणार्थ, आपले मुख्यपृष्ठ बदलून किंवा आपण भेट देता त्या साइटवर अतिरिक्त जाहिराती दर्शवून) इंस्टॉल करून आपली फसवणूक करण्‍याचा प्रयत्न करू शकतात.</translation>
<translation id="7674629440242451245">छान नवीन Chrome वैशिष्ट्यांमध्ये स्वारस्य आहे? chrome.com/dev येथे आमचे dev चॅनेल वापरून पहा.</translation>
<translation id="7682287625158474539">शिपिंग</translation>
<translation id="7701040980221191251">काहीही नाही</translation>
@@ -800,9 +826,11 @@
<translation id="7714464543167945231">प्रमाणपत्र</translation>
<translation id="7716147886133743102">तुमच्या प्रशासकाने ब्लॉक केलेले</translation>
<translation id="7716424297397655342">ही साइट कॅश मधून लोड करणे शक्य नाही</translation>
+<translation id="774634243536837715">धोकादायक आशय ब्लॉक केला.</translation>
<translation id="7752995774971033316">व्यवस्थापित न केलेले</translation>
<translation id="7755287808199759310">आपले पालक आपल्यासाठी ती अनावरोधित करू शकतात</translation>
<translation id="7758069387465995638">फायरवॉल किंवा अँटीव्हायरस सॉफ्टवेअरने कदाचित कनेक्शन अवरोधित केले असावे.</translation>
+<translation id="7759163816903619567">डोमेन दाखवा:</translation>
<translation id="7761701407923456692">सर्व्हरचे प्रमाणपत्र URL शी जुळत नाही.</translation>
<translation id="7763386264682878361">पेमेंट मॅनिफेस्ट विश्लेषक</translation>
<translation id="7764225426217299476">पत्ता जोडा</translation>
@@ -810,13 +838,14 @@
<translation id="7791543448312431591">जोडा</translation>
<translation id="7793809570500803535"><ph name="SITE" />येथील वेबपृष्ठ कदाचित तात्पुरते बंद आहे किंवा ते कदाचित नवीन वेब पत्त्यावर कायमचे हलवले आहे.</translation>
<translation id="7800304661137206267">संदेश प्रमाणीकरणासाठी <ph name="MAC" /> आणि की विनिमय तंत्र महणून <ph name="KX" /> सह <ph name="CIPHER" /> वापरून कनेक्शन कूटबद्ध केले आहे.</translation>
-<translation id="780301667611848630">नाही, धन्यवाद</translation>
+<translation id="780301667611848630">नाही, नको</translation>
<translation id="7805768142964895445">स्थिती</translation>
<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Chrome मधून सूचना फॉर्म काढायचा?</translation>
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />' साठी <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> सापडले</translation>
<translation id="785549533363645510">तथापि, आपण अदृश्य नाही. गुप्त झाल्याने आपले ब्राउझिंग आपला नियोक्ता, आपला इंटरनेट सेवा प्रदाता, किंवा आपण भेट देता त्या वेबसाइटपासून लपत नाही.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">डेबिट आणि प्रीपेड कार्डे स्वीकरली जातात.</translation>
<translation id="7887683347370398519">आपले CVC तपासा आणि पुन्हा प्रयत्न करा</translation>
<translation id="79338296614623784">वैध फोन नंबर प्रविष्ट करा</translation>
<translation id="7935318582918952113">DOM डिस्टिलर</translation>
@@ -830,13 +859,12 @@
<translation id="7995512525968007366">निर्दिष्ट केलेले नाही</translation>
<translation id="800218591365569300">मेमरी मोकळी करण्‍यासाठी अन्य टॅब किंवा प्रोग्राम बंद करून पहा.</translation>
<translation id="8012647001091218357">आम्ही याक्षणी आपल्या पालकांपर्यंत पोहोचू शकलो नाही. कृपया पुन्हा प्रयत्न करा.</translation>
-<translation id="8025119109950072390">या साइट वरील आक्रमणकर्ते सॉफ्‍टवेअर स्थापित करणे किंवा आपली वैयक्तिक माहिती (उदाहरणार्थ, संकेतशब्द, फोन नंबर किंवा क्रेडिट कार्ड) उघड करणे यासारखे काहीतरी धोकादायक करण्‍यामध्‍ये आपल्‍याला युक्तीने गुंतवू शकतात.</translation>
+<translation id="8025119109950072390">या साइट वरील आक्रमणकर्ते सॉफ्‍टवेअर इंस्टॉल करणे किंवा आपली वैयक्तिक माहिती (उदाहरणार्थ, संकेतशब्द, फोन नंबर किंवा क्रेडिट कार्ड) उघड करणे यासारखे काहीतरी धोकादायक करण्‍यामध्‍ये आपल्‍याला युक्तीने गुंतवू शकतात.</translation>
<translation id="8034522405403831421">हे पृष्‍ठ <ph name="SOURCE_LANGUAGE" /> मध्ये आहे. त्यास <ph name="TARGET_LANGUAGE" /> मध्ये भाषांतरीत करायचे?</translation>
<translation id="8037357227543935929">विचारा (डीफॉल्ट)</translation>
<translation id="8041089156583427627">अभिप्राय पाठवा</translation>
<translation id="8041940743680923270">सार्वत्रिक डीफॉल्‍ट वापरा (विचारा)</translation>
<translation id="8088680233425245692">लेख पाहण्यात अयशस्वी.</translation>
-<translation id="8089520772729574115">1 MB पेक्षा कमी</translation>
<translation id="8091372947890762290">सक्रियकरण सर्व्हरवर प्रलंबित आहे</translation>
<translation id="8118489163946903409">पेमेंट पद्धत</translation>
<translation id="8131740175452115882">पुष्टी करा</translation>
@@ -846,12 +874,15 @@
<translation id="8184538546369750125">सार्वत्रिक डीफॉल्‍ट वापरा (अनुमती द्या)</translation>
<translation id="8191494405820426728">स्थानिक क्रॅश आयडी <ph name="CRASH_LOCAL_ID" /></translation>
<translation id="8194797478851900357">&amp;हलवा पूर्ववत करा</translation>
-<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" आयडी असलेल्या विस्तारासाठी अवैध अद्यतन URL.</translation>
+<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" आयडी असलेल्या विस्तारासाठी अवैध अपडेट URL.</translation>
<translation id="8202097416529803614">ऑर्डर सारांश</translation>
+<translation id="8205463626947051446">साइट अनाहूत जाहिराती दाखवणे चालू ठेवेल</translation>
<translation id="8218327578424803826">नियुक्त केलेले स्थान:</translation>
<translation id="8225771182978767009">ज्या व्यक्तीने हा संगणक सेट केला त्या व्यक्तीने ही साइट अवरोधित करण्याचे निवडले आहे.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">पेज नवीन गुप्त टॅबमध्ये उघडा</translation>
<translation id="8241707690549784388">आपण जे पृष्ठ शोधत आहत ते आपण प्रविष्ट केलेली माहिती वापरत आहे. त्या पृष्ठाकडे परत गेल्यास कदाचित आपण केलेल्या कोणत्याही क्रियेची पुनरावृत्ती होईल. आपण सुरू ठेवू इच्छिता?</translation>
+<translation id="8241712895048303527">या साइटवर ब्लॉक करा</translation>
<translation id="8249320324621329438">अंतिम प्राप्त केलेले:</translation>
<translation id="8253091569723639551">बिलिंग पत्ता आवश्यक आहे</translation>
<translation id="8261506727792406068">हटवा</translation>
@@ -862,7 +893,6 @@
<translation id="8308427013383895095">नेटवर्क कनेक्शनसह समस्या असल्यामुळे भाषांतर अयशस्वी झाला.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> मधील प्रवेश नाकारला</translation>
<translation id="834457929814110454">आपल्याला आपल्या सुरक्षिततेच्या जोखमी समजत असल्यास, धोकादायक प्रोग्राम काढले जाण्यापूर्वी आपण <ph name="BEGIN_LINK" />या असुरक्षित साइटला भेट देऊ शकता<ph name="END_LINK" />.</translation>
-<translation id="8344669043927012510">गुप्त मोडमध्‍ये पृष्ठ उघडा (⇧⌘N)</translation>
<translation id="8349305172487531364">बुकमार्क बार</translation>
<translation id="8363502534493474904">विमान मोड बंद करा</translation>
<translation id="8364627913115013041">सेट केलेले नाही.</translation>
@@ -872,6 +902,7 @@
<translation id="8398259832188219207">क्रॅश अहवाल <ph name="UPLOAD_TIME" /> वाजता अपलोड केला</translation>
<translation id="8412145213513410671">क्रॅश (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">आपण समान सांकेतिक वाक्यांश दोनदा प्रविष्ट करणे आवश्यक आहे.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">सेटिंग्ज</translation>
<translation id="8433057134996913067">हे आपल्याला बहुतांश वेबसाइट वरून साइन आउट करेल.</translation>
<translation id="8437238597147034694">&amp;हलवा पूर्ववत करा</translation>
@@ -879,14 +910,16 @@
<translation id="8483780878231876732">आपल्या Google खात्यावरून कार्ड वापरण्यासाठी Chrome मध्ये साइन इन करा</translation>
<translation id="8488350697529856933">यावर लागू होते</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> नी प्रतिसाद देण्यात बराच वेळ घेतला.</translation>
-<translation id="8532105204136943229">कालबाह्य होण्याचे वर्ष</translation>
+<translation id="8503813439785031346">वापरकर्तानाव</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="8571890674111243710"><ph name="LANGUAGE" /> मध्ये पृष्ठ अनुवादित करत आहे...</translation>
<translation id="858637041960032120">फोन नंबर जोडा</translation>
<translation id="859285277496340001">प्रमाणपत्र निरस्त झाले आहे किंवा नाही हे तपासण्यासाठी प्रणाली निर्दिष्ट करत नाही.</translation>
<translation id="8620436878122366504">आपल्या पालकांनी अद्याप ती मंजूर केली नाही</translation>
<translation id="8647750283161643317">सर्व डीफॉल्टमध्ये रीसेट करा</translation>
+<translation id="8660471606262461360">Google Payments वरून</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> चे आपले कनेक्शन कूटबद्ध केलेले नाही.</translation>
<translation id="8718314106902482036">पेमेंट पूर्ण झाले नाही</translation>
<translation id="8725066075913043281">पुन्हा प्रयत्न करा</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">झूम वाढवा</translation>
<translation id="8931333241327730545">आपण आपल्या Google खात्यात हे कार्ड जतन करू इच्छिता?</translation>
<translation id="8932102934695377596">आपले घड्याळ मागे आहे</translation>
+<translation id="8938939909778640821">स्वीकारलेली क्रेडिट आणि प्रीपेड कार्डे</translation>
<translation id="8971063699422889582">सर्व्हरचे प्रमाणपत्र कालबाह्य झाले आहे.</translation>
<translation id="8986494364107987395">Google ला वापर आकडेवारी आणि क्रॅश अहवाल स्वयंचलितपणे पाठवा</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -921,7 +955,7 @@
<translation id="8997023839087525404">सर्व्हरने प्रमाणपत्र पारदर्शकता धोरणाचा वापर करून सार्वजनिकरीत्या उघड न केलेले एक प्रमाणपत्र सादर केले. काही प्रमाणपत्रे विश्वसनीय आहेत आणि आक्रमणकर्त्यांविरूद्ध संरक्षण करतात हे सुनिश्चित करण्‍यासाठी त्यांच्यासाठी ही एक आवश्यकता आहे.</translation>
<translation id="9001074447101275817">प्रॉक्सी <ph name="DOMAIN" /> ला वापरकर्तानाव आणि संकेतशब्द आवश्यक आहेत.</translation>
<translation id="9005998258318286617">PDF दस्तऐवज लोड करण्यात अपयश आले.</translation>
-<translation id="901974403500617787">सिस्टीम-व्याप्त लागू होणारी ध्वजांकने केवळ मालकाद्वारे सेट केली जाऊ शकतात: <ph name="OWNER_EMAIL" />.</translation>
+<translation id="901974403500617787">सिस्टम-व्याप्त लागू होणारी ध्वजांकने केवळ मालकाद्वारे सेट केली जाऊ शकतात: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020200922353704812">कार्ड बिलिंग पत्ता आवश्यक आहे</translation>
<translation id="9020542370529661692">हे पृष्ठ <ph name="TARGET_LANGUAGE" /> मध्ये भाषांतरित केले गेले आहे.</translation>
<translation id="9035022520814077154">सुरक्षितता त्रुटी</translation>
@@ -938,12 +972,11 @@
<translation id="9137013805542155359">मूळ दर्शवा</translation>
<translation id="9137248913990643158">कृपया हा अॅप वापरण्‍यापूर्वी प्रारंभ करा आणि Chrome मध्‍ये साइन इन करा.</translation>
<translation id="9148507642005240123">&amp;संपादित करा पूर्ववत करा</translation>
-<translation id="9154194610265714752">अद्यतनित केलेले</translation>
+<translation id="9154194610265714752">अपडेट केलेले</translation>
<translation id="9157595877708044936">सेट अप करीत आहे...</translation>
<translation id="9169664750068251925">या साइटवर नेहमी अवरोधित करा</translation>
<translation id="9170848237812810038">&amp;पूर्ववत करा</translation>
<translation id="917450738466192189">सर्व्हरचे प्रमाणपत्र अवैध आहे.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> आणि आणखी <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> असमर्थित प्रोटोकॉल वापरतो.</translation>
<translation id="9205078245616868884">आपला डेटा आपल्या संकालन सांकेतिक वाक्यांशासह कूटबद्ध केला जातो. संकालन सुरु करण्यासाठी तो प्रविष्ट करा.</translation>
<translation id="9207861905230894330">लेख जोडण्यात अयशस्वी.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">फॉर्म साफ करा</translation>
<translation id="939736085109172342">नवीन फोल्‍डर</translation>
-<translation id="941721044073577244">आपल्याला या साइटला भेट देण्याची परवानगी नाही असे दिसते</translation>
<translation id="969892804517981540">अधिकृत बिल्ड</translation>
<translation id="975560348586398090">{COUNT,plural, =0{काहीही नाही}=1{1 आयटम}one{# आयटम}other{# आयटम}}</translation>
+<translation id="981121421437150478">ऑफलाइन</translation>
<translation id="988159990683914416">विकसक बिल्ड</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ms.xtb b/chromium/components/strings/components_strings_ms.xtb
index 15a927a190a..d6b5ec986b0 100644
--- a/chromium/components/strings/components_strings_ms.xtb
+++ b/chromium/components/strings/components_strings_ms.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Tutup apl lain</translation>
<translation id="1055184225775184556">&amp;Buat Asal Tambahkan</translation>
<translation id="10614374240317010">Tidak pernah disimpan</translation>
+<translation id="1066396345355680611">Anda mungkin kehilangan akses kepada kandungan yang dilindungi daripada <ph name="SITE" /> dan beberapa tapak lain.</translation>
<translation id="106701514854093668">Penanda Halaman Desktop</translation>
<translation id="1074497978438210769">Tidak selamat</translation>
<translation id="1080116354587839789">Muat ikut lebar</translation>
@@ -27,7 +28,7 @@
<translation id="1161325031994447685">Menyambung semula kepada Wi-Fi</translation>
<translation id="1165039591588034296">Ralat</translation>
<translation id="1175364870820465910">&amp;Cetak...</translation>
-<translation id="1181037720776840403">Buang</translation>
+<translation id="1181037720776840403">Alih keluar</translation>
<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Laporkan secara automatik<ph name="END_WHITEPAPER_LINK" /> tentang butiran kemungkinan insiden keselamatan kepada Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Seterusnya</translation>
<translation id="1201895884277373915">Lagi dari tapak ini</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Senarai bacaan</translation>
<translation id="1264126396475825575">Laporan ranap sistem dirakam pada <ph name="CRASH_TIME" /> (belum dimuat naik atau diabaikan)</translation>
<translation id="1281526147609854549">Dikeluarkan oleh <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Kandungan berbahaya disekat</translation>
<translation id="1285320974508926690">Jangan sekali-kali menterjemahkan tapak ini</translation>
<translation id="129553762522093515">Ditutup baru-baru ini</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Cuba kosongkan kuki anda<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domain pendaftaran:</translation>
<translation id="1340482604681802745">Alamat pengambilan</translation>
-<translation id="1344211575059133124">Nampaknya anda memerlukan kebenaran untuk melawat tapak ini</translation>
<translation id="1344588688991793829">Tetapan Auto Isi Chromium...</translation>
<translation id="1348198688976932919">Tapak yang akan disemak imbas mengandungi apl berbahaya</translation>
<translation id="1374468813861204354">cadangan</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identiti <ph name="ORGANIZATION" /> di <ph name="LOCALITY" /> telah disahkan oleh <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ya</translation>
<translation id="1430915738399379752">Cetak</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> lagi}other{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> lagi}}</translation>
<translation id="1506687042165942984">Tunjukkan salinan yang disimpan (iaitu diketahui telah lapuk) bagi halaman ini.</translation>
<translation id="1517433312004943670">Nombor telefon diperlukan</translation>
+<translation id="1517500485252541695">Kad kredit dan debit yang diterima</translation>
<translation id="1519264250979466059">Tarikh Bina</translation>
+<translation id="1527263332363067270">Menunggu sambungan...</translation>
<translation id="153384715582417236">Itu sahaja buat masa ini</translation>
<translation id="1549470594296187301">Javascript mesti didayakan untuk menggunakan ciri ini.</translation>
<translation id="1555130319947370107">Biru</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Muat turun halaman kemudian</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Medan yang diperlukan</translation>
<translation id="187918866476621466">Buka halaman permulaan</translation>
<translation id="1883255238294161206">Runtuhkan senarai</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> lagi}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> lagi}}</translation>
<translation id="1898423065542865115">Penapisan</translation>
+<translation id="1916770123977586577">Untuk menerapkan tetapan anda yang dikemas kini pada tapak ini, muatkan semula halaman ini</translation>
+<translation id="1919345977826869612">Iklan</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Tiada}=1{1 tapak}other{# tapak}}</translation>
<translation id="194030505837763158">Pergi ke <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Kad prabayar yang diterima</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> Penanda halaman</translation>
<translation id="1973335181906896915">Ralat penyirian</translation>
<translation id="1974060860693918893">Lanjutan</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Ralat HTTP</translation>
<translation id="2270484714375784793">Nombor telefon</translation>
<translation id="2282872951544483773">Eksperimen Tidak Tersedia</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item}other{<ph name="ITEM_COUNT" /> item}}</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>
<translation id="2337852623177822836">Tetapan dikawal oleh pentadbir anda</translation>
<translation id="2354001756790975382">Penanda halaman lain</translation>
<translation id="2354430244986887761">Penyemakan Imbas Selamat Google <ph name="BEGIN_LINK" />menemui apl yang memudaratkan<ph name="END_LINK" /> di <ph name="SITE" /> baru-baru ini.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Teruskan</translation>
<translation id="2365563543831475020">Laporan ranap sistem yang dirakam pada <ph name="CRASH_TIME" /> tidak dimuat naik</translation>
<translation id="2367567093518048410">Tahap</translation>
-<translation id="237718015863234333">Tiada alternatif UI tersedia</translation>
<translation id="2384307209577226199">Lalai perusahaan</translation>
<translation id="2386255080630008482">Sijil pelayan telah dibatalkan.</translation>
<translation id="2392959068659972793">Paparkan dasar tanpa nilai yang ditetapkan</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Jika ditandai, Chromium akan menyimpan salinan kad anda pada peranti ini untuk pengisian borang yang lebih cepat.</translation>
<translation id="2498091847651709837">Imbas kad baharu</translation>
<translation id="2501278716633472235">Kembali</translation>
+<translation id="2503184589641749290">Kad debit dan prabayar yang diterima</translation>
<translation id="2515629240566999685">Semak isyarat di kawasan anda</translation>
-<translation id="2516305470678292029">Alternatif UI</translation>
<translation id="2539524384386349900">Kesan</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> menghantar balasan yang tidak sah.</translation>
<translation id="2556876185419854533">&amp;Buat Asal Edit</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Kaedah penghantaran</translation>
<translation id="277499241957683684">Tiada rekod peranti</translation>
<translation id="2784949926578158345">Sambungan ditetapkan semula.</translation>
+<translation id="2788784517760473862">Kad kredit yang diterima</translation>
<translation id="2794233252405721443">Tapak disekat</translation>
<translation id="2799020568854403057">Tapak yang akan disemak imbas mengandungi apl yang memudaratkan</translation>
<translation id="2803306138276472711">Penyemakan Selamat Google <ph name="BEGIN_LINK" />mengesan perisian hasad<ph name="END_LINK" /> pada <ph name="SITE" /> baru-baru ini. Tapak web yang biasanya selamat kadangkala dijangkiti oleh perisian hasad.</translation>
<translation id="2824775600643448204">Bar alamat dan carian</translation>
<translation id="2826760142808435982">Sambungan disulitkan dan disahkan menggunakan <ph name="CIPHER" /> dan menggunakan <ph name="KX" /> sebagai mekanisme pertukaran kunci.</translation>
<translation id="2835170189407361413">Kosongkan borang</translation>
+<translation id="2851634818064021665">Anda memerlukan kebenaran untuk melawat tapak ini</translation>
<translation id="2856444702002559011">Penyerang mungkin akan cuba mencuri maklumat anda daripada <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (contohnya, kata laluan, mesej atau kad kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Jangan Muat Semula</translation>
-<translation id="2900469785430194048">Google Chrome kehabisan memori semasa cuba memaparkan halaman web ini.</translation>
<translation id="2909946352844186028">Perubahan rangkaian dikesan.</translation>
<translation id="2916038427272391327">Tutup atur cara lain</translation>
<translation id="2922350208395188000">Sijil pelayan tidak boleh diperiksa.</translation>
<translation id="2928905813689894207">Alamat Pengebilan</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> lagi}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> lagi}}</translation>
<translation id="2941952326391522266">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya adalah dari <ph name="DOMAIN2" />. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintasi sambungan anda.</translation>
<translation id="2948083400971632585">Anda boleh melumpuhkan sebarang proksi yang dikonfigurasi untuk sambungan dari halaman tetapan.</translation>
<translation id="2955913368246107853">Tutup bar cari</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Jika didayakan, Chromium akan menyimpan salinan kad anda pada peranti ini untuk pengisian borang yang lebih cepat.</translation>
<translation id="2985398929374701810">Masukkan alamat yang sah</translation>
<translation id="2986368408720340940">Kaedah pengambilan ini tidak tersedia. Cuba kaedah lain.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Halaman yang anda lihat dalam tab inkognito tidak akan kekal dalam sejarah, simpanan kuki atau sejarah carian penyemak imbas anda selepas anda menutup semua tab inkognito anda. Sebarang fail yang anda muat turun atau penanda halaman yang anda buat akan disimpan.</translation>
<translation id="3169472444629675720">Temui</translation>
<translation id="3174168572213147020">Pulau</translation>
-<translation id="317583078218509884">Tetapan kebenaran tapak baharu akan dilaksanakan selepas memuatkan semula halaman.</translation>
<translation id="3176929007561373547">Semak tetapan proksi anda atau hubungi pentadbir rangkaian anda untuk
memastikan pelayan proksi berfungsi. Jika anda tidak percaya anda perlu
menggunakan pelayan proksi:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Buka halaman dalam mod Inkognito</translation>
<translation id="320323717674993345">Batal Pembayaran</translation>
<translation id="3207960819495026254">Ditandai halaman</translation>
<translation id="3225919329040284222">Pelayan memberikan sijil yang tidak sepadan dengan jangkaan terbina dalam. Jangkaan ini disertakan untuk tapak web dengan keselamatan tinggi tertentu untuk melindungi anda.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Buat asal Susun semula</translation>
<translation id="3282497668470633863">Tambahkan nama pada kad</translation>
<translation id="3286538390144397061">Mulakan Semula Sekarang</translation>
+<translation id="3287510313208355388">Muat turun semasa dalam talian</translation>
<translation id="3303855915957856445">Tiada hasil carian ditemui</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Selang masa ambil:</translation>
<translation id="3462200631372590220">Menyembunyikan butiran</translation>
<translation id="3467763166455606212">Nama pemegang kad diperlukan</translation>
-<translation id="3478058380795961209">Bulan Tamat Tempoh</translation>
<translation id="3479539252931486093">Adakah hal ini tidak dijangka? <ph name="BEGIN_LINK" />Beritahu kami<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Bukan sekarang</translation>
<translation id="3498215018399854026">Kami tidak dapat menghubungi ibu bapa anda pada masa ini. Sila cuba lagi.</translation>
@@ -343,12 +345,13 @@
&lt;p&gt;Sila laraskan tarikh dan masa daripada bahagian &lt;strong&gt;Umum&lt;/strong&gt; apl &lt;strong&gt;Tetapan&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kad kredit dan prabayar diterima.</translation>
<translation id="3582930987043644930">Tambah nama</translation>
<translation id="3583757800736429874">&amp;Buat Semula Pindahkan</translation>
<translation id="3586931643579894722">Sembunyikan butiran</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Masukkan tarikh tamat tempoh yang sah</translation>
-<translation id="36224234498066874">Kosongkan Data Penyemakan Imbas...</translation>
+<translation id="36224234498066874">Kosongkan Data Semakan Imbas...</translation>
<translation id="362276910939193118">Paparkan Sejarah Penuh</translation>
<translation id="3623476034248543066">Tunjukkan nilai</translation>
<translation id="3630155396527302611">Jika apl telah disenaraikan sebagai atur cara yang dibenarkan untuk mengakses
@@ -359,10 +362,10 @@
<translation id="3655670868607891010">Jika anda kerap melihatnya, cuba <ph name="HELP_LINK" /> ini.</translation>
<translation id="3658742229777143148">Semakan</translation>
<translation id="3678029195006412963">Permintaan tidak dapat ditandatangani</translation>
+<translation id="3678529606614285348">Buka halaman dalam tetingkap Inkognito baharu (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Laporan ranap sistem dirakam pada <ph name="CRASH_TIME" />, dimuat naik pada <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Maklumat sijil</translation>
<translation id="3690164694835360974">Log masuk tidak selamat</translation>
-<translation id="3693415264595406141">Kata laluan:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Memuatkan...</translation>
<translation id="3712624925041724820">Kehabisan lesen</translation>
@@ -374,6 +377,7 @@
<translation id="3748148204939282805">Penyerang di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin memperdaya anda agar melakukan sesuatu yang berbahaya seperti memasang perisian atau mendedahkan maklumat peribadi anda (contohnya, kata laluan, nombor telefon atau kad kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Gagal menterjemah disebabkan ralat pelayan.</translation>
<translation id="3759461132968374835">Tiada laporan nahas yang dibuat baru-baru ini. Nahas yang berlaku apabila laporan nahas dilumpuhkan tidak akan kelihatan di sini.</translation>
+<translation id="3765032636089507299">Halaman Penyemakan Imbas Selamat sedang dalam pembinaan.</translation>
<translation id="3778403066972421603">Adakah anda ingin menyimpan maklumat kad ini ke Akaun Google anda dan pada peranti ini?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Tamat tempoh pada <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -386,8 +390,10 @@
<translation id="3886446263141354045">Permintaan anda untuk mengakses tapak ini telah dihantar kepada <ph name="NAME" />.</translation>
<translation id="3890664840433101773">Tambah e-mel</translation>
<translation id="3901925938762663762">Kad telah tamat tempoh</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID Laporan Ranap Yang Dimuat Naik <ph name="CRASH_ID" /> (ID Ranap Setempat: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya tidak menyatakan Nama Alternatif Subjek. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang memintasi sambungan anda.</translation>
+<translation id="3949601375789751990">Sejarah penyemakan imbas anda dipaparkan di sini</translation>
<translation id="3963721102035795474">Mod Pembaca</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Tiada}=1{Daripada 1 tapak }other{Daripada # tapak }}</translation>
<translation id="397105322502079400">Mengira...</translation>
@@ -416,6 +422,8 @@
<translation id="4165986682804962316">Tetapan tapak</translation>
<translation id="4169947484918424451">Adakah anda mahu Chromium menyimpan kad ini?</translation>
<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="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>
@@ -439,12 +447,12 @@
<translation id="4356973930735388585">Penyerang pada tapak ini mungkin cuba memasang atur cara berbahaya pada komputer anda yang mencuri atau memadamkan maklumat anda (sebagai contoh, foto, kata laluan, mesej dan kad kredit).</translation>
<translation id="4372948949327679948">Nilai <ph name="VALUE_TYPE" /> yang dijangka.</translation>
<translation id="4377125064752653719">Anda cuba untuk mencapai <ph name="DOMAIN" />, tetapi sijil yang diberi pelayan telah dibatalkan oleh pengeluarnya. Ini bermakna bahawa bukti kelayakan keselamatan yang diberi pelayan sememangnya tidak harus dipercayai. Anda mungkin berkomunikasi dengan penyerang.</translation>
-<translation id="4381091992796011497">Nama Pengguna:</translation>
<translation id="4394049700291259645">Lumpuhkan</translation>
<translation id="4406896451731180161">hasil carian</translation>
<translation id="4424024547088906515">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya tidak dipercayai oleh Chrome. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang memintasi sambungan anda.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> tidak menerima sijil log masuk anda atau sijil log masuk mungkin tidak diberikan.</translation>
<translation id="443673843213245140">Penggunaan proksi dilumpuhkan tetapi konfigurasi proksi yang jelas dinyatakan.</translation>
+<translation id="445100540951337728">Kad debit yang diterima</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>
@@ -456,12 +464,14 @@
<translation id="4587425331216688090">Alih keluar alamat daripada Chrome?</translation>
<translation id="4592951414987517459">Sambungan anda ke <ph name="DOMAIN" /> disulitkan menggunakan suit sifer moden.</translation>
<translation id="4594403342090139922">&amp;Buat asal Pemadaman</translation>
+<translation id="4611292653554630842">Log masuk</translation>
<translation id="4619615317237390068">Tab daripada peranti lain</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya mengandungi ralat. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintas sambungan anda.</translation>
<translation id="4690462567478992370">Berhenti menggunakan sijil tidak sah</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Sambungan anda tergendala</translation>
+<translation id="471880041731876836">Anda tiada kebenaran untuk melawat tapak ini</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Jalankan Diagnostik Rangkaian Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Muat semula dasar</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -483,14 +493,15 @@
<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="4863764087567530506">Kandungan ini mungkin menggunakan tipu muslihat supaya anda memasang perisian atau mendedahkan maklumat peribadi. <ph name="BEGIN_LINK" />Tunjukkan juga<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Sejarah carian</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Pengesahan diperlukan</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{dan 1 lagi halaman web}other{dan # lagi halaman web}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Halaman ini diterjemahkan dari bahasa yang tidak diketahui ke <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pembayaran</translation>
<translation id="4926049483395192435">Mesti ditentukan.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Tindakan</translation>
<translation id="4958444002117714549">Kembangkan senarai</translation>
<translation id="4974590756084640048">Dayakan semula amaran</translation>
@@ -506,6 +517,7 @@
<translation id="5045550434625856497">Kata laluan tidak sah</translation>
<translation id="5056549851600133418">Artikel untuk anda</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Menyemak alamat proksi<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Anda mungkin kehilangan akses kepada kandungan dilindungi daripada sesetengah tapak web.</translation>
<translation id="5087286274860437796">Sijil pelayan tidak sah pada masa ini.</translation>
<translation id="5087580092889165836">Tambah kad</translation>
<translation id="5089810972385038852">Negeri</translation>
@@ -513,18 +525,27 @@
<translation id="5095208057601539847">Wilayah</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5141240743006678641">Sulitkan kata laluan yang disegerakkan dengan bukti kelayakan Google anda</translation>
-<translation id="514421653919133810">Buka halaman dalam mod Inkognito (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Terdapat kod ralat dalam balasan dasar</translation>
+<translation id="5159010409087891077">Buka halaman dalam tetingkap Inkognito baharu (⇧⌘N)</translation>
<translation id="5171045022955879922">Buat carian atau taipkan URL</translation>
<translation id="5172758083709347301">Mesin</translation>
<translation id="5179510805599951267">Bukan dalam <ph name="ORIGINAL_LANGUAGE" />? Laporkan ralat ini</translation>
-<translation id="5181140330217080051">Memuat turun</translation>
<translation id="5190835502935405962">Bar Penanda Halaman</translation>
<translation id="5199729219167945352">Eksperimen</translation>
<translation id="5205222826937269299">Nama diperlukan</translation>
<translation id="5222812217790122047">E-mel diperlukan</translation>
<translation id="5251803541071282808">Awan</translation>
<translation id="5277279256032773186">Menggunakan Chrome di tempat kerja? Perniagaan boleh mengurus tetapan Chrome untuk pekerja mereka. Ketahui lebih lanjut</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Ikut langkah ini untuk melumpuhkan perisian buat sementara waktu supaya anda boleh memasuki web. Anda memerlukan keistimewaan pentadbir.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klik <ph name="BEGIN_BOLD" />Mula<ph name="END_BOLD" />, kemudian cari dan pilih <ph name="BEGIN_BOLD" />"Lihat perkhidmatan setempat"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Pilih <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Di bawah <ph name="BEGIN_BOLD" />Jenis permulaan<ph name="END_BOLD" />, pilih <ph name="BEGIN_BOLD" />Dilumpuhkan<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Di bawah <ph name="BEGIN_BOLD" />Status perkhidmatan<ph name="END_BOLD" />, klik <ph name="BEGIN_BOLD" />Berhenti<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Klik <ph name="BEGIN_BOLD" />Gunakan<ph name="END_BOLD" />, kemudian klik <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Lawati <ph name="BEGIN_LEARN_MORE_LINK" />Pusat bantuan Chrome<ph name="END_LEARN_MORE_LINK" /> untuk mengetahui cara mengalih keluar perisian daripada komputer anda secara kekal
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Sambungan anda ke tapak ini tidak peribadi. Untuk keluar daripada mod VR pada bila-bila masa, tanggalkan set kepala dan tekan kembali.</translation>
<translation id="5299298092464848405">Ralat semasa menghuraikan dasar</translation>
<translation id="5308689395849655368">Pelaporan nahas dilumpuhkan.</translation>
@@ -541,9 +562,10 @@
<translation id="5439770059721715174">Ralat pengesahan skema di "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Halaman <ph name="HOST_NAME" /> ini tidak ditemui</translation>
<translation id="5455374756549232013">Cap waktu dasar tidak elok</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> daripada <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Tidak sah</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> lagi}other{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> lagi}}</translation>
<translation id="5470861586879999274">&amp;Buat semula edit</translation>
+<translation id="5481076368049295676">Kandungan ini mungkin cuba memasang perisian berbahaya pada peranti anda yang mencuri atau memadamkan maklumat anda. <ph name="BEGIN_LINK" />Tunjukkan juga<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Tambahkan alamat yang sah</translation>
<translation id="5492298309214877701">Tapak ini yang berada di intranet syarikat, organisasi atau sekolah mempunyai URL yang sama dengan tapak web luar.
<ph name="LINE_BREAK" />
@@ -555,6 +577,7 @@
<translation id="5540224163453853">Tidak menemui artikel yang diminta.</translation>
<translation id="5544037170328430102">Halaman terbenam di <ph name="SITE" /> menyatakan:</translation>
<translation id="5556459405103347317">Muat Semula</translation>
+<translation id="5560088892362098740">Tarikh Tamat Tempoh</translation>
<translation id="5565735124758917034">Aktif</translation>
<translation id="5571083550517324815">Tidak boleh mengambil dari alamat ini. Pilih alamat lain.</translation>
<translation id="5572851009514199876">Sila mulakan dan log masuk ke Chrome supaya Chrome boleh menyemak sama ada anda dibenarkan mengakses tapak ini.</translation>
@@ -569,12 +592,13 @@
<translation id="5629630648637658800">Gagal memuatkan tetapan dasar</translation>
<translation id="5631439013527180824">Token pengurusan peranti tidak sah</translation>
<translation id="5633066919399395251">Penyerang yang sedang berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin cuba memasang atur cara berbahaya pada komputer anda. Atur cara tersebut boleh mencuri atau memadamkan maklumat anda (contohnya, foto, kata laluan, mesej dan kad kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Kandungan mengelirukan disekat.</translation>
<translation id="5646376287012673985">Lokasi</translation>
<translation id="5659593005791499971">E-mel</translation>
<translation id="5669703222995421982">Dapatkan kandungan yang diperibadikan</translation>
<translation id="5675650730144413517">Halaman ini tidak berfungsi</translation>
<translation id="5710435578057952990">Identiti tapak web ini belum disahkan.</translation>
-<translation id="5713016350996637505">Kandungan mengelirukan disekat</translation>
+<translation id="5719499550583120431">Kad prabayar diterima.</translation>
<translation id="5720705177508910913">Pengguna semasa</translation>
<translation id="5732392974455271431">Ibu bapa anda boleh menyahsekatnya untuk anda</translation>
<translation id="5763042198335101085">Masukkan alamat e-mel yang sah</translation>
@@ -586,15 +610,14 @@
<translation id="5803412860119678065">Adakah anda ingin mengisi <ph name="CARD_DETAIL" /> anda?</translation>
<translation id="5810442152076338065">Sambungan anda ke <ph name="DOMAIN" /> disulitkan menggunakan suit sifer yang sudah usang.</translation>
<translation id="5813119285467412249">&amp;Buat Semula Tambahkan</translation>
-<translation id="5814352347845180253">Anda mungkin kehilangan akses kepada kandungan premium daripada <ph name="SITE" /> dan sesetengah tapak web lain.</translation>
<translation id="5838278095973806738">Anda tidak seharusnya memasukkan sebarang maklumat sensitif pada tapak ini (contohnya, kata laluan atau maklumat kad kredit) kerana maklumat ini boleh dicuri oleh penyerang.</translation>
<translation id="5869405914158311789">Tapak ini tidak dapat dicapai</translation>
<translation id="5869522115854928033">Kata laluan disimpan</translation>
<translation id="5872918882028971132">Cadangan Ibu Bapa</translation>
+<translation id="5893752035575986141">Kad kredit diterima.</translation>
<translation id="5901630391730855834">Kuning</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="5926846154125914413">Anda mungkin kehilangan akses kepada kandungan premium daripada sesetengah tapak web.</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="5967867314010545767">Buang daripada sejarah</translation>
<translation id="5975083100439434680">Zum keluar</translation>
@@ -610,6 +633,7 @@
<translation id="6040143037577758943">Tutup</translation>
<translation id="6042308850641462728">Lagi</translation>
<translation id="6047233362582046994">Jika anda memahami risiko terhadap keselamatan anda, anda boleh <ph name="BEGIN_LINK" />melawat tapak ini<ph name="END_LINK" /> sebelum program yang berbahaya ini dialih keluar.</translation>
+<translation id="6047927260846328439">Kandungan ini mungkin menggunakan tipu muslihat supaya anda memasang perisian atau mendedahkan maklumat peribadi. <ph name="BEGIN_LINK" />Tunjukkan juga<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Anda tidak boleh melawati <ph name="SITE" /> sekarang kerana tapak web ini menggunakan penyematan sijil. Ralat dan serangan rangkaian biasanya bersifat sementara. Oleh sebab itu, halaman ini mungkin akan berfungsi semula kemudian.</translation>
<translation id="6060685159320643512">Berhati-hati, percubaan ini mungkin memudaratkan</translation>
<translation id="6080696365213338172">Anda telah mengakses kandungan menggunakan perakuan yang disediakan oleh pentadbir. Data yang anda berikan kepada <ph name="DOMAIN" /> boleh dipintas oleh pentadbir anda.</translation>
@@ -623,7 +647,6 @@
<translation id="6165508094623778733">Ketahui lebih lanjut</translation>
<translation id="6169916984152623906">Kini, anda boleh menyemak imbas secara sulit dan orang lain yang menggunakan peranti ini tidak akan melihat aktiviti anda. Namun begitu, muat turun dan penanda halaman akan disimpan.</translation>
<translation id="6177128806592000436">Sambungan anda ke tapak ini tidak selamat</translation>
-<translation id="6184817833369986695">(kohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Semak sambungan Internet anda</translation>
<translation id="6218753634732582820">Alih keluar alamat daripada Chromium?</translation>
<translation id="6221345481584921695">Penyemakan Selamat Google <ph name="BEGIN_LINK" />telah mengesan perisian hasad<ph name="END_LINK" /> pada <ph name="SITE" /> baru-baru ini. Tapak web yang lazimnya selamat kadangkala dijangkiti oleh perisian hasad. Kandungan berniat jahat datang dari <ph name="SUBRESOURCE_HOST" />, pengedar perisian hasad yang diketahui.</translation>
@@ -642,13 +665,14 @@
<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>
<translation id="6387478394221739770">Berminat dengan ciri Chrome baharu yang hebat? Cuba saluran beta kami di chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium kehabisan memori semasa cuba memaparkan halaman web ini.</translation>
+<translation id="6397451950548600259">Perisian pada komputer anda menghalang Chrome daripada menyambung ke web dengan selamat</translation>
<translation id="6404511346730675251">Edit penanda halaman</translation>
<translation id="6410264514553301377">Masukkan tarikh tamat tempoh dan CVC untuk <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Anda telah bertanya kepada ibu bapa anda sama ada OK untuk melawat tapak ini</translation>
@@ -663,6 +687,7 @@
<translation id="647261751007945333">Dasar peranti</translation>
<translation id="6477321094435799029">Chrome mengesan kod luar biasa pada halaman ini dan menyekatnya untuk melindungi maklumat peribadi anda (contohnya, kata laluan, nombor telefon dan maklumat kad kredit).</translation>
<translation id="6489534406876378309">Mulakan muat naik ranap sistem</translation>
+<translation id="6507833130742554667">Kad kredit dan debit diterima.</translation>
<translation id="6508722015517270189">Mulakan semula Chrome</translation>
<translation id="6529602333819889595">&amp;Buat Semula Pemadaman</translation>
<translation id="6534179046333460208">Cadangan Web Fizikal</translation>
@@ -671,13 +696,13 @@
<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="657639383826808334">Kandungan ini mungkin cuba memasang perisian berbahaya pada peranti anda yang akan mencuri atau memadamkan maklumat anda. <ph name="BEGIN_LINK" />Tunjukkan juga<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Pilihan penyulitan</translation>
<translation id="662080504995468778">Kekal di sini</translation>
<translation id="6626291197371920147">Tambahkan nombor kad yang sah</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Carian</translation>
<translation id="6630809736994426279">Penyerang yang sedang berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin cuba memasang atur cara berbahaya pada komputer Mac 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="6644283850729428850">Dasar ini telah dikecam.</translation>
+<translation id="6657585470893396449">Kata laluan</translation>
<translation id="6671697161687535275">Alih keluar cadangan borang daripada Chromium?</translation>
<translation id="6685834062052613830">Log keluar dan selesaikan persediaan</translation>
<translation id="6710213216561001401">Sebelumnya</translation>
@@ -708,7 +733,6 @@
<translation id="6970216967273061347">Daerah</translation>
<translation id="6973656660372572881">Pelayan proksi tetap dan juga URL skrip .pac tidak ditetapkan.</translation>
<translation id="6989763994942163495">Paparkan tetapan lanjutan...</translation>
-<translation id="7000990526846637657">Tiada masukan sejarah ditemui</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Akaun Google anda mungkin mempunyai sejarah penyemakan imbas dalam bentuk lain di <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Kata laluan</translation>
@@ -723,7 +747,9 @@
<translation id="7129409597930077180">Tidak dapat menghantar ke alamat ini. Pilih alamat lain.</translation>
<translation id="7138472120740807366">Kaedah penghantaran</translation>
<translation id="7139724024395191329">Emiriah</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> lagi}other{<ph name="PAYMENT_METHOD_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> lagi}}</translation>
<translation id="7155487117670177674">Pembayaran tidak selamat</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> lagi}other{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> lagi}}</translation>
<translation id="7179921470347911571">Lancarkan Semula Sekarang</translation>
<translation id="7180611975245234373">Muat semula</translation>
<translation id="7182878459783632708">Tiada dasar ditetapkan</translation>
@@ -764,6 +790,7 @@
<translation id="7514365320538308">Muat Turun</translation>
<translation id="7518003948725431193">Tiada halaman web dijumpai untuk alamat web: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Sambungan anda ke tapak ini tidak berciri peribadi</translation>
<translation id="7535087603100972091">Nilai</translation>
<translation id="7537536606612762813">Wajib</translation>
<translation id="7542403920425041731">Setelah anda mengesahkan, butiran kad anda akan dikongsi dengan tapak ini.</translation>
@@ -773,7 +800,6 @@
<translation id="7552846755917812628">Cuba petua berikut:</translation>
<translation id="7554791636758816595">Tab Baharu</translation>
<translation id="7567204685887185387">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya mungkin telah dikeluarkan melalui penipuan. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintasi sambungan anda.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> lagi}other{<ph name="CONTACT_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> lagi}}</translation>
<translation id="7568593326407688803">Halaman ini adalah dalam<ph name="ORIGINAL_LANGUAGE" />Adakah anda ingin menterjemahkannya?</translation>
<translation id="7569952961197462199">Alih keluar kad kredit daripada Chrome?</translation>
<translation id="7569983096843329377">Hitam</translation>
@@ -800,9 +826,11 @@
<translation id="7714464543167945231">Sijil</translation>
<translation id="7716147886133743102">Disekat oleh pentadbir anda</translation>
<translation id="7716424297397655342">Tapak ini tidak dapat dimuatkan daripada cache</translation>
+<translation id="774634243536837715">Kandungan berbahaya disekat.</translation>
<translation id="7752995774971033316">Tidak Diurus</translation>
<translation id="7755287808199759310">Ibu bapa anda boleh menyahsekatnya untuk anda</translation>
<translation id="7758069387465995638">Tembok api atau perisian antivirus mungkin telah menyekat sambungan.</translation>
+<translation id="7759163816903619567">Domain paparan:</translation>
<translation id="7761701407923456692">Sijil pelayan tidak sepadan dengan URL.</translation>
<translation id="7763386264682878361">Penghurai Manifes Bayaran</translation>
<translation id="7764225426217299476">Tambahkan alamat</translation>
@@ -817,6 +845,7 @@
<translation id="7815407501681723534">Menemui <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> untuk '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">Walau bagaimanapun, anda tidak halimunan. Apabila anda menggunakan mod inkognito, penyemakan imbas anda tidak akan disembunyikan daripada majikan anda, penyedia perkhidmatan Internet anda atau tapak web yang anda lawati.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Kad debit dan prabayar diterima.</translation>
<translation id="7887683347370398519">Semak CVC anda dan cuba lagi</translation>
<translation id="79338296614623784">Masukkan nombor telefon yang sah</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -836,7 +865,6 @@
<translation id="8041089156583427627">Hantar Maklum Balas</translation>
<translation id="8041940743680923270">Gunakan lalai global (Tanya)</translation>
<translation id="8088680233425245692">Gagal melihat artikel.</translation>
-<translation id="8089520772729574115">kurang daripada 1 MB</translation>
<translation id="8091372947890762290">Pengaktifan belum selesai pada pelayan</translation>
<translation id="8118489163946903409">Kaedah pembayaran</translation>
<translation id="8131740175452115882">Sahkan</translation>
@@ -848,10 +876,13 @@
<translation id="8194797478851900357">&amp;Buat Asal Pindahkan</translation>
<translation id="8201077131113104583">URL kemas kini tidak sah untuk sambungan dengan ID "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Ringkasan pesanan</translation>
+<translation id="8205463626947051446">Tapak cenderung menyiarkan iklan yang mengganggu</translation>
<translation id="8218327578424803826">Lokasi yang Ditentukan:</translation>
<translation id="8225771182978767009">Orang yang menyediakan komputer ini telah memilih untuk menyekat tapak ini.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Buka halaman dalam tab Inkognito baharu</translation>
<translation id="8241707690549784388">Halaman yang anda cari untuk maklumat terpakai yang anda masukkan. Kembali ke halaman tersebut mungkin menyebabkan mana-mana tindakan yang anda ambil akan diulang. Adakah anda mahu teruskan?</translation>
+<translation id="8241712895048303527">Sekat di tapak ini</translation>
<translation id="8249320324621329438">Diambil kali terakhir:</translation>
<translation id="8253091569723639551">Alamat pengebilan diperlukan</translation>
<translation id="8261506727792406068">Padam</translation>
@@ -862,7 +893,6 @@
<translation id="8308427013383895095">Gagal terjemahan kerana masalah dengan sambungan rangkaian.</translation>
<translation id="8332188693563227489">Akses ke <ph name="HOST_NAME" /> dinafikan</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="8344669043927012510">Buka halaman dalam mod Inkognito (⇧⌘N)</translation>
<translation id="8349305172487531364">Bar penanda halaman</translation>
<translation id="8363502534493474904">Matikan mod pesawat</translation>
<translation id="8364627913115013041">Tidak ditetapkan.</translation>
@@ -872,6 +902,7 @@
<translation id="8398259832188219207">Laporan ranap sistem dimuat naik pada <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Nahas (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Anda mesti memasukkan frasa laluan yang sama dua kali.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Tetapan</translation>
<translation id="8433057134996913067">Pemadaman ini akan membuatkan anda dilog keluar daripada kebanyakan tapak web.</translation>
<translation id="8437238597147034694">&amp;Buat asal pindahkan</translation>
@@ -879,8 +910,9 @@
<translation id="8483780878231876732">Log masuk ke Chrome untuk menggunakan kad daripada Akaun Google anda</translation>
<translation id="8488350697529856933">Diguna pakai untuk</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> mengambil masa terlalu lama untuk bertindak balas.</translation>
-<translation id="8532105204136943229">Tahun Tamat Tempoh</translation>
+<translation id="8503813439785031346">Nama pengguna</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="8571890674111243710">Menterjemah halaman ke <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Tambah no. tel.
@@ -888,6 +920,7 @@
<translation id="859285277496340001">Sijil tidak menyatakan mekanisme untuk memeriksa sama ada ia telah dibatalkan.</translation>
<translation id="8620436878122366504">Ibu bapa anda belum meluluskannya</translation>
<translation id="8647750283161643317">Tetapkan semula semua kepada lalai</translation>
+<translation id="8660471606262461360">Daripada Google Payments</translation>
<translation id="8703575177326907206">Sambungan anda ke <ph name="DOMAIN" /> tidak disulitkan.</translation>
<translation id="8718314106902482036">Pembayaran belum selesai</translation>
<translation id="8725066075913043281">Cuba lagi</translation>
@@ -915,6 +948,7 @@
<translation id="8903921497873541725">Zum masuk</translation>
<translation id="8931333241327730545">Adakah anda mahu menyimpan kad ini ke Akaun Google anda?</translation>
<translation id="8932102934695377596">Jam anda di belakang</translation>
+<translation id="8938939909778640821">Kad kredit dan prabayar yang diterima</translation>
<translation id="8971063699422889582">Sijil pelayan telah tamat tempoh.</translation>
<translation id="8986494364107987395">Hantar statistik penggunaan dan laporan nahas kepada Google secara automatik</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -944,7 +978,6 @@
<translation id="9169664750068251925">Sentiasa sekat di tapak ini</translation>
<translation id="9170848237812810038">&amp;Buat asal</translation>
<translation id="917450738466192189">Sijil pelayan tidak sah.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> lagi}other{<ph name="SHIPPING_OPTION_PREVIEW" /> dan <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> lagi}}</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>
<translation id="9207861905230894330">Gagal menambahkan artikel.</translation>
@@ -953,9 +986,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">KOSONGKAN BORANG</translation>
<translation id="939736085109172342">Folder baharu</translation>
-<translation id="941721044073577244">Nampaknya anda tiada kebenaran untuk melawat halaman ini</translation>
<translation id="969892804517981540">Binaan Rasmi</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Tiada}=1{1 item}other{# item}}</translation>
+<translation id="981121421437150478">Luar talian</translation>
<translation id="988159990683914416">Binaan Pemaju</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_nl.xtb b/chromium/components/strings/components_strings_nl.xtb
index 40622ea3046..33f609e48ce 100644
--- a/chromium/components/strings/components_strings_nl.xtb
+++ b/chromium/components/strings/components_strings_nl.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Andere apps sluiten</translation>
<translation id="1055184225775184556">&amp;Toevoegen ongedaan maken</translation>
<translation id="10614374240317010">Nooit opgeslagen</translation>
+<translation id="1066396345355680611">Mogelijk heb je geen toegang meer tot beveiligde content van <ph name="SITE" /> en sommige andere sites.</translation>
<translation id="106701514854093668">Desktopbladwijzers</translation>
<translation id="1074497978438210769">Niet veilig</translation>
<translation id="1080116354587839789">Aanpassen aan breedte</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Leeslijst</translation>
<translation id="1264126396475825575">Crashrapport vastgelegd op <ph name="CRASH_TIME" /> (nog niet geüpload of genegeerd)</translation>
<translation id="1281526147609854549">Uitgegeven door <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Gevaarlijke content geblokkeerd</translation>
<translation id="1285320974508926690">Deze site nooit vertalen</translation>
<translation id="129553762522093515">Recent gesloten</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Probeer je cookies te wissen<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Inschrijvingsdomein:</translation>
<translation id="1340482604681802745">Ophaaladres</translation>
-<translation id="1344211575059133124">Het lijkt erop dat je toestemming nodig hebt om deze site te bezoeken</translation>
<translation id="1344588688991793829">Instellingen voor automatisch aanvullen in Chromium...</translation>
<translation id="1348198688976932919">De volgende site bevat gevaarlijke apps</translation>
<translation id="1374468813861204354">suggesties</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">De identiteit van <ph name="ORGANIZATION" /> op <ph name="LOCALITY" /> is geverifieerd door <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ja</translation>
<translation id="1430915738399379752">Afdrukken</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Een opgeslagen (verouderde) kopie van deze pagina weergeven.</translation>
<translation id="1517433312004943670">Telefoonnummer vereist</translation>
+<translation id="1517500485252541695">Geaccepteerde creditcards en betaalpassen</translation>
<translation id="1519264250979466059">Datum van build</translation>
+<translation id="1527263332363067270">Wachten op verbinding...</translation>
<translation id="153384715582417236">Dat is voorlopig alles</translation>
<translation id="1549470594296187301">JavaScript moet zijn ingeschakeld om deze functie te kunnen gebruiken.</translation>
<translation id="1555130319947370107">Blauw</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Pagina later downloaden</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Verplicht veld</translation>
<translation id="187918866476621466">Startpagina's openen</translation>
<translation id="1883255238294161206">Lijst samenvouwen</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filteren</translation>
+<translation id="1916770123977586577">Laad deze pagina opnieuw om je geüpdatete instellingen toe te passen op deze site</translation>
+<translation id="1919345977826869612">Advertenties</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Geen}=1{1 site}other{# sites}}</translation>
<translation id="194030505837763158">Ga naar <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Geaccepteerde prepaidkaarten</translation>
<translation id="1962204205936693436">Bladwijzers voor <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Serialisatiefout</translation>
<translation id="1974060860693918893">Geavanceerd</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP-fout</translation>
<translation id="2270484714375784793">Telefoonnummer</translation>
<translation id="2282872951544483773">Onbeschikbare experimenten</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item}other{<ph name="ITEM_COUNT" /> items}}</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>
<translation id="2337852623177822836">Instelling beheerd door je beheerder</translation>
<translation id="2354001756790975382">Andere bladwijzers</translation>
<translation id="2354430244986887761">Google Safe Browsing heeft onlangs <ph name="BEGIN_LINK" />schadelijke apps gevonden<ph name="END_LINK" /> op <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Doorgaan</translation>
<translation id="2365563543831475020">Het crashrapport dat is vastgelegd op <ph name="CRASH_TIME" />, is niet geüpload</translation>
<translation id="2367567093518048410">Niveau</translation>
-<translation id="237718015863234333">Geen UI-alternatieven beschikbaar</translation>
<translation id="2384307209577226199">Standaardinstelling van bedrijf</translation>
<translation id="2386255080630008482">Het servercertificaat is ingetrokken.</translation>
<translation id="2392959068659972793">Beleid weergeven zonder waarde ingesteld</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Als deze optie is aangevinkt, bewaart Chromium een exemplaar van je kaart op dit apparaat om formulieren sneller te kunnen invullen.</translation>
<translation id="2498091847651709837">Nieuwe kaart scannen</translation>
<translation id="2501278716633472235">Terug</translation>
+<translation id="2503184589641749290">Geaccepteerde betaalpassen en prepaidkaarten</translation>
<translation id="2515629240566999685">Controleer het signaal in je omgeving</translation>
-<translation id="2516305470678292029">UI-alternatieven</translation>
<translation id="2539524384386349900">Detecteren</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> heeft een ongeldige reactie verzonden.</translation>
<translation id="2556876185419854533">&amp;Bewerken ongedaan maken</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Verzendmethode</translation>
<translation id="277499241957683684">Apparaatrecord ontbreekt</translation>
<translation id="2784949926578158345">De verbinding is opnieuw ingesteld.</translation>
+<translation id="2788784517760473862">Geaccepteerde creditcards</translation>
<translation id="2794233252405721443">Site geblokkeerd</translation>
<translation id="2799020568854403057">De volgende site bevat schadelijke apps</translation>
<translation id="2803306138276472711">Google Safe Browsing heeft onlangs <ph name="BEGIN_LINK" />malware gedetecteerd<ph name="END_LINK" /> op <ph name="SITE" />. Websites die normaal gesproken veilig zijn, worden soms geïnfecteerd met malware.</translation>
<translation id="2824775600643448204">Adres- en zoekbalk</translation>
<translation id="2826760142808435982">De verbinding is gecodeerd en geverifieerd met <ph name="CIPHER" /> en gebruikt <ph name="KX" /> als mechanisme voor sleuteluitwisseling.</translation>
<translation id="2835170189407361413">Formulier leegmaken</translation>
+<translation id="2851634818064021665">Je hebt toestemming nodig om deze site te bezoeken</translation>
<translation id="2856444702002559011">Cybercriminelen proberen mogelijk je gegevens van <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> te stelen (bijvoorbeeld wachtwoorden, berichten of creditcardgegevens). <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Niet opnieuw laden</translation>
-<translation id="2900469785430194048">Google Chrome beschikt over onvoldoende geheugen om deze webpagina weer te geven.</translation>
<translation id="2909946352844186028">Er is een netwerkwijziging gedetecteerd.</translation>
<translation id="2916038427272391327">Andere programma's sluiten</translation>
<translation id="2922350208395188000">Het servercertificaat kan niet worden gecontroleerd.</translation>
<translation id="2928905813689894207">Factuuradres</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server is afkomstig van <ph name="DOMAIN2" />. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation>
<translation id="2948083400971632585">Via de instellingenpagina kun je proxyservers uitschakelen die voor een verbinding zijn geconfigureerd.</translation>
<translation id="2955913368246107853">Zoekbalk sluiten</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Als deze instelling is ingeschakeld, slaat Chromium een kopie van je kaart op dit apparaat op zodat formulieren sneller kunnen worden ingevuld.</translation>
<translation id="2985398929374701810">Geef een geldig adres op</translation>
<translation id="2986368408720340940">Deze ophaalmethode is niet beschikbaar. Kies een andere methode.</translation>
@@ -277,9 +281,7 @@
<translation id="3167968892399408617">De pagina's die je op incognitotabbladen weergeeft, worden niet bewaard in je browsergeschiedenis, cookie-opslag of zoekgeschiedenis nadat je al je incognitotabbladen hebt gesloten. Alle bestanden die je hebt gedownload of bladwijzers die je hebt gemaakt, blijven behouden.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Eiland</translation>
-<translation id="317583078218509884">De nieuwe instellingen voor siterechten worden doorgevoerd nadat de pagina opnieuw is geladen.</translation>
<translation id="3176929007561373547">Controleer je proxyinstellingen of neem contact op met je netwerkbeheerder om te controleren of de proxyserver werkt. Als je denkt dat je geen proxyserver zou moeten gebruiken: <ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">De pagina openen in de incognitomodus</translation>
<translation id="320323717674993345">Betaling annuleren</translation>
<translation id="3207960819495026254">Toegevoegd aan 'Bladwijzers'</translation>
<translation id="3225919329040284222">De server heeft een certificaat gepresenteerd dat niet overeenkomt met de ingebouwde verwachtingen. Deze verwachtingen zijn opgenomen voor bepaalde websites om je te beschermen.</translation>
@@ -292,6 +294,7 @@
<translation id="3270847123878663523">&amp;Volgorde wijzigen ongedaan maken</translation>
<translation id="3282497668470633863">Naam op kaart toevoegen</translation>
<translation id="3286538390144397061">Nu herstarten</translation>
+<translation id="3287510313208355388">Downloaden wanneer online</translation>
<translation id="3303855915957856445">Geen zoekresultaten gevonden</translation>
<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>
@@ -324,7 +327,6 @@
<translation id="3452404311384756672">Ophaalinterval:</translation>
<translation id="3462200631372590220">Gedetailleerde informatie verbergen</translation>
<translation id="3467763166455606212">Naam kaarthouder vereist</translation>
-<translation id="3478058380795961209">Vervalmaand</translation>
<translation id="3479539252931486093">Wat dit onverwacht? <ph name="BEGIN_LINK" />Laat het ons weten<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Niet nu</translation>
<translation id="3498215018399854026">We kunnen je ouder momenteel niet bereiken. Probeer het opnieuw.</translation>
@@ -340,12 +342,13 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Creditcards en prepaidkaarten worden geaccepteerd.</translation>
<translation id="3582930987043644930">Voeg naam toe</translation>
<translation id="3583757800736429874">&amp;Opnieuw verplaatsen</translation>
<translation id="3586931643579894722">Details verbergen</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Geef een geldige vervaldatum op</translation>
-<translation id="36224234498066874">Wis browsegegevens...</translation>
+<translation id="36224234498066874">Browsegegevens wissen...</translation>
<translation id="362276910939193118">Volledige geschiedenis weergeven</translation>
<translation id="3623476034248543066">Waarde weergeven</translation>
<translation id="3630155396527302611">Als dit programma al wordt vermeld als een programma dat toegang heeft tot het netwerk, kun je proberen het uit de lijst te verwijderen en het opnieuw toe te voegen.</translation>
@@ -354,10 +357,10 @@
<translation id="3655670868607891010">Als je deze melding vaker ziet, probeer je deze <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisie</translation>
<translation id="3678029195006412963">Verzoek kan niet worden ondertekend</translation>
+<translation id="3678529606614285348">Pagina openen in een nieuw incognitovenster (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Crashrapport vastgelegd op <ph name="CRASH_TIME" />, geüpload op <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Certificaatgegevens</translation>
<translation id="3690164694835360974">Inloggen niet veilig</translation>
-<translation id="3693415264595406141">Wachtwoord:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Bezig met laden...</translation>
<translation id="3712624925041724820">Licenties zijn verbruikt</translation>
@@ -369,6 +372,7 @@
<translation id="3748148204939282805">Cybercriminelen op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> proberen je mogelijk te misleiden om iets gevaarlijks te doen, zoals software installeren of je persoonlijke gegevens bekendmaken (bijvoorbeeld wachtwoorden, telefoonnummers of creditcardgegevens). <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Het vertalen is mislukt wegens een serverfout.</translation>
<translation id="3759461132968374835">Je hebt geen onlangs gemelde crashes. Crashes die zich voordeden toen de crashrapportage was uitgeschakeld, worden hier niet weergegeven.</translation>
+<translation id="3765032636089507299">De Safe Browsing-pagina is in aanbouw.</translation>
<translation id="3778403066972421603">Wil je deze kaart opslaan in je Google-account en op dit apparaat?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Verloopt: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -381,8 +385,10 @@
<translation id="3886446263141354045">Je verzoek voor toegang tot deze website is verzonden naar <ph name="NAME" /></translation>
<translation id="3890664840433101773">E-mailadres toevoegen</translation>
<translation id="3901925938762663762">De kaart is verlopen</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Crashrapport-ID <ph name="CRASH_ID" /> (lokale crash-ID: <ph name="CRASH_LOCAL_ID" />) geüpload</translation>
<translation id="3949571496842715403">Deze server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. In het bijbehorende beveiligingscertificaat worden geen 'Subject Alternative Names' gespecificeerd. Dit kan worden veroorzaakt door een verkeerde configuratie of door een aanvaller die je verbinding heeft onderschept.</translation>
+<translation id="3949601375789751990">Je browsegeschiedenis wordt hier weergegeven</translation>
<translation id="3963721102035795474">Lezermodus</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Geen}=1{Van één site }other{Van # sites }}</translation>
<translation id="397105322502079400">Berekenen...</translation>
@@ -411,6 +417,8 @@
<translation id="4165986682804962316">Site-instellingen</translation>
<translation id="4169947484918424451">Wil je dat Chromium deze kaart opslaat?</translation>
<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="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>
@@ -434,12 +442,12 @@
<translation id="4356973930735388585">Cybercriminelen op deze site proberen mogelijk gevaarlijke programma's op je computer te installeren waarmee je gegevens worden gestolen of verwijderd (bijvoorbeeld foto's, wachtwoorden, berichten en creditcards).</translation>
<translation id="4372948949327679948">Verwachte <ph name="VALUE_TYPE" /> waarde.</translation>
<translation id="4377125064752653719">Je probeert <ph name="DOMAIN" /> te bereiken, maar het certificaat dat de server heeft geretourneerd, is ingetrokken door de uitgever. Dat betekent dat de veiligheidsgaranties die de server heeft geretourneerd, absoluut niet kunnen worden vertrouwd. Het kan zijn dat je met een hacker aan het communiceren bent.</translation>
-<translation id="4381091992796011497">Gebruikersnaam:</translation>
<translation id="4394049700291259645">Uitschakelen</translation>
<translation id="4406896451731180161">zoekresultaten</translation>
<translation id="4424024547088906515">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server wordt niet vertrouwd door Chrome. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> heeft je inlogcertificaat niet geaccepteerd of er is geen inlogcertificaat geleverd.</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="4506176782989081258">Validatiefout: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Neem contact op met de systeembeheerder</translation>
<translation id="450710068430902550">Delen met beheerder</translation>
@@ -451,12 +459,14 @@
<translation id="4587425331216688090">Adres verwijderen uit Chrome?</translation>
<translation id="4592951414987517459">Je verbinding met <ph name="DOMAIN" /> is versleuteld via een moderne Cipher Suite.</translation>
<translation id="4594403342090139922">&amp;Verwijderen ongedaan maken</translation>
+<translation id="4611292653554630842">Inloggen</translation>
<translation id="4619615317237390068">Tabbladen van andere apparaten</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server bevat fouten. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation>
<translation id="4690462567478992370">Stoppen met het gebruik van een ongeldig certificaat</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Je verbinding is onderbroken</translation>
+<translation id="471880041731876836">Je hebt geen toestemming om deze site te bezoeken</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows Netwerkcontrole uitvoeren<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Beleid opnieuw laden</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -478,14 +488,15 @@
<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="4863764087567530506">Deze content probeert je mogelijk te misleiden zodat je software installeert of persoonlijke gegevens openbaar maakt. <ph name="BEGIN_LINK" />Toch weergeven<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Geschiedenis doorzoeken</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Verificatie vereist</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{en nog 1 webpagina}other{en nog # webpagina's}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Deze pagina is vertaald uit een onbekende taal naar het <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Betaling</translation>
<translation id="4926049483395192435">Moet worden opgegeven.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Acties</translation>
<translation id="4958444002117714549">Lijst uitvouwen</translation>
<translation id="4974590756084640048">Waarschuwingen opnieuw inschakelen</translation>
@@ -501,6 +512,7 @@
<translation id="5045550434625856497">Onjuist wachtwoord</translation>
<translation id="5056549851600133418">Artikelen voor jou</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Het proxy-adres controleren<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Mogelijk heb je geen toegang meer tot beveiligde content van bepaalde sites.</translation>
<translation id="5087286274860437796">Het servercertificaat is momenteel niet geldig.</translation>
<translation id="5087580092889165836">Kaart toevoegen</translation>
<translation id="5089810972385038852">Staat</translation>
@@ -508,18 +520,27 @@
<translation id="5095208057601539847">Provincie</translation>
<translation id="5115563688576182185">(64-bits)</translation>
<translation id="5141240743006678641">Gesynchroniseerde wachtwoorden versleutelen met je Google-aanmeldingsgegevens</translation>
-<translation id="514421653919133810">De pagina openen in de incognitomodus (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Foutcode aanwezig in de beleidsreactie</translation>
+<translation id="5159010409087891077">Pagina openen in een nieuw incognitovenster (⇧⌘N)</translation>
<translation id="5171045022955879922">Zoek of typ een URL</translation>
<translation id="5172758083709347301">Computer</translation>
<translation id="5179510805599951267">Niet in het <ph name="ORIGINAL_LANGUAGE" />? Deze fout melden</translation>
-<translation id="5181140330217080051">Downloaden</translation>
<translation id="5190835502935405962">Bladwijzerbalk</translation>
<translation id="5199729219167945352">Experimenten</translation>
<translation id="5205222826937269299">Naam vereist</translation>
<translation id="5222812217790122047">E-mailadres vereist</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Gebruik je Chrome op het werk? Bedrijven kunnen Chrome-instellingen beheren voor hun werknemers. Meer informatie</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Volg deze stappen om de software tijdelijk uit te schakelen zodat je verbinding met internet kunt maken. Je hebt beheerdersrechten nodig.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klik op <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" /> en zoek en selecteer <ph name="BEGIN_BOLD" />Lokale services weergeven<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Selecteer <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Selecteer <ph name="BEGIN_BOLD" />Uitgeschakeld<ph name="END_BOLD" /> onder <ph name="BEGIN_BOLD" />Opstarttype<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Klik onder <ph name="BEGIN_BOLD" />Servicestatus<ph name="END_BOLD" /> op <ph name="BEGIN_BOLD" />Stoppen<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Klik op <ph name="BEGIN_BOLD" />Toepassen<ph name="END_BOLD" /> en vervolgens op <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Ga naar het <ph name="BEGIN_LEARN_MORE_LINK" />Helpcentrum van Chrome<ph name="END_LEARN_MORE_LINK" /> voor meer informatie over hoe je de software definitief van je computer kunt verwijderen.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Je verbinding met deze site is niet privé. Je kunt de VR-modus op elk gewenst moment afsluiten door de headset af te zetten en op Terug te drukken.</translation>
<translation id="5299298092464848405">Fout bij het parseren van het beleid</translation>
<translation id="5308689395849655368">Crashrapportage is uitgeschakeld.</translation>
@@ -536,9 +557,10 @@
<translation id="5439770059721715174">Schemavalidatiefout op '<ph name="ERROR_PATH" />': <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Deze pagina op <ph name="HOST_NAME" /> kan niet worden gevonden</translation>
<translation id="5455374756549232013">Onjuist tijdstempel van beleid</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> van <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Ongeldig</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Opnieuw bewerken</translation>
+<translation id="5481076368049295676">Deze content probeert mogelijk gevaarlijke software op je apparaat te installeren die je gegevens steelt of verwijdert. <ph name="BEGIN_LINK" />Toch weergeven<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Voeg een geldig adres toe</translation>
<translation id="5492298309214877701">Deze site op het intranet van het bedrijf, de organisatie of de school heeft dezelfde URL als een externe website.
<ph name="LINE_BREAK" />
@@ -550,6 +572,7 @@
<translation id="5540224163453853">Kan het gevraagde artikel niet vinden.</translation>
<translation id="5544037170328430102">Een ingesloten pagina op <ph name="SITE" /> meldt het volgende:</translation>
<translation id="5556459405103347317">Opnieuw laden</translation>
+<translation id="5560088892362098740">Vervaldatum</translation>
<translation id="5565735124758917034">Actief</translation>
<translation id="5571083550517324815">Kan niet ophalen van dit adres. Selecteer een ander adres.</translation>
<translation id="5572851009514199876">Start Chrome en log in zodat Chrome kan controleren of je deze site mag openen.</translation>
@@ -564,12 +587,13 @@
<translation id="5629630648637658800">Laden van beleidsinstellingen is mislukt</translation>
<translation id="5631439013527180824">Ongeldige token voor apparaatbeheer</translation>
<translation id="5633066919399395251">Cybercriminelen op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> proberen mogelijk gevaarlijke programma's op je computer te installeren waarmee je gegevens kunnen worden gestolen of verwijderd (bijvoorbeeld foto's, wachtwoorden, berichten en creditcardgegevens). <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Misleidende content geblokkeerd.</translation>
<translation id="5646376287012673985">Locatie</translation>
<translation id="5659593005791499971">E-mailadres</translation>
<translation id="5669703222995421982">Gepersonaliseerde content ontvangen</translation>
<translation id="5675650730144413517">Deze pagina werkt niet</translation>
<translation id="5710435578057952990">De identiteit van deze website is niet geverifieerd.</translation>
-<translation id="5713016350996637505">Misleidende content geblokkeerd</translation>
+<translation id="5719499550583120431">Prepaidkaarten worden geaccepteerd.</translation>
<translation id="5720705177508910913">Huidige gebruiker</translation>
<translation id="5732392974455271431">Je ouders kunnen de blokkering van deze site opheffen</translation>
<translation id="5763042198335101085">Geef een geldig e-mailadres op</translation>
@@ -581,15 +605,14 @@
<translation id="5803412860119678065">Wil je de gegevens van je <ph name="CARD_DETAIL" /> laten invullen?</translation>
<translation id="5810442152076338065">Je verbinding met <ph name="DOMAIN" /> is versleuteld via een verouderde Cipher Suite.</translation>
<translation id="5813119285467412249">&amp;Opnieuw toevoegen</translation>
-<translation id="5814352347845180253">Mogelijk heb je geen toegang meer tot premium content van <ph name="SITE" /> en enkele andere sites.</translation>
<translation id="5838278095973806738">Je moet geen gevoelige gegevens (zoals wachtwoorden of creditcards) opgeven op deze site omdat ze kunnen worden gestolen door aanvallers.</translation>
<translation id="5869405914158311789">Deze site is niet bereikbaar</translation>
<translation id="5869522115854928033">Opgeslagen wachtwoorden</translation>
<translation id="5872918882028971132">Bovenliggende suggesties</translation>
+<translation id="5893752035575986141">Creditcards worden geaccepteerd.</translation>
<translation id="5901630391730855834">Geel</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="5926846154125914413">Mogelijk heb je geen toegang meer tot premium content van bepaalde sites.</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="5967867314010545767">Verwijderen uit geschiedenis</translation>
<translation id="5975083100439434680">Uitzoomen</translation>
@@ -605,6 +628,7 @@
<translation id="6040143037577758943">Sluiten</translation>
<translation id="6042308850641462728">Meer</translation>
<translation id="6047233362582046994">Als je de beveiligingsrisico's begrijpt, kun je <ph name="BEGIN_LINK" />deze site bezoeken<ph name="END_LINK" /> voordat de schadelijke apps zijn verwijderd.</translation>
+<translation id="6047927260846328439">Deze content probeert je mogelijk te misleiden om software te installeren of persoonlijke gegevens openbaar te maken. <ph name="BEGIN_LINK" />Toch weergeven<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Je kunt <ph name="SITE" /> momenteel niet bezoeken, omdat de website gebruikmaakt van certificaatpinning. Netwerkfouten en aanvallen zijn doorgaans tijdelijk, dus deze pagina werkt later waarschijnlijk correct.</translation>
<translation id="6060685159320643512">Pas op, dit zijn geen experimenten om zonder handschoenen aan te pakken</translation>
<translation id="6080696365213338172">Je hebt toegang tot content gekregen met behulp van een certificaat van je beheerder. Gegevens die je verstrekt aan <ph name="DOMAIN" />, kunnen door je beheerder worden onderschept.</translation>
@@ -617,7 +641,6 @@
<translation id="6165508094623778733">Meer informatie</translation>
<translation id="6169916984152623906">Je kunt nu privé browsen, zodat andere mensen die dit apparaat gebruiken, jouw activiteit niet kunnen zien. Downloads en bladwijzers worden echter wel opgeslagen.</translation>
<translation id="6177128806592000436">Je verbinding met deze site is niet veilig</translation>
-<translation id="6184817833369986695">(cohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Controleer je internetverbinding</translation>
<translation id="6218753634732582820">Adres verwijderen uit Chromium?</translation>
<translation id="6221345481584921695">Google Safe Browsing heeft onlangs <ph name="BEGIN_LINK" />malware gedetecteerd<ph name="END_LINK" /> op <ph name="SITE" />. Websites die normaal gesproken veilig zijn, worden soms geïnfecteerd met malware. De schadelijke content is afkomstig van <ph name="SUBRESOURCE_HOST" />, een bekende distributeur van malware.</translation>
@@ -636,13 +659,14 @@
<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>
<translation id="6387478394221739770">Ben je geïnteresseerd in nieuwe, coole Chrome-functies? Probeer ons bèta-kanaal op chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium beschikt over onvoldoende geheugen om deze webpagina weer te geven.</translation>
+<translation id="6397451950548600259">Software op je computer voorkomt dat Chrome veilig verbinding kan maken met internet</translation>
<translation id="6404511346730675251">Bladwijzer bewerken</translation>
<translation id="6410264514553301377">Geef de vervaldatum en CVC-code op voor <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Je hebt je ouder of voogd gevraagd of je deze site mag bezoeken</translation>
@@ -657,6 +681,7 @@
<translation id="647261751007945333">Apparaatbeleid</translation>
<translation id="6477321094435799029">Chrome heeft ongebruikelijke code op deze pagina gedetecteerd en heeft de code geblokkeerd om je persoonlijke gegevens (zoals wachtwoorden, telefoonnummers en creditcards) te beschermen.</translation>
<translation id="6489534406876378309">Uploaden van crashes starten</translation>
+<translation id="6507833130742554667">Creditcards en betaalpassen worden geaccepteerd.</translation>
<translation id="6508722015517270189">Chrome opnieuw starten</translation>
<translation id="6529602333819889595">&amp;Opnieuw verwijderen</translation>
<translation id="6534179046333460208">Fysieke web-suggesties</translation>
@@ -665,13 +690,13 @@
<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="657639383826808334">Deze content kan proberen gevaarlijke software op je apparaat te installeren die je gegevens steelt of verwijdert. <ph name="BEGIN_LINK" />Toch weergeven<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opties voor encryptie</translation>
<translation id="662080504995468778">Blijven</translation>
<translation id="6626291197371920147">Een geldig kaartnummer toevoegen</translation>
<translation id="6628463337424475685">Zoeken via <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Cybercriminelen op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> proberen mogelijk gevaarlijke programma's op je Mac 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="6644283850729428850">Dit beleid is verouderd.</translation>
+<translation id="6657585470893396449">Wachtwoord</translation>
<translation id="6671697161687535275">Formuliersuggestie verwijderen uit Chromium?</translation>
<translation id="6685834062052613830">Uitloggen en configuratie voltooien</translation>
<translation id="6710213216561001401">Vorige</translation>
@@ -702,7 +727,6 @@
<translation id="6970216967273061347">District</translation>
<translation id="6973656660372572881">Zowel vaste proxyservers als een pac-script-URL worden gespecificeerd.</translation>
<translation id="6989763994942163495">Geavanceerde instellingen weergeven...</translation>
-<translation id="7000990526846637657">Geen geschiedenisitems gevonden</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Voor je Google-account kunnen andere vormen van browsegeschiedenis beschikbaar zijn via <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Wachtwoorden</translation>
@@ -717,7 +741,9 @@
<translation id="7129409597930077180">Kan niet verzenden naar dit adres. Selecteer een ander adres.</translation>
<translation id="7138472120740807366">Bezorgingsmethode</translation>
<translation id="7139724024395191329">Emiraat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Betalen niet veilig</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Nu opnieuw starten</translation>
<translation id="7180611975245234373">Vernieuwen</translation>
<translation id="7182878459783632708">Geen beleid ingesteld</translation>
@@ -758,6 +784,7 @@
<translation id="7514365320538308">Downloaden</translation>
<translation id="7518003948725431193">Er is geen webpagina gevonden voor het webadres: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Je verbinding met deze site is niet privé</translation>
<translation id="7535087603100972091">Waarde</translation>
<translation id="7537536606612762813">Verplicht</translation>
<translation id="7542403920425041731">Zodra je bevestigt, worden je creditcardgegevens gedeeld met deze site.</translation>
@@ -767,7 +794,6 @@
<translation id="7552846755917812628">Probeer de volgende tips:</translation>
<translation id="7554791636758816595">Nieuw tabblad</translation>
<translation id="7567204685887185387">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server is mogelijk frauduleus verstrekt. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Deze pagina is geschreven in het<ph name="ORIGINAL_LANGUAGE" />Wil je deze laten vertalen?</translation>
<translation id="7569952961197462199">Creditcard verwijderen uit Chrome?</translation>
<translation id="7569983096843329377">Zwart</translation>
@@ -794,9 +820,11 @@
<translation id="7714464543167945231">Certificaat</translation>
<translation id="7716147886133743102">Geblokkeerd door je beheerder</translation>
<translation id="7716424297397655342">Deze site kan niet worden geladen vanuit het cachegeheugen</translation>
+<translation id="774634243536837715">Gevaarlijke content geblokkeerd.</translation>
<translation id="7752995774971033316">Niet-beheerd</translation>
<translation id="7755287808199759310">Je ouder of voogd kan de blokkering van deze site opheffen</translation>
<translation id="7758069387465995638">De verbinding is mogelijk geblokkeerd door de firewall of antivirussoftware.</translation>
+<translation id="7759163816903619567">Weergegeven domein:</translation>
<translation id="7761701407923456692">Het servercertificaat komt niet overeen met de URL.</translation>
<translation id="7763386264682878361">Payment Manifest Parser</translation>
<translation id="7764225426217299476">Adres toevoegen</translation>
@@ -811,6 +839,7 @@
<translation id="7815407501681723534"><ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> gevonden voor '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">Je bent echter niet onzichtbaar. Als je incognito bent, wordt je browsegeschiedenis niet verborgen voor je werkgever, je internetprovider of de websites die je bezoekt.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Betaalpassen en prepaidkaarten worden geaccepteerd.</translation>
<translation id="7887683347370398519">Controleer je CVC-code en probeer het opnieuw</translation>
<translation id="79338296614623784">Geef een geldig telefoonnummer op</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -830,7 +859,6 @@
<translation id="8041089156583427627">Feedback verzenden</translation>
<translation id="8041940743680923270">Algemene standaardinstelling gebruiken (Vragen)</translation>
<translation id="8088680233425245692">Kan artikel niet bekijken.</translation>
-<translation id="8089520772729574115">minder dan 1 MB</translation>
<translation id="8091372947890762290">Activering is in behandeling op de server</translation>
<translation id="8118489163946903409">Betaalmethode</translation>
<translation id="8131740175452115882">Bevestigen</translation>
@@ -842,10 +870,13 @@
<translation id="8194797478851900357">&amp;Verplaatsen ongedaan maken</translation>
<translation id="8201077131113104583">Ongeldige update-URL voor de extensie met de ID '<ph name="EXTENSION_ID" />'.</translation>
<translation id="8202097416529803614">Besteloverzicht</translation>
+<translation id="8205463626947051446">Site geeft opdringerige advertenties weer</translation>
<translation id="8218327578424803826">Toegewezen locatie:</translation>
<translation id="8225771182978767009">De persoon die deze computer heeft geconfigureerd, heeft deze site geblokkeerd.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Pagina openen op een nieuw incognitotabblad</translation>
<translation id="8241707690549784388">De pagina die je zoekt, heeft informatie gebruikt die je hebt opgegeven Als je terugkeert naar deze pagina, worden acties die je hebt uitgevoerd, mogelijk herhaald. Wil je doorgaan?</translation>
+<translation id="8241712895048303527">Blokkeren op deze site</translation>
<translation id="8249320324621329438">Laatst opgehaald:</translation>
<translation id="8253091569723639551">Factuuradres vereist</translation>
<translation id="8261506727792406068">Verwijderen</translation>
@@ -856,7 +887,6 @@
<translation id="8308427013383895095">De vertaling is mislukt omdat er een probleem is opgetreden met de netwerkverbinding.</translation>
<translation id="8332188693563227489">Toegang tot <ph name="HOST_NAME" /> is geweigerd</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="8344669043927012510">Een pagina openen in de incognitomodus (⇧⌘N)</translation>
<translation id="8349305172487531364">Bladwijzerbalk</translation>
<translation id="8363502534493474904">Schakel de vliegtuigmodus uit</translation>
<translation id="8364627913115013041">Niet ingesteld.</translation>
@@ -866,6 +896,7 @@
<translation id="8398259832188219207">Crashrapport geüpload op <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Crashes (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Je moet twee keer dezelfde wachtwoordzin opgeven.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Instellingen</translation>
<translation id="8433057134996913067">Hiermee word je uitgelogd van de meeste websites.</translation>
<translation id="8437238597147034694">&amp;Verplaatsen ongedaan maken</translation>
@@ -873,14 +904,16 @@
<translation id="8483780878231876732">Log in bij Chrome om kaarten uit je Google-account te gebruiken</translation>
<translation id="8488350697529856933">Van toepassing op</translation>
<translation id="8498891568109133222">Het duurt te lang voordat <ph name="HOST_NAME" /> reageert.</translation>
-<translation id="8532105204136943229">Vervaljaar</translation>
+<translation id="8503813439785031346">Gebruikersnaam</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="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="8620436878122366504">Je ouders hebben dit nog niet goedgekeurd</translation>
<translation id="8647750283161643317">Alle standaardinstellingen herstellen</translation>
+<translation id="8660471606262461360">Van Google Payments</translation>
<translation id="8703575177326907206">Je verbinding met <ph name="DOMAIN" /> is niet gecodeerd.</translation>
<translation id="8718314106902482036">Betaling niet voltooid</translation>
<translation id="8725066075913043281">Opnieuw proberen</translation>
@@ -908,6 +941,7 @@
<translation id="8903921497873541725">Inzoomen</translation>
<translation id="8931333241327730545">Wil je deze kaart opslaan in je Google-account?</translation>
<translation id="8932102934695377596">Je klok loopt achter</translation>
+<translation id="8938939909778640821">Geaccepteerde creditcards en prepaidkaarten</translation>
<translation id="8971063699422889582">Het servercertificaat is verlopen.</translation>
<translation id="8986494364107987395">Automatisch gebruiksstatistieken en crashrapporten naar Google verzenden</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -937,7 +971,6 @@
<translation id="9169664750068251925">Altijd blokkeren op deze site</translation>
<translation id="9170848237812810038">&amp;Ongedaan maken</translation>
<translation id="917450738466192189">Het servercertificaat is ongeldig.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> en nog <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Kan artikel niet toevoegen.</translation>
@@ -946,9 +979,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">FORMULIER LEEGMAKEN</translation>
<translation id="939736085109172342">Nieuwe map</translation>
-<translation id="941721044073577244">Het lijkt erop dat je geen toestemming hebt om deze site te bezoeken</translation>
<translation id="969892804517981540">Officiële build</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Geen}=1{1 item}other{# items}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Ontwikkelaarsbuild</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_no.xtb b/chromium/components/strings/components_strings_no.xtb
index 076896040ac..995cace762d 100644
--- a/chromium/components/strings/components_strings_no.xtb
+++ b/chromium/components/strings/components_strings_no.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Lukk andre apper</translation>
<translation id="1055184225775184556">&amp;Angre tilleggingen</translation>
<translation id="10614374240317010">Aldri lagret</translation>
+<translation id="1066396345355680611">Du kan miste tilgangen til beskyttet innhold fra <ph name="SITE" /> og enkelte andre nettsteder.</translation>
<translation id="106701514854093668">Bokmerker på datamaskinen</translation>
<translation id="1074497978438210769">Ikke sikker</translation>
<translation id="1080116354587839789">Tilpass til vindusbredden</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Leseliste</translation>
<translation id="1264126396475825575">Programstopprapport fra <ph name="CRASH_TIME" /> (ignorert eller ikke lastet opp ennå)</translation>
<translation id="1281526147609854549">Utstedt av <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Farlig innhold er blokkert</translation>
<translation id="1285320974508926690">Oversett aldri dette nettstedet</translation>
<translation id="129553762522093515">Nylig lukket</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Prøv å slette informasjonskapslene dine<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Registreringsdomene:</translation>
<translation id="1340482604681802745">Henteadresse</translation>
-<translation id="1344211575059133124">Det ser ut til at du trenger tillatelse for å besøke dette nettstedet</translation>
<translation id="1344588688991793829">Innstillinger for autofyll i Chromium</translation>
<translation id="1348198688976932919">Nettstedet du er på vei til, inneholder farlige apper</translation>
<translation id="1374468813861204354">forslagene</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identiteten til <ph name="ORGANIZATION" /> på <ph name="LOCALITY" /> er verifisert av <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ja</translation>
<translation id="1430915738399379752">Skriv ut</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> til}other{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> til}}</translation>
<translation id="1506687042165942984">Vis en lagret kopi av denne siden (dvs. en kopi du vet at er utdatert).</translation>
<translation id="1517433312004943670">Det kreves et telefonnummer</translation>
+<translation id="1517500485252541695">Godkjente kreditt- og debetkort</translation>
<translation id="1519264250979466059">Versjonsdato</translation>
+<translation id="1527263332363067270">Venter på tilkobling …</translation>
<translation id="153384715582417236">Det var alt for denne gangen</translation>
<translation id="1549470594296187301">Denne funksjonen kan ikke brukes når JavaScript er slått av.</translation>
<translation id="1555130319947370107">Blå</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Last ned siden senere</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Obligatorisk felt</translation>
<translation id="187918866476621466">Åpne oppstartssidene</translation>
<translation id="1883255238294161206">Skjul liste</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> til}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> til}}</translation>
<translation id="1898423065542865115">Filtrering</translation>
+<translation id="1916770123977586577">Last inn denne siden på nytt for å ta i bruk de oppdaterte innstillingene dine på dette nettstedet.</translation>
+<translation id="1919345977826869612">Annonser</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Ingen}=1{1 nettsted}other{# nettsteder}}</translation>
<translation id="194030505837763158">Gå til <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Godkjente forhåndsbetalte kort</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" />-bokmerker</translation>
<translation id="1973335181906896915">Serialiseringsfeil</translation>
<translation id="1974060860693918893">Avansert</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP-feil</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2282872951544483773">Utilgjengelige eksperimenter</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element}other{<ph name="ITEM_COUNT" /> elementer}}</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>
<translation id="2337852623177822836">Innstillingen kontrolleres av administratoren din</translation>
<translation id="2354001756790975382">Andre bokmerker</translation>
<translation id="2354430244986887761">Google Safe Browsing har nylig <ph name="BEGIN_LINK" />funnet skadelige apper<ph name="END_LINK" /> på <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Fortsett</translation>
<translation id="2365563543831475020">Programstopprapporten fra <ph name="CRASH_TIME" /> ble ikke lastet opp</translation>
<translation id="2367567093518048410">Nivå</translation>
-<translation id="237718015863234333">Det er ingen tilgjengelige alternativer for brukergrensesnitt</translation>
<translation id="2384307209577226199">Bedriftsstandard</translation>
<translation id="2386255080630008482">Tjenerens sertifikat er tilbakekalt.</translation>
<translation id="2392959068659972793">Vis innstillinger uten verdi</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Hvis det er merket av for dette alternativet, lagrer Chromium en kopi av kortet ditt på denne enheten, slik at det går raskere å fylle ut skjemaer.</translation>
<translation id="2498091847651709837">Skann nytt kort</translation>
<translation id="2501278716633472235">Gå tilbake</translation>
+<translation id="2503184589641749290">Godkjente debetkort og forhåndsbetalte kort</translation>
<translation id="2515629240566999685">Sjekk signalet i området ditt</translation>
-<translation id="2516305470678292029">Alternativer for brukergrensesnitt</translation>
<translation id="2539524384386349900">Oppdag</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> sendte et ugyldig svar.</translation>
<translation id="2556876185419854533">&amp;Angre endringen</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Leveringsmetode</translation>
<translation id="277499241957683684">Manglende enhetsoppføring</translation>
<translation id="2784949926578158345">Tilkoblingen ble tilbakestilt.</translation>
+<translation id="2788784517760473862">Godkjente kredittkort</translation>
<translation id="2794233252405721443">Nettstedet er blokkert</translation>
<translation id="2799020568854403057">Nettstedet du er på vei til, inneholder skadelige apper</translation>
<translation id="2803306138276472711">Google Safe Browsing oppdaget nylig <ph name="BEGIN_LINK" />skadelig programvare<ph name="END_LINK" /> på <ph name="SITE" />. Nettsteder som vanligvis er sikre, kan noen ganger være infisert av skadelig programvare.</translation>
<translation id="2824775600643448204">Adresse- og søkefelt</translation>
<translation id="2826760142808435982">Tilkoblingen er kryptert ved hjelp av <ph name="CIPHER" />, og bruker <ph name="KX" /> som mekanisme for nøkkelutveksling.</translation>
<translation id="2835170189407361413">Slett skjemaet</translation>
+<translation id="2851634818064021665">Du trenger tillatelse for å besøke dette nettstedet</translation>
<translation id="2856444702002559011">Det kan hende at angripere prøver å stjele informasjonen din fra <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (for eksempel passord, meldinger og kredittkortinformasjon). <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Ikke last inn på nytt</translation>
-<translation id="2900469785430194048">Google Chrome gikk tom for minne under forsøket på å vise denne nettsiden.</translation>
<translation id="2909946352844186028">En nettverksendring ble oppdaget.</translation>
<translation id="2916038427272391327">Lukk andre programmer</translation>
<translation id="2922350208395188000">Tjenerens sertifikat kan ikke kontrolleres.</translation>
<translation id="2928905813689894207">Faktureringsadresse</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> til}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> til}}</translation>
<translation id="2941952326391522266">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Tjenerens sikkerhetssertifikat er fra <ph name="DOMAIN2" />. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation>
<translation id="2948083400971632585">På innstillingssiden kan du deaktivere eventuelle mellomtjenere for tilkoblinger.</translation>
<translation id="2955913368246107853">Lukk søkefelt</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Hvis du slår på dette alternativet, lagrer Chromium en kopi av kortet ditt på denne enheten, slik at det går raskere å fylle ut skjemaer.</translation>
<translation id="2985398929374701810">Angi en gyldig adresse</translation>
<translation id="2986368408720340940">Denne hentemetoden er ikke tilgjengelig. Prøv en annen metode.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Sider du går til i inkognitofaner, blir ikke værende i nettleserloggen, lageret for informasjonskapsler eller søkeloggen etter at du har lukket alle inkognitofanene. Filer du laster ned eller bokmerker du oppretter, blir lagret.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Øy</translation>
-<translation id="317583078218509884">Nye nettstedinnstillinger trer i kraft etter at siden er lastet inn på nytt.</translation>
<translation id="3176929007561373547">Sjekk innstillingene for proxy-tjeneren eller kontakt nettverksadministratoren
for å forsikre deg om at den fungerer. Følg disse instruksjonene hvis du
ikke tror du trenger å bruke noen proxy-tjener:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Åpne siden i inkognitomodus</translation>
<translation id="320323717674993345">Avbryt betalingen</translation>
<translation id="3207960819495026254">Bokmerket</translation>
<translation id="3225919329040284222">Tjeneren oppga et sertifikat som ikke samsvarte med innebygde forventninger. Disse forventningene benyttes for visse nettsteder med høy sikkerhet, og brukes for å beskytte deg.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Angre omorganiseringen</translation>
<translation id="3282497668470633863">Legg til navnet på kortet</translation>
<translation id="3286538390144397061">Start på nytt nå</translation>
+<translation id="3287510313208355388">Last ned når du er koblet til Internett</translation>
<translation id="3303855915957856445">Søket ga ingen treff</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Hentingsintervall:</translation>
<translation id="3462200631372590220">Skjul detaljer</translation>
<translation id="3467763166455606212">Kortinnhaverens navn er obligatorisk</translation>
-<translation id="3478058380795961209">Utløpsmåned</translation>
<translation id="3479539252931486093">Var dette uventet? <ph name="BEGIN_LINK" />Si fra til oss<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ikke nå</translation>
<translation id="3498215018399854026">Vi kunne ikke nå foreldrene dine akkurat nå. Prøv igjen.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Endre datoen og klokkeslettet under &lt;strong&gt;Generelt&lt;/strong&gt; i &lt;strong&gt;Innstillinger&lt;/strong&gt;-appen.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" />, <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kredittkort og forhåndsbetalte kort godtas.</translation>
<translation id="3582930987043644930">Legg til navn</translation>
<translation id="3583757800736429874">&amp;Flytt likevel</translation>
<translation id="3586931643579894722">Skjul detaljer</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Hvis du ser denne meldingen ofte, kan du prøve disse <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Versjon</translation>
<translation id="3678029195006412963">Forespørselen kunne ikke undertegnes</translation>
+<translation id="3678529606614285348">Åpne siden i et nytt inkognitovindu (Ctrl + Shift + N)</translation>
<translation id="3679803492151881375">Programstopprapport generert <ph name="CRASH_TIME" /> og lastet opp <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Sertifikatinformasjon</translation>
<translation id="3690164694835360974">Påloggingen er ikke trygg</translation>
-<translation id="3693415264595406141">Passord:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Laster inn ...</translation>
<translation id="3712624925041724820">Lisensene er oppbrukt</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Angripere på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan lure deg til å gjøre farlige ting, deriblant å installere programvare eller oppgi personopplysninger (for eksempel passord, telefonnumre og kredittkortinformasjon). <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Oversettelsen mislyktes på grunn av en tjenerfeil.</translation>
<translation id="3759461132968374835">Du har ingen nylig rapportert programstopp. Programstopp som inntraff når rapportering om programstopp var deaktivert, blir ikke vist her.</translation>
+<translation id="3765032636089507299">Safe Browsing-siden er under utvikling.</translation>
<translation id="3778403066972421603">Vil du lagre dette kortet i Google-kontoen din og på denne enheten?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Utløper <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Forespørselen din om å få tilgang til dette nettstedet er sendt til <ph name="NAME" /></translation>
<translation id="3890664840433101773">Legg til e-post</translation>
<translation id="3901925938762663762">Kortet er utløpt</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID-en for den opplastede programstopprapporten: <ph name="CRASH_ID" /> (lokal kræsj-ID: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Det er ikke angitt noen alternative emnenavn i tjenerens sikkerhetssertifikat. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation>
+<translation id="3949601375789751990">Nettleserloggen din vises her</translation>
<translation id="3963721102035795474">Lesermodus</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Ingen}=1{Fra 1 nettsted }other{Fra # nettsteder }}</translation>
<translation id="397105322502079400">Beregner …</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Nettstedsinnstillinger</translation>
<translation id="4169947484918424451">Vil du at Chromium skal lagre dette kortet?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Angripere på dette nettstedet kan prøve å installere farlige programmer på datamaskinen din. Disse kan stjele eller slette informasjonen din (for eksempel bilder, passord, e-post og kredittkortinformasjon).</translation>
<translation id="4372948949327679948">Forventet <ph name="VALUE_TYPE" />-verdi.</translation>
<translation id="4377125064752653719">Du forsøkte å gå til <ph name="DOMAIN" />, men sertifikatet tjeneren presenterte har blitt trukket tilbake av utstederen. Dette innebærer at sikkerhetsinformasjonen tjeneren presenterte ikke er klarert. Det kan hende at du kommuniserer med en angriper.</translation>
-<translation id="4381091992796011497">Brukernavn:</translation>
<translation id="4394049700291259645">Slå av</translation>
<translation id="4406896451731180161">søkeresultater</translation>
<translation id="4424024547088906515">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Sikkerhetssertifikatet til tjeneren er ikke klarert av Chrome. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation>
<translation id="4432688616882109544">Enten godtok ikke <ph name="HOST_NAME" /> påloggingssertifikatet ditt, eller så ble det ikke oppgitt.</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="4506176782989081258">Valideringsfeil: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kontakt systemadministratoren</translation>
<translation id="450710068430902550">Deling med administratoren</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Vil du fjerne adressen fra Chrome?</translation>
<translation id="4592951414987517459">Tilkoblingen til <ph name="DOMAIN" /> er kryptert med en moderne chifferserie.</translation>
<translation id="4594403342090139922">&amp;Angre slettingen</translation>
+<translation id="4611292653554630842">Logg på</translation>
<translation id="4619615317237390068">Faner fra andre enheter</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Sikkerhetssertifikatet til tjeneren inneholder feil. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation>
<translation id="4690462567478992370">Slutt å bruke et ugyldig sertifikat</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Tilkoblingen ble avbrutt</translation>
+<translation id="471880041731876836">Du har ikke tillatelse til å besøke dette nettstedet</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Kjør Windows Nettverksdiagnose<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Last inn retningslinjer på nytt</translation>
<translation id="4728558894243024398">Plattform</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Dette innholdet kan prøve å lure deg til å installere programvare eller oppgi personopplysninger. <ph name="BEGIN_LINK" />Vis det likevel<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Søk i loggen</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Autentisering kreves</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{og 1 nettside til}other{og # nettsider til}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Denne siden er oversatt til <ph name="LANGUAGE_LANGUAGE" /> fra et ukjent språk</translation>
<translation id="4923459931733593730">Betaling</translation>
<translation id="4926049483395192435">Må angis.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Handlinger</translation>
<translation id="4958444002117714549">Utvid liste</translation>
<translation id="4974590756084640048">Slå på advarsler på nytt</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Feil passord</translation>
<translation id="5056549851600133418">Artikler for deg</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Sjekk proxy-tjeneradressen<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Det kan hende du mister tilgangen til beskyttet innhold fra enkelte nettsteder.</translation>
<translation id="5087286274860437796">Sertifikatet til tjeneren er ikke gyldig for øyeblikket.</translation>
<translation id="5087580092889165836">Legg til et kort</translation>
<translation id="5089810972385038852">Fylke / delstat</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provins</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5141240743006678641">Kryptér synkroniserte passord med Google-legitimasjonen din</translation>
-<translation id="514421653919133810">Åpne siden i inkognitomodus (Ctrl + Shift + N)</translation>
<translation id="5145883236150621069">Feilkode i responsen for enhetsinnstillinger</translation>
+<translation id="5159010409087891077">Åpne siden i et nytt inkognitovindu (⇧ + ⌘ + N)</translation>
<translation id="5171045022955879922">Søk, eller skriv inn en nettadresse</translation>
<translation id="5172758083709347301">Datamaskin</translation>
<translation id="5179510805599951267">Er ikke dette <ph name="ORIGINAL_LANGUAGE" />? Rapportér denne feilen</translation>
-<translation id="5181140330217080051">Laster ned</translation>
<translation id="5190835502935405962">Bokmerkerad</translation>
<translation id="5199729219167945352">Eksperimenter</translation>
<translation id="5205222826937269299">Navn er obligatorisk</translation>
<translation id="5222812217790122047">E-post er obligatorisk</translation>
<translation id="5251803541071282808">Nettsky</translation>
<translation id="5277279256032773186">Bruker du Chrome på jobben? Bedrifter kan administrere Chrome-innstillingene for de ansatte. Finn ut mer</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Følg disse trinnene for å deaktivere programvaren midlertidig, sånn at du kan komme deg på nettet. Du må ha administratorrettigheter.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klikk på <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, og søk etter og velg <ph name="BEGIN_BOLD" />«View local services»<ph name="END_BOLD" /> (se lokale tjenester).
+ <ph name="LIST_ITEM" />Velg <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Velg <ph name="BEGIN_BOLD" />Disabled<ph name="END_BOLD" /> (deaktivert) under <ph name="BEGIN_BOLD" />Startup type<ph name="END_BOLD" /> (oppstartstype).
+ <ph name="LIST_ITEM" />Klikk på <ph name="BEGIN_BOLD" />Stop<ph name="END_BOLD" /> (stopp) under <ph name="BEGIN_BOLD" />Service status<ph name="END_BOLD" /> (tjenestestatus).
+ <ph name="LIST_ITEM" />Klikk på <ph name="BEGIN_BOLD" />Apply<ph name="END_BOLD" /> (bruk) og deretter på <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Gå til <ph name="BEGIN_LEARN_MORE_LINK" />brukerstøtten for Chrome<ph name="END_LEARN_MORE_LINK" /> for å finne ut hvordan du fjerner programvaren fra datamaskinen permanent.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Tilkoblingen til dette nettstedet er ikke privat. For å gå ut av VR-modus, fjern brillene og trykk på tilbake.</translation>
<translation id="5299298092464848405">Feil under analysen av enhetsinnstillingene</translation>
<translation id="5308689395849655368">Rapportering av programstopp er deaktivert.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Skjemavalideringsfeil i «<ph name="ERROR_PATH" />»: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Denne siden på <ph name="HOST_NAME" /> ble ikke funnet</translation>
<translation id="5455374756549232013">Feil tidsstempel for enhetsinnstillinger</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> av <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Ugyldig</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> til}other{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> til}}</translation>
<translation id="5470861586879999274">&amp;Endre likevel</translation>
+<translation id="5481076368049295676">Dette innholdet kan prøve å installere farlig programvare som stjeler eller sletter informasjon, på enheten din. <ph name="BEGIN_LINK" />Vis det likevel<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Du må angi en gyldig adresse</translation>
<translation id="5492298309214877701">Dette nettstedet på selskapets, organisasjonens eller skolens intranett har samme nettadresse som et eksternt nettsted.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Den forespurte artikkelen ble ikke funnet.</translation>
<translation id="5544037170328430102">En innebygd side på <ph name="SITE" /> sier:</translation>
<translation id="5556459405103347317">Last inn på nytt</translation>
+<translation id="5560088892362098740">Utløpsdato</translation>
<translation id="5565735124758917034">Aktiv</translation>
<translation id="5571083550517324815">Kan ikke hente på denne adressen. Velg en annen adresse.</translation>
<translation id="5572851009514199876">Start og logg på Chrome, så Chrome kan sjekke om du har tillatelse til å gå til dette nettstedet.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Kunne ikke laste in angivelsen for enhetsinnstillinger</translation>
<translation id="5631439013527180824">Ugyldig token for enhetsadministrering</translation>
<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="5646376287012673985">Posisjon</translation>
<translation id="5659593005791499971">E-post</translation>
<translation id="5669703222995421982">Få innhold med et personlig preg</translation>
<translation id="5675650730144413517">Denne siden fungerer ikke</translation>
<translation id="5710435578057952990">Identiteten til dette nettstedet er ikke verifisert.</translation>
-<translation id="5713016350996637505">Villedende innhold er blokkert</translation>
+<translation id="5719499550583120431">Forhåndsbetalte kort godtas.</translation>
<translation id="5720705177508910913">Gjeldende bruker</translation>
<translation id="5732392974455271431">Foreldrene dine kan oppheve blokkeringen for deg</translation>
<translation id="5763042198335101085">Angi en gyldig e-postadresse</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Vil du fylle ut informasjonen knyttet til <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Tilkoblingen til <ph name="DOMAIN" /> er kryptert med en foreldet chifferserie.</translation>
<translation id="5813119285467412249">&amp;Legg til likevel</translation>
-<translation id="5814352347845180253">Det kan hende du mister tilgang til premium-innhold fra <ph name="SITE" /> og andre nettsteder.</translation>
<translation id="5838278095973806738">Du bør ikke oppgi sensitiv informasjon på dette nettstedet (for eksempel passord eller kredittkort) fordi den kan bli stjålet av angripere.</translation>
<translation id="5869405914158311789">Dette nettstedet er ikke tilgjengelig</translation>
<translation id="5869522115854928033">Lagrede passord</translation>
<translation id="5872918882028971132">Overordnede forslag</translation>
+<translation id="5893752035575986141">Kredittkort godtas.</translation>
<translation id="5901630391730855834">Gul</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="5926846154125914413">Det kan hende du mister tilgang til premium-innhold fra enkelte nettsteder.</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="5967867314010545767">Fjern fra loggen</translation>
<translation id="5975083100439434680">Zoom ut</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Lukk</translation>
<translation id="6042308850641462728">Mer</translation>
<translation id="6047233362582046994">Hvis du forstår sikkerhetsrisikoen, kan du <ph name="BEGIN_LINK" />gå til dette nettstedet<ph name="END_LINK" /> før de skadelige appene er fjernet.</translation>
+<translation id="6047927260846328439">Dette innholdet kan prøve å lure deg til å installere programvare eller oppgi personopplysninger. <ph name="BEGIN_LINK" />Vis det likevel<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Du kan ikke gå til <ph name="SITE" /> akkurat nå, siden nettstedet bruker sertifikatfesting. Nettverksfeil og -angrep er vanligvis midlertidige, så denne siden fungerer sannsynligvis senere.</translation>
<translation id="6060685159320643512">Vær forsiktig. Disse eksperimentene kan bite</translation>
<translation id="6080696365213338172">Du har åpnet innhold via et administratorlevert sertifikat. Data du sender til <ph name="DOMAIN" /> kan stoppes av administratoren din.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Les mer</translation>
<translation id="6169916984152623906">Nå kan du surfe privat. Andre som bruker denne enheten, ser ikke aktiviteten din, men nedlastinger og bokmerker blir lagret.</translation>
<translation id="6177128806592000436">Tilkoblingen til dette nettstedet er ikke sikker</translation>
-<translation id="6184817833369986695">(kohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Kontrollér Internett-tilkoblingen</translation>
<translation id="6218753634732582820">Vil du fjerne adressen fra Chromium?</translation>
<translation id="6221345481584921695">Google Safe Browsing oppdaget nylig <ph name="BEGIN_LINK" />skadelig programvare<ph name="END_LINK" /> på <ph name="SITE" />. Nettsteder som vanligvis er trygge, kan noen ganger være infisert med skadelig programvare. Det skadelige innholdet kommer fra <ph name="SUBRESOURCE_HOST" />, som er en kjent distributør av skadelig programvare.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Interessert i nye kule Chrome-funksjoner? Prøv betakanalen vår på chrome.com/beta</translation>
-<translation id="6389758589412724634">Chromium gikk tom for minne under forsøket på å vise denne nettsiden.</translation>
+<translation id="6397451950548600259">Det er programvare på datamaskinen din som hindrer Chrome i å koble trygt til Internett</translation>
<translation id="6404511346730675251">Rediger bokmerket</translation>
<translation id="6410264514553301377">Skriv inn utløpsdatoen og verifiseringskoden for <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Du har spurt forelderen din om det er greit å besøke dette nettstedet</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Enhetsinnstillinger</translation>
<translation id="6477321094435799029">Chrome har oppdaget uvanlig kode på denne siden og blokkert den for å beskytte personopplysningene dine (for eksempel passord, telefonnumre og kredittkortinformasjon).</translation>
<translation id="6489534406876378309">Start opplastingen av krasj</translation>
+<translation id="6507833130742554667">Kreditt- og debetkort godtas.</translation>
<translation id="6508722015517270189">Start Chrome på nytt</translation>
<translation id="6529602333819889595">&amp;Slett likevel</translation>
<translation id="6534179046333460208">Fysisk nett-forslag</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Dette innholdet kan prøve å installere farlig programvare som stjeler eller sletter informasjonen din, på enheten du bruker. <ph name="BEGIN_LINK" />Vis det likevel<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Krypteringsalternativer</translation>
<translation id="662080504995468778">Bli her</translation>
<translation id="6626291197371920147">Legg til et gyldig kortnummer</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Søk</translation>
<translation id="6630809736994426279">Angripere som for øyeblikket er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, kan prøve å installere farlige programmer på Macen 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="6644283850729428850">Denne retningslinjen er foreldet.</translation>
+<translation id="6657585470893396449">Passord</translation>
<translation id="6671697161687535275">Vil du fjerne forslaget fra Chromium?</translation>
<translation id="6685834062052613830">Logg av og fullfør konfigurasjonen</translation>
<translation id="6710213216561001401">Forrige</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Område</translation>
<translation id="6973656660372572881">Både statiske proxytjenere og en .pac-skriptnettadresse er angitt.</translation>
<translation id="6989763994942163495">Vis avanserte innstillinger</translation>
-<translation id="7000990526846637657">Fant ingen loggoppføringer</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Google-kontoen din kan ha andre typer nettlesingslogger på <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Passord</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Kan ikke sende til denne adressen. Velg en annen adresse.</translation>
<translation id="7138472120740807366">Leveringsmetode</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> til}other{<ph name="PAYMENT_METHOD_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> til}}</translation>
<translation id="7155487117670177674">Betalingen er ikke trygg</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> til}other{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> til}}</translation>
<translation id="7179921470347911571">Start på nytt nå</translation>
<translation id="7180611975245234373">Last inn på nytt</translation>
<translation id="7182878459783632708">Ingen retningslinjer er angitt</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Last ned</translation>
<translation id="7518003948725431193">Finner ingen nettside for denne nettadressen: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Tilkoblingen til dette nettstedet er ikke privat</translation>
<translation id="7535087603100972091">Verdi</translation>
<translation id="7537536606612762813">Obligatorisk</translation>
<translation id="7542403920425041731">Når du bekrefter, deles kortinformasjonen din med dette nettstedet.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Prøv følgende tips:</translation>
<translation id="7554791636758816595">Ny fane</translation>
<translation id="7567204685887185387">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Tjenerens sikkerhetssertifikat kan ha blitt utstedt på uredelig vis. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> til}other{<ph name="CONTACT_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> til}}</translation>
<translation id="7568593326407688803">Denne siden er på<ph name="ORIGINAL_LANGUAGE" />Vil du ha den oversatt?</translation>
<translation id="7569952961197462199">Vil du fjerne kredittkortet fra Chrome?</translation>
<translation id="7569983096843329377">Svart</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Sertifikat</translation>
<translation id="7716147886133743102">Blokkert av administratoren din</translation>
<translation id="7716424297397655342">Dette nettstedet kan ikke lastes inn fra bufferen</translation>
+<translation id="774634243536837715">Farlig innhold er blokkert.</translation>
<translation id="7752995774971033316">Administreres ikke</translation>
<translation id="7755287808199759310">Forelderen din kan oppheve blokkeringen for deg</translation>
<translation id="7758069387465995638">Brannmur- eller antivirusprogramvare kan ha blokkert tilkoblingen.</translation>
+<translation id="7759163816903619567">Visningsdomene:</translation>
<translation id="7761701407923456692">Tjenerens sertifikat samsvarer ikke med nettadressen.</translation>
<translation id="7763386264682878361">Parser for betalingsmanifest</translation>
<translation id="7764225426217299476">Legg til adresse</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Fant <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> for «<ph name="SEARCH_STRING" />»</translation>
<translation id="785549533363645510">Du er imidlertid ikke usynlig. Inkognitomodus skjuler ikke surfingen din for arbeidsgiveren din, Internett-leverandøren eller nettstedene du besøker.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Debetkort og forhåndsbetalte kort godtas.</translation>
<translation id="7887683347370398519">Kontrollér CVC-koden din, og prøv igjen.</translation>
<translation id="79338296614623784">Angi et gyldig telefonnummer</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Send tilbakemelding</translation>
<translation id="8041940743680923270">Bruk global standardinnstilling (Spør)</translation>
<translation id="8088680233425245692">Kunne ikke åpne artikkelen.</translation>
-<translation id="8089520772729574115">under 1 MB</translation>
<translation id="8091372947890762290">Aktivering venter på tjeneren</translation>
<translation id="8118489163946903409">Betalingsmåte</translation>
<translation id="8131740175452115882">Bekreft</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Angre flyttingen</translation>
<translation id="8201077131113104583">Ugyldig oppdaterings-URL for utvidelse med ID «<ph name="EXTENSION_ID" />».</translation>
<translation id="8202097416529803614">Bestillingssammendrag</translation>
+<translation id="8205463626947051446">Nettstedet viser ofte forstyrrende annonser</translation>
<translation id="8218327578424803826">Tilordnet posisjon:</translation>
<translation id="8225771182978767009">Personen som konfigurerte denne datamaskinen, har valgt å blokkere dette nettstedet.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Åpne siden i et nytt inkognitovindu</translation>
<translation id="8241707690549784388">Siden du ser etter, brukte informasjon som du anga. Hvis du går tilbake til denne siden, kan det føre til at handlinger som er utført, blir gjentatt. Vil du fortsette?</translation>
+<translation id="8241712895048303527">Blokkér på dette nettstedet</translation>
<translation id="8249320324621329438">Sist hentet:</translation>
<translation id="8253091569723639551">Fakturaadresse er obligatorisk</translation>
<translation id="8261506727792406068">Slett</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Oversettelsen mislyktes på grunn av et problem med nettverksforbindelsen.</translation>
<translation id="8332188693563227489">Forsøket på å koble til <ph name="HOST_NAME" /> ble avvist</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="8344669043927012510">Åpne siden i inkognitomodus (⇧ + ⌘ + N)</translation>
<translation id="8349305172487531364">Bokmerkerad</translation>
<translation id="8363502534493474904">Slå av flymodus</translation>
<translation id="8364627913115013041">Ikke angitt.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Programstopprapport lastet opp <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Kræsj (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Du må angi den samme passordfrasen to ganger.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Innstillinger</translation>
<translation id="8433057134996913067">Dette logger deg av de fleste nettsteder.</translation>
<translation id="8437238597147034694">&amp;Angre flyttingen</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">For å bruke kredittkort du har lagret i Google-kontoen din, logg på Chrome</translation>
<translation id="8488350697529856933">Gjelder for</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> brukte for lang tid på å svare.</translation>
-<translation id="8532105204136943229">Utløpsår</translation>
+<translation id="8503813439785031346">Brukernavn</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="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="8620436878122366504">Foreldrene dine har ikke godkjent det ennå</translation>
<translation id="8647750283161643317">Tilbakestill alle til standard</translation>
+<translation id="8660471606262461360">Fra Google Payments</translation>
<translation id="8703575177326907206">Tilkoblingen til <ph name="DOMAIN" /> er ikke kryptert.</translation>
<translation id="8718314106902482036">Betalingen er ikke fullført</translation>
<translation id="8725066075913043281">Prøv igjen</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Zoom inn</translation>
<translation id="8931333241327730545">Vil du lagre dette kortet i Google-kontoen din?</translation>
<translation id="8932102934695377596">Klokken går for sent</translation>
+<translation id="8938939909778640821">Godkjente kredittkort og forhåndsbetalte kort</translation>
<translation id="8971063699422889582">Tjenerens sertifikat er utløpt.</translation>
<translation id="8986494364107987395">Send bruksstatistikk og programstopprapporter automatisk til Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Blokkér alltid på dette nettstedet</translation>
<translation id="9170848237812810038">&amp;Angre</translation>
<translation id="917450738466192189">Tjenerens sertifikat er ugyldig.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> til}other{<ph name="SHIPPING_OPTION_PREVIEW" /> og <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> til}}</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>
<translation id="9207861905230894330">Kunne ikke legge til artikkelen.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">SLETT INNHOLDET I SKJEMAET</translation>
<translation id="939736085109172342">Ny mappe</translation>
-<translation id="941721044073577244">Det ser ut til at du ikke har tillatelse til å besøke dette nettstedet</translation>
<translation id="969892804517981540">Offisiell delversjon</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Ingen}=1{1 element}other{# elementer}}</translation>
+<translation id="981121421437150478">Uten nett</translation>
<translation id="988159990683914416">Utviklerversjon</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_pl.xtb b/chromium/components/strings/components_strings_pl.xtb
index 1f78a34bf15..5236cc93d89 100644
--- a/chromium/components/strings/components_strings_pl.xtb
+++ b/chromium/components/strings/components_strings_pl.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Zamknij inne aplikacje</translation>
<translation id="1055184225775184556">&amp;Cofnij dodanie</translation>
<translation id="10614374240317010">Nigdy nie zapisane</translation>
+<translation id="1066396345355680611">Możesz stracić dostęp do treści chronionych na: <ph name="SITE" /> i na niektórych innych stronach.</translation>
<translation id="106701514854093668">Zakładki na komputerze</translation>
<translation id="1074497978438210769">Niezabezpieczona</translation>
<translation id="1080116354587839789">Dopasuj do szerokości</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Do przeczytania</translation>
<translation id="1264126396475825575">Utworzono raport o awarii w dniu: <ph name="CRASH_TIME" /> (nie został jeszcze przesłany ani zignorowany)</translation>
<translation id="1281526147609854549">Wystawca: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Zablokowano niebezpieczne treści</translation>
<translation id="1285320974508926690">Nigdy nie tłumacz tej witryny</translation>
<translation id="129553762522093515">Ostatnio zamknięte</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Usuń pliki cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domena rejestracji:</translation>
<translation id="1340482604681802745">Adres odbioru</translation>
-<translation id="1344211575059133124">Wygląda na to, że aby wejść na tę stronę, musisz uzyskać pozwolenie</translation>
<translation id="1344588688991793829">Ustawienia autouzupełniania Chromium...</translation>
<translation id="1348198688976932919">Strona, którą chcesz otworzyć, zawiera niebezpieczne aplikacje</translation>
<translation id="1374468813861204354">sugestie</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Tożsamość organizacji <ph name="ORGANIZATION" /> (<ph name="LOCALITY" />) została zweryfikowana przed wystawcę <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Tak</translation>
<translation id="1430915738399379752">Drukuj</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> inna}few{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> inne}many{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> innych}other{<ph name="PAYMENT_METHOD_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> innej}}</translation>
<translation id="1506687042165942984">Pokaż zapisaną (tzn. nieaktualną) kopię tej strony.</translation>
<translation id="1517433312004943670">Numer telefonu jest wymagany</translation>
+<translation id="1517500485252541695">Akceptowane karty kredytowe i debetowe</translation>
<translation id="1519264250979466059">Data kompilacji</translation>
+<translation id="1527263332363067270">Czekam na połączenie…</translation>
<translation id="153384715582417236">Na razie to wszystko</translation>
<translation id="1549470594296187301">Aby można było korzystać z tej funkcji, musi być włączony JavaScript.</translation>
<translation id="1555130319947370107">Niebieski</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Pobierz stronę później</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Pole wymagane</translation>
<translation id="187918866476621466">Otwórz strony początkowe</translation>
<translation id="1883255238294161206">Zwiń listę</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> inny}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> inne}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> innych}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> innego}}</translation>
<translation id="1898423065542865115">Filtrowanie</translation>
+<translation id="1916770123977586577">Załaduj stronę ponownie, by zastosować na niej zaktualizowane ustawienia</translation>
+<translation id="1919345977826869612">Reklamy</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Brak}=1{1 witryna}few{# witryny}many{# witryn}other{# witryny}}</translation>
<translation id="194030505837763158">Wejdź na <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Akceptowane karty przedpłacone</translation>
<translation id="1962204205936693436">Zakładki z <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Podczas przekształcania do postaci szeregowej wystąpił błąd</translation>
<translation id="1974060860693918893">Zaawansowane</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Błąd HTTP</translation>
<translation id="2270484714375784793">Numer telefonu</translation>
<translation id="2282872951544483773">Niedostępne eksperymenty</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element}few{<ph name="ITEM_COUNT" /> elementy}many{<ph name="ITEM_COUNT" /> elementów}other{<ph name="ITEM_COUNT" /> elementu}}</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>
<translation id="2337852623177822836">Ustawienie kontrolowane przez administratora</translation>
<translation id="2354001756790975382">Inne zakładki</translation>
<translation id="2354430244986887761">Funkcja Bezpieczne przeglądanie Google niedawno <ph name="BEGIN_LINK" />wykryła szkodliwe aplikacje<ph name="END_LINK" /> na <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Dalej</translation>
<translation id="2365563543831475020">Raport o awarii utworzony w dniu: <ph name="CRASH_TIME" /> nie został przesłany</translation>
<translation id="2367567093518048410">Poziom</translation>
-<translation id="237718015863234333">Brak dostępnych alternatywnych interfejsów</translation>
<translation id="2384307209577226199">Domyślne zasady przedsiębiorstwa</translation>
<translation id="2386255080630008482">Certyfikat serwera został unieważniony.</translation>
<translation id="2392959068659972793">Pokaż zasady bez ustawionej wartości</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Jeśli zaznaczysz tę opcję, Chromium zapisze kopię Twojej karty na tym urządzeniu, by umożliwić Ci szybsze wypełnianie formularzy.</translation>
<translation id="2498091847651709837">Zeskanuj nową kartę</translation>
<translation id="2501278716633472235">Wróć</translation>
+<translation id="2503184589641749290">Akceptowane karty debetowe i przedpłacone</translation>
<translation id="2515629240566999685">Sprawdź sygnał w swojej okolicy</translation>
-<translation id="2516305470678292029">Alternatywne interfejsy</translation>
<translation id="2539524384386349900">Wykrywaj</translation>
<translation id="255002559098805027">Serwer <ph name="HOST_NAME" /> wysłał nieprawidłową odpowiedź.</translation>
<translation id="2556876185419854533">&amp;Cofnij edycję</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Metoda wysyłki</translation>
<translation id="277499241957683684">Brak rekordu urządzenia</translation>
<translation id="2784949926578158345">Połączenie zostało zresetowane.</translation>
+<translation id="2788784517760473862">Akceptowane karty kredytowe</translation>
<translation id="2794233252405721443">Strona zablokowana</translation>
<translation id="2799020568854403057">Strona, którą chcesz otworzyć, zawiera szkodliwe aplikacje</translation>
<translation id="2803306138276472711">Bezpieczne przeglądanie Google <ph name="BEGIN_LINK" />wykryło ostatnio złośliwe oprogramowanie<ph name="END_LINK" /> na <ph name="SITE" />. Strony, które zazwyczaj są bezpieczne, zostają czasem zainfekowane destrukcyjnym oprogramowaniem.</translation>
<translation id="2824775600643448204">Pasek adresu i wyszukiwania</translation>
<translation id="2826760142808435982">Połączenie jest szyfrowane i uwierzytelniane algorytmem <ph name="CIPHER" />, a mechanizm wymiany kluczy to <ph name="KX" />.</translation>
<translation id="2835170189407361413">Wyczyść formularz</translation>
+<translation id="2851634818064021665">Musisz mieć pozwolenie, by wejść na tę stronę</translation>
<translation id="2856444702002559011">Osoby atakujące mogą próbować wykraść Twoje informacje ze strony <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (na przykład hasła, wiadomości lub dane kart kredytowych). <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Nie ładuj ponownie</translation>
-<translation id="2900469785430194048">Podczas próby wyświetlenia tej strony w Google Chrome zabrakło pamięci.</translation>
<translation id="2909946352844186028">Wykryto zmianę sieci.</translation>
<translation id="2916038427272391327">Zamknij inne programy</translation>
<translation id="2922350208395188000">Nie można sprawdzić certyfikatu serwera.</translation>
<translation id="2928905813689894207">Adres rozliczeniowy</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat pochodzi z <ph name="DOMAIN2" />. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Jeśli włączysz tę opcję, Chromium zapisze kopię Twojej karty na tym urządzeniu, by umożliwić Ci szybsze wypełnianie formularzy.</translation>
<translation id="2985398929374701810">Wpisz prawidłowy adres</translation>
<translation id="2986368408720340940">Ta metoda odbioru jest niedostępna. Wybierz inną.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Po zamknięciu wszystkich kart incognito wyświetlane na nich strony nie pozostawią żadnych śladów w historii przeglądarki, magazynie plików cookie ani historii wyszukiwania. Pobrane pliki i utworzone zakładki zostaną jednak zachowane.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Wyspa</translation>
-<translation id="317583078218509884">Nowe ustawienia pozwoleń dla stron zostaną zastosowane po ponownym załadowaniu strony.</translation>
<translation id="3176929007561373547">Sprawdź ustawienia serwera proxy lub skontaktuj się z administratorem sieci,
by upewnić się, że serwer proxy działa. Jeśli uważasz, że
nie powinien być on używany:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Otwórz stronę w trybie incognito</translation>
<translation id="320323717674993345">Anuluj płatność</translation>
<translation id="3207960819495026254">Dodano do zakładek</translation>
<translation id="3225919329040284222">Serwer przedstawił certyfikat, który nie pasuje do zaprogramowanych oczekiwań. Oczekiwania mają chronić Cię w określonych witrynach o wysokim poziomie zabezpieczeń.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Cofnij zmianę kolejności</translation>
<translation id="3282497668470633863">Dodaj imię i nazwisko na karcie</translation>
<translation id="3286538390144397061">Uruchom ponownie teraz</translation>
+<translation id="3287510313208355388">Pobierz, gdy będę online</translation>
<translation id="3303855915957856445">Brak wyników wyszukiwania</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Okres pobierania:</translation>
<translation id="3462200631372590220">Ukryj zaawansowane</translation>
<translation id="3467763166455606212">Wymagane jest imię i nazwisko posiadacza karty</translation>
-<translation id="3478058380795961209">Miesiąc utraty ważności</translation>
<translation id="3479539252931486093">Zaskoczyło Cię to? <ph name="BEGIN_LINK" />Daj nam znać<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Nie teraz</translation>
<translation id="3498215018399854026">Obecnie nie możemy się skontaktować z Twoim rodzicem. Spróbuj ponownie.</translation>
@@ -343,6 +345,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Karty kredytowe i przedpłacone są akceptowane.</translation>
<translation id="3582930987043644930">Dodaj nazwę</translation>
<translation id="3583757800736429874">&amp;Ponów przeniesienie</translation>
<translation id="3586931643579894722">Ukryj szczegóły</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Jeśli często widzisz ten komunikat, przeczytaj <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Wersja</translation>
<translation id="3678029195006412963">Nie udało się podpisać żądania</translation>
+<translation id="3678529606614285348">Otwórz stronę w nowym oknie incognito (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Raport o awarii zarejestrowano: <ph name="CRASH_TIME" />, przesłano: <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informacje o certyfikacie</translation>
<translation id="3690164694835360974">Logowanie nie jest bezpieczne</translation>
-<translation id="3693415264595406141">Hasło:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Ładuję...</translation>
<translation id="3712624925041724820">Brak wolnych licencji</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Osoby atakujące na stronie <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą podstępem nakłonić Cię do wykonania czynności niebezpiecznych takich jak zainstalowanie oprogramowania czy ujawnienie danych osobowych (na przykład haseł, numerów telefonów lub danych kart kredytowych). <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Tłumaczenie nie powiodło się z powodu błędu serwera.</translation>
<translation id="3759461132968374835">Brak ostatnio zgłoszonych awarii. Awarie, które nastąpiły wówczas, gdy funkcja zgłaszania awarii była wyłączona, nie są tutaj wymienione.</translation>
+<translation id="3765032636089507299">Strona funkcji Bezpieczne przeglądanie jest w budowie.</translation>
<translation id="3778403066972421603">Czy chcesz zapisać tę kartę na swoim koncie Google i na tym urządzeniu?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Wygasa: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Twoja prośba o dostęp to tej strony została wysłana do: <ph name="NAME" /></translation>
<translation id="3890664840433101773">Dodaj adres e-mail</translation>
<translation id="3901925938762663762">Karta straciła ważność</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Przesłano raport o awarii – identyfikator: <ph name="CRASH_ID" /> (lokalny identyfikator awarii: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa nie określa alternatywnych nazw podmiotu. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
+<translation id="3949601375789751990">Tutaj wyświetla się Twoja historia przeglądania</translation>
<translation id="3963721102035795474">Tryb czytnika</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Brak}=1{Z 1 witryny }few{Z # witryn }many{Z # witryn }other{Z # witryny }}</translation>
<translation id="397105322502079400">Obliczanie...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Ustawienia witryny</translation>
<translation id="4169947484918424451">Czy Chromium ma zapisać tę kartę?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Osoby atakujące tę stronę mogą próbować zainstalować na Twoim komputerze niebezpieczne programy przeznaczone do kradzieży lub usuwania Twoich danych (na przykład zdjęć, haseł, wiadomości czy numerów kart kredytowych).</translation>
<translation id="4372948949327679948">Oczekiwano wartości typu <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Próbujesz wejść na <ph name="DOMAIN" />, ale serwer przedstawił certyfikat unieważniony przez wystawcę. Oznacza to, że dane uwierzytelniające podane przez serwer są zupełnie niewiarygodne. Możliwe, że komunikujesz się z intruzem.</translation>
-<translation id="4381091992796011497">Nazwa użytkownika:</translation>
<translation id="4394049700291259645">Wyłącz</translation>
<translation id="4406896451731180161">wyniki wyszukiwania</translation>
<translation id="4424024547088906515">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa nie jest zaufany w Chrome. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
<translation id="4432688616882109544">Serwer <ph name="HOST_NAME" /> nie zaakceptował lub nie otrzymał Twojego certyfikatu logowania.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Usunąć ten adres z Chrome?</translation>
<translation id="4592951414987517459">Połączenie z <ph name="DOMAIN" /> jest szyfrowane przy użyciu nowoczesnego zestawu szyfrów.</translation>
<translation id="4594403342090139922">&amp;Cofnij usunięcie</translation>
+<translation id="4611292653554630842">Zaloguj się</translation>
<translation id="4619615317237390068">Karty z innych urządzeń</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa ma błędy. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
<translation id="4690462567478992370">Przestań używać nieprawidłowego certyfikatu</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Połączenie zostało przerwane</translation>
+<translation id="471880041731876836">Nie masz pozwolenia, by wejść na tę stronę</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Uruchom Diagnostykę sieci systemu Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Odśwież zasady</translation>
<translation id="4728558894243024398">Platforma</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Te treści mogą próbować przez oszustwo nakłonić Cię do zainstalowania oprogramowania lub ujawnienia danych osobowych. <ph name="BEGIN_LINK" />Wyświetl mimo to<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Przeszukaj historię</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Wymagane uwierzytelnienie</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{i jeszcze 1 strona}few{i jeszcze # strony}many{i jeszcze # stron}other{i jeszcze # strony}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Ta strona została przetłumaczona z nieznanego języka na język <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Płatność</translation>
<translation id="4926049483395192435">Musi być określona.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Czynności</translation>
<translation id="4958444002117714549">Rozwiń listę</translation>
<translation id="4974590756084640048">Ponownie włącz ostrzeżenia</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Nieprawidłowe hasło</translation>
<translation id="5056549851600133418">Artykuły dla Ciebie</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Sprawdź adres serwera proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Możesz stracić dostęp do treści chronionych na niektórych stronach.</translation>
<translation id="5087286274860437796">Certyfikat serwera nie jest obecnie ważny.</translation>
<translation id="5087580092889165836">Dodaj kartę</translation>
<translation id="5089810972385038852">Stan</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Prowincja</translation>
<translation id="5115563688576182185">(64-bitowa)</translation>
<translation id="5141240743006678641">Szyfruj synchronizowane hasła za pomocą danych logowania Google</translation>
-<translation id="514421653919133810">Otwórz stronę w trybie incognito (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">W odebranej polityce znajduje się kod błędu</translation>
+<translation id="5159010409087891077">Otwórz stronę w nowym oknie incognito (⇧⌘N)</translation>
<translation id="5171045022955879922">Wyszukaj lub wpisz URL</translation>
<translation id="5172758083709347301">Komputer</translation>
<translation id="5179510805599951267">Jeśli to nie jest język <ph name="ORIGINAL_LANGUAGE" />, zgłoś błąd</translation>
-<translation id="5181140330217080051">Pobieranie</translation>
<translation id="5190835502935405962">Pasek zakładek</translation>
<translation id="5199729219167945352">Eksperymenty</translation>
<translation id="5205222826937269299">Nazwa jest wymagana</translation>
<translation id="5222812217790122047">E-mail jest wymagany</translation>
<translation id="5251803541071282808">Chmura</translation>
<translation id="5277279256032773186">Korzystasz z Chrome w pracy? Firmy mogą zarządzać ustawieniami Chrome swoich pracowników. Więcej informacji</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Wykonaj te czynności, by tymczasowo wyłączyć to oprogramowanie i połączyć się z internetem. Będziesz potrzebować uprawnień administratora.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Kliknij <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" /> i wyszukaj albo wybierz opcję <ph name="BEGIN_BOLD" />„Wyświetl usługi lokalne”<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Wybierz <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />W sekcji <ph name="BEGIN_BOLD" />Typ uruchomienia<ph name="END_BOLD" /> wybierz <ph name="BEGIN_BOLD" />Wyłączony<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />W sekcji <ph name="BEGIN_BOLD" />Stan usługi<ph name="END_BOLD" /> kliknij <ph name="BEGIN_BOLD" />Zatrzymaj<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Kliknij <ph name="BEGIN_BOLD" />Zastosuj<ph name="END_BOLD" />, a następnie <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Odwiedź <ph name="BEGIN_LEARN_MORE_LINK" />Centrum pomocy Chrome<ph name="END_LEARN_MORE_LINK" />, by dowiedzieć się, jak trwale usunąć to oprogramowanie ze swojego komputera.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Połączenie z tą stroną nie jest prywatne. Aby w dowolnym momencie zamknąć tryb VR, zdejmij gogle i naciśnij Wstecz.</translation>
<translation id="5299298092464848405">Podczas przetwarzania zasady wystąpił błąd</translation>
<translation id="5308689395849655368">Funkcja zgłaszania awarii jest wyłączona.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Błąd podczas sprawdzania poprawności schematu – „<ph name="ERROR_PATH" />”: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Nie można znaleźć tej strony na <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Nieprawidłowy czas zasady</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> z <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Nieprawidłowe</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}many{<ph name="CONTACT_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Ponów edycję</translation>
+<translation id="5481076368049295676">Te treści mogą próbować instalować na Twoim urządzeniu niebezpieczne oprogramowanie, które wykrada lub usuwa dane. <ph name="BEGIN_LINK" />Wyświetl mimo to<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Dodaj poprawny adres</translation>
<translation id="5492298309214877701">Ta witryna w intranecie firmy, organizacji czy szkoły ma ten sam URL co zewnętrzna strona internetowa.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Nie udało się znaleźć tego artykułu.</translation>
<translation id="5544037170328430102">Komunikat z elementu umieszczonego na stronie <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Odśwież</translation>
+<translation id="5560088892362098740">Data ważności</translation>
<translation id="5565735124758917034">Aktywny</translation>
<translation id="5571083550517324815">Odbiór spod tego adresu jest niemożliwy. Wybierz inny adres.</translation>
<translation id="5572851009514199876">Uruchom Chrome i zaloguj się w nim, by mógł sprawdzić, czy masz uprawnienia dostępu do tej strony.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Ładowanie ustawień zasady nie powiodło się</translation>
<translation id="5631439013527180824">Nieprawidłowy token zarządzania urządzeniem</translation>
<translation id="5633066919399395251">Osoby obecnie atakujące stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą próbować zainstalować na Twoim komputerze niebezpieczne programy, które wykradają lub usuwają informacje (na przykład zdjęcia, hasła, wiadomości lub dane kart kredytowych). <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Zablokowano treści wprowadzające w błąd.</translation>
<translation id="5646376287012673985">Lokalizacja</translation>
<translation id="5659593005791499971">E-mail</translation>
<translation id="5669703222995421982">Otrzymywanie spersonalizowanych treści</translation>
<translation id="5675650730144413517">Ta strona nie działa</translation>
<translation id="5710435578057952990">Tożsamość witryny nie została zweryfikowana.</translation>
-<translation id="5713016350996637505">Zablokowano treści wprowadzające w błąd</translation>
+<translation id="5719499550583120431">Karty przedpłacone są akceptowane.</translation>
<translation id="5720705177508910913">Bieżący użytkownik</translation>
<translation id="5732392974455271431">Mogą ją dla Ciebie odblokować Twoi rodzice</translation>
<translation id="5763042198335101085">Wpisz prawidłowy adres e-mail</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Chcesz wpisać dane swojej karty (<ph name="CARD_DETAIL" />)?</translation>
<translation id="5810442152076338065">Połączenie z <ph name="DOMAIN" /> jest szyfrowane przy użyciu przestarzałego zestawu szyfrów.</translation>
<translation id="5813119285467412249">&amp;Ponów dodanie</translation>
-<translation id="5814352347845180253">Możesz stracić dostęp do płatnych treści w <ph name="SITE" /> i na niektórych innych stronach.</translation>
<translation id="5838278095973806738">Nie podawaj żadnych informacji poufnych (takich jak hasła czy karty kredytowe) w tej witrynie, bo osoby atakujące będą mogły je wykraść.</translation>
<translation id="5869405914158311789">Ta witryna jest nieosiągalna</translation>
<translation id="5869522115854928033">Zapisane hasła</translation>
<translation id="5872918882028971132">Propozycje rodziców</translation>
+<translation id="5893752035575986141">Karty kredytowe są akceptowane.</translation>
<translation id="5901630391730855834">Żółty</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="5926846154125914413">Możesz stracić dostęp do płatnych treści na niektórych stronach.</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="5967867314010545767">Usuń z historii</translation>
<translation id="5975083100439434680">Pomniejsz</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Zamknij</translation>
<translation id="6042308850641462728">Więcej</translation>
<translation id="6047233362582046994">Jeśli rozumiesz zagrożenie, możesz <ph name="BEGIN_LINK" />wejść na tę stronę<ph name="END_LINK" />, zanim szkodliwe aplikacje zostaną usunięte.</translation>
+<translation id="6047927260846328439">Te treści mogą próbować podstępem nakłonić Cię do zainstalowania oprogramowania lub ujawnienia danych osobowych. <ph name="BEGIN_LINK" />Wyświetl mimo to<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Nie możesz teraz otworzyć strony <ph name="SITE" />, ponieważ stosuje ona przypinanie certyfikatów. Błędy sieciowe i ataki są zazwyczaj tymczasowe, więc prawdopodobnie strona będzie dostępna później.</translation>
<translation id="6060685159320643512">Ostrożnie, na tych eksperymentach można się sparzyć</translation>
<translation id="6080696365213338172">Masz dostęp do treści dzięki certyfikatowi dostarczonemu przez administratora. Administrator może odczytać dane, jakie udostępnisz w <ph name="DOMAIN" />.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Więcej informacji</translation>
<translation id="6169916984152623906">Teraz możesz korzystać z internetu w trybie prywatnym. Inne osoby używające tego urządzenia nie zobaczą Twojej aktywności. Pamiętaj tylko, że zakładki i pobrane pliki są zapisywane.</translation>
<translation id="6177128806592000436">Twoje połączenie z tą witryną nie jest bezpieczne</translation>
-<translation id="6184817833369986695">(kohorta: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Sprawdź połączenie z internetem</translation>
<translation id="6218753634732582820">Usunąć ten adres z Chromium?</translation>
<translation id="6221345481584921695">Bezpieczne przeglądanie Google <ph name="BEGIN_LINK" />wykryło ostatnio złośliwe oprogramowanie<ph name="END_LINK" /> na <ph name="SITE" />. Strony, które zazwyczaj są bezpieczne, zostają czasem zainfekowane destrukcyjnym oprogramowaniem. Źródłem złośliwej zawartości jest <ph name="SUBRESOURCE_HOST" /> – znany dystrybutor złośliwego oprogramowania.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Interesują Cię nowe, przydatne funkcje Chrome? Wypróbuj wersję beta ze strony chrome.com/beta.</translation>
-<translation id="6389758589412724634">Podczas próby wyświetlenia tej strony w Chromium zabrakło pamięci.</translation>
+<translation id="6397451950548600259">Oprogramowanie na Twoim komputerze uniemożliwia Chrome bezpieczne połączenie się z internetem</translation>
<translation id="6404511346730675251">Edytuj zakładkę</translation>
<translation id="6410264514553301377">Wpisz datę ważności i kod CVC karty <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Zapytałeś rodzica, czy możesz odwiedzić tę stronę</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Zasady dotyczące urządzeń</translation>
<translation id="6477321094435799029">Chrome wykrył nietypowy kod na tej stronie i zablokował ją, by chronić Twoje dane osobowe (np. hasła, numery telefonu czy dane kart kredytowych).</translation>
<translation id="6489534406876378309">Rozpocznij przesyłanie informacji o awariach</translation>
+<translation id="6507833130742554667">Karty kredytowe i debetowe są akceptowane.</translation>
<translation id="6508722015517270189">Uruchom ponownie Chrome</translation>
<translation id="6529602333819889595">&amp;Ponów usunięcie</translation>
<translation id="6534179046333460208">Sugestie dotyczące internetu rzeczy</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Te treści mogą próbować instalować na Twoim urządzeniu niebezpieczne oprogramowanie, które wykrada lub usuwa Twoje dane. <ph name="BEGIN_LINK" />Wyświetl mimo to<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opcje szyfrowania</translation>
<translation id="662080504995468778">Zostań</translation>
<translation id="6626291197371920147">Dodaj prawidłowy numer karty</translation>
<translation id="6628463337424475685">Wyszukiwarka <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Osoby obecnie atakujące stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą próbować zainstalować na Twoim komputerze Mac niebezpieczne programy wykradające lub usuwające 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="6644283850729428850">Zasada jest przestarzała.</translation>
+<translation id="6657585470893396449">Hasło</translation>
<translation id="6671697161687535275">Usunąć tę podpowiedź do formularza z Chromium?</translation>
<translation id="6685834062052613830">Wyloguj się i dokończ konfigurację</translation>
<translation id="6710213216561001401">Wstecz</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Okręg</translation>
<translation id="6973656660372572881">Określono zarówno stałe serwery proxy, jak i URL skryptu PAC.</translation>
<translation id="6989763994942163495">Pokaż ustawienia zaawansowane...</translation>
-<translation id="7000990526846637657">Brak wpisów historii</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Inne rodzaje historii przeglądania mogą być nadal dostępne na Twoim koncie Google na <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Hasła</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Nie można wysłać pod ten adres. Wybierz inny.</translation>
<translation id="7138472120740807366">Metoda dostawy</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}many{<ph name="PAYMENT_METHOD_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Płatność nie jest bezpieczna</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}many{<ph name="SHIPPING_OPTION_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> i jeszcze <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Uruchom ponownie teraz</translation>
<translation id="7180611975245234373">Odśwież</translation>
<translation id="7182878459783632708">Brak ustawionych zasad</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Pobierz</translation>
<translation id="7518003948725431193">Nie znaleziono strony internetowej pod adresem <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Twoje połączenie z tą stroną nie jest prywatne</translation>
<translation id="7535087603100972091">Wartość</translation>
<translation id="7537536606612762813">Obowiązkowe</translation>
<translation id="7542403920425041731">Po potwierdzeniu szczegółowe dane karty zostaną udostępnione tej stronie.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Skorzystaj z tych wskazówek:</translation>
<translation id="7554791636758816595">Nowa karta</translation>
<translation id="7567204685887185387">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa mógł zostać wydany w celu oszustwa. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> inny}few{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> inne}many{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> innych}other{<ph name="CONTACT_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> innego}}</translation>
<translation id="7568593326407688803">Język strony:<ph name="ORIGINAL_LANGUAGE" />Chcesz ją przetłumaczyć?</translation>
<translation id="7569952961197462199">Usunąć tę kartę kredytową z Chrome?</translation>
<translation id="7569983096843329377">Czarny</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certyfikat</translation>
<translation id="7716147886133743102">Zablokowane przez administratora</translation>
<translation id="7716424297397655342">Nie można załadować tej strony z pamięci podręcznej</translation>
+<translation id="774634243536837715">Zablokowano niebezpieczne treści.</translation>
<translation id="7752995774971033316">Niezarządzany</translation>
<translation id="7755287808199759310">Może ją dla Ciebie odblokować Twój rodzic</translation>
<translation id="7758069387465995638">Połączenie mogło zostać zablokowane przez zaporę sieciową lub program antywirusowy.</translation>
+<translation id="7759163816903619567">Wyświetlana domena:</translation>
<translation id="7761701407923456692">Certyfikat serwera jest niezgodny z adresem URL.</translation>
<translation id="7763386264682878361">Parser pliku manifestu dla płatności</translation>
<translation id="7764225426217299476">Dodaj adres</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Znalezione <ph name="SEARCH_RESULTS" /> dla zapytania „<ph name="SEARCH_STRING" />”: <ph name="NUMBER_OF_RESULTS" /></translation>
<translation id="785549533363645510">To jednak nie znaczy, że Cię nie widać. Nawet gdy przejdziesz w tryb incognito, Twój pracodawca, dostawca usług internetowych czy webmasterzy stron, na które wchodzisz, mogą dowiedzieć się, co przeglądasz.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" />: <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Karty debetowe i przedpłacone są akceptowane.</translation>
<translation id="7887683347370398519">Sprawdź kod CVC i spróbuj ponownie</translation>
<translation id="79338296614623784">Wpisz prawidłowy numer telefonu</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Prześlij opinię</translation>
<translation id="8041940743680923270">Użyj globalnej wartości domyślnej (Pytaj)</translation>
<translation id="8088680233425245692">Nie udało się wyświetlić artykułu.</translation>
-<translation id="8089520772729574115">mniej niż 1 MB</translation>
<translation id="8091372947890762290">Aktywacja oczekuje na serwerze</translation>
<translation id="8118489163946903409">Forma płatności</translation>
<translation id="8131740175452115882">Potwierdź</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Cofnij przeniesienie</translation>
<translation id="8201077131113104583">Nieprawidłowy URL aktualizowania dla rozszerzenia o identyfikatorze „<ph name="EXTENSION_ID" />”.</translation>
<translation id="8202097416529803614">Podsumowanie zamówienia</translation>
+<translation id="8205463626947051446">Na stronie wyświetlają się uciążliwe reklamy</translation>
<translation id="8218327578424803826">Przypisana lokalizacja:</translation>
<translation id="8225771182978767009">Administrator tego komputera zablokował tę witrynę.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Otwórz stronę w nowej karcie incognito</translation>
<translation id="8241707690549784388">Strona, na której wyszukiwane są wprowadzone przez Ciebie informacje. Powrót do tej strony może spowodować konieczność powtórzenia wykonanych czynności. Czy chcesz kontynuować?</translation>
+<translation id="8241712895048303527">Blokuj na tej stronie</translation>
<translation id="8249320324621329438">Ostatnie pobieranie:</translation>
<translation id="8253091569723639551">Wymagany jest adres rozliczeniowy</translation>
<translation id="8261506727792406068">Usuń</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Tłumaczenie nie powiodło się z powodu problemu z połączeniem sieciowym.</translation>
<translation id="8332188693563227489">Odmowa dostępu do <ph name="HOST_NAME" /></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="8344669043927012510">Otwórz stronę w trybie incognito (⇧⌘N)</translation>
<translation id="8349305172487531364">Pasek zakładek</translation>
<translation id="8363502534493474904">Wyłącz tryb samolotowy</translation>
<translation id="8364627913115013041">Nie ustawiono.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Raport o awarii przesłano: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Awarie (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Musisz dwukrotnie wpisać to samo hasło.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ustawienia</translation>
<translation id="8433057134996913067">Zostaniesz wylogowany z większości stron internetowych.</translation>
<translation id="8437238597147034694">&amp;Cofnij przeniesienie</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Aby użyć kart z konta Google, zaloguj się w Chrome</translation>
<translation id="8488350697529856933">Dotyczy</translation>
<translation id="8498891568109133222">Serwer <ph name="HOST_NAME" /> potrzebował zbyt wiele czasu na odpowiedź.</translation>
-<translation id="8532105204136943229">Rok utraty ważności</translation>
+<translation id="8503813439785031346">Nazwa użytkownika</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="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="8620436878122366504">Twoi rodzice jeszcze na to nie zezwolili</translation>
<translation id="8647750283161643317">Przywróć wszystkie ustawienia domyślne</translation>
+<translation id="8660471606262461360">Z Google Payments</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="8725066075913043281">Spróbuj ponownie</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Powiększ</translation>
<translation id="8931333241327730545">Chcesz zapisać tę kartę na swoim koncie Google?</translation>
<translation id="8932102934695377596">Twój zegar się spóźnia</translation>
+<translation id="8938939909778640821">Akceptowane karty kredytowe i przedpłacone</translation>
<translation id="8971063699422889582">Ważność certyfikatu serwera wygasła.</translation>
<translation id="8986494364107987395">Automatycznie przesyłaj do Google statystyki użytkowania i raporty o awariach</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -924,7 +958,7 @@
<translation id="9020200922353704812">Wymagany jest adres rozliczeniowy karty kredytowej</translation>
<translation id="9020542370529661692">Ta strona została przetłumaczona na <ph name="TARGET_LANGUAGE" />.</translation>
<translation id="9035022520814077154">Błąd zabezpieczeń</translation>
-<translation id="9038649477754266430">Użyj usługi podpowiedzi, by strony ładowały się szybciej</translation>
+<translation id="9038649477754266430">Używaj podpowiedzi, by strony ładowały się szybciej</translation>
<translation id="9039213469156557790">Ta strona zawiera także niezabezpieczone zasoby. Podczas przesyłania mogą je wyświetlić inni użytkownicy, a osoby atakujące mogą je zmodyfikować, by zmienić sposób działania strony.</translation>
<translation id="9049981332609050619">Podjęto próbę nawiązania połączenia z witryną <ph name="DOMAIN" />, jednak serwer przedstawił nieprawidłowy certyfikat.</translation>
<translation id="9050666287014529139">Hasło</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Zawsze blokuj w tej witrynie</translation>
<translation id="9170848237812810038">&amp;Cofnij</translation>
<translation id="917450738466192189">Certyfikat serwera jest nieprawidłowy.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> inna}few{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> inne}many{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> innych}other{<ph name="SHIPPING_OPTION_PREVIEW" /> i <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> innej}}</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>
<translation id="9207861905230894330">Nie udało się dodać artykułu.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">WYCZYŚĆ FORMULARZ</translation>
<translation id="939736085109172342">Nowy folder</translation>
-<translation id="941721044073577244">Wygląda na to, że nie masz uprawnień, by wejść na tę stronę</translation>
<translation id="969892804517981540">Oficjalna wersja</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Brak}=1{1 element}few{# elementy}many{# elementów}other{# elementu}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Build</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_pt-BR.xtb b/chromium/components/strings/components_strings_pt-BR.xtb
index b50edcdd1fd..a4cc4e8b960 100644
--- a/chromium/components/strings/components_strings_pt-BR.xtb
+++ b/chromium/components/strings/components_strings_pt-BR.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Fechar outros apps</translation>
<translation id="1055184225775184556">&amp;Desfazer adicionar</translation>
<translation id="10614374240317010">Nunca salvam</translation>
+<translation id="1066396345355680611">É possível que você perca o acesso a conteúdos protegidos de <ph name="SITE" /> e de alguns outros sites.</translation>
<translation id="106701514854093668">Favoritos em computador</translation>
<translation id="1074497978438210769">Não seguro</translation>
<translation id="1080116354587839789">Ajustar à largura</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lista de leitura</translation>
<translation id="1264126396475825575">Relatório de erros registrado em <ph name="CRASH_TIME" /> (ainda não enviado ou ignorado)</translation>
<translation id="1281526147609854549">Publicado por <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Conteúdo perigoso bloqueado</translation>
<translation id="1285320974508926690">Nunca traduzir este site</translation>
<translation id="129553762522093515">Recentemente fechadas</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Tente limpar os cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domínio de inscrição:</translation>
<translation id="1340482604681802745">Endereço de retirada</translation>
-<translation id="1344211575059133124">Parece que você precisa de permissão para visitar este site</translation>
<translation id="1344588688991793829">Configurações de preenchimento automático do Chromium...</translation>
<translation id="1348198688976932919">O site a seguir contém apps perigosos</translation>
<translation id="1374468813861204354">sugestões</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">A identidade de <ph name="ORGANIZATION" /> em <ph name="LOCALITY" /> foi confirmada por <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Sim</translation>
<translation id="1430915738399379752">Imprimir</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Mostrar uma cópia salva (ou seja, reconhecidamente desatualizada) desta página.</translation>
<translation id="1517433312004943670">Número de telefone necessário</translation>
+<translation id="1517500485252541695">Cartões de crédito e débito aceitos</translation>
<translation id="1519264250979466059">Data da versão</translation>
+<translation id="1527263332363067270">Aguardando conexão...</translation>
<translation id="153384715582417236">Isso é tudo por enquanto</translation>
<translation id="1549470594296187301">O JavaScript deve ser ativado para usar este recurso.</translation>
<translation id="1555130319947370107">Azul</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Fazer o download da página mais tarde</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Campo obrigatório</translation>
<translation id="187918866476621466">Abrir páginas de inicialização</translation>
<translation id="1883255238294161206">Recolher lista</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtragem</translation>
+<translation id="1916770123977586577">Para aplicar as configurações atualizadas a este site, recarregue a página</translation>
+<translation id="1919345977826869612">Anúncios</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Nenhum}=1{1 site}one{# site}other{# sites}}</translation>
<translation id="194030505837763158">Ir para <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Cartões pré-pagos aceitos</translation>
<translation id="1962204205936693436">Favoritos de <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Erro de serialização</translation>
<translation id="1974060860693918893">Avançado</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Erro HTTP</translation>
<translation id="2270484714375784793">Número do telefone</translation>
<translation id="2282872951544483773">Experiências não disponíveis</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item}one{<ph name="ITEM_COUNT" /> item}other{<ph name="ITEM_COUNT" /> itens}}</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>
<translation id="2337852623177822836">Configuração controlada pelo administrador</translation>
<translation id="2354001756790975382">Outros favoritos</translation>
<translation id="2354430244986887761">Recentemente, o recurso "Navegação segura" do Google <ph name="BEGIN_LINK" />encontrou apps prejudiciais<ph name="END_LINK" /> em <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continuar</translation>
<translation id="2365563543831475020">O relatório de erros registrado em <ph name="CRASH_TIME" /> não foi enviado</translation>
<translation id="2367567093518048410">Nível</translation>
-<translation id="237718015863234333">Nenhuma IU alternativa disponível</translation>
<translation id="2384307209577226199">Padrão da empresa</translation>
<translation id="2386255080630008482">O certificado do servidor foi revogado.</translation>
<translation id="2392959068659972793">Mostrar políticas sem valor definido</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Se esta opção for selecionada, o Chromium armazenará uma cópia do seu cartão neste dispositivo para preencher de formulários mais rapidamente.</translation>
<translation id="2498091847651709837">Digitalizar novo cartão</translation>
<translation id="2501278716633472235">Voltar</translation>
+<translation id="2503184589641749290">Cartões de débito e pré-pagos aceitos</translation>
<translation id="2515629240566999685">Verificar o sinal na sua área</translation>
-<translation id="2516305470678292029">IUs alternativas</translation>
<translation id="2539524384386349900">Detectar</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> enviou uma resposta inválida.</translation>
<translation id="2556876185419854533">&amp;Desfazer editar</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Método de envio</translation>
<translation id="277499241957683684">Registro de dispositivo não encontrado</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>
<translation id="2799020568854403057">O site a seguir contém apps prejudiciais</translation>
<translation id="2803306138276472711">A Navegação segura do Google recentemente <ph name="BEGIN_LINK" />detectou malware<ph name="END_LINK" /> em <ph name="SITE" />. Websites que geralmente são seguros, algumas vezes, são infetados com malware.</translation>
<translation id="2824775600643448204">Barra de endereço e de pesquisa</translation>
<translation id="2826760142808435982">A conexão foi criptografada e autenticada utilizando <ph name="CIPHER" /> e usa <ph name="KX" /> como o mecanismo de troca de chave.</translation>
<translation id="2835170189407361413">Limpar formulário</translation>
+<translation id="2851634818064021665">Você precisa de permissão para visitar este site</translation>
<translation id="2856444702002559011">Invasores podem estar tentando roubar suas informações de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (por exemplo, senhas, mensagens ou cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Não atualizar</translation>
-<translation id="2900469785430194048">O Google Chrome ficou sem memória ao tentar exibir essa página da Web.</translation>
<translation id="2909946352844186028">Foi detectada uma alteração na rede.</translation>
<translation id="2916038427272391327">Fechar outros programas</translation>
<translation id="2922350208395188000">O certificado do servidor não pode ser verificado.</translation>
<translation id="2928905813689894207">Endereço de cobrança</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança é de <ph name="DOMAIN2" />. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Se esta opção for ativada, o Chromium armazenará uma cópia do seu cartão neste dispositivo para preencher formulários mais rapidamente.</translation>
<translation id="2985398929374701810">Informe um endereço válido</translation>
<translation id="2986368408720340940">Esse método de retirada não está disponível. Tente um método diferente.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Quando você está no modo invisível, as páginas que você visita não aparecem no seu histórico de navegação e de pesquisa, nem armazenam arquivos "cookies". Mas os downloads e os favoritos continuam funcionando normalmente.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Ilha</translation>
-<translation id="317583078218509884">As novas configurações de permissões de site entrarão em vigor quando a página for atualizada.</translation>
<translation id="3176929007561373547">Verifique suas configurações de proxy ou entre em contato com o administrador de rede para
verificar se o servidor proxy está funcionando. Se você acredita que não deve
usar um servidor proxy:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Abrir a página no modo sem rastros</translation>
<translation id="320323717674993345">Cancelar pagamento</translation>
<translation id="3207960819495026254">Adicionada aos favoritos</translation>
<translation id="3225919329040284222">O servidor apresentou um certificado que não coincide com as expectativas incorporadas. Estas expectativas são incluídas para determinados websites de alta segurança com a finalidade de oferecer proteção a você.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Desfazer reordenar</translation>
<translation id="3282497668470633863">Adicionar nome (como consta no cartão)</translation>
<translation id="3286538390144397061">Reiniciar agora</translation>
+<translation id="3287510313208355388">Fazer o download quando estiver on-line</translation>
<translation id="3303855915957856445">Nenhum resultado de pesquisa encontrado</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Buscar intervalo:</translation>
<translation id="3462200631372590220">Ocultar detalhes</translation>
<translation id="3467763166455606212">O nome do titular do cartão é obrigatório</translation>
-<translation id="3478058380795961209">Mês de vencimento</translation>
<translation id="3479539252931486093">Isso foi inesperado? <ph name="BEGIN_LINK" />Informe-nos<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Não agora</translation>
<translation id="3498215018399854026">Não foi possível contatar seu pai/mãe no momento. Tente novamente.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Ajuste a data e hora na seção &lt;strong&gt;Geral&lt;/strong&gt; do app &lt;strong&gt;Ajustes&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Cartões de crédito e pré-pagos são aceitos.</translation>
<translation id="3582930987043644930">Adicionar nome</translation>
<translation id="3583757800736429874">&amp;Refazer mover</translation>
<translation id="3586931643579894722">Ocultar detalhes</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Caso veja esta página com frequência, tente <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisão</translation>
<translation id="3678029195006412963">Não foi possível assinar a solicitação</translation>
+<translation id="3678529606614285348">Abrir página em uma nova janela sem rastros (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Relatório de erros registrado em <ph name="CRASH_TIME" />, enviado em <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informações do certificado</translation>
<translation id="3690164694835360974">Login não seguro</translation>
-<translation id="3693415264595406141">Senha:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Carregando...</translation>
<translation id="3712624925041724820">Licenças esgotadas</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Invasores em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem levar você a fazer algo perigoso, como instalar software ou revelar suas informações pessoais (por exemplo, senhas, números de telefone ou cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">A tradução falhou devido a um erro no servidor.</translation>
<translation id="3759461132968374835">Você não relatou falhas recentemente. As falhas que ocorreram quando o relatório de erros estava desativado não aparecerão aqui.</translation>
+<translation id="3765032636089507299">A página de navegação segura está em construção.</translation>
<translation id="3778403066972421603">Deseja salvar este cartão na sua Conta do Google e neste dispositivo?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Validade: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Sua solicitação para acessar este site foi enviada para <ph name="NAME" /></translation>
<translation id="3890664840433101773">Adicionar e-mail</translation>
<translation id="3901925938762663762">O cartão expirou</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Código do relatório de falha enviado: <ph name="CRASH_ID" /> (código de falha local: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança dele não especifica a extensão Nomes alternativos do requerente. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
+<translation id="3949601375789751990">Seu histórico de navegação aparece aqui</translation>
<translation id="3963721102035795474">Modo leitor</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Nenhum}=1{1 site }one{# site }other{# sites }}</translation>
<translation id="397105322502079400">Calculando...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Configurações do site</translation>
<translation id="4169947484918424451">Deseja que o Chromium salve este cartão?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Invasores nesse site podem tentar instalar programas perigosos no seu computador para roubar ou excluir informações (por exemplo, fotos, senhas, mensagens e cartões de crédito).</translation>
<translation id="4372948949327679948">Valor <ph name="VALUE_TYPE" /> esperado.</translation>
<translation id="4377125064752653719">Você tentou acessar <ph name="DOMAIN" />, mas o certificado que o servidor apresentou foi revogado pelo seu emissor. Isso significa que as credenciais de segurança que o servidor apresentou não são nem um pouco seguras. Talvez você esteja se comunicando com um invasor.</translation>
-<translation id="4381091992796011497">Nome de usuário:</translation>
<translation id="4394049700291259645">Desativar</translation>
<translation id="4406896451731180161">resultados da pesquisa</translation>
<translation id="4424024547088906515">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança não é confiável para o Chrome. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
<translation id="4432688616882109544">O certificado de login não foi aceito por <ph name="HOST_NAME" /> ou não foi fornecido.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Remover endereço do Chrome?</translation>
<translation id="4592951414987517459">Sua conexão com <ph name="DOMAIN" /> foi criptografada usando um pacote de criptografia moderno.</translation>
<translation id="4594403342090139922">&amp;Desfazer exclusão</translation>
+<translation id="4611292653554630842">Fazer login</translation>
<translation id="4619615317237390068">Guias de outros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança contém erros. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
<translation id="4690462567478992370">Suspender o uso de um certificado inválido</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">A conexão foi interrompida</translation>
+<translation id="471880041731876836">Você não tem permissão para visitar este site</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Executar o Diagnóstico de Rede do Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Atualizar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Esse conteúdo pode tentar enganar você para que instale um software ou revele informações pessoais. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Histórico de pesquisa</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Autenticação necessária</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{e mais 1 página da Web}one{e mais # página da Web}other{e mais # páginas da Web}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Esta página foi traduzida de um idioma desconhecido para o <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pagamento</translation>
<translation id="4926049483395192435">Deve ser especificado.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Ações</translation>
<translation id="4958444002117714549">Expandir lista</translation>
<translation id="4974590756084640048">Reativar avisos</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Senha incorreta</translation>
<translation id="5056549851600133418">Artigos para você</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Verificar o endereço do proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">É possível que você perca o acesso a conteúdos protegidos de alguns sites.</translation>
<translation id="5087286274860437796">O certificado do servidor não é válido no momento.</translation>
<translation id="5087580092889165836">Adicionar cartão</translation>
<translation id="5089810972385038852">Estado</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Província</translation>
<translation id="5115563688576182185">64 bits</translation>
<translation id="5141240743006678641">Criptografar senhas sincronizadas com suas credenciais do Google</translation>
-<translation id="514421653919133810">Abrir a página no modo sem rastros (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Código de erro presente na resposta da política</translation>
+<translation id="5159010409087891077">Abrir página em uma nova janela sem rastros (⇧⌘N)</translation>
<translation id="5171045022955879922">Pesquisar ou digitar URL</translation>
<translation id="5172758083709347301">Máquina</translation>
<translation id="5179510805599951267">Não está em <ph name="ORIGINAL_LANGUAGE" />? Informe este erro</translation>
-<translation id="5181140330217080051">Fazendo download</translation>
<translation id="5190835502935405962">Barra de favoritos</translation>
<translation id="5199729219167945352">Experimentos</translation>
<translation id="5205222826937269299">Nome obrigatório</translation>
<translation id="5222812217790122047">E-mail obrigatório</translation>
<translation id="5251803541071282808">Nuvem</translation>
<translation id="5277279256032773186">Você usa o Chrome no trabalho? As empresas podem gerenciar as configurações do Chrome para seus funcionários. Saiba mais</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Siga estas etapas para desativar temporariamente o software e entrar na Web. Para isso, você precisará de privilégios de administrador.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Clique em <ph name="BEGIN_BOLD" />Iniciar<ph name="END_BOLD" /> e selecione <ph name="BEGIN_BOLD" />Ver serviços locais<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Selecione <ph name="BEGIN_BOLD" />Descoberta Visual<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Em <ph name="BEGIN_BOLD" />Tipo de inicialização<ph name="END_BOLD" />, selecione <ph name="BEGIN_BOLD" />Desativado<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Em <ph name="BEGIN_BOLD" />Status do serviço<ph name="END_BOLD" />, clique em <ph name="BEGIN_BOLD" />Parar<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Clique em <ph name="BEGIN_BOLD" />Aplicar<ph name="END_BOLD" /> e depois em <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Visite a <ph name="BEGIN_LEARN_MORE_LINK" />Central de ajuda do Google Chrome<ph name="END_LEARN_MORE_LINK" /> para saber mais sobre como remover software permanentemente do seu computador
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Sua conexão com este site não é particular. Para sair do modo de RV a qualquer momento, remova o fone de ouvido e pressione "Voltar".</translation>
<translation id="5299298092464848405">Política de análise de erros</translation>
<translation id="5308689395849655368">O relatório de erros está desativado.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Erro de validação de esquema em "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Não foi possível encontrar a página deste <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Carimbo de data/hora da política inválido</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> de <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Inválidos</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Refazer editar</translation>
+<translation id="5481076368049295676">Este conteúdo pode tentar instalar softwares perigosos no seu dispositivo que roubam ou excluem suas informações. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Adicionar endereço válido</translation>
<translation id="5492298309214877701">Esse site na intranet da empresa, organização ou escola tem o mesmo URL de um website externo.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Não foi possível encontrar o artigo solicitado.</translation>
<translation id="5544037170328430102">Uma página incorporada em <ph name="SITE" /> diz:</translation>
<translation id="5556459405103347317">Recarregar</translation>
+<translation id="5560088892362098740">Data de validade</translation>
<translation id="5565735124758917034">Ativo</translation>
<translation id="5571083550517324815">Não é possível fazer a retirada nesse endereço. Tente um endereço diferente.</translation>
<translation id="5572851009514199876">Inicie e faça login no Chrome para que ele possa verificar se você tem permissão para acessar este site.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Falha ao carregar as configurações da política</translation>
<translation id="5631439013527180824">Token de gerenciamento de dispositivo inválido</translation>
<translation id="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="5646376287012673985">Local</translation>
<translation id="5659593005791499971">E-mail</translation>
<translation id="5669703222995421982">Receber conteúdo personalizado</translation>
<translation id="5675650730144413517">Esta página não está funcionando</translation>
<translation id="5710435578057952990">A identidade deste site não foi confirmada.</translation>
-<translation id="5713016350996637505">Conteúdo enganoso bloqueado</translation>
+<translation id="5719499550583120431">Cartões pré-pagos são aceitos.</translation>
<translation id="5720705177508910913">Usuário atual</translation>
<translation id="5732392974455271431">Seus responsáveis podem desbloqueá-lo para você</translation>
<translation id="5763042198335101085">Informe um endereço de e-mail válido.</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Deseja preencher as informações do seu cartão <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Sua conexão com <ph name="DOMAIN" /> está criptografada com um pacote de criptografia obsoleto.</translation>
<translation id="5813119285467412249">&amp;Refazer adicionar</translation>
-<translation id="5814352347845180253">É possível que você perca o acesso a conteúdos premier de <ph name="SITE" /> e alguns outros sites.</translation>
<translation id="5838278095973806738">Você não deve fornecer nenhuma informação confidencial nesse site (por exemplo, senhas ou cartões de crédito), porque elas podem ser roubadas por invasores.</translation>
<translation id="5869405914158311789">Não é possível acessar esse site</translation>
<translation id="5869522115854928033">Senhas salvas</translation>
<translation id="5872918882028971132">Sugestões para pais</translation>
+<translation id="5893752035575986141">Cartões de crédito são aceitos.</translation>
<translation id="5901630391730855834">Amarelo</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="5926846154125914413">É possível que você perca o acesso a conteúdos premier de alguns sites.</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="5967867314010545767">Remover do histórico</translation>
<translation id="5975083100439434680">Diminuir zoom</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Fechar</translation>
<translation id="6042308850641462728">Mais</translation>
<translation id="6047233362582046994">Se você conhece os riscos para sua segurança, pode <ph name="BEGIN_LINK" />visitar este site<ph name="END_LINK" /> antes de os apps prejudiciais serem removidos.</translation>
+<translation id="6047927260846328439">Este conteúdo pode tentar enganar você para que instale um software ou revele informações pessoais. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Não é possível acessar <ph name="SITE" /> no momento, porque o site usa bloqueio de certificados. Como os ataques e erros de rede são geralmente temporários, esta pagina provavelmente funcionará mais tarde.</translation>
<translation id="6060685159320643512">Tome cuidado, esses experimentos podem morder</translation>
<translation id="6080696365213338172">Você acessou conteúdo usando um certificado fornecido pelo administrador. Os dados fornecidos a <ph name="DOMAIN" /> podem ser interceptados por seu administrador.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Saiba mais</translation>
<translation id="6169916984152623906">Agora você pode navegar com privacidade. Outras pessoas que usarem este dispositivo não verão sua atividade, mas os downloads e favoritos serão salvos.</translation>
<translation id="6177128806592000436">Sua conexão com esse site não é segura</translation>
-<translation id="6184817833369986695">(coorte: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Verifique sua conexão com a Internet</translation>
<translation id="6218753634732582820">Remover endereço do Chromium?</translation>
<translation id="6221345481584921695">A Navegação segura do Google recentemente <ph name="BEGIN_LINK" />detectou malware<ph name="END_LINK" /> em <ph name="SITE" />. Websites que geralmente são seguros estão, algumas vezes, infectados com malware. O conteúdo malicioso vem de <ph name="SUBRESOURCE_HOST" />, um distribuidor de malware conhecido.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Interessado em novos recursos interessantes do Google Chrome? Veja nosso Canal Beta em chrome.com/beta.</translation>
-<translation id="6389758589412724634">O Chromium ficou sem memória ao tentar exibir essa página da Web.</translation>
+<translation id="6397451950548600259">Algum software no seu computador está impedindo o Google Chrome de se conectar com segurança à Web</translation>
<translation id="6404511346730675251">Editar favorito</translation>
<translation id="6410264514553301377">Digite a data de validade e o CVC do <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Você perguntou ao seu responsável se pode visitar este site</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Políticas de dispositivos</translation>
<translation id="6477321094435799029">O Chrome detectou um código incomum nesta página e a bloqueou para proteger suas informações pessoais (por exemplo, senhas, números de telefone e cartões de crédito).</translation>
<translation id="6489534406876378309">Iniciar upload de falhas</translation>
+<translation id="6507833130742554667">Cartões de crédito e débito são aceitos.</translation>
<translation id="6508722015517270189">Reiniciar o Chrome</translation>
<translation id="6529602333819889595">&amp;Refazer excluir</translation>
<translation id="6534179046333460208">Sugestões da Web física</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Esse conteúdo pode tentar instalar softwares perigosos no seu dispositivo que roubam ou excluem suas informações. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opções de criptografia</translation>
<translation id="662080504995468778">Ficar</translation>
<translation id="6626291197371920147">Adicionar número de cartão de crédito válido</translation>
<translation id="6628463337424475685">Pesquisa do <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Invasores presentes no momento em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar instalar programas perigosos no seu Mac 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="6644283850729428850">Esta política foi encerrada.</translation>
+<translation id="6657585470893396449">Senha</translation>
<translation id="6671697161687535275">Remover sugestão de formulário do Chromium?</translation>
<translation id="6685834062052613830">Saia e conclua a configuração</translation>
<translation id="6710213216561001401">Anterior</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Distrito</translation>
<translation id="6973656660372572881">Ambos os servidores proxy fixo e um URL de script .pac foram especificados.</translation>
<translation id="6989763994942163495">Mostrar configurações avançadas...</translation>
-<translation id="7000990526846637657">Nenhum entrada de histórico encontrada</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Sua Conta do Google pode ter outras formas de histórico de navegação em <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Senhas</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Não é possível enviar para esse endereço. Selecione um endereço diferente.</translation>
<translation id="7138472120740807366">Método de entrega</translation>
<translation id="7139724024395191329">Emirado</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Pagamento não seguro</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Reiniciar agora</translation>
<translation id="7180611975245234373">Atualizar</translation>
<translation id="7182878459783632708">Não há políticas definidas</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Fazer o download</translation>
<translation id="7518003948725431193">Nenhuma página da web foi encontrada para o endereço da Web:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Sua conexão a este site não é particular</translation>
<translation id="7535087603100972091">Valor</translation>
<translation id="7537536606612762813">Obrigatória</translation>
<translation id="7542403920425041731">Depois da confirmação, os detalhes do cartão serão compartilhados com esse site.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Tente seguir estas dicas:</translation>
<translation id="7554791636758816595">Nova guia</translation>
<translation id="7567204685887185387">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança pode ter sido emitido de forma fraudulenta. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Esta página está em<ph name="ORIGINAL_LANGUAGE" />Deseja traduzi-la?</translation>
<translation id="7569952961197462199">Remover cartão de crédito do Chrome?</translation>
<translation id="7569983096843329377">Preto</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificado</translation>
<translation id="7716147886133743102">Bloqueada pelo administrador</translation>
<translation id="7716424297397655342">Não é possível carregar este site a partir do cache</translation>
+<translation id="774634243536837715">Conteúdo perigoso bloqueado.</translation>
<translation id="7752995774971033316">Não gerenciado</translation>
<translation id="7755287808199759310">Seu responsável pode desbloqueá-lo para você</translation>
<translation id="7758069387465995638">É possível que o software antivírus ou o firewall tenha bloqueado a conexão.</translation>
+<translation id="7759163816903619567">Domínio de exibição:</translation>
<translation id="7761701407923456692">O certificado do servidor não é compatível com o URL.</translation>
<translation id="7763386264682878361">Analisador de manifesto de pagamento</translation>
<translation id="7764225426217299476">Adicionar endereço</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Localizados <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> para "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">O modo invisível NÃO oculta seus dados de navegação. Seu empregador, seu provedor de Internet e os websites visitados continuam tendo acesso a essas informações.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Cartões de débito e pré-pagos são aceitos.</translation>
<translation id="7887683347370398519">Verifique seu CVC e tente novamente</translation>
<translation id="79338296614623784">Informe um número de telefone válido</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Enviar comentários</translation>
<translation id="8041940743680923270">Usar padrão global (Perguntar)</translation>
<translation id="8088680233425245692">Falha ao exibir artigo.</translation>
-<translation id="8089520772729574115">menos de 1 MB</translation>
<translation id="8091372947890762290">A ativação está pendente no servidor</translation>
<translation id="8118489163946903409">Método de pagamento</translation>
<translation id="8131740175452115882">Confirmar</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Desfazer mover</translation>
<translation id="8201077131113104583">URL de atualização inválido para extensão com ID "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Resumo do pedido</translation>
+<translation id="8205463626947051446">O site costuma mostrar anúncios invasivos</translation>
<translation id="8218327578424803826">Local designado:</translation>
<translation id="8225771182978767009">A pessoa que configurou este computador optou por bloquear esse site.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Abrir página em uma nova guia anônima</translation>
<translation id="8241707690549784388">A página que você está procurando usou as informações inseridas. Voltar à essa página poderá fazer com que todas as ações realizadas antes sejam repetidas. Deseja continuar?</translation>
+<translation id="8241712895048303527">Bloquear neste site</translation>
<translation id="8249320324621329438">Última busca:</translation>
<translation id="8253091569723639551">Endereço de faturamento necessário</translation>
<translation id="8261506727792406068">Excluir</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">A tradução falhou devido a um problema com a conexão de rede.</translation>
<translation id="8332188693563227489">O acesso a <ph name="HOST_NAME" /> foi negado</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="8344669043927012510">Abrir a página no modo sem rastros (⇧⌘N)</translation>
<translation id="8349305172487531364">Barra de favoritos</translation>
<translation id="8363502534493474904">Desativar modo avião</translation>
<translation id="8364627913115013041">Não definida.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Relatório de erros enviado em <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Falhas (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Você deve inserir a mesma senha duas vezes.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configurações</translation>
<translation id="8433057134996913067">Essa opção desconecta você da maioria dos websites.</translation>
<translation id="8437238597147034694">&amp;Desfazer mover</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Para usar os cards da sua Conta do Google, faça login no Google Chrome</translation>
<translation id="8488350697529856933">Aplicável a</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> demorou muito para responder.</translation>
-<translation id="8532105204136943229">Ano de vencimento</translation>
+<translation id="8503813439785031346">Nome de usuário</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="8571890674111243710">Traduzindo página para <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Ad. nº. telefone
@@ -887,6 +919,7 @@
<translation id="859285277496340001">O certificado não especifica um mecanismo para verificar se ele foi revogado.</translation>
<translation id="8620436878122366504">Seus responsáveis ainda não o aprovaram</translation>
<translation id="8647750283161643317">Redefinir tudo para o padrão</translation>
+<translation id="8660471606262461360">Do Google Payments</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="8725066075913043281">Tentar novamente</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Aumentar zoom</translation>
<translation id="8931333241327730545">Deseja salvar este cartão na sua Conta do Google?</translation>
<translation id="8932102934695377596">Seu relógio está atrasado</translation>
+<translation id="8938939909778640821">Cartões de crédito e pré-pagos aceitos</translation>
<translation id="8971063699422889582">O certificado do servidor expirou.</translation>
<translation id="8986494364107987395">Enviar estatísticas de uso e relatórios de erros ao Google automaticamente</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -944,7 +978,6 @@ incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser
<translation id="9169664750068251925">Sempre bloquear neste site</translation>
<translation id="9170848237812810038">&amp;Desfazer</translation>
<translation id="917450738466192189">O certificado do servidor é inválido.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Falha ao adicionar artigo.</translation>
@@ -953,9 +986,9 @@ incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">LIMPAR FORMULÁRIO</translation>
<translation id="939736085109172342">Nova pasta</translation>
-<translation id="941721044073577244">Parece que você não tem permissão para visitar este site</translation>
<translation id="969892804517981540">Versão oficial</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Nenhum}=1{1 item}one{# item}other{# itens}}</translation>
+<translation id="981121421437150478">Off-line</translation>
<translation id="988159990683914416">Versão do desenvolvedor</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_pt-PT.xtb b/chromium/components/strings/components_strings_pt-PT.xtb
index 020ab866ec2..6ac1725cf40 100644
--- a/chromium/components/strings/components_strings_pt-PT.xtb
+++ b/chromium/components/strings/components_strings_pt-PT.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Fechar outras aplicações</translation>
<translation id="1055184225775184556">&amp;Anular adição</translation>
<translation id="10614374240317010">Nunca guardadas</translation>
+<translation id="1066396345355680611">Pode perder o acesso ao conteúdo protegido de <ph name="SITE" /> e de outros sites.</translation>
<translation id="106701514854093668">Marcadores do Ambiente de Trabalho</translation>
<translation id="1074497978438210769">Inseguro</translation>
<translation id="1080116354587839789">Ajustar à largura</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lista de leitura</translation>
<translation id="1264126396475825575">Relatório de falhas capturado <ph name="CRASH_TIME" /> (ainda não carregado ou ignorado)</translation>
<translation id="1281526147609854549">Emitido por <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Conteúdo perigoso bloqueado</translation>
<translation id="1285320974508926690">Nunca traduzir este site</translation>
<translation id="129553762522093515">Fechados recentemente</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Experimente limpar os cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domínio de inscrição:</translation>
<translation id="1340482604681802745">Endereço de recolha</translation>
-<translation id="1344211575059133124">Parece que precisas de autorização para visitar este site</translation>
<translation id="1344588688991793829">Definições de preenchimento automático do Chromium…</translation>
<translation id="1348198688976932919">O site que pretende visitar contém aplicações perigosas</translation>
<translation id="1374468813861204354">sugestões</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">A identidade de <ph name="ORGANIZATION" /> em <ph name="LOCALITY" /> foi verificada por <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Sim</translation>
<translation id="1430915738399379752">Imprimir</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Mostrar uma cópia guardada desta página (isto é, uma cópia desatualizada).</translation>
<translation id="1517433312004943670">Número de telefone obrigatório</translation>
+<translation id="1517500485252541695">Cartões de crédito e de débito admitidos</translation>
<translation id="1519264250979466059">Data da Compilação</translation>
+<translation id="1527263332363067270">A aguardar ligação…</translation>
<translation id="153384715582417236">É tudo por agora</translation>
<translation id="1549470594296187301">É necessário ativar o JavaScript para utilizar esta funcionalidade.</translation>
<translation id="1555130319947370107">Azul</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Experimente contactar o administrador do sistema.</translation>
<translation id="1740951997222943430">Introduza um mês de expiração válido</translation>
-<translation id="1745358365027406341">Transferir página mais tarde</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Campo obrigatório</translation>
<translation id="187918866476621466">Abrir páginas iniciais</translation>
<translation id="1883255238294161206">Fechar lista</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtragem</translation>
+<translation id="1916770123977586577">Para aplicar as definições atualizadas a este site, atualize esta página</translation>
+<translation id="1919345977826869612">Anúncios</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Nenhum}=1{1 site}one{# sites}other{# sites}}</translation>
<translation id="194030505837763158">Aceder a <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Cartões pré-pagos admitidos</translation>
<translation id="1962204205936693436">Marcadores de <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Erro de serialização</translation>
<translation id="1974060860693918893">Avançadas</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Erro HTTP</translation>
<translation id="2270484714375784793">Número de telefone</translation>
<translation id="2282872951544483773">Experiências Indisponíveis</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item}one{<ph name="ITEM_COUNT" /> itens}other{<ph name="ITEM_COUNT" /> itens}}</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>
<translation id="2337852623177822836">Definição controlada pelo administrador</translation>
<translation id="2354001756790975382">Outros marcadores</translation>
<translation id="2354430244986887761">A Navegação segura do Google <ph name="BEGIN_LINK" />encontrou aplicações prejudiciais<ph name="END_LINK" /> recentemente em <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continuar</translation>
<translation id="2365563543831475020">O relatório de falhas capturado <ph name="CRASH_TIME" /> não foi carregado</translation>
<translation id="2367567093518048410">Nível</translation>
-<translation id="237718015863234333">Nenhuma alternativa da interface disponível</translation>
<translation id="2384307209577226199">Predefinição empresarial</translation>
<translation id="2386255080630008482">O certificado do servidor foi revogado.</translation>
<translation id="2392959068659972793">Apresentar políticas sem valor definido</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Se marcada, o Chromium armazena uma cópia do seu cartão neste dispositivo para preencher formulários mais rapidamente.</translation>
<translation id="2498091847651709837">Digitalizar novo cartão</translation>
<translation id="2501278716633472235">Retroceder</translation>
+<translation id="2503184589641749290">Cartões de débito e pré-pagos admitidos</translation>
<translation id="2515629240566999685">Verificar o sinal na área</translation>
-<translation id="2516305470678292029">Alternativas da interface</translation>
<translation id="2539524384386349900">Detetar</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> enviou uma resposta inválida.</translation>
<translation id="2556876185419854533">&amp;Anular edição</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Método de envio</translation>
<translation id="277499241957683684">Registo do dispositivo em falta</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>
<translation id="2799020568854403057">O site que pretende visitar contém aplicações prejudiciais</translation>
<translation id="2803306138276472711">A Navegação Segura do Google <ph name="BEGIN_LINK" />detetou software malicioso<ph name="END_LINK" /> recentemente em <ph name="SITE" />. Os Sites que normalmente são seguros por vezes são infetados com software malicioso.</translation>
<translation id="2824775600643448204">Barra de pesquisa e endereço</translation>
<translation id="2826760142808435982">A ligação é encriptada e autenticada com <ph name="CIPHER" /> e utiliza <ph name="KX" /> como mecanismo de troca de chaves.</translation>
<translation id="2835170189407361413">Limpar formulário</translation>
+<translation id="2851634818064021665">Necessita de autorização para aceder a este site</translation>
<translation id="2856444702002559011">Os atacantes poderão estar a tentar roubar as suas informações de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (por exemplo, palavras-passe, mensagens ou cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Não atualizar</translation>
-<translation id="2900469785430194048">O Google Chrome ficou sem memória ao tentar apresentar esta página Web.</translation>
<translation id="2909946352844186028">Foi detetada uma alteração de rede.</translation>
<translation id="2916038427272391327">Fechar outros programas</translation>
<translation id="2922350208395188000">Não é possível verificar o certificado do servidor.</translation>
<translation id="2928905813689894207">Endereço de faturação</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Este servidor não conseguiu provar que é o domínio <ph name="DOMAIN" />; o respetivo certificado de segurança é do domínio <ph name="DOMAIN2" />. Isto pode ser o resultado de uma configuração incorreta ou de um invasor a intercetar a sua ligação.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Se ativada, o Chromium armazena uma cópia do seu cartão neste dispositivo para preencher formulários mais rapidamente.</translation>
<translation id="2985398929374701810">Introduza um endereço válido</translation>
<translation id="2986368408720340940">Este método de recolha não está disponível. Experimente um método diferente.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">As páginas que visualizar em separadores de navegação anónima não são memorizadas no histórico do navegador, no armazenamento de cookies ou no histórico de pesquisas depois de fechar todos os separadores de navegação anónima. Todos os ficheiros transferidos ou os marcadores criados são guardados.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Ilha</translation>
-<translation id="317583078218509884">As novas definições relativas às permissões do Website terão efeito depois de atualizar a página.</translation>
<translation id="3176929007561373547">Verifique as definições de proxy ou contacte o administrador de rede para
se certificar de que o servidor proxy está a funcionar. Se achar que não deve
utilizar um servidor proxy:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Abrir página no modo de navegação anónima</translation>
<translation id="320323717674993345">Cancelar pagamento</translation>
<translation id="3207960819495026254">Adicionado aos marcadores</translation>
<translation id="3225919329040284222">O servidor apresentou um certificado que não corresponde às expectativas existentes. Estas expectativas são incluídas para determinados Web sites de alta segurança para sua proteção.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Anular reordenação</translation>
<translation id="3282497668470633863">Adicionar nome no cartão</translation>
<translation id="3286538390144397061">Reiniciar agora</translation>
+<translation id="3287510313208355388">Transferir quando estiver online</translation>
<translation id="3303855915957856445">Não foram encontrados resultados da pesquisa</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Intervalo de obtenção:</translation>
<translation id="3462200631372590220">Ocultar avançadas</translation>
<translation id="3467763166455606212">Nome do titular do cartão obrigatório</translation>
-<translation id="3478058380795961209">Mês de validade</translation>
<translation id="3479539252931486093">Esta ação foi inesperada? <ph name="BEGIN_LINK" />Informe-nos<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Agora não</translation>
<translation id="3498215018399854026">Não conseguimos falar com o(a) seu (sua) pai/mãe de momento. Tente novamente.</translation>
@@ -343,6 +345,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Os cartões de crédito e pré-pagos são admitidos.</translation>
<translation id="3582930987043644930">Adicionar nome</translation>
<translation id="3583757800736429874">&amp;Refazer movimentação</translation>
<translation id="3586931643579894722">Ocultar detalhes</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Se vê isto com frequência, experimente <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Revisão</translation>
<translation id="3678029195006412963">Não foi possível assinar o pedido</translation>
+<translation id="3678529606614285348">Abrir a página numa nova janela de navegação anónima (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Relatório de falhas capturado no(a) <ph name="CRASH_TIME" /> e carregado no(a) <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informações do certificado</translation>
<translation id="3690164694835360974">Início de sessão não seguro</translation>
-<translation id="3693415264595406141">Palavra-passe:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">A carregar...</translation>
<translation id="3712624925041724820">Licenças esgotadas</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Os atacantes em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem levá-lo a tomar medidas perigosas, como instalar software ou revelar as suas informações pessoais (por exemplo, palavras-passe, números de telefone ou cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">A tradução falhou devido a um erro do servidor.</translation>
<translation id="3759461132968374835">Não tem comunicado falhas recentemente. As falhas que tenham ocorrido enquanto a criação de relatórios de falha esteve desativada não surgem aqui.</translation>
+<translation id="3765032636089507299">A página de Navegação segura está em construção.</translation>
<translation id="3778403066972421603">Pretende guardar este cartão na sua Conta Google e neste dispositivo?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Expira a <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">O seu pedido para aceder a este site foi enviado para <ph name="NAME" />.</translation>
<translation id="3890664840433101773">Adicionar email</translation>
<translation id="3901925938762663762">O cartão expirou</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID do relatório de falhas carregado <ph name="CRASH_ID" /> (ID de falha de sistema local: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O respetivo certificado de segurança não especifica Nomes alternativos do requerente. Isto pode ser o resultado de uma configuração incorreta ou de um utilizador mal-intencionado que intercetou a sua ligação.</translation>
+<translation id="3949601375789751990">O histórico de navegação é apresentado aqui</translation>
<translation id="3963721102035795474">Modo de leitor</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Nenhum}=1{De 1 site }one{De # sites }other{De # sites }}</translation>
<translation id="397105322502079400">A calcular...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Definições de sites</translation>
<translation id="4169947484918424451">Pretende que o Chromium guarde este cartão?</translation>
<translation id="4171400957073367226">Assinatura de verificação incorreta</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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Os utilizadores mal intencionados neste site podem tentar instalar programas perigosos no seu computador que roubam ou eliminam as suas informações (por exemplo, fotos, palavras-passe, mensagens e cartões de crédito).</translation>
<translation id="4372948949327679948">Valor <ph name="VALUE_TYPE" /> esperado.</translation>
<translation id="4377125064752653719">Tentou aceder a <ph name="DOMAIN" />, mas o certificado que o servidor apresentou foi revogado pelo emissor. Isto significa que as credenciais de segurança apresentadas pelo servidor não deverão, em circunstância alguma, ser consideradas fidedignas. Pode estar a comunicar com um utilizador mal intencionado.</translation>
-<translation id="4381091992796011497">Nome do utilizador:</translation>
<translation id="4394049700291259645">Desactivar</translation>
<translation id="4406896451731180161">resultados da pesquisa</translation>
<translation id="4424024547088906515">Este servidor não conseguiu provar que é o domínio <ph name="DOMAIN" />; o Chrome 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="4432688616882109544"><ph name="HOST_NAME" /> não aceitou o seu certificado de início de sessão ou este pode não ter sido fornecido.</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="4506176782989081258">Erro de validação: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Contactar o administrador do sistema</translation>
<translation id="450710068430902550">Partilha com o administrador</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Pretende remover o endereço do Chrome?</translation>
<translation id="4592951414987517459">A sua ligação a <ph name="DOMAIN" /> está encriptada através de um conjunto de cifras moderno.</translation>
<translation id="4594403342090139922">&amp;Anular eliminação</translation>
+<translation id="4611292653554630842">Iniciar sessão</translation>
<translation id="4619615317237390068">Separadores de outros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Este servidor não conseguiu provar que é o domínio <ph name="DOMAIN" />; o respetivo certificado de segurança contém erros. Isto pode ser o resultado de uma configuração incorreta ou de um invasor a intercetar a sua ligação.</translation>
<translation id="4690462567478992370">Parar de utilizar um certificado inválido</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">A ligação foi interrompida</translation>
+<translation id="471880041731876836">Não tem autorização para aceder a este site</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Executar o Diagnóstico de rede do Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Recarregar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Este conteúdo pode tentar enganá-lo de forma a que instale software ou revele informações pessoais. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Pesquisar histórico</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">É necessária autenticação</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{e mais 1 página Web}one{and # more web pages}other{e mais # páginas Web}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Esta página foi traduzida de um idioma desconhecido para <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Pagamento</translation>
<translation id="4926049483395192435">Tem de ser especificado.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Ações</translation>
<translation id="4958444002117714549">Expandir lista</translation>
<translation id="4974590756084640048">Reativar avisos</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Palavra-passe incorrecta</translation>
<translation id="5056549851600133418">Artigos para si</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Verificar o endereço proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Pode perder o acesso ao conteúdo protegido de alguns sites.</translation>
<translation id="5087286274860437796">De momento, o certificado do servidor não é válido.</translation>
<translation id="5087580092889165836">Adicionar cartão</translation>
<translation id="5089810972385038852">Estado</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Província</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5141240743006678641">Encriptar palavras-passe sincronizadas com as credenciais Google</translation>
-<translation id="514421653919133810">Abrir página no modo de navegação anónima (Ctrl+Shift+N)</translation>
<translation id="5145883236150621069">Código de erro presente na resposta da política</translation>
+<translation id="5159010409087891077">Abrir a página numa nova janela de navegação anónima (⇧⌘N)</translation>
<translation id="5171045022955879922">Pesquisar ou escrever URL</translation>
<translation id="5172758083709347301">Equipamento</translation>
<translation id="5179510805599951267">Não está em <ph name="ORIGINAL_LANGUAGE" />? Comunicar este erro</translation>
-<translation id="5181140330217080051">A transferir</translation>
<translation id="5190835502935405962">Barra de marcadores</translation>
<translation id="5199729219167945352">Experiências</translation>
<translation id="5205222826937269299">Nome obrigatório</translation>
<translation id="5222812217790122047">Email obrigatório</translation>
<translation id="5251803541071282808">Nuvem</translation>
<translation id="5277279256032773186">Utiliza o Chrome no trabalho? As empresas podem gerir as definições do Chrome para os seus funcionários. Saiba mais</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Siga estes passos para desativar temporariamente o software para que possa aceder à Web. Precisa de privilégios de administrador.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Clique em <ph name="BEGIN_BOLD" />Iniciar<ph name="END_BOLD" /> e, em seguida, pesquise e selecione <ph name="BEGIN_BOLD" />"Ver serviços locais"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Selecione <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Em <ph name="BEGIN_BOLD" />Tipo de arranque<ph name="END_BOLD" />, selecione <ph name="BEGIN_BOLD" />Desativado<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Em <ph name="BEGIN_BOLD" />Estado do serviço<ph name="END_BOLD" />, clique em <ph name="BEGIN_BOLD" />Parar<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Clique em <ph name="BEGIN_BOLD" />Aplicar<ph name="END_BOLD" /> e, em seguida, clique em <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Visite o <ph name="BEGIN_LEARN_MORE_LINK" />Centro de Ajuda do Chrome<ph name="END_LEARN_MORE_LINK" /> para saber como remover permanentemente o software do computador
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">A sua ligação a este site não é privada. Para sair do modo de RV a qualquer momento, remova os auscultadores com microfone integrado e prima Anterior.</translation>
<translation id="5299298092464848405">Erro ao analisar a política</translation>
<translation id="5308689395849655368">O relatório de falha está desativado.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Erro de validação de esquema em "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Não é possível encontrar esta página de <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Carimbo de data/hora da política incorreto</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> de <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Inválido</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Refazer edição</translation>
+<translation id="5481076368049295676">Este conteúdo pode tentar instalar software perigoso no dispositivo que rouba ou elimina as suas informações. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Adicionar endereço válido</translation>
<translation id="5492298309214877701">Este site na intranet da empresa, da entidade ou da escola tem o mesmo URL que um Website externo.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Não foi possível encontrar o artigo solicitado.</translation>
<translation id="5544037170328430102">Uma página incorporada em <ph name="SITE" /> diz:</translation>
<translation id="5556459405103347317">Recarregar</translation>
+<translation id="5560088892362098740">Data de validade</translation>
<translation id="5565735124758917034">Ativo</translation>
<translation id="5571083550517324815">Não é possível recolher a partir deste endereço. Selecione um diferente.</translation>
<translation id="5572851009514199876">Comece e inicie sessão no Chrome para que este possa verificar se tem autorização para aceder a este site.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Falha ao carregar as definições da política</translation>
<translation id="5631439013527180824">Token de gestão do dispositivo inválido</translation>
<translation id="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="5646376287012673985">Localização</translation>
<translation id="5659593005791499971">Email</translation>
<translation id="5669703222995421982">Obter conteúdo personalizado</translation>
<translation id="5675650730144413517">Esta página não está a funcionar</translation>
<translation id="5710435578057952990">A identidade deste Web site não foi verificada.</translation>
-<translation id="5713016350996637505">Conteúdo fraudulento bloqueado</translation>
+<translation id="5719499550583120431">Os cartões pré-pagos são admitidos.</translation>
<translation id="5720705177508910913">Utilizador atual</translation>
<translation id="5732392974455271431">Os teus pais podem desbloquear-te</translation>
<translation id="5763042198335101085">Introduza um endereço de email válido</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Pretende preencher o seu cartão <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">A sua ligação a <ph name="DOMAIN" /> está encriptada através de um conjunto de cifras obsoleto.</translation>
<translation id="5813119285467412249">&amp;Refazer adição</translation>
-<translation id="5814352347845180253">Pode perder o acesso ao conteúdo premium de <ph name="SITE" /> e de outros sites.</translation>
<translation id="5838278095973806738">Não deve introduzir informações confidenciais neste site (por exemplo, palavras-passe ou números de cartões de crédito), porque podem ser roubadas por atacantes.</translation>
<translation id="5869405914158311789">Não é possível aceder a este site</translation>
<translation id="5869522115854928033">Palavras-passe guardadas</translation>
<translation id="5872918882028971132">Sugestões superiores</translation>
+<translation id="5893752035575986141">Os cartões de crédito são admitidos.</translation>
<translation id="5901630391730855834">Amarelo</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sincronizados)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 em utilização}one{# in use}other{# em utilização}}</translation>
-<translation id="5926846154125914413">Pode perder o acesso ao conteúdo premium de alguns sites.</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="5967867314010545767">Remover do histórico</translation>
<translation id="5975083100439434680">Reduzir</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Fechar</translation>
<translation id="6042308850641462728">Mais</translation>
<translation id="6047233362582046994">Se compreende os riscos para a sua segurança, pode <ph name="BEGIN_LINK" />visitar este site<ph name="END_LINK" /> antes de as aplicações prejudiciais terem sido removidas.</translation>
+<translation id="6047927260846328439">Este conteúdo pode tentar enganá-lo de forma a que instale software ou revele informações pessoais. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Não pode visitar <ph name="SITE" /> neste momento, porque o Website utiliza a afixação de certificados. Os erros de rede e os ataques são geralmente temporários, pelo que esta página deverá funcionar mais tarde.</translation>
<translation id="6060685159320643512">Tenha cuidado, estas experiências podem morder</translation>
<translation id="6080696365213338172">Acedeu a conteúdos utilizando um certificado fornecido por um administrador. Os dados que fornecer a <ph name="DOMAIN" /> podem ser intercetados pelo seu administrador.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Saiba mais</translation>
<translation id="6169916984152623906">Agora, pode navegar em privado e as outras pessoas que utilizarem este dispositivo não veem a sua atividade. No entanto, as transferências e os marcadores são guardados.</translation>
<translation id="6177128806592000436">A sua ligação a este site não é segura</translation>
-<translation id="6184817833369986695">(grupo com caraterísticas em comum: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Verificar a ligação à Internet</translation>
<translation id="6218753634732582820">Pretende remover o endereço do Chromium?</translation>
<translation id="6221345481584921695">A Navegação Segura do Google <ph name="BEGIN_LINK" />detetou software malicioso<ph name="END_LINK" /> recentemente em <ph name="SITE" />. Os Sites que normalmente são seguros por vezes são infetados com software malicioso. O conteúdo malicioso provém de <ph name="SUBRESOURCE_HOST" />, um distribuidor de software malicioso conhecido.</translation>
@@ -641,13 +664,14 @@
<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}one{# other suggestions}other{# outras sugestões}}</translation>
<translation id="6387478394221739770">Está interessado nas novas e fantásticas funcionalidades do Chrome? Experimente o nosso canal beta em chrome.com/beta.</translation>
-<translation id="6389758589412724634">O Chromium ficou sem memória ao tentar apresentar esta página Web.</translation>
+<translation id="6397451950548600259">Existe software no computador que está a impedir que o Chrome se ligue à Web em segurança</translation>
<translation id="6404511346730675251">Editar marcador</translation>
<translation id="6410264514553301377">Introduza a data de validade e o Código de Segurança/CVC de <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Perguntaste ao teu pai/à tua mãe se podes aceder a este site.</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Políticas do dispositivo</translation>
<translation id="6477321094435799029">O Chrome detetou código estranho nesta página e bloqueou-a para proteger as suas informações pessoais (por exemplo, palavras-passe, números de telefone e números de cartões de crédito).</translation>
<translation id="6489534406876378309">Começar a carregar falhas</translation>
+<translation id="6507833130742554667">Os cartões de crédito e de débito são admitidos.</translation>
<translation id="6508722015517270189">Reiniciar o Chrome</translation>
<translation id="6529602333819889595">&amp;Refazer eliminação</translation>
<translation id="6534179046333460208">Sugestões da Web física</translation>
@@ -670,13 +695,13 @@
<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 administrador ainda não o aprovou</translation>
<translation id="6569060085658103619">Está a ver a página de uma extensão</translation>
-<translation id="657639383826808334">Este conteúdo pode tentar instalar software perigoso no dispositivo que rouba ou elimina as suas informações. <ph name="BEGIN_LINK" />Mostrar mesmo assim<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opções de encriptação</translation>
<translation id="662080504995468778">Ficar</translation>
<translation id="6626291197371920147">Adicionar número de cartão válido</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Pesquisar</translation>
<translation id="6630809736994426279">Os atacantes atualmente em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar instalar programas perigosos no seu Mac que roubam ou eliminam 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="6644283850729428850">Esta política está obsoleta.</translation>
+<translation id="6657585470893396449">Palavra-passe</translation>
<translation id="6671697161687535275">Pretende remover a sugestão do formulário do Chromium?</translation>
<translation id="6685834062052613830">Termine sessão e conclua a configuração</translation>
<translation id="6710213216561001401">Anterior</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Distrito</translation>
<translation id="6973656660372572881">Foram especificados servidores proxy fixos e um URL de script .pac.</translation>
<translation id="6989763994942163495">Mostrar definições avançadas...</translation>
-<translation id="7000990526846637657">Não foram encontradas entradas no histórico</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">A sua Conta Google pode ter outras formas do histórico de navegação em <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Palavras-passe</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Não é possível enviar para este endereço. Selecione um diferente.</translation>
<translation id="7138472120740807366">Método de fornecimento</translation>
<translation id="7139724024395191329">Emirado</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Pagamento não seguro</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Reiniciar agora</translation>
<translation id="7180611975245234373">Atualizar</translation>
<translation id="7182878459783632708">Não estão definidas políticas</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Transferir</translation>
<translation id="7518003948725431193">Não foi encontrada qualquer página Web para o endereço Web: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">A sua ligação a este site não é privada.</translation>
<translation id="7535087603100972091">Valor</translation>
<translation id="7537536606612762813">Obrigatório</translation>
<translation id="7542403920425041731">Ao confirmar, os detalhes do cartão são partilhados com este site.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Experimente as sugestões seguintes:</translation>
<translation id="7554791636758816595">Novo separador</translation>
<translation id="7567204685887185387">Este servidor não conseguiu provar que é o domínio <ph name="DOMAIN" />; o respetivo certificado de segurança poderá ter sido emitido de forma fraudulenta. Isto pode ser o resultado de uma configuração incorreta ou de um invasor a intercetar a sua ligação.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Esta página está em<ph name="ORIGINAL_LANGUAGE" />Pretende traduzi-la?</translation>
<translation id="7569952961197462199">Pretende remover o cartão de crédito do Chrome?</translation>
<translation id="7569983096843329377">Preto</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificado</translation>
<translation id="7716147886133743102">Bloqueada pelo administrador</translation>
<translation id="7716424297397655342">Não é possível carregar este site a partir da cache</translation>
+<translation id="774634243536837715">Conteúdo perigoso bloqueado.</translation>
<translation id="7752995774971033316">Não gerido</translation>
<translation id="7755287808199759310">O teu pai/a tua mãe pode desbloquear-te</translation>
<translation id="7758069387465995638">O software de firewall ou antivírus pode ter bloqueado a ligação.</translation>
+<translation id="7759163816903619567">Domínio de apresentação:</translation>
<translation id="7761701407923456692">O certificado do servidor não corresponde ao URL.</translation>
<translation id="7763386264682878361">Analisador de manifestos de pagamento</translation>
<translation id="7764225426217299476">Adicionar endereço</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Encontrados <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> para "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">No entanto, a navegação não é invisível. Passar para o modo de navegação anónima não oculta a navegação do empregador ou do fornecedor de serviços de Internet, nem dos Sites que visitar.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Os cartões de débito e pré-pagos são admitidos.</translation>
<translation id="7887683347370398519">Verifique o Código de Segurança/CVC e tente novamente</translation>
<translation id="79338296614623784">Introduza um número de telefone válido</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Enviar comentários</translation>
<translation id="8041940743680923270">Utilizar predefinição global (Perguntar)</translation>
<translation id="8088680233425245692">Falha ao ver o artigo.</translation>
-<translation id="8089520772729574115">menos de 1 MB</translation>
<translation id="8091372947890762290">Ativação pendente no servidor</translation>
<translation id="8118489163946903409">Método de pagamento</translation>
<translation id="8131740175452115882">Confirmar</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Anular movimentação</translation>
<translation id="8201077131113104583">Atualizar URL inválido para a extensão com o ID "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Resumo da encomenda</translation>
+<translation id="8205463626947051446">O site tem tendência para mostrar anúncios intrusivos</translation>
<translation id="8218327578424803826">Localização atribuída:</translation>
<translation id="8225771182978767009">A pessoa que configurou este computador optou por bloquear este site.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Abrir página num novo separador anónimo</translation>
<translation id="8241707690549784388">A página que procura utilizou informações introduzidas por si. Regressar a essa página poderá originar a repetição de qualquer acção que tenha efetuado. Pretende continuar?</translation>
+<translation id="8241712895048303527">Bloquear neste site</translation>
<translation id="8249320324621329438">Última obtenção:</translation>
<translation id="8253091569723639551">É necessário um endereço de faturação</translation>
<translation id="8261506727792406068">Eliminar</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">A tradução falhou devido a um problema com a ligação de rede.</translation>
<translation id="8332188693563227489">O acesso a <ph name="HOST_NAME" /> foi recusado</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="8344669043927012510">Abrir página no modo de navegação anónima (⇧⌘N)</translation>
<translation id="8349305172487531364">Barra de marcadores</translation>
<translation id="8363502534493474904">Desativar o modo de avião</translation>
<translation id="8364627913115013041">Não definida.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Relatório de falhas carregado no(a) <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Falhas (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Tem de introduzir a mesma frase de acesso duas vezes.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Definições</translation>
<translation id="8433057134996913067">Esta opção termina a sessão na maioria dos Sites.</translation>
<translation id="8437238597147034694">&amp;Anular movimentação</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Para utilizar cartões da sua Conta Google, inicie sessão no Chrome.</translation>
<translation id="8488350697529856933">Aplica-se a</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> demorou demasiado tempo a responder.</translation>
-<translation id="8532105204136943229">Ano de validade</translation>
+<translation id="8503813439785031346">Nome de utilizador</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="8571890674111243710">A traduzir a página para <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Adic. n.º telef.
@@ -887,6 +919,7 @@
<translation id="859285277496340001">O certificado não indica um mecanismo para verificar se foi ou não revogado.</translation>
<translation id="8620436878122366504">Os teus pais ainda não o aprovaram</translation>
<translation id="8647750283161643317">Repor todas as predefinições</translation>
+<translation id="8660471606262461360">Do Google Payments</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="8725066075913043281">Tentar novamente</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Ampliar</translation>
<translation id="8931333241327730545">Pretende guardar este cartão na sua Conta Google?</translation>
<translation id="8932102934695377596">O seu relógio está atrasado</translation>
+<translation id="8938939909778640821">Cartões de crédito e pré-pagos admitidos</translation>
<translation id="8971063699422889582">O certificado do servidor expirou.</translation>
<translation id="8986494364107987395">Enviar automaticamente estatísticas de utilização e relatórios de falhas para a Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Bloquear sempre neste Website</translation>
<translation id="9170848237812810038">An&amp;ular</translation>
<translation id="917450738466192189">O certificado do servidor é inválido.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> e mais <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Falha ao adicionar o artigo.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">LIMPAR FORMULÁRIO</translation>
<translation id="939736085109172342">Nova pasta</translation>
-<translation id="941721044073577244">Parece que não tem autorização para visitar este site</translation>
<translation id="969892804517981540">Compilação oficial</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Nenhum}=1{1 item}one{# itens}other{# itens}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Compilação de programador</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ro.xtb b/chromium/components/strings/components_strings_ro.xtb
index e7d067ecd8c..af27b788855 100644
--- a/chromium/components/strings/components_strings_ro.xtb
+++ b/chromium/components/strings/components_strings_ro.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">închide celelalte aplicații;</translation>
<translation id="1055184225775184556">&amp;Anulați adăugarea</translation>
<translation id="10614374240317010">Nu se salvează niciodată</translation>
+<translation id="1066396345355680611">Este posibil să pierzi accesul la conținutul protejat de pe <ph name="SITE" /> și de pe alte site-uri.</translation>
<translation id="106701514854093668">Marcaje desktop</translation>
<translation id="1074497978438210769">Nesecurizat</translation>
<translation id="1080116354587839789">Încadrează pe lățime</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Lista de lectură</translation>
<translation id="1264126396475825575">Raport de blocare creat <ph name="CRASH_TIME" /> (nu a fost încă încărcat sau ignorat)</translation>
<translation id="1281526147609854549">Emis de <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Conținutul periculos a fost blocat</translation>
<translation id="1285320974508926690">Nu traduce niciodată acest site</translation>
<translation id="129553762522093515">Închise recent</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Șterge cookie-urile<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domeniu de înregistrare:</translation>
<translation id="1340482604681802745">Adresa de preluare</translation>
-<translation id="1344211575059133124">Se pare că ai nevoie de permisiune ca să accesezi acest site</translation>
<translation id="1344588688991793829">Setări de completare automată în Chromium...</translation>
<translation id="1348198688976932919">Site-ul pe care urmează să îl accesezi conține aplicații periculoase</translation>
<translation id="1374468813861204354">sugestii</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identitatea <ph name="ORGANIZATION" /> din <ph name="LOCALITY" /> a fost verificată de către <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Da</translation>
<translation id="1430915738399379752">Printează</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Afișează o copie salvată (adică despre care se știe că este învechită) a acestei pagini.</translation>
<translation id="1517433312004943670">Numărul de telefon este obligatoriu</translation>
+<translation id="1517500485252541695">Carduri de credit și de debit acceptate</translation>
<translation id="1519264250979466059">Dată versiune:</translation>
+<translation id="1527263332363067270">Se așteaptă conectarea…</translation>
<translation id="153384715582417236">Asta este tot</translation>
<translation id="1549470594296187301">Trebuie să activezi JavaScript pentru a folosi această funcție.</translation>
<translation id="1555130319947370107">Albastru</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Descarcă pagina mai târziu</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Câmp obligatoriu</translation>
<translation id="187918866476621466">Deschide paginile de pornire</translation>
<translation id="1883255238294161206">Restrângeți lista</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtrarea</translation>
+<translation id="1916770123977586577">Ca să aplici setările actualizate pentru acest site, reîncarcă pagina</translation>
+<translation id="1919345977826869612">Anunțuri</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Niciunul}=1{1 site}few{# site-uri}other{# de site-uri}}</translation>
<translation id="194030505837763158">Accesați <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Carduri preplătite acceptate</translation>
<translation id="1962204205936693436">Marcaje <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Eroare de serializare</translation>
<translation id="1974060860693918893">Avansate</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Eroare HTTP</translation>
<translation id="2270484714375784793">Număr telefon</translation>
<translation id="2282872951544483773">Experimente indisponibile</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element}few{<ph name="ITEM_COUNT" /> elemente}other{<ph name="ITEM_COUNT" /> de elemente}}</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>
<translation id="2337852623177822836">Setare controlată de administrator</translation>
<translation id="2354001756790975382">Alte marcaje</translation>
<translation id="2354430244986887761">Navigarea sigură Google <ph name="BEGIN_LINK" />a descoperit recent aplicații dăunătoare<ph name="END_LINK" /> pe <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Continuă</translation>
<translation id="2365563543831475020">Raportul de blocare creat <ph name="CRASH_TIME" /> nu a fost încărcat</translation>
<translation id="2367567093518048410">Nivel</translation>
-<translation id="237718015863234333">Nu sunt disponibile interfețe de utilizare alternative</translation>
<translation id="2384307209577226199">Setare prestabilită la nivel de companie</translation>
<translation id="2386255080630008482">Certificatul serverului a fost revocat.</translation>
<translation id="2392959068659972793">Afișați politicile care nu au valori setate</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Dacă opțiunea este bifată, Chromium va stoca o copie a cardului pe dispozitiv pentru a completa formularul mai rapid.</translation>
<translation id="2498091847651709837">Scanează un card nou</translation>
<translation id="2501278716633472235">Înapoi</translation>
+<translation id="2503184589641749290">Carduri de debit și preplătite acceptate</translation>
<translation id="2515629240566999685">să verifici semnalul din zona ta;</translation>
-<translation id="2516305470678292029">Interfețe de utilizare alternative</translation>
<translation id="2539524384386349900">Detectează</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> a trimis un răspuns nevalid.</translation>
<translation id="2556876185419854533">&amp;Anulați editarea</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Metodă de expediere</translation>
<translation id="277499241957683684">Lipsește o înregistrare pentru gadget</translation>
<translation id="2784949926578158345">Conexiunea a fost resetată.</translation>
+<translation id="2788784517760473862">Carduri de credit acceptate</translation>
<translation id="2794233252405721443">Site blocat</translation>
<translation id="2799020568854403057">Site-ul pe care urmează să îl accesezi conține aplicații dăunătoare</translation>
<translation id="2803306138276472711">Navigarea sigură Google <ph name="BEGIN_LINK" />a detectat recent programe malware<ph name="END_LINK" /> pe <ph name="SITE" />. Site-urile care sunt de obicei sigure sunt uneori infectate cu programe malware.</translation>
<translation id="2824775600643448204">Bara de adrese și de căutare</translation>
<translation id="2826760142808435982">Conexiunea este criptată și autentificată utilizând <ph name="CIPHER" /> și folosește <ph name="KX" /> ca mecanism de schimb al cheii.</translation>
<translation id="2835170189407361413">Golește formularul</translation>
+<translation id="2851634818064021665">Ai nevoie de permisiune ca să accesezi acest site</translation>
<translation id="2856444702002559011">Atacatorii pot încerca să îți fure informațiile de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (de exemplu, parole, mesaje sau date despre cardurile de credit). <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Nu reîncărca</translation>
-<translation id="2900469785430194048">Google Chrome nu a avut suficientă memorie la afișarea paginii web.</translation>
<translation id="2909946352844186028">A fost detectată o schimbare a rețelei.</translation>
<translation id="2916038427272391327">închide celelalte programe;</translation>
<translation id="2922350208395188000">Certificatul serverului nu poate fi verificat.</translation>
<translation id="2928905813689894207">Adresă de facturare</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; certificatul său de securitate provine de la <ph name="DOMAIN2" />. Cauza poate fi o configurare greșită sau interceptarea conexiunii de către un atacator.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Dacă opțiunea este activată, Chromium va stoca o copie a cardului pe dispozitiv pentru a completa formularul mai rapid.</translation>
<translation id="2985398929374701810">Introdu o adresă validă</translation>
<translation id="2986368408720340940">Această metodă de preluare nu este disponibilă. Încearcă altă metodă.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Paginile pe care le accesezi în filele incognito nu vor fi înregistrate în istoricul browserului, nu vor stoca cookie-uri și nu vor rămâne în istoricul de căutare după ce închizi toate filele incognito. Fișierele descărcate și marcajele create vor fi păstrate.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Insulă</translation>
-<translation id="317583078218509884">Setările noi pentru permisiuni legate de site-uri se vor aplica după ce reîncărcați pagina.</translation>
<translation id="3176929007561373547">Verifică setările de proxy sau contactează administratorul de rețea
pentru a te asigura că serverul proxy funcționează. Dacă nu consideri că ar trebui
să folosești un server proxy:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">deschide pagina în modul incognito;</translation>
<translation id="320323717674993345">Anulează plata</translation>
<translation id="3207960819495026254">Marcată</translation>
<translation id="3225919329040284222">Serverul a prezentat un certificat care nu se potrivește cu așteptările încorporate. Aceste așteptări sunt incluse pentru anumite site-uri web, cu un grad sporit de securitate, pentru a vă proteja.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Anulați reordonarea</translation>
<translation id="3282497668470633863">Adaugă numele de pe card</translation>
<translation id="3286538390144397061">Repornește acum</translation>
+<translation id="3287510313208355388">Descarcă când ești online</translation>
<translation id="3303855915957856445">Nu s-au găsit rezultate de căutare</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Interval de preluare:</translation>
<translation id="3462200631372590220">Ascundeți detaliile avansate</translation>
<translation id="3467763166455606212">Este necesar numele titularului cardului</translation>
-<translation id="3478058380795961209">Lună expirare</translation>
<translation id="3479539252931486093">A fost o situație neașteptată? <ph name="BEGIN_LINK" />Anunță-ne<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Nu acum</translation>
<translation id="3498215018399854026">Momentan, nu ți-am putut contacta părintele. Încearcă din nou.</translation>
@@ -343,12 +345,13 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Se acceptă carduri de credit și preplătite.</translation>
<translation id="3582930987043644930">Adăugați un nume</translation>
<translation id="3583757800736429874">&amp;Repetați mutarea</translation>
<translation id="3586931643579894722">Ascunde detaliile</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Introdu o dată de expirare validă</translation>
-<translation id="36224234498066874">Ștergeți datele de navigare...</translation>
+<translation id="36224234498066874">Șterge datele de navigare...</translation>
<translation id="362276910939193118">Afișează întregul istoric</translation>
<translation id="3623476034248543066">Afișați valoarea</translation>
<translation id="3630155396527302611">Dacă este deja inclus ca program căruia i se permite să acceseze rețeaua, încearcă
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Dacă această pagină se afișează în mod frecvent, încercați <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Versiune</translation>
<translation id="3678029195006412963">Solicitarea nu a putut fi semnată</translation>
+<translation id="3678529606614285348">Deschide pagina într-o fereastră incognito nouă (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Raport de blocare creat <ph name="CRASH_TIME" /> și încărcat <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informații despre certificat</translation>
<translation id="3690164694835360974">Conectarea nu este securizată</translation>
-<translation id="3693415264595406141">Parolă:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Se încarcă…</translation>
<translation id="3712624925041724820">Licențe epuizate</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Atacatorii de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> te pot înșela, determinându-te să faci ceva periculos, cum ar fi să instalezi software sau să îți dezvălui informațiile cu caracter personal (de exemplu, parole, numere de telefon sau date despre cardurile de credit). <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Traducerea nu a reușit din cauza unei erori de server.</translation>
<translation id="3759461132968374835">Nu există blocări raportate recent. Blocările care au avut loc când raportarea blocărilor era dezactivată nu vor apărea aici.</translation>
+<translation id="3765032636089507299">Pagina Navigare sigură este în construcție.</translation>
<translation id="3778403066972421603">Dorești să salvezi acest card în Contul Google și pe acest dispozitiv?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Expiră în <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Solicitarea de a accesa acest site a fost trimisă către <ph name="NAME" /></translation>
<translation id="3890664840433101773">Adaugă o adresă de e-mail</translation>
<translation id="3901925938762663762">Cardul este expirat</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">S-a încărcat Raportul de blocare cu ID-ul <ph name="CRASH_ID" /> (ID blocare locală: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; în certificatul său de securitate nu este specificat câmpul Nume alternative subiect. Cauza poate fi o eroare de configurare sau interceptarea conexiunii de către un atacator.</translation>
+<translation id="3949601375789751990">Istoricul de navigare apare aici</translation>
<translation id="3963721102035795474">Modul Cititor</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Niciunul}=1{De la 1 site }few{De la # site-uri }other{De la # de site-uri }}</translation>
<translation id="397105322502079400">Se calculează...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Setări pentru site-uri</translation>
<translation id="4169947484918424451">Dorești ca acest card să fie salvat în Chromium?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Atacatorii de pe acest site pot încerca să instaleze programe periculoase pe computerul tău, care să îți fure sau să îți șteargă informațiile (de exemplu, fotografiile, parolele, mesajele sau informațiile despre cardurile de credit).</translation>
<translation id="4372948949327679948">Se aștepta valoarea <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Ați încercat să accesați <ph name="DOMAIN" />, dar certificatul furnizat de server a fost revocat de emitentul său. Aceasta înseamnă că acreditările de securitate furnizate de server nu sunt deloc de încredere. Este posibil să comunicați cu un atacator.</translation>
-<translation id="4381091992796011497">Nume de utilizator:</translation>
<translation id="4394049700291259645">Dezactivează</translation>
<translation id="4406896451731180161">rezultate ale căutării</translation>
<translation id="4424024547088906515">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; Chrome 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="4432688616882109544"><ph name="HOST_NAME" /> nu a acceptat certificatul de conectare sau un astfel de certificat nu a fost oferit.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Elimini adresa din Chrome?</translation>
<translation id="4592951414987517459">Conexiunea la <ph name="DOMAIN" /> este criptată folosind o suită de codificare modernă.</translation>
<translation id="4594403342090139922">&amp;Anulați ștergerea</translation>
+<translation id="4611292653554630842">Conectează-te</translation>
<translation id="4619615317237390068">File de pe alte dispozitive</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; certificatul său de securitate conține erori. Cauza poate fi o configurare greșită sau interceptarea conexiunii de către un atacator.</translation>
<translation id="4690462567478992370">Nu mai folosi un certificat nevalid</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Conexiunea a fost întreruptă</translation>
+<translation id="471880041731876836">Nu ai permisiunea să accesezi acest site</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />să rulezi Diagnostice rețea Windows<ph name="END_LINK" />;</translation>
<translation id="4726672564094551039">Reîncărcați politicile</translation>
<translation id="4728558894243024398">Platformă</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Acest conținut ar putea încerca să te convingă să instalezi software sau să dezvălui informații cu caracter personal, prin înșelătorie. <ph name="BEGIN_LINK" />Afișează oricum<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Caută în istoric</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Autentificare obligatorie</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{și încă 1 pagină web}few{și încă # pagini web}other{și încă # de pagini web}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Această pagină a fost tradusă dintr-o limbă necunoscută în <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Plată</translation>
<translation id="4926049483395192435">Valoarea trebuie specificată.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Acțiuni</translation>
<translation id="4958444002117714549">Extindeți lista</translation>
<translation id="4974590756084640048">Reactivează avertismentele</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Parolă incorectă</translation>
<translation id="5056549851600133418">Articole pentru tine</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />să verifici adresa proxy-ului;<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Este posibil să pierzi accesul la conținutul protejat de pe anumite site-uri.</translation>
<translation id="5087286274860437796">Momentan, certificatul serverului este nevalid.</translation>
<translation id="5087580092889165836">Adaugă un card</translation>
<translation id="5089810972385038852">Stat</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provincie</translation>
<translation id="5115563688576182185">(64 de biți)</translation>
<translation id="5141240743006678641">Criptează parolele sincronizate cu datele de conectare Google</translation>
-<translation id="514421653919133810">deschide pagina în modul incognito (Ctrl-Shift-N);</translation>
<translation id="5145883236150621069">Răspunsul pentru politică include un cod de eroare</translation>
+<translation id="5159010409087891077">Deschide pagina într-o fereastră incognito nouă (⇧⌘N)</translation>
<translation id="5171045022955879922">Caută sau introdu adresa URL</translation>
<translation id="5172758083709347301">Computer</translation>
<translation id="5179510805599951267">Nu este în <ph name="ORIGINAL_LANGUAGE" />? Semnalează această eroare.</translation>
-<translation id="5181140330217080051">Se descarcă</translation>
<translation id="5190835502935405962">Bară de marcaje</translation>
<translation id="5199729219167945352">Experimente</translation>
<translation id="5205222826937269299">Numele este obligatoriu</translation>
<translation id="5222812217790122047">Adresa de e-mail este obligatorie</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Folosești Chrome la serviciu? Companiile pot gestiona setările Chrome pentru angajații lor. Află mai multe</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Urmează acești pași ca să dezactivezi temporar software-ul, astfel încât să poți accesa internetul. Vei avea nevoie de privilegii de administrator.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Dă clic pe <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, apoi caută și selectează <ph name="BEGIN_BOLD" />„View local services”<ph name="END_BOLD" /> (Vezi serviciile locale).
+ <ph name="LIST_ITEM" />Selectează <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Sub <ph name="BEGIN_BOLD" />Startup type<ph name="END_BOLD" /> (Tip de pornire), selectează <ph name="BEGIN_BOLD" />Disabled<ph name="END_BOLD" /> (Dezactivat)
+ <ph name="LIST_ITEM" />Sub <ph name="BEGIN_BOLD" />Service status<ph name="END_BOLD" /> (Starea serviciului), dă clic pe <ph name="BEGIN_BOLD" />Stop<ph name="END_BOLD" /> (Oprește)
+ <ph name="LIST_ITEM" />Clic pe <ph name="BEGIN_BOLD" />Apply<ph name="END_BOLD" /> (Aplică), apoi dă clic pe <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Accesează <ph name="BEGIN_LEARN_MORE_LINK" />Centrul de ajutor Chrome<ph name="END_LEARN_MORE_LINK" /> pentru a afla cum poți să elimini definitiv software-ul de pe computer
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Conexiunea la acest site nu este privată. Pentru a ieși oricând din modul RV, scoate vizualizatorul și apasă pe Înapoi.</translation>
<translation id="5299298092464848405">Eroare la analizarea politicii</translation>
<translation id="5308689395849655368">Raportarea blocărilor este dezactivată.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Eroare de validare a schemei la „<ph name="ERROR_PATH" />”: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Această pagină de pe <ph name="HOST_NAME" /> nu poate fi găsită</translation>
<translation id="5455374756549232013">Marcaj temporal greșit pentru politică</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> din <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Nevalide</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Repetați editarea</translation>
+<translation id="5481076368049295676">Acest conținut ar putea încerca să instaleze software periculos pe dispozitivul tău, care îți fură sau îți șterge informațiile. <ph name="BEGIN_LINK" />Afișează oricum<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Adaugă o adresă validă</translation>
<translation id="5492298309214877701">Acest site din rețeaua intranet a companiei, organizației sau școlii are aceeași adresă URL folosită de un site extern.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Articolul solicitat nu poate fi găsit.</translation>
<translation id="5544037170328430102">O pagină încorporată de pe <ph name="SITE" /> afișează mesajul:</translation>
<translation id="5556459405103347317">Reîncarcă</translation>
+<translation id="5560088892362098740">Data expirării</translation>
<translation id="5565735124758917034">Activ</translation>
<translation id="5571083550517324815">Nu se poate prelua de la această adresă. Selectează altă adresă.</translation>
<translation id="5572851009514199876">Pornește și conectează-te la Chrome, ca acesta să verifice dacă ai permisiunea să accesezi site-ul.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Setările pentru politică nu au putut fi încărcate</translation>
<translation id="5631439013527180824">Indicativ nevalid pentru gestionarea gadgetului</translation>
<translation id="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="5646376287012673985">Locație</translation>
<translation id="5659593005791499971">Adresă de e-mail</translation>
<translation id="5669703222995421982">Obține conținut personalizat</translation>
<translation id="5675650730144413517">Pagina nu funcționează</translation>
<translation id="5710435578057952990">Identitatea acestui site nu a fost confirmată.</translation>
-<translation id="5713016350996637505">Conținutul înșelător a fost blocat</translation>
+<translation id="5719499550583120431">Se acceptă carduri preplătite.</translation>
<translation id="5720705177508910913">Utilizator curent</translation>
<translation id="5732392974455271431">Părinții tăi îl pot debloca pentru tine</translation>
<translation id="5763042198335101085">Introdu o adresă de e-mail validă</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Dorești să completezi datele <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Conexiunea la <ph name="DOMAIN" /> este criptată folosind o suită de codificare învechită.</translation>
<translation id="5813119285467412249">&amp;Repetați adăugarea</translation>
-<translation id="5814352347845180253">Este posibil să pierzi accesul la conținutul premium de pe <ph name="SITE" /> și de pe alte site-uri.</translation>
<translation id="5838278095973806738">Nu ar trebui să introduci informații sensibile pe acest site (de exemplu, parole sau carduri de credit), deoarece ar putea fi furate de atacatori.</translation>
<translation id="5869405914158311789">Acest site nu poate fi accesat</translation>
<translation id="5869522115854928033">Parole salvate</translation>
<translation id="5872918882028971132">Sugestii parentale</translation>
+<translation id="5893752035575986141">Se acceptă carduri de credit.</translation>
<translation id="5901630391730855834">Galben</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="5926846154125914413">Este posibil să pierzi accesul la conținutul premium de pe anumite site-uri.</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="5967867314010545767">Eliminați din istoric</translation>
<translation id="5975083100439434680">Micșorează</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Închide</translation>
<translation id="6042308850641462728">Mai multe</translation>
<translation id="6047233362582046994">Dacă îți asumi riscurile de securitate, poți să <ph name="BEGIN_LINK" />accesezi acest site<ph name="END_LINK" /> înainte ca aplicațiile dăunătoare să fie eliminate.</translation>
+<translation id="6047927260846328439">Acest conținut ar putea încerca să te păcălească să instalezi software sau să dezvălui informații cu caracter personal. <ph name="BEGIN_LINK" />Afișează oricum<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Nu poți accesa <ph name="SITE" /> acum, deoarece site-ul folosește fixarea certificatelor. Erorile de rețea și atacurile sunt de obicei temporare și probabil că această pagină va funcționa mai târziu.</translation>
<translation id="6060685159320643512">Atenție, aceste experimente pot produce daune</translation>
<translation id="6080696365213338172">Ați accesat conținut utilizând un certificat oferit de administrator. Datele pe care le transmiteți către <ph name="DOMAIN" /> pot fi interceptate de administratorul dvs.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Află mai multe</translation>
<translation id="6169916984152623906">Acum poți naviga în mod privat, iar celelalte persoane care folosesc acest dispozitiv nu îți vor vedea activitatea. Cu toate acestea, descărcările și marcajele vor fi salvate.</translation>
<translation id="6177128806592000436">Conexiunea la acest site nu este sigură</translation>
-<translation id="6184817833369986695">(grup: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Verificați conexiunea la internet</translation>
<translation id="6218753634732582820">Elimini adresa din Chromium?</translation>
<translation id="6221345481584921695">Navigarea sigură Google <ph name="BEGIN_LINK" />a detectat recent programe malware<ph name="END_LINK" /> pe <ph name="SITE" />. Site-urile care sunt de obicei sigure sunt uneori infectate cu programe malware. Conținutul rău-intenționat provine de la <ph name="SUBRESOURCE_HOST" />, un distribuitor cunoscut de programe malware.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Doriți să utilizați funcții Chrome noi și interesante? Încercați canalul nostru beta de la chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium nu a avut suficientă memorie la afișarea paginii web.</translation>
+<translation id="6397451950548600259">Pe computer există software care împiedică browserul Chrome să se conecteze în siguranță la internet</translation>
<translation id="6404511346730675251">Modificați marcajul</translation>
<translation id="6410264514553301377">Introdu data de expirare și codul CVC pentru <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Ți-ai întrebat părintele dacă poți accesa acest site</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Politici privind dispozitivele</translation>
<translation id="6477321094435799029">Chrome a detectat un cod neobișnuit pe această pagină și l-a blocat pentru a-ți proteja informațiile cu caracter personal (de exemplu, parole, numere de telefon sau carduri de credit).</translation>
<translation id="6489534406876378309">Începeți încărcarea rapoartelor de blocare</translation>
+<translation id="6507833130742554667">Se acceptă carduri de credit și de debit.</translation>
<translation id="6508722015517270189">repornește Chrome;</translation>
<translation id="6529602333819889595">&amp;Repetați ștergerea</translation>
<translation id="6534179046333460208">Sugestii pentru Webul material</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Acest conținut ar putea încerca să instaleze software periculos pe dispozitivul tău, care îți fură sau îți șterge informațiile. <ph name="BEGIN_LINK" />Afișează oricum<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Opțiuni de criptare</translation>
<translation id="662080504995468778">Rămâi pe pagină</translation>
<translation id="6626291197371920147">Adaugă un număr de card valid</translation>
<translation id="6628463337424475685">Căutare <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Atacatorii de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pot încerca să instaleze programe periculoase pe computerul tău Mac, 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="6644283850729428850">Această politică este învechită.</translation>
+<translation id="6657585470893396449">Parolă</translation>
<translation id="6671697161687535275">Elimini sugestia pentru formular din Chromium?</translation>
<translation id="6685834062052613830">Deconectează-te și finalizează configurarea</translation>
<translation id="6710213216561001401">Înapoi</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Județ</translation>
<translation id="6973656660372572881">Sunt specificate atât servere proxy fixe, cât și o adresă URL pentru scripturi .pac.</translation>
<translation id="6989763994942163495">Afișează setările avansate...</translation>
-<translation id="7000990526846637657">Nu s-au găsit intrări în istoric</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Contul Google poate să ofere alte forme ale istoricului de navigare la <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Parole</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Nu se poate expedia la această adresă. Selectează altă adresă.</translation>
<translation id="7138472120740807366">Metodă de livrare</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Plata nu este securizată</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Relansează acum</translation>
<translation id="7180611975245234373">Actualizați</translation>
<translation id="7182878459783632708">Nu au fost setate politici</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Descarcă</translation>
<translation id="7518003948725431193">Nu a fost găsită nicio pagină web pentru adresa: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Conexiunea la acest site nu este privată</translation>
<translation id="7535087603100972091">Valoare</translation>
<translation id="7537536606612762813">Obligatorie</translation>
<translation id="7542403920425041731">După ce confirmi, acest site va avea acces la detaliile cardului tău.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Încearcă următoarele sfaturi:</translation>
<translation id="7554791636758816595">Filă nouă</translation>
<translation id="7567204685887185387">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; este posibil ca certificatul său de securitate să fi fost emis fraudulos. Cauza poate fi o configurare greșită sau interceptarea conexiunii de către un atacator.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Această pagină este în <ph name="ORIGINAL_LANGUAGE" /> Vrei să fie tradusă?</translation>
<translation id="7569952961197462199">Elimini cardul de credit din Chrome?</translation>
<translation id="7569983096843329377">Negru</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certificat</translation>
<translation id="7716147886133743102">Blocată de administrator</translation>
<translation id="7716424297397655342">Acest site nu poate fi încărcat din memoria cache</translation>
+<translation id="774634243536837715">Conținutul periculos a fost blocat.</translation>
<translation id="7752995774971033316">Negestionat</translation>
<translation id="7755287808199759310">Părintele tău îl poate debloca pentru tine</translation>
<translation id="7758069387465995638">Este posibil ca firewallul sau software-ul antivirus să fi blocat conexiunea.</translation>
+<translation id="7759163816903619567">Domeniu afișat:</translation>
<translation id="7761701407923456692">Certificatul serverului nu se potrivește cu adresa URL.</translation>
<translation id="7763386264682878361">Parser pentru manifestul plății</translation>
<translation id="7764225426217299476">Adaugă o adresă</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">S-au găsit <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> pentru „<ph name="SEARCH_STRING" />”</translation>
<translation id="785549533363645510">Cu toate acestea, nu ești invizibil(ă). Trecerea în modul incognito nu ascunde activitatea de navigare față de angajator, față de furnizorul de servicii de internet sau față de site-urile pe care le accesezi.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Se acceptă carduri de debit și preplătite.</translation>
<translation id="7887683347370398519">Verifică codul CVC și încearcă din nou</translation>
<translation id="79338296614623784">Introdu un număr de telefon valid</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Trimiteți feedback</translation>
<translation id="8041940743680923270">Utilizați setarea prestabilită la nivel global (Întrebați)</translation>
<translation id="8088680233425245692">Articolul nu a fost vizualizat.</translation>
-<translation id="8089520772729574115">mai puțin de 1 MB</translation>
<translation id="8091372947890762290">Se așteaptă activarea pe server</translation>
<translation id="8118489163946903409">Metodă de plată</translation>
<translation id="8131740175452115882">Confirmați</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Anulați mutarea</translation>
<translation id="8201077131113104583">Adresa URL pentru actualizarea extensiei cu ID-ul „<ph name="EXTENSION_ID" />” nu este validă.</translation>
<translation id="8202097416529803614">Rezumatul comenzii</translation>
+<translation id="8205463626947051446">Site-ul tinde să afișeze anunțuri deranjante</translation>
<translation id="8218327578424803826">Locație atribuită:</translation>
<translation id="8225771182978767009">Persoana care a configurat computerul a ales să blocheze acest site.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Deschide pagina într-o filă incognito nouă</translation>
<translation id="8241707690549784388">Pagina pe care o cauți a utilizat informațiile pe care le-ai introdus. Întoarcerea la acea pagină ar putea face ca orice acțiune să fie repetată. Vrei să continui?</translation>
+<translation id="8241712895048303527">Blochează pe acest site</translation>
<translation id="8249320324621329438">Ultima preluare:</translation>
<translation id="8253091569723639551">Adresa de facturare este obligatorie</translation>
<translation id="8261506727792406068">Șterge</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Traducerea nu a reușit din cauza unei probleme cu conexiunea la rețea.</translation>
<translation id="8332188693563227489">Accesul la <ph name="HOST_NAME" /> nu este permis</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="8344669043927012510">deschide pagina în modul incognito (⇧⌘N).</translation>
<translation id="8349305172487531364">Bara de marcaje</translation>
<translation id="8363502534493474904">să dezactivezi modul Avion.</translation>
<translation id="8364627913115013041">Nesetată.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Raport de blocare încărcat <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Blocări (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Trebuie să introduci aceeași expresie de acces de două ori.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Setări</translation>
<translation id="8433057134996913067">Astfel, te vei deconecta de pe majoritatea site-urilor.</translation>
<translation id="8437238597147034694">&amp;Anulați mutarea</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Pentru a folosi cardurile din Contul Google, conectează-te la Chrome</translation>
<translation id="8488350697529856933">Se aplică pentru</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> a răspuns prea târziu.</translation>
-<translation id="8532105204136943229">An expirare</translation>
+<translation id="8503813439785031346">Nume utilizator</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="8571890674111243710">Se traduce pagina în <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Adăugați telefon
@@ -887,6 +919,7 @@
<translation id="859285277496340001">Certificatul nu specifică un mecanism pentru a verifica dacă acesta a fost revocat.</translation>
<translation id="8620436878122366504">Părinții tăi nu l-au aprobat încă</translation>
<translation id="8647750283161643317">Resetați-le pe toate la valorile prestabilite</translation>
+<translation id="8660471606262461360">Din Google Payments</translation>
<translation id="8703575177326907206">Conexiunea la <ph name="DOMAIN" /> nu este criptată.</translation>
<translation id="8718314106902482036">Plata nu a fost finalizată</translation>
<translation id="8725066075913043281">Încearcă din nou</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Mărește</translation>
<translation id="8931333241327730545">Dorești să salvezi acest card în Contul Google?</translation>
<translation id="8932102934695377596">Ora este setată în trecut</translation>
+<translation id="8938939909778640821">Carduri de credit și preplătite acceptate</translation>
<translation id="8971063699422889582">Certificatul serverului a expirat.</translation>
<translation id="8986494364107987395">Trimite automat la Google statistici de utilizare și rapoarte de blocare</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Blocați întotdeauna pe acest site</translation>
<translation id="9170848237812810038">&amp;Anulează</translation>
<translation id="917450738466192189">Certificatul serverului nu este valid.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> și încă <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Articolul nu a fost adăugat.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">GOLEȘTE FORMULARUL</translation>
<translation id="939736085109172342">Dosar nou</translation>
-<translation id="941721044073577244">Se pare că nu ai permisiunea de a accesa acest site</translation>
<translation id="969892804517981540">Versiune oficială</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Niciunul}=1{1 element}few{# elemente}other{# de elemente}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Versiune de programare</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ru.xtb b/chromium/components/strings/components_strings_ru.xtb
index b3d76ceff36..128c0ea414d 100644
--- a/chromium/components/strings/components_strings_ru.xtb
+++ b/chromium/components/strings/components_strings_ru.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Закройте другие приложения.</translation>
<translation id="1055184225775184556">&amp;Отменить добавление</translation>
<translation id="10614374240317010">Сайты, пароли для которых не сохраняются</translation>
+<translation id="1066396345355680611">Вы можете потерять доступ к защищенному контенту на <ph name="SITE" /> и других сайтах.</translation>
<translation id="106701514854093668">Закладки на компьютере</translation>
<translation id="1074497978438210769">Не защищено</translation>
<translation id="1080116354587839789">Выровнять по ширине окна</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Список для чтения</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" />: получен отчет о сбое (ещё не загружен или не отклонен)</translation>
<translation id="1281526147609854549">Выдан <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Опасный контент заблокирован</translation>
<translation id="1285320974508926690">Никогда не переводить этот сайт</translation>
<translation id="129553762522093515">Недавно закрытые</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Удалите файлы cookie<ph name="END_LINK" />.</translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Регистрация домена:</translation>
<translation id="1340482604681802745">Адрес выдачи</translation>
-<translation id="1344211575059133124">Для доступа к этой странице требуется разрешение</translation>
<translation id="1344588688991793829">Настройки автозаполнения в Chromium...</translation>
<translation id="1348198688976932919">Сайт содержит опасные приложения</translation>
<translation id="1374468813861204354">подсказки</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Идентификационные данные <ph name="ORGANIZATION" /> в <ph name="LOCALITY" /> проверены <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Да</translation>
<translation id="1430915738399379752">Печать</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}many{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Показывает предыдущую сохраненную копию этой страницы.</translation>
<translation id="1517433312004943670">Укажите номер телефона</translation>
+<translation id="1517500485252541695">Дебетовые и кредитные карты, которые принимаются к оплате</translation>
<translation id="1519264250979466059">Дата сборки</translation>
+<translation id="1527263332363067270">Ожидание подключения…</translation>
<translation id="153384715582417236">Пока это всё</translation>
<translation id="1549470594296187301">Для использования этой функции необходимо включить JavaScript.</translation>
<translation id="1555130319947370107">Синий</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Обратитесь за помощью к системному администратору.</translation>
<translation id="1740951997222943430">Недопустимый формат месяца.</translation>
-<translation id="1745358365027406341">Скачать позже</translation>
<translation id="17513872634828108">Открытые вкладки</translation>
<translation id="1753706481035618306">Номер страницы</translation>
<translation id="1763864636252898013">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Операционная система устройства не доверяет его сертификату безопасности. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Поле, обязательное для заполнения</translation>
<translation id="187918866476621466">Открыть стартовые страницы</translation>
<translation id="1883255238294161206">Свернуть список</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Фильтры</translation>
+<translation id="1916770123977586577">Чтобы применить к сайту новые настройки, перезагрузите страницу</translation>
+<translation id="1919345977826869612">Реклама</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Нет}=1{1 сайт}one{# сайт}few{# сайта}many{# сайтов}other{# сайта}}</translation>
<translation id="194030505837763158">Перейдите по ссылке: <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Карты предоплаты, которые принимаются к оплате</translation>
<translation id="1962204205936693436">Закладки <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Не удалось выполнить сериализацию</translation>
<translation id="1974060860693918893">Дополнительные</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Ошибка HTTP</translation>
<translation id="2270484714375784793">Номер телефона</translation>
<translation id="2282872951544483773">Недоступные расширения</translation>
-<translation id="2283595600756901917">{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="2292556288342944218">Доступ в Интернет закрыт</translation>
<translation id="230155334948463882">Добавить карту?</translation>
+<translation id="2316887270356262533">Освободится менее 1 МБ пространства. После этого некоторые веб-страницы могут загружаться дольше обычного.</translation>
<translation id="2317259163369394535">Для доступа к домену <ph name="DOMAIN" /> необходимо указать имя пользователя и пароль.</translation>
+<translation id="2317583587496011522">Принимаются дебетовые карты.</translation>
<translation id="2337852623177822836">Эта настройка управляется администратором</translation>
<translation id="2354001756790975382">Другие закладки</translation>
<translation id="2354430244986887761">Система Безопасного просмотра Google недавно <ph name="BEGIN_LINK" />обнаружила вредоносные приложения<ph name="END_LINK" /> на сайте <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Далее</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />: получен отчет о сбое (не загружен)</translation>
<translation id="2367567093518048410">Уровень</translation>
-<translation id="237718015863234333">Ничего не найдено</translation>
<translation id="2384307209577226199">Для предприятий (по умолчанию)</translation>
<translation id="2386255080630008482">Сертификат сервера отозван.</translation>
<translation id="2392959068659972793">Показывать правила, значения которых не заданы</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Если флажок установлен, Chromium будет хранить на этом устройстве данные карты для быстрого заполнения форм.</translation>
<translation id="2498091847651709837">Сканировать новую карту</translation>
<translation id="2501278716633472235">Назад</translation>
+<translation id="2503184589641749290">Дебетовые карты и карты предоплаты, которые принимаются к оплате</translation>
<translation id="2515629240566999685">Проверьте уровень сигнала сети.</translation>
-<translation id="2516305470678292029">Варианты интерфейса</translation>
<translation id="2539524384386349900">Определять</translation>
<translation id="255002559098805027">Сайт <ph name="HOST_NAME" /> отправил недействительный ответ.</translation>
<translation id="2556876185419854533">&amp;Отменить изменения</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Способ доставки</translation>
<translation id="277499241957683684">Устройство не зарегистрировано</translation>
<translation id="2784949926578158345">Соединение сброшено.</translation>
+<translation id="2788784517760473862">Кредитные карты, которые принимаются к оплате</translation>
<translation id="2794233252405721443">Сайт заблокирован</translation>
<translation id="2799020568854403057">Сайт содержит вредоносные приложения</translation>
<translation id="2803306138276472711">Система Google по проверке безопасности сайтов недавно обнаружила на <ph name="SITE" /> <ph name="BEGIN_LINK" />вредоносное ПО<ph name="END_LINK" />. Будьте внимательны, иногда даже на безопасных сайтах появляются вирусы.</translation>
<translation id="2824775600643448204">Адресная строка и строка поиска</translation>
<translation id="2826760142808435982">Соединение зашифровано и проверено с помощью <ph name="CIPHER" />. В качестве механизма обмена ключами используется <ph name="KX" />.</translation>
<translation id="2835170189407361413">Очистить форму</translation>
+<translation id="2851634818064021665">Для доступа к этой странице требуется разрешение.</translation>
<translation id="2856444702002559011">Злоумышленники могут пытаться похитить ваши данные с сайта <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (например, пароли, сообщения или номера банковских карт). <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Не обновлять</translation>
-<translation id="2900469785430194048">Chrome не хватает памяти для показа этой страницы.</translation>
<translation id="2909946352844186028">Похоже, вы подключились к другой сети.</translation>
<translation id="2916038427272391327">Закройте другие программы.</translation>
<translation id="2922350208395188000">Не удается проверить сертификат сервера.</translation>
<translation id="2928905813689894207">Платежный адрес</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> адрес}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> адрес}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> адреса}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> адресов}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> адреса}}</translation>
<translation id="2941952326391522266">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Его сертификат безопасности относится к <ph name="DOMAIN2" />. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
<translation id="2948083400971632585">Прокси-серверы, используемые для соединения, можно отключить на странице настроек.</translation>
<translation id="2955913368246107853">Закрыть панель поиска</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Если функция включена, Chromium будет хранить на этом устройстве данные карты для быстрого заполнения форм.</translation>
<translation id="2985398929374701810">Укажите действительный адрес.</translation>
<translation id="2986368408720340940">Этот способ выдачи недоступен. Выберите другой.</translation>
@@ -277,11 +281,9 @@
<translation id="3167968892399408617">Страницы, открытые в этом окне, не останутся в истории браузера или поиска. Они не оставят на компьютере следов, таких как файлы cookie, после того как вы закроете все вкладки инкогнито. Скачанные вами файлы и добавленные закладки будут сохранены.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Остров</translation>
-<translation id="317583078218509884">Новые настройки разрешений для сайта вступят в силу после обновления страницы.</translation>
<translation id="3176929007561373547">Проверьте настройки прокси-сервера или попросите администратора
задать верные параметры. В противном случае^
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Откройте страницу в режиме инкогнито.</translation>
<translation id="320323717674993345">Отменить оплату</translation>
<translation id="3207960819495026254">Добавлено в закладки</translation>
<translation id="3225919329040284222">Сертификат не соответствует встроенным параметрам определенных сайтов с высоким уровнем безопасности.</translation>
@@ -294,6 +296,7 @@
<translation id="3270847123878663523">&amp;Отменить изменение порядка</translation>
<translation id="3282497668470633863">Укажите имя владельца карты</translation>
<translation id="3286538390144397061">Перезапустить сейчас</translation>
+<translation id="3287510313208355388">Скачать при подключении к Интернету</translation>
<translation id="3303855915957856445">Ничего не найдено</translation>
<translation id="3305707030755673451">Данные были зашифрованы с помощью кодовой фразы <ph name="TIME" />. Введите ее, чтобы начать синхронизацию.</translation>
<translation id="3320021301628644560">Добавьте платежный адрес</translation>
@@ -326,7 +329,6 @@
<translation id="3452404311384756672">Выберите интервал:</translation>
<translation id="3462200631372590220">Скрыть подробности</translation>
<translation id="3467763166455606212">Укажите имя владельца карты.</translation>
-<translation id="3478058380795961209">Месяц</translation>
<translation id="3479539252931486093">Этот сайт не должен быть заблокирован? <ph name="BEGIN_LINK" />Сообщите нам об этом<ph name="END_LINK" />.</translation>
<translation id="3479552764303398839">Не сейчас</translation>
<translation id="3498215018399854026">Не удалось связаться с вашими родителями. Повторите попытку.</translation>
@@ -342,6 +344,7 @@
&lt;p&gt;Установите точную дату и время. Для этого откройте раздел &lt;strong&gt;Основные&lt;/strong&gt; в приложении &lt;strong&gt;Настройки&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Принимаются кредитные карты и карты предоплаты.</translation>
<translation id="3582930987043644930">Добавьте имя</translation>
<translation id="3583757800736429874">&amp;Повторить перемещение</translation>
<translation id="3586931643579894722">Скрыть подробности</translation>
@@ -357,10 +360,10 @@
<translation id="3655670868607891010">Если эта проблема возникает часто, изучите <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Версия</translation>
<translation id="3678029195006412963">Не удалось подписать запрос</translation>
+<translation id="3678529606614285348">Открыть страницу в новом окне в режиме инкогнито (Ctrl + Shift + N)</translation>
<translation id="3679803492151881375"><ph name="CRASH_TIME" />: отчет о сбоях сохранен. <ph name="UPLOAD_TIME" />: отчет о сбоях загружен.</translation>
<translation id="3681007416295224113">Данные сертификата</translation>
<translation id="3690164694835360974">Небезопасный вход</translation>
-<translation id="3693415264595406141">Пароль:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Загрузка...</translation>
<translation id="3712624925041724820">Недостаточно лицензий</translation>
@@ -372,6 +375,7 @@
<translation id="3748148204939282805">Посещение сайта <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="375403751935624634">Сбой при переводе вследствие ошибки сервера.</translation>
<translation id="3759461132968374835">Нет записей о недавних сбоях. Сбои, которые произошли при отключенной функции записи сбоев, здесь не отображаются.</translation>
+<translation id="3765032636089507299">Страница безопасного просмотра находится в разработке.</translation>
<translation id="3778403066972421603">Хотите сохранить карту в аккаунте Google и на этом устройстве?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Срок действия: до <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -384,8 +388,10 @@
<translation id="3886446263141354045">Ваш запрос на доступ к сайту отправлен пользователю <ph name="NAME" /></translation>
<translation id="3890664840433101773">Добавление адреса электронной почты</translation>
<translation id="3901925938762663762">Срок действия карты истек</translation>
+<translation id="3909695131102177774"><ph name="LABEL" />. <ph name="ERROR" />.</translation>
<translation id="3945915738023014686">Идентификатор загруженного отчета о сбоях: <ph name="CRASH_ID" />. Локальный идентификатор сбоя: <ph name="CRASH_LOCAL_ID" />.</translation>
<translation id="3949571496842715403">Сервер не может подтвердить связь с доменом <ph name="DOMAIN" />. В его сертификате безопасности не указаны альтернативные варианты имен. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
+<translation id="3949601375789751990">История просмотров появится здесь</translation>
<translation id="3963721102035795474">Режим чтения</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Нет}=1{С 1 сайта }one{С # сайта }few{С # сайтов }many{С # сайтов }other{С # сайта }}</translation>
<translation id="397105322502079400">Вычисление…</translation>
@@ -411,9 +417,11 @@
<translation id="413544239732274901">Подробнее...</translation>
<translation id="4148925816941278100">American Express</translation>
<translation id="4151403195736952345">Использовать глобальный параметр по умолчанию (находить)</translation>
-<translation id="4165986682804962316">Настройки сайта</translation>
+<translation id="4165986682804962316">Настройки сайтов</translation>
<translation id="4169947484918424451">Сохранить эту карту в Chromium?</translation>
<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="4196861286325780578">&amp;Повторить перемещение</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Проверьте настройки брандмауэра и антивирусного ПО<ph name="END_LINK" />.</translation>
<translation id="4220128509585149162">Завершение работы программы</translation>
@@ -437,12 +445,12 @@
<translation id="4356973930735388585">Злоумышленники могут использовать этот сайт, чтобы установить на ваш компьютер вредоносное ПО, которое крадет или удаляет личную информацию (например, фотографии, пароли, сообщения и реквизиты банковских карт).</translation>
<translation id="4372948949327679948">Ожидаемое значение: <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Вы попытались перейти на сайт <ph name="DOMAIN" />, однако сертификат, предоставленный сервером, был отозван издателем. Это означает, что учетные данные безопасности, предоставленные сервером, не заслуживают доверия. Возможно, вы имеете дело со злоумышленниками.</translation>
-<translation id="4381091992796011497">Имя пользователя:</translation>
<translation id="4394049700291259645">Отключить</translation>
<translation id="4406896451731180161">Результаты поиска</translation>
<translation id="4424024547088906515">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Chrome не доверяет его сертификату безопасности. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
<translation id="4432688616882109544">Ваш сертификат отклонен сайтом <ph name="HOST_NAME" /> или не был выдан.</translation>
<translation id="443673843213245140">Прокси-сервер отключен, но при этом его конфигурация задана явным образом.</translation>
+<translation id="445100540951337728">Дебетовые карты, которые принимаются к оплате</translation>
<translation id="4506176782989081258">Ошибка проверки: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Обратитесь за помощью к системному администратору.</translation>
<translation id="450710068430902550">Доступ администратора</translation>
@@ -454,12 +462,14 @@
<translation id="4587425331216688090">Удалить адрес из Chrome?</translation>
<translation id="4592951414987517459">Соединение с <ph name="DOMAIN" /> зашифровано с помощью современных наборов шифров.</translation>
<translation id="4594403342090139922">&amp;Отменить удаление</translation>
+<translation id="4611292653554630842">Войти</translation>
<translation id="4619615317237390068">Вкладки с других устройств</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Его сертификат безопасности содержит ошибки. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
<translation id="4690462567478992370">Прекратить использование недействительного сертификата</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Соединение прервано</translation>
+<translation id="471880041731876836">Доступ запрещен</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Выполните диагностику сети в Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Повторно загрузить политики</translation>
<translation id="4728558894243024398">Платформа</translation>
@@ -481,14 +491,15 @@
<translation id="4850886885716139402">Посмотреть</translation>
<translation id="4854362297993841467">Этот способ доставки недоступен. Выберите другой.</translation>
<translation id="4858792381671956233">Запрос на просмотр сайта отправлен вашим родителям</translation>
-<translation id="4863764087567530506">Посещение этой страницы может привести к установке вредоносной программы или хищению вашей личной информации. <ph name="BEGIN_LINK" />Все равно продолжить<ph name="END_LINK" /></translation>
<translation id="4880827082731008257">Искать в истории</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> и <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Требуется аутентификация</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{и ещё 1 веб-страница}one{и ещё # веб-страница}few{и ещё # веб-страницы}many{и ещё # веб-страниц}other{и ещё # веб-страницы}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Эта страница была автоматически переведена с неизвестного языка на <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Платеж</translation>
<translation id="4926049483395192435">Укажите значение.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Действия</translation>
<translation id="4958444002117714549">Развернуть список</translation>
<translation id="4974590756084640048">Снова включить предупреждения</translation>
@@ -504,6 +515,7 @@
<translation id="5045550434625856497">Неправильный пароль</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>
<translation id="5087580092889165836">Добавить карту</translation>
<translation id="5089810972385038852">Штат</translation>
@@ -511,18 +523,27 @@
<translation id="5095208057601539847">Провинция</translation>
<translation id="5115563688576182185">(64 бит)</translation>
<translation id="5141240743006678641">Шифровать синхронизированные пароли с помощью учетных данных Google</translation>
-<translation id="514421653919133810">Откройте страницу в режиме инкогнито (Ctrl + Shift + N).</translation>
<translation id="5145883236150621069">При проверке политики возвращен код ошибки</translation>
+<translation id="5159010409087891077">Открыть страницу в новом окне в режиме инкогнито (⇧ + ⌘ + N)</translation>
<translation id="5171045022955879922">Введите запрос или URL</translation>
<translation id="5172758083709347301">Локальный компьютер</translation>
<translation id="5179510805599951267">Это не <ph name="ORIGINAL_LANGUAGE" />? Сообщите об ошибке</translation>
-<translation id="5181140330217080051">Скачивается</translation>
<translation id="5190835502935405962">Панель закладок</translation>
<translation id="5199729219167945352">Экспериментальные функции</translation>
<translation id="5205222826937269299">Введите имя или название</translation>
<translation id="5222812217790122047">Введите адрес электронной почты</translation>
<translation id="5251803541071282808">Облако</translation>
<translation id="5277279256032773186">Используете Chrome на работе? Узнайте, как компании могут управлять настройками Chrome на корпоративных устройствах.</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Чтобы временно отключить ПО и установить соединение с Интернетом, следуйте инструкциям ниже. Вам потребуются права администратора.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Нажмите <ph name="BEGIN_BOLD" />Пуск<ph name="END_BOLD" /> и выполните поиск по запросу <ph name="BEGIN_BOLD" />Просмотр локальных служб<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Выберите <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />В поле <ph name="BEGIN_BOLD" />Тип запуска<ph name="END_BOLD" /> выберите <ph name="BEGIN_BOLD" />Отключена<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />В разделе <ph name="BEGIN_BOLD" />Состояние службы<ph name="END_BOLD" /> нажмите <ph name="BEGIN_BOLD" />Остановить<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Нажмите <ph name="BEGIN_BOLD" />Применить<ph name="END_BOLD" />, затем <ph name="BEGIN_BOLD" />ОК<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Посетите <ph name="BEGIN_LEARN_MORE_LINK" />Справочный центр Chrome<ph name="END_LEARN_MORE_LINK" />, чтобы узнать, как окончательно удалить ПО с компьютера.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Подключение к веб-сайту не защищено. Чтобы выйти из VR-режима, снимите гарнитуру и нажмите кнопку "Назад".</translation>
<translation id="5299298092464848405">Не удалось выполнить анализ политики</translation>
<translation id="5308689395849655368">Отчеты о сбоях отключены.</translation>
@@ -539,9 +560,10 @@
<translation id="5439770059721715174">Ошибка проверки схемы, <ph name="ERROR_PATH" />: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Страница <ph name="HOST_NAME" /> не найдена</translation>
<translation id="5455374756549232013">Неверная временная метка политики</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> из <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Недопустимые данные</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> контакт}one{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> контакт}few{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> контакта}many{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> контактов}other{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> контакта}}</translation>
<translation id="5470861586879999274">&amp;Повторить изменения</translation>
+<translation id="5481076368049295676">Посещение этой страницы может привести к установке вредоносной программы, которая похищает или удаляет данные. <ph name="BEGIN_LINK" />Все равно продолжить<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Введите действительный адрес</translation>
<translation id="5492298309214877701">Этот сайт в интранете организации или учебного заведения имеет тот же URL, что и сайт в Интернете.
<ph name="LINE_BREAK" />
@@ -553,6 +575,7 @@
<translation id="5540224163453853">Не удалось найти указанную статью</translation>
<translation id="5544037170328430102">Подтвердите действие на <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Перезагрузить</translation>
+<translation id="5560088892362098740">Дата окончания срока действия</translation>
<translation id="5565735124758917034">Активен</translation>
<translation id="5571083550517324815">Этот адрес не поддерживается. Выберите другой.</translation>
<translation id="5572851009514199876">Выполните вход, чтобы Chrome определил, разрешен ли вам доступ к этому сайту.</translation>
@@ -567,12 +590,13 @@
<translation id="5629630648637658800">Не удалось применить настройки политики</translation>
<translation id="5631439013527180824">Токен устройства недействителен</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="5646376287012673985">Местоположение</translation>
<translation id="5659593005791499971">Электронная почта</translation>
<translation id="5669703222995421982">Получение персонализированного контента</translation>
<translation id="5675650730144413517">Страница недоступна</translation>
<translation id="5710435578057952990">Идентификационные данные этого сайта не проверены.</translation>
-<translation id="5713016350996637505">Мошеннический контент заблокирован</translation>
+<translation id="5719499550583120431">Принимаются карты предоплаты.</translation>
<translation id="5720705177508910913">Текущий пользователь</translation>
<translation id="5732392974455271431">Для разблокировки обратитесь к родителям.</translation>
<translation id="5763042198335101085">Укажите действительный адрес электронной почты.</translation>
@@ -584,15 +608,14 @@
<translation id="5803412860119678065">Заполнить данные банковской карты <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Соединение с <ph name="DOMAIN" /> зашифровано с помощью устаревшего набора шифров.</translation>
<translation id="5813119285467412249">&amp;Повторить добавление</translation>
-<translation id="5814352347845180253">Вы можете потерять доступ к премиум-контенту на <ph name="SITE" /> и других сайтах.</translation>
<translation id="5838278095973806738">Не сообщайте этому сайту конфиденциальную информацию (например, пароли и номера банковских карт). К ней могут получить доступ злоумышленники.</translation>
<translation id="5869405914158311789">Не удается получить доступ к сайту</translation>
<translation id="5869522115854928033">Сайты с сохраненными паролями</translation>
<translation id="5872918882028971132">Подсказки для родительских элементов</translation>
+<translation id="5893752035575986141">Принимаются кредитные карты.</translation>
<translation id="5901630391730855834">Желтый</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="5926846154125914413">Вы можете потерять доступ к премиум-контенту на некоторых сайтах.</translation>
<translation id="5959728338436674663">Автоматически отправлять <ph name="BEGIN_WHITEPAPER_LINK" />системную информацию и контент страниц<ph name="END_WHITEPAPER_LINK" /> в Google, чтобы улучшить распознавание опасных приложений и сайтов. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">Удалить из истории</translation>
<translation id="5975083100439434680">Уменьшить</translation>
@@ -608,6 +631,7 @@
<translation id="6040143037577758943">Закрыть</translation>
<translation id="6042308850641462728">Подробнее...</translation>
<translation id="6047233362582046994">Если вы осознаете, что можете подвергнуть риску свои личные данные, то можете <ph name="BEGIN_LINK" />перейти на зараженный сайт<ph name="END_LINK" />, не дожидаясь удаления вредоносных приложений.</translation>
+<translation id="6047927260846328439">Посещение этой страницы может привести к установке вредоносной программы или хищению вашей личной информации. <ph name="BEGIN_LINK" />Все равно продолжить<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Веб-сайт <ph name="SITE" /> использует механизм Certificate Pinning, поэтому на нем могла произойти подмена сертификата. Открыть сайт в настоящее время нельзя. Сбой мог быть вызван сетевой ошибкой или действиями злоумышленников. Скорее всего, сайт заработает через некоторое время.</translation>
<translation id="6060685159320643512">Будьте осторожны при работе с экспериментальной версией</translation>
<translation id="6080696365213338172">Вы используете сертификат, предоставленный администратором, поэтому он может заблокировать передачу данных на сайт <ph name="DOMAIN" />.</translation>
@@ -621,7 +645,6 @@
<translation id="6165508094623778733">Подробнее...</translation>
<translation id="6169916984152623906">Ваши действия в режиме инкогнито будут недоступны другим пользователям этого устройства. Однако закладки и скачанные файлы сохранятся.</translation>
<translation id="6177128806592000436">Подключение к сайту не защищено</translation>
-<translation id="6184817833369986695">(когорта: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Проверьте подключение к Интернету</translation>
<translation id="6218753634732582820">Удалить адрес из Chromium?</translation>
<translation id="6221345481584921695">Система Google по проверке безопасности сайтов недавно обнаружила на <ph name="SITE" /> <ph name="BEGIN_LINK" />вредоносное ПО<ph name="END_LINK" />. Его источником является <ph name="SUBRESOURCE_HOST" />, не раз замеченным в распространении вирусов. Будьте внимательны, иногда даже на безопасных сайтах появляются вредоносные программы.</translation>
@@ -640,13 +663,14 @@
<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>
<translation id="6387478394221739770">Хотите быть в курсе новинок Chrome? Выберите бета-канал на странице "chrome.com/beta".</translation>
-<translation id="6389758589412724634">Chromium не хватает памяти для показа этой страницы.</translation>
+<translation id="6397451950548600259">ПО, установленное на компьютере, не позволяет Chrome безопасно подключиться к Интернету</translation>
<translation id="6404511346730675251">Изменить закладку</translation>
<translation id="6410264514553301377">Введите срок действия и CVC-код карты <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Запрос на просмотр сайта отправлен вашему родителю</translation>
@@ -661,6 +685,7 @@
<translation id="647261751007945333">Правила устройства</translation>
<translation id="6477321094435799029">Браузер Chrome обнаружил на этой странице необычный код и заблокировал его, чтобы защитить ваши данные (например, пароли, а также номера телефонов и банковских карт).</translation>
<translation id="6489534406876378309">Начать загрузку сведений об ошибках</translation>
+<translation id="6507833130742554667">Принимаются кредитные и дебетовые карты.</translation>
<translation id="6508722015517270189">Перезапустите Google Chrome.</translation>
<translation id="6529602333819889595">&amp;Повторить удаление</translation>
<translation id="6534179046333460208">Интернет вокруг нас: рекомендации</translation>
@@ -669,13 +694,13 @@
<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="657639383826808334">Посещение этой страницы может привести к установке опасной программы, которая похищает или удаляет данные. <ph name="BEGIN_LINK" />Все равно продолжить<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">Параметры шифрования</translation>
<translation id="662080504995468778">Остаться</translation>
<translation id="6626291197371920147">Введите номер действующей карты</translation>
<translation id="6628463337424475685">Поиск <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Сайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> может установить на ваш компьютер Mac вредоносное ПО, которое крадет или удаляет личную информацию (например, фотографии, пароли, сообщения и реквизиты банковских карт). <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Правило устарело.</translation>
+<translation id="6657585470893396449">Пароль</translation>
<translation id="6671697161687535275">Удалить подсказку из Chromium?</translation>
<translation id="6685834062052613830">Выйдите из аккаунта и завершите настройку</translation>
<translation id="6710213216561001401">Назад</translation>
@@ -706,7 +731,6 @@
<translation id="6970216967273061347">Район</translation>
<translation id="6973656660372572881">Указаны как фиксированные прокси-серверы, так и URL PAC-скриптов.</translation>
<translation id="6989763994942163495">Показать дополнительные настройки</translation>
-<translation id="7000990526846637657">Данные из истории не найдены</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">История просмотра также может храниться в вашем аккаунте Google: <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />.</translation>
<translation id="7029809446516969842">Пароли</translation>
@@ -721,7 +745,9 @@
<translation id="7129409597930077180">Невозможно отправить заказ по этому адресу. Выберите другой вариант.</translation>
<translation id="7138472120740807366">Способ доставки</translation>
<translation id="7139724024395191329">Эмират</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> способ оплаты}one{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> способ оплаты}few{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> способа оплаты}many{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> способов оплаты}other{<ph name="PAYMENT_METHOD_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> способа оплаты}}</translation>
<translation id="7155487117670177674">Небезопасная оплата</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> вариант доставки}one{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> вариант доставки}few{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> варианта доставки}many{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> вариантов доставки}other{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> варианта доставки}}</translation>
<translation id="7179921470347911571">Перезапустить</translation>
<translation id="7180611975245234373">Обновить</translation>
<translation id="7182878459783632708">Правила не заданы</translation>
@@ -762,6 +788,7 @@
<translation id="7514365320538308">Скачать</translation>
<translation id="7518003948725431193">Не найдена страница для веб-адреса <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Подключение к сайту не защищено</translation>
<translation id="7535087603100972091">Значение</translation>
<translation id="7537536606612762813">Обязательная</translation>
<translation id="7542403920425041731">После этого данные вашей карты будут переданы сайту.</translation>
@@ -771,7 +798,6 @@
<translation id="7552846755917812628">Попробуйте сделать следующее:</translation>
<translation id="7554791636758816595">Новая вкладка</translation>
<translation id="7567204685887185387">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Его сертификат безопасности мог быть выдан обманным путем. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}many{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Язык этой страницы<ph name="ORIGINAL_LANGUAGE" />Хотите перевести ее?</translation>
<translation id="7569952961197462199">Удалить кредитную карту из Chrome?</translation>
<translation id="7569983096843329377">Черный</translation>
@@ -798,9 +824,11 @@
<translation id="7714464543167945231">Сертификат</translation>
<translation id="7716147886133743102">Заблокировано администратором</translation>
<translation id="7716424297397655342">Не удалось загрузить сайт из кеша</translation>
+<translation id="774634243536837715">Опасный контент заблокирован</translation>
<translation id="7752995774971033316">Не управляется</translation>
<translation id="7755287808199759310">Для разблокировки обратитесь к родителю.</translation>
<translation id="7758069387465995638">Возможно, подключение заблокировано брандмауэром или антивирусным ПО.</translation>
+<translation id="7759163816903619567">Показывать домен:</translation>
<translation id="7761701407923456692">Сертификат сервера не соответствует URL.</translation>
<translation id="7763386264682878361">Синтаксический анализатор манифестов</translation>
<translation id="7764225426217299476">Добавить адрес</translation>
@@ -815,6 +843,7 @@
<translation id="7815407501681723534"><ph name="SEARCH_RESULTS" /> по запросу "<ph name="SEARCH_STRING" />" (<ph name="NUMBER_OF_RESULTS" />)</translation>
<translation id="785549533363645510">Тем не менее ваши действия будут видны системному администратору и интернет-провайдеру, а также доступны веб-сайтам, которые вы посещаете.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Принимаются дебетовые карты и карты предоплаты.</translation>
<translation id="7887683347370398519">Проверьте CVC-код и повторите попытку</translation>
<translation id="79338296614623784">Укажите действительный номер телефона.</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -834,7 +863,6 @@
<translation id="8041089156583427627">Отправить отзыв</translation>
<translation id="8041940743680923270">Использовать глобальный параметр по умолчанию (спрашивать)</translation>
<translation id="8088680233425245692">Не удалось показать статью</translation>
-<translation id="8089520772729574115">менее 1 МБ</translation>
<translation id="8091372947890762290">Активация управления устройствами не завершена</translation>
<translation id="8118489163946903409">Способ оплаты</translation>
<translation id="8131740175452115882">Подтвердить</translation>
@@ -846,10 +874,13 @@
<translation id="8194797478851900357">&amp;Отменить перемещение</translation>
<translation id="8201077131113104583">Недействительный URL для обновления расширения с идентификатором <ph name="EXTENSION_ID" />.</translation>
<translation id="8202097416529803614">Информация о заказе</translation>
+<translation id="8205463626947051446">На сайте может быть много навязчивой рекламы</translation>
<translation id="8218327578424803826">Назначенное местоположение:</translation>
<translation id="8225771182978767009">Тот, кто настраивал компьютер, заблокировал этот сайт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" /> и <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Открыть страницу в новой вкладке инкогнито</translation>
<translation id="8241707690549784388">На странице, которую вы ищете, использовалась введенная вами информация. При возврате на эту страницу может потребоваться повторить выполненные ранее действия. Продолжить?</translation>
+<translation id="8241712895048303527">Блокировать на этом сайте</translation>
<translation id="8249320324621329438">Время последней загрузки:</translation>
<translation id="8253091569723639551">Необходимо указать платежный адрес</translation>
<translation id="8261506727792406068">Удалить</translation>
@@ -860,7 +891,6 @@
<translation id="8308427013383895095">Перевод не завершен из-за проблем с сетевым подключением.</translation>
<translation id="8332188693563227489">Доступ к <ph name="HOST_NAME" /> запрещен</translation>
<translation id="834457929814110454">Если вы готовы подвергнуть риску ваши личные данные, вы можете <ph name="BEGIN_LINK" />перейти на зараженный сайт<ph name="END_LINK" />, не дожидаясь удаления вредоносного ПО.</translation>
-<translation id="8344669043927012510">Откройте страницу в режиме инкогнито (⇧ + ⌘ + N).</translation>
<translation id="8349305172487531364">Панель закладок</translation>
<translation id="8363502534493474904">Отключите режим полета.</translation>
<translation id="8364627913115013041">Не задано</translation>
@@ -870,6 +900,7 @@
<translation id="8398259832188219207"><ph name="UPLOAD_TIME" />: отчет о сбоях загружен.</translation>
<translation id="8412145213513410671">Сбои (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Необходимо дважды ввести одну и ту же кодовую фразу.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Настройки</translation>
<translation id="8433057134996913067">На большинстве сайтов будет выполнен выход из аккаунта.</translation>
<translation id="8437238597147034694">&amp;Отменить перемещение</translation>
@@ -877,14 +908,16 @@
<translation id="8483780878231876732">Чтобы использовать карты, привязанные к аккаунту Google Account, войдите в Chrome</translation>
<translation id="8488350697529856933">Объект применения</translation>
<translation id="8498891568109133222">Превышено время ожидания ответа от сайта <ph name="HOST_NAME" />.</translation>
-<translation id="8532105204136943229">Год</translation>
+<translation id="8503813439785031346">Имя пользователя</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="8571890674111243710">Перевод страницы на <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Добавьте номер</translation>
<translation id="859285277496340001">Этот сертификат не определяет механизм проверки отзыва.</translation>
<translation id="8620436878122366504">Ещё не одобрено родителями</translation>
<translation id="8647750283161643317">Восстановить настройки по умолчанию</translation>
+<translation id="8660471606262461360">Из Google Payments</translation>
<translation id="8703575177326907206">Соединение с <ph name="DOMAIN" /> не зашифровано.</translation>
<translation id="8718314106902482036">Не удалось обработать платеж</translation>
<translation id="8725066075913043281">Повторить попытку</translation>
@@ -912,6 +945,7 @@
<translation id="8903921497873541725">Увеличить</translation>
<translation id="8931333241327730545">Сохранить эту карту в аккаунте Google?</translation>
<translation id="8932102934695377596">Часы отстают</translation>
+<translation id="8938939909778640821">Кредитные карты и карты предоплаты, которые принимаются к оплате</translation>
<translation id="8971063699422889582">Сертификат сервера устарел.</translation>
<translation id="8986494364107987395">Автоматически отправлять в Google статистику использования и отчеты о сбоях</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -941,7 +975,6 @@
<translation id="9169664750068251925">Всегда блокировать на этом сайте</translation>
<translation id="9170848237812810038">&amp;Отменить</translation>
<translation id="917450738466192189">Сертификат сервера недействителен</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}many{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> и ещё <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="9183425211371246419">На сайте <ph name="HOST_NAME" /> используется неподдерживаемый протокол.</translation>
<translation id="9205078245616868884">Данные зашифрованы с помощью кодовой фразы. Введите ее, чтобы начать синхронизацию.</translation>
<translation id="9207861905230894330">Не удалось добавить статью</translation>
@@ -950,9 +983,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ОЧИСТИТЬ ФОРМУ</translation>
<translation id="939736085109172342">Новая папка</translation>
-<translation id="941721044073577244">Для доступа к этому сайту требуется разрешение</translation>
<translation id="969892804517981540">Официальная сборка</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Нет}=1{1 запись}one{# запись}few{# записи}many{# записей}other{# записи}}</translation>
+<translation id="981121421437150478">Офлайн</translation>
<translation id="988159990683914416">Сборка для разработчиков</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_sk.xtb b/chromium/components/strings/components_strings_sk.xtb
index 91e13881d3c..c2b871d13c3 100644
--- a/chromium/components/strings/components_strings_sk.xtb
+++ b/chromium/components/strings/components_strings_sk.xtb
@@ -9,7 +9,8 @@
<translation id="1050038467049342496">Zavrite ostatné aplikácie</translation>
<translation id="1055184225775184556">&amp;Vrátiť späť pridanie</translation>
<translation id="10614374240317010">Neuložené</translation>
-<translation id="106701514854093668">Záložky plochy</translation>
+<translation id="1066396345355680611">Môžete stratiť prístup ku chránenému obsahu z webu <ph name="SITE" /> a niektorých ďalších webov.</translation>
+<translation id="106701514854093668">Záložky v počítači</translation>
<translation id="1074497978438210769">Nezabezpečené</translation>
<translation id="1080116354587839789">Prispôsobiť šírke</translation>
<translation id="1103523840287552314">Vždy preložiť nasledujúci jazyk: <ph name="LANGUAGE" /></translation>
@@ -24,7 +25,7 @@
<translation id="1151972924205500581">Vyžaduje sa heslo</translation>
<translation id="1152921474424827756">Prístup ku <ph name="BEGIN_LINK" />kópii vo vyrovnávacej pamäti<ph name="END_LINK" /> stránky <ph name="URL" /></translation>
<translation id="1158211211994409885">Hostiteľský web <ph name="HOST_NAME" /> neočakávane ukončil pripojenie.</translation>
-<translation id="1161325031994447685">Znovu sa pripojiť k sieti Wi-Fi</translation>
+<translation id="1161325031994447685">Znovu sa pripojiť k sieti Wi‑Fi</translation>
<translation id="1165039591588034296">Chyba</translation>
<translation id="1175364870820465910">&amp;Tlačiť...</translation>
<translation id="1181037720776840403">Odstrániť</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Čitateľský zoznam</translation>
<translation id="1264126396475825575">Správa o zlyhaní bola zaznamenaná v čase <ph name="CRASH_TIME" /> (ešte nebola nahraná ani ignorovaná)</translation>
<translation id="1281526147609854549">Vydavateľ: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Zablokovaný nebezpečný obsah</translation>
<translation id="1285320974508926690">Nikdy neprekladať tieto webové stránky</translation>
<translation id="129553762522093515">Naposledy zatvorené</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Skúste vymazať súbory cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Doména registrácie:</translation>
<translation id="1340482604681802745">Adresa vyzdvihnutia</translation>
-<translation id="1344211575059133124">Zdá sa, že prístup na tento web si vyžaduje povolenie</translation>
<translation id="1344588688991793829">Nastavenia Automatického dopĺňania prehliadača Chromium...</translation>
<translation id="1348198688976932919">Web, ktorý chcete otvoriť, obsahuje nebezpečné aplikácie</translation>
<translation id="1374468813861204354">návrhy</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identita spoločnosti <ph name="ORGANIZATION" /> so sídlom <ph name="LOCALITY" /> bola overená spoločnosťou <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Áno</translation>
<translation id="1430915738399379752">Tlačiť</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalší}few{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalšie}many{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalšieho}other{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalších}}</translation>
<translation id="1506687042165942984">Zobrazí sa uložená (t. j. neaktuálna) kópia tejto stránky.</translation>
<translation id="1517433312004943670">Vyžaduje sa telefónne číslo</translation>
+<translation id="1517500485252541695">Akceptované kreditné a debetné karty</translation>
<translation id="1519264250979466059">Dátum zostavenia</translation>
+<translation id="1527263332363067270">Čaká sa na pripojenie...</translation>
<translation id="153384715582417236">To je zatiaľ všetko</translation>
<translation id="1549470594296187301">Ak chcete použiť túto funkciu, musíte povoliť JavaScript.</translation>
<translation id="1555130319947370107">Modrá</translation>
@@ -90,7 +90,7 @@
<translation id="1655462015569774233">{1,plural, =1{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; platnosť jej bezpečnostného certifikátu vypršala včera. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom. Hodiny vášho počítača sú momentálne nastavené na <ph name="CURRENT_DATE" />. Je tento čas správny? Ak nie, opravte čas na hodinách systému a potom obnovte túto stránku.}few{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; platnosť jej bezpečnostného certifikátu vypršala pred # dňami. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom. Hodiny vášho počítača sú momentálne nastavené na <ph name="CURRENT_DATE" />. Je tento čas správny? Ak nie, opravte čas na hodinách systému a potom obnovte túto stránku.}many{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; platnosť jej bezpečnostného certifikátu vypršala pred # dňom. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom. Hodiny vášho počítača sú momentálne nastavené na <ph name="CURRENT_DATE" />. Je tento čas správny? Ak nie, opravte čas na hodinách systému a potom obnovte túto stránku.}other{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; platnosť jej bezpečnostného certifikátu vypršala pred # dňami. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom. Hodiny vášho počítača sú momentálne nastavené na <ph name="CURRENT_DATE" />. Je tento čas správny? Ak nie, opravte čas na hodinách systému a potom obnovte túto stránku.}}</translation>
<translation id="1656489000284462475">Vyzdvihnutie</translation>
<translation id="1663943134801823270">Karty a adresy pochádzajú z Chromu. Môžete ich spravovať v <ph name="BEGIN_LINK" />Nastaveniach<ph name="END_LINK" />.</translation>
-<translation id="1676269943528358898">Web <ph name="SITE" /> zvyčajne chráni vaše informácie pomocou šifrovania. Keď sa Chrome 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 Chrome zastavil pripojenie ešte pred výmenou dát.</translation>
+<translation id="1676269943528358898">Web <ph name="SITE" /> zvyčajne chráni vaše informácie pomocou šifrovania. Keď sa Chrome 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 Chrome zastavil pripojenie ešte pred výmenou dát.</translation>
<translation id="168841957122794586">Certifikát servera obsahuje slabý kryptografický kľúč.</translation>
<translation id="1706954506755087368">{1,plural, =1{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; jej certifikát by mal začať platiť od zajtra. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom.}few{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; jej certifikát by mal začať platiť o # dni. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom.}many{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; jej certifikát by mal začať platiť o # dňa. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom.}other{Tomuto serveru sa nepodarilo dokázať, že ide o doménu <ph name="DOMAIN" />; jej certifikát by mal začať platiť o # dní. Môže to byť následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útočníkom.}}</translation>
<translation id="1710259589646384581">OS</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Stiahnuť stránku neskôr</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Povinné pole</translation>
<translation id="187918866476621466">Otvoriť stránky pri spustení</translation>
<translation id="1883255238294161206">Zbaliť zoznam</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalšia}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalšie}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalšej}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalších}}</translation>
<translation id="1898423065542865115">Filtrovanie</translation>
+<translation id="1916770123977586577">Ak chcete na webe uplatniť aktualizované nastavenia, znova načítajte túto stránku</translation>
+<translation id="1919345977826869612">Reklamy</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Žiadne}=1{1 web}few{# weby}many{# webu}other{# webov}}</translation>
<translation id="194030505837763158">Prejdite na stránku <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Akceptované predplatené karty</translation>
<translation id="1962204205936693436">Záložky (<ph name="DOMAIN" />)</translation>
<translation id="1973335181906896915">Chyba serializácie</translation>
<translation id="1974060860693918893">Rozšírené</translation>
@@ -165,12 +166,13 @@
<translation id="2262243747453050782">Chyba protokolu HTTP</translation>
<translation id="2270484714375784793">Telefónne číslo</translation>
<translation id="2282872951544483773">Nedostupné experimenty</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> položka}few{<ph name="ITEM_COUNT" /> položky}many{<ph name="ITEM_COUNT" /> položky}other{<ph name="ITEM_COUNT" /> položiek}}</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>
<translation id="2337852623177822836">Nastavenie ovládané správcom</translation>
-<translation id="2354001756790975382">Iné záložky</translation>
+<translation id="2354001756790975382">Ostatné</translation>
<translation id="2354430244986887761">Funkcia Bezpečné prehliadanie Google nedávno <ph name="BEGIN_LINK" />našla škodlivé aplikácie<ph name="END_LINK" /> na webe <ph name="SITE" />.</translation>
<translation id="2355395290879513365">Útočníci môžu vidieť obrázky, ktoré si prehliadate na tomto webe, a môžu vás napadnúť tým, že ich podvodným spôsobom upravia.</translation>
<translation id="2356070529366658676">Opýtať sa</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Pokračovať</translation>
<translation id="2365563543831475020">Správa o zlyhaní zaznamenaná v čase <ph name="CRASH_TIME" /> nebola nahraná</translation>
<translation id="2367567093518048410">Úroveň</translation>
-<translation id="237718015863234333">Nie sú k dispozícii žiadne alternatívy používateľského rozhrania</translation>
<translation id="2384307209577226199">Predvolené nastavenie na podnikovej úrovni</translation>
<translation id="2386255080630008482">Certifikát servera bol zrušený.</translation>
<translation id="2392959068659972793">Zobraziť pravidlá bez nastavenej hodnoty</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Ak je toto nastavenie začiarknuté, Chromium uloží na tomto zariadení kópiu karty, aby ste mohli rýchlejšie vypĺňať formuláre.</translation>
<translation id="2498091847651709837">Naskenovať novú kartu</translation>
<translation id="2501278716633472235">Prejsť späť</translation>
+<translation id="2503184589641749290">Akceptované debetné a predplatené karty</translation>
<translation id="2515629240566999685">Skontrolovať signál vo vašej oblasti</translation>
-<translation id="2516305470678292029">Alternatívy používateľského rozhrania</translation>
<translation id="2539524384386349900">Rozpoznávať</translation>
<translation id="255002559098805027">Web <ph name="HOST_NAME" /> odoslal neplatnú odpoveď.</translation>
<translation id="2556876185419854533">&amp;Vrátiť späť úpravu</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Spôsob dodania</translation>
<translation id="277499241957683684">Chýbajúci záznam zariadenia</translation>
<translation id="2784949926578158345">Spojenie bolo obnovené.</translation>
+<translation id="2788784517760473862">Akceptované kreditné karty</translation>
<translation id="2794233252405721443">Web je blokovaný</translation>
<translation id="2799020568854403057">Web, ktorý chcete otvoriť, obsahuje škodlivé aplikácie</translation>
<translation id="2803306138276472711">Funkcia Bezpečné prehliadanie Google nedávno <ph name="BEGIN_LINK" />zistila malvér<ph name="END_LINK" /> na stránkach <ph name="SITE" />. Webové stránky, ktoré sú zvyčajne bezpečné, môžu byť niekedy nakazené malvérom.</translation>
<translation id="2824775600643448204">Panel s adresou a vyhľadávací panel</translation>
<translation id="2826760142808435982">Pripojenie je šifrované pomocou štandardu <ph name="CIPHER" /> a používa mechanizmus výmeny kľúčov <ph name="KX" />.</translation>
<translation id="2835170189407361413">Vymazať formulár</translation>
+<translation id="2851634818064021665">Na návštevu tohto webu potrebujete povolenie</translation>
<translation id="2856444702002559011">Útočníci sa môžu pokúsiť ukradnúť vaše informácie z webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (napríklad heslá, správy alebo kreditné karty). <ph name="BEGIN_LEARN_MORE_LINK" />Ďalšie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Nenačítať znova</translation>
-<translation id="2900469785430194048">Pri pokuse o zobrazenie tejto webovej stránky došlo miesto v pamäti prehliadača Google Chrome.</translation>
<translation id="2909946352844186028">Zistila sa zmena siete.</translation>
<translation id="2916038427272391327">Zavrite ostatné programy</translation>
<translation id="2922350208395188000">Certifikát servera sa nedá overiť.</translation>
<translation id="2928905813689894207">Fakturačná adresa</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalšia}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalšie}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalšej}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> ďalších}}</translation>
<translation id="2941952326391522266">Server nedokáže overiť, či ide o doménu <ph name="DOMAIN" />, jej bezpečnostný certifikát pochádza z domény <ph name="DOMAIN2" />. Môže to byť spôsobené nesprávnou konfiguráciou alebo tým, že vaše pripojenie zachytil útočník.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Ak túto možnosť povolíte, Chromium uloží na tomto zariadení kópiu karty, aby ste mohli rýchlejšie vypĺňať formuláre.</translation>
<translation id="2985398929374701810">Zadajte platnú adresu</translation>
<translation id="2986368408720340940">Tento spôsob vyzdvihnutia nie je k dispozícii. Skúste iný spôsob.</translation>
@@ -277,9 +281,7 @@
<translation id="3167968892399408617">Keď zavriete všetky karty inkognito, po stránkach, ktoré ste na nich zobrazili, nezostane v histórii prehliadania, úložisku súborov cookie a histórii vyhľadávania ani stopa. Všetky stiahnuté súbory a vytvorené záložky zostanú zachované.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Ostrov</translation>
-<translation id="317583078218509884">Nové nastavenia povolení webových stránok sa prejavia po opätovnom načítaní stránky.</translation>
<translation id="3176929007561373547">Skontrolujte nastavenia proxy servera alebo kontaktujte správcu siete a požiadajte ho, aby skontroloval, či proxy server funguje. Ak sa domnievate, že by ste nemali používať proxy server: <ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Otvorte stránku v režime inkognito</translation>
<translation id="320323717674993345">Zrušiť platbu</translation>
<translation id="3207960819495026254">Pridané medzi záložky</translation>
<translation id="3225919329040284222">Server sa preukázal certifikátom, ktorý nezodpovedá integrovaným očakávaniam. Tieto očakávania sú kvôli vašej ochrane zahrnuté pri určitých webových stránkach s vysokou úrovňou zabezpečenia.</translation>
@@ -287,11 +289,12 @@
<translation id="3227137524299004712">Mikrofón</translation>
<translation id="3228969707346345236">Prekladanie zlyhalo, pretože stránka už je v jazyku: <ph name="LANGUAGE" /></translation>
<translation id="323107829343500871">Zadajte kód CVC karty <ph name="CREDIT_CARD" /></translation>
-<translation id="3234666976984236645">Vždy na týchto stránkach zisťovať dôležitý obsah</translation>
+<translation id="3234666976984236645">Vždy na tomto webe zisťovať dôležitý obsah</translation>
<translation id="3254409185687681395">Vytvoriť záložku pre túto stránku</translation>
<translation id="3270847123878663523">&amp;Vrátiť späť zmenu poradia</translation>
<translation id="3282497668470633863">Pridanie mena na karte</translation>
<translation id="3286538390144397061">Reštartovať</translation>
+<translation id="3287510313208355388">Stiahnuť po obnovení pripojenia</translation>
<translation id="3303855915957856445">Nenašli sa žiadne výsledky vyhľadávania</translation>
<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>
@@ -324,7 +327,6 @@
<translation id="3452404311384756672">Interval načítania:</translation>
<translation id="3462200631372590220">Skryť rozšírené podrobnosti</translation>
<translation id="3467763166455606212">Meno majiteľa karty je povinný údaj</translation>
-<translation id="3478058380795961209">Mesiac vypršania platnosti</translation>
<translation id="3479539252931486093">Neočakávali ste to? <ph name="BEGIN_LINK" />Dajte nám vedieť<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Teraz nie</translation>
<translation id="3498215018399854026">V tejto chvíli sa nám nepodarilo spojiť s vaším rodičom. Skúste to znova neskôr.</translation>
@@ -340,12 +342,13 @@
&lt;p&gt;Dátum a čas upravte v aplikácii &lt;strong&gt;Nastavenia&lt;/strong&gt; v časti &lt;strong&gt;Všeobecné&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Ďalšie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kreditné a predplatené karty sú akceptované.</translation>
<translation id="3582930987043644930">Pridajte meno</translation>
<translation id="3583757800736429874">&amp;Znova presunúť</translation>
<translation id="3586931643579894722">Skryť podrobnosti</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Zadajte správny dátum vypršania platnosti</translation>
-<translation id="36224234498066874">Vymazať údaje prehliadania...</translation>
+<translation id="36224234498066874">Vymazať dáta prehliadania...</translation>
<translation id="362276910939193118">Zobraziť celú históriu</translation>
<translation id="3623476034248543066">Zobraziť hodnotu</translation>
<translation id="3630155396527302611">Ak je už uvedený ako program s prístupom k sieti, skúste ho odstrániť zo zoznamu a znova ho pridať.</translation>
@@ -354,14 +357,14 @@
<translation id="3655670868607891010">Ak sa vám táto stránka zobrazuje často, skúste použiť tieto stránky <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Verzia</translation>
<translation id="3678029195006412963">Požiadavku sa nepodarilo podpísať</translation>
+<translation id="3678529606614285348">Otvorte stránku v novom okne inkognito (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Správa o zlyhaní zaznamenaná v čase <ph name="CRASH_TIME" /> bola nahraná o <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informácie o certifikáte</translation>
<translation id="3690164694835360974">Prihlásenie nie je zabezpečené</translation>
-<translation id="3693415264595406141">Heslo:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Načítava sa...</translation>
<translation id="3712624925041724820">Vyčerpané licencie</translation>
-<translation id="3714780639079136834">Zapnúť mobilné dáta alebo Wi-Fi</translation>
+<translation id="3714780639079136834">Zapnúť mobilné dáta alebo Wi‑Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Skontrolovať proxy server, bránu firewall a konfiguráciu DNS<ph name="END_LINK" /></translation>
<translation id="3736520371357197498">Ak si uvedomujete bezpečnostné riziko, môžete <ph name="BEGIN_LINK" />tieto nebezpečné stránky navštíviť<ph name="END_LINK" /> ešte skôr, ako budú nebezpečné programy odstránené.</translation>
<translation id="3739623965217189342">Skopírovaný odkaz</translation>
@@ -369,6 +372,7 @@
<translation id="3748148204939282805">Útočníci na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> vás môžu oklamať, aby ste urobili niečo nebezpečné, napríklad nainštalovali softvér alebo odhalili svoje osobné informácie (napríklad heslá, telefónne čísla a kreditné karty). <ph name="BEGIN_LEARN_MORE_LINK" />Ďalšie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Preklad zlyhal v dôsledku chyby servera.</translation>
<translation id="3759461132968374835">Nemáte žiadne nedávno nahlásené zlyhania. Na tejto stránke sa nezobrazujú zlyhania, ktoré nastali pri zakázanej možnosti hlásení zlyhaní.</translation>
+<translation id="3765032636089507299">Stránka Bezpečného prehliadania sa pripravuje.</translation>
<translation id="3778403066972421603">Chcete uložiť túto kartu do svojho účtu Google a tohto zariadenia?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Platnosť vyprší <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -381,8 +385,10 @@
<translation id="3886446263141354045">Vaša žiadosť o prístup na tento web sa odoslala správcovi <ph name="NAME" /></translation>
<translation id="3890664840433101773">Pridanie e-mailu</translation>
<translation id="3901925938762663762">Platnosť karty vypršala</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Identifikátor nahranej správy o zlyhaní: <ph name="CRASH_ID" /> (ID miestneho zlyhania: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Tento server nedokázal potvrdiť, či ide o doménu <ph name="DOMAIN" /> – príslušný bezpečnostný certifikát neuvádza alternatívne názvy predmetu. Môže to byť spôsobené nesprávnou konfiguráciou alebo tým, že vaše pripojenie napadol útočník.</translation>
+<translation id="3949601375789751990">Tu sa zobrazí vaša história prehliadania</translation>
<translation id="3963721102035795474">Režim čítačky</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Žiadne}=1{Z 1 webu }few{Z # webov }many{Z # webu }other{Z # webov }}</translation>
<translation id="397105322502079400">Prebieha výpočet...</translation>
@@ -401,7 +407,7 @@
<translation id="410351446219883937">Automatické prehrávanie</translation>
<translation id="4103763322291513355">Na stránke &lt;strong&gt;chrome://policy&lt;/strong&gt; nájdete zoznam zakázaných webových adries a ďalšie pravidlá vynútené vaším správcom systému.</translation>
<translation id="4115378294792113321">Purpurová</translation>
-<translation id="4116663294526079822">Vždy povoliť na týchto stránkach</translation>
+<translation id="4116663294526079822">Vždy povoliť na tomto webe</translation>
<translation id="4117700440116928470">Rozsah pravidla nie je podporovaný.</translation>
<translation id="4129401438321186435">{COUNT,plural, =1{1 ďalšia}few{# ďalšie}many{# ďalšej}other{# ďalších}}</translation>
<translation id="4130226655945681476">Skontrolovať sieťové káble, modem a smerovač</translation>
@@ -411,6 +417,8 @@
<translation id="4165986682804962316">Nastavenia webu</translation>
<translation id="4169947484918424451">Chcete, aby Chromium uložil túto kartu?</translation>
<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="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>
@@ -434,12 +442,12 @@
<translation id="4356973930735388585">Útočníci na tomto webe sa možno pokúsia nainštalovať na váš počítač nebezpečné programy, ktoré ukradnú alebo odstránia vaše informácie, napríklad fotky, heslá, správy alebo kreditné karty.</translation>
<translation id="4372948949327679948">Očakávaná hodnota <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Pokúsili ste sa o prístup na stránky <ph name="DOMAIN" />, avšak certifikát poskytnutý serverom bol vydavateľom zrušený. Znamená to, že povereniam zabezpečenia, ktoré predložil server, sa celkom nedá dôverovať. Je možné, že komunikujete s útočníkom.</translation>
-<translation id="4381091992796011497">Meno používateľa:</translation>
<translation id="4394049700291259645">Zakázať</translation>
<translation id="4406896451731180161">výsledky vyhľadávania</translation>
<translation id="4424024547088906515">Server nedokáže overiť, či ide o doménu <ph name="DOMAIN" />, Chrome 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="4432688616882109544">Web <ph name="HOST_NAME" /> neakceptoval váš prihlasovací certifikát alebo nebol žiadny poskytnutý.</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="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>
@@ -451,12 +459,14 @@
<translation id="4587425331216688090">Chcete adresu odstrániť z prehliadača Chrome?</translation>
<translation id="4592951414987517459">Vaše pripojenie k doméne <ph name="DOMAIN" /> je šifrované pomocou modernej šifrovacej súpravy.</translation>
<translation id="4594403342090139922">&amp;Vrátiť späť odstránenie</translation>
+<translation id="4611292653554630842">Prihlásiť sa</translation>
<translation id="4619615317237390068">Karty z iných zariadení</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Server nedokáže overiť, či ide o doménu <ph name="DOMAIN" />, jej bezpečnostný certifikát obsahuje chyby. Môže to byť spôsobené nesprávnou konfiguráciou alebo tým, že vaše pripojenie zachytil útočník.</translation>
<translation id="4690462567478992370">Nepoužívať neplatné certifikáty</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Pripojenie bolo prerušené</translation>
+<translation id="471880041731876836">Nemáte povolenie na návštevu tohto webu</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Spustiť nástroj Diagnostika siete systému Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Znova načítať pravidlá</translation>
<translation id="4728558894243024398">Platforma</translation>
@@ -478,14 +488,15 @@
<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="4863764087567530506">Tento obsah sa vás môže podvodom pokúsiť presvedčiť, aby ste si nainštalovali softvér alebo poskytli osobné informácie. <ph name="BEGIN_LINK" />Napriek tomu zobraziť<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Hľadať v histórii</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Vyžaduje sa overenie totožnosti</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{a 1 ďalšia webová stránka}few{a # ďalšie webové stránky}many{a # ďalšej webovej stránky}other{a # ďalších webových stránok}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Táto stránka bola preložená z neznámeho jazyka do jazyka <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Platba</translation>
<translation id="4926049483395192435">Musí byť určená.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Akcie</translation>
<translation id="4958444002117714549">Rozbaliť zoznam</translation>
<translation id="4974590756084640048">Opätovne aktivovať upozornenia</translation>
@@ -501,6 +512,7 @@
<translation id="5045550434625856497">Nesprávne heslo</translation>
<translation id="5056549851600133418">Články pre vás</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Skontrolovať adresu proxy servera<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Môžete stratiť prístup ku chránenému obsahu z niektorých webov.</translation>
<translation id="5087286274860437796">Certifikát servera je momentálne neplatný</translation>
<translation id="5087580092889165836">Pridať kartu</translation>
<translation id="5089810972385038852">Štát</translation>
@@ -508,18 +520,27 @@
<translation id="5095208057601539847">Provincia</translation>
<translation id="5115563688576182185">(64-bitová verzia)</translation>
<translation id="5141240743006678641">Šifrovať synchronizované heslá pomocou poverení Google</translation>
-<translation id="514421653919133810">Otvorte stránku v režime inkognito (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">V odpovedi na pravidlo sa nachádza kód chyby</translation>
+<translation id="5159010409087891077">Otvorte stránku v novom okne inkognito (⇧⌘N)</translation>
<translation id="5171045022955879922">Vyhľadajte alebo zadajte webovú adresu</translation>
<translation id="5172758083709347301">Počítač</translation>
<translation id="5179510805599951267">Text sa nezobrazil v jazyku <ph name="ORIGINAL_LANGUAGE" />? Nahláste túto chybu</translation>
-<translation id="5181140330217080051">Sťahovanie</translation>
<translation id="5190835502935405962">Panel so záložkami</translation>
<translation id="5199729219167945352">Experimenty</translation>
<translation id="5205222826937269299">Meno je povinný údaj</translation>
<translation id="5222812217790122047">E-mailová adresa je povinný údaj</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">Používate Chrome v práci? Firmy môžu spravovať nastavenia prehliadača Chrome pre svojich zamestnancov. Ďalšie informácie</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Postupujte podľa týchto krokov a softvér dočasne zakážte, aby ste mohli prejsť na daný web. Budete potrebovať oprávnenia správcu.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Kliknite na položku <ph name="BEGIN_BOLD" />Štart<ph name="END_BOLD" />, potom vyhľadajte a vyberte možnosť <ph name="BEGIN_BOLD" />Zobraziť miestne služby<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Vyberte položku <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />V časti <ph name="BEGIN_BOLD" />Typ spustenia<ph name="END_BOLD" /> vyberte možnosť <ph name="BEGIN_BOLD" />Zakázané<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />V časti <ph name="BEGIN_BOLD" />Stav služby<ph name="END_BOLD" /> kliknite na možnosť <ph name="BEGIN_BOLD" />Zastaviť<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Kliknite na možnosť <ph name="BEGIN_BOLD" />Použiť<ph name="END_BOLD" />, potom kliknite na <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />.
+ <ph name="LIST_ITEM" />Ďalšie informácie o tom, ako natrvalo odstrániť softvér z počítača, nájdete v <ph name="BEGIN_LEARN_MORE_LINK" />centre pomoci Chrome<ph name="END_LEARN_MORE_LINK" />.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Vaše pripojenie k týmto stránkam nie je súkromné. Režim VR môžete kedykoľvek ukončiť, stačí odpojiť náhlavnú súpravu a stlačiť možnosť Späť.</translation>
<translation id="5299298092464848405">Pri analýze pravidla sa vyskytla chyba</translation>
<translation id="5308689395849655368">Hlásenie zlyhaní je zakázané.</translation>
@@ -536,9 +557,10 @@
<translation id="5439770059721715174">Pri overení schémy sa vyskytla chyba na mieste <ph name="ERROR_PATH" />: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Táto stránka webu <ph name="HOST_NAME" /> sa nenašla</translation>
<translation id="5455374756549232013">Chybná časová pečiatka pravidla</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> z <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Neplatné</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalší}few{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalšie}many{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalšieho}other{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalších}}</translation>
<translation id="5470861586879999274">&amp;Znova upraviť</translation>
+<translation id="5481076368049295676">Tento obsah sa môže pokúsiť nainštalovať v zariadení nebezpečný softvér, ktorý ukradne alebo odstráni vaše informácie. <ph name="BEGIN_LINK" />Napriek tomu zobraziť<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Pridanie platnej adresy</translation>
<translation id="5492298309214877701">Tento web v intranete danej spoločnosti, organizácie či školy má rovnakú webovú adresu ako externý web.
<ph name="LINE_BREAK" />
@@ -550,6 +572,7 @@
<translation id="5540224163453853">Požadovaný článok sa nepodarilo nájsť.</translation>
<translation id="5544037170328430102">Vložená stránka na webe <ph name="SITE" /> hovorí:</translation>
<translation id="5556459405103347317">Obnoviť</translation>
+<translation id="5560088892362098740">Dátum vypršania platnosti</translation>
<translation id="5565735124758917034">Aktívne</translation>
<translation id="5571083550517324815">Vyzdvihnutie na tejto adrese nie je možné. Vyberte inú adresu.</translation>
<translation id="5572851009514199876">Začnite a prihláste sa do Chromu, aby skontroloval, či môžete navštíviť tento web.</translation>
@@ -564,12 +587,13 @@
<translation id="5629630648637658800">Nastavenia pravidla sa nepodarilo načítať</translation>
<translation id="5631439013527180824">Neplatný token správy zariadenia</translation>
<translation id="5633066919399395251">Útočníci na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sa môžu pokúsiť nainštalovať nebezpečné programy vo vašom počítači, pomocou ktorých ukradnú alebo odstránia informácie (napríklad fotky, heslá, správy a kreditné karty). <ph name="BEGIN_LEARN_MORE_LINK" />Ďalšie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Klamlivý obsah bol zablokovaný.</translation>
<translation id="5646376287012673985">Poloha</translation>
<translation id="5659593005791499971">E-mail</translation>
<translation id="5669703222995421982">Ako získať prispôsobený obsah</translation>
<translation id="5675650730144413517">Táto stránka nefunguje</translation>
<translation id="5710435578057952990">Identita tejto webovej stránky nebola overená.</translation>
-<translation id="5713016350996637505">Blokovaný klamlivý obsah</translation>
+<translation id="5719499550583120431">Predplatené karty sú akceptované.</translation>
<translation id="5720705177508910913">Aktuálny používateľ</translation>
<translation id="5732392974455271431">Vaši rodičia ho môžu pre vás odblokovať</translation>
<translation id="5763042198335101085">Zadajte platnú e-mailovú adresu</translation>
@@ -581,15 +605,14 @@
<translation id="5803412860119678065">Chcete vyplniť informácie o karte <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Vaše pripojenie k doméne <ph name="DOMAIN" /> je šifrované pomocou zastaranej šifrovacej súpravy.</translation>
<translation id="5813119285467412249">&amp;Znova pridať</translation>
-<translation id="5814352347845180253">Môžete stratiť prístup k prémiovému obsahu z webu <ph name="SITE" /> a niektorých ďalších webov.</translation>
<translation id="5838278095973806738">Na tomto webe by ste nemali zadávať citlivé informácie (napríklad heslá alebo kreditné karty), pretože by ich mohli ukradnúť útočníci.</translation>
<translation id="5869405914158311789">K tomuto webu sa nedá pripojiť</translation>
<translation id="5869522115854928033">Uložené heslá</translation>
<translation id="5872918882028971132">Návrhy rodiča</translation>
+<translation id="5893752035575986141">Kreditné karty sú akceptované.</translation>
<translation id="5901630391730855834">Žltá</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="5926846154125914413">Môžete stratiť prístup k prémiovému obsahu z niektorých webov.</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="5967867314010545767">Odstrániť z histórie</translation>
<translation id="5975083100439434680">Oddialiť</translation>
@@ -605,6 +628,7 @@
<translation id="6040143037577758943">Zavrieť</translation>
<translation id="6042308850641462728">Viac</translation>
<translation id="6047233362582046994">Ak si uvedomujete bezpečnostné riziko, môžete <ph name="BEGIN_LINK" />tento web navštíviť<ph name="END_LINK" /> ešte skôr, ako budú škodlivé aplikácie odstránené.</translation>
+<translation id="6047927260846328439">Tento obsah sa vás môže podvodom pokúsiť presvedčiť, aby ste si nainštalovali softvér alebo poskytli osobné informácie. <ph name="BEGIN_LINK" />Napriek tomu zobraziť<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Web <ph name="SITE" /> momentálne nemôžete navštíviť, pretože používa pripínanie certifikátov. Chyby siete a útoky sú zvyčajne dočasné, takže by táto stránka mala neskôr pravdepodobne fungovať.</translation>
<translation id="6060685159320643512">Opatrne, tieto experimenty môžu spôsobiť problémy</translation>
<translation id="6080696365213338172">K obsahu ste pristúpili pomocou certifikátu, ktorý poskytol správca. Údaje, ktoré poskytnete doméne <ph name="DOMAIN" /> môžu byť zachytené správcom.</translation>
@@ -614,10 +638,9 @@
<translation id="6151417162996330722">Obdobie platnosti certifikátu servera je príliš dlhé</translation>
<translation id="6157877588268064908">Ak chcete zobraziť spôsoby a požiadavky dodania, vyberte adresu</translation>
<translation id="6158003235852588289">Funkcia Bezpečné prehliadanie Google nedávno zistila na webe <ph name="SITE" /> phishing. Phishingové stránky sa vydávajú za iné weby, aby vás oklamali.</translation>
-<translation id="6165508094623778733">Viac informácií</translation>
+<translation id="6165508094623778733">Ďalšie informácie</translation>
<translation id="6169916984152623906">Teraz môžete prehliadať internet v súkromí a ostatní používatelia tohto zariadenia vašu aktivitu neuvidia. Stiahnuté súbory a záložky však budú uložené.</translation>
<translation id="6177128806592000436">Vaše pripojenie k tomuto webu nie je zabezpečené</translation>
-<translation id="6184817833369986695">(kohorta: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Skontrolujte internetové pripojenie</translation>
<translation id="6218753634732582820">Chcete adresu odstrániť z prehliadača Chromium?</translation>
<translation id="6221345481584921695">Funkcia Bezpečné prehliadanie Google nedávno <ph name="BEGIN_LINK" />zistila malvér<ph name="END_LINK" /> na stránkach <ph name="SITE" />. Webové stránky, ktoré sú zvyčajne bezpečné, môžu byť niekedy nakazené malvérom. Škodlivý obsah pochádza od hostiteľa <ph name="SUBRESOURCE_HOST" />, ktorý je známym distribútorom malvéru.</translation>
@@ -636,13 +659,14 @@
<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>
<translation id="6387478394221739770">Máte záujem o skvelé nové funkcie prehliadača Chrome? Vyskúšajte verziu beta na stránke chrome.com/beta.</translation>
-<translation id="6389758589412724634">Pri pokuse o zobrazenie tejto webovej stránky došlo miesto v pamäti prehliadača Google Chromium.</translation>
+<translation id="6397451950548600259">Softvér vo vašom počítači bráni Chromu bezpečne sa pripojiť k webu</translation>
<translation id="6404511346730675251">Upraviť záložku</translation>
<translation id="6410264514553301377">Zadajte dátum vypršania platnosti a kód CVC karty <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Opýtali ste sa svojho rodiča, či môžete navštíviť tento web</translation>
@@ -657,6 +681,7 @@
<translation id="647261751007945333">Pravidlá zariadenia</translation>
<translation id="6477321094435799029">Chrome na tejto stránke zaznamenal nezvyčajný kód a v záujme ochrany vašich osobných informácií (napr. hesiel, telefónnych čísel a kreditných kariet) ho zablokoval.</translation>
<translation id="6489534406876378309">Spustiť nahrávanie správ o zlyhaní</translation>
+<translation id="6507833130742554667">Kreditné a debetné karty sú akceptované.</translation>
<translation id="6508722015517270189">Reštartujte Chrome</translation>
<translation id="6529602333819889595">&amp;Znova odstrániť</translation>
<translation id="6534179046333460208">Návrhy Fyzického webu</translation>
@@ -665,13 +690,13 @@
<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="657639383826808334">Tento obsah sa môže pokúsiť na vašom zariadení nainštalovať nebezpečný softvér, ktorý ukradne alebo odstráni vaše údaje. <ph name="BEGIN_LINK" />Napriek tomu zobraziť<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">Možnosti šifrovania</translation>
<translation id="662080504995468778">Zostať</translation>
<translation id="6626291197371920147">Pridanie platného čísla karty</translation>
<translation id="6628463337424475685">Vyhľadávanie <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Útočníci na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sa môžu pokúsiť vo vašom počítači Mac nainštalovať nebezpečné programy, 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="6644283850729428850">Toto pravidlo bolo označené ako zastarané.</translation>
+<translation id="6657585470893396449">Heslo</translation>
<translation id="6671697161687535275">Chcete návrh položky formulára odstrániť z prehliadača Chromium?</translation>
<translation id="6685834062052613830">Odhláste sa a dokončite nastavenie</translation>
<translation id="6710213216561001401">Dozadu</translation>
@@ -702,7 +727,6 @@
<translation id="6970216967273061347">Obvod</translation>
<translation id="6973656660372572881">Určené sú pevne dané servery proxy aj skript PAC webovej adresy.</translation>
<translation id="6989763994942163495">Zobraziť rozšírené nastavenia...</translation>
-<translation id="7000990526846637657">V histórii sa nenašli žiadne záznamy</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Váš účet Google môže mať ďalšie formy histórie prehliadania na adrese <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Heslá</translation>
@@ -717,7 +741,9 @@
<translation id="7129409597930077180">Dodanie na túto adresu nie je možné. Vyberte inú adresu.</translation>
<translation id="7138472120740807366">Spôsob doručenia</translation>
<translation id="7139724024395191329">Emirát</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalší}few{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalšie}many{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalšieho}other{<ph name="PAYMENT_METHOD_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> ďalších}}</translation>
<translation id="7155487117670177674">Platba nie je zabezpečená</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalšia}few{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalšie}many{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalšej}other{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalších}}</translation>
<translation id="7179921470347911571">Reštartovať teraz</translation>
<translation id="7180611975245234373">Obnoviť</translation>
<translation id="7182878459783632708">Nie sú nastavené žiadne pravidlá</translation>
@@ -725,7 +751,7 @@
<translation id="7192203810768312527">Uvoľní <ph name="SIZE" />. Niektoré weby sa môžu pri ďalšej návšteve načítať pomalšie.</translation>
<translation id="719464814642662924">Visa</translation>
<translation id="7210863904660874423">Web <ph name="HOST_NAME" /> nespĺňa bezpečnostné štandardy.</translation>
-<translation id="721197778055552897"><ph name="BEGIN_LINK" />Viac informácií<ph name="END_LINK" /> o tomto probléme.</translation>
+<translation id="721197778055552897"><ph name="BEGIN_LINK" />Ďalšie informácie<ph name="END_LINK" /> o tomto probléme.</translation>
<translation id="7219179957768738017">Spojenie používa protokol <ph name="SSL_VERSION" />.</translation>
<translation id="7220786058474068424">Spracováva sa</translation>
<translation id="724691107663265825">Webové stránky, ktoré chcete otvoriť, obsahujú malvér</translation>
@@ -758,6 +784,7 @@
<translation id="7514365320538308">Stiahnuť</translation>
<translation id="7518003948725431193">Na webovej adrese <ph name="URL" /> sa nepodarilo nájsť žiadne webové stránky</translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Vaše pripojenie k tomuto webu nie je súkromné</translation>
<translation id="7535087603100972091">Hodnota</translation>
<translation id="7537536606612762813">Povinné</translation>
<translation id="7542403920425041731">Po potvrdení budú údaje karty zdieľané s týmto webom.</translation>
@@ -767,7 +794,6 @@
<translation id="7552846755917812628">Vyskúšajte nasledujúce tipy:</translation>
<translation id="7554791636758816595">Nová karta</translation>
<translation id="7567204685887185387">Server nedokáže overiť, či ide o doménu <ph name="DOMAIN" />, bol zrejme vydaný falošný bezpečnostný certifikát. Môže to byť spôsobené nesprávnou konfiguráciou alebo tým, že vaše pripojenie zachytil útočník.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalší}few{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalšie}many{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalšieho}other{<ph name="CONTACT_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> ďalších}}</translation>
<translation id="7568593326407688803">Táto stránka je v jazyku<ph name="ORIGINAL_LANGUAGE" />Chceli by ste ju preložiť?</translation>
<translation id="7569952961197462199">Chcete kreditnú kartu odstrániť z prehliadača Chrome?</translation>
<translation id="7569983096843329377">Čierna</translation>
@@ -794,9 +820,11 @@
<translation id="7714464543167945231">Certifikát</translation>
<translation id="7716147886133743102">Blokované správcom</translation>
<translation id="7716424297397655342">Tento web nie je možné načítať z vyrovnávacej pamäte</translation>
+<translation id="774634243536837715">Nebezpečný obsah bol zablokovaný.</translation>
<translation id="7752995774971033316">Nespravované</translation>
<translation id="7755287808199759310">Váš rodič ho môže pre vás odblokovať</translation>
<translation id="7758069387465995638">Pripojenie mohla zablokovať brána firewall alebo antivírusový softvér.</translation>
+<translation id="7759163816903619567">Zobrazená doména:</translation>
<translation id="7761701407923456692">Certifikát servera sa nezhoduje s webovou adresou.</translation>
<translation id="7763386264682878361">Analyzátor manifestov platieb</translation>
<translation id="7764225426217299476">Pridať adresu</translation>
@@ -811,6 +839,7 @@
<translation id="7815407501681723534">Nájdené výsledky (počet: <ph name="NUMBER_OF_RESULTS" />) pre dopyt „<ph name="SEARCH_STRING" />“: <ph name="SEARCH_RESULTS" /></translation>
<translation id="785549533363645510">Nie ste však neviditeľný/-á. Prejdením do režimu inkognito neskryjete svoje prehliadanie pred zamestnávateľom, poskytovateľom internetových služieb ani pred navštívenými webmi.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Debetné a predplatené karty sú akceptované.</translation>
<translation id="7887683347370398519">Skontrolujte svoj kód CVC a skúste to znova</translation>
<translation id="79338296614623784">Zadajte platné telefónne číslo</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -830,7 +859,6 @@
<translation id="8041089156583427627">Odoslať spätnú väzbu</translation>
<translation id="8041940743680923270">Použiť predvolené všeobecné nastavenie (Opýtať sa)</translation>
<translation id="8088680233425245692">Článok sa nepodarilo zobraziť.</translation>
-<translation id="8089520772729574115">menej ako 1 MB</translation>
<translation id="8091372947890762290">Aktivácia čaká na server</translation>
<translation id="8118489163946903409">Spôsob platby</translation>
<translation id="8131740175452115882">Potvrdiť</translation>
@@ -842,10 +870,13 @@
<translation id="8194797478851900357">&amp;Vrátiť späť presunutie</translation>
<translation id="8201077131113104583">Neplatná webová adresa aktualizácie pre rozšírenie s ID <ph name="EXTENSION_ID" />.</translation>
<translation id="8202097416529803614">Súhrn objednávky</translation>
+<translation id="8205463626947051446">Web zvykne zobrazovať obťažujúce reklamy</translation>
<translation id="8218327578424803826">Pridelená poloha:</translation>
<translation id="8225771182978767009">Osoba, ktorá nastavila tento počítač, sa rozhodla daný web blokovať.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Otvorte stránku na novej karte inkognito</translation>
<translation id="8241707690549784388">Vami hľadaná stránka použila informácie, ktoré ste zadali. Návrat na túto stránku môže spôsobiť zopakovanie akcie, ktorú ste vykonali. Chcete pokračovať?</translation>
+<translation id="8241712895048303527">Blokovať na tomto webe</translation>
<translation id="8249320324621329438">Naposledy načítané:</translation>
<translation id="8253091569723639551">Fakturačná adresa je povinná</translation>
<translation id="8261506727792406068">Odstrániť</translation>
@@ -856,7 +887,6 @@
<translation id="8308427013383895095">Preklad zlyhal v dôsledku problému so sieťovým pripojením.</translation>
<translation id="8332188693563227489">Prístup k webu <ph name="HOST_NAME" /> bol zamietnutý</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="8344669043927012510">Otvorte stránku v režime inkognito (⇧⌘N)</translation>
<translation id="8349305172487531364">Panel so záložkami</translation>
<translation id="8363502534493474904">Vypnúť režim v lietadle</translation>
<translation id="8364627913115013041">Nenastavené.</translation>
@@ -866,6 +896,7 @@
<translation id="8398259832188219207">Správa o zlyhaní bola nahraná o <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Zlyhania (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Rovnakú prístupovú frázu musíte zadať dvakrát.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nastavenia</translation>
<translation id="8433057134996913067">Táto možnosť vás odhlási z väčšiny webov.</translation>
<translation id="8437238597147034694">&amp;Vrátiť späť presunutie</translation>
@@ -873,8 +904,9 @@
<translation id="8483780878231876732">Ak chcete používať karty z účtu Google, prihláste sa do Chromu</translation>
<translation id="8488350697529856933">Platí pre</translation>
<translation id="8498891568109133222">Web <ph name="HOST_NAME" /> príliš dlho neodpovedal.</translation>
-<translation id="8532105204136943229">Rok vypršania platnosti</translation>
+<translation id="8503813439785031346">Meno používateľa</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="8571890674111243710">Prebieha preklad stránky do jazyka: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Pridať telefón
@@ -882,6 +914,7 @@
<translation id="859285277496340001">V certifikáte nie je uvedené, akým spôsobom sa má skontrolovať, či certifikát nebol odmietnutý.</translation>
<translation id="8620436878122366504">Vaši rodičia to zatiaľ neschválili</translation>
<translation id="8647750283161643317">Obnoviť predvolené nastavenia všetkých experimentov</translation>
+<translation id="8660471606262461360">Zo služby Google Payments</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="8725066075913043281">Skúsiť znova</translation>
@@ -909,6 +942,7 @@
<translation id="8903921497873541725">Priblížiť</translation>
<translation id="8931333241327730545">Chcete túto kartu uložiť do svojho účtu Google?</translation>
<translation id="8932102934695377596">Vaše hodiny idú pozadu</translation>
+<translation id="8938939909778640821">Akceptované kreditné a predplatené karty</translation>
<translation id="8971063699422889582">Platnosť certifikátu servera vypršala.</translation>
<translation id="8986494364107987395">Automaticky odosielať Googlu štatistiky používania a správy o zlyhaní</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -929,16 +963,15 @@
<translation id="9069693763241529744">Blokované rozšírením</translation>
<translation id="9076283476770535406">Môže zahŕňať obsah pre dospelých</translation>
<translation id="9078964945751709336">Treba zadať ďalšie informácie</translation>
-<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="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="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>
<translation id="9148507642005240123">&amp;Vrátiť späť úpravu</translation>
<translation id="9154194610265714752">Aktualizované</translation>
<translation id="9157595877708044936">Prebieha nastavenie...</translation>
-<translation id="9169664750068251925">Vždy blokovať na týchto stránkach</translation>
+<translation id="9169664750068251925">Vždy blokovať na tomto webe</translation>
<translation id="9170848237812810038">&amp;Naspäť</translation>
<translation id="917450738466192189">Certifikát servera je neplatný.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalšia}few{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalšie}many{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalšej}other{<ph name="SHIPPING_OPTION_PREVIEW" /> a <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ďalších}}</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>
<translation id="9207861905230894330">Článok sa nepodarilo pridať.</translation>
@@ -947,9 +980,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">VYMAZAŤ FORMULÁR</translation>
<translation id="939736085109172342">Nový priečinok</translation>
-<translation id="941721044073577244">Zdá sa, že nemáte povolenie pristupovať na tento web</translation>
<translation id="969892804517981540">oficiálna zostava</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Žiadne}=1{1 položka}few{# položky}many{# položky}other{# položiek}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Zostavenie pre vývojárov</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_sl.xtb b/chromium/components/strings/components_strings_sl.xtb
index 93cb53f22e9..2582c4d7372 100644
--- a/chromium/components/strings/components_strings_sl.xtb
+++ b/chromium/components/strings/components_strings_sl.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Zaprite druge aplikacije</translation>
<translation id="1055184225775184556">&amp;Razveljavi dodajanje</translation>
<translation id="10614374240317010">Nikoli shranjeno</translation>
+<translation id="1066396345355680611">Morda boste izgubili dostop do zaščitene vsebine na spletnem mestu <ph name="SITE" /> in drugih spletnih mestih.</translation>
<translation id="106701514854093668">Zaznamki namizja</translation>
<translation id="1074497978438210769">Ni varno</translation>
<translation id="1080116354587839789">Prilagoditev prikaza širini</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Bralni seznam</translation>
<translation id="1264126396475825575">Poročilo o zrušitvi je bilo zajeto takrat: <ph name="CRASH_TIME" /> (ni še naloženo ali je prezrto)</translation>
<translation id="1281526147609854549">Izdal: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Nevarna vsebina blokirana</translation>
<translation id="1285320974508926690">Nikoli ne prevedi tega spletnega mesta</translation>
<translation id="129553762522093515">Nedavno zaprto</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Poskusite izbrisati piškotke<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Domena za prijavo:</translation>
<translation id="1340482604681802745">Naslov za prevzem</translation>
-<translation id="1344211575059133124">Videti je, da za obisk tega spletnega mesta potrebujete dovoljenje</translation>
<translation id="1344588688991793829">Nastavitve samodejnega izpolnjevanja v Chromiumu …</translation>
<translation id="1348198688976932919">Spletno mesto vsebuje nevarne aplikacije</translation>
<translation id="1374468813861204354">predlogi</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identiteto organizacije <ph name="ORGANIZATION" /> v kraju <ph name="LOCALITY" /> je potrdil izdajatelj <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Da</translation>
<translation id="1430915738399379752">Natisni</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}two{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Prikaži shranjeno (zastarelo) kopijo te strani.</translation>
<translation id="1517433312004943670">Telefonska številka je obvezna</translation>
+<translation id="1517500485252541695">Sprejete kreditne in debetne kartice</translation>
<translation id="1519264250979466059">Datum gradnje</translation>
+<translation id="1527263332363067270">Čakanje na povezavo ...</translation>
<translation id="153384715582417236">To je vse za zdaj</translation>
<translation id="1549470594296187301">Če želite uporabljati to funkcijo, mora biti omogočen JavaScript.</translation>
<translation id="1555130319947370107">Modra</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Prenesi stran pozneje</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Obvezno polje</translation>
<translation id="187918866476621466">Odpiranje strani ob zagonu</translation>
<translation id="1883255238294161206">Strni seznam</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}two{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Filtriranje</translation>
+<translation id="1916770123977586577">Če želite uporabiti posodobljene nastavitve za to spletno mesto, znova naložite to stran</translation>
+<translation id="1919345977826869612">Oglasi</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Brez}=1{1 spletno mesto}one{# spletno mesto}two{# spletni mesti}few{# spletna mesta}other{# spletnih mest}}</translation>
<translation id="194030505837763158">Pojdite na <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Sprejete predplačniške kartice</translation>
<translation id="1962204205936693436">Zaznamki domene <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Napaka pri serializaciji</translation>
<translation id="1974060860693918893">Dodatno</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Napaka HTTP</translation>
<translation id="2270484714375784793">Telefonska številka</translation>
<translation id="2282872951544483773">Preizkusi, ki niso na voljo</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element}one{<ph name="ITEM_COUNT" /> element}two{<ph name="ITEM_COUNT" /> elementa}few{<ph name="ITEM_COUNT" /> elementi}other{<ph name="ITEM_COUNT" /> elementov}}</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>
<translation id="2337852623177822836">Nastavitev nadzira vaš skrbnik</translation>
<translation id="2354001756790975382">Drugi zaznamki</translation>
<translation id="2354430244986887761">Google Varno brskanje je nedavno <ph name="BEGIN_LINK" />našlo škodljive aplikacije<ph name="END_LINK" /> na spletnem mestu <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Nadaljuj</translation>
<translation id="2365563543831475020">Poročilo o zrušitvi, zajeto takrat: <ph name="CRASH_TIME" />, ni bilo naloženo</translation>
<translation id="2367567093518048410">Raven</translation>
-<translation id="237718015863234333">Ni nadomestnih uporabniških vmesnikov</translation>
<translation id="2384307209577226199">Privzeto za podjetja</translation>
<translation id="2386255080630008482">Potrdilo strežnika je bilo preklicano.</translation>
<translation id="2392959068659972793">Pokaži pravilnike brez nastavljene vrednosti</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Če je izbrana ta možnost, bo Chromium shranil kopijo kartice v tej napravi za hitrejše izpolnjevanje obrazcev.</translation>
<translation id="2498091847651709837">Optično branje nove kartice</translation>
<translation id="2501278716633472235">Nazaj</translation>
+<translation id="2503184589641749290">Sprejete debetne in predplačniške kartice</translation>
<translation id="2515629240566999685">preveriti signal na svojem območju</translation>
-<translation id="2516305470678292029">Nadomestni uporabniški vmesniki</translation>
<translation id="2539524384386349900">Zaznava</translation>
<translation id="255002559098805027">Spletno mesto <ph name="HOST_NAME" /> je poslalo neveljaven odgovor.</translation>
<translation id="2556876185419854533">&amp;Razveljavi urejanje</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Način pošiljanja</translation>
<translation id="277499241957683684">Manjka zapis o napravi</translation>
<translation id="2784949926578158345">Povezava je bila obnovljena.</translation>
+<translation id="2788784517760473862">Sprejete kreditne kartice</translation>
<translation id="2794233252405721443">Spletno mesto blokirano</translation>
<translation id="2799020568854403057">Spletno mesto vsebuje škodljive aplikacije</translation>
<translation id="2803306138276472711">Googlova funkcija varnega brskanja je na spletnem mestu <ph name="SITE" /> nedavno <ph name="BEGIN_LINK" />zaznala zlonamerno programsko opremo<ph name="END_LINK" />. Spletna mesta, ki so običajno varna, so včasih okužena z zlonamerno programsko opremo.</translation>
<translation id="2824775600643448204">Naslovna in iskalna vrstica</translation>
<translation id="2826760142808435982">Za šifriranje in preverjanje pristnosti povezave se uporablja <ph name="CIPHER" />, kot mehanizem za izmenjavo ključev pa <ph name="KX" />.</translation>
<translation id="2835170189407361413">Počisti obrazec</translation>
+<translation id="2851634818064021665">Za obisk tega spletnega mesta potrebujete dovoljenje</translation>
<translation id="2856444702002559011">Morda poskušajo napadalci ukrasti vaše podatke s spletnega mesta <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (na primer gesla, sporočila ali podatke kreditnih kartic). <ph name="BEGIN_LEARN_MORE_LINK" />Več o tem<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Ne naloži znova</translation>
-<translation id="2900469785430194048">Google Chromu je med poskusom prikazovanja te spletne strani zmanjkalo pomnilnika.</translation>
<translation id="2909946352844186028">Zaznana je bila sprememba omrežja.</translation>
<translation id="2916038427272391327">Zaprite druge programe</translation>
<translation id="2922350208395188000">Potrdila strežnika ni mogoče preveriti.</translation>
<translation id="2928905813689894207">Naslov za izstavitev računa</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}two{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Strežniku ni uspelo dokazati, da je res <ph name="DOMAIN" />; njegovo varnostno potrdilo je od <ph name="DOMAIN2" />. Razlog za to je lahko napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Če je to omogočeno, Chromium shrani kopijo kartice v tej napravi zaradi hitrejšega izpolnjevanja obrazcev.</translation>
<translation id="2985398929374701810">Vnesite veljaven naslov</translation>
<translation id="2986368408720340940">Ta način prevzema ni na voljo. Poskusite uporabiti drugega.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Strani, ki si jih ogledujete na zavihkih brez beleženja zgodovine, se ne bodo ohranile v zgodovini brskalnika, hrambi piškotkov ali zgodovini iskanja, ko boste zaprli vse zavihke brez beleženja zgodovine. Datoteke, ki jih prenesete, ali zaznamki, ki jih ustvarite, se bodo ohranili.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Otok</translation>
-<translation id="317583078218509884">Nove nastavitve dovoljenj za spletno mesto bodo začele veljati, ko znova naložite stran.</translation>
<translation id="3176929007561373547">Preverite nastavitve strežnika proxy ali se obrnite na skrbnika omrežja in
poskrbite za delovanje strežnika proxy. Če menite, da vam strežnika proxy
ni treba uporabljati:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Odprite stran v načinu brez beleženja zgodovine</translation>
<translation id="320323717674993345">Prekliči plačilo</translation>
<translation id="3207960819495026254">Zaznamovano</translation>
<translation id="3225919329040284222">Strežnik je poslal potrdilo, ki se ne ujema z vgrajenimi pričakovanji. Ta pričakovanja so zaradi vaše varnosti vključena za nekatera strogo zavarovana spletna mesta.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Razveljavi razvrstitev</translation>
<translation id="3282497668470633863">Dodajanje imena na kartico</translation>
<translation id="3286538390144397061">Znova zaženi</translation>
+<translation id="3287510313208355388">Prenesi, ko je povezava</translation>
<translation id="3303855915957856445">Ni rezultatov iskanja</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Interval prejemanja:</translation>
<translation id="3462200631372590220">Skrij podrobnosti</translation>
<translation id="3467763166455606212">Ime imetnika kartice je obvezno</translation>
-<translation id="3478058380795961209">Expiration Month</translation>
<translation id="3479539252931486093">Ali tega niste pričakovali? <ph name="BEGIN_LINK" />Sporočite nam<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Ne zdaj</translation>
<translation id="3498215018399854026">Trenutno ni mogoče vzpostaviti stika s staršem. Poskusi znova pozneje.</translation>
@@ -343,6 +345,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Več o tem<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Sprejema kreditne in predplačniške kartice.</translation>
<translation id="3582930987043644930">Dodajte ime</translation>
<translation id="3583757800736429874">&amp;Uveljavi premik</translation>
<translation id="3586931643579894722">Skrij podrobnosti</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Če se to pogosto pokaže, poskusite <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Različica</translation>
<translation id="3678029195006412963">Zahteve ni bilo mogoče podpisati</translation>
+<translation id="3678529606614285348">Odpiranje strani v novem oknu brez beleženja zgodovine (Ctrl + Shift + N)</translation>
<translation id="3679803492151881375">Poročilo o zrušitvi je bilo zajeto takrat: <ph name="CRASH_TIME" />, naloženo takrat: <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Informacije o potrdilu</translation>
<translation id="3690164694835360974">Prijava ni varna</translation>
-<translation id="3693415264595406141">Geslo:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Nalaganje ...</translation>
<translation id="3712624925041724820">Ni dovolj licenc</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Napadalci na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> vas lahko z zavajanjem morda pripravijo do tega, da storite kaj nevarnega – denimo, da namestite programsko opremo ali razkrijete osebne podatke (na primer gesla, telefonske številke ali podatke kreditnih kartic). <ph name="BEGIN_LEARN_MORE_LINK" />Več o tem<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Prevajanje ni uspelo zaradi napake strežnika.</translation>
<translation id="3759461132968374835">Nimate nedavnih poročil o zrušitvah. Zrušitve, do katerih je prišlo, ko je bilo poročanje onemogočeno, ne bodo prikazane zukaj.</translation>
+<translation id="3765032636089507299">Stran varnega brskanja je v izdelavi.</translation>
<translation id="3778403066972421603">Ali želite shraniti to kartico v Google Račun in v tej napravi?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Poteče: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Vaša zahteva za dostop do tega mesta je poslana uporabniku <ph name="NAME" /></translation>
<translation id="3890664840433101773">Dodajanje e-poštnega naslova</translation>
<translation id="3901925938762663762">Kartica je potekla</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> – <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID naloženega poročila o zrušitvah <ph name="CRASH_ID" /> (ID lokalne zrušitve: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; njegovo varnostno potrdilo ne določa nadomestnih imen SAN (Subject Alternative Name). Razlog za to je morda napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation>
+<translation id="3949601375789751990">Tu je prikazana zgodovina brskanja</translation>
<translation id="3963721102035795474">Način bralnika</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Brez}=1{1 spletno mesto }one{# spletno mesto }two{# spletni mesti }few{# spletna mesta }other{# spletnih mest }}</translation>
<translation id="397105322502079400">Izračunavanje …</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Nastavitve spletnega mesta</translation>
<translation id="4169947484918424451">Ali želite, da Chromium shrani to kartico?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Napadalci na tem spletnem mestu 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).</translation>
<translation id="4372948949327679948">Pričakovana vrednost je vrste <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Poskusili ste dostopati do domene <ph name="DOMAIN" />, vendar je izdajatelj preklical potrdilo, ki ga je poslal strežnik. To pomeni, da varnostnim poverilnicam, ki jih je poslal strežnik, nikakor ne smete zaupati. Morda komunicirate z napadalcem.</translation>
-<translation id="4381091992796011497">Uporabniško ime:</translation>
<translation id="4394049700291259645">Onemogoči</translation>
<translation id="4406896451731180161">rezultati iskanja</translation>
<translation id="4424024547088906515">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; Chrome ne zaupa njegovemu varnostnemu potrdilu. Razlog za to je lahko napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation>
<translation id="4432688616882109544">Spletno mesto <ph name="HOST_NAME" /> ni sprejelo potrdila za prijavo ali pa to ni bilo posredovano.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Želite odstraniti naslov iz Chroma?</translation>
<translation id="4592951414987517459">Povezava z domeno <ph name="DOMAIN" /> je šifrirana s sodobno šifrirno zbirko.</translation>
<translation id="4594403342090139922">&amp;Razveljavi izbris</translation>
+<translation id="4611292653554630842">Prijava</translation>
<translation id="4619615317237390068">Zavihki iz drugih naprav</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; njegovo varnostno potrdilo vsebuje napake. Razlog za to je lahko napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation>
<translation id="4690462567478992370">Prenehaj uporabljati neveljavno potrdilo</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Povezava je bila prekinjena</translation>
+<translation id="471880041731876836">Nimate dovoljenja za obisk tega spletnega mesta</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Izvajanje orodja Omrežna diagnostika Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Znova naloži pravilnike</translation>
<translation id="4728558894243024398">Okolje</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Ta vsebina vas morda poskuša zavesti, da namestite programsko opremo ali razkrijete osebne podatke. <ph name="BEGIN_LINK" />Vseeno prikaži<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Zgodovina iskanja</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Zahtevano je preverjanje pristnosti</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{in še 1 spletna stran}one{in še # spletna stran}two{in še # spletni strani}few{in še # spletne strani}other{in še # spletnih strani}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Stran je bila iz neznanega jezika prevedena v jezik »<ph name="LANGUAGE_LANGUAGE" />«</translation>
<translation id="4923459931733593730">Plačilo</translation>
<translation id="4926049483395192435">Vrednost mora biti določena.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Dejanja</translation>
<translation id="4958444002117714549">Razširi seznam</translation>
<translation id="4974590756084640048">Vnovično omogočanje opozoril</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Nepravilno geslo</translation>
<translation id="5056549851600133418">Članki za vas</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />preveriti naslov strežnika proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Morda boste izgubili dostop do zaščitene vsebine na nekaterih spletnih mestih.</translation>
<translation id="5087286274860437796">Potrdilo strežnika trenutno ni veljavno.</translation>
<translation id="5087580092889165836">Dodaj kartico</translation>
<translation id="5089810972385038852">Zvezna država</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provinca</translation>
<translation id="5115563688576182185">(64-bitno)</translation>
<translation id="5141240743006678641">Šifrirajte sinhronizirana gesla s poverilnicami za Google</translation>
-<translation id="514421653919133810">Odprite stran v načinu brez beleženja zgodovine (Ctrl + Shift + N)</translation>
<translation id="5145883236150621069">Koda napake v odzivu pravilnika</translation>
+<translation id="5159010409087891077">Odpiranje strani v novem oknu brez beleženja zgodovine (⇧⌘N)</translation>
<translation id="5171045022955879922">Poiščite ali vnesite URL</translation>
<translation id="5172758083709347301">Računalnik</translation>
<translation id="5179510805599951267">Ni v <ph name="ORIGINAL_LANGUAGE" />? Obvestite nas o tej napaki</translation>
-<translation id="5181140330217080051">Prenos</translation>
<translation id="5190835502935405962">Vrstica z zaznamki</translation>
<translation id="5199729219167945352">Poskusi</translation>
<translation id="5205222826937269299">Ime je obvezno</translation>
<translation id="5222812217790122047">E-poštni naslov je obvezen</translation>
<translation id="5251803541071282808">Oblak</translation>
<translation id="5277279256032773186">Uporabljate Chrome v službi? Podjetja lahko upravljajo nastavitve Chroma za zaposlene. Preberite več o tem.</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Upoštevajte ta navodila, če želite začasno onemogočiti programsko opremo, da se boste lahko povezali v splet. Potrebujete skrbniške pravice.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Kliknite <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" />, nato poiščite in izberite <ph name="BEGIN_BOLD" />»Ogled lokalnih storitev«<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Izberite <ph name="BEGIN_BOLD" />»VisualDiscovery«<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Pri <ph name="BEGIN_BOLD" />Vrsta zagona<ph name="END_BOLD" /> izberite <ph name="BEGIN_BOLD" />Onemogočeno<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Pri <ph name="BEGIN_BOLD" />Stanje storitve<ph name="END_BOLD" /> kliknite <ph name="BEGIN_BOLD" />Ustavi<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Kliknite <ph name="BEGIN_BOLD" />Uporabi<ph name="END_BOLD" />, nato kliknite <ph name="BEGIN_BOLD" />V redu<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Obiščite <ph name="BEGIN_LEARN_MORE_LINK" />center za pomoč za Chrome<ph name="END_LEARN_MORE_LINK" />, če želite izvedeti, kako trajno odstranite programsko opremo iz računalnika.
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Povezava s tem spletnim mestom ni zasebna. Če želite kadar koli zapustiti način navidezne resničnosti, snemite naglavni komplet in pritisnite tipko za nazaj.</translation>
<translation id="5299298092464848405">Napaka pri razčlenjevanju pravilnika</translation>
<translation id="5308689395849655368">Poročanje o zrušitvah je onemogočeno.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Napaka preverjanja sheme pri »<ph name="ERROR_PATH" />«: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Te strani spletnega mesta <ph name="HOST_NAME" /> ni mogoče najti</translation>
<translation id="5455374756549232013">Napačen časovni žig pravilnika</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> od <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Neveljavno</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}two{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Uveljavi urejanje</translation>
+<translation id="5481076368049295676">Ta vsebina morda v napravi poskuša namestiti nevarno programsko opremo, ki ukrade ali izbriše vaše podatke. <ph name="BEGIN_LINK" />Vseeno prikaži<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Dodajanje veljavnega naslova</translation>
<translation id="5492298309214877701">To spletno mesto v intranetu podjetja, organizacije ali šole ima enak URL kot zunanje spletno mesto.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Zahtevanega članka ni bilo mogoče najti.</translation>
<translation id="5544037170328430102">Vdelana stran na spletnem mestu <ph name="SITE" /> sporoča:</translation>
<translation id="5556459405103347317">Ponovno naloži</translation>
+<translation id="5560088892362098740">Datum poteka</translation>
<translation id="5565735124758917034">Aktivno</translation>
<translation id="5571083550517324815">Prevzem na tem naslovu ni mogoč. Izberite drugega.</translation>
<translation id="5572851009514199876">Začnite s prijavo v Chrome, da lahko Chrome preveri, ali vam je dovoljeno dostopati do tega spletnega mesta.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Nastavitev pravilnika ni bilo mogoče naložiti</translation>
<translation id="5631439013527180824">Neveljaven žeton za upravljanje naprave</translation>
<translation id="5633066919399395251">Napadalci na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> lahko poskusijo v vašem računalniku namestiti nevarne programe, ki kradejo ali brišejo podatke (na primer fotografije, gesla, sporočila in podatke kreditnih kartic). <ph name="BEGIN_LEARN_MORE_LINK" />Več o tem<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Zavajajoča vsebina blokirana.</translation>
<translation id="5646376287012673985">Lokacija</translation>
<translation id="5659593005791499971">E-pošta</translation>
<translation id="5669703222995421982">Prilagojena vsebina</translation>
<translation id="5675650730144413517">Ta stran ne deluje</translation>
<translation id="5710435578057952990">Identiteta tega spletnega mesta ni bila potrjena.</translation>
-<translation id="5713016350996637505">Zavajajoča vsebina blokirana</translation>
+<translation id="5719499550583120431">Sprejema predplačniške kartice.</translation>
<translation id="5720705177508910913">Trenutni uporabnik</translation>
<translation id="5732392974455271431">Starši ga lahko odblokirajo</translation>
<translation id="5763042198335101085">Vnesite veljaven e-poštni naslov</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Ali želite izpolniti podatke za kartico <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Povezava z domeno <ph name="DOMAIN" /> je šifrirana z zastarelo šifrirno zbirko.</translation>
<translation id="5813119285467412249">&amp;Uveljavi dodajanje</translation>
-<translation id="5814352347845180253">Morda boste izgubili dostop do plačljive vsebine na spletnem mestu <ph name="SITE" /> in drugih spletnih mestih.</translation>
<translation id="5838278095973806738">Na tem spletnem mestu ne vnašajte občutljivih informacij (npr. gesel ali številk kreditnih kartic), ker jih lahko ukradejo napadalci.</translation>
<translation id="5869405914158311789">Tega spletnega mesta ni mogoče doseči</translation>
<translation id="5869522115854928033">Shranjena gesla</translation>
<translation id="5872918882028971132">Predlogi staršev</translation>
+<translation id="5893752035575986141">Sprejema kreditne kartice.</translation>
<translation id="5901630391730855834">Rumena</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="5926846154125914413">Morda boste izgubili dostop do plačljive vsebine na nekaterih spletnih mestih.</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="5967867314010545767">Odstrani iz zgodovine</translation>
<translation id="5975083100439434680">Pomanjšaj</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Zapri</translation>
<translation id="6042308850641462728">Več</translation>
<translation id="6047233362582046994">Če se zavedate varnostnega tveganja, lahko <ph name="BEGIN_LINK" />obiščete to spletno mesto<ph name="END_LINK" />, preden bodo škodljive aplikacije odstranjene.</translation>
+<translation id="6047927260846328439">Ta vsebina vas morda poskuša zavesti, da namestite programsko opremo ali razkrijete osebne podatke. <ph name="BEGIN_LINK" />Vseeno prikaži<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Spletnega mesta <ph name="SITE" /> trenutno ni mogoče obiskati, ker uporablja pripenjanje potrdil. Napake omrežja in napadi na omrežje so običajno začasni, zato bo ta stran verjetno delovala pozneje.</translation>
<translation id="6060685159320643512">Previdno, ti poskusi lahko škodujejo</translation>
<translation id="6080696365213338172">Do vsebine ste dostopali z geslom, ki ga je zagotovil skrbnik. Podatke, ki jih pošljete v <ph name="DOMAIN" />, lahko prestreže skrbnik.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Več o tem</translation>
<translation id="6169916984152623906">Zdaj je mogoče brskati zasebno in drugi, ki uporabljajo to napravo, ne bodo videli vaše dejavnosti. Prenosi in zaznamki bodo vseeno shranjeni.</translation>
<translation id="6177128806592000436">Povezava s tem spletnim mestom ni zasebna</translation>
-<translation id="6184817833369986695">(kohorta: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Preverite internetno povezavo</translation>
<translation id="6218753634732582820">Želite naslov odstraniti iz Chromiuma?</translation>
<translation id="6221345481584921695">Googlova funkcija varnega brskanja je na spletnem mestu <ph name="BEGIN_LINK" /> nedavno <ph name="END_LINK" />zaznala zlonamerno programsko opremo<ph name="SITE" />. Spletna mesta, ki so običajno varna, so včasih okužena z zlonamerno programsko opremo. Zlonamerno vsebino razširja znani distributer zlonamerne programske opreme, <ph name="SUBRESOURCE_HOST" />.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Vas zanimajo super nove funkcije Chroma? Preskusite naš kanal za različice beta na chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromiumu je med poskusom prikazovanja te spletne strani zmanjkalo pomnilnika.</translation>
+<translation id="6397451950548600259">Programska oprema v računalniku Chromu preprečuje vzpostavitev varne povezave s spletom</translation>
<translation id="6404511346730675251">Uredi zaznamek</translation>
<translation id="6410264514553301377">Vnesite datum poteka veljavnosti in kodo CVC za kartico <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Starša si vprašal(-a), ali smeš obiskati to spletno mesto</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Pravilniki za naprave</translation>
<translation id="6477321094435799029">Chrome je zaznal nenavadno kodo na tej strani in jo zaradi zaščite vaših osebnih podatkov (na primer gesel, telefonskih številk in kreditnih kartic) blokiral.</translation>
<translation id="6489534406876378309">Začetek prenašanja zrušitev v storitev</translation>
+<translation id="6507833130742554667">Sprejema kreditne in debetne kartice.</translation>
<translation id="6508722015517270189">Znova zaženite Chrome</translation>
<translation id="6529602333819889595">&amp;Uveljavi izbris</translation>
<translation id="6534179046333460208">Predlogi za Fizični splet</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Ta vsebina morda v napravi poskuša namestiti nevarno programsko opremo, ki ukrade ali izbriše vaše podatke. <ph name="BEGIN_LINK" />Vseeno prikaži<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Možnosti šifriranja</translation>
<translation id="662080504995468778">Ostani</translation>
<translation id="6626291197371920147">Dodajanje veljavne številke kartice</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Iskanje</translation>
<translation id="6630809736994426279">Napadalci, ki so trenutno na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, lahko poskusijo v vašem računalniku Mac 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="6644283850729428850">Ta pravilnik je zastarel.</translation>
+<translation id="6657585470893396449">Geslo</translation>
<translation id="6671697161687535275">Želite predlog obrazca odstraniti iz Chromiuma?</translation>
<translation id="6685834062052613830">Odjavite se in dokončajte nastavitev</translation>
<translation id="6710213216561001401">Nazaj</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Okraj</translation>
<translation id="6973656660372572881">Določeni so stalni strežniki proxy in URL skripta .pac.</translation>
<translation id="6989763994942163495">Prikaži dodatne nastavitve ...</translation>
-<translation id="7000990526846637657">Ni vnosov v zgodovino</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">V Google Računu so morda druge vrste zgodovine brskanja na <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Gesla</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Pošiljanje na ta naslov ni mogoče. Izberite drugega.</translation>
<translation id="7138472120740807366">Način dostave</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}two{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Plačilo ni varno</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}two{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Znova zaženi</translation>
<translation id="7180611975245234373">Osveži</translation>
<translation id="7182878459783632708">Ni nastavljenih pravilnikov</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Prenos</translation>
<translation id="7518003948725431193">Za ta spletni naslov in bilo mogoče najti nobene spletne strani:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Povezava s tem mestom ni zasebna</translation>
<translation id="7535087603100972091">Vrednost</translation>
<translation id="7537536606612762813">Obvezen</translation>
<translation id="7542403920425041731">Ko potrdite, bodo temu spletnemu mestu razkriti podatki o kartici.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Poskusite te nasvete:</translation>
<translation id="7554791636758816595">Nov zavihek</translation>
<translation id="7567204685887185387">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; njegovo varnostno potrdilo je bilo morda izdano z goljufijo. Razlog za to je lahko napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}two{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Ta stran je v jeziku:<ph name="ORIGINAL_LANGUAGE" />Jo želite prevesti?</translation>
<translation id="7569952961197462199">Želite odstraniti kreditno kartico iz Chroma?</translation>
<translation id="7569983096843329377">Črna</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Potrdilo</translation>
<translation id="7716147886133743102">Blokiral skrbnik</translation>
<translation id="7716424297397655342">Tega spletnega mesta ni mogoče naložiti iz predpomnilnika</translation>
+<translation id="774634243536837715">Nevarna vsebina blokirana.</translation>
<translation id="7752995774971033316">Odstranjen iz uporabe</translation>
<translation id="7755287808199759310">Starš ga lahko odblokira</translation>
<translation id="7758069387465995638">Povezavo je morda blokiral požarni zid ali protivirusni program.</translation>
+<translation id="7759163816903619567">Prikaz domene:</translation>
<translation id="7761701407923456692">Potrdilo strežnika se ne ujema z URL-jem.</translation>
<translation id="7763386264682878361">Razčlenjevalnik manifesta plačil</translation>
<translation id="7764225426217299476">Dodaj naslov</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Za »<ph name="SEARCH_STRING" />« je bilo najdenih toliko <ph name="SEARCH_RESULTS" />: <ph name="NUMBER_OF_RESULTS" />.</translation>
<translation id="785549533363645510">Kljub temu pa niste nevidni. Z uporabo načina brez beleženja zgodovine brskanja ne skrijete pred delodajalcem, ponudnikom internetnih storitev ali spletnimi mesti, ki jih obiščete.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" />: <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Sprejema debetne in kreditne kartice.</translation>
<translation id="7887683347370398519">Preverite CVC in poskusite znova</translation>
<translation id="79338296614623784">Vnesite veljavno telefonsko številko</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Pošlji povratne informacije</translation>
<translation id="8041940743680923270">Uporabi globalno privzeto (Vprašaj)</translation>
<translation id="8088680233425245692">Članka si ni bilo mogoče ogledati.</translation>
-<translation id="8089520772729574115">manj kot 1 MB</translation>
<translation id="8091372947890762290">Čakanje na aktivacijo v strežniku</translation>
<translation id="8118489163946903409">Plačilno sredstvo</translation>
<translation id="8131740175452115882">Potrdi</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Razveljavi premik</translation>
<translation id="8201077131113104583">Neveljaven posodobitveni URL za razširitev z ID-jem »<ph name="EXTENSION_ID" />«.</translation>
<translation id="8202097416529803614">Povzetek naročila</translation>
+<translation id="8205463626947051446">Spletno mesto prikazuje vsiljive oglase</translation>
<translation id="8218327578424803826">Dodeljena lokacija:</translation>
<translation id="8225771182978767009">Oseba, ki je nastavila ta računalnik, je blokirala to spletno mesto.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Odpiranje strani v novem zavihku brez beleženja zgodovine.</translation>
<translation id="8241707690549784388">Stran, ki jo iščete, je uporabila informacije, ki ste jih vnesli. Z vrnitvijo na to stran se bodo morda ponovila vsa vaša dejanja, ki ste jih opravili. Ali želite nadaljevati?</translation>
+<translation id="8241712895048303527">Blokiraj na tem spletnem mestu</translation>
<translation id="8249320324621329438">Nazadnje preneseno:</translation>
<translation id="8253091569723639551">Naslov za izstavitev računa je obvezen</translation>
<translation id="8261506727792406068">Izbriši</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Prevod ni uspel zaradi težave s povezavo omrežja.</translation>
<translation id="8332188693563227489">Dostop do spletnega mesta <ph name="HOST_NAME" /> je bil zavrnjen</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="8344669043927012510">Odprite stran v načinu brez beleženja zgodovine (⇧ ⌘ N)</translation>
<translation id="8349305172487531364">Vrstica z zaznamki</translation>
<translation id="8363502534493474904">izklopiti način za letalo</translation>
<translation id="8364627913115013041">Ni nastavljen.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Poročilo o zrušitvi je bilo naloženo takrat: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Zrušitve (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Dvakrat morate vnesti isto geslo.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nastavitve</translation>
<translation id="8433057134996913067">S tem boste odjavljeni z večine spletnih mest.</translation>
<translation id="8437238597147034694">&amp;Razveljavi premik</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">Če želite uporabljati kartice iz Google Računa, se prijavite v Chrome</translation>
<translation id="8488350697529856933">Velja za</translation>
<translation id="8498891568109133222">Spletno mesto <ph name="HOST_NAME" /> se ni odzvalo v ustreznem času.</translation>
-<translation id="8532105204136943229">Expiration Year</translation>
+<translation id="8503813439785031346">Uporabniško ime</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="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="8620436878122366504">Starši še niso odobrili</translation>
<translation id="8647750283161643317">Ponastavi vse na privzete</translation>
+<translation id="8660471606262461360">Iz storitve Google Payments</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="8725066075913043281">Poskusite znova</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Povečaj</translation>
<translation id="8931333241327730545">Ali želite to kartico shraniti v Google Račun?</translation>
<translation id="8932102934695377596">Ura zaostaja</translation>
+<translation id="8938939909778640821">Sprejete kreditne in predplačniške kartice</translation>
<translation id="8971063699422889582">Potrdilo strežnika je poteklo.</translation>
<translation id="8986494364107987395">Samodejno pošlji statistične podatke o uporabi in poročila o zrušitvah Googlu</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Vedno blokiraj na tem spletnem mestu</translation>
<translation id="9170848237812810038">&amp;Razveljavi</translation>
<translation id="917450738466192189">Potrdilo strežnika ni veljavno.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}two{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> in še <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Članka ni bilo mogoče dodati.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">POČISTI OBRAZEC</translation>
<translation id="939736085109172342">Nova mapa</translation>
-<translation id="941721044073577244">Videti je, da nimate pravice za obisk tega spletnega mesta</translation>
<translation id="969892804517981540">Uradna različica</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Brez}=1{1 element}one{# element}two{# elementa}few{# elementi}other{# elementov}}</translation>
+<translation id="981121421437150478">Brez povezave</translation>
<translation id="988159990683914416">Različica za razvijalce</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_sr.xtb b/chromium/components/strings/components_strings_sr.xtb
index 05ee3f7f3b9..7c4c6831c00 100644
--- a/chromium/components/strings/components_strings_sr.xtb
+++ b/chromium/components/strings/components_strings_sr.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Затворите друге апликације</translation>
<translation id="1055184225775184556">&amp;Опозови додавање</translation>
<translation id="10614374240317010">Никада се не чува</translation>
+<translation id="1066396345355680611">Можете да изгубите приступ заштићеном садржају са <ph name="SITE" /> и неких других сајтова.</translation>
<translation id="106701514854093668">Обележивачи на рачунару</translation>
<translation id="1074497978438210769">Није безбедно</translation>
<translation id="1080116354587839789">Уклопите по ширини</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Листа за читање</translation>
<translation id="1264126396475825575">Извештај о отказивању је снимљен <ph name="CRASH_TIME" /> (још увек није отпремљен или игнорисан)</translation>
<translation id="1281526147609854549">Издавач: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Опасан садржај је блокиран</translation>
<translation id="1285320974508926690">Никад не преводи овај сајт</translation>
<translation id="129553762522093515">Недавно затворено</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Покушајте да обришете колачиће<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Домен уписнице:</translation>
<translation id="1340482604681802745">Адреса преузимања</translation>
-<translation id="1344211575059133124">Изгледа да вам је потребна дозвола да бисте посетили овај сајт</translation>
<translation id="1344588688991793829">Подешавања Chromium аутоматског попуњавања...</translation>
<translation id="1348198688976932919">Сајт који ћете посетити садржи опасне апликације</translation>
<translation id="1374468813861204354">предлози</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Идентитет организације <ph name="ORGANIZATION" /> на локалитету <ph name="LOCALITY" /> је верификовао издавач <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Да</translation>
<translation id="1430915738399379752">Штампај</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Прикажите сачувану копију ове странице (тј. копију за коју се зна да је застарела).</translation>
<translation id="1517433312004943670">Број телефона је обавезан</translation>
+<translation id="1517500485252541695">Платне и кредитне картице које се прихватају</translation>
<translation id="1519264250979466059">Датум верзије</translation>
+<translation id="1527263332363067270">Чека се веза…</translation>
<translation id="153384715582417236">То је све за сада</translation>
<translation id="1549470594296187301">JavaScript мора да буде омогућен да бисте користили ову функцију.</translation>
<translation id="1555130319947370107">Плава</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Покушајте да контактирате администратора система.</translation>
<translation id="1740951997222943430">Унесите важећи месец истека</translation>
-<translation id="1745358365027406341">Преузми страницу касније</translation>
<translation id="17513872634828108">Отворене картице</translation>
<translation id="1753706481035618306">Број странице</translation>
<translation id="1763864636252898013">Овај сервер не може да докаже да је <ph name="DOMAIN" />; оперативни систем уређаја нема поверења у његов безбедносни сертификат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Обавезно поље</translation>
<translation id="187918866476621466">Отвори почетне странице</translation>
<translation id="1883255238294161206">Скупи листу</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Филтрирање</translation>
+<translation id="1916770123977586577">Да бисте применили ажурирана подешавања на овом сајту, учитајте страницу поново</translation>
+<translation id="1919345977826869612">Огласи</translation>
<translation id="192020519938775529">{COUNT,plural, =0{None}=1{1 сајт}one{# сајт}few{# сајта}other{# сајтова}}</translation>
<translation id="194030505837763158">Идите на <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Припејд картице које се прихватају</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> – обележивачи</translation>
<translation id="1973335181906896915">Грешка при серијализацији</translation>
<translation id="1974060860693918893">Напредне опције</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP грешка</translation>
<translation id="2270484714375784793">Број телефона</translation>
<translation id="2282872951544483773">Недоступни експерименти</translation>
-<translation id="2283595600756901917">{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="2292556288342944218">Приступ интернету је блокиран</translation>
<translation id="230155334948463882">Нова картица?</translation>
+<translation id="2316887270356262533">Ослобађа мање од 1 MB. Неки сајтови ће се можда спорије учитавати кад их следећи пут посетите.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> захтева корисничко име и лозинку.</translation>
+<translation id="2317583587496011522">Прихватају се дебитне картице.</translation>
<translation id="2337852623177822836">Подешавање контролише администратор</translation>
<translation id="2354001756790975382">Остали обележивачи</translation>
<translation id="2354430244986887761">Google безбедно прегледање је недавно <ph name="BEGIN_LINK" />открило штетне апликације<ph name="END_LINK" /> на <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Настави</translation>
<translation id="2365563543831475020">Извештај о отказивању снимљен у <ph name="CRASH_TIME" /> није отпремљен</translation>
<translation id="2367567093518048410">Ниво</translation>
-<translation id="237718015863234333">Нема доступних алтернатива корисничког интерфејса</translation>
<translation id="2384307209577226199">Подразумеване смернице за предузеће</translation>
<translation id="2386255080630008482">Сертификат сервера је опозван.</translation>
<translation id="2392959068659972793">Прикажи смернице без подешених вредности</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Ако означите ову опцију, Chromium ће складиштити копију картице на овом уређају ради бржег попуњавања образаца.</translation>
<translation id="2498091847651709837">Скенирајте нову картицу</translation>
<translation id="2501278716633472235">Иди назад</translation>
+<translation id="2503184589641749290">Дебитне и припејд картице које се прихватају</translation>
<translation id="2515629240566999685">да проверите сигнал у својој области</translation>
-<translation id="2516305470678292029">Алтернативе корисничког интерфејса</translation>
<translation id="2539524384386349900">Откриј</translation>
<translation id="255002559098805027">Хост <ph name="HOST_NAME" /> је послао неважећи одговор.</translation>
<translation id="2556876185419854533">&amp;Опозови измену</translation>
@@ -220,7 +221,7 @@
<translation id="2709516037105925701">Аутоматско попуњавање</translation>
<translation id="2712173769900027643">Затражи дозволу</translation>
<translation id="2713444072780614174">Бела</translation>
-<translation id="2720342946869265578">У околини</translation>
+<translation id="2720342946869265578">У близини</translation>
<translation id="2721148159707890343">Захтев је успео</translation>
<translation id="2728127805433021124">Сертификат сервера је потписан слабим алгоритмом.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />да покренете дијагностику везе<ph name="END_LINK" /></translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Начин слања</translation>
<translation id="277499241957683684">Недостаје евиденција уређаја</translation>
<translation id="2784949926578158345">Веза је враћена на почетне вредности.</translation>
+<translation id="2788784517760473862">Кредитне картице које се прихватају</translation>
<translation id="2794233252405721443">Сајт је блокиран</translation>
<translation id="2799020568854403057">Сајт који ћете посетити садржи штетне апликације</translation>
<translation id="2803306138276472711">Google безбедно прегледање је недавно <ph name="BEGIN_LINK" />открило малвер<ph name="END_LINK" /> на <ph name="SITE" />. Веб-сајтови који су обично безбедни се понекад заразе малвером.</translation>
<translation id="2824775600643448204">Трака за адресу и претрагу</translation>
<translation id="2826760142808435982">Веза је шифрована и њена аутентичност је потврђена помоћу <ph name="CIPHER" /> и користи <ph name="KX" /> као механизам за размену шифара.</translation>
<translation id="2835170189407361413">Обриши образац</translation>
+<translation id="2851634818064021665">Треба вам дозвола да бисте посетили овај сајт</translation>
<translation id="2856444702002559011">Нападачи можда покушавају да украду информације са <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (на пример, лозинке, поруке или кредитне картице). <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Не учитавај поново</translation>
-<translation id="2900469785430194048">Google Chrome-у је понестало меморије док је покушавао да прикаже ову веб-страницу.</translation>
<translation id="2909946352844186028">Откривена је промена на мрежи.</translation>
<translation id="2916038427272391327">Затворите друге програме</translation>
<translation id="2922350208395188000">Није могуће проверити сертификат сервера.</translation>
<translation id="2928905813689894207">Адреса за обрачун</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Овај сервер не може да докаже да је <ph name="DOMAIN" />; његов безбедносни сертификат је са домена <ph name="DOMAIN2" />. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
<translation id="2948083400971632585">На страници Подешавања можете да онемогућите све проксије конфигурисане за везу.</translation>
<translation id="2955913368246107853">Затворите траку за проналажење</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Ако омогућите ову опцију, Chromium ће складиштити копију картице на овом уређају ради бржег попуњавања образаца.</translation>
<translation id="2985398929374701810">Унесите важећу адресу</translation>
<translation id="2986368408720340940">Овај начин преузимања није доступан. Испробајте неки други начин.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Странице које прегледате на картицама без архивирања се неће задржавати у историји прегледача, складишту колачића или историји претраге када затворите све картице без архивирања. Сачуваћемо све преузете датотеке или направљене обележиваче.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Острво</translation>
-<translation id="317583078218509884">Нова подешавања дозвола сајта биће примењена након поновног учитавања странице.</translation>
<translation id="3176929007561373547">Проверите подешавања проксија или контактирајте администратора мреже да
бисте се уверили да прокси сервер функционише. Ако мислите да не
треба да користите прокси сервер:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Отворите страницу у режиму без архивирања</translation>
<translation id="320323717674993345">Откажи плаћање</translation>
<translation id="3207960819495026254">Обележено</translation>
<translation id="3225919329040284222">Сервер је приказао сертификат који се не подудара са уграђеним очекивањима. Та очекивања су обухваћена за одређене веб-сајтове са јаким безбедносним мерама како би вас заштитила.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Опозови промену редоследа</translation>
<translation id="3282497668470633863">Додајте име на картици</translation>
<translation id="3286538390144397061">Поново покрени одмах</translation>
+<translation id="3287510313208355388">Преузми када сам онлајн</translation>
<translation id="3303855915957856445">Нису пронађени резултати претраге</translation>
<translation id="3305707030755673451">Подаци су шифровани помоћу приступне фразе за синхронизацију <ph name="TIME" />. Унесите је да бисте започели синхронизацију.</translation>
<translation id="3320021301628644560">Додајте адресу за обрачун</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Интервал учитавања:</translation>
<translation id="3462200631372590220">Сакриј напредно</translation>
<translation id="3467763166455606212">Име власника картице је обавезно</translation>
-<translation id="3478058380795961209">Месец истека</translation>
<translation id="3479539252931486093">Да ли је ово било неочекивано? <ph name="BEGIN_LINK" />Обавестите нас<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Не сада</translation>
<translation id="3498215018399854026">Тренутно не можемо да контактирамо родитеља. Пробај поново.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Прилагодите датум и време у одељку &lt;strong&gt;Опште&lt;/strong&gt; у апликацији &lt;strong&gt;Подешавања&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Прихватају се кредитне и припејд картице.</translation>
<translation id="3582930987043644930">Додајте име</translation>
<translation id="3583757800736429874">&amp;Понови премештање</translation>
<translation id="3586931643579894722">Сакриј детаље</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Ако вам се ово често приказује, можда вам помогну следећи <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Ревизија</translation>
<translation id="3678029195006412963">Потписивање захтева није успело</translation>
+<translation id="3678529606614285348">Отворите страницу у ноцом прозору без архивирања (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Извештај о отказивању је снимљен <ph name="CRASH_TIME" />, а отпремљен <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Информације о сертификату</translation>
<translation id="3690164694835360974">Пријављивање није безбедно</translation>
-<translation id="3693415264595406141">Лозинка:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Учитава се...</translation>
<translation id="3712624925041724820">Нема више лиценци</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Нападачи на <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="375403751935624634">Превођење није успело због грешке сервера.</translation>
<translation id="3759461132968374835">Немате ниједно недавно пријављено отказивање. Отказивања која су се десила док је пријављивање отказивања било онемогућено неће се овде приказати.</translation>
+<translation id="3765032636089507299">Радови на страници Безбедно прегледање су у току.</translation>
<translation id="3778403066972421603">Да ли желите да сачувате ову картицу на Google налогу и на овом уређају?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Истиче <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Захтев за приступ овом сајту је послат кориснику <ph name="NAME" /></translation>
<translation id="3890664840433101773">Додајте имејл</translation>
<translation id="3901925938762663762">Картица је истекла</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ИД извештаја о отказивању је отпремљен <ph name="CRASH_ID" /> (ИД локалног отказивања: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Овај сервер не може да докаже да је <ph name="DOMAIN" />; његов безбедносни сертификат не наводи Алтернативне називе субјекта. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
+<translation id="3949601375789751990">Историја прегледања ће се појавити овде</translation>
<translation id="3963721102035795474">Режим читаоца</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{None}=1{Са 1 сајта }one{Са # сајта }few{Са # сајта }other{Са # сајтова }}</translation>
<translation id="397105322502079400">Израчунавање...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Подешавања сајта</translation>
<translation id="4169947484918424451">Желите ли да Chromium сачува ову картицу?</translation>
<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="4196861286325780578">&amp;Понови премештање</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />да проверите конфигурацију заштитног зида и антивируса<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Отказивања</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Нападачи на овом сајту ће можда покушати да инсталирају опасне програме на рачунару који краду или бришу информације (на пример, слике, лозинке, поруке и бројеве кредитних картица).</translation>
<translation id="4372948949327679948">Очекивана вредност je <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Покушали сте да контактирате <ph name="DOMAIN" />, али је издавач опозвао сертификат који је сервер навео. То значи да никако не треба имати поверења у безбедносне акредитиве које је сервер навео. Могуће је да комуницирате са нападачем.</translation>
-<translation id="4381091992796011497">Име корисника:</translation>
<translation id="4394049700291259645">Онемогући</translation>
<translation id="4406896451731180161">резултати претраге</translation>
<translation id="4424024547088906515">Овај сервер не може да докаже да је <ph name="DOMAIN" />; Chrome нема поверења у његов безбедносни сертификат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> није прихватио сертификат за пријављивање или сертификат за пријављивање није приложен.</translation>
<translation id="443673843213245140">Коришћење проксија је онемогућено, али је наведена експлицитна конфигурација проксија.</translation>
+<translation id="445100540951337728">Дебитне картице које се прихватају</translation>
<translation id="4506176782989081258">Грешка при потврди ваљаности: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">да контактирате администратора система</translation>
<translation id="450710068430902550">Дељење са администратором</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Желите ли да уклоните адресу из Chrome-а?</translation>
<translation id="4592951414987517459">Веза са доменом <ph name="DOMAIN" /> је шифрована помоћу модерног пакета за шифровање.</translation>
<translation id="4594403342090139922">&amp;Опозови брисање</translation>
+<translation id="4611292653554630842">Пријави ме</translation>
<translation id="4619615317237390068">Картице са других уређаја</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Овај сервер не може да докаже да је <ph name="DOMAIN" />; његов безбедносни сертификат садржи грешке. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
<translation id="4690462567478992370">Обустави коришћење неважећег сертификата</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Веза је прекинута</translation>
+<translation id="471880041731876836">Немате дозволу да посетите овај сајт</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />да покренете Windows дијагностику мреже<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Поново учитај смернице</translation>
<translation id="4728558894243024398">Платформа</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">Приказ</translation>
<translation id="4854362297993841467">Овај начин испоруке није доступан. Испробајте неки други начин.</translation>
<translation id="4858792381671956233">Питао/ла си родитеље да ли смеш да посетиш овај сајт</translation>
-<translation id="4863764087567530506">Овај садржај ће покушати да вас превари да инсталирате софтвер или откријете личне податке. <ph name="BEGIN_LINK" />Ипак прикажи<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Претражи историју</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Потврда идентитета је обавезна</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{и јој 1 веб-страница}one{и још # веб-страница}few{и још # веб-странице}other{и још # веб-страница}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Ова страница је преведена са непознатог језика на <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Плаћање</translation>
<translation id="4926049483395192435">Мора да буде наведено.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Радње</translation>
<translation id="4958444002117714549">Прошири листу</translation>
<translation id="4974590756084640048">Поново омогући упозорења</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Неисправна лозинка</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>
<translation id="5087580092889165836">Додај картицу</translation>
<translation id="5089810972385038852">Држава</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Провинција</translation>
<translation id="5115563688576182185">(64-битни)</translation>
<translation id="5141240743006678641">Шифруј синхронизоване лозинке помоћу Google акредитива</translation>
-<translation id="514421653919133810">Отворите страницу у режиму без архивирања (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Кôд грешке је присутан у одговору на смернице</translation>
+<translation id="5159010409087891077">Отворите страницу у новом прозору без архивирања (⇧⌘N)</translation>
<translation id="5171045022955879922">Претражите или унесите URL адресу</translation>
<translation id="5172758083709347301">Рачунар</translation>
<translation id="5179510805599951267">Није <ph name="ORIGINAL_LANGUAGE" />? Пријавите ову грешку</translation>
-<translation id="5181140330217080051">Преузимање</translation>
<translation id="5190835502935405962">Трака са обележивачима</translation>
<translation id="5199729219167945352">Експерименти</translation>
<translation id="5205222826937269299">Име је обавезно</translation>
<translation id="5222812217790122047">Имејл је обавезан</translation>
<translation id="5251803541071282808">Клауд</translation>
<translation id="5277279256032773186">Да ли користите Chrome на послу? Предузеће може да управља подешавањима Chrome-а за запослене. Сазнајте више</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Пратите ове кораке да бисте привремено онемогућили софтвер и приступили вебу. Требаће вам привилегије администратора.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Кликните на <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" /> (Покрени), затим претражите и изаберите <ph name="BEGIN_BOLD" />„View local services“<ph name="END_BOLD" /> (Прикажи локалне услуге)
+ <ph name="LIST_ITEM" />Изаберите <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />У опцији <ph name="BEGIN_BOLD" />Startup type<ph name="END_BOLD" /> (Тип покретања), изаберите <ph name="BEGIN_BOLD" />Disabled<ph name="END_BOLD" /> (Онемогућено)
+ <ph name="LIST_ITEM" />У опцији <ph name="BEGIN_BOLD" />Service status<ph name="END_BOLD" /> (Статус услуге), кликните на <ph name="BEGIN_BOLD" />Stop<ph name="END_BOLD" /> (Заустави)
+ <ph name="LIST_ITEM" />Кликните на <ph name="BEGIN_BOLD" />Apply<ph name="END_BOLD" /> (Примени), па кликните на <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" /> (Потврди)
+ <ph name="LIST_ITEM" />Посетите <ph name="BEGIN_LEARN_MORE_LINK" />Chrome центар за помоћ<ph name="END_LEARN_MORE_LINK" /> да бисте сазнали како да трајно уклоните софтвер са свог рачунара
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Веза са овим сајтом није приватна. Да бисте у било ком тренутку изашли из ВР режима, уклоните хедсет и притисните Назад.</translation>
<translation id="5299298092464848405">Грешка при рашчлањивању смерница</translation>
<translation id="5308689395849655368">Извештавање о отказивању је онемогућено.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Грешка у валидацији шеме на „<ph name="ERROR_PATH" />“: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Није могуће пронаћи ову страницу хоста <ph name="HOST_NAME" /></translation>
<translation id="5455374756549232013">Неисправна временска ознака смерница</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> од <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Неважеће</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Понови измену</translation>
+<translation id="5481076368049295676">Овај садржај ће покушати да инсталира опасан софтвер на уређај који ће украсти или обрисати ваше податке. <ph name="BEGIN_LINK" />Ипак прикажи<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Додајте важећу адресу</translation>
<translation id="5492298309214877701">Овај сајт на интранету компаније, организације или школе има исти URL као и један спољни веб-сајт.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Нисмо пронашли захтевани чланак.</translation>
<translation id="5544037170328430102">Уграђена страница на <ph name="SITE" /> каже:</translation>
<translation id="5556459405103347317">Учитај поново</translation>
+<translation id="5560088892362098740">Датум истека</translation>
<translation id="5565735124758917034">Активно</translation>
<translation id="5571083550517324815">Преузимање са ове адресе није могуће. Изаберите другу адресу.</translation>
<translation id="5572851009514199876">Отворите и пријавите се у Chrome да би Chrome могао да провери да ли имате дозволу за приступ овом сајту.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Учитавање подешавања смерница није успело</translation>
<translation id="5631439013527180824">Неважећи токен за управљање уређајима</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="5646376287012673985">Локација</translation>
<translation id="5659593005791499971">Имејл</translation>
<translation id="5669703222995421982">Добијте персонализовани садржај</translation>
<translation id="5675650730144413517">Ова страница не функционише</translation>
<translation id="5710435578057952990">Идентитет овог веб-сајта није верификован.</translation>
-<translation id="5713016350996637505">Обмањујући садржај је блокиран</translation>
+<translation id="5719499550583120431">Прихватају се припејд картице.</translation>
<translation id="5720705177508910913">Тренутни корисник</translation>
<translation id="5732392974455271431">Родитељи могу да га деблокирају за тебе</translation>
<translation id="5763042198335101085">Унесите важећу имејл адресу</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Желите ли да попуните подацима <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">Веза са доменом <ph name="DOMAIN" /> је шифрована помоћу застарелог пакета за шифровање.</translation>
<translation id="5813119285467412249">&amp;Понови додавање</translation>
-<translation id="5814352347845180253">Можете да изгубите приступ премијум садржају са сајта <ph name="SITE" /> и неких других сајтова.</translation>
<translation id="5838278095973806738">Немојте да уносите осетљиве информације на овом сајту (на пример, лозинке или кредитне картице) јер нападачи могу да их украду.</translation>
<translation id="5869405914158311789">Овај сајт није доступан</translation>
<translation id="5869522115854928033">Сачуване лозинке</translation>
<translation id="5872918882028971132">Предлози родитеља</translation>
+<translation id="5893752035575986141">Прихватају се кредитне картице.</translation>
<translation id="5901630391730855834">Жута</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (синхронизовано)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Користи се 1}one{Користи се #}few{Користе се #}other{Користи се #}}</translation>
-<translation id="5926846154125914413">Можете да изгубите приступ премијум садржају са неких сајтова.</translation>
<translation id="5959728338436674663">Аутоматски шаљите одређене <ph name="BEGIN_WHITEPAPER_LINK" />информације о систему и садржај страница<ph name="END_WHITEPAPER_LINK" /> Google-у да бисте нам помогли да откријемо опасне апликације и сајтове. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">Уклони из историје</translation>
<translation id="5975083100439434680">Умањивање</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Затвори</translation>
<translation id="6042308850641462728">Више</translation>
<translation id="6047233362582046994">Ако разумете безбедносне ризике, можете да <ph name="BEGIN_LINK" />посетите овај сајт<ph name="END_LINK" /> пре него што уклонимо штетне апликације.</translation>
+<translation id="6047927260846328439">Овај садржај ће покушати да вас превари да инсталирате софтвер или откријете личне податке. <ph name="BEGIN_LINK" />Ипак прикажи<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Тренутно не можете да посетите <ph name="SITE" /> јер веб-сајт користи проверу сертификата. Грешке и напади на мрежи су обично привремени, па ће ова страница вероватно функционисати касније.</translation>
<translation id="6060685159320643512">Пазите, ови експерименти могу бити опасни</translation>
<translation id="6080696365213338172">Приступали сте садржају помоћу сертификата који је обезбедио администратор. Администратор може да пресретне податке које обезбедите домену <ph name="DOMAIN" />.</translation>
@@ -621,7 +645,6 @@
<translation id="6165508094623778733">Сазнајте више</translation>
<translation id="6169916984152623906">Сада можете да прегледате приватно и други људи који користе овај уређај неће видети ваше активности. Међутим, преузимања и обележивачи ће бити сачувани.</translation>
<translation id="6177128806592000436">Веза са овим сајтом није безбедна</translation>
-<translation id="6184817833369986695">(кохорта: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Проверите интернет везу</translation>
<translation id="6218753634732582820">Желите ли да уклоните адресу из Chromium-а?</translation>
<translation id="6221345481584921695">Google безбедно прегледање је недавно <ph name="BEGIN_LINK" />открило малвер<ph name="END_LINK" /> на <ph name="SITE" />. Веб-сајтови који су обично безбедни се понекад заразе малвером. Злонамеран садржај потиче са <ph name="SUBRESOURCE_HOST" />, који је познати дистрибутер малвера.</translation>
@@ -640,13 +663,14 @@
<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>
<translation id="6387478394221739770">Интересују вас нове занимљиве Chrome функције? Испробајте бета канал на chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium-у је понестало меморије док је покушавао да прикаже ову веб-страницу.</translation>
+<translation id="6397451950548600259">Софтвер на вашем рачунару онемогућава Chrome-у да се безбедно повеже на веб.</translation>
<translation id="6404511346730675251">Измена обележивача</translation>
<translation id="6410264514553301377">Унесите датум истека и CVC за картицу <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Питао/ла си родитеља да ли смеш да посетиш овај сајт</translation>
@@ -661,6 +685,7 @@
<translation id="647261751007945333">Смернице за уређај</translation>
<translation id="6477321094435799029">Chrome је открио неуобичајени кôд на овој страници и блокирао је ради заштите ваших личних података (попут лозинки, бројева телефона или бројева кредитних картица).</translation>
<translation id="6489534406876378309">Покрени отпремање отказивања</translation>
+<translation id="6507833130742554667">Прихватају се кредитне и дебитне картице.</translation>
<translation id="6508722015517270189">Поново покрените Chrome</translation>
<translation id="6529602333819889595">&amp;Понови брисање</translation>
<translation id="6534179046333460208">Предлози Интернета око нас</translation>
@@ -669,13 +694,13 @@
<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="657639383826808334">Овај садржај ће покушати да инсталира опасан софтвер на уређају који ће украсти или обрисати ваше податке. <ph name="BEGIN_LINK" />Ипак прикажи<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Опције шифровања</translation>
<translation id="662080504995468778">Не затварај</translation>
<translation id="6626291197371920147">Додајте важећи број картице</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> претрага</translation>
<translation id="6630809736994426279">Нападачи који су тренутно на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ће можда покушати да инсталирају опасне програме на Mac-у који краду или бришу податке (на пример, слике, лозинке, поруке и бројеве кредитних картица). <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Ове смернице су застареле.</translation>
+<translation id="6657585470893396449">Лозинка</translation>
<translation id="6671697161687535275">Желите ли да уклоните предлог из Chromium-а?</translation>
<translation id="6685834062052613830">Одјавите се и довршите подешавање</translation>
<translation id="6710213216561001401">Претходно</translation>
@@ -706,7 +731,6 @@
<translation id="6970216967273061347">Дистрикт</translation>
<translation id="6973656660372572881">Наведени су и фиксни прокси сервери и URL адреса .pac скрипте.</translation>
<translation id="6989763994942163495">Прикажи напредна подешавања...</translation>
-<translation id="7000990526846637657">Није пронађен ниједан унос у историји</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Google налог има друге облике историје прегледања на <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Лозинке</translation>
@@ -721,7 +745,9 @@
<translation id="7129409597930077180">Слање на ову адресу није могуће. Изаберите другу адресу.</translation>
<translation id="7138472120740807366">Начин испоруке</translation>
<translation id="7139724024395191329">Емират</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Плаћање није безбедно</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Поново покрени</translation>
<translation id="7180611975245234373">Освежи</translation>
<translation id="7182878459783632708">Нису подешене никакве смернице</translation>
@@ -762,6 +788,7 @@
<translation id="7514365320538308">Преузми</translation>
<translation id="7518003948725431193">Ниједна веб-страница није пронађена за веб адресу: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Веза са овим сајтом није приватна</translation>
<translation id="7535087603100972091">Вредност</translation>
<translation id="7537536606612762813">Обавезно</translation>
<translation id="7542403920425041731">Када будете потврдили, подаци о картици ће бити послати овом сајту.</translation>
@@ -771,7 +798,6 @@
<translation id="7552846755917812628">Испробајте следеће савете:</translation>
<translation id="7554791636758816595">Нова картица</translation>
<translation id="7567204685887185387">Овај сервер не може да докаже да је <ph name="DOMAIN" />; његов безбедносни сертификат је можда лажно издат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Ова страница је на језику:<ph name="ORIGINAL_LANGUAGE" />Желите ли да је преведете?</translation>
<translation id="7569952961197462199">Желите ли да уклоните кредитну картицу из Chrome-а?</translation>
<translation id="7569983096843329377">Црна</translation>
@@ -798,9 +824,11 @@
<translation id="7714464543167945231">Сертификат</translation>
<translation id="7716147886133743102">Блокира администратор</translation>
<translation id="7716424297397655342">Није могуће учитати овај сајт из кеша</translation>
+<translation id="774634243536837715">Опасан садржај је блокиран.</translation>
<translation id="7752995774971033316">Не управља</translation>
<translation id="7755287808199759310">Родитељ може да га деблокира за тебе</translation>
<translation id="7758069387465995638">Можда је заштитни зид или антивирусни софтвер блокирао везу.</translation>
+<translation id="7759163816903619567">Домен за приказ:</translation>
<translation id="7761701407923456692">Сертификат сервера се не подудара са URL адресом.</translation>
<translation id="7763386264682878361">Рашчлањивач манифеста за плаћање</translation>
<translation id="7764225426217299476">Додајте адресу</translation>
@@ -815,6 +843,7 @@
<translation id="7815407501681723534">Пронашли смо <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> за „<ph name="SEARCH_STRING" />“</translation>
<translation id="785549533363645510">Али, нисте невидљиви. Преласком у режим без архивирања нећете сакрити прегледање од послодавца, интернет провајдера или веб-сајтова које посећујете.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" />: <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Прихватају се дебитне и припејд картице.</translation>
<translation id="7887683347370398519">Проверите CVC и покушајте поново</translation>
<translation id="79338296614623784">Унесите важећи број телефона</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -834,7 +863,6 @@
<translation id="8041089156583427627">Пошаљи повратне информације</translation>
<translation id="8041940743680923270">Користи глобалну подразумевану вредност (Питај)</translation>
<translation id="8088680233425245692">Прегледање чланка није успело.</translation>
-<translation id="8089520772729574115">мање од 1 MB</translation>
<translation id="8091372947890762290">Активација је на чекању на серверу</translation>
<translation id="8118489163946903409">Начин плаћања</translation>
<translation id="8131740175452115882">Потврди</translation>
@@ -846,10 +874,13 @@
<translation id="8194797478851900357">&amp;Опозови премештање</translation>
<translation id="8201077131113104583">Неважећи URL за ажурирање за додатак са ИД-ом „<ph name="EXTENSION_ID" />“.</translation>
<translation id="8202097416529803614">Резиме поруџбине</translation>
+<translation id="8205463626947051446">Сајт приказује огласе који ометају</translation>
<translation id="8218327578424803826">Додељена локација:</translation>
<translation id="8225771182978767009">Особа која је подесила овај рачунар је одлучила да блокира овај сајт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Отворите страницу у новој картици без архивирања</translation>
<translation id="8241707690549784388">Страница коју тражите користила је информације које сте унели. Повратак на ту страницу може да проузрокује потребну понављања радњи које сте извршили. Желите ли да наставите?</translation>
+<translation id="8241712895048303527">Блокирај на овом сајту</translation>
<translation id="8249320324621329438">Последње учитано:</translation>
<translation id="8253091569723639551">Адреса за обрачун је обавезна</translation>
<translation id="8261506727792406068">Избриши</translation>
@@ -860,7 +891,6 @@
<translation id="8308427013383895095">Превођење није успело због проблема са мрежном везом.</translation>
<translation id="8332188693563227489">Приступ хосту <ph name="HOST_NAME" /> је одбијен</translation>
<translation id="834457929814110454">Ако разумете безбедносне ризике, можете да <ph name="BEGIN_LINK" />посетите овај сајт<ph name="END_LINK" /> пре него што уклонимо штетне програме.</translation>
-<translation id="8344669043927012510">Отворите страницу у режиму без архивирања (⇧⌘N)</translation>
<translation id="8349305172487531364">Трака са обележивачима</translation>
<translation id="8363502534493474904">да искључите режим рада у авиону</translation>
<translation id="8364627913115013041">Није подешено.</translation>
@@ -870,6 +900,7 @@
<translation id="8398259832188219207">Извештај о отказивању је отпремљен у: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Отказивања (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Потребно је да двапут унесете исту приступну фразу.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Подешавања</translation>
<translation id="8433057134996913067">Овако ћете се одјавити са већине веб-сајтова.</translation>
<translation id="8437238597147034694">&amp;Опозови премештање</translation>
@@ -877,8 +908,9 @@
<translation id="8483780878231876732">Да бисте користили картице са Google налога, пријавите се у Chrome</translation>
<translation id="8488350697529856933">Односе се на</translation>
<translation id="8498891568109133222">Одговор хоста <ph name="HOST_NAME" /> је трајао предуго.</translation>
-<translation id="8532105204136943229">Година истека</translation>
+<translation id="8503813439785031346">Корисничко име</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="8571890674111243710">Превођење странице на <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Додај тел. број
@@ -886,6 +918,7 @@
<translation id="859285277496340001">Сертификат не наводи механизам којим се проверава да ли је опозван.</translation>
<translation id="8620436878122366504">Родитељи га још увек нису одобрили</translation>
<translation id="8647750283161643317">Врати све на подразумевано</translation>
+<translation id="8660471606262461360">Од Google Payments-а</translation>
<translation id="8703575177326907206">Ваша веза са доменом <ph name="DOMAIN" /> није шифрована.</translation>
<translation id="8718314106902482036">Плаћање није довршено</translation>
<translation id="8725066075913043281">Пробајте поново</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">Увећавање</translation>
<translation id="8931333241327730545">Да ли желите да сачувате ову картицу на Google налог?</translation>
<translation id="8932102934695377596">Сат вам касни</translation>
+<translation id="8938939909778640821">Прихватају се кредитне и припејд картице</translation>
<translation id="8971063699422889582">Сертификат сервера је истекао.</translation>
<translation id="8986494364107987395">Аутоматски шаљи Google-у статистичке податке о коришћењу и извештаје о отказивању</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">Увек блокирај на овом сајту</translation>
<translation id="9170848237812810038">&amp;Опозови</translation>
<translation id="917450738466192189">Сертификат сервера је неважећи.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> и још <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="9183425211371246419">Хост <ph name="HOST_NAME" /> користи неподржани протокол.</translation>
<translation id="9205078245616868884">Подаци се шифрују помоћу приступне фразе за синхронизацију. Унесите је да бисте започели синхронизацију.</translation>
<translation id="9207861905230894330">Додавање чланка није успело.</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ОБРИШИ ОБРАЗАЦ</translation>
<translation id="939736085109172342">Нови директоријум</translation>
-<translation id="941721044073577244">Изгледа да немате дозволу да посетите овај сајт</translation>
<translation id="969892804517981540">Званична верзија</translation>
<translation id="975560348586398090">{COUNT,plural, =0{None}=1{1 ставка}one{# ставка}few{# ставке}other{# ставки}}</translation>
+<translation id="981121421437150478">Офлајн</translation>
<translation id="988159990683914416">Верзија за програмере</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_sv.xtb b/chromium/components/strings/components_strings_sv.xtb
index af51a354898..4870f0daa0f 100644
--- a/chromium/components/strings/components_strings_sv.xtb
+++ b/chromium/components/strings/components_strings_sv.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Stäng andra appar</translation>
<translation id="1055184225775184556">&amp;Ångra Lägg till</translation>
<translation id="10614374240317010">Aldrig sparad</translation>
+<translation id="1066396345355680611">Du kan förlora tillgången till skyddat innehåll från <ph name="SITE" /> och några andra webbplatser.</translation>
<translation id="106701514854093668">Bokmärken på skrivbordet</translation>
<translation id="1074497978438210769">Inte säker</translation>
<translation id="1080116354587839789">Anpassa till fönstrets bredd</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Läslista</translation>
<translation id="1264126396475825575">Felrapport skapades <ph name="CRASH_TIME" /> (har ännu inte laddats upp eller ignorerats)</translation>
<translation id="1281526147609854549">Utfärdades av <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Farligt innehåll har blockerats</translation>
<translation id="1285320974508926690">Översätt aldrig den här webbplatsen</translation>
<translation id="129553762522093515">Nyligen stängda</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Testa att rensa cookies<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Registreringsdomän:</translation>
<translation id="1340482604681802745">Hämtningsadress</translation>
-<translation id="1344211575059133124">Du behöver tillstånd att besöka den här webbplatsen</translation>
<translation id="1344588688991793829">Inställningar för Autofyll i Chromium …</translation>
<translation id="1348198688976932919">Det finns farliga appar på webbplatsen du är på väg till</translation>
<translation id="1374468813861204354">förslag</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Identiteten hos <ph name="ORGANIZATION" /> på <ph name="LOCALITY" /> har verifierats av <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ja</translation>
<translation id="1430915738399379752">Skriv ut</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> till}other{<ph name="PAYMENT_METHOD_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> till}}</translation>
<translation id="1506687042165942984">Visa en sparad kopia av sidan (en som vi vet är inaktuell).</translation>
<translation id="1517433312004943670">Telefonnummer är obligatoriskt</translation>
+<translation id="1517500485252541695">Godkända kredit- och betalkort</translation>
<translation id="1519264250979466059">Programversionsdatum</translation>
+<translation id="1527263332363067270">Väntar på anslutning …</translation>
<translation id="153384715582417236">Det var allt för den här gången</translation>
<translation id="1549470594296187301">JavaScript måste aktiveras för att du ska kunna använda den här funktionen.</translation>
<translation id="1555130319947370107">Blå</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Kontakta systemadministratören.</translation>
<translation id="1740951997222943430">Ange en giltig utgångsmånad</translation>
-<translation id="1745358365027406341">Ladda ned sidan senare</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Obligatoriskt fält</translation>
<translation id="187918866476621466">Öppna startsidorna</translation>
<translation id="1883255238294161206">Komprimera lista</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> till}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> till}}</translation>
<translation id="1898423065542865115">Filtrering</translation>
+<translation id="1916770123977586577">Läs in sidan igen för att tillämpa dina uppdaterade inställningar för webbplatsen</translation>
+<translation id="1919345977826869612">Annonser</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Ingen}=1{1 webbplats}other{# webbplatser}}</translation>
<translation id="194030505837763158">Öppna <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Godkända förbetalda kort</translation>
<translation id="1962204205936693436">Bokmärken på <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Serieproduktionsfel</translation>
<translation id="1974060860693918893">Avancerat</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP-fel</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2282872951544483773">Otillgängliga experiment</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> objekt}other{<ph name="ITEM_COUNT" /> objekt}}</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>
<translation id="2337852623177822836">Inställningen styrs av administratören</translation>
<translation id="2354001756790975382">Övriga bokmärken</translation>
<translation id="2354430244986887761">Google Säker webbsökning <ph name="BEGIN_LINK" />hittade skadliga appar<ph name="END_LINK" /> nyligen på <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Fortsätt</translation>
<translation id="2365563543831475020">Felrapport som skapades <ph name="CRASH_TIME" /> laddades inte upp</translation>
<translation id="2367567093518048410">Nivå</translation>
-<translation id="237718015863234333">Det finns inga användargränssnittsalternativ</translation>
<translation id="2384307209577226199">Standardinställning i företaget</translation>
<translation id="2386255080630008482">Servercertifikatet har återkallats.</translation>
<translation id="2392959068659972793">Visa policyer utan inställt värde</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Om alternativet är markerat sparar Chromium en kopia av kortet på enheten så att det går snabbare att fylla i formulär.</translation>
<translation id="2498091847651709837">Läs in ett nytt kort</translation>
<translation id="2501278716633472235">Föregående</translation>
+<translation id="2503184589641749290">Godkända betalkort och förbetalda kort</translation>
<translation id="2515629240566999685">kontrollera mottagningen i området</translation>
-<translation id="2516305470678292029">Användargränssnittsalternativ</translation>
<translation id="2539524384386349900">Identifiera</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> skickade ett ogiltigt svar.</translation>
<translation id="2556876185419854533">&amp;Ånga Redigera</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Fraktalternativ</translation>
<translation id="277499241957683684">Enhetsregister saknas</translation>
<translation id="2784949926578158345">Anslutningen återställdes.</translation>
+<translation id="2788784517760473862">Godkända kreditkort</translation>
<translation id="2794233252405721443">Webbplatsen har blockerats</translation>
<translation id="2799020568854403057">Det finns skadliga appar på webbplatsen du är på väg till</translation>
<translation id="2803306138276472711">Google Säker webbsökning upptäckte nyligen <ph name="BEGIN_LINK" />skadlig programvara<ph name="END_LINK" /> på <ph name="SITE" />. Ibland förekommer det skadlig programvara på webbplatser som vanligtvis är säkra.</translation>
<translation id="2824775600643448204">Adress- och sökfält</translation>
<translation id="2826760142808435982">Anslutningen är krypterad och verifieras med <ph name="CIPHER" /> och använder <ph name="KX" /> som nyckelutbytesmekanism.</translation>
<translation id="2835170189407361413">Rensa formuläret</translation>
+<translation id="2851634818064021665">Du behöver tillstånd att besöka den här webbplatsen</translation>
<translation id="2856444702002559011">En angripare kan försöka stjäla dina uppgifter från <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (t.ex. lösenord, meddelanden eller kreditkortsuppgifter). <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Läs inte in sidan igen</translation>
-<translation id="2900469785430194048">Minnet tog slut när den här webbsidan skulle visas i Google Chrome.</translation>
<translation id="2909946352844186028">En nätverksförändring upptäcktes.</translation>
<translation id="2916038427272391327">Stäng andra program</translation>
<translation id="2922350208395188000">Servercertifikatet kan inte kontrolleras.</translation>
<translation id="2928905813689894207">Faktureringsadress</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> till}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> till}}</translation>
<translation id="2941952326391522266">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom dess säkerhetscertifikat kommer från <ph name="DOMAIN2" />. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Om alternativet är aktiverat sparar Chromium en kopia av kortet på enheten så att det går snabbare att fylla i formulär.</translation>
<translation id="2985398929374701810">Ange en giltig adress</translation>
<translation id="2986368408720340940">Det här alternativet för utlämning är inte tillgängligt. Testa ett annat alternativ.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Sidor som visas på flikar i inkognitoläget försvinner från webbhistoriken, från dina cookies och från sökhistoriken när alla inkognitoflikar har stängts. Alla filer som du laddar ned eller bokmärken som du skapar sparas.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Ö</translation>
-<translation id="317583078218509884">De nya inställningarna för webbplatsbehörighet börjar gälla när sidan uppdateras.</translation>
<translation id="3176929007561373547">Kontrollera dina proxyinställningar eller kontakta nätverksadministratören om du vill
kontrollera att proxyservern fungerar. Om du inte tror att du ska
använda en proxyserver:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Öppna sidan i inkognitoläge</translation>
<translation id="320323717674993345">Avbryt betalningen</translation>
<translation id="3207960819495026254">Bokmärkt</translation>
<translation id="3225919329040284222">Ett certifikat som inte överensstämmer med inbyggda förväntningar presenterades på servern. Förväntningarna gäller för webbplatser med hög säkerhet för att skydda dig.</translation>
@@ -295,12 +297,13 @@
<translation id="3270847123878663523">&amp;Ångra Ändra ordning</translation>
<translation id="3282497668470633863">Lägg till namnet på kortet</translation>
<translation id="3286538390144397061">Starta om nu</translation>
+<translation id="3287510313208355388">Ladda ned när du är online</translation>
<translation id="3303855915957856445">Inga sökresultat hittades</translation>
<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="3329013043687509092">Mättnad</translation>
<translation id="333371639341676808">Förhindra att den här sidan öppnar ytterligare dialogrutor.</translation>
-<translation id="3338095232262050444">Säkert</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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Hämta intervall:</translation>
<translation id="3462200631372590220">Dölja avancerade uppgifter</translation>
<translation id="3467763166455606212">Kortinnehavarens namn måste anges</translation>
-<translation id="3478058380795961209">Sista giltighetsmånad</translation>
<translation id="3479539252931486093">Var det här oväntat? <ph name="BEGIN_LINK" />Meddela oss<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Inte nu</translation>
<translation id="3498215018399854026">Vi kunde inte nå din förälder just nu. Försök igen.</translation>
@@ -343,12 +345,13 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kreditkort och förbetalda kort kan användas.</translation>
<translation id="3582930987043644930">Lägg till namn</translation>
<translation id="3583757800736429874">&amp;Gör om Flytta</translation>
<translation id="3586931643579894722">Dölj detaljerad information</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Ange ett giltigt utgångsdatum</translation>
-<translation id="36224234498066874">Rensa webbinformation...</translation>
+<translation id="36224234498066874">Rensa webbinformation ...</translation>
<translation id="362276910939193118">Visa fullständig historik</translation>
<translation id="3623476034248543066">Visa värde</translation>
<translation id="3630155396527302611">Om den redan finns med på listan över program som har tillgång till nätverket
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Om du ser detta ofta provar du <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Version</translation>
<translation id="3678029195006412963">Begäran kunde inte signeras</translation>
+<translation id="3678529606614285348">Öppna sidan i ett nytt inkognitofönster (Ctrl-Skift-N)</translation>
<translation id="3679803492151881375">Kraschrapporten skapades den <ph name="CRASH_TIME" /> och laddades upp den <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Certifikatinformation</translation>
<translation id="3690164694835360974">Osäker inloggning</translation>
-<translation id="3693415264595406141">Lösenord:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Läser in...</translation>
<translation id="3712624925041724820">Licenserna har tagit slut</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Angripare på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan försöka lura dig att göra något riskfyllt, som att installera programvara eller avslöja personliga uppgifter (t.ex. lösenord, telefonnummer eller kreditkortsuppgifter). <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Det gick inte att översätta på grund av ett serverfel.</translation>
<translation id="3759461132968374835">Inga krascher har rapporterats nyligen. Krascher som uppstod när kraschrapporteringen var inaktiverad visas inte här.</translation>
+<translation id="3765032636089507299">Sidan Säker webbsökning är under utveckling</translation>
<translation id="3778403066972421603">Vill du spara det här kortet i Google-kontot och på den här enheten?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Utgångsdatum: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Din begäran om att få tillgång till denna webbplats har skickats till <ph name="NAME" /></translation>
<translation id="3890664840433101773">Lägg till e-post</translation>
<translation id="3901925938762663762">Kortet gäller inte längre</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Id i den uppladdade felrapporten <ph name="CRASH_ID" /> (Lokalt krasch-id: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom inga alternativa namn på certifikatobjektet anges i säkerhetscertifikatet. Detta kan bero på en felaktig konfigurering eller att en angripare manipulerat anslutningen.</translation>
+<translation id="3949601375789751990">Webbhistoriken visas här</translation>
<translation id="3963721102035795474">Läsarläge</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Ingen}=1{Från 1 webbplats }other{Från # webbplatser }}</translation>
<translation id="397105322502079400">Beräknar ...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Platsinställningar</translation>
<translation id="4169947484918424451">Vill du att Chromium sparar det här kortet?</translation>
<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="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>
@@ -434,16 +442,16 @@
<translation id="4312866146174492540">Blockera (standard)</translation>
<translation id="4325863107915753736">Det gick inte att hitta artikeln</translation>
<translation id="4326324639298822553">Kontrollera utgångsdatum och försök igen</translation>
-<translation id="4331708818696583467">Inte säkert</translation>
+<translation id="4331708818696583467">Inte säker</translation>
<translation id="4356973930735388585">Det kan hända att angripare på den här webbplatsen försöker installera skadliga program på datorn som stjäl eller raderar dina uppgifter (t.ex. foton, lösenord, meddelanden och kreditkort).</translation>
<translation id="4372948949327679948">Ett <ph name="VALUE_TYPE" />-värde förväntades.</translation>
<translation id="4377125064752653719">Du försökte öppna <ph name="DOMAIN" />, men servern visade ett certifikat som har återkallats av utfärdaren. Det innebär att säkerhetsuppgifterna som servern visar inte är absolut tillförlitliga. Du kanske kommunicerar med en skadlig server.</translation>
-<translation id="4381091992796011497">Användarnamn:</translation>
<translation id="4394049700291259645">Inaktivera</translation>
<translation id="4406896451731180161">sökresultat</translation>
<translation id="4424024547088906515">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom Chrome inte litar på dess säkerhetscertifikat. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> godkände inte inloggningscertifikatet eller så har inget inloggningscertifikat angetts.</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="4506176782989081258">Valideringsfel: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">kontakta systemadministratören</translation>
<translation id="450710068430902550">Delad med en administratör</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Vill du ta bort adressen från Chrome?</translation>
<translation id="4592951414987517459">Anslutningen till <ph name="DOMAIN" /> är krypterad med en modern krypteringssvit.</translation>
<translation id="4594403342090139922">&amp;Ångra Ta bort</translation>
+<translation id="4611292653554630842">Logga in</translation>
<translation id="4619615317237390068">Flikar från andra enheter</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom dess säkerhetscertifikat innehåller fel. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen.</translation>
<translation id="4690462567478992370">Sluta använda ett ogiltigt certifikat</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Anslutningen avbröts</translation>
+<translation id="471880041731876836">Du har inte behörighet att besöka den här webbplatsen.</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" /> köra nätverksdiagnostik för Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Läs in policyer på nytt</translation>
<translation id="4728558894243024398">Plattform</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Sidan kan ha till syfte att försöka lura dig att installera programvara eller avslöja personlig information. <ph name="BEGIN_LINK" />Visa ändå<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Sök i historiken</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Autentisering krävs</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{och en till webbsida}other{och # till webbsidor}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Sidan har översatts från ett okänt språk till <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Betalning</translation>
<translation id="4926049483395192435">Värdet måste anges.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Åtgärder</translation>
<translation id="4958444002117714549">Expandera lista</translation>
<translation id="4974590756084640048">Aktivera varningar igen</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Felaktigt lösenord</translation>
<translation id="5056549851600133418">Artiklar för dig</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />kontrollera proxyadressen<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Du kan förlora tillgången till skyddat innehåll från några webbplatser.</translation>
<translation id="5087286274860437796">Servercertifikatet är inte giltigt för närvarande.</translation>
<translation id="5087580092889165836">Lägg till kort</translation>
<translation id="5089810972385038852">Delstat</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Provins</translation>
<translation id="5115563688576182185">(64 bitar)</translation>
<translation id="5141240743006678641">Kryptera synkroniserade lösenord med dina inloggningsuppgifter för Google</translation>
-<translation id="514421653919133810">Öppna sidan i inkognitoläge (Ctrl-Skift-N)</translation>
<translation id="5145883236150621069">Felkoden ingår i policysvaret</translation>
+<translation id="5159010409087891077">Öppna sidan i ett nytt inkognitofönster (⇧⌘N)</translation>
<translation id="5171045022955879922">Ange eller sök efter webbadress</translation>
<translation id="5172758083709347301">Dator</translation>
<translation id="5179510805599951267">Inte på <ph name="ORIGINAL_LANGUAGE" />? Rapportera felet</translation>
-<translation id="5181140330217080051">Nedladdning</translation>
<translation id="5190835502935405962">Bokmärkesfältet</translation>
<translation id="5199729219167945352">Experiment</translation>
<translation id="5205222826937269299">Namn måste anges</translation>
<translation id="5222812217790122047">E-postadress måste anges</translation>
<translation id="5251803541071282808">Moln</translation>
<translation id="5277279256032773186">Använder du Chrome på jobbet? Företag kan hantera de anställdas inställningar i Chrome. Läs mer</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Följ stegen nedan för att inaktivera programvaran tillfälligt så att du kan komma ut på internet. Du måste ha administratörsbehörighet.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Klicka på <ph name="BEGIN_BOLD" />Start<ph name="END_BOLD" /> och sök efter och välj <ph name="BEGIN_BOLD" />Visa lokala tjänster<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Välj <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Under <ph name="BEGIN_BOLD" />Starttyp<ph name="END_BOLD" /> väljer du <ph name="BEGIN_BOLD" />Inaktiverad<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Under <ph name="BEGIN_BOLD" />Tjänststatus<ph name="END_BOLD" /> klickar du på <ph name="BEGIN_BOLD" />Stopp<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Klicka på <ph name="BEGIN_BOLD" />Använd<ph name="END_BOLD" /> och sedan på <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Besök <ph name="BEGIN_LEARN_MORE_LINK" />hjälpcentret för Chrome<ph name="END_LEARN_MORE_LINK" /> för att lära dig hur du tar bort programvara från datorn permanent
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Din anslutning till webbplatsen är inte privat. Du kan när som helst avsluta VR-läget genom att ta av headsetet och trycka på bakåt.</translation>
<translation id="5299298092464848405">Det uppstod ett fel när policyn analyserades</translation>
<translation id="5308689395849655368">Krashrapportering har inaktiverats.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Schemavalideringsfel i <ph name="ERROR_PATH" />: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Sidan på <ph name="HOST_NAME" /> kan inte hittas</translation>
<translation id="5455374756549232013">Felaktig tidsstämpel för policy</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> av <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Ogiltigt</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> till}other{<ph name="CONTACT_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> till}}</translation>
<translation id="5470861586879999274">&amp;Gör om Redigera</translation>
+<translation id="5481076368049295676">Sidan kan ha till syfte att installera farlig programvara som stjäl eller raderar information på enheten. <ph name="BEGIN_LINK" />Visa ändå<ph name="END_LINK" />.</translation>
<translation id="54817484435770891">Lägg till giltig adress</translation>
<translation id="5492298309214877701">Webbplatsen på företagets, organisationens eller skolans intranät har samma webbadress som en extern webbplats.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Det gick inte att hitta den önskade artikeln.</translation>
<translation id="5544037170328430102">På en inbäddad sida på <ph name="SITE" /> står det:</translation>
<translation id="5556459405103347317">Hämta igen</translation>
+<translation id="5560088892362098740">Sista giltighetsdatum</translation>
<translation id="5565735124758917034">Aktiv</translation>
<translation id="5571083550517324815">Utlämning erbjuds inte på den här adressen. Välj en annan adress.</translation>
<translation id="5572851009514199876">Logga in på Chrome så att Chrome kan kontrollera om du har tillgång till den här webbplatsen.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Det gick inte att läsa in policyinställningarna</translation>
<translation id="5631439013527180824">Ogiltig enhetshanteringstoken</translation>
<translation id="5633066919399395251">Angripare på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan försöka installera skadliga program som stjäl eller raderar information (t.ex. foton, lösenord, meddelanden och kreditkortsuppgifter) på datorn. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Bedrägligt innehåll har blockerats.</translation>
<translation id="5646376287012673985">Plats</translation>
<translation id="5659593005791499971">E-post</translation>
<translation id="5669703222995421982">Få anpassat innehåll</translation>
<translation id="5675650730144413517">Sidan fungerar inte</translation>
<translation id="5710435578057952990">Webbplatsens identitet har inte verifierats.</translation>
-<translation id="5713016350996637505">Bedrägligt innehåll har blockerats</translation>
+<translation id="5719499550583120431">Förbetalda kort kan användas.</translation>
<translation id="5720705177508910913">Aktuell användare</translation>
<translation id="5732392974455271431">Dina föräldrar kan ta bort blockeringen</translation>
<translation id="5763042198335101085">Ange en giltig e-postadress</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Vill du att uppgifterna om <ph name="CARD_DETAIL" /> ska fyllas i?</translation>
<translation id="5810442152076338065">Anslutningen till <ph name="DOMAIN" /> är krypterad med en gammal krypteringssvit.</translation>
<translation id="5813119285467412249">&amp;Gör om Lägg till</translation>
-<translation id="5814352347845180253">Du kan förlora tillgång till premiuminnehåll från <ph name="SITE" /> och några andra webbplatser.</translation>
<translation id="5838278095973806738">Du bör inte ange några känsliga uppgifter på den här webbplatsen (till exempel lösenord eller kreditkortsuppgifter) eftersom hackare kan stjäla dem.</translation>
<translation id="5869405914158311789">Webbplatsen kan inte nås</translation>
<translation id="5869522115854928033">Sparade lösenord</translation>
<translation id="5872918882028971132">Föräldratips</translation>
+<translation id="5893752035575986141">Kreditkort får användas.</translation>
<translation id="5901630391730855834">Gul</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="5926846154125914413">Du kan förlora tillgång till premiuminnehåll från vissa webbplatser.</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="5967867314010545767">Ta bort från historiken</translation>
<translation id="5975083100439434680">Zooma ut</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Stäng</translation>
<translation id="6042308850641462728">Mer</translation>
<translation id="6047233362582046994">Om du är medveten om säkerhetsriskerna kan du <ph name="BEGIN_LINK" />besöka webbplatsen<ph name="END_LINK" /> innan de skadliga apparna har tagits bort.</translation>
+<translation id="6047927260846328439">Sidan kan ha till syfte att försöka lura dig att installera programvara eller avslöja personliga uppgifter. <ph name="BEGIN_LINK" />Visa ändå<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Du kan inte besöka <ph name="SITE" /> just nu eftersom tekniken att fästa certifikat används på webbplatsen. Nätverksfel och attacker är ofta tillfälliga, så sidan kommer förmodligen att fungera senare.</translation>
<translation id="6060685159320643512">Försiktigt, experimenten kan vara skadliga</translation>
<translation id="6080696365213338172">Du har visat innehåll med hjälp av ett certifikat från en administratör. Det innebär att data som du har angett på <ph name="DOMAIN" /> även kan visas av administratören.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Läs mer</translation>
<translation id="6169916984152623906">Nu kan du surfa privat. Din aktivitet visas inte för andra som använder enheten, men nedladdningar och bokmärken sparas.</translation>
<translation id="6177128806592000436">Anslutningen till webbplatsen är inte säker</translation>
-<translation id="6184817833369986695">(kohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Kontrollera internetanslutningen</translation>
<translation id="6218753634732582820">Vill du ta bort adressen från Chromium?</translation>
<translation id="6221345481584921695">Google Säker webbsökning upptäckte nyligen <ph name="BEGIN_LINK" />skadlig programvara<ph name="END_LINK" /> på <ph name="SITE" />. Webbplatser som vanligtvis är säkra utsätts ibland för skadlig programvara. Det skadliga innehållet kommer från <ph name="SUBRESOURCE_HOST" />, som är en känd distributör av skadlig programvara.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Är du intresserad av häftiga nya funktioner i Chrome? Pröva vår betakanal på chrome.com/beta.</translation>
-<translation id="6389758589412724634">Minnet tog slut när den här webbsidan skulle visas i Chromium.</translation>
+<translation id="6397451950548600259">Programvara på datorn förhindrar att Chrome ansluter till internet på ett säkert sätt</translation>
<translation id="6404511346730675251">Redigera bokmärke</translation>
<translation id="6410264514553301377">Ange utgångsdatum och CVC-kod för <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Du har frågat en förälder om lov att besöka den här webbplatsen.</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Enhetspolicyer</translation>
<translation id="6477321094435799029">Chrome har identifierat ovanlig kod på sidan och blockerat den för att skydda dina personliga uppgifter (som lösenord, telefonnummer och kreditkortsuppgifter).</translation>
<translation id="6489534406876378309">Börja överföra information om krascher</translation>
+<translation id="6507833130742554667">Kreditkort och betalkort får användas.</translation>
<translation id="6508722015517270189">Starta om Chrome</translation>
<translation id="6529602333819889595">&amp;Gör om Ta bort</translation>
<translation id="6534179046333460208">Förslag från Physical Web</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Sidan kan ha till syfte att installera farlig programvara som stjäl eller raderar information på enheten. <ph name="BEGIN_LINK" />Visa ändå<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Krypteringsalternativ</translation>
<translation id="662080504995468778">Stanna kvar</translation>
<translation id="6626291197371920147">Lägg till ett giltigt kortnummer</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Sök</translation>
<translation id="6630809736994426279">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å din Mac. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Policyn är föråldrad.</translation>
+<translation id="6657585470893396449">Lösenord</translation>
<translation id="6671697161687535275">Vill du ta bort formulärförslaget från Chromium?</translation>
<translation id="6685834062052613830">Logga ut och slutför konfigureringen</translation>
<translation id="6710213216561001401">Föregående</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Distrikt</translation>
<translation id="6973656660372572881">Både fasta proxyservrar och en webbadress för PAC-skript anges.</translation>
<translation id="6989763994942163495">Visa avancerade inställningar ...</translation>
-<translation id="7000990526846637657">Inga historikposter hittades</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Andra former av webbhistorik för Google-kontot kan finnas på <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Lösenord</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Det går inte att skicka till den här adressen. Välj en annan adress.</translation>
<translation id="7138472120740807366">Leveranssätt</translation>
<translation id="7139724024395191329">Emirat</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> till}other{<ph name="PAYMENT_METHOD_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> till}}</translation>
<translation id="7155487117670177674">Osäker betalning</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> till}other{<ph name="SHIPPING_OPTION_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> till}}</translation>
<translation id="7179921470347911571">Starta om nu</translation>
<translation id="7180611975245234373">Uppdatera</translation>
<translation id="7182878459783632708">Inga policyer har ställts in</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Ladda ned</translation>
<translation id="7518003948725431193">Det fanns ingen webbsida på webbadressen: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Din anslutning till webbplatsen är inte privat</translation>
<translation id="7535087603100972091">Värde</translation>
<translation id="7537536606612762813">Obligatorisk</translation>
<translation id="7542403920425041731">När du bekräftar delas kortuppgifterna med den här webbplatsen.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Testa följande tips:</translation>
<translation id="7554791636758816595">Ny flik</translation>
<translation id="7567204685887185387">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom dess säkerhetscertifikat kan ha utfärdats utan behörighet. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> till}other{<ph name="CONTACT_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> till}}</translation>
<translation id="7568593326407688803">Den här sidan är på<ph name="ORIGINAL_LANGUAGE" />Vill du översätta den?</translation>
<translation id="7569952961197462199">Vill du ta bort kreditkortet från Chrome?</translation>
<translation id="7569983096843329377">Svart</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Certifikat</translation>
<translation id="7716147886133743102">Blockerades av administratören</translation>
<translation id="7716424297397655342">Det går inte att läsa in webbplatsen från cachelagringen</translation>
+<translation id="774634243536837715">Farligt innehåll har blockerats.</translation>
<translation id="7752995774971033316">Hanteras inte</translation>
<translation id="7755287808199759310">En förälder kan ta bort blockeringen</translation>
<translation id="7758069387465995638">Anslutningen kan ha blockerats av en brandvägg eller antivirusprogram.</translation>
+<translation id="7759163816903619567">Visningsdomän:</translation>
<translation id="7761701407923456692">Servercertifikatet överensstämmer inte med webbadressen.</translation>
<translation id="7763386264682878361">Textanalysator för manifest för betalning</translation>
<translation id="7764225426217299476">Lägg till adress</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Hittade <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> för <ph name="SEARCH_STRING" /></translation>
<translation id="785549533363645510">Men du är inte osynlig. Inkognitoläget döljer inte webbhistoriken för din arbetsgivare, internetleverantören eller webbplatserna du besöker.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Betalkort och förbetalda kort får användas.</translation>
<translation id="7887683347370398519">Kontrollera CVC-koden och försök igen</translation>
<translation id="79338296614623784">Ange ett giltigt telefonnummer</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Skicka feedback</translation>
<translation id="8041940743680923270">Använd global standardinställning (Fråga)</translation>
<translation id="8088680233425245692">Det gick inte att visa artikeln.</translation>
-<translation id="8089520772729574115">mindre än 1 MB</translation>
<translation id="8091372947890762290">Aktiveringen väntar på servern</translation>
<translation id="8118489163946903409">Betalningsmetod</translation>
<translation id="8131740175452115882">Bekräfta</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Ångra Flytta</translation>
<translation id="8201077131113104583">Ogiltig webbadress för uppdatering för tillägg med id <ph name="EXTENSION_ID" />.</translation>
<translation id="8202097416529803614">Beställningsöversikt</translation>
+<translation id="8205463626947051446">Webbplatsen brukar visa påträngande annonser</translation>
<translation id="8218327578424803826">Tilldelad plats:</translation>
<translation id="8225771182978767009">Personen som konfigurerade datorn har valt att blockera den här webbplatsen.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Öppna sidan på en ny inkognitoflik</translation>
<translation id="8241707690549784388">Önskad sida använder information som du har angett. Om du återgår till sidan kan eventuella åtgärder på sidan upprepas. Vill du fortsätta?</translation>
+<translation id="8241712895048303527">Blockera på den här webbplatsen</translation>
<translation id="8249320324621329438">Senast hämtad:</translation>
<translation id="8253091569723639551">En faktureringsadress måste anges</translation>
<translation id="8261506727792406068">Radera</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Det gick inte att översätta på grund av ett nätverksfel.</translation>
<translation id="8332188693563227489">Åtkomst nekades till <ph name="HOST_NAME" />.</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="8344669043927012510">Öppna sidan i inkognitoläge (⇧⌘N)</translation>
<translation id="8349305172487531364">Bokmärkesfältet</translation>
<translation id="8363502534493474904">inaktivera flygplansläget</translation>
<translation id="8364627913115013041">Inte angiven.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Kraschrapporten laddades upp den <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Krascher (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Du måste ange samma lösenfras två gånger.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Inställningar</translation>
<translation id="8433057134996913067">Alternativet innebär att du loggas ut från de flesta webbplatser.</translation>
<translation id="8437238597147034694">&amp;Ångra Flytta</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Logga in i Chrome om du vill använda kort i Google-kontot</translation>
<translation id="8488350697529856933">Gäller för</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> tog för lång tid på sig att svara.</translation>
-<translation id="8532105204136943229">Sista giltighetsår</translation>
+<translation id="8503813439785031346">Användarnamn</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="8571890674111243710">Översätter sidan till <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Ange telefonnr
@@ -887,6 +919,7 @@
<translation id="859285277496340001">Certifikatet har inte någon specificerad mekanism för att kontrollera om det har återkallats.</translation>
<translation id="8620436878122366504">Dina föräldrar har inte godkänt den ännu</translation>
<translation id="8647750283161643317">Återställ alla till standardvärden</translation>
+<translation id="8660471606262461360">Från Google Payments</translation>
<translation id="8703575177326907206">Din anslutning till <ph name="DOMAIN" /> är inte krypterad.</translation>
<translation id="8718314106902482036">Betalningen slutfördes inte</translation>
<translation id="8725066075913043281">Försök igen</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Zooma in</translation>
<translation id="8931333241327730545">Vill du spara det här kortet i ditt Google-konto?</translation>
<translation id="8932102934695377596">Klockan går efter</translation>
+<translation id="8938939909778640821">Godkända kreditkort och förbetalda kort</translation>
<translation id="8971063699422889582">Servercertifikatet har gått ut.</translation>
<translation id="8986494364107987395">Skicka användningsstatistik och kraschrapporter till Google automatiskt</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Blockera alltid den här webbplatsen</translation>
<translation id="9170848237812810038">&amp;Ångra</translation>
<translation id="917450738466192189">Servercertifikatet är ogiltigt.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> till}other{<ph name="SHIPPING_OPTION_PREVIEW" /> och <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> till}}</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>
<translation id="9207861905230894330">Det gick inte att lägga till artikeln.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">RENSA FORMULÄRET</translation>
<translation id="939736085109172342">Ny mapp</translation>
-<translation id="941721044073577244">Du har tyvärr inte behörighet att besöka den här webbplatsen.</translation>
<translation id="969892804517981540">Officiell version</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Ingen}=1{1 objekt}other{# objekt}}</translation>
+<translation id="981121421437150478">Offline</translation>
<translation id="988159990683914416">Utvecklarversion</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_sw.xtb b/chromium/components/strings/components_strings_sw.xtb
index c4ee1c80c08..ca5e4ea1d97 100644
--- a/chromium/components/strings/components_strings_sw.xtb
+++ b/chromium/components/strings/components_strings_sw.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Funga programu nyingine</translation>
<translation id="1055184225775184556">Tendua Kuongeza</translation>
<translation id="10614374240317010">Haijahifadhiwa kamwe</translation>
+<translation id="1066396345355680611">Utapoteza idhini ya kufikia maudhui yanayolindwa kwenye <ph name="SITE" /> na tovuti nyingine.</translation>
<translation id="106701514854093668">Alamisho za Eneokazi</translation>
<translation id="1074497978438210769">Si salama</translation>
<translation id="1080116354587839789">Fanya itoshe kwenye upana</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Orodha ya kusoma</translation>
<translation id="1264126396475825575">Ripoti ya kuacha kufanya kazi iliyochukuliwa <ph name="CRASH_TIME" /> (haijapakiwa au imepuuzwa)</translation>
<translation id="1281526147609854549">Imetolewa na <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Maudhui hatari yamezuiwa</translation>
<translation id="1285320974508926690">Kamwe usitafsiri tovuti hii</translation>
<translation id="129553762522093515">Vilivyofungwa hivi karibuni</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Jaribu kufuta vidakuzi kwenye kivinjari chako<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Kikoa cha kujiandikisha:</translation>
<translation id="1340482604681802745">Anwani ya eneo la kuchukulia</translation>
-<translation id="1344211575059133124">Inaonekana unahitaji ruhusa ili uweze kutembelea tovuti hii</translation>
<translation id="1344588688991793829">Mipangilio ya Chromium ya kujaza kiotomatiki...</translation>
<translation id="1348198688976932919">Kuna programu hasidi kwenye tovuti unayotaka kuifungua</translation>
<translation id="1374468813861204354">mapendekezo</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Utambulisho wa <ph name="ORGANIZATION" /> iliyo <ph name="LOCALITY" /> umethibitishwa na <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Ndio</translation>
<translation id="1430915738399379752">Chapisha</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Onyesha nakala iliyohifadhiwa (yaani inayojulikana kwisha muda) ya ukurasa huu.</translation>
<translation id="1517433312004943670">Nambari ya simu inahitajika</translation>
+<translation id="1517500485252541695">Kadi za mikopo na za malipo zinazokubaliwa</translation>
<translation id="1519264250979466059">Unda Tarehe</translation>
+<translation id="1527263332363067270">Inasubiri muunganisho...</translation>
<translation id="153384715582417236">Hayo yanatosha kwa sasa</translation>
<translation id="1549470594296187301">Lazima JavaScript iwashwe ili utumie kipengele hiki.</translation>
<translation id="1555130319947370107">Samawati</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Pakua ukurasa baadaye</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Lazima sehemu hii ijazwe</translation>
<translation id="187918866476621466">Fungua kurasa zinazoanza</translation>
<translation id="1883255238294161206">Kunja orodha</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Kuchuja</translation>
+<translation id="1916770123977586577">Pakia upya ukurasa huu ili mipangilio iliyosasishwa itumike katika tovuti hii</translation>
+<translation id="1919345977826869612">Matangazo</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Hamna}=1{Tovuti 1}other{Tovuti #}}</translation>
<translation id="194030505837763158">Nenda kwenye <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Kadi za kulipia awali zinazokubaliwa</translation>
<translation id="1962204205936693436">Alamisho za <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Hitilafu ya namba tambulishi</translation>
<translation id="1974060860693918893">Mipangilio ya kina</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Hitilfau ya HTTP</translation>
<translation id="2270484714375784793">Nambari ya simu</translation>
<translation id="2282872951544483773">Majaribio Yasiyopatikana</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{Kipengee <ph name="ITEM_COUNT" />}other{Vipengee <ph name="ITEM_COUNT" />}}</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>
<translation id="2337852623177822836">Mipangilio inadhibitiwa na msimamizi wako</translation>
<translation id="2354001756790975382">Alamisho zingine</translation>
<translation id="2354430244986887761">Kipengele cha Kuvinjari Salama kwenye Google <ph name="BEGIN_LINK" />kilipata programu hasidi<ph name="END_LINK" /> kwenye <ph name="SITE" /> hivi majuzi.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Endelea</translation>
<translation id="2365563543831475020">Ripoti ya kuacha kufanya kazi iliyochukuliwa <ph name="CRASH_TIME" /> haikupakiwa</translation>
<translation id="2367567093518048410">Kiwango</translation>
-<translation id="237718015863234333">Hakuna chaguo mbadala za kiolesura zinazopatikana</translation>
<translation id="2384307209577226199">Biashara chaguo-msingi</translation>
<translation id="2386255080630008482">Cheti cha seva kimebatilishwa.</translation>
<translation id="2392959068659972793">Onyesha sera zisizowekwa thamani</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Ikitiwa tiki, Chromium itahifadhi nakala ya kadi yako kwenye kifaa hiki kwa ajili ya kujaza fomu haraka zaidi.</translation>
<translation id="2498091847651709837">Changanua kadi mpya</translation>
<translation id="2501278716633472235">Rudi nyuma</translation>
+<translation id="2503184589641749290">Kadi za malipo na za kulipia awali zinazokubaliwa</translation>
<translation id="2515629240566999685">Kuangalia uthabiti wa mawimbi katika eneo lako</translation>
-<translation id="2516305470678292029">Chaguo mbadala za kiolesura</translation>
<translation id="2539524384386349900">Gundua</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> imetuma jibu ambalo si sahihi.</translation>
<translation id="2556876185419854533">Tendua Kuhariri</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Mbinu ya usafirishaji</translation>
<translation id="277499241957683684">Rekodi ya kifaa inayokosekana</translation>
<translation id="2784949926578158345">Muunganisho uliwekwa upya.</translation>
+<translation id="2788784517760473862">Kadi za malipo zinazokubaliwa</translation>
<translation id="2794233252405721443">Tovuti imezuiwa</translation>
<translation id="2799020568854403057">Kuna programu hasidi kwenye tovuti unayotaka kuifungua</translation>
<translation id="2803306138276472711">Mfumo wa Google wa Kuvinjari kwa Usalama {<ph name="BEGIN_LINK" />uligundua programu hasidi<ph name="END_LINK" /> kwenye <ph name="SITE" /> hivi karibuni. Tovuti ambazo kwa kawaida huwa salama wakati mwingine huathiriwa na programu hasidi.</translation>
<translation id="2824775600643448204">Upau wa anwani na utafutaji</translation>
<translation id="2826760142808435982">Muunganisho umesimbwa kwa njia fiche na kuthibitishwa kupitia <ph name="CIPHER" /> na unatumia <ph name="KX" /> kama utaratibu msingi wa ubadilishaji.</translation>
<translation id="2835170189407361413">Futa fomu</translation>
+<translation id="2851634818064021665">Unahitaji ruhusa ili utembelee tovuti hii</translation>
<translation id="2856444702002559011">Huenda wavamizi wanajaribu kuiba maelezo yako kutoka <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (kwa mfano, manenosiri, ujumbe au kadi za mikopo).<ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Usipakie Upya</translation>
-<translation id="2900469785430194048">Nafasi ya kuhifadhi kwenye Google Chrome iliisha ilipokuwa ikijaribu kuonyesha ukurasa huu wa wavuti.</translation>
<translation id="2909946352844186028">Mabadiliko ya mtandao yamegunduliwa.</translation>
<translation id="2916038427272391327">Funga programu nyingine</translation>
<translation id="2922350208395188000">Cheti cha seva hakiwezi kukaguliwa.</translation>
<translation id="2928905813689894207">Anwani ya kutuma Bili</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; cheti chake cha usalama kinatoka <ph name="DOMAIN2" />. Hii inaweza kusababishwa na usanidi usiofaa au mvamizi kuingilia muunganisho wako.</translation>
<translation id="2948083400971632585">Unaweza kuzima proksi zozote zilizosanidiwa kwa muunganisho kutoka kwenye ukurasa wa mipangilio.</translation>
<translation id="2955913368246107853">Funga upau wa kupata</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Ikiwashwa, Chromium itahifadhi nakala ya kadi yako kwenye kifaa hiki kwa ajili ya kujaza fomu haraka zaidi.</translation>
<translation id="2985398929374701810">Andika anwani sahihi</translation>
<translation id="2986368408720340940">Mbinu hii ya kuchukua haipatikani. Jaribu mbinu tofauti.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Kurasa unazoziangalia katika vichupo fiche hazitaendelea kuwepo katika historia ya kivinjari, hifadhi ya vidakuzi, au historia yako ya utafutaji ukishafunga vichupo vyako vyote fiche. Faili zozote unazopakua au alamisho unazounda hazitafutwa.</translation>
<translation id="3169472444629675720">Gundua</translation>
<translation id="3174168572213147020">Kisiwa</translation>
-<translation id="317583078218509884">Mipangilio mipya ya vibali vya tovuti itaanza kufanya kazi baada ya kupakia upya ukurasa huu.</translation>
<translation id="3176929007561373547">Angalia mipangilio yako ya seva mbadala au wasiliana na msimamizi wako wa mtandao ili
kuhakikisha kuwa seva mbadala inafanya kazi. Ikiwa huamini kwamba unapaswa kuwa
ukitumia seva mbadala:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Fungua ukurasa katika hali Fiche</translation>
<translation id="320323717674993345">Ghairi Malipo</translation>
<translation id="3207960819495026254">Imealamishwa</translation>
<translation id="3225919329040284222">Seva imewasilisha cheti kisicholingana na matarajio ya kijenzi cha ndani. Matarajio haya yanajumlishwa kwa baadhi ya tovuti za usalama wa juu ili kukulinda.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">Tendua Kupanga upya</translation>
<translation id="3282497668470633863">Ongeza jina kwenye kadi</translation>
<translation id="3286538390144397061">Zima na uwashe sasa</translation>
+<translation id="3287510313208355388">Pakua ukiwa mtandaoni</translation>
<translation id="3303855915957856445">Hakuna matokeo ya utafutaji yaliyopatikana</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Muda unaotumika kuleta:</translation>
<translation id="3462200631372590220">Ficha mahiri</translation>
<translation id="3467763166455606212">Jina la mwenye kadi linahitajika</translation>
-<translation id="3478058380795961209">Mwezi wa Muda wa Matumizi Kuisha</translation>
<translation id="3479539252931486093">Je, hukutarajia tukio hili? <ph name="BEGIN_LINK" />Tujulishe<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Sio sasa</translation>
<translation id="3498215018399854026">Hatukuweza kufikia mzazi wako wakati huu. Tafadhali jaribu tena.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Tafadhali rekebisha tarehe na wakati kutoka sehemu ya &lt;strong&gt;Jumla&lt;/strong&gt; ya programu ya &lt;strong&gt;Mipangilio&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kadi za mikopo na za kulipia awali zinazokubaliwa.</translation>
<translation id="3582930987043644930">Ongeza jina</translation>
<translation id="3583757800736429874">Rudia Hatua</translation>
<translation id="3586931643579894722">Ficha maelezo</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Ikiwa unaliona tatizo hili mara kwa mara, jaribu <ph name="HELP_LINK" /> haya.</translation>
<translation id="3658742229777143148">Marekebisho</translation>
<translation id="3678029195006412963">Ombi halikutiwa sahihi</translation>
+<translation id="3678529606614285348">Fungua ukurasa kwenye dirisha fiche jipya (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Ripoti ya kuacha kufanya kazi ilitolewa <ph name="CRASH_TIME" /> na kupakiwa <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Maelezo ya cheti</translation>
<translation id="3690164694835360974">Kuingia katika akaunti si salama</translation>
-<translation id="3693415264595406141">Nenosiri:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Inapakia...</translation>
<translation id="3712624925041724820">Leseni zimekwisha</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Wavamizi kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> huenda wakakuhadaa ufanye kitu hatari kama vile kusakinisha programu au kuonyesha maelezo yako ya binafsi (kwa mfano, manenosiri, nambari za simu au kadi za mikopo). <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Utafsiri haukufanikiwa kwa sababu ya hitilafu ya seva.</translation>
<translation id="3759461132968374835">Huna uharibifu ulioripotiwa hivi karibuni. Uharibifu uliotokea wakati kuripoti kwa uharibifu kulipolemazwa hakutaonekana hapa.</translation>
+<translation id="3765032636089507299">Ukurasa wa Kuvinjari Salama unaboreshwa.</translation>
<translation id="3778403066972421603">Je, ungependa kuhifadhi kadi hii kwenye Akaunti yako ya Google na kwenye kifaa hiki?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Muda wa matumizi utakwisha <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Ombi lako la kufikia tovuti hii limetumwa kwa <ph name="NAME" /></translation>
<translation id="3890664840433101773">Ongeza anwani ya barua pepe</translation>
<translation id="3901925938762663762">Kadi imekwisha muda</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Kitambulisho cha Ripoti ya Kuacha Kufanya Kazi Kilichopakiwa <ph name="CRASH_ID" /> (Kitambulisho cha Kuacha Kufanya Kazi cha Ndani ya Kifaa: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; cheti chake cha usalama hakibainishi Majina Mbadala ya Mada. Hii inaweza kusababishwa na uwekaji mipangilio usiofaa au muunganisho wako kukatwa na mvamizi.</translation>
+<translation id="3949601375789751990">Historia yako ya kuvinjari itaonekana hapa</translation>
<translation id="3963721102035795474">Hali ya Usomaji</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Hamna}=1{Kutoka kwenye tovuti 1 }other{Kutoka kwenye tovuti # }}</translation>
<translation id="397105322502079400">Inakokotoa...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Mipangilio ya tovuti</translation>
<translation id="4169947484918424451">Je, unataka Chromium ihifadhi kadi hii?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Huenda wavamizi walio kwenye tovuti hii wakajaribu kusakinisha programu hatari inayoiba au kufuta maelezo yako yaliyo kwenye kompyuta yako (kwa mfano, picha, manenosiri, ujumbe, na kadi za mikopo).</translation>
<translation id="4372948949327679948">Thamani <ph name="VALUE_TYPE" /> inayotarajiwa.</translation>
<translation id="4377125064752653719">Ulijaribu kufikia <ph name="DOMAIN" />, lakini cheti kilichowasilishwa na seva kimebatilishwa na mtoaji wacho. Huku ni kumaanisha kuwa stakabadhi za usalama zilizowasilishwa na seva hii hazifai kuaminiwa kabisa. Huenda ukawa unawasiliana na mshabulizi.</translation>
-<translation id="4381091992796011497">Jina la mtumiaji:</translation>
<translation id="4394049700291259645">Zima</translation>
<translation id="4406896451731180161">matokeo ya utafutaji</translation>
<translation id="4424024547088906515">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; cheti chake cha usalama hakiaminiwi na Chrome. Hii inaweza kusababishwa na usanidi usiofaa au mvamizi kuingilia muunganisho wako.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> haikukubali cheti chako cha kuingia katika akaunti, au huenda hukutoa cheti.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Ungependa kuondoa anwani kutoka kwenye Chrome?</translation>
<translation id="4592951414987517459">Muunganisho wako kwenye <ph name="DOMAIN" /> umesimbwa kwa njia fiche kwa kutumia mipangilio ya kriptografia ya kisasa.</translation>
<translation id="4594403342090139922">Tendua Kufuta</translation>
+<translation id="4611292653554630842">Ingia katika akaunti</translation>
<translation id="4619615317237390068">Vichupo kutoka kwenye vifaa vingine</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; cheti chake cha usalama kina hitilafu. Hii inaweza kusababishwa na usanidi usiofaa au mvamizi kuingilia muunganisho wako.</translation>
<translation id="4690462567478992370">Acha kutumia cheti kisicho sahihi</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Muunganisho wako umekatizwa</translation>
+<translation id="471880041731876836">Huna ruhusa ya kutembelea tovuti hii</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Kuendesha Zana ya Windows ya Kuchunguza Mtandao<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Pakia sera upya</translation>
<translation id="4728558894243024398">Mfumo wa uendeshaji</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Maudhui haya yanaweza kukuhadaa kusakinisha programu au kuonyesha maelezo yako ya binafsi. <ph name="BEGIN_LINK" />Onyesha<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Tafuta katika historia</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Unahitaji kuthibitisha</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{na ukurasa 1 zaidi wa wavuti}other{na kurasa # zaidi za wavuti}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Ukurasa huu umetafsiriwa kutoka katika lugha ambayo haijulikani hadi <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Malipo</translation>
<translation id="4926049483395192435">Sharti ibainishwe.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Vitendo</translation>
<translation id="4958444002117714549">Panua orodha</translation>
<translation id="4974590756084640048">Onyesha maonyo tena</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Nenosiri lisilo sahihi</translation>
<translation id="5056549851600133418">Makala kwa ajili yako</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Kuangalia anwani mbadala<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Utapoteza idhini ya kufikia maudhui yanayolindwa kwenye tovuti nyingine.</translation>
<translation id="5087286274860437796">Cheti cha seva si sahihi kwa sasa.</translation>
<translation id="5087580092889165836">Ongeza kadi</translation>
<translation id="5089810972385038852">Jimbo</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Mkoa</translation>
<translation id="5115563688576182185">(biti 64)</translation>
<translation id="5141240743006678641">Simba kwa njia fiche manenosiri yaliyosawazishwa ukitumia stakabadhi zako za Google</translation>
-<translation id="514421653919133810">Fungua ukurasa katika hali Fiche (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Msimbo wa hitilafu uko katika jibu la sera</translation>
+<translation id="5159010409087891077">Fungua ukurasa kwenye dirisha fiche jipya (⇧⌘N)</translation>
<translation id="5171045022955879922">Tafuta au charaza URL</translation>
<translation id="5172758083709347301">Mashine</translation>
<translation id="5179510805599951267">Haiko katika <ph name="ORIGINAL_LANGUAGE" />? Ripoti hitilafu hii</translation>
-<translation id="5181140330217080051">Inapakua</translation>
<translation id="5190835502935405962">Sehemu ya Alamisho</translation>
<translation id="5199729219167945352">Majaribio</translation>
<translation id="5205222826937269299">Jina linahitajika</translation>
<translation id="5222812217790122047">Anwani ya barua pepe inahitajika</translation>
<translation id="5251803541071282808">Wingu</translation>
<translation id="5277279256032773186">Je, unatumia Chrome kazini? Kampuni zinaweza kudhibiti mipangilio ya Chrome ya wafanyikazi wao. Pata maelezo zaidi</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Zifuate hatua hizi za kuisitisha programu kwa muda ili ufike kwenye wavuti. Utahitaji mamlaka ya msimamizi.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Bofya <ph name="BEGIN_BOLD" />Anza<ph name="END_BOLD" />, kisha utafute na uchague <ph name="BEGIN_BOLD" />"Angalia huduma kwenye kifaa"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Chagua<ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Chini ya <ph name="BEGIN_BOLD" />aina ya Kuanzisha<ph name="END_BOLD" />, chagua<ph name="BEGIN_BOLD" />Imezimwa<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Chini ya <ph name="BEGIN_BOLD" />hali ya Huduma<ph name="END_BOLD" />, bofya <ph name="BEGIN_BOLD" />Simamisha<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Bofya <ph name="BEGIN_BOLD" />Tumia<ph name="END_BOLD" />, kisha bofya <ph name="BEGIN_BOLD" />Sawa<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Tembelea <ph name="BEGIN_LEARN_MORE_LINK" />kituo cha usaidizi cha Chrome<ph name="END_LEARN_MORE_LINK" /> ili upate maelezo zaidi kuhusu jinsi ya kuiondoa kabisa programu kwenye kompyuta yako
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Muunganisho wako kwenye tovuti hii si wa faragha. Ili uondoke kwenye hali ya VR wakati wowote, ondoa vifaa vya kutazama uhalisia pepe na ubonyeze tena.</translation>
<translation id="5299298092464848405">Hitilafu wakati wa kuchanganua sera</translation>
<translation id="5308689395849655368">Kuripoti uharibifu kumelemazwa.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Hitilafu katika uhalalishaji wa Skima " <ph name="ERROR_PATH" /> ": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Ukurasa wa <ph name="HOST_NAME" /> hii haukupatikana</translation>
<translation id="5455374756549232013">Muhuri wa muda wa sera mbaya</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> ya <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Haiwezi kutumika</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> na mwingine <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> na wengine <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">Rudia kuhariri</translation>
+<translation id="5481076368049295676">Huenda maudhui haya yakajaribu kusakinisha programu hatari inayoiba au kufuta maelezo yaliyo kwenye kifaa chako. <ph name="BEGIN_LINK" />Onyesha tu<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Ongeza anwani sahihi ya barua pepe</translation>
<translation id="5492298309214877701">Tovuti hii kwenye intraneti ya kampuni, shirika au shule ina URL sawa na tovuti ya nje.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Haikuweza kupata makala yaliyoitishwa.</translation>
<translation id="5544037170328430102">Ukurasa uliopachikwa kwenye <ph name="SITE" /> unasema:</translation>
<translation id="5556459405103347317">Pakia upya</translation>
+<translation id="5560088892362098740">Tarehe ya Mwisho wa Matumizi</translation>
<translation id="5565735124758917034">Inatumika</translation>
<translation id="5571083550517324815">Haiwezi kuchukua kutoka kwenye anwani hii. Chagua anwani tofauti.</translation>
<translation id="5572851009514199876">Tafadhali anza na uingie katika Chrome ili Chrome iangalie ikiwa unaruhusiwa kufikia tovuti hii.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Imeshindwa kupakia mipangilio ya sera</translation>
<translation id="5631439013527180824">Ishara ya usimamizi wa kifaa batili</translation>
<translation id="5633066919399395251">Wavamizi ambao sasa wako kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> huenda wakajaribu kusakinisha programu hatari kwenye kompyuta yako ambazo zinaiba au kufuta maelezo yako (kwa mfano, picha, manenosiri, ujumbe na kadi za mikopo). <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Maudhui ya udanganyifu yamezuiwa.</translation>
<translation id="5646376287012673985">Mahali</translation>
<translation id="5659593005791499971">Barua pepe</translation>
<translation id="5669703222995421982">Pata maudhui yanayokufaa</translation>
<translation id="5675650730144413517">Ukurasa huu haufanyi kazi</translation>
<translation id="5710435578057952990">Utambulisho wa tovuti hii haujathibitishwa.</translation>
-<translation id="5713016350996637505">Maudhui danganyifu yamezuiwa</translation>
+<translation id="5719499550583120431">Kadi za kulipia awali zinakubaliwa.</translation>
<translation id="5720705177508910913">Mtumiaji wa sasa</translation>
<translation id="5732392974455271431">Wazazi wako wanaweza kukuondolea kizuizi</translation>
<translation id="5763042198335101085">Andika anwani sahihi ya barua pepe</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Ungependa kujaza maelezo ya <ph name="CARD_DETAIL" /> yako?</translation>
<translation id="5810442152076338065">Muunganisho wako kwenye <ph name="DOMAIN" /> umesimbwa kwa njia fiche kwa kutumia mipangilio ya kriptografia ya zamani.</translation>
<translation id="5813119285467412249">Rudia Kuongeza</translation>
-<translation id="5814352347845180253">Utapoteza idhini ya kufikia maudhui ya kulipiwa kutoka kwenye <ph name="SITE" /> na tovuti nyingine.</translation>
<translation id="5838278095973806738">Hupaswi kuweka maelezo nyeti kwenye tovuti hii (kwa mfano, manenosiri au kadi za mikopo), kwa sababu wavamizi wanaweza kuyaiba.</translation>
<translation id="5869405914158311789">Tovuti hii haiwezi kufikiwa</translation>
<translation id="5869522115854928033">Manenosiri yaliyohifadhiwa</translation>
<translation id="5872918882028971132">Mapendekezo ya Wazazi</translation>
+<translation id="5893752035575986141">Kadi za mikopo zinakubaliwa.</translation>
<translation id="5901630391730855834">Manjano</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (imesawazishwa)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 kinatumika}other{ # vinatumika}}</translation>
-<translation id="5926846154125914413">Utapoteza idhini ya kufikia maudhui ya kulipiwa kutoka kwenye tovuti nyingine.</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="5967867314010545767">Ondoa kwenye historia</translation>
<translation id="5975083100439434680">Fifiza</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Funga</translation>
<translation id="6042308850641462728">Zaidi</translation>
<translation id="6047233362582046994">Ikiwa unaelewa hatari kwa usalama wako, unaweza <ph name="BEGIN_LINK" />kuitembelea tovuti hii<ph name="END_LINK" /> kabla programu hasidi hazijaondolewa.</translation>
+<translation id="6047927260846328439">Maudhui haya yanaweza kukuhadaa kusakinisha programu au kuonyesha maelezo yako ya binafsi. <ph name="BEGIN_LINK" />Onyesha tu<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Huwezi kutembelea <ph name="SITE" /> sasa hivi kwa sababu tovuti hii inatumia ubandikaji cheti. Hitilafu na uvamizi wa mtandao kwa kawaida huwa vya muda, kwa hivyo ukurasa huu huenda utafanya kazi baadaye.</translation>
<translation id="6060685159320643512">Tahadhari, majaribio haya yanaweza kusumbua</translation>
<translation id="6080696365213338172">Umefikia maudhui kwa kutumia cheti kilichotolewa cha msimamizi. Data unayotoa katika <ph name="DOMAIN" /> inaweza kuzuiliwa na msimamizi wako.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Pata maelezo zaidi</translation>
<translation id="6169916984152623906">Sasa unaweza kuvinjari kwa faragha na watu wengine wanaotumia kifaa hiki hawataona shughuli zako. Hata hivyo, vipakuliwa na alamisho zitahifadhiwa.</translation>
<translation id="6177128806592000436">Muunganisho wako kwenye tovuti hii si salama</translation>
-<translation id="6184817833369986695">(kundi: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Angalia muunganisho wako wa Intaneti</translation>
<translation id="6218753634732582820">Je, ungependa kuondoa anwani kwenye Chromium?</translation>
<translation id="6221345481584921695">Mfumo wa Google wa Kuvinjari kwa Usalama <ph name="BEGIN_LINK" />uligundua programu hasidi<ph name="END_LINK" /> kwenye <ph name="SITE" /> hivi karibuni. Tovuti ambazo kwa kawaida huwa salama wakati mwingine huathiriwa na programu hasidi. Maudhui hasidi hutoka kwa <ph name="SUBRESOURCE_HOST" />, msambazaji wa programu hasidi anayejulikana.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Unavutiwa na vipengee vipya vizuri vya Chrome? Jaribu kituo chetu cha beta katika chrome.com/beta.</translation>
-<translation id="6389758589412724634">Nafasi ya kuhifadhi kwenye Chromium iliisha ilipokuwa ikijaribu kuonyesha ukurasa huu wa wavuti.</translation>
+<translation id="6397451950548600259">Programu kwenye kompyuta yako inayoizuia Chrome kuunganisha salama kwenye wavuti</translation>
<translation id="6404511346730675251">Badilisha alamisho</translation>
<translation id="6410264514553301377">Weka tarehe ya mwisho wa matumizi na CVC ya <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Umewaomba wazazi wako ruhusa ya kuitembelea tovuti hii</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Sera za kifaa</translation>
<translation id="6477321094435799029">Chrome imegundua nambari ya kuthibitisha isiyo ya kawaida kwenye ukurasa huu na ikaizuia ili kulinda maelezo ya binafsi (kwa mfano, manenosiri, nambari za simu na kadi za mikopo).</translation>
<translation id="6489534406876378309">Anza kupakia matukio ya kuacha kufanya kazi</translation>
+<translation id="6507833130742554667">Kadi za mikopo na malipo zinakubaliwa.</translation>
<translation id="6508722015517270189">Zima na uwashe Chrome</translation>
<translation id="6529602333819889595">Rudia Kufuta</translation>
<translation id="6534179046333460208">Mapendekezo ya Wavuti kila Mahali</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Huenda maudhui haya yakajaribu kusakinisha programu hatari inayoiba au kufuta maelezo yako kwenye kifaa chako. <ph name="BEGIN_LINK" />Onyesha<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Chaguo za usimbaji fiche</translation>
<translation id="662080504995468778">Usiondoke</translation>
<translation id="6626291197371920147">Ongeza nambari sahihi ya kadi</translation>
<translation id="6628463337424475685">Utafutaji wa <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Wavamizi ambao sasa wako kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> huenda wakajaribu kusakinisha programu hatari kwenye Mac 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="6644283850729428850">Sera hii imepingwa.</translation>
+<translation id="6657585470893396449">Nenosiri</translation>
<translation id="6671697161687535275">Je, ungependa kuondoa pendekezo la fomu kwenye Chromium?</translation>
<translation id="6685834062052613830">Ondoka na ukamilishe kuweka mipangilio</translation>
<translation id="6710213216561001401">Iliyotangulia</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Wilaya</translation>
<translation id="6973656660372572881">Seva zote za proksi thabiti na URL ya hati ya .pac zimebainishwa.</translation>
<translation id="6989763994942163495">Onyesha mipangilio ya kina...</translation>
-<translation id="7000990526846637657">Hakuna historia ya mambo uliyovinjari inayopatikana</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Huenda Akaunti yako ya Google ina aina nyingine za historia ya kuvinjari kwenye <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Manenosiri</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Haiwezi kusafirisha kwenda kwenye anwani hii. Chagua anwani tofauti.</translation>
<translation id="7138472120740807366">Njia ya kusafirisha</translation>
<translation id="7139724024395191329">Emirate</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Malipo si salama</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Zindua upya Sasa</translation>
<translation id="7180611975245234373">Onyesha upya</translation>
<translation id="7182878459783632708">Hakuna sera zilizowekwa</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Pakua</translation>
<translation id="7518003948725431193">Hakuna ukurasa wa wavuti uliopatikana kwa anwani hii ya wavuti: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Muunganisho wako kwenye tovuti hii si wa faragha</translation>
<translation id="7535087603100972091">Thamani</translation>
<translation id="7537536606612762813">Lazima</translation>
<translation id="7542403920425041731">Baada ya kuthibitisha, maelezo ya kadi yako yatashirikiwa na tovuti hii.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Jaribu vidokezo vinavyofuata:</translation>
<translation id="7554791636758816595">Kichupo Kipya</translation>
<translation id="7567204685887185387">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; huenda cheti chake cha usalama kimetolewa kwa njia ya ulaghai. Hii inaweza kusababishwa na usanidi usiofaa au mvamizi kuingilia muunganisho wako.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Ukurasa huu umeandikwa kwa<ph name="ORIGINAL_LANGUAGE" />Je, ungependa kuutafsiri?</translation>
<translation id="7569952961197462199">Ungependa kuondoa kadi ya malipo kutoka kwenye Chrome?</translation>
<translation id="7569983096843329377">Nyeusi</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Cheti</translation>
<translation id="7716147886133743102">Imezuiwa na msimamizi</translation>
<translation id="7716424297397655342">Faili hii haiwezi kupakiwa kutoka akiba</translation>
+<translation id="774634243536837715">Maudhui hatari yamezuiwa.</translation>
<translation id="7752995774971033316">Haidhibitiwi</translation>
<translation id="7755287808199759310">Mzazi wako anaweza kukuondolea kizuizi</translation>
<translation id="7758069387465995638">Huenda programu ya kinga-mtandao au kinga-virusi zimezuia muunganisho huu.</translation>
+<translation id="7759163816903619567">Onyesha kikoa:</translation>
<translation id="7761701407923456692">Cheti cha seva hakilingani na URL.</translation>
<translation id="7763386264682878361">Kichanganuzi cha Faili za Maelezo ya Malipo</translation>
<translation id="7764225426217299476">Ongeza anwani</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Imepata matokeo <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> ya '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">Hata hivyo, huonekani. Kuvinjari katika hali fiche hakufichi kuvinjari kwako kusionekane na mwajiri, mtoaji huduma wako wa intaneti, au tovuti unazotembelea.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Kadi za malipo na za kulipia awali zinakubaliwa.</translation>
<translation id="7887683347370398519">Angalia CVC yako na ujaribu tena</translation>
<translation id="79338296614623784">Andika nambari sahihi ya simu</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Tuma Maoni</translation>
<translation id="8041940743680923270">Tumia chaguo-msingi la duniani (Uliza)</translation>
<translation id="8088680233425245692">Haikufaulu kuangalia makala.</translation>
-<translation id="8089520772729574115">chini ya MB 1</translation>
<translation id="8091372947890762290">Uwashaji unasubiri kwenye seva</translation>
<translation id="8118489163946903409">Njia ya kulipa</translation>
<translation id="8131740175452115882">Thibitisha</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">Tendua hatua</translation>
<translation id="8201077131113104583">URL ya sasisho si sahihi kwa kiendelezi chenye Kitambulisho "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Muhtasari wa agizo</translation>
+<translation id="8205463626947051446">Tovuti hii huonyesha matangazo yanayokatiza matumizi</translation>
<translation id="8218327578424803826">Mahali Palipohawilishwa:</translation>
<translation id="8225771182978767009">Mtu ambaye aliweka mipangilio ya kompyuta hii ameamua kuzuia tovuti hii.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Fungua ukurasa kwenye kichupo fiche kipya</translation>
<translation id="8241707690549784388">Ukurasa unaotafuta ulitumia maelezo uliyoyaingiza. Kurudi kwenye ukurasa huo huenda kukasababisha tendo lolote ulilofanya lirudiwe. Je, ungependa kuendelea?</translation>
+<translation id="8241712895048303527">Zuia kwenye tovuti hii</translation>
<translation id="8249320324621329438">Iliyoletwa mwisho:</translation>
<translation id="8253091569723639551">Anwani ya kutuma bili sharti iandikwe</translation>
<translation id="8261506727792406068">Futa</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Utafsiri haukufanikiwa kwa sababu ya hitilafu ya seva.</translation>
<translation id="8332188693563227489">Ufikiaji wa <ph name="HOST_NAME" /> umekataliwa</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="8344669043927012510">Fungua ukurasa katika hali Fiche (⇧⌘N)</translation>
<translation id="8349305172487531364">Sehemu ya Alamisho</translation>
<translation id="8363502534493474904">Kuzima hali ya ndegeni</translation>
<translation id="8364627913115013041">Haijawekwa.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Ripoti ya kuacha kufanya kazi ilipakiwa <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Mivurugo ( <ph name="CRASH_COUNT" /> )</translation>
<translation id="8412392972487953978">Lazima uingize kaulisiri ile ile mara mbili.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Mipangilio</translation>
<translation id="8433057134996913067">Kufanya hivyo kutakuondoa kwenye tovuti nyingi.</translation>
<translation id="8437238597147034694">Tendua hatua</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Ili utumie kadi kutoka kwenye Akaunti yako ya Google, ingia katika Chrome</translation>
<translation id="8488350697529856933">Inatumika kwenye</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> imechukua muda mrefu kupakia.</translation>
-<translation id="8532105204136943229">Mwaka wa Muda wa Matumizi Kuisha</translation>
+<translation id="8503813439785031346">Jina la mtumiaji</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="8571890674111243710">Inatafsiri ukurasa katika <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Ongeza simu
@@ -887,6 +919,7 @@
<translation id="859285277496340001">Cheti hakibainishi utaratibu wa kuangalia iwapo kimekataliwa.</translation>
<translation id="8620436878122366504">Wazazi wako bado hawajaiidhinisha</translation>
<translation id="8647750283161643317">Weka upya zote kwa chaguo-msingi</translation>
+<translation id="8660471606262461360">Kutoka Google Payments</translation>
<translation id="8703575177326907206">Muunganisho wako kwa <ph name="DOMAIN" /> haujasimbwa.</translation>
<translation id="8718314106902482036">Malipo hayajakamilishwa</translation>
<translation id="8725066075913043281">Jaribu tena</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Kuza karibu</translation>
<translation id="8931333241327730545">Je, ungependa kuhifadhi kadi hii katika Akaunti yako ya Google?</translation>
<translation id="8932102934695377596">Saa yako iko nyuma</translation>
+<translation id="8938939909778640821">Kadi za mikopo na za kulipia awali zinazokubaliwa</translation>
<translation id="8971063699422889582">Cheti cha seva kimechina.</translation>
<translation id="8986494364107987395">Tumia Google takwimu za matumizi na ripoti za mara ambazo kivinjari kinaacha kufanya kazi, moja kwa moja</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Zuia kila wakati kwenye tovuti hii</translation>
<translation id="9170848237812810038">&amp;Tendua</translation>
<translation id="917450738466192189">Cheti cha seva ni batili.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> na nyingine <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</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>
<translation id="9207861905230894330">Haikufaulu kuongeza makala.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">FUTA FOMU</translation>
<translation id="939736085109172342">Folda mpya</translation>
-<translation id="941721044073577244">Inaonekana huna ruhusa ya kutembelea tovuti hii</translation>
<translation id="969892804517981540">Muundo Rasmi</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Hamna}=1{Kipengee 1}other{Vipengee #}}</translation>
+<translation id="981121421437150478">Nje ya mtandao</translation>
<translation id="988159990683914416">Muundo wa Wasanidi Programu</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_ta.xtb b/chromium/components/strings/components_strings_ta.xtb
index 9a573d501f7..ccd378e24ba 100644
--- a/chromium/components/strings/components_strings_ta.xtb
+++ b/chromium/components/strings/components_strings_ta.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">பிற பயன்பாடுகளை மூடவும்</translation>
<translation id="1055184225775184556">&amp;சேர்த்தலைச் செயல்தவிர்</translation>
<translation id="10614374240317010">எப்போதும் சேமிக்காதவை</translation>
+<translation id="1066396345355680611"><ph name="SITE" /> மற்றும் சில தளங்களின் பாதுகாக்கப்பட்ட உள்ளடக்க அணுகலை நீங்கள் இழக்கக்கூடும்.</translation>
<translation id="106701514854093668">டெஸ்க்டாப் புக்மார்க்குகள்</translation>
<translation id="1074497978438210769">பாதுகாப்பற்றது</translation>
<translation id="1080116354587839789">அகலத்திற்குப் பொருத்து</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">வாசிப்புப் பட்டியல்</translation>
<translation id="1264126396475825575"><ph name="CRASH_TIME" /> அன்று சிதைவு அறிக்கை பெறப்பட்டது (இன்னும் பதிவேற்றப்படவில்லை அல்லது புறக்கணிக்கப்படவில்லை)</translation>
<translation id="1281526147609854549">வழங்கியது: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">ஆபத்தான உள்ளடக்கம் தடுக்கப்பட்டது</translation>
<translation id="1285320974508926690">இந்த தளத்தை எப்போதும் மொழிபெயர்க்க வேண்டாம்</translation>
<translation id="129553762522093515">சமீபத்தில் மூடியவை</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />குக்கீகளை அழிக்கவும்<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">பதிவுக் களம்:</translation>
<translation id="1340482604681802745">பிக்கப் முகவரி</translation>
-<translation id="1344211575059133124">இந்தத் தளத்திற்குச் செல்ல, உங்களுக்கு அனுமதி தேவை</translation>
<translation id="1344588688991793829">Chromium தன்னிரப்பி அமைப்புகள்...</translation>
<translation id="1348198688976932919">தளத்தில் ஆபத்தான பயன்பாடுகள் உள்ளன</translation>
<translation id="1374468813861204354">பரிந்துரைகள்</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" /> இல் <ph name="ORGANIZATION" /> அடையாளம் <ph name="ISSUER" /> ஆல் பரிசோதிக்கப்பட்டது.</translation>
<translation id="1426410128494586442">ஆம்</translation>
<translation id="1430915738399379752">அச்சிடுக</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">இந்தப் பக்கத்தின் சேமித்த நகலைக் காட்டு (அதாவது, காலாவதியானது என அறியப்பட்டது).</translation>
<translation id="1517433312004943670">ஃபோன் எண் தேவை</translation>
+<translation id="1517500485252541695">ஏற்கப்படும் கிரெடிட் மற்றும் டெபிட் கார்டுகள்</translation>
<translation id="1519264250979466059">உருவாக்கிய தேதி</translation>
+<translation id="1527263332363067270">இணைப்பிற்காகக் காத்திருக்கிறது…</translation>
<translation id="153384715582417236">அவ்வளவு தான்!</translation>
<translation id="1549470594296187301">இந்த அம்சத்தைப் பயன்படுத்த JavaScript இயக்கப்பட வேண்டும்.</translation>
<translation id="1555130319947370107">நீலம்</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">கணினி நிர்வாகியைத் தொடர்புகொள்ளவும்</translation>
<translation id="1740951997222943430">சரியான காலாவதி மாதத்தை உள்ளிடவும்</translation>
-<translation id="1745358365027406341">பக்கத்தைப் பின்னர் பதிவிறக்கு</translation>
<translation id="17513872634828108">தாவல்களைத் திற</translation>
<translation id="1753706481035618306">பக்க எண்</translation>
<translation id="1763864636252898013">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழை உங்கள் சாதனத்தின் இயக்க முறைமை நம்பவில்லை. இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">அவசியம்</translation>
<translation id="187918866476621466">துவக்கப் பக்கங்களைத் திற</translation>
<translation id="1883255238294161206">பட்டியலைச் சுருக்கு</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">வடிகட்டுதல்</translation>
+<translation id="1916770123977586577">மாற்றிய அமைப்புகளை இந்தத் தளத்தில் பயன்படுத்த, பக்கத்தை மீண்டும் ஏற்றவும்</translation>
+<translation id="1919345977826869612">விளம்பரங்கள்</translation>
<translation id="192020519938775529">{COUNT,plural, =0{ஏதுமில்லை}=1{1 தளம்}other{# தளங்கள்}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> க்குச் செல்க</translation>
+<translation id="1948773908305951926">ஏற்கப்படும் ப்ரீபெய்டு கார்டுகள்</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> புத்தகக்குறிகள்</translation>
<translation id="1973335181906896915">தொடராக்க பிழை</translation>
<translation id="1974060860693918893">மேம்பட்டவை</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP பிழை</translation>
<translation id="2270484714375784793">தொலைபேசி எண்</translation>
<translation id="2282872951544483773">கிடைக்காத பரிசோதனைகள்</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> உருப்படி}other{<ph name="ITEM_COUNT" /> உருப்படிகள்}}</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>
<translation id="2337852623177822836">அமைப்பை உங்கள் நிர்வாகி கட்டுப்படுத்துகிறார்</translation>
<translation id="2354001756790975382">பிற புக்மார்க்ஸ்</translation>
<translation id="2354430244986887761">சமீபத்தில் Google பாதுகாப்பான தேடலானது <ph name="SITE" /> இல் <ph name="BEGIN_LINK" />தீங்கிழைக்கும் பயன்பாடுகளைக் கண்டறிந்தது<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">தொடர்க</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> அன்று பெற்ற சிதைவு அறிக்கை பதிவேற்றப்படவில்லை</translation>
<translation id="2367567093518048410">நிலை</translation>
-<translation id="237718015863234333">UI மாற்றுகள் இல்லை</translation>
<translation id="2384307209577226199">நிறுவன இயல்புநிலை</translation>
<translation id="2386255080630008482">சேவையகச் சான்றிதழ் திரும்பப் பெறப்பட்டது.</translation>
<translation id="2392959068659972793">மதிப்பும் எதுவும் அமைக்கப்படாத கொள்கைகளைக் காட்டு</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">இது தேர்ந்தெடுக்கப்பட்டால், விரைவாகப் படிவத்தை நிரப்ப, உங்கள் கார்டின் நகலை Chromium இந்தச் சாதனத்தில் சேமிக்கும்.</translation>
<translation id="2498091847651709837">புதிய கார்டை ஸ்கேன்செய்</translation>
<translation id="2501278716633472235">திரும்பிச் செல்</translation>
+<translation id="2503184589641749290">ஏற்கப்படும் டெபிட் மற்றும் ப்ரீபெய்டு கார்டுகள்</translation>
<translation id="2515629240566999685">உங்கள் பகுதியில் உள்ள சிக்னலைச் சரிபார்த்தல்</translation>
-<translation id="2516305470678292029">UI மாற்றுகள்</translation>
<translation id="2539524384386349900">கண்டறி</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> தவறான பதிலை அனுப்பியது.</translation>
<translation id="2556876185419854533">&amp;திருத்தலைச் செயல்தவிர்</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">ஷிப்பிங் முறை</translation>
<translation id="277499241957683684">சாதனப் பதிவு இல்லை</translation>
<translation id="2784949926578158345">இணைப்பு மீட்டமைக்கப்பட்டது.</translation>
+<translation id="2788784517760473862">ஏற்கப்படும் கிரெடிட் கார்டுகள்</translation>
<translation id="2794233252405721443">தளம் தடுக்கப்பட்டது</translation>
<translation id="2799020568854403057">தளத்தில் தீங்கிழைக்கும் பயன்பாடுகள் உள்ளன</translation>
<translation id="2803306138276472711">Google பாதுகாப்பு உலாவலானது சமீபத்தில் <ph name="SITE" /> இல் <ph name="BEGIN_LINK" />தீம்பொருள் உள்ளதைக் கண்டுபிடித்தது<ph name="END_LINK" />. இயல்பாகவே பாதுகாப்பாக இருக்கும் இணையதளங்களும் சில சமயங்களில் தீம்பொருளினால் பாதிக்கப்பட்டிருக்கும்.</translation>
<translation id="2824775600643448204">முகவரி மற்றும் தேடல் பட்டி</translation>
<translation id="2826760142808435982">இந்த இணைப்பு <ph name="CIPHER" /> ஐப் பயன்படுத்தி குறியாக்கப்பட்டது, அங்கீகரிக்கப்பட்டது, ஒரு முக்கிய பரிமாற்ற செயல்முறையாக <ph name="KX" /> ஐப் பயன்படுத்துகிறது.</translation>
<translation id="2835170189407361413">படிவத்தை அழி</translation>
+<translation id="2851634818064021665">இந்தத் தளத்தைப் பார்ப்பதற்கு அனுமதி தேவை</translation>
<translation id="2856444702002559011"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> தளத்திலிருந்து ஹேக்கர்கள் உங்கள் தகவலை (எடுத்துக்காட்டாக, கடவுச்சொற்கள், செய்திகள் அல்லது கிரெடிட் கார்டுகள்) திருட முயற்சிக்கக்கூடும். <ph name="BEGIN_LEARN_MORE_LINK" />மேலும் அறிக<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">மீண்டும் ஏற்ற வேண்டாம்</translation>
-<translation id="2900469785430194048">நினைவகப் பற்றாக்குறையினால், Google Chrome இந்த இணையப்பக்கத்தைக் காட்டவில்லை.</translation>
<translation id="2909946352844186028">பிணைய மாற்றம் கண்டறியப்பட்டது.</translation>
<translation id="2916038427272391327">பிற நிரல்களை மூடவும்</translation>
<translation id="2922350208395188000">சேவையகச் சான்றிதழை சோதிக்க முடியவில்லை.</translation>
<translation id="2928905813689894207">பில்லிங் முகவரி</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழ் <ph name="DOMAIN2" /> இலிருந்து பெறப்பட்டது. இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
<translation id="2948083400971632585">இணைப்பிற்காக உள்ளமைத்த எந்த பிராக்சிகளையும் நீங்கள் அமைப்புகள் பக்கத்திலிருந்து முடக்கலாம்.</translation>
<translation id="2955913368246107853">தேடல் பெட்டியை மூடுக</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">இது இயக்கப்பட்டால், விரைவாகப் படிவத்தை நிரப்ப, உங்கள் கார்டின் நகலை Chromium இந்தச் சாதனத்தில் சேமிக்கும்.</translation>
<translation id="2985398929374701810">சரியான முகவரியை உள்ளிடவும்</translation>
<translation id="2986368408720340940">இந்தப் பிக்அப் முறை இல்லை. வேறு முறையைப் பயன்படுத்திப் பார்க்கவும்.</translation>
@@ -277,9 +281,7 @@
<translation id="3167968892399408617">உங்கள் மறைநிலை தாவல்கள் அனைத்தையும் மூடிய பின், அவற்றில் நீங்கள் பார்த்த பக்கங்கள் உலாவியின் வரலாறு, குக்கீ சேமிப்பகம் அல்லது தேடல் வரலாற்றில் இருக்காது. நீங்கள் இறக்கிய எல்லா கோப்புகள் அல்லது உருவாக்கிய புத்தகக்குறிகள் அப்படியே இருக்கும்.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">தீவு</translation>
-<translation id="317583078218509884">பக்கத்தை மீண்டும் ஏற்றியபின் புதிய தள அனுமதிகள் செயலாக்கப்படும்.</translation>
<translation id="3176929007561373547">பிராக்சி சர்வர் இயக்கத்தில் உள்ளது என்பதை உறுதிப்படுத்த உங்கள் பிராக்சி அமைப்புகளைச் சரிபார்க்கவும் அல்லது நெட்வொர்க் நிர்வாகியைத் தொடர்புகொள்ளவும். நீங்கள் பிராக்சி சர்வரைப் பயன்படுத்துகிறீர்கள் என்பதை நம்பவில்லை என்றால், பின்வருவதைச் செய்யவும்:<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">பக்கத்தை மறைநிலையில் திறக்கவும்</translation>
<translation id="320323717674993345">கட்டணம் செலுத்துவதை ரத்துசெய்</translation>
<translation id="3207960819495026254">புக்மார்க் செய்யப்பட்டது</translation>
<translation id="3225919329040284222">உள்ளமைந்த எதிர்பார்ப்புகளுடன் பொருந்தாத சான்றிதழை சேவையகம் வழங்கியது. சில உயர்-பாதுகாப்பு வலைத்தளங்களில் உங்களைப் பாதுகாக்கவே இந்த எதிர்பார்ப்புகள் சேர்க்கப்படுகின்றன.</translation>
@@ -292,6 +294,7 @@
<translation id="3270847123878663523">&amp;மறுவரிசைப்படுத்தலைச் செயல்தவிர்</translation>
<translation id="3282497668470633863">கார்டிலுள்ள பெயரைச் சேர்க்கவும்</translation>
<translation id="3286538390144397061">இப்போது மறுதொடக்கம் செய்க</translation>
+<translation id="3287510313208355388">ஆன்லைனில் இருக்கும் போது பதிவிறக்கு</translation>
<translation id="3303855915957856445">தேடல் முடிவுகள் எதுவுமில்லை</translation>
<translation id="3305707030755673451"><ph name="TIME" /> அன்று உங்கள் தரவு உங்கள் ஒத்திசைவு கடவுச்சொற்றொடரைக் கொண்டு முறைமையாக்கப்பட்டது. ஒத்திசைவைத் தொடங்க, அதை உள்ளிடவும்.</translation>
<translation id="3320021301628644560">பில்லிங் முகவரியைச் சேர்க்கவும்</translation>
@@ -324,7 +327,6 @@
<translation id="3452404311384756672">எடுப்பதற்கான இடைவேளை:</translation>
<translation id="3462200631372590220">மேம்பட்டவையை மறை</translation>
<translation id="3467763166455606212">கார்டு உரிமையாளரின் பெயர் தேவை</translation>
-<translation id="3478058380795961209">காலாவதியாகும் மாதம்</translation>
<translation id="3479539252931486093">இதை எதிர்பார்க்கவில்லையா? <ph name="BEGIN_LINK" />எங்களுக்குத் தெரியப்படுத்தவும்<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">இப்பொழுது இல்லை</translation>
<translation id="3498215018399854026">தற்போது உங்கள் பெற்றோரைத் தொடர்புகொள்ள முடியவில்லை. மீண்டும் முயற்சிக்கவும்.</translation>
@@ -340,6 +342,7 @@
&lt;p&gt;தேதியையும் நேரத்தையும் &lt;strong&gt;அமைப்புகள்&lt;/strong&gt; பயன்பாட்டின் &lt;strong&gt;பொது&lt;/strong&gt; எனும் பிரிவிற்குச் சென்று மாற்றவும்.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />மேலும் அறிக<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">கிரெடிட் மற்றும் ப்ரீபெய்டு கார்டுகள் ஏற்கப்படுகின்றன.</translation>
<translation id="3582930987043644930">பெயரைச் சேர்</translation>
<translation id="3583757800736429874">&amp;நகர்த்தலை மீண்டும் செய்</translation>
<translation id="3586931643579894722">விவரங்களை மறை</translation>
@@ -355,10 +358,10 @@
<translation id="3655670868607891010">இதை அடிக்கடி காண்கிறீர்கள் எனில், <ph name="HELP_LINK" /> ஐ முயற்சிக்கவும்.</translation>
<translation id="3658742229777143148">மீள்திருத்தங்கள்</translation>
<translation id="3678029195006412963">கோரிக்கையில் கையொப்பமிட முடியவில்லை</translation>
+<translation id="3678529606614285348">புதிய மறைநிலைச் சாளரத்தில் பக்கத்தைத் திறக்கவும் (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375"><ph name="CRASH_TIME" /> அன்று சிதைவு அறிக்கை பதிவுசெய்யப்பட்டு, <ph name="UPLOAD_TIME" /> அன்று பதிவேற்றப்பட்டது</translation>
<translation id="3681007416295224113">சான்றிதழ் தகவல்</translation>
<translation id="3690164694835360974">உள்நுழைவது பாதுகாப்பானது அல்ல</translation>
-<translation id="3693415264595406141">கடவுச்சொல்:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">ஏற்றுகிறது…</translation>
<translation id="3712624925041724820">உரிமம் முடிந்தது</translation>
@@ -370,6 +373,7 @@
<translation id="3748148204939282805"><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="375403751935624634">ஒரு சேவையகப் பிழையின் காரணமாக மொழிபெயர்ப்புத் தோல்வியடைந்தது.</translation>
<translation id="3759461132968374835">உங்களிடம் சமீபத்தில் செயலிழப்புகள் எதுவும் புகாரளிக்கப்படவில்லை. செயலிழப்பு புகாரளித்தல் முடக்கப்பட்டிருந்தபோது ஏற்பட்ட செயலிழப்புகள் இங்கு காண்பிக்கப்படாது.</translation>
+<translation id="3765032636089507299">பாதுகாப்பு உலாவல் பக்கம் உருவாக்கப்படுகிறது.</translation>
<translation id="3778403066972421603">கார்டை உங்கள் Google கணக்கிலும் இந்தச் சாதனத்திலும் சேமிக்க விரும்புகிறீர்களா?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">காலாவதி: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -382,8 +386,10 @@
<translation id="3886446263141354045">இந்தத் தளத்தை அணுகுவதற்கான உங்கள் கோரிக்கை <ph name="NAME" />க்கு அனுப்பப்பட்டது</translation>
<translation id="3890664840433101773">மின்னஞ்சலைச் சேர்</translation>
<translation id="3901925938762663762">கார்டு காலாவதியானது</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">பதிவேற்றிய சிதைவு அறிக்கை ஐடி <ph name="CRASH_ID" /> (அகச் சிதைவு ஐடி: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">இது <ph name="DOMAIN" /> தான் என்பதை, இந்தச் சேவையகத்தால் உறுதிப்படுத்த முடியவில்லை; பொருள் மாற்றுப் பெயர்களை அதன் பாதுகாப்புச் சான்றிதழ் குறிப்பிடவில்லை. இது தவறான உள்ளமைவினால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
+<translation id="3949601375789751990">உலாவல் வரலாறு இங்கே தோன்றும்</translation>
<translation id="3963721102035795474">படித்தல் பயன்முறை</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{ஏதுமில்லை}=1{1 தளத்திலிருந்து }other{# தளங்களிலிருந்து }}</translation>
<translation id="397105322502079400">கணக்கிடுகிறது...</translation>
@@ -412,6 +418,8 @@
<translation id="4165986682804962316">தள அமைப்புகள்</translation>
<translation id="4169947484918424451">இந்தக் கார்டை Chromium சேமிக்க வேண்டுமா?</translation>
<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="4196861286325780578">&amp;நகர்த்தலை மீண்டும் செய்</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ஃபயர்வால் மற்றும் ஆண்டிவைரஸ் உள்ளமைவைச் சரிபார்த்தல்<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">செயலிழப்புகள்</translation>
@@ -435,12 +443,12 @@
<translation id="4356973930735388585">இந்தத் தளத்தில் உள்ள தீங்கிழைப்பவர்கள், உங்கள் தகவலைத் (எடுத்துக்காட்டு: படங்கள், கடவுச்சொற்கள், செய்திகள் மற்றும் கிரெடிட் கார்டுகள்) திருடக்கூடிய அல்லது நீக்கக்கூடிய தீங்கிழைக்கும் நிரல்களை உங்கள் கணினியில் நிறுவ முயலலாம்.</translation>
<translation id="4372948949327679948">எதிர்பார்த்த <ph name="VALUE_TYPE" /> மதிப்பு.</translation>
<translation id="4377125064752653719"><ph name="DOMAIN" /> ஐ அடைய முயற்சி செய்தீர்கள். ஆனால் சேவையகம் வழங்கிய சான்றிதழானது அதன் வழங்குநரால் நிராகரிக்கப்பட்டது. அதாவது, சேவையகம் வழங்கிய பாதுகாப்பு நம்பிக்கைச்சான்றுகளை நிச்சயமாக எக்காரணத்தைக்கொண்டும் நம்பக்கூடாது. போலியான ஒன்றுடன் நீங்கள் தகவல் பரிமாற்றம் செய்துகொண்டிருக்கக்கூடும்.</translation>
-<translation id="4381091992796011497">பயனர் பெயர்:</translation>
<translation id="4394049700291259645">முடக்கு</translation>
<translation id="4406896451731180161">தேடல் முடிவுகள்</translation>
<translation id="4424024547088906515">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழை Chrome நம்பவில்லை. இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> உங்கள் உள்நுழைவுச் சான்றிதழை ஏற்கவில்லை அல்லது சான்றிதழ் வழங்கப்படாமல் இருக்கக்கூடும்.</translation>
<translation id="443673843213245140">ப்ராக்ஸி பயன்பாடு முடக்கப்பட்டுள்ளது. ஆனால் வெளிப்படையான ப்ராக்ஸி உள்ளமைவு குறிப்பிடப்பட்டுள்ளது.</translation>
+<translation id="445100540951337728">ஏற்கப்படும் டெபிட் கார்டுகள்</translation>
<translation id="4506176782989081258">சரிபார்ப்புப் பிழை: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">கணினி நிர்வாகியைத் தொடர்புகொள்ளுதல்</translation>
<translation id="450710068430902550">நிர்வாகியுடன் பகிர்பவை</translation>
@@ -452,12 +460,14 @@
<translation id="4587425331216688090">Chrome இலிருந்து முகவரியை அகற்றவா?</translation>
<translation id="4592951414987517459">நவீன சைபர் சூட்டைப் பயன்படுத்தி <ph name="DOMAIN" /> உடனான உங்கள் இணைப்பு என்க்ரிப்ட் செய்யப்பட்டது.</translation>
<translation id="4594403342090139922">&amp;நீக்குதலைச் செயல்தவிர்</translation>
+<translation id="4611292653554630842">உள்நுழைக</translation>
<translation id="4619615317237390068">பிற சாதனங்களின் தாவல்கள்</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழில் பிழைகள் உள்ளன. இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
<translation id="4690462567478992370">தவறான சான்றிதழைப் பயன்படுத்துவதை நிறுத்து</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">உங்கள் இணைப்பில் தடங்கல் ஏற்பட்டது</translation>
+<translation id="471880041731876836">இந்தத் தளத்தைப் பார்ப்பதற்கான அனுமதி உங்களிடம் இல்லை</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows நெட்வொர்க் டயக்னஸ்டிக்ஸ் கருவியை இயக்கவும்<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">கொள்கைகளை மீண்டும் ஏற்று</translation>
<translation id="4728558894243024398">ப்ளாட்ஃபார்ம்</translation>
@@ -479,14 +489,15 @@
<translation id="4850886885716139402">காட்சி</translation>
<translation id="4854362297993841467">இந்த டெலிவரி முறை இல்லை. வேறு முறையைப் பயன்படுத்திப் பார்க்கவும்.</translation>
<translation id="4858792381671956233">இந்தத் தளத்தைப் பார்வையிடலாமா என, நீங்கள் பெற்றோரிடம் கேட்டுள்ளீர்கள்</translation>
-<translation id="4863764087567530506">இந்த உள்ளடக்கம், உங்களை ஏமாற்றி மென்பொருளை நிறுவ வைக்கலாம் அல்லது தனிப்பட்ட தகவலை வெளிப்படுத்த வைக்கலாம். <ph name="BEGIN_LINK" />இருப்பினும் காட்டு<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">வரலாற்றில் தேடு</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">அங்கீகரிப்பு தேவை</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{மேலும் ஒரு இணையப் பக்கம்}other{மேலும் # இணையப் பக்கங்கள்}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">ஒரு அறியப்படாத மொழியிலிருந்து <ph name="LANGUAGE_LANGUAGE" /> -க்கு இந்தப் பக்கம் மொழிபெயர்க்கப்பட்டுள்ளது</translation>
<translation id="4923459931733593730">கட்டண முறை</translation>
<translation id="4926049483395192435">கட்டாயம் குறிப்பிட வேண்டும்.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">செயல்கள்</translation>
<translation id="4958444002117714549">பட்டியலை விரி</translation>
<translation id="4974590756084640048">எச்சரிக்கைகளை மீண்டும் இயக்கு</translation>
@@ -502,6 +513,7 @@
<translation id="5045550434625856497">தவறான கடவுச்சொல்</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>
<translation id="5087580092889165836">கார்டைச் சேர்</translation>
<translation id="5089810972385038852">மாநிலம்</translation>
@@ -509,18 +521,27 @@
<translation id="5095208057601539847">பிராந்தியம்</translation>
<translation id="5115563688576182185">(64-பிட்)</translation>
<translation id="5141240743006678641">ஒத்திசைக்கப்பட்ட கடவுச்சொற்களை உங்கள் Google நற்சான்றுகள் மூலம் என்க்ரிப்ட் செய்யவும்</translation>
-<translation id="514421653919133810">பக்கத்தை மறைநிலையில் திறக்கவும் (கன்ட்ரோல்-ஷிஃப்ட்-N)</translation>
<translation id="5145883236150621069">கொள்கைப் பதிலில் பிழைக் குறியீடு உள்ளது</translation>
+<translation id="5159010409087891077">புதிய மறைநிலைச் சாளரத்தில் பக்கத்தைத் திறக்கவும் (⇧⌘N)</translation>
<translation id="5171045022955879922">தேடுக அல்லது URL-ஐத் தட்டச்சு செய்க</translation>
<translation id="5172758083709347301">இயந்திரம்</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> இல் இல்லையா? இந்தப் பிழையை தெரிவிக்கவும்</translation>
-<translation id="5181140330217080051">பதிவிறக்குகிறது</translation>
<translation id="5190835502935405962">புக்மார்க்குகள் பட்டி</translation>
<translation id="5199729219167945352">சோதனைகள்</translation>
<translation id="5205222826937269299">பெயர் தேவை</translation>
<translation id="5222812217790122047">மின்னஞ்சல் தேவை</translation>
-<translation id="5251803541071282808">மேகக்கணி</translation>
+<translation id="5251803541071282808">கிளவுடு</translation>
<translation id="5277279256032773186">பணியில் Chromeஐப் பயன்படுத்துகிறீர்களா? வணிக நிறுவனங்கள் தங்களின் பணியாளர்களுக்கான Chrome அமைப்புகளை நிர்வகிக்கலாம். மேலும் அறிக</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />மென்பொருளைத் தற்காலிகமாக முடக்கி, இணையத்துடன் இணைவதற்கு, பின்வரும் படிகளைப் பின்பற்றவும். உங்களுக்கு நிர்வாகி முன்னுரிமைகள் தேவைப்படும்.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />தொடங்கு<ph name="END_BOLD" /> என்பதைக் கிளிக் செய்து, பிறகு <ph name="BEGIN_BOLD" />"உள்ளூர் சேவைகளைக் காட்டு"<ph name="END_BOLD" /> என்பதைத் தேடி, தேர்ந்தெடுக்கவும்
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" /> என்பதைத் தேர்ந்தெடுக்கவும்
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />தொடங்கப்படும் வகை<ph name="END_BOLD" /> என்பதன் கீழ், <ph name="BEGIN_BOLD" />முடக்கப்பட்டது<ph name="END_BOLD" /> என்பதைத் தேர்ந்தெடுக்கவும்
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />சேவை நிலை<ph name="END_BOLD" /> என்பதன் கீழ், <ph name="BEGIN_BOLD" />நிறுத்து<ph name="END_BOLD" /> என்பதைக் கிளிக் செய்யவும்
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />பயன்படுத்து<ph name="END_BOLD" /> என்பதைக் கிளிக் செய்து, பிறகு <ph name="BEGIN_BOLD" />சரி<ph name="END_BOLD" /> என்பதைக் கிளிக் செய்யவும்
+ <ph name="LIST_ITEM" />உங்கள் கணினியிலிருந்து மென்பொருளை நிரந்தரமாக அகற்றுவது எப்படி என்பதை அறிய, <ph name="BEGIN_LEARN_MORE_LINK" />Chrome உதவி மையம்<ph name="END_LEARN_MORE_LINK" /> என்பதற்குச் செல்லவும்
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">இந்தத் தளத்திற்கான உங்கள் இணைப்பு தனிப்பட்டதல்ல. VR பயன்முறையிலிருந்து எப்போது வேண்டுமானாலும் வெளியேற, ஹெட்செட்டை அகற்றி, "முந்தையது" என்பதைக் கிளிக் செய்யவும்.</translation>
<translation id="5299298092464848405">கொள்கையை அலசுவதில் பிழை</translation>
<translation id="5308689395849655368">செயலிழப்பு புகாரளித்தல் முடக்கப்பட்டுள்ளது.</translation>
@@ -537,9 +558,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" திட்டமுறை சரிபார்ப்புப் பிழை: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">இந்த <ph name="HOST_NAME" /> பக்கத்தைக் கண்டறிய முடியவில்லை</translation>
<translation id="5455374756549232013">தவறான கொள்கை நேரமுத்திரை</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" /> இல் <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">தவறானது</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;திருத்தலை மீண்டும் செய்</translation>
+<translation id="5481076368049295676">உங்கள் தகவலைத் திருடும் அல்லது நீக்கும் ஆபத்தான மென்பொருளை உங்கள் சாதனத்தில் இந்த உள்ளடக்கம் நிறுவ முயலலாம். <ph name="BEGIN_LINK" />பரவாயில்லை, காட்டு<ph name="END_LINK" /></translation>
<translation id="54817484435770891">சரியான முகவரியைச் சேர்க்கவும்</translation>
<translation id="5492298309214877701">நிறுவனம், அமைப்பு அல்லது பள்ளி அக இணையத்தில் உள்ள இந்தத் தளம் வெளிப்புற இணையதளம் ஒன்றின் அதே URLஐக் கொண்டிருக்கிறது.
<ph name="LINE_BREAK" />
@@ -551,6 +573,7 @@
<translation id="5540224163453853">கோரப்பட்ட கட்டுரையைக் கண்டுபிடிக்க முடியவில்லை.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> இல் உள்ள உட்பொதிக்கப்பட்ட பக்கம் தெரிவிப்பது:</translation>
<translation id="5556459405103347317">மீண்டும் ஏற்று</translation>
+<translation id="5560088892362098740">காலாவதியாகும் தேதி</translation>
<translation id="5565735124758917034">செயலில் உள்ளது</translation>
<translation id="5571083550517324815">இந்த முகவரியிலிருந்து பிக்அப் செய்ய முடியாது. வேறு முகவரியைத் தேர்ந்தெடுக்கவும்.</translation>
<translation id="5572851009514199876">Chromeஐத் தொடங்கி உள்நுழையவும். அப்போதுதான் இந்தத் தளத்தை அணுகுவதற்கு உங்களுக்கு அனுமதி உள்ளதா என்பதை Chrome ஆல் சரிபார்க்க முடியும்.</translation>
@@ -565,12 +588,13 @@
<translation id="5629630648637658800">கொள்கை அமைப்புகளை ஏற்றுவதில் தோல்வி</translation>
<translation id="5631439013527180824">தவறான சாதன நிர்வாக டோக்கன்</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="5646376287012673985">இருப்பிடம்</translation>
<translation id="5659593005791499971">மின்னஞ்சல்</translation>
<translation id="5669703222995421982">தனிப்பயனாக்கிய உள்ளடக்கத்தைப் பெறுங்கள்</translation>
<translation id="5675650730144413517">இந்தப் பக்கம் செயல்படவில்லை</translation>
<translation id="5710435578057952990">இந்த தளத்தின் அடையாளம் சரிபார்க்கப்படவில்லை.</translation>
-<translation id="5713016350996637505">ஏமாற்றக்கூடிய உள்ளடக்கம் தடுக்கப்பட்டது</translation>
+<translation id="5719499550583120431">ப்ரீபெய்டு கார்டுகள் ஏற்கப்படுகின்றன.</translation>
<translation id="5720705177508910913">நடப்புப் பயனர்</translation>
<translation id="5732392974455271431">உங்களுக்காக, தளத்தின் தடுப்பை உங்கள் பெற்றோர் நீக்க முடியும்</translation>
<translation id="5763042198335101085">சரியான மின்னஞ்சல் முகவரியை உள்ளிடவும்</translation>
@@ -582,15 +606,14 @@
<translation id="5803412860119678065"><ph name="CARD_DETAIL" />ஐ நிரப்ப விரும்புகிறீர்களா?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" />க்கான உங்கள் இணைப்பு, நடைமுறையில் இல்லாத சைபர் சூட்டைப் பயன்படுத்தி என்க்ரிப்ட் செய்யப்பட்டது.</translation>
<translation id="5813119285467412249">&amp;சேர்த்தலை மீண்டும் செய்</translation>
-<translation id="5814352347845180253"><ph name="SITE" /> மற்றும் சில தளங்களின் பிரீமிய உள்ளடக்க அணுகலை நீங்கள் இழக்கக்கூடும்.</translation>
<translation id="5838278095973806738">தீங்கிழைப்பவர்கள் திருடிவிடலாம் என்பதால், இந்தத் தளத்தில் முக்கியத் தகவலை (எடுத்துக்காட்டு: கடவுச்சொற்கள் அல்லது கிரெடிட் கார்டுகள்) உள்ளிட வேண்டாம்.</translation>
<translation id="5869405914158311789">இந்தத் தளத்தை அணுக முடியவில்லை</translation>
<translation id="5869522115854928033">சேமிக்கப்பட்ட கடவுச்சொற்கள்</translation>
<translation id="5872918882028971132">மூலப் பரிந்துரைகள்</translation>
+<translation id="5893752035575986141">கிரெடிட் கார்டுகள் ஏற்கப்படுகின்றன.</translation>
<translation id="5901630391730855834">மஞ்சள்</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ஒத்திசைக்கப்பட்டது)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{ஒரு குக்கீ பயன்படுத்தப்படுகிறது}other{# குக்கீகள் பயன்படுத்தப்படுகின்றன}}</translation>
-<translation id="5926846154125914413">சில தளங்களின் பிரீமிய உள்ளடக்க அணுகலை நீங்கள் இழக்கக்கூடும்.</translation>
<translation id="5959728338436674663">ஆபத்தான பயன்பாடுகளையும் தளங்களையும் கண்டறிவதற்கு உதவியாக, சில <ph name="BEGIN_WHITEPAPER_LINK" />சாதனத் தகவலையும் பக்க உள்ளடக்கத்தையும்<ph name="END_WHITEPAPER_LINK" /> Googleக்குத் தானாக அனுப்பு. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">வரலாற்றிலிருந்து அகற்று</translation>
<translation id="5975083100439434680">சிறிதாக்கு</translation>
@@ -606,6 +629,7 @@
<translation id="6040143037577758943">மூடு</translation>
<translation id="6042308850641462728">மேலும்</translation>
<translation id="6047233362582046994">உங்கள் பாதுகாப்பிற்கான ஆபத்துகளைப் புரிந்துகொண்டால், தீங்கிழைக்கும் பயன்பாடுகள் அகற்றப்படுவதற்கு முன் நீங்கள் <ph name="BEGIN_LINK" />இந்தத் தளத்தைப் பார்வையிடலாம்<ph name="END_LINK" />.</translation>
+<translation id="6047927260846328439">இந்த உள்ளடக்கம், உங்களை ஏமாற்றி மென்பொருளை நிறுவ வைக்கலாம் அல்லது தனிப்பட்ட தகவலை வெளிப்படுத்தச் செய்யலாம். <ph name="BEGIN_LINK" />பரவாயில்லை, காட்டு<ph name="END_LINK" /></translation>
<translation id="6051221802930200923"><ph name="SITE" /> தளமானது சர்டிஃபிகேட் பின்னிங்கைப் பயன்படுத்துவதால், தற்போது அதைப் பார்க்க முடியாது. பொதுவாக நெட்வொர்க் பிழைகளும் பாதிப்புகளும் தற்காலிகமானவை என்பதால், இந்தப் பக்கம் பின்னர் சரியாகச் செயல்படக்கூடும்.</translation>
<translation id="6060685159320643512">கவனம், இந்த சோதனைகள் பாதிப்பை ஏற்படுத்தலாம்</translation>
<translation id="6080696365213338172">நிர்வாகி வழங்கிய சான்றிதழைப் பயன்படுத்தி உள்ளடக்கத்தை அணுகியுள்ளீர்கள். <ph name="DOMAIN" /> க்கு நீங்கள் வழங்கிய தரவானது உங்கள் நிர்வாகியால் இடைமறிக்கப்படலாம்.</translation>
@@ -618,7 +642,6 @@
<translation id="6165508094623778733">மேலும் அறிக</translation>
<translation id="6169916984152623906">இப்போது தனிப்பட்ட முறையில் உலாவலாம். இந்தச் சாதனத்தைப் பயன்படுத்தும் பிறரால் உங்கள் செயல்பாட்டைப் பார்க்க முடியாது. எனினும், பதிவிறக்கங்களும் புத்தகக்குறிகளும் சேமிக்கப்படும்.</translation>
<translation id="6177128806592000436">இந்தத் தளத்திற்கான உங்கள் இணைப்பு, பாதுகாப்பாக இல்லை</translation>
-<translation id="6184817833369986695">(குழு: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">உங்கள் இணைய இணைப்பைச் சரிபார்க்கவும்</translation>
<translation id="6218753634732582820">Chromium இலிருந்து முகவரியை அகற்றவா?</translation>
<translation id="6221345481584921695">Google பாதுகாப்பு உலாவலானது, சமீபத்தில் <ph name="SITE" /> இல் <ph name="BEGIN_LINK" />தீம்பொருள் உள்ளதைக் கண்டுபிடித்தது<ph name="END_LINK" />. இயல்பாகவே பாதுகாப்பாக இருக்கும் இணையதளங்களும் சில சமயங்களில் தீம்பொருளினால் பாதிக்கப்பட்டிருக்கும். தீங்குவிளைவிக்கும் உள்ளடக்கமானது தீம்பொருளைப் பகிர்பவர் என அழைக்கப்படும் <ph name="SUBRESOURCE_HOST" /> இலிருந்து வருகிறது.</translation>
@@ -637,13 +660,14 @@
<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>
<translation id="6387478394221739770">புதிய Chrome அம்சங்களில் ஆர்வம் உள்ளதா? chrome.com/beta இல் எங்களுடைய பீட்டா அலைவரிசையை முயற்சிக்கவும்.</translation>
-<translation id="6389758589412724634">நினைவகப் பற்றாக்குறையினால், Chromium இந்த இணையப்பக்கத்தைக் காட்டவில்லை.</translation>
+<translation id="6397451950548600259">உங்கள் கணினியில் உள்ள மென்பொருளானது, Chrome இணையத்துடன் பாதுகாப்பாக இணைவதைத் தடுக்கிறது</translation>
<translation id="6404511346730675251">புக்மார்க்குகளைத் திருத்து</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" />க்கான காலாவதித் தேதியையும் CVC எண்ணையும் உள்ளிடவும்</translation>
<translation id="6414888972213066896">இந்தத் தளத்தைப் பார்வையிடலாமா என, நீங்கள் பெற்றோரிடம் கேட்டுள்ளீர்கள்</translation>
@@ -658,6 +682,7 @@
<translation id="647261751007945333">சாதனக் கொள்கைகள்</translation>
<translation id="6477321094435799029">இந்தப் பக்கத்தில் வழக்கத்திற்கு மாறான குறியீடு இருப்பதை Chrome கண்டறிந்துள்ளது, மேலும் உங்கள் தனிப்பட்ட தகவலை (எடுத்துக்காட்டு: கடவுச்சொற்கள், ஃபோன் எண்கள் மற்றும் கிரெடிட் கார்டுகள்) பாதுகாக்க அதைத் தடுத்துள்ளது.</translation>
<translation id="6489534406876378309">சிதைவுகளைப் பதிவேற்றுவதைத் தொடங்கு</translation>
+<translation id="6507833130742554667">கிரெடிட் மற்றும் டெபிட் கார்டுகள் ஏற்கப்படுகின்றன.</translation>
<translation id="6508722015517270189">Chromeஐ மீண்டும் தொடங்கவும்</translation>
<translation id="6529602333819889595">&amp;நீக்குதலை மீண்டும் செய்</translation>
<translation id="6534179046333460208">இயல்நிலை இணையப் பரிந்துரைகள்</translation>
@@ -666,13 +691,13 @@
<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="657639383826808334">உங்கள் தகவலைத் திருடும் அல்லது நீக்கும் ஆபத்தான மென்பொருளை உங்கள் சாதனத்தில் இந்த உள்ளடக்கம் நிறுவ முயலலாம். <ph name="BEGIN_LINK" />பரவாயில்லை காட்டு<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">குறியாக்க விருப்பங்கள்</translation>
<translation id="662080504995468778">வேண்டாம்</translation>
<translation id="6626291197371920147">சரியான கார்டு எண்ணைச் சேர்க்கவும்</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> தேடல்</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> தளத்தில் தற்போதுள்ள ஹேக்கர்கள் உங்கள் தனிப்பட்ட தகவலை (எடுத்துக்காட்டாக, படங்கள், கடவுச்சொற்கள், செய்திகள் மற்றும் கிரெடிட் கார்டுகள்) திருடக்கூடிய அல்லது நீக்கக்கூடிய ஆபத்தான நிரல்களை உங்கள் Mac இல் நிறுவ முயற்சிக்கக்கூடும். <ph name="BEGIN_LEARN_MORE_LINK" />மேலும் அறிக<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">இந்தக் கொள்கை தவிர்க்கப்பட்டது.</translation>
+<translation id="6657585470893396449">கடவுச்சொல்</translation>
<translation id="6671697161687535275">Chromium இலிருந்து படிவப் பரிந்துரையை அகற்றவா?</translation>
<translation id="6685834062052613830">வெளியேறி, அமைப்பை முடிக்கவும்</translation>
<translation id="6710213216561001401">முந்தையது</translation>
@@ -703,7 +728,6 @@
<translation id="6970216967273061347">மாவட்டம்</translation>
<translation id="6973656660372572881">நிலையான ப்ராக்ஸி சேவையகங்களும் .pac ஸ்கிரிப்ட் URL ஆகிய இரண்டும் குறிப்பிடப்பட்டுள்ளது.</translation>
<translation id="6989763994942163495">மேம்பட்ட அமைப்புகளைக் காண்பி...</translation>
-<translation id="7000990526846637657">வரலாறு உள்ளீடுகள் எதுவுமில்லை</translation>
<translation id="7012363358306927923">சீனா UnionPay</translation>
<translation id="7012372675181957985">உங்கள் Google கணக்கு <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> என்ற தளத்தில் உலாவல் வரலாறு தொடர்பான பிற தகவல்களைக் கொண்டிருக்கக்கூடும்</translation>
<translation id="7029809446516969842">கடவுச்சொற்கள்</translation>
@@ -718,7 +742,9 @@
<translation id="7129409597930077180">இந்த முகவரிக்கு அனுப்ப முடியாது. வேறு முகவரியைத் தேர்ந்தெடுக்கவும்.</translation>
<translation id="7138472120740807366">டெலிவரி முறை</translation>
<translation id="7139724024395191329">எமிரேட்</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">கட்டணம் செலுத்துவது பாதுகாப்பானது அல்ல</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">இப்போது மீண்டும் தொடங்கு</translation>
<translation id="7180611975245234373">புதுப்பி</translation>
<translation id="7182878459783632708">கொள்கைகள் அமைக்கப்படவில்லை</translation>
@@ -759,6 +785,7 @@
<translation id="7514365320538308">பதிவிறக்கு</translation>
<translation id="7518003948725431193">வலை முகவரிக்கான வலைப்பக்கங்கள் ஏதும் காணப்படவில்லை: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">இந்தத் தளத்திற்கான உங்கள் இணைப்பு தனிப்பட்டதல்ல</translation>
<translation id="7535087603100972091">மதிப்பு</translation>
<translation id="7537536606612762813">கட்டாயம்</translation>
<translation id="7542403920425041731">நீங்கள் உறுதிசெய்ததும், உங்கள் கார்டு விவரங்கள் இந்தத் தளத்துடன் பகிரப்படும்.</translation>
@@ -768,7 +795,6 @@
<translation id="7552846755917812628">பின்வரும் உதவிக் குறிப்புகளைச் செய்து பார்க்கவும்:</translation>
<translation id="7554791636758816595">புதிய தாவல்</translation>
<translation id="7567204685887185387">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழில் மோசடி செய்யப்பட்டிருக்கலாம். இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">இந்தப் பக்கமானது<ph name="ORIGINAL_LANGUAGE" />இல் உள்ளது இதை மொழிபெயர்க்க விரும்புகிறீர்களா?</translation>
<translation id="7569952961197462199">Chrome இலிருந்து கிரெடிட் கார்டை அகற்றவா?</translation>
<translation id="7569983096843329377">கருப்பு</translation>
@@ -795,9 +821,11 @@
<translation id="7714464543167945231">சான்றிதழ்</translation>
<translation id="7716147886133743102">உங்கள் நிர்வாகி தடுத்துள்ளார்</translation>
<translation id="7716424297397655342">தற்காலிகச் சேமிப்பிலிருந்து இந்தத் தளத்தை ஏற்ற முடியவில்லை</translation>
+<translation id="774634243536837715">ஆபத்தான உள்ளடக்கம் தடுக்கப்பட்டது.</translation>
<translation id="7752995774971033316">நிர்வகிக்கப்படாதது</translation>
<translation id="7755287808199759310">உங்களுக்காக, தளத்தின் தடுப்பை உங்கள் பெற்றோர் நீக்க முடியும்</translation>
<translation id="7758069387465995638">தீச்சுவர் அல்லது வைரஸ்தடுப்பு மென்பொருள் உங்கள் இணைப்பைத் தடுத்திருக்கலாம்.</translation>
+<translation id="7759163816903619567">காட்சி டொமைன்:</translation>
<translation id="7761701407923456692">சேவையகச் சான்றிதழ் URL உடன் பொருந்தவில்லை.</translation>
<translation id="7763386264682878361">பேமெண்ட் மேனிஃபெஸ்ட் பாகுபடுத்தி</translation>
<translation id="7764225426217299476">முகவரியைச் சேர்</translation>
@@ -812,6 +840,7 @@
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />'க்கு <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> உள்ளன</translation>
<translation id="785549533363645510">இருப்பினும், நீங்கள் மறைந்திருக்கமாட்டீர்கள். மறைநிலைக்குச் செல்வது பணிக்கமர்த்தும் நிறுவனம், இணையச் சேவை வழங்குநர் அல்லது நீங்கள் செல்லும் இணையதளங்களிடம் உங்கள் உலாவலை மறைக்காது.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">டெபிட் மற்றும் ப்ரீபெய்டு கார்டுகள் ஏற்கப்படுகின்றன.</translation>
<translation id="7887683347370398519">CVCஐச் சோதித்து, மீண்டும் முயற்சிக்கவும்</translation>
<translation id="79338296614623784">சரியான ஃபோன் எண்ணை உள்ளிடவும்</translation>
<translation id="7935318582918952113">DOM டிஸ்டில்லர்</translation>
@@ -831,7 +860,6 @@
<translation id="8041089156583427627">கருத்துத் தெரிவிக்கவும்</translation>
<translation id="8041940743680923270">முழுமையான இயல்புநிலையைப் பயன்படுத்து (கேள்)</translation>
<translation id="8088680233425245692">கட்டுரையைக் காட்டுவதில் தோல்வி.</translation>
-<translation id="8089520772729574115">1 மெ.பை. க்கும் குறைவாக உள்ளது</translation>
<translation id="8091372947890762290">சேவையகத்தில் செயலாக்கம் நிலுவையிலுள்ளது</translation>
<translation id="8118489163946903409">கட்டண முறை</translation>
<translation id="8131740175452115882">உறுதிப்படுத்து</translation>
@@ -843,10 +871,13 @@
<translation id="8194797478851900357">&amp;நகர்த்தலைச் செயல்தவிர்</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" ஐடியுடன் கூடிய நீட்டிப்பிற்கான தவறான புதுப்பிப்பு URL.</translation>
<translation id="8202097416529803614">ஆர்டர் பற்றிய சுருக்கவிவரம்</translation>
+<translation id="8205463626947051446">தளத்தில் குறுக்கிடும் விளம்பரங்கள் அடிக்கடிக் காட்டப்படும்</translation>
<translation id="8218327578424803826">ஒதுக்கிய இருப்பிடம்:</translation>
<translation id="8225771182978767009">இந்தக் கணினியை அமைத்த நபர் இந்தத் தளத்தைத் தடுக்கும்படி தேர்வுசெய்துள்ளார்.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">புதிய மறைநிலைத் தாவலில் பக்கத்தைத் திறக்கவும்</translation>
<translation id="8241707690549784388">நீங்கள் தேடும் பக்கமானது நீங்கள் உள்ளிட்ட தகவலைப் பயன்படுத்தியது. மீண்டும் அந்த பக்கத்திற்கு திரும்பினால், நீங்கள் செய்த ஏதேனும் செயலை மீண்டும் செய்ய வேண்டியிருக்கலாம். தொடர விரும்புகிறீர்களா?</translation>
+<translation id="8241712895048303527">இந்தத் தளத்தில் தடு</translation>
<translation id="8249320324621329438">கடைசியாக எடுத்தது:</translation>
<translation id="8253091569723639551">பில்லிங் முகவரி தேவை</translation>
<translation id="8261506727792406068">நீக்கு</translation>
@@ -857,7 +888,6 @@
<translation id="8308427013383895095">பிணைய இணைப்பில் ஒரு சிக்கல் இருப்பதால் மொழிப்பெயர்ப்பு தோல்வியடைந்தது.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> க்கான அணுகல் மறுக்கப்பட்டது</translation>
<translation id="834457929814110454">உங்கள் பாதுகாப்பிற்கான ஆபத்தைப் புரிந்துகொண்டால், தீங்கான நிரல்களை அகற்றும் முன் <ph name="BEGIN_LINK" />இந்தத் தளத்தைப் பார்வையிடலாம்<ph name="END_LINK" />.</translation>
-<translation id="8344669043927012510">பக்கத்தை மறைநிலையில் திறக்கவும் (⇧⌘N)</translation>
<translation id="8349305172487531364">புக்மார்க் பட்டி</translation>
<translation id="8363502534493474904">விமானப் பயன்முறையை முடக்குதல்</translation>
<translation id="8364627913115013041">அமைக்கப்படவில்லை.</translation>
@@ -867,6 +897,7 @@
<translation id="8398259832188219207">சிதைவு அறிக்கை பதிவேற்றப்பட்ட தேதி: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">செயலிழப்புகள் (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">நீங்கள் கண்டிப்பாக ஒரே கடவுச்சொற்றொடரை இரு முறை உள்ளிட வேண்டும்.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">அமைப்புகள்</translation>
<translation id="8433057134996913067">இதனால் பெரும்பாலான வலைத்தளங்களில் இருந்து வெளியேற்றப்படுவீர்கள்.</translation>
<translation id="8437238597147034694">&amp;நகர்த்தலைச் செயல்தவிர்</translation>
@@ -874,14 +905,16 @@
<translation id="8483780878231876732">Google கணக்கில் உள்ள கார்டுகளைப் பயன்படுத்த, Chrome இல் உள்நுழையவும்</translation>
<translation id="8488350697529856933">இதற்குப் பொருந்தும்</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> பதிலளிக்க நீண்ட நேரம் எடுத்துக்கொண்டது.</translation>
-<translation id="8532105204136943229">காலாவதியாகும் ஆண்டு</translation>
+<translation id="8503813439785031346">பயனர்பெயர்</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="8571890674111243710"><ph name="LANGUAGE" /> க்கு பக்கத்தை மொழிபெயர்க்கிறது...</translation>
<translation id="858637041960032120">தொலைபேசி எண்</translation>
<translation id="859285277496340001">இந்த சான்றிதழ் திரும்பப்பெறப்பட்டதா என்பதைச் சரிபார்ப்பதற்கான செயல்முறை இதில் இல்லை.</translation>
<translation id="8620436878122366504">இன்னும் உங்கள் பெற்றோர் அனுமதிக்கவில்லை</translation>
<translation id="8647750283161643317">எல்லாவற்றையும் இயல்புநிலைக்கு மீட்டமை</translation>
+<translation id="8660471606262461360">Google Payments இலிருந்து</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> க்கான உங்கள் இணைப்பு குறியாக்கம் செய்யப்படவில்லை.</translation>
<translation id="8718314106902482036">பேமெண்ட் முடியவில்லை</translation>
<translation id="8725066075913043281">மீண்டும் முயற்சிக்கவும்</translation>
@@ -909,6 +942,7 @@
<translation id="8903921497873541725">பெரிதாக்கு</translation>
<translation id="8931333241327730545">இந்தக் கார்டை உங்கள் Google கணக்கில் சேமிக்க வேண்டுமா?</translation>
<translation id="8932102934695377596">உங்கள் கடிகாரம் மிகவும் பின்தங்கி இருக்கிறது</translation>
+<translation id="8938939909778640821">ஏற்கப்படும் கிரெடிட் மற்றும் ப்ரீபெய்டு கார்டுகள்</translation>
<translation id="8971063699422889582">சேவையகச் சான்றிதழ் காலாவதியானது.</translation>
<translation id="8986494364107987395">பயன்பாட்டுப் புள்ளிவிவரங்களையும் சிதைவு அறிக்கைகளையும் தானாகவே Google க்கு அனுப்பு</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -939,7 +973,6 @@
<translation id="9169664750068251925">இந்தத் தளத்தில் எப்போதும் தடு</translation>
<translation id="9170848237812810038">&amp;செயல்தவிர்</translation>
<translation id="917450738466192189">சேவையகச் சான்றிதழ் செல்லுபடியானதல்ல.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" />, மேலும் <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ஆதரிக்கப்படாத நெறிமுறையைப் பயன்படுத்துகிறது.</translation>
<translation id="9205078245616868884">உங்கள் தரவு உங்கள் ஒத்திசைவு கடவுச்சொற்றொடரைக் கொண்டு முறைமையாக்கப்பட்டுள்ளது. ஒத்திசைவைத் தொடங்க, அதை உள்ளிடவும்.</translation>
<translation id="9207861905230894330">கட்டுரையைச் சேர்ப்பதில் தோல்வி.</translation>
@@ -948,9 +981,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">படிவத்தை அழி</translation>
<translation id="939736085109172342">புதிய கோப்புறை</translation>
-<translation id="941721044073577244">உங்களுக்கு இந்தத் தளத்தைப் பார்வையிட அனுமதி இல்லை</translation>
<translation id="969892804517981540">அதிகாரப்பூர்வ கட்டமைப்பு</translation>
<translation id="975560348586398090">{COUNT,plural, =0{ஏதுமில்லை}=1{1 உருப்படி}other{# உருப்படிகள்}}</translation>
+<translation id="981121421437150478">ஆஃப்லைன்</translation>
<translation id="988159990683914416">டெவலப்பர் கட்டமைப்பு</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_te.xtb b/chromium/components/strings/components_strings_te.xtb
index 0e5ae16314c..5074ba26a03 100644
--- a/chromium/components/strings/components_strings_te.xtb
+++ b/chromium/components/strings/components_strings_te.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">ఇతర అనువర్తనాలను మూసివేయండి</translation>
<translation id="1055184225775184556">&amp;జోడించడాన్ని రద్దు చేయి</translation>
<translation id="10614374240317010">ఎప్పటికి సేవ్ చెయ్యబడవు</translation>
+<translation id="1066396345355680611">మీరు <ph name="SITE" /> మరియు మరికొన్ని ఇతర సైట్‌ల నుండి రక్షిత కంటెంట్‌కి ప్రాప్యతను కోల్పోవచ్చు.</translation>
<translation id="106701514854093668">డెస్క్‌టాప్‌ బుక్‌మార్క్‌లు</translation>
<translation id="1074497978438210769">సురక్షితం కాదు</translation>
<translation id="1080116354587839789">వెడల్పు సరిపోయేలా అమర్చు</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">పఠన జాబితా</translation>
<translation id="1264126396475825575">క్రాష్ నివేదిక <ph name="CRASH_TIME" />కి సంగ్రహించబడింది (ఇంకా అప్‌లోడ్ చేయలేదు లేదా విస్మరించబడింది)</translation>
<translation id="1281526147609854549"><ph name="ISSUER" /> ద్వారా జారీ చేయబడింది</translation>
-<translation id="1283919782143846010">హానికరమైన కంటెంట్ బ్లాక్ చేయబడింది</translation>
<translation id="1285320974508926690">ఈ సైట్‌ను అనువదించవద్దు</translation>
<translation id="129553762522093515">ఇటీవల మూసివెయ్యబడినవి</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />మీ కుక్కీలను తీసివేయడానికి ప్రయత్నించండి<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">నమోదిత డొమైన్:</translation>
<translation id="1340482604681802745">పికప్ చిరునామా</translation>
-<translation id="1344211575059133124">ఈ సైట్‌ను సందర్శించడానికి మీకు అనుమతి అవసరమైనట్లుగా కనిపిస్తోంది</translation>
<translation id="1344588688991793829">Chromium స్వయంపూర్తి సెట్టింగ్‌లు...</translation>
<translation id="1348198688976932919">రాబోయే సైట్‌లో హానికరమైన యాప్‌లు ఉన్నాయి</translation>
<translation id="1374468813861204354">సూచనలు</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" /> వద్ద <ph name="ORGANIZATION" /> యొక్క గుర్తింపు <ph name="ISSUER" />చే ధ్రువీకరించబడింది.</translation>
<translation id="1426410128494586442">అవును</translation>
<translation id="1430915738399379752">ముద్రించు</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">ఈ పేజీ యొక్క సేవ్ చేసిన (అంటే పాతది) కాపీని చూపుతుంది.</translation>
<translation id="1517433312004943670">ఫోన్ నంబర్ అవసరం</translation>
+<translation id="1517500485252541695">ఆమోదించబడిన క్రెడిట్ మరియు డెబిట్ కార్డ్‌లు</translation>
<translation id="1519264250979466059">రూపకల్పన తేదీ</translation>
+<translation id="1527263332363067270">కనెక్షన్ కోసం వేచి ఉన్నాము...</translation>
<translation id="153384715582417236">ఇప్పటికి ఇంతే</translation>
<translation id="1549470594296187301">ఈ లక్షణాన్ని ఉపయోగించడానికి జావాస్క్రిప్ట్ తప్పనిసరిగా ప్రారంభించాలి.</translation>
<translation id="1555130319947370107">నీలం</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">సిస్టమ్ నిర్వాహకుడిని సంప్రదించి ప్రయత్నించండి.</translation>
<translation id="1740951997222943430">చెల్లుబాటు అయ్యే గడువు ముగింపు నెలను నమోదు చేయండి</translation>
-<translation id="1745358365027406341">పేజీని తర్వాత డౌన్‌లోడ్ చేయి</translation>
<translation id="17513872634828108">తెరిచిన ట్యాబ్‍లు</translation>
<translation id="1753706481035618306">పేజీ సంఖ్య</translation>
<translation id="1763864636252898013">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రాన్ని మీ పరికర ఆపరేటింగ్ సిస్టమ్ విశ్వసించలేదు. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">అవసరమైన ఫీల్డ్</translation>
<translation id="187918866476621466">ప్రారంభ పేజీలను తెరువు</translation>
<translation id="1883255238294161206">జాబితాను కుదించు</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">ఫిల్టరింగ్</translation>
+<translation id="1916770123977586577">ఈ సైట్‌కు మీ నవీకరించిన సెట్టింగ్‌లను వర్తింపజేయడానికి, ఈ పేజీని మళ్లీ లోడ్ చేయండి</translation>
+<translation id="1919345977826869612">ప్రకటనలు</translation>
<translation id="192020519938775529">{COUNT,plural, =0{ఏమీ లేవు}=1{1 సైట్}other{# సైట్‌లు}}</translation>
<translation id="194030505837763158"><ph name="LINK" />కి వెళ్లండి</translation>
+<translation id="1948773908305951926">ఆమోదించబడిన ప్రీపెయిడ్ కార్డ్‌లు</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> బుక్‌మార్క్‌లు</translation>
<translation id="1973335181906896915">శ్రేణిగా రూపొందించడంలో లోపం</translation>
<translation id="1974060860693918893">ఆధునిక</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP లోపం</translation>
<translation id="2270484714375784793">ఫోన్ నంబర్</translation>
<translation id="2282872951544483773">అందుబాటులో లేని ప్రయోగాలు</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> అంశం}other{<ph name="ITEM_COUNT" /> అంశాలు}}</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>
<translation id="2337852623177822836">సెట్టింగ్‌ని మీ నిర్వాహకులు నియంత్రిస్తున్నారు</translation>
<translation id="2354001756790975382">ఇతర బుక్‌మార్క్‌లు</translation>
<translation id="2354430244986887761">Google సురక్షిత బ్రౌజింగ్ ఇటీవల <ph name="SITE" />లో <ph name="BEGIN_LINK" />హానికర యాప్‌లను కనుగొంది<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">కొనసాగు</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />కి సంగ్రహించిన క్రాష్ నివేదిక అప్‌లోడ్ కాలేదు</translation>
<translation id="2367567093518048410">స్థాయి</translation>
-<translation id="237718015863234333">UI ప్రత్యామ్నాయాలు అందుబాటులో లేవు</translation>
<translation id="2384307209577226199">ఎంటర్‌ప్రైజ్ డిఫాల్ట్</translation>
<translation id="2386255080630008482">సర్వర్ ప్రమాణపత్రం రద్దు చెయ్యబడింది.</translation>
<translation id="2392959068659972793">విలువ సెట్ చేయని విధానాలను చూపు</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">ఎంచుకుంటే, Chromium వేగవంతమైన ఫారమ్ పూరింపు కోసం ఈ పరికరంలో మీ కార్డ్ కాపీని నిల్వ చేస్తుంది.</translation>
<translation id="2498091847651709837">కొత్త కార్డ్‌ను స్కాన్ చేయండి</translation>
<translation id="2501278716633472235">వెనుకకు వెళ్ళు</translation>
+<translation id="2503184589641749290">ఆమోదించబడిన డెబిట్ మరియు ప్రీపెయిడ్ కార్డ్‌లు</translation>
<translation id="2515629240566999685">మీ ప్రాంతంలో సిగ్నల్‌ను తనిఖీ చేయడం</translation>
-<translation id="2516305470678292029">UI ప్రత్యామ్నాయాలు</translation>
<translation id="2539524384386349900">గుర్తించు</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> చెల్లని ప్రతిస్పందనను పంపింది.</translation>
<translation id="2556876185419854533">&amp;సవరించడాన్ని రద్దు చేయి</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">రవాణా పద్ధతి</translation>
<translation id="277499241957683684">పరికరం రికార్డ్ లేదు</translation>
<translation id="2784949926578158345">కనెక్షన్ మళ్ళీ సెట్ చెయ్యబడింది.</translation>
+<translation id="2788784517760473862">ఆమోదించబడిన క్రెడిట్ కార్డ్‌లు</translation>
<translation id="2794233252405721443">సైట్ బ్లాక్ చేయబడింది</translation>
<translation id="2799020568854403057">రాబోయే సైట్ హానికరమైన యాప్‌లను కలిగి ఉంది</translation>
<translation id="2803306138276472711">Google సురక్షిత బ్రౌజింగ్ ఇటీవల <ph name="SITE" />లో <ph name="BEGIN_LINK" />మాల్వేర్‌ని గుర్తించింది<ph name="END_LINK" />. సాధారణంగా సురక్షితమైన వెబ్‌సైట్‌‌లకు కూడా కొన్నిసార్లు మాల్వేర్ సోకుతుంది.</translation>
<translation id="2824775600643448204">చిరునామా మరియు శోధన బార్</translation>
<translation id="2826760142808435982"><ph name="CIPHER" />ను ఉపయోగించి కనెక్షన్ గుప్తీకరించబడింది మరియు ప్రామాణీకరించబడింది మరియు <ph name="KX" />ను కీలకమైన పరివర్తన విధానంగా ఉపయోగిస్తుంది.</translation>
<translation id="2835170189407361413">ఫారమ్‌ను తుడిచివేయి</translation>
+<translation id="2851634818064021665">ఈ సైట్‌ను సందర్శించడానికి మీకు అనుమతి అవసరం</translation>
<translation id="2856444702002559011">హ్యాకర్‌లు <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> నుండి మీ సమాచారాన్ని దొంగిలించడానికి ప్రయత్నిస్తూ ఉండవచ్చు (ఉదాహరణకు, పాస్‌వర్డ్‌లు, సందేశాలు లేదా క్రెడిట్ కార్డ్‌లు). <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలుసుకోండి<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">మళ్లీ లోడ్ చేయవద్దు</translation>
-<translation id="2900469785430194048">Google Chrome ఈ వెబ్‌పేజీని ప్రదర్శించడానికి ప్రయత్నిస్తున్నప్పుడు మెమరీ అయిపోయింది.</translation>
<translation id="2909946352844186028">నెట్‌వర్క్ మార్పు గుర్తించబడింది.</translation>
<translation id="2916038427272391327">ఇతర ప్రోగ్రామ్‌లను మూసివేయండి</translation>
<translation id="2922350208395188000">సర్వర్ యొక్క ప్రమాణపత్రం తనిఖీ చెయ్యబడదు.</translation>
<translation id="2928905813689894207">బిల్లింగ్ చిరునామా</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రం <ph name="DOMAIN2" /> నుండి జారీ చేయబడింది. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
<translation id="2948083400971632585">మీరు సెట్టింగ్‌ల పేజీ నుండి కనెక్షన్ కోసం కాన్ఫిగర్ చేయబడిన ఏ ప్రాక్సీలను అయినా నిలిపివేయవచ్చు.</translation>
<translation id="2955913368246107853">కనుగొను పట్టీని మూసివేయి</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">ప్రారంభిస్తే, Chromium వేగవంతమైన ఫారమ్ పూరింపు కోసం ఈ పరికరంలో మీ కార్డ్ కాపీని నిల్వ చేస్తుంది.</translation>
<translation id="2985398929374701810">చెల్లుబాటు అయ్యే చిరునామాని నమోదు చేయండి</translation>
<translation id="2986368408720340940">ఈ పికప్ పద్ధతి అందుబాటులో లేదు. వేరే పద్ధతిని ప్రయత్నించండి.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">మీరు అజ్ఞాత ట్యాబ్‌ల్లో వీక్షించిన పేజీలు మీ అన్ని అజ్ఞాత ట్యాబ్‌లను మూసివేసిన అనంతరం మీ బ్రౌజర్ చరిత్ర, కుక్కీ స్టోర్ లేదా శోధన చరిత్రలో ఉంచబడవు. మీరు డౌన్‌లోడ్ చేసే ఏవైనా ఫైల్‌లు లేదా మీరు సృష్టించే ఏవైనా బుక్‌మార్క్‌లు అలాగే ఉంచబడతాయి.</translation>
<translation id="3169472444629675720">కనుగొను</translation>
<translation id="3174168572213147020">దీవి</translation>
-<translation id="317583078218509884">క్రొత్త సైట్ అనుమతుల సెట్టింగ్‌లు పేజీని మళ్లీ లోడ్ చేసిన తర్వాత ప్రభావితమవుతాయి.</translation>
<translation id="3176929007561373547">ప్రాక్సీ సర్వర్ పని చేస్తున్నట్లు నిర్ధారించుకోవడానికి మీ ప్రాక్సీ సెట్టింగ్‌లను తనిఖీ చేయండి లేదా
మీ నెట్‌వర్క్ నిర్వాహకుడిని సంప్రదించండి. మీరు ప్రాక్సీ సర్వర్‌నే ఉపయోగిస్తున్నట్లు మీకు
నమ్మకంగా లేకుంటే:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">అజ్ఞాత మోడ్‌లో పేజీని తెరవండి</translation>
<translation id="320323717674993345">చెల్లింపును రద్దు చేయండి</translation>
<translation id="3207960819495026254">బుక్‌మార్క్ చేయబడింది</translation>
<translation id="3225919329040284222">అంతర్నిర్మిత అంచనాలకు సరిపోలని ఒక ధృవీకరణ పత్రాన్ని సర్వర్ సమర్పించింది. మిమ్మల్ని సంరక్షించే దిశగా నిర్దిష్ట, ఉన్నత స్ధాయి భద్రతా వెబ్‌సైట్‌ల కోసం ఈ అంచనాలు చేర్చబడ్డాయి.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;మళ్లీ క్రమం చేయడాన్ని రద్దు చేయి</translation>
<translation id="3282497668470633863">కార్డ్‌లో పేరుని జోడించండి</translation>
<translation id="3286538390144397061">ఇప్పుడు పునఃప్రారంభించండి</translation>
+<translation id="3287510313208355388">ఆన్‌లైన్‌లో ఉన్నప్పుడు డౌన్‌లోడ్ చేయి</translation>
<translation id="3303855915957856445">శోధన ఫలితాలు ఏవీ కనుగొనబడలేదు</translation>
<translation id="3305707030755673451"><ph name="TIME" />న మీ సమకాలీకరణ రహస్య పదబంధంతో మీ డేటా గుప్తీకరించబడింది. సమకాలీకరణను ప్రారంభించడానికి దీన్ని నమోదు చేయండి.</translation>
<translation id="3320021301628644560">బిల్లింగ్ చిరునామాను జోడించండి</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">విరామాన్ని పొందండి:</translation>
<translation id="3462200631372590220">అధునాతనం దాచు</translation>
<translation id="3467763166455606212">కార్డుదారుని పేరు అవసరం</translation>
-<translation id="3478058380795961209">గడువు ముగింపు నెల</translation>
<translation id="3479539252931486093">ఊహించని విధంగా ఇది సంభవించిందా? <ph name="BEGIN_LINK" />మాకు తెలియజేయండి<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">ఇప్పుడు కాదు</translation>
<translation id="3498215018399854026">మేము ప్రస్తుతం మీ తల్లి/తండ్రిని సంప్రదించలేకపోయాము. దయచేసి మళ్లీ ప్రయత్నించండి.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;దయచేసి &lt;strong&gt;సెట్టింగ్‌లు&lt;/strong&gt; అనువర్తనం యొక్క &lt;strong&gt;సాధారణ&lt;/strong&gt; విభాగం నుండి తేదీ మరియు సమయాన్ని సర్దుబాటు చేయండి.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలుసుకోండి<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">క్రెడిట్ మరియు ప్రీపెయిడ్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
<translation id="3582930987043644930">పేరు జోడించండి</translation>
<translation id="3583757800736429874">&amp;తరలించడాన్ని పునరావృతం చేయి</translation>
<translation id="3586931643579894722">వివరాలను దాచిపెట్టు</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">మీరు దీన్ని తరచుగా చూస్తుంటే, ఈ <ph name="HELP_LINK" />ని ప్రయత్నించండి.</translation>
<translation id="3658742229777143148">పునర్విమర్శ</translation>
<translation id="3678029195006412963">అభ్యర్థనకు సంతకం అందించడం సాధ్యపడలేదు</translation>
+<translation id="3678529606614285348">కొత్త అజ్ఞాత విండోలో పేజీని తెరవండి (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">క్రాష్ నివేదిక <ph name="CRASH_TIME" />కి సంగ్రహించబడింది, <ph name="UPLOAD_TIME" />కి అప్‌లోడ్ చేయబడింది</translation>
<translation id="3681007416295224113">సర్టిఫికెట్ సమాచారం</translation>
<translation id="3690164694835360974">లాగిన్ సురక్షితం కాదు</translation>
-<translation id="3693415264595406141">పాస్‌వర్డ్:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">లోడ్ అవుతోంది...</translation>
<translation id="3712624925041724820">లైసెన్స్‌లు అయిపోయాయి</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805"><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="375403751935624634">సర్వర్ లోపం వల్ల అనువాదం విఫలమైంది.</translation>
<translation id="3759461132968374835">మీకు ఇటీవల నివేదించిన క్రాష్‌లు లేవు. క్రాష్‌ నివేదన నిలిపివేసినపుడు ఏర్పడే క్రాష్‌లు ఇక్కడ కనిపించవు.</translation>
+<translation id="3765032636089507299">సురక్షిత బ్రౌజింగ్ పేజీ నిర్మాణంలో ఉంది.</translation>
<translation id="3778403066972421603">ఈ కార్డ్‌ని మీ Google ఖాతాకు మరియు ఈ పరికరంలో సేవ్ చేయాలనుకుంటున్నారా?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">గడువు ముగింపు <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">మీరు ఈ సైట్‌ను ప్రాప్యత చేయడానికి చేసిన అభ్యర్థన <ph name="NAME" />కి పంపబడింది</translation>
<translation id="3890664840433101773">ఇమెయిల్‌ను జోడించండి</translation>
<translation id="3901925938762663762">కార్డ్ గడువు సమయం ముగిసింది</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">క్రాష్ నివేదిక ID <ph name="CRASH_ID" /> (స్థానిక క్రాష్ ID: <ph name="CRASH_LOCAL_ID" />) అప్‌లోడ్ చేయబడింది</translation>
<translation id="3949571496842715403">ఈ సర్వర్ తను <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రంలో విషయ ప్రత్యామ్నాయ పేర్లు పేర్కొనబడలేదు. తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా హ్యాకర్ మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన ఇలా జరిగి ఉండవచ్చు.</translation>
+<translation id="3949601375789751990">మీ బ్రౌజింగ్ చరిత్ర ఇక్కడ కనిపిస్తుంది</translation>
<translation id="3963721102035795474">పాఠకుని మోడ్</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{ఏవీ కాదు}=1{1 సైట్ నుండి }other{# సైట్‌ల నుండి }}</translation>
<translation id="397105322502079400">గణిస్తోంది...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">సైట్ సెట్టింగ్‌లు</translation>
<translation id="4169947484918424451">Chromium ఈ కార్డ్‌ను సేవ్ చేయాలని మీరు కోరుకుంటున్నారా?</translation>
<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="4196861286325780578">&amp;తరలించడాన్ని పునరావృతం చేయి</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ఫైర్‌వాల్ మరియు యాంటీవైరస్ కాన్ఫిగరేషన్‌లను తనిఖీ చేయడం<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">క్రాష్‌లు</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">ఈ సైట్‌లోని దాడి చేసేవారు మీ సమాచారాన్ని (ఉదాహరణకు, ఫోటోలు, పాస్‌వర్డ్‌లు, సందేశాలు మరియు క్రెడిట్ కార్డ్‌లు) దొంగిలించడం కోసం లేదా తొలగించడం కోసం మీ కంప్యూటర్‌లో ప్రమాదకరమైన ప్రోగ్రామ్‌లను ఇన్‌స్టాల్ చేయడానికి ప్రయత్నించవచ్చు.</translation>
<translation id="4372948949327679948">ఆశిస్తున్న <ph name="VALUE_TYPE" /> విలువ.</translation>
<translation id="4377125064752653719"><ph name="DOMAIN" />ను చేరుకోవడానికి మీరు ప్రయత్నించారు, కానీ సర్వర్ అందించిన ప్రమాణపత్రాన్ని దాన్ని జారీ చేసినవారు రద్దు చేసారు. సర్వర్ అందించిన భద్రత ఆధారాలు ఖచ్చితంగా విశ్వసించబడలేదని దీని అర్థం. మీరు దాడి చేసే వారితో కమ్యూనికేట్ చేస్తూ ఉండవచ్చు.</translation>
-<translation id="4381091992796011497">యూజర్ పేరు:</translation>
<translation id="4394049700291259645">ఆపివెయ్యి</translation>
<translation id="4406896451731180161">శోధన ఫలితాలు</translation>
<translation id="4424024547088906515">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రాన్ని Chrome విశ్వసించలేదు. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> మీ లాగిన్ ప్రమాణపత్రాన్ని ఆమోదించలేదు లేదా ఏదీ అందించి ఉండకపోవచ్చు.</translation>
<translation id="443673843213245140">ప్రాక్సీని ఉపయోగించడం ఆపివేయబడింది కానీ స్పష్టమైన ప్రాక్సీ కాన్ఫిగరేషన్ పేర్కొనబడింది.</translation>
+<translation id="445100540951337728">ఆమోదించబడిన డెబిట్ కార్డ్‌లు</translation>
<translation id="4506176782989081258">ధృవీకరణ లోపం: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">సిస్టమ్ నిర్వాహకుడిని సంప్రదించడం</translation>
<translation id="450710068430902550">నిర్వాహకుడితో భాగస్వామ్యం</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Chrome నుండి చిరునామాను తీసివేయాలా?</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" />కి గల మీ కనెక్షన్ ఆధునిక సైఫర్ సూట్ ఉపయోగించి గుప్తీకరించబడింది.</translation>
<translation id="4594403342090139922">&amp;తొలగించడాన్ని రద్దు చేయి</translation>
+<translation id="4611292653554630842">లాగిన్ చేయండి</translation>
<translation id="4619615317237390068">ఇతర పరికరాల్లోని ట్యాబ్‌లు</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రంలో లోపాలు ఉన్నాయి. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
<translation id="4690462567478992370">చెల్లని ప్రమాణపత్రాన్ని ఉపయోగించడాన్ని ఆపివేయి</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">మీ కనెక్షన్‌కు అంతరాయం ఏర్పడింది</translation>
+<translation id="471880041731876836">ఈ సైట్‌ను సందర్శించడానికి మీకు అనుమతి లేదు</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows నెట్‌వర్క్ సమస్య విశ్లేషణలను అమలు చేయడం<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">విధానాలను మళ్లీ లోడ్ చేయి</translation>
<translation id="4728558894243024398">ప్లాట్‌ఫారమ్</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">వీక్షణ</translation>
<translation id="4854362297993841467">ఈ బట్వాడా పద్ధతి అందుబాటులో లేదు. వేరే పద్ధతిని ప్రయత్నించండి.</translation>
<translation id="4858792381671956233">ఈ సైట్‌ను సందర్శించడానికి అనుమతించమని కోరుతూ మీ తల్లిదండ్రులకు అభ్యర్థన పంపారు</translation>
-<translation id="4863764087567530506">సాఫ్ట్‌వేర్‌ని ఇన్‌స్టాల్ చేయడం లేదా వ్యక్తిగత సమాచారాన్ని బహిర్గతం చేయడం ద్వారా ఈ కంటెంట్ మిమ్మల్ని మోసం చేయడానికి ప్రయత్నించి ఉండవచ్చు. <ph name="BEGIN_LINK" />ఏదేమైనా చూపు<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">శోధన చరిత్ర</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">ప్రమాణీకరణ అవసరం</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{మరియు మరో 1 వెబ్ పేజీ}other{మరియు మరో # వెబ్ పేజీలు}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">ఈ పేజీ తెలియని భాష నుండి <ph name="LANGUAGE_LANGUAGE" />కు అనువదించబడింది</translation>
<translation id="4923459931733593730">చెల్లింపు</translation>
<translation id="4926049483395192435">ఖచ్చితంగా పేర్కొనాలి.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">చర్యలు</translation>
<translation id="4958444002117714549">జాబితాను విస్తరించు</translation>
<translation id="4974590756084640048">హెచ్చరికలను మళ్లీ ప్రారంభించు</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">సరికాని పాస్‌వర్డ్</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>
<translation id="5087580092889165836">కార్డ్‌ను జోడించు</translation>
<translation id="5089810972385038852">రాష్ట్రం</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">ప్రావిన్స్</translation>
<translation id="5115563688576182185">(64-బిట్)</translation>
<translation id="5141240743006678641">మీ Google ఆధారాలతో సమకాలీకరించబడిన పాస్‌వర్డ్‌లను గుప్తీకరించండి</translation>
-<translation id="514421653919133810">అజ్ఞాత మోడ్‌లో పేజీని తెరవండి (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">విధాన ప్రతిస్పందనలో లోపం కోడ్ ఉంది</translation>
+<translation id="5159010409087891077">కొత్త అజ్ఞాత విండోలో పేజీని తెరవండి (⇧⌘N)</translation>
<translation id="5171045022955879922">URLను శోధించండి లేదా టైప్ చేయండి</translation>
<translation id="5172758083709347301">మెషీన్</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />లో లేదా? ఈ లోపాన్ని నివేదించండి</translation>
-<translation id="5181140330217080051">డౌన్‌లోడ్ చేస్తోంది</translation>
<translation id="5190835502935405962">బుక్‌మార్క్‌ల బార్</translation>
<translation id="5199729219167945352">ప్రయోగాలు</translation>
<translation id="5205222826937269299">పేరు ఆవశ్యకం</translation>
<translation id="5222812217790122047">ఇమెయిల్ ఆవశ్యకం</translation>
<translation id="5251803541071282808">క్లౌడ్</translation>
<translation id="5277279256032773186">కార్యాలయంలో Chrome ఉపయోగిస్తున్నారా? వ్యాపార సంస్థలు తమ ఉద్యోగుల కోసం Chrome సెట్టింగ్‌లను నిర్వహించగలవు. మరింత తెలుసుకోండి</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />సాఫ్ట్‌వేర్‌ను తాత్కాలికంగా నిలిపివేయడానికి ఈ దశలను పాటించండి తద్వారా మీరు వెబ్‌ను ప్రాప్యత చేయవచ్చు. మీకు నిర్వాహక అధికారాలు అవసరం.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />ప్రారంభించు<ph name="END_BOLD" /> క్లిక్ చేయండి, ఆపై <ph name="BEGIN_BOLD" />"స్థానిక సేవలను వీక్షించు"<ph name="END_BOLD" />ను శోధించి, ఎంచుకోండి
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" /> ఎంచుకోండి
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />ప్రారంభ రకం<ph name="END_BOLD" />లో, <ph name="BEGIN_BOLD" />నిలిపివేయబడింది<ph name="END_BOLD" /> ఎంచుకోండి
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />సేవ స్థితి<ph name="END_BOLD" />లో, <ph name="BEGIN_BOLD" />ఆపివేయి<ph name="END_BOLD" /> క్లిక్ చేయండి
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />వర్తింపజేయి<ph name="END_BOLD" /> క్లిక్ చేయండి, ఆపై <ph name="BEGIN_BOLD" />సరే<ph name="END_BOLD" /> క్లిక్ చేయండి
+ <ph name="LIST_ITEM" />మీ కంప్యూటర్ నుండి శాశ్వతంగా సాఫ్ట్‌వేర్‌ను ఎలా తొలగించాలో తెలుసుకోవడానికి <ph name="BEGIN_LEARN_MORE_LINK" />Chrome సహాయ కేంద్రం<ph name="END_LEARN_MORE_LINK" />ని సందర్శించండి
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">ఈ సైట్‌కు మీ కనెక్షన్ ప్రైవేట్ కాదు. ఎప్పుడైనా VR మోడ్ నుండి నిష్క్రమించడానికి, హెడ్‌సెట్‌ను తీసివేసి, వెనుకకు నొక్కండి.</translation>
<translation id="5299298092464848405">విధానాన్ని అన్వయించడంలో లోపం</translation>
<translation id="5308689395849655368">క్రాష్ నివేదిక నిలిపివెయ్యబడింది.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />"లో స్కీమా ప్రామాణీకరణ లోపం: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">ఈ <ph name="HOST_NAME" /> పేజీ కనుగొనబడలేదు</translation>
<translation id="5455374756549232013">చెల్లని విధాన సమయముద్ర</translation>
-<translation id="5455790498993699893"><ph name="TOTAL_MATCHCOUNT" />లో <ph name="ACTIVE_MATCH" /></translation>
<translation id="5457113250005438886">చెల్లదు</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;సవరించడాన్ని పునరావృతం చేయి</translation>
+<translation id="5481076368049295676">ఈ కంటెంట్ మీ సమాచారాన్ని దొంగిలించగల లేదా తొలగించగల హానికరమైన సాఫ్ట్‌వేర్‌ని మీ పరికరంలో ఇన్‌స్టాల్ చేయడానికి ప్రయత్నించవచ్చు. <ph name="BEGIN_LINK" />ఏదేమైనా చూపు<ph name="END_LINK" /></translation>
<translation id="54817484435770891">చెల్లుబాటు అయ్యే చిరునామాను జోడించండి</translation>
<translation id="5492298309214877701">కంపెనీ, సంస్థ లేదా పాఠశాల ఇంట్రానెట్‌లోని ఈ సైట్ బాహ్య వెబ్‌సైట్ కలిగి ఉన్న అదే URLని కలిగి ఉంది.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">అభ్యర్థించిన కథనాన్ని కనుగొనడం సాధ్యపడలేదు.</translation>
<translation id="5544037170328430102"><ph name="SITE" />లో పొందుపరిచిన పేజీ ఇలా చెబుతోంది:</translation>
<translation id="5556459405103347317">రీలోడ్</translation>
+<translation id="5560088892362098740">గడువు ముగింపు తేదీ</translation>
<translation id="5565735124758917034">సక్రియం</translation>
<translation id="5571083550517324815">ఈ చిరునామా నుండి పికప్ చేసుకోవడం సాధ్యం కాదు. వేరే చిరునామాని ఎంచుకోండి.</translation>
<translation id="5572851009514199876">దయచేసి Chromeని ప్రారంభించి, దానికి సైన్ ఇన్ చేయండి, అప్పుడు మీకు ఈ సైట్‌ను ప్రాప్యత చేయడానికి అనుమతి ఉందో లేదో Chrome తనిఖీ చేయగలదు.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">విధాన సెట్టింగ్‌లను లోడ్ చేయడంలో విఫలమైంది</translation>
<translation id="5631439013527180824">చెల్లని పరికర నిర్వహణ టోకెన్</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="5646376287012673985">స్థానం</translation>
<translation id="5659593005791499971">ఇమెయిల్</translation>
<translation id="5669703222995421982">వ్యక్తిగతీకరించిన కంటెంట్‌ను పొందండి</translation>
<translation id="5675650730144413517">ఈ పేజీ పని చేయడం లేదు</translation>
<translation id="5710435578057952990">ఈ వెబ్‍‌సైట్ యొక్క గుర్తింపు నిర్థారించబడలేదు.</translation>
-<translation id="5713016350996637505">మోసపూరితమైన కంటెంట్ బ్లాక్ చేయబడింది</translation>
+<translation id="5719499550583120431">ప్రీపెయిడ్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
<translation id="5720705177508910913">ప్రస్తుత వినియోగదారు</translation>
<translation id="5732392974455271431">మీ తల్లిదండ్రులు దీన్ని మీ కోసం అన్‌బ్లాక్ చేయగలరు</translation>
<translation id="5763042198335101085">చెల్లుబాటు అయ్యే ఇమెయిల్ చిరునామాని నమోదు చేయండి</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">మీరు మీ <ph name="CARD_DETAIL" /> కార్డ్ సమాచారం పూరించాలనుకుంటున్నారా?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" />కి గల మీ కనెక్షన్ వాడుకలో లేని సైఫర్ సూట్ ఉపయోగించి గుప్తీకరించబడింది.</translation>
<translation id="5813119285467412249">&amp;జోడించడాన్ని పునరావృతం చేయి</translation>
-<translation id="5814352347845180253">మీరు <ph name="SITE" /> మరియు మరికొన్ని ఇతర సైట్‌ల నుండి ప్రీమియం కంటెంట్‌కి ప్రాప్యతను కోల్పోవచ్చు.</translation>
<translation id="5838278095973806738">మీరు ఈ సైట్‌లో ఎలాంటి గోప్యమైన సమాచారాన్ని నమోదు చేయకూడదు (ఉదాహరణకు, పాస్‌వర్డ్‌లు లేదా క్రెడిట్ కార్డ్‌లు), దాడికి పాల్పడేవారు ఆ సమాచారం దొంగిలించే అవకాశం ఉంటుంది.</translation>
<translation id="5869405914158311789">ఈ సైట్‌ను చేరుకోలేకపోయాము</translation>
<translation id="5869522115854928033">సేవ్ చేసిన పాస్‌వర్డ్‌లు</translation>
<translation id="5872918882028971132">తల్లి/తండ్రి సూచనలు</translation>
+<translation id="5893752035575986141">క్రెడిట్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
<translation id="5901630391730855834">పసుపు</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (సమకాలీకరించబడింది)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 వినియోగంలో ఉంది}other{# వినియోగంలో ఉన్నాయి}}</translation>
-<translation id="5926846154125914413">మీరు కొన్ని సైట్‌ల నుండి ప్రీమియం కంటెంట్‌కి ప్రాప్యతను కోల్పోవచ్చు.</translation>
<translation id="5959728338436674663">హానికరమైన అనువర్తనాలు మరియు సైట్‌లను గుర్తించడంలో సహాయపడటానికి కొంత <ph name="BEGIN_WHITEPAPER_LINK" />సిస్టమ్ సమాచారాన్ని మరియు పేజీ కంటెంట్<ph name="END_WHITEPAPER_LINK" />ను Googleకు స్వయంచాలకంగా పంపుతుంది. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">చరిత్ర నుండి తీసివేయి</translation>
<translation id="5975083100439434680">దూరంగా జూమ్ చెయ్యి</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">మూసివేయి</translation>
<translation id="6042308850641462728">మరింత</translation>
<translation id="6047233362582046994">మీ భద్రతకు వాటిల్లే ఆపదల గురించి మీకు అర్థం అయ్యి ఉంటే, హానికర యాప్‌లు తీసివేయబడటానికి ముందే మీరు <ph name="BEGIN_LINK" />ఈ సైట్‌ను సందర్శించవచ్చు<ph name="END_LINK" />.</translation>
+<translation id="6047927260846328439">ఈ కంటెంట్ సాఫ్ట్‌వేర్‌ని ఇన్‌స్టాల్ చేయడానికి లేదా వ్యక్తిగత సమాచారాన్ని బహిర్గతం చేయడానికి పురిగొల్పేలా మిమ్మల్ని మాయ చేయడానికి ప్రయత్నించవచ్చు. <ph name="BEGIN_LINK" />ఏదేమైనా చూపు<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">ప్రమాణపత్రాన్ని పిన్ చేసే పద్ధతిని వెబ్‌సైట్ ఉపయోగిస్తుంది కనుక మీరు ప్రస్తుతానికి <ph name="SITE" />ని సందర్శించలేరు. నెట్‌వర్క్ లోపాలు మరియు దాడులు సాధారణంగా తాత్కాలికమే, కనుక ఈ పేజీ తర్వాత పని చేయవచ్చు.</translation>
<translation id="6060685159320643512">జాగ్రత్త, ఈ ప్రయోగాలు విఫలం కావచ్చు</translation>
<translation id="6080696365213338172">మీరు నిర్వాహకుని ద్వారా అందించబడిన ప్రమాణపత్రాన్ని ఉపయోగించి కంటెంట్‌ను ప్రాప్యత చేసారు. మీరు <ph name="DOMAIN" />కు అందించే డేటాకు మీ నిర్వాహకుని ద్వారా అంతరాయం ఏర్పడవచ్చు.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">మరింత తెలుసుకోండి</translation>
<translation id="6169916984152623906">‌ఇప్పుడు మీరు వ్యక్తిగతంగా బ్రౌజ్ చేయవచ్చు మరియు ఈ పరికరాన్ని ఉపయోగించే ఇతర వ్యక్తులకు మీ కార్యాచరణ కనిపించదు. అయినప్పటికీ, డౌన్‌లోడ్‌లు మరియు బుక్‌మార్క్‌లు సేవ్ చేయబడతాయి.</translation>
<translation id="6177128806592000436">ఈ సైట్‌కి మీ కనెక్షన్ సురక్షితంగా లేదు</translation>
-<translation id="6184817833369986695">(బృందం: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">మీ ఇంటర్నెట్ కనెక్షన్‌ను తనిఖీ చేయండి</translation>
<translation id="6218753634732582820">Chromium నుండి చిరునామాను తీసివేయాలా?</translation>
<translation id="6221345481584921695">Google సురక్షిత బ్రౌజింగ్ ఇటీవల <ph name="BEGIN_LINK" />లో <ph name="END_LINK" />మాల్వేర్ గుర్తించింది<ph name="SITE" />. సాధారణంగా సురక్షితమైన వెబ్‌సైట్‌‌లకు కూడా కొన్నిసార్లు మాల్వేర్ సోకుతుంది. ఇటువంటి హానికరమైన కంటెంట్ మాల్వేర్ పంపిణీదారుగా ప్రసిద్ధిగాంచిన <ph name="SUBRESOURCE_HOST" /> నుండి సంక్రమిస్తుంది.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">అద్భుతమైన క్రొత్త Chrome లక్షణాల పట్ల ఆసక్తిగా ఉన్నారా? chrome.com/betaలో మా బీటా ఛానెల్‌ను ప్రయత్నించండి.</translation>
-<translation id="6389758589412724634">Chromium ఈ వెబ్‌పేజీని ప్రదర్శించడానికి ప్రయత్నిస్తున్నప్పుడు మెమరీ అయిపోయింది.</translation>
+<translation id="6397451950548600259">మీ కంప్యూటర్‌లోని సాఫ్ట్‌వేర్ Chrome సురక్షితంగా వెబ్‌కు కనెక్ట్ కాకుండా ఆపుతోంది</translation>
<translation id="6404511346730675251">బుక్‌మార్క్‌ను సవరించు</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> గడువు ముగింపు తేదీ మరియు CVCని నమోదు చేయండి</translation>
<translation id="6414888972213066896">మీరు ఈ సైట్‌ని సందర్శించడానికి అనుమతించమని కోరుతూ మీ తల్లి/తండ్రికి అభ్యర్థన పంపారు</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">పరికర విధానాలు</translation>
<translation id="6477321094435799029">Chrome ఈ పేజీలో అసాధారణ కోడ్‌ను గుర్తించింది మరియు మీ వ్యక్తిగత సమాచారం (ఉదాహరణకు, పాస్‌వర్డ్‌లు, ఫోన్ నంబర్‌లు మరియు క్రెడిట్ కార్డ్‌లు) రక్షించడానికి దాన్ని బ్లాక్ చేసింది.</translation>
<translation id="6489534406876378309">క్రాష్‌లను అప్‌లోడ్ చేయడాన్ని ప్రారంభించండి</translation>
+<translation id="6507833130742554667">క్రెడిట్ మరియు డెబిట్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
<translation id="6508722015517270189">Chromeను పునఃప్రారంభించండి</translation>
<translation id="6529602333819889595">&amp;తొలగించడాన్ని పునరావృతం చేయి</translation>
<translation id="6534179046333460208">ప్రత్యక్ష వెబ్ సూచనలు</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">ఈ కంటెంట్ మీ సమాచారాన్ని దొంగిలించగల లేదా తొలగించగల హానికరమైన సాఫ్ట్‌వేర్‌ని మీ పరికరంలో ఇన్‌స్టాల్ చేయడానికి ప్రయత్నించి ఉండవచ్చు. <ph name="BEGIN_LINK" />ఏదేమైనా చూపు<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">గుప్తీకరణ ఎంపికలు</translation>
<translation id="662080504995468778">ఇందులోనే ఉంచు</translation>
<translation id="6626291197371920147">చెల్లుబాటయ్యే కార్డ్ నంబర్‌ను జోడించండి</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> శోధన</translation>
<translation id="6630809736994426279"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />లో హ్యాకర్‌లు మీ సమాచారాన్ని (ఉదాహరణకు, ఫోటోలు, పాస్‌వర్డ్‌లు, సందేశాలు మరియు క్రెడిట్ కార్డ్‌లు) దొంగిలించగల లేదా తొలగించగల హానికరమైన ప్రోగ్రామ్‌లను మీ Macలో ఇన్‌స్టాల్ చేయడానికి ప్రయత్నించవచ్చు. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలుసుకోండి<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">ఈ విధానం విలువ తగ్గించబడింది.</translation>
+<translation id="6657585470893396449">పాస్‌వర్డ్</translation>
<translation id="6671697161687535275">Chromium నుండి ఫారమ్ సూచనను తీసివేయాలా?</translation>
<translation id="6685834062052613830">సైన్ అవుట్ చేసి, సెటప్‌ను పూర్తి చేయండి</translation>
<translation id="6710213216561001401">మునుపటి</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">జిల్లా</translation>
<translation id="6973656660372572881">రెండు స్థిర ప్రాక్సీ సర్వర్లు మరియు ఒక .pac స్క్రిప్ట్ URL పేర్కొనబడ్డాయి.</translation>
<translation id="6989763994942163495">అధునాతన సెట్టింగ్‌లను చూపించు...</translation>
-<translation id="7000990526846637657">చరిత్ర నమోదులు ఏవీ కనుగొనబడలేదు</translation>
<translation id="7012363358306927923">చైనా యూనియన్ పే</translation>
<translation id="7012372675181957985">మీ Google ఖాతా <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />లో ఇతర బ్రౌజింగ్ చరిత్ర రూపాలు ఉండవచ్చు.</translation>
<translation id="7029809446516969842">పాస్‌వర్డ్‌లు</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">ఈ చిరునామాకు రవాణా చేయడం సాధ్యం కాదు. వేరే చిరునామాని ఎంచుకోండి.</translation>
<translation id="7138472120740807366">బట్వాడా పద్ధతి</translation>
<translation id="7139724024395191329">ఎమిరేట్</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">చెల్లింపు సురక్షితం కాదు</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">ఇప్పుడే పునఃప్రారంభించు</translation>
<translation id="7180611975245234373">రీఫ్రెష్ చేయి</translation>
<translation id="7182878459783632708">విధానాలను సెట్ చేయలేదు</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">డౌన్‌లోడ్ చేయి</translation>
<translation id="7518003948725431193">వెబ్ చిరునామాకు వెబ్‌పేజీ కనుగొనబడలేదు: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">ఈ సైట్‌కి మీ కనెక్షన్ ప్రైవేట్ కాదు</translation>
<translation id="7535087603100972091">విలువ</translation>
<translation id="7537536606612762813">తప్పనిసరి</translation>
<translation id="7542403920425041731">మీరు నిర్ధారించిన తర్వాత, మీ కార్డ్ వివరాలు ఈ సైట్‌తో షేర్ చేయబడతాయి.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">క్రింది చిట్కాలను ప్రయత్నించండి:</translation>
<translation id="7554791636758816595">కొత్త ట్యాబ్</translation>
<translation id="7567204685887185387">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రం మోసపూరితంగా జారీ అయ్యి ఉండవచ్చు. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">ఈ పేజీ<ph name="ORIGINAL_LANGUAGE" />లో ఉంది మీరు దీన్ని అనువదించాలనుకుంటున్నారా?</translation>
<translation id="7569952961197462199">Chrome నుండి క్రెడిట్ కార్డ్‌ను తీసివేయాలా?</translation>
<translation id="7569983096843329377">నలుపు</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">సర్టిఫికెట్</translation>
<translation id="7716147886133743102">మీ నిర్వాహకుల ద్వారా బ్లాక్ చేయబడింది</translation>
<translation id="7716424297397655342">కాష్ నుండి ఈ సైట్‌ను లోడ్ చేయలేకపోయాము</translation>
+<translation id="774634243536837715">హానికరమైన కంటెంట్ బ్లాక్ చేయబడింది.</translation>
<translation id="7752995774971033316">నిర్వహించడం లేదు</translation>
<translation id="7755287808199759310">మీ తల్లి/తండ్రి దీన్ని మీ కోసం అన్‌బ్లాక్ చేయగలరు</translation>
<translation id="7758069387465995638">ఫైర్‌వాల్ లేదా యాంటీవైరస్ సాఫ్ట్‌వేర్ కనెక్షన్‌ను బ్లాక్ చేసి ఉండవచ్చు.</translation>
+<translation id="7759163816903619567">ప్రదర్శన డొమైన్:</translation>
<translation id="7761701407923456692">URLతో సర్వర్ ప్రమాణపత్రం సరిపోలడం లేదు.</translation>
<translation id="7763386264682878361">చెల్లింపు మానిఫెస్ట్ అన్వయ నియమం</translation>
<translation id="7764225426217299476">చిరునామాను జోడించు</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">'<ph name="SEARCH_STRING" />' కోసం <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> కనుగొనబడ్డాయి</translation>
<translation id="785549533363645510">అయితే, మీరు అదృశ్యంగా ఉండరు. అజ్ఞాతంలోకి వెళ్లడం వలన మీ బ్రౌజింగ్ మీ యజమానికి, మీ ఇంటర్నెట్ సేవా ప్రదాతకు లేదా మీరు సందర్శించే వెబ్‌సైట్‌లకు కనిపించకుండా దాచబడదు.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">డెబిట్ మరియు ప్రీపెయిడ్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
<translation id="7887683347370398519">మీ CVCని తనిఖీ చేసి, మళ్లీ ప్రయత్నించండి</translation>
<translation id="79338296614623784">చెల్లుబాటు అయ్యే ఫోన్ నంబర్‌ని నమోదు చేయండి</translation>
<translation id="7935318582918952113">DOM డిస్టిల్లర్</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">ప్రతిస్పందనను పంపండి</translation>
<translation id="8041940743680923270">సార్వజనీన డిఫాల్ట్‌ను ఉపయోగించు (అడుగు)</translation>
<translation id="8088680233425245692">కథనాన్ని వీక్షించడంలో విఫలమైంది.</translation>
-<translation id="8089520772729574115">1 MB కంటే తక్కువ</translation>
<translation id="8091372947890762290">సక్రియం సర్వర్‌లో పెండింగ్‌లో ఉంది</translation>
<translation id="8118489163946903409">చెల్లింపు పద్ధతి</translation>
<translation id="8131740175452115882">నిర్ధారించు</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;తరలించడాన్ని రద్దు చేయి</translation>
<translation id="8201077131113104583">ID "<ph name="EXTENSION_ID" />" ఉన్న పొడిగింపు కోసం నవీకరణ URL చెల్లదు.</translation>
<translation id="8202097416529803614">ఆర్డర్ సారాంశం</translation>
+<translation id="8205463626947051446">సైట్ అనుచితమైన ప్రకటనలను చూపవచ్చు</translation>
<translation id="8218327578424803826">కేటాయించిన స్థానం:</translation>
<translation id="8225771182978767009">ఈ కంప్యూటర్‌ను సెటప్ చేసిన వ్యక్తి ఈ సైట్‌ను బ్లాక్ చేయడానికి ఎంచుకున్నారు.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">కొత్త అజ్ఞాత ట్యాబ్‌లో పేజీని తెరవండి</translation>
<translation id="8241707690549784388">మీరు వెతికే పేజీ మీరు ఎంటర్ చేసిన సమాచారాన్ని ఉపయోగించుకుంది. ఆ పేజీకి తిరిగి వెళ్ళడం ద్వారా మీరు చేసిన ఏ చర్య అయినా పునరావృతం చెయ్యవలసి వస్తుంది. మీరు కొనసాగాలనుకుంటున్నారా?</translation>
+<translation id="8241712895048303527">ఈ సైట్‌లో బ్లాక్ చేయి</translation>
<translation id="8249320324621329438">చివరగా పొందబడినవి:</translation>
<translation id="8253091569723639551">బిల్లింగ్ చిరునామా ఆవశ్యకం</translation>
<translation id="8261506727792406068">తొలగించు</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">నెట్‌వర్క్ కనెక్షన్‌తో సమస్య ఉన్నందున అనువాదం విఫలమైంది.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" />కి ప్రాప్యత నిరాకరించబడింది</translation>
<translation id="834457929814110454">మీ భద్రతకు వాటిల్లే ఆపదల గురించి మీకు అర్థం అయ్యి ఉంటే, హానికర ప్రోగ్రామ్‌లు తీసివేయబడటానికి ముందే మీరు <ph name="BEGIN_LINK" />ఈ సైట్‌ను సందర్శించవచ్చు<ph name="END_LINK" />.</translation>
-<translation id="8344669043927012510">అజ్ఞాత మోడ్‌లో పేజీని తెరవండి (⇧⌘N)</translation>
<translation id="8349305172487531364">బుక్‌మార్క్‌ల పట్టీ</translation>
<translation id="8363502534493474904">ఎయిర్‌ప్లైన్ మోడ్‌ను ఆఫ్ చేయడం</translation>
<translation id="8364627913115013041">సెట్ చేయలేదు.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">క్రాష్ నివేదిక <ph name="UPLOAD_TIME" />కి అప్‌లోడ్ చేయబడింది</translation>
<translation id="8412145213513410671">క్రాష్‌లు (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">మీరు అదే పాస్‌ఫ్రేజ్‌ని రెండుసార్లు ఖచ్చితంగా ఎంటర్ చెయ్యాలి.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">సెట్టింగ్‌లు</translation>
<translation id="8433057134996913067">దీని వలన మీరు చాలా వెబ్‌సైట్‌ల నుండి సైన్ అవుట్ చేయబడతారు.</translation>
<translation id="8437238597147034694">&amp;తరలించడాన్ని రద్దు చేయి</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">మీ Google ఖాతా నుండి కార్డ్‌లను ఉపయోగించేందుకు, Chromeకి సైన్ ఇన్ చేయండి</translation>
<translation id="8488350697529856933">వీటికి వర్తిస్తుంది</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ప్రతిస్పందించడానికి చాలా ఎక్కువ సమయం పట్టింది.</translation>
-<translation id="8532105204136943229">గడువు ముగింపు సంవత్సరం</translation>
+<translation id="8503813439785031346">యూజర్‌పేరు</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="8571890674111243710">పేజీని <ph name="LANGUAGE" />కు అనువదిస్తోంది...</translation>
<translation id="858637041960032120">ఫోన్ నం. జోడిం.
@@ -887,6 +919,7 @@
<translation id="859285277496340001">ఇది రద్దు చెయ్యబడిందా అని తనిఖీ చెయ్యడానికి ప్రమాణపత్రం విధానాన్ని పేర్కొనలేదు.</translation>
<translation id="8620436878122366504">మీ తల్లిదండ్రులు దీన్ని ఇంకా ఆమోదించలేదు</translation>
<translation id="8647750283161643317">అన్నింటినీ డిఫాల్ట్‌కు రీసెట్ చేయి</translation>
+<translation id="8660471606262461360">Google Payments నుండి</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" />కు మీ కనెక్షన్ గుప్తీకరించబడలేదు.</translation>
<translation id="8718314106902482036">చెల్లింపు పూర్తి కాలేదు</translation>
<translation id="8725066075913043281">మళ్ళీ ప్రయత్నించండి</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">దగ్గరికి జూమ్ చెయ్యి</translation>
<translation id="8931333241327730545">మీరు ఈ కార్డ్‌ను మీ Google ఖాతాకి సేవ్ చేయాలనుకుంటున్నారా?</translation>
<translation id="8932102934695377596">మీ గడియారం సమయం గతంలో ఉంది</translation>
+<translation id="8938939909778640821">ఆమోదించబడిన క్రెడిట్ మరియు ప్రీపెయిడ్ కార్డ్‌లు</translation>
<translation id="8971063699422889582">సర్వర్ యొక్క ప్రమాణపత్రం గడువు ముగిసింది.</translation>
<translation id="8986494364107987395">Googleకు స్వయంచాలకంగా ఉపయోగ గణాంకాలను మరియు క్రాష్ నివేదికలను పంపు</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">ఈ సైట్‌లో ఎల్లప్పుడూ బ్లాక్ చేయి</translation>
<translation id="9170848237812810038">&amp;అన్డు</translation>
<translation id="917450738466192189">సర్వర్ యొక్క ప్రమాణపత్రం చెల్లుబాటు కాదు.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> మరియు మరో <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> మద్దతు లేని ప్రోటోకాల్‌ను ఉపయోగిస్తోంది.</translation>
<translation id="9205078245616868884">మీ సమకాలీకరణ రహస్య పదబంధంతో మీ డేటా గుప్తీకరించబడింది. సమకాలీకరణను ప్రారంభించడానికి దీన్ని నమోదు చేయండి.</translation>
<translation id="9207861905230894330">కథనాన్ని జోడించడంలో విఫలమైంది.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">డైనర్స్ క్లబ్</translation>
<translation id="935608979562296692">ఫారమ్‌ను తీసివేయండి</translation>
<translation id="939736085109172342">క్రొత్త ఫోల్డర్</translation>
-<translation id="941721044073577244">ఈ సైట్‌ను సందర్శించడానికి మీకు అనుమతి లేనట్లుగా కనిపిస్తోంది</translation>
<translation id="969892804517981540">అధికారిక బిల్డ్</translation>
<translation id="975560348586398090">{COUNT,plural, =0{ఏమీ లేవు}=1{1 అంశం}other{# అంశాలు}}</translation>
+<translation id="981121421437150478">ఆఫ్‌లైన్</translation>
<translation id="988159990683914416">డెవలపర్ బిల్డ్</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_th.xtb b/chromium/components/strings/components_strings_th.xtb
index ddf56834dec..8368e45d3a1 100644
--- a/chromium/components/strings/components_strings_th.xtb
+++ b/chromium/components/strings/components_strings_th.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">ปิดแอปอื่นๆ</translation>
<translation id="1055184225775184556">&amp;เลิกทำการเพิ่ม</translation>
<translation id="10614374240317010">ไม่เคยบันทึก</translation>
+<translation id="1066396345355680611">คุณอาจสูญเสียสิทธิ์เข้าถึงเนื้อหาที่มีการคุ้มครองจาก <ph name="SITE" /> และไซต์อื่นๆ บางแห่ง</translation>
<translation id="106701514854093668">บุ๊กมาร์กบนเดสก์ท็อป</translation>
<translation id="1074497978438210769">ไม่ปลอดภัย</translation>
<translation id="1080116354587839789">พอดีกับความกว้าง</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">เรื่องรออ่าน</translation>
<translation id="1264126396475825575">รายงานข้อขัดข้องเมื่อ <ph name="CRASH_TIME" /> (ยังไม่ได้อัปโหลดหรือละเว้น)</translation>
<translation id="1281526147609854549">ออกโดย <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">บล็อกเนื้อหาอันตรายแล้ว</translation>
<translation id="1285320974508926690">ไม่ต้องแปลไซต์นี้</translation>
<translation id="129553762522093515">เพิ่งปิด</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />ลองล้างคุกกี้<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">โดเมนการลงทะเบียน:</translation>
<translation id="1340482604681802745">ที่อยู่ในการรับ</translation>
-<translation id="1344211575059133124">ดูเหมือนว่าคุณต้องได้รับสิทธิ์เพื่อเข้าชมเว็บไซต์นี้</translation>
<translation id="1344588688991793829">การตั้งค่าป้อนข้อความอัตโนมัติของ Chromium...</translation>
<translation id="1348198688976932919">ไซต์ที่จะเปิดมีแอปอันตราย</translation>
<translation id="1374468813861204354">คำแนะนำ</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">ข้อมูลประจำตัวของ <ph name="ORGANIZATION" /> ใน <ph name="LOCALITY" /> ได้รับการยืนยันโดย <ph name="ISSUER" /></translation>
<translation id="1426410128494586442">ใช่</translation>
<translation id="1430915738399379752">พิมพ์</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> รายการ}other{<ph name="PAYMENT_METHOD_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> รายการ}}</translation>
<translation id="1506687042165942984">แสดงสำเนาที่บันทึกไว้ (หรือที่ล้าสมัย) ของหน้านี้</translation>
<translation id="1517433312004943670">ต้องระบุหมายเลขโทรศัพท์</translation>
+<translation id="1517500485252541695">บัตรเครดิตและบัตรเดบิตที่ยอมรับ</translation>
<translation id="1519264250979466059">วันที่สร้าง</translation>
+<translation id="1527263332363067270">กำลังรอการเชื่อมต่อ…</translation>
<translation id="153384715582417236">เสร็จเรียบร้อย</translation>
<translation id="1549470594296187301">ต้องเปิดใช้ JavaScript เพื่อใช้ฟีเจอร์นี้</translation>
<translation id="1555130319947370107">สีน้ำเงิน</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">ลองติดต่อผู้ดูแลระบบ</translation>
<translation id="1740951997222943430">ป้อนเดือนที่หมดอายุที่ถูกต้อง</translation>
-<translation id="1745358365027406341">ดาวน์โหลดหน้าเว็บในภายหลัง</translation>
<translation id="17513872634828108">แท็บที่เปิดอยู่</translation>
<translation id="1753706481035618306">เลขหน้า</translation>
<translation id="1763864636252898013">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะระบบปฏิบัติการของอุปกรณ์ของคุณไม่เชื่อถือใบรับรองความปลอดภัย โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">ช่องที่ต้องกรอก</translation>
<translation id="187918866476621466">เปิดหน้าเริ่มต้นใช้งาน</translation>
<translation id="1883255238294161206">ยุบรายการ</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> รายการ}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> รายการ}}</translation>
<translation id="1898423065542865115">การกรอง</translation>
+<translation id="1916770123977586577">โหลดหน้านี้ซ้ำเพื่อใช้การตั้งค่าที่อัปเดตกับไซต์นี้</translation>
+<translation id="1919345977826869612">โฆษณา</translation>
<translation id="192020519938775529">{COUNT,plural, =0{ไม่มี}=1{เว็บไซต์ 1 แห่ง}other{เว็บไซต์ # แห่ง}}</translation>
<translation id="194030505837763158">ไปที่ <ph name="LINK" /></translation>
+<translation id="1948773908305951926">บัตรเติมเงินที่ยอมรับ</translation>
<translation id="1962204205936693436">บุ๊กมาร์กของ <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">ข้อผิดพลาดในการจัดเรียง</translation>
<translation id="1974060860693918893">ขั้นสูง</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">ข้อผิดพลาดของ HTTP</translation>
<translation id="2270484714375784793">หมายเลขโทรศัพท์</translation>
<translation id="2282872951544483773">การทดลองที่ไม่พร้อมใช้งาน</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> รายการ}other{<ph name="ITEM_COUNT" /> รายการ}}</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>
<translation id="2337852623177822836">ผู้ดูแลระบบเป็นผู้ควบคุมการตั้งค่า</translation>
<translation id="2354001756790975382">บุ๊กมาร์กอื่นๆ</translation>
<translation id="2354430244986887761">เมื่อเร็วๆ นี้ Google Safe Browsing <ph name="BEGIN_LINK" />พบแอปอันตราย<ph name="END_LINK" />ใน <ph name="SITE" /></translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">ดำเนินการต่อ</translation>
<translation id="2365563543831475020">รายงานข้อขัดข้องเมื่อ <ph name="CRASH_TIME" /> ยังไม่ได้อัปโหลด</translation>
<translation id="2367567093518048410">ระดับ</translation>
-<translation id="237718015863234333">ไม่มีทางเลือก UI ที่พร้อมใช้งาน</translation>
<translation id="2384307209577226199">ค่าเริ่มต้นขององค์กร</translation>
<translation id="2386255080630008482">ใบรับรองของเซิร์ฟเวอร์ถูกเพิกถอนแล้ว</translation>
<translation id="2392959068659972793">แสดงนโยบายโดยที่ไม่ได้ตั้งค่า</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">หากเลือกไว้ Chromium จะจัดเก็บสำเนาบัตรของคุณบนอุปกรณ์นี้เพื่อการกรอกแบบฟอร์มที่รวดเร็วขึ้น</translation>
<translation id="2498091847651709837">สแกนบัตรใหม่</translation>
<translation id="2501278716633472235">ย้อนกลับ</translation>
+<translation id="2503184589641749290">บัตรเดบิตและบัตรเติมเงินที่ยอมรับ</translation>
<translation id="2515629240566999685">ตรวจสอบสัญญาณในพื้นที่ของคุณ</translation>
-<translation id="2516305470678292029">ทางเลือก UI</translation>
<translation id="2539524384386349900">ตรวจหา</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> ส่งการตอบกลับที่ไม่ถูกต้อง</translation>
<translation id="2556876185419854533">&amp;เลิกทำการแก้ไข</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">วิธีการจัดส่งสินค้า</translation>
<translation id="277499241957683684">ไม่มีอุปกรณ์บันทึก</translation>
<translation id="2784949926578158345">การเชื่อมต่อได้รับการรีเซ็ตแล้ว</translation>
+<translation id="2788784517760473862">บัตรเครดิตที่ยอมรับ</translation>
<translation id="2794233252405721443">เว็บไซต์ที่ถูกบล็อก</translation>
<translation id="2799020568854403057">ไซต์ที่จะเปิดมีแอปอันตราย</translation>
<translation id="2803306138276472711">เมื่อเร็วๆ นี้ Google Safe Browsing <ph name="BEGIN_LINK" />ตรวจพบมัลแวร์<ph name="END_LINK" />ใน <ph name="SITE" /> เว็บไซต์ที่โดยปกติจะปลอดภัยบางครั้งอาจติดมัลแวร์</translation>
<translation id="2824775600643448204">ที่อยู่และแถบค้นหา</translation>
<translation id="2826760142808435982">การเชื่อมต่อถูกเข้ารหัสและรับรองความถูกต้องโดยใช้ <ph name="CIPHER" /> และใช้ <ph name="KX" /> เป็นกลไกการแลกเปลี่ยนคีย์</translation>
<translation id="2835170189407361413">ล้างฟอร์ม</translation>
+<translation id="2851634818064021665">คุณต้องได้รับสิทธิ์เพื่อเข้าชมไซต์นี้</translation>
<translation id="2856444702002559011">ผู้โจมตีอาจพยายามขโมยข้อมูลจาก <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ตัวอย่างเช่น รหัสผ่าน ข้อความ หรือบัตรเครดิต) <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">อย่าโหลดซ้ำ</translation>
-<translation id="2900469785430194048">Google Chrome หน่วยความจำเต็มเมื่อพยายามแสดงหน้าเว็บนี้</translation>
<translation id="2909946352844186028">ตรวจพบการเปลี่ยนแปลงเครือข่าย</translation>
<translation id="2916038427272391327">ปิดโปรแกรมอื่นๆ</translation>
<translation id="2922350208395188000">ไม่สามารถตรวจสอบใบรับรองของเซิร์ฟเวอร์</translation>
<translation id="2928905813689894207">ที่อยู่สำหรับการเรียกเก็บเงิน</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> รายการ}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> รายการ}}</translation>
<translation id="2941952326391522266">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะใบรับรองความปลอดภัยมาจาก <ph name="DOMAIN2" /> โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation>
<translation id="2948083400971632585">คุณสามารถปิดใช้งานพร็อกซีที่กำหนดค่าสำหรับการเชื่อมต่อจากหน้าการตั้งค่าได้</translation>
<translation id="2955913368246107853">ปิดแถบค้นหา</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">หากเปิดใช้ไว้ Chromium จะจัดเก็บสำเนาบัตรของคุณในอุปกรณ์นี้เพื่อการกรอกแบบฟอร์มที่รวดเร็วขึ้น</translation>
<translation id="2985398929374701810">ป้อนที่อยู่ที่ถูกต้อง</translation>
<translation id="2986368408720340940">วิธีการรับสินค้านี้ไม่พร้อมให้บริการ โปรดลองใช้วิธีการอื่น</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">หน้าที่คุณดูในแท็บไม่ระบุตัวตนจะไม่เก็บอยู่ในประวัติการเข้าชมของเบราว์เซอร์ การจัดเก็บคุกกี้ หรือประวัติการค้นหาหลังจากที่คุณปิดแท็บไม่ระบุตัวตนทั้งหมด แต่จะมีการเก็บไฟล์ที่คุณดาวน์โหลดหรือบุ๊กมาร์กที่คุณสร้างขึ้น</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">เกาะ</translation>
-<translation id="317583078218509884">การตั้งค่าการอนุญาตไซต์ใหม่จะมีผลหลังจากโหลดซ้ำหน้าเว็บนี้</translation>
<translation id="3176929007561373547">ตรวจสอบการตั้งค่าพร็อกซีหรือติดต่อผู้ดูแลระบบเครือข่ายของคุณเพื่อ
ตรวจสอบว่าพร็อกซีเซิร์ฟเวอร์ทำงานอยู่ หากคุณคิดว่าไม่ควร
ใช้พร็อกซีเซิร์ฟเวอร์ ให้ดำเนินการดังนี้:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">เปิดหน้าในโหมดไม่ระบุตัวตน</translation>
<translation id="320323717674993345">ยกเลิกการชำระเงิน</translation>
<translation id="3207960819495026254">บุ๊กมาร์กแล้ว</translation>
<translation id="3225919329040284222">เซิร์ฟเวอร์แสดงใบรับรองที่ไม่ตรงกับการคาดการณ์ที่มีอยู่ การคาดการณ์เหล่านี้มีอยู่ในบางเว็บไซต์ที่มีการรักษาความปลอดภัยสูงเพื่อปกป้องคุณ</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;เลิกทำการจัดลำดับใหม่</translation>
<translation id="3282497668470633863">เพิ่มชื่อบนบัตร</translation>
<translation id="3286538390144397061">รีสตาร์ทเดี๋ยวนี้</translation>
+<translation id="3287510313208355388">ดาวน์โหลดเมื่อออนไลน์</translation>
<translation id="3303855915957856445">ไม่พบผลการค้นหา</translation>
<translation id="3305707030755673451">ข้อมูลของคุณได้รับการเข้ารหัสด้วยรหัสผ่านการซิงค์เมื่อวันที่ <ph name="TIME" /> โปรดป้อนรหัสผ่านเพื่อเริ่มซิงค์</translation>
<translation id="3320021301628644560">เพิ่มที่อยู่สำหรับการเรียกเก็บเงิน</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">ช่วงการดึงข้อมูล:</translation>
<translation id="3462200631372590220">ซ่อนข้อมูลขั้นสูง</translation>
<translation id="3467763166455606212">ต้องระบุชื่อผู้ถือบัตร</translation>
-<translation id="3478058380795961209">เดือนที่หมดอายุ</translation>
<translation id="3479539252931486093">หากเหตุการณ์นี้ผิดปกติ <ph name="BEGIN_LINK" />โปรดแจ้งให้เราทราบ<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">ไม่ใช่ตอนนี้</translation>
<translation id="3498215018399854026">เราไม่สามารถติดต่อผู้ปกครองของคุณได้ในขณะนี้ โปรดลองอีกครั้ง</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;โปรดปรับวันที่และเวลาจากส่วน&lt;strong&gt;ทั่วไป&lt;/strong&gt;ในแอป&lt;strong&gt;การตั้งค่า&lt;/strong&gt;&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">รับบัตรเครดิตและบัตรเติมเงิน</translation>
<translation id="3582930987043644930">เพิ่มชื่อ</translation>
<translation id="3583757800736429874">&amp;ทำซ้ำการย้าย</translation>
<translation id="3586931643579894722">ซ่อนรายละเอียด</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">หากคุณเห็นข้อความนี้บ่อยๆ ให้ลองไปที่ <ph name="HELP_LINK" /></translation>
<translation id="3658742229777143148">การแก้ไข</translation>
<translation id="3678029195006412963">ไม่สามารถลงนามคำขอ</translation>
+<translation id="3678529606614285348">เปิดหน้าเว็บในหน้าต่างที่ไม่ระบุตัวตนใหม่ (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">รายงานข้อขัดข้องเมื่อ <ph name="CRASH_TIME" /> อัปโหลดเมื่อ <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">ข้อมูลในใบรับรอง</translation>
<translation id="3690164694835360974">การเข้าสู่ระบบไม่ปลอดภัย</translation>
-<translation id="3693415264595406141">รหัสผ่าน:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">กำลังโหลด ...</translation>
<translation id="3712624925041724820">ใบอนุญาตหมด</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">ผู้โจมตีที่อยู่ใน <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="375403751935624634">การแปลล้มเหลวเนื่องจากข้อผิดพลาดของเซิร์ฟเวอร์</translation>
<translation id="3759461132968374835">คุณไม่ได้รายงานข้อขัดข้องเมื่อเร็วๆ นี้ ข้อขัดข้องที่เกิดขึ้นเมื่อปิดใช้งานการรายงานข้อขัดข้อง จะไม่ปรากฏที่นี่</translation>
+<translation id="3765032636089507299">หน้า Google Safe Browsing อยู่ในระหว่างการปรับปรุง</translation>
<translation id="3778403066972421603">คุณต้องการบันทึกบัตรนี้ลงในบัญชี Google และในอุปกรณ์นี้ไหม</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">หมดอายุ <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">ระบบส่งคำขอเข้าถึงเว็บไซต์นี้ของคุณให้ <ph name="NAME" /> แล้ว</translation>
<translation id="3890664840433101773">เพิ่มอีเมล</translation>
<translation id="3901925938762663762">บัตรหมดอายุ</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">อัปโหลดรหัสรายงานข้อขัดข้อง <ph name="CRASH_ID" /> แล้ว (รหัสข้อขัดข้องในเครื่อง: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะใบรับรองความปลอดภัยไม่ได้ระบุชื่อสำรองของหัวเรื่อง โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้โจมตีที่ขัดขวางการเชื่อมต่อของคุณ</translation>
+<translation id="3949601375789751990">ประวัติการท่องเว็บของคุณจะปรากฏที่นี่</translation>
<translation id="3963721102035795474">โหมดนักอ่าน</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{ไม่มี}=1{จากเว็บไซต์ 1 แห่ง }other{จากเว็บไซต์ # แห่ง }}</translation>
<translation id="397105322502079400">กำลังคำนวณ...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">การตั้งค่าไซต์</translation>
<translation id="4169947484918424451">คุณต้องการให้ Chromium บันทึกบัตรนี้ไหม</translation>
<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="4196861286325780578">&amp;ทำซ้ำการย้าย</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ตรวจสอบไฟร์วอลล์และการกำหนดค่าการป้องกันไวรัส<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">การขัดข้อง</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">ผู้โจมตีในเว็บไซต์นี้อาจพยายามติดตั้งโปรแกรมอันตรายซึ่งจะขโมยหรือลบข้อมูล (ตัวอย่างเช่น รูปภาพ รหัสผ่าน ข้อความ และบัตรเครดิต) ลงในคอมพิวเตอร์ของคุณ</translation>
<translation id="4372948949327679948">ค่า <ph name="VALUE_TYPE" /> ที่คาดไว้</translation>
<translation id="4377125064752653719">คุณพยายามเข้าถึง <ph name="DOMAIN" /> แต่ใบรับรองที่เซิร์ฟเวอร์แจ้งมาถูกเพิกถอนโดยผู้ออกใบรับรอง ซึ่งหมายความว่าข้อมูลรับรองด้านความปลอดภัยที่เซิร์ฟเวอร์แจ้งมานั้นไม่สามารถเชื่อถือได้ คุณอาจกำลังติดต่อกับคนที่คิดจะโจมตีคุณ</translation>
-<translation id="4381091992796011497">ชื่อผู้ใช้:</translation>
<translation id="4394049700291259645">ปิดการใช้งาน</translation>
<translation id="4406896451731180161">ผลการค้นหา</translation>
<translation id="4424024547088906515">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะ Chrome ไม่เชื่อถือใบรับรองความปลอดภัย โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> ไม่ยอมรับใบรับรองการเข้าสู่ระบบหรือไม่ได้ให้ใบรับรองไว้</translation>
<translation id="443673843213245140">การใช้พร็อกซีถูกปิดใช้งาน แต่มีการระบุการกำหนดค่าพร็อกซีอย่างชัดเจน</translation>
+<translation id="445100540951337728">บัตรเดบิตที่ยอมรับ</translation>
<translation id="4506176782989081258">ข้อผิดพลาดในการตรวจสอบ: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">ติดต่อผู้ดูแลระบบ</translation>
<translation id="450710068430902550">การแชร์กับผู้ดูแลระบบ</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">นำที่อยู่ออกจาก Chrome ไหม</translation>
<translation id="4592951414987517459">มีการเข้ารหัสการเชื่อมต่อของคุณกับ <ph name="DOMAIN" /> ด้วยชุดการเข้ารหัสที่ทันสมัย</translation>
<translation id="4594403342090139922">&amp;เลิกทำการนำออก</translation>
+<translation id="4611292653554630842">ลงชื่อเข้าสู่ระบบ</translation>
<translation id="4619615317237390068">แท็บจากอุปกรณ์อื่นๆ</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะใบรับรองความปลอดภัยมีข้อผิดพลาด โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation>
<translation id="4690462567478992370">หยุดใช้ใบรับรองที่ไม่ถูกต้อง</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">การเชื่อมต่อของคุณขัดข้อง</translation>
+<translation id="471880041731876836">คุณไม่มีสิทธิ์เข้าชมไซต์นี้</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />เรียกใช้การวินิจฉัยเครือข่ายของ Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">โหลดนโยบายซ้ำ</translation>
<translation id="4728558894243024398">แพลตฟอร์ม</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">มุมมอง</translation>
<translation id="4854362297993841467">วิธีการนำส่งสินค้านี้ไม่พร้อมให้บริการ โปรดลองใช้วิธีการอื่น</translation>
<translation id="4858792381671956233">คุณถามผู้ปกครองแล้วว่าสามารถเข้าชมเว็บไซต์นี้ได้ไหม</translation>
-<translation id="4863764087567530506">เนื้อหานี้อาจลองล่อลวงให้คุณติดตั้งซอฟต์แวร์หรือเปิดเผยข้อมูลส่วนบุคคล <ph name="BEGIN_LINK" />แสดงเนื้อหา<ph name="END_LINK" /></translation>
<translation id="4880827082731008257">ค้นประวัติการเข้าชม</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> และ <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">ต้องมีการตรวจสอบสิทธิ์</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{และหน้าเว็บอีก 1 หน้า}other{และหน้าเว็บอีก # หน้า}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">หน้านี้แปลจากภาษาที่ไม่รู้จักเป็นภาษา <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">การชำระเงิน</translation>
<translation id="4926049483395192435">ต้องระบุ</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">การทำงาน</translation>
<translation id="4958444002117714549">ขยายรายการ</translation>
<translation id="4974590756084640048">เปิดใช้คำเตือนอีกครั้ง</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">รหัสผ่านไม่ถูกต้อง</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>
<translation id="5087580092889165836">เพิ่มบัตร</translation>
<translation id="5089810972385038852">รัฐ</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">จังหวัด</translation>
<translation id="5115563688576182185">(64 บิต)</translation>
<translation id="5141240743006678641">เข้ารหัสผ่านที่ซิงค์ด้วยข้อมูลรับรอง Google ของคุณ</translation>
-<translation id="514421653919133810">เปิดหน้าในโหมดไม่ระบุตัวตน (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">มีรหัสข้อผิดพลาดในการตอบกลับนโยบาย</translation>
+<translation id="5159010409087891077">เปิดหน้าเว็บในหน้าต่างที่ไม่ระบุตัวตนใหม่ (⇧⌘N)</translation>
<translation id="5171045022955879922">ค้นหาหรือพิมพ์ URL</translation>
<translation id="5172758083709347301">ผู้ใช้คอมพิวเตอร์นี้</translation>
<translation id="5179510805599951267">หากไม่มีในภาษา <ph name="ORIGINAL_LANGUAGE" /> ให้รายงานข้อผิดพลาดนี้</translation>
-<translation id="5181140330217080051">กำลังดาวน์โหลด</translation>
<translation id="5190835502935405962">แถบบุ๊กมาร์ก</translation>
<translation id="5199729219167945352">การทดลอง</translation>
<translation id="5205222826937269299">ต้องระบุชื่อ</translation>
<translation id="5222812217790122047">ต้องระบุอีเมล</translation>
<translation id="5251803541071282808">ระบบคลาวด์</translation>
<translation id="5277279256032773186">หากใช้ Chrome ที่ทำงาน ธุรกิจสามารถจัดการการตั้งค่า Chrome ให้พนักงานของตนได้ เรียนรู้เพิ่มเติม</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />ทำตามขั้นตอนการปิดใช้ซอฟต์แวร์ชั่วคราวต่อไปนี้เพื่อให้เข้าสู่เว็บได้ คุณจะต้องมีสิทธิ์ของผู้ดูแลระบบ<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />คลิก<ph name="BEGIN_BOLD" />เริ่ม<ph name="END_BOLD" /> จากนั้นค้นหาและเลือก <ph name="BEGIN_BOLD" />"ดูบริการภายใน"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />เลือก <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />เลือก<ph name="BEGIN_BOLD" />ปิดใช้อยู่<ph name="END_BOLD" />ในส่วน<ph name="BEGIN_BOLD" />ประเภทการเริ่มต้น<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />คลิก<ph name="BEGIN_BOLD" />หยุด<ph name="END_BOLD" />ในส่วน<ph name="BEGIN_BOLD" />สถานะบริการ<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />คลิก<ph name="BEGIN_BOLD" />ใช้<ph name="END_BOLD" /> แล้วคลิก<ph name="BEGIN_BOLD" />ตกลง<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />ไปที่<ph name="BEGIN_LEARN_MORE_LINK" />ศูนย์ช่วยเหลือของ Chrome<ph name="END_LEARN_MORE_LINK" /> เพื่อเรียนรู้วิธีนำซอฟต์แวร์ออกจากคอมพิวเตอร์อย่างถาวร
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">การเชื่อมต่อกับเว็บไซต์นี้ไม่เป็นส่วนตัว หากต้องการออกจากโหมด VR ในเวลาใดก็ตาม ให้นำอุปกรณ์สวมศีรษะออก แล้วกด "กลับ"</translation>
<translation id="5299298092464848405">ข้อผิดพลาดในการแยกวิเคราะห์นโยบาย</translation>
<translation id="5308689395849655368">การรายงานข้อขัดข้องถูกปิดใช้งาน</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">ข้อผิดพลาดในการตรวจสอบรูปแบบที่ "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">ไม่พบหน้า <ph name="HOST_NAME" /> นี้</translation>
<translation id="5455374756549232013">เวลาบันทึกของนโยบายไม่เหมาะสม</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> ของ <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">ไม่ถูกต้อง</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> รายการ}other{<ph name="CONTACT_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> รายการ}}</translation>
<translation id="5470861586879999274">&amp;ทำซ้ำการแก้ไข</translation>
+<translation id="5481076368049295676">เนื้อหานี้อาจพยายามติดตั้งซอฟต์แวร์อันตรายที่จะขโมยหรือลบข้อมูลในอุปกรณ์ของคุณ <ph name="BEGIN_LINK" />แสดงเนื้อหา<ph name="END_LINK" /></translation>
<translation id="54817484435770891">เพิ่มที่อยู่ที่ถูกต้อง</translation>
<translation id="5492298309214877701">เว็บไซต์นี้บนอินทราเน็ตของบริษัท องค์กร หรือโรงเรียนมี URL เหมือนกับเว็บไซต์ภายนอก
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">ไม่พบบทความที่ขอ</translation>
<translation id="5544037170328430102">หน้าที่ฝังไว้ใน <ph name="SITE" /> บอกว่า:</translation>
<translation id="5556459405103347317">โหลดใหม่</translation>
+<translation id="5560088892362098740">วันที่หมดอายุ</translation>
<translation id="5565735124758917034">ใช้งานอยู่</translation>
<translation id="5571083550517324815">ไม่สามารถรับสินค้าจากที่อยู่นี้ โปรดเลือกที่อยู่อื่น</translation>
<translation id="5572851009514199876">โปรดเปิดและลงชื่อเข้าใช้ Chrome เพื่อให้ Chrome ตรวจสอบได้ว่าคุณได้รับอนุญาตให้เข้าถึงไซต์นี้หรือไม่</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">ไม่สามารถโหลดการตั้งค่านโยบาย</translation>
<translation id="5631439013527180824">โทเค็นการจัดการอุปกรณ์ไม่ถูกต้อง</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="5646376287012673985">ตำแหน่ง</translation>
<translation id="5659593005791499971">อีเมล</translation>
<translation id="5669703222995421982">รับเนื้อหาที่ปรับเปลี่ยนในแบบของคุณ</translation>
<translation id="5675650730144413517">หน้านี้ใช้ไม่ได้</translation>
<translation id="5710435578057952990">ข้อมูลประจำตัวของเว็บไซต์นี้ยังไม่ได้รับการยืนยัน</translation>
-<translation id="5713016350996637505">บล็อกเนื้อหาที่หลอกลวงแล้ว</translation>
+<translation id="5719499550583120431">รับบัตรเติมเงิน</translation>
<translation id="5720705177508910913">ผู้ใช้ปัจจุบัน</translation>
<translation id="5732392974455271431">ผู้ปกครองสามารถเลิกบล็อกเว็บไซต์ให้คุณ</translation>
<translation id="5763042198335101085">ป้อนที่อยู่อีเมลที่ถูกต้อง</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">คุณต้องการกรอกข้อมูล <ph name="CARD_DETAIL" /> ไหม</translation>
<translation id="5810442152076338065">มีการเข้ารหัสการเชื่อมต่อของคุณกับ <ph name="DOMAIN" /> ด้วยชุดการเข้ารหัสที่ล้าสมัยแล้ว</translation>
<translation id="5813119285467412249">&amp;ทำซ้ำการเพิ่ม</translation>
-<translation id="5814352347845180253">คุณอาจสูญเสียสิทธิ์การเข้าถึงเนื้อหาระดับพรีเมียมจาก <ph name="SITE" /> และเว็บไซต์อื่นๆ บางแห่ง</translation>
<translation id="5838278095973806738">คุณไม่ควรป้อนข้อมูลที่ละเอียดอ่อนบนเว็บไซต์นี้ (ตัวอย่างเช่น รหัสผ่านหรือบัตรเครดิต) เนื่องจากผู้โจมตีอาจขโมยข้อมูลดังกล่าวไปได้</translation>
<translation id="5869405914158311789">ไม่สามารถเข้าถึงเว็บไซต์นี้</translation>
<translation id="5869522115854928033">รหัสผ่านที่บันทึกไว้</translation>
<translation id="5872918882028971132">คำแนะนำระดับบนสุด</translation>
+<translation id="5893752035575986141">รับบัตรเครดิต</translation>
<translation id="5901630391730855834">สีเหลือง</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ซิงค์แล้ว)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{ใช้งานอยู่ 1 รายการ}other{ใช้งานอยู่ # รายการ}}</translation>
-<translation id="5926846154125914413">คุณอาจสูญเสียสิทธิ์การเข้าถึงเนื้อหาระดับพรีเมียมจากเว็บไซต์บางแห่ง</translation>
<translation id="5959728338436674663">ส่ง<ph name="BEGIN_WHITEPAPER_LINK" />ข้อมูลบางอย่างของระบบและเนื้อหาของหน้าเว็บ<ph name="END_WHITEPAPER_LINK" />ไปยัง Google เพื่อช่วยตรวจหาแอปและเว็บไซต์ที่เป็นอันตรายโดยอัตโนมัติ<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">ลบจากประวัติการเข้าชม</translation>
<translation id="5975083100439434680">ย่อ</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">ปิด</translation>
<translation id="6042308850641462728">เพิ่มเติม</translation>
<translation id="6047233362582046994">หากคุณเข้าใจความเสี่ยงต่อความปลอดภัย คุณสามารถ<ph name="BEGIN_LINK" />ไปยังไซต์นี้<ph name="END_LINK" />ก่อนที่จะมีการนำแอปอันตรายออก</translation>
+<translation id="6047927260846328439">เนื้อหานี้อาจพยายามหลอกล่อให้คุณติดตั้งซอฟต์แวร์หรือเปิดเผยข้อมูลส่วนบุคคล <ph name="BEGIN_LINK" />แสดงเนื้อหา<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">คุณไม่สามารถไปที่ <ph name="SITE" /> ได้ในขณะนี้เนื่องจากเว็บไซต์ใช้การตรึงใบรับรอง โดยปกติข้อผิดพลาดของเครือข่ายและการโจมตีจะเกิดขึ้นเพียงชั่วคราว หน้านี้จึงอาจใช้งานได้ในภายหลัง</translation>
<translation id="6060685159320643512">ระวัง การทดลองนี้อาจเป็นอันตราย</translation>
<translation id="6080696365213338172">คุณเข้าถึงเนื้อหาโดยใช้ใบรับรองที่ผู้ดูแลระบบออกให้ ข้อมูลที่คุณให้กับ <ph name="DOMAIN" /> อาจถูกสกัดกั้นโดยผู้ดูแลระบบ</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">เรียนรู้เพิ่มเติม</translation>
<translation id="6169916984152623906">ขณะนี้คุณสามารถท่องเว็บแบบเป็นส่วนตัว และผู้อื่นที่ใช้อุปกรณ์เครื่องนี้จะไม่เห็นกิจกรรมของคุณ แต่จะมีการบันทึกการดาวน์โหลดและบุ๊กมาร์กไว้</translation>
<translation id="6177128806592000436">การเชื่อมต่อกับเว็บไซต์นี้ไม่ปลอดภัย</translation>
-<translation id="6184817833369986695">(รุ่น: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">ตรวจสอบการเชื่อมต่ออินเทอร์เน็ต</translation>
<translation id="6218753634732582820">ต้องการนำที่อยู่ออกจาก Chromium ใช่ไหม</translation>
<translation id="6221345481584921695">เมื่อเร็วๆ นี้ Google Safe Browsing <ph name="BEGIN_LINK" />ตรวจพบมัลแวร์<ph name="END_LINK" />ใน <ph name="SITE" /> เว็บไซต์ที่โดยปกติจะปลอดภัยบางครั้งอาจติดมัลแวร์ เนื้อหาที่เป็นอันตรายมาจาก <ph name="SUBRESOURCE_HOST" /> ซึ่งเป็นผู้เผยแพร่มัลแวร์ที่เป็นที่รู้จัก</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">หากสนใจในฟีเจอร์สุดเจ๋งของ Chrome ใหม่ ลองใช้เวอร์ชันเบต้าของเราที่ chrome.com/beta</translation>
-<translation id="6389758589412724634">Chromium หน่วยความจำเต็มเมื่อพยายามแสดงหน้าเว็บนี้</translation>
+<translation id="6397451950548600259">ซอฟต์แวร์ในคอมพิวเตอร์ของคุณทำให้ Chrome ไม่สามารถเชื่อมต่อกับเว็บอย่างปลอดภัย</translation>
<translation id="6404511346730675251">แก้ไขบุ๊กมาร์ก</translation>
<translation id="6410264514553301377">ป้อนวันหมดอายุและ CVC ของ <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">คุณถามผู้ปกครองแล้วว่าสามารถเข้าชมเว็บไซต์นี้ได้ไหม</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">นโยบายอุปกรณ์</translation>
<translation id="6477321094435799029">Chrome ได้ตรวจพบรหัสที่ผิดปกติบนหน้านี้และได้บล็อกรหัสดังกล่าวเพื่อปกป้องข้อมูลส่วนบุคคลของคุณ (เช่น รหัสผ่าน หมายเลขโทรศัพท์ และบัตรเครดิต)</translation>
<translation id="6489534406876378309">เริ่มอัปโหลดข้อขัดข้อง</translation>
+<translation id="6507833130742554667">รับบัตรเครดิตและบัตรเดบิต</translation>
<translation id="6508722015517270189">รีสตาร์ท Chrome</translation>
<translation id="6529602333819889595">&amp;ทำซ้ำการนำออก</translation>
<translation id="6534179046333460208">คำแนะนำ Physical Web</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">เนื้อหานี้อาจพยายามติดตั้งซอฟต์แวร์อันตรายที่จะขโมยหรือลบข้อมูลในอุปกรณ์ของคุณ <ph name="BEGIN_LINK" />แสดงเนื้อหา<ph name="END_LINK" /></translation>
<translation id="6596325263575161958">ตัวเลือกการเข้ารหัส</translation>
<translation id="662080504995468778">อยู่ต่อ</translation>
<translation id="6626291197371920147">เพิ่มหมายเลขบัตรที่ถูกต้อง</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> ค้นหา</translation>
<translation id="6630809736994426279">ผู้โจมตีที่กำลังอยู่ใน <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> อาจพยายามติดตั้งโปรแกรมอันตรายลงในเครื่อง Mac ของคุณ เพื่อขโมยหรือลบข้อมูล (ตัวอย่างเช่น รูปภาพ รหัสผ่าน ข้อความ และบัตรเครดิต) <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">นโยบายนี้ถูกยกเลิกแล้ว</translation>
+<translation id="6657585470893396449">รหัสผ่าน</translation>
<translation id="6671697161687535275">ต้องการนำคำแนะนำสำหรับแบบฟอร์มออกจาก Chromium ใช่ไหม</translation>
<translation id="6685834062052613830">ออกจากระบบและตั้งค่าให้เสร็จสมบูรณ์</translation>
<translation id="6710213216561001401">ก่อนหน้า</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">เขต</translation>
<translation id="6973656660372572881">มีการระบุทั้งพร็อกซีเซิร์ฟเวอร์แบบคงที่และ URL สคริปต์ .pac ไว้</translation>
<translation id="6989763994942163495">แสดงการตั้งค่าขั้นสูง...</translation>
-<translation id="7000990526846637657">ไม่พบข้อมูลประวัติการเข้าชม</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">บัญชี Google ของคุณอาจมีประวัติการท่องเว็บในรูปแบบอื่นๆ ที่ <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">รหัสผ่าน</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">ไม่สามารถจัดส่งสินค้าไปยังที่อยู่นี้ โปรดเลือกที่อยู่อื่น</translation>
<translation id="7138472120740807366">วิธีการนำส่งสินค้า</translation>
<translation id="7139724024395191329">เอมิเรต</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> รายการ}other{<ph name="PAYMENT_METHOD_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> รายการ}}</translation>
<translation id="7155487117670177674">การชำระเงินไม่ปลอดภัย</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> วิธี}other{<ph name="SHIPPING_OPTION_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> วิธี}}</translation>
<translation id="7179921470347911571">เปิดใช้งานใหม่เดี๋ยวนี้</translation>
<translation id="7180611975245234373">รีเฟรช</translation>
<translation id="7182878459783632708">ไม่ได้กำหนดนโยบายไว้</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">ดาวน์โหลด</translation>
<translation id="7518003948725431193">ไม่พบหน้าเว็บสำหรับที่อยู่เว็บ: <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">การเชื่อมต่อกับไซต์นี้ไม่เป็นส่วนตัว</translation>
<translation id="7535087603100972091">ราคา</translation>
<translation id="7537536606612762813">จำเป็น</translation>
<translation id="7542403920425041731">เมื่อคุณยืนยันแล้ว จะมีการแชร์รายละเอียดบัตรของคุณกับไซต์นี้</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">ลองทำตามเคล็ดลับต่อไปนี้:</translation>
<translation id="7554791636758816595">แท็บใหม่</translation>
<translation id="7567204685887185387">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะอาจมีการออกใบรับรองความปลอดภัยปลอม โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> รายการ}other{<ph name="CONTACT_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> รายการ}}</translation>
<translation id="7568593326407688803">หน้าเว็บนี้เป็น<ph name="ORIGINAL_LANGUAGE" />คุณต้องการแปลหรือไม่</translation>
<translation id="7569952961197462199">นำบัตรเครดิตออกจาก Chrome ไหม</translation>
<translation id="7569983096843329377">สีดำ</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">ใบรับรอง</translation>
<translation id="7716147886133743102">ถูกบล็อกโดยผู้ดูแลระบบ</translation>
<translation id="7716424297397655342">ไม่สามารถโหลดเว็บไซต์นี้จากแคช</translation>
+<translation id="774634243536837715">บล็อกเนื้อหาอันตรายแล้ว</translation>
<translation id="7752995774971033316">ไม่ได้จัดการ</translation>
<translation id="7755287808199759310">ผู้ปกครองสามารถเลิกบล็อกเว็บไซต์ให้คุณ</translation>
<translation id="7758069387465995638">ไฟร์วอลล์หรือซอฟต์แวร์ป้องกันไวรัสอาจบล็อกการเชื่อมต่อนี้</translation>
+<translation id="7759163816903619567">โดเมนที่แสดง:</translation>
<translation id="7761701407923456692">ใบรับรองของเซิร์ฟเวอร์ไม่ตรงกับ URL</translation>
<translation id="7763386264682878361">โปรแกรมแยกวิเคราะห์ไฟล์ Manifest ของการชำระเงิน</translation>
<translation id="7764225426217299476">เพิ่มที่อยู่</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">พบ<ph name="SEARCH_RESULTS" /> <ph name="NUMBER_OF_RESULTS" /> รายการสำหรับ "<ph name="SEARCH_STRING" />"</translation>
<translation id="785549533363645510">อย่างไรก็ตาม ระบบยังมองเห็นคุณ การเข้าสู่โหมดไม่ระบุตัวตนไม่ได้เป็นการซ่อนการท่องเว็บจากนายจ้างของคุณ ผู้ให้บริการอินเทอร์เน็ต หรือเว็บไซต์ที่คุณเข้าชม</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">รับบัตรเดบิตและบัตรเติมเงิน</translation>
<translation id="7887683347370398519">ตรวจสอบ CVC และลองอีกครั้ง</translation>
<translation id="79338296614623784">ป้อนหมายเลขโทรศัพท์ที่ถูกต้อง</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">ส่งความคิดเห็น</translation>
<translation id="8041940743680923270">ใช้ค่าเริ่มต้นสากล (ถาม)</translation>
<translation id="8088680233425245692">การดูบทความล้มเหลว</translation>
-<translation id="8089520772729574115">ไม่ถึง 1 MB</translation>
<translation id="8091372947890762290">กำลังรอการเปิดใช้งานบนเซิร์ฟเวอร์</translation>
<translation id="8118489163946903409">วิธีการชำระเงิน</translation>
<translation id="8131740175452115882">ยืนยัน</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;เลิกทำการย้าย</translation>
<translation id="8201077131113104583">การอัปเดต URL ไม่ถูกต้องสำหรับส่วนขยายรหัส "<ph name="EXTENSION_ID" />"</translation>
<translation id="8202097416529803614">ข้อมูลสรุปคำสั่งซื้อ</translation>
+<translation id="8205463626947051446">ไซต์มักแสดงโฆษณาที่แทรก</translation>
<translation id="8218327578424803826">ตำแหน่งที่มอบหมาย:</translation>
<translation id="8225771182978767009">ผู้ที่ตั้งค่าคอมพิวเตอร์เครื่องนี้เลือกบล็อกเว็บไซต์นี้</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">เปิดหน้าเว็บในแท็บไม่ระบุตัวตนใหม่</translation>
<translation id="8241707690549784388">หน้าที่คุณกำลังมองหาใช้ข้อมูลที่คุณได้ป้อนไว้แล้ว การกลับไปสู่หน้านั้นอาจทำให้คุณต้องทำซ้ำการดำเนินการใดๆ ที่คุณทำแล้ว คุณต้องการดำเนินการต่อหรือไม่</translation>
+<translation id="8241712895048303527">บล็อกในไซต์นี้</translation>
<translation id="8249320324621329438">เรียกดูครั้งสุดท้ายเมื่อ:</translation>
<translation id="8253091569723639551">ต้องใส่ที่อยู่สำหรับการเรียกเก็บเงิน</translation>
<translation id="8261506727792406068">ลบ</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">การแปลล้มเหลวเนื่องจากเกิดปัญหาการเชื่อมต่อกับเครือข่าย </translation>
<translation id="8332188693563227489">การเข้าถึง <ph name="HOST_NAME" /> ถูกปฏิเสธ</translation>
<translation id="834457929814110454">หากคุณเข้าใจความเสี่ยงต่อความปลอดภัย คุณสามารถ<ph name="BEGIN_LINK" />ไปยังไซต์นี้<ph name="END_LINK" />ก่อนที่จะมีการนำโปรแกรมอันตรายออก</translation>
-<translation id="8344669043927012510">เปิดหน้าในโหมดไม่ระบุตัวตน (⇧⌘N)</translation>
<translation id="8349305172487531364">แถบบุ๊กมาร์ก</translation>
<translation id="8363502534493474904">ปิดโหมดบนเครื่องบิน</translation>
<translation id="8364627913115013041">ไม่ได้ตั้งค่า</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">อัปโหลดรายงานข้อขัดข้องเมื่อ <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">ข้อขัดข้อง (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">คุณต้องป้อนข้อความรหัสผ่านที่เหมือนกันสองครั้ง</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">การตั้งค่า</translation>
<translation id="8433057134996913067">วิธีนี้จะทำให้คุณออกจากระบบของเว็บไซต์ส่วนใหญ่</translation>
<translation id="8437238597147034694">&amp;เลิกทำการย้าย</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">หากต้องการใช้การ์ดจากบัญชี Google ให้ลงชื่อเข้าใช้ Chrome</translation>
<translation id="8488350697529856933">ใช้กับ</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ใช้เวลาตอบกลับนานเกินไป</translation>
-<translation id="8532105204136943229">ปีที่หมดอายุ</translation>
+<translation id="8503813439785031346">ชื่อผู้ใช้</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="8571890674111243710">กำลังแปลหน้าเว็บนี้เป็นภาษา<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">เพิ่มเบอร์โทร
@@ -887,6 +919,7 @@
<translation id="859285277496340001">ใบรับรองไม่ระบุวิธีการตรวจสอบว่ามีการเพิกถอนไปแล้วหรือไม่</translation>
<translation id="8620436878122366504">ผู้ปกครองยังไม่ได้อนุมัติเว็บไซต์นี้</translation>
<translation id="8647750283161643317">รีเซ็ตทั้งหมดเป็นค่าเริ่มต้น</translation>
+<translation id="8660471606262461360">จาก Google Payments</translation>
<translation id="8703575177326907206">การเชื่อมต่อของคุณไปยัง <ph name="DOMAIN" /> ไม่ได้รับการเข้ารหัส</translation>
<translation id="8718314106902482036">การชำระเงินไม่เสร็จสมบูรณ์</translation>
<translation id="8725066075913043281">ลองอีกครั้ง</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">ขยาย</translation>
<translation id="8931333241327730545">คุณต้องการบันทึกบัตรนี้ในบัญชี Google ไหม</translation>
<translation id="8932102934695377596">นาฬิกาช้าเกินไป</translation>
+<translation id="8938939909778640821">บัตรเครดิตและบัตรเติมเงินที่ยอมรับ</translation>
<translation id="8971063699422889582">ใบรับรองของเซิร์ฟเวอร์หมดอายุแล้ว</translation>
<translation id="8986494364107987395">ส่งสถิติการใช้งานและรายงานข้อขัดข้องไปยัง Google โดยอัตโนมัติ</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">บล็อกบนไซต์นี้เสมอ</translation>
<translation id="9170848237812810038">เ&amp;ลิกทำ</translation>
<translation id="917450738466192189">ใบรับรองของเซิร์ฟเวอร์ไม่ถูกต้อง</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> วิธี}other{<ph name="SHIPPING_OPTION_PREVIEW" /> และอีก <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> วิธี}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ใช้โปรโตคอลที่ไม่รองรับ</translation>
<translation id="9205078245616868884">ข้อมูลของคุณมีการเข้ารหัสด้วยรหัสผ่านการซิงค์ โปรดป้อนรหัสผ่านเพื่อเริ่มซิงค์</translation>
<translation id="9207861905230894330">การเพิ่มบทความล้มเหลว</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ล้างฟอร์ม</translation>
<translation id="939736085109172342">โฟลเดอร์ใหม่</translation>
-<translation id="941721044073577244">ดูเหมือนว่าคุณจะไม่มีสิทธิ์เข้าชมเว็บไซต์นี้</translation>
<translation id="969892804517981540">รุ่นที่เป็นทางการ</translation>
<translation id="975560348586398090">{COUNT,plural, =0{ไม่มี}=1{1 รายการ}other{# รายการ}}</translation>
+<translation id="981121421437150478">ออฟไลน์</translation>
<translation id="988159990683914416">รุ่นนักพัฒนา</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_tr.xtb b/chromium/components/strings/components_strings_tr.xtb
index 91c76aa1a18..17d464c961d 100644
--- a/chromium/components/strings/components_strings_tr.xtb
+++ b/chromium/components/strings/components_strings_tr.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Diğer uygulamaları kapatın</translation>
<translation id="1055184225775184556">Eklemeyi &amp;Geri Al</translation>
<translation id="10614374240317010">Hiç kaydedilmeyecekler</translation>
+<translation id="1066396345355680611"><ph name="SITE" /> ve diğer sitelerden alınan korunan içeriğe erişiminizi kaybedebilirsiniz.</translation>
<translation id="106701514854093668">Masaüstü Yer İşaretleri</translation>
<translation id="1074497978438210769">Güvenli değil</translation>
<translation id="1080116354587839789">Genişliğe sığdır</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Okuma listesi</translation>
<translation id="1264126396475825575">Kilitlenme raporu yakalanma zamanı: <ph name="CRASH_TIME" /> (henüz yüklenmedi veya yoksayıldı)</translation>
<translation id="1281526147609854549">Yayınlayan: <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Tehlikeli içerik engellendi</translation>
<translation id="1285320974508926690">Bu siteyi hiçbir zaman çevirme</translation>
<translation id="129553762522093515">Son kapatılan</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Çerezlerinizi temizlemeyi deneyin<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Kayıt alan adı:</translation>
<translation id="1340482604681802745">Alınacağı adres</translation>
-<translation id="1344211575059133124">Bu siteyi ziyaret etmek için izne ihtiyacınız var</translation>
<translation id="1344588688991793829">Chromium Otomatik Doldurma ayarları...</translation>
<translation id="1348198688976932919">Girmekte olduğunuz site tehlikeli uygulamalar içeriyor</translation>
<translation id="1374468813861204354">öneriler</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869"><ph name="LOCALITY" /> konumundaki <ph name="ORGANIZATION" /> kuruluşunun kimliği <ph name="ISSUER" /> tarafından doğrulandı.</translation>
<translation id="1426410128494586442">Evet</translation>
<translation id="1430915738399379752">Yazdır</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> tane daha}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> tane daha}}</translation>
<translation id="1506687042165942984">Bu sayfanın kaydedilmiş (eski olduğu bilinen) bir kopyasını gösterin.</translation>
<translation id="1517433312004943670">Telefon numarası gerekli</translation>
+<translation id="1517500485252541695">Kabul edilen kredi ve banka kartları</translation>
<translation id="1519264250979466059">Oluşturma Tarihi</translation>
+<translation id="1527263332363067270">Bağlantı bekleniyor…</translation>
<translation id="153384715582417236">Şimdilik hepsi bu</translation>
<translation id="1549470594296187301">Bu özelliğin kullanılabilmesi için JavaScript etkinleştirilmelidir.</translation>
<translation id="1555130319947370107">Mavi</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Sayfayı daha sonra indir</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Zorunlu alan</translation>
<translation id="187918866476621466">Başlangıç sayfalarını aç</translation>
<translation id="1883255238294161206">Listeyi daralt</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> tane daha}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> tane daha}}</translation>
<translation id="1898423065542865115">Filtreleme</translation>
+<translation id="1916770123977586577">Güncellenen ayarlarınızı bu siteye uygulamak için sayfayı yeniden yükleyin</translation>
+<translation id="1919345977826869612">Reklamlar</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Yok}=1{1 site}other{# site}}</translation>
<translation id="194030505837763158"><ph name="LINK" /> adresine git</translation>
+<translation id="1948773908305951926">Kabul edilen ön ödemeli kartlar</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> Yer İşaretleri</translation>
<translation id="1973335181906896915">Serileştirme hatası</translation>
<translation id="1974060860693918893">Gelişmiş</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP hatası</translation>
<translation id="2270484714375784793">Telefon numarası</translation>
<translation id="2282872951544483773">Kullanılamayan Deneyler</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> öğe}other{<ph name="ITEM_COUNT" /> öğe}}</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>
<translation id="2337852623177822836">Ayar yöneticinizin kontrolü altındadır</translation>
<translation id="2354001756790975382">Diğer yer işaretleri</translation>
<translation id="2354430244986887761">Google Güvenli Tarama son zamanlarda <ph name="SITE" /> sitesinde <ph name="BEGIN_LINK" />zararlı uygulamalar buldu<ph name="END_LINK" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Devam Et</translation>
<translation id="2365563543831475020">Yakalanan kilitlenme raporu (<ph name="CRASH_TIME" />) yüklenmedi</translation>
<translation id="2367567093518048410">Düzey</translation>
-<translation id="237718015863234333">Mevcut kullanıcı arayüzü alternatifi yok</translation>
<translation id="2384307209577226199">Kuruluşun varsayılan ayarı</translation>
<translation id="2386255080630008482">Sunucunun sertifikası iptal edildi.</translation>
<translation id="2392959068659972793">Hiçbir değer ayarlanmamış politikaları göster</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">İşaretlenirse Chromium, formları daha hızlı doldurma amacıyla kartınızın bir kopyasını bu cihazda depolar.</translation>
<translation id="2498091847651709837">Yeni kart tara</translation>
<translation id="2501278716633472235">Geri dön</translation>
+<translation id="2503184589641749290">Kabul edilen banka ve ön ödemeli kartlar</translation>
<translation id="2515629240566999685">Bulunduğunuz bölgedeki sinyali kontrol etme</translation>
-<translation id="2516305470678292029">Kullanıcı Arayüzü Alternatifleri</translation>
<translation id="2539524384386349900">Algıla</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> geçersiz bir yanıt gönderdi.</translation>
<translation id="2556876185419854533">Düzenlemeyi &amp;Geri Al</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Gönderim yöntemi</translation>
<translation id="277499241957683684">Eksik cihaz kaydı</translation>
<translation id="2784949926578158345">Bağlantı sıfırlandı.</translation>
+<translation id="2788784517760473862">Kabul edilen kredi kartları</translation>
<translation id="2794233252405721443">Site engellenmiş</translation>
<translation id="2799020568854403057">Girmekte olduğunuz site zararlı uygulamalar içeriyor</translation>
<translation id="2803306138276472711">Google Güvenli Tarama yakın bir zamanda <ph name="SITE" /> sitesinde <ph name="BEGIN_LINK" />kötü amaçlı yazılım<ph name="END_LINK" /> tespit etti. Normalde güvenli olan web sitelerine bazen kötü amaçlı yazılımlar bulaşır.</translation>
<translation id="2824775600643448204">Adres ve arama çubuğu</translation>
<translation id="2826760142808435982">Bağlantı <ph name="CIPHER" /> kullanılarak şifrelenmiş ve kimliği doğrulanmıştır. Anahtar değişim mekanizması olarak <ph name="KX" /> kullanılır.</translation>
<translation id="2835170189407361413">Formu temizle</translation>
+<translation id="2851634818064021665">Bu siteyi ziyaret etmek için izninizin olması gerekir</translation>
<translation id="2856444702002559011">Saldırganlar <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> üzerinden bilgilerinizi çalmaya çalışıyor olabilir (örneğin, şifreler, mesajlar veya kredi kartları). <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Yeniden Yükleme</translation>
-<translation id="2900469785430194048">Bu web sayfası görüntülenmeye çalışılırken Google Chrome'da bellek kalmadı.</translation>
<translation id="2909946352844186028">Bir ağ değişikliği algılandı.</translation>
<translation id="2916038427272391327">Diğer programları kapatın</translation>
<translation id="2922350208395188000">Sunucunun sertifikası kontrol edilemiyor.</translation>
<translation id="2928905813689894207">Fatura Adresi</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> adres daha}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> adres daha}}</translation>
<translation id="2941952326391522266">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikası <ph name="DOMAIN2" /> alan adından geliyor. Bu durum, bir yanlış yapılandırmadan veya bağlantıya müdahale eden bir saldırgandan kaynaklanıyor olabilir.</translation>
<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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Bu seçenek etkinleştirildiğinde Chromium, formları daha hızlı doldurmak için kartınızın bir kopyasını bu cihazda saklar.</translation>
<translation id="2985398929374701810">Geçerli bir adres girin</translation>
<translation id="2986368408720340940">Bu alım yöntemi kullanılamıyor. Farklı bir yöntem deneyin.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Gizli sekmelerde görüntülediğiniz sayfalar, açık olan tüm gizli sekmeler kapatıldıktan sonra tarayıcınızın geçmişinden, çerez deposundan veya arama geçmişinden silinecektir. İndirdiğiniz dosyalar veya oluşturduğunuz yer işaretleri kalacaktır.</translation>
<translation id="3169472444629675720">Bul</translation>
<translation id="3174168572213147020">Ada</translation>
-<translation id="317583078218509884">Yeni site izinleri için ayarlar sayfa yeniden yüklendikten sonra geçerli olacaktır.</translation>
<translation id="3176929007561373547">Proxy sunucunun çalışıyor olduğundan emin olmak için
proxy ayarlarınızı kontrol edin veya ağ yöneticinize danışın. Proxy sunucu
kullanmamanız gerektiğini düşünüyorsanız:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Sayfayı Gizli modda açın</translation>
<translation id="320323717674993345">Ödemeyi iptal et</translation>
<translation id="3207960819495026254">Yer işareti koyuldu</translation>
<translation id="3225919329040284222">Sunucu, yerleşik beklentilerle eşleşmeyen bir sertifika sundu. Bu beklentiler sizi korumak amacıyla bazı yüksek güvenlikli web sitelerinde bulunur.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">Sıralama Değişikliğini &amp;Geri Al</translation>
<translation id="3282497668470633863">Kart üzerindeki ismi ekle</translation>
<translation id="3286538390144397061">Şimdi yeniden başlat</translation>
+<translation id="3287510313208355388">Çevrimiçi olduğunda indir</translation>
<translation id="3303855915957856445">Arama sonucu bulunamadı</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Getirme aralığı:</translation>
<translation id="3462200631372590220">Gelişmiş bilgileri gizle</translation>
<translation id="3467763166455606212">Kart sahibinin adı zorunludur</translation>
-<translation id="3478058380795961209">Son Kullanım Ayı</translation>
<translation id="3479539252931486093">Bu beklenmedik bir durum mu? <ph name="BEGIN_LINK" />Bize bildirin<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Şimdi değil</translation>
<translation id="3498215018399854026">Şu anda ebeveyninize erişemedik. Lütfen tekrar deneyin.</translation>
@@ -343,6 +345,7 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Kredi ve ön ödemeli kartlar kabul edilir.</translation>
<translation id="3582930987043644930">Ad ekleyin</translation>
<translation id="3583757800736429874">Taşımayı &amp;Yeniden Yap</translation>
<translation id="3586931643579894722">Ayrıntıları gizle</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Bunu çok sık görüyorsanız, şunları deneyin: <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Düzeltme</translation>
<translation id="3678029195006412963">İstek imzalanamadı</translation>
+<translation id="3678529606614285348">Sayfayı yeni Gizli pencerede açın (Ctrl-Üst Karakter-N)</translation>
<translation id="3679803492151881375">Kilitlenme raporunun oluşturulma zamanı: <ph name="CRASH_TIME" />, raporun yüklenme zamanı: <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Sertifika bilgileri</translation>
<translation id="3690164694835360974">Giriş yapma işlemi güvenli değil</translation>
-<translation id="3693415264595406141">Şifre:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Yükleniyor...</translation>
<translation id="3712624925041724820">Lisanslar bitti</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesine saldıranlar sizi yanıltarak yazılım yüklemek veya kişisel bilgilerinizi (örneğin, şifreler, telefon numaraları veya kredi kartları) ifşa etmek gibi tehlikeli bir şey yapmanızı sağlayabilirler. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Çeviri, bir sunucu hatası nedeniyle başarısız oldu.</translation>
<translation id="3759461132968374835">Son zamanda herhangi bir kilitlenme bildirmediniz. Kilitlenme bildirme özelliği devre dışıyken oluşan kilitlenmeler burada görünmez.</translation>
+<translation id="3765032636089507299">Güvenli Tarama sayfası yapım aşamasında.</translation>
<translation id="3778403066972421603">Bu kartı Google Hesabınıza ve bu cihaza kaydetmek istiyor musunuz?</translation>
<translation id="3783418713923659662">MasterCard</translation>
<translation id="3787705759683870569">Son kullanma tarihi: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Bu siteye erişim isteğiniz şu kişiye gönderildi: <ph name="NAME" /></translation>
<translation id="3890664840433101773">E-posta adresi ekle</translation>
<translation id="3901925938762663762">Kartın kullanım süresi doldu</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Kilitlenme Raporu Kimliği <ph name="CRASH_ID" /> Yüklendi (Yerel Kilitlenme Kimliği: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikasında Konu Diğer Adları belirtilmiyor. Bu durum, bir yanlış yapılandırmadan veya bağlantınıza müdahale eden bir saldırgandan kaynaklanıyor olabilir.</translation>
+<translation id="3949601375789751990">Göz atma geçmişiniz burada görünür</translation>
<translation id="3963721102035795474">Okuyucu Modu</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Yok}=1{1 siteden }other{# siteden }}</translation>
<translation id="397105322502079400">Hesaplanııyor...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Site ayarları</translation>
<translation id="4169947484918424451">Chromium'un bu kartı kaydetmesini istiyor musunuz?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Bu sitedeki saldırganlar, bilgilerinizi (örneğin fotoğraflar, şifreler, mesajlar ve kredi kartları) çalacak veya silecek tehlikeli programları bilgisayarınıza yüklemeyi deneyebilir.</translation>
<translation id="4372948949327679948">Beklenen <ph name="VALUE_TYPE" /> değeri.</translation>
<translation id="4377125064752653719"><ph name="DOMAIN" /> adresine ulaşmayı denediniz, ancak sunucunun sağladığı sertifika, sertifikayı veren tarafından iptal edildi. Bu, sunucunun sağladığı güvenlik kimlik bilgilerine kesinlikle güvenilmemesi gerektiği anlamına gelir. Bir saldırganla irtibat kuruyor olabilirsiniz.</translation>
-<translation id="4381091992796011497">Kullanıcı Adı:</translation>
<translation id="4394049700291259645">Devre dışı bırak</translation>
<translation id="4406896451731180161">arama sonuçları</translation>
<translation id="4424024547088906515">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Chrome, 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="4432688616882109544"><ph name="HOST_NAME" /> giriş sertifikanızı kabul etmedi veya giriş sertifikası sağlanmamış olabilir.</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="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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Adres Chrome'dan kaldırılsın mı?</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" /> ile olan bağlantınız modern bir şifre seti kullanılarak şifrelendi.</translation>
<translation id="4594403342090139922">Silmeyi &amp;Geri Al</translation>
+<translation id="4611292653554630842">Giriş yap</translation>
<translation id="4619615317237390068">Diğer cihazlardan sekmeler</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikasında hatalar var. Bu durum, bir yanlış yapılandırmadan veya bağlantıya müdahale eden bir saldırgandan kaynaklanıyor olabilir.</translation>
<translation id="4690462567478992370">Geçersiz sertifika kullanımını durdur</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Bağlantınız kesildi</translation>
+<translation id="471880041731876836">Bu siteyi ziyaret etmek için izniniz yok</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Windows Ağ Teşhislerini Çalıştırma<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Politikaları yeniden yükle</translation>
<translation id="4728558894243024398">Platform</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Bu içerik sizi kandırarak yazılım yüklemenizi veya kişisel bilgilerinizi ifşa etmenizi sağlamaya çalışabilir. <ph name="BEGIN_LINK" />Yine de göster<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Geçmişte ara</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Kimlik doğrulaması gerekiyor</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ve 1 web sayfası daha}other{ve # web sayfası daha}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Bu sayfa, bilinmeyen bir dilden <ph name="LANGUAGE_LANGUAGE" /> diline çevrildi</translation>
<translation id="4923459931733593730">Ödeme</translation>
<translation id="4926049483395192435">Belirtilmelidir.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">İşlemler</translation>
<translation id="4958444002117714549">Listeyi genişlet</translation>
<translation id="4974590756084640048">Uyarıları yeniden etkinleştir</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Hatalı parola</translation>
<translation id="5056549851600133418">Size uygun makaleler</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Proxy adresini kontrol etme<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Bazı sitelerden alınan korumalı içeriğe erişiminizi kaybedebilirsiniz.</translation>
<translation id="5087286274860437796">Sunucu sertifikası şu anda geçerli değil.</translation>
<translation id="5087580092889165836">Kart ekle</translation>
<translation id="5089810972385038852">Eyalet</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Bölge</translation>
<translation id="5115563688576182185">(64 bit)</translation>
<translation id="5141240743006678641">Senkronize edilen şifreleri Google kimlik bilgilerinizle şifreleyin</translation>
-<translation id="514421653919133810">Sayfayı Gizli modda açın (Ctrl-Üst Karakter-N)</translation>
<translation id="5145883236150621069">Politika yanıtında hata kodu var</translation>
+<translation id="5159010409087891077">Sayfayı yeni Gizli pencerede açın (⇧⌘N)</translation>
<translation id="5171045022955879922">Arayın veya URL'yi yazın</translation>
<translation id="5172758083709347301">Makine</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> dilinde değil mi? Bu hatayı bildirin</translation>
-<translation id="5181140330217080051">İndiriliyor</translation>
<translation id="5190835502935405962">Yer İşareti Çubuğu</translation>
<translation id="5199729219167945352">Deneyler</translation>
<translation id="5205222826937269299">Ad gerekli</translation>
<translation id="5222812217790122047">E-posta gerekli</translation>
<translation id="5251803541071282808">Bulut</translation>
<translation id="5277279256032773186">Chrome'u işte mi kullanıyorsunuz? İşletmeler, çalışanları için Chrome ayarlarını yönetebilir. Daha fazla bilgi edinin</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Web'e ulaşabilmeniz amacıyla yazılımı geçici olarak devre dışı bırakmak için bu adımları uygulayın. Bu adımları gerçekleştirmek için yönetici ayrıcalıklarınızın olması gerekir.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />Başlat<ph name="END_BOLD" />'ı tıklayın, ardından <ph name="BEGIN_BOLD" />"Yerel hizmetleri görüntüle"<ph name="END_BOLD" />yi arayın ve seçin
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />'yi seçin
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />Başlatma türü<ph name="END_BOLD" />'nün altında <ph name="BEGIN_BOLD" />Devre dışı<ph name="END_BOLD" />'nı seçin
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />Hizmet durumu<ph name="END_BOLD" />'nun altında <ph name="BEGIN_BOLD" />Durdur<ph name="END_BOLD" />'u seçin
+ <ph name="LIST_ITEM" /><ph name="BEGIN_BOLD" />Uygula<ph name="END_BOLD" />'yı, ardından <ph name="BEGIN_BOLD" />Tamam<ph name="END_BOLD" />'ı tıklayın
+ <ph name="LIST_ITEM" />Yazılımı bilgisayarınızdan kalıcı olarak nasıl kaldıracağınızı öğrenmek için <ph name="BEGIN_LEARN_MORE_LINK" />Chrome yardım merkezini<ph name="END_LEARN_MORE_LINK" /> ziyaret edin
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Bu siteyle bağlantınız gizli değil. VR modundan istediğiniz zaman çıkmak için başlığı çıkarıp Geri düğmesine basın.</translation>
<translation id="5299298092464848405">Politika ayrıştırma hatası</translation>
<translation id="5308689395849655368">Kilitlenme bildirme devre dışı.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" üzerinde şema doğrulama hatası: <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Bu <ph name="HOST_NAME" /> sayfası bulunamıyor</translation>
<translation id="5455374756549232013">Politika zaman damgası yanlış</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> / <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Geçersiz</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> kişi daha}other{<ph name="CONTACT_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> kişi daha}}</translation>
<translation id="5470861586879999274">Düzenlemeyi &amp;yeniden yap</translation>
+<translation id="5481076368049295676">Bu içerik, cihazınıza bilgilerinizi çalabilecek veya silebilecek tehlikeli yazılımlar yüklemeye çalışabilir. <ph name="BEGIN_LINK" />Yine de göster<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Geçerli adres ekleyin</translation>
<translation id="5492298309214877701">Şirket, kuruluş veya okul intranet'indeki bu site harici bir web sitesiyle aynı URL'ye sahip.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">İstenen makale bulunamadı.</translation>
<translation id="5544037170328430102"><ph name="SITE" /> web sitesindeki yerleşik bir sayfanın mesajı:</translation>
<translation id="5556459405103347317">Yeniden Yükle</translation>
+<translation id="5560088892362098740">Son Kullanma Tarihi</translation>
<translation id="5565735124758917034">Etkin</translation>
<translation id="5571083550517324815">Bu adresten alım yapılamıyor. Farklı bir adres seçin.</translation>
<translation id="5572851009514199876">Chrome'un bu siteye erişmenize izin verilip verilmediğini kontrol edebilmesi için lütfen Chrome'u başlatıp oturum açın.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Politika ayarları yüklenemedi</translation>
<translation id="5631439013527180824">Geçersiz cihaz yönetimi jetonu</translation>
<translation id="5633066919399395251">Şu anda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesindeki saldırganlar, bilgilerinizi (örneğin, fotoğraflar, şifreler, mesajlar ve kredi kartları) çalabilecek veya silebilecek tehlikeli programları bilgisayarınıza yüklemeye çalışabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="563324245173044180">Yanıltıcı içerik engellendi.</translation>
<translation id="5646376287012673985">Konum</translation>
<translation id="5659593005791499971">E-posta</translation>
<translation id="5669703222995421982">Kişiselleştirilmiş içerikler alma</translation>
<translation id="5675650730144413517">Bu sayfa çalışmıyor</translation>
<translation id="5710435578057952990">Bu web sitesinin kimliği doğrulanmadı.</translation>
-<translation id="5713016350996637505">Yanıltıcı içerik engellendi</translation>
+<translation id="5719499550583120431">Ön ödemeli kartlar kabul edilir.</translation>
<translation id="5720705177508910913">Geçerli kullanıcı</translation>
<translation id="5732392974455271431">Ebeveynleriniz engellemeyi kaldırabilir</translation>
<translation id="5763042198335101085">Geçerli bir e-posta adresi girin</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065"><ph name="CARD_DETAIL" /> kartınıza ait bilgilerin doldurulmasını istiyor musunuz?</translation>
<translation id="5810442152076338065"><ph name="DOMAIN" /> ile olan bağlantınız eski bir şifre seti kullanılarak şifrelendi.</translation>
<translation id="5813119285467412249">Eklemeyi &amp;Yeniden Yap</translation>
-<translation id="5814352347845180253"><ph name="SITE" /> ve diğer sitelerden alınan premium içeriğe erişiminizi kaybedebilirsiniz.</translation>
<translation id="5838278095973806738">Bu sitede hiçbir hassas bilginizi (örneğin şifrelerinizi veya kredi kartı bilgilerinizi) girmemelisiniz. Aksi takdirde bu bilgiler saldırganlar tarafından çalınabilir.</translation>
<translation id="5869405914158311789">Bu siteye ulaşılamıyor</translation>
<translation id="5869522115854928033">Kayıtlı şifreler</translation>
<translation id="5872918882028971132">Ebeveyn Önerileri</translation>
+<translation id="5893752035575986141">Kredi kartları kabul edilir.</translation>
<translation id="5901630391730855834">Sarı</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="5926846154125914413">Bazı sitelerden alınan premium içeriğe erişiminizi kaybedebilirsiniz.</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="5967867314010545767">Geçmişten kaldır.</translation>
<translation id="5975083100439434680">Uzaklaştır</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Kapat</translation>
<translation id="6042308850641462728">Daha fazla</translation>
<translation id="6047233362582046994">Güvenliğinize ilişkin riskleri anladıysanız zararlı programlar kaldırılmadan önce <ph name="BEGIN_LINK" />bu siteyi ziyaret edebilirsiniz<ph name="END_LINK" />.</translation>
+<translation id="6047927260846328439">Bu içerik sizi kandırarak yazılım yüklemenizi veya kişisel bilgilerinizi ifşa etmenizi sağlamaya çalışabilir. <ph name="BEGIN_LINK" />Yine de göster<ph name="END_LINK" /></translation>
<translation id="6051221802930200923"><ph name="SITE" /> sitesi sertifika sabitleme yöntemi kullandığından siteyi ş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="6060685159320643512">Dikkatli olun, bu deneyler canınızı yakabilir</translation>
<translation id="6080696365213338172">Yönetici tarafından sağlanmış bir sertifika kullanan içeriğe eriştiniz. <ph name="DOMAIN" /> alan adına sağladığınız verileri yöneticiniz görebilir ve bunlara müdahale edebilir.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Daha fazla bilgi edinin</translation>
<translation id="6169916984152623906">Artık gizli olarak göz atabilirsiniz ve bu cihazı kullanan diğer kişiler etkinliğinizi görmez. Yine de indirdikleriniz ve yer işaretleri kaydedilir.</translation>
<translation id="6177128806592000436">Bu siteye bağlantınız güvenli değil</translation>
-<translation id="6184817833369986695">(kohort: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">İnternet bağlantınızı kontrol edin</translation>
<translation id="6218753634732582820">Adres Chromium'dan kaldırılsın mı?</translation>
<translation id="6221345481584921695">Google Güvenli Tarama yakın bir zamanda <ph name="SITE" /> sitesinde <ph name="BEGIN_LINK" />kötü amaçlı yazılım tespit etti<ph name="END_LINK" />. Normalde güvenli olan web sitelerine bazen kötü amaçlı yazılımlar bulaşır. Kötü amaçlı içerik, kötü amaçlı yazılım dağıtımcısı olduğu bilinen <ph name="SUBRESOURCE_HOST" /> kaynağından gelmektedir.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Chrome'daki etkileyici, yeni özellikler ilginizi çekiyor mu? chrome.com/beta adresinden beta kanalımızı deneyin.</translation>
-<translation id="6389758589412724634">Bu web sayfası görüntülenmeye çalışılırken Chromium'da bellek kalmadı.</translation>
+<translation id="6397451950548600259">Bilgisayarınızdaki yazılım, Chrome'un web'e güvenli bir şekilde bağlanmasını engelliyor</translation>
<translation id="6404511346730675251">Yer işaretini düzenle</translation>
<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> numaralı kartın son kullanma tarihini ve CVC kodunu girin</translation>
<translation id="6414888972213066896">Ebeveyninize bu siteyi ziyaret etmenizin uygun olup olmadığını sordunuz</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Cihaz politikaları</translation>
<translation id="6477321094435799029">Chrome, bu sayfada olağan dışı kod tespit etti ve kişisel bilgilerinizi (örneğin, şifreler, telefon numaraları ve kredi kartları) korumak için sayfayı engelledi.</translation>
<translation id="6489534406876378309">Kilitlenmeleri yüklemeye başla</translation>
+<translation id="6507833130742554667">Kredi ve banka kartları kabul edilir.</translation>
<translation id="6508722015517270189">Chrome'u yeniden başlatın</translation>
<translation id="6529602333819889595">Silmeyi &amp;Yeniden Yap</translation>
<translation id="6534179046333460208">Fiziksel Web önerileri</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Bu içerik, cihazınıza bilgilerinizi çalabilecek veya silebilecek tehlikeli yazılımlar yüklemeye çalışabilir. <ph name="BEGIN_LINK" />Yine de göster<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Şifreleme seçenekleri</translation>
<translation id="662080504995468778">Kal</translation>
<translation id="6626291197371920147">Geçerli kart numarası ekle</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> Arama</translation>
<translation id="6630809736994426279">Ş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ı Mac'inize yüklemeye çalışabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Bu politika uygun bulunmadı.</translation>
+<translation id="6657585470893396449">Şifre</translation>
<translation id="6671697161687535275">Form önerisi Chromium'dan kaldırılsın mı?</translation>
<translation id="6685834062052613830">Çıkış yapın ve kurulumu tamamlayın</translation>
<translation id="6710213216561001401">Önceki</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Bölge</translation>
<translation id="6973656660372572881">Hem sabit proxy sunucular hem de bir .pac komut dosyası URL'si belirtildi.</translation>
<translation id="6989763994942163495">Gelişmiş ayarları göster...</translation>
-<translation id="7000990526846637657">Geçmiş girişi bulunamadı</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985"><ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> adresinde Google Hesabınıza ilişkin başka biçimlerde tarama geçmişi olabilir.</translation>
<translation id="7029809446516969842">Şifreler</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Bu adrese gönderim yapılamıyor. Farklı bir adres seçin.</translation>
<translation id="7138472120740807366">Teslimat yöntemi</translation>
<translation id="7139724024395191329">Emirlik</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> yöntem daha}other{<ph name="PAYMENT_METHOD_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> yöntem daha}}</translation>
<translation id="7155487117670177674">Ödeme işlemi güvenli değil</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> seçenek daha}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> seçenek daha}}</translation>
<translation id="7179921470347911571">Şimdi Yeniden Başlat</translation>
<translation id="7180611975245234373">Yenile</translation>
<translation id="7182878459783632708">Hiçbir politika ayarlanmamış</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">İndir</translation>
<translation id="7518003948725431193">Şu web adresi için web sayfası bulunamadı:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Bu siteye bağlantınız gizli değil</translation>
<translation id="7535087603100972091">Değer</translation>
<translation id="7537536606612762813">Zorunlu</translation>
<translation id="7542403920425041731">Onayladığınızda kart ayrıntılarınız bu siteyle paylaşılacaktır.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Aşağıdaki ipuçlarını deneyin:</translation>
<translation id="7554791636758816595">Yeni Sekme</translation>
<translation id="7567204685887185387">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikası hileli bir şekilde yayınlanmış olabilir. Bu durum, bir yanlış yapılandırmadan veya bağlantıya müdahale eden bir saldırgandan kaynaklanıyor olabilir.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> tane daha}other{<ph name="CONTACT_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> tane daha}}</translation>
<translation id="7568593326407688803">Bu sayfanın dili<ph name="ORIGINAL_LANGUAGE" />Çevrilmesini istiyor musunuz?</translation>
<translation id="7569952961197462199">Kredi kartı Chrome'dan kaldırılsın mı?</translation>
<translation id="7569983096843329377">Siyah</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Sertifika</translation>
<translation id="7716147886133743102">Yöneticiniz tarafından engellendi</translation>
<translation id="7716424297397655342">Bu site önbellekten yüklenemiyor</translation>
+<translation id="774634243536837715">Tehlikeli içerik engellendi.</translation>
<translation id="7752995774971033316">Yönetimden kaldırıldı</translation>
<translation id="7755287808199759310">Ebeveyniniz engellemeyi sizin için kaldırabilir</translation>
<translation id="7758069387465995638">Bağlantıyı güvenlik duvarı veya virüsten korunma yazılımı engellemiş olabilir.</translation>
+<translation id="7759163816903619567">Görünen alan:</translation>
<translation id="7761701407923456692">Sunucu sertifikası URL ile eşleşmiyor.</translation>
<translation id="7763386264682878361">Ödeme Bildirimi Ayrıştırıcısı</translation>
<translation id="7764225426217299476">Adres ekle</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">"<ph name="SEARCH_STRING" />" için <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> bulundu.</translation>
<translation id="785549533363645510">Ancak görünmez olmazsınız. Gizli moda geçmek göz atma etkinliğinizi işvereninizden, İnternet servis sağlayıcınızdan veya ziyaret ettiğiniz web sitelerinden gizlemez.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Banka kartları ve ön ödemeli kartlar kabul edilir.</translation>
<translation id="7887683347370398519">CVC'nizi kontrol edin ve tekrar deneyin</translation>
<translation id="79338296614623784">Geçerli bir telefon numarası girin</translation>
<translation id="7935318582918952113">DOM Ayrıştırıcı</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Görüş bildirin</translation>
<translation id="8041940743680923270">Genel varsayılanı kullan (Sor)</translation>
<translation id="8088680233425245692">Makale görüntülenemedi.</translation>
-<translation id="8089520772729574115">1 MB'tan az</translation>
<translation id="8091372947890762290">Etkinleştirme sunucuda bekliyor</translation>
<translation id="8118489163946903409">Ödeme yöntemi</translation>
<translation id="8131740175452115882">Onayla</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">Taşımayı &amp;Geri Al</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" kodlu uzantı için geçersiz güncelleme URL'si.</translation>
<translation id="8202097416529803614">Sipariş özeti</translation>
+<translation id="8205463626947051446">Site, araya giren reklamlar gösterme eğiliminde</translation>
<translation id="8218327578424803826">Atanan Konum:</translation>
<translation id="8225771182978767009">Bu bilgisayarı kuran kişi bu siteyi engellemeyi seçmiş.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Sayfayı yeni bir Gizli sekmede aç</translation>
<translation id="8241707690549784388">Aradığınız sayfa, girdiğiniz bilgileri kullandı. O sayfaya dönmeniz, gerçekleştirdiğiniz işlemlerin tekrarlanmasına yol açabilir. Devam etmek istiyor musunuz?</translation>
+<translation id="8241712895048303527">Bu sitede engelle</translation>
<translation id="8249320324621329438">Son getirilen:</translation>
<translation id="8253091569723639551">Fatura adresi gerekli</translation>
<translation id="8261506727792406068">Sil</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Ağ bağlantısıyla ilgili bir sorun nedeniyle çeviri başarısız oldu.</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ana makinesine erişim reddedildi</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="8344669043927012510">Sayfayı Gizli modda açın (⇧⌘N)</translation>
<translation id="8349305172487531364">Yer işaretleri çubuğu</translation>
<translation id="8363502534493474904">Uçak modunu kapatma</translation>
<translation id="8364627913115013041">Henüz ayarlanmadı.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Kilitlenme raporunun yüklenme zamanı: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Kilitlenme Sayısı (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Aynı parolayı iki kez girmelisiniz.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ayarlar</translation>
<translation id="8433057134996913067">Bu işlem, çoğu web sitesinden çıkış yapmanıza neden olacak.</translation>
<translation id="8437238597147034694">Taşımayı &amp;geri al</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Google Hesabınızda kayıtlı kartları kullanmak için Chrome'da oturum açın</translation>
<translation id="8488350697529856933">Uygulandığı yer</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ana makinesinin yanıt vermesi çok uzun sürdü.</translation>
-<translation id="8532105204136943229">Son Kullanım Yılı</translation>
+<translation id="8503813439785031346">Kullanıcı adı</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="8571890674111243710">Sayfa <ph name="LANGUAGE" /> diline çevriliyor...</translation>
<translation id="858637041960032120">Telefon no ekle
@@ -887,6 +919,7 @@
<translation id="859285277496340001">Sertifika, iptal edilip edilmediğinin denetlenebileceği bir mekanizma belirtmiyor.</translation>
<translation id="8620436878122366504">Ebeveynleriniz henüz onaylamadı</translation>
<translation id="8647750283161643317">Tümünü varsayılan değerlere sıfırla</translation>
+<translation id="8660471606262461360">Google Payments'tan</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> bağlantınız şifrelenmedi.</translation>
<translation id="8718314106902482036">Ödeme işlemi tamamlanmadı</translation>
<translation id="8725066075913043281">Yeniden dene</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Yakınlaştır</translation>
<translation id="8931333241327730545">Bu kartı Google Hesabınıza kaydetmek istiyor musunuz?</translation>
<translation id="8932102934695377596">Saatiniz geri</translation>
+<translation id="8938939909778640821">Kabul edilen kredi kartları ve ön ödemeli kartlar</translation>
<translation id="8971063699422889582">Sunucu sertifikasının süresi doldu.</translation>
<translation id="8986494364107987395">Kullanım istatistiklerini ve çökme raporlarını otomatik olarak Google'a gönder</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Bu sitede her zaman engelle</translation>
<translation id="9170848237812810038">&amp;Geri al</translation>
<translation id="917450738466192189">Sunucunun sertifikası geçersiz.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> tane daha}other{<ph name="SHIPPING_OPTION_PREVIEW" /> ve <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> tane daha}}</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>
<translation id="9207861905230894330">Makale eklenemedi.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">FORMU TEMİZLE</translation>
<translation id="939736085109172342">Yeni klasör</translation>
-<translation id="941721044073577244">Bu siteyi ziyaret etme izniniz yok</translation>
<translation id="969892804517981540">Resmi Derleme</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Yok}=1{1 öğe}other{# öğe}}</translation>
+<translation id="981121421437150478">Çevrimdışı</translation>
<translation id="988159990683914416">Geliştirici Derlemesi</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_uk.xtb b/chromium/components/strings/components_strings_uk.xtb
index 90ed0373f46..a030a989596 100644
--- a/chromium/components/strings/components_strings_uk.xtb
+++ b/chromium/components/strings/components_strings_uk.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">Закрийте інші додатки</translation>
<translation id="1055184225775184556">&amp;Відмінити додавання</translation>
<translation id="10614374240317010">Ніколи не зберігалося</translation>
+<translation id="1066396345355680611">Ви можете втратити доступ до захищеного вмісту на сайті <ph name="SITE" /> і деяких інших сайтах.</translation>
<translation id="106701514854093668">Закладки для настільного комп’ютера</translation>
<translation id="1074497978438210769">Не конфіденційний</translation>
<translation id="1080116354587839789">За шириною сторінки</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Список читання</translation>
<translation id="1264126396475825575">Звіт про аварійне завершення роботи о <ph name="CRASH_TIME" /> (ще не завантажено або пропущено)</translation>
<translation id="1281526147609854549">Видано <ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">Заблоковано небезпечний вміст</translation>
<translation id="1285320974508926690">Ніколи не перекладати цей сайт</translation>
<translation id="129553762522093515">Нещодавно закриті</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Спробуйте видалити файли cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Домен реєстрації:</translation>
<translation id="1340482604681802745">Адреса отримання</translation>
-<translation id="1344211575059133124">Схоже, вам потрібен дозвіл, щоб перейти на цей сайт</translation>
<translation id="1344588688991793829">Налаштування автозаповнення Chromium…</translation>
<translation id="1348198688976932919">Сайт містить небезпечні додатки</translation>
<translation id="1374468813861204354">пропозиції</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Ідентифікаційну інформацію <ph name="ORGANIZATION" /> із <ph name="LOCALITY" /> було перевірено <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">так</translation>
<translation id="1430915738399379752">Друк</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}many{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="1506687042165942984">Показати стару збережену копію цієї сторінки.</translation>
<translation id="1517433312004943670">Введіть номер телефону</translation>
+<translation id="1517500485252541695">Прийнятні кредитні та дебетові картки</translation>
<translation id="1519264250979466059">Дата створення версії</translation>
+<translation id="1527263332363067270">Очікується з’єднання…</translation>
<translation id="153384715582417236">Більше нічого немає</translation>
<translation id="1549470594296187301">Щоб користуватися цією функцією, потрібно ввімкнути JavaScript.</translation>
<translation id="1555130319947370107">Синій</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Зв’яжіться із системним адміністратором.</translation>
<translation id="1740951997222943430">Введіть дійсний місяць закінчення терміну дії</translation>
-<translation id="1745358365027406341">Завантажити сторінку пізніше</translation>
<translation id="17513872634828108">Відкриті вкладки</translation>
<translation id="1753706481035618306">Номер сторінки</translation>
<translation id="1763864636252898013">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Операційна система вашого пристрою не вважає його сертифікат безпеки надійним. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Обов’язкове поле</translation>
<translation id="187918866476621466">Відкрити стартові сторінки</translation>
<translation id="1883255238294161206">Згорнути список</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="1898423065542865115">Фільтрування</translation>
+<translation id="1916770123977586577">Щоб застосувати оновлені налаштування на цьому сайті, оновіть сторінку</translation>
+<translation id="1919345977826869612">Оголошення</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Немає}=1{1 сайт}one{# сайт}few{# сайти}many{# сайтів}other{# сайту}}</translation>
<translation id="194030505837763158">Перейдіть за адресою <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Прийнятні передплачені картки</translation>
<translation id="1962204205936693436">Закладки <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Помилка серіалізації</translation>
<translation id="1974060860693918893">Розширені</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Помилка HTTP</translation>
<translation id="2270484714375784793">Номер телефону</translation>
<translation id="2282872951544483773">Недоступні експериментальні функції</translation>
-<translation id="2283595600756901917">{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="2292556288342944218">Ваш доступ до Інтернету заблоковано</translation>
<translation id="230155334948463882">Нова картка?</translation>
+<translation id="2316887270356262533">Звільняє менше 1 Мб. Деякі сайти можуть завантажуватися повільніше під час наступного відвідування.</translation>
<translation id="2317259163369394535">Для сайту <ph name="DOMAIN" /> потрібно ввести ім’я користувача та пароль.</translation>
+<translation id="2317583587496011522">Дебетові картки, які приймаються.</translation>
<translation id="2337852623177822836">Налаштуванням керує ваш адміністратор</translation>
<translation id="2354001756790975382">Інші закладки</translation>
<translation id="2354430244986887761">Функція безпечного перегляду від Google нещодавно <ph name="BEGIN_LINK" />виявила шкідливі додатки<ph name="END_LINK" /> на сайті <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Продовжити</translation>
<translation id="2365563543831475020">Звіт про аварійне завершення роботи о <ph name="CRASH_TIME" /> не завантажено</translation>
<translation id="2367567093518048410">Рівень</translation>
-<translation id="237718015863234333">Немає варіантів інтерфейсу користувача</translation>
<translation id="2384307209577226199">Стандартне корпоративне правило</translation>
<translation id="2386255080630008482">Сертифікат сервера відкликано.</translation>
<translation id="2392959068659972793">Показувати правила, для яких не встановлено значення</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Якщо вибрати цю опцію, Chromium зберігатиме копію даних вашої картки на цьому пристрої, щоб ви могли швидше заповнювати форми.</translation>
<translation id="2498091847651709837">Сканувати нову картку</translation>
<translation id="2501278716633472235">Назад</translation>
+<translation id="2503184589641749290">Прийнятні дебетові та передплачені картки</translation>
<translation id="2515629240566999685">перевірити сигнал у своїй місцевості</translation>
-<translation id="2516305470678292029">Варіанти інтерфейсу користувача</translation>
<translation id="2539524384386349900">Визначити</translation>
<translation id="255002559098805027">Хост <ph name="HOST_NAME" /> надіслав недійсну відповідь.</translation>
<translation id="2556876185419854533">&amp;Відмінити редагування</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Спосіб відправлення</translation>
<translation id="277499241957683684">Відсутній запис пристрою</translation>
<translation id="2784949926578158345">З’єднання було скинуто.</translation>
+<translation id="2788784517760473862">Прийнятні кредитні картки</translation>
<translation id="2794233252405721443">Сайт заблоковано</translation>
<translation id="2799020568854403057">Сайт містить шкідливі додатки</translation>
<translation id="2803306138276472711">Функція безпечного перегляду від Google нещодавно <ph name="BEGIN_LINK" />виявила зловмисне програмне забезпечення<ph name="END_LINK" /> на сайті <ph name="SITE" />. Іноді зловмисне програмне забезпечення заражає зазвичай безпечні веб-сайти.</translation>
<translation id="2824775600643448204">Адресний і пошуковий рядок</translation>
<translation id="2826760142808435982">З’єднання зашифровано й автентифіковано з використанням шифру <ph name="CIPHER" /> і використовує механізм обміну ключами <ph name="KX" />.</translation>
<translation id="2835170189407361413">Очистити форму</translation>
+<translation id="2851634818064021665">Щоб відвідувати цей сайт, потрібен дозвіл</translation>
<translation id="2856444702002559011">Зловмисники можуть намагатися викрасти вашу інформацію із сайту <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (наприклад, паролі, повідомлення чи дані кредитних карток). <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Не оновлювати</translation>
-<translation id="2900469785430194048">Недостатньо пам’яті Google Chrome, щоб показати цю веб-сторінку.</translation>
<translation id="2909946352844186028">Виявлено зміну в мережі.</translation>
<translation id="2916038427272391327">Закрийте інші програми</translation>
<translation id="2922350208395188000">Сертифікат сервера неможливо перевірити.</translation>
<translation id="2928905813689894207">Платіжна адреса</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}one{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}few{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}many{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" />}}</translation>
<translation id="2941952326391522266">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Його сертифікат безпеки походить із домену <ph name="DOMAIN2" />. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
<translation id="2948083400971632585">Усі проксі-сервери, налаштовані для з’єднання, можна вимкнути на сторінці налаштувань.</translation>
<translation id="2955913368246107853">Закрити панель пошуку</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Якщо цю функцію ввімкнено, Chromium зберігає копію даних вашої картки на пристрої, щоб ви могли швидше заповнювати форми.</translation>
<translation id="2985398929374701810">Введіть дійсну адресу</translation>
<translation id="2986368408720340940">Цей спосіб отримання недоступний. Виберіть інший спосіб.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Сторінки, які ви переглядаєте на анонімних вкладках, не реєструються в історії веб-переглядача чи історії пошуку та не залишають файлів cookie, коли ви закриваєте всі анонімні вкладки. Усі завантажені файли чи створені закладки зберігаються.</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">Острів</translation>
-<translation id="317583078218509884">Нові налаштування дозволів сайту почнуть діяти після перезавантаження сторінки.</translation>
<translation id="3176929007561373547">Перевірте налаштування свого проксі-сервера чи зверніться до адміністратора мережі,
щоб переконатися, що проксі-сервер працює. Якщо ви вважаєте, що не потрібно
використовувати проксі-сервер, виконайте вказані нижче дії.
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Відкрийте сторінку в режимі анонімного перегляду</translation>
<translation id="320323717674993345">Скасувати оплату</translation>
<translation id="3207960819495026254">Створено закладку</translation>
<translation id="3225919329040284222">Сервер надав сертифікат, який не відповідає очікуваним вбудованим параметрам. Ці очікувані параметри встановлено для певних веб-сайтів із високим рівнем безпеки, щоб захистити вас.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Відмінити перевпорядкування</translation>
<translation id="3282497668470633863">Додати ім’я на кредитній картці</translation>
<translation id="3286538390144397061">Перезапустити зараз</translation>
+<translation id="3287510313208355388">Завантажити після відновлення інтернет-з’єднання</translation>
<translation id="3303855915957856445">Немає результатів</translation>
<translation id="3305707030755673451">Ваші дані було зашифровано <ph name="TIME" /> за допомогою парольної фрази для синхронізації. Введіть її, щоб почати синхронізацію.</translation>
<translation id="3320021301628644560">Додати платіжну адресу</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Інтервал отримання:</translation>
<translation id="3462200631372590220">Сховати додаткову інформацію</translation>
<translation id="3467763166455606212">Потрібно вказати ім’я власника картки</translation>
-<translation id="3478058380795961209">Місяць закінчення терміну дії</translation>
<translation id="3479539252931486093">Не очікували? <ph name="BEGIN_LINK" />Повідомте нас<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Не зараз</translation>
<translation id="3498215018399854026">Не вдалося зв’язатися з одним із ваших батьків. Повторіть спробу.</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;Налаштуйте дату й час у розділі &lt;strong&gt;Загальні&lt;/strong&gt; додатка &lt;strong&gt;Налаштування&lt;/strong&gt;.&lt;/p&gt; <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Кредитні та передплачені картки, які приймаються.</translation>
<translation id="3582930987043644930">Додати ім’я</translation>
<translation id="3583757800736429874">&amp;Повторити переміщення</translation>
<translation id="3586931643579894722">Сховати докладні дані</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Якщо ви бачите це часто, спробуйте <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Редакція</translation>
<translation id="3678029195006412963">Не вдалося підписати запит</translation>
+<translation id="3678529606614285348">Відкрийте сторінку в анонімному вікні (Ctrl+Shift+N)</translation>
<translation id="3679803492151881375">Звіт про аварійне завершення роботи створено: <ph name="CRASH_TIME" />, завантажено: <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Інформація про сертифікат</translation>
<translation id="3690164694835360974">Вхід не захищено</translation>
-<translation id="3693415264595406141">Пароль:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Завантаження...</translation>
<translation id="3712624925041724820">Ліцензії вичерпано</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Зловмисники на сайті <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="375403751935624634">Не вдалося виконати переклад через помилку сервера.</translation>
<translation id="3759461132968374835">У вас немає останніх повідомлень про аварійне завершення роботи. Тут не відображатимуться випадки аварійного завершення роботи, які сталися, коли повідомлення про аварійне завершення роботи було вимкнено.</translation>
+<translation id="3765032636089507299">Сторінка "Безпечний перегляд" розробляється.</translation>
<translation id="3778403066972421603">Зберегти дані картки у вашому обліковому записі Google і на цьому пристрої?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Діє до <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Запит на доступ до цього сайту надіслано користувачу <ph name="NAME" /></translation>
<translation id="3890664840433101773">Додати електронну адресу</translation>
<translation id="3901925938762663762">Термін дії картки минув</translation>
+<translation id="3909695131102177774"><ph name="LABEL" />: <ph name="ERROR" /></translation>
<translation id="3945915738023014686">Ідентифікатор завантаженого звіту про аварійне завершення роботи: <ph name="CRASH_ID" /> (локальний ідентифікатор аварійного завершення роботи: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. У його сертифікаті безпеки не вказано альтернативні імена. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
+<translation id="3949601375789751990">Тут відображається ваша історія веб-перегляду</translation>
<translation id="3963721102035795474">Режим перегляду</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Немає}=1{З 1 сайту }one{З # сайту }few{З # сайтів }many{З # сайтів }other{З # сайту }}</translation>
<translation id="397105322502079400">Обчислення...</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Налаштування сайту</translation>
<translation id="4169947484918424451">Зберегти цю картку в Chromium?</translation>
<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="4196861286325780578">&amp;Повторити переміщення</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />перевірити конфігурацію брандмауера й антивірусної програми<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Збої в роботі</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Зловмисники на цьому сайті можуть намагатися встановити на ваш комп’ютер небезпечні програми, що викрадають або видаляють інформацію (наприклад, фотографії, паролі, повідомлення й дані кредитних карток).</translation>
<translation id="4372948949327679948">Очікуване значення: <ph name="VALUE_TYPE" />.</translation>
<translation id="4377125064752653719">Ви пробували зв’язатися з доменом <ph name="DOMAIN" />, проте сервер надав сертифікат, відкликаний його видавцем. Це означає, що не варто довіряти обліковим даним системи захисту, наданим сервером. Можливо, ви обмінюєтеся даними зі зловмисником.</translation>
-<translation id="4381091992796011497">Ім’я користувача:</translation>
<translation id="4394049700291259645">Вимкнути</translation>
<translation id="4406896451731180161">результати пошуку</translation>
<translation id="4424024547088906515">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Chrome не вважає його сертифікат безпеки надійним. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
<translation id="4432688616882109544">Хост <ph name="HOST_NAME" /> не прийняв ваш сертифікат входу або ви не надали цей сертифікат.</translation>
<translation id="443673843213245140">Використання проксі-сервера вимкнено, але чітко вказано налаштування проксі-сервера.</translation>
+<translation id="445100540951337728">Прийнятні дебетові картки</translation>
<translation id="4506176782989081258">Помилка перевірки: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">зв’язатися із системним адміністратором</translation>
<translation id="450710068430902550">Надання інформації адміністратору</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Видалити адресу з Chrome?</translation>
<translation id="4592951414987517459">З’єднання з доменом <ph name="DOMAIN" /> шифрується за допомогою сучасного набору шифрів.</translation>
<translation id="4594403342090139922">&amp;Відмінити видалення</translation>
+<translation id="4611292653554630842">Увійти</translation>
<translation id="4619615317237390068">Вкладки з інших пристроїв</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Його сертифікат безпеки містить помилки. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
<translation id="4690462567478992370">Припинити використання недійсного сертифіката</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">З’єднання розірвано</translation>
+<translation id="471880041731876836">У вас немає дозволу відвідувати цей сайт</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />провести діагностику мережі Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Перезавантажити правила</translation>
<translation id="4728558894243024398">Платформа</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">Перегляд</translation>
<translation id="4854362297993841467">Цей спосіб доставки недоступний. Виберіть інший спосіб.</translation>
<translation id="4858792381671956233">Ви надіслали батькам запит на перегляд цього сайту</translation>
-<translation id="4863764087567530506">Цей вміст може оманливим шляхом змусити вас установити програмне забезпечення або надати особисту інформацію. <ph name="BEGIN_LINK" />Усе одно показати<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Пошук в історії</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Потрібна автентифікація</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{і ще 1 веб-сторінка}one{і ще # веб-сторінка}few{і ще # веб-сторінки}many{і ще # веб-сторінок}other{і ще # веб-сторінки}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Цю сторінку перекладено з невідомої мови оригіналу такою мовою: <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Оплата</translation>
<translation id="4926049483395192435">Потрібно вказати.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" /> з <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Дії</translation>
<translation id="4958444002117714549">Розгорнути список</translation>
<translation id="4974590756084640048">Показувати застереження</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Неправильний пароль</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>
<translation id="5087580092889165836">Додати картку</translation>
<translation id="5089810972385038852">Штат</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Провінція чи область</translation>
<translation id="5115563688576182185">(64-розрядна версія)</translation>
<translation id="5141240743006678641">Шифрувати синхронізовані паролі за допомогою облікових даних Google</translation>
-<translation id="514421653919133810">Відкрийте сторінку в режимі анонімного перегляду (Ctrl+Shift+N)</translation>
<translation id="5145883236150621069">Відповідь правила містить код помилки</translation>
+<translation id="5159010409087891077">Відкрийте сторінку в анонімному вікні (⇧⌘N)</translation>
<translation id="5171045022955879922">Знайдіть або введіть URL-адресу</translation>
<translation id="5172758083709347301">Комп’ютер</translation>
<translation id="5179510805599951267">Це не <ph name="ORIGINAL_LANGUAGE" />? Повідомте про помилку</translation>
-<translation id="5181140330217080051">Завантаження</translation>
<translation id="5190835502935405962">Панель закладок</translation>
<translation id="5199729219167945352">Експерименти</translation>
<translation id="5205222826937269299">Укажіть ім’я</translation>
<translation id="5222812217790122047">Укажіть електронну адресу</translation>
<translation id="5251803541071282808">Хмара</translation>
<translation id="5277279256032773186">Користуєтеся Chrome на роботі? Компанії можуть налаштовувати Chrome для своїх працівників. Докладніше</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Виконайте ці дії, щоб тимчасово вимкнути програмне забезпечення й отримати доступ до Інтернету. Для цього потрібні права адміністратора.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Натисніть кнопку <ph name="BEGIN_BOLD" />Запуск<ph name="END_BOLD" />, потім знайдіть і виберіть <ph name="BEGIN_BOLD" />Перегляд локальних служб<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Виберіть тип <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />У розділі <ph name="BEGIN_BOLD" />Тип запуску<ph name="END_BOLD" /> виберіть <ph name="BEGIN_BOLD" />Вимкнено<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />У розділі <ph name="BEGIN_BOLD" />Статус служби<ph name="END_BOLD" /> натисніть <ph name="BEGIN_BOLD" />Зупинити<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Натисніть <ph name="BEGIN_BOLD" />Застосувати<ph name="END_BOLD" />, а потім натисніть кнопку <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Відвідайте <ph name="BEGIN_LEARN_MORE_LINK" />Довідковий центр Chrome<ph name="END_LEARN_MORE_LINK" />, щоб дізнатися, як повністю видалити програмне забезпечення з комп’ютера
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Ваше з’єднання з цим сайтом не конфіденційне. Щоб будь-коли вийти з режиму віртуальної реальності, від’єднайте гарнітуру та натисніть кнопку "Назад".</translation>
<translation id="5299298092464848405">Помилка аналізу правила</translation>
<translation id="5308689395849655368">Повідомлення про аварійне завершення роботи вимкнено.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Помилка перевірки схеми за адресою "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Сторінку хосту <ph name="HOST_NAME" /> не знайдено</translation>
<translation id="5455374756549232013">Недійсна мітка часу правила</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> із <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Недійсні дані</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}many{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="5470861586879999274">&amp;Повторити редагування</translation>
+<translation id="5481076368049295676">Цей вміст може намагатися встановити на ваш пристрій небезпечну програму, яка викрадає або видаляє інформацію. <ph name="BEGIN_LINK" />Усе одно показати<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Додати дійсну адресу</translation>
<translation id="5492298309214877701">URL-адреса цього сайту в інтранеті компанії, організації чи навчального закладу збігається з адресою зовнішнього веб-сайту.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Не вдалося знайти потрібну статтю.</translation>
<translation id="5544037170328430102">Повідомлення з вбудованої сторінки сайту <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Перезавантажити</translation>
+<translation id="5560088892362098740">Термін дії</translation>
<translation id="5565735124758917034">Активний клієнт</translation>
<translation id="5571083550517324815">Адреса отримання не підтримується. Укажіть іншу адресу.</translation>
<translation id="5572851009514199876">Увійдіть в обліковий запис Chrome, щоб веб-переглядач міг перевірити, чи ви маєте дозвіл відвідувати цей сайт.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Помилка завантаження налаштувань правила</translation>
<translation id="5631439013527180824">Недійсний маркер керування пристрою</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="5646376287012673985">Місцезнаходження</translation>
<translation id="5659593005791499971">Електронна пошта</translation>
<translation id="5669703222995421982">Отримувати персоналізовані пропозиції</translation>
<translation id="5675650730144413517">Сторінка не працює</translation>
<translation id="5710435578057952990">Ідентифікаційну інформацію цього веб-сайта не було перевірено.</translation>
-<translation id="5713016350996637505">Заблоковано оманливий вміст</translation>
+<translation id="5719499550583120431">Передплачені картки, які приймаються.</translation>
<translation id="5720705177508910913">Поточний користувач</translation>
<translation id="5732392974455271431">Батьки можуть розблокувати його</translation>
<translation id="5763042198335101085">Введіть дійсну електронну адресу</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Ввести дані кредитної картки <ph name="CARD_DETAIL" />?</translation>
<translation id="5810442152076338065">З’єднання з доменом <ph name="DOMAIN" /> шифрується за допомогою застарілого набору шифрів.</translation>
<translation id="5813119285467412249">&amp;Повторити додавання</translation>
-<translation id="5814352347845180253">Ви можете втратити доступ до платного вмісту на сайті <ph name="SITE" /> і деяких інших сайтах.</translation>
<translation id="5838278095973806738">Не вводьте конфіденційну інформацію на цьому сайті (як-от паролі й дані кредитних карток). Зловмисники можуть викрасти її.</translation>
<translation id="5869405914158311789">Немає зв’язку із сайтом</translation>
<translation id="5869522115854928033">Збережені паролі</translation>
<translation id="5872918882028971132">Поради для батьків</translation>
+<translation id="5893752035575986141">Кредитні картки, які приймаються.</translation>
<translation id="5901630391730855834">Жовтий</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (синхронізовано)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Використовується 1 файл}one{Використовується # файл}few{Використовуються # файли}many{Використовуються # файлів}other{Використовуються # файлу}}</translation>
-<translation id="5926846154125914413">Ви можете втратити доступ до платного вмісту на деяких сайтах.</translation>
<translation id="5959728338436674663">Автоматично надсилати в Google деяку <ph name="BEGIN_WHITEPAPER_LINK" />інформацію про систему та вміст сторінок<ph name="END_WHITEPAPER_LINK" />, щоб допомогти виявляти небезпечні додатки й сайти<ph name="PRIVACY_PAGE_LINK" />.</translation>
<translation id="5967867314010545767">Видалити з історії</translation>
<translation id="5975083100439434680">Зменшити масштаб</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Закрити</translation>
<translation id="6042308850641462728">Більше</translation>
<translation id="6047233362582046994">Якщо ви розумієте ризики, пов’язані з безпекою, можете <ph name="BEGIN_LINK" />перейти на цей сайт<ph name="END_LINK" />, перш ніж небезпечні додатки буде видалено.</translation>
+<translation id="6047927260846328439">Цей вміст може оманливим шляхом змусити вас установити програмну або надати особисту інформацію. <ph name="BEGIN_LINK" />Усе одно показати<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Зараз не можна перейти на сторінку <ph name="SITE" />, оскільки цей веб-сайт використовує закріплення сертифікатів. Помилки мережі й атаки зазвичай тимчасові, тому ця сторінка, скоріш за все, запрацює пізніше.</translation>
<translation id="6060685159320643512">Обережно, ці експерименти ненадійні</translation>
<translation id="6080696365213338172">Ви отримали доступ до вмісту, використовуючи наданий адміністратором сертифікат. Адміністратор може перехоплювати дані, які ви надасте домену <ph name="DOMAIN" />.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Докладніше</translation>
<translation id="6169916984152623906">Тепер ви можете переглядати вміст анонімно. Інші користувачі вашого пристрою не бачитимуть дані про вашу активність. Однак завантаження та закладки зберігатимуться.</translation>
<translation id="6177128806592000436">Ваше з’єднання з цим сайтом не захищене</translation>
-<translation id="6184817833369986695">(когорта: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Перевірте з’єднання з Інтернетом</translation>
<translation id="6218753634732582820">Видалити адресу з Chromium?</translation>
<translation id="6221345481584921695">Функція безпечного перегляду від Google <ph name="BEGIN_LINK" />виявила зловмисне програмне забезпечення<ph name="END_LINK" /> на сайті <ph name="SITE" />. Іноді зловмисне програмне забезпечення заражає зазвичай безпечні веб-сайти. Шкідливий вміст походить із хосту <ph name="SUBRESOURCE_HOST" /> – відомого розповсюджувача зловмисного програмного забезпечення.</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">Хочете спробувати нові цікаві функції Chrome? Завантажте бета-версію зі сторінки chrome.com/beta.</translation>
-<translation id="6389758589412724634">Недостатньо пам’яті Chromium, щоб показати цю веб-сторінку.</translation>
+<translation id="6397451950548600259">Програмне забезпечення на вашому комп’ютері перешкоджає веб-переглядачеві Chrome безпечно під’єднуватися до Інтернету</translation>
<translation id="6404511346730675251">Редагувати закладку</translation>
<translation id="6410264514553301377">Введіть дату закінчення терміну дії та код CVC картки <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Ви надіслали одному з батьків запит на перегляд цього сайту</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Правила пристрою</translation>
<translation id="6477321094435799029">Chrome виявив на цій сторінці незвичний код і заблокував його, щоб захистити вашу особисту інформацію (наприклад, паролі, номери телефонів або кредитних карток).</translation>
<translation id="6489534406876378309">Почати завантаження даних про аварійне завершення роботи</translation>
+<translation id="6507833130742554667">Кредитні та дебетові картки, які приймаються.</translation>
<translation id="6508722015517270189">Перезапустіть Chrome</translation>
<translation id="6529602333819889595">&amp;Повторити видалення</translation>
<translation id="6534179046333460208">Пропозиції сервісу "Інтернет навколо нас"</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Цей вміст може встановити небезпечне програмне забезпечення на ваш пристрій, щоб викрасти або видалити інформацію. <ph name="BEGIN_LINK" />Усе одно показати<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Параметри шифрування</translation>
<translation id="662080504995468778">Залишитися</translation>
<translation id="6626291197371920147">Додати дійсний номер картки</translation>
<translation id="6628463337424475685">Пошук <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">Зловмисники на сайті <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> можуть установити на ваш комп’ютер Mac небезпечні програми, що викрадають або видаляють інформацію (як-от фотографії, паролі, повідомлення та дані кредитних карток). <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">Це правило більше не використовується.</translation>
+<translation id="6657585470893396449">Пароль</translation>
<translation id="6671697161687535275">Видалити пропозицію автозаповнення форм із Chromium?</translation>
<translation id="6685834062052613830">Вийдіть з облікового запису та завершіть процедуру налаштування</translation>
<translation id="6710213216561001401">Попереднє</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Район або округ</translation>
<translation id="6973656660372572881">Указано фіксовані проксі-сервери та URL-адреса сценарію .pac.</translation>
<translation id="6989763994942163495">Показати розширені налаштування...</translation>
-<translation id="7000990526846637657">Немає записів в історії</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Історія веб-перегляду також може зберігатися у вашому обліковому записі Google на сторінці <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Паролі</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Неможливо відправити замовлення на цю адресу. Укажіть іншу адресу.</translation>
<translation id="7138472120740807366">Спосіб доставки</translation>
<translation id="7139724024395191329">Емірат</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}one{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}few{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}many{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}other{<ph name="PAYMENT_METHOD_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" />}}</translation>
<translation id="7155487117670177674">Платіж не захищено</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}many{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="7179921470347911571">Перезапустити зараз</translation>
<translation id="7180611975245234373">Оновити</translation>
<translation id="7182878459783632708">Правила не встановлено</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Завантажити</translation>
<translation id="7518003948725431193">Не знайдено веб-сторінок для веб-адреси <ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Ваше з’єднання з цим сайтом не конфіденційне</translation>
<translation id="7535087603100972091">Яскравість</translation>
<translation id="7537536606612762813">Обов’язкове</translation>
<translation id="7542403920425041731">Щойно ви підтвердите, цей сайт отримає доступ до даних вашої картки.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Виконайте вказівки нижче.</translation>
<translation id="7554791636758816595">Нова вкладка</translation>
<translation id="7567204685887185387">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Можливо, його сертифікат безпеки видали шахраї. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}one{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}few{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}many{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}other{<ph name="CONTACT_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" />}}</translation>
<translation id="7568593326407688803">Мова цієї сторінки:<ph name="ORIGINAL_LANGUAGE" />Перекласти її?</translation>
<translation id="7569952961197462199">Видалити дані кредитної картки з Chrome?</translation>
<translation id="7569983096843329377">Чорний</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Сертифікат</translation>
<translation id="7716147886133743102">Заблоковано адміністратором</translation>
<translation id="7716424297397655342">Не вдається завантажити цей сайт із кешу</translation>
+<translation id="774634243536837715">Заблоковано небезпечний вміст.</translation>
<translation id="7752995774971033316">Некерований клієнт</translation>
<translation id="7755287808199759310">Хтось із батьків може розблокувати його</translation>
<translation id="7758069387465995638">Можливо, брандмауер або антивірусна програма заблокували з’єднання.</translation>
+<translation id="7759163816903619567">Видимий домен:</translation>
<translation id="7761701407923456692">Сертифікат сервера не відповідає URL-адресі.</translation>
<translation id="7763386264682878361">Синтаксичний аналізатор маніфесту платежу</translation>
<translation id="7764225426217299476">Додати адресу</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Знайдено результатів за запитом "<ph name="SEARCH_STRING" />": <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /></translation>
<translation id="785549533363645510">Навіть у режимі анонімного перегляду ваш роботодавець, постачальник послуг Інтернету чи веб-сайти, які ви відвідуєте, можуть бачити, що ви переглядаєте.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" />: <ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
+<translation id="7878176543348854470">Дебетові та передплачені картки, які приймаються.</translation>
<translation id="7887683347370398519">Перевірте код CVC й повторіть спробу</translation>
<translation id="79338296614623784">Введіть дійсний номер телефону</translation>
<translation id="7935318582918952113">Дистилятор DOM</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Надіслати відгук</translation>
<translation id="8041940743680923270">Використовувати глобальне налаштування за умовчанням (Запитувати)</translation>
<translation id="8088680233425245692">Не вдалося переглянути статтю.</translation>
-<translation id="8089520772729574115">менше 1 МБ</translation>
<translation id="8091372947890762290">Активація очікує на сервері</translation>
<translation id="8118489163946903409">Спосіб оплати</translation>
<translation id="8131740175452115882">Підтвердити</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Відмінити переміщення</translation>
<translation id="8201077131113104583">Недійсна URL-адреса для оновлення розширення з ідентифікатором "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Підсумок замовлення</translation>
+<translation id="8205463626947051446">Сайт часто показує нав’язливі оголошення</translation>
<translation id="8218327578424803826">Указане місцезнаходження:</translation>
<translation id="8225771182978767009">Користувач, який налаштував комп’ютер, заблокував цей сайт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Відкрити сторінку в новій анонімній вкладці</translation>
<translation id="8241707690549784388">Сторінка, яку ви шукаєте, використовувала інформацію, введену вами. Повернення до такої сторінки може призвести до повторення ваших попередніх дій. Ви дійсно бажаєте продовжити?</translation>
+<translation id="8241712895048303527">Блокувати на цьому сайті</translation>
<translation id="8249320324621329438">Востаннє отримано:</translation>
<translation id="8253091569723639551">Потрібно вказати платіжну адресу</translation>
<translation id="8261506727792406068">Видалити</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Переклад не виконано через проблему підключення до мережі.</translation>
<translation id="8332188693563227489">Відмовлено в доступі до хосту <ph name="HOST_NAME" /></translation>
<translation id="834457929814110454">Якщо ви розумієте ризики, пов’язані з безпекою, можете <ph name="BEGIN_LINK" />перейти на цей сайт<ph name="END_LINK" /> до того, як небезпечні програми буде видалено.</translation>
-<translation id="8344669043927012510">Відкрийте сторінку в режимі анонімного перегляду (⇧⌘N)</translation>
<translation id="8349305172487531364">Панель закладок</translation>
<translation id="8363502534493474904">вимкнути режим польоту</translation>
<translation id="8364627913115013041">Не встановлено.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Дата завантаження звіту про аварійне завершення роботи: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Аварійне завершення роботи (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Потрібно двічі ввести однакову парольну фразу.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Налаштування</translation>
<translation id="8433057134996913067">Ви вийдете з облікового запису на більшості веб-сайтів.</translation>
<translation id="8437238597147034694">&amp;Відмінити переміщення</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Щоб користуватися картками у своєму обліковому записі Google, увійдіть у Chrome</translation>
<translation id="8488350697529856933">Застосовується до:</translation>
<translation id="8498891568109133222">Хост <ph name="HOST_NAME" /> довго не відповідає.</translation>
-<translation id="8532105204136943229">Рік закінчення терміну дії</translation>
+<translation id="8503813439785031346">Ім’я користувача</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="8571890674111243710">Виконується переклад сторінки такою мовою: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Додати тел.номер
@@ -887,6 +919,7 @@
<translation id="859285277496340001">Сертифікат не вказує на механізм перевірки його відкликання.</translation>
<translation id="8620436878122366504">Батьки ще не схвалили його</translation>
<translation id="8647750283161643317">Скинути все до налаштувань за умовчанням</translation>
+<translation id="8660471606262461360">З Google Payments</translation>
<translation id="8703575177326907206">Ваше з’єднання з <ph name="DOMAIN" /> не зашифровано.</translation>
<translation id="8718314106902482036">Оплату не завершено</translation>
<translation id="8725066075913043281">Повторити спробу</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Збільшити масштаб</translation>
<translation id="8931333241327730545">Зберегти цю картку у вашому обліковому записі Google?</translation>
<translation id="8932102934695377596">Ваш годинник запізнюється</translation>
+<translation id="8938939909778640821">Прийнятні кредитні та передплачені картки</translation>
<translation id="8971063699422889582">Термін дії сертифіката сервера завершився.</translation>
<translation id="8986494364107987395">Автоматично надсилати статистику використання та звіти про аварійне завершення роботи в Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<translation id="9169664750068251925">Завжди блокувати на цьому сайті</translation>
<translation id="9170848237812810038">&amp;Скасувати</translation>
<translation id="917450738466192189">Сертифікат сервера недійсний.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}one{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}few{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}many{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}other{<ph name="SHIPPING_OPTION_PREVIEW" /> і ще <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" />}}</translation>
<translation id="9183425211371246419">Протокол, який використовує хост <ph name="HOST_NAME" />, не підтримується.</translation>
<translation id="9205078245616868884">Ваші дані зашифровано за допомогою парольної фрази. Введіть її, щоб почати синхронізацію.</translation>
<translation id="9207861905230894330">Не вдалося додати статтю.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">ОЧИСТИТИ ФОРМУ</translation>
<translation id="939736085109172342">Нова папка</translation>
-<translation id="941721044073577244">Схоже, у вас немає дозволу, щоб перейти на цей сайт</translation>
<translation id="969892804517981540">Розробка</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Немає}=1{1 запис}one{# запис}few{# записи}many{# записів}other{# запису}}</translation>
+<translation id="981121421437150478">Офлайн</translation>
<translation id="988159990683914416">Конструкція розробника</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_vi.xtb b/chromium/components/strings/components_strings_vi.xtb
index 3c8efcf9643..aafefc8b9a1 100644
--- a/chromium/components/strings/components_strings_vi.xtb
+++ b/chromium/components/strings/components_strings_vi.xtb
@@ -8,7 +8,8 @@
<translation id="1038842779957582377">tên không biết</translation>
<translation id="1050038467049342496">Đóng các ứng dụng khác</translation>
<translation id="1055184225775184556">&amp;Hoàn tác thêm</translation>
-<translation id="10614374240317010">Không bao giờ được lưu</translation>
+<translation id="10614374240317010">Không bao giờ lưu</translation>
+<translation id="1066396345355680611">Bạn có thể mất quyền truy cập vào nội dung được bảo vệ của <ph name="SITE" /> và một số trang web khác.</translation>
<translation id="106701514854093668">Dấu trang máy tính</translation>
<translation id="1074497978438210769">Không bảo mật</translation>
<translation id="1080116354587839789">Vừa với chiều rộng</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">Danh sách đọc</translation>
<translation id="1264126396475825575">Báo cáo sự cố được ghi lại vào <ph name="CRASH_TIME" /> (nhưng chưa tải lên hoặc đã bị bỏ qua)</translation>
<translation id="1281526147609854549">Do <ph name="ISSUER" /> phát hành</translation>
-<translation id="1283919782143846010">Đã chặn nội dung nguy hiểm</translation>
<translation id="1285320974508926690">Không bao giờ dịch trang web này</translation>
<translation id="129553762522093515">Các tab đã đóng gần đây</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />Thử xóa các cookie của bạn<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">Tên miền đăng ký:</translation>
<translation id="1340482604681802745">Địa chỉ nhận hàng</translation>
-<translation id="1344211575059133124">Có vẻ như bạn cần có quyền truy cập trang web này</translation>
<translation id="1344588688991793829">Cài đặt tự động điền trong Chromium...</translation>
<translation id="1348198688976932919">Trang web bạn sắp truy cập chứa ứng dụng nguy hiểm</translation>
<translation id="1374468813861204354">đề xuất</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">Nhận dạng <ph name="ORGANIZATION" /> tại <ph name="LOCALITY" /> đã được xác minh bởi <ph name="ISSUER" />.</translation>
<translation id="1426410128494586442">Có</translation>
<translation id="1430915738399379752">In</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> phương thức thanh toán khác}other{<ph name="PAYMENT_METHOD_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> phương thức thanh toán khác}}</translation>
<translation id="1506687042165942984">Hiển thị bản sao đã lưu (nghĩa là đã lỗi thời) của trang này.</translation>
<translation id="1517433312004943670">Phải có số điện thoại</translation>
+<translation id="1517500485252541695">Thẻ tín dụng và thẻ ghi nợ được chấp nhận</translation>
<translation id="1519264250979466059">Ngày tạo</translation>
+<translation id="1527263332363067270">Đang chờ kết nối…</translation>
<translation id="153384715582417236">Hiện đã hoàn tất</translation>
<translation id="1549470594296187301">Bạn phải bật JavaScript để sử dụng tính năng này.</translation>
<translation id="1555130319947370107">Xanh lam</translation>
@@ -101,7 +101,6 @@
<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="1745358365027406341">Tải trang xuống sau</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>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">Trường bắt buộc</translation>
<translation id="187918866476621466">Mở trang khởi động</translation>
<translation id="1883255238294161206">Thu gọn danh sách</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> địa chỉ giao hàng khác}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> địa chỉ giao hàng khác}}</translation>
<translation id="1898423065542865115">Lọc</translation>
+<translation id="1916770123977586577">Để áp dụng các cài đặt đã cập nhật cho trang web này, hãy tải lại trang</translation>
+<translation id="1919345977826869612">Quảng cáo</translation>
<translation id="192020519938775529">{COUNT,plural, =0{Không có}=1{1 trang web}other{# trang web}}</translation>
<translation id="194030505837763158">Truy cập <ph name="LINK" /></translation>
+<translation id="1948773908305951926">Thẻ trả trước được chấp nhận</translation>
<translation id="1962204205936693436">Dấu trang của <ph name="DOMAIN" /></translation>
<translation id="1973335181906896915">Lỗi nối tiếp hóa</translation>
<translation id="1974060860693918893">Nâng cao</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">Lỗi HTTP</translation>
<translation id="2270484714375784793">Số điện thoại</translation>
<translation id="2282872951544483773">Thử nghiệm không khả dụng</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> mục}other{<ph name="ITEM_COUNT" /> mục}}</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ớ cache 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>
<translation id="2337852623177822836">Cài đặt do quản trị viên kiểm soát</translation>
<translation id="2354001756790975382">Dấu trang khác</translation>
<translation id="2354430244986887761">Gần đây, Duyệt web an toàn của Google <ph name="BEGIN_LINK" />đã tìm thấy các ứng dụng có hại<ph name="END_LINK" /> trên <ph name="SITE" />.</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">Tiếp tục</translation>
<translation id="2365563543831475020">Báo cáo sự cố được ghi lại vào <ph name="CRASH_TIME" /> chưa được tải lên</translation>
<translation id="2367567093518048410">Mức độ</translation>
-<translation id="237718015863234333">Không có giao diện người dùng thay thế nào</translation>
<translation id="2384307209577226199">Đặt mặc định trong môi trường doanh nghiệp</translation>
<translation id="2386255080630008482">Chứng chỉ của máy chủ đã bị thu hồi.</translation>
<translation id="2392959068659972793">Hiển thị chính sách không có giá trị được đặt</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">Nếu được chọn, Chromium sẽ lưu trữ bản sao thẻ của bạn trên thiết bị này để điền biểu mẫu nhanh hơn.</translation>
<translation id="2498091847651709837">Quét thẻ mới</translation>
<translation id="2501278716633472235">Quay lại</translation>
+<translation id="2503184589641749290">Thẻ ghi nợ và thẻ trả trước được chấp nhận</translation>
<translation id="2515629240566999685">Kiểm tra tín hiệu trong khu vực của bạn</translation>
-<translation id="2516305470678292029">Giao diện người dùng thay thế</translation>
<translation id="2539524384386349900">Phát hiện</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> đã gửi phản hồi không hợp lệ.</translation>
<translation id="2556876185419854533">&amp;Hoàn tác chỉnh sửa</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">Phương thức giao hàng</translation>
<translation id="277499241957683684">Thiếu hồ sơ thiết bị</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>
<translation id="2799020568854403057">Trang web bạn sắp truy cập chứa ứng dụng có hại</translation>
<translation id="2803306138276472711">Duyệt web an toàn của Google gần đây <ph name="BEGIN_LINK" />đã phát hiện phần mềm độc hại<ph name="END_LINK" /> trên <ph name="SITE" />. Các trang web thường được coi là an toàn đôi khi vẫn bị nhiễm phần mềm độc hại.</translation>
<translation id="2824775600643448204">Thanh địa chỉ và tìm kiếm</translation>
<translation id="2826760142808435982">Kết nối được mã hóa và xác thực bằng <ph name="CIPHER" /> đồng thời sử dụng <ph name="KX" /> làm cơ chế trao đổi chính.</translation>
<translation id="2835170189407361413">Xóa biểu mẫu</translation>
+<translation id="2851634818064021665">Bạn cần quyền để truy cập trang web này</translation>
<translation id="2856444702002559011">Những kẻ tấn công có thể đang cố gắng đánh cắp thông tin của bạn từ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ví dụ: mật khẩu, thư hoặc thẻ tín dụng). <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">Không tải lại</translation>
-<translation id="2900469785430194048">Google Chrome đã hết bộ nhớ khi cố gắng hiển thị trang web này.</translation>
<translation id="2909946352844186028">Đã phát hiện thấy thay đổi mạng.</translation>
<translation id="2916038427272391327">Đóng các chương trình khác</translation>
<translation id="2922350208395188000">Không thể kiểm tra chứng chỉ của máy chủ.</translation>
<translation id="2928905813689894207">Ðịa chỉ thanh toán</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> địa chỉ giao hàng khác}other{<ph name="SHIPPING_ADDRESS_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> địa chỉ giao hàng khác}}</translation>
<translation id="2941952326391522266">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 là từ <ph name="DOMAIN2" />. Đ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="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>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">Nếu được bật, Chromium sẽ lưu trữ bản sao thẻ của bạn trên thiết bị này để điền vào biểu mẫu nhanh hơn.</translation>
<translation id="2985398929374701810">Nhập địa chỉ hợp lệ</translation>
<translation id="2986368408720340940">Phương thức nhận hàng này không có sẵn. Hãy thử một phương thức khác.</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">Các trang bạn xem trong tab ẩn danh sẽ không bị lưu lại trong lịch sử của trình duyệt, kho cookie hoặc lịch sử tìm kiếm sau khi bạn đóng tất cả các tab ẩn danh của mình. Mọi tệp bạn tải xuống hoặc dấu trang mà bạn tạo sẽ được giữ nguyên.</translation>
<translation id="3169472444629675720">Khám phá</translation>
<translation id="3174168572213147020">Đảo</translation>
-<translation id="317583078218509884">Cài đặt giấy phép trang web mới sẽ có hiệu lực sau khi tải lại trang.</translation>
<translation id="3176929007561373547">Kiểm tra cài đặt proxy của bạn hoặc liên hệ với quản trị viên mạng để
đảm bảo rằng máy chủ proxy đang hoạt động. Nếu bạn cho rằng mình không cần
sử dụng máy chủ proxy:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">Mở trang ở chế độ ẩn danh</translation>
<translation id="320323717674993345">Hủy thanh toán</translation>
<translation id="3207960819495026254">Đã được đánh dấu trang</translation>
<translation id="3225919329040284222">Máy chủ đưa ra chứng chỉ không khớp với kỳ vọng được tích hợp sẵn. Các kỳ vọng này có trong một số trang web nhất định, có tính bảo mật cao với mục đích bảo vệ bạn.</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">&amp;Hoàn tác sắp xếp lại</translation>
<translation id="3282497668470633863">Thêm tên trên thẻ</translation>
<translation id="3286538390144397061">Khởi động lại Ngay bây giờ</translation>
+<translation id="3287510313208355388">Tải xuống khi trực tuyến</translation>
<translation id="3303855915957856445">Không tìm thấy kết quả tìm kiếm nào</translation>
<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>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">Khoảng thời gian tìm nạp:</translation>
<translation id="3462200631372590220">Ẩn chi tiết</translation>
<translation id="3467763166455606212">Yêu cầu tên chủ thẻ</translation>
-<translation id="3478058380795961209">Tháng hết hạn</translation>
<translation id="3479539252931486093">Trang web này có như bạn mong đợi không? Hãy <ph name="BEGIN_LINK" />cho chúng tôi biết<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">Không phải bây giờ</translation>
<translation id="3498215018399854026">Chúng tôi không thể liên lạc với cha mẹ của bạn vào thời điểm này. Vui lòng thử lại.</translation>
@@ -343,12 +345,13 @@
&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; <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="CITY" />, <ph name="STATE" /> <ph name="COUNTRY" /></translation>
+<translation id="358285529439630156">Thẻ tín dụng và thẻ trả trước được chấp nhận.</translation>
<translation id="3582930987043644930">Thêm tên</translation>
<translation id="3583757800736429874">&amp;Làm lại di chuyển</translation>
<translation id="3586931643579894722">Ẩn chi tiết</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
<translation id="3615877443314183785">Nhập ngày hết hạn hợp lệ</translation>
-<translation id="36224234498066874">Xóa Dữ liệu Duyệt web...</translation>
+<translation id="36224234498066874">Xóa DL duyệt web</translation>
<translation id="362276910939193118">Hiển thị Toàn bộ Lịch sử</translation>
<translation id="3623476034248543066">Hiển thị giá trị</translation>
<translation id="3630155396527302611">Nếu chương trình đã được liệt kê là chương trình được phép truy cập mạng, hãy thử
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">Nếu bạn thường xuyên thấy thông báo này, hãy thử các <ph name="HELP_LINK" /> sau.</translation>
<translation id="3658742229777143148">Bản sửa đổi</translation>
<translation id="3678029195006412963">Không thể ký yêu cầu</translation>
+<translation id="3678529606614285348">Mở trang trong cửa sổ Ẩn danh mới (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">Báo cáo sự cố được ghi lại vào <ph name="CRASH_TIME" />, được tải lên vào <ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">Thông tin chứng chỉ</translation>
<translation id="3690164694835360974">Đăng nhập không an toàn</translation>
-<translation id="3693415264595406141">Mật khẩu:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">Đang tải...</translation>
<translation id="3712624925041724820">Giấy phép không đủ</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">Những kẻ tấn công trên <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> có thể đánh lừa bạn làm việc gì đó nguy hiểm như cài đặt phần mềm hoặc tiết lộ thông tin cá nhân (ví dụ: mật khẩu, số điện thoại hoặc thẻ tín dụng). <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Không thể dịch do lỗi máy chủ.</translation>
<translation id="3759461132968374835">Bạn không nhận được báo cáo sự cố nào gần đây. Sự cố xảy ra khi báo cáo sự cố đã bị tắt sẽ không xuất hiện ở đây.</translation>
+<translation id="3765032636089507299">Trang Duyệt web an toàn đang được xây dựng.</translation>
<translation id="3778403066972421603">Bạn có muốn lưu thẻ này vào Tài khoản Google của bạn và trên thiết bị này không?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">Ngày hết hạn <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">Yêu cầu truy cập trang web này của bạn đã được gửi tới <ph name="NAME" /></translation>
<translation id="3890664840433101773">Thêm email</translation>
<translation id="3901925938762663762">Thẻ đã hết hạn</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">ID báo cáo sự cố đã tải lên <ph name="CRASH_ID" /> (ID sự cố cục bộ: <ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">Máy chủ này không thể chứng minh được đó là <ph name="DOMAIN" />; chứng chỉ bảo mật của máy chủ không chỉ định Tên thay thế đối tượng. Điều này có thể do 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="3949601375789751990">Lịch sử duyệt web của bạn xuất hiện ở đây</translation>
<translation id="3963721102035795474">Chế độ đọc</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{Không có}=1{Từ 1 trang web }other{Từ # trang web }}</translation>
<translation id="397105322502079400">Đang tính...</translation>
@@ -400,7 +406,7 @@
<translation id="4072486802667267160">Đã xảy ra lỗi khi xử lý đơn đặt hàng của bạn. Vui lòng thử lại.</translation>
<translation id="4075732493274867456">Ứng dụng và máy chủ không hỗ trợ bộ mã hóa hoặc phiên bản giao thức SSL thông thường.</translation>
<translation id="4079302484614802869">Cấu hình proxy được đặt để sử dụng URL tập lệnh .pac chứ không phải máy chủ proxy cố định.</translation>
-<translation id="4098354747657067197">Trang web lừa đảo phía trước</translation>
+<translation id="4098354747657067197">Bạn sắp truy cập trang web lừa đảo</translation>
<translation id="4103249731201008433">Số sê-ri thiết bị không hợp lệ</translation>
<translation id="410351446219883937">Tự động phát</translation>
<translation id="4103763322291513355">Truy cập &lt;strong&gt;chrome://policy&lt;/strong&gt; để xem danh sách các URL bị chặn quyền truy cập và các chính sách khác bị quản trị viên hệ thống buộc phải thực thi.</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">Cài đặt trang web</translation>
<translation id="4169947484918424451">Bạn có muốn Chromium lưu thẻ này không?</translation>
<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="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>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">Những kẻ tấn công trên trang web này có thể tìm cách 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).</translation>
<translation id="4372948949327679948">Giá trị <ph name="VALUE_TYPE" /> mong đợi.</translation>
<translation id="4377125064752653719">Bạn đã cố truy cập vào <ph name="DOMAIN" /> nhưng chứng chỉ mà máy chủ xuất trình đã bị nhà phát hành thu hồi. Điều này có nghĩa là giấy ủy nhiệm bảo mật mà máy chủ xuất trình hoàn toàn không đáng tin cậy. Bạn có thể đang giao tiếp với kẻ tấn công.</translation>
-<translation id="4381091992796011497">Tên Người dùng:</translation>
<translation id="4394049700291259645">Vô hiệu hóa</translation>
<translation id="4406896451731180161">kết quả tìm kiếm</translation>
<translation id="4424024547088906515">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 Chrome 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="4432688616882109544"><ph name="HOST_NAME" /> không chấp nhận chứng chỉ đăng nhập của bạn hoặc có thể bạn chưa cung cấp chứng chỉ đăng nhập.</translation>
<translation id="443673843213245140">Đã tắt sử dụng proxy nhưng cấu hình proxy rõ ràng được chỉ định.</translation>
+<translation id="445100540951337728">Thẻ ghi nợ được chấp nhận</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>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">Xóa địa chỉ khỏi Chrome?</translation>
<translation id="4592951414987517459">Kết nối của bạn tới <ph name="DOMAIN" /> được mã hóa bằng bộ số 0 hiện đại.</translation>
<translation id="4594403342090139922">&amp;Hoàn tác xóa</translation>
+<translation id="4611292653554630842">Đăng nhập</translation>
<translation id="4619615317237390068">Tab từ các thiết bị khác</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">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 có lỗi. Đ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="4690462567478992370">Dừng sử dụng chứng chỉ không hợp lệ</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">Kết nối của bạn bị gián đoạn</translation>
+<translation id="471880041731876836">Bạn không có quyền để truy cập trang web này</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />Chạy Chẩn đoán mạng của Windows<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">Tải lại chính sách</translation>
<translation id="4728558894243024398">Nền tảng</translation>
@@ -482,14 +492,15 @@
<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="4863764087567530506">Nội dung này có thể tìm cách đánh lừa bạn cài đặt phần mềm hoặc tiết lộ thông tin cá nhân. <ph name="BEGIN_LINK" />Vẫn hiển thị<ph name="END_LINK" />.</translation>
<translation id="4880827082731008257">Lịch sử tìm kiếm</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">Yêu cầu xác thực</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{và thêm 1 trang web}other{và thêm # trang web}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">Trang này đã được dịch từ một ngôn ngữ không xác định sang <ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">Thanh toán</translation>
<translation id="4926049483395192435">Phải được chỉ định.</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">Tác vụ</translation>
<translation id="4958444002117714549">Mở rộng danh sách</translation>
<translation id="4974590756084640048">Bật lại cảnh báo</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">Mật khẩu sai</translation>
<translation id="5056549851600133418">Bài viết dành cho bạn</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Kiểm tra địa chỉ proxy<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">Bạn có thể mất quyền truy cập vào nội dung được bảo vệ của một số trang web.</translation>
<translation id="5087286274860437796">Chứng chỉ của máy chủ không hợp lệ tại thời điểm này.</translation>
<translation id="5087580092889165836">Thêm thẻ</translation>
<translation id="5089810972385038852">Tiểu bang</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">Tỉnh/thành phố</translation>
<translation id="5115563688576182185">(64 bit)</translation>
<translation id="5141240743006678641">Mã hóa mật khẩu đã đồng bộ hóa với thông tin đăng nhập Google của bạn</translation>
-<translation id="514421653919133810">Mở trang ở chế độ ẩn danh (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">Mã lỗi có trong phản hồi chính sách</translation>
+<translation id="5159010409087891077">Mở trang trong cửa sổ Ẩn danh mới (⇧⌘N)</translation>
<translation id="5171045022955879922">Tìm kiếm hoặc nhập URL</translation>
<translation id="5172758083709347301">Máy</translation>
<translation id="5179510805599951267">Không ở <ph name="ORIGINAL_LANGUAGE" />? Báo cáo lỗi này</translation>
-<translation id="5181140330217080051">Đang tải xuống</translation>
<translation id="5190835502935405962">Thanh Dấu trang</translation>
<translation id="5199729219167945352">Thử nghiệm</translation>
<translation id="5205222826937269299">Cần có tên</translation>
<translation id="5222812217790122047">Cần có email</translation>
<translation id="5251803541071282808">Đám mây</translation>
<translation id="5277279256032773186">Sử dụng Chrome ở cơ quan? Các doanh nghiệp có thể quản lý cài đặt Chrome cho nhân viên của họ. Tìm hiểu thêm</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />Hãy làm theo các bước sau để tạm thời vô hiệu hóa phần mềm nhằm giúp bạn có thể truy cập vào web. Bạn cần phải có đặc quyền của quản trị viên.<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />Nhấp vào <ph name="BEGIN_BOLD" />Bắt đầu<ph name="END_BOLD" />, sau đó tìm kiếm và chọn <ph name="BEGIN_BOLD" />"Xem dịch vụ cục bộ"<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Chọn <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Trong phần <ph name="BEGIN_BOLD" />Loại khởi động<ph name="END_BOLD" />, chọn <ph name="BEGIN_BOLD" />Đã tắt<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Trong phần <ph name="BEGIN_BOLD" />Trạng thái dịch vụ<ph name="END_BOLD" />, nhấp vào <ph name="BEGIN_BOLD" />Dừng<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Nhấp vào <ph name="BEGIN_BOLD" />Áp dụng<ph name="END_BOLD" />, sau đó nhấp vào <ph name="BEGIN_BOLD" />OK<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />Truy cập vào <ph name="BEGIN_LEARN_MORE_LINK" />Trung tâm trợ giúp Chrome<ph name="END_LEARN_MORE_LINK" /> để tìm hiểu cách xóa vĩnh viễn phần mềm khỏi máy tính của bạn
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">Kết nối của bạn tới trang web này không ở chế độ riêng tư. Để thoát chế độ VR bất cứ lúc nào, hãy tháo tai nghe và nhấn quay lại.</translation>
<translation id="5299298092464848405">Lỗi phân tích cú pháp chính sách</translation>
<translation id="5308689395849655368">Báo cáo sự cố bị tắt.</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">Lỗi xác thực lược đồ tại "<ph name="ERROR_PATH" />": <ph name="ERROR" /></translation>
<translation id="5452270690849572955">Không thể tìm thấy trang <ph name="HOST_NAME" /> này</translation>
<translation id="5455374756549232013">Dấu thời gian chính sách không hợp lệ</translation>
-<translation id="5455790498993699893"><ph name="ACTIVE_MATCH" /> của <ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="5457113250005438886">Không hợp lệ</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> liên hệ khác}other{<ph name="CONTACT_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> liên hệ khác}}</translation>
<translation id="5470861586879999274">&amp;Làm lại chỉnh sửa</translation>
+<translation id="5481076368049295676">Nội dung này có thể tìm cách cài đặt phần mềm nguy hiểm trên thiết bị của bạn để lấy cắp hoặc xóa thông tin. <ph name="BEGIN_LINK" />Vẫn hiển thị<ph name="END_LINK" /></translation>
<translation id="54817484435770891">Thêm địa chỉ hợp lệ</translation>
<translation id="5492298309214877701">Trang web trên mạng nội bộ của công ty, tổ chức hoặc trường học này có URL tương tự như trang web bên ngoài.
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">Không thể tìm thấy bài viết đã yêu cầu.</translation>
<translation id="5544037170328430102">Trang được nhúng tại <ph name="SITE" /> cho biết:</translation>
<translation id="5556459405103347317">Tải lại</translation>
+<translation id="5560088892362098740">Ngày hết hạn</translation>
<translation id="5565735124758917034">Đang hoạt động</translation>
<translation id="5571083550517324815">Không thể nhận hàng từ địa chỉ này. Chọn một địa chỉ khác.</translation>
<translation id="5572851009514199876">Vui lòng khởi động và đăng nhập vào Chrome để Chrome có thể kiểm tra xem bạn có được phép truy cập trang web này không.</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">Không thể tải cài đặt chính sách</translation>
<translation id="5631439013527180824">Mã thông báo quản lý thiết bị không hợp lệ</translation>
<translation id="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="5646376287012673985">Vị trí</translation>
<translation id="5659593005791499971">Email</translation>
<translation id="5669703222995421982">Nhận nội dung được cá nhân hóa</translation>
<translation id="5675650730144413517">Trang này hiện không hoạt động</translation>
<translation id="5710435578057952990">Nhận dạng trang web này chưa được xác minh.</translation>
-<translation id="5713016350996637505">Đã chặn nội dung lừa đảo</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>
<translation id="5732392974455271431">Cha mẹ của bạn có thể bỏ chặn trang web cho bạn</translation>
<translation id="5763042198335101085">Nhập địa chỉ email hợp lệ</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">Bạn có muốn điền <ph name="CARD_DETAIL" /> của mình không?</translation>
<translation id="5810442152076338065">Kết nối của bạn tới <ph name="DOMAIN" /> được mã hóa bằng bộ số 0 đã lỗi thời.</translation>
<translation id="5813119285467412249">&amp;Làm lại thêm</translation>
-<translation id="5814352347845180253">Bạn có thể mất quyền truy cập vào nội dung cao cấp từ <ph name="SITE" /> và một số trang web khác.</translation>
<translation id="5838278095973806738">Bạn không nên nhập bất kỳ thông tin nhạy cảm nào trên trang web này (ví dụ: mật khẩu hoặc thẻ tín dụng), vì những kẻ tấn công có thể đánh cắp thông tin đó.</translation>
<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="5872918882028971132">Đề xuất chính</translation>
+<translation id="5893752035575986141">Thẻ tín dụng được chấp nhận.</translation>
<translation id="5901630391730855834">Vàng</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="5926846154125914413">Bạn có thể mất quyền truy cập vào nội dung cao cấp từ một số trang web.</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="5967867314010545767">Xóa khỏi lịch sử</translation>
<translation id="5975083100439434680">Thu nhỏ</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">Đóng</translation>
<translation id="6042308850641462728">Thêm</translation>
<translation id="6047233362582046994">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 ứng dụng có hại bị xóa.</translation>
+<translation id="6047927260846328439">Nội dung này có thể tìm cách đánh lừa bạn cài đặt phần mềm hoặc tiết lộ thông tin cá nhân. <ph name="BEGIN_LINK" />Vẫn hiển thị<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">Bạn không thể truy cập vào <ph name="SITE" /> ngay bây giờ do trang web sử dụng tính năng ghim chứng chỉ. 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="6060685159320643512">Hãy cẩn thận, thử nghiệm này có thể gây lỗi</translation>
<translation id="6080696365213338172">Bạn đã truy cập nội dung bằng chứng chỉ do quản trị viên cấp. Dữ liệu mà bạn cung cấp cho <ph name="DOMAIN" /> có thể bị quản trị viên của bạn chặn.</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">Tìm hiểu thêm</translation>
<translation id="6169916984152623906">Giờ đây, bạn có thể duyệt web riêng tư và người khác sử dụng thiết bị này sẽ không thấy hoạt động của bạn. Tuy nhiên, tài nguyên đã tải xuống và dấu trang sẽ được lưu.</translation>
<translation id="6177128806592000436">Kết nối của bạn tới trang web này không an toàn</translation>
-<translation id="6184817833369986695">(nhóm thuần tập: <ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">Kiểm tra kết nối Internet của bạn</translation>
<translation id="6218753634732582820">Bạn muốn xóa địa chỉ khỏi Chromium?</translation>
<translation id="6221345481584921695">Duyệt web an toàn của Google gần đây <ph name="BEGIN_LINK" />đã phát hiện phần mềm độc hại<ph name="END_LINK" /> trên <ph name="SITE" />. Các trang web thường được coi là an toàn đôi khi vẫn bị nhiễm phần mềm độc hại. Nội dung độc hại xuất phát từ <ph name="SUBRESOURCE_HOST" />, một nguồn phát tán phần mềm độc hại đã xác định.</translation>
@@ -641,13 +664,14 @@
<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ớ cache 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>
<translation id="6387478394221739770">Bạn quan tâm đến các tính năng mới thú vị của Chrome? Hãy dùng thử kênh thử nghiệm beta của chúng tôi tại chrome.com/beta.</translation>
-<translation id="6389758589412724634">Chromium đã hết bộ nhớ khi cố gắng hiển thị trang web này.</translation>
+<translation id="6397451950548600259">Phần mềm trên máy tính của bạn đang ngăn Chrome kết nối an toàn với web</translation>
<translation id="6404511346730675251">Chỉnh sửa dấu trang</translation>
<translation id="6410264514553301377">Nhập ngày hết hạn và CVC cho <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Bạn đã hỏi cha mẹ mình xem có thể truy cập vào trang này hay không</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">Chính sách thiết bị</translation>
<translation id="6477321094435799029">Chrome đã phát hiện mã bất thường trên trang này và đã chặn mã này để bảo vệ thông tin cá nhân của bạn (ví dụ như mật khẩu, số điện thoại và thẻ tín dụng).</translation>
<translation id="6489534406876378309">Bắt đầu tải lên sự cố</translation>
+<translation id="6507833130742554667">Thẻ tín dụng và thẻ ghi nợ được chấp nhận.</translation>
<translation id="6508722015517270189">Khởi động lại Chrome</translation>
<translation id="6529602333819889595">&amp;Làm lại xóa</translation>
<translation id="6534179046333460208">Đề xuất Web trong cuộc sống</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">Nội dung này có thể tìm cách cài đặt phần mềm nguy hiểm trên thiết bị của bạn để lấy cắp hoặc xóa thông tin. <ph name="BEGIN_LINK" />Vẫn hiển thị<ph name="END_LINK" />.</translation>
<translation id="6596325263575161958">Tùy chọn mã hóa</translation>
<translation id="662080504995468778">Ở lại</translation>
<translation id="6626291197371920147">Thêm số thẻ hợp lệ</translation>
<translation id="6628463337424475685">Tìm kiếm trên <ph name="ENGINE" /></translation>
<translation id="6630809736994426279">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 Mac 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="6644283850729428850">Chính sách này không được chấp thuận.</translation>
+<translation id="6657585470893396449">Mật khẩu</translation>
<translation id="6671697161687535275">Bạn muốn xóa đề xuất biểu mẫu khỏi Chromium?</translation>
<translation id="6685834062052613830">Đăng xuất và hoàn thành quá trình thiết lập</translation>
<translation id="6710213216561001401">Trước đó</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">Quận</translation>
<translation id="6973656660372572881">Cả hai máy chủ proxy cố định và URL tập lệnh .pac đều được chỉ định.</translation>
<translation id="6989763994942163495">Hiển thị cài đặt nâng cao...</translation>
-<translation id="7000990526846637657">Không tìm thấy mục nhập lịch sử nào</translation>
<translation id="7012363358306927923">China UnionPay</translation>
<translation id="7012372675181957985">Tài khoản Google của bạn có thể có các biểu mẫu lịch sử duyệt web khác tại <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /></translation>
<translation id="7029809446516969842">Mật khẩu</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">Không thể giao hàng đến địa chỉ này. Chọn một địa chỉ khác.</translation>
<translation id="7138472120740807366">Phương thức phân phối</translation>
<translation id="7139724024395191329">Tiểu vương quốc</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> phương thức thanh toán khác}other{<ph name="PAYMENT_METHOD_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> phương thức thanh toán khác}}</translation>
<translation id="7155487117670177674">Thanh toán không an toàn</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> tùy chọn giao hàng khác}other{<ph name="SHIPPING_OPTION_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> tùy chọn giao hàng khác}}</translation>
<translation id="7179921470347911571">Chạy lại ngay bây giờ</translation>
<translation id="7180611975245234373">Làm mới</translation>
<translation id="7182878459783632708">Không có chính sách nào được đặt</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">Tải xuống</translation>
<translation id="7518003948725431193">Không tìm thấy trang web nào ứng với địa chỉ web:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">Kết nối của bạn tới trang web này không ở chế độ riêng tư</translation>
<translation id="7535087603100972091">Giá trị</translation>
<translation id="7537536606612762813">Bắt buộc</translation>
<translation id="7542403920425041731">Sau khi bạn xác nhận, chi tiết thẻ của bạn sẽ được chia sẻ với trang web này.</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">Thử các mẹo sau:</translation>
<translation id="7554791636758816595">Tab mới</translation>
<translation id="7567204685887185387">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 có thể đã bị gian lận khi phát hành. Đ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="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> liên hệ khác}other{<ph name="CONTACT_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> liên hệ khác}}</translation>
<translation id="7568593326407688803">Trang này bằng<ph name="ORIGINAL_LANGUAGE" />Bạn có muốn dịch trang này không?</translation>
<translation id="7569952961197462199">Xóa thẻ tín dụng khỏi Chrome?</translation>
<translation id="7569983096843329377">Đen</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">Chứng chỉ</translation>
<translation id="7716147886133743102">Bị quản trị viên của bạn chặn</translation>
<translation id="7716424297397655342">Không thể tải trang web này từ bộ nhớ cache</translation>
+<translation id="774634243536837715">Đã chặn nội dung nguy hiểm.</translation>
<translation id="7752995774971033316">Không được quản lý</translation>
<translation id="7755287808199759310">Cha mẹ của bạn có thể bỏ chặn trang web cho bạn</translation>
<translation id="7758069387465995638">Tường lửa hoặc phần mềm diệt vi-rút có thể đã chặn kết nối.</translation>
+<translation id="7759163816903619567">Miền hiển thị:</translation>
<translation id="7761701407923456692">Chứng chỉ của máy chủ không phù hợp với URL.</translation>
<translation id="7763386264682878361">Trình phân tích cú pháp tệp kê khai thanh toán</translation>
<translation id="7764225426217299476">Thêm địa chỉ</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">Đã tìm thấy <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> cho '<ph name="SEARCH_STRING" />'</translation>
<translation id="785549533363645510">Tuy nhiên, bạn không ẩn. Việc chuyển sang chế độ ẩn danh sẽ không ẩn thao tác duyệt của bạn với chủ lao động, nhà cung cấp dịch vụ internet hoặc các trang web bạn truy cập.</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">Thẻ ghi nợ và thẻ trả trước được chấp nhận.</translation>
<translation id="7887683347370398519">Kiểm tra CVC của bạn và thử lại</translation>
<translation id="79338296614623784">Nhập số điện thoại hợp lệ</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">Gửi phản hồi</translation>
<translation id="8041940743680923270">Sử dụng cài đặt mặc định chung (Hỏi)</translation>
<translation id="8088680233425245692">Không xem được bài viết.</translation>
-<translation id="8089520772729574115">dưới 1 MB</translation>
<translation id="8091372947890762290">Kích hoạt đang chờ xử lý trên máy chủ</translation>
<translation id="8118489163946903409">Phương thức thanh toán</translation>
<translation id="8131740175452115882">Xác nhận</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">&amp;Hoàn tác di chuyển</translation>
<translation id="8201077131113104583">URL cập nhật không hợp lệ cho tiện ích có ID "<ph name="EXTENSION_ID" />".</translation>
<translation id="8202097416529803614">Tóm tắt đơn đặt hàng</translation>
+<translation id="8205463626947051446">Trang web thường hiển thị quảng cáo xâm nhập</translation>
<translation id="8218327578424803826">Vị trí được gán:</translation>
<translation id="8225771182978767009">Người thiết lập máy tính này đã chọn chặn trang web này.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">Mở trang trong tab Ẩn danh mới</translation>
<translation id="8241707690549784388">Trang mà bạn đang tìm sử dụng thông tin bạn đã nhập vào. Việc quay lại trang đó có thể lặp lại bất kỳ tác vụ nào bạn đã thực hiện. Bạn có muốn tiếp tục không?</translation>
+<translation id="8241712895048303527">Chặn trên trang web này</translation>
<translation id="8249320324621329438">Tìm nạp lần cuối:</translation>
<translation id="8253091569723639551">Yêu cầu địa chỉ thanh toán</translation>
<translation id="8261506727792406068">Xóa</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">Không thể dịch do kết nối mạng có sự cố.</translation>
<translation id="8332188693563227489">Quyền truy cập <ph name="HOST_NAME" /> bị từ chối</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="8344669043927012510">Mở trang ở chế độ ẩn danh (⇧⌘N)</translation>
<translation id="8349305172487531364">Thanh dấu trang</translation>
<translation id="8363502534493474904">Tắt chế độ trên máy bay</translation>
<translation id="8364627913115013041">Chưa được đặt.</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">Báo cáo sự cố được tải lên vào <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Sự cố (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Bạn phải nhập cùng một cụm mật khẩu hai lần.</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Cài đặt</translation>
<translation id="8433057134996913067">Thao tác này sẽ đăng xuất bạn khỏi hầu hết các trang web.</translation>
<translation id="8437238597147034694">&amp;Hoàn tác di chuyển</translation>
@@ -878,8 +909,9 @@
<translation id="8483780878231876732">Để sử dụng thẻ từ Tài khoản Google, hãy đăng nhập vào Chrome</translation>
<translation id="8488350697529856933">Áp dụng cho</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> mất quá nhiều thời gian để phản hồi.</translation>
-<translation id="8532105204136943229">Năm hết hạn</translation>
+<translation id="8503813439785031346">Tên người dùng</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="8571890674111243710">Đang dịch trang sang <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Thêm số đ.thoại
@@ -887,6 +919,7 @@
<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="8620436878122366504">Cha mẹ của bạn chưa phê duyệt trang web</translation>
<translation id="8647750283161643317">Đặt lại tất cả về mặc định</translation>
+<translation id="8660471606262461360">Từ Google Payments</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="8725066075913043281">Thử lại</translation>
@@ -914,6 +947,7 @@
<translation id="8903921497873541725">Phóng to</translation>
<translation id="8931333241327730545">Bạn có muốn lưu thẻ này vào Tài khoản Google của mình không?</translation>
<translation id="8932102934695377596">Đồng hồ của bạn chạy chậm</translation>
+<translation id="8938939909778640821">Thẻ tín dụng và thẻ trả trước được chấp nhận</translation>
<translation id="8971063699422889582">Chứng chỉ của máy chủ đã hết hạn.</translation>
<translation id="8986494364107987395">Tự động gửi số liệu thống kê về việc sử dụng và báo cáo sự cố cho Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -943,7 +977,6 @@
<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="917450738466192189">Chứng chỉ của máy chủ không hợp lệ.</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> tùy chọn giao hàng khác}other{<ph name="SHIPPING_OPTION_PREVIEW" /> và <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> tùy chọn giao hàng khác}}</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>
<translation id="9207861905230894330">Không thêm được bài viết.</translation>
@@ -952,9 +985,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">XÓA BIỂU MẪU</translation>
<translation id="939736085109172342">Thư mục mới</translation>
-<translation id="941721044073577244">Có vẻ như bạn không có quyền truy cập trang web này</translation>
<translation id="969892804517981540">Phiên bản Chính thức</translation>
<translation id="975560348586398090">{COUNT,plural, =0{Không có}=1{1 mục}other{# mục}}</translation>
+<translation id="981121421437150478">Ngoại tuyến</translation>
<translation id="988159990683914416">Phiên bản dành cho Nhà phát triển</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_zh-CN.xtb b/chromium/components/strings/components_strings_zh-CN.xtb
index e5280d6b7d9..5d67971f339 100644
--- a/chromium/components/strings/components_strings_zh-CN.xtb
+++ b/chromium/components/strings/components_strings_zh-CN.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">关闭其他应用</translation>
<translation id="1055184225775184556">撤消添加(&amp;U)</translation>
<translation id="10614374240317010">一律不保存</translation>
+<translation id="1066396345355680611">您可能会无法再访问 <ph name="SITE" /> 及其他一些网站上的受保护内容。</translation>
<translation id="106701514854093668">桌面书签</translation>
<translation id="1074497978438210769">不安全</translation>
<translation id="1080116354587839789">适合窗口宽度</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">阅读清单</translation>
<translation id="1264126396475825575">崩溃报告获取时间:<ph name="CRASH_TIME" />(该报告尚未上传或已被忽略)</translation>
<translation id="1281526147609854549">颁发者:<ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">危险内容已被拦截</translation>
<translation id="1285320974508926690">一律不翻译此网站</translation>
<translation id="129553762522093515">最近关闭的标签页</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />尝试清除 Cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">注册域:</translation>
<translation id="1340482604681802745">取货地址</translation>
-<translation id="1344211575059133124">您似乎需要获得许可,然后才能访问此网站</translation>
<translation id="1344588688991793829">Chromium自动填充设置…</translation>
<translation id="1348198688976932919">您要访问的网站包含危险应用</translation>
<translation id="1374468813861204354">建议</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">位于<ph name="LOCALITY" />的<ph name="ORGANIZATION" />的身份已通过了<ph name="ISSUER" />的验证。</translation>
<translation id="1426410128494586442">是</translation>
<translation id="1430915738399379752">打印</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 种付款方式}other{<ph name="PAYMENT_METHOD_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 种付款方式}}</translation>
<translation id="1506687042165942984">显示为此页面保存的已知过时副本。</translation>
<translation id="1517433312004943670">必须提供电话号码</translation>
+<translation id="1517500485252541695">接受的信用卡和借记卡</translation>
<translation id="1519264250979466059">构建日期</translation>
+<translation id="1527263332363067270">正在等待建立连接…</translation>
<translation id="153384715582417236">暂无新内容</translation>
<translation id="1549470594296187301">必须启用 JavaScript 才能使用此功能。</translation>
<translation id="1555130319947370107">蓝色</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">美国运通卡</translation>
<translation id="1734878702283171397">请尝试联系系统管理员。</translation>
<translation id="1740951997222943430">请输入有效的失效月份</translation>
-<translation id="1745358365027406341">稍后下载网页</translation>
<translation id="17513872634828108">目前打开的标签页</translation>
<translation id="1753706481035618306">页码</translation>
<translation id="1763864636252898013">此服务器无法证明它是<ph name="DOMAIN" />;您设备的操作系统不信任其安全证书。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">必填字段</translation>
<translation id="187918866476621466">打开启动页</translation>
<translation id="1883255238294161206">收起列表</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{<ph name="SHIPPING_ADDRESS_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 个地址}other{<ph name="SHIPPING_ADDRESS_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 个地址}}</translation>
<translation id="1898423065542865115">过滤</translation>
+<translation id="1916770123977586577">要将您更新后的设置应用到此网站,请重新加载此页面</translation>
+<translation id="1919345977826869612">广告</translation>
<translation id="192020519938775529">{COUNT,plural, =0{无}=1{1 个网站}other{# 个网站}}</translation>
<translation id="194030505837763158">请访问<ph name="LINK" /></translation>
+<translation id="1948773908305951926">接受的预付卡</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" />书签</translation>
<translation id="1973335181906896915">序列化错误</translation>
<translation id="1974060860693918893">高级</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP 错误</translation>
<translation id="2270484714375784793">电话号码</translation>
<translation id="2282872951544483773">无法使用的实验功能</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> 项内容}other{<ph name="ITEM_COUNT" /> 项内容}}</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>
<translation id="2337852623177822836">设置由管理员控制</translation>
<translation id="2354001756790975382">其他书签</translation>
<translation id="2354430244986887761">Google 安全浏览功能最近在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />发现了有害应用<ph name="END_LINK" />。</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">继续</translation>
<translation id="2365563543831475020">于 <ph name="CRASH_TIME" /> 获取的崩溃报告未上传</translation>
<translation id="2367567093518048410">级别</translation>
-<translation id="237718015863234333">没有可用的界面备选项</translation>
<translation id="2384307209577226199">在企业环境中默认实施</translation>
<translation id="2386255080630008482">服务器的证书已撤消。</translation>
<translation id="2392959068659972793">显示未设定值的政策</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">选中后,Chromium 会将您的信用卡副本存储在此设备上,以加快表单填写速度。</translation>
<translation id="2498091847651709837">扫描新的信用卡</translation>
<translation id="2501278716633472235">返回</translation>
+<translation id="2503184589641749290">接受的借记卡和预付卡</translation>
<translation id="2515629240566999685">检查您所在区域的网络信号</translation>
-<translation id="2516305470678292029">界面备选项</translation>
<translation id="2539524384386349900">检测</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> 发送的响应无效。</translation>
<translation id="2556876185419854533">撤消修改(&amp;U)</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">送货方式</translation>
<translation id="277499241957683684">缺少设备记录</translation>
<translation id="2784949926578158345">连接已重置。</translation>
+<translation id="2788784517760473862">接受的信用卡</translation>
<translation id="2794233252405721443">网站已被屏蔽</translation>
<translation id="2799020568854403057">您要访问的网站包含有害应用</translation>
<translation id="2803306138276472711">Google安全浏览功能最近在<ph name="SITE" />上<ph name="BEGIN_LINK" />检测到了恶意软件<ph name="END_LINK" />。平常非常安全的网站有时也会感染恶意软件。</translation>
<translation id="2824775600643448204">地址和搜索栏</translation>
<translation id="2826760142808435982">该连接使用 <ph name="CIPHER" /> 进行加密和身份验证,并使用 <ph name="KX" /> 作为密钥交换机制。</translation>
<translation id="2835170189407361413">清除表单</translation>
+<translation id="2851634818064021665">您需获得许可,才能访问此网站</translation>
<translation id="2856444702002559011">攻击者可能会试图从 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 窃取您的信息(例如:密码、通讯内容或信用卡信息)。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">不重新加载</translation>
-<translation id="2900469785430194048">Google Chrome 在尝试显示此网页时内存不足。</translation>
<translation id="2909946352844186028">检测到了网络变化。</translation>
<translation id="2916038427272391327">关闭其他程序</translation>
<translation id="2922350208395188000">无法核实服务器证书。</translation>
<translation id="2928905813689894207">帐单邮寄地址</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{<ph name="SHIPPING_ADDRESS_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 个地址}other{<ph name="SHIPPING_ADDRESS_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 个地址}}</translation>
<translation id="2941952326391522266">此服务器无法证明它是<ph name="DOMAIN" />;其安全证书来自<ph name="DOMAIN2" />。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
<translation id="2948083400971632585">您可以在设置页面中停用任何针对某个连接配置的代理。</translation>
<translation id="2955913368246107853">关闭查找栏</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">启用后,Chromium 会将您的信用卡副本存储在此设备上,以加快表单填写速度。</translation>
<translation id="2985398929374701810">请输入有效的送货地址</translation>
<translation id="2986368408720340940">该取货方式不可用。请另选一种方式。</translation>
@@ -277,10 +281,8 @@
<translation id="3167968892399408617">在您关闭所有隐身标签页后,您在这些标签页中查看的网页不会在浏览器历史记录、Cookie 存储区或搜索记录中留下任何痕迹。不过,您下载的所有文件或创建的书签均会保留下来。</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">岛</translation>
-<translation id="317583078218509884">新的网站权限设置会在重新加载页面后生效。</translation>
<translation id="3176929007561373547">请检查您的代理服务器设置或与网络管理员联系,以确保代理服务器正常运行。如果您认为自己不需要使用代理服务器,请执行以下操作:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">以隐身模式打开网页</translation>
<translation id="320323717674993345">取消付款</translation>
<translation id="3207960819495026254">已加书签</translation>
<translation id="3225919329040284222">服务器提供的证书与内置预期证书不匹配。这些预期证书是针对某些高安全性网站提供的,以便为您提供保护。</translation>
@@ -293,6 +295,7 @@
<translation id="3270847123878663523">撤消顺序调整(&amp;U)</translation>
<translation id="3282497668470633863">添加持卡人姓名</translation>
<translation id="3286538390144397061">立即重新启动</translation>
+<translation id="3287510313208355388">联网时下载</translation>
<translation id="3303855915957856445">未找到任何搜索结果</translation>
<translation id="3305707030755673451">您的数据已于 <ph name="TIME" />使用您的同步密码加密。输入该密码即可开始同步。</translation>
<translation id="3320021301628644560">添加账单邮寄地址</translation>
@@ -325,7 +328,6 @@
<translation id="3452404311384756672">抓取时间间隔:</translation>
<translation id="3462200631372590220">隐藏详情</translation>
<translation id="3467763166455606212">必须输入持卡人姓名</translation>
-<translation id="3478058380795961209">到期月份</translation>
<translation id="3479539252931486093">不应该出现这种情况?请<ph name="BEGIN_LINK" />告诉我们<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">以后再说</translation>
<translation id="3498215018399854026">我们暂时无法与您父母取得联系,请重试。</translation>
@@ -341,6 +343,7 @@
&lt;p&gt;请在&lt;strong&gt;设置&lt;/strong&gt;应用的&lt;strong&gt;常规&lt;/strong&gt;部分调整日期和时间。&lt;/p&gt;<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="COUNTRY" /><ph name="STATE" /><ph name="CITY" /></translation>
+<translation id="358285529439630156">接受信用卡和预付卡。</translation>
<translation id="3582930987043644930">添加名称</translation>
<translation id="3583757800736429874">恢复移动(&amp;R)</translation>
<translation id="3586931643579894722">隐藏详细信息</translation>
@@ -355,10 +358,10 @@
<translation id="3655670868607891010">如果您频繁遇到此问题,请尝试这些<ph name="HELP_LINK" />。</translation>
<translation id="3658742229777143148">修订版本</translation>
<translation id="3678029195006412963">无法签署该请求</translation>
+<translation id="3678529606614285348">在新的无痕式窗口中打开网页 (Ctrl-Shift-N)</translation>
<translation id="3679803492151881375">崩溃报告的获取时间:<ph name="CRASH_TIME" />;上传时间:<ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">证书信息</translation>
<translation id="3690164694835360974">登录方式不安全</translation>
-<translation id="3693415264595406141">密码:</translation>
<translation id="3704609568417268905"><ph name="TIME" /> - <ph name="BOOKMARKED" /> - <ph name="TITLE" /> - <ph name="DOMAIN" /></translation>
<translation id="370665806235115550">正在加载...</translation>
<translation id="3712624925041724820">许可已用尽</translation>
@@ -370,6 +373,7 @@
<translation id="3748148204939282805"><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="375403751935624634">由于服务器出错,翻译失败。</translation>
<translation id="3759461132968374835">您最近未收到崩溃报告。崩溃报告停用时发生的崩溃不会在此处显示。</translation>
+<translation id="3765032636089507299">安全浏览页面正在构建中。</translation>
<translation id="3778403066972421603">要将此卡的信息保存到您的 Google 帐号中和此设备上吗?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">失效日期:<ph name="EXPIRATION_YEAR" /> 年 <ph name="EXPIRATION_MONTH" /> 月</translation>
@@ -382,8 +386,10 @@
<translation id="3886446263141354045">系统已将您想访问此网站的请求发送给<ph name="NAME" /></translation>
<translation id="3890664840433101773">添加电子邮件地址</translation>
<translation id="3901925938762663762">此信用卡已过期</translation>
+<translation id="3909695131102177774"><ph name="LABEL" /> <ph name="ERROR" /></translation>
<translation id="3945915738023014686">已上传的崩溃报告的 ID:<ph name="CRASH_ID" />(本地崩溃 ID:<ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">此服务器无法证实它就是 <ph name="DOMAIN" /> - 它的安全证书没有指定主题备用名称。这可能是因为某项配置有误或某个攻击者拦截了您的连接。</translation>
+<translation id="3949601375789751990">您的浏览记录会显示在此处</translation>
<translation id="3963721102035795474">阅读器模式</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{无}=1{来自 1 个网站}other{来自 # 个网站}}</translation>
<translation id="397105322502079400">正在计算...</translation>
@@ -412,6 +418,8 @@
<translation id="4165986682804962316">网站设置</translation>
<translation id="4169947484918424451">您希望 Chromium 保存此信用卡吗?</translation>
<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="4196861286325780578">恢复移动(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />检查防火墙和防病毒配置<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">崩溃</translation>
@@ -435,12 +443,12 @@
<translation id="4356973930735388585">此网站上的攻击者可能会试图在您的计算机上安装危险程序,以窃取或删除您的信息(例如:照片、密码、通讯内容和信用卡信息)。</translation>
<translation id="4372948949327679948">应使用<ph name="VALUE_TYPE" />值。</translation>
<translation id="4377125064752653719">您尝试访问的是 <ph name="DOMAIN" />,但服务器出示的证书已被其颁发者吊销。这表明绝对不应该信任此服务器出示的安全凭据。您可能正在与攻击者进行通信。</translation>
-<translation id="4381091992796011497">用户名:</translation>
<translation id="4394049700291259645">停用</translation>
<translation id="4406896451731180161">搜索结果</translation>
<translation id="4424024547088906515">此服务器无法证明它是<ph name="DOMAIN" />;Chrome不信任其安全证书。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> 不接受您的登录证书,或者您可能没有提供登录证书。</translation>
<translation id="443673843213245140">已停用代理,但是指定了明确的代理配置。</translation>
+<translation id="445100540951337728">接受的借记卡</translation>
<translation id="4506176782989081258">验证错误:<ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">联系系统管理员</translation>
<translation id="450710068430902550">与管理员分享</translation>
@@ -452,12 +460,14 @@
<translation id="4587425331216688090">从 Chrome 中移除地址?</translation>
<translation id="4592951414987517459">您与 <ph name="DOMAIN" /> 之间的连接采用新型加密套件进行了加密。</translation>
<translation id="4594403342090139922">撤消删除(&amp;U)</translation>
+<translation id="4611292653554630842">登录</translation>
<translation id="4619615317237390068">从其他设备打开的标签页</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">此服务器无法证明它是<ph name="DOMAIN" />;其安全证书有误。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
<translation id="4690462567478992370">停止使用无效的证书</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /><ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">您的连接已中断</translation>
+<translation id="471880041731876836">您无权访问此网站</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />运行 Windows 网络诊断<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">重新加载政策</translation>
<translation id="4728558894243024398">平台</translation>
@@ -479,14 +489,15 @@
<translation id="4850886885716139402">视图</translation>
<translation id="4854362297993841467">该递送方式不可用。请另选一种方式。</translation>
<translation id="4858792381671956233">您已向父母发送请求,询问他们是否允许您访问此网站</translation>
-<translation id="4863764087567530506">此内容可能会试图诱骗您安装软件或透露个人信息。<ph name="BEGIN_LINK" />仍然显示<ph name="END_LINK" />。</translation>
<translation id="4880827082731008257">搜索历史记录</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />、<ph name="TYPE_2" />、<ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">需要进行身份验证</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{和另外 1 个网页}other{和另外 # 个网页}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">翻译服务器已将此网页从某种未知语言翻译成了<ph name="LANGUAGE_LANGUAGE" />。</translation>
<translation id="4923459931733593730">付款</translation>
<translation id="4926049483395192435">必须指定。</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">操作</translation>
<translation id="4958444002117714549">展开列表</translation>
<translation id="4974590756084640048">重新启用警告功能</translation>
@@ -502,6 +513,7 @@
<translation id="5045550434625856497">密码不正确</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>
<translation id="5087580092889165836">添加新卡</translation>
<translation id="5089810972385038852">州</translation>
@@ -509,18 +521,27 @@
<translation id="5095208057601539847">省</translation>
<translation id="5115563688576182185">(64 位)</translation>
<translation id="5141240743006678641">使用您的 Google 凭据加密已同步的密码</translation>
-<translation id="514421653919133810">以隐身模式打开网页 (Ctrl-Shift-N)</translation>
<translation id="5145883236150621069">策略响应中存在错误代码</translation>
+<translation id="5159010409087891077">在新的无痕式窗口中打开网页 (⇧⌘N)</translation>
<translation id="5171045022955879922">搜索或输入网址</translation>
<translation id="5172758083709347301">本机</translation>
<translation id="5179510805599951267">不是<ph name="ORIGINAL_LANGUAGE" />?报告此错误</translation>
-<translation id="5181140330217080051">正在下载</translation>
<translation id="5190835502935405962">书签栏</translation>
<translation id="5199729219167945352">实验</translation>
<translation id="5205222826937269299">需要提供名称</translation>
<translation id="5222812217790122047">需要提供电子邮件地址</translation>
<translation id="5251803541071282808">云端</translation>
<translation id="5277279256032773186">使用 Chrome 办公?企业可以为其员工管理 Chrome 设置。了解详情</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />按照下述步骤操作可临时停用此类软件,以便您能够连接到网络。您需要具有管理员权限。<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />点击<ph name="BEGIN_BOLD" />开始<ph name="END_BOLD" />,然后搜索并选择<ph name="BEGIN_BOLD" />“查看本地服务”<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />选择 <ph name="BEGIN_BOLD" />VisualDiscovery<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />选择<ph name="BEGIN_BOLD" />启动类型<ph name="END_BOLD" />下方的<ph name="BEGIN_BOLD" />停用<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />点击<ph name="BEGIN_BOLD" />服务状态<ph name="END_BOLD" />下方的<ph name="BEGIN_BOLD" />停止<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />依次点击<ph name="BEGIN_BOLD" />应用<ph name="END_BOLD" />和<ph name="BEGIN_BOLD" />确定<ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />访问 <ph name="BEGIN_LEARN_MORE_LINK" />Chrome 帮助中心<ph name="END_LEARN_MORE_LINK" />,了解如何从您的计算机中永久移除此类软件
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">您与此网站建立的不是私密连接。您可以随时取下头戴式设备,然后按“返回”,以退出 VR 模式。</translation>
<translation id="5299298092464848405">解析策略时出错</translation>
<translation id="5308689395849655368">已停用崩溃报告。</translation>
@@ -537,9 +558,10 @@
<translation id="5439770059721715174">“<ph name="ERROR_PATH" />”中存在模式验证错误:<ph name="ERROR" /></translation>
<translation id="5452270690849572955">找不到 <ph name="HOST_NAME" /> 的网页</translation>
<translation id="5455374756549232013">策略时间戳无效</translation>
-<translation id="5455790498993699893">第 <ph name="ACTIVE_MATCH" /> 条,共 <ph name="TOTAL_MATCHCOUNT" /> 条</translation>
<translation id="5457113250005438886">无效</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{<ph name="CONTACT_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 名联系人}other{<ph name="CONTACT_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 名联系人}}</translation>
<translation id="5470861586879999274">恢复修改(&amp;R)</translation>
+<translation id="5481076368049295676">此内容可能会试图在您的设备上安装危险的软件来窃取或删除您的信息。<ph name="BEGIN_LINK" />仍然显示<ph name="END_LINK" /></translation>
<translation id="54817484435770891">添加有效地址</translation>
<translation id="5492298309214877701">这个位于公司、组织或学校内网中的网站使用的网址与某个外部网站的网址相同。
<ph name="LINE_BREAK" />
@@ -551,6 +573,7 @@
<translation id="5540224163453853">找不到请求的文章。</translation>
<translation id="5544037170328430102"><ph name="SITE" /> 上的嵌入式页面显示:</translation>
<translation id="5556459405103347317">重新加载</translation>
+<translation id="5560088892362098740">到期日期</translation>
<translation id="5565735124758917034">主动</translation>
<translation id="5571083550517324815">无法从此地址取货。请另选一个地址。</translation>
<translation id="5572851009514199876">请启动并登录 Chrome,以便 Chrome 能够检查您是否可以访问此网站。</translation>
@@ -565,12 +588,13 @@
<translation id="5629630648637658800">无法加载策略设置</translation>
<translation id="5631439013527180824">设备管理令牌无效</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="5646376287012673985">位置</translation>
<translation id="5659593005791499971">电子邮件</translation>
<translation id="5669703222995421982">获取个性化内容</translation>
<translation id="5675650730144413517">该网页无法正常运作</translation>
<translation id="5710435578057952990">此网站尚未经过身份验证。</translation>
-<translation id="5713016350996637505">欺骗性内容已被拦截</translation>
+<translation id="5719499550583120431">接受预付卡。</translation>
<translation id="5720705177508910913">当前用户</translation>
<translation id="5732392974455271431">您的父母可为您取消屏蔽此网站</translation>
<translation id="5763042198335101085">请输入有效的电子邮件地址</translation>
@@ -582,15 +606,14 @@
<translation id="5803412860119678065">要填充您的“<ph name="CARD_DETAIL" />”吗?</translation>
<translation id="5810442152076338065">您与 <ph name="DOMAIN" /> 之间的连接采用过时的加密套件进行了加密。</translation>
<translation id="5813119285467412249">恢复添加(&amp;R)</translation>
-<translation id="5814352347845180253">您可能无法再访问 <ph name="SITE" /> 以及其他一些网站上的付费内容。</translation>
<translation id="5838278095973806738">请勿在此网站上输入任何敏感信息(例如密码或信用卡信息),因为攻击者可能会盗取这些信息。</translation>
<translation id="5869405914158311789">无法访问此网站</translation>
<translation id="5869522115854928033">已保存的密码</translation>
<translation id="5872918882028971132">家长建议</translation>
+<translation id="5893752035575986141">接受信用卡。</translation>
<translation id="5901630391730855834">黄色</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" />(已同步)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{正在使用 1 个}other{正在使用 # 个}}</translation>
-<translation id="5926846154125914413">您可能无法再访问某些网站上的付费内容。</translation>
<translation id="5959728338436674663">自动向 Google 发送一些<ph name="BEGIN_WHITEPAPER_LINK" />系统信息和网页内容<ph name="END_WHITEPAPER_LINK" />,以帮助检测危险应用和网站。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">从历史记录中移除</translation>
<translation id="5975083100439434680">缩小</translation>
@@ -606,6 +629,7 @@
<translation id="6040143037577758943">关闭</translation>
<translation id="6042308850641462728">更多</translation>
<translation id="6047233362582046994">如果您了解自己将面临的安全风险,则可在有害应用被移除之前<ph name="BEGIN_LINK" />访问此网站<ph name="END_LINK" />。</translation>
+<translation id="6047927260846328439">此内容可能会试图诱骗您安装软件或透露个人信息。<ph name="BEGIN_LINK" />仍然显示<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">您目前无法访问 <ph name="SITE" />,因为此网站使用了证书锁定。网络错误和攻击通常是暂时的,因此,此网页稍后可能会恢复正常。</translation>
<translation id="6060685159320643512">请小心,这些实验性功能可能有风险</translation>
<translation id="6080696365213338172">您已使用管理员提供的证书访问了内容,因此管理员可以拦截您提供给 <ph name="DOMAIN" /> 的数据。</translation>
@@ -618,7 +642,6 @@
<translation id="6165508094623778733">了解详情</translation>
<translation id="6169916984152623906">现在,您便可进行私密浏览了。共用此设备的其他用户将不会看到您的活动,但您下载的内容和添加的书签仍会保存在设备上。</translation>
<translation id="6177128806592000436">您与此网站之间建立的连接不安全</translation>
-<translation id="6184817833369986695">(同类群组:<ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">请检查您的互联网连接是否正常</translation>
<translation id="6218753634732582820">要从 Chromium 中移除地址吗?</translation>
<translation id="6221345481584921695">Google安全浏览功能最近在<ph name="SITE" />上<ph name="BEGIN_LINK" />检测到了恶意软件<ph name="END_LINK" />。平常非常安全的网站有时也会感染恶意软件。检测到的恶意内容来自于<ph name="SUBRESOURCE_HOST" />,这是个出了名的恶意软件散布方。</translation>
@@ -637,13 +660,14 @@
<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>
<translation id="6387478394221739770">想试试超酷的 Chrome 新功能?欢迎访问 chrome.com/beta,试用我们的测试版!</translation>
-<translation id="6389758589412724634">Chromium 在尝试显示此网页时内存不足。</translation>
+<translation id="6397451950548600259">您计算机上的软件导致 Chrome 无法安全地连接到网络</translation>
<translation id="6404511346730675251">修改书签</translation>
<translation id="6410264514553301377">请输入“<ph name="CREDIT_CARD" />”的到期日期和银行卡验证码 (CVC)</translation>
<translation id="6414888972213066896">您已向父亲/母亲发送请求,询问其是否允许您访问此网站</translation>
@@ -658,6 +682,7 @@
<translation id="647261751007945333">设备政策</translation>
<translation id="6477321094435799029">Chrome 在此网页上检测到了异常代码。为保护您的个人信息(例如密码、电话号码和信用卡信息),Chrome 已将该网页拦截。</translation>
<translation id="6489534406876378309">开始上传崩溃数据</translation>
+<translation id="6507833130742554667">接受信用卡和借记卡。</translation>
<translation id="6508722015517270189">重新启动 Chrome</translation>
<translation id="6529602333819889595">恢复删除(&amp;R)</translation>
<translation id="6534179046333460208">实物网建议</translation>
@@ -666,13 +691,13 @@
<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="657639383826808334">此内容可能会试图在您的设备上安装危险软件来窃取或删除您的信息。<ph name="BEGIN_LINK" />仍然显示<ph name="END_LINK" />。</translation>
<translation id="6596325263575161958">加密选项</translation>
<translation id="662080504995468778">留下</translation>
<translation id="6626291197371920147">添加有效的卡号</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> 搜索</translation>
<translation id="6630809736994426279">攻击者可能会试图通过 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在您的 Mac 上安装危险程序,以窃取或删除您的信息(如照片、密码、通讯内容和信用卡信息)。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">此政策已弃用。</translation>
+<translation id="6657585470893396449">密码</translation>
<translation id="6671697161687535275">要从 Chromium 中移除表单填写建议吗?</translation>
<translation id="6685834062052613830">请退出并完成设置</translation>
<translation id="6710213216561001401">上一个</translation>
@@ -703,7 +728,6 @@
<translation id="6970216967273061347">区</translation>
<translation id="6973656660372572881">固定代理服务器和 .pac 脚本网址均已指定。</translation>
<translation id="6989763994942163495">显示高级设置...</translation>
-<translation id="7000990526846637657">未找到任何记录条目</translation>
<translation id="7012363358306927923">中国银联</translation>
<translation id="7012372675181957985">您的 Google 帐号在 <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> 上可能有其他形式的浏览记录</translation>
<translation id="7029809446516969842">密码</translation>
@@ -718,7 +742,9 @@
<translation id="7129409597930077180">无法向此地址送货。请另选一个地址。</translation>
<translation id="7138472120740807366">递送方式</translation>
<translation id="7139724024395191329">酋长国</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 种付款方式}other{<ph name="PAYMENT_METHOD_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 种付款方式}}</translation>
<translation id="7155487117670177674">付款方式不安全</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 种送货方式}other{<ph name="SHIPPING_OPTION_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 种送货方式}}</translation>
<translation id="7179921470347911571">立即重新启动</translation>
<translation id="7180611975245234373">刷新</translation>
<translation id="7182878459783632708">未设置任何政策</translation>
@@ -759,6 +785,7 @@
<translation id="7514365320538308">下载</translation>
<translation id="7518003948725431193">找不到与以下网址对应的网页:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">您与此网站的连接不是私密连接</translation>
<translation id="7535087603100972091">值</translation>
<translation id="7537536606612762813">强制</translation>
<translation id="7542403920425041731">一旦您予以确认,系统便会将您的信用卡详情共享给此网站。</translation>
@@ -768,7 +795,6 @@
<translation id="7552846755917812628">请尝试按以下提示操作:</translation>
<translation id="7554791636758816595">新标签页</translation>
<translation id="7567204685887185387">此服务器无法证明它是<ph name="DOMAIN" />;其安全证书可能是由骗子发出的。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{<ph name="CONTACT_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 名联系人}other{<ph name="CONTACT_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 名联系人}}</translation>
<translation id="7568593326407688803">此网页为<ph name="ORIGINAL_LANGUAGE" />网页,是否需要翻译?</translation>
<translation id="7569952961197462199">从 Chrome 中移除信用卡信息?</translation>
<translation id="7569983096843329377">黑色</translation>
@@ -795,9 +821,11 @@
<translation id="7714464543167945231">证书</translation>
<translation id="7716147886133743102">已被您的管理员阻止</translation>
<translation id="7716424297397655342">无法从缓存中加载此网站</translation>
+<translation id="774634243536837715">危险内容已被拦截。</translation>
<translation id="7752995774971033316">非托管</translation>
<translation id="7755287808199759310">您的父亲/母亲可为您取消屏蔽此网站</translation>
<translation id="7758069387465995638">防火墙或防病毒软件可能已阻止您连接到网络。</translation>
+<translation id="7759163816903619567">显示网域:</translation>
<translation id="7761701407923456692">服务器的证书与网址不相符。</translation>
<translation id="7763386264682878361">付款清单解析器</translation>
<translation id="7764225426217299476">添加地址</translation>
@@ -812,6 +840,7 @@
<translation id="7815407501681723534">找到了 <ph name="NUMBER_OF_RESULTS" /> 个与“<ph name="SEARCH_STRING" />”相符的<ph name="SEARCH_RESULTS" /></translation>
<translation id="785549533363645510">但是,这并不意味着您能完全隐身。即使您进入隐身模式,您的雇主、互联网服务提供商和您访问的网站仍然能看到您的浏览活动。</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">接受借记卡和预付卡。</translation>
<translation id="7887683347370398519">请检查您的银行卡验证码 (CVC),然后重试</translation>
<translation id="79338296614623784">请输入有效的电话号码</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -831,7 +860,6 @@
<translation id="8041089156583427627">发送反馈</translation>
<translation id="8041940743680923270">使用全局默认设置(询问)</translation>
<translation id="8088680233425245692">无法查看文章。</translation>
-<translation id="8089520772729574115">小于 1 MB</translation>
<translation id="8091372947890762290">正等待在服务器上激活</translation>
<translation id="8118489163946903409">付款方式</translation>
<translation id="8131740175452115882">确认</translation>
@@ -843,10 +871,13 @@
<translation id="8194797478851900357">撤消移动(&amp;U)</translation>
<translation id="8201077131113104583">ID 为“<ph name="EXTENSION_ID" />”的扩展程序的更新网址无效。</translation>
<translation id="8202097416529803614">订单摘要</translation>
+<translation id="8205463626947051446">网站常常会展示侵扰性广告</translation>
<translation id="8218327578424803826">分配的位置:</translation>
<translation id="8225771182978767009">设置此计算机的用户已选择屏蔽此网站。</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />、<ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">在新的无痕式标签页中打开网页</translation>
<translation id="8241707690549784388">您所查找的网页要使用已输入的信息。返回此页可能需要重复已进行的所有操作。是否要继续操作?</translation>
+<translation id="8241712895048303527">在此网站上拦截</translation>
<translation id="8249320324621329438">最后一次抓取时间:</translation>
<translation id="8253091569723639551">账单邮寄地址是必填项</translation>
<translation id="8261506727792406068">删除</translation>
@@ -857,7 +888,6 @@
<translation id="8308427013383895095">由于网络连接问题,翻译失败。</translation>
<translation id="8332188693563227489">访问 <ph name="HOST_NAME" /> 的请求遭到拒绝</translation>
<translation id="834457929814110454">如果您了解自己将面临的安全风险,则可以在有害程序被清除之前<ph name="BEGIN_LINK" />访问此网站<ph name="END_LINK" />。</translation>
-<translation id="8344669043927012510">以隐身模式打开网页 (⇧⌘N)</translation>
<translation id="8349305172487531364">书签栏</translation>
<translation id="8363502534493474904">关闭飞行模式</translation>
<translation id="8364627913115013041">未设置。</translation>
@@ -867,6 +897,7 @@
<translation id="8398259832188219207">崩溃报告的上传时间:<ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">崩溃次数(<ph name="CRASH_COUNT" /> 次)</translation>
<translation id="8412392972487953978">您两次输入的密码必须相同。</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> - <ph name="SECOND_LABEL" /> - <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">设置</translation>
<translation id="8433057134996913067">这将使您退出大多数网站。</translation>
<translation id="8437238597147034694">撤消移动(&amp;U)</translation>
@@ -874,8 +905,9 @@
<translation id="8483780878231876732">要使用您 Google 帐号中的信用卡,请登录 Chrome</translation>
<translation id="8488350697529856933">适用对象</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> 的响应时间过长。</translation>
-<translation id="8532105204136943229">到期年份</translation>
+<translation id="8503813439785031346">用户名</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="8571890674111243710">正在将网页翻译成<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">添加电话号码
@@ -883,6 +915,7 @@
<translation id="859285277496340001">证书未指定用于检查是否已吊销证书的机制。</translation>
<translation id="8620436878122366504">您的父母尚未批准此请求</translation>
<translation id="8647750283161643317">全部重置为默认值</translation>
+<translation id="8660471606262461360">来自 Google Payments</translation>
<translation id="8703575177326907206">您与 <ph name="DOMAIN" /> 的连接未加密。</translation>
<translation id="8718314106902482036">未能完成付款</translation>
<translation id="8725066075913043281">重试</translation>
@@ -910,6 +943,7 @@
<translation id="8903921497873541725">放大</translation>
<translation id="8931333241327730545">要将此卡的信息保存到您的 Google 帐号吗?</translation>
<translation id="8932102934695377596">您的时钟慢了</translation>
+<translation id="8938939909778640821">接受的信用卡和预付卡</translation>
<translation id="8971063699422889582">服务器的证书已过期。</translation>
<translation id="8986494364107987395">将使用情况统计信息和崩溃报告自动发送给 Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -939,7 +973,6 @@
<translation id="9169664750068251925">在此网站上始终阻止</translation>
<translation id="9170848237812810038">撤消(&amp;U)</translation>
<translation id="917450738466192189">服务器证书无效。</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 种送货方式}other{<ph name="SHIPPING_OPTION_PREVIEW" />以及另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 种送货方式}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> 使用了不受支持的协议。</translation>
<translation id="9205078245616868884">您的数据已使用您的同步密码加密。输入该密码即可开始同步。</translation>
<translation id="9207861905230894330">无法添加文章。</translation>
@@ -948,9 +981,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">清除表单内容</translation>
<translation id="939736085109172342">新建文件夹</translation>
-<translation id="941721044073577244">您似乎无权访问此网站</translation>
<translation id="969892804517981540">正式版本</translation>
<translation id="975560348586398090">{COUNT,plural, =0{无}=1{1 项内容}other{# 项内容}}</translation>
+<translation id="981121421437150478">离线</translation>
<translation id="988159990683914416">开发者内部版本</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/strings/components_strings_zh-TW.xtb b/chromium/components/strings/components_strings_zh-TW.xtb
index 659321a7742..8084658a3b1 100644
--- a/chromium/components/strings/components_strings_zh-TW.xtb
+++ b/chromium/components/strings/components_strings_zh-TW.xtb
@@ -9,6 +9,7 @@
<translation id="1050038467049342496">關閉其他應用程式</translation>
<translation id="1055184225775184556">復原新增(&amp;U)</translation>
<translation id="10614374240317010">一律不儲存</translation>
+<translation id="1066396345355680611">你可能無法再存取 <ph name="SITE" /> 和其他網站上的受保護內容。</translation>
<translation id="106701514854093668">電腦版書籤</translation>
<translation id="1074497978438210769">不安全</translation>
<translation id="1080116354587839789">符合視窗寬度</translation>
@@ -43,7 +44,6 @@
<translation id="1263231323834454256">閱讀清單</translation>
<translation id="1264126396475825575">當機報告擷取時間:<ph name="CRASH_TIME" /> (尚未上傳或略過)</translation>
<translation id="1281526147609854549">核發者:<ph name="ISSUER" /></translation>
-<translation id="1283919782143846010">已封鎖不安全的內容</translation>
<translation id="1285320974508926690">一律不翻譯此網站</translation>
<translation id="129553762522093515">最近關閉的分頁</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />試試看清除 Cookie<ph name="END_LINK" /></translation>
@@ -55,7 +55,6 @@
<ph name="END_LIST" /></translation>
<translation id="1339601241726513588">註冊網域:</translation>
<translation id="1340482604681802745">取件地址</translation>
-<translation id="1344211575059133124">你必須獲得授權,才能造訪這個網站</translation>
<translation id="1344588688991793829">Chromium 自動填入設定...</translation>
<translation id="1348198688976932919">你要造訪的網站含有不安全的應用程式</translation>
<translation id="1374468813861204354">建議</translation>
@@ -67,10 +66,11 @@
<translation id="14171126816530869">位於 <ph name="LOCALITY" /> 的 <ph name="ORGANIZATION" />,已經過 <ph name="ISSUER" /> 驗證身分。</translation>
<translation id="1426410128494586442">是</translation>
<translation id="1430915738399379752">列印</translation>
-<translation id="1495172241291051507">{PAYMENT_METHOD,plural, =1{<ph name="PAYMENT_METHOD_PREVIEW" /> 和另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 種付款方式}other{<ph name="PAYMENT_METHOD_PREVIEW" /> 和另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 種付款方式}}</translation>
<translation id="1506687042165942984">顯示這個網頁的儲存複本 (亦即現已不再使用的版本)。</translation>
<translation id="1517433312004943670">必須輸入電話號碼</translation>
+<translation id="1517500485252541695">接受的信用卡和簽帳金融卡</translation>
<translation id="1519264250979466059">建立日期</translation>
+<translation id="1527263332363067270">正在等待連線…</translation>
<translation id="153384715582417236">暫無內容</translation>
<translation id="1549470594296187301">您必須啟用 JavaScript 才能使用這項功能。</translation>
<translation id="1555130319947370107">藍色</translation>
@@ -101,7 +101,6 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">建議您與系統管理員聯絡。</translation>
<translation id="1740951997222943430">請輸入有效的到期月份</translation>
-<translation id="1745358365027406341">稍後下載網頁</translation>
<translation id="17513872634828108">開啟分頁</translation>
<translation id="1753706481035618306">頁碼</translation>
<translation id="1763864636252898013">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證未取得你裝置作業系統的信任。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。</translation>
@@ -121,10 +120,12 @@
<translation id="1871284979644508959">必填欄位</translation>
<translation id="187918866476621466">開啟起始網頁</translation>
<translation id="1883255238294161206">收合清單</translation>
-<translation id="1894246157993903018">{SHIPPING_ADDRESS,plural, =1{「<ph name="SHIPPING_ADDRESS_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 個地址}other{「<ph name="SHIPPING_ADDRESS_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 個地址}}</translation>
<translation id="1898423065542865115">篩選</translation>
+<translation id="1916770123977586577">如要將你更新過的設定套用到這個網站,請重新載入這個網頁</translation>
+<translation id="1919345977826869612">廣告</translation>
<translation id="192020519938775529">{COUNT,plural, =0{無}=1{1 個網站}other{# 個網站}}</translation>
<translation id="194030505837763158">前往 <ph name="LINK" /></translation>
+<translation id="1948773908305951926">接受的預付卡</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> 書籤</translation>
<translation id="1973335181906896915">序列化錯誤</translation>
<translation id="1974060860693918893">進階</translation>
@@ -165,10 +166,11 @@
<translation id="2262243747453050782">HTTP 錯誤</translation>
<translation id="2270484714375784793">電話號碼</translation>
<translation id="2282872951544483773">無法使用的實驗性功能</translation>
-<translation id="2283595600756901917">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> 個項目}other{<ph name="ITEM_COUNT" /> 個項目}}</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>
<translation id="2337852623177822836">管理員所控制的設定</translation>
<translation id="2354001756790975382">其他書籤</translation>
<translation id="2354430244986887761">Google 安全瀏覽服務最近在 <ph name="SITE" /> <ph name="BEGIN_LINK" />發現有害的應用程式<ph name="END_LINK" />。</translation>
@@ -178,7 +180,6 @@
<translation id="2359808026110333948">繼續</translation>
<translation id="2365563543831475020">當機報告擷取時間:<ph name="CRASH_TIME" /> (尚未上傳)</translation>
<translation id="2367567093518048410">等級</translation>
-<translation id="237718015863234333">沒有可用的替代使用者介面</translation>
<translation id="2384307209577226199">企業預設</translation>
<translation id="2386255080630008482">伺服器憑證已遭撤銷。</translation>
<translation id="2392959068659972793">顯示尚未設定任何值的政策</translation>
@@ -194,8 +195,8 @@
<translation id="2495093607237746763">勾選後,Chromium 會將您的信用卡資料儲存在這個裝置上,以加快表單填寫速度。</translation>
<translation id="2498091847651709837">掃描新信用卡</translation>
<translation id="2501278716633472235">返回</translation>
+<translation id="2503184589641749290">接受的簽帳金融卡和預付卡</translation>
<translation id="2515629240566999685">檢查所在位置的網路訊號</translation>
-<translation id="2516305470678292029">替代使用者介面</translation>
<translation id="2539524384386349900">偵測</translation>
<translation id="255002559098805027"><ph name="HOST_NAME" /> 傳送的回應無效。</translation>
<translation id="2556876185419854533">復原編輯(&amp;U)</translation>
@@ -229,19 +230,21 @@
<translation id="277133753123645258">運送方式</translation>
<translation id="277499241957683684">沒有裝置紀錄</translation>
<translation id="2784949926578158345">連線已重設。</translation>
+<translation id="2788784517760473862">接受的信用卡</translation>
<translation id="2794233252405721443">網站遭到封鎖</translation>
<translation id="2799020568854403057">你要造訪的網站含有有害的應用程式</translation>
<translation id="2803306138276472711">Google 安全瀏覽功能最近在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />偵測到惡意軟體<ph name="END_LINK" />。即使是平常可以安全使用的網站,有時也會遭到惡意軟體感染。</translation>
<translation id="2824775600643448204">網址與搜尋列</translation>
<translation id="2826760142808435982">連線採用 <ph name="CIPHER" /> 加密,並設有 <ph name="KX" /> 金鑰交換機制。</translation>
<translation id="2835170189407361413">清除表單</translation>
+<translation id="2851634818064021665">你必須獲得授權,才能造訪這個網站</translation>
<translation id="2856444702002559011">攻擊者可能會試圖從 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 竊取你的資訊 (例如密碼、郵件或信用卡資料)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2889159643044928134">不要重新載入</translation>
-<translation id="2900469785430194048">Google Chrome 嘗試顯示這個網頁時用盡了記憶體。</translation>
<translation id="2909946352844186028">系統偵測到網路變更。</translation>
<translation id="2916038427272391327">關閉其他程式</translation>
<translation id="2922350208395188000">無法檢查伺服器憑證。</translation>
<translation id="2928905813689894207">帳單地址</translation>
+<translation id="2929525460561903222">{SHIPPING_ADDRESS,plural, =0{<ph name="SHIPPING_ADDRESS_PREVIEW" />}=1{「<ph name="SHIPPING_ADDRESS_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 個地址}other{「<ph name="SHIPPING_ADDRESS_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_ADDRESSES" /> 個地址}}</translation>
<translation id="2941952326391522266">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證來自 <ph name="DOMAIN2" /> 網域。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。</translation>
<translation id="2948083400971632585">你可以在設定頁面停用任何為連線設置的 Proxy。</translation>
<translation id="2955913368246107853">關閉搜尋列</translation>
@@ -249,6 +252,7 @@
<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>
<translation id="2985306909656435243">啟用後,Chromium 會將您的信用卡資料儲存在這個裝置上,以加快表單填寫速度。</translation>
<translation id="2985398929374701810">請輸入有效的地址</translation>
<translation id="2986368408720340940">不支援所選的取件方式,請改選其他方式。</translation>
@@ -277,12 +281,10 @@
<translation id="3167968892399408617">當您關閉所有無痕式分頁後,您在其中瀏覽的網頁都不會保留在瀏覽器紀錄、Cookie 儲存庫或搜尋紀錄中。不過,您下載的檔案或建立的書籤全部都會保留下來。</translation>
<translation id="3169472444629675720">Discover</translation>
<translation id="3174168572213147020">島</translation>
-<translation id="317583078218509884">新的網站權限設定會在重新載入網頁後生效。</translation>
<translation id="3176929007561373547">檢查您的 Proxy 設定,或是與您的網路管理員聯絡
確認您的 Proxy 伺服器運作正常。如果您認為自己不需要使用
Proxy 伺服器:
<ph name="PLATFORM_TEXT" /></translation>
-<translation id="3200379322500587640">以無痕模式開啟網頁</translation>
<translation id="320323717674993345">取消付款</translation>
<translation id="3207960819495026254">已加入書籤</translation>
<translation id="3225919329040284222">伺服器呈現的憑證與內建的預期條件不符。我們在系統中針對特定高安全性的網站內建了這些預期條件,目的在於保護你的資料安全無虞。</translation>
@@ -295,6 +297,7 @@
<translation id="3270847123878663523">復原重新排序(&amp;U)</translation>
<translation id="3282497668470633863">新增持卡人姓名</translation>
<translation id="3286538390144397061">立即重新啟動</translation>
+<translation id="3287510313208355388">上線時進行下載</translation>
<translation id="3303855915957856445">找不到相符的搜尋結果</translation>
<translation id="3305707030755673451">您已在 <ph name="TIME" />使用同步通關密語對資料進行加密,請輸入通關密語開始進行同步。</translation>
<translation id="3320021301628644560">新增帳單地址</translation>
@@ -327,7 +330,6 @@
<translation id="3452404311384756672">擷取間隔:</translation>
<translation id="3462200631372590220">隱藏詳細資料</translation>
<translation id="3467763166455606212">持卡人姓名為必填項目</translation>
-<translation id="3478058380795961209">到期月份</translation>
<translation id="3479539252931486093">這是未預期的情況嗎?<ph name="BEGIN_LINK" />通知我們<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">現在不要</translation>
<translation id="3498215018399854026">我們暫時無法與您的家長聯絡,請再試一次。</translation>
@@ -343,6 +345,7 @@
&lt;p&gt;請前往「設定」應用程式的「一般設定」專區調整日期和時間。&lt;strong&gt;&lt;/strong&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="3574305903863751447"><ph name="COUNTRY" />,<ph name="STATE" />,<ph name="CITY" /></translation>
+<translation id="358285529439630156">接受信用卡和預付卡。</translation>
<translation id="3582930987043644930">新增名稱</translation>
<translation id="3583757800736429874">重做移動(&amp;R)</translation>
<translation id="3586931643579894722">隱藏詳細資訊</translation>
@@ -358,10 +361,10 @@
<translation id="3655670868607891010">如果你經常看到這個頁面,請參考這些說明:<ph name="HELP_LINK" />。</translation>
<translation id="3658742229777143148">修訂版本</translation>
<translation id="3678029195006412963">無法簽署要求</translation>
+<translation id="3678529606614285348">在新的無痕式視窗中開啟網頁 (Ctrl + Shift + N 鍵)</translation>
<translation id="3679803492151881375">當機報告擷取時間:<ph name="CRASH_TIME" />,報告上傳時間:<ph name="UPLOAD_TIME" /></translation>
<translation id="3681007416295224113">憑證資訊</translation>
<translation id="3690164694835360974">登入行為不安全</translation>
-<translation id="3693415264595406141">密碼:</translation>
<translation id="3704609568417268905"><ph name="TIME" />,<ph name="BOOKMARKED" />,<ph name="TITLE" />,<ph name="DOMAIN" /></translation>
<translation id="370665806235115550">載入中…</translation>
<translation id="3712624925041724820">授權已用盡</translation>
@@ -373,6 +376,7 @@
<translation id="3748148204939282805">攻擊者可能會試圖透過 <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="375403751935624634">伺服器錯誤,翻譯作業失敗。</translation>
<translation id="3759461132968374835">最近沒有收到當機資訊。當機回報功能停用時發生的當機不會列在這裡。</translation>
+<translation id="3765032636089507299">安全瀏覽網頁正在建構中。</translation>
<translation id="3778403066972421603">你要將這張信用卡的資訊儲存到你在這個裝置上的 Google 帳戶嗎?</translation>
<translation id="3783418713923659662">Mastercard</translation>
<translation id="3787705759683870569">到期日:<ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
@@ -385,8 +389,10 @@
<translation id="3886446263141354045">你想存取這個網站的要求已傳送給<ph name="NAME" /></translation>
<translation id="3890664840433101773">新增電子郵件地址</translation>
<translation id="3901925938762663762">這張信用卡已過期</translation>
+<translation id="3909695131102177774"><ph name="LABEL" />:<ph name="ERROR" /></translation>
<translation id="3945915738023014686">已上傳的當機報告 ID:<ph name="CRASH_ID" /> (本機當機 ID:<ph name="CRASH_LOCAL_ID" />)</translation>
<translation id="3949571496842715403">這個伺服器無法證明所屬網域為 <ph name="DOMAIN" />;其安全性憑證未指定主體別名。這可能是因為設定錯誤,或是有攻擊者攔截你的連線所致。</translation>
+<translation id="3949601375789751990">你的瀏覽紀錄會顯示在這裡</translation>
<translation id="3963721102035795474">閱讀器模式</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{無}=1{來自 1 個網站 }other{來自 # 個網站 }}</translation>
<translation id="397105322502079400">計算中…</translation>
@@ -415,6 +421,8 @@
<translation id="4165986682804962316">網站設定</translation>
<translation id="4169947484918424451">您希望 Chromium 儲存這張信用卡嗎?</translation>
<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="4196861286325780578">重做移動(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />檢查防火牆和防毒軟體設定<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">當機</translation>
@@ -438,12 +446,12 @@
<translation id="4356973930735388585">攻擊者可能會試圖透過這個網站在你的電腦上安裝危險程式,藉此竊取或刪除你的資訊 (例如相片、密碼、郵件和信用卡資料)。</translation>
<translation id="4372948949327679948">預期的「<ph name="VALUE_TYPE" />」值。</translation>
<translation id="4377125064752653719">你嘗試前往 <ph name="DOMAIN" />,但是發行者已撤銷伺服器提供的憑證。在這種情況下,請勿信任伺服器提供的安全性憑證,因為你的連線對象可能是攻擊者的電腦。</translation>
-<translation id="4381091992796011497">使用者名稱:</translation>
<translation id="4394049700291259645">停用</translation>
<translation id="4406896451731180161">搜尋結果</translation>
<translation id="4424024547088906515">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證未取得 Chrome 的信任。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。</translation>
<translation id="4432688616882109544"><ph name="HOST_NAME" /> 不接受你的登入憑證,或是你可能未提供登入憑證。</translation>
<translation id="443673843213245140">雖然已停用 Proxy,不過已指定明確 Proxy 設定。</translation>
+<translation id="445100540951337728">接受的簽帳金融卡</translation>
<translation id="4506176782989081258">驗證錯誤:<ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">與系統管理員聯絡</translation>
<translation id="450710068430902550">與管理員分享</translation>
@@ -455,12 +463,14 @@
<translation id="4587425331216688090">要從 Chrome 中移除地址嗎?</translation>
<translation id="4592951414987517459">您的 <ph name="DOMAIN" /> 連線使用新型加密套件進行加密。</translation>
<translation id="4594403342090139922">復原刪除(&amp;U)</translation>
+<translation id="4611292653554630842">登入</translation>
<translation id="4619615317237390068">在其他裝置上開啟的分頁</translation>
<translation id="4668929960204016307">,</translation>
<translation id="467662567472608290">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證含有錯誤。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。</translation>
<translation id="4690462567478992370">停止使用無效的憑證</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">您的連線已中斷</translation>
+<translation id="471880041731876836">你沒有這個網站的瀏覽權限</translation>
<translation id="4722547256916164131"><ph name="BEGIN_LINK" />執行 Windows 網路診斷<ph name="END_LINK" /></translation>
<translation id="4726672564094551039">重新載入政策</translation>
<translation id="4728558894243024398">平台</translation>
@@ -482,14 +492,15 @@
<translation id="4850886885716139402">檢視</translation>
<translation id="4854362297993841467">不支援所選的快遞方式,請改選其他方式。</translation>
<translation id="4858792381671956233">你已詢問家長是否同意你造訪這個網站</translation>
-<translation id="4863764087567530506">這項內容可能會試圖誘使你安裝軟體或提供個人資訊。<ph name="BEGIN_LINK" />仍要顯示<ph name="END_LINK" />。</translation>
<translation id="4880827082731008257">搜尋紀錄</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />、<ph name="TYPE_2" />,<ph name="TYPE_3" /></translation>
+<translation id="4913131542719409934">需要進行驗證</translation>
<translation id="4914479371620770914">{URL_count,plural, =1{還有另外 1 個網頁}other{還有另外 # 個網頁}}</translation>
<translation id="4916962322362512664"><ph name="DEVICE_NAME" /></translation>
<translation id="4923417429809017348">系統已將此網頁從不明語言翻譯成<ph name="LANGUAGE_LANGUAGE" /></translation>
<translation id="4923459931733593730">付款</translation>
<translation id="4926049483395192435">必須指定。</translation>
+<translation id="4926340098269537727"><ph name="ACTIVE_MATCH" />/<ph name="TOTAL_MATCHCOUNT" /></translation>
<translation id="495170559598752135">動作</translation>
<translation id="4958444002117714549">展開清單</translation>
<translation id="4974590756084640048">重新啟用警告功能</translation>
@@ -505,6 +516,7 @@
<translation id="5045550434625856497">密碼不正確</translation>
<translation id="5056549851600133418">為您推薦的文章</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />檢查 Proxy 位址<ph name="END_LINK" /></translation>
+<translation id="5086888986931078152">你可能無法再存取部分網站上的受保護內容。</translation>
<translation id="5087286274860437796">伺服器憑證目前無效。</translation>
<translation id="5087580092889165836">新增信用卡</translation>
<translation id="5089810972385038852">州</translation>
@@ -512,18 +524,27 @@
<translation id="5095208057601539847">省</translation>
<translation id="5115563688576182185">(64 位元)</translation>
<translation id="5141240743006678641">使用你的 Google 憑證對已同步處理的密碼進行加密</translation>
-<translation id="514421653919133810">以無痕模式開啟網頁 (Ctrl + Shift + N 鍵)</translation>
<translation id="5145883236150621069">政策回應中存在錯誤代碼</translation>
+<translation id="5159010409087891077">在新的無痕式視窗中開啟網頁 (⇧ + ⌘ + N 鍵)</translation>
<translation id="5171045022955879922">搜尋或輸入網址</translation>
<translation id="5172758083709347301">本機</translation>
<translation id="5179510805599951267">網頁內容不是<ph name="ORIGINAL_LANGUAGE" />嗎?請回報此錯誤</translation>
-<translation id="5181140330217080051">下載中</translation>
<translation id="5190835502935405962">書籤列</translation>
<translation id="5199729219167945352">實驗性功能</translation>
<translation id="5205222826937269299">請輸入名稱</translation>
<translation id="5222812217790122047">請輸入電子郵件地址</translation>
<translation id="5251803541071282808">雲端</translation>
<translation id="5277279256032773186">在工作環境使用 Chrome 嗎?企業可以管理員工的 Chrome 設定。瞭解詳情</translation>
+<translation id="5281113152797308730"><ph name="BEGIN_PARAGRAPH" />請按照以下步驟暫時停用該軟體,以便順利連上網路。你需要管理員權限。<ph name="END_PARAGRAPH" />
+
+ <ph name="BEGIN_LIST" />
+ <ph name="LIST_ITEM" />按一下 [開始]<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />,然後搜尋並選取「檢視本機服務」<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />選取 [VisualDiscovery]<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />在「啟動類型」<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />之下,選取 [已停用]<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />在「服務狀態」<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />之下,按一下 [停止]<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />依序按一下 [套用]<ph name="BEGIN_BOLD" /><ph name="END_BOLD" /> 和 [確定]<ph name="BEGIN_BOLD" /><ph name="END_BOLD" />
+ <ph name="LIST_ITEM" />前往 <ph name="BEGIN_LEARN_MORE_LINK" />Chrome 說明中心<ph name="END_LEARN_MORE_LINK" />瞭解如何將該軟體從你的電腦上永久移除
+ <ph name="END_LIST" /></translation>
<translation id="5297526204711817721">你與這個網站之間的連線不是私人連線。你隨時可以摘下頭戴式裝置,然後按下返回鍵退出 VR 模式。</translation>
<translation id="5299298092464848405">解析政策時發生錯誤</translation>
<translation id="5308689395849655368">當機報告功能已停用。</translation>
@@ -540,9 +561,10 @@
<translation id="5439770059721715174">「<ph name="ERROR_PATH" />」發生架構驗證錯誤:<ph name="ERROR" /></translation>
<translation id="5452270690849572955">找不到 <ph name="HOST_NAME" /> 網頁</translation>
<translation id="5455374756549232013">政策時間戳記有誤</translation>
-<translation id="5455790498993699893">第 <ph name="ACTIVE_MATCH" /> 個,共 <ph name="TOTAL_MATCHCOUNT" /> 個</translation>
<translation id="5457113250005438886">無效</translation>
+<translation id="5458150163479425638">{CONTACT,plural, =0{<ph name="CONTACT_PREVIEW" />}=1{「<ph name="CONTACT_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 項聯絡資訊}other{「<ph name="CONTACT_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 項聯絡資訊}}</translation>
<translation id="5470861586879999274">重做編輯(&amp;R)</translation>
+<translation id="5481076368049295676">這項內容可能會試圖在你的裝置上安裝危險軟體,藉此竊取或刪除你的資訊。<ph name="BEGIN_LINK" />仍要顯示<ph name="END_LINK" /></translation>
<translation id="54817484435770891">新增有效的地址</translation>
<translation id="5492298309214877701">這個位於公司、機構或學校內部網路的網站使用的網址與某個外部網站相同。
<ph name="LINE_BREAK" />
@@ -554,6 +576,7 @@
<translation id="5540224163453853">找不到要求的文章。</translation>
<translation id="5544037170328430102"><ph name="SITE" /> 的嵌入式網頁顯示:</translation>
<translation id="5556459405103347317">重新載入</translation>
+<translation id="5560088892362098740">到期日</translation>
<translation id="5565735124758917034">管理中</translation>
<translation id="5571083550517324815">無法在這個地址取件,請改用其他地址。</translation>
<translation id="5572851009514199876">請啟動 Chrome 並登入帳戶,Chrome 將確認你是否可存取這個網站。</translation>
@@ -568,12 +591,13 @@
<translation id="5629630648637658800">無法載入政策設定</translation>
<translation id="5631439013527180824">裝置管理符記無效</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="5646376287012673985">位置</translation>
<translation id="5659593005791499971">電子郵件</translation>
<translation id="5669703222995421982">取得個人化內容</translation>
<translation id="5675650730144413517">這個網頁無法正常運作</translation>
<translation id="5710435578057952990">此網頁的身分未經驗證。</translation>
-<translation id="5713016350996637505">已封鎖欺騙性內容</translation>
+<translation id="5719499550583120431">接受預付卡。</translation>
<translation id="5720705177508910913">目前使用者</translation>
<translation id="5732392974455271431">你的家長可以為你解除封鎖這個網站</translation>
<translation id="5763042198335101085">請輸入有效的電子郵件地址</translation>
@@ -585,15 +609,14 @@
<translation id="5803412860119678065">要填入你的 <ph name="CARD_DETAIL" /> 資訊嗎?</translation>
<translation id="5810442152076338065">您的 <ph name="DOMAIN" /> 連線使用過舊的加密套件進行加密。</translation>
<translation id="5813119285467412249">重做新增(&amp;R)</translation>
-<translation id="5814352347845180253">您可能無法再存取 <ph name="SITE" /> 和另一些網站的付費內容。</translation>
<translation id="5838278095973806738">請勿在這個網站上輸入任何機密資訊 (例如密碼或信用卡號碼),以免遭到攻擊者竊取。</translation>
<translation id="5869405914158311789">無法連上這個網站</translation>
<translation id="5869522115854928033">已儲存的密碼</translation>
<translation id="5872918882028971132">家長建議</translation>
+<translation id="5893752035575986141">接受簽帳金融卡。</translation>
<translation id="5901630391730855834">黃色</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (已同步)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{目前使用 1 個 Cookie}other{目前使用 # 個 Cookie}}</translation>
-<translation id="5926846154125914413">您可能無法再存取部分網站的付費內容。</translation>
<translation id="5959728338436674663">自動傳送部分<ph name="BEGIN_WHITEPAPER_LINK" />系統資訊和網頁內容<ph name="END_WHITEPAPER_LINK" />給 Google,協助偵測危險的應用程式和網站。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967867314010545767">從紀錄中移除</translation>
<translation id="5975083100439434680">縮小</translation>
@@ -609,6 +632,7 @@
<translation id="6040143037577758943">關閉</translation>
<translation id="6042308850641462728">更多</translation>
<translation id="6047233362582046994">如果你瞭解安全性風險,也可以選擇在有害應用程式尚未遭到移除的狀態下<ph name="BEGIN_LINK" />造訪這個網站<ph name="END_LINK" />。</translation>
+<translation id="6047927260846328439">這項內容可能會試圖誘使你安裝軟體或提供個人資訊。<ph name="BEGIN_LINK" />仍要顯示<ph name="END_LINK" /></translation>
<translation id="6051221802930200923">目前無法造訪 <ph name="SITE" />,因為這個網站使用憑證鎖定功能。網路錯誤和攻擊行為通常是暫時性的,因此這個網頁可能稍後就會恢復正常狀態。</translation>
<translation id="6060685159320643512">請注意,這些實驗性功能可能對電腦有害</translation>
<translation id="6080696365213338172">你使用了管理員提供的憑證存取內容,因此管理員可攔截你傳送至「<ph name="DOMAIN" />」的資料。</translation>
@@ -622,7 +646,6 @@
<translation id="6165508094623778733">瞭解詳情</translation>
<translation id="6169916984152623906">現在,你可以進行私密瀏覽了。共用這部裝置的其他使用者不會看到你的活動,不過,你下載的內容和新增的書籤仍會保留在裝置上。</translation>
<translation id="6177128806592000436">你與這個網站的連線不安全</translation>
-<translation id="6184817833369986695">(發佈版本:<ph name="UPDATE_COHORT_NAME" />)</translation>
<translation id="6203231073485539293">檢查網際網路連線</translation>
<translation id="6218753634732582820">要從 Chromium 中移除地址嗎?</translation>
<translation id="6221345481584921695">Google 安全瀏覽功能最近在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />偵測到惡意軟體<ph name="END_LINK" />。即使是平常可以安全使用的網站,有時也會遭到惡意軟體感染。這些惡意內容來自已知的惡意軟體散佈網站 <ph name="SUBRESOURCE_HOST" />。</translation>
@@ -641,13 +664,14 @@
<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>
<translation id="6387478394221739770">想搶先試用酷炫的 Chrome 新功能嗎?請前往 chrome.com/beta 安裝測試版。</translation>
-<translation id="6389758589412724634">Chromium 嘗試顯示這個網頁時用盡了記憶體。</translation>
+<translation id="6397451950548600259">你的電腦上有軟體阻止 Chrome 建立安全的網路連線</translation>
<translation id="6404511346730675251">編輯書籤</translation>
<translation id="6410264514553301377">輸入 <ph name="CREDIT_CARD" /> 的到期日和信用卡安全碼</translation>
<translation id="6414888972213066896">你已詢問家長是否同意你造訪這個網站</translation>
@@ -662,6 +686,7 @@
<translation id="647261751007945333">裝置政策</translation>
<translation id="6477321094435799029">Chrome 在這個網頁上偵測到異常代碼。為了保護你的個人資訊 (包括密碼、電話號碼或信用卡資料),Chrome 已封鎖這個網頁。</translation>
<translation id="6489534406876378309">開始上傳當機報告</translation>
+<translation id="6507833130742554667">接受信用卡和簽帳金融卡。</translation>
<translation id="6508722015517270189">重新啟動 Chrome</translation>
<translation id="6529602333819889595">重做刪除(&amp;R)</translation>
<translation id="6534179046333460208">實體化網路建議</translation>
@@ -670,13 +695,13 @@
<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="657639383826808334">這項內容可能會試圖在你的裝置上安裝不安全的軟體,藉此竊取或刪除你的資訊。<ph name="BEGIN_LINK" />仍要顯示<ph name="END_LINK" />。</translation>
<translation id="6596325263575161958">加密選項</translation>
<translation id="662080504995468778">不離開</translation>
<translation id="6626291197371920147">新增有效的信用卡號碼</translation>
<translation id="6628463337424475685"><ph name="ENGINE" /> 搜尋</translation>
<translation id="6630809736994426279">攻擊者目前可能會試圖透過 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在你的 Mac 上安裝危險程式,藉此竊取或刪除你的資訊 (例如相片、密碼、郵件和信用卡資料)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6644283850729428850">這項政策已遭取代。</translation>
+<translation id="6657585470893396449">密碼</translation>
<translation id="6671697161687535275">要從 Chromium 中移除表單填寫建議嗎?</translation>
<translation id="6685834062052613830">請登出並完成設定程序</translation>
<translation id="6710213216561001401">返回</translation>
@@ -707,7 +732,6 @@
<translation id="6970216967273061347">區</translation>
<translation id="6973656660372572881">已指定固定的 Proxy 伺服器和 .pac 指令碼網址。</translation>
<translation id="6989763994942163495">顯示進階設定...</translation>
-<translation id="7000990526846637657">找不到任何紀錄項目</translation>
<translation id="7012363358306927923">中國銀聯</translation>
<translation id="7012372675181957985">你仍可前往 <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> 存取 Google 帳戶中保存的各種瀏覽紀錄</translation>
<translation id="7029809446516969842">密碼</translation>
@@ -722,7 +746,9 @@
<translation id="7129409597930077180">無法運送到這個地址,請改用其他地址。</translation>
<translation id="7138472120740807366">快遞方式</translation>
<translation id="7139724024395191329">大公國</translation>
+<translation id="7153549335910886479">{PAYMENT_METHOD,plural, =0{<ph name="PAYMENT_METHOD_PREVIEW" />}=1{<ph name="PAYMENT_METHOD_PREVIEW" /> 和另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 種付款方式}other{<ph name="PAYMENT_METHOD_PREVIEW" /> 和另外 <ph name="NUMBER_OF_ADDITIONAL_PAYMENT_METHODS" /> 種付款方式}}</translation>
<translation id="7155487117670177674">付款行為不安全</translation>
+<translation id="7175401108899573750">{SHIPPING_OPTIONS,plural, =0{<ph name="SHIPPING_OPTION_PREVIEW" />}=1{<ph name="SHIPPING_OPTION_PREVIEW" />和另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 種選項}other{<ph name="SHIPPING_OPTION_PREVIEW" />和另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 種選項}}</translation>
<translation id="7179921470347911571">立即重新啟動</translation>
<translation id="7180611975245234373">重新整理</translation>
<translation id="7182878459783632708">沒有設定任何政策</translation>
@@ -763,6 +789,7 @@
<translation id="7514365320538308">下載</translation>
<translation id="7518003948725431193">找不到此網址的網頁:<ph name="URL" /></translation>
<translation id="7521387064766892559">JavaScript</translation>
+<translation id="7526934274050461096">你與這個網站之間的連線不是私人連線</translation>
<translation id="7535087603100972091">值</translation>
<translation id="7537536606612762813">強制</translation>
<translation id="7542403920425041731">經過你確認後,這個網站就會取得你的信用卡詳細資料。</translation>
@@ -772,7 +799,6 @@
<translation id="7552846755917812628">嘗試按照下列提示操作:</translation>
<translation id="7554791636758816595">新增分頁</translation>
<translation id="7567204685887185387">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證是以欺詐方式發行。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。</translation>
-<translation id="7567794288553138946">{CONTACT,plural, =1{「<ph name="CONTACT_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 項聯絡資訊}other{「<ph name="CONTACT_PREVIEW" />」和另外 <ph name="NUMBER_OF_ADDITIONAL_CONTACTS" /> 項聯絡資訊}}</translation>
<translation id="7568593326407688803">此網頁為<ph name="ORIGINAL_LANGUAGE" />你要翻譯網頁內容嗎?</translation>
<translation id="7569952961197462199">要從 Chrome 中移除信用卡嗎?</translation>
<translation id="7569983096843329377">黑色</translation>
@@ -799,9 +825,11 @@
<translation id="7714464543167945231">憑證</translation>
<translation id="7716147886133743102">依據管理員的設定封鎖</translation>
<translation id="7716424297397655342">無法從快取載入這個網站</translation>
+<translation id="774634243536837715">已封鎖危險內容。</translation>
<translation id="7752995774971033316">未管理</translation>
<translation id="7755287808199759310">你的家長可以為你解除封鎖這個網站</translation>
<translation id="7758069387465995638">防火牆或防毒軟體可能封鎖了連線。</translation>
+<translation id="7759163816903619567">顯示網域:</translation>
<translation id="7761701407923456692">伺服器憑證與網址不符。</translation>
<translation id="7763386264682878361">付款資訊清單剖析器</translation>
<translation id="7764225426217299476">新增地址</translation>
@@ -816,6 +844,7 @@
<translation id="7815407501681723534">找到 <ph name="NUMBER_OF_RESULTS" /> 個與「<ph name="SEARCH_STRING" />」相符的<ph name="SEARCH_RESULTS" /></translation>
<translation id="785549533363645510">不過,這並不意味著您可以完全隱形。使用無痕模式時,您的雇主和網際網路服務供應商仍然可以追蹤您的瀏覽紀錄,您所造訪的網站也可能會記錄您的瀏覽行為。</translation>
<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="7878176543348854470">接受簽帳金融卡和預付卡。</translation>
<translation id="7887683347370398519">請檢查您的 CVC,然後再試一次</translation>
<translation id="79338296614623784">請輸入有效的電話號碼</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
@@ -835,7 +864,6 @@
<translation id="8041089156583427627">提供意見</translation>
<translation id="8041940743680923270">使用全域預設值 (要求確認)</translation>
<translation id="8088680233425245692">無法查看文章。</translation>
-<translation id="8089520772729574115">不到 1 MB</translation>
<translation id="8091372947890762290">尚未在伺服器上啟動</translation>
<translation id="8118489163946903409">付款方式</translation>
<translation id="8131740175452115882">確認</translation>
@@ -847,10 +875,13 @@
<translation id="8194797478851900357">復原移動(&amp;U)</translation>
<translation id="8201077131113104583">擴充功能 (ID:「<ph name="EXTENSION_ID" />」) 的更新網址無效。</translation>
<translation id="8202097416529803614">訂單摘要</translation>
+<translation id="8205463626947051446">網站經常顯示侵入式廣告</translation>
<translation id="8218327578424803826">指派的位置:</translation>
<translation id="8225771182978767009">設定這部電腦的使用者選擇封鎖這個網站。</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />、<ph name="TYPE_2" /></translation>
+<translation id="8238581221633243064">在新的無痕式分頁中開啟網頁</translation>
<translation id="8241707690549784388">你尋找的網頁使用了你輸入的資料。返回該頁會重複你剛才的行動。你確定要繼續嗎?</translation>
+<translation id="8241712895048303527">禁止這個網站顯示廣告</translation>
<translation id="8249320324621329438">上次擷取時間:</translation>
<translation id="8253091569723639551">請提供帳單地址</translation>
<translation id="8261506727792406068">刪除</translation>
@@ -861,7 +892,6 @@
<translation id="8308427013383895095">網路連線發生問題,翻譯作業失敗。</translation>
<translation id="8332188693563227489">存取 <ph name="HOST_NAME" /> 的要求遭到拒絕</translation>
<translation id="834457929814110454">如果你瞭解安全性風險,也可以選擇在有害程式尚未遭到移除的狀態下<ph name="BEGIN_LINK" />造訪這個網站<ph name="END_LINK" />。</translation>
-<translation id="8344669043927012510">以無痕模式開啟網頁 (⇧ + ⌘ + N 鍵)</translation>
<translation id="8349305172487531364">書籤列</translation>
<translation id="8363502534493474904">關閉飛航模式</translation>
<translation id="8364627913115013041">未設定。</translation>
@@ -871,6 +901,7 @@
<translation id="8398259832188219207">當機報告上傳時間:<ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">當機次數 (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">你必須輸入兩次相同的通關密語。</translation>
+<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">設定</translation>
<translation id="8433057134996913067">您會因此登出大多數網站。</translation>
<translation id="8437238597147034694">復原移動(&amp;U)</translation>
@@ -878,14 +909,16 @@
<translation id="8483780878231876732">如要使用您的 Google 帳戶中的信用卡,請登入 Chrome</translation>
<translation id="8488350697529856933">適用對象</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> 的回應時間過長。</translation>
-<translation id="8532105204136943229">到期年份</translation>
+<translation id="8503813439785031346">使用者名稱</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="8571890674111243710">正在將網頁翻譯成<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">新增電話號碼</translation>
<translation id="859285277496340001">憑證未指定負責檢查其本身是否已遭到撤銷的機制。</translation>
<translation id="8620436878122366504">你的家長尚未核准這個網站</translation>
<translation id="8647750283161643317">全部重設為預設值</translation>
+<translation id="8660471606262461360">來自 Google Payments</translation>
<translation id="8703575177326907206">你到 <ph name="DOMAIN" /> 的連線未加密。</translation>
<translation id="8718314106902482036">未完成付款程序</translation>
<translation id="8725066075913043281">再試一次</translation>
@@ -913,6 +946,7 @@
<translation id="8903921497873541725">放大</translation>
<translation id="8931333241327730545">您要將這張卡片的資訊儲存到您的 Google 帳戶嗎?</translation>
<translation id="8932102934695377596">你的時鐘時間過慢</translation>
+<translation id="8938939909778640821">接受的信用卡和預付卡</translation>
<translation id="8971063699422889582">伺服器憑證已過期。</translation>
<translation id="8986494364107987395">自動傳送使用統計資料及當機報告給 Google</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
@@ -942,7 +976,6 @@
<translation id="9169664750068251925">永遠禁止在這個網站執行</translation>
<translation id="9170848237812810038">取消(&amp;U)</translation>
<translation id="917450738466192189">伺服器憑證無效。</translation>
-<translation id="9180730219696997905">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" />和另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 種選項}other{<ph name="SHIPPING_OPTION_PREVIEW" />和另外 <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> 種選項}}</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> 使用了不支援的通訊協定。</translation>
<translation id="9205078245616868884">您已使用同步通關密語對資料進行加密,請輸入通關密語開始進行同步。</translation>
<translation id="9207861905230894330">無法新增文章。</translation>
@@ -951,9 +984,9 @@
<translation id="933712198907837967">Diners Club</translation>
<translation id="935608979562296692">清除表單</translation>
<translation id="939736085109172342">新增資料夾</translation>
-<translation id="941721044073577244">你沒有這個網站的瀏覽權限</translation>
<translation id="969892804517981540">正式版本</translation>
<translation id="975560348586398090">{COUNT,plural, =0{無}=1{1 個項目}other{# 個項目}}</translation>
+<translation id="981121421437150478">離線</translation>
<translation id="988159990683914416">開發人員版本</translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/subresource_filter/DEPS b/chromium/components/subresource_filter/DEPS
index 39b36595011..1194dc13f3d 100644
--- a/chromium/components/subresource_filter/DEPS
+++ b/chromium/components/subresource_filter/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/prefs",
+ "+components/url_pattern_index",
"+components/variations",
# subresource_filter is a layered component; subdirectories must explicitly
# introduce the ability to use non-core layers as appropriate.
diff --git a/chromium/components/subresource_filter/content/browser/BUILD.gn b/chromium/components/subresource_filter/content/browser/BUILD.gn
index dd59a011c22..c498959f056 100644
--- a/chromium/components/subresource_filter/content/browser/BUILD.gn
+++ b/chromium/components/subresource_filter/content/browser/BUILD.gn
@@ -37,7 +37,6 @@ static_library("browser") {
"//base",
"//components/safe_browsing_db:database_manager",
"//components/safe_browsing_db:util",
- "//components/safe_browsing_db:v4_local_database_manager",
"//components/subresource_filter/content/common",
"//components/subresource_filter/core/browser",
"//components/subresource_filter/core/common",
@@ -59,6 +58,8 @@ static_library("test_support") {
"async_document_subresource_filter_test_utils.h",
"fake_safe_browsing_database_manager.cc",
"fake_safe_browsing_database_manager.h",
+ "subresource_filter_observer_test_utils.cc",
+ "subresource_filter_observer_test_utils.h",
]
deps = [
":browser",
diff --git a/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc b/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
index 01c8cb75e45..e8a80a7dd8b 100644
--- a/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
+++ b/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
@@ -52,7 +52,10 @@ ActivationStateComputingNavigationThrottle::
weak_ptr_factory_(this) {}
ActivationStateComputingNavigationThrottle::
- ~ActivationStateComputingNavigationThrottle() {}
+ ~ActivationStateComputingNavigationThrottle() {
+ if (!destruction_closure_.is_null())
+ std::move(destruction_closure_).Run();
+}
void ActivationStateComputingNavigationThrottle::
NotifyPageActivationWithRuleset(
@@ -117,20 +120,32 @@ void ActivationStateComputingNavigationThrottle::OnActivationStateComputed(
delay, base::TimeDelta::FromMicroseconds(1),
base::TimeDelta::FromSeconds(10), 50);
}
- navigation_handle()->Resume();
+ Resume();
+}
+
+AsyncDocumentSubresourceFilter*
+ActivationStateComputingNavigationThrottle::filter() const {
+ // TODO(csharrison): This should not really be necessary, as we should be
+ // delaying the navigation until the filter has computed an activation state.
+ // See crbug.com/736249. In the mean time, have a check here to avoid
+ // returning a filter in an invalid state.
+ if (async_filter_ && async_filter_->has_activation_state())
+ return async_filter_.get();
+ return nullptr;
}
// Ensure the caller cannot take ownership of a subresource filter for cases
// when activation IPCs are not sent to the render process.
std::unique_ptr<AsyncDocumentSubresourceFilter>
ActivationStateComputingNavigationThrottle::ReleaseFilter() {
- return will_send_activation_to_renderer_ ? std::move(async_filter_) : nullptr;
+ return could_send_activation_to_renderer_ ? std::move(async_filter_)
+ : nullptr;
}
void ActivationStateComputingNavigationThrottle::
- WillSendActivationToRenderer() {
+ CouldSendActivationToRenderer() {
DCHECK(async_filter_);
- will_send_activation_to_renderer_ = true;
+ could_send_activation_to_renderer_ = true;
}
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h b/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
index 2ca8b3ef33e..16d67ea012e 100644
--- a/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
+++ b/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
@@ -53,6 +53,10 @@ class ActivationStateComputingNavigationThrottle
VerifiedRuleset::Handle* ruleset_handle,
const ActivationState& page_activation_state);
+ void set_destruction_closure(base::OnceClosure closure) {
+ destruction_closure_ = std::move(closure);
+ }
+
// content::NavigationThrottle:
content::NavigationThrottle::ThrottleCheckResult WillProcessResponse()
override;
@@ -65,16 +69,12 @@ class ActivationStateComputingNavigationThrottle
// frame.
std::unique_ptr<AsyncDocumentSubresourceFilter> ReleaseFilter();
- AsyncDocumentSubresourceFilter* filter() { return async_filter_.get(); }
+ AsyncDocumentSubresourceFilter* filter() const;
- void WillSendActivationToRenderer();
+ void CouldSendActivationToRenderer();
private:
void OnActivationStateComputed(ActivationState state);
- void set_filter(
- std::unique_ptr<AsyncDocumentSubresourceFilter> async_filter) {
- async_filter_ = std::move(async_filter);
- }
ActivationStateComputingNavigationThrottle(
content::NavigationHandle* navigation_handle,
@@ -92,11 +92,15 @@ class ActivationStateComputingNavigationThrottle
base::TimeTicks defer_timestamp_;
- // Becomes true when the throttle manager reaches ReadyToCommitNavigation and
- // sends an activation IPC to the render process. Makes sure a caller cannot
- // take ownership of the subresource filter unless an activation IPC is sent
- // to the renderer.
- bool will_send_activation_to_renderer_ = false;
+ // Callback to be run in the destructor.
+ base::OnceClosure destruction_closure_;
+
+ // Can become true when the throttle manager reaches ReadyToCommitNavigation.
+ // Makes sure a caller cannot take ownership of the subresource filter unless
+ // the throttle has reached this point. After this point the throttle manager
+ // can send an activation IPC to the render process. Note that an IPC is not
+ // always sent in case this activation is ignoring ruleset rules.
+ bool could_send_activation_to_renderer_ = false;
base::WeakPtrFactory<ActivationStateComputingNavigationThrottle>
weak_ptr_factory_;
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 9b06df81a45..b49ad49dd98 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
@@ -20,9 +20,9 @@
#include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.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/proto/rules.pb.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/navigation_simulator.h"
@@ -31,6 +31,8 @@
namespace subresource_filter {
+namespace proto = url_pattern_index::proto;
+
class ActivationStateComputingNavigationThrottleTest
: public content::RenderViewHostTestHarness,
public content::WebContentsObserver {
@@ -174,7 +176,7 @@ class ActivationStateComputingNavigationThrottleTest
return;
ASSERT_EQ(navigation_handle, test_throttle_->navigation_handle());
if (test_throttle_->filter())
- test_throttle_->WillSendActivationToRenderer();
+ test_throttle_->CouldSendActivationToRenderer();
if (auto filter = test_throttle_->ReleaseFilter()) {
EXPECT_NE(ActivationLevel::DISABLED,
diff --git a/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.cc b/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.cc
index 3d804da57d5..c91f0b90154 100644
--- a/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.cc
+++ b/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.cc
@@ -13,9 +13,47 @@
#include "base/task_runner_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
namespace subresource_filter {
+ActivationState ComputeActivationState(
+ const GURL& document_url,
+ const url::Origin& parent_document_origin,
+ const ActivationState& parent_activation_state,
+ const MemoryMappedRuleset* ruleset) {
+ DCHECK(ruleset);
+ SCOPED_UMA_HISTOGRAM_MICRO_TIMER(
+ "SubresourceFilter.DocumentLoad.Activation.WallDuration");
+ SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(
+ "SubresourceFilter.DocumentLoad.Activation.CPUDuration");
+ if (parent_document_origin.unique()) {
+ SCOPED_UMA_HISTOGRAM_MICRO_TIMER(
+ "SubresourceFilter.PageLoad.Activation.WallDuration");
+ SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(
+ "SubresourceFilter.PageLoad.Activation.CPUDuration");
+ }
+
+ IndexedRulesetMatcher matcher(ruleset->data(), ruleset->length());
+ ActivationState activation_state = parent_activation_state;
+ if (activation_state.filtering_disabled_for_document)
+ return activation_state;
+
+ // TODO(pkalinnikov): Match several activation types in a batch.
+ if (matcher.ShouldDisableFilteringForDocument(
+ document_url, parent_document_origin,
+ url_pattern_index::proto::ACTIVATION_TYPE_DOCUMENT)) {
+ activation_state.filtering_disabled_for_document = true;
+ } else if (!activation_state.generic_blocking_rules_disabled &&
+ matcher.ShouldDisableFilteringForDocument(
+ document_url, parent_document_origin,
+ url_pattern_index::proto::ACTIVATION_TYPE_GENERICBLOCK)) {
+ activation_state.generic_blocking_rules_disabled = true;
+ }
+ return activation_state;
+}
+
// AsyncDocumentSubresourceFilter::InitializationParams ------------------------
using InitializationParams =
@@ -99,8 +137,9 @@ void AsyncDocumentSubresourceFilter::GetLoadPolicyForSubdocument(
DCHECK(core);
DocumentSubresourceFilter* filter = core->filter();
return filter
- ? filter->GetLoadPolicy(subdocument_url,
- proto::ELEMENT_TYPE_SUBDOCUMENT)
+ ? filter->GetLoadPolicy(
+ subdocument_url,
+ url_pattern_index::proto::ELEMENT_TYPE_SUBDOCUMENT)
: LoadPolicy::ALLOW;
},
core_.get(), subdocument_url),
@@ -112,6 +151,12 @@ void AsyncDocumentSubresourceFilter::ReportDisallowedLoad() {
std::move(first_disallowed_load_callback_).Run();
}
+const ActivationState& AsyncDocumentSubresourceFilter::activation_state()
+ const {
+ CHECK(activation_state_);
+ return activation_state_.value();
+}
+
// AsyncDocumentSubresourceFilter::Core ----------------------------------------
AsyncDocumentSubresourceFilter::Core::Core() {
diff --git a/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.h b/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.h
index b96e827d398..1c3b511d1f2 100644
--- a/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.h
+++ b/chromium/components/subresource_filter/content/browser/async_document_subresource_filter.h
@@ -16,11 +16,24 @@
#include "components/subresource_filter/core/common/activation_level.h"
#include "components/subresource_filter/core/common/activation_state.h"
#include "components/subresource_filter/core/common/document_subresource_filter.h"
+#include "components/subresource_filter/core/common/load_policy.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace subresource_filter {
+class MemoryMappedRuleset;
+
+// Computes whether/how subresource filtering should be activated while loading
+// |document_url| in a frame, based on the parent document's |activation_state|,
+// the |parent_document_origin|, as well as any applicable deactivation rules in
+// non-null |ruleset|.
+ActivationState ComputeActivationState(
+ const GURL& document_url,
+ const url::Origin& parent_document_origin,
+ const ActivationState& parent_activation_state,
+ const MemoryMappedRuleset* ruleset);
+
// An asynchronous wrapper around DocumentSubresourceFilter (DSF).
//
// It is accessed on the UI thread and owns a DSF living on a dedicated
@@ -101,9 +114,9 @@ class AsyncDocumentSubresourceFilter {
void ReportDisallowedLoad();
// Must be called after activation state computation is finished.
- const ActivationState& activation_state() const {
- return activation_state_.value();
- }
+ const ActivationState& activation_state() const;
+
+ bool has_activation_state() const { return activation_state_.has_value(); }
// The |first_disallowed_load_callback|, if it is non-null, is invoked on the
// first ReportDisallowedLoad() call.
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 88a08629c34..6b9282d9980 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
@@ -16,13 +16,17 @@
#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_test_utils.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
+#include "components/subresource_filter/core/common/load_policy.h"
+#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace subresource_filter {
+namespace proto = url_pattern_index::proto;
+
class AsyncDocumentSubresourceFilterTest : public ::testing::Test {
public:
AsyncDocumentSubresourceFilterTest() = default;
@@ -241,4 +245,117 @@ TEST_F(AsyncDocumentSubresourceFilterTest, FirstDisallowedLoadIsReported) {
RunUntilIdle();
}
+// Tests for ComputeActivationState:
+
+class SubresourceFilterComputeActivationStateTest : public ::testing::Test {
+ public:
+ SubresourceFilterComputeActivationStateTest() {}
+
+ protected:
+ void SetUp() override {
+ constexpr int32_t kDocument = proto::ACTIVATION_TYPE_DOCUMENT;
+ constexpr int32_t kGenericBlock = proto::ACTIVATION_TYPE_GENERICBLOCK;
+
+ std::vector<proto::UrlRule> rules;
+ rules.push_back(testing::CreateWhitelistRuleForDocument(
+ "child1.com", kDocument, {"parent1.com", "parent2.com"}));
+ rules.push_back(testing::CreateWhitelistRuleForDocument(
+ "child2.com", kGenericBlock, {"parent1.com", "parent2.com"}));
+ rules.push_back(testing::CreateWhitelistRuleForDocument(
+ "child3.com", kDocument | kGenericBlock,
+ {"parent1.com", "parent2.com"}));
+
+ testing::TestRulesetPair test_ruleset_pair;
+ ASSERT_NO_FATAL_FAILURE(test_ruleset_creator_.CreateRulesetWithRules(
+ rules, &test_ruleset_pair));
+ ruleset_ = new MemoryMappedRuleset(
+ testing::TestRuleset::Open(test_ruleset_pair.indexed));
+ }
+
+ static ActivationState MakeState(
+ bool filtering_disabled_for_document,
+ bool generic_blocking_rules_disabled = false,
+ ActivationLevel activation_level = ActivationLevel::ENABLED) {
+ ActivationState activation_state(activation_level);
+ activation_state.filtering_disabled_for_document =
+ filtering_disabled_for_document;
+ activation_state.generic_blocking_rules_disabled =
+ generic_blocking_rules_disabled;
+ return activation_state;
+ }
+
+ const MemoryMappedRuleset* ruleset() { return ruleset_.get(); }
+
+ private:
+ testing::TestRulesetCreator test_ruleset_creator_;
+ scoped_refptr<const MemoryMappedRuleset> ruleset_;
+
+ DISALLOW_COPY_AND_ASSIGN(SubresourceFilterComputeActivationStateTest);
+};
+
+TEST_F(SubresourceFilterComputeActivationStateTest,
+ ActivationBitsCorrectlyPropagateToChildDocument) {
+ // Make sure that the |generic_blocking_rules_disabled| flag is disregarded
+ // when |filtering_disabled_for_document| is true.
+ ASSERT_EQ(MakeState(true, false), MakeState(true, true));
+
+ // TODO(pkalinnikov): Find a short way to express all these tests.
+ const struct {
+ const char* document_url;
+ const char* parent_document_origin;
+ ActivationState parent_activation;
+ ActivationState expected_activation_state;
+ } kTestCases[] = {
+ {"http://example.com", "http://example.com", MakeState(false, false),
+ MakeState(false, false)},
+ {"http://example.com", "http://example.com", MakeState(false, true),
+ MakeState(false, true)},
+ {"http://example.com", "http://example.com", MakeState(true, false),
+ MakeState(true)},
+ {"http://example.com", "http://example.com", MakeState(true, true),
+ MakeState(true)},
+
+ {"http://child1.com", "http://parrrrent1.com", MakeState(false, false),
+ MakeState(false, false)},
+ {"http://child1.com", "http://parent1.com", MakeState(false, false),
+ MakeState(true, false)},
+ {"http://child1.com", "http://parent2.com", MakeState(false, false),
+ MakeState(true, false)},
+ {"http://child1.com", "http://parent2.com", MakeState(true, false),
+ MakeState(true)},
+ {"http://child1.com", "http://parent2.com", MakeState(false, true),
+ MakeState(true)},
+
+ {"http://child2.com", "http://parent1.com", MakeState(false, false),
+ MakeState(false, true)},
+ {"http://child2.com", "http://parent1.com", MakeState(false, true),
+ MakeState(false, true)},
+ {"http://child2.com", "http://parent1.com", MakeState(true, false),
+ MakeState(true)},
+ {"http://child2.com", "http://parent1.com", MakeState(true, true),
+ MakeState(true)},
+
+ {"http://child3.com", "http://parent1.com", MakeState(false, false),
+ MakeState(true)},
+ {"http://child3.com", "http://parent1.com", MakeState(false, true),
+ MakeState(true)},
+ {"http://child3.com", "http://parent1.com", MakeState(true, false),
+ MakeState(true)},
+ {"http://child3.com", "http://parent1.com", MakeState(true, true),
+ MakeState(true)},
+ };
+
+ for (size_t i = 0, size = arraysize(kTestCases); i != size; ++i) {
+ SCOPED_TRACE(::testing::Message() << "Test number: " << i);
+ const auto& test_case = kTestCases[i];
+
+ GURL document_url(test_case.document_url);
+ url::Origin parent_document_origin(GURL(test_case.parent_document_origin));
+ ActivationState activation_state =
+ ComputeActivationState(document_url, parent_document_origin,
+ test_case.parent_activation, ruleset());
+ EXPECT_EQ(test_case.expected_activation_state, activation_state);
+ }
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/browser/content_ruleset_service.cc b/chromium/components/subresource_filter/content/browser/content_ruleset_service.cc
index 67dbd914b23..f6b9cca0b56 100644
--- a/chromium/components/subresource_filter/content/browser/content_ruleset_service.cc
+++ b/chromium/components/subresource_filter/content/browser/content_ruleset_service.cc
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/sequenced_task_runner.h"
+#include "base/task_scheduler/post_task.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/core/browser/ruleset_service.h"
#include "content/public/browser/browser_thread.h"
@@ -39,8 +40,9 @@ void CloseFile(base::File) {}
void CloseFileOnFileThread(base::File* file) {
if (!file->IsValid())
return;
- content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
- base::Bind(&CloseFile, base::Passed(file)));
+ base::PostTaskWithTraits(FROM_HERE,
+ {base::TaskPriority::BACKGROUND, base::MayBlock()},
+ base::Bind(&CloseFile, base::Passed(file)));
}
} // namespace
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
index cff5203f629..01d474160f0 100644
--- 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
@@ -73,12 +73,10 @@ void ContentSubresourceFilterDriverFactory::NotifyPageActivationComputed(
activation_decision_ = activation_decision;
activation_options_ = matched_options;
DCHECK_NE(activation_decision_, ActivationDecision::UNKNOWN);
- if (activation_decision_ != ActivationDecision::ACTIVATED) {
- DCHECK_EQ(activation_options_.activation_level, ActivationLevel::DISABLED);
- return;
- }
- DCHECK_NE(activation_options_.activation_level, ActivationLevel::DISABLED);
+ // ACTIVATION_DISABLED implies DISABLED activation level.
+ DCHECK(activation_decision_ != ActivationDecision::ACTIVATION_DISABLED ||
+ activation_options_.activation_level == ActivationLevel::DISABLED);
ActivationState state = ActivationState(activation_options_.activation_level);
state.measure_performance = ShouldMeasurePerformanceForPageLoad(
activation_options_.performance_measurement_rate);
@@ -103,6 +101,14 @@ void ContentSubresourceFilterDriverFactory::OnFirstSubresourceLoadDisallowed() {
ActivationLevel::ENABLED);
}
+bool ContentSubresourceFilterDriverFactory::AllowStrongPopupBlocking() {
+ return activation_options_.should_strengthen_popup_blocker;
+}
+
+bool ContentSubresourceFilterDriverFactory::AllowRulesetRules() {
+ return !activation_options_.should_disable_ruleset_rules;
+}
+
void ContentSubresourceFilterDriverFactory::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (navigation_handle->IsInMainFrame() &&
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
index bbb2d6b2b53..b63e80afac1 100644
--- 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
@@ -69,6 +69,8 @@ class ContentSubresourceFilterDriverFactory
// ContentSubresourceFilterThrottleManager::Delegate:
void OnFirstSubresourceLoadDisallowed() override;
+ bool AllowStrongPopupBlocking() override;
+ bool AllowRulesetRules() override;
ContentSubresourceFilterThrottleManager* throttle_manager() {
return throttle_manager_.get();
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 bf71456cf28..8311ba76a6c 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
@@ -9,6 +9,8 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h"
@@ -20,6 +22,7 @@
#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.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"
@@ -27,6 +30,15 @@
namespace subresource_filter {
+bool ContentSubresourceFilterThrottleManager::Delegate::
+ AllowStrongPopupBlocking() {
+ return false;
+}
+
+bool ContentSubresourceFilterThrottleManager::Delegate::AllowRulesetRules() {
+ return true;
+}
+
ContentSubresourceFilterThrottleManager::
ContentSubresourceFilterThrottleManager(
Delegate* delegate,
@@ -62,29 +74,45 @@ void ContentSubresourceFilterThrottleManager::RenderFrameDeleted(
// of subframe navigations.
void ContentSubresourceFilterThrottleManager::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
- auto throttle = ongoing_activation_throttles_.find(navigation_handle);
- if (throttle == ongoing_activation_throttles_.end())
+ if (navigation_handle->GetNetErrorCode() != net::OK)
+ return;
+
+ auto it = ongoing_activation_throttles_.find(navigation_handle);
+ if (it == ongoing_activation_throttles_.end())
+ return;
+
+ // TODO(crbug.com/736249): Remove CHECKs in this file when the root cause of
+ // the crash is found.
+ ActivationStateComputingNavigationThrottle* throttle = it->second;
+ CHECK_EQ(navigation_handle, throttle->navigation_handle());
+
+ // Main frame throttles with disabled page-level activation will not have
+ // associated filters.
+ AsyncDocumentSubresourceFilter* filter = throttle->filter();
+ if (!filter)
return;
// A filter with DISABLED activation indicates a corrupted ruleset.
- AsyncDocumentSubresourceFilter* filter = throttle->second->filter();
- if (!filter || navigation_handle->GetNetErrorCode() != net::OK ||
- filter->activation_state().activation_level ==
- ActivationLevel::DISABLED) {
+ ActivationLevel level = filter->activation_state().activation_level;
+ if (level == ActivationLevel::DISABLED)
return;
- }
TRACE_EVENT1(
TRACE_DISABLED_BY_DEFAULT("loading"),
"ContentSubresourceFilterThrottleManager::ReadyToCommitNavigation",
"activation_state", filter->activation_state().ToTracedValue());
- throttle->second->WillSendActivationToRenderer();
-
- content::RenderFrameHost* frame_host =
- navigation_handle->GetRenderFrameHost();
- frame_host->Send(new SubresourceFilterMsg_ActivateForNextCommittedLoad(
- frame_host->GetRoutingID(), filter->activation_state()));
+ // Only send the IPC to the renderer if not actively ignoring rules from our
+ // ruleset. Note, if we ever want to do anything more complex in the renderer
+ // (other than just consume the rules), we will likely have to find a
+ // different solution here.
+ throttle->CouldSendActivationToRenderer();
+ if (delegate_->AllowRulesetRules()) {
+ content::RenderFrameHost* frame_host =
+ navigation_handle->GetRenderFrameHost();
+ frame_host->Send(new SubresourceFilterMsg_ActivateForNextCommittedLoad(
+ frame_host->GetRoutingID(), filter->activation_state()));
+ }
}
void ContentSubresourceFilterThrottleManager::DidFinishNavigation(
@@ -100,6 +128,7 @@ void ContentSubresourceFilterThrottleManager::DidFinishNavigation(
auto throttle = ongoing_activation_throttles_.find(navigation_handle);
std::unique_ptr<AsyncDocumentSubresourceFilter> filter;
if (throttle != ongoing_activation_throttles_.end()) {
+ CHECK_EQ(navigation_handle, throttle->second->navigation_handle());
filter = throttle->second->ReleaseFilter();
ongoing_activation_throttles_.erase(throttle);
}
@@ -119,14 +148,19 @@ void ContentSubresourceFilterThrottleManager::DidFinishNavigation(
kActivationConsoleMessage);
}
}
+ ActivationLevel level = filter ? filter->activation_state().activation_level
+ : ActivationLevel::DISABLED;
+ UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.PageLoad.ActivationState",
+ level, ActivationLevel::LAST);
}
// Make sure |activated_frame_hosts_| is updated or cleaned up depending on
// this navigation's activation state.
if (filter) {
- filter->set_first_disallowed_load_callback(base::Bind(
+ base::OnceClosure disallowed_callback(base::BindOnce(
&ContentSubresourceFilterThrottleManager::MaybeCallFirstDisallowedLoad,
weak_ptr_factory_.GetWeakPtr()));
+ filter->set_first_disallowed_load_callback(std::move(disallowed_callback));
activated_frame_hosts_[frame_host] = std::move(filter);
} else {
activated_frame_hosts_.erase(frame_host);
@@ -186,24 +220,46 @@ void ContentSubresourceFilterThrottleManager::MaybeAppendNavigationThrottles(
MaybeCreateSubframeNavigationFilteringThrottle(navigation_handle)) {
throttles->push_back(std::move(filtering_throttle));
}
+
+ CHECK(!base::ContainsKey(ongoing_activation_throttles_, navigation_handle));
if (auto activation_throttle =
MaybeCreateActivationStateComputingThrottle(navigation_handle)) {
ongoing_activation_throttles_[navigation_handle] =
activation_throttle.get();
+ activation_throttle->set_destruction_closure(base::BindOnce(
+ &ContentSubresourceFilterThrottleManager::OnActivationThrottleDestroyed,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(navigation_handle)));
throttles->push_back(std::move(activation_throttle));
}
}
-bool ContentSubresourceFilterThrottleManager::ShouldDisallowNewWindow() {
+// Blocking popups here should trigger the standard popup blocking UI, so don't
+// force the subresource filter specific UI.
+bool ContentSubresourceFilterThrottleManager::ShouldDisallowNewWindow(
+ const content::OpenURLParams* open_url_params) {
auto it = activated_frame_hosts_.find(web_contents()->GetMainFrame());
if (it == activated_frame_hosts_.end())
return false;
const ActivationState state = it->second->activation_state();
- // This should trigger the standard popup blocking UI, so don't force the
- // subresource filter specific UI here.
- return state.activation_level == ActivationLevel::ENABLED &&
- !state.filtering_disabled_for_document &&
- !state.generic_blocking_rules_disabled;
+ if (state.activation_level != ActivationLevel::ENABLED ||
+ state.filtering_disabled_for_document ||
+ state.generic_blocking_rules_disabled ||
+ !delegate_->AllowStrongPopupBlocking()) {
+ return false;
+ }
+
+ // Block new windows from navigations whose triggering JS Event has an
+ // isTrusted bit set to false. This bit is set to true if the event is
+ // generated via a user action. See docs:
+ // https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted
+ bool should_block = true;
+ if (open_url_params) {
+ should_block = open_url_params->triggering_event_info ==
+ blink::WebTriggeringEventInfo::kFromUntrustedEvent;
+ }
+ if (should_block)
+ delegate_->OnFirstSubresourceLoadDisallowed();
+ return should_block;
}
std::unique_ptr<SubframeNavigationFilteringThrottle>
@@ -212,6 +268,8 @@ ContentSubresourceFilterThrottleManager::
content::NavigationHandle* navigation_handle) {
if (navigation_handle->IsInMainFrame())
return nullptr;
+ if (!delegate_->AllowRulesetRules())
+ return nullptr;
AsyncDocumentSubresourceFilter* parent_filter =
GetParentFrameFilter(navigation_handle);
return parent_filter ? base::MakeUnique<SubframeNavigationFilteringThrottle>(
@@ -278,4 +336,10 @@ void ContentSubresourceFilterThrottleManager::OnDocumentLoadStatistics(
statistics_->OnDocumentLoadStatistics(statistics);
}
+void ContentSubresourceFilterThrottleManager::OnActivationThrottleDestroyed(
+ content::NavigationHandle* navigation_handle) {
+ size_t num_erased = ongoing_activation_throttles_.erase(navigation_handle);
+ CHECK_EQ(0u, num_erased);
+}
+
} // namespace subresource_filter
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 4240c57946b..dc6652f364a 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
@@ -19,6 +19,7 @@
#include "content/public/browser/web_contents_observer.h"
namespace content {
+struct OpenURLParams;
class NavigationHandle;
class NavigationThrottle;
class RenderFrameHost;
@@ -57,6 +58,13 @@ class ContentSubresourceFilterThrottleManager
// 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() {}
+
+ // Whether the stronger version of the popup blocker is enabled for this
+ // page load.
+ virtual bool AllowStrongPopupBlocking();
+
+ // Whether we should be using ruleset rules for this page load.
+ virtual bool AllowRulesetRules();
};
ContentSubresourceFilterThrottleManager(
@@ -80,7 +88,7 @@ class ContentSubresourceFilterThrottleManager
// Returns whether or not the current WebContents is allowed to create a new
// window.
- bool ShouldDisallowNewWindow();
+ bool ShouldDisallowNewWindow(const content::OpenURLParams* open_url_params);
VerifiedRuleset::Handle* ruleset_handle_for_testing() {
return ruleset_handle_.get();
@@ -127,6 +135,11 @@ class ContentSubresourceFilterThrottleManager
void OnDocumentLoadStatistics(const DocumentLoadStatistics& statistics);
+ // The navigation handle ptr will be in an invalid state, do not access any
+ // members on it. This method is only for debugging crbug.com/736249.
+ void OnActivationThrottleDestroyed(
+ content::NavigationHandle* navigation_handle);
+
// For each RenderFrameHost where the last committed load has subresource
// filtering activated, owns the corresponding AsyncDocumentSubresourceFilter.
std::unordered_map<content::RenderFrameHost*,
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 fbd140e28ba..f714da0babb 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,15 +14,16 @@
#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/test_simple_task_runner.h"
#include "components/subresource_filter/content/browser/async_document_subresource_filter.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"
#include "components/subresource_filter/core/common/activation_state.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/web_contents.h"
@@ -36,6 +37,8 @@
namespace subresource_filter {
+namespace proto = url_pattern_index::proto;
+
const char kTestURLWithActivation[] = "https://www.page-with-activation.com/";
const char kTestURLWithActivation2[] =
"https://www.page-with-activation-2.com/";
@@ -122,8 +125,6 @@ class ContentSubresourceFilterThrottleManagerTest
NavigateAndCommit(GURL("https://example.first"));
- Observe(RenderViewHostTestHarness::web_contents());
-
// Initialize the ruleset dealer.
std::vector<proto::UrlRule> rules;
rules.push_back(testing::CreateWhitelistRuleForDocument(
@@ -145,6 +146,7 @@ class ContentSubresourceFilterThrottleManagerTest
base::MakeUnique<ContentSubresourceFilterThrottleManager>(
this, dealer_handle_.get(),
RenderViewHostTestHarness::web_contents());
+ Observe(RenderViewHostTestHarness::web_contents());
}
void TearDown() override {
@@ -676,6 +678,37 @@ TEST_P(ContentSubresourceFilterThrottleManagerTest,
EXPECT_EQ(0, disallowed_notification_count());
}
+TEST_F(ContentSubresourceFilterThrottleManagerTest, LogActivation) {
+ base::HistogramTester tester;
+ const char kActivationStateHistogram[] =
+ "SubresourceFilter.PageLoad.ActivationState";
+ NavigateAndCommitMainFrame(GURL(kTestURLWithDryRun));
+ tester.ExpectBucketCount(kActivationStateHistogram,
+ static_cast<int>(ActivationLevel::DRYRUN), 1);
+
+ NavigateAndCommitMainFrame(GURL(kTestURLWithNoActivation));
+ tester.ExpectBucketCount(kActivationStateHistogram,
+ static_cast<int>(ActivationLevel::DISABLED), 1);
+
+ NavigateAndCommitMainFrame(GURL(kTestURLWithActivation));
+ tester.ExpectBucketCount(kActivationStateHistogram,
+ static_cast<int>(ActivationLevel::ENABLED), 1);
+
+ // Navigate a subframe that is not filtered, but should still activate.
+ CreateSubframeWithTestNavigation(GURL("https://whitelist.com"), main_rfh());
+ SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+ content::RenderFrameHost* subframe1 =
+ SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+ ExpectActivationSignalForFrame(subframe1, true /* expect_activation */);
+
+ tester.ExpectTotalCount(kActivationStateHistogram, 3);
+ // Only those with page level activation do ruleset lookups.
+ tester.ExpectTotalCount("SubresourceFilter.PageLoad.Activation.WallDuration",
+ 2);
+ tester.ExpectTotalCount("SubresourceFilter.PageLoad.Activation.CPUDuration",
+ 2);
+}
+
// TODO(csharrison): Make sure the following conditions are exercised in tests:
//
// - Synchronous navigations to about:blank. These hit issues with the
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 6ac5a136c8c..14c9cab3df9 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
@@ -10,8 +10,7 @@
#include "content/public/browser/browser_thread.h"
#include "url/gurl.h"
-FakeSafeBrowsingDatabaseManager::FakeSafeBrowsingDatabaseManager()
- : simulate_timeout_(false) {}
+FakeSafeBrowsingDatabaseManager::FakeSafeBrowsingDatabaseManager() {}
void FakeSafeBrowsingDatabaseManager::AddBlacklistedUrl(
const GURL& url,
@@ -38,15 +37,17 @@ FakeSafeBrowsingDatabaseManager::~FakeSafeBrowsingDatabaseManager() {}
bool FakeSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
const GURL& url,
Client* client) {
- if (simulate_timeout_)
- return false;
- if (!url_to_threat_type_.count(url))
+ DCHECK(CanCheckSubresourceFilter());
+
+ if (synchronous_failure_ && !url_to_threat_type_.count(url))
return true;
// Enforce the invariant that a client will not send multiple requests, with
// the subresource filter client implementation.
DCHECK(checks_.find(client) == checks_.end());
checks_.insert(client);
+ if (simulate_timeout_)
+ return false;
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&FakeSafeBrowsingDatabaseManager::
@@ -62,9 +63,15 @@ void FakeSafeBrowsingDatabaseManager::OnCheckUrlForSubresourceFilterComplete(
if (checks_.find(client) == checks_.end())
return;
safe_browsing::ThreatMetadata metadata;
- metadata.threat_pattern_type = url_to_threat_type_[url].second;
+ safe_browsing::SBThreatType threat_type =
+ safe_browsing::SBThreatType::SB_THREAT_TYPE_SAFE;
+ auto it = url_to_threat_type_.find(url);
+ if (it != url_to_threat_type_.end()) {
+ threat_type = it->second.first;
+ metadata.threat_pattern_type = it->second.second;
+ }
+ client->OnCheckBrowseUrlResult(url, threat_type, metadata);
- client->OnCheckBrowseUrlResult(url, url_to_threat_type_[url].first, metadata);
// Erase the client when a check is complete. Otherwise, it's possible
// subsequent clients that share an address with this one will DCHECK in
// CheckUrlForSubresourceFilter.
@@ -83,13 +90,18 @@ bool FakeSafeBrowsingDatabaseManager::ChecksAreAlwaysAsync() const {
return false;
}
void FakeSafeBrowsingDatabaseManager::CancelCheck(Client* client) {
- checks_.erase(client);
+ size_t erased = checks_.erase(client);
+ DCHECK_EQ(erased, 1u);
}
bool FakeSafeBrowsingDatabaseManager::CanCheckResourceType(
content::ResourceType /* resource_type */) const {
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 ba2f9c7e348..d28c0284421 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
@@ -30,6 +30,10 @@ class FakeSafeBrowsingDatabaseManager
void SimulateTimeout();
+ // If set, will synchronously fail from CheckUrlForSubresourceFilter rather
+ // than posting a task to fail if the URL does not match the blacklist.
+ void set_synchronous_failure() { synchronous_failure_ = true; }
+
protected:
~FakeSafeBrowsingDatabaseManager() override;
@@ -41,6 +45,7 @@ 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;
@@ -53,7 +58,8 @@ class FakeSafeBrowsingDatabaseManager
GURL,
std::pair<safe_browsing::SBThreatType, safe_browsing::ThreatPatternType>>
url_to_threat_type_;
- bool simulate_timeout_;
+ bool simulate_timeout_ = false;
+ bool synchronous_failure_ = false;
DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingDatabaseManager);
};
diff --git a/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc b/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
index 8254f538d79..21a5f71a5b5 100644
--- a/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
+++ b/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
@@ -4,12 +4,19 @@
#include "components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h"
+#include <sstream>
+
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.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/time_measurements.h"
#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
#include "content/public/common/browser_side_navigation_policy.h"
+#include "content/public/common/console_message_level.h"
namespace subresource_filter {
@@ -24,16 +31,21 @@ SubframeNavigationFilteringThrottle::SubframeNavigationFilteringThrottle(
}
SubframeNavigationFilteringThrottle::~SubframeNavigationFilteringThrottle() {
- if (disallowed_) {
- UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
- "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed",
- total_defer_time_, base::TimeDelta::FromMicroseconds(1),
- base::TimeDelta::FromSeconds(10), 50);
- } else {
- UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
- "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Allowed",
- total_defer_time_, base::TimeDelta::FromMicroseconds(1),
- base::TimeDelta::FromSeconds(10), 50);
+ switch (load_policy_) {
+ case LoadPolicy::ALLOW:
+ UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+ "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Allowed",
+ total_defer_time_, base::TimeDelta::FromMicroseconds(1),
+ base::TimeDelta::FromSeconds(10), 50);
+ break;
+ case LoadPolicy::WOULD_DISALLOW:
+ // fall through
+ case LoadPolicy::DISALLOW:
+ UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+ "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed",
+ total_defer_time_, base::TimeDelta::FromMicroseconds(1),
+ base::TimeDelta::FromSeconds(10), 50);
+ break;
}
}
@@ -47,6 +59,13 @@ SubframeNavigationFilteringThrottle::WillRedirectRequest() {
return DeferToCalculateLoadPolicy(ThrottlingStage::WillRedirectRequest);
}
+content::NavigationThrottle::ThrottleCheckResult
+SubframeNavigationFilteringThrottle::WillProcessResponse() {
+ DCHECK_NE(load_policy_, LoadPolicy::DISALLOW);
+ NotifyLoadPolicy();
+ return content::NavigationThrottle::ThrottleCheckResult::PROCEED;
+}
+
const char* SubframeNavigationFilteringThrottle::GetNameForLogging() {
return "SubframeNavigationFilteringThrottle";
}
@@ -54,6 +73,9 @@ const char* SubframeNavigationFilteringThrottle::GetNameForLogging() {
content::NavigationThrottle::ThrottleCheckResult
SubframeNavigationFilteringThrottle::DeferToCalculateLoadPolicy(
ThrottlingStage stage) {
+ DCHECK_NE(load_policy_, LoadPolicy::DISALLOW);
+ if (load_policy_ == LoadPolicy::WOULD_DISALLOW)
+ return content::NavigationThrottle::ThrottleCheckResult::PROCEED;
parent_frame_filter_->GetLoadPolicyForSubdocument(
navigation_handle()->GetURL(),
base::Bind(&SubframeNavigationFilteringThrottle::OnCalculatedLoadPolicy,
@@ -66,22 +88,43 @@ void SubframeNavigationFilteringThrottle::OnCalculatedLoadPolicy(
ThrottlingStage stage,
LoadPolicy policy) {
DCHECK(!last_defer_timestamp_.is_null());
+ load_policy_ = policy;
total_defer_time_ += base::TimeTicks::Now() - last_defer_timestamp_;
- // TODO(csharrison): Support WouldDisallow pattern and expose the policy for
- // metrics.
+
if (policy == LoadPolicy::DISALLOW) {
- disallowed_ = true;
+ if (parent_frame_filter_->activation_state().enable_logging) {
+ std::ostringstream oss(kDisallowSubframeConsoleMessagePrefix);
+ oss << navigation_handle()->GetURL();
+ oss << kDisallowSubframeConsoleMessageSuffix;
+ navigation_handle()
+ ->GetWebContents()
+ ->GetMainFrame()
+ ->AddMessageToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR,
+ oss.str());
+ }
+
parent_frame_filter_->ReportDisallowedLoad();
+ // Other load policies will be reported in WillProcessResponse.
+ NotifyLoadPolicy();
const bool block_and_collapse_is_supported =
content::IsBrowserSideNavigationEnabled() ||
stage == ThrottlingStage::WillStartRequest;
- navigation_handle()->CancelDeferredNavigation(
+ CancelDeferredNavigation(
block_and_collapse_is_supported
? content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE
: content::NavigationThrottle::CANCEL);
} else {
- navigation_handle()->Resume();
+ Resume();
+ }
+}
+
+void SubframeNavigationFilteringThrottle::NotifyLoadPolicy() const {
+ if (auto* observer_manager =
+ SubresourceFilterObserverManager::FromWebContents(
+ navigation_handle()->GetWebContents())) {
+ observer_manager->NotifySubframeNavigationEvaluated(navigation_handle(),
+ load_policy_);
}
}
diff --git a/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h b/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
index e4dbfe12c84..74d89219895 100644
--- a/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
+++ b/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
@@ -9,6 +9,7 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
+#include "components/subresource_filter/core/common/load_policy.h"
#include "content/public/browser/navigation_throttle.h"
namespace content {
@@ -38,6 +39,8 @@ class SubframeNavigationFilteringThrottle : public content::NavigationThrottle {
content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
override;
+ content::NavigationThrottle::ThrottleCheckResult WillProcessResponse()
+ override;
const char* GetNameForLogging() override;
private:
@@ -47,12 +50,14 @@ class SubframeNavigationFilteringThrottle : public content::NavigationThrottle {
ThrottlingStage stage);
void OnCalculatedLoadPolicy(ThrottlingStage stage, LoadPolicy policy);
+ void NotifyLoadPolicy() const;
+
// Must outlive this class.
AsyncDocumentSubresourceFilter* parent_frame_filter_;
base::TimeTicks last_defer_timestamp_;
base::TimeDelta total_defer_time_;
- bool disallowed_ = false;
+ LoadPolicy load_policy_ = LoadPolicy::ALLOW;
base::WeakPtrFactory<SubframeNavigationFilteringThrottle> weak_ptr_factory_;
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 7daa78a65f7..0f7347904cb 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/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/load_policy.h"
namespace content {
class NavigationHandle;
@@ -21,12 +22,22 @@ class SubresourceFilterObserver {
public:
virtual ~SubresourceFilterObserver() = default;
+ // Called before the observer manager is destroyed. Observers must unregister
+ // themselves by this point.
virtual void OnSubresourceFilterGoingAway() {}
+ // 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
+ // WillRedirectRequest stage.
+ virtual void OnSubframeNavigationEvaluated(
+ content::NavigationHandle* navigation_handle,
+ LoadPolicy load_policy) {}
};
} // namespace subresource_filter
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 f3b891c7aee..a0d02d19c78 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
@@ -39,4 +39,11 @@ void SubresourceFilterObserverManager::NotifyPageActivationComputed(
}
}
+void SubresourceFilterObserverManager::NotifySubframeNavigationEvaluated(
+ content::NavigationHandle* navigation_handle,
+ LoadPolicy load_policy) {
+ for (auto& observer : observers_)
+ observer.OnSubframeNavigationEvaluated(navigation_handle, load_policy);
+}
+
} // namespace subresource_filter
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 489fb214306..978b1a3b1e4 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
@@ -9,6 +9,7 @@
#include "base/observer_list.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer.h"
#include "components/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/load_policy.h"
#include "content/public/browser/web_contents_user_data.h"
namespace content {
@@ -39,6 +40,10 @@ class SubresourceFilterObserverManager
ActivationDecision activation_decision,
const ActivationState& activation_state);
+ void NotifySubframeNavigationEvaluated(
+ content::NavigationHandle* navigation_handle,
+ LoadPolicy load_policy);
+
private:
base::ObserverList<SubresourceFilterObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(SubresourceFilterObserverManager);
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
new file mode 100644
index 00000000000..24cf94808a5
--- /dev/null
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.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/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
+
+#include "content/public/browser/navigation_handle.h"
+
+namespace subresource_filter {
+
+TestSubresourceFilterObserver::TestSubresourceFilterObserver(
+ content::WebContents* web_contents)
+ : scoped_observer_(this) {
+ scoped_observer_.Add(
+ SubresourceFilterObserverManager::FromWebContents(web_contents));
+}
+
+TestSubresourceFilterObserver::~TestSubresourceFilterObserver() {}
+
+void TestSubresourceFilterObserver::OnSubresourceFilterGoingAway() {
+ scoped_observer_.RemoveAll();
+}
+
+void TestSubresourceFilterObserver::OnPageActivationComputed(
+ content::NavigationHandle* navigation_handle,
+ ActivationDecision activation_decision,
+ const ActivationState& activation_state) {
+ page_activations_[navigation_handle->GetURL()] = activation_decision;
+}
+
+void TestSubresourceFilterObserver::OnSubframeNavigationEvaluated(
+ content::NavigationHandle* navigation_handle,
+ LoadPolicy load_policy) {
+ subframe_load_evaluations_[navigation_handle->GetURL()] = load_policy;
+}
+
+base::Optional<ActivationDecision>
+TestSubresourceFilterObserver::GetPageActivation(const GURL& url) {
+ auto it = page_activations_.find(url);
+ if (it != page_activations_.end())
+ return it->second;
+ return base::Optional<ActivationDecision>();
+}
+
+base::Optional<LoadPolicy> TestSubresourceFilterObserver::GetSubframeLoadPolicy(
+ const GURL& url) {
+ auto it = subframe_load_evaluations_.find(url);
+ if (it != subframe_load_evaluations_.end())
+ return it->second;
+ return base::Optional<LoadPolicy>();
+}
+
+} // namespace subresource_filter
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
new file mode 100644
index 00000000000..b76f203bdf6
--- /dev/null
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
@@ -0,0 +1,57 @@
+// 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_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_TEST_UTILS_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_TEST_UTILS_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/scoped_observer.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/load_policy.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+} // namespace content
+
+namespace subresource_filter {
+
+// This class can be used to observe subresource filtering events associated
+// with a particular web contents. Particular events can be expected by using
+// the Get* methods.
+class TestSubresourceFilterObserver : public SubresourceFilterObserver {
+ public:
+ TestSubresourceFilterObserver(content::WebContents* web_contents);
+ ~TestSubresourceFilterObserver() override;
+
+ // SubresourceFilterObserver:
+ void OnSubresourceFilterGoingAway() override;
+ void OnPageActivationComputed(
+ content::NavigationHandle* navigation_handle,
+ ActivationDecision activation_decision,
+ const ActivationState& activation_state) override;
+ void OnSubframeNavigationEvaluated(
+ content::NavigationHandle* navigation_handle,
+ LoadPolicy load_policy) override;
+
+ base::Optional<ActivationDecision> GetPageActivation(const GURL& url);
+ base::Optional<LoadPolicy> GetSubframeLoadPolicy(const GURL& url);
+
+ private:
+ std::map<GURL, LoadPolicy> subframe_load_evaluations_;
+ std::map<GURL, ActivationDecision> page_activations_;
+
+ ScopedObserver<SubresourceFilterObserverManager, SubresourceFilterObserver>
+ scoped_observer_;
+ DISALLOW_COPY_AND_ASSIGN(TestSubresourceFilterObserver);
+};
+
+} // namespace subresource_filter
+
+#endif // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_TEST_UTILS_H_
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 dfa868bdc33..5c578c41098 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
@@ -4,6 +4,7 @@
#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
+#include <sstream>
#include <utility>
#include <vector>
@@ -61,6 +62,10 @@ SubresourceFilterSafeBrowsingActivationThrottle::
base::OnTaskRunnerDeleter(io_task_runner_)),
client_(client) {
DCHECK(handle->IsInMainFrame());
+
+ CheckCurrentUrl();
+ // Check added to investigate crbug.com/733099.
+ CHECK(!database_client_ || !check_results_.empty());
}
SubresourceFilterSafeBrowsingActivationThrottle::
@@ -85,18 +90,24 @@ bool SubresourceFilterSafeBrowsingActivationThrottle::NavigationIsPageReload(
content::NavigationThrottle::ThrottleCheckResult
SubresourceFilterSafeBrowsingActivationThrottle::WillStartRequest() {
- CheckCurrentUrl();
+ will_start_request_called_ = true;
return content::NavigationThrottle::ThrottleCheckResult::PROCEED;
}
content::NavigationThrottle::ThrottleCheckResult
SubresourceFilterSafeBrowsingActivationThrottle::WillRedirectRequest() {
CheckCurrentUrl();
+ // Check added to investigate crbug.com/733099.
+ CHECK(!database_client_ || !check_results_.empty());
return content::NavigationThrottle::ThrottleCheckResult::PROCEED;
}
content::NavigationThrottle::ThrottleCheckResult
SubresourceFilterSafeBrowsingActivationThrottle::WillProcessResponse() {
+ // Checks added to investigate crbug.com/733099.
+ CHECK(will_start_request_called_);
+ CHECK(!database_client_ || !check_results_.empty());
+
// No need to defer the navigation if the check already happened.
if (!database_client_ || check_results_.back().finished) {
NotifyResult();
@@ -122,7 +133,7 @@ void SubresourceFilterSafeBrowsingActivationThrottle::OnCheckUrlResultOnUI(
stored_result = result;
if (!defer_time_.is_null() && request_id == check_results_.size() - 1) {
NotifyResult();
- navigation_handle()->Resume();
+ Resume();
}
}
@@ -209,13 +220,13 @@ SubresourceFilterSafeBrowsingActivationThrottle::ComputeActivation(
bool has_activated_config =
highest_priority_activated_config !=
config_list->configs_by_decreasing_priority().end();
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
- "ContentSubresourceFilterDriverFactory::"
- "ComputeActivationForMainFrameNavigation",
- "highest_priority_activated_config",
- has_activated_config
- ? highest_priority_activated_config->ToTracedValue()
- : base::MakeUnique<base::trace_event::TracedValue>());
+ TRACE_EVENT1(
+ TRACE_DISABLED_BY_DEFAULT("loading"),
+ "SubresourceFilterSafeBrowsingActivationThrottle::ComputeActivation",
+ "highest_priority_activated_config",
+ has_activated_config
+ ? highest_priority_activated_config->ToTracedValue()
+ : base::MakeUnique<base::trace_event::TracedValue>());
if (!has_activated_config)
return ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET;
@@ -239,6 +250,17 @@ bool SubresourceFilterSafeBrowsingActivationThrottle::
bool scheme_is_http_or_https,
const Configuration::ActivationConditions& conditions,
ActivationList matched_list) const {
+ // Avoid copies when tracing disabled.
+ auto list_to_string = [](ActivationList activation_list) {
+ std::ostringstream matched_list_stream;
+ matched_list_stream << activation_list;
+ return matched_list_stream.str();
+ };
+ TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "SubresourceFilterSafeBrowsingActivationThrottle::"
+ "DoesMainFrameURLSatisfyActivationConditions",
+ "matched_list", list_to_string(matched_list), "conditions",
+ conditions.ToTracedValue());
switch (conditions.activation_scope) {
case ActivationScope::ALL_SITES:
return true;
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 885e4e3e953..774ec62be88 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
@@ -94,6 +94,9 @@ class SubresourceFilterSafeBrowsingActivationThrottle
// WillProcessResponse. If deferral was not necessary, will remain null.
base::TimeTicks defer_time_;
+ // Added to investigate crbug.com/733099.
+ bool will_start_request_called_ = false;
+
DISALLOW_COPY_AND_ASSIGN(SubresourceFilterSafeBrowsingActivationThrottle);
};
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 9939a319584..0e307f99d4f 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
@@ -31,6 +31,7 @@
#include "components/subresource_filter/core/common/activation_state.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents_observer.h"
@@ -166,7 +167,7 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
Configure();
test_io_task_runner_ = new base::TestMockTimeTaskRunner();
// Note: Using NiceMock to allow uninteresting calls and suppress warnings.
- std::vector<proto::UrlRule> rules;
+ std::vector<url_pattern_index::proto::UrlRule> rules;
rules.push_back(testing::CreateSuffixRule("disallowed.html"));
ASSERT_NO_FATAL_FAILURE(test_ruleset_creator_.CreateRulesetWithRules(
rules, &test_ruleset_pair_));
@@ -192,7 +193,17 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
void TearDown() override {
client_.reset();
+
+ // RunUntilIdle() must be called multiple times to flush any outstanding
+ // cross-thread interactions.
+ // TODO(csharrison): Clean up test teardown logic.
+ RunUntilIdle();
+ RunUntilIdle();
+
+ // RunUntilIdle() called once more, to delete the database on the IO thread.
+ fake_safe_browsing_database_ = nullptr;
RunUntilIdle();
+
content::RenderViewHostTestHarness::TearDown();
}
@@ -303,7 +314,9 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
metadata);
}
- void SimulateTimeout() { fake_safe_browsing_database_->SimulateTimeout(); }
+ FakeSafeBrowsingDatabaseManager* fake_safe_browsing_database() {
+ return fake_safe_browsing_database_.get();
+ }
void ClearAllBlacklistedUrls() {
fake_safe_browsing_database_->RemoveAllBlacklistedUrls();
@@ -313,8 +326,8 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
void UsePassThroughThrottle() { fake_safe_browsing_database_ = nullptr; }
void RunUntilIdle() {
- test_io_task_runner_->RunUntilIdle();
base::RunLoop().RunUntilIdle();
+ test_io_task_runner_->RunUntilIdle();
}
content::NavigationSimulator* navigation_simulator() {
@@ -700,11 +713,11 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest, ActivationList) {
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
{ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
ActivationList::PHISHING_INTERSTITIAL,
- safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL,
+ safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
{ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
ActivationList::PHISHING_INTERSTITIAL,
- safe_browsing::SB_THREAT_TYPE_BINARY_MALWARE_URL,
+ safe_browsing::SB_THREAT_TYPE_URL_BINARY_MALWARE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
{ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
ActivationList::PHISHING_INTERSTITIAL,
@@ -716,7 +729,7 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest, ActivationList) {
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
{ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
ActivationList::PHISHING_INTERSTITIAL,
- safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL,
+ safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
{ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
ActivationList::PHISHING_INTERSTITIAL,
@@ -755,6 +768,18 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest, ActivationList) {
}
}
+// Regression test for an issue where synchronous failure from the SB database
+// caused a double cancel. This is DCHECKed in the fake database.
+TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
+ SynchronousResponse) {
+ const GURL url(kURL);
+ fake_safe_browsing_database()->set_synchronous_failure();
+ SimulateStartAndExpectProceed(url);
+ SimulateCommitAndExpectProceed();
+ tester().ExpectTotalCount(kMatchesPatternHistogramNameSubresourceFilterSuffix,
+ 0);
+}
+
TEST_P(SubresourceFilterSafeBrowsingActivationThrottleScopeTest,
ActivateForScopeType) {
const ActivationScopeTestData& test_data = GetParam();
@@ -898,7 +923,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
const ActivationListTestData& test_data = GetParam();
const GURL url(kURL);
const std::string suffix(GetSuffixForList(test_data.activation_list_type));
- SimulateTimeout();
+ fake_safe_browsing_database()->SimulateTimeout();
SimulateStartAndExpectProceed(url);
// Flush the pending tasks on the IO thread, so the delayed task surely gets
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.cc b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.cc
index d5bc6a7cc97..fd97742f362 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.cc
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.cc
@@ -11,12 +11,25 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h"
#include "content/public/browser/browser_thread.h"
namespace subresource_filter {
+std::unique_ptr<base::trace_event::TracedValue>
+SubresourceFilterSafeBrowsingClient::CheckResult::ToTracedValue() const {
+ auto value = base::MakeUnique<base::trace_event::TracedValue>();
+ value->SetInteger("request_id", request_id);
+ value->SetInteger("threat_type", threat_type);
+ value->SetInteger("pattern_type", static_cast<int>(pattern_type));
+ value->SetInteger("check_time (us)", check_time.InMicroseconds());
+ value->SetBoolean("finished", finished);
+ return value;
+}
+
SubresourceFilterSafeBrowsingClient::SubresourceFilterSafeBrowsingClient(
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager,
base::WeakPtr<SubresourceFilterSafeBrowsingActivationThrottle> throttle,
@@ -39,6 +52,10 @@ void SubresourceFilterSafeBrowsingClient::CheckUrlOnIO(const GURL& url,
auto* raw_request = request.get();
DCHECK(requests_.find(raw_request) == requests_.end());
requests_[raw_request] = std::move(request);
+ TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "SubresourceFilterSBCheck", raw_request,
+ "check_result",
+ base::MakeUnique<base::trace_event::TracedValue>());
raw_request->Start();
// Careful, |raw_request| can be destroyed after this line.
}
@@ -47,6 +64,9 @@ void SubresourceFilterSafeBrowsingClient::OnCheckBrowseUrlResult(
SubresourceFilterSafeBrowsingClientRequest* request,
const CheckResult& check_result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ TRACE_EVENT_ASYNC_END1(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "SubresourceFilterSBCheck", request, "check_result",
+ check_result.ToTracedValue());
throttle_task_runner_->PostTask(
FROM_HERE, base::Bind(&SubresourceFilterSafeBrowsingActivationThrottle::
OnCheckUrlResultOnUI,
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h
index 96929ac751a..5b06b854aac 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h
@@ -15,14 +15,20 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/safe_browsing_db/util.h"
-#include "components/safe_browsing_db/v4_local_database_manager.h"
class GURL;
namespace base {
class SingleThreadTaskRunner;
+namespace trace_event {
+class TracedValue;
+} // namespace trace_event
} // namespace base
+namespace safe_browsing {
+class SafeBrowsingDatabaseManager;
+} // namespace safe_browsing
+
namespace subresource_filter {
class SubresourceFilterSafeBrowsingActivationThrottle;
@@ -43,6 +49,8 @@ class SubresourceFilterSafeBrowsingClient {
safe_browsing::ThreatPatternType::NONE;
base::TimeDelta check_time;
bool finished = false;
+
+ std::unique_ptr<base::trace_event::TracedValue> ToTracedValue() const;
};
SubresourceFilterSafeBrowsingClient(
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 a7b477838eb..3e98d1141fd 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,6 +48,7 @@ void SubresourceFilterSafeBrowsingClientRequest::Start() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
start_time_ = base::TimeTicks::Now();
if (database_manager_->CheckUrlForSubresourceFilter(url_, this)) {
+ request_completed_ = true;
SendCheckResultToClient(false /* served_from_network */,
safe_browsing::SB_THREAT_TYPE_SAFE,
safe_browsing::ThreatMetadata());
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h
index ef8bf14b5c1..2560050d943 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h
@@ -13,7 +13,6 @@
#include "base/timer/timer.h"
#include "components/safe_browsing_db/database_manager.h"
#include "components/safe_browsing_db/util.h"
-#include "components/safe_browsing_db/v4_local_database_manager.h"
#include "url/gurl.h"
namespace base {
diff --git a/chromium/components/subresource_filter/content/common/ruleset_dealer.cc b/chromium/components/subresource_filter/content/common/ruleset_dealer.cc
index 5977350a9bb..04a6fd75e04 100644
--- a/chromium/components/subresource_filter/content/common/ruleset_dealer.cc
+++ b/chromium/components/subresource_filter/content/common/ruleset_dealer.cc
@@ -44,4 +44,8 @@ scoped_refptr<const MemoryMappedRuleset> RulesetDealer::GetRuleset() {
return strong_ruleset_ref;
}
+base::File RulesetDealer::DuplicateRulesetFile() {
+ return ruleset_file_.Duplicate();
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/common/ruleset_dealer.h b/chromium/components/subresource_filter/content/common/ruleset_dealer.h
index 14c1723fbdd..fe5db99ff9f 100644
--- a/chromium/components/subresource_filter/content/common/ruleset_dealer.h
+++ b/chromium/components/subresource_filter/content/common/ruleset_dealer.h
@@ -50,6 +50,10 @@ class RulesetDealer {
// For testing only.
bool has_cached_ruleset() const { return !!weak_cached_ruleset_.get(); }
+ // Duplicates the ruleset file. This is used to pass the file to another
+ // thread.
+ base::File DuplicateRulesetFile();
+
protected:
bool CalledOnValidSequence() const {
return sequence_checker_.CalledOnValidSequence();
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 c9b47d51177..8c66b5bfa03 100644
--- a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc
+++ b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc
@@ -6,6 +6,7 @@
#include <vector>
+#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
@@ -19,11 +20,15 @@
#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/time_measurements.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "ipc/ipc_message.h"
+#include "third_party/WebKit/public/platform/WebWorkerFetchContext.h"
#include "third_party/WebKit/public/web/WebDataSource.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "url/url_constants.h"
namespace subresource_filter {
@@ -31,6 +36,7 @@ SubresourceFilterAgent::SubresourceFilterAgent(
content::RenderFrame* render_frame,
UnverifiedRulesetDealer* ruleset_dealer)
: content::RenderFrameObserver(render_frame),
+ content::RenderFrameObserverTracker<SubresourceFilterAgent>(render_frame),
ruleset_dealer_(ruleset_dealer) {
DCHECK(ruleset_dealer);
}
@@ -59,6 +65,20 @@ void SubresourceFilterAgent::SendDocumentLoadStatistics(
render_frame()->GetRoutingID(), statistics));
}
+// static
+ActivationState SubresourceFilterAgent::GetParentActivationState(
+ content::RenderFrame* render_frame) {
+ blink::WebFrame* parent =
+ render_frame ? render_frame->GetWebFrame()->Parent() : nullptr;
+ if (parent && parent->IsWebLocalFrame()) {
+ auto* agent = SubresourceFilterAgent::Get(
+ content::RenderFrame::FromWebFrame(parent->ToWebLocalFrame()));
+ if (agent && agent->filter_for_last_committed_load_)
+ return agent->filter_for_last_committed_load_->activation_state();
+ }
+ return ActivationState(ActivationLevel::DISABLED);
+}
+
void SubresourceFilterAgent::OnActivateForNextCommittedLoad(
ActivationState activation_state) {
activation_state_for_next_commit_ = activation_state;
@@ -142,7 +162,15 @@ void SubresourceFilterAgent::DidCommitProvisionalLoad(
// TODO(csharrison): Use WebURL and WebSecurityOrigin for efficiency here,
// which require changes to the unit tests.
const GURL& url = GetDocumentURL();
- if (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsFile()) {
+
+ bool use_parent_activation = ShouldUseParentActivation(url);
+ if (use_parent_activation) {
+ activation_state_for_next_commit_ =
+ GetParentActivationState(render_frame());
+ }
+
+ if (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsFile() ||
+ use_parent_activation) {
RecordHistogramsOnLoadCommitted();
if (activation_state_for_next_commit_.activation_level !=
ActivationLevel::DISABLED &&
@@ -153,7 +181,10 @@ void SubresourceFilterAgent::DidCommitProvisionalLoad(
AsWeakPtr()));
auto ruleset = ruleset_dealer_->GetRuleset();
- DCHECK(ruleset);
+ // TODO(csharrison): Replace with DCHECK when crbug.com/734102 is
+ // resolved.
+ CHECK(ruleset);
+ CHECK(ruleset->data());
auto filter = base::MakeUnique<WebDocumentSubresourceFilterImpl>(
url::Origin(url), activation_state_for_next_commit_,
std::move(ruleset), std::move(first_disallowed_load_callback));
@@ -188,4 +219,34 @@ bool SubresourceFilterAgent::OnMessageReceived(const IPC::Message& message) {
return handled;
}
+void SubresourceFilterAgent::WillCreateWorkerFetchContext(
+ blink::WebWorkerFetchContext* worker_fetch_context) {
+ DCHECK(base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
+ if (!filter_for_last_committed_load_)
+ return;
+ if (!ruleset_dealer_->IsRulesetFileAvailable())
+ return;
+ base::File ruleset_file = ruleset_dealer_->DuplicateRulesetFile();
+ if (!ruleset_file.IsValid())
+ return;
+ worker_fetch_context->SetSubresourceFilterBuilder(
+ base::MakeUnique<WebDocumentSubresourceFilterImpl::BuilderImpl>(
+ url::Origin(GetDocumentURL()),
+ filter_for_last_committed_load_->filter().activation_state(),
+ std::move(ruleset_file),
+ base::BindOnce(&SubresourceFilterAgent::
+ SignalFirstSubresourceDisallowedForCommittedLoad,
+ AsWeakPtr())));
+}
+
+bool SubresourceFilterAgent::ShouldUseParentActivation(const GURL& url) const {
+ // 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 render_frame() && !render_frame()->IsMainFrame() &&
+ (url.SchemeIs(url::kDataScheme) || url == url::kAboutBlankURL ||
+ url == content::kAboutSrcDocURL);
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h
index 22bc3fc2a8d..3b886c33076 100644
--- a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h
+++ b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h
@@ -11,6 +11,7 @@
#include "base/memory/weak_ptr.h"
#include "components/subresource_filter/core/common/activation_state.h"
#include "content/public/renderer/render_frame_observer.h"
+#include "content/public/renderer/render_frame_observer_tracker.h"
#include "url/gurl.h"
namespace blink {
@@ -29,6 +30,7 @@ class WebDocumentSubresourceFilterImpl;
// to do so by the driver.
class SubresourceFilterAgent
: public content::RenderFrameObserver,
+ public content::RenderFrameObserverTracker<SubresourceFilterAgent>,
public base::SupportsWeakPtr<SubresourceFilterAgent> {
public:
// The |ruleset_dealer| must not be null and must outlive this instance. The
@@ -57,6 +59,11 @@ class SubresourceFilterAgent
const DocumentLoadStatistics& statistics);
private:
+ // Assumes that the parent will be in a local frame relative to this one, upon
+ // construction.
+ static ActivationState GetParentActivationState(
+ content::RenderFrame* render_frame);
+
void OnActivateForNextCommittedLoad(ActivationState activation_state);
void RecordHistogramsOnLoadCommitted();
void RecordHistogramsOnLoadFinished();
@@ -69,6 +76,12 @@ class SubresourceFilterAgent
void DidFailProvisionalLoad(const blink::WebURLError& error) override;
void DidFinishLoad() override;
bool OnMessageReceived(const IPC::Message& message) override;
+ void WillCreateWorkerFetchContext(blink::WebWorkerFetchContext*) override;
+
+ // Subframe navigations matching these URLs/schemes will not trigger
+ // ReadyToCommitNavigation in the browser process, so they must be treated
+ // specially to maintain activation.
+ bool ShouldUseParentActivation(const GURL& url) const;
// Owned by the ChromeContentRendererClient and outlives us.
UnverifiedRulesetDealer* ruleset_dealer_;
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 eb8a67b4bc7..ab996288586 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
@@ -249,6 +249,10 @@ TEST_F(SubresourceFilterAgentTest,
histogram_tester.ExpectTotalCount(kEvaluationTotalCPUDuration, 0);
}
+// Never inject a filter for main frame about:blank loads, even though we do for
+// subframe loads. Those are tested via browser tests.
+// TODO(csharrison): Refactor these unit tests so it is easier to test with
+// real backing RenderFrames.
TEST_F(SubresourceFilterAgentTest, EmptyDocumentLoad_NoFilterIsInjected) {
base::HistogramTester histogram_tester;
ExpectNoSubresourceFilterGetsInjected();
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 de17ed654bc..f44fbce6e72 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
@@ -6,8 +6,12 @@
#include <utility>
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
#include "components/subresource_filter/core/common/activation_state.h"
+#include "components/subresource_filter/core/common/load_policy.h"
#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
@@ -16,6 +20,8 @@
namespace subresource_filter {
+namespace proto = url_pattern_index::proto;
+
namespace {
using WebLoadPolicy = blink::WebDocumentSubresourceFilter::LoadPolicy;
@@ -87,6 +93,11 @@ WebLoadPolicy ToWebLoadPolicy(LoadPolicy load_policy) {
}
}
+void ProxyToTaskRunner(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ base::OnceClosure callback) {
+ task_runner->PostTask(FROM_HERE, std::move(callback));
+}
+
} // namespace
WebDocumentSubresourceFilterImpl::~WebDocumentSubresourceFilterImpl() = default;
@@ -96,7 +107,8 @@ WebDocumentSubresourceFilterImpl::WebDocumentSubresourceFilterImpl(
ActivationState activation_state,
scoped_refptr<const MemoryMappedRuleset> ruleset,
base::OnceClosure first_disallowed_load_callback)
- : filter_(std::move(document_origin), activation_state, std::move(ruleset)),
+ : activation_state_(activation_state),
+ filter_(std::move(document_origin), activation_state, std::move(ruleset)),
first_disallowed_load_callback_(
std::move(first_disallowed_load_callback)) {}
@@ -119,7 +131,7 @@ void WebDocumentSubresourceFilterImpl::ReportDisallowedLoad() {
}
bool WebDocumentSubresourceFilterImpl::ShouldLogToConsole() {
- return filter_.activation_state().enable_logging;
+ return activation_state().enable_logging;
}
WebLoadPolicy WebDocumentSubresourceFilterImpl::getLoadPolicyImpl(
@@ -135,4 +147,29 @@ WebLoadPolicy WebDocumentSubresourceFilterImpl::getLoadPolicyImpl(
return ToWebLoadPolicy(filter_.GetLoadPolicy(GURL(url), element_type));
}
+WebDocumentSubresourceFilterImpl::BuilderImpl::BuilderImpl(
+ url::Origin document_origin,
+ ActivationState activation_state,
+ base::File ruleset_file,
+ base::OnceClosure first_disallowed_load_callback)
+ : document_origin_(std::move(document_origin)),
+ activation_state_(std::move(activation_state)),
+ ruleset_file_(std::move(ruleset_file)),
+ first_disallowed_load_callback_(
+ std::move(first_disallowed_load_callback)),
+ main_task_runner_(base::MessageLoop::current()->task_runner()) {}
+
+WebDocumentSubresourceFilterImpl::BuilderImpl::~BuilderImpl() {}
+
+std::unique_ptr<blink::WebDocumentSubresourceFilter>
+WebDocumentSubresourceFilterImpl::BuilderImpl::Build() {
+ DCHECK(ruleset_file_.IsValid());
+ DCHECK(!main_task_runner_->BelongsToCurrentThread());
+ return base::MakeUnique<WebDocumentSubresourceFilterImpl>(
+ document_origin_, activation_state_,
+ base::MakeRefCounted<MemoryMappedRuleset>(std::move(ruleset_file_)),
+ base::BindOnce(&ProxyToTaskRunner, main_task_runner_,
+ std::move(first_disallowed_load_callback_)));
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h b/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
index 9722aa46814..826a07e01a5 100644
--- a/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
+++ b/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
@@ -6,11 +6,14 @@
#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_RENDERER_WEB_DOCUMENT_SUBRESOURCE_FILTER_IMPL_H_
#include "base/callback.h"
+#include "base/files/file.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
#include "components/subresource_filter/core/common/document_subresource_filter.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
#include "third_party/WebKit/public/platform/WebDocumentSubresourceFilter.h"
+#include "url/origin.h"
namespace subresource_filter {
@@ -21,6 +24,28 @@ class WebDocumentSubresourceFilterImpl
: public blink::WebDocumentSubresourceFilter,
public base::SupportsWeakPtr<WebDocumentSubresourceFilterImpl> {
public:
+ // This builder class is created on the main thread and passed to a worker
+ // thread to create the subresource filter for the worker thread.
+ class BuilderImpl : public blink::WebDocumentSubresourceFilter::Builder {
+ public:
+ BuilderImpl(url::Origin document_origin,
+ ActivationState activation_state,
+ base::File ruleset_file,
+ base::OnceClosure first_disallowed_load_callback);
+ ~BuilderImpl() override;
+
+ std::unique_ptr<blink::WebDocumentSubresourceFilter> Build() override;
+
+ private:
+ url::Origin document_origin_;
+ ActivationState activation_state_;
+ base::File ruleset_file_;
+ base::OnceClosure first_disallowed_load_callback_;
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuilderImpl);
+ };
+
// See DocumentSubresourceFilter description.
//
// Invokes |first_disallowed_load_callback|, if it is non-null, on the first
@@ -43,10 +68,16 @@ class WebDocumentSubresourceFilterImpl
void ReportDisallowedLoad() override;
bool ShouldLogToConsole() override;
+ const ActivationState& activation_state() const {
+ return filter_.activation_state();
+ }
+
private:
- LoadPolicy getLoadPolicyImpl(const blink::WebURL& url,
- proto::ElementType element_type);
+ LoadPolicy getLoadPolicyImpl(
+ const blink::WebURL& url,
+ url_pattern_index::proto::ElementType element_type);
+ ActivationState activation_state_;
DocumentSubresourceFilter filter_;
base::OnceClosure first_disallowed_load_callback_;
diff --git a/chromium/components/subresource_filter/core/browser/BUILD.gn b/chromium/components/subresource_filter/core/browser/BUILD.gn
index 2e3e756b118..a8536dc0694 100644
--- a/chromium/components/subresource_filter/core/browser/BUILD.gn
+++ b/chromium/components/subresource_filter/core/browser/BUILD.gn
@@ -14,7 +14,7 @@ static_library("browser") {
]
deps = [
"//base",
- "//components/prefs:prefs",
+ "//components/prefs",
"//components/subresource_filter/core/common",
"//components/variations",
"//third_party/protobuf:protobuf_lite",
@@ -50,7 +50,7 @@ source_set("unit_tests") {
"//base/test:test_support",
"//components/prefs:test_support",
"//components/subresource_filter/core/common:test_support",
- "//components/subresource_filter/core/common/proto",
+ "//components/url_pattern_index/proto:url_pattern_index",
"//components/variations",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/subresource_filter/core/browser/ruleset_service.cc b/chromium/components/subresource_filter/core/browser/ruleset_service.cc
index 46256729f5c..ece8d8bf0a5 100644
--- a/chromium/components/subresource_filter/core/browser/ruleset_service.cc
+++ b/chromium/components/subresource_filter/core/browser/ruleset_service.cc
@@ -25,11 +25,11 @@
#include "components/prefs/pref_service.h"
#include "components/subresource_filter/core/browser/ruleset_service_delegate.h"
#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
-#include "components/subresource_filter/core/common/copying_file_stream.h"
#include "components/subresource_filter/core/common/indexed_ruleset.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
#include "components/subresource_filter/core/common/time_measurements.h"
-#include "components/subresource_filter/core/common/unindexed_ruleset.h"
+#include "components/url_pattern_index/copying_file_stream.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
+#include "components/url_pattern_index/unindexed_ruleset.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
namespace subresource_filter {
@@ -351,15 +351,16 @@ bool RulesetService::IndexRuleset(base::File unindexed_ruleset_file,
int64_t unindexed_ruleset_size = unindexed_ruleset_file.GetLength();
if (unindexed_ruleset_size < 0)
return false;
- CopyingFileInputStream copying_stream(std::move(unindexed_ruleset_file));
+ url_pattern_index::CopyingFileInputStream copying_stream(
+ std::move(unindexed_ruleset_file));
google::protobuf::io::CopyingInputStreamAdaptor zero_copy_stream_adaptor(
&copying_stream, 4096 /* buffer_size */);
- UnindexedRulesetReader reader(&zero_copy_stream_adaptor);
+ url_pattern_index::UnindexedRulesetReader reader(&zero_copy_stream_adaptor);
size_t num_unsupported_rules = 0;
- proto::FilteringRules ruleset_chunk;
+ url_pattern_index::proto::FilteringRules ruleset_chunk;
while (reader.ReadNextChunk(&ruleset_chunk)) {
- for (const proto::UrlRule& rule : ruleset_chunk.url_rules()) {
+ for (const auto& rule : ruleset_chunk.url_rules()) {
if (!indexer->AddUrlRule(rule))
++num_unsupported_rules;
}
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 fa7d2fb159d..56444462517 100644
--- a/chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc
+++ b/chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc
@@ -27,8 +27,8 @@
#include "build/build_config.h"
#include "components/prefs/testing_pref_service.h"
#include "components/subresource_filter/core/browser/ruleset_service_delegate.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -661,7 +661,7 @@ TEST_F(SubresourceFilteringRulesetServiceTest,
mock_delegate()->SimulateStartupCompleted();
// The default field values are considered unsupported.
- proto::UrlRule unfilled_rule;
+ url_pattern_index::proto::UrlRule unfilled_rule;
TestRulesetPair ruleset_with_unsupported_rule;
ASSERT_NO_FATAL_FAILURE(
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_constants.cc b/chromium/components/subresource_filter/core/browser/subresource_filter_constants.cc
index a1bee67ca17..dbc9e0999c0 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_constants.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_constants.cc
@@ -30,8 +30,4 @@ const base::FilePath::CharType kUnindexedRulesetLicenseFileName[] =
const base::FilePath::CharType kUnindexedRulesetDataFileName[] =
FILE_PATH_LITERAL("Filtering Rules");
-// TODO(shivanisha): Update the string when finalized.
-const std::string kActivationConsoleMessage =
- "Subresource filter is activated on this site";
-
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_constants.h b/chromium/components/subresource_filter/core/browser/subresource_filter_constants.h
index ce2ba436d36..a8ccdf9a30c 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_constants.h
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_constants.h
@@ -48,7 +48,23 @@ extern const base::FilePath::CharType kUnindexedRulesetLicenseFileName[];
extern const base::FilePath::CharType kUnindexedRulesetDataFileName[];
// Console message to be displayed on activation.
-extern const std::string kActivationConsoleMessage;
+constexpr char kActivationConsoleMessage[] =
+ "Chrome is blocking ads on this site because this site tends to show ads "
+ "that interrupt, distract, or prevent user control. You should fix the "
+ "issues as soon as possible and submit your site for another review. Learn "
+ "more at https://www.chromestatus.com/feature/5738264052891648";
+
+// Console message to be displayed on disallowing subframe.
+constexpr char kDisallowSubframeConsoleMessagePrefix[] =
+ "Chrome blocked resource ";
+
+constexpr char kDisallowSubframeConsoleMessageSuffix[] =
+ " on this site because this site tends to show ads that interrupt, "
+ "distract, or prevent user control. Learn more at "
+ "https://www.chromestatus.com/feature/5738264052891648";
+
+constexpr char kLearnMoreLink[] =
+ "https://support.google.com/chrome/?p=blocked_ads";
} // namespace subresource_filter
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 6621c922404..450e3283380 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -172,6 +172,13 @@ Configuration ParseExperimentalConfiguration(
ParseBool(TakeVariationParamOrReturnEmpty(
params, kWhitelistSiteOnReloadParameterName));
+ configuration.activation_options.should_strengthen_popup_blocker =
+ ParseBool(TakeVariationParamOrReturnEmpty(
+ params, kStrengthenPopupBlockerParameterName));
+
+ configuration.activation_options.should_disable_ruleset_rules =
+ ParseBool(TakeVariationParamOrReturnEmpty(params, kDisableRulesetRules));
+
// GeneralSettings:
configuration.general_settings.ruleset_flavor =
TakeVariationParamOrReturnEmpty(params, kRulesetFlavorParameterName);
@@ -259,6 +266,8 @@ const char kPerformanceMeasurementRateParameterName[] =
"performance_measurement_rate";
const char kSuppressNotificationsParameterName[] = "suppress_notifications";
const char kWhitelistSiteOnReloadParameterName[] = "whitelist_site_on_reload";
+const char kStrengthenPopupBlockerParameterName[] = "strengthen_popup_blocker";
+const char kDisableRulesetRules[] = "disable_ruleset_rules";
const char kRulesetFlavorParameterName[] = "ruleset_flavor";
@@ -310,6 +319,8 @@ bool Configuration::operator==(const Configuration& rhs) const {
config.activation_options.performance_measurement_rate,
config.activation_options.should_whitelist_site_on_reload,
config.activation_options.should_suppress_notifications,
+ config.activation_options.should_strengthen_popup_blocker,
+ config.activation_options.should_disable_ruleset_rules,
config.general_settings.ruleset_flavor);
};
return tie(*this) == tie(rhs);
@@ -319,14 +330,20 @@ bool Configuration::operator!=(const Configuration& rhs) const {
return !(*this == rhs);
}
+std::unique_ptr<base::trace_event::TracedValue>
+Configuration::ActivationConditions::ToTracedValue() const {
+ auto value = base::MakeUnique<base::trace_event::TracedValue>();
+ value->SetString("activation_scope", StreamToString(activation_scope));
+ value->SetString("activation_list", StreamToString(activation_list));
+ value->SetInteger("priority", priority);
+ return value;
+}
+
std::unique_ptr<base::trace_event::TracedValue> Configuration::ToTracedValue()
const {
auto value = base::MakeUnique<base::trace_event::TracedValue>();
- value->SetString("activation_scope",
- StreamToString(activation_conditions.activation_scope));
- value->SetString("activation_list",
- StreamToString(activation_conditions.activation_list));
- value->SetInteger("priority", activation_conditions.priority);
+ auto traced_conditions = activation_conditions.ToTracedValue();
+ value->SetValue("activation_conditions", *traced_conditions);
value->SetString("activation_level",
StreamToString(activation_options.activation_level));
value->SetDouble("performance_measurement_rate",
@@ -335,6 +352,10 @@ std::unique_ptr<base::trace_event::TracedValue> Configuration::ToTracedValue()
activation_options.should_suppress_notifications);
value->SetBoolean("should_whitelist_site_on_reload",
activation_options.should_whitelist_site_on_reload);
+ value->SetBoolean("should_strengthen_popup_blocker",
+ activation_options.should_strengthen_popup_blocker);
+ value->SetBoolean("should_disable_ruleset_rules",
+ activation_options.should_disable_ruleset_rules);
value->SetString("ruleset_flavor",
StreamToString(general_settings.ruleset_flavor));
return value;
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 a828a4ef778..2d011a473d3 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features.h
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features.h
@@ -61,6 +61,8 @@ struct Configuration {
// there are multiple configurations whose activation conditions are
// otherwise satisfied. A greater value indicates higher priority.
int priority = 0;
+
+ std::unique_ptr<base::trace_event::TracedValue> ToTracedValue() const;
};
// The details of how subresource filtering should operate for a given main
@@ -83,6 +85,13 @@ struct Configuration {
// Whether to whitelist a site when a page loaded from that site is
// reloaded.
bool should_whitelist_site_on_reload = false;
+
+ // Whether to apply a more powerful popup blocker on pages with activation.
+ bool should_strengthen_popup_blocker = false;
+
+ // Whether to disable rules from the ruleset. In practice this might be used
+ // if e.g. only popup blocking behavior is desired.
+ bool should_disable_ruleset_rules = false;
};
// General settings that apply outside of the scope of a navigation.
@@ -201,6 +210,10 @@ extern const char kSuppressNotificationsParameterName[];
extern const char kWhitelistSiteOnReloadParameterName[];
+extern const char kStrengthenPopupBlockerParameterName[];
+
+extern const char kDisableRulesetRules[];
+
extern const char kRulesetFlavorParameterName[];
extern const char kEnablePresetsParameterName[];
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 6b7263dcd94..5ab04be3d16 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
@@ -428,6 +428,40 @@ TEST(SubresourceFilterFeaturesTest, WhitelistSiteOnReload) {
}
}
+TEST(SubresourceFilterFeaturesTest, StrengthenPopupBlocker) {
+ const struct {
+ bool feature_enabled;
+ const char* strengthen_popup_blocker_param;
+ bool expected_strengthen_popup_blocker_value;
+ } kTestCases[] = {{false, "", false},
+ {false, "true", false},
+ {false, "false", false},
+ {false, "invalid value", false},
+ {true, "", false},
+ {true, "false", false},
+ {true, "invalid value", false},
+ {true, "True", true},
+ {true, "TRUE", true},
+ {true, "true", true}};
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
+ SCOPED_TRACE(::testing::Message("StrengthenPopupBlockerParam = \"")
+ << test_case.strengthen_popup_blocker_param << "\"");
+
+ ScopedExperimentalStateToggle scoped_experimental_state(
+ test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
+ : base::FeatureList::OVERRIDE_USE_DEFAULT,
+ {{kStrengthenPopupBlockerParameterName,
+ test_case.strengthen_popup_blocker_param}});
+
+ Configuration actual_configuration;
+ ExpectAndRetrieveExactlyOneEnabledConfig(&actual_configuration);
+ EXPECT_EQ(test_case.expected_strengthen_popup_blocker_value,
+ actual_configuration.activation_options
+ .should_strengthen_popup_blocker);
+ }
+}
+
TEST(SubresourceFilterFeaturesTest, RulesetFlavor) {
const struct {
bool feature_enabled;
diff --git a/chromium/components/subresource_filter/core/common/BUILD.gn b/chromium/components/subresource_filter/core/common/BUILD.gn
index 61964f8d5e9..55111b6107f 100644
--- a/chromium/components/subresource_filter/core/common/BUILD.gn
+++ b/chromium/components/subresource_filter/core/common/BUILD.gn
@@ -13,42 +13,29 @@ static_library("common") {
"activation_scope.h",
"activation_state.cc",
"activation_state.h",
- "closed_hash_map.h",
- "copying_file_stream.cc",
- "copying_file_stream.h",
"document_load_statistics.h",
"document_subresource_filter.cc",
"document_subresource_filter.h",
"first_party_origin.cc",
"first_party_origin.h",
- "fuzzy_pattern_matching.cc",
- "fuzzy_pattern_matching.h",
"indexed_ruleset.cc",
"indexed_ruleset.h",
+ "load_policy.h",
"memory_mapped_ruleset.cc",
"memory_mapped_ruleset.h",
- "ngram_extractor.h",
"scoped_timers.h",
- "string_splitter.h",
"time_measurements.h",
- "uint64_hasher.h",
- "unindexed_ruleset.cc",
- "unindexed_ruleset.h",
- "url_pattern.cc",
- "url_pattern.h",
- "url_pattern_index.cc",
- "url_pattern_index.h",
]
public_deps = [
- "//components/subresource_filter/core/common/flat:flatbuffer",
- "//components/subresource_filter/core/common/proto:proto",
+ "//components/subresource_filter/core/common/flat:indexed_ruleset",
+ "//components/url_pattern_index",
]
deps = [
"//base",
"//net",
- "//third_party/flatbuffers:flatbuffers",
+ "//third_party/flatbuffers",
"//third_party/protobuf:protobuf_lite",
"//url",
]
@@ -61,8 +48,6 @@ static_library("test_support") {
"test_ruleset_creator.h",
"test_ruleset_utils.cc",
"test_ruleset_utils.h",
- "url_rule_test_support.cc",
- "url_rule_test_support.h",
]
deps = [
":common",
@@ -76,23 +61,17 @@ static_library("test_support") {
source_set("unit_tests") {
testonly = true
sources = [
- "closed_hash_map_unittest.cc",
"document_subresource_filter_unittest.cc",
"first_party_origin_unittest.cc",
- "fuzzy_pattern_matching_unittest.cc",
"indexed_ruleset_unittest.cc",
- "ngram_extractor_unittest.cc",
"scoped_timers_unittest.cc",
- "string_splitter_unittest.cc",
- "unindexed_ruleset_unittest.cc",
- "url_pattern_index_unittest.cc",
- "url_pattern_unittest.cc",
]
deps = [
":common",
":test_support",
"//base",
"//base/test:test_support",
+ "//components/url_pattern_index:test_support",
"//testing/gtest",
"//third_party/protobuf:protobuf_lite",
"//url",
diff --git a/chromium/components/subresource_filter/core/common/PRESUBMIT.py b/chromium/components/subresource_filter/core/common/PRESUBMIT.py
index 7e054081adb..79908dc38b3 100644
--- a/chromium/components/subresource_filter/core/common/PRESUBMIT.py
+++ b/chromium/components/subresource_filter/core/common/PRESUBMIT.py
@@ -11,10 +11,12 @@ for more details about the presubmit API built into depot_tools.
def CheckIndexedRulesetVersion(input_api, output_api):
""" Checks that IndexedRuleset format version is modified when necessary.
- Whenever a *.fbs or indexed_ruleset.cc file is touched in
- components/subresource_filter/core/common and kIndexedFormatVersion constant
- is not changed, this check returns a presubmit warning to make sure the value
- should not be updated.
+ Whenever any of the following files is changed:
+ - components/subresource_filter/core/common/indexed_ruleset.cc
+ - components/url_pattern_index/flat/*.fbs
+ - components/url_pattern_index/url_pattern_index.cc
+ and kIndexedFormatVersion constant stays intact, this check returns a
+ presubmit warning to make sure the value should not be updated.
"""
indexed_ruleset_changed = False
@@ -22,7 +24,8 @@ def CheckIndexedRulesetVersion(input_api, output_api):
for affected_file in input_api.AffectedFiles():
path = affected_file.LocalPath()
- if not 'components/subresource_filter/core/common' in path:
+ if (not 'components/subresource_filter/core/common' in path and
+ not 'components/url_pattern_index/flat' in path):
continue
basename = input_api.basename(path)
@@ -37,9 +40,9 @@ def CheckIndexedRulesetVersion(input_api, output_api):
if indexed_ruleset_changed and not indexed_ruleset_version_changed:
return [output_api.PresubmitPromptWarning(
- 'Please make sure that IndexedRuleset modifications in *.fbs and '
- 'indexed_ruleset.cc do not require updating '
- 'RulesetIndexer::kIndexedFormatVersion.')]
+ 'Please make sure that UrlPatternIndex/IndexedRuleset modifications in '
+ '*.fbs and url_pattern_index.cc/indexed_ruleset.cc do not require '
+ 'updating RulesetIndexer::kIndexedFormatVersion.')]
return []
def CheckChangeOnUpload(input_api, output_api):
diff --git a/chromium/components/subresource_filter/core/common/activation_state.cc b/chromium/components/subresource_filter/core/common/activation_state.cc
index 43e75137a7d..7daec3d82fc 100644
--- a/chromium/components/subresource_filter/core/common/activation_state.cc
+++ b/chromium/components/subresource_filter/core/common/activation_state.cc
@@ -17,7 +17,7 @@ std::unique_ptr<base::trace_event::TracedValue> ActivationState::ToTracedValue()
const {
auto value = base::MakeUnique<base::trace_event::TracedValue>();
std::ostringstream level;
- level << this;
+ level << activation_level;
value->SetString("activation_level", level.str());
value->SetBoolean("filtering_disabled_for_document",
filtering_disabled_for_document);
diff --git a/chromium/components/subresource_filter/core/common/closed_hash_map.h b/chromium/components/subresource_filter/core/common/closed_hash_map.h
deleted file mode 100644
index 7fca880dee5..00000000000
--- a/chromium/components/subresource_filter/core/common/closed_hash_map.h
+++ /dev/null
@@ -1,293 +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_CORE_COMMON_CLOSED_HASH_MAP_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_CLOSED_HASH_MAP_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <functional>
-#include <limits>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "base/bits.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/numerics/safe_conversions.h"
-
-namespace subresource_filter {
-
-template <typename KeyType_, typename Hasher_>
-class SimpleQuadraticProber;
-
-template <typename KeyType_, typename ValueType_, typename Prober_>
-class ClosedHashMap;
-
-// The default Prober used by the HashMap.
-template <typename KeyType_, typename Hasher_>
-using DefaultProber = SimpleQuadraticProber<KeyType_, Hasher_>;
-
-// The default HashMap data structure meant to be used. For more fine-grained
-// control one can use ClosedHashMap with their own Prober.
-template <typename KeyType_,
- typename ValueType_,
- typename Hasher_ = std::hash<KeyType_>>
-using HashMap =
- ClosedHashMap<KeyType_, ValueType_, DefaultProber<KeyType_, Hasher_>>;
-
-// An insert-only map implemented as a hash-table with open addressing (also
-// called closed hashing). The table can contain up to 2^30 distinct keys (or up
-// to 2^32 - 1 on 64bit systems).
-//
-// On normal operation load factor varies within range (1/4, 1/2]. The real load
-// factor can be less or equal to 1/4 if rehashing is requested explicitly to
-// allocate more memory for future insertions.
-//
-// The table discloses its internal structure in order to allow converting it to
-// different formats. It consists of 2 vectors, |hash_table| and |entries|. The
-// |entries| is a vector of pair<KeyType, ValueType>, whereas |hash_table|'s
-// elements (also called slots) are indexes into the |entries| vector. If, for
-// some i, hash_table[i] >= entries.size() then the i-th slot is empty. It is
-// guaranteed that each of the entries is referenced by exactly one table slot.
-//
-// The Prober is a strategy class used to find a slot for a particular key by
-// probing the key against a sequence of slots called a probe sequence. Often it
-// stores one or more hashers - functors used to calculate hash values of the
-// keys in order to parameterize the probe sequence. The Prober class should
-// provide the following:
-//
-// Public type(s):
-// KeyType - the type of the keys the table can store. Should be equal to the
-// ClosedHashTable's KeyType.
-//
-// Public method(s):
-// template <typename SlotCompare>
-// size_t FindSlot(const KeyType& key,
-// size_t table_size,
-// const SlotCompare& compare) const;
-//
-// Walks the probe sequence for the given |key| starting from some initial
-// slot calculated deterministically from the |key|, e.g. by computing its
-// hash code. The walk continues until it finds a hash table slot such that
-// compare(key, slot) returns true (which, for instance, can mean that the
-// slot is empty or its key is equal to |key|). Returns the index of that slot
-// in [0, table_size). The method is required to perform a finite number of
-// probes until it finds a slot.
-//
-// The same Prober class can be used to ensure compatibility when performing
-// lookups in two different representations of what is conceptually the same
-// hash map.
-//
-// TODO(pkalinnikov): Add key comparator.
-template <typename KeyType_, typename ValueType_, typename Prober_>
-class ClosedHashMap {
- public:
- using KeyType = KeyType_;
- using ValueType = ValueType_;
- using EntryType = std::pair<KeyType, ValueType>;
- using Prober = Prober_;
-
- static_assert(std::is_same<KeyType, typename Prober::KeyType>::value,
- "The KeyType should be the same in the Prober.");
-
- // Creates an empty hash-map with the default prober, and space allocated for
- // 4 distinct keys.
- ClosedHashMap() : ClosedHashMap(4) {}
-
- // Creates an empty hash-map with the specified |capacity|, so that this many
- // distinct keys can be inserted with no rehashing or reallocation taking
- // place. The |prober| is a strategy used for finding slots for the keys.
- explicit ClosedHashMap(size_t capacity, const Prober& prober = Prober())
- : hash_table_(CalculateHashTableSizeFor(capacity), EmptySlot()),
- prober_(prober) {
- entries_.reserve(capacity);
- }
-
- // Returns the number of distinct keys.
- size_t size() const { return entries_.size(); }
-
- // Returns the number of slots in the |hash_table|.
- size_t table_size() const { return hash_table_.size(); }
-
- const std::vector<uint32_t>& hash_table() const { return hash_table_; }
- const std::vector<EntryType>& entries() const { return entries_; }
-
- // Returns a pointer to the value stored for the given |key|, or nullptr if
- // the |key| is not present in the map. The returned pointer is guaranteed to
- // be valid as long as no new keys get added to the map (more strictly
- // speaking, as long as no reallocations happen for the |entries| vector).
- const ValueType* Get(const KeyType& key) const {
- const size_t entry_index = hash_table_[FindSlotForKey(key)];
- if (entry_index == EmptySlot())
- return nullptr;
- DCHECK_LT(entry_index, entries_.size());
- return &entries_[entry_index].second;
- }
-
- // Associates |value| with the given |key| if the |key| is not yet present in
- // the map, and returns true. Otherwise does nothing and returns false.
- bool Insert(const KeyType& key, ValueType value) {
- const size_t slot = FindSlotForKey(key);
- if (hash_table_[slot] != EmptySlot())
- return false;
-
- EmplaceKeyValue(slot, key, std::move(value));
- return true;
- }
-
- // Returns a reference to the value stored for the given |key|. If the |key|
- // is not present in the map, it's inserted and value-initialized. The same
- // guarantee on reference validity applies as for the result of Get(key).
- ValueType& operator[](const KeyType& key) {
- const size_t slot = FindSlotForKey(key);
- size_t entry_index = hash_table_[slot];
- if (entry_index == EmptySlot()) {
- entry_index = EmplaceKeyValue(slot, key, ValueType());
- }
-
- DCHECK_LT(entry_index, entries_.size());
- return entries_[entry_index].second;
- }
-
- // Resizes the |hash_table|, if necessary, so that at least |capacity|
- // distinct keys can be stored with the load factor being no higher than 1/2.
- void Rehash(size_t capacity) {
- if (capacity <= hash_table_.size() / 2)
- return;
- DCHECK_LE(entries_.size(), static_cast<size_t>(EmptySlot()));
-
- hash_table_.assign(CalculateHashTableSizeFor(capacity), EmptySlot());
- for (size_t index = 0; index != entries_.size(); ++index) {
- const size_t slot = FindSlotForKey(entries_[index].first);
- DCHECK_EQ(hash_table_[slot], EmptySlot());
- hash_table_[slot] = static_cast<uint32_t>(index);
- }
- }
-
- // Reserves enough space so that |capacity| distinct keys can be stored with
- // the load factor being no higher than 1/2.
- void Reserve(size_t capacity) {
- Rehash(capacity);
- entries_.reserve(capacity);
- }
-
- private:
- // The indicator of absent entry for a certain slot in |hash_table|.
- static constexpr uint32_t EmptySlot() {
- return std::numeric_limits<uint32_t>::max();
- }
-
- // Returns the number of |hash_table| slots necessary to maintain a load
- // factor between 1/4 and 1/2 for the number of distinct keys given by
- // |capacity|.
- static size_t CalculateHashTableSizeFor(size_t capacity) {
- // TODO(pkalinnikov): Implement base::bits::Log2Ceiling for arbitrary types.
- const uint32_t capacity_32 = base::checked_cast<uint32_t>(capacity);
- const int power_of_two = base::bits::Log2Ceiling(capacity_32) + 1;
- CHECK_LT(power_of_two, std::numeric_limits<size_t>::digits);
- return static_cast<size_t>(1) << power_of_two;
- }
-
- // Adds a new |key|-|value| pair to the structure. Returns the index of the
- // newly created entry. The table is rehashed if the newly created entry
- // bursts the load factor above 1/2. Otherwise the new entry is associated
- // with a specific |slot| of the table.
- size_t EmplaceKeyValue(size_t slot, KeyType key, ValueType value) {
- const size_t entry_index = entries_.size();
- CHECK_LT(entry_index, static_cast<size_t>(EmptySlot()));
- entries_.emplace_back(std::move(key), std::move(value));
-
- if (entry_index >= hash_table_.size() / 2) {
- Rehash(entry_index + 1);
- } else {
- DCHECK_LT(slot, hash_table_.size());
- DCHECK_EQ(hash_table_[slot], EmptySlot());
- hash_table_[slot] = static_cast<uint32_t>(entry_index);
- }
-
- return entry_index;
- }
-
- // Finds a slot such that it's either empty (indicating that the |key| is not
- // stored) or contains the |key|.
- size_t FindSlotForKey(const KeyType& key) const {
- return prober_.FindSlot(key, hash_table_.size(), [this](const KeyType& key,
- size_t slot_index) {
- DCHECK_LT(slot_index, hash_table_.size());
- const uint32_t entry_index = hash_table_[slot_index];
- DCHECK(entry_index == EmptySlot() || entry_index < entries_.size());
- return entry_index == EmptySlot() || entries_[entry_index].first == key;
- });
- }
-
- // Contains indices into |entries_|, or EmptySlot() for free table slots.
- // Always contains at least twice as many slots as there are elements in
- // |entries_|, i.e. the load factor is always less than or equal to 1/2.
- std::vector<uint32_t> hash_table_;
-
- // Contains all the inserted key-value pairs. The keys are unique. The size of
- // the vector is never greater than EmptySlot().
- std::vector<EntryType> entries_;
-
- // The strategy used to find a slot for a key.
- Prober prober_;
-
- DISALLOW_COPY_AND_ASSIGN(ClosedHashMap);
-};
-
-// The implementation of Prober that uses a simple form of quadratic probing.
-// That is, for a given |key| the sequence of probes is the following (modulo
-// the hash table size):
-// hash(key), hash(key) + 1, hash(key) + 3, ..., hash(key) + k * (k - 1) / 2
-//
-// To use this prober the hash table should maintain its size M equal to powers
-// of two. It can be shown that in this case the aforementioned quadratic probe
-// sequence for k <= M visits k distinct table slots.
-//
-// Template parameters:
-// KeyType_ - the type of the table's keys.
-// Hasher_ - the type of the functor used to calculate hashes of keys.
-template <typename KeyType_, typename Hasher_>
-class SimpleQuadraticProber {
- public:
- using KeyType = KeyType_;
- using Hasher = Hasher_;
-
- // Constructs the prober that uses the |hasher| functor to hash the keys.
- explicit SimpleQuadraticProber(const Hasher& hasher = Hasher())
- : hasher_(hasher) {}
-
- // Returns the slot index for the |key|. Requires that |compare| returns true
- // for at least one slot index between 0 and |table_size| - 1.
- template <typename SlotCompare>
- size_t FindSlot(const KeyType& key,
- size_t table_size,
- const SlotCompare& compare) const {
- DCHECK_GT(table_size, 0u);
- DCHECK_EQ(table_size & (table_size - 1), 0u);
- const size_t kMask = table_size - 1;
-
- size_t slot_index = hasher_(key) & kMask;
- // The loop will always be finite, since |compare| is guaranteed to return
- // true for at least 1 slot index, and the probe sequence visits all slots.
- for (size_t step_size = 1; !compare(key, slot_index); ++step_size) {
- DCHECK_LT(step_size, table_size);
- slot_index = (slot_index + step_size) & kMask;
- }
-
- DCHECK_LT(slot_index, table_size);
- return slot_index;
- }
-
- private:
- // The functor used to calculate hashes of keys.
- Hasher hasher_;
-};
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_CLOSED_HASH_MAP_H_
diff --git a/chromium/components/subresource_filter/core/common/closed_hash_map_unittest.cc b/chromium/components/subresource_filter/core/common/closed_hash_map_unittest.cc
deleted file mode 100644
index 1987c568fee..00000000000
--- a/chromium/components/subresource_filter/core/common/closed_hash_map_unittest.cc
+++ /dev/null
@@ -1,166 +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/core/common/closed_hash_map.h"
-
-#include <string>
-#include <vector>
-
-#include "components/subresource_filter/core/common/uint64_hasher.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace subresource_filter {
-
-namespace {
-
-template <typename MapType>
-void ExpectHashMapIntegrity(const MapType& map, size_t min_capacity = 0) {
- EXPECT_EQ(map.entries().size(), map.size());
- EXPECT_EQ(map.hash_table().size(), map.table_size());
- EXPECT_LE(map.size() * 2, map.table_size());
- EXPECT_LE(min_capacity * 2, map.table_size());
-
- std::vector<bool> entry_is_referenced(map.size());
- for (size_t i = 0; i < map.table_size(); ++i) {
- SCOPED_TRACE(testing::Message() << "Hash-table slot: " << i);
-
- const uint32_t entry_index = map.hash_table()[i];
- if (static_cast<size_t>(entry_index) >= map.size())
- continue;
- EXPECT_FALSE(entry_is_referenced[entry_index]);
- entry_is_referenced[entry_index] = true;
- }
-
- for (size_t i = 0; i < map.size(); ++i) {
- SCOPED_TRACE(testing::Message() << "Hash-table entry index: " << i);
- EXPECT_TRUE(entry_is_referenced[i]);
- }
-}
-
-template <typename MapType>
-void ExpectEmptyMap(const MapType& map, size_t min_capacity) {
- ExpectHashMapIntegrity(map, min_capacity);
- EXPECT_EQ(0u, map.size());
-}
-
-} // namespace
-
-TEST(ClosedHashMapTest, EmptyMapDefault) {
- HashMap<int, int> hm;
- ExpectEmptyMap(hm, 0);
- EXPECT_EQ(nullptr, hm.Get(0));
- EXPECT_EQ(nullptr, hm.Get(100500));
- EXPECT_GT(hm.table_size(), 0u);
-}
-
-TEST(ClosedHashMapTest, EmptyMapWithCapacity) {
- HashMap<int, int> hm(100);
- ExpectEmptyMap(hm, 100);
- EXPECT_EQ(nullptr, hm.Get(0));
- EXPECT_EQ(nullptr, hm.Get(100500));
-}
-
-TEST(ClosedHashMapTest, InsertDistinctAndGet) {
- HashMap<int, int> hm;
- static const int kKeys[] = {1, 5, 10, 3, -100500};
- for (int key : kKeys) {
- EXPECT_TRUE(hm.Insert(key, -key));
- ExpectHashMapIntegrity(hm);
- }
- for (int key : kKeys) {
- const int* value = hm.Get(key);
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(-key, *value);
- }
- EXPECT_EQ(nullptr, hm.Get(1234567));
-}
-
-TEST(ClosedHashMapTest, InsertExistingAndGet) {
- HashMap<int, int> hm;
-
- EXPECT_TRUE(hm.Insert(123, -123));
- EXPECT_FALSE(hm.Insert(123, -124));
- ExpectHashMapIntegrity(hm);
-
- const int* value = hm.Get(123);
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(-123, *value);
-}
-
-TEST(ClosedHashMapTest, InsertManyKeysWithCustomHasher) {
- using CustomProber = SimpleQuadraticProber<uint64_t, Uint64Hasher>;
-
- ClosedHashMap<uint64_t, std::string, CustomProber> hm;
- ExpectEmptyMap(hm, 0);
-
- std::vector<std::pair<uint64_t, std::string>> entries;
- for (int key = 10, i = 0; key < 1000000; key += ++i) {
- entries.push_back(std::make_pair(key, std::to_string(key)));
- }
-
- size_t expected_size = 0;
- for (const auto& entry : entries) {
- EXPECT_TRUE(hm.Insert(entry.first, entry.second));
- EXPECT_FALSE(hm.Insert(entry.first, "-1"));
- ++expected_size;
-
- EXPECT_EQ(expected_size, hm.size());
- EXPECT_LE(expected_size * 2, hm.table_size());
- }
- ExpectHashMapIntegrity(hm);
-
- for (const auto& entry : entries) {
- const std::string* value = hm.Get(entry.first);
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(entry.second, *value);
- }
-}
-
-TEST(ClosedHashMapTest, OperatorBrackets) {
- HashMap<int, int> hm;
-
- for (int i = 0; i < 5; ++i) {
- const size_t expected_size = i ? 1 : 0;
- EXPECT_EQ(expected_size, hm.size());
-
- int expected_value = (i + 1) * 10;
- hm[123] = expected_value;
- EXPECT_EQ(1u, hm.size());
-
- const int* value_ptr = hm.Get(123);
- ASSERT_NE(nullptr, value_ptr);
- EXPECT_EQ(expected_value, *value_ptr);
-
- EXPECT_EQ(expected_value, hm[123]);
- EXPECT_EQ(1u, hm.size());
-
- expected_value *= 100;
- hm[123] = expected_value;
- EXPECT_EQ(expected_value, hm[123]);
- }
-}
-
-TEST(ClosedHashMapTest, ManualRehash) {
- HashMap<int, int> hm(3);
- const size_t expected_table_size = hm.table_size();
-
- static const int kKeys[] = {1, 5, 10};
- for (int key : kKeys) {
- EXPECT_TRUE(hm.Insert(key, -key));
- }
- // No rehashing occurred.
- EXPECT_EQ(expected_table_size, hm.table_size());
-
- for (int i = 1; i <= 2; ++i) {
- for (int key : kKeys) {
- const int* value = hm.Get(key);
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(-key, *value);
- }
- hm.Rehash(100 * i);
- ExpectHashMapIntegrity(hm, 100 * i);
- }
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/copying_file_stream.cc b/chromium/components/subresource_filter/core/common/copying_file_stream.cc
deleted file mode 100644
index 5d4b82bfbf8..00000000000
--- a/chromium/components/subresource_filter/core/common/copying_file_stream.cc
+++ /dev/null
@@ -1,31 +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/core/common/copying_file_stream.h"
-
-namespace subresource_filter {
-
-// CopyingFileInputStream ------------------------------------------------------
-
-CopyingFileInputStream::~CopyingFileInputStream() = default;
-
-CopyingFileInputStream::CopyingFileInputStream(base::File file)
- : file_(std::move(file)) {}
-
-int CopyingFileInputStream::Read(void* buffer, int size) {
- return file_.ReadAtCurrentPosNoBestEffort(static_cast<char*>(buffer), size);
-}
-
-// CopyingFileOutputStream -----------------------------------------------------
-CopyingFileOutputStream::~CopyingFileOutputStream() = default;
-
-CopyingFileOutputStream::CopyingFileOutputStream(base::File file)
- : file_(std::move(file)) {}
-
-bool CopyingFileOutputStream::Write(const void* buffer, int size) {
- return file_.WriteAtCurrentPos(static_cast<const char*>(buffer), size) ==
- size;
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/copying_file_stream.h b/chromium/components/subresource_filter/core/common/copying_file_stream.h
deleted file mode 100644
index f8318163da4..00000000000
--- a/chromium/components/subresource_filter/core/common/copying_file_stream.h
+++ /dev/null
@@ -1,51 +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_CORE_COMMON_COPYING_FILE_STREAM_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_COPYING_FILE_STREAM_H_
-
-#include "base/files/file.h"
-#include "base/macros.h"
-#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
-
-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
-// the ZeroCopyInputStream interface.
-class CopyingFileInputStream : public google::protobuf::io::CopyingInputStream {
- public:
- explicit CopyingFileInputStream(base::File file);
- ~CopyingFileInputStream() override;
-
- // google::protobuf::io::CopyingInputStream:
- int Read(void* buffer, int size) override;
-
- private:
- base::File file_;
-
- DISALLOW_COPY_AND_ASSIGN(CopyingFileInputStream);
-};
-
-// Implements a CopyingOutputStream that writes to a base::File. Can be used in
-// combination with CopyingOutputStreamAdaptor for writing to that file through
-// the ZeroCopyOutputStream interface.
-class CopyingFileOutputStream
- : public google::protobuf::io::CopyingOutputStream {
- public:
- explicit CopyingFileOutputStream(base::File file);
- ~CopyingFileOutputStream() override;
-
- // google::protobuf::io::CopyingOutputStream:
- bool Write(const void* buffer, int size) override;
-
- private:
- base::File file_;
-
- DISALLOW_COPY_AND_ASSIGN(CopyingFileOutputStream);
-};
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_COPYING_FILE_STREAM_H_
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 3895a3a88f8..2dcbaaa5292 100644
--- a/chromium/components/subresource_filter/core/common/document_subresource_filter.cc
+++ b/chromium/components/subresource_filter/core/common/document_subresource_filter.cc
@@ -17,36 +17,6 @@
namespace subresource_filter {
-ActivationState ComputeActivationState(
- const GURL& document_url,
- const url::Origin& parent_document_origin,
- const ActivationState& parent_activation_state,
- const MemoryMappedRuleset* ruleset) {
- DCHECK(ruleset);
- SCOPED_UMA_HISTOGRAM_MICRO_TIMER(
- "SubresourceFilter.DocumentLoad.Activation.WallDuration");
- SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(
- "SubresourceFilter.DocumentLoad.Activation.CPUDuration");
-
- IndexedRulesetMatcher matcher(ruleset->data(), ruleset->length());
- ActivationState activation_state = parent_activation_state;
- if (activation_state.filtering_disabled_for_document)
- return activation_state;
-
- // TODO(pkalinnikov): Match several activation types in a batch.
- if (matcher.ShouldDisableFilteringForDocument(
- document_url, parent_document_origin,
- proto::ACTIVATION_TYPE_DOCUMENT)) {
- activation_state.filtering_disabled_for_document = true;
- } else if (!activation_state.generic_blocking_rules_disabled &&
- matcher.ShouldDisableFilteringForDocument(
- document_url, parent_document_origin,
- proto::ACTIVATION_TYPE_GENERICBLOCK)) {
- activation_state.generic_blocking_rules_disabled = true;
- }
- return activation_state;
-}
-
DocumentSubresourceFilter::DocumentSubresourceFilter(
url::Origin document_origin,
ActivationState activation_state,
@@ -63,7 +33,7 @@ DocumentSubresourceFilter::~DocumentSubresourceFilter() = default;
LoadPolicy DocumentSubresourceFilter::GetLoadPolicy(
const GURL& subresource_url,
- proto::ElementType subresource_type) {
+ url_pattern_index::proto::ElementType subresource_type) {
TRACE_EVENT1("loader", "DocumentSubresourceFilter::GetLoadPolicy", "url",
subresource_url.spec());
diff --git a/chromium/components/subresource_filter/core/common/document_subresource_filter.h b/chromium/components/subresource_filter/core/common/document_subresource_filter.h
index 611707d9c3d..66496e3bfa8 100644
--- a/chromium/components/subresource_filter/core/common/document_subresource_filter.h
+++ b/chromium/components/subresource_filter/core/common/document_subresource_filter.h
@@ -16,7 +16,8 @@
#include "components/subresource_filter/core/common/activation_state.h"
#include "components/subresource_filter/core/common/document_load_statistics.h"
#include "components/subresource_filter/core/common/indexed_ruleset.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
+#include "components/subresource_filter/core/common/load_policy.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
class GURL;
@@ -29,22 +30,6 @@ namespace subresource_filter {
class FirstPartyOrigin;
class MemoryMappedRuleset;
-enum class LoadPolicy {
- ALLOW,
- DISALLOW,
- WOULD_DISALLOW,
-};
-
-// Computes whether/how subresource filtering should be activated while loading
-// |document_url| in a frame, based on the parent document's |activation_state|,
-// the |parent_document_origin|, as well as any applicable deactivation rules in
-// non-null |ruleset|.
-ActivationState ComputeActivationState(
- const GURL& document_url,
- const url::Origin& parent_document_origin,
- const ActivationState& parent_activation_state,
- const MemoryMappedRuleset* ruleset);
-
// Performs filtering of subresource loads in the scope of a given document.
class DocumentSubresourceFilter {
public:
@@ -59,7 +44,7 @@ class DocumentSubresourceFilter {
~DocumentSubresourceFilter();
- ActivationState activation_state() const { return activation_state_; }
+ const ActivationState& activation_state() const { return activation_state_; }
const DocumentLoadStatistics& statistics() const { return statistics_; }
// WARNING: This is only to allow DocumentSubresourceFilter's wrappers to
@@ -67,8 +52,9 @@ class DocumentSubresourceFilter {
// TODO(pkalinnikov): Find a better way to achieve this.
DocumentLoadStatistics& statistics() { return statistics_; }
- LoadPolicy GetLoadPolicy(const GURL& subresource_url,
- proto::ElementType subresource_type);
+ LoadPolicy GetLoadPolicy(
+ const GURL& subresource_url,
+ url_pattern_index::proto::ElementType subresource_type);
private:
const ActivationState activation_state_;
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 f447177ab8d..2ecfcf54f3d 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
@@ -16,6 +16,8 @@
namespace subresource_filter {
+namespace proto = url_pattern_index::proto;
+
namespace {
constexpr auto kDryRun = ActivationLevel::DRYRUN;
@@ -128,117 +130,4 @@ TEST_F(DocumentSubresourceFilterTest, Enabled) {
test_impl(false /* measure_performance */);
}
-// Tests for ComputeActivationState functions. ---------------------------------
-
-class SubresourceFilterComputeActivationStateTest : public ::testing::Test {
- public:
- SubresourceFilterComputeActivationStateTest() {}
-
- protected:
- void SetUp() override {
- constexpr int32_t kDocument = proto::ACTIVATION_TYPE_DOCUMENT;
- constexpr int32_t kGenericBlock = proto::ACTIVATION_TYPE_GENERICBLOCK;
-
- std::vector<proto::UrlRule> rules;
- rules.push_back(testing::CreateWhitelistRuleForDocument(
- "child1.com", kDocument, {"parent1.com", "parent2.com"}));
- rules.push_back(testing::CreateWhitelistRuleForDocument(
- "child2.com", kGenericBlock, {"parent1.com", "parent2.com"}));
- rules.push_back(testing::CreateWhitelistRuleForDocument(
- "child3.com", kDocument | kGenericBlock,
- {"parent1.com", "parent2.com"}));
-
- testing::TestRulesetPair test_ruleset_pair;
- ASSERT_NO_FATAL_FAILURE(test_ruleset_creator_.CreateRulesetWithRules(
- rules, &test_ruleset_pair));
- ruleset_ = new MemoryMappedRuleset(
- testing::TestRuleset::Open(test_ruleset_pair.indexed));
- }
-
- static ActivationState MakeState(
- bool filtering_disabled_for_document,
- bool generic_blocking_rules_disabled = false,
- ActivationLevel activation_level = kEnabled) {
- ActivationState activation_state(activation_level);
- activation_state.filtering_disabled_for_document =
- filtering_disabled_for_document;
- activation_state.generic_blocking_rules_disabled =
- generic_blocking_rules_disabled;
- return activation_state;
- }
-
- const MemoryMappedRuleset* ruleset() { return ruleset_.get(); }
-
- private:
- testing::TestRulesetCreator test_ruleset_creator_;
- scoped_refptr<const MemoryMappedRuleset> ruleset_;
-
- DISALLOW_COPY_AND_ASSIGN(SubresourceFilterComputeActivationStateTest);
-};
-
-TEST_F(SubresourceFilterComputeActivationStateTest,
- ActivationBitsCorrectlyPropagateToChildDocument) {
- // Make sure that the |generic_blocking_rules_disabled| flag is disregarded
- // when |filtering_disabled_for_document| is true.
- ASSERT_EQ(MakeState(true, false), MakeState(true, true));
-
- // TODO(pkalinnikov): Find a short way to express all these tests.
- const struct {
- const char* document_url;
- const char* parent_document_origin;
- ActivationState parent_activation;
- ActivationState expected_activation_state;
- } kTestCases[] = {
- {"http://example.com", "http://example.com", MakeState(false, false),
- MakeState(false, false)},
- {"http://example.com", "http://example.com", MakeState(false, true),
- MakeState(false, true)},
- {"http://example.com", "http://example.com", MakeState(true, false),
- MakeState(true)},
- {"http://example.com", "http://example.com", MakeState(true, true),
- MakeState(true)},
-
- {"http://child1.com", "http://parrrrent1.com", MakeState(false, false),
- MakeState(false, false)},
- {"http://child1.com", "http://parent1.com", MakeState(false, false),
- MakeState(true, false)},
- {"http://child1.com", "http://parent2.com", MakeState(false, false),
- MakeState(true, false)},
- {"http://child1.com", "http://parent2.com", MakeState(true, false),
- MakeState(true)},
- {"http://child1.com", "http://parent2.com", MakeState(false, true),
- MakeState(true)},
-
- {"http://child2.com", "http://parent1.com", MakeState(false, false),
- MakeState(false, true)},
- {"http://child2.com", "http://parent1.com", MakeState(false, true),
- MakeState(false, true)},
- {"http://child2.com", "http://parent1.com", MakeState(true, false),
- MakeState(true)},
- {"http://child2.com", "http://parent1.com", MakeState(true, true),
- MakeState(true)},
-
- {"http://child3.com", "http://parent1.com", MakeState(false, false),
- MakeState(true)},
- {"http://child3.com", "http://parent1.com", MakeState(false, true),
- MakeState(true)},
- {"http://child3.com", "http://parent1.com", MakeState(true, false),
- MakeState(true)},
- {"http://child3.com", "http://parent1.com", MakeState(true, true),
- MakeState(true)},
- };
-
- for (size_t i = 0, size = arraysize(kTestCases); i != size; ++i) {
- SCOPED_TRACE(::testing::Message() << "Test number: " << i);
- const auto& test_case = kTestCases[i];
-
- GURL document_url(test_case.document_url);
- url::Origin parent_document_origin(GURL(test_case.parent_document_origin));
- ActivationState activation_state =
- ComputeActivationState(document_url, parent_document_origin,
- test_case.parent_activation, ruleset());
- EXPECT_EQ(test_case.expected_activation_state, activation_state);
- }
-}
-
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/flat/BUILD.gn b/chromium/components/subresource_filter/core/common/flat/BUILD.gn
index d554b35a338..d46e71cc83d 100644
--- a/chromium/components/subresource_filter/core/common/flat/BUILD.gn
+++ b/chromium/components/subresource_filter/core/common/flat/BUILD.gn
@@ -4,9 +4,11 @@
import("//third_party/flatbuffers/flatbuffer.gni")
-flatbuffer("flatbuffer") {
+flatbuffer("indexed_ruleset") {
sources = [
"indexed_ruleset.fbs",
- "url_pattern_index.fbs",
+ ]
+ public_deps = [
+ "//components/url_pattern_index/flat:url_pattern_index",
]
}
diff --git a/chromium/components/subresource_filter/core/common/flat/indexed_ruleset.fbs b/chromium/components/subresource_filter/core/common/flat/indexed_ruleset.fbs
index 91262283f09..6401486d99f 100644
--- a/chromium/components/subresource_filter/core/common/flat/indexed_ruleset.fbs
+++ b/chromium/components/subresource_filter/core/common/flat/indexed_ruleset.fbs
@@ -2,21 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// TODO(pkalinnikov): Make including by absolute path work.
-include "url_pattern_index.fbs";
+include "components/url_pattern_index/flat/url_pattern_index.fbs";
namespace subresource_filter.flat;
// The top-level data structure used to store URL rules.
table IndexedRuleset {
// The index of all blacklist URL rules.
- blacklist_index : UrlPatternIndex;
+ blacklist_index : url_pattern_index.flat.UrlPatternIndex;
// The index of all whitelist URL rules, except pure deactivation rules.
- whitelist_index : UrlPatternIndex;
+ whitelist_index : url_pattern_index.flat.UrlPatternIndex;
// The index of all whitelist URL rules with activation options.
- deactivation_index : UrlPatternIndex;
+ deactivation_index : url_pattern_index.flat.UrlPatternIndex;
}
root_type IndexedRuleset;
diff --git a/chromium/components/subresource_filter/core/common/flat/url_pattern_index.fbs b/chromium/components/subresource_filter/core/common/flat/url_pattern_index.fbs
deleted file mode 100644
index ace4a0566dc..00000000000
--- a/chromium/components/subresource_filter/core/common/flat/url_pattern_index.fbs
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-namespace subresource_filter.flat;
-
-// Corresponds to subresource_filter::proto::UrlPatternType.
-enum UrlPatternType : ubyte {
- SUBSTRING,
- WILDCARDED,
- REGEXP,
-}
-
-// Corresponds to subresource_filter::proto::AnchorType.
-enum AnchorType : ubyte {
- NONE,
- BOUNDARY,
- SUBDOMAIN,
-}
-
-// URL rule matching options. These correspond to multiple fields of
-// subresource_filter::proto::UrlRule, but here, they are represented as flags
-// of the same bitmask to allow for compact storage.
-enum OptionFlag : ubyte (bit_flags) {
- IS_WHITELIST,
- APPLIES_TO_FIRST_PARTY,
- APPLIES_TO_THIRD_PARTY,
- IS_MATCH_CASE,
-}
-
-// The flat representation of a single URL rule. For more details regarding the
-// fields please see the comments to subresource_filter::proto::UrlRule.
-table UrlRule {
- // Rule matching options, a bitmask consisting of OptionFlags.
- options : ubyte;
-
- // A bitmask of element types, same as proto::UrlRule::element_types. Enables
- // all element types except POPUP by default.
- // Note: Keep it equal to ELEMENT_TYPE_ALL-ELEMENT_TYPE_POPUP for compactness.
- element_types : ushort = 6143;
-
- // A bitmask of activation types, same as proto::UrlRule::activation_types.
- // Disables all activation types by default.
- activation_types : ubyte = 0;
-
- // Use SUBSTRING as default, since it's the most used pattern type. Same as
- // the corresponding proto::UrlRule::url_pattern_type.
- url_pattern_type : UrlPatternType = SUBSTRING;
-
- // Use NONE as default, since most of the rules are not anchored.
- anchor_left : AnchorType = NONE;
- anchor_right : AnchorType = NONE;
-
- // The list of domains to be included/excluded from the filter's affected set.
- domains_included : [string];
- domains_excluded : [string];
-
- // A URL pattern in the format defined by |url_pattern_type|.
- url_pattern : string;
-}
-
-// Contains an N-gram (acting as a key in a hash table) and a list of URL rules
-// associated with that N-gram.
-table NGramToRules {
- // A string consisting of N (up to 8) non-special characters, which are stored
- // in the lowest N non-zero bytes, lower bytes corresponding to later symbols.
- ngram : ulong;
-
- // The list of rules containing |ngram| as a substring of their URL pattern.
- rule_list : [UrlRule];
-}
-
-// A data structure used to select only a handful of URL rule candidates that
-// need to be matched against a certain resource URL.
-table UrlPatternIndex {
- // The N of an N-gram index. Note: |n| should be between 1 and 8.
- n : uint;
-
- // A hash table with open addressing. The keys of the table are N-grams.
- ngram_index : [NGramToRules];
-
- // The slot that is pointed to by all empty slots of |ngram_index| hash table.
- // Note: This is a workaround needed because null offsets are not allowed as
- // elements of FlatBuffer arrays.
- ngram_index_empty_slot : NGramToRules;
-
- // A list storing the rules that doesn't contain any valid N-grams in their
- // URL patterns. Contains all the REGEXP rules as well.
- // TODO(pkalinnikov): Think about better implementation for the fallback
- // index. Possibly make it a hash map and maybe merge it with the N-gram
- // index, since we can treat any sequence of characters shorter than N as an
- // N-gram with zero bytes used for padding.
- fallback_rules : [UrlRule];
-}
-
-root_type UrlPatternIndex;
diff --git a/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.cc b/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.cc
deleted file mode 100644
index f0a4dfd50aa..00000000000
--- a/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.cc
+++ /dev/null
@@ -1,59 +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/core/common/fuzzy_pattern_matching.h"
-
-#include <algorithm>
-
-namespace subresource_filter {
-
-namespace {
-
-bool StartsWithFuzzyImpl(base::StringPiece text, base::StringPiece subpattern) {
- DCHECK_LE(subpattern.size(), text.size());
-
- for (size_t i = 0; i != subpattern.size(); ++i) {
- const char text_char = text[i];
- const char pattern_char = subpattern[i];
- if (text_char != pattern_char &&
- (pattern_char != kSeparatorPlaceholder || !IsSeparator(text_char))) {
- return false;
- }
- }
- return true;
-}
-
-} // namespace
-
-bool StartsWithFuzzy(base::StringPiece text, base::StringPiece subpattern) {
- return subpattern.size() <= text.size() &&
- StartsWithFuzzyImpl(text, subpattern);
-}
-
-bool EndsWithFuzzy(base::StringPiece text, base::StringPiece subpattern) {
- return subpattern.size() <= text.size() &&
- StartsWithFuzzyImpl(text.substr(text.size() - subpattern.size()),
- subpattern);
-}
-
-size_t FindFuzzy(base::StringPiece text,
- base::StringPiece subpattern,
- size_t from) {
- if (from > text.size())
- return base::StringPiece::npos;
- if (subpattern.empty())
- return from;
-
- auto fuzzy_compare = [](char text_char, char subpattern_char) {
- return text_char == subpattern_char ||
- (subpattern_char == kSeparatorPlaceholder && IsSeparator(text_char));
- };
-
- base::StringPiece::const_iterator found =
- std::search(text.begin() + from, text.end(), subpattern.begin(),
- subpattern.end(), fuzzy_compare);
- return found == text.end() ? base::StringPiece::npos : found - text.begin();
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.h b/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.h
deleted file mode 100644
index 68472e76ae6..00000000000
--- a/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching.h
+++ /dev/null
@@ -1,68 +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.
-
-// The separator placeholder '^' symbol is used in subpatterns to match any
-// separator character, which is any ASCII symbol except letters, digits, and
-// the following: '_', '-', '.', '%'. Note that the separator placeholder
-// character '^' is itself a separator, as well as '\0'.
-// TODO(pkalinnikov): In addition, a separator placeholder at the end of the
-// pattern can be matched by the end of |text|.
-//
-// We define a fuzzy occurrence as an occurrence of a |subpattern| in |text|
-// such that all its non-placeholder characters are equal to the corresponding
-// characters of the |text|, whereas each '^' placeholder can correspond to any
-// type of separator in |text|.
-
-#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_FUZZY_PATTERN_MATCHING_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_FUZZY_PATTERN_MATCHING_H_
-
-#include <stddef.h>
-
-#include "base/strings/string_piece.h"
-
-namespace subresource_filter {
-
-constexpr char kSeparatorPlaceholder = '^';
-
-inline bool IsAscii(char c) {
- return !(c & ~0x7F);
-}
-
-inline bool IsAlphaNumericAscii(char c) {
- if (c <= '9')
- return c >= '0';
- c |= 0x20; // Puts all alphabetics (and only them) into the 'a'-'z' range.
- return c >= 'a' && c <= 'z';
-}
-
-// Returns whether |c| is a separator.
-inline bool IsSeparator(char c) {
- switch (c) {
- case '_':
- case '-':
- case '.':
- case '%':
- return false;
- case kSeparatorPlaceholder:
- return true;
- default:
- return !IsAlphaNumericAscii(c) && IsAscii(c);
- }
-}
-
-// Returns whether |text| starts with a fuzzy occurrence of |subpattern|.
-bool StartsWithFuzzy(base::StringPiece text, base::StringPiece subpattern);
-
-// Returns whether |text| ends with a fuzzy occurrence of |subpattern|.
-bool EndsWithFuzzy(base::StringPiece text, base::StringPiece subpattern);
-
-// Returns the position of the leftmost fuzzy occurrence of a |subpattern| in
-// the |text| starting no earlier than |from| the specified position.
-size_t FindFuzzy(base::StringPiece text,
- base::StringPiece subpattern,
- size_t from = 0);
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_FUZZY_PATTERN_MATCHING_H_
diff --git a/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching_unittest.cc b/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching_unittest.cc
deleted file mode 100644
index 0aab4832d2c..00000000000
--- a/chromium/components/subresource_filter/core/common/fuzzy_pattern_matching_unittest.cc
+++ /dev/null
@@ -1,108 +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/core/common/fuzzy_pattern_matching.h"
-
-#include <vector>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace subresource_filter {
-
-TEST(SubresourceFilterFuzzyPatternMatchingTest, StartsWithFuzzy) {
- const struct {
- const char* text;
- const char* subpattern;
- bool expected_starts_with;
- } kTestCases[] = {
- {"abc", "", true}, {"abc", "a", true}, {"abc", "ab", true},
- {"abc", "abc", true}, {"abc", "abcd", false}, {"abc", "abc^^", false},
- {"abc", "abcd^", false}, {"abc", "ab^", false}, {"abc", "bc", false},
- {"abc", "bc^", false}, {"abc", "^abc", false},
- };
- // TODO(pkalinnikov): Make end-of-string match '^' again.
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(testing::Message()
- << "Test: " << test_case.text
- << "; Subpattern: " << test_case.subpattern);
-
- const bool starts_with =
- StartsWithFuzzy(test_case.text, test_case.subpattern);
- EXPECT_EQ(test_case.expected_starts_with, starts_with);
- }
-}
-
-TEST(SubresourceFilterFuzzyPatternMatchingTest, EndsWithFuzzy) {
- const struct {
- const char* text;
- const char* subpattern;
- bool expected_ends_with;
- } kTestCases[] = {
- {"abc", "", true}, {"abc", "c", true}, {"abc", "bc", true},
- {"abc", "abc", true}, {"abc", "0abc", false}, {"abc", "abc^^", false},
- {"abc", "abcd^", false}, {"abc", "ab^", false}, {"abc", "ab", false},
- {"abc", "^abc", false},
- };
- // TODO(pkalinnikov): Make end-of-string match '^' again.
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(testing::Message()
- << "Test: " << test_case.text
- << "; Subpattern: " << test_case.subpattern);
-
- const bool ends_with = EndsWithFuzzy(test_case.text, test_case.subpattern);
- EXPECT_EQ(test_case.expected_ends_with, ends_with);
- }
-}
-
-TEST(SubresourceFilterFuzzyPatternMatchingTest, FindFuzzy) {
- const struct {
- base::StringPiece text;
- base::StringPiece subpattern;
- std::vector<size_t> expected_occurrences;
- } kTestCases[] = {
- {"abcd", "", {0, 1, 2, 3, 4}},
- {"abcd", "de", std::vector<size_t>()},
- {"abcd", "ab", {0}},
- {"abcd", "bc", {1}},
- {"abcd", "cd", {2}},
-
- {"a/bc/a/b", "", {0, 1, 2, 3, 4, 5, 6, 7, 8}},
- {"a/bc/a/b", "de", std::vector<size_t>()},
- {"a/bc/a/b", "a/", {0, 5}},
- {"a/bc/a/c", "a/c", {5}},
- {"a/bc/a/c", "a^c", {5}},
- {"a/bc/a/c", "a?c", std::vector<size_t>()},
-
- {"ab^cd", "ab/cd", std::vector<size_t>()},
- {"ab^cd", "b/c", std::vector<size_t>()},
- {"ab^cd", "ab^cd", {0}},
- {"ab^cd", "b^c", {1}},
- {"ab^b/b", "b/b", {3}},
-
- {"a/a/a/a", "a/a", {0, 2, 4}},
- {"a/a/a/a", "^a^a^a", {1}},
- {"a/a/a/a", "^a^a?a", std::vector<size_t>()},
- {"a/a/a/a", "?a?a?a", std::vector<size_t>()},
- };
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(testing::Message()
- << "Test: " << test_case.text
- << "; Subpattern: " << test_case.subpattern);
-
- std::vector<size_t> occurrences;
- for (size_t position = 0; position <= test_case.text.size(); ++position) {
- position = FindFuzzy(test_case.text, test_case.subpattern, position);
- if (position == base::StringPiece::npos)
- break;
- occurrences.push_back(position);
- }
-
- EXPECT_EQ(test_case.expected_occurrences, occurrences);
- }
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/indexed_ruleset.cc b/chromium/components/subresource_filter/core/common/indexed_ruleset.cc
index edbbee88c9d..009258e7081 100644
--- a/chromium/components/subresource_filter/core/common/indexed_ruleset.cc
+++ b/chromium/components/subresource_filter/core/common/indexed_ruleset.cc
@@ -11,10 +11,12 @@
namespace subresource_filter {
+namespace proto = url_pattern_index::proto;
+
// RulesetIndexer --------------------------------------------------------------
// static
-const int RulesetIndexer::kIndexedFormatVersion = 17;
+const int RulesetIndexer::kIndexedFormatVersion = 18;
RulesetIndexer::RulesetIndexer()
: blacklist_(&builder_), whitelist_(&builder_), deactivation_(&builder_) {}
@@ -22,7 +24,7 @@ RulesetIndexer::RulesetIndexer()
RulesetIndexer::~RulesetIndexer() = default;
bool RulesetIndexer::AddUrlRule(const proto::UrlRule& rule) {
- const UrlRuleOffset offset = SerializeUrlRule(rule, &builder_);
+ const auto offset = url_pattern_index::SerializeUrlRule(rule, &builder_);
// Note: A zero offset.o means a "nullptr" offset. It is returned when the
// rule has not been serialized.
if (!offset.o)
diff --git a/chromium/components/subresource_filter/core/common/indexed_ruleset.h b/chromium/components/subresource_filter/core/common/indexed_ruleset.h
index 00588f8a0a7..defd8023d16 100644
--- a/chromium/components/subresource_filter/core/common/indexed_ruleset.h
+++ b/chromium/components/subresource_filter/core/common/indexed_ruleset.h
@@ -11,7 +11,7 @@
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
#include "components/subresource_filter/core/common/flat/indexed_ruleset_generated.h"
-#include "components/subresource_filter/core/common/url_pattern_index.h"
+#include "components/url_pattern_index/url_pattern_index.h"
#include "third_party/flatbuffers/src/include/flatbuffers/flatbuffers.h"
class GURL;
@@ -20,13 +20,15 @@ namespace url {
class Origin;
}
-namespace subresource_filter {
-
-class FirstPartyOrigin;
-
+namespace url_pattern_index {
namespace proto {
class UrlRule;
}
+}
+
+namespace subresource_filter {
+
+class FirstPartyOrigin;
// The class used to construct flat data structures representing the set of URL
// filtering rules, as well as the index of those. Internally owns a
@@ -48,7 +50,7 @@ class RulesetIndexer {
// Adds |rule| to the ruleset and the index unless the |rule| has unsupported
// filter options, in which case the data structures remain unmodified.
// Returns whether the |rule| has been serialized and added to the index.
- bool AddUrlRule(const proto::UrlRule& rule);
+ bool AddUrlRule(const url_pattern_index::proto::UrlRule& rule);
// Finalizes construction of the data structures.
void Finish();
@@ -63,9 +65,9 @@ class RulesetIndexer {
private:
flatbuffers::FlatBufferBuilder builder_;
- UrlPatternIndexBuilder blacklist_;
- UrlPatternIndexBuilder whitelist_;
- UrlPatternIndexBuilder deactivation_;
+ url_pattern_index::UrlPatternIndexBuilder blacklist_;
+ url_pattern_index::UrlPatternIndexBuilder whitelist_;
+ url_pattern_index::UrlPatternIndexBuilder deactivation_;
DISALLOW_COPY_AND_ASSIGN(RulesetIndexer);
};
@@ -91,22 +93,23 @@ class IndexedRulesetMatcher {
bool ShouldDisableFilteringForDocument(
const GURL& document_url,
const url::Origin& parent_document_origin,
- proto::ActivationType activation_type) const;
+ url_pattern_index::proto::ActivationType activation_type) const;
// Returns whether the network request to |url| of |element_type| initiated by
// |document_origin| is not allowed to proceed. Always returns false if the
// |url| is not valid or |element_type| == ELEMENT_TYPE_UNSPECIFIED.
- bool ShouldDisallowResourceLoad(const GURL& url,
- const FirstPartyOrigin& first_party,
- proto::ElementType element_type,
- bool disable_generic_rules) const;
+ bool ShouldDisallowResourceLoad(
+ const GURL& url,
+ const FirstPartyOrigin& first_party,
+ url_pattern_index::proto::ElementType element_type,
+ bool disable_generic_rules) const;
private:
const flat::IndexedRuleset* root_;
- UrlPatternIndexMatcher blacklist_;
- UrlPatternIndexMatcher whitelist_;
- UrlPatternIndexMatcher deactivation_;
+ url_pattern_index::UrlPatternIndexMatcher blacklist_;
+ url_pattern_index::UrlPatternIndexMatcher whitelist_;
+ url_pattern_index::UrlPatternIndexMatcher deactivation_;
DISALLOW_COPY_AND_ASSIGN(IndexedRulesetMatcher);
};
diff --git a/chromium/components/subresource_filter/core/common/indexed_ruleset_unittest.cc b/chromium/components/subresource_filter/core/common/indexed_ruleset_unittest.cc
index bf94498f8e2..e51a1404891 100644
--- a/chromium/components/subresource_filter/core/common/indexed_ruleset_unittest.cc
+++ b/chromium/components/subresource_filter/core/common/indexed_ruleset_unittest.cc
@@ -10,16 +10,19 @@
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "components/subresource_filter/core/common/first_party_origin.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
-#include "components/subresource_filter/core/common/url_pattern.h"
-#include "components/subresource_filter/core/common/url_rule_test_support.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
+#include "components/url_pattern_index/url_pattern.h"
+#include "components/url_pattern_index/url_rule_test_support.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace subresource_filter {
-using namespace testing;
+namespace proto = url_pattern_index::proto;
+namespace testing = url_pattern_index::testing;
+using testing::MakeUrlRule;
+using url_pattern_index::UrlPattern;
class SubresourceFilterIndexedRulesetTest : public ::testing::Test {
public:
@@ -28,21 +31,22 @@ class SubresourceFilterIndexedRulesetTest : public ::testing::Test {
protected:
bool ShouldAllow(base::StringPiece url,
base::StringPiece document_origin = nullptr,
- proto::ElementType element_type = kOther,
+ proto::ElementType element_type = testing::kOther,
bool disable_generic_rules = false) const {
DCHECK(matcher_);
return !matcher_->ShouldDisallowResourceLoad(
- GURL(url), FirstPartyOrigin(GetOrigin(document_origin)), element_type,
- disable_generic_rules);
+ GURL(url), FirstPartyOrigin(testing::GetOrigin(document_origin)),
+ element_type, disable_generic_rules);
}
bool ShouldDeactivate(
base::StringPiece document_url,
base::StringPiece parent_document_origin = nullptr,
- proto::ActivationType activation_type = kNoActivation) const {
+ proto::ActivationType activation_type = testing::kNoActivation) const {
DCHECK(matcher_);
return matcher_->ShouldDisableFilteringForDocument(
- GURL(document_url), GetOrigin(parent_document_origin), activation_type);
+ GURL(document_url), testing::GetOrigin(parent_document_origin),
+ activation_type);
}
bool AddUrlRule(const proto::UrlRule& rule) {
@@ -50,18 +54,19 @@ class SubresourceFilterIndexedRulesetTest : public ::testing::Test {
}
bool AddSimpleRule(base::StringPiece url_pattern) {
- return AddUrlRule(MakeUrlRule(UrlPattern(url_pattern, kSubstring)));
+ return AddUrlRule(
+ MakeUrlRule(UrlPattern(url_pattern, testing::kSubstring)));
}
bool AddSimpleWhitelistRule(base::StringPiece url_pattern) {
- auto rule = MakeUrlRule(UrlPattern(url_pattern, kSubstring));
+ auto rule = MakeUrlRule(UrlPattern(url_pattern, testing::kSubstring));
rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
return AddUrlRule(rule);
}
bool AddSimpleWhitelistRule(base::StringPiece url_pattern,
int32_t activation_types) {
- auto rule = MakeUrlRule(UrlPattern(url_pattern, kSubstring));
+ auto rule = MakeUrlRule(UrlPattern(url_pattern, testing::kSubstring));
rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
rule.clear_element_types();
rule.set_activation_types(activation_types);
@@ -130,11 +135,13 @@ TEST_F(SubresourceFilterIndexedRulesetTest, SimpleBlacklistAndWhitelist) {
TEST_F(SubresourceFilterIndexedRulesetTest,
OneBlacklistAndOneDeactivationRule) {
ASSERT_TRUE(AddSimpleRule("example.com"));
- ASSERT_TRUE(AddSimpleWhitelistRule("example.com", kDocument));
+ ASSERT_TRUE(AddSimpleWhitelistRule("example.com", testing::kDocument));
Finish();
- EXPECT_TRUE(ShouldDeactivate("https://example.com", nullptr, kDocument));
- EXPECT_FALSE(ShouldDeactivate("https://xample.com", nullptr, kDocument));
+ EXPECT_TRUE(
+ ShouldDeactivate("https://example.com", nullptr, testing::kDocument));
+ EXPECT_FALSE(
+ ShouldDeactivate("https://xample.com", nullptr, testing::kDocument));
EXPECT_FALSE(ShouldAllow("https://example.com"));
EXPECT_TRUE(ShouldAllow("https://xample.com"));
}
diff --git a/chromium/components/subresource_filter/core/common/load_policy.h b/chromium/components/subresource_filter/core/common/load_policy.h
new file mode 100644
index 00000000000..0a235a7e4a0
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/load_policy.h
@@ -0,0 +1,20 @@
+// 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_SUBRESOURCE_FILTER_CORE_COMMON_LOAD_POLICY_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_LOAD_POLICY_H_
+
+namespace subresource_filter {
+
+// Represents the value returned by the DocumentSubresourceFilter corresponding
+// to a resource load.
+enum class LoadPolicy {
+ ALLOW,
+ DISALLOW,
+ WOULD_DISALLOW,
+};
+
+} // namespace subresource_filter
+
+#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_LOAD_POLICY_H_
diff --git a/chromium/components/subresource_filter/core/common/ngram_extractor.h b/chromium/components/subresource_filter/core/common/ngram_extractor.h
deleted file mode 100644
index 110c64b04d1..00000000000
--- a/chromium/components/subresource_filter/core/common/ngram_extractor.h
+++ /dev/null
@@ -1,133 +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_CORE_COMMON_NGRAM_EXTRACTOR_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_NGRAM_EXTRACTOR_H_
-
-#include <stddef.h>
-
-#include <iterator>
-#include <type_traits>
-
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-
-namespace subresource_filter {
-
-// The class used to iteratively extract N-grams from strings. An N-gram is a
-// string consisting of N (up to 8) non-special characters, which are stored in
-// the lowest N non-zero bytes, lower bytes corresponding to later symbols. The
-// size of the integer type limits the maximum value of N. For example an
-// uint64_t can store up to 8-grams.
-//
-// Note: If used for UTF-8 strings, the N-grams can have partial byte sequences.
-//
-// Template parameters:
-// * N - the size of N-grams.
-// * NGramType - the integer type used to encode N-grams.
-// * IsSeparator - the type of a bool(char) functor.
-template <size_t N, typename NGramType, typename IsSeparator>
-class NGramExtractor {
- public:
- // An STL compatible input iterator over N-grams contained in a string.
- class Iterator : public std::iterator<std::input_iterator_tag, NGramType> {
- public:
- // Creates an iterator, which points to the leftmost valid N-gram within the
- // |extractor|'s string, starting from |head|.
- Iterator(const NGramExtractor& extractor,
- base::StringPiece::const_iterator head)
- : extractor_(extractor), head_(head), end_(extractor.string_.end()) {
- DCHECK_GE(head, extractor_.string_.begin());
- DCHECK_LE(head, end_);
-
- CompleteNGramFrom(0);
- }
-
- bool operator==(const Iterator& rhs) const { return head_ == rhs.head_; }
- bool operator!=(const Iterator& rhs) const { return !operator==(rhs); }
-
- NGramType operator*() const { return ngram_; }
- NGramType* operator->() const { return &ngram_; }
-
- Iterator& operator++() {
- ngram_ &= ~(static_cast<NGramType>(0xFFu) << 8 * (N - 1));
- ++head_;
- CompleteNGramFrom(N - 1);
- return *this;
- }
-
- Iterator operator++(int) {
- Iterator copy(*this);
- operator++();
- return copy;
- }
-
- private:
- // Consumes characters starting with the one pointed to by |head_|, as many
- // of them as needed to extend |ngram_| from its |current_length| to a
- // length of N. Leaves |head_| pointing to the last character consumed.
- void CompleteNGramFrom(size_t current_length) {
- for (; head_ != end_; ++head_) {
- if (extractor_.is_separator_(*head_)) {
- current_length = 0;
- ngram_ = 0;
- } else {
- ngram_ = ngram_ << 8 | static_cast<NGramType>(*head_);
- if (++current_length == N)
- break;
- }
- }
- }
-
- const NGramExtractor& extractor_;
-
- // Always points to the last character included in the current |ngram_|.
- base::StringPiece::const_iterator head_;
- // Always points to extractor_.string_.end().
- base::StringPiece::const_iterator end_;
-
- // Contains the N-gram currently pointed to by the iterator. Undefined if
- // the iterator is at the end.
- NGramType ngram_ = 0;
- };
-
- // Constructs an extractor for iterating over N-grams contained in the
- // |string|. |is_separator| is used to determine whether a certain character
- // is a separator and should not be contained in an N-gram.
- NGramExtractor(base::StringPiece string, IsSeparator is_separator)
- : string_(string), is_separator_(is_separator) {}
-
- Iterator begin() const { return Iterator(*this, string_.begin()); }
- Iterator end() const { return Iterator(*this, string_.end()); }
-
- private:
- static_assert(std::is_integral<NGramType>::value, "Not an integral type.");
- static_assert(std::is_unsigned<NGramType>::value, "Not an unsigned type.");
- static_assert(N > 0u, "N should be positive.");
- static_assert(N <= sizeof(NGramType), "N-gram doesn't fit into the type.");
-
- base::StringPiece string_;
- IsSeparator is_separator_;
-};
-
-// A helper function used to create an NGramExtractor for a |string| without
-// knowing the direct type of the |is_separator| functor.
-//
-// Typical usage:
-// const char* str = "no*abacaba*abcd";
-// auto extractor = CreateNGramExtractor<5, uint64_t>(
-// str, [](char c) { return c == '*'; });
-// for (uint64_t ngram : extractor) {
-// ... process the |ngram| ...
-// }
-template <size_t N, typename NGramType, typename IsSeparator>
-NGramExtractor<N, NGramType, IsSeparator> CreateNGramExtractor(
- base::StringPiece string,
- IsSeparator is_separator) {
- return NGramExtractor<N, NGramType, IsSeparator>(string, is_separator);
-}
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_NGRAM_EXTRACTOR_H_
diff --git a/chromium/components/subresource_filter/core/common/ngram_extractor_unittest.cc b/chromium/components/subresource_filter/core/common/ngram_extractor_unittest.cc
deleted file mode 100644
index bea40150ab5..00000000000
--- a/chromium/components/subresource_filter/core/common/ngram_extractor_unittest.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/subresource_filter/core/common/ngram_extractor.h"
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace subresource_filter {
-
-namespace {
-
-bool IsSeparatorTrue(char) {
- return true;
-}
-bool IsSeparatorFalse(char) {
- return false;
-}
-bool IsSpecialChar(char c) {
- return c == '*' || c == '^';
-}
-
-template <typename IntType>
-IntType EncodeStringToInteger(const char* data, size_t size) {
- EXPECT_LE(size, sizeof(IntType));
-
- IntType encoded_string = 0;
- for (size_t i = 0; i < size; ++i) {
- encoded_string = (encoded_string << 8) | static_cast<IntType>(data[i]);
- }
- return encoded_string;
-}
-
-} // namespace
-
-TEST(NGramExtractorTest, EmptyString) {
- const char* kString = "";
- auto extractor = CreateNGramExtractor<3, uint32_t>(kString, IsSpecialChar);
- EXPECT_EQ(extractor.begin(), extractor.end());
-}
-
-TEST(NGramExtractorTest, ShortString) {
- const char* kString = "abacab";
- auto extractor = CreateNGramExtractor<7, uint64_t>(kString, IsSeparatorFalse);
- EXPECT_EQ(extractor.begin(), extractor.end());
-}
-
-TEST(NGramExtractorTest, ShortPieces) {
- const char* kString = "1**abac*abc*abcd*00";
- auto extractor = CreateNGramExtractor<6, uint64_t>(kString, IsSpecialChar);
- EXPECT_EQ(extractor.begin(), extractor.end());
-}
-
-TEST(NGramExtractorTest, IsSeparatorAlwaysTrue) {
- const char* kString = "abacaba";
- auto extractor = CreateNGramExtractor<3, uint32_t>(kString, IsSeparatorTrue);
- EXPECT_EQ(extractor.begin(), extractor.end());
-}
-
-TEST(NGramExtractorTest, IsSeparatorAlwaysFalse) {
- const std::string kString = "abacaba123";
- constexpr size_t N = 3;
-
- std::vector<uint32_t> expected_ngrams;
- for (size_t begin = 0; begin + N <= kString.size(); ++begin) {
- expected_ngrams.push_back(
- EncodeStringToInteger<uint32_t>(kString.data() + begin, N));
- }
-
- auto extractor = CreateNGramExtractor<N, uint32_t>(kString, IsSeparatorFalse);
- std::vector<uint32_t> actual_ngrams(extractor.begin(), extractor.end());
- EXPECT_EQ(expected_ngrams, actual_ngrams);
-}
-
-TEST(NGramExtractorTest, NGramsArePresent) {
- constexpr size_t N = 6;
- const std::string kTestCases[] = {
- "abcdef", "abacaba", "*abacaba",
- "abacaba*", "*abacaba*", "*abacaba*abc^1005001*",
- };
-
- for (const std::string& string : kTestCases) {
- SCOPED_TRACE(testing::Message() << "String: " << string);
-
- std::vector<uint64_t> expected_ngrams;
- for (size_t begin = 0; begin + N <= string.size(); ++begin) {
- bool is_valid_ngram = true;
- for (size_t i = 0; i < N; ++i) {
- if (IsSpecialChar(string[begin + i])) {
- is_valid_ngram = false;
- break;
- }
- }
- if (is_valid_ngram) {
- expected_ngrams.push_back(
- EncodeStringToInteger<uint64_t>(string.data() + begin, N));
- }
- }
-
- auto extractor = CreateNGramExtractor<N, uint64_t>(string, IsSpecialChar);
- std::vector<uint64_t> actual_ngrams(extractor.begin(), extractor.end());
- EXPECT_EQ(expected_ngrams, actual_ngrams);
- }
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/proto/BUILD.gn b/chromium/components/subresource_filter/core/common/proto/BUILD.gn
deleted file mode 100644
index b0ccabe19c3..00000000000
--- a/chromium/components/subresource_filter/core/common/proto/BUILD.gn
+++ /dev/null
@@ -1,11 +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.
-
-import("//third_party/protobuf/proto_library.gni")
-
-proto_library("proto") {
- sources = [
- "rules.proto",
- ]
-}
diff --git a/chromium/components/subresource_filter/core/common/proto/rules.proto b/chromium/components/subresource_filter/core/common/proto/rules.proto
deleted file mode 100644
index b918dd95a99..00000000000
--- a/chromium/components/subresource_filter/core/common/proto/rules.proto
+++ /dev/null
@@ -1,195 +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.
-
-syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-
-package subresource_filter.proto;
-option java_package = "org.chromium.components.subresource_filter.proto";
-
-// The type of a subresource filtering rule.
-enum RuleType {
- RULE_TYPE_UNSPECIFIED = 0;
-
- RULE_TYPE_COMMENT = 1; // Comment rule.
- RULE_TYPE_URL = 2; // Network level filtering rule based on URL pattern.
- RULE_TYPE_CSS = 3; // Element hiding rule based on a CSS selector.
-};
-
-// The format of a URL pattern.
-enum UrlPatternType {
- URL_PATTERN_TYPE_UNSPECIFIED = 0;
-
- // A pattern without special characters, e.g. "example.com".
- URL_PATTERN_TYPE_SUBSTRING = 1;
-
- // The pattern contains one or more wildcards, namely '*' and/or '^'
- // characters. The '*' matches any sequence of characters, while the '^'
- // matches a separator, i.e. anything but a letter, a digit, or one of [-._%].
- URL_PATTERN_TYPE_WILDCARDED = 2;
-
- // The pattern is a regular expression.
- URL_PATTERN_TYPE_REGEXP = 3;
-};
-
-// Types of anchors that can be used to constrain where a URL pattern must
-// begin/end in the URL in order to be considered a match.
-enum AnchorType {
- ANCHOR_TYPE_UNSPECIFIED = 0;
-
- // Acts like a '*' wildcard at the respective end of a pattern.
- ANCHOR_TYPE_NONE = 1;
- // The pattern must match from the start/until the end of the URL.
- ANCHOR_TYPE_BOUNDARY = 2;
- // The pattern must match starting with the TLD+n of the URL's domain, but the
- // scheme and subdomains (if any) can be arbitrary.
- ANCHOR_TYPE_SUBDOMAIN = 3;
-};
-
-// The types of subresource requests that a URL rule should be applied to.
-// Note: Values are used as flags in a bitmask.
-enum ElementType {
- option allow_alias = true;
- ELEMENT_TYPE_UNSPECIFIED = 0;
-
- ELEMENT_TYPE_OTHER = 1;
- ELEMENT_TYPE_SCRIPT = 2;
- ELEMENT_TYPE_IMAGE = 4;
- ELEMENT_TYPE_STYLESHEET = 8;
- ELEMENT_TYPE_OBJECT = 16;
- ELEMENT_TYPE_XMLHTTPREQUEST = 32;
- ELEMENT_TYPE_OBJECT_SUBREQUEST = 64;
- ELEMENT_TYPE_SUBDOCUMENT = 128;
- ELEMENT_TYPE_PING = 256;
- ELEMENT_TYPE_MEDIA = 512;
- ELEMENT_TYPE_FONT = 1024;
- ELEMENT_TYPE_POPUP = 2048;
- ELEMENT_TYPE_WEBSOCKET = 4096;
-
- // NOTE: Keep these two values consistent with the values above.
- ELEMENT_TYPE_MAX = 4096;
- ELEMENT_TYPE_ALL = 8191;
-};
-
-// The options controlling whether or not to activate filtering for subresources
-// of documents that match the URL pattern of the rule.
-// Note: Values are used as flags in a bitmask.
-enum ActivationType {
- option allow_alias = true;
- ACTIVATION_TYPE_UNSPECIFIED = 0;
-
- ACTIVATION_TYPE_DOCUMENT = 1; // Disable all rules on the page.
- ACTIVATION_TYPE_ELEMHIDE = 2; // Disable CSS rules on the page.
- ACTIVATION_TYPE_GENERICHIDE = 4; // Disable generic CSS rules on the page.
- ACTIVATION_TYPE_GENERICBLOCK = 8; // Disable generic URL rules on the page.
-
- // NOTE: Keep these two values consistent with the values above.
- ACTIVATION_TYPE_MAX = 8;
- ACTIVATION_TYPE_ALL = 15;
-};
-
-// The semantics of a rule. Defines how the rule relates to other rules and how
-// it influences the filtering decision.
-enum RuleSemantics {
- RULE_SEMANTICS_UNSPECIFIED = 0;
-
- // Matching subresource requests should be aborted, matching elements should
- // be hidden.
- RULE_SEMANTICS_BLACKLIST = 1;
- // If the rule matches, it suppresses any matching RULE_SEMANTICS_BLACKLIST
- // rule.
- RULE_SEMANTICS_WHITELIST = 2;
-}
-
-// The type of relation between the source of the requested subresource and that
-// of the document.
-enum SourceType {
- SOURCE_TYPE_UNSPECIFIED = 0;
-
- SOURCE_TYPE_ANY = 1; // Doesn't matter.
- SOURCE_TYPE_THIRD_PARTY = 2; // Requesting a trird-party resource.
- SOURCE_TYPE_FIRST_PARTY = 3; // Requesting a first-party resource.
-}
-
-// An item of the domain list.
-message DomainListItem {
- // The UTF-8 representation of the domain, e.g. "subdomain.example.com".
- optional string domain = 1;
-
- // Defines whether the domain is excluded from the set of domains.
- optional bool exclude = 2;
-}
-
-// A network level filtering rule based on a URL pattern. Corresponds to
-// RULE_TYPE_URL.
-message UrlRule {
- // The semantics of the rule.
- optional RuleSemantics semantics = 1;
-
- // Restricts application of the rule to first-party/third-party requests.
- optional SourceType source_type = 2;
-
- // Stores the ElementTypes that the rule applies to as a bitwise OR of the
- // corresponding ElementType values.
- optional int32 element_types = 3;
-
- // Stores the ActivationTypes associated with the rule as a bitwise OR of the
- // corresponding ActivationType values.
- optional int32 activation_types = 4;
-
- // The rule applies only to subresources of documents loaded from included
- // domains (or subdomains thereof). If the list is empty, the rule is applied
- // on documents from all domains.
- // If |domains| is empty or has exceptions only, the rule is called generic.
- // Otherwise is it called domain specific, i.e. applies to a limited number of
- // domains.
- repeated DomainListItem domains = 5;
-
- // The format of |url_pattern|.
- optional UrlPatternType url_pattern_type = 6;
-
- // Defines where the URL pattern must start in the URL in order to be
- // considered a match. Never used with REGEXP patterns.
- optional AnchorType anchor_left = 7;
-
- // Defines where the URL pattern must end in the URL in order to be
- // considered a match. Never used with REGEXP patterns. Never equals to
- // ANCHOR_TYPE_SUBDOMAIN.
- optional AnchorType anchor_right = 8;
-
- // When set, the rule applies only to URLs that match |url_pattern| in a
- // case-sensitive way.
- optional bool match_case = 9;
-
- // The URL pattern of the format prescribed by |url_pattern_type|.
- optional string url_pattern = 10;
-}
-
-// Element hiding rule based on a CSS selector. Corresponds to RULE_TYPE_CSS.
-message CssRule {
- // The semantics of the rule.
- optional RuleSemantics semantics = 1;
-
- // The list of domains, same as UrlRule::domains.
- repeated DomainListItem domains = 2;
-
- // A CSS selector as specified in http://www.w3.org/TR/css3-selectors.
- optional string css_selector = 3;
-}
-
-// A comment line.
-message Comment {
- // Comment text.
- optional string text = 1;
-
- // For special key-value comments, if any.
- optional string key = 2;
- optional string value = 3;
-}
-
-// A container for lists of non-comment rules collated by RuleType.
-message FilteringRules {
- repeated UrlRule url_rules = 1;
- repeated CssRule css_rules = 2;
-}
diff --git a/chromium/components/subresource_filter/core/common/string_splitter.h b/chromium/components/subresource_filter/core/common/string_splitter.h
deleted file mode 100644
index f3b4e85b06b..00000000000
--- a/chromium/components/subresource_filter/core/common/string_splitter.h
+++ /dev/null
@@ -1,101 +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_CORE_COMMON_STRING_SPLITTER_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_STRING_SPLITTER_H_
-
-#include <iterator>
-
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-
-namespace subresource_filter {
-
-// A zero-allocation string splitter. Splits a string into non-empty tokens
-// divided by separator characters as defined by the IsSeparator predicate.
-// However, instead of materializing and returning a collection of all tokens in
-// the string, it provides an InputIterator that can be used to extract the
-// tokens.
-//
-// TODO(pkalinnikov): Move it to "base/strings" after some generalization.
-template <typename IsSeparator>
-class StringSplitter {
- public:
- class Iterator
- : public std::iterator<std::input_iterator_tag, base::StringPiece> {
- public:
- // Creates an iterator, which points to the leftmost token within the
- // |splitter|'s |text|, starting from |head|.
- Iterator(const StringSplitter& splitter,
- base::StringPiece::const_iterator head)
- : splitter_(&splitter), current_(head, 0), end_(splitter.text_.end()) {
- DCHECK_GE(head, splitter_->text_.begin());
- DCHECK_LE(head, end_);
-
- Advance();
- }
-
- bool operator==(const Iterator& rhs) const {
- return current_.begin() == rhs.current_.begin();
- }
-
- bool operator!=(const Iterator& rhs) const { return !operator==(rhs); }
-
- base::StringPiece operator*() const { return current_; }
- const base::StringPiece* operator->() const { return &current_; }
-
- Iterator& operator++() {
- Advance();
- return *this;
- }
-
- Iterator operator++(int) {
- Iterator copy(*this);
- operator++();
- return copy;
- }
-
- private:
- void Advance() {
- auto begin = current_.end();
- while (begin != end_ && splitter_->is_separator_(*begin))
- ++begin;
- auto end = begin;
- while (end != end_ && !splitter_->is_separator_(*end))
- ++end;
- current_ = base::StringPiece(begin, end - begin);
- }
-
- const StringSplitter* splitter_;
-
- // Contains the token currently pointed to by the iterator.
- base::StringPiece current_;
- // Always points to the text_.end().
- base::StringPiece::const_iterator end_;
- };
-
- // Constructs a splitter for iterating over non-empty tokens contained in the
- // |text|. |is_separator| predicate is used to determine whether a certain
- // character is a separator.
- StringSplitter(base::StringPiece text,
- IsSeparator is_separator = IsSeparator())
- : text_(text), is_separator_(is_separator) {}
-
- Iterator begin() const { return Iterator(*this, text_.begin()); }
- Iterator end() const { return Iterator(*this, text_.end()); }
-
- private:
- base::StringPiece text_;
- IsSeparator is_separator_;
-};
-
-template <typename IsSeparator>
-StringSplitter<IsSeparator> CreateStringSplitter(base::StringPiece text,
- IsSeparator is_separator) {
- return StringSplitter<IsSeparator>(text, is_separator);
-}
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_STRING_SPLITTER_H_
diff --git a/chromium/components/subresource_filter/core/common/string_splitter_unittest.cc b/chromium/components/subresource_filter/core/common/string_splitter_unittest.cc
deleted file mode 100644
index 5e638405b2d..00000000000
--- a/chromium/components/subresource_filter/core/common/string_splitter_unittest.cc
+++ /dev/null
@@ -1,82 +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/core/common/string_splitter.h"
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string_piece.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace subresource_filter {
-
-namespace {
-bool IsTestSeparator(char c) {
- return c == ' ' || c == '\t' || c == ',';
-}
-}
-
-TEST(StringSplitterTest, SplitWithEmptyResult) {
- const char* const kStrings[] = {
- "", " ", "\t", ",", " \t ", ",,,,", "\t\t\t",
- };
-
- for (const char* string : kStrings) {
- auto splitter = CreateStringSplitter(string, IsTestSeparator);
- // Explicitly verify both operator== and operator!=.
- EXPECT_TRUE(splitter.begin() == splitter.end());
- EXPECT_FALSE(splitter.begin() != splitter.end());
- }
-}
-
-TEST(StringSplitterTest, SplitOneWord) {
- const char* const kLongStrings[] = {
- "word", " word ", " word", "word ", ",word,",
- "\tword\t", " word ", "word ", " word", ", word, \t",
- };
- const char* const kShortStrings[] = {
- "w", " w ", " w", "w ", " w ", " w", "w ", ", w, ", "w, \t",
- };
-
- const char kLongWord[] = "word";
- const char kShortWord[] = "w";
-
- auto expect_word = [](const char* text, const char* word) {
- auto splitter = CreateStringSplitter(text, IsTestSeparator);
- // Explicitly verify both operator== and operator!=.
- EXPECT_TRUE(splitter.begin() != splitter.end());
- EXPECT_FALSE(splitter.begin() == splitter.end());
-
- EXPECT_EQ(splitter.end(), ++splitter.begin());
- EXPECT_EQ(word, *splitter.begin());
-
- auto iterator = splitter.begin();
- EXPECT_EQ(splitter.begin(), iterator++);
- EXPECT_EQ(splitter.end(), iterator);
- };
-
- for (const char* string : kLongStrings)
- expect_word(string, kLongWord);
- for (const char* string : kShortStrings)
- expect_word(string, kShortWord);
-}
-
-TEST(StringSplitterTest, SplitThreeWords) {
- const char* const kStrings[] = {
- "one two three", " one two three ", " one two, three",
- "one,two\t\t three", "one, two, three, ",
- };
- const std::vector<base::StringPiece> kResults = {
- "one", "two", "three",
- };
-
- for (const char* string : kStrings) {
- auto splitter = CreateStringSplitter(string, IsTestSeparator);
- std::vector<base::StringPiece> tokens(splitter.begin(), splitter.end());
- EXPECT_EQ(kResults, tokens);
- }
-}
-
-} // namespace subresource_filter
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 939dff3085e..ba02d32f048 100644
--- a/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc
@@ -12,14 +12,16 @@
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "components/subresource_filter/core/common/indexed_ruleset.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
-#include "components/subresource_filter/core/common/unindexed_ruleset.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
+#include "components/url_pattern_index/unindexed_ruleset.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
namespace subresource_filter {
+namespace proto = url_pattern_index::proto;
+
namespace {
// The methods below assume that char and uint8_t are interchangeable.
@@ -39,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);
- UnindexedRulesetWriter ruleset_writer(&output);
+ url_pattern_index::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/test_ruleset_creator.h b/chromium/components/subresource_filter/core/common/test_ruleset_creator.h
index cb07526e787..7d1d5cab667 100644
--- a/chromium/components/subresource_filter/core/common/test_ruleset_creator.h
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_creator.h
@@ -14,7 +14,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
namespace subresource_filter {
namespace testing {
@@ -83,10 +83,12 @@ class TestRulesetCreator {
int num_of_suffixes,
TestRulesetPair* test_ruleset_pair);
- void CreateRulesetWithRules(const std::vector<proto::UrlRule>& rules,
- TestRulesetPair* test_ruleset_pair);
- void CreateUnindexedRulesetWithRules(const std::vector<proto::UrlRule>& rules,
- TestRuleset* test_unindexed_ruleset);
+ void CreateRulesetWithRules(
+ const std::vector<url_pattern_index::proto::UrlRule>& rules,
+ TestRulesetPair* test_ruleset_pair);
+ void CreateUnindexedRulesetWithRules(
+ const std::vector<url_pattern_index::proto::UrlRule>& rules,
+ TestRuleset* test_unindexed_ruleset);
// Returns a unique |path| that is valid for the lifetime of this instance.
// No file at |path| will be automatically created.
diff --git a/chromium/components/subresource_filter/core/common/test_ruleset_utils.cc b/chromium/components/subresource_filter/core/common/test_ruleset_utils.cc
index e97548eced7..9ea5f663371 100644
--- a/chromium/components/subresource_filter/core/common/test_ruleset_utils.cc
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_utils.cc
@@ -7,6 +7,8 @@
namespace subresource_filter {
namespace testing {
+namespace proto = url_pattern_index::proto;
+
proto::UrlRule CreateSuffixRule(base::StringPiece suffix) {
proto::UrlRule rule;
rule.set_semantics(proto::RULE_SEMANTICS_BLACKLIST);
diff --git a/chromium/components/subresource_filter/core/common/test_ruleset_utils.h b/chromium/components/subresource_filter/core/common/test_ruleset_utils.h
index d7023f86b05..5c001c90b55 100644
--- a/chromium/components/subresource_filter/core/common/test_ruleset_utils.h
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_utils.h
@@ -10,21 +10,22 @@
#include <vector>
#include "base/strings/string_piece.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
namespace subresource_filter {
namespace testing {
// Creates a blacklist URL rule which targets subresources of any type such that
// the resource URL ends with |suffix|.
-proto::UrlRule CreateSuffixRule(base::StringPiece suffix);
+url_pattern_index::proto::UrlRule CreateSuffixRule(base::StringPiece suffix);
// Same as CreateUrlRule(pattern, proto::URL_PATTERN_TYPE_WILDCARDED), but the
// rule applies to the specified |activation_types|, and to no element types.
// Additionally, it is restricted to a set of |domains| (if provided).
-proto::UrlRule CreateWhitelistRuleForDocument(
+url_pattern_index::proto::UrlRule CreateWhitelistRuleForDocument(
base::StringPiece pattern,
- int32_t activation_types = proto::ACTIVATION_TYPE_DOCUMENT,
+ int32_t activation_types =
+ url_pattern_index::proto::ACTIVATION_TYPE_DOCUMENT,
std::vector<std::string> domains = std::vector<std::string>());
} // namespace testing
diff --git a/chromium/components/subresource_filter/core/common/uint64_hasher.h b/chromium/components/subresource_filter/core/common/uint64_hasher.h
deleted file mode 100644
index 349d9d73585..00000000000
--- a/chromium/components/subresource_filter/core/common/uint64_hasher.h
+++ /dev/null
@@ -1,52 +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.
-
-// Based on "v8/src/base/functional.cc".
-// See: Thomas Wang, Integer Hash Functions.
-// https://gist.github.com/badboy/6267743
-// TODO(pkalinnikov): Consider moving the implementation into base/.
-
-#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_HASH_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_HASH_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <type_traits>
-
-namespace subresource_filter {
-
-template <typename T>
-typename std::enable_if<sizeof(T) == 4, T>::type Uint64Hash(uint64_t v) {
- // "64 bit to 32 bit Hash Functions"
- v = ~v + (v << 18); // v = (v << 18) - v - 1;
- v = v ^ (v >> 31);
- v = v * 21; // v = (v + (v << 2)) + (v << 4);
- v = v ^ (v >> 11);
- v = v + (v << 6);
- v = v ^ (v >> 22);
- return static_cast<T>(v);
-}
-
-template <typename T>
-typename std::enable_if<sizeof(T) == 8, T>::type Uint64Hash(uint64_t v) {
- // "64 bit Mix Functions"
- v = ~v + (v << 21); // v = (v << 21) - v - 1;
- v = v ^ (v >> 24);
- v = (v + (v << 3)) + (v << 8); // v * 265
- v = v ^ (v >> 14);
- v = (v + (v << 2)) + (v << 4); // v * 21
- v = v ^ (v >> 28);
- v = v + (v << 31);
- return static_cast<T>(v);
-}
-
-class Uint64Hasher {
- public:
- size_t operator()(uint64_t v) const { return Uint64Hash<size_t>(v); }
-};
-
-} // subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_HASH_H_
diff --git a/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc b/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
deleted file mode 100644
index 12a4f5d446b..00000000000
--- a/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
+++ /dev/null
@@ -1,71 +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/core/common/unindexed_ruleset.h"
-
-#include "base/logging.h"
-#include "base/numerics/safe_conversions.h"
-
-namespace subresource_filter {
-
-// UnindexedRulesetReader ------------------------------------------------------
-
-UnindexedRulesetReader::UnindexedRulesetReader(
- google::protobuf::io::ZeroCopyInputStream* stream)
- : coded_stream_(stream) {}
-
-UnindexedRulesetReader::~UnindexedRulesetReader() = default;
-
-bool UnindexedRulesetReader::ReadNextChunk(proto::FilteringRules* chunk) {
- uint32_t chunk_size = 0;
- if (!coded_stream_.ReadVarint32(&chunk_size))
- return false;
- auto limit = coded_stream_.PushLimit(chunk_size);
- if (!chunk->ParseFromCodedStream(&coded_stream_))
- return false;
- coded_stream_.PopLimit(limit);
- return true;
-}
-
-// UnindexedRulesetWriter ------------------------------------------------------
-
-UnindexedRulesetWriter::UnindexedRulesetWriter(
- google::protobuf::io::ZeroCopyOutputStream* stream,
- int max_rules_per_chunk)
- : coded_stream_(stream), max_rules_per_chunk_(max_rules_per_chunk) {}
-
-UnindexedRulesetWriter::~UnindexedRulesetWriter() {
- DCHECK_EQ(pending_chunk_.url_rules_size(), 0);
- DCHECK_EQ(pending_chunk_.css_rules_size(), 0);
-}
-
-bool UnindexedRulesetWriter::AddUrlRule(const proto::UrlRule& rule) {
- DCHECK(!had_error());
- pending_chunk_.add_url_rules()->CopyFrom(rule);
- if (pending_chunk_.url_rules_size() >= max_rules_per_chunk_) {
- DCHECK_EQ(pending_chunk_.url_rules_size(), max_rules_per_chunk_);
- return WritePendingChunk();
- }
- return true;
-}
-
-bool UnindexedRulesetWriter::Finish() {
- DCHECK(!had_error());
- const bool success = !pending_chunk_.url_rules_size() || WritePendingChunk();
- if (success)
- coded_stream_.Trim();
- return success;
-}
-
-bool UnindexedRulesetWriter::WritePendingChunk() {
- DCHECK(!had_error());
- DCHECK_GT(pending_chunk_.url_rules_size(), 0);
-
- proto::FilteringRules chunk;
- chunk.Swap(&pending_chunk_);
- coded_stream_.WriteVarint32(base::checked_cast<uint32_t>(chunk.ByteSize()));
- return !had_error() && chunk.SerializeToCodedStream(&coded_stream_);
-}
-
-} // 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
deleted file mode 100644
index af847ea8a06..00000000000
--- a/chromium/components/subresource_filter/core/common/unindexed_ruleset.h
+++ /dev/null
@@ -1,100 +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.
-
-// 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.
-//
-// 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.
-//
-// 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
-// before reading the next one. In practice, this should not be an issue as
-// indexing of the ruleset is expected to be performed in an on-line fashion.
-
-#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_UNINDEXED_RULESET_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_UNINDEXED_RULESET_H_
-
-#include "base/macros.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
-#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
-#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h"
-
-namespace subresource_filter {
-
-// Reads an unindexed ruleset from |stream| one chunk at a time.
-class UnindexedRulesetReader {
- public:
- // Note: The |stream| should outlive |this| instance.
- explicit UnindexedRulesetReader(
- google::protobuf::io::ZeroCopyInputStream* stream);
- ~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);
-
- // Returns how many bytes of the |stream| have been consumed.
- int num_bytes_read() const { return coded_stream_.CurrentPosition(); }
-
- private:
- google::protobuf::io::CodedInputStream coded_stream_;
-
- DISALLOW_COPY_AND_ASSIGN(UnindexedRulesetReader);
-};
-
-// Divides an unindexed ruleset into chunks and writes them into |stream|.
-//
-// Writing methods of this class return bool false if an I/O error occurrs
-// during these calls. In this case the UnindexedRulesetWriter becomes broken,
-// 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.
- explicit UnindexedRulesetWriter(
- google::protobuf::io::ZeroCopyOutputStream* stream,
- int max_rules_per_chunk = 64);
- ~UnindexedRulesetWriter();
-
- int max_rules_per_chunk() const { return max_rules_per_chunk_; }
-
- // Returns whether an I/O error occurred since this object was created.
- bool had_error() const { return coded_stream_.HadError(); }
-
- // 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);
- // TODO(pkalinnikov): Implement AddCssRule when needed.
-
- // Finalizes the serialization of the unindexed ruleset, i.e., writes the
- // final chunk of rules, if there are any still pending. This method *should*
- // be called exactly once when interaction with |this| instance ends, unless
- // some of the writing operations returned false, which indicates an error.
- // After calling Finish write operations should not be used any more.
- bool Finish();
-
- private:
- // Writes the non-empty |pending_chunk_| to the |coded_stream_|. Always leaves
- // the |panding_chunk_| empty, regardless of whether an error occurred.
- bool WritePendingChunk();
-
- google::protobuf::io::CodedOutputStream coded_stream_;
-
- const int max_rules_per_chunk_ = 0;
- proto::FilteringRules pending_chunk_;
-
- DISALLOW_COPY_AND_ASSIGN(UnindexedRulesetWriter);
-};
-
-} // 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
deleted file mode 100644
index f193b5ed46e..00000000000
--- a/chromium/components/subresource_filter/core/common/unindexed_ruleset_unittest.cc
+++ /dev/null
@@ -1,182 +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/core/common/unindexed_ruleset.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
-#include "components/subresource_filter/core/common/url_pattern.h"
-#include "components/subresource_filter/core/common/url_rule_test_support.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#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 subresource_filter {
-
-namespace {
-
-using namespace testing;
-
-bool IsEqual(const proto::UrlRule& lhs, const proto::UrlRule& rhs) {
- return lhs.SerializeAsString() == rhs.SerializeAsString();
-}
-
-// The helper class used for building UnindexedRulesets.
-class UnindexedRulesetTestBuilder {
- public:
- // Initializes the builder that writes the ruleset to StringOutputStream.
- UnindexedRulesetTestBuilder()
- : output_(
- new google::protobuf::io::StringOutputStream(&ruleset_contents_)),
- ruleset_writer_(output_.get()) {}
-
- // Initializes the builder that writes the ruleset to an array of |array_size|
- // through an ArrayOutputStream.
- UnindexedRulesetTestBuilder(int array_size)
- : ruleset_contents_(array_size, '\0'),
- output_(
- new google::protobuf::io::ArrayOutputStream(&ruleset_contents_[0],
- array_size)),
- ruleset_writer_(output_.get()) {}
-
- int max_rules_per_chunk() const {
- return ruleset_writer_.max_rules_per_chunk();
- }
-
- bool AddUrlRule(const UrlPattern& url_pattern,
- proto::SourceType source_type,
- bool is_whitelist = false) {
- auto rule = MakeUrlRule(url_pattern);
- if (is_whitelist)
- rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
- rule.set_source_type(source_type);
-
- url_rules_.push_back(rule);
- return !ruleset_writer_.had_error() &&
- ruleset_writer_.AddUrlRule(url_rules_.back());
- }
-
- bool AddUrlRules(int number_of_rules) {
- for (int i = 0; i < number_of_rules; ++i) {
- std::string url_pattern = "example" + base::IntToString(i) + ".com";
- if (!AddUrlRule(UrlPattern(url_pattern), kAnyParty, i & 1))
- return false;
- }
- return true;
- }
-
- bool Finish() {
- if (!ruleset_writer_.had_error() && ruleset_writer_.Finish()) {
- // Note: This line has effect only when |output_| is an ArrayOutputStream.
- ruleset_contents_.resize(output_->ByteCount());
- return true;
- }
- return false;
- }
-
- const std::vector<proto::UrlRule>& url_rules() const { return url_rules_; }
- const std::string& ruleset_contents() const { return ruleset_contents_; }
-
- private:
- std::vector<proto::UrlRule> url_rules_;
- std::string ruleset_contents_;
- std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> output_;
- UnindexedRulesetWriter ruleset_writer_;
-
- DISALLOW_COPY_AND_ASSIGN(UnindexedRulesetTestBuilder);
-};
-
-bool IsRulesetValid(const std::string& ruleset_contents,
- const std::vector<proto::UrlRule>& expected_url_rules) {
- google::protobuf::io::ArrayInputStream array_input(ruleset_contents.data(),
- ruleset_contents.size());
- UnindexedRulesetReader reader(&array_input);
- proto::FilteringRules chunk;
- std::vector<proto::UrlRule> read_rules;
- while (reader.ReadNextChunk(&chunk)) {
- read_rules.insert(read_rules.end(), chunk.url_rules().begin(),
- chunk.url_rules().end());
- }
- if (base::checked_cast<size_t>(reader.num_bytes_read()) !=
- ruleset_contents.size()) {
- return false;
- }
-
- if (expected_url_rules.size() != read_rules.size())
- return false;
- for (size_t i = 0, size = read_rules.size(); i != size; ++i) {
- if (!IsEqual(expected_url_rules[i], read_rules[i]))
- return false;
- }
- return true;
-}
-
-} // namespace
-
-TEST(UnindexedRulesetTest, EmptyRuleset) {
- UnindexedRulesetTestBuilder builder;
- EXPECT_TRUE(builder.Finish());
- EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
-}
-
-TEST(UnindexedRulesetTest, OneUrlRule) {
- UnindexedRulesetTestBuilder builder;
- EXPECT_TRUE(builder.AddUrlRule(UrlPattern("example.com"), kThirdParty));
- EXPECT_TRUE(builder.Finish());
- EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
-}
-
-TEST(UnindexedRulesetTest, ManyUrlRules) {
- UnindexedRulesetTestBuilder builder;
- EXPECT_TRUE(builder.AddUrlRules(1234));
- EXPECT_TRUE(builder.Finish());
- EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
-}
-
-TEST(UnindexedRulesetTest, ExactlyMaxRulesPerChunk) {
- UnindexedRulesetTestBuilder builder;
- EXPECT_TRUE(builder.AddUrlRules(builder.max_rules_per_chunk()));
- EXPECT_TRUE(builder.Finish());
- EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
-}
-
-TEST(UnindexedRulesetTest, MaxRulesPerChunkPlusOne) {
- UnindexedRulesetTestBuilder builder;
- EXPECT_TRUE(builder.AddUrlRules(builder.max_rules_per_chunk() + 1));
- EXPECT_TRUE(builder.Finish());
- EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
-}
-
-TEST(UnindexedRulesetTest, ErrorOnWrite) {
- UnindexedRulesetTestBuilder builder(1000);
- EXPECT_FALSE(builder.AddUrlRules(1234));
-}
-
-TEST(UnindexedRulesetTest, ReadCorruptedInput) {
- UnindexedRulesetTestBuilder builder;
- EXPECT_TRUE(builder.AddUrlRules(1000));
- EXPECT_TRUE(builder.Finish());
-
- {
- std::string ruleset_contents = builder.ruleset_contents();
- ASSERT_GE(ruleset_contents.size(), static_cast<size_t>(2000));
- ruleset_contents[100] ^= 239;
- ruleset_contents[1000] ^= 3;
- EXPECT_FALSE(IsRulesetValid(ruleset_contents, builder.url_rules()));
- }
-
- {
- std::string ruleset_contents = builder.ruleset_contents();
- ASSERT_GT(ruleset_contents.size(), static_cast<size_t>(100));
- ruleset_contents.resize(100);
- EXPECT_FALSE(IsRulesetValid(ruleset_contents, builder.url_rules()));
- }
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/url_pattern.cc b/chromium/components/subresource_filter/core/common/url_pattern.cc
deleted file mode 100644
index 381def4b45e..00000000000
--- a/chromium/components/subresource_filter/core/common/url_pattern.cc
+++ /dev/null
@@ -1,256 +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.
-
-// The matching logic distinguishes between the terms URL pattern and
-// subpattern. A URL pattern usually stands for the full thing, e.g.
-// "example.com^*path*par=val^", whereas subpattern denotes a maximal substring
-// of a pattern not containing the wildcard '*' character. For the example above
-// the subpatterns are: "example.com^", "path" and "par=val^".
-//
-// The separator placeholder '^' symbol is used in subpatterns to match any
-// separator character, which is any ASCII symbol except letters, digits, and
-// the following: '_', '-', '.', '%'. Note that the separator placeholder
-// character '^' is itself a separator, as well as '\0'.
-
-#include "components/subresource_filter/core/common/url_pattern.h"
-
-#include <stddef.h>
-
-#include <ostream>
-
-#include "base/logging.h"
-#include "components/subresource_filter/core/common/flat/url_pattern_index_generated.h"
-#include "components/subresource_filter/core/common/fuzzy_pattern_matching.h"
-#include "components/subresource_filter/core/common/string_splitter.h"
-#include "url/gurl.h"
-#include "url/third_party/mozilla/url_parse.h"
-
-namespace subresource_filter {
-
-namespace {
-
-class IsWildcard {
- public:
- bool operator()(char c) const { return c == '*'; }
-};
-
-proto::UrlPatternType ConvertUrlPatternType(flat::UrlPatternType type) {
- switch (type) {
- case flat::UrlPatternType_SUBSTRING:
- return proto::URL_PATTERN_TYPE_SUBSTRING;
- case flat::UrlPatternType_WILDCARDED:
- return proto::URL_PATTERN_TYPE_WILDCARDED;
- case flat::UrlPatternType_REGEXP:
- return proto::URL_PATTERN_TYPE_REGEXP;
- default:
- return proto::URL_PATTERN_TYPE_UNSPECIFIED;
- }
-}
-
-proto::AnchorType ConvertAnchorType(flat::AnchorType type) {
- switch (type) {
- case flat::AnchorType_NONE:
- return proto::ANCHOR_TYPE_NONE;
- case flat::AnchorType_BOUNDARY:
- return proto::ANCHOR_TYPE_BOUNDARY;
- case flat::AnchorType_SUBDOMAIN:
- return proto::ANCHOR_TYPE_SUBDOMAIN;
- default:
- return proto::ANCHOR_TYPE_UNSPECIFIED;
- }
-}
-
-base::StringPiece ConvertString(const flatbuffers::String* string) {
- return string ? base::StringPiece(string->data(), string->size())
- : base::StringPiece();
-}
-
-// Returns whether |position| within the |url| belongs to its |host| component
-// and corresponds to the beginning of a (sub-)domain.
-inline bool IsSubdomainAnchored(base::StringPiece url,
- url::Component host,
- size_t position) {
- DCHECK_LE(position, url.size());
- const size_t host_begin = static_cast<size_t>(host.begin);
- const size_t host_end = static_cast<size_t>(host.end());
- DCHECK_LE(host_end, url.size());
-
- return position == host_begin ||
- (position > host_begin && position <= host_end &&
- url[position - 1] == '.');
-}
-
-// Returns the position of the leftmost occurrence of a |subpattern| in the
-// |text| starting no earlier than |from| the specified position. If the
-// |subpattern| has separator placeholders, searches for a fuzzy occurrence.
-size_t FindSubpattern(base::StringPiece text,
- base::StringPiece subpattern,
- size_t from = 0) {
- const bool is_fuzzy =
- (subpattern.find(kSeparatorPlaceholder) != base::StringPiece::npos);
- return is_fuzzy ? FindFuzzy(text, subpattern, from)
- : text.find(subpattern, from);
-}
-
-// Same as FindSubpattern(url, subpattern), but searches for an occurrence that
-// starts at the beginning of a (sub-)domain within the url's |host| component.
-size_t FindSubdomainAnchoredSubpattern(base::StringPiece url,
- url::Component host,
- base::StringPiece subpattern) {
- const bool is_fuzzy =
- (subpattern.find(kSeparatorPlaceholder) != base::StringPiece::npos);
-
- for (size_t position = 0; position <= url.size(); ++position) {
- position = is_fuzzy ? FindFuzzy(url, subpattern, position)
- : url.find(subpattern, position);
- if (position == base::StringPiece::npos ||
- IsSubdomainAnchored(url, host, position)) {
- return position;
- }
- }
- return base::StringPiece::npos;
-}
-
-} // namespace
-
-UrlPattern::UrlPattern() = default;
-
-UrlPattern::UrlPattern(base::StringPiece url_pattern,
- proto::UrlPatternType type)
- : type_(type), url_pattern_(url_pattern) {}
-
-UrlPattern::UrlPattern(base::StringPiece url_pattern,
- proto::AnchorType anchor_left,
- proto::AnchorType anchor_right)
- : type_(proto::URL_PATTERN_TYPE_WILDCARDED),
- url_pattern_(url_pattern),
- anchor_left_(anchor_left),
- anchor_right_(anchor_right) {}
-
-UrlPattern::UrlPattern(const flat::UrlRule& rule)
- : type_(ConvertUrlPatternType(rule.url_pattern_type())),
- url_pattern_(ConvertString(rule.url_pattern())),
- anchor_left_(ConvertAnchorType(rule.anchor_left())),
- anchor_right_(ConvertAnchorType(rule.anchor_right())),
- match_case_(!!(rule.options() & flat::OptionFlag_IS_MATCH_CASE)) {}
-
-UrlPattern::UrlPattern(const proto::UrlRule& rule)
- : type_(rule.url_pattern_type()),
- url_pattern_(rule.url_pattern()),
- anchor_left_(rule.anchor_left()),
- anchor_right_(rule.anchor_right()),
- match_case_(rule.match_case()) {}
-
-UrlPattern::~UrlPattern() = default;
-
-bool UrlPattern::MatchesUrl(const GURL& url) const {
- // Note: Empty domains are also invalid.
- DCHECK(url.is_valid());
- DCHECK(type_ == proto::URL_PATTERN_TYPE_SUBSTRING ||
- type_ == proto::URL_PATTERN_TYPE_WILDCARDED);
-
- StringSplitter<IsWildcard> subpatterns(url_pattern_);
- auto subpattern_it = subpatterns.begin();
- auto subpattern_end = subpatterns.end();
-
- if (subpattern_it == subpattern_end) {
- return anchor_left_ == proto::ANCHOR_TYPE_NONE ||
- anchor_right_ == proto::ANCHOR_TYPE_NONE;
- }
-
- const base::StringPiece spec = url.possibly_invalid_spec();
- const url::Component host_part = url.parsed_for_possibly_invalid_spec().host;
- DCHECK(!spec.empty());
-
- base::StringPiece subpattern = *subpattern_it;
- ++subpattern_it;
-
- // If there is only one |subpattern|, and it has a right anchor, then simply
- // check that it is a suffix of the |spec|, and the left anchor is fulfilled.
- if (subpattern_it == subpattern_end &&
- anchor_right_ == proto::ANCHOR_TYPE_BOUNDARY) {
- if (!EndsWithFuzzy(spec, subpattern))
- return false;
- if (anchor_left_ == proto::ANCHOR_TYPE_BOUNDARY)
- return spec.size() == subpattern.size();
- if (anchor_left_ == proto::ANCHOR_TYPE_SUBDOMAIN) {
- DCHECK_LE(subpattern.size(), spec.size());
- return url.has_host() &&
- IsSubdomainAnchored(spec, host_part,
- spec.size() - subpattern.size());
- }
- return true;
- }
-
- // Otherwise, the first |subpattern| does not have to be a suffix. But it
- // still can have a left anchor. Check and handle that.
- base::StringPiece text = spec;
- if (anchor_left_ == proto::ANCHOR_TYPE_BOUNDARY) {
- if (!StartsWithFuzzy(spec, subpattern))
- return false;
- if (subpattern_it == subpattern_end) {
- DCHECK_EQ(anchor_right_, proto::ANCHOR_TYPE_NONE);
- return true;
- }
- text.remove_prefix(subpattern.size());
- } else if (anchor_left_ == proto::ANCHOR_TYPE_SUBDOMAIN) {
- if (!url.has_host())
- return false;
- const size_t match_begin =
- FindSubdomainAnchoredSubpattern(spec, host_part, subpattern);
- if (match_begin == base::StringPiece::npos)
- return false;
- if (subpattern_it == subpattern_end) {
- DCHECK_EQ(anchor_right_, proto::ANCHOR_TYPE_NONE);
- return true;
- }
- text.remove_prefix(match_begin + subpattern.size());
- } else {
- DCHECK_EQ(anchor_left_, proto::ANCHOR_TYPE_NONE);
- // Get back to the initial |subpattern|, process it in the loop below.
- subpattern_it = subpatterns.begin();
- }
-
- // Consecutively find all the remaining subpatterns in the |text|. If the
- // pattern has a right anchor, don't search for the last subpattern, but
- // instead check that it is a suffix of the |text|.
- while (subpattern_it != subpattern_end) {
- subpattern = *subpattern_it;
- DCHECK(!subpattern.empty());
-
- if (++subpattern_it == subpattern_end &&
- anchor_right_ == proto::ANCHOR_TYPE_BOUNDARY) {
- break;
- }
-
- const size_t match_position = FindSubpattern(text, subpattern);
- if (match_position == base::StringPiece::npos)
- return false;
- text.remove_prefix(match_position + subpattern.size());
- }
-
- return anchor_right_ != proto::ANCHOR_TYPE_BOUNDARY ||
- EndsWithFuzzy(text, subpattern);
-}
-
-std::ostream& operator<<(std::ostream& out, const UrlPattern& pattern) {
- // Note: Each fall-through in this switch is intentional.
- switch (pattern.anchor_left()) {
- case proto::ANCHOR_TYPE_SUBDOMAIN:
- out << '|';
- case proto::ANCHOR_TYPE_BOUNDARY:
- out << '|';
- default:
- break;
- }
- out << pattern.url_pattern();
- if (pattern.anchor_right() == proto::ANCHOR_TYPE_BOUNDARY)
- out << '|';
- if (pattern.match_case())
- out << "$match-case";
-
- return out;
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/url_pattern.h b/chromium/components/subresource_filter/core/common/url_pattern.h
deleted file mode 100644
index edbef99a6d4..00000000000
--- a/chromium/components/subresource_filter/core/common/url_pattern.h
+++ /dev/null
@@ -1,79 +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_CORE_COMMON_URL_PATTERN_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_URL_PATTERN_H_
-
-#include <iosfwd>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
-
-class GURL;
-
-namespace subresource_filter {
-
-namespace flat {
-struct UrlRule; // The FlatBuffers version of UrlRule.
-}
-
-// The structure used to mirror a URL pattern regardless of the representation
-// of the UrlRule that owns it, and to match it against URLs.
-class UrlPattern {
- public:
- UrlPattern();
-
- // Creates a |url_pattern| of a certain |type|.
- UrlPattern(base::StringPiece url_pattern,
- proto::UrlPatternType type = proto::URL_PATTERN_TYPE_WILDCARDED);
-
- // Creates a WILDCARDED |url_pattern| with the specified anchors.
- UrlPattern(base::StringPiece url_pattern,
- proto::AnchorType anchor_left,
- proto::AnchorType anchor_right);
-
- // The following constructors create UrlPattern from one of the UrlRule
- // representations. The passed in |rule| must outlive the created instance.
- explicit UrlPattern(const flat::UrlRule& rule);
- explicit UrlPattern(const proto::UrlRule& rule);
-
- ~UrlPattern();
-
- proto::UrlPatternType type() const { return type_; }
- base::StringPiece url_pattern() const { return url_pattern_; }
- proto::AnchorType anchor_left() const { return anchor_left_; }
- proto::AnchorType anchor_right() const { return anchor_right_; }
- bool match_case() const { return match_case_; }
-
- // Returns whether the |url| matches the URL |pattern|. Requires the type of
- // |this| pattern to be either SUBSTRING or WILDCARDED.
- //
- // Splits the pattern into subpatterns separated by '*' wildcards, and
- // greedily finds each of them in the spec of the |url|. Respects anchors at
- // either end of the pattern, and '^' separator placeholders when comparing a
- // subpattern to a subtring of the spec.
- bool MatchesUrl(const GURL& url) const;
-
- private:
- // TODO(pkalinnikov): Store flat:: types instead of proto::, in order to avoid
- // conversions in IndexedRuleset.
- proto::UrlPatternType type_ = proto::URL_PATTERN_TYPE_UNSPECIFIED;
- base::StringPiece url_pattern_;
-
- proto::AnchorType anchor_left_ = proto::ANCHOR_TYPE_NONE;
- proto::AnchorType anchor_right_ = proto::ANCHOR_TYPE_NONE;
-
- // TODO(pkalinnikov): Implement case-insensitive matching.
- bool match_case_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(UrlPattern);
-};
-
-// Allow pretty-printing URLPatterns when they are used in GTest assertions.
-std::ostream& operator<<(std::ostream& out, const UrlPattern& pattern);
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_URL_PATTERN_H_
diff --git a/chromium/components/subresource_filter/core/common/url_pattern_index.cc b/chromium/components/subresource_filter/core/common/url_pattern_index.cc
deleted file mode 100644
index 574f577a325..00000000000
--- a/chromium/components/subresource_filter/core/common/url_pattern_index.cc
+++ /dev/null
@@ -1,567 +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/subresource_filter/core/common/url_pattern_index.h"
-
-#include <algorithm>
-#include <limits>
-#include <string>
-
-#include "base/logging.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "components/subresource_filter/core/common/ngram_extractor.h"
-#include "components/subresource_filter/core/common/url_pattern.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace subresource_filter {
-
-namespace {
-
-using FlatStringOffset = flatbuffers::Offset<flatbuffers::String>;
-using FlatDomains = flatbuffers::Vector<FlatStringOffset>;
-using FlatDomainsOffset = flatbuffers::Offset<FlatDomains>;
-
-base::StringPiece ToStringPiece(const flatbuffers::String* string) {
- DCHECK(string);
- return base::StringPiece(string->c_str(), string->size());
-}
-
-// Performs three-way comparison between two domains. In the total order defined
-// by this predicate, the lengths of domains will be monotonically decreasing.
-int CompareDomains(base::StringPiece lhs_domain, base::StringPiece rhs_domain) {
- if (lhs_domain.size() != rhs_domain.size())
- return lhs_domain.size() > rhs_domain.size() ? -1 : 1;
- return lhs_domain.compare(rhs_domain);
-}
-
-bool HasNoUpperAscii(base::StringPiece string) {
- return std::none_of(string.begin(), string.end(),
- [](char c) { return base::IsAsciiUpper(c); });
-}
-
-// Checks whether a URL |rule| can be converted to its FlatBuffers equivalent,
-// and performs the actual conversion.
-class UrlRuleFlatBufferConverter {
- public:
- // Creates the converter, and initializes |is_convertible| bit. If
- // |is_convertible| == true, then all the fields, needed for serializing the
- // |rule| to FlatBuffer, are initialized (|options|, |anchor_right|, etc.).
- explicit UrlRuleFlatBufferConverter(const proto::UrlRule& rule)
- : rule_(rule) {
- is_convertible_ = InitializeOptions() && InitializeElementTypes() &&
- InitializeActivationTypes() && InitializeUrlPattern() &&
- IsMeaningful();
- }
-
- // Returns whether the |rule| can be converted to its FlatBuffers equivalent.
- // The conversion is not possible if the rule has attributes not supported by
- // this client version.
- bool is_convertible() const { return is_convertible_; }
-
- // Writes the URL |rule| to the FlatBuffer using the |builder|, and returns
- // the offset to the serialized rule.
- UrlRuleOffset SerializeConvertedRule(
- flatbuffers::FlatBufferBuilder* builder) const {
- DCHECK(is_convertible());
-
- FlatDomainsOffset domains_included_offset;
- FlatDomainsOffset domains_excluded_offset;
- if (rule_.domains_size()) {
- // TODO(pkalinnikov): Consider sharing the vectors between rules.
- std::vector<FlatStringOffset> domains_included;
- std::vector<FlatStringOffset> domains_excluded;
- // Reserve only for |domains_included| because it is expected to be the
- // one used more frequently.
- domains_included.reserve(rule_.domains_size());
-
- for (const auto& domain_list_item : rule_.domains()) {
- // Note: The |domain| can have non-ASCII UTF-8 characters, but
- // ToLowerASCII leaves these intact.
- // TODO(pkalinnikov): Convert non-ASCII characters to lower case too.
- // TODO(pkalinnikov): Possibly convert Punycode to IDN here or directly
- // assume this is done in the proto::UrlRule.
- const std::string& domain = domain_list_item.domain();
- auto offset = builder->CreateSharedString(
- HasNoUpperAscii(domain) ? domain : base::ToLowerASCII(domain));
-
- if (domain_list_item.exclude())
- domains_excluded.push_back(offset);
- else
- domains_included.push_back(offset);
- }
-
- // The comparator ensuring the domains order necessary for fast matching.
- auto precedes = [&builder](FlatStringOffset lhs, FlatStringOffset rhs) {
- return CompareDomains(ToStringPiece(flatbuffers::GetTemporaryPointer(
- *builder, lhs)),
- ToStringPiece(flatbuffers::GetTemporaryPointer(
- *builder, rhs))) < 0;
- };
-
- // The domains are stored in sorted order to support fast matching.
- if (!domains_included.empty()) {
- // TODO(pkalinnikov): Don't sort if it is already sorted offline.
- std::sort(domains_included.begin(), domains_included.end(), precedes);
- domains_included_offset = builder->CreateVector(domains_included);
- }
- if (!domains_excluded.empty()) {
- std::sort(domains_excluded.begin(), domains_excluded.end(), precedes);
- domains_excluded_offset = builder->CreateVector(domains_excluded);
- }
- }
-
- auto url_pattern_offset = builder->CreateString(rule_.url_pattern());
-
- return flat::CreateUrlRule(
- *builder, options_, element_types_, activation_types_,
- url_pattern_type_, anchor_left_, anchor_right_, domains_included_offset,
- domains_excluded_offset, url_pattern_offset);
- }
-
- private:
- static bool ConvertAnchorType(proto::AnchorType anchor_type,
- flat::AnchorType* result) {
- switch (anchor_type) {
- case proto::ANCHOR_TYPE_NONE:
- *result = flat::AnchorType_NONE;
- break;
- case proto::ANCHOR_TYPE_BOUNDARY:
- *result = flat::AnchorType_BOUNDARY;
- break;
- case proto::ANCHOR_TYPE_SUBDOMAIN:
- *result = flat::AnchorType_SUBDOMAIN;
- break;
- default:
- return false; // Unsupported anchor type.
- }
- return true;
- }
-
- bool InitializeOptions() {
- if (rule_.semantics() == proto::RULE_SEMANTICS_WHITELIST) {
- options_ |= flat::OptionFlag_IS_WHITELIST;
- } else if (rule_.semantics() != proto::RULE_SEMANTICS_BLACKLIST) {
- return false; // Unsupported semantics.
- }
-
- switch (rule_.source_type()) {
- case proto::SOURCE_TYPE_ANY:
- options_ |= flat::OptionFlag_APPLIES_TO_THIRD_PARTY;
- // Note: fall through here intentionally.
- case proto::SOURCE_TYPE_FIRST_PARTY:
- options_ |= flat::OptionFlag_APPLIES_TO_FIRST_PARTY;
- break;
- case proto::SOURCE_TYPE_THIRD_PARTY:
- options_ |= flat::OptionFlag_APPLIES_TO_THIRD_PARTY;
- break;
-
- default:
- return false; // Unsupported source type.
- }
-
- if (rule_.match_case())
- options_ |= flat::OptionFlag_IS_MATCH_CASE;
-
- return true;
- }
-
- bool InitializeElementTypes() {
- static_assert(
- proto::ELEMENT_TYPE_ALL <= std::numeric_limits<uint16_t>::max(),
- "Element types can not be stored in uint16_t.");
- element_types_ = static_cast<uint16_t>(rule_.element_types());
-
- // Note: Normally we can not distinguish between the main plugin resource
- // and any other loads it makes. We treat them both as OBJECT requests.
- if (element_types_ & proto::ELEMENT_TYPE_OBJECT_SUBREQUEST)
- element_types_ |= proto::ELEMENT_TYPE_OBJECT;
-
- // Ignore unknown element types.
- element_types_ &= proto::ELEMENT_TYPE_ALL;
- // Filtering popups is not supported.
- element_types_ &= ~proto::ELEMENT_TYPE_POPUP;
-
- return true;
- }
-
- bool InitializeActivationTypes() {
- static_assert(
- proto::ACTIVATION_TYPE_ALL <= std::numeric_limits<uint8_t>::max(),
- "Activation types can not be stored in uint8_t.");
- activation_types_ = static_cast<uint8_t>(rule_.activation_types());
-
- // Only the following activation types are supported, ignore the others.
- activation_types_ &=
- proto::ACTIVATION_TYPE_DOCUMENT | proto::ACTIVATION_TYPE_GENERICBLOCK;
-
- return true;
- }
-
- bool InitializeUrlPattern() {
- switch (rule_.url_pattern_type()) {
- case proto::URL_PATTERN_TYPE_SUBSTRING:
- url_pattern_type_ = flat::UrlPatternType_SUBSTRING;
- break;
- case proto::URL_PATTERN_TYPE_WILDCARDED:
- url_pattern_type_ = flat::UrlPatternType_WILDCARDED;
- break;
-
- // TODO(pkalinnikov): Implement REGEXP rules matching.
- case proto::URL_PATTERN_TYPE_REGEXP:
- default:
- return false; // Unsupported URL pattern type.
- }
-
- if (!ConvertAnchorType(rule_.anchor_left(), &anchor_left_) ||
- !ConvertAnchorType(rule_.anchor_right(), &anchor_right_)) {
- return false;
- }
- if (anchor_right_ == flat::AnchorType_SUBDOMAIN)
- return false; // Unsupported right anchor.
-
- return true;
- }
-
- // Returns whether the rule is not a no-op after all the modifications above.
- bool IsMeaningful() const { return element_types_ || activation_types_; }
-
- const proto::UrlRule& rule_;
-
- uint8_t options_ = 0;
- uint16_t element_types_ = 0;
- uint8_t activation_types_ = 0;
- flat::UrlPatternType url_pattern_type_ = flat::UrlPatternType_WILDCARDED;
- flat::AnchorType anchor_left_ = flat::AnchorType_NONE;
- flat::AnchorType anchor_right_ = flat::AnchorType_NONE;
-
- bool is_convertible_ = true;
-};
-
-} // namespace
-
-// Helpers. --------------------------------------------------------------------
-
-UrlRuleOffset SerializeUrlRule(const proto::UrlRule& rule,
- flatbuffers::FlatBufferBuilder* builder) {
- DCHECK(builder);
- UrlRuleFlatBufferConverter converter(rule);
- if (!converter.is_convertible())
- return UrlRuleOffset();
- DCHECK_NE(rule.url_pattern_type(), proto::URL_PATTERN_TYPE_REGEXP);
- return converter.SerializeConvertedRule(builder);
-}
-
-// UrlPatternIndexBuilder ------------------------------------------------------
-
-UrlPatternIndexBuilder::UrlPatternIndexBuilder(
- flatbuffers::FlatBufferBuilder* flat_builder)
- : flat_builder_(flat_builder) {
- DCHECK(flat_builder_);
-}
-
-UrlPatternIndexBuilder::~UrlPatternIndexBuilder() = default;
-
-void UrlPatternIndexBuilder::IndexUrlRule(UrlRuleOffset offset) {
- DCHECK(offset.o);
-
- const auto* rule = flatbuffers::GetTemporaryPointer(*flat_builder_, offset);
- DCHECK(rule);
- NGram ngram = GetMostDistinctiveNGram(ToStringPiece(rule->url_pattern()));
-
- if (ngram) {
- ngram_index_[ngram].push_back(offset);
- } else {
- // TODO(pkalinnikov): Index fallback rules as well.
- fallback_rules_.push_back(offset);
- }
-}
-
-UrlPatternIndexOffset UrlPatternIndexBuilder::Finish() {
- std::vector<flatbuffers::Offset<flat::NGramToRules>> flat_hash_table(
- ngram_index_.table_size());
-
- flatbuffers::Offset<flat::NGramToRules> empty_slot_offset =
- flat::CreateNGramToRules(*flat_builder_);
- for (size_t i = 0, size = ngram_index_.table_size(); i != size; ++i) {
- const uint32_t entry_index = ngram_index_.hash_table()[i];
- if (entry_index >= ngram_index_.size()) {
- flat_hash_table[i] = empty_slot_offset;
- continue;
- }
- const MutableNGramIndex::EntryType& entry =
- ngram_index_.entries()[entry_index];
- auto rules_offset = flat_builder_->CreateVector(entry.second);
- flat_hash_table[i] =
- flat::CreateNGramToRules(*flat_builder_, entry.first, rules_offset);
- }
- auto ngram_index_offset = flat_builder_->CreateVector(flat_hash_table);
-
- auto fallback_rules_offset = flat_builder_->CreateVector(fallback_rules_);
-
- return flat::CreateUrlPatternIndex(*flat_builder_, kNGramSize,
- ngram_index_offset, empty_slot_offset,
- fallback_rules_offset);
-}
-
-NGram UrlPatternIndexBuilder::GetMostDistinctiveNGram(
- base::StringPiece pattern) {
- size_t min_list_size = std::numeric_limits<size_t>::max();
- NGram best_ngram = 0;
-
- auto ngrams = CreateNGramExtractor<kNGramSize, NGram>(
- pattern, [](char c) { return c == '*' || c == '^'; });
-
- for (uint64_t ngram : ngrams) {
- const MutableUrlRuleList* rules = ngram_index_.Get(ngram);
- const size_t list_size = rules ? rules->size() : 0;
- if (list_size < min_list_size) {
- // TODO(pkalinnikov): Pick random of the same-sized lists.
- min_list_size = list_size;
- best_ngram = ngram;
- if (list_size == 0)
- break;
- }
- }
-
- return best_ngram;
-}
-
-// UrlPatternIndex -------------------------------------------------------------
-
-namespace {
-
-using FlatUrlRuleList = flatbuffers::Vector<flatbuffers::Offset<flat::UrlRule>>;
-using FlatNGramIndex =
- flatbuffers::Vector<flatbuffers::Offset<flat::NGramToRules>>;
-
-// Returns the size of the longest (sub-)domain of |origin| matching one of the
-// |domains| in the list.
-//
-// The |domains| should be sorted in descending order of their length, and
-// ascending alphabetical order within the groups of same-length domains.
-size_t GetLongestMatchingSubdomain(const url::Origin& origin,
- const FlatDomains& domains) {
- // If the |domains| list is short, then the simple strategy is usually faster.
- if (domains.size() <= 5) {
- for (auto* domain : domains) {
- const base::StringPiece domain_piece = ToStringPiece(domain);
- if (origin.DomainIs(domain_piece))
- return domain_piece.size();
- }
- return 0;
- }
- // Otherwise look for each subdomain of the |origin| using binary search.
-
- DCHECK(!origin.unique());
- base::StringPiece canonicalized_host(origin.host());
- if (canonicalized_host.empty())
- return 0;
-
- // If the host name ends with a dot, then ignore it.
- if (canonicalized_host.back() == '.')
- canonicalized_host.remove_suffix(1);
-
- // The |left| bound of the search is shared between iterations, because
- // subdomains are considered in decreasing order of their lengths, therefore
- // each consecutive lower_bound will be at least as far as the previous.
- flatbuffers::uoffset_t left = 0;
- for (size_t position = 0;; ++position) {
- const base::StringPiece subdomain = canonicalized_host.substr(position);
-
- flatbuffers::uoffset_t right = domains.size();
- while (left + 1 < right) {
- auto middle = left + (right - left) / 2;
- DCHECK_LT(middle, domains.size());
- if (CompareDomains(ToStringPiece(domains[middle]), subdomain) <= 0)
- left = middle;
- else
- right = middle;
- }
-
- DCHECK_LT(left, domains.size());
- if (ToStringPiece(domains[left]) == subdomain)
- return subdomain.size();
-
- position = canonicalized_host.find('.', position);
- if (position == base::StringPiece::npos)
- break;
- }
-
- return 0;
-}
-
-// Returns whether the |origin| matches the domain list of the |rule|. A match
-// means that the longest domain in |domains| that |origin| is a sub-domain of
-// is not an exception OR all the |domains| are exceptions and neither matches
-// the |origin|. Thus, domain filters with more domain components trump filters
-// with fewer domain components, i.e. the more specific a filter is, the higher
-// the priority.
-//
-// A rule whose domain list is empty or contains only negative domains is still
-// considered a "generic" rule. Therefore, if |disable_generic_rules| is set,
-// this function will always return false for such rules.
-bool DoesOriginMatchDomainList(const url::Origin& origin,
- const flat::UrlRule& rule,
- bool disable_generic_rules) {
- const bool is_generic = !rule.domains_included();
- DCHECK(is_generic || rule.domains_included()->size());
- if (disable_generic_rules && is_generic)
- return false;
-
- // Unique |origin| matches lists of exception domains only.
- if (origin.unique())
- return is_generic;
-
- size_t longest_matching_included_domain_length = 1;
- if (!is_generic) {
- longest_matching_included_domain_length =
- GetLongestMatchingSubdomain(origin, *rule.domains_included());
- }
- if (longest_matching_included_domain_length && rule.domains_excluded()) {
- return GetLongestMatchingSubdomain(origin, *rule.domains_excluded()) <
- longest_matching_included_domain_length;
- }
- return !!longest_matching_included_domain_length;
-}
-
-// Returns whether the request matches flags of the specified URL |rule|. Takes
-// into account:
-// - |element_type| of the requested resource, if not *_UNSPECIFIED.
-// - |activation_type| for a subdocument request, if not *_UNSPECIFIED.
-// - Whether the resource |is_third_party| w.r.t. its embedding document.
-bool DoesRuleFlagsMatch(const flat::UrlRule& rule,
- proto::ElementType element_type,
- proto::ActivationType activation_type,
- bool is_third_party) {
- DCHECK((element_type == proto::ELEMENT_TYPE_UNSPECIFIED) !=
- (activation_type == proto::ACTIVATION_TYPE_UNSPECIFIED));
-
- if (element_type != proto::ELEMENT_TYPE_UNSPECIFIED &&
- !(rule.element_types() & element_type)) {
- return false;
- }
- if (activation_type != proto::ACTIVATION_TYPE_UNSPECIFIED &&
- !(rule.activation_types() & activation_type)) {
- return false;
- }
-
- if (is_third_party &&
- !(rule.options() & flat::OptionFlag_APPLIES_TO_THIRD_PARTY)) {
- return false;
- }
- if (!is_third_party &&
- !(rule.options() & flat::OptionFlag_APPLIES_TO_FIRST_PARTY)) {
- return false;
- }
-
- return true;
-}
-
-const flat::UrlRule* FindMatchAmongCandidates(
- const FlatUrlRuleList* candidates,
- const GURL& url,
- const url::Origin& document_origin,
- proto::ElementType element_type,
- proto::ActivationType activation_type,
- bool is_third_party,
- bool disable_generic_rules) {
- if (!candidates)
- return nullptr;
- for (const flat::UrlRule* rule : *candidates) {
- DCHECK_NE(rule, nullptr);
- DCHECK_NE(rule->url_pattern_type(), flat::UrlPatternType_REGEXP);
- if (!DoesRuleFlagsMatch(*rule, element_type, activation_type,
- is_third_party)) {
- continue;
- }
- if (!UrlPattern(*rule).MatchesUrl(url))
- continue;
-
- if (DoesOriginMatchDomainList(document_origin, *rule,
- disable_generic_rules)) {
- return rule;
- }
- }
-
- return nullptr;
-}
-
-// Returns whether the network request matches a UrlPattern |index| represented
-// in its FlatBuffers format. |is_third_party| should reflect the relation
-// between |url| and |document_origin|.
-const flat::UrlRule* FindMatchInFlatUrlPatternIndex(
- const flat::UrlPatternIndex& index,
- const GURL& url,
- const url::Origin& document_origin,
- proto::ElementType element_type,
- proto::ActivationType activation_type,
- bool is_third_party,
- bool disable_generic_rules) {
- const FlatNGramIndex* hash_table = index.ngram_index();
- const flat::NGramToRules* empty_slot = index.ngram_index_empty_slot();
- DCHECK_NE(hash_table, nullptr);
-
- NGramHashTableProber prober;
-
- auto ngrams = CreateNGramExtractor<kNGramSize, uint64_t>(
- url.spec(), [](char) { return false; });
- for (uint64_t ngram : ngrams) {
- const size_t slot_index = prober.FindSlot(
- ngram, base::strict_cast<size_t>(hash_table->size()),
- [hash_table, empty_slot](NGram ngram, size_t slot_index) {
- const flat::NGramToRules* entry = hash_table->Get(slot_index);
- DCHECK_NE(entry, nullptr);
- return entry == empty_slot || entry->ngram() == ngram;
- });
- DCHECK_LT(slot_index, hash_table->size());
-
- const flat::NGramToRules* entry = hash_table->Get(slot_index);
- if (entry == empty_slot)
- continue;
- const flat::UrlRule* rule = FindMatchAmongCandidates(
- entry->rule_list(), url, document_origin, element_type, activation_type,
- is_third_party, disable_generic_rules);
- if (rule)
- return rule;
- }
-
- const FlatUrlRuleList* rules = index.fallback_rules();
- return FindMatchAmongCandidates(rules, url, document_origin, element_type,
- activation_type, is_third_party,
- disable_generic_rules);
-}
-
-} // namespace
-
-UrlPatternIndexMatcher::UrlPatternIndexMatcher(
- const flat::UrlPatternIndex* flat_index)
- : flat_index_(flat_index) {
- DCHECK(!flat_index || flat_index->n() == kNGramSize);
-}
-
-UrlPatternIndexMatcher::~UrlPatternIndexMatcher() = default;
-
-const flat::UrlRule* UrlPatternIndexMatcher::FindMatch(
- const GURL& url,
- const url::Origin& first_party_origin,
- proto::ElementType element_type,
- proto::ActivationType activation_type,
- bool is_third_party,
- bool disable_generic_rules) const {
- if (!flat_index_ || !url.is_valid())
- return nullptr;
- if ((element_type == proto::ELEMENT_TYPE_UNSPECIFIED) ==
- (activation_type == proto::ACTIVATION_TYPE_UNSPECIFIED)) {
- return nullptr;
- }
-
- return FindMatchInFlatUrlPatternIndex(*flat_index_, url, first_party_origin,
- element_type, activation_type,
- is_third_party, disable_generic_rules);
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/url_pattern_index.h b/chromium/components/subresource_filter/core/common/url_pattern_index.h
deleted file mode 100644
index 30224dce768..00000000000
--- a/chromium/components/subresource_filter/core/common/url_pattern_index.h
+++ /dev/null
@@ -1,135 +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_SUBRESOURCE_FILTER_CORE_COMMON_URL_PATTERN_INDEX_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_URL_PATTERN_INDEX_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string_piece_forward.h"
-#include "components/subresource_filter/core/common/closed_hash_map.h"
-#include "components/subresource_filter/core/common/flat/url_pattern_index_generated.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
-#include "components/subresource_filter/core/common/uint64_hasher.h"
-#include "third_party/flatbuffers/src/include/flatbuffers/flatbuffers.h"
-
-class GURL;
-
-namespace url {
-class Origin;
-}
-
-namespace subresource_filter {
-
-// The integer type used to represent N-grams.
-using NGram = uint64_t;
-// The hasher used for hashing N-grams.
-using NGramHasher = Uint64Hasher;
-// The hash table probe sequence used both by UrlPatternIndex and its builder.
-using NGramHashTableProber = DefaultProber<NGram, NGramHasher>;
-
-// FlatBuffer offset aliases.
-using UrlRuleOffset = flatbuffers::Offset<flat::UrlRule>;
-using UrlPatternIndexOffset = flatbuffers::Offset<flat::UrlPatternIndex>;
-
-constexpr size_t kNGramSize = 5;
-static_assert(kNGramSize <= sizeof(NGram), "NGram type is too narrow.");
-
-// Serializes the |rule| to the FlatBuffer |builder|, and returns an offset to
-// it in the resulting buffer. Returns null offset iff the |rule| could not be
-// serialized because of unsupported options or it is otherwise invalid.
-UrlRuleOffset SerializeUrlRule(const proto::UrlRule& rule,
- flatbuffers::FlatBufferBuilder* builder);
-
-// The class used to construct an index over the URL patterns of a set of URL
-// rules. The rules themselves need to be converted to FlatBuffers format by the
-// client of this class, as well as persisted into the |flat_builder| that is
-// supplied in the constructor.
-class UrlPatternIndexBuilder {
- public:
- explicit UrlPatternIndexBuilder(flatbuffers::FlatBufferBuilder* flat_builder);
- ~UrlPatternIndexBuilder();
-
- // Adds a UrlRule to the index. The caller should have already persisted the
- // rule into the same |flat_builder| by a call to SerializeUrlRule returning a
- // non-null |offset|, and should pass in the resulting |offset| here.
- void IndexUrlRule(UrlRuleOffset offset);
-
- // Finalizes construction of the index, serializes it using |flat_builder|,
- // and returns an offset to it in the resulting FlatBuffer.
- UrlPatternIndexOffset Finish();
-
- private:
- using MutableUrlRuleList = std::vector<UrlRuleOffset>;
- using MutableNGramIndex =
- ClosedHashMap<NGram, MutableUrlRuleList, NGramHashTableProber>;
-
- // Returns an N-gram of the |pattern| encoded into the NGram integer type. The
- // N-gram is picked using a greedy heuristic, i.e. the one is chosen which
- // corresponds to the shortest list of rules within the index. If there are no
- // valid N-grams in the |pattern|, the return value is 0.
- NGram GetMostDistinctiveNGram(base::StringPiece pattern);
-
- // This index contains all non-REGEXP rules that have at least one acceptable
- // N-gram. For each given rule, the N-gram used as an index key is picked
- // greedily (see GetMostDistinctiveNGram).
- MutableNGramIndex ngram_index_;
-
- // A fallback list that contains all the rules with no acceptable N-gram.
- MutableUrlRuleList fallback_rules_;
-
- // Must outlive this instance.
- flatbuffers::FlatBufferBuilder* flat_builder_;
-
- DISALLOW_COPY_AND_ASSIGN(UrlPatternIndexBuilder);
-};
-
-// Encapsulates a read-only index built over the URL patterns of a set of URL
-// rules, and provides fast matching of network requests against these rules.
-class UrlPatternIndexMatcher {
- public:
- // Creates an instance to access the given |flat_index|. If |flat_index| is
- // nullptr, then all requests return no match.
- explicit UrlPatternIndexMatcher(const flat::UrlPatternIndex* flat_index);
- ~UrlPatternIndexMatcher();
-
- // If the index contains one or more UrlRules that match the request, returns
- // one of them (it is undefined which one). Otherwise, returns nullptr.
- //
- // Notes on parameters:
- // - |url| should be valid, otherwise the return value is nullptr.
- // - Exactly one of |element_type| and |activation_type| should be specified,
- // i.e., not equal to *_UNSPECIFIED, otherwise the return value is nullptr.
- // - |is_third_party| should be pre-computed by the caller, e.g. using the
- // registry_controlled_domains library, to reflect the relation between
- // |url| and |first_party_origin|.
- //
- // A rule is deemed to match the request iff all of the following applies:
- // - The |url| matches the rule's UrlPattern (see url_pattern.h).
- // - The |first_party_origin| matches the rule's targeted domains list.
- // - |element_type| or |activation_type| is among the rule's targeted types.
- // - The |is_third_party| bit matches the rule's requirement on the requested
- // |url| being first-/third-party w.r.t. its |first_party_origin|.
- // - The rule is not generic if |disable_generic_rules| is true.
- const flat::UrlRule* FindMatch(const GURL& url,
- const url::Origin& first_party_origin,
- proto::ElementType element_type,
- proto::ActivationType activation_type,
- bool is_third_party,
- bool disable_generic_rules) const;
-
- private:
- // Must outlive this instance.
- const flat::UrlPatternIndex* flat_index_;
-
- DISALLOW_COPY_AND_ASSIGN(UrlPatternIndexMatcher);
-};
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_URL_PATTERN_INDEX_H_
diff --git a/chromium/components/subresource_filter/core/common/url_pattern_index_unittest.cc b/chromium/components/subresource_filter/core/common/url_pattern_index_unittest.cc
deleted file mode 100644
index fcdb9da54c4..00000000000
--- a/chromium/components/subresource_filter/core/common/url_pattern_index_unittest.cc
+++ /dev/null
@@ -1,707 +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/subresource_filter/core/common/url_pattern_index.h"
-
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "components/subresource_filter/core/common/url_pattern.h"
-#include "components/subresource_filter/core/common/url_rule_test_support.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace subresource_filter {
-
-using namespace testing;
-
-class UrlPatternIndexTest : public ::testing::Test {
- public:
- UrlPatternIndexTest() { Reset(); }
-
- protected:
- bool AddUrlRule(const proto::UrlRule& rule) {
- auto offset = SerializeUrlRule(rule, flat_builder_.get());
- if (offset.o)
- index_builder_->IndexUrlRule(offset);
- return !!offset.o;
- }
-
- void Finish() {
- const auto index_offset = index_builder_->Finish();
- flat_builder_->Finish(index_offset);
-
- const flat::UrlPatternIndex* flat_index =
- flat::GetUrlPatternIndex(flat_builder_->GetBufferPointer());
- index_matcher_.reset(new UrlPatternIndexMatcher(flat_index));
- }
-
- const flat::UrlRule* FindMatch(
- base::StringPiece url_string,
- base::StringPiece document_origin_string = base::StringPiece(),
- proto::ElementType element_type = kOther,
- proto::ActivationType activation_type = kNoActivation,
- bool disable_generic_rules = false) {
- const GURL url(url_string);
- const url::Origin document_origin = GetOrigin(document_origin_string);
- return index_matcher_->FindMatch(
- url, document_origin, element_type, activation_type,
- IsThirdParty(url, document_origin), disable_generic_rules);
- }
-
- bool IsOutOfRange(const flat::UrlRule* rule) const {
- if (!rule)
- return false;
- const auto* data = reinterpret_cast<const uint8_t*>(rule);
- return data < flat_builder_->GetBufferPointer() ||
- data >= flat_builder_->GetBufferPointer() + flat_builder_->GetSize();
- }
-
- void Reset() {
- index_matcher_.reset();
- index_builder_.reset();
- flat_builder_.reset(new flatbuffers::FlatBufferBuilder());
- index_builder_.reset(new UrlPatternIndexBuilder(flat_builder_.get()));
- }
-
- private:
- std::unique_ptr<flatbuffers::FlatBufferBuilder> flat_builder_;
- std::unique_ptr<UrlPatternIndexBuilder> index_builder_;
- std::unique_ptr<UrlPatternIndexMatcher> index_matcher_;
-
- DISALLOW_COPY_AND_ASSIGN(UrlPatternIndexTest);
-};
-
-TEST_F(UrlPatternIndexTest, EmptyIndex) {
- Finish();
- EXPECT_FALSE(FindMatch(base::StringPiece() /* url */));
- EXPECT_FALSE(FindMatch("http://example.com"));
- EXPECT_FALSE(FindMatch("http://another.example.com?param=val"));
-}
-
-TEST_F(UrlPatternIndexTest, OneSimpleRule) {
- ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("?param=", kSubstring))));
- Finish();
-
- EXPECT_FALSE(FindMatch("https://example.com"));
- EXPECT_TRUE(FindMatch("http://example.org?param=image1"));
-}
-
-TEST_F(UrlPatternIndexTest, NoRuleApplies) {
- ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("?filter_out=", kSubstring))));
- ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("&filter_out=", kSubstring))));
- Finish();
-
- EXPECT_FALSE(FindMatch("http://example.com"));
- EXPECT_FALSE(FindMatch("http://example.com?filter_not"));
- EXPECT_FALSE(FindMatch("http://example.com?k=v&filter_not"));
-}
-
-TEST_F(UrlPatternIndexTest, OneRuleWithoutMetaInfo) {
- const struct {
- UrlPattern url_pattern;
- const char* url;
- bool expect_match;
- } kTestCases[] = {
- // SUBSTRING
- {{"abcd", kSubstring}, "http://ex.com/abcd", true},
- {{"abcd", kSubstring}, "http://ex.com/dcab", false},
- {{"42", kSubstring}, "http://ex.com/adcd/picture42.png", true},
- {{"&test", kSubstring},
- "http://ex.com/params?param1=false&test=true",
- true},
- {{"-test-42.", kSubstring}, "http://ex.com/unit-test-42.1", true},
- {{"/abcdtest160x600.", kSubstring},
- "http://ex.com/abcdtest160x600.png",
- true},
-
- // WILDCARDED
- {{"http://ex.com/abcd/picture*.png"},
- "http://ex.com/abcd/picture42.png",
- true},
- {{"ex.com", kSubdomain, kAnchorNone}, "http://ex.com", true},
- {{"ex.com", kSubdomain, kAnchorNone}, "http://test.ex.com", true},
- {{"ex.com", kSubdomain, kAnchorNone}, "https://test.ex.com.com", true},
- {{"ex.com", kSubdomain, kAnchorNone}, "https://test.rest.ex.com", true},
- {{"ex.com", kSubdomain, kAnchorNone}, "https://test_ex.com", false},
-
- {{"http://ex.com", kBoundary, kAnchorNone}, "http://ex.com/", true},
- {{"http://ex.com", kBoundary, kAnchorNone}, "http://ex.com/42", true},
- {{"http://ex.com", kBoundary, kAnchorNone},
- "http://ex.com/42/http://ex.com/",
- true},
- {{"http://ex.com", kBoundary, kAnchorNone},
- "http://ex.com/42/http://ex.info/",
- true},
- {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com", true},
- {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com/42", false},
- {{"http://ex.com/", kBoundary, kBoundary},
- "http://ex.info/42/http://ex.com/",
- false},
- {{"http://ex.com/", kBoundary, kBoundary},
- "http://ex.info/42/http://ex.com/",
- false},
- {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com/", true},
- {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com/42.swf", false},
- {{"http://ex.com/", kBoundary, kBoundary},
- "http://ex.info/redirect/http://ex.com/",
- false},
- {{"pdf", kAnchorNone, kBoundary}, "http://ex.com/abcd.pdf", true},
- {{"pdf", kAnchorNone, kBoundary}, "http://ex.com/pdfium", false},
- {{"http://ex.com^"}, "http://ex.com/", true},
- {{"http://ex.com^"}, "http://ex.com:8000/", true},
- {{"http://ex.com^"}, "http://ex.com.ru", false},
- {{"^ex.com^"},
- "http://ex.com:8000/42.loss?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
- true},
- {{"^42.loss^"},
- "http://ex.com:8000/42.loss?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
- true},
-
- // TODO(pkalinnikov): The '^' at the end should match end-of-string.
- //
- // {"^%D1%82%D0%B5%D1%81%D1%82^",
- // "http://ex.com:8000/42.loss?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
- // true},
- // {"/abcd/*/picture^", "http://ex.com/abcd/42/picture", true},
-
- {{"/abcd/*/picture^"}, "http://ex.com/abcd/42/loss/picture?param", true},
- {{"/abcd/*/picture^"}, "http://ex.com/abcd//picture/42", true},
- {{"/abcd/*/picture^"}, "http://ex.com/abcd/picture", false},
- {{"/abcd/*/picture^"}, "http://ex.com/abcd/42/pictureraph", false},
- {{"/abcd/*/picture^"}, "http://ex.com/abcd/42/picture.swf", false},
- {{"test.ex.com^", kSubdomain, kAnchorNone},
- "http://test.ex.com/42.swf",
- true},
- {{"test.ex.com^", kSubdomain, kAnchorNone},
- "http://server1.test.ex.com/42.swf",
- true},
- {{"test.ex.com^", kSubdomain, kAnchorNone},
- "https://test.ex.com:8000/",
- true},
- {{"test.ex.com^", kSubdomain, kAnchorNone},
- "http://test.ex.com.ua/42.swf",
- false},
- {{"test.ex.com^", kSubdomain, kAnchorNone},
- "http://ex.com/redirect/http://test.ex.com/",
- false},
-
- {{"/abcd/*"}, "https://ex.com/abcd/", true},
- {{"/abcd/*"}, "http://ex.com/abcd/picture.jpeg", true},
- {{"/abcd/*"}, "https://ex.com/abcd", false},
- {{"/abcd/*"}, "http://abcd.ex.com", false},
- {{"*/abcd/"}, "https://ex.com/abcd/", true},
- {{"*/abcd/"}, "http://ex.com/abcd/picture.jpeg", true},
- {{"*/abcd/"}, "https://ex.com/test-abcd/", false},
- {{"*/abcd/"}, "http://abcd.ex.com", false},
- };
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(::testing::Message() << "UrlPattern: " << test_case.url_pattern
- << "; URL: " << test_case.url);
-
- ASSERT_TRUE(AddUrlRule(MakeUrlRule(test_case.url_pattern)));
- Finish();
-
- EXPECT_EQ(test_case.expect_match, !!FindMatch(test_case.url));
- Reset();
- }
-}
-
-TEST_F(UrlPatternIndexTest, OneRuleWithThirdParty) {
- const struct {
- const char* url_pattern;
- proto::SourceType source_type;
-
- const char* url;
- const char* document_origin;
- bool expect_match;
- } kTestCases[] = {
- {"ex.com", kThirdParty, "http://ex.com", "http://exmpl.org", true},
- {"ex.com", kThirdParty, "http://ex.com", "http://ex.com", false},
- {"ex.com", kThirdParty, "http://ex.com/path?k=v", "http://exmpl.org",
- true},
- {"ex.com", kThirdParty, "http://ex.com/path?k=v", "http://ex.com", false},
- {"ex.com", kFirstParty, "http://ex.com/path?k=v", "http://ex.com", true},
- {"ex.com", kFirstParty, "http://ex.com/path?k=v", "http://exmpl.com",
- false},
- {"ex.com", kAnyParty, "http://ex.com/path?k=v", "http://ex.com", true},
- {"ex.com", kAnyParty, "http://ex.com/path?k=v", "http://exmpl.com", true},
- {"ex.com", kThirdParty, "http://subdomain.ex.com", "http://ex.com",
- false},
- {"ex.com", kThirdParty, "http://ex.com", nullptr, true},
-
- // Public Suffix List tests.
- {"ex.com", kThirdParty, "http://two.ex.com", "http://one.ex.com", false},
- {"ex.com", kThirdParty, "http://ex.com", "http://one.ex.com", false},
- {"ex.com", kThirdParty, "http://two.ex.com", "http://ex.com", false},
- {"ex.com", kThirdParty, "http://ex.com", "http://example.org", true},
- {"appspot.com", kThirdParty, "http://two.appspot.org",
- "http://one.appspot.com", false},
- };
-
- for (auto test_case : kTestCases) {
- SCOPED_TRACE(::testing::Message()
- << "UrlPattern: " << test_case.url_pattern
- << "; SourceType: " << static_cast<int>(test_case.source_type)
- << "; URL: " << test_case.url
- << "; DocumentOrigin: " << test_case.document_origin);
-
- auto rule = MakeUrlRule(UrlPattern(test_case.url_pattern, kSubstring));
- rule.set_source_type(test_case.source_type);
- ASSERT_TRUE(AddUrlRule(rule));
- Finish();
-
- EXPECT_EQ(test_case.expect_match,
- !!FindMatch(test_case.url, test_case.document_origin));
- Reset();
- }
-}
-
-TEST_F(UrlPatternIndexTest, OneRuleWithDomainList) {
- constexpr const char* kUrl = "http://example.com";
-
- const struct {
- std::vector<std::string> domains;
- const char* document_origin;
- bool expect_match;
- } kTestCases[] = {
- {std::vector<std::string>(), nullptr, true},
- {std::vector<std::string>(), "http://domain.com", true},
-
- {{"domain.com"}, nullptr, false},
- {{"domain.com"}, "http://domain.com", true},
- {{"ddomain.com"}, "http://domain.com", false},
- {{"domain.com"}, "http://ddomain.com", false},
- {{"domain.com"}, "http://sub.domain.com", true},
- {{"sub.domain.com"}, "http://domain.com", false},
- {{"sub.domain.com"}, "http://sub.domain.com", true},
- {{"sub.domain.com"}, "http://a.b.c.sub.domain.com", true},
- {{"sub.domain.com"}, "http://sub.domain.com.com", false},
-
- // TODO(pkalinnikov): Probably need to canonicalize domain patterns to
- // avoid subtleties like below.
- {{"domain.com"}, "http://domain.com.", true},
- {{"domain.com"}, "http://.domain.com", true},
- {{"domain.com"}, "http://.domain.com.", true},
- {{".domain.com"}, "http://.domain.com", true},
- {{"domain.com."}, "http://domain.com", false},
- {{"domain.com."}, "http://domain.com.", true},
-
- {{"domain..com"}, "http://domain.com", false},
- {{"domain.com"}, "http://domain..com", false},
- {{"domain..com"}, "http://domain..com", true},
-
- {{"~domain.com"}, nullptr, true},
- {{"~domain.com"}, "http://domain.com", false},
- {{"~ddomain.com"}, "http://domain.com", true},
- {{"~domain.com"}, "http://ddomain.com", true},
- {{"~domain.com"}, "http://sub.domain.com", false},
- {{"~sub.domain.com"}, "http://domain.com", true},
- {{"~sub.domain.com"}, "http://sub.domain.com", false},
- {{"~sub.domain.com"}, "http://a.b.c.sub.domain.com", false},
- {{"~sub.domain.com"}, "http://sub.domain.com.com", true},
-
- {{"domain1.com", "domain2.com"}, nullptr, false},
- {{"domain1.com", "domain2.com"}, "http://domain1.com", true},
- {{"domain1.com", "domain2.com"}, "http://domain2.com", true},
- {{"domain1.com", "domain2.com"}, "http://domain3.com", false},
- {{"domain1.com", "domain2.com"}, "http://not_domain1.com", false},
- {{"domain1.com", "domain2.com"}, "http://sub.domain1.com", true},
- {{"domain1.com", "domain2.com"}, "http://a.b.c.sub.domain2.com", true},
-
- {{"~domain1.com", "~domain2.com"}, "http://domain1.com", false},
- {{"~domain1.com", "~domain2.com"}, "http://domain2.com", false},
- {{"~domain1.com", "~domain2.com"}, "http://domain3.com", true},
-
- {{"domain.com", "~sub.domain.com"}, "http://domain.com", true},
- {{"domain.com", "~sub.domain.com"}, "http://sub.domain.com", false},
- {{"domain.com", "~sub.domain.com"}, "http://a.b.sub.domain.com", false},
- {{"domain.com", "~sub.domain.com"}, "http://ssub.domain.com", true},
-
- {{"domain.com", "~a.domain.com", "~b.domain.com"},
- "http://domain.com",
- true},
- {{"domain.com", "~a.domain.com", "~b.domain.com"},
- "http://a.domain.com",
- false},
- {{"domain.com", "~a.domain.com", "~b.domain.com"},
- "http://b.domain.com",
- false},
-
- {{"domain.com", "~a.domain.com", "b.a.domain.com"},
- "http://domain.com",
- true},
- {{"domain.com", "~a.domain.com", "b.a.domain.com"},
- "http://a.domain.com",
- false},
- {{"domain.com", "~a.domain.com", "b.a.domain.com"},
- "http://b.a.domain.com",
- true},
- {{"domain.com", "~a.domain.com", "b.a.domain.com"},
- "http://c.b.a.domain.com",
- true},
-
- // The following test addresses a former bug in domain list matcher. When
- // "domain.com" was matched, the positive filters lookup stopped, and the
- // next domain was considered as a negative. The initial character was
- // skipped (supposing it's a '~') and the remainder was considered a
- // domain. So "ddomain.com" would be matched and thus the whole rule would
- // be classified as non-matching, which is not correct.
- {{"domain.com", "ddomain.com", "~sub.domain.com"},
- "http://domain.com",
- true},
- };
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(::testing::Message()
- << "Domains: " << ::testing::PrintToString(test_case.domains)
- << "; DocumentOrigin: " << test_case.document_origin);
-
- auto rule = MakeUrlRule(UrlPattern(kUrl, kSubstring));
- AddDomains(test_case.domains, &rule);
- ASSERT_TRUE(AddUrlRule(rule));
- Finish();
-
- EXPECT_EQ(test_case.expect_match,
- !!FindMatch(kUrl, test_case.document_origin));
- Reset();
- }
-}
-
-TEST_F(UrlPatternIndexTest, OneRuleWithLongDomainList) {
- constexpr const char* kUrl = "http://example.com";
- constexpr size_t kDomains = 200;
-
- std::vector<std::string> domains;
- for (size_t i = 0; i < kDomains; ++i) {
- const std::string domain = "domain" + std::to_string(i) + ".com";
- domains.push_back(domain);
- domains.push_back("~sub." + domain);
- domains.push_back("a.sub." + domain);
- domains.push_back("b.sub." + domain);
- domains.push_back("c.sub." + domain);
- domains.push_back("~aa.sub." + domain);
- domains.push_back("~ab.sub." + domain);
- domains.push_back("~ba.sub." + domain);
- domains.push_back("~bb.sub." + domain);
- domains.push_back("~sub.sub.c.sub." + domain);
- }
-
- auto rule = MakeUrlRule(UrlPattern(kUrl, kSubstring));
- AddDomains(domains, &rule);
- ASSERT_TRUE(AddUrlRule(rule));
- Finish();
-
- for (size_t i = 0; i < kDomains; ++i) {
- SCOPED_TRACE(::testing::Message() << "Iteration: " << i);
- const std::string domain = "domain" + std::to_string(i) + ".com";
-
- EXPECT_TRUE(FindMatch(kUrl, "http://" + domain));
- EXPECT_FALSE(FindMatch(kUrl, "http://sub." + domain));
- EXPECT_TRUE(FindMatch(kUrl, "http://a.sub." + domain));
- EXPECT_TRUE(FindMatch(kUrl, "http://b.sub." + domain));
- EXPECT_TRUE(FindMatch(kUrl, "http://c.sub." + domain));
- EXPECT_FALSE(FindMatch(kUrl, "http://aa.sub." + domain));
- EXPECT_FALSE(FindMatch(kUrl, "http://ab.sub." + domain));
- EXPECT_FALSE(FindMatch(kUrl, "http://ba.sub." + domain));
- EXPECT_FALSE(FindMatch(kUrl, "http://bb.sub." + domain));
- EXPECT_TRUE(FindMatch(kUrl, "http://sub.c.sub." + domain));
- EXPECT_FALSE(FindMatch(kUrl, "http://sub.sub.c.sub." + domain));
- }
-}
-
-TEST_F(UrlPatternIndexTest, OneRuleWithElementTypes) {
- constexpr auto kAll = kAllElementTypes;
- const struct {
- const char* url_pattern;
- int32_t element_types;
-
- const char* url;
- proto::ElementType element_type;
- bool expect_match;
- } kTestCases[] = {
- {"ex.com", kAll, "http://ex.com/img.jpg", kImage, true},
- {"ex.com", kAll & ~kPopup, "http://ex.com/img", kPopup, false},
-
- {"ex.com", kImage, "http://ex.com/img.jpg", kImage, true},
- {"ex.com", kAll & ~kImage, "http://ex.com/img.jpg", kImage, false},
- {"ex.com", kScript, "http://ex.com/img.jpg", kImage, false},
- {"ex.com", kAll & ~kScript, "http://ex.com/img.jpg", kImage, true},
-
- {"ex.com", kImage | kFont, "http://ex.com/font", kFont, true},
- {"ex.com", kImage | kFont, "http://ex.com/image", kImage, true},
- {"ex.com", kImage | kFont, "http://ex.com/video",
- proto::ELEMENT_TYPE_MEDIA, false},
- {"ex.com", kAll & ~kFont & ~kScript, "http://ex.com/font", kFont, false},
- {"ex.com", kAll & ~kFont & ~kScript, "http://ex.com/scr", kScript, false},
- {"ex.com", kAll & ~kFont & ~kScript, "http://ex.com/img", kImage, true},
-
- {"ex.com", kAll, "http://ex.com", proto::ELEMENT_TYPE_OTHER, true},
- {"ex.com", kAll, "http://ex.com", proto::ELEMENT_TYPE_UNSPECIFIED, false},
- {"ex.com", kWebSocket, "ws://ex.com", proto::ELEMENT_TYPE_WEBSOCKET,
- true},
- };
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(
- ::testing::Message()
- << "UrlPattern: " << test_case.url_pattern
- << "; ElementTypes: " << static_cast<int>(test_case.element_types)
- << "; URL: " << test_case.url
- << "; ElementType: " << static_cast<int>(test_case.element_type));
-
- auto rule = MakeUrlRule(UrlPattern(test_case.url_pattern, kSubstring));
- rule.set_element_types(test_case.element_types);
- ASSERT_TRUE(AddUrlRule(rule));
- Finish();
-
- EXPECT_EQ(test_case.expect_match,
- !!FindMatch(test_case.url, nullptr /* document_origin_string */,
- test_case.element_type));
- Reset();
- }
-}
-
-TEST_F(UrlPatternIndexTest, OneRuleWithActivationTypes) {
- const struct {
- const char* url_pattern;
- int32_t activation_types;
-
- const char* document_url;
- proto::ActivationType activation_type;
- bool expect_match;
- } kTestCases[] = {
- {"example.com", kDocument, "http://example.com", kDocument, true},
- {"xample.com", kDocument, "http://example.com", kDocument, true},
- {"exampl.com", kDocument, "http://example.com", kDocument, false},
-
- {"example.com", kGenericBlock, "http://example.com", kDocument, false},
- {"example.com", kDocument, "http://example.com", kNoActivation, false},
- {"example.com", kGenericBlock, "http://example.com", kNoActivation,
- false},
-
- // Invalid GURL.
- {"example.com", kDocument, "http;//example.com", kDocument, false},
- };
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(
- ::testing::Message()
- << "UrlPattern: " << test_case.url_pattern
- << "; ActivationTypes: " << static_cast<int>(test_case.activation_types)
- << "; DocumentURL: " << test_case.document_url
- << "; ActivationType: " << static_cast<int>(test_case.activation_type));
-
- auto rule = MakeUrlRule(UrlPattern(test_case.url_pattern, kSubstring));
- rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
- rule.clear_element_types();
- rule.set_activation_types(test_case.activation_types);
- ASSERT_TRUE(AddUrlRule(rule));
- Finish();
-
- EXPECT_EQ(test_case.expect_match,
- !!FindMatch(test_case.document_url,
- nullptr /* parent_document_origin */, kNoElement,
- test_case.activation_type));
- EXPECT_EQ(test_case.expect_match,
- !!FindMatch(test_case.document_url, "http://example.com/",
- kNoElement, test_case.activation_type));
- EXPECT_EQ(test_case.expect_match,
- !!FindMatch(test_case.document_url, "http://xmpl.com/",
- kNoElement, test_case.activation_type));
- Reset();
- }
-}
-
-TEST_F(UrlPatternIndexTest, OneRuleWithElementAndActivationTypes) {
- auto rule = MakeUrlRule(UrlPattern("allow.ex.com", kSubstring));
- rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
- rule.set_element_types(kSubdocument);
- rule.set_activation_types(kDocument);
- ASSERT_TRUE(AddUrlRule(rule));
- Finish();
-
- EXPECT_FALSE(FindMatch("http://allow.ex.com"));
- EXPECT_TRUE(FindMatch("http://allow.ex.com",
- nullptr /*document_origin_string */, kSubdocument));
-
- EXPECT_FALSE(FindMatch("http://allow.ex.com",
- nullptr /* document_origin_string */, kNoElement,
- kGenericBlock));
- EXPECT_TRUE(FindMatch("http://allow.ex.com",
- nullptr /* document_origin_string */, kNoElement,
- kDocument));
-}
-
-TEST_F(UrlPatternIndexTest, MatchWithDisableGenericRules) {
- const struct {
- const char* url_pattern;
- std::vector<std::string> domains;
- } kRules[] = {
- // Generic rules.
- {"some_text", std::vector<std::string>()},
- {"another_text", {"~example.com"}},
- {"final_text", {"~example1.com", "~example2.com"}},
- // Domain specific rules.
- {"some_text", {"example1.com"}},
- {"more_text", {"example.com", "~exclude.example.com"}},
- {"last_text", {"example1.com", "sub.example2.com"}},
- };
-
- for (const auto& rule_data : kRules) {
- auto rule = MakeUrlRule(UrlPattern(rule_data.url_pattern, kSubstring));
- AddDomains(rule_data.domains, &rule);
- ASSERT_TRUE(AddUrlRule(rule))
- << "UrlPattern: " << rule_data.url_pattern
- << "; Domains: " << ::testing::PrintToString(rule_data.domains);
- }
-
- // Note: Some of the rules have common domains (e.g., example1.com), which are
- // ultimately shared by FlatBuffers' CreateSharedString. The test also makes
- // sure that the data structure works properly with such optimization.
- Finish();
-
- const struct {
- const char* url;
- const char* document_origin;
- bool expect_match_with_enable_all_rules;
- bool expect_match_with_disable_generic_rules;
- } kTestCases[] = {
- {"http://ex.com/some_text", "http://example.com", true, false},
- {"http://ex.com/some_text", "http://example1.com", true, true},
-
- {"http://ex.com/another_text", "http://example.com", false, false},
- {"http://ex.com/another_text", "http://example1.com", true, false},
-
- {"http://ex.com/final_text", "http://example.com", true, false},
- {"http://ex.com/final_text", "http://example1.com", false, false},
- {"http://ex.com/final_text", "http://example2.com", false, false},
-
- {"http://ex.com/more_text", "http://example.com", true, true},
- {"http://ex.com/more_text", "http://exclude.example.com", false, false},
- {"http://ex.com/more_text", "http://example1.com", false, false},
-
- {"http://ex.com/last_text", "http://example.com", false, false},
- {"http://ex.com/last_text", "http://example1.com", true, true},
- {"http://ex.com/last_text", "http://example2.com", false, false},
- {"http://ex.com/last_text", "http://sub.example2.com", true, true},
- };
-
- constexpr bool kDisableGenericRules = true;
- constexpr bool kEnableAllRules = false;
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(::testing::Message()
- << "UrlPattern: " << test_case.url
- << "; DocumentOrigin: " << test_case.document_origin);
-
- EXPECT_EQ(test_case.expect_match_with_disable_generic_rules,
- !!FindMatch(test_case.url, test_case.document_origin, kOther,
- kNoActivation, kDisableGenericRules));
- EXPECT_EQ(test_case.expect_match_with_enable_all_rules,
- !!FindMatch(test_case.url, test_case.document_origin, kOther,
- kNoActivation, kEnableAllRules));
- }
-}
-
-TEST_F(UrlPatternIndexTest, RulesWithUnsupportedTypes) {
- const struct {
- int element_types;
- int activation_types;
- } kRules[] = {
- {proto::ELEMENT_TYPE_MAX << 1, 0},
- {0, proto::ACTIVATION_TYPE_MAX << 1},
- {proto::ELEMENT_TYPE_MAX << 1, proto::ACTIVATION_TYPE_MAX << 1},
-
- {kPopup, 0},
- {0, proto::ACTIVATION_TYPE_ELEMHIDE},
- {0, proto::ACTIVATION_TYPE_GENERICHIDE},
- {0, proto::ACTIVATION_TYPE_ELEMHIDE | proto::ACTIVATION_TYPE_GENERICHIDE},
- {proto::ELEMENT_TYPE_POPUP, proto::ACTIVATION_TYPE_ELEMHIDE},
- };
-
- for (const auto& rule_data : kRules) {
- auto rule = MakeUrlRule(UrlPattern("example.com", kSubstring));
- rule.set_element_types(rule_data.element_types);
- rule.set_activation_types(rule_data.activation_types);
- EXPECT_FALSE(AddUrlRule(rule))
- << "ElementTypes: " << static_cast<int>(rule_data.element_types)
- << "; ActivationTypes: "
- << static_cast<int>(rule_data.activation_types);
- }
- ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("exmpl.com", kSubstring))));
- Finish();
-
- EXPECT_FALSE(FindMatch("http://example.com/"));
- EXPECT_TRUE(FindMatch("https://exmpl.com/"));
-}
-
-TEST_F(UrlPatternIndexTest, RulesWithSupportedAndUnsupportedTypes) {
- const struct {
- int element_types;
- int activation_types;
- } kRules[] = {
- {kImage | (proto::ELEMENT_TYPE_MAX << 1), 0},
- {kScript | kPopup, 0},
- {0, kDocument | (proto::ACTIVATION_TYPE_MAX << 1)},
- };
-
- for (const auto& rule_data : kRules) {
- auto rule = MakeUrlRule(UrlPattern("example.com", kSubstring));
- rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
- rule.set_element_types(rule_data.element_types);
- rule.set_activation_types(rule_data.activation_types);
- EXPECT_TRUE(AddUrlRule(rule))
- << "ElementTypes: " << static_cast<int>(rule_data.element_types)
- << "; ActivationTypes: "
- << static_cast<int>(rule_data.activation_types);
- }
- Finish();
-
- EXPECT_TRUE(FindMatch("http://example.com/", nullptr, kImage));
- EXPECT_TRUE(FindMatch("http://example.com/", nullptr, kScript));
- EXPECT_FALSE(FindMatch("http://example.com/", nullptr, kPopup));
- EXPECT_FALSE(FindMatch("http://example.com/"));
-
- EXPECT_TRUE(FindMatch("http://example.com", nullptr, kNoElement, kDocument));
- EXPECT_FALSE(
- FindMatch("http://example.com", nullptr, kNoElement, kGenericBlock));
-}
-
-TEST_F(UrlPatternIndexTest, FindMatchReturnsCorrectRules) {
- constexpr size_t kNumOfPatterns = 1024;
-
- std::vector<std::string> url_patterns(kNumOfPatterns);
- for (size_t i = 0; i < kNumOfPatterns; ++i) {
- url_patterns[i] = "http://example." + std::to_string(i) + ".com";
- ASSERT_TRUE(
- AddUrlRule(MakeUrlRule(UrlPattern(url_patterns[i], kSubstring))))
- << "Rule #" << i;
- }
- Finish();
-
- std::reverse(url_patterns.begin() + kNumOfPatterns / 2, url_patterns.end());
- for (const std::string& url_pattern : url_patterns) {
- SCOPED_TRACE(::testing::Message() << "UrlPattern: " << url_pattern);
-
- const flat::UrlRule* rule = FindMatch(url_pattern);
- ASSERT_TRUE(rule);
- ASSERT_FALSE(IsOutOfRange(rule));
-
- const flatbuffers::String* rule_pattern = rule->url_pattern();
- ASSERT_TRUE(rule_pattern);
- EXPECT_EQ(url_pattern,
- base::StringPiece(rule_pattern->data(), rule_pattern->size()));
- }
-
- EXPECT_FALSE(
- FindMatch("http://example." + std::to_string(kNumOfPatterns) + ".com"));
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/url_pattern_unittest.cc b/chromium/components/subresource_filter/core/common/url_pattern_unittest.cc
deleted file mode 100644
index 91ba4fa7d6f..00000000000
--- a/chromium/components/subresource_filter/core/common/url_pattern_unittest.cc
+++ /dev/null
@@ -1,124 +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/core/common/url_pattern.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace subresource_filter {
-
-namespace {
-
-constexpr proto::AnchorType kAnchorNone = proto::ANCHOR_TYPE_NONE;
-constexpr proto::AnchorType kBoundary = proto::ANCHOR_TYPE_BOUNDARY;
-constexpr proto::AnchorType kSubdomain = proto::ANCHOR_TYPE_SUBDOMAIN;
-
-} // namespace
-
-TEST(SubresourceFilterUrlPatternTest, MatchesUrl) {
- const struct {
- UrlPattern url_pattern;
- const char* url;
- bool expect_match;
- } kTestCases[] = {
- {{"", proto::URL_PATTERN_TYPE_SUBSTRING}, "http://ex.com/", true},
- {{"", proto::URL_PATTERN_TYPE_WILDCARDED}, "http://ex.com/", true},
- {{"", kBoundary, kAnchorNone}, "http://ex.com/", true},
- {{"", kSubdomain, kAnchorNone}, "http://ex.com/", true},
- {{"", kSubdomain, kAnchorNone}, "http://ex.com/", true},
- {{"^", kSubdomain, kAnchorNone}, "http://ex.com/", false},
- {{".", kSubdomain, kAnchorNone}, "http://ex.com/", false},
- {{"", kAnchorNone, kBoundary}, "http://ex.com/", true},
- {{"^", kAnchorNone, kBoundary}, "http://ex.com/", true},
- {{".", kAnchorNone, kBoundary}, "http://ex.com/", false},
- {{"", kBoundary, kBoundary}, "http://ex.com/", false},
- {{"", kSubdomain, kBoundary}, "http://ex.com/", false},
- {{"com/", kSubdomain, kBoundary}, "http://ex.com/", true},
-
- {{"xampl", proto::URL_PATTERN_TYPE_SUBSTRING},
- "http://example.com",
- true},
- {{"example", proto::URL_PATTERN_TYPE_SUBSTRING},
- "http://example.com",
- true},
- {{"/a?a"}, "http://ex.com/a?a", true},
- {{"^abc"}, "http://ex.com/abc?a", true},
- {{"^abc"}, "http://ex.com/a?abc", true},
- {{"^abc"}, "http://ex.com/abc?abc", true},
- {{"^abc^abc"}, "http://ex.com/abc?abc", true},
- {{"^com^abc^abc"}, "http://ex.com/abc?abc", false},
-
- {{"http://ex", kBoundary, kAnchorNone}, "http://example.com", true},
- {{"http://ex", kAnchorNone, kAnchorNone}, "http://example.com", true},
- {{"mple.com/", kAnchorNone, kBoundary}, "http://example.com", true},
- {{"mple.com/", kAnchorNone, kAnchorNone}, "http://example.com", true},
- {{"mple.com/", kSubdomain, kAnchorNone}, "http://example.com", false},
- {{"ex.com", kSubdomain, kAnchorNone}, "http://hex.com", false},
- {{"ex.com", kSubdomain, kAnchorNone}, "http://ex.com", true},
- {{"ex.com", kSubdomain, kAnchorNone}, "http://hex.ex.com", true},
- {{"ex.com", kSubdomain, kAnchorNone}, "http://hex.hex.com", false},
-
- // Note: "example.com" will be normalized into "example.com/".
- {{"http://*mpl", kBoundary, kAnchorNone}, "http://example.com", true},
- {{"mpl*com/", kAnchorNone, kBoundary}, "http://example.com", true},
- {{"example^com"}, "http://example.com", false},
- {{"example^com"}, "http://example/com", true},
- {{"example.com^"}, "http://example.com:8080", true},
- {{"http*.com/", kBoundary, kBoundary}, "http://example.com", true},
- {{"http*.org/", kBoundary, kBoundary}, "http://example.com", false},
-
- {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path/bbb?k=v&p1=0&p2=1", false},
- {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path?k=v&p1=0&p2=1", true},
- {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path?k=v&k=v&p1=0&p2=1", true},
- {{"/path?*&p1=*&p2="},
- "http://ex.com/aaa/path?k=v&p1=0&p3=10&p2=1",
- true},
- {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path&p1=0&p2=1", false},
- {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path?k=v&p2=0&p1=1", false},
-
- {{"abc*def*ghijk*xyz"},
- "http://example.com/abcdeffffghijkmmmxyzzz",
- true},
- {{"abc*cdef"}, "http://example.com/abcdef", false},
-
- {{"^^a^^"}, "http://ex.com/?a=/", true},
- {{"^^a^^"}, "http://ex.com/?a=/&b=0", true},
- {{"^^a^^"}, "http://ex.com/?a=", false},
-
- {{"ex.com^path^*k=v^"}, "http://ex.com/path/?k1=v1&ak=v&kk=vv", true},
- {{"ex.com^path^*k=v^"}, "http://ex.com/p/path/?k1=v1&ak=v&kk=vv", false},
- {{"a^a&a^a&"}, "http://ex.com/a/a/a/a/?a&a&a&a&a", true},
-
- {{"abc*def^"}, "http://ex.com/abc/a/ddef/", true},
-
- {{"https://example.com/"}, "http://example.com/", false},
- {{"example.com/", kSubdomain, kAnchorNone}, "http://example.com/", true},
- {{"examp", kSubdomain, kAnchorNone}, "http://example.com/", true},
- {{"xamp", kSubdomain, kAnchorNone}, "http://example.com/", false},
- {{"examp", kSubdomain, kAnchorNone}, "http://test.example.com/", true},
- {{"t.examp", kSubdomain, kAnchorNone}, "http://test.example.com/", false},
- {{"com^", kSubdomain, kAnchorNone}, "http://test.example.com/", true},
- {{"x.com", kSubdomain, kAnchorNone}, "http://ex.com/?url=x.com", false},
- {{"ex.com/", kSubdomain, kBoundary}, "http://ex.com/", true},
- {{"ex.com^", kSubdomain, kBoundary}, "http://ex.com/", true},
- {{"ex.co", kSubdomain, kBoundary}, "http://ex.com/", false},
- {{"ex.com", kSubdomain, kBoundary}, "http://rex.com.ex.com/", false},
- {{"ex.com/", kSubdomain, kBoundary}, "http://rex.com.ex.com/", true},
- {{"http", kSubdomain, kBoundary}, "http://http.com/", false},
- {{"http", kSubdomain, kAnchorNone}, "http://http.com/", true},
- {{"/example.com", kSubdomain, kBoundary}, "http://example.com/", false},
- {{"/example.com/", kSubdomain, kBoundary}, "http://example.com/", false},
- };
-
- for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(testing::Message() << "Rule: " << test_case.url_pattern
- << "; URL: " << GURL(test_case.url));
-
- const bool is_match = test_case.url_pattern.MatchesUrl(GURL(test_case.url));
- EXPECT_EQ(test_case.expect_match, is_match);
- }
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/url_rule_test_support.cc b/chromium/components/subresource_filter/core/common/url_rule_test_support.cc
deleted file mode 100644
index ab6f7f1bfe7..00000000000
--- a/chromium/components/subresource_filter/core/common/url_rule_test_support.cc
+++ /dev/null
@@ -1,56 +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/subresource_filter/core/common/url_rule_test_support.h"
-
-#include "base/logging.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace subresource_filter {
-namespace testing {
-
-proto::UrlRule MakeUrlRule(const UrlPattern& url_pattern) {
- proto::UrlRule rule;
-
- rule.set_semantics(proto::RULE_SEMANTICS_BLACKLIST);
- rule.set_source_type(proto::SOURCE_TYPE_ANY);
- rule.set_element_types(kAllElementTypes);
-
- rule.set_url_pattern_type(url_pattern.type());
- rule.set_anchor_left(url_pattern.anchor_left());
- rule.set_anchor_right(url_pattern.anchor_right());
- rule.set_match_case(url_pattern.match_case());
- rule.set_url_pattern(url_pattern.url_pattern().as_string());
-
- return rule;
-}
-
-void AddDomains(const std::vector<std::string>& domains, proto::UrlRule* rule) {
- for (std::string domain_pattern : domains) {
- DCHECK(!domain_pattern.empty());
- auto* domain = rule->add_domains();
- if (domain_pattern[0] == '~') {
- domain_pattern.erase(0, 1);
- domain->set_exclude(true);
- }
- domain->set_domain(std::move(domain_pattern));
- }
-}
-
-url::Origin GetOrigin(base::StringPiece origin_string) {
- return !origin_string.empty() ? url::Origin(GURL(origin_string))
- : url::Origin();
-}
-
-bool IsThirdParty(const GURL& url, const url::Origin& first_party_origin) {
- return first_party_origin.unique() ||
- !net::registry_controlled_domains::SameDomainOrHost(
- url, first_party_origin,
- net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-}
-
-} // namespace testing
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/url_rule_test_support.h b/chromium/components/subresource_filter/core/common/url_rule_test_support.h
deleted file mode 100644
index fef73b9456e..00000000000
--- a/chromium/components/subresource_filter/core/common/url_rule_test_support.h
+++ /dev/null
@@ -1,74 +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_SUBRESOURCE_FILTER_CORE_COMMON_URL_RULE_TEST_SUPPORT_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_URL_RULE_TEST_SUPPORT_H_
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string_piece.h"
-#include "components/subresource_filter/core/common/proto/rules.pb.h"
-#include "components/subresource_filter/core/common/url_pattern.h"
-
-class GURL;
-
-namespace url {
-class Origin;
-}
-
-namespace subresource_filter {
-namespace testing {
-
-// Constants -------------------------------------------------------------------
-
-constexpr proto::UrlPatternType kSubstring = proto::URL_PATTERN_TYPE_SUBSTRING;
-
-constexpr proto::AnchorType kAnchorNone = proto::ANCHOR_TYPE_NONE;
-constexpr proto::AnchorType kBoundary = proto::ANCHOR_TYPE_BOUNDARY;
-constexpr proto::AnchorType kSubdomain = proto::ANCHOR_TYPE_SUBDOMAIN;
-
-constexpr proto::ElementType kNoElement = proto::ELEMENT_TYPE_UNSPECIFIED;
-constexpr proto::ElementType kOther = proto::ELEMENT_TYPE_OTHER;
-constexpr proto::ElementType kScript = proto::ELEMENT_TYPE_SCRIPT;
-constexpr proto::ElementType kImage = proto::ELEMENT_TYPE_IMAGE;
-constexpr proto::ElementType kSubdocument = proto::ELEMENT_TYPE_SUBDOCUMENT;
-constexpr proto::ElementType kFont = proto::ELEMENT_TYPE_FONT;
-constexpr proto::ElementType kPopup = proto::ELEMENT_TYPE_POPUP;
-constexpr proto::ElementType kWebSocket = proto::ELEMENT_TYPE_WEBSOCKET;
-constexpr proto::ElementType kAllElementTypes = proto::ELEMENT_TYPE_ALL;
-
-constexpr proto::ActivationType kNoActivation =
- proto::ACTIVATION_TYPE_UNSPECIFIED;
-constexpr proto::ActivationType kDocument = proto::ACTIVATION_TYPE_DOCUMENT;
-constexpr proto::ActivationType kGenericBlock =
- proto::ACTIVATION_TYPE_GENERICBLOCK;
-
-constexpr proto::SourceType kAnyParty = proto::SOURCE_TYPE_ANY;
-constexpr proto::SourceType kThirdParty = proto::SOURCE_TYPE_THIRD_PARTY;
-constexpr proto::SourceType kFirstParty = proto::SOURCE_TYPE_FIRST_PARTY;
-
-// Helpers ---------------------------------------------------------------------
-
-// Creates a UrlRule with the given |url_pattern|, and all necessary fields
-// initialized to defaults.
-proto::UrlRule MakeUrlRule(const UrlPattern& url_pattern = UrlPattern());
-
-// Parses |domains| and adds them to the domain list of the |rule|.
-//
-// The |domains| vector should contain non-empty strings. If a string starts
-// with '~' then the following part of the string is an exception domain.
-void AddDomains(const std::vector<std::string>& domains, proto::UrlRule* rule);
-
-// Returns the url::Origin parsed from |origin_string|, or the unique origin if
-// the string is empty.
-url::Origin GetOrigin(base::StringPiece origin_string);
-
-// Returns whether |url| is third-party resource w.r.t. |first_party_origin|.
-bool IsThirdParty(const GURL& url, const url::Origin& first_party_origin);
-
-} // namespace testing
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_URL_RULE_TEST_SUPPORT_H_
diff --git a/chromium/components/suggestions/BUILD.gn b/chromium/components/suggestions/BUILD.gn
index 6d93d045bc4..fa4acf4a65f 100644
--- a/chromium/components/suggestions/BUILD.gn
+++ b/chromium/components/suggestions/BUILD.gn
@@ -18,6 +18,8 @@ static_library("suggestions") {
"suggestions_service_impl.h",
"suggestions_store.cc",
"suggestions_store.h",
+ "webui/suggestions_source.cc",
+ "webui/suggestions_source.h",
]
public_deps = [
@@ -25,6 +27,7 @@ static_library("suggestions") {
"//components/prefs",
"//components/suggestions/proto",
"//net",
+ "//ui/base",
"//ui/gfx",
"//url",
]
diff --git a/chromium/components/suggestions/image_encoder.cc b/chromium/components/suggestions/image_encoder.cc
index 8b0fa09fc26..414766446c4 100644
--- a/chromium/components/suggestions/image_encoder.cc
+++ b/chromium/components/suggestions/image_encoder.cc
@@ -20,10 +20,8 @@ bool EncodeSkBitmapToJPEG(const SkBitmap& bitmap,
if (!bitmap.readyToDraw() || bitmap.isNull()) {
return false;
}
- return gfx::JPEGCodec::Encode(
- reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
- gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(),
- bitmap.rowBytes(), 100, dest);
+
+ return gfx::JPEGCodec::Encode(bitmap, 100, dest);
}
} // namespace suggestions
diff --git a/chromium/components/suggestions/image_manager.cc b/chromium/components/suggestions/image_manager.cc
index d2fb227f9ac..321aef28ac1 100644
--- a/chromium/components/suggestions/image_manager.cc
+++ b/chromium/components/suggestions/image_manager.cc
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/suggestions/image_encoder.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
@@ -82,11 +83,11 @@ ImageManager::ImageManager() : weak_ptr_factory_(this) {}
ImageManager::ImageManager(
std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
std::unique_ptr<ProtoDatabase<ImageData>> database,
- const base::FilePath& database_dir,
- scoped_refptr<base::TaskRunner> background_task_runner)
+ const base::FilePath& database_dir)
: image_fetcher_(std::move(image_fetcher)),
database_(std::move(database)),
- background_task_runner_(background_task_runner),
+ background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::TaskPriority::USER_VISIBLE})),
database_ready_(false),
weak_ptr_factory_(this) {
image_fetcher_->SetImageFetcherDelegate(this);
@@ -216,6 +217,8 @@ void ImageManager::ServeFromCacheOrNetwork(
void ImageManager::SaveImage(const std::string& url, const SkBitmap& bitmap) {
scoped_refptr<base::RefCountedBytes> encoded_data(
new base::RefCountedBytes());
+ // TODO(treib): Should encoding happen on the |background_task_runner_|?
+ // *De*coding happens there.
if (!EncodeSkBitmapToJPEG(bitmap, &encoded_data->data())) {
return;
}
diff --git a/chromium/components/suggestions/image_manager.h b/chromium/components/suggestions/image_manager.h
index 20de2eea4d3..04ffc002b77 100644
--- a/chromium/components/suggestions/image_manager.h
+++ b/chromium/components/suggestions/image_manager.h
@@ -47,8 +47,7 @@ class ImageManager : public image_fetcher::ImageFetcherDelegate {
ImageManager(
std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
std::unique_ptr<leveldb_proto::ProtoDatabase<ImageData>> database,
- const base::FilePath& database_dir,
- scoped_refptr<base::TaskRunner> background_task_runner);
+ const base::FilePath& database_dir);
~ImageManager() override;
virtual void Initialize(const SuggestionsProfile& suggestions);
diff --git a/chromium/components/suggestions/image_manager_unittest.cc b/chromium/components/suggestions/image_manager_unittest.cc
index 86717dd169b..5c42a290d47 100644
--- a/chromium/components/suggestions/image_manager_unittest.cc
+++ b/chromium/components/suggestions/image_manager_unittest.cc
@@ -11,7 +11,6 @@
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/image_fetcher/core/image_fetcher_delegate.h"
#include "components/leveldb_proto/proto_database.h"
@@ -23,7 +22,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
using ::testing::Return;
@@ -129,8 +127,7 @@ class ImageManagerTest : public testing::Test {
EXPECT_CALL(*mock_image_fetcher_, SetImageFetcherDelegate(_));
return new ImageManager(base::WrapUnique(mock_image_fetcher_),
base::WrapUnique(fake_db),
- FakeDB<ImageData>::DirectoryForTestDB(),
- base::ThreadTaskRunnerHandle::Get());
+ FakeDB<ImageData>::DirectoryForTestDB());
}
EntryMap db_model_;
diff --git a/chromium/components/suggestions/proto/suggestions.proto b/chromium/components/suggestions/proto/suggestions.proto
index 27f5d141c33..5fcf03ae8ac 100644
--- a/chromium/components/suggestions/proto/suggestions.proto
+++ b/chromium/components/suggestions/proto/suggestions.proto
@@ -23,15 +23,19 @@ enum ProviderId {
//
// Notice that the tags on this proto must match the ones on the server side.
//
-// Next tag: 2
+// Next tag: 17
message SuggestionsProfile {
repeated ChromeSuggestion suggestions = 1;
+ reserved 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15;
+
// Timestamp when the profile was generated (usec).
optional int64 timestamp = 16;
}
-// The suggestions for this user, ordered from best to worst.
+// An individual suggestion.
+//
+// Notice that the tags on this proto must match the ones on the server side.
//
// Next tag: 15
message ChromeSuggestion {
@@ -50,8 +54,12 @@ message ChromeSuggestion {
// The provider(s) responsible for this suggestion.
repeated ProviderId providers = 5;
+ reserved 6;
+
// The timestamp (usec) at which this suggestion ceases to be valid.
optional int64 expiry_ts = 7;
+
+ reserved 8, 9, 10, 11, 12, 13, 14;
}
// A list of URLs that should be filtered from the SuggestionsProfile.
diff --git a/chromium/components/suggestions/suggestions_service_impl.cc b/chromium/components/suggestions/suggestions_service_impl.cc
index aacd400d7b5..e1af0660e44 100644
--- a/chromium/components/suggestions/suggestions_service_impl.cc
+++ b/chromium/components/suggestions/suggestions_service_impl.cc
@@ -92,7 +92,7 @@ const char kSuggestionsBlacklistClearURLFormat[] =
const char kSuggestionsBlacklistURLParam[] = "url";
const char kSuggestionsDeviceParam[] = "t=%s";
-const char kSuggestionsMinParam[] = "min=%i";
+const char kSuggestionsMinParam[] = "num=%i";
const char kSuggestionsMinVariationName[] = "min_suggestions";
const int kSuggestionsMinVariationDefault = 0;
diff --git a/chromium/components/suggestions/suggestions_service_impl_unittest.cc b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
index 7aa45beb51e..90fc4f5959c 100644
--- a/chromium/components/suggestions/suggestions_service_impl_unittest.cc
+++ b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
@@ -343,7 +343,7 @@ TEST_F(SuggestionsServiceTest, BuildUrlWithDefaultMinZeroParamForFewFeature) {
ASSERT_TRUE(GetCurrentlyQueriedUrl().is_valid());
EXPECT_EQ(GetCurrentlyQueriedUrl().path(), kSuggestionsUrlPath);
std::string min_suggestions;
- EXPECT_TRUE(net::GetValueForKeyInQuery(GetCurrentlyQueriedUrl(), "min",
+ EXPECT_TRUE(net::GetValueForKeyInQuery(GetCurrentlyQueriedUrl(), "num",
&min_suggestions));
EXPECT_EQ(min_suggestions, "0");
RespondToFetchWithProfile(CreateSuggestionsProfile());
diff --git a/chromium/components/suggestions/webui/suggestions_source.cc b/chromium/components/suggestions/webui/suggestions_source.cc
new file mode 100644
index 00000000000..ab9b927cd5e
--- /dev/null
+++ b/chromium/components/suggestions/webui/suggestions_source.cc
@@ -0,0 +1,197 @@
+// 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/suggestions/webui/suggestions_source.h"
+
+#include "base/barrier_closure.h"
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/suggestions/proto/suggestions.pb.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/time_format.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace suggestions {
+
+namespace {
+
+const char kHtmlHeader[] =
+ "<!DOCTYPE html>\n<html>\n<head>\n<title>Suggestions</title>\n"
+ "<meta charset=\"utf-8\">\n"
+ "<style type=\"text/css\">\nli {white-space: nowrap;}\n</style>\n";
+const char kHtmlBody[] = "</head>\n<body>\n";
+const char kHtmlFooter[] = "</body>\n</html>\n";
+
+const char kRefreshPath[] = "refresh";
+
+std::string GetRefreshHtml(const std::string& base_url, bool is_refresh) {
+ if (is_refresh)
+ return "<p>Refreshing in the background, reload to see new data.</p>\n";
+ return std::string("<p><a href=\"") + base_url + kRefreshPath +
+ "\">Refresh</a></p>\n";
+}
+// Returns the HTML needed to display the suggestions.
+std::string RenderOutputHtml(
+ const std::string& base_url,
+ bool is_refresh,
+ const SuggestionsProfile& profile,
+ const std::map<GURL, std::string>& base64_encoded_pngs) {
+ std::vector<std::string> out;
+ out.push_back(kHtmlHeader);
+ out.push_back(kHtmlBody);
+ out.push_back("<h1>Suggestions</h1>\n");
+ out.push_back(GetRefreshHtml(base_url, is_refresh));
+ out.push_back("<ul>");
+ int64_t now = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch())
+ .ToInternalValue();
+ size_t size = profile.suggestions_size();
+ for (size_t i = 0; i < size; ++i) {
+ const ChromeSuggestion& suggestion = profile.suggestions(i);
+ base::TimeDelta remaining_time =
+ base::TimeDelta::FromMicroseconds(suggestion.expiry_ts() - now);
+ base::string16 remaining_time_formatted = ui::TimeFormat::Detailed(
+ ui::TimeFormat::Format::FORMAT_DURATION,
+ ui::TimeFormat::Length::LENGTH_LONG, -1, remaining_time);
+ std::string line;
+ line += "<li><a href=\"";
+ line += net::EscapeForHTML(suggestion.url());
+ line += "\" target=\"_blank\">";
+ line += net::EscapeForHTML(suggestion.title());
+ std::map<GURL, std::string>::const_iterator it =
+ base64_encoded_pngs.find(GURL(suggestion.url()));
+ if (it != base64_encoded_pngs.end()) {
+ line += "<br><img src='";
+ line += it->second;
+ line += "'>";
+ }
+ line += "</a> Expires in ";
+ line += base::UTF16ToUTF8(remaining_time_formatted);
+ std::vector<std::string> providers;
+ for (int p = 0; p < suggestion.providers_size(); ++p)
+ providers.push_back(base::IntToString(suggestion.providers(p)));
+ line += ". Provider IDs: " + base::JoinString(providers, ", ");
+ line += "</li>\n";
+ out.push_back(line);
+ }
+ out.push_back("</ul>");
+ out.push_back(kHtmlFooter);
+ return base::JoinString(out, base::StringPiece());
+}
+
+// Returns the HTML needed to display that no suggestions are available.
+std::string RenderOutputHtmlNoSuggestions(const std::string& base_url,
+ bool is_refresh) {
+ std::vector<std::string> out;
+ out.push_back(kHtmlHeader);
+ out.push_back(kHtmlBody);
+ out.push_back("<h1>Suggestions</h1>\n");
+ out.push_back("<p>You have no suggestions.</p>\n");
+ out.push_back(GetRefreshHtml(base_url, is_refresh));
+ out.push_back(kHtmlFooter);
+ return base::JoinString(out, base::StringPiece());
+}
+
+} // namespace
+
+SuggestionsSource::SuggestionsSource(SuggestionsService* suggestions_service,
+ const std::string& base_url)
+ : suggestions_service_(suggestions_service),
+ base_url_(base_url),
+ weak_ptr_factory_(this) {}
+
+SuggestionsSource::~SuggestionsSource() {}
+
+SuggestionsSource::RequestContext::RequestContext(
+ bool is_refresh_in,
+ const SuggestionsProfile& suggestions_profile_in,
+ const GotDataCallback& callback_in)
+ : is_refresh(is_refresh_in),
+ suggestions_profile(suggestions_profile_in), // Copy.
+ callback(callback_in) // Copy.
+{}
+
+SuggestionsSource::RequestContext::~RequestContext() {}
+
+void SuggestionsSource::StartDataRequest(const std::string& path,
+ const GotDataCallback& callback) {
+ // If this was called as "chrome://suggestions/refresh", we also trigger an
+ // async update of the suggestions.
+ bool is_refresh = (path == kRefreshPath);
+
+ // |suggestions_service| is null for guest profiles.
+ if (!suggestions_service_) {
+ std::string output = RenderOutputHtmlNoSuggestions(base_url_, is_refresh);
+ callback.Run(base::RefCountedString::TakeString(&output));
+ return;
+ }
+
+ if (is_refresh)
+ suggestions_service_->FetchSuggestionsData();
+
+ SuggestionsProfile suggestions_profile =
+ suggestions_service_->GetSuggestionsDataFromCache().value_or(
+ SuggestionsProfile());
+ size_t size = suggestions_profile.suggestions_size();
+ if (!size) {
+ std::string output = RenderOutputHtmlNoSuggestions(base_url_, is_refresh);
+ callback.Run(base::RefCountedString::TakeString(&output));
+ } else {
+ RequestContext* context =
+ new RequestContext(is_refresh, suggestions_profile, callback);
+ base::Closure barrier = BarrierClosure(
+ size, base::BindOnce(&SuggestionsSource::OnThumbnailsFetched,
+ weak_ptr_factory_.GetWeakPtr(), context));
+ for (size_t i = 0; i < size; ++i) {
+ const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
+ // Fetch the thumbnail for this URL (exercising the fetcher). After all
+ // fetches are done, including NULL callbacks for unavailable thumbnails,
+ // SuggestionsSource::OnThumbnailsFetched will be called.
+ suggestions_service_->GetPageThumbnail(
+ GURL(suggestion.url()),
+ base::Bind(&SuggestionsSource::OnThumbnailAvailable,
+ weak_ptr_factory_.GetWeakPtr(), context, barrier));
+ }
+ }
+}
+
+std::string SuggestionsSource::GetMimeType(const std::string& path) const {
+ return "text/html";
+}
+
+void SuggestionsSource::OnThumbnailsFetched(RequestContext* context) {
+ std::unique_ptr<RequestContext> context_deleter(context);
+
+ std::string output = RenderOutputHtml(base_url_, context->is_refresh,
+ context->suggestions_profile,
+ context->base64_encoded_pngs);
+ context->callback.Run(base::RefCountedString::TakeString(&output));
+}
+
+void SuggestionsSource::OnThumbnailAvailable(RequestContext* context,
+ const base::Closure& barrier,
+ const GURL& url,
+ const gfx::Image& image) {
+ if (!image.IsEmpty()) {
+ std::vector<unsigned char> output;
+ gfx::PNGCodec::EncodeBGRASkBitmap(*image.ToSkBitmap(), false, &output);
+
+ std::string encoded_output;
+ base::Base64Encode(
+ base::StringPiece(reinterpret_cast<const char*>(output.data()),
+ output.size()),
+ &encoded_output);
+ context->base64_encoded_pngs[url] = "data:image/png;base64,";
+ context->base64_encoded_pngs[url] += encoded_output;
+ }
+ barrier.Run();
+}
+
+} // namespace suggestions
diff --git a/chromium/components/suggestions/webui/suggestions_source.h b/chromium/components/suggestions/webui/suggestions_source.h
new file mode 100644
index 00000000000..c5c2b4f9c38
--- /dev/null
+++ b/chromium/components/suggestions/webui/suggestions_source.h
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SUGGESTIONS_WEBUI_SUGGESTIONS_SOURCE_H_
+#define COMPONENTS_SUGGESTIONS_WEBUI_SUGGESTIONS_SOURCE_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "components/suggestions/suggestions_service.h"
+#include "ui/gfx/image/image.h"
+#include "url/gurl.h"
+
+namespace suggestions {
+
+// SuggestionsSource renders a webpage to list SuggestionsService data.
+class SuggestionsSource {
+ public:
+ SuggestionsSource(SuggestionsService* suggestions_service,
+ const std::string& base_url);
+ ~SuggestionsSource();
+
+ using GotDataCallback =
+ base::Callback<void(scoped_refptr<base::RefCountedMemory>)>;
+
+ void StartDataRequest(const std::string& path,
+ const GotDataCallback& callback);
+ std::string GetMimeType(const std::string& path) const;
+
+ private:
+ // Container for the state of a request.
+ struct RequestContext {
+ RequestContext(
+ bool is_refresh_in,
+ const suggestions::SuggestionsProfile& suggestions_profile_in,
+ const GotDataCallback& callback_in);
+ ~RequestContext();
+
+ const bool is_refresh;
+ const suggestions::SuggestionsProfile suggestions_profile;
+ const GotDataCallback callback;
+ std::map<GURL, std::string> base64_encoded_pngs;
+ };
+
+ // Callback for responses from each Thumbnail request.
+ void OnThumbnailAvailable(RequestContext* context,
+ const base::Closure& barrier,
+ const GURL& url,
+ const gfx::Image& image);
+
+ // Callback for when all requests are complete. Renders the output webpage and
+ // passes the result to the original caller.
+ void OnThumbnailsFetched(RequestContext* context);
+
+ // Only used when servicing requests on the UI thread.
+ SuggestionsService* suggestions_service_;
+
+ // The base URL at which which the Suggestions WebUI lives in the context of
+ // the embedder.
+ const std::string base_url_;
+
+ // For callbacks may be run after destruction.
+ base::WeakPtrFactory<SuggestionsSource> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuggestionsSource);
+};
+
+} // namespace suggestions
+
+#endif // COMPONENTS_SUGGESTIONS_WEBUI_SUGGESTIONS_SOURCE_H_
diff --git a/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html b/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
index 2a7de4b5467..c04f9249f6f 100644
--- a/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
+++ b/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
@@ -1,10 +1,10 @@
<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="initial-scale=1, minimum-scale=1, width=device-width">
-<title i18n-content="blockPageTitle"></title>
+<title>$i18n{blockPageTitle}</title>
<link rel="stylesheet" href="supervised_user_block_interstitial.css">
<script src="../../../ui/webui/resources/js/cr.js"></script>
<script src="../../../ui/webui/resources/js/util.js"></script>
@@ -15,11 +15,10 @@
<div id="information-container">
<div class="icon" id="icon"></div>
<div id="main-message">
- <h1 id="block-page-header" i18n-content="blockPageHeader"></h1>
- <p id="block-page-message" i18n-content="blockPageMessage"></p>
- <h1 id="request-failed-message" i18n-content="requestFailedMessage"
- hidden></h1>
- <h1 id="request-sent-message" i18n-content="requestSentMessage" hidden></h1>
+ <h1 id="block-page-header">$i18n{blockPageHeader}</h1>
+ <p id="block-page-message">$i18n{blockPageMessage}</p>
+ <h1 id="request-failed-message" hidden>$i18n{requestFailedMessage}</h1>
+ <h1 id="request-sent-message" hidden>$i18n{requestSentMessage}</h1>
</div>
<div id="custodians-information" hidden>
<div id="custodian-information" class="custodian-information">
@@ -39,18 +38,25 @@
</div>
</div>
<div class="button-container">
- <button id="request-access-button" class="primary-button" i18n-content="requestAccessButton">
+ <button id="request-access-button" class="primary-button">
+ $i18n{requestAccessButton}
</button>
<div id="details-button-container">
- <button id="show-details-link" class="details-button small-link" i18n-content="showDetailsLink" hidden></button>
- <button id="hide-details-link" class="details-button small-link" i18n-content="hideDetailsLink" hidden></button>
- <button id="back-button" class="details-button small-link" i18n-content="backButton" hidden></button>
+ <button id="show-details-link" class="details-button small-link">
+ $i18n{showDetailsLink}
+ </button>
+ <button id="hide-details-link" class="details-button small-link" hidden>
+ $i18n{hideDetailsLink}
+ </button>
</div>
+ <button id="back-button" class="details-button small-link" hidden>
+ $i18n{backButton}
+ </button>
</div>
<div id="details" hidden>
- <p id="details-header" i18n-content="blockReasonHeader"></p>
- <p id="details-message" i18n-content="blockReasonMessage"></p>
- <p id="feedback" i18n-values=".innerHTML:feedbackLink"></p>
+ <p id="details-header">$i18n{blockReasonHeader}</p>
+ <p id="details-message">$i18n{blockReasonMessage}</p>
+ <p id="feedback">$i18nRaw{feedbackLink}</p>
</div>
</div>
</body>
diff --git a/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js b/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
index 6b9abf41343..d6b93275843 100644
--- a/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
+++ b/chromium/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
@@ -37,6 +37,9 @@ function setupMobileNav() {
document.addEventListener('DOMContentLoaded', setupMobileNav);
function sendCommand(cmd) {
+ // TODO(bauerb): domAutomationController is not defined when this page is
+ // shown in chrome://interstitials. Use a MessageHandler or something to
+ // support interactions.
window.domAutomationController.setAutomationId(1);
window.domAutomationController.send(cmd);
}
@@ -46,14 +49,11 @@ function makeImageSet(url1x, url2x) {
}
function initialize() {
- if (loadTimeData.getBoolean('allowAccessRequests')) {
+ var allowAccessRequests = loadTimeData.getBoolean('allowAccessRequests');
+ if (allowAccessRequests) {
$('request-access-button').onclick = function(event) {
$('request-access-button').hidden = true;
- if (window.domAutomationController) {
- sendCommand('request');
- } else {
- window.webRestrictions.requestPermission(setRequestStatus);
- }
+ sendCommand('request');
};
} else {
$('request-access-button').hidden = true;
@@ -61,7 +61,7 @@ function initialize() {
var avatarURL1x = loadTimeData.getString('avatarURL1x');
var avatarURL2x = loadTimeData.getString('avatarURL2x');
var custodianName = loadTimeData.getString('custodianName');
- if (custodianName) {
+ if (custodianName && allowAccessRequests) {
$('custodians-information').hidden = false;
if (avatarURL1x) {
$('custodian-avatar-img').style.content =
@@ -84,31 +84,28 @@ function initialize() {
'secondCustodianEmail');
}
}
- var showDetailsLink = loadTimeData.getString('showDetailsLink');
- $('show-details-link').hidden = !showDetailsLink;
- $('back-button').hidden = showDetailsLink || !window.domAutomationController;
$('back-button').onclick = function(event) {
sendCommand('back');
};
- $('show-details-link').onclick = function(event) {
- showDetails = true;
- $('show-details-link').hidden = true;
- $('hide-details-link').hidden = false;
- updateDetails();
- };
- $('hide-details-link').onclick = function(event) {
- showDetails = false;
- $('show-details-link').hidden = false;
- $('hide-details-link').hidden = true;
- updateDetails();
- };
- if (window.domAutomationController &&
- loadTimeData.getBoolean('showFeedbackLink')) {
+ if (loadTimeData.getBoolean('showFeedbackLink')) {
+ $('show-details-link').onclick = function(event) {
+ showDetails = true;
+ $('show-details-link').hidden = true;
+ $('hide-details-link').hidden = false;
+ updateDetails();
+ };
+ $('hide-details-link').onclick = function(event) {
+ showDetails = false;
+ $('show-details-link').hidden = false;
+ $('hide-details-link').hidden = true;
+ updateDetails();
+ };
$('feedback-link').onclick = function(event) {
sendCommand('feedback');
};
} else {
$('feedback').hidden = true;
+ $('details-button-container').hidden = true;
}
}
@@ -127,7 +124,7 @@ function setRequestStatus(isSuccessful) {
if (isSuccessful) {
$('request-failed-message').hidden = true;
$('request-sent-message').hidden = false;
- $('back-button').hidden = !window.domAutomationController;
+ $('back-button').hidden = false;
$('request-access-button').hidden = true;
$('show-details-link').hidden = true;
} else {
diff --git a/chromium/components/supervised_user_error_page/supervised_user_error_page.cc b/chromium/components/supervised_user_error_page/supervised_user_error_page.cc
index e098c06251f..6763db5a055 100644
--- a/chromium/components/supervised_user_error_page/supervised_user_error_page.cc
+++ b/chromium/components/supervised_user_error_page/supervised_user_error_page.cc
@@ -74,6 +74,7 @@ std::string BuildHtml(bool allow_access_requests,
const std::string& second_custodian,
const std::string& second_custodian_email,
bool is_child_account,
+ bool is_deprecated,
FilteringBehaviorReason reason,
const std::string& app_locale) {
base::DictionaryValue strings;
@@ -113,7 +114,12 @@ std::string BuildHtml(bool allow_access_requests,
} else {
block_header = l10n_util::GetStringUTF16(
IDS_BLOCK_INTERSTITIAL_HEADER_ACCESS_REQUESTS_DISABLED);
- // If access requests are disabled, there is no block message.
+
+ if (is_deprecated) {
+ DCHECK(!is_child_account);
+ block_message = l10n_util::GetStringUTF16(
+ IDS_BLOCK_INTERSTITIAL_MESSAGE_SUPERVISED_USERS_DEPRECATED);
+ }
}
strings.SetString("blockPageHeader", block_header);
strings.SetString("blockPageMessage", block_message);
diff --git a/chromium/components/supervised_user_error_page/supervised_user_error_page.h b/chromium/components/supervised_user_error_page/supervised_user_error_page.h
index 66456f2b2fc..0950ada4265 100644
--- a/chromium/components/supervised_user_error_page/supervised_user_error_page.h
+++ b/chromium/components/supervised_user_error_page/supervised_user_error_page.h
@@ -33,6 +33,7 @@ std::string BuildHtml(bool allow_access_requests,
const std::string& second_custodian,
const std::string& second_custodian_email,
bool is_child_account,
+ bool is_deprecated,
FilteringBehaviorReason reason,
const std::string& app_locale);
diff --git a/chromium/components/supervised_user_error_page/supervised_user_error_page_android.cc b/chromium/components/supervised_user_error_page/supervised_user_error_page_android.cc
index b430dd58701..ee7e3c16752 100644
--- a/chromium/components/supervised_user_error_page/supervised_user_error_page_android.cc
+++ b/chromium/components/supervised_user_error_page/supervised_user_error_page_android.cc
@@ -29,6 +29,7 @@ std::string BuildHtmlFromWebRestrictionsResult(
result->stringParams["Second custodian"],
result->stringParams["Second custodian email"],
result->intParams["Is child account"],
+ /* is_deprecated = */ false,
static_cast<FilteringBehaviorReason>(result->intParams["Reason"]),
app_locale);
}
diff --git a/chromium/components/supervised_user_error_page/supervised_user_error_page_unittest.cc b/chromium/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
index 7565ff4b03d..873c8508103 100644
--- a/chromium/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
+++ b/chromium/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
@@ -62,6 +62,7 @@ struct BuildHtmlTestParameter {
const std::string& second_custodian;
const std::string& second_custodian_email;
bool is_child_account;
+ bool is_deprecated;
FilteringBehaviorReason reason;
bool has_two_parents;
};
@@ -75,15 +76,11 @@ TEST_P(SupervisedUserErrorPageTest_BuildHtml, BuildHtml) {
param.allow_access_requests, param.profile_image_url,
param.profile_image_url2, param.custodian, param.custodian_email,
param.second_custodian, param.second_custodian_email,
- param.is_child_account, param.reason, "");
- // The result should contain the original HTML plus scripts that plug values
- // into it. The test can't easily check that the scripts are correct, but
- // can check that the output contains the expected values.
- std::string html =
- ResourceBundle::GetSharedInstance()
- .GetRawDataResource(IDR_SUPERVISED_USER_BLOCK_INTERSTITIAL_HTML)
- .as_string();
- EXPECT_THAT(result, testing::HasSubstr(html));
+ param.is_child_account, param.is_deprecated, param.reason, "");
+ // The result should contain the original HTML (with $i18n{} replacements)
+ // plus scripts that plug values into it. The test can't easily check that the
+ // scripts are correct, but can check that the output contains the expected
+ // values.
EXPECT_THAT(result, testing::HasSubstr(param.profile_image_url));
EXPECT_THAT(result, testing::HasSubstr(param.profile_image_url2));
EXPECT_THAT(result, testing::HasSubstr(param.custodian));
@@ -190,16 +187,18 @@ TEST_P(SupervisedUserErrorPageTest_BuildHtml, BuildHtml) {
}
BuildHtmlTestParameter build_html_test_parameter[] = {
- {true, "url1", "url2", "custodian", "custodian_email", "", "", true,
+ {true, "url1", "url2", "custodian", "custodian_email", "", "", true, false,
DEFAULT, false},
{true, "url1", "url2", "custodian", "custodian_email", "custodian2",
- "custodian2_email", true, DEFAULT, true},
+ "custodian2_email", true, false, DEFAULT, true},
{false, "url1", "url2", "custodian", "custodian_email", "custodian2",
- "custodian2_email", true, DEFAULT, true},
+ "custodian2_email", true, false, DEFAULT, true},
+ {false, "url1", "url2", "custodian", "custodian_email", "custodian2",
+ "custodian2_email", false, true, DEFAULT, true},
{true, "url1", "url2", "custodian", "custodian_email", "custodian2",
- "custodian2_email", false, DEFAULT, true},
+ "custodian2_email", false, false, DEFAULT, true},
{true, "url1", "url2", "custodian", "custodian_email", "custodian2",
- "custodian2_email", true, ASYNC_CHECKER, true},
+ "custodian2_email", true, false, ASYNC_CHECKER, true},
};
INSTANTIATE_TEST_CASE_P(GetBlockMessageIDParameterized,
diff --git a/chromium/components/supervised_user_error_page_strings.grdp b/chromium/components/supervised_user_error_page_strings.grdp
index 264f4d673e2..5798f26c596 100644
--- a/chromium/components/supervised_user_error_page_strings.grdp
+++ b/chromium/components/supervised_user_error_page_strings.grdp
@@ -11,13 +11,16 @@
Ask your parent
</message>
<message name="IDS_BLOCK_INTERSTITIAL_HEADER_ACCESS_REQUESTS_DISABLED" desc="A heading for the supervised user when they attempt to visit a site that is not permitted by the manager (and they can't ask for permission).">
- Looks like you don't have permission to visit this site
+ You don't have permission to visit this site
</message>
<message name="IDS_BLOCK_INTERSTITIAL_HEADER_NOT_SIGNED_IN" desc="A heading for the supervised user when the status cannot be established because they haven't signed in to Chrome).">
Please start and sign in to Chrome before using this app.
</message>
<message name="IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE" desc="A message for the child user when they attempt to visit a site that is not permitted by their parent.">
- Looks like you need permission to visit this site
+ You need permission to visit this site
+ </message>
+ <message name="IDS_BLOCK_INTERSTITIAL_MESSAGE_SUPERVISED_USERS_DEPRECATED" desc="Message to be shown to a supervised user directing them to the supervisor.">
+ Questions? Contact the person who supervises your profile.
</message>
<message name="IDS_BACK_BUTTON" desc="A button for going back to the last safe url after being blocked.">
Go back
diff --git a/chromium/components/sync/BUILD.gn b/chromium/components/sync/BUILD.gn
index 8fa00f334f7..d3402fa132c 100644
--- a/chromium/components/sync/BUILD.gn
+++ b/chromium/components/sync/BUILD.gn
@@ -182,8 +182,6 @@ static_library("sync") {
"engine/attachments/in_memory_attachment_store.h",
"engine/attachments/on_disk_attachment_store.cc",
"engine/attachments/on_disk_attachment_store.h",
- "engine/browser_thread_model_worker.cc",
- "engine/browser_thread_model_worker.h",
"engine/commit_queue.cc",
"engine/commit_queue.h",
"engine/configure_reason.h",
@@ -235,6 +233,8 @@ static_library("sync") {
"engine/passive_model_worker.h",
"engine/polling_constants.cc",
"engine/polling_constants.h",
+ "engine/sequenced_model_worker.cc",
+ "engine/sequenced_model_worker.h",
"engine/shutdown_reason.h",
"engine/sync_auth_provider.h",
"engine/sync_backend_registrar.cc",
@@ -572,8 +572,14 @@ static_library("sync") {
"syncable/write_transaction.h",
"syncable/write_transaction_info.cc",
"syncable/write_transaction_info.h",
+ "user_events/fake_user_event_service.cc",
+ "user_events/fake_user_event_service.h",
+ "user_events/no_op_user_event_service.cc",
+ "user_events/no_op_user_event_service.h",
"user_events/user_event_service.cc",
"user_events/user_event_service.h",
+ "user_events/user_event_service_impl.cc",
+ "user_events/user_event_service_impl.h",
"user_events/user_event_sync_bridge.cc",
"user_events/user_event_sync_bridge.h",
]
@@ -596,7 +602,7 @@ static_library("sync") {
"//components/os_crypt",
"//components/pref_registry",
"//components/prefs",
- "//components/reading_list/core:reading_list_enable_flags",
+ "//components/reading_list/features:reading_list_enable_flags",
"//components/signin/core/browser",
"//components/sync/engine_impl/attachments/proto",
"//components/version_info",
@@ -879,10 +885,10 @@ source_set("unit_tests") {
"engine/attachments/fake_attachment_uploader_unittest.cc",
"engine/attachments/in_memory_attachment_store_unittest.cc",
"engine/attachments/on_disk_attachment_store_unittest.cc",
- "engine/browser_thread_model_worker_unittest.cc",
"engine/cycle/sync_cycle_snapshot_unittest.cc",
"engine/model_safe_worker_unittest.cc",
"engine/net/http_bridge_unittest.cc",
+ "engine/sequenced_model_worker_unittest.cc",
"engine/sync_backend_registrar_unittest.cc",
"engine/ui_model_worker_unittest.cc",
"engine_impl/apply_control_data_updates_unittest.cc",
@@ -945,7 +951,7 @@ source_set("unit_tests") {
"syncable/syncable_enum_conversions_unittest.cc",
"syncable/syncable_id_unittest.cc",
"syncable/syncable_unittest.cc",
- "user_events/user_event_service_unittest.cc",
+ "user_events/user_event_service_impl_unittest.cc",
"user_events/user_event_sync_bridge_unittest.cc",
]
@@ -976,6 +982,7 @@ source_set("unit_tests") {
"//components/sync_preferences",
"//components/sync_preferences:test_support",
"//components/version_info",
+ "//components/version_info:version_string",
"//google_apis",
"//google_apis:test_support",
"//net",
@@ -1017,30 +1024,20 @@ source_set("unit_tests") {
static_library("test_support_fake_server") {
testonly = true
sources = [
- "test/fake_server/bookmark_entity.cc",
- "test/fake_server/bookmark_entity.h",
"test/fake_server/bookmark_entity_builder.cc",
"test/fake_server/bookmark_entity_builder.h",
"test/fake_server/entity_builder_factory.cc",
"test/fake_server/entity_builder_factory.h",
"test/fake_server/fake_server.cc",
"test/fake_server/fake_server.h",
- "test/fake_server/fake_server_entity.cc",
- "test/fake_server/fake_server_entity.h",
"test/fake_server/fake_server_http_post_provider.cc",
"test/fake_server/fake_server_http_post_provider.h",
"test/fake_server/fake_server_network_resources.cc",
"test/fake_server/fake_server_network_resources.h",
"test/fake_server/fake_server_verifier.cc",
"test/fake_server/fake_server_verifier.h",
- "test/fake_server/permanent_entity.cc",
- "test/fake_server/permanent_entity.h",
"test/fake_server/sessions_hierarchy.cc",
"test/fake_server/sessions_hierarchy.h",
- "test/fake_server/tombstone_entity.cc",
- "test/fake_server/tombstone_entity.h",
- "test/fake_server/unique_client_entity.cc",
- "test/fake_server/unique_client_entity.h",
]
deps = [
diff --git a/chromium/components/sync_bookmarks/bookmark_change_processor.cc b/chromium/components/sync_bookmarks/bookmark_change_processor.cc
index 96d50b12215..63930d1d690 100644
--- a/chromium/components/sync_bookmarks/bookmark_change_processor.cc
+++ b/chromium/components/sync_bookmarks/bookmark_change_processor.cc
@@ -968,7 +968,9 @@ void BookmarkChangeProcessor::SetSyncNodeFavicon(
sync_node->GetBookmarkSpecifics());
updated_specifics.set_favicon(favicon_bytes->front(),
favicon_bytes->size());
- updated_specifics.set_icon_url(bookmark_node->icon_url().spec());
+ updated_specifics.set_icon_url(bookmark_node->icon_url()
+ ? bookmark_node->icon_url()->spec()
+ : std::string());
sync_node->SetBookmarkSpecifics(updated_specifics);
}
}
diff --git a/chromium/components/sync_preferences/BUILD.gn b/chromium/components/sync_preferences/BUILD.gn
index e8786777f6c..a3ae9d2535d 100644
--- a/chromium/components/sync_preferences/BUILD.gn
+++ b/chromium/components/sync_preferences/BUILD.gn
@@ -25,6 +25,7 @@ static_library("sync_preferences") {
"//components/prefs",
"//components/sync",
"//services/preferences/public/cpp",
+ "//services/preferences/public/cpp:service_main",
]
if (!is_ios) {
diff --git a/chromium/components/sync_preferences/pref_model_associator.cc b/chromium/components/sync_preferences/pref_model_associator.cc
index 49bd331ffe6..3175208aa1b 100644
--- a/chromium/components/sync_preferences/pref_model_associator.cc
+++ b/chromium/components/sync_preferences/pref_model_associator.cc
@@ -63,12 +63,12 @@ PrefModelAssociator::PrefModelAssociator(
pref_service_(NULL),
type_(type),
client_(client) {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(type_ == PREFERENCES || type_ == PRIORITY_PREFERENCES);
}
PrefModelAssociator::~PrefModelAssociator() {
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pref_service_ = NULL;
synced_pref_observers_.clear();
@@ -166,7 +166,7 @@ syncer::SyncMergeResult PrefModelAssociator::MergeDataAndStartSyncing(
std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
DCHECK_EQ(type_, type);
- DCHECK(CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(pref_service_);
DCHECK(!sync_processor_.get());
DCHECK(sync_processor.get());
diff --git a/chromium/components/sync_preferences/pref_model_associator.h b/chromium/components/sync_preferences/pref_model_associator.h
index 2afbef011e2..257f0f53758 100644
--- a/chromium/components/sync_preferences/pref_model_associator.h
+++ b/chromium/components/sync_preferences/pref_model_associator.h
@@ -16,7 +16,7 @@
#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "base/observer_list.h"
-#include "base/threading/non_thread_safe.h"
+#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"
@@ -37,8 +37,7 @@ class PrefServiceSyncable;
// Contains all preference sync related logic.
// TODO(sync): Merge this into PrefService once we separate the profile
// PrefService from the local state PrefService.
-class PrefModelAssociator : public syncer::SyncableService,
- public base::NonThreadSafe {
+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
@@ -201,6 +200,8 @@ class PrefModelAssociator : public syncer::SyncableService,
std::vector<base::Closure> callback_list_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(PrefModelAssociator);
};
diff --git a/chromium/components/sync_preferences/pref_service_syncable.cc b/chromium/components/sync_preferences/pref_service_syncable.cc
index ac32f54d0f7..e86cb800c4f 100644
--- a/chromium/components/sync_preferences/pref_service_syncable.cc
+++ b/chromium/components/sync_preferences/pref_service_syncable.cc
@@ -14,9 +14,13 @@
#include "components/prefs/overlay_user_pref_store.h"
#include "components/prefs/pref_notifier_impl.h"
#include "components/prefs/pref_registry.h"
-#include "components/prefs/pref_value_store.h"
#include "components/sync_preferences/pref_model_associator.h"
#include "components/sync_preferences/pref_service_syncable_observer.h"
+#include "services/preferences/public/cpp/in_process_service_factory.h"
+#include "services/preferences/public/cpp/persistent_pref_store_client.h"
+#include "services/preferences/public/cpp/pref_registry_serializer.h"
+#include "services/preferences/public/interfaces/preferences.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
namespace sync_preferences {
@@ -70,28 +74,36 @@ PrefServiceSyncable::~PrefServiceSyncable() {
PrefServiceSyncable* PrefServiceSyncable::CreateIncognitoPrefService(
PrefStore* incognito_extension_pref_store,
- const std::vector<const char*>& overlay_pref_names) {
+ const std::vector<const char*>& overlay_pref_names,
+ std::unique_ptr<PrefValueStore::Delegate> delegate) {
pref_service_forked_ = true;
PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
- OverlayUserPrefStore* incognito_pref_store =
- new OverlayUserPrefStore(user_pref_store_.get());
- for (const char* overlay_pref_name : overlay_pref_names)
- incognito_pref_store->RegisterOverlayPref(overlay_pref_name);
scoped_refptr<user_prefs::PrefRegistrySyncable> forked_registry =
static_cast<user_prefs::PrefRegistrySyncable*>(pref_registry_.get())
->ForkForIncognito();
+
+ if (delegate) {
+ delegate->InitIncognitoUnderlay(user_pref_store_.get());
+ delegate->InitPrefRegistry(forked_registry.get());
+ }
+ auto incognito_pref_store =
+ base::MakeRefCounted<OverlayUserPrefStore>(user_pref_store_.get());
+
+ for (const char* overlay_pref_name : overlay_pref_names)
+ incognito_pref_store->RegisterOverlayPref(overlay_pref_name);
+
PrefServiceSyncable* incognito_service = new PrefServiceSyncable(
pref_notifier,
pref_value_store_->CloneAndSpecialize(NULL, // managed
NULL, // supervised_user
incognito_extension_pref_store,
NULL, // command_line_prefs
- incognito_pref_store,
+ incognito_pref_store.get(),
NULL, // recommended
forked_registry->defaults().get(),
- pref_notifier),
- incognito_pref_store, forked_registry.get(),
+ pref_notifier, std::move(delegate)),
+ incognito_pref_store.get(), forked_registry.get(),
pref_sync_associator_.client(), read_error_callback_, false);
return incognito_service;
}
diff --git a/chromium/components/sync_preferences/pref_service_syncable.h b/chromium/components/sync_preferences/pref_service_syncable.h
index 7fc44d04a90..21f58b9fd39 100644
--- a/chromium/components/sync_preferences/pref_service_syncable.h
+++ b/chromium/components/sync_preferences/pref_service_syncable.h
@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_value_store.h"
#include "components/sync_preferences/pref_model_associator.h"
#include "components/sync_preferences/synced_pref_observer.h"
@@ -51,7 +52,8 @@ class PrefServiceSyncable : public PrefService {
// whose changes will not be persisted by the returned incognito pref service.
PrefServiceSyncable* CreateIncognitoPrefService(
PrefStore* incognito_extension_pref_store,
- const std::vector<const char*>& overlay_pref_names);
+ const std::vector<const char*>& overlay_pref_names,
+ std::unique_ptr<PrefValueStore::Delegate> delegate);
// Returns true if preferences state has synchronized with the remote
// preferences. If true is returned it can be assumed the local preferences
diff --git a/chromium/components/sync_preferences/pref_service_syncable_factory.cc b/chromium/components/sync_preferences/pref_service_syncable_factory.cc
index 66d98feebfd..516415a0c91 100644
--- a/chromium/components/sync_preferences/pref_service_syncable_factory.cc
+++ b/chromium/components/sync_preferences/pref_service_syncable_factory.cc
@@ -11,9 +11,6 @@
#include "components/prefs/pref_notifier_impl.h"
#include "components/prefs/pref_value_store.h"
#include "components/sync_preferences/pref_service_syncable.h"
-#include "services/preferences/public/cpp/pref_store_adapter.h"
-#include "services/preferences/public/interfaces/preferences.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
#if !defined(OS_IOS)
#include "components/policy/core/browser/browser_policy_connector.h"
@@ -55,54 +52,19 @@ void PrefServiceSyncableFactory::SetPrefModelAssociatorClient(
pref_model_associator_client_ = pref_model_associator_client;
}
-namespace {
-
-// Expose the |backing_pref_store| through the prefs service.
-scoped_refptr<::PrefStore> CreateRegisteredPrefStore(
- prefs::mojom::PrefStoreRegistry* registry,
- scoped_refptr<::PrefStore> backing_pref_store,
- PrefValueStore::PrefStoreType type) {
- // If we're testing or if the prefs service feature flag is off we don't
- // register.
- if (!registry || !backing_pref_store)
- return backing_pref_store;
-
- return make_scoped_refptr(new prefs::PrefStoreAdapter(
- backing_pref_store,
- prefs::PrefStoreImpl::Create(registry, backing_pref_store, type)));
-}
-
-} // namespace
-
std::unique_ptr<PrefServiceSyncable> PrefServiceSyncableFactory::CreateSyncable(
user_prefs::PrefRegistrySyncable* pref_registry,
- service_manager::Connector* connector) {
+ std::unique_ptr<PrefValueStore::Delegate> delegate) {
TRACE_EVENT0("browser", "PrefServiceSyncableFactory::CreateSyncable");
PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
- prefs::mojom::PrefStoreRegistryPtr registry;
- if (connector)
- connector->BindInterface(prefs::mojom::kServiceName, &registry);
-
- // Expose all read-only stores through the prefs service.
- auto managed = CreateRegisteredPrefStore(registry.get(), managed_prefs_,
- PrefValueStore::MANAGED_STORE);
- auto supervised =
- CreateRegisteredPrefStore(registry.get(), supervised_user_prefs_,
- PrefValueStore::SUPERVISED_USER_STORE);
- auto extension = CreateRegisteredPrefStore(registry.get(), extension_prefs_,
- PrefValueStore::EXTENSION_STORE);
- auto command_line = CreateRegisteredPrefStore(
- registry.get(), command_line_prefs_, PrefValueStore::COMMAND_LINE_STORE);
- auto recommended = CreateRegisteredPrefStore(
- registry.get(), recommended_prefs_, PrefValueStore::RECOMMENDED_STORE);
-
std::unique_ptr<PrefServiceSyncable> pref_service(new PrefServiceSyncable(
pref_notifier,
- new PrefValueStore(managed.get(), supervised.get(), extension.get(),
- command_line.get(), user_prefs_.get(),
- recommended.get(), pref_registry->defaults().get(),
- pref_notifier),
+ new PrefValueStore(managed_prefs_.get(), supervised_user_prefs_.get(),
+ extension_prefs_.get(), command_line_prefs_.get(),
+ user_prefs_.get(), recommended_prefs_.get(),
+ pref_registry->defaults().get(), pref_notifier,
+ std::move(delegate)),
user_prefs_.get(), pref_registry, pref_model_associator_client_,
read_error_callback_, async_));
return pref_service;
diff --git a/chromium/components/sync_preferences/pref_service_syncable_factory.h b/chromium/components/sync_preferences/pref_service_syncable_factory.h
index 3b9bb394421..b6726b377b7 100644
--- a/chromium/components/sync_preferences/pref_service_syncable_factory.h
+++ b/chromium/components/sync_preferences/pref_service_syncable_factory.h
@@ -9,16 +9,13 @@
#include "base/macros.h"
#include "components/prefs/pref_service_factory.h"
+#include "components/prefs/pref_value_store.h"
namespace policy {
class BrowserPolicyConnector;
class PolicyService;
}
-namespace service_manager {
-class Connector;
-}
-
namespace user_prefs {
class PrefRegistrySyncable;
}
@@ -46,11 +43,11 @@ class PrefServiceSyncableFactory : public PrefServiceFactory {
void SetPrefModelAssociatorClient(
PrefModelAssociatorClient* pref_model_associator_client);
- // |connector| might be null during test or if we're not using the Mojo pref
- // |service.
+ // |delegate| might be null during test or if we're not using the Mojo pref
+ // service.
std::unique_ptr<PrefServiceSyncable> CreateSyncable(
user_prefs::PrefRegistrySyncable* registry,
- service_manager::Connector* connector = nullptr);
+ std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr);
private:
PrefModelAssociatorClient* pref_model_associator_client_;
diff --git a/chromium/components/sync_sessions/favicon_cache.cc b/chromium/components/sync_sessions/favicon_cache.cc
index dff0ae06cca..e6eddc55551 100644
--- a/chromium/components/sync_sessions/favicon_cache.cc
+++ b/chromium/components/sync_sessions/favicon_cache.cc
@@ -9,6 +9,7 @@
#include "base/location.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/favicon/core/favicon_service.h"
#include "components/history/core/browser/history_service.h"
@@ -961,7 +962,11 @@ void FaviconCache::DeleteSyncedFavicon(
void FaviconCache::DropSyncedFavicon(FaviconMap::iterator favicon_iter) {
DVLOG(1) << "Dropping favicon " << favicon_iter->second.get()->favicon_url;
+ const GURL& url = favicon_iter->first;
recent_favicons_.erase(favicon_iter->second);
+ base::EraseIf(page_favicon_map_, [url](const PageFaviconMap::value_type& kv) {
+ return kv.second == url;
+ });
synced_favicons_.erase(favicon_iter);
}
diff --git a/chromium/components/sync_sessions/revisit/page_revisit_broadcaster.cc b/chromium/components/sync_sessions/revisit/page_revisit_broadcaster.cc
index cfeca0b13b6..f887f26f123 100644
--- a/chromium/components/sync_sessions/revisit/page_revisit_broadcaster.cc
+++ b/chromium/components/sync_sessions/revisit/page_revisit_broadcaster.cc
@@ -28,7 +28,7 @@ class SessionsSyncManagerWrapper : public ForeignSessionsProvider {
public:
explicit SessionsSyncManagerWrapper(SessionsSyncManager* manager)
: manager_(manager) {}
- ~SessionsSyncManagerWrapper() override{};
+ ~SessionsSyncManagerWrapper() override {}
bool GetAllForeignSessions(
std::vector<const SyncedSession*>* sessions) override {
return manager_->GetAllForeignSessions(sessions);
diff --git a/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer_unittest.cc b/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer_unittest.cc
index b86549eba22..f69c731c664 100644
--- a/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer_unittest.cc
+++ b/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer_unittest.cc
@@ -31,7 +31,7 @@ class TestForeignSessionsProvider : public ForeignSessionsProvider {
TestForeignSessionsProvider(const std::vector<const SyncedSession*>& sessions,
bool return_value)
: sessions_(sessions), return_value_(return_value) {}
- ~TestForeignSessionsProvider() override{};
+ ~TestForeignSessionsProvider() override {}
bool GetAllForeignSessions(
std::vector<const SyncedSession*>* sessions) override {
diff --git a/chromium/components/sync_sessions/sessions_sync_manager.cc b/chromium/components/sync_sessions/sessions_sync_manager.cc
index 47351d79953..5810f8fc937 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager.cc
+++ b/chromium/components/sync_sessions/sessions_sync_manager.cc
@@ -153,7 +153,7 @@ SessionsSyncManager::SessionsSyncManager(
current_device_type_(sync_pb::SyncEnums_DeviceType_TYPE_OTHER),
local_session_header_node_id_(TabNodePool::kInvalidTabNodeID),
stale_session_threshold_days_(kDefaultStaleSessionThresholdDays),
- local_event_router_(std::move(router)),
+ local_event_router_(router),
page_revisit_broadcaster_(this, sessions_client),
sessions_updated_callback_(sessions_updated_callback),
datatype_refresh_callback_(datatype_refresh_callback),
@@ -429,6 +429,9 @@ void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab_delegate,
return;
}
+ // Ensure the task tracker has up to date task ids for this tab.
+ UpdateTaskTracker(tab_delegate);
+
if (!tab_delegate->ShouldSync(sessions_client_))
return;
@@ -466,9 +469,7 @@ void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab_delegate,
sync_pb::EntitySpecifics specifics;
specifics.mutable_session()->CopyFrom(
SessionTabToSpecifics(*session_tab, current_machine_tag(), tab_node_id));
- // Intercept the sync model here to update task tracker and fill navigations
- // with their ancestor navigations.
- TrackTasks(tab_delegate, specifics.mutable_session());
+ WriteTasksIntoSpecifics(specifics.mutable_session()->mutable_tab());
syncer::SyncData data = syncer::SyncData::CreateLocalData(
TabNodeIdToTag(current_machine_tag(), tab_node_id), current_session_name_,
specifics);
@@ -488,25 +489,29 @@ void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab_delegate,
}
}
-void SessionsSyncManager::TrackTasks(
- SyncedTabDelegate* const tab_delegate,
- sync_pb::SessionSpecifics* session_specifics) {
- sync_pb::SessionTab* tab_specifics = session_specifics->mutable_tab();
- // Index in the whole navigations of the tab.
- int current_navigation_index = tab_delegate->GetCurrentEntryIndex();
- // Index in the tab_specifics, where the navigations is a -6/+6 window
- int current_index_in_tab_specifics =
- tab_specifics->current_navigation_index();
- int64_t current_navigation_global_id =
- tab_specifics->navigation(current_index_in_tab_specifics).global_id();
+void SessionsSyncManager::UpdateTaskTracker(
+ SyncedTabDelegate* const tab_delegate) {
+ TabTasks* tab_tasks = task_tracker_->GetTabTasks(
+ tab_delegate->GetSessionId(), tab_delegate->GetSourceTabID());
- TabTasks* tab_tasks =
- task_tracker_->GetTabTasks(tab_delegate->GetSessionId());
- tab_tasks->UpdateWithNavigation(
- current_navigation_index,
- tab_delegate->GetTransitionAtIndex(current_navigation_index),
- current_navigation_global_id);
+ // Iterate through all navigations in the tab to ensure they all have a task
+ // id set (it's possible some haven't been seen before, such as when a tab
+ // is restored).
+ for (int i = 0; i < tab_delegate->GetEntryCount(); ++i) {
+ sessions::SerializedNavigationEntry serialized_entry;
+ tab_delegate->GetSerializedNavigationAtIndex(i, &serialized_entry);
+ int nav_id = serialized_entry.unique_id();
+ int64_t global_id = serialized_entry.timestamp().ToInternalValue();
+ tab_tasks->UpdateWithNavigation(
+ nav_id, tab_delegate->GetTransitionAtIndex(i), global_id);
+ }
+}
+
+void SessionsSyncManager::WriteTasksIntoSpecifics(
+ sync_pb::SessionTab* tab_specifics) {
+ TabTasks* tab_tasks =
+ task_tracker_->GetTabTasks(tab_specifics->tab_id(), kInvalidTabID);
for (int i = 0; i < tab_specifics->navigation_size(); i++) {
// Excluding blocked navigations, which are appended at tail.
if (tab_specifics->navigation(i).blocked_state() ==
@@ -514,21 +519,15 @@ void SessionsSyncManager::TrackTasks(
break;
}
- int navigation_index =
- current_navigation_index - current_index_in_tab_specifics + i;
- // Skipping navigations not been tracked by task_tracker.
- if (navigation_index < 0 ||
- navigation_index >= tab_tasks->GetNavigationsCount()) {
- continue;
- }
- std::vector<int64_t> task_ids =
- tab_tasks->GetTaskIdsForNavigation(navigation_index);
+ std::vector<int64_t> task_ids = tab_tasks->GetTaskIdsForNavigation(
+ tab_specifics->navigation(i).unique_id());
if (task_ids.empty())
continue;
tab_specifics->mutable_navigation(i)->set_task_id(task_ids.back());
// Pop the task id of navigation self.
task_ids.pop_back();
+ tab_specifics->mutable_navigation(i)->clear_ancestor_task_id();
for (auto ancestor_task_id : task_ids) {
tab_specifics->mutable_navigation(i)->add_ancestor_task_id(
ancestor_task_id);
diff --git a/chromium/components/sync_sessions/sessions_sync_manager.h b/chromium/components/sync_sessions/sessions_sync_manager.h
index 3c5e5fec3ad..975b673cdc6 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager.h
+++ b/chromium/components/sync_sessions/sessions_sync_manager.h
@@ -236,10 +236,11 @@ class SessionsSyncManager : public syncer::SyncableService,
void LocalTabDelegateToSpecifics(const SyncedTabDelegate& tab_delegate,
sync_pb::SessionSpecifics* specifics);
- // Updates task tracker with current navigation of |tab_delegate|, and fills
- // TabNavigation's task id related fields in |specifics|.
- void TrackTasks(SyncedTabDelegate* const tab_delegate,
- sync_pb::SessionSpecifics* specifics);
+ // Updates task tracker with the navigations of |tab_delegate|.
+ void UpdateTaskTracker(SyncedTabDelegate* const tab_delegate);
+
+ // Update |tab_specifics| with the corresponding task ids.
+ void WriteTasksIntoSpecifics(sync_pb::SessionTab* tab_specifics);
// 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
diff --git a/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc b/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc
index 26eac1b5136..d61838d3102 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc
+++ b/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc
@@ -714,11 +714,19 @@ class SessionsSyncManagerTest : public testing::Test {
const std::string& url,
base::Time time,
ui::PageTransition transition) {
+ sync_pb::TabNavigation tab_navigation;
+ tab_navigation.set_virtual_url(url);
+ tab_navigation.set_title(kTitle);
+ tab_navigation.set_global_id(time.ToInternalValue());
+ tab_navigation.set_unique_id(time.ToInternalValue());
+ tab_navigation.set_http_status_code(200);
+
auto entry = base::MakeUnique<sessions::SerializedNavigationEntry>(
- SerializedNavigationEntryTestHelper::CreateNavigation(url, kTitle));
+ sessions::SerializedNavigationEntry::FromSyncData(0, tab_navigation));
SerializedNavigationEntryTestHelper::SetTimestamp(time, entry.get());
SerializedNavigationEntryTestHelper::SetTransitionType(transition,
entry.get());
+
delegate->AppendEntry(std::move(entry));
delegate->set_current_entry_index(delegate->GetCurrentEntryIndex() + 1);
router_->NotifyNav(delegate);
@@ -2590,7 +2598,7 @@ TEST_F(SessionsSyncManagerTest, TrackTasksOnLocalTabModified) {
// Sync data of updating Tab 1 change
tab = SyncDataLocal(changes[1].sync_data()).GetSpecifics().session().tab();
EXPECT_EQ(tab.navigation_size(), 2);
- // navigation(0) and navigation(1) are two seperated tasks.
+ // navigation(0) and navigation(1) are two separated tasks.
EXPECT_EQ(tab.navigation(0).global_id(), tab.navigation(0).task_id());
EXPECT_TRUE(tab.navigation(0).ancestor_task_id().empty());
EXPECT_EQ(tab.navigation(1).global_id(), tab.navigation(1).task_id());
diff --git a/chromium/components/sync_sessions/task_tracker.cc b/chromium/components/sync_sessions/task_tracker.cc
index 0f8902e2905..d8351cbb7b0 100644
--- a/chromium/components/sync_sessions/task_tracker.cc
+++ b/chromium/components/sync_sessions/task_tracker.cc
@@ -11,153 +11,129 @@
namespace sync_sessions {
namespace {
-// The maximum number of tasks we track in a tab.
-int kMaxNumTasksPerTab = 100;
+
+// The criteria for whether a navigation will continue a task chain or start a
+// new one.
+bool DoesTransitionContinueTask(ui::PageTransition transition) {
+ if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK) ||
+ ui::PageTransitionCoreTypeIs(transition,
+ ui::PAGE_TRANSITION_AUTO_SUBFRAME) ||
+ ui::PageTransitionCoreTypeIs(transition,
+ ui::PAGE_TRANSITION_MANUAL_SUBFRAME) ||
+ ui::PageTransitionCoreTypeIs(transition,
+ ui::PAGE_TRANSITION_FORM_SUBMIT) ||
+ transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) {
+ return true;
+ }
+ return false;
}
+} // namespace
+
TabTasks::TabTasks() {}
+TabTasks::TabTasks(const TabTasks& rhs)
+ : nav_to_task_id_map_(rhs.nav_to_task_id_map_),
+ most_recent_nav_id_(rhs.most_recent_nav_id_) {}
+
TabTasks::~TabTasks() {}
-std::vector<int64_t> TabTasks::GetTaskIdsForNavigation(
- int navigation_index) const {
- CHECK_LE(0, navigation_index);
- CHECK_LT(navigation_index, GetNavigationsCount());
-
- std::vector<int64_t> root_to_self_task_ids;
- // Position of the navigation in task_ids_ vector.
- int navigation_position = navigation_index - excluded_navigation_num_;
-
- // If navigation_index is an excluded ancestor task, returns empty.
- if (navigation_position < 0)
- return root_to_self_task_ids;
-
- TaskIdAndRoot task_id_and_root = task_ids_[navigation_position];
-
- // If navigation_index is an invalid task, returns empty.
- if (task_id_and_root.root_navigation_index < 0)
- return root_to_self_task_ids;
-
- // The root task can be excluded. If so, consider the oldest ancestor
- // available as root.
- int root_navigation_index =
- task_id_and_root.root_navigation_index > excluded_navigation_num_
- ? task_id_and_root.root_navigation_index - excluded_navigation_num_
- : 0;
- for (int i = root_navigation_index; i <= navigation_position; i++) {
- // Fills the vector with valid tasks.
- if (task_ids_[i].root_navigation_index >= 0)
- root_to_self_task_ids.push_back(task_ids_[i].task_id);
+std::vector<int64_t> TabTasks::GetTaskIdsForNavigation(int nav_id) const {
+ std::vector<int64_t> task_id_chain;
+ int next_id = nav_id;
+ while (next_id != kInvalidNavID) {
+ if (nav_to_task_id_map_.count(next_id) == 0)
+ break;
+ task_id_chain.push_back(nav_to_task_id_map_.at(next_id).task_id);
+ DCHECK_NE(kInvalidGlobalID, task_id_chain.back());
+ next_id = nav_to_task_id_map_.at(next_id).parent_nav_id;
}
- return root_to_self_task_ids;
-}
+ DCHECK_LE(task_id_chain.size(), static_cast<size_t>(kMaxNumTasksPerTab));
-int TabTasks::GetNavigationsCount() const {
- return excluded_navigation_num_ + task_ids_.size();
+ // Reverse the order so the root is the first item (oldest -> newest).
+ std::reverse(task_id_chain.begin(), task_id_chain.end());
+
+ DVLOG(1) << "Returning " << task_id_chain.size() << " task ids for nav "
+ << nav_id;
+ return task_id_chain;
}
-void TabTasks::UpdateWithNavigation(int navigation_index,
+void TabTasks::UpdateWithNavigation(int nav_id,
ui::PageTransition transition,
- int64_t navigation_id) {
- // Triggered by some notifications on the current page, do nothing.
- if (navigation_index == current_navigation_index_) {
- DVLOG(1) << "Doing nothing for navigation_index: " << navigation_index
- << " of transition: " << transition;
- return;
- }
+ int64_t global_id) {
+ DCHECK_NE(kInvalidNavID, nav_id);
+ DCHECK_NE(kInvalidGlobalID, global_id);
- // Going back/forward to some previous navigation.
- if (navigation_index < current_navigation_index_ ||
- (navigation_index > current_navigation_index_ &&
- transition & ui::PAGE_TRANSITION_FORWARD_BACK &&
- base::checked_cast<size_t>(navigation_index) < task_ids_.size())) {
- DVLOG(1) << "Just updating task position with navigation_index: "
- << navigation_index << " of transition: " << transition;
- current_navigation_index_ = navigation_index;
+ if (nav_to_task_id_map_.count(nav_id) > 0) {
+ most_recent_nav_id_ = nav_id;
+ nav_to_task_id_map_[most_recent_nav_id_].global_id = global_id;
return;
}
- // A new task for the new navigation.
- int root_navigation_index = navigation_index;
- if (current_navigation_index_ != -1 &&
- (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK) ||
- ui::PageTransitionCoreTypeIs(transition,
- ui::PAGE_TRANSITION_AUTO_SUBFRAME) ||
- ui::PageTransitionCoreTypeIs(transition,
- ui::PAGE_TRANSITION_MANUAL_SUBFRAME) ||
- ui::PageTransitionCoreTypeIs(transition,
- ui::PAGE_TRANSITION_FORM_SUBMIT) ||
- transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK)) {
- // Creating a sub-task with navigation at current_navigation_index as
- // parent.
- DVLOG(1) << "Creating a sub-task with navigation_index: "
- << navigation_index << " of transition: " << transition
- << " under navigation_index: " << current_navigation_index_;
- // Position in task_id_.
- int current_navigation_position =
- current_navigation_index_ - excluded_navigation_num_;
- // If current/parent task is excluded, consider the new task as a root task.
- if (current_navigation_position >= 0) {
- CHECK_LT(current_navigation_position,
- base::checked_cast<int>(task_ids_.size()));
- root_navigation_index =
- task_ids_[current_navigation_position].root_navigation_index;
- } else {
- DVLOG(1) << "Becaue parent task is excluded, consider the sub-task as a "
- "root task.";
- }
- } else {
- // Creating a root task.
- // For now, we don't consider tasks cross tabs, so first navigation of the
- // tab always creates a root task.
- DVLOG(1) << "Creating a root task with navigation_index: "
- << navigation_index << " of transition: " << transition;
- }
+ nav_to_task_id_map_[nav_id].task_id = global_id;
+ nav_to_task_id_map_[nav_id].global_id = global_id;
- // In most cases navigation_index == excluded_navigation_num_ +
- // task_ids_.size() if the previous navigation is end of chain, or
- // navigation_index < excluded_navigation_num_ + task_ids_.size() otherwise.
- // In few case navigation_index > excluded_navigation_num_ + task_ids_.size(),
- // we fill task_ids_ with invalid contents. A known case is the first
- // navigation after newtab.
- for (int i = task_ids_.size() + excluded_navigation_num_;
- i < navigation_index; i++) {
- task_ids_.push_back({-1, -1});
+ if (DoesTransitionContinueTask(transition) &&
+ most_recent_nav_id_ != kInvalidGlobalID) {
+ nav_to_task_id_map_[nav_id].parent_nav_id = most_recent_nav_id_;
}
- // Erase all task ids associated with an outdated forward navigation stack.
- if (navigation_index > excluded_navigation_num_) {
- int new_task_id_position = navigation_index - excluded_navigation_num_;
- task_ids_.erase(task_ids_.begin() + new_task_id_position, task_ids_.end());
- } else {
- excluded_navigation_num_ = navigation_index;
- // new task id position is 0
- task_ids_.clear();
- }
+ DVLOG(1) << "Setting most recent nav id to " << nav_id;
+ DVLOG(1) << "Setting current task id to "
+ << nav_to_task_id_map_[nav_id].task_id;
+ most_recent_nav_id_ = nav_id;
+
+ // Go through and drop the oldest navigations until kMaxNumTasksPerTab
+ // navigations remain.
+ // TODO(zea): we go through max of 100 iterations here on each new navigation.
+ // May be worth attempting to optimize this further if it becomes an issue.
+ if (nav_to_task_id_map_.size() > kMaxNumTasksPerTab) {
+ int64_t oldest_nav_time = kInvalidGlobalID;
+ int oldest_nav_id = kInvalidNavID;
+ for (auto& iter : nav_to_task_id_map_) {
+ if (oldest_nav_id == kInvalidNavID ||
+ oldest_nav_time > iter.second.global_id) {
+ oldest_nav_id = iter.first;
+ oldest_nav_time = iter.second.global_id;
+ }
+ }
- // Exclude oldest ancestors if task number reaches the limit.
- int more_tasks_number = task_ids_.size() + 1 - kMaxNumTasksPerTab;
- if (more_tasks_number > 0) {
- task_ids_.erase(task_ids_.begin(), task_ids_.begin() + more_tasks_number);
- DVLOG(1) << "Excluding " << more_tasks_number
- << " oldest ancestor(s) from navigation index "
- << excluded_navigation_num_;
- excluded_navigation_num_ += more_tasks_number;
+ nav_to_task_id_map_.erase(oldest_nav_id);
+ DCHECK_EQ(static_cast<uint64_t>(kMaxNumTasksPerTab),
+ nav_to_task_id_map_.size());
}
-
- TaskIdAndRoot new_task = {root_navigation_index, navigation_id};
- // Add the current task at navigation_index.
- task_ids_.push_back(new_task);
- current_navigation_index_ = navigation_index;
- return;
}
TaskTracker::TaskTracker() {}
TaskTracker::~TaskTracker() {}
-TabTasks* TaskTracker::GetTabTasks(SessionID::id_type tab_id) {
- if (local_tab_tasks_map_.find(tab_id) == local_tab_tasks_map_.end()) {
+TabTasks* TaskTracker::GetTabTasks(SessionID::id_type tab_id,
+ SessionID::id_type parent_tab_id) {
+ DVLOG(1) << "Getting tab tasks for " << tab_id << " with parent "
+ << parent_tab_id;
+ // If an existing TabTasks exists, attempt to reuse it. The caveat is that if
+ // a parent tab id is provided, it must match the parent tab id associated
+ // with the existing TabTasks. Otherwise a new TabTasks should be created from
+ // the specified parent. This is to handle the case where at the time the
+ // initial GetTabTasks is called, the parent id is not yet known, but it
+ // becomes known at a later time.
+ if (local_tab_tasks_map_.count(tab_id) > 0 &&
+ (parent_tab_id == kInvalidTabID ||
+ local_tab_tasks_map_[tab_id]->parent_tab_id() == parent_tab_id)) {
+ return local_tab_tasks_map_[tab_id].get();
+ }
+
+ DVLOG(1) << "Creating tab tasks for " << tab_id;
+ if (local_tab_tasks_map_.count(parent_tab_id) > 0) {
+ // If the parent id is set, it means this tab forked from another tab.
+ // In that case, the task for the current navigation might be part of a
+ // larger task encompassing the parent tab. Perform a deep copy of the
+ // parent's TabTasks object in order to simplify tracking this relationship.
+ local_tab_tasks_map_[tab_id] =
+ base::MakeUnique<TabTasks>(*local_tab_tasks_map_[parent_tab_id]);
+ local_tab_tasks_map_[tab_id]->set_parent_tab_id(parent_tab_id);
+ } else {
local_tab_tasks_map_[tab_id] = base::MakeUnique<TabTasks>();
}
return local_tab_tasks_map_[tab_id].get();
@@ -165,9 +141,8 @@ TabTasks* TaskTracker::GetTabTasks(SessionID::id_type tab_id) {
void TaskTracker::CleanTabTasks(SessionID::id_type tab_id) {
auto iter = local_tab_tasks_map_.find(tab_id);
- if (iter != local_tab_tasks_map_.end()) {
+ if (iter != local_tab_tasks_map_.end())
local_tab_tasks_map_.erase(iter);
- }
}
} // namespace sync_sessions
diff --git a/chromium/components/sync_sessions/task_tracker.h b/chromium/components/sync_sessions/task_tracker.h
index 44ee36368a6..30f932058e6 100644
--- a/chromium/components/sync_sessions/task_tracker.h
+++ b/chromium/components/sync_sessions/task_tracker.h
@@ -21,77 +21,97 @@
namespace sync_sessions {
-// Class to generate and manage task ids for navigations of a tab. For each
-// current navigation of a tab, UpdateWithNavigation(int navigation_index,
-// ui::PageTransition transition)
-// needs to be called to update the object.
-//
-// TODO(shenchao): If the tab is restored, then the input navigation is not
-// necessarily the first navigation in this case. Need to fix it by initalizing
-// the object with restored data.
-// TODO(shenchao): Support to track tasks cross tabs.
+// Default/invalid global id. These are internal timestamp representations of
+// a navigation (see base::Time::ToInternalValue()). Note that task ids are
+// a subset of global ids.
+static constexpr int64_t kInvalidGlobalID = -1;
+
+// Default/invalid id for a navigation.
+static constexpr int kInvalidNavID = 0;
+
+// The maximum number of tasks we track in a tab.
+static constexpr int kMaxNumTasksPerTab = 100;
+
+// Class to generate and manage task ids for navigations of a tab. It is
+// expected that there is only 1 TabTasks object for every tab, although Task
+// ids can be duplicated across TabTasks (see copy constructor).
class TabTasks {
public:
TabTasks();
+ explicit TabTasks(const TabTasks& rhs);
virtual ~TabTasks();
- // Gets top-down task id list of ancestors and itself for
- // |navigation_index|-th navigation of the tab.
- std::vector<int64_t> GetTaskIdsForNavigation(int navigation_index) const;
+ SessionID::id_type parent_tab_id() { return parent_tab_id_; }
+ void set_parent_tab_id(SessionID::id_type parent_tab_id) {
+ parent_tab_id_ = parent_tab_id;
+ }
- int GetNavigationsCount() const;
+ // Gets root->leaf task id list for the navigation denoted by |nav_id|.
+ // Returns an empty vector if |nav_id| is not found.
+ std::vector<int64_t> GetTaskIdsForNavigation(int nav_id) const;
- // Updates the current task of the tab, given current navigation index of the
- // tab as |navigation_index|, and its |transition|.
- // If the navigation is from going back/forward of the tab, we set its first
- // visit as current task; if the navigation is new, we create a subtask of the
- // previous navigation if it's linked from the previous one or a root task
- // otherwise, and use |navigation_id| as new task id.
- void UpdateWithNavigation(int navigation_index,
+ void UpdateWithNavigation(int nav_id,
ui::PageTransition transition,
- int64_t navigation_id);
+ int64_t global_id);
private:
FRIEND_TEST_ALL_PREFIXES(TaskTrackerTest, LimitMaxNumberOfTasksPerTab);
-
FRIEND_TEST_ALL_PREFIXES(TaskTrackerTest,
CreateSubTaskFromExcludedAncestorTask);
- struct TaskIdAndRoot {
- // Root task index in task_ids_. Negative value means it's an invalid task
- // just for filling the task_ids_.
- int root_navigation_index;
- int64_t task_id;
+ // The task id and and immediate parent for a navigation (see
+ // |nav_to_task_id_map_|).
+ struct TaskIdAndParent {
+ // The task id for this navigation. Should always be present.
+ int64_t task_id = kInvalidGlobalID;
+
+ // The most recent global id for this navigation. This is used to determine
+ // the age of this navigation when expiring old navigations. Should always
+ // be present.
+ int64_t global_id = kInvalidGlobalID;
+
+ // If present, the nav id that this task is a continuation of. If this is
+ // the first navigation in a new task, may not be present.
+ int parent_nav_id = kInvalidNavID;
};
- // Task ids (with root task) for the navigations of the tab. The vector is
- // corresponding to the sequence of navigations of the tab.
- std::vector<TaskIdAndRoot> task_ids_;
- // Index of current navigation in task_ids_.
- int current_navigation_index_ = -1;
- // Number of oldest ancestors which have been excluded from being tracked in
- // task_ids_;
- int excluded_navigation_num_ = 0;
+ // Map converting navigation ids to a TaskIdAndParent. To find the root to
+ // leaf chain for a navigation, start with its navigation id, and reindex into
+ // the map using the parent id, until the parent id is kInvalidNavID.
+ std::map<int, TaskIdAndParent> nav_to_task_id_map_;
+
+ // The most recent navigation id seen for this tab.
+ int most_recent_nav_id_ = kInvalidNavID;
- DISALLOW_COPY_AND_ASSIGN(TabTasks);
+ // The parent id for this tab (if there is one).
+ SessionID::id_type parent_tab_id_ = kInvalidTabID;
+
+ DISALLOW_ASSIGN(TabTasks);
};
-// Tracks tasks of current session.
+// Tracks tasks of current session. Tasks are a set of navigations that are
+// related by explicit user actions (as determined by transition type. See
+// |UpdateWithNavigation| above). Each task is composed of a tree of task ids
+// that identify the navigations of that task (see |GetTaskIdsForNavigation|).
class TaskTracker {
public:
- // Constructs with a clock to get timestamp as new task ids.
TaskTracker();
virtual ~TaskTracker();
// Returns a TabTasks pointer, which is owned by this object, for the tab of
- // given |tab_id|.
- TabTasks* GetTabTasks(SessionID::id_type tab_id);
+ // given |tab_id|. |parent_tab_id|, if set, can be used to link the task ids
+ // from one tab to another (e.g. when opening a navigation in a new tab, the
+ // task ids from the original tab are necessary to continue tracking the
+ // task chain).
+ TabTasks* GetTabTasks(SessionID::id_type tab_id,
+ SessionID::id_type parent_tab_id);
// Cleans tracked task ids of navigations in the tab of |tab_id|.
void CleanTabTasks(SessionID::id_type tab_id);
private:
FRIEND_TEST_ALL_PREFIXES(TaskTrackerTest, CleanTabTasks);
+
std::map<SessionID::id_type, std::unique_ptr<TabTasks>> local_tab_tasks_map_;
DISALLOW_COPY_AND_ASSIGN(TaskTracker);
diff --git a/chromium/components/sync_sessions/task_tracker_unittest.cc b/chromium/components/sync_sessions/task_tracker_unittest.cc
index 40fd0424d4d..be541ca63d8 100644
--- a/chromium/components/sync_sessions/task_tracker_unittest.cc
+++ b/chromium/components/sync_sessions/task_tracker_unittest.cc
@@ -6,10 +6,12 @@
#include <utility>
+#include "components/sync_sessions/synced_tab_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::ElementsAre;
+using testing::ElementsAreArray;
using testing::SizeIs;
namespace sync_sessions {
@@ -21,15 +23,15 @@ const int kTab2 = 25;
TEST(TaskTrackerTest, GetTabTasks) {
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
ASSERT_NE(tab_tasks, nullptr);
- EXPECT_EQ(task_tracker.GetTabTasks(kTab1), tab_tasks);
- EXPECT_NE(task_tracker.GetTabTasks(kTab2), tab_tasks);
+ EXPECT_EQ(task_tracker.GetTabTasks(kTab1, kInvalidTabID), tab_tasks);
+ EXPECT_NE(task_tracker.GetTabTasks(kTab2, kInvalidTabID), tab_tasks);
}
TEST(TaskTrackerTest, CleanTabTasks) {
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
ASSERT_NE(tab_tasks, nullptr);
ASSERT_FALSE(task_tracker.local_tab_tasks_map_.empty());
@@ -39,7 +41,7 @@ TEST(TaskTrackerTest, CleanTabTasks) {
TEST(TaskTrackerTest, UpdateTasksWithMultipleClicks) {
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
tab_tasks->UpdateWithNavigation(1, ui::PageTransition::PAGE_TRANSITION_TYPED,
100);
@@ -60,7 +62,7 @@ TEST(TaskTrackerTest, UpdateTasksWithMultipleClicks) {
TEST(TaskTrackerTest, UpdateTasksWithMultipleClicksAndTypes) {
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
tab_tasks->UpdateWithNavigation(1, ui::PAGE_TRANSITION_LINK, 100);
tab_tasks->UpdateWithNavigation(2, ui::PAGE_TRANSITION_LINK, 200);
@@ -82,7 +84,7 @@ TEST(TaskTrackerTest, UpdateTasksWithMultipleClicksAndTypes) {
TEST(TaskTrackerTest, UpdateTasksWithBackforwards) {
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
tab_tasks->UpdateWithNavigation(1, ui::PAGE_TRANSITION_TYPED, 100);
tab_tasks->UpdateWithNavigation(2, ui::PAGE_TRANSITION_LINK, 200);
@@ -111,7 +113,7 @@ TEST(TaskTrackerTest, UpdateTasksWithBackforwards) {
TEST(TaskTrackerTest, UpdateWithNavigationsWithBackAndForkedNavigation) {
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
tab_tasks->UpdateWithNavigation(1, ui::PAGE_TRANSITION_LINK, 100);
tab_tasks->UpdateWithNavigation(2, ui::PAGE_TRANSITION_LINK, 200);
tab_tasks->UpdateWithNavigation(3, ui::PAGE_TRANSITION_LINK, 300);
@@ -120,27 +122,24 @@ TEST(TaskTrackerTest, UpdateWithNavigationsWithBackAndForkedNavigation) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_FORWARD_BACK),
400);
- tab_tasks->UpdateWithNavigation(2, ui::PAGE_TRANSITION_LINK, 500);
+ tab_tasks->UpdateWithNavigation(4, ui::PAGE_TRANSITION_LINK, 500);
EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(1), ElementsAre(100));
- EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(2), ElementsAre(100, 500));
- // We don't track navigation at index 3 any more, and it's out of scope now.
- EXPECT_THAT(tab_tasks->GetNavigationsCount(), 3);
+ EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(4), ElementsAre(100, 500));
}
TEST(TaskTrackerTest, LimitMaxNumberOfTasksPerTab) {
- int kMaxNumTasksPerTab = 100;
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
// Reaching max number of tasks for a tab.
- for (int i = 0; i < kMaxNumTasksPerTab; i++) {
+ for (int i = 1; i <= kMaxNumTasksPerTab; i++) {
tab_tasks->UpdateWithNavigation(i, ui::PAGE_TRANSITION_LINK, i * 100);
}
tab_tasks->UpdateWithNavigation(kMaxNumTasksPerTab, ui::PAGE_TRANSITION_LINK,
kMaxNumTasksPerTab * 100);
- ASSERT_THAT(tab_tasks->task_ids_, SizeIs(kMaxNumTasksPerTab));
+ ASSERT_THAT(tab_tasks->nav_to_task_id_map_, SizeIs(kMaxNumTasksPerTab));
EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(0), ElementsAre());
EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(1), ElementsAre(100));
std::vector<int64_t> task_ids =
@@ -153,7 +152,7 @@ TEST(TaskTrackerTest, LimitMaxNumberOfTasksPerTab) {
ui::PAGE_TRANSITION_LINK,
(kMaxNumTasksPerTab + 1) * 100);
- ASSERT_THAT(tab_tasks->task_ids_, SizeIs(kMaxNumTasksPerTab));
+ ASSERT_THAT(tab_tasks->nav_to_task_id_map_, SizeIs(kMaxNumTasksPerTab));
EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(0), ElementsAre());
EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(1), ElementsAre());
EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(2), ElementsAre(200));
@@ -163,36 +162,101 @@ TEST(TaskTrackerTest, LimitMaxNumberOfTasksPerTab) {
EXPECT_EQ(task_ids[kMaxNumTasksPerTab - 1], (kMaxNumTasksPerTab + 1) * 100);
}
-TEST(TaskTrackerTest, CreateSubTaskFromExcludedAncestorTask) {
- int kMaxNumTasksPerTab = 100;
+TEST(TaskTrackerTest, CreateTabTasksFromSourceTab) {
TaskTracker task_tracker;
- TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab1);
+ TabTasks* source_tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
+ source_tab_tasks->UpdateWithNavigation(1, ui::PAGE_TRANSITION_LINK, 100);
+ source_tab_tasks->UpdateWithNavigation(2, ui::PAGE_TRANSITION_LINK, 200);
+ source_tab_tasks->UpdateWithNavigation(3, ui::PAGE_TRANSITION_TYPED, 300);
+ source_tab_tasks->UpdateWithNavigation(4, ui::PAGE_TRANSITION_LINK, 400);
+ source_tab_tasks->UpdateWithNavigation(5, ui::PAGE_TRANSITION_LINK, 500);
+
+ TabTasks* target_tab_tasks = task_tracker.GetTabTasks(kTab2, kTab1);
+ EXPECT_EQ(kTab1, target_tab_tasks->parent_tab_id());
+ target_tab_tasks->UpdateWithNavigation(6, ui::PAGE_TRANSITION_LINK, 600);
+ target_tab_tasks->UpdateWithNavigation(7, ui::PAGE_TRANSITION_LINK, 700);
+ target_tab_tasks->UpdateWithNavigation(8, ui::PAGE_TRANSITION_TYPED, 800);
+
+ EXPECT_THAT(target_tab_tasks->GetTaskIdsForNavigation(6),
+ ElementsAre(300, 400, 500, 600));
+ EXPECT_THAT(target_tab_tasks->GetTaskIdsForNavigation(7),
+ ElementsAre(300, 400, 500, 600, 700));
+ EXPECT_THAT(target_tab_tasks->GetTaskIdsForNavigation(8), ElementsAre(800));
+}
- // Reaching max number of tasks for a tab.
- for (int i = 0; i < kMaxNumTasksPerTab; i++) {
- tab_tasks->UpdateWithNavigation(i, ui::PAGE_TRANSITION_LINK, i * 100);
+TEST(TaskTrackerTest, CreateTabTasksFromSourceTabAfterGoingBack) {
+ TaskTracker task_tracker;
+ TabTasks* source_tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
+ source_tab_tasks->UpdateWithNavigation(1, ui::PAGE_TRANSITION_LINK, 100);
+ source_tab_tasks->UpdateWithNavigation(2, ui::PAGE_TRANSITION_LINK, 200);
+ source_tab_tasks->UpdateWithNavigation(3, ui::PAGE_TRANSITION_TYPED, 300);
+ source_tab_tasks->UpdateWithNavigation(4, ui::PAGE_TRANSITION_LINK, 400);
+ source_tab_tasks->UpdateWithNavigation(5, ui::PAGE_TRANSITION_LINK, 500);
+ source_tab_tasks->UpdateWithNavigation(
+ 2,
+ ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
+ ui::PAGE_TRANSITION_FORWARD_BACK),
+ 600);
+ ASSERT_THAT(source_tab_tasks->GetTaskIdsForNavigation(2),
+ ElementsAre(100, 200));
+
+ TabTasks* target_tab_tasks = task_tracker.GetTabTasks(kTab2, kTab1);
+ EXPECT_EQ(kTab1, target_tab_tasks->parent_tab_id());
+ target_tab_tasks->UpdateWithNavigation(7, ui::PAGE_TRANSITION_LINK, 700);
+
+ EXPECT_THAT(target_tab_tasks->GetTaskIdsForNavigation(7),
+ ElementsAre(100, 200, 700));
+}
+
+TEST(TaskTrackerTest, CreateTabTasksFromSourceTabWithLimitedTaskNum) {
+ TaskTracker task_tracker;
+
+ TabTasks* source_tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
+ // Adding max number of tasks to tab1.
+ int nav_id = 1;
+ for (; nav_id <= kMaxNumTasksPerTab; nav_id++) {
+ source_tab_tasks->UpdateWithNavigation(nav_id, ui::PAGE_TRANSITION_LINK,
+ nav_id * 100);
}
- tab_tasks->UpdateWithNavigation(kMaxNumTasksPerTab, ui::PAGE_TRANSITION_LINK,
- kMaxNumTasksPerTab * 100);
- ASSERT_EQ(tab_tasks->excluded_navigation_num_, 1);
- ASSERT_EQ(tab_tasks->current_navigation_index_, kMaxNumTasksPerTab);
+ TabTasks* tab_tasks = task_tracker.GetTabTasks(kTab2, kTab1);
+ EXPECT_EQ(kTab1, tab_tasks->parent_tab_id());
+ tab_tasks->UpdateWithNavigation(nav_id, ui::PAGE_TRANSITION_LINK,
+ nav_id * 100);
+
+ std::vector<int64_t> task_ids = tab_tasks->GetTaskIdsForNavigation(nav_id);
+ // The first task from source tab (id 100) is excluded because of max task
+ // number limit, so start at id 200.
+ int expected_task_ids[kMaxNumTasksPerTab];
+ for (int i = 0; i < kMaxNumTasksPerTab; i++)
+ expected_task_ids[i] = (i + 2) * 100;
+ EXPECT_THAT(task_ids,
+ ElementsAreArray(expected_task_ids, kMaxNumTasksPerTab));
+}
- tab_tasks->UpdateWithNavigation(
- 0,
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_FORWARD_BACK),
- (kMaxNumTasksPerTab + 1) * 100);
- ASSERT_EQ(tab_tasks->excluded_navigation_num_, 1);
- ASSERT_EQ(tab_tasks->current_navigation_index_, 0);
-
- tab_tasks->UpdateWithNavigation(1, ui::PAGE_TRANSITION_LINK,
- (kMaxNumTasksPerTab + 2) * 100);
- ASSERT_THAT(tab_tasks->task_ids_, SizeIs(1));
- ASSERT_EQ(tab_tasks->GetNavigationsCount(), 2);
- EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(0), ElementsAre());
- EXPECT_THAT(tab_tasks->GetTaskIdsForNavigation(1),
- ElementsAre((kMaxNumTasksPerTab + 2) * 100));
+TEST(TaskTrackerTest, GetTabTasksWithNewSource) {
+ TaskTracker task_tracker;
+ TabTasks* target_tab_tasks = task_tracker.GetTabTasks(kTab2, kInvalidTabID);
+ EXPECT_EQ(kInvalidTabID, target_tab_tasks->parent_tab_id());
+
+ TabTasks* source_tab_tasks = task_tracker.GetTabTasks(kTab1, kInvalidTabID);
+ source_tab_tasks->UpdateWithNavigation(1, ui::PAGE_TRANSITION_LINK, 100);
+ source_tab_tasks->UpdateWithNavigation(2, ui::PAGE_TRANSITION_LINK, 200);
+ source_tab_tasks->UpdateWithNavigation(3, ui::PAGE_TRANSITION_TYPED, 300);
+ source_tab_tasks->UpdateWithNavigation(4, ui::PAGE_TRANSITION_LINK, 400);
+ source_tab_tasks->UpdateWithNavigation(5, ui::PAGE_TRANSITION_LINK, 500);
+
+ target_tab_tasks = task_tracker.GetTabTasks(kTab2, kTab1);
+ EXPECT_EQ(kTab1, target_tab_tasks->parent_tab_id());
+ target_tab_tasks->UpdateWithNavigation(6, ui::PAGE_TRANSITION_LINK, 600);
+ target_tab_tasks->UpdateWithNavigation(7, ui::PAGE_TRANSITION_LINK, 700);
+ target_tab_tasks->UpdateWithNavigation(8, ui::PAGE_TRANSITION_TYPED, 800);
+
+ EXPECT_THAT(target_tab_tasks->GetTaskIdsForNavigation(6),
+ ElementsAre(300, 400, 500, 600));
+ EXPECT_THAT(target_tab_tasks->GetTaskIdsForNavigation(7),
+ ElementsAre(300, 400, 500, 600, 700));
+ EXPECT_THAT(target_tab_tasks->GetTaskIdsForNavigation(8), ElementsAre(800));
}
} // namespace sync_sessions
diff --git a/chromium/components/sync_wifi/wifi_credential.cc b/chromium/components/sync_wifi/wifi_credential.cc
index 0337ee8188f..bf6294a3f27 100644
--- a/chromium/components/sync_wifi/wifi_credential.cc
+++ b/chromium/components/sync_wifi/wifi_credential.cc
@@ -52,20 +52,19 @@ std::unique_ptr<base::DictionaryValue> WifiCredential::ToOncProperties() const {
return base::MakeUnique<base::DictionaryValue>();
}
- std::unique_ptr<base::DictionaryValue> onc_properties(
- new base::DictionaryValue());
- onc_properties->Set(onc::toplevel_config::kType,
- new base::Value(onc::network_type::kWiFi));
+ auto onc_properties = base::MakeUnique<base::DictionaryValue>();
+ onc_properties->SetString(onc::toplevel_config::kType,
+ onc::network_type::kWiFi);
// TODO(quiche): Switch to the HexSSID property, once ONC fully supports it.
// crbug.com/432546.
- onc_properties->Set(onc::network_config::WifiProperty(onc::wifi::kSSID),
- new base::Value(ssid_utf8));
- onc_properties->Set(onc::network_config::WifiProperty(onc::wifi::kSecurity),
- new base::Value(onc_security));
+ onc_properties->SetString(onc::network_config::WifiProperty(onc::wifi::kSSID),
+ ssid_utf8);
+ onc_properties->SetString(
+ onc::network_config::WifiProperty(onc::wifi::kSecurity), onc_security);
if (WifiSecurityClassSupportsPassphrases(security_class())) {
- onc_properties->Set(
+ onc_properties->SetString(
onc::network_config::WifiProperty(onc::wifi::kPassphrase),
- new base::Value(passphrase()));
+ passphrase());
}
return onc_properties;
}
diff --git a/chromium/components/task_scheduler_util/browser/initialization.cc b/chromium/components/task_scheduler_util/browser/initialization.cc
index c6e492bf2ea..57753164414 100644
--- a/chromium/components/task_scheduler_util/browser/initialization.cc
+++ b/chromium/components/task_scheduler_util/browser/initialization.cc
@@ -7,10 +7,6 @@
#include <map>
#include <string>
-#include "base/command_line.h"
-#include "base/task_scheduler/scheduler_worker_params.h"
-#include "base/task_scheduler/switches.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "components/task_scheduler_util/common/variations_util.h"
#include "components/variations/variations_associated_data.h"
@@ -32,20 +28,4 @@ GetBrowserTaskSchedulerInitParamsFromVariations() {
"", variation_params, base::SchedulerBackwardCompatibility::INIT_COM_STA);
}
-void MaybePerformBrowserTaskSchedulerRedirection() {
- // TODO(gab): Remove this when http://crbug.com/622400 concludes.
- if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableBrowserTaskScheduler) &&
- variations::GetVariationParamValue(
- kFieldTrialName, "RedirectSequencedWorkerPools") == "true") {
- const base::TaskPriority max_task_priority =
- variations::GetVariationParamValue(
- kFieldTrialName, "CapSequencedWorkerPoolsAtUserVisible") == "true"
- ? base::TaskPriority::USER_VISIBLE
- : base::TaskPriority::HIGHEST;
- base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess(
- max_task_priority);
- }
-}
-
} // namespace task_scheduler_util
diff --git a/chromium/components/task_scheduler_util/browser/initialization.h b/chromium/components/task_scheduler_util/browser/initialization.h
index 46ad21860f2..102f6981567 100644
--- a/chromium/components/task_scheduler_util/browser/initialization.h
+++ b/chromium/components/task_scheduler_util/browser/initialization.h
@@ -16,10 +16,6 @@ namespace task_scheduler_util {
std::unique_ptr<base::TaskScheduler::InitParams>
GetBrowserTaskSchedulerInitParamsFromVariations();
-// Redirects zero-to-many PostTask APIs to the browser task scheduler based off
-// variations.
-void MaybePerformBrowserTaskSchedulerRedirection();
-
} // namespace task_scheduler_util
#endif // COMPONENTS_TASK_SCHEDULER_UTIL_BROWSER_INITIALIZATION_H_
diff --git a/chromium/components/test/BUILD.gn b/chromium/components/test/BUILD.gn
index 0a62f38f655..f6402ed979a 100644
--- a/chromium/components/test/BUILD.gn
+++ b/chromium/components/test/BUILD.gn
@@ -36,3 +36,16 @@ source_set("test_support") {
public_deps += [ "//content/test:test_support" ]
}
}
+
+# Defines a main() function that uses components_test_suite.h
+source_set("run_all_unittests") {
+ testonly = true
+
+ sources = [
+ "run_all_unittests.cc",
+ ]
+
+ deps = [
+ ":test_support",
+ ]
+}
diff --git a/chromium/components/timers/alarm_timer_chromeos.cc b/chromium/components/timers/alarm_timer_chromeos.cc
index 601b411bd7a..cc550eb5006 100644
--- a/chromium/components/timers/alarm_timer_chromeos.cc
+++ b/chromium/components/timers/alarm_timer_chromeos.cc
@@ -26,12 +26,12 @@ AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
weak_factory_(this) {}
AlarmTimer::~AlarmTimer() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
Stop();
}
void AlarmTimer::Stop() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
if (!base::Timer::is_running())
return;
@@ -53,7 +53,7 @@ void AlarmTimer::Stop() {
}
void AlarmTimer::Reset() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
DCHECK(!base::Timer::user_task().is_null());
if (!CanWakeFromSuspend()) {
@@ -107,7 +107,7 @@ void AlarmTimer::Reset() {
}
void AlarmTimer::OnAlarmFdReadableWithoutBlocking() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
DCHECK(base::Timer::IsRunning());
// Read from |alarm_fd_| to ack the event.
@@ -119,7 +119,7 @@ void AlarmTimer::OnAlarmFdReadableWithoutBlocking() {
}
void AlarmTimer::OnTimerFired() {
- DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
DCHECK(base::Timer::IsRunning());
DCHECK(pending_task_.get());
diff --git a/chromium/components/toolbar/BUILD.gn b/chromium/components/toolbar/BUILD.gn
index 0f1cc9f0e6a..9cdd003e7f6 100644
--- a/chromium/components/toolbar/BUILD.gn
+++ b/chromium/components/toolbar/BUILD.gn
@@ -2,8 +2,15 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/buildflag_header.gni")
import("//build/config/ui.gni")
-import("//ui/vector_icons/vector_icons.gni")
+import("//device/vr/features/features.gni")
+import("//components/vector_icons/vector_icons.gni")
+
+buildflag_header("build_features") {
+ header = "features.h"
+ flags = [ "ENABLE_VR=$enable_vr" ]
+}
aggregate_vector_icons("toolbar_vector_icons") {
icon_directory = "vector_icons"
@@ -17,6 +24,7 @@ aggregate_vector_icons("toolbar_vector_icons") {
"https_valid.icon",
"https_valid_in_chip.1x.icon",
"https_valid_in_chip.icon",
+ "offline_pin.icon",
"product.1x.icon",
"product.icon",
"star_active.icon",
@@ -28,14 +36,15 @@ static_library("vector_icons") {
sources = get_target_outputs(":toolbar_vector_icons")
deps = [
":toolbar_vector_icons",
+ "//components/vector_icons",
"//skia",
"//ui/gfx",
- "//ui/vector_icons",
]
}
static_library("toolbar") {
sources = [
+ "features.h",
"toolbar_model.h",
"toolbar_model_delegate.h",
"toolbar_model_impl.cc",
@@ -49,6 +58,7 @@ static_library("toolbar") {
]
deps = [
+ ":build_features",
"//components/google/core/browser",
"//components/prefs",
"//components/resources",
@@ -59,7 +69,7 @@ static_library("toolbar") {
"//ui/gfx",
]
- if (!is_android && !is_ios) {
+ if ((!is_android || enable_vr) && !is_ios) {
deps += [ ":vector_icons" ]
}
}
diff --git a/chromium/components/toolbar/DEPS b/chromium/components/toolbar/DEPS
index 999012d6e34..e844d5b2c44 100644
--- a/chromium/components/toolbar/DEPS
+++ b/chromium/components/toolbar/DEPS
@@ -8,5 +8,5 @@ include_rules = [
"+net",
"+ui/base",
"+ui/gfx",
- "+ui/vector_icons",
+ "+components/vector_icons",
]
diff --git a/chromium/components/toolbar/test_toolbar_model.cc b/chromium/components/toolbar/test_toolbar_model.cc
index cebc76cc2a0..59a81d9a9d4 100644
--- a/chromium/components/toolbar/test_toolbar_model.cc
+++ b/chromium/components/toolbar/test_toolbar_model.cc
@@ -47,3 +47,7 @@ base::string16 TestToolbarModel::GetEVCertName() const {
bool TestToolbarModel::ShouldDisplayURL() const {
return should_display_url_;
}
+
+bool TestToolbarModel::IsOfflinePage() const {
+ return offline_page_;
+}
diff --git a/chromium/components/toolbar/test_toolbar_model.h b/chromium/components/toolbar/test_toolbar_model.h
index 01daf608d46..0484b074248 100644
--- a/chromium/components/toolbar/test_toolbar_model.h
+++ b/chromium/components/toolbar/test_toolbar_model.h
@@ -31,6 +31,7 @@ class TestToolbarModel : public ToolbarModel {
base::string16 GetSecureVerboseText() const override;
base::string16 GetEVCertName() const override;
bool ShouldDisplayURL() const override;
+ bool IsOfflinePage() const override;
void set_text(const base::string16& text) { text_ = text; }
void set_url(const GURL& url) { url_ = url; }
@@ -44,14 +45,16 @@ class TestToolbarModel : public ToolbarModel {
void set_should_display_url(bool should_display_url) {
should_display_url_ = should_display_url;
}
+ void set_offline_page(bool offline_page) { offline_page_ = offline_page; }
private:
base::string16 text_;
GURL url_;
- security_state::SecurityLevel security_level_;
+ security_state::SecurityLevel security_level_ = security_state::NONE;
const gfx::VectorIcon* icon_ = nullptr;
base::string16 ev_cert_name_;
- bool should_display_url_;
+ bool should_display_url_ = false;
+ bool offline_page_ = false;
DISALLOW_COPY_AND_ASSIGN(TestToolbarModel);
};
diff --git a/chromium/components/toolbar/toolbar_model.h b/chromium/components/toolbar/toolbar_model.h
index 8aa1727dc91..05648825ce8 100644
--- a/chromium/components/toolbar/toolbar_model.h
+++ b/chromium/components/toolbar/toolbar_model.h
@@ -60,6 +60,10 @@ class ToolbarModel {
// in the location bar.
virtual bool ShouldDisplayURL() const = 0;
+ // Returns whether the page is an offline page, sourced from a cache of
+ // previously-downloaded content.
+ virtual bool IsOfflinePage() const = 0;
+
// Whether the text in the omnibox is currently being edited.
void set_input_in_progress(bool input_in_progress) {
input_in_progress_ = input_in_progress;
diff --git a/chromium/components/toolbar/toolbar_model_delegate.h b/chromium/components/toolbar/toolbar_model_delegate.h
index 8c61c451bec..f0f4f171b13 100644
--- a/chromium/components/toolbar/toolbar_model_delegate.h
+++ b/chromium/components/toolbar/toolbar_model_delegate.h
@@ -50,12 +50,16 @@ class ToolbarModelDelegate {
// Returns true if the current page fails the malware check.
virtual bool FailsMalwareCheck() const = 0;
- // Returns the id of the icon to show to the left of the address, or
- // gfx::VectorIconId::VECTOR_ICON_NONE if the icon should be selected by the
- // caller. This is useful for associating particular URLs with particular
- // schemes without importing knowledge of those schemes into this component.
+ // Returns the id of the icon to show to the left of the address, or nullptr
+ // if the icon should be selected by the caller. This is useful for
+ // associating particular URLs with particular schemes without importing
+ // knowledge of those schemes into this component.
virtual const gfx::VectorIcon* GetVectorIconOverride() const = 0;
+ // Returns whether the page is an offline page, sourced from a cache of
+ // previously-downloaded content.
+ virtual bool IsOfflinePage() const = 0;
+
protected:
virtual ~ToolbarModelDelegate() {}
};
diff --git a/chromium/components/toolbar/toolbar_model_impl.cc b/chromium/components/toolbar/toolbar_model_impl.cc
index fee722b7abb..6e275ef989d 100644
--- a/chromium/components/toolbar/toolbar_model_impl.cc
+++ b/chromium/components/toolbar/toolbar_model_impl.cc
@@ -11,6 +11,7 @@
#include "components/prefs/pref_service.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
+#include "components/toolbar/features.h"
#include "components/toolbar/toolbar_model_delegate.h"
#include "components/url_formatter/elide_url.h"
#include "components/url_formatter/url_formatter.h"
@@ -21,9 +22,9 @@
#include "ui/gfx/text_elider.h"
#include "ui/gfx/vector_icon_types.h"
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
#include "components/toolbar/vector_icons.h" // nogncheck
-#include "ui/vector_icons/vector_icons.h" // nogncheck
+#include "components/vector_icons/vector_icons.h" // nogncheck
#endif
ToolbarModelImpl::ToolbarModelImpl(ToolbarModelDelegate* delegate,
@@ -73,11 +74,14 @@ security_state::SecurityLevel ToolbarModelImpl::GetSecurityLevel(
}
const gfx::VectorIcon& ToolbarModelImpl::GetVectorIcon() const {
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
auto* const icon_override = delegate_->GetVectorIconOverride();
if (icon_override)
return *icon_override;
+ if (IsOfflinePage())
+ return toolbar::kOfflinePinIcon;
+
switch (GetSecurityLevel(false)) {
case security_state::NONE:
case security_state::HTTP_SHOW_WARNING:
@@ -89,7 +93,7 @@ const gfx::VectorIcon& ToolbarModelImpl::GetVectorIcon() const {
// Surface Dubious as Neutral.
return toolbar::kHttpIcon;
case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
- return ui::kBusinessIcon;
+ return vector_icons::kBusinessIcon;
case security_state::DANGEROUS:
return toolbar::kHttpsInvalidIcon;
}
@@ -120,6 +124,9 @@ base::string16 ToolbarModelImpl::GetEVCertName() const {
}
base::string16 ToolbarModelImpl::GetSecureVerboseText() const {
+ if (IsOfflinePage())
+ return l10n_util::GetStringUTF16(IDS_OFFLINE_VERBOSE_STATE);
+
switch (GetSecurityLevel(false)) {
case security_state::HTTP_SHOW_WARNING:
return l10n_util::GetStringUTF16(IDS_NOT_SECURE_VERBOSE_STATE);
@@ -137,3 +144,7 @@ base::string16 ToolbarModelImpl::GetSecureVerboseText() const {
bool ToolbarModelImpl::ShouldDisplayURL() const {
return delegate_->ShouldDisplayURL();
}
+
+bool ToolbarModelImpl::IsOfflinePage() const {
+ return delegate_->IsOfflinePage();
+}
diff --git a/chromium/components/toolbar/toolbar_model_impl.h b/chromium/components/toolbar/toolbar_model_impl.h
index 619d3e56d8e..6bbf45af108 100644
--- a/chromium/components/toolbar/toolbar_model_impl.h
+++ b/chromium/components/toolbar/toolbar_model_impl.h
@@ -36,6 +36,7 @@ class ToolbarModelImpl : public ToolbarModel {
base::string16 GetSecureVerboseText() const override;
base::string16 GetEVCertName() const override;
bool ShouldDisplayURL() const override;
+ bool IsOfflinePage() const override;
ToolbarModelDelegate* delegate_;
const size_t max_url_display_chars_;
diff --git a/chromium/components/toolbar/vector_icons/offline_pin.icon b/chromium/components/toolbar/vector_icons/offline_pin.icon
new file mode 100644
index 00000000000..935b7332035
--- /dev/null
+++ b/chromium/components/toolbar/vector_icons/offline_pin.icon
@@ -0,0 +1,26 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 12, 2,
+CUBIC_TO, 6.5f, 2, 2, 6.5f, 2, 12,
+R_CUBIC_TO, 0, 5.5f, 4.5f, 10, 10, 10,
+R_CUBIC_TO, 5.5f, 0, 10, -4.5f, 10, -10,
+R_CUBIC_TO, 0, -5.5f, -4.5f, -10, -10, -10,
+CLOSE,
+R_MOVE_TO, 5, 16,
+H_LINE_TO, 7,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, 10,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, -6.7f, -4,
+LINE_TO, 7, 10.7f,
+R_LINE_TO, 1.4f, -1.4f,
+R_LINE_TO, 1.9f, 1.9f,
+R_LINE_TO, 5.3f, -5.3f,
+LINE_TO, 17, 7.3f,
+LINE_TO, 10.3f, 14,
+CLOSE,
+END
diff --git a/chromium/components/tracing/BUILD.gn b/chromium/components/tracing/BUILD.gn
index 8b0bd09b7ac..7ec5f96f96c 100644
--- a/chromium/components/tracing/BUILD.gn
+++ b/chromium/components/tracing/BUILD.gn
@@ -35,6 +35,7 @@ component("tracing") {
deps = [
"//base",
"//ipc",
+ "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
]
public_deps = [
@@ -43,6 +44,7 @@ component("tracing") {
if (is_nacl) {
sources -= [ "common/process_metrics_memory_dump_provider.cc" ]
+ deps -= [ "//services/resource_coordinator/public/cpp:resource_coordinator_cpp" ]
}
}
@@ -92,7 +94,6 @@ source_set("unit_tests") {
sources = [
"child/child_trace_message_filter_unittest.cc",
"common/graphics_memory_dump_provider_android_unittest.cc",
- "common/process_metrics_memory_dump_provider_unittest.cc",
"core/proto_utils_unittest.cc",
"core/proto_zero_message_unittest.cc",
"core/scattered_stream_writer_unittest.cc",
@@ -121,6 +122,7 @@ 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/common/DEPS b/chromium/components/tracing/common/DEPS
new file mode 100644
index 00000000000..75450912357
--- /dev/null
+++ b/chromium/components/tracing/common/DEPS
@@ -0,0 +1,7 @@
+specific_include_rules = {
+ # TODO(hjd): crbug.com/728199 Remove once service knows which
+ # processes to collect OS dumps.
+ 'process_metrics_memory_dump_provider\.cc': [
+ "+services/resource_coordinator/public/cpp/memory_instrumentation",
+ ],
+}
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 5416b2ced24..ab003526dcc 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
@@ -25,10 +25,10 @@ TEST(GraphicsMemoryDumpProviderTest, ParseResponse) {
std::string json;
mad->attributes_for_testing()->AppendAsTraceFormat(&json);
ASSERT_EQ(
- "{\"memtrack_pss\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
- "\"22\"},"
- "\"memtrack_total\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
- "\"c\"}}",
+ "{\"memtrack_total\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
+ "\"c\"},"
+ "\"memtrack_pss\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
+ "\"22\"}}",
json);
// Check the "gl" row.
@@ -37,10 +37,10 @@ TEST(GraphicsMemoryDumpProviderTest, ParseResponse) {
json = "";
mad->attributes_for_testing()->AppendAsTraceFormat(&json);
ASSERT_EQ(
- "{\"memtrack_pss\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
- "\"4e\"},"
- "\"memtrack_total\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
- "\"38\"}}",
+ "{\"memtrack_total\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
+ "\"38\"},"
+ "\"memtrack_pss\":{\"type\":\"scalar\",\"units\":\"bytes\",\"value\":"
+ "\"4e\"}}",
json);
// Test for truncated input.
diff --git a/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc b/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc
index 829f47e76e6..023fb5dc8ac 100644
--- a/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc
+++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc
@@ -4,760 +4,26 @@
#include "components/tracing/common/process_metrics_memory_dump_provider.h"
-#include <fcntl.h>
-#include <stdint.h>
-
-#include <map>
-
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/format_macros.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/process/process_metrics.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/process_memory_maps.h"
-#include "base/trace_event/process_memory_totals.h"
-#include "build/build_config.h"
-
-#if defined(OS_MACOSX)
-#include <libproc.h>
-#include <mach/mach.h>
-#include <mach/mach_vm.h>
-#include <mach/shared_region.h>
-#include <sys/param.h>
-
-#include <mach-o/dyld_images.h>
-#include <mach-o/loader.h>
-#include <mach/mach.h>
-
-#include "base/numerics/safe_math.h"
-#include "base/process/process_metrics.h"
-#endif // defined(OS_MACOSX)
-
-#if defined(OS_WIN)
-#include <psapi.h>
-#include <tchar.h>
-#include <windows.h>
-
-#include <base/strings/sys_string_conversions.h>
-#include <base/win/win_util.h>
-#endif // defined(OS_WIN)
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.h"
namespace tracing {
-namespace {
-
-base::LazyInstance<
- std::map<base::ProcessId,
- std::unique_ptr<ProcessMetricsMemoryDumpProvider>>>::Leaky
- g_dump_providers_map = LAZY_INSTANCE_INITIALIZER;
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-const char kClearPeakRssCommand[] = "5";
-
-const uint32_t kMaxLineSize = 4096;
-
-bool ParseSmapsHeader(const char* header_line,
- base::trace_event::ProcessMemoryMaps::VMRegion* region) {
- // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n"
- bool res = true; // Whether this region should be appended or skipped.
- uint64_t end_addr = 0;
- char protection_flags[5] = {0};
- char mapped_file[kMaxLineSize];
-
- if (sscanf(header_line, "%" SCNx64 "-%" SCNx64 " %4c %*s %*s %*s%4095[^\n]\n",
- &region->start_address, &end_addr, protection_flags,
- mapped_file) != 4)
- return false;
-
- if (end_addr > region->start_address) {
- region->size_in_bytes = end_addr - region->start_address;
- } else {
- // This is not just paranoia, it can actually happen (See crbug.com/461237).
- region->size_in_bytes = 0;
- res = false;
- }
-
- region->protection_flags = 0;
- if (protection_flags[0] == 'r') {
- region->protection_flags |=
- base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
- }
- if (protection_flags[1] == 'w') {
- region->protection_flags |=
- base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
- }
- if (protection_flags[2] == 'x') {
- region->protection_flags |=
- base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
- }
- if (protection_flags[3] == 's') {
- region->protection_flags |=
- base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsMayshare;
- }
-
- region->mapped_file = mapped_file;
- base::TrimWhitespaceASCII(region->mapped_file, base::TRIM_ALL,
- &region->mapped_file);
-
- return res;
-}
-
-uint64_t ReadCounterBytes(char* counter_line) {
- uint64_t counter_value = 0;
- int res = sscanf(counter_line, "%*s %" SCNu64 " kB", &counter_value);
- return res == 1 ? counter_value * 1024 : 0;
-}
-
-uint32_t ParseSmapsCounter(
- char* counter_line,
- base::trace_event::ProcessMemoryMaps::VMRegion* region) {
- // A smaps counter lines looks as follows: "RSS: 0 Kb\n"
- uint32_t res = 1;
- char counter_name[20];
- int did_read = sscanf(counter_line, "%19[^\n ]", counter_name);
- if (did_read != 1)
- return 0;
-
- if (strcmp(counter_name, "Pss:") == 0) {
- region->byte_stats_proportional_resident = ReadCounterBytes(counter_line);
- } else if (strcmp(counter_name, "Private_Dirty:") == 0) {
- region->byte_stats_private_dirty_resident = ReadCounterBytes(counter_line);
- } else if (strcmp(counter_name, "Private_Clean:") == 0) {
- region->byte_stats_private_clean_resident = ReadCounterBytes(counter_line);
- } else if (strcmp(counter_name, "Shared_Dirty:") == 0) {
- region->byte_stats_shared_dirty_resident = ReadCounterBytes(counter_line);
- } else if (strcmp(counter_name, "Shared_Clean:") == 0) {
- region->byte_stats_shared_clean_resident = ReadCounterBytes(counter_line);
- } else if (strcmp(counter_name, "Swap:") == 0) {
- region->byte_stats_swapped = ReadCounterBytes(counter_line);
- } else {
- res = 0;
- }
-
- return res;
-}
-
-uint32_t ReadLinuxProcSmapsFile(FILE* smaps_file,
- base::trace_event::ProcessMemoryMaps* pmm) {
- if (!smaps_file)
- return 0;
-
- fseek(smaps_file, 0, SEEK_SET);
-
- char line[kMaxLineSize];
- const uint32_t kNumExpectedCountersPerRegion = 6;
- uint32_t counters_parsed_for_current_region = 0;
- uint32_t num_valid_regions = 0;
- base::trace_event::ProcessMemoryMaps::VMRegion region;
- bool should_add_current_region = false;
- for (;;) {
- line[0] = '\0';
- if (fgets(line, kMaxLineSize, smaps_file) == nullptr || !strlen(line))
- break;
- if (isxdigit(line[0]) && !isupper(line[0])) {
- region = base::trace_event::ProcessMemoryMaps::VMRegion();
- counters_parsed_for_current_region = 0;
- should_add_current_region = ParseSmapsHeader(line, &region);
- } else {
- counters_parsed_for_current_region += ParseSmapsCounter(line, &region);
- DCHECK_LE(counters_parsed_for_current_region,
- kNumExpectedCountersPerRegion);
- if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
- if (should_add_current_region) {
- pmm->AddVMRegion(region);
- ++num_valid_regions;
- should_add_current_region = false;
- }
- }
- }
- }
- return num_valid_regions;
-}
-
-bool GetResidentAndSharedPagesFromStatmFile(int fd,
- uint64_t* resident_pages,
- uint64_t* shared_pages) {
- lseek(fd, 0, SEEK_SET);
- char line[kMaxLineSize];
- int res = read(fd, line, kMaxLineSize - 1);
- if (res <= 0)
- return false;
- line[res] = '\0';
- int num_scanned =
- sscanf(line, "%*s %" SCNu64 " %" SCNu64, resident_pages, shared_pages);
- return num_scanned == 2;
-}
-
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
- base::ProcessId process) {
- if (process == base::kNullProcessId)
- return base::ProcessMetrics::CreateCurrentProcessMetrics();
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- // Just pass ProcessId instead of handle since they are the same in linux and
- // android.
- return base::ProcessMetrics::CreateProcessMetrics(process);
-#else
- // Creating process metrics for child processes in mac or windows requires
- // additional information like ProcessHandle or port provider.
- NOTREACHED();
- return std::unique_ptr<base::ProcessMetrics>();
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-}
-
-} // namespace
-
-// static
-uint64_t ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 0;
-
-// static
-ProcessMetricsMemoryDumpProvider::FactoryFunction
- ProcessMetricsMemoryDumpProvider::factory_for_testing = nullptr;
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-
-// static
-FILE* ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = nullptr;
-
-// static
-int ProcessMetricsMemoryDumpProvider::fast_polling_statm_fd_for_testing = -1;
-
-bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- uint32_t res = 0;
- if (proc_smaps_for_testing) {
- res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps());
- } else {
- std::string file_name = "/proc/" + (process_ == base::kNullProcessId
- ? "self"
- : base::IntToString(process_)) +
- "/smaps";
- base::ScopedFILE smaps_file(fopen(file_name.c_str(), "r"));
- res = ReadLinuxProcSmapsFile(smaps_file.get(), pmd->process_mmaps());
- }
-
- if (res)
- pmd->set_has_process_mmaps();
- return res;
-}
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-#if defined(OS_WIN)
-bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- std::vector<HMODULE> modules;
- if (!base::win::GetLoadedModulesSnapshot(::GetCurrentProcess(), &modules))
- return false;
-
- // Query the base address for each module, and attach it to the dump.
- for (size_t i = 0; i < modules.size(); ++i) {
- wchar_t module_name[MAX_PATH];
- if (!::GetModuleFileName(modules[i], module_name, MAX_PATH))
- continue;
-
- MODULEINFO module_info;
- if (!::GetModuleInformation(::GetCurrentProcess(), modules[i],
- &module_info, sizeof(MODULEINFO))) {
- continue;
- }
- base::trace_event::ProcessMemoryMaps::VMRegion region;
- region.size_in_bytes = module_info.SizeOfImage;
- region.mapped_file = base::SysWideToNativeMB(module_name);
- region.start_address = reinterpret_cast<uint64_t>(module_info.lpBaseOfDll);
- pmd->process_mmaps()->AddVMRegion(region);
- }
- if (!pmd->process_mmaps()->vm_regions().empty())
- pmd->set_has_process_mmaps();
- return true;
-}
-#endif // defined(OS_WIN)
-
-#if defined(OS_MACOSX)
-
-namespace {
-
-using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion;
-
-bool IsAddressInSharedRegion(uint64_t address) {
- return address >= SHARED_REGION_BASE_X86_64 &&
- address < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64);
-}
-
-bool IsRegionContainedInRegion(const VMRegion& containee,
- const VMRegion& container) {
- uint64_t containee_end_address =
- containee.start_address + containee.size_in_bytes;
- uint64_t container_end_address =
- container.start_address + container.size_in_bytes;
- return containee.start_address >= container.start_address &&
- containee_end_address <= container_end_address;
-}
-
-bool DoRegionsIntersect(const VMRegion& a, const VMRegion& b) {
- uint64_t a_end_address = a.start_address + a.size_in_bytes;
- uint64_t b_end_address = b.start_address + b.size_in_bytes;
- return a.start_address < b_end_address && b.start_address < a_end_address;
-}
-
-// Creates VMRegions for all dyld images. Returns whether the operation
-// succeeded.
-bool GetDyldRegions(std::vector<VMRegion>* regions) {
- task_dyld_info_data_t dyld_info;
- mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
- kern_return_t kr =
- task_info(mach_task_self(), TASK_DYLD_INFO,
- reinterpret_cast<task_info_t>(&dyld_info), &count);
- if (kr != KERN_SUCCESS)
- return false;
-
- const struct dyld_all_image_infos* all_image_infos =
- reinterpret_cast<const struct dyld_all_image_infos*>(
- dyld_info.all_image_info_addr);
-
- bool emitted_linkedit_from_dyld_shared_cache = false;
- for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) {
- const char* image_name = all_image_infos->infoArray[i].imageFilePath;
-
- // The public definition for dyld_all_image_infos/dyld_image_info is wrong
- // for 64-bit platforms. We explicitly cast to struct mach_header_64 even
- // though the public definition claims that this is a struct mach_header.
- const struct mach_header_64* const header =
- reinterpret_cast<const struct mach_header_64* const>(
- all_image_infos->infoArray[i].imageLoadAddress);
-
- uint64_t next_command = reinterpret_cast<uint64_t>(header + 1);
- uint64_t command_end = next_command + header->sizeofcmds;
- uint64_t slide = 0;
- for (unsigned int j = 0; j < header->ncmds; ++j) {
- // Ensure that next_command doesn't run past header->sizeofcmds.
- if (next_command + sizeof(struct load_command) > command_end)
- return false;
- const struct load_command* load_cmd =
- reinterpret_cast<const struct load_command*>(next_command);
- next_command += load_cmd->cmdsize;
-
- if (load_cmd->cmd == LC_SEGMENT_64) {
- if (load_cmd->cmdsize < sizeof(segment_command_64))
- return false;
- const segment_command_64* seg =
- reinterpret_cast<const segment_command_64*>(load_cmd);
- if (strcmp(seg->segname, SEG_PAGEZERO) == 0)
- continue;
- if (strcmp(seg->segname, SEG_TEXT) == 0) {
- slide = reinterpret_cast<uint64_t>(header) - seg->vmaddr;
- }
-
- // Avoid emitting LINKEDIT regions in the dyld shared cache, since they
- // all overlap.
- if (IsAddressInSharedRegion(seg->vmaddr) &&
- strcmp(seg->segname, SEG_LINKEDIT) == 0) {
- if (emitted_linkedit_from_dyld_shared_cache) {
- continue;
- } else {
- emitted_linkedit_from_dyld_shared_cache = true;
- image_name = "dyld shared cache combined __LINKEDIT";
- }
- }
-
- uint32_t protection_flags = 0;
- if (seg->initprot & VM_PROT_READ)
- protection_flags |= VMRegion::kProtectionFlagsRead;
- if (seg->initprot & VM_PROT_WRITE)
- protection_flags |= VMRegion::kProtectionFlagsWrite;
- if (seg->initprot & VM_PROT_EXECUTE)
- protection_flags |= VMRegion::kProtectionFlagsExec;
-
- VMRegion region;
- region.size_in_bytes = seg->vmsize;
- region.protection_flags = protection_flags;
- region.mapped_file = image_name;
- region.start_address = slide + seg->vmaddr;
-
- // We intentionally avoid setting any page information, which is not
- // available from dyld. The fields will be populated later.
- regions->push_back(region);
- }
- }
- }
- return true;
-}
-
-void PopulateByteStats(VMRegion* region,
- const vm_region_top_info_data_t& info) {
- uint64_t dirty_bytes =
- (info.private_pages_resident + info.shared_pages_resident) * PAGE_SIZE;
- switch (info.share_mode) {
- case SM_LARGE_PAGE:
- case SM_PRIVATE:
- case SM_COW:
- region->byte_stats_private_dirty_resident = dirty_bytes;
- case SM_SHARED:
- case SM_PRIVATE_ALIASED:
- case SM_TRUESHARED:
- case SM_SHARED_ALIASED:
- region->byte_stats_shared_dirty_resident = dirty_bytes;
- break;
- case SM_EMPTY:
- break;
- default:
- NOTREACHED();
- break;
- }
-}
-
-// Creates VMRegions using mach vm syscalls. Returns whether the operation
-// succeeded.
-bool GetAllRegions(std::vector<VMRegion>* regions) {
- const int pid = getpid();
- task_t task = mach_task_self();
- mach_vm_size_t size = 0;
- mach_vm_address_t address = MACH_VM_MIN_ADDRESS;
- while (true) {
- base::CheckedNumeric<mach_vm_address_t> next_address(address);
- next_address += size;
- if (!next_address.IsValid())
- return false;
- address = next_address.ValueOrDie();
- mach_vm_address_t address_copy = address;
-
- vm_region_top_info_data_t info;
- base::MachVMRegionResult result =
- base::GetTopInfo(task, &size, &address, &info);
- if (result == base::MachVMRegionResult::Error)
- return false;
- if (result == base::MachVMRegionResult::Finished)
- break;
-
- vm_region_basic_info_64 basic_info;
- mach_vm_size_t dummy_size = 0;
- result = base::GetBasicInfo(task, &dummy_size, &address_copy, &basic_info);
- if (result == base::MachVMRegionResult::Error)
- return false;
- if (result == base::MachVMRegionResult::Finished)
- break;
-
- VMRegion region;
- PopulateByteStats(&region, info);
-
- if (basic_info.protection & VM_PROT_READ)
- region.protection_flags |= VMRegion::kProtectionFlagsRead;
- if (basic_info.protection & VM_PROT_WRITE)
- region.protection_flags |= VMRegion::kProtectionFlagsWrite;
- if (basic_info.protection & VM_PROT_EXECUTE)
- region.protection_flags |= VMRegion::kProtectionFlagsExec;
-
- char buffer[MAXPATHLEN];
- int length = proc_regionfilename(pid, address, buffer, MAXPATHLEN);
- if (length != 0)
- region.mapped_file.assign(buffer, length);
-
- // There's no way to get swapped or clean bytes without doing a
- // very expensive syscalls that crawls every single page in the memory
- // object.
- region.start_address = address;
- region.size_in_bytes = size;
- regions->push_back(region);
- }
- return true;
-}
-
-void AddRegionByteStats(VMRegion* dest, const VMRegion& source) {
- dest->byte_stats_private_dirty_resident +=
- source.byte_stats_private_dirty_resident;
- dest->byte_stats_private_clean_resident +=
- source.byte_stats_private_clean_resident;
- dest->byte_stats_shared_dirty_resident +=
- source.byte_stats_shared_dirty_resident;
- dest->byte_stats_shared_clean_resident +=
- source.byte_stats_shared_clean_resident;
- dest->byte_stats_swapped += source.byte_stats_swapped;
- dest->byte_stats_proportional_resident +=
- source.byte_stats_proportional_resident;
-}
-
-} // namespace
-
-bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion;
-
- std::vector<VMRegion> dyld_regions;
- if (!GetDyldRegions(&dyld_regions))
- return false;
- std::vector<VMRegion> all_regions;
- if (!GetAllRegions(&all_regions))
- return false;
-
- // Merge information from dyld regions and all regions.
- for (const VMRegion& region : all_regions) {
- bool skip = false;
- const bool in_shared_region = IsAddressInSharedRegion(region.start_address);
- for (VMRegion& dyld_region : dyld_regions) {
- // If this region is fully contained in a dyld region, then add the bytes
- // stats.
- if (IsRegionContainedInRegion(region, dyld_region)) {
- AddRegionByteStats(&dyld_region, region);
- skip = true;
- break;
- }
-
- // Check to see if the region is likely used for the dyld shared cache.
- if (in_shared_region) {
- // This region is likely used for the dyld shared cache. Don't record
- // any byte stats since:
- // 1. It's not possible to figure out which dyld regions the byte
- // stats correspond to.
- // 2. The region is likely shared by non-Chrome processes, so there's
- // no point in charging the pages towards Chrome.
- if (DoRegionsIntersect(region, dyld_region)) {
- skip = true;
- break;
- }
- }
- }
- if (skip)
- continue;
- pmd->process_mmaps()->AddVMRegion(region);
- }
-
- for (VMRegion& region : dyld_regions) {
- pmd->process_mmaps()->AddVMRegion(region);
- }
-
- pmd->set_has_process_mmaps();
- return true;
-}
-#endif // defined(OS_MACOSX)
-
+// TODO(hjd): crbug.com/728199 Remove once service knows which
+// processes to collect OS dumps from.
// static
void ProcessMetricsMemoryDumpProvider::RegisterForProcess(
base::ProcessId process) {
- std::unique_ptr<ProcessMetricsMemoryDumpProvider> owned_provider;
- if (factory_for_testing) {
- owned_provider = factory_for_testing(process);
- } else {
- owned_provider = std::unique_ptr<ProcessMetricsMemoryDumpProvider>(
- new ProcessMetricsMemoryDumpProvider(process));
- }
-
- ProcessMetricsMemoryDumpProvider* provider = owned_provider.get();
- bool did_insert =
- g_dump_providers_map.Get()
- .insert(std::make_pair(process, std::move(owned_provider)))
- .second;
- if (!did_insert) {
- DLOG(ERROR) << "ProcessMetricsMemoryDumpProvider already registered for "
- << (process == base::kNullProcessId
- ? "current process"
- : "process id " + base::IntToString(process));
- return;
- }
- base::trace_event::MemoryDumpProvider::Options options;
- options.target_pid = process;
- options.is_fast_polling_supported = true;
- base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
- provider, "ProcessMemoryMetrics", nullptr, options);
+ memory_instrumentation::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
+ process);
}
+// TODO(hjd): crbug.com/728199 Remove once service knows which
+// processes to collect OS dumps from.
// static
void ProcessMetricsMemoryDumpProvider::UnregisterForProcess(
base::ProcessId process) {
- auto iter = g_dump_providers_map.Get().find(process);
- if (iter == g_dump_providers_map.Get().end())
- return;
- base::trace_event::MemoryDumpManager::GetInstance()
- ->UnregisterAndDeleteDumpProviderSoon(std::move(iter->second));
- g_dump_providers_map.Get().erase(iter);
-}
-
-ProcessMetricsMemoryDumpProvider::ProcessMetricsMemoryDumpProvider(
- base::ProcessId process)
- : process_(process),
- process_metrics_(CreateProcessMetrics(process)),
- is_rss_peak_resettable_(true) {}
-
-ProcessMetricsMemoryDumpProvider::~ProcessMetricsMemoryDumpProvider() {}
-
-// Called at trace dump point time. Creates a snapshot of the memory maps for
-// the current process.
-bool ProcessMetricsMemoryDumpProvider::OnMemoryDump(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- bool res = DumpProcessTotals(args, pmd);
-
- if (args.level_of_detail ==
- base::trace_event::MemoryDumpLevelOfDetail::DETAILED)
- res &= DumpProcessMemoryMaps(args, pmd);
- return res;
-}
-
-bool ProcessMetricsMemoryDumpProvider::DumpProcessTotals(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
-#if defined(OS_MACOSX)
- size_t private_bytes;
- size_t shared_bytes;
- size_t resident_bytes;
- size_t locked_bytes;
- if (!process_metrics_->GetMemoryBytes(&private_bytes, &shared_bytes,
- &resident_bytes, &locked_bytes)) {
- return false;
- }
- uint64_t rss_bytes = resident_bytes;
- pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
- pmd->process_totals()->SetExtraFieldInBytes("shared_bytes", shared_bytes);
- pmd->process_totals()->SetExtraFieldInBytes("locked_bytes", locked_bytes);
-
- base::trace_event::ProcessMemoryTotals::PlatformPrivateFootprint footprint;
- base::ProcessMetrics::TaskVMInfo info = process_metrics_->GetTaskVMInfo();
- footprint.phys_footprint_bytes = info.phys_footprint;
- footprint.internal_bytes = info.internal;
- footprint.compressed_bytes = info.compressed;
-
- pmd->process_totals()->SetPlatformPrivateFootprint(footprint);
-#else
- uint64_t rss_bytes = process_metrics_->GetWorkingSetSize();
-#endif // defined(OS_MACOSX)
- if (rss_bytes_for_testing)
- rss_bytes = rss_bytes_for_testing;
-
- // rss_bytes will be 0 if the process ended while dumping.
- if (!rss_bytes)
- return false;
-
- uint64_t peak_rss_bytes = 0;
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- base::trace_event::ProcessMemoryTotals::PlatformPrivateFootprint footprint;
-
- base::ScopedFD autoclose;
- int statm_fd = fast_polling_statm_fd_.get();
- if (statm_fd == -1) {
- autoclose = OpenStatm();
- statm_fd = autoclose.get();
- }
- if (statm_fd == -1)
- return false;
- const static size_t page_size = base::GetPageSize();
- uint64_t resident_pages;
- uint64_t shared_pages;
- bool success = GetResidentAndSharedPagesFromStatmFile(
- statm_fd, &resident_pages, &shared_pages);
- if (!success)
- return false;
-
- footprint.rss_anon_bytes = (resident_pages - shared_pages) * page_size;
- footprint.vm_swap_bytes = process_metrics_->GetVmSwapBytes();
- pmd->process_totals()->SetPlatformPrivateFootprint(footprint);
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-#if defined(OS_WIN)
- {
- size_t private_bytes;
- base::trace_event::ProcessMemoryTotals::PlatformPrivateFootprint footprint;
- process_metrics_->GetMemoryBytes(&private_bytes, nullptr);
- footprint.private_bytes = private_bytes;
- pmd->process_totals()->SetPlatformPrivateFootprint(footprint);
- }
-#endif
-
-#if !defined(OS_IOS)
- peak_rss_bytes = process_metrics_->GetPeakWorkingSetSize();
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- if (is_rss_peak_resettable_) {
- std::string clear_refs_file =
- "/proc/" +
- (process_ == base::kNullProcessId ? "self"
- : base::IntToString(process_)) +
- "/clear_refs";
- int clear_refs_fd = open(clear_refs_file.c_str(), O_WRONLY);
- if (clear_refs_fd > 0 &&
- base::WriteFileDescriptor(clear_refs_fd, kClearPeakRssCommand,
- sizeof(kClearPeakRssCommand))) {
- pmd->process_totals()->set_is_peak_rss_resetable(true);
- } else {
- is_rss_peak_resettable_ = false;
- }
- close(clear_refs_fd);
- }
-#elif defined(OS_WIN)
- if (args.level_of_detail ==
- base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {
- uint64_t pss_bytes = 0;
- bool res = process_metrics_->GetProportionalSetSizeBytes(&pss_bytes);
- if (res) {
- base::trace_event::ProcessMemoryMaps::VMRegion region;
- region.byte_stats_proportional_resident = pss_bytes;
- pmd->process_mmaps()->AddVMRegion(region);
- pmd->set_has_process_mmaps();
- }
- }
-
-#endif
-#endif // !defined(OS_IOS)
-
- pmd->process_totals()->set_resident_set_bytes(rss_bytes);
- pmd->set_has_process_totals();
- pmd->process_totals()->set_peak_resident_set_bytes(peak_rss_bytes);
-
- // Returns true even if other metrics failed, since rss is reported.
- return true;
-}
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-base::ScopedFD ProcessMetricsMemoryDumpProvider::OpenStatm() {
- std::string name =
- "/proc/" +
- (process_ == base::kNullProcessId ? "self"
- : base::IntToString(process_)) +
- "/statm";
- base::ScopedFD fd = base::ScopedFD(open(name.c_str(), O_RDONLY));
- DCHECK(fd.is_valid());
- return fd;
-}
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-void ProcessMetricsMemoryDumpProvider::PollFastMemoryTotal(
- uint64_t* memory_total) {
- *memory_total = 0;
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-
- int statm_fd = fast_polling_statm_fd_for_testing;
- if (statm_fd == -1) {
- if (!fast_polling_statm_fd_.is_valid())
- fast_polling_statm_fd_ = OpenStatm();
- statm_fd = fast_polling_statm_fd_.get();
- if (statm_fd == -1)
- return;
- }
-
- uint64_t resident_pages = 0;
- uint64_t ignored_shared_pages = 0;
- if (!GetResidentAndSharedPagesFromStatmFile(statm_fd, &resident_pages,
- &ignored_shared_pages))
- return;
-
- static size_t page_size = base::GetPageSize();
- *memory_total = resident_pages * page_size;
-#else
- *memory_total = process_metrics_->GetWorkingSetSize();
-#endif
-}
-
-void ProcessMetricsMemoryDumpProvider::SuspendFastMemoryPolling() {
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- fast_polling_statm_fd_.reset();
-#endif
+ memory_instrumentation::ProcessMetricsMemoryDumpProvider::
+ UnregisterForProcess(process);
}
} // namespace tracing
diff --git a/chromium/components/tracing/common/process_metrics_memory_dump_provider.h b/chromium/components/tracing/common/process_metrics_memory_dump_provider.h
index d3c53344907..eb89e6297b3 100644
--- a/chromium/components/tracing/common/process_metrics_memory_dump_provider.h
+++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider.h
@@ -5,20 +5,12 @@
#ifndef COMPONENTS_TRACING_COMMON_PROCESS_MEMORY_METRICS_DUMP_PROVIDER_H_
#define COMPONENTS_TRACING_COMMON_PROCESS_MEMORY_METRICS_DUMP_PROVIDER_H_
-#include <memory>
-
-#include "base/files/scoped_file.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
#include "base/process/process_handle.h"
#include "base/trace_event/memory_dump_provider.h"
-#include "build/build_config.h"
#include "components/tracing/tracing_export.h"
-namespace base {
-class ProcessMetrics;
-}
-
+// TODO(hjd): The actual impl lives in service/resource_coordinator. Update the
+// callsites and remove this file.
namespace tracing {
// Dump provider which collects process-wide memory stats.
@@ -29,64 +21,10 @@ class TRACING_EXPORT ProcessMetricsMemoryDumpProvider
static void RegisterForProcess(base::ProcessId process);
static void UnregisterForProcess(base::ProcessId process);
- ~ProcessMetricsMemoryDumpProvider() override;
-
- // MemoryDumpProvider implementation.
- bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) override;
- void PollFastMemoryTotal(uint64_t* memory_total) override;
- void SuspendFastMemoryPolling() override;
-
- protected:
- ProcessMetricsMemoryDumpProvider(base::ProcessId process);
private:
- using FactoryFunction =
- std::unique_ptr<ProcessMetricsMemoryDumpProvider> (*)(base::ProcessId);
-
- FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
- ParseProcSmaps);
- FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, DumpRSS);
- FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
- TestPollFastMemoryTotal);
-#if defined(OS_MACOSX)
- FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
- TestMachOReading);
- FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
- NoDuplicateRegions);
-#elif defined(OS_WIN)
- FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
- TestWinModuleReading);
-#elif defined(OS_LINUX) || defined(OS_ANDROID)
- FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
- DoubleRegister);
-#endif
-
- bool DumpProcessTotals(const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd);
- bool DumpProcessMemoryMaps(const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd);
-
- static uint64_t rss_bytes_for_testing;
- static FactoryFunction factory_for_testing;
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- static FILE* proc_smaps_for_testing;
- static int fast_polling_statm_fd_for_testing;
-
- base::ScopedFD fast_polling_statm_fd_;
-
- base::ScopedFD OpenStatm();
-#endif
-
- base::ProcessId process_;
- std::unique_ptr<base::ProcessMetrics> process_metrics_;
-
- // The peak may not be resettable on all the processes if the linux kernel is
- // older than http://bit.ly/reset_rss or only on child processes if yama LSM
- // sandbox is enabled.
- bool is_rss_peak_resettable_;
-
+ ProcessMetricsMemoryDumpProvider();
+ ~ProcessMetricsMemoryDumpProvider() override;
DISALLOW_COPY_AND_ASSIGN(ProcessMetricsMemoryDumpProvider);
};
diff --git a/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc b/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc
deleted file mode 100644
index b28d3f2aad6..00000000000
--- a/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc
+++ /dev/null
@@ -1,414 +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/tracing/common/process_metrics_memory_dump_provider.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <unordered_set>
-
-#include "base/files/file_util.h"
-#include "base/memory/ptr_util.h"
-#include "base/process/process_metrics.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/process_memory_maps.h"
-#include "base/trace_event/process_memory_totals.h"
-#include "base/trace_event/trace_event_argument.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_MACOSX)
-#include <libgen.h>
-#include <mach-o/dyld.h>
-#endif
-
-#if defined(OS_WIN)
-#include <base/strings/sys_string_conversions.h>
-#endif
-
-namespace tracing {
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-namespace {
-const char kTestSmaps1[] =
- "00400000-004be000 r-xp 00000000 fc:01 1234 /file/1\n"
- "Size: 760 kB\n"
- "Rss: 296 kB\n"
- "Pss: 162 kB\n"
- "Shared_Clean: 228 kB\n"
- "Shared_Dirty: 0 kB\n"
- "Private_Clean: 0 kB\n"
- "Private_Dirty: 68 kB\n"
- "Referenced: 296 kB\n"
- "Anonymous: 68 kB\n"
- "AnonHugePages: 0 kB\n"
- "Swap: 4 kB\n"
- "KernelPageSize: 4 kB\n"
- "MMUPageSize: 4 kB\n"
- "Locked: 0 kB\n"
- "VmFlags: rd ex mr mw me dw sd\n"
- "ff000000-ff800000 -w-p 00001080 fc:01 0 /file/name with space\n"
- "Size: 0 kB\n"
- "Rss: 192 kB\n"
- "Pss: 128 kB\n"
- "Shared_Clean: 120 kB\n"
- "Shared_Dirty: 4 kB\n"
- "Private_Clean: 60 kB\n"
- "Private_Dirty: 8 kB\n"
- "Referenced: 296 kB\n"
- "Anonymous: 0 kB\n"
- "AnonHugePages: 0 kB\n"
- "Swap: 0 kB\n"
- "KernelPageSize: 4 kB\n"
- "MMUPageSize: 4 kB\n"
- "Locked: 0 kB\n"
- "VmFlags: rd ex mr mw me dw sd";
-
-const char kTestSmaps2[] =
- // An invalid region, with zero size and overlapping with the last one
- // (See crbug.com/461237).
- "7fe7ce79c000-7fe7ce79c000 ---p 00000000 00:00 0 \n"
- "Size: 4 kB\n"
- "Rss: 0 kB\n"
- "Pss: 0 kB\n"
- "Shared_Clean: 0 kB\n"
- "Shared_Dirty: 0 kB\n"
- "Private_Clean: 0 kB\n"
- "Private_Dirty: 0 kB\n"
- "Referenced: 0 kB\n"
- "Anonymous: 0 kB\n"
- "AnonHugePages: 0 kB\n"
- "Swap: 0 kB\n"
- "KernelPageSize: 4 kB\n"
- "MMUPageSize: 4 kB\n"
- "Locked: 0 kB\n"
- "VmFlags: rd ex mr mw me dw sd\n"
- // A invalid region with its range going backwards.
- "00400000-00200000 ---p 00000000 00:00 0 \n"
- "Size: 4 kB\n"
- "Rss: 0 kB\n"
- "Pss: 0 kB\n"
- "Shared_Clean: 0 kB\n"
- "Shared_Dirty: 0 kB\n"
- "Private_Clean: 0 kB\n"
- "Private_Dirty: 0 kB\n"
- "Referenced: 0 kB\n"
- "Anonymous: 0 kB\n"
- "AnonHugePages: 0 kB\n"
- "Swap: 0 kB\n"
- "KernelPageSize: 4 kB\n"
- "MMUPageSize: 4 kB\n"
- "Locked: 0 kB\n"
- "VmFlags: rd ex mr mw me dw sd\n"
- // A good anonymous region at the end.
- "7fe7ce79c000-7fe7ce7a8000 ---p 00000000 00:00 0 \n"
- "Size: 48 kB\n"
- "Rss: 40 kB\n"
- "Pss: 32 kB\n"
- "Shared_Clean: 16 kB\n"
- "Shared_Dirty: 12 kB\n"
- "Private_Clean: 8 kB\n"
- "Private_Dirty: 4 kB\n"
- "Referenced: 40 kB\n"
- "Anonymous: 16 kB\n"
- "AnonHugePages: 0 kB\n"
- "Swap: 0 kB\n"
- "KernelPageSize: 4 kB\n"
- "MMUPageSize: 4 kB\n"
- "Locked: 0 kB\n"
- "VmFlags: rd wr mr mw me ac sd\n";
-
-const char kTestStatm[] = "200 100 20 2 3 4";
-
-void CreateTempFileWithContents(const char* contents, base::ScopedFILE* file) {
- base::FilePath temp_path;
- FILE* temp_file = CreateAndOpenTemporaryFile(&temp_path);
- file->reset(temp_file);
- ASSERT_TRUE(temp_file);
-
- ASSERT_TRUE(
- base::WriteFileDescriptor(fileno(temp_file), contents, strlen(contents)));
-}
-
-} // namespace
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-class MockMemoryDumpProvider : public ProcessMetricsMemoryDumpProvider {
- public:
- MockMemoryDumpProvider(base::ProcessId process);
- ~MockMemoryDumpProvider() override;
-};
-
-std::unordered_set<MockMemoryDumpProvider*> g_live_mocks;
-std::unordered_set<MockMemoryDumpProvider*> g_dead_mocks;
-
-MockMemoryDumpProvider::MockMemoryDumpProvider(base::ProcessId process)
- : ProcessMetricsMemoryDumpProvider(process) {
- g_live_mocks.insert(this);
-}
-
-MockMemoryDumpProvider::~MockMemoryDumpProvider() {
- g_live_mocks.erase(this);
- g_dead_mocks.insert(this);
-}
-
-TEST(ProcessMetricsMemoryDumpProviderTest, DumpRSS) {
- const base::trace_event::MemoryDumpArgs high_detail_args = {
- base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
- std::unique_ptr<ProcessMetricsMemoryDumpProvider> pmtdp(
- new ProcessMetricsMemoryDumpProvider(base::kNullProcessId));
- std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd_before(
- new base::trace_event::ProcessMemoryDump(nullptr, high_detail_args));
- std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd_after(
- new base::trace_event::ProcessMemoryDump(nullptr, high_detail_args));
-
- ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 1024;
- pmtdp->OnMemoryDump(high_detail_args, pmd_before.get());
-
- // Pretend that the RSS of the process increased of +1M.
- const size_t kAllocSize = 1048576;
- ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing += kAllocSize;
-
- pmtdp->OnMemoryDump(high_detail_args, pmd_after.get());
-
- ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 0;
-
- ASSERT_TRUE(pmd_before->has_process_totals());
- ASSERT_TRUE(pmd_after->has_process_totals());
-
- const uint64_t rss_before =
- pmd_before->process_totals()->resident_set_bytes();
- const uint64_t rss_after = pmd_after->process_totals()->resident_set_bytes();
-
- EXPECT_NE(0U, rss_before);
- EXPECT_NE(0U, rss_after);
-
- EXPECT_EQ(rss_after - rss_before, kAllocSize);
-}
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-TEST(ProcessMetricsMemoryDumpProviderTest, ParseProcSmaps) {
- const uint32_t kProtR =
- base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
- const uint32_t kProtW =
- base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
- const uint32_t kProtX =
- base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
- const base::trace_event::MemoryDumpArgs dump_args = {
- base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
-
- std::unique_ptr<ProcessMetricsMemoryDumpProvider> pmmdp(
- new ProcessMetricsMemoryDumpProvider(base::kNullProcessId));
-
- // Emulate an empty /proc/self/smaps.
- base::trace_event::ProcessMemoryDump pmd_invalid(nullptr /* session_state */,
- dump_args);
- base::ScopedFILE empty_file(OpenFile(base::FilePath("/dev/null"), "r"));
- ASSERT_TRUE(empty_file.get());
- ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = empty_file.get();
- pmmdp->OnMemoryDump(dump_args, &pmd_invalid);
- ASSERT_FALSE(pmd_invalid.has_process_mmaps());
-
- // Parse the 1st smaps file.
- base::trace_event::ProcessMemoryDump pmd_1(nullptr /* session_state */,
- dump_args);
- base::ScopedFILE temp_file1;
- CreateTempFileWithContents(kTestSmaps1, &temp_file1);
- ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = temp_file1.get();
- pmmdp->OnMemoryDump(dump_args, &pmd_1);
- ASSERT_TRUE(pmd_1.has_process_mmaps());
- const auto& regions_1 = pmd_1.process_mmaps()->vm_regions();
- ASSERT_EQ(2UL, regions_1.size());
-
- EXPECT_EQ(0x00400000UL, regions_1[0].start_address);
- EXPECT_EQ(0x004be000UL - 0x00400000UL, regions_1[0].size_in_bytes);
- EXPECT_EQ(kProtR | kProtX, regions_1[0].protection_flags);
- EXPECT_EQ("/file/1", regions_1[0].mapped_file);
- EXPECT_EQ(162 * 1024UL, regions_1[0].byte_stats_proportional_resident);
- EXPECT_EQ(228 * 1024UL, regions_1[0].byte_stats_shared_clean_resident);
- EXPECT_EQ(0UL, regions_1[0].byte_stats_shared_dirty_resident);
- EXPECT_EQ(0UL, regions_1[0].byte_stats_private_clean_resident);
- EXPECT_EQ(68 * 1024UL, regions_1[0].byte_stats_private_dirty_resident);
- EXPECT_EQ(4 * 1024UL, regions_1[0].byte_stats_swapped);
-
- EXPECT_EQ(0xff000000UL, regions_1[1].start_address);
- EXPECT_EQ(0xff800000UL - 0xff000000UL, regions_1[1].size_in_bytes);
- EXPECT_EQ(kProtW, regions_1[1].protection_flags);
- EXPECT_EQ("/file/name with space", regions_1[1].mapped_file);
- EXPECT_EQ(128 * 1024UL, regions_1[1].byte_stats_proportional_resident);
- EXPECT_EQ(120 * 1024UL, regions_1[1].byte_stats_shared_clean_resident);
- EXPECT_EQ(4 * 1024UL, regions_1[1].byte_stats_shared_dirty_resident);
- EXPECT_EQ(60 * 1024UL, regions_1[1].byte_stats_private_clean_resident);
- EXPECT_EQ(8 * 1024UL, regions_1[1].byte_stats_private_dirty_resident);
- EXPECT_EQ(0 * 1024UL, regions_1[1].byte_stats_swapped);
-
- // Parse the 2nd smaps file.
- base::trace_event::ProcessMemoryDump pmd_2(nullptr /* session_state */,
- dump_args);
- base::ScopedFILE temp_file2;
- CreateTempFileWithContents(kTestSmaps2, &temp_file2);
- ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = temp_file2.get();
- pmmdp->OnMemoryDump(dump_args, &pmd_2);
- ASSERT_TRUE(pmd_2.has_process_mmaps());
- const auto& regions_2 = pmd_2.process_mmaps()->vm_regions();
- ASSERT_EQ(1UL, regions_2.size());
- EXPECT_EQ(0x7fe7ce79c000UL, regions_2[0].start_address);
- EXPECT_EQ(0x7fe7ce7a8000UL - 0x7fe7ce79c000UL, regions_2[0].size_in_bytes);
- EXPECT_EQ(0U, regions_2[0].protection_flags);
- EXPECT_EQ("", regions_2[0].mapped_file);
- EXPECT_EQ(32 * 1024UL, regions_2[0].byte_stats_proportional_resident);
- EXPECT_EQ(16 * 1024UL, regions_2[0].byte_stats_shared_clean_resident);
- EXPECT_EQ(12 * 1024UL, regions_2[0].byte_stats_shared_dirty_resident);
- EXPECT_EQ(8 * 1024UL, regions_2[0].byte_stats_private_clean_resident);
- EXPECT_EQ(4 * 1024UL, regions_2[0].byte_stats_private_dirty_resident);
- EXPECT_EQ(0 * 1024UL, regions_2[0].byte_stats_swapped);
-}
-
-TEST(ProcessMetricsMemoryDumpProviderTest, DoubleRegister) {
- auto factory = [](base::ProcessId process) {
- return std::unique_ptr<ProcessMetricsMemoryDumpProvider>(
- new MockMemoryDumpProvider(process));
- };
- ProcessMetricsMemoryDumpProvider::factory_for_testing = factory;
- ProcessMetricsMemoryDumpProvider::RegisterForProcess(1);
- ProcessMetricsMemoryDumpProvider::RegisterForProcess(1);
- ASSERT_EQ(1u, g_live_mocks.size());
- ASSERT_EQ(1u, g_dead_mocks.size());
- auto* manager = base::trace_event::MemoryDumpManager::GetInstance();
- MockMemoryDumpProvider* live_mock = *g_live_mocks.begin();
- EXPECT_TRUE(manager->IsDumpProviderRegisteredForTesting(live_mock));
- auto* dead_mock = *g_dead_mocks.begin();
- EXPECT_FALSE(manager->IsDumpProviderRegisteredForTesting(dead_mock));
- ProcessMetricsMemoryDumpProvider::UnregisterForProcess(1);
- g_live_mocks.clear();
- g_dead_mocks.clear();
-}
-
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-TEST(ProcessMetricsMemoryDumpProviderTest, TestPollFastMemoryTotal) {
- ProcessMetricsMemoryDumpProvider mdp(base::kNullProcessId);
-
- uint64_t total1, total2;
- mdp.PollFastMemoryTotal(&total1);
- ASSERT_GT(total1, 0u);
- size_t kBufSize = 16 * 1024 * 1024;
- auto buf = base::MakeUnique<char[]>(kBufSize);
- for (size_t i = 0; i < kBufSize; i++)
- buf[i] = *((volatile char*)&buf[i]) + 1;
- mdp.PollFastMemoryTotal(&total2);
- ASSERT_GT(total2, 0u);
- EXPECT_GT(total2, total1 + kBufSize / 2);
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- EXPECT_GE(mdp.fast_polling_statm_fd_.get(), 0);
-
- base::ScopedFILE temp_file;
- CreateTempFileWithContents(kTestStatm, &temp_file);
- mdp.fast_polling_statm_fd_for_testing = fileno(temp_file.get());
- size_t page_size = base::GetPageSize();
- uint64_t value;
- mdp.PollFastMemoryTotal(&value);
- EXPECT_EQ(100 * page_size, value);
-
- mdp.SuspendFastMemoryPolling();
- EXPECT_FALSE(mdp.fast_polling_statm_fd_.is_valid());
-#else
- mdp.SuspendFastMemoryPolling();
-#endif
-}
-
-#if defined(OS_WIN)
-
-void DummyFunction() {}
-
-TEST(ProcessMetricsMemoryDumpProviderTest, TestWinModuleReading) {
- using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion;
-
- ProcessMetricsMemoryDumpProvider mdp(base::kNullProcessId);
- base::trace_event::MemoryDumpArgs args;
- base::trace_event::ProcessMemoryDump dump(nullptr, args);
- ASSERT_TRUE(mdp.DumpProcessMemoryMaps(args, &dump));
- ASSERT_TRUE(dump.has_process_mmaps());
-
- wchar_t module_name[MAX_PATH];
- DWORD result = GetModuleFileName(nullptr, module_name, MAX_PATH);
- ASSERT_TRUE(result);
- std::string executable_name = base::SysWideToNativeMB(module_name);
-
- HMODULE module_containing_dummy = nullptr;
- uintptr_t dummy_function_address =
- reinterpret_cast<uintptr_t>(&DummyFunction);
- result = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
- reinterpret_cast<LPCWSTR>(dummy_function_address),
- &module_containing_dummy);
- ASSERT_TRUE(result);
- result = GetModuleFileName(nullptr, module_name, MAX_PATH);
- ASSERT_TRUE(result);
- std::string module_containing_dummy_name =
- base::SysWideToNativeMB(module_name);
-
- bool found_executable = false;
- bool found_region_with_dummy = false;
- for (const VMRegion& region : dump.process_mmaps()->vm_regions()) {
- EXPECT_NE(0u, region.start_address);
- EXPECT_NE(0u, region.size_in_bytes);
-
- if (region.mapped_file.find(executable_name) != std::string::npos)
- found_executable = true;
-
- if (dummy_function_address >= region.start_address &&
- dummy_function_address < region.start_address + region.size_in_bytes) {
- found_region_with_dummy = true;
- EXPECT_EQ(module_containing_dummy_name, region.mapped_file);
- }
- }
- EXPECT_TRUE(found_executable);
- EXPECT_TRUE(found_region_with_dummy);
-}
-#endif
-
-#if defined(OS_MACOSX)
-TEST(ProcessMetricsMemoryDumpProviderTest, TestMachOReading) {
- using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion;
- ProcessMetricsMemoryDumpProvider mdp(base::kNullProcessId);
- base::trace_event::MemoryDumpArgs args;
- base::trace_event::ProcessMemoryDump dump(nullptr, args);
- ASSERT_TRUE(mdp.DumpProcessMemoryMaps(args, &dump));
- ASSERT_TRUE(dump.has_process_mmaps());
- uint32_t size = 100;
- char full_path[size];
- int result = _NSGetExecutablePath(full_path, &size);
- ASSERT_EQ(0, result);
- std::string name = basename(full_path);
-
- uint64_t components_unittests_resident_pages = 0;
- bool found_appkit = false;
- for (const VMRegion& region : dump.process_mmaps()->vm_regions()) {
- EXPECT_NE(0u, region.start_address);
- EXPECT_NE(0u, region.size_in_bytes);
-
- EXPECT_LT(region.size_in_bytes, 1ull << 32);
- uint32_t required_protection_flags =
- VMRegion::kProtectionFlagsRead | VMRegion::kProtectionFlagsExec;
- if (region.mapped_file.find(name) != std::string::npos &&
- region.protection_flags == required_protection_flags) {
- components_unittests_resident_pages +=
- region.byte_stats_private_dirty_resident +
- region.byte_stats_shared_dirty_resident +
- region.byte_stats_private_clean_resident +
- region.byte_stats_shared_clean_resident;
- }
-
- if (region.mapped_file.find("AppKit") != std::string::npos) {
- found_appkit = true;
- }
- }
- EXPECT_GT(components_unittests_resident_pages, 0u);
- EXPECT_TRUE(found_appkit);
-}
-
-#endif // defined(OS_MACOSX)
-} // namespace tracing
diff --git a/chromium/components/tracing/common/trace_config_file_unittest.cc b/chromium/components/tracing/common/trace_config_file_unittest.cc
index 2c7df54d2e1..471d676212c 100644
--- a/chromium/components/tracing/common/trace_config_file_unittest.cc
+++ b/chromium/components/tracing/common/trace_config_file_unittest.cc
@@ -16,16 +16,15 @@ namespace tracing {
namespace {
const char kTraceConfig[] =
- "{"
+ "{"
"\"enable_argument_filter\":true,"
"\"enable_systrace\":true,"
"\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
"\"included_categories\":[\"included\","
- "\"inc_pattern*\","
- "\"disabled-by-default-cc\"],"
- "\"record_mode\":\"record-continuously\","
- "\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]"
- "}";
+ "\"inc_pattern*\","
+ "\"disabled-by-default-cc\"],"
+ "\"record_mode\":\"record-continuously\""
+ "}";
std::string GetTraceConfigFileContent(std::string trace_config,
std::string startup_duration,
diff --git a/chromium/components/tracing/common/tracing_messages.h b/chromium/components/tracing/common/tracing_messages.h
index 3b7d7258e69..e1d82d6541c 100644
--- a/chromium/components/tracing/common/tracing_messages.h
+++ b/chromium/components/tracing/common/tracing_messages.h
@@ -11,7 +11,6 @@
#include "base/metrics/histogram.h"
#include "base/sync_socket.h"
-#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/trace_event_impl.h"
#include "components/tracing/tracing_export.h"
#include "ipc/ipc_channel_handle.h"
@@ -28,23 +27,6 @@ IPC_STRUCT_TRAITS_MEMBER(event_capacity)
IPC_STRUCT_TRAITS_MEMBER(event_count)
IPC_STRUCT_TRAITS_END()
-IPC_STRUCT_TRAITS_BEGIN(base::trace_event::MemoryDumpRequestArgs)
-IPC_STRUCT_TRAITS_MEMBER(dump_guid)
-IPC_STRUCT_TRAITS_MEMBER(dump_type)
-IPC_STRUCT_TRAITS_MEMBER(level_of_detail)
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(base::trace_event::MemoryDumpArgs)
- IPC_STRUCT_TRAITS_MEMBER(level_of_detail)
-IPC_STRUCT_TRAITS_END()
-
-IPC_ENUM_TRAITS_MAX_VALUE(base::trace_event::MemoryDumpLevelOfDetail,
- base::trace_event::MemoryDumpLevelOfDetail::LAST)
-
-IPC_ENUM_TRAITS_MAX_VALUE(
- base::trace_event::MemoryDumpType,
- static_cast<int>(base::trace_event::MemoryDumpType::LAST))
-
// Sent to all child processes to enable trace event recording.
IPC_MESSAGE_CONTROL3(TracingMsg_BeginTracing,
std::string /* trace_config_str */,
@@ -60,16 +42,6 @@ IPC_MESSAGE_CONTROL0(TracingMsg_CancelTracing)
// Sent to all child processes to get trace buffer fullness.
IPC_MESSAGE_CONTROL0(TracingMsg_GetTraceLogStatus)
-// Sent to all child processes to request a local (current process) memory dump.
-IPC_MESSAGE_CONTROL1(TracingMsg_ProcessMemoryDumpRequest,
- base::trace_event::MemoryDumpRequestArgs)
-
-// Reply to TracingHostMsg_GlobalMemoryDumpRequest, sent by the browser process.
-// This is to get the result of a global dump initiated by a child process.
-IPC_MESSAGE_CONTROL2(TracingMsg_GlobalMemoryDumpResponse,
- uint64_t /* dump_guid */,
- bool /* success */)
-
IPC_MESSAGE_CONTROL4(TracingMsg_SetUMACallback,
std::string /* histogram_name */,
base::HistogramBase::Sample /* histogram_lower_value */,
@@ -95,14 +67,6 @@ IPC_MESSAGE_CONTROL1(
TracingHostMsg_TraceLogStatusReply,
base::trace_event::TraceLogStatus /*status of the trace log*/)
-// Sent to the browser to initiate a global memory dump from a child process.
-IPC_MESSAGE_CONTROL1(TracingHostMsg_GlobalMemoryDumpRequest,
- base::trace_event::MemoryDumpRequestArgs)
-
-// Reply to TracingMsg_ProcessMemoryDumpRequest.
-IPC_MESSAGE_CONTROL2(TracingHostMsg_ProcessMemoryDumpResponse,
- uint64_t /* dump_guid */,
- bool /* success */)
IPC_MESSAGE_CONTROL1(TracingHostMsg_TriggerBackgroundTrace,
std::string /* name */)
diff --git a/chromium/components/tracing/core/proto_zero_message.h b/chromium/components/tracing/core/proto_zero_message.h
index 2e6bf4a1c3e..ac005ec3bd7 100644
--- a/chromium/components/tracing/core/proto_zero_message.h
+++ b/chromium/components/tracing/core/proto_zero_message.h
@@ -199,7 +199,7 @@ class TRACING_EXPORT ProtoZeroMessage {
// the arena are meaningful only for the root message. The static_assert in
// the .cc file guarantees that the sizeof(nested_messages_arena_) is enough
// to contain up to kMaxNestingDepth messages.
- ALIGNAS(sizeof(void*)) uint8_t nested_messages_arena_[512];
+ alignas(sizeof(void*)) uint8_t nested_messages_arena_[512];
// DO NOT add any fields below |nested_messages_arena_|. The memory layout of
// nested messages would overflow the storage allocated by the root message.
diff --git a/chromium/components/translate/content/renderer/BUILD.gn b/chromium/components/translate/content/renderer/BUILD.gn
index 17ab7fa3fcc..773039fa02e 100644
--- a/chromium/components/translate/content/renderer/BUILD.gn
+++ b/chromium/components/translate/content/renderer/BUILD.gn
@@ -24,3 +24,15 @@ static_library("renderer") {
"//v8",
]
}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "translate_helper_unittest.cc",
+ ]
+ deps = [
+ ":renderer",
+ "//base",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/translate/core/browser/BUILD.gn b/chromium/components/translate/core/browser/BUILD.gn
index 45e0c2b0fe9..293234062d8 100644
--- a/chromium/components/translate/core/browser/BUILD.gn
+++ b/chromium/components/translate/core/browser/BUILD.gn
@@ -6,15 +6,9 @@ import("//build/config/ui.gni")
static_library("browser") {
sources = [
- "language_model.cc",
- "language_model.h",
"language_state.cc",
"language_state.h",
"page_translated_details.h",
- "ranker_model.cc",
- "ranker_model.h",
- "ranker_model_loader.cc",
- "ranker_model_loader.h",
"translate_accept_languages.cc",
"translate_accept_languages.h",
"translate_browser_metrics.cc",
@@ -56,13 +50,14 @@ static_library("browser") {
"//components/data_use_measurement/core",
"//components/keyed_service/core",
"//components/language_usage_metrics",
+ "//components/machine_intelligence",
+ "//components/machine_intelligence/proto",
"//components/metrics",
"//components/metrics/proto",
"//components/pref_registry",
"//components/prefs",
"//components/resources:components_resources",
"//components/strings",
- "//components/translate/core/browser/proto",
"//components/translate/core/common",
"//components/ukm",
"//components/variations",
@@ -92,32 +87,35 @@ static_library("browser") {
source_set("unit_tests") {
testonly = true
sources = [
- "language_model_unittest.cc",
"language_state_unittest.cc",
+ "mock_translate_client.cc",
+ "mock_translate_client.h",
"mock_translate_driver.cc",
"mock_translate_driver.h",
"mock_translate_ranker.cc",
"mock_translate_ranker.h",
- "ranker_model_loader_unittest.cc",
- "ranker_model_unittest.cc",
+ "translate_accept_languages_unittest.cc",
"translate_browser_metrics_unittest.cc",
+ "translate_experiment_unittest.cc",
"translate_language_list_unittest.cc",
"translate_manager_unittest.cc",
"translate_prefs_unittest.cc",
"translate_ranker_impl_unittest.cc",
"translate_script_unittest.cc",
"translate_ui_delegate_unittest.cc",
+ "translate_url_util_unittest.cc",
]
deps = [
":browser",
"//base",
"//components/infobars/core",
+ "//components/machine_intelligence",
+ "//components/machine_intelligence/proto",
"//components/metrics/proto",
"//components/pref_registry:pref_registry",
"//components/prefs",
"//components/prefs:test_support",
"//components/sync_preferences:test_support",
- "//components/translate/core/browser/proto",
"//components/translate/core/common",
"//components/ukm",
"//components/ukm:test_support",
diff --git a/chromium/components/translate/core/common/BUILD.gn b/chromium/components/translate/core/common/BUILD.gn
index f8038c92d81..11cffc6770d 100644
--- a/chromium/components/translate/core/common/BUILD.gn
+++ b/chromium/components/translate/core/common/BUILD.gn
@@ -6,6 +6,8 @@ static_library("common") {
sources = [
"language_detection_details.cc",
"language_detection_details.h",
+ "language_detection_logging_helper.cc",
+ "language_detection_logging_helper.h",
"translate_constants.cc",
"translate_constants.h",
"translate_errors.h",
@@ -15,10 +17,14 @@ static_library("common") {
"translate_switches.h",
"translate_util.cc",
"translate_util.h",
+ "translation_logging_helper.cc",
+ "translation_logging_helper.h",
]
deps = [
"//base",
+ "//components/metrics/proto",
+ "//components/sync/protocol",
"//url",
]
}
@@ -26,12 +32,16 @@ static_library("common") {
source_set("unit_tests") {
testonly = true
sources = [
+ "language_detection_logging_helper_unittest.cc",
"translate_metrics_unittest.cc",
"translate_util_unittest.cc",
+ "translation_logging_helper_unittest.cc",
]
deps = [
":common",
"//base",
+ "//components/metrics/proto",
+ "//components/sync/protocol",
"//testing/gtest",
"//url",
]
diff --git a/chromium/components/translate/ios/browser/BUILD.gn b/chromium/components/translate/ios/browser/BUILD.gn
index 3ef1f8d203d..ea58e5374cb 100644
--- a/chromium/components/translate/ios/browser/BUILD.gn
+++ b/chromium/components/translate/ios/browser/BUILD.gn
@@ -24,6 +24,7 @@ source_set("browser") {
deps = [
":injected_js",
"//base",
+ "//components/language/core/browser",
"//components/prefs",
"//components/translate/core/browser",
"//components/translate/core/common",
@@ -58,7 +59,8 @@ source_set("unit_tests") {
"//components/prefs:test_support",
"//components/resources",
"//components/translate/core/browser",
- "//ios/web:test_support",
+ "//ios/web/public/test",
+ "//ios/web/public/test/fakes",
"//testing/gtest",
"//third_party/ocmock",
"//ui/base",
diff --git a/chromium/components/typemaps.gni b/chromium/components/typemaps.gni
index 57cb150542d..f19028a6bb0 100644
--- a/chromium/components/typemaps.gni
+++ b/chromium/components/typemaps.gni
@@ -4,9 +4,11 @@
typemaps = [
"//components/autofill/content/common/autofill_types.typemap",
+ "//components/chrome_cleaner/public/typemaps/chrome_prompt.typemap",
"//components/content_settings/core/common/content_settings.typemap",
"//components/nacl/common/nacl.typemap",
"//components/password_manager/content/common/credential_manager.typemap",
"//components/signin/public/interfaces/account_id.typemap",
+ "//components/spellcheck/common/spellcheck.typemap",
"//components/translate/content/common/translate.typemap",
]
diff --git a/chromium/components/ui_devtools/BUILD.gn b/chromium/components/ui_devtools/BUILD.gn
index eab5c09cbf9..bb1d83b3bf2 100644
--- a/chromium/components/ui_devtools/BUILD.gn
+++ b/chromium/components/ui_devtools/BUILD.gn
@@ -52,7 +52,7 @@ inspector_protocol_generate("protocol_generated_sources") {
outputs = _protocol_generated
}
-source_set("ui_devtools") {
+component("ui_devtools") {
sources = rebase_path(_protocol_generated, ".", target_gen_dir)
sources += [
"devtools_base_agent.h",
diff --git a/chromium/components/ui_devtools/README.md b/chromium/components/ui_devtools/README.md
new file mode 100644
index 00000000000..71c9bbb1e86
--- /dev/null
+++ b/chromium/components/ui_devtools/README.md
@@ -0,0 +1,3 @@
+# Inspecting Chrome Native UI with DevTools.
+
+https://www.chromium.org/developers/how-tos/inspecting-ash
diff --git a/chromium/components/ui_devtools/devtools_base_agent.h b/chromium/components/ui_devtools/devtools_base_agent.h
index 0b66d825436..da1c6fe374f 100644
--- a/chromium/components/ui_devtools/devtools_base_agent.h
+++ b/chromium/components/ui_devtools/devtools_base_agent.h
@@ -7,8 +7,7 @@
#include "components/ui_devtools/Protocol.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
class UiDevToolsAgent {
public:
@@ -39,12 +38,12 @@ class UiDevToolsBaseAgent : public UiDevToolsAgent,
// Common methods between all generated Backends, subclasses may
// choose to override them (but not necessary).
- ui::devtools::protocol::Response enable() override {
- return ui::devtools::protocol::Response::OK();
+ ui_devtools::protocol::Response enable() override {
+ return ui_devtools::protocol::Response::OK();
};
- ui::devtools::protocol::Response disable() override {
- return ui::devtools::protocol::Response::OK();
+ ui_devtools::protocol::Response disable() override {
+ return ui_devtools::protocol::Response::OK();
};
protected:
@@ -59,7 +58,6 @@ class UiDevToolsBaseAgent : public UiDevToolsAgent,
DISALLOW_COPY_AND_ASSIGN(UiDevToolsBaseAgent);
};
-} // namespace devtools
-} // namespace ui
+} // namespace ui_devtools
#endif // COMPONENTS_UI_DEVTOOLS_DEVTOOLS_BASE_AGENT_H_
diff --git a/chromium/components/ui_devtools/devtools_client.cc b/chromium/components/ui_devtools/devtools_client.cc
index 4c2a1fc0e7d..6640fa91fd1 100644
--- a/chromium/components/ui_devtools/devtools_client.cc
+++ b/chromium/components/ui_devtools/devtools_client.cc
@@ -6,8 +6,7 @@
#include "components/ui_devtools/devtools_server.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
UiDevToolsClient::UiDevToolsClient(const std::string& name,
UiDevToolsServer* server)
@@ -68,5 +67,4 @@ void UiDevToolsClient::flushProtocolNotifications() {
NOTIMPLEMENTED();
}
-} // namespace devtools
-} // namespace ui
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/devtools_client.h b/chromium/components/ui_devtools/devtools_client.h
index 18125561549..b9dfe88e481 100644
--- a/chromium/components/ui_devtools/devtools_client.h
+++ b/chromium/components/ui_devtools/devtools_client.h
@@ -11,9 +11,9 @@
#include "components/ui_devtools/Forward.h"
#include "components/ui_devtools/Protocol.h"
#include "components/ui_devtools/devtools_base_agent.h"
+#include "components/ui_devtools/devtools_export.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
class UiDevToolsServer;
@@ -21,7 +21,7 @@ class UiDevToolsServer;
// this class and attach the corresponding backends/frontends (i.e: DOM, CSS,
// etc). This client is then attached to the UiDevToolsServer and all messages
// from this client are sent over the web socket owned by the server.
-class UiDevToolsClient : public protocol::FrontendChannel {
+class UI_DEVTOOLS_EXPORT UiDevToolsClient : public protocol::FrontendChannel {
public:
static const int kNotConnected = -1;
@@ -57,7 +57,6 @@ class UiDevToolsClient : public protocol::FrontendChannel {
DISALLOW_COPY_AND_ASSIGN(UiDevToolsClient);
};
-} // namespace devtools
-} // namespace ui
+} // namespace ui_devtools
#endif // COMPONENTS_UI_DEVTOOLS_DEVTOOLS_CLIENT_H_
diff --git a/chromium/components/ui_devtools/devtools_server.cc b/chromium/components/ui_devtools/devtools_server.cc
index 2bab50aafc4..a901f937e98 100644
--- a/chromium/components/ui_devtools/devtools_server.cc
+++ b/chromium/components/ui_devtools/devtools_server.cc
@@ -21,8 +21,7 @@
#include "net/socket/server_socket.h"
#include "net/socket/tcp_server_socket.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
namespace {
const char kChromeDeveloperToolsPrefix[] =
@@ -34,6 +33,7 @@ bool IsUiDevToolsEnabled() {
int GetUiDevToolsPort() {
DCHECK(IsUiDevToolsEnabled());
+ // This value is duplicated in the chrome://flags description.
constexpr int kDefaultPort = 9223;
int port;
if (!base::StringToInt(
@@ -65,6 +65,10 @@ UiDevToolsServer::UiDevToolsServer(
}
UiDevToolsServer::~UiDevToolsServer() {
+ if (io_thread_task_runner_)
+ io_thread_task_runner_->DeleteSoon(FROM_HERE, server_.release());
+ if (thread_ && thread_->IsRunning())
+ thread_->Stop();
devtools_server_ = nullptr;
}
@@ -181,5 +185,4 @@ void UiDevToolsServer::OnClose(int connection_id) {
connections_.erase(it);
}
-} // namespace devtools
-} // namespace ui
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/devtools_server.h b/chromium/components/ui_devtools/devtools_server.h
index 04483cc899c..f5bce624581 100644
--- a/chromium/components/ui_devtools/devtools_server.h
+++ b/chromium/components/ui_devtools/devtools_server.h
@@ -14,13 +14,14 @@
#include "components/ui_devtools/Forward.h"
#include "components/ui_devtools/Protocol.h"
#include "components/ui_devtools/devtools_client.h"
+#include "components/ui_devtools/devtools_export.h"
#include "components/ui_devtools/string_util.h"
#include "net/server/http_server.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
-class UiDevToolsServer : public net::HttpServer::Delegate {
+class UI_DEVTOOLS_EXPORT UiDevToolsServer
+ : public NON_EXPORTED_BASE(net::HttpServer::Delegate) {
public:
~UiDevToolsServer() override;
@@ -68,7 +69,6 @@ class UiDevToolsServer : public net::HttpServer::Delegate {
DISALLOW_COPY_AND_ASSIGN(UiDevToolsServer);
};
-} // namespace devtools
-} // namespace ui
+} // namespace ui_devtools
#endif // COMPONENTS_UI_DEVTOOLS_DEVTOOLS_SERVER_H_
diff --git a/chromium/components/ui_devtools/inspector_protocol_config.json b/chromium/components/ui_devtools/inspector_protocol_config.json
index 9ad3815a2b9..76292154de1 100644
--- a/chromium/components/ui_devtools/inspector_protocol_config.json
+++ b/chromium/components/ui_devtools/inspector_protocol_config.json
@@ -3,7 +3,7 @@
"path": "protocol.json",
"package": "components/ui_devtools",
"output": "",
- "namespace": ["ui", "devtools", "protocol"],
+ "namespace": ["ui_devtools", "protocol"],
"export_macro": "UI_DEVTOOLS_EXPORT",
"export_header": "components/ui_devtools/devtools_export.h"
},
diff --git a/chromium/components/ui_devtools/string_util.cc b/chromium/components/ui_devtools/string_util.cc
index 0b936a38cf2..ade4b6f3152 100644
--- a/chromium/components/ui_devtools/string_util.cc
+++ b/chromium/components/ui_devtools/string_util.cc
@@ -7,8 +7,7 @@
#include "base/strings/string_util.h"
#include "components/ui_devtools/Protocol.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
namespace protocol {
// static
@@ -20,5 +19,4 @@ std::unique_ptr<Value> StringUtil::parseJSON(const String& string) {
};
} // namespace protocol
-} // namespace ws
-} // namespace ui
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/string_util.h b/chromium/components/ui_devtools/string_util.h
index d08b8b6f22f..8f12102f406 100644
--- a/chromium/components/ui_devtools/string_util.h
+++ b/chromium/components/ui_devtools/string_util.h
@@ -10,8 +10,7 @@
#include "base/json/json_reader.h"
#include "base/strings/string_number_conversions.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
using String = std::string;
@@ -28,9 +27,7 @@ class CustomStringBuilder {
void reserveCapacity(std::size_t size) { s_.reserve(size); }
void append(const String& s) { s_ += s; }
void append(char c) { s_ += c; }
- void append(const char* data, unsigned int length) {
- s_.append(data, length);
- }
+ void append(const char* data, size_t length) { s_.append(data, length); }
String toString() { return s_; }
};
@@ -76,7 +73,6 @@ class StringUtil {
};
} // namespace protocol
-} // namespace devtools
-} // namespace ui
+} // namespace ui_devtools
#endif // COMPONENTS_UI_DEVTOOLS_STRING_UTIL_H_
diff --git a/chromium/components/ui_devtools/switches.cc b/chromium/components/ui_devtools/switches.cc
index c76f5b4ae2e..b4762aa9720 100644
--- a/chromium/components/ui_devtools/switches.cc
+++ b/chromium/components/ui_devtools/switches.cc
@@ -4,12 +4,10 @@
#include "components/ui_devtools/switches.h"
-namespace ui {
-namespace devtools {
+namespace ui_devtools {
// Enables DevTools server for UI (mus, ash, etc). Value should be the port the
// server is started on. Default port is 9332.
const char kEnableUiDevTools[] = "enable-ui-devtools";
-} // namespace devtools
-} // namespace ui
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/switches.h b/chromium/components/ui_devtools/switches.h
index 8dcb6980618..4d96c9ca0b6 100644
--- a/chromium/components/ui_devtools/switches.h
+++ b/chromium/components/ui_devtools/switches.h
@@ -5,12 +5,12 @@
#ifndef COMPONENTS_UI_DEVTOOLS_SWITCHES_H_
#define COMPONENTS_UI_DEVTOOLS_SWITCHES_H_
-namespace ui {
-namespace devtools {
+#include "components/ui_devtools/devtools_export.h"
-extern const char kEnableUiDevTools[];
+namespace ui_devtools {
-} // namespace devtools
-} // namespace ui
+extern UI_DEVTOOLS_EXPORT const char kEnableUiDevTools[];
+
+} // namespace ui_devtools
#endif // COMPONENTS_UI_DEVTOOLS_SWITCHES_H_
diff --git a/chromium/components/ui_devtools/views/BUILD.gn b/chromium/components/ui_devtools/views/BUILD.gn
new file mode 100644
index 00000000000..514fadfb170
--- /dev/null
+++ b/chromium/components/ui_devtools/views/BUILD.gn
@@ -0,0 +1,62 @@
+# 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.
+
+import("//build/config/ui.gni")
+
+source_set("views") {
+ cflags = []
+ if (is_win) {
+ cflags += [ "/wd4800" ] # Value forced to bool.
+ }
+
+ sources = [
+ "ui_devtools_css_agent.cc",
+ "ui_devtools_css_agent.h",
+ "ui_devtools_dom_agent.cc",
+ "ui_devtools_dom_agent.h",
+ "ui_element.cc",
+ "ui_element.h",
+ "ui_element_delegate.h",
+ "view_element.cc",
+ "view_element.h",
+ "widget_element.cc",
+ "widget_element.h",
+ "window_element.cc",
+ "window_element.h",
+ ]
+
+ deps = [
+ "//components/ui_devtools",
+ "//skia",
+ "//ui/aura",
+ "//ui/views",
+ "//ui/wm:wm",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ cflags = []
+ if (is_win) {
+ cflags += [ "/wd4800" ] # Value forced to bool.
+ }
+
+ sources = [
+ "ui_devtools_unittest.cc",
+ ]
+
+ deps = [
+ ":views",
+ "//components/ui_devtools",
+ "//skia",
+ "//testing/gtest",
+ "//ui/aura",
+ "//ui/views",
+ "//ui/views:test_support",
+ "//ui/wm:wm",
+ ]
+
+ configs += [ "//build/config:precompiled_headers" ]
+}
diff --git a/chromium/components/ui_devtools/views/DEPS b/chromium/components/ui_devtools/views/DEPS
new file mode 100644
index 00000000000..35272fdbaa4
--- /dev/null
+++ b/chromium/components/ui_devtools/views/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+third_party/skia/include/core",
+ "+ui"
+]
diff --git a/chromium/components/ui_devtools/views/OWNERS b/chromium/components/ui_devtools/views/OWNERS
new file mode 100644
index 00000000000..b8e32c10903
--- /dev/null
+++ b/chromium/components/ui_devtools/views/OWNERS
@@ -0,0 +1 @@
+sadrul@chromium.org
diff --git a/chromium/components/ui_devtools/views/ui_devtools_css_agent.cc b/chromium/components/ui_devtools/views/ui_devtools_css_agent.cc
new file mode 100644
index 00000000000..62a4be272e8
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_devtools_css_agent.cc
@@ -0,0 +1,208 @@
+// 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/ui_devtools/views/ui_devtools_css_agent.h"
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "components/ui_devtools/views/ui_element.h"
+#include "ui/aura/window.h"
+
+namespace ui_devtools {
+namespace {
+
+using namespace ui_devtools::protocol;
+
+const char kHeight[] = "height";
+const char kWidth[] = "width";
+const char kX[] = "x";
+const char kY[] = "y";
+const char kVisibility[] = "visibility";
+
+std::unique_ptr<CSS::SourceRange> BuildDefaultSourceRange() {
+ // These tell the frontend where in the stylesheet a certain style
+ // is located. Since we don't have stylesheets, this is all 0.
+ // We need this because CSS fields are not editable unless
+ // the range is provided.
+ return CSS::SourceRange::create()
+ .setStartLine(0)
+ .setEndLine(0)
+ .setStartColumn(0)
+ .setEndColumn(0)
+ .build();
+}
+
+std::unique_ptr<CSS::CSSProperty> BuildCSSProperty(const std::string& name,
+ int value) {
+ return CSS::CSSProperty::create()
+ .setRange(BuildDefaultSourceRange())
+ .setName(name)
+ .setValue(base::IntToString(value))
+ .build();
+}
+
+std::unique_ptr<Array<CSS::CSSProperty>> BuildCSSPropertyArray(
+ const gfx::Rect& bounds,
+ const bool visible) {
+ auto cssProperties = Array<CSS::CSSProperty>::create();
+ cssProperties->addItem(BuildCSSProperty(kHeight, bounds.height()));
+ cssProperties->addItem(BuildCSSProperty(kWidth, bounds.width()));
+ cssProperties->addItem(BuildCSSProperty(kX, bounds.x()));
+ cssProperties->addItem(BuildCSSProperty(kY, bounds.y()));
+ cssProperties->addItem(BuildCSSProperty(kVisibility, visible));
+ return cssProperties;
+}
+
+std::unique_ptr<CSS::CSSStyle> BuildCSSStyle(int node_id,
+ const gfx::Rect& bounds,
+ bool visible) {
+ return CSS::CSSStyle::create()
+ .setRange(BuildDefaultSourceRange())
+ .setStyleSheetId(base::IntToString(node_id))
+ .setCssProperties(BuildCSSPropertyArray(bounds, visible))
+ .setShorthandEntries(Array<std::string>::create())
+ .build();
+}
+
+ui_devtools::protocol::Response NodeNotFoundError(int node_id) {
+ return ui_devtools::protocol::Response::Error(
+ "Node with id=" + std::to_string(node_id) + " not found");
+}
+
+Response ParseProperties(const std::string& style_text,
+ gfx::Rect* bounds,
+ bool* visible) {
+ std::vector<std::string> tokens = base::SplitString(
+ style_text, ":;", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ for (size_t i = 0; i < tokens.size() - 1; i += 2) {
+ const std::string& property = tokens.at(i);
+ int value;
+ if (!base::StringToInt(tokens.at(i + 1), &value))
+ return Response::Error("Unable to parse value for property=" + property);
+
+ if (property == kHeight)
+ bounds->set_height(std::max(0, value));
+ else if (property == kWidth)
+ bounds->set_width(std::max(0, value));
+ else if (property == kX)
+ bounds->set_x(value);
+ else if (property == kY)
+ bounds->set_y(value);
+ else if (property == kVisibility)
+ *visible = std::max(0, value) == 1;
+ else
+ return Response::Error("Unsupported property=" + property);
+ }
+ return Response::OK();
+}
+
+} // namespace
+
+UIDevToolsCSSAgent::UIDevToolsCSSAgent(UIDevToolsDOMAgent* dom_agent)
+ : dom_agent_(dom_agent) {
+ DCHECK(dom_agent_);
+}
+
+UIDevToolsCSSAgent::~UIDevToolsCSSAgent() {
+ disable();
+}
+
+ui_devtools::protocol::Response UIDevToolsCSSAgent::enable() {
+ dom_agent_->AddObserver(this);
+ return ui_devtools::protocol::Response::OK();
+}
+
+ui_devtools::protocol::Response UIDevToolsCSSAgent::disable() {
+ dom_agent_->RemoveObserver(this);
+ return ui_devtools::protocol::Response::OK();
+}
+
+ui_devtools::protocol::Response UIDevToolsCSSAgent::getMatchedStylesForNode(
+ int node_id,
+ ui_devtools::protocol::Maybe<ui_devtools::protocol::CSS::CSSStyle>*
+ inline_style) {
+ *inline_style = GetStylesForNode(node_id);
+ if (!inline_style)
+ return NodeNotFoundError(node_id);
+ return ui_devtools::protocol::Response::OK();
+}
+
+ui_devtools::protocol::Response UIDevToolsCSSAgent::setStyleTexts(
+ std::unique_ptr<ui_devtools::protocol::Array<
+ ui_devtools::protocol::CSS::StyleDeclarationEdit>> edits,
+ std::unique_ptr<
+ ui_devtools::protocol::Array<ui_devtools::protocol::CSS::CSSStyle>>*
+ result) {
+ std::unique_ptr<
+ ui_devtools::protocol::Array<ui_devtools::protocol::CSS::CSSStyle>>
+ updated_styles = ui_devtools::protocol::Array<
+ ui_devtools::protocol::CSS::CSSStyle>::create();
+ for (size_t i = 0; i < edits->length(); i++) {
+ auto* edit = edits->get(i);
+ int node_id;
+ if (!base::StringToInt(edit->getStyleSheetId(), &node_id))
+ return ui_devtools::protocol::Response::Error("Invalid node id");
+
+ gfx::Rect updated_bounds;
+ bool visible = false;
+ if (!GetPropertiesForNodeId(node_id, &updated_bounds, &visible))
+ return NodeNotFoundError(node_id);
+
+ ui_devtools::protocol::Response response(
+ ParseProperties(edit->getText(), &updated_bounds, &visible));
+ if (!response.isSuccess())
+ return response;
+
+ updated_styles->addItem(BuildCSSStyle(node_id, updated_bounds, visible));
+
+ if (!SetPropertiesForNodeId(node_id, updated_bounds, visible))
+ return NodeNotFoundError(node_id);
+ }
+ *result = std::move(updated_styles);
+ return ui_devtools::protocol::Response::OK();
+}
+
+void UIDevToolsCSSAgent::OnNodeBoundsChanged(int node_id) {
+ InvalidateStyleSheet(node_id);
+}
+
+std::unique_ptr<ui_devtools::protocol::CSS::CSSStyle>
+UIDevToolsCSSAgent::GetStylesForNode(int node_id) {
+ gfx::Rect bounds;
+ bool visible = false;
+ return GetPropertiesForNodeId(node_id, &bounds, &visible)
+ ? BuildCSSStyle(node_id, bounds, visible)
+ : nullptr;
+}
+
+void UIDevToolsCSSAgent::InvalidateStyleSheet(int node_id) {
+ // The stylesheetId for each node is equivalent to its node_id (as a string).
+ frontend()->styleSheetChanged(base::IntToString(node_id));
+}
+
+bool UIDevToolsCSSAgent::GetPropertiesForNodeId(int node_id,
+ gfx::Rect* bounds,
+ bool* visible) {
+ UIElement* ui_element = dom_agent_->GetElementFromNodeId(node_id);
+ if (ui_element) {
+ ui_element->GetBounds(bounds);
+ ui_element->GetVisible(visible);
+ return true;
+ }
+ return false;
+}
+
+bool UIDevToolsCSSAgent::SetPropertiesForNodeId(int node_id,
+ const gfx::Rect& bounds,
+ bool visible) {
+ UIElement* ui_element = dom_agent_->GetElementFromNodeId(node_id);
+ if (ui_element) {
+ ui_element->SetBounds(bounds);
+ ui_element->SetVisible(visible);
+ return true;
+ }
+ return false;
+}
+
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/views/ui_devtools_css_agent.h b/chromium/components/ui_devtools/views/ui_devtools_css_agent.h
new file mode 100644
index 00000000000..ef88dccce1d
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_devtools_css_agent.h
@@ -0,0 +1,53 @@
+// 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_UI_DEVTOOLS_VIEWS_UI_DEVTOOLS_CSS_AGENT_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_UI_DEVTOOLS_CSS_AGENT_H_
+
+#include "base/macros.h"
+#include "components/ui_devtools/CSS.h"
+#include "components/ui_devtools/views/ui_devtools_dom_agent.h"
+
+namespace ui_devtools {
+
+class UIDevToolsCSSAgent : public ui_devtools::UiDevToolsBaseAgent<
+ ui_devtools::protocol::CSS::Metainfo>,
+ public UIDevToolsDOMAgentObserver {
+ public:
+ explicit UIDevToolsCSSAgent(UIDevToolsDOMAgent* dom_agent);
+ ~UIDevToolsCSSAgent() override;
+
+ // CSS::Backend:
+ ui_devtools::protocol::Response enable() override;
+ ui_devtools::protocol::Response disable() override;
+ ui_devtools::protocol::Response getMatchedStylesForNode(
+ int node_id,
+ ui_devtools::protocol::Maybe<ui_devtools::protocol::CSS::CSSStyle>*
+ inline_style) override;
+ ui_devtools::protocol::Response setStyleTexts(
+ std::unique_ptr<ui_devtools::protocol::Array<
+ ui_devtools::protocol::CSS::StyleDeclarationEdit>> edits,
+ std::unique_ptr<
+ ui_devtools::protocol::Array<ui_devtools::protocol::CSS::CSSStyle>>*
+ result) override;
+
+ // UIDevToolsDOMAgentObserver:
+ void OnNodeBoundsChanged(int node_id) override;
+
+ private:
+ std::unique_ptr<ui_devtools::protocol::CSS::CSSStyle> GetStylesForNode(
+ int node_id);
+ void InvalidateStyleSheet(int node_id);
+ bool GetPropertiesForNodeId(int node_id, gfx::Rect* bounds, bool* visible);
+ bool SetPropertiesForNodeId(int node_id,
+ const gfx::Rect& bounds,
+ bool visible);
+ UIDevToolsDOMAgent* dom_agent_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIDevToolsCSSAgent);
+};
+
+} // namespace ui_devtools
+
+#endif // COMPONENTS_UI_DEVTOOLS_VIEWS_UI_DEVTOOLS_CSS_AGENT_H_
diff --git a/chromium/components/ui_devtools/views/ui_devtools_dom_agent.cc b/chromium/components/ui_devtools/views/ui_devtools_dom_agent.cc
new file mode 100644
index 00000000000..1e7638b1b0c
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_devtools_dom_agent.cc
@@ -0,0 +1,385 @@
+// 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/ui_devtools/views/ui_devtools_dom_agent.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/ui_devtools/devtools_server.h"
+#include "components/ui_devtools/views/ui_element.h"
+#include "components/ui_devtools/views/view_element.h"
+#include "components/ui_devtools/views/widget_element.h"
+#include "components/ui_devtools/views/window_element.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+#include "ui/wm/core/window_util.h"
+
+namespace ui_devtools {
+namespace {
+
+using namespace ui_devtools::protocol;
+// TODO(mhashmi): Make ids reusable
+
+std::unique_ptr<DOM::Node> BuildNode(
+ const std::string& name,
+ std::unique_ptr<Array<std::string>> attributes,
+ std::unique_ptr<Array<DOM::Node>> children,
+ int node_ids) {
+ constexpr int kDomElementNodeType = 1;
+ std::unique_ptr<DOM::Node> node = DOM::Node::create()
+ .setNodeId(node_ids)
+ .setNodeName(name)
+ .setNodeType(kDomElementNodeType)
+ .setAttributes(std::move(attributes))
+ .build();
+ node->setChildNodeCount(static_cast<int>(children->length()));
+ node->setChildren(std::move(children));
+ return node;
+}
+
+// TODO(thanhph): Move this function to UIElement::GetAttributes().
+std::unique_ptr<Array<std::string>> GetAttributes(UIElement* ui_element) {
+ std::unique_ptr<Array<std::string>> attributes = Array<std::string>::create();
+ attributes->addItem("name");
+ switch (ui_element->type()) {
+ case UIElementType::WINDOW: {
+ aura::Window* window =
+ UIElement::GetBackingElement<aura::Window, WindowElement>(ui_element);
+ attributes->addItem(window->GetName());
+ attributes->addItem("active");
+ attributes->addItem(::wm::IsActiveWindow(window) ? "true" : "false");
+ break;
+ }
+ case UIElementType::WIDGET: {
+ views::Widget* widget =
+ UIElement::GetBackingElement<views::Widget, WidgetElement>(
+ ui_element);
+ attributes->addItem(widget->GetName());
+ attributes->addItem("active");
+ attributes->addItem(widget->IsActive() ? "true" : "false");
+ break;
+ }
+ case UIElementType::VIEW: {
+ attributes->addItem(
+ UIElement::GetBackingElement<views::View, ViewElement>(ui_element)
+ ->GetClassName());
+ break;
+ }
+ default:
+ DCHECK(false);
+ }
+ return attributes;
+}
+
+int MaskColor(int value) {
+ return value & 0xff;
+}
+
+SkColor RGBAToSkColor(DOM::RGBA* rgba) {
+ if (!rgba)
+ return SkColorSetARGB(0, 0, 0, 0);
+ // Default alpha value is 0 (not visible) and need to convert alpha decimal
+ // percentage value to hex
+ return SkColorSetARGB(MaskColor(static_cast<int>(rgba->getA(0) * 255)),
+ MaskColor(rgba->getR()), MaskColor(rgba->getG()),
+ MaskColor(rgba->getB()));
+}
+
+views::Widget* GetWidgetFromWindow(aura::Window* window) {
+ return views::Widget::GetWidgetForNativeView(window);
+}
+
+std::unique_ptr<DOM::Node> BuildDomNodeFromUIElement(UIElement* root) {
+ std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+ for (auto* it : root->children())
+ children->addItem(BuildDomNodeFromUIElement(it));
+
+ constexpr int kDomElementNodeType = 1;
+ std::unique_ptr<DOM::Node> node = DOM::Node::create()
+ .setNodeId(root->node_id())
+ .setNodeName(root->GetTypeName())
+ .setNodeType(kDomElementNodeType)
+ .setAttributes(GetAttributes(root))
+ .build();
+ node->setChildNodeCount(static_cast<int>(children->length()));
+ node->setChildren(std::move(children));
+ return node;
+}
+
+} // namespace
+
+UIDevToolsDOMAgent::UIDevToolsDOMAgent() : is_building_tree_(false) {
+ aura::Env::GetInstance()->AddObserver(this);
+}
+
+UIDevToolsDOMAgent::~UIDevToolsDOMAgent() {
+ aura::Env::GetInstance()->RemoveObserver(this);
+ Reset();
+}
+
+ui_devtools::protocol::Response UIDevToolsDOMAgent::disable() {
+ Reset();
+ return ui_devtools::protocol::Response::OK();
+}
+
+ui_devtools::protocol::Response UIDevToolsDOMAgent::getDocument(
+ std::unique_ptr<ui_devtools::protocol::DOM::Node>* out_root) {
+ *out_root = BuildInitialTree();
+ return ui_devtools::protocol::Response::OK();
+}
+
+ui_devtools::protocol::Response UIDevToolsDOMAgent::highlightNode(
+ std::unique_ptr<ui_devtools::protocol::DOM::HighlightConfig>
+ highlight_config,
+ ui_devtools::protocol::Maybe<int> node_id) {
+ return HighlightNode(std::move(highlight_config), node_id.fromJust());
+}
+
+ui_devtools::protocol::Response UIDevToolsDOMAgent::hideHighlight() {
+ if (layer_for_highlighting_ && layer_for_highlighting_->visible())
+ layer_for_highlighting_->SetVisible(false);
+ return ui_devtools::protocol::Response::OK();
+}
+
+void UIDevToolsDOMAgent::OnUIElementAdded(UIElement* parent, UIElement* child) {
+ // When parent is null, only need to update |node_id_to_ui_element_|.
+ if (!parent) {
+ node_id_to_ui_element_[child->node_id()] = child;
+ return;
+ }
+ // If tree is being built, don't add child to dom tree again.
+ if (is_building_tree_)
+ return;
+ DCHECK(node_id_to_ui_element_.count(parent->node_id()));
+
+ const auto& children = parent->children();
+ auto iter = std::find(children.begin(), children.end(), child);
+ int prev_node_id =
+ (iter == children.end() - 1) ? 0 : (*std::next(iter))->node_id();
+ frontend()->childNodeInserted(parent->node_id(), prev_node_id,
+ BuildTreeForUIElement(child));
+}
+
+void UIDevToolsDOMAgent::OnUIElementReordered(UIElement* parent,
+ UIElement* child) {
+ DCHECK(node_id_to_ui_element_.count(parent->node_id()));
+
+ const auto& children = parent->children();
+ auto iter = std::find(children.begin(), children.end(), child);
+ int prev_node_id =
+ (iter == children.begin()) ? 0 : (*std::prev(iter))->node_id();
+ RemoveDomNode(child);
+ frontend()->childNodeInserted(parent->node_id(), prev_node_id,
+ BuildDomNodeFromUIElement(child));
+}
+
+void UIDevToolsDOMAgent::OnUIElementRemoved(UIElement* ui_element) {
+ DCHECK(node_id_to_ui_element_.count(ui_element->node_id()));
+
+ RemoveDomNode(ui_element);
+ node_id_to_ui_element_.erase(ui_element->node_id());
+}
+
+void UIDevToolsDOMAgent::OnUIElementBoundsChanged(UIElement* ui_element) {
+ for (auto& observer : observers_)
+ observer.OnNodeBoundsChanged(ui_element->node_id());
+}
+
+void UIDevToolsDOMAgent::AddObserver(UIDevToolsDOMAgentObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void UIDevToolsDOMAgent::RemoveObserver(UIDevToolsDOMAgentObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+UIElement* UIDevToolsDOMAgent::GetElementFromNodeId(int node_id) {
+ return node_id_to_ui_element_[node_id];
+}
+
+void UIDevToolsDOMAgent::OnHostInitialized(aura::WindowTreeHost* host) {
+ root_windows_.push_back(host->window());
+}
+
+void UIDevToolsDOMAgent::OnNodeBoundsChanged(int node_id) {
+ for (auto& observer : observers_)
+ observer.OnNodeBoundsChanged(node_id);
+}
+
+std::unique_ptr<ui_devtools::protocol::DOM::Node>
+UIDevToolsDOMAgent::BuildInitialTree() {
+ is_building_tree_ = true;
+ std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+
+ // TODO(thanhph): Root of UIElement tree shoudn't be WindowElement
+ // but maybe a new different element type.
+ window_element_root_ =
+ base::MakeUnique<WindowElement>(nullptr, this, nullptr);
+
+ for (aura::Window* window : root_windows()) {
+ UIElement* window_element =
+ new WindowElement(window, this, window_element_root_.get());
+
+ children->addItem(BuildTreeForUIElement(window_element));
+ window_element_root_->AddChild(window_element);
+ }
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root_node = BuildNode(
+ "root", nullptr, std::move(children), window_element_root_->node_id());
+ is_building_tree_ = false;
+ return root_node;
+}
+
+std::unique_ptr<ui_devtools::protocol::DOM::Node>
+UIDevToolsDOMAgent::BuildTreeForUIElement(UIElement* ui_element) {
+ if (ui_element->type() == UIElementType::WINDOW) {
+ return BuildTreeForWindow(
+ ui_element,
+ UIElement::GetBackingElement<aura::Window, WindowElement>(ui_element));
+ } else if (ui_element->type() == UIElementType::WIDGET) {
+ return BuildTreeForRootWidget(
+ ui_element,
+ UIElement::GetBackingElement<views::Widget, WidgetElement>(ui_element));
+ } else if (ui_element->type() == UIElementType::VIEW) {
+ return BuildTreeForView(
+ ui_element,
+ UIElement::GetBackingElement<views::View, ViewElement>(ui_element));
+ }
+ return nullptr;
+}
+
+std::unique_ptr<DOM::Node> UIDevToolsDOMAgent::BuildTreeForWindow(
+ UIElement* window_element_root,
+ aura::Window* window) {
+ std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+ views::Widget* widget = GetWidgetFromWindow(window);
+ if (widget) {
+ UIElement* widget_element =
+ new WidgetElement(widget, this, window_element_root);
+
+ children->addItem(BuildTreeForRootWidget(widget_element, widget));
+ window_element_root->AddChild(widget_element);
+ }
+ for (aura::Window* child : window->children()) {
+ UIElement* window_element =
+ new WindowElement(child, this, window_element_root);
+
+ children->addItem(BuildTreeForWindow(window_element, child));
+ window_element_root->AddChild(window_element);
+ }
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> node =
+ BuildNode("Window", GetAttributes(window_element_root),
+ std::move(children), window_element_root->node_id());
+ return node;
+}
+
+std::unique_ptr<DOM::Node> UIDevToolsDOMAgent::BuildTreeForRootWidget(
+ UIElement* widget_element,
+ views::Widget* widget) {
+ std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+
+ UIElement* view_element =
+ new ViewElement(widget->GetRootView(), this, widget_element);
+
+ children->addItem(BuildTreeForView(view_element, widget->GetRootView()));
+ widget_element->AddChild(view_element);
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> node =
+ BuildNode("Widget", GetAttributes(widget_element), std::move(children),
+ widget_element->node_id());
+ return node;
+}
+
+std::unique_ptr<DOM::Node> UIDevToolsDOMAgent::BuildTreeForView(
+ UIElement* view_element,
+ views::View* view) {
+ std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
+
+ for (auto* child : view->GetChildrenInZOrder()) {
+ UIElement* view_element_child = new ViewElement(child, this, view_element);
+
+ children->addItem(BuildTreeForView(view_element_child, child));
+ view_element->AddChild(view_element_child);
+ }
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> node =
+ BuildNode("View", GetAttributes(view_element), std::move(children),
+ view_element->node_id());
+ return node;
+}
+
+void UIDevToolsDOMAgent::RemoveDomNode(UIElement* ui_element) {
+ for (auto* child_element : ui_element->children())
+ RemoveDomNode(child_element);
+ frontend()->childNodeRemoved(ui_element->parent()->node_id(),
+ ui_element->node_id());
+}
+
+void UIDevToolsDOMAgent::Reset() {
+ is_building_tree_ = false;
+ layer_for_highlighting_.reset();
+ window_element_root_.reset();
+ node_id_to_ui_element_.clear();
+ observers_.Clear();
+}
+
+void UIDevToolsDOMAgent::UpdateHighlight(
+ const std::pair<aura::Window*, gfx::Rect>& window_and_bounds,
+ SkColor background) {
+ layer_for_highlighting_->SetColor(background);
+
+ display::Display display =
+ display::Screen::GetScreen()->GetDisplayNearestWindow(
+ window_and_bounds.first);
+ aura::Window* root = window_and_bounds.first->GetRootWindow();
+ if (root->layer() != layer_for_highlighting_->parent())
+ root->layer()->Add(layer_for_highlighting_.get());
+ else
+ root->layer()->StackAtTop(layer_for_highlighting_.get());
+
+ aura::client::ScreenPositionClient* screen_position_client =
+ aura::client::GetScreenPositionClient(root);
+
+ gfx::Rect bounds(window_and_bounds.second);
+ gfx::Point origin = bounds.origin();
+ screen_position_client->ConvertPointFromScreen(root, &origin);
+ bounds.set_origin(origin);
+ layer_for_highlighting_->SetBounds(bounds);
+}
+
+ui_devtools::protocol::Response UIDevToolsDOMAgent::HighlightNode(
+ std::unique_ptr<ui_devtools::protocol::DOM::HighlightConfig>
+ highlight_config,
+ int node_id) {
+ if (!layer_for_highlighting_) {
+ layer_for_highlighting_.reset(
+ new ui::Layer(ui::LayerType::LAYER_SOLID_COLOR));
+ layer_for_highlighting_->set_name("HighlightingLayer");
+ }
+
+ std::pair<aura::Window*, gfx::Rect> window_and_bounds =
+ node_id_to_ui_element_.count(node_id)
+ ? node_id_to_ui_element_[node_id]->GetNodeWindowAndBounds()
+ : std::make_pair<aura::Window*, gfx::Rect>(nullptr, gfx::Rect());
+
+ if (!window_and_bounds.first) {
+ return ui_devtools::protocol::Response::Error("No node found with that id");
+ }
+ SkColor content_color =
+ RGBAToSkColor(highlight_config->getContentColor(nullptr));
+ UpdateHighlight(window_and_bounds, content_color);
+
+ if (!layer_for_highlighting_->visible())
+ layer_for_highlighting_->SetVisible(true);
+
+ return ui_devtools::protocol::Response::OK();
+}
+
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/views/ui_devtools_dom_agent.h b/chromium/components/ui_devtools/views/ui_devtools_dom_agent.h
new file mode 100644
index 00000000000..cdd2382fd7c
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_devtools_dom_agent.h
@@ -0,0 +1,102 @@
+// 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_UI_DEVTOOLS_VIEWS_UI_DEVTOOLS_DOM_AGENT_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_UI_DEVTOOLS_DOM_AGENT_H_
+
+#include "components/ui_devtools/DOM.h"
+#include "components/ui_devtools/devtools_base_agent.h"
+#include "components/ui_devtools/views/ui_element_delegate.h"
+#include "ui/aura/env_observer.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ui_devtools {
+
+class UIElement;
+
+class UIDevToolsDOMAgentObserver {
+ public:
+ // TODO(thanhph): Use UIElement* as input argument instead.
+ virtual void OnNodeBoundsChanged(int node_id) = 0;
+};
+
+class UIDevToolsDOMAgent : public ui_devtools::UiDevToolsBaseAgent<
+ ui_devtools::protocol::DOM::Metainfo>,
+ public UIElementDelegate,
+ public aura::EnvObserver {
+ public:
+ UIDevToolsDOMAgent();
+ ~UIDevToolsDOMAgent() override;
+
+ // DOM::Backend:
+ ui_devtools::protocol::Response disable() override;
+ ui_devtools::protocol::Response getDocument(
+ std::unique_ptr<ui_devtools::protocol::DOM::Node>* out_root) override;
+ ui_devtools::protocol::Response highlightNode(
+ std::unique_ptr<ui_devtools::protocol::DOM::HighlightConfig>
+ highlight_config,
+ ui_devtools::protocol::Maybe<int> node_id) override;
+ ui_devtools::protocol::Response hideHighlight() override;
+
+ // UIElementDelegate:
+ void OnUIElementAdded(UIElement* parent, UIElement* child) override;
+ void OnUIElementReordered(UIElement* parent, UIElement* child) override;
+ void OnUIElementRemoved(UIElement* ui_element) override;
+ void OnUIElementBoundsChanged(UIElement* ui_element) override;
+
+ void AddObserver(UIDevToolsDOMAgentObserver* observer);
+ void RemoveObserver(UIDevToolsDOMAgentObserver* observer);
+ UIElement* GetElementFromNodeId(int node_id);
+ UIElement* window_element_root() const { return window_element_root_.get(); };
+ const std::vector<aura::Window*>& root_windows() const {
+ return root_windows_;
+ };
+
+ private:
+ // aura::EnvObserver:
+ void OnWindowInitialized(aura::Window* window) override {}
+ void OnHostInitialized(aura::WindowTreeHost* host) override;
+
+ void OnNodeBoundsChanged(int node_id);
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> BuildInitialTree();
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> BuildTreeForUIElement(
+ UIElement* ui_element);
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> BuildTreeForWindow(
+ UIElement* window_element_root,
+ aura::Window* window);
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> BuildTreeForRootWidget(
+ UIElement* widget_element,
+ views::Widget* widget);
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> BuildTreeForView(
+ UIElement* view_element,
+ views::View* view);
+ void RemoveDomNode(UIElement* ui_element);
+ void Reset();
+ void InitializeHighlightingWidget();
+ void UpdateHighlight(
+ const std::pair<aura::Window*, gfx::Rect>& window_and_bounds,
+ SkColor background);
+ ui_devtools::protocol::Response HighlightNode(
+ std::unique_ptr<ui_devtools::protocol::DOM::HighlightConfig>
+ highlight_config,
+ int node_id);
+
+ bool is_building_tree_;
+ std::unique_ptr<UIElement> window_element_root_;
+ std::unordered_map<int, UIElement*> node_id_to_ui_element_;
+ std::unique_ptr<ui::Layer> layer_for_highlighting_;
+ std::vector<aura::Window*> root_windows_;
+ base::ObserverList<UIDevToolsDOMAgentObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIDevToolsDOMAgent);
+};
+
+} // namespace ui_devtools
+
+#endif // COMPONENTS_UI_DEVTOOLS_VIEWS_UI_DEVTOOLS_DOM_AGENT_H_
diff --git a/chromium/components/ui_devtools/views/ui_devtools_unittest.cc b/chromium/components/ui_devtools/views/ui_devtools_unittest.cc
new file mode 100644
index 00000000000..9ebf91f541c
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_devtools_unittest.cc
@@ -0,0 +1,876 @@
+// 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 "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/ui_devtools/views/ui_devtools_css_agent.h"
+#include "components/ui_devtools/views/ui_devtools_dom_agent.h"
+#include "components/ui_devtools/views/ui_element.h"
+#include "components/ui_devtools/views/view_element.h"
+#include "components/ui_devtools/views/widget_element.h"
+#include "components/ui_devtools/views/window_element.h"
+#include "ui/aura/client/window_parenting_client.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/display/display.h"
+#include "ui/views/background.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/native_widget_private.h"
+#include "ui/views/widget/widget.h"
+#include "ui/wm/core/coordinate_conversion.h"
+
+namespace ui_devtools {
+namespace {
+
+using namespace ui_devtools::protocol;
+
+const int kDefaultChildNodeCount = -1;
+const SkColor kBackgroundColor = SK_ColorRED;
+const SkColor kBorderColor = SK_ColorBLUE;
+
+class TestView : public views::View {
+ public:
+ TestView(const char* name) : views::View(), name_(name) {}
+
+ const char* GetClassName() const override { return name_; }
+
+ private:
+ const char* name_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+class FakeFrontendChannel : public FrontendChannel {
+ public:
+ FakeFrontendChannel() {}
+ ~FakeFrontendChannel() override {}
+
+ int CountProtocolNotificationMessageStartsWith(const std::string& message) {
+ int count = 0;
+ for (const std::string& s : protocol_notification_messages_) {
+ if (base::StartsWith(s, message, base::CompareCase::SENSITIVE))
+ count++;
+ }
+ return count;
+ }
+
+ int CountProtocolNotificationMessage(const std::string& message) {
+ return std::count(protocol_notification_messages_.begin(),
+ protocol_notification_messages_.end(), message);
+ }
+
+ // FrontendChannel
+ void sendProtocolResponse(int callId,
+ std::unique_ptr<Serializable> message) override {}
+ void flushProtocolNotifications() override {}
+ void sendProtocolNotification(
+ std::unique_ptr<Serializable> message) override {
+ protocol_notification_messages_.push_back(message->serialize());
+ }
+
+ private:
+ std::vector<std::string> protocol_notification_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeFrontendChannel);
+};
+
+std::string GetAttributeValue(const std::string& attribute, DOM::Node* node) {
+ EXPECT_TRUE(node->hasAttributes());
+ Array<std::string>* attributes = node->getAttributes(nullptr);
+ for (size_t i = 0; i < attributes->length() - 1; i++) {
+ if (attributes->get(i) == attribute)
+ return attributes->get(i + 1);
+ }
+ return nullptr;
+}
+
+bool Equals(aura::Window* window, DOM::Node* node) {
+ int children_count = static_cast<int>(window->children().size());
+ if (views::Widget::GetWidgetForNativeView(window))
+ children_count++;
+ return "Window" == node->getNodeName() &&
+ window->GetName() == GetAttributeValue("name", node) &&
+ children_count == node->getChildNodeCount(kDefaultChildNodeCount);
+}
+
+void Compare(views::Widget* widget, DOM::Node* node) {
+ EXPECT_EQ("Widget", node->getNodeName());
+ EXPECT_EQ(widget->GetName(), GetAttributeValue("name", node));
+ EXPECT_EQ(widget->GetRootView() ? 1 : 0,
+ node->getChildNodeCount(kDefaultChildNodeCount));
+}
+
+void Compare(views::View* view, DOM::Node* node) {
+ EXPECT_EQ("View", node->getNodeName());
+ EXPECT_EQ(view->GetClassName(), GetAttributeValue("name", node));
+ EXPECT_EQ(view->child_count(),
+ node->getChildNodeCount(kDefaultChildNodeCount));
+}
+
+void Compare(aura::Window* window, DOM::Node* node) {
+ EXPECT_TRUE(Equals(window, node));
+}
+
+DOM::Node* FindInRoot(aura::Window* window, DOM::Node* root) {
+ if (Equals(window, root))
+ return root;
+
+ Array<DOM::Node>* children = root->getChildren(nullptr);
+ DOM::Node* window_node = nullptr;
+ for (size_t i = 0; i < children->length(); i++) {
+ window_node = FindInRoot(window, children->get(i));
+ if (window_node)
+ return window_node;
+ }
+ return window_node;
+}
+
+int GetPropertyByName(const std::string& name,
+ Array<CSS::CSSProperty>* properties) {
+ for (size_t i = 0; i < properties->length(); i++) {
+ CSS::CSSProperty* property = properties->get(i);
+ if (property->getName() == name) {
+ int value;
+ EXPECT_TRUE(base::StringToInt(property->getValue(), &value));
+ return value;
+ }
+ }
+ NOTREACHED();
+ return -1;
+}
+
+ui::Layer* GetHighlightingLayer(aura::Window* root_window) {
+ for (auto* layer : root_window->layer()->children()) {
+ if (layer->name() == "HighlightingLayer")
+ return layer;
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+std::unique_ptr<DOM::RGBA> SkColorToRGBA(const SkColor& color) {
+ return DOM::RGBA::create()
+ .setA(SkColorGetA(color) / 255)
+ .setB(SkColorGetB(color))
+ .setG(SkColorGetG(color))
+ .setR(SkColorGetR(color))
+ .build();
+}
+
+std::unique_ptr<DOM::HighlightConfig> CreateHighlightConfig(
+ const SkColor& background_color,
+ const SkColor& border_color) {
+ return DOM::HighlightConfig::create()
+ .setContentColor(SkColorToRGBA(background_color))
+ .setBorderColor(SkColorToRGBA(border_color))
+ .build();
+}
+
+void ExpectHighlighted(const gfx::Rect& bounds, aura::Window* root_window) {
+ ui::Layer* highlighting_layer = GetHighlightingLayer(root_window);
+ EXPECT_TRUE(highlighting_layer->visible());
+ EXPECT_EQ(bounds, highlighting_layer->bounds());
+ EXPECT_EQ(kBackgroundColor, highlighting_layer->GetTargetColor());
+}
+
+} // namespace
+
+class UIDevToolsTest : public views::ViewsTestBase {
+ public:
+ UIDevToolsTest() {}
+ ~UIDevToolsTest() override {}
+
+ views::internal::NativeWidgetPrivate* CreateTestNativeWidget() {
+ views::Widget* widget = new views::Widget;
+ views::Widget::InitParams params;
+ params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
+ params.parent = GetPrimaryRootWindow();
+ widget->Init(params);
+ return widget->native_widget_private();
+ }
+
+ std::unique_ptr<views::Widget> CreateTestWidget(const gfx::Rect& bounds) {
+ std::unique_ptr<views::Widget> widget(new views::Widget);
+ views::Widget::InitParams params;
+ params.delegate = nullptr;
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.bounds = bounds;
+ params.parent = GetPrimaryRootWindow();
+ widget->Init(params);
+ widget->Show();
+ return widget;
+ }
+
+ std::unique_ptr<aura::Window> CreateChildWindow(
+ aura::Window* parent,
+ aura::client::WindowType type = aura::client::WINDOW_TYPE_NORMAL) {
+ std::unique_ptr<aura::Window> window =
+ base::MakeUnique<aura::Window>(nullptr, type);
+ window->Init(ui::LAYER_NOT_DRAWN);
+ window->SetBounds(gfx::Rect());
+ parent->AddChild(window.get());
+ window->Show();
+ return window;
+ }
+
+ void SetUp() override {
+ fake_frontend_channel_ = base::MakeUnique<FakeFrontendChannel>();
+ uber_dispatcher_ =
+ base::MakeUnique<UberDispatcher>(fake_frontend_channel_.get());
+ dom_agent_ = base::MakeUnique<ui_devtools::UIDevToolsDOMAgent>();
+ dom_agent_->Init(uber_dispatcher_.get());
+ css_agent_ =
+ base::MakeUnique<ui_devtools::UIDevToolsCSSAgent>(dom_agent_.get());
+ css_agent_->Init(uber_dispatcher_.get());
+ css_agent_->enable();
+
+ // We need to create |dom_agent| first to observe creation of
+ // WindowTreeHosts in ViewTestBase::SetUp().
+ views::ViewsTestBase::SetUp();
+
+ top_window = CreateChildWindow(GetPrimaryRootWindow());
+ top_default_container_window = CreateChildWindow(GetPrimaryRootWindow());
+ top_overlay_window = CreateChildWindow(GetPrimaryRootWindow(),
+ aura::client::WINDOW_TYPE_UNKNOWN);
+ }
+
+ void TearDown() override {
+ top_overlay_window.reset();
+ top_default_container_window.reset();
+ top_window.reset();
+ css_agent_.reset();
+ dom_agent_.reset();
+ uber_dispatcher_.reset();
+ fake_frontend_channel_.reset();
+ views::ViewsTestBase::TearDown();
+ }
+
+ void ExpectChildNodeInserted(int parent_id, int prev_sibling_id) {
+ EXPECT_EQ(1, frontend_channel()->CountProtocolNotificationMessageStartsWith(
+ base::StringPrintf("{\"method\":\"DOM.childNodeInserted\","
+ "\"params\":{\"parentNodeId\":%d,"
+ "\"previousNodeId\":%d",
+ parent_id, prev_sibling_id)));
+ }
+
+ void ExpectChildNodeRemoved(int parent_id, int node_id) {
+ EXPECT_EQ(1, frontend_channel()->CountProtocolNotificationMessage(
+ base::StringPrintf(
+ "{\"method\":\"DOM.childNodeRemoved\",\"params\":{"
+ "\"parentNodeId\":%d,\"nodeId\":%d}}",
+ parent_id, node_id)));
+ }
+
+ int GetStyleSheetChangedCount(int node_id) {
+ return frontend_channel()->CountProtocolNotificationMessage(
+ base::StringPrintf("{\"method\":\"CSS.styleSheetChanged\",\"params\":{"
+ "\"styleSheetId\":\"%d\"}}",
+ node_id));
+ }
+
+ void CompareNodeBounds(DOM::Node* node, const gfx::Rect& bounds) {
+ Maybe<CSS::CSSStyle> styles;
+ css_agent_->getMatchedStylesForNode(node->getNodeId(), &styles);
+ ASSERT_TRUE(styles.isJust());
+ Array<CSS::CSSProperty>* properties = styles.fromJust()->getCssProperties();
+ EXPECT_EQ(bounds.height(), GetPropertyByName("height", properties));
+ EXPECT_EQ(bounds.width(), GetPropertyByName("width", properties));
+ EXPECT_EQ(bounds.x(), GetPropertyByName("x", properties));
+ EXPECT_EQ(bounds.y(), GetPropertyByName("y", properties));
+ }
+
+ void SetStyleTexts(DOM::Node* node,
+ const std::string& style_text,
+ bool success) {
+ auto edits = Array<CSS::StyleDeclarationEdit>::create();
+ auto edit = CSS::StyleDeclarationEdit::create()
+ .setStyleSheetId(base::IntToString(node->getNodeId()))
+ .setText(style_text)
+ .build();
+ edits->addItem(std::move(edit));
+ std::unique_ptr<Array<CSS::CSSStyle>> output;
+ EXPECT_EQ(success,
+ css_agent_->setStyleTexts(std::move(edits), &output).isSuccess());
+
+ if (success)
+ ASSERT_TRUE(output);
+ else
+ ASSERT_FALSE(output);
+ }
+
+ void HighlightNode(int node_id) {
+ dom_agent_->highlightNode(
+ CreateHighlightConfig(kBackgroundColor, kBorderColor), node_id);
+ }
+
+ void HideHighlight(int root_window_index) {
+ dom_agent_->hideHighlight();
+ DCHECK_GE(root_window_index, 0);
+ DCHECK_LE(root_window_index,
+ static_cast<int>(dom_agent()->root_windows().size()));
+ ASSERT_FALSE(
+ GetHighlightingLayer(dom_agent()->root_windows()[root_window_index])
+ ->visible());
+ }
+
+ FakeFrontendChannel* frontend_channel() {
+ return fake_frontend_channel_.get();
+ }
+
+ aura::Window* GetPrimaryRootWindow() {
+ DCHECK(dom_agent()->root_windows().size());
+ return dom_agent()->root_windows()[0];
+ }
+
+ ui_devtools::UIDevToolsCSSAgent* css_agent() { return css_agent_.get(); }
+ ui_devtools::UIDevToolsDOMAgent* dom_agent() { return dom_agent_.get(); }
+
+ std::unique_ptr<aura::Window> top_overlay_window;
+ std::unique_ptr<aura::Window> top_window;
+ std::unique_ptr<aura::Window> top_default_container_window;
+
+ private:
+ std::unique_ptr<UberDispatcher> uber_dispatcher_;
+ std::unique_ptr<FakeFrontendChannel> fake_frontend_channel_;
+ std::unique_ptr<ui_devtools::UIDevToolsDOMAgent> dom_agent_;
+ std::unique_ptr<ui_devtools::UIDevToolsCSSAgent> css_agent_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIDevToolsTest);
+};
+
+TEST_F(UIDevToolsTest, GetDocumentWithWindowWidgetView) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+ aura::Window* parent_window = widget->GetNativeWindow();
+ parent_window->SetName("parent_window");
+ std::unique_ptr<aura::Window> child_window = CreateChildWindow(parent_window);
+ child_window->SetName("child_window");
+ widget->Show();
+ views::View* child_view = new TestView("child_view");
+ widget->GetRootView()->AddChildView(child_view);
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+ ASSERT_TRUE(parent_node);
+ Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+ ASSERT_TRUE(parent_children);
+ DOM::Node* widget_node = parent_children->get(0);
+ Compare(widget.get(), widget_node);
+ Compare(child_window.get(), parent_children->get(1));
+ Array<DOM::Node>* widget_children = widget_node->getChildren(nullptr);
+ ASSERT_TRUE(widget_children);
+ Compare(widget->GetRootView(), widget_children->get(0));
+ ASSERT_TRUE(widget_children->get(0)->getChildren(nullptr));
+ Compare(child_view, widget_children->get(0)->getChildren(nullptr)->get(1));
+}
+
+TEST_F(UIDevToolsTest, GetDocumentNativeWidgetOwnsWidget) {
+ views::internal::NativeWidgetPrivate* native_widget_private =
+ CreateTestNativeWidget();
+ views::Widget* widget = native_widget_private->GetWidget();
+ aura::Window* parent_window = widget->GetNativeWindow();
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+ ASSERT_TRUE(parent_node);
+ DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
+ Compare(widget, widget_node);
+ // Destroy NativeWidget followed by |widget|
+ widget->CloseNow();
+}
+
+TEST_F(UIDevToolsTest, WindowAddedChildNodeInserted) {
+ // Initialize DOMAgent
+ std::unique_ptr<aura::Window> window_child =
+ CreateChildWindow(top_window.get());
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ aura::Window* root_window = GetPrimaryRootWindow();
+ aura::Window* parent_window = root_window->children()[0];
+ DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+ Array<DOM::Node>* parent_node_children = parent_node->getChildren(nullptr);
+ DOM::Node* sibling_node =
+ parent_node_children->get(parent_node_children->length() - 1);
+
+ std::unique_ptr<aura::Window> child(CreateChildWindow(parent_window));
+ ExpectChildNodeInserted(parent_node->getNodeId(), sibling_node->getNodeId());
+}
+
+TEST_F(UIDevToolsTest, WindowDestroyedChildNodeRemoved) {
+ std::unique_ptr<aura::Window> child_1 = CreateChildWindow(top_window.get());
+ std::unique_ptr<aura::Window> child_2 = CreateChildWindow(child_1.get());
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ aura::Window* root_window = GetPrimaryRootWindow();
+ aura::Window* rotation_window = root_window->children()[0];
+ aura::Window* parent_window = rotation_window->children()[0];
+ aura::Window* child_window = parent_window->children()[0];
+ DOM::Node* root_node =
+ root->getChildren(nullptr)->get(0)->getChildren(nullptr)->get(0);
+ DOM::Node* parent_node = root_node->getChildren(nullptr)->get(0);
+ DOM::Node* child_node = parent_node->getChildren(nullptr)->get(0);
+
+ Compare(parent_window, parent_node);
+ Compare(child_window, child_node);
+ child_2.reset();
+ ExpectChildNodeRemoved(parent_node->getNodeId(), child_node->getNodeId());
+}
+
+TEST_F(UIDevToolsTest, WindowReorganizedChildNodeRearranged) {
+ std::unique_ptr<aura::Window> child_1 = CreateChildWindow(top_window.get());
+ std::unique_ptr<aura::Window> child_2 = CreateChildWindow(top_window.get());
+ std::unique_ptr<aura::Window> child_11 = CreateChildWindow(child_1.get());
+ std::unique_ptr<aura::Window> child_21 = CreateChildWindow(child_2.get());
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ aura::Window* root_window = GetPrimaryRootWindow();
+ aura::Window* rotation_window = root_window->children()[0];
+ aura::Window* parent_window = rotation_window->children()[0];
+ aura::Window* target_window = rotation_window->children()[1];
+ aura::Window* child_window = parent_window->children()[0];
+
+ DOM::Node* root_node =
+ root->getChildren(nullptr)->get(0)->getChildren(nullptr)->get(0);
+ DOM::Node* parent_node = root_node->getChildren(nullptr)->get(0);
+ DOM::Node* target_node = root_node->getChildren(nullptr)->get(1);
+ Array<DOM::Node>* target_node_children = target_node->getChildren(nullptr);
+ DOM::Node* sibling_node =
+ target_node_children->get(target_node_children->length() - 1);
+ DOM::Node* child_node = parent_node->getChildren(nullptr)->get(0);
+
+ Compare(parent_window, parent_node);
+ Compare(target_window, target_node);
+ Compare(child_window, child_node);
+ target_window->AddChild(child_window);
+ ExpectChildNodeRemoved(parent_node->getNodeId(), child_node->getNodeId());
+ ExpectChildNodeInserted(target_node->getNodeId(), sibling_node->getNodeId());
+}
+
+TEST_F(UIDevToolsTest, WindowReorganizedChildNodeRemovedAndInserted) {
+ std::unique_ptr<aura::Window> child_1 = CreateChildWindow(top_window.get());
+ std::unique_ptr<aura::Window> child_2 = CreateChildWindow(top_window.get());
+ std::unique_ptr<aura::Window> child_21 = CreateChildWindow(child_2.get());
+ std::unique_ptr<aura::Window> child_22 = CreateChildWindow(child_2.get());
+
+ aura::Window* root_window = GetPrimaryRootWindow();
+ aura::Window* rotation_window = root_window->children()[0];
+ aura::Window* parent_window = rotation_window->children()[0];
+ aura::Window* target_window = rotation_window->children()[1];
+ std::unique_ptr<aura::Window> child_window(CreateChildWindow(parent_window));
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+ DOM::Node* root_node =
+ root->getChildren(nullptr)->get(0)->getChildren(nullptr)->get(0);
+ DOM::Node* parent_node = root_node->getChildren(nullptr)->get(0);
+ DOM::Node* target_node = root_node->getChildren(nullptr)->get(1);
+ Array<DOM::Node>* target_node_children = target_node->getChildren(nullptr);
+ DOM::Node* sibling_node =
+ target_node_children->get(target_node_children->length() - 1);
+ Array<DOM::Node>* parent_node_children = parent_node->getChildren(nullptr);
+ DOM::Node* child_node =
+ parent_node_children->get(parent_node_children->length() - 1);
+
+ Compare(parent_window, parent_node);
+ Compare(target_window, target_node);
+ Compare(child_window.get(), child_node);
+ parent_window->RemoveChild(child_window.get());
+ target_window->AddChild(child_window.get());
+ ExpectChildNodeRemoved(parent_node->getNodeId(), child_node->getNodeId());
+ ExpectChildNodeInserted(target_node->getNodeId(), sibling_node->getNodeId());
+}
+
+TEST_F(UIDevToolsTest, WindowStackingChangedChildNodeRemovedAndInserted) {
+ std::unique_ptr<aura::Window> child_11 = CreateChildWindow(top_window.get());
+ std::unique_ptr<aura::Window> child_12 = CreateChildWindow(top_window.get());
+ std::unique_ptr<aura::Window> child_13 = CreateChildWindow(top_window.get());
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ aura::Window* root_window = GetPrimaryRootWindow();
+ aura::Window* parent_window = root_window->children()[0];
+ aura::Window* child_window = parent_window->children()[0];
+ aura::Window* target_window = parent_window->children()[1];
+
+ DOM::Node* parent_node =
+ root->getChildren(nullptr)->get(0)->getChildren(nullptr)->get(0);
+ Array<DOM::Node>* parent_node_children = parent_node->getChildren(nullptr);
+ DOM::Node* child_node = parent_node_children->get(0);
+ DOM::Node* sibling_node = parent_node_children->get(1);
+ int parent_id = parent_node->getNodeId();
+
+ Compare(parent_window, parent_node);
+ Compare(child_window, child_node);
+ parent_window->StackChildAbove(child_window, target_window);
+ ExpectChildNodeRemoved(parent_id, child_node->getNodeId());
+ ExpectChildNodeInserted(parent_id, sibling_node->getNodeId());
+}
+
+TEST_F(UIDevToolsTest, ViewInserted) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+ aura::Window* window = widget->GetNativeWindow();
+ widget->Show();
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(window, root.get());
+ ASSERT_TRUE(parent_node);
+ DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
+ DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+ Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
+ ASSERT_TRUE(root_view_children);
+ DOM::Node* sibling_view_node =
+ root_view_children->get(root_view_children->length() - 1);
+
+ widget->GetRootView()->AddChildView(new views::View);
+ ExpectChildNodeInserted(root_view_node->getNodeId(),
+ sibling_view_node->getNodeId());
+}
+
+TEST_F(UIDevToolsTest, ViewRemoved) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+ // Need to store |view| in unique_ptr because it is removed from the widget
+ // and needs to be destroyed independently
+ std::unique_ptr<views::View> child_view = base::MakeUnique<views::View>();
+ aura::Window* window = widget->GetNativeWindow();
+ widget->Show();
+ views::View* root_view = widget->GetRootView();
+ root_view->AddChildView(child_view.get());
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(window, root.get());
+ ASSERT_TRUE(parent_node);
+ DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
+ DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+ Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
+ ASSERT_TRUE(root_view_children);
+ DOM::Node* child_view_node =
+ root_view_children->get(root_view_children->length() - 1);
+
+ Compare(child_view.get(), child_view_node);
+ root_view->RemoveChildView(child_view.get());
+ ExpectChildNodeRemoved(root_view_node->getNodeId(),
+ child_view_node->getNodeId());
+}
+
+TEST_F(UIDevToolsTest, ViewRearranged) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+ aura::Window* window = widget->GetNativeWindow();
+ widget->Show();
+ views::View* root_view = widget->GetRootView();
+ views::View* parent_view = new views::View;
+ views::View* target_view = new views::View;
+ views::View* child_view = new views::View;
+ views::View* child_view_1 = new views::View;
+
+ root_view->AddChildView(parent_view);
+ root_view->AddChildView(target_view);
+ parent_view->AddChildView(child_view);
+ parent_view->AddChildView(child_view_1);
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(window, root.get());
+ ASSERT_TRUE(parent_node);
+ DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
+ DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+ Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
+ ASSERT_TRUE(root_view_children);
+ size_t root_children_size = root_view_children->length();
+ ASSERT_TRUE(root_children_size >= 2);
+ DOM::Node* parent_view_node = root_view_children->get(root_children_size - 2);
+ DOM::Node* target_view_node = root_view_children->get(root_children_size - 1);
+ DOM::Node* child_view_node = parent_view_node->getChildren(nullptr)->get(0);
+ DOM::Node* child_view_node_1 = parent_view_node->getChildren(nullptr)->get(1);
+
+ Compare(parent_view, parent_view_node);
+ Compare(target_view, target_view_node);
+ Compare(child_view, child_view_node);
+ Compare(child_view_1, child_view_node_1);
+
+ ASSERT_NE(child_view_node->getNodeId(), child_view_node_1->getNodeId());
+
+ // Reorder child_view_1 from index 1 to 0 in view::Views tree. This makes DOM
+ // tree remove view node at position 1 and insert it at position 0.
+ parent_view->ReorderChildView(child_view_1, 0);
+ ExpectChildNodeRemoved(parent_view_node->getNodeId(),
+ child_view_node_1->getNodeId());
+ ExpectChildNodeInserted(parent_view_node->getNodeId(), 0);
+
+ target_view->AddChildView(child_view);
+ ExpectChildNodeRemoved(parent_view_node->getNodeId(),
+ child_view_node->getNodeId());
+ ExpectChildNodeInserted(target_view_node->getNodeId(), 0);
+}
+
+TEST_F(UIDevToolsTest, ViewRearrangedRemovedAndInserted) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+ aura::Window* window = widget->GetNativeWindow();
+ widget->Show();
+ views::View* root_view = widget->GetRootView();
+ views::View* parent_view = new views::View;
+ views::View* target_view = new views::View;
+ views::View* child_view = new views::View;
+ root_view->AddChildView(parent_view);
+ root_view->AddChildView(target_view);
+ parent_view->AddChildView(child_view);
+
+ // Initialize DOMAgent
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(window, root.get());
+ ASSERT_TRUE(parent_node);
+ DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
+ DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+ Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
+ ASSERT_TRUE(root_view_children);
+ size_t root_children_size = root_view_children->length();
+ ASSERT_TRUE(root_children_size >= 2);
+ DOM::Node* parent_view_node = root_view_children->get(root_children_size - 2);
+ DOM::Node* target_view_node = root_view_children->get(root_children_size - 1);
+ DOM::Node* child_view_node = parent_view_node->getChildren(nullptr)->get(0);
+
+ Compare(parent_view, parent_view_node);
+ Compare(target_view, target_view_node);
+ Compare(child_view, child_view_node);
+ parent_view->RemoveChildView(child_view);
+ target_view->AddChildView(child_view);
+ ExpectChildNodeRemoved(parent_view_node->getNodeId(),
+ child_view_node->getNodeId());
+ ExpectChildNodeInserted(target_view_node->getNodeId(), 0);
+}
+
+TEST_F(UIDevToolsTest, WindowWidgetViewHighlight) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(0, 0, 400, 400)));
+ aura::Window* parent_window = widget->GetNativeWindow();
+ std::unique_ptr<aura::Window> window(CreateChildWindow(parent_window));
+ views::View* root_view = widget->GetRootView();
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+ ASSERT_TRUE(parent_node);
+ Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+ ASSERT_TRUE(parent_children);
+ DOM::Node* window_node = parent_children->get(1);
+ DOM::Node* widget_node = parent_children->get(0);
+ DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+
+ HighlightNode(window_node->getNodeId());
+ ExpectHighlighted(window->GetBoundsInScreen(), GetPrimaryRootWindow());
+ ui_devtools::UIElement* element =
+ dom_agent()->GetElementFromNodeId(window_node->getNodeId());
+ ASSERT_EQ(ui_devtools::UIElementType::WINDOW, element->type());
+ EXPECT_EQ(element->GetNodeWindowAndBounds().first, window.get());
+ EXPECT_EQ(element->GetNodeWindowAndBounds().second,
+ window->GetBoundsInScreen());
+
+ HideHighlight(0);
+
+ HighlightNode(widget_node->getNodeId());
+ ExpectHighlighted(widget->GetWindowBoundsInScreen(), GetPrimaryRootWindow());
+
+ element = dom_agent()->GetElementFromNodeId(widget_node->getNodeId());
+ ASSERT_EQ(ui_devtools::UIElementType::WIDGET, element->type());
+ EXPECT_EQ(element->GetNodeWindowAndBounds().first, widget->GetNativeWindow());
+ EXPECT_EQ(element->GetNodeWindowAndBounds().second,
+ widget->GetWindowBoundsInScreen());
+
+ HideHighlight(0);
+
+ HighlightNode(root_view_node->getNodeId());
+ ExpectHighlighted(root_view->GetBoundsInScreen(), GetPrimaryRootWindow());
+
+ element = dom_agent()->GetElementFromNodeId(root_view_node->getNodeId());
+ ASSERT_EQ(ui_devtools::UIElementType::VIEW, element->type());
+ EXPECT_EQ(element->GetNodeWindowAndBounds().first,
+ root_view->GetWidget()->GetNativeWindow());
+ EXPECT_EQ(element->GetNodeWindowAndBounds().second,
+ root_view->GetBoundsInScreen());
+
+ HideHighlight(0);
+
+ // Highlight non-existent node
+ HighlightNode(10000);
+ EXPECT_FALSE(GetHighlightingLayer(GetPrimaryRootWindow())->visible());
+}
+
+int GetNodeIdFromWindow(ui_devtools::UIElement* ui_element,
+ aura::Window* window) {
+ for (auto* child : ui_element->children()) {
+ if (child->type() == ui_devtools::UIElementType::WINDOW &&
+ static_cast<ui_devtools::WindowElement*>(child)->window() == window) {
+ return child->node_id();
+ }
+ }
+ for (auto* child : ui_element->children()) {
+ if (child->type() == ui_devtools::UIElementType::WINDOW) {
+ int node_id = GetNodeIdFromWindow(child, window);
+ if (node_id > 0)
+ return node_id;
+ }
+ }
+ return 0;
+}
+
+// TODO(thanhph): Make test AshDevToolsTest.MultipleDisplayHighlight work with
+// multiple displays. https://crbug.com/726831.
+
+TEST_F(UIDevToolsTest, WindowWidgetViewGetMatchedStylesForNode) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+ aura::Window* parent_window = widget->GetNativeWindow();
+ std::unique_ptr<aura::Window> window(CreateChildWindow(parent_window));
+ gfx::Rect window_bounds(2, 2, 3, 3);
+ gfx::Rect widget_bounds(50, 50, 100, 75);
+ gfx::Rect view_bounds(4, 4, 3, 3);
+ window->SetBounds(window_bounds);
+ widget->SetBounds(widget_bounds);
+ widget->GetRootView()->SetBoundsRect(view_bounds);
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+ ASSERT_TRUE(parent_node);
+ Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+ ASSERT_TRUE(parent_children);
+
+ CompareNodeBounds(parent_node, widget_bounds);
+ CompareNodeBounds(parent_children->get(1), window_bounds);
+ CompareNodeBounds(parent_children->get(0)->getChildren(nullptr)->get(0),
+ view_bounds);
+}
+
+TEST_F(UIDevToolsTest, WindowWidgetViewStyleSheetChanged) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+ aura::Window* widget_window = widget->GetNativeWindow();
+ std::unique_ptr<aura::Window> child(CreateChildWindow(widget_window));
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ gfx::Rect child_bounds(2, 2, 3, 3);
+ gfx::Rect widget_bounds(10, 10, 150, 160);
+ gfx::Rect view_bounds(4, 4, 3, 3);
+ child->SetBounds(child_bounds);
+ widget->SetBounds(widget_bounds);
+ widget->GetRootView()->SetBoundsRect(view_bounds);
+
+ DOM::Node* widget_node = FindInRoot(widget_window, root.get());
+ ASSERT_TRUE(widget_node);
+ Array<DOM::Node>* widget_node_children = widget_node->getChildren(nullptr);
+ ASSERT_TRUE(widget_node_children);
+
+ EXPECT_EQ(1, GetStyleSheetChangedCount(widget_node->getNodeId()));
+ EXPECT_EQ(
+ 1, GetStyleSheetChangedCount(widget_node_children->get(1)->getNodeId()));
+ EXPECT_EQ(2, GetStyleSheetChangedCount(widget_node_children->get(0)
+ ->getChildren(nullptr)
+ ->get(0)
+ ->getNodeId()));
+}
+
+TEST_F(UIDevToolsTest, WindowWidgetViewSetStyleText) {
+ std::unique_ptr<views::Widget> widget(
+ CreateTestWidget(gfx::Rect(0, 0, 400, 400)));
+ aura::Window* parent_window = widget->GetNativeWindow();
+ std::unique_ptr<aura::Window> window(CreateChildWindow(parent_window));
+ views::View* root_view = widget->GetRootView();
+
+ std::unique_ptr<ui_devtools::protocol::DOM::Node> root;
+ dom_agent()->getDocument(&root);
+
+ DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+ ASSERT_TRUE(parent_node);
+ Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+ ASSERT_TRUE(parent_children);
+
+ // Test different combinations on window node
+ DOM::Node* window_node = parent_children->get(1);
+
+ SetStyleTexts(window_node,
+ "x: 25; y:35; width: 5; height: 20; visibility: 1;", true);
+ EXPECT_EQ(gfx::Rect(25, 35, 5, 20), window->bounds());
+ EXPECT_TRUE(window->IsVisible());
+
+ SetStyleTexts(window_node, "test_nothing_happens:1;", false);
+ EXPECT_EQ(gfx::Rect(25, 35, 5, 20), window->bounds()); // Not changed
+
+ SetStyleTexts(window_node, "\nheight: 10;\nvisibility: 0;\n", true);
+ EXPECT_EQ(gfx::Rect(25, 35, 5, 10), window->bounds());
+ EXPECT_FALSE(window->IsVisible());
+
+ SetStyleTexts(window_node, "\nx: 10; y: 23; width: 52;\n ", true);
+ EXPECT_EQ(gfx::Rect(10, 23, 52, 10), window->bounds());
+
+ // Test different combinations on widget node
+ DOM::Node* widget_node = parent_children->get(0);
+
+ SetStyleTexts(widget_node,
+ "x: 25; y:35; width: 53; height: 64; visibility: 0;", true);
+ EXPECT_EQ(gfx::Rect(25, 35, 53, 64), widget->GetRestoredBounds());
+ EXPECT_FALSE(widget->IsVisible());
+
+ SetStyleTexts(widget_node, "test_nothing_happens:1;", false);
+ EXPECT_EQ(gfx::Rect(25, 35, 53, 64),
+ widget->GetRestoredBounds()); // Not changed
+
+ SetStyleTexts(widget_node, "\nheight: 123;\nvisibility: 1;\n", true);
+ EXPECT_EQ(gfx::Rect(25, 35, 53, 123), widget->GetRestoredBounds());
+ EXPECT_TRUE(widget->IsVisible());
+
+ SetStyleTexts(widget_node, "\nx: 10; y: 23; width: 98;\n ", true);
+ EXPECT_EQ(gfx::Rect(10, 23, 98, 123), widget->GetRestoredBounds());
+
+ // Test different combinations on view node
+ DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+
+ SetStyleTexts(root_view_node,
+ "x: 25; y:35; width: 45; height: 20; visibility: 0;", true);
+ EXPECT_EQ(gfx::Rect(25, 35, 45, 20), root_view->bounds());
+ EXPECT_FALSE(root_view->visible());
+
+ SetStyleTexts(root_view_node, "test_nothing_happens:1;", false);
+ EXPECT_EQ(gfx::Rect(25, 35, 45, 20), root_view->bounds()); // Not changed
+
+ SetStyleTexts(root_view_node, "\nheight: 73;\n ", true);
+ EXPECT_EQ(gfx::Rect(25, 35, 45, 73), root_view->bounds());
+
+ SetStyleTexts(root_view_node, "\nx: 10; y: 23; width: 52;\nvisibility: 1;\n",
+ true);
+ EXPECT_EQ(gfx::Rect(10, 23, 52, 73), root_view->bounds());
+ EXPECT_TRUE(root_view->visible());
+}
+
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/views/ui_element.cc b/chromium/components/ui_devtools/views/ui_element.cc
new file mode 100644
index 00000000000..a44ff1658c5
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_element.cc
@@ -0,0 +1,78 @@
+// 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/ui_devtools/views/ui_element.h"
+
+#include <algorithm>
+
+#include "components/ui_devtools/views/ui_element_delegate.h"
+#include "components/ui_devtools/views/view_element.h"
+#include "components/ui_devtools/views/widget_element.h"
+#include "components/ui_devtools/views/window_element.h"
+
+namespace ui_devtools {
+namespace {
+
+static int node_ids = 0;
+
+} // namespace
+
+UIElement::~UIElement() {
+ for (auto* child : children_)
+ delete child;
+ children_.clear();
+}
+
+std::string UIElement::GetTypeName() const {
+ switch (type_) {
+ case UIElementType::WINDOW:
+ return "Window";
+ case UIElementType::WIDGET:
+ return "Widget";
+ case UIElementType::VIEW:
+ return "View";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+void UIElement::AddChild(UIElement* child, UIElement* before) {
+ if (before) {
+ auto iter = std::find(children_.begin(), children_.end(), before);
+ DCHECK(iter != children_.end());
+ children_.insert(iter, child);
+ } else {
+ children_.push_back(child);
+ }
+ delegate_->OnUIElementAdded(this, child);
+}
+
+void UIElement::RemoveChild(UIElement* child) {
+ delegate()->OnUIElementRemoved(child);
+ auto iter = std::find(children_.begin(), children_.end(), child);
+ DCHECK(iter != children_.end());
+ children_.erase(iter);
+}
+
+void UIElement::ReorderChild(UIElement* child, int new_index) {
+ // Remove |child| out of vector |children_|.
+ auto iter = std::find(children_.begin(), children_.end(), child);
+ DCHECK(iter != children_.end());
+ children_.erase(iter);
+
+ // Move child to new position |new_index| in vector |children_|.
+ new_index = std::min(static_cast<int>(children_.size()) - 1, new_index);
+ iter = children_.begin() + new_index;
+ children_.insert(iter, child);
+ delegate()->OnUIElementReordered(child->parent(), child);
+}
+
+UIElement::UIElement(const UIElementType type,
+ UIElementDelegate* delegate,
+ UIElement* parent)
+ : node_id_(++node_ids), type_(type), parent_(parent), delegate_(delegate) {
+ delegate_->OnUIElementAdded(0, this);
+}
+
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/views/ui_element.h b/chromium/components/ui_devtools/views/ui_element.h
new file mode 100644
index 00000000000..dcd614a02fb
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_element.h
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UI_DEVTOOLS_VIEWS_UI_ELEMENT_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_UI_ELEMENT_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/view.h"
+
+namespace ui_devtools {
+
+class UIElementDelegate;
+
+// UIElement type.
+enum UIElementType { WINDOW, WIDGET, VIEW };
+
+class UIElement {
+ public:
+ virtual ~UIElement();
+ int node_id() const { return node_id_; };
+ std::string GetTypeName() const;
+ UIElement* parent() const { return parent_; };
+ UIElementDelegate* delegate() const { return delegate_; };
+ UIElementType type() const { return type_; };
+ const std::vector<UIElement*>& children() const { return children_; };
+
+ // |child| is inserted in front of |before|. If |before| is null, it
+ // is inserted at the end. Parent takes ownership of the added child.
+ void AddChild(UIElement* child, UIElement* before = nullptr);
+
+ // Remove |child| out of vector |children_| but |child| is not destroyed.
+ // The caller is responsible for destroying |child|.
+ void RemoveChild(UIElement* child);
+
+ // Move |child| to position new_index in |children_|.
+ void ReorderChild(UIElement* child, int new_index);
+
+ virtual void GetBounds(gfx::Rect* bounds) const = 0;
+ virtual void SetBounds(const gfx::Rect& bounds) = 0;
+ virtual void GetVisible(bool* visible) const = 0;
+ virtual void SetVisible(bool visible) = 0;
+
+ // If element exists, return its associated native window and its bounds.
+ // Otherwise, return null and empty bounds.
+ virtual std::pair<aura::Window*, gfx::Rect> GetNodeWindowAndBounds()
+ const = 0;
+
+ template <typename BackingT, typename T>
+ static BackingT* GetBackingElement(UIElement* element) {
+ return T::From(element);
+ };
+
+ protected:
+ UIElement(const UIElementType type,
+ UIElementDelegate* delegate,
+ UIElement* parent);
+
+ private:
+ const int node_id_;
+ const UIElementType type_;
+ std::vector<UIElement*> children_;
+ UIElement* parent_;
+ UIElementDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIElement);
+};
+
+} // namespace ui_devtools
+
+#endif // COMPONENTS_UI_DEVTOOLS_VIEWS_UI_ELEMENT_H_
diff --git a/chromium/components/ui_devtools/views/ui_element_delegate.h b/chromium/components/ui_devtools/views/ui_element_delegate.h
new file mode 100644
index 00000000000..966a71edb1b
--- /dev/null
+++ b/chromium/components/ui_devtools/views/ui_element_delegate.h
@@ -0,0 +1,38 @@
+// 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_UI_DEVTOOLS_VIEWS_UI_ELEMENT_DELEGATE_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_UI_ELEMENT_DELEGATE_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "ui/aura/window.h"
+
+namespace ui_devtools {
+
+class UIElement;
+
+class UIElementDelegate {
+ public:
+ UIElementDelegate(){};
+ virtual ~UIElementDelegate(){};
+
+ virtual void OnUIElementAdded(UIElement* parent, UIElement* child) = 0;
+
+ // Move |child| to different sibling index under |parent| in DOM tree.
+ virtual void OnUIElementReordered(UIElement* parent, UIElement* child) = 0;
+
+ // Remove ui_element in DOM tree.
+ virtual void OnUIElementRemoved(UIElement* ui_element) = 0;
+
+ // Update CSS agent when bounds change.
+ virtual void OnUIElementBoundsChanged(UIElement* ui_element) = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(UIElementDelegate);
+};
+
+} // namespace ui_devtools
+
+#endif // COMPONENTS_UI_DEVTOOLS_VIEWS_UI_ELEMENT_DELEGATE_H_
diff --git a/chromium/components/ui_devtools/views/view_element.cc b/chromium/components/ui_devtools/views/view_element.cc
new file mode 100644
index 00000000000..e7080110587
--- /dev/null
+++ b/chromium/components/ui_devtools/views/view_element.cc
@@ -0,0 +1,87 @@
+// 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/ui_devtools/views/view_element.h"
+
+#include "components/ui_devtools/views/ui_element_delegate.h"
+#include "ui/views/widget/widget.h"
+
+namespace ui_devtools {
+
+ViewElement::ViewElement(views::View* view,
+ UIElementDelegate* ui_element_delegate,
+ UIElement* parent)
+ : UIElement(UIElementType::VIEW, ui_element_delegate, parent), view_(view) {
+ view_->AddObserver(this);
+}
+
+ViewElement::~ViewElement() {
+ view_->RemoveObserver(this);
+}
+
+void ViewElement::OnChildViewRemoved(views::View* parent, views::View* view) {
+ DCHECK_EQ(parent, view_);
+ auto iter = std::find_if(
+ children().begin(), children().end(), [view](UIElement* child) {
+ return view ==
+ UIElement::GetBackingElement<views::View, ViewElement>(child);
+ });
+ DCHECK(iter != children().end());
+ UIElement* child_element = *iter;
+ RemoveChild(child_element);
+ delete child_element;
+}
+
+void ViewElement::OnChildViewAdded(views::View* parent, views::View* view) {
+ DCHECK_EQ(parent, view_);
+ AddChild(new ViewElement(view, delegate(), this),
+ children().empty() ? nullptr : children().back());
+}
+
+void ViewElement::OnChildViewReordered(views::View* parent, views::View* view) {
+ DCHECK_EQ(parent, view_);
+ auto iter = std::find_if(
+ children().begin(), children().end(), [view](UIElement* child) {
+ return view ==
+ UIElement::GetBackingElement<views::View, ViewElement>(child);
+ });
+ DCHECK(iter != children().end());
+ UIElement* child_element = *iter;
+ ReorderChild(child_element, parent->GetIndexOf(view));
+}
+
+void ViewElement::OnViewBoundsChanged(views::View* view) {
+ DCHECK_EQ(view_, view);
+ delegate()->OnUIElementBoundsChanged(this);
+}
+
+void ViewElement::GetBounds(gfx::Rect* bounds) const {
+ *bounds = view_->bounds();
+}
+
+void ViewElement::SetBounds(const gfx::Rect& bounds) {
+ view_->SetBoundsRect(bounds);
+}
+
+void ViewElement::GetVisible(bool* visible) const {
+ *visible = view_->visible();
+}
+
+void ViewElement::SetVisible(bool visible) {
+ view_->SetVisible(visible);
+}
+
+std::pair<aura::Window*, gfx::Rect> ViewElement::GetNodeWindowAndBounds()
+ const {
+ return std::make_pair(view_->GetWidget()->GetNativeWindow(),
+ view_->GetBoundsInScreen());
+}
+
+// static
+views::View* ViewElement::From(UIElement* element) {
+ DCHECK_EQ(UIElementType::VIEW, element->type());
+ return static_cast<ViewElement*>(element)->view_;
+}
+
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/views/view_element.h b/chromium/components/ui_devtools/views/view_element.h
new file mode 100644
index 00000000000..37e46cc81c4
--- /dev/null
+++ b/chromium/components/ui_devtools/views/view_element.h
@@ -0,0 +1,49 @@
+// 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_UI_DEVTOOLS_VIEWS_VIEW_ELEMENT_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_VIEW_ELEMENT_H_
+
+#include "base/macros.h"
+#include "components/ui_devtools/views/ui_element.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/view.h"
+#include "ui/views/view_observer.h"
+
+namespace ui_devtools {
+
+class UIElementDelegate;
+
+class ViewElement : public views::ViewObserver, public UIElement {
+ public:
+ ViewElement(views::View* view,
+ UIElementDelegate* ui_element_delegate,
+ UIElement* parent);
+ ~ViewElement() override;
+ views::View* view() const { return view_; };
+
+ // views::ViewObserver
+ void OnChildViewRemoved(views::View* parent, views::View* view) override;
+ void OnChildViewAdded(views::View* parent, views::View* view) override;
+ void OnChildViewReordered(views::View* parent, views::View*) override;
+ void OnViewBoundsChanged(views::View* view) override;
+
+ // UIElement:
+ void GetBounds(gfx::Rect* bounds) const override;
+ void SetBounds(const gfx::Rect& bounds) override;
+ void GetVisible(bool* visible) const override;
+ void SetVisible(bool visible) override;
+ std::pair<aura::Window*, gfx::Rect> GetNodeWindowAndBounds() const override;
+ static views::View* From(UIElement* element);
+
+ private:
+ views::View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewElement);
+};
+
+} // namespace ui_devtools
+
+#endif // COMPONENTS_UI_DEVTOOLS_VIEWS_VIEW_ELEMENT_H_
diff --git a/chromium/components/ui_devtools/views/widget_element.cc b/chromium/components/ui_devtools/views/widget_element.cc
new file mode 100644
index 00000000000..7f87219bdad
--- /dev/null
+++ b/chromium/components/ui_devtools/views/widget_element.cc
@@ -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.
+
+#include "components/ui_devtools/views/widget_element.h"
+
+#include "components/ui_devtools/views/ui_element_delegate.h"
+
+namespace ui_devtools {
+
+WidgetElement::WidgetElement(views::Widget* widget,
+ UIElementDelegate* ui_element_delegate,
+ UIElement* parent)
+ : UIElement(UIElementType::WIDGET, ui_element_delegate, parent),
+ widget_(widget) {
+ widget_->AddRemovalsObserver(this);
+}
+
+WidgetElement::~WidgetElement() {
+ widget_->RemoveRemovalsObserver(this);
+}
+
+void WidgetElement::OnWillRemoveView(views::Widget* widget, views::View* view) {
+ if (view != widget->GetRootView())
+ return;
+ DCHECK_EQ(1u, children().size());
+ UIElement* child = children()[0];
+ RemoveChild(child);
+ delete child;
+}
+
+void WidgetElement::OnWidgetBoundsChanged(views::Widget* widget,
+ const gfx::Rect& new_bounds) {
+ DCHECK_EQ(widget, widget_);
+ delegate()->OnUIElementBoundsChanged(this);
+}
+
+void WidgetElement::GetBounds(gfx::Rect* bounds) const {
+ *bounds = widget_->GetRestoredBounds();
+}
+
+void WidgetElement::SetBounds(const gfx::Rect& bounds) {
+ widget_->SetBounds(bounds);
+}
+
+void WidgetElement::GetVisible(bool* visible) const {
+ *visible = widget_->IsVisible();
+}
+
+void WidgetElement::SetVisible(bool visible) {
+ if (visible == widget_->IsVisible())
+ return;
+ if (visible)
+ widget_->Show();
+ else
+ widget_->Hide();
+}
+
+std::pair<aura::Window*, gfx::Rect> WidgetElement::GetNodeWindowAndBounds()
+ const {
+ return std::make_pair(widget_->GetNativeWindow(),
+ widget_->GetWindowBoundsInScreen());
+}
+
+// static
+views::Widget* WidgetElement::From(UIElement* element) {
+ DCHECK_EQ(UIElementType::WIDGET, element->type());
+ return static_cast<WidgetElement*>(element)->widget_;
+}
+
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/views/widget_element.h b/chromium/components/ui_devtools/views/widget_element.h
new file mode 100644
index 00000000000..9acc5ebfcce
--- /dev/null
+++ b/chromium/components/ui_devtools/views/widget_element.h
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UI_DEVTOOLS_VIEWS_WIDGET_ELEMENT_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_WIDGET_ELEMENT_H_
+
+#include "base/macros.h"
+#include "components/ui_devtools/views/ui_element.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+#include "ui/views/widget/widget_removals_observer.h"
+
+namespace ui_devtools {
+
+class UIElementDelegate;
+
+class WidgetElement : public views::WidgetRemovalsObserver,
+ public views::WidgetObserver,
+ public UIElement {
+ public:
+ WidgetElement(views::Widget* widget,
+ UIElementDelegate* ui_element_delegate,
+ UIElement* parent);
+ ~WidgetElement() override;
+ views::Widget* widget() const { return widget_; };
+
+ // views::WidgetRemovalsObserver:
+ void OnWillRemoveView(views::Widget* widget, views::View* view) override;
+
+ // views::WidgetObserver:
+ void OnWidgetBoundsChanged(views::Widget* widget,
+ const gfx::Rect& new_bounds) override;
+
+ // UIElement:
+ void GetBounds(gfx::Rect* bounds) const override;
+ void SetBounds(const gfx::Rect& bounds) override;
+ void GetVisible(bool* visible) const override;
+ void SetVisible(bool visible) override;
+ std::pair<aura::Window*, gfx::Rect> GetNodeWindowAndBounds() const override;
+
+ static views::Widget* From(UIElement* element);
+
+ private:
+ views::Widget* widget_;
+
+ DISALLOW_COPY_AND_ASSIGN(WidgetElement);
+};
+
+} // namespace ui_devtools
+
+#endif // COMPONENTS_UI_DEVTOOLS_VIEWS_WIDGET_ELEMENT_H_
diff --git a/chromium/components/ui_devtools/views/window_element.cc b/chromium/components/ui_devtools/views/window_element.cc
new file mode 100644
index 00000000000..951d13792e8
--- /dev/null
+++ b/chromium/components/ui_devtools/views/window_element.cc
@@ -0,0 +1,96 @@
+// 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/ui_devtools/views/window_element.h"
+
+#include "components/ui_devtools/views/ui_element_delegate.h"
+#include "ui/aura/window.h"
+
+namespace ui_devtools {
+namespace {
+
+int GetIndexOfChildInParent(aura::Window* window) {
+ const aura::Window::Windows& siblings = window->parent()->children();
+ auto it = std::find(siblings.begin(), siblings.end(), window);
+ DCHECK(it != siblings.end());
+ return std::distance(siblings.begin(), it);
+}
+
+} // namespace
+
+WindowElement::WindowElement(aura::Window* window,
+ UIElementDelegate* ui_element_delegate,
+ UIElement* parent)
+ : UIElement(UIElementType::WINDOW, ui_element_delegate, parent),
+ window_(window) {
+ if (window)
+ window_->AddObserver(this);
+}
+
+WindowElement::~WindowElement() {
+ if (window_)
+ window_->RemoveObserver(this);
+}
+
+// Handles removing window_.
+void WindowElement::OnWindowHierarchyChanging(
+ const aura::WindowObserver::HierarchyChangeParams& params) {
+ if (params.target == window_) {
+ parent()->RemoveChild(this);
+ delete this;
+ }
+}
+
+// Handles adding window_.
+void WindowElement::OnWindowHierarchyChanged(
+ const aura::WindowObserver::HierarchyChangeParams& params) {
+ if (window_ == params.new_parent && params.receiver == params.new_parent) {
+ AddChild(new WindowElement(params.target, delegate(), this),
+ children().empty() ? nullptr : children().back());
+ }
+}
+
+void WindowElement::OnWindowStackingChanged(aura::Window* window) {
+ DCHECK_EQ(window_, window);
+ parent()->ReorderChild(this, GetIndexOfChildInParent(window));
+}
+
+void WindowElement::OnWindowBoundsChanged(aura::Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ DCHECK_EQ(window_, window);
+ delegate()->OnUIElementBoundsChanged(this);
+}
+
+void WindowElement::GetBounds(gfx::Rect* bounds) const {
+ *bounds = window_->bounds();
+}
+
+void WindowElement::SetBounds(const gfx::Rect& bounds) {
+ window_->SetBounds(bounds);
+}
+
+void WindowElement::GetVisible(bool* visible) const {
+ *visible = window_->IsVisible();
+}
+
+void WindowElement::SetVisible(bool visible) {
+ if (visible)
+ window_->Show();
+ else
+ window_->Hide();
+}
+
+std::pair<aura::Window*, gfx::Rect> WindowElement::GetNodeWindowAndBounds()
+ const {
+ return std::make_pair(window_, window_->GetBoundsInScreen());
+}
+
+// static
+aura::Window* WindowElement::From(UIElement* element) {
+ DCHECK_EQ(UIElementType::WINDOW, element->type());
+ return static_cast<WindowElement*>(element)->window_;
+}
+
+} // namespace ui_devtools
diff --git a/chromium/components/ui_devtools/views/window_element.h b/chromium/components/ui_devtools/views/window_element.h
new file mode 100644
index 00000000000..2574eff9156
--- /dev/null
+++ b/chromium/components/ui_devtools/views/window_element.h
@@ -0,0 +1,51 @@
+// 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_UI_DEVTOOLS_VIEWS_WINDOW_ELEMENT_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_WINDOW_ELEMENT_H_
+
+#include "base/macros.h"
+#include "components/ui_devtools/views/ui_element.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace ui_devtools {
+
+class WindowElement : public aura::WindowObserver, public UIElement {
+ public:
+ WindowElement(aura::Window* window,
+ UIElementDelegate* ui_element_delegate,
+ UIElement* parent);
+ ~WindowElement() override;
+ aura::Window* window() const { return window_; };
+
+ // WindowObserver:
+ void OnWindowHierarchyChanging(
+ const aura::WindowObserver::HierarchyChangeParams& params) override;
+ void OnWindowHierarchyChanged(
+ const aura::WindowObserver::HierarchyChangeParams& params) override;
+ void OnWindowStackingChanged(aura::Window* window) override;
+ void OnWindowBoundsChanged(aura::Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) override;
+
+ // UIElement:
+ void GetBounds(gfx::Rect* bounds) const override;
+ void SetBounds(const gfx::Rect& bounds) override;
+ void GetVisible(bool* visible) const override;
+ void SetVisible(bool visible) override;
+ std::pair<aura::Window*, gfx::Rect> GetNodeWindowAndBounds() const override;
+
+ static aura::Window* From(UIElement* element);
+
+ private:
+ aura::Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowElement);
+};
+
+} // namespace ui_devtools
+
+#endif // COMPONENTS_UI_DEVTOOLS_VIEWS_WINDOW_ELEMENT_H_
diff --git a/chromium/components/ui_metrics/BUILD.gn b/chromium/components/ui_metrics/BUILD.gn
new file mode 100644
index 00000000000..941ed1c5eef
--- /dev/null
+++ b/chromium/components/ui_metrics/BUILD.gn
@@ -0,0 +1,9 @@
+# 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.
+
+source_set("ui_metrics") {
+ sources = [
+ "sadtab_metrics_types.h",
+ ]
+}
diff --git a/chromium/components/ui_metrics/sadtab_metrics_types.h b/chromium/components/ui_metrics/sadtab_metrics_types.h
new file mode 100644
index 00000000000..056541aecd6
--- /dev/null
+++ b/chromium/components/ui_metrics/sadtab_metrics_types.h
@@ -0,0 +1,28 @@
+// 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_UI_METRICS_SADTAB_METRICS_TYPES_H_
+#define COMPONENTS_UI_METRICS_SADTAB_METRICS_TYPES_H_
+
+namespace ui_metrics {
+// An enum for reporting interaction events to a UMA histogram.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.tab
+enum class SadTabEvent {
+ // Records that the Sad Tab was displayed.
+ DISPLAYED = 0,
+ // Records that the main Sad Tab button was triggered.
+ BUTTON_CLICKED = 1,
+ // Records that the Sad Tab help link was triggered.
+ HELP_LINK_CLICKED = 2,
+ // Enum end marker.
+ MAX_SAD_TAB_EVENT = 3,
+};
+
+// Describes the mode of the Sad Tab as being in 'reload' mode.
+const char kSadTabReloadHistogramKey[] = "Tabs.SadTab.Reload.Event";
+// Describes the mode of the Sad Tab as being in 'feedback' mode.
+const char kSadTabFeedbackHistogramKey[] = "Tabs.SadTab.Feedback.Event";
+}
+
+#endif // COMPONENTS_UI_METRICS_SADTAB_METRICS_TYPES_H_
diff --git a/chromium/components/ukm/BUILD.gn b/chromium/components/ukm/BUILD.gn
index ab5807fd644..6dbee54fc86 100644
--- a/chromium/components/ukm/BUILD.gn
+++ b/chromium/components/ukm/BUILD.gn
@@ -27,8 +27,8 @@ static_library("ukm") {
public_deps = [
"//components/metrics/proto",
- "//components/ukm/public",
- "//components/ukm/public/interfaces",
+ "//services/metrics/public/cpp:metrics_cpp",
+ "//services/metrics/public/interfaces",
]
deps = [
@@ -41,6 +41,23 @@ static_library("ukm") {
]
}
+static_library("ukm_interface") {
+ sources = [
+ "ukm_interface.cc",
+ "ukm_interface.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//services/metrics/public/interfaces",
+ ]
+
+ deps = [
+ ":ukm",
+ "//mojo/public/cpp/bindings",
+ ]
+}
+
# Helper library for observing signals that we need to clear any local data.
static_library("observers") {
sources = [
@@ -72,6 +89,7 @@ static_library("test_support") {
"//base",
"//components/metrics:test_support",
"//components/prefs:test_support",
+ "//testing/gtest:gtest",
]
}
@@ -94,6 +112,7 @@ source_set("unit_tests") {
"//components/sync:test_support_driver",
"//components/variations",
"//net:test_support",
+ "//services/metrics/public/cpp:ukm_builders",
"//testing/gtest",
"//third_party/zlib/google:compression_utils",
"//url",
diff --git a/chromium/components/ukm/DEPS b/chromium/components/ukm/DEPS
index abd3591f71b..099b24d0d0c 100644
--- a/chromium/components/ukm/DEPS
+++ b/chromium/components/ukm/DEPS
@@ -3,5 +3,6 @@ include_rules = [
"+components/prefs",
"+components/variations",
"+mojo/public",
+ "+services/metrics/public",
"+third_party/zlib/google",
]
diff --git a/chromium/components/ukm/debug_page/debug_page.cc b/chromium/components/ukm/debug_page/debug_page.cc
index 72df62fea95..23a774772ee 100644
--- a/chromium/components/ukm/debug_page/debug_page.cc
+++ b/chromium/components/ukm/debug_page/debug_page.cc
@@ -46,7 +46,8 @@ void DebugPage::StartDataRequest(
UkmService* ukm_service = service_getter_.Run();
if (ukm_service) {
data.append(
- base::StringPrintf("<p>IsEnabled:%s</p>",
+ // 'id' attribute set so tests can extract this element.
+ base::StringPrintf("<p>IsEnabled:<span id='state'>%s</span></p>",
ukm_service->recording_enabled_ ? "True" : "False"));
data.append(base::StringPrintf("<p>ClientId:%" PRIu64 "</p>",
ukm_service->client_id_));
diff --git a/chromium/components/ukm/observers/sync_disable_observer_unittest.cc b/chromium/components/ukm/observers/sync_disable_observer_unittest.cc
index 4e4080e38fb..05f9f2f7fad 100644
--- a/chromium/components/ukm/observers/sync_disable_observer_unittest.cc
+++ b/chromium/components/ukm/observers/sync_disable_observer_unittest.cc
@@ -28,7 +28,7 @@ class MockSyncService : public syncer::FakeSyncService {
}
}
- void Shutdown() {
+ void Shutdown() override {
for (auto& observer : observers_) {
observer.OnSyncShutdown(this);
}
diff --git a/chromium/components/ukm/public/BUILD.gn b/chromium/components/ukm/public/BUILD.gn
deleted file mode 100644
index 1383752327d..00000000000
--- a/chromium/components/ukm/public/BUILD.gn
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-component("public") {
- sources = [
- "ukm_entry_builder.cc",
- "ukm_entry_builder.h",
- "ukm_export.h",
- "ukm_recorder.cc",
- "ukm_recorder.h",
- ]
-
- defines = [ "UKM_IMPLEMENTATION" ]
-
- public_deps = [
- "//base",
- "//components/ukm/public/interfaces",
- "//url",
- ]
-
- deps = [
- "//mojo/public/cpp/bindings",
- ]
-}
diff --git a/chromium/components/ukm/public/interfaces/BUILD.gn b/chromium/components/ukm/public/interfaces/BUILD.gn
deleted file mode 100644
index b4804bedb30..00000000000
--- a/chromium/components/ukm/public/interfaces/BUILD.gn
+++ /dev/null
@@ -1,15 +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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("interfaces") {
- sources = [
- "ukm_interface.mojom",
- ]
-
- public_deps = [
- "//url/mojo:url_mojom_gurl",
- ]
-}
diff --git a/chromium/components/ukm/public/interfaces/OWNERS b/chromium/components/ukm/public/interfaces/OWNERS
deleted file mode 100644
index 08850f42120..00000000000
--- a/chromium/components/ukm/public/interfaces/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/ukm/public/interfaces/ukm_interface.mojom b/chromium/components/ukm/public/interfaces/ukm_interface.mojom
deleted file mode 100644
index 0625916b88a..00000000000
--- a/chromium/components/ukm/public/interfaces/ukm_interface.mojom
+++ /dev/null
@@ -1,22 +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.
-
-module ukm.mojom;
-
-import "url/mojo/url.mojom";
-
-struct UkmMetric {
- uint64 metric_hash;
- int64 value;
-};
-
-struct UkmEntry {
- int64 source_id;
- uint64 event_hash;
- array<UkmMetric> metrics;
-};
-
-interface UkmRecorderInterface {
- AddEntry(UkmEntry entry);
-};
diff --git a/chromium/components/ukm/public/ukm_entry_builder.cc b/chromium/components/ukm/public/ukm_entry_builder.cc
deleted file mode 100644
index 72a227acc08..00000000000
--- a/chromium/components/ukm/public/ukm_entry_builder.cc
+++ /dev/null
@@ -1,32 +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/ukm/public/ukm_entry_builder.h"
-
-#include <memory>
-
-#include "base/metrics/metrics_hashes.h"
-#include "components/ukm/public/interfaces/ukm_interface.mojom.h"
-
-namespace ukm {
-
-UkmEntryBuilder::UkmEntryBuilder(
- const UkmEntryBuilder::AddEntryCallback& callback,
- ukm::SourceId source_id,
- const char* event_name)
- : add_entry_callback_(callback), entry_(mojom::UkmEntry::New()) {
- entry_->source_id = source_id;
- entry_->event_hash = base::HashMetricName(event_name);
-}
-
-UkmEntryBuilder::~UkmEntryBuilder() {
- add_entry_callback_.Run(std::move(entry_));
-}
-
-void UkmEntryBuilder::AddMetric(const char* metric_name, int64_t value) {
- entry_->metrics.emplace_back(
- mojom::UkmMetric::New(base::HashMetricName(metric_name), value));
-}
-
-} // namespace ukm
diff --git a/chromium/components/ukm/public/ukm_entry_builder.h b/chromium/components/ukm/public/ukm_entry_builder.h
deleted file mode 100644
index 1c9facfe039..00000000000
--- a/chromium/components/ukm/public/ukm_entry_builder.h
+++ /dev/null
@@ -1,52 +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_UKM_UKM_ENTRY_BUILDER_H
-#define COMPONENTS_UKM_UKM_ENTRY_BUILDER_H
-
-#include <string>
-
-#include "base/macros.h"
-#include "components/ukm/public/interfaces/ukm_interface.mojom.h"
-#include "components/ukm/public/ukm_export.h"
-
-namespace ukm {
-
-typedef int64_t SourceId;
-
-// The builder that builds UkmEntry and adds it to UkmRecorder.
-// The example usage is:
-//
-// {
-// unique_ptr<UkmEntryBuilder> builder =
-// ukm_recorder->GetEntryBuilder(source_id, "PageLoad");
-// builder->AddMetric("NavigationStart", navigation_start_time);
-// builder->AddMetric("ResponseStart", response_start_time);
-// builder->AddMetric("FirstPaint", first_paint_time);
-// builder->AddMetric("FirstContentfulPaint", fcp_time);
-// }
-//
-// When there exists an added metric, the builder will automatically add the
-// UkmEntry to UkmService upon destruction when going out of scope.
-class UKM_EXPORT UkmEntryBuilder {
- public:
- using AddEntryCallback = base::Callback<void(mojom::UkmEntryPtr)>;
- UkmEntryBuilder(const AddEntryCallback& callback,
- ukm::SourceId source_id,
- const char* event_name);
- ~UkmEntryBuilder();
-
- // Add metric to the entry. A metric contains a metric name and value.
- void AddMetric(const char* metric_name, int64_t value);
-
- private:
- AddEntryCallback add_entry_callback_;
- mojom::UkmEntryPtr entry_;
-
- DISALLOW_COPY_AND_ASSIGN(UkmEntryBuilder);
-};
-
-} // namespace ukm
-
-#endif // COMPONENTS_UKM_UKM_ENTRY_BUILDER_H
diff --git a/chromium/components/ukm/public/ukm_export.h b/chromium/components/ukm/public/ukm_export.h
deleted file mode 100644
index 6c460eb5834..00000000000
--- a/chromium/components/ukm/public/ukm_export.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_UKM_PUBLIC_UKM_EXPORT_H_
-#define COMPONENTS_UKM_PUBLIC_UKM_EXPORT_H_
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined(UKM_IMPLEMENTATION)
-#define UKM_EXPORT __declspec(dllexport)
-#else
-#define UKM_EXPORT __declspec(dllimport)
-#endif // defined(UKM_IMPLEMENTATION)
-
-#else // defined(WIN32)
-#if defined(UKM_IMPLEMENTATION)
-#define UKM_EXPORT __attribute__((visibility("default")))
-#else
-#define UKM_EXPORT
-#endif
-#endif
-
-#else // defined(COMPONENT_BUILD)
-#define UKM_EXPORT
-#endif
-
-#endif // COMPONENTS_UKM_PUBLIC_UKM_EXPORT_H_
diff --git a/chromium/components/ukm/public/ukm_recorder.cc b/chromium/components/ukm/public/ukm_recorder.cc
deleted file mode 100644
index 25fdcb822f1..00000000000
--- a/chromium/components/ukm/public/ukm_recorder.cc
+++ /dev/null
@@ -1,47 +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/ukm/public/ukm_recorder.h"
-
-#include "base/atomic_sequence_num.h"
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/memory/ptr_util.h"
-#include "components/ukm/public/ukm_entry_builder.h"
-
-namespace ukm {
-
-UkmRecorder* g_ukm_recorder = nullptr;
-
-const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT};
-
-UkmRecorder::UkmRecorder() {
- DCHECK(!g_ukm_recorder);
- g_ukm_recorder = this;
-}
-
-UkmRecorder::~UkmRecorder() {
- g_ukm_recorder = nullptr;
-}
-
-// static
-UkmRecorder* UkmRecorder::Get() {
- return g_ukm_recorder;
-}
-
-// static
-ukm::SourceId UkmRecorder::GetNewSourceID() {
- static base::StaticAtomicSequenceNumber seq;
- return static_cast<ukm::SourceId>(seq.GetNext());
-}
-
-std::unique_ptr<UkmEntryBuilder> UkmRecorder::GetEntryBuilder(
- ukm::SourceId source_id,
- const char* event_name) {
- return base::MakeUnique<UkmEntryBuilder>(
- base::Bind(&UkmRecorder::AddEntry, base::Unretained(this)), source_id,
- event_name);
-}
-
-} // namespace ukm
diff --git a/chromium/components/ukm/public/ukm_recorder.h b/chromium/components/ukm/public/ukm_recorder.h
deleted file mode 100644
index 74cd232d509..00000000000
--- a/chromium/components/ukm/public/ukm_recorder.h
+++ /dev/null
@@ -1,107 +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_UKM_PUBLIC_UKM_RECORDER_H_
-#define COMPONENTS_UKM_PUBLIC_UKM_RECORDER_H_
-
-#include <stddef.h>
-
-#include "base/callback.h"
-#include "base/feature_list.h"
-#include "base/macros.h"
-#include "base/threading/thread_checker.h"
-#include "components/ukm/public/interfaces/ukm_interface.mojom.h"
-#include "components/ukm/public/ukm_entry_builder.h"
-#include "components/ukm/public/ukm_export.h"
-#include "url/gurl.h"
-
-class ContextualSearchRankerLoggerImpl;
-class PluginInfoMessageFilter;
-class UkmPageLoadMetricsObserver;
-
-namespace autofill {
-class AutofillMetrics;
-}
-
-namespace content {
-class MediaInternals;
-class RenderFrameImpl;
-}
-
-namespace translate {
-class TranslateRankerImpl;
-}
-
-namespace payments {
-class JourneyLogger;
-}
-
-namespace ukm {
-
-class UkmEntryBuilder;
-class UkmInterface;
-class TestRecordingHelper;
-
-// This feature controls whether UkmService should be created.
-UKM_EXPORT extern const base::Feature kUkmFeature;
-
-typedef int64_t SourceId;
-
-// Interface for recording UKM
-class UKM_EXPORT UkmRecorder {
- public:
- UkmRecorder();
- virtual ~UkmRecorder();
-
- // Provides access to a previously constructed UkmRecorder instance. Only one
- // instance exists per process and must have been constructed prior to any
- // calls to this method.
- static UkmRecorder* Get();
-
- // Get the new source ID, which is unique for the duration of a browser
- // session.
- static SourceId GetNewSourceID();
-
- // Update the URL on the source keyed to the given source ID. If the source
- // does not exist, it will create a new UkmSource object.
- virtual void UpdateSourceURL(SourceId source_id, const GURL& url) = 0;
-
- private:
- friend autofill::AutofillMetrics;
- friend payments::JourneyLogger;
- friend ContextualSearchRankerLoggerImpl;
- friend PluginInfoMessageFilter;
- friend UkmPageLoadMetricsObserver;
- friend translate::TranslateRankerImpl;
- friend TestRecordingHelper;
- friend UkmInterface;
- friend content::MediaInternals;
- friend content::RenderFrameImpl;
- FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, AddEntryWithEmptyMetrics);
- FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, EntryBuilderAndSerialization);
- FRIEND_TEST_ALL_PREFIXES(UkmServiceTest,
- LogsUploadedOnlyWhenHavingSourcesOrEntries);
- FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, MetricsProviderTest);
- FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, PersistAndPurge);
- FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, WhitelistEntryTest);
-
- // Get a new UkmEntryBuilder object for the specified source ID and event,
- // which can get metrics added to.
- //
- // This API being private is intentional. Any client using UKM needs to
- // declare itself to be a friend of UkmService and go through code review
- // process.
- std::unique_ptr<UkmEntryBuilder> GetEntryBuilder(SourceId source_id,
- const char* event_name);
-
- private:
- // Add an entry to the UkmEntry list.
- virtual void AddEntry(mojom::UkmEntryPtr entry) = 0;
-
- DISALLOW_COPY_AND_ASSIGN(UkmRecorder);
-};
-
-} // namespace ukm
-
-#endif // COMPONENTS_UKM_PUBLIC_UKM_RECORDER_H_
diff --git a/chromium/components/ukm/test_ukm_recorder.cc b/chromium/components/ukm/test_ukm_recorder.cc
index 26a1307fede..faab026bb74 100644
--- a/chromium/components/ukm/test_ukm_recorder.cc
+++ b/chromium/components/ukm/test_ukm_recorder.cc
@@ -4,17 +4,59 @@
#include "components/ukm/test_ukm_recorder.h"
+#include <algorithm>
+#include <iterator>
+
#include "base/logging.h"
#include "base/metrics/metrics_hashes.h"
#include "components/ukm/ukm_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
namespace ukm {
+namespace {
+
+// Provides a single merged ukm::mojom::UkmEntry proto that contains all metrics
+// from the given |entries|. |entries| must be non-empty, and all |entries| must
+// have the same |source_id| and |event_hash|.
+mojom::UkmEntryPtr GetMergedEntry(
+ const std::vector<const mojom::UkmEntry*>& entries) {
+ EXPECT_FALSE(entries.empty());
+ mojom::UkmEntryPtr merged_entry = mojom::UkmEntry::New();
+ for (const auto* entry : entries) {
+ if (merged_entry->event_hash) {
+ EXPECT_EQ(merged_entry->source_id, entry->source_id);
+ EXPECT_EQ(merged_entry->event_hash, entry->event_hash);
+ } else {
+ merged_entry->event_hash = entry->event_hash;
+ merged_entry->source_id = entry->source_id;
+ }
+ for (const auto& metric : entry->metrics) {
+ merged_entry->metrics.emplace_back(metric->Clone());
+ }
+ }
+ return merged_entry;
+}
+
+std::vector<int64_t> GetValuesForMetric(const mojom::UkmEntry* entry,
+ const char* metric_name) {
+ std::vector<int64_t> values;
+ const uint64_t metric_hash = base::HashMetricName(metric_name);
+ for (const auto& metric : entry->metrics) {
+ if (metric->metric_hash == metric_hash)
+ values.push_back(metric->value);
+ }
+ return values;
+}
+
+} // namespace
+
TestUkmRecorder::TestUkmRecorder() {
EnableRecording();
}
-TestUkmRecorder::~TestUkmRecorder() = default;
+TestUkmRecorder::~TestUkmRecorder() {
+};
const UkmSource* TestUkmRecorder::GetSourceForUrl(const char* url) const {
const UkmSource* source = nullptr;
@@ -27,8 +69,18 @@ const UkmSource* TestUkmRecorder::GetSourceForUrl(const char* url) const {
return source;
}
+std::vector<const UkmSource*> TestUkmRecorder::GetSourcesForUrl(
+ const char* url) const {
+ std::vector<const UkmSource*> matching_sources;
+ for (const auto& kv : sources()) {
+ if (kv.second->url() == url)
+ matching_sources.push_back(kv.second.get());
+ }
+ return matching_sources;
+}
+
const UkmSource* TestUkmRecorder::GetSourceForSourceId(
- ukm::SourceId source_id) const {
+ SourceId source_id) const {
const UkmSource* source = nullptr;
for (const auto& kv : sources()) {
if (kv.second->id() == source_id) {
@@ -64,4 +116,142 @@ const mojom::UkmMetric* TestUkmRecorder::FindMetric(
return nullptr;
}
+std::vector<const mojom::UkmEntry*> TestUkmRecorder::GetEntriesForSourceID(
+ SourceId source_id,
+ const char* event_name) const {
+ const uint64_t event_hash = base::HashMetricName(event_name);
+ std::vector<const mojom::UkmEntry*> entries;
+ for (size_t i = 0; i < entries_count(); ++i) {
+ const mojom::UkmEntry* entry = GetEntry(i);
+ if (entry->source_id == source_id && entry->event_hash == event_hash) {
+ entries.push_back(entry);
+ }
+ }
+ return entries;
+}
+
+mojom::UkmEntryPtr TestUkmRecorder::GetMergedEntryForSourceID(
+ SourceId source_id,
+ const char* event_name) const {
+ mojom::UkmEntryPtr entry =
+ GetMergedEntry(GetEntriesForSourceID(source_id, event_name));
+ EXPECT_EQ(source_id, entry->source_id);
+ EXPECT_EQ(base::HashMetricName(event_name), entry->event_hash);
+ return entry;
+}
+
+bool TestUkmRecorder::HasEntry(const UkmSource& source,
+ const char* event_name) const {
+ return CountEntries(source, event_name) > 0;
+}
+
+int TestUkmRecorder::CountEntries(const UkmSource& source,
+ const char* event_name) const {
+ return GetEntriesForSourceID(source.id(), event_name).size();
+}
+
+void TestUkmRecorder::ExpectEntry(
+ const UkmSource& source,
+ const char* event_name,
+ const std::vector<std::pair<const char*, int64_t>>& expected_metrics)
+ const {
+ // Produce a sorted view of the expected metrics, since order is not
+ // significant.
+ std::vector<std::pair<uint64_t, int64_t>> sorted_expected_metrics;
+ std::transform(expected_metrics.begin(), expected_metrics.end(),
+ std::back_inserter(sorted_expected_metrics),
+ [](const std::pair<const char*, int64_t>& metric) {
+ return std::make_pair(base::HashMetricName(metric.first),
+ metric.second);
+ });
+ std::sort(sorted_expected_metrics.begin(), sorted_expected_metrics.end());
+
+ std::vector<const mojom::UkmEntry*> candidate_entries =
+ GetEntriesForSourceID(source.id(), event_name);
+
+ // Produce a view of each matching entry's metrics that can be compared with
+ // sorted_expected_metrics.
+ std::vector<std::vector<std::pair<uint64_t, int64_t>>> candidate_metrics;
+ std::transform(
+ candidate_entries.begin(), candidate_entries.end(),
+ std::back_inserter(candidate_metrics),
+ [](const mojom::UkmEntry* candidate_entry) {
+ std::vector<std::pair<uint64_t, int64_t>> metrics;
+ std::transform(
+ candidate_entry->metrics.begin(), candidate_entry->metrics.end(),
+ std::back_inserter(metrics), [](const mojom::UkmMetricPtr& metric) {
+ return std::make_pair(metric->metric_hash, metric->value);
+ });
+ std::sort(metrics.begin(), metrics.end());
+ return metrics;
+ });
+
+ if (std::find(candidate_metrics.begin(), candidate_metrics.end(),
+ sorted_expected_metrics) == candidate_metrics.end()) {
+ FAIL() << "Failed to find metrics for event: " << event_name;
+ }
+}
+
+int TestUkmRecorder::CountMetricsForEventName(const UkmSource& source,
+ const char* event_name) const {
+ mojom::UkmEntryPtr entry = GetMergedEntryForSourceID(source.id(), event_name);
+ return entry.get() ? entry->metrics.size() : 0;
+}
+
+bool TestUkmRecorder::HasMetric(const UkmSource& source,
+ const char* event_name,
+ const char* metric_name) const {
+ return CountMetrics(source, event_name, metric_name) > 0;
+}
+
+int TestUkmRecorder::CountMetrics(const UkmSource& source,
+ const char* event_name,
+ const char* metric_name) const {
+ mojom::UkmEntryPtr entry = GetMergedEntryForSourceID(source.id(), event_name);
+ return GetValuesForMetric(entry.get(), metric_name).size();
+}
+
+void TestUkmRecorder::ExpectMetric(const UkmSource& source,
+ const char* event_name,
+ const char* metric_name,
+ int64_t expected_value) const {
+ ExpectMetrics(source, event_name, metric_name, {expected_value});
+}
+
+void TestUkmRecorder::ExpectMetrics(
+ const UkmSource& source,
+ const char* event_name,
+ const char* metric_name,
+ const std::vector<int64_t>& expected_values) const {
+ mojom::UkmEntryPtr entry = GetMergedEntryForSourceID(source.id(), event_name);
+
+ // Make sure both vectors are sorted before comparing, since order is not
+ // significant.
+ std::vector<int64_t> sorted_expected_values(expected_values);
+ std::sort(sorted_expected_values.begin(), sorted_expected_values.end());
+
+ std::vector<int64_t> actual_values =
+ GetValuesForMetric(entry.get(), metric_name);
+ std::sort(actual_values.begin(), actual_values.end());
+
+ EXPECT_EQ(actual_values, sorted_expected_values)
+ << "Failed to find expected_values for metric: " << metric_name;
+}
+
+std::vector<int64_t> TestUkmRecorder::GetMetrics(
+ const UkmSource& source,
+ const char* event_name,
+ const char* metric_name) const {
+ mojom::UkmEntryPtr entry = GetMergedEntryForSourceID(source.id(), event_name);
+ return GetValuesForMetric(entry.get(), metric_name);
+}
+
+TestAutoSetUkmRecorder::TestAutoSetUkmRecorder() {
+ UkmRecorder::Set(this);
+}
+
+TestAutoSetUkmRecorder::~TestAutoSetUkmRecorder() {
+ UkmRecorder::Set(nullptr);
+};
+
} // namespace ukm
diff --git a/chromium/components/ukm/test_ukm_recorder.h b/chromium/components/ukm/test_ukm_recorder.h
index ef989b9dca6..1d9a50612e1 100644
--- a/chromium/components/ukm/test_ukm_recorder.h
+++ b/chromium/components/ukm/test_ukm_recorder.h
@@ -6,10 +6,16 @@
#define COMPONENTS_UKM_TEST_UKM_RECORDER_H_
#include <stddef.h>
+
#include <memory>
+#include <utility>
+#include <vector>
+#include "base/compiler_specific.h"
#include "base/macros.h"
#include "components/ukm/ukm_recorder_impl.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/interfaces/ukm_interface.mojom.h"
namespace ukm {
@@ -25,19 +31,102 @@ class TestUkmRecorder : public UkmRecorderImpl {
return sources();
}
const UkmSource* GetSourceForUrl(const char* url) const;
+ std::vector<const ukm::UkmSource*> GetSourcesForUrl(const char* url) const;
const UkmSource* GetSourceForSourceId(ukm::SourceId source_id) const;
size_t entries_count() const { return entries().size(); }
+
+ // Returns whether the given UKM |source| has an entry with the given
+ // |event_name|.
+ bool HasEntry(const ukm::UkmSource& source,
+ const char* event_name) const WARN_UNUSED_RESULT;
+
+ // Returns the number of metrics recorded for the given UKM |source| and
+ // |event_name|.
+ int CountMetricsForEventName(const ukm::UkmSource& source,
+ const char* event_name) const;
+
+ // Returns whether a metric with the given |metric_name| was recorded for the
+ // given UKM |source| and |event_name|.
+ bool HasMetric(const ukm::UkmSource& source,
+ const char* event_name,
+ const char* metric_name) const WARN_UNUSED_RESULT;
+
+ // Returns the number of metrics recorded with the given |metric_name|, for
+ // the given UKM |source| and |event_name|.
+ int CountMetrics(const ukm::UkmSource& source,
+ const char* event_name,
+ const char* metric_name) const WARN_UNUSED_RESULT;
+
+ // Expects that a single metric was recorded with the given |expected_value|,
+ // for the given |source| and |event_name|. This is shorthand for calling
+ // ExpectMetrics with a single value in the expected values vector.
+ void ExpectMetric(const ukm::UkmSource& source,
+ const char* event_name,
+ const char* metric_name,
+ int64_t expected_value) const;
+
+ // Expects that metrics were recorded with the given |expected_values|, for
+ // the given |source| and |event_name|. The order of |expected_values| is not
+ // significant.
+ void ExpectMetrics(const ukm::UkmSource& source,
+ const char* event_name,
+ const char* metric_name,
+ const std::vector<int64_t>& expected_values) const;
+
+ // Returns all collected metrics for the given |source|, |event_name| and
+ // |metric_name|. The order of values is not specified.
+ std::vector<int64_t> GetMetrics(const UkmSource& source,
+ const char* event_name,
+ const char* metric_name) const;
+
+ // UKM entries that may be recorded multiple times for a single source may
+ // need to verify that an expected number of UKM entries were logged. The
+ // utility methods below can be used to verify expected entries. UKM entries
+ // that aren't recorded multiple times per source should prefer using
+ // HasMetric/CountMetrics/ExpectMetric(s) above.
+
+ // Returns the number of entries recorded with the given |event_name|, for the
+ // given UKM |source|.
+ int CountEntries(const ukm::UkmSource& source, const char* event_name) const;
+
+ // Expects that an entry with the given |expected_metrics| was recorded, for
+ // the given |source| and |event_name|. The order of |expected_metrics| is not
+ // significant. |expected_metrics| contains (metric name,metric value) pairs.
+ void ExpectEntry(const ukm::UkmSource& source,
+ const char* event_name,
+ const std::vector<std::pair<const char*, int64_t>>&
+ expected_metrics) const;
+
+ // Deprecated.
const mojom::UkmEntry* GetEntry(size_t entry_num) const;
+ // Deprecated.
const mojom::UkmEntry* GetEntryForEntryName(const char* entry_name) const;
+ // Deprecated.
static const mojom::UkmMetric* FindMetric(const mojom::UkmEntry* entry,
const char* metric_name);
private:
+ ukm::mojom::UkmEntryPtr GetMergedEntryForSourceID(
+ ukm::SourceId source_id,
+ const char* event_name) const;
+
+ std::vector<const ukm::mojom::UkmEntry*> GetEntriesForSourceID(
+ ukm::SourceId source_id,
+ const char* event_name) const;
+
DISALLOW_COPY_AND_ASSIGN(TestUkmRecorder);
};
+// Similar to a TestUkmRecorder, but also sets itself as the global UkmRecorder
+// on construction, and unsets itself on destruction.
+class TestAutoSetUkmRecorder : public TestUkmRecorder {
+ public:
+ TestAutoSetUkmRecorder();
+ ~TestAutoSetUkmRecorder() override;
+};
+
} // namespace ukm
#endif // COMPONENTS_UKM_TEST_UKM_RECORDER_H_
diff --git a/chromium/components/ukm/ukm_interface.cc b/chromium/components/ukm/ukm_interface.cc
new file mode 100644
index 00000000000..c0eb4de7f4a
--- /dev/null
+++ b/chromium/components/ukm/ukm_interface.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/ukm/ukm_interface.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/memory/ptr_util.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "url/gurl.h"
+
+namespace ukm {
+
+namespace {
+
+// Map source ids from different instances into unique namespaces, so that
+// clients can create thier own IDs without having them collide.
+// This won't be necessary once we switch to using CoordinationUnitIDs.
+int64_t ConvertSourceId(int64_t source_id, int64_t instance_id) {
+ const int64_t low_bits = (INT64_C(1) << 32) - 1;
+ // Neither ID should get large enough to cause an issue, but explicitly
+ // discard down to 32 bits anyway.
+ return ((instance_id & low_bits) << 32) | (source_id & low_bits);
+}
+
+} // namespace
+
+UkmInterface::UkmInterface(UkmRecorder* ukm_recorder, int64_t instance_id)
+ : ukm_recorder_(ukm_recorder), instance_id_(instance_id) {}
+
+UkmInterface::~UkmInterface() = default;
+
+// static
+void UkmInterface::Create(UkmRecorder* ukm_recorder,
+ mojom::UkmRecorderInterfaceRequest request) {
+ static base::AtomicSequenceNumber seq;
+ mojo::MakeStrongBinding(
+ base::MakeUnique<UkmInterface>(ukm_recorder,
+ static_cast<int64_t>(seq.GetNext()) + 1),
+ std::move(request));
+}
+
+void UkmInterface::AddEntry(mojom::UkmEntryPtr ukm_entry) {
+ ukm_entry->source_id = ConvertSourceId(instance_id_, ukm_entry->source_id);
+ ukm_recorder_->AddEntry(std::move(ukm_entry));
+}
+
+void UkmInterface::UpdateSourceURL(int64_t source_id, const std::string& url) {
+ ukm_recorder_->UpdateSourceURL(ConvertSourceId(instance_id_, source_id),
+ GURL(url));
+}
+
+} // namespace ukm
diff --git a/chromium/components/ukm/ukm_interface.h b/chromium/components/ukm/ukm_interface.h
new file mode 100644
index 00000000000..9432de1fa7d
--- /dev/null
+++ b/chromium/components/ukm/ukm_interface.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_UKM_UKM_INTERFACE_H_
+#define COMPONENTS_UKM_UKM_INTERFACE_H_
+
+#include "services/metrics/public/interfaces/ukm_interface.mojom.h"
+
+namespace ukm {
+
+class UkmRecorder;
+
+class UkmInterface : public mojom::UkmRecorderInterface {
+ public:
+ UkmInterface(UkmRecorder* ukm_recorder, int64_t instance_id);
+ ~UkmInterface() override;
+
+ static void Create(UkmRecorder* ukm_recorder,
+ mojom::UkmRecorderInterfaceRequest request);
+
+ private:
+ // ukm::mojom::UkmRecorderInterface:
+ void AddEntry(mojom::UkmEntryPtr entry) override;
+ void UpdateSourceURL(int64_t source_id, const std::string& url) override;
+
+ UkmRecorder* ukm_recorder_;
+ int64_t instance_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(UkmInterface);
+};
+
+} // namespace ukm
+
+#endif // COMPONENTS_UKM_UKM_INTERFACE_H_
diff --git a/chromium/components/ukm/ukm_recorder_impl.h b/chromium/components/ukm/ukm_recorder_impl.h
index 50976e436d0..1a20ddab4d1 100644
--- a/chromium/components/ukm/ukm_recorder_impl.h
+++ b/chromium/components/ukm/ukm_recorder_impl.h
@@ -10,8 +10,8 @@
#include <vector>
#include "base/threading/thread_checker.h"
-#include "components/ukm/public/interfaces/ukm_interface.mojom.h"
-#include "components/ukm/public/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/interfaces/ukm_interface.mojom.h"
namespace metrics {
class UkmBrowserTest;
diff --git a/chromium/components/ukm/ukm_service.cc b/chromium/components/ukm/ukm_service.cc
index 312ba041dd1..c38c2fc634c 100644
--- a/chromium/components/ukm/ukm_service.cc
+++ b/chromium/components/ukm/ukm_service.cc
@@ -98,10 +98,13 @@ UkmService::UkmService(PrefService* pref_service,
provider->Init();
StoreWhitelistedEntries();
+
+ UkmRecorder::Set(this);
}
UkmService::~UkmService() {
DisableReporting();
+ UkmRecorder::Set(nullptr);
}
void UkmService::Initialize() {
diff --git a/chromium/components/ukm/ukm_service_unittest.cc b/chromium/components/ukm/ukm_service_unittest.cc
index 3f3e6432c8a..4059680ef70 100644
--- a/chromium/components/ukm/ukm_service_unittest.cc
+++ b/chromium/components/ukm/ukm_service_unittest.cc
@@ -19,10 +19,11 @@
#include "components/metrics/test_metrics_service_client.h"
#include "components/prefs/testing_pref_service.h"
#include "components/ukm/persisted_logs_metrics_impl.h"
-#include "components/ukm/public/ukm_entry_builder.h"
#include "components/ukm/ukm_pref_names.h"
#include "components/ukm/ukm_source.h"
#include "components/variations/variations_associated_data.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_entry_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/compression_utils.h"
@@ -292,10 +293,7 @@ TEST_F(UkmServiceTest, AddEntryWithEmptyMetrics) {
ukm::SourceId id = UkmRecorder::GetNewSourceID();
recorder.UpdateSourceURL(id, GURL("https://google.com/foobar"));
- {
- std::unique_ptr<UkmEntryBuilder> builder =
- service.GetEntryBuilder(id, "PageLoad");
- }
+ { ::ukm::builders::PageLoad(id).Record(&service); }
service.Flush();
EXPECT_EQ(1, GetPersistedLogCount());
Report proto_report = GetPersistedReport();
@@ -322,9 +320,9 @@ TEST_F(UkmServiceTest, MetricsProviderTest) {
ukm::SourceId id = UkmRecorder::GetNewSourceID();
recorder.UpdateSourceURL(id, GURL("https://google.com/foobar"));
{
- std::unique_ptr<UkmEntryBuilder> builder =
- service.GetEntryBuilder(id, "PageLoad");
- builder->AddMetric("FirstContentfulPaint", 300);
+ ::ukm::builders::PageLoad(id)
+ .SetPaintTiming_NavigationToFirstContentfulPaint(300)
+ .Record(&service);
}
service.Flush();
EXPECT_EQ(GetPersistedLogCount(), 1);
diff --git a/chromium/components/update_client/BUILD.gn b/chromium/components/update_client/BUILD.gn
index 1408a4292d0..545210a9a5a 100644
--- a/chromium/components/update_client/BUILD.gn
+++ b/chromium/components/update_client/BUILD.gn
@@ -6,6 +6,9 @@ import("//net/features.gni")
static_library("update_client") {
sources = [
+ "action_runner.cc",
+ "action_runner.h",
+ "action_runner_win.cc",
"background_downloader_win.cc",
"background_downloader_win.h",
"component.cc",
@@ -114,6 +117,7 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx",
"//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx",
"//components/test/data/update_client/jebgalgnebhfojomionfpkfelancnnkf.crx",
+ "//components/test/data/update_client/runaction_test_win.crx3",
"//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",
@@ -151,6 +155,7 @@ source_set("unit_tests") {
":unit_tests_bundle_data",
":update_client",
"//base",
+ "//components/crx_file",
"//components/prefs",
"//components/prefs:test_support",
"//components/version_info:version_info",
diff --git a/chromium/components/update_client/action_runner.cc b/chromium/components/update_client/action_runner.cc
new file mode 100644
index 00000000000..0cc68d02861
--- /dev/null
+++ b/chromium/components/update_client/action_runner.cc
@@ -0,0 +1,95 @@
+// 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/update_client/action_runner.h"
+
+#include <iterator>
+#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/sequenced_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/update_client/component.h"
+#include "components/update_client/update_client.h"
+
+namespace update_client {
+
+ActionRunner::ActionRunner(
+ const Component& component,
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ const std::vector<uint8_t>& key_hash)
+ : component_(component),
+ task_runner_(task_runner),
+ key_hash_(key_hash),
+ main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+
+ActionRunner::~ActionRunner() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void ActionRunner::Run(const Callback& run_complete) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ run_complete_ = run_complete;
+
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ActionRunner::Unpack, base::Unretained(this)));
+}
+
+void ActionRunner::Unpack() {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ const auto& installer = component_.crx_component().installer;
+
+ base::FilePath file_path;
+ installer->GetInstalledFile(component_.action_run(), &file_path);
+
+ auto unpacker = base::MakeRefCounted<ComponentUnpacker>(
+ key_hash_, file_path, installer, nullptr, task_runner_);
+
+ unpacker->Unpack(
+ base::Bind(&ActionRunner::UnpackComplete, base::Unretained(this)));
+}
+
+void ActionRunner::UnpackComplete(const ComponentUnpacker::Result& result) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ if (result.error != UnpackerError::kNone) {
+ DCHECK(!base::DirectoryExists(result.unpack_path));
+
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(run_complete_, false, static_cast<int>(result.error),
+ result.extended_error));
+ return;
+ }
+
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ActionRunner::RunCommand, base::Unretained(this),
+ MakeCommandLine(result.unpack_path)));
+}
+
+#if !defined(OS_WIN)
+
+void ActionRunner::RunCommand(const base::CommandLine& cmdline) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ main_task_runner_->PostTask(FROM_HERE,
+ base::Bind(run_complete_, false, -1, 0));
+}
+
+base::CommandLine ActionRunner::MakeCommandLine(
+ const base::FilePath& unpack_path) const {
+ return base::CommandLine(base::CommandLine::NO_PROGRAM);
+}
+
+#endif // OS_WIN
+
+} // namespace update_client
diff --git a/chromium/components/update_client/action_runner.h b/chromium/components/update_client/action_runner.h
new file mode 100644
index 00000000000..b253ae0ccff
--- /dev/null
+++ b/chromium/components/update_client/action_runner.h
@@ -0,0 +1,70 @@
+// 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_UPDATE_CLIENT_ACTION_RUNNER_H_
+#define COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/component_unpacker.h"
+
+namespace base {
+class CommandLine;
+class Process;
+class SequencedTaskRunner;
+}
+
+namespace update_client {
+
+class Component;
+
+class ActionRunner {
+ public:
+ using Callback =
+ base::Callback<void(bool succeeded, int error_code, int extra_code1)>;
+
+ ActionRunner(const Component& component,
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ const std::vector<uint8_t>& key_hash);
+ ~ActionRunner();
+
+ void Run(const Callback& run_complete);
+
+ private:
+ void Unpack();
+ void UnpackComplete(const ComponentUnpacker::Result& result);
+
+ void RunCommand(const base::CommandLine& cmdline);
+
+ base::CommandLine MakeCommandLine(const base::FilePath& unpack_path) const;
+
+ void WaitForCommand(base::Process process);
+
+ const Component& component_;
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner_;
+
+ // Contains the key hash of the CRX this object is allowed to run. This value
+ // is using during the unpacking of the CRX to verify its integrity.
+ const std::vector<uint8_t> key_hash_;
+
+ // Used to post callbacks to the main thread.
+ scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
+
+ Callback run_complete_;
+
+ THREAD_CHECKER(thread_checker_);
+ DISALLOW_COPY_AND_ASSIGN(ActionRunner);
+};
+
+} // namespace update_client
+
+#endif // COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_
diff --git a/chromium/components/update_client/action_runner_win.cc b/chromium/components/update_client/action_runner_win.cc
new file mode 100644
index 00000000000..f6ce555a90e
--- /dev/null
+++ b/chromium/components/update_client/action_runner_win.cc
@@ -0,0 +1,57 @@
+// 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/update_client/action_runner.h"
+
+#include <utility>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_scheduler/post_task.h"
+
+namespace {
+
+const base::FilePath::CharType kRecoveryFileName[] =
+ FILE_PATH_LITERAL("ChromeRecovery.exe");
+
+} // namespace
+
+namespace update_client {
+
+void ActionRunner::RunCommand(const base::CommandLine& cmdline) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ base::LaunchOptions options;
+ options.start_hidden = true;
+ base::Process process = base::LaunchProcess(cmdline, options);
+
+ // This task joins a process, hence .WithBaseSyncPrimitives().
+ base::PostTaskWithTraits(
+ FROM_HERE,
+ {base::WithBaseSyncPrimitives(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+ base::BindOnce(&ActionRunner::WaitForCommand, base::Unretained(this),
+ std::move(process)));
+}
+
+void ActionRunner::WaitForCommand(base::Process process) {
+ int exit_code = 0;
+ const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromSeconds(600);
+ const bool succeeded =
+ process.WaitForExitWithTimeout(kMaxWaitTime, &exit_code);
+
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(run_complete_, succeeded, exit_code, 0));
+}
+
+base::CommandLine ActionRunner::MakeCommandLine(
+ const base::FilePath& unpack_path) const {
+ return base::CommandLine(unpack_path.Append(kRecoveryFileName));
+}
+
+} // namespace update_client
diff --git a/chromium/components/update_client/background_downloader_win.cc b/chromium/components/update_client/background_downloader_win.cc
index 2c3766ff1bd..feaef33a682 100644
--- a/chromium/components/update_client/background_downloader_win.cc
+++ b/chromium/components/update_client/background_downloader_win.cc
@@ -7,8 +7,9 @@
#include <atlbase.h>
#include <atlcom.h>
#include <objbase.h>
-#include <stddef.h>
+#include <winerror.h>
+#include <stddef.h>
#include <stdint.h>
#include <functional>
#include <iomanip>
@@ -22,14 +23,19 @@
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
+#include "base/strings/string_piece.h"
#include "base/strings/sys_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "base/win/scoped_co_mem.h"
+#include "components/update_client/update_client_errors.h"
#include "components/update_client/utils.h"
#include "url/gurl.h"
using base::win::ScopedCoMem;
-using base::win::ScopedComPtr;
+using Microsoft::WRL::ComPtr;
// The class BackgroundDownloader in this module is an adapter between
// the CrxDownloader interface and the BITS service interfaces.
@@ -103,7 +109,7 @@ namespace {
// All jobs created by this module have a specific description so they can
// be found at run-time or by using system administration tools.
-const base::char16 kJobDescription[] = L"Chrome Component Updater";
+const base::char16 kJobName[] = L"Chrome Component Updater";
// How often the code looks for changes in the BITS job state.
const int kJobPollingIntervalSec = 4;
@@ -127,18 +133,21 @@ const int kSetNoProgressTimeoutDays = 1;
// system restarts, etc. Also, the check to purge stale jobs only happens
// at most once a day. If the job clean up code is not running, the BITS
// default policy is to cancel jobs after 90 days of inactivity.
-const int kPurgeStaleJobsAfterDays = 7;
+const int kPurgeStaleJobsAfterDays = 3;
const int kPurgeStaleJobsIntervalBetweenChecksDays = 1;
+// Number of maximum BITS jobs this downloader can create and queue up.
+const int kMaxQueuedJobs = 10;
+
// Retrieves the singleton instance of GIT for this process.
-HRESULT GetGit(ScopedComPtr<IGlobalInterfaceTable>* git) {
+HRESULT GetGit(ComPtr<IGlobalInterfaceTable>* git) {
return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(git->GetAddressOf()));
}
// Retrieves an interface pointer from the process GIT for a given |cookie|.
-HRESULT GetInterfaceFromGit(IGlobalInterfaceTable* git,
+HRESULT GetInterfaceFromGit(const ComPtr<IGlobalInterfaceTable>& git,
DWORD cookie,
REFIID riid,
void** ppv) {
@@ -147,8 +156,8 @@ HRESULT GetInterfaceFromGit(IGlobalInterfaceTable* git,
// Registers an interface pointer in GIT and returns its corresponding |cookie|.
template <typename T>
-HRESULT RegisterInterfaceInGit(const ScopedComPtr<IGlobalInterfaceTable>& git,
- const ScopedComPtr<T>& p,
+HRESULT RegisterInterfaceInGit(const ComPtr<IGlobalInterfaceTable>& git,
+ const ComPtr<T>& p,
DWORD* cookie) {
return git->RegisterInterfaceInGlobal(p.Get(), __uuidof(T), cookie);
}
@@ -167,9 +176,9 @@ int GetHttpStatusFromBitsError(HRESULT error) {
}
// Returns the files in a BITS job.
-HRESULT GetFilesInJob(IBackgroundCopyJob* job,
- std::vector<ScopedComPtr<IBackgroundCopyFile>>* files) {
- ScopedComPtr<IEnumBackgroundCopyFiles> enum_files;
+HRESULT GetFilesInJob(const ComPtr<IBackgroundCopyJob>& job,
+ std::vector<ComPtr<IBackgroundCopyFile>>* files) {
+ ComPtr<IEnumBackgroundCopyFiles> enum_files;
HRESULT hr = job->EnumFiles(enum_files.GetAddressOf());
if (FAILED(hr))
return hr;
@@ -180,7 +189,7 @@ HRESULT GetFilesInJob(IBackgroundCopyJob* job,
return hr;
for (ULONG i = 0; i != num_files; ++i) {
- ScopedComPtr<IBackgroundCopyFile> file;
+ ComPtr<IBackgroundCopyFile> file;
if (enum_files->Next(1, file.GetAddressOf(), NULL) == S_OK && file.Get())
files->push_back(file);
}
@@ -190,7 +199,7 @@ HRESULT GetFilesInJob(IBackgroundCopyJob* job,
// Returns the file name, the url, and some per-file progress information.
// The function out parameters can be NULL if that data is not requested.
-HRESULT GetJobFileProperties(IBackgroundCopyFile* file,
+HRESULT GetJobFileProperties(const ComPtr<IBackgroundCopyFile>& file,
base::string16* local_name,
base::string16* remote_name,
BG_FILE_PROGRESS* progress) {
@@ -229,7 +238,7 @@ HRESULT GetJobFileProperties(IBackgroundCopyFile* file,
// Returns the number of bytes downloaded and bytes to download for all files
// in the job. If the values are not known or if an error has occurred,
// a value of -1 is reported.
-HRESULT GetJobByteCount(IBackgroundCopyJob* job,
+HRESULT GetJobByteCount(const ComPtr<IBackgroundCopyJob>& job,
int64_t* downloaded_bytes,
int64_t* total_bytes) {
*downloaded_bytes = -1;
@@ -255,17 +264,23 @@ HRESULT GetJobByteCount(IBackgroundCopyJob* job,
return S_OK;
}
-HRESULT GetJobDescription(IBackgroundCopyJob* job, const base::string16* name) {
- ScopedCoMem<base::char16> description;
- return job->GetDescription(&description);
+HRESULT GetJobDisplayName(const ComPtr<IBackgroundCopyJob>& job,
+ base::string16* name) {
+ ScopedCoMem<base::char16> local_name;
+ const HRESULT hr = job->GetDisplayName(&local_name);
+ if (FAILED(hr))
+ return hr;
+ *name = local_name.get();
+ return S_OK;
}
// Returns the job error code in |error_code| if the job is in the transient
// or the final error state. Otherwise, the job error is not available and
// the function fails.
-HRESULT GetJobError(IBackgroundCopyJob* job, HRESULT* error_code_out) {
+HRESULT GetJobError(const ComPtr<IBackgroundCopyJob>& job,
+ HRESULT* error_code_out) {
*error_code_out = S_OK;
- ScopedComPtr<IBackgroundCopyError> copy_error;
+ ComPtr<IBackgroundCopyError> copy_error;
HRESULT hr = job->GetError(copy_error.GetAddressOf());
if (FAILED(hr))
return hr;
@@ -285,9 +300,9 @@ HRESULT GetJobError(IBackgroundCopyJob* job, HRESULT* error_code_out) {
// no job was found, and it returns an error otherwise.
template <class Predicate>
HRESULT FindBitsJobIf(Predicate pred,
- IBackgroundCopyManager* bits_manager,
- std::vector<ScopedComPtr<IBackgroundCopyJob>>* jobs) {
- ScopedComPtr<IEnumBackgroundCopyJobs> enum_jobs;
+ const ComPtr<IBackgroundCopyManager>& bits_manager,
+ std::vector<ComPtr<IBackgroundCopyJob>>* jobs) {
+ ComPtr<IEnumBackgroundCopyJobs> enum_jobs;
HRESULT hr = bits_manager->EnumJobs(0, enum_jobs.GetAddressOf());
if (FAILED(hr))
return hr;
@@ -300,12 +315,12 @@ HRESULT FindBitsJobIf(Predicate pred,
// Iterate over jobs, run the predicate, and select the job only if
// the job description matches the component updater jobs.
for (ULONG i = 0; i != job_count; ++i) {
- ScopedComPtr<IBackgroundCopyJob> current_job;
+ ComPtr<IBackgroundCopyJob> current_job;
if (enum_jobs->Next(1, current_job.GetAddressOf(), NULL) == S_OK &&
- pred(current_job.Get())) {
- base::string16 job_description;
- hr = GetJobDescription(current_job.Get(), &job_description);
- if (job_description.compare(kJobDescription) == 0)
+ pred(current_job)) {
+ base::string16 job_name;
+ hr = GetJobDisplayName(current_job, &job_name);
+ if (job_name.compare(kJobName) == 0)
jobs->push_back(current_job);
}
}
@@ -313,51 +328,29 @@ HRESULT FindBitsJobIf(Predicate pred,
return jobs->empty() ? S_FALSE : S_OK;
}
-// Compares the job creation time and returns true if the job creation time
-// is older than |num_days|.
-class JobCreationOlderThanDays {
- public:
- explicit JobCreationOlderThanDays(int num_days) : num_days_(num_days) {}
- bool operator()(IBackgroundCopyJob* job) const;
-
- private:
- int num_days_;
-};
-
-bool JobCreationOlderThanDays::operator()(IBackgroundCopyJob* job) const {
+bool JobCreationOlderThanDaysPredicate(ComPtr<IBackgroundCopyJob> job,
+ int num_days) {
BG_JOB_TIMES times = {};
HRESULT hr = job->GetTimes(&times);
if (FAILED(hr))
return false;
- const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days_));
+ const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days));
const base::Time creation_time(base::Time::FromFileTime(times.CreationTime));
return creation_time + time_delta < base::Time::Now();
}
-// Compares the url of a file in a job and returns true if the remote name
-// of any file in a job matches the argument.
-class JobFileUrlEqual {
- public:
- explicit JobFileUrlEqual(const base::string16& remote_name)
- : remote_name_(remote_name) {}
- bool operator()(IBackgroundCopyJob* job) const;
-
- private:
- base::string16 remote_name_;
-};
-
-bool JobFileUrlEqual::operator()(IBackgroundCopyJob* job) const {
- std::vector<ScopedComPtr<IBackgroundCopyFile>> files;
+bool JobFileUrlEqualPredicate(ComPtr<IBackgroundCopyJob> job, const GURL& url) {
+ std::vector<ComPtr<IBackgroundCopyFile>> files;
HRESULT hr = GetFilesInJob(job, &files);
if (FAILED(hr))
return false;
for (size_t i = 0; i != files.size(); ++i) {
- ScopedCoMem<base::char16> name;
- if (SUCCEEDED(files[i]->GetRemoteName(&name)) &&
- remote_name_.compare(name) == 0)
+ ScopedCoMem<base::char16> remote_name;
+ if (SUCCEEDED(files[i]->GetRemoteName(&remote_name)) &&
+ url == GURL(base::StringPiece16(remote_name)))
return true;
}
@@ -365,67 +358,52 @@ bool JobFileUrlEqual::operator()(IBackgroundCopyJob* job) const {
}
// Creates an instance of the BITS manager.
-HRESULT CreateBitsManager(IBackgroundCopyManager** bits_manager) {
- ScopedComPtr<IBackgroundCopyManager> object;
- HRESULT hr = ::CoCreateInstance(__uuidof(BackgroundCopyManager), nullptr,
- CLSCTX_ALL, IID_PPV_ARGS(&object));
+HRESULT CreateBitsManager(ComPtr<IBackgroundCopyManager>* bits_manager) {
+ ComPtr<IBackgroundCopyManager> local_bits_manager;
+ HRESULT hr =
+ ::CoCreateInstance(__uuidof(BackgroundCopyManager), nullptr, CLSCTX_ALL,
+ IID_PPV_ARGS(&local_bits_manager));
if (FAILED(hr)) {
return hr;
}
- *bits_manager = object.Detach();
+ *bits_manager = local_bits_manager;
return S_OK;
}
-void CleanupJobFiles(IBackgroundCopyJob* job) {
- std::vector<ScopedComPtr<IBackgroundCopyFile>> files;
- if (FAILED(GetFilesInJob(job, &files)))
+void CleanupJob(const ComPtr<IBackgroundCopyJob>& job) {
+ if (!job)
return;
- for (size_t i = 0; i != files.size(); ++i) {
+
+ // Get the file paths associated with this job before canceling the job.
+ // Canceling the job removes it from the BITS queue right away. It appears
+ // that it is still possible to query for the properties of the job after
+ // the job has been canceled. It seems safer though to get the paths first.
+ std::vector<ComPtr<IBackgroundCopyFile>> files;
+ GetFilesInJob(job, &files);
+
+ std::vector<base::FilePath> paths;
+ for (const auto& file : files) {
base::string16 local_name;
- HRESULT hr(GetJobFileProperties(files[i].Get(), &local_name, NULL, NULL));
+ HRESULT hr = GetJobFileProperties(file, &local_name, NULL, NULL);
if (SUCCEEDED(hr))
- DeleteFileAndEmptyParentDirectory(base::FilePath(local_name));
+ paths.push_back(base::FilePath(local_name));
}
-}
-
-// Cleans up incompleted jobs that are too old.
-HRESULT CleanupStaleJobs(
- const ScopedComPtr<IBackgroundCopyManager>& bits_manager) {
- if (!bits_manager.Get())
- return E_FAIL;
-
- static base::Time last_sweep;
-
- const base::TimeDelta time_delta(
- base::TimeDelta::FromDays(kPurgeStaleJobsIntervalBetweenChecksDays));
- const base::Time current_time(base::Time::Now());
- if (last_sweep + time_delta > current_time)
- return S_OK;
-
- last_sweep = current_time;
-
- std::vector<ScopedComPtr<IBackgroundCopyJob>> jobs;
- HRESULT hr = FindBitsJobIf(
- JobCreationOlderThanDays(kPurgeStaleJobsAfterDays),
- bits_manager.Get(), &jobs);
- if (FAILED(hr))
- return hr;
- for (size_t i = 0; i != jobs.size(); ++i) {
- jobs[i]->Cancel();
- CleanupJobFiles(jobs[i].Get());
- }
+ job->Cancel();
- return S_OK;
+ for (const auto& path : paths)
+ DeleteFileAndEmptyParentDirectory(path);
}
} // namespace
BackgroundDownloader::BackgroundDownloader(
std::unique_ptr<CrxDownloader> successor,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner)
- : CrxDownloader(task_runner, std::move(successor)),
+ net::URLRequestContextGetter* context_getter)
+ : CrxDownloader(std::move(successor)),
+ com_task_runner_(base::CreateCOMSTATaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
context_getter_(context_getter),
git_cookie_bits_manager_(0),
git_cookie_job_(0) {}
@@ -443,25 +421,21 @@ void BackgroundDownloader::StartTimer() {
void BackgroundDownloader::OnTimer() {
DCHECK(thread_checker_.CalledOnValidThread());
- task_runner()->PostTask(
+ com_task_runner_->PostTask(
FROM_HERE,
base::Bind(&BackgroundDownloader::OnDownloading, base::Unretained(this)));
}
-bool BackgroundDownloader::TimerIsRunning() const {
- return timer_.get() && timer_->IsRunning();
-}
-
void BackgroundDownloader::DoStartDownload(const GURL& url) {
DCHECK(thread_checker_.CalledOnValidThread());
- task_runner()->PostTask(FROM_HERE,
- base::Bind(&BackgroundDownloader::BeginDownload,
- base::Unretained(this), url));
+ com_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&BackgroundDownloader::BeginDownload,
+ base::Unretained(this), url));
}
// Called one time when this class is asked to do a download.
void BackgroundDownloader::BeginDownload(const GURL& url) {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
download_start_time_ = base::TimeTicks::Now();
job_stuck_begin_time_ = download_start_time_;
@@ -478,23 +452,23 @@ void BackgroundDownloader::BeginDownload(const GURL& url) {
base::Bind(&BackgroundDownloader::StartTimer, base::Unretained(this)));
}
-// Creates or opens an existing bits job, and handles the marshalling of
-// the interfaces in GIT.
+// Creates or opens an existing BITS job to download the |url|, and handles
+// the marshalling of the interfaces in GIT.
HRESULT BackgroundDownloader::BeginDownloadHelper(const GURL& url) {
- ScopedComPtr<IGlobalInterfaceTable> git;
+ ComPtr<IGlobalInterfaceTable> git;
HRESULT hr = GetGit(&git);
if (FAILED(hr))
return hr;
- hr = CreateBitsManager(bits_manager_.GetAddressOf());
+ hr = CreateBitsManager(&bits_manager_);
if (FAILED(hr))
return hr;
- hr = RegisterInterfaceInGit(git, bits_manager_, &git_cookie_bits_manager_);
+ hr = QueueBitsJob(url, &job_);
if (FAILED(hr))
return hr;
- hr = QueueBitsJob(url, job_.GetAddressOf());
+ hr = RegisterInterfaceInGit(git, bits_manager_, &git_cookie_bits_manager_);
if (FAILED(hr))
return hr;
@@ -507,7 +481,7 @@ HRESULT BackgroundDownloader::BeginDownloadHelper(const GURL& url) {
// Called any time the timer fires.
void BackgroundDownloader::OnDownloading() {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
HRESULT hr = UpdateInterfacePointers();
if (FAILED(hr)) {
@@ -572,9 +546,7 @@ void BackgroundDownloader::OnDownloading() {
// Completes the BITS download, picks up the file path of the response, and
// notifies the CrxDownloader. The function should be called only once.
void BackgroundDownloader::EndDownload(HRESULT error) {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
-
- DCHECK(!TimerIsRunning());
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
const base::TimeTicks download_end_time(base::TimeTicks::Now());
const base::TimeDelta download_time =
@@ -584,14 +556,10 @@ void BackgroundDownloader::EndDownload(HRESULT error) {
int64_t downloaded_bytes = -1;
int64_t total_bytes = -1;
- GetJobByteCount(job_.Get(), &downloaded_bytes, &total_bytes);
-
- if (FAILED(error) && job_.Get()) {
- job_->Cancel();
- CleanupJobFiles(job_.Get());
- }
+ GetJobByteCount(job_, &downloaded_bytes, &total_bytes);
- CleanupStaleJobs(bits_manager_);
+ if (FAILED(error))
+ CleanupJob(job_);
ClearGit();
@@ -621,9 +589,8 @@ void BackgroundDownloader::EndDownload(HRESULT error) {
base::Unretained(this), is_handled, result, download_metrics));
// Once the task is posted to the the main thread, this object may be deleted
- // by its owner. It is not safe to access members of this object on the
- // task runner from this point on. The timer is stopped and all BITS
- // interface pointers have been released.
+ // by its owner. It is not safe to access members of this object on this task
+ // runner from now on.
}
// Called when the BITS job has been transferred successfully. Completes the
@@ -638,7 +605,7 @@ bool BackgroundDownloader::OnStateTransferred() {
// be made. Cancels this job and removes it from the BITS queue.
bool BackgroundDownloader::OnStateError() {
HRESULT error_code = S_OK;
- HRESULT hr = GetJobError(job_.Get(), &error_code);
+ HRESULT hr = GetJobError(job_, &error_code);
if (FAILED(hr))
error_code = hr;
@@ -673,7 +640,7 @@ bool BackgroundDownloader::OnStateTransientError() {
// Don't retry at all if the transient error was a 5xx.
HRESULT error_code = S_OK;
- HRESULT hr = GetJobError(job_.Get(), &error_code);
+ HRESULT hr = GetJobError(job_, &error_code);
if (SUCCEEDED(hr) &&
IsHttpServerError(GetHttpStatusFromBitsError(error_code))) {
return OnStateError();
@@ -698,7 +665,7 @@ bool BackgroundDownloader::OnStateTransferring() {
int64_t downloaded_bytes = -1;
int64_t total_bytes = -1;
- HRESULT hr = GetJobByteCount(job_.Get(), &downloaded_bytes, &total_bytes);
+ HRESULT hr = GetJobByteCount(job_, &downloaded_bytes, &total_bytes);
if (FAILED(hr))
return false;
@@ -712,75 +679,87 @@ bool BackgroundDownloader::OnStateTransferring() {
return false;
}
-// Creates or opens a job for the given url and queues it up. Tries to
-// install a job observer but continues on if an observer can't be set up.
HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url,
- IBackgroundCopyJob** job) {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
+ ComPtr<IBackgroundCopyJob>* job) {
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
- ScopedComPtr<IBackgroundCopyJob> p;
- HRESULT hr = CreateOrOpenJob(url, p.GetAddressOf());
- if (FAILED(hr))
- return hr;
+ size_t num_jobs = 0;
+ GetBackgroundDownloaderJobCount(&num_jobs);
+ UMA_HISTOGRAM_COUNTS_100("UpdateClient.BackgroundDownloaderJobs", num_jobs);
- if (hr == S_OK) {
- // This is a new job and it needs initialization.
- hr = InitializeNewJob(p, url);
- if (FAILED(hr))
- return hr;
- }
+ // Remove some old jobs from the BITS queue before creating new jobs.
+ CleanupStaleJobs();
- hr = p->Resume();
- if (FAILED(hr))
+ if (num_jobs >= kMaxQueuedJobs)
+ return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF,
+ CrxDownloaderError::BITS_TOO_MANY_JOBS);
+
+ ComPtr<IBackgroundCopyJob> local_job;
+ HRESULT hr = CreateOrOpenJob(url, &local_job);
+ if (FAILED(hr)) {
+ CleanupJob(local_job);
return hr;
+ }
+
+ const bool is_new_job = hr == S_OK;
+ UMA_HISTOGRAM_BOOLEAN("UpdateClient.BackgroundDownloaderNewJob", is_new_job);
- *job = p.Detach();
+ hr = local_job->Resume();
+ if (FAILED(hr)) {
+ CleanupJob(local_job);
+ return hr;
+ }
+ *job = local_job;
return S_OK;
}
HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url,
- IBackgroundCopyJob** job) {
- std::vector<ScopedComPtr<IBackgroundCopyJob>> jobs;
- HRESULT hr = FindBitsJobIf(
- JobFileUrlEqual(base::SysUTF8ToWide(url.spec())),
- bits_manager_.Get(), &jobs);
+ ComPtr<IBackgroundCopyJob>* job) {
+ std::vector<ComPtr<IBackgroundCopyJob>> jobs;
+ HRESULT hr =
+ FindBitsJobIf(std::bind2nd(std::ptr_fun(JobFileUrlEqualPredicate), url),
+ bits_manager_, &jobs);
if (SUCCEEDED(hr) && !jobs.empty()) {
- *job = jobs.front().Detach();
+ *job = jobs.front();
return S_FALSE;
}
- // Use kJobDescription as a temporary job display name until the proper
- // display name is initialized later on.
+ ComPtr<IBackgroundCopyJob> local_job;
+
GUID guid = {0};
- hr = bits_manager_->CreateJob(kJobDescription, BG_JOB_TYPE_DOWNLOAD, &guid,
- job);
- if (FAILED(hr))
+ hr = bits_manager_->CreateJob(kJobName, BG_JOB_TYPE_DOWNLOAD, &guid,
+ local_job.GetAddressOf());
+ if (FAILED(hr)) {
+ CleanupJob(local_job);
return hr;
+ }
+ hr = InitializeNewJob(local_job, url);
+ if (FAILED(hr)) {
+ CleanupJob(local_job);
+ return hr;
+ }
+
+ *job = local_job;
return S_OK;
}
HRESULT BackgroundDownloader::InitializeNewJob(
- const base::win::ScopedComPtr<IBackgroundCopyJob>& job,
+ const ComPtr<IBackgroundCopyJob>& job,
const GURL& url) {
- const base::string16 filename(base::SysUTF8ToWide(url.ExtractFileName()));
-
base::FilePath tempdir;
if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_BITS_"),
&tempdir))
return E_FAIL;
+ const base::string16 filename(base::SysUTF8ToWide(url.ExtractFileName()));
HRESULT hr = job->AddFile(base::SysUTF8ToWide(url.spec()).c_str(),
tempdir.Append(filename).AsUTF16Unsafe().c_str());
if (FAILED(hr))
return hr;
- hr = job->SetDisplayName(filename.c_str());
- if (FAILED(hr))
- return hr;
-
- hr = job->SetDescription(kJobDescription);
+ hr = job->SetDescription(filename.c_str());
if (FAILED(hr))
return hr;
@@ -811,8 +790,8 @@ HRESULT BackgroundDownloader::CompleteJob() {
if (FAILED(hr) && hr != BG_S_UNABLE_TO_DELETE_FILES)
return hr;
- std::vector<ScopedComPtr<IBackgroundCopyFile>> files;
- hr = GetFilesInJob(job_.Get(), &files);
+ std::vector<ComPtr<IBackgroundCopyFile>> files;
+ hr = GetFilesInJob(job_, &files);
if (FAILED(hr))
return hr;
@@ -821,7 +800,7 @@ HRESULT BackgroundDownloader::CompleteJob() {
base::string16 local_name;
BG_FILE_PROGRESS progress = {0};
- hr = GetJobFileProperties(files.front().Get(), &local_name, NULL, &progress);
+ hr = GetJobFileProperties(files.front(), &local_name, NULL, &progress);
if (FAILED(hr))
return hr;
@@ -837,21 +816,23 @@ HRESULT BackgroundDownloader::CompleteJob() {
}
HRESULT BackgroundDownloader::UpdateInterfacePointers() {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+
+ bits_manager_ = nullptr;
+ job_ = nullptr;
- ScopedComPtr<IGlobalInterfaceTable> git;
+ ComPtr<IGlobalInterfaceTable> git;
HRESULT hr = GetGit(&git);
if (FAILED(hr))
return hr;
- bits_manager_ = nullptr;
- hr = GetInterfaceFromGit(git.Get(), git_cookie_bits_manager_,
- IID_PPV_ARGS(&bits_manager_));
+ hr = GetInterfaceFromGit(git, git_cookie_bits_manager_,
+ IID_PPV_ARGS(bits_manager_.GetAddressOf()));
if (FAILED(hr))
return hr;
- job_ = nullptr;
- hr = GetInterfaceFromGit(git.Get(), git_cookie_job_, IID_PPV_ARGS(&job_));
+ hr = GetInterfaceFromGit(git, git_cookie_job_,
+ IID_PPV_ARGS(job_.GetAddressOf()));
if (FAILED(hr))
return hr;
@@ -864,11 +845,11 @@ void BackgroundDownloader::ResetInterfacePointers() {
}
HRESULT BackgroundDownloader::ClearGit() {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
ResetInterfacePointers();
- ScopedComPtr<IGlobalInterfaceTable> git;
+ ComPtr<IGlobalInterfaceTable> git;
HRESULT hr = GetGit(&git);
if (FAILED(hr))
return hr;
@@ -885,4 +866,43 @@ HRESULT BackgroundDownloader::ClearGit() {
return S_OK;
}
+HRESULT BackgroundDownloader::GetBackgroundDownloaderJobCount(
+ size_t* num_jobs) {
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(bits_manager_);
+
+ std::vector<ComPtr<IBackgroundCopyJob>> jobs;
+ const HRESULT hr =
+ FindBitsJobIf([](const ComPtr<IBackgroundCopyJob>&) { return true; },
+ bits_manager_, &jobs);
+ if (FAILED(hr))
+ return hr;
+
+ *num_jobs = jobs.size();
+ return S_OK;
+}
+
+void BackgroundDownloader::CleanupStaleJobs() {
+ DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(bits_manager_);
+
+ static base::Time last_sweep;
+
+ const base::TimeDelta time_delta(
+ base::TimeDelta::FromDays(kPurgeStaleJobsIntervalBetweenChecksDays));
+ const base::Time current_time(base::Time::Now());
+ if (last_sweep + time_delta > current_time)
+ return;
+
+ last_sweep = current_time;
+
+ std::vector<ComPtr<IBackgroundCopyJob>> jobs;
+ FindBitsJobIf(std::bind2nd(std::ptr_fun(JobCreationOlderThanDaysPredicate),
+ kPurgeStaleJobsAfterDays),
+ bits_manager_, &jobs);
+
+ for (const auto& job : jobs)
+ CleanupJob(job);
+}
+
} // namespace update_client
diff --git a/chromium/components/update_client/background_downloader_win.h b/chromium/components/update_client/background_downloader_win.h
index cf90d18d58a..7549b709455 100644
--- a/chromium/components/update_client/background_downloader_win.h
+++ b/chromium/components/update_client/background_downloader_win.h
@@ -5,8 +5,9 @@
#ifndef COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_
#define COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_
-#include <windows.h>
#include <bits.h>
+#include <windows.h>
+#include <wrl/client.h>
#include <memory>
@@ -16,7 +17,6 @@
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
-#include "base/win/scoped_comptr.h"
#include "components/update_client/crx_downloader.h"
namespace base {
@@ -40,21 +40,19 @@ namespace update_client {
class BackgroundDownloader : public CrxDownloader {
protected:
friend class CrxDownloader;
- BackgroundDownloader(
- std::unique_ptr<CrxDownloader> successor,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+ BackgroundDownloader(std::unique_ptr<CrxDownloader> successor,
+ net::URLRequestContextGetter* context_getter);
~BackgroundDownloader() override;
private:
// Overrides for CrxDownloader.
void DoStartDownload(const GURL& url) override;
- // Called asynchronously on the |task_runner_| at different stages during
+ // Called asynchronously on the |com_task_runner_| at different stages during
// the download. |OnDownloading| can be called multiple times.
- // |EndDownload| switches the execution flow from the |task_runner_| to the
- // main thread. Accessing any data members of this object from the
- // |task_runner_| after calling |EndDownload| is unsafe.
+ // |EndDownload| switches the execution flow from the |com_task_runner_| to
+ // the main thread. Accessing any data members of this object from the
+ // |com_task_runner_| after calling |EndDownload| is unsafe.
void BeginDownload(const GURL& url);
void OnDownloading();
void EndDownload(HRESULT hr);
@@ -87,14 +85,15 @@ class BackgroundDownloader : public CrxDownloader {
void StartTimer();
void OnTimer();
- // Returns true if the timer is running or false if the timer is not
- // created or not running at all.
- bool TimerIsRunning() const;
-
- HRESULT QueueBitsJob(const GURL& url, IBackgroundCopyJob** job);
- HRESULT CreateOrOpenJob(const GURL& url, IBackgroundCopyJob** job);
+ // Creates or opens a job for the given url and queues it up. Returns S_OK if
+ // a new job was created or S_FALSE if an existing job for the |url| was found
+ // in the BITS queue.
+ HRESULT QueueBitsJob(const GURL& url,
+ Microsoft::WRL::ComPtr<IBackgroundCopyJob>* job);
+ HRESULT CreateOrOpenJob(const GURL& url,
+ Microsoft::WRL::ComPtr<IBackgroundCopyJob>* job);
HRESULT InitializeNewJob(
- const base::win::ScopedComPtr<IBackgroundCopyJob>& job,
+ const Microsoft::WRL::ComPtr<IBackgroundCopyJob>& job,
const GURL& url);
// Returns true if at the time of the call, it appears that the job
@@ -117,9 +116,19 @@ class BackgroundDownloader : public CrxDownloader {
// from the thread pool leaves the object to release the interface pointers.
void ResetInterfacePointers();
+ // Returns the number of jobs in the BITS queue which were created by this
+ // downloader.
+ HRESULT GetBackgroundDownloaderJobCount(size_t* num_jobs);
+
+ // Cleans up incompleted jobs that are too old.
+ void CleanupStaleJobs();
+
// Ensures that we are running on the same thread we created the object on.
base::ThreadChecker thread_checker_;
+ // Executes blocking COM calls to BITS.
+ scoped_refptr<base::SequencedTaskRunner> com_task_runner_;
+
net::URLRequestContextGetter* context_getter_;
// The timer has thread affinity. This member is initialized and destroyed
@@ -132,8 +141,8 @@ class BackgroundDownloader : public CrxDownloader {
// COM interface pointers are valid for the thread that called
// |UpdateInterfacePointers| to get pointers to COM proxies, which are valid
// for that thread only.
- base::win::ScopedComPtr<IBackgroundCopyManager> bits_manager_;
- base::win::ScopedComPtr<IBackgroundCopyJob> job_;
+ Microsoft::WRL::ComPtr<IBackgroundCopyManager> bits_manager_;
+ Microsoft::WRL::ComPtr<IBackgroundCopyJob> job_;
// Contains the time when the download of the current url has started.
base::TimeTicks download_start_time_;
diff --git a/chromium/components/update_client/component.cc b/chromium/components/update_client/component.cc
index a63b5cdc680..750ee8ae18d 100644
--- a/chromium/components/update_client/component.cc
+++ b/chromium/components/update_client/component.cc
@@ -15,6 +15,7 @@
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "components/update_client/action_runner.h"
#include "components/update_client/component_unpacker.h"
#include "components/update_client/configurator.h"
#include "components/update_client/protocol_builder.h"
@@ -25,36 +26,35 @@
// The state machine representing how a CRX component changes during an update.
//
-//
-// on-demand on-demand
-// +--------------------------> kNew <---------------+-------------+
-// | | | |
-// | V | |
-// | +---------------0----> kChecking -<-------+---|---<-----+ |
-// | | | | | | |
-// | | error V no | | | |
-// kUpdateError <------------- [update?] ->---- kUpToDate kUpdated
-// ^ | ^
-// | yes | |
-// | V |
-// | kCanUpdate |
-// | | |
-// | V no |
-// | [differential update?]--->----+ |
-// | | | |
-// | yes | | |
-// | V error | |
-// | kDownloadingDiff --->---------+ |
-// | | | |
-// | | | |
-// | V error | |
-// | kUpdatingDiff --->--------+-----------+ success
-// | | |
-// | error V |
-// +----------------------------------------- kDownloading |
-// | | |
-// | error V |
-// +------------------------------------------ kUpdating ->----+ success
+// +------------------------> kNew <---------------------+--------+
+// | | | |
+// | V | |
+// | kChecking | |
+// | | | |
+// | error V no no | |
+// kUpdateError <------------- [update?] -> [action?] -> kUpToDate kUpdated
+// ^ | | ^ ^
+// | yes | | yes | |
+// | V | | |
+// | kCanUpdate +--------> kRun |
+// | | |
+// | no V |
+// | +-<- [differential update?] |
+// | | | |
+// | | yes | |
+// | | error V |
+// | +-<----- kDownloadingDiff kRun---->-+
+// | | | ^ |
+// | | | yes | |
+// | | error V | |
+// | +-<----- kUpdatingDiff ---------> [action?] ->-+
+// | | ^ no
+// | error V |
+// +-<-------- kDownloading |
+// | | |
+// | | |
+// | error V |
+// +-<-------- kUpdating --------------------------------+
namespace update_client {
@@ -70,7 +70,7 @@ CrxInstaller::Result DoInstallOnBlockingTaskRunner(
const std::string& fingerprint,
const scoped_refptr<CrxInstaller>& installer,
InstallOnBlockingTaskRunnerCompleteCallback callback) {
- DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
+ DCHECK(blocking_task_runner->RunsTasksInCurrentSequence());
if (static_cast<int>(fingerprint.size()) !=
base::WriteFile(
@@ -83,7 +83,7 @@ CrxInstaller::Result DoInstallOnBlockingTaskRunner(
if (!manifest)
return CrxInstaller::Result(InstallError::BAD_MANIFEST);
- return installer->Install(*manifest, unpack_path);
+ return installer->Install(std::move(manifest), unpack_path);
}
void InstallOnBlockingTaskRunner(
@@ -93,7 +93,7 @@ void InstallOnBlockingTaskRunner(
const std::string& fingerprint,
const scoped_refptr<CrxInstaller>& installer,
InstallOnBlockingTaskRunnerCompleteCallback callback) {
- DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
+ DCHECK(blocking_task_runner->RunsTasksInCurrentSequence());
DCHECK(base::DirectoryExists(unpack_path));
const auto result = DoInstallOnBlockingTaskRunner(
@@ -117,7 +117,7 @@ void UnpackCompleteOnBlockingTaskRunner(
const scoped_refptr<CrxInstaller>& installer,
InstallOnBlockingTaskRunnerCompleteCallback callback,
const ComponentUnpacker::Result& result) {
- DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
+ DCHECK(blocking_task_runner->RunsTasksInCurrentSequence());
update_client::DeleteFileAndEmptyParentDirectory(crx_path);
@@ -144,7 +144,7 @@ void StartInstallOnBlockingTaskRunner(
const scoped_refptr<CrxInstaller>& installer,
const scoped_refptr<OutOfProcessPatcher>& oop_patcher,
InstallOnBlockingTaskRunnerCompleteCallback callback) {
- DCHECK(blocking_task_runner->RunsTasksOnCurrentThread());
+ DCHECK(blocking_task_runner->RunsTasksInCurrentSequence());
auto unpacker = base::MakeRefCounted<ComponentUnpacker>(
pk_hash, crx_path, installer, oop_patcher, blocking_task_runner);
@@ -175,6 +175,7 @@ void Component::Handle(CallbackHandleComplete callback) {
void Component::ChangeState(std::unique_ptr<State> next_state) {
DCHECK(thread_checker_.CalledOnValidThread());
+ previous_state_ = state();
if (next_state)
state_ = std::move(next_state);
@@ -348,7 +349,10 @@ void Component::StateChecking::UpdateCheckComplete() {
}
if (component.status_ == "noupdate") {
- TransitionState(base::MakeUnique<StateUpToDate>(&component));
+ if (component.action_run_.empty())
+ TransitionState(base::MakeUnique<StateUpToDate>(&component));
+ else
+ TransitionState(base::MakeUnique<StateRun>(&component));
return;
}
}
@@ -429,8 +433,8 @@ void Component::StateUpToDate::DoHandle() {
auto& component = State::component();
- TransitionState(nullptr);
component.NotifyObservers(Events::COMPONENT_NOT_UPDATED);
+ TransitionState(nullptr);
}
Component::StateDownloadingDiff::StateDownloadingDiff(Component* component)
@@ -448,8 +452,7 @@ void Component::StateDownloadingDiff::DoHandle() {
crx_downloader_ = update_context.crx_downloader_factory(
component.CanDoBackgroundDownload(),
- update_context.config->RequestContext(),
- update_context.blocking_task_runner);
+ update_context.config->RequestContext());
const auto& id = component.id_;
crx_downloader_->set_progress_callback(
@@ -515,8 +518,7 @@ void Component::StateDownloading::DoHandle() {
crx_downloader_ = update_context.crx_downloader_factory(
component.CanDoBackgroundDownload(),
- update_context.config->RequestContext(),
- update_context.blocking_task_runner);
+ update_context.config->RequestContext());
const auto& id = component.id_;
crx_downloader_->set_progress_callback(
@@ -619,7 +621,10 @@ void Component::StateUpdatingDiff::InstallComplete(int error_category,
DCHECK_EQ(0, component.error_code_);
DCHECK_EQ(0, component.extra_code1_);
- TransitionState(base::MakeUnique<StateUpdated>(&component));
+ if (component.action_run_.empty())
+ TransitionState(base::MakeUnique<StateUpdated>(&component));
+ else
+ TransitionState(base::MakeUnique<StateRun>(&component));
}
Component::StateUpdating::StateUpdating(Component* component)
@@ -670,7 +675,10 @@ void Component::StateUpdating::InstallComplete(int error_category,
DCHECK_EQ(0, component.error_code_);
DCHECK_EQ(0, component.extra_code1_);
- TransitionState(base::MakeUnique<StateUpdated>(&component));
+ if (component.action_run_.empty())
+ TransitionState(base::MakeUnique<StateUpdated>(&component));
+ else
+ TransitionState(base::MakeUnique<StateRun>(&component));
}
Component::StateUpdated::StateUpdated(Component* component)
@@ -691,8 +699,8 @@ void Component::StateUpdated::DoHandle() {
component.AppendEvent(BuildUpdateCompleteEventElement(component));
- TransitionState(nullptr);
component.NotifyObservers(Events::COMPONENT_UPDATED);
+ TransitionState(nullptr);
}
Component::StateUninstalled::StateUninstalled(Component* component)
@@ -713,4 +721,46 @@ void Component::StateUninstalled::DoHandle() {
TransitionState(nullptr);
}
+Component::StateRun::StateRun(Component* component)
+ : State(component, ComponentState::kRun) {}
+
+Component::StateRun::~StateRun() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateRun::DoHandle() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ const auto& component = State::component();
+ action_runner_ = base::MakeUnique<ActionRunner>(
+ component, component.update_context_.blocking_task_runner,
+ component.update_context_.config->GetRunActionKeyHash());
+
+ action_runner_->Run(
+ base::Bind(&StateRun::ActionRunComplete, base::Unretained(this)));
+}
+
+void Component::StateRun::ActionRunComplete(bool succeeded,
+ int error_code,
+ int extra_code1) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ auto& component = State::component();
+
+ component.AppendEvent(
+ BuildActionRunEventElement(succeeded, error_code, extra_code1));
+ switch (component.previous_state_) {
+ case ComponentState::kChecking:
+ TransitionState(base::MakeUnique<StateUpToDate>(&component));
+ return;
+ case ComponentState::kUpdating:
+ case ComponentState::kUpdatingDiff:
+ TransitionState(base::MakeUnique<StateUpdated>(&component));
+ return;
+ default:
+ break;
+ }
+ NOTREACHED();
+}
+
} // namespace update_client
diff --git a/chromium/components/update_client/component.h b/chromium/components/update_client/component.h
index 782a7148f46..5b85bd89fb0 100644
--- a/chromium/components/update_client/component.h
+++ b/chromium/components/update_client/component.h
@@ -27,6 +27,7 @@
namespace update_client {
+class ActionRunner;
struct CrxUpdateItem;
struct UpdateContext;
@@ -116,6 +117,8 @@ class Component {
int diff_error_code() const { return diff_error_code_; }
int diff_extra_code1() const { return diff_extra_code1_; }
+ std::string action_run() const { return action_run_; }
+
private:
friend class FakePingManagerImpl;
friend class UpdateCheckerTest;
@@ -335,6 +338,24 @@ class Component {
DISALLOW_COPY_AND_ASSIGN(StateUninstalled);
};
+ class StateRun : public State {
+ public:
+ explicit StateRun(Component* component);
+ ~StateRun() override;
+
+ private:
+ // State overrides.
+ void DoHandle() override;
+
+ void ActionRunComplete(bool succeeded, int error_code, int extra_code1);
+
+ // Runs the action referred by the |action_run_| member of the Component
+ // class.
+ std::unique_ptr<ActionRunner> action_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(StateRun);
+ };
+
// Returns true is the update payload for this component can be downloaded
// by a downloader which can do bandwidth throttling on the client side.
bool CanDoBackgroundDownload() const;
@@ -353,6 +374,7 @@ class Component {
const std::string id_;
CrxComponent crx_component_;
+ // The status of the updatecheck response.
std::string status_;
// Time when an update check for this CRX has happened.
@@ -375,7 +397,8 @@ class Component {
std::string previous_fp_;
std::string next_fp_;
- // Contains the file name of the payload to run.
+ // Contains the file name of the payload to run. This member is set by
+ // the update response parser, when the update response includes a run action.
std::string action_run_;
// True if the update check response for this component includes an update.
@@ -411,6 +434,8 @@ class Component {
base::Closure update_check_complete_;
+ ComponentState previous_state_ = ComponentState::kLastStatus;
+
DISALLOW_COPY_AND_ASSIGN(Component);
};
diff --git a/chromium/components/update_client/component_unpacker.cc b/chromium/components/update_client/component_unpacker.cc
index bc532b2d923..50444e80d9e 100644
--- a/chromium/components/update_client/component_unpacker.cc
+++ b/chromium/components/update_client/component_unpacker.cc
@@ -90,6 +90,7 @@ bool ComponentUnpacker::Verify() {
if (result != crx_file::VerifierResult::OK_FULL &&
result != crx_file::VerifierResult::OK_DELTA) {
error_ = UnpackerError::kInvalidFile;
+ extended_error_ = static_cast<int>(result);
return false;
}
is_delta_ = result == crx_file::VerifierResult::OK_DELTA;
diff --git a/chromium/components/update_client/component_unpacker_unittest.cc b/chromium/components/update_client/component_unpacker_unittest.cc
index e488c484301..1488c3273a8 100644
--- a/chromium/components/update_client/component_unpacker_unittest.cc
+++ b/chromium/components/update_client/component_unpacker_unittest.cc
@@ -17,6 +17,7 @@
#include "base/task_scheduler/post_task.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "components/crx_file/crx_verifier.h"
#include "components/update_client/component_unpacker.h"
#include "components/update_client/test_configurator.h"
#include "components/update_client/test_installer.h"
@@ -146,7 +147,8 @@ TEST_F(ComponentUnpackerTest, UnpackFileNotFound) {
RunThreads();
EXPECT_EQ(UnpackerError::kInvalidFile, result_.error);
- EXPECT_EQ(0, result_.extended_error);
+ EXPECT_EQ(static_cast<int>(crx_file::VerifierResult::ERROR_FILE_NOT_READABLE),
+ result_.extended_error);
EXPECT_TRUE(result_.unpack_path.empty());
}
@@ -162,7 +164,9 @@ TEST_F(ComponentUnpackerTest, UnpackFileHashMismatch) {
RunThreads();
EXPECT_EQ(UnpackerError::kInvalidFile, result_.error);
- EXPECT_EQ(0, result_.extended_error);
+ EXPECT_EQ(
+ static_cast<int>(crx_file::VerifierResult::ERROR_REQUIRED_PROOF_MISSING),
+ result_.extended_error);
EXPECT_TRUE(result_.unpack_path.empty());
}
diff --git a/chromium/components/update_client/configurator.h b/chromium/components/update_client/configurator.h
index be2caff58d8..ddb5b5d29f6 100644
--- a/chromium/components/update_client/configurator.h
+++ b/chromium/components/update_client/configurator.h
@@ -133,6 +133,12 @@ class Configurator : public base::RefCountedThreadSafe<Configurator> {
// called only from a blocking pool thread, as it may access the file system.
virtual bool IsPerUserInstall() const = 0;
+ // Returns the key hash corresponding to a CRX trusted by ActionRun. The
+ // CRX payloads are signed with this key, and their integrity is verified
+ // during the unpacking by the action runner. This is a dependency injection
+ // feature to support testing.
+ virtual std::vector<uint8_t> GetRunActionKeyHash() const = 0;
+
protected:
friend class base::RefCountedThreadSafe<Configurator>;
diff --git a/chromium/components/update_client/crx_downloader.cc b/chromium/components/update_client/crx_downloader.cc
index f31593352a1..afdfdbf6d8f 100644
--- a/chromium/components/update_client/crx_downloader.cc
+++ b/chromium/components/update_client/crx_downloader.cc
@@ -11,7 +11,8 @@
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/logging.h"
-#include "base/sequenced_task_runner.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#if defined(OS_WIN)
@@ -39,26 +40,22 @@ CrxDownloader::DownloadMetrics::DownloadMetrics()
// which uses the BITS service.
std::unique_ptr<CrxDownloader> CrxDownloader::Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
std::unique_ptr<CrxDownloader> url_fetcher_downloader(
std::unique_ptr<CrxDownloader>(new UrlFetcherDownloader(
- std::unique_ptr<CrxDownloader>(), context_getter, task_runner)));
+ std::unique_ptr<CrxDownloader>(), context_getter)));
#if defined(OS_WIN)
if (is_background_download) {
return std::unique_ptr<CrxDownloader>(new BackgroundDownloader(
- std::move(url_fetcher_downloader), context_getter, task_runner));
+ std::move(url_fetcher_downloader), context_getter));
}
#endif
return url_fetcher_downloader;
}
-CrxDownloader::CrxDownloader(
- const scoped_refptr<base::SequencedTaskRunner>& task_runner,
- std::unique_ptr<CrxDownloader> successor)
- : task_runner_(task_runner),
- main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+CrxDownloader::CrxDownloader(std::unique_ptr<CrxDownloader> successor)
+ : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
successor_(std::move(successor)) {}
CrxDownloader::~CrxDownloader() {}
@@ -107,8 +104,8 @@ void CrxDownloader::StartDownload(const std::vector<GURL>& urls,
if (error != CrxDownloaderError::NONE) {
Result result;
result.error = static_cast<int>(error);
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(download_callback, result));
+ main_task_runner()->PostTask(FROM_HERE,
+ base::Bind(download_callback, result));
return;
}
@@ -127,8 +124,10 @@ void CrxDownloader::OnDownloadComplete(
DCHECK(thread_checker_.CalledOnValidThread());
if (!result.error)
- task_runner()->PostTask(
+ base::PostTaskWithTraits(
FROM_HERE,
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::Bind(&CrxDownloader::VerifyResponse, base::Unretained(this),
is_handled, result, download_metrics));
else
@@ -152,7 +151,6 @@ void CrxDownloader::OnDownloadProgress(const Result& result) {
void CrxDownloader::VerifyResponse(bool is_handled,
Result result,
DownloadMetrics download_metrics) {
- DCHECK(task_runner()->RunsTasksOnCurrentThread());
DCHECK_EQ(0, result.error);
DCHECK_EQ(0, download_metrics.error);
DCHECK(is_handled);
diff --git a/chromium/components/update_client/crx_downloader.h b/chromium/components/update_client/crx_downloader.h
index 635098c988c..0b6cae8981a 100644
--- a/chromium/components/update_client/crx_downloader.h
+++ b/chromium/components/update_client/crx_downloader.h
@@ -86,10 +86,8 @@ class CrxDownloader {
// bytes is not guaranteed to monotonically increment over time.
using ProgressCallback = base::Callback<void(const Result& result)>;
- using Factory = std::unique_ptr<CrxDownloader> (*)(
- bool,
- net::URLRequestContextGetter*,
- const scoped_refptr<base::SequencedTaskRunner>&);
+ using Factory =
+ std::unique_ptr<CrxDownloader> (*)(bool, net::URLRequestContextGetter*);
// Factory method to create an instance of this class and build the
// chain of responsibility. |is_background_download| specifies that a
@@ -98,8 +96,7 @@ class CrxDownloader {
// code such as file IO operations.
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+ net::URLRequestContextGetter* context_getter);
virtual ~CrxDownloader();
void set_progress_callback(const ProgressCallback& progress_callback);
@@ -119,8 +116,7 @@ class CrxDownloader {
const std::vector<DownloadMetrics> download_metrics() const;
protected:
- CrxDownloader(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
- std::unique_ptr<CrxDownloader> successor);
+ explicit CrxDownloader(std::unique_ptr<CrxDownloader> successor);
// Handles the fallback in the case of multiple urls and routing of the
// download to the following successor in the chain. Derived classes must call
@@ -140,10 +136,6 @@ class CrxDownloader {
// Returns the url which is currently being downloaded from.
GURL url() const;
- scoped_refptr<base::SequencedTaskRunner> task_runner() const {
- return task_runner_;
- }
-
scoped_refptr<base::SequencedTaskRunner> main_task_runner() const {
return main_task_runner_;
}
@@ -161,9 +153,6 @@ class CrxDownloader {
base::ThreadChecker thread_checker_;
- // Executes blocking operations such as file I/O.
- scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
// Used to post callbacks to the main thread.
scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
diff --git a/chromium/components/update_client/crx_downloader_unittest.cc b/chromium/components/update_client/crx_downloader_unittest.cc
index aba14b116db..40440085ab1 100644
--- a/chromium/components/update_client/crx_downloader_unittest.cc
+++ b/chromium/components/update_client/crx_downloader_unittest.cc
@@ -14,9 +14,11 @@
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
#include "net/base/net_errors.h"
#include "net/url_request/test_url_request_interceptor.h"
#include "net/url_request/url_request_test_util.h"
@@ -83,7 +85,7 @@ class CrxDownloaderTest : public testing::Test {
static const int kExpectedContext = 0xaabb;
private:
- base::MessageLoopForIO loop_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<net::TestURLRequestContextGetter> context_;
base::Closure quit_closure_;
};
@@ -100,9 +102,10 @@ CrxDownloaderTest::CrxDownloaderTest()
crx_context_(0),
num_download_complete_calls_(0),
num_progress_calls_(0),
+ scoped_task_environment_(
+ base::test::ScopedTaskEnvironment::MainThreadType::IO),
context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())) {
-}
+ base::ThreadTaskRunnerHandle::Get())) {}
CrxDownloaderTest::~CrxDownloaderTest() {
context_ = NULL;
@@ -119,10 +122,7 @@ void CrxDownloaderTest::SetUp() {
download_progress_result_ = CrxDownloader::Result();
// Do not use the background downloader in these tests.
- crx_downloader_.reset(
- CrxDownloader::Create(false, context_.get(),
- base::ThreadTaskRunnerHandle::Get())
- .release());
+ crx_downloader_.reset(CrxDownloader::Create(false, context_.get()).release());
crx_downloader_->set_progress_callback(progress_callback_);
get_interceptor_.reset(
@@ -166,6 +166,7 @@ void CrxDownloaderTest::RunThreads() {
}
void CrxDownloaderTest::RunThreadsUntilIdle() {
+ scoped_task_environment_.RunUntilIdle();
base::RunLoop().RunUntilIdle();
}
@@ -223,7 +224,8 @@ TEST_F(CrxDownloaderTest, OneUrl) {
EXPECT_EQ(1843, download_complete_result_.total_bytes);
EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
- EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
+ EXPECT_TRUE(
+ DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
EXPECT_LE(1, num_progress_calls_);
EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
@@ -292,7 +294,8 @@ TEST_F(CrxDownloaderTest, MAYBE_TwoUrls) {
EXPECT_EQ(1843, download_complete_result_.total_bytes);
EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
- EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
+ EXPECT_TRUE(
+ DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
EXPECT_LE(1, num_progress_calls_);
EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
@@ -371,7 +374,8 @@ TEST_F(CrxDownloaderTest, MAYBE_TwoUrls_FirstInvalid) {
EXPECT_EQ(1843, download_complete_result_.total_bytes);
EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
- EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
+ EXPECT_TRUE(
+ DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
EXPECT_LE(1, num_progress_calls_);
EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
@@ -403,7 +407,8 @@ TEST_F(CrxDownloaderTest, TwoUrls_SecondInvalid) {
EXPECT_EQ(1843, download_complete_result_.total_bytes);
EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
- EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
+ EXPECT_TRUE(
+ DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
EXPECT_LE(1, num_progress_calls_);
EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
diff --git a/chromium/components/update_client/ping_manager_unittest.cc b/chromium/components/update_client/ping_manager_unittest.cc
index f555bf204a1..503a4d59528 100644
--- a/chromium/components/update_client/ping_manager_unittest.cc
+++ b/chromium/components/update_client/ping_manager_unittest.cc
@@ -157,6 +157,27 @@ TEST_F(PingManagerTest, SendPing) {
}
{
+ // Test an invalid |next_version| is not serialized.
+ Component component(*update_context, "abc");
+ component.state_ =
+ base::MakeUnique<Component::StateUpdateError>(&component);
+ component.previous_version_ = base::Version("1.0");
+
+ component.AppendEvent(BuildUpdateCompleteEventElement(component));
+
+ ping_manager_->SendPing(component);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString();
+ EXPECT_NE(string::npos,
+ interceptor->GetRequests()[0].find(
+ "<app appid=\"abc\" version=\"1.0\">"
+ "<event eventtype=\"3\" eventresult=\"0\"/></app>"))
+ << interceptor->GetRequestsAsString();
+ interceptor->Reset();
+ }
+
+ {
// Test the download metrics.
Component component(*update_context, "abc");
component.state_ = base::MakeUnique<Component::StateUpdated>(&component);
diff --git a/chromium/components/update_client/protocol_builder.cc b/chromium/components/update_client/protocol_builder.cc
index ab273ffe0ed..f8430b117a8 100644
--- a/chromium/components/update_client/protocol_builder.cc
+++ b/chromium/components/update_client/protocol_builder.cc
@@ -157,13 +157,32 @@ std::string BuildUninstalledEventElement(const Component& component) {
using base::StringAppendF;
- std::string event("<event eventtype=\"4\" eventresult=\"1\"");
+ std::string event;
+ StringAppendF(&event, "<event eventtype=\"4\" extracode1=\"%d\"",
+ component.extra_code1());
+
if (component.extra_code1())
StringAppendF(&event, " extracode1=\"%d\"", component.extra_code1());
StringAppendF(&event, "/>");
return event;
}
+std::string BuildActionRunEventElement(bool succeeded,
+ int error_code,
+ int extra_code1) {
+ using base::StringAppendF;
+
+ std::string event;
+ StringAppendF(&event, "<event eventtype=\"42\" eventresult=\"%d\"",
+ succeeded);
+ if (error_code)
+ StringAppendF(&event, " errorcode=\"%d\"", error_code);
+ if (extra_code1)
+ StringAppendF(&event, " extracode1=\"%d\"", extra_code1);
+ StringAppendF(&event, "/>");
+ return event;
+}
+
std::string BuildProtocolRequest(
const std::string& prod_id,
const std::string& browser_version,
@@ -331,26 +350,31 @@ std::string BuildUpdateCheckRequest(
std::string BuildEventPingRequest(const Configurator& config,
const Component& component) {
DCHECK(component.state() == ComponentState::kUpdateError ||
+ component.state() == ComponentState::kUpToDate ||
component.state() == ComponentState::kUpdated ||
component.state() == ComponentState::kUninstalled);
- const char app_element_format[] =
- "<app appid=\"%s\" version=\"%s\" nextversion=\"%s\">"
- "%s"
- "</app>";
-
- const std::string app_element(base::StringPrintf(
- app_element_format,
- component.id().c_str(), // "appid"
- component.previous_version().GetString().c_str(), // "version"
- component.next_version().GetString().c_str(), // "nextversion"
- base::JoinString(component.events(), "").c_str())); // events
+ // |next_version| is an optional argument for some ping types. This member
+ // is set by the value returned in the update check response. However, some
+ // of the pings are sent as a result of actions where the response did not
+ // include a manifest, neither a version.
+ std::string app =
+ base::StringPrintf("<app appid=\"%s\"", component.id().c_str());
+ base::StringAppendF(&app, " version=\"%s\"",
+ component.previous_version().GetString().c_str());
+ if (component.next_version().IsValid())
+ base::StringAppendF(&app, " nextversion=\"%s\"",
+ component.next_version().GetString().c_str());
+ base::StringAppendF(&app, ">");
+ base::StringAppendF(&app, "%s",
+ base::JoinString(component.events(), "").c_str());
+ base::StringAppendF(&app, "</app>");
// The ping request does not include any updater state.
return BuildProtocolRequest(
config.GetProdId(), config.GetBrowserVersion().GetString(),
config.GetChannel(), config.GetLang(), config.GetOSLongName(),
- config.GetDownloadPreference(), app_element, "", nullptr);
+ config.GetDownloadPreference(), app, "", nullptr);
}
} // namespace update_client
diff --git a/chromium/components/update_client/protocol_builder.h b/chromium/components/update_client/protocol_builder.h
index 9b2f4a51ab2..7c6422eb37e 100644
--- a/chromium/components/update_client/protocol_builder.h
+++ b/chromium/components/update_client/protocol_builder.h
@@ -71,6 +71,10 @@ std::string BuildUninstalledEventElement(const Component& component);
std::string BuildDownloadCompleteEventElement(
const CrxDownloader::DownloadMetrics& metrics);
+std::string BuildActionRunEventElement(bool succeeded,
+ int error_code,
+ int extra_code1);
+
// An update protocol request starts with a common preamble which includes
// version and platform information for Chrome and the operating system,
// followed by a request body, which is the actual payload of the request.
@@ -108,4 +112,4 @@ std::string BuildProtocolRequest(
} // namespace update_client
-#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_H_
+#endif // COMPONENTS_UPDATE_CLIENT_PROTOCOL_BUILDER_H_
diff --git a/chromium/components/update_client/test_configurator.cc b/chromium/components/update_client/test_configurator.cc
index b2bf95ddccb..ade005de167 100644
--- a/chromium/components/update_client/test_configurator.cc
+++ b/chromium/components/update_client/test_configurator.cc
@@ -4,6 +4,8 @@
#include "components/update_client/test_configurator.h"
+#include <utility>
+
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/version.h"
@@ -175,4 +177,8 @@ bool TestConfigurator::IsPerUserInstall() const {
return true;
}
+std::vector<uint8_t> TestConfigurator::GetRunActionKeyHash() const {
+ return std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash));
+}
+
} // namespace update_client
diff --git a/chromium/components/update_client/test_configurator.h b/chromium/components/update_client/test_configurator.h
index 1f26ec051d2..fd0bc2f31df 100644
--- a/chromium/components/update_client/test_configurator.h
+++ b/chromium/components/update_client/test_configurator.h
@@ -53,6 +53,12 @@ const uint8_t ihfo_hash[] = {0x87, 0x5e, 0xa1, 0xa6, 0x9f, 0x85, 0xd1, 0x1e,
0xe7, 0xc5, 0xc8, 0xf5, 0x60, 0x19, 0x78, 0x1b,
0x6d, 0xe9, 0x4c, 0xeb, 0x96, 0x05, 0x42, 0x17};
+// runaction_test_win.crx and its payload id: gjpmebpgbhcamgdgjcmnjfhggjpgcimm
+const uint8_t gjpm_hash[] = {0x69, 0xfc, 0x41, 0xf6, 0x17, 0x20, 0xc6, 0x36,
+ 0x92, 0xcd, 0x95, 0x76, 0x69, 0xf6, 0x28, 0xcc,
+ 0xbe, 0x98, 0x4b, 0x93, 0x17, 0xd6, 0x9c, 0xb3,
+ 0x64, 0x0c, 0x0d, 0x25, 0x61, 0xc5, 0x80, 0x1d};
+
class TestConfigurator : public Configurator {
public:
TestConfigurator(
@@ -84,6 +90,7 @@ class TestConfigurator : public Configurator {
const override;
PrefService* GetPrefService() const override;
bool IsPerUserInstall() const override;
+ std::vector<uint8_t> GetRunActionKeyHash() const override;
void SetBrand(const std::string& brand);
void SetOnDemandTime(int seconds);
diff --git a/chromium/components/update_client/test_installer.cc b/chromium/components/update_client/test_installer.cc
index 7a003dbf817..795dd418727 100644
--- a/chromium/components/update_client/test_installer.cc
+++ b/chromium/components/update_client/test_installer.cc
@@ -30,7 +30,7 @@ void TestInstaller::OnUpdateError(int error) {
}
CrxInstaller::Result TestInstaller::Install(
- const base::DictionaryValue& manifest,
+ std::unique_ptr<base::DictionaryValue> manifest,
const base::FilePath& unpack_path) {
++install_count_;
@@ -70,10 +70,10 @@ VersionedTestInstaller::~VersionedTestInstaller() {
}
CrxInstaller::Result VersionedTestInstaller::Install(
- const base::DictionaryValue& manifest,
+ std::unique_ptr<base::DictionaryValue> manifest,
const base::FilePath& unpack_path) {
std::string version_string;
- manifest.GetStringASCII("version", &version_string);
+ manifest->GetStringASCII("version", &version_string);
base::Version version(version_string.c_str());
base::FilePath path;
diff --git a/chromium/components/update_client/test_installer.h b/chromium/components/update_client/test_installer.h
index 473772e8a0f..3fc810adb53 100644
--- a/chromium/components/update_client/test_installer.h
+++ b/chromium/components/update_client/test_installer.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
#define COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
+#include <memory>
#include <string>
#include "base/files/file_path.h"
@@ -25,7 +26,7 @@ class TestInstaller : public CrxInstaller {
void OnUpdateError(int error) override;
- Result Install(const base::DictionaryValue& manifest,
+ Result Install(std::unique_ptr<base::DictionaryValue> manifest,
const base::FilePath& unpack_path) override;
bool GetInstalledFile(const std::string& file,
@@ -69,7 +70,7 @@ class VersionedTestInstaller : public TestInstaller {
public:
VersionedTestInstaller();
- Result Install(const base::DictionaryValue& manifest,
+ Result Install(std::unique_ptr<base::DictionaryValue> manifest,
const base::FilePath& unpack_path) override;
bool GetInstalledFile(const std::string& file,
diff --git a/chromium/components/update_client/update_client.h b/chromium/components/update_client/update_client.h
index d068a5654bf..8d02d168825 100644
--- a/chromium/components/update_client/update_client.h
+++ b/chromium/components/update_client/update_client.h
@@ -156,6 +156,7 @@ enum class ComponentState {
kUpToDate,
kUpdateError,
kUninstalled,
+ kRun,
kLastStatus
};
@@ -185,7 +186,7 @@ class CrxInstaller : public base::RefCountedThreadSafe<CrxInstaller> {
// as a json dictionary.|unpack_path| contains the temporary directory
// with all the unpacked CRX files.
// This method may be called from a thread other than the main thread.
- virtual Result Install(const base::DictionaryValue& manifest,
+ virtual Result Install(std::unique_ptr<base::DictionaryValue> manifest,
const base::FilePath& unpack_path) = 0;
// Sets |installed_file| to the full path to the installed |file|. |file| is
diff --git a/chromium/components/update_client/update_client_errors.h b/chromium/components/update_client/update_client_errors.h
index 050ab3cf725..814f08a1d77 100644
--- a/chromium/components/update_client/update_client_errors.h
+++ b/chromium/components/update_client/update_client_errors.h
@@ -37,6 +37,9 @@ enum class CrxDownloaderError {
NO_URL = 10,
NO_HASH = 11,
BAD_HASH = 12, // The downloaded file fails the hash verification.
+ // The Windows BITS queue contains to many update client jobs. The value is
+ // chosen so that it can be reported as a custom COM error on this platform.
+ BITS_TOO_MANY_JOBS = 0x0200,
GENERIC_ERROR = -1
};
diff --git a/chromium/components/update_client/update_client_unittest.cc b/chromium/components/update_client/update_client_unittest.cc
index fae978c0775..98d02bcdc32 100644
--- a/chromium/components/update_client/update_client_unittest.cc
+++ b/chromium/components/update_client/update_client_unittest.cc
@@ -16,11 +16,13 @@
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_path_override.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "base/version.h"
#include "components/prefs/testing_pref_service.h"
+#include "components/update_client/component_unpacker.h"
#include "components/update_client/crx_update_item.h"
#include "components/update_client/persisted_data.h"
#include "components/update_client/ping_manager.h"
@@ -106,8 +108,11 @@ class FakePingManagerImpl : public PingManager {
const std::vector<PingData>& ping_data() const;
+ const std::vector<std::string>& events() const;
+
private:
std::vector<PingData> ping_data_;
+ std::vector<std::string> events_;
DISALLOW_COPY_AND_ASSIGN(FakePingManagerImpl);
};
@@ -130,6 +135,10 @@ bool FakePingManagerImpl::SendPing(const Component& component) {
ping_data.diff_error_code = component.diff_error_code_;
ping_data.diff_update_failed = component.diff_update_failed();
ping_data_.push_back(ping_data);
+
+ const auto& events = component.events();
+ events_.insert(events_.end(), events.begin(), events.end());
+
return true;
}
@@ -138,6 +147,10 @@ FakePingManagerImpl::ping_data() const {
return ping_data_;
}
+const std::vector<std::string>& FakePingManagerImpl::events() const {
+ return events_;
+}
+
class UpdateClientTest : public testing::Test {
public:
UpdateClientTest();
@@ -264,13 +277,11 @@ TEST_F(UpdateClientTest, OneCrxNoUpdate) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
@@ -427,13 +438,11 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -663,13 +672,11 @@ TEST_F(UpdateClientTest, TwoCrxUpdate) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -932,13 +939,11 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -1221,13 +1226,11 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -1362,15 +1365,22 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
TEST_F(UpdateClientTest, OneCrxInstallError) {
class MockInstaller : public CrxInstaller {
public:
+ // gMock does not support mocking functions with parameters which have
+ // move semantics. This function is a shim to work around it.
+ Result Install(std::unique_ptr<base::DictionaryValue> manifest,
+ const base::FilePath& unpack_path) {
+ return Install_(manifest, unpack_path);
+ }
+
MOCK_METHOD1(OnUpdateError, void(int error));
- MOCK_METHOD2(Install,
- Result(const base::DictionaryValue& manifest,
+ MOCK_METHOD2(Install_,
+ Result(const std::unique_ptr<base::DictionaryValue>& manifest,
const base::FilePath& unpack_path));
MOCK_METHOD2(GetInstalledFile,
bool(const std::string& file, base::FilePath* installed_file));
MOCK_METHOD0(Uninstall, bool());
- void OnInstall(const base::DictionaryValue& manifest,
+ void OnInstall(const std::unique_ptr<base::DictionaryValue>& manifest,
const base::FilePath& unpack_path) {
unpack_path_ = unpack_path;
EXPECT_TRUE(base::DirectoryExists(unpack_path_));
@@ -1398,7 +1408,7 @@ TEST_F(UpdateClientTest, OneCrxInstallError) {
base::MakeRefCounted<MockInstaller>();
EXPECT_CALL(*installer, OnUpdateError(_)).Times(0);
- EXPECT_CALL(*installer, Install(_, _))
+ EXPECT_CALL(*installer, Install_(_, _))
.WillOnce(
DoAll(Invoke(installer.get(), &MockInstaller::OnInstall),
Return(CrxInstaller::Result(InstallError::GENERIC_ERROR))));
@@ -1487,13 +1497,11 @@ TEST_F(UpdateClientTest, OneCrxInstallError) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -1737,13 +1745,11 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -1959,13 +1965,11 @@ TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
@@ -2106,13 +2110,11 @@ TEST_F(UpdateClientTest, OneCrxInstall) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -2270,13 +2272,11 @@ TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
@@ -2360,13 +2360,11 @@ TEST_F(UpdateClientTest, EmptyIdList) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
@@ -2414,14 +2412,12 @@ TEST_F(UpdateClientTest, SendUninstallPing) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return nullptr;
}
private:
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
~FakeCrxDownloader() override {}
void DoStartDownload(const GURL& url) override {}
@@ -2542,13 +2538,11 @@ TEST_F(UpdateClientTest, RetryAfter) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
@@ -2783,13 +2777,11 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override {
@@ -2952,13 +2944,11 @@ TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) {
public:
static std::unique_ptr<CrxDownloader> Create(
bool is_background_download,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+ net::URLRequestContextGetter* context_getter) {
return base::MakeUnique<FakeCrxDownloader>();
}
- FakeCrxDownloader()
- : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
private:
void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
@@ -3002,4 +2992,314 @@ TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) {
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.
+TEST_F(UpdateClientTest, ActionRun_Install) {
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static std::unique_ptr<UpdateChecker> Create(
+ const scoped_refptr<Configurator>& config,
+ PersistedData* metadata) {
+ return base::MakeUnique<FakeUpdateChecker>();
+ }
+
+ bool CheckForUpdates(
+ const std::vector<std::string>& ids_to_check,
+ const IdToComponentPtrMap& components,
+ const std::string& additional_attributes,
+ bool enabled_component_updates,
+ const UpdateCheckCallback& update_check_callback) override {
+ /*
+ Fake the following response:
+
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.1'>
+ <app appid='gjpmebpgbhcamgdgjcmnjfhggjpgcimm'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='runaction_test_win.crx3'
+ hash_sha256='89290a0d2ff21ca5b45e109c6cc859ab5fe294e19c102d54acd321429c372cea'/>
+ </packages>
+ </manifest>
+ <actions>"
+ <action run='ChromeRecovery.crx3'/>"
+ </actions>"
+ </updatecheck>
+ </app>
+ </response>
+ */
+ EXPECT_TRUE(enabled_component_updates);
+ EXPECT_EQ(1u, ids_to_check.size());
+
+ const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm";
+ EXPECT_EQ(id, ids_to_check[0]);
+ EXPECT_EQ(1u, components.count(id));
+
+ ProtocolParser::Result::Manifest::Package package;
+ package.name = "runaction_test_win.crx3";
+ package.hash_sha256 =
+ "89290a0d2ff21ca5b45e109c6cc859ab5fe294e19c102d54acd321429c372cea";
+
+ ProtocolParser::Result result;
+ result.extension_id = id;
+ 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);
+ result.action_run = "ChromeRecovery.crx3";
+
+ auto& component = components.at(id);
+ component->SetParseResult(result);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, 0, 0));
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static std::unique_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter) {
+ return base::MakeUnique<FakeCrxDownloader>();
+ }
+
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
+
+ private:
+ void DoStartDownload(const GURL& url) override {
+ DownloadMetrics download_metrics;
+ FilePath path;
+ Result result;
+ if (url.path() == "/download/runaction_test_win.crx3") {
+ 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;
+
+ EXPECT_TRUE(
+ MakeTestFile(TestFilePath("runaction_test_win.crx3"), &path));
+
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 1843;
+ result.total_bytes = 1843;
+ } else {
+ NOTREACHED();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeCrxDownloader::OnDownloadComplete,
+ base::Unretained(this), true, result, download_metrics));
+ }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const scoped_refptr<Configurator>& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override {
+ const auto& events = FakePingManagerImpl::events();
+ EXPECT_EQ(3u, events.size());
+ EXPECT_STREQ(
+ "<event eventtype=\"14\" eventresult=\"1\" downloader=\"unknown\" "
+ "url=\"http://localhost/download/"
+ "runaction_test_win.crx3\" downloaded=\"1843\" "
+ "total=\"1843\" download_time_ms=\"1000\"/>",
+ events[0].c_str());
+ EXPECT_STREQ(
+ "<event eventtype=\"42\" eventresult=\"1\" "
+ "errorcode=\"1877345072\"/>",
+ events[1].c_str());
+ EXPECT_STREQ("<event eventtype=\"3\" eventresult=\"1\"/>",
+ events[2].c_str());
+ }
+ };
+
+ scoped_refptr<UpdateClient> update_client =
+ base::MakeRefCounted<UpdateClientImpl>(
+ config(), base::MakeUnique<FakePingManager>(config()),
+ &FakeUpdateChecker::Create, &FakeCrxDownloader::Create);
+
+ // The action is a program which returns 1877345072 as a hardcoded value.
+ update_client->Install(
+ std::string("gjpmebpgbhcamgdgjcmnjfhggjpgcimm"),
+ base::Bind([](const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ CrxComponent crx;
+ crx.name = "test_niea";
+ crx.pk_hash.assign(gjpm_hash, gjpm_hash + arraysize(gjpm_hash));
+ crx.version = base::Version("0.0");
+ crx.installer = base::MakeRefCounted<VersionedTestInstaller>();
+ components->push_back(crx);
+ }),
+ base::Bind(
+ [](const base::Closure& quit_closure, Error error) {
+ EXPECT_EQ(Error::NONE, error);
+ quit_closure.Run();
+ },
+ quit_closure()));
+
+ RunThreads();
+}
+
+// Tests that a run action is invoked in an update scenario when there was
+// no update.
+TEST_F(UpdateClientTest, ActionRun_NoUpdate) {
+ class FakeUpdateChecker : public UpdateChecker {
+ public:
+ static std::unique_ptr<UpdateChecker> Create(
+ const scoped_refptr<Configurator>& config,
+ PersistedData* metadata) {
+ return base::MakeUnique<FakeUpdateChecker>();
+ }
+
+ bool CheckForUpdates(
+ const std::vector<std::string>& ids_to_check,
+ const IdToComponentPtrMap& components,
+ const std::string& additional_attributes,
+ bool enabled_component_updates,
+ const UpdateCheckCallback& update_check_callback) override {
+ /*
+ Fake the following response:
+
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.1'>
+ <app appid='gjpmebpgbhcamgdgjcmnjfhggjpgcimm'>
+ <updatecheck status='noupdate'>
+ <actions>"
+ <action run=ChromeRecovery.crx3'/>"
+ </actions>"
+ </updatecheck>
+ </app>
+ </response>
+ */
+ EXPECT_EQ(1u, ids_to_check.size());
+ const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm";
+ 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);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(update_check_callback, 0, 0));
+
+ return true;
+ }
+ };
+
+ class FakeCrxDownloader : public CrxDownloader {
+ public:
+ static std::unique_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ net::URLRequestContextGetter* context_getter) {
+ return base::MakeUnique<FakeCrxDownloader>();
+ }
+
+ FakeCrxDownloader() : CrxDownloader(nullptr) {}
+
+ private:
+ void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+ };
+
+ class FakePingManager : public FakePingManagerImpl {
+ public:
+ explicit FakePingManager(const scoped_refptr<Configurator>& config)
+ : FakePingManagerImpl(config) {}
+ ~FakePingManager() override {
+ const auto& events = FakePingManagerImpl::events();
+ EXPECT_EQ(1u, events.size());
+ EXPECT_STREQ(
+ "<event eventtype=\"42\" eventresult=\"1\" "
+ "errorcode=\"1877345072\"/>",
+ events[0].c_str());
+ }
+ };
+
+ // Unpack the CRX to mock an existing install to be updated. The payload to
+ // run is going to be picked up from this directory.
+ base::FilePath unpack_path;
+ {
+ base::RunLoop runloop;
+ base::Closure quit_closure = runloop.QuitClosure();
+
+ scoped_refptr<ComponentUnpacker> component_unpacker = new ComponentUnpacker(
+ std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash)),
+ TestFilePath("runaction_test_win.crx3"), nullptr, nullptr,
+ config()->GetSequencedTaskRunner());
+
+ component_unpacker->Unpack(base::Bind(
+ [](base::FilePath* unpack_path, const base::Closure& quit_closure,
+ const ComponentUnpacker::Result& result) {
+ EXPECT_EQ(UnpackerError::kNone, result.error);
+ EXPECT_EQ(0, result.extended_error);
+ *unpack_path = result.unpack_path;
+ quit_closure.Run();
+ },
+ &unpack_path, runloop.QuitClosure()));
+
+ runloop.Run();
+ }
+
+ EXPECT_FALSE(unpack_path.empty());
+ EXPECT_TRUE(base::DirectoryExists(unpack_path));
+ int64_t file_size = 0;
+ EXPECT_TRUE(base::GetFileSize(unpack_path.AppendASCII("ChromeRecovery.crx3"),
+ &file_size));
+ EXPECT_EQ(44582, file_size);
+
+ base::ScopedTempDir unpack_path_owner;
+ EXPECT_TRUE(unpack_path_owner.Set(unpack_path));
+
+ scoped_refptr<UpdateClient> update_client =
+ base::MakeRefCounted<UpdateClientImpl>(
+ config(), base::MakeUnique<FakePingManager>(config()),
+ &FakeUpdateChecker::Create, &FakeCrxDownloader::Create);
+
+ // The action is a program which returns 1877345072 as a hardcoded value.
+ const std::vector<std::string> ids = {"gjpmebpgbhcamgdgjcmnjfhggjpgcimm"};
+ update_client->Update(
+ ids,
+ base::Bind(
+ [](const base::FilePath& unpack_path,
+ const std::vector<std::string>& ids,
+ std::vector<CrxComponent>* components) {
+ CrxComponent crx;
+ crx.name = "test_niea";
+ crx.pk_hash.assign(gjpm_hash, gjpm_hash + arraysize(gjpm_hash));
+ crx.version = base::Version("1.0");
+ crx.installer =
+ base::MakeRefCounted<ReadOnlyTestInstaller>(unpack_path);
+ components->push_back(crx);
+ },
+ unpack_path),
+ base::Bind(
+ [](const base::Closure& quit_closure, Error error) {
+ EXPECT_EQ(Error::NONE, error);
+ quit_closure.Run();
+ },
+ quit_closure()));
+
+ RunThreads();
+}
+
+#endif // OS_WIN
+
} // namespace update_client
diff --git a/chromium/components/update_client/url_fetcher_downloader.cc b/chromium/components/update_client/url_fetcher_downloader.cc
index 57fb50d704f..03e6190bd4d 100644
--- a/chromium/components/update_client/url_fetcher_downloader.cc
+++ b/chromium/components/update_client/url_fetcher_downloader.cc
@@ -9,9 +9,12 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/sequenced_task_runner.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/update_client/utils.h"
#include "net/base/load_flags.h"
@@ -19,23 +22,43 @@
#include "net/url_request/url_fetcher.h"
#include "url/gurl.h"
+namespace {
+
+constexpr base::TaskTraits kTaskTraits = {
+ base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
+
+} // namespace
+
namespace update_client {
UrlFetcherDownloader::UrlFetcherDownloader(
std::unique_ptr<CrxDownloader> successor,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner)
- : CrxDownloader(task_runner, std::move(successor)),
- context_getter_(context_getter),
- downloaded_bytes_(-1),
- total_bytes_(-1) {}
+ net::URLRequestContextGetter* context_getter)
+ : CrxDownloader(std::move(successor)), context_getter_(context_getter) {}
UrlFetcherDownloader::~UrlFetcherDownloader() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ base::PostTaskWithTraitsAndReply(
+ FROM_HERE, kTaskTraits,
+ base::BindOnce(&UrlFetcherDownloader::CreateDownloadDir,
+ base::Unretained(this)),
+ base::BindOnce(&UrlFetcherDownloader::StartURLFetch,
+ base::Unretained(this), url));
+}
+
+void UrlFetcherDownloader::CreateDownloadDir() {
+ base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_url_fetcher_"),
+ &download_dir_);
+}
+
+void UrlFetcherDownloader::StartURLFetch(const GURL& url) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("url_fetcher_downloader", R"(
@@ -65,6 +88,31 @@ void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
}
}
})");
+
+ if (download_dir_.empty()) {
+ Result result;
+ result.error = -1;
+ result.downloaded_bytes = downloaded_bytes_;
+ result.total_bytes = total_bytes_;
+
+ DownloadMetrics download_metrics;
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kUrlFetcher;
+ download_metrics.error = -1;
+ download_metrics.downloaded_bytes = downloaded_bytes_;
+ download_metrics.total_bytes = total_bytes_;
+ download_metrics.download_time_ms = 0;
+
+ main_task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete,
+ base::Unretained(this), false, result,
+ download_metrics));
+ return;
+ }
+
+ const base::FilePath response =
+ download_dir_.AppendASCII(url.ExtractFileName());
+
url_fetcher_ = net::URLFetcher::Create(0, url, net::URLFetcher::GET, this,
traffic_annotation);
url_fetcher_->SetRequestContext(context_getter_);
@@ -72,7 +120,8 @@ void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DISABLE_CACHE);
url_fetcher_->SetAutomaticallyRetryOn5xx(false);
- url_fetcher_->SaveResponseToTemporaryFile(task_runner());
+ url_fetcher_->SaveResponseToFileAtPath(
+ response, base::CreateSequencedTaskRunnerWithTraits(kTaskTraits));
data_use_measurement::DataUseUserData::AttachToFetcher(
url_fetcher_.get(), data_use_measurement::DataUseUserData::UPDATE_CLIENT);
@@ -80,13 +129,10 @@ void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
url_fetcher_->Start();
download_start_time_ = base::TimeTicks::Now();
-
- downloaded_bytes_ = -1;
- total_bytes_ = -1;
}
void UrlFetcherDownloader::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
const base::TimeTicks download_end_time(base::TimeTicks::Now());
const base::TimeDelta download_time =
@@ -116,16 +162,20 @@ void UrlFetcherDownloader::OnURLFetchComplete(const net::URLFetcher* source) {
download_metrics.total_bytes = total_bytes_;
download_metrics.download_time_ms = download_time.InMilliseconds();
- base::FilePath local_path_;
- source->GetResponseAsFilePath(false, &local_path_);
VLOG(1) << "Downloaded " << downloaded_bytes_ << " bytes in "
<< download_time.InMilliseconds() << "ms from "
- << source->GetURL().spec() << " to " << local_path_.value();
+ << source->GetURL().spec() << " to " << result.response.value();
+
+ // Delete the download directory in the error cases.
+ if (fetch_error && !download_dir_.empty())
+ base::PostTaskWithTraits(
+ FROM_HERE, kTaskTraits,
+ base::BindOnce(IgnoreResult(&base::DeleteFile), download_dir_, true));
main_task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&UrlFetcherDownloader::OnDownloadComplete,
- base::Unretained(this), is_handled, result, download_metrics));
+ FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete,
+ base::Unretained(this), is_handled, result,
+ download_metrics));
}
void UrlFetcherDownloader::OnURLFetchDownloadProgress(
@@ -133,7 +183,7 @@ void UrlFetcherDownloader::OnURLFetchDownloadProgress(
int64_t current,
int64_t total,
int64_t current_network_bytes) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
downloaded_bytes_ = current;
total_bytes_ = total;
diff --git a/chromium/components/update_client/url_fetcher_downloader.h b/chromium/components/update_client/url_fetcher_downloader.h
index fe613dfcfa2..cb81fbc8850 100644
--- a/chromium/components/update_client/url_fetcher_downloader.h
+++ b/chromium/components/update_client/url_fetcher_downloader.h
@@ -9,6 +9,7 @@
#include <memory>
+#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
@@ -16,10 +17,6 @@
#include "components/update_client/crx_downloader.h"
#include "net/url_request/url_fetcher_delegate.h"
-namespace base {
-class SequencedTaskRunner;
-}
-
namespace net {
class URLFetcher;
class URLRequestContextGetter;
@@ -32,10 +29,8 @@ class UrlFetcherDownloader : public CrxDownloader,
public net::URLFetcherDelegate {
protected:
friend class CrxDownloader;
- UrlFetcherDownloader(
- std::unique_ptr<CrxDownloader> successor,
- net::URLRequestContextGetter* context_getter,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+ UrlFetcherDownloader(std::unique_ptr<CrxDownloader> successor,
+ net::URLRequestContextGetter* context_getter);
~UrlFetcherDownloader() override;
private:
@@ -48,15 +43,22 @@ class UrlFetcherDownloader : public CrxDownloader,
int64_t current,
int64_t total,
int64_t current_network_bytes) override;
+
+ void CreateDownloadDir();
+ void StartURLFetch(const GURL& url);
+
+ THREAD_CHECKER(thread_checker_);
+
std::unique_ptr<net::URLFetcher> url_fetcher_;
- net::URLRequestContextGetter* context_getter_;
+ net::URLRequestContextGetter* context_getter_ = nullptr;
- base::TimeTicks download_start_time_;
+ // Contains a temporary download directory for the downloaded file.
+ base::FilePath download_dir_;
- int64_t downloaded_bytes_;
- int64_t total_bytes_;
+ base::TimeTicks download_start_time_;
- base::ThreadChecker thread_checker_;
+ int64_t downloaded_bytes_ = -1;
+ int64_t total_bytes_ = -1;
DISALLOW_COPY_AND_ASSIGN(UrlFetcherDownloader);
};
diff --git a/chromium/components/update_client/url_request_post_interceptor.cc b/chromium/components/update_client/url_request_post_interceptor.cc
index acd75d50e8d..50bf0370039 100644
--- a/chromium/components/update_client/url_request_post_interceptor.cc
+++ b/chromium/components/update_client/url_request_post_interceptor.cc
@@ -67,7 +67,7 @@ URLRequestPostInterceptor::URLRequestPostInterceptor(
}
URLRequestPostInterceptor::~URLRequestPostInterceptor() {
- DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
ClearExpectations();
}
@@ -154,13 +154,13 @@ class URLRequestPostInterceptor::Delegate : public net::URLRequestInterceptor {
: scheme_(scheme), hostname_(hostname), io_task_runner_(io_task_runner) {}
void Register() {
- DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
scheme_, hostname_, std::unique_ptr<net::URLRequestInterceptor>(this));
}
void Unregister() {
- DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
for (InterceptorMap::iterator it = interceptors_.begin();
it != interceptors_.end(); ++it)
delete (*it).second;
@@ -169,7 +169,7 @@ class URLRequestPostInterceptor::Delegate : public net::URLRequestInterceptor {
}
void OnCreateInterceptor(URLRequestPostInterceptor* interceptor) {
- DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
DCHECK(interceptors_.find(interceptor->GetUrl()) == interceptors_.end());
interceptors_.insert(std::make_pair(interceptor->GetUrl(), interceptor));
@@ -181,7 +181,7 @@ class URLRequestPostInterceptor::Delegate : public net::URLRequestInterceptor {
net::URLRequestJob* MaybeInterceptRequest(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
- DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
// Only intercepts POST.
if (!request->has_upload())
diff --git a/chromium/components/update_client/utils.h b/chromium/components/update_client/utils.h
index 02c99259445..7655ffb44f9 100644
--- a/chromium/components/update_client/utils.h
+++ b/chromium/components/update_client/utils.h
@@ -85,7 +85,7 @@ bool IsValidInstallerAttribute(const InstallerAttribute& attr);
void RemoveUnsecureUrls(std::vector<GURL>* urls);
// Adapter function for the old definitions of CrxInstaller::Install until the
-// component installer code is migrated to use a REsult instead of bool.
+// component installer code is migrated to use a Result instead of bool.
CrxInstaller::Result InstallFunctionWrapper(base::Callback<bool()> callback);
} // namespace update_client
diff --git a/chromium/components/upload_list/BUILD.gn b/chromium/components/upload_list/BUILD.gn
index 46b869229ca..28aaec4133c 100644
--- a/chromium/components/upload_list/BUILD.gn
+++ b/chromium/components/upload_list/BUILD.gn
@@ -6,6 +6,8 @@ static_library("upload_list") {
sources = [
"crash_upload_list.cc",
"crash_upload_list.h",
+ "text_log_upload_list.cc",
+ "text_log_upload_list.h",
"upload_list.cc",
"upload_list.h",
]
@@ -18,7 +20,7 @@ static_library("upload_list") {
source_set("unit_tests") {
testonly = true
sources = [
- "upload_list_unittest.cc",
+ "text_log_upload_list_unittest.cc",
]
deps = [
diff --git a/chromium/components/upload_list/crash_upload_list.cc b/chromium/components/upload_list/crash_upload_list.cc
index 9b00ed2cbd3..704814ae03d 100644
--- a/chromium/components/upload_list/crash_upload_list.cc
+++ b/chromium/components/upload_list/crash_upload_list.cc
@@ -4,17 +4,5 @@
#include "components/upload_list/crash_upload_list.h"
-#include <utility>
-
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-
// static
const char CrashUploadList::kReporterLogFilename[] = "uploads.log";
-
-CrashUploadList::CrashUploadList(Delegate* delegate,
- const base::FilePath& upload_log_path,
- scoped_refptr<base::TaskRunner> task_runner)
- : UploadList(delegate, upload_log_path, std::move(task_runner)) {}
-
-CrashUploadList::~CrashUploadList() = default;
diff --git a/chromium/components/upload_list/crash_upload_list.h b/chromium/components/upload_list/crash_upload_list.h
index b1095ebc360..cbb2d318d52 100644
--- a/chromium/components/upload_list/crash_upload_list.h
+++ b/chromium/components/upload_list/crash_upload_list.h
@@ -5,32 +5,16 @@
#ifndef COMPONENTS_UPLOAD_LIST_CRASH_UPLOAD_LIST_H_
#define COMPONENTS_UPLOAD_LIST_CRASH_UPLOAD_LIST_H_
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/task_runner.h"
-#include "components/upload_list/upload_list.h"
-
-namespace base {
-class FilePath;
-}
-
-// An upload list manager for crash reports from breakpad.
-class CrashUploadList : public UploadList {
+// TODO(rsesek): Remove this class. Clients only use it for the constant
+// it defines.
+class CrashUploadList {
public:
// Should match kReporterLogFilename in
// breakpad/src/client/apple/Framework/BreakpadDefines.h.
static const char kReporterLogFilename[];
- // Creates a new crash upload list with the given callback delegate.
- CrashUploadList(Delegate* delegate,
- const base::FilePath& upload_log_path,
- scoped_refptr<base::TaskRunner> task_runner);
-
- protected:
- ~CrashUploadList() override;
-
private:
- DISALLOW_COPY_AND_ASSIGN(CrashUploadList);
+ CrashUploadList() {}
};
#endif // COMPONENTS_UPLOAD_LIST_CRASH_UPLOAD_LIST_H_
diff --git a/chromium/components/upload_list/text_log_upload_list.cc b/chromium/components/upload_list/text_log_upload_list.cc
new file mode 100644
index 00000000000..32397394615
--- /dev/null
+++ b/chromium/components/upload_list/text_log_upload_list.cc
@@ -0,0 +1,76 @@
+// 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/upload_list/text_log_upload_list.h"
+
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+
+TextLogUploadList::TextLogUploadList(const base::FilePath& upload_log_path)
+ : UploadList(), upload_log_path_(upload_log_path) {}
+
+TextLogUploadList::~TextLogUploadList() = default;
+
+base::TaskTraits TextLogUploadList::LoadingTaskTraits() {
+ return {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
+}
+
+std::vector<UploadList::UploadInfo> TextLogUploadList::LoadUploadList() {
+ std::vector<UploadInfo> uploads;
+
+ if (base::PathExists(upload_log_path_)) {
+ std::string contents;
+ base::ReadFileToString(upload_log_path_, &contents);
+ std::vector<std::string> log_entries =
+ base::SplitString(contents, base::kWhitespaceASCII,
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ ParseLogEntries(log_entries, &uploads);
+ }
+
+ return uploads;
+}
+
+void TextLogUploadList::ParseLogEntries(
+ const std::vector<std::string>& log_entries,
+ std::vector<UploadInfo>* uploads) {
+ std::vector<std::string>::const_reverse_iterator i;
+ for (i = log_entries.rbegin(); i != log_entries.rend(); ++i) {
+ std::vector<std::string> components =
+ base::SplitString(*i, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ // Skip any blank (or corrupted) lines.
+ if (components.size() < 2 || components.size() > 5)
+ continue;
+ base::Time upload_time;
+ double seconds_since_epoch;
+ if (!components[0].empty()) {
+ if (!base::StringToDouble(components[0], &seconds_since_epoch))
+ continue;
+ upload_time = base::Time::FromDoubleT(seconds_since_epoch);
+ }
+ UploadInfo info(components[1], upload_time);
+
+ // Add local ID if present.
+ if (components.size() > 2)
+ info.local_id = components[2];
+
+ // Add capture time if present.
+ if (components.size() > 3 && !components[3].empty() &&
+ base::StringToDouble(components[3], &seconds_since_epoch)) {
+ info.capture_time = base::Time::FromDoubleT(seconds_since_epoch);
+ }
+
+ int state;
+ if (components.size() > 4 && !components[4].empty() &&
+ base::StringToInt(components[4], &state)) {
+ info.state = static_cast<UploadInfo::State>(state);
+ }
+
+ uploads->push_back(info);
+ }
+}
diff --git a/chromium/components/upload_list/text_log_upload_list.h b/chromium/components/upload_list/text_log_upload_list.h
new file mode 100644
index 00000000000..449dcd42e37
--- /dev/null
+++ b/chromium/components/upload_list/text_log_upload_list.h
@@ -0,0 +1,44 @@
+// 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_UPLOAD_LIST_TEXT_LOG_UPLOAD_LIST_H_
+#define COMPONENTS_UPLOAD_LIST_TEXT_LOG_UPLOAD_LIST_H_
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "components/upload_list/upload_list.h"
+
+// Loads and parses an upload list text file of the format
+// upload_time,upload_id[,local_id[,capture_time[,state]]]
+// upload_time,upload_id[,local_id[,capture_time[,state]]]
+// etc.
+// where each line represents an upload. |upload_time| and |capture_time| are in
+// Unix time. |state| is an int in the range of UploadInfo::State. A line may
+// or may not contain |local_id|, |capture_time|, and |state|.
+class TextLogUploadList : public UploadList {
+ public:
+ // Creates a new upload list that parses the log at |upload_log_path|.
+ explicit TextLogUploadList(const base::FilePath& upload_log_path);
+
+ const base::FilePath& upload_log_path() const { return upload_log_path_; }
+
+ protected:
+ ~TextLogUploadList() override;
+
+ // UploadList:
+ base::TaskTraits LoadingTaskTraits() override;
+ std::vector<UploadList::UploadInfo> LoadUploadList() override;
+
+ // Parses upload log lines, converting them to UploadInfo entries.
+ void ParseLogEntries(const std::vector<std::string>& log_entries,
+ std::vector<UploadInfo>* uploads);
+
+ const base::FilePath upload_log_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextLogUploadList);
+};
+
+#endif // COMPONENTS_UPLOAD_LIST_TEXT_LOG_UPLOAD_LIST_H_
diff --git a/chromium/components/upload_list/text_log_upload_list_unittest.cc b/chromium/components/upload_list/text_log_upload_list_unittest.cc
new file mode 100644
index 00000000000..ce1174af76e
--- /dev/null
+++ b/chromium/components/upload_list/text_log_upload_list_unittest.cc
@@ -0,0 +1,317 @@
+// 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/upload_list/text_log_upload_list.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestUploadTime[] = "1234567890";
+const char kTestUploadId[] = "0123456789abcdef";
+const char kTestLocalID[] = "fedcba9876543210";
+const char kTestCaptureTime[] = "2345678901";
+
+class TextLogUploadListTest : public testing::Test {
+ public:
+ TextLogUploadListTest() = default;
+
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ void WriteUploadLog(const std::string& log_data) {
+ ASSERT_GT(base::WriteFile(log_path(), log_data.c_str(),
+ static_cast<int>(log_data.size())),
+ 0);
+ }
+
+ base::FilePath log_path() {
+ return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("uploads.log"));
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ base::ScopedTempDir temp_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextLogUploadListTest);
+};
+
+// These tests test that UploadList can parse a vector of log entry strings of
+// various formats to a vector of UploadInfo objects. See the UploadList
+// declaration for a description of the log entry string formats.
+
+// Test log entry string with upload time and upload ID.
+// This is the format that crash reports are stored in.
+TEST_F(TextLogUploadListTest, ParseUploadTimeUploadId) {
+ std::string test_entry = kTestUploadTime;
+ test_entry += ",";
+ test_entry.append(kTestUploadId);
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(1u, uploads.size());
+ double time_double = uploads[0].upload_time.ToDoubleT();
+ EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
+ EXPECT_STREQ("", uploads[0].local_id.c_str());
+ time_double = uploads[0].capture_time.ToDoubleT();
+ EXPECT_STREQ("0", base::DoubleToString(time_double).c_str());
+}
+
+// Test log entry string with upload time, upload ID and local ID.
+// This is the old format that WebRTC logs were stored in.
+TEST_F(TextLogUploadListTest, ParseUploadTimeUploadIdLocalId) {
+ std::string test_entry = kTestUploadTime;
+ test_entry += ",";
+ test_entry.append(kTestUploadId);
+ test_entry += ",";
+ test_entry.append(kTestLocalID);
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(1u, uploads.size());
+ double time_double = uploads[0].upload_time.ToDoubleT();
+ EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
+ EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
+ time_double = uploads[0].capture_time.ToDoubleT();
+ EXPECT_STREQ("0", base::DoubleToString(time_double).c_str());
+}
+
+// Test log entry string with upload time, upload ID and capture time.
+// This is the format that WebRTC logs that only have been uploaded only are
+// stored in.
+TEST_F(TextLogUploadListTest, ParseUploadTimeUploadIdCaptureTime) {
+ std::string test_entry = kTestUploadTime;
+ test_entry += ",";
+ test_entry.append(kTestUploadId);
+ test_entry += ",,";
+ test_entry.append(kTestCaptureTime);
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(1u, uploads.size());
+ double time_double = uploads[0].upload_time.ToDoubleT();
+ EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
+ EXPECT_STREQ("", uploads[0].local_id.c_str());
+ time_double = uploads[0].capture_time.ToDoubleT();
+ EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
+}
+
+// Test log entry string with local ID and capture time.
+// This is the format that WebRTC logs that only are stored locally are stored
+// in.
+TEST_F(TextLogUploadListTest, ParseLocalIdCaptureTime) {
+ std::string test_entry = ",,";
+ test_entry.append(kTestLocalID);
+ test_entry += ",";
+ test_entry.append(kTestCaptureTime);
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(1u, uploads.size());
+ double time_double = uploads[0].upload_time.ToDoubleT();
+ EXPECT_STREQ("0", base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ("", uploads[0].upload_id.c_str());
+ EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
+ time_double = uploads[0].capture_time.ToDoubleT();
+ EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
+}
+
+// Test log entry string with upload time, upload ID, local ID and capture
+// time.
+// This is the format that WebRTC logs that are stored locally and have been
+// uploaded are stored in.
+TEST_F(TextLogUploadListTest, ParseUploadTimeUploadIdLocalIdCaptureTime) {
+ std::string test_entry = kTestUploadTime;
+ test_entry += ",";
+ test_entry.append(kTestUploadId);
+ test_entry += ",";
+ test_entry.append(kTestLocalID);
+ test_entry += ",";
+ test_entry.append(kTestCaptureTime);
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(1u, uploads.size());
+ double time_double = uploads[0].upload_time.ToDoubleT();
+ EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
+ EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
+ time_double = uploads[0].capture_time.ToDoubleT();
+ EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
+}
+
+TEST_F(TextLogUploadListTest, ParseMultipleEntries) {
+ std::string test_entry;
+ for (int i = 1; i <= 4; ++i) {
+ test_entry.append(kTestUploadTime);
+ test_entry += ",";
+ test_entry.append(kTestUploadId);
+ test_entry += ",";
+ test_entry.append(kTestLocalID);
+ test_entry += ",";
+ test_entry.append(kTestCaptureTime);
+ test_entry += "\n";
+ }
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(4u, uploads.size());
+ for (size_t i = 0; i < uploads.size(); ++i) {
+ double time_double = uploads[i].upload_time.ToDoubleT();
+ EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ(kTestUploadId, uploads[i].upload_id.c_str());
+ EXPECT_STREQ(kTestLocalID, uploads[i].local_id.c_str());
+ time_double = uploads[i].capture_time.ToDoubleT();
+ EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
+ }
+}
+
+TEST_F(TextLogUploadListTest, ParseWithState) {
+ std::string test_entry;
+ for (int i = 1; i <= 4; ++i) {
+ test_entry.append(kTestUploadTime);
+ test_entry += ",";
+ test_entry.append(kTestUploadId);
+ test_entry += ",";
+ test_entry.append(kTestLocalID);
+ test_entry += ",";
+ test_entry.append(kTestCaptureTime);
+ test_entry += ",";
+ test_entry.append(base::IntToString(
+ static_cast<int>(UploadList::UploadInfo::State::Uploaded)));
+ test_entry += "\n";
+ }
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(4u, uploads.size());
+ for (size_t i = 0; i < uploads.size(); ++i) {
+ double time_double = uploads[i].upload_time.ToDoubleT();
+ EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ(kTestUploadId, uploads[i].upload_id.c_str());
+ EXPECT_STREQ(kTestLocalID, uploads[i].local_id.c_str());
+ time_double = uploads[i].capture_time.ToDoubleT();
+ EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
+ EXPECT_EQ(UploadList::UploadInfo::State::Uploaded, uploads[i].state);
+ }
+}
+
+// https://crbug.com/597384
+TEST_F(TextLogUploadListTest, SimultaneousAccess) {
+ std::string test_entry = kTestUploadTime;
+ test_entry += ",";
+ test_entry.append(kTestUploadId);
+ test_entry += ",";
+ test_entry.append(kTestLocalID);
+ test_entry += ",";
+ test_entry.append(kTestCaptureTime);
+ WriteUploadLog(test_entry);
+
+ scoped_refptr<TextLogUploadList> upload_list =
+ new TextLogUploadList(log_path());
+
+ // Queue up a bunch of loads, waiting only for the first one to complete.
+ base::RunLoop run_loop;
+ upload_list->Load(run_loop.QuitClosure());
+ run_loop.Run();
+
+ for (int i = 1; i <= 20; ++i) {
+ upload_list->Load(base::OnceClosure());
+ }
+
+ // Read the list a few times to try and race one of the loads above.
+ for (int i = 1; i <= 4; ++i) {
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list->GetUploads(999, &uploads);
+
+ EXPECT_EQ(1u, uploads.size());
+ double time_double = uploads[0].upload_time.ToDoubleT();
+ EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
+ EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
+ EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
+ time_double = uploads[0].capture_time.ToDoubleT();
+ EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
+ }
+}
+
+} // namespace
diff --git a/chromium/components/upload_list/upload_list.cc b/chromium/components/upload_list/upload_list.cc
index 082c01dfa05..3c8d9472cca 100644
--- a/chromium/components/upload_list/upload_list.cc
+++ b/chromium/components/upload_list/upload_list.cc
@@ -9,14 +9,7 @@
#include <utility>
#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/task_runner.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/task_scheduler/post_task.h"
UploadList::UploadInfo::UploadInfo(const std::string& upload_id,
const base::Time& upload_time,
@@ -52,98 +45,28 @@ UploadList::UploadInfo::UploadInfo(const UploadInfo& upload_info)
UploadList::UploadInfo::~UploadInfo() = default;
-UploadList::UploadList(Delegate* delegate,
- const base::FilePath& upload_log_path,
- scoped_refptr<base::TaskRunner> task_runner)
- : delegate_(delegate),
- upload_log_path_(upload_log_path),
- task_runner_(std::move(task_runner)) {}
+UploadList::UploadList() = default;
UploadList::~UploadList() = default;
-void UploadList::LoadUploadListAsynchronously() {
+void UploadList::Load(base::OnceClosure callback) {
DCHECK(sequence_checker_.CalledOnValidSequence());
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&UploadList::PerformLoadAndNotifyDelegate, this,
- base::SequencedTaskRunnerHandle::Get()));
+ callback_ = std::move(callback);
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, LoadingTaskTraits(),
+ base::Bind(&UploadList::LoadUploadList, this),
+ base::Bind(&UploadList::OnLoadComplete, this));
}
-void UploadList::ClearDelegate() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- delegate_ = NULL;
-}
-
-void UploadList::PerformLoadAndNotifyDelegate(
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
- std::vector<UploadInfo> uploads;
- LoadUploadList(&uploads);
- task_runner->PostTask(
- FROM_HERE,
- base::Bind(&UploadList::SetUploadsAndNotifyDelegate, this,
- std::move(uploads)));
-}
-
-void UploadList::LoadUploadList(std::vector<UploadInfo>* uploads) {
- if (base::PathExists(upload_log_path_)) {
- std::string contents;
- base::ReadFileToString(upload_log_path_, &contents);
- std::vector<std::string> log_entries = base::SplitString(
- contents, base::kWhitespaceASCII, base::KEEP_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
- ParseLogEntries(log_entries, uploads);
- }
-}
-
-const base::FilePath& UploadList::upload_log_path() const {
- return upload_log_path_;
+void UploadList::CancelCallback() {
+ callback_.Reset();
}
-void UploadList::ParseLogEntries(
- const std::vector<std::string>& log_entries,
- std::vector<UploadInfo>* uploads) {
- std::vector<std::string>::const_reverse_iterator i;
- for (i = log_entries.rbegin(); i != log_entries.rend(); ++i) {
- std::vector<std::string> components = base::SplitString(
- *i, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
- // Skip any blank (or corrupted) lines.
- if (components.size() < 2 || components.size() > 5)
- continue;
- base::Time upload_time;
- double seconds_since_epoch;
- if (!components[0].empty()) {
- if (!base::StringToDouble(components[0], &seconds_since_epoch))
- continue;
- upload_time = base::Time::FromDoubleT(seconds_since_epoch);
- }
- UploadInfo info(components[1], upload_time);
-
- // Add local ID if present.
- if (components.size() > 2)
- info.local_id = components[2];
-
- // Add capture time if present.
- if (components.size() > 3 &&
- !components[3].empty() &&
- base::StringToDouble(components[3], &seconds_since_epoch)) {
- info.capture_time = base::Time::FromDoubleT(seconds_since_epoch);
- }
-
- int state;
- if (components.size() > 4 &&
- !components[4].empty() &&
- base::StringToInt(components[4], &state)) {
- info.state = static_cast<UploadInfo::State>(state);
- }
-
- uploads->push_back(info);
- }
-}
-
-void UploadList::SetUploadsAndNotifyDelegate(std::vector<UploadInfo> uploads) {
+void UploadList::RequestSingleUploadAsync(const std::string& local_id) {
DCHECK(sequence_checker_.CalledOnValidSequence());
- uploads_ = std::move(uploads);
- if (delegate_)
- delegate_->OnUploadListAvailable();
+ base::PostTaskWithTraits(
+ FROM_HERE, LoadingTaskTraits(),
+ base::Bind(&UploadList::RequestSingleUpload, this, local_id));
}
void UploadList::GetUploads(size_t max_count,
@@ -154,15 +77,14 @@ void UploadList::GetUploads(size_t max_count,
std::back_inserter(*uploads));
}
-void UploadList::RequestSingleCrashUploadAsync(const std::string& local_id) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&UploadList::RequestSingleCrashUpload, this, local_id));
-}
-
-void UploadList::RequestSingleCrashUpload(const std::string& local_id) {
+void UploadList::RequestSingleUpload(const std::string& local_id) {
// Manual uploads for not-yet uploaded crash reports are only available for
// Crashpad systems and for Android.
NOTREACHED();
}
+
+void UploadList::OnLoadComplete(const std::vector<UploadInfo>& uploads) {
+ uploads_ = uploads;
+ if (!callback_.is_null())
+ std::move(callback_).Run();
+}
diff --git a/chromium/components/upload_list/upload_list.h b/chromium/components/upload_list/upload_list.h
index ebcab5054bb..8b5a866ed33 100644
--- a/chromium/components/upload_list/upload_list.h
+++ b/chromium/components/upload_list/upload_list.h
@@ -10,26 +10,19 @@
#include <string>
#include <vector>
-#include "base/files/file_path.h"
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
+#include "base/task_scheduler/task_traits.h"
#include "base/time/time.h"
-namespace base {
-class SequencedTaskRunner;
-class TaskRunner;
-}
-
-// Loads and parses an upload list text file of the format
-// upload_time,upload_id[,local_id[,capture_time[,state]]]
-// upload_time,upload_id[,local_id[,capture_time[,state]]]
-// etc.
-// where each line represents an upload. |upload_time| and |capture_time| are in
-// Unix time. |state| is an int in the range of UploadInfo::State. Must be used
-// from the UI thread. The loading and parsing is done on a blocking pool task
-// runner. A line may or may not contain |local_id|, |capture_time|, and
-// |state|.
+// An UploadList is an abstraction over a list of client-side data files that
+// are uploaded to a server. The UploadList allows accessing the UploadInfo
+// for these files, usually to display in a UI.
+//
+// The UploadList loads the information asynchronously and notifies the
+// client that requested the information when it is available.
class UploadList : public base::RefCountedThreadSafe<UploadList> {
public:
struct UploadInfo {
@@ -71,74 +64,52 @@ class UploadList : public base::RefCountedThreadSafe<UploadList> {
base::string16 file_size;
};
- class Delegate {
- public:
- // Invoked when the upload list has been loaded. Will be called on the
- // UI thread.
- virtual void OnUploadListAvailable() = 0;
-
- protected:
- virtual ~Delegate() {}
- };
-
// Creates a new upload list with the given callback delegate.
- UploadList(Delegate* delegate,
- const base::FilePath& upload_log_path,
- scoped_refptr<base::TaskRunner> task_runner);
+ UploadList();
// Starts loading the upload list. OnUploadListAvailable will be called when
- // loading is complete.
- void LoadUploadListAsynchronously();
+ // loading is complete. If this is called twice, the second |callback| will
+ // overwrite the previously supplied one, and the first will not be called.
+ void Load(base::OnceClosure callback);
- // Asynchronously requests a user triggered upload.
- void RequestSingleCrashUploadAsync(const std::string& local_id);
+ // Clears any callback specified in Load().
+ void CancelCallback();
- // Clears the delegate, so that any outstanding asynchronous load will not
- // call the delegate on completion.
- void ClearDelegate();
+ // Asynchronously requests a user triggered upload.
+ void RequestSingleUploadAsync(const std::string& local_id);
// Populates |uploads| with the |max_count| most recent uploads,
// in reverse chronological order.
- // Must be called only after OnUploadListAvailable has been called.
+ // Must be called only after a Load() callback has been received.
void GetUploads(size_t max_count, std::vector<UploadInfo>* uploads);
protected:
virtual ~UploadList();
+ // Returns the TaskTraits that should be used for LoadUploadList() and
+ // RequestSingleUpload().
+ virtual base::TaskTraits LoadingTaskTraits() = 0;
+
// Reads the upload log and stores the entries in |uploads|.
- virtual void LoadUploadList(std::vector<UploadInfo>* uploads);
+ virtual std::vector<UploadInfo> LoadUploadList() = 0;
// Requests a user triggered upload for a crash report with a given id.
- virtual void RequestSingleCrashUpload(const std::string& local_id);
-
- const base::FilePath& upload_log_path() const;
+ virtual void RequestSingleUpload(const std::string& local_id);
private:
friend class base::RefCountedThreadSafe<UploadList>;
- // Manages the background thread work for LoadUploadListAsynchronously().
- void PerformLoadAndNotifyDelegate(
- scoped_refptr<base::SequencedTaskRunner> task_runner);
-
- // Calls the delegate's callback method, if there is a delegate. Stores
- // the newly loaded |uploads| into |uploads_| on the delegate's task runner.
- void SetUploadsAndNotifyDelegate(std::vector<UploadInfo> uploads);
-
- // Parses upload log lines, converting them to UploadInfo entries.
- void ParseLogEntries(const std::vector<std::string>& log_entries,
- std::vector<UploadInfo>* uploads);
+ // When LoadUploadList() finishes, the results are reported in |uploads|
+ // and the |callback_| is run.
+ void OnLoadComplete(const std::vector<UploadInfo>& uploads);
// Ensures that this class' thread unsafe state is only accessed from the
// sequence that owns this UploadList.
base::SequenceChecker sequence_checker_;
- std::vector<UploadInfo> uploads_;
-
- Delegate* delegate_;
-
- const base::FilePath upload_log_path_;
+ base::OnceClosure callback_;
- const scoped_refptr<base::TaskRunner> task_runner_;
+ std::vector<UploadInfo> uploads_;
DISALLOW_COPY_AND_ASSIGN(UploadList);
};
diff --git a/chromium/components/upload_list/upload_list_unittest.cc b/chromium/components/upload_list/upload_list_unittest.cc
deleted file mode 100644
index 837f7fb817f..00000000000
--- a/chromium/components/upload_list/upload_list_unittest.cc
+++ /dev/null
@@ -1,334 +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/upload_list/upload_list.h"
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task_runner.h"
-#include "base/threading/thread.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const char kTestUploadTime[] = "1234567890";
-const char kTestUploadId[] = "0123456789abcdef";
-const char kTestLocalID[] = "fedcba9876543210";
-const char kTestCaptureTime[] = "2345678901";
-
-class UploadListTest : public testing::Test,
- public UploadList::Delegate {
- public:
- UploadListTest() : worker_thread_("UploadListTest") {}
-
- void SetUp() override {
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- ASSERT_TRUE(worker_thread_.Start());
- }
-
- protected:
- void WriteUploadLog(const std::string& log_data) {
- ASSERT_GT(base::WriteFile(log_path(), log_data.c_str(),
- static_cast<int>(log_data.size())),
- 0);
- }
-
- void WaitForUploadList() {
- base::RunLoop run_loop;
- quit_closure_ = run_loop.QuitClosure();
- run_loop.Run();
- }
-
- // UploadList::Delegate:
- void OnUploadListAvailable() override {
- ASSERT_FALSE(quit_closure_.is_null());
- quit_closure_.Run();
- }
-
- scoped_refptr<base::TaskRunner> task_runner() const {
- return worker_thread_.task_runner();
- }
-
- base::FilePath log_path() {
- return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("uploads.log"));
- }
-
- private:
- base::MessageLoop message_loop_;
- base::ScopedTempDir temp_dir_;
- base::Thread worker_thread_;
- base::Closure quit_closure_;
-
- DISALLOW_COPY_AND_ASSIGN(UploadListTest);
-};
-
-// These tests test that UploadList can parse a vector of log entry strings of
-// various formats to a vector of UploadInfo objects. See the UploadList
-// declaration for a description of the log entry string formats.
-
-// Test log entry string with upload time and upload ID.
-// This is the format that crash reports are stored in.
-TEST_F(UploadListTest, ParseUploadTimeUploadId) {
- std::string test_entry = kTestUploadTime;
- test_entry += ",";
- test_entry.append(kTestUploadId);
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- upload_list->LoadUploadListAsynchronously();
- WaitForUploadList();
-
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(1u, uploads.size());
- double time_double = uploads[0].upload_time.ToDoubleT();
- EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
- EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
- EXPECT_STREQ("", uploads[0].local_id.c_str());
- time_double = uploads[0].capture_time.ToDoubleT();
- EXPECT_STREQ("0", base::DoubleToString(time_double).c_str());
-}
-
-// Test log entry string with upload time, upload ID and local ID.
-// This is the old format that WebRTC logs were stored in.
-TEST_F(UploadListTest, ParseUploadTimeUploadIdLocalId) {
- std::string test_entry = kTestUploadTime;
- test_entry += ",";
- test_entry.append(kTestUploadId);
- test_entry += ",";
- test_entry.append(kTestLocalID);
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- upload_list->LoadUploadListAsynchronously();
- WaitForUploadList();
-
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(1u, uploads.size());
- double time_double = uploads[0].upload_time.ToDoubleT();
- EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
- EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
- EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
- time_double = uploads[0].capture_time.ToDoubleT();
- EXPECT_STREQ("0", base::DoubleToString(time_double).c_str());
-}
-
-// Test log entry string with upload time, upload ID and capture time.
-// This is the format that WebRTC logs that only have been uploaded only are
-// stored in.
-TEST_F(UploadListTest, ParseUploadTimeUploadIdCaptureTime) {
- std::string test_entry = kTestUploadTime;
- test_entry += ",";
- test_entry.append(kTestUploadId);
- test_entry += ",,";
- test_entry.append(kTestCaptureTime);
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- upload_list->LoadUploadListAsynchronously();
- WaitForUploadList();
-
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(1u, uploads.size());
- double time_double = uploads[0].upload_time.ToDoubleT();
- EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
- EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
- EXPECT_STREQ("", uploads[0].local_id.c_str());
- time_double = uploads[0].capture_time.ToDoubleT();
- EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
-}
-
-// Test log entry string with local ID and capture time.
-// This is the format that WebRTC logs that only are stored locally are stored
-// in.
-TEST_F(UploadListTest, ParseLocalIdCaptureTime) {
- std::string test_entry = ",,";
- test_entry.append(kTestLocalID);
- test_entry += ",";
- test_entry.append(kTestCaptureTime);
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- upload_list->LoadUploadListAsynchronously();
- WaitForUploadList();
-
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(1u, uploads.size());
- double time_double = uploads[0].upload_time.ToDoubleT();
- EXPECT_STREQ("0", base::DoubleToString(time_double).c_str());
- EXPECT_STREQ("", uploads[0].upload_id.c_str());
- EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
- time_double = uploads[0].capture_time.ToDoubleT();
- EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
-}
-
-// Test log entry string with upload time, upload ID, local ID and capture
-// time.
-// This is the format that WebRTC logs that are stored locally and have been
-// uploaded are stored in.
-TEST_F(UploadListTest, ParseUploadTimeUploadIdLocalIdCaptureTime) {
- std::string test_entry = kTestUploadTime;
- test_entry += ",";
- test_entry.append(kTestUploadId);
- test_entry += ",";
- test_entry.append(kTestLocalID);
- test_entry += ",";
- test_entry.append(kTestCaptureTime);
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- upload_list->LoadUploadListAsynchronously();
- WaitForUploadList();
-
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(1u, uploads.size());
- double time_double = uploads[0].upload_time.ToDoubleT();
- EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
- EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
- EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
- time_double = uploads[0].capture_time.ToDoubleT();
- EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
-}
-
-TEST_F(UploadListTest, ParseMultipleEntries) {
- std::string test_entry;
- for (int i = 1; i <= 4; ++i) {
- test_entry.append(kTestUploadTime);
- test_entry += ",";
- test_entry.append(kTestUploadId);
- test_entry += ",";
- test_entry.append(kTestLocalID);
- test_entry += ",";
- test_entry.append(kTestCaptureTime);
- test_entry += "\n";
- }
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- upload_list->LoadUploadListAsynchronously();
- WaitForUploadList();
-
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(4u, uploads.size());
- for (size_t i = 0; i < uploads.size(); ++i) {
- double time_double = uploads[i].upload_time.ToDoubleT();
- EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
- EXPECT_STREQ(kTestUploadId, uploads[i].upload_id.c_str());
- EXPECT_STREQ(kTestLocalID, uploads[i].local_id.c_str());
- time_double = uploads[i].capture_time.ToDoubleT();
- EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
- }
-}
-
-TEST_F(UploadListTest, ParseWithState) {
- std::string test_entry;
- for (int i = 1; i <= 4; ++i) {
- test_entry.append(kTestUploadTime);
- test_entry += ",";
- test_entry.append(kTestUploadId);
- test_entry += ",";
- test_entry.append(kTestLocalID);
- test_entry += ",";
- test_entry.append(kTestCaptureTime);
- test_entry += ",";
- test_entry.append(base::IntToString(
- static_cast<int>(UploadList::UploadInfo::State::Uploaded)));
- test_entry += "\n";
- }
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- upload_list->LoadUploadListAsynchronously();
- WaitForUploadList();
-
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(4u, uploads.size());
- for (size_t i = 0; i < uploads.size(); ++i) {
- double time_double = uploads[i].upload_time.ToDoubleT();
- EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
- EXPECT_STREQ(kTestUploadId, uploads[i].upload_id.c_str());
- EXPECT_STREQ(kTestLocalID, uploads[i].local_id.c_str());
- time_double = uploads[i].capture_time.ToDoubleT();
- EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
- EXPECT_EQ(UploadList::UploadInfo::State::Uploaded, uploads[i].state);
- }
-}
-
-// https://crbug.com/597384
-TEST_F(UploadListTest, SimultaneousAccess) {
- std::string test_entry = kTestUploadTime;
- test_entry += ",";
- test_entry.append(kTestUploadId);
- test_entry += ",";
- test_entry.append(kTestLocalID);
- test_entry += ",";
- test_entry.append(kTestCaptureTime);
- WriteUploadLog(test_entry);
-
- scoped_refptr<UploadList> upload_list =
- new UploadList(this, log_path(), task_runner());
-
- // Queue up a bunch of loads, waiting only for the first one to complete.
- // Clearing the delegate prevents the QuitClosure from being Run more than
- // once.
- for (int i = 1; i <= 20; ++i) {
- upload_list->LoadUploadListAsynchronously();
- }
- WaitForUploadList();
- upload_list->ClearDelegate();
-
- // Read the list a few times to try and race one of the loads above.
- for (int i = 1; i <= 4; ++i) {
- std::vector<UploadList::UploadInfo> uploads;
- upload_list->GetUploads(999, &uploads);
-
- EXPECT_EQ(1u, uploads.size());
- double time_double = uploads[0].upload_time.ToDoubleT();
- EXPECT_STREQ(kTestUploadTime, base::DoubleToString(time_double).c_str());
- EXPECT_STREQ(kTestUploadId, uploads[0].upload_id.c_str());
- EXPECT_STREQ(kTestLocalID, uploads[0].local_id.c_str());
- time_double = uploads[0].capture_time.ToDoubleT();
- EXPECT_STREQ(kTestCaptureTime, base::DoubleToString(time_double).c_str());
- }
-}
-
-} // namespace
diff --git a/chromium/components/url_formatter/BUILD.gn b/chromium/components/url_formatter/BUILD.gn
index 3ed810c276a..b4ce96fc684 100644
--- a/chromium/components/url_formatter/BUILD.gn
+++ b/chromium/components/url_formatter/BUILD.gn
@@ -8,8 +8,6 @@ if (is_android) {
static_library("url_formatter") {
sources = [
- "android/component_jni_registrar.cc",
- "android/component_jni_registrar.h",
"elide_url.cc",
"elide_url.h",
"idn_spoof_checker.cc",
@@ -19,7 +17,6 @@ static_library("url_formatter") {
"url_formatter.cc",
"url_formatter.h",
"url_formatter_android.cc",
- "url_formatter_android.h",
]
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
diff --git a/chromium/components/url_formatter/elide_url.cc b/chromium/components/url_formatter/elide_url.cc
index e19da1f7339..984de31952c 100644
--- a/chromium/components/url_formatter/elide_url.cc
+++ b/chromium/components/url_formatter/elide_url.cc
@@ -174,8 +174,12 @@ base::string16 ElideUrl(const GURL& url,
// Get the path substring, including query and reference.
const size_t path_start_index = parsed.path.begin;
const size_t path_len = parsed.path.len;
- base::string16 url_path_query_etc = url_string.substr(path_start_index);
- base::string16 url_path = url_string.substr(path_start_index, path_len);
+ base::string16 url_path_query_etc;
+ base::string16 url_path;
+ if (parsed.path.is_valid()) {
+ url_path_query_etc = url_string.substr(path_start_index);
+ url_path = url_string.substr(path_start_index, path_len);
+ }
// Return general elided text if url minus the query fits.
const base::string16 url_minus_query =
@@ -255,6 +259,8 @@ base::string16 ElideUrl(const GURL& url,
}
const size_t kMaxNumberOfUrlPathElementsAllowed = 1024;
+ // TODO(mgiuca): If there is no path, this means the end of the domain gets
+ // elided, not the start (inconsistent). https://crbug.com/739636.
if (url_path_number_of_elements <= 1 ||
url_path_number_of_elements > kMaxNumberOfUrlPathElementsAllowed) {
// No path to elide, or too long of a path (could overflow in loop below)
diff --git a/chromium/components/url_formatter/elide_url_unittest.cc b/chromium/components/url_formatter/elide_url_unittest.cc
index 4ded2f99db3..54f70243e03 100644
--- a/chromium/components/url_formatter/elide_url_unittest.cc
+++ b/chromium/components/url_formatter/elide_url_unittest.cc
@@ -30,7 +30,7 @@ struct Testcase {
#if !defined(OS_ANDROID)
void RunUrlTest(Testcase* testcases, size_t num_testcases) {
- static const gfx::FontList font_list;
+ const gfx::FontList font_list;
for (size_t i = 0; i < num_testcases; ++i) {
const GURL url(testcases[i].input);
const float available_width =
@@ -110,10 +110,35 @@ TEST(TextEliderTest, TestMoreEliding) {
#endif
const std::string kEllipsisStr(gfx::kEllipsis);
Testcase testcases[] = {
+ // Eliding the same URL to various lengths.
{"http://www.google.com/foo?bar", "www.google.com/foo?bar"},
{"http://xyz.google.com/foo?bar", "xyz.google.com/foo?" + kEllipsisStr},
{"http://xyz.google.com/foo?bar", "xyz.google.com/foo" + kEllipsisStr},
{"http://xyz.google.com/foo?bar", "xyz.google.com/fo" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar",
+ kEllipsisStr + "google.com/fo" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar",
+ kEllipsisStr + "google.com/f" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar",
+ kEllipsisStr + "google.com/" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar",
+ kEllipsisStr + "google.com" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar",
+ kEllipsisStr + "google.co" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar",
+ kEllipsisStr + "google.c" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar",
+ kEllipsisStr + "google." + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar", kEllipsisStr + "google" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar", kEllipsisStr + "googl" + kEllipsisStr},
+ {"http://xyz.google.com/foo?bar", kEllipsisStr + "g" + kEllipsisStr},
+
+ // URL with no path.
+ // TODO(mgiuca): These should elide the start of the URL, not the end.
+ // https://crbug.com/739636.
+ {"http://xyz.google.com", "xyz.google" + kEllipsisStr},
+ {"https://xyz.google.com", "xyz.google" + kEllipsisStr},
+
{"http://a.b.com/pathname/c?d", "a.b.com/" + kEllipsisStr + "/c?d"},
{"", ""},
{"http://foo.bar..example.com...hello/test/filename.html",
diff --git a/chromium/components/url_formatter/idn_spoof_checker.cc b/chromium/components/url_formatter/idn_spoof_checker.cc
index 17de0fd9c65..1c4d9bdb74f 100644
--- a/chromium/components/url_formatter/idn_spoof_checker.cc
+++ b/chromium/components/url_formatter/idn_spoof_checker.cc
@@ -240,6 +240,8 @@ bool IDNSpoofChecker::SafeToDisplayAsUnicode(base::StringPiece16 label,
// - Disallow combining diacritical mark (U+0300-U+0339) after a non-LGC
// character. Other combining diacritical marks are not in the allowed
// character set.
+ // - Disallow Arabic non-spacing marks after non-Arabic characters.
+ // - Disallow Hebrew non-spacing marks after non-Hebrew characters.
dangerous_pattern = new icu::RegexMatcher(
icu::UnicodeString(
R"([^\p{scx=kana}\p{scx=hira}\p{scx=hani}])"
@@ -370,14 +372,26 @@ void IDNSpoofChecker::SetAllowedUnicodeSet(UErrorCode* status) {
// blacklisted by Mozilla. We keep it, even though it can look like a double
// quotation mark. Using it in Hebrew should be safe. When used with a
// non-Hebrew script, it'd be filtered by other checks in place.
- //
+
+ // The following 5 characters are disallowed because they're in NV8 (invalid
+ // in IDNA 2008).
+ allowed_set.remove(0x58au); // Armenian Hyphen
// U+2010 (Hyphen) is in the inclusion set, but we drop it because it can be
// confused with an ASCII U+002D (Hyphen-Minus).
allowed_set.remove(0x2010u);
+ // U+2019 is hard to notice when sitting next to a regular character.
+ allowed_set.remove(0x2019u); // Right Single Quotation Mark
// U+2027 (Hyphenation Point) is in the inclusion set, but is blacklisted by
// Mozilla. It is dropped, as it can be confused with U+30FB (Katakana Middle
// Dot).
allowed_set.remove(0x2027u);
+ allowed_set.remove(0x30a0u); // Katakana-Hiragana Double Hyphen
+
+ // Block {Single,double}-quotation-mark look-alikes.
+ allowed_set.remove(0x2bbu); // Modifier Letter Turned Comma
+ allowed_set.remove(0x2bcu); // Modifier Letter Apostrophe
+ // No need to block U+144A (Canadian Syllabics West-Cree P) separately
+ // because it's blocked from mixing with other scripts including Latin.
#if defined(OS_MACOSX)
// The following characters are reported as present in the default macOS
diff --git a/chromium/components/url_formatter/top_domains/BUILD.gn b/chromium/components/url_formatter/top_domains/BUILD.gn
index a24132b39ab..67719cd9da5 100644
--- a/chromium/components/url_formatter/top_domains/BUILD.gn
+++ b/chromium/components/url_formatter/top_domains/BUILD.gn
@@ -26,6 +26,7 @@ if (!is_ios && !is_android) {
deps = [
"//base",
"//base:i18n",
+ "//build/config:exe_and_shlib_deps",
"//third_party/icu",
]
}
diff --git a/chromium/components/url_formatter/url_formatter.cc b/chromium/components/url_formatter/url_formatter.cc
index 298b1c18ff4..461bc7942c5 100644
--- a/chromium/components/url_formatter/url_formatter.cc
+++ b/chromium/components/url_formatter/url_formatter.cc
@@ -12,11 +12,13 @@
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_piece.h"
+#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_offset_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_local_storage.h"
#include "components/url_formatter/idn_spoof_checker.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "third_party/icu/source/common/unicode/uidna.h"
#include "third_party/icu/source/common/unicode/utypes.h"
#include "url/gurl.h"
@@ -50,14 +52,60 @@ class AppendComponentTransform {
class HostComponentTransform : public AppendComponentTransform {
public:
- HostComponentTransform() {}
+ HostComponentTransform(bool trim_trivial_subdomains)
+ : trim_trivial_subdomains_(trim_trivial_subdomains) {}
private:
base::string16 Execute(
const std::string& component_text,
base::OffsetAdjuster::Adjustments* adjustments) const override {
- return IDNToUnicodeWithAdjustments(component_text, adjustments);
+ if (!trim_trivial_subdomains_)
+ return IDNToUnicodeWithAdjustments(component_text, adjustments);
+
+ // Exclude the registry and domain from trivial subdomain stripping.
+ // To get the adjustment offset calculations correct, we need to transform
+ // the registry and domain portion of the host as well.
+ std::string domain_and_registry =
+ net::registry_controlled_domains::GetDomainAndRegistry(
+ component_text,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+
+ base::OffsetAdjuster::Adjustments trivial_subdomains_adjustments;
+ base::StringTokenizer tokenizer(
+ component_text.begin(),
+ component_text.end() - domain_and_registry.length(), ".");
+ tokenizer.set_options(base::StringTokenizer::RETURN_DELIMS);
+
+ std::string transformed_subdomain;
+ while (tokenizer.GetNext()) {
+ // Append delimiters and non-trivial subdomains to the new subdomain part.
+ if (tokenizer.token_is_delim() ||
+ (tokenizer.token() != "m" && tokenizer.token() != "www")) {
+ transformed_subdomain += tokenizer.token();
+ continue;
+ }
+
+ // We found a trivial subdomain, so we add an adjustment accounting for
+ // the subdomain and the following consumed delimiter.
+ size_t trivial_subdomain_begin =
+ tokenizer.token_begin() - component_text.begin();
+ trivial_subdomains_adjustments.push_back(base::OffsetAdjuster::Adjustment(
+ trivial_subdomain_begin, tokenizer.token().length() + 1, 0));
+
+ // Consume the next token, which must be a delimiter.
+ bool next_delimiter_found = tokenizer.GetNext();
+ DCHECK(next_delimiter_found);
+ DCHECK(tokenizer.token_is_delim());
+ }
+
+ base::string16 unicode_result = IDNToUnicodeWithAdjustments(
+ transformed_subdomain + domain_and_registry, adjustments);
+ base::OffsetAdjuster::MergeSequentialAdjustments(
+ trivial_subdomains_adjustments, adjustments);
+ return unicode_result;
}
+
+ bool trim_trivial_subdomains_;
};
class NonHostComponentTransform : public AppendComponentTransform {
@@ -360,6 +408,9 @@ const FormatUrlType kFormatUrlOmitTrailingSlashOnBareHostname = 1 << 2;
const FormatUrlType kFormatUrlOmitAll =
kFormatUrlOmitUsernamePassword | kFormatUrlOmitHTTP |
kFormatUrlOmitTrailingSlashOnBareHostname;
+const FormatUrlType kFormatUrlExperimentalElideAfterHost = 1 << 3;
+const FormatUrlType kFormatUrlExperimentalOmitHTTPS = 1 << 4;
+const FormatUrlType kFormatUrlExperimentalOmitTrivialSubdomains = 1 << 5;
base::string16 FormatUrl(const GURL& url,
FormatUrlTypes format_types,
@@ -367,14 +418,13 @@ base::string16 FormatUrl(const GURL& url,
url::Parsed* new_parsed,
size_t* prefix_end,
size_t* offset_for_adjustment) {
- std::vector<size_t> offsets;
- if (offset_for_adjustment)
- offsets.push_back(*offset_for_adjustment);
- base::string16 result =
- FormatUrlWithOffsets(url, format_types, unescape_rules, new_parsed,
- prefix_end, &offsets);
- if (offset_for_adjustment)
- *offset_for_adjustment = offsets[0];
+ base::OffsetAdjuster::Adjustments adjustments;
+ base::string16 result = FormatUrlWithAdjustments(
+ url, format_types, unescape_rules, new_parsed, prefix_end, &adjustments);
+ if (offset_for_adjustment) {
+ base::OffsetAdjuster::AdjustOffset(adjustments, offset_for_adjustment,
+ result.length());
+ }
return result;
}
@@ -386,16 +436,11 @@ base::string16 FormatUrlWithOffsets(
size_t* prefix_end,
std::vector<size_t>* offsets_for_adjustment) {
base::OffsetAdjuster::Adjustments adjustments;
- const base::string16& format_url_return_value =
- FormatUrlWithAdjustments(url, format_types, unescape_rules, new_parsed,
- prefix_end, &adjustments);
- base::OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
- if (offsets_for_adjustment) {
- std::for_each(
- offsets_for_adjustment->begin(), offsets_for_adjustment->end(),
- base::LimitOffset<std::string>(format_url_return_value.length()));
- }
- return format_url_return_value;
+ const base::string16& result = FormatUrlWithAdjustments(
+ url, format_types, unescape_rules, new_parsed, prefix_end, &adjustments);
+ base::OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment,
+ result.length());
+ return result;
}
base::string16 FormatUrlWithAdjustments(
@@ -405,7 +450,7 @@ base::string16 FormatUrlWithAdjustments(
url::Parsed* new_parsed,
size_t* prefix_end,
base::OffsetAdjuster::Adjustments* adjustments) {
- DCHECK(adjustments != NULL);
+ DCHECK(adjustments);
adjustments->clear();
url::Parsed parsed_temp;
if (!new_parsed)
@@ -431,22 +476,10 @@ base::string16 FormatUrlWithAdjustments(
const url::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
// Scheme & separators. These are ASCII.
+ const size_t scheme_size = static_cast<size_t>(parsed.CountCharactersBefore(
+ url::Parsed::USERNAME, true /* include_delimiter */));
base::string16 url_string;
- url_string.insert(
- url_string.end(), spec.begin(),
- spec.begin() + parsed.CountCharactersBefore(url::Parsed::USERNAME, true));
- const char kHTTP[] = "http://";
- const char kFTP[] = "ftp.";
- // url_formatter::FixupURL() treats "ftp.foo.com" as ftp://ftp.foo.com. This
- // means that if we trim "http://" off a URL whose host starts with "ftp." and
- // the user inputs this into any field subject to fixup (which is basically
- // all input fields), the meaning would be changed. (In fact, often the
- // formatted URL is directly pre-filled into an input field.) For this reason
- // we avoid stripping "http://" in this case.
- bool omit_http =
- (format_types & kFormatUrlOmitHTTP) &&
- base::EqualsASCII(url_string, kHTTP) &&
- !base::StartsWith(url.host(), kFTP, base::CompareCase::SENSITIVE);
+ url_string.insert(url_string.end(), spec.begin(), spec.begin() + scheme_size);
new_parsed->scheme = parsed.scheme;
// Username & password.
@@ -491,7 +524,10 @@ base::string16 FormatUrlWithAdjustments(
*prefix_end = static_cast<size_t>(url_string.length());
// Host.
- AppendFormattedComponent(spec, parsed.host, HostComponentTransform(),
+ bool trim_trivial_subdomains =
+ (format_types & kFormatUrlExperimentalOmitTrivialSubdomains) != 0;
+ AppendFormattedComponent(spec, parsed.host,
+ HostComponentTransform(trim_trivial_subdomains),
&url_string, &new_parsed->host, adjustments);
// Port.
@@ -506,44 +542,83 @@ base::string16 FormatUrlWithAdjustments(
}
// Path & query. Both get the same general unescape & convert treatment.
- if (!(format_types & kFormatUrlOmitTrailingSlashOnBareHostname) ||
- !CanStripTrailingSlash(url)) {
- AppendFormattedComponent(spec, parsed.path,
- NonHostComponentTransform(unescape_rules),
- &url_string, &new_parsed->path, adjustments);
- } else {
+ if ((format_types & kFormatUrlOmitTrailingSlashOnBareHostname) &&
+ CanStripTrailingSlash(url)) {
+ // Omit the path, which is a single trailing slash. There's no query or ref.
if (parsed.path.len > 0) {
adjustments->push_back(base::OffsetAdjuster::Adjustment(
parsed.path.begin, parsed.path.len, 0));
}
+ } else if ((format_types & kFormatUrlExperimentalElideAfterHost) &&
+ url.IsStandard() && !url.SchemeIsFile() &&
+ !url.SchemeIsFileSystem()) {
+ // Replace everything after the host with a forward slash and ellipsis.
+ url_string.push_back('/');
+ constexpr base::char16 kEllipsisUTF16[] = {0x2026, 0};
+ url_string.append(kEllipsisUTF16);
+
+ // Compute the length of everything we're replacing. For the path, we are
+ // removing everything but the first slash.
+ size_t old_length = parsed.path.len - 1;
+
+ // We're also removing any query, plus the delimiting '?'.
+ if (parsed.query.is_valid())
+ old_length += parsed.query.len + 1;
+
+ // We're also removing any ref, plus the delimiting '#'.
+ if (parsed.ref.is_valid())
+ old_length += parsed.ref.len + 1;
+
+ // We're replacing all of these with a single character (an ellipsis). The
+ // adjustment begins after the forward slash at the beginning of the path.
+ adjustments->push_back(
+ base::OffsetAdjuster::Adjustment(parsed.path.begin + 1, old_length, 1));
+ } else {
+ // Append the formatted path, query, and ref.
+ AppendFormattedComponent(spec, parsed.path,
+ NonHostComponentTransform(unescape_rules),
+ &url_string, &new_parsed->path, adjustments);
+
+ if (parsed.query.is_valid())
+ url_string.push_back('?');
+ AppendFormattedComponent(spec, parsed.query,
+ NonHostComponentTransform(unescape_rules),
+ &url_string, &new_parsed->query, adjustments);
+
+ // Ref. This is valid, unescaped UTF-8, so we can just convert.
+ if (parsed.ref.is_valid())
+ url_string.push_back('#');
+ AppendFormattedComponent(spec, parsed.ref,
+ NonHostComponentTransform(net::UnescapeRule::NONE),
+ &url_string, &new_parsed->ref, adjustments);
}
- if (parsed.query.is_valid())
- url_string.push_back('?');
- AppendFormattedComponent(spec, parsed.query,
- NonHostComponentTransform(unescape_rules),
- &url_string, &new_parsed->query, adjustments);
-
- // Ref. This is valid, unescaped UTF-8, so we can just convert.
- if (parsed.ref.is_valid())
- url_string.push_back('#');
- AppendFormattedComponent(spec, parsed.ref,
- NonHostComponentTransform(net::UnescapeRule::NONE),
- &url_string, &new_parsed->ref, adjustments);
-
- // If we need to strip out http do it after the fact.
- if (omit_http && base::StartsWith(url_string, base::ASCIIToUTF16(kHTTP),
- base::CompareCase::SENSITIVE)) {
- const size_t kHTTPSize = arraysize(kHTTP) - 1;
- url_string = url_string.substr(kHTTPSize);
+
+ // url_formatter::FixupURL() treats "ftp.foo.com" as ftp://ftp.foo.com. This
+ // means that if we trim the scheme off a URL whose host starts with "ftp."
+ // and the user inputs this into any field subject to fixup (which is
+ // basically all input fields), the meaning would be changed. (In fact, often
+ // the formatted URL is directly pre-filled into an input field.) For this
+ // reason we avoid stripping schemes in this case.
+ const char kFTP[] = "ftp.";
+ bool strip_scheme =
+ !base::StartsWith(url.host(), kFTP, base::CompareCase::SENSITIVE) &&
+ (((format_types & kFormatUrlOmitHTTP) &&
+ url.SchemeIs(url::kHttpScheme)) ||
+ ((format_types & kFormatUrlExperimentalOmitHTTPS) &&
+ url.SchemeIs(url::kHttpsScheme)));
+
+ // If we need to strip out schemes do it after the fact.
+ if (strip_scheme) {
+ url_string.erase(0, scheme_size);
// Because offsets in the |adjustments| are already calculated with respect
// to the string with the http:// prefix in it, those offsets remain correct
// after stripping the prefix. The only thing necessary is to add an
// adjustment to reflect the stripped prefix.
adjustments->insert(adjustments->begin(),
- base::OffsetAdjuster::Adjustment(0, kHTTPSize, 0));
+ base::OffsetAdjuster::Adjustment(0, scheme_size, 0));
if (prefix_end)
- *prefix_end -= kHTTPSize;
+ *prefix_end -= scheme_size;
// Adjust new_parsed.
DCHECK(new_parsed->scheme.is_valid());
@@ -565,7 +640,7 @@ bool CanStripTrailingSlash(const GURL& url) {
void AppendFormattedHost(const GURL& url, base::string16* output) {
AppendFormattedComponent(
url.possibly_invalid_spec(), url.parsed_for_possibly_invalid_spec().host,
- HostComponentTransform(), output, NULL, NULL);
+ HostComponentTransform(false), output, nullptr, nullptr);
}
base::string16 IDNToUnicode(base::StringPiece host) {
diff --git a/chromium/components/url_formatter/url_formatter.h b/chromium/components/url_formatter/url_formatter.h
index 90f2bbf9b73..a44f60eeb5c 100644
--- a/chromium/components/url_formatter/url_formatter.h
+++ b/chromium/components/url_formatter/url_formatter.h
@@ -51,9 +51,21 @@ extern const FormatUrlType kFormatUrlOmitHTTP;
// meaningful for non-file "standard" URLs.
extern const FormatUrlType kFormatUrlOmitTrailingSlashOnBareHostname;
-// Convenience for omitting all unecessary types.
+// Convenience for omitting all unecessary types. Does not include experimental
+// flags below.
extern const FormatUrlType kFormatUrlOmitAll;
+// Replaces the path, query, and ref with an ellipsis. Experimental and not in
+// kFormatUrlOmitAll.
+extern const FormatUrlType kFormatUrlExperimentalElideAfterHost;
+
+// If the scheme is 'https://', it's removed. Experimental and not in
+// kFormatUrlOmitAll.
+extern const FormatUrlType kFormatUrlExperimentalOmitHTTPS;
+
+// Omits some trivially informative subdomains such as "www" or "m".
+extern const FormatUrlType kFormatUrlExperimentalOmitTrivialSubdomains;
+
// Creates a string representation of |url|. The IDN host name is turned to
// Unicode if the Unicode representation is deemed safe. |format_type| is a
// bitmask of FormatUrlTypes, see it for details. |unescape_rules| defines how
diff --git a/chromium/components/url_formatter/url_formatter_android.cc b/chromium/components/url_formatter/url_formatter_android.cc
index bc2e16cb9f5..aca32139056 100644
--- a/chromium/components/url_formatter/url_formatter_android.cc
+++ b/chromium/components/url_formatter/url_formatter_android.cc
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/url_formatter/url_formatter_android.h"
-
#include <string>
#include "base/android/jni_android.h"
@@ -68,10 +66,6 @@ static ScopedJavaLocalRef<jstring> FormatUrlForSecurityDisplayOmitScheme(
url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS));
}
-bool RegisterUrlFormatterNatives(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
} // namespace android
} // namespace url_formatter
diff --git a/chromium/components/url_formatter/url_formatter_android.h b/chromium/components/url_formatter/url_formatter_android.h
deleted file mode 100644
index fdf3d91f575..00000000000
--- a/chromium/components/url_formatter/url_formatter_android.h
+++ /dev/null
@@ -1,20 +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_URL_FORMATTER_URL_FORMATTER_ANDROID_H_
-#define COMPONENTS_URL_FORMATTER_URL_FORMATTER_ANDROID_H_
-
-#include <jni.h>
-
-namespace url_formatter {
-
-namespace android {
-
-bool RegisterUrlFormatterNatives(JNIEnv* env);
-
-} // namespace android
-
-} // namespace url_formatter
-
-#endif // COMPONENTS_URL_FORMATTER_URL_FORMATTER_ANDROID_H_
diff --git a/chromium/components/url_formatter/url_formatter_unittest.cc b/chromium/components/url_formatter/url_formatter_unittest.cc
index 62876358470..49f03f163e1 100644
--- a/chromium/components/url_formatter/url_formatter_unittest.cc
+++ b/chromium/components/url_formatter/url_formatter_unittest.cc
@@ -53,466 +53,626 @@ struct IDNTestCase {
// TODO(jshin): Replace L"..." with "..." in UTF-8 when it's easier to read.
const IDNTestCase idn_cases[] = {
- // No IDN
- {"www.google.com", L"www.google.com", true},
- {"www.google.com.", L"www.google.com.", true},
- {".", L".", true},
- {"", L"", true},
- // IDN
- // Hanzi (Traditional Chinese)
- {"xn--1lq90ic7f1rc.cn", L"\x5317\x4eac\x5927\x5b78.cn", true},
- // Hanzi ('video' in Simplified Chinese
- {"xn--cy2a840a.com", L"\x89c6\x9891.com", true},
- // Hanzi + '123'
- {"www.xn--123-p18d.com", L"www.\x4e00" L"123.com", true},
- // Hanzi + Latin : U+56FD is simplified
- {"www.xn--hello-9n1hm04c.com", L"www.hello\x4e2d\x56fd.com", true},
- // Kanji + Kana (Japanese)
- {"xn--l8jvb1ey91xtjb.jp", L"\x671d\x65e5\x3042\x3055\x3072.jp", true},
- // Katakana including U+30FC
- {"xn--tckm4i2e.jp", L"\x30b3\x30de\x30fc\x30b9.jp", true},
- {"xn--3ck7a7g.jp", L"\u30ce\u30f3\u30bd.jp", true},
- // Katakana + Latin (Japanese)
- {"xn--e-efusa1mzf.jp", L"e\x30b3\x30de\x30fc\x30b9.jp", true},
- {"xn--3bkxe.jp", L"\x30c8\x309a.jp", true},
- // Hangul (Korean)
- {"www.xn--or3b17p6jjc.kr", L"www.\xc804\xc790\xc815\xbd80.kr", true},
- // b<u-umlaut>cher (German)
- {"xn--bcher-kva.de", L"b\x00fc" L"cher.de", true},
- // a with diaeresis
- {"www.xn--frgbolaget-q5a.se", L"www.f\x00e4rgbolaget.se", true},
- // c-cedilla (French)
- {"www.xn--alliancefranaise-npb.fr",
- L"www.alliancefran\x00e7" L"aise.fr", true},
- // caf'e with acute accent' (French)
- {"xn--caf-dma.fr", L"caf\x00e9.fr", true},
- // c-cedillla and a with tilde (Portuguese)
- {"xn--poema-9qae5a.com.br", L"p\x00e3oema\x00e7\x00e3.com.br", true},
- // s with caron
- {"xn--achy-f6a.com", L"\x0161" L"achy.com", true},
- {"xn--kxae4bafwg.gr", L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1.gr",
- true},
- // Eutopia + 123 (Greek)
- {"xn---123-pldm0haj2bk.gr",
- L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1-123.gr", true},
- // Cyrillic (Russian)
- {"xn--n1aeec9b.ru", L"\x0442\x043e\x0440\x0442\x044b.ru", true},
- // Cyrillic + 123 (Russian)
- {"xn---123-45dmmc5f.ru", L"\x0442\x043e\x0440\x0442\x044b-123.ru", true},
- // 'president' in Russian. Is a wholescript confusable, but allowed.
- {"xn--d1abbgf6aiiy.xn--p1ai",
- L"\x043f\x0440\x0435\x0437\x0438\x0434\x0435\x043d\x0442.\x0440\x0444",
- true},
- // Arabic
- {"xn--mgba1fmg.eg", L"\x0627\x0641\x0644\x0627\x0645.eg", true},
- // Hebrew
- {"xn--4dbib.he", L"\x05d5\x05d0\x05d4.he", true},
- // Hebrew + Common
- {"xn---123-ptf2c5c6bt.il", L"\x05e2\x05d1\x05e8\x05d9\x05ea-123.il", true},
- // Thai
- {"xn--12c2cc4ag3b4ccu.th",
- L"\x0e2a\x0e32\x0e22\x0e01\x0e32\x0e23\x0e1a\x0e34\x0e19.th", true},
- // Thai + Common
- {"xn---123-9goxcp8c9db2r.th",
- L"\x0e20\x0e32\x0e29\x0e32\x0e44\x0e17\x0e22-123.th", true},
- // Devangari (Hindi)
- {"www.xn--l1b6a9e1b7c.in", L"www.\x0905\x0915\x094b\x0932\x093e.in", true},
- // Devanagari + Common
- {"xn---123-kbjl2j0bl2k.in",
- L"\x0939\x093f\x0928\x094d\x0926\x0940-123.in", true},
-
- // 5 Aspirational scripts
- // Unifieid Canadian Syllabary
- {"xn--dfe0tte.ca", L"\x1456\x14c2\x14ef.ca", true},
- // Tifinagh
- {"xn--4ljxa2bb4a6bxb.ma",
- L"\x2d5c\x2d49\x2d3c\x2d49\x2d4f\x2d30\x2d56.ma", true},
- // Tifinagh with a disallowed character(U+2D6F)
- {"xn--hmjzaby5d5f.ma", L"\x2d5c\x2d49\x2d3c\x2d6f\x2d49\x2d4f.ma", false},
- // Yi
- {"xn--4o7a6e1x64c.cn", L"\xa188\xa320\xa071\xa0b7.cn", true},
- // Mongolian - 'ordu' (place, camp)
- {"xn--56ec8bp.cn", L"\x1823\x1837\x1833\x1824.cn", true},
- // Mongolian with a disallowed character
- {"xn--95e5de3ds.cn", L"\x1823\x1837\x1804\x1833\x1824.cn", false},
- // Miao/Pollad
- {"xn--2u0fpf0a.cn", L"\U00016f04\U00016f62\U00016f59.cn", true},
-
- // Script mixing tests
- // The following script combinations are allowed.
- // MODERATELY_RESTRICTIVE with Latin limited to ASCII-Latin.
- // ASCII-Latin + Japn (Kana + Han)
- // ASCII-Latin + Kore (Hangul + Han)
- // ASCII-Latin + Han + Bopomofo
- // ASCII-Latin + any allowed script other than Cyrillic, Greek, Cherokee
- // and Unified Canadian Syllabary
- // "payp<alpha>l.com"
- {"www.xn--paypl-g9d.com", L"payp\x03b1l.com", false},
- // google.gr with Greek omicron and epsilon
- {"xn--ggl-6xc1ca.gr", L"g\x03bf\x03bfgl\x03b5.gr", false},
- // google.ru with Cyrillic o
- {"xn--ggl-tdd6ba.ru", L"g\x043e\x043egl\x0435.ru", false},
- // h<e with acute>llo<China in Han>.cn
- {"xn--hllo-bpa7979ih5m.cn", L"h\x00e9llo\x4e2d\x56fd.cn", false},
- // <Greek rho><Cyrillic a><Cyrillic u>.ru
- {"xn--2xa6t2b.ru", L"\x03c1\x0430\x0443.ru", false},
- // Hangul + Latin
- {"xn--han-eb9ll88m.kr", L"\xd55c\xae00han.kr", true},
- // Hangul + Latin + Han with IDN ccTLD
- {"xn--han-or0kq92gkm3c.xn--3e0b707e",
- L"\xd55c\xae00han\x97d3.\xd55c\xad6d", true},
- // non-ASCII Latin + Hangul
- {"xn--caf-dma9024xvpg.kr", L"caf\x00e9\xce74\xd398.kr", false},
- // Hangul + Hiragana
- {"xn--y9j3b9855e.kr", L"\xd55c\x3072\x3089.kr", false},
- // <Hiragana>.<Hangul> is allowed because script mixing check is per label.
- {"xn--y9j3b.xn--3e0b707e", L"\x3072\x3089.\xd55c\xad6d", true},
- // Traditional Han + Latin
- {"xn--hanzi-u57ii69i.tw", L"\x6f22\x5b57hanzi.tw", true},
- // Simplified Han + Latin
- {"xn--hanzi-u57i952h.cn", L"\x6c49\x5b57hanzi.cn", true},
- // Simplified Han + Traditonal Han
- {"xn--hanzi-if9kt8n.cn", L"\x6c49\x6f22hanzi.cn", true},
- // Han + Hiragana + Katakana + Latin
- {"xn--kanji-ii4dpizfq59yuykqr4b.jp",
- L"\x632f\x308a\x4eee\x540d\x30ab\x30bfkanji.jp", true},
- // Han + Bopomofo
- {"xn--5ekcde0577e87tc.tw", L"\x6ce8\x97f3\x3105\x3106\x3107\x3108.tw", true},
- // Han + Latin + Bopomofo
- {"xn--bopo-ty4cghi8509kk7xd.tw",
- L"\x6ce8\x97f3" L"bopo\x3105\x3106\x3107\x3108.tw", true},
- // Latin + Bopomofo
- {"xn--bopomofo-hj5gkalm.tw", L"bopomofo\x3105\x3106\x3107\x3108.tw", true},
- // Bopomofo + Katakana
- {"xn--lcka3d1bztghi.tw",
- L"\x3105\x3106\x3107\x3108\x30ab\x30bf\x30ab\x30ca.tw", false},
- // Bopomofo + Hangul
- {"xn--5ekcde4543qbec.tw", L"\x3105\x3106\x3107\x3108\xc8fc\xc74c.tw", false},
- // Devanagari + Latin
- {"xn--ab-3ofh8fqbj6h.in", L"ab\x0939\x093f\x0928\x094d\x0926\x0940.in", true},
- // Thai + Latin
- {"xn--ab-jsi9al4bxdb6n.th",
- L"ab\x0e20\x0e32\x0e29\x0e32\x0e44\x0e17\x0e22.th", true},
- // <vitamin in Katakana>b1.com
- {"xn--b1-xi4a7cvc9f.com", L"\x30d3\x30bf\x30df\x30f3" L"b1.com", true},
- // Devanagari + Han
- {"xn--t2bes3ds6749n.com", L"\x0930\x094b\x0932\x0947\x76e7\x0938.com", false},
- // Devanagari + Bengali
- {"xn--11b0x.in", L"\x0915\x0995.in", false},
- // Canadian Syllabary + Latin
- {"xn--ab-lym.com", L"ab\x14BF.com", false},
- {"xn--ab1-p6q.com", L"ab1\x14BF.com", false},
- {"xn--1ab-m6qd.com", L"\x14BF" L"1ab.com", false},
- {"xn--ab-jymc.com", L"\x14BF" L"ab.com", false},
- // Tifinagh + Latin
- {"xn--liy-go4a.com", L"li\u24dfy.com", false},
- {"xn--rol-ho4a.com", L"rol\u24df.com", false},
- {"xn--ily-eo4a.com", L"\u24dfily.com", false},
- {"xn--1ly-eo4a.com", L"\u24df1ly.com", false},
-
- // Invisibility check
- // Thai tone mark malek(U+0E48) repeated
- {"xn--03c0b3ca.th", L"\x0e23\x0e35\x0e48\x0e48.th", false},
- // Accute accent repeated
- {"xn--a-xbba.com", L"a\x0301\x0301.com", false},
- // 'a' with acuted accent + another acute accent
- {"xn--1ca20i.com", L"\x00e1\x0301.com", false},
- // Combining mark at the beginning
- {"xn--abc-fdc.jp", L"\x0300" L"abc.jp", false},
-
- // Mixed script confusable
- // google with Armenian Small Letter Oh(U+0585)
- {"xn--gogle-lkg.com", L"g\x0585ogle.com", false},
- {"xn--range-kkg.com", L"\x0585range.com", false},
- {"xn--cucko-pkg.com", L"cucko\x0585.com", false},
- // Latin 'o' in Armenian.
- {"xn--o-ybcg0cu0cq.com",
- L"o\x0585\x0580\x0574\x0578\x0582\x0566\x0568.com", false},
- // Hiragana HE(U+3078) mixed with Katakana
- {"xn--49jxi3as0d0fpc.com",
- L"\x30e2\x30d2\x30fc\x30c8\x3078\x30d6\x30f3.com", false},
-
- // U+30FC should be preceded by a Hiragana/Katakana.
- // Katakana + U+30FC + Han
- {"xn--lck0ip02qw5ya.jp", L"\x30ab\x30fc\x91ce\x7403.jp", true},
- // Hiragana + U+30FC + Han
- {"xn--u8j5tr47nw5ya.jp", L"\x304b\x30fc\x91ce\x7403.jp", true},
- // U+30FC + Han
- {"xn--weka801xo02a.com", L"\x30fc\x52d5\x753b\x30fc.com", false},
- // Han + U+30FC + Han
- {"xn--wekz60nb2ay85atj0b.jp", L"\x65e5\x672c\x30fc\x91ce\x7403.jp", false},
- // U+30FC at the beginning
- {"xn--wek060nb2a.jp", L"\x30fc\x65e5\x672c", false},
- // Latin + U+30FC + Latin
- {"xn--abcdef-r64e.jp", L"abc\x30fc" L"def.jp", false},
-
- // U+30FB (・) is not allowed next to Latin, but allowed otherwise.
- // U+30FB + Han
- {"xn--vekt920a.jp", L"\x30fb\x91ce.jp", true},
- // Han + U+30FB + Han
- {"xn--vek160nb2ay85atj0b.jp", L"\x65e5\x672c\x30fb\x91ce\x7403.jp", true},
- // Latin + U+30FB + Latin
- {"xn--abcdef-k64e.jp", L"abc\x30fb" L"def.jp", false},
- // U+30FB + Latin
- {"xn--abc-os4b.jp", L"\x30fb" L"abc.jp", false},
-
- // U+30FD (ヽ) is allowed only after Katakana.
- // Katakana + U+30FD
- {"xn--lck2i.jp", L"\x30ab\x30fd.jp", true},
- // Hiragana + U+30FD
- {"xn--u8j7t.jp", L"\x304b\x30fd.jp", false},
- // Han + U+30FD
- {"xn--xek368f.jp", L"\x4e00\x30fd.jp", false},
- {"xn--aa-mju.jp", L"a\x30fd.jp", false},
- {"xn--a1-bo4a.jp", L"a1\x30fd.jp", false},
-
- // U+30FE (ヾ) is allowed only after Katakana.
- // Katakana + U+30FE
- {"xn--lck4i.jp", L"\x30ab\x30fe.jp", true},
- // Hiragana + U+30FE
- {"xn--u8j9t.jp", L"\x304b\x30fe.jp", false},
- // Han + U+30FE
- {"xn--yek168f.jp", L"\x4e00\x30fe.jp", false},
- {"xn--a-oju.jp", L"a\x30fe.jp", false},
- {"xn--a1-eo4a.jp", L"a1\x30fe.jp", false},
-
- // Cyrillic labels made of Latin-look-alike Cyrillic letters.
- // ѕсоре.com with ѕсоре in Cyrillic
- {"xn--e1argc3h.com", L"\x0455\x0441\x043e\x0440\x0435.com", false},
- // ѕсоре123.com with ѕсоре in Cyrillic.
- {"xn--123-qdd8bmf3n.com",
- L"\x0455\x0441\x043e\x0440\x0435" L"123.com", false},
- // ѕсоре-рау.com with ѕсоре and рау in Cyrillic.
- {"xn----8sbn9akccw8m.com",
- L"\x0455\x0441\x043e\x0440\x0435-\x0440\x0430\x0443.com", false},
- // ѕсоре·рау.com with scope and pay in Cyrillic and U+00B7 between them.
- {"xn--uba29ona9akccw8m.com",
- L"\x0455\x0441\x043e\x0440\x0435\u00b7\x0440\x0430\x0443.com", false},
-
- // The same as above three, but in IDN TLD.
- {"xn--e1argc3h.xn--p1ai",
- L"\x0455\x0441\x043e\x0440\x0435.\x0440\x0444", true},
- {"xn--123-qdd8bmf3n.xn--p1ai",
- L"\x0455\x0441\x043e\x0440\x0435" L"123.\x0440\x0444", true},
- {"xn--uba29ona9akccw8m.xn--p1ai",
- L"\x0455\x0441\x043e\x0440\x0435\u00b7\x0440\x0430\x0443.\x0440\x0444",
- true},
-
- // ѕсоре-рау.한국 with ѕсоре and рау in Cyrillic.
- {"xn----8sbn9akccw8m.xn--3e0b707e",
- L"\x0455\x0441\x043e\x0440\x0435-\x0440\x0430\x0443.\xd55c\xad6d", true},
-
- // музей (museum in Russian) has characters without a Latin-look-alike.
- {"xn--e1adhj9a.com", L"\x043c\x0443\x0437\x0435\x0439.com", true},
-
- // Combining Diacritic marks after a script other than Latin-Greek-Cyrillic
- {"xn--rsa2568fvxya.com", L"\xd55c\x0301\xae00.com", false}, // 한́글.com
- {"xn--rsa0336bjom.com", L"\x6f22\x0307\x5b57.com", false}, // 漢̇字.com
- // नागरी́.com
- {"xn--lsa922apb7a6do.com", L"\x0928\x093e\x0917\x0930\x0940\x0301.com",
- false},
-
- // Similarity checks against the list of top domains. "digklmo68.com" and
- // 'digklmo68.co.uk" are listed for unittest in the top domain list.
- {"xn--igklmo68-nea32c.com", L"\x0111igklmo68.com", false}, // đigklmo68.com
- {"www.xn--igklmo68-nea32c.com", L"www.\x0111igklmo68.com", false},
- {"foo.bar.xn--igklmo68-nea32c.com", L"foo.bar.\x0111igklmo68.com", false},
- {"xn--igklmo68-nea32c.co.uk", L"\x0111igklmo68.co.uk", false},
- {"mail.xn--igklmo68-nea32c.co.uk", L"mail.\x0111igklmo68.co.uk", false},
- {"xn--digklmo68-6jf.com", L"di\x0307gklmo68.com", false}, // di̇gklmo68.com
- {"xn--digklmo68-7vf.com", L"dig\x0331klmo68.com", false}, // dig̱klmo68.com
- {"xn--diglmo68-omb.com", L"dig\x0138lmo68.com", false}, // digĸlmo68.com
- {"xn--digkmo68-9ob.com", L"digk\x0142mo68.com", false}, // digkłmo68.com
- {"xn--digklo68-l89c.com", L"digkl\x1e43o68.com", false}, // digklṃo68.com
- {"xn--digklm68-b5a.com", L"digklm\x00f8" L"68.com", false}, // digklmø68.com
- {"xn--digklmo8-h7g.com", L"digklmo\x0431" L"8.com", false}, // digklmoб8.com
- {"xn--digklmo6-7yr.com", L"digklmo6\x09ea.com", false}, // digklmo6৪.com
-
- // 'islkpx123.com' is listed for unitest in the top domain list.
- // 'іѕӏкрх123' can look like 'islkpx123' in some fonts.
- {"xn--123-bed4a4a6hh40i.com",
- L"\x0456\x0455\x04cf\x043a\x0440\x0445" L"123.com", false},
-
- // Mixed digits: the first two will also fail mixed script test
- // Latin + ASCII digit + Deva digit
- {"xn--asc1deva-j0q.co.in", L"asc1deva\x0967.co.in", false},
- // Latin + Deva digit + Beng digit
- {"xn--devabeng-f0qu3f.co.in", L"deva\x0967" L"beng\x09e7.co.in", false},
- // ASCII digit + Deva digit
- {"xn--79-v5f.co.in", L"7\x09ea" L"9.co.in", false},
- // Deva digit + Beng digit
- {"xn--e4b0x.co.in", L"\x0967\x09e7.co.in", false},
- // U+4E00 (CJK Ideograph One) is not a digit
- {"xn--d12-s18d.cn", L"d12\x4e00.cn", true},
- // One that's really long that will force a buffer realloc
- {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- "aaaaaaa",
- L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- L"aaaaaaaa",
- true},
-
- // Not allowed; characters outside [:Identifier_Status=Allowed:]
- // Limited Use Scripts: UTS 31 Table 7.
- // Vai
- {"xn--sn8a.com", L"\xa50b.com", false},
- // 'CARD' look-alike in Cherokee
- {"xn--58db0a9q.com", L"\x13df\x13aa\x13a1\x13a0.com", false},
- // Scripts excluded from Identifiers: UTS 31 Table 4
- // Coptic
- {"xn--5ya.com", L"\x03e7.com", false},
- // Old Italic
- {"xn--097cc.com", L"\U00010300\U00010301.com", false},
-
- // U+115F (Hangul Filler)
- {"xn--osd3820f24c.kr", L"\xac00\xb098\x115f.kr", false},
- {"www.xn--google-ho0coa.com", L"www.\x2039google\x203a.com", false},
- // Latin small capital w: hardᴡare.com
- {"xn--hardare-l41c.com", L"hard\x1d21" L"are.com", false},
- // Minus Sign(U+2212)
- {"xn--t9g238xc2a.jp", L"\x65e5\x2212\x672c.jp", false},
- // Latin Small Letter Script G: ɡɡ.com
- {"xn--0naa.com", L"\x0261\x0261.com", false},
- // Hangul Jamo(U+11xx)
- {"xn--0pdc3b.com", L"\x1102\x1103\x1110.com", false},
- // degree sign: 36°c.com
- {"xn--36c-tfa.com", L"36\x00b0" L"c.com", false},
- // Pound sign
- {"xn--5free-9ga.com", L"5free\x00a8.com", false},
- // Hebrew points (U+05B0, U+05B6)
- {"xn--7cbl2kc2a.com", L"\x05e1\x05b6\x05e7\x05b0\x05e1.com", false},
- // Danda(U+0964)
- {"xn--81bp1b6ch8s.com", L"\x0924\x093f\x091c\x0964\x0930\x0940.com", false},
- // Small letter script G(U+0261)
- {"xn--oogle-qmc.com", L"\x0261oogle.com", false},
- // Small Katakana Extension(U+31F1)
- {"xn--wlk.com", L"\x31f1.com", false},
- // Heart symbol: ♥
- {"xn--ab-u0x.com", L"ab\x2665.com", false},
- // Emoji
- {"xn--vi8hiv.xyz", L"\U0001f355\U0001f4a9.xyz", false},
- // Registered trade mark
- {"xn--egistered-fna.com", L"\x00ae" L"egistered.com", false},
- // Latin Letter Retroflex Click
- {"xn--registered-25c.com", L"registered\x01c3.com", false},
- // ASCII '!' not allowed in IDN
- {"xn--!-257eu42c.kr", L"\xc548\xb155!.kr", false},
- // 'GOOGLE' in IPA extension: ɢᴏᴏɢʟᴇ
- {"xn--1naa7pn51hcbaa.com",
- L"\x0262\x1d0f\x1d0f\x0262\x029f\x1d07.com", false},
- // Padlock icon spoof.
- {"xn--google-hj64e", L"\U0001f512google.com", false},
-
- // Custom black list
- // Combining Long Solidus Overlay
- {"google.xn--comabc-k8d", L"google.com\x0338" L"abc", false},
- // Hyphenation Point instead of Katakana Middle dot
- {"xn--svgy16dha.jp", L"\x30a1\x2027\x30a3.jp", false},
- // Gershayim with other Hebrew characters is allowed.
- {"xn--5db6bh9b.il", L"\x05e9\x05d1\x05f4\x05e6.il", true},
- // Hebrew Gershayim with Latin is disallowed.
- {"xn--ab-yod.com", L"a\x05f4" L"b.com", false},
- // Hebrew Gershayim with Arabic is disallowed.
- {"xn--5eb7h.eg", L"\x0628\x05f4.eg", false},
+ // No IDN
+ {"www.google.com", L"www.google.com", true},
+ {"www.google.com.", L"www.google.com.", true},
+ {".", L".", true},
+ {"", L"", true},
+ // IDN
+ // Hanzi (Traditional Chinese)
+ {"xn--1lq90ic7f1rc.cn", L"\x5317\x4eac\x5927\x5b78.cn", true},
+ // Hanzi ('video' in Simplified Chinese
+ {"xn--cy2a840a.com", L"\x89c6\x9891.com", true},
+ // Hanzi + '123'
+ {"www.xn--123-p18d.com",
+ L"www.\x4e00"
+ L"123.com",
+ true},
+ // Hanzi + Latin : U+56FD is simplified
+ {"www.xn--hello-9n1hm04c.com", L"www.hello\x4e2d\x56fd.com", true},
+ // Kanji + Kana (Japanese)
+ {"xn--l8jvb1ey91xtjb.jp", L"\x671d\x65e5\x3042\x3055\x3072.jp", true},
+ // Katakana including U+30FC
+ {"xn--tckm4i2e.jp", L"\x30b3\x30de\x30fc\x30b9.jp", true},
+ {"xn--3ck7a7g.jp", L"\u30ce\u30f3\u30bd.jp", true},
+ // Katakana + Latin (Japanese)
+ {"xn--e-efusa1mzf.jp", L"e\x30b3\x30de\x30fc\x30b9.jp", true},
+ {"xn--3bkxe.jp", L"\x30c8\x309a.jp", true},
+ // Hangul (Korean)
+ {"www.xn--or3b17p6jjc.kr", L"www.\xc804\xc790\xc815\xbd80.kr", true},
+ // b<u-umlaut>cher (German)
+ {"xn--bcher-kva.de",
+ L"b\x00fc"
+ L"cher.de",
+ true},
+ // a with diaeresis
+ {"www.xn--frgbolaget-q5a.se", L"www.f\x00e4rgbolaget.se", true},
+ // c-cedilla (French)
+ {"www.xn--alliancefranaise-npb.fr",
+ L"www.alliancefran\x00e7"
+ L"aise.fr",
+ true},
+ // caf'e with acute accent' (French)
+ {"xn--caf-dma.fr", L"caf\x00e9.fr", true},
+ // c-cedillla and a with tilde (Portuguese)
+ {"xn--poema-9qae5a.com.br", L"p\x00e3oema\x00e7\x00e3.com.br", true},
+ // s with caron
+ {"xn--achy-f6a.com",
+ L"\x0161"
+ L"achy.com",
+ true},
+ {"xn--kxae4bafwg.gr", L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1.gr",
+ true},
+ // Eutopia + 123 (Greek)
+ {"xn---123-pldm0haj2bk.gr",
+ L"\x03bf\x03c5\x03c4\x03bf\x03c0\x03af\x03b1-123.gr", true},
+ // Cyrillic (Russian)
+ {"xn--n1aeec9b.ru", L"\x0442\x043e\x0440\x0442\x044b.ru", true},
+ // Cyrillic + 123 (Russian)
+ {"xn---123-45dmmc5f.ru", L"\x0442\x043e\x0440\x0442\x044b-123.ru", true},
+ // 'president' in Russian. Is a wholescript confusable, but allowed.
+ {"xn--d1abbgf6aiiy.xn--p1ai",
+ L"\x043f\x0440\x0435\x0437\x0438\x0434\x0435\x043d\x0442.\x0440\x0444",
+ true},
+ // Arabic
+ {"xn--mgba1fmg.eg", L"\x0627\x0641\x0644\x0627\x0645.eg", true},
+ // Hebrew
+ {"xn--4dbib.he", L"\x05d5\x05d0\x05d4.he", true},
+ // Hebrew + Common
+ {"xn---123-ptf2c5c6bt.il", L"\x05e2\x05d1\x05e8\x05d9\x05ea-123.il", true},
+ // Thai
+ {"xn--12c2cc4ag3b4ccu.th",
+ L"\x0e2a\x0e32\x0e22\x0e01\x0e32\x0e23\x0e1a\x0e34\x0e19.th", true},
+ // Thai + Common
+ {"xn---123-9goxcp8c9db2r.th",
+ L"\x0e20\x0e32\x0e29\x0e32\x0e44\x0e17\x0e22-123.th", true},
+ // Devangari (Hindi)
+ {"www.xn--l1b6a9e1b7c.in", L"www.\x0905\x0915\x094b\x0932\x093e.in", true},
+ // Devanagari + Common
+ {"xn---123-kbjl2j0bl2k.in", L"\x0939\x093f\x0928\x094d\x0926\x0940-123.in",
+ true},
+
+ // 5 Aspirational scripts
+ // Unifieid Canadian Syllabary
+ {"xn--dfe0tte.ca", L"\x1456\x14c2\x14ef.ca", true},
+ // Tifinagh
+ {"xn--4ljxa2bb4a6bxb.ma", L"\x2d5c\x2d49\x2d3c\x2d49\x2d4f\x2d30\x2d56.ma",
+ true},
+ // Tifinagh with a disallowed character(U+2D6F)
+ {"xn--hmjzaby5d5f.ma", L"\x2d5c\x2d49\x2d3c\x2d6f\x2d49\x2d4f.ma", false},
+ // Yi
+ {"xn--4o7a6e1x64c.cn", L"\xa188\xa320\xa071\xa0b7.cn", true},
+ // Mongolian - 'ordu' (place, camp)
+ {"xn--56ec8bp.cn", L"\x1823\x1837\x1833\x1824.cn", true},
+ // Mongolian with a disallowed character
+ {"xn--95e5de3ds.cn", L"\x1823\x1837\x1804\x1833\x1824.cn", false},
+ // Miao/Pollad
+ {"xn--2u0fpf0a.cn", L"\U00016f04\U00016f62\U00016f59.cn", true},
+
+ // Script mixing tests
+ // The following script combinations are allowed.
+ // MODERATELY_RESTRICTIVE with Latin limited to ASCII-Latin.
+ // ASCII-Latin + Japn (Kana + Han)
+ // ASCII-Latin + Kore (Hangul + Han)
+ // ASCII-Latin + Han + Bopomofo
+ // ASCII-Latin + any allowed script other than Cyrillic, Greek, Cherokee
+ // and Unified Canadian Syllabary
+ // "payp<alpha>l.com"
+ {"www.xn--paypl-g9d.com", L"payp\x03b1l.com", false},
+ // google.gr with Greek omicron and epsilon
+ {"xn--ggl-6xc1ca.gr", L"g\x03bf\x03bfgl\x03b5.gr", false},
+ // google.ru with Cyrillic o
+ {"xn--ggl-tdd6ba.ru", L"g\x043e\x043egl\x0435.ru", false},
+ // h<e with acute>llo<China in Han>.cn
+ {"xn--hllo-bpa7979ih5m.cn", L"h\x00e9llo\x4e2d\x56fd.cn", false},
+ // <Greek rho><Cyrillic a><Cyrillic u>.ru
+ {"xn--2xa6t2b.ru", L"\x03c1\x0430\x0443.ru", false},
+ // Hangul + Latin
+ {"xn--han-eb9ll88m.kr", L"\xd55c\xae00han.kr", true},
+ // Hangul + Latin + Han with IDN ccTLD
+ {"xn--han-or0kq92gkm3c.xn--3e0b707e", L"\xd55c\xae00han\x97d3.\xd55c\xad6d",
+ true},
+ // non-ASCII Latin + Hangul
+ {"xn--caf-dma9024xvpg.kr", L"caf\x00e9\xce74\xd398.kr", false},
+ // Hangul + Hiragana
+ {"xn--y9j3b9855e.kr", L"\xd55c\x3072\x3089.kr", false},
+ // <Hiragana>.<Hangul> is allowed because script mixing check is per label.
+ {"xn--y9j3b.xn--3e0b707e", L"\x3072\x3089.\xd55c\xad6d", true},
+ // Traditional Han + Latin
+ {"xn--hanzi-u57ii69i.tw", L"\x6f22\x5b57hanzi.tw", true},
+ // Simplified Han + Latin
+ {"xn--hanzi-u57i952h.cn", L"\x6c49\x5b57hanzi.cn", true},
+ // Simplified Han + Traditonal Han
+ {"xn--hanzi-if9kt8n.cn", L"\x6c49\x6f22hanzi.cn", true},
+ // Han + Hiragana + Katakana + Latin
+ {"xn--kanji-ii4dpizfq59yuykqr4b.jp",
+ L"\x632f\x308a\x4eee\x540d\x30ab\x30bfkanji.jp", true},
+ // Han + Bopomofo
+ {"xn--5ekcde0577e87tc.tw", L"\x6ce8\x97f3\x3105\x3106\x3107\x3108.tw",
+ true},
+ // Han + Latin + Bopomofo
+ {"xn--bopo-ty4cghi8509kk7xd.tw",
+ L"\x6ce8\x97f3"
+ L"bopo\x3105\x3106\x3107\x3108.tw",
+ true},
+ // Latin + Bopomofo
+ {"xn--bopomofo-hj5gkalm.tw", L"bopomofo\x3105\x3106\x3107\x3108.tw", true},
+ // Bopomofo + Katakana
+ {"xn--lcka3d1bztghi.tw",
+ L"\x3105\x3106\x3107\x3108\x30ab\x30bf\x30ab\x30ca.tw", false},
+ // Bopomofo + Hangul
+ {"xn--5ekcde4543qbec.tw", L"\x3105\x3106\x3107\x3108\xc8fc\xc74c.tw",
+ false},
+ // Devanagari + Latin
+ {"xn--ab-3ofh8fqbj6h.in", L"ab\x0939\x093f\x0928\x094d\x0926\x0940.in",
+ true},
+ // Thai + Latin
+ {"xn--ab-jsi9al4bxdb6n.th",
+ L"ab\x0e20\x0e32\x0e29\x0e32\x0e44\x0e17\x0e22.th", true},
+ // <vitamin in Katakana>b1.com
+ {"xn--b1-xi4a7cvc9f.com",
+ L"\x30d3\x30bf\x30df\x30f3"
+ L"b1.com",
+ true},
+ // Devanagari + Han
+ {"xn--t2bes3ds6749n.com", L"\x0930\x094b\x0932\x0947\x76e7\x0938.com",
+ false},
+ // Devanagari + Bengali
+ {"xn--11b0x.in", L"\x0915\x0995.in", false},
+ // Canadian Syllabary + Latin
+ {"xn--ab-lym.com", L"ab\x14BF.com", false},
+ {"xn--ab1-p6q.com", L"ab1\x14BF.com", false},
+ {"xn--1ab-m6qd.com",
+ L"\x14BF"
+ L"1ab.com",
+ false},
+ {"xn--ab-jymc.com",
+ L"\x14BF"
+ L"ab.com",
+ false},
+ // Tifinagh + Latin
+ {"xn--liy-go4a.com", L"li\u24dfy.com", false},
+ {"xn--rol-ho4a.com", L"rol\u24df.com", false},
+ {"xn--ily-eo4a.com", L"\u24dfily.com", false},
+ {"xn--1ly-eo4a.com", L"\u24df1ly.com", false},
+
+ // Invisibility check
+ // Thai tone mark malek(U+0E48) repeated
+ {"xn--03c0b3ca.th", L"\x0e23\x0e35\x0e48\x0e48.th", false},
+ // Accute accent repeated
+ {"xn--a-xbba.com", L"a\x0301\x0301.com", false},
+ // 'a' with acuted accent + another acute accent
+ {"xn--1ca20i.com", L"\x00e1\x0301.com", false},
+ // Combining mark at the beginning
+ {"xn--abc-fdc.jp",
+ L"\x0300"
+ L"abc.jp",
+ false},
+
+ // Mixed script confusable
+ // google with Armenian Small Letter Oh(U+0585)
+ {"xn--gogle-lkg.com", L"g\x0585ogle.com", false},
+ {"xn--range-kkg.com", L"\x0585range.com", false},
+ {"xn--cucko-pkg.com", L"cucko\x0585.com", false},
+ // Latin 'o' in Armenian.
+ {"xn--o-ybcg0cu0cq.com", L"o\x0585\x0580\x0574\x0578\x0582\x0566\x0568.com",
+ false},
+ // Hiragana HE(U+3078) mixed with Katakana
+ {"xn--49jxi3as0d0fpc.com",
+ L"\x30e2\x30d2\x30fc\x30c8\x3078\x30d6\x30f3.com", false},
+
+ // U+30FC should be preceded by a Hiragana/Katakana.
+ // Katakana + U+30FC + Han
+ {"xn--lck0ip02qw5ya.jp", L"\x30ab\x30fc\x91ce\x7403.jp", true},
+ // Hiragana + U+30FC + Han
+ {"xn--u8j5tr47nw5ya.jp", L"\x304b\x30fc\x91ce\x7403.jp", true},
+ // U+30FC + Han
+ {"xn--weka801xo02a.com", L"\x30fc\x52d5\x753b\x30fc.com", false},
+ // Han + U+30FC + Han
+ {"xn--wekz60nb2ay85atj0b.jp", L"\x65e5\x672c\x30fc\x91ce\x7403.jp", false},
+ // U+30FC at the beginning
+ {"xn--wek060nb2a.jp", L"\x30fc\x65e5\x672c", false},
+ // Latin + U+30FC + Latin
+ {"xn--abcdef-r64e.jp",
+ L"abc\x30fc"
+ L"def.jp",
+ false},
+
+ // U+30FB (・) is not allowed next to Latin, but allowed otherwise.
+ // U+30FB + Han
+ {"xn--vekt920a.jp", L"\x30fb\x91ce.jp", true},
+ // Han + U+30FB + Han
+ {"xn--vek160nb2ay85atj0b.jp", L"\x65e5\x672c\x30fb\x91ce\x7403.jp", true},
+ // Latin + U+30FB + Latin
+ {"xn--abcdef-k64e.jp",
+ L"abc\x30fb"
+ L"def.jp",
+ false},
+ // U+30FB + Latin
+ {"xn--abc-os4b.jp",
+ L"\x30fb"
+ L"abc.jp",
+ false},
+
+ // U+30FD (ヽ) is allowed only after Katakana.
+ // Katakana + U+30FD
+ {"xn--lck2i.jp", L"\x30ab\x30fd.jp", true},
+ // Hiragana + U+30FD
+ {"xn--u8j7t.jp", L"\x304b\x30fd.jp", false},
+ // Han + U+30FD
+ {"xn--xek368f.jp", L"\x4e00\x30fd.jp", false},
+ {"xn--aa-mju.jp", L"a\x30fd.jp", false},
+ {"xn--a1-bo4a.jp", L"a1\x30fd.jp", false},
+
+ // U+30FE (ヾ) is allowed only after Katakana.
+ // Katakana + U+30FE
+ {"xn--lck4i.jp", L"\x30ab\x30fe.jp", true},
+ // Hiragana + U+30FE
+ {"xn--u8j9t.jp", L"\x304b\x30fe.jp", false},
+ // Han + U+30FE
+ {"xn--yek168f.jp", L"\x4e00\x30fe.jp", false},
+ {"xn--a-oju.jp", L"a\x30fe.jp", false},
+ {"xn--a1-eo4a.jp", L"a1\x30fe.jp", false},
+
+ // Cyrillic labels made of Latin-look-alike Cyrillic letters.
+ // ѕсоре.com with ѕсоре in Cyrillic
+ {"xn--e1argc3h.com", L"\x0455\x0441\x043e\x0440\x0435.com", false},
+ // ѕсоре123.com with ѕсоре in Cyrillic.
+ {"xn--123-qdd8bmf3n.com",
+ L"\x0455\x0441\x043e\x0440\x0435"
+ L"123.com",
+ false},
+ // ѕсоре-рау.com with ѕсоре and рау in Cyrillic.
+ {"xn----8sbn9akccw8m.com",
+ L"\x0455\x0441\x043e\x0440\x0435-\x0440\x0430\x0443.com", false},
+ // ѕсоре·рау.com with scope and pay in Cyrillic and U+00B7 between them.
+ {"xn--uba29ona9akccw8m.com",
+ L"\x0455\x0441\x043e\x0440\x0435\u00b7\x0440\x0430\x0443.com", false},
+
+ // The same as above three, but in IDN TLD.
+ {"xn--e1argc3h.xn--p1ai", L"\x0455\x0441\x043e\x0440\x0435.\x0440\x0444",
+ true},
+ {"xn--123-qdd8bmf3n.xn--p1ai",
+ L"\x0455\x0441\x043e\x0440\x0435"
+ L"123.\x0440\x0444",
+ true},
+ {"xn--uba29ona9akccw8m.xn--p1ai",
+ L"\x0455\x0441\x043e\x0440\x0435\u00b7\x0440\x0430\x0443.\x0440\x0444",
+ true},
+
+ // ѕсоре-рау.한국 with ѕсоре and рау in Cyrillic.
+ {"xn----8sbn9akccw8m.xn--3e0b707e",
+ L"\x0455\x0441\x043e\x0440\x0435-\x0440\x0430\x0443.\xd55c\xad6d", true},
+
+ // музей (museum in Russian) has characters without a Latin-look-alike.
+ {"xn--e1adhj9a.com", L"\x043c\x0443\x0437\x0435\x0439.com", true},
+
+ // Combining Diacritic marks after a script other than Latin-Greek-Cyrillic
+ {"xn--rsa2568fvxya.com", L"\xd55c\x0301\xae00.com", false}, // 한́글.com
+ {"xn--rsa0336bjom.com", L"\x6f22\x0307\x5b57.com", false}, // 漢̇字.com
+ // नागरी́.com
+ {"xn--lsa922apb7a6do.com", L"\x0928\x093e\x0917\x0930\x0940\x0301.com",
+ false},
+
+ // Similarity checks against the list of top domains. "digklmo68.com" and
+ // 'digklmo68.co.uk" are listed for unittest in the top domain list.
+ {"xn--igklmo68-nea32c.com", L"\x0111igklmo68.com", false}, // đigklmo68.com
+ {"www.xn--igklmo68-nea32c.com", L"www.\x0111igklmo68.com", false},
+ {"foo.bar.xn--igklmo68-nea32c.com", L"foo.bar.\x0111igklmo68.com", false},
+ {"xn--igklmo68-nea32c.co.uk", L"\x0111igklmo68.co.uk", false},
+ {"mail.xn--igklmo68-nea32c.co.uk", L"mail.\x0111igklmo68.co.uk", false},
+ {"xn--digklmo68-6jf.com", L"di\x0307gklmo68.com", false}, // di̇gklmo68.com
+ {"xn--digklmo68-7vf.com", L"dig\x0331klmo68.com", false}, // dig̱klmo68.com
+ {"xn--diglmo68-omb.com", L"dig\x0138lmo68.com", false}, // digĸlmo68.com
+ {"xn--digkmo68-9ob.com", L"digk\x0142mo68.com", false}, // digkłmo68.com
+ {"xn--digklo68-l89c.com", L"digkl\x1e43o68.com", false}, // digklṃo68.com
+ {"xn--digklm68-b5a.com",
+ L"digklm\x00f8"
+ L"68.com",
+ false}, // digklmø68.com
+ {"xn--digklmo8-h7g.com",
+ L"digklmo\x0431"
+ L"8.com",
+ false}, // digklmoб8.com
+ {"xn--digklmo6-7yr.com", L"digklmo6\x09ea.com", false}, // digklmo6৪.com
+
+ // 'islkpx123.com' is listed for unitest in the top domain list.
+ // 'іѕӏкрх123' can look like 'islkpx123' in some fonts.
+ {"xn--123-bed4a4a6hh40i.com",
+ L"\x0456\x0455\x04cf\x043a\x0440\x0445"
+ L"123.com",
+ false},
+
+ // Mixed digits: the first two will also fail mixed script test
+ // Latin + ASCII digit + Deva digit
+ {"xn--asc1deva-j0q.co.in", L"asc1deva\x0967.co.in", false},
+ // Latin + Deva digit + Beng digit
+ {"xn--devabeng-f0qu3f.co.in",
+ L"deva\x0967"
+ L"beng\x09e7.co.in",
+ false},
+ // ASCII digit + Deva digit
+ {"xn--79-v5f.co.in",
+ L"7\x09ea"
+ L"9.co.in",
+ false},
+ // Deva digit + Beng digit
+ {"xn--e4b0x.co.in", L"\x0967\x09e7.co.in", false},
+ // U+4E00 (CJK Ideograph One) is not a digit
+ {"xn--d12-s18d.cn", L"d12\x4e00.cn", true},
+ // One that's really long that will force a buffer realloc
+ {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaa",
+ L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ L"aaaaaaaa",
+ true},
+
+ // Not allowed; characters outside [:Identifier_Status=Allowed:]
+ // Limited Use Scripts: UTS 31 Table 7.
+ // Vai
+ {"xn--sn8a.com", L"\xa50b.com", false},
+ // 'CARD' look-alike in Cherokee
+ {"xn--58db0a9q.com", L"\x13df\x13aa\x13a1\x13a0.com", false},
+ // Scripts excluded from Identifiers: UTS 31 Table 4
+ // Coptic
+ {"xn--5ya.com", L"\x03e7.com", false},
+ // Old Italic
+ {"xn--097cc.com", L"\U00010300\U00010301.com", false},
+
+ // U+115F (Hangul Filler)
+ {"xn--osd3820f24c.kr", L"\xac00\xb098\x115f.kr", false},
+ {"www.xn--google-ho0coa.com", L"www.\x2039google\x203a.com", false},
+ // Latin small capital w: hardᴡare.com
+ {"xn--hardare-l41c.com",
+ L"hard\x1d21"
+ L"are.com",
+ false},
+ // Minus Sign(U+2212)
+ {"xn--t9g238xc2a.jp", L"\x65e5\x2212\x672c.jp", false},
+ // Latin Small Letter Script G: ɡɡ.com
+ {"xn--0naa.com", L"\x0261\x0261.com", false},
+ // Hangul Jamo(U+11xx)
+ {"xn--0pdc3b.com", L"\x1102\x1103\x1110.com", false},
+ // degree sign: 36°c.com
+ {"xn--36c-tfa.com",
+ L"36\x00b0"
+ L"c.com",
+ false},
+ // Pound sign
+ {"xn--5free-9ga.com", L"5free\x00a8.com", false},
+ // Hebrew points (U+05B0, U+05B6)
+ {"xn--7cbl2kc2a.com", L"\x05e1\x05b6\x05e7\x05b0\x05e1.com", false},
+ // Danda(U+0964)
+ {"xn--81bp1b6ch8s.com", L"\x0924\x093f\x091c\x0964\x0930\x0940.com", false},
+ // Small letter script G(U+0261)
+ {"xn--oogle-qmc.com", L"\x0261oogle.com", false},
+ // Small Katakana Extension(U+31F1)
+ {"xn--wlk.com", L"\x31f1.com", false},
+ // Heart symbol: ♥
+ {"xn--ab-u0x.com", L"ab\x2665.com", false},
+ // Emoji
+ {"xn--vi8hiv.xyz", L"\U0001f355\U0001f4a9.xyz", false},
+ // Registered trade mark
+ {"xn--egistered-fna.com",
+ L"\x00ae"
+ L"egistered.com",
+ false},
+ // Latin Letter Retroflex Click
+ {"xn--registered-25c.com", L"registered\x01c3.com", false},
+ // ASCII '!' not allowed in IDN
+ {"xn--!-257eu42c.kr", L"\xc548\xb155!.kr", false},
+ // 'GOOGLE' in IPA extension: ɢᴏᴏɢʟᴇ
+ {"xn--1naa7pn51hcbaa.com", L"\x0262\x1d0f\x1d0f\x0262\x029f\x1d07.com",
+ false},
+ // Padlock icon spoof.
+ {"xn--google-hj64e", L"\U0001f512google.com", false},
+
+ // Custom black list
+ // Combining Long Solidus Overlay
+ {"google.xn--comabc-k8d",
+ L"google.com\x0338"
+ L"abc",
+ false},
+ // Hyphenation Point instead of Katakana Middle dot
+ {"xn--svgy16dha.jp", L"\x30a1\x2027\x30a3.jp", false},
+ // Gershayim with other Hebrew characters is allowed.
+ {"xn--5db6bh9b.il", L"\x05e9\x05d1\x05f4\x05e6.il", true},
+ // Hebrew Gershayim with Latin is disallowed.
+ {"xn--ab-yod.com",
+ L"a\x05f4"
+ L"b.com",
+ false},
+ // Hebrew Gershayim with Arabic is disallowed.
+ {"xn--5eb7h.eg", L"\x0628\x05f4.eg", false},
#if defined(OS_MACOSX)
- // These characters are blocked due to a font issue on Mac.
- // Tibetan transliteration characters.
- {"xn--com-luma.test.pl", L"\u0f8c.test.pl", false},
- // Arabic letter KASHMIRI YEH
- {"xn--fgb.com", L"\u0620.com", false},
+ // These characters are blocked due to a font issue on Mac.
+ // Tibetan transliteration characters.
+ {"xn--com-luma.test.pl", L"\u0f8c.test.pl", false},
+ // Arabic letter KASHMIRI YEH
+ {"xn--fgb.com", L"\u0620.com", false},
#endif
- // Hyphens (http://unicode.org/cldr/utility/confusables.jsp?a=-)
- // Hyphen-Minus (the only hyphen allowed)
- // abc-def
- {"abc-def.com", L"abc-def.com", true},
- // Modifier Letter Minus Sign
- {"xn--abcdef-5od.com", L"abc\x02d7" L"def.com", false},
- // Hyphen
- {"xn--abcdef-dg0c.com", L"abc\x2010" L"def.com", false},
- // Non-Breaking Hyphen
- // This is actually an invalid IDNA domain (U+2011 normalizes to U+2010), but
- // it is included to ensure that we do not inadvertently allow this character
- // to be displayed as Unicode.
- {"xn--abcdef-kg0c.com", L"abc\x2011" L"def.com", false},
- // Figure Dash
- {"xn--abcdef-rg0c.com", L"abc\x2012" L"def.com", false},
- // En Dash
- {"xn--abcdef-yg0c.com", L"abc\x2013" L"def.com", false},
- // Hyphen Bullet
- {"xn--abcdef-kq0c.com", L"abc\x2043" L"def.com", false},
- // Minus Sign
- {"xn--abcdef-5d3c.com", L"abc\x2212" L"def.com", false},
- // Heavy Minus Sign
- {"xn--abcdef-kg1d.com", L"abc\x2796" L"def.com", false},
- // Coptic Capital Letter Dialect-P Ni
- {"xn--abcdef-yy8d.com", L"abc\x2cba" L"def.com", false},
- // Small Em Dash
- {"xn--abcdef-5g0c.com", L"abc\xfe58" L"def.com", false},
-
- // Custom dangerous patterns
- // Two Katakana-Hiragana combining mark in a row
- {"google.xn--com-oh4ba.evil.jp", L"google.com\x309a\x309a.evil.jp", false},
- // Katakana Letter No not enclosed by {Han,Hiragana,Katakana}.
- {"google.xn--comevil-v04f.jp", L"google.com\x30ce" L"evil.jp", false},
- // TODO(jshin): Review the danger of allowing the following two.
- // Hiragana 'No' by itself is allowed.
- {"xn--ldk.jp", L"\x30ce.jp", true},
- // Hebrew Gershayim used by itself is allowed.
- {"xn--5eb.il", L"\x05f4.il", true},
-
- // Block RTL nonspacing marks (NSM) after unrelated scripts.
- {"xn--foog-ycg.com", L"foog\x0650.com", false}, // Latin + Arabic NSM
- {"xn--foog-jdg.com", L"foog\x0654.com", false}, // Latin + Arabic NSM
- {"xn--foog-jhg.com", L"foog\x0670.com", false}, // Latin + Arbic NSM
- {"xn--foog-opf.com", L"foog\x05b4.com", false}, // Latin + Hebrew NSM
- {"xn--shb5495f.com", L"\xac00\x0650.com", false}, // Hang + Arabic NSM
-
- // 4 Deviation characters between IDNA 2003 and IDNA 2008
- // When entered in Unicode, the first two are mapped to 'ss' and Greek sigma
- // and the latter two are mapped away. However, the punycode form should
- // remain in punycode.
- // U+00DF(sharp-s)
- {"xn--fu-hia.de", L"fu\x00df.de", false},
- // U+03C2(final-sigma)
- {"xn--mxac2c.gr", L"\x03b1\x03b2\x03c2.gr", false},
- // U+200C(ZWNJ)
- {"xn--h2by8byc123p.in", L"\x0924\x094d\x200c\x0930\x093f.in", false},
- // U+200C(ZWJ)
- {"xn--11b6iy14e.in", L"\x0915\x094d\x200d.in", false},
-
- // Math Monospace Small A. When entered in Unicode, it's canonicalized to
- // 'a'. The punycode form should remain in punycode.
- {"xn--bc-9x80a.xyz", L"\U0001d68a" L"bc.xyz", false},
- // Math Sans Bold Capital Alpha
- {"xn--bc-rg90a.xyz", L"\U0001d756" L"bc.xyz", false},
- // U+3000 is canonicalized to a space(U+0020), but the punycode form
- // should remain in punycode.
- {"xn--p6j412gn7f.cn", L"\x4e2d\x56fd\x3000", false},
- // U+3002 is canonicalized to ASCII fullstop(U+002E), but the punycode form
- // should remain in punycode.
- {"xn--r6j012gn7f.cn", L"\x4e2d\x56fd\x3002", false},
- // Invalid punycode
- // Has a codepoint beyond U+10FFFF.
- {"xn--krank-kg706554a", nullptr, false},
- // '?' in punycode.
- {"xn--hello?world.com", nullptr, false},
-
- // Not allowed in UTS46/IDNA 2008
- // Georgian Capital Letter(U+10BD)
- {"xn--1nd.com", L"\x10bd.com", false},
- // 3rd and 4th characters are '-'.
- {"xn-----8kci4dhsd", L"\x0440\x0443--\x0430\x0432\x0442\x043e", false},
- // Leading combining mark
- {"xn--72b.com", L"\x093e.com", false},
- // BiDi check per IDNA 2008/UTS 46
- // Cannot starts with AN(Arabic-Indic Number)
- {"xn--8hbae.eg", L"\x0662\x0660\x0660.eg", false},
- // Cannot start with a RTL character and ends with a LTR
- {"xn--x-ymcov.eg", L"\x062c\x0627\x0631x.eg", false},
- // Can start with a RTL character and ends with EN(European Number)
- {"xn--2-ymcov.eg", L"\x062c\x0627\x0631" L"2.eg", true},
- // Can start with a RTL and end with AN
- {"xn--mgbjq0r.eg", L"\x062c\x0627\x0631\x0662.eg", true},
+ // Hyphens (http://unicode.org/cldr/utility/confusables.jsp?a=-)
+ // Hyphen-Minus (the only hyphen allowed)
+ // abc-def
+ {"abc-def.com", L"abc-def.com", true},
+ // Modifier Letter Minus Sign
+ {"xn--abcdef-5od.com",
+ L"abc\x02d7"
+ L"def.com",
+ false},
+ // Hyphen
+ {"xn--abcdef-dg0c.com",
+ L"abc\x2010"
+ L"def.com",
+ false},
+ // Non-Breaking Hyphen
+ // This is actually an invalid IDNA domain (U+2011 normalizes to U+2010),
+ // but
+ // it is included to ensure that we do not inadvertently allow this
+ // character
+ // to be displayed as Unicode.
+ {"xn--abcdef-kg0c.com",
+ L"abc\x2011"
+ L"def.com",
+ false},
+ // Figure Dash
+ {"xn--abcdef-rg0c.com",
+ L"abc\x2012"
+ L"def.com",
+ false},
+ // En Dash
+ {"xn--abcdef-yg0c.com",
+ L"abc\x2013"
+ L"def.com",
+ false},
+ // Hyphen Bullet
+ {"xn--abcdef-kq0c.com",
+ L"abc\x2043"
+ L"def.com",
+ false},
+ // Minus Sign
+ {"xn--abcdef-5d3c.com",
+ L"abc\x2212"
+ L"def.com",
+ false},
+ // Heavy Minus Sign
+ {"xn--abcdef-kg1d.com",
+ L"abc\x2796"
+ L"def.com",
+ false},
+ // Coptic Capital Letter Dialect-P Ni
+ {"xn--abcdef-yy8d.com",
+ L"abc\x2cba"
+ L"def.com",
+ false},
+ // Small Em Dash
+ {"xn--abcdef-5g0c.com",
+ L"abc\xfe58"
+ L"def.com",
+ false},
+
+ // Block NV8 (Not valid in IDN 2008) characters.
+ // U+058A (֊)
+ {"xn--ab-vfd.com",
+ L"a\x058a"
+ L"b.com",
+ false},
+ {"xn--y9ac3j.com", L"\x0561\x058a\x0562.com", false},
+ // U+2019 (’)
+ {"xn--ab-n2t.com",
+ L"a\x2019"
+ L"b.com",
+ false},
+ // U+2027 (‧)
+ {"xn--ab-u3t.com",
+ L"a\x2027"
+ L"b.com",
+ false},
+ // U+30A0 (゠)
+ {"xn--ab-bg4a.com",
+ L"a\x30a0"
+ L"b.com",
+ false},
+ {"xn--9bk3828aea.com", L"\xac00\x30a0\xac01.com", false},
+ {"xn--9bk279fba.com", L"\x4e00\x30a0\x4e00.com", false},
+ {"xn--n8jl2x.com", L"\x304a\x30a0\x3044.com", false},
+ {"xn--fbke7f.com", L"\x3082\x30a0\x3084.com", false},
+
+ // Block single/double-quote-like characters.
+ // U+02BB (ʻ)
+ {"xn--ab-8nb.com",
+ L"a\x02bb"
+ L"b.com",
+ false},
+ // U+02BC (ʼ)
+ {"xn--ab-cob.com",
+ L"a\x02bc"
+ L"b.com",
+ false},
+ // U+144A: Not allowed to mix with scripts other than Canadian Syllabics.
+ {"xn--ab-jom.com",
+ L"a\x144a"
+ L"b.com",
+ false},
+ {"xn--xcec9s.com", L"\x1401\x144a\x1402.com", true},
+
+ // Custom dangerous patterns
+ // Two Katakana-Hiragana combining mark in a row
+ {"google.xn--com-oh4ba.evil.jp", L"google.com\x309a\x309a.evil.jp", false},
+ // Katakana Letter No not enclosed by {Han,Hiragana,Katakana}.
+ {"google.xn--comevil-v04f.jp",
+ L"google.com\x30ce"
+ L"evil.jp",
+ false},
+ // TODO(jshin): Review the danger of allowing the following two.
+ // Hiragana 'No' by itself is allowed.
+ {"xn--ldk.jp", L"\x30ce.jp", true},
+ // Hebrew Gershayim used by itself is allowed.
+ {"xn--5eb.il", L"\x05f4.il", true},
+
+ // Block RTL nonspacing marks (NSM) after unrelated scripts.
+ {"xn--foog-ycg.com", L"foog\x0650.com", false}, // Latin + Arabic NSM
+ {"xn--foog-jdg.com", L"foog\x0654.com", false}, // Latin + Arabic NSM
+ {"xn--foog-jhg.com", L"foog\x0670.com", false}, // Latin + Arbic NSM
+ {"xn--foog-opf.com", L"foog\x05b4.com", false}, // Latin + Hebrew NSM
+ {"xn--shb5495f.com", L"\xac00\x0650.com", false}, // Hang + Arabic NSM
+
+ // 4 Deviation characters between IDNA 2003 and IDNA 2008
+ // When entered in Unicode, the first two are mapped to 'ss' and Greek sigma
+ // and the latter two are mapped away. However, the punycode form should
+ // remain in punycode.
+ // U+00DF(sharp-s)
+ {"xn--fu-hia.de", L"fu\x00df.de", false},
+ // U+03C2(final-sigma)
+ {"xn--mxac2c.gr", L"\x03b1\x03b2\x03c2.gr", false},
+ // U+200C(ZWNJ)
+ {"xn--h2by8byc123p.in", L"\x0924\x094d\x200c\x0930\x093f.in", false},
+ // U+200C(ZWJ)
+ {"xn--11b6iy14e.in", L"\x0915\x094d\x200d.in", false},
+
+ // Math Monospace Small A. When entered in Unicode, it's canonicalized to
+ // 'a'. The punycode form should remain in punycode.
+ {"xn--bc-9x80a.xyz",
+ L"\U0001d68a"
+ L"bc.xyz",
+ false},
+ // Math Sans Bold Capital Alpha
+ {"xn--bc-rg90a.xyz",
+ L"\U0001d756"
+ L"bc.xyz",
+ false},
+ // U+3000 is canonicalized to a space(U+0020), but the punycode form
+ // should remain in punycode.
+ {"xn--p6j412gn7f.cn", L"\x4e2d\x56fd\x3000", false},
+ // U+3002 is canonicalized to ASCII fullstop(U+002E), but the punycode form
+ // should remain in punycode.
+ {"xn--r6j012gn7f.cn", L"\x4e2d\x56fd\x3002", false},
+ // Invalid punycode
+ // Has a codepoint beyond U+10FFFF.
+ {"xn--krank-kg706554a", nullptr, false},
+ // '?' in punycode.
+ {"xn--hello?world.com", nullptr, false},
+
+ // Not allowed in UTS46/IDNA 2008
+ // Georgian Capital Letter(U+10BD)
+ {"xn--1nd.com", L"\x10bd.com", false},
+ // 3rd and 4th characters are '-'.
+ {"xn-----8kci4dhsd", L"\x0440\x0443--\x0430\x0432\x0442\x043e", false},
+ // Leading combining mark
+ {"xn--72b.com", L"\x093e.com", false},
+ // BiDi check per IDNA 2008/UTS 46
+ // Cannot starts with AN(Arabic-Indic Number)
+ {"xn--8hbae.eg", L"\x0662\x0660\x0660.eg", false},
+ // Cannot start with a RTL character and ends with a LTR
+ {"xn--x-ymcov.eg", L"\x062c\x0627\x0631x.eg", false},
+ // Can start with a RTL character and ends with EN(European Number)
+ {"xn--2-ymcov.eg",
+ L"\x062c\x0627\x0631"
+ L"2.eg",
+ true},
+ // Can start with a RTL and end with AN
+ {"xn--mgbjq0r.eg", L"\x062c\x0627\x0631\x0662.eg", true},
};
struct AdjustOffsetCase {
@@ -576,10 +736,9 @@ TEST(UrlFormatterTest, IDNToUnicode) {
TEST(UrlFormatterTest, FormatUrl) {
FormatUrlTypes default_format_type = kFormatUrlOmitUsernamePassword;
const UrlTestData tests[] = {
- {"Empty URL", "", default_format_type, net::UnescapeRule::NORMAL, L"",
- 0},
+ {"Empty URL", "", default_format_type, net::UnescapeRule::NORMAL, L"", 0},
- {"Simple URL", "http://www.google.com/", default_format_type,
+ {"Simple URL", "http://www.google.com/", default_format_type,
net::UnescapeRule::NORMAL, L"http://www.google.com/", 7},
{"With a port number and a reference",
@@ -633,7 +792,8 @@ TEST(UrlFormatterTest, FormatUrl) {
"?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB",
default_format_type, net::UnescapeRule::NONE,
// GURL parses %-encoded hostnames into Punycode.
- L"http://\x30B0\x30FC\x30B0\x30EB.jp/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
+ L"http://\x30B0\x30FC\x30B0\x30EB.jp/"
+ L"%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
L"?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB",
7},
@@ -647,14 +807,14 @@ TEST(UrlFormatterTest, FormatUrl) {
7},
{"Unescape normally with BiDi control character",
- "http://example.com/%E2%80%AEabc?q=%E2%80%8Fxy",
- default_format_type, net::UnescapeRule::NORMAL,
+ "http://example.com/%E2%80%AEabc?q=%E2%80%8Fxy", default_format_type,
+ net::UnescapeRule::NORMAL,
L"http://example.com/%E2%80%AEabc?q=%E2%80%8Fxy", 7},
{"Unescape normally including unescape spaces",
- "http://www.google.com/search?q=Hello%20World",
- default_format_type, net::UnescapeRule::SPACES,
- L"http://www.google.com/search?q=Hello World", 7},
+ "http://www.google.com/search?q=Hello%20World", default_format_type,
+ net::UnescapeRule::SPACES, L"http://www.google.com/search?q=Hello World",
+ 7},
/*
{"unescape=true with some special characters",
@@ -665,15 +825,17 @@ TEST(UrlFormatterTest, FormatUrl) {
// Disabled: the resultant URL becomes "...user%253A:%2540passwd...".
// -------- omit http: --------
- {"omit http with user name", "http://user@example.com/foo",
- kFormatUrlOmitAll, net::UnescapeRule::NORMAL, L"example.com/foo", 0},
-
{"omit http", "http://www.google.com/", kFormatUrlOmitHTTP,
net::UnescapeRule::NORMAL, L"www.google.com/", 0},
- {"omit http with https", "https://www.google.com/",
- kFormatUrlOmitHTTP, net::UnescapeRule::NORMAL,
- L"https://www.google.com/", 8},
+ {"omit http on bare scheme", "http://", kFormatUrlOmitAll,
+ net::UnescapeRule::NORMAL, L"", 0},
+
+ {"omit http with user name", "http://user@example.com/foo",
+ kFormatUrlOmitAll, net::UnescapeRule::NORMAL, L"example.com/foo", 0},
+
+ {"omit http with https", "https://www.google.com/", kFormatUrlOmitHTTP,
+ net::UnescapeRule::NORMAL, L"https://www.google.com/", 8},
{"omit http starts with ftp.", "http://ftp.google.com/",
kFormatUrlOmitHTTP, net::UnescapeRule::NORMAL, L"http://ftp.google.com/",
@@ -690,8 +852,8 @@ TEST(UrlFormatterTest, FormatUrl) {
kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
L"http://www.google.com/?", 7},
{"omit slash when it's not the entire path", "http://www.google.com/foo",
- kFormatUrlOmitTrailingSlashOnBareHostname,
- net::UnescapeRule::NORMAL, L"http://www.google.com/foo", 7},
+ kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
+ L"http://www.google.com/foo", 7},
{"omit slash for nonstandard URLs", "data:/",
kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
L"data:/", 5},
@@ -705,20 +867,123 @@ TEST(UrlFormatterTest, FormatUrl) {
L"view-source:http://\x30B0\x30FC\x30B0\x30EB.jp/", 19},
{"view-source of view-source",
- "view-source:view-source:http://xn--qcka1pmc.jp/",
- default_format_type, net::UnescapeRule::NORMAL,
+ "view-source:view-source:http://xn--qcka1pmc.jp/", default_format_type,
+ net::UnescapeRule::NORMAL,
L"view-source:view-source:http://xn--qcka1pmc.jp/", 12},
// view-source should omit http and trailing slash where non-view-source
// would.
- {"view-source omit http", "view-source:http://a.b/c",
- kFormatUrlOmitAll, net::UnescapeRule::NORMAL, L"view-source:a.b/c", 12},
+ {"view-source omit http", "view-source:http://a.b/c", kFormatUrlOmitAll,
+ net::UnescapeRule::NORMAL, L"view-source:a.b/c", 12},
{"view-source omit http starts with ftp.", "view-source:http://ftp.b/c",
kFormatUrlOmitAll, net::UnescapeRule::NORMAL,
L"view-source:http://ftp.b/c", 19},
{"view-source omit slash when it's the entire path",
- "view-source:http://a.b/", kFormatUrlOmitAll,
- net::UnescapeRule::NORMAL, L"view-source:a.b", 12},
+ "view-source:http://a.b/", kFormatUrlOmitAll, net::UnescapeRule::NORMAL,
+ L"view-source:a.b", 12},
+
+ // -------- elide after host --------
+ {"elide after host but still strip trailing slashes",
+ "http://google.com/",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com", 0},
+ {"elide after host in simple filename-only case", "http://google.com/foo",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host in directory and file case", "http://google.com/ab/cd",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host with query only", "http://google.com/?foo=bar",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host with ref only", "http://google.com/#foobar",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host with path and query only", "http://google.com/foo?a=b",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host with path and ref only", "http://google.com/foo#c",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host with query and ref only", "http://google.com/?a=b#c",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host with path, query and ref",
+ "http://google.com/foo?a=b#c",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+ {"elide after host with repeated delimiters (sanity check)",
+ "http://google.com////???####",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, L"google.com/\x2026\x0000", 0},
+
+ // -------- omit https --------
+ {"omit https", "https://www.google.com/", kFormatUrlExperimentalOmitHTTPS,
+ net::UnescapeRule::NORMAL, L"www.google.com/", 0},
+ {"omit https but do not omit http", "http://www.google.com/",
+ kFormatUrlExperimentalOmitHTTPS, net::UnescapeRule::NORMAL,
+ L"http://www.google.com/", 7},
+ {"omit https, username, and password",
+ "https://user:password@example.com/foo",
+ kFormatUrlOmitAll | kFormatUrlExperimentalOmitHTTPS,
+ net::UnescapeRule::NORMAL, L"example.com/foo", 0},
+ {"omit https, but preserve user name and password",
+ "https://user:password@example.com/foo", kFormatUrlExperimentalOmitHTTPS,
+ net::UnescapeRule::NORMAL, L"user:password@example.com/foo", 14},
+ {"omit https should not affect hosts starting with ftp.",
+ "https://ftp.google.com/",
+ kFormatUrlOmitHTTP | kFormatUrlExperimentalOmitHTTPS,
+ net::UnescapeRule::NORMAL, L"https://ftp.google.com/", 8},
+
+ // -------- omit trivial subdomains --------
+ {"omit trivial subdomains - trim www", "http://www.google.com/",
+ kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ L"http://google.com/", 7},
+ {"omit trivial subdomains - trim m", "http://m.google.com/",
+ kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ L"http://google.com/", 7},
+ {"omit trivial subdomains - trim m and www", "http://m.www.google.com/",
+ kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ L"http://google.com/", 7},
+ {"omit trivial subdomains - trim m from middle",
+ "http://en.m.wikipedia.org/",
+ kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ L"http://en.wikipedia.org/", 7},
+ {"omit trivial subdomains - don't do blind substring matches for www",
+ "http://wwww.google.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, L"http://wwww.google.com/", 7},
+ {"omit trivial subdomains - don't do blind substring matches for m",
+ "http://foom.google.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, L"http://foom.google.com/", 7},
+ {"omit trivial subdomains - don't crash on multiple delimiters",
+ "http://www...m..foobar...google.com/",
+ kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ L"http://...foobar...google.com/", 7},
+
+ {"omit trivial subdomains - sanity check for ordinary subdomains",
+ "http://mail.yahoo.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, L"http://mail.yahoo.com/", 7},
+ {"omit trivial subdomains - sanity check for auth",
+ "http://www:m@google.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, L"http://www:m@google.com/", 13},
+ {"omit trivial subdomains - sanity check for path",
+ "http://google.com/www.m.foobar",
+ kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ L"http://google.com/www.m.foobar", 7},
+ {"omit trivial subdomains - sanity check for IDN",
+ "http://www.xn--cy2a840a.m.xn--cy2a840a.com",
+ kFormatUrlExperimentalOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ L"http://\x89c6\x9891.\x89c6\x9891.com/", 7},
+
+ {"omit trivial subdomains but leave registry and domain alone - trivial",
+ "http://google.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, L"http://google.com/", 7},
+ {"omit trivial subdomains but leave registry and domain alone - www",
+ "http://www.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, L"http://www.com/", 7},
+ {"omit trivial subdomains but leave registry and domain alone - co.uk",
+ "http://m.co.uk/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, L"http://m.co.uk/", 7},
};
for (size_t i = 0; i < arraysize(tests); ++i) {
@@ -1061,6 +1326,68 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
};
CheckAdjustedOffsets("http://user@foo.com/", kFormatUrlOmitAll,
net::UnescapeRule::NORMAL, omit_all_offsets);
+
+ const size_t elide_after_host_offsets[] = {
+ 0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 9};
+ CheckAdjustedOffsets("http://foo.com/abcdefg",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, elide_after_host_offsets);
+ CheckAdjustedOffsets("http://foo.com/abc/def",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, elide_after_host_offsets);
+ CheckAdjustedOffsets("http://foo.com/abc?a=b",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, elide_after_host_offsets);
+ CheckAdjustedOffsets("http://foo.com/abc#def",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, elide_after_host_offsets);
+ CheckAdjustedOffsets("http://foo.com/a?a=b#f",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, elide_after_host_offsets);
+ CheckAdjustedOffsets("http://foo.com//??###",
+ kFormatUrlOmitAll | kFormatUrlExperimentalElideAfterHost,
+ net::UnescapeRule::NORMAL, elide_after_host_offsets);
+
+ const size_t omit_https_offsets[] = {
+ 0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, 1, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ CheckAdjustedOffsets("https://www.google.com/",
+ kFormatUrlExperimentalOmitHTTPS,
+ net::UnescapeRule::NORMAL, omit_https_offsets);
+
+ const size_t omit_https_with_auth_offsets[] = {
+ 0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0,
+ kNpos, kNpos, kNpos, 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ CheckAdjustedOffsets("https://u:p@www.google.com/",
+ kFormatUrlOmitAll | kFormatUrlExperimentalOmitHTTPS,
+ net::UnescapeRule::NORMAL, omit_https_with_auth_offsets);
+
+ const size_t strip_trivial_subdomains_offsets_1[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, 7, kNpos, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
+ CheckAdjustedOffsets("http://www.m.google.com/foo/",
+ kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL,
+ strip_trivial_subdomains_offsets_1);
+
+ const size_t strip_trivial_subdomains_offsets_2[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, kNpos, 7, 8, 9,
+ 10, kNpos, kNpos, kNpos, 10, 11, 12, 13, 14, 15, 16, 17};
+ CheckAdjustedOffsets(
+ "http://m.en.www.foo.com/", kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL, strip_trivial_subdomains_offsets_2);
+
+ const size_t strip_trivial_subdomains_from_idn_offsets[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos,
+ kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
+ kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 12,
+ 13, 14, 15, 16, 17, 18, 19};
+ CheckAdjustedOffsets("http://www.xn--l8jvb1ey91xtjb.jp/foo/",
+ kFormatUrlExperimentalOmitTrivialSubdomains,
+ net::UnescapeRule::NORMAL,
+ strip_trivial_subdomains_from_idn_offsets);
}
} // namespace
diff --git a/chromium/components/url_matcher/url_matcher_factory_unittest.cc b/chromium/components/url_matcher/url_matcher_factory_unittest.cc
index 3e51023d984..bb598348976 100644
--- a/chromium/components/url_matcher/url_matcher_factory_unittest.cc
+++ b/chromium/components/url_matcher/url_matcher_factory_unittest.cc
@@ -34,7 +34,8 @@ TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) {
// Invalid value type: {"hostSuffix": []}
base::DictionaryValue invalid_condition2;
- invalid_condition2.Set(keys::kHostSuffixKey, new base::ListValue);
+ invalid_condition2.Set(keys::kHostSuffixKey,
+ base::MakeUnique<base::ListValue>());
// Invalid regex value: {"urlMatches": "*"}
base::DictionaryValue invalid_condition3;
@@ -53,21 +54,21 @@ TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) {
// }
// Port range: Allow 80;1000-1010.
- std::unique_ptr<base::ListValue> port_range(new base::ListValue());
+ auto port_range = base::MakeUnique<base::ListValue>();
port_range->AppendInteger(1000);
port_range->AppendInteger(1010);
- base::ListValue* port_ranges = new base::ListValue();
+ auto port_ranges = base::MakeUnique<base::ListValue>();
port_ranges->AppendInteger(80);
port_ranges->Append(std::move(port_range));
- base::ListValue* scheme_list = new base::ListValue();
+ auto scheme_list = base::MakeUnique<base::ListValue>();
scheme_list->AppendString("http");
base::DictionaryValue valid_condition;
valid_condition.SetString(keys::kHostSuffixKey, "example.com");
valid_condition.SetString(keys::kHostPrefixKey, "www");
- valid_condition.Set(keys::kPortsKey, port_ranges);
- valid_condition.Set(keys::kSchemesKey, scheme_list);
+ valid_condition.Set(keys::kPortsKey, std::move(port_ranges));
+ valid_condition.Set(keys::kSchemesKey, std::move(scheme_list));
// Test wrong condition name passed.
error.clear();
@@ -142,10 +143,10 @@ TEST(URLMatcherFactoryTest, UpperCase) {
invalid_condition4.SetString(keys::kHostEqualsKey, "WWW.example.Com");
// {"scheme": ["HTTP"]}
- base::ListValue* scheme_list = new base::ListValue();
+ auto scheme_list = base::MakeUnique<base::ListValue>();
scheme_list->AppendString("HTTP");
base::DictionaryValue invalid_condition5;
- invalid_condition5.Set(keys::kSchemesKey, scheme_list);
+ invalid_condition5.Set(keys::kSchemesKey, std::move(scheme_list));
const base::DictionaryValue* invalid_conditions[] = {
&invalid_condition1,
diff --git a/chromium/components/url_pattern_index/BUILD.gn b/chromium/components/url_pattern_index/BUILD.gn
new file mode 100644
index 00000000000..410252b0dec
--- /dev/null
+++ b/chromium/components/url_pattern_index/BUILD.gn
@@ -0,0 +1,70 @@
+# 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.
+
+static_library("url_pattern_index") {
+ sources = [
+ "closed_hash_map.h",
+ "copying_file_stream.cc",
+ "copying_file_stream.h",
+ "fuzzy_pattern_matching.cc",
+ "fuzzy_pattern_matching.h",
+ "ngram_extractor.h",
+ "string_splitter.h",
+ "uint64_hasher.h",
+ "unindexed_ruleset.cc",
+ "unindexed_ruleset.h",
+ "url_pattern.cc",
+ "url_pattern.h",
+ "url_pattern_index.cc",
+ "url_pattern_index.h",
+ ]
+
+ public_deps = [
+ "//components/url_pattern_index/flat:url_pattern_index",
+ "//components/url_pattern_index/proto:url_pattern_index",
+ ]
+
+ deps = [
+ "//base",
+ "//third_party/flatbuffers:flatbuffers",
+ "//third_party/protobuf:protobuf_lite",
+ "//url",
+ ]
+}
+
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "url_rule_test_support.cc",
+ "url_rule_test_support.h",
+ ]
+ deps = [
+ ":url_pattern_index",
+ "//base",
+ "//net",
+ "//third_party/protobuf:protobuf_lite",
+ "//url",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "closed_hash_map_unittest.cc",
+ "fuzzy_pattern_matching_unittest.cc",
+ "ngram_extractor_unittest.cc",
+ "string_splitter_unittest.cc",
+ "unindexed_ruleset_unittest.cc",
+ "url_pattern_index_unittest.cc",
+ "url_pattern_unittest.cc",
+ ]
+ deps = [
+ ":test_support",
+ ":url_pattern_index",
+ "//base",
+ "//testing/gtest",
+ "//third_party/protobuf:protobuf_lite",
+ "//url",
+ ]
+}
diff --git a/chromium/components/url_pattern_index/DEPS b/chromium/components/url_pattern_index/DEPS
new file mode 100644
index 00000000000..55dda8afb39
--- /dev/null
+++ b/chromium/components/url_pattern_index/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+net/base/registry_controlled_domains",
+ "+third_party/flatbuffers",
+ "+third_party/protobuf",
+]
diff --git a/chromium/components/url_pattern_index/OWNERS b/chromium/components/url_pattern_index/OWNERS
new file mode 100644
index 00000000000..8e8ba099fc7
--- /dev/null
+++ b/chromium/components/url_pattern_index/OWNERS
@@ -0,0 +1,3 @@
+csharrison@chromium.org
+engedy@chromium.org
+pkalinnikov@chromium.org
diff --git a/chromium/components/url_pattern_index/README b/chromium/components/url_pattern_index/README
new file mode 100644
index 00000000000..755e9df34a3
--- /dev/null
+++ b/chromium/components/url_pattern_index/README
@@ -0,0 +1,12 @@
+UrlPatternIndex component can be used to build an index over a set of URL rules,
+and speed up matching network requests against these rules.
+
+A URL rule (see flat::UrlRule structure) describes a subset of network requests
+that it targets. The essential element of the rule is its URL pattern, which is
+a simplified regular expression (a string with wildcards). UrlPatternIndex is
+mainly based on text fragments extracted from the patterns.
+
+The component uses FlatBuffers serialization library to represent the rules and
+the index. The key advantage of the format is that it does not require
+deserialization. Once built, the data structure can be stored on disk or
+transferred, then copied/loaded/memory-mapped and used directly.
diff --git a/chromium/components/url_pattern_index/closed_hash_map.h b/chromium/components/url_pattern_index/closed_hash_map.h
new file mode 100644
index 00000000000..a91cfb2dd2a
--- /dev/null
+++ b/chromium/components/url_pattern_index/closed_hash_map.h
@@ -0,0 +1,294 @@
+// 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_URL_PATTERN_INDEX_CLOSED_HASH_MAP_H_
+#define COMPONENTS_URL_PATTERN_INDEX_CLOSED_HASH_MAP_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "base/bits.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace url_pattern_index {
+
+template <typename KeyType_, typename Hasher_>
+class SimpleQuadraticProber;
+
+template <typename KeyType_, typename ValueType_, typename Prober_>
+class ClosedHashMap;
+
+// The default Prober used by the HashMap.
+template <typename KeyType_, typename Hasher_>
+using DefaultProber = SimpleQuadraticProber<KeyType_, Hasher_>;
+
+// The default HashMap data structure meant to be used. For more fine-grained
+// control one can use ClosedHashMap with their own Prober.
+template <typename KeyType_,
+ typename ValueType_,
+ typename Hasher_ = std::hash<KeyType_>>
+using HashMap =
+ ClosedHashMap<KeyType_, ValueType_, DefaultProber<KeyType_, Hasher_>>;
+
+// An insert-only map implemented as a hash-table with open addressing (also
+// called closed hashing). The table can contain up to 2^30 distinct keys (or up
+// to 2^32 - 1 on 64bit systems).
+//
+// On normal operation load factor varies within range (1/4, 1/2]. The real load
+// factor can be less or equal to 1/4 if rehashing is requested explicitly to
+// allocate more memory for future insertions.
+//
+// The table discloses its internal structure in order to allow converting it to
+// different formats. It consists of 2 vectors, |hash_table| and |entries|. The
+// |entries| is a vector of pair<KeyType, ValueType>, whereas |hash_table|'s
+// elements (also called slots) are indexes into the |entries| vector. If, for
+// some i, hash_table[i] >= entries.size() then the i-th slot is empty. It is
+// guaranteed that each of the entries is referenced by exactly one table slot.
+//
+// The Prober is a strategy class used to find a slot for a particular key by
+// probing the key against a sequence of slots called a probe sequence. Often it
+// stores one or more hashers - functors used to calculate hash values of the
+// keys in order to parameterize the probe sequence. The Prober class should
+// provide the following:
+//
+// Public type(s):
+// KeyType - the type of the keys the table can store. Should be equal to the
+// ClosedHashTable's KeyType.
+//
+// Public method(s):
+// template <typename SlotCompare>
+// size_t FindSlot(const KeyType& key,
+// size_t table_size,
+// const SlotCompare& compare) const;
+//
+// Walks the probe sequence for the given |key| starting from some initial
+// slot calculated deterministically from the |key|, e.g. by computing its
+// hash code. The walk continues until it finds a hash table slot such that
+// compare(key, slot) returns true (which, for instance, can mean that the
+// slot is empty or its key is equal to |key|). Returns the index of that slot
+// in [0, table_size). The method is required to perform a finite number of
+// probes until it finds a slot.
+//
+// The same Prober class can be used to ensure compatibility when performing
+// lookups in two different representations of what is conceptually the same
+// hash map.
+//
+// TODO(pkalinnikov): Add key comparator.
+template <typename KeyType_, typename ValueType_, typename Prober_>
+class ClosedHashMap {
+ public:
+ using KeyType = KeyType_;
+ using ValueType = ValueType_;
+ using EntryType = std::pair<KeyType, ValueType>;
+ using Prober = Prober_;
+
+ static_assert(std::is_same<KeyType, typename Prober::KeyType>::value,
+ "The KeyType should be the same in the Prober.");
+
+ // Creates an empty hash-map with the default prober, and space allocated for
+ // 4 distinct keys.
+ ClosedHashMap() : ClosedHashMap(4) {}
+
+ // Creates an empty hash-map with the specified |capacity|, so that this many
+ // distinct keys can be inserted with no rehashing or reallocation taking
+ // place. The |prober| is a strategy used for finding slots for the keys.
+ explicit ClosedHashMap(size_t capacity, const Prober& prober = Prober())
+ : hash_table_(CalculateHashTableSizeFor(capacity), EmptySlot()),
+ prober_(prober) {
+ entries_.reserve(capacity);
+ }
+
+ // Returns the number of distinct keys.
+ size_t size() const { return entries_.size(); }
+
+ // Returns the number of slots in the |hash_table|.
+ size_t table_size() const { return hash_table_.size(); }
+
+ const std::vector<uint32_t>& hash_table() const { return hash_table_; }
+ const std::vector<EntryType>& entries() const { return entries_; }
+
+ // Returns a pointer to the value stored for the given |key|, or nullptr if
+ // the |key| is not present in the map. The returned pointer is guaranteed to
+ // be valid as long as no new keys get added to the map (more strictly
+ // speaking, as long as no reallocations happen for the |entries| vector).
+ const ValueType* Get(const KeyType& key) const {
+ const size_t entry_index = hash_table_[FindSlotForKey(key)];
+ if (entry_index == EmptySlot())
+ return nullptr;
+ DCHECK_LT(entry_index, entries_.size());
+ return &entries_[entry_index].second;
+ }
+
+ // Associates |value| with the given |key| if the |key| is not yet present in
+ // the map, and returns true. Otherwise does nothing and returns false.
+ bool Insert(const KeyType& key, ValueType value) {
+ const size_t slot = FindSlotForKey(key);
+ if (hash_table_[slot] != EmptySlot())
+ return false;
+
+ EmplaceKeyValue(slot, key, std::move(value));
+ return true;
+ }
+
+ // Returns a reference to the value stored for the given |key|. If the |key|
+ // is not present in the map, it's inserted and value-initialized. The same
+ // guarantee on reference validity applies as for the result of Get(key).
+ ValueType& operator[](const KeyType& key) {
+ const size_t slot = FindSlotForKey(key);
+ size_t entry_index = hash_table_[slot];
+ if (entry_index == EmptySlot()) {
+ entry_index = EmplaceKeyValue(slot, key, ValueType());
+ }
+
+ DCHECK_LT(entry_index, entries_.size());
+ return entries_[entry_index].second;
+ }
+
+ // Resizes the |hash_table|, if necessary, so that at least |capacity|
+ // distinct keys can be stored with the load factor being no higher than 1/2.
+ void Rehash(size_t capacity) {
+ if (capacity <= hash_table_.size() / 2)
+ return;
+ DCHECK_LE(entries_.size(), static_cast<size_t>(EmptySlot()));
+
+ hash_table_.assign(CalculateHashTableSizeFor(capacity), EmptySlot());
+ for (size_t index = 0; index != entries_.size(); ++index) {
+ const size_t slot = FindSlotForKey(entries_[index].first);
+ DCHECK_EQ(hash_table_[slot], EmptySlot());
+ hash_table_[slot] = static_cast<uint32_t>(index);
+ }
+ }
+
+ // Reserves enough space so that |capacity| distinct keys can be stored with
+ // the load factor being no higher than 1/2.
+ void Reserve(size_t capacity) {
+ Rehash(capacity);
+ entries_.reserve(capacity);
+ }
+
+ private:
+ // The indicator of absent entry for a certain slot in |hash_table|.
+ static constexpr uint32_t EmptySlot() {
+ return std::numeric_limits<uint32_t>::max();
+ }
+
+ // Returns the number of |hash_table| slots necessary to maintain a load
+ // factor between 1/4 and 1/2 for the number of distinct keys given by
+ // |capacity|.
+ static size_t CalculateHashTableSizeFor(size_t capacity) {
+ // TODO(pkalinnikov): Implement base::bits::Log2Ceiling for arbitrary types.
+ const uint32_t capacity_32 = base::checked_cast<uint32_t>(capacity);
+ const int power_of_two = base::bits::Log2Ceiling(capacity_32) + 1;
+ CHECK_LT(power_of_two, std::numeric_limits<size_t>::digits);
+ return static_cast<size_t>(1) << power_of_two;
+ }
+
+ // Adds a new |key|-|value| pair to the structure. Returns the index of the
+ // newly created entry. The table is rehashed if the newly created entry
+ // bursts the load factor above 1/2. Otherwise the new entry is associated
+ // with a specific |slot| of the table.
+ size_t EmplaceKeyValue(size_t slot, KeyType key, ValueType value) {
+ const size_t entry_index = entries_.size();
+ CHECK_LT(entry_index, static_cast<size_t>(EmptySlot()));
+ entries_.emplace_back(std::move(key), std::move(value));
+
+ if (entry_index >= hash_table_.size() / 2) {
+ Rehash(entry_index + 1);
+ } else {
+ DCHECK_LT(slot, hash_table_.size());
+ DCHECK_EQ(hash_table_[slot], EmptySlot());
+ hash_table_[slot] = static_cast<uint32_t>(entry_index);
+ }
+
+ return entry_index;
+ }
+
+ // Finds a slot such that it's either empty (indicating that the |key| is not
+ // stored) or contains the |key|.
+ size_t FindSlotForKey(const KeyType& key) const {
+ return prober_.FindSlot(
+ key, hash_table_.size(), [this](const KeyType& key, size_t slot_index) {
+ DCHECK_LT(slot_index, hash_table_.size());
+ const uint32_t entry_index = hash_table_[slot_index];
+ DCHECK(entry_index == EmptySlot() || entry_index < entries_.size());
+ return entry_index == EmptySlot() ||
+ entries_[entry_index].first == key;
+ });
+ }
+
+ // Contains indices into |entries_|, or EmptySlot() for free table slots.
+ // Always contains at least twice as many slots as there are elements in
+ // |entries_|, i.e. the load factor is always less than or equal to 1/2.
+ std::vector<uint32_t> hash_table_;
+
+ // Contains all the inserted key-value pairs. The keys are unique. The size of
+ // the vector is never greater than EmptySlot().
+ std::vector<EntryType> entries_;
+
+ // The strategy used to find a slot for a key.
+ Prober prober_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClosedHashMap);
+};
+
+// The implementation of Prober that uses a simple form of quadratic probing.
+// That is, for a given |key| the sequence of probes is the following (modulo
+// the hash table size):
+// hash(key), hash(key) + 1, hash(key) + 3, ..., hash(key) + k * (k - 1) / 2
+//
+// To use this prober the hash table should maintain its size M equal to powers
+// of two. It can be shown that in this case the aforementioned quadratic probe
+// sequence for k <= M visits k distinct table slots.
+//
+// Template parameters:
+// KeyType_ - the type of the table's keys.
+// Hasher_ - the type of the functor used to calculate hashes of keys.
+template <typename KeyType_, typename Hasher_>
+class SimpleQuadraticProber {
+ public:
+ using KeyType = KeyType_;
+ using Hasher = Hasher_;
+
+ // Constructs the prober that uses the |hasher| functor to hash the keys.
+ explicit SimpleQuadraticProber(const Hasher& hasher = Hasher())
+ : hasher_(hasher) {}
+
+ // Returns the slot index for the |key|. Requires that |compare| returns true
+ // for at least one slot index between 0 and |table_size| - 1.
+ template <typename SlotCompare>
+ size_t FindSlot(const KeyType& key,
+ size_t table_size,
+ const SlotCompare& compare) const {
+ DCHECK_GT(table_size, 0u);
+ DCHECK_EQ(table_size & (table_size - 1), 0u);
+ const size_t kMask = table_size - 1;
+
+ size_t slot_index = hasher_(key) & kMask;
+ // The loop will always be finite, since |compare| is guaranteed to return
+ // true for at least 1 slot index, and the probe sequence visits all slots.
+ for (size_t step_size = 1; !compare(key, slot_index); ++step_size) {
+ DCHECK_LT(step_size, table_size);
+ slot_index = (slot_index + step_size) & kMask;
+ }
+
+ DCHECK_LT(slot_index, table_size);
+ return slot_index;
+ }
+
+ private:
+ // The functor used to calculate hashes of keys.
+ Hasher hasher_;
+};
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_CLOSED_HASH_MAP_H_
diff --git a/chromium/components/url_pattern_index/closed_hash_map_unittest.cc b/chromium/components/url_pattern_index/closed_hash_map_unittest.cc
new file mode 100644
index 00000000000..122e9125c7c
--- /dev/null
+++ b/chromium/components/url_pattern_index/closed_hash_map_unittest.cc
@@ -0,0 +1,166 @@
+// 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/url_pattern_index/closed_hash_map.h"
+
+#include <string>
+#include <vector>
+
+#include "components/url_pattern_index/uint64_hasher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace url_pattern_index {
+
+namespace {
+
+template <typename MapType>
+void ExpectHashMapIntegrity(const MapType& map, size_t min_capacity = 0) {
+ EXPECT_EQ(map.entries().size(), map.size());
+ EXPECT_EQ(map.hash_table().size(), map.table_size());
+ EXPECT_LE(map.size() * 2, map.table_size());
+ EXPECT_LE(min_capacity * 2, map.table_size());
+
+ std::vector<bool> entry_is_referenced(map.size());
+ for (size_t i = 0; i < map.table_size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "Hash-table slot: " << i);
+
+ const uint32_t entry_index = map.hash_table()[i];
+ if (static_cast<size_t>(entry_index) >= map.size())
+ continue;
+ EXPECT_FALSE(entry_is_referenced[entry_index]);
+ entry_is_referenced[entry_index] = true;
+ }
+
+ for (size_t i = 0; i < map.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "Hash-table entry index: " << i);
+ EXPECT_TRUE(entry_is_referenced[i]);
+ }
+}
+
+template <typename MapType>
+void ExpectEmptyMap(const MapType& map, size_t min_capacity) {
+ ExpectHashMapIntegrity(map, min_capacity);
+ EXPECT_EQ(0u, map.size());
+}
+
+} // namespace
+
+TEST(ClosedHashMapTest, EmptyMapDefault) {
+ HashMap<int, int> hm;
+ ExpectEmptyMap(hm, 0);
+ EXPECT_EQ(nullptr, hm.Get(0));
+ EXPECT_EQ(nullptr, hm.Get(100500));
+ EXPECT_GT(hm.table_size(), 0u);
+}
+
+TEST(ClosedHashMapTest, EmptyMapWithCapacity) {
+ HashMap<int, int> hm(100);
+ ExpectEmptyMap(hm, 100);
+ EXPECT_EQ(nullptr, hm.Get(0));
+ EXPECT_EQ(nullptr, hm.Get(100500));
+}
+
+TEST(ClosedHashMapTest, InsertDistinctAndGet) {
+ HashMap<int, int> hm;
+ static const int kKeys[] = {1, 5, 10, 3, -100500};
+ for (int key : kKeys) {
+ EXPECT_TRUE(hm.Insert(key, -key));
+ ExpectHashMapIntegrity(hm);
+ }
+ for (int key : kKeys) {
+ const int* value = hm.Get(key);
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(-key, *value);
+ }
+ EXPECT_EQ(nullptr, hm.Get(1234567));
+}
+
+TEST(ClosedHashMapTest, InsertExistingAndGet) {
+ HashMap<int, int> hm;
+
+ EXPECT_TRUE(hm.Insert(123, -123));
+ EXPECT_FALSE(hm.Insert(123, -124));
+ ExpectHashMapIntegrity(hm);
+
+ const int* value = hm.Get(123);
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(-123, *value);
+}
+
+TEST(ClosedHashMapTest, InsertManyKeysWithCustomHasher) {
+ using CustomProber = SimpleQuadraticProber<uint64_t, Uint64Hasher>;
+
+ ClosedHashMap<uint64_t, std::string, CustomProber> hm;
+ ExpectEmptyMap(hm, 0);
+
+ std::vector<std::pair<uint64_t, std::string>> entries;
+ for (int key = 10, i = 0; key < 1000000; key += ++i) {
+ entries.push_back(std::make_pair(key, std::to_string(key)));
+ }
+
+ size_t expected_size = 0;
+ for (const auto& entry : entries) {
+ EXPECT_TRUE(hm.Insert(entry.first, entry.second));
+ EXPECT_FALSE(hm.Insert(entry.first, "-1"));
+ ++expected_size;
+
+ EXPECT_EQ(expected_size, hm.size());
+ EXPECT_LE(expected_size * 2, hm.table_size());
+ }
+ ExpectHashMapIntegrity(hm);
+
+ for (const auto& entry : entries) {
+ const std::string* value = hm.Get(entry.first);
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(entry.second, *value);
+ }
+}
+
+TEST(ClosedHashMapTest, OperatorBrackets) {
+ HashMap<int, int> hm;
+
+ for (int i = 0; i < 5; ++i) {
+ const size_t expected_size = i ? 1 : 0;
+ EXPECT_EQ(expected_size, hm.size());
+
+ int expected_value = (i + 1) * 10;
+ hm[123] = expected_value;
+ EXPECT_EQ(1u, hm.size());
+
+ const int* value_ptr = hm.Get(123);
+ ASSERT_NE(nullptr, value_ptr);
+ EXPECT_EQ(expected_value, *value_ptr);
+
+ EXPECT_EQ(expected_value, hm[123]);
+ EXPECT_EQ(1u, hm.size());
+
+ expected_value *= 100;
+ hm[123] = expected_value;
+ EXPECT_EQ(expected_value, hm[123]);
+ }
+}
+
+TEST(ClosedHashMapTest, ManualRehash) {
+ HashMap<int, int> hm(3);
+ const size_t expected_table_size = hm.table_size();
+
+ static const int kKeys[] = {1, 5, 10};
+ for (int key : kKeys) {
+ EXPECT_TRUE(hm.Insert(key, -key));
+ }
+ // No rehashing occurred.
+ EXPECT_EQ(expected_table_size, hm.table_size());
+
+ for (int i = 1; i <= 2; ++i) {
+ for (int key : kKeys) {
+ const int* value = hm.Get(key);
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(-key, *value);
+ }
+ hm.Rehash(100 * i);
+ ExpectHashMapIntegrity(hm, 100 * i);
+ }
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/copying_file_stream.cc b/chromium/components/url_pattern_index/copying_file_stream.cc
new file mode 100644
index 00000000000..7f6d6d1d838
--- /dev/null
+++ b/chromium/components/url_pattern_index/copying_file_stream.cc
@@ -0,0 +1,31 @@
+// 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/url_pattern_index/copying_file_stream.h"
+
+namespace url_pattern_index {
+
+// CopyingFileInputStream ------------------------------------------------------
+
+CopyingFileInputStream::~CopyingFileInputStream() = default;
+
+CopyingFileInputStream::CopyingFileInputStream(base::File file)
+ : file_(std::move(file)) {}
+
+int CopyingFileInputStream::Read(void* buffer, int size) {
+ return file_.ReadAtCurrentPosNoBestEffort(static_cast<char*>(buffer), size);
+}
+
+// CopyingFileOutputStream -----------------------------------------------------
+CopyingFileOutputStream::~CopyingFileOutputStream() = default;
+
+CopyingFileOutputStream::CopyingFileOutputStream(base::File file)
+ : file_(std::move(file)) {}
+
+bool CopyingFileOutputStream::Write(const void* buffer, int size) {
+ return file_.WriteAtCurrentPos(static_cast<const char*>(buffer), size) ==
+ size;
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/copying_file_stream.h b/chromium/components/url_pattern_index/copying_file_stream.h
new file mode 100644
index 00000000000..f8e3009327f
--- /dev/null
+++ b/chromium/components/url_pattern_index/copying_file_stream.h
@@ -0,0 +1,51 @@
+// 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_URL_PATTERN_INDEX_COPYING_FILE_STREAM_H_
+#define COMPONENTS_URL_PATTERN_INDEX_COPYING_FILE_STREAM_H_
+
+#include "base/files/file.h"
+#include "base/macros.h"
+#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
+
+namespace url_pattern_index {
+
+// Implements a CopyingInputStream that reads from a base::File. Can be used in
+// combination with CopyingInputStreamAdaptor for reading from that file through
+// the ZeroCopyInputStream interface.
+class CopyingFileInputStream : public google::protobuf::io::CopyingInputStream {
+ public:
+ explicit CopyingFileInputStream(base::File file);
+ ~CopyingFileInputStream() override;
+
+ // google::protobuf::io::CopyingInputStream:
+ int Read(void* buffer, int size) override;
+
+ private:
+ base::File file_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyingFileInputStream);
+};
+
+// Implements a CopyingOutputStream that writes to a base::File. Can be used in
+// combination with CopyingOutputStreamAdaptor for writing to that file through
+// the ZeroCopyOutputStream interface.
+class CopyingFileOutputStream
+ : public google::protobuf::io::CopyingOutputStream {
+ public:
+ explicit CopyingFileOutputStream(base::File file);
+ ~CopyingFileOutputStream() override;
+
+ // google::protobuf::io::CopyingOutputStream:
+ bool Write(const void* buffer, int size) override;
+
+ private:
+ base::File file_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyingFileOutputStream);
+};
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_COPYING_FILE_STREAM_H_
diff --git a/chromium/components/url_pattern_index/flat/BUILD.gn b/chromium/components/url_pattern_index/flat/BUILD.gn
new file mode 100644
index 00000000000..0b20b05a5e0
--- /dev/null
+++ b/chromium/components/url_pattern_index/flat/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+import("//third_party/flatbuffers/flatbuffer.gni")
+
+flatbuffer("url_pattern_index") {
+ sources = [
+ "url_pattern_index.fbs",
+ ]
+}
diff --git a/chromium/components/url_pattern_index/flat/url_pattern_index.fbs b/chromium/components/url_pattern_index/flat/url_pattern_index.fbs
new file mode 100644
index 00000000000..cb5d305a55c
--- /dev/null
+++ b/chromium/components/url_pattern_index/flat/url_pattern_index.fbs
@@ -0,0 +1,125 @@
+// 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.
+
+namespace url_pattern_index.flat;
+
+// Corresponds to url_pattern_index::proto::UrlPatternType.
+enum UrlPatternType : ubyte {
+ SUBSTRING,
+ WILDCARDED,
+ REGEXP,
+}
+
+// Corresponds to url_pattern_index::proto::AnchorType.
+enum AnchorType : ubyte {
+ NONE,
+ BOUNDARY,
+ SUBDOMAIN,
+}
+
+// URL rule matching options. These correspond to multiple fields of
+// url_pattern_index::proto::UrlRule, but here, they are represented as flags
+// of the same bitmask to allow for compact storage.
+enum OptionFlag : ubyte (bit_flags) {
+ IS_WHITELIST,
+ APPLIES_TO_FIRST_PARTY,
+ APPLIES_TO_THIRD_PARTY,
+ IS_MATCH_CASE,
+}
+
+// The options controlling whether or not to activate filtering for subresources
+// of documents that match the URL pattern of the rule.
+// Corresponds to url_pattern_index::proto::ActivationType.
+enum ActivationType : ubyte (bit_flags) {
+ DOCUMENT, // Disable all rules on the page.
+ GENERIC_BLOCK, // Disable generic URL rules on the page.
+}
+
+// The types of subresource requests that a URL rule should be applied to.
+// Corresponds to url_pattern_index::proto::ElementType.
+enum ElementType : ushort (bit_flags) {
+ OTHER,
+ SCRIPT,
+ IMAGE,
+ STYLESHEET,
+ OBJECT,
+ XMLHTTPREQUEST,
+ // TODO(crbug.com/713774): Remove OBJECT_SUBREQUEST type once
+ // url_pattern_index no longer has a dependency on proto::UrlRule.
+ OBJECT_SUBREQUEST,
+ SUBDOCUMENT,
+ PING,
+ MEDIA,
+ FONT,
+ WEBSOCKET,
+ // Note: Update the default value for |element_types| field in UrlRule, on
+ // adding/removing values from this enum.
+}
+
+// The flat representation of a single URL rule. For more details regarding the
+// fields please see the comments to url_pattern_index::proto::UrlRule.
+table UrlRule {
+ // Rule matching options, a bitmask consisting of OptionFlags.
+ options : ubyte;
+
+ // A bitmask of ElementType. Equals ElementType_ANY by default for
+ // compactness.
+ element_types : ushort = 4095;
+
+ // A bitmask of ActivationType. Disables all activation types by default.
+ activation_types : ubyte = 0;
+
+ // Use SUBSTRING as default, since it's the most used pattern type. Same as
+ // the corresponding proto::UrlRule::url_pattern_type.
+ url_pattern_type : UrlPatternType = SUBSTRING;
+
+ // Use NONE as default, since most of the rules are not anchored.
+ anchor_left : AnchorType = NONE;
+ anchor_right : AnchorType = NONE;
+
+ // The list of domains to be included/excluded from the filter's affected set.
+ // Kept sorted for fast matching. Should either be null or have at least a
+ // single element.
+ domains_included : [string];
+ domains_excluded : [string];
+
+ // A URL pattern in the format defined by |url_pattern_type|.
+ url_pattern : string;
+}
+
+// Contains an N-gram (acting as a key in a hash table) and a list of URL rules
+// associated with that N-gram.
+table NGramToRules {
+ // A string consisting of N (up to 8) non-special characters, which are stored
+ // in the lowest N non-zero bytes, lower bytes corresponding to later symbols.
+ ngram : ulong;
+
+ // The list of rules containing |ngram| as a substring of their URL pattern.
+ rule_list : [UrlRule];
+}
+
+// A data structure used to select only a handful of URL rule candidates that
+// need to be matched against a certain resource URL.
+table UrlPatternIndex {
+ // The N of an N-gram index. Note: |n| should be between 1 and 8.
+ n : uint;
+
+ // A hash table with open addressing. The keys of the table are N-grams.
+ ngram_index : [NGramToRules];
+
+ // The slot that is pointed to by all empty slots of |ngram_index| hash table.
+ // Note: This is a workaround needed because null offsets are not allowed as
+ // elements of FlatBuffer arrays.
+ ngram_index_empty_slot : NGramToRules;
+
+ // A list storing the rules that doesn't contain any valid N-grams in their
+ // URL patterns. Contains all the REGEXP rules as well.
+ // TODO(pkalinnikov): Think about better implementation for the fallback
+ // index. Possibly make it a hash map and maybe merge it with the N-gram
+ // index, since we can treat any sequence of characters shorter than N as an
+ // N-gram with zero bytes used for padding.
+ fallback_rules : [UrlRule];
+}
+
+root_type UrlPatternIndex;
diff --git a/chromium/components/url_pattern_index/fuzzy_pattern_matching.cc b/chromium/components/url_pattern_index/fuzzy_pattern_matching.cc
new file mode 100644
index 00000000000..92b6032c17f
--- /dev/null
+++ b/chromium/components/url_pattern_index/fuzzy_pattern_matching.cc
@@ -0,0 +1,59 @@
+// 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/url_pattern_index/fuzzy_pattern_matching.h"
+
+#include <algorithm>
+
+namespace url_pattern_index {
+
+namespace {
+
+bool StartsWithFuzzyImpl(base::StringPiece text, base::StringPiece subpattern) {
+ DCHECK_LE(subpattern.size(), text.size());
+
+ for (size_t i = 0; i != subpattern.size(); ++i) {
+ const char text_char = text[i];
+ const char pattern_char = subpattern[i];
+ if (text_char != pattern_char &&
+ (pattern_char != kSeparatorPlaceholder || !IsSeparator(text_char))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+bool StartsWithFuzzy(base::StringPiece text, base::StringPiece subpattern) {
+ return subpattern.size() <= text.size() &&
+ StartsWithFuzzyImpl(text, subpattern);
+}
+
+bool EndsWithFuzzy(base::StringPiece text, base::StringPiece subpattern) {
+ return subpattern.size() <= text.size() &&
+ StartsWithFuzzyImpl(text.substr(text.size() - subpattern.size()),
+ subpattern);
+}
+
+size_t FindFuzzy(base::StringPiece text,
+ base::StringPiece subpattern,
+ size_t from) {
+ if (from > text.size())
+ return base::StringPiece::npos;
+ if (subpattern.empty())
+ return from;
+
+ auto fuzzy_compare = [](char text_char, char subpattern_char) {
+ return text_char == subpattern_char ||
+ (subpattern_char == kSeparatorPlaceholder && IsSeparator(text_char));
+ };
+
+ base::StringPiece::const_iterator found =
+ std::search(text.begin() + from, text.end(), subpattern.begin(),
+ subpattern.end(), fuzzy_compare);
+ return found == text.end() ? base::StringPiece::npos : found - text.begin();
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/fuzzy_pattern_matching.h b/chromium/components/url_pattern_index/fuzzy_pattern_matching.h
new file mode 100644
index 00000000000..52dc75b4069
--- /dev/null
+++ b/chromium/components/url_pattern_index/fuzzy_pattern_matching.h
@@ -0,0 +1,68 @@
+// 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.
+
+// The separator placeholder '^' symbol is used in subpatterns to match any
+// separator character, which is any ASCII symbol except letters, digits, and
+// the following: '_', '-', '.', '%'. Note that the separator placeholder
+// character '^' is itself a separator, as well as '\0'.
+// TODO(pkalinnikov): In addition, a separator placeholder at the end of the
+// pattern can be matched by the end of |text|.
+//
+// We define a fuzzy occurrence as an occurrence of a |subpattern| in |text|
+// such that all its non-placeholder characters are equal to the corresponding
+// characters of the |text|, whereas each '^' placeholder can correspond to any
+// type of separator in |text|.
+
+#ifndef COMPONENTS_URL_PATTERN_INDEX_FUZZY_PATTERN_MATCHING_H_
+#define COMPONENTS_URL_PATTERN_INDEX_FUZZY_PATTERN_MATCHING_H_
+
+#include <stddef.h>
+
+#include "base/strings/string_piece.h"
+
+namespace url_pattern_index {
+
+constexpr char kSeparatorPlaceholder = '^';
+
+inline bool IsAscii(char c) {
+ return !(c & ~0x7F);
+}
+
+inline bool IsAlphaNumericAscii(char c) {
+ if (c <= '9')
+ return c >= '0';
+ c |= 0x20; // Puts all alphabetics (and only them) into the 'a'-'z' range.
+ return c >= 'a' && c <= 'z';
+}
+
+// Returns whether |c| is a separator.
+inline bool IsSeparator(char c) {
+ switch (c) {
+ case '_':
+ case '-':
+ case '.':
+ case '%':
+ return false;
+ case kSeparatorPlaceholder:
+ return true;
+ default:
+ return !IsAlphaNumericAscii(c) && IsAscii(c);
+ }
+}
+
+// Returns whether |text| starts with a fuzzy occurrence of |subpattern|.
+bool StartsWithFuzzy(base::StringPiece text, base::StringPiece subpattern);
+
+// Returns whether |text| ends with a fuzzy occurrence of |subpattern|.
+bool EndsWithFuzzy(base::StringPiece text, base::StringPiece subpattern);
+
+// Returns the position of the leftmost fuzzy occurrence of a |subpattern| in
+// the |text| starting no earlier than |from| the specified position.
+size_t FindFuzzy(base::StringPiece text,
+ base::StringPiece subpattern,
+ size_t from = 0);
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_FUZZY_PATTERN_MATCHING_H_
diff --git a/chromium/components/url_pattern_index/fuzzy_pattern_matching_unittest.cc b/chromium/components/url_pattern_index/fuzzy_pattern_matching_unittest.cc
new file mode 100644
index 00000000000..6bbaf90a90b
--- /dev/null
+++ b/chromium/components/url_pattern_index/fuzzy_pattern_matching_unittest.cc
@@ -0,0 +1,108 @@
+// 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/url_pattern_index/fuzzy_pattern_matching.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace url_pattern_index {
+
+TEST(SubresourceFilterFuzzyPatternMatchingTest, StartsWithFuzzy) {
+ const struct {
+ const char* text;
+ const char* subpattern;
+ bool expected_starts_with;
+ } kTestCases[] = {
+ {"abc", "", true}, {"abc", "a", true}, {"abc", "ab", true},
+ {"abc", "abc", true}, {"abc", "abcd", false}, {"abc", "abc^^", false},
+ {"abc", "abcd^", false}, {"abc", "ab^", false}, {"abc", "bc", false},
+ {"abc", "bc^", false}, {"abc", "^abc", false},
+ };
+ // TODO(pkalinnikov): Make end-of-string match '^' again.
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(testing::Message()
+ << "Test: " << test_case.text
+ << "; Subpattern: " << test_case.subpattern);
+
+ const bool starts_with =
+ StartsWithFuzzy(test_case.text, test_case.subpattern);
+ EXPECT_EQ(test_case.expected_starts_with, starts_with);
+ }
+}
+
+TEST(SubresourceFilterFuzzyPatternMatchingTest, EndsWithFuzzy) {
+ const struct {
+ const char* text;
+ const char* subpattern;
+ bool expected_ends_with;
+ } kTestCases[] = {
+ {"abc", "", true}, {"abc", "c", true}, {"abc", "bc", true},
+ {"abc", "abc", true}, {"abc", "0abc", false}, {"abc", "abc^^", false},
+ {"abc", "abcd^", false}, {"abc", "ab^", false}, {"abc", "ab", false},
+ {"abc", "^abc", false},
+ };
+ // TODO(pkalinnikov): Make end-of-string match '^' again.
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(testing::Message()
+ << "Test: " << test_case.text
+ << "; Subpattern: " << test_case.subpattern);
+
+ const bool ends_with = EndsWithFuzzy(test_case.text, test_case.subpattern);
+ EXPECT_EQ(test_case.expected_ends_with, ends_with);
+ }
+}
+
+TEST(SubresourceFilterFuzzyPatternMatchingTest, FindFuzzy) {
+ const struct {
+ base::StringPiece text;
+ base::StringPiece subpattern;
+ std::vector<size_t> expected_occurrences;
+ } kTestCases[] = {
+ {"abcd", "", {0, 1, 2, 3, 4}},
+ {"abcd", "de", std::vector<size_t>()},
+ {"abcd", "ab", {0}},
+ {"abcd", "bc", {1}},
+ {"abcd", "cd", {2}},
+
+ {"a/bc/a/b", "", {0, 1, 2, 3, 4, 5, 6, 7, 8}},
+ {"a/bc/a/b", "de", std::vector<size_t>()},
+ {"a/bc/a/b", "a/", {0, 5}},
+ {"a/bc/a/c", "a/c", {5}},
+ {"a/bc/a/c", "a^c", {5}},
+ {"a/bc/a/c", "a?c", std::vector<size_t>()},
+
+ {"ab^cd", "ab/cd", std::vector<size_t>()},
+ {"ab^cd", "b/c", std::vector<size_t>()},
+ {"ab^cd", "ab^cd", {0}},
+ {"ab^cd", "b^c", {1}},
+ {"ab^b/b", "b/b", {3}},
+
+ {"a/a/a/a", "a/a", {0, 2, 4}},
+ {"a/a/a/a", "^a^a^a", {1}},
+ {"a/a/a/a", "^a^a?a", std::vector<size_t>()},
+ {"a/a/a/a", "?a?a?a", std::vector<size_t>()},
+ };
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(testing::Message()
+ << "Test: " << test_case.text
+ << "; Subpattern: " << test_case.subpattern);
+
+ std::vector<size_t> occurrences;
+ for (size_t position = 0; position <= test_case.text.size(); ++position) {
+ position = FindFuzzy(test_case.text, test_case.subpattern, position);
+ if (position == base::StringPiece::npos)
+ break;
+ occurrences.push_back(position);
+ }
+
+ EXPECT_EQ(test_case.expected_occurrences, occurrences);
+ }
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/ngram_extractor.h b/chromium/components/url_pattern_index/ngram_extractor.h
new file mode 100644
index 00000000000..d91a04674b3
--- /dev/null
+++ b/chromium/components/url_pattern_index/ngram_extractor.h
@@ -0,0 +1,133 @@
+// 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_URL_PATTERN_INDEX_NGRAM_EXTRACTOR_H_
+#define COMPONENTS_URL_PATTERN_INDEX_NGRAM_EXTRACTOR_H_
+
+#include <stddef.h>
+
+#include <iterator>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+
+namespace url_pattern_index {
+
+// The class used to iteratively extract N-grams from strings. An N-gram is a
+// string consisting of N (up to 8) non-special characters, which are stored in
+// the lowest N non-zero bytes, lower bytes corresponding to later symbols. The
+// size of the integer type limits the maximum value of N. For example an
+// uint64_t can store up to 8-grams.
+//
+// Note: If used for UTF-8 strings, the N-grams can have partial byte sequences.
+//
+// Template parameters:
+// * N - the size of N-grams.
+// * NGramType - the integer type used to encode N-grams.
+// * IsSeparator - the type of a bool(char) functor.
+template <size_t N, typename NGramType, typename IsSeparator>
+class NGramExtractor {
+ public:
+ // An STL compatible input iterator over N-grams contained in a string.
+ class Iterator : public std::iterator<std::input_iterator_tag, NGramType> {
+ public:
+ // Creates an iterator, which points to the leftmost valid N-gram within the
+ // |extractor|'s string, starting from |head|.
+ Iterator(const NGramExtractor& extractor,
+ base::StringPiece::const_iterator head)
+ : extractor_(extractor), head_(head), end_(extractor.string_.end()) {
+ DCHECK_GE(head, extractor_.string_.begin());
+ DCHECK_LE(head, end_);
+
+ CompleteNGramFrom(0);
+ }
+
+ bool operator==(const Iterator& rhs) const { return head_ == rhs.head_; }
+ bool operator!=(const Iterator& rhs) const { return !operator==(rhs); }
+
+ NGramType operator*() const { return ngram_; }
+ NGramType* operator->() const { return &ngram_; }
+
+ Iterator& operator++() {
+ ngram_ &= ~(static_cast<NGramType>(0xFFu) << 8 * (N - 1));
+ ++head_;
+ CompleteNGramFrom(N - 1);
+ return *this;
+ }
+
+ Iterator operator++(int) {
+ Iterator copy(*this);
+ operator++();
+ return copy;
+ }
+
+ private:
+ // Consumes characters starting with the one pointed to by |head_|, as many
+ // of them as needed to extend |ngram_| from its |current_length| to a
+ // length of N. Leaves |head_| pointing to the last character consumed.
+ void CompleteNGramFrom(size_t current_length) {
+ for (; head_ != end_; ++head_) {
+ if (extractor_.is_separator_(*head_)) {
+ current_length = 0;
+ ngram_ = 0;
+ } else {
+ ngram_ = ngram_ << 8 | static_cast<NGramType>(*head_);
+ if (++current_length == N)
+ break;
+ }
+ }
+ }
+
+ const NGramExtractor& extractor_;
+
+ // Always points to the last character included in the current |ngram_|.
+ base::StringPiece::const_iterator head_;
+ // Always points to extractor_.string_.end().
+ base::StringPiece::const_iterator end_;
+
+ // Contains the N-gram currently pointed to by the iterator. Undefined if
+ // the iterator is at the end.
+ NGramType ngram_ = 0;
+ };
+
+ // Constructs an extractor for iterating over N-grams contained in the
+ // |string|. |is_separator| is used to determine whether a certain character
+ // is a separator and should not be contained in an N-gram.
+ NGramExtractor(base::StringPiece string, IsSeparator is_separator)
+ : string_(string), is_separator_(is_separator) {}
+
+ Iterator begin() const { return Iterator(*this, string_.begin()); }
+ Iterator end() const { return Iterator(*this, string_.end()); }
+
+ private:
+ static_assert(std::is_integral<NGramType>::value, "Not an integral type.");
+ static_assert(std::is_unsigned<NGramType>::value, "Not an unsigned type.");
+ static_assert(N > 0u, "N should be positive.");
+ static_assert(N <= sizeof(NGramType), "N-gram doesn't fit into the type.");
+
+ base::StringPiece string_;
+ IsSeparator is_separator_;
+};
+
+// A helper function used to create an NGramExtractor for a |string| without
+// knowing the direct type of the |is_separator| functor.
+//
+// Typical usage:
+// const char* str = "no*abacaba*abcd";
+// auto extractor = CreateNGramExtractor<5, uint64_t>(
+// str, [](char c) { return c == '*'; });
+// for (uint64_t ngram : extractor) {
+// ... process the |ngram| ...
+// }
+template <size_t N, typename NGramType, typename IsSeparator>
+NGramExtractor<N, NGramType, IsSeparator> CreateNGramExtractor(
+ base::StringPiece string,
+ IsSeparator is_separator) {
+ return NGramExtractor<N, NGramType, IsSeparator>(string, is_separator);
+}
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_NGRAM_EXTRACTOR_H_
diff --git a/chromium/components/url_pattern_index/ngram_extractor_unittest.cc b/chromium/components/url_pattern_index/ngram_extractor_unittest.cc
new file mode 100644
index 00000000000..c758f2a9aee
--- /dev/null
+++ b/chromium/components/url_pattern_index/ngram_extractor_unittest.cc
@@ -0,0 +1,111 @@
+// 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/url_pattern_index/ngram_extractor.h"
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace url_pattern_index {
+
+namespace {
+
+bool IsSeparatorTrue(char) {
+ return true;
+}
+bool IsSeparatorFalse(char) {
+ return false;
+}
+bool IsSpecialChar(char c) {
+ return c == '*' || c == '^';
+}
+
+template <typename IntType>
+IntType EncodeStringToInteger(const char* data, size_t size) {
+ EXPECT_LE(size, sizeof(IntType));
+
+ IntType encoded_string = 0;
+ for (size_t i = 0; i < size; ++i) {
+ encoded_string = (encoded_string << 8) | static_cast<IntType>(data[i]);
+ }
+ return encoded_string;
+}
+
+} // namespace
+
+TEST(NGramExtractorTest, EmptyString) {
+ const char* kString = "";
+ auto extractor = CreateNGramExtractor<3, uint32_t>(kString, IsSpecialChar);
+ EXPECT_EQ(extractor.begin(), extractor.end());
+}
+
+TEST(NGramExtractorTest, ShortString) {
+ const char* kString = "abacab";
+ auto extractor = CreateNGramExtractor<7, uint64_t>(kString, IsSeparatorFalse);
+ EXPECT_EQ(extractor.begin(), extractor.end());
+}
+
+TEST(NGramExtractorTest, ShortPieces) {
+ const char* kString = "1**abac*abc*abcd*00";
+ auto extractor = CreateNGramExtractor<6, uint64_t>(kString, IsSpecialChar);
+ EXPECT_EQ(extractor.begin(), extractor.end());
+}
+
+TEST(NGramExtractorTest, IsSeparatorAlwaysTrue) {
+ const char* kString = "abacaba";
+ auto extractor = CreateNGramExtractor<3, uint32_t>(kString, IsSeparatorTrue);
+ EXPECT_EQ(extractor.begin(), extractor.end());
+}
+
+TEST(NGramExtractorTest, IsSeparatorAlwaysFalse) {
+ const std::string kString = "abacaba123";
+ constexpr size_t N = 3;
+
+ std::vector<uint32_t> expected_ngrams;
+ for (size_t begin = 0; begin + N <= kString.size(); ++begin) {
+ expected_ngrams.push_back(
+ EncodeStringToInteger<uint32_t>(kString.data() + begin, N));
+ }
+
+ auto extractor = CreateNGramExtractor<N, uint32_t>(kString, IsSeparatorFalse);
+ std::vector<uint32_t> actual_ngrams(extractor.begin(), extractor.end());
+ EXPECT_EQ(expected_ngrams, actual_ngrams);
+}
+
+TEST(NGramExtractorTest, NGramsArePresent) {
+ constexpr size_t N = 6;
+ const std::string kTestCases[] = {
+ "abcdef", "abacaba", "*abacaba",
+ "abacaba*", "*abacaba*", "*abacaba*abc^1005001*",
+ };
+
+ for (const std::string& string : kTestCases) {
+ SCOPED_TRACE(testing::Message() << "String: " << string);
+
+ std::vector<uint64_t> expected_ngrams;
+ for (size_t begin = 0; begin + N <= string.size(); ++begin) {
+ bool is_valid_ngram = true;
+ for (size_t i = 0; i < N; ++i) {
+ if (IsSpecialChar(string[begin + i])) {
+ is_valid_ngram = false;
+ break;
+ }
+ }
+ if (is_valid_ngram) {
+ expected_ngrams.push_back(
+ EncodeStringToInteger<uint64_t>(string.data() + begin, N));
+ }
+ }
+
+ auto extractor = CreateNGramExtractor<N, uint64_t>(string, IsSpecialChar);
+ std::vector<uint64_t> actual_ngrams(extractor.begin(), extractor.end());
+ EXPECT_EQ(expected_ngrams, actual_ngrams);
+ }
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/proto/BUILD.gn b/chromium/components/url_pattern_index/proto/BUILD.gn
new file mode 100644
index 00000000000..035744e2a2b
--- /dev/null
+++ b/chromium/components/url_pattern_index/proto/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("url_pattern_index") {
+ sources = [
+ "rules.proto",
+ ]
+}
diff --git a/chromium/components/url_pattern_index/proto/rules.proto b/chromium/components/url_pattern_index/proto/rules.proto
new file mode 100644
index 00000000000..086dfd34179
--- /dev/null
+++ b/chromium/components/url_pattern_index/proto/rules.proto
@@ -0,0 +1,195 @@
+// 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.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package url_pattern_index.proto;
+option java_package = "org.chromium.components.url_pattern_index.proto";
+
+// The type of a subresource filtering rule.
+enum RuleType {
+ RULE_TYPE_UNSPECIFIED = 0;
+
+ RULE_TYPE_COMMENT = 1; // Comment rule.
+ RULE_TYPE_URL = 2; // Network level filtering rule based on URL pattern.
+ RULE_TYPE_CSS = 3; // Element hiding rule based on a CSS selector.
+};
+
+// The format of a URL pattern.
+enum UrlPatternType {
+ URL_PATTERN_TYPE_UNSPECIFIED = 0;
+
+ // A pattern without special characters, e.g. "example.com".
+ URL_PATTERN_TYPE_SUBSTRING = 1;
+
+ // The pattern contains one or more wildcards, namely '*' and/or '^'
+ // characters. The '*' matches any sequence of characters, while the '^'
+ // matches a separator, i.e. anything but a letter, a digit, or one of [-._%].
+ URL_PATTERN_TYPE_WILDCARDED = 2;
+
+ // The pattern is a regular expression.
+ URL_PATTERN_TYPE_REGEXP = 3;
+};
+
+// Types of anchors that can be used to constrain where a URL pattern must
+// begin/end in the URL in order to be considered a match.
+enum AnchorType {
+ ANCHOR_TYPE_UNSPECIFIED = 0;
+
+ // Acts like a '*' wildcard at the respective end of a pattern.
+ ANCHOR_TYPE_NONE = 1;
+ // The pattern must match from the start/until the end of the URL.
+ ANCHOR_TYPE_BOUNDARY = 2;
+ // The pattern must match starting with the TLD+n of the URL's domain, but the
+ // scheme and subdomains (if any) can be arbitrary.
+ ANCHOR_TYPE_SUBDOMAIN = 3;
+};
+
+// The types of subresource requests that a URL rule should be applied to.
+// Note: Values are used as flags in a bitmask.
+enum ElementType {
+ option allow_alias = true;
+ ELEMENT_TYPE_UNSPECIFIED = 0;
+
+ ELEMENT_TYPE_OTHER = 1;
+ ELEMENT_TYPE_SCRIPT = 2;
+ ELEMENT_TYPE_IMAGE = 4;
+ ELEMENT_TYPE_STYLESHEET = 8;
+ ELEMENT_TYPE_OBJECT = 16;
+ ELEMENT_TYPE_XMLHTTPREQUEST = 32;
+ ELEMENT_TYPE_OBJECT_SUBREQUEST = 64;
+ ELEMENT_TYPE_SUBDOCUMENT = 128;
+ ELEMENT_TYPE_PING = 256;
+ ELEMENT_TYPE_MEDIA = 512;
+ ELEMENT_TYPE_FONT = 1024;
+ ELEMENT_TYPE_POPUP = 2048;
+ ELEMENT_TYPE_WEBSOCKET = 4096;
+
+ // NOTE: Keep these two values consistent with the values above.
+ ELEMENT_TYPE_MAX = 4096;
+ ELEMENT_TYPE_ALL = 8191;
+};
+
+// The options controlling whether or not to activate filtering for subresources
+// of documents that match the URL pattern of the rule.
+// Note: Values are used as flags in a bitmask.
+enum ActivationType {
+ option allow_alias = true;
+ ACTIVATION_TYPE_UNSPECIFIED = 0;
+
+ ACTIVATION_TYPE_DOCUMENT = 1; // Disable all rules on the page.
+ ACTIVATION_TYPE_ELEMHIDE = 2; // Disable CSS rules on the page.
+ ACTIVATION_TYPE_GENERICHIDE = 4; // Disable generic CSS rules on the page.
+ ACTIVATION_TYPE_GENERICBLOCK = 8; // Disable generic URL rules on the page.
+
+ // NOTE: Keep these two values consistent with the values above.
+ ACTIVATION_TYPE_MAX = 8;
+ ACTIVATION_TYPE_ALL = 15;
+};
+
+// The semantics of a rule. Defines how the rule relates to other rules and how
+// it influences the filtering decision.
+enum RuleSemantics {
+ RULE_SEMANTICS_UNSPECIFIED = 0;
+
+ // Matching subresource requests should be aborted, matching elements should
+ // be hidden.
+ RULE_SEMANTICS_BLACKLIST = 1;
+ // If the rule matches, it suppresses any matching RULE_SEMANTICS_BLACKLIST
+ // rule.
+ RULE_SEMANTICS_WHITELIST = 2;
+}
+
+// The type of relation between the source of the requested subresource and that
+// of the document.
+enum SourceType {
+ SOURCE_TYPE_UNSPECIFIED = 0;
+
+ SOURCE_TYPE_ANY = 1; // Doesn't matter.
+ SOURCE_TYPE_THIRD_PARTY = 2; // Requesting a trird-party resource.
+ SOURCE_TYPE_FIRST_PARTY = 3; // Requesting a first-party resource.
+}
+
+// An item of the domain list.
+message DomainListItem {
+ // The UTF-8 representation of the domain, e.g. "subdomain.example.com".
+ optional string domain = 1;
+
+ // Defines whether the domain is excluded from the set of domains.
+ optional bool exclude = 2;
+}
+
+// A network level filtering rule based on a URL pattern. Corresponds to
+// RULE_TYPE_URL.
+message UrlRule {
+ // The semantics of the rule.
+ optional RuleSemantics semantics = 1;
+
+ // Restricts application of the rule to first-party/third-party requests.
+ optional SourceType source_type = 2;
+
+ // Stores the ElementTypes that the rule applies to as a bitwise OR of the
+ // corresponding ElementType values.
+ optional int32 element_types = 3;
+
+ // Stores the ActivationTypes associated with the rule as a bitwise OR of the
+ // corresponding ActivationType values.
+ optional int32 activation_types = 4;
+
+ // The rule applies only to subresources of documents loaded from included
+ // domains (or subdomains thereof). If the list is empty, the rule is applied
+ // on documents from all domains.
+ // If |domains| is empty or has exceptions only, the rule is called generic.
+ // Otherwise is it called domain specific, i.e. applies to a limited number of
+ // domains.
+ repeated DomainListItem domains = 5;
+
+ // The format of |url_pattern|.
+ optional UrlPatternType url_pattern_type = 6;
+
+ // Defines where the URL pattern must start in the URL in order to be
+ // considered a match. Never used with REGEXP patterns.
+ optional AnchorType anchor_left = 7;
+
+ // Defines where the URL pattern must end in the URL in order to be
+ // considered a match. Never used with REGEXP patterns. Never equals to
+ // ANCHOR_TYPE_SUBDOMAIN.
+ optional AnchorType anchor_right = 8;
+
+ // When set, the rule applies only to URLs that match |url_pattern| in a
+ // case-sensitive way.
+ optional bool match_case = 9;
+
+ // The URL pattern of the format prescribed by |url_pattern_type|.
+ optional string url_pattern = 10;
+}
+
+// Element hiding rule based on a CSS selector. Corresponds to RULE_TYPE_CSS.
+message CssRule {
+ // The semantics of the rule.
+ optional RuleSemantics semantics = 1;
+
+ // The list of domains, same as UrlRule::domains.
+ repeated DomainListItem domains = 2;
+
+ // A CSS selector as specified in http://www.w3.org/TR/css3-selectors.
+ optional string css_selector = 3;
+}
+
+// A comment line.
+message Comment {
+ // Comment text.
+ optional string text = 1;
+
+ // For special key-value comments, if any.
+ optional string key = 2;
+ optional string value = 3;
+}
+
+// A container for lists of non-comment rules collated by RuleType.
+message FilteringRules {
+ repeated UrlRule url_rules = 1;
+ repeated CssRule css_rules = 2;
+}
diff --git a/chromium/components/url_pattern_index/string_splitter.h b/chromium/components/url_pattern_index/string_splitter.h
new file mode 100644
index 00000000000..b51a0e87fd8
--- /dev/null
+++ b/chromium/components/url_pattern_index/string_splitter.h
@@ -0,0 +1,101 @@
+// 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_URL_PATTERN_INDEX_STRING_SPLITTER_H_
+#define COMPONENTS_URL_PATTERN_INDEX_STRING_SPLITTER_H_
+
+#include <iterator>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+
+namespace url_pattern_index {
+
+// A zero-allocation string splitter. Splits a string into non-empty tokens
+// divided by separator characters as defined by the IsSeparator predicate.
+// However, instead of materializing and returning a collection of all tokens in
+// the string, it provides an InputIterator that can be used to extract the
+// tokens.
+//
+// TODO(pkalinnikov): Move it to "base/strings" after some generalization.
+template <typename IsSeparator>
+class StringSplitter {
+ public:
+ class Iterator
+ : public std::iterator<std::input_iterator_tag, base::StringPiece> {
+ public:
+ // Creates an iterator, which points to the leftmost token within the
+ // |splitter|'s |text|, starting from |head|.
+ Iterator(const StringSplitter& splitter,
+ base::StringPiece::const_iterator head)
+ : splitter_(&splitter), current_(head, 0), end_(splitter.text_.end()) {
+ DCHECK_GE(head, splitter_->text_.begin());
+ DCHECK_LE(head, end_);
+
+ Advance();
+ }
+
+ bool operator==(const Iterator& rhs) const {
+ return current_.begin() == rhs.current_.begin();
+ }
+
+ bool operator!=(const Iterator& rhs) const { return !operator==(rhs); }
+
+ base::StringPiece operator*() const { return current_; }
+ const base::StringPiece* operator->() const { return &current_; }
+
+ Iterator& operator++() {
+ Advance();
+ return *this;
+ }
+
+ Iterator operator++(int) {
+ Iterator copy(*this);
+ operator++();
+ return copy;
+ }
+
+ private:
+ void Advance() {
+ auto begin = current_.end();
+ while (begin != end_ && splitter_->is_separator_(*begin))
+ ++begin;
+ auto end = begin;
+ while (end != end_ && !splitter_->is_separator_(*end))
+ ++end;
+ current_ = base::StringPiece(begin, end - begin);
+ }
+
+ const StringSplitter* splitter_;
+
+ // Contains the token currently pointed to by the iterator.
+ base::StringPiece current_;
+ // Always points to the text_.end().
+ base::StringPiece::const_iterator end_;
+ };
+
+ // Constructs a splitter for iterating over non-empty tokens contained in the
+ // |text|. |is_separator| predicate is used to determine whether a certain
+ // character is a separator.
+ StringSplitter(base::StringPiece text,
+ IsSeparator is_separator = IsSeparator())
+ : text_(text), is_separator_(is_separator) {}
+
+ Iterator begin() const { return Iterator(*this, text_.begin()); }
+ Iterator end() const { return Iterator(*this, text_.end()); }
+
+ private:
+ base::StringPiece text_;
+ IsSeparator is_separator_;
+};
+
+template <typename IsSeparator>
+StringSplitter<IsSeparator> CreateStringSplitter(base::StringPiece text,
+ IsSeparator is_separator) {
+ return StringSplitter<IsSeparator>(text, is_separator);
+}
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_STRING_SPLITTER_H_
diff --git a/chromium/components/url_pattern_index/string_splitter_unittest.cc b/chromium/components/url_pattern_index/string_splitter_unittest.cc
new file mode 100644
index 00000000000..d2d2938c882
--- /dev/null
+++ b/chromium/components/url_pattern_index/string_splitter_unittest.cc
@@ -0,0 +1,82 @@
+// 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/url_pattern_index/string_splitter.h"
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace url_pattern_index {
+
+namespace {
+bool IsTestSeparator(char c) {
+ return c == ' ' || c == '\t' || c == ',';
+}
+}
+
+TEST(StringSplitterTest, SplitWithEmptyResult) {
+ const char* const kStrings[] = {
+ "", " ", "\t", ",", " \t ", ",,,,", "\t\t\t",
+ };
+
+ for (const char* string : kStrings) {
+ auto splitter = CreateStringSplitter(string, IsTestSeparator);
+ // Explicitly verify both operator== and operator!=.
+ EXPECT_TRUE(splitter.begin() == splitter.end());
+ EXPECT_FALSE(splitter.begin() != splitter.end());
+ }
+}
+
+TEST(StringSplitterTest, SplitOneWord) {
+ const char* const kLongStrings[] = {
+ "word", " word ", " word", "word ", ",word,",
+ "\tword\t", " word ", "word ", " word", ", word, \t",
+ };
+ const char* const kShortStrings[] = {
+ "w", " w ", " w", "w ", " w ", " w", "w ", ", w, ", "w, \t",
+ };
+
+ const char kLongWord[] = "word";
+ const char kShortWord[] = "w";
+
+ auto expect_word = [](const char* text, const char* word) {
+ auto splitter = CreateStringSplitter(text, IsTestSeparator);
+ // Explicitly verify both operator== and operator!=.
+ EXPECT_TRUE(splitter.begin() != splitter.end());
+ EXPECT_FALSE(splitter.begin() == splitter.end());
+
+ EXPECT_EQ(splitter.end(), ++splitter.begin());
+ EXPECT_EQ(word, *splitter.begin());
+
+ auto iterator = splitter.begin();
+ EXPECT_EQ(splitter.begin(), iterator++);
+ EXPECT_EQ(splitter.end(), iterator);
+ };
+
+ for (const char* string : kLongStrings)
+ expect_word(string, kLongWord);
+ for (const char* string : kShortStrings)
+ expect_word(string, kShortWord);
+}
+
+TEST(StringSplitterTest, SplitThreeWords) {
+ const char* const kStrings[] = {
+ "one two three", " one two three ", " one two, three",
+ "one,two\t\t three", "one, two, three, ",
+ };
+ const std::vector<base::StringPiece> kResults = {
+ "one", "two", "three",
+ };
+
+ for (const char* string : kStrings) {
+ auto splitter = CreateStringSplitter(string, IsTestSeparator);
+ std::vector<base::StringPiece> tokens(splitter.begin(), splitter.end());
+ EXPECT_EQ(kResults, tokens);
+ }
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/uint64_hasher.h b/chromium/components/url_pattern_index/uint64_hasher.h
new file mode 100644
index 00000000000..7fddbe649af
--- /dev/null
+++ b/chromium/components/url_pattern_index/uint64_hasher.h
@@ -0,0 +1,52 @@
+// 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.
+
+// Based on "v8/src/base/functional.cc".
+// See: Thomas Wang, Integer Hash Functions.
+// https://gist.github.com/badboy/6267743
+// TODO(pkalinnikov): Consider moving the implementation into base/.
+
+#ifndef COMPONENTS_URL_PATTERN_INDEX_HASH_H_
+#define COMPONENTS_URL_PATTERN_INDEX_HASH_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <type_traits>
+
+namespace url_pattern_index {
+
+template <typename T>
+typename std::enable_if<sizeof(T) == 4, T>::type Uint64Hash(uint64_t v) {
+ // "64 bit to 32 bit Hash Functions"
+ v = ~v + (v << 18); // v = (v << 18) - v - 1;
+ v = v ^ (v >> 31);
+ v = v * 21; // v = (v + (v << 2)) + (v << 4);
+ v = v ^ (v >> 11);
+ v = v + (v << 6);
+ v = v ^ (v >> 22);
+ return static_cast<T>(v);
+}
+
+template <typename T>
+typename std::enable_if<sizeof(T) == 8, T>::type Uint64Hash(uint64_t v) {
+ // "64 bit Mix Functions"
+ v = ~v + (v << 21); // v = (v << 21) - v - 1;
+ v = v ^ (v >> 24);
+ v = (v + (v << 3)) + (v << 8); // v * 265
+ v = v ^ (v >> 14);
+ v = (v + (v << 2)) + (v << 4); // v * 21
+ v = v ^ (v >> 28);
+ v = v + (v << 31);
+ return static_cast<T>(v);
+}
+
+class Uint64Hasher {
+ public:
+ size_t operator()(uint64_t v) const { return Uint64Hash<size_t>(v); }
+};
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_HASH_H_
diff --git a/chromium/components/url_pattern_index/unindexed_ruleset.cc b/chromium/components/url_pattern_index/unindexed_ruleset.cc
new file mode 100644
index 00000000000..d9f4c2df037
--- /dev/null
+++ b/chromium/components/url_pattern_index/unindexed_ruleset.cc
@@ -0,0 +1,71 @@
+// 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/url_pattern_index/unindexed_ruleset.h"
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace url_pattern_index {
+
+// UnindexedRulesetReader ------------------------------------------------------
+
+UnindexedRulesetReader::UnindexedRulesetReader(
+ google::protobuf::io::ZeroCopyInputStream* stream)
+ : coded_stream_(stream) {}
+
+UnindexedRulesetReader::~UnindexedRulesetReader() = default;
+
+bool UnindexedRulesetReader::ReadNextChunk(proto::FilteringRules* chunk) {
+ uint32_t chunk_size = 0;
+ if (!coded_stream_.ReadVarint32(&chunk_size))
+ return false;
+ auto limit = coded_stream_.PushLimit(chunk_size);
+ if (!chunk->ParseFromCodedStream(&coded_stream_))
+ return false;
+ coded_stream_.PopLimit(limit);
+ return true;
+}
+
+// UnindexedRulesetWriter ------------------------------------------------------
+
+UnindexedRulesetWriter::UnindexedRulesetWriter(
+ google::protobuf::io::ZeroCopyOutputStream* stream,
+ int max_rules_per_chunk)
+ : coded_stream_(stream), max_rules_per_chunk_(max_rules_per_chunk) {}
+
+UnindexedRulesetWriter::~UnindexedRulesetWriter() {
+ DCHECK_EQ(pending_chunk_.url_rules_size(), 0);
+ DCHECK_EQ(pending_chunk_.css_rules_size(), 0);
+}
+
+bool UnindexedRulesetWriter::AddUrlRule(const proto::UrlRule& rule) {
+ DCHECK(!had_error());
+ pending_chunk_.add_url_rules()->CopyFrom(rule);
+ if (pending_chunk_.url_rules_size() >= max_rules_per_chunk_) {
+ DCHECK_EQ(pending_chunk_.url_rules_size(), max_rules_per_chunk_);
+ return WritePendingChunk();
+ }
+ return true;
+}
+
+bool UnindexedRulesetWriter::Finish() {
+ DCHECK(!had_error());
+ const bool success = !pending_chunk_.url_rules_size() || WritePendingChunk();
+ if (success)
+ coded_stream_.Trim();
+ return success;
+}
+
+bool UnindexedRulesetWriter::WritePendingChunk() {
+ DCHECK(!had_error());
+ DCHECK_GT(pending_chunk_.url_rules_size(), 0);
+
+ proto::FilteringRules chunk;
+ chunk.Swap(&pending_chunk_);
+ coded_stream_.WriteVarint32(base::checked_cast<uint32_t>(chunk.ByteSize()));
+ return !had_error() && chunk.SerializeToCodedStream(&coded_stream_);
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/unindexed_ruleset.h b/chromium/components/url_pattern_index/unindexed_ruleset.h
new file mode 100644
index 00000000000..520f5a1efcc
--- /dev/null
+++ b/chromium/components/url_pattern_index/unindexed_ruleset.h
@@ -0,0 +1,100 @@
+// 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.
+
+// 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.
+//
+// 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.
+//
+// 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
+// before reading the next one. In practice, this should not be an issue as
+// indexing of the ruleset is expected to be performed in an on-line fashion.
+
+#ifndef COMPONENTS_URL_PATTERN_INDEX_UNINDEXED_RULESET_H_
+#define COMPONENTS_URL_PATTERN_INDEX_UNINDEXED_RULESET_H_
+
+#include "base/macros.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
+#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 {
+
+// Reads an unindexed ruleset from |stream| one chunk at a time.
+class UnindexedRulesetReader {
+ public:
+ // Note: The |stream| should outlive |this| instance.
+ explicit UnindexedRulesetReader(
+ google::protobuf::io::ZeroCopyInputStream* stream);
+ ~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);
+
+ // Returns how many bytes of the |stream| have been consumed.
+ int num_bytes_read() const { return coded_stream_.CurrentPosition(); }
+
+ private:
+ google::protobuf::io::CodedInputStream coded_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnindexedRulesetReader);
+};
+
+// Divides an unindexed ruleset into chunks and writes them into |stream|.
+//
+// Writing methods of this class return bool false if an I/O error occurrs
+// during these calls. In this case the UnindexedRulesetWriter becomes broken,
+// 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.
+ explicit UnindexedRulesetWriter(
+ google::protobuf::io::ZeroCopyOutputStream* stream,
+ int max_rules_per_chunk = 64);
+ ~UnindexedRulesetWriter();
+
+ int max_rules_per_chunk() const { return max_rules_per_chunk_; }
+
+ // Returns whether an I/O error occurred since this object was created.
+ bool had_error() const { return coded_stream_.HadError(); }
+
+ // 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);
+ // TODO(pkalinnikov): Implement AddCssRule when needed.
+
+ // Finalizes the serialization of the unindexed ruleset, i.e., writes the
+ // final chunk of rules, if there are any still pending. This method *should*
+ // be called exactly once when interaction with |this| instance ends, unless
+ // some of the writing operations returned false, which indicates an error.
+ // After calling Finish write operations should not be used any more.
+ bool Finish();
+
+ private:
+ // Writes the non-empty |pending_chunk_| to the |coded_stream_|. Always leaves
+ // the |panding_chunk_| empty, regardless of whether an error occurred.
+ bool WritePendingChunk();
+
+ google::protobuf::io::CodedOutputStream coded_stream_;
+
+ const int max_rules_per_chunk_ = 0;
+ proto::FilteringRules pending_chunk_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnindexedRulesetWriter);
+};
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_UNINDEXED_RULESET_H_
diff --git a/chromium/components/url_pattern_index/unindexed_ruleset_unittest.cc b/chromium/components/url_pattern_index/unindexed_ruleset_unittest.cc
new file mode 100644
index 00000000000..318855e013a
--- /dev/null
+++ b/chromium/components/url_pattern_index/unindexed_ruleset_unittest.cc
@@ -0,0 +1,181 @@
+// 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/url_pattern_index/unindexed_ruleset.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/url_pattern_index/url_pattern.h"
+#include "components/url_pattern_index/url_rule_test_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#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 {
+
+bool IsEqual(const proto::UrlRule& lhs, const proto::UrlRule& rhs) {
+ return lhs.SerializeAsString() == rhs.SerializeAsString();
+}
+
+// The helper class used for building UnindexedRulesets.
+class UnindexedRulesetTestBuilder {
+ public:
+ // Initializes the builder that writes the ruleset to StringOutputStream.
+ UnindexedRulesetTestBuilder()
+ : output_(
+ new google::protobuf::io::StringOutputStream(&ruleset_contents_)),
+ ruleset_writer_(output_.get()) {}
+
+ // Initializes the builder that writes the ruleset to an array of |array_size|
+ // through an ArrayOutputStream.
+ UnindexedRulesetTestBuilder(int array_size)
+ : ruleset_contents_(array_size, '\0'),
+ output_(
+ new google::protobuf::io::ArrayOutputStream(&ruleset_contents_[0],
+ array_size)),
+ ruleset_writer_(output_.get()) {}
+
+ int max_rules_per_chunk() const {
+ return ruleset_writer_.max_rules_per_chunk();
+ }
+
+ bool AddUrlRule(const UrlPattern& url_pattern,
+ proto::SourceType source_type,
+ bool is_whitelist = false) {
+ auto rule = testing::MakeUrlRule(url_pattern);
+ if (is_whitelist)
+ rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
+ rule.set_source_type(source_type);
+
+ url_rules_.push_back(rule);
+ return !ruleset_writer_.had_error() &&
+ ruleset_writer_.AddUrlRule(url_rules_.back());
+ }
+
+ bool AddUrlRules(int number_of_rules) {
+ for (int i = 0; i < number_of_rules; ++i) {
+ std::string url_pattern = "example" + base::IntToString(i) + ".com";
+ if (!AddUrlRule(UrlPattern(url_pattern), testing::kAnyParty, i & 1))
+ return false;
+ }
+ return true;
+ }
+
+ bool Finish() {
+ if (!ruleset_writer_.had_error() && ruleset_writer_.Finish()) {
+ // Note: This line has effect only when |output_| is an ArrayOutputStream.
+ ruleset_contents_.resize(output_->ByteCount());
+ return true;
+ }
+ return false;
+ }
+
+ const std::vector<proto::UrlRule>& url_rules() const { return url_rules_; }
+ const std::string& ruleset_contents() const { return ruleset_contents_; }
+
+ private:
+ std::vector<proto::UrlRule> url_rules_;
+ std::string ruleset_contents_;
+ std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> output_;
+ UnindexedRulesetWriter ruleset_writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnindexedRulesetTestBuilder);
+};
+
+bool IsRulesetValid(const std::string& ruleset_contents,
+ const std::vector<proto::UrlRule>& expected_url_rules) {
+ google::protobuf::io::ArrayInputStream array_input(ruleset_contents.data(),
+ ruleset_contents.size());
+ UnindexedRulesetReader reader(&array_input);
+ proto::FilteringRules chunk;
+ std::vector<proto::UrlRule> read_rules;
+ while (reader.ReadNextChunk(&chunk)) {
+ read_rules.insert(read_rules.end(), chunk.url_rules().begin(),
+ chunk.url_rules().end());
+ }
+ if (base::checked_cast<size_t>(reader.num_bytes_read()) !=
+ ruleset_contents.size()) {
+ return false;
+ }
+
+ if (expected_url_rules.size() != read_rules.size())
+ return false;
+ for (size_t i = 0, size = read_rules.size(); i != size; ++i) {
+ if (!IsEqual(expected_url_rules[i], read_rules[i]))
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+TEST(UnindexedRulesetTest, EmptyRuleset) {
+ UnindexedRulesetTestBuilder builder;
+ EXPECT_TRUE(builder.Finish());
+ EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
+}
+
+TEST(UnindexedRulesetTest, OneUrlRule) {
+ UnindexedRulesetTestBuilder builder;
+ EXPECT_TRUE(
+ builder.AddUrlRule(UrlPattern("example.com"), testing::kThirdParty));
+ EXPECT_TRUE(builder.Finish());
+ EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
+}
+
+TEST(UnindexedRulesetTest, ManyUrlRules) {
+ UnindexedRulesetTestBuilder builder;
+ EXPECT_TRUE(builder.AddUrlRules(1234));
+ EXPECT_TRUE(builder.Finish());
+ EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
+}
+
+TEST(UnindexedRulesetTest, ExactlyMaxRulesPerChunk) {
+ UnindexedRulesetTestBuilder builder;
+ EXPECT_TRUE(builder.AddUrlRules(builder.max_rules_per_chunk()));
+ EXPECT_TRUE(builder.Finish());
+ EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
+}
+
+TEST(UnindexedRulesetTest, MaxRulesPerChunkPlusOne) {
+ UnindexedRulesetTestBuilder builder;
+ EXPECT_TRUE(builder.AddUrlRules(builder.max_rules_per_chunk() + 1));
+ EXPECT_TRUE(builder.Finish());
+ EXPECT_TRUE(IsRulesetValid(builder.ruleset_contents(), builder.url_rules()));
+}
+
+TEST(UnindexedRulesetTest, ErrorOnWrite) {
+ UnindexedRulesetTestBuilder builder(1000);
+ EXPECT_FALSE(builder.AddUrlRules(1234));
+}
+
+TEST(UnindexedRulesetTest, ReadCorruptedInput) {
+ UnindexedRulesetTestBuilder builder;
+ EXPECT_TRUE(builder.AddUrlRules(1000));
+ EXPECT_TRUE(builder.Finish());
+
+ {
+ std::string ruleset_contents = builder.ruleset_contents();
+ ASSERT_GE(ruleset_contents.size(), static_cast<size_t>(2000));
+ ruleset_contents[100] ^= 239;
+ ruleset_contents[1000] ^= 3;
+ EXPECT_FALSE(IsRulesetValid(ruleset_contents, builder.url_rules()));
+ }
+
+ {
+ std::string ruleset_contents = builder.ruleset_contents();
+ ASSERT_GT(ruleset_contents.size(), static_cast<size_t>(100));
+ ruleset_contents.resize(100);
+ EXPECT_FALSE(IsRulesetValid(ruleset_contents, builder.url_rules()));
+ }
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/url_pattern.cc b/chromium/components/url_pattern_index/url_pattern.cc
new file mode 100644
index 00000000000..83584d4fd2e
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_pattern.cc
@@ -0,0 +1,256 @@
+// 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.
+
+// The matching logic distinguishes between the terms URL pattern and
+// subpattern. A URL pattern usually stands for the full thing, e.g.
+// "example.com^*path*par=val^", whereas subpattern denotes a maximal substring
+// of a pattern not containing the wildcard '*' character. For the example above
+// the subpatterns are: "example.com^", "path" and "par=val^".
+//
+// The separator placeholder '^' symbol is used in subpatterns to match any
+// separator character, which is any ASCII symbol except letters, digits, and
+// the following: '_', '-', '.', '%'. Note that the separator placeholder
+// character '^' is itself a separator, as well as '\0'.
+
+#include "components/url_pattern_index/url_pattern.h"
+
+#include <stddef.h>
+
+#include <ostream>
+
+#include "base/logging.h"
+#include "components/url_pattern_index/flat/url_pattern_index_generated.h"
+#include "components/url_pattern_index/fuzzy_pattern_matching.h"
+#include "components/url_pattern_index/string_splitter.h"
+#include "url/gurl.h"
+#include "url/third_party/mozilla/url_parse.h"
+
+namespace url_pattern_index {
+
+namespace {
+
+class IsWildcard {
+ public:
+ bool operator()(char c) const { return c == '*'; }
+};
+
+proto::UrlPatternType ConvertUrlPatternType(flat::UrlPatternType type) {
+ switch (type) {
+ case flat::UrlPatternType_SUBSTRING:
+ return proto::URL_PATTERN_TYPE_SUBSTRING;
+ case flat::UrlPatternType_WILDCARDED:
+ return proto::URL_PATTERN_TYPE_WILDCARDED;
+ case flat::UrlPatternType_REGEXP:
+ return proto::URL_PATTERN_TYPE_REGEXP;
+ default:
+ return proto::URL_PATTERN_TYPE_UNSPECIFIED;
+ }
+}
+
+proto::AnchorType ConvertAnchorType(flat::AnchorType type) {
+ switch (type) {
+ case flat::AnchorType_NONE:
+ return proto::ANCHOR_TYPE_NONE;
+ case flat::AnchorType_BOUNDARY:
+ return proto::ANCHOR_TYPE_BOUNDARY;
+ case flat::AnchorType_SUBDOMAIN:
+ return proto::ANCHOR_TYPE_SUBDOMAIN;
+ default:
+ return proto::ANCHOR_TYPE_UNSPECIFIED;
+ }
+}
+
+base::StringPiece ConvertString(const flatbuffers::String* string) {
+ return string ? base::StringPiece(string->data(), string->size())
+ : base::StringPiece();
+}
+
+// Returns whether |position| within the |url| belongs to its |host| component
+// and corresponds to the beginning of a (sub-)domain.
+inline bool IsSubdomainAnchored(base::StringPiece url,
+ url::Component host,
+ size_t position) {
+ DCHECK_LE(position, url.size());
+ const size_t host_begin = static_cast<size_t>(host.begin);
+ const size_t host_end = static_cast<size_t>(host.end());
+ DCHECK_LE(host_end, url.size());
+
+ return position == host_begin ||
+ (position > host_begin && position <= host_end &&
+ url[position - 1] == '.');
+}
+
+// Returns the position of the leftmost occurrence of a |subpattern| in the
+// |text| starting no earlier than |from| the specified position. If the
+// |subpattern| has separator placeholders, searches for a fuzzy occurrence.
+size_t FindSubpattern(base::StringPiece text,
+ base::StringPiece subpattern,
+ size_t from = 0) {
+ const bool is_fuzzy =
+ (subpattern.find(kSeparatorPlaceholder) != base::StringPiece::npos);
+ return is_fuzzy ? FindFuzzy(text, subpattern, from)
+ : text.find(subpattern, from);
+}
+
+// Same as FindSubpattern(url, subpattern), but searches for an occurrence that
+// starts at the beginning of a (sub-)domain within the url's |host| component.
+size_t FindSubdomainAnchoredSubpattern(base::StringPiece url,
+ url::Component host,
+ base::StringPiece subpattern) {
+ const bool is_fuzzy =
+ (subpattern.find(kSeparatorPlaceholder) != base::StringPiece::npos);
+
+ for (size_t position = 0; position <= url.size(); ++position) {
+ position = is_fuzzy ? FindFuzzy(url, subpattern, position)
+ : url.find(subpattern, position);
+ if (position == base::StringPiece::npos ||
+ IsSubdomainAnchored(url, host, position)) {
+ return position;
+ }
+ }
+ return base::StringPiece::npos;
+}
+
+} // namespace
+
+UrlPattern::UrlPattern() = default;
+
+UrlPattern::UrlPattern(base::StringPiece url_pattern,
+ proto::UrlPatternType type)
+ : type_(type), url_pattern_(url_pattern) {}
+
+UrlPattern::UrlPattern(base::StringPiece url_pattern,
+ proto::AnchorType anchor_left,
+ proto::AnchorType anchor_right)
+ : type_(proto::URL_PATTERN_TYPE_WILDCARDED),
+ url_pattern_(url_pattern),
+ anchor_left_(anchor_left),
+ anchor_right_(anchor_right) {}
+
+UrlPattern::UrlPattern(const flat::UrlRule& rule)
+ : type_(ConvertUrlPatternType(rule.url_pattern_type())),
+ url_pattern_(ConvertString(rule.url_pattern())),
+ anchor_left_(ConvertAnchorType(rule.anchor_left())),
+ anchor_right_(ConvertAnchorType(rule.anchor_right())),
+ match_case_(!!(rule.options() & flat::OptionFlag_IS_MATCH_CASE)) {}
+
+UrlPattern::UrlPattern(const proto::UrlRule& rule)
+ : type_(rule.url_pattern_type()),
+ url_pattern_(rule.url_pattern()),
+ anchor_left_(rule.anchor_left()),
+ anchor_right_(rule.anchor_right()),
+ match_case_(rule.match_case()) {}
+
+UrlPattern::~UrlPattern() = default;
+
+bool UrlPattern::MatchesUrl(const GURL& url) const {
+ // Note: Empty domains are also invalid.
+ DCHECK(url.is_valid());
+ DCHECK(type_ == proto::URL_PATTERN_TYPE_SUBSTRING ||
+ type_ == proto::URL_PATTERN_TYPE_WILDCARDED);
+
+ StringSplitter<IsWildcard> subpatterns(url_pattern_);
+ auto subpattern_it = subpatterns.begin();
+ auto subpattern_end = subpatterns.end();
+
+ if (subpattern_it == subpattern_end) {
+ return anchor_left_ == proto::ANCHOR_TYPE_NONE ||
+ anchor_right_ == proto::ANCHOR_TYPE_NONE;
+ }
+
+ const base::StringPiece spec = url.possibly_invalid_spec();
+ const url::Component host_part = url.parsed_for_possibly_invalid_spec().host;
+ DCHECK(!spec.empty());
+
+ base::StringPiece subpattern = *subpattern_it;
+ ++subpattern_it;
+
+ // If there is only one |subpattern|, and it has a right anchor, then simply
+ // check that it is a suffix of the |spec|, and the left anchor is fulfilled.
+ if (subpattern_it == subpattern_end &&
+ anchor_right_ == proto::ANCHOR_TYPE_BOUNDARY) {
+ if (!EndsWithFuzzy(spec, subpattern))
+ return false;
+ if (anchor_left_ == proto::ANCHOR_TYPE_BOUNDARY)
+ return spec.size() == subpattern.size();
+ if (anchor_left_ == proto::ANCHOR_TYPE_SUBDOMAIN) {
+ DCHECK_LE(subpattern.size(), spec.size());
+ return url.has_host() &&
+ IsSubdomainAnchored(spec, host_part,
+ spec.size() - subpattern.size());
+ }
+ return true;
+ }
+
+ // Otherwise, the first |subpattern| does not have to be a suffix. But it
+ // still can have a left anchor. Check and handle that.
+ base::StringPiece text = spec;
+ if (anchor_left_ == proto::ANCHOR_TYPE_BOUNDARY) {
+ if (!StartsWithFuzzy(spec, subpattern))
+ return false;
+ if (subpattern_it == subpattern_end) {
+ DCHECK_EQ(anchor_right_, proto::ANCHOR_TYPE_NONE);
+ return true;
+ }
+ text.remove_prefix(subpattern.size());
+ } else if (anchor_left_ == proto::ANCHOR_TYPE_SUBDOMAIN) {
+ if (!url.has_host())
+ return false;
+ const size_t match_begin =
+ FindSubdomainAnchoredSubpattern(spec, host_part, subpattern);
+ if (match_begin == base::StringPiece::npos)
+ return false;
+ if (subpattern_it == subpattern_end) {
+ DCHECK_EQ(anchor_right_, proto::ANCHOR_TYPE_NONE);
+ return true;
+ }
+ text.remove_prefix(match_begin + subpattern.size());
+ } else {
+ DCHECK_EQ(anchor_left_, proto::ANCHOR_TYPE_NONE);
+ // Get back to the initial |subpattern|, process it in the loop below.
+ subpattern_it = subpatterns.begin();
+ }
+
+ // Consecutively find all the remaining subpatterns in the |text|. If the
+ // pattern has a right anchor, don't search for the last subpattern, but
+ // instead check that it is a suffix of the |text|.
+ while (subpattern_it != subpattern_end) {
+ subpattern = *subpattern_it;
+ DCHECK(!subpattern.empty());
+
+ if (++subpattern_it == subpattern_end &&
+ anchor_right_ == proto::ANCHOR_TYPE_BOUNDARY) {
+ break;
+ }
+
+ const size_t match_position = FindSubpattern(text, subpattern);
+ if (match_position == base::StringPiece::npos)
+ return false;
+ text.remove_prefix(match_position + subpattern.size());
+ }
+
+ return anchor_right_ != proto::ANCHOR_TYPE_BOUNDARY ||
+ EndsWithFuzzy(text, subpattern);
+}
+
+std::ostream& operator<<(std::ostream& out, const UrlPattern& pattern) {
+ // Note: Each fall-through in this switch is intentional.
+ switch (pattern.anchor_left()) {
+ case proto::ANCHOR_TYPE_SUBDOMAIN:
+ out << '|';
+ case proto::ANCHOR_TYPE_BOUNDARY:
+ out << '|';
+ default:
+ break;
+ }
+ out << pattern.url_pattern();
+ if (pattern.anchor_right() == proto::ANCHOR_TYPE_BOUNDARY)
+ out << '|';
+ if (pattern.match_case())
+ out << "$match-case";
+
+ return out;
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/url_pattern.h b/chromium/components/url_pattern_index/url_pattern.h
new file mode 100644
index 00000000000..0994803ac63
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_pattern.h
@@ -0,0 +1,79 @@
+// 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_URL_PATTERN_INDEX_URL_PATTERN_H_
+#define COMPONENTS_URL_PATTERN_INDEX_URL_PATTERN_H_
+
+#include <iosfwd>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
+
+class GURL;
+
+namespace url_pattern_index {
+
+namespace flat {
+struct UrlRule; // The FlatBuffers version of UrlRule.
+}
+
+// The structure used to mirror a URL pattern regardless of the representation
+// of the UrlRule that owns it, and to match it against URLs.
+class UrlPattern {
+ public:
+ UrlPattern();
+
+ // Creates a |url_pattern| of a certain |type|.
+ UrlPattern(base::StringPiece url_pattern,
+ proto::UrlPatternType type = proto::URL_PATTERN_TYPE_WILDCARDED);
+
+ // Creates a WILDCARDED |url_pattern| with the specified anchors.
+ UrlPattern(base::StringPiece url_pattern,
+ proto::AnchorType anchor_left,
+ proto::AnchorType anchor_right);
+
+ // The following constructors create UrlPattern from one of the UrlRule
+ // representations. The passed in |rule| must outlive the created instance.
+ explicit UrlPattern(const flat::UrlRule& rule);
+ explicit UrlPattern(const proto::UrlRule& rule);
+
+ ~UrlPattern();
+
+ proto::UrlPatternType type() const { return type_; }
+ base::StringPiece url_pattern() const { return url_pattern_; }
+ proto::AnchorType anchor_left() const { return anchor_left_; }
+ proto::AnchorType anchor_right() const { return anchor_right_; }
+ bool match_case() const { return match_case_; }
+
+ // Returns whether the |url| matches the URL |pattern|. Requires the type of
+ // |this| pattern to be either SUBSTRING or WILDCARDED.
+ //
+ // Splits the pattern into subpatterns separated by '*' wildcards, and
+ // greedily finds each of them in the spec of the |url|. Respects anchors at
+ // either end of the pattern, and '^' separator placeholders when comparing a
+ // subpattern to a subtring of the spec.
+ bool MatchesUrl(const GURL& url) const;
+
+ private:
+ // TODO(pkalinnikov): Store flat:: types instead of proto::, in order to avoid
+ // conversions in IndexedRuleset.
+ proto::UrlPatternType type_ = proto::URL_PATTERN_TYPE_UNSPECIFIED;
+ base::StringPiece url_pattern_;
+
+ proto::AnchorType anchor_left_ = proto::ANCHOR_TYPE_NONE;
+ proto::AnchorType anchor_right_ = proto::ANCHOR_TYPE_NONE;
+
+ // TODO(pkalinnikov): Implement case-insensitive matching.
+ bool match_case_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlPattern);
+};
+
+// Allow pretty-printing URLPatterns when they are used in GTest assertions.
+std::ostream& operator<<(std::ostream& out, const UrlPattern& pattern);
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_URL_PATTERN_H_
diff --git a/chromium/components/url_pattern_index/url_pattern_index.cc b/chromium/components/url_pattern_index/url_pattern_index.cc
new file mode 100644
index 00000000000..d868afdd1a5
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_pattern_index.cc
@@ -0,0 +1,667 @@
+// 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/url_pattern_index/url_pattern_index.h"
+
+#include <algorithm>
+#include <limits>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "components/url_pattern_index/ngram_extractor.h"
+#include "components/url_pattern_index/url_pattern.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace url_pattern_index {
+
+namespace {
+
+using FlatStringOffset = flatbuffers::Offset<flatbuffers::String>;
+using FlatDomains = flatbuffers::Vector<FlatStringOffset>;
+using FlatDomainsOffset = flatbuffers::Offset<FlatDomains>;
+
+using ActivationTypeMap =
+ base::flat_map<proto::ActivationType, flat::ActivationType>;
+using ElementTypeMap = base::flat_map<proto::ElementType, flat::ElementType>;
+
+// Maps proto::ActivationType to flat::ActivationType.
+const ActivationTypeMap& GetActivationTypeMap() {
+ CR_DEFINE_STATIC_LOCAL(
+ ActivationTypeMap, activation_type_map,
+ (
+ {
+ {proto::ACTIVATION_TYPE_UNSPECIFIED, flat::ActivationType_NONE},
+ {proto::ACTIVATION_TYPE_DOCUMENT, flat::ActivationType_DOCUMENT},
+ // ELEMHIDE is not supported.
+ {proto::ACTIVATION_TYPE_ELEMHIDE, flat::ActivationType_NONE},
+ // GENERICHIDE is not supported.
+ {proto::ACTIVATION_TYPE_GENERICHIDE, flat::ActivationType_NONE},
+ {proto::ACTIVATION_TYPE_GENERICBLOCK,
+ flat::ActivationType_GENERIC_BLOCK},
+ },
+ base::KEEP_FIRST_OF_DUPES));
+ return activation_type_map;
+}
+
+// Maps proto::ElementType to flat::ElementType.
+const ElementTypeMap& GetElementTypeMap() {
+ CR_DEFINE_STATIC_LOCAL(
+ ElementTypeMap, element_type_map,
+ (
+ {
+ {proto::ELEMENT_TYPE_UNSPECIFIED, flat::ElementType_NONE},
+ {proto::ELEMENT_TYPE_OTHER, flat::ElementType_OTHER},
+ {proto::ELEMENT_TYPE_SCRIPT, flat::ElementType_SCRIPT},
+ {proto::ELEMENT_TYPE_IMAGE, flat::ElementType_IMAGE},
+ {proto::ELEMENT_TYPE_STYLESHEET, flat::ElementType_STYLESHEET},
+ {proto::ELEMENT_TYPE_OBJECT, flat::ElementType_OBJECT},
+ {proto::ELEMENT_TYPE_XMLHTTPREQUEST,
+ flat::ElementType_XMLHTTPREQUEST},
+ {proto::ELEMENT_TYPE_OBJECT_SUBREQUEST,
+ flat::ElementType_OBJECT_SUBREQUEST},
+ {proto::ELEMENT_TYPE_SUBDOCUMENT, flat::ElementType_SUBDOCUMENT},
+ {proto::ELEMENT_TYPE_PING, flat::ElementType_PING},
+ {proto::ELEMENT_TYPE_MEDIA, flat::ElementType_MEDIA},
+ {proto::ELEMENT_TYPE_FONT, flat::ElementType_FONT},
+ // Filtering popups is not supported.
+ {proto::ELEMENT_TYPE_POPUP, flat::ElementType_NONE},
+ {proto::ELEMENT_TYPE_WEBSOCKET, flat::ElementType_WEBSOCKET},
+ },
+ base::KEEP_FIRST_OF_DUPES));
+ return element_type_map;
+}
+
+flat::ActivationType ProtoToFlatActivationType(proto::ActivationType type) {
+ const auto it = GetActivationTypeMap().find(type);
+ DCHECK(it != GetActivationTypeMap().end());
+ return it->second;
+}
+
+flat::ElementType ProtoToFlatElementType(proto::ElementType type) {
+ const auto it = GetElementTypeMap().find(type);
+ DCHECK(it != GetElementTypeMap().end());
+ return it->second;
+}
+
+base::StringPiece ToStringPiece(const flatbuffers::String* string) {
+ DCHECK(string);
+ return base::StringPiece(string->c_str(), string->size());
+}
+
+// Performs three-way comparison between two domains. In the total order defined
+// by this predicate, the lengths of domains will be monotonically decreasing.
+int CompareDomains(base::StringPiece lhs_domain, base::StringPiece rhs_domain) {
+ if (lhs_domain.size() != rhs_domain.size())
+ return lhs_domain.size() > rhs_domain.size() ? -1 : 1;
+ return lhs_domain.compare(rhs_domain);
+}
+
+bool HasNoUpperAscii(base::StringPiece string) {
+ return std::none_of(string.begin(), string.end(),
+ [](char c) { return base::IsAsciiUpper(c); });
+}
+
+// Returns a bitmask of all the keys of the |map| passed.
+template <typename T>
+int GetKeysMask(const T& map) {
+ int mask = 0;
+ for (const auto& pair : map)
+ mask |= pair.first;
+ return mask;
+}
+
+// Checks whether a URL |rule| can be converted to its FlatBuffers equivalent,
+// and performs the actual conversion.
+class UrlRuleFlatBufferConverter {
+ public:
+ // Creates the converter, and initializes |is_convertible| bit. If
+ // |is_convertible| == true, then all the fields, needed for serializing the
+ // |rule| to FlatBuffer, are initialized (|options|, |anchor_right|, etc.).
+ explicit UrlRuleFlatBufferConverter(const proto::UrlRule& rule)
+ : rule_(rule) {
+ is_convertible_ = InitializeOptions() && InitializeElementTypes() &&
+ InitializeActivationTypes() && InitializeUrlPattern() &&
+ IsMeaningful();
+ }
+
+ // Returns whether the |rule| can be converted to its FlatBuffers equivalent.
+ // The conversion is not possible if the rule has attributes not supported by
+ // this client version.
+ bool is_convertible() const { return is_convertible_; }
+
+ // Writes the URL |rule| to the FlatBuffer using the |builder|, and returns
+ // the offset to the serialized rule.
+ UrlRuleOffset SerializeConvertedRule(
+ flatbuffers::FlatBufferBuilder* builder) const {
+ DCHECK(is_convertible());
+
+ FlatDomainsOffset domains_included_offset;
+ FlatDomainsOffset domains_excluded_offset;
+ if (rule_.domains_size()) {
+ // TODO(pkalinnikov): Consider sharing the vectors between rules.
+ std::vector<FlatStringOffset> domains_included;
+ std::vector<FlatStringOffset> domains_excluded;
+ // Reserve only for |domains_included| because it is expected to be the
+ // one used more frequently.
+ domains_included.reserve(rule_.domains_size());
+
+ for (const auto& domain_list_item : rule_.domains()) {
+ // Note: The |domain| can have non-ASCII UTF-8 characters, but
+ // ToLowerASCII leaves these intact.
+ // TODO(pkalinnikov): Convert non-ASCII characters to lower case too.
+ // TODO(pkalinnikov): Possibly convert Punycode to IDN here or directly
+ // assume this is done in the proto::UrlRule.
+ const std::string& domain = domain_list_item.domain();
+ auto offset = builder->CreateSharedString(
+ HasNoUpperAscii(domain) ? domain : base::ToLowerASCII(domain));
+
+ if (domain_list_item.exclude())
+ domains_excluded.push_back(offset);
+ else
+ domains_included.push_back(offset);
+ }
+
+ // The comparator ensuring the domains order necessary for fast matching.
+ auto precedes = [&builder](FlatStringOffset lhs, FlatStringOffset rhs) {
+ return CompareDomains(ToStringPiece(flatbuffers::GetTemporaryPointer(
+ *builder, lhs)),
+ ToStringPiece(flatbuffers::GetTemporaryPointer(
+ *builder, rhs))) < 0;
+ };
+
+ // The domains are stored in sorted order to support fast matching.
+ if (!domains_included.empty()) {
+ // TODO(pkalinnikov): Don't sort if it is already sorted offline.
+ std::sort(domains_included.begin(), domains_included.end(), precedes);
+ domains_included_offset = builder->CreateVector(domains_included);
+ }
+ if (!domains_excluded.empty()) {
+ std::sort(domains_excluded.begin(), domains_excluded.end(), precedes);
+ domains_excluded_offset = builder->CreateVector(domains_excluded);
+ }
+ }
+
+ auto url_pattern_offset = builder->CreateString(rule_.url_pattern());
+
+ return flat::CreateUrlRule(
+ *builder, options_, element_types_, activation_types_,
+ url_pattern_type_, anchor_left_, anchor_right_, domains_included_offset,
+ domains_excluded_offset, url_pattern_offset);
+ }
+
+ private:
+ static bool ConvertAnchorType(proto::AnchorType anchor_type,
+ flat::AnchorType* result) {
+ switch (anchor_type) {
+ case proto::ANCHOR_TYPE_NONE:
+ *result = flat::AnchorType_NONE;
+ break;
+ case proto::ANCHOR_TYPE_BOUNDARY:
+ *result = flat::AnchorType_BOUNDARY;
+ break;
+ case proto::ANCHOR_TYPE_SUBDOMAIN:
+ *result = flat::AnchorType_SUBDOMAIN;
+ break;
+ default:
+ return false; // Unsupported anchor type.
+ }
+ return true;
+ }
+
+ bool InitializeOptions() {
+ static_assert(flat::OptionFlag_ANY <= std::numeric_limits<uint8_t>::max(),
+ "Option flags can not be stored in uint8_t.");
+
+ if (rule_.semantics() == proto::RULE_SEMANTICS_WHITELIST) {
+ options_ |= flat::OptionFlag_IS_WHITELIST;
+ } else if (rule_.semantics() != proto::RULE_SEMANTICS_BLACKLIST) {
+ return false; // Unsupported semantics.
+ }
+
+ switch (rule_.source_type()) {
+ case proto::SOURCE_TYPE_ANY:
+ options_ |= flat::OptionFlag_APPLIES_TO_THIRD_PARTY;
+ // Note: fall through here intentionally.
+ case proto::SOURCE_TYPE_FIRST_PARTY:
+ options_ |= flat::OptionFlag_APPLIES_TO_FIRST_PARTY;
+ break;
+ case proto::SOURCE_TYPE_THIRD_PARTY:
+ options_ |= flat::OptionFlag_APPLIES_TO_THIRD_PARTY;
+ break;
+
+ default:
+ return false; // Unsupported source type.
+ }
+
+ if (rule_.match_case())
+ options_ |= flat::OptionFlag_IS_MATCH_CASE;
+
+ return true;
+ }
+
+ bool InitializeElementTypes() {
+ static_assert(flat::ElementType_ANY <= std::numeric_limits<uint16_t>::max(),
+ "Element types can not be stored in uint16_t.");
+
+ const ElementTypeMap& element_type_map = GetElementTypeMap();
+ // Ensure all proto::ElementType(s) are mapped in |element_type_map|.
+ DCHECK_EQ(proto::ELEMENT_TYPE_ALL, GetKeysMask(element_type_map));
+
+ element_types_ = flat::ElementType_NONE;
+
+ for (const auto& pair : element_type_map)
+ if (rule_.element_types() & pair.first)
+ element_types_ |= pair.second;
+
+ // Normally we can not distinguish between the main plugin resource and any
+ // other loads it makes. We treat them both as OBJECT requests. Hence an
+ // OBJECT request would also match OBJECT_SUBREQUEST rules, but not the
+ // the other way round.
+ if (element_types_ & flat::ElementType_OBJECT_SUBREQUEST)
+ element_types_ |= flat::ElementType_OBJECT;
+
+ return true;
+ }
+
+ bool InitializeActivationTypes() {
+ static_assert(
+ flat::ActivationType_ANY <= std::numeric_limits<uint8_t>::max(),
+ "Activation types can not be stored in uint8_t.");
+
+ const ActivationTypeMap& activation_type_map = GetActivationTypeMap();
+ // Ensure all proto::ActivationType(s) are mapped in |activation_type_map|.
+ DCHECK_EQ(proto::ACTIVATION_TYPE_ALL, GetKeysMask(activation_type_map));
+
+ activation_types_ = flat::ActivationType_NONE;
+
+ for (const auto& pair : activation_type_map)
+ if (rule_.activation_types() & pair.first)
+ activation_types_ |= pair.second;
+
+ return true;
+ }
+
+ bool InitializeUrlPattern() {
+ switch (rule_.url_pattern_type()) {
+ case proto::URL_PATTERN_TYPE_SUBSTRING:
+ url_pattern_type_ = flat::UrlPatternType_SUBSTRING;
+ break;
+ case proto::URL_PATTERN_TYPE_WILDCARDED:
+ url_pattern_type_ = flat::UrlPatternType_WILDCARDED;
+ break;
+
+ // TODO(pkalinnikov): Implement REGEXP rules matching.
+ case proto::URL_PATTERN_TYPE_REGEXP:
+ default:
+ return false; // Unsupported URL pattern type.
+ }
+
+ if (!ConvertAnchorType(rule_.anchor_left(), &anchor_left_) ||
+ !ConvertAnchorType(rule_.anchor_right(), &anchor_right_)) {
+ return false;
+ }
+ if (anchor_right_ == flat::AnchorType_SUBDOMAIN)
+ return false; // Unsupported right anchor.
+
+ return true;
+ }
+
+ // Returns whether the rule is not a no-op after all the modifications above.
+ bool IsMeaningful() const { return element_types_ || activation_types_; }
+
+ const proto::UrlRule& rule_;
+
+ uint8_t options_ = 0;
+ uint16_t element_types_ = 0;
+ uint8_t activation_types_ = 0;
+ flat::UrlPatternType url_pattern_type_ = flat::UrlPatternType_WILDCARDED;
+ flat::AnchorType anchor_left_ = flat::AnchorType_NONE;
+ flat::AnchorType anchor_right_ = flat::AnchorType_NONE;
+
+ bool is_convertible_ = true;
+};
+
+} // namespace
+
+// Helpers. --------------------------------------------------------------------
+
+UrlRuleOffset SerializeUrlRule(const proto::UrlRule& rule,
+ flatbuffers::FlatBufferBuilder* builder) {
+ DCHECK(builder);
+ UrlRuleFlatBufferConverter converter(rule);
+ if (!converter.is_convertible())
+ return UrlRuleOffset();
+ DCHECK_NE(rule.url_pattern_type(), proto::URL_PATTERN_TYPE_REGEXP);
+ return converter.SerializeConvertedRule(builder);
+}
+
+// UrlPatternIndexBuilder ------------------------------------------------------
+
+UrlPatternIndexBuilder::UrlPatternIndexBuilder(
+ flatbuffers::FlatBufferBuilder* flat_builder)
+ : flat_builder_(flat_builder) {
+ DCHECK(flat_builder_);
+}
+
+UrlPatternIndexBuilder::~UrlPatternIndexBuilder() = default;
+
+void UrlPatternIndexBuilder::IndexUrlRule(UrlRuleOffset offset) {
+ DCHECK(offset.o);
+
+ const auto* rule = flatbuffers::GetTemporaryPointer(*flat_builder_, offset);
+ DCHECK(rule);
+ NGram ngram = GetMostDistinctiveNGram(ToStringPiece(rule->url_pattern()));
+
+ if (ngram) {
+ ngram_index_[ngram].push_back(offset);
+ } else {
+ // TODO(pkalinnikov): Index fallback rules as well.
+ fallback_rules_.push_back(offset);
+ }
+}
+
+UrlPatternIndexOffset UrlPatternIndexBuilder::Finish() {
+ std::vector<flatbuffers::Offset<flat::NGramToRules>> flat_hash_table(
+ ngram_index_.table_size());
+
+ flatbuffers::Offset<flat::NGramToRules> empty_slot_offset =
+ flat::CreateNGramToRules(*flat_builder_);
+ for (size_t i = 0, size = ngram_index_.table_size(); i != size; ++i) {
+ const uint32_t entry_index = ngram_index_.hash_table()[i];
+ if (entry_index >= ngram_index_.size()) {
+ flat_hash_table[i] = empty_slot_offset;
+ continue;
+ }
+ const MutableNGramIndex::EntryType& entry =
+ ngram_index_.entries()[entry_index];
+ auto rules_offset = flat_builder_->CreateVector(entry.second);
+ flat_hash_table[i] =
+ flat::CreateNGramToRules(*flat_builder_, entry.first, rules_offset);
+ }
+ auto ngram_index_offset = flat_builder_->CreateVector(flat_hash_table);
+
+ auto fallback_rules_offset = flat_builder_->CreateVector(fallback_rules_);
+
+ return flat::CreateUrlPatternIndex(*flat_builder_, kNGramSize,
+ ngram_index_offset, empty_slot_offset,
+ fallback_rules_offset);
+}
+
+NGram UrlPatternIndexBuilder::GetMostDistinctiveNGram(
+ base::StringPiece pattern) {
+ size_t min_list_size = std::numeric_limits<size_t>::max();
+ NGram best_ngram = 0;
+
+ auto ngrams = CreateNGramExtractor<kNGramSize, NGram>(
+ pattern, [](char c) { return c == '*' || c == '^'; });
+
+ for (uint64_t ngram : ngrams) {
+ const MutableUrlRuleList* rules = ngram_index_.Get(ngram);
+ const size_t list_size = rules ? rules->size() : 0;
+ if (list_size < min_list_size) {
+ // TODO(pkalinnikov): Pick random of the same-sized lists.
+ min_list_size = list_size;
+ best_ngram = ngram;
+ if (list_size == 0)
+ break;
+ }
+ }
+
+ return best_ngram;
+}
+
+// UrlPatternIndex -------------------------------------------------------------
+
+namespace {
+
+using FlatUrlRuleList = flatbuffers::Vector<flatbuffers::Offset<flat::UrlRule>>;
+using FlatNGramIndex =
+ flatbuffers::Vector<flatbuffers::Offset<flat::NGramToRules>>;
+
+// Returns the size of the longest (sub-)domain of |origin| matching one of the
+// |domains| in the list.
+//
+// The |domains| should be sorted in descending order of their length, and
+// ascending alphabetical order within the groups of same-length domains.
+size_t GetLongestMatchingSubdomain(const url::Origin& origin,
+ const FlatDomains& domains) {
+ // If the |domains| list is short, then the simple strategy is usually faster.
+ if (domains.size() <= 5) {
+ for (auto* domain : domains) {
+ const base::StringPiece domain_piece = ToStringPiece(domain);
+ if (origin.DomainIs(domain_piece))
+ return domain_piece.size();
+ }
+ return 0;
+ }
+ // Otherwise look for each subdomain of the |origin| using binary search.
+
+ DCHECK(!origin.unique());
+ base::StringPiece canonicalized_host(origin.host());
+ if (canonicalized_host.empty())
+ return 0;
+
+ // If the host name ends with a dot, then ignore it.
+ if (canonicalized_host.back() == '.')
+ canonicalized_host.remove_suffix(1);
+
+ // The |left| bound of the search is shared between iterations, because
+ // subdomains are considered in decreasing order of their lengths, therefore
+ // each consecutive lower_bound will be at least as far as the previous.
+ flatbuffers::uoffset_t left = 0;
+ for (size_t position = 0;; ++position) {
+ const base::StringPiece subdomain = canonicalized_host.substr(position);
+
+ flatbuffers::uoffset_t right = domains.size();
+ while (left + 1 < right) {
+ auto middle = left + (right - left) / 2;
+ DCHECK_LT(middle, domains.size());
+ if (CompareDomains(ToStringPiece(domains[middle]), subdomain) <= 0)
+ left = middle;
+ else
+ right = middle;
+ }
+
+ DCHECK_LT(left, domains.size());
+ if (ToStringPiece(domains[left]) == subdomain)
+ return subdomain.size();
+
+ position = canonicalized_host.find('.', position);
+ if (position == base::StringPiece::npos)
+ break;
+ }
+
+ return 0;
+}
+
+// Returns whether the |origin| matches the domain list of the |rule|. A match
+// means that the longest domain in |domains| that |origin| is a sub-domain of
+// is not an exception OR all the |domains| are exceptions and neither matches
+// the |origin|. Thus, domain filters with more domain components trump filters
+// with fewer domain components, i.e. the more specific a filter is, the higher
+// the priority.
+//
+// A rule whose domain list is empty or contains only negative domains is still
+// considered a "generic" rule. Therefore, if |disable_generic_rules| is set,
+// this function will always return false for such rules.
+bool DoesOriginMatchDomainList(const url::Origin& origin,
+ const flat::UrlRule& rule,
+ bool disable_generic_rules) {
+ const bool is_generic = !rule.domains_included();
+ DCHECK(is_generic || rule.domains_included()->size());
+ if (disable_generic_rules && is_generic)
+ return false;
+
+ // Unique |origin| matches lists of exception domains only.
+ if (origin.unique())
+ return is_generic;
+
+ size_t longest_matching_included_domain_length = 1;
+ if (!is_generic) {
+ longest_matching_included_domain_length =
+ GetLongestMatchingSubdomain(origin, *rule.domains_included());
+ }
+ if (longest_matching_included_domain_length && rule.domains_excluded()) {
+ return GetLongestMatchingSubdomain(origin, *rule.domains_excluded()) <
+ longest_matching_included_domain_length;
+ }
+ return !!longest_matching_included_domain_length;
+}
+
+// Returns whether the request matches flags of the specified URL |rule|. Takes
+// into account:
+// - |element_type| of the requested resource, if not *_NONE.
+// - |activation_type| for a subdocument request, if not *_NONE.
+// - Whether the resource |is_third_party| w.r.t. its embedding document.
+bool DoesRuleFlagsMatch(const flat::UrlRule& rule,
+ flat::ElementType element_type,
+ flat::ActivationType activation_type,
+ bool is_third_party) {
+ DCHECK((element_type == flat::ElementType_NONE) !=
+ (activation_type == flat::ActivationType_NONE));
+
+ if (element_type != flat::ElementType_NONE &&
+ !(rule.element_types() & element_type)) {
+ return false;
+ }
+ if (activation_type != flat::ActivationType_NONE &&
+ !(rule.activation_types() & activation_type)) {
+ return false;
+ }
+
+ if (is_third_party &&
+ !(rule.options() & flat::OptionFlag_APPLIES_TO_THIRD_PARTY)) {
+ return false;
+ }
+ if (!is_third_party &&
+ !(rule.options() & flat::OptionFlag_APPLIES_TO_FIRST_PARTY)) {
+ return false;
+ }
+
+ return true;
+}
+
+const flat::UrlRule* FindMatchAmongCandidates(
+ const FlatUrlRuleList* candidates,
+ const GURL& url,
+ const url::Origin& document_origin,
+ flat::ElementType element_type,
+ flat::ActivationType activation_type,
+ bool is_third_party,
+ bool disable_generic_rules) {
+ if (!candidates)
+ return nullptr;
+ for (const flat::UrlRule* rule : *candidates) {
+ DCHECK_NE(rule, nullptr);
+ DCHECK_NE(rule->url_pattern_type(), flat::UrlPatternType_REGEXP);
+ if (!DoesRuleFlagsMatch(*rule, element_type, activation_type,
+ is_third_party)) {
+ continue;
+ }
+ if (!UrlPattern(*rule).MatchesUrl(url))
+ continue;
+
+ if (DoesOriginMatchDomainList(document_origin, *rule,
+ disable_generic_rules)) {
+ return rule;
+ }
+ }
+
+ return nullptr;
+}
+
+// Returns whether the network request matches a UrlPattern |index| represented
+// in its FlatBuffers format. |is_third_party| should reflect the relation
+// between |url| and |document_origin|.
+const flat::UrlRule* FindMatchInFlatUrlPatternIndex(
+ const flat::UrlPatternIndex& index,
+ const GURL& url,
+ const url::Origin& document_origin,
+ flat::ElementType element_type,
+ flat::ActivationType activation_type,
+ bool is_third_party,
+ bool disable_generic_rules) {
+ const FlatNGramIndex* hash_table = index.ngram_index();
+ const flat::NGramToRules* empty_slot = index.ngram_index_empty_slot();
+ DCHECK_NE(hash_table, nullptr);
+
+ NGramHashTableProber prober;
+
+ auto ngrams = CreateNGramExtractor<kNGramSize, uint64_t>(
+ url.spec(), [](char) { return false; });
+ for (uint64_t ngram : ngrams) {
+ const size_t slot_index = prober.FindSlot(
+ ngram, base::strict_cast<size_t>(hash_table->size()),
+ [hash_table, empty_slot](NGram ngram, size_t slot_index) {
+ const flat::NGramToRules* entry = hash_table->Get(slot_index);
+ DCHECK_NE(entry, nullptr);
+ return entry == empty_slot || entry->ngram() == ngram;
+ });
+ DCHECK_LT(slot_index, hash_table->size());
+
+ const flat::NGramToRules* entry = hash_table->Get(slot_index);
+ if (entry == empty_slot)
+ continue;
+ const flat::UrlRule* rule = FindMatchAmongCandidates(
+ entry->rule_list(), url, document_origin, element_type, activation_type,
+ is_third_party, disable_generic_rules);
+ if (rule)
+ return rule;
+ }
+
+ const FlatUrlRuleList* rules = index.fallback_rules();
+ return FindMatchAmongCandidates(rules, url, document_origin, element_type,
+ activation_type, is_third_party,
+ disable_generic_rules);
+}
+
+} // namespace
+
+UrlPatternIndexMatcher::UrlPatternIndexMatcher(
+ const flat::UrlPatternIndex* flat_index)
+ : flat_index_(flat_index) {
+ DCHECK(!flat_index || flat_index->n() == kNGramSize);
+}
+
+UrlPatternIndexMatcher::~UrlPatternIndexMatcher() = default;
+
+const flat::UrlRule* UrlPatternIndexMatcher::FindMatch(
+ const GURL& url,
+ const url::Origin& first_party_origin,
+ proto::ElementType element_type,
+ proto::ActivationType activation_type,
+ bool is_third_party,
+ bool disable_generic_rules) const {
+ return FindMatch(url, first_party_origin,
+ ProtoToFlatElementType(element_type),
+ ProtoToFlatActivationType(activation_type), is_third_party,
+ disable_generic_rules);
+}
+
+const flat::UrlRule* UrlPatternIndexMatcher::FindMatch(
+ const GURL& url,
+ const url::Origin& first_party_origin,
+ flat::ElementType element_type,
+ flat::ActivationType activation_type,
+ bool is_third_party,
+ bool disable_generic_rules) const {
+ if (!flat_index_ || !url.is_valid())
+ return nullptr;
+ if ((element_type == flat::ElementType_NONE) ==
+ (activation_type == flat::ActivationType_NONE)) {
+ return nullptr;
+ }
+
+ return FindMatchInFlatUrlPatternIndex(*flat_index_, url, first_party_origin,
+ element_type, activation_type,
+ is_third_party, disable_generic_rules);
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/url_pattern_index.h b/chromium/components/url_pattern_index/url_pattern_index.h
new file mode 100644
index 00000000000..8f7618871e7
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_pattern_index.h
@@ -0,0 +1,145 @@
+// 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_URL_PATTERN_INDEX_URL_PATTERN_INDEX_H_
+#define COMPONENTS_URL_PATTERN_INDEX_URL_PATTERN_INDEX_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string_piece_forward.h"
+#include "components/url_pattern_index/closed_hash_map.h"
+#include "components/url_pattern_index/flat/url_pattern_index_generated.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
+#include "components/url_pattern_index/uint64_hasher.h"
+#include "third_party/flatbuffers/src/include/flatbuffers/flatbuffers.h"
+
+class GURL;
+
+namespace url {
+class Origin;
+}
+
+namespace url_pattern_index {
+
+// The integer type used to represent N-grams.
+using NGram = uint64_t;
+// The hasher used for hashing N-grams.
+using NGramHasher = Uint64Hasher;
+// The hash table probe sequence used both by UrlPatternIndex and its builder.
+using NGramHashTableProber = DefaultProber<NGram, NGramHasher>;
+
+// FlatBuffer offset aliases.
+using UrlRuleOffset = flatbuffers::Offset<flat::UrlRule>;
+using UrlPatternIndexOffset = flatbuffers::Offset<flat::UrlPatternIndex>;
+
+constexpr size_t kNGramSize = 5;
+static_assert(kNGramSize <= sizeof(NGram), "NGram type is too narrow.");
+
+// Serializes the |rule| to the FlatBuffer |builder|, and returns an offset to
+// it in the resulting buffer. Returns null offset iff the |rule| could not be
+// serialized because of unsupported options or it is otherwise invalid.
+UrlRuleOffset SerializeUrlRule(const proto::UrlRule& rule,
+ flatbuffers::FlatBufferBuilder* builder);
+
+// The class used to construct an index over the URL patterns of a set of URL
+// rules. The rules themselves need to be converted to FlatBuffers format by the
+// client of this class, as well as persisted into the |flat_builder| that is
+// supplied in the constructor.
+class UrlPatternIndexBuilder {
+ public:
+ explicit UrlPatternIndexBuilder(flatbuffers::FlatBufferBuilder* flat_builder);
+ ~UrlPatternIndexBuilder();
+
+ // Adds a UrlRule to the index. The caller should have already persisted the
+ // rule into the same |flat_builder| by a call to SerializeUrlRule returning a
+ // non-null |offset|, and should pass in the resulting |offset| here.
+ void IndexUrlRule(UrlRuleOffset offset);
+
+ // Finalizes construction of the index, serializes it using |flat_builder|,
+ // and returns an offset to it in the resulting FlatBuffer.
+ UrlPatternIndexOffset Finish();
+
+ private:
+ using MutableUrlRuleList = std::vector<UrlRuleOffset>;
+ using MutableNGramIndex =
+ ClosedHashMap<NGram, MutableUrlRuleList, NGramHashTableProber>;
+
+ // Returns an N-gram of the |pattern| encoded into the NGram integer type. The
+ // N-gram is picked using a greedy heuristic, i.e. the one is chosen which
+ // corresponds to the shortest list of rules within the index. If there are no
+ // valid N-grams in the |pattern|, the return value is 0.
+ NGram GetMostDistinctiveNGram(base::StringPiece pattern);
+
+ // This index contains all non-REGEXP rules that have at least one acceptable
+ // N-gram. For each given rule, the N-gram used as an index key is picked
+ // greedily (see GetMostDistinctiveNGram).
+ MutableNGramIndex ngram_index_;
+
+ // A fallback list that contains all the rules with no acceptable N-gram.
+ MutableUrlRuleList fallback_rules_;
+
+ // Must outlive this instance.
+ flatbuffers::FlatBufferBuilder* flat_builder_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlPatternIndexBuilder);
+};
+
+// Encapsulates a read-only index built over the URL patterns of a set of URL
+// rules, and provides fast matching of network requests against these rules.
+class UrlPatternIndexMatcher {
+ public:
+ // Creates an instance to access the given |flat_index|. If |flat_index| is
+ // nullptr, then all requests return no match.
+ explicit UrlPatternIndexMatcher(const flat::UrlPatternIndex* flat_index);
+ ~UrlPatternIndexMatcher();
+
+ // If the index contains one or more UrlRules that match the request, returns
+ // one of them (it is undefined which one). Otherwise, returns nullptr.
+ //
+ // Notes on parameters:
+ // - |url| should be valid, otherwise the return value is nullptr.
+ // - Exactly one of |element_type| and |activation_type| should be specified,
+ // i.e., not equal to *_UNSPECIFIED, otherwise the return value is nullptr.
+ // - |is_third_party| should be pre-computed by the caller, e.g. using the
+ // registry_controlled_domains library, to reflect the relation between
+ // |url| and |first_party_origin|.
+ //
+ // A rule is deemed to match the request iff all of the following applies:
+ // - The |url| matches the rule's UrlPattern (see url_pattern.h).
+ // - The |first_party_origin| matches the rule's targeted domains list.
+ // - |element_type| or |activation_type| is among the rule's targeted types.
+ // - The |is_third_party| bit matches the rule's requirement on the requested
+ // |url| being first-/third-party w.r.t. its |first_party_origin|.
+ // - The rule is not generic if |disable_generic_rules| is true.
+ const flat::UrlRule* FindMatch(const GURL& url,
+ const url::Origin& first_party_origin,
+ proto::ElementType element_type,
+ proto::ActivationType activation_type,
+ bool is_third_party,
+ bool disable_generic_rules) const;
+
+ private:
+ // Helper function to work with flat::*Type(s). If the index contains one or
+ // more UrlRules that match the request, returns one of them (it is undefined
+ // which one). Otherwise, returns nullptr.
+ const flat::UrlRule* FindMatch(const GURL& url,
+ const url::Origin& first_party_origin,
+ flat::ElementType element_type,
+ flat::ActivationType activation_type,
+ bool is_third_party,
+ bool disable_generic_rules) const;
+
+ // Must outlive this instance.
+ const flat::UrlPatternIndex* flat_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlPatternIndexMatcher);
+};
+
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_URL_PATTERN_INDEX_H_
diff --git a/chromium/components/url_pattern_index/url_pattern_index_unittest.cc b/chromium/components/url_pattern_index/url_pattern_index_unittest.cc
new file mode 100644
index 00000000000..53d19677929
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_pattern_index_unittest.cc
@@ -0,0 +1,707 @@
+// 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/url_pattern_index/url_pattern_index.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "components/url_pattern_index/url_pattern.h"
+#include "components/url_pattern_index/url_rule_test_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace url_pattern_index {
+
+using namespace testing;
+
+class UrlPatternIndexTest : public ::testing::Test {
+ public:
+ UrlPatternIndexTest() { Reset(); }
+
+ protected:
+ bool AddUrlRule(const proto::UrlRule& rule) {
+ auto offset = SerializeUrlRule(rule, flat_builder_.get());
+ if (offset.o)
+ index_builder_->IndexUrlRule(offset);
+ return !!offset.o;
+ }
+
+ void Finish() {
+ const auto index_offset = index_builder_->Finish();
+ flat_builder_->Finish(index_offset);
+
+ const flat::UrlPatternIndex* flat_index =
+ flat::GetUrlPatternIndex(flat_builder_->GetBufferPointer());
+ index_matcher_.reset(new UrlPatternIndexMatcher(flat_index));
+ }
+
+ const flat::UrlRule* FindMatch(
+ base::StringPiece url_string,
+ base::StringPiece document_origin_string = base::StringPiece(),
+ proto::ElementType element_type = kOther,
+ proto::ActivationType activation_type = kNoActivation,
+ bool disable_generic_rules = false) {
+ const GURL url(url_string);
+ const url::Origin document_origin = GetOrigin(document_origin_string);
+ return index_matcher_->FindMatch(
+ url, document_origin, element_type, activation_type,
+ IsThirdParty(url, document_origin), disable_generic_rules);
+ }
+
+ bool IsOutOfRange(const flat::UrlRule* rule) const {
+ if (!rule)
+ return false;
+ const auto* data = reinterpret_cast<const uint8_t*>(rule);
+ return data < flat_builder_->GetBufferPointer() ||
+ data >= flat_builder_->GetBufferPointer() + flat_builder_->GetSize();
+ }
+
+ void Reset() {
+ index_matcher_.reset();
+ index_builder_.reset();
+ flat_builder_.reset(new flatbuffers::FlatBufferBuilder());
+ index_builder_.reset(new UrlPatternIndexBuilder(flat_builder_.get()));
+ }
+
+ private:
+ std::unique_ptr<flatbuffers::FlatBufferBuilder> flat_builder_;
+ std::unique_ptr<UrlPatternIndexBuilder> index_builder_;
+ std::unique_ptr<UrlPatternIndexMatcher> index_matcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlPatternIndexTest);
+};
+
+TEST_F(UrlPatternIndexTest, EmptyIndex) {
+ Finish();
+ EXPECT_FALSE(FindMatch(base::StringPiece() /* url */));
+ EXPECT_FALSE(FindMatch("http://example.com"));
+ EXPECT_FALSE(FindMatch("http://another.example.com?param=val"));
+}
+
+TEST_F(UrlPatternIndexTest, OneSimpleRule) {
+ ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("?param=", kSubstring))));
+ Finish();
+
+ EXPECT_FALSE(FindMatch("https://example.com"));
+ EXPECT_TRUE(FindMatch("http://example.org?param=image1"));
+}
+
+TEST_F(UrlPatternIndexTest, NoRuleApplies) {
+ ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("?filter_out=", kSubstring))));
+ ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("&filter_out=", kSubstring))));
+ Finish();
+
+ EXPECT_FALSE(FindMatch("http://example.com"));
+ EXPECT_FALSE(FindMatch("http://example.com?filter_not"));
+ EXPECT_FALSE(FindMatch("http://example.com?k=v&filter_not"));
+}
+
+TEST_F(UrlPatternIndexTest, OneRuleWithoutMetaInfo) {
+ const struct {
+ UrlPattern url_pattern;
+ const char* url;
+ bool expect_match;
+ } kTestCases[] = {
+ // SUBSTRING
+ {{"abcd", kSubstring}, "http://ex.com/abcd", true},
+ {{"abcd", kSubstring}, "http://ex.com/dcab", false},
+ {{"42", kSubstring}, "http://ex.com/adcd/picture42.png", true},
+ {{"&test", kSubstring},
+ "http://ex.com/params?param1=false&test=true",
+ true},
+ {{"-test-42.", kSubstring}, "http://ex.com/unit-test-42.1", true},
+ {{"/abcdtest160x600.", kSubstring},
+ "http://ex.com/abcdtest160x600.png",
+ true},
+
+ // WILDCARDED
+ {{"http://ex.com/abcd/picture*.png"},
+ "http://ex.com/abcd/picture42.png",
+ true},
+ {{"ex.com", kSubdomain, kAnchorNone}, "http://ex.com", true},
+ {{"ex.com", kSubdomain, kAnchorNone}, "http://test.ex.com", true},
+ {{"ex.com", kSubdomain, kAnchorNone}, "https://test.ex.com.com", true},
+ {{"ex.com", kSubdomain, kAnchorNone}, "https://test.rest.ex.com", true},
+ {{"ex.com", kSubdomain, kAnchorNone}, "https://test_ex.com", false},
+
+ {{"http://ex.com", kBoundary, kAnchorNone}, "http://ex.com/", true},
+ {{"http://ex.com", kBoundary, kAnchorNone}, "http://ex.com/42", true},
+ {{"http://ex.com", kBoundary, kAnchorNone},
+ "http://ex.com/42/http://ex.com/",
+ true},
+ {{"http://ex.com", kBoundary, kAnchorNone},
+ "http://ex.com/42/http://ex.info/",
+ true},
+ {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com", true},
+ {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com/42", false},
+ {{"http://ex.com/", kBoundary, kBoundary},
+ "http://ex.info/42/http://ex.com/",
+ false},
+ {{"http://ex.com/", kBoundary, kBoundary},
+ "http://ex.info/42/http://ex.com/",
+ false},
+ {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com/", true},
+ {{"http://ex.com/", kBoundary, kBoundary}, "http://ex.com/42.swf", false},
+ {{"http://ex.com/", kBoundary, kBoundary},
+ "http://ex.info/redirect/http://ex.com/",
+ false},
+ {{"pdf", kAnchorNone, kBoundary}, "http://ex.com/abcd.pdf", true},
+ {{"pdf", kAnchorNone, kBoundary}, "http://ex.com/pdfium", false},
+ {{"http://ex.com^"}, "http://ex.com/", true},
+ {{"http://ex.com^"}, "http://ex.com:8000/", true},
+ {{"http://ex.com^"}, "http://ex.com.ru", false},
+ {{"^ex.com^"},
+ "http://ex.com:8000/42.loss?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
+ true},
+ {{"^42.loss^"},
+ "http://ex.com:8000/42.loss?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
+ true},
+
+ // TODO(pkalinnikov): The '^' at the end should match end-of-string.
+ //
+ // {"^%D1%82%D0%B5%D1%81%D1%82^",
+ // "http://ex.com:8000/42.loss?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
+ // true},
+ // {"/abcd/*/picture^", "http://ex.com/abcd/42/picture", true},
+
+ {{"/abcd/*/picture^"}, "http://ex.com/abcd/42/loss/picture?param", true},
+ {{"/abcd/*/picture^"}, "http://ex.com/abcd//picture/42", true},
+ {{"/abcd/*/picture^"}, "http://ex.com/abcd/picture", false},
+ {{"/abcd/*/picture^"}, "http://ex.com/abcd/42/pictureraph", false},
+ {{"/abcd/*/picture^"}, "http://ex.com/abcd/42/picture.swf", false},
+ {{"test.ex.com^", kSubdomain, kAnchorNone},
+ "http://test.ex.com/42.swf",
+ true},
+ {{"test.ex.com^", kSubdomain, kAnchorNone},
+ "http://server1.test.ex.com/42.swf",
+ true},
+ {{"test.ex.com^", kSubdomain, kAnchorNone},
+ "https://test.ex.com:8000/",
+ true},
+ {{"test.ex.com^", kSubdomain, kAnchorNone},
+ "http://test.ex.com.ua/42.swf",
+ false},
+ {{"test.ex.com^", kSubdomain, kAnchorNone},
+ "http://ex.com/redirect/http://test.ex.com/",
+ false},
+
+ {{"/abcd/*"}, "https://ex.com/abcd/", true},
+ {{"/abcd/*"}, "http://ex.com/abcd/picture.jpeg", true},
+ {{"/abcd/*"}, "https://ex.com/abcd", false},
+ {{"/abcd/*"}, "http://abcd.ex.com", false},
+ {{"*/abcd/"}, "https://ex.com/abcd/", true},
+ {{"*/abcd/"}, "http://ex.com/abcd/picture.jpeg", true},
+ {{"*/abcd/"}, "https://ex.com/test-abcd/", false},
+ {{"*/abcd/"}, "http://abcd.ex.com", false},
+ };
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(::testing::Message() << "UrlPattern: " << test_case.url_pattern
+ << "; URL: " << test_case.url);
+
+ ASSERT_TRUE(AddUrlRule(MakeUrlRule(test_case.url_pattern)));
+ Finish();
+
+ EXPECT_EQ(test_case.expect_match, !!FindMatch(test_case.url));
+ Reset();
+ }
+}
+
+TEST_F(UrlPatternIndexTest, OneRuleWithThirdParty) {
+ const struct {
+ const char* url_pattern;
+ proto::SourceType source_type;
+
+ const char* url;
+ const char* document_origin;
+ bool expect_match;
+ } kTestCases[] = {
+ {"ex.com", kThirdParty, "http://ex.com", "http://exmpl.org", true},
+ {"ex.com", kThirdParty, "http://ex.com", "http://ex.com", false},
+ {"ex.com", kThirdParty, "http://ex.com/path?k=v", "http://exmpl.org",
+ true},
+ {"ex.com", kThirdParty, "http://ex.com/path?k=v", "http://ex.com", false},
+ {"ex.com", kFirstParty, "http://ex.com/path?k=v", "http://ex.com", true},
+ {"ex.com", kFirstParty, "http://ex.com/path?k=v", "http://exmpl.com",
+ false},
+ {"ex.com", kAnyParty, "http://ex.com/path?k=v", "http://ex.com", true},
+ {"ex.com", kAnyParty, "http://ex.com/path?k=v", "http://exmpl.com", true},
+ {"ex.com", kThirdParty, "http://subdomain.ex.com", "http://ex.com",
+ false},
+ {"ex.com", kThirdParty, "http://ex.com", nullptr, true},
+
+ // Public Suffix List tests.
+ {"ex.com", kThirdParty, "http://two.ex.com", "http://one.ex.com", false},
+ {"ex.com", kThirdParty, "http://ex.com", "http://one.ex.com", false},
+ {"ex.com", kThirdParty, "http://two.ex.com", "http://ex.com", false},
+ {"ex.com", kThirdParty, "http://ex.com", "http://example.org", true},
+ {"appspot.com", kThirdParty, "http://two.appspot.org",
+ "http://one.appspot.com", false},
+ };
+
+ for (auto test_case : kTestCases) {
+ SCOPED_TRACE(::testing::Message()
+ << "UrlPattern: " << test_case.url_pattern
+ << "; SourceType: " << static_cast<int>(test_case.source_type)
+ << "; URL: " << test_case.url
+ << "; DocumentOrigin: " << test_case.document_origin);
+
+ auto rule = MakeUrlRule(UrlPattern(test_case.url_pattern, kSubstring));
+ rule.set_source_type(test_case.source_type);
+ ASSERT_TRUE(AddUrlRule(rule));
+ Finish();
+
+ EXPECT_EQ(test_case.expect_match,
+ !!FindMatch(test_case.url, test_case.document_origin));
+ Reset();
+ }
+}
+
+TEST_F(UrlPatternIndexTest, OneRuleWithDomainList) {
+ constexpr const char* kUrl = "http://example.com";
+
+ const struct {
+ std::vector<std::string> domains;
+ const char* document_origin;
+ bool expect_match;
+ } kTestCases[] = {
+ {std::vector<std::string>(), nullptr, true},
+ {std::vector<std::string>(), "http://domain.com", true},
+
+ {{"domain.com"}, nullptr, false},
+ {{"domain.com"}, "http://domain.com", true},
+ {{"ddomain.com"}, "http://domain.com", false},
+ {{"domain.com"}, "http://ddomain.com", false},
+ {{"domain.com"}, "http://sub.domain.com", true},
+ {{"sub.domain.com"}, "http://domain.com", false},
+ {{"sub.domain.com"}, "http://sub.domain.com", true},
+ {{"sub.domain.com"}, "http://a.b.c.sub.domain.com", true},
+ {{"sub.domain.com"}, "http://sub.domain.com.com", false},
+
+ // TODO(pkalinnikov): Probably need to canonicalize domain patterns to
+ // avoid subtleties like below.
+ {{"domain.com"}, "http://domain.com.", true},
+ {{"domain.com"}, "http://.domain.com", true},
+ {{"domain.com"}, "http://.domain.com.", true},
+ {{".domain.com"}, "http://.domain.com", true},
+ {{"domain.com."}, "http://domain.com", false},
+ {{"domain.com."}, "http://domain.com.", true},
+
+ {{"domain..com"}, "http://domain.com", false},
+ {{"domain.com"}, "http://domain..com", false},
+ {{"domain..com"}, "http://domain..com", true},
+
+ {{"~domain.com"}, nullptr, true},
+ {{"~domain.com"}, "http://domain.com", false},
+ {{"~ddomain.com"}, "http://domain.com", true},
+ {{"~domain.com"}, "http://ddomain.com", true},
+ {{"~domain.com"}, "http://sub.domain.com", false},
+ {{"~sub.domain.com"}, "http://domain.com", true},
+ {{"~sub.domain.com"}, "http://sub.domain.com", false},
+ {{"~sub.domain.com"}, "http://a.b.c.sub.domain.com", false},
+ {{"~sub.domain.com"}, "http://sub.domain.com.com", true},
+
+ {{"domain1.com", "domain2.com"}, nullptr, false},
+ {{"domain1.com", "domain2.com"}, "http://domain1.com", true},
+ {{"domain1.com", "domain2.com"}, "http://domain2.com", true},
+ {{"domain1.com", "domain2.com"}, "http://domain3.com", false},
+ {{"domain1.com", "domain2.com"}, "http://not_domain1.com", false},
+ {{"domain1.com", "domain2.com"}, "http://sub.domain1.com", true},
+ {{"domain1.com", "domain2.com"}, "http://a.b.c.sub.domain2.com", true},
+
+ {{"~domain1.com", "~domain2.com"}, "http://domain1.com", false},
+ {{"~domain1.com", "~domain2.com"}, "http://domain2.com", false},
+ {{"~domain1.com", "~domain2.com"}, "http://domain3.com", true},
+
+ {{"domain.com", "~sub.domain.com"}, "http://domain.com", true},
+ {{"domain.com", "~sub.domain.com"}, "http://sub.domain.com", false},
+ {{"domain.com", "~sub.domain.com"}, "http://a.b.sub.domain.com", false},
+ {{"domain.com", "~sub.domain.com"}, "http://ssub.domain.com", true},
+
+ {{"domain.com", "~a.domain.com", "~b.domain.com"},
+ "http://domain.com",
+ true},
+ {{"domain.com", "~a.domain.com", "~b.domain.com"},
+ "http://a.domain.com",
+ false},
+ {{"domain.com", "~a.domain.com", "~b.domain.com"},
+ "http://b.domain.com",
+ false},
+
+ {{"domain.com", "~a.domain.com", "b.a.domain.com"},
+ "http://domain.com",
+ true},
+ {{"domain.com", "~a.domain.com", "b.a.domain.com"},
+ "http://a.domain.com",
+ false},
+ {{"domain.com", "~a.domain.com", "b.a.domain.com"},
+ "http://b.a.domain.com",
+ true},
+ {{"domain.com", "~a.domain.com", "b.a.domain.com"},
+ "http://c.b.a.domain.com",
+ true},
+
+ // The following test addresses a former bug in domain list matcher. When
+ // "domain.com" was matched, the positive filters lookup stopped, and the
+ // next domain was considered as a negative. The initial character was
+ // skipped (supposing it's a '~') and the remainder was considered a
+ // domain. So "ddomain.com" would be matched and thus the whole rule would
+ // be classified as non-matching, which is not correct.
+ {{"domain.com", "ddomain.com", "~sub.domain.com"},
+ "http://domain.com",
+ true},
+ };
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(::testing::Message()
+ << "Domains: " << ::testing::PrintToString(test_case.domains)
+ << "; DocumentOrigin: " << test_case.document_origin);
+
+ auto rule = MakeUrlRule(UrlPattern(kUrl, kSubstring));
+ AddDomains(test_case.domains, &rule);
+ ASSERT_TRUE(AddUrlRule(rule));
+ Finish();
+
+ EXPECT_EQ(test_case.expect_match,
+ !!FindMatch(kUrl, test_case.document_origin));
+ Reset();
+ }
+}
+
+TEST_F(UrlPatternIndexTest, OneRuleWithLongDomainList) {
+ constexpr const char* kUrl = "http://example.com";
+ constexpr size_t kDomains = 200;
+
+ std::vector<std::string> domains;
+ for (size_t i = 0; i < kDomains; ++i) {
+ const std::string domain = "domain" + std::to_string(i) + ".com";
+ domains.push_back(domain);
+ domains.push_back("~sub." + domain);
+ domains.push_back("a.sub." + domain);
+ domains.push_back("b.sub." + domain);
+ domains.push_back("c.sub." + domain);
+ domains.push_back("~aa.sub." + domain);
+ domains.push_back("~ab.sub." + domain);
+ domains.push_back("~ba.sub." + domain);
+ domains.push_back("~bb.sub." + domain);
+ domains.push_back("~sub.sub.c.sub." + domain);
+ }
+
+ auto rule = MakeUrlRule(UrlPattern(kUrl, kSubstring));
+ AddDomains(domains, &rule);
+ ASSERT_TRUE(AddUrlRule(rule));
+ Finish();
+
+ for (size_t i = 0; i < kDomains; ++i) {
+ SCOPED_TRACE(::testing::Message() << "Iteration: " << i);
+ const std::string domain = "domain" + std::to_string(i) + ".com";
+
+ EXPECT_TRUE(FindMatch(kUrl, "http://" + domain));
+ EXPECT_FALSE(FindMatch(kUrl, "http://sub." + domain));
+ EXPECT_TRUE(FindMatch(kUrl, "http://a.sub." + domain));
+ EXPECT_TRUE(FindMatch(kUrl, "http://b.sub." + domain));
+ EXPECT_TRUE(FindMatch(kUrl, "http://c.sub." + domain));
+ EXPECT_FALSE(FindMatch(kUrl, "http://aa.sub." + domain));
+ EXPECT_FALSE(FindMatch(kUrl, "http://ab.sub." + domain));
+ EXPECT_FALSE(FindMatch(kUrl, "http://ba.sub." + domain));
+ EXPECT_FALSE(FindMatch(kUrl, "http://bb.sub." + domain));
+ EXPECT_TRUE(FindMatch(kUrl, "http://sub.c.sub." + domain));
+ EXPECT_FALSE(FindMatch(kUrl, "http://sub.sub.c.sub." + domain));
+ }
+}
+
+TEST_F(UrlPatternIndexTest, OneRuleWithElementTypes) {
+ constexpr auto kAll = kAllElementTypes;
+ const struct {
+ const char* url_pattern;
+ int32_t element_types;
+
+ const char* url;
+ proto::ElementType element_type;
+ bool expect_match;
+ } kTestCases[] = {
+ {"ex.com", kAll, "http://ex.com/img.jpg", kImage, true},
+ {"ex.com", kAll & ~kPopup, "http://ex.com/img", kPopup, false},
+
+ {"ex.com", kImage, "http://ex.com/img.jpg", kImage, true},
+ {"ex.com", kAll & ~kImage, "http://ex.com/img.jpg", kImage, false},
+ {"ex.com", kScript, "http://ex.com/img.jpg", kImage, false},
+ {"ex.com", kAll & ~kScript, "http://ex.com/img.jpg", kImage, true},
+
+ {"ex.com", kImage | kFont, "http://ex.com/font", kFont, true},
+ {"ex.com", kImage | kFont, "http://ex.com/image", kImage, true},
+ {"ex.com", kImage | kFont, "http://ex.com/video",
+ proto::ELEMENT_TYPE_MEDIA, false},
+ {"ex.com", kAll & ~kFont & ~kScript, "http://ex.com/font", kFont, false},
+ {"ex.com", kAll & ~kFont & ~kScript, "http://ex.com/scr", kScript, false},
+ {"ex.com", kAll & ~kFont & ~kScript, "http://ex.com/img", kImage, true},
+
+ {"ex.com", kAll, "http://ex.com", proto::ELEMENT_TYPE_OTHER, true},
+ {"ex.com", kAll, "http://ex.com", proto::ELEMENT_TYPE_UNSPECIFIED, false},
+ {"ex.com", kWebSocket, "ws://ex.com", proto::ELEMENT_TYPE_WEBSOCKET,
+ true},
+ };
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(
+ ::testing::Message()
+ << "UrlPattern: " << test_case.url_pattern
+ << "; ElementTypes: " << static_cast<int>(test_case.element_types)
+ << "; URL: " << test_case.url
+ << "; ElementType: " << static_cast<int>(test_case.element_type));
+
+ auto rule = MakeUrlRule(UrlPattern(test_case.url_pattern, kSubstring));
+ rule.set_element_types(test_case.element_types);
+ ASSERT_TRUE(AddUrlRule(rule));
+ Finish();
+
+ EXPECT_EQ(test_case.expect_match,
+ !!FindMatch(test_case.url, nullptr /* document_origin_string */,
+ test_case.element_type));
+ Reset();
+ }
+}
+
+TEST_F(UrlPatternIndexTest, OneRuleWithActivationTypes) {
+ const struct {
+ const char* url_pattern;
+ int32_t activation_types;
+
+ const char* document_url;
+ proto::ActivationType activation_type;
+ bool expect_match;
+ } kTestCases[] = {
+ {"example.com", kDocument, "http://example.com", kDocument, true},
+ {"xample.com", kDocument, "http://example.com", kDocument, true},
+ {"exampl.com", kDocument, "http://example.com", kDocument, false},
+
+ {"example.com", kGenericBlock, "http://example.com", kDocument, false},
+ {"example.com", kDocument, "http://example.com", kNoActivation, false},
+ {"example.com", kGenericBlock, "http://example.com", kNoActivation,
+ false},
+
+ // Invalid GURL.
+ {"example.com", kDocument, "http;//example.com", kDocument, false},
+ };
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(
+ ::testing::Message()
+ << "UrlPattern: " << test_case.url_pattern
+ << "; ActivationTypes: " << static_cast<int>(test_case.activation_types)
+ << "; DocumentURL: " << test_case.document_url
+ << "; ActivationType: " << static_cast<int>(test_case.activation_type));
+
+ auto rule = MakeUrlRule(UrlPattern(test_case.url_pattern, kSubstring));
+ rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
+ rule.clear_element_types();
+ rule.set_activation_types(test_case.activation_types);
+ ASSERT_TRUE(AddUrlRule(rule));
+ Finish();
+
+ EXPECT_EQ(test_case.expect_match,
+ !!FindMatch(test_case.document_url,
+ nullptr /* parent_document_origin */, kNoElement,
+ test_case.activation_type));
+ EXPECT_EQ(test_case.expect_match,
+ !!FindMatch(test_case.document_url, "http://example.com/",
+ kNoElement, test_case.activation_type));
+ EXPECT_EQ(test_case.expect_match,
+ !!FindMatch(test_case.document_url, "http://xmpl.com/",
+ kNoElement, test_case.activation_type));
+ Reset();
+ }
+}
+
+TEST_F(UrlPatternIndexTest, OneRuleWithElementAndActivationTypes) {
+ auto rule = MakeUrlRule(UrlPattern("allow.ex.com", kSubstring));
+ rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
+ rule.set_element_types(kSubdocument);
+ rule.set_activation_types(kDocument);
+ ASSERT_TRUE(AddUrlRule(rule));
+ Finish();
+
+ EXPECT_FALSE(FindMatch("http://allow.ex.com"));
+ EXPECT_TRUE(FindMatch("http://allow.ex.com",
+ nullptr /*document_origin_string */, kSubdocument));
+
+ EXPECT_FALSE(FindMatch("http://allow.ex.com",
+ nullptr /* document_origin_string */, kNoElement,
+ kGenericBlock));
+ EXPECT_TRUE(FindMatch("http://allow.ex.com",
+ nullptr /* document_origin_string */, kNoElement,
+ kDocument));
+}
+
+TEST_F(UrlPatternIndexTest, MatchWithDisableGenericRules) {
+ const struct {
+ const char* url_pattern;
+ std::vector<std::string> domains;
+ } kRules[] = {
+ // Generic rules.
+ {"some_text", std::vector<std::string>()},
+ {"another_text", {"~example.com"}},
+ {"final_text", {"~example1.com", "~example2.com"}},
+ // Domain specific rules.
+ {"some_text", {"example1.com"}},
+ {"more_text", {"example.com", "~exclude.example.com"}},
+ {"last_text", {"example1.com", "sub.example2.com"}},
+ };
+
+ for (const auto& rule_data : kRules) {
+ auto rule = MakeUrlRule(UrlPattern(rule_data.url_pattern, kSubstring));
+ AddDomains(rule_data.domains, &rule);
+ ASSERT_TRUE(AddUrlRule(rule))
+ << "UrlPattern: " << rule_data.url_pattern
+ << "; Domains: " << ::testing::PrintToString(rule_data.domains);
+ }
+
+ // Note: Some of the rules have common domains (e.g., example1.com), which are
+ // ultimately shared by FlatBuffers' CreateSharedString. The test also makes
+ // sure that the data structure works properly with such optimization.
+ Finish();
+
+ const struct {
+ const char* url;
+ const char* document_origin;
+ bool expect_match_with_enable_all_rules;
+ bool expect_match_with_disable_generic_rules;
+ } kTestCases[] = {
+ {"http://ex.com/some_text", "http://example.com", true, false},
+ {"http://ex.com/some_text", "http://example1.com", true, true},
+
+ {"http://ex.com/another_text", "http://example.com", false, false},
+ {"http://ex.com/another_text", "http://example1.com", true, false},
+
+ {"http://ex.com/final_text", "http://example.com", true, false},
+ {"http://ex.com/final_text", "http://example1.com", false, false},
+ {"http://ex.com/final_text", "http://example2.com", false, false},
+
+ {"http://ex.com/more_text", "http://example.com", true, true},
+ {"http://ex.com/more_text", "http://exclude.example.com", false, false},
+ {"http://ex.com/more_text", "http://example1.com", false, false},
+
+ {"http://ex.com/last_text", "http://example.com", false, false},
+ {"http://ex.com/last_text", "http://example1.com", true, true},
+ {"http://ex.com/last_text", "http://example2.com", false, false},
+ {"http://ex.com/last_text", "http://sub.example2.com", true, true},
+ };
+
+ constexpr bool kDisableGenericRules = true;
+ constexpr bool kEnableAllRules = false;
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(::testing::Message()
+ << "UrlPattern: " << test_case.url
+ << "; DocumentOrigin: " << test_case.document_origin);
+
+ EXPECT_EQ(test_case.expect_match_with_disable_generic_rules,
+ !!FindMatch(test_case.url, test_case.document_origin, kOther,
+ kNoActivation, kDisableGenericRules));
+ EXPECT_EQ(test_case.expect_match_with_enable_all_rules,
+ !!FindMatch(test_case.url, test_case.document_origin, kOther,
+ kNoActivation, kEnableAllRules));
+ }
+}
+
+TEST_F(UrlPatternIndexTest, RulesWithUnsupportedTypes) {
+ const struct {
+ int element_types;
+ int activation_types;
+ } kRules[] = {
+ {proto::ELEMENT_TYPE_MAX << 1, 0},
+ {0, proto::ACTIVATION_TYPE_MAX << 1},
+ {proto::ELEMENT_TYPE_MAX << 1, proto::ACTIVATION_TYPE_MAX << 1},
+
+ {kPopup, 0},
+ {0, proto::ACTIVATION_TYPE_ELEMHIDE},
+ {0, proto::ACTIVATION_TYPE_GENERICHIDE},
+ {0, proto::ACTIVATION_TYPE_ELEMHIDE | proto::ACTIVATION_TYPE_GENERICHIDE},
+ {proto::ELEMENT_TYPE_POPUP, proto::ACTIVATION_TYPE_ELEMHIDE},
+ };
+
+ for (const auto& rule_data : kRules) {
+ auto rule = MakeUrlRule(UrlPattern("example.com", kSubstring));
+ rule.set_element_types(rule_data.element_types);
+ rule.set_activation_types(rule_data.activation_types);
+ EXPECT_FALSE(AddUrlRule(rule))
+ << "ElementTypes: " << static_cast<int>(rule_data.element_types)
+ << "; ActivationTypes: "
+ << static_cast<int>(rule_data.activation_types);
+ }
+ ASSERT_TRUE(AddUrlRule(MakeUrlRule(UrlPattern("exmpl.com", kSubstring))));
+ Finish();
+
+ EXPECT_FALSE(FindMatch("http://example.com/"));
+ EXPECT_TRUE(FindMatch("https://exmpl.com/"));
+}
+
+TEST_F(UrlPatternIndexTest, RulesWithSupportedAndUnsupportedTypes) {
+ const struct {
+ int element_types;
+ int activation_types;
+ } kRules[] = {
+ {kImage | (proto::ELEMENT_TYPE_MAX << 1), 0},
+ {kScript | kPopup, 0},
+ {0, kDocument | (proto::ACTIVATION_TYPE_MAX << 1)},
+ };
+
+ for (const auto& rule_data : kRules) {
+ auto rule = MakeUrlRule(UrlPattern("example.com", kSubstring));
+ rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
+ rule.set_element_types(rule_data.element_types);
+ rule.set_activation_types(rule_data.activation_types);
+ EXPECT_TRUE(AddUrlRule(rule))
+ << "ElementTypes: " << static_cast<int>(rule_data.element_types)
+ << "; ActivationTypes: "
+ << static_cast<int>(rule_data.activation_types);
+ }
+ Finish();
+
+ EXPECT_TRUE(FindMatch("http://example.com/", nullptr, kImage));
+ EXPECT_TRUE(FindMatch("http://example.com/", nullptr, kScript));
+ EXPECT_FALSE(FindMatch("http://example.com/", nullptr, kPopup));
+ EXPECT_FALSE(FindMatch("http://example.com/"));
+
+ EXPECT_TRUE(FindMatch("http://example.com", nullptr, kNoElement, kDocument));
+ EXPECT_FALSE(
+ FindMatch("http://example.com", nullptr, kNoElement, kGenericBlock));
+}
+
+TEST_F(UrlPatternIndexTest, FindMatchReturnsCorrectRules) {
+ constexpr size_t kNumOfPatterns = 1024;
+
+ std::vector<std::string> url_patterns(kNumOfPatterns);
+ for (size_t i = 0; i < kNumOfPatterns; ++i) {
+ url_patterns[i] = "http://example." + std::to_string(i) + ".com";
+ ASSERT_TRUE(
+ AddUrlRule(MakeUrlRule(UrlPattern(url_patterns[i], kSubstring))))
+ << "Rule #" << i;
+ }
+ Finish();
+
+ std::reverse(url_patterns.begin() + kNumOfPatterns / 2, url_patterns.end());
+ for (const std::string& url_pattern : url_patterns) {
+ SCOPED_TRACE(::testing::Message() << "UrlPattern: " << url_pattern);
+
+ const flat::UrlRule* rule = FindMatch(url_pattern);
+ ASSERT_TRUE(rule);
+ ASSERT_FALSE(IsOutOfRange(rule));
+
+ const flatbuffers::String* rule_pattern = rule->url_pattern();
+ ASSERT_TRUE(rule_pattern);
+ EXPECT_EQ(url_pattern,
+ base::StringPiece(rule_pattern->data(), rule_pattern->size()));
+ }
+
+ EXPECT_FALSE(
+ FindMatch("http://example." + std::to_string(kNumOfPatterns) + ".com"));
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/url_pattern_unittest.cc b/chromium/components/url_pattern_index/url_pattern_unittest.cc
new file mode 100644
index 00000000000..09434567eb8
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_pattern_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/url_pattern_index/url_pattern.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace url_pattern_index {
+
+namespace {
+
+constexpr proto::AnchorType kAnchorNone = proto::ANCHOR_TYPE_NONE;
+constexpr proto::AnchorType kBoundary = proto::ANCHOR_TYPE_BOUNDARY;
+constexpr proto::AnchorType kSubdomain = proto::ANCHOR_TYPE_SUBDOMAIN;
+
+} // namespace
+
+TEST(SubresourceFilterUrlPatternTest, MatchesUrl) {
+ const struct {
+ UrlPattern url_pattern;
+ const char* url;
+ bool expect_match;
+ } kTestCases[] = {
+ {{"", proto::URL_PATTERN_TYPE_SUBSTRING}, "http://ex.com/", true},
+ {{"", proto::URL_PATTERN_TYPE_WILDCARDED}, "http://ex.com/", true},
+ {{"", kBoundary, kAnchorNone}, "http://ex.com/", true},
+ {{"", kSubdomain, kAnchorNone}, "http://ex.com/", true},
+ {{"", kSubdomain, kAnchorNone}, "http://ex.com/", true},
+ {{"^", kSubdomain, kAnchorNone}, "http://ex.com/", false},
+ {{".", kSubdomain, kAnchorNone}, "http://ex.com/", false},
+ {{"", kAnchorNone, kBoundary}, "http://ex.com/", true},
+ {{"^", kAnchorNone, kBoundary}, "http://ex.com/", true},
+ {{".", kAnchorNone, kBoundary}, "http://ex.com/", false},
+ {{"", kBoundary, kBoundary}, "http://ex.com/", false},
+ {{"", kSubdomain, kBoundary}, "http://ex.com/", false},
+ {{"com/", kSubdomain, kBoundary}, "http://ex.com/", true},
+
+ {{"xampl", proto::URL_PATTERN_TYPE_SUBSTRING},
+ "http://example.com",
+ true},
+ {{"example", proto::URL_PATTERN_TYPE_SUBSTRING},
+ "http://example.com",
+ true},
+ {{"/a?a"}, "http://ex.com/a?a", true},
+ {{"^abc"}, "http://ex.com/abc?a", true},
+ {{"^abc"}, "http://ex.com/a?abc", true},
+ {{"^abc"}, "http://ex.com/abc?abc", true},
+ {{"^abc^abc"}, "http://ex.com/abc?abc", true},
+ {{"^com^abc^abc"}, "http://ex.com/abc?abc", false},
+
+ {{"http://ex", kBoundary, kAnchorNone}, "http://example.com", true},
+ {{"http://ex", kAnchorNone, kAnchorNone}, "http://example.com", true},
+ {{"mple.com/", kAnchorNone, kBoundary}, "http://example.com", true},
+ {{"mple.com/", kAnchorNone, kAnchorNone}, "http://example.com", true},
+ {{"mple.com/", kSubdomain, kAnchorNone}, "http://example.com", false},
+ {{"ex.com", kSubdomain, kAnchorNone}, "http://hex.com", false},
+ {{"ex.com", kSubdomain, kAnchorNone}, "http://ex.com", true},
+ {{"ex.com", kSubdomain, kAnchorNone}, "http://hex.ex.com", true},
+ {{"ex.com", kSubdomain, kAnchorNone}, "http://hex.hex.com", false},
+
+ // Note: "example.com" will be normalized into "example.com/".
+ {{"http://*mpl", kBoundary, kAnchorNone}, "http://example.com", true},
+ {{"mpl*com/", kAnchorNone, kBoundary}, "http://example.com", true},
+ {{"example^com"}, "http://example.com", false},
+ {{"example^com"}, "http://example/com", true},
+ {{"example.com^"}, "http://example.com:8080", true},
+ {{"http*.com/", kBoundary, kBoundary}, "http://example.com", true},
+ {{"http*.org/", kBoundary, kBoundary}, "http://example.com", false},
+
+ {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path/bbb?k=v&p1=0&p2=1", false},
+ {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path?k=v&p1=0&p2=1", true},
+ {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path?k=v&k=v&p1=0&p2=1", true},
+ {{"/path?*&p1=*&p2="},
+ "http://ex.com/aaa/path?k=v&p1=0&p3=10&p2=1",
+ true},
+ {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path&p1=0&p2=1", false},
+ {{"/path?*&p1=*&p2="}, "http://ex.com/aaa/path?k=v&p2=0&p1=1", false},
+
+ {{"abc*def*ghijk*xyz"},
+ "http://example.com/abcdeffffghijkmmmxyzzz",
+ true},
+ {{"abc*cdef"}, "http://example.com/abcdef", false},
+
+ {{"^^a^^"}, "http://ex.com/?a=/", true},
+ {{"^^a^^"}, "http://ex.com/?a=/&b=0", true},
+ {{"^^a^^"}, "http://ex.com/?a=", false},
+
+ {{"ex.com^path^*k=v^"}, "http://ex.com/path/?k1=v1&ak=v&kk=vv", true},
+ {{"ex.com^path^*k=v^"}, "http://ex.com/p/path/?k1=v1&ak=v&kk=vv", false},
+ {{"a^a&a^a&"}, "http://ex.com/a/a/a/a/?a&a&a&a&a", true},
+
+ {{"abc*def^"}, "http://ex.com/abc/a/ddef/", true},
+
+ {{"https://example.com/"}, "http://example.com/", false},
+ {{"example.com/", kSubdomain, kAnchorNone}, "http://example.com/", true},
+ {{"examp", kSubdomain, kAnchorNone}, "http://example.com/", true},
+ {{"xamp", kSubdomain, kAnchorNone}, "http://example.com/", false},
+ {{"examp", kSubdomain, kAnchorNone}, "http://test.example.com/", true},
+ {{"t.examp", kSubdomain, kAnchorNone}, "http://test.example.com/", false},
+ {{"com^", kSubdomain, kAnchorNone}, "http://test.example.com/", true},
+ {{"x.com", kSubdomain, kAnchorNone}, "http://ex.com/?url=x.com", false},
+ {{"ex.com/", kSubdomain, kBoundary}, "http://ex.com/", true},
+ {{"ex.com^", kSubdomain, kBoundary}, "http://ex.com/", true},
+ {{"ex.co", kSubdomain, kBoundary}, "http://ex.com/", false},
+ {{"ex.com", kSubdomain, kBoundary}, "http://rex.com.ex.com/", false},
+ {{"ex.com/", kSubdomain, kBoundary}, "http://rex.com.ex.com/", true},
+ {{"http", kSubdomain, kBoundary}, "http://http.com/", false},
+ {{"http", kSubdomain, kAnchorNone}, "http://http.com/", true},
+ {{"/example.com", kSubdomain, kBoundary}, "http://example.com/", false},
+ {{"/example.com/", kSubdomain, kBoundary}, "http://example.com/", false},
+ };
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(testing::Message() << "Rule: " << test_case.url_pattern
+ << "; URL: " << GURL(test_case.url));
+
+ const bool is_match = test_case.url_pattern.MatchesUrl(GURL(test_case.url));
+ EXPECT_EQ(test_case.expect_match, is_match);
+ }
+}
+
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/url_rule_test_support.cc b/chromium/components/url_pattern_index/url_rule_test_support.cc
new file mode 100644
index 00000000000..9f2a462d372
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_rule_test_support.cc
@@ -0,0 +1,56 @@
+// 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/url_pattern_index/url_rule_test_support.h"
+
+#include "base/logging.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace url_pattern_index {
+namespace testing {
+
+proto::UrlRule MakeUrlRule(const UrlPattern& url_pattern) {
+ proto::UrlRule rule;
+
+ rule.set_semantics(proto::RULE_SEMANTICS_BLACKLIST);
+ rule.set_source_type(proto::SOURCE_TYPE_ANY);
+ rule.set_element_types(kAllElementTypes);
+
+ rule.set_url_pattern_type(url_pattern.type());
+ rule.set_anchor_left(url_pattern.anchor_left());
+ rule.set_anchor_right(url_pattern.anchor_right());
+ rule.set_match_case(url_pattern.match_case());
+ rule.set_url_pattern(url_pattern.url_pattern().as_string());
+
+ return rule;
+}
+
+void AddDomains(const std::vector<std::string>& domains, proto::UrlRule* rule) {
+ for (std::string domain_pattern : domains) {
+ DCHECK(!domain_pattern.empty());
+ auto* domain = rule->add_domains();
+ if (domain_pattern[0] == '~') {
+ domain_pattern.erase(0, 1);
+ domain->set_exclude(true);
+ }
+ domain->set_domain(std::move(domain_pattern));
+ }
+}
+
+url::Origin GetOrigin(base::StringPiece origin_string) {
+ return !origin_string.empty() ? url::Origin(GURL(origin_string))
+ : url::Origin();
+}
+
+bool IsThirdParty(const GURL& url, const url::Origin& first_party_origin) {
+ return first_party_origin.unique() ||
+ !net::registry_controlled_domains::SameDomainOrHost(
+ url, first_party_origin,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+}
+
+} // namespace testing
+} // namespace url_pattern_index
diff --git a/chromium/components/url_pattern_index/url_rule_test_support.h b/chromium/components/url_pattern_index/url_rule_test_support.h
new file mode 100644
index 00000000000..895f0f8446a
--- /dev/null
+++ b/chromium/components/url_pattern_index/url_rule_test_support.h
@@ -0,0 +1,74 @@
+// 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_URL_PATTERN_INDEX_URL_RULE_TEST_SUPPORT_H_
+#define COMPONENTS_URL_PATTERN_INDEX_URL_RULE_TEST_SUPPORT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "components/url_pattern_index/proto/rules.pb.h"
+#include "components/url_pattern_index/url_pattern.h"
+
+class GURL;
+
+namespace url {
+class Origin;
+}
+
+namespace url_pattern_index {
+namespace testing {
+
+// Constants -------------------------------------------------------------------
+
+constexpr proto::UrlPatternType kSubstring = proto::URL_PATTERN_TYPE_SUBSTRING;
+
+constexpr proto::AnchorType kAnchorNone = proto::ANCHOR_TYPE_NONE;
+constexpr proto::AnchorType kBoundary = proto::ANCHOR_TYPE_BOUNDARY;
+constexpr proto::AnchorType kSubdomain = proto::ANCHOR_TYPE_SUBDOMAIN;
+
+constexpr proto::ElementType kNoElement = proto::ELEMENT_TYPE_UNSPECIFIED;
+constexpr proto::ElementType kOther = proto::ELEMENT_TYPE_OTHER;
+constexpr proto::ElementType kScript = proto::ELEMENT_TYPE_SCRIPT;
+constexpr proto::ElementType kImage = proto::ELEMENT_TYPE_IMAGE;
+constexpr proto::ElementType kSubdocument = proto::ELEMENT_TYPE_SUBDOCUMENT;
+constexpr proto::ElementType kFont = proto::ELEMENT_TYPE_FONT;
+constexpr proto::ElementType kPopup = proto::ELEMENT_TYPE_POPUP;
+constexpr proto::ElementType kWebSocket = proto::ELEMENT_TYPE_WEBSOCKET;
+constexpr proto::ElementType kAllElementTypes = proto::ELEMENT_TYPE_ALL;
+
+constexpr proto::ActivationType kNoActivation =
+ proto::ACTIVATION_TYPE_UNSPECIFIED;
+constexpr proto::ActivationType kDocument = proto::ACTIVATION_TYPE_DOCUMENT;
+constexpr proto::ActivationType kGenericBlock =
+ proto::ACTIVATION_TYPE_GENERICBLOCK;
+
+constexpr proto::SourceType kAnyParty = proto::SOURCE_TYPE_ANY;
+constexpr proto::SourceType kThirdParty = proto::SOURCE_TYPE_THIRD_PARTY;
+constexpr proto::SourceType kFirstParty = proto::SOURCE_TYPE_FIRST_PARTY;
+
+// Helpers ---------------------------------------------------------------------
+
+// Creates a UrlRule with the given |url_pattern|, and all necessary fields
+// initialized to defaults.
+proto::UrlRule MakeUrlRule(const UrlPattern& url_pattern = UrlPattern());
+
+// Parses |domains| and adds them to the domain list of the |rule|.
+//
+// The |domains| vector should contain non-empty strings. If a string starts
+// with '~' then the following part of the string is an exception domain.
+void AddDomains(const std::vector<std::string>& domains, proto::UrlRule* rule);
+
+// Returns the url::Origin parsed from |origin_string|, or the unique origin if
+// the string is empty.
+url::Origin GetOrigin(base::StringPiece origin_string);
+
+// Returns whether |url| is third-party resource w.r.t. |first_party_origin|.
+bool IsThirdParty(const GURL& url, const url::Origin& first_party_origin);
+
+} // namespace testing
+} // namespace url_pattern_index
+
+#endif // COMPONENTS_URL_PATTERN_INDEX_URL_RULE_TEST_SUPPORT_H_
diff --git a/chromium/components/user_manager/fake_user_manager.cc b/chromium/components/user_manager/fake_user_manager.cc
index 80799cc3c33..c046e774c8f 100644
--- a/chromium/components/user_manager/fake_user_manager.cc
+++ b/chromium/components/user_manager/fake_user_manager.cc
@@ -298,7 +298,7 @@ void FakeUserManager::UpdateLoginState(const user_manager::User* active_user,
bool FakeUserManager::GetPlatformKnownUserId(const std::string& user_email,
const std::string& gaia_id,
AccountId* out_account_id) const {
- if (user_email == kStubUser) {
+ if (user_email == kStubUserEmail) {
*out_account_id = StubAccountId();
return true;
}
diff --git a/chromium/components/user_manager/known_user.cc b/chromium/components/user_manager/known_user.cc
index 22db652965c..5e0a156f819 100644
--- a/chromium/components/user_manager/known_user.cc
+++ b/chromium/components/user_manager/known_user.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
#include "base/values.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
@@ -56,6 +57,10 @@ const char kGaiaIdMigration[] = "gaia_id_migration";
// Key of the boolean flag telling if user session has finished init yet.
const char kProfileEverInitialized[] = "profile_ever_initialized";
+// Key of the boolean flag telling if a minimal user home migration has been
+// attempted.
+const char kMinimalMigrationAttempted[] = "minimal_migration_attempted";
+
PrefService* GetLocalState() {
if (!UserManager::IsInitialized())
return nullptr;
@@ -456,8 +461,13 @@ bool IsUsingSAML(const AccountId& account_id) {
bool WasProfileEverInitialized(const AccountId& account_id) {
bool profile_ever_initialized;
- if (GetBooleanPref(account_id, kProfileEverInitialized,
- &profile_ever_initialized))
+ const bool pref_set = GetBooleanPref(account_id, kProfileEverInitialized,
+ &profile_ever_initialized);
+ // TODO(atwilson): Remove migration code below once this UMA stat reports
+ // that migration is completed - crbug.com/736760.
+ UMA_HISTOGRAM_BOOLEAN("UserManager.ProfileEverInitializedMigrationCompleted",
+ pref_set);
+ if (pref_set)
return profile_ever_initialized;
// Sessions created before we started setting the session_initialized flag
@@ -478,6 +488,24 @@ bool FindReauthReason(const AccountId& account_id, int* out_value) {
return GetIntegerPref(account_id, kReauthReasonKey, out_value);
}
+bool WasUserHomeMinimalMigrationAttempted(const AccountId& account_id) {
+ bool minimal_migration_attempted;
+ const bool pref_set = GetBooleanPref(account_id, kMinimalMigrationAttempted,
+ &minimal_migration_attempted);
+ if (pref_set)
+ return minimal_migration_attempted;
+
+ // If we haven't recorded that a minimal migration has been attempted, assume
+ // no.
+ return false;
+}
+
+void SetUserHomeMinimalMigrationAttempted(const AccountId& account_id,
+ bool minimal_migration_attempted) {
+ SetBooleanPref(account_id, kMinimalMigrationAttempted,
+ minimal_migration_attempted);
+}
+
void RemovePrefs(const AccountId& account_id) {
PrefService* local_state = GetLocalState();
diff --git a/chromium/components/user_manager/known_user.h b/chromium/components/user_manager/known_user.h
index da26caaac53..22d9c4bf8cc 100644
--- a/chromium/components/user_manager/known_user.h
+++ b/chromium/components/user_manager/known_user.h
@@ -148,6 +148,15 @@ void USER_MANAGER_EXPORT UpdateReauthReason(const AccountId& account_id,
bool USER_MANAGER_EXPORT FindReauthReason(const AccountId& account_id,
int* out_value);
+// Saves that a minimal migration was attempted for this user's cryptohome.
+void USER_MANAGER_EXPORT
+SetUserHomeMinimalMigrationAttempted(const AccountId& account_id,
+ bool minimal_migration_attempted);
+
+// Returns true if minimal migration was attempted for this user's cryptohome.
+bool USER_MANAGER_EXPORT
+WasUserHomeMinimalMigrationAttempted(const AccountId& account_id);
+
// Removes all user preferences associated with |account_id|.
// Not exported as code should not be calling this outside this component
// (with the exception of tests, so a test-only API is exposed).
diff --git a/chromium/components/user_manager/user.h b/chromium/components/user_manager/user.h
index fecc33bec1d..b8940e62b6a 100644
--- a/chromium/components/user_manager/user.h
+++ b/chromium/components/user_manager/user.h
@@ -66,24 +66,6 @@ class USER_MANAGER_EXPORT User : public UserInfo {
USER_IMAGE_EXTERNAL = -1,
} UserImageType;
- // This enum is used to define the buckets for an enumerated UMA histogram.
- // Hence,
- // (a) existing enumerated constants should never be deleted or reordered,
- // (b) new constants should only be appended at the end of the enumeration.
- enum WallpaperType {
- DAILY = 0, // Surprise wallpaper. Changes once a day if enabled.
- CUSTOMIZED = 1, // Selected by user.
- DEFAULT = 2, // Default.
- /* UNKNOWN = 3 */ // Removed.
- ONLINE = 4, // WallpaperInfo.location denotes an URL.
- POLICY = 5, // Controlled by policy, can't be changed by the user.
- THIRDPARTY = 6, // Current wallpaper is set by a third party app.
- DEVICE = 7, // Current wallpaper is the device policy controlled
- // wallpaper. It shows on the login screen if the device
- // is an enterprise managed device.
- WALLPAPER_TYPE_COUNT = 8
- };
-
// Returns true if user type has gaia account.
static bool TypeHasGaiaAccount(UserType user_type);
@@ -190,6 +172,10 @@ class USER_MANAGER_EXPORT User : public UserInfo {
// True if the user Profile is created.
bool is_profile_created() const { return profile_is_created_; }
+ static User* CreatePublicAccountUserForTesting(const AccountId& account_id) {
+ return CreatePublicAccountUser(account_id);
+ }
+
protected:
friend class UserManagerBase;
friend class chromeos::ChromeUserManagerImpl;
diff --git a/chromium/components/user_manager/user_image/user_image.cc b/chromium/components/user_manager/user_image/user_image.cc
index db293d53b30..55b76866169 100644
--- a/chromium/components/user_manager/user_image/user_image.cc
+++ b/chromium/components/user_manager/user_image/user_image.cc
@@ -27,18 +27,13 @@ scoped_refptr<base::RefCountedBytes> UserImage::Encode(
TRACE_EVENT2("oobe", "UserImage::Encode",
"width", bitmap.width(), "height", bitmap.height());
std::vector<unsigned char> output;
- auto* bitmap_data = reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0));
if (image_format == FORMAT_JPEG) {
- if (gfx::JPEGCodec::Encode(
- bitmap_data,
- gfx::JPEGCodec::FORMAT_SkBitmap,
- bitmap.width(),
- bitmap.height(),
- bitmap.width() * bitmap.bytesPerPixel(),
- kDefaultEncodingQuality, &output)) {
+ if (gfx::JPEGCodec::Encode(bitmap, kDefaultEncodingQuality, &output)) {
return base::RefCountedBytes::TakeVector(&output);
}
} else if (image_format == FORMAT_PNG) {
+ auto* bitmap_data =
+ reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0));
if (gfx::PNGCodec::Encode(
bitmap_data,
gfx::PNGCodec::FORMAT_SkBitmap,
diff --git a/chromium/components/user_manager/user_manager.h b/chromium/components/user_manager/user_manager.h
index b3eb6682bc6..0586d762e95 100644
--- a/chromium/components/user_manager/user_manager.h
+++ b/chromium/components/user_manager/user_manager.h
@@ -329,6 +329,9 @@ class USER_MANAGER_EXPORT UserManager {
// Changes the child status and notifies observers.
virtual void ChangeUserChildStatus(User* user, bool is_child) = 0;
+ // Resets this profile to be regarded as if it has never been initialized
+ // before. Used on profile wipe.
+ virtual void ResetProfileEverInitialized(const AccountId& account_id) = 0;
// Returns true if supervised users allowed.
virtual bool AreSupervisedUsersAllowed() const = 0;
diff --git a/chromium/components/user_manager/user_manager_base.cc b/chromium/components/user_manager/user_manager_base.cc
index db4ac7aeb6f..4b52f9f98fd 100644
--- a/chromium/components/user_manager/user_manager_base.cc
+++ b/chromium/components/user_manager/user_manager_base.cc
@@ -105,7 +105,7 @@ UserManagerBase::~UserManagerBase() {
}
void UserManagerBase::Shutdown() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
}
const UserList& UserManagerBase::GetUsers() const {
@@ -128,7 +128,7 @@ const AccountId& UserManagerBase::GetOwnerAccountId() const {
void UserManagerBase::UserLoggedIn(const AccountId& account_id,
const std::string& username_hash,
bool browser_restart) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
if (!last_session_active_account_id_initialized_) {
last_session_active_account_id_ =
@@ -259,14 +259,14 @@ void UserManagerBase::SwitchToLastActiveUser() {
}
void UserManagerBase::OnSessionStarted() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
CallUpdateLoginState();
GetLocalState()->CommitPendingWrite();
}
void UserManagerBase::OnProfileInitialized(User* user) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
// Mark the user as having an initialized session and persist this in
// the known_user DB.
@@ -277,7 +277,7 @@ void UserManagerBase::OnProfileInitialized(User* user) {
void UserManagerBase::RemoveUser(const AccountId& account_id,
RemoveUserDelegate* delegate) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
if (!CanUserBeRemoved(FindUser(account_id)))
return;
@@ -302,7 +302,7 @@ void UserManagerBase::RemoveNonOwnerUserInternal(const AccountId& account_id,
}
void UserManagerBase::RemoveUserFromList(const AccountId& account_id) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
RemoveNonCryptohomeData(account_id);
if (user_loading_stage_ == STAGE_LOADED) {
DeleteUser(RemoveRegularOrSupervisedUserFromList(account_id));
@@ -328,38 +328,38 @@ bool UserManagerBase::IsKnownUser(const AccountId& account_id) const {
}
const User* UserManagerBase::FindUser(const AccountId& account_id) const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
if (active_user_ && active_user_->GetAccountId() == account_id)
return active_user_;
return FindUserInList(account_id);
}
User* UserManagerBase::FindUserAndModify(const AccountId& account_id) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
if (active_user_ && active_user_->GetAccountId() == account_id)
return active_user_;
return FindUserInListAndModify(account_id);
}
const User* UserManagerBase::GetActiveUser() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return active_user_;
}
User* UserManagerBase::GetActiveUser() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return active_user_;
}
const User* UserManagerBase::GetPrimaryUser() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return primary_user_;
}
void UserManagerBase::SaveUserOAuthStatus(
const AccountId& account_id,
User::OAuthTokenStatus oauth_token_status) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
DVLOG(1) << "Saving user OAuth token status in Local State";
User* user = FindUserAndModify(account_id);
@@ -382,7 +382,11 @@ void UserManagerBase::SaveUserOAuthStatus(
void UserManagerBase::SaveForceOnlineSignin(const AccountId& account_id,
bool force_online_signin) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
+
+ User* const user = FindUserAndModify(account_id);
+ if (user)
+ user->set_force_online_signin(force_online_signin);
// Do not update local state if data stored or cached outside the user's
// cryptohome is to be treated as ephemeral.
@@ -400,7 +404,7 @@ void UserManagerBase::SaveForceOnlineSignin(const AccountId& account_id,
void UserManagerBase::SaveUserDisplayName(const AccountId& account_id,
const base::string16& display_name) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
if (User* user = FindUserAndModify(account_id)) {
user->set_display_name(display_name);
@@ -424,7 +428,7 @@ base::string16 UserManagerBase::GetUserDisplayName(
void UserManagerBase::SaveUserDisplayEmail(const AccountId& account_id,
const std::string& display_email) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
User* user = FindUserAndModify(account_id);
if (!user) {
@@ -452,7 +456,7 @@ std::string UserManagerBase::GetUserDisplayEmail(
void UserManagerBase::SaveUserType(const AccountId& account_id,
const UserType& user_type) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
User* user = FindUserAndModify(account_id);
if (!user) {
@@ -474,7 +478,7 @@ void UserManagerBase::SaveUserType(const AccountId& account_id,
void UserManagerBase::UpdateUserAccountData(
const AccountId& account_id,
const UserAccountData& account_data) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
SaveUserDisplayName(account_id, account_data.display_name());
@@ -517,76 +521,76 @@ void UserManagerBase::ParseUserList(const base::ListValue& users_list,
}
bool UserManagerBase::IsCurrentUserOwner() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return !owner_account_id_.empty() && active_user_ &&
active_user_->GetAccountId() == owner_account_id_;
}
bool UserManagerBase::IsCurrentUserNew() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return is_current_user_new_;
}
bool UserManagerBase::IsCurrentUserNonCryptohomeDataEphemeral() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() &&
IsUserNonCryptohomeDataEphemeral(GetActiveUser()->GetAccountId());
}
bool UserManagerBase::IsCurrentUserCryptohomeDataEphemeral() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() &&
IsUserCryptohomeDataEphemeral(GetActiveUser()->GetAccountId());
}
bool UserManagerBase::CanCurrentUserLock() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && active_user_->can_lock();
}
bool UserManagerBase::IsUserLoggedIn() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return active_user_;
}
bool UserManagerBase::IsLoggedInAsUserWithGaiaAccount() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && active_user_->HasGaiaAccount();
}
bool UserManagerBase::IsLoggedInAsChildUser() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_CHILD;
}
bool UserManagerBase::IsLoggedInAsPublicAccount() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() &&
active_user_->GetType() == USER_TYPE_PUBLIC_ACCOUNT;
}
bool UserManagerBase::IsLoggedInAsGuest() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_GUEST;
}
bool UserManagerBase::IsLoggedInAsSupervisedUser() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_SUPERVISED;
}
bool UserManagerBase::IsLoggedInAsKioskApp() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_KIOSK_APP;
}
bool UserManagerBase::IsLoggedInAsArcKioskApp() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_ARC_KIOSK_APP;
}
bool UserManagerBase::IsLoggedInAsStub() const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
return IsUserLoggedIn() && IsStubAccountId(active_user_->GetAccountId());
}
@@ -648,41 +652,41 @@ bool UserManagerBase::IsUserCryptohomeDataEphemeral(
}
void UserManagerBase::AddObserver(UserManager::Observer* obs) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
observer_list_.AddObserver(obs);
}
void UserManagerBase::RemoveObserver(UserManager::Observer* obs) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
observer_list_.RemoveObserver(obs);
}
void UserManagerBase::AddSessionStateObserver(
UserManager::UserSessionStateObserver* obs) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
session_state_observer_list_.AddObserver(obs);
}
void UserManagerBase::RemoveSessionStateObserver(
UserManager::UserSessionStateObserver* obs) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
session_state_observer_list_.RemoveObserver(obs);
}
void UserManagerBase::NotifyLocalStateChanged() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : observer_list_)
observer.LocalStateChanged(this);
}
void UserManagerBase::NotifyUserImageChanged(const User& user) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : observer_list_)
observer.OnUserImageChanged(user);
}
void UserManagerBase::NotifyUserProfileImageUpdateFailed(const User& user) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : observer_list_)
observer.OnUserProfileImageUpdateFailed(user);
}
@@ -690,7 +694,7 @@ void UserManagerBase::NotifyUserProfileImageUpdateFailed(const User& user) {
void UserManagerBase::NotifyUserProfileImageUpdated(
const User& user,
const gfx::ImageSkia& profile_image) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : observer_list_)
observer.OnUserProfileImageUpdated(user, profile_image);
}
@@ -752,7 +756,7 @@ void UserManagerBase::SetPendingUserSwitchId(const AccountId& account_id) {
}
void UserManagerBase::EnsureUsersLoaded() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
if (!GetLocalState())
return;
@@ -863,7 +867,7 @@ User* UserManagerBase::FindUserInListAndModify(const AccountId& account_id) {
}
void UserManagerBase::GuestUserLoggedIn() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
active_user_ = User::CreateGuestUser(GetGuestAccountId());
}
@@ -898,14 +902,14 @@ void UserManagerBase::RegularUserLoggedIn(const AccountId& account_id) {
void UserManagerBase::RegularUserLoggedInAsEphemeral(
const AccountId& account_id) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
SetIsCurrentUserNew(true);
is_current_user_ephemeral_regular_user_ = true;
active_user_ = User::CreateRegularUser(account_id);
}
void UserManagerBase::NotifyOnLogin() {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
NotifyActiveUserHashChanged(active_user_->username_hash());
NotifyActiveUserChanged(active_user_);
@@ -914,7 +918,7 @@ void UserManagerBase::NotifyOnLogin() {
User::OAuthTokenStatus UserManagerBase::LoadUserOAuthStatus(
const AccountId& account_id) const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
const base::DictionaryValue* prefs_oauth_status =
GetLocalState()->GetDictionary(kUserOAuthTokenStatus);
@@ -932,7 +936,7 @@ User::OAuthTokenStatus UserManagerBase::LoadUserOAuthStatus(
}
bool UserManagerBase::LoadForceOnlineSignin(const AccountId& account_id) const {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
const base::DictionaryValue* prefs_force_online =
GetLocalState()->GetDictionary(kUserForceOnlineSignin);
@@ -997,26 +1001,26 @@ User* UserManagerBase::RemoveRegularOrSupervisedUserFromList(
}
void UserManagerBase::NotifyActiveUserChanged(const User* active_user) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : session_state_observer_list_)
observer.ActiveUserChanged(active_user);
}
void UserManagerBase::NotifyUserAddedToSession(const User* added_user,
bool user_switch_pending) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : session_state_observer_list_)
observer.UserAddedToSession(added_user);
}
void UserManagerBase::NotifyActiveUserHashChanged(const std::string& hash) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : session_state_observer_list_)
observer.ActiveUserHashChanged(hash);
}
void UserManagerBase::ChangeUserChildStatus(User* user, bool is_child) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
if (user->IsSupervised() == is_child)
return;
user->SetIsChild(is_child);
@@ -1027,6 +1031,18 @@ void UserManagerBase::ChangeUserChildStatus(User* user, bool is_child) {
observer.UserChangedChildStatus(user);
}
+void UserManagerBase::ResetProfileEverInitialized(const AccountId& account_id) {
+ User* user = FindUserAndModify(account_id);
+ if (!user) {
+ LOG(ERROR) << "User not found: " << account_id.GetUserEmail();
+ return; // Ignore if there is no such user.
+ }
+
+ user->set_profile_ever_initialized(false);
+ known_user::SetProfileEverInitialized(user->GetAccountId(), false);
+ GetLocalState()->CommitPendingWrite();
+}
+
void UserManagerBase::Initialize() {
UserManager::Initialize();
CallUpdateLoginState();
diff --git a/chromium/components/user_manager/user_manager_base.h b/chromium/components/user_manager/user_manager_base.h
index 70776a72077..da6898fb8f1 100644
--- a/chromium/components/user_manager/user_manager_base.h
+++ b/chromium/components/user_manager/user_manager_base.h
@@ -111,6 +111,7 @@ class USER_MANAGER_EXPORT UserManagerBase : public UserManager {
const User& user,
const gfx::ImageSkia& profile_image) override;
void ChangeUserChildStatus(User* user, bool is_child) override;
+ void ResetProfileEverInitialized(const AccountId& account_id) override;
void Initialize() override;
// This method updates "User was added to the device in this session nad is
@@ -132,6 +133,10 @@ class USER_MANAGER_EXPORT UserManagerBase : public UserManager {
// and ephemeral users are enabled.
virtual bool AreEphemeralUsersEnabled() const = 0;
+ void AddUserRecordForTesting(User* user) {
+ return AddUserRecord(user);
+ }
+
protected:
// Adds |user| to users list, and adds it to front of LRU list. It is assumed
// that there is no user with same id.
diff --git a/chromium/components/user_manager/user_names.cc b/chromium/components/user_manager/user_names.cc
index b873a3da5d9..5342a5e8b2c 100644
--- a/chromium/components/user_manager/user_names.cc
+++ b/chromium/components/user_manager/user_names.cc
@@ -34,27 +34,25 @@ class FixedAccountManager {
private:
friend struct base::DefaultSingletonTraits<FixedAccountManager>;
- FixedAccountManager();
+ FixedAccountManager() = default;
- const AccountId stub_account_id_;
- const AccountId signin_account_id_;
- const AccountId guest_account_id_;
- const AccountId demo_account_id_;
+ const AccountId stub_account_id_ =
+ AccountId::FromUserEmailGaiaId(user_manager::kStubUserEmail,
+ user_manager::kStubUserId);
+ const AccountId signin_account_id_ = AccountId::FromUserEmail(kSignInUser);
+ const AccountId guest_account_id_ =
+ AccountId::FromUserEmail(user_manager::kGuestUserName);
+ const AccountId demo_account_id_ = AccountId::FromUserEmail(kDemoUserName);
DISALLOW_COPY_AND_ASSIGN(FixedAccountManager);
};
-FixedAccountManager::FixedAccountManager()
- : stub_account_id_(AccountId::FromUserEmail(user_manager::kStubUser)),
- signin_account_id_(AccountId::FromUserEmail(kSignInUser)),
- guest_account_id_(AccountId::FromUserEmail(user_manager::kGuestUserName)),
- demo_account_id_(AccountId::FromUserEmail(kDemoUserName)) {}
-
} // namespace
namespace user_manager {
-const char kStubUser[] = "stub-user@example.com";
+const char kStubUserEmail[] = "stub-user@example.com";
+const char kStubUserId[] = "1234567890123456789012";
// Should match cros constant in platform/libchromeos/chromeos/cryptohome.h
const char kGuestUserName[] = "$guest";
diff --git a/chromium/components/user_manager/user_names.h b/chromium/components/user_manager/user_names.h
index a3c9476e8f5..d56880a834f 100644
--- a/chromium/components/user_manager/user_names.h
+++ b/chromium/components/user_manager/user_names.h
@@ -13,9 +13,11 @@ class AccountId;
namespace user_manager {
-// Username for stub login for tests only.
-// It is also used for Chrome for ChromeOS linux dev build.
-USER_MANAGER_EXPORT extern const char kStubUser[];
+// Stub user name. For tests and CrOS on Linux dev build only.
+USER_MANAGER_EXPORT extern const char kStubUserEmail[];
+
+// Stub user id. For tests and CrOS on Linux dev build only.
+USER_MANAGER_EXPORT extern const char kStubUserId[];
// Magic e-mail addresses are bad. They exist here because some code already
// depends on them and it is hard to figure out what. Any user types added in
diff --git a/chromium/components/variations/BUILD.gn b/chromium/components/variations/BUILD.gn
index 38d6e3e5ce3..04ab95dbeff 100644
--- a/chromium/components/variations/BUILD.gn
+++ b/chromium/components/variations/BUILD.gn
@@ -20,12 +20,17 @@ static_library("variations") {
"caching_permuted_entropy_provider.h",
"child_process_field_trial_syncer.cc",
"child_process_field_trial_syncer.h",
+ "client_filterable_state.cc",
+ "client_filterable_state.h",
"entropy_provider.cc",
"entropy_provider.h",
"experiment_labels.cc",
"experiment_labels.h",
+ "metrics.cc",
+ "metrics.h",
"metrics_util.cc",
"metrics_util.h",
+ "platform_field_trials.h",
"pref_names.cc",
"pref_names.h",
"processed_study.cc",
@@ -36,8 +41,12 @@ static_library("variations") {
"proto/variations_seed.proto",
"study_filtering.cc",
"study_filtering.h",
+ "synthetic_trial_registry.cc",
+ "synthetic_trial_registry.h",
"synthetic_trials.cc",
"synthetic_trials.h",
+ "synthetic_trials_active_group_id_provider.cc",
+ "synthetic_trials_active_group_id_provider.h",
"variations_associated_data.cc",
"variations_associated_data.h",
"variations_experiment_util.cc",
@@ -105,6 +114,7 @@ static_library("test_support") {
]
deps = [
+ "field_trial_config:field_trial_config",
"//base/test:test_support",
]
}
@@ -120,6 +130,7 @@ source_set("unit_tests") {
"metrics_util_unittest.cc",
"net/variations_http_headers_unittest.cc",
"study_filtering_unittest.cc",
+ "synthetic_trial_registry_unittest.cc",
"variations_associated_data_unittest.cc",
"variations_http_header_provider_unittest.cc",
"variations_request_scheduler_unittest.cc",
diff --git a/chromium/components/variations/active_field_trials.cc b/chromium/components/variations/active_field_trials.cc
index 07c8b6b599e..d71d6e6b39a 100644
--- a/chromium/components/variations/active_field_trials.cc
+++ b/chromium/components/variations/active_field_trials.cc
@@ -11,61 +11,79 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/variations/metrics_util.h"
+#include "components/variations/synthetic_trials_active_group_id_provider.h"
namespace variations {
namespace {
-// Populates |name_group_ids| based on |active_groups|.
+// Populates |name_group_ids| based on |active_groups|. Field trial names are
+// suffixed with |suffix| before hashing is executed.
void GetFieldTrialActiveGroupIdsForActiveGroups(
+ base::StringPiece suffix,
const base::FieldTrial::ActiveGroups& active_groups,
std::vector<ActiveGroupId>* name_group_ids) {
DCHECK(name_group_ids->empty());
- for (base::FieldTrial::ActiveGroups::const_iterator it =
- active_groups.begin(); it != active_groups.end(); ++it) {
- name_group_ids->push_back(MakeActiveGroupId(it->trial_name,
- it->group_name));
+ for (auto it = active_groups.begin(); it != active_groups.end(); ++it) {
+ name_group_ids->push_back(
+ MakeActiveGroupId(it->trial_name + suffix.as_string(),
+ it->group_name + suffix.as_string()));
+ }
+}
+
+void AppendActiveGroupIdsAsStrings(
+ const std::vector<ActiveGroupId> name_group_ids,
+ std::vector<std::string>* output) {
+ for (const auto& active_group_id : name_group_ids) {
+ output->push_back(base::StringPrintf("%x-%x", active_group_id.name,
+ active_group_id.group));
}
}
} // namespace
-ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
- const std::string& group_name) {
+ActiveGroupId MakeActiveGroupId(base::StringPiece trial_name,
+ base::StringPiece group_name) {
ActiveGroupId id;
id.name = metrics::HashName(trial_name);
id.group = metrics::HashName(group_name);
return id;
}
-void GetFieldTrialActiveGroupIds(
- std::vector<ActiveGroupId>* name_group_ids) {
+void GetFieldTrialActiveGroupIds(base::StringPiece suffix,
+ std::vector<ActiveGroupId>* name_group_ids) {
DCHECK(name_group_ids->empty());
// A note on thread safety: Since GetActiveFieldTrialGroups() is thread
// safe, and we operate on a separate list of that data, this function is
// technically thread safe as well, with respect to the FieldTrialList data.
base::FieldTrial::ActiveGroups active_groups;
base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
- GetFieldTrialActiveGroupIdsForActiveGroups(active_groups,
+ GetFieldTrialActiveGroupIdsForActiveGroups(suffix, active_groups,
name_group_ids);
}
-void GetFieldTrialActiveGroupIdsAsStrings(std::vector<std::string>* output) {
+void GetFieldTrialActiveGroupIdsAsStrings(base::StringPiece suffix,
+ std::vector<std::string>* output) {
DCHECK(output->empty());
std::vector<ActiveGroupId> name_group_ids;
- GetFieldTrialActiveGroupIds(&name_group_ids);
- for (size_t i = 0; i < name_group_ids.size(); ++i) {
- output->push_back(base::StringPrintf(
- "%x-%x", name_group_ids[i].name, name_group_ids[i].group));
- }
+ GetFieldTrialActiveGroupIds(suffix, &name_group_ids);
+ AppendActiveGroupIdsAsStrings(name_group_ids, output);
+}
+
+void GetSyntheticTrialGroupIdsAsString(std::vector<std::string>* output) {
+ std::vector<ActiveGroupId> name_group_ids;
+ SyntheticTrialsActiveGroupIdProvider::GetInstance()->GetActiveGroupIds(
+ &name_group_ids);
+ AppendActiveGroupIdsAsStrings(name_group_ids, output);
}
namespace testing {
void TestGetFieldTrialActiveGroupIds(
+ base::StringPiece suffix,
const base::FieldTrial::ActiveGroups& active_groups,
std::vector<ActiveGroupId>* name_group_ids) {
- GetFieldTrialActiveGroupIdsForActiveGroups(active_groups,
+ GetFieldTrialActiveGroupIdsForActiveGroups(suffix, active_groups,
name_group_ids);
}
diff --git a/chromium/components/variations/active_field_trials.h b/chromium/components/variations/active_field_trials.h
index b7425510a10..ecadf08a376 100644
--- a/chromium/components/variations/active_field_trials.h
+++ b/chromium/components/variations/active_field_trials.h
@@ -10,6 +10,7 @@
#include <string>
#include "base/metrics/field_trial.h"
+#include "base/strings/string_piece.h"
namespace variations {
@@ -21,8 +22,8 @@ struct ActiveGroupId {
};
// Returns an ActiveGroupId struct for the given trial and group names.
-ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
- const std::string& group_name);
+ActiveGroupId MakeActiveGroupId(base::StringPiece trial_name,
+ base::StringPiece group_name);
// We need to supply a Compare class for templates since ActiveGroupId is a
// user-defined type.
@@ -40,21 +41,34 @@ struct ActiveGroupIdCompare {
// Fills the supplied vector |name_group_ids| (which must be empty when called)
// with unique ActiveGroupIds for each Field Trial that has a chosen group.
// Field Trials for which a group has not been chosen yet are NOT returned in
-// this list.
-void GetFieldTrialActiveGroupIds(std::vector<ActiveGroupId>* name_group_ids);
+// this list. Field trial names are suffixed with |suffix| before hashing is
+// executed.
+void GetFieldTrialActiveGroupIds(base::StringPiece suffix,
+ std::vector<ActiveGroupId>* name_group_ids);
// Fills the supplied vector |output| (which must be empty when called) with
// unique string representations of ActiveGroupIds for each Field Trial that
// has a chosen group. The strings are formatted as "<TrialName>-<GroupName>",
// with the names as hex strings. Field Trials for which a group has not been
-// chosen yet are NOT returned in this list.
-void GetFieldTrialActiveGroupIdsAsStrings(std::vector<std::string>* output);
+// chosen yet are NOT returned in this list. Field trial names are suffixed with
+// |suffix| before hashing is executed.
+void GetFieldTrialActiveGroupIdsAsStrings(base::StringPiece suffix,
+ std::vector<std::string>* output);
+
+// TODO(rkaplow): Support suffixing for synthetic trials.
+// Fills the supplied vector |output| (which must be empty when called) with
+// unique string representations of ActiveGroupIds for each Syntehtic Trial
+// group. The strings are formatted as "<TrialName>-<GroupName>",
+// with the names as hex strings. Synthetic Field Trials for which a group
+// which hasn't been chosen yet are NOT returned in this list.
+void GetSyntheticTrialGroupIdsAsString(std::vector<std::string>* output);
// Expose some functions for testing. These functions just wrap functionality
// that is implemented above.
namespace testing {
void TestGetFieldTrialActiveGroupIds(
+ base::StringPiece suffix,
const base::FieldTrial::ActiveGroups& active_groups,
std::vector<ActiveGroupId>* name_group_ids);
diff --git a/chromium/components/variations/active_field_trials_unittest.cc b/chromium/components/variations/active_field_trials_unittest.cc
index 548b09ce76f..40909e37f49 100644
--- a/chromium/components/variations/active_field_trials_unittest.cc
+++ b/chromium/components/variations/active_field_trials_unittest.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include "base/strings/string_piece.h"
#include "components/variations/metrics_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -39,7 +40,8 @@ TEST(ActiveFieldTrialsTest, GetFieldTrialActiveGroups) {
expected_groups.insert(name_group_id);
std::vector<ActiveGroupId> active_group_ids;
- testing::TestGetFieldTrialActiveGroupIds(active_groups, &active_group_ids);
+ testing::TestGetFieldTrialActiveGroupIds(base::StringPiece(), active_groups,
+ &active_group_ids);
EXPECT_EQ(2U, active_group_ids.size());
for (size_t i = 0; i < active_group_ids.size(); ++i) {
ActiveGroupIdSet::iterator expected_group =
@@ -50,4 +52,26 @@ TEST(ActiveFieldTrialsTest, GetFieldTrialActiveGroups) {
EXPECT_EQ(0U, expected_groups.size());
}
+TEST(ActiveFieldTrialsTest, GetFieldTrialActiveGroupsWithSuffix) {
+ std::string trial_one("trial one");
+ std::string group_one("group one");
+ std::string suffix("some_suffix");
+
+ base::FieldTrial::ActiveGroups active_groups;
+ base::FieldTrial::ActiveGroup active_group;
+ active_group.trial_name = trial_one;
+ active_group.group_name = group_one;
+ active_groups.push_back(active_group);
+
+ std::vector<ActiveGroupId> active_group_ids;
+ testing::TestGetFieldTrialActiveGroupIds(suffix, active_groups,
+ &active_group_ids);
+ EXPECT_EQ(1U, active_group_ids.size());
+
+ uint32_t expected_name = metrics::HashName("trial onesome_suffix");
+ uint32_t expected_group = metrics::HashName("group onesome_suffix");
+ EXPECT_EQ(expected_name, active_group_ids[0].name);
+ EXPECT_EQ(expected_group, active_group_ids[0].group);
+}
+
} // namespace variations
diff --git a/chromium/components/variations/android/variations_associated_data_android.cc b/chromium/components/variations/android/variations_associated_data_android.cc
index 1dd80969781..ebbb0a748fd 100644
--- a/chromium/components/variations/android/variations_associated_data_android.cc
+++ b/chromium/components/variations/android/variations_associated_data_android.cc
@@ -17,7 +17,6 @@ using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace variations {
-
namespace android {
ScopedJavaLocalRef<jstring> GetVariationParamValue(
@@ -45,5 +44,4 @@ bool RegisterVariationsAssociatedData(JNIEnv* env) {
}
} // namespace android
-
} // namespace variations
diff --git a/chromium/components/variations/child_process_field_trial_syncer.cc b/chromium/components/variations/child_process_field_trial_syncer.cc
index 49014e08109..9315825ed8f 100644
--- a/chromium/components/variations/child_process_field_trial_syncer.cc
+++ b/chromium/components/variations/child_process_field_trial_syncer.cc
@@ -20,13 +20,7 @@ ChildProcessFieldTrialSyncer::ChildProcessFieldTrialSyncer(
ChildProcessFieldTrialSyncer::~ChildProcessFieldTrialSyncer() {}
void ChildProcessFieldTrialSyncer::InitFieldTrialObserving(
- const base::CommandLine& command_line,
- const char* single_process_switch_name) {
- // In single-process mode, there is no need to synchronize trials to the
- // browser process (because it's the same process), so this class is a no-op.
- if (command_line.HasSwitch(single_process_switch_name))
- return;
-
+ const base::CommandLine& command_line) {
// Set up initial set of crash dump data for field trials in this process.
SetVariationListCrashKeys();
diff --git a/chromium/components/variations/child_process_field_trial_syncer.h b/chromium/components/variations/child_process_field_trial_syncer.h
index 1a93d368df9..22f946513ae 100644
--- a/chromium/components/variations/child_process_field_trial_syncer.h
+++ b/chromium/components/variations/child_process_field_trial_syncer.h
@@ -31,11 +31,8 @@ class ChildProcessFieldTrialSyncer {
~ChildProcessFieldTrialSyncer();
// Initializes field trial state change observation and notifies the browser
- // of any field trials that might have already been activated. Takes the
- // switch name for single process mode as a parameter, since that's not
- // visible to the component.
- void InitFieldTrialObserving(const base::CommandLine& command_line,
- const char* single_process_switch_name);
+ // of any field trials that might have already been activated.
+ void InitFieldTrialObserving(const base::CommandLine& command_line);
// Handler for messages from the browser process notifying the child process
// that a field trial was activated.
diff --git a/chromium/components/variations/child_process_field_trial_syncer_unittest.cc b/chromium/components/variations/child_process_field_trial_syncer_unittest.cc
index 49b574ce8f6..dc8e52ddaa4 100644
--- a/chromium/components/variations/child_process_field_trial_syncer_unittest.cc
+++ b/chromium/components/variations/child_process_field_trial_syncer_unittest.cc
@@ -76,8 +76,7 @@ TEST(ChildProcessFieldTrialSyncerTest, FieldTrialState) {
TestFieldTrialObserver observer;
ChildProcessFieldTrialSyncer syncer(&observer);
- syncer.InitFieldTrialObserving(*base::CommandLine::ForCurrentProcess(),
- "single_process");
+ syncer.InitFieldTrialObserving(*base::CommandLine::ForCurrentProcess());
// The observer should be notified of activated entries that were not activate
// on the command line. In this case, trial 2. (Trial 1 was already active via
diff --git a/chromium/components/variations/client_filterable_state.cc b/chromium/components/variations/client_filterable_state.cc
new file mode 100644
index 00000000000..0e36db82e70
--- /dev/null
+++ b/chromium/components/variations/client_filterable_state.cc
@@ -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.
+
+#include "components/variations/client_filterable_state.h"
+
+#include "build/build_config.h"
+
+namespace variations {
+
+// static
+Study::Platform ClientFilterableState::GetCurrentPlatform() {
+#if defined(OS_WIN)
+ return Study::PLATFORM_WINDOWS;
+#elif defined(OS_IOS)
+ return Study::PLATFORM_IOS;
+#elif defined(OS_MACOSX)
+ return Study::PLATFORM_MAC;
+#elif defined(OS_CHROMEOS)
+ return Study::PLATFORM_CHROMEOS;
+#elif defined(OS_ANDROID)
+ return Study::PLATFORM_ANDROID;
+#elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
+ // Default BSD and SOLARIS to Linux to not break those builds, although these
+ // platforms are not officially supported by Chrome.
+ return Study::PLATFORM_LINUX;
+#else
+#error Unknown platform
+#endif
+}
+
+ClientFilterableState::ClientFilterableState() {}
+ClientFilterableState::~ClientFilterableState() {}
+
+} // namespace variations
diff --git a/chromium/components/variations/client_filterable_state.h b/chromium/components/variations/client_filterable_state.h
new file mode 100644
index 00000000000..20ebaa98476
--- /dev/null
+++ b/chromium/components/variations/client_filterable_state.h
@@ -0,0 +1,60 @@
+// 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_VARIATIONS_CLIENT_FILTERABLE_STATE_H_
+#define COMPONENTS_VARIATIONS_CLIENT_FILTERABLE_STATE_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/version.h"
+#include "components/variations/proto/study.pb.h"
+
+namespace variations {
+
+// A contianer for all of the client state which is used for filtering studies.
+struct ClientFilterableState {
+ static Study::Platform GetCurrentPlatform();
+
+ ClientFilterableState();
+ ~ClientFilterableState();
+
+ // The system locale.
+ std::string locale;
+
+ // The date on which the variations seed was fetched.
+ base::Time reference_date;
+
+ // The Chrome version to filter on.
+ base::Version version;
+
+ // The Channel for this Chrome installation.
+ Study::Channel channel;
+
+ // The hardware form factor that Chrome is running on.
+ Study::FormFactor form_factor;
+
+ // The OS on which Chrome is running.
+ Study::Platform platform;
+
+ // The named hardware configuration that Chrome is running on -- used to
+ // identify models of devices.
+ std::string hardware_class;
+
+ // Whether this is a low-end device. Currently only supported on Android.
+ // Based on base::SysInfo::IsLowEndDevice().
+ bool is_low_end_device = false;
+
+ // The country code to use for studies configured with session consistency.
+ std::string session_consistency_country;
+
+ // The country code to use for studies configured with permanent consistency.
+ std::string permanent_consistency_country;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ClientFilterableState);
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_CLIENT_FILTERABLE_STATE_H_
diff --git a/chromium/components/variations/experiment_labels.cc b/chromium/components/variations/experiment_labels.cc
index c7cce9518b8..1b0d85a5c96 100644
--- a/chromium/components/variations/experiment_labels.cc
+++ b/chromium/components/variations/experiment_labels.cc
@@ -15,7 +15,6 @@
#include "components/variations/variations_experiment_util.h"
namespace variations {
-
namespace {
const char kVariationPrefix[] = "CrVar";
@@ -25,7 +24,7 @@ const char kVariationPrefix[] = "CrVar";
base::string16 ExtractNonVariationLabels(const base::string16& labels) {
// First, split everything by the label separator.
std::vector<base::StringPiece16> entries = base::SplitStringPiece(
- labels, base::StringPiece16(&variations::kExperimentLabelSeparator, 1),
+ labels, base::StringPiece16(&kExperimentLabelSeparator, 1),
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
// For each label, keep the ones that do not look like a Variations label.
@@ -40,7 +39,7 @@ base::string16 ExtractNonVariationLabels(const base::string16& labels) {
// Dump the whole thing, including the timestamp.
if (!non_variation_labels.empty())
- non_variation_labels += variations::kExperimentLabelSeparator;
+ non_variation_labels += kExperimentLabelSeparator;
entry.AppendToString(&non_variation_labels);
}
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 762991515ea..761ba237aca 100644
--- a/chromium/components/variations/field_trial_config/field_trial_util.cc
+++ b/chromium/components/variations/field_trial_config/field_trial_util.cc
@@ -19,10 +19,9 @@
#include "net/base/escape.h"
namespace variations {
-
namespace {
-std::string EscapeValue(const std::string& value) {
+std::string UnescapeValue(const std::string& value) {
return net::UnescapeURLComponent(
value, net::UnescapeRule::PATH_SEPARATORS |
net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
@@ -38,7 +37,7 @@ void AssociateParamsFromExperiment(
const FieldTrialTestingExperimentParams& param = experiment.params[i];
params[param.key] = param.value;
}
- variations::AssociateVariationParams(study_name, experiment.name, params);
+ AssociateVariationParams(study_name, experiment.name, params);
}
base::FieldTrial* trial =
base::FieldTrialList::CreateFieldTrial(study_name, experiment.name);
@@ -84,6 +83,12 @@ const FieldTrialTestingExperiment& ChooseExperiment(
} // namespace
+std::string EscapeValue(const std::string& value) {
+ // This needs to be the inverse of UnescapeValue in the anonymous namespace
+ // above.
+ return net::EscapeQueryParamValue(value, true /* use_plus */);
+}
+
bool AssociateParamsFromString(const std::string& varations_string) {
// Format: Trial1.Group1:k1/v1/k2/v2,Trial2.Group2:k1/v1/k2/v2
std::set<std::pair<std::string, std::string>> trial_groups;
@@ -110,8 +115,8 @@ bool AssociateParamsFromString(const std::string& varations_string) {
DLOG(ERROR) << "Param name and param value should be separated by '/'";
return false;
}
- std::string trial = EscapeValue(group_parts[0]);
- std::string group = EscapeValue(group_parts[1]);
+ std::string trial = UnescapeValue(group_parts[0]);
+ std::string group = UnescapeValue(group_parts[1]);
auto trial_group = std::make_pair(trial, group);
if (trial_groups.find(trial_group) != trial_groups.end()) {
DLOG(ERROR) << base::StringPrintf(
@@ -122,11 +127,11 @@ bool AssociateParamsFromString(const std::string& varations_string) {
trial_groups.insert(trial_group);
std::map<std::string, std::string> params;
for (size_t i = 0; i < key_values.size(); i += 2) {
- std::string key = EscapeValue(key_values[i]);
- std::string value = EscapeValue(key_values[i + 1]);
+ std::string key = UnescapeValue(key_values[i]);
+ std::string value = UnescapeValue(key_values[i + 1]);
params[key] = value;
}
- variations::AssociateVariationParams(trial, group, params);
+ AssociateVariationParams(trial, group, params);
}
return true;
}
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 e9487c4ef67..8bb44eded02 100644
--- a/chromium/components/variations/field_trial_config/field_trial_util.h
+++ b/chromium/components/variations/field_trial_config/field_trial_util.h
@@ -15,6 +15,10 @@ namespace variations {
struct FieldTrialTestingConfig;
+// Escapes the trial name, or parameter name, or parameter value in a way that
+// makes it usable within variations::switches::kForceFieldTrialParams.
+std::string EscapeValue(const std::string& value);
+
// Provides a mechanism to associate multiple set of params to multiple groups
// with a formatted string specified from commandline. See
// kForceFieldTrialParams in components/variations/variations_switches.cc for
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 2101280ef6b..00fc288f672 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
@@ -16,7 +16,6 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace variations {
-
namespace {
class FieldTrialUtilTest : public ::testing::Test {
@@ -26,8 +25,8 @@ class FieldTrialUtilTest : public ::testing::Test {
~FieldTrialUtilTest() override {
// Ensure that the maps are cleared between tests, since they are stored as
// process singletons.
- variations::testing::ClearAllVariationIDs();
- variations::testing::ClearAllVariationParams();
+ testing::ClearAllVariationIDs();
+ testing::ClearAllVariationParams();
}
private:
@@ -45,12 +44,12 @@ TEST_F(FieldTrialUtilTest, AssociateParamsFromString) {
ASSERT_TRUE(AssociateParamsFromString(kVariationsString));
base::FieldTrialList::CreateFieldTrial(kTrialName, "B");
- EXPECT_EQ("/", variations::GetVariationParamValue(kTrialName, "a"));
- EXPECT_EQ(std::string(), variations::GetVariationParamValue(kTrialName, "b"));
- EXPECT_EQ(std::string(), variations::GetVariationParamValue(kTrialName, "x"));
+ EXPECT_EQ("/", GetVariationParamValue(kTrialName, "a"));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
std::map<std::string, std::string> params;
- EXPECT_TRUE(variations::GetVariationParams(kTrialName, &params));
+ EXPECT_TRUE(GetVariationParams(kTrialName, &params));
EXPECT_EQ(1U, params.size());
EXPECT_EQ("/", params["a"]);
}
@@ -88,11 +87,11 @@ TEST_F(FieldTrialUtilTest, AssociateParamsFromFieldTrialConfig) {
base::FeatureList feature_list;
AssociateParamsFromFieldTrialConfig(kConfig, &feature_list);
- EXPECT_EQ("1", variations::GetVariationParamValue("TestTrial1", "x"));
- EXPECT_EQ("2", variations::GetVariationParamValue("TestTrial1", "y"));
+ EXPECT_EQ("1", GetVariationParamValue("TestTrial1", "x"));
+ EXPECT_EQ("2", GetVariationParamValue("TestTrial1", "y"));
std::map<std::string, std::string> params;
- EXPECT_TRUE(variations::GetVariationParams("TestTrial1", &params));
+ EXPECT_TRUE(GetVariationParams("TestTrial1", &params));
EXPECT_EQ(2U, params.size());
EXPECT_EQ("1", params["x"]);
EXPECT_EQ("2", params["y"]);
diff --git a/chromium/components/variations/metrics.cc b/chromium/components/variations/metrics.cc
new file mode 100644
index 00000000000..a59d3d6920e
--- /dev/null
+++ b/chromium/components/variations/metrics.cc
@@ -0,0 +1,28 @@
+// 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/variations/metrics.h"
+
+#include "base/metrics/histogram_macros.h"
+
+namespace variations {
+
+#if defined(OS_ANDROID)
+void RecordFirstRunSeedImportResult(FirstRunSeedImportResult result) {
+ UMA_HISTOGRAM_ENUMERATION("Variations.FirstRunResult", result,
+ FirstRunSeedImportResult::ENUM_SIZE);
+}
+#endif // OS_ANDROID
+
+void RecordLoadSeedResult(LoadSeedResult state) {
+ UMA_HISTOGRAM_ENUMERATION("Variations.SeedLoadResult", state,
+ LoadSeedResult::ENUM_SIZE);
+}
+
+void RecordStoreSeedResult(StoreSeedResult result) {
+ UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result,
+ StoreSeedResult::ENUM_SIZE);
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/metrics.h b/chromium/components/variations/metrics.h
new file mode 100644
index 00000000000..28e846da512
--- /dev/null
+++ b/chromium/components/variations/metrics.h
@@ -0,0 +1,95 @@
+// 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_VARIATIONS_METRICS_H_
+#define COMPONENTS_VARIATIONS_METRICS_H_
+
+#include "build/build_config.h"
+
+namespace variations {
+
+#if defined(OS_ANDROID)
+// The result of importing a seed during Android first run.
+// Note: UMA histogram enum - don't re-order or remove entries.
+enum class FirstRunSeedImportResult {
+ SUCCESS,
+ FAIL_NO_CALLBACK,
+ FAIL_NO_FIRST_RUN_SEED,
+ FAIL_STORE_FAILED,
+ FAIL_INVALID_RESPONSE_DATE,
+ ENUM_SIZE
+};
+#endif // OS_ANDROID
+
+// The result of attempting to load a variations seed on startup.
+// Note: UMA histogram enum - don't re-order or remove entries.
+enum class LoadSeedResult {
+ SUCCESS,
+ EMPTY,
+ CORRUPT,
+ INVALID_SIGNATURE,
+ CORRUPT_BASE64,
+ CORRUPT_PROTOBUF,
+ CORRUPT_GZIP,
+ ENUM_SIZE
+};
+
+// The result of attempting to store a variations seed received from the server.
+// Note: UMA histogram enum - don't re-order or remove entries.
+enum class StoreSeedResult {
+ SUCCESS,
+ FAILED_EMPTY,
+ FAILED_PARSE,
+ FAILED_SIGNATURE,
+ FAILED_GZIP,
+ // DELTA_COUNT is not so much a result of the seed store, but rather counting
+ // the number of delta-compressed seeds the SeedStore() function saw. Kept in
+ // the same histogram for convenience of comparing against the other values.
+ DELTA_COUNT,
+ FAILED_DELTA_READ_SEED,
+ FAILED_DELTA_APPLY,
+ FAILED_DELTA_STORE,
+ FAILED_UNGZIP,
+ FAILED_EMPTY_GZIP_CONTENTS,
+ FAILED_UNSUPPORTED_SEED_FORMAT,
+ ENUM_SIZE
+};
+
+// The result of updating the date associated with an existing stored variations
+// seed.
+// Note: UMA histogram enum - don't re-order or remove entries.
+enum class UpdateSeedDateResult {
+ NO_OLD_DATE,
+ NEW_DATE_IS_OLDER,
+ SAME_DAY,
+ NEW_DAY,
+ ENUM_SIZE
+};
+
+// The result of verifying a variation seed's signature.
+// Note: UMA histogram enum - don't re-order or remove entries.
+enum class VerifySignatureResult {
+ MISSING_SIGNATURE,
+ DECODE_FAILED,
+ INVALID_SIGNATURE,
+ INVALID_SEED,
+ VALID_SIGNATURE,
+ ENUM_SIZE
+};
+
+#if defined(OS_ANDROID)
+// Records the result of importing a seed during Android first run.
+void RecordFirstRunSeedImportResult(FirstRunSeedImportResult result);
+#endif // OS_ANDROID
+
+// Records the result of attempting to load a variations seed on startup.
+void RecordLoadSeedResult(LoadSeedResult state);
+
+// Records the result of attempting to store a variations seed received from the
+// server.
+void RecordStoreSeedResult(StoreSeedResult result);
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_METRICS_H_
diff --git a/chromium/components/variations/metrics_util.h b/chromium/components/variations/metrics_util.h
index 0a03fd43460..76254d45810 100644
--- a/chromium/components/variations/metrics_util.h
+++ b/chromium/components/variations/metrics_util.h
@@ -9,6 +9,7 @@
#include "base/strings/string_piece.h"
+// TODO(rkaplow): Move to variations namespace and rename file hashing.h.
namespace metrics {
// Computes a uint32_t hash of a given string based on its SHA1 hash. Suitable
diff --git a/chromium/components/variations/platform_field_trials.h b/chromium/components/variations/platform_field_trials.h
new file mode 100644
index 00000000000..bba99b86303
--- /dev/null
+++ b/chromium/components/variations/platform_field_trials.h
@@ -0,0 +1,38 @@
+// 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_VARIATIONS_PLATFORM_FIELD_TRIALS_H_
+#define COMPONENTS_VARIATIONS_PLATFORM_FIELD_TRIALS_H_
+
+#include "base/metrics/field_trial.h"
+
+namespace variations {
+
+// Infrastructure for setting up platform specific field trials. Chrome and
+// WebView make use through their corresponding subclasses.
+class PlatformFieldTrials {
+ public:
+ PlatformFieldTrials(){};
+ virtual ~PlatformFieldTrials(){};
+
+ // Set up field trials for a specific platform.
+ virtual void SetupFieldTrials() = 0;
+
+ // Create field trials that will control feature list features. This should be
+ // called during the same timing window as
+ // FeatureList::AssociateReportingFieldTrial. |has_seed| indicates that the
+ // variations service used a seed to create field trials. This can be used to
+ // prevent associating a field trial with a feature that you expect to be
+ // controlled by the variations seed.
+ virtual void SetupFeatureControllingFieldTrials(
+ bool has_seed,
+ base::FeatureList* feature_list) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformFieldTrials);
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_PLATFORM_FIELD_TRIALS_H_
diff --git a/chromium/components/variations/pref_names.cc b/chromium/components/variations/pref_names.cc
index 8bd52ce045a..440d739263e 100644
--- a/chromium/components/variations/pref_names.cc
+++ b/chromium/components/variations/pref_names.cc
@@ -10,14 +10,23 @@ namespace prefs {
// Base64-encoded compressed serialized form of the variations seed protobuf.
const char kVariationsCompressedSeed[] = "variations_compressed_seed";
-// 64-bit integer serialization of the base::Time from the last successful seed
-// fetch (i.e. when the Variations server responds with 200 or 304).
-const char kVariationsLastFetchTime[] = "variations_last_fetch_time";
-
// The latest country code received by the VariationsService for evaluating
// studies.
const char kVariationsCountry[] = "variations_country";
+// The number of times that Chrome has crashed before successfully fetching a
+// new seed. Used to determine whether to fall back to a "safe" seed.
+const char kVariationsCrashStreak[] = "variations_crash_streak";
+
+// The number of times that the VariationsService has failed to fetch a new
+// seed. Used to determine whether to fall back to a "safe" seed.
+const char kVariationsFailedToFetchSeedStreak[] =
+ "variations_failed_to_fetch_seed_streak";
+
+// 64-bit integer serialization of the base::Time from the last successful seed
+// fetch (i.e. when the Variations server responds with 200 or 304).
+const char kVariationsLastFetchTime[] = "variations_last_fetch_time";
+
// Pair of <Chrome version string, country code string> representing the country
// used for filtering permanent consistency studies until the next time Chrome
// is updated.
@@ -32,9 +41,6 @@ const char kVariationsPermutedEntropyCache[] =
// String for the restrict parameter to be appended to the variations URL.
const char kVariationsRestrictParameter[] = "variations_restrict_parameter";
-// Base64-encoded serialized form of the variations seed protobuf.
-const char kVariationsSeed[] = "variations_seed";
-
// 64-bit integer serialization of the base::Time from the last seed received.
const char kVariationsSeedDate[] = "variations_seed_date";
diff --git a/chromium/components/variations/pref_names.h b/chromium/components/variations/pref_names.h
index 17cbd8858b0..461c39a3d5a 100644
--- a/chromium/components/variations/pref_names.h
+++ b/chromium/components/variations/pref_names.h
@@ -12,12 +12,13 @@ namespace prefs {
// Keep alphabetized, and document each in the .cc file.
extern const char kVariationsCompressedSeed[];
+extern const char kVariationsCountry[];
+extern const char kVariationsCrashStreak[];
+extern const char kVariationsFailedToFetchSeedStreak[];
extern const char kVariationsLastFetchTime[];
extern const char kVariationsPermanentConsistencyCountry[];
extern const char kVariationsPermutedEntropyCache[];
-extern const char kVariationsCountry[];
extern const char kVariationsRestrictParameter[];
-extern const char kVariationsSeed[];
extern const char kVariationsSeedDate[];
extern const char kVariationsSeedSignature[];
diff --git a/chromium/components/variations/proto/study.proto b/chromium/components/variations/proto/study.proto
index d4a88bedd3b..b7a0bb69b1b 100644
--- a/chromium/components/variations/proto/study.proto
+++ b/chromium/components/variations/proto/study.proto
@@ -182,12 +182,13 @@ message Study {
// Possible Chrome operating system platforms.
enum Platform {
- PLATFORM_WINDOWS = 0;
- PLATFORM_MAC = 1;
- PLATFORM_LINUX = 2;
- PLATFORM_CHROMEOS = 3;
- PLATFORM_ANDROID = 4;
- PLATFORM_IOS = 5;
+ PLATFORM_WINDOWS = 0;
+ PLATFORM_MAC = 1;
+ PLATFORM_LINUX = 2;
+ PLATFORM_CHROMEOS = 3;
+ PLATFORM_ANDROID = 4;
+ PLATFORM_IOS = 5;
+ PLATFORM_ANDROID_WEBVIEW = 6;
}
// Possible form factors Chrome is running on.
@@ -205,7 +206,7 @@ message Study {
// Filtering criteria specifying whether this study is applicable to a given
// Chrome instance.
//
- // Next tag: 15
+ // Next tag: 16
message Filter {
// The start date of the study in Unix time format. (Seconds since midnight
// January 1, 1970 UTC). See: http://en.wikipedia.org/wiki/Unix_time
@@ -299,6 +300,10 @@ message Study {
// |country| is specified. Mutually exclusive with |country|.
// Ex: ["in", "us"]
repeated string exclude_country = 11;
+
+ // Specifies whether the config should apply to low-end devices only. This
+ // is currently only supported on Android.
+ optional bool is_low_end_device = 15;
}
// Filtering criteria for this study. A study that is filtered out for a given
diff --git a/chromium/components/variations/proto/variations_seed.proto b/chromium/components/variations/proto/variations_seed.proto
index cfb58fed669..8824b7ec92b 100644
--- a/chromium/components/variations/proto/variations_seed.proto
+++ b/chromium/components/variations/proto/variations_seed.proto
@@ -21,6 +21,6 @@ message VariationsSeed {
repeated Study study = 2;
// Lowercase ISO 3166-1 alpha-2 country code of the client, according to IP
- // address.
- optional string country_code = 3;
+ // address. Deprecated.
+ optional string country_code = 3 [deprecated = true];
}
diff --git a/chromium/components/variations/service/BUILD.gn b/chromium/components/variations/service/BUILD.gn
index d2ea94f1029..f0e3cc719cc 100644
--- a/chromium/components/variations/service/BUILD.gn
+++ b/chromium/components/variations/service/BUILD.gn
@@ -6,6 +6,8 @@ static_library("service") {
sources = [
"ui_string_overrider.cc",
"ui_string_overrider.h",
+ "variations_field_trial_creator.cc",
+ "variations_field_trial_creator.h",
"variations_service.cc",
"variations_service.h",
"variations_service_client.h",
@@ -31,6 +33,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"ui_string_overrider_unittest.cc",
+ "variations_field_trial_creator_unittest.cc",
"variations_service_unittest.cc",
]
@@ -38,6 +41,8 @@ source_set("unit_tests") {
":service",
"//base",
"//base/test:test_support",
+ "//components/metrics",
+ "//components/metrics:test_support",
"//components/prefs:test_support",
"//components/variations",
"//components/variations/proto",
diff --git a/chromium/components/variations/service/ui_string_overrider_unittest.cc b/chromium/components/variations/service/ui_string_overrider_unittest.cc
index 0f4ab527528..3ceef785d31 100644
--- a/chromium/components/variations/service/ui_string_overrider_unittest.cc
+++ b/chromium/components/variations/service/ui_string_overrider_unittest.cc
@@ -10,8 +10,7 @@
#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace chrome_variations {
-
+namespace variations {
namespace {
const size_t kNumResources = 4;
@@ -42,7 +41,7 @@ class UIStringOverriderTest : public ::testing::Test {
}
private:
- variations::UIStringOverrider provider_;
+ UIStringOverrider provider_;
DISALLOW_COPY_AND_ASSIGN(UIStringOverriderTest);
};
@@ -61,4 +60,4 @@ TEST_F(UIStringOverriderTest, LookupFound) {
EXPECT_EQ(kResourceIndices[i], GetResourceIndex(kResourceHashes[i]));
}
-} // namespace chrome_variations
+} // namespace variations
diff --git a/chromium/components/variations/service/variations_field_trial_creator.cc b/chromium/components/variations/service/variations_field_trial_creator.cc
new file mode 100644
index 00000000000..d01415249b3
--- /dev/null
+++ b/chromium/components/variations/service/variations_field_trial_creator.cc
@@ -0,0 +1,328 @@
+// 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/variations/service/variations_field_trial_creator.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/build_time.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/sys_info.h"
+#include "base/version.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/pref_names.h"
+#include "components/variations/proto/variations_seed.pb.h"
+#include "components/variations/service/variations_service_client.h"
+#include "components/variations/variations_seed_processor.h"
+#include "components/variations/variations_switches.h"
+#include "ui/base/device_form_factor.h"
+
+namespace variations {
+
+// Maximum age permitted for a variations seed, in days.
+const int kMaxVariationsSeedAgeDays = 30;
+
+enum VariationsSeedExpiry {
+ VARIATIONS_SEED_EXPIRY_NOT_EXPIRED,
+ VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING,
+ VARIATIONS_SEED_EXPIRY_EXPIRED,
+ VARIATIONS_SEED_EXPIRY_ENUM_SIZE,
+};
+
+// Gets current form factor and converts it from enum DeviceFormFactor to enum
+// Study_FormFactor.
+Study::FormFactor GetCurrentFormFactor() {
+ switch (ui::GetDeviceFormFactor()) {
+ case ui::DEVICE_FORM_FACTOR_PHONE:
+ return Study::PHONE;
+ case ui::DEVICE_FORM_FACTOR_TABLET:
+ return Study::TABLET;
+ case ui::DEVICE_FORM_FACTOR_DESKTOP:
+ return Study::DESKTOP;
+ }
+ NOTREACHED();
+ return Study::DESKTOP;
+}
+
+// Gets the hardware class and returns it as a string. This returns an empty
+// string if the client is not ChromeOS.
+std::string GetHardwareClass() {
+#if defined(OS_CHROMEOS)
+ return base::SysInfo::GetLsbReleaseBoard();
+#endif // OS_CHROMEOS
+ return std::string();
+}
+
+// Returns the date that should be used by the VariationsSeedProcessor to do
+// expiry and start date checks.
+base::Time GetReferenceDateForExpiryChecks(PrefService* local_state) {
+ const int64_t date_value = local_state->GetInt64(prefs::kVariationsSeedDate);
+ const base::Time seed_date = base::Time::FromInternalValue(date_value);
+ const base::Time build_time = base::GetBuildTime();
+ // Use the build time for date checks if either the seed date is invalid or
+ // the build time is newer than the seed date.
+ base::Time reference_date = seed_date;
+ if (seed_date.is_null() || seed_date < build_time)
+ reference_date = build_time;
+ return reference_date;
+}
+
+// Wrapper around channel checking, used to enable channel mocking for
+// testing. If the current browser channel is not UNKNOWN, this will return
+// that channel value. Otherwise, if the fake channel flag is provided, this
+// will return the fake channel. Failing that, this will return the UNKNOWN
+// channel.
+Study::Channel GetChannelForVariations(version_info::Channel product_channel) {
+ switch (product_channel) {
+ case version_info::Channel::CANARY:
+ return Study::CANARY;
+ case version_info::Channel::DEV:
+ return Study::DEV;
+ case version_info::Channel::BETA:
+ return Study::BETA;
+ case version_info::Channel::STABLE:
+ return Study::STABLE;
+ case version_info::Channel::UNKNOWN:
+ break;
+ }
+ const std::string forced_channel =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kFakeVariationsChannel);
+ if (forced_channel == "stable")
+ return Study::STABLE;
+ if (forced_channel == "beta")
+ return Study::BETA;
+ if (forced_channel == "dev")
+ return Study::DEV;
+ if (forced_channel == "canary")
+ return Study::CANARY;
+ DVLOG(1) << "Invalid channel provided: " << forced_channel;
+ return Study::UNKNOWN;
+}
+
+// Records UMA histogram with the result of the variations seed expiry check.
+void RecordCreateTrialsSeedExpiry(VariationsSeedExpiry expiry_check_result) {
+ UMA_HISTOGRAM_ENUMERATION("Variations.CreateTrials.SeedExpiry",
+ expiry_check_result,
+ VARIATIONS_SEED_EXPIRY_ENUM_SIZE);
+}
+
+VariationsFieldTrialCreator::VariationsFieldTrialCreator(
+ PrefService* local_state,
+ VariationsServiceClient* client,
+ const UIStringOverrider& ui_string_overrider)
+ : client_(client),
+ ui_string_overrider_(ui_string_overrider),
+ seed_store_(local_state),
+ create_trials_from_seed_called_(false) {}
+
+VariationsFieldTrialCreator::~VariationsFieldTrialCreator() {}
+
+std::string VariationsFieldTrialCreator::GetLatestCountry() const {
+ const std::string override_country =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kVariationsOverrideCountry);
+ return !override_country.empty()
+ ? override_country
+ : local_state()->GetString(prefs::kVariationsCountry);
+}
+
+bool VariationsFieldTrialCreator::CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>
+ low_entropy_provider,
+ base::FeatureList* feature_list) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ CHECK(!create_trials_from_seed_called_);
+
+ create_trials_from_seed_called_ = true;
+
+ VariationsSeed seed;
+ if (!LoadSeed(&seed))
+ return false;
+
+ const int64_t last_fetch_time_internal =
+ local_state()->GetInt64(prefs::kVariationsLastFetchTime);
+ const base::Time last_fetch_time =
+ base::Time::FromInternalValue(last_fetch_time_internal);
+ if (last_fetch_time.is_null()) {
+ // If the last fetch time is missing and we have a seed, then this must be
+ // the first run of Chrome. Store the current time as the last fetch time.
+ RecordLastFetchTime();
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING);
+ } else {
+ // Reject the seed if it is more than 30 days old.
+ const base::TimeDelta seed_age = base::Time::Now() - last_fetch_time;
+ if (seed_age.InDays() > kMaxVariationsSeedAgeDays) {
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_EXPIRED);
+ return false;
+ }
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_NOT_EXPIRED);
+ }
+
+ const base::Version current_version(version_info::GetVersionNumber());
+ if (!current_version.IsValid())
+ return false;
+
+ std::unique_ptr<ClientFilterableState> client_state =
+ GetClientFilterableStateForVersion(current_version);
+
+ // Note that passing |&ui_string_overrider_| via base::Unretained below is
+ // safe because the callback is executed synchronously. It is not possible
+ // to pass UIStringOverrider itself to VariationSeedProcessor as variations
+ // components should not depends on //ui/base.
+ VariationsSeedProcessor().CreateTrialsFromSeed(
+ seed, *client_state,
+ base::Bind(&UIStringOverrider::OverrideUIString,
+ base::Unretained(&ui_string_overrider_)),
+ low_entropy_provider.get(), feature_list);
+
+ const base::Time now = base::Time::Now();
+
+ // Log the "freshness" of the seed that was just used. The freshness is the
+ // time between the last successful seed download and now.
+ if (!last_fetch_time.is_null()) {
+ const base::TimeDelta delta = now - last_fetch_time;
+ // Log the value in number of minutes.
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Variations.SeedFreshness", delta.InMinutes(),
+ 1, base::TimeDelta::FromDays(30).InMinutes(),
+ 50);
+ }
+
+ return true;
+}
+
+void VariationsFieldTrialCreator::SetCreateTrialsFromSeedCalledForTesting(
+ bool called) {
+ create_trials_from_seed_called_ = called;
+}
+
+std::unique_ptr<ClientFilterableState>
+VariationsFieldTrialCreator::GetClientFilterableStateForVersion(
+ const base::Version& version) {
+ std::unique_ptr<ClientFilterableState> state =
+ base::MakeUnique<ClientFilterableState>();
+ state->locale = client_->GetApplicationLocale();
+ state->reference_date = GetReferenceDateForExpiryChecks(local_state());
+ state->version = version;
+ state->channel = GetChannelForVariations(client_->GetChannel());
+ state->form_factor = GetCurrentFormFactor();
+ state->platform = ClientFilterableState::GetCurrentPlatform();
+ state->hardware_class = GetHardwareClass();
+#if defined(OS_ANDROID)
+ // This is set on Android only currently, because the IsLowEndDevice() API
+ // on other platforms has no intrinsic meaning outside of a field trial that
+ // controls its value. Since this is before server-side field trials are
+ // evaluated, that field trial would not be able to apply for this case.
+ state->is_low_end_device = base::SysInfo::IsLowEndDevice();
+#endif
+ state->session_consistency_country = GetLatestCountry();
+ state->permanent_consistency_country = LoadPermanentConsistencyCountry(
+ version, state->session_consistency_country);
+ return state;
+}
+
+std::string VariationsFieldTrialCreator::LoadPermanentConsistencyCountry(
+ const base::Version& version,
+ const std::string& latest_country) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(version.IsValid());
+
+ const std::string override_country =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kVariationsOverrideCountry);
+ if (!override_country.empty())
+ return override_country;
+
+ const base::ListValue* list_value =
+ local_state()->GetList(prefs::kVariationsPermanentConsistencyCountry);
+ std::string stored_version_string;
+ std::string stored_country;
+
+ // Determine if the saved pref value is present and valid.
+ const bool is_pref_empty = list_value->empty();
+ const bool is_pref_valid = list_value->GetSize() == 2 &&
+ list_value->GetString(0, &stored_version_string) &&
+ list_value->GetString(1, &stored_country) &&
+ base::Version(stored_version_string).IsValid();
+
+ // Determine if the version from the saved pref matches |version|.
+ const bool does_version_match =
+ is_pref_valid && version == base::Version(stored_version_string);
+
+ // Determine if the country in the saved pref matches the country in
+ // |latest_country|.
+ const bool does_country_match = is_pref_valid && !latest_country.empty() &&
+ stored_country == latest_country;
+
+ // Record a histogram for how the saved pref value compares to the current
+ // version and the country code in the variations seed.
+ LoadPermanentConsistencyCountryResult result;
+ if (is_pref_empty) {
+ result = !latest_country.empty() ? LOAD_COUNTRY_NO_PREF_HAS_SEED
+ : LOAD_COUNTRY_NO_PREF_NO_SEED;
+ } else if (!is_pref_valid) {
+ result = !latest_country.empty() ? LOAD_COUNTRY_INVALID_PREF_HAS_SEED
+ : LOAD_COUNTRY_INVALID_PREF_NO_SEED;
+ } else if (latest_country.empty()) {
+ result = does_version_match ? LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
+ : LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ;
+ } else if (does_version_match) {
+ result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
+ : LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ;
+ } else {
+ result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
+ : LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Variations.LoadPermanentConsistencyCountryResult",
+ result, LOAD_COUNTRY_MAX);
+
+ // Use the stored country if one is available and was fetched since the last
+ // time Chrome was updated.
+ if (does_version_match)
+ return stored_country;
+
+ if (latest_country.empty()) {
+ if (!is_pref_valid)
+ local_state()->ClearPref(prefs::kVariationsPermanentConsistencyCountry);
+ // If we've never received a country code from the server, use an empty
+ // country so that it won't pass any filters that specifically include
+ // countries, but so that it will pass any filters that specifically exclude
+ // countries.
+ return std::string();
+ }
+
+ // Otherwise, update the pref with the current Chrome version and country.
+ StorePermanentCountry(version, latest_country);
+ return latest_country;
+}
+
+void VariationsFieldTrialCreator::StorePermanentCountry(
+ const base::Version& version,
+ const std::string& country) {
+ base::ListValue new_list_value;
+ new_list_value.AppendString(version.GetString());
+ new_list_value.AppendString(country);
+ local_state()->Set(prefs::kVariationsPermanentConsistencyCountry,
+ new_list_value);
+}
+
+void VariationsFieldTrialCreator::RecordLastFetchTime() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ local_state()->SetInt64(prefs::kVariationsLastFetchTime,
+ base::Time::Now().ToInternalValue());
+}
+
+bool VariationsFieldTrialCreator::LoadSeed(VariationsSeed* seed) {
+ return seed_store_.LoadSeed(seed);
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/service/variations_field_trial_creator.h b/chromium/components/variations/service/variations_field_trial_creator.h
new file mode 100644
index 00000000000..8e32f8168d1
--- /dev/null
+++ b/chromium/components/variations/service/variations_field_trial_creator.h
@@ -0,0 +1,123 @@
+// 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_VARIATIONS_FIELD_TRIAL_CREATOR_H_
+#define COMPONENTS_VARIATIONS_FIELD_TRIAL_CREATOR_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+#include "components/variations/client_filterable_state.h"
+#include "components/variations/service/ui_string_overrider.h"
+#include "components/variations/variations_seed_store.h"
+
+namespace variations {
+class VariationsServiceClient;
+}
+
+namespace variations {
+
+// Used to setup field trials based on stored variations seed data.
+class VariationsFieldTrialCreator {
+ public:
+ // Caller is responsible for ensuring that objects passed to the constructor
+ // stay valid for the lifetime of this object.
+ VariationsFieldTrialCreator(PrefService* local_state,
+ VariationsServiceClient* client,
+ const UIStringOverrider& ui_string_overrider);
+ ~VariationsFieldTrialCreator();
+
+ // Returns what variations will consider to be the latest country. Returns
+ // empty if it is not available.
+ std::string GetLatestCountry() const;
+
+ // Creates field trials based on the variations seed loaded from local state.
+ // If there is a problem loading the seed data, all trials specified by the
+ // seed may not be created. Some field trials are configured to override or
+ // associate with (for reporting) specific features. These associations are
+ // registered with |feature_list|.
+ bool CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>
+ low_entropy_provider,
+ base::FeatureList* feature_list);
+
+ VariationsSeedStore& seed_store() { return seed_store_; }
+
+ const VariationsSeedStore& seed_store() const { return seed_store_; }
+
+ bool create_trials_from_seed_called() const {
+ return create_trials_from_seed_called_;
+ }
+
+ // Exposed for testing.
+ void SetCreateTrialsFromSeedCalledForTesting(bool called);
+
+ // Returns all of the client state used for filtering studies.
+ // As a side-effect, may update the stored permanent consistency country.
+ std::unique_ptr<ClientFilterableState> GetClientFilterableStateForVersion(
+ const base::Version& version);
+
+ // Loads the country code to use for filtering permanent consistency studies,
+ // updating the stored country code if the stored value was for a different
+ // Chrome version. The country used for permanent consistency studies is kept
+ // consistent between Chrome upgrades in order to avoid annoying the user due
+ // to experiment churn while traveling.
+ std::string LoadPermanentConsistencyCountry(
+ const base::Version& version,
+ const std::string& latest_country);
+
+ // Sets the stored permanent country pref for this client.
+ void StorePermanentCountry(const base::Version& version,
+ const std::string& country);
+
+ // Records the time of the most recent successful fetch.
+ void RecordLastFetchTime();
+
+ // Loads the seed from the variations store into |seed|. If successfull,
+ // |seed| will contain the loaded data and true is returned. Set as virtual
+ // so that it can be overridden by tests.
+ virtual bool LoadSeed(VariationsSeed* seed);
+
+ private:
+ PrefService* local_state() { return seed_store_.local_state(); }
+
+ const PrefService* local_state() const { return seed_store_.local_state(); }
+
+ // Set of different possible values to report for the
+ // Variations.LoadPermanentConsistencyCountryResult histogram. This enum must
+ // be kept consistent with its counterpart in histograms.xml.
+ enum LoadPermanentConsistencyCountryResult {
+ LOAD_COUNTRY_NO_PREF_NO_SEED = 0,
+ LOAD_COUNTRY_NO_PREF_HAS_SEED,
+ LOAD_COUNTRY_INVALID_PREF_NO_SEED,
+ LOAD_COUNTRY_INVALID_PREF_HAS_SEED,
+ LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ,
+ LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ,
+ LOAD_COUNTRY_MAX,
+ };
+
+ VariationsServiceClient* client_;
+
+ UIStringOverrider ui_string_overrider_;
+
+ VariationsSeedStore seed_store_;
+
+ // Tracks whether |CreateTrialsFromSeed| has been called, to ensure that
+ // it gets called prior to |StartRepeatedVariationsSeedFetch|.
+ bool create_trials_from_seed_called_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(VariationsFieldTrialCreator);
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_FIELD_TRIAL_CREATOR_H_
diff --git a/chromium/components/variations/service/variations_field_trial_creator_unittest.cc b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
new file mode 100644
index 00000000000..47fefb3a0a1
--- /dev/null
+++ b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
@@ -0,0 +1,276 @@
+// 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/variations/service/variations_field_trial_creator.h"
+
+#include <stddef.h>
+
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/version.h"
+#include "components/metrics/clean_exit_beacon.h"
+#include "components/metrics/client_info.h"
+#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/test_enabled_state_provider.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/pref_names.h"
+#include "components/variations/service/variations_service.h"
+#include "components/web_resource/resource_request_allowed_notifier_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace variations {
+namespace {
+
+// A stub for the metrics state manager.
+void StubStoreClientInfo(const metrics::ClientInfo& /* client_info */) {}
+
+// A stub for the metrics state manager.
+std::unique_ptr<metrics::ClientInfo> StubLoadClientInfo() {
+ return std::unique_ptr<metrics::ClientInfo>();
+}
+
+class TestVariationsServiceClient : public VariationsServiceClient {
+ public:
+ TestVariationsServiceClient() {}
+ ~TestVariationsServiceClient() override {}
+
+ // VariationsServiceClient:
+ std::string GetApplicationLocale() override { return std::string(); }
+ base::Callback<base::Version(void)> GetVersionForSimulationCallback()
+ override {
+ return base::Callback<base::Version(void)>();
+ }
+ net::URLRequestContextGetter* GetURLRequestContext() override {
+ return nullptr;
+ }
+ network_time::NetworkTimeTracker* GetNetworkTimeTracker() override {
+ return nullptr;
+ }
+ version_info::Channel GetChannel() override {
+ return version_info::Channel::UNKNOWN;
+ }
+ bool OverridesRestrictParameter(std::string* parameter) override {
+ if (restrict_parameter_.empty())
+ return false;
+ *parameter = restrict_parameter_;
+ return true;
+ }
+
+ void set_restrict_parameter(const std::string& value) {
+ restrict_parameter_ = value;
+ }
+
+ private:
+ std::string restrict_parameter_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceClient);
+};
+
+class TestVariationsFieldTrialCreator : public VariationsFieldTrialCreator {
+ public:
+ TestVariationsFieldTrialCreator(
+ std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier,
+ PrefService* local_state,
+ TestVariationsServiceClient* client)
+ : VariationsFieldTrialCreator(local_state, client, UIStringOverrider()),
+ client_(client) {
+ SetCreateTrialsFromSeedCalledForTesting(true);
+ }
+
+ ~TestVariationsFieldTrialCreator() {
+ delete client_;
+ client_ = 0;
+ }
+
+ bool StoreSeed(const std::string& seed_data,
+ const std::string& seed_signature,
+ const std::string& country_code,
+ const base::Time& date_fetched,
+ bool is_delta_compressed,
+ bool is_gzip_compressed) {
+ seed_stored_ = true;
+ stored_seed_data_ = seed_data;
+ stored_country_ = country_code;
+ delta_compressed_seed_ = is_delta_compressed;
+ gzip_compressed_seed_ = is_gzip_compressed;
+ return true;
+ }
+
+ private:
+ bool LoadSeed(VariationsSeed* seed) override {
+ if (!seed_stored_)
+ return false;
+ return seed->ParseFromString(stored_seed_data_);
+ }
+
+ bool seed_stored_;
+ std::string stored_seed_data_;
+ std::string stored_country_;
+ bool delta_compressed_seed_;
+ bool gzip_compressed_seed_;
+
+ TestVariationsServiceClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVariationsFieldTrialCreator);
+};
+
+// A test class used to validate expected functionality in VariationsService.
+class TestVariationsService : public VariationsService {
+ public:
+ TestVariationsService(
+ std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier,
+ PrefService* local_state,
+ metrics::MetricsStateManager* state_manager)
+ : VariationsService(base::WrapUnique(new TestVariationsServiceClient()),
+ std::move(test_notifier),
+ local_state,
+ state_manager,
+ UIStringOverrider()) {}
+
+ ~TestVariationsService() override {}
+};
+
+// Constants used to create the test seed.
+const char kTestSeedStudyName[] = "test";
+const char kTestSeedExperimentName[] = "abc";
+const int kTestSeedExperimentProbability = 100;
+const char kTestSeedSerialNumber[] = "123";
+
+// Populates |seed| with simple test data. The resulting seed will contain one
+// study called "test", which contains one experiment called "abc" with
+// probability weight 100. |seed|'s study field will be cleared before adding
+// the new study.
+VariationsSeed CreateTestSeed() {
+ VariationsSeed seed;
+ Study* study = seed.add_study();
+ study->set_name(kTestSeedStudyName);
+ study->set_default_experiment_name(kTestSeedExperimentName);
+ Study_Experiment* experiment = study->add_experiment();
+ experiment->set_name(kTestSeedExperimentName);
+ experiment->set_probability_weight(kTestSeedExperimentProbability);
+ seed.set_serial_number(kTestSeedSerialNumber);
+ return seed;
+}
+
+// Serializes |seed| to protobuf binary format.
+std::string SerializeSeed(const VariationsSeed& seed) {
+ std::string serialized_seed;
+ seed.SerializeToString(&serialized_seed);
+ return serialized_seed;
+}
+
+} // namespace
+
+class FieldTrialCreatorTest : public ::testing::Test {
+ protected:
+ FieldTrialCreatorTest()
+ : enabled_state_provider_(
+ new metrics::TestEnabledStateProvider(false, false)) {
+ VariationsService::RegisterPrefs(prefs_.registry());
+ metrics::CleanExitBeacon::RegisterPrefs(prefs_.registry());
+ metrics::MetricsStateManager::RegisterPrefs(prefs_.registry());
+ }
+
+ metrics::MetricsStateManager* GetMetricsStateManager() {
+ // Lazy-initialize the metrics_state_manager so that it correctly reads the
+ // stability state from prefs after tests have a chance to initialize it.
+ if (!metrics_state_manager_) {
+ metrics_state_manager_ = metrics::MetricsStateManager::Create(
+ &prefs_, enabled_state_provider_.get(), base::string16(),
+ base::Bind(&StubStoreClientInfo), base::Bind(&StubLoadClientInfo));
+ }
+ return metrics_state_manager_.get();
+ }
+
+ protected:
+ TestingPrefServiceSimple prefs_;
+
+ private:
+ base::MessageLoop message_loop_;
+ std::unique_ptr<metrics::TestEnabledStateProvider> enabled_state_provider_;
+ std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(FieldTrialCreatorTest);
+};
+
+TEST_F(FieldTrialCreatorTest, CreateTrialsFromSeed) {
+ // Create a local base::FieldTrialList, to hold the field trials created in
+ // this test.
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // Create a variations service.
+ TestVariationsFieldTrialCreator field_trial_creator(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, new TestVariationsServiceClient());
+ field_trial_creator.SetCreateTrialsFromSeedCalledForTesting(false);
+
+ // Store a seed.
+ field_trial_creator.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
+ std::string(), base::Time::Now(), false, false);
+ prefs_.SetInt64(prefs::kVariationsLastFetchTime,
+ base::Time::Now().ToInternalValue());
+
+ // Check that field trials are created from the seed. Since the test study has
+ // only 1 experiment with 100% probability weight, we must be part of it.
+ EXPECT_TRUE(field_trial_creator.CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>(nullptr),
+ base::FeatureList::GetInstance()));
+ EXPECT_EQ(kTestSeedExperimentName,
+ base::FieldTrialList::FindFullName(kTestSeedStudyName));
+}
+
+TEST_F(FieldTrialCreatorTest, CreateTrialsFromSeedNoLastFetchTime) {
+ // Create a local base::FieldTrialList, to hold the field trials created in
+ // this test.
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // Create a variations service
+ TestVariationsFieldTrialCreator field_trial_creator(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, new TestVariationsServiceClient());
+ field_trial_creator.SetCreateTrialsFromSeedCalledForTesting(false);
+
+ // Store a seed. To simulate a first run, |prefs::kVariationsLastFetchTime|
+ // is left empty.
+ field_trial_creator.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
+ std::string(), base::Time::Now(), false, false);
+ EXPECT_EQ(0, prefs_.GetInt64(prefs::kVariationsLastFetchTime));
+
+ // Check that field trials are created from the seed. Since the test study has
+ // only 1 experiment with 100% probability weight, we must be part of it.
+ EXPECT_TRUE(field_trial_creator.CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>(nullptr),
+ base::FeatureList::GetInstance()));
+ EXPECT_EQ(base::FieldTrialList::FindFullName(kTestSeedStudyName),
+ kTestSeedExperimentName);
+}
+
+TEST_F(FieldTrialCreatorTest, CreateTrialsFromOutdatedSeed) {
+ // Create a local base::FieldTrialList, to hold the field trials created in
+ // this test.
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // Create a variations service.
+ TestVariationsFieldTrialCreator field_trial_creator(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, new TestVariationsServiceClient());
+ field_trial_creator.SetCreateTrialsFromSeedCalledForTesting(false);
+
+ // Store a seed, with a fetch time 31 days in the past.
+ const base::Time seed_date =
+ base::Time::Now() - base::TimeDelta::FromDays(31);
+ field_trial_creator.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
+ std::string(), seed_date, false, false);
+ prefs_.SetInt64(prefs::kVariationsLastFetchTime, seed_date.ToInternalValue());
+
+ // Check that field trials are not created from the seed.
+ EXPECT_FALSE(field_trial_creator.CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>(nullptr),
+ base::FeatureList::GetInstance()));
+ EXPECT_TRUE(base::FieldTrialList::FindFullName(kTestSeedStudyName).empty());
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/service/variations_service.cc b/chromium/components/variations/service/variations_service.cc
index e669699c2cd..11c418e4faf 100644
--- a/chromium/components/variations/service/variations_service.cc
+++ b/chromium/components/variations/service/variations_service.cc
@@ -10,6 +10,7 @@
#include <utility>
#include <vector>
+#include "base/base_switches.h"
#include "base/build_time.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
@@ -18,6 +19,7 @@
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/timer/elapsed_timer.h"
#include "base/values.h"
@@ -31,6 +33,7 @@
#include "components/prefs/pref_service.h"
#include "components/variations/pref_names.h"
#include "components/variations/proto/variations_seed.pb.h"
+#include "components/variations/service/variations_field_trial_creator.h"
#include "components/variations/variations_seed_processor.h"
#include "components/variations/variations_seed_simulator.h"
#include "components/variations/variations_switches.h"
@@ -49,49 +52,15 @@
#include "url/gurl.h"
namespace variations {
-
namespace {
// TODO(mad): To be removed when we stop updating the NetworkTimeTracker.
// For the HTTP date headers, the resolution of the server time is 1 second.
const int64_t kServerTimeResolutionMs = 1000;
-// Maximum age permitted for a variations seed, in days.
-const int kMaxVariationsSeedAgeDays = 30;
-
-// Wrapper around channel checking, used to enable channel mocking for
-// testing. If the current browser channel is not UNKNOWN, this will return
-// that channel value. Otherwise, if the fake channel flag is provided, this
-// will return the fake channel. Failing that, this will return the UNKNOWN
-// channel.
-variations::Study_Channel GetChannelForVariations(
- version_info::Channel product_channel) {
- switch (product_channel) {
- case version_info::Channel::CANARY:
- return variations::Study_Channel_CANARY;
- case version_info::Channel::DEV:
- return variations::Study_Channel_DEV;
- case version_info::Channel::BETA:
- return variations::Study_Channel_BETA;
- case version_info::Channel::STABLE:
- return variations::Study_Channel_STABLE;
- case version_info::Channel::UNKNOWN:
- break;
- }
- const std::string forced_channel =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kFakeVariationsChannel);
- if (forced_channel == "stable")
- return variations::Study_Channel_STABLE;
- if (forced_channel == "beta")
- return variations::Study_Channel_BETA;
- if (forced_channel == "dev")
- return variations::Study_Channel_DEV;
- if (forced_channel == "canary")
- return variations::Study_Channel_CANARY;
- DVLOG(1) << "Invalid channel provided: " << forced_channel;
- return variations::Study_Channel_UNKNOWN;
-}
+// Whether the VariationsService should always be created, even in Chromium
+// builds.
+bool g_enabled_for_testing = false;
// Returns a string that will be used for the value of the 'osname' URL param
// to the variations server.
@@ -148,13 +117,6 @@ enum VariationsSeedExpiry {
VARIATIONS_SEED_EXPIRY_ENUM_SIZE,
};
-// Records UMA histogram with the result of the variations seed expiry check.
-void RecordCreateTrialsSeedExpiry(VariationsSeedExpiry expiry_check_result) {
- UMA_HISTOGRAM_ENUMERATION("Variations.CreateTrials.SeedExpiry",
- expiry_check_result,
- VARIATIONS_SEED_EXPIRY_ENUM_SIZE);
-}
-
// Converts ResourceRequestAllowedNotifier::State to the corresponding
// ResourceRequestsAllowedState value.
ResourceRequestsAllowedState ResourceRequestStateToHistogramValue(
@@ -174,45 +136,6 @@ ResourceRequestsAllowedState ResourceRequestStateToHistogramValue(
return RESOURCE_REQUESTS_NOT_ALLOWED;
}
-
-// Gets current form factor and converts it from enum DeviceFormFactor to enum
-// Study_FormFactor.
-variations::Study_FormFactor GetCurrentFormFactor() {
- switch (ui::GetDeviceFormFactor()) {
- case ui::DEVICE_FORM_FACTOR_PHONE:
- return variations::Study_FormFactor_PHONE;
- case ui::DEVICE_FORM_FACTOR_TABLET:
- return variations::Study_FormFactor_TABLET;
- case ui::DEVICE_FORM_FACTOR_DESKTOP:
- return variations::Study_FormFactor_DESKTOP;
- }
- NOTREACHED();
- return variations::Study_FormFactor_DESKTOP;
-}
-
-// Gets the hardware class and returns it as a string. This returns an empty
-// string if the client is not ChromeOS.
-std::string GetHardwareClass() {
-#if defined(OS_CHROMEOS)
- return base::SysInfo::GetLsbReleaseBoard();
-#endif // OS_CHROMEOS
- return std::string();
-}
-
-// Returns the date that should be used by the VariationsSeedProcessor to do
-// expiry and start date checks.
-base::Time GetReferenceDateForExpiryChecks(PrefService* local_state) {
- const int64_t date_value = local_state->GetInt64(prefs::kVariationsSeedDate);
- const base::Time seed_date = base::Time::FromInternalValue(date_value);
- const base::Time build_time = base::GetBuildTime();
- // Use the build time for date checks if either the seed date is invalid or
- // the build time is newer than the seed date.
- base::Time reference_date = seed_date;
- if (seed_date.is_null() || seed_date < build_time)
- reference_date = build_time;
- return reference_date;
-}
-
// Returns the header value for |name| from |headers| or an empty string if not
// set.
std::string GetHeaderValue(const net::HttpResponseHeaders* headers,
@@ -280,103 +203,64 @@ VariationsService::VariationsService(
metrics::MetricsStateManager* state_manager,
const UIStringOverrider& ui_string_overrider)
: client_(std::move(client)),
- ui_string_overrider_(ui_string_overrider),
local_state_(local_state),
state_manager_(state_manager),
policy_pref_service_(local_state),
- seed_store_(local_state),
- create_trials_from_seed_called_(false),
initial_request_completed_(false),
disable_deltas_for_next_request_(false),
resource_request_allowed_notifier_(std::move(notifier)),
request_count_(0),
+ field_trial_creator_(local_state, client_.get(), ui_string_overrider),
weak_ptr_factory_(this) {
DCHECK(client_.get());
DCHECK(resource_request_allowed_notifier_.get());
resource_request_allowed_notifier_->Init(this);
-}
-
-VariationsService::~VariationsService() {
-}
-
-bool VariationsService::CreateTrialsFromSeed(base::FeatureList* feature_list) {
- DCHECK(thread_checker_.CalledOnValidThread());
- CHECK(!create_trials_from_seed_called_);
- create_trials_from_seed_called_ = true;
-
- variations::VariationsSeed seed;
- if (!LoadSeed(&seed))
- return false;
-
- const int64_t last_fetch_time_internal =
- local_state_->GetInt64(prefs::kVariationsLastFetchTime);
- const base::Time last_fetch_time =
- base::Time::FromInternalValue(last_fetch_time_internal);
- if (last_fetch_time.is_null()) {
- // If the last fetch time is missing and we have a seed, then this must be
- // the first run of Chrome. Store the current time as the last fetch time.
- RecordLastFetchTime();
- RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING);
- } else {
- // Reject the seed if it is more than 30 days old.
- const base::TimeDelta seed_age = base::Time::Now() - last_fetch_time;
- if (seed_age.InDays() > kMaxVariationsSeedAgeDays) {
- RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_EXPIRED);
- return false;
- }
- RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_NOT_EXPIRED);
+ // Increment the crash streak if the previous session crashed.
+ // Note that the streak is not cleared if the previous run didn’t crash.
+ // Instead, it’s incremented on each crash until Chrome is able to
+ // successfully fetch a new seed. This way, a seed update that mostly
+ // destabilizes Chrome will still result in a fallback to safe mode.
+ int num_crashes = local_state->GetInteger(prefs::kVariationsCrashStreak);
+ if (!state_manager_->clean_exit_beacon()->exited_cleanly()) {
+ ++num_crashes;
+ local_state->SetInteger(prefs::kVariationsCrashStreak, num_crashes);
}
- const base::Version current_version(version_info::GetVersionNumber());
- if (!current_version.IsValid())
- return false;
-
- variations::Study_Channel channel =
- GetChannelForVariations(client_->GetChannel());
- UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.UserChannel", channel);
-
- const std::string latest_country = GetLatestCountry();
- std::unique_ptr<const base::FieldTrial::EntropyProvider> low_entropy_provider(
- CreateLowEntropyProvider());
- // Note that passing |&ui_string_overrider_| via base::Unretained below is
- // safe because the callback is executed synchronously. It is not possible
- // to pass UIStringOverrider itself to VariationSeedProcesor as variations
- // components should not depends on //ui/base.
- variations::VariationsSeedProcessor().CreateTrialsFromSeed(
- seed, client_->GetApplicationLocale(),
- GetReferenceDateForExpiryChecks(local_state_), current_version, channel,
- GetCurrentFormFactor(), GetHardwareClass(), latest_country,
- LoadPermanentConsistencyCountry(current_version, latest_country),
- base::Bind(&UIStringOverrider::OverrideUIString,
- base::Unretained(&ui_string_overrider_)),
- low_entropy_provider.get(), feature_list);
-
- const base::Time now = base::Time::Now();
-
- // Log the "freshness" of the seed that was just used. The freshness is the
- // time between the last successful seed download and now.
- if (last_fetch_time_internal) {
- const base::TimeDelta delta =
- now - base::Time::FromInternalValue(last_fetch_time_internal);
- // Log the value in number of minutes.
- UMA_HISTOGRAM_CUSTOM_COUNTS("Variations.SeedFreshness", delta.InMinutes(),
- 1, base::TimeDelta::FromDays(30).InMinutes(), 50);
- }
+ // After three failures in a row -- either consistent crashes or consistent
+ // failures to fetch the seed -- assume that the current seed is bad, and fall
+ // back to the safe seed. However, ignore any number of failures if the
+ // --force-fieldtrials flag is set, as this flag is only used by developers,
+ // and there's no need to make the development process flakier.
+ const int kMaxFailuresBeforeRevertingToSafeSeed = 3;
+ int num_failures_to_fetch =
+ local_state->GetInteger(prefs::kVariationsFailedToFetchSeedStreak);
+ bool fall_back_to_safe_mode =
+ (num_crashes >= kMaxFailuresBeforeRevertingToSafeSeed ||
+ num_failures_to_fetch >= kMaxFailuresBeforeRevertingToSafeSeed) &&
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kForceFieldTrials);
+ UMA_HISTOGRAM_BOOLEAN("Variations.SafeMode.FellBackToSafeMode",
+ fall_back_to_safe_mode);
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.SafeMode.Streak.Crashes",
+ std::min(std::max(num_crashes, 0), 100));
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Variations.SafeMode.Streak.FetchFailures",
+ std::min(std::max(num_failures_to_fetch, 0), 100));
+}
- return true;
+VariationsService::~VariationsService() {
}
void VariationsService::PerformPreMainMessageLoopStartup() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
StartRepeatedVariationsSeedFetch();
- client_->OnInitialStartup();
}
void VariationsService::StartRepeatedVariationsSeedFetch() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Initialize the Variations server URL.
variations_server_url_ =
@@ -384,30 +268,37 @@ void VariationsService::StartRepeatedVariationsSeedFetch() {
// Check that |CreateTrialsFromSeed| was called, which is necessary to
// retrieve the serial number that will be sent to the server.
- DCHECK(create_trials_from_seed_called_);
+ DCHECK(field_trial_creator_.create_trials_from_seed_called());
DCHECK(!request_scheduler_.get());
- // Note that the act of instantiating the scheduler will start the fetch, if
- // the scheduler deems appropriate.
request_scheduler_.reset(VariationsRequestScheduler::Create(
base::Bind(&VariationsService::FetchVariationsSeed,
weak_ptr_factory_.GetWeakPtr()),
local_state_));
+ // Note that the act of starting the scheduler will start the fetch, if the
+ // scheduler deems appropriate.
request_scheduler_->Start();
}
+std::string VariationsService::LoadPermanentConsistencyCountry(
+ const base::Version& version,
+ const std::string& latest_country) {
+ return field_trial_creator_.LoadPermanentConsistencyCountry(version,
+ latest_country);
+}
+
void VariationsService::AddObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.AddObserver(observer);
}
void VariationsService::RemoveObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.RemoveObserver(observer);
}
void VariationsService::OnAppEnterForeground() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// On mobile platforms, initialize the fetch scheduler when we receive the
// first app foreground notification.
@@ -417,7 +308,7 @@ void VariationsService::OnAppEnterForeground() {
}
void VariationsService::SetRestrictMode(const std::string& restrict_mode) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This should be called before the server URL has been computed.
DCHECK(variations_server_url_.is_empty());
@@ -425,8 +316,8 @@ void VariationsService::SetRestrictMode(const std::string& restrict_mode) {
}
void VariationsService::SetCreateTrialsFromSeedCalledForTesting(bool called) {
- DCHECK(thread_checker_.CalledOnValidThread());
- create_trials_from_seed_called_ = called;
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ field_trial_creator_.SetCreateTrialsFromSeedCalledForTesting(called);
}
GURL VariationsService::GetVariationsServerURL(
@@ -472,6 +363,11 @@ void VariationsService::RegisterPrefs(PrefRegistrySimple* registry) {
// This preference keeps track of the country code used to filter
// permanent-consistency studies.
registry->RegisterListPref(prefs::kVariationsPermanentConsistencyCountry);
+
+ // Prefs tracking failures along the way to fetching a seed, used to implement
+ // Safe Mode.
+ registry->RegisterIntegerPref(prefs::kVariationsCrashStreak, 0);
+ registry->RegisterIntegerPref(prefs::kVariationsFailedToFetchSeedStreak, 0);
}
// static
@@ -495,7 +391,8 @@ std::unique_ptr<VariationsService> VariationsService::Create(
// Unless the URL was provided, unsupported builds should return NULL to
// indicate that the service should not be used.
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kVariationsServerURL)) {
+ switches::kVariationsServerURL) &&
+ !g_enabled_for_testing) {
DVLOG(1) << "Not creating VariationsService in unofficial build without --"
<< switches::kVariationsServerURL << " specified.";
return result;
@@ -510,18 +407,19 @@ std::unique_ptr<VariationsService> VariationsService::Create(
}
// static
-std::unique_ptr<VariationsService> VariationsService::CreateForTesting(
- std::unique_ptr<VariationsServiceClient> client,
- PrefService* local_state) {
- return base::WrapUnique(new VariationsService(
- std::move(client),
- base::MakeUnique<web_resource::ResourceRequestAllowedNotifier>(
- local_state, nullptr),
- local_state, nullptr, UIStringOverrider()));
+void VariationsService::EnableForTesting() {
+ g_enabled_for_testing = true;
}
void VariationsService::DoActualFetch() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Pessimistically assume the fetch will fail. The failure streak will be
+ // reset upon success.
+ int num_failures_to_fetch =
+ local_state_->GetInteger(prefs::kVariationsFailedToFetchSeedStreak);
+ local_state_->SetInteger(prefs::kVariationsFailedToFetchSeedStreak,
+ num_failures_to_fetch + 1);
// Normally, there shouldn't be a |pending_request_| when this fires. However
// it's not impossible - for example if Chrome was paused (e.g. in a debugger
@@ -560,21 +458,15 @@ void VariationsService::DoActualFetch() {
net::LOAD_DO_NOT_SAVE_COOKIES);
pending_seed_request_->SetRequestContext(client_->GetURLRequestContext());
bool enable_deltas = false;
- if (!seed_store_.variations_serial_number().empty() &&
+ if (!field_trial_creator_.seed_store().variations_serial_number().empty() &&
!disable_deltas_for_next_request_) {
- // If the current seed includes a country code, deltas are not supported (as
- // the serial number doesn't take into account the country code). The server
- // will update us with a seed that doesn't include a country code which will
- // enable deltas to work.
- // TODO(asvitkine): Remove the check in M50+ when the percentage of clients
- // that have an old seed with a country code becomes miniscule.
- if (!seed_store_.seed_has_country_code()) {
- // Tell the server that delta-compressed seeds are supported.
- enable_deltas = true;
- }
+ // Tell the server that delta-compressed seeds are supported.
+ enable_deltas = true;
+
// Get the seed only if its serial number doesn't match what we have.
pending_seed_request_->AddExtraRequestHeader(
- "If-None-Match:" + seed_store_.variations_serial_number());
+ "If-None-Match:" +
+ field_trial_creator_.seed_store().variations_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";
@@ -602,24 +494,19 @@ bool VariationsService::StoreSeed(const std::string& seed_data,
const base::Time& date_fetched,
bool is_delta_compressed,
bool is_gzip_compressed) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- std::unique_ptr<variations::VariationsSeed> seed(
- new variations::VariationsSeed);
- if (!seed_store_.StoreSeedData(seed_data, seed_signature, country_code,
- date_fetched, is_delta_compressed,
- is_gzip_compressed, seed.get())) {
+ std::unique_ptr<VariationsSeed> seed(new VariationsSeed);
+ if (!field_trial_creator_.seed_store().StoreSeedData(
+ seed_data, seed_signature, country_code, date_fetched,
+ is_delta_compressed, is_gzip_compressed, seed.get())) {
return false;
}
- RecordLastFetchTime();
- // Perform seed simulation only if |state_manager_| is not-NULL. The state
- // manager may be NULL for some unit tests.
- if (!state_manager_)
- return true;
+ RecordSuccessfulFetch();
- base::PostTaskAndReplyWithResult(
- client_->GetBlockingPool(), FROM_HERE,
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
client_->GetVersionForSimulationCallback(),
base::Bind(&VariationsService::PerformSimulationWithVersion,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&seed)));
@@ -631,12 +518,8 @@ VariationsService::CreateLowEntropyProvider() {
return state_manager_->CreateLowEntropyProvider();
}
-bool VariationsService::LoadSeed(VariationsSeed* seed) {
- return seed_store_.LoadSeed(seed);
-}
-
void VariationsService::FetchVariationsSeed() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const web_resource::ResourceRequestAllowedNotifier::State state =
resource_request_allowed_notifier_->GetResourceRequestsAllowedState();
@@ -650,8 +533,8 @@ void VariationsService::FetchVariationsSeed() {
}
void VariationsService::NotifyObservers(
- const variations::VariationsSeedSimulator::Result& result) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ const VariationsSeedSimulator::Result& result) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result.kill_critical_group_change_count > 0) {
for (auto& observer : observer_list_)
@@ -663,7 +546,7 @@ void VariationsService::NotifyObservers(
}
void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(pending_seed_request_.get(), source);
const bool is_first_request = !initial_request_completed_;
@@ -710,10 +593,12 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
DVLOG(1) << "Variations server request returned non-HTTP_OK response code: "
<< response_code;
if (response_code == net::HTTP_NOT_MODIFIED) {
- RecordLastFetchTime();
+ RecordSuccessfulFetch();
+
// Update the seed date value in local state (used for expiry check on
// next start up), since 304 is a successful response.
- seed_store_.UpdateSeedDateAndLogDayChange(response_date);
+ field_trial_creator_.seed_store().UpdateSeedDateAndLogDayChange(
+ response_date);
}
return;
}
@@ -729,7 +614,7 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
&is_gzip_compressed)) {
// The header does not specify supported instance manipulations, unable to
// process data. Details of errors were logged by GetInstanceManipulations.
- seed_store_.ReportUnsupportedSeedFormatError();
+ field_trial_creator_.seed_store().ReportUnsupportedSeedFormatError();
return;
}
@@ -745,7 +630,7 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
}
void VariationsService::OnResourceRequestsAllowed() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Note that this only attempts to fetch the seed at most once per period
// (kSeedFetchPeriodHours). This works because
@@ -763,9 +648,9 @@ void VariationsService::OnResourceRequestsAllowed() {
}
void VariationsService::PerformSimulationWithVersion(
- std::unique_ptr<variations::VariationsSeed> seed,
+ std::unique_ptr<VariationsSeed> seed,
const base::Version& version) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!version.IsValid())
return;
@@ -776,17 +661,12 @@ void VariationsService::PerformSimulationWithVersion(
state_manager_->CreateDefaultEntropyProvider();
std::unique_ptr<const base::FieldTrial::EntropyProvider> low_provider =
state_manager_->CreateLowEntropyProvider();
- variations::VariationsSeedSimulator seed_simulator(*default_provider,
- *low_provider);
-
- const std::string latest_country = GetLatestCountry();
- const variations::VariationsSeedSimulator::Result result =
- seed_simulator.SimulateSeedStudies(
- *seed, client_->GetApplicationLocale(),
- GetReferenceDateForExpiryChecks(local_state_), version,
- GetChannelForVariations(client_->GetChannel()),
- GetCurrentFormFactor(), GetHardwareClass(), latest_country,
- LoadPermanentConsistencyCountry(version, latest_country));
+ VariationsSeedSimulator seed_simulator(*default_provider, *low_provider);
+
+ std::unique_ptr<ClientFilterableState> client_state =
+ field_trial_creator_.GetClientFilterableStateForVersion(version);
+ const VariationsSeedSimulator::Result result =
+ seed_simulator.SimulateSeedStudies(*seed, *client_state);
UMA_HISTOGRAM_COUNTS_100("Variations.SimulateSeed.NormalChanges",
result.normal_group_change_count);
@@ -800,103 +680,37 @@ void VariationsService::PerformSimulationWithVersion(
NotifyObservers(result);
}
-void VariationsService::RecordLastFetchTime() {
- DCHECK(thread_checker_.CalledOnValidThread());
+void VariationsService::RecordSuccessfulFetch() {
+ field_trial_creator_.RecordLastFetchTime();
- // local_state_ is NULL in tests, so check it first.
- if (local_state_) {
- local_state_->SetInt64(prefs::kVariationsLastFetchTime,
- base::Time::Now().ToInternalValue());
- }
+ // Note: It's important to clear the crash streak as well as the fetch
+ // failures streak. Crashes that occur after a successful seed fetch do not
+ // prevent updating to a new seed, and therefore do not necessitate falling
+ // back to a safe seed.
+ local_state_->SetInteger(prefs::kVariationsCrashStreak, 0);
+ local_state_->SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 0);
}
std::string VariationsService::GetInvalidVariationsSeedSignature() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return seed_store_.GetInvalidSignature();
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return field_trial_creator_.seed_store().GetInvalidSignature();
}
-std::string VariationsService::LoadPermanentConsistencyCountry(
- const base::Version& version,
- const std::string& latest_country) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(version.IsValid());
-
- const std::string override_country =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kVariationsOverrideCountry);
- if (!override_country.empty())
- return override_country;
-
- const base::ListValue* list_value =
- local_state_->GetList(prefs::kVariationsPermanentConsistencyCountry);
- std::string stored_version_string;
- std::string stored_country;
+void VariationsService::GetClientFilterableStateForVersionCalledForTesting() {
+ const base::Version current_version(version_info::GetVersionNumber());
+ if (!current_version.IsValid())
+ return;
- // Determine if the saved pref value is present and valid.
- const bool is_pref_empty = list_value->empty();
- const bool is_pref_valid = list_value->GetSize() == 2 &&
- list_value->GetString(0, &stored_version_string) &&
- list_value->GetString(1, &stored_country) &&
- base::Version(stored_version_string).IsValid();
-
- // Determine if the version from the saved pref matches |version|.
- const bool does_version_match =
- is_pref_valid && version == base::Version(stored_version_string);
-
- // Determine if the country in the saved pref matches the country in
- // |latest_country|.
- const bool does_country_match = is_pref_valid && !latest_country.empty() &&
- stored_country == latest_country;
-
- // Record a histogram for how the saved pref value compares to the current
- // version and the country code in the variations seed.
- LoadPermanentConsistencyCountryResult result;
- if (is_pref_empty) {
- result = !latest_country.empty() ? LOAD_COUNTRY_NO_PREF_HAS_SEED
- : LOAD_COUNTRY_NO_PREF_NO_SEED;
- } else if (!is_pref_valid) {
- result = !latest_country.empty() ? LOAD_COUNTRY_INVALID_PREF_HAS_SEED
- : LOAD_COUNTRY_INVALID_PREF_NO_SEED;
- } else if (latest_country.empty()) {
- result = does_version_match ? LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
- : LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ;
- } else if (does_version_match) {
- result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
- : LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ;
- } else {
- result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
- : LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ;
- }
- UMA_HISTOGRAM_ENUMERATION("Variations.LoadPermanentConsistencyCountryResult",
- result, LOAD_COUNTRY_MAX);
-
- // Use the stored country if one is available and was fetched since the last
- // time Chrome was updated.
- if (does_version_match)
- return stored_country;
-
- if (latest_country.empty()) {
- if (!is_pref_valid)
- local_state_->ClearPref(prefs::kVariationsPermanentConsistencyCountry);
- // If we've never received a country code from the server, use an empty
- // country so that it won't pass any filters that specifically include
- // countries, but so that it will pass any filters that specifically exclude
- // countries.
- return std::string();
- }
+ field_trial_creator_.GetClientFilterableStateForVersion(current_version);
+}
- // Otherwise, update the pref with the current Chrome version and country.
- StorePermanentCountry(version, latest_country);
- return latest_country;
+std::string VariationsService::GetLatestCountry() const {
+ return field_trial_creator_.GetLatestCountry();
}
-void VariationsService::StorePermanentCountry(const base::Version& version,
- const std::string& country) {
- base::ListValue new_list_value;
- new_list_value.AppendString(version.GetString());
- new_list_value.AppendString(country);
- local_state_->Set(prefs::kVariationsPermanentConsistencyCountry,
- new_list_value);
+bool VariationsService::CreateTrialsFromSeed(base::FeatureList* feature_list) {
+ return field_trial_creator_.CreateTrialsFromSeed(CreateLowEntropyProvider(),
+ feature_list);
}
std::string VariationsService::GetStoredPermanentCountry() {
@@ -913,7 +727,7 @@ std::string VariationsService::GetStoredPermanentCountry() {
bool VariationsService::OverrideStoredPermanentCountry(
const std::string& country_override) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (country_override.empty())
return false;
@@ -929,17 +743,8 @@ bool VariationsService::OverrideStoredPermanentCountry(
return false;
base::Version version(version_info::GetVersionNumber());
- StorePermanentCountry(version, country_override);
+ field_trial_creator_.StorePermanentCountry(version, country_override);
return true;
}
-std::string VariationsService::GetLatestCountry() const {
- const std::string override_country =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kVariationsOverrideCountry);
- return !override_country.empty()
- ? override_country
- : local_state_->GetString(prefs::kVariationsCountry);
-}
-
} // namespace variations
diff --git a/chromium/components/variations/service/variations_service.h b/chromium/components/variations/service/variations_service.h
index 6e7033b69d9..75b6a774198 100644
--- a/chromium/components/variations/service/variations_service.h
+++ b/chromium/components/variations/service/variations_service.h
@@ -14,9 +14,10 @@
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/observer_list.h"
-#include "base/threading/thread_checker.h"
#include "base/time/time.h"
+#include "components/variations/client_filterable_state.h"
#include "components/variations/service/ui_string_overrider.h"
+#include "components/variations/service/variations_field_trial_creator.h"
#include "components/variations/service/variations_service_client.h"
#include "components/variations/variations_request_scheduler.h"
#include "components/variations/variations_seed_simulator.h"
@@ -151,10 +152,8 @@ class VariationsService
const char* disable_network_switch,
const UIStringOverrider& ui_string_overrider);
- // Factory method for creating a VariationsService in a testing context.
- static std::unique_ptr<VariationsService> CreateForTesting(
- std::unique_ptr<VariationsServiceClient> client,
- PrefService* local_state);
+ // Enables the VariationsService for testing, even in Chromium builds.
+ static void EnableForTesting();
// Set the PrefService responsible for getting policy-related preferences,
// such as the restrict parameter.
@@ -168,6 +167,9 @@ class VariationsService
// disabled.
std::string GetInvalidVariationsSeedSignature() const;
+ // Exposed for testing.
+ void GetClientFilterableStateForVersionCalledForTesting();
+
protected:
// Starts the fetching process once, where |OnURLFetchComplete| is called with
// the response.
@@ -213,6 +215,10 @@ class VariationsService
LoadPermanentConsistencyCountry);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, CountryHeader);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, GetVariationsServerURL);
+ FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest,
+ SafeMode_SuccessfulFetchClearsFailureStreaks);
+ FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest,
+ SafeMode_NotModifiedFetchClearsFailureStreaks);
// Set of different possible values to report for the
// Variations.LoadPermanentConsistencyCountryResult histogram. This enum must
@@ -231,15 +237,6 @@ class VariationsService
LOAD_COUNTRY_MAX,
};
- // Loads the seed from the variations store into |seed|. If successfull,
- // |seed| will contain the loaded data and true is returned. Set as virtual
- // so that it can be overridden by tests.
- virtual bool LoadSeed(VariationsSeed* seed);
-
- // Sets the stored permanent country pref for this client.
- void StorePermanentCountry(const base::Version& version,
- const std::string& country);
-
// Checks if prerequisites for fetching the Variations seed are met, and if
// so, performs the actual fetch using |DoActualFetch|.
void FetchVariationsSeed();
@@ -260,8 +257,10 @@ class VariationsService
std::unique_ptr<variations::VariationsSeed> seed,
const base::Version& version);
- // Record the time of the most recent successful fetch.
- void RecordLastFetchTime();
+ // 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();
// Loads the country code to use for filtering permanent consistency studies,
// updating the stored country code if the stored value was for a different
@@ -273,7 +272,6 @@ class VariationsService
const std::string& latest_country);
std::unique_ptr<VariationsServiceClient> client_;
- UIStringOverrider ui_string_overrider_;
// The pref service used to store persist the variations seed.
PrefService* local_state_;
@@ -286,8 +284,6 @@ class VariationsService
// either be Local State or Profile prefs.
PrefService* policy_pref_service_;
- VariationsSeedStore seed_store_;
-
// Contains the scheduler instance that handles timing for requests to the
// server. Initially NULL and instantiated when the initial fetch is
// requested.
@@ -305,10 +301,6 @@ class VariationsService
// The URL to use for querying the variations server.
GURL variations_server_url_;
- // Tracks whether |CreateTrialsFromSeed| has been called, to ensure that
- // it gets called prior to |StartRepeatedVariationsSeedFetch|.
- bool create_trials_from_seed_called_;
-
// Tracks whether the initial request to the variations server had completed.
bool initial_request_completed_;
@@ -332,7 +324,10 @@ class VariationsService
// List of observers of the VariationsService.
base::ObserverList<Observer> observer_list_;
- base::ThreadChecker thread_checker_;
+ // Member responsible for creating trials from a variations seed.
+ VariationsFieldTrialCreator field_trial_creator_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<VariationsService> weak_ptr_factory_;
diff --git a/chromium/components/variations/service/variations_service_client.h b/chromium/components/variations/service/variations_service_client.h
index b3758a64b37..c9a269b327f 100644
--- a/chromium/components/variations/service/variations_service_client.h
+++ b/chromium/components/variations/service/variations_service_client.h
@@ -12,10 +12,6 @@
#include "base/version.h"
#include "components/version_info/version_info.h"
-namespace base {
-class SequencedWorkerPool;
-}
-
namespace net {
class URLRequestContextGetter;
}
@@ -35,14 +31,9 @@ class VariationsServiceClient {
// Returns the current application locale (e.g. "en-US").
virtual std::string GetApplicationLocale() = 0;
- // Returns the SequencedWorkerPool on which the VariationsService should run
- // tasks that may block.
- virtual base::SequencedWorkerPool* GetBlockingPool() = 0;
-
// Returns a callback that when run returns the base::Version to use for
// variations seed simulation. VariationsService guarantees that the callback
- // will be run on the pool returned by
- // VariationsServiceClient::GetBlockingPool().
+ // will be run on a background threadt that permits blocking.
virtual base::Callback<base::Version(void)>
GetVersionForSimulationCallback() = 0;
@@ -56,9 +47,6 @@ class VariationsServiceClient {
// |parameter| is an out-param that will contain the value of the restrict
// parameter if true is returned.
virtual bool OverridesRestrictParameter(std::string* parameter) = 0;
-
- // Called from VariationsService::PerformPreMainMessageLoopStartup().
- virtual void OnInitialStartup() {}
};
} // namespace variations
diff --git a/chromium/components/variations/service/variations_service_unittest.cc b/chromium/components/variations/service/variations_service_unittest.cc
index d6293876b09..54718df8434 100644
--- a/chromium/components/variations/service/variations_service_unittest.cc
+++ b/chromium/components/variations/service/variations_service_unittest.cc
@@ -11,17 +11,24 @@
#include <vector>
#include "base/base64.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/json/json_string_value_serializer.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_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/scoped_task_environment.h"
#include "base/version.h"
+#include "components/metrics/clean_exit_beacon.h"
+#include "components/metrics/client_info.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/test_enabled_state_provider.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/pref_names.h"
#include "components/variations/proto/study.pb.h"
@@ -34,20 +41,30 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace variations {
-
namespace {
+// A stub for the metrics state manager.
+void StubStoreClientInfo(const metrics::ClientInfo& /* client_info */) {}
+
+// A stub for the metrics state manager.
+std::unique_ptr<metrics::ClientInfo> StubLoadClientInfo() {
+ return std::unique_ptr<metrics::ClientInfo>();
+}
+
+base::Version StubGetVersionForSimulation() {
+ return base::Version();
+}
+
class TestVariationsServiceClient : public VariationsServiceClient {
public:
TestVariationsServiceClient() {}
~TestVariationsServiceClient() override {}
- // variations::VariationsServiceClient:
+ // VariationsServiceClient:
std::string GetApplicationLocale() override { return std::string(); }
- base::SequencedWorkerPool* GetBlockingPool() override { return nullptr; }
base::Callback<base::Version(void)> GetVersionForSimulationCallback()
override {
- return base::Callback<base::Version(void)>();
+ return base::Bind(&StubGetVersionForSimulation);
}
net::URLRequestContextGetter* GetURLRequestContext() override {
return nullptr;
@@ -64,7 +81,6 @@ class TestVariationsServiceClient : public VariationsServiceClient {
*parameter = restrict_parameter_;
return true;
}
- void OnInitialStartup() override {}
void set_restrict_parameter(const std::string& value) {
restrict_parameter_ = value;
@@ -81,11 +97,12 @@ class TestVariationsService : public VariationsService {
public:
TestVariationsService(
std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier,
- PrefService* local_state)
+ PrefService* local_state,
+ metrics::MetricsStateManager* state_manager)
: VariationsService(base::WrapUnique(new TestVariationsServiceClient()),
std::move(test_notifier),
local_state,
- NULL,
+ state_manager,
UIStringOverrider()),
intercepts_fetch_(true),
fetch_attempted_(false),
@@ -139,11 +156,6 @@ class TestVariationsService : public VariationsService {
}
private:
- bool LoadSeed(VariationsSeed* seed) override {
- if (!seed_stored_)
- return false;
- return seed->ParseFromString(stored_seed_data_);
- }
bool intercepts_fetch_;
bool fetch_attempted_;
@@ -159,9 +171,7 @@ class TestVariationsService : public VariationsService {
class TestVariationsServiceObserver : public VariationsService::Observer {
public:
TestVariationsServiceObserver()
- : best_effort_changes_notified_(0),
- crticial_changes_notified_(0) {
- }
+ : best_effort_changes_notified_(0), crticial_changes_notified_(0) {}
~TestVariationsServiceObserver() override {}
void OnExperimentChangesDetected(Severity severity) override {
@@ -203,12 +213,12 @@ const char kTestSeedSerialNumber[] = "123";
// study called "test", which contains one experiment called "abc" with
// probability weight 100. |seed|'s study field will be cleared before adding
// the new study.
-variations::VariationsSeed CreateTestSeed() {
- variations::VariationsSeed seed;
- variations::Study* study = seed.add_study();
+VariationsSeed CreateTestSeed() {
+ VariationsSeed seed;
+ Study* study = seed.add_study();
study->set_name(kTestSeedStudyName);
study->set_default_experiment_name(kTestSeedExperimentName);
- variations::Study_Experiment* experiment = study->add_experiment();
+ Study_Experiment* experiment = study->add_experiment();
experiment->set_name(kTestSeedExperimentName);
experiment->set_probability_weight(kTestSeedExperimentProbability);
seed.set_serial_number(kTestSeedSerialNumber);
@@ -216,7 +226,7 @@ variations::VariationsSeed CreateTestSeed() {
}
// Serializes |seed| to protobuf binary format.
-std::string SerializeSeed(const variations::VariationsSeed& seed) {
+std::string SerializeSeed(const VariationsSeed& seed) {
std::string serialized_seed;
seed.SerializeToString(&serialized_seed);
return serialized_seed;
@@ -260,97 +270,37 @@ std::string ListValueToString(const base::ListValue& list_value) {
class VariationsServiceTest : public ::testing::Test {
protected:
- VariationsServiceTest() {}
+ VariationsServiceTest()
+ : enabled_state_provider_(
+ new metrics::TestEnabledStateProvider(false, false)) {
+ VariationsService::RegisterPrefs(prefs_.registry());
+ metrics::CleanExitBeacon::RegisterPrefs(prefs_.registry());
+ metrics::MetricsStateManager::RegisterPrefs(prefs_.registry());
+ }
+
+ metrics::MetricsStateManager* GetMetricsStateManager() {
+ // Lazy-initialize the metrics_state_manager so that it correctly reads the
+ // stability state from prefs after tests have a chance to initialize it.
+ if (!metrics_state_manager_) {
+ metrics_state_manager_ = metrics::MetricsStateManager::Create(
+ &prefs_, enabled_state_provider_.get(), base::string16(),
+ base::Bind(&StubStoreClientInfo), base::Bind(&StubLoadClientInfo));
+ }
+ return metrics_state_manager_.get();
+ }
+
+ protected:
+ TestingPrefServiceSimple prefs_;
private:
- base::MessageLoop message_loop_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<metrics::TestEnabledStateProvider> enabled_state_provider_;
+ std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest);
};
-TEST_F(VariationsServiceTest, CreateTrialsFromSeed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
- // Create a local base::FieldTrialList, to hold the field trials created in
- // this test.
- base::FieldTrialList field_trial_list(nullptr);
-
- // Create a variations service.
- TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- service.SetCreateTrialsFromSeedCalledForTesting(false);
-
- // Store a seed.
- service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), base::Time::Now(), false, false);
- prefs.SetInt64(prefs::kVariationsLastFetchTime,
- base::Time::Now().ToInternalValue());
-
- // Check that field trials are created from the seed. Since the test study has
- // only 1 experiment with 100% probability weight, we must be part of it.
- EXPECT_TRUE(service.CreateTrialsFromSeed(base::FeatureList::GetInstance()));
- EXPECT_EQ(kTestSeedExperimentName,
- base::FieldTrialList::FindFullName(kTestSeedStudyName));
-}
-
-TEST_F(VariationsServiceTest, CreateTrialsFromSeedNoLastFetchTime) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
- // Create a local base::FieldTrialList, to hold the field trials created in
- // this test.
- base::FieldTrialList field_trial_list(nullptr);
-
- // Create a variations service
- TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- service.SetCreateTrialsFromSeedCalledForTesting(false);
-
- // Store a seed. To simulate a first run, |prefs::kVariationsLastFetchTime|
- // is left empty.
- service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), base::Time::Now(), false, false);
- EXPECT_EQ(0, prefs.GetInt64(prefs::kVariationsLastFetchTime));
-
- // Check that field trials are created from the seed. Since the test study has
- // only 1 experiment with 100% probability weight, we must be part of it.
- EXPECT_TRUE(service.CreateTrialsFromSeed(base::FeatureList::GetInstance()));
- EXPECT_EQ(base::FieldTrialList::FindFullName(kTestSeedStudyName),
- kTestSeedExperimentName);
-}
-
-TEST_F(VariationsServiceTest, CreateTrialsFromOutdatedSeed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
- // Create a local base::FieldTrialList, to hold the field trials created in
- // this test.
- base::FieldTrialList field_trial_list(nullptr);
-
- // Create a variations service.
- TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- service.SetCreateTrialsFromSeedCalledForTesting(false);
-
- // Store a seed, with a fetch time 31 days in the past.
- const base::Time seed_date =
- base::Time::Now() - base::TimeDelta::FromDays(31);
- service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), seed_date, false, false);
- prefs.SetInt64(prefs::kVariationsLastFetchTime, seed_date.ToInternalValue());
-
- // Check that field trials are not created from the seed.
- EXPECT_FALSE(service.CreateTrialsFromSeed(base::FeatureList::GetInstance()));
- EXPECT_TRUE(base::FieldTrialList::FindFullName(kTestSeedStudyName).empty());
-}
-
TEST_F(VariationsServiceTest, GetVariationsServerURL) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
const std::string default_variations_url =
VariationsService::GetDefaultVariationsServerURLForTesting();
@@ -360,31 +310,31 @@ TEST_F(VariationsServiceTest, GetVariationsServerURL) {
TestVariationsServiceClient* raw_client = client.get();
VariationsService service(
std::move(client),
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs, NULL, UIStringOverrider());
- GURL url = service.GetVariationsServerURL(&prefs, std::string());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
+ GURL url = service.GetVariationsServerURL(&prefs_, std::string());
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value));
- prefs.SetString(prefs::kVariationsRestrictParameter, "restricted");
- url = service.GetVariationsServerURL(&prefs, std::string());
+ prefs_.SetString(prefs::kVariationsRestrictParameter, "restricted");
+ url = service.GetVariationsServerURL(&prefs_, std::string());
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
EXPECT_EQ("restricted", value);
- // A client override should take precedence over what's in prefs.
+ // A client override should take precedence over what's in prefs_.
raw_client->set_restrict_parameter("client");
- url = service.GetVariationsServerURL(&prefs, std::string());
+ url = service.GetVariationsServerURL(&prefs_, std::string());
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
EXPECT_EQ("client", value);
// The override value passed to the method should take precedence over
- // what's in prefs and a client override.
- url = service.GetVariationsServerURL(&prefs, "override");
+ // what's in prefs_ and a client override.
+ url = service.GetVariationsServerURL(&prefs_, "override");
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
@@ -392,12 +342,10 @@ TEST_F(VariationsServiceTest, GetVariationsServerURL) {
}
TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- const GURL url = service.GetVariationsServerURL(&prefs, std::string());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+ const GURL url = service.GetVariationsServerURL(&prefs_, std::string());
std::string value;
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "osname", &value));
@@ -405,15 +353,13 @@ TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) {
}
TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
// Pass ownership to TestVariationsService, but keep a weak pointer to
// manipulate it for this test.
std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier =
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_);
web_resource::TestRequestAllowedNotifier* raw_notifier = test_notifier.get();
- TestVariationsService test_service(std::move(test_notifier), &prefs);
+ TestVariationsService test_service(std::move(test_notifier), &prefs_,
+ GetMetricsStateManager());
// Force the notifier to initially disallow requests.
raw_notifier->SetRequestsAllowedOverride(false);
@@ -425,15 +371,13 @@ TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) {
}
TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
// Pass ownership to TestVariationsService, but keep a weak pointer to
// manipulate it for this test.
std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier =
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_);
web_resource::TestRequestAllowedNotifier* raw_notifier = test_notifier.get();
- TestVariationsService test_service(std::move(test_notifier), &prefs);
+ TestVariationsService test_service(std::move(test_notifier), &prefs_,
+ GetMetricsStateManager());
raw_notifier->SetRequestsAllowedOverride(true);
test_service.StartRepeatedVariationsSeedFetch();
@@ -441,12 +385,9 @@ TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) {
}
TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
net::TestURLFetcherFactory factory;
@@ -470,34 +411,31 @@ TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
net::HTTP_SERVICE_UNAVAILABLE,
};
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
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::kVariationsSeed)->IsDefaultValue());
+ EXPECT_TRUE(prefs_.FindPreference(prefs::kVariationsCompressedSeed)
+ ->IsDefaultValue());
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
SimulateServerResponse(non_ok_status_codes[i], fetcher);
service.OnURLFetchComplete(fetcher);
- EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue());
+ EXPECT_TRUE(prefs_.FindPreference(prefs::kVariationsCompressedSeed)
+ ->IsDefaultValue());
}
}
TEST_F(VariationsServiceTest, RequestGzipCompressedSeed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
net::TestURLFetcherFactory factory;
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
service.DoActualFetch();
@@ -525,15 +463,13 @@ TEST_F(VariationsServiceTest, InstanceManipulations) {
{"IM:deflate,x-bm,gzip", false, false, false},
};
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
std::string serialized_seed = SerializeSeed(CreateTestSeed());
net::TestURLFetcherFactory factory;
for (size_t i = 0; i < arraysize(cases); ++i) {
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
service.DoActualFetch();
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
@@ -552,12 +488,9 @@ TEST_F(VariationsServiceTest, InstanceManipulations) {
}
TEST_F(VariationsServiceTest, CountryHeader) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
net::TestURLFetcherFactory factory;
@@ -576,12 +509,10 @@ TEST_F(VariationsServiceTest, CountryHeader) {
}
TEST_F(VariationsServiceTest, Observer) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
VariationsService service(
base::MakeUnique<TestVariationsServiceClient>(),
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs, NULL, UIStringOverrider());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
struct {
int normal_count;
@@ -607,7 +538,7 @@ TEST_F(VariationsServiceTest, Observer) {
TestVariationsServiceObserver observer;
service.AddObserver(&observer);
- variations::VariationsSeedSimulator::Result result;
+ VariationsSeedSimulator::Result result;
result.normal_group_change_count = cases[i].normal_count;
result.kill_best_effort_group_change_count = cases[i].best_effort_count;
result.kill_critical_group_change_count = cases[i].critical_count;
@@ -676,24 +607,24 @@ TEST_F(VariationsServiceTest, LoadPermanentConsistencyCountry) {
};
for (const auto& test : test_cases) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
VariationsService service(
base::MakeUnique<TestVariationsServiceClient>(),
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs, NULL, UIStringOverrider());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
- if (test.pref_value_before) {
+ if (!test.pref_value_before) {
+ prefs_.ClearPref(prefs::kVariationsPermanentConsistencyCountry);
+ } else {
base::ListValue list_value;
for (const std::string& component :
base::SplitString(test.pref_value_before, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL)) {
list_value.AppendString(component);
}
- prefs.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
+ prefs_.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
}
- variations::VariationsSeed seed(CreateTestSeed());
+ VariationsSeed seed(CreateTestSeed());
std::string latest_country;
if (test.latest_country_code)
latest_country = test.latest_country_code;
@@ -712,7 +643,7 @@ TEST_F(VariationsServiceTest, LoadPermanentConsistencyCountry) {
expected_list_value.AppendString(component);
}
const base::ListValue* pref_value =
- prefs.GetList(prefs::kVariationsPermanentConsistencyCountry);
+ prefs_.GetList(prefs::kVariationsPermanentConsistencyCountry);
EXPECT_EQ(ListValueToString(expected_list_value),
ListValueToString(*pref_value))
<< test.pref_value_before << ", " << test.version << ", "
@@ -748,23 +679,23 @@ TEST_F(VariationsServiceTest, OverrideStoredPermanentCountry) {
};
for (const auto& test : test_cases) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
- if (!test.pref_value_before.empty()) {
+ if (test.pref_value_before.empty()) {
+ prefs_.ClearPref(prefs::kVariationsPermanentConsistencyCountry);
+ } else {
base::ListValue list_value;
for (const std::string& component :
base::SplitString(test.pref_value_before, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL)) {
list_value.AppendString(component);
}
- prefs.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
+ prefs_.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
}
- variations::VariationsSeed seed(CreateTestSeed());
+ VariationsSeed seed(CreateTestSeed());
EXPECT_EQ(test.has_updated, service.OverrideStoredPermanentCountry(
test.country_code_override))
@@ -777,11 +708,228 @@ TEST_F(VariationsServiceTest, OverrideStoredPermanentCountry) {
expected_list_value.AppendString(component);
}
const base::ListValue* pref_value =
- prefs.GetList(prefs::kVariationsPermanentConsistencyCountry);
+ prefs_.GetList(prefs::kVariationsPermanentConsistencyCountry);
EXPECT_EQ(ListValueToString(expected_list_value),
ListValueToString(*pref_value))
<< test.pref_value_before << ", " << test.country_code_override;
}
}
+TEST_F(VariationsServiceTest, SafeMode_NoPrefs) {
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 0, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NoCrashes_NoFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 0);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 0);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 0, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_SomeCrashes_SomeFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 1);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 2);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 1,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 2, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NoCrashes_ManyFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 0);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 3);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ true, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 3, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_ManyCrashes_NoFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 3);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 0);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ true, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 3,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 0, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_OverriddenByCommandlineFlag) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 3);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 3);
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ ::switches::kForceFieldTrials, "SomeFieldTrial");
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 3,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 3, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_CrashIncrementsCrashStreak) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 1);
+ prefs_.SetBoolean(metrics::prefs::kStabilityExitedCleanly, false);
+
+ // Create a variations service.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ EXPECT_EQ(2, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NoCrashPreservesCrashStreak) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 1);
+ prefs_.SetBoolean(metrics::prefs::kStabilityExitedCleanly, true);
+
+ // Create a variations service.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ EXPECT_EQ(1, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_StartingRequestIncrementsFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
+
+ // Create a variations service and start the fetch.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+ service.set_intercepts_fetch(false);
+ net::TestURLFetcherFactory factory;
+ service.DoActualFetch();
+
+ EXPECT_EQ(2, prefs_.GetInteger(prefs::kVariationsFailedToFetchSeedStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_SuccessfulFetchClearsFailureStreaks) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 2);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
+
+ // Create a variations service and perform a successful fetch.
+ VariationsService service(
+ base::MakeUnique<TestVariationsServiceClient>(),
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
+
+ service.SetCreateTrialsFromSeedCalledForTesting(true);
+ net::TestURLFetcherFactory factory;
+ // This will actually start the fetch.
+ service.PerformPreMainMessageLoopStartup();
+
+ // The below seed and signature pair were generated using the server's
+ // private key.
+ const std::string base64_seed_data =
+ "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
+ "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
+ "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
+ "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
+ "MDgQAUoMCghncm91cF8wORAB";
+ const std::string base64_seed_signature =
+ "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
+ "96JkMYgzTkHPwbv7K/CmgA==";
+
+ std::string response;
+ ASSERT_TRUE(base::Base64Decode(base64_seed_data, &response));
+ const std::string header = "X-Seed-Signature:" + base64_seed_signature;
+
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ SimulateServerResponseWithHeader(net::HTTP_OK, fetcher, &header);
+ fetcher->SetResponseString(response);
+ service.OnURLFetchComplete(fetcher);
+
+ // Verify that the streaks were rest.
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsFailedToFetchSeedStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NotModifiedFetchClearsFailureStreaks) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 2);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
+
+ // Create a variations service and perform a successful fetch.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+ service.set_intercepts_fetch(false);
+
+ net::TestURLFetcherFactory factory;
+ service.DoActualFetch();
+
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ SimulateServerResponse(net::HTTP_NOT_MODIFIED, fetcher);
+ service.OnURLFetchComplete(fetcher);
+
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsFailedToFetchSeedStreak));
+}
+
+TEST_F(VariationsServiceTest, FieldTrialCreatorInitializedCorrectly) {
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ // Call will crash in service's VariationsFieldTrialCreator if not initialized
+ // correctly.
+ service.GetClientFilterableStateForVersionCalledForTesting();
+}
+
} // namespace variations
diff --git a/chromium/components/variations/study_filtering.cc b/chromium/components/variations/study_filtering.cc
index 0712c2068fc..97d43905d8b 100644
--- a/chromium/components/variations/study_filtering.cc
+++ b/chromium/components/variations/study_filtering.cc
@@ -10,32 +10,12 @@
#include <set>
#include "base/stl_util.h"
-#include "build/build_config.h"
+#include "base/strings/string_util.h"
+#include "components/variations/client_filterable_state.h"
namespace variations {
-
namespace {
-Study_Platform GetCurrentPlatform() {
-#if defined(OS_WIN)
- return Study_Platform_PLATFORM_WINDOWS;
-#elif defined(OS_IOS)
- return Study_Platform_PLATFORM_IOS;
-#elif defined(OS_MACOSX)
- return Study_Platform_PLATFORM_MAC;
-#elif defined(OS_CHROMEOS)
- return Study_Platform_PLATFORM_CHROMEOS;
-#elif defined(OS_ANDROID)
- return Study_Platform_PLATFORM_ANDROID;
-#elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
- // Default BSD and SOLARIS to Linux to not break those builds, although these
- // platforms are not officially supported by Chrome.
- return Study_Platform_PLATFORM_LINUX;
-#else
-#error Unknown platform
-#endif
-}
-
// Converts |date_time| in Study date format to base::Time.
base::Time ConvertStudyDateToBaseTime(int64_t date_time) {
return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(date_time);
@@ -45,7 +25,7 @@ base::Time ConvertStudyDateToBaseTime(int64_t date_time) {
namespace internal {
-bool CheckStudyChannel(const Study_Filter& filter, Study_Channel channel) {
+bool CheckStudyChannel(const Study::Filter& filter, Study::Channel channel) {
// An empty channel list matches all channels.
if (filter.channel_size() == 0)
return true;
@@ -57,8 +37,8 @@ bool CheckStudyChannel(const Study_Filter& filter, Study_Channel channel) {
return false;
}
-bool CheckStudyFormFactor(const Study_Filter& filter,
- Study_FormFactor form_factor) {
+bool CheckStudyFormFactor(const Study::Filter& filter,
+ Study::FormFactor form_factor) {
// Empty whitelist and blacklist signifies matching any form factor.
if (filter.form_factor_size() == 0 && filter.exclude_form_factor_size() == 0)
return true;
@@ -74,7 +54,7 @@ bool CheckStudyFormFactor(const Study_Filter& filter,
return !base::ContainsValue(filter.exclude_form_factor(), form_factor);
}
-bool CheckStudyHardwareClass(const Study_Filter& filter,
+bool CheckStudyHardwareClass(const Study::Filter& filter,
const std::string& hardware_class) {
// Empty hardware_class and exclude_hardware_class matches all.
if (filter.hardware_class_size() == 0 &&
@@ -109,7 +89,7 @@ bool CheckStudyHardwareClass(const Study_Filter& filter,
return true;
}
-bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale) {
+bool CheckStudyLocale(const Study::Filter& filter, const std::string& locale) {
// Empty locale and exclude_locale lists matches all locales.
if (filter.locale_size() == 0 && filter.exclude_locale_size() == 0)
return true;
@@ -124,7 +104,7 @@ bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale) {
return !base::ContainsValue(filter.exclude_locale(), locale);
}
-bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform) {
+bool CheckStudyPlatform(const Study::Filter& filter, Study::Platform platform) {
// An empty platform list matches all platforms.
if (filter.platform_size() == 0)
return true;
@@ -136,7 +116,13 @@ bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform) {
return false;
}
-bool CheckStudyStartDate(const Study_Filter& filter,
+bool CheckStudyLowEndDevice(const Study::Filter& filter,
+ bool is_low_end_device) {
+ return !filter.has_is_low_end_device() ||
+ filter.is_low_end_device() == is_low_end_device;
+}
+
+bool CheckStudyStartDate(const Study::Filter& filter,
const base::Time& date_time) {
if (filter.has_start_date()) {
const base::Time start_date =
@@ -147,7 +133,7 @@ bool CheckStudyStartDate(const Study_Filter& filter,
return true;
}
-bool CheckStudyEndDate(const Study_Filter& filter,
+bool CheckStudyEndDate(const Study::Filter& filter,
const base::Time& date_time) {
if (filter.has_end_date()) {
const base::Time end_date = ConvertStudyDateToBaseTime(filter.end_date());
@@ -157,7 +143,7 @@ bool CheckStudyEndDate(const Study_Filter& filter,
return true;
}
-bool CheckStudyVersion(const Study_Filter& filter,
+bool CheckStudyVersion(const Study::Filter& filter,
const base::Version& version) {
if (filter.has_min_version()) {
if (version.CompareToWildcardString(filter.min_version()) < 0)
@@ -172,7 +158,8 @@ bool CheckStudyVersion(const Study_Filter& filter,
return true;
}
-bool CheckStudyCountry(const Study_Filter& filter, const std::string& country) {
+bool CheckStudyCountry(const Study::Filter& filter,
+ const std::string& country) {
// Empty country and exclude_country matches all.
if (filter.country_size() == 0 && filter.exclude_country_size() == 0)
return true;
@@ -187,6 +174,27 @@ bool CheckStudyCountry(const Study_Filter& filter, const std::string& country) {
return !base::ContainsValue(filter.exclude_country(), country);
}
+const std::string& GetClientCountryForStudy(
+ const Study& study,
+ const ClientFilterableState& client_state) {
+ switch (study.consistency()) {
+ case Study::SESSION:
+ return client_state.session_consistency_country;
+ case Study::PERMANENT:
+ // Use the saved country for permanent consistency studies. This allows
+ // Chrome to use the same country for filtering permanent consistency
+ // studies between Chrome upgrades. Since some studies have user-visible
+ // effects, this helps to avoid annoying users with experimental group
+ // churn while traveling.
+ return client_state.permanent_consistency_country;
+ }
+
+ // Unless otherwise specified, use an empty country that won't pass any
+ // filters that specifically include countries, but will pass any filters
+ // that specifically exclude countries.
+ return base::EmptyString();
+}
+
bool IsStudyExpired(const Study& study, const base::Time& date_time) {
if (study.has_expiry_date()) {
const base::Time expiry_date =
@@ -197,59 +205,60 @@ bool IsStudyExpired(const Study& study, const base::Time& date_time) {
return false;
}
-bool ShouldAddStudy(
- const Study& study,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& country) {
+bool ShouldAddStudy(const Study& study,
+ const ClientFilterableState& client_state) {
if (study.has_filter()) {
- if (!CheckStudyChannel(study.filter(), channel)) {
+ if (!CheckStudyChannel(study.filter(), client_state.channel)) {
DVLOG(1) << "Filtered out study " << study.name() << " due to channel.";
return false;
}
- if (!CheckStudyFormFactor(study.filter(), form_factor)) {
+ if (!CheckStudyFormFactor(study.filter(), client_state.form_factor)) {
DVLOG(1) << "Filtered out study " << study.name() <<
" due to form factor.";
return false;
}
- if (!CheckStudyLocale(study.filter(), locale)) {
+ if (!CheckStudyLocale(study.filter(), client_state.locale)) {
DVLOG(1) << "Filtered out study " << study.name() << " due to locale.";
return false;
}
- if (!CheckStudyPlatform(study.filter(), GetCurrentPlatform())) {
+ if (!CheckStudyPlatform(study.filter(), client_state.platform)) {
DVLOG(1) << "Filtered out study " << study.name() << " due to platform.";
return false;
}
- if (!CheckStudyVersion(study.filter(), version)) {
+ if (!CheckStudyVersion(study.filter(), client_state.version)) {
DVLOG(1) << "Filtered out study " << study.name() << " due to version.";
return false;
}
- if (!CheckStudyStartDate(study.filter(), reference_date)) {
+ if (!CheckStudyStartDate(study.filter(), client_state.reference_date)) {
DVLOG(1) << "Filtered out study " << study.name() <<
" due to start date.";
return false;
}
- if (!CheckStudyEndDate(study.filter(), reference_date)) {
+ if (!CheckStudyEndDate(study.filter(), client_state.reference_date)) {
DVLOG(1) << "Filtered out study " << study.name() << " due to end date.";
return false;
}
- if (!CheckStudyHardwareClass(study.filter(), hardware_class)) {
+ if (!CheckStudyHardwareClass(study.filter(), client_state.hardware_class)) {
DVLOG(1) << "Filtered out study " << study.name() <<
" due to hardware_class.";
return false;
}
+ if (!CheckStudyLowEndDevice(study.filter(),
+ client_state.is_low_end_device)) {
+ DVLOG(1) << "Filtered out study " << study.name()
+ << " due to is_low_end_device.";
+ return false;
+ }
+
+ const std::string& country = GetClientCountryForStudy(study, client_state);
if (!CheckStudyCountry(study.filter(), country)) {
DVLOG(1) << "Filtered out study " << study.name() << " due to country.";
return false;
@@ -263,16 +272,9 @@ bool ShouldAddStudy(
} // namespace internal
void FilterAndValidateStudies(const VariationsSeed& seed,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& session_consistency_country,
- const std::string& permanent_consistency_country,
+ const ClientFilterableState& client_state,
std::vector<ProcessedStudy>* filtered_studies) {
- DCHECK(version.IsValid());
+ DCHECK(client_state.version.IsValid());
// Add expired studies (in a disabled state) only after all the non-expired
// studies have been added (and do not add an expired study if a corresponding
@@ -283,32 +285,10 @@ void FilterAndValidateStudies(const VariationsSeed& seed,
for (int i = 0; i < seed.study_size(); ++i) {
const Study& study = seed.study(i);
-
- // Unless otherwise specified, use an empty country that won't pass any
- // filters that specifically include countries, but will pass any filters
- // that specifically exclude countries.
- std::string country;
- switch (study.consistency()) {
- case Study_Consistency_SESSION:
- country = session_consistency_country;
- break;
- case Study_Consistency_PERMANENT:
- // Use the saved |permanent_consistency_country| for permanent
- // consistency studies. This allows Chrome to use the same country for
- // filtering permanent consistency studies between Chrome upgrades.
- // Since some studies have user-visible effects, this helps to avoid
- // annoying users with experimental group churn while traveling.
- country = permanent_consistency_country;
- break;
- }
-
- if (!internal::ShouldAddStudy(study, locale, reference_date, version,
- channel, form_factor, hardware_class,
- country)) {
+ if (!internal::ShouldAddStudy(study, client_state))
continue;
- }
- if (internal::IsStudyExpired(study, reference_date)) {
+ if (internal::IsStudyExpired(study, client_state.reference_date)) {
expired_studies.push_back(&study);
} else if (!base::ContainsKey(created_studies, study.name())) {
ProcessedStudy::ValidateAndAppendStudy(&study, false, filtered_studies);
diff --git a/chromium/components/variations/study_filtering.h b/chromium/components/variations/study_filtering.h
index 9917ec7e828..87b4fae996f 100644
--- a/chromium/components/variations/study_filtering.h
+++ b/chromium/components/variations/study_filtering.h
@@ -17,70 +17,71 @@
namespace variations {
+struct ClientFilterableState;
+
// Internal functions exposed for testing purposes only.
namespace internal {
// Checks whether a study is applicable for the given |channel| per |filter|.
-bool CheckStudyChannel(const Study_Filter& filter, Study_Channel channel);
+bool CheckStudyChannel(const Study::Filter& filter, Study::Channel channel);
// Checks whether a study is applicable for the given |form_factor| per
// |filter|.
-bool CheckStudyFormFactor(const Study_Filter& filter,
- Study_FormFactor form_factor);
+bool CheckStudyFormFactor(const Study::Filter& filter,
+ Study::FormFactor form_factor);
// Checks whether a study is applicable for the given |hardware_class| per
// |filter|.
-bool CheckStudyHardwareClass(const Study_Filter& filter,
+bool CheckStudyHardwareClass(const Study::Filter& filter,
const std::string& hardware_class);
// Checks whether a study is applicable for the given |locale| per |filter|.
-bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale);
+bool CheckStudyLocale(const Study::Filter& filter, const std::string& locale);
// Checks whether a study is applicable for the given |platform| per |filter|.
-bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform);
+bool CheckStudyPlatform(const Study::Filter& filter, Study::Platform platform);
+
+// Checks whether a study is applicable given |is_low_end_device| per |filter|.
+bool CheckStudyLowEndDevice(const Study::Filter& filter,
+ bool is_low_end_device);
// Checks whether a study is applicable for the given date/time per |filter|.
-bool CheckStudyStartDate(const Study_Filter& filter,
+bool CheckStudyStartDate(const Study::Filter& filter,
const base::Time& date_time);
// Checks whether a study is applicable for the given date/time per |filter|.
-bool CheckStudyEndDate(const Study_Filter& filter, const base::Time& date_time);
+bool CheckStudyEndDate(const Study::Filter& filter,
+ const base::Time& date_time);
// Checks whether a study is applicable for the given version per |filter|.
-bool CheckStudyVersion(const Study_Filter& filter,
+bool CheckStudyVersion(const Study::Filter& filter,
const base::Version& version);
// Checks whether a study is applicable for the given |country| per |filter|.
-bool CheckStudyCountry(const Study_Filter& filter, const std::string& country);
+bool CheckStudyCountry(const Study::Filter& filter, const std::string& country);
+
+// Returns the country that should be used for filtering this study, depending
+// on whether the study has session or permanent consistency.
+const std::string& GetClientCountryForStudy(
+ const Study& study,
+ const ClientFilterableState& client_state);
// Checks whether |study| is expired using the given date/time.
bool IsStudyExpired(const Study& study, const base::Time& date_time);
-// Returns whether |study| should be disabled according to its restriction
-// parameters.
+// Returns whether |study| should be disabled according to the restriction
+// parameters in the |config|.
bool ShouldAddStudy(const Study& study,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& country);
+ const ClientFilterableState& client_state);
} // namespace internal
-// Filters the list of studies in |seed| and validates and pre-processes them,
-// adding any kept studies to |filtered_studies| list. Ensures that the
-// resulting list will not have more than one study with the same name.
+// Filters the list of studies in |seed| according ot the |client_state|, and
+// validates and pre-processes them, adding any kept studies to the
+// |filtered_studies| list. Ensures that the resulting list will not have more
+// than one study with the same name.
void FilterAndValidateStudies(const VariationsSeed& seed,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& session_consistency_country,
- const std::string& permanent_consistency_country,
+ const ClientFilterableState& client_state,
std::vector<ProcessedStudy>* filtered_studies);
} // namespace variations
diff --git a/chromium/components/variations/study_filtering_unittest.cc b/chromium/components/variations/study_filtering_unittest.cc
index 14f4f0865d0..c9728e4fddc 100644
--- a/chromium/components/variations/study_filtering_unittest.cc
+++ b/chromium/components/variations/study_filtering_unittest.cc
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/strings/string_split.h"
+#include "components/variations/client_filterable_state.h"
#include "components/variations/processed_study.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,9 +26,10 @@ int64_t TimeToProtoTime(const base::Time& time) {
}
// Adds an experiment to |study| with the specified |name| and |probability|.
-Study_Experiment* AddExperiment(const std::string& name, int probability,
- Study* study) {
- Study_Experiment* experiment = study->add_experiment();
+Study::Experiment* AddExperiment(const std::string& name,
+ int probability,
+ Study* study) {
+ Study::Experiment* experiment = study->add_experiment();
experiment->set_name(name);
experiment->set_probability_weight(probability);
return experiment;
@@ -36,15 +38,12 @@ Study_Experiment* AddExperiment(const std::string& name, int probability,
} // namespace
TEST(VariationsStudyFilteringTest, CheckStudyChannel) {
- const Study_Channel channels[] = {
- Study_Channel_CANARY,
- Study_Channel_DEV,
- Study_Channel_BETA,
- Study_Channel_STABLE,
+ const Study::Channel channels[] = {
+ Study::CANARY, Study::DEV, Study::BETA, Study::STABLE,
};
bool channel_added[arraysize(channels)] = { 0 };
- Study_Filter filter;
+ Study::Filter filter;
// Check in the forwarded order. The loop cond is <= arraysize(channels)
// instead of < so that the result of adding the last channel gets checked.
@@ -80,18 +79,15 @@ TEST(VariationsStudyFilteringTest, CheckStudyChannel) {
}
TEST(VariationsStudyFilteringTest, CheckStudyFormFactor) {
- const Study_FormFactor form_factors[] = {
- Study_FormFactor_DESKTOP,
- Study_FormFactor_PHONE,
- Study_FormFactor_TABLET,
- Study_FormFactor_KIOSK,
+ const Study::FormFactor form_factors[] = {
+ Study::DESKTOP, Study::PHONE, Study::TABLET, Study::KIOSK,
};
- ASSERT_EQ(Study_FormFactor_FormFactor_ARRAYSIZE,
+ ASSERT_EQ(Study::FormFactor_ARRAYSIZE,
static_cast<int>(arraysize(form_factors)));
bool form_factor_added[arraysize(form_factors)] = { 0 };
- Study_Filter filter;
+ Study::Filter filter;
for (size_t i = 0; i <= arraysize(form_factors); ++i) {
for (size_t j = 0; j < arraysize(form_factors); ++j) {
@@ -196,7 +192,7 @@ TEST(VariationsStudyFilteringTest, CheckStudyLocale) {
};
for (size_t i = 0; i < arraysize(test_cases); ++i) {
- Study_Filter filter;
+ Study::Filter filter;
for (const std::string& locale : base::SplitString(
test_cases[i].filter_locales, ",",
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL))
@@ -215,19 +211,16 @@ TEST(VariationsStudyFilteringTest, CheckStudyLocale) {
}
TEST(VariationsStudyFilteringTest, CheckStudyPlatform) {
- const Study_Platform platforms[] = {
- Study_Platform_PLATFORM_WINDOWS,
- Study_Platform_PLATFORM_MAC,
- Study_Platform_PLATFORM_LINUX,
- Study_Platform_PLATFORM_CHROMEOS,
- Study_Platform_PLATFORM_ANDROID,
- Study_Platform_PLATFORM_IOS,
+ const Study::Platform platforms[] = {
+ Study::PLATFORM_WINDOWS, Study::PLATFORM_MAC,
+ Study::PLATFORM_LINUX, Study::PLATFORM_CHROMEOS,
+ Study::PLATFORM_ANDROID, Study::PLATFORM_IOS,
+ Study::PLATFORM_ANDROID_WEBVIEW,
};
- ASSERT_EQ(Study_Platform_Platform_ARRAYSIZE,
- static_cast<int>(arraysize(platforms)));
+ ASSERT_EQ(Study::Platform_ARRAYSIZE, static_cast<int>(arraysize(platforms)));
bool platform_added[arraysize(platforms)] = { 0 };
- Study_Filter filter;
+ Study::Filter filter;
// Check in the forwarded order. The loop cond is <= arraysize(platforms)
// instead of < so that the result of adding the last channel gets checked.
@@ -262,6 +255,22 @@ TEST(VariationsStudyFilteringTest, CheckStudyPlatform) {
}
}
+TEST(VariationsStudyFilteringTest, CheckStudyLowEndDevice) {
+ Study::Filter filter;
+
+ // Check that if the filter is not set, study applies to either low end value.
+ EXPECT_TRUE(internal::CheckStudyLowEndDevice(filter, true));
+ EXPECT_TRUE(internal::CheckStudyLowEndDevice(filter, false));
+
+ filter.set_is_low_end_device(true);
+ EXPECT_TRUE(internal::CheckStudyLowEndDevice(filter, true));
+ EXPECT_FALSE(internal::CheckStudyLowEndDevice(filter, false));
+
+ filter.set_is_low_end_device(false);
+ EXPECT_FALSE(internal::CheckStudyLowEndDevice(filter, true));
+ EXPECT_TRUE(internal::CheckStudyLowEndDevice(filter, false));
+}
+
TEST(VariationsStudyFilteringTest, CheckStudyStartDate) {
const base::Time now = base::Time::Now();
const base::TimeDelta delta = base::TimeDelta::FromHours(1);
@@ -276,7 +285,7 @@ TEST(VariationsStudyFilteringTest, CheckStudyStartDate) {
{now + delta, false},
};
- Study_Filter filter;
+ Study::Filter filter;
// Start date not set should result in true.
EXPECT_TRUE(internal::CheckStudyStartDate(filter, now));
@@ -299,7 +308,7 @@ TEST(VariationsStudyFilteringTest, CheckStudyEndDate) {
{now - delta, false}, {now + delta, true},
};
- Study_Filter filter;
+ Study::Filter filter;
// End date not set should result in true.
EXPECT_TRUE(internal::CheckStudyEndDate(filter, now));
@@ -354,7 +363,7 @@ TEST(VariationsStudyFilteringTest, CheckStudyVersion) {
{ "1.*", "2.3.4", false },
};
- Study_Filter filter;
+ Study::Filter filter;
// Min/max version not set should result in true.
EXPECT_TRUE(internal::CheckStudyVersion(filter, base::Version("1.2.3")));
@@ -438,7 +447,7 @@ TEST(VariationsStudyFilteringTest, CheckStudyHardwareClass) {
};
for (size_t i = 0; i < arraysize(test_cases); ++i) {
- Study_Filter filter;
+ Study::Filter filter;
for (const std::string& cur : base::SplitString(
test_cases[i].hardware_class, ",",
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL))
@@ -488,7 +497,7 @@ TEST(VariationsStudyFilteringTest, CheckStudyCountry) {
};
for (const auto& test : test_cases) {
- Study_Filter filter;
+ Study::Filter filter;
for (const std::string& country : base::SplitString(
test.country, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL))
filter.add_country(country);
@@ -526,11 +535,16 @@ TEST(VariationsStudyFilteringTest, FilterAndValidateStudies) {
AddExperiment("A", 10, study3);
AddExperiment("Default", 25, study3);
+ ClientFilterableState client_state;
+ client_state.locale = "en-CA";
+ client_state.reference_date = base::Time::Now();
+ client_state.version = base::Version("20.0.0.0");
+ client_state.channel = Study::STABLE;
+ client_state.form_factor = Study::DESKTOP;
+ client_state.platform = Study::PLATFORM_ANDROID;
+
std::vector<ProcessedStudy> processed_studies;
- FilterAndValidateStudies(seed, "en-CA", base::Time::Now(),
- base::Version("20.0.0.0"), Study_Channel_STABLE,
- Study_FormFactor_DESKTOP, "", "", "",
- &processed_studies);
+ FilterAndValidateStudies(seed, client_state, &processed_studies);
// Check that only the first kTrial1Name study was kept.
ASSERT_EQ(2U, processed_studies.size());
@@ -544,27 +558,27 @@ TEST(VariationsStudyFilteringTest, FilterAndValidateStudiesWithCountry) {
const char kPermanentCountry[] = "us";
struct {
- Study_Consistency consistency;
+ Study::Consistency consistency;
const char* filter_country;
const char* filter_exclude_country;
bool expect_study_kept;
} test_cases[] = {
// Country-agnostic studies should be kept regardless of country.
- {Study_Consistency_SESSION, nullptr, nullptr, true},
- {Study_Consistency_PERMANENT, nullptr, nullptr, true},
+ {Study::SESSION, nullptr, nullptr, true},
+ {Study::PERMANENT, nullptr, nullptr, true},
// Session-consistency studies should obey the country code in the seed.
- {Study_Consistency_SESSION, kSessionCountry, nullptr, true},
- {Study_Consistency_SESSION, nullptr, kSessionCountry, false},
- {Study_Consistency_SESSION, kPermanentCountry, nullptr, false},
- {Study_Consistency_SESSION, nullptr, kPermanentCountry, true},
+ {Study::SESSION, kSessionCountry, nullptr, true},
+ {Study::SESSION, nullptr, kSessionCountry, false},
+ {Study::SESSION, kPermanentCountry, nullptr, false},
+ {Study::SESSION, nullptr, kPermanentCountry, true},
// Permanent-consistency studies should obey the permanent-consistency
// country code.
- {Study_Consistency_PERMANENT, kPermanentCountry, nullptr, true},
- {Study_Consistency_PERMANENT, nullptr, kPermanentCountry, false},
- {Study_Consistency_PERMANENT, kSessionCountry, nullptr, false},
- {Study_Consistency_PERMANENT, nullptr, kSessionCountry, true},
+ {Study::PERMANENT, kPermanentCountry, nullptr, true},
+ {Study::PERMANENT, nullptr, kPermanentCountry, false},
+ {Study::PERMANENT, kSessionCountry, nullptr, false},
+ {Study::PERMANENT, nullptr, kSessionCountry, true},
};
for (const auto& test : test_cases) {
@@ -579,16 +593,45 @@ TEST(VariationsStudyFilteringTest, FilterAndValidateStudiesWithCountry) {
if (test.filter_exclude_country)
study->mutable_filter()->add_exclude_country(test.filter_exclude_country);
+ ClientFilterableState client_state;
+ client_state.locale = "en-CA";
+ client_state.reference_date = base::Time::Now();
+ client_state.version = base::Version("20.0.0.0");
+ client_state.channel = Study::STABLE;
+ client_state.form_factor = Study::DESKTOP;
+ client_state.platform = Study::PLATFORM_ANDROID;
+ client_state.session_consistency_country = kSessionCountry;
+ client_state.permanent_consistency_country = kPermanentCountry;
+
std::vector<ProcessedStudy> processed_studies;
- FilterAndValidateStudies(seed, "en-CA", base::Time::Now(),
- base::Version("20.0.0.0"), Study_Channel_STABLE,
- Study_FormFactor_DESKTOP, "", kSessionCountry,
- kPermanentCountry, &processed_studies);
+ FilterAndValidateStudies(seed, client_state, &processed_studies);
EXPECT_EQ(test.expect_study_kept, !processed_studies.empty());
}
}
+TEST(VariationsStudyFilteringTest, GetClientCountryForStudy_Session) {
+ ClientFilterableState client_state;
+ client_state.session_consistency_country = "session_country";
+ client_state.permanent_consistency_country = "permanent_country";
+
+ Study study;
+ study.set_consistency(Study::SESSION);
+ EXPECT_EQ("session_country",
+ internal::GetClientCountryForStudy(study, client_state));
+}
+
+TEST(VariationsStudyFilteringTest, GetClientCountryForStudy_Permanent) {
+ ClientFilterableState client_state;
+ client_state.session_consistency_country = "session_country";
+ client_state.permanent_consistency_country = "permanent_country";
+
+ Study study;
+ study.set_consistency(Study::PERMANENT);
+ EXPECT_EQ("permanent_country",
+ internal::GetClientCountryForStudy(study, client_state));
+}
+
TEST(VariationsStudyFilteringTest, IsStudyExpired) {
const base::Time now = base::Time::Now();
const base::TimeDelta delta = base::TimeDelta::FromHours(1);
@@ -618,7 +661,7 @@ TEST(VariationsStudyFilteringTest, ValidateStudy) {
Study study;
study.set_default_experiment_name("def");
AddExperiment("abc", 100, &study);
- Study_Experiment* default_group = AddExperiment("def", 200, &study);
+ Study::Experiment* default_group = AddExperiment("def", 200, &study);
ProcessedStudy processed_study;
EXPECT_TRUE(processed_study.Init(&study, false));
@@ -653,7 +696,7 @@ TEST(VariationsStudyFilteringTest, ValidateStudy) {
default_group->set_name("def");
EXPECT_TRUE(processed_study.Init(&study, false));
- Study_Experiment* repeated_group = study.add_experiment();
+ Study::Experiment* repeated_group = study.add_experiment();
repeated_group->set_name("abc");
repeated_group->set_probability_weight(1);
EXPECT_FALSE(processed_study.Init(&study, false));
diff --git a/chromium/components/variations/synthetic_trial_registry.cc b/chromium/components/variations/synthetic_trial_registry.cc
new file mode 100644
index 00000000000..28e162cd1a3
--- /dev/null
+++ b/chromium/components/variations/synthetic_trial_registry.cc
@@ -0,0 +1,85 @@
+// 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/variations/synthetic_trial_registry.h"
+
+namespace variations {
+
+SyntheticTrialRegistry::SyntheticTrialRegistry() = default;
+SyntheticTrialRegistry::~SyntheticTrialRegistry() = default;
+
+void SyntheticTrialRegistry::AddSyntheticTrialObserver(
+ SyntheticTrialObserver* observer) {
+ synthetic_trial_observer_list_.AddObserver(observer);
+ if (!synthetic_trial_groups_.empty())
+ observer->OnSyntheticTrialsChanged(synthetic_trial_groups_);
+}
+
+void SyntheticTrialRegistry::RemoveSyntheticTrialObserver(
+ SyntheticTrialObserver* observer) {
+ synthetic_trial_observer_list_.RemoveObserver(observer);
+}
+
+void SyntheticTrialRegistry::RegisterSyntheticFieldTrial(
+ const SyntheticTrialGroup& trial) {
+ for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
+ if (synthetic_trial_groups_[i].id.name == trial.id.name) {
+ if (synthetic_trial_groups_[i].id.group != trial.id.group) {
+ synthetic_trial_groups_[i].id.group = trial.id.group;
+ synthetic_trial_groups_[i].start_time = base::TimeTicks::Now();
+ NotifySyntheticTrialObservers();
+ }
+ return;
+ }
+ }
+
+ SyntheticTrialGroup trial_group = trial;
+ trial_group.start_time = base::TimeTicks::Now();
+ synthetic_trial_groups_.push_back(trial_group);
+ NotifySyntheticTrialObservers();
+}
+
+void SyntheticTrialRegistry::RegisterSyntheticMultiGroupFieldTrial(
+ uint32_t trial_name_hash,
+ const std::vector<uint32_t>& group_name_hashes) {
+ auto has_same_trial_name = [trial_name_hash](const SyntheticTrialGroup& x) {
+ return x.id.name == trial_name_hash;
+ };
+ synthetic_trial_groups_.erase(
+ std::remove_if(synthetic_trial_groups_.begin(),
+ synthetic_trial_groups_.end(), has_same_trial_name),
+ synthetic_trial_groups_.end());
+
+ if (group_name_hashes.empty())
+ return;
+
+ SyntheticTrialGroup trial_group(trial_name_hash, group_name_hashes[0]);
+ trial_group.start_time = base::TimeTicks::Now();
+ for (uint32_t group_name_hash : group_name_hashes) {
+ // Note: Adding the trial group will copy it, so this re-uses the same
+ // |trial_group| struct for convenience (e.g. so start_time's all match).
+ trial_group.id.group = group_name_hash;
+ synthetic_trial_groups_.push_back(trial_group);
+ }
+ NotifySyntheticTrialObservers();
+}
+
+void SyntheticTrialRegistry::NotifySyntheticTrialObservers() {
+ for (SyntheticTrialObserver& observer : synthetic_trial_observer_list_) {
+ observer.OnSyntheticTrialsChanged(synthetic_trial_groups_);
+ }
+}
+
+void SyntheticTrialRegistry::GetSyntheticFieldTrialsOlderThan(
+ base::TimeTicks time,
+ std::vector<ActiveGroupId>* synthetic_trials) {
+ DCHECK(synthetic_trials);
+ synthetic_trials->clear();
+ for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
+ if (synthetic_trial_groups_[i].start_time <= time)
+ synthetic_trials->push_back(synthetic_trial_groups_[i].id);
+ }
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/synthetic_trial_registry.h b/chromium/components/variations/synthetic_trial_registry.h
new file mode 100644
index 00000000000..db37ade097e
--- /dev/null
+++ b/chromium/components/variations/synthetic_trial_registry.h
@@ -0,0 +1,79 @@
+// 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_VARIATIONS_SYNTHETIC_TRIAL_REGISTRY_H_
+#define COMPONENTS_VARIATIONS_SYNTHETIC_TRIAL_REGISTRY_H_
+
+#include <vector>
+
+#include "base/observer_list.h"
+#include "components/variations/synthetic_trials.h"
+
+namespace metrics {
+class MetricsServiceAccessor;
+} // namespace metrics
+
+namespace variations {
+
+struct ActiveGroupId;
+class FieldTrialsProvider;
+class FieldTrialsProviderTest;
+
+class SyntheticTrialRegistry {
+ public:
+ SyntheticTrialRegistry();
+ ~SyntheticTrialRegistry();
+
+ // Adds an observer to be notified when the synthetic trials list changes.
+ void AddSyntheticTrialObserver(SyntheticTrialObserver* observer);
+
+ // Removes an existing observer of synthetic trials list changes.
+ void RemoveSyntheticTrialObserver(SyntheticTrialObserver* observer);
+
+ private:
+ friend metrics::MetricsServiceAccessor;
+ friend FieldTrialsProvider;
+ friend FieldTrialsProviderTest;
+ FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest, RegisterSyntheticTrial);
+ FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest,
+ RegisterSyntheticMultiGroupFieldTrial);
+ FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest,
+ GetSyntheticFieldTrialActiveGroups);
+
+ // Registers a field trial name and group to be used to annotate a UMA report
+ // with a particular Chrome configuration state. A UMA report will be
+ // annotated with this trial group if and only if all events in the report
+ // were created after the trial is registered. Only one group name may be
+ // registered at a time for a given trial_name. Only the last group name that
+ // is registered for a given trial name will be recorded. The values passed
+ // in must not correspond to any real field trial in the code.
+ // Note: Should not be used to replace trials that were registered with
+ // RegisterMultiGroupSyntheticFieldTrial().
+ void RegisterSyntheticFieldTrial(const SyntheticTrialGroup& trial_group);
+
+ // Similar to RegisterSyntheticFieldTrial(), but registers a synthetic trial
+ // that has multiple active groups for a given trial name hash. Any previous
+ // groups registered for |trial_name_hash| will be replaced.
+ void RegisterSyntheticMultiGroupFieldTrial(
+ uint32_t trial_name_hash,
+ const std::vector<uint32_t>& group_name_hashes);
+
+ // Returns a list of synthetic field trials that are older than |time|.
+ void GetSyntheticFieldTrialsOlderThan(
+ base::TimeTicks time,
+ std::vector<ActiveGroupId>* synthetic_trials);
+
+ // Notifies observers on a synthetic trial list change.
+ void NotifySyntheticTrialObservers();
+
+ // Field trial groups that map to Chrome configuration states.
+ std::vector<SyntheticTrialGroup> synthetic_trial_groups_;
+
+ // List of observers of |synthetic_trial_groups_| changes.
+ base::ObserverList<SyntheticTrialObserver> synthetic_trial_observer_list_;
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_SYNTHETIC_TRIAL_REGISTRY_H_
diff --git a/chromium/components/variations/synthetic_trial_registry_unittest.cc b/chromium/components/variations/synthetic_trial_registry_unittest.cc
new file mode 100644
index 00000000000..632aa3f95b9
--- /dev/null
+++ b/chromium/components/variations/synthetic_trial_registry_unittest.cc
@@ -0,0 +1,177 @@
+// 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/variations/synthetic_trial_registry.h"
+
+#include "base/strings/stringprintf.h"
+#include "components/variations/active_field_trials.h"
+#include "components/variations/metrics_util.h"
+#include "components/variations/synthetic_trials_active_group_id_provider.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace variations {
+
+namespace {
+
+class SyntheticTrialRegistryTest : public ::testing::Test {
+ public:
+ // Returns true if there is a synthetic trial in the given vector that matches
+ // the given trial name and trial group; returns false otherwise.
+ bool HasSyntheticTrial(const std::vector<ActiveGroupId>& synthetic_trials,
+ const std::string& trial_name,
+ const std::string& trial_group) {
+ uint32_t trial_name_hash = metrics::HashName(trial_name);
+ uint32_t trial_group_hash = metrics::HashName(trial_group);
+ for (const ActiveGroupId& trial : synthetic_trials) {
+ if (trial.name == trial_name_hash && trial.group == trial_group_hash)
+ return true;
+ }
+ return false;
+ }
+
+ // Waits until base::TimeTicks::Now() no longer equals |value|. This should
+ // take between 1-15ms per the documented resolution of base::TimeTicks.
+ void WaitUntilTimeChanges(const base::TimeTicks& value) {
+ while (base::TimeTicks::Now() == value) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
+ }
+ }
+};
+
+} // namespace
+
+TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
+ SyntheticTrialRegistry registry;
+
+ // Add two synthetic trials and confirm that they show up in the list.
+ SyntheticTrialGroup trial1(metrics::HashName("TestTrial1"),
+ metrics::HashName("Group1"));
+ registry.RegisterSyntheticFieldTrial(trial1);
+
+ SyntheticTrialGroup trial2(metrics::HashName("TestTrial2"),
+ metrics::HashName("Group2"));
+ registry.RegisterSyntheticFieldTrial(trial2);
+ // Ensure that time has advanced by at least a tick before proceeding.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ // Save the time when the log was started (it's okay for this to be greater
+ // than the time recorded by the above call since it's used to ensure the
+ // value changes).
+ const base::TimeTicks begin_log_time = base::TimeTicks::Now();
+
+ std::vector<ActiveGroupId> synthetic_trials;
+ registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
+ &synthetic_trials);
+ EXPECT_EQ(2U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group1"));
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
+
+ // Ensure that time has advanced by at least a tick before proceeding.
+ WaitUntilTimeChanges(begin_log_time);
+
+ // Change the group for the first trial after the log started.
+ SyntheticTrialGroup trial3(metrics::HashName("TestTrial1"),
+ metrics::HashName("Group2"));
+ registry.RegisterSyntheticFieldTrial(trial3);
+ registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
+ EXPECT_EQ(1U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
+
+ // Add a new trial after the log started and confirm that it doesn't show up.
+ SyntheticTrialGroup trial4(metrics::HashName("TestTrial3"),
+ metrics::HashName("Group3"));
+ registry.RegisterSyntheticFieldTrial(trial4);
+ registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
+ EXPECT_EQ(1U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
+
+ // Ensure that time has advanced by at least a tick before proceeding.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ // Start a new log and ensure all three trials appear in it.
+ registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
+ &synthetic_trials);
+ EXPECT_EQ(3U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group2"));
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial3", "Group3"));
+}
+
+TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticMultiGroupFieldTrial) {
+ SyntheticTrialRegistry registry;
+
+ // Register a synthetic trial TestTrial1 with groups A and B.
+ uint32_t trial_name_hash = metrics::HashName("TestTrial1");
+ std::vector<uint32_t> group_name_hashes = {metrics::HashName("A"),
+ metrics::HashName("B")};
+ registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
+ group_name_hashes);
+ // Ensure that time has advanced by at least a tick before proceeding.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ std::vector<ActiveGroupId> synthetic_trials;
+ registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
+ &synthetic_trials);
+ EXPECT_EQ(2U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "A"));
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "B"));
+
+ // Change the group for the trial to a single group.
+ group_name_hashes = {metrics::HashName("X")};
+ registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
+ group_name_hashes);
+ // Ensure that time has advanced by at least a tick before proceeding.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
+ &synthetic_trials);
+ EXPECT_EQ(1U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "X"));
+
+ // Register a trial with no groups, which should effectively remove the trial.
+ group_name_hashes.clear();
+ registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
+ group_name_hashes);
+ // Ensure that time has advanced by at least a tick before proceeding.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
+ &synthetic_trials);
+}
+
+TEST_F(SyntheticTrialRegistryTest, GetSyntheticFieldTrialActiveGroups) {
+ SyntheticTrialRegistry registry;
+
+ // Instantiate and setup the corresponding singleton observer which tracks the
+ // creation of all SyntheticTrialGroups.
+ registry.AddSyntheticTrialObserver(
+ SyntheticTrialsActiveGroupIdProvider::GetInstance());
+
+ // Add two synthetic trials and confirm that they show up in the list.
+ SyntheticTrialGroup trial1(metrics::HashName("TestTrial1"),
+ metrics::HashName("Group1"));
+ registry.RegisterSyntheticFieldTrial(trial1);
+
+ SyntheticTrialGroup trial2(metrics::HashName("TestTrial2"),
+ metrics::HashName("Group2"));
+ registry.RegisterSyntheticFieldTrial(trial2);
+
+ // Ensure that time has advanced by at least a tick before proceeding.
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+
+ // Now get the list of currently active groups.
+ std::vector<std::string> output;
+ GetSyntheticTrialGroupIdsAsString(&output);
+ EXPECT_EQ(2U, output.size());
+
+ std::string trial1_hash =
+ base::StringPrintf("%x-%x", trial1.id.name, trial1.id.group);
+ EXPECT_TRUE(base::ContainsValue(output, trial1_hash));
+
+ std::string trial2_hash =
+ base::StringPrintf("%x-%x", trial2.id.name, trial2.id.group);
+ EXPECT_TRUE(base::ContainsValue(output, trial2_hash));
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/synthetic_trials_active_group_id_provider.cc b/chromium/components/variations/synthetic_trials_active_group_id_provider.cc
new file mode 100644
index 00000000000..527425f7fdb
--- /dev/null
+++ b/chromium/components/variations/synthetic_trials_active_group_id_provider.cc
@@ -0,0 +1,42 @@
+// 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/variations/synthetic_trials_active_group_id_provider.h"
+
+#include "base/memory/singleton.h"
+#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_util.h"
+
+namespace variations {
+
+SyntheticTrialsActiveGroupIdProvider*
+SyntheticTrialsActiveGroupIdProvider::GetInstance() {
+ return base::Singleton<SyntheticTrialsActiveGroupIdProvider>::get();
+}
+
+SyntheticTrialsActiveGroupIdProvider::SyntheticTrialsActiveGroupIdProvider() {}
+
+SyntheticTrialsActiveGroupIdProvider::~SyntheticTrialsActiveGroupIdProvider() {}
+
+void SyntheticTrialsActiveGroupIdProvider::OnSyntheticTrialsChanged(
+ const std::vector<SyntheticTrialGroup>& groups) {
+ {
+ base::AutoLock scoped_lock(lock_);
+ synthetic_trials_.clear();
+ for (const auto& group : groups)
+ synthetic_trials_.push_back(group.id);
+ }
+ // Update the experiments lists for crash reports to include the newly added
+ // group.
+ SetVariationListCrashKeys();
+}
+
+void SyntheticTrialsActiveGroupIdProvider::GetActiveGroupIds(
+ std::vector<ActiveGroupId>* output) {
+ base::AutoLock scoped_lock(lock_);
+ for (const auto& group_id : synthetic_trials_)
+ output->push_back(group_id);
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/synthetic_trials_active_group_id_provider.h b/chromium/components/variations/synthetic_trials_active_group_id_provider.h
new file mode 100644
index 00000000000..33bf5514227
--- /dev/null
+++ b/chromium/components/variations/synthetic_trials_active_group_id_provider.h
@@ -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.
+
+#ifndef COMPONENTS_VARIATIONS_SYNTEHTIC_TRIALS_ACTIVE_GROUP_ID_PROVIDER_H_
+#define COMPONENTS_VARIATIONS_SYNTEHTIC_TRIALS_ACTIVE_GROUP_ID_PROVIDER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "components/variations/active_field_trials.h"
+#include "components/variations/synthetic_trials.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace variations {
+
+// This is a helper class which can observe the creation of SyntheticTrialGroups
+// and later provide a list of active group IDs to be included in the crash
+// reports. This class is a thread-safe singleton.
+class SyntheticTrialsActiveGroupIdProvider : public SyntheticTrialObserver {
+ public:
+ static SyntheticTrialsActiveGroupIdProvider* GetInstance();
+
+ // Populates |output| with currently active synthetic trial groups. |output|
+ // cannot be nullptr.
+ void GetActiveGroupIds(std::vector<ActiveGroupId>* output);
+
+ private:
+ friend struct base::DefaultSingletonTraits<
+ SyntheticTrialsActiveGroupIdProvider>;
+
+ SyntheticTrialsActiveGroupIdProvider();
+ ~SyntheticTrialsActiveGroupIdProvider() override;
+
+ // metrics::SyntheticTrialObserver:
+ void OnSyntheticTrialsChanged(
+ const std::vector<SyntheticTrialGroup>& groups) override;
+
+ std::vector<ActiveGroupId> synthetic_trials_;
+
+ base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyntheticTrialsActiveGroupIdProvider);
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_SYNTEHTIC_TRIALS_ACTIVE_GROUP_ID_PROVIDER_H_
diff --git a/chromium/components/variations/variations_params_manager.cc b/chromium/components/variations/variations_params_manager.cc
index 18ebdeed0ad..be71186b466 100644
--- a/chromium/components/variations/variations_params_manager.cc
+++ b/chromium/components/variations/variations_params_manager.cc
@@ -6,15 +6,17 @@
#include <utility>
+#include "base/base_switches.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/test/scoped_feature_list.h"
+#include "components/variations/field_trial_config/field_trial_util.h"
#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_switches.h"
namespace variations {
namespace testing {
-
namespace {
// The fixed testing group created in the provided trail when setting up params.
@@ -23,7 +25,7 @@ const char kGroupTesting[] = "Testing";
base::FieldTrial* CreateFieldTrialWithParams(
const std::string& trial_name,
const std::map<std::string, std::string>& param_values) {
- variations::AssociateVariationParams(trial_name, kGroupTesting, param_values);
+ AssociateVariationParams(trial_name, kGroupTesting, param_values);
return base::FieldTrialList::CreateFieldTrial(trial_name, kGroupTesting);
}
@@ -79,11 +81,11 @@ void VariationParamsManager::SetVariationParamsWithFeatureAssociations(
}
void VariationParamsManager::ClearAllVariationIDs() {
- variations::testing::ClearAllVariationIDs();
+ testing::ClearAllVariationIDs();
}
void VariationParamsManager::ClearAllVariationParams() {
- variations::testing::ClearAllVariationParams();
+ testing::ClearAllVariationParams();
// When the scoped feature list is destroyed, it puts back the original
// feature list that was there when InitWithFeatureList() was called.
scoped_feature_list_.reset(new base::test::ScopedFeatureList());
@@ -92,5 +94,35 @@ void VariationParamsManager::ClearAllVariationParams() {
field_trial_list_ = base::MakeUnique<base::FieldTrialList>(nullptr);
}
+// static
+void VariationParamsManager::AppendVariationParams(
+ const std::string& trial_name,
+ const std::string& trial_group_name,
+ const std::map<std::string, std::string>& param_values,
+ base::CommandLine* command_line) {
+ // Register the trial group.
+ command_line->AppendSwitchASCII(
+ ::switches::kForceFieldTrials,
+ EscapeValue(trial_name) + "/" + EscapeValue(trial_group_name));
+
+ // Associate |param_values| with the trial group.
+ std::string params_arg =
+ EscapeValue(trial_name) + "." + EscapeValue(trial_group_name) + ":";
+ bool first = true;
+ for (const auto& param : param_values) {
+ // Separate each |param|.
+ if (!first)
+ params_arg += "/";
+ first = false;
+
+ // Append each |param|.
+ const std::string& name = param.first;
+ const std::string& value = param.second;
+ params_arg += EscapeValue(name) + "/" + EscapeValue(value);
+ }
+ command_line->AppendSwitchASCII(variations::switches::kForceFieldTrialParams,
+ params_arg);
+}
+
} // namespace testing
} // namespace variations
diff --git a/chromium/components/variations/variations_params_manager.h b/chromium/components/variations/variations_params_manager.h
index 6a65e189abf..2c61c52b904 100644
--- a/chromium/components/variations/variations_params_manager.h
+++ b/chromium/components/variations/variations_params_manager.h
@@ -14,6 +14,7 @@
#include "base/metrics/field_trial.h"
namespace base {
+class CommandLine;
class FieldTrialList;
namespace test {
@@ -70,6 +71,19 @@ class VariationParamsManager {
// Clears all of the associated params.
void ClearAllVariationParams();
+ // Appends command line switches to |command_line| in a way that mimics
+ // SetVariationParams.
+ //
+ // This static method is useful in situations where using
+ // VariationParamsManager directly would have resulted in initializing
+ // FieldTrialList twice (once from ChromeBrowserMainParts::SetupFieldTrials
+ // and once from VariationParamsManager).
+ static void AppendVariationParams(
+ const std::string& trial_name,
+ const std::string& trial_group_name,
+ const std::map<std::string, std::string>& param_values,
+ base::CommandLine* command_line);
+
private:
std::unique_ptr<base::FieldTrialList> field_trial_list_;
std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
diff --git a/chromium/components/variations/variations_request_scheduler.cc b/chromium/components/variations/variations_request_scheduler.cc
index dfc39d9c138..42c47b67543 100644
--- a/chromium/components/variations/variations_request_scheduler.cc
+++ b/chromium/components/variations/variations_request_scheduler.cc
@@ -47,8 +47,7 @@ void VariationsRequestScheduler::OnAppEnterForeground() {
base::TimeDelta VariationsRequestScheduler::GetFetchPeriod() const {
// The fetch interval can be overridden by a variation param.
std::string period_min_str =
- variations::GetVariationParamValue("VariationsServiceControl",
- "fetch_period_min");
+ GetVariationParamValue("VariationsServiceControl", "fetch_period_min");
size_t period_min;
if (base::StringToSizeT(period_min_str, &period_min))
return base::TimeDelta::FromMinutes(period_min);
diff --git a/chromium/components/variations/variations_seed_processor.cc b/chromium/components/variations/variations_seed_processor.cc
index 5c88264c00a..9ac9388f1e3 100644
--- a/chromium/components/variations/variations_seed_processor.cc
+++ b/chromium/components/variations/variations_seed_processor.cc
@@ -14,6 +14,7 @@
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
+#include "components/variations/client_filterable_state.h"
#include "components/variations/processed_study.h"
#include "components/variations/study_filtering.h"
#include "components/variations/variations_associated_data.h"
@@ -101,9 +102,11 @@ void RegisterFeatureOverrides(const ProcessedStudy& processed_study,
base::FeatureList* feature_list) {
const std::string& group_name = trial->GetGroupNameWithoutActivation();
int experiment_index = processed_study.GetExperimentIndexByName(group_name);
- // The field trial was defined from |study|, so the active experiment's name
- // must be in the |study|.
- DCHECK_NE(-1, experiment_index);
+ // If the chosen experiment was not found in the study, simply return.
+ // Although not normally expected, but could happen in exception cases, see
+ // tests: ExpiredStudy_NoDefaultGroup, ExistingFieldTrial_ExpiredByConfig
+ if (experiment_index == -1)
+ return;
const Study& study = *processed_study.study();
const Study_Experiment& experiment = study.experiment(experiment_index);
@@ -173,26 +176,17 @@ VariationsSeedProcessor::~VariationsSeedProcessor() {
void VariationsSeedProcessor::CreateTrialsFromSeed(
const VariationsSeed& seed,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& session_consistency_country,
- const std::string& permanent_consistency_country,
+ const ClientFilterableState& client_state,
const UIStringOverrideCallback& override_callback,
const base::FieldTrial::EntropyProvider* low_entropy_provider,
base::FeatureList* feature_list) {
std::vector<ProcessedStudy> filtered_studies;
- FilterAndValidateStudies(seed, locale, reference_date, version, channel,
- form_factor, hardware_class,
- session_consistency_country,
- permanent_consistency_country, &filtered_studies);
-
- for (size_t i = 0; i < filtered_studies.size(); ++i)
- CreateTrialFromStudy(filtered_studies[i], override_callback,
- low_entropy_provider, feature_list);
+ FilterAndValidateStudies(seed, client_state, &filtered_studies);
+
+ for (const ProcessedStudy& study : filtered_studies) {
+ CreateTrialFromStudy(study, override_callback, low_entropy_provider,
+ feature_list);
+ }
}
// static
@@ -325,13 +319,13 @@ void VariationsSeedProcessor::CreateTrialFromStudy(
// UI Strings can only be overridden from ACTIVATION_AUTO experiments.
int experiment_index = processed_study.GetExperimentIndexByName(group_name);
-
- // The field trial was defined from |study|, so the active experiment's name
- // must be in the |study|.
- DCHECK_NE(-1, experiment_index);
-
- ApplyUIStringOverrides(study.experiment(experiment_index),
- override_callback);
+ // If the chosen experiment was not found in the study, simply return.
+ // Although not normally expected, but could happen in exception cases, see
+ // tests: ExpiredStudy_NoDefaultGroup, ExistingFieldTrial_ExpiredByConfig
+ if (experiment_index != -1) {
+ ApplyUIStringOverrides(study.experiment(experiment_index),
+ override_callback);
+ }
}
}
diff --git a/chromium/components/variations/variations_seed_processor.h b/chromium/components/variations/variations_seed_processor.h
index bcb938ad064..140dc2714a9 100644
--- a/chromium/components/variations/variations_seed_processor.h
+++ b/chromium/components/variations/variations_seed_processor.h
@@ -28,6 +28,7 @@ class FeatureList;
namespace variations {
class ProcessedStudy;
+struct ClientFilterableState;
// Helper class to instantiate field trials from a variations seed.
class VariationsSeedProcessor {
@@ -38,20 +39,13 @@ class VariationsSeedProcessor {
VariationsSeedProcessor();
virtual ~VariationsSeedProcessor();
- // Creates field trials from the specified variations |seed|, based on the
- // specified configuration, as specified in the parameters. Any study that
- // should use low entropy will use |low_entropy_provider| for group
- // selection. These studies are defined by ShouldStudyUseLowEntropy;
+ // Creates field trials from the specified variations |seed|, filtered
+ // according to the client's |client_state|. Any study that should use low
+ // entropy will use |low_entropy_provider| for group selection. These studies
+ // are defined by ShouldStudyUseLowEntropy;
void CreateTrialsFromSeed(
const VariationsSeed& seed,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& session_consistency_country,
- const std::string& permanent_consistency_country,
+ const ClientFilterableState& client_state,
const UIStringOverrideCallback& override_callback,
const base::FieldTrial::EntropyProvider* low_entropy_provider,
base::FeatureList* feature_list);
diff --git a/chromium/components/variations/variations_seed_processor_unittest.cc b/chromium/components/variations/variations_seed_processor_unittest.cc
index 01b36af19c3..da4cb4c53f9 100644
--- a/chromium/components/variations/variations_seed_processor_unittest.cc
+++ b/chromium/components/variations/variations_seed_processor_unittest.cc
@@ -23,13 +23,13 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/scoped_feature_list.h"
+#include "components/variations/client_filterable_state.h"
#include "components/variations/processed_study.h"
#include "components/variations/study_filtering.h"
#include "components/variations/variations_associated_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace variations {
-
namespace {
// Converts |time| to Study proto format.
@@ -262,7 +262,13 @@ TEST_F(VariationsSeedProcessorTest,
const base::Time year_ago =
base::Time::Now() - base::TimeDelta::FromDays(365);
- const base::Version version("20.0.0.0");
+ ClientFilterableState client_state;
+ client_state.locale = "en-CA";
+ client_state.reference_date = base::Time::Now();
+ client_state.version = base::Version("20.0.0.0");
+ client_state.channel = Study::STABLE;
+ client_state.form_factor = Study::DESKTOP;
+ client_state.platform = Study::PLATFORM_ANDROID;
// Check that adding [expired, non-expired] activates the non-expired one.
ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
@@ -270,10 +276,9 @@ TEST_F(VariationsSeedProcessorTest,
base::FeatureList feature_list;
base::FieldTrialList field_trial_list(nullptr);
study1->set_expiry_date(TimeToProtoTime(year_ago));
- seed_processor.CreateTrialsFromSeed(
- seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE,
- Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback(),
- nullptr, &feature_list);
+ seed_processor.CreateTrialsFromSeed(seed, client_state,
+ override_callback_.callback(), nullptr,
+ &feature_list);
EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
}
@@ -284,10 +289,9 @@ TEST_F(VariationsSeedProcessorTest,
base::FieldTrialList field_trial_list(nullptr);
study1->clear_expiry_date();
study2->set_expiry_date(TimeToProtoTime(year_ago));
- seed_processor.CreateTrialsFromSeed(
- seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE,
- Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback(),
- nullptr, &feature_list);
+ seed_processor.CreateTrialsFromSeed(seed, client_state,
+ override_callback_.callback(), nullptr,
+ &feature_list);
EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
}
}
@@ -532,11 +536,18 @@ TEST_F(VariationsSeedProcessorTest, StartsActive) {
AddExperiment("Default", 0, study3);
study3->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT);
+ ClientFilterableState client_state;
+ client_state.locale = "en-CA";
+ client_state.reference_date = base::Time::Now();
+ client_state.version = base::Version("20.0.0.0");
+ client_state.channel = Study::STABLE;
+ client_state.form_factor = Study::DESKTOP;
+ client_state.platform = Study::PLATFORM_ANDROID;
+
VariationsSeedProcessor seed_processor;
- seed_processor.CreateTrialsFromSeed(
- seed, "en-CA", base::Time::Now(), base::Version("20.0.0.0"),
- Study_Channel_STABLE, Study_FormFactor_DESKTOP, "", "", "",
- override_callback_.callback(), nullptr, &feature_list_);
+ seed_processor.CreateTrialsFromSeed(seed, client_state,
+ override_callback_.callback(), nullptr,
+ &feature_list_);
// Non-specified and ACTIVATION_EXPLICIT should not start active, but
// ACTIVATION_AUTO should.
@@ -863,6 +874,60 @@ TEST_F(VariationsSeedProcessorTest, NoDefaultExperiment) {
base::FieldTrialList::FindFullName("Study1"));
}
+TEST_F(VariationsSeedProcessorTest, ExistingFieldTrial_ExpiredByConfig) {
+ static struct base::Feature kFeature {
+ "FeatureName", base::FEATURE_ENABLED_BY_DEFAULT
+ };
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // In this case, an existing forced trial exists with a different default
+ // group than the study config, which is expired. This tests that we don't
+ // crash in such a case.
+ auto* trial = base::FieldTrialList::FactoryGetFieldTrial(
+ "Study1", 100, "ExistingDefault", base::FieldTrialList::kNoExpirationYear,
+ 1, 1, base::FieldTrial::SESSION_RANDOMIZED, nullptr);
+ trial->AppendGroup("A", 100);
+ trial->SetForced();
+
+ Study study;
+ study.set_name("Study1");
+ const base::Time year_ago =
+ base::Time::Now() - base::TimeDelta::FromDays(365);
+ study.set_expiry_date(TimeToProtoTime(year_ago));
+ auto* exp1 = AddExperiment("A", 1, &study);
+ exp1->mutable_feature_association()->add_enable_feature(kFeature.name);
+ AddExperiment("Default", 1, &study);
+ study.set_default_experiment_name("Default");
+
+ EXPECT_TRUE(CreateTrialFromStudy(study));
+
+ // The expected effect is that processing the server config will expire
+ // the existing trial.
+ EXPECT_EQ("ExistingDefault", trial->group_name());
+}
+
+TEST_F(VariationsSeedProcessorTest, ExpiredStudy_NoDefaultGroup) {
+ static struct base::Feature kFeature {
+ "FeatureName", base::FEATURE_ENABLED_BY_DEFAULT
+ };
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // Although it's not expected for the server to provide a study with an expiry
+ // date set, but not default experiment, this tests that we don't crash if
+ // that happens.
+ Study study;
+ study.set_name("Study1");
+ const base::Time year_ago =
+ base::Time::Now() - base::TimeDelta::FromDays(365);
+ study.set_expiry_date(TimeToProtoTime(year_ago));
+ auto* exp1 = AddExperiment("A", 1, &study);
+ exp1->mutable_feature_association()->add_enable_feature(kFeature.name);
+
+ EXPECT_TRUE(CreateTrialFromStudy(study));
+ EXPECT_EQ("VariationsDefaultExperiment",
+ base::FieldTrialList::FindFullName("Study1"));
+}
+
TEST_F(VariationsSeedProcessorTest, LowEntropyStudyTest) {
const std::string kTrial1Name = "A";
const std::string kTrial2Name = "B";
@@ -872,13 +937,13 @@ TEST_F(VariationsSeedProcessorTest, LowEntropyStudyTest) {
VariationsSeed seed;
Study* study1 = seed.add_study();
study1->set_name(kTrial1Name);
- study1->set_consistency(variations::Study_Consistency_PERMANENT);
+ study1->set_consistency(Study::PERMANENT);
study1->set_default_experiment_name(kDefaultName);
AddExperiment(kGroup1Name, 50, study1);
AddExperiment(kDefaultName, 50, study1);
Study* study2 = seed.add_study();
study2->set_name(kTrial2Name);
- study2->set_consistency(variations::Study_Consistency_PERMANENT);
+ study2->set_consistency(Study::PERMANENT);
study2->set_default_experiment_name(kDefaultName);
AddExperiment(kGroup1Name, 50, study2);
AddExperiment(kDefaultName, 50, study2);
diff --git a/chromium/components/variations/variations_seed_simulator.cc b/chromium/components/variations/variations_seed_simulator.cc
index 59b56643adf..05f5f73ebd2 100644
--- a/chromium/components/variations/variations_seed_simulator.cc
+++ b/chromium/components/variations/variations_seed_simulator.cc
@@ -9,6 +9,7 @@
#include <map>
#include "base/metrics/field_trial.h"
+#include "components/variations/client_filterable_state.h"
#include "components/variations/processed_study.h"
#include "components/variations/proto/study.pb.h"
#include "components/variations/study_filtering.h"
@@ -114,19 +115,9 @@ VariationsSeedSimulator::~VariationsSeedSimulator() {
VariationsSeedSimulator::Result VariationsSeedSimulator::SimulateSeedStudies(
const VariationsSeed& seed,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& session_consistency_country,
- const std::string& permanent_consistency_country) {
+ const ClientFilterableState& client_state) {
std::vector<ProcessedStudy> filtered_studies;
- FilterAndValidateStudies(seed, locale, reference_date, version, channel,
- form_factor, hardware_class,
- session_consistency_country,
- permanent_consistency_country, &filtered_studies);
+ FilterAndValidateStudies(seed, client_state, &filtered_studies);
return ComputeDifferences(filtered_studies);
}
diff --git a/chromium/components/variations/variations_seed_simulator.h b/chromium/components/variations/variations_seed_simulator.h
index c18e6c3210a..72986afc6ff 100644
--- a/chromium/components/variations/variations_seed_simulator.h
+++ b/chromium/components/variations/variations_seed_simulator.h
@@ -18,6 +18,7 @@
namespace variations {
class ProcessedStudy;
+struct ClientFilterableState;
class VariationsSeed;
// VariationsSeedSimulator simulates the result of creating a set of studies
@@ -59,14 +60,7 @@ class VariationsSeedSimulator {
// results of the simulation as a set of expected group change counts of each
// type.
Result SimulateSeedStudies(const VariationsSeed& seed,
- const std::string& locale,
- const base::Time& reference_date,
- const base::Version& version,
- Study_Channel channel,
- Study_FormFactor form_factor,
- const std::string& hardware_class,
- const std::string& session_consistency_country,
- const std::string& permanent_consistency_country);
+ const ClientFilterableState& client_state);
private:
friend class VariationsSeedSimulatorTest;
diff --git a/chromium/components/variations/variations_seed_store.cc b/chromium/components/variations/variations_seed_store.cc
index 647cc8dbf9c..73cb6b49aa0 100644
--- a/chromium/components/variations/variations_seed_store.cc
+++ b/chromium/components/variations/variations_seed_store.cc
@@ -10,7 +10,6 @@
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_math.h"
-#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "components/prefs/pref_registry_simple.h"
@@ -29,16 +28,6 @@ namespace variations {
namespace {
-// Signature verification is disabled on mobile platforms for now, since it
-// adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop).
-bool SignatureVerificationEnabled() {
-#if defined(OS_IOS) || defined(OS_ANDROID)
- return false;
-#else
- return true;
-#endif
-}
-
// The ECDSA public key of the variations server for verifying variations seed
// signatures.
const uint8_t kPublicKey[] = {
@@ -51,55 +40,36 @@ const uint8_t kPublicKey[] = {
0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf,
};
-// Note: UMA histogram enum - don't re-order or remove entries.
-enum VariationSeedEmptyState {
- VARIATIONS_SEED_NOT_EMPTY,
- VARIATIONS_SEED_EMPTY,
- VARIATIONS_SEED_CORRUPT,
- VARIATIONS_SEED_INVALID_SIGNATURE,
- VARIATIONS_SEED_CORRUPT_BASE64,
- VARIATIONS_SEED_CORRUPT_PROTOBUF,
- VARIATIONS_SEED_CORRUPT_GZIP,
- VARIATIONS_SEED_EMPTY_ENUM_SIZE,
-};
+// Verifies a variations seed (the serialized proto bytes) with the specified
+// base-64 encoded signature that was received from the server and returns the
+// result. The signature is assumed to be an "ECDSA with SHA-256" signature
+// (see kECDSAWithSHA256AlgorithmID in the .cc file). Returns the result of
+// signature verification.
+VerifySignatureResult VerifySeedSignature(
+ const std::string& seed_bytes,
+ const std::string& base64_seed_signature) {
+ if (base64_seed_signature.empty())
+ return VerifySignatureResult::MISSING_SIGNATURE;
-void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) {
- UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state,
- VARIATIONS_SEED_EMPTY_ENUM_SIZE);
-}
+ std::string signature;
+ if (!base::Base64Decode(base64_seed_signature, &signature))
+ return VerifySignatureResult::DECODE_FAILED;
-enum VariationsSeedStoreResult {
- VARIATIONS_SEED_STORE_SUCCESS,
- VARIATIONS_SEED_STORE_FAILED_EMPTY,
- VARIATIONS_SEED_STORE_FAILED_PARSE,
- VARIATIONS_SEED_STORE_FAILED_SIGNATURE,
- VARIATIONS_SEED_STORE_FAILED_GZIP,
- // DELTA_COUNT is not so much a result of the seed store, but rather counting
- // the number of delta-compressed seeds the SeedStore() function saw. Kept in
- // the same histogram for convenience of comparing against the other values.
- VARIATIONS_SEED_STORE_DELTA_COUNT,
- VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED,
- VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY,
- VARIATIONS_SEED_STORE_FAILED_DELTA_STORE,
- VARIATIONS_SEED_STORE_FAILED_UNGZIP,
- VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS,
- VARIATIONS_SEED_STORE_FAILED_UNSUPPORTED_SEED_FORMAT,
- VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE,
-};
+ crypto::SignatureVerifier verifier;
+ if (!verifier.VerifyInit(crypto::SignatureVerifier::ECDSA_SHA256,
+ reinterpret_cast<const uint8_t*>(signature.data()),
+ signature.size(), kPublicKey,
+ arraysize(kPublicKey))) {
+ return VerifySignatureResult::INVALID_SIGNATURE;
+ }
-void RecordSeedStoreHistogram(VariationsSeedStoreResult result) {
- UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result,
- VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE);
-}
+ verifier.VerifyUpdate(reinterpret_cast<const uint8_t*>(seed_bytes.data()),
+ seed_bytes.size());
+ if (!verifier.VerifyFinal())
+ return VerifySignatureResult::INVALID_SEED;
-// Note: UMA histogram enum - don't re-order or remove entries.
-enum VariationsSeedDateChangeState {
- SEED_DATE_NO_OLD_DATE,
- SEED_DATE_NEW_DATE_OLDER,
- SEED_DATE_SAME_DAY,
- SEED_DATE_NEW_DAY,
- SEED_DATE_ENUM_SIZE,
-};
+ return VerifySignatureResult::VALID_SIGNATURE;
+}
// Truncates a time to the start of the day in UTC. If given a time representing
// 2014-03-11 10:18:03.1 UTC, it will return a time representing
@@ -118,47 +88,30 @@ base::Time TruncateToUTCDay(const base::Time& time) {
return out_time;
}
-VariationsSeedDateChangeState GetSeedDateChangeState(
+UpdateSeedDateResult GetSeedDateChangeState(
const base::Time& server_seed_date,
const base::Time& stored_seed_date) {
if (server_seed_date < stored_seed_date)
- return SEED_DATE_NEW_DATE_OLDER;
+ return UpdateSeedDateResult::NEW_DATE_IS_OLDER;
if (TruncateToUTCDay(server_seed_date) !=
TruncateToUTCDay(stored_seed_date)) {
- // The server date is earlier than the stored date, and they are from
+ // The server date is later than the stored date, and they are from
// different UTC days, so |server_seed_date| is a valid new day.
- return SEED_DATE_NEW_DAY;
+ return UpdateSeedDateResult::NEW_DAY;
}
- return SEED_DATE_SAME_DAY;
+ return UpdateSeedDateResult::SAME_DAY;
}
-#if defined(OS_ANDROID)
-enum FirstRunResult {
- FIRST_RUN_SEED_IMPORT_SUCCESS,
- FIRST_RUN_SEED_IMPORT_FAIL_NO_CALLBACK,
- FIRST_RUN_SEED_IMPORT_FAIL_NO_FIRST_RUN_SEED,
- FIRST_RUN_SEED_IMPORT_FAIL_STORE_FAILED,
- FIRST_RUN_SEED_IMPORT_FAIL_INVALID_RESPONSE_DATE,
- FIRST_RUN_RESULT_ENUM_SIZE,
-};
-
-void RecordFirstRunResult(FirstRunResult result) {
- UMA_HISTOGRAM_ENUMERATION("Variations.FirstRunResult", result,
- FIRST_RUN_RESULT_ENUM_SIZE);
-}
-#endif // OS_ANDROID
-
} // namespace
VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
- : local_state_(local_state), seed_has_country_code_(false) {
-}
+ : local_state_(local_state) {}
VariationsSeedStore::~VariationsSeedStore() {
}
-bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
+bool VariationsSeedStore::LoadSeed(VariationsSeed* seed) {
invalid_base64_signature_.clear();
#if defined(OS_ANDROID)
@@ -167,41 +120,37 @@ bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
#endif // OS_ANDROID
std::string seed_data;
- if (!ReadSeedData(&seed_data))
+ LoadSeedResult read_result = ReadSeedData(&seed_data);
+ if (read_result != LoadSeedResult::SUCCESS) {
+ RecordLoadSeedResult(read_result);
return false;
+ }
+
+ if (SignatureVerificationEnabled()) {
+ const std::string base64_signature =
+ local_state_->GetString(prefs::kVariationsSeedSignature);
- const std::string base64_seed_signature =
- local_state_->GetString(prefs::kVariationsSeedSignature);
- const VerifySignatureResult result =
- VerifySeedSignature(seed_data, base64_seed_signature);
- if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
+ const VerifySignatureResult result =
+ VerifySeedSignature(seed_data, base64_signature);
UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result,
- VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
- if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
- ClearPrefs();
- RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE);
+ VerifySignatureResult::ENUM_SIZE);
+ if (result != VerifySignatureResult::VALID_SIGNATURE) {
// Record the invalid signature.
- invalid_base64_signature_ = base64_seed_signature;
+ invalid_base64_signature_ = base64_signature;
+ ClearPrefs();
+ RecordLoadSeedResult(LoadSeedResult::INVALID_SIGNATURE);
return false;
}
}
if (!seed->ParseFromString(seed_data)) {
ClearPrefs();
- RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_PROTOBUF);
+ RecordLoadSeedResult(LoadSeedResult::CORRUPT_PROTOBUF);
return false;
}
- // Migrate any existing country code from the seed to the pref, if the pref is
- // empty. TODO(asvitkine): Clean up the code in M50+ when sufficient number
- // of clients have migrated.
- if (seed->has_country_code() &&
- local_state_->GetString(prefs::kVariationsCountry).empty()) {
- local_state_->SetString(prefs::kVariationsCountry, seed->country_code());
- }
variations_serial_number_ = seed->serial_number();
- seed_has_country_code_ = seed->has_country_code();
- RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
+ RecordLoadSeedResult(LoadSeedResult::SUCCESS);
return true;
}
@@ -212,14 +161,16 @@ bool VariationsSeedStore::StoreSeedData(
const base::Time& date_fetched,
bool is_delta_compressed,
bool is_gzip_compressed,
- variations::VariationsSeed* parsed_seed) {
+ VariationsSeed* parsed_seed) {
+ UMA_HISTOGRAM_BOOLEAN("Variations.StoreSeed.HasCountry",
+ !country_code.empty());
+
// If the data is gzip compressed, first uncompress it.
std::string ungzipped_data;
if (is_gzip_compressed) {
if (compression::GzipUncompress(data, &ungzipped_data)) {
if (ungzipped_data.empty()) {
- RecordSeedStoreHistogram(
- VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_EMPTY_GZIP_CONTENTS);
return false;
}
@@ -229,7 +180,7 @@ bool VariationsSeedStore::StoreSeedData(
UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.GzipSize",
data.length() / 1024);
} else {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_UNGZIP);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_UNGZIP);
return false;
}
} else {
@@ -249,17 +200,18 @@ bool VariationsSeedStore::StoreSeedData(
// If the data is delta compressed, first decode it.
DCHECK(invalid_base64_signature_.empty());
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_DELTA_COUNT);
+ RecordStoreSeedResult(StoreSeedResult::DELTA_COUNT);
std::string existing_seed_data;
std::string updated_seed_data;
- if (!ReadSeedData(&existing_seed_data)) {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED);
+ LoadSeedResult read_result = ReadSeedData(&existing_seed_data);
+ if (read_result != LoadSeedResult::SUCCESS) {
+ RecordStoreSeedResult(StoreSeedResult::FAILED_DELTA_READ_SEED);
return false;
}
if (!ApplyDeltaPatch(existing_seed_data, ungzipped_data,
&updated_seed_data)) {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_DELTA_APPLY);
return false;
}
@@ -275,14 +227,14 @@ bool VariationsSeedStore::StoreSeedData(
UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DeltaSize",
ungzipped_data.length() / 1024);
} else {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_STORE);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_DELTA_STORE);
}
return result;
}
void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
const base::Time& server_date_fetched) {
- VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE;
+ UpdateSeedDateResult result = UpdateSeedDateResult::NO_OLD_DATE;
if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) {
const int64_t stored_date_value =
@@ -290,11 +242,11 @@ void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
const base::Time stored_date =
base::Time::FromInternalValue(stored_date_value);
- date_change = GetSeedDateChangeState(server_date_fetched, stored_date);
+ result = GetSeedDateChangeState(server_date_fetched, stored_date);
}
- UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change,
- SEED_DATE_ENUM_SIZE);
+ UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", result,
+ UpdateSeedDateResult::ENUM_SIZE);
local_state_->SetInt64(prefs::kVariationsSeedDate,
server_date_fetched.ToInternalValue());
@@ -307,45 +259,25 @@ std::string VariationsSeedStore::GetInvalidSignature() const {
// static
void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string());
- registry->RegisterStringPref(prefs::kVariationsSeed, std::string());
+ registry->RegisterStringPref(prefs::kVariationsCountry, std::string());
registry->RegisterInt64Pref(prefs::kVariationsSeedDate,
base::Time().ToInternalValue());
registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string());
- registry->RegisterStringPref(prefs::kVariationsCountry, std::string());
}
-VariationsSeedStore::VerifySignatureResult
-VariationsSeedStore::VerifySeedSignature(
- const std::string& seed_bytes,
- const std::string& base64_seed_signature) {
- if (!SignatureVerificationEnabled())
- return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE;
-
- if (base64_seed_signature.empty())
- return VARIATIONS_SEED_SIGNATURE_MISSING;
-
- std::string signature;
- if (!base::Base64Decode(base64_seed_signature, &signature))
- return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED;
-
- crypto::SignatureVerifier verifier;
- if (!verifier.VerifyInit(crypto::SignatureVerifier::ECDSA_SHA256,
- reinterpret_cast<const uint8_t*>(signature.data()),
- signature.size(), kPublicKey,
- arraysize(kPublicKey))) {
- return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
- }
-
- verifier.VerifyUpdate(reinterpret_cast<const uint8_t*>(seed_bytes.data()),
- seed_bytes.size());
- if (verifier.VerifyFinal())
- return VARIATIONS_SEED_SIGNATURE_VALID;
- return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
+bool VariationsSeedStore::SignatureVerificationEnabled() {
+#if defined(OS_IOS) || defined(OS_ANDROID)
+ // Signature verification is disabled on mobile platforms for now, since it
+ // adds about ~15ms to the startup time on mobile (vs. a couple ms on
+ // desktop).
+ return false;
+#else
+ return true;
+#endif
}
void VariationsSeedStore::ClearPrefs() {
local_state_->ClearPref(prefs::kVariationsCompressedSeed);
- local_state_->ClearPref(prefs::kVariationsSeed);
local_state_->ClearPref(prefs::kVariationsSeedDate);
local_state_->ClearPref(prefs::kVariationsSeedSignature);
}
@@ -363,57 +295,48 @@ void VariationsSeedStore::ImportFirstRunJavaSeed() {
android::GetVariationsFirstRunSeed(&seed_data, &seed_signature, &seed_country,
&response_date, &is_gzip_compressed);
if (seed_data.empty()) {
- RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_FAIL_NO_FIRST_RUN_SEED);
+ RecordFirstRunSeedImportResult(
+ FirstRunSeedImportResult::FAIL_NO_FIRST_RUN_SEED);
return;
}
base::Time current_date;
if (!base::Time::FromUTCString(response_date.c_str(), &current_date)) {
- RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_FAIL_INVALID_RESPONSE_DATE);
+ RecordFirstRunSeedImportResult(
+ FirstRunSeedImportResult::FAIL_INVALID_RESPONSE_DATE);
LOG(WARNING) << "Invalid response date: " << response_date;
return;
}
if (!StoreSeedData(seed_data, seed_signature, seed_country, current_date,
false, is_gzip_compressed, nullptr)) {
- RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_FAIL_STORE_FAILED);
+ RecordFirstRunSeedImportResult(FirstRunSeedImportResult::FAIL_STORE_FAILED);
LOG(WARNING) << "First run variations seed is invalid.";
return;
}
- RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_SUCCESS);
+ RecordFirstRunSeedImportResult(FirstRunSeedImportResult::SUCCESS);
}
#endif // OS_ANDROID
-bool VariationsSeedStore::ReadSeedData(std::string* seed_data) {
+LoadSeedResult VariationsSeedStore::ReadSeedData(std::string* seed_data) {
std::string base64_seed_data =
local_state_->GetString(prefs::kVariationsCompressedSeed);
- const bool is_compressed = !base64_seed_data.empty();
- // If there's no compressed seed, fall back to the uncompressed one.
- if (!is_compressed)
- base64_seed_data = local_state_->GetString(prefs::kVariationsSeed);
-
- if (base64_seed_data.empty()) {
- RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY);
- return false;
- }
+ if (base64_seed_data.empty())
+ return LoadSeedResult::EMPTY;
// If the decode process fails, assume the pref value is corrupt and clear it.
std::string decoded_data;
if (!base::Base64Decode(base64_seed_data, &decoded_data)) {
ClearPrefs();
- RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_BASE64);
- return false;
+ return LoadSeedResult::CORRUPT_BASE64;
}
- if (!is_compressed) {
- seed_data->swap(decoded_data);
- } else if (!compression::GzipUncompress(decoded_data, seed_data)) {
+ if (!compression::GzipUncompress(decoded_data, seed_data)) {
ClearPrefs();
- RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_GZIP);
- return false;
+ return LoadSeedResult::CORRUPT_GZIP;
}
- return true;
+ return LoadSeedResult::SUCCESS;
}
bool VariationsSeedStore::StoreSeedDataNoDelta(
@@ -421,26 +344,26 @@ bool VariationsSeedStore::StoreSeedDataNoDelta(
const std::string& base64_seed_signature,
const std::string& country_code,
const base::Time& date_fetched,
- variations::VariationsSeed* parsed_seed) {
+ VariationsSeed* parsed_seed) {
if (seed_data.empty()) {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_EMPTY_GZIP_CONTENTS);
return false;
}
// Only store the seed data if it parses correctly.
- variations::VariationsSeed seed;
+ VariationsSeed seed;
if (!seed.ParseFromString(seed_data)) {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_PARSE);
return false;
}
- const VerifySignatureResult result =
- VerifySeedSignature(seed_data, base64_seed_signature);
- if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
+ if (SignatureVerificationEnabled()) {
+ const VerifySignatureResult result =
+ VerifySeedSignature(seed_data, base64_seed_signature);
UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
- VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
- if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_SIGNATURE);
+ VerifySignatureResult::ENUM_SIZE);
+ if (result != VerifySignatureResult::VALID_SIGNATURE) {
+ RecordStoreSeedResult(StoreSeedResult::FAILED_SIGNATURE);
return false;
}
}
@@ -448,17 +371,13 @@ bool VariationsSeedStore::StoreSeedDataNoDelta(
// Compress the seed before base64-encoding and storing.
std::string compressed_seed_data;
if (!compression::GzipCompress(seed_data, &compressed_seed_data)) {
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_GZIP);
return false;
}
std::string base64_seed_data;
base::Base64Encode(compressed_seed_data, &base64_seed_data);
- // TODO(asvitkine): This pref is no longer being used. Remove it completely
- // in M45+.
- local_state_->ClearPref(prefs::kVariationsSeed);
-
#if defined(OS_ANDROID)
// If currently we do not have any stored pref then we mark seed storing as
// successful on the Java side of Chrome for Android to avoid repeated seed
@@ -470,12 +389,8 @@ bool VariationsSeedStore::StoreSeedDataNoDelta(
#endif
// Update the saved country code only if one was returned from the server.
- // Prefer the country code that was transmitted in the header over the one in
- // the seed (which is deprecated).
if (!country_code.empty())
local_state_->SetString(prefs::kVariationsCountry, country_code);
- else if (seed.has_country_code())
- local_state_->SetString(prefs::kVariationsCountry, seed.country_code());
local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
UpdateSeedDateAndLogDayChange(date_fetched);
@@ -485,7 +400,7 @@ bool VariationsSeedStore::StoreSeedDataNoDelta(
if (parsed_seed)
seed.Swap(parsed_seed);
- RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS);
+ RecordStoreSeedResult(StoreSeedResult::SUCCESS);
return true;
}
@@ -538,8 +453,7 @@ bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data,
}
void VariationsSeedStore::ReportUnsupportedSeedFormatError() {
- RecordSeedStoreHistogram(
- VARIATIONS_SEED_STORE_FAILED_UNSUPPORTED_SEED_FORMAT);
+ RecordStoreSeedResult(StoreSeedResult::FAILED_UNSUPPORTED_SEED_FORMAT);
}
} // namespace variations
diff --git a/chromium/components/variations/variations_seed_store.h b/chromium/components/variations/variations_seed_store.h
index 2450e682ee5..b2406efc5ab 100644
--- a/chromium/components/variations/variations_seed_store.h
+++ b/chromium/components/variations/variations_seed_store.h
@@ -12,15 +12,14 @@
#include "base/macros.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "components/variations/metrics.h"
class PrefService;
class PrefRegistrySimple;
namespace variations {
-class VariationsSeed;
-}
-namespace variations {
+class VariationsSeed;
// VariationsSeedStore is a helper class for reading and writing the variations
// seed from Local State.
@@ -32,7 +31,7 @@ class VariationsSeedStore {
// Loads the variations seed data from local state into |seed|. If there is a
// problem with loading, the pref value is cleared and false is returned. If
// successful, |seed| will contain the loaded data and true is returned.
- bool LoadSeed(variations::VariationsSeed* seed);
+ bool LoadSeed(VariationsSeed* seed) WARN_UNUSED_RESULT;
// Stores the given seed |data| (serialized protobuf) to local state, along
// with a base64-encoded digital signature for seed and the date when it was
@@ -51,7 +50,7 @@ class VariationsSeedStore {
const base::Time& date_fetched,
bool is_delta_compressed,
bool is_gzip_compressed,
- variations::VariationsSeed* parsed_seed);
+ VariationsSeed* parsed_seed) WARN_UNUSED_RESULT;
// Updates |kVariationsSeedDate| and logs when previous date was from a
// different day.
@@ -65,11 +64,6 @@ class VariationsSeedStore {
return variations_serial_number_;
}
- // Returns whether the last loaded or stored seed has the country field set.
- bool seed_has_country_code() const {
- return seed_has_country_code_;
- }
-
// Returns the invalid signature in base64 format, or an empty string if the
// signature was valid, missing, or if signature verification is disabled.
std::string GetInvalidSignature() const;
@@ -77,26 +71,13 @@ class VariationsSeedStore {
// Registers Local State prefs used by this class.
static void RegisterPrefs(PrefRegistrySimple* registry);
+ PrefService* local_state() { return local_state_; }
+
+ const PrefService* local_state() const { return local_state_; }
+
protected:
- // Note: UMA histogram enum - don't re-order or remove entries.
- enum VerifySignatureResult {
- VARIATIONS_SEED_SIGNATURE_MISSING,
- VARIATIONS_SEED_SIGNATURE_DECODE_FAILED,
- VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE,
- VARIATIONS_SEED_SIGNATURE_INVALID_SEED,
- VARIATIONS_SEED_SIGNATURE_VALID,
- VARIATIONS_SEED_SIGNATURE_ENUM_SIZE,
- };
-
- // Verifies a variations seed (the serialized proto bytes) with the specified
- // base-64 encoded signature that was received from the server and returns the
- // result. The signature is assumed to be an "ECDSA with SHA-256" signature
- // (see kECDSAWithSHA256AlgorithmID in the .cc file). Returns the result of
- // signature verification or VARIATIONS_SEED_SIGNATURE_ENUM_SIZE if signature
- // verification is not enabled.
- virtual VariationsSeedStore::VerifySignatureResult VerifySeedSignature(
- const std::string& seed_bytes,
- const std::string& base64_seed_signature);
+ // Whether signature verification is enabled. Overridable for tests.
+ virtual bool SignatureVerificationEnabled();
private:
FRIEND_TEST_ALL_PREFIXES(VariationsSeedStoreTest, VerifySeedSignature);
@@ -111,23 +92,25 @@ class VariationsSeedStore {
void ImportFirstRunJavaSeed();
#endif // OS_ANDROID
- // Reads the variations seed data from prefs; returns true on success.
- bool ReadSeedData(std::string* seed_data);
+ // Reads the variations seed data from prefs into |seed_data|, and returns the
+ // result of the load. The value stored into |seed_data| should only be used
+ // if the result is SUCCESS.
+ // Side-effect: If the read fails, clears the prefs associated with the seed.
+ LoadSeedResult ReadSeedData(std::string* seed_data) WARN_UNUSED_RESULT;
// Internal version of |StoreSeedData()| that assumes |seed_data| is not delta
// compressed.
- bool StoreSeedDataNoDelta(
- const std::string& seed_data,
- const std::string& base64_seed_signature,
- const std::string& country_code,
- const base::Time& date_fetched,
- variations::VariationsSeed* parsed_seed);
+ bool StoreSeedDataNoDelta(const std::string& seed_data,
+ const std::string& base64_seed_signature,
+ const std::string& country_code,
+ const base::Time& date_fetched,
+ VariationsSeed* parsed_seed) WARN_UNUSED_RESULT;
// Applies a delta-compressed |patch| to |existing_data|, producing the result
// in |output|. Returns whether the operation was successful.
static bool ApplyDeltaPatch(const std::string& existing_data,
const std::string& patch,
- std::string* output);
+ std::string* output) WARN_UNUSED_RESULT;
// The pref service used to persist the variations seed.
PrefService* local_state_;
@@ -135,10 +118,6 @@ class VariationsSeedStore {
// Cached serial number from the most recently fetched variations seed.
std::string variations_serial_number_;
- // Whether the most recently fetched variations seed has the country code
- // field set.
- bool seed_has_country_code_;
-
// Keeps track of an invalid signature.
std::string invalid_base64_signature_;
diff --git a/chromium/components/variations/variations_seed_store_unittest.cc b/chromium/components/variations/variations_seed_store_unittest.cc
index c687932c25d..9958c7c916c 100644
--- a/chromium/components/variations/variations_seed_store_unittest.cc
+++ b/chromium/components/variations/variations_seed_store_unittest.cc
@@ -6,6 +6,7 @@
#include "base/base64.h"
#include "base/macros.h"
+#include "base/test/histogram_tester.h"
#include "build/build_config.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/pref_names.h"
@@ -33,27 +34,37 @@ class TestVariationsSeedStore : public VariationsSeedStore {
base::Time::Now(), false, false, nullptr);
}
- VariationsSeedStore::VerifySignatureResult VerifySeedSignature(
- const std::string& seed_bytes,
- const std::string& base64_seed_signature) override {
- return VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE;
- }
+ bool SignatureVerificationEnabled() override { return false; }
private:
DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore);
};
+// Signature verification is disabled on Android and iOS for performance
+// reasons. This class re-enables it for tests, which don't mind the (small)
+// performance penalty.
+class SignatureVerifyingVariationsSeedStore : public VariationsSeedStore {
+ public:
+ explicit SignatureVerifyingVariationsSeedStore(PrefService* local_state)
+ : VariationsSeedStore(local_state) {}
+ ~SignatureVerifyingVariationsSeedStore() override {}
+
+ bool SignatureVerificationEnabled() override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SignatureVerifyingVariationsSeedStore);
+};
// Populates |seed| with simple test data. The resulting seed will contain one
// study called "test", which contains one experiment called "abc" with
// probability weight 100. |seed|'s study field will be cleared before adding
// the new study.
-variations::VariationsSeed CreateTestSeed() {
- variations::VariationsSeed seed;
- variations::Study* study = seed.add_study();
+VariationsSeed CreateTestSeed() {
+ VariationsSeed seed;
+ Study* study = seed.add_study();
study->set_name("test");
study->set_default_experiment_name("abc");
- variations::Study_Experiment* experiment = study->add_experiment();
+ Study_Experiment* experiment = study->add_experiment();
experiment->set_name("abc");
experiment->set_probability_weight(100);
seed.set_serial_number("123");
@@ -61,7 +72,7 @@ variations::VariationsSeed CreateTestSeed() {
}
// Serializes |seed| to protobuf binary format.
-std::string SerializeSeed(const variations::VariationsSeed& seed) {
+std::string SerializeSeed(const VariationsSeed& seed) {
std::string serialized_seed;
seed.SerializeToString(&serialized_seed);
return serialized_seed;
@@ -76,7 +87,7 @@ std::string Compress(const std::string& data) {
}
// Serializes |seed| to compressed base64-encoded protobuf binary format.
-std::string SerializeSeedBase64(const variations::VariationsSeed& seed) {
+std::string SerializeSeedBase64(const VariationsSeed& seed) {
std::string serialized_seed = SerializeSeed(seed);
std::string base64_serialized_seed;
base::Base64Encode(Compress(serialized_seed), &base64_serialized_seed);
@@ -94,7 +105,7 @@ bool PrefHasDefaultValue(const TestingPrefServiceSimple& prefs,
TEST(VariationsSeedStoreTest, LoadSeed) {
// Store good seed data to test if loading from prefs works.
- const variations::VariationsSeed seed = CreateTestSeed();
+ const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
TestingPrefServiceSimple prefs;
@@ -103,7 +114,7 @@ TEST(VariationsSeedStoreTest, LoadSeed) {
TestVariationsSeedStore seed_store(&prefs);
- variations::VariationsSeed loaded_seed;
+ VariationsSeed loaded_seed;
// Check that loading a seed works correctly.
EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed));
@@ -127,16 +138,12 @@ TEST(VariationsSeedStoreTest, LoadSeed) {
}
TEST(VariationsSeedStoreTest, GetInvalidSignature) {
- const variations::VariationsSeed seed = CreateTestSeed();
- const std::string base64_seed = SerializeSeedBase64(seed);
-
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
- prefs.SetString(prefs::kVariationsSeed, base64_seed);
// The below seed and signature pair were generated using the server's
// private key.
- const std::string base64_seed_data =
+ const std::string uncompressed_base64_seed_data =
"CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
"Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
"SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
@@ -149,20 +156,32 @@ TEST(VariationsSeedStoreTest, GetInvalidSignature) {
"AEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
"96JkMYgzTkHPwbv7K/CmgA==";
+ std::string uncompressed_seed_data;
+ ASSERT_TRUE(base::Base64Decode(uncompressed_base64_seed_data,
+ &uncompressed_seed_data));
+ std::string compressed_base64_seed_data;
+ base::Base64Encode(Compress(uncompressed_seed_data),
+ &compressed_base64_seed_data);
+
// Set seed and valid signature in prefs.
- prefs.SetString(prefs::kVariationsSeed, base64_seed_data);
+ prefs.SetString(prefs::kVariationsCompressedSeed,
+ compressed_base64_seed_data);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
VariationsSeedStore seed_store(&prefs);
- variations::VariationsSeed loaded_seed;
- seed_store.LoadSeed(&loaded_seed);
+ VariationsSeed loaded_seed;
+ EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed));
std::string invalid_signature = seed_store.GetInvalidSignature();
// Valid signature so we get an empty string.
EXPECT_EQ(std::string(), invalid_signature);
prefs.SetString(prefs::kVariationsSeedSignature,
base64_seed_signature_invalid);
- seed_store.LoadSeed(&loaded_seed);
+#if defined(OS_IOS) || defined(OS_ANDROID)
+ EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed));
+#else
+ EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed));
+#endif
// Invalid signature, so we should get the signature itself, except on mobile
// where we should get an empty string because verification is not enabled.
invalid_signature = seed_store.GetInvalidSignature();
@@ -173,14 +192,18 @@ TEST(VariationsSeedStoreTest, GetInvalidSignature) {
#endif
prefs.SetString(prefs::kVariationsSeedSignature, std::string());
- seed_store.LoadSeed(&loaded_seed);
+#if defined(OS_IOS) || defined(OS_ANDROID)
+ EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed));
+#else
+ EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed));
+#endif
invalid_signature = seed_store.GetInvalidSignature();
// Empty signature, not considered invalid.
EXPECT_EQ(std::string(), invalid_signature);
}
TEST(VariationsSeedStoreTest, StoreSeedData) {
- const variations::VariationsSeed seed = CreateTestSeed();
+ const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
TestingPrefServiceSimple prefs;
@@ -207,14 +230,14 @@ TEST(VariationsSeedStoreTest, StoreSeedData) {
}
TEST(VariationsSeedStoreTest, StoreSeedData_ParsedSeed) {
- const variations::VariationsSeed seed = CreateTestSeed();
+ const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
- variations::VariationsSeed parsed_seed;
+ VariationsSeed parsed_seed;
EXPECT_TRUE(seed_store.StoreSeedData(serialized_seed, std::string(),
std::string(), base::Time::Now(), false,
false, &parsed_seed));
@@ -226,40 +249,22 @@ TEST(VariationsSeedStoreTest, StoreSeedData_CountryCode) {
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
- // Test with a seed country code and no header value.
- variations::VariationsSeed seed = CreateTestSeed();
- seed.set_country_code("test_country");
- EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
- std::string(), base::Time::Now(), false,
- false, nullptr));
+ // Test with a valid header value.
+ std::string seed = SerializeSeed(CreateTestSeed());
+ EXPECT_TRUE(seed_store.StoreSeedData(seed, std::string(), "test_country",
+ base::Time::Now(), false, false,
+ nullptr));
EXPECT_EQ("test_country", prefs.GetString(prefs::kVariationsCountry));
- // Test with a header value and no seed country.
- prefs.ClearPref(prefs::kVariationsCountry);
- seed.clear_country_code();
- EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
- "test_country2", base::Time::Now(),
- false, false, nullptr));
- EXPECT_EQ("test_country2", prefs.GetString(prefs::kVariationsCountry));
-
- // Test with a seed country code and header value.
- prefs.ClearPref(prefs::kVariationsCountry);
- seed.set_country_code("test_country3");
- EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
- "test_country4", base::Time::Now(),
- false, false, nullptr));
- EXPECT_EQ("test_country4", prefs.GetString(prefs::kVariationsCountry));
-
// Test with no country code specified - which should preserve the old value.
- seed.clear_country_code();
- EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(),
- std::string(), base::Time::Now(), false,
- false, nullptr));
- EXPECT_EQ("test_country4", prefs.GetString(prefs::kVariationsCountry));
+ EXPECT_TRUE(seed_store.StoreSeedData(seed, std::string(), std::string(),
+ base::Time::Now(), false, false,
+ nullptr));
+ EXPECT_EQ("test_country", prefs.GetString(prefs::kVariationsCountry));
}
TEST(VariationsSeedStoreTest, StoreSeedData_GzippedSeed) {
- const variations::VariationsSeed seed = CreateTestSeed();
+ const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
std::string compressed_seed;
ASSERT_TRUE(compression::GzipCompress(serialized_seed, &compressed_seed));
@@ -268,7 +273,7 @@ TEST(VariationsSeedStoreTest, StoreSeedData_GzippedSeed) {
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
- variations::VariationsSeed parsed_seed;
+ VariationsSeed parsed_seed;
EXPECT_TRUE(seed_store.StoreSeedData(compressed_seed, std::string(),
std::string(), base::Time::Now(), false,
true, &parsed_seed));
@@ -284,7 +289,7 @@ TEST(VariationsSeedStoreTest, StoreSeedData_GzippedEmptySeed) {
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
- variations::VariationsSeed parsed_seed;
+ VariationsSeed parsed_seed;
EXPECT_FALSE(seed_store.StoreSeedData(compressed_seed, std::string(),
std::string(), base::Time::Now(), false,
true, &parsed_seed));
@@ -293,7 +298,7 @@ TEST(VariationsSeedStoreTest, StoreSeedData_GzippedEmptySeed) {
TEST(VariationsSeedStoreTest, VerifySeedSignature) {
// The below seed and signature pair were generated using the server's
// private key.
- const std::string base64_seed_data =
+ const std::string uncompressed_base64_seed_data =
"CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
"Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
"SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
@@ -304,40 +309,101 @@ TEST(VariationsSeedStoreTest, VerifySeedSignature) {
"96JkMYgzTkHPwbv7K/CmgA==";
std::string seed_data;
- EXPECT_TRUE(base::Base64Decode(base64_seed_data, &seed_data));
+ ASSERT_TRUE(base::Base64Decode(uncompressed_base64_seed_data, &seed_data));
+ VariationsSeed seed;
+ ASSERT_TRUE(seed.ParseFromString(seed_data));
+ std::string base64_seed_data = SerializeSeedBase64(seed);
- VariationsSeedStore seed_store(NULL);
-
-#if defined(OS_IOS) || defined(OS_ANDROID)
- // Signature verification is not enabled on mobile.
- if (seed_store.VerifySeedSignature(seed_data, base64_seed_signature) ==
- VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
- return;
- }
-#endif
+ TestingPrefServiceSimple prefs;
+ VariationsSeedStore::RegisterPrefs(prefs.registry());
// The above inputs should be valid.
- EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_VALID,
- seed_store.VerifySeedSignature(seed_data, base64_seed_signature));
+ {
+ prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
+ prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
+ SignatureVerifyingVariationsSeedStore seed_store(&prefs);
+
+ base::HistogramTester histogram_tester;
+ VariationsSeed seed;
+ EXPECT_TRUE(seed_store.LoadSeed(&seed));
+ histogram_tester.ExpectUniqueSample(
+ "Variations.LoadSeedSignature",
+ static_cast<base::HistogramBase::Sample>(
+ VerifySignatureResult::VALID_SIGNATURE),
+ 1);
+ }
// If there's no signature, the corresponding result should be returned.
- EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_MISSING,
- seed_store.VerifySeedSignature(seed_data, std::string()));
+ {
+ prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
+ prefs.SetString(prefs::kVariationsSeedSignature, std::string());
+ SignatureVerifyingVariationsSeedStore seed_store(&prefs);
+
+ base::HistogramTester histogram_tester;
+ VariationsSeed seed;
+ EXPECT_FALSE(seed_store.LoadSeed(&seed));
+ histogram_tester.ExpectUniqueSample(
+ "Variations.LoadSeedSignature",
+ static_cast<base::HistogramBase::Sample>(
+ VerifySignatureResult::MISSING_SIGNATURE),
+ 1);
+ }
- // Using non-base64 encoded value as signature (e.g. seed data) should fail.
- EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_DECODE_FAILED,
- seed_store.VerifySeedSignature(seed_data, seed_data));
+ // Using non-base64 encoded value as signature should fail.
+ {
+ prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
+ prefs.SetString(prefs::kVariationsSeedSignature,
+ "not a base64-encoded string");
+ SignatureVerifyingVariationsSeedStore seed_store(&prefs);
+
+ base::HistogramTester histogram_tester;
+ VariationsSeed seed;
+ EXPECT_FALSE(seed_store.LoadSeed(&seed));
+ histogram_tester.ExpectUniqueSample(
+ "Variations.LoadSeedSignature",
+ static_cast<base::HistogramBase::Sample>(
+ VerifySignatureResult::DECODE_FAILED),
+ 1);
+ }
// Using a different signature (e.g. the base64 seed data) should fail.
// OpenSSL doesn't distinguish signature decode failure from the
// signature not matching.
- EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED,
- seed_store.VerifySeedSignature(seed_data, base64_seed_data));
+ {
+ prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
+ prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_data);
+ SignatureVerifyingVariationsSeedStore seed_store(&prefs);
+
+ base::HistogramTester histogram_tester;
+ VariationsSeed seed;
+ EXPECT_FALSE(seed_store.LoadSeed(&seed));
+ histogram_tester.ExpectUniqueSample(
+ "Variations.LoadSeedSignature",
+ static_cast<base::HistogramBase::Sample>(
+ VerifySignatureResult::INVALID_SEED),
+ 1);
+ }
// Using a different seed should not match the signature.
- seed_data[0] = 'x';
- EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED,
- seed_store.VerifySeedSignature(seed_data, base64_seed_signature));
+ {
+ VariationsSeed wrong_seed;
+ ASSERT_TRUE(wrong_seed.ParseFromString(seed_data));
+ (*wrong_seed.mutable_study(0)->mutable_name())[0] = 'x';
+ std::string base64_wrong_seed_data = SerializeSeedBase64(wrong_seed);
+
+ prefs.SetString(prefs::kVariationsCompressedSeed, base64_wrong_seed_data);
+ prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
+ SignatureVerifyingVariationsSeedStore seed_store(&prefs);
+
+ base::HistogramTester histogram_tester;
+ VariationsSeed seed;
+ EXPECT_FALSE(seed_store.LoadSeed(&seed));
+ histogram_tester.ExpectUniqueSample(
+ "Variations.LoadSeedSignature",
+ static_cast<base::HistogramBase::Sample>(
+ VerifySignatureResult::INVALID_SEED),
+ 1);
+ }
}
TEST(VariationsSeedStoreTest, ApplyDeltaPatch) {
diff --git a/chromium/components/variations/variations_switches.cc b/chromium/components/variations/variations_switches.cc
index 5b19c665f5a..2f83ead0f94 100644
--- a/chromium/components/variations/variations_switches.cc
+++ b/chromium/components/variations/variations_switches.cc
@@ -10,6 +10,10 @@ namespace switches {
// Disable field trial tests configured in fieldtrial_testing_config.json.
const char kDisableFieldTrialTestingConfig[] = "disable-field-trial-config";
+// TODO(asvitkine): Consider removing or renaming this functionality.
+// Enables the benchmarking extensions.
+const char kEnableBenchmarking[] = "enable-benchmarking";
+
// Fakes the channel of the browser for purposes of Variations filtering. This
// is to be used for testing only. Possible values are "stable", "beta", "dev"
// and "canary". Note that this only applies if the browser's reported channel
@@ -26,6 +30,11 @@ const char kFakeVariationsChannel[] = "fake-variations-channel";
// escaped for all non-alphanumeric characters.
const char kForceFieldTrialParams[] = "force-fieldtrial-params";
+// Forces additional Chrome Variation Ids that will be sent in X-Client-Data
+// header, specified as a 64-bit encoded list of numeric experiment ids. Ids
+// prefixed with the character "t" will be treated as Trigger Variation Ids.
+const char kForceVariationIds[] = "force-variation-ids";
+
// Allows overriding the country used for evaluating variations. This is similar
// to the "Override Variations Country" entry on chrome://translate-internals,
// but is exposed as a command-line flag to allow testing First Run scenarios.
diff --git a/chromium/components/variations/variations_switches.h b/chromium/components/variations/variations_switches.h
index 9755dac4145..95999b1fdf4 100644
--- a/chromium/components/variations/variations_switches.h
+++ b/chromium/components/variations/variations_switches.h
@@ -12,8 +12,10 @@ namespace switches {
// each in the .cc file.
extern const char kDisableFieldTrialTestingConfig[];
+extern const char kEnableBenchmarking[];
extern const char kFakeVariationsChannel[];
extern const char kForceFieldTrialParams[];
+extern const char kForceVariationIds[];
extern const char kVariationsOverrideCountry[];
extern const char kVariationsServerURL[];
diff --git a/chromium/components/variations/variations_util.cc b/chromium/components/variations/variations_util.cc
index b5116d101d8..f2a2633a917 100644
--- a/chromium/components/variations/variations_util.cc
+++ b/chromium/components/variations/variations_util.cc
@@ -6,6 +6,7 @@
#include <vector>
+#include "base/strings/string_piece.h"
#include "components/crash/core/common/crash_keys.h"
#include "components/variations/active_field_trials.h"
@@ -13,7 +14,9 @@ namespace variations {
void SetVariationListCrashKeys() {
std::vector<std::string> experiment_strings;
- GetFieldTrialActiveGroupIdsAsStrings(&experiment_strings);
+ GetFieldTrialActiveGroupIdsAsStrings(base::StringPiece(),
+ &experiment_strings);
+ GetSyntheticTrialGroupIdsAsString(&experiment_strings);
crash_keys::SetVariationsList(experiment_strings);
}
diff --git a/chromium/components/vector_icons/BUILD.gn b/chromium/components/vector_icons/BUILD.gn
index ffc9a535228..24c915b5f8f 100644
--- a/chromium/components/vector_icons/BUILD.gn
+++ b/chromium/components/vector_icons/BUILD.gn
@@ -2,14 +2,39 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//ui/vector_icons/vector_icons.gni")
+import("//components/vector_icons/vector_icons.gni")
aggregate_vector_icons("components_vector_icons") {
icon_directory = "."
icons = [
- "screen_share.1x.icon",
+ "back_arrow.1x.icon",
+ "back_arrow.icon",
+ "bluetooth_connected.icon",
+ "business.icon",
+ "check_circle.icon",
+ "close.1x.icon",
+ "close.icon",
+ "edit.icon",
+ "error_circle.icon",
+ "forward_arrow.1x.icon",
+ "forward_arrow.icon",
+ "info_outline.icon",
+ "location_on.icon",
+ "lock.icon",
+ "media_router_active.icon",
+ "media_router_error.icon",
+ "media_router_idle.icon",
+ "media_router_warning.icon",
+ "microphone.icon",
+ "midi.icon",
+ "notifications.icon",
+ "notifications_off.icon",
+ "protocol_handler.icon",
"screen_share.icon",
+ "search.icon",
+ "videocam.icon",
+ "warning.icon",
]
}
diff --git a/chromium/components/vector_icons/DEPS b/chromium/components/vector_icons/DEPS
new file mode 100644
index 00000000000..b7a2ad62678
--- /dev/null
+++ b/chromium/components/vector_icons/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+base",
+ "+skia",
+ "+ui/gfx",
+]
diff --git a/chromium/components/vector_icons/aggregate_vector_icons.py b/chromium/components/vector_icons/aggregate_vector_icons.py
new file mode 100644
index 00000000000..87aa1bea0f6
--- /dev/null
+++ b/chromium/components/vector_icons/aggregate_vector_icons.py
@@ -0,0 +1,157 @@
+# 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.
+
+import fileinput
+import glob
+import optparse
+import os
+import shlex
+import sys
+import textwrap
+
+def Error(msg):
+ print >> sys.stderr, msg
+ sys.exit(1)
+
+
+def CamelCase(name, suffix):
+ words = name.split('_')
+ words = [w.capitalize() for w in words]
+ return 'k' + ''.join(words) + suffix
+
+
+def AggregateVectorIcons(working_directory, file_list, output_cc, output_h):
+ """Compiles all .icon files in a directory into two C++ files.
+
+ Args:
+ working_directory: The path to the directory that holds the .icon files
+ and C++ templates.
+ file_list: A file containing the list of vector icon files to process.
+ output_cc: The path that should be used to write the .cc file.
+ output_h: The path that should be used to write the .h file.
+ """
+
+ # For each file in |file_list|, place it in |path_map| if its extension is
+ # .icon or place it in |path_map_1x| if its extension is .1x.icon. The
+ # two dictionaries map the icon's name to its path, e.g.,
+ # path_map['cat'] = 'foo/bar/cat.icon'.
+ icon_list = []
+ with open(file_list, 'r') as f:
+ file_list_contents = f.read()
+ icon_list = shlex.split(file_list_contents)
+
+ path_map = {}
+ path_map_1x = {}
+
+ for icon_path in icon_list:
+ (icon_name, extension) = os.path.splitext(os.path.basename(icon_path))
+ (icon_name, scale_factor) = os.path.splitext(icon_name)
+
+ if (scale_factor and scale_factor != ".1x") or (extension != ".icon"):
+ Error("Only filenames " + icon_name + ".icon or " + icon_name +
+ ".1x.icon are allowed.")
+
+ if not scale_factor and icon_name not in path_map:
+ path_map[icon_name] = icon_path
+ elif scale_factor and icon_name not in path_map_1x:
+ path_map_1x[icon_name] = icon_path
+ else:
+ Error("A vector icon with name '" + icon_name + "' already exists.")
+
+ for icon_1x in path_map_1x:
+ if icon_1x not in path_map:
+ Error("The file " + icon_1x + ".icon must be provided.")
+
+ # Generate the file vector_icons.h which declares a variable for each
+ # icon in |path_map|. The variable name is derived from the icon name by
+ # converting to camel case, prepending 'k', and appending 'Icon'. For
+ # example, the icon 'foo_bar' will have the variable name kFooBarIcon.
+ input_header_template = open(os.path.join(working_directory,
+ "vector_icons.h.template"))
+ header_template_contents = input_header_template.readlines()
+ input_header_template.close()
+
+ output_header = open(output_h, "w")
+ for line in header_template_contents:
+ if not "TEMPLATE_PLACEHOLDER" in line:
+ output_header.write(line)
+ continue
+
+ for icon in path_map:
+ (icon_name, extension) = os.path.splitext(
+ os.path.basename(path_map[icon]))
+ (icon_name, scale_factor) = os.path.splitext(icon_name)
+ output_header.write(
+ "VECTOR_ICON_TEMPLATE_H({})\n".format(CamelCase(icon_name, "Icon")))
+ output_header.close()
+
+ # Copy the vector icon drawing commands from the .icon and .1x.icon files
+ # and use them to generate vector_icons.cc, which defines the variables
+ # declared in vector_icons.h.
+ input_cc_template = open(
+ os.path.join(working_directory, "vector_icons.cc.template"))
+ cc_template_contents = input_cc_template.readlines()
+ input_cc_template.close()
+
+ output_cc = open(output_cc, "w")
+ for line in cc_template_contents:
+ if not "TEMPLATE_PLACEHOLDER" in line:
+ output_cc.write(line)
+ continue;
+
+ for icon in path_map:
+ (icon_name, extension) = os.path.splitext(
+ os.path.basename(path_map[icon]))
+ (icon_name, scale_factor) = os.path.splitext(icon_name)
+
+ # Store the vector-drawing commands for foo_bar.icon in the temporary
+ # variable kFooBarPath.
+ icon_file = open(path_map[icon])
+ vector_commands = "".join(icon_file.readlines())
+ icon_file.close()
+ output_cc.write("PATH_ELEMENT_TEMPLATE({}, {})\n".format(
+ CamelCase(icon_name, "Path"), vector_commands))
+
+ # Store the vector-drawing commands for foo_bar.1x.icon in the temporary
+ # variable kFooBarPath1x, if the file exists.
+ vector_commands_1x = None
+ if (icon in path_map_1x):
+ icon_file = open(path_map_1x[icon])
+ vector_commands_1x = "".join(icon_file.readlines())
+ icon_file.close()
+ output_cc.write("PATH_ELEMENT_TEMPLATE({}, {})\n".format(
+ CamelCase(icon_name, "Path1x"), vector_commands_1x))
+
+ # Define the value of kFooBarIcon.
+ third_arg = "nullptr" if vector_commands_1x is None else CamelCase(
+ icon_name, "Path1x")
+ output_cc.write("VECTOR_ICON_TEMPLATE({}, {}, {})\n".format(CamelCase(
+ icon_name, "Icon"), CamelCase(icon_name, "Path"), third_arg))
+
+ output_cc.close()
+
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option("--working_directory",
+ help="The directory to look for template C++ as well as "
+ "icon files.")
+ parser.add_option("--file_list",
+ help="A response file containing the list of icon files "
+ "to be processed.")
+ parser.add_option("--output_cc",
+ help="The path to output the CC file to.")
+ parser.add_option("--output_h",
+ help="The path to output the header file to.")
+
+ (options, args) = parser.parse_args()
+
+ AggregateVectorIcons(options.working_directory,
+ options.file_list,
+ options.output_cc,
+ options.output_h)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/chromium/components/vector_icons/back_arrow.1x.icon b/chromium/components/vector_icons/back_arrow.1x.icon
new file mode 100644
index 00000000000..219193ff138
--- /dev/null
+++ b/chromium/components/vector_icons/back_arrow.1x.icon
@@ -0,0 +1,26 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 9, 13.51f,
+LINE_TO, 4.49f, 9,
+LINE_TO, 14, 9,
+CUBIC_TO, 14.55f, 9, 15, 8.55f, 15, 8,
+CUBIC_TO, 15, 7.45f, 14.55f, 7, 14, 7,
+LINE_TO, 4.49f, 7,
+LINE_TO, 9, 2.49f,
+CUBIC_TO, 8.97f, 2.28f, 9, 2.14f, 9, 2,
+CUBIC_TO, 9, 1.45f, 8.55f, 1, 8, 1,
+CUBIC_TO, 7.86f, 1, 7.72f, 1.03f, 7.59f, 1.09f,
+LINE_TO, 7.51f, 1,
+LINE_TO, 1, 7.51f,
+CUBIC_TO, 1.03f, 7.72f, 1, 7.85f, 1, 8,
+CUBIC_TO, 1, 8.15f, 1.03f, 8.28f, 1.09f, 8.41f,
+LINE_TO, 1, 8.49f,
+LINE_TO, 7.51f, 15,
+CUBIC_TO, 7.72f, 14.97f, 7.86f, 15, 8, 15,
+CUBIC_TO, 8.55f, 15, 9, 14.55f, 9, 14,
+CUBIC_TO, 9, 13.86f, 8.97f, 13.72f, 8.91f, 13.59f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/back_arrow.icon b/chromium/components/vector_icons/back_arrow.icon
new file mode 100644
index 00000000000..4b76b7ed008
--- /dev/null
+++ b/chromium/components/vector_icons/back_arrow.icon
@@ -0,0 +1,27 @@
+// 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.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 4.22f, 14.03f,
+CUBIC_TO, 3.53f, 14.15f, 3, 14.77f, 3, 15.5f,
+CUBIC_TO, 3, 16.25f, 3.53f, 16.86f, 4.24f, 16.98f,
+LINE_TO, 4.02f, 17.19f,
+LINE_TO, 14.36f, 27.51f,
+CUBIC_TO, 14.65f, 27.81f, 15.05f, 28, 15.5f, 28,
+CUBIC_TO, 16.33f, 28, 17, 27.33f, 17, 26.5f,
+CUBIC_TO, 17, 26.05f, 16.8f, 25.65f, 16.49f, 25.38f,
+LINE_TO, 16.55f, 25.32f,
+LINE_TO, 8.22f, 17,
+LINE_TO, 27.51f, 17,
+CUBIC_TO, 28.33f, 17, 29, 16.33f, 29, 15.5f,
+CUBIC_TO, 29, 14.67f, 28.33f, 14, 27.51f, 14,
+LINE_TO, 8.2f, 14,
+LINE_TO, 16.55f, 5.66f,
+CUBIC_TO, 16.81f, 5.34f, 17, 4.94f, 17, 4.5f,
+CUBIC_TO, 17, 3.68f, 16.33f, 3, 15.5f, 3,
+CUBIC_TO, 15.06f, 3, 14.66f, 3.2f, 14.39f, 3.5f,
+LINE_TO, 14.36f, 3.47f,
+LINE_TO, 4, 13.81f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/bluetooth_connected.icon b/chromium/components/vector_icons/bluetooth_connected.icon
new file mode 100644
index 00000000000..ea01302eb25
--- /dev/null
+++ b/chromium/components/vector_icons/bluetooth_connected.icon
@@ -0,0 +1,43 @@
+// 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.
+
+MOVE_TO, 14, 24,
+R_LINE_TO, -4, -4,
+R_LINE_TO, -4, 4,
+R_LINE_TO, 4, 4,
+R_LINE_TO, 4, -4,
+CLOSE,
+R_MOVE_TO, 21.41f, -8.59f,
+LINE_TO, 24, 4,
+R_H_LINE_TO, -2,
+R_V_LINE_TO, 15.17f,
+LINE_TO, 12.83f, 10,
+LINE_TO, 10, 12.83f,
+LINE_TO, 21.17f, 24,
+LINE_TO, 10, 35.17f,
+LINE_TO, 12.83f, 38,
+LINE_TO, 22, 28.83f,
+V_LINE_TO, 44,
+R_H_LINE_TO, 2,
+R_LINE_TO, 11.41f, -11.41f,
+LINE_TO, 26.83f, 24,
+R_LINE_TO, 8.58f, -8.59f,
+CLOSE,
+MOVE_TO, 26, 11.66f,
+R_LINE_TO, 3.76f, 3.76f,
+LINE_TO, 26, 19.17f,
+R_V_LINE_TO, -7.51f,
+CLOSE,
+R_MOVE_TO, 3.76f, 20.93f,
+LINE_TO, 26, 36.34f,
+R_V_LINE_TO, -7.52f,
+R_LINE_TO, 3.76f, 3.77f,
+CLOSE,
+MOVE_TO, 38, 20,
+R_LINE_TO, -4, 4,
+R_LINE_TO, 4, 4,
+R_LINE_TO, 4, -4,
+R_LINE_TO, -4, -4,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/business.icon b/chromium/components/vector_icons/business.icon
new file mode 100644
index 00000000000..bfefb7b4a3d
--- /dev/null
+++ b/chromium/components/vector_icons/business.icon
@@ -0,0 +1,87 @@
+// 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.
+
+MOVE_TO, 24, 14,
+V_LINE_TO, 6,
+H_LINE_TO, 4,
+R_V_LINE_TO, 36,
+R_H_LINE_TO, 40,
+V_LINE_TO, 14,
+H_LINE_TO, 24,
+CLOSE,
+MOVE_TO, 12, 38,
+H_LINE_TO, 8,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+H_LINE_TO, 8,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+H_LINE_TO, 8,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+H_LINE_TO, 8,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 8, 24,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 20, 24,
+H_LINE_TO, 24,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 20,
+CLOSE,
+R_MOVE_TO, -4, -16,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, -4,
+CLOSE,
+R_MOVE_TO, 0, 8,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, -4,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/check_circle.icon b/chromium/components/vector_icons/check_circle.icon
new file mode 100644
index 00000000000..cd24bbd38e8
--- /dev/null
+++ b/chromium/components/vector_icons/check_circle.icon
@@ -0,0 +1,13 @@
+// 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.
+
+CIRCLE, 24, 24, 20,
+MOVE_TO, 20, 34,
+LINE_TO, 10, 24,
+R_LINE_TO, 2.83f, -2.83f,
+LINE_TO, 20, 28.34f,
+R_LINE_TO, 15.17f, -15.17f,
+LINE_TO, 38, 16,
+LINE_TO, 20, 34,
+END
diff --git a/chromium/components/vector_icons/close.1x.icon b/chromium/components/vector_icons/close.1x.icon
new file mode 100644
index 00000000000..a7267fdb7d5
--- /dev/null
+++ b/chromium/components/vector_icons/close.1x.icon
@@ -0,0 +1,13 @@
+// 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.
+
+// A variant of ic_close with rounded corners and optimized for 1x scale factor
+// devices.
+CANVAS_DIMENSIONS, 16,
+STROKE, 1.85f,
+MOVE_TO, 4, 4,
+R_LINE_TO, 8, 8,
+MOVE_TO, 4, 12,
+R_LINE_TO, 8, -8,
+END
diff --git a/chromium/components/vector_icons/close.icon b/chromium/components/vector_icons/close.icon
new file mode 100644
index 00000000000..cfbabc117a8
--- /dev/null
+++ b/chromium/components/vector_icons/close.icon
@@ -0,0 +1,12 @@
+// 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.
+
+// A variant of ic_close with rounded corners.
+CANVAS_DIMENSIONS, 32,
+STROKE, 3.5f,
+MOVE_TO, 8.75f, 8.75f,
+R_LINE_TO, 14.5f, 14.5f,
+MOVE_TO, 8.75f, 23.25f,
+R_LINE_TO, 14.5f, -14.5f,
+END
diff --git a/chromium/components/vector_icons/edit.icon b/chromium/components/vector_icons/edit.icon
new file mode 100644
index 00000000000..0cbb1f786af
--- /dev/null
+++ b/chromium/components/vector_icons/edit.icon
@@ -0,0 +1,21 @@
+// 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.
+
+CANVAS_DIMENSIONS, 48,
+MOVE_TO, 6, 34.51f,
+LINE_TO, 6, 42.01f,
+LINE_TO, 13.5f, 42.01f,
+LINE_TO, 35.62f, 19.89f,
+LINE_TO, 28.12f, 12.39f,
+LINE_TO, 6, 34.51f,
+CLOSE,
+MOVE_TO, 41.42f, 14.09f,
+CUBIC_TO, 42.2f, 13.31f, 42.2f, 12.05f, 41.42f, 11.27f,
+LINE_TO, 36.74f, 6.59f,
+CUBIC_TO, 35.96f, 5.81f, 34.7f, 5.81f, 33.92f, 6.59f,
+LINE_TO, 30.26f, 10.25f,
+LINE_TO, 37.76f, 17.75f,
+LINE_TO, 41.42f, 14.09f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/error_circle.icon b/chromium/components/vector_icons/error_circle.icon
new file mode 100644
index 00000000000..ebb3e14c6a2
--- /dev/null
+++ b/chromium/components/vector_icons/error_circle.icon
@@ -0,0 +1,18 @@
+// 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.
+
+CIRCLE, 24, 24, 20,
+MOVE_TO, 26, 34,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+R_H_LINE_TO, -4,
+V_LINE_TO, 14,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 12,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/forward_arrow.1x.icon b/chromium/components/vector_icons/forward_arrow.1x.icon
new file mode 100644
index 00000000000..60b2c9c671e
--- /dev/null
+++ b/chromium/components/vector_icons/forward_arrow.1x.icon
@@ -0,0 +1,27 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 7, 13.51f,
+LINE_TO, 11.51f, 9,
+LINE_TO, 2, 9,
+CUBIC_TO, 1.45f, 9, 1, 8.55f, 1, 8,
+CUBIC_TO, 1, 7.45f, 1.45f, 7, 2, 7,
+LINE_TO, 11.51f, 7,
+LINE_TO, 7, 2.49f,
+CUBIC_TO, 7.03f, 2.28f, 7, 2.14f, 7, 2,
+CUBIC_TO, 7, 1.45f, 7.45f, 1, 8, 1,
+CUBIC_TO, 8.14f, 1, 8.28f, 1.03f, 8.41f, 1.09f,
+LINE_TO, 8.49f, 1,
+LINE_TO, 15, 7.51f,
+LINE_TO, 14.91f, 7.59f,
+CUBIC_TO, 14.97f, 7.72f, 15, 7.85f, 15, 8,
+CUBIC_TO, 15, 8.15f, 14.97f, 8.28f, 14.91f, 8.41f,
+LINE_TO, 15, 8.49f,
+LINE_TO, 8.49f, 15,
+CUBIC_TO, 8.28f, 14.97f, 8.14f, 15, 8, 15,
+CUBIC_TO, 7.45f, 15, 7, 14.55f, 7, 14,
+CUBIC_TO, 7, 13.86f, 7.03f, 13.72f, 7.09f, 13.59f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/forward_arrow.icon b/chromium/components/vector_icons/forward_arrow.icon
new file mode 100644
index 00000000000..77363e01f72
--- /dev/null
+++ b/chromium/components/vector_icons/forward_arrow.icon
@@ -0,0 +1,27 @@
+// 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.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 27.78f, 14.03f,
+CUBIC_TO, 28.47f, 14.15f, 29, 14.77f, 29, 15.5f,
+CUBIC_TO, 29, 16.25f, 28.47f, 16.86f, 27.76f, 16.98f,
+LINE_TO, 27.98f, 17.19f,
+LINE_TO, 17.64f, 27.51f,
+CUBIC_TO, 17.35f, 27.81f, 16.95f, 28, 16.5f, 28,
+CUBIC_TO, 15.67f, 28, 15, 27.33f, 15, 26.5f,
+CUBIC_TO, 15, 26.05f, 15.2f, 25.65f, 15.51f, 25.38f,
+LINE_TO, 15.45f, 25.32f,
+LINE_TO, 23.78f, 17,
+LINE_TO, 4.49f, 17,
+CUBIC_TO, 3.67f, 17, 3, 16.33f, 3, 15.5f,
+CUBIC_TO, 3, 14.67f, 3.67f, 14, 4.49f, 14,
+LINE_TO, 23.8f, 14,
+LINE_TO, 15.45f, 5.66f,
+CUBIC_TO, 15.19f, 5.34f, 15, 4.94f, 15, 4.5f,
+CUBIC_TO, 15, 3.68f, 15.67f, 3, 16.5f, 3,
+CUBIC_TO, 16.94f, 3, 17.34f, 3.2f, 17.61f, 3.5f,
+LINE_TO, 17.64f, 3.47f,
+LINE_TO, 28, 13.81f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/info_outline.icon b/chromium/components/vector_icons/info_outline.icon
new file mode 100644
index 00000000000..049fba9ba17
--- /dev/null
+++ b/chromium/components/vector_icons/info_outline.icon
@@ -0,0 +1,30 @@
+// 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.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 16, 2,
+CUBIC_TO, 8.27f, 2, 2, 8.27f, 2, 16,
+CUBIC_TO, 2, 23.73f, 8.27f, 30, 16, 30,
+CUBIC_TO, 23.73f, 30, 30, 23.73f, 30, 16,
+CUBIC_TO, 30, 8.27f, 23.73f, 2, 16, 2,
+CLOSE,
+MOVE_TO, 16, 27.2f,
+CUBIC_TO, 9.83f, 27.2f, 4.8f, 22.17f, 4.8f, 16,
+CUBIC_TO, 4.8f, 9.83f, 9.83f, 4.8f, 16, 4.8f,
+CUBIC_TO, 22.17f, 4.8f, 27.2f, 9.83f, 27.2f, 16,
+CUBIC_TO, 27.2f, 22.17f, 22.17f, 27.2f, 16, 27.2f,
+CLOSE,
+MOVE_TO, 14.6f, 23,
+LINE_TO, 17.4f, 23,
+LINE_TO, 17.4f, 14.6f,
+LINE_TO, 14.6f, 14.6f,
+LINE_TO, 14.6f, 23,
+CLOSE,
+MOVE_TO, 14.6f, 11.8f,
+LINE_TO, 17.4f, 11.8f,
+LINE_TO, 17.4f, 9,
+LINE_TO, 14.6f, 9,
+LINE_TO, 14.6f, 11.8f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/location_on.icon b/chromium/components/vector_icons/location_on.icon
new file mode 100644
index 00000000000..49f18a427aa
--- /dev/null
+++ b/chromium/components/vector_icons/location_on.icon
@@ -0,0 +1,12 @@
+// 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.
+
+MOVE_TO, 24, 4,
+R_CUBIC_TO, -7.73f, 0, -14, 6.27f, -14, 14,
+R_CUBIC_TO, 0, 10.5f, 14, 26, 14, 26,
+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,
+END
diff --git a/chromium/components/vector_icons/lock.icon b/chromium/components/vector_icons/lock.icon
new file mode 100644
index 00000000000..df4b8eb4a58
--- /dev/null
+++ b/chromium/components/vector_icons/lock.icon
@@ -0,0 +1,33 @@
+// 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.
+
+MOVE_TO, 36, 16,
+R_H_LINE_TO, -2,
+R_V_LINE_TO, -4,
+R_CUBIC_TO, 0, -5.52f, -4.48f, -10, -10, -10,
+CUBIC_TO, 18.48f, 2, 14, 6.48f, 14, 12,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, -2,
+R_CUBIC_TO, -2.21f, 0, -4, 1.79f, -4, 4,
+R_V_LINE_TO, 20,
+R_CUBIC_TO, 0, 2.21f, 1.79f, 4, 4, 4,
+R_H_LINE_TO, 24,
+R_CUBIC_TO, 2.21f, 0, 4, -1.79f, 4, -4,
+V_LINE_TO, 20,
+R_CUBIC_TO, 0, -2.21f, -1.79f, -4, -4, -4,
+CLOSE,
+MOVE_TO, 24, 34,
+R_CUBIC_TO, -2.21f, 0, -4, -1.79f, -4, -4,
+R_CUBIC_TO, 0, -2.21f, 1.79f, -4, 4, -4,
+R_CUBIC_TO, 2.21f, 0, 4, 1.79f, 4, 4,
+R_CUBIC_TO, 0, 2.21f, -1.79f, 4, -4, 4,
+CLOSE,
+R_MOVE_TO, 6.2f, -18,
+H_LINE_TO, 17.8f,
+R_V_LINE_TO, -4,
+R_CUBIC_TO, 0, -3.42f, 2.78f, -6.2f, 6.2f, -6.2f,
+R_CUBIC_TO, 3.42f, 0, 6.2f, 2.78f, 6.2f, 6.2f,
+R_V_LINE_TO, 4,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/media_router_active.icon b/chromium/components/vector_icons/media_router_active.icon
new file mode 100644
index 00000000000..74899b027d5
--- /dev/null
+++ b/chromium/components/vector_icons/media_router_active.icon
@@ -0,0 +1,57 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 14.55f, 1.5f,
+H_LINE_TO, 1.45f,
+CUBIC_TO, 0.65f, 1.5f, 0, 2.15f, 0, 2.94f,
+R_V_LINE_TO, 2.18f,
+R_H_LINE_TO, 1.5f,
+V_LINE_TO, 3,
+R_H_LINE_TO, 13,
+R_V_LINE_TO, 10,
+R_H_LINE_TO, -5,
+R_V_LINE_TO, 1.5f,
+R_H_LINE_TO, 5.05f,
+R_CUBIC_TO, 0.8f, 0, 1.45f, -0.65f, 1.45f, -1.44f,
+V_LINE_TO, 2.94f,
+R_CUBIC_TO, 0, -0.79f, -0.65f, -1.44f, -1.45f, -1.44f,
+CLOSE,
+MOVE_TO, 0, 12.37f,
+R_V_LINE_TO, 2.13f,
+R_H_LINE_TO, 2.12f,
+R_CUBIC_TO, 0, -1.2f, -0.91f, -2.13f, -2.12f, -2.13f,
+CLOSE,
+R_MOVE_TO, 0, -3,
+R_V_LINE_TO, 1.5f,
+R_CUBIC_TO, 2.01f, 0, 3.62f, 1.63f, 3.62f, 3.63f,
+R_H_LINE_TO, 1.5f,
+R_CUBIC_TO, 0, -2.79f, -2.31f, -5.13f, -5.12f, -5.13f,
+CLOSE,
+R_MOVE_TO, 0, -2.81f,
+V_LINE_TO, 8,
+R_CUBIC_TO, 3.61f, 0, 6.5f, 2.91f, 6.5f, 6.5f,
+H_LINE_TO, 8,
+R_CUBIC_TO, 0, -4.39f, -3.59f, -7.94f, -8, -7.94f,
+CLOSE,
+MOVE_TO, 13.12f, 4.38f,
+H_LINE_TO, 2.88f,
+V_LINE_TO, 5.5f,
+R_CUBIC_TO, 2.88f, 0.92f, 5.19f, 3.25f, 6.12f, 6.11f,
+R_LINE_TO, 4.13f, 0.01f,
+V_LINE_TO, 4.38f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/media_router_error.icon b/chromium/components/vector_icons/media_router_error.icon
new file mode 100644
index 00000000000..eb1fccd2124
--- /dev/null
+++ b/chromium/components/vector_icons/media_router_error.icon
@@ -0,0 +1,63 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 14.55f, 1.5f,
+H_LINE_TO, 1.45f,
+CUBIC_TO, 0.65f, 1.5f, 0, 2.15f, 0, 2.94f,
+R_V_LINE_TO, 2.18f,
+R_LINE_TO, 1.5f, 0,
+V_LINE_TO, 3,
+R_H_LINE_TO, 13,
+R_V_LINE_TO, 10,
+R_H_LINE_TO, -5,
+R_V_LINE_TO, 1.5f,
+R_H_LINE_TO, 5.05f,
+R_CUBIC_TO, 0.8f, 0, 1.45f, -0.65f, 1.45f, -1.44f,
+V_LINE_TO, 2.94f,
+CUBIC_TO, 16, 2.15f, 15.35f, 1.5f, 14.55f, 1.5f,
+CLOSE,
+MOVE_TO, 0, 12.37f,
+R_V_LINE_TO, 2.13f,
+R_H_LINE_TO, 2.12f,
+CUBIC_TO, 2.12f, 13.3f, 1.21f, 12.37f, 0, 12.37f,
+CLOSE,
+MOVE_TO, 0, 9.37f,
+R_LINE_TO, 0, 1.5f,
+R_CUBIC_TO, 2.01f, 0, 3.62f, 1.63f, 3.62f, 3.63f,
+R_H_LINE_TO, 1.5f,
+CUBIC_TO, 5.12f, 11.71f, 2.81f, 9.37f, 0, 9.37f,
+CLOSE,
+MOVE_TO, 0, 6.56f,
+V_LINE_TO, 8,
+R_CUBIC_TO, 3.61f, 0, 6.5f, 2.91f, 6.5f, 6.5f,
+H_LINE_TO, 8,
+CUBIC_TO, 8, 10.11f, 4.41f, 6.56f, 0, 6.56f,
+CLOSE,
+MOVE_TO, 12.94f, 6,
+R_LINE_TO, -1.69f, 1.69f,
+LINE_TO, 9.56f, 6,
+LINE_TO, 8.5f, 7.06f,
+R_LINE_TO, 1.69f, 1.69f,
+R_LINE_TO, -1.69f, 1.69f,
+R_LINE_TO, 1.06f, 1.06f,
+R_LINE_TO, 1.69f, -1.69f,
+R_LINE_TO, 1.69f, 1.69f,
+R_LINE_TO, 1.06f, -1.06f,
+R_LINE_TO, -1.69f, -1.69f,
+R_LINE_TO, 1.69f, -1.69f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/media_router_idle.icon b/chromium/components/vector_icons/media_router_idle.icon
new file mode 100644
index 00000000000..d320062e1a5
--- /dev/null
+++ b/chromium/components/vector_icons/media_router_idle.icon
@@ -0,0 +1,50 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 14.55f, 1.5f,
+H_LINE_TO, 1.45f,
+CUBIC_TO, 0.65f, 1.5f, 0, 2.15f, 0, 2.94f,
+R_V_LINE_TO, 2.18f,
+R_H_LINE_TO, 1.5f,
+V_LINE_TO, 3,
+R_H_LINE_TO, 13,
+R_V_LINE_TO, 10,
+R_H_LINE_TO, -5,
+R_V_LINE_TO, 1.5f,
+R_H_LINE_TO, 5.05f,
+R_CUBIC_TO, 0.8f, 0, 1.45f, -0.65f, 1.45f, -1.44f,
+V_LINE_TO, 2.94f,
+R_CUBIC_TO, 0, -0.79f, -0.65f, -1.44f, -1.45f, -1.44f,
+CLOSE,
+MOVE_TO, 0, 12.37f,
+R_V_LINE_TO, 2.13f,
+R_H_LINE_TO, 2.12f,
+R_CUBIC_TO, 0, -1.2f, -0.91f, -2.13f, -2.12f, -2.13f,
+CLOSE,
+R_MOVE_TO, 0, -3,
+R_V_LINE_TO, 1.5f,
+R_CUBIC_TO, 2.01f, 0, 3.62f, 1.63f, 3.62f, 3.63f,
+R_H_LINE_TO, 1.5f,
+R_CUBIC_TO, 0, -2.79f, -2.31f, -5.13f, -5.12f, -5.13f,
+CLOSE,
+R_MOVE_TO, 0, -2.81f,
+V_LINE_TO, 8,
+R_CUBIC_TO, 3.61f, 0, 6.5f, 2.91f, 6.5f, 6.5f,
+H_LINE_TO, 8,
+R_CUBIC_TO, 0, -4.39f, -3.59f, -7.94f, -8, -7.94f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/media_router_warning.icon b/chromium/components/vector_icons/media_router_warning.icon
new file mode 100644
index 00000000000..4cbd535b167
--- /dev/null
+++ b/chromium/components/vector_icons/media_router_warning.icon
@@ -0,0 +1,60 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 0, 0,
+R_H_LINE_TO, 16,
+R_V_LINE_TO, 16,
+H_LINE_TO, 0,
+V_LINE_TO, 0,
+CLOSE,
+MOVE_TO, 14.55f, 1.5f,
+H_LINE_TO, 1.45f,
+CUBIC_TO, 0.65f, 1.5f, 0, 2.15f, 0, 2.94f,
+R_V_LINE_TO, 2.18f,
+R_H_LINE_TO, 1.5f,
+V_LINE_TO, 3,
+R_H_LINE_TO, 13,
+R_V_LINE_TO, 10,
+R_H_LINE_TO, -5,
+R_V_LINE_TO, 1.5f,
+R_H_LINE_TO, 5.05f,
+R_CUBIC_TO, 0.8f, 0, 1.45f, -0.65f, 1.45f, -1.44f,
+V_LINE_TO, 2.94f,
+R_CUBIC_TO, 0, -0.79f, -0.65f, -1.44f, -1.45f, -1.44f,
+CLOSE,
+MOVE_TO, 0, 12.37f,
+R_V_LINE_TO, 2.13f,
+R_H_LINE_TO, 2.12f,
+R_CUBIC_TO, 0, -1.2f, -0.91f, -2.13f, -2.12f, -2.13f,
+CLOSE,
+R_MOVE_TO, 0, -3,
+R_V_LINE_TO, 1.5f,
+R_CUBIC_TO, 2.01f, 0, 3.62f, 1.63f, 3.62f, 3.63f,
+R_H_LINE_TO, 1.5f,
+R_CUBIC_TO, 0, -2.79f, -2.31f, -5.13f, -5.12f, -5.13f,
+CLOSE,
+R_MOVE_TO, 0, -2.81f,
+V_LINE_TO, 8,
+R_CUBIC_TO, 3.61f, 0, 6.5f, 2.91f, 6.5f, 6.5f,
+H_LINE_TO, 8,
+R_CUBIC_TO, 0, -4.39f, -3.59f, -7.94f, -8, -7.94f,
+CLOSE,
+MOVE_TO, 11.75f, 4.38f,
+R_H_LINE_TO, 1.38f,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, -1.38f,
+CLOSE,
+MOVE_TO, 11.75f, 9.75f,
+R_H_LINE_TO, 1.38f,
+R_V_LINE_TO, 1.38f,
+R_H_LINE_TO, -1.38f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/microphone.icon b/chromium/components/vector_icons/microphone.icon
new file mode 100644
index 00000000000..8c7962af678
--- /dev/null
+++ b/chromium/components/vector_icons/microphone.icon
@@ -0,0 +1,24 @@
+// 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.
+
+MOVE_TO, 24, 30,
+R_CUBIC_TO, 3.31f, 0, 5.98f, -2.69f, 5.98f, -6,
+LINE_TO, 30, 12,
+R_CUBIC_TO, 0, -3.32f, -2.68f, -6, -6, -6,
+R_CUBIC_TO, -3.31f, 0, -6, 2.68f, -6, 6,
+R_V_LINE_TO, 12,
+R_CUBIC_TO, 0, 3.31f, 2.69f, 6, 6, 6,
+CLOSE,
+R_MOVE_TO, 10.6f, -6,
+R_CUBIC_TO, 0, 6, -5.07f, 10.2f, -10.6f, 10.2f,
+R_CUBIC_TO, -5.52f, 0, -10.6f, -4.2f, -10.6f, -10.2f,
+H_LINE_TO, 10,
+R_CUBIC_TO, 0, 6.83f, 5.44f, 12.47f, 12, 13.44f,
+V_LINE_TO, 44,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, -6.56f,
+R_CUBIC_TO, 6.56f, -0.97f, 12, -6.61f, 12, -13.44f,
+R_H_LINE_TO, -3.4f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/midi.icon b/chromium/components/vector_icons/midi.icon
new file mode 100644
index 00000000000..10c73a85522
--- /dev/null
+++ b/chromium/components/vector_icons/midi.icon
@@ -0,0 +1,41 @@
+// 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.
+
+MOVE_TO, 33, 31,
+LINE_TO, 33, 41,
+LINE_TO, 29, 41,
+LINE_TO, 29, 31,
+CUBIC_TO, 27.9f, 31, 27, 30.1f, 27, 29,
+LINE_TO, 27, 7,
+LINE_TO, 35, 7,
+LINE_TO, 35, 29,
+CUBIC_TO, 35, 30.1f, 34.1f, 31, 33, 31,
+CLOSE,
+MOVE_TO, 19, 31,
+LINE_TO, 19, 41,
+LINE_TO, 15, 41,
+LINE_TO, 15, 31,
+CUBIC_TO, 13.9f, 31, 13, 30.1f, 13, 29,
+LINE_TO, 13, 7,
+LINE_TO, 21, 7,
+LINE_TO, 21, 29,
+CUBIC_TO, 21, 30.1f, 20.1f, 31, 19, 31,
+CLOSE,
+MOVE_TO, 40, 4,
+LINE_TO, 8, 4,
+CUBIC_TO, 5.8f, 4, 4, 5.8f, 4, 8,
+LINE_TO, 4, 40,
+CUBIC_TO, 4, 42.2f, 5.8f, 44, 8, 44,
+LINE_TO, 40, 44,
+CUBIC_TO, 42.2f, 44, 44, 42.2f, 44, 40,
+LINE_TO, 44, 8,
+CUBIC_TO, 44, 5.8f, 42.2f, 4, 40, 4,
+CLOSE,
+MOVE_TO, 7, 7,
+LINE_TO, 41, 7,
+LINE_TO, 41, 41,
+LINE_TO, 7, 41,
+LINE_TO, 7, 7,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/notifications.icon b/chromium/components/vector_icons/notifications.icon
new file mode 100644
index 00000000000..8240b82981c
--- /dev/null
+++ b/chromium/components/vector_icons/notifications.icon
@@ -0,0 +1,24 @@
+// 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.
+
+MOVE_TO, 24, 44,
+R_CUBIC_TO, 2.21f, 0, 4, -1.49f, 4, -4,
+R_H_LINE_TO, -8,
+R_CUBIC_TO, 0, 2.21f, 1.79f, 4, 4, 4,
+CLOSE,
+R_MOVE_TO, 12, -12,
+V_LINE_TO, 22,
+R_CUBIC_TO, 0, -6.15f, -3.27f, -11.28f, -9, -12.64f,
+V_LINE_TO, 8,
+R_CUBIC_TO, 0, -1.66f, -1.34f, -3, -3, -3,
+R_CUBIC_TO, -1.66f, 0, -3, 1.34f, -3, 3,
+R_V_LINE_TO, 1.36f,
+R_CUBIC_TO, -5.73f, 1.36f, -9, 6.49f, -9, 12.64f,
+R_V_LINE_TO, 10,
+R_LINE_TO, -4, 4,
+R_V_LINE_TO, 2,
+R_H_LINE_TO, 32,
+R_V_LINE_TO, -2,
+R_LINE_TO, -4, -4,
+END
diff --git a/chromium/components/vector_icons/notifications_off.icon b/chromium/components/vector_icons/notifications_off.icon
new file mode 100644
index 00000000000..9b327847ed5
--- /dev/null
+++ b/chromium/components/vector_icons/notifications_off.icon
@@ -0,0 +1,41 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 20, 18.69f,
+LINE_TO, 7.84f, 6.14f,
+LINE_TO, 5.27f, 3.49f,
+LINE_TO, 4, 4.76f,
+R_LINE_TO, 2.8f, 2.8f,
+R_V_LINE_TO, 0.01f,
+R_CUBIC_TO, -0.52f, 0.99f, -0.8f, 2.16f, -0.8f, 3.42f,
+R_V_LINE_TO, 5,
+R_LINE_TO, -2, 2,
+R_V_LINE_TO, 1,
+R_H_LINE_TO, 13.73f,
+R_LINE_TO, 2, 2,
+LINE_TO, 21, 19.72f,
+R_LINE_TO, -1, -1.03f,
+CLOSE,
+MOVE_TO, 12, 22,
+R_CUBIC_TO, 1.11f, 0, 2, -0.89f, 2, -2,
+R_H_LINE_TO, -4,
+R_CUBIC_TO, 0, 1.11f, 0.89f, 2, 2, 2,
+CLOSE,
+R_MOVE_TO, 6, -7.32f,
+V_LINE_TO, 11,
+R_CUBIC_TO, 0, -3.08f, -1.64f, -5.64f, -4.5f, -6.32f,
+V_LINE_TO, 4,
+R_CUBIC_TO, 0, -0.83f, -0.67f, -1.5f, -1.5f, -1.5f,
+R_CUBIC_TO, -0.83f, 0, -1.5f, 0.67f, -1.5f, 1.5f,
+R_V_LINE_TO, 0.68f,
+R_CUBIC_TO, -0.15f, 0.03f, -0.29f, 0.08f, -0.42f, 0.12f,
+R_CUBIC_TO, -0.1f, 0.03f, -0.2f, 0.07f, -0.3f, 0.11f,
+R_H_LINE_TO, -0.01f,
+R_CUBIC_TO, -0.01f, 0, -0.01f, 0, -0.02f, 0.01f,
+R_CUBIC_TO, -0.23f, 0.09f, -0.46f, 0.2f, -0.68f, 0.31f,
+R_CUBIC_TO, 0, 0, -0.01f, 0, -0.01f, 0.01f,
+LINE_TO, 18, 14.68f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/protocol_handler.icon b/chromium/components/vector_icons/protocol_handler.icon
new file mode 100644
index 00000000000..3ded270d1e0
--- /dev/null
+++ b/chromium/components/vector_icons/protocol_handler.icon
@@ -0,0 +1,48 @@
+// 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.
+
+MOVE_TO, 43.44f, 22.66f,
+LINE_TO, 30.15f, 8.59f,
+CUBIC_TO, 29.42f, 7.82f, 28.14f, 7.81f, 27.39f, 8.57f,
+LINE_TO, 24.05f, 12.01f,
+LINE_TO, 20.82f, 8.59f,
+CUBIC_TO, 20.08f, 7.81f, 18.79f, 7.81f, 18.06f, 8.57f,
+LINE_TO, 4.58f, 22.44f,
+CUBIC_TO, 4.21f, 22.82f, 4, 23.33f, 4, 23.87f,
+CUBIC_TO, 4, 24.42f, 4.2f, 24.93f, 4.56f, 25.32f,
+LINE_TO, 17.85f, 39.39f,
+CUBIC_TO, 18.22f, 39.78f, 18.71f, 40, 19.24f, 40,
+CUBIC_TO, 19.75f, 40, 20.24f, 39.79f, 20.61f, 39.41f,
+LINE_TO, 23.95f, 35.97f,
+LINE_TO, 27.18f, 39.39f,
+CUBIC_TO, 27.55f, 39.78f, 28.04f, 40, 28.57f, 40,
+CUBIC_TO, 29.09f, 40, 29.58f, 39.79f, 29.94f, 39.41f,
+LINE_TO, 43.42f, 25.54f,
+CUBIC_TO, 44.18f, 24.76f, 44.19f, 23.46f, 43.44f, 22.66f,
+LINE_TO, 43.44f, 22.66f,
+LINE_TO, 43.44f, 22.66f,
+CLOSE,
+MOVE_TO, 13.89f, 24.93f,
+LINE_TO, 21.21f, 32.5f,
+LINE_TO, 19.26f, 34.46f,
+LINE_TO, 8.71f, 23.54f,
+LINE_TO, 19.41f, 12.79f,
+LINE_TO, 21.27f, 14.71f,
+LINE_TO, 13.92f, 22.11f,
+CUBIC_TO, 13.54f, 22.49f, 13.34f, 22.98f, 13.33f, 23.52f,
+CUBIC_TO, 13.33f, 24.05f, 13.53f, 24.55f, 13.89f, 24.93f,
+LINE_TO, 13.89f, 24.93f,
+LINE_TO, 13.89f, 24.93f,
+CLOSE,
+MOVE_TO, 28.59f, 34.46f,
+LINE_TO, 26.73f, 32.54f,
+LINE_TO, 34.09f, 25.14f,
+CUBIC_TO, 34.85f, 24.37f, 34.86f, 23.1f, 34.11f, 22.32f,
+LINE_TO, 26.79f, 14.75f,
+LINE_TO, 28.74f, 12.79f,
+LINE_TO, 39.29f, 23.71f,
+LINE_TO, 28.59f, 34.46f,
+LINE_TO, 28.59f, 34.46f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/screen_share.1x.icon b/chromium/components/vector_icons/screen_share.1x.icon
deleted file mode 100644
index 316ad3786b8..00000000000
--- a/chromium/components/vector_icons/screen_share.1x.icon
+++ /dev/null
@@ -1,30 +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.
-
-CANVAS_DIMENSIONS, 16,
-MOVE_TO, 2.31f, 3,
-CUBIC_TO, 1.59f, 3, 1, 3.57f, 1, 4.27f,
-R_V_LINE_TO, 7.62f,
-CUBIC_TO, 1, 12.58f, 1.59f, 13, 2.31f, 13,
-R_H_LINE_TO, 11.38f,
-R_CUBIC_TO, 0.72f, 0, 1.31f, -0.42f, 1.31f, -1.11f,
-V_LINE_TO, 4.27f,
-CUBIC_TO, 15, 3.57f, 14.41f, 3, 13.69f, 3,
-H_LINE_TO, 2.31f,
-CLOSE,
-MOVE_TO, 2, 12,
-V_LINE_TO, 4,
-R_H_LINE_TO, 12,
-R_V_LINE_TO, 8,
-H_LINE_TO, 2,
-CLOSE,
-R_MOVE_TO, 6.36f, -3.33f,
-CUBIC_TO, 6.62f, 8.64f, 5.8f, 9.07f, 5, 10,
-R_CUBIC_TO, 0.32f, -1.33f, 0.98f, -2.66f, 3.36f, -2.67f,
-V_LINE_TO, 6,
-LINE_TO, 11, 8,
-R_LINE_TO, -2.64f, 2,
-V_LINE_TO, 8.67f,
-CLOSE,
-END
diff --git a/chromium/components/vector_icons/search.icon b/chromium/components/vector_icons/search.icon
new file mode 100644
index 00000000000..c1e5f084347
--- /dev/null
+++ b/chromium/components/vector_icons/search.icon
@@ -0,0 +1,27 @@
+// 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.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 20.29f, 19.45f,
+LINE_TO, 19.93f, 19.09f,
+CUBIC_TO, 21.23f, 17.59f, 22.13f, 15.52f, 22.13f, 12.96f,
+CUBIC_TO, 22.13f, 8.23f, 17.85f, 4, 13.07f, 4,
+CUBIC_TO, 8.28f, 4, 4, 8.23f, 4, 12.96f,
+CUBIC_TO, 4, 17.69f, 9.18f, 21.92f, 13.26f, 21.92f,
+CUBIC_TO, 15.66f, 21.92f, 17.51f, 21.26f, 18.98f, 20.02f,
+LINE_TO, 19.35f, 20.38f,
+LINE_TO, 19.35f, 21.42f,
+LINE_TO, 26.01f, 28,
+LINE_TO, 28, 26.04f,
+LINE_TO, 21.35f, 19.45f,
+LINE_TO, 20.29f, 19.45f,
+CLOSE,
+MOVE_TO, 13.07f, 19.36f,
+CUBIC_TO, 9.49f, 19.36f, 6.59f, 16.5f, 6.59f, 12.96f,
+CUBIC_TO, 6.59f, 9.43f, 9.49f, 6.56f, 13.07f, 6.56f,
+CUBIC_TO, 16.64f, 6.56f, 19.54f, 9.43f, 19.54f, 12.96f,
+CUBIC_TO, 19.54f, 16.5f, 16.64f, 19.36f, 13.07f, 19.36f,
+LINE_TO, 13.07f, 19.36f,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/vector_icons.gni b/chromium/components/vector_icons/vector_icons.gni
new file mode 100644
index 00000000000..e1a36481074
--- /dev/null
+++ b/chromium/components/vector_icons/vector_icons.gni
@@ -0,0 +1,61 @@
+# 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.
+
+# Creates an action that aggregates vector icon files (.icon) into a C++ file.
+# In addition to running the action, the outputs should be added to another
+# target's sources for compilation.
+#
+# Parameters
+#
+# icons (required)
+# A list of icon filenames to use as inputs.
+#
+# icon_directory (required)
+# The path component of the location of the icons, relative to the current
+# directory. For example, if the invoking BUILD file exists in //foo/bar
+# and the icons are in //foo/bar/vector_icons/, then icon_directory should
+# be set to "vector_icons". There must also be template files in this
+# directory.
+#
+# Example
+#
+# See BUILD.gn in this directory (//components/vector_icons/) for an example.
+template("aggregate_vector_icons") {
+ assert(defined(invoker.icons),
+ "Need icons in $target_name listing the icon files.")
+ assert(
+ defined(invoker.icon_directory),
+ "Need icon_directory in $target_name where the icons and templates live.")
+
+ action(target_name) {
+ visibility = [ ":*" ]
+
+ script = "//components/vector_icons/aggregate_vector_icons.py"
+
+ output_cc = "$target_gen_dir/vector_icons.cc"
+ output_h = "$target_gen_dir/vector_icons.h"
+
+ templates = [
+ "vector_icons.cc.template",
+ "vector_icons.h.template",
+ ]
+ inputs = rebase_path(templates + invoker.icons, ".", invoker.icon_directory)
+
+ outputs = [
+ output_cc,
+ output_h,
+ ]
+
+ response_file_contents =
+ rebase_path(invoker.icons, root_build_dir, invoker.icon_directory)
+
+ args = [
+ "--working_directory=" +
+ rebase_path(invoker.icon_directory, root_build_dir),
+ "--file_list={{response_file_name}}",
+ "--output_cc=" + rebase_path(output_cc, root_build_dir),
+ "--output_h=" + rebase_path(output_h, root_build_dir),
+ ]
+ }
+}
diff --git a/chromium/components/vector_icons/videocam.icon b/chromium/components/vector_icons/videocam.icon
new file mode 100644
index 00000000000..994e07ed03a
--- /dev/null
+++ b/chromium/components/vector_icons/videocam.icon
@@ -0,0 +1,19 @@
+// 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.
+
+MOVE_TO, 34, 21,
+R_V_LINE_TO, -7,
+R_CUBIC_TO, 0, -1.1f, -0.9f, -2, -2, -2,
+H_LINE_TO, 8,
+R_CUBIC_TO, -1.1f, 0, -2, 0.9f, -2, 2,
+R_V_LINE_TO, 20,
+R_CUBIC_TO, 0, 1.1f, 0.9f, 2, 2, 2,
+R_H_LINE_TO, 24,
+R_CUBIC_TO, 1.1f, 0, 2, -0.9f, 2, -2,
+R_V_LINE_TO, -7,
+R_LINE_TO, 8, 8,
+V_LINE_TO, 13,
+R_LINE_TO, -8, 8,
+CLOSE,
+END
diff --git a/chromium/components/vector_icons/warning.icon b/chromium/components/vector_icons/warning.icon
new file mode 100644
index 00000000000..7a5f42562fd
--- /dev/null
+++ b/chromium/components/vector_icons/warning.icon
@@ -0,0 +1,25 @@
+// 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.
+
+MOVE_TO, 2, 42,
+R_H_LINE_TO, 44,
+LINE_TO, 24, 4,
+LINE_TO, 2, 42,
+CLOSE,
+// Divergence from ic_warning: white fill on the ! portion.
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF,
+MOVE_TO, 26, 36,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 4,
+CLOSE,
+R_MOVE_TO, 0, -8,
+R_H_LINE_TO, -4,
+R_V_LINE_TO, -8,
+R_H_LINE_TO, 4,
+R_V_LINE_TO, 8,
+CLOSE,
+END
diff --git a/chromium/components/version_info/BUILD.gn b/chromium/components/version_info/BUILD.gn
index 1fe5cfb1cad..17b24f412ae 100644
--- a/chromium/components/version_info/BUILD.gn
+++ b/chromium/components/version_info/BUILD.gn
@@ -19,12 +19,24 @@ static_library("version_info") {
deps = [
":generate_version_info",
"//base",
- "//components/strings",
]
public_deps = [
":channel",
]
+}
+
+# Isolate the //ui/base dependency in this target.
+static_library("version_string") {
+ sources = [
+ "version_string.cc",
+ "version_string.h",
+ ]
+
+ deps = [
+ ":version_info",
+ "//components/strings",
+ ]
if (use_unofficial_version_number) {
defines = [ "USE_UNOFFICIAL_VERSION_NUMBER" ]
@@ -35,6 +47,8 @@ static_library("version_info") {
source_set("channel") {
sources = [
"channel.h",
+ "channel_android.cc",
+ "channel_android.h",
]
}
diff --git a/chromium/components/version_info/channel_android.cc b/chromium/components/version_info/channel_android.cc
new file mode 100644
index 00000000000..b63b5ac924c
--- /dev/null
+++ b/chromium/components/version_info/channel_android.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/version_info/channel_android.h"
+
+#include <cstring>
+
+namespace version_info {
+
+Channel ChannelFromPackageName(const char* package_name) {
+ if (!strcmp(package_name, "com.android.chrome") ||
+ !strcmp(package_name, "com.chrome.work"))
+ return Channel::STABLE;
+ if (!strcmp(package_name, "com.chrome.beta"))
+ return Channel::BETA;
+ if (!strcmp(package_name, "com.chrome.dev"))
+ return Channel::DEV;
+ if (!strcmp(package_name, "com.chrome.canary"))
+ return Channel::CANARY;
+
+ return Channel::UNKNOWN;
+}
+
+} // namespace version_info
diff --git a/chromium/components/version_info/channel_android.h b/chromium/components/version_info/channel_android.h
new file mode 100644
index 00000000000..d4e5cf2002f
--- /dev/null
+++ b/chromium/components/version_info/channel_android.h
@@ -0,0 +1,17 @@
+// 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_VERSION_INFO_CHANNEL_ANDROID_H_
+#define COMPONENTS_VERSION_INFO_CHANNEL_ANDROID_H_
+
+#include "components/version_info/channel.h"
+
+namespace version_info {
+
+// Determine channel based on Chrome's package name.
+Channel ChannelFromPackageName(const char* package_name);
+
+} // namespace version_info
+
+#endif // COMPONENTS_VERSION_INFO_CHANNEL_ANDROID_H_
diff --git a/chromium/components/version_info/version_info.cc b/chromium/components/version_info/version_info.cc
index b12ad0ff687..0b28f152390 100644
--- a/chromium/components/version_info/version_info.cc
+++ b/chromium/components/version_info/version_info.cc
@@ -5,7 +5,6 @@
#include "components/version_info/version_info.h"
#include "build/build_config.h"
-#include "components/strings/grit/components_strings.h"
#include "components/version_info/version_info_values.h"
#if defined(USE_UNOFFICIAL_VERSION_NUMBER)
@@ -83,21 +82,4 @@ std::string GetChannelString(Channel channel) {
return std::string();
}
-std::string GetVersionStringWithModifier(const std::string& modifier) {
- std::string current_version;
- current_version += GetVersionNumber();
-#if defined(USE_UNOFFICIAL_VERSION_NUMBER)
- current_version += " (";
- current_version += l10n_util::GetStringUTF8(IDS_VERSION_UI_UNOFFICIAL);
- current_version += " ";
- current_version += GetLastChange();
- current_version += " ";
- current_version += GetOSType();
- current_version += ")";
-#endif // USE_UNOFFICIAL_VERSION_NUMBER
- if (!modifier.empty())
- current_version += " " + modifier;
- return current_version;
-}
-
} // namespace version_info
diff --git a/chromium/components/version_info/version_info.h b/chromium/components/version_info/version_info.h
index 62125412b65..f25fc5ce52e 100644
--- a/chromium/components/version_info/version_info.h
+++ b/chromium/components/version_info/version_info.h
@@ -36,12 +36,6 @@ std::string GetOSType();
// is branded or not and without any additional modifiers.
std::string GetChannelString(Channel channel);
-// Returns a version string to be displayed in "About Chromium" dialog.
-// |modifier| is a string representation of the channel with system specific
-// information, e.g. "dev SyzyASan". It is appended to the returned version
-// information if non-empty.
-std::string GetVersionStringWithModifier(const std::string& modifier);
-
} // namespace version_info
#endif // COMPONENTS_VERSION_INFO_VERSION_INFO_H_
diff --git a/chromium/components/version_info/version_string.cc b/chromium/components/version_info/version_string.cc
new file mode 100644
index 00000000000..c1e9dcda509
--- /dev/null
+++ b/chromium/components/version_info/version_string.cc
@@ -0,0 +1,33 @@
+// 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/version_info/version_string.h"
+
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+
+#if defined(USE_UNOFFICIAL_VERSION_NUMBER)
+#include "ui/base/l10n/l10n_util.h" // nogncheck
+#endif // USE_UNOFFICIAL_VERSION_NUMBER
+
+namespace version_info {
+
+std::string GetVersionStringWithModifier(const std::string& modifier) {
+ std::string current_version;
+ current_version += GetVersionNumber();
+#if defined(USE_UNOFFICIAL_VERSION_NUMBER)
+ current_version += " (";
+ current_version += l10n_util::GetStringUTF8(IDS_VERSION_UI_UNOFFICIAL);
+ current_version += " ";
+ current_version += GetLastChange();
+ current_version += " ";
+ current_version += GetOSType();
+ current_version += ")";
+#endif // USE_UNOFFICIAL_VERSION_NUMBER
+ if (!modifier.empty())
+ current_version += " " + modifier;
+ return current_version;
+}
+
+} // namespace version_info
diff --git a/chromium/components/version_info/version_string.h b/chromium/components/version_info/version_string.h
new file mode 100644
index 00000000000..083cfbf33cb
--- /dev/null
+++ b/chromium/components/version_info/version_string.h
@@ -0,0 +1,20 @@
+// 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_VERSION_INFO_VERSION_STRING_H_
+#define COMPONENTS_VERSION_INFO_VERSION_STRING_H_
+
+#include <string>
+
+namespace version_info {
+
+// Returns a version string to be displayed in "About Chromium" dialog.
+// |modifier| is a string representation of the channel with system specific
+// information, e.g. "dev SyzyASan". It is appended to the returned version
+// information if non-empty.
+std::string GetVersionStringWithModifier(const std::string& modifier);
+
+} // namespace version_info
+
+#endif // COMPONENTS_VERSION_INFO_VERSION_STRING_H_
diff --git a/chromium/components/version_ui/resources/about_version.html b/chromium/components/version_ui/resources/about_version.html
index 19a261c948f..c19c5f87a04 100644
--- a/chromium/components/version_ui/resources/about_version.html
+++ b/chromium/components/version_ui/resources/about_version.html
@@ -4,10 +4,10 @@
about:version template page
-->
-<html id="t" i18n-values="dir:textdirection;lang:language">
+<html id="t" dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
- <title i18n-content="title"></title>
+ <title>$i18n{title}</title>
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<if expr="is_android or is_ios">
<meta name="viewport" content="width=device-width">
@@ -40,66 +40,66 @@ about:version template page
<if expr="is_ios or is_android">
<img src="../../../components/resources/default_100_percent/%DISTRIBUTION%/product_logo.png">
</if>
- <div id="company" i18n-content="company"></div>
- <div id="copyright" i18n-content="copyright"></div>
+ <div id="company">$i18n{company}</div>
+ <div id="copyright">$i18n{copyright}</div>
</div>
<table id="inner" cellpadding="0" cellspacing="0" border="0">
- <tr><td class="label" i18n-content="application_label"></td>
+ <tr><td class="label">$i18n{application_label}</td>
<td class="version" id="version">
- <span i18n-content="version"></span>
- (<span i18n-content="official"></span>)
- <span i18n-content="version_modifier"></span>
- <span i18n-content="version_bitsize"></span>
+ <span>$i18n{version}</span>
+ (<span>$i18n{official}</span>)
+ <span>$i18n{version_modifier}</span>
+ <span>$i18n{version_bitsize}</span>
<if expr="is_win">
- <span i18n-content="update_cohort_name"></span>
+ <span>$i18n{update_cohort_name}</span>
</if>
</td>
</tr>
<tr>
- <td class="label" i18n-content="revision"></td>
+ <td class="label">$i18n{revision}</td>
<td class="version">
- <span i18n-content="cl"></span>
+ <span>$i18n{cl}</span>
</td>
</tr>
<if expr="not chromeos">
<tr>
- <td class="label" i18n-content="os_name"></td>
+ <td class="label">$i18n{os_name}</td>
<td class="version" id="os_type">
- <span i18n-content="os_type"></span>
+ <span>$i18n{os_type}</span>
<if expr="is_android">
- <span i18n-content="os_version"></span>
+ <span>$i18n{os_version}</span>
</if>
</td>
</tr>
</if>
<if expr="is_android">
<tr>
- <td class="label" i18n-content="gms_name"></td>
+ <td class="label">$i18n{gms_name}</td>
<td class="version" id="gms_version">
- <span i18n-content="gms_version"></span>
+ <span>$i18n{gms_version}</span>
</td>
</tr>
</if>
<if expr="chromeos">
<tr>
- <td class="label" i18n-content="platform"></td>
+ <td class="label">$i18n{platform}</td>
<td class="version" id="os_type">
<span id="os_version"></span>
</td>
</tr>
<tr>
- <td class="label" i18n-content="firmware_version"></td>
+ <td class="label">$i18n{firmware_version}</td>
<td class="version">
<span id="firmware_version"></span>
</td>
</tr>
<tr id="customization_id_holder" hidden>
- <td class="label" i18n-content="customization_id"></td>
+ <td class="label">$i18n{customization_id}</td>
<td class="version">
<span id="customization_id"></span>
</td>
</tr>
- <tr id="arc_holder"><td class="label" i18n-content="arc_label"></td>
+ <tr id="arc_holder"><td class="label">$i18n{arc_label}</td>
<td class="version">
<span id="arc_version"></span>
</td>
@@ -108,38 +108,38 @@ about:version template page
<if expr="not is_ios">
<tr><td class="label">JavaScript</td>
<td class="version" id="js_engine">
- <span i18n-content="js_engine"></span>
- <span i18n-content="js_version"></span>
+ <span>$i18n{js_engine}</span>
+ <span>$i18n{js_version}</span>
</td>
</tr>
</if>
<if expr="not is_android and not is_ios">
- <tr><td class="label" i18n-content="flash_plugin"></td>
- <td class="version" id="flash_version" i18n-content="flash_version"></td>
+ <tr><td class="label">$i18n{flash_plugin}</td>
+ <td class="version" id="flash_version">$i18n{flash_version}</td>
</tr>
</if>
- <tr><td class="label" i18n-content="user_agent_name"></td>
- <td class="version" id="useragent" i18n-content="useragent"></td>
+ <tr><td class="label">$i18n{user_agent_name}</td>
+ <td class="version" id="useragent">$i18n{useragent}</td>
</tr>
- <tr><td class="label" i18n-content="command_line_name"></td>
- <td class="version" id="command_line" i18n-content="command_line"></td>
+ <tr><td class="label">$i18n{command_line_name}</td>
+ <td class="version" id="command_line">$i18n{command_line}</td>
</tr>
<if expr="not is_ios">
- <tr><td class="label" i18n-content="executable_path_name"></td>
- <td class="version" id="executable_path" i18n-content="executable_path"></td>
+ <tr><td class="label">$i18n{executable_path_name}</td>
+ <td class="version" id="executable_path">$i18n{executable_path}</td>
</tr>
- <tr><td class="label" i18n-content="profile_path_name"></td>
- <td class="version" id="profile_path" i18n-content="profile_path"></td>
+ <tr><td class="label">$i18n{profile_path_name}</td>
+ <td class="version" id="profile_path">$i18n{profile_path}</td>
</tr>
</if>
<tr id="variations-section">
- <td class="label" i18n-content="variations_name"></td>
+ <td class="label">$i18n{variations_name}</td>
<td class="version" id="variations-list"></td>
</tr>
<if expr="is_win">
<tr id="compiler-section">
<td class="label">Compiler</td>
- <td class="version" id="compiler" i18n-content="compiler"></td>
+ <td class="version" id="compiler">$i18n{compiler}</td>
</tr>
</if>
</table>
diff --git a/chromium/components/version_ui/version_handler_helper.cc b/chromium/components/version_ui/version_handler_helper.cc
index b24c03c6f76..6bdc949bf61 100644
--- a/chromium/components/version_ui/version_handler_helper.cc
+++ b/chromium/components/version_ui/version_handler_helper.cc
@@ -8,6 +8,7 @@
#include <vector>
#include "base/metrics/field_trial.h"
+#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "components/variations/active_field_trials.h"
@@ -30,7 +31,8 @@ std::unique_ptr<base::Value> GetVariationsList() {
}
#else
// In release mode, display the hashes only.
- variations::GetFieldTrialActiveGroupIdsAsStrings(&variations);
+ variations::GetFieldTrialActiveGroupIdsAsStrings(base::StringPiece(),
+ &variations);
#endif
std::unique_ptr<base::ListValue> variations_list(new base::ListValue);
diff --git a/chromium/components/version_ui_strings.grdp b/chromium/components/version_ui_strings.grdp
index 55076d27567..72925ab1291 100644
--- a/chromium/components/version_ui_strings.grdp
+++ b/chromium/components/version_ui_strings.grdp
@@ -56,7 +56,7 @@
Variations
</message>
<if expr="is_win">
- <message name="IDS_VERSION_UI_COHORT_NAME" desc="Update cohort substring included in the version number line on the about:version page.">
+ <message translateable="false" name="IDS_VERSION_UI_COHORT_NAME" desc="Update cohort substring included in the version number line on the about:version page.">
(cohort: <ph name="UPDATE_COHORT_NAME">$1<ex>Stable</ex></ph>)
</message>
</if>
diff --git a/chromium/components/visitedlink/renderer/visitedlink_slave.cc b/chromium/components/visitedlink/renderer/visitedlink_slave.cc
index 97c4ad32790..5355c14d5a1 100644
--- a/chromium/components/visitedlink/renderer/visitedlink_slave.cc
+++ b/chromium/components/visitedlink/renderer/visitedlink_slave.cc
@@ -20,8 +20,7 @@ VisitedLinkSlave::~VisitedLinkSlave() {
FreeTable();
}
-base::Callback<void(const service_manager::BindSourceInfo&,
- mojom::VisitedLinkNotificationSinkRequest)>
+base::Callback<void(mojom::VisitedLinkNotificationSinkRequest)>
VisitedLinkSlave::GetBindCallback() {
return base::Bind(&VisitedLinkSlave::Bind, weak_factory_.GetWeakPtr());
}
@@ -80,8 +79,7 @@ void VisitedLinkSlave::FreeTable() {
table_length_ = 0;
}
-void VisitedLinkSlave::Bind(const service_manager::BindSourceInfo& source_info,
- mojom::VisitedLinkNotificationSinkRequest request) {
+void VisitedLinkSlave::Bind(mojom::VisitedLinkNotificationSinkRequest request) {
binding_.Bind(std::move(request));
}
diff --git a/chromium/components/visitedlink/renderer/visitedlink_slave.h b/chromium/components/visitedlink/renderer/visitedlink_slave.h
index 658d373e932..bbf0cfde2f8 100644
--- a/chromium/components/visitedlink/renderer/visitedlink_slave.h
+++ b/chromium/components/visitedlink/renderer/visitedlink_slave.h
@@ -12,7 +12,6 @@
#include "components/visitedlink/common/visitedlink_common.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/system/buffer.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
namespace visitedlink {
@@ -24,8 +23,7 @@ class VisitedLinkSlave : public VisitedLinkCommon,
VisitedLinkSlave();
~VisitedLinkSlave() override;
- base::Callback<void(const service_manager::BindSourceInfo&,
- mojom::VisitedLinkNotificationSinkRequest)>
+ base::Callback<void(mojom::VisitedLinkNotificationSinkRequest)>
GetBindCallback();
// mojom::VisitedLinkNotificationSink overrides.
@@ -37,8 +35,7 @@ class VisitedLinkSlave : public VisitedLinkCommon,
private:
void FreeTable();
- void Bind(const service_manager::BindSourceInfo& source_info,
- mojom::VisitedLinkNotificationSinkRequest request);
+ void Bind(mojom::VisitedLinkNotificationSinkRequest request);
mojo::ScopedSharedBufferMapping table_mapping_;
diff --git a/chromium/components/viz/BUILD.gn b/chromium/components/viz/BUILD.gn
new file mode 100644
index 00000000000..2795882f245
--- /dev/null
+++ b/chromium/components/viz/BUILD.gn
@@ -0,0 +1,56 @@
+# 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.
+
+import("//components/viz/viz.gni")
+import("//testing/test.gni")
+
+viz_test("viz_unittests") {
+ sources = [
+ "test/run_all_unittests.cc",
+ ]
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//components/viz/common:unit_tests",
+ "//components/viz/host:unit_tests",
+ "//components/viz/service:unit_tests",
+ "//components/viz/test:test_suite",
+ "//components/viz/test:test_support",
+ "//mojo/edk/system",
+ ]
+
+ if (!is_android) {
+ data = [
+ "test/data/",
+ ]
+ }
+
+ data_deps = [
+ "//third_party/mesa:osmesa",
+ ]
+}
+
+viz_test("viz_perftests") {
+ sources = [
+ "test/run_all_perftests.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//components/viz/service:perf_tests",
+ "//components/viz/test:test_suite",
+ ]
+
+ # This target should not require the Chrome executable to run.
+ assert_no_deps = [ "//chrome" ]
+
+ 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",
+ ]
+}
diff --git a/chromium/components/viz/DEPS b/chromium/components/viz/DEPS
index f8654e9ef32..c4d836f14ed 100644
--- a/chromium/components/viz/DEPS
+++ b/chromium/components/viz/DEPS
@@ -1,4 +1,4 @@
include_rules = [
- "+gpu",
- "+mojo/public",
+ "-components/viz",
+ "+components/viz/common",
]
diff --git a/chromium/components/viz/client/BUILD.gn b/chromium/components/viz/client/BUILD.gn
new file mode 100644
index 00000000000..1c97eed5def
--- /dev/null
+++ b/chromium/components/viz/client/BUILD.gn
@@ -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.
+
+import("//components/viz/viz.gni")
+
+viz_source_set("client") {
+ sources = [
+ "client_layer_tree_frame_sink.cc",
+ "client_layer_tree_frame_sink.h",
+ "client_shared_bitmap_manager.cc",
+ "client_shared_bitmap_manager.h",
+ "local_surface_id_provider.cc",
+ "local_surface_id_provider.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//cc",
+ "//cc/ipc:interfaces",
+ "//cc/surfaces",
+ "//components/viz/common",
+ "//mojo/public/cpp/bindings",
+ ]
+}
diff --git a/chromium/components/viz/client/DEPS b/chromium/components/viz/client/DEPS
new file mode 100644
index 00000000000..78b21a84333
--- /dev/null
+++ b/chromium/components/viz/client/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+cc",
+ "-cc/blink",
+ "-cc/test",
+
+ "+components/viz/client",
+ "+mojo/public/cpp/bindings",
+ "+mojo/public/cpp/system",
+ "+ui/gfx/geometry",
+]
diff --git a/chromium/components/viz/client/client_layer_tree_frame_sink.cc b/chromium/components/viz/client/client_layer_tree_frame_sink.cc
new file mode 100644
index 00000000000..4425d5f5758
--- /dev/null
+++ b/chromium/components/viz/client/client_layer_tree_frame_sink.cc
@@ -0,0 +1,167 @@
+// 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 "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "cc/output/begin_frame_args.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/layer_tree_frame_sink_client.h"
+#include "components/viz/client/local_surface_id_provider.h"
+#include "components/viz/common/resources/shared_bitmap_manager.h"
+
+namespace viz {
+
+ClientLayerTreeFrameSink::ClientLayerTreeFrameSink(
+ scoped_refptr<ContextProvider> context_provider,
+ scoped_refptr<ContextProvider> worker_context_provider,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ SharedBitmapManager* shared_bitmap_manager,
+ std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source,
+ cc::mojom::CompositorFrameSinkPtrInfo compositor_frame_sink_info,
+ cc::mojom::CompositorFrameSinkClientRequest client_request,
+ std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
+ bool enable_surface_synchronization)
+ : cc::LayerTreeFrameSink(std::move(context_provider),
+ std::move(worker_context_provider),
+ gpu_memory_buffer_manager,
+ shared_bitmap_manager),
+ local_surface_id_provider_(std::move(local_surface_id_provider)),
+ synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)),
+ compositor_frame_sink_info_(std::move(compositor_frame_sink_info)),
+ client_request_(std::move(client_request)),
+ client_binding_(this),
+ enable_surface_synchronization_(enable_surface_synchronization),
+ weak_factory_(this) {
+ DETACH_FROM_THREAD(thread_checker_);
+}
+
+ClientLayerTreeFrameSink::ClientLayerTreeFrameSink(
+ scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider,
+ std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source,
+ cc::mojom::CompositorFrameSinkPtrInfo compositor_frame_sink_info,
+ cc::mojom::CompositorFrameSinkClientRequest client_request,
+ std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
+ bool enable_surface_synchronization)
+ : cc::LayerTreeFrameSink(std::move(vulkan_context_provider)),
+ local_surface_id_provider_(std::move(local_surface_id_provider)),
+ synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)),
+ compositor_frame_sink_info_(std::move(compositor_frame_sink_info)),
+ client_request_(std::move(client_request)),
+ client_binding_(this),
+ enable_surface_synchronization_(enable_surface_synchronization),
+ weak_factory_(this) {
+ DETACH_FROM_THREAD(thread_checker_);
+}
+
+ClientLayerTreeFrameSink::~ClientLayerTreeFrameSink() {}
+
+base::WeakPtr<ClientLayerTreeFrameSink> ClientLayerTreeFrameSink::GetWeakPtr() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ return weak_factory_.GetWeakPtr();
+}
+
+bool ClientLayerTreeFrameSink::BindToClient(
+ cc::LayerTreeFrameSinkClient* client) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!cc::LayerTreeFrameSink::BindToClient(client))
+ return false;
+
+ compositor_frame_sink_.Bind(std::move(compositor_frame_sink_info_));
+ compositor_frame_sink_.set_connection_error_with_reason_handler(
+ base::Bind(ClientLayerTreeFrameSink::OnMojoConnectionError));
+ client_binding_.Bind(std::move(client_request_));
+
+ if (synthetic_begin_frame_source_) {
+ client->SetBeginFrameSource(synthetic_begin_frame_source_.get());
+ } else {
+ begin_frame_source_ = base::MakeUnique<cc::ExternalBeginFrameSource>(this);
+ begin_frame_source_->OnSetBeginFrameSourcePaused(begin_frames_paused_);
+ client->SetBeginFrameSource(begin_frame_source_.get());
+ }
+
+ 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();
+ 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(
+ cc::CompositorFrame frame) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(frame.metadata.begin_frame_ack.has_damage);
+ DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber,
+ frame.metadata.begin_frame_ack.sequence_number);
+
+ if (!enable_surface_synchronization_) {
+ local_surface_id_ =
+ local_surface_id_provider_->GetLocalSurfaceIdForFrame(frame);
+ }
+
+ compositor_frame_sink_->SubmitCompositorFrame(local_surface_id_,
+ std::move(frame));
+}
+
+void ClientLayerTreeFrameSink::DidNotProduceFrame(
+ const cc::BeginFrameAck& ack) {
+ DCHECK(!ack.has_damage);
+ DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber, ack.sequence_number);
+ compositor_frame_sink_->DidNotProduceFrame(ack);
+}
+
+void ClientLayerTreeFrameSink::DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ client_->ReclaimResources(resources);
+ client_->DidReceiveCompositorFrameAck();
+}
+
+void ClientLayerTreeFrameSink::OnBeginFrame(
+ const cc::BeginFrameArgs& begin_frame_args) {
+ if (begin_frame_source_)
+ begin_frame_source_->OnBeginFrame(begin_frame_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<cc::ReturnedResource>& resources) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ client_->ReclaimResources(resources);
+}
+
+void ClientLayerTreeFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) {
+ compositor_frame_sink_->SetNeedsBeginFrame(needs_begin_frames);
+}
+
+// static
+void ClientLayerTreeFrameSink::OnMojoConnectionError(
+ uint32_t custom_reason,
+ const std::string& description) {
+ if (custom_reason)
+ DLOG(FATAL) << description;
+}
+
+} // 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
new file mode 100644
index 00000000000..b2893ccf828
--- /dev/null
+++ b/chromium/components/viz/client/client_layer_tree_frame_sink.h
@@ -0,0 +1,93 @@
+// 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 "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/ipc/compositor_frame_sink.mojom.h"
+#include "cc/output/layer_tree_frame_sink.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace viz {
+
+class LocalSurfaceIdProvider;
+class SharedBitmapManager;
+
+class ClientLayerTreeFrameSink : public cc::LayerTreeFrameSink,
+ public cc::mojom::CompositorFrameSinkClient,
+ public cc::ExternalBeginFrameSourceClient {
+ public:
+ ClientLayerTreeFrameSink(
+ scoped_refptr<ContextProvider> context_provider,
+ scoped_refptr<ContextProvider> worker_context_provider,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ SharedBitmapManager* shared_bitmap_manager,
+ std::unique_ptr<cc::SyntheticBeginFrameSource>
+ synthetic_begin_frame_source,
+ cc::mojom::CompositorFrameSinkPtrInfo compositor_frame_sink_info,
+ cc::mojom::CompositorFrameSinkClientRequest client_request,
+ std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
+ bool enable_surface_synchronization);
+
+ ClientLayerTreeFrameSink(
+ scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider,
+ std::unique_ptr<cc::SyntheticBeginFrameSource>
+ synthetic_begin_frame_source,
+ cc::mojom::CompositorFrameSinkPtrInfo compositor_frame_sink_info,
+ cc::mojom::CompositorFrameSinkClientRequest client_request,
+ std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
+ bool enable_surface_synchronization);
+
+ ~ClientLayerTreeFrameSink() override;
+
+ base::WeakPtr<ClientLayerTreeFrameSink> GetWeakPtr();
+
+ // cc::LayerTreeFrameSink implementation.
+ bool BindToClient(cc::LayerTreeFrameSinkClient* client) override;
+ void DetachFromClient() override;
+ void SetLocalSurfaceId(const LocalSurfaceId& local_surface_id) override;
+ void SubmitCompositorFrame(cc::CompositorFrame frame) override;
+ void DidNotProduceFrame(const cc::BeginFrameAck& ack) override;
+
+ private:
+ // cc::mojom::CompositorFrameSinkClient implementation:
+ void DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void OnBeginFrame(const cc::BeginFrameArgs& begin_frame_args) override;
+ void OnBeginFramePausedChanged(bool paused) override;
+ void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) override;
+
+ // cc::ExternalBeginFrameSourceClient implementation.
+ void OnNeedsBeginFrames(bool needs_begin_frames) override;
+
+ static void OnMojoConnectionError(uint32_t custom_reason,
+ const std::string& description);
+
+ bool begin_frames_paused_ = false;
+ LocalSurfaceId local_surface_id_;
+ std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider_;
+ std::unique_ptr<cc::ExternalBeginFrameSource> begin_frame_source_;
+ std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source_;
+ cc::mojom::CompositorFrameSinkPtrInfo compositor_frame_sink_info_;
+ cc::mojom::CompositorFrameSinkClientRequest client_request_;
+ cc::mojom::CompositorFrameSinkPtr compositor_frame_sink_;
+ mojo::Binding<cc::mojom::CompositorFrameSinkClient> client_binding_;
+ THREAD_CHECKER(thread_checker_);
+ const bool enable_surface_synchronization_;
+
+ 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_shared_bitmap_manager.cc b/chromium/components/viz/client/client_shared_bitmap_manager.cc
new file mode 100644
index 00000000000..8c1d51a6819
--- /dev/null
+++ b/chromium/components/viz/client/client_shared_bitmap_manager.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/client/client_shared_bitmap_manager.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/debug/alias.h"
+#include "base/memory/ptr_util.h"
+#include "base/process/memory.h"
+#include "base/process/process_metrics.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace viz {
+
+namespace {
+
+class ClientSharedBitmap : public SharedBitmap {
+ public:
+ ClientSharedBitmap(
+ scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+ shared_bitmap_allocation_notifier,
+ base::SharedMemory* shared_memory,
+ const SharedBitmapId& id,
+ uint32_t sequence_number)
+ : SharedBitmap(static_cast<uint8_t*>(shared_memory->memory()),
+ id,
+ sequence_number),
+ shared_bitmap_allocation_notifier_(
+ std::move(shared_bitmap_allocation_notifier)) {}
+
+ ClientSharedBitmap(
+ scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+ shared_bitmap_allocation_notifier,
+ std::unique_ptr<base::SharedMemory> shared_memory_holder,
+ const SharedBitmapId& id,
+ uint32_t sequence_number)
+ : ClientSharedBitmap(std::move(shared_bitmap_allocation_notifier),
+ shared_memory_holder.get(),
+ id,
+ sequence_number) {
+ shared_memory_holder_ = std::move(shared_memory_holder);
+ }
+
+ ~ClientSharedBitmap() override {
+ (*shared_bitmap_allocation_notifier_)->DidDeleteSharedBitmap(id());
+ }
+
+ // SharedBitmap:
+ base::SharedMemoryHandle GetSharedMemoryHandle() const override {
+ if (!shared_memory_holder_)
+ return base::SharedMemoryHandle();
+ return shared_memory_holder_->handle();
+ }
+
+ private:
+ scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+ shared_bitmap_allocation_notifier_;
+ std::unique_ptr<base::SharedMemory> shared_memory_holder_;
+};
+
+// Collect extra information for debugging bitmap creation failures.
+void CollectMemoryUsageAndDie(const gfx::Size& size, size_t alloc_size) {
+#if defined(OS_WIN)
+ int width = size.width();
+ int height = size.height();
+ DWORD last_error = GetLastError();
+
+ std::unique_ptr<base::ProcessMetrics> metrics(
+ base::ProcessMetrics::CreateProcessMetrics(
+ base::GetCurrentProcessHandle()));
+
+ size_t private_bytes = 0;
+ size_t shared_bytes = 0;
+ metrics->GetMemoryBytes(&private_bytes, &shared_bytes);
+
+ base::debug::Alias(&width);
+ base::debug::Alias(&height);
+ base::debug::Alias(&last_error);
+ base::debug::Alias(&private_bytes);
+ base::debug::Alias(&shared_bytes);
+#endif
+ base::TerminateBecauseOutOfMemory(alloc_size);
+}
+
+// Allocates a block of shared memory of the given size. Returns nullptr on
+// failure.
+std::unique_ptr<base::SharedMemory> AllocateSharedMemory(size_t buf_size) {
+ mojo::ScopedSharedBufferHandle mojo_buf =
+ mojo::SharedBufferHandle::Create(buf_size);
+ if (!mojo_buf->is_valid()) {
+ LOG(WARNING) << "Browser failed to allocate shared memory";
+ return nullptr;
+ }
+
+ base::SharedMemoryHandle shared_buf;
+ if (mojo::UnwrapSharedMemoryHandle(std::move(mojo_buf), &shared_buf, nullptr,
+ nullptr) != MOJO_RESULT_OK) {
+ LOG(WARNING) << "Browser failed to allocate shared memory";
+ return nullptr;
+ }
+
+ return base::MakeUnique<base::SharedMemory>(shared_buf, false);
+}
+
+} // namespace
+
+ClientSharedBitmapManager::ClientSharedBitmapManager(
+ scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+ shared_bitmap_allocation_notifier)
+ : shared_bitmap_allocation_notifier_(
+ std::move(shared_bitmap_allocation_notifier)) {}
+
+ClientSharedBitmapManager::~ClientSharedBitmapManager() {}
+
+std::unique_ptr<SharedBitmap> ClientSharedBitmapManager::AllocateSharedBitmap(
+ const gfx::Size& size) {
+ TRACE_EVENT2("renderer", "ClientSharedBitmapManager::AllocateSharedBitmap",
+ "width", size.width(), "height", size.height());
+ size_t memory_size;
+ if (!SharedBitmap::SizeInBytes(size, &memory_size))
+ return nullptr;
+ SharedBitmapId id = SharedBitmap::GenerateId();
+ std::unique_ptr<base::SharedMemory> memory =
+ AllocateSharedMemory(memory_size);
+ if (!memory || !memory->Map(memory_size))
+ CollectMemoryUsageAndDie(size, memory_size);
+
+ uint32_t sequence_number = NotifyAllocatedSharedBitmap(memory.get(), id);
+
+ // Close the associated FD to save resources, the previously mapped memory
+ // remains available.
+ memory->Close();
+
+ return base::MakeUnique<ClientSharedBitmap>(
+ shared_bitmap_allocation_notifier_, std::move(memory), id,
+ sequence_number);
+}
+
+std::unique_ptr<SharedBitmap> ClientSharedBitmapManager::GetSharedBitmapFromId(
+ const gfx::Size&,
+ const SharedBitmapId&) {
+ NOTREACHED();
+ return nullptr;
+}
+
+std::unique_ptr<SharedBitmap>
+ClientSharedBitmapManager::GetBitmapForSharedMemory(base::SharedMemory* mem) {
+ SharedBitmapId id = SharedBitmap::GenerateId();
+ uint32_t sequence_number = NotifyAllocatedSharedBitmap(mem, id);
+ return base::MakeUnique<ClientSharedBitmap>(
+ shared_bitmap_allocation_notifier_, mem, id, sequence_number);
+}
+
+// Notifies the browser process that a shared bitmap with the given ID was
+// allocated. Caller keeps ownership of |memory|.
+uint32_t ClientSharedBitmapManager::NotifyAllocatedSharedBitmap(
+ base::SharedMemory* memory,
+ const SharedBitmapId& id) {
+ base::SharedMemoryHandle handle_to_send =
+ base::SharedMemory::DuplicateHandle(memory->handle());
+ if (!base::SharedMemory::IsHandleValid(handle_to_send)) {
+ LOG(ERROR) << "Failed to duplicate shared memory handle for bitmap.";
+ return 0;
+ }
+
+ mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
+ handle_to_send, memory->mapped_size(), true /* read_only */);
+
+ {
+ base::AutoLock lock(lock_);
+ (*shared_bitmap_allocation_notifier_)
+ ->DidAllocateSharedBitmap(std::move(buffer_handle), id);
+ return ++last_sequence_number_;
+ }
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/client/client_shared_bitmap_manager.h b/chromium/components/viz/client/client_shared_bitmap_manager.h
new file mode 100644
index 00000000000..8bb479cbed1
--- /dev/null
+++ b/chromium/components/viz/client/client_shared_bitmap_manager.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_CLIENT_CLIENT_SHARED_BITMAP_MANAGER_H_
+#define COMPONENTS_VIZ_CLIENT_CLIENT_SHARED_BITMAP_MANAGER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
+#include "base/synchronization/lock.h"
+#include "cc/ipc/shared_bitmap_allocation_notifier.mojom.h"
+#include "components/viz/common/resources/shared_bitmap_manager.h"
+#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
+
+namespace viz {
+
+// A SharedBitmapManager implementation for use outside of the display
+// compositor's process. This implementation supports SharedBitmaps that
+// can be transported over process boundaries to the display compositor.
+class ClientSharedBitmapManager : public SharedBitmapManager {
+ public:
+ explicit ClientSharedBitmapManager(
+ scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+ shared_bitmap_allocation_notifier);
+ ~ClientSharedBitmapManager() override;
+
+ // SharedBitmapManager implementation.
+ std::unique_ptr<SharedBitmap> AllocateSharedBitmap(
+ const gfx::Size& size) override;
+ std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
+ const gfx::Size&,
+ const SharedBitmapId&) override;
+
+ std::unique_ptr<SharedBitmap> GetBitmapForSharedMemory(
+ base::SharedMemory* mem);
+
+ private:
+ uint32_t NotifyAllocatedSharedBitmap(base::SharedMemory* memory,
+ const SharedBitmapId& id);
+
+ scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+ shared_bitmap_allocation_notifier_;
+
+ base::Lock lock_;
+
+ // Each SharedBitmap allocated by this class is assigned a unique sequence
+ // number that is incremental.
+ uint32_t last_sequence_number_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientSharedBitmapManager);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_CLIENT_CLIENT_SHARED_BITMAP_MANAGER_H_
diff --git a/chromium/components/viz/client/local_surface_id_provider.cc b/chromium/components/viz/client/local_surface_id_provider.cc
new file mode 100644
index 00000000000..90773f302ff
--- /dev/null
+++ b/chromium/components/viz/client/local_surface_id_provider.cc
@@ -0,0 +1,28 @@
+// 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/local_surface_id_provider.h"
+#include "cc/output/compositor_frame.h"
+
+namespace viz {
+
+LocalSurfaceIdProvider::LocalSurfaceIdProvider() = default;
+
+LocalSurfaceIdProvider::~LocalSurfaceIdProvider() = default;
+
+DefaultLocalSurfaceIdProvider::DefaultLocalSurfaceIdProvider() = default;
+
+const LocalSurfaceId& DefaultLocalSurfaceIdProvider::GetLocalSurfaceIdForFrame(
+ const cc::CompositorFrame& frame) {
+ gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size();
+ if (!local_surface_id_.is_valid() || surface_size_ != frame_size ||
+ frame.metadata.device_scale_factor != device_scale_factor_) {
+ local_surface_id_ = local_surface_id_allocator_.GenerateId();
+ }
+ surface_size_ = frame_size;
+ device_scale_factor_ = frame.metadata.device_scale_factor;
+ return local_surface_id_;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/client/local_surface_id_provider.h b/chromium/components/viz/client/local_surface_id_provider.h
new file mode 100644
index 00000000000..4d1ea050dc2
--- /dev/null
+++ b/chromium/components/viz/client/local_surface_id_provider.h
@@ -0,0 +1,48 @@
+// 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_LOCAL_SURFACE_ID_PROVIDER_H_
+#define COMPONENTS_VIZ_CLIENT_LOCAL_SURFACE_ID_PROVIDER_H_
+
+#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+class CompositorFrame;
+}
+
+namespace viz {
+
+class LocalSurfaceIdProvider {
+ public:
+ LocalSurfaceIdProvider();
+ virtual ~LocalSurfaceIdProvider();
+
+ virtual const LocalSurfaceId& GetLocalSurfaceIdForFrame(
+ const cc::CompositorFrame& frame) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LocalSurfaceIdProvider);
+};
+
+class DefaultLocalSurfaceIdProvider : public LocalSurfaceIdProvider {
+ public:
+ DefaultLocalSurfaceIdProvider();
+
+ const LocalSurfaceId& GetLocalSurfaceIdForFrame(
+ const cc::CompositorFrame& frame) override;
+
+ private:
+ LocalSurfaceId local_surface_id_;
+ gfx::Size surface_size_;
+ float device_scale_factor_ = 0;
+ LocalSurfaceIdAllocator local_surface_id_allocator_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultLocalSurfaceIdProvider);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_CLIENT_LOCAL_SURFACE_ID_PROVIDER_H_
diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn
new file mode 100644
index 00000000000..84f40f15662
--- /dev/null
+++ b/chromium/components/viz/common/BUILD.gn
@@ -0,0 +1,134 @@
+# 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.
+
+import("//components/viz/viz.gni")
+import("//testing/test.gni")
+
+viz_component("common") {
+ output_name = "viz_common"
+
+ defines = [ "VIZ_COMMON_IMPLEMENTATION" ]
+
+ sources = [
+ "display/renderer_settings.cc",
+ "display/renderer_settings.h",
+ "gl_helper.cc",
+ "gl_helper.h",
+ "gl_helper_readback_support.cc",
+ "gl_helper_readback_support.h",
+ "gl_helper_scaling.cc",
+ "gl_helper_scaling.h",
+ "gpu/context_cache_controller.cc",
+ "gpu/context_cache_controller.h",
+ "gpu/context_provider.cc",
+ "gpu/context_provider.h",
+ "gpu/in_process_context_provider.cc",
+ "gpu/in_process_context_provider.h",
+ "hit_test/aggregated_hit_test_region.h",
+ "quads/resource_format.h",
+ "quads/shared_bitmap.cc",
+ "quads/shared_bitmap.h",
+ "quads/texture_mailbox.cc",
+ "quads/texture_mailbox.h",
+ "resources/buffer_to_texture_target_map.cc",
+ "resources/buffer_to_texture_target_map.h",
+ "resources/platform_color.h",
+ "resources/resource_format_utils.cc",
+ "resources/resource_format_utils.h",
+ "resources/resource_settings.cc",
+ "resources/resource_settings.h",
+ "resources/shared_bitmap_manager.h",
+ "surfaces/frame_sink_id.cc",
+ "surfaces/frame_sink_id.h",
+ "surfaces/frame_sink_id_allocator.h",
+ "surfaces/local_surface_id.cc",
+ "surfaces/local_surface_id.h",
+ "surfaces/local_surface_id_allocator.cc",
+ "surfaces/local_surface_id_allocator.h",
+ "surfaces/sequence_surface_reference_factory.cc",
+ "surfaces/sequence_surface_reference_factory.h",
+ "surfaces/surface_id.cc",
+ "surfaces/surface_id.h",
+ "surfaces/surface_info.h",
+ "surfaces/surface_reference_factory.h",
+ "surfaces/surface_reference_owner.h",
+ "surfaces/surface_sequence.h",
+ "surfaces/surface_sequence_generator.cc",
+ "surfaces/surface_sequence_generator.h",
+ "viz_common_export.h",
+ ]
+
+ deps = [
+ "//base",
+ "//gpu",
+ "//gpu/command_buffer/client:gles2_implementation",
+ "//gpu/command_buffer/client:gles2_interface",
+ "//gpu/command_buffer/service",
+ "//gpu/ipc:gl_in_process_context",
+ "//gpu/skia_bindings:skia_bindings",
+ "//mojo/public/cpp/bindings",
+ "//skia",
+ "//ui/gfx:color_space",
+ "//ui/gfx:geometry_skia",
+ "//ui/gfx/geometry",
+ ]
+
+ public_deps = [
+ "//gpu/command_buffer/client",
+ "//gpu/command_buffer/common",
+ "//mojo/public/cpp/bindings",
+ ]
+}
+
+viz_source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "gl_helper_unittest.cc",
+ "resources/buffer_to_texture_target_map_unittest.cc",
+ "resources/platform_color_unittest.cc",
+ "surfaces/surface_sequence_generator_unittest.cc",
+ "yuv_readback_unittest.cc",
+ ]
+
+ deps = [
+ ":common",
+ "//base/test:test_support",
+ "//gpu/command_buffer/client:gles2_implementation",
+ "//gpu/command_buffer/client:gles2_interface",
+ "//gpu/ipc:gl_in_process_context",
+ "//gpu/ipc/common:surface_handle_type",
+ "//media",
+ "//testing/gtest",
+ ]
+}
+
+# Microbenchmark to measure performance of GLHelper code, for use in
+# debugging, profiling, and optimizing.
+viz_test("viz_benchmark") {
+ sources = [
+ "gl_helper_benchmark.cc",
+ ]
+
+ configs = [
+ "//build/config/compiler:no_size_t_to_int_warning",
+ "//third_party/khronos:khronos_headers",
+ ]
+
+ deps = [
+ ":common",
+ "//base",
+ "//base/test:test_support",
+ "//components/test:run_all_unittests",
+ "//gpu/command_buffer/client",
+ "//gpu/command_buffer/client:gles2_implementation",
+ "//gpu/ipc:gl_in_process_context",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//ui/gfx",
+ ]
+
+ data_deps = [
+ "//third_party/mesa:osmesa",
+ ]
+}
diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS
new file mode 100644
index 00000000000..4efdc7698ad
--- /dev/null
+++ b/chromium/components/viz/common/DEPS
@@ -0,0 +1,24 @@
+specific_include_rules = {
+ # DEPS for GLHelper and friends which are in the root common/ directory.
+ "(yuv_readback|gl_helper).*\.(cc|h)": [
+ "+gpu/GLES2",
+ "+gpu/command_buffer/client",
+ "+gpu/command_buffer/common",
+ "+gpu/command_buffer/service",
+ "+gpu/ipc/common",
+ "+mojo/public/cpp/bindings",
+ "+ui/gfx/geometry",
+ "+services/ui/gpu/interfaces",
+ "+third_party/skia",
+ ],
+ ".*_unittest\.cc": [
+ "+gpu/ipc/gl_in_process_context.h",
+ "+media/base",
+ "+ui/gfx",
+ "+ui/gl",
+ ],
+ ".*_benchmark\.cc": [
+ "+gpu/ipc/gl_in_process_context.h",
+ "+ui/gfx",
+ ],
+}
diff --git a/chromium/components/viz/common/README.md b/chromium/components/viz/common/README.md
new file mode 100644
index 00000000000..d2e8fd6f3d9
--- /dev/null
+++ b/chromium/components/viz/common/README.md
@@ -0,0 +1,4 @@
+This directory contains common code used by implementations of client/, host/,
+and/or service/. It also contains common code used by the users of these
+components (i.e. code outside of //components/viz that use code in client/,
+host/, and/or service/ can also use this code).
diff --git a/chromium/components/viz/common/display/renderer_settings.cc b/chromium/components/viz/common/display/renderer_settings.cc
new file mode 100644
index 00000000000..643e6fdd23d
--- /dev/null
+++ b/chromium/components/viz/common/display/renderer_settings.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/display/renderer_settings.h"
+
+namespace viz {
+
+RendererSettings::RendererSettings() = default;
+
+RendererSettings::RendererSettings(const RendererSettings& other) = default;
+
+RendererSettings::~RendererSettings() = default;
+
+} // namespace viz
diff --git a/chromium/components/viz/common/display/renderer_settings.h b/chromium/components/viz/common/display/renderer_settings.h
new file mode 100644
index 00000000000..c7fb57c2540
--- /dev/null
+++ b/chromium/components/viz/common/display/renderer_settings.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_DISPLAY_RENDERER_SETTINGS_H_
+#define COMPONENTS_VIZ_COMMON_DISPLAY_RENDERER_SETTINGS_H_
+
+#include <stddef.h>
+
+#include "components/viz/common/resources/resource_settings.h"
+#include "components/viz/common/viz_common_export.h"
+
+namespace viz {
+
+class VIZ_COMMON_EXPORT RendererSettings {
+ public:
+ RendererSettings();
+ RendererSettings(const RendererSettings& other);
+ ~RendererSettings();
+
+ ResourceSettings resource_settings;
+ bool allow_antialiasing = true;
+ bool force_antialiasing = false;
+ bool force_blending_with_shaders = false;
+ bool partial_swap_enabled = false;
+ bool finish_rendering_on_resize = false;
+ bool should_clear_root_render_pass = true;
+ bool release_overlay_resources_after_gpu_query = false;
+ bool gl_composited_overlay_candidate_quad_border = false;
+ bool show_overdraw_feedback = false;
+ bool enable_color_correct_rendering = false;
+ int highp_threshold_min = 0;
+
+ // Determines whether we disallow non-exact matches when finding resources
+ // in ResourcePool. Only used for layout or pixel tests, as non-deterministic
+ // resource sizes can lead to floating point error and noise in these tests.
+ bool disallow_non_exact_resource_reuse = false;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_DISPLAY_RENDERER_SETTINGS_H_
diff --git a/chromium/components/viz/common/gl_helper.cc b/chromium/components/viz/common/gl_helper.cc
new file mode 100644
index 00000000000..003db7b762e
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper.cc
@@ -0,0 +1,1237 @@
+// 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/viz/common/gl_helper.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <queue>
+#include <string>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "components/viz/common/gl_helper_readback_support.h"
+#include "components/viz/common/gl_helper_scaling.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/context_support.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+using gpu::gles2::GLES2Interface;
+
+namespace {
+
+class ScopedFlush {
+ public:
+ explicit ScopedFlush(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
+
+ ~ScopedFlush() { gl_->Flush(); }
+
+ private:
+ gpu::gles2::GLES2Interface* gl_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFlush);
+};
+
+// Helper class for allocating and holding an RGBA texture of a given
+// size and an associated framebuffer.
+class TextureFrameBufferPair {
+ public:
+ TextureFrameBufferPair(GLES2Interface* gl, gfx::Size size)
+ : texture_(gl), framebuffer_(gl), size_(size) {
+ viz::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ viz::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
+ gl, framebuffer_);
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture_, 0);
+ }
+
+ GLuint texture() const { return texture_.id(); }
+ GLuint framebuffer() const { return framebuffer_.id(); }
+ gfx::Size size() const { return size_; }
+
+ private:
+ viz::ScopedTexture texture_;
+ viz::ScopedFramebuffer framebuffer_;
+ gfx::Size size_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair);
+};
+
+// Helper class for holding a scaler, a texture for the output of that
+// scaler and an associated frame buffer. This is inteded to be used
+// when the output of a scaler is to be sent to a readback.
+class ScalerHolder {
+ public:
+ ScalerHolder(GLES2Interface* gl, viz::GLHelper::ScalerInterface* scaler)
+ : texture_and_framebuffer_(gl, scaler->DstSize()), scaler_(scaler) {}
+
+ void Scale(GLuint src_texture) {
+ scaler_->Scale(src_texture, texture_and_framebuffer_.texture());
+ }
+
+ viz::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); }
+ TextureFrameBufferPair* texture_and_framebuffer() {
+ return &texture_and_framebuffer_;
+ }
+ GLuint texture() const { return texture_and_framebuffer_.texture(); }
+
+ private:
+ TextureFrameBufferPair texture_and_framebuffer_;
+ std::unique_ptr<viz::GLHelper::ScalerInterface> scaler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScalerHolder);
+};
+
+} // namespace
+
+namespace viz {
+typedef GLHelperReadbackSupport::FormatSupport FormatSupport;
+
+// Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates
+// the data needed for it.
+class GLHelper::CopyTextureToImpl
+ : public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> {
+ public:
+ CopyTextureToImpl(GLES2Interface* gl,
+ gpu::ContextSupport* context_support,
+ GLHelper* helper)
+ : gl_(gl),
+ context_support_(context_support),
+ helper_(helper),
+ flush_(gl),
+ max_draw_buffers_(0) {
+ const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS);
+ if (!extensions)
+ return;
+ std::string extensions_string =
+ " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
+ if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) {
+ gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
+ }
+ }
+ ~CopyTextureToImpl() { CancelRequests(); }
+
+ GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token) {
+ return helper_->ConsumeMailboxToTexture(mailbox, sync_token);
+ }
+
+ void CropScaleReadbackAndCleanTexture(
+ GLuint src_texture,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ const SkColorType out_color_type,
+ const base::Callback<void(bool)>& callback,
+ GLHelper::ScalerQuality quality);
+
+ void ReadbackTextureSync(GLuint texture,
+ const gfx::Rect& src_rect,
+ unsigned char* out,
+ SkColorType format);
+
+ void ReadbackTextureAsync(GLuint texture,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ SkColorType color_type,
+ const base::Callback<void(bool)>& callback);
+
+ // Reads back bytes from the currently bound frame buffer.
+ // Note that dst_size is specified in bytes, not pixels.
+ void ReadbackAsync(const gfx::Size& dst_size,
+ size_t bytes_per_row, // generally dst_size.width() * 4
+ size_t row_stride_bytes, // generally dst_size.width() * 4
+ unsigned char* out,
+ GLenum format,
+ GLenum type,
+ size_t bytes_per_pixel,
+ const base::Callback<void(bool)>& callback);
+
+ void ReadbackPlane(TextureFrameBufferPair* source,
+ int row_stride_bytes,
+ unsigned char* data,
+ int size_shift,
+ const gfx::Rect& paste_rect,
+ ReadbackSwizzle swizzle,
+ const base::Callback<void(bool)>& callback);
+
+ GLuint CopyAndScaleTexture(GLuint texture,
+ const gfx::Size& src_size,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ GLHelper::ScalerQuality quality);
+
+ ReadbackYUVInterface* CreateReadbackPipelineYUV(
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ bool use_mrt);
+
+ // Returns the maximum number of draw buffers available,
+ // 0 if GL_EXT_draw_buffers is not available.
+ GLint MaxDrawBuffers() const { return max_draw_buffers_; }
+
+ FormatSupport GetReadbackConfig(SkColorType color_type,
+ bool can_swizzle,
+ GLenum* format,
+ GLenum* type,
+ size_t* bytes_per_pixel);
+
+ private:
+ // A single request to CropScaleReadbackAndCleanTexture.
+ // The main thread can cancel the request, before it's handled by the helper
+ // thread, by resetting the texture and pixels fields. Alternatively, the
+ // thread marks that it handles the request by resetting the pixels field
+ // (meaning it guarantees that the callback with be called).
+ // In either case, the callback must be called exactly once, and the texture
+ // must be deleted by the main thread gl.
+ struct Request {
+ Request(const gfx::Size& size_,
+ size_t bytes_per_row_,
+ size_t row_stride_bytes_,
+ unsigned char* pixels_,
+ const base::Callback<void(bool)>& callback_)
+ : done(false),
+ size(size_),
+ bytes_per_row(bytes_per_row_),
+ row_stride_bytes(row_stride_bytes_),
+ pixels(pixels_),
+ callback(callback_),
+ buffer(0),
+ query(0) {}
+
+ bool done;
+ bool result;
+ gfx::Size size;
+ size_t bytes_per_row;
+ size_t row_stride_bytes;
+ unsigned char* pixels;
+ base::Callback<void(bool)> callback;
+ GLuint buffer;
+ GLuint query;
+ };
+
+ // We must take care to call the callbacks last, as they may
+ // end up destroying the gl_helper and make *this invalid.
+ // We stick the finished requests in a stack object that calls
+ // the callbacks when it goes out of scope.
+ class FinishRequestHelper {
+ public:
+ FinishRequestHelper() {}
+ ~FinishRequestHelper() {
+ while (!requests_.empty()) {
+ Request* request = requests_.front();
+ requests_.pop();
+ request->callback.Run(request->result);
+ delete request;
+ }
+ }
+ void Add(Request* r) { requests_.push(r); }
+
+ private:
+ std::queue<Request*> requests_;
+ DISALLOW_COPY_AND_ASSIGN(FinishRequestHelper);
+ };
+
+ // A readback pipeline that also converts the data to YUV before
+ // reading it back.
+ class ReadbackYUVImpl : public ReadbackYUVInterface {
+ public:
+ ReadbackYUVImpl(GLES2Interface* gl,
+ CopyTextureToImpl* copy_impl,
+ GLHelperScaling* scaler_impl,
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ ReadbackSwizzle swizzle);
+
+ void ReadbackYUV(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ const gfx::Rect& target_visible_rect,
+ int y_plane_row_stride_bytes,
+ unsigned char* y_plane_data,
+ int u_plane_row_stride_bytes,
+ unsigned char* u_plane_data,
+ int v_plane_row_stride_bytes,
+ unsigned char* v_plane_data,
+ const gfx::Point& paste_location,
+ const base::Callback<void(bool)>& callback) override;
+
+ ScalerInterface* scaler() override { return scaler_.scaler(); }
+
+ private:
+ GLES2Interface* gl_;
+ CopyTextureToImpl* copy_impl_;
+ gfx::Size dst_size_;
+ ReadbackSwizzle swizzle_;
+ ScalerHolder scaler_;
+ ScalerHolder y_;
+ ScalerHolder u_;
+ ScalerHolder v_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl);
+ };
+
+ // A readback pipeline that also converts the data to YUV before
+ // reading it back. This one uses Multiple Render Targets, which
+ // may not be supported on all platforms.
+ class ReadbackYUV_MRT : public ReadbackYUVInterface {
+ public:
+ ReadbackYUV_MRT(GLES2Interface* gl,
+ CopyTextureToImpl* copy_impl,
+ GLHelperScaling* scaler_impl,
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ ReadbackSwizzle swizzle);
+
+ void ReadbackYUV(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ const gfx::Rect& target_visible_rect,
+ int y_plane_row_stride_bytes,
+ unsigned char* y_plane_data,
+ int u_plane_row_stride_bytes,
+ unsigned char* u_plane_data,
+ int v_plane_row_stride_bytes,
+ unsigned char* v_plane_data,
+ const gfx::Point& paste_location,
+ const base::Callback<void(bool)>& callback) override;
+
+ ScalerInterface* scaler() override { return scaler_.scaler(); }
+
+ private:
+ GLES2Interface* gl_;
+ CopyTextureToImpl* copy_impl_;
+ gfx::Size dst_size_;
+ GLHelper::ScalerQuality quality_;
+ ReadbackSwizzle swizzle_;
+ ScalerHolder scaler_;
+ std::unique_ptr<GLHelperScaling::ShaderInterface> pass1_shader_;
+ std::unique_ptr<GLHelperScaling::ShaderInterface> pass2_shader_;
+ TextureFrameBufferPair y_;
+ ScopedTexture uv_;
+ TextureFrameBufferPair u_;
+ TextureFrameBufferPair v_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT);
+ };
+
+ // Copies the block of pixels specified with |src_subrect| from |src_texture|,
+ // scales it to |dst_size|, writes it into a texture, and returns its ID.
+ // |src_size| is the size of |src_texture|.
+ GLuint ScaleTexture(GLuint src_texture,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ SkColorType color_type,
+ GLHelper::ScalerQuality quality);
+
+ // Converts each four consecutive pixels of the source texture into one pixel
+ // in the result texture with each pixel channel representing the grayscale
+ // color of one of the four original pixels:
+ // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X1X2X3X4
+ // The resulting texture is still an RGBA texture (which is ~4 times narrower
+ // than the original). If rendered directly, it wouldn't show anything useful,
+ // but the data in it can be used to construct a grayscale image.
+ // |encoded_texture_size| is the exact size of the resulting RGBA texture. It
+ // is equal to src_size.width()/4 rounded upwards. Some channels in the last
+ // pixel ((-src_size.width()) % 4) to be exact) are padding and don't contain
+ // useful data.
+ // If swizzle is set to true, the transformed pixels are reordered:
+ // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X3X2X1X4.
+ GLuint EncodeTextureAsGrayscale(GLuint src_texture,
+ const gfx::Size& src_size,
+ gfx::Size* const encoded_texture_size,
+ bool vertically_flip_texture,
+ bool swizzle);
+
+ static void nullcallback(bool success) {}
+ void ReadbackDone(Request* request, size_t bytes_per_pixel);
+ void FinishRequest(Request* request,
+ bool result,
+ FinishRequestHelper* helper);
+ void CancelRequests();
+
+ static const float kRGBtoYColorWeights[];
+ static const float kRGBtoUColorWeights[];
+ static const float kRGBtoVColorWeights[];
+ static const float kRGBtoGrayscaleColorWeights[];
+
+ GLES2Interface* gl_;
+ gpu::ContextSupport* context_support_;
+ GLHelper* helper_;
+
+ // A scoped flush that will ensure all resource deletions are flushed when
+ // this object is destroyed. Must be declared before other Scoped* fields.
+ ScopedFlush flush_;
+
+ std::queue<Request*> request_queue_;
+ GLint max_draw_buffers_;
+};
+
+GLHelper::ScalerInterface* GLHelper::CreateScaler(ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle) {
+ InitScalerImpl();
+ return scaler_impl_->CreateScaler(quality, src_size, src_subrect, dst_size,
+ vertically_flip_texture, swizzle);
+}
+
+GLuint GLHelper::CopyTextureToImpl::ScaleTexture(
+ GLuint src_texture,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ SkColorType color_type,
+ GLHelper::ScalerQuality quality) {
+ GLuint dst_texture = 0u;
+ gl_->GenTextures(1, &dst_texture);
+ {
+ GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE;
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture);
+
+ // Use GL_RGBA for destination/temporary texture unless we're working with
+ // 16-bit data
+ if (color_type == kRGB_565_SkColorType) {
+ format = GL_RGB;
+ type = GL_UNSIGNED_SHORT_5_6_5;
+ }
+
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, format, dst_size.width(),
+ dst_size.height(), 0, format, type, NULL);
+ }
+ std::unique_ptr<ScalerInterface> scaler(
+ helper_->CreateScaler(quality, src_size, src_subrect, dst_size,
+ vertically_flip_texture, swizzle));
+ scaler->Scale(src_texture, dst_texture);
+ return dst_texture;
+}
+
+GLuint GLHelper::CopyTextureToImpl::EncodeTextureAsGrayscale(
+ GLuint src_texture,
+ const gfx::Size& src_size,
+ gfx::Size* const encoded_texture_size,
+ bool vertically_flip_texture,
+ bool swizzle) {
+ GLuint dst_texture = 0u;
+ gl_->GenTextures(1, &dst_texture);
+ // The size of the encoded texture.
+ *encoded_texture_size =
+ gfx::Size((src_size.width() + 3) / 4, src_size.height());
+ {
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, encoded_texture_size->width(),
+ encoded_texture_size->height(), 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, NULL);
+ }
+
+ helper_->InitScalerImpl();
+ std::unique_ptr<ScalerInterface> grayscale_scaler(
+ helper_->scaler_impl_.get()->CreatePlanarScaler(
+ src_size,
+ gfx::Rect(0, 0, (src_size.width() + 3) & ~3, src_size.height()),
+ *encoded_texture_size, vertically_flip_texture, swizzle,
+ kRGBtoGrayscaleColorWeights));
+ grayscale_scaler->Scale(src_texture, dst_texture);
+ return dst_texture;
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackAsync(
+ const gfx::Size& dst_size,
+ size_t bytes_per_row,
+ size_t row_stride_bytes,
+ unsigned char* out,
+ GLenum format,
+ GLenum type,
+ size_t bytes_per_pixel,
+ const base::Callback<void(bool)>& callback) {
+ TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::ReadbackAsync");
+ Request* request =
+ new Request(dst_size, bytes_per_row, row_stride_bytes, out, callback);
+ request_queue_.push(request);
+ request->buffer = 0u;
+
+ gl_->GenBuffers(1, &request->buffer);
+ gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
+ gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
+ bytes_per_pixel * dst_size.GetArea(), NULL, GL_STREAM_READ);
+
+ request->query = 0u;
+ gl_->GenQueriesEXT(1, &request->query);
+ gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query);
+ gl_->ReadPixels(0, 0, dst_size.width(), dst_size.height(), format, type,
+ NULL);
+ gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
+ gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
+ context_support_->SignalQuery(
+ request->query, base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(),
+ request, bytes_per_pixel));
+}
+
+void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture(
+ GLuint src_texture,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ const SkColorType out_color_type,
+ const base::Callback<void(bool)>& callback,
+ GLHelper::ScalerQuality quality) {
+ GLenum format, type;
+ size_t bytes_per_pixel;
+ SkColorType readback_color_type = out_color_type;
+ // Single-component textures are not supported by all GPUs, so we implement
+ // kAlpha_8_SkColorType support here via a special encoding (see below) using
+ // a 32-bit texture to represent an 8-bit image.
+ // Thus we use generic 32-bit readback in this case.
+ if (out_color_type == kAlpha_8_SkColorType) {
+ readback_color_type = kRGBA_8888_SkColorType;
+ }
+
+ FormatSupport supported = GetReadbackConfig(readback_color_type, true,
+ &format, &type, &bytes_per_pixel);
+
+ if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) {
+ callback.Run(false);
+ return;
+ }
+
+ GLuint texture = src_texture;
+
+ // Scale texture if needed
+ // Optimization: SCALER_QUALITY_FAST is just a single bilinear pass, which we
+ // can do just as well in EncodeTextureAsGrayscale, which we will do if
+ // out_color_type is kAlpha_8_SkColorType, so let's skip the scaling step
+ // in that case.
+ bool scale_texture = out_color_type != kAlpha_8_SkColorType ||
+ quality != GLHelper::SCALER_QUALITY_FAST;
+ if (scale_texture) {
+ // Don't swizzle during the scale step for kAlpha_8_SkColorType.
+ // We will swizzle in the encode step below if needed.
+ bool scale_swizzle = out_color_type == kAlpha_8_SkColorType
+ ? false
+ : supported == GLHelperReadbackSupport::SWIZZLE;
+ texture = ScaleTexture(
+ src_texture, src_size, src_subrect, dst_size, true, scale_swizzle,
+ out_color_type == kAlpha_8_SkColorType ? kN32_SkColorType
+ : out_color_type,
+ quality);
+ DCHECK(texture);
+ }
+
+ gfx::Size readback_texture_size = dst_size;
+ // Encode texture to grayscale if needed.
+ if (out_color_type == kAlpha_8_SkColorType) {
+ // Do the vertical flip here if we haven't already done it when we scaled
+ // the texture.
+ bool encode_as_grayscale_vertical_flip = !scale_texture;
+ // EncodeTextureAsGrayscale by default creates a texture which should be
+ // read back as RGBA, so need to swizzle if the readback format is BGRA.
+ bool encode_as_grayscale_swizzle = format == GL_BGRA_EXT;
+ GLuint tmp_texture = EncodeTextureAsGrayscale(
+ texture, dst_size, &readback_texture_size,
+ encode_as_grayscale_vertical_flip, encode_as_grayscale_swizzle);
+ // If the scaled texture was created - delete it
+ if (scale_texture)
+ gl_->DeleteTextures(1, &texture);
+ texture = tmp_texture;
+ DCHECK(texture);
+ }
+
+ // Readback the pixels of the resulting texture
+ ScopedFramebuffer dst_framebuffer(gl_);
+ ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
+ dst_framebuffer);
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ texture, 0);
+
+ size_t bytes_per_row = out_color_type == kAlpha_8_SkColorType
+ ? dst_size.width()
+ : dst_size.width() * bytes_per_pixel;
+
+ ReadbackAsync(readback_texture_size, bytes_per_row, bytes_per_row, out,
+ format, type, bytes_per_pixel, callback);
+ gl_->DeleteTextures(1, &texture);
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackTextureSync(GLuint texture,
+ const gfx::Rect& src_rect,
+ unsigned char* out,
+ SkColorType color_type) {
+ GLenum format, type;
+ size_t bytes_per_pixel;
+ FormatSupport supported =
+ GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel);
+ if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) {
+ return;
+ }
+
+ ScopedFramebuffer dst_framebuffer(gl_);
+ ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
+ dst_framebuffer);
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ texture, 0);
+ gl_->ReadPixels(src_rect.x(), src_rect.y(), src_rect.width(),
+ src_rect.height(), format, type, out);
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackTextureAsync(
+ GLuint texture,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ SkColorType color_type,
+ const base::Callback<void(bool)>& callback) {
+ GLenum format, type;
+ size_t bytes_per_pixel;
+ FormatSupport supported =
+ GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel);
+ if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) {
+ callback.Run(false);
+ return;
+ }
+
+ ScopedFramebuffer dst_framebuffer(gl_);
+ ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
+ dst_framebuffer);
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ texture, 0);
+ ReadbackAsync(dst_size, dst_size.width() * bytes_per_pixel,
+ dst_size.width() * bytes_per_pixel, out, format, type,
+ bytes_per_pixel, callback);
+}
+
+GLuint GLHelper::CopyTextureToImpl::CopyAndScaleTexture(
+ GLuint src_texture,
+ const gfx::Size& src_size,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ GLHelper::ScalerQuality quality) {
+ return ScaleTexture(src_texture, src_size, gfx::Rect(src_size), dst_size,
+ vertically_flip_texture, false,
+ kRGBA_8888_SkColorType, // GL_RGBA
+ quality);
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request,
+ size_t bytes_per_pixel) {
+ TRACE_EVENT0("gpu.capture",
+ "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete");
+ finished_request->done = true;
+
+ FinishRequestHelper finish_request_helper;
+
+ // We process transfer requests in the order they were received, regardless
+ // of the order we get the callbacks in.
+ while (!request_queue_.empty()) {
+ Request* request = request_queue_.front();
+ if (!request->done) {
+ break;
+ }
+
+ bool result = false;
+ if (request->buffer != 0) {
+ gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
+ unsigned char* data = static_cast<unsigned char*>(gl_->MapBufferCHROMIUM(
+ GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
+ if (data) {
+ result = true;
+ if (request->bytes_per_row == request->size.width() * bytes_per_pixel &&
+ request->bytes_per_row == request->row_stride_bytes) {
+ memcpy(request->pixels, data,
+ request->size.GetArea() * bytes_per_pixel);
+ } else {
+ unsigned char* out = request->pixels;
+ for (int y = 0; y < request->size.height(); y++) {
+ memcpy(out, data, request->bytes_per_row);
+ out += request->row_stride_bytes;
+ data += request->size.width() * bytes_per_pixel;
+ }
+ }
+ gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
+ }
+ gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
+ }
+ FinishRequest(request, result, &finish_request_helper);
+ }
+}
+
+void GLHelper::CopyTextureToImpl::FinishRequest(
+ Request* request,
+ bool result,
+ FinishRequestHelper* finish_request_helper) {
+ TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::FinishRequest");
+ DCHECK(request_queue_.front() == request);
+ request_queue_.pop();
+ request->result = result;
+ ScopedFlush flush(gl_);
+ if (request->query != 0) {
+ gl_->DeleteQueriesEXT(1, &request->query);
+ request->query = 0;
+ }
+ if (request->buffer != 0) {
+ gl_->DeleteBuffers(1, &request->buffer);
+ request->buffer = 0;
+ }
+ finish_request_helper->Add(request);
+}
+
+void GLHelper::CopyTextureToImpl::CancelRequests() {
+ FinishRequestHelper finish_request_helper;
+ while (!request_queue_.empty()) {
+ Request* request = request_queue_.front();
+ FinishRequest(request, false, &finish_request_helper);
+ }
+}
+
+FormatSupport GLHelper::CopyTextureToImpl::GetReadbackConfig(
+ SkColorType color_type,
+ bool can_swizzle,
+ GLenum* format,
+ GLenum* type,
+ size_t* bytes_per_pixel) {
+ return helper_->readback_support_->GetReadbackConfig(
+ color_type, can_swizzle, format, type, bytes_per_pixel);
+}
+
+GLHelper::GLHelper(GLES2Interface* gl, gpu::ContextSupport* context_support)
+ : gl_(gl),
+ context_support_(context_support),
+ readback_support_(new GLHelperReadbackSupport(gl)) {}
+
+GLHelper::~GLHelper() {}
+
+void GLHelper::CropScaleReadbackAndCleanTexture(
+ GLuint src_texture,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ const SkColorType out_color_type,
+ const base::Callback<void(bool)>& callback,
+ GLHelper::ScalerQuality quality) {
+ InitCopyTextToImpl();
+ copy_texture_to_impl_->CropScaleReadbackAndCleanTexture(
+ src_texture, src_size, src_subrect, dst_size, out, out_color_type,
+ callback, quality);
+}
+
+void GLHelper::CropScaleReadbackAndCleanMailbox(
+ const gpu::Mailbox& src_mailbox,
+ const gpu::SyncToken& sync_token,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ const SkColorType out_color_type,
+ const base::Callback<void(bool)>& callback,
+ GLHelper::ScalerQuality quality) {
+ GLuint mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_token);
+ CropScaleReadbackAndCleanTexture(mailbox_texture, src_size, src_subrect,
+ dst_size, out, out_color_type, callback,
+ quality);
+ gl_->DeleteTextures(1, &mailbox_texture);
+}
+
+void GLHelper::ReadbackTextureSync(GLuint texture,
+ const gfx::Rect& src_rect,
+ unsigned char* out,
+ SkColorType format) {
+ InitCopyTextToImpl();
+ copy_texture_to_impl_->ReadbackTextureSync(texture, src_rect, out, format);
+}
+
+void GLHelper::ReadbackTextureAsync(
+ GLuint texture,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ SkColorType color_type,
+ const base::Callback<void(bool)>& callback) {
+ InitCopyTextToImpl();
+ copy_texture_to_impl_->ReadbackTextureAsync(texture, dst_size, out,
+ color_type, callback);
+}
+
+GLuint GLHelper::CopyTexture(GLuint texture, const gfx::Size& size) {
+ InitCopyTextToImpl();
+ return copy_texture_to_impl_->CopyAndScaleTexture(
+ texture, size, size, false, GLHelper::SCALER_QUALITY_FAST);
+}
+
+GLuint GLHelper::CopyAndScaleTexture(GLuint texture,
+ const gfx::Size& src_size,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ ScalerQuality quality) {
+ InitCopyTextToImpl();
+ return copy_texture_to_impl_->CopyAndScaleTexture(
+ texture, src_size, dst_size, vertically_flip_texture, quality);
+}
+
+GLuint GLHelper::CompileShaderFromSource(const GLchar* source, GLenum type) {
+ GLuint shader = gl_->CreateShader(type);
+ GLint length = base::checked_cast<GLint>(strlen(source));
+ gl_->ShaderSource(shader, 1, &source, &length);
+ gl_->CompileShader(shader);
+ GLint compile_status = 0;
+ gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
+ if (!compile_status) {
+ GLint log_length = 0;
+ gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+ if (log_length) {
+ std::unique_ptr<GLchar[]> log(new GLchar[log_length]);
+ GLsizei returned_log_length = 0;
+ gl_->GetShaderInfoLog(shader, log_length, &returned_log_length,
+ log.get());
+ LOG(ERROR) << std::string(log.get(), returned_log_length);
+ }
+ gl_->DeleteShader(shader);
+ return 0;
+ }
+ return shader;
+}
+
+void GLHelper::InitCopyTextToImpl() {
+ // Lazily initialize |copy_texture_to_impl_|
+ if (!copy_texture_to_impl_)
+ copy_texture_to_impl_.reset(
+ new CopyTextureToImpl(gl_, context_support_, this));
+}
+
+void GLHelper::InitScalerImpl() {
+ // Lazily initialize |scaler_impl_|
+ if (!scaler_impl_)
+ scaler_impl_.reset(new GLHelperScaling(gl_, this));
+}
+
+GLint GLHelper::MaxDrawBuffers() {
+ InitCopyTextToImpl();
+ return copy_texture_to_impl_->MaxDrawBuffers();
+}
+
+void GLHelper::CopySubBufferDamage(GLenum target,
+ GLuint texture,
+ GLuint previous_texture,
+ const SkRegion& new_damage,
+ const SkRegion& old_damage) {
+ SkRegion region(old_damage);
+ if (region.op(new_damage, SkRegion::kDifference_Op)) {
+ ScopedFramebuffer dst_framebuffer(gl_);
+ ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
+ dst_framebuffer);
+ gl_->BindTexture(target, texture);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target,
+ previous_texture, 0);
+ for (SkRegion::Iterator it(region); !it.done(); it.next()) {
+ const SkIRect& rect = it.rect();
+ gl_->CopyTexSubImage2D(target, 0, rect.x(), rect.y(), rect.x(), rect.y(),
+ rect.width(), rect.height());
+ }
+ gl_->BindTexture(target, 0);
+ gl_->Flush();
+ }
+}
+
+GLuint GLHelper::CreateTexture() {
+ GLuint texture = 0u;
+ gl_->GenTextures(1, &texture);
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ return texture;
+}
+
+void GLHelper::DeleteTexture(GLuint texture_id) {
+ gl_->DeleteTextures(1, &texture_id);
+}
+
+void GLHelper::GenerateSyncToken(gpu::SyncToken* sync_token) {
+ const uint64_t fence_sync = gl_->InsertFenceSyncCHROMIUM();
+ gl_->ShallowFlushCHROMIUM();
+ gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token->GetData());
+}
+
+void GLHelper::WaitSyncToken(const gpu::SyncToken& sync_token) {
+ gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+}
+
+gpu::MailboxHolder GLHelper::ProduceMailboxHolderFromTexture(
+ GLuint texture_id) {
+ gpu::Mailbox mailbox;
+ gl_->GenMailboxCHROMIUM(mailbox.name);
+ gl_->ProduceTextureDirectCHROMIUM(texture_id, GL_TEXTURE_2D, mailbox.name);
+
+ gpu::SyncToken sync_token;
+ GenerateSyncToken(&sync_token);
+
+ return gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D);
+}
+
+GLuint GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token) {
+ if (mailbox.IsZero())
+ return 0;
+ if (sync_token.HasData())
+ WaitSyncToken(sync_token);
+ GLuint texture =
+ gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
+ return texture;
+}
+
+void GLHelper::ResizeTexture(GLuint texture, const gfx::Size& size) {
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+}
+
+void GLHelper::CopyTextureSubImage(GLuint texture, const gfx::Rect& rect) {
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
+ gl_->CopyTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.x(),
+ rect.y(), rect.width(), rect.height());
+}
+
+void GLHelper::CopyTextureFullImage(GLuint texture, const gfx::Size& size) {
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
+ gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size.width(),
+ size.height(), 0);
+}
+
+void GLHelper::Flush() {
+ gl_->Flush();
+}
+
+void GLHelper::InsertOrderingBarrier() {
+ gl_->OrderingBarrierCHROMIUM();
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackPlane(
+ TextureFrameBufferPair* source,
+ int row_stride_bytes,
+ unsigned char* data,
+ int size_shift,
+ const gfx::Rect& paste_rect,
+ ReadbackSwizzle swizzle,
+ const base::Callback<void(bool)>& callback) {
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, source->framebuffer());
+ const size_t offset = row_stride_bytes * (paste_rect.y() >> size_shift) +
+ (paste_rect.x() >> size_shift);
+ ReadbackAsync(source->size(), paste_rect.width() >> size_shift,
+ row_stride_bytes, data + offset,
+ (swizzle == kSwizzleBGRA) ? GL_BGRA_EXT : GL_RGBA,
+ GL_UNSIGNED_BYTE, 4, callback);
+}
+
+const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = {
+ 0.257f, 0.504f, 0.098f, 0.0625f};
+const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = {
+ -0.148f, -0.291f, 0.439f, 0.5f};
+const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = {
+ 0.439f, -0.368f, -0.071f, 0.5f};
+const float GLHelper::CopyTextureToImpl::kRGBtoGrayscaleColorWeights[] = {
+ 0.213f, 0.715f, 0.072f, 0.0f};
+
+// YUV readback constructors. Initiates the main scaler pipeline and
+// one planar scaler for each of the Y, U and V planes.
+GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl(
+ GLES2Interface* gl,
+ CopyTextureToImpl* copy_impl,
+ GLHelperScaling* scaler_impl,
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ ReadbackSwizzle swizzle)
+ : gl_(gl),
+ copy_impl_(copy_impl),
+ dst_size_(dst_size),
+ swizzle_(swizzle),
+ scaler_(gl,
+ scaler_impl->CreateScaler(quality,
+ src_size,
+ src_subrect,
+ dst_size,
+ flip_vertically,
+ false)),
+ y_(gl,
+ scaler_impl->CreatePlanarScaler(
+ dst_size,
+ gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()),
+ gfx::Size((dst_size.width() + 3) / 4, dst_size.height()),
+ false,
+ (swizzle == kSwizzleBGRA),
+ kRGBtoYColorWeights)),
+ u_(gl,
+ scaler_impl->CreatePlanarScaler(
+ dst_size,
+ gfx::Rect(0,
+ 0,
+ (dst_size.width() + 7) & ~7,
+ (dst_size.height() + 1) & ~1),
+ gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2),
+ false,
+ (swizzle == kSwizzleBGRA),
+ kRGBtoUColorWeights)),
+ v_(gl,
+ scaler_impl->CreatePlanarScaler(
+ dst_size,
+ gfx::Rect(0,
+ 0,
+ (dst_size.width() + 7) & ~7,
+ (dst_size.height() + 1) & ~1),
+ gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2),
+ false,
+ (swizzle == kSwizzleBGRA),
+ kRGBtoVColorWeights)) {
+ DCHECK(!(dst_size.width() & 1));
+ DCHECK(!(dst_size.height() & 1));
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV(
+ const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ const gfx::Rect& target_visible_rect,
+ int y_plane_row_stride_bytes,
+ unsigned char* y_plane_data,
+ int u_plane_row_stride_bytes,
+ unsigned char* u_plane_data,
+ int v_plane_row_stride_bytes,
+ unsigned char* v_plane_data,
+ const gfx::Point& paste_location,
+ const base::Callback<void(bool)>& callback) {
+ DCHECK(!(paste_location.x() & 1));
+ DCHECK(!(paste_location.y() & 1));
+
+ GLuint mailbox_texture =
+ copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token);
+
+ // Scale texture to right size.
+ scaler_.Scale(mailbox_texture);
+ gl_->DeleteTextures(1, &mailbox_texture);
+
+ // Convert the scaled texture in to Y, U and V planes.
+ y_.Scale(scaler_.texture());
+ u_.Scale(scaler_.texture());
+ v_.Scale(scaler_.texture());
+
+ const gfx::Rect paste_rect(paste_location, dst_size_);
+ if (!target_visible_rect.Contains(paste_rect)) {
+ LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!";
+ callback.Run(false);
+ return;
+ }
+
+ // Read back planes, one at a time. Keep the video frame alive while doing the
+ // readback.
+ copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(),
+ y_plane_row_stride_bytes, y_plane_data, 0,
+ paste_rect, swizzle_, base::Bind(&nullcallback));
+ copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(),
+ u_plane_row_stride_bytes, u_plane_data, 1,
+ paste_rect, swizzle_, base::Bind(&nullcallback));
+ copy_impl_->ReadbackPlane(v_.texture_and_framebuffer(),
+ v_plane_row_stride_bytes, v_plane_data, 1,
+ paste_rect, swizzle_, callback);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+// YUV readback constructors. Initiates the main scaler pipeline and
+// one planar scaler for each of the Y, U and V planes.
+GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT(
+ GLES2Interface* gl,
+ CopyTextureToImpl* copy_impl,
+ GLHelperScaling* scaler_impl,
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ ReadbackSwizzle swizzle)
+ : gl_(gl),
+ copy_impl_(copy_impl),
+ dst_size_(dst_size),
+ quality_(quality),
+ swizzle_(swizzle),
+ scaler_(gl,
+ scaler_impl->CreateScaler(quality,
+ src_size,
+ src_subrect,
+ dst_size,
+ false,
+ false)),
+ pass1_shader_(scaler_impl->CreateYuvMrtShader(
+ dst_size,
+ gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()),
+ gfx::Size((dst_size.width() + 3) / 4, dst_size.height()),
+ flip_vertically,
+ (swizzle == kSwizzleBGRA),
+ GLHelperScaling::SHADER_YUV_MRT_PASS1)),
+ pass2_shader_(scaler_impl->CreateYuvMrtShader(
+ gfx::Size((dst_size.width() + 3) / 4, dst_size.height()),
+ gfx::Rect(0, 0, (dst_size.width() + 7) / 8 * 2, dst_size.height()),
+ gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2),
+ false,
+ (swizzle == kSwizzleBGRA),
+ GLHelperScaling::SHADER_YUV_MRT_PASS2)),
+ y_(gl, gfx::Size((dst_size.width() + 3) / 4, dst_size.height())),
+ uv_(gl),
+ u_(gl,
+ gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)),
+ v_(gl,
+ gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)) {
+ DCHECK(!(dst_size.width() & 1));
+ DCHECK(!(dst_size.height() & 1));
+
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, uv_);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (dst_size.width() + 3) / 4,
+ dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV(
+ const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ const gfx::Rect& target_visible_rect,
+ int y_plane_row_stride_bytes,
+ unsigned char* y_plane_data,
+ int u_plane_row_stride_bytes,
+ unsigned char* u_plane_data,
+ int v_plane_row_stride_bytes,
+ unsigned char* v_plane_data,
+ const gfx::Point& paste_location,
+ const base::Callback<void(bool)>& callback) {
+ DCHECK(!(paste_location.x() & 1));
+ DCHECK(!(paste_location.y() & 1));
+
+ GLuint mailbox_texture =
+ copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token);
+
+ GLuint texture;
+ if (quality_ == GLHelper::SCALER_QUALITY_FAST) {
+ // Optimization: SCALER_QUALITY_FAST is just a single bilinear
+ // pass, which pass1_shader_ can do just as well, so let's skip
+ // the actual scaling in that case.
+ texture = mailbox_texture;
+ } else {
+ // Scale texture to right size.
+ scaler_.Scale(mailbox_texture);
+ texture = scaler_.texture();
+ }
+
+ std::vector<GLuint> outputs(2);
+ // Convert the scaled texture in to Y, U and V planes.
+ outputs[0] = y_.texture();
+ outputs[1] = uv_;
+ pass1_shader_->Execute(texture, outputs);
+
+ gl_->DeleteTextures(1, &mailbox_texture);
+
+ outputs[0] = u_.texture();
+ outputs[1] = v_.texture();
+ pass2_shader_->Execute(uv_, outputs);
+
+ const gfx::Rect paste_rect(paste_location, dst_size_);
+ if (!target_visible_rect.Contains(paste_rect)) {
+ LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!";
+ callback.Run(false);
+ return;
+ }
+
+ // Read back planes, one at a time.
+ copy_impl_->ReadbackPlane(&y_, y_plane_row_stride_bytes, y_plane_data, 0,
+ paste_rect, swizzle_, base::Bind(&nullcallback));
+ copy_impl_->ReadbackPlane(&u_, u_plane_row_stride_bytes, u_plane_data, 1,
+ paste_rect, swizzle_, base::Bind(&nullcallback));
+ copy_impl_->ReadbackPlane(&v_, v_plane_row_stride_bytes, v_plane_data, 1,
+ paste_rect, swizzle_, callback);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+bool GLHelper::IsReadbackConfigSupported(SkColorType color_type) {
+ DCHECK(readback_support_.get());
+ GLenum format, type;
+ size_t bytes_per_pixel;
+ FormatSupport support = readback_support_->GetReadbackConfig(
+ color_type, false, &format, &type, &bytes_per_pixel);
+
+ return (support == GLHelperReadbackSupport::SUPPORTED);
+}
+
+ReadbackYUVInterface* GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV(
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ bool use_mrt) {
+ helper_->InitScalerImpl();
+ // Just query if the best readback configuration needs a swizzle In
+ // ReadbackPlane() we will choose GL_RGBA/GL_BGRA_EXT based on swizzle
+ GLenum format, type;
+ size_t bytes_per_pixel;
+ FormatSupport supported = GetReadbackConfig(kRGBA_8888_SkColorType, true,
+ &format, &type, &bytes_per_pixel);
+ DCHECK((format == GL_RGBA || format == GL_BGRA_EXT) &&
+ type == GL_UNSIGNED_BYTE);
+
+ ReadbackSwizzle swizzle = kSwizzleNone;
+ if (supported == GLHelperReadbackSupport::SWIZZLE)
+ swizzle = kSwizzleBGRA;
+
+ if (max_draw_buffers_ >= 2 && use_mrt) {
+ return new ReadbackYUV_MRT(gl_, this, helper_->scaler_impl_.get(), quality,
+ src_size, src_subrect, dst_size, flip_vertically,
+ swizzle);
+ }
+ return new ReadbackYUVImpl(gl_, this, helper_->scaler_impl_.get(), quality,
+ src_size, src_subrect, dst_size, flip_vertically,
+ swizzle);
+}
+
+ReadbackYUVInterface* GLHelper::CreateReadbackPipelineYUV(
+ ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ bool use_mrt) {
+ InitCopyTextToImpl();
+ return copy_texture_to_impl_->CreateReadbackPipelineYUV(
+ quality, src_size, src_subrect, dst_size, flip_vertically, use_mrt);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gl_helper.h b/chromium/components/viz/common/gl_helper.h
new file mode 100644
index 00000000000..931b8549fed
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper.h
@@ -0,0 +1,383 @@
+// 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_VIZ_COMMON_GL_HELPER_H_
+#define COMPONENTS_VIZ_COMMON_GL_HELPER_H_
+
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace gfx {
+class Point;
+class Rect;
+class Size;
+} // namespace gfx
+
+namespace gpu {
+class ContextSupport;
+struct Mailbox;
+} // namespace gpu
+
+class SkRegion;
+
+namespace viz {
+
+class GLHelperScaling;
+
+class ScopedGLuint {
+ public:
+ typedef void (gpu::gles2::GLES2Interface::*GenFunc)(GLsizei n, GLuint* ids);
+ typedef void (gpu::gles2::GLES2Interface::*DeleteFunc)(GLsizei n,
+ const GLuint* ids);
+ ScopedGLuint(gpu::gles2::GLES2Interface* gl,
+ GenFunc gen_func,
+ DeleteFunc delete_func)
+ : gl_(gl), id_(0u), delete_func_(delete_func) {
+ (gl_->*gen_func)(1, &id_);
+ }
+
+ operator GLuint() const { return id_; }
+
+ GLuint id() const { return id_; }
+
+ ~ScopedGLuint() {
+ if (id_ != 0) {
+ (gl_->*delete_func_)(1, &id_);
+ }
+ }
+
+ private:
+ gpu::gles2::GLES2Interface* gl_;
+ GLuint id_;
+ DeleteFunc delete_func_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedGLuint);
+};
+
+class ScopedBuffer : public ScopedGLuint {
+ public:
+ explicit ScopedBuffer(gpu::gles2::GLES2Interface* gl)
+ : ScopedGLuint(gl,
+ &gpu::gles2::GLES2Interface::GenBuffers,
+ &gpu::gles2::GLES2Interface::DeleteBuffers) {}
+};
+
+class ScopedFramebuffer : public ScopedGLuint {
+ public:
+ explicit ScopedFramebuffer(gpu::gles2::GLES2Interface* gl)
+ : ScopedGLuint(gl,
+ &gpu::gles2::GLES2Interface::GenFramebuffers,
+ &gpu::gles2::GLES2Interface::DeleteFramebuffers) {}
+};
+
+class ScopedTexture : public ScopedGLuint {
+ public:
+ explicit ScopedTexture(gpu::gles2::GLES2Interface* gl)
+ : ScopedGLuint(gl,
+ &gpu::gles2::GLES2Interface::GenTextures,
+ &gpu::gles2::GLES2Interface::DeleteTextures) {}
+};
+
+template <GLenum Target>
+class ScopedBinder {
+ public:
+ typedef void (gpu::gles2::GLES2Interface::*BindFunc)(GLenum target,
+ GLuint id);
+ ScopedBinder(gpu::gles2::GLES2Interface* gl, GLuint id, BindFunc bind_func)
+ : gl_(gl), bind_func_(bind_func) {
+ (gl_->*bind_func_)(Target, id);
+ }
+
+ virtual ~ScopedBinder() { (gl_->*bind_func_)(Target, 0); }
+
+ private:
+ gpu::gles2::GLES2Interface* gl_;
+ BindFunc bind_func_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedBinder);
+};
+
+template <GLenum Target>
+class ScopedBufferBinder : ScopedBinder<Target> {
+ public:
+ ScopedBufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id)
+ : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindBuffer) {}
+};
+
+template <GLenum Target>
+class ScopedFramebufferBinder : ScopedBinder<Target> {
+ public:
+ ScopedFramebufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id)
+ : ScopedBinder<Target>(gl,
+ id,
+ &gpu::gles2::GLES2Interface::BindFramebuffer) {}
+};
+
+template <GLenum Target>
+class ScopedTextureBinder : ScopedBinder<Target> {
+ public:
+ ScopedTextureBinder(gpu::gles2::GLES2Interface* gl, GLuint id)
+ : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindTexture) {
+ }
+};
+
+class ReadbackYUVInterface;
+class GLHelperReadbackSupport;
+
+// Provides higher level operations on top of the gpu::gles2::GLES2Interface
+// interfaces.
+class VIZ_COMMON_EXPORT GLHelper {
+ public:
+ GLHelper(gpu::gles2::GLES2Interface* gl,
+ gpu::ContextSupport* context_support);
+ ~GLHelper();
+
+ enum ScalerQuality {
+ // Bilinear single pass, fastest possible.
+ SCALER_QUALITY_FAST = 1,
+
+ // Bilinear upscale + N * 50% bilinear downscales.
+ // This is still fast enough for most purposes and
+ // Image quality is nearly as good as the BEST option.
+ SCALER_QUALITY_GOOD = 2,
+
+ // Bicubic upscale + N * 50% bicubic downscales.
+ // Produces very good quality scaled images, but it's
+ // 2-8x slower than the "GOOD" quality, so it's not always
+ // worth it.
+ SCALER_QUALITY_BEST = 3,
+ };
+
+ // Copies the block of pixels specified with |src_subrect| from |src_texture|,
+ // scales it to |dst_size|, and writes it into |out|.
+ // |src_size| is the size of |src_texture|. The result is in |out_color_type|
+ // format and is potentially flipped vertically to make it a correct image
+ // representation. |callback| is invoked with the copy result when the copy
+ // operation has completed.
+ // Note that the src_texture will have the min/mag filter set to GL_LINEAR
+ // and wrap_s/t set to CLAMP_TO_EDGE in this call.
+ void CropScaleReadbackAndCleanTexture(
+ GLuint src_texture,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ const SkColorType out_color_type,
+ const base::Callback<void(bool)>& callback,
+ GLHelper::ScalerQuality quality);
+
+ // Copies the block of pixels specified with |src_subrect| from |src_mailbox|,
+ // scales it to |dst_size|, and writes it into |out|.
+ // |src_size| is the size of |src_mailbox|. The result is in |out_color_type|
+ // format and is potentially flipped vertically to make it a correct image
+ // representation. |callback| is invoked with the copy result when the copy
+ // operation has completed.
+ // Note that the texture bound to src_mailbox will have the min/mag filter set
+ // to GL_LINEAR and wrap_s/t set to CLAMP_TO_EDGE in this call. src_mailbox is
+ // assumed to be GL_TEXTURE_2D.
+ void CropScaleReadbackAndCleanMailbox(
+ const gpu::Mailbox& src_mailbox,
+ const gpu::SyncToken& sync_token,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ const SkColorType out_color_type,
+ const base::Callback<void(bool)>& callback,
+ GLHelper::ScalerQuality quality);
+
+ // Copies the texture data out of |texture| into |out|. |size| is the
+ // size of the texture. No post processing is applied to the pixels. The
+ // texture is assumed to have a format of GL_RGBA with a pixel type of
+ // GL_UNSIGNED_BYTE. This is a blocking call that calls glReadPixels on the
+ // current OpenGL context.
+ void ReadbackTextureSync(GLuint texture,
+ const gfx::Rect& src_rect,
+ unsigned char* out,
+ SkColorType format);
+
+ void ReadbackTextureAsync(GLuint texture,
+ const gfx::Size& dst_size,
+ unsigned char* out,
+ SkColorType color_type,
+ const base::Callback<void(bool)>& callback);
+
+ // Creates a copy of the specified texture. |size| is the size of the texture.
+ // Note that the src_texture will have the min/mag filter set to GL_LINEAR
+ // and wrap_s/t set to CLAMP_TO_EDGE in this call.
+ GLuint CopyTexture(GLuint texture, const gfx::Size& size);
+
+ // Creates a scaled copy of the specified texture. |src_size| is the size of
+ // the texture and |dst_size| is the size of the resulting copy.
+ // Note that the src_texture will have the min/mag filter set to GL_LINEAR
+ // and wrap_s/t set to CLAMP_TO_EDGE in this call.
+ GLuint CopyAndScaleTexture(GLuint texture,
+ const gfx::Size& src_size,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ ScalerQuality quality);
+
+ // Returns the shader compiled from the source.
+ GLuint CompileShaderFromSource(const GLchar* source, GLenum type);
+
+ // Copies all pixels from |previous_texture| into |texture| that are
+ // inside the region covered by |old_damage| but not part of |new_damage|.
+ void CopySubBufferDamage(GLenum target,
+ GLuint texture,
+ GLuint previous_texture,
+ const SkRegion& new_damage,
+ const SkRegion& old_damage);
+
+ // Simply creates a texture.
+ GLuint CreateTexture();
+ // Deletes a texture.
+ void DeleteTexture(GLuint texture_id);
+
+ // Inserts a fence sync, flushes, and generates a sync token.
+ void GenerateSyncToken(gpu::SyncToken* sync_token);
+
+ // Wait for the sync token before executing further GL commands.
+ void WaitSyncToken(const gpu::SyncToken& sync_token);
+
+ // Creates a mailbox holder that is attached to the given texture id, with a
+ // sync point to wait on before using the mailbox. Returns a holder with an
+ // empty mailbox on failure.
+ // Note the texture is assumed to be GL_TEXTURE_2D.
+ gpu::MailboxHolder ProduceMailboxHolderFromTexture(GLuint texture_id);
+
+ // Creates a texture and consumes a mailbox into it. Returns 0 on failure.
+ // Note the mailbox is assumed to be GL_TEXTURE_2D.
+ GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token);
+
+ // Resizes the texture's size to |size|.
+ void ResizeTexture(GLuint texture, const gfx::Size& size);
+
+ // Copies the framebuffer data given in |rect| to |texture|.
+ void CopyTextureSubImage(GLuint texture, const gfx::Rect& rect);
+
+ // Copies the all framebuffer data to |texture|. |size| specifies the
+ // size of the framebuffer.
+ void CopyTextureFullImage(GLuint texture, const gfx::Size& size);
+
+ // Flushes GL commands.
+ void Flush();
+
+ // Force commands in the current command buffer to be executed before commands
+ // in other command buffers from the same process (ie channel to the GPU
+ // process).
+ void InsertOrderingBarrier();
+
+ // A scaler will cache all intermediate textures and programs
+ // needed to scale from a specified size to a destination size.
+ // If the source or destination sizes changes, you must create
+ // a new scaler.
+ class ScalerInterface {
+ public:
+ ScalerInterface() {}
+ virtual ~ScalerInterface() {}
+
+ // Note that the src_texture will have the min/mag filter set to GL_LINEAR
+ // and wrap_s/t set to CLAMP_TO_EDGE in this call.
+ virtual void Scale(GLuint source_texture, GLuint dest_texture) = 0;
+ virtual const gfx::Size& SrcSize() = 0;
+ virtual const gfx::Rect& SrcSubrect() = 0;
+ virtual const gfx::Size& DstSize() = 0;
+ };
+
+ // Note that the quality may be adjusted down if texture
+ // allocations fail or hardware doesn't support the requtested
+ // quality. Note that ScalerQuality enum is arranged in
+ // numerical order for simplicity.
+ ScalerInterface* CreateScaler(ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle);
+
+ // Create a readback pipeline that will scale a subsection of the source
+ // texture, then convert it to YUV422 planar form and then read back that.
+ // This reduces the amount of memory read from GPU to CPU memory by a factor
+ // 2.6, which can be quite handy since readbacks have very limited speed
+ // on some platforms. All values in |dst_size| must be a multiple of two. If
+ // |use_mrt| is true, the pipeline will try to optimize the YUV conversion
+ // using the multi-render-target extension. |use_mrt| should only be set to
+ // false for testing.
+ ReadbackYUVInterface* CreateReadbackPipelineYUV(ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool flip_vertically,
+ bool use_mrt);
+
+ // Returns the maximum number of draw buffers available,
+ // 0 if GL_EXT_draw_buffers is not available.
+ GLint MaxDrawBuffers();
+
+ // Checks whether the readbback is supported for texture with the
+ // matching config. This doesnt check for cross format readbacks.
+ bool IsReadbackConfigSupported(SkColorType texture_format);
+
+ protected:
+ class CopyTextureToImpl;
+
+ // Creates |copy_texture_to_impl_| if NULL.
+ void InitCopyTextToImpl();
+ // Creates |scaler_impl_| if NULL.
+ void InitScalerImpl();
+
+ enum ReadbackSwizzle { kSwizzleNone = 0, kSwizzleBGRA };
+
+ gpu::gles2::GLES2Interface* gl_;
+ gpu::ContextSupport* context_support_;
+ std::unique_ptr<CopyTextureToImpl> copy_texture_to_impl_;
+ std::unique_ptr<GLHelperScaling> scaler_impl_;
+ std::unique_ptr<GLHelperReadbackSupport> readback_support_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GLHelper);
+};
+
+// Similar to a ScalerInterface, a yuv readback pipeline will
+// cache a scaler and all intermediate textures and frame buffers
+// needed to scale, crop, letterbox and read back a texture from
+// the GPU into CPU-accessible RAM. A single readback pipeline
+// can handle multiple outstanding readbacks at the same time, but
+// if the source or destination sizes change, you'll need to create
+// a new readback pipeline.
+class ReadbackYUVInterface {
+ public:
+ ReadbackYUVInterface() {}
+ virtual ~ReadbackYUVInterface() {}
+
+ // Note that |target| must use YV12 format. |paste_location| specifies where
+ // the captured pixels that are read back will be placed in the video frame.
+ // The region defined by the |paste_location| and the |dst_size| specified in
+ // the call to CreateReadbackPipelineYUV() must be fully contained within
+ // |target->visible_rect()|.
+ virtual void ReadbackYUV(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ const gfx::Rect& target_visible_rect,
+ int y_plane_row_stride_bytes,
+ unsigned char* y_plane_data,
+ int u_plane_row_stride_bytes,
+ unsigned char* u_plane_data,
+ int v_plane_row_stride_bytes,
+ unsigned char* v_plane_data,
+ const gfx::Point& paste_location,
+ const base::Callback<void(bool)>& callback) = 0;
+ virtual GLHelper::ScalerInterface* scaler() = 0;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GL_HELPER_H_
diff --git a/chromium/components/viz/common/gl_helper_benchmark.cc b/chromium/components/viz/common/gl_helper_benchmark.cc
new file mode 100644
index 00000000000..59c3a73e537
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper_benchmark.cc
@@ -0,0 +1,252 @@
+// 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.
+
+// This file looks like a unit test, but it contains benchmarks and test
+// utilities intended for manual evaluation of the scalers in
+// gl_helper*. These tests produce output in the form of files and printouts,
+// but cannot really "fail". There is no point in making these tests part
+// of any test automation run.
+
+#include <stddef.h>
+#include <stdio.h>
+#include <cmath>
+#include <string>
+#include <vector>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/viz/common/gl_helper.h"
+#include "components/viz/common/gl_helper_scaling.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/ipc/gl_in_process_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkTypes.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace viz {
+
+namespace {
+
+GLHelper::ScalerQuality kQualities[] = {
+ GLHelper::SCALER_QUALITY_BEST, GLHelper::SCALER_QUALITY_GOOD,
+ GLHelper::SCALER_QUALITY_FAST,
+};
+
+const char* const kQualityNames[] = {
+ "best", "good", "fast",
+};
+
+} // namespace
+
+class GLHelperBenchmark : public testing::Test {
+ protected:
+ void SetUp() override {
+ gpu::gles2::ContextCreationAttribHelper attributes;
+ attributes.alpha_size = 8;
+ attributes.depth_size = 24;
+ attributes.red_size = 8;
+ attributes.green_size = 8;
+ attributes.blue_size = 8;
+ attributes.stencil_size = 8;
+ attributes.samples = 4;
+ attributes.sample_buffers = 1;
+ attributes.bind_generates_resource = false;
+ attributes.gpu_preference = gl::PreferDiscreteGpu;
+
+ context_.reset(
+ gpu::GLInProcessContext::Create(nullptr, /* service */
+ nullptr, /* surface */
+ true, /* offscreen */
+ gpu::kNullSurfaceHandle, /* window */
+ nullptr, /* share_context */
+ attributes, gpu::SharedMemoryLimits(),
+ nullptr, /* gpu_memory_buffer_manager */
+ nullptr, /* image_factory */
+ base::ThreadTaskRunnerHandle::Get()));
+ gl_ = context_->GetImplementation();
+ gpu::ContextSupport* support = context_->GetImplementation();
+
+ helper_.reset(new GLHelper(gl_, support));
+ helper_scaling_.reset(new GLHelperScaling(gl_, helper_.get()));
+ }
+
+ void TearDown() override {
+ helper_scaling_.reset(NULL);
+ helper_.reset(NULL);
+ context_.reset(NULL);
+ }
+
+ void LoadPngFileToSkBitmap(const base::FilePath& filename, SkBitmap* bitmap) {
+ std::string compressed;
+ base::ReadFileToString(base::MakeAbsoluteFilePath(filename), &compressed);
+ ASSERT_TRUE(compressed.size());
+ ASSERT_TRUE(gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(compressed.data()),
+ compressed.size(), bitmap));
+ }
+
+ // Save the image to a png file. Used to create the initial test files.
+ void SaveToFile(SkBitmap* bitmap, const base::FilePath& filename) {
+ std::vector<unsigned char> compressed;
+ ASSERT_TRUE(gfx::PNGCodec::Encode(
+ static_cast<unsigned char*>(bitmap->getPixels()),
+ gfx::PNGCodec::FORMAT_BGRA,
+ gfx::Size(bitmap->width(), bitmap->height()),
+ static_cast<int>(bitmap->rowBytes()), true,
+ std::vector<gfx::PNGCodec::Comment>(), &compressed));
+ ASSERT_TRUE(compressed.size());
+ FILE* f = base::OpenFile(filename, "wb");
+ ASSERT_TRUE(f);
+ ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
+ compressed.size());
+ base::CloseFile(f);
+ }
+
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<gpu::GLInProcessContext> context_;
+ gpu::gles2::GLES2Interface* gl_;
+ std::unique_ptr<GLHelper> helper_;
+ std::unique_ptr<GLHelperScaling> helper_scaling_;
+ std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
+};
+
+TEST_F(GLHelperBenchmark, ScaleBenchmark) {
+ int output_sizes[] = {1920, 1080, 1249, 720, // Output size on pixel
+ 256, 144};
+ int input_sizes[] = {3200, 2040, 2560, 1476, // Pixel tab size
+ 1920, 1080, 1280, 720, 800, 480, 256, 144};
+
+ for (size_t q = 0; q < arraysize(kQualities); q++) {
+ for (size_t outsize = 0; outsize < arraysize(output_sizes); outsize += 2) {
+ for (size_t insize = 0; insize < arraysize(input_sizes); insize += 2) {
+ uint32_t src_texture;
+ gl_->GenTextures(1, &src_texture);
+ uint32_t dst_texture;
+ gl_->GenTextures(1, &dst_texture);
+ uint32_t framebuffer;
+ gl_->GenFramebuffers(1, &framebuffer);
+ const gfx::Size src_size(input_sizes[insize], input_sizes[insize + 1]);
+ const gfx::Size dst_size(output_sizes[outsize],
+ output_sizes[outsize + 1]);
+ SkBitmap input;
+ input.allocN32Pixels(src_size.width(), src_size.height());
+
+ SkBitmap output_pixels;
+ output_pixels.allocN32Pixels(dst_size.width(), dst_size.height());
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ gl_->BindTexture(GL_TEXTURE_2D, dst_texture);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dst_size.width(),
+ dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ gl_->BindTexture(GL_TEXTURE_2D, src_texture);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(),
+ src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ input.getPixels());
+
+ gfx::Rect src_subrect(0, 0, src_size.width(), src_size.height());
+ std::unique_ptr<GLHelper::ScalerInterface> scaler(helper_->CreateScaler(
+ kQualities[q], src_size, src_subrect, dst_size, false, false));
+ // Scale once beforehand before we start measuring.
+ scaler->Scale(src_texture, dst_texture);
+ gl_->Finish();
+
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ int iterations = 0;
+ base::TimeTicks end_time;
+ while (true) {
+ for (int i = 0; i < 50; i++) {
+ iterations++;
+ scaler->Scale(src_texture, dst_texture);
+ gl_->Flush();
+ }
+ gl_->Finish();
+ end_time = base::TimeTicks::Now();
+ if (iterations > 2000) {
+ break;
+ }
+ if ((end_time - start_time).InMillisecondsF() > 1000) {
+ break;
+ }
+ }
+ gl_->DeleteTextures(1, &dst_texture);
+ gl_->DeleteTextures(1, &src_texture);
+ gl_->DeleteFramebuffers(1, &framebuffer);
+
+ std::string name;
+ name = base::StringPrintf("scale_%dx%d_to_%dx%d_%s", src_size.width(),
+ src_size.height(), dst_size.width(),
+ dst_size.height(), kQualityNames[q]);
+
+ float ms = (end_time - start_time).InMillisecondsF() / iterations;
+ VLOG(0) << base::StringPrintf("*RESULT gpu_scale_time: %s=%.2f ms\n",
+ name.c_str(), ms);
+ }
+ }
+ }
+}
+
+// This is more of a test utility than a test.
+// Put an PNG image called "testimage.png" in your
+// current directory, then run this test. It will
+// create testoutput_Q_P.png, where Q is the scaling
+// mode and P is the scaling percentage taken from
+// the table below.
+TEST_F(GLHelperBenchmark, DISABLED_ScaleTestImage) {
+ int percents[] = {
+ 230, 180, 150, 110, 90, 70, 50, 49, 40, 20, 10,
+ };
+
+ SkBitmap input;
+ LoadPngFileToSkBitmap(base::FilePath(FILE_PATH_LITERAL("testimage.png")),
+ &input);
+
+ uint32_t framebuffer;
+ gl_->GenFramebuffers(1, &framebuffer);
+ uint32_t src_texture;
+ gl_->GenTextures(1, &src_texture);
+ const gfx::Size src_size(input.width(), input.height());
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ gl_->BindTexture(GL_TEXTURE_2D, src_texture);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(),
+ src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ input.getPixels());
+
+ for (size_t q = 0; q < arraysize(kQualities); q++) {
+ for (size_t p = 0; p < arraysize(percents); p++) {
+ const gfx::Size dst_size(input.width() * percents[p] / 100,
+ input.height() * percents[p] / 100);
+ uint32_t dst_texture = helper_->CopyAndScaleTexture(
+ src_texture, src_size, dst_size, false, kQualities[q]);
+
+ SkBitmap output_pixels;
+ output_pixels.allocN32Pixels(dst_size.width(), dst_size.height());
+
+ helper_->ReadbackTextureSync(
+ dst_texture, gfx::Rect(0, 0, dst_size.width(), dst_size.height()),
+ static_cast<unsigned char*>(output_pixels.getPixels()),
+ kN32_SkColorType);
+ gl_->DeleteTextures(1, &dst_texture);
+ std::string filename = base::StringPrintf("testoutput_%s_%d.ppm",
+ kQualityNames[q], percents[p]);
+ VLOG(0) << "Writing " << filename;
+ SaveToFile(&output_pixels, base::FilePath::FromUTF8Unsafe(filename));
+ }
+ }
+ gl_->DeleteTextures(1, &src_texture);
+ gl_->DeleteFramebuffers(1, &framebuffer);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gl_helper_readback_support.cc b/chromium/components/viz/common/gl_helper_readback_support.cc
new file mode 100644
index 00000000000..6c8161937d6
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper_readback_support.cc
@@ -0,0 +1,172 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/gl_helper_readback_support.h"
+#include "base/logging.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+
+namespace viz {
+
+GLHelperReadbackSupport::GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl)
+ : gl_(gl) {
+ InitializeReadbackSupport();
+}
+
+GLHelperReadbackSupport::~GLHelperReadbackSupport() {}
+
+void GLHelperReadbackSupport::InitializeReadbackSupport() {
+ // We are concerned about 16, 32-bit formats only. The below are the most
+ // used 16, 32-bit formats. In future if any new format support is needed
+ // that should be added here. Initialize the array with
+ // GLHelperReadbackSupport::NOT_SUPPORTED as we dont know the supported
+ // formats yet.
+ for (int i = 0; i <= kLastEnum_SkColorType; ++i) {
+ format_support_table_[i] = GLHelperReadbackSupport::NOT_SUPPORTED;
+ }
+ // TODO(sikugu): kAlpha_8_SkColorType support check is failing on mesa.
+ // See crbug.com/415667.
+ CheckForReadbackSupport(kRGB_565_SkColorType);
+ CheckForReadbackSupport(kARGB_4444_SkColorType);
+ CheckForReadbackSupport(kRGBA_8888_SkColorType);
+ CheckForReadbackSupport(kBGRA_8888_SkColorType);
+ // Further any formats, support should be checked here.
+}
+
+void GLHelperReadbackSupport::CheckForReadbackSupport(
+ SkColorType texture_format) {
+ bool supports_format = false;
+ switch (texture_format) {
+ case kRGB_565_SkColorType:
+ supports_format = SupportsFormat(GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
+ break;
+ case kRGBA_8888_SkColorType:
+ // This is the baseline, assume always true.
+ supports_format = true;
+ break;
+ case kBGRA_8888_SkColorType:
+ supports_format = SupportsFormat(GL_BGRA_EXT, GL_UNSIGNED_BYTE);
+ break;
+ case kARGB_4444_SkColorType:
+ supports_format = false;
+ break;
+ default:
+ NOTREACHED();
+ supports_format = false;
+ break;
+ }
+ DCHECK((int)texture_format <= (int)kLastEnum_SkColorType);
+ format_support_table_[texture_format] =
+ supports_format ? GLHelperReadbackSupport::SUPPORTED
+ : GLHelperReadbackSupport::NOT_SUPPORTED;
+}
+
+void GLHelperReadbackSupport::GetAdditionalFormat(GLenum format,
+ GLenum type,
+ GLenum* format_out,
+ GLenum* type_out) {
+ for (unsigned int i = 0; i < format_cache_.size(); i++) {
+ if (format_cache_[i].format == format && format_cache_[i].type == type) {
+ *format_out = format_cache_[i].read_format;
+ *type_out = format_cache_[i].read_type;
+ return;
+ }
+ }
+
+ const int kTestSize = 64;
+ ScopedTexture dst_texture(gl_);
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, format, kTestSize, kTestSize, 0, format,
+ type, NULL);
+ ScopedFramebuffer dst_framebuffer(gl_);
+ ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
+ dst_framebuffer);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ dst_texture, 0);
+ GLint format_tmp = 0, type_tmp = 0;
+ gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format_tmp);
+ gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type_tmp);
+ *format_out = format_tmp;
+ *type_out = type_tmp;
+
+ struct FormatCacheEntry entry = {format, type, *format_out, *type_out};
+ format_cache_.push_back(entry);
+}
+
+bool GLHelperReadbackSupport::SupportsFormat(GLenum format, GLenum type) {
+ // GLES2.0 Specification says this pairing is always supported
+ // with additional format from GL_IMPLEMENTATION_COLOR_READ_FORMAT/TYPE
+ if (format == GL_RGBA && type == GL_UNSIGNED_BYTE)
+ return true;
+
+ bool supports_format = false;
+ GLenum ext_format = 0, ext_type = 0;
+ GetAdditionalFormat(format, type, &ext_format, &ext_type);
+ if ((ext_format == format) && (ext_type == type)) {
+ supports_format = true;
+ }
+ return supports_format;
+}
+
+GLHelperReadbackSupport::FormatSupport
+GLHelperReadbackSupport::GetReadbackConfig(SkColorType color_type,
+ bool can_swizzle,
+ GLenum* format,
+ GLenum* type,
+ size_t* bytes_per_pixel) {
+ DCHECK(format && type && bytes_per_pixel);
+ *bytes_per_pixel = 4;
+ *type = GL_UNSIGNED_BYTE;
+ GLenum new_format = 0, new_type = 0;
+ switch (color_type) {
+ case kRGB_565_SkColorType:
+ if (format_support_table_[color_type] ==
+ GLHelperReadbackSupport::SUPPORTED) {
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5;
+ *bytes_per_pixel = 2;
+ return GLHelperReadbackSupport::SUPPORTED;
+ }
+ break;
+ case kRGBA_8888_SkColorType:
+ *format = GL_RGBA;
+ if (can_swizzle) {
+ // If GL_BGRA_EXT is advertised as the readback format through
+ // GL_IMPLEMENTATION_COLOR_READ_FORMAT then assume it is preferred by
+ // the implementation for performance.
+ GetAdditionalFormat(*format, *type, &new_format, &new_type);
+
+ if (new_format == GL_BGRA_EXT && new_type == GL_UNSIGNED_BYTE) {
+ *format = GL_BGRA_EXT;
+ return GLHelperReadbackSupport::SWIZZLE;
+ }
+ }
+ return GLHelperReadbackSupport::SUPPORTED;
+ case kBGRA_8888_SkColorType:
+ *format = GL_BGRA_EXT;
+ if (format_support_table_[color_type] ==
+ GLHelperReadbackSupport::SUPPORTED)
+ return GLHelperReadbackSupport::SUPPORTED;
+
+ if (can_swizzle) {
+ *format = GL_RGBA;
+ return GLHelperReadbackSupport::SWIZZLE;
+ }
+
+ break;
+ case kARGB_4444_SkColorType:
+ return GLHelperReadbackSupport::NOT_SUPPORTED;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ return GLHelperReadbackSupport::NOT_SUPPORTED;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gl_helper_readback_support.h b/chromium/components/viz/common/gl_helper_readback_support.h
new file mode 100644
index 00000000000..0bb76d59517
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper_readback_support.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_GL_HELPER_READBACK_SUPPORT_H_
+#define COMPONENTS_VIZ_COMMON_GL_HELPER_READBACK_SUPPORT_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "components/viz/common/gl_helper.h"
+
+namespace viz {
+
+class GLHelperReadbackSupport {
+ public:
+ enum FormatSupport { SUPPORTED, SWIZZLE, NOT_SUPPORTED };
+
+ explicit GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl);
+
+ ~GLHelperReadbackSupport();
+
+ // For a given color type retrieve whether readback is supported and if so
+ // how it should be performed. The |format|, |type| and |bytes_per_pixel| are
+ // the values that should be used with glReadPixels to facilitate the
+ // readback. If |can_swizzle| is true then this method will return SWIZZLE if
+ // the data needs to be swizzled before using the returned |format| otherwise
+ // the method will return SUPPORTED to indicate that readback is permitted of
+ // this color othewise NOT_SUPPORTED will be returned. This method always
+ // overwrites the out values irrespective of the return value.
+ FormatSupport GetReadbackConfig(SkColorType color_type,
+ bool can_swizzle,
+ GLenum* format,
+ GLenum* type,
+ size_t* bytes_per_pixel);
+ // Provides the additional readback format/type pairing for a render target
+ // of a given format/type pairing
+ void GetAdditionalFormat(GLenum format,
+ GLenum type,
+ GLenum* format_out,
+ GLenum* type_out);
+
+ private:
+ struct FormatCacheEntry {
+ GLenum format;
+ GLenum type;
+ GLenum read_format;
+ GLenum read_type;
+ };
+
+ // This populates the format_support_table with the list of supported
+ // formats.
+ void InitializeReadbackSupport();
+
+ // This api is called once per format and it is done in the
+ // InitializeReadbackSupport. We should not use this any where
+ // except the InitializeReadbackSupport.Calling this at other places
+ // can distrub the state of normal gl operations.
+ void CheckForReadbackSupport(SkColorType texture_format);
+
+ // Helper functions for checking the supported texture formats.
+ // Avoid using this API in between texture operations, as this does some
+ // teture opertions (bind, attach) internally.
+ bool SupportsFormat(GLenum format, GLenum type);
+
+ FormatSupport format_support_table_[kLastEnum_SkColorType + 1];
+
+ gpu::gles2::GLES2Interface* gl_;
+ std::vector<struct FormatCacheEntry> format_cache_;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GL_HELPER_READBACK_SUPPORT_H_
diff --git a/chromium/components/viz/common/gl_helper_scaling.cc b/chromium/components/viz/common/gl_helper_scaling.cc
new file mode 100644
index 00000000000..e394202df35
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper_scaling.cc
@@ -0,0 +1,882 @@
+// 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/viz/common/gl_helper_scaling.h"
+
+#include <stddef.h>
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+using gpu::gles2::GLES2Interface;
+
+namespace viz {
+
+GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper)
+ : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) {
+ InitBuffer();
+}
+
+GLHelperScaling::~GLHelperScaling() {}
+
+// Used to keep track of a generated shader program. The program
+// is passed in as text through Setup and is used by calling
+// UseProgram() with the right parameters. Note that |gl_|
+// and |helper_| are assumed to live longer than this program.
+class ShaderProgram : public base::RefCounted<ShaderProgram> {
+ public:
+ ShaderProgram(GLES2Interface* gl, GLHelper* helper)
+ : gl_(gl),
+ helper_(helper),
+ program_(gl_->CreateProgram()),
+ position_location_(-1),
+ texcoord_location_(-1),
+ src_subrect_location_(-1),
+ src_pixelsize_location_(-1),
+ dst_pixelsize_location_(-1),
+ scaling_vector_location_(-1),
+ color_weights_location_(-1) {}
+
+ // Compile shader program.
+ void Setup(const GLchar* vertex_shader_text,
+ const GLchar* fragment_shader_text);
+
+ // UseProgram must be called with GL_TEXTURE_2D bound to the
+ // source texture and GL_ARRAY_BUFFER bound to a vertex
+ // attribute buffer.
+ void UseProgram(const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool scale_x,
+ bool flip_y,
+ GLfloat color_weights[4]);
+
+ bool Initialized() const { return position_location_ != -1; }
+
+ private:
+ friend class base::RefCounted<ShaderProgram>;
+ ~ShaderProgram() { gl_->DeleteProgram(program_); }
+
+ GLES2Interface* gl_;
+ GLHelper* helper_;
+
+ // A program for copying a source texture into a destination texture.
+ GLuint program_;
+
+ // The location of the position in the program.
+ GLint position_location_;
+ // The location of the texture coordinate in the program.
+ GLint texcoord_location_;
+ // The location of the source texture in the program.
+ GLint texture_location_;
+ // The location of the texture coordinate of
+ // the sub-rectangle in the program.
+ GLint src_subrect_location_;
+ // Location of size of source image in pixels.
+ GLint src_pixelsize_location_;
+ // Location of size of destination image in pixels.
+ GLint dst_pixelsize_location_;
+ // Location of vector for scaling direction.
+ GLint scaling_vector_location_;
+ // Location of color weights.
+ GLint color_weights_location_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShaderProgram);
+};
+
+// Implementation of a single stage in a scaler pipeline. If the pipeline has
+// multiple stages, it calls Scale() on the subscaler, then further scales the
+// output. Caches textures and framebuffers to avoid allocating/deleting
+// them once per frame, which can be expensive on some drivers.
+class ScalerImpl : public GLHelper::ScalerInterface,
+ public GLHelperScaling::ShaderInterface {
+ public:
+ // |gl| and |copy_impl| are expected to live longer than this object.
+ // |src_size| is the size of the input texture in pixels.
+ // |dst_size| is the size of the output texutre in pixels.
+ // |src_subrect| is the portion of the src to copy to the output texture.
+ // If |scale_x| is true, we are scaling along the X axis, otherwise Y.
+ // If we are scaling in both X and Y, |scale_x| is ignored.
+ // If |vertically_flip_texture| is true, output will be upside-down.
+ // If |swizzle| is true, RGBA will be transformed into BGRA.
+ // |color_weights| are only used together with SHADER_PLANAR to specify
+ // how to convert RGB colors into a single value.
+ ScalerImpl(GLES2Interface* gl,
+ GLHelperScaling* scaler_helper,
+ const GLHelperScaling::ScalerStage& scaler_stage,
+ ScalerImpl* subscaler,
+ const float* color_weights)
+ : gl_(gl),
+ scaler_helper_(scaler_helper),
+ spec_(scaler_stage),
+ intermediate_texture_(0),
+ dst_framebuffer_(gl),
+ subscaler_(subscaler) {
+ if (color_weights) {
+ color_weights_[0] = color_weights[0];
+ color_weights_[1] = color_weights[1];
+ color_weights_[2] = color_weights[2];
+ color_weights_[3] = color_weights[3];
+ } else {
+ color_weights_[0] = 0.0;
+ color_weights_[1] = 0.0;
+ color_weights_[2] = 0.0;
+ color_weights_[3] = 0.0;
+ }
+ shader_program_ =
+ scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle);
+
+ if (subscaler_) {
+ intermediate_texture_ = 0u;
+ gl_->GenTextures(1, &intermediate_texture_);
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_,
+ intermediate_texture_);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spec_.src_size.width(),
+ spec_.src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ NULL);
+ }
+ }
+
+ ~ScalerImpl() override {
+ if (intermediate_texture_) {
+ gl_->DeleteTextures(1, &intermediate_texture_);
+ }
+ }
+
+ // GLHelperShader::ShaderInterface implementation.
+ void Execute(GLuint source_texture,
+ const std::vector<GLuint>& dest_textures) override {
+ if (subscaler_) {
+ subscaler_->Scale(source_texture, intermediate_texture_);
+ source_texture = intermediate_texture_;
+ }
+
+ ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
+ gl_, dst_framebuffer_);
+ DCHECK_GT(dest_textures.size(), 0U);
+ auto num_dest_textures = static_cast<GLsizei>(dest_textures.size());
+ auto buffers = base::MakeUnique<GLenum[]>(num_dest_textures);
+ for (GLsizei t = 0; t < num_dest_textures; t++) {
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dest_textures[t]);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + t,
+ GL_TEXTURE_2D, dest_textures[t], 0);
+ buffers[t] = GL_COLOR_ATTACHMENT0 + t;
+ }
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, source_texture);
+
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(
+ gl_, scaler_helper_->vertex_attributes_buffer_);
+ shader_program_->UseProgram(spec_.src_size, spec_.src_subrect,
+ spec_.dst_size, spec_.scale_x,
+ spec_.vertically_flip_texture, color_weights_);
+ gl_->Viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height());
+
+ if (num_dest_textures > 1) {
+ DCHECK_LE(num_dest_textures, scaler_helper_->helper_->MaxDrawBuffers());
+ gl_->DrawBuffersEXT(num_dest_textures, buffers.get());
+ }
+ // Conduct texture mapping by drawing a quad composed of two triangles.
+ gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ if (dest_textures.size() > 1) {
+ // Set the draw buffers back to not confuse others.
+ gl_->DrawBuffersEXT(1, &buffers[0]);
+ }
+ }
+
+ // GLHelper::ScalerInterface implementation.
+ void Scale(GLuint source_texture, GLuint dest_texture) override {
+ std::vector<GLuint> tmp(1);
+ tmp[0] = dest_texture;
+ Execute(source_texture, tmp);
+ }
+
+ const gfx::Size& SrcSize() override {
+ if (subscaler_) {
+ return subscaler_->SrcSize();
+ }
+ return spec_.src_size;
+ }
+ const gfx::Rect& SrcSubrect() override {
+ if (subscaler_) {
+ return subscaler_->SrcSubrect();
+ }
+ return spec_.src_subrect;
+ }
+ const gfx::Size& DstSize() override { return spec_.dst_size; }
+
+ private:
+ GLES2Interface* gl_;
+ GLHelperScaling* scaler_helper_;
+ GLHelperScaling::ScalerStage spec_;
+ GLfloat color_weights_[4];
+ GLuint intermediate_texture_;
+ scoped_refptr<ShaderProgram> shader_program_;
+ ScopedFramebuffer dst_framebuffer_;
+ std::unique_ptr<ScalerImpl> subscaler_;
+};
+
+GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_,
+ gfx::Size src_size_,
+ gfx::Rect src_subrect_,
+ gfx::Size dst_size_,
+ bool scale_x_,
+ bool vertically_flip_texture_,
+ bool swizzle_)
+ : shader(shader_),
+ src_size(src_size_),
+ src_subrect(src_subrect_),
+ dst_size(dst_size_),
+ scale_x(scale_x_),
+ vertically_flip_texture(vertically_flip_texture_),
+ swizzle(swizzle_) {}
+
+GLHelperScaling::ScalerStage::ScalerStage(const ScalerStage& other) = default;
+
+// The important inputs for this function is |x_ops| and
+// |y_ops|. They represent scaling operations to be done
+// on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST,
+// then we will interpret these scale operations literally and we'll
+// create one scaler stage for each ScaleOp. However, if |quality|
+// is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations
+// by combining two or more ScaleOps in to a single scaler stage.
+// Normally we process ScaleOps from |y_ops| first and |x_ops| after
+// all |y_ops| are processed, but sometimes we can combine one or more
+// operation from both queues essentially for free. This is the reason
+// why |x_ops| and |y_ops| aren't just one single queue.
+void GLHelperScaling::ConvertScalerOpsToScalerStages(
+ GLHelper::ScalerQuality quality,
+ gfx::Size src_size,
+ gfx::Rect src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ std::deque<GLHelperScaling::ScaleOp>* x_ops,
+ std::deque<GLHelperScaling::ScaleOp>* y_ops,
+ std::vector<ScalerStage>* scaler_stages) {
+ while (!x_ops->empty() || !y_ops->empty()) {
+ gfx::Size intermediate_size = src_subrect.size();
+ std::deque<ScaleOp>* current_queue = NULL;
+
+ if (!y_ops->empty()) {
+ current_queue = y_ops;
+ } else {
+ current_queue = x_ops;
+ }
+
+ ShaderType current_shader = SHADER_BILINEAR;
+ switch (current_queue->front().scale_factor) {
+ case 0:
+ if (quality == GLHelper::SCALER_QUALITY_BEST) {
+ current_shader = SHADER_BICUBIC_UPSCALE;
+ }
+ break;
+ case 2:
+ if (quality == GLHelper::SCALER_QUALITY_BEST) {
+ current_shader = SHADER_BICUBIC_HALF_1D;
+ }
+ break;
+ case 3:
+ DCHECK(quality != GLHelper::SCALER_QUALITY_BEST);
+ current_shader = SHADER_BILINEAR3;
+ break;
+ default:
+ NOTREACHED();
+ }
+ bool scale_x = current_queue->front().scale_x;
+ current_queue->front().UpdateSize(&intermediate_size);
+ current_queue->pop_front();
+
+ // Optimization: Sometimes we can combine 2-4 scaling operations into
+ // one operation.
+ if (quality == GLHelper::SCALER_QUALITY_GOOD) {
+ if (!current_queue->empty() && current_shader == SHADER_BILINEAR) {
+ // Combine two steps in the same dimension.
+ current_queue->front().UpdateSize(&intermediate_size);
+ current_queue->pop_front();
+ current_shader = SHADER_BILINEAR2;
+ if (!current_queue->empty()) {
+ // Combine three steps in the same dimension.
+ current_queue->front().UpdateSize(&intermediate_size);
+ current_queue->pop_front();
+ current_shader = SHADER_BILINEAR4;
+ }
+ }
+ // Check if we can combine some steps in the other dimension as well.
+ // Since all shaders currently use GL_LINEAR, we can easily scale up
+ // or scale down by exactly 2x at the same time as we do another
+ // operation. Currently, the following mergers are supported:
+ // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down)
+ // * 2 bilinear Y-passes with 2 bilinear X-passes
+ // * 1 bilinear Y-pass with N bilinear X-pass
+ // * N bilinear Y-passes with 1 bilinear X-pass (down only)
+ // Measurements indicate that generalizing this for 3x3 and 4x4
+ // makes it slower on some platforms, such as the Pixel.
+ if (!scale_x && x_ops->size() > 0 && x_ops->front().scale_factor <= 2) {
+ int x_passes = 0;
+ if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) {
+ // 2y + 2x passes
+ x_passes = 2;
+ current_shader = SHADER_BILINEAR2X2;
+ } else if (current_shader == SHADER_BILINEAR) {
+ // 1y + Nx passes
+ scale_x = true;
+ switch (x_ops->size()) {
+ case 0:
+ NOTREACHED();
+ case 1:
+ if (x_ops->front().scale_factor == 3) {
+ current_shader = SHADER_BILINEAR3;
+ }
+ x_passes = 1;
+ break;
+ case 2:
+ x_passes = 2;
+ current_shader = SHADER_BILINEAR2;
+ break;
+ default:
+ x_passes = 3;
+ current_shader = SHADER_BILINEAR4;
+ break;
+ }
+ } else if (x_ops->front().scale_factor == 2) {
+ // Ny + 1x-downscale
+ x_passes = 1;
+ }
+
+ for (int i = 0; i < x_passes; i++) {
+ x_ops->front().UpdateSize(&intermediate_size);
+ x_ops->pop_front();
+ }
+ }
+ }
+
+ scaler_stages->push_back(ScalerStage(current_shader, src_size, src_subrect,
+ intermediate_size, scale_x,
+ vertically_flip_texture, swizzle));
+ src_size = intermediate_size;
+ src_subrect = gfx::Rect(intermediate_size);
+ vertically_flip_texture = false;
+ swizzle = false;
+ }
+}
+
+void GLHelperScaling::ComputeScalerStages(
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ std::vector<ScalerStage>* scaler_stages) {
+ if (quality == GLHelper::SCALER_QUALITY_FAST ||
+ src_subrect.size() == dst_size) {
+ scaler_stages->push_back(ScalerStage(SHADER_BILINEAR, src_size, src_subrect,
+ dst_size, false,
+ vertically_flip_texture, swizzle));
+ return;
+ }
+
+ std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops;
+ GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), dst_size.width(), true,
+ quality == GLHelper::SCALER_QUALITY_GOOD,
+ &x_ops);
+ GLHelperScaling::ScaleOp::AddOps(
+ src_subrect.height(), dst_size.height(), false,
+ quality == GLHelper::SCALER_QUALITY_GOOD, &y_ops);
+
+ ConvertScalerOpsToScalerStages(quality, src_size, src_subrect, dst_size,
+ vertically_flip_texture, swizzle, &x_ops,
+ &y_ops, scaler_stages);
+}
+
+GLHelper::ScalerInterface* GLHelperScaling::CreateScaler(
+ GLHelper::ScalerQuality quality,
+ gfx::Size src_size,
+ gfx::Rect src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle) {
+ std::vector<ScalerStage> scaler_stages;
+ ComputeScalerStages(quality, src_size, src_subrect, dst_size,
+ vertically_flip_texture, swizzle, &scaler_stages);
+
+ ScalerImpl* ret = NULL;
+ for (unsigned int i = 0; i < scaler_stages.size(); i++) {
+ ret = new ScalerImpl(gl_, this, scaler_stages[i], ret, NULL);
+ }
+ return ret;
+}
+
+GLHelper::ScalerInterface* GLHelperScaling::CreatePlanarScaler(
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ const float color_weights[4]) {
+ ScalerStage stage(SHADER_PLANAR, src_size, src_subrect, dst_size, true,
+ vertically_flip_texture, swizzle);
+ return new ScalerImpl(gl_, this, stage, NULL, color_weights);
+}
+
+GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader(
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ ShaderType shader) {
+ DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2);
+ ScalerStage stage(shader, src_size, src_subrect, dst_size, true,
+ vertically_flip_texture, swizzle);
+ return new ScalerImpl(gl_, this, stage, NULL, NULL);
+}
+
+const GLfloat GLHelperScaling::kVertexAttributes[] = {
+ -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0
+ 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1
+ -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2
+ 1.0f, 1.0f, 1.0f, 1.0f,
+}; // vertex 3
+
+void GLHelperScaling::InitBuffer() {
+ ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(gl_,
+ vertex_attributes_buffer_);
+ gl_->BufferData(GL_ARRAY_BUFFER, sizeof(kVertexAttributes), kVertexAttributes,
+ GL_STATIC_DRAW);
+}
+
+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()) {
+ cache_entry = new ShaderProgram(gl_, helper_);
+ std::basic_string<GLchar> vertex_program;
+ std::basic_string<GLchar> fragment_program;
+ std::basic_string<GLchar> vertex_header;
+ std::basic_string<GLchar> fragment_directives;
+ std::basic_string<GLchar> fragment_header;
+ std::basic_string<GLchar> shared_variables;
+
+ vertex_header.append(
+ "precision highp float;\n"
+ "attribute vec2 a_position;\n"
+ "attribute vec2 a_texcoord;\n"
+ "uniform vec4 src_subrect;\n");
+
+ fragment_header.append(
+ "precision mediump float;\n"
+ "uniform sampler2D s_texture;\n");
+
+ vertex_program.append(
+ " gl_Position = vec4(a_position, 0.0, 1.0);\n"
+ " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n");
+
+ switch (type) {
+ case SHADER_BILINEAR:
+ shared_variables.append("varying vec2 v_texcoord;\n");
+ vertex_program.append(" v_texcoord = texcoord;\n");
+ fragment_program.append(
+ " gl_FragColor = texture2D(s_texture, v_texcoord);\n");
+ break;
+
+ case SHADER_BILINEAR2:
+ // This is equivialent to two passes of the BILINEAR shader above.
+ // It can be used to scale an image down 1.0x-2.0x in either dimension,
+ // or exactly 4x.
+ shared_variables.append(
+ "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad
+ vertex_header.append(
+ "uniform vec2 scaling_vector;\n"
+ "uniform vec2 dst_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
+ " step /= 4.0;\n"
+ " v_texcoords.xy = texcoord + step;\n"
+ " v_texcoords.zw = texcoord - step;\n");
+
+ fragment_program.append(
+ " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n"
+ " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n");
+ break;
+
+ case SHADER_BILINEAR3:
+ // This is kind of like doing 1.5 passes of the BILINEAR shader.
+ // It can be used to scale an image down 1.5x-3.0x, or exactly 6x.
+ shared_variables.append(
+ "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad
+ "varying vec2 v_texcoords2;\n");
+ vertex_header.append(
+ "uniform vec2 scaling_vector;\n"
+ "uniform vec2 dst_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
+ " step /= 3.0;\n"
+ " v_texcoords1.xy = texcoord + step;\n"
+ " v_texcoords1.zw = texcoord;\n"
+ " v_texcoords2 = texcoord - step;\n");
+ fragment_program.append(
+ " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n"
+ " texture2D(s_texture, v_texcoords1.zw) +\n"
+ " texture2D(s_texture, v_texcoords2)) / 3.0;\n");
+ break;
+
+ case SHADER_BILINEAR4:
+ // This is equivialent to three passes of the BILINEAR shader above,
+ // It can be used to scale an image down 2.0x-4.0x or exactly 8x.
+ shared_variables.append("varying vec4 v_texcoords[2];\n");
+ vertex_header.append(
+ "uniform vec2 scaling_vector;\n"
+ "uniform vec2 dst_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
+ " step /= 8.0;\n"
+ " v_texcoords[0].xy = texcoord - step * 3.0;\n"
+ " v_texcoords[0].zw = texcoord - step;\n"
+ " v_texcoords[1].xy = texcoord + step;\n"
+ " v_texcoords[1].zw = texcoord + step * 3.0;\n");
+ fragment_program.append(
+ " gl_FragColor = (\n"
+ " texture2D(s_texture, v_texcoords[0].xy) +\n"
+ " texture2D(s_texture, v_texcoords[0].zw) +\n"
+ " texture2D(s_texture, v_texcoords[1].xy) +\n"
+ " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
+ break;
+
+ case SHADER_BILINEAR2X2:
+ // This is equivialent to four passes of the BILINEAR shader above.
+ // Two in each dimension. It can be used to scale an image down
+ // 1.0x-2.0x in both X and Y directions. Or, it could be used to
+ // scale an image down by exactly 4x in both dimensions.
+ shared_variables.append("varying vec4 v_texcoords[2];\n");
+ vertex_header.append("uniform vec2 dst_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n"
+ " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
+ " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
+ " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
+ " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
+ fragment_program.append(
+ " gl_FragColor = (\n"
+ " texture2D(s_texture, v_texcoords[0].xy) +\n"
+ " texture2D(s_texture, v_texcoords[0].zw) +\n"
+ " texture2D(s_texture, v_texcoords[1].xy) +\n"
+ " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
+ break;
+
+ case SHADER_BICUBIC_HALF_1D:
+ // This scales down texture by exactly half in one dimension.
+ // directions in one pass. We use bilinear lookup to reduce
+ // the number of texture reads from 8 to 4
+ shared_variables.append(
+ "const float CenterDist = 99.0 / 140.0;\n"
+ "const float LobeDist = 11.0 / 4.0;\n"
+ "const float CenterWeight = 35.0 / 64.0;\n"
+ "const float LobeWeight = -3.0 / 64.0;\n"
+ "varying vec4 v_texcoords[2];\n");
+ vertex_header.append(
+ "uniform vec2 scaling_vector;\n"
+ "uniform vec2 src_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n"
+ " v_texcoords[0].xy = texcoord - LobeDist * step;\n"
+ " v_texcoords[0].zw = texcoord - CenterDist * step;\n"
+ " v_texcoords[1].xy = texcoord + CenterDist * step;\n"
+ " v_texcoords[1].zw = texcoord + LobeDist * step;\n");
+ fragment_program.append(
+ " gl_FragColor = \n"
+ // Lobe pixels
+ " (texture2D(s_texture, v_texcoords[0].xy) +\n"
+ " texture2D(s_texture, v_texcoords[1].zw)) *\n"
+ " LobeWeight +\n"
+ // Center pixels
+ " (texture2D(s_texture, v_texcoords[0].zw) +\n"
+ " texture2D(s_texture, v_texcoords[1].xy)) *\n"
+ " CenterWeight;\n");
+ break;
+
+ case SHADER_BICUBIC_UPSCALE:
+ // When scaling up, we need 4 texture reads, but we can
+ // save some instructions because will know in which range of
+ // the bicubic function each call call to the bicubic function
+ // will be in.
+ // Also, when sampling the bicubic function like this, the sum
+ // is always exactly one, so we can skip normalization as well.
+ shared_variables.append("varying vec2 v_texcoord;\n");
+ vertex_program.append(" v_texcoord = texcoord;\n");
+ fragment_header.append(
+ "uniform vec2 src_pixelsize;\n"
+ "uniform vec2 scaling_vector;\n"
+ "const float a = -0.5;\n"
+ // This function is equivialent to calling the bicubic
+ // function with x-1, x, 1-x and 2-x
+ // (assuming 0 <= x < 1)
+ "vec4 filt4(float x) {\n"
+ " return vec4(x * x * x, x * x, x, 1) *\n"
+ " mat4( a, -2.0 * a, a, 0.0,\n"
+ " a + 2.0, -a - 3.0, 0.0, 1.0,\n"
+ " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n"
+ " -a, a, 0.0, 0.0);\n"
+ "}\n"
+ "mat4 pixels_x(vec2 pos, vec2 step) {\n"
+ " return mat4(\n"
+ " texture2D(s_texture, pos - step),\n"
+ " texture2D(s_texture, pos),\n"
+ " texture2D(s_texture, pos + step),\n"
+ " texture2D(s_texture, pos + step * 2.0));\n"
+ "}\n");
+ fragment_program.append(
+ " vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
+ " scaling_vector / 2.0;\n"
+ " float frac = fract(dot(pixel_pos, scaling_vector));\n"
+ " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
+ " vec2 step = scaling_vector / src_pixelsize;\n"
+ " gl_FragColor = pixels_x(base, step) * filt4(frac);\n");
+ break;
+
+ case SHADER_PLANAR:
+ // Converts four RGBA pixels into one pixel. Each RGBA
+ // pixel will be dot-multiplied with the color weights and
+ // then placed into a component of the output. This is used to
+ // convert RGBA textures into Y, U and V textures. We do this
+ // because single-component textures are not renderable on all
+ // architectures.
+ shared_variables.append("varying vec4 v_texcoords[2];\n");
+ vertex_header.append(
+ "uniform vec2 scaling_vector;\n"
+ "uniform vec2 dst_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
+ " step /= 4.0;\n"
+ " v_texcoords[0].xy = texcoord - step * 1.5;\n"
+ " v_texcoords[0].zw = texcoord - step * 0.5;\n"
+ " v_texcoords[1].xy = texcoord + step * 0.5;\n"
+ " v_texcoords[1].zw = texcoord + step * 1.5;\n");
+ fragment_header.append("uniform vec4 color_weights;\n");
+ fragment_program.append(
+ " gl_FragColor = color_weights * mat4(\n"
+ " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n"
+ " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n"
+ " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n"
+ " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n");
+ break;
+
+ case SHADER_YUV_MRT_PASS1:
+ // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
+ //
+ // YV12 is full-resolution luma and half-resolution blue/red chroma.
+ //
+ // (original)
+ // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
+ // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
+ // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
+ // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
+ // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
+ // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
+ // |
+ // | (y plane) (temporary)
+ // | YYYY YYYY UUVV UUVV
+ // +--> { YYYY YYYY + UUVV UUVV }
+ // YYYY YYYY UUVV UUVV
+ // First YYYY YYYY UUVV UUVV
+ // pass YYYY YYYY UUVV UUVV
+ // YYYY YYYY UUVV UUVV
+ // |
+ // | (u plane) (v plane)
+ // Second | UUUU VVVV
+ // pass +--> { UUUU + VVVV }
+ // UUUU VVVV
+ //
+ shared_variables.append("varying vec4 v_texcoords[2];\n");
+ vertex_header.append(
+ "uniform vec2 scaling_vector;\n"
+ "uniform vec2 dst_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
+ " step /= 4.0;\n"
+ " v_texcoords[0].xy = texcoord - step * 1.5;\n"
+ " v_texcoords[0].zw = texcoord - step * 0.5;\n"
+ " v_texcoords[1].xy = texcoord + step * 0.5;\n"
+ " v_texcoords[1].zw = texcoord + step * 1.5;\n");
+ fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n");
+ fragment_header.append(
+ "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n"
+ "const float kYBias = 0.0625;\n"
+ // Divide U and V by two to compensate for averaging below.
+ "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n"
+ "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n"
+ "const float kUVBias = 0.5;\n");
+ fragment_program.append(
+ " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
+ " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
+ " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
+ " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
+ " vec3 pixel12 = pixel1 + pixel2;\n"
+ " vec3 pixel34 = pixel3 + pixel4;\n"
+ " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n"
+ " dot(pixel2, kRGBtoY),\n"
+ " dot(pixel3, kRGBtoY),\n"
+ " dot(pixel4, kRGBtoY)) + kYBias;\n"
+ " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n"
+ " dot(pixel34, kRGBtoU),\n"
+ " dot(pixel12, kRGBtoV),\n"
+ " dot(pixel34, kRGBtoV)) + kUVBias;\n");
+ break;
+
+ case SHADER_YUV_MRT_PASS2:
+ // We're just sampling two pixels and unswizzling them. There's
+ // no need to do vertical scaling with math, since bilinear
+ // interpolation in the sampler takes care of that.
+ shared_variables.append("varying vec4 v_texcoords;\n");
+ vertex_header.append(
+ "uniform vec2 scaling_vector;\n"
+ "uniform vec2 dst_pixelsize;\n");
+ vertex_program.append(
+ " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
+ " step /= 2.0;\n"
+ " v_texcoords.xy = texcoord - step * 0.5;\n"
+ " v_texcoords.zw = texcoord + step * 0.5;\n");
+ fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n");
+ fragment_program.append(
+ " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n"
+ " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n"
+ " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n"
+ " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n");
+ break;
+ }
+ if (swizzle) {
+ switch (type) {
+ case SHADER_YUV_MRT_PASS1:
+ fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n");
+ break;
+ case SHADER_YUV_MRT_PASS2:
+ fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n");
+ fragment_program.append(" gl_FragData[1] = gl_FragData[1].bgra;\n");
+ break;
+ default:
+ fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n");
+ break;
+ }
+ }
+
+ vertex_program = vertex_header + shared_variables + "void main() {\n" +
+ vertex_program + "}\n";
+
+ fragment_program = fragment_directives + fragment_header +
+ shared_variables + "void main() {\n" + fragment_program +
+ "}\n";
+
+ cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str());
+ }
+ return cache_entry;
+}
+
+void ShaderProgram::Setup(const GLchar* vertex_shader_text,
+ const GLchar* fragment_shader_text) {
+ // Shaders to map the source texture to |dst_texture_|.
+ GLuint vertex_shader =
+ helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER);
+ if (vertex_shader == 0)
+ return;
+
+ gl_->AttachShader(program_, vertex_shader);
+ gl_->DeleteShader(vertex_shader);
+
+ GLuint fragment_shader = helper_->CompileShaderFromSource(
+ fragment_shader_text, GL_FRAGMENT_SHADER);
+ if (fragment_shader == 0)
+ return;
+ gl_->AttachShader(program_, fragment_shader);
+ gl_->DeleteShader(fragment_shader);
+
+ gl_->LinkProgram(program_);
+
+ GLint link_status = 0;
+ gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
+ if (!link_status)
+ return;
+
+ position_location_ = gl_->GetAttribLocation(program_, "a_position");
+ texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
+ texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
+ src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect");
+ src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize");
+ dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize");
+ scaling_vector_location_ =
+ gl_->GetUniformLocation(program_, "scaling_vector");
+ color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights");
+ // The only reason fetching these attribute locations should fail is
+ // if the context was spontaneously lost (i.e., because the GPU
+ // process crashed, perhaps deliberately for testing).
+ DCHECK(Initialized() || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR);
+}
+
+void ShaderProgram::UseProgram(const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool scale_x,
+ bool flip_y,
+ GLfloat color_weights[4]) {
+ gl_->UseProgram(program_);
+
+ // OpenGL defines the last parameter to VertexAttribPointer as type
+ // "const GLvoid*" even though it is actually an offset into the buffer
+ // object's data store and not a pointer to the client's address space.
+ const void* offsets[2] = {0,
+ reinterpret_cast<const void*>(2 * sizeof(GLfloat))};
+
+ gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE,
+ 4 * sizeof(GLfloat), offsets[0]);
+ gl_->EnableVertexAttribArray(position_location_);
+
+ gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE,
+ 4 * sizeof(GLfloat), offsets[1]);
+ gl_->EnableVertexAttribArray(texcoord_location_);
+
+ gl_->Uniform1i(texture_location_, 0);
+
+ // Convert |src_subrect| to texture coordinates.
+ GLfloat src_subrect_texcoord[] = {
+ static_cast<float>(src_subrect.x()) / src_size.width(),
+ static_cast<float>(src_subrect.y()) / src_size.height(),
+ static_cast<float>(src_subrect.width()) / src_size.width(),
+ static_cast<float>(src_subrect.height()) / src_size.height(),
+ };
+ if (flip_y) {
+ src_subrect_texcoord[1] += src_subrect_texcoord[3];
+ src_subrect_texcoord[3] *= -1.0;
+ }
+ gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord);
+
+ gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height());
+ gl_->Uniform2f(dst_pixelsize_location_, static_cast<float>(dst_size.width()),
+ static_cast<float>(dst_size.height()));
+
+ gl_->Uniform2f(scaling_vector_location_, scale_x ? 1.0 : 0.0,
+ scale_x ? 0.0 : 1.0);
+ gl_->Uniform4fv(color_weights_location_, 1, color_weights);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gl_helper_scaling.h b/chromium/components/viz/common/gl_helper_scaling.h
new file mode 100644
index 00000000000..a37ea919ee5
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper_scaling.h
@@ -0,0 +1,208 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_
+#define COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_
+
+#include <deque>
+#include <map>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/viz/common/gl_helper.h"
+#include "components/viz/common/viz_common_export.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace viz {
+
+class ShaderProgram;
+class ScalerImpl;
+class GLHelperTest;
+
+// Implements GPU texture scaling methods.
+// Note that you should probably not use this class directly.
+// See gl_helper.cc::CreateScaler instead.
+class VIZ_COMMON_EXPORT GLHelperScaling {
+ public:
+ enum ShaderType {
+ SHADER_BILINEAR,
+ SHADER_BILINEAR2,
+ SHADER_BILINEAR3,
+ SHADER_BILINEAR4,
+ SHADER_BILINEAR2X2,
+ SHADER_BICUBIC_UPSCALE,
+ SHADER_BICUBIC_HALF_1D,
+ SHADER_PLANAR,
+ SHADER_YUV_MRT_PASS1,
+ SHADER_YUV_MRT_PASS2,
+ };
+
+ // Similar to ScalerInterface, but can generate multiple outputs.
+ // Used for YUV conversion in gl_helper.c
+ class ShaderInterface {
+ public:
+ ShaderInterface() {}
+ virtual ~ShaderInterface() {}
+ // Note that the src_texture will have the min/mag filter set to GL_LINEAR
+ // and wrap_s/t set to CLAMP_TO_EDGE in this call.
+ virtual void Execute(GLuint source_texture,
+ const std::vector<GLuint>& dest_textures) = 0;
+ };
+
+ using ShaderProgramKeyType = std::pair<ShaderType, bool>;
+
+ GLHelperScaling(gpu::gles2::GLES2Interface* gl, GLHelper* helper);
+ ~GLHelperScaling();
+ void InitBuffer();
+
+ GLHelper::ScalerInterface* CreateScaler(GLHelper::ScalerQuality quality,
+ gfx::Size src_size,
+ gfx::Rect src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle);
+
+ GLHelper::ScalerInterface* CreatePlanarScaler(const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ const float color_weights[4]);
+
+ ShaderInterface* CreateYuvMrtShader(const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ ShaderType shader);
+
+ private:
+ // A ScaleOp represents a pass in a scaler pipeline, in one dimension.
+ // Note that when quality is GOOD, multiple scaler passes will be
+ // combined into one operation for increased performance.
+ // Exposed in the header file for testing purposes.
+ struct ScaleOp {
+ ScaleOp(int factor, bool x, int size)
+ : scale_factor(factor), scale_x(x), scale_size(size) {}
+
+ // Calculate a set of ScaleOp needed to convert an image of size
+ // |src| into an image of size |dst|. If |scale_x| is true, then
+ // the calculations are for the X axis of the image, otherwise Y.
+ // If |allow3| is true, we can use a SHADER_BILINEAR3 to replace
+ // a scale up and scale down with a 3-tap bilinear scale.
+ // The calculated ScaleOps are added to |ops|.
+ static void AddOps(int src,
+ int dst,
+ bool scale_x,
+ bool allow3,
+ std::deque<ScaleOp>* ops) {
+ int num_downscales = 0;
+ if (allow3 && dst * 3 >= src && dst * 2 < src) {
+ // Technically, this should be a scale up and then a
+ // scale down, but it makes the optimization code more
+ // complicated.
+ ops->push_back(ScaleOp(3, scale_x, dst));
+ return;
+ }
+ while ((dst << num_downscales) < src) {
+ num_downscales++;
+ }
+ if ((dst << num_downscales) != src) {
+ ops->push_back(ScaleOp(0, scale_x, dst << num_downscales));
+ }
+ while (num_downscales) {
+ num_downscales--;
+ ops->push_back(ScaleOp(2, scale_x, dst << num_downscales));
+ }
+ }
+
+ // Update |size| to its new size. Before calling this function
+ // |size| should be the size of the input image. After calling it,
+ // |size| will be the size of the image after this particular
+ // scaling operation.
+ void UpdateSize(gfx::Size* subrect) {
+ if (scale_x) {
+ subrect->set_width(scale_size);
+ } else {
+ subrect->set_height(scale_size);
+ }
+ }
+
+ // A scale factor of 0 means upscale
+ // 2 means 50% scale
+ // 3 means 33% scale, etc.
+ int scale_factor;
+ bool scale_x; // Otherwise y
+ int scale_size; // Size to scale to.
+ };
+
+ // Full specification for a single scaling stage.
+ struct ScalerStage {
+ ScalerStage(ShaderType shader_,
+ gfx::Size src_size_,
+ gfx::Rect src_subrect_,
+ gfx::Size dst_size_,
+ bool scale_x_,
+ bool vertically_flip_texture_,
+ bool swizzle_);
+ ScalerStage(const ScalerStage& other);
+ ShaderType shader;
+ gfx::Size src_size;
+ gfx::Rect src_subrect;
+ gfx::Size dst_size;
+ bool scale_x;
+ bool vertically_flip_texture;
+ bool swizzle;
+ };
+
+ // Compute a vector of scaler stages for a particular
+ // set of input/output parameters.
+ void ComputeScalerStages(GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ std::vector<ScalerStage>* scaler_stages);
+
+ // Take two queues of ScaleOp structs and generate a
+ // vector of scaler stages. This is the second half of
+ // ComputeScalerStages.
+ void ConvertScalerOpsToScalerStages(
+ GLHelper::ScalerQuality quality,
+ gfx::Size src_size,
+ gfx::Rect src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ bool swizzle,
+ std::deque<GLHelperScaling::ScaleOp>* x_ops,
+ std::deque<GLHelperScaling::ScaleOp>* y_ops,
+ std::vector<ScalerStage>* scaler_stages);
+
+ scoped_refptr<ShaderProgram> GetShaderProgram(ShaderType type, bool swizzle);
+
+ // Interleaved array of 2-dimentional vertex positions (x, y) and
+ // 2-dimentional texture coordinates (s, t).
+ static const GLfloat kVertexAttributes[];
+
+ gpu::gles2::GLES2Interface* gl_;
+ GLHelper* helper_;
+
+ // The buffer that holds the vertices and the texture coordinates data for
+ // drawing a quad.
+ ScopedBuffer vertex_attributes_buffer_;
+
+ std::map<ShaderProgramKeyType, scoped_refptr<ShaderProgram>> shader_programs_;
+
+ friend class ShaderProgram;
+ friend class ScalerImpl;
+ friend class GLHelperBenchmark;
+ friend class GLHelperTest;
+ DISALLOW_COPY_AND_ASSIGN(GLHelperScaling);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_
diff --git a/chromium/components/viz/common/gl_helper_unittest.cc b/chromium/components/viz/common/gl_helper_unittest.cc
new file mode 100644
index 00000000000..34192b4837c
--- /dev/null
+++ b/chromium/components/viz/common/gl_helper_unittest.cc
@@ -0,0 +1,1425 @@
+// 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 <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <cmath>
+#include <string>
+#include <vector>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/viz/common/gl_helper.h"
+#include "components/viz/common/gl_helper_readback_support.h"
+#include "components/viz/common/gl_helper_scaling.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/ipc/gl_in_process_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+#if !defined(OS_ANDROID)
+
+namespace viz {
+
+GLHelper::ScalerQuality kQualities[] = {
+ GLHelper::SCALER_QUALITY_BEST, GLHelper::SCALER_QUALITY_GOOD,
+ GLHelper::SCALER_QUALITY_FAST,
+};
+
+const char* kQualityNames[] = {
+ "best", "good", "fast",
+};
+
+class GLHelperTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ gpu::gles2::ContextCreationAttribHelper attributes;
+ attributes.alpha_size = 8;
+ attributes.depth_size = 24;
+ attributes.red_size = 8;
+ attributes.green_size = 8;
+ attributes.blue_size = 8;
+ attributes.stencil_size = 8;
+ attributes.samples = 4;
+ attributes.sample_buffers = 1;
+ attributes.bind_generates_resource = false;
+
+ context_.reset(
+ gpu::GLInProcessContext::Create(nullptr, /* service */
+ nullptr, /* surface */
+ true, /* offscreen */
+ gpu::kNullSurfaceHandle, /* window */
+ nullptr, /* share_context */
+ attributes, gpu::SharedMemoryLimits(),
+ nullptr, /* gpu_memory_buffer_manager */
+ nullptr, /* image_factory */
+ base::ThreadTaskRunnerHandle::Get()));
+ gl_ = context_->GetImplementation();
+ gpu::ContextSupport* support = context_->GetImplementation();
+
+ helper_.reset(new GLHelper(gl_, support));
+ helper_scaling_.reset(new GLHelperScaling(gl_, helper_.get()));
+ }
+
+ void TearDown() override {
+ helper_scaling_.reset(nullptr);
+ helper_.reset(nullptr);
+ context_.reset(nullptr);
+ }
+
+ // Bicubic filter kernel function.
+ static float Bicubic(float x) {
+ const float a = -0.5;
+ x = std::abs(x);
+ float x2 = x * x;
+ float x3 = x2 * x;
+ if (x <= 1) {
+ return (a + 2) * x3 - (a + 3) * x2 + 1;
+ } else if (x < 2) {
+ return a * x3 - 5 * a * x2 + 8 * a * x - 4 * a;
+ } else {
+ return 0.0f;
+ }
+ }
+
+ // Look up a single channel value. Works for 4-channel and single channel
+ // bitmaps. Clamp x/y.
+ int Channel(SkBitmap* pixels, int x, int y, int c) {
+ if (pixels->bytesPerPixel() == 4) {
+ uint32_t* data =
+ pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
+ std::max(0, std::min(y, pixels->height() - 1)));
+ return (*data) >> (c * 8) & 0xff;
+ } else {
+ DCHECK_EQ(pixels->bytesPerPixel(), 1);
+ DCHECK_EQ(c, 0);
+ return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)),
+ std::max(0, std::min(y, pixels->height() - 1)));
+ }
+ }
+
+ // Set a single channel value. Works for 4-channel and single channel
+ // bitmaps. Clamp x/y.
+ void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
+ DCHECK_GE(x, 0);
+ DCHECK_GE(y, 0);
+ DCHECK_LT(x, pixels->width());
+ DCHECK_LT(y, pixels->height());
+ if (pixels->bytesPerPixel() == 4) {
+ uint32_t* data = pixels->getAddr32(x, y);
+ v = std::max(0, std::min(v, 255));
+ *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
+ } else {
+ DCHECK_EQ(pixels->bytesPerPixel(), 1);
+ DCHECK_EQ(c, 0);
+ uint8_t* data = pixels->getAddr8(x, y);
+ v = std::max(0, std::min(v, 255));
+ *data = v;
+ }
+ }
+
+ // Print all the R, G, B or A values from an SkBitmap in a
+ // human-readable format.
+ void PrintChannel(SkBitmap* pixels, int c) {
+ for (int y = 0; y < pixels->height(); y++) {
+ std::string formatted;
+ for (int x = 0; x < pixels->width(); x++) {
+ formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c)));
+ }
+ LOG(ERROR) << formatted;
+ }
+ }
+
+ // Print out the individual steps of a scaler pipeline.
+ std::string PrintStages(
+ const std::vector<GLHelperScaling::ScalerStage>& scaler_stages) {
+ std::string ret;
+ for (size_t i = 0; i < scaler_stages.size(); i++) {
+ ret.append(base::StringPrintf(
+ "%dx%d -> %dx%d ", scaler_stages[i].src_size.width(),
+ scaler_stages[i].src_size.height(), scaler_stages[i].dst_size.width(),
+ scaler_stages[i].dst_size.height()));
+ bool xy_matters = false;
+ switch (scaler_stages[i].shader) {
+ case GLHelperScaling::SHADER_BILINEAR:
+ ret.append("bilinear");
+ break;
+ case GLHelperScaling::SHADER_BILINEAR2:
+ ret.append("bilinear2");
+ xy_matters = true;
+ break;
+ case GLHelperScaling::SHADER_BILINEAR3:
+ ret.append("bilinear3");
+ xy_matters = true;
+ break;
+ case GLHelperScaling::SHADER_BILINEAR4:
+ ret.append("bilinear4");
+ xy_matters = true;
+ break;
+ case GLHelperScaling::SHADER_BILINEAR2X2:
+ ret.append("bilinear2x2");
+ break;
+ case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
+ ret.append("bicubic upscale");
+ xy_matters = true;
+ break;
+ case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
+ ret.append("bicubic 1/2");
+ xy_matters = true;
+ break;
+ case GLHelperScaling::SHADER_PLANAR:
+ ret.append("planar");
+ break;
+ case GLHelperScaling::SHADER_YUV_MRT_PASS1:
+ ret.append("rgb2yuv pass 1");
+ break;
+ case GLHelperScaling::SHADER_YUV_MRT_PASS2:
+ ret.append("rgb2yuv pass 2");
+ break;
+ }
+
+ if (xy_matters) {
+ if (scaler_stages[i].scale_x) {
+ ret.append(" X");
+ } else {
+ ret.append(" Y");
+ }
+ }
+ ret.append("\n");
+ }
+ return ret;
+ }
+
+ bool CheckScale(double scale, int samples, bool already_scaled) {
+ // 1:1 is valid if there is one sample.
+ if (samples == 1 && scale == 1.0) {
+ return true;
+ }
+ // Is it an exact down-scale (50%, 25%, etc.?)
+ if (scale == 2.0 * samples) {
+ return true;
+ }
+ // Upscales, only valid if we haven't already scaled in this dimension.
+ if (!already_scaled) {
+ // Is it a valid bilinear upscale?
+ if (samples == 1 && scale <= 1.0) {
+ return true;
+ }
+ // Multi-sample upscale-downscale combination?
+ if (scale > samples / 2.0 && scale < samples) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Make sure that the stages of the scaler pipeline are sane.
+ void ValidateScalerStages(
+ GLHelper::ScalerQuality quality,
+ const std::vector<GLHelperScaling::ScalerStage>& scaler_stages,
+ const gfx::Size& dst_size,
+ const std::string& message) {
+ bool previous_error = HasFailure();
+ // First, check that the input size for each stage is equal to
+ // the output size of the previous stage.
+ for (size_t i = 1; i < scaler_stages.size(); i++) {
+ EXPECT_EQ(scaler_stages[i - 1].dst_size.width(),
+ scaler_stages[i].src_size.width());
+ EXPECT_EQ(scaler_stages[i - 1].dst_size.height(),
+ scaler_stages[i].src_size.height());
+ EXPECT_EQ(scaler_stages[i].src_subrect.x(), 0);
+ EXPECT_EQ(scaler_stages[i].src_subrect.y(), 0);
+ EXPECT_EQ(scaler_stages[i].src_subrect.width(),
+ scaler_stages[i].src_size.width());
+ EXPECT_EQ(scaler_stages[i].src_subrect.height(),
+ scaler_stages[i].src_size.height());
+ }
+
+ // Check the output size matches the destination of the last stage
+ EXPECT_EQ(scaler_stages.back().dst_size.width(), dst_size.width());
+ EXPECT_EQ(scaler_stages.back().dst_size.height(), dst_size.height());
+
+ // Used to verify that up-scales are not attempted after some
+ // other scale.
+ bool scaled_x = false;
+ bool scaled_y = false;
+
+ for (size_t i = 0; i < scaler_stages.size(); i++) {
+ // Note: 2.0 means scaling down by 50%
+ double x_scale =
+ static_cast<double>(scaler_stages[i].src_subrect.width()) /
+ static_cast<double>(scaler_stages[i].dst_size.width());
+ double y_scale =
+ static_cast<double>(scaler_stages[i].src_subrect.height()) /
+ static_cast<double>(scaler_stages[i].dst_size.height());
+
+ int x_samples = 0;
+ int y_samples = 0;
+
+ // Codify valid scale operations.
+ switch (scaler_stages[i].shader) {
+ case GLHelperScaling::SHADER_PLANAR:
+ case GLHelperScaling::SHADER_YUV_MRT_PASS1:
+ case GLHelperScaling::SHADER_YUV_MRT_PASS2:
+ EXPECT_TRUE(false) << "Invalid shader.";
+ break;
+
+ case GLHelperScaling::SHADER_BILINEAR:
+ if (quality != GLHelper::SCALER_QUALITY_FAST) {
+ x_samples = 1;
+ y_samples = 1;
+ }
+ break;
+ case GLHelperScaling::SHADER_BILINEAR2:
+ x_samples = 2;
+ y_samples = 1;
+ break;
+ case GLHelperScaling::SHADER_BILINEAR3:
+ x_samples = 3;
+ y_samples = 1;
+ break;
+ case GLHelperScaling::SHADER_BILINEAR4:
+ x_samples = 4;
+ y_samples = 1;
+ break;
+ case GLHelperScaling::SHADER_BILINEAR2X2:
+ x_samples = 2;
+ y_samples = 2;
+ break;
+ case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
+ if (scaler_stages[i].scale_x) {
+ EXPECT_LT(x_scale, 1.0);
+ EXPECT_EQ(y_scale, 1.0);
+ } else {
+ EXPECT_EQ(x_scale, 1.0);
+ EXPECT_LT(y_scale, 1.0);
+ }
+ break;
+ case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
+ if (scaler_stages[i].scale_x) {
+ EXPECT_EQ(x_scale, 2.0);
+ EXPECT_EQ(y_scale, 1.0);
+ } else {
+ EXPECT_EQ(x_scale, 1.0);
+ EXPECT_EQ(y_scale, 2.0);
+ }
+ break;
+ }
+
+ if (!scaler_stages[i].scale_x) {
+ std::swap(x_samples, y_samples);
+ }
+
+ if (x_samples) {
+ EXPECT_TRUE(CheckScale(x_scale, x_samples, scaled_x))
+ << "x_scale = " << x_scale;
+ }
+ if (y_samples) {
+ EXPECT_TRUE(CheckScale(y_scale, y_samples, scaled_y))
+ << "y_scale = " << y_scale;
+ }
+
+ if (x_scale != 1.0) {
+ scaled_x = true;
+ }
+ if (y_scale != 1.0) {
+ scaled_y = true;
+ }
+ }
+
+ if (HasFailure() && !previous_error) {
+ LOG(ERROR) << "Invalid scaler stages: " << message;
+ LOG(ERROR) << "Scaler stages:";
+ LOG(ERROR) << PrintStages(scaler_stages);
+ }
+ }
+
+ // Compares two bitmaps taking color types into account. Checks whether each
+ // component of each pixel is no more than |maxdiff| apart. If bitmaps are not
+ // similar enough, prints out |truth|, |other|, |source|, |scaler_stages|
+ // and |message|.
+ void Compare(SkBitmap* truth,
+ SkBitmap* other,
+ int maxdiff,
+ SkBitmap* source,
+ const std::vector<GLHelperScaling::ScalerStage>& scaler_stages,
+ std::string message) {
+ EXPECT_EQ(truth->width(), other->width());
+ EXPECT_EQ(truth->height(), other->height());
+ bool swizzle = (truth->colorType() == kRGBA_8888_SkColorType &&
+ other->colorType() == kBGRA_8888_SkColorType) ||
+ (truth->colorType() == kBGRA_8888_SkColorType &&
+ other->colorType() == kRGBA_8888_SkColorType);
+ EXPECT_TRUE(swizzle || truth->colorType() == other->colorType());
+ int bpp = truth->bytesPerPixel();
+ for (int x = 0; x < truth->width(); x++) {
+ for (int y = 0; y < truth->height(); y++) {
+ for (int c = 0; c < bpp; c++) {
+ int a = Channel(truth, x, y, c);
+ // swizzle when comparing if needed
+ int b = swizzle && (c == 0 || c == 2)
+ ? Channel(other, x, y, (c + 2) & 2)
+ : Channel(other, x, y, c);
+ EXPECT_NEAR(a, b, maxdiff)
+ << " x=" << x << " y=" << y << " c=" << c << " " << message;
+ if (std::abs(a - b) > maxdiff) {
+ LOG(ERROR) << "-------expected--------";
+ for (int i = 0; i < bpp; i++) {
+ LOG(ERROR) << "Channel " << i << ":";
+ PrintChannel(truth, i);
+ }
+ LOG(ERROR) << "-------actual--------";
+ for (int i = 0; i < bpp; i++) {
+ LOG(ERROR) << "Channel " << i << ":";
+ PrintChannel(other, i);
+ }
+ if (source) {
+ LOG(ERROR) << "-------original--------";
+ for (int i = 0; i < source->bytesPerPixel(); i++) {
+ LOG(ERROR) << "Channel " << i << ":";
+ PrintChannel(source, i);
+ }
+ }
+ LOG(ERROR) << "-----Scaler stages------";
+ LOG(ERROR) << PrintStages(scaler_stages);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // Get a single R, G, B or A value as a float.
+ float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) {
+ return Channel(pixels, x, y, c) / 255.0;
+ }
+
+ // Works like a GL_LINEAR lookup on an SkBitmap.
+ float Bilinear(SkBitmap* pixels, float x, float y, int c) {
+ x -= 0.5;
+ y -= 0.5;
+ int base_x = static_cast<int>(floorf(x));
+ int base_y = static_cast<int>(floorf(y));
+ x -= base_x;
+ y -= base_y;
+ return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) +
+ ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) +
+ ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y +
+ ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
+ }
+
+ // Encodes an RGBA bitmap to grayscale.
+ // Reference implementation for
+ // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale.
+ void EncodeToGrayscaleSlow(SkBitmap* input, SkBitmap* output) {
+ const float kRGBtoGrayscaleColorWeights[3] = {0.213f, 0.715f, 0.072f};
+ CHECK_EQ(kAlpha_8_SkColorType, output->colorType());
+ CHECK_EQ(input->width(), output->width());
+ CHECK_EQ(input->height(), output->height());
+ CHECK_EQ(input->colorType(), kRGBA_8888_SkColorType);
+
+ for (int dst_y = 0; dst_y < output->height(); dst_y++) {
+ for (int dst_x = 0; dst_x < output->width(); dst_x++) {
+ float c0 = ChannelAsFloat(input, dst_x, dst_y, 0);
+ float c1 = ChannelAsFloat(input, dst_x, dst_y, 1);
+ float c2 = ChannelAsFloat(input, dst_x, dst_y, 2);
+ float value = c0 * kRGBtoGrayscaleColorWeights[0] +
+ c1 * kRGBtoGrayscaleColorWeights[1] +
+ c2 * kRGBtoGrayscaleColorWeights[2];
+ SetChannel(output, dst_x, dst_y, 0,
+ static_cast<int>(value * 255.0f + 0.5f));
+ }
+ }
+ }
+
+ // Very slow bicubic / bilinear scaler for reference.
+ void ScaleSlow(SkBitmap* input,
+ SkBitmap* output,
+ GLHelper::ScalerQuality quality) {
+ float xscale = static_cast<float>(input->width()) / output->width();
+ float yscale = static_cast<float>(input->height()) / output->height();
+ float clamped_xscale = xscale < 1.0 ? 1.0 : 1.0 / xscale;
+ float clamped_yscale = yscale < 1.0 ? 1.0 : 1.0 / yscale;
+ for (int dst_y = 0; dst_y < output->height(); dst_y++) {
+ for (int dst_x = 0; dst_x < output->width(); dst_x++) {
+ for (int channel = 0; channel < 4; channel++) {
+ float dst_x_in_src = (dst_x + 0.5f) * xscale;
+ float dst_y_in_src = (dst_y + 0.5f) * yscale;
+
+ float value = 0.0f;
+ float sum = 0.0f;
+ switch (quality) {
+ case GLHelper::SCALER_QUALITY_BEST:
+ for (int src_y = -10; src_y < input->height() + 10; ++src_y) {
+ float coeff_y =
+ Bicubic((src_y + 0.5f - dst_y_in_src) * clamped_yscale);
+ if (coeff_y == 0.0f) {
+ continue;
+ }
+ for (int src_x = -10; src_x < input->width() + 10; ++src_x) {
+ float coeff =
+ coeff_y *
+ Bicubic((src_x + 0.5f - dst_x_in_src) * clamped_xscale);
+ if (coeff == 0.0f) {
+ continue;
+ }
+ sum += coeff;
+ float c = ChannelAsFloat(input, src_x, src_y, channel);
+ value += c * coeff;
+ }
+ }
+ break;
+
+ case GLHelper::SCALER_QUALITY_GOOD: {
+ int xshift = 0, yshift = 0;
+ while ((output->width() << xshift) < input->width()) {
+ xshift++;
+ }
+ while ((output->height() << yshift) < input->height()) {
+ yshift++;
+ }
+ int xmag = 1 << xshift;
+ int ymag = 1 << yshift;
+ if (xmag == 4 && output->width() * 3 >= input->width()) {
+ xmag = 3;
+ }
+ if (ymag == 4 && output->height() * 3 >= input->height()) {
+ ymag = 3;
+ }
+ for (int x = 0; x < xmag; x++) {
+ for (int y = 0; y < ymag; y++) {
+ value += Bilinear(
+ input, (dst_x * xmag + x + 0.5) * xscale / xmag,
+ (dst_y * ymag + y + 0.5) * yscale / ymag, channel);
+ sum += 1.0;
+ }
+ }
+ break;
+ }
+
+ case GLHelper::SCALER_QUALITY_FAST:
+ value = Bilinear(input, dst_x_in_src, dst_y_in_src, channel);
+ sum = 1.0;
+ }
+ value /= sum;
+ SetChannel(output, dst_x, dst_y, channel,
+ static_cast<int>(value * 255.0f + 0.5f));
+ }
+ }
+ }
+ }
+
+ void FlipSKBitmap(SkBitmap* bitmap) {
+ int bpp = bitmap->bytesPerPixel();
+ DCHECK(bpp == 4 || bpp == 1);
+ int top_line = 0;
+ int bottom_line = bitmap->height() - 1;
+ while (top_line < bottom_line) {
+ for (int x = 0; x < bitmap->width(); x++) {
+ bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line),
+ *bitmap->getAddr32(x, bottom_line))
+ : std::swap(*bitmap->getAddr8(x, top_line),
+ *bitmap->getAddr8(x, bottom_line));
+ }
+ top_line++;
+ bottom_line--;
+ }
+ }
+
+ // Swaps red and blue channels in each pixel in a 32-bit bitmap.
+ void SwizzleSKBitmap(SkBitmap* bitmap) {
+ int bpp = bitmap->bytesPerPixel();
+ DCHECK_EQ(bpp, 4);
+ for (int y = 0; y < bitmap->height(); y++) {
+ for (int x = 0; x < bitmap->width(); x++) {
+ // Swap channels 0 and 2 (red and blue)
+ int c0 = Channel(bitmap, x, y, 0);
+ int c2 = Channel(bitmap, x, y, 2);
+ SetChannel(bitmap, x, y, 2, c0);
+ SetChannel(bitmap, x, y, 0, c2);
+ }
+ }
+ }
+
+ // gl_helper scales recursively, so we'll need to do that
+ // in the reference implementation too.
+ void ScaleSlowRecursive(SkBitmap* input,
+ SkBitmap* output,
+ GLHelper::ScalerQuality quality) {
+ if (quality == GLHelper::SCALER_QUALITY_FAST ||
+ quality == GLHelper::SCALER_QUALITY_GOOD) {
+ ScaleSlow(input, output, quality);
+ return;
+ }
+
+ float xscale = static_cast<float>(output->width()) / input->width();
+
+ // This corresponds to all the operations we can do directly.
+ float yscale = static_cast<float>(output->height()) / input->height();
+ if ((xscale == 1.0f && yscale == 1.0f) ||
+ (xscale == 0.5f && yscale == 1.0f) ||
+ (xscale == 1.0f && yscale == 0.5f) ||
+ (xscale >= 1.0f && yscale == 1.0f) ||
+ (xscale == 1.0f && yscale >= 1.0f)) {
+ ScaleSlow(input, output, quality);
+ return;
+ }
+
+ // Now we break the problem down into smaller pieces, using the
+ // operations available.
+ int xtmp = input->width();
+ int ytmp = input->height();
+
+ if (output->height() != input->height()) {
+ ytmp = output->height();
+ while (ytmp < input->height() && ytmp * 2 != input->height()) {
+ ytmp += ytmp;
+ }
+ } else {
+ xtmp = output->width();
+ while (xtmp < input->width() && xtmp * 2 != input->width()) {
+ xtmp += xtmp;
+ }
+ }
+
+ SkBitmap tmp;
+ tmp.allocN32Pixels(xtmp, ytmp);
+
+ ScaleSlowRecursive(input, &tmp, quality);
+ ScaleSlowRecursive(&tmp, output, quality);
+ }
+
+ // Creates an RGBA SkBitmap
+ std::unique_ptr<SkBitmap> CreateTestBitmap(int width,
+ int height,
+ int test_pattern) {
+ std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
+ bitmap->allocPixels(SkImageInfo::Make(width, height, kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType));
+
+ for (int x = 0; x < width; ++x) {
+ for (int y = 0; y < height; ++y) {
+ switch (test_pattern) {
+ case 0: // Smooth test pattern
+ SetChannel(bitmap.get(), x, y, 0, x * 10);
+ SetChannel(bitmap.get(), x, y, 0, y == 0 ? x * 50 : x * 10);
+ SetChannel(bitmap.get(), x, y, 1, y * 10);
+ SetChannel(bitmap.get(), x, y, 2, (x + y) * 10);
+ SetChannel(bitmap.get(), x, y, 3, 255);
+ break;
+ case 1: // Small blocks
+ SetChannel(bitmap.get(), x, y, 0, x & 1 ? 255 : 0);
+ SetChannel(bitmap.get(), x, y, 1, y & 1 ? 255 : 0);
+ SetChannel(bitmap.get(), x, y, 2, (x + y) & 1 ? 255 : 0);
+ SetChannel(bitmap.get(), x, y, 3, 255);
+ break;
+ case 2: // Medium blocks
+ SetChannel(bitmap.get(), x, y, 0, 10 + x / 2 * 50);
+ SetChannel(bitmap.get(), x, y, 1, 10 + y / 3 * 50);
+ SetChannel(bitmap.get(), x, y, 2, (x + y) / 5 * 50 + 5);
+ SetChannel(bitmap.get(), x, y, 3, 255);
+ break;
+ }
+ }
+ }
+ return bitmap;
+ }
+
+ // Binds texture and framebuffer and loads the bitmap pixels into the texture.
+ void BindTextureAndFrameBuffer(GLuint texture,
+ GLuint framebuffer,
+ SkBitmap* bitmap,
+ int width,
+ int height) {
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ gl_->BindTexture(GL_TEXTURE_2D, texture);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, bitmap->getPixels());
+ }
+
+ // Create a test image, transform it using
+ // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation
+ // and compare the results.
+ void TestCropScaleReadbackAndCleanTexture(int xsize,
+ int ysize,
+ int scaled_xsize,
+ int scaled_ysize,
+ int test_pattern,
+ SkColorType out_color_type,
+ bool swizzle,
+ size_t quality_index) {
+ DCHECK(out_color_type == kAlpha_8_SkColorType ||
+ out_color_type == kRGBA_8888_SkColorType ||
+ out_color_type == kBGRA_8888_SkColorType);
+ GLuint src_texture;
+ gl_->GenTextures(1, &src_texture);
+ GLuint framebuffer;
+ gl_->GenFramebuffers(1, &framebuffer);
+ std::unique_ptr<SkBitmap> input_pixels =
+ CreateTestBitmap(xsize, ysize, test_pattern);
+ BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(),
+ xsize, ysize);
+
+ std::string message = base::StringPrintf(
+ "input size: %dx%d "
+ "output size: %dx%d "
+ "pattern: %d , quality: %s, "
+ "out_color_type: %d",
+ xsize, ysize, scaled_xsize, scaled_ysize, test_pattern,
+ kQualityNames[quality_index], out_color_type);
+
+ // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture.
+ SkBitmap output_pixels;
+ output_pixels.allocPixels(SkImageInfo::Make(
+ scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
+ base::RunLoop run_loop;
+ gfx::Size encoded_texture_size;
+ helper_->CropScaleReadbackAndCleanTexture(
+ src_texture, gfx::Size(xsize, ysize), gfx::Rect(xsize, ysize),
+ gfx::Size(scaled_xsize, scaled_ysize),
+ static_cast<unsigned char*>(output_pixels.getPixels()), out_color_type,
+ base::Bind(&callcallback, run_loop.QuitClosure()),
+ kQualities[quality_index]);
+ run_loop.Run();
+ // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back.
+ FlipSKBitmap(&output_pixels);
+
+ // If the bitmap shouldn't have changed - compare against input.
+ if (xsize == scaled_xsize && ysize == scaled_ysize &&
+ out_color_type != kAlpha_8_SkColorType) {
+ const std::vector<GLHelperScaling::ScalerStage> dummy_stages;
+ Compare(input_pixels.get(), &output_pixels, 0, nullptr, dummy_stages,
+ message + " comparing against input");
+ return;
+ }
+
+ // Now transform the bitmap using the reference implementation.
+ SkBitmap scaled_pixels;
+ scaled_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize,
+ kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType));
+ SkBitmap truth_pixels;
+ // Step 1: Scale
+ ScaleSlowRecursive(input_pixels.get(), &scaled_pixels,
+ kQualities[quality_index]);
+ // Step 2: Encode to grayscale if needed.
+ if (out_color_type == kAlpha_8_SkColorType) {
+ truth_pixels.allocPixels(SkImageInfo::Make(
+ scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
+ EncodeToGrayscaleSlow(&scaled_pixels, &truth_pixels);
+ } else {
+ truth_pixels = scaled_pixels;
+ }
+
+ // Now compare the results.
+ const std::vector<GLHelperScaling::ScalerStage> dummy_stages;
+ Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), dummy_stages,
+ message + " comparing against transformed/scaled");
+
+ gl_->DeleteTextures(1, &src_texture);
+ gl_->DeleteFramebuffers(1, &framebuffer);
+ }
+
+ // Scaling test: Create a test image, scale it using GLHelperScaling
+ // and a reference implementation and compare the results.
+ void TestScale(int xsize,
+ int ysize,
+ int scaled_xsize,
+ int scaled_ysize,
+ int test_pattern,
+ size_t quality_index,
+ bool flip) {
+ GLuint src_texture;
+ gl_->GenTextures(1, &src_texture);
+ GLuint framebuffer;
+ gl_->GenFramebuffers(1, &framebuffer);
+ std::unique_ptr<SkBitmap> input_pixels =
+ CreateTestBitmap(xsize, ysize, test_pattern);
+ BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(),
+ xsize, ysize);
+
+ std::string message = base::StringPrintf(
+ "input size: %dx%d "
+ "output size: %dx%d "
+ "pattern: %d quality: %s",
+ xsize, ysize, scaled_xsize, scaled_ysize, test_pattern,
+ kQualityNames[quality_index]);
+
+ std::vector<GLHelperScaling::ScalerStage> stages;
+ helper_scaling_->ComputeScalerStages(
+ kQualities[quality_index], gfx::Size(xsize, ysize),
+ gfx::Rect(0, 0, xsize, ysize), gfx::Size(scaled_xsize, scaled_ysize),
+ flip, false, &stages);
+ ValidateScalerStages(kQualities[quality_index], stages,
+ gfx::Size(scaled_xsize, scaled_ysize), message);
+
+ GLuint dst_texture = helper_->CopyAndScaleTexture(
+ src_texture, gfx::Size(xsize, ysize),
+ gfx::Size(scaled_xsize, scaled_ysize), flip, kQualities[quality_index]);
+
+ SkBitmap output_pixels;
+ output_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize,
+ kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType));
+
+ helper_->ReadbackTextureSync(
+ dst_texture, gfx::Rect(0, 0, scaled_xsize, scaled_ysize),
+ static_cast<unsigned char*>(output_pixels.getPixels()),
+ kRGBA_8888_SkColorType);
+ if (flip) {
+ // Flip the pixels back.
+ FlipSKBitmap(&output_pixels);
+ }
+
+ // If the bitmap shouldn't have changed - compare against input.
+ if (xsize == scaled_xsize && ysize == scaled_ysize) {
+ Compare(input_pixels.get(), &output_pixels, 0, nullptr, stages,
+ message + " comparing against input");
+ return;
+ }
+
+ // Now scale the bitmap using the reference implementation.
+ SkBitmap truth_pixels;
+ truth_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize,
+ kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType));
+ ScaleSlowRecursive(input_pixels.get(), &truth_pixels,
+ kQualities[quality_index]);
+ Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), stages,
+ message + " comparing against scaled");
+
+ gl_->DeleteTextures(1, &src_texture);
+ gl_->DeleteTextures(1, &dst_texture);
+ gl_->DeleteFramebuffers(1, &framebuffer);
+ }
+
+ // Create a scaling pipeline and check that it is made up of
+ // valid scaling operations.
+ void TestScalerPipeline(size_t quality,
+ int xsize,
+ int ysize,
+ int dst_xsize,
+ int dst_ysize) {
+ std::vector<GLHelperScaling::ScalerStage> stages;
+ helper_scaling_->ComputeScalerStages(
+ kQualities[quality], gfx::Size(xsize, ysize),
+ gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false,
+ false, &stages);
+ ValidateScalerStages(kQualities[quality], stages,
+ gfx::Size(dst_xsize, dst_ysize),
+ base::StringPrintf("input size: %dx%d "
+ "output size: %dx%d "
+ "quality: %s",
+ xsize, ysize, dst_xsize, dst_ysize,
+ kQualityNames[quality]));
+ }
+
+ // Create a scaling pipeline and make sure that the steps
+ // are exactly the steps we expect.
+ void CheckPipeline(GLHelper::ScalerQuality quality,
+ int xsize,
+ int ysize,
+ int dst_xsize,
+ int dst_ysize,
+ const std::string& description) {
+ std::vector<GLHelperScaling::ScalerStage> stages;
+ helper_scaling_->ComputeScalerStages(
+ quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize),
+ gfx::Size(dst_xsize, dst_ysize), false, false, &stages);
+ ValidateScalerStages(GLHelper::SCALER_QUALITY_GOOD, stages,
+ gfx::Size(dst_xsize, dst_ysize), "");
+ EXPECT_EQ(PrintStages(stages), description);
+ }
+
+ static void callcallback(const base::Callback<void()>& callback,
+ bool result) {
+ callback.Run();
+ }
+
+ void DrawGridToBitmap(int w,
+ int h,
+ SkColor background_color,
+ SkColor grid_color,
+ int grid_pitch,
+ int grid_width,
+ const SkBitmap& bmp) {
+ ASSERT_GT(grid_pitch, 0);
+ ASSERT_GT(grid_width, 0);
+ ASSERT_NE(background_color, grid_color);
+
+ for (int y = 0; y < h; ++y) {
+ bool y_on_grid = ((y % grid_pitch) < grid_width);
+
+ for (int x = 0; x < w; ++x) {
+ bool on_grid = (y_on_grid || ((x % grid_pitch) < grid_width));
+
+ if (bmp.colorType() == kRGBA_8888_SkColorType ||
+ bmp.colorType() == kBGRA_8888_SkColorType) {
+ *bmp.getAddr32(x, y) = (on_grid ? grid_color : background_color);
+ } else if (bmp.colorType() == kRGB_565_SkColorType) {
+ *bmp.getAddr16(x, y) = (on_grid ? grid_color : background_color);
+ }
+ }
+ }
+ }
+
+ void DrawCheckerToBitmap(int w,
+ int h,
+ SkColor color1,
+ SkColor color2,
+ int rect_w,
+ int rect_h,
+ const SkBitmap& bmp) {
+ ASSERT_GT(rect_w, 0);
+ ASSERT_GT(rect_h, 0);
+ ASSERT_NE(color1, color2);
+
+ for (int y = 0; y < h; ++y) {
+ bool y_bit = (((y / rect_h) & 0x1) == 0);
+
+ for (int x = 0; x < w; ++x) {
+ bool x_bit = (((x / rect_w) & 0x1) == 0);
+
+ bool use_color2 = (x_bit != y_bit); // xor
+ if (bmp.colorType() == kRGBA_8888_SkColorType ||
+ bmp.colorType() == kBGRA_8888_SkColorType) {
+ *bmp.getAddr32(x, y) = (use_color2 ? color2 : color1);
+ } else if (bmp.colorType() == kRGB_565_SkColorType) {
+ *bmp.getAddr16(x, y) = (use_color2 ? color2 : color1);
+ }
+ }
+ }
+ }
+
+ bool ColorComponentsClose(SkColor component1,
+ SkColor component2,
+ SkColorType color_type) {
+ int c1 = static_cast<int>(component1);
+ int c2 = static_cast<int>(component2);
+ bool result = false;
+ switch (color_type) {
+ case kRGBA_8888_SkColorType:
+ case kBGRA_8888_SkColorType:
+ result = (std::abs(c1 - c2) == 0);
+ break;
+ case kRGB_565_SkColorType:
+ result = (std::abs(c1 - c2) <= 7);
+ break;
+ default:
+ break;
+ }
+ return result;
+ }
+
+ bool ColorsClose(SkColor color1, SkColor color2, SkColorType color_type) {
+ bool red = ColorComponentsClose(SkColorGetR(color1), SkColorGetR(color2),
+ color_type);
+ bool green = ColorComponentsClose(SkColorGetG(color1), SkColorGetG(color2),
+ color_type);
+ bool blue = ColorComponentsClose(SkColorGetB(color1), SkColorGetB(color2),
+ color_type);
+ bool alpha = ColorComponentsClose(SkColorGetA(color1), SkColorGetA(color2),
+ color_type);
+ if (color_type == kRGB_565_SkColorType) {
+ return red && blue && green;
+ }
+ return red && blue && green && alpha;
+ }
+
+ bool IsEqual(const SkBitmap& bmp1, const SkBitmap& bmp2) {
+ if (bmp1.isNull() && bmp2.isNull())
+ return true;
+ if (bmp1.width() != bmp2.width() || bmp1.height() != bmp2.height()) {
+ LOG(ERROR) << "Bitmap geometry check failure";
+ return false;
+ }
+ if (bmp1.colorType() != bmp2.colorType())
+ return false;
+
+ if (!bmp1.getPixels() || !bmp2.getPixels()) {
+ LOG(ERROR) << "Empty Bitmap!";
+ return false;
+ }
+ for (int y = 0; y < bmp1.height(); ++y) {
+ for (int x = 0; x < bmp1.width(); ++x) {
+ if (!ColorsClose(bmp1.getColor(x, y), bmp2.getColor(x, y),
+ bmp1.colorType())) {
+ LOG(ERROR) << "Bitmap color comparision failure";
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ void BindAndAttachTextureWithPixels(GLuint src_texture,
+ SkColorType color_type,
+ const gfx::Size& src_size,
+ const SkBitmap& input_pixels) {
+ gl_->BindTexture(GL_TEXTURE_2D, src_texture);
+ GLenum format = 0;
+ switch (color_type) {
+ case kBGRA_8888_SkColorType:
+ format = GL_BGRA_EXT;
+ break;
+ case kRGBA_8888_SkColorType:
+ format = GL_RGBA;
+ break;
+ case kRGB_565_SkColorType:
+ format = GL_RGB;
+ break;
+ default:
+ NOTREACHED();
+ }
+ GLenum type = (color_type == kRGB_565_SkColorType) ? GL_UNSIGNED_SHORT_5_6_5
+ : GL_UNSIGNED_BYTE;
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, format, src_size.width(),
+ src_size.height(), 0, format, type,
+ input_pixels.getPixels());
+ }
+
+ void ReadBackTexture(GLuint src_texture,
+ const gfx::Size& src_size,
+ unsigned char* pixels,
+ SkColorType color_type,
+ bool async) {
+ if (async) {
+ base::RunLoop run_loop;
+ helper_->ReadbackTextureAsync(
+ src_texture, src_size, pixels, color_type,
+ base::Bind(&callcallback, run_loop.QuitClosure()));
+ run_loop.Run();
+ } else {
+ helper_->ReadbackTextureSync(src_texture, gfx::Rect(src_size), pixels,
+ color_type);
+ }
+ }
+ // Test basic format readback.
+ bool TestTextureFormatReadback(const gfx::Size& src_size,
+ SkColorType color_type,
+ bool async) {
+ SkImageInfo info = SkImageInfo::Make(src_size.width(), src_size.height(),
+ color_type, kPremul_SkAlphaType);
+ if (!helper_->IsReadbackConfigSupported(color_type)) {
+ LOG(INFO) << "Skipping test format not supported" << color_type;
+ return true;
+ }
+ GLuint src_texture;
+ gl_->GenTextures(1, &src_texture);
+ SkBitmap input_pixels;
+ input_pixels.allocPixels(info);
+ // Test Pattern-1, Fill with Plain color pattern.
+ // Erase the input bitmap with red color.
+ input_pixels.eraseColor(SK_ColorRED);
+ BindAndAttachTextureWithPixels(src_texture, color_type, src_size,
+ input_pixels);
+ SkBitmap output_pixels;
+ output_pixels.allocPixels(info);
+ // Initialize the output bitmap with Green color.
+ // When the readback is over output bitmap should have the red color.
+ output_pixels.eraseColor(SK_ColorGREEN);
+ uint8_t* pixels = static_cast<uint8_t*>(output_pixels.getPixels());
+ ReadBackTexture(src_texture, src_size, pixels, color_type, async);
+ bool result = IsEqual(input_pixels, output_pixels);
+ if (!result) {
+ LOG(ERROR) << "Bitmap comparision failure Pattern-1";
+ return false;
+ }
+ const int rect_w = 10, rect_h = 4, src_grid_pitch = 10, src_grid_width = 4;
+ const SkColor color1 = SK_ColorRED, color2 = SK_ColorBLUE;
+ // Test Pattern-2, Fill with Grid Pattern.
+ DrawGridToBitmap(src_size.width(), src_size.height(), color2, color1,
+ src_grid_pitch, src_grid_width, input_pixels);
+ BindAndAttachTextureWithPixels(src_texture, color_type, src_size,
+ input_pixels);
+ ReadBackTexture(src_texture, src_size, pixels, color_type, async);
+ result = IsEqual(input_pixels, output_pixels);
+ if (!result) {
+ LOG(ERROR) << "Bitmap comparision failure Pattern-2";
+ return false;
+ }
+ // Test Pattern-3, Fill with CheckerBoard Pattern.
+ DrawCheckerToBitmap(src_size.width(), src_size.height(), color1, color2,
+ rect_w, rect_h, input_pixels);
+ BindAndAttachTextureWithPixels(src_texture, color_type, src_size,
+ input_pixels);
+ ReadBackTexture(src_texture, src_size, pixels, color_type, async);
+ result = IsEqual(input_pixels, output_pixels);
+ if (!result) {
+ LOG(ERROR) << "Bitmap comparision failure Pattern-3";
+ return false;
+ }
+ gl_->DeleteTextures(1, &src_texture);
+ if (HasFailure()) {
+ return false;
+ }
+ return true;
+ }
+
+ void TestAddOps(int src, int dst, bool scale_x, bool allow3) {
+ std::deque<GLHelperScaling::ScaleOp> ops;
+ GLHelperScaling::ScaleOp::AddOps(src, dst, scale_x, allow3, &ops);
+ // Scale factor 3 is a special case.
+ // It is currently only allowed by itself.
+ if (allow3 && dst * 3 >= src && dst * 2 < src) {
+ EXPECT_EQ(ops[0].scale_factor, 3);
+ EXPECT_EQ(ops.size(), 1U);
+ EXPECT_EQ(ops[0].scale_x, scale_x);
+ EXPECT_EQ(ops[0].scale_size, dst);
+ return;
+ }
+
+ for (size_t i = 0; i < ops.size(); i++) {
+ EXPECT_EQ(ops[i].scale_x, scale_x);
+ if (i == 0) {
+ // Only the first op is allowed to be a scale up.
+ // (Scaling up *after* scaling down would make it fuzzy.)
+ EXPECT_TRUE(ops[0].scale_factor == 0 || ops[0].scale_factor == 2);
+ } else {
+ // All other operations must be 50% downscales.
+ EXPECT_EQ(ops[i].scale_factor, 2);
+ }
+ }
+ // Check that the scale factors make sense and add up.
+ int tmp = dst;
+ for (int i = static_cast<int>(ops.size() - 1); i >= 0; i--) {
+ EXPECT_EQ(tmp, ops[i].scale_size);
+ if (ops[i].scale_factor == 0) {
+ EXPECT_EQ(i, 0);
+ EXPECT_GT(tmp, src);
+ tmp = src;
+ } else {
+ tmp *= ops[i].scale_factor;
+ }
+ }
+ EXPECT_EQ(tmp, src);
+ }
+
+ void CheckPipeline2(int xsize,
+ int ysize,
+ int dst_xsize,
+ int dst_ysize,
+ const std::string& description) {
+ std::vector<GLHelperScaling::ScalerStage> stages;
+ helper_scaling_->ConvertScalerOpsToScalerStages(
+ GLHelper::SCALER_QUALITY_GOOD, gfx::Size(xsize, ysize),
+ gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false,
+ false, &x_ops_, &y_ops_, &stages);
+ EXPECT_EQ(x_ops_.size(), 0U);
+ EXPECT_EQ(y_ops_.size(), 0U);
+ ValidateScalerStages(GLHelper::SCALER_QUALITY_GOOD, stages,
+ gfx::Size(dst_xsize, dst_ysize), "");
+ EXPECT_EQ(PrintStages(stages), description);
+ }
+
+ void CheckOptimizationsTest() {
+ // Basic upscale. X and Y should be combined into one pass.
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
+ CheckPipeline2(1024, 768, 2000, 2000, "1024x768 -> 2000x2000 bilinear\n");
+
+ // X scaled 1/2, Y upscaled, should still be one pass.
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
+ CheckPipeline2(1024, 768, 512, 2000, "1024x768 -> 512x2000 bilinear\n");
+
+ // X upscaled, Y scaled 1/2, one bilinear pass
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
+ CheckPipeline2(1024, 768, 2000, 384, "1024x768 -> 2000x384 bilinear\n");
+
+ // X scaled 1/2, Y scaled 1/2, one bilinear pass
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
+ CheckPipeline2(1024, 768, 512, 384, "1024x768 -> 512x384 bilinear\n");
+
+ // X scaled 1/2, Y scaled to 60%, one bilinear2 pass.
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
+ CheckPipeline2(100, 100, 50, 60, "100x100 -> 50x60 bilinear2 Y\n");
+
+ // X scaled to 60%, Y scaled 1/2, one bilinear2 pass.
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 50));
+ CheckPipeline2(100, 100, 60, 50, "100x100 -> 60x50 bilinear2 X\n");
+
+ // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass.
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
+ CheckPipeline2(100, 100, 60, 60, "100x100 -> 60x60 bilinear2x2\n");
+
+ // X scaled to 40%, Y scaled 40%, two bilinear3 passes.
+ x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
+ CheckPipeline2(100, 100, 40, 40,
+ "100x100 -> 100x40 bilinear3 Y\n"
+ "100x40 -> 40x40 bilinear3 X\n");
+
+ // X scaled to 60%, Y scaled 40%
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
+ CheckPipeline2(100, 100, 60, 40,
+ "100x100 -> 100x40 bilinear3 Y\n"
+ "100x40 -> 60x40 bilinear2 X\n");
+
+ // X scaled to 40%, Y scaled 60%
+ x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
+ CheckPipeline2(100, 100, 40, 60,
+ "100x100 -> 100x60 bilinear2 Y\n"
+ "100x60 -> 40x60 bilinear3 X\n");
+
+ // X scaled to 30%, Y scaled 30%
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 30));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
+ CheckPipeline2(100, 100, 30, 30,
+ "100x100 -> 100x30 bilinear4 Y\n"
+ "100x30 -> 30x30 bilinear4 X\n");
+
+ // X scaled to 50%, Y scaled 30%
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
+ CheckPipeline2(100, 100, 50, 30, "100x100 -> 50x30 bilinear4 Y\n");
+
+ // X scaled to 150%, Y scaled 30%
+ // Note that we avoid combinding X and Y passes
+ // as that would probably be LESS efficient here.
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 150));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
+ CheckPipeline2(100, 100, 150, 30,
+ "100x100 -> 100x30 bilinear4 Y\n"
+ "100x30 -> 150x30 bilinear\n");
+
+ // X scaled to 1%, Y scaled 1%
+ x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 128));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 64));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 32));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 16));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 8));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 4));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 2));
+ x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 1));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 128));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 64));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 32));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 16));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 8));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 4));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 2));
+ y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 1));
+ CheckPipeline2(100, 100, 1, 1,
+ "100x100 -> 100x32 bilinear4 Y\n"
+ "100x32 -> 100x4 bilinear4 Y\n"
+ "100x4 -> 64x1 bilinear2x2\n"
+ "64x1 -> 8x1 bilinear4 X\n"
+ "8x1 -> 1x1 bilinear4 X\n");
+ }
+
+ std::unique_ptr<gpu::GLInProcessContext> context_;
+ gpu::gles2::GLES2Interface* gl_;
+ std::unique_ptr<GLHelper> helper_;
+ std::unique_ptr<GLHelperScaling> helper_scaling_;
+ std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
+};
+
+class GLHelperPixelTest : public GLHelperTest {
+ private:
+ gl::DisableNullDrawGLBindings enable_pixel_output_;
+};
+
+TEST_F(GLHelperTest, RGBASyncReadbackTest) {
+ const int kTestSize = 64;
+ bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
+ kRGBA_8888_SkColorType, false);
+ EXPECT_EQ(result, true);
+}
+
+TEST_F(GLHelperTest, BGRASyncReadbackTest) {
+ const int kTestSize = 64;
+ bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
+ kBGRA_8888_SkColorType, false);
+ EXPECT_EQ(result, true);
+}
+
+TEST_F(GLHelperTest, RGB565SyncReadbackTest) {
+ const int kTestSize = 64;
+ bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
+ kRGB_565_SkColorType, false);
+ EXPECT_EQ(result, true);
+}
+
+TEST_F(GLHelperTest, RGBAASyncReadbackTest) {
+ const int kTestSize = 64;
+ bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
+ kRGBA_8888_SkColorType, true);
+ EXPECT_EQ(result, true);
+}
+
+TEST_F(GLHelperTest, BGRAASyncReadbackTest) {
+ const int kTestSize = 64;
+ bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
+ kBGRA_8888_SkColorType, true);
+ EXPECT_EQ(result, true);
+}
+
+TEST_F(GLHelperTest, RGB565ASyncReadbackTest) {
+ const int kTestSize = 64;
+ bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
+ kRGB_565_SkColorType, true);
+ EXPECT_EQ(result, true);
+}
+
+int kRGBReadBackSizes[] = {3, 6, 16};
+
+class GLHelperPixelReadbackTest
+ : public GLHelperPixelTest,
+ public ::testing::WithParamInterface<std::tr1::tuple<unsigned int,
+ unsigned int,
+ unsigned int,
+ unsigned int,
+ unsigned int>> {};
+
+// Per pixel tests, all sizes are small so that we can print
+// out the generated bitmaps.
+TEST_P(GLHelperPixelReadbackTest, ScaleTest) {
+ unsigned int q_index = std::tr1::get<0>(GetParam());
+ unsigned int x = std::tr1::get<1>(GetParam());
+ unsigned int y = std::tr1::get<2>(GetParam());
+ unsigned int dst_x = std::tr1::get<3>(GetParam());
+ unsigned int dst_y = std::tr1::get<4>(GetParam());
+
+ for (int flip = 0; flip <= 1; flip++) {
+ for (int pattern = 0; pattern < 3; pattern++) {
+ TestScale(kRGBReadBackSizes[x], kRGBReadBackSizes[y],
+ kRGBReadBackSizes[dst_x], kRGBReadBackSizes[dst_y], pattern,
+ q_index, flip == 1);
+ if (HasFailure()) {
+ return;
+ }
+ }
+ }
+}
+
+// Per pixel tests, all sizes are small so that we can print
+// out the generated bitmaps.
+TEST_P(GLHelperPixelReadbackTest, CropScaleReadbackAndCleanTextureTest) {
+ unsigned int q_index = std::tr1::get<0>(GetParam());
+ unsigned int x = std::tr1::get<1>(GetParam());
+ unsigned int y = std::tr1::get<2>(GetParam());
+ unsigned int dst_x = std::tr1::get<3>(GetParam());
+ unsigned int dst_y = std::tr1::get<4>(GetParam());
+
+ const SkColorType kColorTypes[] = {
+ kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType};
+ for (size_t color_type = 0; color_type < arraysize(kColorTypes);
+ color_type++) {
+ for (int pattern = 0; pattern < 3; pattern++) {
+ TestCropScaleReadbackAndCleanTexture(
+ kRGBReadBackSizes[x], kRGBReadBackSizes[y], kRGBReadBackSizes[dst_x],
+ kRGBReadBackSizes[dst_y], pattern, kColorTypes[color_type], false,
+ q_index);
+ if (HasFailure())
+ return;
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ,
+ GLHelperPixelReadbackTest,
+ ::testing::Combine(
+ ::testing::Range<unsigned int>(0, arraysize(kQualities)),
+ ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)),
+ ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)),
+ ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)),
+ ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes))));
+
+// Validate that all scaling generates valid pipelines.
+TEST_F(GLHelperTest, ValidateScalerPipelines) {
+ int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096};
+ for (size_t q = 0; q < arraysize(kQualities); q++) {
+ for (size_t x = 0; x < arraysize(sizes); x++) {
+ for (size_t y = 0; y < arraysize(sizes); y++) {
+ for (size_t dst_x = 0; dst_x < arraysize(sizes); dst_x++) {
+ for (size_t dst_y = 0; dst_y < arraysize(sizes); dst_y++) {
+ TestScalerPipeline(q, sizes[x], sizes[y], sizes[dst_x],
+ sizes[dst_y]);
+ if (HasFailure()) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Make sure we don't create overly complicated pipelines
+// for a few common use cases.
+TEST_F(GLHelperTest, CheckSpecificPipelines) {
+ // Upscale should be single pass.
+ CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 1024, 700, 1280, 720,
+ "1024x700 -> 1280x720 bilinear\n");
+ // Slight downscale should use BILINEAR2X2.
+ CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 1280, 720, 1024, 700,
+ "1280x720 -> 1024x700 bilinear2x2\n");
+ // Most common tab capture pipeline on the Pixel.
+ // Should be using two BILINEAR3 passes.
+ CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 2560, 1476, 1249, 720,
+ "2560x1476 -> 2560x720 bilinear3 Y\n"
+ "2560x720 -> 1249x720 bilinear3 X\n");
+}
+
+TEST_F(GLHelperTest, ScalerOpTest) {
+ for (int allow3 = 0; allow3 <= 1; allow3++) {
+ for (int dst = 1; dst < 2049; dst += 1 + (dst >> 3)) {
+ for (int src = 1; src < 2049; src++) {
+ TestAddOps(src, dst, allow3 == 1, (src & 1) == 1);
+ if (HasFailure()) {
+ LOG(ERROR) << "Failed for src=" << src << " dst=" << dst
+ << " allow3=" << allow3;
+ return;
+ }
+ }
+ }
+ }
+}
+
+TEST_F(GLHelperTest, CheckOptimizations) {
+ // Test in baseclass since it is friends with GLHelperScaling
+ CheckOptimizationsTest();
+}
+
+} // namespace viz
+
+#endif // OS_ANDROID
diff --git a/chromium/components/viz/common/gpu/DEPS b/chromium/components/viz/common/gpu/DEPS
new file mode 100644
index 00000000000..945aba3fc2b
--- /dev/null
+++ b/chromium/components/viz/common/gpu/DEPS
@@ -0,0 +1,12 @@
+include_rules = [
+ "+cc/output",
+ "+cc/resources",
+ "+gpu/command_buffer",
+ "+gpu/GLES2/gl2extchromium.h",
+ "+gpu/ipc",
+ "+gpu/skia_bindings",
+ "+ui/gfx",
+ "+third_party/khronos/GLES2/gl2.h",
+ "+third_party/khronos/GLES2/gl2ext.h",
+ "+third_party/skia/include/gpu",
+]
diff --git a/chromium/components/viz/common/gpu/README.md b/chromium/components/viz/common/gpu/README.md
new file mode 100644
index 00000000000..f05f2922908
--- /dev/null
+++ b/chromium/components/viz/common/gpu/README.md
@@ -0,0 +1,9 @@
+# gpu/
+
+This directory contains viz APIs for access to the gpu services.
+
+## ContextProvider
+
+The primary interface to control access to the gpu and lifetime of client-side
+gpu control structures (such as the GLES2Implementation that gives access to
+the command buffer).
diff --git a/chromium/components/viz/common/gpu/context_cache_controller.cc b/chromium/components/viz/common/gpu/context_cache_controller.cc
new file mode 100644
index 00000000000..d49abb72890
--- /dev/null
+++ b/chromium/components/viz/common/gpu/context_cache_controller.cc
@@ -0,0 +1,175 @@
+// 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/viz/common/gpu/context_cache_controller.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/lock.h"
+#include "gpu/command_buffer/client/context_support.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace viz {
+namespace {
+static const int kIdleCleanupDelaySeconds = 1;
+} // namespace
+
+ContextCacheController::ScopedToken::ScopedToken() = default;
+
+ContextCacheController::ScopedToken::~ScopedToken() {
+ DCHECK(released_);
+}
+
+void ContextCacheController::ScopedToken::Release() {
+ DCHECK(!released_);
+ released_ = true;
+}
+
+ContextCacheController::ContextCacheController(
+ gpu::ContextSupport* context_support,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : context_support_(context_support),
+ task_runner_(std::move(task_runner)),
+ weak_factory_(this) {
+ // The |weak_factory_| can only be used from a single thread. We
+ // create/destroy this class and run callbacks on a single thread, but we
+ // want to be able to post callbacks from multiple threads. We need a weak
+ // ptr to post callbacks, so acquire one here, while we're on the right
+ // thread.
+ weak_ptr_ = weak_factory_.GetWeakPtr();
+}
+
+ContextCacheController::~ContextCacheController() = default;
+
+void ContextCacheController::SetGrContext(GrContext* gr_context) {
+ gr_context_ = gr_context;
+}
+
+void ContextCacheController::SetLock(base::Lock* lock) {
+ context_lock_ = lock;
+}
+
+std::unique_ptr<ContextCacheController::ScopedVisibility>
+ContextCacheController::ClientBecameVisible() {
+ if (context_lock_)
+ context_lock_->AssertAcquired();
+
+ bool became_visible = num_clients_visible_ == 0;
+ ++num_clients_visible_;
+
+ if (became_visible)
+ context_support_->SetAggressivelyFreeResources(false);
+
+ return base::WrapUnique(new ScopedVisibility());
+}
+
+void ContextCacheController::ClientBecameNotVisible(
+ std::unique_ptr<ScopedVisibility> scoped_visibility) {
+ DCHECK(scoped_visibility);
+ scoped_visibility->Release();
+
+ if (context_lock_)
+ context_lock_->AssertAcquired();
+
+ DCHECK_GT(num_clients_visible_, 0u);
+ --num_clients_visible_;
+
+ if (num_clients_visible_ == 0) {
+ // We are freeing resources now - cancel any pending idle callbacks.
+ InvalidatePendingIdleCallbacks();
+
+ if (gr_context_)
+ gr_context_->freeGpuResources();
+ context_support_->SetAggressivelyFreeResources(true);
+ }
+}
+
+std::unique_ptr<ContextCacheController::ScopedBusy>
+ContextCacheController::ClientBecameBusy() {
+ if (context_lock_)
+ context_lock_->AssertAcquired();
+
+ ++num_clients_busy_;
+ // We are busy, cancel any pending idle callbacks.
+ InvalidatePendingIdleCallbacks();
+
+ return base::WrapUnique(new ScopedBusy());
+}
+
+void ContextCacheController::ClientBecameNotBusy(
+ std::unique_ptr<ScopedBusy> scoped_busy) {
+ DCHECK(scoped_busy);
+ scoped_busy->Release();
+
+ if (context_lock_)
+ context_lock_->AssertAcquired();
+
+ DCHECK_GT(num_clients_busy_, 0u);
+ --num_clients_busy_;
+
+ // If we have become idle and we are visible, queue a task to drop resources
+ // after a delay. If are not visible, we have already dropped resources.
+ if (num_clients_busy_ == 0 && num_clients_visible_ > 0 && task_runner_) {
+ // If we already have a callback pending, don't post a new one. The pending
+ // callback will handle posting a new callback itself. This prevents us from
+ // flooding the system with tasks.
+ if (!callback_pending_) {
+ {
+ base::AutoLock hold(current_idle_generation_lock_);
+ PostIdleCallback(current_idle_generation_);
+ }
+ callback_pending_ = true;
+ }
+ }
+}
+
+void ContextCacheController::PostIdleCallback(
+ uint32_t current_idle_generation) const {
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&ContextCacheController::OnIdle, weak_ptr_,
+ current_idle_generation),
+ base::TimeDelta::FromSeconds(kIdleCleanupDelaySeconds));
+}
+
+void ContextCacheController::InvalidatePendingIdleCallbacks() {
+ base::AutoLock hold(current_idle_generation_lock_);
+ ++current_idle_generation_;
+}
+
+void ContextCacheController::OnIdle(uint32_t idle_generation) {
+ // First check if we should run our idle callback at all. If we have become
+ // busy since scheduling, just schedule another idle callback and return.
+ {
+ base::AutoLock hold(current_idle_generation_lock_);
+ if (current_idle_generation_ != idle_generation) {
+ PostIdleCallback(current_idle_generation_);
+ return;
+ }
+ }
+
+ // Try to acquire the context lock - if we can't acquire it then we've become
+ // busy since checking |current_idle_generation_| above. In this case, just
+ // re-post our idle callback and return.
+ if (context_lock_ && !context_lock_->Try()) {
+ base::AutoLock hold(current_idle_generation_lock_);
+ PostIdleCallback(current_idle_generation_);
+ return;
+ }
+
+ if (gr_context_)
+ gr_context_->freeGpuResources();
+
+ // Toggle SetAggressivelyFreeResources to drop command buffer data.
+ context_support_->SetAggressivelyFreeResources(true);
+ context_support_->SetAggressivelyFreeResources(false);
+
+ callback_pending_ = false;
+
+ if (context_lock_)
+ context_lock_->Release();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gpu/context_cache_controller.h b/chromium/components/viz/common/gpu/context_cache_controller.h
new file mode 100644
index 00000000000..feea7f33b3d
--- /dev/null
+++ b/chromium/components/viz/common/gpu/context_cache_controller.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_
+#define COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_
+
+#include <cstdint>
+#include <memory>
+
+#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/viz/common/viz_common_export.h"
+
+class GrContext;
+
+namespace base {
+class Lock;
+}
+
+namespace gpu {
+class ContextSupport;
+}
+
+namespace viz {
+
+// ContextCacheController manages clearing cached data on ContextProvider when
+// appropriate. Currently, cache clearing is triggered when the Context
+// provider transitions from Visible to Not Visible, or from Busy to Idle. As a
+// ContextProvider may have multiple clients, ContextCacheController tracks
+// visibility and idle status across all clients and only cleans up when
+// appropriate.
+class VIZ_COMMON_EXPORT ContextCacheController {
+ public:
+ class VIZ_COMMON_EXPORT ScopedToken {
+ public:
+ ~ScopedToken();
+
+ private:
+ friend class ContextCacheController;
+ ScopedToken();
+ void Release();
+
+ bool released_ = false;
+ };
+ using ScopedVisibility = ScopedToken;
+ using ScopedBusy = ScopedToken;
+
+ ContextCacheController(
+ gpu::ContextSupport* context_support,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ virtual ~ContextCacheController();
+
+ void SetGrContext(GrContext* gr_context);
+ void SetLock(base::Lock* lock);
+
+ // Clients of the owning ContextProvider should call this function when they
+ // become visible. The returned ScopedVisibility pointer must be passed back
+ // to ClientBecameNotVisible or it will DCHECK in its destructor.
+ virtual std::unique_ptr<ScopedVisibility> ClientBecameVisible();
+
+ // When a client becomes not visible (either due to a visibility change or
+ // because it is being deleted), it must pass back any ScopedVisibility
+ // pointers it owns via this function.
+ virtual void ClientBecameNotVisible(
+ std::unique_ptr<ScopedVisibility> scoped_visibility);
+
+ // Clients of the owning ContextProvider may call this function when they
+ // become busy. The returned ScopedBusy pointer must be passed back
+ // to ClientBecameNotBusy or it will DCHECK in its destructor.
+ std::unique_ptr<ScopedBusy> ClientBecameBusy();
+
+ // When a client becomes not busy, it must pass back any ScopedBusy
+ // pointers it owns via this function.
+ void ClientBecameNotBusy(std::unique_ptr<ScopedBusy> scoped_busy);
+
+ private:
+ void OnIdle(uint32_t idle_generation);
+ void PostIdleCallback(uint32_t current_idle_generation) const;
+ void InvalidatePendingIdleCallbacks();
+
+ gpu::ContextSupport* context_support_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ GrContext* gr_context_ = nullptr;
+
+ // If set, |context_lock_| must be held before accessing any member within
+ // the idle callback. Exceptions to this are |current_idle_generation_|,
+ // which has its own lock, and weak_ptr_ and task_runner_, which may be
+ // accessed from multiple threads without locking.
+ base::Lock* context_lock_ = nullptr;
+
+ uint32_t num_clients_visible_ = 0;
+ uint32_t num_clients_busy_ = 0;
+ bool callback_pending_ = false;
+
+ // |current_idle_generation_lock_| must be held when accessing
+ // |current_idle_generation_|. |current_idle_generation_lock_| must never be
+ // held while acquiring |context_lock_|.
+ base::Lock current_idle_generation_lock_;
+ uint32_t current_idle_generation_ = 0;
+
+ base::WeakPtr<ContextCacheController> weak_ptr_;
+ base::WeakPtrFactory<ContextCacheController> weak_factory_;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_
diff --git a/chromium/components/viz/common/gpu/context_provider.cc b/chromium/components/viz/common/gpu/context_provider.cc
new file mode 100644
index 00000000000..b54a4303698
--- /dev/null
+++ b/chromium/components/viz/common/gpu/context_provider.cc
@@ -0,0 +1,26 @@
+// 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/viz/common/gpu/context_provider.h"
+
+namespace viz {
+
+ContextProvider::ScopedContextLock::ScopedContextLock(
+ ContextProvider* context_provider)
+ : context_provider_(context_provider),
+ context_lock_(*context_provider_->GetLock()) {
+ // Allow current thread to use |context_provider_|.
+ context_provider_->DetachFromThread();
+ busy_ = context_provider_->CacheController()->ClientBecameBusy();
+}
+
+ContextProvider::ScopedContextLock::~ScopedContextLock() {
+ // Let ContextCacheController know we are no longer busy.
+ context_provider_->CacheController()->ClientBecameNotBusy(std::move(busy_));
+
+ // Allow usage by thread for which |context_provider_| is bound to.
+ context_provider_->DetachFromThread();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gpu/context_provider.h b/chromium/components/viz/common/gpu/context_provider.h
new file mode 100644
index 00000000000..35cc27fa1c4
--- /dev/null
+++ b/chromium/components/viz/common/gpu/context_provider.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_
+#define COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/common/capabilities.h"
+
+class GrContext;
+
+namespace base {
+class Lock;
+}
+
+namespace gpu {
+class ContextSupport;
+namespace gles2 {
+class GLES2Interface;
+}
+} // namespace gpu
+
+namespace viz {
+
+class VIZ_COMMON_EXPORT ContextProvider
+ : public base::RefCountedThreadSafe<ContextProvider> {
+ public:
+ // Hold an instance of this lock while using a context across multiple
+ // threads. This only works for ContextProviders that will return a valid
+ // lock from GetLock(), so is not always supported. Most use of
+ // ContextProvider should be single-thread only on the thread that
+ // BindToCurrentThread is run on.
+ class VIZ_COMMON_EXPORT ScopedContextLock {
+ public:
+ explicit ScopedContextLock(ContextProvider* context_provider);
+ ~ScopedContextLock();
+
+ gpu::gles2::GLES2Interface* ContextGL() {
+ return context_provider_->ContextGL();
+ }
+
+ private:
+ ContextProvider* const context_provider_;
+ base::AutoLock context_lock_;
+ std::unique_ptr<ContextCacheController::ScopedBusy> busy_;
+ };
+
+ // Bind the 3d context to the current thread. This should be called before
+ // accessing the contexts. Calling it more than once should have no effect.
+ // Once this function has been called, the class should only be accessed
+ // from the same thread unless the function has some explicitly specified
+ // rules for access on a different thread. See SetupLockOnMainThread(), which
+ // can be used to provide access from multiple threads.
+ virtual bool BindToCurrentThread() = 0;
+
+ virtual gpu::gles2::GLES2Interface* ContextGL() = 0;
+ virtual gpu::ContextSupport* ContextSupport() = 0;
+ virtual class GrContext* GrContext() = 0;
+ virtual ContextCacheController* CacheController() = 0;
+
+ // Invalidates the cached OpenGL state in GrContext.
+ // See skia GrContext::resetContext for details.
+ virtual void InvalidateGrContext(uint32_t state) = 0;
+
+ // Returns the capabilities of the currently bound 3d context.
+ virtual gpu::Capabilities ContextCapabilities() = 0;
+
+ // Sets a callback to be called when the context is lost. This should be
+ // called from the same thread that the context is bound to. To avoid races,
+ // it should be called before BindToCurrentThread().
+ // Implementation note: Implementations must avoid post-tasking the provided
+ // |lost_context_callback| directly as clients expect the method to not be
+ // called once they call SetLostContextCallback() again with a different
+ // callback.
+ typedef base::Closure LostContextCallback;
+ virtual void SetLostContextCallback(
+ const LostContextCallback& lost_context_callback) = 0;
+
+ // Below are helper methods for ScopedContextLock. Use that instead of calling
+ // these directly.
+ //
+ // Detaches debugging thread checkers to allow use of the provider from the
+ // current thread. This can be called on any thread.
+ virtual void DetachFromThread() {}
+ // Returns the lock that should be held if using this context from multiple
+ // threads. This can be called on any thread.
+ virtual base::Lock* GetLock() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<ContextProvider>;
+ virtual ~ContextProvider() {}
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_
diff --git a/chromium/components/viz/common/gpu/in_process_context_provider.cc b/chromium/components/viz/common/gpu/in_process_context_provider.cc
new file mode 100644
index 00000000000..bd20e2548eb
--- /dev/null
+++ b/chromium/components/viz/common/gpu/in_process_context_provider.cc
@@ -0,0 +1,154 @@
+// 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/common/gpu/in_process_context_provider.h"
+
+#include <stdint.h>
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.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/common/gles2_cmd_utils.h"
+#include "gpu/command_buffer/service/framebuffer_completeness_cache.h"
+#include "gpu/command_buffer/service/gpu_preferences.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/shader_translator_cache.h"
+#include "gpu/command_buffer/service/sync_point_manager.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"
+#include "gpu/ipc/in_process_command_buffer.h"
+#include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace viz {
+
+namespace {
+
+gpu::gles2::ContextCreationAttribHelper CreateAttributes() {
+ gpu::gles2::ContextCreationAttribHelper attributes;
+ attributes.alpha_size = -1;
+ attributes.depth_size = 0;
+ attributes.stencil_size = 8;
+ attributes.samples = 0;
+ attributes.sample_buffers = 0;
+ attributes.fail_if_major_perf_caveat = false;
+ attributes.bind_generates_resource = false;
+ return attributes;
+}
+
+std::unique_ptr<gpu::GLInProcessContext> CreateInProcessContext(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
+ const gpu::gles2::ContextCreationAttribHelper& attributes,
+ gpu::SurfaceHandle widget,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gpu::ImageFactory* image_factory,
+ const gpu::SharedMemoryLimits& limits,
+ gpu::GLInProcessContext* shared_context) {
+ const bool is_offscreen = widget == gpu::kNullSurfaceHandle;
+ return base::WrapUnique(gpu::GLInProcessContext::Create(
+ service, nullptr, is_offscreen, widget, shared_context, attributes,
+ limits, gpu_memory_buffer_manager, image_factory,
+ base::ThreadTaskRunnerHandle::Get()));
+}
+
+} // namespace
+
+InProcessContextProvider::InProcessContextProvider(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
+ gpu::SurfaceHandle widget,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gpu::ImageFactory* image_factory,
+ const gpu::SharedMemoryLimits& limits,
+ InProcessContextProvider* shared_context)
+ : attributes_(CreateAttributes()),
+ context_(CreateInProcessContext(
+ service,
+ attributes_,
+ widget,
+ gpu_memory_buffer_manager,
+ image_factory,
+ limits,
+ (shared_context ? shared_context->context_.get() : nullptr))) {
+ cache_controller_.reset(new ContextCacheController(
+ context_->GetImplementation(), base::ThreadTaskRunnerHandle::Get()));
+}
+
+InProcessContextProvider::~InProcessContextProvider() = default;
+
+bool InProcessContextProvider::BindToCurrentThread() {
+ return !!context_;
+}
+
+gpu::gles2::GLES2Interface* InProcessContextProvider::ContextGL() {
+ return context_->GetImplementation();
+}
+
+gpu::ContextSupport* InProcessContextProvider::ContextSupport() {
+ return context_->GetImplementation();
+}
+
+class GrContext* InProcessContextProvider::GrContext() {
+ if (gr_context_)
+ return gr_context_->get();
+
+ gr_context_.reset(new skia_bindings::GrContextForGLES2Interface(
+ ContextGL(), ContextCapabilities()));
+ return gr_context_->get();
+}
+
+ContextCacheController* InProcessContextProvider::CacheController() {
+ return cache_controller_.get();
+}
+
+void InProcessContextProvider::InvalidateGrContext(uint32_t state) {
+ if (gr_context_)
+ gr_context_->ResetContext(state);
+}
+
+base::Lock* InProcessContextProvider::GetLock() {
+ return &context_lock_;
+}
+
+gpu::Capabilities InProcessContextProvider::ContextCapabilities() {
+ return context_->GetCapabilities();
+}
+
+void InProcessContextProvider::SetLostContextCallback(
+ const LostContextCallback& lost_context_callback) {
+ // This code lives in the GPU process and so this would go away
+ // if the context is lost?
+}
+
+uint32_t InProcessContextProvider::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 InProcessContextProvider::SetSwapBuffersCompletionCallback(
+ const gpu::InProcessCommandBuffer::SwapBuffersCompletionCallback&
+ callback) {
+ context_->SetSwapBuffersCompletionCallback(callback);
+}
+
+void InProcessContextProvider::SetUpdateVSyncParametersCallback(
+ const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback&
+ callback) {
+ context_->SetUpdateVSyncParametersCallback(callback);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gpu/in_process_context_provider.h b/chromium/components/viz/common/gpu/in_process_context_provider.h
new file mode 100644
index 00000000000..493191fc389
--- /dev/null
+++ b/chromium/components/viz/common/gpu/in_process_context_provider.h
@@ -0,0 +1,83 @@
+// 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_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_
+#define COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+#include "components/viz/common/gpu/context_cache_controller.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "gpu/ipc/in_process_command_buffer.h"
+#include "ui/gfx/native_widget_types.h"
+
+class GrContext;
+
+namespace gpu {
+class GLInProcessContext;
+class GpuMemoryBufferManager;
+class ImageFactory;
+struct SharedMemoryLimits;
+} // namespace gpu
+
+namespace skia_bindings {
+class GrContextForGLES2Interface;
+}
+
+namespace viz {
+
+class VIZ_COMMON_EXPORT InProcessContextProvider
+ : public NON_EXPORTED_BASE(ContextProvider) {
+ public:
+ InProcessContextProvider(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
+ gpu::SurfaceHandle widget,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gpu::ImageFactory* image_factory,
+ const gpu::SharedMemoryLimits& limits,
+ InProcessContextProvider* shared_context);
+
+ bool BindToCurrentThread() override;
+ gpu::gles2::GLES2Interface* ContextGL() override;
+ gpu::ContextSupport* ContextSupport() override;
+ class GrContext* GrContext() override;
+ ContextCacheController* CacheController() override;
+ void InvalidateGrContext(uint32_t state) override;
+ base::Lock* GetLock() override;
+ gpu::Capabilities ContextCapabilities() override;
+ void SetLostContextCallback(
+ const LostContextCallback& lost_context_callback) override;
+
+ uint32_t GetCopyTextureInternalFormat();
+
+ void SetSwapBuffersCompletionCallback(
+ const gpu::InProcessCommandBuffer::SwapBuffersCompletionCallback&
+ callback);
+
+ void SetUpdateVSyncParametersCallback(
+ const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback&
+ callback);
+
+ protected:
+ friend class base::RefCountedThreadSafe<InProcessContextProvider>;
+ ~InProcessContextProvider() override;
+
+ private:
+ const gpu::gles2::ContextCreationAttribHelper attributes_;
+
+ base::Lock context_lock_;
+ std::unique_ptr<gpu::GLInProcessContext> context_;
+ std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
+ std::unique_ptr<ContextCacheController> cache_controller_;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_
diff --git a/chromium/components/viz/common/hit_test/DEPS b/chromium/components/viz/common/hit_test/DEPS
new file mode 100644
index 00000000000..b49b8636b9b
--- /dev/null
+++ b/chromium/components/viz/common/hit_test/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+ui/gfx/geometry",
+ "+ui/gfx/transform.h",
+] \ No newline at end of file
diff --git a/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h b/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h
new file mode 100644
index 00000000000..b90bfb19a6e
--- /dev/null
+++ b/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h
@@ -0,0 +1,49 @@
+// 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_HIT_TEST_AGGREGATED_HIT_TEST_REGION_H_
+#define COMPONENTS_VIZ_COMMON_HIT_TEST_AGGREGATED_HIT_TEST_REGION_H_
+
+#include <stdint.h>
+
+#include "components/viz/common/surfaces/surface_id.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/transform.h"
+
+namespace viz {
+
+// A AggregatedHitTestRegion element with child_count of kEndOfList indicates
+// the last element and end of the list.
+constexpr int kEndOfList = -1;
+
+// An array of AggregatedHitTestRegion elements is used to define the
+// aggregated hit-test data for the Display.
+//
+// It is designed to be in shared memory so that the viz service can
+// write the hit_test data, and the viz host can read without
+// process hops.
+struct AggregatedHitTestRegion {
+ // The FrameSinkId corresponding to this region. Events that match
+ // are routed to this surface.
+ FrameSinkId frame_sink_id;
+
+ // Flags to indicate the type of region as defined for
+ // mojom::HitTestRegion
+ uint32_t flags;
+
+ // The rectangle that defines the region in parent region's coordinate space.
+ gfx::Rect rect;
+
+ // The transform applied to the rect in parent region's coordinate space.
+ gfx::Transform transform;
+
+ // The number of children including their children below this entry.
+ // If this element is not matched then child_count elements can be skipped
+ // to move to the next entry.
+ int child_count;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_HIT_TEST_AGGREGATED_HIT_TEST_REGION_H_
diff --git a/chromium/components/viz/common/quads/DEPS b/chromium/components/viz/common/quads/DEPS
new file mode 100644
index 00000000000..766ac6bb3c0
--- /dev/null
+++ b/chromium/components/viz/common/quads/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+gpu/command_buffer/common",
+ "+mojo/public/cpp/bindings",
+ "+ui/gfx",
+]
diff --git a/chromium/components/viz/common/quads/README.md b/chromium/components/viz/common/quads/README.md
new file mode 100644
index 00000000000..c6295e1c7da
--- /dev/null
+++ b/chromium/components/viz/common/quads/README.md
@@ -0,0 +1,8 @@
+# quads/
+
+This directory contains the transport data structures for submitting frames
+from the viz client to the service, and returning data back to the client.
+
+The root of the structure for submitting frames is CompositorFrame.
+Viz-specific types that are included in the CompositorFrame are all part
+of this directory, including RenderPass, DrawQuads and SharedQuadState.
diff --git a/chromium/components/viz/common/quads/resource_format.h b/chromium/components/viz/common/quads/resource_format.h
new file mode 100644
index 00000000000..4ae7b0675fb
--- /dev/null
+++ b/chromium/components/viz/common/quads/resource_format.h
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_QUADS_RESOURCE_FORMAT_H_
+#define COMPONENTS_VIZ_COMMON_QUADS_RESOURCE_FORMAT_H_
+
+#include "base/logging.h"
+#include "ui/gfx/buffer_types.h"
+
+// TODO(prashant.n): Including third_party/khronos/GLES2/gl2.h causes
+// redefinition errors as macros/functions defined in it conflict with
+// macros/functions defined in ui/gl/gl_bindings.h. (http://crbug.com/512833).
+typedef unsigned int GLenum;
+
+namespace viz {
+
+// Keep in sync with arrays below.
+enum ResourceFormat {
+ RGBA_8888,
+ RGBA_4444,
+ BGRA_8888,
+ ALPHA_8,
+ LUMINANCE_8,
+ RGB_565,
+ ETC1,
+ RED_8,
+ LUMINANCE_F16,
+ RGBA_F16,
+ RESOURCE_FORMAT_MAX = RGBA_F16,
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_QUADS_RESOURCE_FORMAT_H_
diff --git a/chromium/components/viz/common/quads/shared_bitmap.cc b/chromium/components/viz/common/quads/shared_bitmap.cc
new file mode 100644
index 00000000000..c17074bd391
--- /dev/null
+++ b/chromium/components/viz/common/quads/shared_bitmap.cc
@@ -0,0 +1,82 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/quads/shared_bitmap.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/numerics/safe_math.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+SharedBitmap::SharedBitmap(uint8_t* pixels,
+ const SharedBitmapId& id,
+ uint32_t sequence_number)
+ : pixels_(pixels), id_(id), sequence_number_(sequence_number) {}
+
+SharedBitmap::~SharedBitmap() {}
+
+// static
+bool SharedBitmap::SizeInBytes(const gfx::Size& size, size_t* size_in_bytes) {
+ if (size.IsEmpty())
+ return false;
+ base::CheckedNumeric<size_t> s = 4;
+ s *= size.width();
+ s *= size.height();
+ if (!s.IsValid())
+ return false;
+ *size_in_bytes = s.ValueOrDie();
+ return true;
+}
+
+// static
+size_t SharedBitmap::CheckedSizeInBytes(const gfx::Size& size) {
+ CHECK(!size.IsEmpty());
+ base::CheckedNumeric<size_t> s = 4;
+ s *= size.width();
+ s *= size.height();
+ return s.ValueOrDie();
+}
+
+// static
+size_t SharedBitmap::UncheckedSizeInBytes(const gfx::Size& size) {
+ DCHECK(VerifySizeInBytes(size));
+ size_t s = 4;
+ s *= size.width();
+ s *= size.height();
+ return s;
+}
+
+// static
+bool SharedBitmap::VerifySizeInBytes(const gfx::Size& size) {
+ if (size.IsEmpty())
+ return false;
+ base::CheckedNumeric<size_t> s = 4;
+ s *= size.width();
+ s *= size.height();
+ return s.IsValid();
+}
+
+// static
+SharedBitmapId SharedBitmap::GenerateId() {
+ SharedBitmapId id;
+ // Needs cryptographically-secure random numbers.
+ base::RandBytes(id.name, sizeof(id.name));
+ 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/quads/shared_bitmap.h
new file mode 100644
index 00000000000..0d5fa83a48e
--- /dev/null
+++ b/chromium/components/viz/common/quads/shared_bitmap.h
@@ -0,0 +1,77 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_QUADS_SHARED_BITMAP_H_
+#define COMPONENTS_VIZ_COMMON_QUADS_SHARED_BITMAP_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/hash.h"
+#include "base/macros.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace base {
+class SharedMemoryHandle;
+}
+
+namespace viz {
+using SharedBitmapId = gpu::Mailbox;
+
+struct SharedBitmapIdHash {
+ size_t operator()(const SharedBitmapId& id) const {
+ return base::Hash(reinterpret_cast<const char*>(id.name), sizeof(id.name));
+ }
+};
+
+VIZ_COMMON_EXPORT base::trace_event::MemoryAllocatorDumpGuid
+GetSharedBitmapGUIDForTracing(const SharedBitmapId& bitmap_id);
+
+class VIZ_COMMON_EXPORT SharedBitmap {
+ public:
+ SharedBitmap(uint8_t* pixels,
+ const SharedBitmapId& id,
+ uint32_t sequence_number);
+
+ 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 shared memory's handle when the back end is base::SharedMemory.
+ // Otherwise, this returns an invalid handle.
+ virtual base::SharedMemoryHandle GetSharedMemoryHandle() const = 0;
+
+ // Returns true if the size is valid and false otherwise.
+ static bool SizeInBytes(const gfx::Size& size, size_t* size_in_bytes);
+ // Dies with a CRASH() if the size can not be represented as a positive number
+ // of bytes.
+ static size_t CheckedSizeInBytes(const gfx::Size& size);
+ // Returns the size in bytes but may overflow or return 0. Only do this for
+ // sizes that have already been checked.
+ static size_t UncheckedSizeInBytes(const gfx::Size& size);
+ // Returns true if the size is valid and false otherwise.
+ static bool VerifySizeInBytes(const gfx::Size& size);
+
+ 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_
diff --git a/chromium/components/viz/common/quads/texture_mailbox.cc b/chromium/components/viz/common/quads/texture_mailbox.cc
new file mode 100644
index 00000000000..6b04c11493e
--- /dev/null
+++ b/chromium/components/viz/common/quads/texture_mailbox.cc
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/quads/texture_mailbox.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "components/viz/common/quads/shared_bitmap.h"
+
+namespace viz {
+
+TextureMailbox::TextureMailbox() : shared_bitmap_(NULL) {}
+
+TextureMailbox::TextureMailbox(const TextureMailbox& other) = default;
+
+TextureMailbox::TextureMailbox(const gpu::MailboxHolder& mailbox_holder)
+ : mailbox_holder_(mailbox_holder),
+ shared_bitmap_(NULL),
+ is_overlay_candidate_(false),
+#if defined(OS_ANDROID)
+ is_backed_by_surface_texture_(false),
+ wants_promotion_hint_(false),
+#endif
+ secure_output_only_(false),
+ nearest_neighbor_(false) {
+}
+
+TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ uint32_t target)
+ : mailbox_holder_(mailbox, sync_token, target),
+ shared_bitmap_(NULL),
+ is_overlay_candidate_(false),
+#if defined(OS_ANDROID)
+ is_backed_by_surface_texture_(false),
+ wants_promotion_hint_(false),
+#endif
+ secure_output_only_(false),
+ nearest_neighbor_(false) {
+}
+
+TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ uint32_t target,
+ const gfx::Size& size_in_pixels,
+ bool is_overlay_candidate,
+ bool secure_output_only)
+ : mailbox_holder_(mailbox, sync_token, target),
+ shared_bitmap_(nullptr),
+ size_in_pixels_(size_in_pixels),
+ is_overlay_candidate_(is_overlay_candidate),
+#if defined(OS_ANDROID)
+ is_backed_by_surface_texture_(false),
+ wants_promotion_hint_(false),
+#endif
+ secure_output_only_(secure_output_only),
+ nearest_neighbor_(false) {
+ DCHECK(!is_overlay_candidate || !size_in_pixels.IsEmpty());
+}
+
+TextureMailbox::TextureMailbox(SharedBitmap* shared_bitmap,
+ const gfx::Size& size_in_pixels)
+ : shared_bitmap_(shared_bitmap),
+ size_in_pixels_(size_in_pixels),
+ is_overlay_candidate_(false),
+#if defined(OS_ANDROID)
+ is_backed_by_surface_texture_(false),
+ wants_promotion_hint_(false),
+#endif
+ secure_output_only_(false),
+ nearest_neighbor_(false) {
+ // If an embedder of cc gives an invalid TextureMailbox, we should crash
+ // here to identify the offender.
+ CHECK(SharedBitmap::VerifySizeInBytes(size_in_pixels_));
+}
+
+TextureMailbox::~TextureMailbox() {}
+
+bool TextureMailbox::Equals(const TextureMailbox& other) const {
+ if (other.IsTexture()) {
+ return IsTexture() && !memcmp(mailbox_holder_.mailbox.name,
+ other.mailbox_holder_.mailbox.name,
+ sizeof(mailbox_holder_.mailbox.name));
+ } else if (other.IsSharedMemory()) {
+ return IsSharedMemory() && (shared_bitmap_ == other.shared_bitmap_);
+ }
+
+ DCHECK(!other.IsValid());
+ return !IsValid();
+}
+
+size_t TextureMailbox::SharedMemorySizeInBytes() const {
+ // UncheckedSizeInBytes is okay because we VerifySizeInBytes in the
+ // constructor and the field is immutable.
+ return SharedBitmap::UncheckedSizeInBytes(size_in_pixels_);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/quads/texture_mailbox.h b/chromium/components/viz/common/quads/texture_mailbox.h
new file mode 100644
index 00000000000..3c24f3bb9dc
--- /dev/null
+++ b/chromium/components/viz/common/quads/texture_mailbox.h
@@ -0,0 +1,123 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_QUADS_TEXTURE_MAILBOX_H_
+#define COMPONENTS_VIZ_COMMON_QUADS_TEXTURE_MAILBOX_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/memory/shared_memory.h"
+#include "build/build_config.h"
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+namespace mojom {
+class TextureMailboxDataView;
+}
+} // namespace cc
+
+namespace viz {
+
+class SharedBitmap;
+
+// TODO(skaslev, danakj) Rename this class more apropriately since now it
+// can hold a shared memory resource as well as a texture mailbox.
+class VIZ_COMMON_EXPORT TextureMailbox {
+ public:
+ TextureMailbox();
+ TextureMailbox(const TextureMailbox& other);
+ explicit TextureMailbox(const gpu::MailboxHolder& mailbox_holder);
+ TextureMailbox(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ uint32_t target);
+ TextureMailbox(const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ uint32_t target,
+ const gfx::Size& size_in_pixels,
+ bool is_overlay_candidate,
+ bool secure_output_only);
+ TextureMailbox(SharedBitmap* shared_bitmap, const gfx::Size& size_in_pixels);
+
+ ~TextureMailbox();
+
+ bool IsValid() const { return IsTexture() || IsSharedMemory(); }
+ bool IsTexture() const { return !mailbox_holder_.mailbox.IsZero(); }
+ bool IsSharedMemory() const { return shared_bitmap_ != NULL; }
+ bool HasSyncToken() const { return mailbox_holder_.sync_token.HasData(); }
+
+ int8_t* GetSyncTokenData() { return mailbox_holder_.sync_token.GetData(); }
+
+ bool Equals(const TextureMailbox&) const;
+
+ const gpu::Mailbox& mailbox() const { return mailbox_holder_.mailbox; }
+ const int8_t* name() const { return mailbox().name; }
+ uint32_t target() const { return mailbox_holder_.texture_target; }
+ const gpu::SyncToken& sync_token() const {
+ return mailbox_holder_.sync_token;
+ }
+ void set_sync_token(const gpu::SyncToken& sync_token) {
+ mailbox_holder_.sync_token = sync_token;
+ }
+
+ bool is_overlay_candidate() const { return is_overlay_candidate_; }
+ void set_is_overlay_candidate(bool overlay_candidate) {
+ is_overlay_candidate_ = overlay_candidate;
+ }
+ bool secure_output_only() const { return secure_output_only_; }
+ bool nearest_neighbor() const { return nearest_neighbor_; }
+ void set_nearest_neighbor(bool nearest_neighbor) {
+ nearest_neighbor_ = nearest_neighbor;
+ }
+ const gfx::ColorSpace& color_space() const { return color_space_; }
+ void set_color_space(const gfx::ColorSpace& color_space) {
+ color_space_ = color_space;
+ }
+
+ // This is valid if allow_overlau() or IsSharedMemory() is true.
+ gfx::Size size_in_pixels() const { return size_in_pixels_; }
+
+ SharedBitmap* shared_bitmap() const { return shared_bitmap_; }
+ size_t SharedMemorySizeInBytes() const;
+
+#if defined(OS_ANDROID)
+ bool is_backed_by_surface_texture() const {
+ return is_backed_by_surface_texture_;
+ }
+
+ void set_is_backed_by_surface_texture(bool value) {
+ is_backed_by_surface_texture_ = value;
+ }
+
+ bool wants_promotion_hint() const { return wants_promotion_hint_; }
+
+ void set_wants_promotion_hint(bool value) { wants_promotion_hint_ = value; }
+#endif
+
+ private:
+ friend struct mojo::StructTraits<cc::mojom::TextureMailboxDataView,
+ TextureMailbox>;
+
+ gpu::MailboxHolder mailbox_holder_;
+ SharedBitmap* shared_bitmap_;
+ gfx::Size size_in_pixels_;
+ bool is_overlay_candidate_;
+#if defined(OS_ANDROID)
+ bool is_backed_by_surface_texture_;
+ bool wants_promotion_hint_;
+#endif
+ bool secure_output_only_;
+ bool nearest_neighbor_;
+ gfx::ColorSpace color_space_;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_QUADS_TEXTURE_MAILBOX_H_
diff --git a/chromium/components/viz/common/resources/DEPS b/chromium/components/viz/common/resources/DEPS
new file mode 100644
index 00000000000..ce3dad47826
--- /dev/null
+++ b/chromium/components/viz/common/resources/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+gpu/GLES2",
+ "+third_party/khronos/GLES2",
+ "+third_party/skia",
+ "+ui/gfx",
+]
diff --git a/chromium/components/viz/common/resources/README.md b/chromium/components/viz/common/resources/README.md
new file mode 100644
index 00000000000..70939e6867e
--- /dev/null
+++ b/chromium/components/viz/common/resources/README.md
@@ -0,0 +1,7 @@
+# resources/
+
+This directory contains abstractions and helpers to allocate and use both
+gpu and software resources.
+
+This code provides means to decide on texture formats, including compressed
+formats, or classes to manage the lifetime and (re)-use of resources.
diff --git a/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc b/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc
new file mode 100644
index 00000000000..83877f4e1fb
--- /dev/null
+++ b/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc
@@ -0,0 +1,72 @@
+// 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/common/resources/buffer_to_texture_target_map.h"
+
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "gpu/GLES2/gl2extchromium.h"
+
+namespace viz {
+
+BufferToTextureTargetMap StringToBufferToTextureTargetMap(
+ const std::string& str) {
+ BufferToTextureTargetMap map;
+ std::vector<std::string> entries =
+ base::SplitString(str, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ for (const auto& entry : entries) {
+ std::vector<std::string> fields = base::SplitString(
+ entry, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ CHECK_EQ(fields.size(), 3u);
+ uint32_t usage = 0;
+ uint32_t format = 0;
+ uint32_t target = 0;
+ bool succeeded = base::StringToUint(fields[0], &usage) &&
+ base::StringToUint(fields[1], &format) &&
+ base::StringToUint(fields[2], &target);
+ CHECK(succeeded);
+ CHECK_LE(usage, static_cast<uint32_t>(gfx::BufferUsage::LAST));
+ CHECK_LE(format, static_cast<uint32_t>(gfx::BufferFormat::LAST));
+ map.insert(BufferToTextureTargetMap::value_type(
+ BufferToTextureTargetKey(static_cast<gfx::BufferUsage>(usage),
+ static_cast<gfx::BufferFormat>(format)),
+ target));
+ }
+ return map;
+}
+
+std::string BufferToTextureTargetMapToString(
+ const BufferToTextureTargetMap& map) {
+ std::string str;
+ for (const auto& entry : map) {
+ if (!str.empty())
+ str += ";";
+ str += base::UintToString(static_cast<uint32_t>(entry.first.first));
+ str += ",";
+ str += base::UintToString(static_cast<uint32_t>(entry.first.second));
+ str += ",";
+ str += base::UintToString(entry.second);
+ }
+ return str;
+}
+
+BufferToTextureTargetMap DefaultBufferToTextureTargetMapForTesting() {
+ BufferToTextureTargetMap image_targets;
+ for (int usage_idx = 0; usage_idx <= static_cast<int>(gfx::BufferUsage::LAST);
+ ++usage_idx) {
+ gfx::BufferUsage usage = static_cast<gfx::BufferUsage>(usage_idx);
+ for (int format_idx = 0;
+ format_idx <= static_cast<int>(gfx::BufferFormat::LAST);
+ ++format_idx) {
+ gfx::BufferFormat format = static_cast<gfx::BufferFormat>(format_idx);
+ image_targets.insert(BufferToTextureTargetMap::value_type(
+ BufferToTextureTargetKey(usage, format), GL_TEXTURE_2D));
+ }
+ }
+
+ return image_targets;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/resources/buffer_to_texture_target_map.h b/chromium/components/viz/common/resources/buffer_to_texture_target_map.h
new file mode 100644
index 00000000000..27539475287
--- /dev/null
+++ b/chromium/components/viz/common/resources/buffer_to_texture_target_map.h
@@ -0,0 +1,37 @@
+// 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_COMMON_RESOURCES_BUFFER_TO_TEXTURE_TARGET_MAP_H_
+#define COMPONENTS_VIZ_COMMON_RESOURCES_BUFFER_TO_TEXTURE_TARGET_MAP_H_
+
+#include <map>
+#include <string>
+
+#include "components/viz/common/viz_common_export.h"
+#include "ui/gfx/buffer_types.h"
+
+namespace viz {
+// A map of GPU Memory Buffer usage/format to GL texture target.
+using BufferToTextureTargetKey = std::pair<gfx::BufferUsage, gfx::BufferFormat>;
+using BufferToTextureTargetMap = std::map<BufferToTextureTargetKey, uint32_t>;
+
+// Converts a serialized ImageTextureTargetsMap back to the runtime format.
+// Serialization takes the form:
+// "usage,format,target;usage,format,target;...;usage,format,target"
+VIZ_COMMON_EXPORT BufferToTextureTargetMap
+StringToBufferToTextureTargetMap(const std::string& str);
+
+// Converts an ImageTextureTargetsMap to a string representation of the format:
+// "usage,format,target;usage,format,target;...;usage,format,target"
+VIZ_COMMON_EXPORT std::string BufferToTextureTargetMapToString(
+ const BufferToTextureTargetMap& map);
+
+// Returns a default-initialized BufferToTextureTargetsMap where every entry
+// maps to GL_TEXTURE_2D.
+VIZ_COMMON_EXPORT BufferToTextureTargetMap
+DefaultBufferToTextureTargetMapForTesting();
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_RESOURCES_BUFFER_TO_TEXTURE_TARGET_MAP_H_
diff --git a/chromium/components/viz/common/resources/buffer_to_texture_target_map_unittest.cc b/chromium/components/viz/common/resources/buffer_to_texture_target_map_unittest.cc
new file mode 100644
index 00000000000..f037b998412
--- /dev/null
+++ b/chromium/components/viz/common/resources/buffer_to_texture_target_map_unittest.cc
@@ -0,0 +1,37 @@
+// 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/common/resources/buffer_to_texture_target_map.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/buffer_types.h"
+
+namespace viz {
+namespace {
+
+// Ensures that a map populated with various values can be serialized to/from
+// string successfully.
+TEST(BufferToTextureTargetMapTest, SerializeRoundTrip) {
+ BufferToTextureTargetMap test_map;
+ uint32_t next_value = 0;
+ for (int usage_idx = 0; usage_idx <= static_cast<int>(gfx::BufferUsage::LAST);
+ ++usage_idx) {
+ gfx::BufferUsage usage = static_cast<gfx::BufferUsage>(usage_idx);
+ for (int format_idx = 0;
+ format_idx <= static_cast<int>(gfx::BufferFormat::LAST);
+ ++format_idx) {
+ gfx::BufferFormat format = static_cast<gfx::BufferFormat>(format_idx);
+ test_map.insert(BufferToTextureTargetMap::value_type(
+ BufferToTextureTargetKey(usage, format), next_value++));
+ }
+ }
+
+ std::string serialized_map = BufferToTextureTargetMapToString(test_map);
+ BufferToTextureTargetMap deserialized_map =
+ StringToBufferToTextureTargetMap(serialized_map);
+ EXPECT_EQ(test_map, deserialized_map);
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/common/resources/platform_color.h b/chromium/components/viz/common/resources/platform_color.h
new file mode 100644
index 00000000000..505b6d43269
--- /dev/null
+++ b/chromium/components/viz/common/resources/platform_color.h
@@ -0,0 +1,77 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_
+#define COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "components/viz/common/quads/resource_format.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace viz {
+
+class PlatformColor {
+ public:
+ enum SourceDataFormat { SOURCE_FORMAT_RGBA8, SOURCE_FORMAT_BGRA8 };
+
+ static SourceDataFormat Format() {
+ return SK_B32_SHIFT ? SOURCE_FORMAT_RGBA8 : SOURCE_FORMAT_BGRA8;
+ }
+
+ // Returns the most efficient texture format for this platform.
+ static ResourceFormat BestTextureFormat() {
+ switch (Format()) {
+ case SOURCE_FORMAT_BGRA8:
+ return BGRA_8888;
+ case SOURCE_FORMAT_RGBA8:
+ return RGBA_8888;
+ }
+ NOTREACHED();
+ return RGBA_8888;
+ }
+
+ // Returns the most efficient supported texture format for this platform.
+ static ResourceFormat BestSupportedTextureFormat(bool supports_bgra8888) {
+ switch (Format()) {
+ case SOURCE_FORMAT_BGRA8:
+ return (supports_bgra8888) ? BGRA_8888 : RGBA_8888;
+ case SOURCE_FORMAT_RGBA8:
+ return RGBA_8888;
+ }
+ NOTREACHED();
+ return RGBA_8888;
+ }
+
+ // Return true if the given 32bpp resource format has the same component order
+ // as the platform color data format.
+ static bool SameComponentOrder(ResourceFormat format) {
+ switch (format) {
+ case RGBA_8888:
+ return Format() == SOURCE_FORMAT_RGBA8;
+ case BGRA_8888:
+ return Format() == SOURCE_FORMAT_BGRA8;
+ case ALPHA_8:
+ case LUMINANCE_8:
+ case RGB_565:
+ case RGBA_4444:
+ case ETC1:
+ case RED_8:
+ case LUMINANCE_F16:
+ case RGBA_F16:
+ NOTREACHED();
+ return false;
+ }
+
+ NOTREACHED();
+ return false;
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformColor);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_
diff --git a/chromium/components/viz/common/resources/platform_color_unittest.cc b/chromium/components/viz/common/resources/platform_color_unittest.cc
new file mode 100644
index 00000000000..f801217673b
--- /dev/null
+++ b/chromium/components/viz/common/resources/platform_color_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/resources/platform_color.h"
+
+#include <stddef.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+namespace {
+
+// Verify SameComponentOrder on this platform.
+TEST(PlatformColorTest, SameComponentOrder) {
+ bool rgba = !!SK_B32_SHIFT;
+
+ for (size_t i = 0; i <= RESOURCE_FORMAT_MAX; ++i) {
+ ResourceFormat format = static_cast<ResourceFormat>(i);
+ switch (format) {
+ case RGBA_8888:
+ EXPECT_EQ(rgba, PlatformColor::SameComponentOrder(format));
+ break;
+ case BGRA_8888:
+ EXPECT_NE(rgba, PlatformColor::SameComponentOrder(format));
+ break;
+ // The following formats are not platform colors.
+ case ALPHA_8:
+ case LUMINANCE_8:
+ case RGB_565:
+ case RGBA_4444:
+ case ETC1:
+ case RED_8:
+ case LUMINANCE_F16:
+ case RGBA_F16:
+ break;
+ }
+ }
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc
new file mode 100644
index 00000000000..f15ddc72682
--- /dev/null
+++ b/chromium/components/viz/common/resources/resource_format_utils.cc
@@ -0,0 +1,177 @@
+// 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/common/resources/resource_format_utils.h"
+
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace viz {
+
+SkColorType ResourceFormatToClosestSkColorType(ResourceFormat format) {
+ // Use kN32_SkColorType if there is no corresponding SkColorType.
+ switch (format) {
+ case RGBA_4444:
+ return kARGB_4444_SkColorType;
+ case RGBA_8888:
+ case BGRA_8888:
+ return kN32_SkColorType;
+ case ALPHA_8:
+ return kAlpha_8_SkColorType;
+ case RGB_565:
+ return kRGB_565_SkColorType;
+ case LUMINANCE_8:
+ return kGray_8_SkColorType;
+ case ETC1:
+ case RED_8:
+ case LUMINANCE_F16:
+ return kN32_SkColorType;
+ case RGBA_F16:
+ return kRGBA_F16_SkColorType;
+ }
+ NOTREACHED();
+ return kN32_SkColorType;
+}
+
+int BitsPerPixel(ResourceFormat format) {
+ switch (format) {
+ case RGBA_F16:
+ return 64;
+ case BGRA_8888:
+ case RGBA_8888:
+ return 32;
+ case RGBA_4444:
+ case RGB_565:
+ case LUMINANCE_F16:
+ return 16;
+ case ALPHA_8:
+ case LUMINANCE_8:
+ case RED_8:
+ return 8;
+ case ETC1:
+ return 4;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+GLenum GLDataType(ResourceFormat format) {
+ DCHECK_LE(format, RESOURCE_FORMAT_MAX);
+ static const GLenum format_gl_data_type[] = {
+ GL_UNSIGNED_BYTE, // RGBA_8888
+ GL_UNSIGNED_SHORT_4_4_4_4, // RGBA_4444
+ GL_UNSIGNED_BYTE, // BGRA_8888
+ GL_UNSIGNED_BYTE, // ALPHA_8
+ GL_UNSIGNED_BYTE, // LUMINANCE_8
+ GL_UNSIGNED_SHORT_5_6_5, // RGB_565,
+ GL_UNSIGNED_BYTE, // ETC1
+ GL_UNSIGNED_BYTE, // RED_8
+ GL_HALF_FLOAT_OES, // LUMINANCE_F16
+ GL_HALF_FLOAT_OES, // RGBA_F16
+ };
+ static_assert(arraysize(format_gl_data_type) == (RESOURCE_FORMAT_MAX + 1),
+ "format_gl_data_type does not handle all cases.");
+
+ return format_gl_data_type[format];
+}
+
+GLenum GLDataFormat(ResourceFormat format) {
+ DCHECK_LE(format, RESOURCE_FORMAT_MAX);
+ static const GLenum format_gl_data_format[] = {
+ GL_RGBA, // RGBA_8888
+ GL_RGBA, // RGBA_4444
+ GL_BGRA_EXT, // BGRA_8888
+ GL_ALPHA, // ALPHA_8
+ GL_LUMINANCE, // LUMINANCE_8
+ GL_RGB, // RGB_565
+ GL_ETC1_RGB8_OES, // ETC1
+ GL_RED_EXT, // RED_8
+ GL_LUMINANCE, // LUMINANCE_F16
+ GL_RGBA, // RGBA_F16
+ };
+ 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];
+}
+
+GLenum GLInternalFormat(ResourceFormat format) {
+ // In GLES2, the internal format must match the texture format. (It no longer
+ // is true in GLES3, however it still holds for the BGRA extension.)
+ return GLDataFormat(format);
+}
+
+GLenum GLCopyTextureInternalFormat(ResourceFormat format) {
+ // In GLES2, valid formats for glCopyTexImage2D are: GL_ALPHA, GL_LUMINANCE,
+ // GL_LUMINANCE_ALPHA, GL_RGB, or GL_RGBA.
+ // Extensions typically used for glTexImage2D do not also work for
+ // glCopyTexImage2D. For instance GL_BGRA_EXT may not be used for
+ // anything but gl(Sub)TexImage2D:
+ // https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt
+ DCHECK_LE(format, RESOURCE_FORMAT_MAX);
+ static const GLenum format_gl_data_format[] = {
+ GL_RGBA, // RGBA_8888
+ GL_RGBA, // RGBA_4444
+ GL_RGBA, // BGRA_8888
+ GL_ALPHA, // ALPHA_8
+ GL_LUMINANCE, // LUMINANCE_8
+ GL_RGB, // RGB_565
+ GL_RGB, // ETC1
+ GL_LUMINANCE, // RED_8
+ GL_LUMINANCE, // LUMINANCE_F16
+ GL_RGBA, // RGBA_F16
+ };
+ 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];
+}
+
+gfx::BufferFormat BufferFormat(ResourceFormat format) {
+ switch (format) {
+ case BGRA_8888:
+ return gfx::BufferFormat::BGRA_8888;
+ case RED_8:
+ return gfx::BufferFormat::R_8;
+ case RGBA_4444:
+ return gfx::BufferFormat::RGBA_4444;
+ case RGBA_8888:
+ return gfx::BufferFormat::RGBA_8888;
+ case ETC1:
+ return gfx::BufferFormat::ETC1;
+ case RGBA_F16:
+ return gfx::BufferFormat::RGBA_F16;
+ case ALPHA_8:
+ case LUMINANCE_8:
+ case RGB_565:
+ case LUMINANCE_F16:
+ break;
+ }
+ NOTREACHED();
+ return gfx::BufferFormat::RGBA_8888;
+}
+
+bool IsResourceFormatCompressed(ResourceFormat format) {
+ return format == ETC1;
+}
+
+bool DoesResourceFormatSupportAlpha(ResourceFormat format) {
+ switch (format) {
+ case RGBA_4444:
+ case RGBA_8888:
+ case BGRA_8888:
+ case ALPHA_8:
+ case RGBA_F16:
+ return true;
+ case LUMINANCE_8:
+ case RGB_565:
+ case ETC1:
+ case RED_8:
+ case LUMINANCE_F16:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/resources/resource_format_utils.h b/chromium/components/viz/common/resources/resource_format_utils.h
new file mode 100644
index 00000000000..935ccc8796e
--- /dev/null
+++ b/chromium/components/viz/common/resources/resource_format_utils.h
@@ -0,0 +1,27 @@
+// 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_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_
+#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_
+
+#include "components/viz/common/quads/resource_format.h"
+#include "components/viz/common/viz_common_export.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+
+namespace viz {
+
+VIZ_COMMON_EXPORT SkColorType
+ResourceFormatToClosestSkColorType(ResourceFormat format);
+VIZ_COMMON_EXPORT int BitsPerPixel(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLDataType(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLDataFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLInternalFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT GLenum GLCopyTextureInternalFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT gfx::BufferFormat BufferFormat(ResourceFormat format);
+VIZ_COMMON_EXPORT bool IsResourceFormatCompressed(ResourceFormat format);
+VIZ_COMMON_EXPORT bool DoesResourceFormatSupportAlpha(ResourceFormat format);
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_
diff --git a/chromium/components/viz/common/resources/resource_settings.cc b/chromium/components/viz/common/resources/resource_settings.cc
new file mode 100644
index 00000000000..2519c0c2f57
--- /dev/null
+++ b/chromium/components/viz/common/resources/resource_settings.cc
@@ -0,0 +1,18 @@
+// 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_settings.h"
+
+namespace viz {
+
+ResourceSettings::ResourceSettings() = default;
+
+ResourceSettings::ResourceSettings(const ResourceSettings& other) = default;
+
+ResourceSettings::~ResourceSettings() = default;
+
+ResourceSettings& ResourceSettings::operator=(const ResourceSettings& other) =
+ default;
+
+} // namespace viz
diff --git a/chromium/components/viz/common/resources/resource_settings.h b/chromium/components/viz/common/resources/resource_settings.h
new file mode 100644
index 00000000000..31ab9d006d0
--- /dev/null
+++ b/chromium/components/viz/common/resources/resource_settings.h
@@ -0,0 +1,29 @@
+// 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_SETTINGS_H_
+#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SETTINGS_H_
+
+#include "components/viz/common/resources/buffer_to_texture_target_map.h"
+#include "components/viz/common/viz_common_export.h"
+
+namespace viz {
+
+// ResourceSettings contains all the settings that are needed to create a
+// ResourceProvider.
+class VIZ_COMMON_EXPORT ResourceSettings {
+ public:
+ ResourceSettings();
+ ResourceSettings(const ResourceSettings& other);
+ ResourceSettings& operator=(const ResourceSettings& other);
+ ~ResourceSettings();
+
+ size_t texture_id_allocation_chunk_size = 64;
+ bool use_gpu_memory_buffer_resources = false;
+ BufferToTextureTargetMap buffer_to_texture_target_map;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SETTINGS_H_
diff --git a/chromium/components/viz/common/resources/shared_bitmap_manager.h b/chromium/components/viz/common/resources/shared_bitmap_manager.h
new file mode 100644
index 00000000000..630f0d852a0
--- /dev/null
+++ b/chromium/components/viz/common/resources/shared_bitmap_manager.h
@@ -0,0 +1,33 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_
+#define COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/viz/common/quads/shared_bitmap.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace viz {
+
+class SharedBitmapManager {
+ public:
+ SharedBitmapManager() {}
+ virtual ~SharedBitmapManager() {}
+
+ virtual std::unique_ptr<SharedBitmap> AllocateSharedBitmap(
+ const gfx::Size&) = 0;
+ virtual std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
+ const gfx::Size&,
+ const SharedBitmapId&) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SharedBitmapManager);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_
diff --git a/chromium/components/viz/common/surfaces/DEPS b/chromium/components/viz/common/surfaces/DEPS
new file mode 100644
index 00000000000..826db9ece7e
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/public/cpp/bindings",
+ "+ui/gfx/geometry",
+]
diff --git a/chromium/components/viz/common/surfaces/frame_sink_id.cc b/chromium/components/viz/common/surfaces/frame_sink_id.cc
new file mode 100644
index 00000000000..2b12f1b8942
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/frame_sink_id.cc
@@ -0,0 +1,19 @@
+// 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/common/surfaces/frame_sink_id.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+std::string FrameSinkId::ToString() const {
+ return base::StringPrintf("FrameSinkId(%d, %d)", client_id_, sink_id_);
+}
+
+std::ostream& operator<<(std::ostream& out, const FrameSinkId& frame_sink_id) {
+ return out << frame_sink_id.ToString();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/frame_sink_id.h b/chromium/components/viz/common/surfaces/frame_sink_id.h
new file mode 100644
index 00000000000..30319d7b3eb
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/frame_sink_id.h
@@ -0,0 +1,64 @@
+// 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_COMMON_SURFACES_FRAME_SINK_ID_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <string>
+#include <tuple>
+
+#include "base/hash.h"
+#include "components/viz/common/viz_common_export.h"
+
+namespace viz {
+
+class VIZ_COMMON_EXPORT FrameSinkId {
+ public:
+ constexpr FrameSinkId() : client_id_(0), sink_id_(0) {}
+
+ constexpr FrameSinkId(const FrameSinkId& other)
+ : client_id_(other.client_id_), sink_id_(other.sink_id_) {}
+
+ constexpr FrameSinkId(uint32_t client_id, uint32_t sink_id)
+ : client_id_(client_id), sink_id_(sink_id) {}
+
+ constexpr bool is_valid() const { return client_id_ != 0 || sink_id_ != 0; }
+
+ constexpr uint32_t client_id() const { return client_id_; }
+
+ constexpr uint32_t sink_id() const { return sink_id_; }
+
+ bool operator==(const FrameSinkId& other) const {
+ return client_id_ == other.client_id_ && sink_id_ == other.sink_id_;
+ }
+
+ bool operator!=(const FrameSinkId& other) const { return !(*this == other); }
+
+ bool operator<(const FrameSinkId& other) const {
+ return std::tie(client_id_, sink_id_) <
+ std::tie(other.client_id_, other.sink_id_);
+ }
+
+ size_t hash() const { return base::HashInts(client_id_, sink_id_); }
+
+ std::string ToString() const;
+
+ private:
+ uint32_t client_id_;
+ uint32_t sink_id_;
+};
+
+VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
+ const FrameSinkId& frame_sink_id);
+
+struct FrameSinkIdHash {
+ size_t operator()(const FrameSinkId& key) const { return key.hash(); }
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_H_
diff --git a/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h
new file mode 100644
index 00000000000..3594dab8748
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h
@@ -0,0 +1,32 @@
+// 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_SURFACES_FRAME_SINK_ID_ALLOCATOR_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_ALLOCATOR_H_
+
+#include "components/viz/common/surfaces/frame_sink_id.h"
+
+namespace viz {
+
+// This class generates FrameSinkId with a fixed client_id and an
+// incrementally-increasing sink_id.
+class FrameSinkIdAllocator {
+ public:
+ constexpr explicit FrameSinkIdAllocator(uint32_t client_id)
+ : client_id_(client_id), next_sink_id_(1u) {}
+
+ FrameSinkId NextFrameSinkId() {
+ return FrameSinkId(client_id_, next_sink_id_++);
+ }
+
+ private:
+ const uint32_t client_id_;
+ uint32_t next_sink_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameSinkIdAllocator);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_ALLOCATOR_H_
diff --git a/chromium/components/viz/common/surfaces/local_surface_id.cc b/chromium/components/viz/common/surfaces/local_surface_id.cc
new file mode 100644
index 00000000000..67fa5750092
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/local_surface_id.cc
@@ -0,0 +1,21 @@
+// 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/surfaces/local_surface_id.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+std::string LocalSurfaceId::ToString() const {
+ return base::StringPrintf("LocalSurfaceId(%d, %s" PRIu64 ")", local_id_,
+ nonce_.ToString().c_str());
+}
+
+std::ostream& operator<<(std::ostream& out,
+ const LocalSurfaceId& local_surface_id) {
+ return out << local_surface_id.ToString();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/local_surface_id.h b/chromium/components/viz/common/surfaces/local_surface_id.h
new file mode 100644
index 00000000000..450386c6479
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/local_surface_id.h
@@ -0,0 +1,85 @@
+// 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_SURFACES_LOCAL_SURFACE_ID_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_H_
+
+#include <inttypes.h>
+
+#include <iosfwd>
+#include <string>
+#include <tuple>
+
+#include "base/hash.h"
+#include "base/unguessable_token.h"
+#include "components/viz/common/viz_common_export.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace cc {
+namespace mojom {
+class LocalSurfaceIdDataView;
+}
+} // namespace cc
+
+namespace viz {
+
+class VIZ_COMMON_EXPORT LocalSurfaceId {
+ public:
+ constexpr LocalSurfaceId() : local_id_(0) {}
+
+ constexpr LocalSurfaceId(const LocalSurfaceId& other)
+ : local_id_(other.local_id_), nonce_(other.nonce_) {}
+
+ constexpr LocalSurfaceId(uint32_t local_id,
+ const base::UnguessableToken& nonce)
+ : local_id_(local_id), nonce_(nonce) {}
+
+ constexpr bool is_valid() const {
+ return local_id_ != 0 && !nonce_.is_empty();
+ }
+
+ constexpr uint32_t local_id() const { return local_id_; }
+
+ constexpr const base::UnguessableToken& nonce() const { return nonce_; }
+
+ bool operator==(const LocalSurfaceId& other) const {
+ return local_id_ == other.local_id_ && nonce_ == other.nonce_;
+ }
+
+ bool operator!=(const LocalSurfaceId& other) const {
+ return !(*this == other);
+ }
+
+ bool operator<(const LocalSurfaceId& other) const {
+ return std::tie(local_id_, nonce_) <
+ std::tie(other.local_id_, other.nonce_);
+ }
+
+ size_t hash() const {
+ DCHECK(is_valid()) << ToString();
+ return base::HashInts(
+ local_id_, static_cast<uint64_t>(base::UnguessableTokenHash()(nonce_)));
+ }
+
+ std::string ToString() const;
+
+ private:
+ friend struct mojo::StructTraits<cc::mojom::LocalSurfaceIdDataView,
+ LocalSurfaceId>;
+
+ uint32_t local_id_;
+ base::UnguessableToken nonce_;
+};
+
+VIZ_COMMON_EXPORT std::ostream& operator<<(
+ std::ostream& out,
+ const LocalSurfaceId& local_surface_id);
+
+struct LocalSurfaceIdHash {
+ size_t operator()(const LocalSurfaceId& key) const { return key.hash(); }
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_H_
diff --git a/chromium/components/viz/common/surfaces/local_surface_id_allocator.cc b/chromium/components/viz/common/surfaces/local_surface_id_allocator.cc
new file mode 100644
index 00000000000..16660338d68
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/local_surface_id_allocator.cc
@@ -0,0 +1,24 @@
+// 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/surfaces/local_surface_id_allocator.h"
+
+#include <stdint.h>
+
+#include "base/rand_util.h"
+#include "base/unguessable_token.h"
+
+namespace viz {
+
+LocalSurfaceIdAllocator::LocalSurfaceIdAllocator() : next_id_(1u) {}
+
+LocalSurfaceIdAllocator::~LocalSurfaceIdAllocator() {}
+
+LocalSurfaceId LocalSurfaceIdAllocator::GenerateId() {
+ LocalSurfaceId id(next_id_, base::UnguessableToken::Create());
+ next_id_++;
+ return id;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/local_surface_id_allocator.h b/chromium/components/viz/common/surfaces/local_surface_id_allocator.h
new file mode 100644
index 00000000000..bc01fd5aea0
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/local_surface_id_allocator.h
@@ -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.
+
+#ifndef COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_ALLOCATOR_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_ALLOCATOR_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/common/viz_common_export.h"
+
+namespace viz {
+
+// This is a helper class for generating local surface IDs for a single
+// FrameSink. This is not threadsafe, to use from multiple threads wrap this
+// class in a mutex.
+class VIZ_COMMON_EXPORT LocalSurfaceIdAllocator {
+ public:
+ LocalSurfaceIdAllocator();
+ ~LocalSurfaceIdAllocator();
+
+ LocalSurfaceId GenerateId();
+
+ private:
+ uint32_t next_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalSurfaceIdAllocator);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_ALLOCATOR_H_
diff --git a/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.cc b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.cc
new file mode 100644
index 00000000000..7faf30ec2ea
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.cc
@@ -0,0 +1,23 @@
+// 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/common/surfaces/sequence_surface_reference_factory.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+
+#include "components/viz/common/surfaces/surface_sequence.h"
+
+namespace viz {
+
+base::Closure SequenceSurfaceReferenceFactory::CreateReference(
+ SurfaceReferenceOwner* owner,
+ const SurfaceId& surface_id) const {
+ auto seq = owner->GetSurfaceSequenceGenerator()->CreateSurfaceSequence();
+ RequireSequence(surface_id, seq);
+ return base::Bind(&SequenceSurfaceReferenceFactory::SatisfySequence, this,
+ seq);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h
new file mode 100644
index 00000000000..93fa9534bf2
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h
@@ -0,0 +1,37 @@
+// 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_COMMON_SURFACES_SEQUENCE_SURFACE_REFERENCE_FACTORY_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SEQUENCE_SURFACE_REFERENCE_FACTORY_H_
+
+#include "components/viz/common/surfaces/surface_reference_factory.h"
+#include "components/viz/common/surfaces/surface_sequence.h"
+#include "components/viz/common/viz_common_export.h"
+
+namespace viz {
+
+// A surface reference factory that uses SurfaceSequence.
+class VIZ_COMMON_EXPORT SequenceSurfaceReferenceFactory
+ : public NON_EXPORTED_BASE(SurfaceReferenceFactory) {
+ public:
+ SequenceSurfaceReferenceFactory() = default;
+
+ // SurfaceReferenceFactory implementation:
+ base::Closure CreateReference(SurfaceReferenceOwner* owner,
+ const SurfaceId& surface_id) const override;
+
+ protected:
+ ~SequenceSurfaceReferenceFactory() override = default;
+
+ private:
+ virtual void RequireSequence(const SurfaceId& surface_id,
+ const SurfaceSequence& sequence) const = 0;
+ virtual void SatisfySequence(const SurfaceSequence& sequence) const = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(SequenceSurfaceReferenceFactory);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SEQUENCE_SURFACE_REFERENCE_FACTORY_H_
diff --git a/chromium/components/viz/common/surfaces/surface_id.cc b/chromium/components/viz/common/surfaces/surface_id.cc
new file mode 100644
index 00000000000..a9068c2ab7d
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_id.cc
@@ -0,0 +1,21 @@
+// 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/common/surfaces/surface_id.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+std::string SurfaceId::ToString() const {
+ return base::StringPrintf("SurfaceId(%s, %s)",
+ frame_sink_id_.ToString().c_str(),
+ local_surface_id_.ToString().c_str());
+}
+
+std::ostream& operator<<(std::ostream& out, const SurfaceId& surface_id) {
+ return out << surface_id.ToString();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/surface_id.h b/chromium/components/viz/common/surfaces/surface_id.h
new file mode 100644
index 00000000000..b008abaf288
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_id.h
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_ID_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_ID_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "base/format_macros.h"
+#include "base/hash.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/viz_common_export.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace cc {
+namespace mojom {
+class SurfaceIdDataView;
+}
+} // namespace cc
+
+namespace viz {
+
+class VIZ_COMMON_EXPORT SurfaceId {
+ public:
+ constexpr SurfaceId() = default;
+
+ constexpr SurfaceId(const SurfaceId& other) = default;
+
+ // A SurfaceId consists of two components: FrameSinkId and LocalSurfaceId.
+ // A |frame_sink_id| consists of two components; one is allocated by the
+ // display compositor service and one is allocated by the client. The
+ // |frame_sink_id| uniquely identifies a FrameSink (and frame source).
+ // A |local_surface_id| is a sequentially allocated ID generated by the frame
+ // source that uniquely identifies a sequential set of frames of the same size
+ // and device scale factor.
+ constexpr SurfaceId(const FrameSinkId& frame_sink_id,
+ const LocalSurfaceId& local_surface_id)
+ : frame_sink_id_(frame_sink_id), local_surface_id_(local_surface_id) {}
+
+ bool is_valid() const {
+ return frame_sink_id_.is_valid() && local_surface_id_.is_valid();
+ }
+
+ size_t hash() const {
+ return base::HashInts(static_cast<uint64_t>(frame_sink_id_.hash()),
+ static_cast<uint64_t>(local_surface_id_.hash()));
+ }
+
+ const FrameSinkId& frame_sink_id() const { return frame_sink_id_; }
+
+ const LocalSurfaceId& local_surface_id() const { return local_surface_id_; }
+
+ std::string ToString() const;
+
+ bool operator==(const SurfaceId& other) const {
+ return frame_sink_id_ == other.frame_sink_id_ &&
+ local_surface_id_ == other.local_surface_id_;
+ }
+
+ bool operator!=(const SurfaceId& other) const { return !(*this == other); }
+
+ bool operator<(const SurfaceId& other) const {
+ return std::tie(frame_sink_id_, local_surface_id_) <
+ std::tie(other.frame_sink_id_, other.local_surface_id_);
+ }
+
+ private:
+ friend struct mojo::StructTraits<cc::mojom::SurfaceIdDataView, SurfaceId>;
+
+ FrameSinkId frame_sink_id_;
+ LocalSurfaceId local_surface_id_;
+};
+
+VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
+ const SurfaceId& surface_id);
+
+struct SurfaceIdHash {
+ size_t operator()(const SurfaceId& key) const { return key.hash(); }
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_ID_H_
diff --git a/chromium/components/viz/common/surfaces/surface_info.h b/chromium/components/viz/common/surfaces/surface_info.h
new file mode 100644
index 00000000000..1e21fda57d8
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_info.h
@@ -0,0 +1,63 @@
+// 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_COMMON_SURFACES_SURFACE_INFO_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_INFO_H_
+
+#include "components/viz/common/surfaces/surface_id.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace IPC {
+template <class T>
+struct ParamTraits;
+} // namespace IPC
+
+namespace cc {
+namespace mojom {
+class SurfaceInfoDataView;
+}
+} // namespace cc
+
+namespace viz {
+
+// This class contains information about the surface that is being embedded.
+class SurfaceInfo {
+ public:
+ SurfaceInfo() = default;
+ SurfaceInfo(const SurfaceId& id,
+ float device_scale_factor,
+ const gfx::Size& size_in_pixels)
+ : id_(id),
+ device_scale_factor_(device_scale_factor),
+ size_in_pixels_(size_in_pixels) {}
+
+ bool is_valid() const {
+ return id_.is_valid() && device_scale_factor_ != 0 &&
+ !size_in_pixels_.IsEmpty();
+ }
+
+ bool operator==(const SurfaceInfo& info) const {
+ return id_ == info.id() &&
+ device_scale_factor_ == info.device_scale_factor() &&
+ size_in_pixels_ == info.size_in_pixels();
+ }
+
+ bool operator!=(const SurfaceInfo& info) const { return !(*this == info); }
+
+ const SurfaceId& id() const { return id_; }
+ float device_scale_factor() const { return device_scale_factor_; }
+ const gfx::Size& size_in_pixels() const { return size_in_pixels_; }
+
+ private:
+ friend struct mojo::StructTraits<cc::mojom::SurfaceInfoDataView, SurfaceInfo>;
+ friend struct IPC::ParamTraits<SurfaceInfo>;
+
+ SurfaceId id_;
+ float device_scale_factor_ = 1.f;
+ gfx::Size size_in_pixels_;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_INFO_H_
diff --git a/chromium/components/viz/common/surfaces/surface_reference_factory.h b/chromium/components/viz/common/surfaces/surface_reference_factory.h
new file mode 100644
index 00000000000..03e5808f24e
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_reference_factory.h
@@ -0,0 +1,39 @@
+// 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_COMMON_SURFACES_SURFACE_REFERENCE_FACTORY_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_FACTORY_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/common/surfaces/surface_reference_owner.h"
+
+namespace viz {
+
+// Confusingly, SurfaceReferenceFactory is only used to create SurfaceSequences.
+// TODO(kylechar): Delete all usage of SurfaceReferenceFactory when surface
+// references are enabled by default.
+class SurfaceReferenceFactory
+ : public base::RefCountedThreadSafe<SurfaceReferenceFactory> {
+ public:
+ // Creates a reference to the surface with the given surface id and returns
+ // a closure that must be called exactly once to remove the reference.
+ virtual base::Closure CreateReference(SurfaceReferenceOwner* owner,
+ const SurfaceId& surface_id) const = 0;
+
+ SurfaceReferenceFactory() = default;
+
+ protected:
+ virtual ~SurfaceReferenceFactory() = default;
+
+ private:
+ friend class base::RefCountedThreadSafe<SurfaceReferenceFactory>;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceReferenceFactory);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_FACTORY_H_
diff --git a/chromium/components/viz/common/surfaces/surface_reference_owner.h b/chromium/components/viz/common/surfaces/surface_reference_owner.h
new file mode 100644
index 00000000000..c966fb9fad1
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_reference_owner.h
@@ -0,0 +1,23 @@
+// 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_COMMON_SURFACES_SURFACE_REFERENCE_OWNER_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_OWNER_H_
+
+#include "components/viz/common/surfaces/surface_sequence_generator.h"
+
+namespace viz {
+
+// Implementations of this interface can be passed to
+// SurfaceReferenceFactory::CreateReference as the reference owner.
+class SurfaceReferenceOwner {
+ public:
+ virtual ~SurfaceReferenceOwner() {}
+
+ virtual SurfaceSequenceGenerator* GetSurfaceSequenceGenerator() = 0;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_OWNER_H_
diff --git a/chromium/components/viz/common/surfaces/surface_sequence.h b/chromium/components/viz/common/surfaces/surface_sequence.h
new file mode 100644
index 00000000000..1b5bda93d54
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_sequence.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <tuple>
+
+#include "base/hash.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+
+namespace viz {
+
+// A per-surface-namespace sequence number that's used to coordinate
+// dependencies between frames. A sequence number may be satisfied once, and
+// may be depended on once.
+struct SurfaceSequence {
+ SurfaceSequence() : sequence(0u) {}
+ SurfaceSequence(const FrameSinkId& frame_sink_id, uint32_t sequence)
+ : frame_sink_id(frame_sink_id), sequence(sequence) {}
+ bool is_valid() const { return frame_sink_id.is_valid() && sequence > 0u; }
+
+ FrameSinkId frame_sink_id;
+ uint32_t sequence;
+};
+
+inline bool operator==(const SurfaceSequence& a, const SurfaceSequence& b) {
+ return a.frame_sink_id == b.frame_sink_id && a.sequence == b.sequence;
+}
+
+inline bool operator!=(const SurfaceSequence& a, const SurfaceSequence& b) {
+ return !(a == b);
+}
+
+inline bool operator<(const SurfaceSequence& a, const SurfaceSequence& b) {
+ return std::tie(a.frame_sink_id, a.sequence) <
+ std::tie(b.frame_sink_id, b.sequence);
+}
+
+struct SurfaceSequenceHash {
+ size_t operator()(SurfaceSequence key) const {
+ return base::HashInts(static_cast<uint64_t>(key.frame_sink_id.hash()),
+ key.sequence);
+ }
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_H_
diff --git a/chromium/components/viz/common/surfaces/surface_sequence_generator.cc b/chromium/components/viz/common/surfaces/surface_sequence_generator.cc
new file mode 100644
index 00000000000..f3c351e53f9
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_sequence_generator.cc
@@ -0,0 +1,19 @@
+// 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/common/surfaces/surface_sequence_generator.h"
+#include "components/viz/common/surfaces/surface_sequence.h"
+
+namespace viz {
+
+SurfaceSequenceGenerator::SurfaceSequenceGenerator()
+ : next_surface_sequence_(1u) {}
+
+SurfaceSequenceGenerator::~SurfaceSequenceGenerator() = default;
+
+SurfaceSequence SurfaceSequenceGenerator::CreateSurfaceSequence() {
+ return SurfaceSequence(frame_sink_id_, next_surface_sequence_++);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/surface_sequence_generator.h b/chromium/components/viz/common/surfaces/surface_sequence_generator.h
new file mode 100644
index 00000000000..1bed7d5baf7
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_sequence_generator.h
@@ -0,0 +1,41 @@
+// 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_COMMON_SURFACES_SURFACE_SEQUENCE_GENERATOR_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_GENERATOR_H_
+
+#include <stdint.h>
+
+#include <tuple>
+
+#include "base/macros.h"
+
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/surface_sequence.h"
+#include "components/viz/common/viz_common_export.h"
+
+namespace viz {
+
+// Generates unique surface sequences for a surface client id.
+class VIZ_COMMON_EXPORT SurfaceSequenceGenerator {
+ public:
+ SurfaceSequenceGenerator();
+ ~SurfaceSequenceGenerator();
+
+ void set_frame_sink_id(const FrameSinkId& frame_sink_id) {
+ frame_sink_id_ = frame_sink_id;
+ }
+
+ SurfaceSequence CreateSurfaceSequence();
+
+ private:
+ FrameSinkId frame_sink_id_;
+ uint32_t next_surface_sequence_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceSequenceGenerator);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_GENERATOR_H_
diff --git a/chromium/components/viz/common/surfaces/surface_sequence_generator_unittest.cc b/chromium/components/viz/common/surfaces/surface_sequence_generator_unittest.cc
new file mode 100644
index 00000000000..5d1913cc0d4
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_sequence_generator_unittest.cc
@@ -0,0 +1,24 @@
+// 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/common/surfaces/surface_sequence_generator.h"
+
+#include "components/viz/common/surfaces/surface_sequence.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+namespace {
+
+static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
+
+TEST(SurfaceSequenceGeneratorTest, Basic) {
+ SurfaceSequenceGenerator generator;
+ generator.set_frame_sink_id(kArbitraryFrameSinkId);
+ SurfaceSequence sequence1 = generator.CreateSurfaceSequence();
+ SurfaceSequence sequence2 = generator.CreateSurfaceSequence();
+ EXPECT_NE(sequence1, sequence2);
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/common/viz_common_export.h b/chromium/components/viz/common/viz_common_export.h
new file mode 100644
index 00000000000..9e58b1327c9
--- /dev/null
+++ b/chromium/components/viz/common/viz_common_export.h
@@ -0,0 +1,29 @@
+// 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_VIZ_COMMON_EXPORT_H_
+#define COMPONENTS_VIZ_COMMON_VIZ_COMMON_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(VIZ_COMMON_IMPLEMENTATION)
+#define VIZ_COMMON_EXPORT __declspec(dllexport)
+#else
+#define VIZ_COMMON_EXPORT __declspec(dllimport)
+#endif // defined(VIZ_COMMON_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(VIZ_COMMON_IMPLEMENTATION)
+#define VIZ_COMMON_EXPORT __attribute__((visibility("default")))
+#else
+#define VIZ_COMMON_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define VIZ_COMMON_EXPORT
+#endif
+
+#endif // COMPONENTS_VIZ_COMMON_VIZ_COMMON_EXPORT_H_
diff --git a/chromium/components/viz/common/yuv_readback_unittest.cc b/chromium/components/viz/common/yuv_readback_unittest.cc
new file mode 100644
index 00000000000..3a92257ae3a
--- /dev/null
+++ b/chromium/components/viz/common/yuv_readback_unittest.cc
@@ -0,0 +1,558 @@
+// 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 "base/json/json_reader.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "components/viz/common/gl_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "gpu/ipc/gl_in_process_context.h"
+#include "media/base/video_frame.h"
+#include "media/base/video_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+#if !defined(OS_ANDROID)
+
+namespace viz {
+
+namespace {
+int kYUVReadbackSizes[] = {2, 4, 14};
+}
+
+class YUVReadbackTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ gpu::gles2::ContextCreationAttribHelper attributes;
+ attributes.alpha_size = 8;
+ attributes.depth_size = 24;
+ attributes.red_size = 8;
+ attributes.green_size = 8;
+ attributes.blue_size = 8;
+ attributes.stencil_size = 8;
+ attributes.samples = 4;
+ attributes.sample_buffers = 1;
+ attributes.bind_generates_resource = false;
+
+ context_.reset(
+ gpu::GLInProcessContext::Create(nullptr, /* service */
+ nullptr, /* surface */
+ true, /* offscreen */
+ gpu::kNullSurfaceHandle, /* window */
+ nullptr, /* share_context */
+ attributes, gpu::SharedMemoryLimits(),
+ nullptr, /* gpu_memory_buffer_manager */
+ nullptr, /* image_factory */
+ base::ThreadTaskRunnerHandle::Get()));
+ gl_ = context_->GetImplementation();
+ gpu::ContextSupport* support = context_->GetImplementation();
+
+ helper_.reset(new GLHelper(gl_, support));
+ }
+
+ void TearDown() override {
+ helper_.reset(NULL);
+ context_.reset(NULL);
+ }
+
+ void StartTracing(const std::string& filter) {
+ base::trace_event::TraceLog::GetInstance()->SetEnabled(
+ base::trace_event::TraceConfig(filter,
+ base::trace_event::RECORD_UNTIL_FULL),
+ base::trace_event::TraceLog::RECORDING_MODE);
+ }
+
+ static void TraceDataCB(
+ const base::Callback<void()>& callback,
+ std::string* output,
+ const scoped_refptr<base::RefCountedString>& json_events_str,
+ bool has_more_events) {
+ if (output->size() > 1 && !json_events_str->data().empty()) {
+ output->append(",");
+ }
+ output->append(json_events_str->data());
+ if (!has_more_events) {
+ callback.Run();
+ }
+ }
+
+ // End tracing, return tracing data in a simple map
+ // of event name->counts.
+ void EndTracing(std::map<std::string, int>* event_counts) {
+ std::string json_data = "[";
+ base::trace_event::TraceLog::GetInstance()->SetDisabled();
+ base::RunLoop run_loop;
+ base::trace_event::TraceLog::GetInstance()->Flush(
+ base::Bind(&YUVReadbackTest::TraceDataCB, run_loop.QuitClosure(),
+ base::Unretained(&json_data)));
+ run_loop.Run();
+ json_data.append("]");
+
+ std::string error_msg;
+ std::unique_ptr<base::Value> trace_data =
+ base::JSONReader::ReadAndReturnError(json_data, 0, NULL, &error_msg);
+ CHECK(trace_data) << "JSON parsing failed (" << error_msg
+ << ") JSON data:" << std::endl
+ << json_data;
+
+ base::ListValue* list;
+ CHECK(trace_data->GetAsList(&list));
+ for (size_t i = 0; i < list->GetSize(); i++) {
+ base::Value* item = NULL;
+ if (list->Get(i, &item)) {
+ base::DictionaryValue* dict;
+ CHECK(item->GetAsDictionary(&dict));
+ std::string name;
+ CHECK(dict->GetString("name", &name));
+ std::string trace_type;
+ CHECK(dict->GetString("ph", &trace_type));
+ // Count all except END traces, as they come in BEGIN/END pairs.
+ if (trace_type != "E" && trace_type != "e")
+ (*event_counts)[name]++;
+ VLOG(1) << "trace name: " << name;
+ }
+ }
+ }
+
+ // Look up a single channel value. Works for 4-channel and single channel
+ // bitmaps. Clamp x/y.
+ int Channel(SkBitmap* pixels, int x, int y, int c) {
+ if (pixels->bytesPerPixel() == 4) {
+ uint32_t* data =
+ pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
+ std::max(0, std::min(y, pixels->height() - 1)));
+ return (*data) >> (c * 8) & 0xff;
+ } else {
+ DCHECK_EQ(pixels->bytesPerPixel(), 1);
+ DCHECK_EQ(c, 0);
+ return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)),
+ std::max(0, std::min(y, pixels->height() - 1)));
+ }
+ }
+
+ // Set a single channel value. Works for 4-channel and single channel
+ // bitmaps. Clamp x/y.
+ void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
+ DCHECK_GE(x, 0);
+ DCHECK_GE(y, 0);
+ DCHECK_LT(x, pixels->width());
+ DCHECK_LT(y, pixels->height());
+ if (pixels->bytesPerPixel() == 4) {
+ uint32_t* data = pixels->getAddr32(x, y);
+ v = std::max(0, std::min(v, 255));
+ *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
+ } else {
+ DCHECK_EQ(pixels->bytesPerPixel(), 1);
+ DCHECK_EQ(c, 0);
+ uint8_t* data = pixels->getAddr8(x, y);
+ v = std::max(0, std::min(v, 255));
+ *data = v;
+ }
+ }
+
+ // Print all the R, G, B or A values from an SkBitmap in a
+ // human-readable format.
+ void PrintChannel(SkBitmap* pixels, int c) {
+ for (int y = 0; y < pixels->height(); y++) {
+ std::string formatted;
+ for (int x = 0; x < pixels->width(); x++) {
+ formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c)));
+ }
+ LOG(ERROR) << formatted;
+ }
+ }
+
+ // Get a single R, G, B or A value as a float.
+ float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) {
+ return Channel(pixels, x, y, c) / 255.0;
+ }
+
+ // Works like a GL_LINEAR lookup on an SkBitmap.
+ float Bilinear(SkBitmap* pixels, float x, float y, int c) {
+ x -= 0.5;
+ y -= 0.5;
+ int base_x = static_cast<int>(floorf(x));
+ int base_y = static_cast<int>(floorf(y));
+ x -= base_x;
+ y -= base_y;
+ return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) +
+ ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) +
+ ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y +
+ ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
+ }
+
+ void FlipSKBitmap(SkBitmap* bitmap) {
+ int bpp = bitmap->bytesPerPixel();
+ DCHECK(bpp == 4 || bpp == 1);
+ int top_line = 0;
+ int bottom_line = bitmap->height() - 1;
+ while (top_line < bottom_line) {
+ for (int x = 0; x < bitmap->width(); x++) {
+ bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line),
+ *bitmap->getAddr32(x, bottom_line))
+ : std::swap(*bitmap->getAddr8(x, top_line),
+ *bitmap->getAddr8(x, bottom_line));
+ }
+ top_line++;
+ bottom_line--;
+ }
+ }
+
+ // Note: Left/Right means Top/Bottom when used for Y dimension.
+ enum Margin {
+ MarginLeft,
+ MarginMiddle,
+ MarginRight,
+ MarginInvalid,
+ };
+
+ static Margin NextMargin(Margin m) {
+ switch (m) {
+ case MarginLeft:
+ return MarginMiddle;
+ case MarginMiddle:
+ return MarginRight;
+ case MarginRight:
+ return MarginInvalid;
+ default:
+ return MarginInvalid;
+ }
+ }
+
+ int compute_margin(int insize, int outsize, Margin m) {
+ int available = outsize - insize;
+ switch (m) {
+ default:
+ EXPECT_TRUE(false) << "This should not happen.";
+ return 0;
+ case MarginLeft:
+ return 0;
+ case MarginMiddle:
+ return (available / 2) & ~1;
+ case MarginRight:
+ return available;
+ }
+ }
+
+ // Convert 0.0 - 1.0 to 0 - 255
+ int float_to_byte(float v) {
+ int ret = static_cast<int>(floorf(v * 255.0f + 0.5f));
+ if (ret < 0) {
+ return 0;
+ }
+ if (ret > 255) {
+ return 255;
+ }
+ return ret;
+ }
+
+ static void callcallback(const base::Callback<void()>& callback,
+ bool result) {
+ callback.Run();
+ }
+
+ void PrintPlane(unsigned char* plane, int xsize, int stride, int ysize) {
+ for (int y = 0; y < ysize; y++) {
+ std::string formatted;
+ for (int x = 0; x < xsize; x++) {
+ formatted.append(base::StringPrintf("%3d, ", plane[y * stride + x]));
+ }
+ LOG(ERROR) << formatted << " (" << (plane + y * stride) << ")";
+ }
+ }
+
+ // Compare two planes make sure that each component of each pixel
+ // is no more than |maxdiff| apart.
+ void ComparePlane(unsigned char* truth,
+ int truth_stride,
+ unsigned char* other,
+ int other_stride,
+ int maxdiff,
+ int xsize,
+ int ysize,
+ SkBitmap* source,
+ std::string message) {
+ for (int x = 0; x < xsize; x++) {
+ for (int y = 0; y < ysize; y++) {
+ int a = other[y * other_stride + x];
+ int b = truth[y * truth_stride + x];
+ EXPECT_NEAR(a, b, maxdiff)
+ << " x=" << x << " y=" << y << " " << message;
+ if (std::abs(a - b) > maxdiff) {
+ LOG(ERROR) << "-------expected--------";
+ PrintPlane(truth, xsize, truth_stride, ysize);
+ LOG(ERROR) << "-------actual--------";
+ PrintPlane(other, xsize, other_stride, ysize);
+ if (source) {
+ LOG(ERROR) << "-------before yuv conversion: red--------";
+ PrintChannel(source, 0);
+ LOG(ERROR) << "-------before yuv conversion: green------";
+ PrintChannel(source, 1);
+ LOG(ERROR) << "-------before yuv conversion: blue-------";
+ PrintChannel(source, 2);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ // YUV readback test. Create a test pattern, convert to YUV
+ // with reference implementation and compare to what gl_helper
+ // returns.
+ void TestYUVReadback(int xsize,
+ int ysize,
+ int output_xsize,
+ int output_ysize,
+ int xmargin,
+ int ymargin,
+ int test_pattern,
+ bool flip,
+ bool use_mrt,
+ GLHelper::ScalerQuality quality) {
+ GLuint src_texture;
+ gl_->GenTextures(1, &src_texture);
+ SkBitmap input_pixels;
+ input_pixels.allocN32Pixels(xsize, ysize);
+
+ for (int x = 0; x < xsize; ++x) {
+ for (int y = 0; y < ysize; ++y) {
+ switch (test_pattern) {
+ case 0: // Smooth test pattern
+ SetChannel(&input_pixels, x, y, 0, x * 10);
+ SetChannel(&input_pixels, x, y, 1, y * 10);
+ SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
+ SetChannel(&input_pixels, x, y, 3, 255);
+ break;
+ case 1: // Small blocks
+ SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
+ SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
+ SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
+ SetChannel(&input_pixels, x, y, 3, 255);
+ break;
+ case 2: // Medium blocks
+ SetChannel(&input_pixels, x, y, 0, 10 + x / 2 * 50);
+ SetChannel(&input_pixels, x, y, 1, 10 + y / 3 * 50);
+ SetChannel(&input_pixels, x, y, 2, (x + y) / 5 * 50 + 5);
+ SetChannel(&input_pixels, x, y, 3, 255);
+ break;
+ }
+ }
+ }
+
+ gl_->BindTexture(GL_TEXTURE_2D, src_texture);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsize, ysize, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, input_pixels.getPixels());
+
+ gpu::Mailbox mailbox;
+ gl_->GenMailboxCHROMIUM(mailbox.name);
+ EXPECT_FALSE(mailbox.IsZero());
+ gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
+ const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM();
+ gl_->ShallowFlushCHROMIUM();
+
+ gpu::SyncToken sync_token;
+ gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
+
+ std::string message = base::StringPrintf(
+ "input size: %dx%d "
+ "output size: %dx%d "
+ "margin: %dx%d "
+ "pattern: %d %s %s",
+ xsize, ysize, output_xsize, output_ysize, xmargin, ymargin,
+ test_pattern, flip ? "flip" : "noflip", flip ? "mrt" : "nomrt");
+ std::unique_ptr<ReadbackYUVInterface> yuv_reader(
+ helper_->CreateReadbackPipelineYUV(
+ quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize),
+ gfx::Size(xsize, ysize), flip, use_mrt));
+
+ scoped_refptr<media::VideoFrame> output_frame =
+ media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12,
+ // The coded size of the output frame is rounded up to the next
+ // 16-byte boundary. This tests that the readback is being
+ // positioned inside the frame's visible region, and not dependent
+ // on its coded size.
+ gfx::Size((output_xsize + 15) & ~15, (output_ysize + 15) & ~15),
+ gfx::Rect(0, 0, output_xsize, output_ysize),
+ gfx::Size(output_xsize, output_ysize),
+ base::TimeDelta::FromSeconds(0));
+ scoped_refptr<media::VideoFrame> truth_frame =
+ media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12, gfx::Size(output_xsize, output_ysize),
+ gfx::Rect(0, 0, output_xsize, output_ysize),
+ gfx::Size(output_xsize, output_ysize),
+ base::TimeDelta::FromSeconds(0));
+
+ base::RunLoop run_loop;
+ yuv_reader->ReadbackYUV(mailbox, sync_token, output_frame->visible_rect(),
+ output_frame->stride(media::VideoFrame::kYPlane),
+ output_frame->data(media::VideoFrame::kYPlane),
+ output_frame->stride(media::VideoFrame::kUPlane),
+ output_frame->data(media::VideoFrame::kUPlane),
+ output_frame->stride(media::VideoFrame::kVPlane),
+ output_frame->data(media::VideoFrame::kVPlane),
+ gfx::Point(xmargin, ymargin),
+ base::Bind(&callcallback, run_loop.QuitClosure()));
+
+ const gfx::Rect paste_rect(gfx::Point(xmargin, ymargin),
+ gfx::Size(xsize, ysize));
+ media::LetterboxYUV(output_frame.get(), paste_rect);
+ run_loop.Run();
+
+ if (flip) {
+ FlipSKBitmap(&input_pixels);
+ }
+
+ unsigned char* Y = truth_frame->visible_data(media::VideoFrame::kYPlane);
+ unsigned char* U = truth_frame->visible_data(media::VideoFrame::kUPlane);
+ unsigned char* V = truth_frame->visible_data(media::VideoFrame::kVPlane);
+ int32_t y_stride = truth_frame->stride(media::VideoFrame::kYPlane);
+ int32_t u_stride = truth_frame->stride(media::VideoFrame::kUPlane);
+ int32_t v_stride = truth_frame->stride(media::VideoFrame::kVPlane);
+ memset(Y, 0x00, y_stride * output_ysize);
+ memset(U, 0x80, u_stride * output_ysize / 2);
+ memset(V, 0x80, v_stride * output_ysize / 2);
+
+ const float kRGBtoYColorWeights[] = {0.257f, 0.504f, 0.098f, 0.0625f};
+ const float kRGBtoUColorWeights[] = {-0.148f, -0.291f, 0.439f, 0.5f};
+ const float kRGBtoVColorWeights[] = {0.439f, -0.368f, -0.071f, 0.5f};
+
+ for (int y = 0; y < ysize; y++) {
+ for (int x = 0; x < xsize; x++) {
+ Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte(
+ ChannelAsFloat(&input_pixels, x, y, 0) * kRGBtoYColorWeights[0] +
+ ChannelAsFloat(&input_pixels, x, y, 1) * kRGBtoYColorWeights[1] +
+ ChannelAsFloat(&input_pixels, x, y, 2) * kRGBtoYColorWeights[2] +
+ kRGBtoYColorWeights[3]);
+ }
+ }
+
+ for (int y = 0; y < ysize / 2; y++) {
+ for (int x = 0; x < xsize / 2; x++) {
+ U[(y + ymargin / 2) * u_stride + x + xmargin / 2] =
+ float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) *
+ kRGBtoUColorWeights[0] +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
+ kRGBtoUColorWeights[1] +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
+ kRGBtoUColorWeights[2] +
+ kRGBtoUColorWeights[3]);
+ V[(y + ymargin / 2) * v_stride + x + xmargin / 2] =
+ float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) *
+ kRGBtoVColorWeights[0] +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
+ kRGBtoVColorWeights[1] +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
+ kRGBtoVColorWeights[2] +
+ kRGBtoVColorWeights[3]);
+ }
+ }
+
+ ComparePlane(
+ Y, y_stride, output_frame->visible_data(media::VideoFrame::kYPlane),
+ output_frame->stride(media::VideoFrame::kYPlane), 2, output_xsize,
+ output_ysize, &input_pixels, message + " Y plane");
+ ComparePlane(
+ U, u_stride, output_frame->visible_data(media::VideoFrame::kUPlane),
+ output_frame->stride(media::VideoFrame::kUPlane), 2, output_xsize / 2,
+ output_ysize / 2, &input_pixels, message + " U plane");
+ ComparePlane(
+ V, v_stride, output_frame->visible_data(media::VideoFrame::kVPlane),
+ output_frame->stride(media::VideoFrame::kVPlane), 2, output_xsize / 2,
+ output_ysize / 2, &input_pixels, message + " V plane");
+
+ gl_->DeleteTextures(1, &src_texture);
+ }
+
+ std::unique_ptr<gpu::GLInProcessContext> context_;
+ gpu::gles2::GLES2Interface* gl_;
+ std::unique_ptr<GLHelper> helper_;
+ gl::DisableNullDrawGLBindings enable_pixel_output_;
+};
+
+TEST_F(YUVReadbackTest, YUVReadbackOptTest) {
+ // This test uses the gpu.service/gpu_decoder tracing events to detect how
+ // many scaling passes are actually performed by the YUV readback pipeline.
+ StartTracing(TRACE_DISABLED_BY_DEFAULT(
+ "gpu.service") "," TRACE_DISABLED_BY_DEFAULT("gpu_decoder"));
+
+ TestYUVReadback(800, 400, 800, 400, 0, 0, 1, false, true,
+ GLHelper::SCALER_QUALITY_FAST);
+
+ std::map<std::string, int> event_counts;
+ EndTracing(&event_counts);
+ int draw_buffer_calls = event_counts["kDrawBuffersEXTImmediate"];
+ int draw_arrays_calls = event_counts["kDrawArrays"];
+ VLOG(1) << "Draw buffer calls: " << draw_buffer_calls;
+ VLOG(1) << "DrawArrays calls: " << draw_arrays_calls;
+
+ if (draw_buffer_calls) {
+ // When using MRT, the YUV readback code should only
+ // execute two draw arrays, and scaling should be integrated
+ // into those two calls since we are using the FAST scalign
+ // quality.
+ EXPECT_EQ(2, draw_arrays_calls);
+ } else {
+ // When not using MRT, there are three passes for the YUV,
+ // and one for the scaling.
+ EXPECT_EQ(4, draw_arrays_calls);
+ }
+}
+
+class YUVReadbackPixelTest
+ : public YUVReadbackTest,
+ public ::testing::WithParamInterface<
+ std::tr1::tuple<bool, bool, unsigned int, unsigned int>> {};
+
+TEST_P(YUVReadbackPixelTest, Test) {
+ bool flip = std::tr1::get<0>(GetParam());
+ bool use_mrt = std::tr1::get<1>(GetParam());
+ unsigned int x = std::tr1::get<2>(GetParam());
+ unsigned int y = std::tr1::get<3>(GetParam());
+
+ for (unsigned int ox = x; ox < arraysize(kYUVReadbackSizes); ox++) {
+ for (unsigned int oy = y; oy < arraysize(kYUVReadbackSizes); oy++) {
+ // If output is a subsection of the destination frame, (letterbox)
+ // then try different variations of where the subsection goes.
+ for (Margin xm = x < ox ? MarginLeft : MarginRight; xm <= MarginRight;
+ xm = NextMargin(xm)) {
+ for (Margin ym = y < oy ? MarginLeft : MarginRight; ym <= MarginRight;
+ ym = NextMargin(ym)) {
+ for (int pattern = 0; pattern < 3; pattern++) {
+ TestYUVReadback(
+ kYUVReadbackSizes[x], kYUVReadbackSizes[y],
+ kYUVReadbackSizes[ox], kYUVReadbackSizes[oy],
+ compute_margin(kYUVReadbackSizes[x], kYUVReadbackSizes[ox], xm),
+ compute_margin(kYUVReadbackSizes[y], kYUVReadbackSizes[oy], ym),
+ pattern, flip, use_mrt, GLHelper::SCALER_QUALITY_GOOD);
+ if (HasFailure()) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// First argument is intentionally empty.
+INSTANTIATE_TEST_CASE_P(
+ ,
+ YUVReadbackPixelTest,
+ ::testing::Combine(
+ ::testing::Bool(),
+ ::testing::Bool(),
+ ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes)),
+ ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes))));
+
+} // namespace viz
+
+#endif
diff --git a/chromium/components/viz/display_compositor/BUILD.gn b/chromium/components/viz/display_compositor/BUILD.gn
deleted file mode 100644
index 740b277bab0..00000000000
--- a/chromium/components/viz/display_compositor/BUILD.gn
+++ /dev/null
@@ -1,175 +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.
-
-import("//build/config/ui.gni")
-import("//testing/test.gni")
-
-component("display_compositor") {
- sources = [
- "buffer_queue.cc",
- "buffer_queue.h",
- "compositor_overlay_candidate_validator.h",
- "gl_helper.cc",
- "gl_helper.h",
- "gl_helper_readback_support.cc",
- "gl_helper_readback_support.h",
- "gl_helper_scaling.cc",
- "gl_helper_scaling.h",
- "host_shared_bitmap_manager.cc",
- "host_shared_bitmap_manager.h",
- ]
-
- configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
- defines = [ "VIZ_IMPLEMENTATION" ]
-
- deps = [
- "//base",
- "//cc",
- "//cc/ipc:interfaces",
- "//cc/surfaces",
- "//gpu/command_buffer/client",
- "//gpu/command_buffer/client:gles2_interface",
- "//gpu/command_buffer/common",
- "//gpu/ipc/common:surface_handle_type",
- "//skia",
- "//ui/display/types",
- "//ui/gfx",
- ]
-
- if (is_mac) {
- sources += [
- "compositor_overlay_candidate_validator_mac.h",
- "compositor_overlay_candidate_validator_mac.mm",
- ]
- }
-
- if (is_android) {
- sources += [
- "compositor_overlay_candidate_validator_android.cc",
- "compositor_overlay_candidate_validator_android.h",
- ]
- }
-
- if (use_ozone) {
- sources += [
- "compositor_overlay_candidate_validator_ozone.cc",
- "compositor_overlay_candidate_validator_ozone.h",
- ]
-
- deps += [ "//ui/ozone" ]
- }
-
- if (is_win) {
- sources += [
- "compositor_overlay_candidate_validator_win.cc",
- "compositor_overlay_candidate_validator_win.h",
- ]
- }
-}
-
-# These are part of the components_unittests build target.
-source_set("unit_tests") {
- testonly = true
- sources = [
- "buffer_queue_unittest.cc",
- "host_shared_bitmap_manager_unittest.cc",
- ]
-
- if (!use_aura && !is_mac) {
- sources -= [ "buffer_queue_unittest.cc" ]
- }
-
- configs += [
- "//build/config/compiler:no_size_t_to_int_warning",
- "//third_party/khronos:khronos_headers",
- ]
-
- deps = [
- ":display_compositor",
- "//base",
- "//base/test:test_support",
- "//cc:test_support",
- "//gpu/command_buffer/client",
- "//gpu/command_buffer/client:gles2_implementation",
- "//gpu/ipc:gl_in_process_context",
- "//media",
- "//skia",
- "//testing/gmock",
- "//testing/gtest",
- "//ui/display/types",
- "//ui/gl:test_support",
- ]
-
- data_deps = [
- "//third_party/mesa:osmesa",
- ]
-}
-
-test("display_compositor_gl_tests") {
- sources = [
- "display_compositor_test_suite.cc",
- "display_compositor_test_suite.h",
- "gl_helper_unittest.cc",
- "run_all_unittests.cc",
- "yuv_readback_unittest.cc",
- ]
-
- configs += [
- "//build/config/compiler:no_size_t_to_int_warning",
- "//third_party/khronos:khronos_headers",
- ]
-
- deps = [
- ":display_compositor",
- "//base",
- "//base/test:test_support",
- "//cc:test_support",
- "//gpu/command_buffer/client",
- "//gpu/command_buffer/client:gles2_implementation",
- "//gpu/ipc:gl_in_process_context",
- "//media",
- "//skia",
- "//testing/gmock",
- "//testing/gtest",
- "//ui/gl:test_support",
- ]
-
- data_deps = [
- "//third_party/mesa:osmesa",
- ]
-}
-
-test("display_compositor_benchmark") {
- sources = [
- "display_compositor_test_suite.cc",
- "display_compositor_test_suite.h",
- "gl_helper_benchmark.cc",
- "run_all_unittests.cc",
- ]
-
- configs += [
- "//build/config/compiler:no_size_t_to_int_warning",
- "//third_party/khronos:khronos_headers",
- ]
-
- deps = [
- ":display_compositor",
- "//base",
- "//base/test:test_support",
- "//cc:test_support",
- "//gpu/command_buffer/client",
- "//gpu/command_buffer/client:gles2_implementation",
- "//gpu/ipc:gl_in_process_context",
- "//media",
- "//skia",
- "//testing/gmock",
- "//testing/gtest",
- "//ui/gl:test_support",
- ]
-
- data_deps = [
- "//third_party/mesa:osmesa",
- ]
-}
diff --git a/chromium/components/viz/display_compositor/DEPS b/chromium/components/viz/display_compositor/DEPS
deleted file mode 100644
index 5f7809be7ec..00000000000
--- a/chromium/components/viz/display_compositor/DEPS
+++ /dev/null
@@ -1,27 +0,0 @@
-include_rules = [
- "+cc/base",
- "+cc/ipc",
- "+cc/output",
- "+cc/resources",
- "+cc/surfaces",
- "+cc/test",
- "+gpu/GLES2",
- "+gpu/command_buffer/client",
- "+gpu/command_buffer/common",
- "+gpu/ipc",
- "+gpu/ipc/common",
- "+mojo/public/cpp/bindings",
- "+mojo/public/cpp/system",
- "+third_party/khronos/GLES2",
- "+third_party/skia",
- "+ui/display",
- "+ui/gfx",
- "+ui/gl",
- "+ui/ozone/public",
-]
-
-specific_include_rules = {
- "yuv_readback_unittest\.cc": [
- "+media/base",
- ],
-}
diff --git a/chromium/components/viz/display_compositor/buffer_queue.cc b/chromium/components/viz/display_compositor/buffer_queue.cc
deleted file mode 100644
index baa5a3877ea..00000000000
--- a/chromium/components/viz/display_compositor/buffer_queue.cc
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/display_compositor/buffer_queue.h"
-
-#include "base/containers/adapters.h"
-#include "base/memory/ptr_util.h"
-#include "build/build_config.h"
-#include "components/viz/display_compositor/gl_helper.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
-#include "third_party/skia/include/core/SkRect.h"
-#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/display/types/display_snapshot.h"
-#include "ui/gfx/gpu_memory_buffer.h"
-#include "ui/gfx/skia_util.h"
-
-namespace viz {
-
-BufferQueue::BufferQueue(gpu::gles2::GLES2Interface* gl,
- uint32_t texture_target,
- uint32_t internal_format,
- gfx::BufferFormat format,
- GLHelper* gl_helper,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::SurfaceHandle surface_handle)
- : gl_(gl),
- fbo_(0),
- allocated_count_(0),
- texture_target_(texture_target),
- internal_format_(internal_format),
- format_(format),
- gl_helper_(gl_helper),
- gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
- surface_handle_(surface_handle) {
- DCHECK(gpu::IsImageFormatCompatibleWithGpuMemoryBufferFormat(internal_format,
- format_));
-}
-
-BufferQueue::~BufferQueue() {
- FreeAllSurfaces();
-
- if (fbo_)
- gl_->DeleteFramebuffers(1, &fbo_);
-}
-
-void BufferQueue::Initialize() {
- gl_->GenFramebuffers(1, &fbo_);
-}
-
-void BufferQueue::BindFramebuffer() {
- gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
-
- if (!current_surface_)
- current_surface_ = GetNextSurface();
-
- if (current_surface_) {
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- texture_target_, current_surface_->texture, 0);
- if (current_surface_->stencil) {
- gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, current_surface_->stencil);
- }
- }
-}
-
-void BufferQueue::CopyBufferDamage(int texture,
- int source_texture,
- const gfx::Rect& new_damage,
- const gfx::Rect& old_damage) {
- gl_helper_->CopySubBufferDamage(texture_target_, texture, source_texture,
- SkRegion(gfx::RectToSkIRect(new_damage)),
- SkRegion(gfx::RectToSkIRect(old_damage)));
-}
-
-void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) {
- if (displayed_surface_)
- displayed_surface_->damage.Union(damage);
- for (auto& surface : available_surfaces_)
- surface->damage.Union(damage);
- for (auto& surface : in_flight_surfaces_) {
- if (surface)
- surface->damage.Union(damage);
- }
-}
-
-void BufferQueue::SwapBuffers(const gfx::Rect& damage) {
- if (current_surface_) {
- if (damage != gfx::Rect(size_)) {
- // Copy damage from the most recently swapped buffer. In the event that
- // the buffer was destroyed and failed to recreate, pick from the most
- // recently available buffer.
- uint32_t texture_id = 0;
- for (auto& surface : base::Reversed(in_flight_surfaces_)) {
- if (surface) {
- texture_id = surface->texture;
- break;
- }
- }
- if (!texture_id && displayed_surface_)
- texture_id = displayed_surface_->texture;
-
- if (texture_id) {
- CopyBufferDamage(current_surface_->texture, texture_id, damage,
- current_surface_->damage);
- }
- }
- current_surface_->damage = gfx::Rect();
- }
- UpdateBufferDamage(damage);
- in_flight_surfaces_.push_back(std::move(current_surface_));
- // Some things reset the framebuffer (CopySubBufferDamage, some GLRenderer
- // paths), so ensure we restore it here.
- gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
-}
-
-void BufferQueue::Reshape(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- bool use_stencil) {
- if (size == size_ && color_space == color_space_ &&
- use_stencil == use_stencil_)
- return;
-#if !defined(OS_MACOSX)
- // TODO(ccameron): This assert is being hit on Mac try jobs. Determine if that
- // is cause for concern or if it is benign.
- // http://crbug.com/524624
- DCHECK(!current_surface_);
-#endif
- size_ = size;
- color_space_ = color_space;
- use_stencil_ = use_stencil;
-
- gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- texture_target_, 0, 0);
- gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, 0);
-
- FreeAllSurfaces();
-}
-
-void BufferQueue::RecreateBuffers() {
- // We need to recreate the buffers, for whatever reason the old ones are not
- // presentable on the device anymore.
- // Unused buffers can be freed directly, they will be re-allocated as needed.
- // Any in flight, current or displayed surface must be replaced.
- available_surfaces_.clear();
-
- // Recreate all in-flight surfaces and put the recreated copies in the queue.
- for (auto& surface : in_flight_surfaces_)
- surface = RecreateBuffer(std::move(surface));
-
- current_surface_ = RecreateBuffer(std::move(current_surface_));
- displayed_surface_ = RecreateBuffer(std::move(displayed_surface_));
-
- if (current_surface_) {
- // If we have a texture bound, we will need to re-bind it.
- gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- texture_target_, current_surface_->texture, 0);
- }
-}
-
-std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::RecreateBuffer(
- std::unique_ptr<AllocatedSurface> surface) {
- if (!surface)
- return nullptr;
-
- std::unique_ptr<AllocatedSurface> new_surface(GetNextSurface());
- if (!new_surface)
- return nullptr;
-
- new_surface->damage = surface->damage;
-
- // Copy the entire texture.
- CopyBufferDamage(new_surface->texture, surface->texture, gfx::Rect(),
- gfx::Rect(size_));
- return new_surface;
-}
-
-void BufferQueue::PageFlipComplete() {
- // Early out when no surface is in-flight. This can happen when using
- // overlays and page flipping without changing the primary plane.
- if (in_flight_surfaces_.empty())
- return;
- if (displayed_surface_)
- available_surfaces_.push_back(std::move(displayed_surface_));
- displayed_surface_ = std::move(in_flight_surfaces_.front());
- in_flight_surfaces_.pop_front();
-}
-
-uint32_t BufferQueue::GetCurrentTextureId() const {
- // Return current surface texture if bound.
- if (current_surface_)
- return current_surface_->texture;
-
- // Return in-flight or displayed surface texture if no surface is
- // currently bound. This can happen when using overlays and surface
- // damage is empty. Note: |in_flight_surfaces_| entries can be null
- // as a result of calling FreeAllSurfaces().
- if (!in_flight_surfaces_.empty() && in_flight_surfaces_.back())
- return in_flight_surfaces_.back()->texture;
- if (displayed_surface_)
- return displayed_surface_->texture;
-
- return 0;
-}
-
-void BufferQueue::FreeAllSurfaces() {
- displayed_surface_.reset();
- current_surface_.reset();
- // This is intentionally not emptied since the swap buffers acks are still
- // expected to arrive.
- for (auto& surface : in_flight_surfaces_)
- surface = nullptr;
- available_surfaces_.clear();
-}
-
-void BufferQueue::FreeSurfaceResources(AllocatedSurface* surface) {
- if (!surface->texture)
- return;
-
- gl_->BindTexture(texture_target_, surface->texture);
- gl_->ReleaseTexImage2DCHROMIUM(texture_target_, surface->image);
- gl_->DeleteTextures(1, &surface->texture);
- gl_->DestroyImageCHROMIUM(surface->image);
- if (surface->stencil)
- gl_->DeleteRenderbuffers(1, &surface->stencil);
- surface->buffer.reset();
- allocated_count_--;
-}
-
-std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface() {
- if (!available_surfaces_.empty()) {
- std::unique_ptr<AllocatedSurface> surface =
- std::move(available_surfaces_.back());
- available_surfaces_.pop_back();
- return surface;
- }
-
- GLuint texture;
- gl_->GenTextures(1, &texture);
-
- GLuint stencil = 0;
- if (use_stencil_) {
- gl_->GenRenderbuffers(1, &stencil);
- gl_->BindRenderbuffer(GL_RENDERBUFFER, stencil);
- gl_->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size_.width(),
- size_.height());
- gl_->BindRenderbuffer(GL_RENDERBUFFER, 0);
- }
-
- // We don't want to allow anything more than triple buffering.
- DCHECK_LT(allocated_count_, 4U);
- std::unique_ptr<gfx::GpuMemoryBuffer> buffer(
- gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
- size_, format_, gfx::BufferUsage::SCANOUT, surface_handle_));
- if (!buffer.get()) {
- gl_->DeleteTextures(1, &texture);
- DLOG(ERROR) << "Failed to allocate GPU memory buffer";
- return nullptr;
- }
- buffer->SetColorSpaceForScanout(color_space_);
-
- uint32_t id =
- gl_->CreateImageCHROMIUM(buffer->AsClientBuffer(), size_.width(),
- size_.height(), internal_format_);
- if (!id) {
- LOG(ERROR) << "Failed to allocate backing image surface";
- gl_->DeleteTextures(1, &texture);
- return nullptr;
- }
-
- allocated_count_++;
- gl_->BindTexture(texture_target_, texture);
- gl_->BindTexImage2DCHROMIUM(texture_target_, id);
- return base::MakeUnique<AllocatedSurface>(this, std::move(buffer), texture,
- id, stencil, gfx::Rect(size_));
-}
-
-BufferQueue::AllocatedSurface::AllocatedSurface(
- BufferQueue* buffer_queue,
- std::unique_ptr<gfx::GpuMemoryBuffer> buffer,
- uint32_t texture,
- uint32_t image,
- uint32_t stencil,
- const gfx::Rect& rect)
- : buffer_queue(buffer_queue),
- buffer(buffer.release()),
- texture(texture),
- image(image),
- stencil(stencil),
- damage(rect) {}
-
-BufferQueue::AllocatedSurface::~AllocatedSurface() {
- buffer_queue->FreeSurfaceResources(this);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/buffer_queue.h b/chromium/components/viz/display_compositor/buffer_queue.h
deleted file mode 100644
index a0e91441eb0..00000000000
--- a/chromium/components/viz/display_compositor/buffer_queue.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_BUFFER_QUEUE_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_BUFFER_QUEUE_H_
-
-#include <stddef.h>
-
-#include <deque>
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/viz/viz_export.h"
-#include "gpu/ipc/common/surface_handle.h"
-#include "ui/gfx/buffer_types.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace gfx {
-class GpuMemoryBuffer;
-}
-
-namespace gpu {
-class GpuMemoryBufferManager;
-
-namespace gles2 {
-class GLES2Interface;
-}
-}
-
-namespace viz {
-
-class GLHelper;
-
-// Provides a surface that manages its own buffers, backed by GpuMemoryBuffers
-// created using CHROMIUM_image. Double/triple buffering is implemented
-// internally. Doublebuffering occurs if PageFlipComplete is called before the
-// next BindFramebuffer call, otherwise it creates extra buffers.
-class VIZ_EXPORT BufferQueue {
- public:
- BufferQueue(gpu::gles2::GLES2Interface* gl,
- uint32_t texture_target,
- uint32_t internal_format,
- gfx::BufferFormat format,
- GLHelper* gl_helper,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::SurfaceHandle surface_handle);
- virtual ~BufferQueue();
-
- void Initialize();
-
- void BindFramebuffer();
- void SwapBuffers(const gfx::Rect& damage);
- void PageFlipComplete();
- void Reshape(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- bool use_stencil);
- void RecreateBuffers();
- uint32_t GetCurrentTextureId() const;
-
- uint32_t fbo() const { return fbo_; }
- uint32_t internal_format() const { return internal_format_; }
-
- private:
- friend class BufferQueueTest;
- friend class AllocatedSurface;
-
- struct VIZ_EXPORT AllocatedSurface {
- AllocatedSurface(BufferQueue* buffer_queue,
- std::unique_ptr<gfx::GpuMemoryBuffer> buffer,
- uint32_t texture,
- uint32_t image,
- uint32_t stencil,
- const gfx::Rect& rect);
- ~AllocatedSurface();
- BufferQueue* const buffer_queue;
- std::unique_ptr<gfx::GpuMemoryBuffer> buffer;
- const uint32_t texture;
- const uint32_t image;
- const uint32_t stencil;
- gfx::Rect damage; // This is the damage for this frame from the previous.
- };
-
- void FreeAllSurfaces();
-
- void FreeSurfaceResources(AllocatedSurface* surface);
-
- // Copy everything that is in |copy_rect|, except for what is in
- // |exclude_rect| from |source_texture| to |texture|.
- virtual void CopyBufferDamage(int texture,
- int source_texture,
- const gfx::Rect& new_damage,
- const gfx::Rect& old_damage);
-
- void UpdateBufferDamage(const gfx::Rect& damage);
-
- // Return a surface, available to be drawn into.
- std::unique_ptr<AllocatedSurface> GetNextSurface();
-
- std::unique_ptr<AllocatedSurface> RecreateBuffer(
- std::unique_ptr<AllocatedSurface> surface);
-
- gpu::gles2::GLES2Interface* const gl_;
- gfx::Size size_;
- gfx::ColorSpace color_space_;
- bool use_stencil_ = false;
- uint32_t fbo_;
- size_t allocated_count_;
- uint32_t texture_target_;
- uint32_t internal_format_;
- gfx::BufferFormat format_;
- // This surface is currently bound. This may be nullptr if no surface has
- // been bound, or if allocation failed at bind.
- std::unique_ptr<AllocatedSurface> current_surface_;
- // The surface currently on the screen, if any.
- std::unique_ptr<AllocatedSurface> displayed_surface_;
- // These are free for use, and are not nullptr.
- std::vector<std::unique_ptr<AllocatedSurface>> available_surfaces_;
- // These have been swapped but are not displayed yet. Entries of this deque
- // may be nullptr, if they represent frames that have been destroyed.
- std::deque<std::unique_ptr<AllocatedSurface>> in_flight_surfaces_;
- GLHelper* gl_helper_;
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
- gpu::SurfaceHandle surface_handle_;
-
- DISALLOW_COPY_AND_ASSIGN(BufferQueue);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_BUFFER_QUEUE_H_
diff --git a/chromium/components/viz/display_compositor/buffer_queue_unittest.cc b/chromium/components/viz/display_compositor/buffer_queue_unittest.cc
deleted file mode 100644
index 44c36aaa596..00000000000
--- a/chromium/components/viz/display_compositor/buffer_queue_unittest.cc
+++ /dev/null
@@ -1,680 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/display_compositor/buffer_queue.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <set>
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "cc/test/test_context_provider.h"
-#include "cc/test/test_gpu_memory_buffer_manager.h"
-#include "cc/test/test_web_graphics_context_3d.h"
-#include "components/viz/display_compositor/gl_helper.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/display/types/display_snapshot.h"
-
-using ::testing::_;
-using ::testing::Expectation;
-using ::testing::Ne;
-using ::testing::Return;
-
-namespace viz {
-
-class StubGpuMemoryBufferImpl : public gfx::GpuMemoryBuffer {
- public:
- explicit StubGpuMemoryBufferImpl(size_t* set_color_space_count)
- : set_color_space_count_(set_color_space_count) {}
-
- // Overridden from gfx::GpuMemoryBuffer:
- bool Map() override { return false; }
- void* memory(size_t plane) override { return nullptr; }
- void Unmap() override {}
- gfx::Size GetSize() const override { return gfx::Size(); }
- gfx::BufferFormat GetFormat() const override {
- return gfx::BufferFormat::BGRX_8888;
- }
- int stride(size_t plane) const override { return 0; }
- gfx::GpuMemoryBufferId GetId() const override {
- return gfx::GpuMemoryBufferId(0);
- }
- void SetColorSpaceForScanout(const gfx::ColorSpace& color_space) override {
- *set_color_space_count_ += 1;
- }
- gfx::GpuMemoryBufferHandle GetHandle() const override {
- return gfx::GpuMemoryBufferHandle();
- }
- ClientBuffer AsClientBuffer() override {
- return reinterpret_cast<ClientBuffer>(this);
- }
-
- size_t* set_color_space_count_;
-};
-
-class StubGpuMemoryBufferManager : public cc::TestGpuMemoryBufferManager {
- public:
- StubGpuMemoryBufferManager() : allocate_succeeds_(true) {}
-
- size_t set_color_space_count() const { return set_color_space_count_; }
-
- void set_allocate_succeeds(bool value) { allocate_succeeds_ = value; }
-
- std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
- const gfx::Size& size,
- gfx::BufferFormat format,
- gfx::BufferUsage usage,
- gpu::SurfaceHandle surface_handle) override {
- if (!surface_handle) {
- return TestGpuMemoryBufferManager::CreateGpuMemoryBuffer(
- size, format, usage, surface_handle);
- }
- if (allocate_succeeds_)
- return base::WrapUnique<gfx::GpuMemoryBuffer>(
- new StubGpuMemoryBufferImpl(&set_color_space_count_));
- return nullptr;
- }
-
- private:
- bool allocate_succeeds_;
- size_t set_color_space_count_ = 0;
-};
-
-#if defined(OS_WIN)
-const gpu::SurfaceHandle kFakeSurfaceHandle =
- reinterpret_cast<gpu::SurfaceHandle>(1);
-#else
-const gpu::SurfaceHandle kFakeSurfaceHandle = 1;
-#endif
-
-class MockBufferQueue : public BufferQueue {
- public:
- MockBufferQueue(gpu::gles2::GLES2Interface* gl,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- unsigned int target,
- unsigned int internalformat)
- : BufferQueue(gl,
- target,
- internalformat,
- display::DisplaySnapshot::PrimaryFormat(),
- nullptr,
- gpu_memory_buffer_manager,
- kFakeSurfaceHandle) {}
- MOCK_METHOD4(CopyBufferDamage,
- void(int, int, const gfx::Rect&, const gfx::Rect&));
-};
-
-class BufferQueueTest : public ::testing::Test {
- public:
- BufferQueueTest() : doublebuffering_(true), first_frame_(true) {}
-
- void SetUp() override {
- InitWithContext(cc::TestWebGraphicsContext3D::Create());
- }
-
- void InitWithContext(std::unique_ptr<cc::TestWebGraphicsContext3D> context) {
- context_provider_ = cc::TestContextProvider::Create(std::move(context));
- context_provider_->BindToCurrentThread();
- gpu_memory_buffer_manager_.reset(new StubGpuMemoryBufferManager);
- mock_output_surface_ = new MockBufferQueue(context_provider_->ContextGL(),
- gpu_memory_buffer_manager_.get(),
- GL_TEXTURE_2D, GL_RGB);
- output_surface_.reset(mock_output_surface_);
- output_surface_->Initialize();
- }
-
- unsigned current_surface() {
- return output_surface_->current_surface_
- ? output_surface_->current_surface_->image
- : 0;
- }
- const std::vector<std::unique_ptr<BufferQueue::AllocatedSurface>>&
- available_surfaces() {
- return output_surface_->available_surfaces_;
- }
- std::deque<std::unique_ptr<BufferQueue::AllocatedSurface>>&
- in_flight_surfaces() {
- return output_surface_->in_flight_surfaces_;
- }
-
- const BufferQueue::AllocatedSurface* displayed_frame() {
- return output_surface_->displayed_surface_.get();
- }
- const BufferQueue::AllocatedSurface* current_frame() {
- return output_surface_->current_surface_.get();
- }
- const BufferQueue::AllocatedSurface* next_frame() {
- return output_surface_->available_surfaces_.back().get();
- }
- const gfx::Size size() { return output_surface_->size_; }
-
- int CountBuffers() {
- int n = available_surfaces().size() + in_flight_surfaces().size() +
- (displayed_frame() ? 1 : 0);
- if (current_surface())
- n++;
- return n;
- }
-
- // Check that each buffer is unique if present.
- void CheckUnique() {
- std::set<unsigned> buffers;
- EXPECT_TRUE(InsertUnique(&buffers, current_surface()));
- if (displayed_frame())
- EXPECT_TRUE(InsertUnique(&buffers, displayed_frame()->image));
- for (auto& surface : available_surfaces())
- EXPECT_TRUE(InsertUnique(&buffers, surface->image));
- for (auto& surface : in_flight_surfaces()) {
- if (surface)
- EXPECT_TRUE(InsertUnique(&buffers, surface->image));
- }
- }
-
- void SwapBuffers() {
- output_surface_->SwapBuffers(gfx::Rect(output_surface_->size_));
- }
-
- void SendDamagedFrame(const gfx::Rect& damage) {
- // We don't care about the GL-level implementation here, just how it uses
- // damage rects.
- output_surface_->BindFramebuffer();
- output_surface_->SwapBuffers(damage);
- if (doublebuffering_ || !first_frame_)
- output_surface_->PageFlipComplete();
- first_frame_ = false;
- }
-
- void SendFullFrame() { SendDamagedFrame(gfx::Rect(output_surface_->size_)); }
-
- protected:
- bool InsertUnique(std::set<unsigned>* set, unsigned value) {
- if (!value)
- return true;
- if (set->find(value) != set->end())
- return false;
- set->insert(value);
- return true;
- }
-
- scoped_refptr<cc::TestContextProvider> context_provider_;
- std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager_;
- std::unique_ptr<BufferQueue> output_surface_;
- MockBufferQueue* mock_output_surface_;
- bool doublebuffering_;
- bool first_frame_;
-};
-
-namespace {
-const gfx::Size screen_size = gfx::Size(30, 30);
-const gfx::Rect screen_rect = gfx::Rect(screen_size);
-const gfx::Rect small_damage = gfx::Rect(gfx::Size(10, 10));
-const gfx::Rect large_damage = gfx::Rect(gfx::Size(20, 20));
-const gfx::Rect overlapping_damage = gfx::Rect(gfx::Size(5, 20));
-
-GLuint CreateImageDefault() {
- static GLuint id = 0;
- return ++id;
-}
-
-class MockedContext : public cc::TestWebGraphicsContext3D {
- public:
- MockedContext() {
- 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,
- GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
- MOCK_METHOD1(destroyImageCHROMIUM, void(GLuint));
- MOCK_METHOD5(framebufferTexture2D,
- void(GLenum, GLenum, GLenum, GLuint, GLint));
-};
-
-class BufferQueueMockedContextTest : public BufferQueueTest {
- public:
- void SetUp() override {
- context_ = new MockedContext();
- InitWithContext(std::unique_ptr<cc::TestWebGraphicsContext3D>(context_));
- }
-
- protected:
- MockedContext* context_;
-};
-
-scoped_refptr<cc::TestContextProvider> CreateMockedContextProvider(
- MockedContext** context) {
- std::unique_ptr<MockedContext> owned_context(new MockedContext);
- *context = owned_context.get();
- scoped_refptr<cc::TestContextProvider> context_provider =
- cc::TestContextProvider::Create(std::move(owned_context));
- context_provider->BindToCurrentThread();
- return context_provider;
-}
-
-std::unique_ptr<BufferQueue> CreateBufferQueue(
- unsigned int target,
- gpu::gles2::GLES2Interface* gl,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) {
- std::unique_ptr<BufferQueue> buffer_queue(new BufferQueue(
- gl, target, GL_RGB, display::DisplaySnapshot::PrimaryFormat(), nullptr,
- gpu_memory_buffer_manager, kFakeSurfaceHandle));
- buffer_queue->Initialize();
- return buffer_queue;
-}
-
-TEST(BufferQueueStandaloneTest, FboInitialization) {
- MockedContext* context;
- scoped_refptr<cc::TestContextProvider> context_provider =
- CreateMockedContextProvider(&context);
- std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager(
- new StubGpuMemoryBufferManager);
- std::unique_ptr<BufferQueue> output_surface =
- CreateBufferQueue(GL_TEXTURE_2D, context_provider->ContextGL(),
- gpu_memory_buffer_manager.get());
-
- 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);
-}
-
-TEST(BufferQueueStandaloneTest, FboBinding) {
- GLenum targets[] = {GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB};
- for (size_t i = 0; i < 2; ++i) {
- GLenum target = targets[i];
-
- MockedContext* context;
- scoped_refptr<cc::TestContextProvider> context_provider =
- CreateMockedContextProvider(&context);
- std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager(
- new StubGpuMemoryBufferManager);
- 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));
- Expectation image =
- EXPECT_CALL(*context, createImageCHROMIUM(_, 0, 0, GL_RGB))
- .WillOnce(Return(1));
- Expectation fb =
- 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))
- .After(tex, image);
- EXPECT_CALL(*context,
- framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- target, Ne(0U), _))
- .After(fb, bind_tex);
-
- output_surface->BindFramebuffer();
- }
-}
-
-TEST(BufferQueueStandaloneTest, CheckBoundFramebuffer) {
- scoped_refptr<cc::TestContextProvider> context_provider =
- cc::TestContextProvider::Create();
- context_provider->BindToCurrentThread();
- std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager;
- std::unique_ptr<BufferQueue> output_surface;
- gpu_memory_buffer_manager.reset(new StubGpuMemoryBufferManager);
-
- std::unique_ptr<GLHelper> gl_helper;
- gl_helper.reset(new GLHelper(context_provider->ContextGL(),
- context_provider->ContextSupport()));
-
- output_surface.reset(new BufferQueue(
- context_provider->ContextGL(), GL_TEXTURE_2D, GL_RGB,
- display::DisplaySnapshot::PrimaryFormat(), gl_helper.get(),
- gpu_memory_buffer_manager.get(), kFakeSurfaceHandle));
- output_surface->Initialize();
- output_surface->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
- // Trigger a sub-buffer copy to exercise all paths.
- output_surface->BindFramebuffer();
- output_surface->SwapBuffers(screen_rect);
- output_surface->PageFlipComplete();
- output_surface->BindFramebuffer();
- output_surface->SwapBuffers(small_damage);
-
- int current_fbo = 0;
- context_provider->ContextGL()->GetIntegerv(GL_FRAMEBUFFER_BINDING,
- &current_fbo);
- EXPECT_EQ(static_cast<int>(output_surface->fbo()), current_fbo);
-}
-
-TEST_F(BufferQueueTest, PartialSwapReuse) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
- ASSERT_TRUE(doublebuffering_);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, _, small_damage, screen_rect))
- .Times(1);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, _, small_damage, small_damage))
- .Times(1);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, _, large_damage, small_damage))
- .Times(1);
- SendFullFrame();
- SendDamagedFrame(small_damage);
- SendDamagedFrame(small_damage);
- SendDamagedFrame(large_damage);
- // Verify that the damage has propagated.
- EXPECT_EQ(next_frame()->damage, large_damage);
-}
-
-TEST_F(BufferQueueTest, PartialSwapFullFrame) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
- ASSERT_TRUE(doublebuffering_);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, _, small_damage, screen_rect))
- .Times(1);
- SendFullFrame();
- SendDamagedFrame(small_damage);
- SendFullFrame();
- SendFullFrame();
- EXPECT_EQ(next_frame()->damage, screen_rect);
-}
-
-TEST_F(BufferQueueTest, PartialSwapOverlapping) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
- ASSERT_TRUE(doublebuffering_);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, _, small_damage, screen_rect))
- .Times(1);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, _, overlapping_damage, small_damage))
- .Times(1);
-
- SendFullFrame();
- SendDamagedFrame(small_damage);
- SendDamagedFrame(overlapping_damage);
- EXPECT_EQ(next_frame()->damage, overlapping_damage);
-}
-
-TEST_F(BufferQueueTest, MultipleBindCalls) {
- // Check that multiple bind calls do not create or change surfaces.
- output_surface_->BindFramebuffer();
- EXPECT_EQ(1, CountBuffers());
- unsigned int fb = current_surface();
- output_surface_->BindFramebuffer();
- EXPECT_EQ(1, CountBuffers());
- EXPECT_EQ(fb, current_surface());
-}
-
-TEST_F(BufferQueueTest, CheckDoubleBuffering) {
- // Check buffer flow through double buffering path.
- EXPECT_EQ(0, CountBuffers());
- output_surface_->BindFramebuffer();
- EXPECT_EQ(1, CountBuffers());
- EXPECT_NE(0U, current_surface());
- EXPECT_FALSE(displayed_frame());
- SwapBuffers();
- EXPECT_EQ(1U, in_flight_surfaces().size());
- output_surface_->PageFlipComplete();
- EXPECT_EQ(0U, in_flight_surfaces().size());
- EXPECT_TRUE(displayed_frame()->texture);
- output_surface_->BindFramebuffer();
- EXPECT_EQ(2, CountBuffers());
- CheckUnique();
- EXPECT_NE(0U, current_surface());
- EXPECT_EQ(0U, in_flight_surfaces().size());
- EXPECT_TRUE(displayed_frame()->texture);
- SwapBuffers();
- CheckUnique();
- EXPECT_EQ(1U, in_flight_surfaces().size());
- EXPECT_TRUE(displayed_frame()->texture);
- output_surface_->PageFlipComplete();
- CheckUnique();
- EXPECT_EQ(0U, in_flight_surfaces().size());
- EXPECT_EQ(1U, available_surfaces().size());
- EXPECT_TRUE(displayed_frame()->texture);
- output_surface_->BindFramebuffer();
- EXPECT_EQ(2, CountBuffers());
- CheckUnique();
- EXPECT_TRUE(available_surfaces().empty());
-}
-
-TEST_F(BufferQueueTest, CheckTripleBuffering) {
- // Check buffer flow through triple buffering path.
-
- // This bit is the same sequence tested in the doublebuffering case.
- output_surface_->BindFramebuffer();
- EXPECT_FALSE(displayed_frame());
- SwapBuffers();
- output_surface_->PageFlipComplete();
- output_surface_->BindFramebuffer();
- SwapBuffers();
-
- EXPECT_EQ(2, CountBuffers());
- CheckUnique();
- EXPECT_EQ(1U, in_flight_surfaces().size());
- EXPECT_TRUE(displayed_frame()->texture);
- output_surface_->BindFramebuffer();
- EXPECT_EQ(3, CountBuffers());
- CheckUnique();
- EXPECT_NE(0U, current_surface());
- EXPECT_EQ(1U, in_flight_surfaces().size());
- EXPECT_TRUE(displayed_frame()->texture);
- output_surface_->PageFlipComplete();
- EXPECT_EQ(3, CountBuffers());
- CheckUnique();
- EXPECT_NE(0U, current_surface());
- EXPECT_EQ(0U, in_flight_surfaces().size());
- EXPECT_TRUE(displayed_frame()->texture);
- EXPECT_EQ(1U, available_surfaces().size());
-}
-
-TEST_F(BufferQueueTest, CheckCorrectBufferOrdering) {
- const size_t kSwapCount = 3;
- for (size_t i = 0; i < kSwapCount; ++i) {
- output_surface_->BindFramebuffer();
- SwapBuffers();
- }
-
- EXPECT_EQ(kSwapCount, in_flight_surfaces().size());
- for (size_t i = 0; i < kSwapCount; ++i) {
- unsigned int next_texture_id = in_flight_surfaces().front()->texture;
- output_surface_->PageFlipComplete();
- EXPECT_EQ(displayed_frame()->texture, next_texture_id);
- }
-}
-
-TEST_F(BufferQueueTest, ReshapeWithInFlightSurfaces) {
- const size_t kSwapCount = 3;
- for (size_t i = 0; i < kSwapCount; ++i) {
- output_surface_->BindFramebuffer();
- SwapBuffers();
- }
-
- output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace(), false);
- EXPECT_EQ(3u, in_flight_surfaces().size());
-
- for (size_t i = 0; i < kSwapCount; ++i) {
- output_surface_->PageFlipComplete();
- EXPECT_FALSE(displayed_frame());
- }
-
- // The dummy surfacess left should be discarded.
- EXPECT_EQ(0u, available_surfaces().size());
-}
-
-TEST_F(BufferQueueTest, SwapAfterReshape) {
- DCHECK_EQ(0u, gpu_memory_buffer_manager_->set_color_space_count());
- const size_t kSwapCount = 3;
- for (size_t i = 0; i < kSwapCount; ++i) {
- output_surface_->BindFramebuffer();
- SwapBuffers();
- }
- DCHECK_EQ(kSwapCount, gpu_memory_buffer_manager_->set_color_space_count());
-
- output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace(), false);
- DCHECK_EQ(kSwapCount, gpu_memory_buffer_manager_->set_color_space_count());
-
- for (size_t i = 0; i < kSwapCount; ++i) {
- output_surface_->BindFramebuffer();
- SwapBuffers();
- }
- DCHECK_EQ(2 * kSwapCount,
- gpu_memory_buffer_manager_->set_color_space_count());
- EXPECT_EQ(2 * kSwapCount, in_flight_surfaces().size());
-
- for (size_t i = 0; i < kSwapCount; ++i) {
- output_surface_->PageFlipComplete();
- EXPECT_FALSE(displayed_frame());
- }
-
- CheckUnique();
-
- for (size_t i = 0; i < kSwapCount; ++i) {
- unsigned int next_texture_id = in_flight_surfaces().front()->texture;
- output_surface_->PageFlipComplete();
- EXPECT_EQ(displayed_frame()->texture, next_texture_id);
- EXPECT_TRUE(displayed_frame());
- }
-
- DCHECK_EQ(2 * kSwapCount,
- gpu_memory_buffer_manager_->set_color_space_count());
- for (size_t i = 0; i < kSwapCount; ++i) {
- output_surface_->BindFramebuffer();
- SwapBuffers();
- output_surface_->PageFlipComplete();
- }
- DCHECK_EQ(2 * kSwapCount,
- gpu_memory_buffer_manager_->set_color_space_count());
-}
-
-TEST_F(BufferQueueMockedContextTest, RecreateBuffers) {
- // This setup is to easily get one frame in each of:
- // - currently bound for drawing.
- // - in flight to GPU.
- // - currently displayed.
- // - free frame.
- // This tests buffers in all states.
- // Bind/swap pushes frames into the in flight list, then the PageFlipComplete
- // calls pull one frame into displayed and another into the free list.
- output_surface_->BindFramebuffer();
- SwapBuffers();
- output_surface_->BindFramebuffer();
- SwapBuffers();
- output_surface_->BindFramebuffer();
- SwapBuffers();
- output_surface_->BindFramebuffer();
- output_surface_->PageFlipComplete();
- output_surface_->PageFlipComplete();
- // We should have one buffer in each possible state right now, including one
- // being drawn to.
- ASSERT_EQ(1U, in_flight_surfaces().size());
- ASSERT_EQ(1U, available_surfaces().size());
- EXPECT_TRUE(displayed_frame());
- EXPECT_TRUE(current_frame());
-
- auto* current = current_frame();
- auto* displayed = displayed_frame();
- auto* in_flight = in_flight_surfaces().front().get();
- auto* available = available_surfaces().front().get();
-
- // 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(_, 0, 0, GL_RGB)).Times(3);
- Expectation copy1 = EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, displayed->texture, _, _))
- .Times(1);
- Expectation copy2 = EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, current->texture, _, _))
- .Times(1);
- Expectation copy3 = EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(_, in_flight->texture, _, _))
- .Times(1);
-
- EXPECT_CALL(*context_, destroyImageCHROMIUM(displayed->image))
- .Times(1)
- .After(copy1);
- EXPECT_CALL(*context_, destroyImageCHROMIUM(current->image))
- .Times(1)
- .After(copy2);
- EXPECT_CALL(*context_, destroyImageCHROMIUM(in_flight->image))
- .Times(1)
- .After(copy3);
- EXPECT_CALL(*context_, destroyImageCHROMIUM(available->image)).Times(1);
- // After copying, we expect the framebuffer binding to be updated.
- EXPECT_CALL(*context_, bindFramebuffer(_, _))
- .After(copy1)
- .After(copy2)
- .After(copy3);
- EXPECT_CALL(*context_, framebufferTexture2D(_, _, _, _, _))
- .After(copy1)
- .After(copy2)
- .After(copy3);
-
- output_surface_->RecreateBuffers();
- testing::Mock::VerifyAndClearExpectations(context_);
- testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
-
- // All free buffers should be destroyed, the remaining buffers should all
- // be replaced but still valid.
- EXPECT_EQ(1U, in_flight_surfaces().size());
- EXPECT_EQ(0U, available_surfaces().size());
- EXPECT_TRUE(displayed_frame());
- EXPECT_TRUE(current_frame());
-}
-
-TEST_F(BufferQueueTest, AllocateFails) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
-
- // Succeed in the two swaps.
- output_surface_->BindFramebuffer();
- EXPECT_TRUE(current_frame());
- output_surface_->SwapBuffers(screen_rect);
-
- // Fail the next surface allocation.
- gpu_memory_buffer_manager_->set_allocate_succeeds(false);
- output_surface_->BindFramebuffer();
- EXPECT_FALSE(current_frame());
- output_surface_->SwapBuffers(screen_rect);
- EXPECT_FALSE(current_frame());
-
- // Try another swap. It should copy the buffer damage from the back
- // surface.
- gpu_memory_buffer_manager_->set_allocate_succeeds(true);
- output_surface_->BindFramebuffer();
- unsigned int source_texture = in_flight_surfaces().front()->texture;
- unsigned int target_texture = current_frame()->texture;
- testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(target_texture, source_texture, small_damage, _))
- .Times(1);
- output_surface_->SwapBuffers(small_damage);
- testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
-
- // Destroy the just-created buffer, and try another swap. The copy should
- // come from the displayed surface (because both in-flight surfaces are
- // gone now).
- output_surface_->PageFlipComplete();
- in_flight_surfaces().back().reset();
- EXPECT_EQ(2u, in_flight_surfaces().size());
- for (auto& surface : in_flight_surfaces())
- EXPECT_FALSE(surface);
- output_surface_->BindFramebuffer();
- source_texture = displayed_frame()->texture;
- EXPECT_TRUE(current_frame());
- EXPECT_TRUE(displayed_frame());
- target_texture = current_frame()->texture;
- testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
- EXPECT_CALL(*mock_output_surface_,
- CopyBufferDamage(target_texture, source_texture, small_damage, _))
- .Times(1);
- output_surface_->SwapBuffers(small_damage);
- testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator.h b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator.h
deleted file mode 100644
index 62eae190122..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_H_
-
-#include "base/macros.h"
-#include "cc/output/overlay_candidate_validator.h"
-#include "components/viz/viz_export.h"
-
-namespace viz {
-
-class VIZ_EXPORT CompositorOverlayCandidateValidator
- : public cc::OverlayCandidateValidator {
- public:
- CompositorOverlayCandidateValidator() {}
- ~CompositorOverlayCandidateValidator() override {}
-
- virtual void SetSoftwareMirrorMode(bool enabled) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidator);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_H_
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.cc b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.cc
deleted file mode 100644
index e183ee10540..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/display_compositor/compositor_overlay_candidate_validator_android.h"
-
-#include <memory>
-
-#include "base/memory/ptr_util.h"
-#include "cc/output/overlay_processor.h"
-#include "cc/output/overlay_strategy_underlay.h"
-#include "ui/gfx/geometry/rect_conversions.h"
-
-namespace viz {
-
-CompositorOverlayCandidateValidatorAndroid::
- CompositorOverlayCandidateValidatorAndroid() {}
-
-CompositorOverlayCandidateValidatorAndroid::
- ~CompositorOverlayCandidateValidatorAndroid() {}
-
-void CompositorOverlayCandidateValidatorAndroid::GetStrategies(
- cc::OverlayProcessor::StrategyList* strategies) {
- strategies->push_back(base::MakeUnique<cc::OverlayStrategyUnderlay>(this));
-}
-
-void CompositorOverlayCandidateValidatorAndroid::CheckOverlaySupport(
- cc::OverlayCandidateList* candidates) {
- // There should only be at most a single overlay candidate: the video quad.
- // There's no check that the presented candidate is really a video frame for
- // a fullscreen video. Instead it's assumed that if a quad is marked as
- // overlayable, it's a fullscreen video quad.
- DCHECK_LE(candidates->size(), 1u);
-
- if (!candidates->empty()) {
- cc::OverlayCandidate& candidate = candidates->front();
-
- // This quad either will be promoted, or would be if it were backed by a
- // SurfaceView. Record that it should get a promotion hint.
- candidates->AddPromotionHint(candidate);
-
- if (candidate.is_backed_by_surface_texture) {
- // This quad would be promoted if it were backed by a SurfaceView. Since
- // it isn't, we can't promote it.
- return;
- }
-
- candidate.display_rect =
- gfx::RectF(gfx::ToEnclosingRect(candidate.display_rect));
- candidate.overlay_handled = true;
- candidate.plane_z_order = -1;
- }
-}
-
-bool CompositorOverlayCandidateValidatorAndroid::AllowCALayerOverlays() {
- return false;
-}
-
-bool CompositorOverlayCandidateValidatorAndroid::AllowDCLayerOverlays() {
- return false;
-}
-
-// Overlays will still be allowed when software mirroring is enabled, even
-// though they won't appear in the mirror.
-void CompositorOverlayCandidateValidatorAndroid::SetSoftwareMirrorMode(
- bool enabled) {}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.h b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.h
deleted file mode 100644
index 9f6e75c9917..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_android.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_ANDROID_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_ANDROID_H_
-
-#include "base/macros.h"
-#include "components/viz/display_compositor/compositor_overlay_candidate_validator.h"
-#include "components/viz/viz_export.h"
-
-namespace viz {
-
-// An overlay validator for supporting fullscreen video underlays on Android.
-// Things are a bit different on Android compared with other platforms. By the
-// time a video frame is marked as overlayable it means the video decoder was
-// outputting to a Surface that we can't read back from. As a result, the
-// overlay must always succeed, or the video won't be visible. This is one of of
-// the reasons that only fullscreen is supported: we have to be sure that
-// nothing will cause the overlay to be rejected, because there's no fallback to
-// gl compositing.
-class VIZ_EXPORT CompositorOverlayCandidateValidatorAndroid
- : public CompositorOverlayCandidateValidator {
- public:
- CompositorOverlayCandidateValidatorAndroid();
- ~CompositorOverlayCandidateValidatorAndroid() override;
-
- void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
- void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
- bool AllowCALayerOverlays() override;
- bool AllowDCLayerOverlays() override;
-
- void SetSoftwareMirrorMode(bool enabled) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorAndroid);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_ANDROID_H_
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.h b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.h
deleted file mode 100644
index 774c22ced75..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_MAC_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_MAC_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/viz/display_compositor/compositor_overlay_candidate_validator.h"
-#include "components/viz/viz_export.h"
-
-namespace viz {
-
-class VIZ_EXPORT CompositorOverlayCandidateValidatorMac
- : public CompositorOverlayCandidateValidator {
- public:
- explicit CompositorOverlayCandidateValidatorMac(bool ca_layer_disabled);
- ~CompositorOverlayCandidateValidatorMac() override;
-
- // cc::OverlayCandidateValidator implementation.
- void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
- bool AllowCALayerOverlays() override;
- bool AllowDCLayerOverlays() override;
- void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
-
- // CompositorOverlayCandidateValidator implementation.
- void SetSoftwareMirrorMode(bool enabled) override;
-
- private:
- bool software_mirror_active_;
- const bool ca_layer_disabled_;
-
- DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorMac);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_MAC_H_
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.mm b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.mm
deleted file mode 100644
index 1fe6d35a9ef..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_mac.mm
+++ /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.
-
-#include "components/viz/display_compositor/compositor_overlay_candidate_validator_mac.h"
-
-#include <stddef.h>
-
-namespace viz {
-
-CompositorOverlayCandidateValidatorMac::CompositorOverlayCandidateValidatorMac(
- bool ca_layer_disabled)
- : software_mirror_active_(false), ca_layer_disabled_(ca_layer_disabled) {}
-
-CompositorOverlayCandidateValidatorMac::
- ~CompositorOverlayCandidateValidatorMac() {}
-
-void CompositorOverlayCandidateValidatorMac::GetStrategies(
- cc::OverlayProcessor::StrategyList* strategies) {}
-
-bool CompositorOverlayCandidateValidatorMac::AllowCALayerOverlays() {
- return !ca_layer_disabled_ && !software_mirror_active_;
-}
-
-bool CompositorOverlayCandidateValidatorMac::AllowDCLayerOverlays() {
- return false;
-}
-
-void CompositorOverlayCandidateValidatorMac::CheckOverlaySupport(
- cc::OverlayCandidateList* surfaces) {}
-
-void CompositorOverlayCandidateValidatorMac::SetSoftwareMirrorMode(
- bool enabled) {
- software_mirror_active_ = enabled;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.cc b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.cc
deleted file mode 100644
index c21d8c7728a..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.cc
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.h"
-
-#include <stddef.h>
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/string_split.h"
-#include "cc/output/overlay_strategy_fullscreen.h"
-#include "cc/output/overlay_strategy_single_on_top.h"
-#include "cc/output/overlay_strategy_underlay.h"
-#include "cc/output/overlay_strategy_underlay_cast.h"
-#include "ui/ozone/public/overlay_candidates_ozone.h"
-
-namespace viz {
-namespace {
-// Templated function used to create an OverlayProcessor::Strategy
-// of type |S|.
-template <typename S>
-std::unique_ptr<cc::OverlayProcessor::Strategy> MakeOverlayStrategy(
- CompositorOverlayCandidateValidatorOzone* capability_checker) {
- return base::MakeUnique<S>(capability_checker);
-}
-
-} // namespace
-
-// |overlay_candidates| is an object used to answer questions about possible
-// overlays configuarations.
-// |strategies_string| is a comma-separated string containing all the overaly
-// strategies that should be returned by GetStrategies.
-// If |strategies_string| is empty "single-on-top,underlay" will be used as
-// default.
-CompositorOverlayCandidateValidatorOzone::
- CompositorOverlayCandidateValidatorOzone(
- std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates,
- std::string strategies_string)
- : overlay_candidates_(std::move(overlay_candidates)),
- software_mirror_active_(false) {
- if (!strategies_string.length())
- strategies_string = "single-on-top,underlay";
-
- for (const auto& strategy_name :
- base::SplitStringPiece(strategies_string, ",", base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY)) {
- if (strategy_name == "single-fullscreen") {
- strategies_instantiators_.push_back(
- base::Bind(MakeOverlayStrategy<cc::OverlayStrategyFullscreen>));
- } else if (strategy_name == "single-on-top") {
- strategies_instantiators_.push_back(
- base::Bind(MakeOverlayStrategy<cc::OverlayStrategySingleOnTop>));
- } else if (strategy_name == "underlay") {
- strategies_instantiators_.push_back(
- base::Bind(MakeOverlayStrategy<cc::OverlayStrategyUnderlay>));
- } else if (strategy_name == "cast") {
- strategies_instantiators_.push_back(
- base::Bind(MakeOverlayStrategy<cc::OverlayStrategyUnderlayCast>));
- } else {
- LOG(WARNING) << "Unrecognized overlay strategy " << strategy_name;
- }
- }
-}
-
-CompositorOverlayCandidateValidatorOzone::
- ~CompositorOverlayCandidateValidatorOzone() {}
-
-void CompositorOverlayCandidateValidatorOzone::GetStrategies(
- cc::OverlayProcessor::StrategyList* strategies) {
- for (auto& instantiator : strategies_instantiators_)
- strategies->push_back(instantiator.Run(this));
-}
-
-bool CompositorOverlayCandidateValidatorOzone::AllowCALayerOverlays() {
- return false;
-}
-
-bool CompositorOverlayCandidateValidatorOzone::AllowDCLayerOverlays() {
- return false;
-}
-
-void CompositorOverlayCandidateValidatorOzone::CheckOverlaySupport(
- cc::OverlayCandidateList* surfaces) {
- // SW mirroring copies out of the framebuffer, so we can't remove any
- // quads for overlaying, otherwise the output is incorrect.
- if (software_mirror_active_) {
- for (size_t i = 0; i < surfaces->size(); i++) {
- surfaces->at(i).overlay_handled = false;
- }
- return;
- }
-
- DCHECK_GE(2U, surfaces->size());
- ui::OverlayCandidatesOzone::OverlaySurfaceCandidateList ozone_surface_list;
- ozone_surface_list.resize(surfaces->size());
-
- for (size_t i = 0; i < surfaces->size(); i++) {
- ozone_surface_list.at(i).transform = surfaces->at(i).transform;
- ozone_surface_list.at(i).format = surfaces->at(i).format;
- ozone_surface_list.at(i).display_rect = surfaces->at(i).display_rect;
- ozone_surface_list.at(i).crop_rect = surfaces->at(i).uv_rect;
- ozone_surface_list.at(i).quad_rect_in_target_space =
- surfaces->at(i).quad_rect_in_target_space;
- ozone_surface_list.at(i).clip_rect = surfaces->at(i).clip_rect;
- ozone_surface_list.at(i).is_clipped = surfaces->at(i).is_clipped;
- ozone_surface_list.at(i).plane_z_order = surfaces->at(i).plane_z_order;
- ozone_surface_list.at(i).buffer_size =
- surfaces->at(i).resource_size_in_pixels;
- }
-
- overlay_candidates_->CheckOverlaySupport(&ozone_surface_list);
- DCHECK_EQ(surfaces->size(), ozone_surface_list.size());
-
- for (size_t i = 0; i < surfaces->size(); i++) {
- surfaces->at(i).overlay_handled = ozone_surface_list.at(i).overlay_handled;
- surfaces->at(i).display_rect = ozone_surface_list.at(i).display_rect;
- }
-}
-
-void CompositorOverlayCandidateValidatorOzone::SetSoftwareMirrorMode(
- bool enabled) {
- software_mirror_active_ = enabled;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.h b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.h
deleted file mode 100644
index 3e95825c8ad..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_ozone.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "components/viz/display_compositor/compositor_overlay_candidate_validator.h"
-#include "components/viz/viz_export.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace ui {
-class OverlayCandidatesOzone;
-}
-
-namespace viz {
-
-class VIZ_EXPORT CompositorOverlayCandidateValidatorOzone
- : public CompositorOverlayCandidateValidator {
- public:
- CompositorOverlayCandidateValidatorOzone(
- std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates,
- std::string strategies_string);
- ~CompositorOverlayCandidateValidatorOzone() override;
-
- // cc::OverlayCandidateValidator implementation.
- void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
- bool AllowCALayerOverlays() override;
- bool AllowDCLayerOverlays() override;
- void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
-
- // CompositorOverlayCandidateValidator implementation.
- void SetSoftwareMirrorMode(bool enabled) override;
-
- private:
- std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates_;
- // Callback declaration to allocate a new OverlayProcessor::Strategy.
- using StrategyInstantiator =
- base::Callback<std::unique_ptr<cc::OverlayProcessor::Strategy>(
- CompositorOverlayCandidateValidatorOzone*)>;
- // List callbacks used to instantiate OverlayProcessor::Strategy
- // as defined by |strategies_string| paramter in the constructor.
- std::vector<StrategyInstantiator> strategies_instantiators_;
- bool software_mirror_active_;
-
- DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorOzone);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.cc b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.cc
deleted file mode 100644
index 0a4e0f98431..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.cc
+++ /dev/null
@@ -1,39 +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/display_compositor/compositor_overlay_candidate_validator_win.h"
-
-#include "cc/output/overlay_processor.h"
-
-namespace viz {
-
-CompositorOverlayCandidateValidatorWin::
- CompositorOverlayCandidateValidatorWin() {}
-
-CompositorOverlayCandidateValidatorWin::
- ~CompositorOverlayCandidateValidatorWin() {}
-
-void CompositorOverlayCandidateValidatorWin::GetStrategies(
- cc::OverlayProcessor::StrategyList* strategies) {}
-
-void CompositorOverlayCandidateValidatorWin::CheckOverlaySupport(
- cc::OverlayCandidateList* candidates) {
- NOTIMPLEMENTED();
-}
-
-bool CompositorOverlayCandidateValidatorWin::AllowCALayerOverlays() {
- return false;
-}
-
-bool CompositorOverlayCandidateValidatorWin::AllowDCLayerOverlays() {
- return true;
-}
-
-void CompositorOverlayCandidateValidatorWin::SetSoftwareMirrorMode(
- bool enabled) {
- // Software mirroring isn't supported on Windows.
- NOTIMPLEMENTED();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.h b/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.h
deleted file mode 100644
index 9cd9446ab1d..00000000000
--- a/chromium/components/viz/display_compositor/compositor_overlay_candidate_validator_win.h
+++ /dev/null
@@ -1,35 +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_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_WIN_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_WIN_H_
-
-#include "base/macros.h"
-#include "components/viz/display_compositor/compositor_overlay_candidate_validator.h"
-#include "components/viz/viz_export.h"
-
-namespace viz {
-
-// This is a simple overlay candidate validator that promotes everything
-// possible to an overlay.
-class VIZ_EXPORT CompositorOverlayCandidateValidatorWin
- : public CompositorOverlayCandidateValidator {
- public:
- CompositorOverlayCandidateValidatorWin();
- ~CompositorOverlayCandidateValidatorWin() override;
-
- void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
- void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
- bool AllowCALayerOverlays() override;
- bool AllowDCLayerOverlays() override;
-
- void SetSoftwareMirrorMode(bool enabled) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorWin);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_WIN_H_
diff --git a/chromium/components/viz/display_compositor/display_compositor_test_suite.cc b/chromium/components/viz/display_compositor/display_compositor_test_suite.cc
deleted file mode 100644
index 8744ff84d19..00000000000
--- a/chromium/components/viz/display_compositor/display_compositor_test_suite.cc
+++ /dev/null
@@ -1,38 +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/display_compositor/display_compositor_test_suite.h"
-
-#include "base/message_loop/message_loop.h"
-#include "base/threading/thread_id_name_manager.h"
-#include "cc/test/paths.h"
-#include "ui/gl/test/gl_surface_test_support.h"
-
-namespace viz {
-
-DisplayCompositorTestSuite::DisplayCompositorTestSuite(int argc, char** argv)
- : base::TestSuite(argc, argv) {}
-
-DisplayCompositorTestSuite::~DisplayCompositorTestSuite() {}
-
-void DisplayCompositorTestSuite::Initialize() {
- base::TestSuite::Initialize();
- gl::GLSurfaceTestSupport::InitializeOneOff();
- cc::CCPaths::RegisterPathProvider();
-
- message_loop_.reset(new base::MessageLoop);
-
- base::ThreadIdNameManager::GetInstance()->SetName(
- base::PlatformThread::CurrentId(), "Main");
-
- base::DiscardableMemoryAllocator::SetInstance(&discardable_memory_allocator_);
-}
-
-void DisplayCompositorTestSuite::Shutdown() {
- message_loop_ = nullptr;
-
- base::TestSuite::Shutdown();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/display_compositor_test_suite.h b/chromium/components/viz/display_compositor/display_compositor_test_suite.h
deleted file mode 100644
index e2a755b2ec2..00000000000
--- a/chromium/components/viz/display_compositor/display_compositor_test_suite.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_VIZ_DISPLAY_COMPOSITOR_DISPLAY_COMPOSITOR_TEST_SUITE_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_DISPLAY_COMPOSITOR_TEST_SUITE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/test/test_discardable_memory_allocator.h"
-#include "base/test/test_suite.h"
-
-namespace base {
-class MessageLoop;
-}
-
-namespace viz {
-
-class DisplayCompositorTestSuite : public base::TestSuite {
- public:
- DisplayCompositorTestSuite(int argc, char** argv);
- ~DisplayCompositorTestSuite() override;
-
- protected:
- // Overridden from base::TestSuite:
- void Initialize() override;
- void Shutdown() override;
-
- private:
- std::unique_ptr<base::MessageLoop> message_loop_;
-
- base::TestDiscardableMemoryAllocator discardable_memory_allocator_;
- DISALLOW_COPY_AND_ASSIGN(DisplayCompositorTestSuite);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_DISPLAY_COMPOSITOR_TEST_SUITE_H_
diff --git a/chromium/components/viz/display_compositor/gl_helper.cc b/chromium/components/viz/display_compositor/gl_helper.cc
deleted file mode 100644
index 0d3745520e5..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper.cc
+++ /dev/null
@@ -1,1237 +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/viz/display_compositor/gl_helper.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <queue>
-#include <string>
-
-#include "base/bind.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string_util.h"
-#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
-#include "components/viz/display_compositor/gl_helper_readback_support.h"
-#include "components/viz/display_compositor/gl_helper_scaling.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/mailbox_holder.h"
-#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-
-using gpu::gles2::GLES2Interface;
-
-namespace {
-
-class ScopedFlush {
- public:
- explicit ScopedFlush(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
-
- ~ScopedFlush() { gl_->Flush(); }
-
- private:
- gpu::gles2::GLES2Interface* gl_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFlush);
-};
-
-// Helper class for allocating and holding an RGBA texture of a given
-// size and an associated framebuffer.
-class TextureFrameBufferPair {
- public:
- TextureFrameBufferPair(GLES2Interface* gl, gfx::Size size)
- : texture_(gl), framebuffer_(gl), size_(size) {
- viz::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_);
- gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- viz::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
- gl, framebuffer_);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture_, 0);
- }
-
- GLuint texture() const { return texture_.id(); }
- GLuint framebuffer() const { return framebuffer_.id(); }
- gfx::Size size() const { return size_; }
-
- private:
- viz::ScopedTexture texture_;
- viz::ScopedFramebuffer framebuffer_;
- gfx::Size size_;
-
- DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair);
-};
-
-// Helper class for holding a scaler, a texture for the output of that
-// scaler and an associated frame buffer. This is inteded to be used
-// when the output of a scaler is to be sent to a readback.
-class ScalerHolder {
- public:
- ScalerHolder(GLES2Interface* gl, viz::GLHelper::ScalerInterface* scaler)
- : texture_and_framebuffer_(gl, scaler->DstSize()), scaler_(scaler) {}
-
- void Scale(GLuint src_texture) {
- scaler_->Scale(src_texture, texture_and_framebuffer_.texture());
- }
-
- viz::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); }
- TextureFrameBufferPair* texture_and_framebuffer() {
- return &texture_and_framebuffer_;
- }
- GLuint texture() const { return texture_and_framebuffer_.texture(); }
-
- private:
- TextureFrameBufferPair texture_and_framebuffer_;
- std::unique_ptr<viz::GLHelper::ScalerInterface> scaler_;
-
- DISALLOW_COPY_AND_ASSIGN(ScalerHolder);
-};
-
-} // namespace
-
-namespace viz {
-typedef GLHelperReadbackSupport::FormatSupport FormatSupport;
-
-// Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates
-// the data needed for it.
-class GLHelper::CopyTextureToImpl
- : public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> {
- public:
- CopyTextureToImpl(GLES2Interface* gl,
- gpu::ContextSupport* context_support,
- GLHelper* helper)
- : gl_(gl),
- context_support_(context_support),
- helper_(helper),
- flush_(gl),
- max_draw_buffers_(0) {
- const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS);
- if (!extensions)
- return;
- std::string extensions_string =
- " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
- if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) {
- gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
- }
- }
- ~CopyTextureToImpl() { CancelRequests(); }
-
- GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token) {
- return helper_->ConsumeMailboxToTexture(mailbox, sync_token);
- }
-
- void CropScaleReadbackAndCleanTexture(
- GLuint src_texture,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- unsigned char* out,
- const SkColorType out_color_type,
- const base::Callback<void(bool)>& callback,
- GLHelper::ScalerQuality quality);
-
- void ReadbackTextureSync(GLuint texture,
- const gfx::Rect& src_rect,
- unsigned char* out,
- SkColorType format);
-
- void ReadbackTextureAsync(GLuint texture,
- const gfx::Size& dst_size,
- unsigned char* out,
- SkColorType color_type,
- const base::Callback<void(bool)>& callback);
-
- // Reads back bytes from the currently bound frame buffer.
- // Note that dst_size is specified in bytes, not pixels.
- void ReadbackAsync(
- const gfx::Size& dst_size,
- int32_t bytes_per_row, // generally dst_size.width() * 4
- int32_t row_stride_bytes, // generally dst_size.width() * 4
- unsigned char* out,
- GLenum format,
- GLenum type,
- size_t bytes_per_pixel,
- const base::Callback<void(bool)>& callback);
-
- void ReadbackPlane(TextureFrameBufferPair* source,
- int row_stride_bytes,
- unsigned char* data,
- int size_shift,
- const gfx::Rect& paste_rect,
- ReadbackSwizzle swizzle,
- const base::Callback<void(bool)>& callback);
-
- GLuint CopyAndScaleTexture(GLuint texture,
- const gfx::Size& src_size,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- GLHelper::ScalerQuality quality);
-
- ReadbackYUVInterface* CreateReadbackPipelineYUV(
- GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- bool use_mrt);
-
- // Returns the maximum number of draw buffers available,
- // 0 if GL_EXT_draw_buffers is not available.
- GLint MaxDrawBuffers() const { return max_draw_buffers_; }
-
- FormatSupport GetReadbackConfig(SkColorType color_type,
- bool can_swizzle,
- GLenum* format,
- GLenum* type,
- size_t* bytes_per_pixel);
-
- private:
- // A single request to CropScaleReadbackAndCleanTexture.
- // The main thread can cancel the request, before it's handled by the helper
- // thread, by resetting the texture and pixels fields. Alternatively, the
- // thread marks that it handles the request by resetting the pixels field
- // (meaning it guarantees that the callback with be called).
- // In either case, the callback must be called exactly once, and the texture
- // must be deleted by the main thread gl.
- struct Request {
- Request(const gfx::Size& size_,
- int32_t bytes_per_row_,
- int32_t row_stride_bytes_,
- unsigned char* pixels_,
- const base::Callback<void(bool)>& callback_)
- : done(false),
- size(size_),
- bytes_per_row(bytes_per_row_),
- row_stride_bytes(row_stride_bytes_),
- pixels(pixels_),
- callback(callback_),
- buffer(0),
- query(0) {}
-
- bool done;
- bool result;
- gfx::Size size;
- int bytes_per_row;
- int row_stride_bytes;
- unsigned char* pixels;
- base::Callback<void(bool)> callback;
- GLuint buffer;
- GLuint query;
- };
-
- // We must take care to call the callbacks last, as they may
- // end up destroying the gl_helper and make *this invalid.
- // We stick the finished requests in a stack object that calls
- // the callbacks when it goes out of scope.
- class FinishRequestHelper {
- public:
- FinishRequestHelper() {}
- ~FinishRequestHelper() {
- while (!requests_.empty()) {
- Request* request = requests_.front();
- requests_.pop();
- request->callback.Run(request->result);
- delete request;
- }
- }
- void Add(Request* r) { requests_.push(r); }
-
- private:
- std::queue<Request*> requests_;
- DISALLOW_COPY_AND_ASSIGN(FinishRequestHelper);
- };
-
- // A readback pipeline that also converts the data to YUV before
- // reading it back.
- class ReadbackYUVImpl : public ReadbackYUVInterface {
- public:
- ReadbackYUVImpl(GLES2Interface* gl,
- CopyTextureToImpl* copy_impl,
- GLHelperScaling* scaler_impl,
- GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- ReadbackSwizzle swizzle);
-
- void ReadbackYUV(const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Rect& target_visible_rect,
- int y_plane_row_stride_bytes,
- unsigned char* y_plane_data,
- int u_plane_row_stride_bytes,
- unsigned char* u_plane_data,
- int v_plane_row_stride_bytes,
- unsigned char* v_plane_data,
- const gfx::Point& paste_location,
- const base::Callback<void(bool)>& callback) override;
-
- ScalerInterface* scaler() override { return scaler_.scaler(); }
-
- private:
- GLES2Interface* gl_;
- CopyTextureToImpl* copy_impl_;
- gfx::Size dst_size_;
- ReadbackSwizzle swizzle_;
- ScalerHolder scaler_;
- ScalerHolder y_;
- ScalerHolder u_;
- ScalerHolder v_;
-
- DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl);
- };
-
- // A readback pipeline that also converts the data to YUV before
- // reading it back. This one uses Multiple Render Targets, which
- // may not be supported on all platforms.
- class ReadbackYUV_MRT : public ReadbackYUVInterface {
- public:
- ReadbackYUV_MRT(GLES2Interface* gl,
- CopyTextureToImpl* copy_impl,
- GLHelperScaling* scaler_impl,
- GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- ReadbackSwizzle swizzle);
-
- void ReadbackYUV(const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Rect& target_visible_rect,
- int y_plane_row_stride_bytes,
- unsigned char* y_plane_data,
- int u_plane_row_stride_bytes,
- unsigned char* u_plane_data,
- int v_plane_row_stride_bytes,
- unsigned char* v_plane_data,
- const gfx::Point& paste_location,
- const base::Callback<void(bool)>& callback) override;
-
- ScalerInterface* scaler() override { return scaler_.scaler(); }
-
- private:
- GLES2Interface* gl_;
- CopyTextureToImpl* copy_impl_;
- gfx::Size dst_size_;
- GLHelper::ScalerQuality quality_;
- ReadbackSwizzle swizzle_;
- ScalerHolder scaler_;
- std::unique_ptr<GLHelperScaling::ShaderInterface> pass1_shader_;
- std::unique_ptr<GLHelperScaling::ShaderInterface> pass2_shader_;
- TextureFrameBufferPair y_;
- ScopedTexture uv_;
- TextureFrameBufferPair u_;
- TextureFrameBufferPair v_;
-
- DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT);
- };
-
- // Copies the block of pixels specified with |src_subrect| from |src_texture|,
- // scales it to |dst_size|, writes it into a texture, and returns its ID.
- // |src_size| is the size of |src_texture|.
- GLuint ScaleTexture(GLuint src_texture,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- SkColorType color_type,
- GLHelper::ScalerQuality quality);
-
- // Converts each four consecutive pixels of the source texture into one pixel
- // in the result texture with each pixel channel representing the grayscale
- // color of one of the four original pixels:
- // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X1X2X3X4
- // The resulting texture is still an RGBA texture (which is ~4 times narrower
- // than the original). If rendered directly, it wouldn't show anything useful,
- // but the data in it can be used to construct a grayscale image.
- // |encoded_texture_size| is the exact size of the resulting RGBA texture. It
- // is equal to src_size.width()/4 rounded upwards. Some channels in the last
- // pixel ((-src_size.width()) % 4) to be exact) are padding and don't contain
- // useful data.
- // If swizzle is set to true, the transformed pixels are reordered:
- // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X3X2X1X4.
- GLuint EncodeTextureAsGrayscale(GLuint src_texture,
- const gfx::Size& src_size,
- gfx::Size* const encoded_texture_size,
- bool vertically_flip_texture,
- bool swizzle);
-
- static void nullcallback(bool success) {}
- void ReadbackDone(Request* request, int bytes_per_pixel);
- void FinishRequest(Request* request,
- bool result,
- FinishRequestHelper* helper);
- void CancelRequests();
-
- static const float kRGBtoYColorWeights[];
- static const float kRGBtoUColorWeights[];
- static const float kRGBtoVColorWeights[];
- static const float kRGBtoGrayscaleColorWeights[];
-
- GLES2Interface* gl_;
- gpu::ContextSupport* context_support_;
- GLHelper* helper_;
-
- // A scoped flush that will ensure all resource deletions are flushed when
- // this object is destroyed. Must be declared before other Scoped* fields.
- ScopedFlush flush_;
-
- std::queue<Request*> request_queue_;
- GLint max_draw_buffers_;
-};
-
-GLHelper::ScalerInterface* GLHelper::CreateScaler(ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle) {
- InitScalerImpl();
- return scaler_impl_->CreateScaler(quality, src_size, src_subrect, dst_size,
- vertically_flip_texture, swizzle);
-}
-
-GLuint GLHelper::CopyTextureToImpl::ScaleTexture(
- GLuint src_texture,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- SkColorType color_type,
- GLHelper::ScalerQuality quality) {
- GLuint dst_texture = 0u;
- gl_->GenTextures(1, &dst_texture);
- {
- GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE;
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture);
-
- // Use GL_RGBA for destination/temporary texture unless we're working with
- // 16-bit data
- if (color_type == kRGB_565_SkColorType) {
- format = GL_RGB;
- type = GL_UNSIGNED_SHORT_5_6_5;
- }
-
- gl_->TexImage2D(GL_TEXTURE_2D, 0, format, dst_size.width(),
- dst_size.height(), 0, format, type, NULL);
- }
- std::unique_ptr<ScalerInterface> scaler(
- helper_->CreateScaler(quality, src_size, src_subrect, dst_size,
- vertically_flip_texture, swizzle));
- scaler->Scale(src_texture, dst_texture);
- return dst_texture;
-}
-
-GLuint GLHelper::CopyTextureToImpl::EncodeTextureAsGrayscale(
- GLuint src_texture,
- const gfx::Size& src_size,
- gfx::Size* const encoded_texture_size,
- bool vertically_flip_texture,
- bool swizzle) {
- GLuint dst_texture = 0u;
- gl_->GenTextures(1, &dst_texture);
- // The size of the encoded texture.
- *encoded_texture_size =
- gfx::Size((src_size.width() + 3) / 4, src_size.height());
- {
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, encoded_texture_size->width(),
- encoded_texture_size->height(), 0, GL_RGBA,
- GL_UNSIGNED_BYTE, NULL);
- }
-
- helper_->InitScalerImpl();
- std::unique_ptr<ScalerInterface> grayscale_scaler(
- helper_->scaler_impl_.get()->CreatePlanarScaler(
- src_size,
- gfx::Rect(0, 0, (src_size.width() + 3) & ~3, src_size.height()),
- *encoded_texture_size, vertically_flip_texture, swizzle,
- kRGBtoGrayscaleColorWeights));
- grayscale_scaler->Scale(src_texture, dst_texture);
- return dst_texture;
-}
-
-void GLHelper::CopyTextureToImpl::ReadbackAsync(
- const gfx::Size& dst_size,
- int32_t bytes_per_row,
- int32_t row_stride_bytes,
- unsigned char* out,
- GLenum format,
- GLenum type,
- size_t bytes_per_pixel,
- const base::Callback<void(bool)>& callback) {
- TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::ReadbackAsync");
- Request* request =
- new Request(dst_size, bytes_per_row, row_stride_bytes, out, callback);
- request_queue_.push(request);
- request->buffer = 0u;
-
- gl_->GenBuffers(1, &request->buffer);
- gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
- gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
- bytes_per_pixel * dst_size.GetArea(), NULL, GL_STREAM_READ);
-
- request->query = 0u;
- gl_->GenQueriesEXT(1, &request->query);
- gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query);
- gl_->ReadPixels(0, 0, dst_size.width(), dst_size.height(), format, type,
- NULL);
- gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
- gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- context_support_->SignalQuery(
- request->query, base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(),
- request, bytes_per_pixel));
-}
-
-void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture(
- GLuint src_texture,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- unsigned char* out,
- const SkColorType out_color_type,
- const base::Callback<void(bool)>& callback,
- GLHelper::ScalerQuality quality) {
- GLenum format, type;
- size_t bytes_per_pixel;
- SkColorType readback_color_type = out_color_type;
- // Single-component textures are not supported by all GPUs, so we implement
- // kAlpha_8_SkColorType support here via a special encoding (see below) using
- // a 32-bit texture to represent an 8-bit image.
- // Thus we use generic 32-bit readback in this case.
- if (out_color_type == kAlpha_8_SkColorType) {
- readback_color_type = kRGBA_8888_SkColorType;
- }
-
- FormatSupport supported = GetReadbackConfig(readback_color_type, true,
- &format, &type, &bytes_per_pixel);
-
- if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) {
- callback.Run(false);
- return;
- }
-
- GLuint texture = src_texture;
-
- // Scale texture if needed
- // Optimization: SCALER_QUALITY_FAST is just a single bilinear pass, which we
- // can do just as well in EncodeTextureAsGrayscale, which we will do if
- // out_color_type is kAlpha_8_SkColorType, so let's skip the scaling step
- // in that case.
- bool scale_texture = out_color_type != kAlpha_8_SkColorType ||
- quality != GLHelper::SCALER_QUALITY_FAST;
- if (scale_texture) {
- // Don't swizzle during the scale step for kAlpha_8_SkColorType.
- // We will swizzle in the encode step below if needed.
- bool scale_swizzle = out_color_type == kAlpha_8_SkColorType
- ? false
- : supported == GLHelperReadbackSupport::SWIZZLE;
- texture = ScaleTexture(
- src_texture, src_size, src_subrect, dst_size, true, scale_swizzle,
- out_color_type == kAlpha_8_SkColorType ? kN32_SkColorType
- : out_color_type,
- quality);
- DCHECK(texture);
- }
-
- gfx::Size readback_texture_size = dst_size;
- // Encode texture to grayscale if needed.
- if (out_color_type == kAlpha_8_SkColorType) {
- // Do the vertical flip here if we haven't already done it when we scaled
- // the texture.
- bool encode_as_grayscale_vertical_flip = !scale_texture;
- // EncodeTextureAsGrayscale by default creates a texture which should be
- // read back as RGBA, so need to swizzle if the readback format is BGRA.
- bool encode_as_grayscale_swizzle = format == GL_BGRA_EXT;
- GLuint tmp_texture = EncodeTextureAsGrayscale(
- texture, dst_size, &readback_texture_size,
- encode_as_grayscale_vertical_flip, encode_as_grayscale_swizzle);
- // If the scaled texture was created - delete it
- if (scale_texture)
- gl_->DeleteTextures(1, &texture);
- texture = tmp_texture;
- DCHECK(texture);
- }
-
- // Readback the pixels of the resulting texture
- ScopedFramebuffer dst_framebuffer(gl_);
- ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
- dst_framebuffer);
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- texture, 0);
-
- int32_t bytes_per_row = out_color_type == kAlpha_8_SkColorType
- ? dst_size.width()
- : dst_size.width() * bytes_per_pixel;
-
- ReadbackAsync(readback_texture_size, bytes_per_row, bytes_per_row, out,
- format, type, bytes_per_pixel, callback);
- gl_->DeleteTextures(1, &texture);
-}
-
-void GLHelper::CopyTextureToImpl::ReadbackTextureSync(GLuint texture,
- const gfx::Rect& src_rect,
- unsigned char* out,
- SkColorType color_type) {
- GLenum format, type;
- size_t bytes_per_pixel;
- FormatSupport supported =
- GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel);
- if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) {
- return;
- }
-
- ScopedFramebuffer dst_framebuffer(gl_);
- ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
- dst_framebuffer);
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- texture, 0);
- gl_->ReadPixels(src_rect.x(), src_rect.y(), src_rect.width(),
- src_rect.height(), format, type, out);
-}
-
-void GLHelper::CopyTextureToImpl::ReadbackTextureAsync(
- GLuint texture,
- const gfx::Size& dst_size,
- unsigned char* out,
- SkColorType color_type,
- const base::Callback<void(bool)>& callback) {
- GLenum format, type;
- size_t bytes_per_pixel;
- FormatSupport supported =
- GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel);
- if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) {
- callback.Run(false);
- return;
- }
-
- ScopedFramebuffer dst_framebuffer(gl_);
- ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
- dst_framebuffer);
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- texture, 0);
- ReadbackAsync(dst_size, dst_size.width() * bytes_per_pixel,
- dst_size.width() * bytes_per_pixel, out, format, type,
- bytes_per_pixel, callback);
-}
-
-GLuint GLHelper::CopyTextureToImpl::CopyAndScaleTexture(
- GLuint src_texture,
- const gfx::Size& src_size,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- GLHelper::ScalerQuality quality) {
- return ScaleTexture(src_texture, src_size, gfx::Rect(src_size), dst_size,
- vertically_flip_texture, false,
- kRGBA_8888_SkColorType, // GL_RGBA
- quality);
-}
-
-void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request,
- int bytes_per_pixel) {
- TRACE_EVENT0("gpu.capture",
- "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete");
- finished_request->done = true;
-
- FinishRequestHelper finish_request_helper;
-
- // We process transfer requests in the order they were received, regardless
- // of the order we get the callbacks in.
- while (!request_queue_.empty()) {
- Request* request = request_queue_.front();
- if (!request->done) {
- break;
- }
-
- bool result = false;
- if (request->buffer != 0) {
- gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
- unsigned char* data = static_cast<unsigned char*>(gl_->MapBufferCHROMIUM(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
- if (data) {
- result = true;
- if (request->bytes_per_row == request->size.width() * bytes_per_pixel &&
- request->bytes_per_row == request->row_stride_bytes) {
- memcpy(request->pixels, data,
- request->size.GetArea() * bytes_per_pixel);
- } else {
- unsigned char* out = request->pixels;
- for (int y = 0; y < request->size.height(); y++) {
- memcpy(out, data, request->bytes_per_row);
- out += request->row_stride_bytes;
- data += request->size.width() * bytes_per_pixel;
- }
- }
- gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
- }
- gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- }
- FinishRequest(request, result, &finish_request_helper);
- }
-}
-
-void GLHelper::CopyTextureToImpl::FinishRequest(
- Request* request,
- bool result,
- FinishRequestHelper* finish_request_helper) {
- TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::FinishRequest");
- DCHECK(request_queue_.front() == request);
- request_queue_.pop();
- request->result = result;
- ScopedFlush flush(gl_);
- if (request->query != 0) {
- gl_->DeleteQueriesEXT(1, &request->query);
- request->query = 0;
- }
- if (request->buffer != 0) {
- gl_->DeleteBuffers(1, &request->buffer);
- request->buffer = 0;
- }
- finish_request_helper->Add(request);
-}
-
-void GLHelper::CopyTextureToImpl::CancelRequests() {
- FinishRequestHelper finish_request_helper;
- while (!request_queue_.empty()) {
- Request* request = request_queue_.front();
- FinishRequest(request, false, &finish_request_helper);
- }
-}
-
-FormatSupport GLHelper::CopyTextureToImpl::GetReadbackConfig(
- SkColorType color_type,
- bool can_swizzle,
- GLenum* format,
- GLenum* type,
- size_t* bytes_per_pixel) {
- return helper_->readback_support_->GetReadbackConfig(
- color_type, can_swizzle, format, type, bytes_per_pixel);
-}
-
-GLHelper::GLHelper(GLES2Interface* gl, gpu::ContextSupport* context_support)
- : gl_(gl),
- context_support_(context_support),
- readback_support_(new GLHelperReadbackSupport(gl)) {}
-
-GLHelper::~GLHelper() {}
-
-void GLHelper::CropScaleReadbackAndCleanTexture(
- GLuint src_texture,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- unsigned char* out,
- const SkColorType out_color_type,
- const base::Callback<void(bool)>& callback,
- GLHelper::ScalerQuality quality) {
- InitCopyTextToImpl();
- copy_texture_to_impl_->CropScaleReadbackAndCleanTexture(
- src_texture, src_size, src_subrect, dst_size, out, out_color_type,
- callback, quality);
-}
-
-void GLHelper::CropScaleReadbackAndCleanMailbox(
- const gpu::Mailbox& src_mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- unsigned char* out,
- const SkColorType out_color_type,
- const base::Callback<void(bool)>& callback,
- GLHelper::ScalerQuality quality) {
- GLuint mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_token);
- CropScaleReadbackAndCleanTexture(mailbox_texture, src_size, src_subrect,
- dst_size, out, out_color_type, callback,
- quality);
- gl_->DeleteTextures(1, &mailbox_texture);
-}
-
-void GLHelper::ReadbackTextureSync(GLuint texture,
- const gfx::Rect& src_rect,
- unsigned char* out,
- SkColorType format) {
- InitCopyTextToImpl();
- copy_texture_to_impl_->ReadbackTextureSync(texture, src_rect, out, format);
-}
-
-void GLHelper::ReadbackTextureAsync(
- GLuint texture,
- const gfx::Size& dst_size,
- unsigned char* out,
- SkColorType color_type,
- const base::Callback<void(bool)>& callback) {
- InitCopyTextToImpl();
- copy_texture_to_impl_->ReadbackTextureAsync(texture, dst_size, out,
- color_type, callback);
-}
-
-GLuint GLHelper::CopyTexture(GLuint texture, const gfx::Size& size) {
- InitCopyTextToImpl();
- return copy_texture_to_impl_->CopyAndScaleTexture(
- texture, size, size, false, GLHelper::SCALER_QUALITY_FAST);
-}
-
-GLuint GLHelper::CopyAndScaleTexture(GLuint texture,
- const gfx::Size& src_size,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- ScalerQuality quality) {
- InitCopyTextToImpl();
- return copy_texture_to_impl_->CopyAndScaleTexture(
- texture, src_size, dst_size, vertically_flip_texture, quality);
-}
-
-GLuint GLHelper::CompileShaderFromSource(const GLchar* source, GLenum type) {
- GLuint shader = gl_->CreateShader(type);
- GLint length = strlen(source);
- gl_->ShaderSource(shader, 1, &source, &length);
- gl_->CompileShader(shader);
- GLint compile_status = 0;
- gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
- if (!compile_status) {
- GLint log_length = 0;
- gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
- if (log_length) {
- std::unique_ptr<GLchar[]> log(new GLchar[log_length]);
- GLsizei returned_log_length = 0;
- gl_->GetShaderInfoLog(shader, log_length, &returned_log_length,
- log.get());
- LOG(ERROR) << std::string(log.get(), returned_log_length);
- }
- gl_->DeleteShader(shader);
- return 0;
- }
- return shader;
-}
-
-void GLHelper::InitCopyTextToImpl() {
- // Lazily initialize |copy_texture_to_impl_|
- if (!copy_texture_to_impl_)
- copy_texture_to_impl_.reset(
- new CopyTextureToImpl(gl_, context_support_, this));
-}
-
-void GLHelper::InitScalerImpl() {
- // Lazily initialize |scaler_impl_|
- if (!scaler_impl_)
- scaler_impl_.reset(new GLHelperScaling(gl_, this));
-}
-
-GLint GLHelper::MaxDrawBuffers() {
- InitCopyTextToImpl();
- return copy_texture_to_impl_->MaxDrawBuffers();
-}
-
-void GLHelper::CopySubBufferDamage(GLenum target,
- GLuint texture,
- GLuint previous_texture,
- const SkRegion& new_damage,
- const SkRegion& old_damage) {
- SkRegion region(old_damage);
- if (region.op(new_damage, SkRegion::kDifference_Op)) {
- ScopedFramebuffer dst_framebuffer(gl_);
- ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
- dst_framebuffer);
- gl_->BindTexture(target, texture);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target,
- previous_texture, 0);
- for (SkRegion::Iterator it(region); !it.done(); it.next()) {
- const SkIRect& rect = it.rect();
- gl_->CopyTexSubImage2D(target, 0, rect.x(), rect.y(), rect.x(), rect.y(),
- rect.width(), rect.height());
- }
- gl_->BindTexture(target, 0);
- gl_->Flush();
- }
-}
-
-GLuint GLHelper::CreateTexture() {
- GLuint texture = 0u;
- gl_->GenTextures(1, &texture);
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- return texture;
-}
-
-void GLHelper::DeleteTexture(GLuint texture_id) {
- gl_->DeleteTextures(1, &texture_id);
-}
-
-void GLHelper::GenerateSyncToken(gpu::SyncToken* sync_token) {
- const uint64_t fence_sync = gl_->InsertFenceSyncCHROMIUM();
- gl_->ShallowFlushCHROMIUM();
- gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token->GetData());
-}
-
-void GLHelper::WaitSyncToken(const gpu::SyncToken& sync_token) {
- gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
-}
-
-gpu::MailboxHolder GLHelper::ProduceMailboxHolderFromTexture(
- GLuint texture_id) {
- gpu::Mailbox mailbox;
- gl_->GenMailboxCHROMIUM(mailbox.name);
- gl_->ProduceTextureDirectCHROMIUM(texture_id, GL_TEXTURE_2D, mailbox.name);
-
- gpu::SyncToken sync_token;
- GenerateSyncToken(&sync_token);
-
- return gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D);
-}
-
-GLuint GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token) {
- if (mailbox.IsZero())
- return 0;
- if (sync_token.HasData())
- WaitSyncToken(sync_token);
- GLuint texture =
- gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
- return texture;
-}
-
-void GLHelper::ResizeTexture(GLuint texture, const gfx::Size& size) {
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0,
- GL_RGB, GL_UNSIGNED_BYTE, NULL);
-}
-
-void GLHelper::CopyTextureSubImage(GLuint texture, const gfx::Rect& rect) {
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
- gl_->CopyTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.x(),
- rect.y(), rect.width(), rect.height());
-}
-
-void GLHelper::CopyTextureFullImage(GLuint texture, const gfx::Size& size) {
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
- gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size.width(),
- size.height(), 0);
-}
-
-void GLHelper::Flush() {
- gl_->Flush();
-}
-
-void GLHelper::InsertOrderingBarrier() {
- gl_->OrderingBarrierCHROMIUM();
-}
-
-void GLHelper::CopyTextureToImpl::ReadbackPlane(
- TextureFrameBufferPair* source,
- int row_stride_bytes,
- unsigned char* data,
- int size_shift,
- const gfx::Rect& paste_rect,
- ReadbackSwizzle swizzle,
- const base::Callback<void(bool)>& callback) {
- gl_->BindFramebuffer(GL_FRAMEBUFFER, source->framebuffer());
- const size_t offset = row_stride_bytes * (paste_rect.y() >> size_shift) +
- (paste_rect.x() >> size_shift);
- ReadbackAsync(source->size(), paste_rect.width() >> size_shift,
- row_stride_bytes, data + offset,
- (swizzle == kSwizzleBGRA) ? GL_BGRA_EXT : GL_RGBA,
- GL_UNSIGNED_BYTE, 4, callback);
-}
-
-const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = {
- 0.257f, 0.504f, 0.098f, 0.0625f};
-const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = {
- -0.148f, -0.291f, 0.439f, 0.5f};
-const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = {
- 0.439f, -0.368f, -0.071f, 0.5f};
-const float GLHelper::CopyTextureToImpl::kRGBtoGrayscaleColorWeights[] = {
- 0.213f, 0.715f, 0.072f, 0.0f};
-
-// YUV readback constructors. Initiates the main scaler pipeline and
-// one planar scaler for each of the Y, U and V planes.
-GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl(
- GLES2Interface* gl,
- CopyTextureToImpl* copy_impl,
- GLHelperScaling* scaler_impl,
- GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- ReadbackSwizzle swizzle)
- : gl_(gl),
- copy_impl_(copy_impl),
- dst_size_(dst_size),
- swizzle_(swizzle),
- scaler_(gl,
- scaler_impl->CreateScaler(quality,
- src_size,
- src_subrect,
- dst_size,
- flip_vertically,
- false)),
- y_(gl,
- scaler_impl->CreatePlanarScaler(
- dst_size,
- gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()),
- gfx::Size((dst_size.width() + 3) / 4, dst_size.height()),
- false,
- (swizzle == kSwizzleBGRA),
- kRGBtoYColorWeights)),
- u_(gl,
- scaler_impl->CreatePlanarScaler(
- dst_size,
- gfx::Rect(0,
- 0,
- (dst_size.width() + 7) & ~7,
- (dst_size.height() + 1) & ~1),
- gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2),
- false,
- (swizzle == kSwizzleBGRA),
- kRGBtoUColorWeights)),
- v_(gl,
- scaler_impl->CreatePlanarScaler(
- dst_size,
- gfx::Rect(0,
- 0,
- (dst_size.width() + 7) & ~7,
- (dst_size.height() + 1) & ~1),
- gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2),
- false,
- (swizzle == kSwizzleBGRA),
- kRGBtoVColorWeights)) {
- DCHECK(!(dst_size.width() & 1));
- DCHECK(!(dst_size.height() & 1));
-}
-
-void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV(
- const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Rect& target_visible_rect,
- int y_plane_row_stride_bytes,
- unsigned char* y_plane_data,
- int u_plane_row_stride_bytes,
- unsigned char* u_plane_data,
- int v_plane_row_stride_bytes,
- unsigned char* v_plane_data,
- const gfx::Point& paste_location,
- const base::Callback<void(bool)>& callback) {
- DCHECK(!(paste_location.x() & 1));
- DCHECK(!(paste_location.y() & 1));
-
- GLuint mailbox_texture =
- copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token);
-
- // Scale texture to right size.
- scaler_.Scale(mailbox_texture);
- gl_->DeleteTextures(1, &mailbox_texture);
-
- // Convert the scaled texture in to Y, U and V planes.
- y_.Scale(scaler_.texture());
- u_.Scale(scaler_.texture());
- v_.Scale(scaler_.texture());
-
- const gfx::Rect paste_rect(paste_location, dst_size_);
- if (!target_visible_rect.Contains(paste_rect)) {
- LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!";
- callback.Run(false);
- return;
- }
-
- // Read back planes, one at a time. Keep the video frame alive while doing the
- // readback.
- copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(),
- y_plane_row_stride_bytes, y_plane_data, 0,
- paste_rect, swizzle_, base::Bind(&nullcallback));
- copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(),
- u_plane_row_stride_bytes, u_plane_data, 1,
- paste_rect, swizzle_, base::Bind(&nullcallback));
- copy_impl_->ReadbackPlane(v_.texture_and_framebuffer(),
- v_plane_row_stride_bytes, v_plane_data, 1,
- paste_rect, swizzle_, callback);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-// YUV readback constructors. Initiates the main scaler pipeline and
-// one planar scaler for each of the Y, U and V planes.
-GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT(
- GLES2Interface* gl,
- CopyTextureToImpl* copy_impl,
- GLHelperScaling* scaler_impl,
- GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- ReadbackSwizzle swizzle)
- : gl_(gl),
- copy_impl_(copy_impl),
- dst_size_(dst_size),
- quality_(quality),
- swizzle_(swizzle),
- scaler_(gl,
- scaler_impl->CreateScaler(quality,
- src_size,
- src_subrect,
- dst_size,
- false,
- false)),
- pass1_shader_(scaler_impl->CreateYuvMrtShader(
- dst_size,
- gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()),
- gfx::Size((dst_size.width() + 3) / 4, dst_size.height()),
- flip_vertically,
- (swizzle == kSwizzleBGRA),
- GLHelperScaling::SHADER_YUV_MRT_PASS1)),
- pass2_shader_(scaler_impl->CreateYuvMrtShader(
- gfx::Size((dst_size.width() + 3) / 4, dst_size.height()),
- gfx::Rect(0, 0, (dst_size.width() + 7) / 8 * 2, dst_size.height()),
- gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2),
- false,
- (swizzle == kSwizzleBGRA),
- GLHelperScaling::SHADER_YUV_MRT_PASS2)),
- y_(gl, gfx::Size((dst_size.width() + 3) / 4, dst_size.height())),
- uv_(gl),
- u_(gl,
- gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)),
- v_(gl,
- gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)) {
- DCHECK(!(dst_size.width() & 1));
- DCHECK(!(dst_size.height() & 1));
-
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, uv_);
- gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (dst_size.width() + 3) / 4,
- dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-}
-
-void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV(
- const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Rect& target_visible_rect,
- int y_plane_row_stride_bytes,
- unsigned char* y_plane_data,
- int u_plane_row_stride_bytes,
- unsigned char* u_plane_data,
- int v_plane_row_stride_bytes,
- unsigned char* v_plane_data,
- const gfx::Point& paste_location,
- const base::Callback<void(bool)>& callback) {
- DCHECK(!(paste_location.x() & 1));
- DCHECK(!(paste_location.y() & 1));
-
- GLuint mailbox_texture =
- copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token);
-
- GLuint texture;
- if (quality_ == GLHelper::SCALER_QUALITY_FAST) {
- // Optimization: SCALER_QUALITY_FAST is just a single bilinear
- // pass, which pass1_shader_ can do just as well, so let's skip
- // the actual scaling in that case.
- texture = mailbox_texture;
- } else {
- // Scale texture to right size.
- scaler_.Scale(mailbox_texture);
- texture = scaler_.texture();
- }
-
- std::vector<GLuint> outputs(2);
- // Convert the scaled texture in to Y, U and V planes.
- outputs[0] = y_.texture();
- outputs[1] = uv_;
- pass1_shader_->Execute(texture, outputs);
-
- gl_->DeleteTextures(1, &mailbox_texture);
-
- outputs[0] = u_.texture();
- outputs[1] = v_.texture();
- pass2_shader_->Execute(uv_, outputs);
-
- const gfx::Rect paste_rect(paste_location, dst_size_);
- if (!target_visible_rect.Contains(paste_rect)) {
- LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!";
- callback.Run(false);
- return;
- }
-
- // Read back planes, one at a time.
- copy_impl_->ReadbackPlane(&y_, y_plane_row_stride_bytes, y_plane_data, 0,
- paste_rect, swizzle_, base::Bind(&nullcallback));
- copy_impl_->ReadbackPlane(&u_, u_plane_row_stride_bytes, u_plane_data, 1,
- paste_rect, swizzle_, base::Bind(&nullcallback));
- copy_impl_->ReadbackPlane(&v_, v_plane_row_stride_bytes, v_plane_data, 1,
- paste_rect, swizzle_, callback);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-bool GLHelper::IsReadbackConfigSupported(SkColorType color_type) {
- DCHECK(readback_support_.get());
- GLenum format, type;
- size_t bytes_per_pixel;
- FormatSupport support = readback_support_->GetReadbackConfig(
- color_type, false, &format, &type, &bytes_per_pixel);
-
- return (support == GLHelperReadbackSupport::SUPPORTED);
-}
-
-ReadbackYUVInterface* GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV(
- GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- bool use_mrt) {
- helper_->InitScalerImpl();
- // Just query if the best readback configuration needs a swizzle In
- // ReadbackPlane() we will choose GL_RGBA/GL_BGRA_EXT based on swizzle
- GLenum format, type;
- size_t bytes_per_pixel;
- FormatSupport supported = GetReadbackConfig(kRGBA_8888_SkColorType, true,
- &format, &type, &bytes_per_pixel);
- DCHECK((format == GL_RGBA || format == GL_BGRA_EXT) &&
- type == GL_UNSIGNED_BYTE);
-
- ReadbackSwizzle swizzle = kSwizzleNone;
- if (supported == GLHelperReadbackSupport::SWIZZLE)
- swizzle = kSwizzleBGRA;
-
- if (max_draw_buffers_ >= 2 && use_mrt) {
- return new ReadbackYUV_MRT(gl_, this, helper_->scaler_impl_.get(), quality,
- src_size, src_subrect, dst_size, flip_vertically,
- swizzle);
- }
- return new ReadbackYUVImpl(gl_, this, helper_->scaler_impl_.get(), quality,
- src_size, src_subrect, dst_size, flip_vertically,
- swizzle);
-}
-
-ReadbackYUVInterface* GLHelper::CreateReadbackPipelineYUV(
- ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- bool use_mrt) {
- InitCopyTextToImpl();
- return copy_texture_to_impl_->CreateReadbackPipelineYUV(
- quality, src_size, src_subrect, dst_size, flip_vertically, use_mrt);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/gl_helper.h b/chromium/components/viz/display_compositor/gl_helper.h
deleted file mode 100644
index 430c72f5e17..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper.h
+++ /dev/null
@@ -1,383 +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_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_H_
-
-#include <memory>
-
-#include "base/atomicops.h"
-#include "base/callback.h"
-#include "base/macros.h"
-#include "components/viz/viz_export.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/common/mailbox_holder.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace gfx {
-class Point;
-class Rect;
-class Size;
-}
-
-namespace gpu {
-class ContextSupport;
-struct Mailbox;
-}
-
-class SkRegion;
-
-namespace viz {
-
-class GLHelperScaling;
-
-class VIZ_EXPORT ScopedGLuint {
- public:
- typedef void (gpu::gles2::GLES2Interface::*GenFunc)(GLsizei n, GLuint* ids);
- typedef void (gpu::gles2::GLES2Interface::*DeleteFunc)(GLsizei n,
- const GLuint* ids);
- ScopedGLuint(gpu::gles2::GLES2Interface* gl,
- GenFunc gen_func,
- DeleteFunc delete_func)
- : gl_(gl), id_(0u), delete_func_(delete_func) {
- (gl_->*gen_func)(1, &id_);
- }
-
- operator GLuint() const { return id_; }
-
- GLuint id() const { return id_; }
-
- ~ScopedGLuint() {
- if (id_ != 0) {
- (gl_->*delete_func_)(1, &id_);
- }
- }
-
- private:
- gpu::gles2::GLES2Interface* gl_;
- GLuint id_;
- DeleteFunc delete_func_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedGLuint);
-};
-
-class ScopedBuffer : public ScopedGLuint {
- public:
- explicit ScopedBuffer(gpu::gles2::GLES2Interface* gl)
- : ScopedGLuint(gl,
- &gpu::gles2::GLES2Interface::GenBuffers,
- &gpu::gles2::GLES2Interface::DeleteBuffers) {}
-};
-
-class ScopedFramebuffer : public ScopedGLuint {
- public:
- explicit ScopedFramebuffer(gpu::gles2::GLES2Interface* gl)
- : ScopedGLuint(gl,
- &gpu::gles2::GLES2Interface::GenFramebuffers,
- &gpu::gles2::GLES2Interface::DeleteFramebuffers) {}
-};
-
-class ScopedTexture : public ScopedGLuint {
- public:
- explicit ScopedTexture(gpu::gles2::GLES2Interface* gl)
- : ScopedGLuint(gl,
- &gpu::gles2::GLES2Interface::GenTextures,
- &gpu::gles2::GLES2Interface::DeleteTextures) {}
-};
-
-template <GLenum Target>
-class ScopedBinder {
- public:
- typedef void (gpu::gles2::GLES2Interface::*BindFunc)(GLenum target,
- GLuint id);
- ScopedBinder(gpu::gles2::GLES2Interface* gl, GLuint id, BindFunc bind_func)
- : gl_(gl), bind_func_(bind_func) {
- (gl_->*bind_func_)(Target, id);
- }
-
- virtual ~ScopedBinder() { (gl_->*bind_func_)(Target, 0); }
-
- private:
- gpu::gles2::GLES2Interface* gl_;
- BindFunc bind_func_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedBinder);
-};
-
-template <GLenum Target>
-class ScopedBufferBinder : ScopedBinder<Target> {
- public:
- ScopedBufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id)
- : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindBuffer) {}
-};
-
-template <GLenum Target>
-class ScopedFramebufferBinder : ScopedBinder<Target> {
- public:
- ScopedFramebufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id)
- : ScopedBinder<Target>(gl,
- id,
- &gpu::gles2::GLES2Interface::BindFramebuffer) {}
-};
-
-template <GLenum Target>
-class ScopedTextureBinder : ScopedBinder<Target> {
- public:
- ScopedTextureBinder(gpu::gles2::GLES2Interface* gl, GLuint id)
- : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindTexture) {
- }
-};
-
-class ReadbackYUVInterface;
-class GLHelperReadbackSupport;
-
-// Provides higher level operations on top of the gpu::gles2::GLES2Interface
-// interfaces.
-class VIZ_EXPORT GLHelper {
- public:
- GLHelper(gpu::gles2::GLES2Interface* gl,
- gpu::ContextSupport* context_support);
- ~GLHelper();
-
- enum ScalerQuality {
- // Bilinear single pass, fastest possible.
- SCALER_QUALITY_FAST = 1,
-
- // Bilinear upscale + N * 50% bilinear downscales.
- // This is still fast enough for most purposes and
- // Image quality is nearly as good as the BEST option.
- SCALER_QUALITY_GOOD = 2,
-
- // Bicubic upscale + N * 50% bicubic downscales.
- // Produces very good quality scaled images, but it's
- // 2-8x slower than the "GOOD" quality, so it's not always
- // worth it.
- SCALER_QUALITY_BEST = 3,
- };
-
- // Copies the block of pixels specified with |src_subrect| from |src_texture|,
- // scales it to |dst_size|, and writes it into |out|.
- // |src_size| is the size of |src_texture|. The result is in |out_color_type|
- // format and is potentially flipped vertically to make it a correct image
- // representation. |callback| is invoked with the copy result when the copy
- // operation has completed.
- // Note that the src_texture will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- void CropScaleReadbackAndCleanTexture(
- GLuint src_texture,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- unsigned char* out,
- const SkColorType out_color_type,
- const base::Callback<void(bool)>& callback,
- GLHelper::ScalerQuality quality);
-
- // Copies the block of pixels specified with |src_subrect| from |src_mailbox|,
- // scales it to |dst_size|, and writes it into |out|.
- // |src_size| is the size of |src_mailbox|. The result is in |out_color_type|
- // format and is potentially flipped vertically to make it a correct image
- // representation. |callback| is invoked with the copy result when the copy
- // operation has completed.
- // Note that the texture bound to src_mailbox will have the min/mag filter set
- // to GL_LINEAR and wrap_s/t set to CLAMP_TO_EDGE in this call. src_mailbox is
- // assumed to be GL_TEXTURE_2D.
- void CropScaleReadbackAndCleanMailbox(
- const gpu::Mailbox& src_mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- unsigned char* out,
- const SkColorType out_color_type,
- const base::Callback<void(bool)>& callback,
- GLHelper::ScalerQuality quality);
-
- // Copies the texture data out of |texture| into |out|. |size| is the
- // size of the texture. No post processing is applied to the pixels. The
- // texture is assumed to have a format of GL_RGBA with a pixel type of
- // GL_UNSIGNED_BYTE. This is a blocking call that calls glReadPixels on the
- // current OpenGL context.
- void ReadbackTextureSync(GLuint texture,
- const gfx::Rect& src_rect,
- unsigned char* out,
- SkColorType format);
-
- void ReadbackTextureAsync(GLuint texture,
- const gfx::Size& dst_size,
- unsigned char* out,
- SkColorType color_type,
- const base::Callback<void(bool)>& callback);
-
- // Creates a copy of the specified texture. |size| is the size of the texture.
- // Note that the src_texture will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- GLuint CopyTexture(GLuint texture, const gfx::Size& size);
-
- // Creates a scaled copy of the specified texture. |src_size| is the size of
- // the texture and |dst_size| is the size of the resulting copy.
- // Note that the src_texture will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- GLuint CopyAndScaleTexture(GLuint texture,
- const gfx::Size& src_size,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- ScalerQuality quality);
-
- // Returns the shader compiled from the source.
- GLuint CompileShaderFromSource(const GLchar* source, GLenum type);
-
- // Copies all pixels from |previous_texture| into |texture| that are
- // inside the region covered by |old_damage| but not part of |new_damage|.
- void CopySubBufferDamage(GLenum target,
- GLuint texture,
- GLuint previous_texture,
- const SkRegion& new_damage,
- const SkRegion& old_damage);
-
- // Simply creates a texture.
- GLuint CreateTexture();
- // Deletes a texture.
- void DeleteTexture(GLuint texture_id);
-
- // Inserts a fence sync, flushes, and generates a sync token.
- void GenerateSyncToken(gpu::SyncToken* sync_token);
-
- // Wait for the sync token before executing further GL commands.
- void WaitSyncToken(const gpu::SyncToken& sync_token);
-
- // Creates a mailbox holder that is attached to the given texture id, with a
- // sync point to wait on before using the mailbox. Returns a holder with an
- // empty mailbox on failure.
- // Note the texture is assumed to be GL_TEXTURE_2D.
- gpu::MailboxHolder ProduceMailboxHolderFromTexture(GLuint texture_id);
-
- // Creates a texture and consumes a mailbox into it. Returns 0 on failure.
- // Note the mailbox is assumed to be GL_TEXTURE_2D.
- GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token);
-
- // Resizes the texture's size to |size|.
- void ResizeTexture(GLuint texture, const gfx::Size& size);
-
- // Copies the framebuffer data given in |rect| to |texture|.
- void CopyTextureSubImage(GLuint texture, const gfx::Rect& rect);
-
- // Copies the all framebuffer data to |texture|. |size| specifies the
- // size of the framebuffer.
- void CopyTextureFullImage(GLuint texture, const gfx::Size& size);
-
- // Flushes GL commands.
- void Flush();
-
- // Force commands in the current command buffer to be executed before commands
- // in other command buffers from the same process (ie channel to the GPU
- // process).
- void InsertOrderingBarrier();
-
- // A scaler will cache all intermediate textures and programs
- // needed to scale from a specified size to a destination size.
- // If the source or destination sizes changes, you must create
- // a new scaler.
- class VIZ_EXPORT ScalerInterface {
- public:
- ScalerInterface() {}
- virtual ~ScalerInterface() {}
-
- // Note that the src_texture will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- virtual void Scale(GLuint source_texture, GLuint dest_texture) = 0;
- virtual const gfx::Size& SrcSize() = 0;
- virtual const gfx::Rect& SrcSubrect() = 0;
- virtual const gfx::Size& DstSize() = 0;
- };
-
- // Note that the quality may be adjusted down if texture
- // allocations fail or hardware doesn't support the requtested
- // quality. Note that ScalerQuality enum is arranged in
- // numerical order for simplicity.
- ScalerInterface* CreateScaler(ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle);
-
- // Create a readback pipeline that will scale a subsection of the source
- // texture, then convert it to YUV422 planar form and then read back that.
- // This reduces the amount of memory read from GPU to CPU memory by a factor
- // 2.6, which can be quite handy since readbacks have very limited speed
- // on some platforms. All values in |dst_size| must be a multiple of two. If
- // |use_mrt| is true, the pipeline will try to optimize the YUV conversion
- // using the multi-render-target extension. |use_mrt| should only be set to
- // false for testing.
- ReadbackYUVInterface* CreateReadbackPipelineYUV(ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool flip_vertically,
- bool use_mrt);
-
- // Returns the maximum number of draw buffers available,
- // 0 if GL_EXT_draw_buffers is not available.
- GLint MaxDrawBuffers();
-
- // Checks whether the readbback is supported for texture with the
- // matching config. This doesnt check for cross format readbacks.
- bool IsReadbackConfigSupported(SkColorType texture_format);
-
- protected:
- class CopyTextureToImpl;
-
- // Creates |copy_texture_to_impl_| if NULL.
- void InitCopyTextToImpl();
- // Creates |scaler_impl_| if NULL.
- void InitScalerImpl();
-
- enum ReadbackSwizzle { kSwizzleNone = 0, kSwizzleBGRA };
-
- gpu::gles2::GLES2Interface* gl_;
- gpu::ContextSupport* context_support_;
- std::unique_ptr<CopyTextureToImpl> copy_texture_to_impl_;
- std::unique_ptr<GLHelperScaling> scaler_impl_;
- std::unique_ptr<GLHelperReadbackSupport> readback_support_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(GLHelper);
-};
-
-// Similar to a ScalerInterface, a yuv readback pipeline will
-// cache a scaler and all intermediate textures and frame buffers
-// needed to scale, crop, letterbox and read back a texture from
-// the GPU into CPU-accessible RAM. A single readback pipeline
-// can handle multiple outstanding readbacks at the same time, but
-// if the source or destination sizes change, you'll need to create
-// a new readback pipeline.
-class VIZ_EXPORT ReadbackYUVInterface {
- public:
- ReadbackYUVInterface() {}
- virtual ~ReadbackYUVInterface() {}
-
- // Note that |target| must use YV12 format. |paste_location| specifies where
- // the captured pixels that are read back will be placed in the video frame.
- // The region defined by the |paste_location| and the |dst_size| specified in
- // the call to CreateReadbackPipelineYUV() must be fully contained within
- // |target->visible_rect()|.
- virtual void ReadbackYUV(const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Rect& target_visible_rect,
- int y_plane_row_stride_bytes,
- unsigned char* y_plane_data,
- int u_plane_row_stride_bytes,
- unsigned char* u_plane_data,
- int v_plane_row_stride_bytes,
- unsigned char* v_plane_data,
- const gfx::Point& paste_location,
- const base::Callback<void(bool)>& callback) = 0;
- virtual GLHelper::ScalerInterface* scaler() = 0;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_H_
diff --git a/chromium/components/viz/display_compositor/gl_helper_benchmark.cc b/chromium/components/viz/display_compositor/gl_helper_benchmark.cc
deleted file mode 100644
index e390e035dc0..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper_benchmark.cc
+++ /dev/null
@@ -1,251 +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.
-
-// This file looks like a unit test, but it contains benchmarks and test
-// utilities intended for manual evaluation of the scalers in
-// gl_helper*. These tests produce output in the form of files and printouts,
-// but cannot really "fail". There is no point in making these tests part
-// of any test automation run.
-
-#include <stddef.h>
-#include <stdio.h>
-#include <cmath>
-#include <string>
-#include <vector>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES2/gl2extchromium.h>
-
-#include "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/viz/display_compositor/gl_helper.h"
-#include "components/viz/display_compositor/gl_helper_scaling.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "gpu/command_buffer/client/shared_memory_limits.h"
-#include "gpu/ipc/gl_in_process_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkTypes.h"
-#include "ui/gfx/codec/png_codec.h"
-#include "ui/gl/gl_surface.h"
-
-namespace viz {
-
-namespace {
-
-GLHelper::ScalerQuality kQualities[] = {
- GLHelper::SCALER_QUALITY_BEST, GLHelper::SCALER_QUALITY_GOOD,
- GLHelper::SCALER_QUALITY_FAST,
-};
-
-const char* const kQualityNames[] = {
- "best", "good", "fast",
-};
-
-} // namespace
-
-class GLHelperBenchmark : public testing::Test {
- protected:
- void SetUp() override {
- gpu::gles2::ContextCreationAttribHelper attributes;
- attributes.alpha_size = 8;
- attributes.depth_size = 24;
- attributes.red_size = 8;
- attributes.green_size = 8;
- attributes.blue_size = 8;
- attributes.stencil_size = 8;
- attributes.samples = 4;
- attributes.sample_buffers = 1;
- attributes.bind_generates_resource = false;
- attributes.gpu_preference = gl::PreferDiscreteGpu;
-
- context_.reset(
- gpu::GLInProcessContext::Create(nullptr, /* service */
- nullptr, /* surface */
- true, /* offscreen */
- gpu::kNullSurfaceHandle, /* window */
- nullptr, /* share_context */
- attributes, gpu::SharedMemoryLimits(),
- nullptr, /* gpu_memory_buffer_manager */
- nullptr, /* image_factory */
- base::ThreadTaskRunnerHandle::Get()));
- gl_ = context_->GetImplementation();
- gpu::ContextSupport* support = context_->GetImplementation();
-
- helper_.reset(new GLHelper(gl_, support));
- helper_scaling_.reset(new GLHelperScaling(gl_, helper_.get()));
- }
-
- void TearDown() override {
- helper_scaling_.reset(NULL);
- helper_.reset(NULL);
- context_.reset(NULL);
- }
-
- void LoadPngFileToSkBitmap(const base::FilePath& filename, SkBitmap* bitmap) {
- std::string compressed;
- base::ReadFileToString(base::MakeAbsoluteFilePath(filename), &compressed);
- ASSERT_TRUE(compressed.size());
- ASSERT_TRUE(gfx::PNGCodec::Decode(
- reinterpret_cast<const unsigned char*>(compressed.data()),
- compressed.size(), bitmap));
- }
-
- // Save the image to a png file. Used to create the initial test files.
- void SaveToFile(SkBitmap* bitmap, const base::FilePath& filename) {
- std::vector<unsigned char> compressed;
- ASSERT_TRUE(gfx::PNGCodec::Encode(
- static_cast<unsigned char*>(bitmap->getPixels()),
- gfx::PNGCodec::FORMAT_BGRA,
- gfx::Size(bitmap->width(), bitmap->height()),
- static_cast<int>(bitmap->rowBytes()), true,
- std::vector<gfx::PNGCodec::Comment>(), &compressed));
- ASSERT_TRUE(compressed.size());
- FILE* f = base::OpenFile(filename, "wb");
- ASSERT_TRUE(f);
- ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
- compressed.size());
- base::CloseFile(f);
- }
-
- std::unique_ptr<gpu::GLInProcessContext> context_;
- gpu::gles2::GLES2Interface* gl_;
- std::unique_ptr<GLHelper> helper_;
- std::unique_ptr<GLHelperScaling> helper_scaling_;
- std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
-};
-
-TEST_F(GLHelperBenchmark, ScaleBenchmark) {
- int output_sizes[] = {1920, 1080, 1249, 720, // Output size on pixel
- 256, 144};
- int input_sizes[] = {3200, 2040, 2560, 1476, // Pixel tab size
- 1920, 1080, 1280, 720, 800, 480, 256, 144};
-
- for (size_t q = 0; q < arraysize(kQualities); q++) {
- for (size_t outsize = 0; outsize < arraysize(output_sizes); outsize += 2) {
- for (size_t insize = 0; insize < arraysize(input_sizes); insize += 2) {
- uint32_t src_texture;
- gl_->GenTextures(1, &src_texture);
- uint32_t dst_texture;
- gl_->GenTextures(1, &dst_texture);
- uint32_t framebuffer;
- gl_->GenFramebuffers(1, &framebuffer);
- const gfx::Size src_size(input_sizes[insize], input_sizes[insize + 1]);
- const gfx::Size dst_size(output_sizes[outsize],
- output_sizes[outsize + 1]);
- SkBitmap input;
- input.allocN32Pixels(src_size.width(), src_size.height());
-
- SkBitmap output_pixels;
- output_pixels.allocN32Pixels(dst_size.width(), dst_size.height());
-
- gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- gl_->BindTexture(GL_TEXTURE_2D, dst_texture);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dst_size.width(),
- dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- gl_->BindTexture(GL_TEXTURE_2D, src_texture);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(),
- src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
- input.getPixels());
-
- gfx::Rect src_subrect(0, 0, src_size.width(), src_size.height());
- std::unique_ptr<GLHelper::ScalerInterface> scaler(helper_->CreateScaler(
- kQualities[q], src_size, src_subrect, dst_size, false, false));
- // Scale once beforehand before we start measuring.
- scaler->Scale(src_texture, dst_texture);
- gl_->Finish();
-
- base::TimeTicks start_time = base::TimeTicks::Now();
- int iterations = 0;
- base::TimeTicks end_time;
- while (true) {
- for (int i = 0; i < 50; i++) {
- iterations++;
- scaler->Scale(src_texture, dst_texture);
- gl_->Flush();
- }
- gl_->Finish();
- end_time = base::TimeTicks::Now();
- if (iterations > 2000) {
- break;
- }
- if ((end_time - start_time).InMillisecondsF() > 1000) {
- break;
- }
- }
- gl_->DeleteTextures(1, &dst_texture);
- gl_->DeleteTextures(1, &src_texture);
- gl_->DeleteFramebuffers(1, &framebuffer);
-
- std::string name;
- name = base::StringPrintf("scale_%dx%d_to_%dx%d_%s", src_size.width(),
- src_size.height(), dst_size.width(),
- dst_size.height(), kQualityNames[q]);
-
- float ms = (end_time - start_time).InMillisecondsF() / iterations;
- VLOG(0) << base::StringPrintf("*RESULT gpu_scale_time: %s=%.2f ms\n",
- name.c_str(), ms);
- }
- }
- }
-}
-
-// This is more of a test utility than a test.
-// Put an PNG image called "testimage.png" in your
-// current directory, then run this test. It will
-// create testoutput_Q_P.png, where Q is the scaling
-// mode and P is the scaling percentage taken from
-// the table below.
-TEST_F(GLHelperBenchmark, DISABLED_ScaleTestImage) {
- int percents[] = {
- 230, 180, 150, 110, 90, 70, 50, 49, 40, 20, 10,
- };
-
- SkBitmap input;
- LoadPngFileToSkBitmap(base::FilePath(FILE_PATH_LITERAL("testimage.png")),
- &input);
-
- uint32_t framebuffer;
- gl_->GenFramebuffers(1, &framebuffer);
- uint32_t src_texture;
- gl_->GenTextures(1, &src_texture);
- const gfx::Size src_size(input.width(), input.height());
- gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- gl_->BindTexture(GL_TEXTURE_2D, src_texture);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(),
- src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
- input.getPixels());
-
- for (size_t q = 0; q < arraysize(kQualities); q++) {
- for (size_t p = 0; p < arraysize(percents); p++) {
- const gfx::Size dst_size(input.width() * percents[p] / 100,
- input.height() * percents[p] / 100);
- uint32_t dst_texture = helper_->CopyAndScaleTexture(
- src_texture, src_size, dst_size, false, kQualities[q]);
-
- SkBitmap output_pixels;
- output_pixels.allocN32Pixels(dst_size.width(), dst_size.height());
-
- helper_->ReadbackTextureSync(
- dst_texture, gfx::Rect(0, 0, dst_size.width(), dst_size.height()),
- static_cast<unsigned char*>(output_pixels.getPixels()),
- kN32_SkColorType);
- gl_->DeleteTextures(1, &dst_texture);
- std::string filename = base::StringPrintf("testoutput_%s_%d.ppm",
- kQualityNames[q], percents[p]);
- VLOG(0) << "Writing " << filename;
- SaveToFile(&output_pixels, base::FilePath::FromUTF8Unsafe(filename));
- }
- }
- gl_->DeleteTextures(1, &src_texture);
- gl_->DeleteFramebuffers(1, &framebuffer);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/gl_helper_readback_support.cc b/chromium/components/viz/display_compositor/gl_helper_readback_support.cc
deleted file mode 100644
index 5fd12924b37..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper_readback_support.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/display_compositor/gl_helper_readback_support.h"
-#include "base/logging.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
-
-namespace viz {
-
-GLHelperReadbackSupport::GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl)
- : gl_(gl) {
- InitializeReadbackSupport();
-}
-
-GLHelperReadbackSupport::~GLHelperReadbackSupport() {}
-
-void GLHelperReadbackSupport::InitializeReadbackSupport() {
- // We are concerned about 16, 32-bit formats only. The below are the most
- // used 16, 32-bit formats. In future if any new format support is needed
- // that should be added here. Initialize the array with
- // GLHelperReadbackSupport::NOT_SUPPORTED as we dont know the supported
- // formats yet.
- for (int i = 0; i <= kLastEnum_SkColorType; ++i) {
- format_support_table_[i] = GLHelperReadbackSupport::NOT_SUPPORTED;
- }
- // TODO(sikugu): kAlpha_8_SkColorType support check is failing on mesa.
- // See crbug.com/415667.
- CheckForReadbackSupport(kRGB_565_SkColorType);
- CheckForReadbackSupport(kARGB_4444_SkColorType);
- CheckForReadbackSupport(kRGBA_8888_SkColorType);
- CheckForReadbackSupport(kBGRA_8888_SkColorType);
- // Further any formats, support should be checked here.
-}
-
-void GLHelperReadbackSupport::CheckForReadbackSupport(
- SkColorType texture_format) {
- bool supports_format = false;
- switch (texture_format) {
- case kRGB_565_SkColorType:
- supports_format = SupportsFormat(GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
- break;
- case kRGBA_8888_SkColorType:
- // This is the baseline, assume always true.
- supports_format = true;
- break;
- case kBGRA_8888_SkColorType:
- supports_format = SupportsFormat(GL_BGRA_EXT, GL_UNSIGNED_BYTE);
- break;
- case kARGB_4444_SkColorType:
- supports_format = false;
- break;
- default:
- NOTREACHED();
- supports_format = false;
- break;
- }
- DCHECK((int)texture_format <= (int)kLastEnum_SkColorType);
- format_support_table_[texture_format] =
- supports_format ? GLHelperReadbackSupport::SUPPORTED
- : GLHelperReadbackSupport::NOT_SUPPORTED;
-}
-
-void GLHelperReadbackSupport::GetAdditionalFormat(GLenum format,
- GLenum type,
- GLenum* format_out,
- GLenum* type_out) {
- for (unsigned int i = 0; i < format_cache_.size(); i++) {
- if (format_cache_[i].format == format && format_cache_[i].type == type) {
- *format_out = format_cache_[i].read_format;
- *type_out = format_cache_[i].read_type;
- return;
- }
- }
-
- const int kTestSize = 64;
- ScopedTexture dst_texture(gl_);
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, format, kTestSize, kTestSize, 0, format,
- type, NULL);
- ScopedFramebuffer dst_framebuffer(gl_);
- ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
- dst_framebuffer);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- dst_texture, 0);
- GLint format_tmp = 0, type_tmp = 0;
- gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format_tmp);
- gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type_tmp);
- *format_out = format_tmp;
- *type_out = type_tmp;
-
- struct FormatCacheEntry entry = {format, type, *format_out, *type_out};
- format_cache_.push_back(entry);
-}
-
-bool GLHelperReadbackSupport::SupportsFormat(GLenum format, GLenum type) {
- // GLES2.0 Specification says this pairing is always supported
- // with additional format from GL_IMPLEMENTATION_COLOR_READ_FORMAT/TYPE
- if (format == GL_RGBA && type == GL_UNSIGNED_BYTE)
- return true;
-
- bool supports_format = false;
- GLenum ext_format = 0, ext_type = 0;
- GetAdditionalFormat(format, type, &ext_format, &ext_type);
- if ((ext_format == format) && (ext_type == type)) {
- supports_format = true;
- }
- return supports_format;
-}
-
-GLHelperReadbackSupport::FormatSupport
-GLHelperReadbackSupport::GetReadbackConfig(SkColorType color_type,
- bool can_swizzle,
- GLenum* format,
- GLenum* type,
- size_t* bytes_per_pixel) {
- DCHECK(format && type && bytes_per_pixel);
- *bytes_per_pixel = 4;
- *type = GL_UNSIGNED_BYTE;
- GLenum new_format = 0, new_type = 0;
- switch (color_type) {
- case kRGB_565_SkColorType:
- if (format_support_table_[color_type] ==
- GLHelperReadbackSupport::SUPPORTED) {
- *format = GL_RGB;
- *type = GL_UNSIGNED_SHORT_5_6_5;
- *bytes_per_pixel = 2;
- return GLHelperReadbackSupport::SUPPORTED;
- }
- break;
- case kRGBA_8888_SkColorType:
- *format = GL_RGBA;
- if (can_swizzle) {
- // If GL_BGRA_EXT is advertised as the readback format through
- // GL_IMPLEMENTATION_COLOR_READ_FORMAT then assume it is preferred by
- // the implementation for performance.
- GetAdditionalFormat(*format, *type, &new_format, &new_type);
-
- if (new_format == GL_BGRA_EXT && new_type == GL_UNSIGNED_BYTE) {
- *format = GL_BGRA_EXT;
- return GLHelperReadbackSupport::SWIZZLE;
- }
- }
- return GLHelperReadbackSupport::SUPPORTED;
- case kBGRA_8888_SkColorType:
- *format = GL_BGRA_EXT;
- if (format_support_table_[color_type] ==
- GLHelperReadbackSupport::SUPPORTED)
- return GLHelperReadbackSupport::SUPPORTED;
-
- if (can_swizzle) {
- *format = GL_RGBA;
- return GLHelperReadbackSupport::SWIZZLE;
- }
-
- break;
- case kARGB_4444_SkColorType:
- return GLHelperReadbackSupport::NOT_SUPPORTED;
- default:
- NOTREACHED();
- break;
- }
-
- return GLHelperReadbackSupport::NOT_SUPPORTED;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/gl_helper_readback_support.h b/chromium/components/viz/display_compositor/gl_helper_readback_support.h
deleted file mode 100644
index b68f4dd5c60..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper_readback_support.h
+++ /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.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_READBACK_SUPPORT_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_READBACK_SUPPORT_H_
-
-#include <stddef.h>
-
-#include <vector>
-
-#include "components/viz/display_compositor/gl_helper.h"
-#include "components/viz/viz_export.h"
-
-namespace viz {
-
-class VIZ_EXPORT GLHelperReadbackSupport {
- public:
- enum FormatSupport { SUPPORTED, SWIZZLE, NOT_SUPPORTED };
-
- explicit GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl);
-
- ~GLHelperReadbackSupport();
-
- // For a given color type retrieve whether readback is supported and if so
- // how it should be performed. The |format|, |type| and |bytes_per_pixel| are
- // the values that should be used with glReadPixels to facilitate the
- // readback. If |can_swizzle| is true then this method will return SWIZZLE if
- // the data needs to be swizzled before using the returned |format| otherwise
- // the method will return SUPPORTED to indicate that readback is permitted of
- // this color othewise NOT_SUPPORTED will be returned. This method always
- // overwrites the out values irrespective of the return value.
- FormatSupport GetReadbackConfig(SkColorType color_type,
- bool can_swizzle,
- GLenum* format,
- GLenum* type,
- size_t* bytes_per_pixel);
- // Provides the additional readback format/type pairing for a render target
- // of a given format/type pairing
- void GetAdditionalFormat(GLenum format,
- GLenum type,
- GLenum* format_out,
- GLenum* type_out);
-
- private:
- struct FormatCacheEntry {
- GLenum format;
- GLenum type;
- GLenum read_format;
- GLenum read_type;
- };
-
- // This populates the format_support_table with the list of supported
- // formats.
- void InitializeReadbackSupport();
-
- // This api is called once per format and it is done in the
- // InitializeReadbackSupport. We should not use this any where
- // except the InitializeReadbackSupport.Calling this at other places
- // can distrub the state of normal gl operations.
- void CheckForReadbackSupport(SkColorType texture_format);
-
- // Helper functions for checking the supported texture formats.
- // Avoid using this API in between texture operations, as this does some
- // teture opertions (bind, attach) internally.
- bool SupportsFormat(GLenum format, GLenum type);
-
- FormatSupport format_support_table_[kLastEnum_SkColorType + 1];
-
- gpu::gles2::GLES2Interface* gl_;
- std::vector<struct FormatCacheEntry> format_cache_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_READBACK_SUPPORT_H_
diff --git a/chromium/components/viz/display_compositor/gl_helper_scaling.cc b/chromium/components/viz/display_compositor/gl_helper_scaling.cc
deleted file mode 100644
index 9f29c3625f2..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper_scaling.cc
+++ /dev/null
@@ -1,881 +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/viz/display_compositor/gl_helper_scaling.h"
-
-#include <stddef.h>
-
-#include <deque>
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-
-using gpu::gles2::GLES2Interface;
-
-namespace viz {
-
-GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper)
- : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) {
- InitBuffer();
-}
-
-GLHelperScaling::~GLHelperScaling() {}
-
-// Used to keep track of a generated shader program. The program
-// is passed in as text through Setup and is used by calling
-// UseProgram() with the right parameters. Note that |gl_|
-// and |helper_| are assumed to live longer than this program.
-class ShaderProgram : public base::RefCounted<ShaderProgram> {
- public:
- ShaderProgram(GLES2Interface* gl, GLHelper* helper)
- : gl_(gl),
- helper_(helper),
- program_(gl_->CreateProgram()),
- position_location_(-1),
- texcoord_location_(-1),
- src_subrect_location_(-1),
- src_pixelsize_location_(-1),
- dst_pixelsize_location_(-1),
- scaling_vector_location_(-1),
- color_weights_location_(-1) {}
-
- // Compile shader program.
- void Setup(const GLchar* vertex_shader_text,
- const GLchar* fragment_shader_text);
-
- // UseProgram must be called with GL_TEXTURE_2D bound to the
- // source texture and GL_ARRAY_BUFFER bound to a vertex
- // attribute buffer.
- void UseProgram(const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool scale_x,
- bool flip_y,
- GLfloat color_weights[4]);
-
- bool Initialized() const { return position_location_ != -1; }
-
- private:
- friend class base::RefCounted<ShaderProgram>;
- ~ShaderProgram() { gl_->DeleteProgram(program_); }
-
- GLES2Interface* gl_;
- GLHelper* helper_;
-
- // A program for copying a source texture into a destination texture.
- GLuint program_;
-
- // The location of the position in the program.
- GLint position_location_;
- // The location of the texture coordinate in the program.
- GLint texcoord_location_;
- // The location of the source texture in the program.
- GLint texture_location_;
- // The location of the texture coordinate of
- // the sub-rectangle in the program.
- GLint src_subrect_location_;
- // Location of size of source image in pixels.
- GLint src_pixelsize_location_;
- // Location of size of destination image in pixels.
- GLint dst_pixelsize_location_;
- // Location of vector for scaling direction.
- GLint scaling_vector_location_;
- // Location of color weights.
- GLint color_weights_location_;
-
- DISALLOW_COPY_AND_ASSIGN(ShaderProgram);
-};
-
-// Implementation of a single stage in a scaler pipeline. If the pipeline has
-// multiple stages, it calls Scale() on the subscaler, then further scales the
-// output. Caches textures and framebuffers to avoid allocating/deleting
-// them once per frame, which can be expensive on some drivers.
-class ScalerImpl : public GLHelper::ScalerInterface,
- public GLHelperScaling::ShaderInterface {
- public:
- // |gl| and |copy_impl| are expected to live longer than this object.
- // |src_size| is the size of the input texture in pixels.
- // |dst_size| is the size of the output texutre in pixels.
- // |src_subrect| is the portion of the src to copy to the output texture.
- // If |scale_x| is true, we are scaling along the X axis, otherwise Y.
- // If we are scaling in both X and Y, |scale_x| is ignored.
- // If |vertically_flip_texture| is true, output will be upside-down.
- // If |swizzle| is true, RGBA will be transformed into BGRA.
- // |color_weights| are only used together with SHADER_PLANAR to specify
- // how to convert RGB colors into a single value.
- ScalerImpl(GLES2Interface* gl,
- GLHelperScaling* scaler_helper,
- const GLHelperScaling::ScalerStage& scaler_stage,
- ScalerImpl* subscaler,
- const float* color_weights)
- : gl_(gl),
- scaler_helper_(scaler_helper),
- spec_(scaler_stage),
- intermediate_texture_(0),
- dst_framebuffer_(gl),
- subscaler_(subscaler) {
- if (color_weights) {
- color_weights_[0] = color_weights[0];
- color_weights_[1] = color_weights[1];
- color_weights_[2] = color_weights[2];
- color_weights_[3] = color_weights[3];
- } else {
- color_weights_[0] = 0.0;
- color_weights_[1] = 0.0;
- color_weights_[2] = 0.0;
- color_weights_[3] = 0.0;
- }
- shader_program_ =
- scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle);
-
- if (subscaler_) {
- intermediate_texture_ = 0u;
- gl_->GenTextures(1, &intermediate_texture_);
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_,
- intermediate_texture_);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spec_.src_size.width(),
- spec_.src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
- NULL);
- }
- }
-
- ~ScalerImpl() override {
- if (intermediate_texture_) {
- gl_->DeleteTextures(1, &intermediate_texture_);
- }
- }
-
- // GLHelperShader::ShaderInterface implementation.
- void Execute(GLuint source_texture,
- const std::vector<GLuint>& dest_textures) override {
- if (subscaler_) {
- subscaler_->Scale(source_texture, intermediate_texture_);
- source_texture = intermediate_texture_;
- }
-
- ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
- gl_, dst_framebuffer_);
- DCHECK_GT(dest_textures.size(), 0U);
- std::unique_ptr<GLenum[]> buffers(new GLenum[dest_textures.size()]);
- for (size_t t = 0; t < dest_textures.size(); t++) {
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dest_textures[t]);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + t,
- GL_TEXTURE_2D, dest_textures[t], 0);
- buffers[t] = GL_COLOR_ATTACHMENT0 + t;
- }
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, source_texture);
-
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(
- gl_, scaler_helper_->vertex_attributes_buffer_);
- shader_program_->UseProgram(spec_.src_size, spec_.src_subrect,
- spec_.dst_size, spec_.scale_x,
- spec_.vertically_flip_texture, color_weights_);
- gl_->Viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height());
-
- if (dest_textures.size() > 1) {
- DCHECK_LE(static_cast<int>(dest_textures.size()),
- scaler_helper_->helper_->MaxDrawBuffers());
- gl_->DrawBuffersEXT(dest_textures.size(), buffers.get());
- }
- // Conduct texture mapping by drawing a quad composed of two triangles.
- gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- if (dest_textures.size() > 1) {
- // Set the draw buffers back to not confuse others.
- gl_->DrawBuffersEXT(1, &buffers[0]);
- }
- }
-
- // GLHelper::ScalerInterface implementation.
- void Scale(GLuint source_texture, GLuint dest_texture) override {
- std::vector<GLuint> tmp(1);
- tmp[0] = dest_texture;
- Execute(source_texture, tmp);
- }
-
- const gfx::Size& SrcSize() override {
- if (subscaler_) {
- return subscaler_->SrcSize();
- }
- return spec_.src_size;
- }
- const gfx::Rect& SrcSubrect() override {
- if (subscaler_) {
- return subscaler_->SrcSubrect();
- }
- return spec_.src_subrect;
- }
- const gfx::Size& DstSize() override { return spec_.dst_size; }
-
- private:
- GLES2Interface* gl_;
- GLHelperScaling* scaler_helper_;
- GLHelperScaling::ScalerStage spec_;
- GLfloat color_weights_[4];
- GLuint intermediate_texture_;
- scoped_refptr<ShaderProgram> shader_program_;
- ScopedFramebuffer dst_framebuffer_;
- std::unique_ptr<ScalerImpl> subscaler_;
-};
-
-GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_,
- gfx::Size src_size_,
- gfx::Rect src_subrect_,
- gfx::Size dst_size_,
- bool scale_x_,
- bool vertically_flip_texture_,
- bool swizzle_)
- : shader(shader_),
- src_size(src_size_),
- src_subrect(src_subrect_),
- dst_size(dst_size_),
- scale_x(scale_x_),
- vertically_flip_texture(vertically_flip_texture_),
- swizzle(swizzle_) {}
-
-GLHelperScaling::ScalerStage::ScalerStage(const ScalerStage& other) = default;
-
-// The important inputs for this function is |x_ops| and
-// |y_ops|. They represent scaling operations to be done
-// on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST,
-// then we will interpret these scale operations literally and we'll
-// create one scaler stage for each ScaleOp. However, if |quality|
-// is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations
-// by combining two or more ScaleOps in to a single scaler stage.
-// Normally we process ScaleOps from |y_ops| first and |x_ops| after
-// all |y_ops| are processed, but sometimes we can combine one or more
-// operation from both queues essentially for free. This is the reason
-// why |x_ops| and |y_ops| aren't just one single queue.
-void GLHelperScaling::ConvertScalerOpsToScalerStages(
- GLHelper::ScalerQuality quality,
- gfx::Size src_size,
- gfx::Rect src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- std::deque<GLHelperScaling::ScaleOp>* x_ops,
- std::deque<GLHelperScaling::ScaleOp>* y_ops,
- std::vector<ScalerStage>* scaler_stages) {
- while (!x_ops->empty() || !y_ops->empty()) {
- gfx::Size intermediate_size = src_subrect.size();
- std::deque<ScaleOp>* current_queue = NULL;
-
- if (!y_ops->empty()) {
- current_queue = y_ops;
- } else {
- current_queue = x_ops;
- }
-
- ShaderType current_shader = SHADER_BILINEAR;
- switch (current_queue->front().scale_factor) {
- case 0:
- if (quality == GLHelper::SCALER_QUALITY_BEST) {
- current_shader = SHADER_BICUBIC_UPSCALE;
- }
- break;
- case 2:
- if (quality == GLHelper::SCALER_QUALITY_BEST) {
- current_shader = SHADER_BICUBIC_HALF_1D;
- }
- break;
- case 3:
- DCHECK(quality != GLHelper::SCALER_QUALITY_BEST);
- current_shader = SHADER_BILINEAR3;
- break;
- default:
- NOTREACHED();
- }
- bool scale_x = current_queue->front().scale_x;
- current_queue->front().UpdateSize(&intermediate_size);
- current_queue->pop_front();
-
- // Optimization: Sometimes we can combine 2-4 scaling operations into
- // one operation.
- if (quality == GLHelper::SCALER_QUALITY_GOOD) {
- if (!current_queue->empty() && current_shader == SHADER_BILINEAR) {
- // Combine two steps in the same dimension.
- current_queue->front().UpdateSize(&intermediate_size);
- current_queue->pop_front();
- current_shader = SHADER_BILINEAR2;
- if (!current_queue->empty()) {
- // Combine three steps in the same dimension.
- current_queue->front().UpdateSize(&intermediate_size);
- current_queue->pop_front();
- current_shader = SHADER_BILINEAR4;
- }
- }
- // Check if we can combine some steps in the other dimension as well.
- // Since all shaders currently use GL_LINEAR, we can easily scale up
- // or scale down by exactly 2x at the same time as we do another
- // operation. Currently, the following mergers are supported:
- // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down)
- // * 2 bilinear Y-passes with 2 bilinear X-passes
- // * 1 bilinear Y-pass with N bilinear X-pass
- // * N bilinear Y-passes with 1 bilinear X-pass (down only)
- // Measurements indicate that generalizing this for 3x3 and 4x4
- // makes it slower on some platforms, such as the Pixel.
- if (!scale_x && x_ops->size() > 0 && x_ops->front().scale_factor <= 2) {
- int x_passes = 0;
- if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) {
- // 2y + 2x passes
- x_passes = 2;
- current_shader = SHADER_BILINEAR2X2;
- } else if (current_shader == SHADER_BILINEAR) {
- // 1y + Nx passes
- scale_x = true;
- switch (x_ops->size()) {
- case 0:
- NOTREACHED();
- case 1:
- if (x_ops->front().scale_factor == 3) {
- current_shader = SHADER_BILINEAR3;
- }
- x_passes = 1;
- break;
- case 2:
- x_passes = 2;
- current_shader = SHADER_BILINEAR2;
- break;
- default:
- x_passes = 3;
- current_shader = SHADER_BILINEAR4;
- break;
- }
- } else if (x_ops->front().scale_factor == 2) {
- // Ny + 1x-downscale
- x_passes = 1;
- }
-
- for (int i = 0; i < x_passes; i++) {
- x_ops->front().UpdateSize(&intermediate_size);
- x_ops->pop_front();
- }
- }
- }
-
- scaler_stages->push_back(ScalerStage(current_shader, src_size, src_subrect,
- intermediate_size, scale_x,
- vertically_flip_texture, swizzle));
- src_size = intermediate_size;
- src_subrect = gfx::Rect(intermediate_size);
- vertically_flip_texture = false;
- swizzle = false;
- }
-}
-
-void GLHelperScaling::ComputeScalerStages(
- GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- std::vector<ScalerStage>* scaler_stages) {
- if (quality == GLHelper::SCALER_QUALITY_FAST ||
- src_subrect.size() == dst_size) {
- scaler_stages->push_back(ScalerStage(SHADER_BILINEAR, src_size, src_subrect,
- dst_size, false,
- vertically_flip_texture, swizzle));
- return;
- }
-
- std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops;
- GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), dst_size.width(), true,
- quality == GLHelper::SCALER_QUALITY_GOOD,
- &x_ops);
- GLHelperScaling::ScaleOp::AddOps(
- src_subrect.height(), dst_size.height(), false,
- quality == GLHelper::SCALER_QUALITY_GOOD, &y_ops);
-
- ConvertScalerOpsToScalerStages(quality, src_size, src_subrect, dst_size,
- vertically_flip_texture, swizzle, &x_ops,
- &y_ops, scaler_stages);
-}
-
-GLHelper::ScalerInterface* GLHelperScaling::CreateScaler(
- GLHelper::ScalerQuality quality,
- gfx::Size src_size,
- gfx::Rect src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle) {
- std::vector<ScalerStage> scaler_stages;
- ComputeScalerStages(quality, src_size, src_subrect, dst_size,
- vertically_flip_texture, swizzle, &scaler_stages);
-
- ScalerImpl* ret = NULL;
- for (unsigned int i = 0; i < scaler_stages.size(); i++) {
- ret = new ScalerImpl(gl_, this, scaler_stages[i], ret, NULL);
- }
- return ret;
-}
-
-GLHelper::ScalerInterface* GLHelperScaling::CreatePlanarScaler(
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- const float color_weights[4]) {
- ScalerStage stage(SHADER_PLANAR, src_size, src_subrect, dst_size, true,
- vertically_flip_texture, swizzle);
- return new ScalerImpl(gl_, this, stage, NULL, color_weights);
-}
-
-GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader(
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- ShaderType shader) {
- DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2);
- ScalerStage stage(shader, src_size, src_subrect, dst_size, true,
- vertically_flip_texture, swizzle);
- return new ScalerImpl(gl_, this, stage, NULL, NULL);
-}
-
-const GLfloat GLHelperScaling::kVertexAttributes[] = {
- -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0
- 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1
- -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2
- 1.0f, 1.0f, 1.0f, 1.0f,
-}; // vertex 3
-
-void GLHelperScaling::InitBuffer() {
- ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(gl_,
- vertex_attributes_buffer_);
- gl_->BufferData(GL_ARRAY_BUFFER, sizeof(kVertexAttributes), kVertexAttributes,
- GL_STATIC_DRAW);
-}
-
-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()) {
- cache_entry = new ShaderProgram(gl_, helper_);
- std::basic_string<GLchar> vertex_program;
- std::basic_string<GLchar> fragment_program;
- std::basic_string<GLchar> vertex_header;
- std::basic_string<GLchar> fragment_directives;
- std::basic_string<GLchar> fragment_header;
- std::basic_string<GLchar> shared_variables;
-
- vertex_header.append(
- "precision highp float;\n"
- "attribute vec2 a_position;\n"
- "attribute vec2 a_texcoord;\n"
- "uniform vec4 src_subrect;\n");
-
- fragment_header.append(
- "precision mediump float;\n"
- "uniform sampler2D s_texture;\n");
-
- vertex_program.append(
- " gl_Position = vec4(a_position, 0.0, 1.0);\n"
- " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n");
-
- switch (type) {
- case SHADER_BILINEAR:
- shared_variables.append("varying vec2 v_texcoord;\n");
- vertex_program.append(" v_texcoord = texcoord;\n");
- fragment_program.append(
- " gl_FragColor = texture2D(s_texture, v_texcoord);\n");
- break;
-
- case SHADER_BILINEAR2:
- // This is equivialent to two passes of the BILINEAR shader above.
- // It can be used to scale an image down 1.0x-2.0x in either dimension,
- // or exactly 4x.
- shared_variables.append(
- "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad
- vertex_header.append(
- "uniform vec2 scaling_vector;\n"
- "uniform vec2 dst_pixelsize;\n");
- vertex_program.append(
- " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
- " step /= 4.0;\n"
- " v_texcoords.xy = texcoord + step;\n"
- " v_texcoords.zw = texcoord - step;\n");
-
- fragment_program.append(
- " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n"
- " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n");
- break;
-
- case SHADER_BILINEAR3:
- // This is kind of like doing 1.5 passes of the BILINEAR shader.
- // It can be used to scale an image down 1.5x-3.0x, or exactly 6x.
- shared_variables.append(
- "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad
- "varying vec2 v_texcoords2;\n");
- vertex_header.append(
- "uniform vec2 scaling_vector;\n"
- "uniform vec2 dst_pixelsize;\n");
- vertex_program.append(
- " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
- " step /= 3.0;\n"
- " v_texcoords1.xy = texcoord + step;\n"
- " v_texcoords1.zw = texcoord;\n"
- " v_texcoords2 = texcoord - step;\n");
- fragment_program.append(
- " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n"
- " texture2D(s_texture, v_texcoords1.zw) +\n"
- " texture2D(s_texture, v_texcoords2)) / 3.0;\n");
- break;
-
- case SHADER_BILINEAR4:
- // This is equivialent to three passes of the BILINEAR shader above,
- // It can be used to scale an image down 2.0x-4.0x or exactly 8x.
- shared_variables.append("varying vec4 v_texcoords[2];\n");
- vertex_header.append(
- "uniform vec2 scaling_vector;\n"
- "uniform vec2 dst_pixelsize;\n");
- vertex_program.append(
- " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
- " step /= 8.0;\n"
- " v_texcoords[0].xy = texcoord - step * 3.0;\n"
- " v_texcoords[0].zw = texcoord - step;\n"
- " v_texcoords[1].xy = texcoord + step;\n"
- " v_texcoords[1].zw = texcoord + step * 3.0;\n");
- fragment_program.append(
- " gl_FragColor = (\n"
- " texture2D(s_texture, v_texcoords[0].xy) +\n"
- " texture2D(s_texture, v_texcoords[0].zw) +\n"
- " texture2D(s_texture, v_texcoords[1].xy) +\n"
- " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
- break;
-
- case SHADER_BILINEAR2X2:
- // This is equivialent to four passes of the BILINEAR shader above.
- // Two in each dimension. It can be used to scale an image down
- // 1.0x-2.0x in both X and Y directions. Or, it could be used to
- // scale an image down by exactly 4x in both dimensions.
- shared_variables.append("varying vec4 v_texcoords[2];\n");
- vertex_header.append("uniform vec2 dst_pixelsize;\n");
- vertex_program.append(
- " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n"
- " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
- " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
- " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
- " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
- fragment_program.append(
- " gl_FragColor = (\n"
- " texture2D(s_texture, v_texcoords[0].xy) +\n"
- " texture2D(s_texture, v_texcoords[0].zw) +\n"
- " texture2D(s_texture, v_texcoords[1].xy) +\n"
- " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
- break;
-
- case SHADER_BICUBIC_HALF_1D:
- // This scales down texture by exactly half in one dimension.
- // directions in one pass. We use bilinear lookup to reduce
- // the number of texture reads from 8 to 4
- shared_variables.append(
- "const float CenterDist = 99.0 / 140.0;\n"
- "const float LobeDist = 11.0 / 4.0;\n"
- "const float CenterWeight = 35.0 / 64.0;\n"
- "const float LobeWeight = -3.0 / 64.0;\n"
- "varying vec4 v_texcoords[2];\n");
- vertex_header.append(
- "uniform vec2 scaling_vector;\n"
- "uniform vec2 src_pixelsize;\n");
- vertex_program.append(
- " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n"
- " v_texcoords[0].xy = texcoord - LobeDist * step;\n"
- " v_texcoords[0].zw = texcoord - CenterDist * step;\n"
- " v_texcoords[1].xy = texcoord + CenterDist * step;\n"
- " v_texcoords[1].zw = texcoord + LobeDist * step;\n");
- fragment_program.append(
- " gl_FragColor = \n"
- // Lobe pixels
- " (texture2D(s_texture, v_texcoords[0].xy) +\n"
- " texture2D(s_texture, v_texcoords[1].zw)) *\n"
- " LobeWeight +\n"
- // Center pixels
- " (texture2D(s_texture, v_texcoords[0].zw) +\n"
- " texture2D(s_texture, v_texcoords[1].xy)) *\n"
- " CenterWeight;\n");
- break;
-
- case SHADER_BICUBIC_UPSCALE:
- // When scaling up, we need 4 texture reads, but we can
- // save some instructions because will know in which range of
- // the bicubic function each call call to the bicubic function
- // will be in.
- // Also, when sampling the bicubic function like this, the sum
- // is always exactly one, so we can skip normalization as well.
- shared_variables.append("varying vec2 v_texcoord;\n");
- vertex_program.append(" v_texcoord = texcoord;\n");
- fragment_header.append(
- "uniform vec2 src_pixelsize;\n"
- "uniform vec2 scaling_vector;\n"
- "const float a = -0.5;\n"
- // This function is equivialent to calling the bicubic
- // function with x-1, x, 1-x and 2-x
- // (assuming 0 <= x < 1)
- "vec4 filt4(float x) {\n"
- " return vec4(x * x * x, x * x, x, 1) *\n"
- " mat4( a, -2.0 * a, a, 0.0,\n"
- " a + 2.0, -a - 3.0, 0.0, 1.0,\n"
- " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n"
- " -a, a, 0.0, 0.0);\n"
- "}\n"
- "mat4 pixels_x(vec2 pos, vec2 step) {\n"
- " return mat4(\n"
- " texture2D(s_texture, pos - step),\n"
- " texture2D(s_texture, pos),\n"
- " texture2D(s_texture, pos + step),\n"
- " texture2D(s_texture, pos + step * 2.0));\n"
- "}\n");
- fragment_program.append(
- " vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
- " scaling_vector / 2.0;\n"
- " float frac = fract(dot(pixel_pos, scaling_vector));\n"
- " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
- " vec2 step = scaling_vector / src_pixelsize;\n"
- " gl_FragColor = pixels_x(base, step) * filt4(frac);\n");
- break;
-
- case SHADER_PLANAR:
- // Converts four RGBA pixels into one pixel. Each RGBA
- // pixel will be dot-multiplied with the color weights and
- // then placed into a component of the output. This is used to
- // convert RGBA textures into Y, U and V textures. We do this
- // because single-component textures are not renderable on all
- // architectures.
- shared_variables.append("varying vec4 v_texcoords[2];\n");
- vertex_header.append(
- "uniform vec2 scaling_vector;\n"
- "uniform vec2 dst_pixelsize;\n");
- vertex_program.append(
- " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
- " step /= 4.0;\n"
- " v_texcoords[0].xy = texcoord - step * 1.5;\n"
- " v_texcoords[0].zw = texcoord - step * 0.5;\n"
- " v_texcoords[1].xy = texcoord + step * 0.5;\n"
- " v_texcoords[1].zw = texcoord + step * 1.5;\n");
- fragment_header.append("uniform vec4 color_weights;\n");
- fragment_program.append(
- " gl_FragColor = color_weights * mat4(\n"
- " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n"
- " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n"
- " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n"
- " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n");
- break;
-
- case SHADER_YUV_MRT_PASS1:
- // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
- //
- // YV12 is full-resolution luma and half-resolution blue/red chroma.
- //
- // (original)
- // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
- // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
- // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
- // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
- // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
- // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
- // |
- // | (y plane) (temporary)
- // | YYYY YYYY UUVV UUVV
- // +--> { YYYY YYYY + UUVV UUVV }
- // YYYY YYYY UUVV UUVV
- // First YYYY YYYY UUVV UUVV
- // pass YYYY YYYY UUVV UUVV
- // YYYY YYYY UUVV UUVV
- // |
- // | (u plane) (v plane)
- // Second | UUUU VVVV
- // pass +--> { UUUU + VVVV }
- // UUUU VVVV
- //
- shared_variables.append("varying vec4 v_texcoords[2];\n");
- vertex_header.append(
- "uniform vec2 scaling_vector;\n"
- "uniform vec2 dst_pixelsize;\n");
- vertex_program.append(
- " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
- " step /= 4.0;\n"
- " v_texcoords[0].xy = texcoord - step * 1.5;\n"
- " v_texcoords[0].zw = texcoord - step * 0.5;\n"
- " v_texcoords[1].xy = texcoord + step * 0.5;\n"
- " v_texcoords[1].zw = texcoord + step * 1.5;\n");
- fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n");
- fragment_header.append(
- "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n"
- "const float kYBias = 0.0625;\n"
- // Divide U and V by two to compensate for averaging below.
- "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n"
- "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n"
- "const float kUVBias = 0.5;\n");
- fragment_program.append(
- " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
- " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
- " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
- " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
- " vec3 pixel12 = pixel1 + pixel2;\n"
- " vec3 pixel34 = pixel3 + pixel4;\n"
- " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n"
- " dot(pixel2, kRGBtoY),\n"
- " dot(pixel3, kRGBtoY),\n"
- " dot(pixel4, kRGBtoY)) + kYBias;\n"
- " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n"
- " dot(pixel34, kRGBtoU),\n"
- " dot(pixel12, kRGBtoV),\n"
- " dot(pixel34, kRGBtoV)) + kUVBias;\n");
- break;
-
- case SHADER_YUV_MRT_PASS2:
- // We're just sampling two pixels and unswizzling them. There's
- // no need to do vertical scaling with math, since bilinear
- // interpolation in the sampler takes care of that.
- shared_variables.append("varying vec4 v_texcoords;\n");
- vertex_header.append(
- "uniform vec2 scaling_vector;\n"
- "uniform vec2 dst_pixelsize;\n");
- vertex_program.append(
- " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
- " step /= 2.0;\n"
- " v_texcoords.xy = texcoord - step * 0.5;\n"
- " v_texcoords.zw = texcoord + step * 0.5;\n");
- fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n");
- fragment_program.append(
- " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n"
- " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n"
- " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n"
- " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n");
- break;
- }
- if (swizzle) {
- switch (type) {
- case SHADER_YUV_MRT_PASS1:
- fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n");
- break;
- case SHADER_YUV_MRT_PASS2:
- fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n");
- fragment_program.append(" gl_FragData[1] = gl_FragData[1].bgra;\n");
- break;
- default:
- fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n");
- break;
- }
- }
-
- vertex_program = vertex_header + shared_variables + "void main() {\n" +
- vertex_program + "}\n";
-
- fragment_program = fragment_directives + fragment_header +
- shared_variables + "void main() {\n" + fragment_program +
- "}\n";
-
- cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str());
- }
- return cache_entry;
-}
-
-void ShaderProgram::Setup(const GLchar* vertex_shader_text,
- const GLchar* fragment_shader_text) {
- // Shaders to map the source texture to |dst_texture_|.
- GLuint vertex_shader =
- helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER);
- if (vertex_shader == 0)
- return;
-
- gl_->AttachShader(program_, vertex_shader);
- gl_->DeleteShader(vertex_shader);
-
- GLuint fragment_shader = helper_->CompileShaderFromSource(
- fragment_shader_text, GL_FRAGMENT_SHADER);
- if (fragment_shader == 0)
- return;
- gl_->AttachShader(program_, fragment_shader);
- gl_->DeleteShader(fragment_shader);
-
- gl_->LinkProgram(program_);
-
- GLint link_status = 0;
- gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
- if (!link_status)
- return;
-
- position_location_ = gl_->GetAttribLocation(program_, "a_position");
- texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
- texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
- src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect");
- src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize");
- dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize");
- scaling_vector_location_ =
- gl_->GetUniformLocation(program_, "scaling_vector");
- color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights");
- // The only reason fetching these attribute locations should fail is
- // if the context was spontaneously lost (i.e., because the GPU
- // process crashed, perhaps deliberately for testing).
- DCHECK(Initialized() || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR);
-}
-
-void ShaderProgram::UseProgram(const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool scale_x,
- bool flip_y,
- GLfloat color_weights[4]) {
- gl_->UseProgram(program_);
-
- // OpenGL defines the last parameter to VertexAttribPointer as type
- // "const GLvoid*" even though it is actually an offset into the buffer
- // object's data store and not a pointer to the client's address space.
- const void* offsets[2] = {0,
- reinterpret_cast<const void*>(2 * sizeof(GLfloat))};
-
- gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE,
- 4 * sizeof(GLfloat), offsets[0]);
- gl_->EnableVertexAttribArray(position_location_);
-
- gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE,
- 4 * sizeof(GLfloat), offsets[1]);
- gl_->EnableVertexAttribArray(texcoord_location_);
-
- gl_->Uniform1i(texture_location_, 0);
-
- // Convert |src_subrect| to texture coordinates.
- GLfloat src_subrect_texcoord[] = {
- static_cast<float>(src_subrect.x()) / src_size.width(),
- static_cast<float>(src_subrect.y()) / src_size.height(),
- static_cast<float>(src_subrect.width()) / src_size.width(),
- static_cast<float>(src_subrect.height()) / src_size.height(),
- };
- if (flip_y) {
- src_subrect_texcoord[1] += src_subrect_texcoord[3];
- src_subrect_texcoord[3] *= -1.0;
- }
- gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord);
-
- gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height());
- gl_->Uniform2f(dst_pixelsize_location_, static_cast<float>(dst_size.width()),
- static_cast<float>(dst_size.height()));
-
- gl_->Uniform2f(scaling_vector_location_, scale_x ? 1.0 : 0.0,
- scale_x ? 0.0 : 1.0);
- gl_->Uniform4fv(color_weights_location_, 1, color_weights);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/gl_helper_scaling.h b/chromium/components/viz/display_compositor/gl_helper_scaling.h
deleted file mode 100644
index cc90617d677..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper_scaling.h
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_SCALING_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_SCALING_H_
-
-#include <deque>
-#include <map>
-#include <vector>
-
-#include "base/macros.h"
-#include "components/viz/display_compositor/gl_helper.h"
-#include "components/viz/viz_export.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-
-class ShaderProgram;
-class ScalerImpl;
-class GLHelperTest;
-
-// Implements GPU texture scaling methods.
-// Note that you should probably not use this class directly.
-// See gl_helper.cc::CreateScaler instead.
-class VIZ_EXPORT GLHelperScaling {
- public:
- enum ShaderType {
- SHADER_BILINEAR,
- SHADER_BILINEAR2,
- SHADER_BILINEAR3,
- SHADER_BILINEAR4,
- SHADER_BILINEAR2X2,
- SHADER_BICUBIC_UPSCALE,
- SHADER_BICUBIC_HALF_1D,
- SHADER_PLANAR,
- SHADER_YUV_MRT_PASS1,
- SHADER_YUV_MRT_PASS2,
- };
-
- // Similar to ScalerInterface, but can generate multiple outputs.
- // Used for YUV conversion in gl_helper.c
- class VIZ_EXPORT ShaderInterface {
- public:
- ShaderInterface() {}
- virtual ~ShaderInterface() {}
- // Note that the src_texture will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- virtual void Execute(GLuint source_texture,
- const std::vector<GLuint>& dest_textures) = 0;
- };
-
- typedef std::pair<ShaderType, bool> ShaderProgramKeyType;
-
- GLHelperScaling(gpu::gles2::GLES2Interface* gl, GLHelper* helper);
- ~GLHelperScaling();
- void InitBuffer();
-
- GLHelper::ScalerInterface* CreateScaler(GLHelper::ScalerQuality quality,
- gfx::Size src_size,
- gfx::Rect src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle);
-
- GLHelper::ScalerInterface* CreatePlanarScaler(const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- const float color_weights[4]);
-
- ShaderInterface* CreateYuvMrtShader(const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- ShaderType shader);
-
- private:
- // A ScaleOp represents a pass in a scaler pipeline, in one dimension.
- // Note that when quality is GOOD, multiple scaler passes will be
- // combined into one operation for increased performance.
- // Exposed in the header file for testing purposes.
- struct ScaleOp {
- ScaleOp(int factor, bool x, int size)
- : scale_factor(factor), scale_x(x), scale_size(size) {}
-
- // Calculate a set of ScaleOp needed to convert an image of size
- // |src| into an image of size |dst|. If |scale_x| is true, then
- // the calculations are for the X axis of the image, otherwise Y.
- // If |allow3| is true, we can use a SHADER_BILINEAR3 to replace
- // a scale up and scale down with a 3-tap bilinear scale.
- // The calculated ScaleOps are added to |ops|.
- static void AddOps(int src,
- int dst,
- bool scale_x,
- bool allow3,
- std::deque<ScaleOp>* ops) {
- int num_downscales = 0;
- if (allow3 && dst * 3 >= src && dst * 2 < src) {
- // Technically, this should be a scale up and then a
- // scale down, but it makes the optimization code more
- // complicated.
- ops->push_back(ScaleOp(3, scale_x, dst));
- return;
- }
- while ((dst << num_downscales) < src) {
- num_downscales++;
- }
- if ((dst << num_downscales) != src) {
- ops->push_back(ScaleOp(0, scale_x, dst << num_downscales));
- }
- while (num_downscales) {
- num_downscales--;
- ops->push_back(ScaleOp(2, scale_x, dst << num_downscales));
- }
- }
-
- // Update |size| to its new size. Before calling this function
- // |size| should be the size of the input image. After calling it,
- // |size| will be the size of the image after this particular
- // scaling operation.
- void UpdateSize(gfx::Size* subrect) {
- if (scale_x) {
- subrect->set_width(scale_size);
- } else {
- subrect->set_height(scale_size);
- }
- }
-
- // A scale factor of 0 means upscale
- // 2 means 50% scale
- // 3 means 33% scale, etc.
- int scale_factor;
- bool scale_x; // Otherwise y
- int scale_size; // Size to scale to.
- };
-
- // Full specification for a single scaling stage.
- struct ScalerStage {
- ScalerStage(ShaderType shader_,
- gfx::Size src_size_,
- gfx::Rect src_subrect_,
- gfx::Size dst_size_,
- bool scale_x_,
- bool vertically_flip_texture_,
- bool swizzle_);
- ScalerStage(const ScalerStage& other);
- ShaderType shader;
- gfx::Size src_size;
- gfx::Rect src_subrect;
- gfx::Size dst_size;
- bool scale_x;
- bool vertically_flip_texture;
- bool swizzle;
- };
-
- // Compute a vector of scaler stages for a particular
- // set of input/output parameters.
- void ComputeScalerStages(GLHelper::ScalerQuality quality,
- const gfx::Size& src_size,
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- std::vector<ScalerStage>* scaler_stages);
-
- // Take two queues of ScaleOp structs and generate a
- // vector of scaler stages. This is the second half of
- // ComputeScalerStages.
- void ConvertScalerOpsToScalerStages(
- GLHelper::ScalerQuality quality,
- gfx::Size src_size,
- gfx::Rect src_subrect,
- const gfx::Size& dst_size,
- bool vertically_flip_texture,
- bool swizzle,
- std::deque<GLHelperScaling::ScaleOp>* x_ops,
- std::deque<GLHelperScaling::ScaleOp>* y_ops,
- std::vector<ScalerStage>* scaler_stages);
-
- scoped_refptr<ShaderProgram> GetShaderProgram(ShaderType type, bool swizzle);
-
- // Interleaved array of 2-dimentional vertex positions (x, y) and
- // 2-dimentional texture coordinates (s, t).
- static const GLfloat kVertexAttributes[];
-
- gpu::gles2::GLES2Interface* gl_;
- GLHelper* helper_;
-
- // The buffer that holds the vertices and the texture coordinates data for
- // drawing a quad.
- ScopedBuffer vertex_attributes_buffer_;
-
- std::map<ShaderProgramKeyType, scoped_refptr<ShaderProgram>> shader_programs_;
-
- friend class ShaderProgram;
- friend class ScalerImpl;
- friend class GLHelperBenchmark;
- friend class GLHelperTest;
- DISALLOW_COPY_AND_ASSIGN(GLHelperScaling);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_GL_HELPER_SCALING_H_
diff --git a/chromium/components/viz/display_compositor/gl_helper_unittest.cc b/chromium/components/viz/display_compositor/gl_helper_unittest.cc
deleted file mode 100644
index 6f5383f3c50..00000000000
--- a/chromium/components/viz/display_compositor/gl_helper_unittest.cc
+++ /dev/null
@@ -1,1430 +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 <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <cmath>
-#include <string>
-#include <vector>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES2/gl2extchromium.h>
-
-#include "base/at_exit.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/json/json_reader.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
-#include "components/viz/display_compositor/gl_helper.h"
-#include "components/viz/display_compositor/gl_helper_readback_support.h"
-#include "components/viz/display_compositor/gl_helper_scaling.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "gpu/command_buffer/client/shared_memory_limits.h"
-#include "gpu/ipc/gl_in_process_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkTypes.h"
-#include "ui/gl/gl_implementation.h"
-
-namespace viz {
-
-GLHelper::ScalerQuality kQualities[] = {
- GLHelper::SCALER_QUALITY_BEST, GLHelper::SCALER_QUALITY_GOOD,
- GLHelper::SCALER_QUALITY_FAST,
-};
-
-const char* kQualityNames[] = {
- "best", "good", "fast",
-};
-
-class GLHelperTest : public testing::Test {
- protected:
- void SetUp() override {
- gpu::gles2::ContextCreationAttribHelper attributes;
- attributes.alpha_size = 8;
- attributes.depth_size = 24;
- attributes.red_size = 8;
- attributes.green_size = 8;
- attributes.blue_size = 8;
- attributes.stencil_size = 8;
- attributes.samples = 4;
- attributes.sample_buffers = 1;
- attributes.bind_generates_resource = false;
-
- context_.reset(
- gpu::GLInProcessContext::Create(nullptr, /* service */
- nullptr, /* surface */
- true, /* offscreen */
- gpu::kNullSurfaceHandle, /* window */
- nullptr, /* share_context */
- attributes, gpu::SharedMemoryLimits(),
- nullptr, /* gpu_memory_buffer_manager */
- nullptr, /* image_factory */
- base::ThreadTaskRunnerHandle::Get()));
- gl_ = context_->GetImplementation();
- gpu::ContextSupport* support = context_->GetImplementation();
-
- helper_.reset(new GLHelper(gl_, support));
- helper_scaling_.reset(new GLHelperScaling(gl_, helper_.get()));
- }
-
- void TearDown() override {
- helper_scaling_.reset(nullptr);
- helper_.reset(nullptr);
- context_.reset(nullptr);
- }
-
- // Bicubic filter kernel function.
- static float Bicubic(float x) {
- const float a = -0.5;
- x = std::abs(x);
- float x2 = x * x;
- float x3 = x2 * x;
- if (x <= 1) {
- return (a + 2) * x3 - (a + 3) * x2 + 1;
- } else if (x < 2) {
- return a * x3 - 5 * a * x2 + 8 * a * x - 4 * a;
- } else {
- return 0.0f;
- }
- }
-
- // Look up a single channel value. Works for 4-channel and single channel
- // bitmaps. Clamp x/y.
- int Channel(SkBitmap* pixels, int x, int y, int c) {
- if (pixels->bytesPerPixel() == 4) {
- uint32_t* data =
- pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
- std::max(0, std::min(y, pixels->height() - 1)));
- return (*data) >> (c * 8) & 0xff;
- } else {
- DCHECK_EQ(pixels->bytesPerPixel(), 1);
- DCHECK_EQ(c, 0);
- return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)),
- std::max(0, std::min(y, pixels->height() - 1)));
- }
- }
-
- // Set a single channel value. Works for 4-channel and single channel
- // bitmaps. Clamp x/y.
- void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
- DCHECK_GE(x, 0);
- DCHECK_GE(y, 0);
- DCHECK_LT(x, pixels->width());
- DCHECK_LT(y, pixels->height());
- if (pixels->bytesPerPixel() == 4) {
- uint32_t* data = pixels->getAddr32(x, y);
- v = std::max(0, std::min(v, 255));
- *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
- } else {
- DCHECK_EQ(pixels->bytesPerPixel(), 1);
- DCHECK_EQ(c, 0);
- uint8_t* data = pixels->getAddr8(x, y);
- v = std::max(0, std::min(v, 255));
- *data = v;
- }
- }
-
- // Print all the R, G, B or A values from an SkBitmap in a
- // human-readable format.
- void PrintChannel(SkBitmap* pixels, int c) {
- for (int y = 0; y < pixels->height(); y++) {
- std::string formatted;
- for (int x = 0; x < pixels->width(); x++) {
- formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c)));
- }
- LOG(ERROR) << formatted;
- }
- }
-
- // Print out the individual steps of a scaler pipeline.
- std::string PrintStages(
- const std::vector<GLHelperScaling::ScalerStage>& scaler_stages) {
- std::string ret;
- for (size_t i = 0; i < scaler_stages.size(); i++) {
- ret.append(base::StringPrintf(
- "%dx%d -> %dx%d ", scaler_stages[i].src_size.width(),
- scaler_stages[i].src_size.height(), scaler_stages[i].dst_size.width(),
- scaler_stages[i].dst_size.height()));
- bool xy_matters = false;
- switch (scaler_stages[i].shader) {
- case GLHelperScaling::SHADER_BILINEAR:
- ret.append("bilinear");
- break;
- case GLHelperScaling::SHADER_BILINEAR2:
- ret.append("bilinear2");
- xy_matters = true;
- break;
- case GLHelperScaling::SHADER_BILINEAR3:
- ret.append("bilinear3");
- xy_matters = true;
- break;
- case GLHelperScaling::SHADER_BILINEAR4:
- ret.append("bilinear4");
- xy_matters = true;
- break;
- case GLHelperScaling::SHADER_BILINEAR2X2:
- ret.append("bilinear2x2");
- break;
- case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
- ret.append("bicubic upscale");
- xy_matters = true;
- break;
- case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
- ret.append("bicubic 1/2");
- xy_matters = true;
- break;
- case GLHelperScaling::SHADER_PLANAR:
- ret.append("planar");
- break;
- case GLHelperScaling::SHADER_YUV_MRT_PASS1:
- ret.append("rgb2yuv pass 1");
- break;
- case GLHelperScaling::SHADER_YUV_MRT_PASS2:
- ret.append("rgb2yuv pass 2");
- break;
- }
-
- if (xy_matters) {
- if (scaler_stages[i].scale_x) {
- ret.append(" X");
- } else {
- ret.append(" Y");
- }
- }
- ret.append("\n");
- }
- return ret;
- }
-
- bool CheckScale(double scale, int samples, bool already_scaled) {
- // 1:1 is valid if there is one sample.
- if (samples == 1 && scale == 1.0) {
- return true;
- }
- // Is it an exact down-scale (50%, 25%, etc.?)
- if (scale == 2.0 * samples) {
- return true;
- }
- // Upscales, only valid if we haven't already scaled in this dimension.
- if (!already_scaled) {
- // Is it a valid bilinear upscale?
- if (samples == 1 && scale <= 1.0) {
- return true;
- }
- // Multi-sample upscale-downscale combination?
- if (scale > samples / 2.0 && scale < samples) {
- return true;
- }
- }
- return false;
- }
-
- // Make sure that the stages of the scaler pipeline are sane.
- void ValidateScalerStages(
- GLHelper::ScalerQuality quality,
- const std::vector<GLHelperScaling::ScalerStage>& scaler_stages,
- const gfx::Size& dst_size,
- const std::string& message) {
- bool previous_error = HasFailure();
- // First, check that the input size for each stage is equal to
- // the output size of the previous stage.
- for (size_t i = 1; i < scaler_stages.size(); i++) {
- EXPECT_EQ(scaler_stages[i - 1].dst_size.width(),
- scaler_stages[i].src_size.width());
- EXPECT_EQ(scaler_stages[i - 1].dst_size.height(),
- scaler_stages[i].src_size.height());
- EXPECT_EQ(scaler_stages[i].src_subrect.x(), 0);
- EXPECT_EQ(scaler_stages[i].src_subrect.y(), 0);
- EXPECT_EQ(scaler_stages[i].src_subrect.width(),
- scaler_stages[i].src_size.width());
- EXPECT_EQ(scaler_stages[i].src_subrect.height(),
- scaler_stages[i].src_size.height());
- }
-
- // Check the output size matches the destination of the last stage
- EXPECT_EQ(scaler_stages.back().dst_size.width(), dst_size.width());
- EXPECT_EQ(scaler_stages.back().dst_size.height(), dst_size.height());
-
- // Used to verify that up-scales are not attempted after some
- // other scale.
- bool scaled_x = false;
- bool scaled_y = false;
-
- for (size_t i = 0; i < scaler_stages.size(); i++) {
- // Note: 2.0 means scaling down by 50%
- double x_scale =
- static_cast<double>(scaler_stages[i].src_subrect.width()) /
- static_cast<double>(scaler_stages[i].dst_size.width());
- double y_scale =
- static_cast<double>(scaler_stages[i].src_subrect.height()) /
- static_cast<double>(scaler_stages[i].dst_size.height());
-
- int x_samples = 0;
- int y_samples = 0;
-
- // Codify valid scale operations.
- switch (scaler_stages[i].shader) {
- case GLHelperScaling::SHADER_PLANAR:
- case GLHelperScaling::SHADER_YUV_MRT_PASS1:
- case GLHelperScaling::SHADER_YUV_MRT_PASS2:
- EXPECT_TRUE(false) << "Invalid shader.";
- break;
-
- case GLHelperScaling::SHADER_BILINEAR:
- if (quality != GLHelper::SCALER_QUALITY_FAST) {
- x_samples = 1;
- y_samples = 1;
- }
- break;
- case GLHelperScaling::SHADER_BILINEAR2:
- x_samples = 2;
- y_samples = 1;
- break;
- case GLHelperScaling::SHADER_BILINEAR3:
- x_samples = 3;
- y_samples = 1;
- break;
- case GLHelperScaling::SHADER_BILINEAR4:
- x_samples = 4;
- y_samples = 1;
- break;
- case GLHelperScaling::SHADER_BILINEAR2X2:
- x_samples = 2;
- y_samples = 2;
- break;
- case GLHelperScaling::SHADER_BICUBIC_UPSCALE:
- if (scaler_stages[i].scale_x) {
- EXPECT_LT(x_scale, 1.0);
- EXPECT_EQ(y_scale, 1.0);
- } else {
- EXPECT_EQ(x_scale, 1.0);
- EXPECT_LT(y_scale, 1.0);
- }
- break;
- case GLHelperScaling::SHADER_BICUBIC_HALF_1D:
- if (scaler_stages[i].scale_x) {
- EXPECT_EQ(x_scale, 2.0);
- EXPECT_EQ(y_scale, 1.0);
- } else {
- EXPECT_EQ(x_scale, 1.0);
- EXPECT_EQ(y_scale, 2.0);
- }
- break;
- }
-
- if (!scaler_stages[i].scale_x) {
- std::swap(x_samples, y_samples);
- }
-
- if (x_samples) {
- EXPECT_TRUE(CheckScale(x_scale, x_samples, scaled_x))
- << "x_scale = " << x_scale;
- }
- if (y_samples) {
- EXPECT_TRUE(CheckScale(y_scale, y_samples, scaled_y))
- << "y_scale = " << y_scale;
- }
-
- if (x_scale != 1.0) {
- scaled_x = true;
- }
- if (y_scale != 1.0) {
- scaled_y = true;
- }
- }
-
- if (HasFailure() && !previous_error) {
- LOG(ERROR) << "Invalid scaler stages: " << message;
- LOG(ERROR) << "Scaler stages:";
- LOG(ERROR) << PrintStages(scaler_stages);
- }
- }
-
- // Compares two bitmaps taking color types into account. Checks whether each
- // component of each pixel is no more than |maxdiff| apart. If bitmaps are not
- // similar enough, prints out |truth|, |other|, |source|, |scaler_stages|
- // and |message|.
- void Compare(SkBitmap* truth,
- SkBitmap* other,
- int maxdiff,
- SkBitmap* source,
- const std::vector<GLHelperScaling::ScalerStage>& scaler_stages,
- std::string message) {
- EXPECT_EQ(truth->width(), other->width());
- EXPECT_EQ(truth->height(), other->height());
- bool swizzle = (truth->colorType() == kRGBA_8888_SkColorType &&
- other->colorType() == kBGRA_8888_SkColorType) ||
- (truth->colorType() == kBGRA_8888_SkColorType &&
- other->colorType() == kRGBA_8888_SkColorType);
- EXPECT_TRUE(swizzle || truth->colorType() == other->colorType());
- int bpp = truth->bytesPerPixel();
- for (int x = 0; x < truth->width(); x++) {
- for (int y = 0; y < truth->height(); y++) {
- for (int c = 0; c < bpp; c++) {
- int a = Channel(truth, x, y, c);
- // swizzle when comparing if needed
- int b = swizzle && (c == 0 || c == 2)
- ? Channel(other, x, y, (c + 2) & 2)
- : Channel(other, x, y, c);
- EXPECT_NEAR(a, b, maxdiff)
- << " x=" << x << " y=" << y << " c=" << c << " " << message;
- if (std::abs(a - b) > maxdiff) {
- LOG(ERROR) << "-------expected--------";
- for (int i = 0; i < bpp; i++) {
- LOG(ERROR) << "Channel " << i << ":";
- PrintChannel(truth, i);
- }
- LOG(ERROR) << "-------actual--------";
- for (int i = 0; i < bpp; i++) {
- LOG(ERROR) << "Channel " << i << ":";
- PrintChannel(other, i);
- }
- if (source) {
- LOG(ERROR) << "-------original--------";
- for (int i = 0; i < source->bytesPerPixel(); i++) {
- LOG(ERROR) << "Channel " << i << ":";
- PrintChannel(source, i);
- }
- }
- LOG(ERROR) << "-----Scaler stages------";
- LOG(ERROR) << PrintStages(scaler_stages);
- return;
- }
- }
- }
- }
- }
-
- // Get a single R, G, B or A value as a float.
- float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) {
- return Channel(pixels, x, y, c) / 255.0;
- }
-
- // Works like a GL_LINEAR lookup on an SkBitmap.
- float Bilinear(SkBitmap* pixels, float x, float y, int c) {
- x -= 0.5;
- y -= 0.5;
- int base_x = static_cast<int>(floorf(x));
- int base_y = static_cast<int>(floorf(y));
- x -= base_x;
- y -= base_y;
- return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) +
- ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) +
- ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y +
- ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
- }
-
- // Encodes an RGBA bitmap to grayscale.
- // Reference implementation for
- // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale.
- void EncodeToGrayscaleSlow(SkBitmap* input, SkBitmap* output) {
- const float kRGBtoGrayscaleColorWeights[3] = {0.213f, 0.715f, 0.072f};
- CHECK_EQ(kAlpha_8_SkColorType, output->colorType());
- CHECK_EQ(input->width(), output->width());
- CHECK_EQ(input->height(), output->height());
- CHECK_EQ(input->colorType(), kRGBA_8888_SkColorType);
-
- for (int dst_y = 0; dst_y < output->height(); dst_y++) {
- for (int dst_x = 0; dst_x < output->width(); dst_x++) {
- float c0 = ChannelAsFloat(input, dst_x, dst_y, 0);
- float c1 = ChannelAsFloat(input, dst_x, dst_y, 1);
- float c2 = ChannelAsFloat(input, dst_x, dst_y, 2);
- float value = c0 * kRGBtoGrayscaleColorWeights[0] +
- c1 * kRGBtoGrayscaleColorWeights[1] +
- c2 * kRGBtoGrayscaleColorWeights[2];
- SetChannel(output, dst_x, dst_y, 0,
- static_cast<int>(value * 255.0f + 0.5f));
- }
- }
- }
-
- // Very slow bicubic / bilinear scaler for reference.
- void ScaleSlow(SkBitmap* input,
- SkBitmap* output,
- GLHelper::ScalerQuality quality) {
- float xscale = static_cast<float>(input->width()) / output->width();
- float yscale = static_cast<float>(input->height()) / output->height();
- float clamped_xscale = xscale < 1.0 ? 1.0 : 1.0 / xscale;
- float clamped_yscale = yscale < 1.0 ? 1.0 : 1.0 / yscale;
- for (int dst_y = 0; dst_y < output->height(); dst_y++) {
- for (int dst_x = 0; dst_x < output->width(); dst_x++) {
- for (int channel = 0; channel < 4; channel++) {
- float dst_x_in_src = (dst_x + 0.5f) * xscale;
- float dst_y_in_src = (dst_y + 0.5f) * yscale;
-
- float value = 0.0f;
- float sum = 0.0f;
- switch (quality) {
- case GLHelper::SCALER_QUALITY_BEST:
- for (int src_y = -10; src_y < input->height() + 10; ++src_y) {
- float coeff_y =
- Bicubic((src_y + 0.5f - dst_y_in_src) * clamped_yscale);
- if (coeff_y == 0.0f) {
- continue;
- }
- for (int src_x = -10; src_x < input->width() + 10; ++src_x) {
- float coeff =
- coeff_y *
- Bicubic((src_x + 0.5f - dst_x_in_src) * clamped_xscale);
- if (coeff == 0.0f) {
- continue;
- }
- sum += coeff;
- float c = ChannelAsFloat(input, src_x, src_y, channel);
- value += c * coeff;
- }
- }
- break;
-
- case GLHelper::SCALER_QUALITY_GOOD: {
- int xshift = 0, yshift = 0;
- while ((output->width() << xshift) < input->width()) {
- xshift++;
- }
- while ((output->height() << yshift) < input->height()) {
- yshift++;
- }
- int xmag = 1 << xshift;
- int ymag = 1 << yshift;
- if (xmag == 4 && output->width() * 3 >= input->width()) {
- xmag = 3;
- }
- if (ymag == 4 && output->height() * 3 >= input->height()) {
- ymag = 3;
- }
- for (int x = 0; x < xmag; x++) {
- for (int y = 0; y < ymag; y++) {
- value += Bilinear(
- input, (dst_x * xmag + x + 0.5) * xscale / xmag,
- (dst_y * ymag + y + 0.5) * yscale / ymag, channel);
- sum += 1.0;
- }
- }
- break;
- }
-
- case GLHelper::SCALER_QUALITY_FAST:
- value = Bilinear(input, dst_x_in_src, dst_y_in_src, channel);
- sum = 1.0;
- }
- value /= sum;
- SetChannel(output, dst_x, dst_y, channel,
- static_cast<int>(value * 255.0f + 0.5f));
- }
- }
- }
- }
-
- void FlipSKBitmap(SkBitmap* bitmap) {
- int bpp = bitmap->bytesPerPixel();
- DCHECK(bpp == 4 || bpp == 1);
- int top_line = 0;
- int bottom_line = bitmap->height() - 1;
- while (top_line < bottom_line) {
- for (int x = 0; x < bitmap->width(); x++) {
- bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line),
- *bitmap->getAddr32(x, bottom_line))
- : std::swap(*bitmap->getAddr8(x, top_line),
- *bitmap->getAddr8(x, bottom_line));
- }
- top_line++;
- bottom_line--;
- }
- }
-
- // Swaps red and blue channels in each pixel in a 32-bit bitmap.
- void SwizzleSKBitmap(SkBitmap* bitmap) {
- int bpp = bitmap->bytesPerPixel();
- DCHECK_EQ(bpp, 4);
- for (int y = 0; y < bitmap->height(); y++) {
- for (int x = 0; x < bitmap->width(); x++) {
- // Swap channels 0 and 2 (red and blue)
- int c0 = Channel(bitmap, x, y, 0);
- int c2 = Channel(bitmap, x, y, 2);
- SetChannel(bitmap, x, y, 2, c0);
- SetChannel(bitmap, x, y, 0, c2);
- }
- }
- }
-
- // gl_helper scales recursively, so we'll need to do that
- // in the reference implementation too.
- void ScaleSlowRecursive(SkBitmap* input,
- SkBitmap* output,
- GLHelper::ScalerQuality quality) {
- if (quality == GLHelper::SCALER_QUALITY_FAST ||
- quality == GLHelper::SCALER_QUALITY_GOOD) {
- ScaleSlow(input, output, quality);
- return;
- }
-
- float xscale = static_cast<float>(output->width()) / input->width();
-
- // This corresponds to all the operations we can do directly.
- float yscale = static_cast<float>(output->height()) / input->height();
- if ((xscale == 1.0f && yscale == 1.0f) ||
- (xscale == 0.5f && yscale == 1.0f) ||
- (xscale == 1.0f && yscale == 0.5f) ||
- (xscale >= 1.0f && yscale == 1.0f) ||
- (xscale == 1.0f && yscale >= 1.0f)) {
- ScaleSlow(input, output, quality);
- return;
- }
-
- // Now we break the problem down into smaller pieces, using the
- // operations available.
- int xtmp = input->width();
- int ytmp = input->height();
-
- if (output->height() != input->height()) {
- ytmp = output->height();
- while (ytmp < input->height() && ytmp * 2 != input->height()) {
- ytmp += ytmp;
- }
- } else {
- xtmp = output->width();
- while (xtmp < input->width() && xtmp * 2 != input->width()) {
- xtmp += xtmp;
- }
- }
-
- SkBitmap tmp;
- tmp.allocN32Pixels(xtmp, ytmp);
-
- ScaleSlowRecursive(input, &tmp, quality);
- ScaleSlowRecursive(&tmp, output, quality);
- }
-
- // Creates an RGBA SkBitmap
- std::unique_ptr<SkBitmap> CreateTestBitmap(int width,
- int height,
- int test_pattern) {
- std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
- bitmap->allocPixels(SkImageInfo::Make(width, height, kRGBA_8888_SkColorType,
- kPremul_SkAlphaType));
-
- for (int x = 0; x < width; ++x) {
- for (int y = 0; y < height; ++y) {
- switch (test_pattern) {
- case 0: // Smooth test pattern
- SetChannel(bitmap.get(), x, y, 0, x * 10);
- SetChannel(bitmap.get(), x, y, 0, y == 0 ? x * 50 : x * 10);
- SetChannel(bitmap.get(), x, y, 1, y * 10);
- SetChannel(bitmap.get(), x, y, 2, (x + y) * 10);
- SetChannel(bitmap.get(), x, y, 3, 255);
- break;
- case 1: // Small blocks
- SetChannel(bitmap.get(), x, y, 0, x & 1 ? 255 : 0);
- SetChannel(bitmap.get(), x, y, 1, y & 1 ? 255 : 0);
- SetChannel(bitmap.get(), x, y, 2, (x + y) & 1 ? 255 : 0);
- SetChannel(bitmap.get(), x, y, 3, 255);
- break;
- case 2: // Medium blocks
- SetChannel(bitmap.get(), x, y, 0, 10 + x / 2 * 50);
- SetChannel(bitmap.get(), x, y, 1, 10 + y / 3 * 50);
- SetChannel(bitmap.get(), x, y, 2, (x + y) / 5 * 50 + 5);
- SetChannel(bitmap.get(), x, y, 3, 255);
- break;
- }
- }
- }
- return bitmap;
- }
-
- // Binds texture and framebuffer and loads the bitmap pixels into the texture.
- void BindTextureAndFrameBuffer(GLuint texture,
- GLuint framebuffer,
- SkBitmap* bitmap,
- int width,
- int height) {
- gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- gl_->BindTexture(GL_TEXTURE_2D, texture);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
- GL_UNSIGNED_BYTE, bitmap->getPixels());
- }
-
- // Create a test image, transform it using
- // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation
- // and compare the results.
- void TestCropScaleReadbackAndCleanTexture(int xsize,
- int ysize,
- int scaled_xsize,
- int scaled_ysize,
- int test_pattern,
- SkColorType out_color_type,
- bool swizzle,
- size_t quality_index) {
- DCHECK(out_color_type == kAlpha_8_SkColorType ||
- out_color_type == kRGBA_8888_SkColorType ||
- out_color_type == kBGRA_8888_SkColorType);
- GLuint src_texture;
- gl_->GenTextures(1, &src_texture);
- GLuint framebuffer;
- gl_->GenFramebuffers(1, &framebuffer);
- std::unique_ptr<SkBitmap> input_pixels =
- CreateTestBitmap(xsize, ysize, test_pattern);
- BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(),
- xsize, ysize);
-
- std::string message = base::StringPrintf(
- "input size: %dx%d "
- "output size: %dx%d "
- "pattern: %d , quality: %s, "
- "out_color_type: %d",
- xsize, ysize, scaled_xsize, scaled_ysize, test_pattern,
- kQualityNames[quality_index], out_color_type);
-
- // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture.
- SkBitmap output_pixels;
- output_pixels.allocPixels(SkImageInfo::Make(
- scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
- base::RunLoop run_loop;
- gfx::Size encoded_texture_size;
- helper_->CropScaleReadbackAndCleanTexture(
- src_texture, gfx::Size(xsize, ysize), gfx::Rect(xsize, ysize),
- gfx::Size(scaled_xsize, scaled_ysize),
- static_cast<unsigned char*>(output_pixels.getPixels()), out_color_type,
- base::Bind(&callcallback, run_loop.QuitClosure()),
- kQualities[quality_index]);
- run_loop.Run();
- // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back.
- FlipSKBitmap(&output_pixels);
-
- // If the bitmap shouldn't have changed - compare against input.
- if (xsize == scaled_xsize && ysize == scaled_ysize &&
- out_color_type != kAlpha_8_SkColorType) {
- const std::vector<GLHelperScaling::ScalerStage> dummy_stages;
- Compare(input_pixels.get(), &output_pixels, 0, nullptr, dummy_stages,
- message + " comparing against input");
- return;
- }
-
- // Now transform the bitmap using the reference implementation.
- SkBitmap scaled_pixels;
- scaled_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize,
- kRGBA_8888_SkColorType,
- kPremul_SkAlphaType));
- SkBitmap truth_pixels;
- // Step 1: Scale
- ScaleSlowRecursive(input_pixels.get(), &scaled_pixels,
- kQualities[quality_index]);
- // Step 2: Encode to grayscale if needed.
- if (out_color_type == kAlpha_8_SkColorType) {
- truth_pixels.allocPixels(SkImageInfo::Make(
- scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType));
- EncodeToGrayscaleSlow(&scaled_pixels, &truth_pixels);
- } else {
- truth_pixels = scaled_pixels;
- }
-
- // Now compare the results.
- const std::vector<GLHelperScaling::ScalerStage> dummy_stages;
- Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), dummy_stages,
- message + " comparing against transformed/scaled");
-
- gl_->DeleteTextures(1, &src_texture);
- gl_->DeleteFramebuffers(1, &framebuffer);
- }
-
- // Scaling test: Create a test image, scale it using GLHelperScaling
- // and a reference implementation and compare the results.
- void TestScale(int xsize,
- int ysize,
- int scaled_xsize,
- int scaled_ysize,
- int test_pattern,
- size_t quality_index,
- bool flip) {
- GLuint src_texture;
- gl_->GenTextures(1, &src_texture);
- GLuint framebuffer;
- gl_->GenFramebuffers(1, &framebuffer);
- std::unique_ptr<SkBitmap> input_pixels =
- CreateTestBitmap(xsize, ysize, test_pattern);
- BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(),
- xsize, ysize);
-
- std::string message = base::StringPrintf(
- "input size: %dx%d "
- "output size: %dx%d "
- "pattern: %d quality: %s",
- xsize, ysize, scaled_xsize, scaled_ysize, test_pattern,
- kQualityNames[quality_index]);
-
- std::vector<GLHelperScaling::ScalerStage> stages;
- helper_scaling_->ComputeScalerStages(
- kQualities[quality_index], gfx::Size(xsize, ysize),
- gfx::Rect(0, 0, xsize, ysize), gfx::Size(scaled_xsize, scaled_ysize),
- flip, false, &stages);
- ValidateScalerStages(kQualities[quality_index], stages,
- gfx::Size(scaled_xsize, scaled_ysize), message);
-
- GLuint dst_texture = helper_->CopyAndScaleTexture(
- src_texture, gfx::Size(xsize, ysize),
- gfx::Size(scaled_xsize, scaled_ysize), flip, kQualities[quality_index]);
-
- SkBitmap output_pixels;
- output_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize,
- kRGBA_8888_SkColorType,
- kPremul_SkAlphaType));
-
- helper_->ReadbackTextureSync(
- dst_texture, gfx::Rect(0, 0, scaled_xsize, scaled_ysize),
- static_cast<unsigned char*>(output_pixels.getPixels()),
- kRGBA_8888_SkColorType);
- if (flip) {
- // Flip the pixels back.
- FlipSKBitmap(&output_pixels);
- }
-
- // If the bitmap shouldn't have changed - compare against input.
- if (xsize == scaled_xsize && ysize == scaled_ysize) {
- Compare(input_pixels.get(), &output_pixels, 0, nullptr, stages,
- message + " comparing against input");
- return;
- }
-
- // Now scale the bitmap using the reference implementation.
- SkBitmap truth_pixels;
- truth_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize,
- kRGBA_8888_SkColorType,
- kPremul_SkAlphaType));
- ScaleSlowRecursive(input_pixels.get(), &truth_pixels,
- kQualities[quality_index]);
- Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), stages,
- message + " comparing against scaled");
-
- gl_->DeleteTextures(1, &src_texture);
- gl_->DeleteTextures(1, &dst_texture);
- gl_->DeleteFramebuffers(1, &framebuffer);
- }
-
- // Create a scaling pipeline and check that it is made up of
- // valid scaling operations.
- void TestScalerPipeline(size_t quality,
- int xsize,
- int ysize,
- int dst_xsize,
- int dst_ysize) {
- std::vector<GLHelperScaling::ScalerStage> stages;
- helper_scaling_->ComputeScalerStages(
- kQualities[quality], gfx::Size(xsize, ysize),
- gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false,
- false, &stages);
- ValidateScalerStages(kQualities[quality], stages,
- gfx::Size(dst_xsize, dst_ysize),
- base::StringPrintf("input size: %dx%d "
- "output size: %dx%d "
- "quality: %s",
- xsize, ysize, dst_xsize, dst_ysize,
- kQualityNames[quality]));
- }
-
- // Create a scaling pipeline and make sure that the steps
- // are exactly the steps we expect.
- void CheckPipeline(GLHelper::ScalerQuality quality,
- int xsize,
- int ysize,
- int dst_xsize,
- int dst_ysize,
- const std::string& description) {
- std::vector<GLHelperScaling::ScalerStage> stages;
- helper_scaling_->ComputeScalerStages(
- quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize),
- gfx::Size(dst_xsize, dst_ysize), false, false, &stages);
- ValidateScalerStages(GLHelper::SCALER_QUALITY_GOOD, stages,
- gfx::Size(dst_xsize, dst_ysize), "");
- EXPECT_EQ(PrintStages(stages), description);
- }
-
- static void callcallback(const base::Callback<void()>& callback,
- bool result) {
- callback.Run();
- }
-
- void DrawGridToBitmap(int w,
- int h,
- SkColor background_color,
- SkColor grid_color,
- int grid_pitch,
- int grid_width,
- const SkBitmap& bmp) {
- ASSERT_GT(grid_pitch, 0);
- ASSERT_GT(grid_width, 0);
- ASSERT_NE(background_color, grid_color);
-
- for (int y = 0; y < h; ++y) {
- bool y_on_grid = ((y % grid_pitch) < grid_width);
-
- for (int x = 0; x < w; ++x) {
- bool on_grid = (y_on_grid || ((x % grid_pitch) < grid_width));
-
- if (bmp.colorType() == kRGBA_8888_SkColorType ||
- bmp.colorType() == kBGRA_8888_SkColorType) {
- *bmp.getAddr32(x, y) = (on_grid ? grid_color : background_color);
- } else if (bmp.colorType() == kRGB_565_SkColorType) {
- *bmp.getAddr16(x, y) = (on_grid ? grid_color : background_color);
- }
- }
- }
- }
-
- void DrawCheckerToBitmap(int w,
- int h,
- SkColor color1,
- SkColor color2,
- int rect_w,
- int rect_h,
- const SkBitmap& bmp) {
- ASSERT_GT(rect_w, 0);
- ASSERT_GT(rect_h, 0);
- ASSERT_NE(color1, color2);
-
- for (int y = 0; y < h; ++y) {
- bool y_bit = (((y / rect_h) & 0x1) == 0);
-
- for (int x = 0; x < w; ++x) {
- bool x_bit = (((x / rect_w) & 0x1) == 0);
-
- bool use_color2 = (x_bit != y_bit); // xor
- if (bmp.colorType() == kRGBA_8888_SkColorType ||
- bmp.colorType() == kBGRA_8888_SkColorType) {
- *bmp.getAddr32(x, y) = (use_color2 ? color2 : color1);
- } else if (bmp.colorType() == kRGB_565_SkColorType) {
- *bmp.getAddr16(x, y) = (use_color2 ? color2 : color1);
- }
- }
- }
- }
-
- bool ColorComponentsClose(SkColor component1,
- SkColor component2,
- SkColorType color_type) {
- int c1 = static_cast<int>(component1);
- int c2 = static_cast<int>(component2);
- bool result = false;
- switch (color_type) {
- case kRGBA_8888_SkColorType:
- case kBGRA_8888_SkColorType:
- result = (std::abs(c1 - c2) == 0);
- break;
- case kRGB_565_SkColorType:
- result = (std::abs(c1 - c2) <= 7);
- break;
- default:
- break;
- }
- return result;
- }
-
- bool ColorsClose(SkColor color1, SkColor color2, SkColorType color_type) {
- bool red = ColorComponentsClose(SkColorGetR(color1), SkColorGetR(color2),
- color_type);
- bool green = ColorComponentsClose(SkColorGetG(color1), SkColorGetG(color2),
- color_type);
- bool blue = ColorComponentsClose(SkColorGetB(color1), SkColorGetB(color2),
- color_type);
- bool alpha = ColorComponentsClose(SkColorGetA(color1), SkColorGetA(color2),
- color_type);
- if (color_type == kRGB_565_SkColorType) {
- return red && blue && green;
- }
- return red && blue && green && alpha;
- }
-
- bool IsEqual(const SkBitmap& bmp1, const SkBitmap& bmp2) {
- if (bmp1.isNull() && bmp2.isNull())
- return true;
- if (bmp1.width() != bmp2.width() || bmp1.height() != bmp2.height()) {
- LOG(ERROR) << "Bitmap geometry check failure";
- return false;
- }
- if (bmp1.colorType() != bmp2.colorType())
- return false;
-
- if (!bmp1.getPixels() || !bmp2.getPixels()) {
- LOG(ERROR) << "Empty Bitmap!";
- return false;
- }
- for (int y = 0; y < bmp1.height(); ++y) {
- for (int x = 0; x < bmp1.width(); ++x) {
- if (!ColorsClose(bmp1.getColor(x, y), bmp2.getColor(x, y),
- bmp1.colorType())) {
- LOG(ERROR) << "Bitmap color comparision failure";
- return false;
- }
- }
- }
- return true;
- }
-
- void BindAndAttachTextureWithPixels(GLuint src_texture,
- SkColorType color_type,
- const gfx::Size& src_size,
- const SkBitmap& input_pixels) {
- gl_->BindTexture(GL_TEXTURE_2D, src_texture);
- GLenum format = 0;
- switch (color_type) {
- case kBGRA_8888_SkColorType:
- format = GL_BGRA_EXT;
- break;
- case kRGBA_8888_SkColorType:
- format = GL_RGBA;
- break;
- case kRGB_565_SkColorType:
- format = GL_RGB;
- break;
- default:
- NOTREACHED();
- }
- GLenum type = (color_type == kRGB_565_SkColorType) ? GL_UNSIGNED_SHORT_5_6_5
- : GL_UNSIGNED_BYTE;
- gl_->TexImage2D(GL_TEXTURE_2D, 0, format, src_size.width(),
- src_size.height(), 0, format, type,
- input_pixels.getPixels());
- }
-
- void ReadBackTexture(GLuint src_texture,
- const gfx::Size& src_size,
- unsigned char* pixels,
- SkColorType color_type,
- bool async) {
- if (async) {
- base::RunLoop run_loop;
- helper_->ReadbackTextureAsync(
- src_texture, src_size, pixels, color_type,
- base::Bind(&callcallback, run_loop.QuitClosure()));
- run_loop.Run();
- } else {
- helper_->ReadbackTextureSync(src_texture, gfx::Rect(src_size), pixels,
- color_type);
- }
- }
- // Test basic format readback.
- bool TestTextureFormatReadback(const gfx::Size& src_size,
- SkColorType color_type,
- bool async) {
- SkImageInfo info = SkImageInfo::Make(src_size.width(), src_size.height(),
- color_type, kPremul_SkAlphaType);
- if (!helper_->IsReadbackConfigSupported(color_type)) {
- LOG(INFO) << "Skipping test format not supported" << color_type;
- return true;
- }
- GLuint src_texture;
- gl_->GenTextures(1, &src_texture);
- SkBitmap input_pixels;
- input_pixels.allocPixels(info);
- // Test Pattern-1, Fill with Plain color pattern.
- // Erase the input bitmap with red color.
- input_pixels.eraseColor(SK_ColorRED);
- BindAndAttachTextureWithPixels(src_texture, color_type, src_size,
- input_pixels);
- SkBitmap output_pixels;
- output_pixels.allocPixels(info);
- // Initialize the output bitmap with Green color.
- // When the readback is over output bitmap should have the red color.
- output_pixels.eraseColor(SK_ColorGREEN);
- uint8_t* pixels = static_cast<uint8_t*>(output_pixels.getPixels());
- ReadBackTexture(src_texture, src_size, pixels, color_type, async);
- bool result = IsEqual(input_pixels, output_pixels);
- if (!result) {
- LOG(ERROR) << "Bitmap comparision failure Pattern-1";
- return false;
- }
- const int rect_w = 10, rect_h = 4, src_grid_pitch = 10, src_grid_width = 4;
- const SkColor color1 = SK_ColorRED, color2 = SK_ColorBLUE;
- // Test Pattern-2, Fill with Grid Pattern.
- DrawGridToBitmap(src_size.width(), src_size.height(), color2, color1,
- src_grid_pitch, src_grid_width, input_pixels);
- BindAndAttachTextureWithPixels(src_texture, color_type, src_size,
- input_pixels);
- ReadBackTexture(src_texture, src_size, pixels, color_type, async);
- result = IsEqual(input_pixels, output_pixels);
- if (!result) {
- LOG(ERROR) << "Bitmap comparision failure Pattern-2";
- return false;
- }
- // Test Pattern-3, Fill with CheckerBoard Pattern.
- DrawCheckerToBitmap(src_size.width(), src_size.height(), color1, color2,
- rect_w, rect_h, input_pixels);
- BindAndAttachTextureWithPixels(src_texture, color_type, src_size,
- input_pixels);
- ReadBackTexture(src_texture, src_size, pixels, color_type, async);
- result = IsEqual(input_pixels, output_pixels);
- if (!result) {
- LOG(ERROR) << "Bitmap comparision failure Pattern-3";
- return false;
- }
- gl_->DeleteTextures(1, &src_texture);
- if (HasFailure()) {
- return false;
- }
- return true;
- }
-
- void TestAddOps(int src, int dst, bool scale_x, bool allow3) {
- std::deque<GLHelperScaling::ScaleOp> ops;
- GLHelperScaling::ScaleOp::AddOps(src, dst, scale_x, allow3, &ops);
- // Scale factor 3 is a special case.
- // It is currently only allowed by itself.
- if (allow3 && dst * 3 >= src && dst * 2 < src) {
- EXPECT_EQ(ops[0].scale_factor, 3);
- EXPECT_EQ(ops.size(), 1U);
- EXPECT_EQ(ops[0].scale_x, scale_x);
- EXPECT_EQ(ops[0].scale_size, dst);
- return;
- }
-
- for (size_t i = 0; i < ops.size(); i++) {
- EXPECT_EQ(ops[i].scale_x, scale_x);
- if (i == 0) {
- // Only the first op is allowed to be a scale up.
- // (Scaling up *after* scaling down would make it fuzzy.)
- EXPECT_TRUE(ops[0].scale_factor == 0 || ops[0].scale_factor == 2);
- } else {
- // All other operations must be 50% downscales.
- EXPECT_EQ(ops[i].scale_factor, 2);
- }
- }
- // Check that the scale factors make sense and add up.
- int tmp = dst;
- for (int i = static_cast<int>(ops.size() - 1); i >= 0; i--) {
- EXPECT_EQ(tmp, ops[i].scale_size);
- if (ops[i].scale_factor == 0) {
- EXPECT_EQ(i, 0);
- EXPECT_GT(tmp, src);
- tmp = src;
- } else {
- tmp *= ops[i].scale_factor;
- }
- }
- EXPECT_EQ(tmp, src);
- }
-
- void CheckPipeline2(int xsize,
- int ysize,
- int dst_xsize,
- int dst_ysize,
- const std::string& description) {
- std::vector<GLHelperScaling::ScalerStage> stages;
- helper_scaling_->ConvertScalerOpsToScalerStages(
- GLHelper::SCALER_QUALITY_GOOD, gfx::Size(xsize, ysize),
- gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false,
- false, &x_ops_, &y_ops_, &stages);
- EXPECT_EQ(x_ops_.size(), 0U);
- EXPECT_EQ(y_ops_.size(), 0U);
- ValidateScalerStages(GLHelper::SCALER_QUALITY_GOOD, stages,
- gfx::Size(dst_xsize, dst_ysize), "");
- EXPECT_EQ(PrintStages(stages), description);
- }
-
- void CheckOptimizationsTest() {
- // Basic upscale. X and Y should be combined into one pass.
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
- CheckPipeline2(1024, 768, 2000, 2000, "1024x768 -> 2000x2000 bilinear\n");
-
- // X scaled 1/2, Y upscaled, should still be one pass.
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000));
- CheckPipeline2(1024, 768, 512, 2000, "1024x768 -> 512x2000 bilinear\n");
-
- // X upscaled, Y scaled 1/2, one bilinear pass
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
- CheckPipeline2(1024, 768, 2000, 384, "1024x768 -> 2000x384 bilinear\n");
-
- // X scaled 1/2, Y scaled 1/2, one bilinear pass
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384));
- CheckPipeline2(1024, 768, 512, 384, "1024x768 -> 512x384 bilinear\n");
-
- // X scaled 1/2, Y scaled to 60%, one bilinear2 pass.
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
- CheckPipeline2(100, 100, 50, 60, "100x100 -> 50x60 bilinear2 Y\n");
-
- // X scaled to 60%, Y scaled 1/2, one bilinear2 pass.
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 50));
- CheckPipeline2(100, 100, 60, 50, "100x100 -> 60x50 bilinear2 X\n");
-
- // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass.
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
- CheckPipeline2(100, 100, 60, 60, "100x100 -> 60x60 bilinear2x2\n");
-
- // X scaled to 40%, Y scaled 40%, two bilinear3 passes.
- x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
- y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
- CheckPipeline2(100, 100, 40, 40,
- "100x100 -> 100x40 bilinear3 Y\n"
- "100x40 -> 40x40 bilinear3 X\n");
-
- // X scaled to 60%, Y scaled 40%
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
- y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40));
- CheckPipeline2(100, 100, 60, 40,
- "100x100 -> 100x40 bilinear3 Y\n"
- "100x40 -> 60x40 bilinear2 X\n");
-
- // X scaled to 40%, Y scaled 60%
- x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
- CheckPipeline2(100, 100, 40, 60,
- "100x100 -> 100x60 bilinear2 Y\n"
- "100x60 -> 40x60 bilinear3 X\n");
-
- // X scaled to 30%, Y scaled 30%
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 30));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
- CheckPipeline2(100, 100, 30, 30,
- "100x100 -> 100x30 bilinear4 Y\n"
- "100x30 -> 30x30 bilinear4 X\n");
-
- // X scaled to 50%, Y scaled 30%
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
- CheckPipeline2(100, 100, 50, 30, "100x100 -> 50x30 bilinear4 Y\n");
-
- // X scaled to 150%, Y scaled 30%
- // Note that we avoid combinding X and Y passes
- // as that would probably be LESS efficient here.
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 150));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30));
- CheckPipeline2(100, 100, 150, 30,
- "100x100 -> 100x30 bilinear4 Y\n"
- "100x30 -> 150x30 bilinear\n");
-
- // X scaled to 1%, Y scaled 1%
- x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 128));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 64));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 32));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 16));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 8));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 4));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 2));
- x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 1));
- y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 128));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 64));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 32));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 16));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 8));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 4));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 2));
- y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 1));
- CheckPipeline2(100, 100, 1, 1,
- "100x100 -> 100x32 bilinear4 Y\n"
- "100x32 -> 100x4 bilinear4 Y\n"
- "100x4 -> 64x1 bilinear2x2\n"
- "64x1 -> 8x1 bilinear4 X\n"
- "8x1 -> 1x1 bilinear4 X\n");
- }
-
- std::unique_ptr<gpu::GLInProcessContext> context_;
- gpu::gles2::GLES2Interface* gl_;
- std::unique_ptr<GLHelper> helper_;
- std::unique_ptr<GLHelperScaling> helper_scaling_;
- std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
- base::MessageLoop message_loop_;
-};
-
-class GLHelperPixelTest : public GLHelperTest {
- private:
- gl::DisableNullDrawGLBindings enable_pixel_output_;
-};
-
-TEST_F(GLHelperTest, RGBASyncReadbackTest) {
- const int kTestSize = 64;
- bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
- kRGBA_8888_SkColorType, false);
- EXPECT_EQ(result, true);
-}
-
-TEST_F(GLHelperTest, BGRASyncReadbackTest) {
- const int kTestSize = 64;
- bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
- kBGRA_8888_SkColorType, false);
- EXPECT_EQ(result, true);
-}
-
-TEST_F(GLHelperTest, RGB565SyncReadbackTest) {
- const int kTestSize = 64;
- bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
- kRGB_565_SkColorType, false);
- EXPECT_EQ(result, true);
-}
-
-TEST_F(GLHelperTest, RGBAASyncReadbackTest) {
- const int kTestSize = 64;
- bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
- kRGBA_8888_SkColorType, true);
- EXPECT_EQ(result, true);
-}
-
-TEST_F(GLHelperTest, BGRAASyncReadbackTest) {
- const int kTestSize = 64;
- bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
- kBGRA_8888_SkColorType, true);
- EXPECT_EQ(result, true);
-}
-
-TEST_F(GLHelperTest, RGB565ASyncReadbackTest) {
- const int kTestSize = 64;
- bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize),
- kRGB_565_SkColorType, true);
- EXPECT_EQ(result, true);
-}
-
-int kRGBReadBackSizes[] = {3, 6, 16};
-
-class GLHelperPixelReadbackTest
- : public GLHelperPixelTest,
- public ::testing::WithParamInterface<std::tr1::tuple<unsigned int,
- unsigned int,
- unsigned int,
- unsigned int,
- unsigned int>> {};
-
-// Per pixel tests, all sizes are small so that we can print
-// out the generated bitmaps.
-TEST_P(GLHelperPixelReadbackTest, ScaleTest) {
- unsigned int q_index = std::tr1::get<0>(GetParam());
- unsigned int x = std::tr1::get<1>(GetParam());
- unsigned int y = std::tr1::get<2>(GetParam());
- unsigned int dst_x = std::tr1::get<3>(GetParam());
- unsigned int dst_y = std::tr1::get<4>(GetParam());
-
- for (int flip = 0; flip <= 1; flip++) {
- for (int pattern = 0; pattern < 3; pattern++) {
- TestScale(kRGBReadBackSizes[x], kRGBReadBackSizes[y],
- kRGBReadBackSizes[dst_x], kRGBReadBackSizes[dst_y], pattern,
- q_index, flip == 1);
- if (HasFailure()) {
- return;
- }
- }
- }
-}
-
-// Per pixel tests, all sizes are small so that we can print
-// out the generated bitmaps.
-TEST_P(GLHelperPixelReadbackTest, CropScaleReadbackAndCleanTextureTest) {
- unsigned int q_index = std::tr1::get<0>(GetParam());
- unsigned int x = std::tr1::get<1>(GetParam());
- unsigned int y = std::tr1::get<2>(GetParam());
- unsigned int dst_x = std::tr1::get<3>(GetParam());
- unsigned int dst_y = std::tr1::get<4>(GetParam());
-
- const SkColorType kColorTypes[] = {
- kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType};
- for (size_t color_type = 0; color_type < arraysize(kColorTypes);
- color_type++) {
- for (int pattern = 0; pattern < 3; pattern++) {
- TestCropScaleReadbackAndCleanTexture(
- kRGBReadBackSizes[x], kRGBReadBackSizes[y], kRGBReadBackSizes[dst_x],
- kRGBReadBackSizes[dst_y], pattern, kColorTypes[color_type], false,
- q_index);
- if (HasFailure())
- return;
- }
- }
-}
-
-INSTANTIATE_TEST_CASE_P(
- ,
- GLHelperPixelReadbackTest,
- ::testing::Combine(
- ::testing::Range<unsigned int>(0, arraysize(kQualities)),
- ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)),
- ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)),
- ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)),
- ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes))));
-
-// Validate that all scaling generates valid pipelines.
-TEST_F(GLHelperTest, ValidateScalerPipelines) {
- int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096};
- for (size_t q = 0; q < arraysize(kQualities); q++) {
- for (size_t x = 0; x < arraysize(sizes); x++) {
- for (size_t y = 0; y < arraysize(sizes); y++) {
- for (size_t dst_x = 0; dst_x < arraysize(sizes); dst_x++) {
- for (size_t dst_y = 0; dst_y < arraysize(sizes); dst_y++) {
- TestScalerPipeline(q, sizes[x], sizes[y], sizes[dst_x],
- sizes[dst_y]);
- if (HasFailure()) {
- return;
- }
- }
- }
- }
- }
- }
-}
-
-// Make sure we don't create overly complicated pipelines
-// for a few common use cases.
-TEST_F(GLHelperTest, CheckSpecificPipelines) {
- // Upscale should be single pass.
- CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 1024, 700, 1280, 720,
- "1024x700 -> 1280x720 bilinear\n");
- // Slight downscale should use BILINEAR2X2.
- CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 1280, 720, 1024, 700,
- "1280x720 -> 1024x700 bilinear2x2\n");
- // Most common tab capture pipeline on the Pixel.
- // Should be using two BILINEAR3 passes.
- CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 2560, 1476, 1249, 720,
- "2560x1476 -> 2560x720 bilinear3 Y\n"
- "2560x720 -> 1249x720 bilinear3 X\n");
-}
-
-TEST_F(GLHelperTest, ScalerOpTest) {
- for (int allow3 = 0; allow3 <= 1; allow3++) {
- for (int dst = 1; dst < 2049; dst += 1 + (dst >> 3)) {
- for (int src = 1; src < 2049; src++) {
- TestAddOps(src, dst, allow3 == 1, (src & 1) == 1);
- if (HasFailure()) {
- LOG(ERROR) << "Failed for src=" << src << " dst=" << dst
- << " allow3=" << allow3;
- return;
- }
- }
- }
- }
-}
-
-TEST_F(GLHelperTest, CheckOptimizations) {
- // Test in baseclass since it is friends with GLHelperScaling
- CheckOptimizationsTest();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/host_shared_bitmap_manager.cc b/chromium/components/viz/display_compositor/host_shared_bitmap_manager.cc
deleted file mode 100644
index 938d5fea8ec..00000000000
--- a/chromium/components/viz/display_compositor/host_shared_bitmap_manager.cc
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/display_compositor/host_shared_bitmap_manager.h"
-
-#include <stdint.h>
-
-#include <utility>
-
-#include "base/lazy_instance.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "build/build_config.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-
-class BitmapData : public base::RefCountedThreadSafe<BitmapData> {
- public:
- explicit BitmapData(size_t buffer_size) : buffer_size(buffer_size) {}
- std::unique_ptr<base::SharedMemory> memory;
- std::unique_ptr<uint8_t[]> pixels;
- size_t buffer_size;
-
- private:
- friend class base::RefCountedThreadSafe<BitmapData>;
- ~BitmapData() {}
- DISALLOW_COPY_AND_ASSIGN(BitmapData);
-};
-
-namespace {
-
-class HostSharedBitmap : public cc::SharedBitmap {
- public:
- HostSharedBitmap(uint8_t* pixels,
- scoped_refptr<BitmapData> bitmap_data,
- const cc::SharedBitmapId& id,
- HostSharedBitmapManager* manager)
- : SharedBitmap(pixels, id, 0 /* sequence_number */),
- bitmap_data_(bitmap_data),
- manager_(manager) {}
-
- ~HostSharedBitmap() override {
- if (manager_)
- manager_->FreeSharedMemoryFromMap(id());
- }
-
- private:
- scoped_refptr<BitmapData> bitmap_data_;
- HostSharedBitmapManager* manager_;
-};
-
-} // namespace
-
-base::LazyInstance<HostSharedBitmapManager>::DestructorAtExit
- g_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
-
-HostSharedBitmapManagerClient::HostSharedBitmapManagerClient(
- HostSharedBitmapManager* manager)
- : manager_(manager), binding_(this) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-}
-
-HostSharedBitmapManagerClient::~HostSharedBitmapManagerClient() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- ChildDied();
-}
-
-void HostSharedBitmapManagerClient::Bind(
- cc::mojom::SharedBitmapManagerRequest request) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (binding_.is_bound()) {
- DLOG(ERROR) << "Only one SharedBitmapAllocationNotifierRequest is "
- << "expected from the renderer.";
- return;
- }
- binding_.Bind(std::move(request));
-}
-
-void HostSharedBitmapManagerClient::DidAllocateSharedBitmap(
- mojo::ScopedSharedBufferHandle buffer,
- const cc::SharedBitmapId& id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- base::SharedMemoryHandle memory_handle;
- size_t size;
- MojoResult result = mojo::UnwrapSharedMemoryHandle(
- std::move(buffer), &memory_handle, &size, NULL);
- DCHECK_EQ(result, MOJO_RESULT_OK);
- this->ChildAllocatedSharedBitmap(size, memory_handle, id);
- last_sequence_number_++;
- for (SharedBitmapAllocationObserver& observer : observers_)
- observer.DidAllocateSharedBitmap(last_sequence_number_);
-}
-
-void HostSharedBitmapManagerClient::ChildAllocatedSharedBitmap(
- size_t buffer_size,
- const base::SharedMemoryHandle& handle,
- const cc::SharedBitmapId& id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (manager_->ChildAllocatedSharedBitmap(buffer_size, handle, id))
- owned_bitmaps_.insert(id);
-}
-
-void HostSharedBitmapManagerClient::DidDeleteSharedBitmap(
- const cc::SharedBitmapId& id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- manager_->ChildDeletedSharedBitmap(id);
- owned_bitmaps_.erase(id);
-}
-
-void HostSharedBitmapManagerClient::ChildDied() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- for (const auto& id : owned_bitmaps_)
- manager_->ChildDeletedSharedBitmap(id);
- owned_bitmaps_.clear();
- binding_.Close();
-}
-
-void HostSharedBitmapManagerClient::AddObserver(
- SharedBitmapAllocationObserver* observer) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- observers_.AddObserver(observer);
-}
-
-void HostSharedBitmapManagerClient::RemoveObserver(
- SharedBitmapAllocationObserver* observer) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- observers_.RemoveObserver(observer);
-}
-
-
-HostSharedBitmapManager::HostSharedBitmapManager() {}
-HostSharedBitmapManager::~HostSharedBitmapManager() {
- DCHECK(handle_map_.empty());
-}
-
-HostSharedBitmapManager* HostSharedBitmapManager::current() {
- return g_shared_memory_manager.Pointer();
-}
-
-std::unique_ptr<cc::SharedBitmap> HostSharedBitmapManager::AllocateSharedBitmap(
- const gfx::Size& size) {
- base::AutoLock lock(lock_);
- size_t bitmap_size;
- if (!cc::SharedBitmap::SizeInBytes(size, &bitmap_size))
- return nullptr;
-
- scoped_refptr<BitmapData> data(new BitmapData(bitmap_size));
- // Bitmaps allocated in host don't need to be shared to other processes, so
- // allocate them with new instead.
- data->pixels = std::unique_ptr<uint8_t[]>(new uint8_t[bitmap_size]);
-
- cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
- handle_map_[id] = data;
- return base::MakeUnique<HostSharedBitmap>(data->pixels.get(), data, id, this);
-}
-
-std::unique_ptr<cc::SharedBitmap>
-HostSharedBitmapManager::GetSharedBitmapFromId(const gfx::Size& size,
- const cc::SharedBitmapId& id) {
- base::AutoLock lock(lock_);
- BitmapMap::iterator it = handle_map_.find(id);
- if (it == handle_map_.end())
- return nullptr;
-
- BitmapData* data = it->second.get();
-
- size_t bitmap_size;
- if (!cc::SharedBitmap::SizeInBytes(size, &bitmap_size) ||
- bitmap_size > data->buffer_size)
- return nullptr;
-
- if (data->pixels) {
- return base::MakeUnique<HostSharedBitmap>(data->pixels.get(), data, id,
- nullptr);
- }
- if (!data->memory->memory()) {
- return nullptr;
- }
-
- return base::MakeUnique<HostSharedBitmap>(
- static_cast<uint8_t*>(data->memory->memory()), data, id, nullptr);
-}
-
-bool HostSharedBitmapManager::OnMemoryDump(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- base::AutoLock lock(lock_);
-
- for (const auto& bitmap : handle_map_) {
- base::trace_event::MemoryAllocatorDump* dump =
- pmd->CreateAllocatorDump(base::StringPrintf(
- "sharedbitmap/%s",
- base::HexEncode(bitmap.first.name, sizeof(bitmap.first.name))
- .c_str()));
- if (!dump)
- return false;
-
- dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
- base::trace_event::MemoryAllocatorDump::kUnitsBytes,
- bitmap.second->buffer_size);
-
- // Generate a global GUID used to share this allocation with renderer
- // processes.
- auto guid = cc::GetSharedBitmapGUIDForTracing(bitmap.first);
- pmd->CreateSharedGlobalAllocatorDump(guid);
- pmd->AddOwnershipEdge(dump->guid(), guid);
- }
-
- return true;
-}
-
-bool HostSharedBitmapManager::ChildAllocatedSharedBitmap(
- size_t buffer_size,
- const base::SharedMemoryHandle& handle,
- const cc::SharedBitmapId& id) {
- base::AutoLock lock(lock_);
- if (handle_map_.find(id) != handle_map_.end())
- return false;
- scoped_refptr<BitmapData> data(new BitmapData(buffer_size));
-
- handle_map_[id] = data;
- data->memory = base::MakeUnique<base::SharedMemory>(handle, false);
- data->memory->Map(data->buffer_size);
- data->memory->Close();
- return true;
-}
-
-void HostSharedBitmapManager::ChildDeletedSharedBitmap(
- const cc::SharedBitmapId& id) {
- base::AutoLock lock(lock_);
- handle_map_.erase(id);
-}
-
-size_t HostSharedBitmapManager::AllocatedBitmapCount() const {
- base::AutoLock lock(lock_);
- return handle_map_.size();
-}
-
-void HostSharedBitmapManager::FreeSharedMemoryFromMap(
- const cc::SharedBitmapId& id) {
- base::AutoLock lock(lock_);
- handle_map_.erase(id);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/host_shared_bitmap_manager.h b/chromium/components/viz/display_compositor/host_shared_bitmap_manager.h
deleted file mode 100644
index 0c075d11fb1..00000000000
--- a/chromium/components/viz/display_compositor/host_shared_bitmap_manager.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_DISPLAY_COMPOSITOR_HOST_SHARED_BITMAP_MANAGER_H_
-#define COMPONENTS_VIZ_DISPLAY_COMPOSITOR_HOST_SHARED_BITMAP_MANAGER_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <memory>
-#include <set>
-
-#include "base/containers/hash_tables.h"
-#include "base/hash.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory.h"
-#include "base/observer_list.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
-#include "base/trace_event/memory_dump_provider.h"
-#include "cc/ipc/shared_bitmap_manager.mojom.h"
-#include "cc/resources/shared_bitmap_manager.h"
-#include "components/viz/viz_export.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace BASE_HASH_NAMESPACE {
-template <>
-struct hash<cc::SharedBitmapId> {
- size_t operator()(const cc::SharedBitmapId& id) const {
- return base::Hash(reinterpret_cast<const char*>(id.name), sizeof(id.name));
- }
-};
-} // namespace BASE_HASH_NAMESPACE
-
-namespace viz {
-class BitmapData;
-class HostSharedBitmapManager;
-
-class SharedBitmapAllocationObserver {
- public:
- virtual void DidAllocateSharedBitmap(uint32_t sequence_number) = 0;
-};
-
-class VIZ_EXPORT HostSharedBitmapManagerClient
- : NON_EXPORTED_BASE(public cc::mojom::SharedBitmapManager) {
- public:
- explicit HostSharedBitmapManagerClient(HostSharedBitmapManager* manager);
-
- ~HostSharedBitmapManagerClient() override;
-
- void AddObserver(SharedBitmapAllocationObserver* observer);
- void RemoveObserver(SharedBitmapAllocationObserver* observer);
-
- void Bind(cc::mojom::SharedBitmapManagerRequest request);
-
- // cc::mojom::SharedBitmapManager overrides:
- void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
- const cc::SharedBitmapId& id) override;
- void DidDeleteSharedBitmap(const cc::SharedBitmapId& id) override;
-
- void ChildAllocatedSharedBitmap(size_t buffer_size,
- const base::SharedMemoryHandle& handle,
- const cc::SharedBitmapId& id);
-
- void ChildDied();
-
- uint32_t last_sequence_number() const { return last_sequence_number_; }
-
- private:
- THREAD_CHECKER(thread_checker_);
- HostSharedBitmapManager* manager_;
- mojo::Binding<cc::mojom::SharedBitmapManager> binding_;
- base::hash_set<cc::SharedBitmapId> owned_bitmaps_;
- base::ObserverList<SharedBitmapAllocationObserver> observers_;
- uint32_t last_sequence_number_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(HostSharedBitmapManagerClient);
-};
-
-class VIZ_EXPORT HostSharedBitmapManager
- : public cc::SharedBitmapManager,
- public base::trace_event::MemoryDumpProvider {
- public:
- HostSharedBitmapManager();
- ~HostSharedBitmapManager() override;
-
- static HostSharedBitmapManager* current();
-
- // cc::SharedBitmapManager implementation.
- std::unique_ptr<cc::SharedBitmap> AllocateSharedBitmap(
- const gfx::Size& size) override;
- std::unique_ptr<cc::SharedBitmap> GetSharedBitmapFromId(
- const gfx::Size& size,
- const cc::SharedBitmapId&) override;
-
- // base::trace_event::MemoryDumpProvider implementation.
- bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) override;
-
- size_t AllocatedBitmapCount() const;
-
- void FreeSharedMemoryFromMap(const cc::SharedBitmapId& id);
-
- private:
- friend class HostSharedBitmapManagerClient;
-
- bool ChildAllocatedSharedBitmap(size_t buffer_size,
- const base::SharedMemoryHandle& handle,
- const cc::SharedBitmapId& id);
- void ChildDeletedSharedBitmap(const cc::SharedBitmapId& id);
-
- mutable base::Lock lock_;
-
- typedef base::hash_map<cc::SharedBitmapId, scoped_refptr<BitmapData>>
- BitmapMap;
- BitmapMap handle_map_;
-
- DISALLOW_COPY_AND_ASSIGN(HostSharedBitmapManager);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_DISPLAY_COMPOSITOR_HOST_SHARED_BITMAP_MANAGER_H_
diff --git a/chromium/components/viz/display_compositor/host_shared_bitmap_manager_unittest.cc b/chromium/components/viz/display_compositor/host_shared_bitmap_manager_unittest.cc
deleted file mode 100644
index ee7a92f1ab8..00000000000
--- a/chromium/components/viz/display_compositor/host_shared_bitmap_manager_unittest.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (c) 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 "components/viz/display_compositor/host_shared_bitmap_manager.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace viz {
-namespace {
-
-class HostSharedBitmapManagerTest : public testing::Test {
- protected:
- void SetUp() override { manager_.reset(new HostSharedBitmapManager()); }
- std::unique_ptr<HostSharedBitmapManager> manager_;
-};
-
-TEST_F(HostSharedBitmapManagerTest, TestCreate) {
- gfx::Size bitmap_size(1, 1);
- size_t size_in_bytes;
- EXPECT_TRUE(cc::SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
- std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
- bitmap->CreateAndMapAnonymous(size_in_bytes);
- memset(bitmap->memory(), 0xff, size_in_bytes);
- cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
-
- HostSharedBitmapManagerClient client(manager_.get());
- base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
- client.ChildAllocatedSharedBitmap(size_in_bytes, handle, id);
-
- std::unique_ptr<cc::SharedBitmap> large_bitmap;
- large_bitmap = manager_->GetSharedBitmapFromId(gfx::Size(1024, 1024), id);
- EXPECT_TRUE(large_bitmap.get() == NULL);
-
- std::unique_ptr<cc::SharedBitmap> very_large_bitmap;
- very_large_bitmap =
- manager_->GetSharedBitmapFromId(gfx::Size(1, (1 << 30) | 1), id);
- EXPECT_TRUE(very_large_bitmap.get() == NULL);
-
- std::unique_ptr<cc::SharedBitmap> negative_size_bitmap;
- negative_size_bitmap =
- manager_->GetSharedBitmapFromId(gfx::Size(-1, 1024), id);
- EXPECT_TRUE(negative_size_bitmap.get() == NULL);
-
- cc::SharedBitmapId id2 = cc::SharedBitmap::GenerateId();
- std::unique_ptr<cc::SharedBitmap> invalid_bitmap;
- invalid_bitmap = manager_->GetSharedBitmapFromId(bitmap_size, id2);
- EXPECT_TRUE(invalid_bitmap.get() == NULL);
-
- std::unique_ptr<cc::SharedBitmap> shared_bitmap;
- shared_bitmap = manager_->GetSharedBitmapFromId(bitmap_size, id);
- ASSERT_TRUE(shared_bitmap.get() != NULL);
- EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), 4), 0);
-
- std::unique_ptr<cc::SharedBitmap> large_bitmap2;
- large_bitmap2 = manager_->GetSharedBitmapFromId(gfx::Size(1024, 1024), id);
- EXPECT_TRUE(large_bitmap2.get() == NULL);
-
- std::unique_ptr<cc::SharedBitmap> shared_bitmap2;
- shared_bitmap2 = manager_->GetSharedBitmapFromId(bitmap_size, id);
- EXPECT_TRUE(shared_bitmap2->pixels() == shared_bitmap->pixels());
- shared_bitmap2.reset();
- EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
- 0);
-
- client.DidDeleteSharedBitmap(id);
-
- memset(bitmap->memory(), 0, size_in_bytes);
-
- EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
- 0);
- bitmap.reset();
- shared_bitmap.reset();
-}
-
-TEST_F(HostSharedBitmapManagerTest, RemoveProcess) {
- gfx::Size bitmap_size(1, 1);
- size_t size_in_bytes;
- EXPECT_TRUE(cc::SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
- std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
- bitmap->CreateAndMapAnonymous(size_in_bytes);
- memset(bitmap->memory(), 0xff, size_in_bytes);
- cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
-
- std::unique_ptr<HostSharedBitmapManagerClient> client(
- new HostSharedBitmapManagerClient(manager_.get()));
- base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
- client->ChildAllocatedSharedBitmap(size_in_bytes, handle, id);
-
- std::unique_ptr<cc::SharedBitmap> shared_bitmap;
- shared_bitmap = manager_->GetSharedBitmapFromId(bitmap_size, id);
- ASSERT_TRUE(shared_bitmap.get() != NULL);
-
- EXPECT_EQ(1u, manager_->AllocatedBitmapCount());
- client.reset();
- EXPECT_EQ(0u, manager_->AllocatedBitmapCount());
-
- std::unique_ptr<cc::SharedBitmap> shared_bitmap2;
- shared_bitmap2 = manager_->GetSharedBitmapFromId(bitmap_size, id);
- EXPECT_TRUE(shared_bitmap2.get() == NULL);
- EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
- 0);
-
- shared_bitmap.reset();
-}
-
-TEST_F(HostSharedBitmapManagerTest, AddDuplicate) {
- gfx::Size bitmap_size(1, 1);
- size_t size_in_bytes;
- EXPECT_TRUE(cc::SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
- std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
- bitmap->CreateAndMapAnonymous(size_in_bytes);
- memset(bitmap->memory(), 0xff, size_in_bytes);
- cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
- HostSharedBitmapManagerClient client(manager_.get());
-
- base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
- client.ChildAllocatedSharedBitmap(size_in_bytes, handle, id);
-
- std::unique_ptr<base::SharedMemory> bitmap2(new base::SharedMemory());
- bitmap2->CreateAndMapAnonymous(size_in_bytes);
- memset(bitmap2->memory(), 0x00, size_in_bytes);
-
- client.ChildAllocatedSharedBitmap(size_in_bytes, bitmap2->handle(), id);
-
- std::unique_ptr<cc::SharedBitmap> shared_bitmap;
- shared_bitmap = manager_->GetSharedBitmapFromId(bitmap_size, id);
- ASSERT_TRUE(shared_bitmap.get() != NULL);
- EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
- 0);
- client.DidDeleteSharedBitmap(id);
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/display_compositor/run_all_unittests.cc b/chromium/components/viz/display_compositor/run_all_unittests.cc
deleted file mode 100644
index 80336af2172..00000000000
--- a/chromium/components/viz/display_compositor/run_all_unittests.cc
+++ /dev/null
@@ -1,15 +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 "base/bind.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "components/viz/display_compositor/display_compositor_test_suite.h"
-
-int main(int argc, char** argv) {
- viz::DisplayCompositorTestSuite test_suite(argc, argv);
-
- return base::LaunchUnitTests(argc, argv,
- base::Bind(&viz::DisplayCompositorTestSuite::Run,
- base::Unretained(&test_suite)));
-}
diff --git a/chromium/components/viz/display_compositor/yuv_readback_unittest.cc b/chromium/components/viz/display_compositor/yuv_readback_unittest.cc
deleted file mode 100644
index ef6d83bd758..00000000000
--- a/chromium/components/viz/display_compositor/yuv_readback_unittest.cc
+++ /dev/null
@@ -1,558 +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 "base/json/json_reader.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/trace_event.h"
-#include "components/viz/display_compositor/gl_helper.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "gpu/command_buffer/client/shared_memory_limits.h"
-#include "gpu/ipc/common/surface_handle.h"
-#include "gpu/ipc/gl_in_process_context.h"
-#include "media/base/video_frame.h"
-#include "media/base/video_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gl/gl_implementation.h"
-
-namespace viz {
-
-namespace {
-int kYUVReadbackSizes[] = {2, 4, 14};
-}
-
-class YUVReadbackTest : public testing::Test {
- protected:
- void SetUp() override {
- gpu::gles2::ContextCreationAttribHelper attributes;
- attributes.alpha_size = 8;
- attributes.depth_size = 24;
- attributes.red_size = 8;
- attributes.green_size = 8;
- attributes.blue_size = 8;
- attributes.stencil_size = 8;
- attributes.samples = 4;
- attributes.sample_buffers = 1;
- attributes.bind_generates_resource = false;
-
- context_.reset(
- gpu::GLInProcessContext::Create(nullptr, /* service */
- nullptr, /* surface */
- true, /* offscreen */
- gpu::kNullSurfaceHandle, /* window */
- nullptr, /* share_context */
- attributes, gpu::SharedMemoryLimits(),
- nullptr, /* gpu_memory_buffer_manager */
- nullptr, /* image_factory */
- base::ThreadTaskRunnerHandle::Get()));
- gl_ = context_->GetImplementation();
- gpu::ContextSupport* support = context_->GetImplementation();
-
- helper_.reset(new GLHelper(gl_, support));
- }
-
- void TearDown() override {
- helper_.reset(NULL);
- context_.reset(NULL);
- }
-
- void StartTracing(const std::string& filter) {
- base::trace_event::TraceLog::GetInstance()->SetEnabled(
- base::trace_event::TraceConfig(filter,
- base::trace_event::RECORD_UNTIL_FULL),
- base::trace_event::TraceLog::RECORDING_MODE);
- }
-
- static void TraceDataCB(
- const base::Callback<void()>& callback,
- std::string* output,
- const scoped_refptr<base::RefCountedString>& json_events_str,
- bool has_more_events) {
- if (output->size() > 1 && !json_events_str->data().empty()) {
- output->append(",");
- }
- output->append(json_events_str->data());
- if (!has_more_events) {
- callback.Run();
- }
- }
-
- // End tracing, return tracing data in a simple map
- // of event name->counts.
- void EndTracing(std::map<std::string, int>* event_counts) {
- std::string json_data = "[";
- base::trace_event::TraceLog::GetInstance()->SetDisabled();
- base::RunLoop run_loop;
- base::trace_event::TraceLog::GetInstance()->Flush(
- base::Bind(&YUVReadbackTest::TraceDataCB, run_loop.QuitClosure(),
- base::Unretained(&json_data)));
- run_loop.Run();
- json_data.append("]");
-
- std::string error_msg;
- std::unique_ptr<base::Value> trace_data =
- base::JSONReader::ReadAndReturnError(json_data, 0, NULL, &error_msg);
- CHECK(trace_data) << "JSON parsing failed (" << error_msg
- << ") JSON data:" << std::endl
- << json_data;
-
- base::ListValue* list;
- CHECK(trace_data->GetAsList(&list));
- for (size_t i = 0; i < list->GetSize(); i++) {
- base::Value* item = NULL;
- if (list->Get(i, &item)) {
- base::DictionaryValue* dict;
- CHECK(item->GetAsDictionary(&dict));
- std::string name;
- CHECK(dict->GetString("name", &name));
- std::string trace_type;
- CHECK(dict->GetString("ph", &trace_type));
- // Count all except END traces, as they come in BEGIN/END pairs.
- if (trace_type != "E" && trace_type != "e")
- (*event_counts)[name]++;
- VLOG(1) << "trace name: " << name;
- }
- }
- }
-
- // Look up a single channel value. Works for 4-channel and single channel
- // bitmaps. Clamp x/y.
- int Channel(SkBitmap* pixels, int x, int y, int c) {
- if (pixels->bytesPerPixel() == 4) {
- uint32_t* data =
- pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)),
- std::max(0, std::min(y, pixels->height() - 1)));
- return (*data) >> (c * 8) & 0xff;
- } else {
- DCHECK_EQ(pixels->bytesPerPixel(), 1);
- DCHECK_EQ(c, 0);
- return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)),
- std::max(0, std::min(y, pixels->height() - 1)));
- }
- }
-
- // Set a single channel value. Works for 4-channel and single channel
- // bitmaps. Clamp x/y.
- void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) {
- DCHECK_GE(x, 0);
- DCHECK_GE(y, 0);
- DCHECK_LT(x, pixels->width());
- DCHECK_LT(y, pixels->height());
- if (pixels->bytesPerPixel() == 4) {
- uint32_t* data = pixels->getAddr32(x, y);
- v = std::max(0, std::min(v, 255));
- *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8));
- } else {
- DCHECK_EQ(pixels->bytesPerPixel(), 1);
- DCHECK_EQ(c, 0);
- uint8_t* data = pixels->getAddr8(x, y);
- v = std::max(0, std::min(v, 255));
- *data = v;
- }
- }
-
- // Print all the R, G, B or A values from an SkBitmap in a
- // human-readable format.
- void PrintChannel(SkBitmap* pixels, int c) {
- for (int y = 0; y < pixels->height(); y++) {
- std::string formatted;
- for (int x = 0; x < pixels->width(); x++) {
- formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c)));
- }
- LOG(ERROR) << formatted;
- }
- }
-
- // Get a single R, G, B or A value as a float.
- float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) {
- return Channel(pixels, x, y, c) / 255.0;
- }
-
- // Works like a GL_LINEAR lookup on an SkBitmap.
- float Bilinear(SkBitmap* pixels, float x, float y, int c) {
- x -= 0.5;
- y -= 0.5;
- int base_x = static_cast<int>(floorf(x));
- int base_y = static_cast<int>(floorf(y));
- x -= base_x;
- y -= base_y;
- return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) +
- ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) +
- ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y +
- ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y);
- }
-
- void FlipSKBitmap(SkBitmap* bitmap) {
- int bpp = bitmap->bytesPerPixel();
- DCHECK(bpp == 4 || bpp == 1);
- int top_line = 0;
- int bottom_line = bitmap->height() - 1;
- while (top_line < bottom_line) {
- for (int x = 0; x < bitmap->width(); x++) {
- bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line),
- *bitmap->getAddr32(x, bottom_line))
- : std::swap(*bitmap->getAddr8(x, top_line),
- *bitmap->getAddr8(x, bottom_line));
- }
- top_line++;
- bottom_line--;
- }
- }
-
- // Note: Left/Right means Top/Bottom when used for Y dimension.
- enum Margin {
- MarginLeft,
- MarginMiddle,
- MarginRight,
- MarginInvalid,
- };
-
- static Margin NextMargin(Margin m) {
- switch (m) {
- case MarginLeft:
- return MarginMiddle;
- case MarginMiddle:
- return MarginRight;
- case MarginRight:
- return MarginInvalid;
- default:
- return MarginInvalid;
- }
- }
-
- int compute_margin(int insize, int outsize, Margin m) {
- int available = outsize - insize;
- switch (m) {
- default:
- EXPECT_TRUE(false) << "This should not happen.";
- return 0;
- case MarginLeft:
- return 0;
- case MarginMiddle:
- return (available / 2) & ~1;
- case MarginRight:
- return available;
- }
- }
-
- // Convert 0.0 - 1.0 to 0 - 255
- int float_to_byte(float v) {
- int ret = static_cast<int>(floorf(v * 255.0f + 0.5f));
- if (ret < 0) {
- return 0;
- }
- if (ret > 255) {
- return 255;
- }
- return ret;
- }
-
- static void callcallback(const base::Callback<void()>& callback,
- bool result) {
- callback.Run();
- }
-
- void PrintPlane(unsigned char* plane, int xsize, int stride, int ysize) {
- for (int y = 0; y < ysize; y++) {
- std::string formatted;
- for (int x = 0; x < xsize; x++) {
- formatted.append(base::StringPrintf("%3d, ", plane[y * stride + x]));
- }
- LOG(ERROR) << formatted << " (" << (plane + y * stride) << ")";
- }
- }
-
- // Compare two planes make sure that each component of each pixel
- // is no more than |maxdiff| apart.
- void ComparePlane(unsigned char* truth,
- int truth_stride,
- unsigned char* other,
- int other_stride,
- int maxdiff,
- int xsize,
- int ysize,
- SkBitmap* source,
- std::string message) {
- for (int x = 0; x < xsize; x++) {
- for (int y = 0; y < ysize; y++) {
- int a = other[y * other_stride + x];
- int b = truth[y * truth_stride + x];
- EXPECT_NEAR(a, b, maxdiff)
- << " x=" << x << " y=" << y << " " << message;
- if (std::abs(a - b) > maxdiff) {
- LOG(ERROR) << "-------expected--------";
- PrintPlane(truth, xsize, truth_stride, ysize);
- LOG(ERROR) << "-------actual--------";
- PrintPlane(other, xsize, other_stride, ysize);
- if (source) {
- LOG(ERROR) << "-------before yuv conversion: red--------";
- PrintChannel(source, 0);
- LOG(ERROR) << "-------before yuv conversion: green------";
- PrintChannel(source, 1);
- LOG(ERROR) << "-------before yuv conversion: blue-------";
- PrintChannel(source, 2);
- }
- return;
- }
- }
- }
- }
-
- // YUV readback test. Create a test pattern, convert to YUV
- // with reference implementation and compare to what gl_helper
- // returns.
- void TestYUVReadback(int xsize,
- int ysize,
- int output_xsize,
- int output_ysize,
- int xmargin,
- int ymargin,
- int test_pattern,
- bool flip,
- bool use_mrt,
- GLHelper::ScalerQuality quality) {
- GLuint src_texture;
- gl_->GenTextures(1, &src_texture);
- SkBitmap input_pixels;
- input_pixels.allocN32Pixels(xsize, ysize);
-
- for (int x = 0; x < xsize; ++x) {
- for (int y = 0; y < ysize; ++y) {
- switch (test_pattern) {
- case 0: // Smooth test pattern
- SetChannel(&input_pixels, x, y, 0, x * 10);
- SetChannel(&input_pixels, x, y, 1, y * 10);
- SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
- SetChannel(&input_pixels, x, y, 3, 255);
- break;
- case 1: // Small blocks
- SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
- SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
- SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
- SetChannel(&input_pixels, x, y, 3, 255);
- break;
- case 2: // Medium blocks
- SetChannel(&input_pixels, x, y, 0, 10 + x / 2 * 50);
- SetChannel(&input_pixels, x, y, 1, 10 + y / 3 * 50);
- SetChannel(&input_pixels, x, y, 2, (x + y) / 5 * 50 + 5);
- SetChannel(&input_pixels, x, y, 3, 255);
- break;
- }
- }
- }
-
- gl_->BindTexture(GL_TEXTURE_2D, src_texture);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsize, ysize, 0, GL_RGBA,
- GL_UNSIGNED_BYTE, input_pixels.getPixels());
-
- gpu::Mailbox mailbox;
- gl_->GenMailboxCHROMIUM(mailbox.name);
- EXPECT_FALSE(mailbox.IsZero());
- gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
- const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM();
- gl_->ShallowFlushCHROMIUM();
-
- gpu::SyncToken sync_token;
- gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
-
- std::string message = base::StringPrintf(
- "input size: %dx%d "
- "output size: %dx%d "
- "margin: %dx%d "
- "pattern: %d %s %s",
- xsize, ysize, output_xsize, output_ysize, xmargin, ymargin,
- test_pattern, flip ? "flip" : "noflip", flip ? "mrt" : "nomrt");
- std::unique_ptr<ReadbackYUVInterface> yuv_reader(
- helper_->CreateReadbackPipelineYUV(
- quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize),
- gfx::Size(xsize, ysize), flip, use_mrt));
-
- scoped_refptr<media::VideoFrame> output_frame =
- media::VideoFrame::CreateFrame(
- media::PIXEL_FORMAT_YV12,
- // The coded size of the output frame is rounded up to the next
- // 16-byte boundary. This tests that the readback is being
- // positioned inside the frame's visible region, and not dependent
- // on its coded size.
- gfx::Size((output_xsize + 15) & ~15, (output_ysize + 15) & ~15),
- gfx::Rect(0, 0, output_xsize, output_ysize),
- gfx::Size(output_xsize, output_ysize),
- base::TimeDelta::FromSeconds(0));
- scoped_refptr<media::VideoFrame> truth_frame =
- media::VideoFrame::CreateFrame(
- media::PIXEL_FORMAT_YV12, gfx::Size(output_xsize, output_ysize),
- gfx::Rect(0, 0, output_xsize, output_ysize),
- gfx::Size(output_xsize, output_ysize),
- base::TimeDelta::FromSeconds(0));
-
- base::RunLoop run_loop;
- yuv_reader->ReadbackYUV(mailbox, sync_token, output_frame->visible_rect(),
- output_frame->stride(media::VideoFrame::kYPlane),
- output_frame->data(media::VideoFrame::kYPlane),
- output_frame->stride(media::VideoFrame::kUPlane),
- output_frame->data(media::VideoFrame::kUPlane),
- output_frame->stride(media::VideoFrame::kVPlane),
- output_frame->data(media::VideoFrame::kVPlane),
- gfx::Point(xmargin, ymargin),
- base::Bind(&callcallback, run_loop.QuitClosure()));
-
- const gfx::Rect paste_rect(gfx::Point(xmargin, ymargin),
- gfx::Size(xsize, ysize));
- media::LetterboxYUV(output_frame.get(), paste_rect);
- run_loop.Run();
-
- if (flip) {
- FlipSKBitmap(&input_pixels);
- }
-
- unsigned char* Y = truth_frame->visible_data(media::VideoFrame::kYPlane);
- unsigned char* U = truth_frame->visible_data(media::VideoFrame::kUPlane);
- unsigned char* V = truth_frame->visible_data(media::VideoFrame::kVPlane);
- int32_t y_stride = truth_frame->stride(media::VideoFrame::kYPlane);
- int32_t u_stride = truth_frame->stride(media::VideoFrame::kUPlane);
- int32_t v_stride = truth_frame->stride(media::VideoFrame::kVPlane);
- memset(Y, 0x00, y_stride * output_ysize);
- memset(U, 0x80, u_stride * output_ysize / 2);
- memset(V, 0x80, v_stride * output_ysize / 2);
-
- const float kRGBtoYColorWeights[] = {0.257f, 0.504f, 0.098f, 0.0625f};
- const float kRGBtoUColorWeights[] = {-0.148f, -0.291f, 0.439f, 0.5f};
- const float kRGBtoVColorWeights[] = {0.439f, -0.368f, -0.071f, 0.5f};
-
- for (int y = 0; y < ysize; y++) {
- for (int x = 0; x < xsize; x++) {
- Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte(
- ChannelAsFloat(&input_pixels, x, y, 0) * kRGBtoYColorWeights[0] +
- ChannelAsFloat(&input_pixels, x, y, 1) * kRGBtoYColorWeights[1] +
- ChannelAsFloat(&input_pixels, x, y, 2) * kRGBtoYColorWeights[2] +
- kRGBtoYColorWeights[3]);
- }
- }
-
- for (int y = 0; y < ysize / 2; y++) {
- for (int x = 0; x < xsize / 2; x++) {
- U[(y + ymargin / 2) * u_stride + x + xmargin / 2] =
- float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) *
- kRGBtoUColorWeights[0] +
- Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
- kRGBtoUColorWeights[1] +
- Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
- kRGBtoUColorWeights[2] +
- kRGBtoUColorWeights[3]);
- V[(y + ymargin / 2) * v_stride + x + xmargin / 2] =
- float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) *
- kRGBtoVColorWeights[0] +
- Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) *
- kRGBtoVColorWeights[1] +
- Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) *
- kRGBtoVColorWeights[2] +
- kRGBtoVColorWeights[3]);
- }
- }
-
- ComparePlane(
- Y, y_stride, output_frame->visible_data(media::VideoFrame::kYPlane),
- output_frame->stride(media::VideoFrame::kYPlane), 2, output_xsize,
- output_ysize, &input_pixels, message + " Y plane");
- ComparePlane(
- U, u_stride, output_frame->visible_data(media::VideoFrame::kUPlane),
- output_frame->stride(media::VideoFrame::kUPlane), 2, output_xsize / 2,
- output_ysize / 2, &input_pixels, message + " U plane");
- ComparePlane(
- V, v_stride, output_frame->visible_data(media::VideoFrame::kVPlane),
- output_frame->stride(media::VideoFrame::kVPlane), 2, output_xsize / 2,
- output_ysize / 2, &input_pixels, message + " V plane");
-
- gl_->DeleteTextures(1, &src_texture);
- }
-
- std::unique_ptr<gpu::GLInProcessContext> context_;
- gpu::gles2::GLES2Interface* gl_;
- std::unique_ptr<GLHelper> helper_;
- gl::DisableNullDrawGLBindings enable_pixel_output_;
- base::test::ScopedTaskEnvironment scoped_task_environment_;
-};
-
-TEST_F(YUVReadbackTest, YUVReadbackOptTest) {
- // This test uses the gpu.service/gpu_decoder tracing events to detect how
- // many scaling passes are actually performed by the YUV readback pipeline.
- StartTracing(TRACE_DISABLED_BY_DEFAULT(
- "gpu.service") "," TRACE_DISABLED_BY_DEFAULT("gpu_decoder"));
-
- TestYUVReadback(800, 400, 800, 400, 0, 0, 1, false, true,
- GLHelper::SCALER_QUALITY_FAST);
-
- std::map<std::string, int> event_counts;
- EndTracing(&event_counts);
- int draw_buffer_calls = event_counts["kDrawBuffersEXTImmediate"];
- int draw_arrays_calls = event_counts["kDrawArrays"];
- VLOG(1) << "Draw buffer calls: " << draw_buffer_calls;
- VLOG(1) << "DrawArrays calls: " << draw_arrays_calls;
-
- if (draw_buffer_calls) {
- // When using MRT, the YUV readback code should only
- // execute two draw arrays, and scaling should be integrated
- // into those two calls since we are using the FAST scalign
- // quality.
- EXPECT_EQ(2, draw_arrays_calls);
- } else {
- // When not using MRT, there are three passes for the YUV,
- // and one for the scaling.
- EXPECT_EQ(4, draw_arrays_calls);
- }
-}
-
-class YUVReadbackPixelTest
- : public YUVReadbackTest,
- public ::testing::WithParamInterface<
- std::tr1::tuple<bool, bool, unsigned int, unsigned int>> {};
-
-TEST_P(YUVReadbackPixelTest, Test) {
- bool flip = std::tr1::get<0>(GetParam());
- bool use_mrt = std::tr1::get<1>(GetParam());
- unsigned int x = std::tr1::get<2>(GetParam());
- unsigned int y = std::tr1::get<3>(GetParam());
-
- for (unsigned int ox = x; ox < arraysize(kYUVReadbackSizes); ox++) {
- for (unsigned int oy = y; oy < arraysize(kYUVReadbackSizes); oy++) {
- // If output is a subsection of the destination frame, (letterbox)
- // then try different variations of where the subsection goes.
- for (Margin xm = x < ox ? MarginLeft : MarginRight; xm <= MarginRight;
- xm = NextMargin(xm)) {
- for (Margin ym = y < oy ? MarginLeft : MarginRight; ym <= MarginRight;
- ym = NextMargin(ym)) {
- for (int pattern = 0; pattern < 3; pattern++) {
- TestYUVReadback(
- kYUVReadbackSizes[x], kYUVReadbackSizes[y],
- kYUVReadbackSizes[ox], kYUVReadbackSizes[oy],
- compute_margin(kYUVReadbackSizes[x], kYUVReadbackSizes[ox], xm),
- compute_margin(kYUVReadbackSizes[y], kYUVReadbackSizes[oy], ym),
- pattern, flip, use_mrt, GLHelper::SCALER_QUALITY_GOOD);
- if (HasFailure()) {
- return;
- }
- }
- }
- }
- }
- }
-}
-
-// First argument is intentionally empty.
-INSTANTIATE_TEST_CASE_P(
- ,
- YUVReadbackPixelTest,
- ::testing::Combine(
- ::testing::Bool(),
- ::testing::Bool(),
- ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes)),
- ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes))));
-
-} // namespace viz
diff --git a/chromium/components/viz/frame_sinks/BUILD.gn b/chromium/components/viz/frame_sinks/BUILD.gn
deleted file mode 100644
index 386ec2f6ef8..00000000000
--- a/chromium/components/viz/frame_sinks/BUILD.gn
+++ /dev/null
@@ -1,35 +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.
-
-component("frame_sinks") {
- defines = [ "VIZ_IMPLEMENTATION" ]
-
- sources = [
- "display_provider.h",
- "frame_eviction_manager.cc",
- "frame_eviction_manager.h",
- "frame_evictor.cc",
- "frame_evictor.h",
- "gpu_compositor_frame_sink.cc",
- "gpu_compositor_frame_sink.h",
- "gpu_compositor_frame_sink_delegate.h",
- "gpu_root_compositor_frame_sink.cc",
- "gpu_root_compositor_frame_sink.h",
- "mojo_frame_sink_manager.cc",
- "mojo_frame_sink_manager.h",
- ]
-
- deps = [
- "//base",
- "//cc",
- "//cc/ipc:interfaces",
- "//cc/surfaces",
- "//components/viz/display_compositor",
- "//gpu/ipc:command_buffer",
- ]
-
- if (is_win) {
- cflags = [ "/wd4267" ] # conversion from 'size_t' to 'int' on x64 (crbug.com/633312)
- }
-}
diff --git a/chromium/components/viz/frame_sinks/DEPS b/chromium/components/viz/frame_sinks/DEPS
deleted file mode 100644
index 2628c703220..00000000000
--- a/chromium/components/viz/frame_sinks/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-include_rules = [
- "+cc/base",
- "+cc/ipc",
- "+cc/surfaces",
- "+cc/scheduler",
- "+components/viz/display_compositor",
-]
diff --git a/chromium/components/viz/frame_sinks/display_provider.h b/chromium/components/viz/frame_sinks/display_provider.h
deleted file mode 100644
index de6127b5d7c..00000000000
--- a/chromium/components/viz/frame_sinks/display_provider.h
+++ /dev/null
@@ -1,36 +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_FRAME_SINKS_DISPLAY_PROVIDER_H_
-#define COMPONENTS_VIZ_FRAME_SINKS_DISPLAY_PROVIDER_H_
-
-#include <memory>
-
-#include "gpu/ipc/common/surface_handle.h"
-
-namespace cc {
-class BeginFrameSource;
-class Display;
-class FrameSinkId;
-}
-
-namespace viz {
-
-// Handles creating new cc::Displays and related classes for
-// MojoFrameSinkManager.
-class DisplayProvider {
- public:
- virtual ~DisplayProvider() {}
-
- // Creates a new cc::Display for |surface_handle| with |frame_sink_id|. Will
- // also create cc::BeginFrameSource and return it in |begin_frame_source|.
- virtual std::unique_ptr<cc::Display> CreateDisplay(
- const cc::FrameSinkId& frame_sink_id,
- gpu::SurfaceHandle surface_handle,
- std::unique_ptr<cc::BeginFrameSource>* begin_frame_source) = 0;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_FRAME_SINKS_DISPLAY_PROVIDER_H_
diff --git a/chromium/components/viz/frame_sinks/frame_eviction_manager.cc b/chromium/components/viz/frame_sinks/frame_eviction_manager.cc
deleted file mode 100644
index be419fd3197..00000000000
--- a/chromium/components/viz/frame_sinks/frame_eviction_manager.cc
+++ /dev/null
@@ -1,179 +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/frame_sinks/frame_eviction_manager.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/memory_coordinator_client_registry.h"
-#include "base/memory/memory_coordinator_proxy.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/memory/memory_pressure_monitor.h"
-#include "base/memory/shared_memory.h"
-#include "base/sys_info.h"
-#include "build/build_config.h"
-#include "components/viz/display_compositor/host_shared_bitmap_manager.h"
-
-namespace viz {
-namespace {
-
-const int kModeratePressurePercentage = 50;
-const int kCriticalPressurePercentage = 10;
-
-} // namespace
-
-FrameEvictionManager* FrameEvictionManager::GetInstance() {
- return base::Singleton<FrameEvictionManager>::get();
-}
-
-void FrameEvictionManager::AddFrame(FrameEvictionManagerClient* frame,
- bool locked) {
- RemoveFrame(frame);
- if (locked)
- locked_frames_[frame] = 1;
- else
- unlocked_frames_.push_front(frame);
- CullUnlockedFrames(GetMaxNumberOfSavedFrames());
-}
-
-void FrameEvictionManager::RemoveFrame(FrameEvictionManagerClient* frame) {
- std::map<FrameEvictionManagerClient*, size_t>::iterator locked_iter =
- locked_frames_.find(frame);
- if (locked_iter != locked_frames_.end())
- locked_frames_.erase(locked_iter);
- unlocked_frames_.remove(frame);
-}
-
-void FrameEvictionManager::LockFrame(FrameEvictionManagerClient* frame) {
- std::list<FrameEvictionManagerClient*>::iterator unlocked_iter =
- std::find(unlocked_frames_.begin(), unlocked_frames_.end(), frame);
- if (unlocked_iter != unlocked_frames_.end()) {
- DCHECK(locked_frames_.find(frame) == locked_frames_.end());
- unlocked_frames_.remove(frame);
- locked_frames_[frame] = 1;
- } else {
- DCHECK(locked_frames_.find(frame) != locked_frames_.end());
- locked_frames_[frame]++;
- }
-}
-
-void FrameEvictionManager::UnlockFrame(FrameEvictionManagerClient* frame) {
- DCHECK(locked_frames_.find(frame) != locked_frames_.end());
- size_t locked_count = locked_frames_[frame];
- DCHECK(locked_count);
- if (locked_count > 1) {
- locked_frames_[frame]--;
- } else {
- RemoveFrame(frame);
- unlocked_frames_.push_front(frame);
- CullUnlockedFrames(GetMaxNumberOfSavedFrames());
- }
-}
-
-size_t FrameEvictionManager::GetMaxNumberOfSavedFrames() const {
- int percentage = 100;
- auto* memory_coordinator_proxy = base::MemoryCoordinatorProxy::GetInstance();
- if (memory_coordinator_proxy) {
- switch (memory_coordinator_proxy->GetCurrentMemoryState()) {
- case base::MemoryState::NORMAL:
- percentage = 100;
- break;
- case base::MemoryState::THROTTLED:
- percentage = kCriticalPressurePercentage;
- break;
- case base::MemoryState::SUSPENDED:
- case base::MemoryState::UNKNOWN:
- NOTREACHED();
- break;
- }
- } else {
- base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
-
- if (!monitor)
- return max_number_of_saved_frames_;
-
- // Until we have a global OnMemoryPressureChanged event we need to query the
- // value from our specific pressure monitor.
- switch (monitor->GetCurrentPressureLevel()) {
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
- percentage = 100;
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
- percentage = kModeratePressurePercentage;
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
- percentage = kCriticalPressurePercentage;
- break;
- }
- }
- size_t frames = (max_number_of_saved_frames_ * percentage) / 100;
- return std::max(static_cast<size_t>(1), frames);
-}
-
-FrameEvictionManager::FrameEvictionManager()
- : memory_pressure_listener_(new base::MemoryPressureListener(
- base::Bind(&FrameEvictionManager::OnMemoryPressure,
- base::Unretained(this)))) {
- base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this);
- max_number_of_saved_frames_ =
-#if defined(OS_ANDROID)
- // If the amount of memory on the device is >= 3.5 GB, save up to 5
- // frames.
- base::SysInfo::AmountOfPhysicalMemoryMB() < 1024 * 3.5f ? 1 : 5;
-#else
- std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
-#endif
- max_handles_ = base::SharedMemory::GetHandleLimit() / 8.0f;
-}
-
-FrameEvictionManager::~FrameEvictionManager() {}
-
-void FrameEvictionManager::CullUnlockedFrames(size_t saved_frame_limit) {
- if (unlocked_frames_.size() + locked_frames_.size() > 0) {
- float handles_per_frame =
- HostSharedBitmapManager::current()->AllocatedBitmapCount() * 1.0f /
- (unlocked_frames_.size() + locked_frames_.size());
-
- saved_frame_limit = std::max(
- 1, static_cast<int>(std::min(static_cast<float>(saved_frame_limit),
- max_handles_ / handles_per_frame)));
- }
- while (!unlocked_frames_.empty() &&
- unlocked_frames_.size() + locked_frames_.size() > saved_frame_limit) {
- size_t old_size = unlocked_frames_.size();
- // Should remove self from list.
- unlocked_frames_.back()->EvictCurrentFrame();
- DCHECK_EQ(unlocked_frames_.size() + 1, old_size);
- }
-}
-
-void FrameEvictionManager::OnMemoryPressure(
- base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
- switch (memory_pressure_level) {
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
- PurgeMemory(kModeratePressurePercentage);
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
- PurgeMemory(kCriticalPressurePercentage);
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
- // No need to change anything when there is no pressure.
- return;
- }
-}
-
-void FrameEvictionManager::OnPurgeMemory() {
- PurgeMemory(kCriticalPressurePercentage);
-}
-
-void FrameEvictionManager::PurgeMemory(int percentage) {
- int saved_frame_limit = max_number_of_saved_frames_;
- if (saved_frame_limit <= 1)
- return;
- CullUnlockedFrames(std::max(1, (saved_frame_limit * percentage) / 100));
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/frame_sinks/frame_eviction_manager.h b/chromium/components/viz/frame_sinks/frame_eviction_manager.h
deleted file mode 100644
index 15a2909ffd7..00000000000
--- a/chromium/components/viz/frame_sinks/frame_eviction_manager.h
+++ /dev/null
@@ -1,82 +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_FRAME_SINKS_FRAME_EVICTION_MANAGER_H_
-#define COMPONENTS_VIZ_FRAME_SINKS_FRAME_EVICTION_MANAGER_H_
-
-#include <stddef.h>
-
-#include <list>
-#include <map>
-
-#include "base/macros.h"
-#include "base/memory/memory_coordinator_client.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/memory/singleton.h"
-#include "components/viz/viz_export.h"
-
-namespace viz {
-
-class VIZ_EXPORT FrameEvictionManagerClient {
- public:
- virtual ~FrameEvictionManagerClient() {}
- virtual void EvictCurrentFrame() = 0;
-};
-
-// This class is responsible for globally managing which renderers keep their
-// compositor frame when offscreen. We actively discard compositor frames for
-// offscreen tabs, but keep a minimum amount, as an LRU cache, to make switching
-// between a small set of tabs faster. The limit is a soft limit, because
-// clients can lock their frame to prevent it from being discarded, e.g. if the
-// tab is visible, or while capturing a screenshot.
-class VIZ_EXPORT FrameEvictionManager : public base::MemoryCoordinatorClient {
- public:
- static FrameEvictionManager* GetInstance();
-
- void AddFrame(FrameEvictionManagerClient*, bool locked);
- void RemoveFrame(FrameEvictionManagerClient*);
- void LockFrame(FrameEvictionManagerClient*);
- void UnlockFrame(FrameEvictionManagerClient*);
-
- size_t GetMaxNumberOfSavedFrames() const;
-
- // For testing only
- void set_max_number_of_saved_frames(size_t max_number_of_saved_frames) {
- max_number_of_saved_frames_ = max_number_of_saved_frames;
- }
- void set_max_handles(float max_handles) { max_handles_ = max_handles; }
-
- // React on memory pressure events to adjust the number of cached frames.
- // Please make this private when crbug.com/443824 has been fixed.
- void OnMemoryPressure(
- base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
-
- private:
- FrameEvictionManager();
- ~FrameEvictionManager() override;
-
- // base::MemoryCoordinatorClient implementation:
- void OnPurgeMemory() override;
-
- void CullUnlockedFrames(size_t saved_frame_limit);
-
- void PurgeMemory(int percentage);
-
- friend struct base::DefaultSingletonTraits<FrameEvictionManager>;
-
- // Listens for system under pressure notifications and adjusts number of
- // cached frames accordingly.
- std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
-
- std::map<FrameEvictionManagerClient*, size_t> locked_frames_;
- std::list<FrameEvictionManagerClient*> unlocked_frames_;
- size_t max_number_of_saved_frames_;
- float max_handles_;
-
- DISALLOW_COPY_AND_ASSIGN(FrameEvictionManager);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_FRAME_SINKS_FRAME_EVICTION_MANAGER_H_
diff --git a/chromium/components/viz/frame_sinks/frame_evictor.cc b/chromium/components/viz/frame_sinks/frame_evictor.cc
deleted file mode 100644
index 0207423adce..00000000000
--- a/chromium/components/viz/frame_sinks/frame_evictor.cc
+++ /dev/null
@@ -1,56 +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/frame_sinks/frame_evictor.h"
-
-#include "base/logging.h"
-
-namespace viz {
-
-FrameEvictor::FrameEvictor(FrameEvictorClient* client)
- : client_(client), has_frame_(false), visible_(false) {}
-
-FrameEvictor::~FrameEvictor() {
- DiscardedFrame();
-}
-
-void FrameEvictor::SwappedFrame(bool visible) {
- visible_ = visible;
- has_frame_ = true;
- FrameEvictionManager::GetInstance()->AddFrame(this, visible);
-}
-
-void FrameEvictor::DiscardedFrame() {
- FrameEvictionManager::GetInstance()->RemoveFrame(this);
- has_frame_ = false;
-}
-
-void FrameEvictor::SetVisible(bool visible) {
- if (visible_ == visible)
- return;
- visible_ = visible;
- if (has_frame_) {
- if (visible) {
- LockFrame();
- } else {
- UnlockFrame();
- }
- }
-}
-
-void FrameEvictor::LockFrame() {
- DCHECK(has_frame_);
- FrameEvictionManager::GetInstance()->LockFrame(this);
-}
-
-void FrameEvictor::UnlockFrame() {
- DCHECK(has_frame_);
- FrameEvictionManager::GetInstance()->UnlockFrame(this);
-}
-
-void FrameEvictor::EvictCurrentFrame() {
- client_->EvictDelegatedFrame();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/frame_sinks/frame_evictor.h b/chromium/components/viz/frame_sinks/frame_evictor.h
deleted file mode 100644
index f7e5b0d0504..00000000000
--- a/chromium/components/viz/frame_sinks/frame_evictor.h
+++ /dev/null
@@ -1,46 +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_FRAME_SINKS_FRAME_EVICTOR_H_
-#define COMPONENTS_VIZ_FRAME_SINKS_FRAME_EVICTOR_H_
-
-#include "base/macros.h"
-#include "components/viz/frame_sinks/frame_eviction_manager.h"
-#include "components/viz/viz_export.h"
-
-namespace viz {
-
-class VIZ_EXPORT FrameEvictorClient {
- public:
- virtual ~FrameEvictorClient() {}
- virtual void EvictDelegatedFrame() = 0;
-};
-
-class VIZ_EXPORT FrameEvictor : public FrameEvictionManagerClient {
- public:
- // |client| must outlive |this|.
- explicit FrameEvictor(FrameEvictorClient* client);
- ~FrameEvictor() override;
-
- void SwappedFrame(bool visible);
- void DiscardedFrame();
- void SetVisible(bool visible);
- void LockFrame();
- void UnlockFrame();
- bool HasFrame() { return has_frame_; }
-
- private:
- // FrameEvictionManagerClient implementation.
- void EvictCurrentFrame() override;
-
- FrameEvictorClient* client_;
- bool has_frame_;
- bool visible_;
-
- DISALLOW_COPY_AND_ASSIGN(FrameEvictor);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_FRAME_SINKS_FRAME_EVICTOR_H_
diff --git a/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.cc b/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.cc
deleted file mode 100644
index a2b6007c19c..00000000000
--- a/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/frame_sinks/gpu_compositor_frame_sink.h"
-
-namespace viz {
-
-GpuCompositorFrameSink::GpuCompositorFrameSink(
- GpuCompositorFrameSinkDelegate* delegate,
- cc::SurfaceManager* surface_manager,
- const cc::FrameSinkId& frame_sink_id,
- cc::mojom::MojoCompositorFrameSinkRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest
- compositor_frame_sink_private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client)
- : delegate_(delegate),
- support_(cc::CompositorFrameSinkSupport::Create(
- this,
- surface_manager,
- frame_sink_id,
- false /* is_root */,
- true /* handles_frame_sink_id_invalidation */,
- true /* needs_sync_points */)),
- client_(std::move(client)),
- compositor_frame_sink_binding_(this, std::move(request)),
- compositor_frame_sink_private_binding_(
- this,
- std::move(compositor_frame_sink_private_request)) {
- compositor_frame_sink_binding_.set_connection_error_handler(base::Bind(
- &GpuCompositorFrameSink::OnClientConnectionLost, base::Unretained(this)));
- compositor_frame_sink_private_binding_.set_connection_error_handler(
- base::Bind(&GpuCompositorFrameSink::OnPrivateConnectionLost,
- base::Unretained(this)));
-}
-
-GpuCompositorFrameSink::~GpuCompositorFrameSink() {}
-
-void GpuCompositorFrameSink::EvictCurrentSurface() {
- support_->EvictCurrentSurface();
-}
-
-void GpuCompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
- support_->SetNeedsBeginFrame(needs_begin_frame);
-}
-
-void GpuCompositorFrameSink::SubmitCompositorFrame(
- const cc::LocalSurfaceId& local_surface_id,
- cc::CompositorFrame frame) {
- if (!support_->SubmitCompositorFrame(local_surface_id, std::move(frame))) {
- compositor_frame_sink_binding_.Close();
- OnClientConnectionLost();
- }
-}
-
-void GpuCompositorFrameSink::DidNotProduceFrame(
- const cc::BeginFrameAck& begin_frame_ack) {
- support_->DidNotProduceFrame(begin_frame_ack);
-}
-
-void GpuCompositorFrameSink::DidReceiveCompositorFrameAck(
- const cc::ReturnedResourceArray& resources) {
- if (client_)
- client_->DidReceiveCompositorFrameAck(resources);
-}
-
-void GpuCompositorFrameSink::ClaimTemporaryReference(
- const cc::SurfaceId& surface_id) {
- support_->ClaimTemporaryReference(surface_id);
-}
-
-void GpuCompositorFrameSink::RequestCopyOfSurface(
- std::unique_ptr<cc::CopyOutputRequest> request) {
- support_->RequestCopyOfSurface(std::move(request));
-}
-
-void GpuCompositorFrameSink::OnBeginFrame(const cc::BeginFrameArgs& args) {
- if (client_)
- client_->OnBeginFrame(args);
-}
-
-void GpuCompositorFrameSink::ReclaimResources(
- const cc::ReturnedResourceArray& resources) {
- if (client_)
- client_->ReclaimResources(resources);
-}
-
-void GpuCompositorFrameSink::WillDrawSurface(
- const cc::LocalSurfaceId& local_surface_id,
- const gfx::Rect& damage_rect) {}
-
-void GpuCompositorFrameSink::OnClientConnectionLost() {
- client_connection_lost_ = true;
- // Request destruction of |this| only if both connections are lost.
- delegate_->OnClientConnectionLost(support_->frame_sink_id(),
- private_connection_lost_);
-}
-
-void GpuCompositorFrameSink::OnPrivateConnectionLost() {
- private_connection_lost_ = true;
- // Request destruction of |this| only if both connections are lost.
- delegate_->OnPrivateConnectionLost(support_->frame_sink_id(),
- client_connection_lost_);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.h b/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.h
deleted file mode 100644
index 83cbf2f211d..00000000000
--- a/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
-#define COMPONENTS_VIZ_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
-#include "cc/surfaces/compositor_frame_sink_support.h"
-#include "cc/surfaces/compositor_frame_sink_support_client.h"
-#include "cc/surfaces/local_surface_id.h"
-#include "cc/surfaces/surface_id.h"
-#include "components/viz/frame_sinks/gpu_compositor_frame_sink_delegate.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace viz {
-
-// Server side representation of a WindowSurface.
-class GpuCompositorFrameSink
- : public NON_EXPORTED_BASE(cc::CompositorFrameSinkSupportClient),
- public NON_EXPORTED_BASE(cc::mojom::MojoCompositorFrameSink),
- public NON_EXPORTED_BASE(cc::mojom::MojoCompositorFrameSinkPrivate) {
- public:
- GpuCompositorFrameSink(
- GpuCompositorFrameSinkDelegate* delegate,
- cc::SurfaceManager* surface_manager,
- const cc::FrameSinkId& frame_sink_id,
- cc::mojom::MojoCompositorFrameSinkRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client);
-
- ~GpuCompositorFrameSink() override;
-
- // cc::mojom::MojoCompositorFrameSink:
- void EvictCurrentSurface() override;
- void SetNeedsBeginFrame(bool needs_begin_frame) override;
- void SubmitCompositorFrame(const cc::LocalSurfaceId& local_surface_id,
- cc::CompositorFrame frame) override;
- void DidNotProduceFrame(const cc::BeginFrameAck& begin_frame_ack) override;
-
- // cc::mojom::MojoCompositorFrameSinkPrivate:
- void ClaimTemporaryReference(const cc::SurfaceId& surface_id) override;
- void RequestCopyOfSurface(
- std::unique_ptr<cc::CopyOutputRequest> request) override;
-
- private:
- // cc::CompositorFrameSinkSupportClient implementation:
- void DidReceiveCompositorFrameAck(
- const cc::ReturnedResourceArray& resources) override;
- void OnBeginFrame(const cc::BeginFrameArgs& args) override;
- void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
- void WillDrawSurface(const cc::LocalSurfaceId& local_surface_id,
- const gfx::Rect& damage_rect) override;
-
- void OnClientConnectionLost();
- void OnPrivateConnectionLost();
-
- GpuCompositorFrameSinkDelegate* const delegate_;
- std::unique_ptr<cc::CompositorFrameSinkSupport> support_;
-
- bool client_connection_lost_ = false;
- bool private_connection_lost_ = false;
-
- cc::mojom::MojoCompositorFrameSinkClientPtr client_;
- mojo::Binding<cc::mojom::MojoCompositorFrameSink>
- compositor_frame_sink_binding_;
- mojo::Binding<cc::mojom::MojoCompositorFrameSinkPrivate>
- compositor_frame_sink_private_binding_;
-
- DISALLOW_COPY_AND_ASSIGN(GpuCompositorFrameSink);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
diff --git a/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink_delegate.h b/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink_delegate.h
deleted file mode 100644
index d9d45ae2a6b..00000000000
--- a/chromium/components/viz/frame_sinks/gpu_compositor_frame_sink_delegate.h
+++ /dev/null
@@ -1,30 +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_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_DELEGATE_H_
-#define COMPONENTS_VIZ_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_DELEGATE_H_
-
-namespace cc {
-class FrameSinkId;
-}
-
-namespace viz {
-
-class GpuCompositorFrameSinkDelegate {
- public:
- // We must avoid destroying a GpuCompositorFrameSink until both the display
- // compositor host and the client drop their connection to avoid getting into
- // a state where surfaces references are inconsistent.
- virtual void OnClientConnectionLost(const cc::FrameSinkId& frame_sink_id,
- bool destroy_compositor_frame_sink) = 0;
- virtual void OnPrivateConnectionLost(const cc::FrameSinkId& frame_sink_id,
- bool destroy_compositor_frame_sink) = 0;
-
- protected:
- virtual ~GpuCompositorFrameSinkDelegate() {}
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_DELEGATE_H_
diff --git a/chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc b/chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc
deleted file mode 100644
index 393f53fc2eb..00000000000
--- a/chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc
+++ /dev/null
@@ -1,156 +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/frame_sinks/gpu_root_compositor_frame_sink.h"
-
-#include "cc/surfaces/compositor_frame_sink_support.h"
-#include "cc/surfaces/display.h"
-
-namespace viz {
-
-GpuRootCompositorFrameSink::GpuRootCompositorFrameSink(
- GpuCompositorFrameSinkDelegate* delegate,
- cc::SurfaceManager* surface_manager,
- const cc::FrameSinkId& frame_sink_id,
- std::unique_ptr<cc::Display> display,
- std::unique_ptr<cc::BeginFrameSource> begin_frame_source,
- cc::mojom::MojoCompositorFrameSinkAssociatedRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest
- compositor_frame_sink_private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client,
- cc::mojom::DisplayPrivateAssociatedRequest display_private_request)
- : delegate_(delegate),
- support_(cc::CompositorFrameSinkSupport::Create(
- this,
- surface_manager,
- frame_sink_id,
- true /* is_root */,
- true /* handles_frame_sink_id_invalidation */,
- true /* needs_sync_points */)),
- display_begin_frame_source_(std::move(begin_frame_source)),
- display_(std::move(display)),
- client_(std::move(client)),
- compositor_frame_sink_binding_(this, std::move(request)),
- compositor_frame_sink_private_binding_(
- this,
- std::move(compositor_frame_sink_private_request)),
- display_private_binding_(this, std::move(display_private_request)) {
- compositor_frame_sink_binding_.set_connection_error_handler(
- base::Bind(&GpuRootCompositorFrameSink::OnClientConnectionLost,
- base::Unretained(this)));
- compositor_frame_sink_private_binding_.set_connection_error_handler(
- base::Bind(&GpuRootCompositorFrameSink::OnPrivateConnectionLost,
- base::Unretained(this)));
- display_->Initialize(this, surface_manager);
- display_->SetVisible(true);
-}
-
-GpuRootCompositorFrameSink::~GpuRootCompositorFrameSink() = default;
-
-void GpuRootCompositorFrameSink::SetDisplayVisible(bool visible) {
- DCHECK(display_);
- display_->SetVisible(visible);
-}
-
-void GpuRootCompositorFrameSink::ResizeDisplay(const gfx::Size& size) {
- DCHECK(display_);
- display_->Resize(size);
-}
-
-void GpuRootCompositorFrameSink::SetDisplayColorSpace(
- const gfx::ColorSpace& color_space) {
- DCHECK(display_);
- display_->SetColorSpace(color_space, color_space);
-}
-
-void GpuRootCompositorFrameSink::SetOutputIsSecure(bool secure) {
- DCHECK(display_);
- display_->SetOutputIsSecure(secure);
-}
-
-void GpuRootCompositorFrameSink::SetLocalSurfaceId(
- const cc::LocalSurfaceId& local_surface_id,
- float scale_factor) {
- display_->SetLocalSurfaceId(local_surface_id, scale_factor);
-}
-
-void GpuRootCompositorFrameSink::EvictCurrentSurface() {
- support_->EvictCurrentSurface();
-}
-
-void GpuRootCompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
- support_->SetNeedsBeginFrame(needs_begin_frame);
-}
-
-void GpuRootCompositorFrameSink::SubmitCompositorFrame(
- const cc::LocalSurfaceId& local_surface_id,
- cc::CompositorFrame frame) {
- if (!support_->SubmitCompositorFrame(local_surface_id, std::move(frame))) {
- compositor_frame_sink_binding_.Close();
- OnClientConnectionLost();
- }
-}
-
-void GpuRootCompositorFrameSink::DidNotProduceFrame(
- const cc::BeginFrameAck& begin_frame_ack) {
- support_->DidNotProduceFrame(begin_frame_ack);
-}
-
-void GpuRootCompositorFrameSink::ClaimTemporaryReference(
- const cc::SurfaceId& surface_id) {
- support_->ClaimTemporaryReference(surface_id);
-}
-
-void GpuRootCompositorFrameSink::RequestCopyOfSurface(
- std::unique_ptr<cc::CopyOutputRequest> request) {
- support_->RequestCopyOfSurface(std::move(request));
-}
-
-void GpuRootCompositorFrameSink::DisplayOutputSurfaceLost() {
- // TODO(staraz): Implement this. Client should hear about context/output
- // surface lost.
-}
-
-void GpuRootCompositorFrameSink::DisplayWillDrawAndSwap(
- bool will_draw_and_swap,
- const cc::RenderPassList& render_pass) {}
-
-void GpuRootCompositorFrameSink::DisplayDidDrawAndSwap() {}
-
-void GpuRootCompositorFrameSink::DidReceiveCompositorFrameAck(
- const cc::ReturnedResourceArray& resources) {
- if (client_)
- client_->DidReceiveCompositorFrameAck(resources);
-}
-
-void GpuRootCompositorFrameSink::OnBeginFrame(const cc::BeginFrameArgs& args) {
- if (client_)
- client_->OnBeginFrame(args);
-}
-
-void GpuRootCompositorFrameSink::ReclaimResources(
- const cc::ReturnedResourceArray& resources) {
- if (client_)
- client_->ReclaimResources(resources);
-}
-
-void GpuRootCompositorFrameSink::WillDrawSurface(
- const cc::LocalSurfaceId& local_surface_id,
- const gfx::Rect& damage_rect) {}
-
-void GpuRootCompositorFrameSink::OnClientConnectionLost() {
- client_connection_lost_ = true;
- // Request destruction of |this| only if both connections are lost.
- delegate_->OnClientConnectionLost(support_->frame_sink_id(),
- private_connection_lost_);
-}
-
-void GpuRootCompositorFrameSink::OnPrivateConnectionLost() {
- private_connection_lost_ = true;
- // Request destruction of |this| only if both connections are lost.
- delegate_->OnPrivateConnectionLost(support_->frame_sink_id(),
- client_connection_lost_);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.h b/chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.h
deleted file mode 100644
index 18df306f331..00000000000
--- a/chromium/components/viz/frame_sinks/gpu_root_compositor_frame_sink.h
+++ /dev/null
@@ -1,110 +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_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
-#define COMPONENTS_VIZ_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
-
-#include "cc/ipc/frame_sink_manager.mojom.h"
-#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
-#include "cc/surfaces/compositor_frame_sink_support_client.h"
-#include "cc/surfaces/display_client.h"
-#include "cc/surfaces/local_surface_id.h"
-#include "cc/surfaces/surface_id.h"
-#include "components/viz/frame_sinks/gpu_compositor_frame_sink_delegate.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace cc {
-class BeginFrameSource;
-class CompositorFrameSinkSupport;
-class Display;
-class SurfaceManager;
-}
-
-namespace viz {
-
-class GpuCompositorFrameSinkDelegate;
-
-class GpuRootCompositorFrameSink
- : public NON_EXPORTED_BASE(cc::CompositorFrameSinkSupportClient),
- public NON_EXPORTED_BASE(cc::mojom::MojoCompositorFrameSink),
- public NON_EXPORTED_BASE(cc::mojom::MojoCompositorFrameSinkPrivate),
- public NON_EXPORTED_BASE(cc::mojom::DisplayPrivate),
- public NON_EXPORTED_BASE(cc::DisplayClient) {
- public:
- GpuRootCompositorFrameSink(
- GpuCompositorFrameSinkDelegate* delegate,
- cc::SurfaceManager* surface_manager,
- const cc::FrameSinkId& frame_sink_id,
- std::unique_ptr<cc::Display> display,
- std::unique_ptr<cc::BeginFrameSource> begin_frame_source,
- cc::mojom::MojoCompositorFrameSinkAssociatedRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client,
- cc::mojom::DisplayPrivateAssociatedRequest display_private_request);
-
- ~GpuRootCompositorFrameSink() override;
-
- // cc::mojom::DisplayPrivate:
- void SetDisplayVisible(bool visible) override;
- void ResizeDisplay(const gfx::Size& size) override;
- void SetDisplayColorSpace(const gfx::ColorSpace& color_space) override;
- void SetOutputIsSecure(bool secure) override;
- void SetLocalSurfaceId(const cc::LocalSurfaceId& local_surface_id,
- float scale_factor) override;
-
- // cc::mojom::MojoCompositorFrameSink:
- void EvictCurrentSurface() override;
- void SetNeedsBeginFrame(bool needs_begin_frame) override;
- void SubmitCompositorFrame(const cc::LocalSurfaceId& local_surface_id,
- cc::CompositorFrame frame) override;
- void DidNotProduceFrame(const cc::BeginFrameAck& begin_frame_ack) override;
-
- // cc::mojom::MojoCompositorFrameSinkPrivate:
- void ClaimTemporaryReference(const cc::SurfaceId& surface_id) override;
- void RequestCopyOfSurface(
- std::unique_ptr<cc::CopyOutputRequest> request) override;
-
- private:
- // cc::DisplayClient:
- void DisplayOutputSurfaceLost() override;
- void DisplayWillDrawAndSwap(bool will_draw_and_swap,
- const cc::RenderPassList& render_passes) override;
- void DisplayDidDrawAndSwap() override;
-
- // cc::CompositorFrameSinkSupportClient:
- void DidReceiveCompositorFrameAck(
- const cc::ReturnedResourceArray& resources) override;
- void OnBeginFrame(const cc::BeginFrameArgs& args) override;
- void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
- void WillDrawSurface(const cc::LocalSurfaceId& local_surface_id,
- const gfx::Rect& damage_rect) override;
-
- void OnClientConnectionLost();
- void OnPrivateConnectionLost();
-
- GpuCompositorFrameSinkDelegate* const delegate_;
- std::unique_ptr<cc::CompositorFrameSinkSupport> support_;
-
- // GpuRootCompositorFrameSink holds a Display and its BeginFrameSource if
- // it was created with a non-null gpu::SurfaceHandle.
- std::unique_ptr<cc::BeginFrameSource> display_begin_frame_source_;
- std::unique_ptr<cc::Display> display_;
-
- bool client_connection_lost_ = false;
- bool private_connection_lost_ = false;
-
- cc::mojom::MojoCompositorFrameSinkClientPtr client_;
- mojo::AssociatedBinding<cc::mojom::MojoCompositorFrameSink>
- compositor_frame_sink_binding_;
- mojo::Binding<cc::mojom::MojoCompositorFrameSinkPrivate>
- compositor_frame_sink_private_binding_;
- mojo::AssociatedBinding<cc::mojom::DisplayPrivate> display_private_binding_;
-
- DISALLOW_COPY_AND_ASSIGN(GpuRootCompositorFrameSink);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
diff --git a/chromium/components/viz/frame_sinks/mojo_frame_sink_manager.cc b/chromium/components/viz/frame_sinks/mojo_frame_sink_manager.cc
deleted file mode 100644
index 1c4cc82bc51..00000000000
--- a/chromium/components/viz/frame_sinks/mojo_frame_sink_manager.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/frame_sinks/mojo_frame_sink_manager.h"
-
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/memory/ptr_util.h"
-#include "cc/base/switches.h"
-#include "cc/scheduler/begin_frame_source.h"
-#include "cc/surfaces/display.h"
-#include "cc/surfaces/surface_dependency_tracker.h"
-#include "components/viz/frame_sinks/display_provider.h"
-#include "components/viz/frame_sinks/gpu_compositor_frame_sink.h"
-#include "components/viz/frame_sinks/gpu_root_compositor_frame_sink.h"
-
-namespace viz {
-
-MojoFrameSinkManager::MojoFrameSinkManager(bool use_surface_references,
- DisplayProvider* display_provider)
- : manager_(use_surface_references
- ? cc::SurfaceManager::LifetimeType::REFERENCES
- : cc::SurfaceManager::LifetimeType::SEQUENCES),
- display_provider_(display_provider),
- binding_(this) {
- manager_.AddObserver(this);
- dependency_tracker_ = base::MakeUnique<cc::SurfaceDependencyTracker>(
- &manager_, manager_.GetPrimaryBeginFrameSource());
- manager_.SetDependencyTracker(dependency_tracker_.get());
-}
-
-MojoFrameSinkManager::~MojoFrameSinkManager() {
- DCHECK(thread_checker_.CalledOnValidThread());
- manager_.SetDependencyTracker(nullptr);
- dependency_tracker_.reset();
- manager_.RemoveObserver(this);
-}
-
-void MojoFrameSinkManager::Connect(
- cc::mojom::FrameSinkManagerRequest request,
- cc::mojom::FrameSinkManagerClientPtr client) {
- DCHECK(!binding_.is_bound());
- binding_.Bind(std::move(request));
- client_ = std::move(client);
-}
-
-void MojoFrameSinkManager::CreateRootCompositorFrameSink(
- const cc::FrameSinkId& frame_sink_id,
- gpu::SurfaceHandle surface_handle,
- cc::mojom::MojoCompositorFrameSinkAssociatedRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client,
- cc::mojom::DisplayPrivateAssociatedRequest display_private_request) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_NE(surface_handle, gpu::kNullSurfaceHandle);
- DCHECK_EQ(0u, compositor_frame_sinks_.count(frame_sink_id));
- DCHECK(display_provider_);
-
- std::unique_ptr<cc::BeginFrameSource> begin_frame_source;
- std::unique_ptr<cc::Display> display = display_provider_->CreateDisplay(
- frame_sink_id, surface_handle, &begin_frame_source);
-
- compositor_frame_sinks_[frame_sink_id] =
- base::MakeUnique<GpuRootCompositorFrameSink>(
- this, &manager_, frame_sink_id, std::move(display),
- std::move(begin_frame_source), std::move(request),
- std::move(private_request), std::move(client),
- std::move(display_private_request));
-}
-
-void MojoFrameSinkManager::CreateCompositorFrameSink(
- const cc::FrameSinkId& frame_sink_id,
- cc::mojom::MojoCompositorFrameSinkRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_EQ(0u, compositor_frame_sinks_.count(frame_sink_id));
-
- compositor_frame_sinks_[frame_sink_id] =
- base::MakeUnique<GpuCompositorFrameSink>(
- this, &manager_, frame_sink_id, std::move(request),
- std::move(private_request), std::move(client));
-}
-
-void MojoFrameSinkManager::RegisterFrameSinkHierarchy(
- const cc::FrameSinkId& parent_frame_sink_id,
- const cc::FrameSinkId& child_frame_sink_id) {
- manager_.RegisterFrameSinkHierarchy(parent_frame_sink_id,
- child_frame_sink_id);
-}
-
-void MojoFrameSinkManager::UnregisterFrameSinkHierarchy(
- const cc::FrameSinkId& parent_frame_sink_id,
- const cc::FrameSinkId& child_frame_sink_id) {
- manager_.UnregisterFrameSinkHierarchy(parent_frame_sink_id,
- child_frame_sink_id);
-}
-
-void MojoFrameSinkManager::DropTemporaryReference(
- const cc::SurfaceId& surface_id) {
- manager_.DropTemporaryReference(surface_id);
-}
-
-void MojoFrameSinkManager::DestroyCompositorFrameSink(cc::FrameSinkId sink_id) {
- compositor_frame_sinks_.erase(sink_id);
-}
-
-void MojoFrameSinkManager::OnSurfaceCreated(
- const cc::SurfaceInfo& surface_info) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_GT(surface_info.device_scale_factor(), 0.0f);
-
- // TODO(kylechar): |client_| will try to find an owner for the temporary
- // reference to the new surface. With surface synchronization this might not
- // be necessary, because a surface reference might already exist and no
- // temporary reference was created. It could be useful to let |client_| know
- // if it should find an owner.
- if (client_)
- client_->OnSurfaceCreated(surface_info);
-}
-
-void MojoFrameSinkManager::OnSurfaceDamaged(const cc::SurfaceId& surface_id,
- bool* changed) {}
-
-void MojoFrameSinkManager::OnSurfaceDiscarded(const cc::SurfaceId& surface_id) {
-}
-
-void MojoFrameSinkManager::OnClientConnectionLost(
- const cc::FrameSinkId& frame_sink_id,
- bool destroy_compositor_frame_sink) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (destroy_compositor_frame_sink)
- DestroyCompositorFrameSink(frame_sink_id);
- // TODO(fsamuel): Tell the frame sink manager host that the client connection
- // has been lost so that it can drop its private connection and allow a new
- // client instance to create a new CompositorFrameSink.
-}
-
-void MojoFrameSinkManager::OnPrivateConnectionLost(
- const cc::FrameSinkId& frame_sink_id,
- bool destroy_compositor_frame_sink) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (destroy_compositor_frame_sink)
- DestroyCompositorFrameSink(frame_sink_id);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/frame_sinks/mojo_frame_sink_manager.h b/chromium/components/viz/frame_sinks/mojo_frame_sink_manager.h
deleted file mode 100644
index af01a4bfe29..00000000000
--- a/chromium/components/viz/frame_sinks/mojo_frame_sink_manager.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_FRAME_SINKS_MOJO_FRAME_SINK_MANAGER_H_
-#define COMPONENTS_VIZ_FRAME_SINKS_MOJO_FRAME_SINK_MANAGER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <unordered_map>
-
-#include "base/macros.h"
-#include "base/threading/thread_checker.h"
-#include "cc/ipc/frame_sink_manager.mojom.h"
-#include "cc/surfaces/frame_sink_id.h"
-#include "cc/surfaces/surface_manager.h"
-#include "cc/surfaces/surface_observer.h"
-#include "components/viz/frame_sinks/gpu_compositor_frame_sink_delegate.h"
-#include "components/viz/viz_export.h"
-#include "gpu/ipc/common/surface_handle.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace viz {
-
-class DisplayProvider;
-
-// MojoFrameSinkManager manages state associated with CompositorFrameSinks. It
-// provides a Mojo interface to create CompositorFrameSinks, manages BeginFrame
-// hierarchy and manages surface lifetime.
-//
-// This is intended to be created in the viz or GPU process. For mus+ash this
-// will be true after the mus process split. For non-mus Chrome this will be
-// created in the browser process, at least until GPU implementations can be
-// unified.
-class VIZ_EXPORT MojoFrameSinkManager
- : public cc::SurfaceObserver,
- public NON_EXPORTED_BASE(GpuCompositorFrameSinkDelegate),
- public NON_EXPORTED_BASE(cc::mojom::FrameSinkManager) {
- public:
- MojoFrameSinkManager(bool use_surface_references,
- DisplayProvider* display_provider);
- ~MojoFrameSinkManager() override;
-
- cc::SurfaceManager* surface_manager() { return &manager_; }
-
- // Binds to |request| and store connection back to |client|.
- void Connect(cc::mojom::FrameSinkManagerRequest request,
- cc::mojom::FrameSinkManagerClientPtr client);
-
- // cc::mojom::FrameSinkManager implementation:
- void CreateRootCompositorFrameSink(
- const cc::FrameSinkId& frame_sink_id,
- gpu::SurfaceHandle surface_handle,
- cc::mojom::MojoCompositorFrameSinkAssociatedRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client,
- cc::mojom::DisplayPrivateAssociatedRequest display_private_request)
- override;
- void CreateCompositorFrameSink(
- const cc::FrameSinkId& frame_sink_id,
- cc::mojom::MojoCompositorFrameSinkRequest request,
- cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
- cc::mojom::MojoCompositorFrameSinkClientPtr client) override;
- void RegisterFrameSinkHierarchy(
- const cc::FrameSinkId& parent_frame_sink_id,
- const cc::FrameSinkId& child_frame_sink_id) override;
- void UnregisterFrameSinkHierarchy(
- const cc::FrameSinkId& parent_frame_sink_id,
- const cc::FrameSinkId& child_frame_sink_id) override;
- void DropTemporaryReference(const cc::SurfaceId& surface_id) override;
-
- private:
- // It is necessary to pass |frame_sink_id| by value because the id
- // is owned by the GpuCompositorFrameSink in the map. When the sink is
- // removed from the map, |frame_sink_id| would also be destroyed if it were a
- // reference. But the map can continue to iterate and try to use it. Passing
- // by value avoids this.
- void DestroyCompositorFrameSink(cc::FrameSinkId frame_sink_id);
-
- // cc::SurfaceObserver implementation.
- void OnSurfaceCreated(const cc::SurfaceInfo& surface_info) override;
- void OnSurfaceDamaged(const cc::SurfaceId& surface_id,
- bool* changed) override;
- void OnSurfaceDiscarded(const cc::SurfaceId& surface_id) override;
-
- // GpuCompositorFrameSinkDelegate implementation.
- void OnClientConnectionLost(const cc::FrameSinkId& frame_sink_id,
- bool destroy_compositor_frame_sink) override;
- void OnPrivateConnectionLost(const cc::FrameSinkId& frame_sink_id,
- bool destroy_compositor_frame_sink) override;
-
- // SurfaceManager should be the first object constructed and the last object
- // destroyed in order to ensure that all other objects that depend on it have
- // access to a valid pointer for the entirety of their lifetimes.
- cc::SurfaceManager manager_;
-
- std::unique_ptr<cc::SurfaceDependencyTracker> dependency_tracker_;
-
- // Provides a cc::Display for CreateRootCompositorFrameSink().
- DisplayProvider* const display_provider_;
-
- std::unordered_map<cc::FrameSinkId,
- std::unique_ptr<cc::mojom::MojoCompositorFrameSink>,
- cc::FrameSinkIdHash>
- compositor_frame_sinks_;
-
- base::ThreadChecker thread_checker_;
-
- cc::mojom::FrameSinkManagerClientPtr client_;
- mojo::Binding<cc::mojom::FrameSinkManager> binding_;
-
- DISALLOW_COPY_AND_ASSIGN(MojoFrameSinkManager);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_FRAME_SINKS_MOJO_FRAME_SINK_MANAGER_H_
diff --git a/chromium/components/viz/host/BUILD.gn b/chromium/components/viz/host/BUILD.gn
new file mode 100644
index 00000000000..e5d833ecb92
--- /dev/null
+++ b/chromium/components/viz/host/BUILD.gn
@@ -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.
+
+import("//components/viz/viz.gni")
+
+viz_component("host") {
+ defines = [ "VIZ_HOST_IMPLEMENTATION" ]
+
+ sources = [
+ "frame_sink_observer.h",
+ "host_frame_sink_manager.cc",
+ "host_frame_sink_manager.h",
+ "server_gpu_memory_buffer_manager.cc",
+ "server_gpu_memory_buffer_manager.h",
+ "viz_host_export.h",
+ ]
+
+ deps = [
+ "//base",
+ "//cc/ipc:interfaces",
+ "//cc/surfaces",
+ "//gpu/ipc/client",
+ "//gpu/ipc/common",
+ "//services/ui/gpu/interfaces",
+
+ # TODO(kylechar): This is temporary and will be removed when all host to
+ # service communication is over Mojo.
+ "//components/viz/service",
+ ]
+
+ public_deps = [
+ "//gpu/command_buffer/client",
+ "//gpu/ipc/host",
+ ]
+}
+
+viz_source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "host_frame_sink_manager_unittests.cc",
+ "server_gpu_memory_buffer_manager_unittest.cc",
+ ]
+
+ deps = [
+ ":host",
+ "//base",
+ "//base/test:test_support",
+ "//cc/ipc:interfaces",
+ "//cc/surfaces",
+ "//gpu/ipc/host",
+ "//mojo/public/cpp/bindings",
+ "//services/ui/gpu/interfaces",
+ "//testing/gmock",
+ "//testing/gtest",
+
+ # TODO(kylechar): This is temporary and will be removed when all host to
+ # service communication is over Mojo.
+ "//components/viz/service",
+ ]
+}
diff --git a/chromium/components/viz/host/DEPS b/chromium/components/viz/host/DEPS
new file mode 100644
index 00000000000..b6198f4bd8b
--- /dev/null
+++ b/chromium/components/viz/host/DEPS
@@ -0,0 +1,23 @@
+include_rules = [
+ "+cc/ipc",
+ "+cc/surfaces",
+ "+components/viz/host",
+ "+gpu/command_buffer/client",
+ "+gpu/command_buffer/common",
+ "+gpu/ipc/client",
+ "+gpu/ipc/common",
+ "+gpu/ipc/host",
+ "+mojo/public/cpp/bindings",
+ "+services/ui/gpu/interfaces",
+ "+ui/gfx",
+]
+
+specific_include_rules = {
+ "host_frame_sink_manager.*": [
+ "+components/viz/service/frame_sinks/compositor_frame_sink_support.h",
+ "+components/viz/service/frame_sinks/compositor_frame_sink_support_client.h",
+ "+components/viz/service/frame_sinks/frame_sink_manager.h",
+ "+components/viz/service/frame_sinks/frame_sink_manager_impl.h",
+ "+components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h",
+ ]
+}
diff --git a/chromium/components/viz/host/OWNERS b/chromium/components/viz/host/OWNERS
new file mode 100644
index 00000000000..c795b67cbd8
--- /dev/null
+++ b/chromium/components/viz/host/OWNERS
@@ -0,0 +1 @@
+kylechar@chromium.org \ No newline at end of file
diff --git a/chromium/components/viz/host/frame_sink_observer.h b/chromium/components/viz/host/frame_sink_observer.h
new file mode 100644
index 00000000000..eaa0acff3cf
--- /dev/null
+++ b/chromium/components/viz/host/frame_sink_observer.h
@@ -0,0 +1,26 @@
+// 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_FRAME_SINK_OBSERVER_H_
+#define COMPONENTS_VIZ_HOST_FRAME_SINK_OBSERVER_H_
+
+namespace cc {
+class SurfaceInfo;
+}
+
+namespace viz {
+
+class FrameSinkObserver {
+ public:
+ // Runs when a CompositorFrame is received for the given SurfaceInfo for the
+ // first time.
+ virtual void OnSurfaceCreated(const SurfaceInfo& surface_info) = 0;
+
+ protected:
+ ~FrameSinkObserver() {}
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_HOST_FRAME_SINK_OBSERVER_H_
diff --git a/chromium/components/viz/host/host_frame_sink_manager.cc b/chromium/components/viz/host/host_frame_sink_manager.cc
new file mode 100644
index 00000000000..48ad64c117b
--- /dev/null
+++ b/chromium/components/viz/host/host_frame_sink_manager.cc
@@ -0,0 +1,150 @@
+// 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_frame_sink_manager.h"
+
+#include <utility>
+
+#include "base/sequenced_task_runner.h"
+#include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_client.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+
+namespace viz {
+
+HostFrameSinkManager::HostFrameSinkManager()
+ : binding_(this), weak_ptr_factory_(this) {}
+
+HostFrameSinkManager::~HostFrameSinkManager() = default;
+
+void HostFrameSinkManager::SetLocalManager(
+ FrameSinkManagerImpl* frame_sink_manager_impl) {
+ DCHECK(!frame_sink_manager_ptr_);
+ frame_sink_manager_impl_ = frame_sink_manager_impl;
+
+ frame_sink_manager_ = frame_sink_manager_impl;
+}
+
+void HostFrameSinkManager::BindAndSetManager(
+ cc::mojom::FrameSinkManagerClientRequest request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ cc::mojom::FrameSinkManagerPtr ptr) {
+ DCHECK(!frame_sink_manager_impl_);
+ DCHECK(!binding_.is_bound());
+
+ binding_.Bind(std::move(request), std::move(task_runner));
+ frame_sink_manager_ptr_ = std::move(ptr);
+
+ frame_sink_manager_ = frame_sink_manager_ptr_.get();
+}
+
+void HostFrameSinkManager::AddObserver(FrameSinkObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void HostFrameSinkManager::RemoveObserver(FrameSinkObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void HostFrameSinkManager::CreateCompositorFrameSink(
+ const FrameSinkId& frame_sink_id,
+ cc::mojom::CompositorFrameSinkRequest request,
+ cc::mojom::CompositorFrameSinkClientPtr client) {
+ DCHECK_EQ(frame_sink_data_map_.count(frame_sink_id), 0u);
+
+ FrameSinkData& data = frame_sink_data_map_[frame_sink_id];
+ data.is_root = false;
+
+ frame_sink_manager_->CreateCompositorFrameSink(
+ frame_sink_id, std::move(request),
+ mojo::MakeRequest(&data.private_interface), std::move(client));
+}
+
+void HostFrameSinkManager::DestroyCompositorFrameSink(
+ const FrameSinkId& frame_sink_id) {
+ auto iter = frame_sink_data_map_.find(frame_sink_id);
+ DCHECK(iter != frame_sink_data_map_.end());
+
+ FrameSinkData& data = iter->second;
+ if (data.parent.has_value())
+ UnregisterFrameSinkHierarchy(data.parent.value(), frame_sink_id);
+
+ // This destroys the CompositorFrameSinkPrivatePtr and closes the pipe.
+ frame_sink_data_map_.erase(iter);
+}
+
+void HostFrameSinkManager::RegisterFrameSinkHierarchy(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) {
+ DCHECK_EQ(frame_sink_data_map_.count(child_frame_sink_id), 1u);
+
+ // Register and store the parent, either directly or over Mojo.
+ frame_sink_manager_->RegisterFrameSinkHierarchy(parent_frame_sink_id,
+ child_frame_sink_id);
+
+ frame_sink_data_map_[child_frame_sink_id].parent = parent_frame_sink_id;
+}
+
+void HostFrameSinkManager::UnregisterFrameSinkHierarchy(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) {
+ auto iter = frame_sink_data_map_.find(child_frame_sink_id);
+ DCHECK(iter != frame_sink_data_map_.end());
+
+ FrameSinkData& data = iter->second;
+ DCHECK_EQ(data.parent.value(), parent_frame_sink_id);
+
+ // Unregister and clear the stored parent, either directly or over Mojo.
+ frame_sink_manager_->UnregisterFrameSinkHierarchy(parent_frame_sink_id,
+ child_frame_sink_id);
+
+ data.parent.reset();
+}
+
+std::unique_ptr<CompositorFrameSinkSupport>
+HostFrameSinkManager::CreateCompositorFrameSinkSupport(
+ CompositorFrameSinkSupportClient* client,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_points) {
+ DCHECK(frame_sink_manager_impl_);
+ DCHECK_EQ(frame_sink_data_map_.count(frame_sink_id), 0u);
+
+ auto support = CompositorFrameSinkSupport::Create(
+ client, frame_sink_manager_impl_, frame_sink_id, is_root,
+ handles_frame_sink_id_invalidation, needs_sync_points);
+ support->SetDestructionCallback(
+ base::BindOnce(&HostFrameSinkManager::DestroyCompositorFrameSink,
+ weak_ptr_factory_.GetWeakPtr(), frame_sink_id));
+
+ FrameSinkData& data = frame_sink_data_map_[frame_sink_id];
+ data.support = support.get();
+ data.is_root = is_root;
+
+ return support;
+}
+
+void HostFrameSinkManager::OnSurfaceCreated(const SurfaceInfo& surface_info) {
+ for (auto& observer : observers_)
+ observer.OnSurfaceCreated(surface_info);
+}
+
+void HostFrameSinkManager::OnClientConnectionClosed(
+ const FrameSinkId& frame_sink_id) {
+ // TODO(kylechar): Notify observers.
+}
+
+HostFrameSinkManager::FrameSinkData::FrameSinkData() = default;
+
+HostFrameSinkManager::FrameSinkData::FrameSinkData(FrameSinkData&& other) =
+ default;
+
+HostFrameSinkManager::FrameSinkData::~FrameSinkData() = default;
+
+HostFrameSinkManager::FrameSinkData& HostFrameSinkManager::FrameSinkData::
+operator=(FrameSinkData&& other) = default;
+
+} // namespace viz
diff --git a/chromium/components/viz/host/host_frame_sink_manager.h b/chromium/components/viz/host/host_frame_sink_manager.h
new file mode 100644
index 00000000000..bcfd9faeffe
--- /dev/null
+++ b/chromium/components/viz/host/host_frame_sink_manager.h
@@ -0,0 +1,157 @@
+// 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_FRAME_SINK_MANAGER_H_
+#define COMPONENTS_VIZ_HOST_HOST_FRAME_SINK_MANAGER_H_
+
+#include "base/compiler_specific.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "cc/ipc/frame_sink_manager.mojom.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/host/frame_sink_observer.h"
+#include "components/viz/host/viz_host_export.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace cc {
+class SurfaceInfo;
+} // namespace cc
+
+namespace viz {
+
+class CompositorFrameSinkSupport;
+class CompositorFrameSinkSupportClient;
+class FrameSinkManagerImpl;
+
+namespace test {
+class HostFrameSinkManagerTest;
+}
+
+// Browser side wrapper of mojom::FrameSinkManager, to be used from the
+// UI thread. Manages frame sinks and is intended to replace SurfaceManager.
+class VIZ_HOST_EXPORT HostFrameSinkManager
+ : public NON_EXPORTED_BASE(cc::mojom::FrameSinkManagerClient),
+ public NON_EXPORTED_BASE(CompositorFrameSinkSupportManager) {
+ public:
+ HostFrameSinkManager();
+ ~HostFrameSinkManager() override;
+
+ // Sets a local FrameSinkManagerImpl instance and connects directly to it.
+ void SetLocalManager(FrameSinkManagerImpl* frame_sink_manager_impl);
+
+ // Binds |this| as a FrameSinkManagerClient for |request| on |task_runner|. On
+ // Mac |task_runner| will be the resize helper task runner. May only be called
+ // once.
+ void BindAndSetManager(
+ cc::mojom::FrameSinkManagerClientRequest request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ cc::mojom::FrameSinkManagerPtr ptr);
+
+ void AddObserver(FrameSinkObserver* observer);
+ void RemoveObserver(FrameSinkObserver* observer);
+
+ // Creates a connection between client to viz, using |request| and |client|,
+ // that allows the client to submit CompositorFrames. When no longer needed,
+ // call DestroyCompositorFrameSink().
+ void CreateCompositorFrameSink(
+ const FrameSinkId& frame_sink_id,
+ cc::mojom::CompositorFrameSinkRequest request,
+ cc::mojom::CompositorFrameSinkClientPtr client);
+
+ // Destroys a client connection. Will call UnregisterFrameSinkHierarchy() with
+ // the registered parent if there is one.
+ void DestroyCompositorFrameSink(const FrameSinkId& frame_sink_id);
+
+ // Registers FrameSink hierarchy. Clients can call this multiple times to
+ // reparent without calling UnregisterFrameSinkHierarchy(). If a client uses
+ // CompositorFrameSink, then CreateCompositorFrameSink() should be called
+ // before this.
+ void RegisterFrameSinkHierarchy(const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id);
+
+ // Unregisters FrameSink hierarchy. Client must have registered FrameSink
+ // hierarchy before unregistering.
+ void UnregisterFrameSinkHierarchy(const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id);
+
+ // CompositorFrameSinkSupportManager:
+ std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
+ CompositorFrameSinkSupportClient* client,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_points) override;
+
+ private:
+ friend class test::HostFrameSinkManagerTest;
+
+ struct FrameSinkData {
+ FrameSinkData();
+ FrameSinkData(FrameSinkData&& other);
+ ~FrameSinkData();
+ FrameSinkData& operator=(FrameSinkData&& other);
+
+ // If the frame sink is a root that corresponds to a Display.
+ bool is_root = false;
+
+ // The FrameSinkId registered as the parent in the BeginFrame hierarchy.
+ // This mirrors state in viz.
+ base::Optional<FrameSinkId> parent;
+
+ // The private interface that gives the host control over the
+ // CompositorFrameSink connection between the client and viz. This will be
+ // unbound if not using Mojo.
+ cc::mojom::CompositorFrameSinkPrivatePtr private_interface;
+
+ // This will be null if using Mojo.
+ CompositorFrameSinkSupport* support = nullptr;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FrameSinkData);
+ };
+
+ // cc::mojom::FrameSinkManagerClient:
+ void OnSurfaceCreated(const SurfaceInfo& surface_info) override;
+ void OnClientConnectionClosed(const FrameSinkId& frame_sink_id) override;
+
+ // This will point to |frame_sink_manager_ptr_| if using Mojo or
+ // |frame_sink_manager_impl_| if directly connected. Use this to make function
+ // calls.
+ cc::mojom::FrameSinkManager* frame_sink_manager_ = nullptr;
+
+ // Mojo connection to the FrameSinkManager. If this is bound then
+ // |frame_sink_manager_impl_| must be null.
+ cc::mojom::FrameSinkManagerPtr frame_sink_manager_ptr_;
+
+ // Mojo connection back from the FrameSinkManager.
+ mojo::Binding<cc::mojom::FrameSinkManagerClient> binding_;
+
+ // A direct connection to FrameSinkManagerImpl. If this is set then
+ // |frame_sink_manager_ptr_| must be unbound. For use in browser process only,
+ // viz process should not set this.
+ FrameSinkManagerImpl* frame_sink_manager_impl_ = nullptr;
+
+ // Per CompositorFrameSink data.
+ base::flat_map<FrameSinkId, FrameSinkData> frame_sink_data_map_;
+
+ // Local observers to that receive OnSurfaceCreated() messages from IPC.
+ base::ObserverList<FrameSinkObserver> observers_;
+
+ base::WeakPtrFactory<HostFrameSinkManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostFrameSinkManager);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_HOST_HOST_FRAME_SINK_MANAGER_H_
diff --git a/chromium/components/viz/host/host_frame_sink_manager_unittests.cc b/chromium/components/viz/host/host_frame_sink_manager_unittests.cc
new file mode 100644
index 00000000000..84e288351cf
--- /dev/null
+++ b/chromium/components/viz/host/host_frame_sink_manager_unittests.cc
@@ -0,0 +1,142 @@
+// 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_frame_sink_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "cc/ipc/frame_sink_manager.mojom.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+namespace test {
+namespace {
+
+constexpr FrameSinkId kFrameSinkId1(1, 1);
+constexpr FrameSinkId kFrameSinkId2(2, 1);
+
+ACTION_P(InvokeClosure, closure) {
+ closure.Run();
+}
+
+// A stub CompositorFrameSinkClient that does nothing.
+class StubCompositorFrameSinkClient
+ : public cc::mojom::CompositorFrameSinkClient {
+ public:
+ StubCompositorFrameSinkClient() : binding_(this) {}
+ ~StubCompositorFrameSinkClient() override = default;
+
+ cc::mojom::CompositorFrameSinkClientPtr GetInterfacePtr() {
+ cc::mojom::CompositorFrameSinkClientPtr client;
+ binding_.Bind(mojo::MakeRequest(&client));
+ return client;
+ }
+
+ private:
+ // cc::mojom::CompositorFrameSinkClient:
+ void DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) override {}
+ void OnBeginFrame(const cc::BeginFrameArgs& begin_frame_args) override {}
+ void OnBeginFramePausedChanged(bool paused) override {}
+ void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) override {}
+
+ mojo::Binding<cc::mojom::CompositorFrameSinkClient> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(StubCompositorFrameSinkClient);
+};
+
+// A mock implementation of mojom::FrameSinkManager.
+class MockFrameSinkManagerImpl : public FrameSinkManagerImpl {
+ public:
+ MockFrameSinkManagerImpl() = default;
+ ~MockFrameSinkManagerImpl() override = default;
+
+ // cc::mojom::FrameSinkManager:
+ MOCK_METHOD2(RegisterFrameSinkHierarchy,
+ void(const FrameSinkId& parent, const FrameSinkId& child));
+ MOCK_METHOD2(UnregisterFrameSinkHierarchy,
+ void(const FrameSinkId& parent, const FrameSinkId& child));
+ MOCK_METHOD1(DropTemporaryReference, void(const SurfaceId& surface_id));
+
+ // TODO(kylechar): See if we can mock functions with InterfacePtr parameters.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockFrameSinkManagerImpl);
+};
+
+} // namespace
+
+class HostFrameSinkManagerTest : public testing::Test {
+ public:
+ HostFrameSinkManagerTest() = default;
+ ~HostFrameSinkManagerTest() override = default;
+
+ HostFrameSinkManager& host_manager() { return *host_manager_; }
+
+ MockFrameSinkManagerImpl& manager_impl() { return *manager_impl_; }
+
+ bool FrameSinkIdExists(const FrameSinkId& frame_sink_id) {
+ return host_manager_->frame_sink_data_map_.count(frame_sink_id) > 0;
+ }
+
+ // testing::Test:
+ void SetUp() override {
+ manager_impl_ = base::MakeUnique<MockFrameSinkManagerImpl>();
+ host_manager_ = base::MakeUnique<HostFrameSinkManager>();
+
+ manager_impl_->SetLocalClient(host_manager_.get());
+ host_manager_->SetLocalManager(manager_impl_.get());
+ }
+
+ private:
+ std::unique_ptr<HostFrameSinkManager> host_manager_;
+ std::unique_ptr<MockFrameSinkManagerImpl> manager_impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostFrameSinkManagerTest);
+};
+
+// Verify that when destroying a CompositorFrameSink with registered FrameSink
+// hierarchy, the hierarchy is automatically unregistered.
+TEST_F(HostFrameSinkManagerTest, UnregisterHierarchyOnDestroy) {
+ // Register is called explicitly.
+ EXPECT_CALL(manager_impl(),
+ RegisterFrameSinkHierarchy(kFrameSinkId2, kFrameSinkId1));
+
+ // Unregister should be called when DestroyCompositorFrameSink() is called.
+ EXPECT_CALL(manager_impl(),
+ UnregisterFrameSinkHierarchy(kFrameSinkId2, kFrameSinkId1));
+
+ cc::mojom::CompositorFrameSinkPtr frame_sink;
+ StubCompositorFrameSinkClient frame_sink_client;
+ host_manager().CreateCompositorFrameSink(kFrameSinkId1,
+ mojo::MakeRequest(&frame_sink),
+ frame_sink_client.GetInterfacePtr());
+ host_manager().RegisterFrameSinkHierarchy(kFrameSinkId2, kFrameSinkId1);
+ host_manager().DestroyCompositorFrameSink(kFrameSinkId1);
+}
+
+// Checks that creating a CompositorFrameSinkSupport registers it and destroying
+// the CompositorFrameSinkSupport unregisters it.
+TEST_F(HostFrameSinkManagerTest, CreateDestroyCompositorFrameSinkSupport) {
+ auto support = host_manager().CreateCompositorFrameSinkSupport(
+ nullptr /* client */, kFrameSinkId1, true /* is_root */,
+ true /* handles_frame_sink_id_invalidation */,
+ false /* needs_sync_points */);
+ EXPECT_TRUE(FrameSinkIdExists(kFrameSinkId1));
+
+ support.reset();
+ EXPECT_FALSE(FrameSinkIdExists(kFrameSinkId1));
+}
+
+} // namespace test
+} // namespace viz
diff --git a/chromium/components/viz/host/server_gpu_memory_buffer_manager.cc b/chromium/components/viz/host/server_gpu_memory_buffer_manager.cc
new file mode 100644
index 00000000000..afbc5e144ad
--- /dev/null
+++ b/chromium/components/viz/host/server_gpu_memory_buffer_manager.cc
@@ -0,0 +1,232 @@
+// 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 "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "gpu/ipc/client/gpu_memory_buffer_impl.h"
+#include "gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "services/ui/gpu/interfaces/gpu_service.mojom.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/gpu_memory_buffer_tracing.h"
+
+namespace viz {
+
+ServerGpuMemoryBufferManager::BufferInfo::BufferInfo() = default;
+ServerGpuMemoryBufferManager::BufferInfo::~BufferInfo() = default;
+
+ServerGpuMemoryBufferManager::ServerGpuMemoryBufferManager(
+ ui::mojom::GpuService* gpu_service,
+ int client_id)
+ : gpu_service_(gpu_service),
+ client_id_(client_id),
+ native_configurations_(gpu::GetNativeGpuMemoryBufferConfigurations()),
+ task_runner_(base::SequencedTaskRunnerHandle::Get()),
+ weak_factory_(this) {}
+
+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::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::Bind(&ServerGpuMemoryBufferManager::OnGpuMemoryBufferAllocated,
+ weak_factory_.GetWeakPtr(), client_id,
+ gfx::BufferSizeForBufferFormat(size, format),
+ base::Passed(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);
+ 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;
+ return gpu::GpuMemoryBufferImpl::CreateFromHandle(
+ handle, size, format, usage,
+ base::Bind(&ServerGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
+ weak_factory_.GetWeakPtr(), 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) {
+ auto shared_buffer_guid = gfx::GetSharedMemoryGUIDForTracing(
+ client_tracing_process_id, buffer_id);
+ pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_buffer_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(
+ reinterpret_cast<const char*>(&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
new file mode 100644
index 00000000000..5ae16d953ef
--- /dev/null
+++ b/chromium/components/viz/host/server_gpu_memory_buffer_manager.h
@@ -0,0 +1,103 @@
+// 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/sequenced_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 ui {
+namespace mojom {
+class GpuService;
+}
+} // namespace ui
+
+namespace viz {
+
+// 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(ui::mojom::GpuService* gpu_service,
+ int client_id);
+ ~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);
+
+ ui::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_;
+
+ const gpu::GpuMemoryBufferConfigurationSet native_configurations_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ 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/host/server_gpu_memory_buffer_manager_unittest.cc b/chromium/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
new file mode 100644
index 00000000000..6737f18f795
--- /dev/null
+++ b/chromium/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
@@ -0,0 +1,288 @@
+// 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/server_gpu_memory_buffer_manager.h"
+
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "gpu/ipc/host/gpu_memory_buffer_support.h"
+#include "services/ui/gpu/interfaces/gpu_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/client_native_pixmap_factory.h"
+
+namespace viz {
+
+namespace {
+
+class TestGpuService : public ui::mojom::GpuService {
+ public:
+ TestGpuService() {}
+ ~TestGpuService() override {}
+
+ bool HasAllocationRequest(gfx::GpuMemoryBufferId id, int client_id) const {
+ for (const auto& req : allocation_requests_) {
+ if (req.id == id && req.client_id == client_id)
+ return true;
+ }
+ return false;
+ }
+
+ bool HasDestructionRequest(gfx::GpuMemoryBufferId id, int client_id) const {
+ for (const auto& req : destruction_requests_) {
+ if (req.id == id && req.client_id == client_id)
+ return true;
+ }
+ return false;
+ }
+
+ void SatisfyAllocationRequest(gfx::GpuMemoryBufferId id, int client_id) {
+ for (const auto& req : allocation_requests_) {
+ if (req.id == id && req.client_id == client_id) {
+ gfx::GpuMemoryBufferHandle handle;
+ handle.id = id;
+ handle.type = gfx::SHARED_MEMORY_BUFFER;
+ req.callback.Run(handle);
+ return;
+ }
+ }
+ NOTREACHED();
+ }
+
+ // ui::mojom::GpuService:
+ void EstablishGpuChannel(
+ int32_t client_id,
+ uint64_t client_tracing_id,
+ bool is_gpu_host,
+ const EstablishGpuChannelCallback& callback) override {}
+
+ void CloseChannel(int32_t client_id) override {}
+
+ void CreateJpegDecodeAccelerator(
+ media::mojom::GpuJpegDecodeAcceleratorRequest jda_request) override {}
+
+ void CreateVideoEncodeAccelerator(
+ media::mojom::VideoEncodeAcceleratorRequest vea_request) override {}
+
+ void CreateGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ int client_id,
+ gpu::SurfaceHandle surface_handle,
+ const CreateGpuMemoryBufferCallback& callback) override {
+ allocation_requests_.push_back(
+ {id, size, format, usage, client_id, callback});
+ }
+
+ void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gpu::SyncToken& sync_token) override {
+ destruction_requests_.push_back({id, client_id});
+ }
+
+ void GetVideoMemoryUsageStats(
+ const GetVideoMemoryUsageStatsCallback& callback) override {}
+
+ void RequestCompleteGpuInfo(
+ const RequestCompleteGpuInfoCallback& callback) override {}
+
+ void LoadedShader(const std::string& key, const std::string& data) override {}
+
+ void DestroyingVideoSurface(
+ int32_t surface_id,
+ const DestroyingVideoSurfaceCallback& callback) override {}
+
+ void WakeUpGpu() override {}
+
+ void GpuSwitched() override {}
+
+ void DestroyAllChannels() override {}
+
+ void Crash() override {}
+
+ void Hang() override {}
+
+ void ThrowJavaException() override {}
+
+ void Stop(const StopCallback& callback) override {}
+
+ private:
+ struct AllocationRequest {
+ const gfx::GpuMemoryBufferId id;
+ const gfx::Size size;
+ const gfx::BufferFormat format;
+ const gfx::BufferUsage usage;
+ const int client_id;
+ const CreateGpuMemoryBufferCallback callback;
+ };
+ std::vector<AllocationRequest> allocation_requests_;
+
+ struct DestructionRequest {
+ const gfx::GpuMemoryBufferId id;
+ const int client_id;
+ };
+ std::vector<DestructionRequest> destruction_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestGpuService);
+};
+
+// It is necessary to install a custom pixmap factory which claims to support
+// all native configurations, so that code that deals with this can be tested
+// correctly.
+class FakeClientNativePixmapFactory : public gfx::ClientNativePixmapFactory {
+ public:
+ FakeClientNativePixmapFactory() {}
+ ~FakeClientNativePixmapFactory() override {}
+
+ // gfx::ClientNativePixmapFactory:
+ bool IsConfigurationSupported(gfx::BufferFormat format,
+ gfx::BufferUsage usage) const override {
+ return true;
+ }
+ std::unique_ptr<gfx::ClientNativePixmap> ImportFromHandle(
+ const gfx::NativePixmapHandle& handle,
+ const gfx::Size& size,
+ gfx::BufferUsage usage) override {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeClientNativePixmapFactory);
+};
+
+} // namespace
+
+class ServerGpuMemoryBufferManagerTest : public ::testing::Test {
+ public:
+ ServerGpuMemoryBufferManagerTest() = default;
+ ~ServerGpuMemoryBufferManagerTest() override = default;
+
+ // ::testing::Test:
+ void SetUp() override {
+ gfx::ClientNativePixmapFactory::ResetInstance();
+ gfx::ClientNativePixmapFactory::SetInstance(&pixmap_factory_);
+ }
+
+ void TearDown() override { gfx::ClientNativePixmapFactory::ResetInstance(); }
+
+ private:
+ FakeClientNativePixmapFactory pixmap_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerGpuMemoryBufferManagerTest);
+};
+
+// Tests that allocation requests from a client that goes away before allocation
+// completes are cleaned up correctly.
+TEST_F(ServerGpuMemoryBufferManagerTest, AllocationRequestsForDestroyedClient) {
+#if !defined(USE_OZONE) && !defined(OS_MACOSX)
+ // Not all platforms support native configurations (currently only ozone and
+ // mac support it). Abort the test in those platforms.
+ DCHECK(gpu::GetNativeGpuMemoryBufferConfigurations().empty());
+ return;
+#else
+ // Note: ServerGpuMemoryBufferManager 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;
+ ServerGpuMemoryBufferManager manager(&gpu_service, 1);
+
+ 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) {}));
+ EXPECT_TRUE(gpu_service.HasAllocationRequest(buffer_id, client_id));
+ EXPECT_FALSE(gpu_service.HasDestructionRequest(buffer_id, client_id));
+
+ // Destroy the client. Since no memory has been allocated yet, there will be
+ // no request for freeing memory.
+ manager.DestroyAllGpuMemoryBufferForClient(client_id);
+ EXPECT_TRUE(gpu_service.HasAllocationRequest(buffer_id, client_id));
+ EXPECT_FALSE(gpu_service.HasDestructionRequest(buffer_id, client_id));
+
+ // When the host receives the allocated memory for the destroyed client, it
+ // should request the allocated memory to be freed.
+ gpu_service.SatisfyAllocationRequest(buffer_id, client_id);
+ EXPECT_TRUE(gpu_service.HasDestructionRequest(buffer_id, client_id));
+#endif
+}
+
+TEST_F(ServerGpuMemoryBufferManagerTest,
+ RequestsFromUntrustedClientsValidated) {
+ gfx::ClientNativePixmapFactory::ResetInstance();
+ TestGpuService gpu_service;
+ ServerGpuMemoryBufferManager manager(&gpu_service, 1);
+ 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.
+ // When ATC is used, both width and height should be multiples of 4.
+ struct {
+ gfx::BufferUsage usage;
+ gfx::BufferFormat format;
+ gfx::Size size;
+ bool expect_null_handle;
+ } configs[] = {
+ {gfx::BufferUsage::SCANOUT, gfx::BufferFormat::YVU_420, {10, 20}, true},
+ {gfx::BufferUsage::GPU_READ, gfx::BufferFormat::ATC, {10, 20}, true},
+ {gfx::BufferUsage::GPU_READ, gfx::BufferFormat::YVU_420, {64, 64}, false},
+ };
+ for (const auto& config : configs) {
+ gfx::GpuMemoryBufferHandle allocated_handle;
+ base::RunLoop runloop;
+ manager.AllocateGpuMemoryBuffer(
+ buffer_id, client_id, config.size, config.format, config.usage,
+ gpu::kNullSurfaceHandle,
+ base::BindOnce(
+ [](gfx::GpuMemoryBufferHandle* allocated_handle,
+ const base::Closure& callback,
+ const gfx::GpuMemoryBufferHandle& handle) {
+ *allocated_handle = handle;
+ callback.Run();
+ },
+ &allocated_handle, runloop.QuitClosure()));
+ // Since native gpu memory buffers are not supported, the mojom.GpuService
+ // should not receive any allocation requests.
+ EXPECT_FALSE(gpu_service.HasAllocationRequest(buffer_id, client_id));
+ runloop.Run();
+ if (config.expect_null_handle) {
+ EXPECT_TRUE(allocated_handle.is_null());
+ } else {
+ EXPECT_FALSE(allocated_handle.is_null());
+ EXPECT_EQ(gfx::GpuMemoryBufferType::SHARED_MEMORY_BUFFER,
+ allocated_handle.type);
+ }
+ }
+}
+
+TEST_F(ServerGpuMemoryBufferManagerTest, GpuMemoryBufferDestroyed) {
+ gfx::ClientNativePixmapFactory::ResetInstance();
+ TestGpuService gpu_service;
+ ServerGpuMemoryBufferManager manager(&gpu_service, 1);
+ base::Thread diff_thread("TestThread");
+ ASSERT_TRUE(diff_thread.Start());
+ std::unique_ptr<gfx::GpuMemoryBuffer> buffer;
+ base::RunLoop run_loop;
+ ASSERT_TRUE(diff_thread.task_runner()->PostTask(
+ FROM_HERE, base::Bind(
+ [](ServerGpuMemoryBufferManager* manager,
+ std::unique_ptr<gfx::GpuMemoryBuffer>* out_buffer,
+ const base::Closure& callback) {
+ *out_buffer = manager->CreateGpuMemoryBuffer(
+ gfx::Size(64, 64), gfx::BufferFormat::YVU_420,
+ gfx::BufferUsage::GPU_READ, gpu::kNullSurfaceHandle);
+ callback.Run();
+ },
+ &manager, &buffer, run_loop.QuitClosure())));
+ run_loop.Run();
+ EXPECT_TRUE(buffer);
+ buffer.reset();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/host/viz_host_export.h b/chromium/components/viz/host/viz_host_export.h
new file mode 100644
index 00000000000..39846c1fbdc
--- /dev/null
+++ b/chromium/components/viz/host/viz_host_export.h
@@ -0,0 +1,29 @@
+// 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_VIZ_HOST_EXPORT_H_
+#define COMPONENTS_VIZ_HOST_VIZ_HOST_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(VIZ_HOST_IMPLEMENTATION)
+#define VIZ_HOST_EXPORT __declspec(dllexport)
+#else
+#define VIZ_HOST_EXPORT __declspec(dllimport)
+#endif // defined(VIZ_HOST_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(VIZ_HOST_IMPLEMENTATION)
+#define VIZ_HOST_EXPORT __attribute__((visibility("default")))
+#else
+#define VIZ_HOST_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define VIZ_HOST_EXPORT
+#endif
+
+#endif // COMPONENTS_VIZ_HOST_VIZ_HOST_EXPORT_H_
diff --git a/chromium/components/viz/service/BUILD.gn b/chromium/components/viz/service/BUILD.gn
new file mode 100644
index 00000000000..22a0b3cdc14
--- /dev/null
+++ b/chromium/components/viz/service/BUILD.gn
@@ -0,0 +1,184 @@
+# 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.
+
+import("//build/config/ui.gni")
+import("//components/viz/viz.gni")
+
+config("viz_service_implementation") {
+}
+
+viz_component("service") {
+ sources = [
+ "display/display.cc",
+ "display/display.h",
+ "display/display_client.h",
+ "display/display_scheduler.cc",
+ "display/display_scheduler.h",
+ "display/surface_aggregator.cc",
+ "display/surface_aggregator.h",
+ "display_embedder/buffer_queue.cc",
+ "display_embedder/buffer_queue.h",
+ "display_embedder/compositor_overlay_candidate_validator.h",
+ "display_embedder/display_output_surface.cc",
+ "display_embedder/display_output_surface.h",
+ "display_embedder/display_provider.h",
+ "display_embedder/gpu_display_provider.cc",
+ "display_embedder/gpu_display_provider.h",
+ "display_embedder/in_process_gpu_memory_buffer_manager.cc",
+ "display_embedder/in_process_gpu_memory_buffer_manager.h",
+ "display_embedder/server_shared_bitmap_manager.cc",
+ "display_embedder/server_shared_bitmap_manager.h",
+ "display_embedder/shared_bitmap_allocation_notifier_impl.cc",
+ "display_embedder/shared_bitmap_allocation_notifier_impl.h",
+ "frame_sinks/compositor_frame_sink_support.cc",
+ "frame_sinks/compositor_frame_sink_support.h",
+ "frame_sinks/compositor_frame_sink_support_client.h",
+ "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/frame_eviction_manager.cc",
+ "frame_sinks/frame_eviction_manager.h",
+ "frame_sinks/frame_evictor.cc",
+ "frame_sinks/frame_evictor.h",
+ "frame_sinks/frame_sink_manager_client.h",
+ "frame_sinks/frame_sink_manager_impl.cc",
+ "frame_sinks/frame_sink_manager_impl.h",
+ "frame_sinks/gpu_compositor_frame_sink.cc",
+ "frame_sinks/gpu_compositor_frame_sink.h",
+ "frame_sinks/gpu_root_compositor_frame_sink.cc",
+ "frame_sinks/gpu_root_compositor_frame_sink.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/surface_resource_holder.cc",
+ "frame_sinks/surface_resource_holder.h",
+ "frame_sinks/surface_resource_holder_client.h",
+ "hit_test/hit_test_aggregator.cc",
+ "hit_test/hit_test_aggregator.h",
+ "viz_service_export.h",
+ ]
+
+ defines = [ "VIZ_SERVICE_IMPLEMENTATION" ]
+
+ configs = [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+ deps = [
+ "//components/viz/common",
+
+ # Note that dependency on //gpu/ipc/client is for GpuMemoryBufferImpl. This
+ # dependency should not be in public_deps.
+ "//gpu/ipc/client",
+ "//gpu/ipc/service",
+ "//gpu/vulkan:features",
+ "//skia",
+ "//ui/display/types",
+ ]
+
+ public_deps = [
+ "//base",
+ "//cc",
+ "//cc/ipc:interfaces",
+ "//cc/surfaces",
+ "//gpu/command_buffer/client:gles2_interface",
+ "//gpu/ipc:command_buffer",
+ "//services/viz/hit_test/public/interfaces",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/latency",
+ ]
+
+ if (is_mac) {
+ sources += [
+ "display_embedder/compositor_overlay_candidate_validator_mac.h",
+ "display_embedder/compositor_overlay_candidate_validator_mac.mm",
+ ]
+ }
+
+ if (is_android) {
+ sources += [
+ "display_embedder/compositor_overlay_candidate_validator_android.cc",
+ "display_embedder/compositor_overlay_candidate_validator_android.h",
+ ]
+ }
+
+ if (use_ozone) {
+ sources += [
+ "display_embedder/compositor_overlay_candidate_validator_ozone.cc",
+ "display_embedder/compositor_overlay_candidate_validator_ozone.h",
+ "display_embedder/display_output_surface_ozone.cc",
+ "display_embedder/display_output_surface_ozone.h",
+ ]
+
+ public_deps += [ "//ui/ozone" ]
+ }
+
+ if (is_win) {
+ sources += [
+ "display_embedder/compositor_overlay_candidate_validator_win.cc",
+ "display_embedder/compositor_overlay_candidate_validator_win.h",
+ ]
+ }
+}
+
+viz_source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "display/display_scheduler_unittest.cc",
+ "display/display_unittest.cc",
+ "display/surface_aggregator_pixeltest.cc",
+ "display/surface_aggregator_unittest.cc",
+ "display_embedder/buffer_queue_unittest.cc",
+ "display_embedder/server_shared_bitmap_manager_unittest.cc",
+ "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",
+ "hit_test/hit_test_aggregator_unittest.cc",
+ ]
+
+ if (!use_aura && !is_mac) {
+ sources -= [ "display_embedder/buffer_queue_unittest.cc" ]
+ }
+
+ configs = [
+ "//build/config/compiler:no_size_t_to_int_warning",
+ "//third_party/khronos:khronos_headers",
+ ]
+
+ deps = [
+ ":service",
+ "//base",
+ "//base/test:test_support",
+ "//cc:test_support",
+ "//components/viz/common",
+ "//gpu/command_buffer/client",
+ "//gpu/command_buffer/client:gles2_implementation",
+ "//gpu/ipc:gl_in_process_context",
+ "//media",
+ "//services/viz/hit_test/public/interfaces",
+ "//skia",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//ui/display/types",
+ ]
+}
+
+viz_source_set("perf_tests") {
+ testonly = true
+ sources = [
+ "display/surface_aggregator_perftest.cc",
+ ]
+
+ deps = [
+ ":service",
+ "//base",
+ "//cc:test_support",
+ "//cc/base",
+ "//testing/gtest",
+ "//testing/perf",
+ ]
+}
diff --git a/chromium/components/viz/service/DEPS b/chromium/components/viz/service/DEPS
new file mode 100644
index 00000000000..19ccf893a16
--- /dev/null
+++ b/chromium/components/viz/service/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+cc",
+ "+components/viz/service",
+ "+third_party/skia",
+ "+ui/gfx",
+ "+ui/gfx/geometry",
+ "+ui/latency",
+]
diff --git a/chromium/components/viz/service/display/DEPS b/chromium/components/viz/service/display/DEPS
new file mode 100644
index 00000000000..e8ecaf1670f
--- /dev/null
+++ b/chromium/components/viz/service/display/DEPS
@@ -0,0 +1,24 @@
+include_rules = [
+ "+base",
+ "+cc/base",
+ "+cc/benchmarks",
+ "+cc/output",
+ "+cc/quads",
+ "+cc/resources",
+ "+cc/scheduler",
+ "+cc/surfaces",
+ "+gpu/command_buffer/client",
+ "+gpu/command_buffer/common",
+ "+gpu/vulkan",
+ "+third_party/skia",
+ "+ui/gfx",
+ "+ui/latency",
+]
+
+specific_include_rules = {
+ ".*_(unit|pixel|perf)test\.cc": [
+ "+cc/test",
+ "+components/viz/service/frame_sinks",
+ "+gpu/GLES2",
+ ],
+}
diff --git a/chromium/components/viz/service/display/README.md b/chromium/components/viz/service/display/README.md
new file mode 100644
index 00000000000..9d70d664198
--- /dev/null
+++ b/chromium/components/viz/service/display/README.md
@@ -0,0 +1,12 @@
+# display/
+
+This directory is the implementation of the Display Compositor.
+
+The Display Compositor combines frames submitted to the viz service through
+frame sinks, and combines them into a single gpu or software resource to be
+presented to the user.
+
+This directory is agnostic w.r.t. platform-specific presentation details, which
+are abstracted behind OutputSurface and SoftwareOutputDevice. Through these
+APIs, it supports either compositing gpu resources (textures) into a single
+texture/framebuffer, or software resources (bitmaps) into a single bitmap.
diff --git a/chromium/components/viz/service/display/display.cc b/chromium/components/viz/service/display/display.cc
new file mode 100644
index 00000000000..2af77eaa229
--- /dev/null
+++ b/chromium/components/viz/service/display/display.cc
@@ -0,0 +1,434 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display.h"
+
+#include <stddef.h>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/benchmarks/benchmark_instrumentation.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/direct_renderer.h"
+#include "cc/output/gl_renderer.h"
+#include "cc/output/software_renderer.h"
+#include "cc/output/texture_mailbox_deleter.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_manager.h"
+#include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/service/display/display_client.h"
+#include "components/viz/service/display/display_scheduler.h"
+#include "components/viz/service/display/surface_aggregator.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/vulkan/features.h"
+#include "ui/gfx/buffer_types.h"
+
+#if BUILDFLAG(ENABLE_VULKAN)
+#include "cc/output/vulkan_renderer.h"
+#endif
+
+namespace viz {
+
+Display::Display(
+ SharedBitmapManager* bitmap_manager,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ const RendererSettings& settings,
+ const FrameSinkId& frame_sink_id,
+ std::unique_ptr<cc::OutputSurface> output_surface,
+ std::unique_ptr<DisplayScheduler> scheduler,
+ std::unique_ptr<cc::TextureMailboxDeleter> texture_mailbox_deleter)
+ : bitmap_manager_(bitmap_manager),
+ gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+ settings_(settings),
+ frame_sink_id_(frame_sink_id),
+ output_surface_(std::move(output_surface)),
+ scheduler_(std::move(scheduler)),
+ texture_mailbox_deleter_(std::move(texture_mailbox_deleter)) {
+ DCHECK(output_surface_);
+ DCHECK(frame_sink_id_.is_valid());
+ if (scheduler_)
+ scheduler_->SetClient(this);
+}
+
+Display::~Display() {
+ // Only do this if Initialize() happened.
+ if (client_) {
+ if (auto* context = output_surface_->context_provider())
+ context->SetLostContextCallback(base::Closure());
+ if (scheduler_)
+ surface_manager_->RemoveObserver(scheduler_.get());
+ }
+ if (aggregator_) {
+ for (const auto& id_entry : aggregator_->previous_contained_surfaces()) {
+ cc::Surface* surface = surface_manager_->GetSurfaceForId(id_entry.first);
+ if (surface)
+ surface->RunDrawCallback();
+ }
+ }
+}
+
+void Display::Initialize(DisplayClient* client,
+ cc::SurfaceManager* surface_manager) {
+ DCHECK(client);
+ DCHECK(surface_manager);
+ client_ = client;
+ surface_manager_ = surface_manager;
+ if (scheduler_)
+ surface_manager_->AddObserver(scheduler_.get());
+
+ output_surface_->BindToClient(this);
+ InitializeRenderer();
+
+ if (auto* context = output_surface_->context_provider()) {
+ // This depends on assumptions that Display::Initialize will happen
+ // on the same callstack as the ContextProvider being created/initialized
+ // or else it could miss a callback before setting this.
+ context->SetLostContextCallback(base::Bind(
+ &Display::DidLoseContextProvider,
+ // Unretained is safe since the callback is unset in this class'
+ // destructor and is never posted.
+ base::Unretained(this)));
+ }
+}
+
+void Display::SetLocalSurfaceId(const LocalSurfaceId& id,
+ float device_scale_factor) {
+ if (current_surface_id_.local_surface_id() == id &&
+ device_scale_factor_ == device_scale_factor) {
+ return;
+ }
+
+ TRACE_EVENT0("viz", "Display::SetSurfaceId");
+ current_surface_id_ = SurfaceId(frame_sink_id_, id);
+ device_scale_factor_ = device_scale_factor;
+
+ UpdateRootSurfaceResourcesLocked();
+ if (scheduler_)
+ scheduler_->SetNewRootSurface(current_surface_id_);
+}
+
+void Display::SetVisible(bool visible) {
+ TRACE_EVENT1("viz", "Display::SetVisible", "visible", visible);
+ if (renderer_)
+ renderer_->SetVisible(visible);
+ if (scheduler_)
+ scheduler_->SetVisible(visible);
+ visible_ = visible;
+
+ if (!visible) {
+ // Damage tracker needs a full reset as renderer resources are dropped when
+ // not visible.
+ if (aggregator_ && current_surface_id_.is_valid())
+ aggregator_->SetFullDamageForSurface(current_surface_id_);
+ }
+}
+
+void Display::Resize(const gfx::Size& size) {
+ if (size == current_surface_size_)
+ return;
+
+ TRACE_EVENT0("viz", "Display::Resize");
+
+ // Need to ensure all pending swaps have executed before the window is
+ // resized, or D3D11 will scale the swap output.
+ if (settings_.finish_rendering_on_resize) {
+ if (!swapped_since_resize_ && scheduler_)
+ scheduler_->ForceImmediateSwapIfPossible();
+ if (swapped_since_resize_ && output_surface_ &&
+ output_surface_->context_provider())
+ output_surface_->context_provider()->ContextGL()->ShallowFinishCHROMIUM();
+ }
+ swapped_since_resize_ = false;
+ current_surface_size_ = size;
+ if (scheduler_)
+ scheduler_->DisplayResized();
+}
+
+void Display::SetColorSpace(const gfx::ColorSpace& blending_color_space,
+ const gfx::ColorSpace& device_color_space) {
+ blending_color_space_ = blending_color_space;
+ device_color_space_ = device_color_space;
+ if (aggregator_) {
+ aggregator_->SetOutputColorSpace(blending_color_space, device_color_space_);
+ }
+}
+
+void Display::SetOutputIsSecure(bool secure) {
+ if (secure == output_is_secure_)
+ return;
+ output_is_secure_ = secure;
+
+ if (aggregator_) {
+ aggregator_->set_output_is_secure(secure);
+ // Force a redraw.
+ if (current_surface_id_.is_valid())
+ aggregator_->SetFullDamageForSurface(current_surface_id_);
+ }
+}
+
+void Display::InitializeRenderer() {
+ // Not relevant for display compositor since it's not delegated.
+ constexpr bool delegated_sync_points_required = false;
+ resource_provider_ = base::MakeUnique<cc::ResourceProvider>(
+ output_surface_->context_provider(), bitmap_manager_,
+ gpu_memory_buffer_manager_, nullptr, delegated_sync_points_required,
+ settings_.enable_color_correct_rendering, settings_.resource_settings);
+
+ if (output_surface_->context_provider()) {
+ DCHECK(texture_mailbox_deleter_);
+ renderer_ = base::MakeUnique<cc::GLRenderer>(
+ &settings_, output_surface_.get(), resource_provider_.get(),
+ texture_mailbox_deleter_.get());
+ } else if (output_surface_->vulkan_context_provider()) {
+#if defined(ENABLE_VULKAN)
+ DCHECK(texture_mailbox_deleter_);
+ renderer_ = base::MakeUnique<cc::VulkanRenderer>(
+ &settings_, output_surface_.get(), resource_provider_.get(),
+ texture_mailbox_deleter_.get(), settings_.highp_threshold_min);
+#else
+ NOTREACHED();
+#endif
+ } else {
+ auto renderer = base::MakeUnique<cc::SoftwareRenderer>(
+ &settings_, output_surface_.get(), resource_provider_.get());
+ software_renderer_ = renderer.get();
+ renderer_ = std::move(renderer);
+ }
+
+ renderer_->Initialize();
+ renderer_->SetVisible(visible_);
+
+ // TODO(jbauman): Outputting an incomplete quad list doesn't work when using
+ // overlays.
+ bool output_partial_list = renderer_->use_partial_swap() &&
+ !output_surface_->GetOverlayCandidateValidator();
+ aggregator_.reset(new SurfaceAggregator(
+ surface_manager_, resource_provider_.get(), output_partial_list));
+ aggregator_->set_output_is_secure(output_is_secure_);
+ aggregator_->SetOutputColorSpace(blending_color_space_, device_color_space_);
+}
+
+void Display::UpdateRootSurfaceResourcesLocked() {
+ cc::Surface* surface = surface_manager_->GetSurfaceForId(current_surface_id_);
+ bool root_surface_resources_locked = !surface || !surface->HasActiveFrame();
+ if (scheduler_)
+ scheduler_->SetRootSurfaceResourcesLocked(root_surface_resources_locked);
+}
+
+void Display::DidLoseContextProvider() {
+ if (scheduler_)
+ scheduler_->OutputSurfaceLost();
+ // WARNING: The client may delete the Display in this method call. Do not
+ // make any additional references to members after this call.
+ client_->DisplayOutputSurfaceLost();
+}
+
+bool Display::DrawAndSwap() {
+ TRACE_EVENT0("viz", "Display::DrawAndSwap");
+
+ if (!current_surface_id_.is_valid()) {
+ TRACE_EVENT_INSTANT0("viz", "No root surface.", TRACE_EVENT_SCOPE_THREAD);
+ return false;
+ }
+
+ if (!output_surface_) {
+ TRACE_EVENT_INSTANT0("viz", "No output surface", TRACE_EVENT_SCOPE_THREAD);
+ return false;
+ }
+
+ base::ElapsedTimer aggregate_timer;
+ cc::CompositorFrame frame = aggregator_->Aggregate(current_surface_id_);
+ UMA_HISTOGRAM_COUNTS_1M("Compositing.SurfaceAggregator.AggregateUs",
+ aggregate_timer.Elapsed().InMicroseconds());
+
+ if (frame.render_pass_list.empty()) {
+ TRACE_EVENT_INSTANT0("viz", "Empty aggregated frame.",
+ TRACE_EVENT_SCOPE_THREAD);
+ return false;
+ }
+
+ // Run callbacks early to allow pipelining.
+ for (const auto& id_entry : aggregator_->previous_contained_surfaces()) {
+ cc::Surface* surface = surface_manager_->GetSurfaceForId(id_entry.first);
+ if (surface)
+ surface->RunDrawCallback();
+ }
+
+ frame.metadata.latency_info.insert(frame.metadata.latency_info.end(),
+ stored_latency_info_.begin(),
+ stored_latency_info_.end());
+ stored_latency_info_.clear();
+ bool have_copy_requests = false;
+ for (const auto& pass : frame.render_pass_list) {
+ have_copy_requests |= !pass->copy_requests.empty();
+ }
+
+ gfx::Size surface_size;
+ bool have_damage = false;
+ auto& last_render_pass = *frame.render_pass_list.back();
+ if (last_render_pass.output_rect.size() != current_surface_size_ &&
+ last_render_pass.damage_rect == last_render_pass.output_rect &&
+ !current_surface_size_.IsEmpty()) {
+ // Resize the output rect to the current surface size so that we won't
+ // skip the draw and so that the GL swap won't stretch the output.
+ last_render_pass.output_rect.set_size(current_surface_size_);
+ last_render_pass.damage_rect = last_render_pass.output_rect;
+ }
+ surface_size = last_render_pass.output_rect.size();
+ have_damage = !last_render_pass.damage_rect.size().IsEmpty();
+
+ bool size_matches = surface_size == current_surface_size_;
+ if (!size_matches)
+ TRACE_EVENT_INSTANT0("viz", "Size mismatch.", TRACE_EVENT_SCOPE_THREAD);
+
+ bool should_draw = have_copy_requests || (have_damage && size_matches);
+
+ // If the surface is suspended then the resources to be used by the draw are
+ // likely destroyed.
+ if (output_surface_->SurfaceIsSuspendForRecycle()) {
+ TRACE_EVENT_INSTANT0("viz", "Surface is suspended for recycle.",
+ TRACE_EVENT_SCOPE_THREAD);
+ should_draw = false;
+ }
+
+ client_->DisplayWillDrawAndSwap(should_draw, frame.render_pass_list);
+
+ if (should_draw) {
+ bool disable_image_filtering =
+ frame.metadata.is_resourceless_software_draw_with_scroll_or_animation;
+ if (software_renderer_) {
+ software_renderer_->SetDisablePictureQuadImageFiltering(
+ disable_image_filtering);
+ } else {
+ // This should only be set for software draws in synchronous compositor.
+ DCHECK(!disable_image_filtering);
+ }
+
+ base::ElapsedTimer draw_timer;
+ renderer_->DecideRenderPassAllocationsForFrame(frame.render_pass_list);
+ renderer_->DrawFrame(&frame.render_pass_list, device_scale_factor_,
+ current_surface_size_);
+ if (software_renderer_) {
+ UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.Software.DrawFrameUs",
+ draw_timer.Elapsed().InMicroseconds());
+ } else {
+ UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.GL.DrawFrameUs",
+ draw_timer.Elapsed().InMicroseconds());
+ }
+ } else {
+ TRACE_EVENT_INSTANT0("viz", "Draw skipped.", TRACE_EVENT_SCOPE_THREAD);
+ }
+
+ bool should_swap = should_draw && size_matches;
+ if (should_swap) {
+ swapped_since_resize_ = true;
+ for (auto& latency : frame.metadata.latency_info) {
+ TRACE_EVENT_WITH_FLOW1(
+ "input,benchmark", "LatencyInfo.Flow",
+ TRACE_ID_DONT_MANGLE(latency.trace_id()),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "Display::DrawAndSwap");
+ }
+ cc::benchmark_instrumentation::IssueDisplayRenderingStatsEvent();
+ renderer_->SwapBuffers(std::move(frame.metadata.latency_info));
+ if (scheduler_)
+ scheduler_->DidSwapBuffers();
+ } else {
+ if (have_damage && !size_matches)
+ aggregator_->SetFullDamageForSurface(current_surface_id_);
+ TRACE_EVENT_INSTANT0("viz", "Swap skipped.", TRACE_EVENT_SCOPE_THREAD);
+
+ // Do not store more that the allowed size.
+ if (ui::LatencyInfo::Verify(frame.metadata.latency_info,
+ "Display::DrawAndSwap")) {
+ stored_latency_info_.insert(stored_latency_info_.end(),
+ frame.metadata.latency_info.begin(),
+ frame.metadata.latency_info.end());
+ }
+
+ if (scheduler_) {
+ scheduler_->DidSwapBuffers();
+ scheduler_->DidReceiveSwapBuffersAck();
+ }
+ }
+
+ client_->DisplayDidDrawAndSwap();
+ return true;
+}
+
+void Display::DidReceiveSwapBuffersAck() {
+ if (scheduler_)
+ scheduler_->DidReceiveSwapBuffersAck();
+ if (renderer_)
+ renderer_->SwapBuffersComplete();
+}
+
+void Display::DidReceiveTextureInUseResponses(
+ const gpu::TextureInUseResponses& responses) {
+ if (renderer_)
+ renderer_->DidReceiveTextureInUseResponses(responses);
+}
+
+void Display::SetNeedsRedrawRect(const gfx::Rect& damage_rect) {
+ aggregator_->SetFullDamageForSurface(current_surface_id_);
+ if (scheduler_) {
+ cc::BeginFrameAck ack;
+ ack.has_damage = true;
+ scheduler_->ProcessSurfaceDamage(current_surface_id_, ack, true);
+ }
+}
+
+bool Display::SurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) {
+ bool display_damaged = false;
+ if (ack.has_damage) {
+ if (aggregator_ &&
+ aggregator_->previous_contained_surfaces().count(surface_id)) {
+ cc::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();
+ }
+ }
+
+ return display_damaged;
+}
+
+void Display::SurfaceDiscarded(const SurfaceId& surface_id) {
+ if (aggregator_)
+ aggregator_->ReleaseResources(surface_id);
+}
+
+bool Display::SurfaceHasUndrawnFrame(const SurfaceId& surface_id) const {
+ if (!surface_manager_)
+ return false;
+
+ cc::Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
+ if (!surface)
+ return false;
+
+ return surface->HasUndrawnActiveFrame();
+}
+
+const SurfaceId& Display::CurrentSurfaceId() {
+ return current_surface_id_;
+}
+
+void Display::ForceImmediateDrawAndSwapIfPossible() {
+ if (scheduler_)
+ scheduler_->ForceImmediateSwapIfPossible();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display/display.h b/chromium/components/viz/service/display/display.h
new file mode 100644
index 00000000000..33f4a3f0123
--- /dev/null
+++ b/chromium/components/viz/service/display/display.h
@@ -0,0 +1,135 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "cc/output/output_surface_client.h"
+#include "cc/resources/returned_resource.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/surface_manager.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/display/display_scheduler.h"
+#include "components/viz/service/display/surface_aggregator.h"
+#include "components/viz/service/viz_service_export.h"
+#include "gpu/command_buffer/common/texture_in_use_response.h"
+#include "ui/gfx/color_space.h"
+#include "ui/latency/latency_info.h"
+
+namespace cc {
+class DirectRenderer;
+class OutputSurface;
+class RendererSettings;
+class ResourceProvider;
+class SoftwareRenderer;
+class TextureMailboxDeleter;
+} // namespace cc
+
+namespace gpu {
+class GpuMemoryBufferManager;
+}
+
+namespace gfx {
+class Size;
+}
+
+namespace viz {
+
+class DisplayClient;
+class SharedBitmapManager;
+
+// A Display produces a surface that can be used to draw to a physical display
+// (OutputSurface). The client is responsible for creating and sizing the
+// surface IDs used to draw into the display and deciding when to draw.
+class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient,
+ public cc::OutputSurfaceClient {
+ public:
+ // The |begin_frame_source| and |scheduler| may be null (together). In that
+ // case, DrawAndSwap must be called externally when needed.
+ Display(SharedBitmapManager* bitmap_manager,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ const RendererSettings& settings,
+ const FrameSinkId& frame_sink_id,
+ std::unique_ptr<cc::OutputSurface> output_surface,
+ std::unique_ptr<DisplayScheduler> scheduler,
+ std::unique_ptr<cc::TextureMailboxDeleter> texture_mailbox_deleter);
+
+ ~Display() override;
+
+ void Initialize(DisplayClient* client, cc::SurfaceManager* surface_manager);
+
+ // device_scale_factor is used to communicate to the external window system
+ // what scale this was rendered at.
+ void SetLocalSurfaceId(const LocalSurfaceId& id, float device_scale_factor);
+ void SetVisible(bool visible);
+ void Resize(const gfx::Size& new_size);
+ void SetColorSpace(const gfx::ColorSpace& blending_color_space,
+ const gfx::ColorSpace& device_color_space);
+ void SetOutputIsSecure(bool secure);
+
+ const SurfaceId& CurrentSurfaceId();
+
+ // DisplaySchedulerClient implementation.
+ bool DrawAndSwap() override;
+ bool SurfaceHasUndrawnFrame(const SurfaceId& surface_id) const override;
+ bool SurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) override;
+ void SurfaceDiscarded(const SurfaceId& surface_id) override;
+
+ // OutputSurfaceClient implementation.
+ void SetNeedsRedrawRect(const gfx::Rect& damage_rect) override;
+ void DidReceiveSwapBuffersAck() override;
+ void DidReceiveTextureInUseResponses(
+ const gpu::TextureInUseResponses& responses) override;
+
+ bool has_scheduler() const { return !!scheduler_; }
+ cc::DirectRenderer* renderer_for_testing() const { return renderer_.get(); }
+ size_t stored_latency_info_size_for_testing() const {
+ return stored_latency_info_.size();
+ }
+
+ void ForceImmediateDrawAndSwapIfPossible();
+
+ private:
+ void InitializeRenderer();
+ void UpdateRootSurfaceResourcesLocked();
+ void DidLoseContextProvider();
+
+ SharedBitmapManager* const bitmap_manager_;
+ gpu::GpuMemoryBufferManager* const gpu_memory_buffer_manager_;
+ const RendererSettings settings_;
+
+ DisplayClient* client_ = nullptr;
+ cc::SurfaceManager* surface_manager_ = nullptr;
+ const FrameSinkId frame_sink_id_;
+ SurfaceId current_surface_id_;
+ gfx::Size current_surface_size_;
+ float device_scale_factor_ = 1.f;
+ gfx::ColorSpace blending_color_space_ = gfx::ColorSpace::CreateSRGB();
+ gfx::ColorSpace device_color_space_ = gfx::ColorSpace::CreateSRGB();
+ bool visible_ = false;
+ bool swapped_since_resize_ = false;
+ bool output_is_secure_ = false;
+
+ std::unique_ptr<cc::OutputSurface> output_surface_;
+ std::unique_ptr<DisplayScheduler> scheduler_;
+ std::unique_ptr<cc::ResourceProvider> resource_provider_;
+ std::unique_ptr<SurfaceAggregator> aggregator_;
+ std::unique_ptr<cc::TextureMailboxDeleter> texture_mailbox_deleter_;
+ std::unique_ptr<cc::DirectRenderer> renderer_;
+ cc::SoftwareRenderer* software_renderer_ = nullptr;
+ std::vector<ui::LatencyInfo> stored_latency_info_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Display);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_H_
diff --git a/chromium/components/viz/service/display/display_client.h b/chromium/components/viz/service/display/display_client.h
new file mode 100644
index 00000000000..b22a6938009
--- /dev/null
+++ b/chromium/components/viz/service/display/display_client.h
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_CLIENT_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_CLIENT_H_
+
+#include "cc/quads/render_pass.h"
+
+namespace viz {
+
+class DisplayClient {
+ public:
+ virtual ~DisplayClient() {}
+ virtual void DisplayOutputSurfaceLost() = 0;
+ virtual void DisplayWillDrawAndSwap(
+ bool will_draw_and_swap,
+ const cc::RenderPassList& render_passes) = 0;
+ virtual void DisplayDidDrawAndSwap() = 0;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_CLIENT_H_
diff --git a/chromium/components/viz/service/display/display_scheduler.cc b/chromium/components/viz/service/display/display_scheduler.cc
new file mode 100644
index 00000000000..647554967a2
--- /dev/null
+++ b/chromium/components/viz/service/display/display_scheduler.cc
@@ -0,0 +1,492 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display_scheduler.h"
+
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/output/output_surface.h"
+#include "components/viz/common/surfaces/surface_info.h"
+
+namespace viz {
+
+DisplayScheduler::DisplayScheduler(cc::BeginFrameSource* begin_frame_source,
+ base::SingleThreadTaskRunner* task_runner,
+ int max_pending_swaps,
+ bool wait_for_all_surfaces_before_draw)
+ : client_(nullptr),
+ begin_frame_source_(begin_frame_source),
+ task_runner_(task_runner),
+ inside_surface_damaged_(false),
+ visible_(false),
+ output_surface_lost_(false),
+ root_surface_resources_locked_(true),
+ inside_begin_frame_deadline_interval_(false),
+ needs_draw_(false),
+ expecting_root_surface_damage_because_of_resize_(false),
+ has_pending_surfaces_(false),
+ next_swap_id_(1),
+ pending_swaps_(0),
+ max_pending_swaps_(max_pending_swaps),
+ wait_for_all_surfaces_before_draw_(wait_for_all_surfaces_before_draw),
+ observing_begin_frame_source_(false),
+ weak_ptr_factory_(this) {
+ begin_frame_deadline_closure_ = base::Bind(
+ &DisplayScheduler::OnBeginFrameDeadline, weak_ptr_factory_.GetWeakPtr());
+}
+
+DisplayScheduler::~DisplayScheduler() {
+ StopObservingBeginFrames();
+}
+
+void DisplayScheduler::SetClient(DisplaySchedulerClient* client) {
+ client_ = client;
+}
+
+void DisplayScheduler::SetVisible(bool visible) {
+ if (visible_ == visible)
+ return;
+
+ visible_ = visible;
+ // If going invisible, we'll stop observing begin frames once we try
+ // to draw and fail.
+ StartObservingBeginFrames();
+ ScheduleBeginFrameDeadline();
+}
+
+// If we try to draw when the root surface resources are locked, the
+// draw will fail.
+void DisplayScheduler::SetRootSurfaceResourcesLocked(bool locked) {
+ TRACE_EVENT1("viz", "DisplayScheduler::SetRootSurfaceResourcesLocked",
+ "locked", locked);
+ root_surface_resources_locked_ = locked;
+ ScheduleBeginFrameDeadline();
+}
+
+// This is used to force an immediate swap before a resize.
+void DisplayScheduler::ForceImmediateSwapIfPossible() {
+ TRACE_EVENT0("viz", "DisplayScheduler::ForceImmediateSwapIfPossible");
+ bool in_begin = inside_begin_frame_deadline_interval_;
+ bool did_draw = AttemptDrawAndSwap();
+ if (in_begin)
+ DidFinishFrame(did_draw);
+}
+
+void DisplayScheduler::DisplayResized() {
+ expecting_root_surface_damage_because_of_resize_ = true;
+ needs_draw_ = true;
+ ScheduleBeginFrameDeadline();
+}
+
+// Notification that there was a resize or the root surface changed and
+// that we should just draw immediately.
+void DisplayScheduler::SetNewRootSurface(const SurfaceId& root_surface_id) {
+ TRACE_EVENT0("viz", "DisplayScheduler::SetNewRootSurface");
+ root_surface_id_ = root_surface_id;
+ cc::BeginFrameAck ack;
+ ack.has_damage = true;
+ ProcessSurfaceDamage(root_surface_id, ack, true);
+}
+
+// Indicates that there was damage to one of the surfaces.
+// Has some logic to wait for multiple active surfaces before
+// triggering the deadline.
+void DisplayScheduler::ProcessSurfaceDamage(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack,
+ bool display_damaged) {
+ TRACE_EVENT1("viz", "DisplayScheduler::SurfaceDamaged", "surface_id",
+ surface_id.ToString());
+
+ // We may cause a new BeginFrame to be run inside this method, but to help
+ // avoid being reentrant to the caller of SurfaceDamaged, track when this is
+ // happening with |inside_surface_damaged_|.
+ base::AutoReset<bool> auto_reset(&inside_surface_damaged_, true);
+
+ if (display_damaged) {
+ needs_draw_ = true;
+
+ if (surface_id == root_surface_id_)
+ expecting_root_surface_damage_because_of_resize_ = false;
+
+ StartObservingBeginFrames();
+ }
+
+ // Update surface state.
+ bool valid_ack =
+ ack.sequence_number != cc::BeginFrameArgs::kInvalidFrameNumber;
+ if (valid_ack) {
+ auto it = surface_states_.find(surface_id);
+ if (it != surface_states_.end())
+ it->second.last_ack = ack;
+ else
+ valid_ack = false;
+ }
+
+ bool pending_surfaces_changed = false;
+ if (display_damaged || valid_ack)
+ pending_surfaces_changed = UpdateHasPendingSurfaces();
+
+ if (display_damaged || pending_surfaces_changed)
+ ScheduleBeginFrameDeadline();
+}
+
+bool DisplayScheduler::UpdateHasPendingSurfaces() {
+ // If we're not currently inside a deadline interval, we will call
+ // UpdateHasPendingSurfaces() again during OnBeginFrameImpl().
+ if (!inside_begin_frame_deadline_interval_ || !client_)
+ return false;
+
+ bool old_value = has_pending_surfaces_;
+
+ for (const std::pair<SurfaceId, SurfaceBeginFrameState>& entry :
+ surface_states_) {
+ const SurfaceId& surface_id = entry.first;
+ const SurfaceBeginFrameState& state = entry.second;
+
+ // Surface is ready if it hasn't received the current BeginFrame or receives
+ // BeginFrames from a different source and thus likely belongs to a
+ // different surface hierarchy.
+ uint32_t source_id = current_begin_frame_args_.source_id;
+ uint64_t sequence_number = current_begin_frame_args_.sequence_number;
+ if (!state.last_args.IsValid() || state.last_args.source_id != source_id ||
+ state.last_args.sequence_number != sequence_number) {
+ continue;
+ }
+
+ // Surface is ready if it has acknowledged the current BeginFrame.
+ if (state.last_ack.source_id == source_id &&
+ state.last_ack.sequence_number == sequence_number) {
+ continue;
+ }
+
+ // Surface is ready if there is an undrawn active CompositorFrame, because
+ // its producer is CompositorFrameAck throttled.
+ if (client_->SurfaceHasUndrawnFrame(entry.first))
+ continue;
+
+ has_pending_surfaces_ = true;
+ TRACE_EVENT_INSTANT2("viz", "DisplayScheduler::UpdateHasPendingSurfaces",
+ TRACE_EVENT_SCOPE_THREAD, "has_pending_surfaces",
+ has_pending_surfaces_, "pending_surface_id",
+ surface_id.ToString());
+ return has_pending_surfaces_ != old_value;
+ }
+ has_pending_surfaces_ = false;
+ TRACE_EVENT_INSTANT1("viz", "DisplayScheduler::UpdateHasPendingSurfaces",
+ TRACE_EVENT_SCOPE_THREAD, "has_pending_surfaces",
+ has_pending_surfaces_);
+ return has_pending_surfaces_ != old_value;
+}
+
+void DisplayScheduler::OutputSurfaceLost() {
+ TRACE_EVENT0("viz", "DisplayScheduler::OutputSurfaceLost");
+ output_surface_lost_ = true;
+ ScheduleBeginFrameDeadline();
+}
+
+bool DisplayScheduler::DrawAndSwap() {
+ TRACE_EVENT0("viz", "DisplayScheduler::DrawAndSwap");
+ DCHECK_LT(pending_swaps_, max_pending_swaps_);
+ DCHECK(!output_surface_lost_);
+
+ bool success = client_->DrawAndSwap();
+ if (!success)
+ return false;
+
+ needs_draw_ = false;
+ return true;
+}
+
+bool DisplayScheduler::OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) {
+ base::TimeTicks now = base::TimeTicks::Now();
+ TRACE_EVENT2("viz", "DisplayScheduler::BeginFrame", "args", args.AsValue(),
+ "now", now);
+
+ if (inside_surface_damaged_) {
+ // Repost this so that we don't run a missed BeginFrame on the same
+ // callstack. Otherwise we end up running unexpected scheduler actions
+ // immediately while inside some other action (such as submitting a
+ // CompositorFrame for a SurfaceFactory).
+ DCHECK_EQ(args.type, cc::BeginFrameArgs::MISSED);
+ DCHECK(missed_begin_frame_task_.IsCancelled());
+ missed_begin_frame_task_.Reset(base::Bind(
+ base::IgnoreResult(&DisplayScheduler::OnBeginFrameDerivedImpl),
+ // The CancelableCallback will not run after it is destroyed, which
+ // happens when |this| is destroyed.
+ base::Unretained(this), args));
+ task_runner_->PostTask(FROM_HERE, missed_begin_frame_task_.callback());
+ return true;
+ }
+
+ // Save the |BeginFrameArgs| as the callback (missed_begin_frame_task_) can be
+ // destroyed if we StopObservingBeginFrames(), and it would take the |args|
+ // with it. Instead save the args and cancel the |missed_begin_frame_task_|.
+ cc::BeginFrameArgs save_args = args;
+ // If we get another BeginFrame before a posted missed frame, just drop the
+ // missed frame. Also if this was the missed frame, drop the Callback inside
+ // it.
+ missed_begin_frame_task_.Cancel();
+
+ // If we get another BeginFrame before the previous deadline,
+ // synchronously trigger the previous deadline before progressing.
+ if (inside_begin_frame_deadline_interval_)
+ OnBeginFrameDeadline();
+
+ // Schedule the deadline.
+ current_begin_frame_args_ = save_args;
+ current_begin_frame_args_.deadline -=
+ cc::BeginFrameArgs::DefaultEstimatedParentDrawTime();
+ inside_begin_frame_deadline_interval_ = true;
+ UpdateHasPendingSurfaces();
+ ScheduleBeginFrameDeadline();
+
+ return true;
+}
+
+void DisplayScheduler::StartObservingBeginFrames() {
+ if (!observing_begin_frame_source_ && ShouldDraw()) {
+ begin_frame_source_->AddObserver(this);
+ observing_begin_frame_source_ = true;
+ }
+}
+
+void DisplayScheduler::StopObservingBeginFrames() {
+ if (observing_begin_frame_source_) {
+ begin_frame_source_->RemoveObserver(this);
+ observing_begin_frame_source_ = false;
+
+ // A missed BeginFrame may be queued, so drop that too if we're going to
+ // stop listening.
+ missed_begin_frame_task_.Cancel();
+ }
+}
+
+bool DisplayScheduler::ShouldDraw() {
+ // Note: When any of these cases becomes true, StartObservingBeginFrames must
+ // be called to ensure the draw will happen.
+ return needs_draw_ && !output_surface_lost_ && visible_;
+}
+
+void DisplayScheduler::OnBeginFrameSourcePausedChanged(bool paused) {
+ // BeginFrameSources used with DisplayScheduler do not make use of this
+ // feature.
+ if (paused)
+ NOTIMPLEMENTED();
+}
+
+void DisplayScheduler::OnSurfaceCreated(const SurfaceInfo& surface_info) {}
+
+void DisplayScheduler::OnSurfaceDestroyed(const SurfaceId& surface_id) {
+ auto it = surface_states_.find(surface_id);
+ if (it == surface_states_.end())
+ return;
+ surface_states_.erase(it);
+ if (UpdateHasPendingSurfaces())
+ ScheduleBeginFrameDeadline();
+}
+
+bool DisplayScheduler::OnSurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) {
+ bool damaged = client_->SurfaceDamaged(surface_id, ack);
+ ProcessSurfaceDamage(surface_id, ack, damaged);
+
+ return damaged;
+}
+
+void DisplayScheduler::OnSurfaceDiscarded(const SurfaceId& surface_id) {
+ client_->SurfaceDiscarded(surface_id);
+}
+
+void DisplayScheduler::OnSurfaceDamageExpected(const SurfaceId& surface_id,
+ const cc::BeginFrameArgs& args) {
+ TRACE_EVENT1("viz", "DisplayScheduler::SurfaceDamageExpected", "surface_id",
+ surface_id.ToString());
+ // Insert a new state for the surface if we don't know of it yet. We don't use
+ // OnSurfaceCreated for this, because it may not be called if a
+ // CompositorFrameSinkSupport starts submitting frames to a different Display,
+ // but continues using the same Surface, or if a Surface does not activate its
+ // first CompositorFrame immediately.
+ surface_states_[surface_id].last_args = args;
+ if (UpdateHasPendingSurfaces())
+ ScheduleBeginFrameDeadline();
+}
+
+void DisplayScheduler::OnSurfaceWillDraw(const SurfaceId& surface_id) {}
+
+base::TimeTicks DisplayScheduler::DesiredBeginFrameDeadlineTime() const {
+ switch (AdjustedBeginFrameDeadlineMode()) {
+ case BeginFrameDeadlineMode::kImmediate:
+ return base::TimeTicks();
+ case BeginFrameDeadlineMode::kRegular:
+ return current_begin_frame_args_.deadline;
+ case BeginFrameDeadlineMode::kLate:
+ return current_begin_frame_args_.frame_time +
+ current_begin_frame_args_.interval;
+ case BeginFrameDeadlineMode::kNone:
+ return base::TimeTicks::Max();
+ default:
+ NOTREACHED();
+ return base::TimeTicks();
+ }
+}
+
+DisplayScheduler::BeginFrameDeadlineMode
+DisplayScheduler::AdjustedBeginFrameDeadlineMode() const {
+ BeginFrameDeadlineMode mode = DesiredBeginFrameDeadlineMode();
+
+ // In blocking mode, late and regular deadline should not apply. Wait
+ // indefinitely instead.
+ if (wait_for_all_surfaces_before_draw_ &&
+ (mode == BeginFrameDeadlineMode::kRegular ||
+ mode == BeginFrameDeadlineMode::kLate)) {
+ return BeginFrameDeadlineMode::kNone;
+ }
+
+ return mode;
+}
+
+DisplayScheduler::BeginFrameDeadlineMode
+DisplayScheduler::DesiredBeginFrameDeadlineMode() const {
+ if (output_surface_lost_) {
+ TRACE_EVENT_INSTANT0("viz", "Lost output surface",
+ TRACE_EVENT_SCOPE_THREAD);
+ return BeginFrameDeadlineMode::kImmediate;
+ }
+
+ if (pending_swaps_ >= max_pending_swaps_) {
+ TRACE_EVENT_INSTANT0("viz", "Swap throttled", TRACE_EVENT_SCOPE_THREAD);
+ return BeginFrameDeadlineMode::kLate;
+ }
+
+ if (root_surface_resources_locked_) {
+ TRACE_EVENT_INSTANT0("viz", "Root surface resources locked",
+ TRACE_EVENT_SCOPE_THREAD);
+ return BeginFrameDeadlineMode::kLate;
+ }
+
+ bool all_surfaces_ready = !has_pending_surfaces_ &&
+ root_surface_id_.is_valid() &&
+ !expecting_root_surface_damage_because_of_resize_;
+
+ // When no draw is needed, only allow an early deadline in full-pipe mode.
+ // This way, we can unblock the BeginFrame in full-pipe mode if no draw is
+ // necessary, but accommodate damage as a result of missed BeginFrames from
+ // clients otherwise.
+ bool allow_early_deadline_without_draw = wait_for_all_surfaces_before_draw_;
+
+ if (all_surfaces_ready &&
+ (needs_draw_ || allow_early_deadline_without_draw)) {
+ TRACE_EVENT_INSTANT0("viz", "All active surfaces ready",
+ TRACE_EVENT_SCOPE_THREAD);
+ return BeginFrameDeadlineMode::kImmediate;
+ }
+
+ if (!needs_draw_) {
+ TRACE_EVENT_INSTANT0("cc", "No damage yet", TRACE_EVENT_SCOPE_THREAD);
+ return BeginFrameDeadlineMode::kLate;
+ }
+
+ // TODO(mithro): Be smarter about resize deadlines.
+ if (expecting_root_surface_damage_because_of_resize_) {
+ TRACE_EVENT_INSTANT0("viz", "Entire display damaged",
+ TRACE_EVENT_SCOPE_THREAD);
+ return BeginFrameDeadlineMode::kLate;
+ }
+
+ TRACE_EVENT_INSTANT0("viz", "More damage expected soon",
+ TRACE_EVENT_SCOPE_THREAD);
+ return BeginFrameDeadlineMode::kRegular;
+}
+
+void DisplayScheduler::ScheduleBeginFrameDeadline() {
+ TRACE_EVENT0("viz", "DisplayScheduler::ScheduleBeginFrameDeadline");
+
+ // We need to wait for the next BeginFrame before scheduling a deadline.
+ if (!inside_begin_frame_deadline_interval_) {
+ TRACE_EVENT_INSTANT0("viz", "Waiting for next BeginFrame",
+ TRACE_EVENT_SCOPE_THREAD);
+ DCHECK(begin_frame_deadline_task_.IsCancelled());
+ return;
+ }
+
+ // Determine the deadline we want to use.
+ base::TimeTicks desired_deadline = DesiredBeginFrameDeadlineTime();
+
+ // Avoid re-scheduling the deadline if it's already correctly scheduled.
+ if (!begin_frame_deadline_task_.IsCancelled() &&
+ desired_deadline == begin_frame_deadline_task_time_) {
+ TRACE_EVENT_INSTANT0("viz", "Using existing deadline",
+ TRACE_EVENT_SCOPE_THREAD);
+ return;
+ }
+
+ // Schedule the deadline.
+ begin_frame_deadline_task_time_ = desired_deadline;
+ begin_frame_deadline_task_.Cancel();
+
+ if (begin_frame_deadline_task_time_ == base::TimeTicks::Max()) {
+ TRACE_EVENT_INSTANT0("cc", "Using infinite deadline",
+ TRACE_EVENT_SCOPE_THREAD);
+ return;
+ }
+
+ begin_frame_deadline_task_.Reset(begin_frame_deadline_closure_);
+ base::TimeDelta delta =
+ std::max(base::TimeDelta(), desired_deadline - base::TimeTicks::Now());
+ task_runner_->PostDelayedTask(FROM_HERE,
+ begin_frame_deadline_task_.callback(), delta);
+ TRACE_EVENT2("viz", "Using new deadline", "delta", delta.ToInternalValue(),
+ "desired_deadline", desired_deadline);
+}
+
+bool DisplayScheduler::AttemptDrawAndSwap() {
+ inside_begin_frame_deadline_interval_ = false;
+ begin_frame_deadline_task_.Cancel();
+ begin_frame_deadline_task_time_ = base::TimeTicks();
+
+ if (ShouldDraw()) {
+ if (pending_swaps_ < max_pending_swaps_ && !root_surface_resources_locked_)
+ return DrawAndSwap();
+ } else {
+ // We are going idle, so reset expectations.
+ // TODO(eseckler): Should we avoid going idle if
+ // |expecting_root_surface_damage_because_of_resize_| is true?
+ expecting_root_surface_damage_because_of_resize_ = false;
+
+ StopObservingBeginFrames();
+ }
+ return false;
+}
+
+void DisplayScheduler::OnBeginFrameDeadline() {
+ TRACE_EVENT0("viz", "DisplayScheduler::OnBeginFrameDeadline");
+ DCHECK(inside_begin_frame_deadline_interval_);
+
+ bool did_draw = AttemptDrawAndSwap();
+ DidFinishFrame(did_draw);
+}
+
+void DisplayScheduler::DidFinishFrame(bool did_draw) {
+ DCHECK(begin_frame_source_);
+ // TODO(eseckler): Let client know that frame was completed.
+ begin_frame_source_->DidFinishFrame(this);
+}
+
+void DisplayScheduler::DidSwapBuffers() {
+ pending_swaps_++;
+ uint32_t swap_id = next_swap_id_++;
+ TRACE_EVENT_ASYNC_BEGIN0("viz", "DisplayScheduler:pending_swaps", swap_id);
+}
+
+void DisplayScheduler::DidReceiveSwapBuffersAck() {
+ uint32_t swap_id = next_swap_id_ - pending_swaps_;
+ pending_swaps_--;
+ TRACE_EVENT_ASYNC_END0("viz", "DisplayScheduler:pending_swaps", swap_id);
+ ScheduleBeginFrameDeadline();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display/display_scheduler.h b/chromium/components/viz/service/display/display_scheduler.h
new file mode 100644
index 00000000000..652208db836
--- /dev/null
+++ b/chromium/components/viz/service/display/display_scheduler.h
@@ -0,0 +1,136 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_SCHEDULER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/cancelable_callback.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/surface_observer.h"
+#include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+class BeginFrameSource;
+class SurfaceInfo;
+
+class VIZ_SERVICE_EXPORT DisplaySchedulerClient {
+ public:
+ virtual ~DisplaySchedulerClient() {}
+
+ virtual bool DrawAndSwap() = 0;
+ virtual bool SurfaceHasUndrawnFrame(const SurfaceId& surface_id) const = 0;
+ virtual bool SurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) = 0;
+ virtual void SurfaceDiscarded(const SurfaceId& surface_id) = 0;
+};
+
+class VIZ_SERVICE_EXPORT DisplayScheduler : public cc::BeginFrameObserverBase,
+ public cc::SurfaceObserver {
+ public:
+ DisplayScheduler(cc::BeginFrameSource* begin_frame_source,
+ base::SingleThreadTaskRunner* task_runner,
+ int max_pending_swaps,
+ bool wait_for_all_surfaces_before_draw = false);
+ ~DisplayScheduler() override;
+
+ void SetClient(DisplaySchedulerClient* client);
+
+ void SetVisible(bool visible);
+ void SetRootSurfaceResourcesLocked(bool locked);
+ void ForceImmediateSwapIfPossible();
+ virtual void DisplayResized();
+ virtual void SetNewRootSurface(const SurfaceId& root_surface_id);
+ virtual void ProcessSurfaceDamage(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack,
+ bool display_damaged);
+
+ virtual void DidSwapBuffers();
+ void DidReceiveSwapBuffersAck();
+
+ void OutputSurfaceLost();
+
+ // BeginFrameObserverBase implementation.
+ bool OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
+
+ // SurfaceObserver implementation.
+ void OnSurfaceCreated(const SurfaceInfo& surface_info) override;
+ void OnSurfaceDestroyed(const SurfaceId& surface_id) override;
+ bool OnSurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) override;
+ void OnSurfaceDiscarded(const SurfaceId& surface_id) override;
+ void OnSurfaceDamageExpected(const SurfaceId& surface_id,
+ const cc::BeginFrameArgs& args) override;
+ void OnSurfaceWillDraw(const SurfaceId& surface_id) override;
+
+ protected:
+ enum class BeginFrameDeadlineMode { kImmediate, kRegular, kLate, kNone };
+ base::TimeTicks DesiredBeginFrameDeadlineTime() const;
+ BeginFrameDeadlineMode AdjustedBeginFrameDeadlineMode() const;
+ BeginFrameDeadlineMode DesiredBeginFrameDeadlineMode() const;
+ virtual void ScheduleBeginFrameDeadline();
+ bool AttemptDrawAndSwap();
+ void OnBeginFrameDeadline();
+ bool DrawAndSwap();
+ void StartObservingBeginFrames();
+ void StopObservingBeginFrames();
+ bool ShouldDraw();
+ void DidFinishFrame(bool did_draw);
+ // Updates |has_pending_surfaces_| and returns whether its value changed.
+ bool UpdateHasPendingSurfaces();
+
+ DisplaySchedulerClient* client_;
+ cc::BeginFrameSource* begin_frame_source_;
+ base::SingleThreadTaskRunner* task_runner_;
+
+ cc::BeginFrameArgs current_begin_frame_args_;
+ base::Closure begin_frame_deadline_closure_;
+ base::CancelableClosure begin_frame_deadline_task_;
+ base::TimeTicks begin_frame_deadline_task_time_;
+
+ base::CancelableClosure missed_begin_frame_task_;
+ bool inside_surface_damaged_;
+
+ bool visible_;
+ bool output_surface_lost_;
+ bool root_surface_resources_locked_;
+
+ bool inside_begin_frame_deadline_interval_;
+ bool needs_draw_;
+ bool expecting_root_surface_damage_because_of_resize_;
+ bool has_pending_surfaces_;
+
+ struct SurfaceBeginFrameState {
+ cc::BeginFrameArgs last_args;
+ cc::BeginFrameAck last_ack;
+ };
+ base::flat_map<SurfaceId, SurfaceBeginFrameState> surface_states_;
+
+ int next_swap_id_;
+ int pending_swaps_;
+ int max_pending_swaps_;
+ bool wait_for_all_surfaces_before_draw_;
+
+ bool observing_begin_frame_source_;
+
+ SurfaceId root_surface_id_;
+
+ base::WeakPtrFactory<DisplayScheduler> weak_ptr_factory_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisplayScheduler);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_SCHEDULER_H_
diff --git a/chromium/components/viz/service/display/display_scheduler_unittest.cc b/chromium/components/viz/service/display/display_scheduler_unittest.cc
new file mode 100644
index 00000000000..1f1f84927ba
--- /dev/null
+++ b/chromium/components/viz/service/display/display_scheduler_unittest.cc
@@ -0,0 +1,734 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display_scheduler.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/test/null_task_runner.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/output/begin_frame_args.h"
+#include "cc/test/fake_external_begin_frame_source.h"
+#include "cc/test/scheduler_test_common.h"
+#include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/service/display/display.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+namespace {
+
+const int kMaxPendingSwaps = 1;
+
+static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
+
+class FakeDisplaySchedulerClient : public DisplaySchedulerClient {
+ public:
+ FakeDisplaySchedulerClient()
+ : draw_and_swap_count_(0), next_draw_and_swap_fails_(false) {}
+
+ ~FakeDisplaySchedulerClient() override {}
+
+ bool DrawAndSwap() override {
+ draw_and_swap_count_++;
+
+ bool success = !next_draw_and_swap_fails_;
+ next_draw_and_swap_fails_ = false;
+
+ if (success)
+ undrawn_surfaces_.clear();
+ return success;
+ }
+
+ bool SurfaceHasUndrawnFrame(const SurfaceId& surface_id) const override {
+ return base::ContainsKey(undrawn_surfaces_, surface_id);
+ }
+
+ bool SurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) override {
+ return false;
+ }
+
+ void SurfaceDiscarded(const SurfaceId& surface_id) override {}
+
+ int draw_and_swap_count() const { return draw_and_swap_count_; }
+
+ void SetNextDrawAndSwapFails() { next_draw_and_swap_fails_ = true; }
+
+ void SurfaceDamaged(const SurfaceId& surface_id) {
+ undrawn_surfaces_.insert(surface_id);
+ }
+
+ protected:
+ int draw_and_swap_count_;
+ bool next_draw_and_swap_fails_;
+ std::set<SurfaceId> undrawn_surfaces_;
+};
+
+class TestDisplayScheduler : public DisplayScheduler {
+ public:
+ TestDisplayScheduler(cc::BeginFrameSource* begin_frame_source,
+ cc::SurfaceManager* surface_manager,
+ base::SingleThreadTaskRunner* task_runner,
+ int max_pending_swaps,
+ bool wait_for_all_surfaces_before_draw)
+ : DisplayScheduler(begin_frame_source,
+ task_runner,
+ max_pending_swaps,
+ wait_for_all_surfaces_before_draw),
+ scheduler_begin_frame_deadline_count_(0) {}
+
+ base::TimeTicks DesiredBeginFrameDeadlineTimeForTest() {
+ return DesiredBeginFrameDeadlineTime();
+ }
+
+ void BeginFrameDeadlineForTest() {
+ // Ensure that any missed BeginFrames were handled by the scheduler. We need
+ // to run the scheduled task ourselves since the NullTaskRunner won't.
+ if (!missed_begin_frame_task_.IsCancelled())
+ missed_begin_frame_task_.callback().Run();
+ OnBeginFrameDeadline();
+ }
+
+ void ScheduleBeginFrameDeadline() override {
+ scheduler_begin_frame_deadline_count_++;
+ DisplayScheduler::ScheduleBeginFrameDeadline();
+ }
+
+ int scheduler_begin_frame_deadline_count() {
+ return scheduler_begin_frame_deadline_count_;
+ }
+
+ bool inside_begin_frame_deadline_interval() {
+ return inside_begin_frame_deadline_interval_;
+ }
+
+ bool has_pending_surfaces() { return has_pending_surfaces_; }
+
+ protected:
+ int scheduler_begin_frame_deadline_count_;
+};
+
+class DisplaySchedulerTest : public testing::Test {
+ public:
+ explicit DisplaySchedulerTest(bool wait_for_all_surfaces_before_draw = false)
+ : fake_begin_frame_source_(0.f, false),
+ task_runner_(new base::NullTaskRunner),
+ scheduler_(&fake_begin_frame_source_,
+ &surface_manager_,
+ task_runner_.get(),
+ kMaxPendingSwaps,
+ wait_for_all_surfaces_before_draw) {
+ now_src_.Advance(base::TimeDelta::FromMicroseconds(10000));
+ surface_manager_.AddObserver(&scheduler_);
+ scheduler_.SetClient(&client_);
+ }
+
+ ~DisplaySchedulerTest() override {
+ surface_manager_.RemoveObserver(&scheduler_);
+ }
+
+ void SetUp() override { scheduler_.SetRootSurfaceResourcesLocked(false); }
+
+ void AdvanceTimeAndBeginFrameForTest(
+ const std::vector<SurfaceId>& observing_surfaces) {
+ now_src_.Advance(base::TimeDelta::FromMicroseconds(10000));
+ // FakeBeginFrameSource deals with |source_id| and |sequence_number|.
+ last_begin_frame_args_ = fake_begin_frame_source_.CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, &now_src_);
+ fake_begin_frame_source_.TestOnBeginFrame(last_begin_frame_args_);
+ for (const auto& surface_id : observing_surfaces)
+ scheduler_.OnSurfaceDamageExpected(surface_id, last_begin_frame_args_);
+ }
+
+ void SurfaceDamaged(const SurfaceId& surface_id) {
+ client_.SurfaceDamaged(surface_id);
+ scheduler_.ProcessSurfaceDamage(surface_id, AckForCurrentBeginFrame(),
+ true);
+ }
+
+ protected:
+ base::SimpleTestTickClock& now_src() { return now_src_; }
+ FakeDisplaySchedulerClient& client() { return client_; }
+ DisplayScheduler& scheduler() { return scheduler_; }
+ cc::BeginFrameAck AckForCurrentBeginFrame() {
+ DCHECK(last_begin_frame_args_.IsValid());
+ return cc::BeginFrameAck(last_begin_frame_args_.source_id,
+ last_begin_frame_args_.sequence_number, true);
+ }
+
+ cc::FakeExternalBeginFrameSource fake_begin_frame_source_;
+ cc::BeginFrameArgs last_begin_frame_args_;
+
+ base::SimpleTestTickClock now_src_;
+ scoped_refptr<base::NullTaskRunner> task_runner_;
+ cc::SurfaceManager surface_manager_;
+ FakeDisplaySchedulerClient client_;
+ TestDisplayScheduler scheduler_;
+};
+
+TEST_F(DisplaySchedulerTest, ResizeHasLateDeadlineUntilNewRootSurface) {
+ SurfaceId root_surface_id1(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId root_surface_id2(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(3, base::UnguessableToken::Create()));
+ base::TimeTicks late_deadline;
+
+ scheduler_.SetVisible(true);
+
+ // Go trough an initial BeginFrame cycle with the root surface.
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ scheduler_.SetNewRootSurface(root_surface_id1);
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Resize on the next begin frame cycle should cause the deadline to wait
+ // for a new root surface.
+ AdvanceTimeAndBeginFrameForTest({root_surface_id1});
+ late_deadline = now_src().NowTicks() + cc::BeginFrameArgs::DefaultInterval();
+ SurfaceDamaged(sid1);
+ EXPECT_GT(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.DisplayResized();
+ EXPECT_EQ(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.OnSurfaceDestroyed(root_surface_id1);
+ scheduler_.SetNewRootSurface(root_surface_id2);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Verify deadline goes back to normal after resize.
+ late_deadline = now_src().NowTicks() + cc::BeginFrameArgs::DefaultInterval();
+ AdvanceTimeAndBeginFrameForTest({root_surface_id2, sid1});
+ SurfaceDamaged(sid1);
+ EXPECT_GT(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ SurfaceDamaged(root_surface_id2);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+}
+
+TEST_F(DisplaySchedulerTest, ResizeHasLateDeadlineUntilDamagedSurface) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+ base::TimeTicks late_deadline;
+
+ scheduler_.SetVisible(true);
+
+ // Go trough an initial BeginFrame cycle with the root surface.
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ scheduler_.SetNewRootSurface(root_surface_id);
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Resize on the next begin frame cycle should cause the deadline to wait
+ // for a new root surface.
+ AdvanceTimeAndBeginFrameForTest({root_surface_id});
+ late_deadline = now_src().NowTicks() + cc::BeginFrameArgs::DefaultInterval();
+ SurfaceDamaged(sid1);
+ EXPECT_GT(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.DisplayResized();
+ EXPECT_EQ(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ SurfaceDamaged(root_surface_id);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Verify deadline goes back to normal after resize.
+ AdvanceTimeAndBeginFrameForTest({root_surface_id, sid1});
+ late_deadline = now_src().NowTicks() + cc::BeginFrameArgs::DefaultInterval();
+ SurfaceDamaged(sid1);
+ EXPECT_GT(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ SurfaceDamaged(root_surface_id);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+}
+
+TEST_F(DisplaySchedulerTest, SurfaceDamaged) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+ SurfaceId sid2(kArbitraryFrameSinkId,
+ LocalSurfaceId(3, base::UnguessableToken::Create()));
+
+ scheduler_.SetVisible(true);
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ // Set surface1 as active via SurfaceDamageExpected().
+ AdvanceTimeAndBeginFrameForTest({sid1});
+
+ // Damage only from surface 2 (inactive) does not trigger deadline early.
+ SurfaceDamaged(sid2);
+ EXPECT_TRUE(scheduler_.has_pending_surfaces());
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ // Damage from surface 1 triggers deadline early.
+ SurfaceDamaged(sid1);
+ EXPECT_FALSE(scheduler_.has_pending_surfaces());
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Set both surface 1 and 2 as active via SurfaceDamageExpected().
+ AdvanceTimeAndBeginFrameForTest({sid1, sid2});
+
+ // Deadline doesn't trigger early until surface 1 and 2 are both damaged.
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ SurfaceDamaged(sid1);
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ SurfaceDamaged(sid2);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Surface damage with |!has_damage| triggers early deadline if other damage
+ // exists.
+ AdvanceTimeAndBeginFrameForTest({sid1, sid2});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ SurfaceDamaged(sid2);
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ cc::BeginFrameAck ack = AckForCurrentBeginFrame();
+ ack.has_damage = false;
+ scheduler_.ProcessSurfaceDamage(sid1, ack, false);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Surface damage with |!has_damage| does not trigger early deadline if no
+ // other damage exists.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ ack = AckForCurrentBeginFrame();
+ ack.has_damage = false;
+ scheduler_.ProcessSurfaceDamage(sid1, ack, false);
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // System should be idle now.
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ EXPECT_FALSE(scheduler_.inside_begin_frame_deadline_interval());
+
+ // Surface damage with |!display_damaged| does not affect needs_draw and
+ // scheduler stays idle.
+ scheduler_.ProcessSurfaceDamage(sid1, AckForCurrentBeginFrame(), false);
+ EXPECT_FALSE(scheduler_.inside_begin_frame_deadline_interval());
+
+ // Deadline should trigger early if child surfaces are idle and
+ // we get damage on the root surface.
+ scheduler_.OnSurfaceDamageExpected(root_surface_id, last_begin_frame_args_);
+ EXPECT_FALSE(scheduler_.inside_begin_frame_deadline_interval());
+ SurfaceDamaged(root_surface_id);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+}
+
+class DisplaySchedulerWaitForAllSurfacesTest : public DisplaySchedulerTest {
+ public:
+ DisplaySchedulerWaitForAllSurfacesTest()
+ : DisplaySchedulerTest(true /* wait_for_all_surfaces_before_draw */) {}
+};
+
+TEST_F(DisplaySchedulerWaitForAllSurfacesTest, WaitForAllSurfacesBeforeDraw) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+ SurfaceId sid2(kArbitraryFrameSinkId,
+ LocalSurfaceId(3, base::UnguessableToken::Create()));
+
+ scheduler_.SetVisible(true);
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ // Set surface1 as active via SurfaceDamageExpected().
+ AdvanceTimeAndBeginFrameForTest({sid1});
+
+ // Deadline is blocked indefinitely until surface 1 is damaged.
+ EXPECT_EQ(base::TimeTicks::Max(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ // Damage only from surface 2 (inactive) does not change deadline.
+ SurfaceDamaged(sid2);
+ EXPECT_TRUE(scheduler_.has_pending_surfaces());
+ EXPECT_EQ(base::TimeTicks::Max(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ // Damage from surface 1 triggers deadline immediately.
+ SurfaceDamaged(sid1);
+ EXPECT_FALSE(scheduler_.has_pending_surfaces());
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Surface damage with |!has_damage| triggers immediate deadline if other
+ // damage exists.
+ AdvanceTimeAndBeginFrameForTest({sid1, sid2});
+ EXPECT_EQ(base::TimeTicks::Max(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ SurfaceDamaged(sid2);
+ EXPECT_EQ(base::TimeTicks::Max(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ cc::BeginFrameAck ack = AckForCurrentBeginFrame();
+ ack.has_damage = false;
+ scheduler_.ProcessSurfaceDamage(sid1, ack, false);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // Surface damage with |!has_damage| also triggers immediate deadline even if
+ // no other damage exists.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_EQ(base::TimeTicks::Max(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ ack = AckForCurrentBeginFrame();
+ ack.has_damage = false;
+ scheduler_.ProcessSurfaceDamage(sid1, ack, false);
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // System should be idle now because we had a frame without damage. Restore it
+ // to active state (DisplayScheduler observing BeginFrames) for the next test.
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ EXPECT_FALSE(scheduler_.inside_begin_frame_deadline_interval());
+ SurfaceDamaged(sid1);
+ scheduler_.BeginFrameDeadlineForTest();
+
+ // BeginFrame without expected surface damage triggers immediate deadline.
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ EXPECT_TRUE(scheduler_.inside_begin_frame_deadline_interval());
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.BeginFrameDeadlineForTest();
+}
+
+TEST_F(DisplaySchedulerTest, OutputSurfaceLost) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+
+ scheduler_.SetVisible(true);
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ // DrawAndSwap normally.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ EXPECT_EQ(0, client_.draw_and_swap_count());
+ SurfaceDamaged(sid1);
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+
+ // Deadline triggers immediately on OutputSurfaceLost.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.OutputSurfaceLost();
+ EXPECT_GE(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ // Deadline does not DrawAndSwap after OutputSurfaceLost.
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ SurfaceDamaged(sid1);
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+}
+
+TEST_F(DisplaySchedulerTest, VisibleWithoutDamageNoTicks) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+
+ EXPECT_EQ(0u, fake_begin_frame_source_.num_observers());
+ scheduler_.SetVisible(true);
+
+ // When becoming visible, don't start listening for begin frames until there
+ // is some damage.
+ EXPECT_EQ(0u, fake_begin_frame_source_.num_observers());
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ EXPECT_EQ(1u, fake_begin_frame_source_.num_observers());
+}
+
+TEST_F(DisplaySchedulerTest, VisibleWithDamageTicks) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ // When there is damage, start listening for begin frames once becoming
+ // visible.
+ EXPECT_EQ(0u, fake_begin_frame_source_.num_observers());
+ scheduler_.SetVisible(true);
+
+ EXPECT_EQ(1u, fake_begin_frame_source_.num_observers());
+}
+
+TEST_F(DisplaySchedulerTest, Visibility) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+
+ // Set the root surface.
+ scheduler_.SetNewRootSurface(root_surface_id);
+ scheduler_.SetVisible(true);
+ EXPECT_EQ(1u, fake_begin_frame_source_.num_observers());
+
+ // DrawAndSwap normally.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ EXPECT_EQ(0, client_.draw_and_swap_count());
+ SurfaceDamaged(sid1);
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ // Become not visible.
+ scheduler_.SetVisible(false);
+
+ // It will stop listening for begin frames after the current deadline.
+ EXPECT_EQ(1u, fake_begin_frame_source_.num_observers());
+
+ // Deadline does not DrawAndSwap when not visible.
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ // Now it stops listening for begin frames.
+ EXPECT_EQ(0u, fake_begin_frame_source_.num_observers());
+
+ // Does not start listening for begin frames when becoming visible without
+ // damage.
+ scheduler_.SetVisible(true);
+ EXPECT_EQ(0u, fake_begin_frame_source_.num_observers());
+ scheduler_.SetVisible(false);
+
+ // Does not start listening for begin frames when damage arrives.
+ SurfaceDamaged(sid1);
+ EXPECT_EQ(0u, fake_begin_frame_source_.num_observers());
+
+ // But does when becoming visible with damage again.
+ scheduler_.SetVisible(true);
+ EXPECT_EQ(1u, fake_begin_frame_source_.num_observers());
+}
+
+TEST_F(DisplaySchedulerTest, ResizeCausesSwap) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+
+ scheduler_.SetVisible(true);
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ // DrawAndSwap normally.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ EXPECT_EQ(0, client_.draw_and_swap_count());
+ SurfaceDamaged(sid1);
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+
+ scheduler_.DisplayResized();
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ // DisplayResized should trigger a swap to happen.
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(2, client_.draw_and_swap_count());
+}
+
+TEST_F(DisplaySchedulerTest, RootSurfaceResourcesLocked) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+ base::TimeTicks late_deadline;
+
+ scheduler_.SetVisible(true);
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ // DrawAndSwap normally.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ EXPECT_EQ(0, client_.draw_and_swap_count());
+ SurfaceDamaged(sid1);
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+
+ // Deadline triggers late while root resources are locked.
+ AdvanceTimeAndBeginFrameForTest({sid1});
+ late_deadline = now_src().NowTicks() + cc::BeginFrameArgs::DefaultInterval();
+ SurfaceDamaged(sid1);
+ EXPECT_GT(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.SetRootSurfaceResourcesLocked(true);
+ EXPECT_EQ(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ // Deadline does not DrawAndSwap while root resources are locked.
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ SurfaceDamaged(sid1);
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+
+ // Deadline triggers normally when root resources are unlocked.
+ AdvanceTimeAndBeginFrameForTest({sid1, root_surface_id});
+ late_deadline = now_src().NowTicks() + cc::BeginFrameArgs::DefaultInterval();
+ SurfaceDamaged(sid1);
+ EXPECT_EQ(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ scheduler_.SetRootSurfaceResourcesLocked(false);
+ SurfaceDamaged(root_surface_id);
+ EXPECT_EQ(base::TimeTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(2, client_.draw_and_swap_count());
+}
+
+TEST_F(DisplaySchedulerTest, DidSwapBuffers) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+ SurfaceId sid2(kArbitraryFrameSinkId,
+ LocalSurfaceId(3, base::UnguessableToken::Create()));
+
+ scheduler_.SetVisible(true);
+ scheduler_.SetNewRootSurface(root_surface_id);
+
+ // Set surface 1 and 2 as active.
+ AdvanceTimeAndBeginFrameForTest({sid1, sid2});
+
+ // DrawAndSwap normally.
+ EXPECT_LT(now_src().NowTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ EXPECT_EQ(0, client_.draw_and_swap_count());
+ SurfaceDamaged(sid1);
+ SurfaceDamaged(sid2);
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ scheduler_.DidSwapBuffers();
+
+ // Deadline triggers late when swap throttled.
+ AdvanceTimeAndBeginFrameForTest({sid1, sid2});
+ base::TimeTicks late_deadline =
+ now_src().NowTicks() + cc::BeginFrameArgs::DefaultInterval();
+ // Damage surface 1, but not surface 2 so we avoid triggering deadline
+ // early because all surfaces are ready.
+ SurfaceDamaged(sid1);
+ EXPECT_EQ(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+
+ // Don't draw and swap in deadline while swap throttled.
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+
+ // Deadline triggers normally once not swap throttled.
+ // Damage from previous BeginFrame should cary over, so don't damage again.
+ scheduler_.DidReceiveSwapBuffersAck();
+ AdvanceTimeAndBeginFrameForTest({sid2});
+ base::TimeTicks expected_deadline =
+ scheduler_.LastUsedBeginFrameArgs().deadline -
+ cc::BeginFrameArgs::DefaultEstimatedParentDrawTime();
+ EXPECT_EQ(expected_deadline,
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ // Still waiting for surface 2. Once it updates, deadline should trigger
+ // immediately again.
+ SurfaceDamaged(sid2);
+ EXPECT_EQ(base::TimeTicks(),
+ scheduler_.DesiredBeginFrameDeadlineTimeForTest());
+ // Draw and swap now that we aren't throttled.
+ EXPECT_EQ(1, client_.draw_and_swap_count());
+ scheduler_.BeginFrameDeadlineForTest();
+ EXPECT_EQ(2, client_.draw_and_swap_count());
+}
+
+// This test verfies that we try to reschedule the deadline
+// after any event that may change what deadline we want.
+TEST_F(DisplaySchedulerTest, ScheduleBeginFrameDeadline) {
+ SurfaceId root_surface_id(
+ kArbitraryFrameSinkId,
+ LocalSurfaceId(1, base::UnguessableToken::Create()));
+ SurfaceId sid1(kArbitraryFrameSinkId,
+ LocalSurfaceId(2, base::UnguessableToken::Create()));
+ int count = 1;
+ EXPECT_EQ(count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.SetVisible(true);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.SetVisible(true);
+ EXPECT_EQ(count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.SetVisible(false);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ // Set the root surface while not visible.
+ scheduler_.SetNewRootSurface(root_surface_id);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.SetVisible(true);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ // Set the root surface while visible.
+ scheduler_.SetNewRootSurface(root_surface_id);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.BeginFrameDeadlineForTest();
+ scheduler_.DidSwapBuffers();
+ AdvanceTimeAndBeginFrameForTest(std::vector<SurfaceId>());
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.DidReceiveSwapBuffersAck();
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.DisplayResized();
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.SetNewRootSurface(root_surface_id);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ SurfaceDamaged(sid1);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.SetRootSurfaceResourcesLocked(true);
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+
+ scheduler_.OutputSurfaceLost();
+ EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/display/display_unittest.cc b/chromium/components/viz/service/display/display_unittest.cc
new file mode 100644
index 00000000000..aa68584504c
--- /dev/null
+++ b/chromium/components/viz/service/display/display_unittest.cc
@@ -0,0 +1,658 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/display.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/test/null_task_runner.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/copy_output_result.h"
+#include "cc/output/texture_mailbox_deleter.h"
+#include "cc/quads/render_pass.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/scheduler_test_common.h"
+#include "cc/test/test_shared_bitmap_manager.h"
+#include "components/viz/common/resources/shared_bitmap_manager.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/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/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::AnyNumber;
+
+namespace viz {
+namespace {
+
+static constexpr FrameSinkId kArbitraryFrameSinkId(3, 3);
+static constexpr FrameSinkId kAnotherFrameSinkId(4, 4);
+
+class TestSoftwareOutputDevice : public cc::SoftwareOutputDevice {
+ public:
+ TestSoftwareOutputDevice() {}
+
+ gfx::Rect damage_rect() const { return damage_rect_; }
+ gfx::Size viewport_pixel_size() const { return viewport_pixel_size_; }
+};
+
+class TestDisplayScheduler : public DisplayScheduler {
+ public:
+ explicit TestDisplayScheduler(cc::BeginFrameSource* begin_frame_source,
+ base::SingleThreadTaskRunner* task_runner)
+ : DisplayScheduler(begin_frame_source, task_runner, 1),
+ damaged(false),
+ display_resized_(false),
+ has_new_root_surface(false),
+ swapped(false) {}
+
+ ~TestDisplayScheduler() override {}
+
+ void DisplayResized() override { display_resized_ = true; }
+
+ void SetNewRootSurface(const SurfaceId& root_surface_id) override {
+ has_new_root_surface = true;
+ }
+
+ void ProcessSurfaceDamage(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack,
+ bool display_damaged) override {
+ if (display_damaged) {
+ damaged = true;
+ needs_draw_ = true;
+ }
+ }
+
+ void DidSwapBuffers() override { swapped = true; }
+
+ void ResetDamageForTest() {
+ damaged = false;
+ display_resized_ = false;
+ has_new_root_surface = false;
+ }
+
+ bool damaged;
+ bool display_resized_;
+ bool has_new_root_surface;
+ bool swapped;
+};
+
+class DisplayTest : public testing::Test {
+ public:
+ DisplayTest()
+ : support_(CompositorFrameSinkSupport::Create(
+ nullptr,
+ &manager_,
+ kArbitraryFrameSinkId,
+ true /* is_root */,
+ true /* handles_frame_sink_id_invalidation */,
+ true /* needs_sync_points */)),
+ task_runner_(new base::NullTaskRunner) {}
+
+ ~DisplayTest() override { support_->EvictCurrentSurface(); }
+
+ void SetUpDisplay(const RendererSettings& settings,
+ std::unique_ptr<cc::TestWebGraphicsContext3D> context) {
+ begin_frame_source_.reset(new cc::StubBeginFrameSource);
+
+ std::unique_ptr<cc::FakeOutputSurface> output_surface;
+ if (context) {
+ auto provider = cc::TestContextProvider::Create(std::move(context));
+ provider->BindToCurrentThread();
+ output_surface = cc::FakeOutputSurface::Create3d(std::move(provider));
+ } else {
+ auto device = base::MakeUnique<TestSoftwareOutputDevice>();
+ software_output_device_ = device.get();
+ output_surface = cc::FakeOutputSurface::CreateSoftware(std::move(device));
+ }
+ output_surface_ = output_surface.get();
+ auto scheduler = base::MakeUnique<TestDisplayScheduler>(
+ begin_frame_source_.get(), task_runner_.get());
+ scheduler_ = scheduler.get();
+ display_ = CreateDisplay(settings, kArbitraryFrameSinkId,
+ std::move(scheduler), std::move(output_surface));
+ manager_.RegisterBeginFrameSource(begin_frame_source_.get(),
+ kArbitraryFrameSinkId);
+ }
+
+ std::unique_ptr<Display> CreateDisplay(
+ const RendererSettings& settings,
+ const FrameSinkId& frame_sink_id,
+ std::unique_ptr<DisplayScheduler> scheduler,
+ std::unique_ptr<cc::OutputSurface> output_surface) {
+ auto display = base::MakeUnique<Display>(
+ &shared_bitmap_manager_, nullptr /* gpu_memory_buffer_manager */,
+ settings, frame_sink_id, std::move(output_surface),
+ std::move(scheduler),
+ base::MakeUnique<cc::TextureMailboxDeleter>(task_runner_.get()));
+ display->SetVisible(true);
+ return display;
+ }
+
+ void TearDownDisplay() {
+ // Only call UnregisterBeginFrameSource if SetupDisplay has been called.
+ if (begin_frame_source_)
+ manager_.UnregisterBeginFrameSource(begin_frame_source_.get());
+ }
+
+ protected:
+ void SubmitCompositorFrame(cc::RenderPassList* pass_list,
+ const LocalSurfaceId& local_surface_id) {
+ cc::CompositorFrame frame = cc::test::MakeCompositorFrame();
+ pass_list->swap(frame.render_pass_list);
+
+ support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
+ }
+
+ FrameSinkManagerImpl manager_;
+ std::unique_ptr<CompositorFrameSinkSupport> support_;
+ LocalSurfaceIdAllocator id_allocator_;
+ scoped_refptr<base::NullTaskRunner> task_runner_;
+ cc::TestSharedBitmapManager shared_bitmap_manager_;
+ std::unique_ptr<cc::BeginFrameSource> begin_frame_source_;
+ std::unique_ptr<Display> display_;
+ TestSoftwareOutputDevice* software_output_device_ = nullptr;
+ cc::FakeOutputSurface* output_surface_ = nullptr;
+ TestDisplayScheduler* scheduler_ = nullptr;
+};
+
+class StubDisplayClient : public DisplayClient {
+ public:
+ void DisplayOutputSurfaceLost() override {}
+ void DisplayWillDrawAndSwap(
+ bool will_draw_and_swap,
+ const cc::RenderPassList& render_passes) override {}
+ void DisplayDidDrawAndSwap() override {}
+};
+
+void CopyCallback(bool* called, std::unique_ptr<cc::CopyOutputResult> result) {
+ *called = true;
+}
+
+// Check that frame is damaged and swapped only under correct conditions.
+TEST_F(DisplayTest, DisplayDamaged) {
+ RendererSettings settings;
+ settings.partial_swap_enabled = true;
+ settings.finish_rendering_on_resize = true;
+ SetUpDisplay(settings, nullptr);
+ gfx::ColorSpace color_space_1 = gfx::ColorSpace::CreateXYZD50();
+ gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSCRGBLinear();
+
+ StubDisplayClient client;
+ display_->Initialize(&client, manager_.surface_manager());
+ display_->SetColorSpace(color_space_1, color_space_1);
+
+ LocalSurfaceId local_surface_id(id_allocator_.GenerateId());
+ EXPECT_FALSE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+ display_->SetLocalSurfaceId(local_surface_id, 1.f);
+ EXPECT_FALSE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_TRUE(scheduler_->has_new_root_surface);
+
+ scheduler_->ResetDamageForTest();
+ display_->Resize(gfx::Size(100, 100));
+ EXPECT_FALSE(scheduler_->damaged);
+ EXPECT_TRUE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ // First draw from surface should have full damage.
+ cc::RenderPassList pass_list;
+ auto pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+ pass->id = 1u;
+ pass_list.push_back(std::move(pass));
+
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ EXPECT_FALSE(scheduler_->swapped);
+ EXPECT_EQ(0u, output_surface_->num_sent_frames());
+ EXPECT_EQ(gfx::ColorSpace(), output_surface_->last_reshape_color_space());
+ display_->DrawAndSwap();
+ EXPECT_EQ(color_space_1, output_surface_->last_reshape_color_space());
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(1u, output_surface_->num_sent_frames());
+ EXPECT_EQ(gfx::Size(100, 100),
+ software_output_device_->viewport_pixel_size());
+ EXPECT_EQ(gfx::Rect(0, 0, 100, 100), software_output_device_->damage_rect());
+ {
+ // Only damaged portion should be swapped.
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+ pass->id = 1u;
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ EXPECT_EQ(color_space_1, output_surface_->last_reshape_color_space());
+ display_->SetColorSpace(color_space_2, color_space_2);
+ display_->DrawAndSwap();
+ EXPECT_EQ(color_space_2, output_surface_->last_reshape_color_space());
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(2u, output_surface_->num_sent_frames());
+ EXPECT_EQ(gfx::Size(100, 100),
+ software_output_device_->viewport_pixel_size());
+ EXPECT_EQ(gfx::Rect(10, 10, 1, 1), software_output_device_->damage_rect());
+ }
+
+ {
+ // Pass has no damage so shouldn't be swapped.
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+ pass->id = 1u;
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ display_->DrawAndSwap();
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(2u, output_surface_->num_sent_frames());
+ }
+
+ {
+ // Pass is wrong size so shouldn't be swapped.
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 99, 99);
+ pass->damage_rect = gfx::Rect(10, 10, 10, 10);
+ pass->id = 1u;
+
+ local_surface_id = id_allocator_.GenerateId();
+ display_->SetLocalSurfaceId(local_surface_id, 1.f);
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ display_->DrawAndSwap();
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(2u, output_surface_->num_sent_frames());
+ }
+
+ {
+ // Previous frame wasn't swapped, so next swap should have full damage.
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+ pass->id = 1u;
+
+ local_surface_id = id_allocator_.GenerateId();
+ display_->SetLocalSurfaceId(local_surface_id, 1.f);
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ display_->DrawAndSwap();
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(3u, output_surface_->num_sent_frames());
+ EXPECT_EQ(gfx::Rect(0, 0, 100, 100),
+ software_output_device_->damage_rect());
+ }
+
+ {
+ // Pass has copy output request so should be swapped.
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+ bool copy_called = false;
+ pass->copy_requests.push_back(cc::CopyOutputRequest::CreateRequest(
+ base::BindOnce(&CopyCallback, &copy_called)));
+ pass->id = 1u;
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ display_->DrawAndSwap();
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(4u, output_surface_->num_sent_frames());
+ EXPECT_TRUE(copy_called);
+ }
+
+ // Pass has no damage, so shouldn't be swapped, but latency info should be
+ // saved for next swap.
+ {
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+ pass->id = 1u;
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+
+ cc::CompositorFrame frame = cc::test::MakeCompositorFrame();
+ pass_list.swap(frame.render_pass_list);
+ frame.metadata.latency_info.push_back(ui::LatencyInfo());
+
+ support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ display_->DrawAndSwap();
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(4u, output_surface_->num_sent_frames());
+ }
+
+ // Resize should cause a swap if no frame was swapped at the previous size.
+ {
+ local_surface_id = id_allocator_.GenerateId();
+ display_->SetLocalSurfaceId(local_surface_id, 1.f);
+ scheduler_->swapped = false;
+ display_->Resize(gfx::Size(200, 200));
+ EXPECT_FALSE(scheduler_->swapped);
+ EXPECT_EQ(4u, output_surface_->num_sent_frames());
+
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 200, 200);
+ pass->damage_rect = gfx::Rect(10, 10, 10, 10);
+ pass->id = 1u;
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+
+ cc::CompositorFrame frame = cc::test::MakeCompositorFrame();
+ pass_list.swap(frame.render_pass_list);
+
+ support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ display_->Resize(gfx::Size(100, 100));
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(5u, output_surface_->num_sent_frames());
+
+ // Latency info from previous frame should be sent now.
+ EXPECT_EQ(1u, output_surface_->last_sent_frame()->latency_info.size());
+ }
+
+ {
+ local_surface_id = id_allocator_.GenerateId();
+ display_->SetLocalSurfaceId(local_surface_id, 1.0f);
+ // Surface that's damaged completely should be resized and swapped.
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 99, 99);
+ pass->damage_rect = gfx::Rect(0, 0, 99, 99);
+ pass->id = 1u;
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler_->display_resized_);
+ EXPECT_FALSE(scheduler_->has_new_root_surface);
+
+ scheduler_->swapped = false;
+ display_->DrawAndSwap();
+ EXPECT_TRUE(scheduler_->swapped);
+ EXPECT_EQ(6u, output_surface_->num_sent_frames());
+ EXPECT_EQ(gfx::Size(100, 100),
+ software_output_device_->viewport_pixel_size());
+ EXPECT_EQ(gfx::Rect(0, 0, 100, 100),
+ software_output_device_->damage_rect());
+ EXPECT_EQ(0u, output_surface_->last_sent_frame()->latency_info.size());
+ }
+ TearDownDisplay();
+}
+
+// Check LatencyInfo storage is cleaned up if it exceeds the limit.
+TEST_F(DisplayTest, MaxLatencyInfoCap) {
+ RendererSettings settings;
+ settings.partial_swap_enabled = true;
+ settings.finish_rendering_on_resize = true;
+ SetUpDisplay(settings, nullptr);
+ gfx::ColorSpace color_space_1 = gfx::ColorSpace::CreateXYZD50();
+ gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSCRGBLinear();
+
+ StubDisplayClient client;
+ display_->Initialize(&client, manager_.surface_manager());
+ display_->SetColorSpace(color_space_1, color_space_1);
+
+ LocalSurfaceId local_surface_id(id_allocator_.GenerateId());
+ display_->SetLocalSurfaceId(local_surface_id, 1.f);
+
+ scheduler_->ResetDamageForTest();
+ display_->Resize(gfx::Size(100, 100));
+
+ cc::RenderPassList pass_list;
+ auto pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+ pass->id = 1u;
+ pass_list.push_back(std::move(pass));
+
+ scheduler_->ResetDamageForTest();
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+
+ display_->DrawAndSwap();
+
+ // This is the same as LatencyInfo::kMaxLatencyInfoNumber.
+ const size_t max_latency_info_count = 100;
+ for (size_t i = 0; i <= max_latency_info_count; ++i) {
+ pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+ pass->id = 1u;
+
+ pass_list.push_back(std::move(pass));
+ scheduler_->ResetDamageForTest();
+
+ cc::CompositorFrame frame = cc::test::MakeCompositorFrame();
+ pass_list.swap(frame.render_pass_list);
+ frame.metadata.latency_info.push_back(ui::LatencyInfo());
+
+ support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
+
+ display_->DrawAndSwap();
+
+ if (i < max_latency_info_count)
+ EXPECT_EQ(i + 1, display_->stored_latency_info_size_for_testing());
+ else
+ EXPECT_EQ(0u, display_->stored_latency_info_size_for_testing());
+ }
+
+ TearDownDisplay();
+}
+
+class MockedContext : public cc::TestWebGraphicsContext3D {
+ public:
+ MOCK_METHOD0(shallowFinishCHROMIUM, void());
+};
+
+TEST_F(DisplayTest, Finish) {
+ LocalSurfaceId local_surface_id1(id_allocator_.GenerateId());
+ LocalSurfaceId local_surface_id2(id_allocator_.GenerateId());
+
+ RendererSettings settings;
+ settings.partial_swap_enabled = true;
+ settings.finish_rendering_on_resize = true;
+
+ auto context = base::MakeUnique<MockedContext>();
+ MockedContext* context_ptr = context.get();
+ EXPECT_CALL(*context_ptr, shallowFinishCHROMIUM()).Times(0);
+
+ SetUpDisplay(settings, std::move(context));
+
+ StubDisplayClient client;
+ display_->Initialize(&client, manager_.surface_manager());
+
+ display_->SetLocalSurfaceId(local_surface_id1, 1.f);
+
+ display_->Resize(gfx::Size(100, 100));
+
+ {
+ cc::RenderPassList pass_list;
+ auto pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+ pass->id = 1u;
+ pass_list.push_back(std::move(pass));
+
+ SubmitCompositorFrame(&pass_list, local_surface_id1);
+ }
+
+ display_->DrawAndSwap();
+
+ // First resize and draw shouldn't finish.
+ testing::Mock::VerifyAndClearExpectations(context_ptr);
+
+ EXPECT_CALL(*context_ptr, shallowFinishCHROMIUM());
+ display_->Resize(gfx::Size(150, 150));
+ testing::Mock::VerifyAndClearExpectations(context_ptr);
+
+ // Another resize without a swap doesn't need to finish.
+ EXPECT_CALL(*context_ptr, shallowFinishCHROMIUM()).Times(0);
+ display_->SetLocalSurfaceId(local_surface_id2, 1.f);
+ display_->Resize(gfx::Size(200, 200));
+ testing::Mock::VerifyAndClearExpectations(context_ptr);
+
+ EXPECT_CALL(*context_ptr, shallowFinishCHROMIUM()).Times(0);
+ {
+ cc::RenderPassList pass_list;
+ auto pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 200, 200);
+ pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+ pass->id = 1u;
+ pass_list.push_back(std::move(pass));
+
+ SubmitCompositorFrame(&pass_list, local_surface_id2);
+ }
+
+ display_->DrawAndSwap();
+
+ testing::Mock::VerifyAndClearExpectations(context_ptr);
+
+ EXPECT_CALL(*context_ptr, shallowFinishCHROMIUM());
+ display_->Resize(gfx::Size(250, 250));
+ testing::Mock::VerifyAndClearExpectations(context_ptr);
+ TearDownDisplay();
+}
+
+class CountLossDisplayClient : public StubDisplayClient {
+ public:
+ CountLossDisplayClient() = default;
+
+ void DisplayOutputSurfaceLost() override { ++loss_count_; }
+
+ int loss_count() const { return loss_count_; }
+
+ private:
+ int loss_count_ = 0;
+};
+
+TEST_F(DisplayTest, ContextLossInformsClient) {
+ SetUpDisplay(RendererSettings(), cc::TestWebGraphicsContext3D::Create());
+
+ CountLossDisplayClient client;
+ display_->Initialize(&client, manager_.surface_manager());
+
+ // Verify DidLoseOutputSurface callback is hooked up correctly.
+ EXPECT_EQ(0, client.loss_count());
+ output_surface_->context_provider()->ContextGL()->LoseContextCHROMIUM(
+ GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
+ output_surface_->context_provider()->ContextGL()->Flush();
+ EXPECT_EQ(1, client.loss_count());
+ TearDownDisplay();
+}
+
+// Regression test for https://crbug.com/727162: Submitting a CompositorFrame to
+// a surface should only cause damage on the Display the surface belongs to.
+// There should not be a side-effect on other Displays.
+TEST_F(DisplayTest, CompositorFrameDamagesCorrectDisplay) {
+ RendererSettings settings;
+ LocalSurfaceId local_surface_id(id_allocator_.GenerateId());
+
+ // Set up first display.
+ SetUpDisplay(settings, nullptr);
+ StubDisplayClient client;
+ display_->Initialize(&client, manager_.surface_manager());
+ display_->SetLocalSurfaceId(local_surface_id, 1.f);
+
+ // Set up second frame sink + display.
+ auto support2 = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kAnotherFrameSinkId, true /* is_root */,
+ true /* handles_frame_sink_id_invalidation */,
+ true /* needs_sync_points */);
+ auto begin_frame_source2 = base::MakeUnique<cc::StubBeginFrameSource>();
+ auto scheduler_for_display2 = base::MakeUnique<TestDisplayScheduler>(
+ begin_frame_source2.get(), task_runner_.get());
+ TestDisplayScheduler* scheduler2 = scheduler_for_display2.get();
+ auto display2 = CreateDisplay(
+ settings, kAnotherFrameSinkId, std::move(scheduler_for_display2),
+ cc::FakeOutputSurface::CreateSoftware(
+ base::MakeUnique<TestSoftwareOutputDevice>()));
+ manager_.RegisterBeginFrameSource(begin_frame_source2.get(),
+ kAnotherFrameSinkId);
+ StubDisplayClient client2;
+ display2->Initialize(&client2, manager_.surface_manager());
+ display2->SetLocalSurfaceId(local_surface_id, 1.f);
+
+ display_->Resize(gfx::Size(100, 100));
+ display2->Resize(gfx::Size(100, 100));
+
+ scheduler_->ResetDamageForTest();
+ scheduler2->ResetDamageForTest();
+ EXPECT_FALSE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler2->damaged);
+
+ // Submit a frame for display_ with full damage.
+ cc::RenderPassList pass_list;
+ auto pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+ pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+ pass->id = 1;
+ pass_list.push_back(std::move(pass));
+
+ SubmitCompositorFrame(&pass_list, local_surface_id);
+
+ // Should have damaged only display_ but not display2.
+ EXPECT_TRUE(scheduler_->damaged);
+ EXPECT_FALSE(scheduler2->damaged);
+ manager_.UnregisterBeginFrameSource(begin_frame_source2.get());
+ TearDownDisplay();
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/display/surface_aggregator.cc b/chromium/components/viz/service/display/surface_aggregator.cc
new file mode 100644
index 00000000000..0d6cdc7e90c
--- /dev/null
+++ b/chromium/components/viz/service/display/surface_aggregator.cc
@@ -0,0 +1,992 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/surface_aggregator.h"
+
+#include <stddef.h>
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/containers/adapters.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/base/math_util.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/quads/draw_quad.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/resources/resource_provider.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_client.h"
+#include "cc/surfaces/surface_manager.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+
+namespace viz {
+namespace {
+
+// Maximum bucket size for the UMA stats.
+constexpr int kUmaStatMaxSurfaces = 30;
+
+const char kUmaValidSurface[] =
+ "Compositing.SurfaceAggregator.SurfaceDrawQuad.ValidSurface";
+const char kUmaMissingSurface[] =
+ "Compositing.SurfaceAggregator.SurfaceDrawQuad.MissingSurface";
+const char kUmaNoActiveFrame[] =
+ "Compositing.SurfaceAggregator.SurfaceDrawQuad.NoActiveFrame";
+
+void MoveMatchingRequests(
+ cc::RenderPassId render_pass_id,
+ std::multimap<cc::RenderPassId, std::unique_ptr<cc::CopyOutputRequest>>*
+ copy_requests,
+ std::vector<std::unique_ptr<cc::CopyOutputRequest>>* output_requests) {
+ auto request_range = copy_requests->equal_range(render_pass_id);
+ for (auto it = request_range.first; it != request_range.second; ++it) {
+ DCHECK(it->second);
+ output_requests->push_back(std::move(it->second));
+ }
+ copy_requests->erase(request_range.first, request_range.second);
+}
+
+// Returns true if the damage rect is valid.
+bool CalculateQuadSpaceDamageRect(
+ const gfx::Transform& quad_to_target_transform,
+ const gfx::Transform& target_to_root_transform,
+ const gfx::Rect& root_damage_rect,
+ gfx::Rect* quad_space_damage_rect) {
+ gfx::Transform quad_to_root_transform(target_to_root_transform,
+ quad_to_target_transform);
+ gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+ bool inverse_valid = quad_to_root_transform.GetInverse(&inverse_transform);
+ if (!inverse_valid)
+ return false;
+
+ *quad_space_damage_rect = cc::MathUtil::ProjectEnclosingClippedRect(
+ inverse_transform, root_damage_rect);
+ return true;
+}
+
+} // namespace
+
+SurfaceAggregator::SurfaceAggregator(cc::SurfaceManager* manager,
+ cc::ResourceProvider* provider,
+ bool aggregate_only_damaged)
+ : manager_(manager),
+ provider_(provider),
+ next_render_pass_id_(1),
+ aggregate_only_damaged_(aggregate_only_damaged),
+ weak_factory_(this) {
+ DCHECK(manager_);
+}
+
+SurfaceAggregator::~SurfaceAggregator() {
+ // Notify client of all surfaces being removed.
+ contained_surfaces_.clear();
+ ProcessAddedAndRemovedSurfaces();
+}
+
+SurfaceAggregator::PrewalkResult::PrewalkResult() {}
+
+SurfaceAggregator::PrewalkResult::~PrewalkResult() {}
+
+// Create a clip rect for an aggregated quad from the original clip rect and
+// the clip rect from the surface it's on.
+SurfaceAggregator::ClipData SurfaceAggregator::CalculateClipRect(
+ const ClipData& surface_clip,
+ const ClipData& quad_clip,
+ const gfx::Transform& target_transform) {
+ ClipData out_clip;
+ if (surface_clip.is_clipped)
+ out_clip = surface_clip;
+
+ if (quad_clip.is_clipped) {
+ // TODO(jamesr): This only works if target_transform maps integer
+ // rects to integer rects.
+ gfx::Rect final_clip =
+ cc::MathUtil::MapEnclosingClippedRect(target_transform, quad_clip.rect);
+ if (out_clip.is_clipped)
+ out_clip.rect.Intersect(final_clip);
+ else
+ out_clip.rect = final_clip;
+ out_clip.is_clipped = true;
+ }
+
+ return out_clip;
+}
+
+cc::RenderPassId SurfaceAggregator::RemapPassId(
+ cc::RenderPassId surface_local_pass_id,
+ const SurfaceId& surface_id) {
+ auto key = std::make_pair(surface_id, surface_local_pass_id);
+ auto it = render_pass_allocator_map_.find(key);
+ if (it != render_pass_allocator_map_.end()) {
+ it->second.in_use = true;
+ return it->second.id;
+ }
+
+ RenderPassInfo render_pass_info;
+ render_pass_info.id = next_render_pass_id_++;
+ render_pass_allocator_map_[key] = render_pass_info;
+ return render_pass_info.id;
+}
+
+int SurfaceAggregator::ChildIdForSurface(cc::Surface* surface) {
+ auto it = surface_id_to_resource_child_id_.find(surface->surface_id());
+ if (it == surface_id_to_resource_child_id_.end()) {
+ int child_id = provider_->CreateChild(
+ base::Bind(&SurfaceAggregator::UnrefResources, surface->client()));
+ provider_->SetChildNeedsSyncTokens(child_id, surface->needs_sync_tokens());
+ surface_id_to_resource_child_id_[surface->surface_id()] = child_id;
+ return child_id;
+ } else {
+ return it->second;
+ }
+}
+
+gfx::Rect SurfaceAggregator::DamageRectForSurface(
+ const cc::Surface* surface,
+ const cc::RenderPass& source,
+ const gfx::Rect& full_rect) const {
+ auto it = previous_contained_surfaces_.find(surface->surface_id());
+ if (it != previous_contained_surfaces_.end()) {
+ int previous_index = it->second;
+ if (previous_index == surface->frame_index())
+ return gfx::Rect();
+ }
+ const SurfaceId& previous_surface_id = surface->previous_frame_surface_id();
+
+ if (surface->surface_id() != previous_surface_id) {
+ it = previous_contained_surfaces_.find(previous_surface_id);
+ }
+ if (it != previous_contained_surfaces_.end()) {
+ int previous_index = it->second;
+ if (previous_index == surface->frame_index() - 1)
+ return source.damage_rect;
+ }
+
+ return full_rect;
+}
+
+// static
+void SurfaceAggregator::UnrefResources(
+ base::WeakPtr<cc::SurfaceClient> surface_client,
+ const std::vector<cc::ReturnedResource>& resources,
+ cc::BlockingTaskRunner* main_thread_task_runner) {
+ if (surface_client)
+ surface_client->UnrefResources(resources);
+}
+
+void SurfaceAggregator::HandleSurfaceQuad(
+ const cc::SurfaceDrawQuad* surface_quad,
+ const gfx::Transform& target_transform,
+ const ClipData& clip_rect,
+ cc::RenderPass* dest_pass,
+ bool ignore_undamaged,
+ gfx::Rect* damage_rect_in_quad_space,
+ bool* damage_rect_in_quad_space_valid) {
+ SurfaceId surface_id = surface_quad->surface_id;
+ // If this surface's id is already in our referenced set then it creates
+ // a cycle in the graph and should be dropped.
+ if (referenced_surfaces_.count(surface_id))
+ return;
+ cc::Surface* surface = manager_->GetSurfaceForId(surface_id);
+ if (!surface || !surface->HasActiveFrame()) {
+ if (surface_quad->fallback_quad) {
+ HandleSurfaceQuad(surface_quad->fallback_quad, target_transform,
+ clip_rect, dest_pass, ignore_undamaged,
+ damage_rect_in_quad_space,
+ damage_rect_in_quad_space_valid);
+ } else if (!surface) {
+ ++uma_stats_.missing_surface;
+ } else {
+ ++uma_stats_.no_active_frame;
+ }
+ return;
+ }
+ ++uma_stats_.valid_surface;
+
+ if (ignore_undamaged) {
+ gfx::Transform quad_to_target_transform(
+ target_transform,
+ surface_quad->shared_quad_state->quad_to_target_transform);
+ *damage_rect_in_quad_space_valid = CalculateQuadSpaceDamageRect(
+ quad_to_target_transform, dest_pass->transform_to_root_target,
+ root_damage_rect_, damage_rect_in_quad_space);
+ if (*damage_rect_in_quad_space_valid &&
+ !damage_rect_in_quad_space->Intersects(surface_quad->visible_rect)) {
+ return;
+ }
+ }
+
+ const cc::CompositorFrame& frame = surface->GetActiveFrame();
+
+ // A map keyed by RenderPass id.
+ cc::Surface::CopyRequestsMap copy_requests;
+ surface->TakeCopyOutputRequests(&copy_requests);
+
+ const cc::RenderPassList& render_pass_list = frame.render_pass_list;
+ if (!valid_surfaces_.count(surface->surface_id())) {
+ for (auto& request : copy_requests)
+ request.second->SendEmptyResult();
+ return;
+ }
+
+ referenced_surfaces_.insert(surface_id);
+ // TODO(vmpstr): provider check is a hack for unittests that don't set up a
+ // resource provider.
+ cc::ResourceProvider::ResourceIdMap empty_map;
+ const auto& child_to_parent_map =
+ provider_ ? provider_->GetChildToParentMap(ChildIdForSurface(surface))
+ : empty_map;
+ bool merge_pass =
+ surface_quad->shared_quad_state->opacity == 1.f && copy_requests.empty();
+
+ const cc::RenderPassList& referenced_passes = render_pass_list;
+ size_t passes_to_copy =
+ merge_pass ? referenced_passes.size() - 1 : referenced_passes.size();
+ for (size_t j = 0; j < passes_to_copy; ++j) {
+ const cc::RenderPass& source = *referenced_passes[j];
+
+ size_t sqs_size = source.shared_quad_state_list.size();
+ size_t dq_size = source.quad_list.size();
+ std::unique_ptr<cc::RenderPass> copy_pass(
+ cc::RenderPass::Create(sqs_size, dq_size));
+
+ cc::RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id);
+
+ copy_pass->SetAll(remapped_pass_id, source.output_rect, source.output_rect,
+ source.transform_to_root_target, source.filters,
+ source.background_filters, blending_color_space_,
+ source.has_transparent_background,
+ source.cache_render_pass,
+ source.has_damage_from_contributing_content);
+
+ MoveMatchingRequests(source.id, &copy_requests, &copy_pass->copy_requests);
+
+ // Contributing passes aggregated in to the pass list need to take the
+ // transform of the surface quad into account to update their transform to
+ // the root surface.
+ copy_pass->transform_to_root_target.ConcatTransform(
+ surface_quad->shared_quad_state->quad_to_target_transform);
+ copy_pass->transform_to_root_target.ConcatTransform(target_transform);
+ copy_pass->transform_to_root_target.ConcatTransform(
+ dest_pass->transform_to_root_target);
+
+ CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
+ child_to_parent_map, gfx::Transform(), ClipData(),
+ copy_pass.get(), surface_id);
+
+ // If the render pass has copy requests, or should be cached, or has
+ // moving-pixel filters, or in a moving-pixel surface, we should damage the
+ // whole output rect so that we always drawn the full content. Otherwise, we
+ // might have incompleted copy request, or cached patially drawn render
+ // pass.
+ if (!copy_request_passes_.count(remapped_pass_id) &&
+ !copy_pass->cache_render_pass &&
+ !moved_pixel_passes_.count(remapped_pass_id)) {
+ gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+ if (copy_pass->transform_to_root_target.GetInverse(&inverse_transform)) {
+ gfx::Rect damage_rect_in_render_pass_space =
+ cc::MathUtil::ProjectEnclosingClippedRect(inverse_transform,
+ root_damage_rect_);
+ copy_pass->damage_rect.Intersect(damage_rect_in_render_pass_space);
+ }
+ }
+
+ if (copy_pass->has_damage_from_contributing_content)
+ contributing_content_damaged_passes_.insert(copy_pass->id);
+ dest_pass_list_->push_back(std::move(copy_pass));
+ }
+
+ gfx::Transform surface_transform =
+ surface_quad->shared_quad_state->quad_to_target_transform;
+ surface_transform.ConcatTransform(target_transform);
+
+ const auto& last_pass = *render_pass_list.back();
+ // This will check if all the surface_quads (including child surfaces) has
+ // damage because HandleSurfaceQuad is a recursive call by calling
+ // CopyQuadsToPass in it.
+ dest_pass->has_damage_from_contributing_content |=
+ !DamageRectForSurface(surface, last_pass, last_pass.output_rect)
+ .IsEmpty();
+
+ if (merge_pass) {
+ // TODO(jamesr): Clean up last pass special casing.
+ const cc::QuadList& quads = last_pass.quad_list;
+
+ // Intersect the transformed visible rect and the clip rect to create a
+ // smaller cliprect for the quad.
+ ClipData surface_quad_clip_rect(
+ true, cc::MathUtil::MapEnclosingClippedRect(
+ surface_quad->shared_quad_state->quad_to_target_transform,
+ surface_quad->visible_rect));
+ if (surface_quad->shared_quad_state->is_clipped) {
+ surface_quad_clip_rect.rect.Intersect(
+ surface_quad->shared_quad_state->clip_rect);
+ }
+
+ ClipData quads_clip =
+ CalculateClipRect(clip_rect, surface_quad_clip_rect, target_transform);
+
+ CopyQuadsToPass(quads, last_pass.shared_quad_state_list,
+ child_to_parent_map, surface_transform, quads_clip,
+ dest_pass, surface_id);
+ } else {
+ cc::RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id);
+
+ auto* shared_quad_state =
+ CopySharedQuadState(surface_quad->shared_quad_state, target_transform,
+ clip_rect, dest_pass);
+
+ auto* quad = dest_pass->CreateAndAppendDrawQuad<cc::RenderPassDrawQuad>();
+ quad->SetNew(shared_quad_state, surface_quad->rect,
+ surface_quad->visible_rect, remapped_pass_id, 0, gfx::RectF(),
+ gfx::Size(), gfx::Vector2dF(), gfx::PointF(),
+ gfx::RectF(surface_quad->rect));
+ }
+
+ // Need to re-query since referenced_surfaces_ iterators are not stable.
+ referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
+}
+
+void SurfaceAggregator::AddColorConversionPass() {
+ if (dest_pass_list_->empty())
+ return;
+
+ auto* root_render_pass = dest_pass_list_->back().get();
+ if (root_render_pass->color_space == output_color_space_)
+ return;
+
+ gfx::Rect output_rect = root_render_pass->output_rect;
+ CHECK(root_render_pass->transform_to_root_target == gfx::Transform());
+
+ if (!color_conversion_render_pass_id_)
+ color_conversion_render_pass_id_ = next_render_pass_id_++;
+
+ auto color_conversion_pass = cc::RenderPass::Create(1, 1);
+ color_conversion_pass->SetNew(color_conversion_render_pass_id_, output_rect,
+ root_render_pass->damage_rect,
+ root_render_pass->transform_to_root_target);
+ color_conversion_pass->color_space = output_color_space_;
+
+ auto* shared_quad_state =
+ color_conversion_pass->CreateAndAppendSharedQuadState();
+ shared_quad_state->quad_layer_rect = output_rect;
+ shared_quad_state->visible_quad_layer_rect = output_rect;
+ shared_quad_state->opacity = 1.f;
+
+ auto* quad =
+ color_conversion_pass->CreateAndAppendDrawQuad<cc::RenderPassDrawQuad>();
+ quad->SetNew(shared_quad_state, output_rect, output_rect,
+ root_render_pass->id, 0, gfx::RectF(), gfx::Size(),
+ gfx::Vector2dF(), gfx::PointF(), gfx::RectF(output_rect));
+ dest_pass_list_->push_back(std::move(color_conversion_pass));
+}
+
+cc::SharedQuadState* SurfaceAggregator::CopySharedQuadState(
+ const cc::SharedQuadState* source_sqs,
+ const gfx::Transform& target_transform,
+ const ClipData& clip_rect,
+ cc::RenderPass* dest_render_pass) {
+ auto* copy_shared_quad_state =
+ dest_render_pass->CreateAndAppendSharedQuadState();
+ *copy_shared_quad_state = *source_sqs;
+ // target_transform contains any transformation that may exist
+ // between the context that these quads are being copied from (i.e. the
+ // surface's draw transform when aggregated from within a surface) to the
+ // target space of the pass. This will be identity except when copying the
+ // root draw pass from a surface into a pass when the surface draw quad's
+ // transform is not identity.
+ copy_shared_quad_state->quad_to_target_transform.ConcatTransform(
+ target_transform);
+
+ ClipData new_clip_rect = CalculateClipRect(
+ clip_rect, ClipData(source_sqs->is_clipped, source_sqs->clip_rect),
+ target_transform);
+ copy_shared_quad_state->is_clipped = new_clip_rect.is_clipped;
+ copy_shared_quad_state->clip_rect = new_clip_rect.rect;
+ return copy_shared_quad_state;
+}
+
+void SurfaceAggregator::CopyQuadsToPass(
+ const cc::QuadList& source_quad_list,
+ const cc::SharedQuadStateList& source_shared_quad_state_list,
+ const std::unordered_map<cc::ResourceId, cc::ResourceId>&
+ child_to_parent_map,
+ const gfx::Transform& target_transform,
+ const ClipData& clip_rect,
+ cc::RenderPass* dest_pass,
+ const SurfaceId& surface_id) {
+ const cc::SharedQuadState* last_copied_source_shared_quad_state = nullptr;
+ const cc::SharedQuadState* dest_shared_quad_state = nullptr;
+ // If the current frame has copy requests or cached render passes, then
+ // aggregate the entire thing, as otherwise parts of the copy requests may be
+ // ignored and we could cache partially drawn render pass.
+ const bool ignore_undamaged =
+ aggregate_only_damaged_ && !has_copy_requests_ &&
+ !has_cached_render_passes_ && !moved_pixel_passes_.count(dest_pass->id);
+ // Damage rect in the quad space of the current shared quad state.
+ // TODO(jbauman): This rect may contain unnecessary area if
+ // transform isn't axis-aligned.
+ gfx::Rect damage_rect_in_quad_space;
+ bool damage_rect_in_quad_space_valid = false;
+
+#if DCHECK_IS_ON()
+ // If quads have come in with SharedQuadState out of order, or when quads have
+ // invalid SharedQuadState pointer, it should DCHECK.
+ auto sqs_iter = source_shared_quad_state_list.cbegin();
+ for (auto* quad : source_quad_list) {
+ while (sqs_iter != source_shared_quad_state_list.cend() &&
+ quad->shared_quad_state != *sqs_iter) {
+ ++sqs_iter;
+ }
+ DCHECK(sqs_iter != source_shared_quad_state_list.cend());
+ }
+#endif
+
+ for (auto* quad : source_quad_list) {
+ if (quad->material == cc::DrawQuad::SURFACE_CONTENT) {
+ const auto* surface_quad = cc::SurfaceDrawQuad::MaterialCast(quad);
+ // HandleSurfaceQuad may add other shared quad state, so reset the
+ // current data.
+ last_copied_source_shared_quad_state = nullptr;
+
+ // The primary SurfaceDrawQuad should have already dealt with the fallback
+ // DrawQuad.
+ if (surface_quad->surface_draw_quad_type ==
+ cc::SurfaceDrawQuadType::FALLBACK)
+ continue;
+
+ HandleSurfaceQuad(surface_quad, target_transform, clip_rect, dest_pass,
+ ignore_undamaged, &damage_rect_in_quad_space,
+ &damage_rect_in_quad_space_valid);
+ } else {
+ if (quad->shared_quad_state != last_copied_source_shared_quad_state) {
+ dest_shared_quad_state = CopySharedQuadState(
+ quad->shared_quad_state, target_transform, clip_rect, dest_pass);
+ last_copied_source_shared_quad_state = quad->shared_quad_state;
+ if (aggregate_only_damaged_ && !has_copy_requests_ &&
+ !has_cached_render_passes_) {
+ damage_rect_in_quad_space_valid = CalculateQuadSpaceDamageRect(
+ dest_shared_quad_state->quad_to_target_transform,
+ dest_pass->transform_to_root_target, root_damage_rect_,
+ &damage_rect_in_quad_space);
+ }
+ }
+
+ if (ignore_undamaged) {
+ if (damage_rect_in_quad_space_valid &&
+ !damage_rect_in_quad_space.Intersects(quad->visible_rect))
+ continue;
+ }
+
+ cc::DrawQuad* dest_quad;
+ if (quad->material == cc::DrawQuad::RENDER_PASS) {
+ const auto* pass_quad = cc::RenderPassDrawQuad::MaterialCast(quad);
+ cc::RenderPassId original_pass_id = pass_quad->render_pass_id;
+ cc::RenderPassId remapped_pass_id =
+ RemapPassId(original_pass_id, surface_id);
+
+ // If the RenderPassDrawQuad is referring to other render pass with the
+ // |has_damage_from_contributing_content| set on it, then the dest_pass
+ // should have the flag set on it as well.
+ if (contributing_content_damaged_passes_.count(remapped_pass_id))
+ dest_pass->has_damage_from_contributing_content = true;
+
+ dest_quad = dest_pass->CopyFromAndAppendRenderPassDrawQuad(
+ pass_quad, dest_shared_quad_state, remapped_pass_id);
+ } else if (quad->material == cc::DrawQuad::TEXTURE_CONTENT) {
+ const auto* texture_quad = cc::TextureDrawQuad::MaterialCast(quad);
+ if (texture_quad->secure_output_only &&
+ (!output_is_secure_ || copy_request_passes_.count(dest_pass->id))) {
+ auto* solid_color_quad =
+ dest_pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ solid_color_quad->SetNew(dest_shared_quad_state, quad->rect,
+ quad->visible_rect, SK_ColorBLACK, false);
+ dest_quad = solid_color_quad;
+ } else {
+ dest_quad = dest_pass->CopyFromAndAppendDrawQuad(
+ quad, dest_shared_quad_state);
+ }
+ } else {
+ dest_quad =
+ dest_pass->CopyFromAndAppendDrawQuad(quad, dest_shared_quad_state);
+ }
+ if (!child_to_parent_map.empty()) {
+ for (cc::ResourceId& resource_id : dest_quad->resources) {
+ auto it = child_to_parent_map.find(resource_id);
+ DCHECK(it != child_to_parent_map.end());
+
+ DCHECK_EQ(it->first, resource_id);
+ cc::ResourceId remapped_id = it->second;
+ resource_id = remapped_id;
+ }
+ }
+ }
+ }
+}
+
+void SurfaceAggregator::CopyPasses(const cc::CompositorFrame& frame,
+ cc::Surface* surface) {
+ // The root surface is allowed to have copy output requests, so grab them
+ // off its render passes. This map contains a set of CopyOutputRequests
+ // keyed by each RenderPass id.
+ cc::Surface::CopyRequestsMap copy_requests;
+ surface->TakeCopyOutputRequests(&copy_requests);
+
+ const auto& source_pass_list = frame.render_pass_list;
+ DCHECK(valid_surfaces_.count(surface->surface_id()));
+ if (!valid_surfaces_.count(surface->surface_id()))
+ return;
+
+ // TODO(vmpstr): provider check is a hack for unittests that don't set up a
+ // resource provider.
+ cc::ResourceProvider::ResourceIdMap empty_map;
+ const auto& child_to_parent_map =
+ provider_ ? provider_->GetChildToParentMap(ChildIdForSurface(surface))
+ : empty_map;
+ for (size_t i = 0; i < source_pass_list.size(); ++i) {
+ const auto& source = *source_pass_list[i];
+
+ size_t sqs_size = source.shared_quad_state_list.size();
+ size_t dq_size = source.quad_list.size();
+ auto copy_pass = cc::RenderPass::Create(sqs_size, dq_size);
+
+ MoveMatchingRequests(source.id, &copy_requests, &copy_pass->copy_requests);
+
+ cc::RenderPassId remapped_pass_id =
+ RemapPassId(source.id, surface->surface_id());
+
+ copy_pass->SetAll(remapped_pass_id, source.output_rect, source.output_rect,
+ source.transform_to_root_target, source.filters,
+ source.background_filters, blending_color_space_,
+ source.has_transparent_background,
+ source.cache_render_pass,
+ source.has_damage_from_contributing_content);
+
+ CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
+ child_to_parent_map, gfx::Transform(), ClipData(),
+ copy_pass.get(), surface->surface_id());
+
+ // If the render pass has copy requests, or should be cached, or has
+ // moving-pixel filters, or in a moving-pixel surface, we should damage the
+ // whole output rect so that we always drawn the full content. Otherwise, we
+ // might have incompleted copy request, or cached patially drawn render
+ // pass.
+ if (!copy_request_passes_.count(remapped_pass_id) &&
+ !copy_pass->cache_render_pass &&
+ !moved_pixel_passes_.count(remapped_pass_id)) {
+ gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+ if (copy_pass->transform_to_root_target.GetInverse(&inverse_transform)) {
+ gfx::Rect damage_rect_in_render_pass_space =
+ cc::MathUtil::ProjectEnclosingClippedRect(inverse_transform,
+ root_damage_rect_);
+ copy_pass->damage_rect.Intersect(damage_rect_in_render_pass_space);
+ }
+ }
+
+ if (copy_pass->has_damage_from_contributing_content)
+ contributing_content_damaged_passes_.insert(copy_pass->id);
+ dest_pass_list_->push_back(std::move(copy_pass));
+ }
+}
+
+void SurfaceAggregator::ProcessAddedAndRemovedSurfaces() {
+ for (const auto& surface : previous_contained_surfaces_) {
+ if (!contained_surfaces_.count(surface.first)) {
+ // Release resources of removed surface.
+ auto it = surface_id_to_resource_child_id_.find(surface.first);
+ if (it != surface_id_to_resource_child_id_.end()) {
+ provider_->DestroyChild(it->second);
+ surface_id_to_resource_child_id_.erase(it);
+ }
+
+ // Notify client of removed surface.
+ cc::Surface* surface_ptr = manager_->GetSurfaceForId(surface.first);
+ if (surface_ptr) {
+ surface_ptr->RunDrawCallback();
+ }
+ }
+ }
+}
+
+// Walk the Surface tree from surface_id. Validate the resources of the current
+// surface and its descendants, check if there are any copy requests, and
+// return the combined damage rect.
+gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id,
+ bool in_moved_pixel_surface,
+ int parent_pass_id,
+ PrewalkResult* result) {
+ // This is for debugging a possible use after free.
+ // TODO(jbauman): Remove this once we have enough information.
+ // http://crbug.com/560181
+ base::WeakPtr<SurfaceAggregator> debug_weak_this = weak_factory_.GetWeakPtr();
+
+ if (referenced_surfaces_.count(surface_id))
+ return gfx::Rect();
+ cc::Surface* surface = manager_->GetSurfaceForId(surface_id);
+ if (!surface) {
+ contained_surfaces_[surface_id] = 0;
+ return gfx::Rect();
+ }
+ contained_surfaces_[surface_id] = surface->frame_index();
+ if (!surface->HasActiveFrame())
+ return gfx::Rect();
+ const cc::CompositorFrame& frame = surface->GetActiveFrame();
+ int child_id = 0;
+ // TODO(jbauman): hack for unit tests that don't set up rp
+ if (provider_) {
+ child_id = ChildIdForSurface(surface);
+ surface->RefResources(frame.resource_list);
+ provider_->ReceiveFromChild(child_id, frame.resource_list);
+ }
+ CHECK(debug_weak_this.get());
+
+ std::vector<cc::ResourceId> referenced_resources;
+ size_t reserve_size = frame.resource_list.size();
+ referenced_resources.reserve(reserve_size);
+
+ bool invalid_frame = false;
+ cc::ResourceProvider::ResourceIdMap empty_map;
+ const auto& child_to_parent_map =
+ provider_ ? provider_->GetChildToParentMap(child_id) : empty_map;
+
+ CHECK(debug_weak_this.get());
+ cc::RenderPassId remapped_pass_id =
+ RemapPassId(frame.render_pass_list.back()->id, surface_id);
+ if (in_moved_pixel_surface)
+ moved_pixel_passes_.insert(remapped_pass_id);
+ if (parent_pass_id)
+ render_pass_dependencies_[parent_pass_id].insert(remapped_pass_id);
+
+ struct SurfaceInfo {
+ SurfaceInfo(const SurfaceId& id,
+ bool has_moved_pixels,
+ cc::RenderPassId parent_pass_id,
+ const gfx::Transform& target_to_surface_transform)
+ : id(id),
+ has_moved_pixels(has_moved_pixels),
+ parent_pass_id(parent_pass_id),
+ target_to_surface_transform(target_to_surface_transform) {}
+
+ SurfaceId id;
+ bool has_moved_pixels;
+ cc::RenderPassId parent_pass_id;
+ gfx::Transform target_to_surface_transform;
+ };
+ std::vector<SurfaceInfo> child_surfaces;
+
+ gfx::Rect pixel_moving_background_filters_rect;
+ // This data is created once and typically small or empty. Collect all items
+ // and pass to a flat_vector to sort once.
+ std::vector<cc::RenderPassId> pixel_moving_background_filter_passes_data;
+ for (const auto& render_pass : frame.render_pass_list) {
+ if (render_pass->background_filters.HasFilterThatMovesPixels()) {
+ pixel_moving_background_filter_passes_data.push_back(
+ RemapPassId(render_pass->id, surface_id));
+ // TODO(wutao): Partial swap does not work with pixel moving background
+ // filter. See https://crbug.com/737255. Current solution is to mark the
+ // whole output rect as damaged.
+ pixel_moving_background_filters_rect.Union(
+ cc::MathUtil::MapEnclosingClippedRect(
+ render_pass->transform_to_root_target, render_pass->output_rect));
+ }
+ }
+ base::flat_set<cc::RenderPassId> pixel_moving_background_filter_passes(
+ std::move(pixel_moving_background_filter_passes_data),
+ base::KEEP_FIRST_OF_DUPES);
+
+ for (const auto& render_pass : base::Reversed(frame.render_pass_list)) {
+ cc::RenderPassId remapped_pass_id =
+ RemapPassId(render_pass->id, surface_id);
+ bool has_pixel_moving_filter =
+ render_pass->filters.HasFilterThatMovesPixels();
+ if (has_pixel_moving_filter)
+ moved_pixel_passes_.insert(remapped_pass_id);
+ bool in_moved_pixel_pass = has_pixel_moving_filter ||
+ !!moved_pixel_passes_.count(remapped_pass_id);
+ for (auto* quad : render_pass->quad_list) {
+ if (quad->material == cc::DrawQuad::SURFACE_CONTENT) {
+ const auto* surface_quad = cc::SurfaceDrawQuad::MaterialCast(quad);
+ gfx::Transform target_to_surface_transform(
+ render_pass->transform_to_root_target,
+ surface_quad->shared_quad_state->quad_to_target_transform);
+ child_surfaces.emplace_back(surface_quad->surface_id,
+ in_moved_pixel_pass, remapped_pass_id,
+ target_to_surface_transform);
+ } else if (quad->material == cc::DrawQuad::RENDER_PASS) {
+ const auto* render_pass_quad =
+ cc::RenderPassDrawQuad::MaterialCast(quad);
+ if (in_moved_pixel_pass) {
+ moved_pixel_passes_.insert(
+ RemapPassId(render_pass_quad->render_pass_id, surface_id));
+ }
+ if (pixel_moving_background_filter_passes.count(
+ render_pass_quad->render_pass_id)) {
+ in_moved_pixel_pass = true;
+ }
+ render_pass_dependencies_[remapped_pass_id].insert(
+ RemapPassId(render_pass_quad->render_pass_id, surface_id));
+ }
+
+ if (!provider_)
+ continue;
+ for (cc::ResourceId resource_id : quad->resources) {
+ if (!child_to_parent_map.count(resource_id)) {
+ invalid_frame = true;
+ break;
+ }
+ referenced_resources.push_back(resource_id);
+ }
+ }
+ }
+
+ if (invalid_frame)
+ return gfx::Rect();
+ CHECK(debug_weak_this.get());
+ valid_surfaces_.insert(surface->surface_id());
+
+ cc::ResourceIdSet resource_set(std::move(referenced_resources),
+ base::KEEP_FIRST_OF_DUPES);
+ if (provider_)
+ provider_->DeclareUsedResourcesFromChild(child_id, resource_set);
+ CHECK(debug_weak_this.get());
+
+ gfx::Rect damage_rect;
+ gfx::Rect full_damage;
+ cc::RenderPass* last_pass = frame.render_pass_list.back().get();
+ full_damage = last_pass->output_rect;
+ damage_rect =
+ DamageRectForSurface(surface, *last_pass, last_pass->output_rect);
+
+ // Avoid infinite recursion by adding current surface to
+ // referenced_surfaces_.
+ referenced_surfaces_.insert(surface->surface_id());
+ for (const auto& surface_info : child_surfaces) {
+ gfx::Rect surface_damage =
+ PrewalkTree(surface_info.id, surface_info.has_moved_pixels,
+ surface_info.parent_pass_id, result);
+ if (surface_damage.IsEmpty())
+ continue;
+ if (surface_info.has_moved_pixels) {
+ // Areas outside the rect hit by target_to_surface_transform may be
+ // modified if there is a filter that moves pixels.
+ damage_rect = full_damage;
+ continue;
+ }
+
+ damage_rect.Union(cc::MathUtil::MapEnclosingClippedRect(
+ surface_info.target_to_surface_transform, surface_damage));
+ }
+
+ CHECK(debug_weak_this.get());
+ for (const auto& surface_id : frame.metadata.referenced_surfaces) {
+ if (!contained_surfaces_.count(surface_id)) {
+ result->undrawn_surfaces.insert(surface_id);
+ PrewalkTree(surface_id, false, 0, result);
+ }
+ }
+
+ CHECK(debug_weak_this.get());
+ // TODO(staraz): It shouldn't need to call the callback when the damage is
+ // from |surface| and not from |child_surfaces|.
+ if (!damage_rect.IsEmpty()) {
+ surface->RunWillDrawCallback(damage_rect);
+ manager_->SurfaceWillDraw(surface->surface_id());
+ }
+
+ CHECK(debug_weak_this.get());
+ for (const auto& render_pass : frame.render_pass_list) {
+ if (!render_pass->copy_requests.empty()) {
+ cc::RenderPassId remapped_pass_id =
+ RemapPassId(render_pass->id, surface_id);
+ copy_request_passes_.insert(remapped_pass_id);
+ }
+ if (render_pass->cache_render_pass)
+ has_cached_render_passes_ = true;
+ }
+
+ referenced_surfaces_.erase(referenced_surfaces_.find(surface->surface_id()));
+ if (!damage_rect.IsEmpty() && frame.metadata.may_contain_video)
+ result->may_contain_video = true;
+
+ damage_rect.Union(pixel_moving_background_filters_rect);
+ return damage_rect;
+}
+
+void SurfaceAggregator::CopyUndrawnSurfaces(PrewalkResult* prewalk_result) {
+ // undrawn_surfaces are Surfaces that were identified by prewalk as being
+ // referenced by a drawn Surface, but aren't contained in a SurfaceDrawQuad.
+ // They need to be iterated over to ensure that any copy requests on them
+ // (or on Surfaces they reference) are executed.
+ std::vector<SurfaceId> surfaces_to_copy(
+ prewalk_result->undrawn_surfaces.begin(),
+ prewalk_result->undrawn_surfaces.end());
+ DCHECK(referenced_surfaces_.empty());
+
+ for (size_t i = 0; i < surfaces_to_copy.size(); i++) {
+ SurfaceId surface_id = surfaces_to_copy[i];
+ cc::Surface* surface = manager_->GetSurfaceForId(surface_id);
+ if (!surface)
+ continue;
+ if (!surface->HasActiveFrame())
+ continue;
+ const cc::CompositorFrame& frame = surface->GetActiveFrame();
+ bool surface_has_copy_requests = false;
+ for (const auto& render_pass : frame.render_pass_list) {
+ surface_has_copy_requests |= !render_pass->copy_requests.empty();
+ }
+ if (!surface_has_copy_requests) {
+ // 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) {
+ // 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.
+ if (!prewalk_result->undrawn_surfaces.count(child_id)) {
+ surfaces_to_copy.push_back(child_id);
+ prewalk_result->undrawn_surfaces.insert(child_id);
+ }
+ }
+ } else {
+ referenced_surfaces_.insert(surface_id);
+ CopyPasses(frame, surface);
+ // CopyPasses may have mutated container, need to re-query to erase.
+ referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
+ }
+ }
+}
+
+void SurfaceAggregator::PropagateCopyRequestPasses() {
+ std::vector<cc::RenderPassId> copy_requests_to_iterate(
+ copy_request_passes_.begin(), copy_request_passes_.end());
+ while (!copy_requests_to_iterate.empty()) {
+ int first = copy_requests_to_iterate.back();
+ copy_requests_to_iterate.pop_back();
+ auto it = render_pass_dependencies_.find(first);
+ if (it == render_pass_dependencies_.end())
+ continue;
+ for (auto pass : it->second) {
+ if (copy_request_passes_.insert(pass).second) {
+ copy_requests_to_iterate.push_back(pass);
+ }
+ }
+ }
+}
+
+cc::CompositorFrame SurfaceAggregator::Aggregate(const SurfaceId& surface_id) {
+ uma_stats_.Reset();
+
+ cc::Surface* surface = manager_->GetSurfaceForId(surface_id);
+ DCHECK(surface);
+ contained_surfaces_[surface_id] = surface->frame_index();
+
+ if (!surface->HasActiveFrame())
+ return {};
+
+ const cc::CompositorFrame& root_surface_frame = surface->GetActiveFrame();
+ TRACE_EVENT0("viz", "SurfaceAggregator::Aggregate");
+
+ cc::CompositorFrame frame;
+
+ dest_pass_list_ = &frame.render_pass_list;
+
+ valid_surfaces_.clear();
+ has_cached_render_passes_ = false;
+ PrewalkResult prewalk_result;
+ root_damage_rect_ = PrewalkTree(surface_id, false, 0, &prewalk_result);
+ PropagateCopyRequestPasses();
+ has_copy_requests_ = !copy_request_passes_.empty();
+ frame.metadata.may_contain_video = prewalk_result.may_contain_video;
+
+ CopyUndrawnSurfaces(&prewalk_result);
+ referenced_surfaces_.insert(surface_id);
+ CopyPasses(root_surface_frame, surface);
+ // CopyPasses may have mutated container, need to re-query to erase.
+ referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
+ AddColorConversionPass();
+
+ moved_pixel_passes_.clear();
+ copy_request_passes_.clear();
+ contributing_content_damaged_passes_.clear();
+ render_pass_dependencies_.clear();
+
+ // Remove all render pass mappings that weren't used in the current frame.
+ for (auto it = render_pass_allocator_map_.begin();
+ it != render_pass_allocator_map_.end();) {
+ if (it->second.in_use) {
+ it->second.in_use = false;
+ it++;
+ } else {
+ it = render_pass_allocator_map_.erase(it);
+ }
+ }
+
+ DCHECK(referenced_surfaces_.empty());
+
+ if (dest_pass_list_->empty())
+ return {};
+
+ dest_pass_list_ = NULL;
+ ProcessAddedAndRemovedSurfaces();
+ contained_surfaces_.swap(previous_contained_surfaces_);
+ contained_surfaces_.clear();
+
+ for (auto it : previous_contained_surfaces_) {
+ cc::Surface* surface = manager_->GetSurfaceForId(it.first);
+ if (surface)
+ surface->TakeLatencyInfo(&frame.metadata.latency_info);
+ }
+
+ // TODO(jamesr): Aggregate all resource references into the returned frame's
+ // resource list.
+
+ // Log UMA stats for SurfaceDrawQuads on the number of surfaces that were
+ // aggregated together and any failures.
+ UMA_HISTOGRAM_EXACT_LINEAR(kUmaValidSurface, uma_stats_.valid_surface,
+ kUmaStatMaxSurfaces);
+ UMA_HISTOGRAM_EXACT_LINEAR(kUmaMissingSurface, uma_stats_.missing_surface,
+ kUmaStatMaxSurfaces);
+ UMA_HISTOGRAM_EXACT_LINEAR(kUmaNoActiveFrame, uma_stats_.no_active_frame,
+ kUmaStatMaxSurfaces);
+
+ return frame;
+}
+
+void SurfaceAggregator::ReleaseResources(const SurfaceId& surface_id) {
+ auto it = surface_id_to_resource_child_id_.find(surface_id);
+ if (it != surface_id_to_resource_child_id_.end()) {
+ provider_->DestroyChild(it->second);
+ surface_id_to_resource_child_id_.erase(it);
+ }
+}
+
+void SurfaceAggregator::SetFullDamageForSurface(const SurfaceId& surface_id) {
+ auto it = previous_contained_surfaces_.find(surface_id);
+ if (it == previous_contained_surfaces_.end())
+ return;
+ // Set the last drawn index as 0 to ensure full damage next time it's drawn.
+ it->second = 0;
+}
+
+void SurfaceAggregator::SetOutputColorSpace(
+ const gfx::ColorSpace& blending_color_space,
+ const gfx::ColorSpace& output_color_space) {
+ blending_color_space_ = blending_color_space.IsValid()
+ ? blending_color_space
+ : gfx::ColorSpace::CreateSRGB();
+ output_color_space_ = output_color_space.IsValid()
+ ? output_color_space
+ : gfx::ColorSpace::CreateSRGB();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display/surface_aggregator.h b/chromium/components/viz/service/display/surface_aggregator.h
new file mode 100644
index 00000000000..f6cff7bde45
--- /dev/null
+++ b/chromium/components/viz/service/display/surface_aggregator.h
@@ -0,0 +1,233 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_SURFACE_AGGREGATOR_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_SURFACE_AGGREGATOR_H_
+
+#include <memory>
+#include <unordered_map>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/quads/draw_quad.h"
+#include "cc/quads/render_pass.h"
+#include "cc/resources/transferable_resource.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/viz_service_export.h"
+#include "ui/gfx/color_space.h"
+
+namespace cc {
+class BlockingTaskRunner;
+class CompositorFrame;
+class ResourceProvider;
+class Surface;
+class SurfaceClient;
+class SurfaceDrawQuad;
+class SurfaceManager;
+} // namespace cc
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT SurfaceAggregator {
+ public:
+ using SurfaceIndexMap = base::flat_map<SurfaceId, int>;
+
+ SurfaceAggregator(cc::SurfaceManager* manager,
+ cc::ResourceProvider* provider,
+ bool aggregate_only_damaged);
+ ~SurfaceAggregator();
+
+ cc::CompositorFrame Aggregate(const SurfaceId& surface_id);
+ void ReleaseResources(const SurfaceId& surface_id);
+ SurfaceIndexMap& previous_contained_surfaces() {
+ return previous_contained_surfaces_;
+ }
+ void SetFullDamageForSurface(const SurfaceId& surface_id);
+ void set_output_is_secure(bool secure) { output_is_secure_ = secure; }
+
+ // Set the color spaces for the created RenderPasses, which is propagated
+ // to the output surface.
+ void SetOutputColorSpace(const gfx::ColorSpace& blending_color_space,
+ const gfx::ColorSpace& output_color_space);
+
+ private:
+ struct ClipData {
+ ClipData() : is_clipped(false) {}
+ ClipData(bool is_clipped, const gfx::Rect& rect)
+ : is_clipped(is_clipped), rect(rect) {}
+
+ bool is_clipped;
+ gfx::Rect rect;
+ };
+
+ struct PrewalkResult {
+ PrewalkResult();
+ ~PrewalkResult();
+ // This is the set of Surfaces that were referenced by another Surface, but
+ // not included in a SurfaceDrawQuad.
+ base::flat_set<SurfaceId> undrawn_surfaces;
+ bool may_contain_video = false;
+ };
+
+ struct RenderPassInfo {
+ // This is the id the pass is mapped to.
+ int id;
+ // This is true if the pass was used in the last aggregated frame.
+ bool in_use = true;
+ };
+
+ struct SurfaceDrawQuadUmaStats {
+ void Reset() {
+ valid_surface = 0;
+ missing_surface = 0;
+ no_active_frame = 0;
+ }
+
+ // The surface exists and has an active frame.
+ int valid_surface;
+ // The surface doesn't exist.
+ int missing_surface;
+ // The surface exists but doesn't have an active frame.
+ int no_active_frame;
+ };
+
+ ClipData CalculateClipRect(const ClipData& surface_clip,
+ const ClipData& quad_clip,
+ const gfx::Transform& target_transform);
+
+ cc::RenderPassId RemapPassId(cc::RenderPassId surface_local_pass_id,
+ const SurfaceId& surface_id);
+
+ void HandleSurfaceQuad(const cc::SurfaceDrawQuad* surface_quad,
+ const gfx::Transform& target_transform,
+ const ClipData& clip_rect,
+ cc::RenderPass* dest_pass,
+ bool ignore_undamaged,
+ gfx::Rect* damage_rect_in_quad_space,
+ bool* damage_rect_in_quad_space_valid);
+
+ cc::SharedQuadState* CopySharedQuadState(
+ const cc::SharedQuadState* source_sqs,
+ const gfx::Transform& target_transform,
+ const ClipData& clip_rect,
+ cc::RenderPass* dest_render_pass);
+ void CopyQuadsToPass(
+ const cc::QuadList& source_quad_list,
+ const cc::SharedQuadStateList& source_shared_quad_state_list,
+ const std::unordered_map<cc::ResourceId, cc::ResourceId>&
+ resource_to_child_map,
+ const gfx::Transform& target_transform,
+ const ClipData& clip_rect,
+ cc::RenderPass* dest_pass,
+ const SurfaceId& surface_id);
+ gfx::Rect PrewalkTree(const SurfaceId& surface_id,
+ bool in_moved_pixel_surface,
+ int parent_pass,
+ PrewalkResult* result);
+ void CopyUndrawnSurfaces(PrewalkResult* prewalk);
+ void CopyPasses(const cc::CompositorFrame& frame, cc::Surface* surface);
+ void AddColorConversionPass();
+
+ // Remove Surfaces that were referenced before but aren't currently
+ // referenced from the ResourceProvider.
+ // Also notifies SurfaceAggregatorClient of newly added and removed
+ // child surfaces.
+ void ProcessAddedAndRemovedSurfaces();
+
+ void PropagateCopyRequestPasses();
+
+ int ChildIdForSurface(cc::Surface* surface);
+ gfx::Rect DamageRectForSurface(const cc::Surface* surface,
+ const cc::RenderPass& source,
+ const gfx::Rect& full_rect) const;
+
+ static void UnrefResources(base::WeakPtr<cc::SurfaceClient> surface_client,
+ const std::vector<cc::ReturnedResource>& resources,
+ cc::BlockingTaskRunner* main_thread_task_runner);
+
+ cc::SurfaceManager* manager_;
+ cc::ResourceProvider* provider_;
+
+ // Every Surface has its own RenderPass ID namespace. This structure maps
+ // each source (SurfaceId, RenderPass id) to a unified ID namespace that's
+ // used in the aggregated frame. An entry is removed from the map if it's not
+ // used for one output frame.
+ base::flat_map<std::pair<SurfaceId, cc::RenderPassId>, RenderPassInfo>
+ render_pass_allocator_map_;
+ cc::RenderPassId next_render_pass_id_;
+ const bool aggregate_only_damaged_;
+ bool output_is_secure_;
+
+ // The color space for the root render pass. If this is different from
+ // |blending_color_space_|, then a final render pass to convert between
+ // the two will be added. This space must always be valid.
+ gfx::ColorSpace output_color_space_ = gfx::ColorSpace::CreateSRGB();
+ // The color space in which blending is done, used for all non-root render
+ // passes. This space must always be valid.
+ gfx::ColorSpace blending_color_space_ = gfx::ColorSpace::CreateSRGB();
+ // The id for the final color conversion render pass.
+ cc::RenderPassId color_conversion_render_pass_id_ = 0;
+
+ base::flat_map<SurfaceId, int> surface_id_to_resource_child_id_;
+
+ // The following state is only valid for the duration of one Aggregate call
+ // and is only stored on the class to avoid having to pass through every
+ // function call.
+
+ // This is the set of surfaces referenced in the aggregation so far, used to
+ // detect cycles.
+ base::flat_set<SurfaceId> referenced_surfaces_;
+
+ // For each Surface used in the last aggregation, gives the frame_index at
+ // that time.
+ SurfaceIndexMap previous_contained_surfaces_;
+ SurfaceIndexMap contained_surfaces_;
+
+ // After surface validation, every Surface in this set is valid.
+ base::flat_set<SurfaceId> valid_surfaces_;
+
+ // This is the pass list for the aggregated frame.
+ cc::RenderPassList* dest_pass_list_;
+
+ // This is the set of aggregated pass ids that are affected by filters that
+ // move pixels.
+ base::flat_set<cc::RenderPassId> moved_pixel_passes_;
+
+ // This is the set of aggregated pass ids that are drawn by copy requests, so
+ // should not have their damage rects clipped to the root damage rect.
+ base::flat_set<cc::RenderPassId> copy_request_passes_;
+
+ // This is the set of aggregated pass ids that has damage from contributing
+ // content.
+ base::flat_set<cc::RenderPassId> contributing_content_damaged_passes_;
+
+ // This maps each aggregated pass id to the set of (aggregated) pass ids
+ // that its RenderPassDrawQuads depend on
+ base::flat_map<cc::RenderPassId, base::flat_set<cc::RenderPassId>>
+ render_pass_dependencies_;
+
+ // The root damage rect of the currently-aggregating frame.
+ gfx::Rect root_damage_rect_;
+
+ // True if the frame that's currently being aggregated has copy requests.
+ // This is valid during Aggregate after PrewalkTree is called.
+ bool has_copy_requests_;
+
+ // True if the frame that's currently being aggregated has cached render
+ // passes. This is valid during Aggregate after PrewalkTree is called.
+ bool has_cached_render_passes_;
+
+ // Tracks UMA stats for SurfaceDrawQuads during a call to Aggregate().
+ SurfaceDrawQuadUmaStats uma_stats_;
+
+ base::WeakPtrFactory<SurfaceAggregator> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceAggregator);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SURFACE_AGGREGATOR_H_
diff --git a/chromium/components/viz/service/display/surface_aggregator_perftest.cc b/chromium/components/viz/service/display/surface_aggregator_perftest.cc
new file mode 100644
index 00000000000..ab49928a818
--- /dev/null
+++ b/chromium/components/viz/service/display/surface_aggregator_perftest.cc
@@ -0,0 +1,187 @@
+// 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 "base/memory/ptr_util.h"
+#include "cc/base/lap_timer.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "cc/test/fake_output_surface_client.h"
+#include "cc/test/fake_resource_provider.h"
+#include "cc/test/test_context_provider.h"
+#include "cc/test/test_shared_bitmap_manager.h"
+#include "components/viz/service/display/surface_aggregator.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace viz {
+namespace {
+
+constexpr bool kIsRoot = true;
+constexpr bool kIsChildRoot = false;
+constexpr bool kHandlesFrameSinkIdInvalidation = true;
+constexpr bool kNeedsSyncPoints = true;
+
+const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create();
+
+class SurfaceAggregatorPerfTest : public testing::Test {
+ public:
+ SurfaceAggregatorPerfTest() {
+ context_provider_ = cc::TestContextProvider::Create();
+ context_provider_->BindToCurrentThread();
+ shared_bitmap_manager_ = base::MakeUnique<cc::TestSharedBitmapManager>();
+
+ resource_provider_ = cc::FakeResourceProvider::Create(
+ context_provider_.get(), shared_bitmap_manager_.get());
+ }
+
+ void RunTest(int num_surfaces,
+ int num_textures,
+ float opacity,
+ bool optimize_damage,
+ bool full_damage,
+ const std::string& name) {
+ std::vector<std::unique_ptr<CompositorFrameSinkSupport>> child_supports(
+ num_surfaces);
+ for (int i = 0; i < num_surfaces; i++) {
+ child_supports[i] = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, FrameSinkId(1, i + 1), kIsChildRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ }
+ aggregator_ = base::MakeUnique<SurfaceAggregator>(
+ manager_.surface_manager(), resource_provider_.get(), optimize_damage);
+ for (int i = 0; i < num_surfaces; i++) {
+ LocalSurfaceId local_surface_id(i + 1, kArbitraryToken);
+
+ auto pass = cc::RenderPass::Create();
+ pass->output_rect = gfx::Rect(0, 0, 1, 2);
+
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+
+ auto* sqs = pass->CreateAndAppendSharedQuadState();
+ for (int j = 0; j < num_textures; j++) {
+ cc::TransferableResource resource;
+ resource.id = j;
+ resource.is_software = true;
+ frame.resource_list.push_back(resource);
+
+ auto* quad = pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+ const gfx::Rect rect(0, 0, 1, 2);
+ const gfx::Rect opaque_rect;
+ // Half of rects should be visible with partial damage.
+ gfx::Rect visible_rect =
+ j % 2 == 0 ? gfx::Rect(0, 0, 1, 2) : gfx::Rect(0, 1, 1, 1);
+ bool needs_blending = false;
+ bool premultiplied_alpha = false;
+ const gfx::PointF uv_top_left;
+ const gfx::PointF uv_bottom_right;
+ SkColor background_color = SK_ColorGREEN;
+ const float vertex_opacity[4] = {0.f, 0.f, 1.f, 1.f};
+ bool flipped = false;
+ bool nearest_neighbor = false;
+ quad->SetAll(sqs, rect, opaque_rect, visible_rect, needs_blending, j,
+ gfx::Size(), premultiplied_alpha, uv_top_left,
+ uv_bottom_right, background_color, vertex_opacity, flipped,
+ nearest_neighbor, false);
+ }
+ sqs = pass->CreateAndAppendSharedQuadState();
+ sqs->opacity = opacity;
+ if (i >= 1) {
+ auto* surface_quad =
+ pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ surface_quad->SetNew(
+ sqs, gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1),
+ SurfaceId(FrameSinkId(1, i), LocalSurfaceId(i, kArbitraryToken)),
+ cc::SurfaceDrawQuadType::PRIMARY, nullptr);
+ }
+
+ frame.render_pass_list.push_back(std::move(pass));
+ child_supports[i]->SubmitCompositorFrame(local_surface_id,
+ std::move(frame));
+ }
+
+ auto root_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, FrameSinkId(1, num_surfaces + 1), kIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ timer_.Reset();
+ do {
+ auto pass = cc::RenderPass::Create();
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+
+ auto* sqs = pass->CreateAndAppendSharedQuadState();
+ auto* surface_quad = pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ surface_quad->SetNew(
+ sqs, gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 100, 100),
+ SurfaceId(FrameSinkId(1, num_surfaces),
+ LocalSurfaceId(num_surfaces, kArbitraryToken)),
+ cc::SurfaceDrawQuadType::PRIMARY, nullptr);
+
+ pass->output_rect = gfx::Rect(0, 0, 100, 100);
+
+ if (full_damage)
+ pass->damage_rect = gfx::Rect(0, 0, 100, 100);
+ else
+ pass->damage_rect = gfx::Rect(0, 0, 1, 1);
+
+ frame.render_pass_list.push_back(std::move(pass));
+
+ root_support->SubmitCompositorFrame(
+ LocalSurfaceId(num_surfaces + 1, kArbitraryToken), std::move(frame));
+
+ cc::CompositorFrame aggregated = aggregator_->Aggregate(
+ SurfaceId(FrameSinkId(1, num_surfaces + 1),
+ LocalSurfaceId(num_surfaces + 1, kArbitraryToken)));
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+
+ perf_test::PrintResult("aggregator_speed", "", name, timer_.LapsPerSecond(),
+ "runs/s", true);
+ for (int i = 0; i < num_surfaces; i++)
+ child_supports[i]->EvictCurrentSurface();
+ root_support->EvictCurrentSurface();
+ }
+
+ protected:
+ FrameSinkManagerImpl manager_;
+ scoped_refptr<cc::TestContextProvider> context_provider_;
+ std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
+ std::unique_ptr<cc::ResourceProvider> resource_provider_;
+ std::unique_ptr<SurfaceAggregator> aggregator_;
+ cc::LapTimer timer_;
+};
+
+TEST_F(SurfaceAggregatorPerfTest, ManySurfacesOpaque) {
+ RunTest(20, 100, 1.f, false, true, "many_surfaces_opaque");
+}
+
+TEST_F(SurfaceAggregatorPerfTest, ManySurfacesTransparent) {
+ RunTest(20, 100, .5f, false, true, "many_surfaces_transparent");
+}
+
+TEST_F(SurfaceAggregatorPerfTest, FewSurfaces) {
+ RunTest(3, 1000, 1.f, false, true, "few_surfaces");
+}
+
+TEST_F(SurfaceAggregatorPerfTest, ManySurfacesOpaqueDamageCalc) {
+ RunTest(20, 100, 1.f, true, true, "many_surfaces_opaque_damage_calc");
+}
+
+TEST_F(SurfaceAggregatorPerfTest, ManySurfacesTransparentDamageCalc) {
+ RunTest(20, 100, .5f, true, true, "many_surfaces_transparent_damage_calc");
+}
+
+TEST_F(SurfaceAggregatorPerfTest, FewSurfacesDamageCalc) {
+ RunTest(3, 1000, 1.f, true, true, "few_surfaces_damage_calc");
+}
+
+TEST_F(SurfaceAggregatorPerfTest, FewSurfacesAggregateDamaged) {
+ RunTest(3, 1000, 1.f, true, false, "few_surfaces_aggregate_damaged");
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
new file mode 100644
index 00000000000..91c06d9956f
--- /dev/null
+++ b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
@@ -0,0 +1,319 @@
+// 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 "build/build_config.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "cc/test/pixel_comparator.h"
+#include "cc/test/pixel_test.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
+#include "components/viz/service/display/surface_aggregator.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(OS_ANDROID)
+
+namespace viz {
+namespace {
+
+constexpr FrameSinkId kArbitraryRootFrameSinkId(1, 1);
+constexpr FrameSinkId kArbitraryChildFrameSinkId(2, 2);
+constexpr FrameSinkId kArbitraryLeftFrameSinkId(3, 3);
+constexpr FrameSinkId kArbitraryRightFrameSinkId(4, 4);
+constexpr bool kIsRoot = true;
+constexpr bool kIsChildRoot = false;
+constexpr bool kHandlesFrameSinkIdInvalidation = true;
+constexpr bool kNeedsSyncPoints = true;
+
+class SurfaceAggregatorPixelTest
+ : public cc::RendererPixelTest<cc::GLRenderer> {
+ public:
+ SurfaceAggregatorPixelTest()
+ : support_(
+ CompositorFrameSinkSupport::Create(nullptr,
+ &manager_,
+ kArbitraryRootFrameSinkId,
+ kIsRoot,
+ kHandlesFrameSinkIdInvalidation,
+ kNeedsSyncPoints)) {}
+ ~SurfaceAggregatorPixelTest() override { support_->EvictCurrentSurface(); }
+
+ protected:
+ FrameSinkManagerImpl manager_;
+ LocalSurfaceIdAllocator allocator_;
+ std::unique_ptr<CompositorFrameSinkSupport> support_;
+};
+
+cc::SharedQuadState* CreateAndAppendTestSharedQuadState(
+ cc::RenderPass* render_pass,
+ const gfx::Transform& transform,
+ const gfx::Size& size) {
+ const gfx::Rect layer_rect = gfx::Rect(size);
+ const gfx::Rect visible_layer_rect = gfx::Rect(size);
+ const gfx::Rect clip_rect = gfx::Rect(size);
+ bool is_clipped = false;
+ float opacity = 1.f;
+ const SkBlendMode blend_mode = SkBlendMode::kSrcOver;
+ auto* shared_state = render_pass->CreateAndAppendSharedQuadState();
+ shared_state->SetAll(transform, layer_rect, visible_layer_rect, clip_rect,
+ is_clipped, opacity, blend_mode, 0);
+ return shared_state;
+}
+
+// Draws a very simple frame with no surface references.
+TEST_F(SurfaceAggregatorPixelTest, DrawSimpleFrame) {
+ gfx::Rect rect(device_viewport_size_);
+ int id = 1;
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(id, rect, rect, gfx::Transform());
+
+ CreateAndAppendTestSharedQuadState(pass.get(), gfx::Transform(),
+ device_viewport_size_);
+
+ auto* color_quad = pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ bool force_anti_aliasing_off = false;
+ color_quad->SetNew(pass->shared_quad_state_list.back(), rect, rect,
+ SK_ColorGREEN, force_anti_aliasing_off);
+
+ auto root_frame = cc::test::MakeCompositorFrame();
+ root_frame.render_pass_list.push_back(std::move(pass));
+
+ LocalSurfaceId root_local_surface_id = allocator_.GenerateId();
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id);
+ support_->SubmitCompositorFrame(root_local_surface_id, std::move(root_frame));
+
+ SurfaceAggregator aggregator(manager_.surface_manager(),
+ resource_provider_.get(), true);
+ cc::CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
+
+ bool discard_alpha = false;
+ cc::ExactPixelComparator pixel_comparator(discard_alpha);
+ cc::RenderPassList* pass_list = &aggregated_frame.render_pass_list;
+ EXPECT_TRUE(RunPixelTest(pass_list,
+ base::FilePath(FILE_PATH_LITERAL("green.png")),
+ pixel_comparator));
+}
+
+// Draws a frame with simple surface embedding.
+TEST_F(SurfaceAggregatorPixelTest, DrawSimpleAggregatedFrame) {
+ gfx::Size child_size(200, 100);
+ std::unique_ptr<CompositorFrameSinkSupport> child_support =
+ CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryChildFrameSinkId, kIsChildRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support->frame_sink_id(),
+ child_local_surface_id);
+ LocalSurfaceId root_local_surface_id = allocator_.GenerateId();
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id);
+
+ {
+ gfx::Rect rect(device_viewport_size_);
+ int id = 1;
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(id, rect, rect, gfx::Transform());
+
+ CreateAndAppendTestSharedQuadState(pass.get(), gfx::Transform(),
+ device_viewport_size_);
+
+ auto* surface_quad = pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ surface_quad->SetNew(pass->shared_quad_state_list.back(),
+ gfx::Rect(child_size), gfx::Rect(child_size),
+ child_surface_id, cc::SurfaceDrawQuadType::PRIMARY,
+ nullptr);
+
+ auto* color_quad = pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ bool force_anti_aliasing_off = false;
+ color_quad->SetNew(pass->shared_quad_state_list.back(), rect, rect,
+ SK_ColorYELLOW, force_anti_aliasing_off);
+
+ auto root_frame = cc::test::MakeCompositorFrame();
+ root_frame.render_pass_list.push_back(std::move(pass));
+
+ support_->SubmitCompositorFrame(root_local_surface_id,
+ std::move(root_frame));
+ }
+
+ {
+ gfx::Rect rect(child_size);
+ int id = 1;
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(id, rect, rect, gfx::Transform());
+
+ CreateAndAppendTestSharedQuadState(pass.get(), gfx::Transform(),
+ child_size);
+
+ auto* color_quad = pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ bool force_anti_aliasing_off = false;
+ color_quad->SetNew(pass->shared_quad_state_list.back(), rect, rect,
+ SK_ColorBLUE, force_anti_aliasing_off);
+
+ auto child_frame = cc::test::MakeCompositorFrame();
+ child_frame.render_pass_list.push_back(std::move(pass));
+
+ child_support->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+ }
+
+ SurfaceAggregator aggregator(manager_.surface_manager(),
+ resource_provider_.get(), true);
+ cc::CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
+
+ bool discard_alpha = false;
+ cc::ExactPixelComparator pixel_comparator(discard_alpha);
+ cc::RenderPassList* pass_list = &aggregated_frame.render_pass_list;
+ EXPECT_TRUE(RunPixelTest(pass_list,
+ base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")),
+ pixel_comparator));
+
+ child_support->EvictCurrentSurface();
+}
+
+// Tests a surface quad that has a non-identity transform into its pass.
+TEST_F(SurfaceAggregatorPixelTest, DrawAggregatedFrameWithSurfaceTransforms) {
+ gfx::Size child_size(100, 200);
+ gfx::Size quad_size(100, 100);
+ // Structure:
+ // root (200x200) -> left_child (100x200 @ 0x0,
+ // right_child (100x200 @ 0x100)
+ // left_child -> top_green_quad (100x100 @ 0x0),
+ // bottom_blue_quad (100x100 @ 0x100)
+ // right_child -> top_blue_quad (100x100 @ 0x0),
+ // bottom_green_quad (100x100 @ 0x100)
+ std::unique_ptr<CompositorFrameSinkSupport> left_support =
+ CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryLeftFrameSinkId, kIsChildRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ std::unique_ptr<CompositorFrameSinkSupport> right_support =
+ CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryRightFrameSinkId, kIsChildRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId left_child_local_id = allocator_.GenerateId();
+ SurfaceId left_child_id(left_support->frame_sink_id(), left_child_local_id);
+ LocalSurfaceId right_child_local_id = allocator_.GenerateId();
+ SurfaceId right_child_id(right_support->frame_sink_id(),
+ right_child_local_id);
+ LocalSurfaceId root_local_surface_id = allocator_.GenerateId();
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id);
+
+ {
+ gfx::Rect rect(device_viewport_size_);
+ int id = 1;
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(id, rect, rect, gfx::Transform());
+
+ gfx::Transform surface_transform;
+ CreateAndAppendTestSharedQuadState(pass.get(), surface_transform,
+ device_viewport_size_);
+
+ auto* left_surface_quad =
+ pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ left_surface_quad->SetNew(pass->shared_quad_state_list.back(),
+ gfx::Rect(child_size), gfx::Rect(child_size),
+ left_child_id, cc::SurfaceDrawQuadType::PRIMARY,
+ nullptr);
+
+ surface_transform.Translate(100, 0);
+ CreateAndAppendTestSharedQuadState(pass.get(), surface_transform,
+ device_viewport_size_);
+
+ auto* right_surface_quad =
+ pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ right_surface_quad->SetNew(pass->shared_quad_state_list.back(),
+ gfx::Rect(child_size), gfx::Rect(child_size),
+ right_child_id, cc::SurfaceDrawQuadType::PRIMARY,
+ nullptr);
+
+ auto root_frame = cc::test::MakeCompositorFrame();
+ root_frame.render_pass_list.push_back(std::move(pass));
+
+ support_->SubmitCompositorFrame(root_local_surface_id,
+ std::move(root_frame));
+ }
+
+ {
+ gfx::Rect rect(child_size);
+ int id = 1;
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(id, rect, rect, gfx::Transform());
+
+ CreateAndAppendTestSharedQuadState(pass.get(), gfx::Transform(),
+ child_size);
+
+ auto* top_color_quad =
+ pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ bool force_anti_aliasing_off = false;
+ top_color_quad->SetNew(pass->shared_quad_state_list.back(),
+ gfx::Rect(quad_size), gfx::Rect(quad_size),
+ SK_ColorGREEN, force_anti_aliasing_off);
+
+ auto* bottom_color_quad =
+ pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ bottom_color_quad->SetNew(
+ pass->shared_quad_state_list.back(), gfx::Rect(0, 100, 100, 100),
+ gfx::Rect(0, 100, 100, 100), SK_ColorBLUE, force_anti_aliasing_off);
+
+ auto child_frame = cc::test::MakeCompositorFrame();
+ child_frame.render_pass_list.push_back(std::move(pass));
+
+ left_support->SubmitCompositorFrame(left_child_local_id,
+ std::move(child_frame));
+ }
+
+ {
+ gfx::Rect rect(child_size);
+ int id = 1;
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(id, rect, rect, gfx::Transform());
+
+ CreateAndAppendTestSharedQuadState(pass.get(), gfx::Transform(),
+ child_size);
+
+ auto* top_color_quad =
+ pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ bool force_anti_aliasing_off = false;
+ top_color_quad->SetNew(pass->shared_quad_state_list.back(),
+ gfx::Rect(quad_size), gfx::Rect(quad_size),
+ SK_ColorBLUE, force_anti_aliasing_off);
+
+ auto* bottom_color_quad =
+ pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ bottom_color_quad->SetNew(
+ pass->shared_quad_state_list.back(), gfx::Rect(0, 100, 100, 100),
+ gfx::Rect(0, 100, 100, 100), SK_ColorGREEN, force_anti_aliasing_off);
+
+ auto child_frame = cc::test::MakeCompositorFrame();
+ child_frame.render_pass_list.push_back(std::move(pass));
+
+ right_support->SubmitCompositorFrame(right_child_local_id,
+ std::move(child_frame));
+ }
+
+ SurfaceAggregator aggregator(manager_.surface_manager(),
+ resource_provider_.get(), true);
+ cc::CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
+
+ bool discard_alpha = false;
+ cc::ExactPixelComparator pixel_comparator(discard_alpha);
+ cc::RenderPassList* pass_list = &aggregated_frame.render_pass_list;
+ EXPECT_TRUE(RunPixelTest(
+ pass_list,
+ base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")),
+ pixel_comparator));
+
+ left_support->EvictCurrentSurface();
+ right_support->EvictCurrentSurface();
+}
+
+} // namespace
+} // namespace viz
+
+#endif // !defined(OS_ANDROID)
diff --git a/chromium/components/viz/service/display/surface_aggregator_unittest.cc b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
new file mode 100644
index 00000000000..78f35d4de81
--- /dev/null
+++ b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
@@ -0,0 +1,3095 @@
+
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/surface_aggregator.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/surface_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "cc/test/fake_compositor_frame_sink_support_client.h"
+#include "cc/test/fake_resource_provider.h"
+#include "cc/test/fake_surface_observer.h"
+#include "cc/test/render_pass_test_utils.h"
+#include "cc/test/test_shared_bitmap_manager.h"
+#include "components/viz/common/resources/shared_bitmap_manager.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace viz {
+namespace {
+
+constexpr FrameSinkId kArbitraryRootFrameSinkId(1, 1);
+constexpr FrameSinkId kArbitraryFrameSinkId1(2, 2);
+constexpr FrameSinkId kArbitraryFrameSinkId2(3, 3);
+constexpr FrameSinkId kArbitraryMiddleFrameSinkId(4, 4);
+constexpr FrameSinkId kArbitraryReservedFrameSinkId(5, 5);
+constexpr FrameSinkId kArbitraryFrameSinkId3(6, 6);
+const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create();
+constexpr bool kRootIsRoot = true;
+constexpr bool kChildIsRoot = false;
+constexpr bool kHandlesFrameSinkIdInvalidation = true;
+constexpr bool kNeedsSyncPoints = false;
+
+SurfaceId InvalidSurfaceId() {
+ static SurfaceId invalid(FrameSinkId(),
+ LocalSurfaceId(0xdeadbeef, kArbitraryToken));
+ return invalid;
+}
+
+gfx::Size SurfaceSize() {
+ static gfx::Size size(100, 100);
+ return size;
+}
+
+class SurfaceAggregatorTest : public testing::Test {
+ public:
+ explicit SurfaceAggregatorTest(bool use_damage_rect)
+ : observer_(false),
+ support_(
+ CompositorFrameSinkSupport::Create(&fake_client_,
+ &manager_,
+ kArbitraryRootFrameSinkId,
+ kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation,
+ kNeedsSyncPoints)),
+ aggregator_(manager_.surface_manager(), NULL, use_damage_rect) {
+ manager_.surface_manager()->AddObserver(&observer_);
+ }
+
+ SurfaceAggregatorTest() : SurfaceAggregatorTest(false) {}
+
+ void TearDown() override {
+ observer_.Reset();
+ support_->EvictCurrentSurface();
+ testing::Test::TearDown();
+ }
+
+ struct Quad {
+ static Quad SolidColorQuad(SkColor color) {
+ Quad quad;
+ quad.material = cc::DrawQuad::SOLID_COLOR;
+ quad.color = color;
+ return quad;
+ }
+
+ static Quad SurfaceQuad(const SurfaceId& primary_surface_id,
+ const SurfaceId& fallback_surface_id,
+ float opacity) {
+ Quad quad;
+ quad.material = cc::DrawQuad::SURFACE_CONTENT;
+ quad.opacity = opacity;
+ quad.primary_surface_id = primary_surface_id;
+ quad.fallback_surface_id = fallback_surface_id;
+ return quad;
+ }
+
+ static Quad RenderPassQuad(int id) {
+ Quad quad;
+ quad.material = cc::DrawQuad::RENDER_PASS;
+ quad.render_pass_id = id;
+ return quad;
+ }
+
+ cc::DrawQuad::Material material;
+ // Set when material==DrawQuad::SURFACE_CONTENT.
+ SurfaceId primary_surface_id;
+ SurfaceId fallback_surface_id;
+ float opacity;
+ // Set when material==DrawQuad::SOLID_COLOR.
+ SkColor color;
+ // Set when material==DrawQuad::RENDER_PASS.
+ cc::RenderPassId render_pass_id;
+
+ private:
+ Quad()
+ : material(cc::DrawQuad::INVALID), opacity(1.f), color(SK_ColorWHITE) {}
+ };
+
+ struct Pass {
+ Pass(Quad* quads, size_t quad_count, int id)
+ : quads(quads), quad_count(quad_count), id(id) {}
+ Pass(Quad* quads, size_t quad_count)
+ : quads(quads), quad_count(quad_count) {}
+
+ Quad* quads;
+ size_t quad_count;
+ int id = 1;
+ };
+
+ static void AddQuadInPass(cc::RenderPass* pass, Quad desc) {
+ switch (desc.material) {
+ case cc::DrawQuad::SOLID_COLOR:
+ AddQuad(pass, gfx::Rect(0, 0, 5, 5), desc.color);
+ break;
+ case cc::DrawQuad::SURFACE_CONTENT:
+ AddSurfaceQuad(pass, gfx::Size(5, 5), desc.opacity,
+ desc.primary_surface_id, desc.fallback_surface_id);
+ break;
+ case cc::DrawQuad::RENDER_PASS:
+ AddRenderPassQuad(pass, desc.render_pass_id);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ static void AddPasses(cc::RenderPassList* pass_list,
+ const gfx::Rect& output_rect,
+ Pass* passes,
+ size_t pass_count) {
+ gfx::Transform root_transform;
+ for (size_t i = 0; i < pass_count; ++i) {
+ Pass pass = passes[i];
+ cc::RenderPass* test_pass =
+ AddRenderPass(pass_list, pass.id, output_rect, root_transform,
+ cc::FilterOperations());
+ for (size_t j = 0; j < pass.quad_count; ++j) {
+ AddQuadInPass(test_pass, pass.quads[j]);
+ }
+ }
+ }
+
+ static void TestQuadMatchesExpectations(Quad expected_quad,
+ const cc::DrawQuad* quad) {
+ switch (expected_quad.material) {
+ case cc::DrawQuad::SOLID_COLOR: {
+ ASSERT_EQ(cc::DrawQuad::SOLID_COLOR, quad->material);
+
+ const auto* solid_color_quad =
+ cc::SolidColorDrawQuad::MaterialCast(quad);
+
+ EXPECT_EQ(expected_quad.color, solid_color_quad->color);
+ break;
+ }
+ case cc::DrawQuad::RENDER_PASS: {
+ ASSERT_EQ(cc::DrawQuad::RENDER_PASS, quad->material);
+
+ const auto* render_pass_quad =
+ cc::RenderPassDrawQuad::MaterialCast(quad);
+
+ EXPECT_EQ(expected_quad.render_pass_id,
+ render_pass_quad->render_pass_id);
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ static void TestPassMatchesExpectations(Pass expected_pass,
+ const cc::RenderPass* pass) {
+ ASSERT_EQ(expected_pass.quad_count, pass->quad_list.size());
+ for (auto iter = pass->quad_list.cbegin(); iter != pass->quad_list.cend();
+ ++iter) {
+ SCOPED_TRACE(base::StringPrintf("Quad number %" PRIuS, iter.index()));
+ TestQuadMatchesExpectations(expected_pass.quads[iter.index()], *iter);
+ }
+ }
+
+ static void TestPassesMatchExpectations(Pass* expected_passes,
+ size_t expected_pass_count,
+ const cc::RenderPassList* passes) {
+ ASSERT_EQ(expected_pass_count, passes->size());
+
+ for (size_t i = 0; i < passes->size(); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Pass number %" PRIuS, i));
+ cc::RenderPass* pass = (*passes)[i].get();
+ TestPassMatchesExpectations(expected_passes[i], pass);
+ }
+ }
+
+ private:
+ static void AddSurfaceQuad(cc::RenderPass* pass,
+ const gfx::Size& surface_size,
+ float opacity,
+ const SurfaceId& primary_surface_id,
+ const SurfaceId& fallback_surface_id) {
+ gfx::Transform layer_to_target_transform;
+ gfx::Size layer_bounds = surface_size;
+ gfx::Rect visible_layer_rect = gfx::Rect(surface_size);
+ gfx::Rect clip_rect = gfx::Rect(surface_size);
+ bool is_clipped = false;
+ SkBlendMode blend_mode = SkBlendMode::kSrcOver;
+
+ auto* shared_quad_state = pass->CreateAndAppendSharedQuadState();
+ shared_quad_state->SetAll(layer_to_target_transform,
+ gfx::Rect(layer_bounds), visible_layer_rect,
+ clip_rect, is_clipped, opacity, blend_mode, 0);
+
+ cc::SurfaceDrawQuad* surface_quad =
+ pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ cc::SurfaceDrawQuad* fallback_surface_quad = nullptr;
+ if (fallback_surface_id.is_valid())
+ fallback_surface_quad =
+ pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+
+ gfx::Rect quad_rect = gfx::Rect(surface_size);
+ surface_quad->SetNew(pass->shared_quad_state_list.back(), quad_rect,
+ quad_rect, primary_surface_id,
+ cc::SurfaceDrawQuadType::PRIMARY,
+ fallback_surface_quad);
+
+ if (fallback_surface_quad) {
+ fallback_surface_quad->SetNew(pass->shared_quad_state_list.back(),
+ quad_rect, quad_rect, fallback_surface_id,
+ cc::SurfaceDrawQuadType::FALLBACK, nullptr);
+ }
+ }
+
+ static void AddRenderPassQuad(cc::RenderPass* pass,
+ cc::RenderPassId render_pass_id) {
+ gfx::Rect output_rect = gfx::Rect(0, 0, 5, 5);
+ auto* shared_state = pass->CreateAndAppendSharedQuadState();
+ shared_state->SetAll(gfx::Transform(), output_rect, output_rect,
+ output_rect, false, 1, SkBlendMode::kSrcOver, 0);
+ auto* quad = pass->CreateAndAppendDrawQuad<cc::RenderPassDrawQuad>();
+ quad->SetNew(shared_state, output_rect, output_rect, render_pass_id, 0,
+ gfx::RectF(), gfx::Size(), gfx::Vector2dF(), gfx::PointF(),
+ gfx::RectF());
+ }
+
+ protected:
+ FrameSinkManagerImpl manager_;
+ cc::FakeSurfaceObserver observer_;
+ cc::FakeCompositorFrameSinkSupportClient fake_client_;
+ std::unique_ptr<CompositorFrameSinkSupport> support_;
+ SurfaceAggregator aggregator_;
+};
+
+class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest {
+ public:
+ explicit SurfaceAggregatorValidSurfaceTest(bool use_damage_rect)
+ : SurfaceAggregatorTest(use_damage_rect),
+ child_support_(
+ CompositorFrameSinkSupport::Create(nullptr,
+ &manager_,
+ kArbitraryReservedFrameSinkId,
+ kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation,
+ kNeedsSyncPoints)) {}
+ SurfaceAggregatorValidSurfaceTest()
+ : SurfaceAggregatorValidSurfaceTest(false) {}
+
+ void SetUp() override {
+ SurfaceAggregatorTest::SetUp();
+ root_local_surface_id_ = allocator_.GenerateId();
+ root_surface_ = manager_.surface_manager()->GetSurfaceForId(
+ SurfaceId(support_->frame_sink_id(), root_local_surface_id_));
+ }
+
+ void TearDown() override {
+ child_support_->EvictCurrentSurface();
+ SurfaceAggregatorTest::TearDown();
+ }
+
+ void AggregateAndVerify(Pass* expected_passes,
+ size_t expected_pass_count,
+ SurfaceId* surface_ids,
+ size_t expected_surface_count) {
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(
+ SurfaceId(support_->frame_sink_id(), root_local_surface_id_));
+
+ TestPassesMatchExpectations(expected_passes, expected_pass_count,
+ &aggregated_frame.render_pass_list);
+
+ // Ensure no duplicate pass ids output.
+ std::set<cc::RenderPassId> used_passes;
+ for (const auto& pass : aggregated_frame.render_pass_list) {
+ EXPECT_TRUE(used_passes.insert(pass->id).second);
+ }
+
+ EXPECT_EQ(expected_surface_count,
+ aggregator_.previous_contained_surfaces().size());
+ for (size_t i = 0; i < expected_surface_count; i++) {
+ EXPECT_TRUE(
+ aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
+ aggregator_.previous_contained_surfaces().end());
+ }
+ }
+
+ void SubmitPassListAsFrame(CompositorFrameSinkSupport* support,
+ const LocalSurfaceId& local_surface_id,
+ cc::RenderPassList* pass_list) {
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+ pass_list->swap(frame.render_pass_list);
+
+ support->SubmitCompositorFrame(local_surface_id, std::move(frame));
+ }
+
+ void SubmitCompositorFrame(CompositorFrameSinkSupport* support,
+ Pass* passes,
+ size_t pass_count,
+ const LocalSurfaceId& local_surface_id) {
+ cc::RenderPassList pass_list;
+ AddPasses(&pass_list, gfx::Rect(SurfaceSize()), passes, pass_count);
+ SubmitPassListAsFrame(support, local_surface_id, &pass_list);
+ }
+
+ void QueuePassAsFrame(std::unique_ptr<cc::RenderPass> pass,
+ const LocalSurfaceId& local_surface_id,
+ CompositorFrameSinkSupport* support) {
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ child_frame.render_pass_list.push_back(std::move(pass));
+
+ support->SubmitCompositorFrame(local_surface_id, std::move(child_frame));
+ }
+
+ protected:
+ LocalSurfaceId root_local_surface_id_;
+ cc::Surface* root_surface_;
+ LocalSurfaceIdAllocator allocator_;
+ std::unique_ptr<CompositorFrameSinkSupport> child_support_;
+ LocalSurfaceIdAllocator child_allocator_;
+};
+
+// Tests that a very simple frame containing only two solid color quads makes it
+// through the aggregator correctly.
+TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
+ Quad quads[] = {Quad::SolidColorQuad(SK_ColorRED),
+ Quad::SolidColorQuad(SK_ColorBLUE)};
+ Pass passes[] = {Pass(quads, arraysize(quads))};
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ SurfaceId ids[] = {root_surface_id};
+
+ AggregateAndVerify(passes, arraysize(passes), ids, arraysize(ids));
+
+ // Check that WillDrawSurface was called.
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), fake_client_.last_damage_rect());
+ EXPECT_EQ(root_local_surface_id_, fake_client_.last_local_surface_id());
+
+ // Check that SurfaceObserver::OnSurfaceWillDraw was called.
+ EXPECT_TRUE(observer_.SurfaceWillDrawCalled(root_surface_id));
+}
+
+TEST_F(SurfaceAggregatorValidSurfaceTest, OpacityCopied) {
+ auto embedded_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId embedded_local_surface_id = allocator_.GenerateId();
+ SurfaceId embedded_surface_id(embedded_support->frame_sink_id(),
+ embedded_local_surface_id);
+
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SolidColorQuad(SK_ColorBLUE)};
+ Pass embedded_passes[] = {Pass(embedded_quads, arraysize(embedded_quads))};
+
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ arraysize(embedded_passes), embedded_local_surface_id);
+
+ Quad quads[] = {
+ Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), .5f)};
+ Pass passes[] = {Pass(quads, arraysize(quads))};
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ auto& render_pass_list = aggregated_frame.render_pass_list;
+ ASSERT_EQ(2u, render_pass_list.size());
+ auto& shared_quad_state_list = render_pass_list[0]->shared_quad_state_list;
+ ASSERT_EQ(2u, shared_quad_state_list.size());
+ EXPECT_EQ(1.f, shared_quad_state_list.ElementAt(0)->opacity);
+ EXPECT_EQ(1.f, shared_quad_state_list.ElementAt(1)->opacity);
+
+ auto& shared_quad_state_list2 = render_pass_list[1]->shared_quad_state_list;
+ ASSERT_EQ(1u, shared_quad_state_list2.size());
+ EXPECT_EQ(.5f, shared_quad_state_list2.ElementAt(0)->opacity);
+
+ embedded_support->EvictCurrentSurface();
+}
+
+TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSimpleFrame) {
+ Quad quads[][2] = {{Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SolidColorQuad(SK_ColorLTGRAY)},
+ {Quad::SolidColorQuad(SK_ColorGRAY),
+ Quad::SolidColorQuad(SK_ColorDKGRAY)}};
+ Pass passes[] = {Pass(quads[0], arraysize(quads[0]), 1),
+ Pass(quads[1], arraysize(quads[1]), 2)};
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ SurfaceId ids[] = {root_surface_id};
+
+ AggregateAndVerify(passes, arraysize(passes), ids, arraysize(ids));
+}
+
+// Ensure that the render pass ID map properly keeps and deletes entries.
+TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassDeallocation) {
+ Quad quads[][2] = {{Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SolidColorQuad(SK_ColorLTGRAY)},
+ {Quad::SolidColorQuad(SK_ColorGRAY),
+ Quad::SolidColorQuad(SK_ColorDKGRAY)}};
+ Pass passes[] = {Pass(quads[0], arraysize(quads[0]), 2),
+ Pass(quads[1], arraysize(quads[1]), 1)};
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ SurfaceId surface_id(support_->frame_sink_id(), root_local_surface_id_);
+
+ cc::CompositorFrame aggregated_frame;
+ aggregated_frame = aggregator_.Aggregate(surface_id);
+ auto id0 = aggregated_frame.render_pass_list[0]->id;
+ auto id1 = aggregated_frame.render_pass_list[1]->id;
+ EXPECT_NE(id1, id0);
+
+ // Aggregated cc::RenderPass ids should remain the same between frames.
+ aggregated_frame = aggregator_.Aggregate(surface_id);
+ 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),
+ Pass(quads[1], arraysize(quads[1]), 1)};
+
+ SubmitCompositorFrame(support_.get(), passes2, arraysize(passes2),
+ root_local_surface_id_);
+
+ // The cc::RenderPass that still exists should keep the same ID.
+ aggregated_frame = aggregator_.Aggregate(surface_id);
+ auto id2 = aggregated_frame.render_pass_list[0]->id;
+ EXPECT_NE(id2, id1);
+ EXPECT_NE(id2, id0);
+ EXPECT_EQ(id1, aggregated_frame.render_pass_list[1]->id);
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ // |id1| didn't exist in the previous frame, so it should be
+ // mapped to a new ID.
+ aggregated_frame = aggregator_.Aggregate(surface_id);
+ auto id3 = aggregated_frame.render_pass_list[0]->id;
+ EXPECT_NE(id3, id2);
+ EXPECT_NE(id3, id1);
+ EXPECT_NE(id3, id0);
+ EXPECT_EQ(id1, aggregated_frame.render_pass_list[1]->id);
+}
+
+// This tests very simple embedding. root_surface has a frame containing a few
+// solid color quads and a surface quad referencing embedded_surface.
+// embedded_surface has a frame containing only a solid color quad. The solid
+// color quad should be aggregated into the final frame.
+TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) {
+ auto embedded_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId embedded_local_surface_id = allocator_.GenerateId();
+ SurfaceId embedded_surface_id(embedded_support->frame_sink_id(),
+ embedded_local_surface_id);
+
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass embedded_passes[] = {Pass(embedded_quads, arraysize(embedded_quads))};
+
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ arraysize(embedded_passes), embedded_local_surface_id);
+
+ Quad root_quads[] = {
+ Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorBLACK)};
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads))};
+
+ SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ root_local_surface_id_);
+
+ Quad expected_quads[] = {Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SolidColorQuad(SK_ColorBLACK)};
+ Pass expected_passes[] = {Pass(expected_quads, arraysize(expected_quads))};
+ 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));
+
+ embedded_support->EvictCurrentSurface();
+}
+
+// This test verifies that in the absence of a primary Surface,
+// SurfaceAggregator will embed a fallback Surface, if available. If the primary
+// Surface is available, though, the fallback will not be used.
+TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
+ auto primary_child_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId primary_child_local_surface_id = allocator_.GenerateId();
+ SurfaceId primary_child_surface_id(primary_child_support->frame_sink_id(),
+ primary_child_local_surface_id);
+
+ auto fallback_child_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId fallback_child_local_surface_id = allocator_.GenerateId();
+ SurfaceId fallback_child_surface_id(fallback_child_support->frame_sink_id(),
+ fallback_child_local_surface_id);
+
+ Quad fallback_child_quads[] = {Quad::SolidColorQuad(SK_ColorRED)};
+ Pass fallback_child_passes[] = {
+ Pass(fallback_child_quads, arraysize(fallback_child_quads))};
+
+ // Submit a cc::CompositorFrame to the fallback Surface containing a red
+ // SolidColorDrawQuad.
+ SubmitCompositorFrame(fallback_child_support.get(), fallback_child_passes,
+ arraysize(fallback_child_passes),
+ fallback_child_local_surface_id);
+
+ // Try to embed |primary_child_surface_id| and if unavailabe, embed
+ // |fallback_child_surface_id|.
+ Quad root_quads[] = {Quad::SurfaceQuad(primary_child_surface_id,
+ fallback_child_surface_id, 1.f)};
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads))};
+
+ SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ root_local_surface_id_);
+
+ // There is no cc::CompositorFrame submitted to |primary_child_surface_id| and
+ // so |fallback_child_surface_id| will be embedded and we should see a red
+ // SolidColorDrawQuad.
+ Quad expected_quads1[] = {Quad::SolidColorQuad(SK_ColorRED)};
+ Pass expected_passes1[] = {Pass(expected_quads1, arraysize(expected_quads1))};
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ SurfaceId ids[] = {root_surface_id, primary_child_surface_id,
+ fallback_child_surface_id};
+ AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
+ arraysize(ids));
+
+ // Check that SurfaceObserver::OnSurfaceWillDraw was called only
+ // for the fallback surface.
+ EXPECT_FALSE(observer_.SurfaceWillDrawCalled(primary_child_surface_id));
+ EXPECT_TRUE(observer_.SurfaceWillDrawCalled(fallback_child_surface_id));
+
+ observer_.Reset();
+
+ Quad primary_child_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass primary_child_passes[] = {
+ Pass(primary_child_quads, arraysize(primary_child_quads))};
+
+ // Submit a cc::CompositorFrame to the primary Surface containing a green
+ // SolidColorDrawQuad.
+ SubmitCompositorFrame(primary_child_support.get(), primary_child_passes,
+ arraysize(primary_child_passes),
+ primary_child_local_surface_id);
+
+ // Now that the primary Surface has a cc::CompositorFrame, we expect
+ // SurfaceAggregator to embed the primary Surface, and drop the fallback
+ // Surface.
+ Quad expected_quads2[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass expected_passes2[] = {Pass(expected_quads2, arraysize(expected_quads2))};
+ AggregateAndVerify(expected_passes2, arraysize(expected_passes2), ids,
+ arraysize(ids));
+
+ // Check that SurfaceObserver::OnSurfaceWillDraw was called only
+ // for the primary surface.
+ EXPECT_TRUE(observer_.SurfaceWillDrawCalled(primary_child_surface_id));
+ EXPECT_FALSE(observer_.SurfaceWillDrawCalled(fallback_child_surface_id));
+
+ primary_child_support->EvictCurrentSurface();
+ fallback_child_support->EvictCurrentSurface();
+}
+
+// This test verifies that in the presence of both primary Surface and fallback
+// Surface, the fallback will not be used.
+TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
+ auto primary_child_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId primary_child_local_surface_id = allocator_.GenerateId();
+ SurfaceId primary_child_surface_id(primary_child_support->frame_sink_id(),
+ primary_child_local_surface_id);
+ Quad primary_child_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass primary_child_passes[] = {
+ Pass(primary_child_quads, arraysize(primary_child_quads))};
+
+ // Submit a cc::CompositorFrame to the primary Surface containing a green
+ // SolidColorDrawQuad.
+ SubmitCompositorFrame(primary_child_support.get(), primary_child_passes,
+ arraysize(primary_child_passes),
+ primary_child_local_surface_id);
+
+ auto fallback_child_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId fallback_child_local_surface_id = allocator_.GenerateId();
+ SurfaceId fallback_child_surface_id(fallback_child_support->frame_sink_id(),
+ fallback_child_local_surface_id);
+
+ Quad fallback_child_quads[] = {Quad::SolidColorQuad(SK_ColorRED)};
+ Pass fallback_child_passes[] = {
+ Pass(fallback_child_quads, arraysize(fallback_child_quads))};
+
+ // Submit a cc::CompositorFrame to the fallback Surface containing a red
+ // SolidColorDrawQuad.
+ SubmitCompositorFrame(fallback_child_support.get(), fallback_child_passes,
+ arraysize(fallback_child_passes),
+ fallback_child_local_surface_id);
+
+ // Try to embed |primary_child_surface_id| and if unavailabe, embed
+ // |fallback_child_surface_id|.
+ Quad root_quads[] = {Quad::SurfaceQuad(primary_child_surface_id,
+ fallback_child_surface_id, 1.f)};
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads))};
+
+ SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ root_local_surface_id_);
+
+ // The cc::CompositorFrame is submitted to |primary_child_surface_id|, so
+ // |fallback_child_surface_id| will not be used and we should see a green
+ // SolidColorDrawQuad.
+ Quad expected_quads1[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass expected_passes1[] = {Pass(expected_quads1, arraysize(expected_quads1))};
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ SurfaceId ids[] = {root_surface_id, primary_child_surface_id,
+ fallback_child_surface_id};
+ AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
+ arraysize(ids));
+
+ primary_child_support->EvictCurrentSurface();
+ fallback_child_support->EvictCurrentSurface();
+}
+
+TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) {
+ auto embedded_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId embedded_local_surface_id = allocator_.GenerateId();
+ SurfaceId embedded_surface_id(embedded_support->frame_sink_id(),
+ embedded_local_surface_id);
+
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass embedded_passes[] = {Pass(embedded_quads, arraysize(embedded_quads))};
+
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ arraysize(embedded_passes), embedded_local_surface_id);
+ auto copy_request = cc::CopyOutputRequest::CreateEmptyRequest();
+ auto* copy_request_ptr = copy_request.get();
+ embedded_support->RequestCopyOfSurface(std::move(copy_request));
+
+ Quad root_quads[] = {
+ Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorBLACK)};
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads))};
+
+ SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ root_local_surface_id_);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ Quad expected_quads[] = {
+ Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::RenderPassQuad(aggregated_frame.render_pass_list[0]->id),
+ Quad::SolidColorQuad(SK_ColorBLACK)};
+ Pass expected_passes[] = {Pass(embedded_quads, arraysize(embedded_quads)),
+ Pass(expected_quads, arraysize(expected_quads))};
+ TestPassesMatchExpectations(expected_passes, arraysize(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());
+ DCHECK_EQ(copy_request_ptr,
+ aggregated_frame.render_pass_list[0]->copy_requests[0].get());
+
+ SurfaceId surface_ids[] = {root_surface_id, embedded_surface_id};
+ EXPECT_EQ(arraysize(surface_ids),
+ aggregator_.previous_contained_surfaces().size());
+ for (size_t i = 0; i < arraysize(surface_ids); i++) {
+ EXPECT_TRUE(
+ aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
+ aggregator_.previous_contained_surfaces().end());
+ }
+
+ embedded_support->EvictCurrentSurface();
+}
+
+// Root surface may contain copy requests.
+TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) {
+ auto embedded_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId embedded_local_surface_id = allocator_.GenerateId();
+ SurfaceId embedded_surface_id(embedded_support->frame_sink_id(),
+ embedded_local_surface_id);
+
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass embedded_passes[] = {Pass(embedded_quads, arraysize(embedded_quads))};
+
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ arraysize(embedded_passes), embedded_local_surface_id);
+ auto copy_request(cc::CopyOutputRequest::CreateEmptyRequest());
+ auto* copy_request_ptr = copy_request.get();
+ auto copy_request2(cc::CopyOutputRequest::CreateEmptyRequest());
+ auto* copy_request2_ptr = copy_request2.get();
+
+ Quad root_quads[] = {
+ Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorBLACK)};
+ Quad root_quads2[] = {Quad::SolidColorQuad(SK_ColorRED)};
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), 1),
+ Pass(root_quads2, arraysize(root_quads2), 2)};
+ {
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(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));
+
+ support_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame));
+ }
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ Quad expected_quads[] = {Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SolidColorQuad(SK_ColorBLACK)};
+ Pass expected_passes[] = {Pass(expected_quads, arraysize(expected_quads)),
+ Pass(root_quads2, arraysize(root_quads2))};
+ TestPassesMatchExpectations(expected_passes, arraysize(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());
+ DCHECK_EQ(copy_request_ptr,
+ aggregated_frame.render_pass_list[0]->copy_requests[0].get());
+ ASSERT_EQ(1u, aggregated_frame.render_pass_list[1]->copy_requests.size());
+ DCHECK_EQ(copy_request2_ptr,
+ aggregated_frame.render_pass_list[1]->copy_requests[0].get());
+
+ SurfaceId surface_ids[] = {root_surface_id, embedded_surface_id};
+ EXPECT_EQ(arraysize(surface_ids),
+ aggregator_.previous_contained_surfaces().size());
+ for (size_t i = 0; i < arraysize(surface_ids); i++) {
+ EXPECT_TRUE(
+ aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
+ aggregator_.previous_contained_surfaces().end());
+ }
+
+ // Ensure copy requests have been removed from root surface.
+ const cc::CompositorFrame& original_frame =
+ manager_.surface_manager()
+ ->GetSurfaceForId(root_surface_id)
+ ->GetActiveFrame();
+ const auto& original_pass_list = original_frame.render_pass_list;
+ ASSERT_EQ(2u, original_pass_list.size());
+ DCHECK(original_pass_list[0]->copy_requests.empty());
+ DCHECK(original_pass_list[1]->copy_requests.empty());
+
+ embedded_support->EvictCurrentSurface();
+}
+
+TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
+ auto embedded_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ auto parent_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId2, kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId embedded_local_surface_id = allocator_.GenerateId();
+ SurfaceId embedded_surface_id(embedded_support->frame_sink_id(),
+ embedded_local_surface_id);
+ SurfaceId nonexistent_surface_id(support_->frame_sink_id(),
+ allocator_.GenerateId());
+
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN)};
+ Pass embedded_passes[] = {Pass(embedded_quads, arraysize(embedded_quads))};
+
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ arraysize(embedded_passes), embedded_local_surface_id);
+ auto copy_request(cc::CopyOutputRequest::CreateEmptyRequest());
+ auto* copy_request_ptr = copy_request.get();
+ embedded_support->RequestCopyOfSurface(std::move(copy_request));
+
+ LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
+ SurfaceId parent_surface_id(parent_support->frame_sink_id(),
+ parent_local_surface_id);
+
+ Quad parent_quads[] = {
+ Quad::SolidColorQuad(SK_ColorGRAY),
+ Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorLTGRAY)};
+ Pass parent_passes[] = {Pass(parent_quads, arraysize(parent_quads))};
+
+ {
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+
+ AddPasses(&frame.render_pass_list, gfx::Rect(SurfaceSize()), parent_passes,
+ arraysize(parent_passes));
+
+ frame.metadata.referenced_surfaces.push_back(embedded_surface_id);
+
+ parent_support->SubmitCompositorFrame(parent_local_surface_id,
+ std::move(frame));
+ }
+
+ Quad root_quads[] = {Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SolidColorQuad(SK_ColorBLACK)};
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads))};
+
+ {
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ frame.metadata.referenced_surfaces.push_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);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame));
+ }
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ // First pass should come from surface that had a copy request but was not
+ // referenced directly. The second pass comes from the root surface.
+ // 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)),
+ Pass(root_quads, arraysize(root_quads))};
+ TestPassesMatchExpectations(expected_passes, arraysize(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());
+ DCHECK_EQ(copy_request_ptr,
+ aggregated_frame.render_pass_list[0]->copy_requests[0].get());
+
+ SurfaceId surface_ids[] = {
+ SurfaceId(support_->frame_sink_id(), root_local_surface_id_),
+ parent_surface_id, embedded_surface_id, nonexistent_surface_id};
+ EXPECT_EQ(arraysize(surface_ids),
+ aggregator_.previous_contained_surfaces().size());
+ for (size_t i = 0; i < arraysize(surface_ids); i++) {
+ EXPECT_TRUE(
+ aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
+ aggregator_.previous_contained_surfaces().end());
+ }
+
+ embedded_support->EvictCurrentSurface();
+ parent_support->EvictCurrentSurface();
+}
+
+// This tests referencing a surface that has multiple render passes.
+TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSurfaceReference) {
+ LocalSurfaceId embedded_local_surface_id = child_allocator_.GenerateId();
+ SurfaceId embedded_surface_id(child_support_->frame_sink_id(),
+ embedded_local_surface_id);
+
+ int pass_ids[] = {1, 2, 3};
+
+ Quad embedded_quads[][2] = {
+ {Quad::SolidColorQuad(1), Quad::SolidColorQuad(2)},
+ {Quad::SolidColorQuad(3), Quad::RenderPassQuad(pass_ids[0])},
+ {Quad::SolidColorQuad(4), Quad::RenderPassQuad(pass_ids[1])}};
+ Pass embedded_passes[] = {
+ Pass(embedded_quads[0], arraysize(embedded_quads[0]), pass_ids[0]),
+ Pass(embedded_quads[1], arraysize(embedded_quads[1]), pass_ids[1]),
+ Pass(embedded_quads[2], arraysize(embedded_quads[2]), pass_ids[2])};
+
+ SubmitCompositorFrame(child_support_.get(), embedded_passes,
+ arraysize(embedded_passes), embedded_local_surface_id);
+
+ Quad root_quads[][2] = {
+ {Quad::SolidColorQuad(5), Quad::SolidColorQuad(6)},
+ {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::RenderPassQuad(pass_ids[0])},
+ {Quad::SolidColorQuad(7), Quad::RenderPassQuad(pass_ids[1])}};
+ Pass root_passes[] = {
+ Pass(root_quads[0], arraysize(root_quads[0]), pass_ids[0]),
+ Pass(root_quads[1], arraysize(root_quads[1]), pass_ids[1]),
+ Pass(root_quads[2], arraysize(root_quads[2]), pass_ids[2])};
+
+ SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ root_local_surface_id_);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(5u, aggregated_pass_list.size());
+ cc::RenderPassId actual_pass_ids[] = {
+ aggregated_pass_list[0]->id, aggregated_pass_list[1]->id,
+ aggregated_pass_list[2]->id, aggregated_pass_list[3]->id,
+ aggregated_pass_list[4]->id};
+ for (size_t i = 0; i < 5; ++i) {
+ for (size_t j = 0; j < i; ++j) {
+ EXPECT_NE(actual_pass_ids[i], actual_pass_ids[j]);
+ }
+ }
+
+ {
+ SCOPED_TRACE("First pass");
+ // The first pass will just be the first pass from the root surfaces quad
+ // with no render pass quads to remap.
+ TestPassMatchesExpectations(root_passes[0], aggregated_pass_list[0].get());
+ }
+
+ {
+ SCOPED_TRACE("Second pass");
+ // The next two passes will be from the embedded surface since we have to
+ // draw those passes before they are referenced from the render pass draw
+ // quad embedded into the root surface's second pass.
+ // First, there's the first embedded pass which doesn't reference anything
+ // else.
+ TestPassMatchesExpectations(embedded_passes[0],
+ aggregated_pass_list[1].get());
+ }
+
+ {
+ SCOPED_TRACE("Third pass");
+ const auto& third_pass_quad_list = aggregated_pass_list[2]->quad_list;
+ ASSERT_EQ(2u, third_pass_quad_list.size());
+ TestQuadMatchesExpectations(embedded_quads[1][0],
+ third_pass_quad_list.ElementAt(0));
+
+ // This render pass pass quad will reference the first pass from the
+ // embedded surface, which is the second pass in the aggregated frame.
+ ASSERT_EQ(cc::DrawQuad::RENDER_PASS,
+ third_pass_quad_list.ElementAt(1)->material);
+ const auto* third_pass_render_pass_draw_quad =
+ cc::RenderPassDrawQuad::MaterialCast(third_pass_quad_list.ElementAt(1));
+ EXPECT_EQ(actual_pass_ids[1],
+ third_pass_render_pass_draw_quad->render_pass_id);
+ }
+
+ {
+ SCOPED_TRACE("Fourth pass");
+ // The fourth pass will have aggregated quads from the root surface's second
+ // pass and the embedded surface's first pass.
+ const auto& fourth_pass_quad_list = aggregated_pass_list[3]->quad_list;
+ ASSERT_EQ(3u, fourth_pass_quad_list.size());
+
+ // The first quad will be the yellow quad from the embedded surface's last
+ // pass.
+ TestQuadMatchesExpectations(embedded_quads[2][0],
+ fourth_pass_quad_list.ElementAt(0));
+
+ // The next quad will be a render pass quad referencing the second pass from
+ // the embedded surface, which is the third pass in the aggregated frame.
+ ASSERT_EQ(cc::DrawQuad::RENDER_PASS,
+ fourth_pass_quad_list.ElementAt(1)->material);
+ const auto* fourth_pass_first_render_pass_draw_quad =
+ cc::RenderPassDrawQuad::MaterialCast(
+ fourth_pass_quad_list.ElementAt(1));
+ EXPECT_EQ(actual_pass_ids[2],
+ fourth_pass_first_render_pass_draw_quad->render_pass_id);
+
+ // The last quad will be a render pass quad referencing the first pass from
+ // the root surface, which is the first pass overall.
+ ASSERT_EQ(cc::DrawQuad::RENDER_PASS,
+ fourth_pass_quad_list.ElementAt(2)->material);
+ const auto* fourth_pass_second_render_pass_draw_quad =
+ cc::RenderPassDrawQuad::MaterialCast(
+ fourth_pass_quad_list.ElementAt(2));
+ EXPECT_EQ(actual_pass_ids[0],
+ fourth_pass_second_render_pass_draw_quad->render_pass_id);
+ }
+
+ {
+ SCOPED_TRACE("Fifth pass");
+ const auto& fifth_pass_quad_list = aggregated_pass_list[4]->quad_list;
+ ASSERT_EQ(2u, fifth_pass_quad_list.size());
+
+ TestQuadMatchesExpectations(root_quads[2][0],
+ fifth_pass_quad_list.ElementAt(0));
+
+ // The last quad in the last pass will reference the second pass from the
+ // root surface, which after aggregating is the fourth pass in the overall
+ // list.
+ ASSERT_EQ(cc::DrawQuad::RENDER_PASS,
+ fifth_pass_quad_list.ElementAt(1)->material);
+ const auto* fifth_pass_render_pass_draw_quad =
+ cc::RenderPassDrawQuad::MaterialCast(fifth_pass_quad_list.ElementAt(1));
+ EXPECT_EQ(actual_pass_ids[3],
+ fifth_pass_render_pass_draw_quad->render_pass_id);
+ }
+}
+
+// Tests an invalid surface reference in a frame. The surface quad should just
+// be dropped.
+TEST_F(SurfaceAggregatorValidSurfaceTest, InvalidSurfaceReference) {
+ Quad quads[] = {
+ Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SurfaceQuad(InvalidSurfaceId(), InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorBLUE)};
+ Pass passes[] = {Pass(quads, arraysize(quads))};
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ Quad expected_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SolidColorQuad(SK_ColorBLUE)};
+ Pass expected_passes[] = {Pass(expected_quads, arraysize(expected_quads))};
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ SurfaceId ids[] = {root_surface_id, InvalidSurfaceId()};
+
+ AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
+ arraysize(ids));
+}
+
+// Tests a reference to a valid surface with no submitted frame. This quad
+// should also just be dropped.
+TEST_F(SurfaceAggregatorValidSurfaceTest, ValidSurfaceReferenceWithNoFrame) {
+ LocalSurfaceId empty_local_surface_id = allocator_.GenerateId();
+ SurfaceId surface_with_no_frame_id(support_->frame_sink_id(),
+ empty_local_surface_id);
+
+ Quad quads[] = {
+ Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SurfaceQuad(surface_with_no_frame_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorBLUE)};
+ Pass passes[] = {Pass(quads, arraysize(quads))};
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ Quad expected_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SolidColorQuad(SK_ColorBLUE)};
+ Pass expected_passes[] = {Pass(expected_quads, arraysize(expected_quads))};
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ SurfaceId ids[] = {root_surface_id, surface_with_no_frame_id};
+ AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
+ arraysize(ids));
+}
+
+// Tests a surface quad referencing itself, generating a trivial cycle.
+// The quad creating the cycle should be dropped from the final frame.
+TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleCyclicalReference) {
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ Quad quads[] = {Quad::SurfaceQuad(root_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorYELLOW)};
+ Pass passes[] = {Pass(quads, arraysize(quads))};
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ Quad expected_quads[] = {Quad::SolidColorQuad(SK_ColorYELLOW)};
+ Pass expected_passes[] = {Pass(expected_quads, arraysize(expected_quads))};
+ SurfaceId ids[] = {root_surface_id};
+ AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
+ arraysize(ids));
+}
+
+// Tests a more complex cycle with one intermediate surface.
+TEST_F(SurfaceAggregatorValidSurfaceTest, TwoSurfaceCyclicalReference) {
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+
+ Quad parent_quads[] = {
+ Quad::SolidColorQuad(SK_ColorBLUE),
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorCYAN)};
+ Pass parent_passes[] = {Pass(parent_quads, arraysize(parent_quads))};
+
+ SubmitCompositorFrame(support_.get(), parent_passes, arraysize(parent_passes),
+ root_local_surface_id_);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ Quad child_quads[] = {
+ Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SurfaceQuad(root_surface_id, InvalidSurfaceId(), 1.f),
+ Quad::SolidColorQuad(SK_ColorMAGENTA)};
+ Pass child_passes[] = {Pass(child_quads, arraysize(child_quads))};
+
+ SubmitCompositorFrame(child_support_.get(), child_passes,
+ arraysize(child_passes), child_local_surface_id);
+
+ // The child surface's reference to the root_surface_ will be dropped, so
+ // we'll end up with:
+ // SK_ColorBLUE from the parent
+ // SK_ColorGREEN from the child
+ // SK_ColorMAGENTA from the child
+ // SK_ColorCYAN from the parent
+ Quad expected_quads[] = {Quad::SolidColorQuad(SK_ColorBLUE),
+ Quad::SolidColorQuad(SK_ColorGREEN),
+ Quad::SolidColorQuad(SK_ColorMAGENTA),
+ Quad::SolidColorQuad(SK_ColorCYAN)};
+ Pass expected_passes[] = {Pass(expected_quads, arraysize(expected_quads))};
+ SurfaceId ids[] = {root_surface_id, child_surface_id};
+ AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
+ arraysize(ids));
+}
+
+// Tests that we map render pass IDs from different surfaces into a unified
+// namespace and update cc::RenderPassDrawQuad's id references to match.
+TEST_F(SurfaceAggregatorValidSurfaceTest, RenderPassIdMapping) {
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+
+ cc::RenderPassId child_pass_id[] = {1u, 2u};
+ Quad child_quad[][1] = {{Quad::SolidColorQuad(SK_ColorGREEN)},
+ {Quad::RenderPassQuad(child_pass_id[0])}};
+ Pass surface_passes[] = {
+ Pass(child_quad[0], arraysize(child_quad[0]), child_pass_id[0]),
+ Pass(child_quad[1], arraysize(child_quad[1]), child_pass_id[1])};
+
+ SubmitCompositorFrame(child_support_.get(), surface_passes,
+ arraysize(surface_passes), child_local_surface_id);
+
+ // Pass IDs from the parent surface may collide with ones from the child.
+ cc::RenderPassId parent_pass_id[] = {3u, 2u};
+ Quad parent_quad[][1] = {
+ {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)},
+ {Quad::RenderPassQuad(parent_pass_id[0])}};
+ Pass parent_passes[] = {
+ Pass(parent_quad[0], arraysize(parent_quad[0]), parent_pass_id[0]),
+ Pass(parent_quad[1], arraysize(parent_quad[1]), parent_pass_id[1])};
+
+ SubmitCompositorFrame(support_.get(), parent_passes, arraysize(parent_passes),
+ root_local_surface_id_);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+ cc::RenderPassId actual_pass_ids[] = {aggregated_pass_list[0]->id,
+ aggregated_pass_list[1]->id,
+ aggregated_pass_list[2]->id};
+ // Make sure the aggregated frame's pass IDs are all unique.
+ for (size_t i = 0; i < 3; ++i) {
+ for (size_t j = 0; j < i; ++j) {
+ EXPECT_NE(actual_pass_ids[j], actual_pass_ids[i])
+ << "pass ids " << i << " and " << j;
+ }
+ }
+
+ // Make sure the render pass quads reference the remapped pass IDs.
+ cc::DrawQuad* render_pass_quads[] = {
+ aggregated_pass_list[1]->quad_list.front(),
+ aggregated_pass_list[2]->quad_list.front()};
+ ASSERT_EQ(render_pass_quads[0]->material, cc::DrawQuad::RENDER_PASS);
+ EXPECT_EQ(actual_pass_ids[0],
+ cc::RenderPassDrawQuad::MaterialCast(render_pass_quads[0])
+ ->render_pass_id);
+
+ ASSERT_EQ(render_pass_quads[1]->material, cc::DrawQuad::RENDER_PASS);
+ EXPECT_EQ(actual_pass_ids[1],
+ cc::RenderPassDrawQuad::MaterialCast(render_pass_quads[1])
+ ->render_pass_id);
+}
+
+void AddSolidColorQuadWithBlendMode(const gfx::Size& size,
+ cc::RenderPass* pass,
+ const SkBlendMode blend_mode) {
+ const gfx::Transform layer_to_target_transform;
+ const gfx::Rect layer_rect(size);
+ const gfx::Rect visible_layer_rect(size);
+ const gfx::Rect clip_rect(size);
+
+ bool is_clipped = false;
+ float opacity = 1.f;
+
+ bool force_anti_aliasing_off = false;
+ auto* sqs = pass->CreateAndAppendSharedQuadState();
+ sqs->SetAll(layer_to_target_transform, layer_rect, visible_layer_rect,
+ clip_rect, is_clipped, opacity, blend_mode, 0);
+
+ auto* color_quad = pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
+ color_quad->SetNew(pass->shared_quad_state_list.back(), visible_layer_rect,
+ visible_layer_rect, SK_ColorGREEN,
+ force_anti_aliasing_off);
+}
+
+// This tests that we update shared quad state pointers correctly within
+// aggregated passes. The shared quad state list on the aggregated pass will
+// include the shared quad states from each pass in one list so the quads will
+// end up pointed to shared quad state objects at different offsets. This test
+// uses the blend_mode value stored on the shared quad state to track the shared
+// quad state, but anything saved on the shared quad state would work.
+//
+// This test has 4 surfaces in the following structure:
+// root_surface -> quad with kClear_Mode,
+// [child_one_surface],
+// quad with kDstOver_Mode,
+// [child_two_surface],
+// quad with kDstIn_Mode
+// child_one_surface -> quad with kSrc_Mode,
+// [grandchild_surface],
+// quad with kSrcOver_Mode
+// child_two_surface -> quad with kSrcIn_Mode
+// grandchild_surface -> quad with kDst_Mode
+//
+// Resulting in the following aggregated pass:
+// quad_root_0 - blend_mode kClear_Mode
+// quad_child_one_0 - blend_mode kSrc_Mode
+// quad_grandchild_0 - blend_mode kDst_Mode
+// quad_child_one_1 - blend_mode kSrcOver_Mode
+// quad_root_1 - blend_mode kDstOver_Mode
+// quad_child_two_0 - blend_mode kSrcIn_Mode
+// quad_root_2 - blend_mode kDstIn_Mode
+TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateSharedQuadStateProperties) {
+ const SkBlendMode blend_modes[] = {
+ SkBlendMode::kClear, // 0
+ SkBlendMode::kSrc, // 1
+ SkBlendMode::kDst, // 2
+ SkBlendMode::kSrcOver, // 3
+ SkBlendMode::kDstOver, // 4
+ SkBlendMode::kSrcIn, // 5
+ SkBlendMode::kDstIn, // 6
+ };
+ auto grandchild_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ auto child_one_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ auto child_two_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId3, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ int pass_id = 1;
+ LocalSurfaceId grandchild_local_surface_id = allocator_.GenerateId();
+ SurfaceId grandchild_surface_id(grandchild_support->frame_sink_id(),
+ grandchild_local_surface_id);
+
+ auto grandchild_pass = cc::RenderPass::Create();
+ gfx::Rect output_rect(SurfaceSize());
+ gfx::Rect damage_rect(SurfaceSize());
+ gfx::Transform transform_to_root_target;
+ grandchild_pass->SetNew(pass_id, output_rect, damage_rect,
+ transform_to_root_target);
+ AddSolidColorQuadWithBlendMode(SurfaceSize(), grandchild_pass.get(),
+ blend_modes[2]);
+ QueuePassAsFrame(std::move(grandchild_pass), grandchild_local_surface_id,
+ grandchild_support.get());
+
+ LocalSurfaceId child_one_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_one_surface_id(child_one_support->frame_sink_id(),
+ child_one_local_surface_id);
+
+ auto child_one_pass = cc::RenderPass::Create();
+ child_one_pass->SetNew(pass_id, output_rect, damage_rect,
+ transform_to_root_target);
+ AddSolidColorQuadWithBlendMode(SurfaceSize(), child_one_pass.get(),
+ blend_modes[1]);
+ auto* grandchild_surface_quad =
+ child_one_pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ grandchild_surface_quad->SetNew(
+ child_one_pass->shared_quad_state_list.back(), gfx::Rect(SurfaceSize()),
+ gfx::Rect(SurfaceSize()), grandchild_surface_id,
+ cc::SurfaceDrawQuadType::PRIMARY, nullptr);
+ AddSolidColorQuadWithBlendMode(SurfaceSize(), child_one_pass.get(),
+ blend_modes[3]);
+ QueuePassAsFrame(std::move(child_one_pass), child_one_local_surface_id,
+ child_one_support.get());
+
+ LocalSurfaceId child_two_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_two_surface_id(child_two_support->frame_sink_id(),
+ child_two_local_surface_id);
+
+ auto child_two_pass = cc::RenderPass::Create();
+ child_two_pass->SetNew(pass_id, output_rect, damage_rect,
+ transform_to_root_target);
+ AddSolidColorQuadWithBlendMode(SurfaceSize(), child_two_pass.get(),
+ blend_modes[5]);
+ QueuePassAsFrame(std::move(child_two_pass), child_two_local_surface_id,
+ child_two_support.get());
+
+ auto root_pass = cc::RenderPass::Create();
+ root_pass->SetNew(pass_id, output_rect, damage_rect,
+ transform_to_root_target);
+
+ AddSolidColorQuadWithBlendMode(SurfaceSize(), root_pass.get(),
+ blend_modes[0]);
+ auto* child_one_surface_quad =
+ root_pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ child_one_surface_quad->SetNew(root_pass->shared_quad_state_list.back(),
+ gfx::Rect(SurfaceSize()),
+ gfx::Rect(SurfaceSize()), child_one_surface_id,
+ cc::SurfaceDrawQuadType::PRIMARY, nullptr);
+ AddSolidColorQuadWithBlendMode(SurfaceSize(), root_pass.get(),
+ blend_modes[4]);
+ auto* child_two_surface_quad =
+ root_pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ child_two_surface_quad->SetNew(root_pass->shared_quad_state_list.back(),
+ gfx::Rect(SurfaceSize()),
+ gfx::Rect(SurfaceSize()), child_two_surface_id,
+ cc::SurfaceDrawQuadType::PRIMARY, nullptr);
+ AddSolidColorQuadWithBlendMode(SurfaceSize(), root_pass.get(),
+ blend_modes[6]);
+
+ QueuePassAsFrame(std::move(root_pass), root_local_surface_id_,
+ support_.get());
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(1u, aggregated_pass_list.size());
+
+ const auto& aggregated_quad_list = aggregated_pass_list[0]->quad_list;
+
+ ASSERT_EQ(7u, aggregated_quad_list.size());
+
+ for (auto iter = aggregated_quad_list.cbegin();
+ iter != aggregated_quad_list.cend(); ++iter) {
+ EXPECT_EQ(blend_modes[iter.index()], iter->shared_quad_state->blend_mode)
+ << iter.index();
+ }
+
+ grandchild_support->EvictCurrentSurface();
+ child_one_support->EvictCurrentSurface();
+ child_two_support->EvictCurrentSurface();
+}
+
+// This tests that when aggregating a frame with multiple render passes that we
+// map the transforms for the root pass but do not modify the transform on child
+// passes.
+//
+// The root surface has one pass with a surface quad transformed by +10 in the y
+// direction.
+//
+// The middle surface has one pass with a surface quad scaled by 2 in the x
+// and 3 in the y directions.
+//
+// The child surface has two passes. The first pass has a quad with a transform
+// of +5 in the x direction. The second pass has a reference to the first pass'
+// pass id and a transform of +8 in the x direction.
+//
+// After aggregation, the child surface's root pass quad should have all
+// transforms concatenated for a total transform of +23 x, +10 y. The
+// contributing render pass' transform in the aggregate frame should not be
+// affected.
+TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
+ auto middle_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ // Innermost child surface.
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ {
+ int child_pass_id[] = {1, 2};
+ Quad child_quads[][1] = {
+ {Quad::SolidColorQuad(SK_ColorGREEN)},
+ {Quad::RenderPassQuad(child_pass_id[0])},
+ };
+ Pass child_passes[] = {
+ Pass(child_quads[0], arraysize(child_quads[0]), child_pass_id[0]),
+ Pass(child_quads[1], arraysize(child_quads[1]), child_pass_id[1])};
+
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(child_passes));
+
+ auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
+ child_nonroot_pass->transform_to_root_target.Translate(8, 0);
+ auto* child_nonroot_pass_sqs =
+ child_nonroot_pass->shared_quad_state_list.front();
+ child_nonroot_pass_sqs->quad_to_target_transform.Translate(5, 0);
+
+ auto* child_root_pass = child_frame.render_pass_list[1].get();
+ auto* child_root_pass_sqs = child_root_pass->shared_quad_state_list.front();
+ child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
+ child_root_pass_sqs->is_clipped = true;
+ child_root_pass_sqs->clip_rect = gfx::Rect(0, 0, 5, 5);
+
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+ }
+
+ // Middle child surface.
+ LocalSurfaceId middle_local_surface_id = allocator_.GenerateId();
+ SurfaceId middle_surface_id(middle_support->frame_sink_id(),
+ middle_local_surface_id);
+ {
+ Quad middle_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ Pass middle_passes[] = {
+ Pass(middle_quads, arraysize(middle_quads)),
+ };
+
+ cc::CompositorFrame middle_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&middle_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ middle_passes, arraysize(middle_passes));
+
+ auto* middle_root_pass = middle_frame.render_pass_list[0].get();
+ middle_root_pass->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 1, 100, 7);
+ auto* middle_root_pass_sqs =
+ middle_root_pass->shared_quad_state_list.front();
+ middle_root_pass_sqs->quad_to_target_transform.Scale(2, 3);
+
+ middle_support->SubmitCompositorFrame(middle_local_surface_id,
+ std::move(middle_frame));
+ }
+
+ // Root surface.
+ Quad secondary_quads[] = {
+ Quad::SolidColorQuad(1),
+ Quad::SurfaceQuad(middle_surface_id, InvalidSurfaceId(), 1.f)};
+ Quad root_quads[] = {Quad::SolidColorQuad(1)};
+ Pass root_passes[] = {Pass(secondary_quads, arraysize(secondary_quads)),
+ Pass(root_quads, arraysize(root_quads))};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ root_frame.render_pass_list[0]
+ ->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(0, 7);
+ root_frame.render_pass_list[0]
+ ->shared_quad_state_list.ElementAt(1)
+ ->quad_to_target_transform.Translate(0, 10);
+ root_frame.render_pass_list[0]->quad_list.ElementAt(1)->visible_rect =
+ gfx::Rect(0, 0, 8, 100);
+
+ root_frame.render_pass_list[0]->transform_to_root_target.Translate(10, 5);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ ASSERT_EQ(1u, aggregated_pass_list[0]->shared_quad_state_list.size());
+
+ // The first pass should have one shared quad state for the one solid color
+ // quad.
+ EXPECT_EQ(1u, aggregated_pass_list[0]->shared_quad_state_list.size());
+ // The second pass should have just two shared quad states. We'll
+ // verify the properties through the quads.
+ EXPECT_EQ(2u, aggregated_pass_list[1]->shared_quad_state_list.size());
+
+ EXPECT_EQ(1u, aggregated_pass_list[2]->shared_quad_state_list.size());
+
+ auto* aggregated_first_pass_sqs =
+ aggregated_pass_list[0]->shared_quad_state_list.front();
+
+ // The first pass's transform should be unaffected by the embedding and still
+ // be a translation by +5 in the x direction.
+ gfx::Transform expected_aggregated_first_pass_sqs_transform;
+ expected_aggregated_first_pass_sqs_transform.Translate(5, 0);
+ EXPECT_EQ(expected_aggregated_first_pass_sqs_transform.ToString(),
+ aggregated_first_pass_sqs->quad_to_target_transform.ToString());
+
+ // The first pass's transform to the root target should include the aggregated
+ // transform, including the transform from the child pass to the root.
+ gfx::Transform expected_first_pass_transform_to_root_target;
+ expected_first_pass_transform_to_root_target.Translate(10, 5);
+ expected_first_pass_transform_to_root_target.Translate(0, 10);
+ expected_first_pass_transform_to_root_target.Scale(2, 3);
+ expected_first_pass_transform_to_root_target.Translate(8, 0);
+ EXPECT_EQ(expected_first_pass_transform_to_root_target.ToString(),
+ aggregated_pass_list[0]->transform_to_root_target.ToString());
+
+ ASSERT_EQ(2u, aggregated_pass_list[1]->quad_list.size());
+
+ gfx::Transform expected_root_pass_quad_transforms[2];
+ // The first quad in the root pass is the solid color quad from the original
+ // root surface. Its transform should be unaffected by the aggregation and
+ // still be +7 in the y direction.
+ expected_root_pass_quad_transforms[0].Translate(0, 7);
+ // The second quad in the root pass is aggregated from the child surface so
+ // its transform should be the combination of its original translation
+ // (0, 10), the middle surface draw quad's scale of (2, 3), and the
+ // child surface draw quad's translation (8, 0).
+ expected_root_pass_quad_transforms[1].Translate(0, 10);
+ expected_root_pass_quad_transforms[1].Scale(2, 3);
+ expected_root_pass_quad_transforms[1].Translate(8, 0);
+
+ for (auto iter = aggregated_pass_list[1]->quad_list.cbegin();
+ iter != aggregated_pass_list[1]->quad_list.cend(); ++iter) {
+ EXPECT_EQ(expected_root_pass_quad_transforms[iter.index()].ToString(),
+ iter->shared_quad_state->quad_to_target_transform.ToString())
+ << iter.index();
+ }
+
+ EXPECT_TRUE(
+ aggregated_pass_list[1]->shared_quad_state_list.ElementAt(1)->is_clipped);
+
+ // The second quad in the root pass is aggregated from the child, so its
+ // clip rect must be transformed by the child's translation/scale and
+ // clipped be the visible_rects for both children.
+ EXPECT_EQ(gfx::Rect(0, 13, 8, 12).ToString(),
+ aggregated_pass_list[1]
+ ->shared_quad_state_list.ElementAt(1)
+ ->clip_rect.ToString());
+
+ middle_support->EvictCurrentSurface();
+}
+
+// Tests that damage rects are aggregated correctly when surfaces change.
+TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
+ auto parent_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ Quad child_quads[] = {Quad::RenderPassQuad(1)};
+ Pass child_passes[] = {Pass(child_quads, arraysize(child_quads), 1)};
+
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(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();
+ child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
+
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+
+ Quad parent_surface_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ Pass parent_surface_passes[] = {
+ Pass(parent_surface_quads, arraysize(parent_surface_quads), 1)};
+
+ // Parent surface is only used to test if the transform is applied correctly
+ // to the child surface's damage.
+ cc::CompositorFrame parent_surface_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&parent_surface_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ parent_surface_passes, arraysize(parent_surface_passes));
+
+ LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
+ SurfaceId parent_surface_id(parent_support->frame_sink_id(),
+ parent_local_surface_id);
+ parent_support->SubmitCompositorFrame(parent_local_surface_id,
+ std::move(parent_surface_frame));
+
+ Quad root_surface_quads[] = {
+ Quad::SurfaceQuad(parent_surface_id, InvalidSurfaceId(), 1.f)};
+ Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
+
+ Pass root_passes[] = {
+ Pass(root_surface_quads, arraysize(root_surface_quads), 1),
+ Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ root_frame.render_pass_list[0]
+ ->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(0, 10);
+ root_frame.render_pass_list[0]->damage_rect = gfx::Rect(5, 5, 10, 10);
+ root_frame.render_pass_list[1]->damage_rect = gfx::Rect(5, 5, 100, 100);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ // Damage rect for first aggregation should contain entire root surface.
+ EXPECT_TRUE(
+ aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+
+ {
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(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();
+ child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
+ child_root_pass->damage_rect = gfx::Rect(10, 10, 10, 10);
+
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(),
+ root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ // Outer surface didn't change, so transformed inner damage rect should be
+ // used.
+ EXPECT_EQ(gfx::Rect(10, 20, 10, 10).ToString(),
+ aggregated_pass_list[1]->damage_rect.ToString());
+ }
+
+ {
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ root_passes, arraysize(root_passes));
+
+ root_frame.render_pass_list[0]
+ ->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(0, 10);
+ root_frame.render_pass_list[0]->damage_rect = gfx::Rect(0, 0, 1, 1);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+ }
+
+ {
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ root_passes, arraysize(root_passes));
+
+ root_frame.render_pass_list[0]
+ ->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(0, 10);
+ root_frame.render_pass_list[0]->damage_rect = gfx::Rect(1, 1, 1, 1);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(),
+ root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ // The root surface was enqueued without being aggregated once, so it should
+ // be treated as completely damaged.
+ EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.Contains(
+ gfx::Rect(SurfaceSize())));
+ }
+
+ // No Surface changed, so no damage should be given.
+ {
+ SurfaceId root_surface_id(support_->frame_sink_id(),
+ root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.IsEmpty());
+ }
+
+ // SetFullDamageRectForSurface should cause the entire output to be
+ // marked as damaged.
+ {
+ aggregator_.SetFullDamageForSurface(root_surface_id);
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.Contains(
+ gfx::Rect(SurfaceSize())));
+ }
+
+ parent_support->EvictCurrentSurface();
+}
+
+// Check that damage is correctly calculated for surfaces.
+TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) {
+ Quad root_render_pass_quads[] = {Quad::SolidColorQuad(1)};
+
+ Pass root_passes[] = {
+ Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ root_frame.render_pass_list[0]->damage_rect = gfx::Rect(5, 5, 100, 100);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ {
+ SurfaceId root_surface_id(support_->frame_sink_id(),
+ root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(1u, aggregated_pass_list.size());
+
+ // Damage rect for first aggregation should contain entire root surface.
+ EXPECT_TRUE(aggregated_pass_list[0]->damage_rect.Contains(
+ gfx::Rect(SurfaceSize())));
+ }
+
+ LocalSurfaceId second_root_local_surface_id = allocator_.GenerateId();
+ SurfaceId second_root_surface_id(support_->frame_sink_id(),
+ second_root_local_surface_id);
+ {
+ Quad root_render_pass_quads[] = {Quad::SolidColorQuad(1)};
+
+ Pass root_passes[] = {
+ Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ root_passes, arraysize(root_passes));
+
+ root_frame.render_pass_list[0]->damage_rect = gfx::Rect(1, 2, 3, 4);
+
+ support_->SubmitCompositorFrame(second_root_local_surface_id,
+ std::move(root_frame));
+ }
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(second_root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(1u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(1, 2, 3, 4), aggregated_pass_list[0]->damage_rect);
+ }
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(second_root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(1u, aggregated_pass_list.size());
+
+ // No new frame, so no new damage.
+ EXPECT_TRUE(aggregated_pass_list[0]->damage_rect.IsEmpty());
+ }
+}
+
+class SurfaceAggregatorPartialSwapTest
+ : public SurfaceAggregatorValidSurfaceTest {
+ public:
+ SurfaceAggregatorPartialSwapTest()
+ : SurfaceAggregatorValidSurfaceTest(true) {}
+};
+
+// Tests that quads outside the damage rect are ignored.
+TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ // The child surface has three quads, one with a visible rect of 13,13 4x4 and
+ // the other other with a visible rect of 10,10 2x2 (relative to root target
+ // space), and one with a non-invertible transform.
+ {
+ int child_pass_id = 1;
+ 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), child_pass_id),
+ Pass(child_quads2, arraysize(child_quads2), child_pass_id),
+ Pass(child_quads3, arraysize(child_quads2), child_pass_id)};
+
+ cc::RenderPassList child_pass_list;
+ AddPasses(&child_pass_list, gfx::Rect(SurfaceSize()), child_passes,
+ arraysize(child_passes));
+
+ child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(1, 1, 2, 2);
+ auto* child_sqs = child_pass_list[0]->shared_quad_state_list.ElementAt(0u);
+ child_sqs->quad_to_target_transform.Translate(1, 1);
+ child_sqs->quad_to_target_transform.Scale(2, 2);
+
+ child_pass_list[1]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 0, 2, 2);
+
+ auto* child_noninvertible_sqs =
+ child_pass_list[2]->shared_quad_state_list.ElementAt(0u);
+ child_noninvertible_sqs->quad_to_target_transform.matrix().setDouble(0, 0,
+ 0.0);
+ EXPECT_FALSE(
+ child_noninvertible_sqs->quad_to_target_transform.IsInvertible());
+ child_pass_list[2]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 0, 2, 2);
+
+ SubmitPassListAsFrame(child_support_.get(), child_local_surface_id,
+ &child_pass_list);
+ }
+
+ {
+ Quad root_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads))};
+
+ cc::RenderPassList root_pass_list;
+ AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ auto* root_pass = root_pass_list[0].get();
+ root_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ root_pass->damage_rect = gfx::Rect(0, 0, 1, 1);
+
+ SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
+ &root_pass_list);
+ }
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // Damage rect for first aggregation should contain entire root surface.
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+
+ // Create a root surface with a smaller damage rect.
+ {
+ Quad root_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+
+ Pass root_passes[] = {Pass(root_quads, arraysize(root_quads))};
+
+ cc::RenderPassList root_pass_list;
+ AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ auto* root_pass = root_pass_list[0].get();
+ root_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
+ SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
+ &root_pass_list);
+ }
+
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // Only first quad from surface is inside damage rect and should be
+ // included.
+ EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(0u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 2),
+ aggregated_pass_list[1]->quad_list.back()->visible_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ }
+
+ // New child frame has same content and no damage, but has a
+ // CopyOutputRequest.
+ {
+ int child_pass_ids[] = {1, 2};
+ Quad child_quads1[] = {Quad::SolidColorQuad(1)};
+ Quad child_quads2[] = {Quad::RenderPassQuad(child_pass_ids[0])};
+ Pass child_passes[] = {
+ Pass(child_quads1, arraysize(child_quads1), child_pass_ids[0]),
+ Pass(child_quads2, arraysize(child_quads2), child_pass_ids[1])};
+
+ cc::RenderPassList child_pass_list;
+ AddPasses(&child_pass_list, gfx::Rect(SurfaceSize()), child_passes,
+ arraysize(child_passes));
+
+ child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(1, 1, 2, 2);
+ auto* child_sqs = child_pass_list[0]->shared_quad_state_list.ElementAt(0u);
+ child_sqs->quad_to_target_transform.Translate(1, 1);
+ child_sqs->quad_to_target_transform.Scale(2, 2);
+
+ child_pass_list[1]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 0, 2, 2);
+
+ auto* child_root_pass = child_pass_list[1].get();
+
+ child_root_pass->copy_requests.push_back(
+ cc::CopyOutputRequest::CreateEmptyRequest());
+ child_root_pass->damage_rect = gfx::Rect();
+ SubmitPassListAsFrame(child_support_.get(), child_local_surface_id,
+ &child_pass_list);
+ }
+
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ // Output frame should have no damage, but all quads included.
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[1]->damage_rect);
+ EXPECT_TRUE(aggregated_pass_list[2]->damage_rect.IsEmpty());
+ ASSERT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ ASSERT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(1, 1, 2, 2),
+ aggregated_pass_list[0]->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 2),
+ aggregated_pass_list[1]->quad_list.ElementAt(0)->visible_rect);
+ ASSERT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ }
+
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+ // There were no changes since last aggregation, so output should be empty
+ // and have no damage.
+ ASSERT_EQ(1u, aggregated_pass_list.size());
+ EXPECT_TRUE(aggregated_pass_list[0]->damage_rect.IsEmpty());
+ ASSERT_EQ(0u, aggregated_pass_list[0]->quad_list.size());
+ }
+
+ // Root surface has smaller damage rect, but filter on render pass means all
+ // of it and its descendant passes should be aggregated.
+ {
+ int root_pass_ids[] = {1, 2, 3};
+ Quad root_quads1[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ 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), root_pass_ids[0]),
+ Pass(root_quads2, arraysize(root_quads2), root_pass_ids[1]),
+ Pass(root_quads3, arraysize(root_quads3), root_pass_ids[2])};
+
+ cc::RenderPassList root_pass_list;
+ AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ auto* filter_pass = root_pass_list[1].get();
+ filter_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ auto* root_pass = root_pass_list[2].get();
+ filter_pass->filters.Append(cc::FilterOperation::CreateBlurFilter(2));
+ root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
+ SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
+ &root_pass_list);
+ }
+
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(4u, aggregated_pass_list.size());
+
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[3]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ // First render pass draw quad is outside damage rect, so shouldn't be
+ // drawn.
+ EXPECT_EQ(0u, aggregated_pass_list[3]->quad_list.size());
+ }
+
+ // Root surface has smaller damage rect. Opacity filter on render pass
+ // means Surface quad under it should be aggregated.
+ {
+ int root_pass_ids[] = {1, 2};
+ Quad root_quads1[] = {
+ Quad::SolidColorQuad(1),
+ };
+ Quad root_quads2[] = {
+ Quad::RenderPassQuad(root_pass_ids[0]),
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ Pass root_passes[] = {
+ Pass(root_quads1, arraysize(root_quads1), root_pass_ids[0]),
+ Pass(root_quads2, arraysize(root_quads2), root_pass_ids[1])};
+
+ cc::RenderPassList root_pass_list;
+ AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ auto* pass = root_pass_list[0].get();
+ auto* root_pass = root_pass_list[1].get();
+ root_pass->shared_quad_state_list.ElementAt(1)
+ ->quad_to_target_transform.Translate(10, 10);
+ pass->background_filters.Append(
+ cc::FilterOperation::CreateOpacityFilter(0.5f));
+ root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
+ SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
+ &root_pass_list);
+ }
+
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // Pass 0 is solid color quad from root, but outside damage rect.
+ EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(0u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 2), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(0u, aggregated_pass_list[1]->quad_list.size());
+
+ // First render pass draw quad is outside damage rect, so shouldn't be
+ // drawn. SurfaceDrawQuad is after opacity filter, so corresponding
+ // cc::RenderPassDrawQuad should be drawn.
+ EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ }
+
+ // TODO(wutao): Partial swap does not work with pixel moving background
+ // filter. See https://crbug.com/737255.
+ // Has background filter on render pass will make the whole output rect as
+ // damaged.
+ {
+ int root_pass_ids[] = {1, 2};
+ Quad root_quads1[] = {
+ Quad::SolidColorQuad(1),
+ };
+ Quad root_quads2[] = {
+ Quad::RenderPassQuad(root_pass_ids[0]),
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ Pass root_passes[] = {
+ Pass(root_quads1, arraysize(root_quads1), root_pass_ids[0]),
+ Pass(root_quads2, arraysize(root_quads2), root_pass_ids[1])};
+
+ cc::RenderPassList root_pass_list;
+ AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ auto* pass = root_pass_list[0].get();
+ auto* root_pass = root_pass_list[1].get();
+ root_pass->shared_quad_state_list.ElementAt(1)
+ ->quad_to_target_transform.Translate(10, 10);
+ pass->background_filters.Append(cc::FilterOperation::CreateBlurFilter(2));
+ root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
+ SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
+ &root_pass_list);
+ }
+
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // Pass 0 has background blur filter, so should be drawn.
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+
+ // First render pass draw quad is outside damage rect but has background
+ // filter, so should be drawn. SurfaceDrawQuad is after background filter,
+ // so corresponding cc::RenderPassDrawQuad should be drawn.
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(2u, aggregated_pass_list[2]->quad_list.size());
+ }
+}
+
+class SurfaceAggregatorWithResourcesTest : public testing::Test {
+ public:
+ void SetUp() override {
+ shared_bitmap_manager_ = base::MakeUnique<cc::TestSharedBitmapManager>();
+ resource_provider_ =
+ cc::FakeResourceProvider::Create(nullptr, shared_bitmap_manager_.get());
+
+ aggregator_ = base::MakeUnique<SurfaceAggregator>(
+ manager_.surface_manager(), resource_provider_.get(), false);
+ aggregator_->set_output_is_secure(true);
+ }
+
+ protected:
+ FrameSinkManagerImpl manager_;
+ std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
+ std::unique_ptr<cc::ResourceProvider> resource_provider_;
+ std::unique_ptr<SurfaceAggregator> aggregator_;
+};
+
+void SubmitCompositorFrameWithResources(cc::ResourceId* resource_ids,
+ size_t num_resource_ids,
+ bool valid,
+ SurfaceId child_id,
+ CompositorFrameSinkSupport* support,
+ SurfaceId surface_id) {
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(1, gfx::Rect(0, 0, 20, 20), gfx::Rect(), gfx::Transform());
+ auto* sqs = pass->CreateAndAppendSharedQuadState();
+ sqs->opacity = 1.f;
+ if (child_id.is_valid()) {
+ auto* surface_quad = pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+ surface_quad->SetNew(sqs, gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1),
+ child_id, cc::SurfaceDrawQuadType::PRIMARY, nullptr);
+ }
+
+ for (size_t i = 0u; i < num_resource_ids; ++i) {
+ cc::TransferableResource resource;
+ resource.id = resource_ids[i];
+ // ResourceProvider is software, so only software resources are valid.
+ resource.is_software = valid;
+ frame.resource_list.push_back(resource);
+ auto* quad = pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
+ const gfx::Rect rect;
+ const gfx::Rect opaque_rect;
+ const gfx::Rect visible_rect;
+ bool needs_blending = false;
+ bool premultiplied_alpha = false;
+ const gfx::PointF uv_top_left;
+ const gfx::PointF uv_bottom_right;
+ SkColor background_color = SK_ColorGREEN;
+ const float vertex_opacity[4] = {0.f, 0.f, 1.f, 1.f};
+ bool flipped = false;
+ bool nearest_neighbor = false;
+ bool secure_output_only = true;
+ quad->SetAll(sqs, rect, opaque_rect, visible_rect, needs_blending,
+ resource_ids[i], gfx::Size(), premultiplied_alpha, uv_top_left,
+ uv_bottom_right, background_color, vertex_opacity, flipped,
+ nearest_neighbor, secure_output_only);
+ }
+ frame.render_pass_list.push_back(std::move(pass));
+ support->SubmitCompositorFrame(surface_id.local_surface_id(),
+ std::move(frame));
+}
+
+TEST_F(SurfaceAggregatorWithResourcesTest, TakeResourcesOneSurface) {
+ cc::FakeCompositorFrameSinkSupportClient client;
+ auto support = CompositorFrameSinkSupport::Create(
+ &client, &manager_, kArbitraryRootFrameSinkId, kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_surface_id(7u, base::UnguessableToken::Create());
+ SurfaceId surface_id(support->frame_sink_id(), local_surface_id);
+
+ cc::ResourceId ids[] = {11, 12, 13};
+ SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ support.get(), surface_id);
+
+ cc::CompositorFrame frame = aggregator_->Aggregate(surface_id);
+
+ // Nothing should be available to be returned yet.
+ EXPECT_TRUE(client.returned_resources().empty());
+
+ SubmitCompositorFrameWithResources(NULL, 0u, true, SurfaceId(), support.get(),
+ surface_id);
+
+ frame = aggregator_->Aggregate(surface_id);
+
+ ASSERT_EQ(3u, client.returned_resources().size());
+ cc::ResourceId returned_ids[3];
+ for (size_t i = 0; i < 3; ++i) {
+ returned_ids[i] = client.returned_resources()[i].id;
+ }
+ EXPECT_THAT(returned_ids,
+ testing::WhenSorted(testing::ElementsAreArray(ids)));
+
+ support->EvictCurrentSurface();
+}
+
+// This test verifies that when a CompositorFrame is submitted to a new surface
+// ID, and a new display frame is generated, then the resources of the old
+// surface are returned to the appropriate client.
+TEST_F(SurfaceAggregatorWithResourcesTest, ReturnResourcesAsSurfacesChange) {
+ cc::FakeCompositorFrameSinkSupportClient client;
+ auto support = CompositorFrameSinkSupport::Create(
+ &client, &manager_, kArbitraryRootFrameSinkId, kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_surface_id1(7u, base::UnguessableToken::Create());
+ LocalSurfaceId local_surface_id2(8u, base::UnguessableToken::Create());
+ SurfaceId surface_id1(support->frame_sink_id(), local_surface_id1);
+ SurfaceId surface_id2(support->frame_sink_id(), local_surface_id2);
+
+ cc::ResourceId ids[] = {11, 12, 13};
+ SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ support.get(), surface_id1);
+
+ cc::CompositorFrame frame = aggregator_->Aggregate(surface_id1);
+
+ // Nothing should be available to be returned yet.
+ EXPECT_TRUE(client.returned_resources().empty());
+
+ // Submitting a CompositorFrame to |surface_id2| should cause the surface
+ // associated with |surface_id1| to get garbage collected.
+ SubmitCompositorFrameWithResources(NULL, 0u, true, SurfaceId(), support.get(),
+ surface_id2);
+
+ frame = aggregator_->Aggregate(surface_id2);
+
+ ASSERT_EQ(3u, client.returned_resources().size());
+ cc::ResourceId returned_ids[3];
+ for (size_t i = 0; i < 3; ++i) {
+ returned_ids[i] = client.returned_resources()[i].id;
+ }
+ EXPECT_THAT(returned_ids,
+ testing::WhenSorted(testing::ElementsAreArray(ids)));
+
+ support->EvictCurrentSurface();
+}
+
+TEST_F(SurfaceAggregatorWithResourcesTest, TakeInvalidResources) {
+ cc::FakeCompositorFrameSinkSupportClient client;
+ auto support = CompositorFrameSinkSupport::Create(
+ &client, &manager_, kArbitraryRootFrameSinkId, kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_surface_id(7u, base::UnguessableToken::Create());
+ SurfaceId surface_id(support->frame_sink_id(), local_surface_id);
+
+ cc::CompositorFrame frame = cc::test::MakeCompositorFrame();
+ cc::TransferableResource resource;
+ resource.id = 11;
+ // ResourceProvider is software but resource is not, so it should be
+ // ignored.
+ resource.is_software = false;
+ frame.resource_list.push_back(resource);
+ support->SubmitCompositorFrame(local_surface_id, std::move(frame));
+
+ cc::CompositorFrame returned_frame = aggregator_->Aggregate(surface_id);
+
+ // Nothing should be available to be returned yet.
+ EXPECT_TRUE(client.returned_resources().empty());
+
+ SubmitCompositorFrameWithResources(NULL, 0, true, SurfaceId(), support.get(),
+ surface_id);
+ ASSERT_EQ(1u, client.returned_resources().size());
+ EXPECT_EQ(11u, client.returned_resources()[0].id);
+
+ support->EvictCurrentSurface();
+}
+
+TEST_F(SurfaceAggregatorWithResourcesTest, TwoSurfaces) {
+ cc::FakeCompositorFrameSinkSupportClient client;
+ auto support1 = CompositorFrameSinkSupport::Create(
+ &client, &manager_, FrameSinkId(1, 1), kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ auto support2 = CompositorFrameSinkSupport::Create(
+ &client, &manager_, FrameSinkId(2, 2), kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_frame1_id(7u, base::UnguessableToken::Create());
+ SurfaceId surface1_id(support1->frame_sink_id(), local_frame1_id);
+
+ LocalSurfaceId local_frame2_id(8u, base::UnguessableToken::Create());
+ SurfaceId surface2_id(support2->frame_sink_id(), local_frame2_id);
+
+ cc::ResourceId ids[] = {11, 12, 13};
+ SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ support1.get(), surface1_id);
+ cc::ResourceId ids2[] = {14, 15, 16};
+ SubmitCompositorFrameWithResources(ids2, arraysize(ids2), true, SurfaceId(),
+ support2.get(), surface2_id);
+
+ cc::CompositorFrame frame = aggregator_->Aggregate(surface1_id);
+
+ SubmitCompositorFrameWithResources(NULL, 0, true, SurfaceId(), support1.get(),
+ surface1_id);
+
+ // Nothing should be available to be returned yet.
+ EXPECT_TRUE(client.returned_resources().empty());
+
+ frame = aggregator_->Aggregate(surface2_id);
+
+ // surface1_id wasn't referenced, so its resources should be returned.
+ ASSERT_EQ(3u, client.returned_resources().size());
+ cc::ResourceId returned_ids[3];
+ for (size_t i = 0; i < 3; ++i) {
+ returned_ids[i] = client.returned_resources()[i].id;
+ }
+ EXPECT_THAT(returned_ids,
+ testing::WhenSorted(testing::ElementsAreArray(ids)));
+ EXPECT_EQ(3u, resource_provider_->num_resources());
+
+ support1->EvictCurrentSurface();
+ support2->EvictCurrentSurface();
+}
+
+// Ensure that aggregator completely ignores Surfaces that reference invalid
+// resources.
+TEST_F(SurfaceAggregatorWithResourcesTest, InvalidChildSurface) {
+ auto root_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryRootFrameSinkId, kRootIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ auto middle_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ auto child_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId root_local_surface_id(7u, kArbitraryToken);
+ SurfaceId root_surface_id(root_support->frame_sink_id(),
+ root_local_surface_id);
+ LocalSurfaceId middle_local_surface_id(8u, kArbitraryToken);
+ SurfaceId middle_surface_id(middle_support->frame_sink_id(),
+ middle_local_surface_id);
+ LocalSurfaceId child_local_surface_id(9u, kArbitraryToken);
+ SurfaceId child_surface_id(child_support->frame_sink_id(),
+ child_local_surface_id);
+
+ cc::ResourceId ids[] = {14, 15, 16};
+ SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ child_support.get(), child_surface_id);
+
+ cc::ResourceId ids2[] = {17, 18, 19};
+ SubmitCompositorFrameWithResources(ids2, arraysize(ids2), false,
+ child_surface_id, middle_support.get(),
+ middle_surface_id);
+
+ cc::ResourceId ids3[] = {20, 21, 22};
+ SubmitCompositorFrameWithResources(ids3, arraysize(ids3), true,
+ middle_surface_id, root_support.get(),
+ root_surface_id);
+
+ cc::CompositorFrame frame;
+ frame = aggregator_->Aggregate(root_surface_id);
+
+ auto* pass_list = &frame.render_pass_list;
+ 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,
+ child_surface_id, middle_support.get(),
+ middle_surface_id);
+
+ frame = aggregator_->Aggregate(root_surface_id);
+
+ pass_list = &frame.render_pass_list;
+ ASSERT_EQ(1u, pass_list->size());
+ EXPECT_EQ(3u, pass_list->back()->shared_quad_state_list.size());
+ EXPECT_EQ(9u, pass_list->back()->quad_list.size());
+
+ root_support->EvictCurrentSurface();
+ middle_support->EvictCurrentSurface();
+ child_support->EvictCurrentSurface();
+}
+
+TEST_F(SurfaceAggregatorWithResourcesTest, SecureOutputTexture) {
+ auto support1 = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, FrameSinkId(1, 1), kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ auto support2 = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, FrameSinkId(2, 2), kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_frame1_id(7u, base::UnguessableToken::Create());
+ SurfaceId surface1_id(support1->frame_sink_id(), local_frame1_id);
+
+ LocalSurfaceId local_frame2_id(8u, base::UnguessableToken::Create());
+ SurfaceId surface2_id(support2->frame_sink_id(), local_frame2_id);
+
+ cc::ResourceId ids[] = {11, 12, 13};
+ SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ support1.get(), surface1_id);
+
+ cc::CompositorFrame frame = aggregator_->Aggregate(surface1_id);
+
+ auto* render_pass = frame.render_pass_list.back().get();
+
+ EXPECT_EQ(cc::DrawQuad::TEXTURE_CONTENT,
+ render_pass->quad_list.back()->material);
+
+ {
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(1, gfx::Rect(0, 0, 20, 20), gfx::Rect(), gfx::Transform());
+ auto* sqs = pass->CreateAndAppendSharedQuadState();
+ sqs->opacity = 1.f;
+ auto* surface_quad = pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
+
+ surface_quad->SetNew(sqs, gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1),
+ surface1_id, cc::SurfaceDrawQuadType::PRIMARY,
+ nullptr);
+ pass->copy_requests.push_back(cc::CopyOutputRequest::CreateEmptyRequest());
+
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+ frame.render_pass_list.push_back(std::move(pass));
+
+ support2->SubmitCompositorFrame(local_frame2_id, std::move(frame));
+ }
+
+ frame = aggregator_->Aggregate(surface2_id);
+ EXPECT_EQ(1u, frame.render_pass_list.size());
+ render_pass = frame.render_pass_list.front().get();
+
+ // Parent has copy request, so texture should not be drawn.
+ EXPECT_EQ(cc::DrawQuad::SOLID_COLOR, render_pass->quad_list.back()->material);
+
+ frame = aggregator_->Aggregate(surface2_id);
+ EXPECT_EQ(1u, frame.render_pass_list.size());
+ render_pass = frame.render_pass_list.front().get();
+
+ // Copy request has been executed earlier, so texture should be drawn.
+ EXPECT_EQ(cc::DrawQuad::TEXTURE_CONTENT,
+ render_pass->quad_list.front()->material);
+
+ aggregator_->set_output_is_secure(false);
+
+ frame = aggregator_->Aggregate(surface2_id);
+ render_pass = frame.render_pass_list.back().get();
+
+ // Output is insecure, so texture should be drawn.
+ EXPECT_EQ(cc::DrawQuad::SOLID_COLOR, render_pass->quad_list.back()->material);
+
+ support1->EvictCurrentSurface();
+ support2->EvictCurrentSurface();
+}
+
+// Ensure that the render passes have correct color spaces.
+TEST_F(SurfaceAggregatorValidSurfaceTest, ColorSpaceTest) {
+ Quad quads[][2] = {{Quad::SolidColorQuad(SK_ColorWHITE),
+ Quad::SolidColorQuad(SK_ColorLTGRAY)},
+ {Quad::SolidColorQuad(SK_ColorGRAY),
+ Quad::SolidColorQuad(SK_ColorDKGRAY)}};
+ Pass passes[] = {Pass(quads[0], arraysize(quads[0]), 2),
+ Pass(quads[1], arraysize(quads[1]), 1)};
+ gfx::ColorSpace color_space1 = gfx::ColorSpace::CreateXYZD50();
+ gfx::ColorSpace color_space2 = gfx::ColorSpace::CreateSRGB();
+ gfx::ColorSpace color_space3 = gfx::ColorSpace::CreateSCRGBLinear();
+
+ SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ root_local_surface_id_);
+
+ SurfaceId surface_id(support_->frame_sink_id(), root_local_surface_id_);
+
+ cc::CompositorFrame aggregated_frame;
+ aggregator_.SetOutputColorSpace(color_space1, color_space1);
+ aggregated_frame = aggregator_.Aggregate(surface_id);
+ EXPECT_EQ(2u, aggregated_frame.render_pass_list.size());
+ EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[0]->color_space);
+ EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[1]->color_space);
+
+ aggregator_.SetOutputColorSpace(color_space2, color_space2);
+ aggregated_frame = aggregator_.Aggregate(surface_id);
+ EXPECT_EQ(2u, aggregated_frame.render_pass_list.size());
+ EXPECT_EQ(color_space2, aggregated_frame.render_pass_list[0]->color_space);
+ EXPECT_EQ(color_space2, aggregated_frame.render_pass_list[1]->color_space);
+
+ aggregator_.SetOutputColorSpace(color_space1, color_space3);
+ aggregated_frame = aggregator_.Aggregate(surface_id);
+ EXPECT_EQ(3u, aggregated_frame.render_pass_list.size());
+ EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[0]->color_space);
+ EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[1]->color_space);
+ EXPECT_EQ(color_space3, aggregated_frame.render_pass_list[2]->color_space);
+}
+
+// Tests that has_damage_from_contributing_content is aggregated correctly from
+// child surface quads.
+TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
+ Quad child_surface_quads[] = {Quad::RenderPassQuad(1)};
+ Pass child_surface_passes[] = {
+ Pass(child_surface_quads, arraysize(child_surface_quads), 1)};
+
+ cc::CompositorFrame child_surface_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_surface_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_surface_passes, arraysize(child_surface_passes));
+
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_surface_frame));
+
+ Quad root_surface_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ Pass root_passes[] = {
+ Pass(root_surface_quads, arraysize(root_surface_quads), 1)};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ // On first frame there is no existing cache texture to worry about re-using,
+ // so we don't worry what this bool is set to.
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ // No Surface changed, so no damage should be given.
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ EXPECT_FALSE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+
+ // Change child_frame with damage should set the flag.
+ {
+ cc::CompositorFrame child_surface_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_surface_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_surface_passes, arraysize(child_surface_passes));
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_surface_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ // True for new child_frame with damage.
+ EXPECT_TRUE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+
+ // Change child_frame without damage should not set the flag.
+ {
+ cc::CompositorFrame child_surface_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_surface_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_surface_passes, arraysize(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));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ // False for new child_frame without damage.
+ EXPECT_FALSE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+}
+
+// Tests that has_damage_from_contributing_content is aggregated correctly from
+// grand child surface quads.
+TEST_F(SurfaceAggregatorValidSurfaceTest,
+ HasDamageByChangingGrandChildSurface) {
+ auto grand_child_support = CompositorFrameSinkSupport::Create(
+ nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+
+ Quad child_surface_quads[] = {Quad::RenderPassQuad(1)};
+ Pass child_surface_passes[] = {
+ Pass(child_surface_quads, arraysize(child_surface_quads), 1)};
+
+ cc::CompositorFrame child_surface_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_surface_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_surface_passes, arraysize(child_surface_passes));
+
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_surface_frame));
+
+ Quad root_surface_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ Pass root_passes[] = {
+ Pass(root_surface_quads, arraysize(root_surface_quads), 1)};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ // On first frame there is no existing cache texture to worry about re-using,
+ // so we don't worry what this bool is set to.
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ // No Surface changed, so no damage should be given.
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ EXPECT_FALSE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+
+ // 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)};
+ LocalSurfaceId grand_child_local_surface_id = allocator_.GenerateId();
+ SurfaceId grand_child_surface_id(grand_child_support->frame_sink_id(),
+ grand_child_local_surface_id);
+ {
+ cc::CompositorFrame grand_child_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&grand_child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ grand_child_passes, arraysize(grand_child_passes));
+
+ grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
+ std::move(grand_child_frame));
+
+ Quad new_child_surface_quads[] = {
+ child_surface_quads[0],
+ Quad::SurfaceQuad(grand_child_surface_id, InvalidSurfaceId(), 1.f)};
+ Pass new_child_surface_passes[] = {
+ Pass(new_child_surface_quads, arraysize(new_child_surface_quads), 1)};
+ child_surface_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_surface_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ new_child_surface_passes, arraysize(new_child_surface_passes));
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_surface_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ // True for new grand_child_frame.
+ EXPECT_TRUE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+
+ // No Surface changed, so no damage should be given.
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ EXPECT_FALSE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+
+ // Change grand_child_frame with damage should set the flag.
+ {
+ cc::CompositorFrame grand_child_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&grand_child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ grand_child_passes, arraysize(grand_child_passes));
+ grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
+ std::move(grand_child_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ // True for new grand_child_frame with damage.
+ EXPECT_TRUE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+
+ // Change grand_child_frame without damage should not set the flag.
+ {
+ cc::CompositorFrame grand_child_frame =
+ cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&grand_child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ grand_child_passes, arraysize(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));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ // False for new grand_child_frame without damage.
+ EXPECT_FALSE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ }
+
+ grand_child_support->EvictCurrentSurface();
+}
+
+// Tests that has_damage_from_contributing_content is aggregated correctly from
+// render pass quads.
+TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageFromRenderPassQuads) {
+ Quad child_quads[] = {Quad::RenderPassQuad(1)};
+ Pass child_passes[] = {Pass(child_quads, arraysize(child_quads), 1)};
+
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(child_passes));
+
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+
+ Quad root_surface_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+ Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
+
+ Pass root_passes[] = {
+ Pass(root_surface_quads, arraysize(root_surface_quads), 1),
+ Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ // On first frame there is no existing cache texture to worry about re-using,
+ // so we don't worry what this bool is set to.
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ // No Surface changed, so no damage should be given.
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ EXPECT_FALSE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ EXPECT_FALSE(aggregated_frame.render_pass_list[1]
+ ->has_damage_from_contributing_content);
+ }
+
+ // Changing child_frame should damage both render_pass.
+ {
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(child_passes));
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ // True for new child_frame.
+ EXPECT_TRUE(aggregated_frame.render_pass_list[0]
+ ->has_damage_from_contributing_content);
+ EXPECT_TRUE(aggregated_frame.render_pass_list[1]
+ ->has_damage_from_contributing_content);
+ }
+}
+
+// Tests that the first frame damage_rect of a cached render pass should be
+// fully damaged.
+TEST_F(SurfaceAggregatorValidSurfaceTest, DamageRectOfCachedRenderPass) {
+ int pass_id[] = {1, 2};
+ Quad root_quads[][1] = {
+ {Quad::SolidColorQuad(SK_ColorGREEN)}, {Quad::RenderPassQuad(pass_id[0])},
+ };
+ Pass root_passes[] = {
+ Pass(root_quads[0], arraysize(root_quads[0]), pass_id[0]),
+ Pass(root_quads[1], arraysize(root_quads[1]), pass_id[1])};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ // The root surface was enqueued without being aggregated once, so it should
+ // be treated as completely damaged.
+ EXPECT_TRUE(
+ aggregated_pass_list[0]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+ EXPECT_TRUE(
+ aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+
+ // For offscreen render pass, only the visible area is damaged.
+ {
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ root_passes, arraysize(root_passes));
+
+ auto* nonroot_pass = root_frame.render_pass_list[0].get();
+ nonroot_pass->transform_to_root_target.Translate(8, 0);
+
+ gfx::Rect root_pass_damage = gfx::Rect(0, 0, 10, 10);
+ auto* root_pass = root_frame.render_pass_list[1].get();
+ root_pass->damage_rect = root_pass_damage;
+ auto* root_pass_sqs = root_pass->shared_quad_state_list.front();
+ root_pass_sqs->quad_to_target_transform.Translate(8, 0);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ // Only the visible area is damaged.
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 10), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(root_pass_damage, aggregated_pass_list[1]->damage_rect);
+ }
+
+ // For offscreen cached render pass, should have full damage.
+ {
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ root_passes, arraysize(root_passes));
+
+ auto* nonroot_pass = root_frame.render_pass_list[0].get();
+ nonroot_pass->transform_to_root_target.Translate(8, 0);
+ nonroot_pass->cache_render_pass = true;
+
+ gfx::Rect root_pass_damage = gfx::Rect(0, 0, 10, 10);
+ auto* root_pass = root_frame.render_pass_list[1].get();
+ root_pass->damage_rect = root_pass_damage;
+ auto* root_pass_sqs = root_pass->shared_quad_state_list.front();
+ root_pass_sqs->quad_to_target_transform.Translate(8, 0);
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ // Should have full damage.
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(root_pass_damage, aggregated_pass_list[1]->damage_rect);
+ }
+}
+
+// Tests that the first frame damage_rect of cached render pass of a child
+// surface should be fully damaged.
+TEST_F(SurfaceAggregatorValidSurfaceTest,
+ DamageRectOfCachedRenderPassInChildSurface) {
+ int pass_id[] = {1, 2};
+ Quad child_quads[][1] = {
+ {Quad::SolidColorQuad(SK_ColorGREEN)}, {Quad::RenderPassQuad(pass_id[0])},
+ };
+ Pass child_passes[] = {
+ Pass(child_quads[0], arraysize(child_quads[0]), pass_id[0]),
+ Pass(child_quads[1], arraysize(child_quads[1]), pass_id[1])};
+
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(child_passes));
+
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+
+ Quad root_surface_quads[] = {
+ Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)};
+
+ Pass root_passes[] = {
+ Pass(root_surface_quads, arraysize(root_surface_quads), 1)};
+
+ cc::CompositorFrame root_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ support_->SubmitCompositorFrame(root_local_surface_id_,
+ std::move(root_frame));
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(2u, aggregated_pass_list.size());
+
+ // The root surface was enqueued without being aggregated once, so it should
+ // be treated as completely damaged.
+ EXPECT_TRUE(
+ aggregated_pass_list[0]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+ EXPECT_TRUE(
+ aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+
+ // For offscreen render pass, only the visible area is damaged.
+ {
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(child_passes));
+
+ auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
+ child_nonroot_pass->transform_to_root_target.Translate(8, 0);
+
+ gfx::Rect child_root_pass_damage = gfx::Rect(0, 0, 10, 10);
+ auto* child_root_pass = child_frame.render_pass_list[1].get();
+ child_root_pass->damage_rect = child_root_pass_damage;
+ auto* child_root_pass_sqs = child_root_pass->shared_quad_state_list.front();
+ child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
+
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ // Only the visible area is damaged.
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 10), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(child_root_pass_damage, aggregated_pass_list[1]->damage_rect);
+ }
+
+ // For offscreen cached render pass, should have full damage.
+ {
+ cc::CompositorFrame child_frame = cc::test::MakeEmptyCompositorFrame();
+ AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
+ child_passes, arraysize(child_passes));
+
+ auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
+ child_nonroot_pass->transform_to_root_target.Translate(8, 0);
+ child_nonroot_pass->cache_render_pass = true;
+
+ gfx::Rect child_root_pass_damage = gfx::Rect(0, 0, 10, 10);
+ auto* child_root_pass = child_frame.render_pass_list[1].get();
+ child_root_pass->damage_rect = child_root_pass_damage;
+ auto* child_root_pass_sqs = child_root_pass->shared_quad_state_list.front();
+ child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
+
+ child_support_->SubmitCompositorFrame(child_local_surface_id,
+ std::move(child_frame));
+
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ // Should have full damage.
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(child_root_pass_damage, aggregated_pass_list[1]->damage_rect);
+ }
+}
+
+// Tests that quads outside the damage rect are not ignored for cached render
+// pass.
+TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
+ LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
+ SurfaceId child_surface_id(child_support_->frame_sink_id(),
+ child_local_surface_id);
+ // The child surface has two quads, one with a visible rect of 15,15 6x6 and
+ // the other other with a visible rect of 10,10 2x2 (relative to root target
+ // space).
+ {
+ int pass_id[] = {1, 2};
+ Quad child_quads[][1] = {
+ {Quad::SolidColorQuad(SK_ColorGREEN)},
+ {Quad::RenderPassQuad(pass_id[0])},
+ };
+ Pass child_passes[] = {
+ Pass(child_quads[0], arraysize(child_quads[0]), pass_id[0]),
+ Pass(child_quads[1], arraysize(child_quads[1]), pass_id[1])};
+
+ cc::RenderPassList child_pass_list;
+ AddPasses(&child_pass_list, gfx::Rect(SurfaceSize()), child_passes,
+ arraysize(child_passes));
+
+ child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(1, 1, 3, 3);
+ auto* child_sqs = child_pass_list[0]->shared_quad_state_list.ElementAt(0u);
+ child_sqs->quad_to_target_transform.Translate(3, 3);
+ child_sqs->quad_to_target_transform.Scale(2, 2);
+
+ child_pass_list[0]->cache_render_pass = true;
+
+ child_pass_list[1]->quad_list.ElementAt(0)->visible_rect =
+ gfx::Rect(0, 0, 2, 2);
+
+ SubmitPassListAsFrame(child_support_.get(), child_local_surface_id,
+ &child_pass_list);
+ }
+
+ {
+ int pass_id[] = {1, 2};
+ Quad root_quads[][1] = {
+ {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)},
+ {Quad::RenderPassQuad(pass_id[0])},
+ };
+ Pass root_passes[] = {
+ Pass(root_quads[0], arraysize(root_quads[0]), pass_id[0]),
+ Pass(root_quads[1], arraysize(root_quads[1]), pass_id[1])};
+
+ cc::RenderPassList root_pass_list;
+ AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ auto* root_pass = root_pass_list[1].get();
+ root_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ root_pass->damage_rect = gfx::Rect(0, 0, 1, 1);
+
+ SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
+ &root_pass_list);
+ }
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ cc::CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // Damage rect for first aggregation should contain entire root surface.
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+
+ // Test should not ignore outside for cached render pass.
+ // Create a root surface with a smaller damage rect.
+ {
+ int pass_id[] = {1, 2};
+ Quad root_quads[][1] = {
+ {Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), 1.f)},
+ {Quad::RenderPassQuad(pass_id[0])},
+ };
+ Pass root_passes[] = {
+ Pass(root_quads[0], arraysize(root_quads[0]), pass_id[0]),
+ Pass(root_quads[1], arraysize(root_quads[1]), pass_id[1])};
+
+ cc::RenderPassList root_pass_list;
+ AddPasses(&root_pass_list, gfx::Rect(SurfaceSize()), root_passes,
+ arraysize(root_passes));
+
+ auto* root_pass = root_pass_list[1].get();
+ root_pass->shared_quad_state_list.front()
+ ->quad_to_target_transform.Translate(10, 10);
+ root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
+ SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
+ &root_pass_list);
+ }
+
+ {
+ cc::CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id);
+ const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+
+ ASSERT_EQ(3u, aggregated_pass_list.size());
+
+ // The first quad is a cached render pass, should be included and fully
+ // damaged.
+ EXPECT_EQ(gfx::Rect(1, 1, 3, 3),
+ aggregated_pass_list[0]->quad_list.back()->visible_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 2, 2),
+ aggregated_pass_list[1]->quad_list.back()->visible_rect);
+ EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[1]->damage_rect);
+ EXPECT_EQ(gfx::Rect(10, 10, 2, 2), aggregated_pass_list[2]->damage_rect);
+ EXPECT_EQ(1u, aggregated_pass_list[0]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[1]->quad_list.size());
+ EXPECT_EQ(1u, aggregated_pass_list[2]->quad_list.size());
+ }
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/DEPS b/chromium/components/viz/service/display_embedder/DEPS
new file mode 100644
index 00000000000..f97559d9f01
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/DEPS
@@ -0,0 +1,32 @@
+include_rules = [
+ "+cc/base",
+ "+cc/ipc",
+ "+cc/output",
+ "+cc/resources",
+ "+cc/scheduler",
+ "+cc/surfaces",
+ "+gpu/GLES2",
+ "+gpu/command_buffer/client",
+ "+gpu/command_buffer/common",
+ "+gpu/command_buffer/service",
+ "+gpu/ipc/client",
+ "+gpu/ipc/common",
+ "+gpu/ipc/in_process_command_buffer.h",
+ "+gpu/ipc/service",
+ "+mojo/public/cpp/bindings",
+ "+mojo/public/cpp/system",
+ "+third_party/skia",
+ "+ui/display",
+ "+ui/gfx",
+ "+ui/gfx/geometry",
+ "+ui/gl",
+ "+ui/latency",
+ "+ui/ozone/public",
+]
+
+specific_include_rules = {
+ ".*_unittest\.cc": [
+ "+cc/test",
+ "+third_party/khronos/GLES2",
+ ],
+}
diff --git a/chromium/components/viz/display_compositor/OWNERS b/chromium/components/viz/service/display_embedder/OWNERS
index 68de279ade5..68de279ade5 100644
--- a/chromium/components/viz/display_compositor/OWNERS
+++ b/chromium/components/viz/service/display_embedder/OWNERS
diff --git a/chromium/components/viz/service/display_embedder/README.md b/chromium/components/viz/service/display_embedder/README.md
new file mode 100644
index 00000000000..f7f83c6f8a4
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/README.md
@@ -0,0 +1,6 @@
+# display_embedder
+
+This directory contains the implementation used to embed a Display Compositor
+for a specific platform. That includes code for managing presentation of the
+Display Compositor's output to the user.
+
diff --git a/chromium/components/viz/service/display_embedder/buffer_queue.cc b/chromium/components/viz/service/display_embedder/buffer_queue.cc
new file mode 100644
index 00000000000..8c59cf2f5e9
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/buffer_queue.cc
@@ -0,0 +1,310 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/buffer_queue.h"
+
+#include "base/containers/adapters.h"
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "components/viz/common/gl_helper.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/gfx/skia_util.h"
+
+namespace viz {
+
+BufferQueue::BufferQueue(gpu::gles2::GLES2Interface* gl,
+ uint32_t texture_target,
+ uint32_t internal_format,
+ gfx::BufferFormat format,
+ GLHelper* gl_helper,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gpu::SurfaceHandle surface_handle)
+ : gl_(gl),
+ fbo_(0),
+ allocated_count_(0),
+ texture_target_(texture_target),
+ internal_format_(internal_format),
+ format_(format),
+ gl_helper_(gl_helper),
+ gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+ surface_handle_(surface_handle) {
+ DCHECK(gpu::IsImageFormatCompatibleWithGpuMemoryBufferFormat(internal_format,
+ format_));
+}
+
+BufferQueue::~BufferQueue() {
+ FreeAllSurfaces();
+
+ if (fbo_)
+ gl_->DeleteFramebuffers(1, &fbo_);
+}
+
+void BufferQueue::Initialize() {
+ gl_->GenFramebuffers(1, &fbo_);
+}
+
+void BufferQueue::BindFramebuffer() {
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+
+ if (!current_surface_)
+ current_surface_ = GetNextSurface();
+
+ if (current_surface_) {
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, current_surface_->texture, 0);
+ if (current_surface_->stencil) {
+ gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, current_surface_->stencil);
+ }
+ }
+}
+
+void BufferQueue::CopyBufferDamage(int texture,
+ int source_texture,
+ const gfx::Rect& new_damage,
+ const gfx::Rect& old_damage) {
+ gl_helper_->CopySubBufferDamage(texture_target_, texture, source_texture,
+ SkRegion(gfx::RectToSkIRect(new_damage)),
+ SkRegion(gfx::RectToSkIRect(old_damage)));
+}
+
+void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) {
+ if (displayed_surface_)
+ displayed_surface_->damage.Union(damage);
+ for (auto& surface : available_surfaces_)
+ surface->damage.Union(damage);
+ for (auto& surface : in_flight_surfaces_) {
+ if (surface)
+ surface->damage.Union(damage);
+ }
+}
+
+void BufferQueue::SwapBuffers(const gfx::Rect& damage) {
+ if (damage.IsEmpty()) {
+ in_flight_surfaces_.push_back(nullptr);
+ return;
+ }
+
+ if (current_surface_) {
+ if (damage != gfx::Rect(size_)) {
+ // Copy damage from the most recently swapped buffer. In the event that
+ // the buffer was destroyed and failed to recreate, pick from the most
+ // recently available buffer.
+ uint32_t texture_id = 0;
+ for (auto& surface : base::Reversed(in_flight_surfaces_)) {
+ if (surface) {
+ texture_id = surface->texture;
+ break;
+ }
+ }
+ if (!texture_id && displayed_surface_)
+ texture_id = displayed_surface_->texture;
+
+ if (texture_id) {
+ CopyBufferDamage(current_surface_->texture, texture_id, damage,
+ current_surface_->damage);
+ }
+ }
+ current_surface_->damage = gfx::Rect();
+ }
+ UpdateBufferDamage(damage);
+ in_flight_surfaces_.push_back(std::move(current_surface_));
+ // Some things reset the framebuffer (CopySubBufferDamage, some GLRenderer
+ // paths), so ensure we restore it here.
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+}
+
+void BufferQueue::Reshape(const gfx::Size& size,
+ float scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool use_stencil) {
+ if (size == size_ && color_space == color_space_ &&
+ use_stencil == use_stencil_)
+ return;
+#if !defined(OS_MACOSX)
+ // TODO(ccameron): This assert is being hit on Mac try jobs. Determine if that
+ // is cause for concern or if it is benign.
+ // http://crbug.com/524624
+ DCHECK(!current_surface_);
+#endif
+ size_ = size;
+ color_space_ = color_space;
+ use_stencil_ = use_stencil;
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, 0, 0);
+ gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, 0);
+
+ FreeAllSurfaces();
+}
+
+void BufferQueue::RecreateBuffers() {
+ // We need to recreate the buffers, for whatever reason the old ones are not
+ // presentable on the device anymore.
+ // Unused buffers can be freed directly, they will be re-allocated as needed.
+ // Any in flight, current or displayed surface must be replaced.
+ available_surfaces_.clear();
+
+ // Recreate all in-flight surfaces and put the recreated copies in the queue.
+ for (auto& surface : in_flight_surfaces_)
+ surface = RecreateBuffer(std::move(surface));
+
+ current_surface_ = RecreateBuffer(std::move(current_surface_));
+ displayed_surface_ = RecreateBuffer(std::move(displayed_surface_));
+
+ if (current_surface_) {
+ // If we have a texture bound, we will need to re-bind it.
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, current_surface_->texture, 0);
+ }
+}
+
+std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::RecreateBuffer(
+ std::unique_ptr<AllocatedSurface> surface) {
+ if (!surface)
+ return nullptr;
+
+ std::unique_ptr<AllocatedSurface> new_surface(GetNextSurface());
+ if (!new_surface)
+ return nullptr;
+
+ new_surface->damage = surface->damage;
+
+ // Copy the entire texture.
+ CopyBufferDamage(new_surface->texture, surface->texture, gfx::Rect(),
+ gfx::Rect(size_));
+ return new_surface;
+}
+
+void BufferQueue::PageFlipComplete() {
+ DCHECK(!in_flight_surfaces_.empty());
+ if (in_flight_surfaces_.front()) {
+ if (displayed_surface_)
+ available_surfaces_.push_back(std::move(displayed_surface_));
+ displayed_surface_ = std::move(in_flight_surfaces_.front());
+ }
+
+ in_flight_surfaces_.pop_front();
+}
+
+uint32_t BufferQueue::GetCurrentTextureId() const {
+ if (current_surface_)
+ return current_surface_->texture;
+
+ // Return in-flight or displayed surface texture if no surface is
+ // currently bound. This can happen when using overlays and surface
+ // damage is empty. Note: |in_flight_surfaces_| entries can be null
+ // as a result of calling FreeAllSurfaces().
+ for (auto& surface : base::Reversed(in_flight_surfaces_)) {
+ if (surface)
+ return surface->texture;
+ }
+
+ if (displayed_surface_)
+ return displayed_surface_->texture;
+
+ return 0;
+}
+
+void BufferQueue::FreeAllSurfaces() {
+ displayed_surface_.reset();
+ current_surface_.reset();
+ // This is intentionally not emptied since the swap buffers acks are still
+ // expected to arrive.
+ for (auto& surface : in_flight_surfaces_)
+ surface = nullptr;
+ available_surfaces_.clear();
+}
+
+void BufferQueue::FreeSurfaceResources(AllocatedSurface* surface) {
+ if (!surface->texture)
+ return;
+
+ gl_->BindTexture(texture_target_, surface->texture);
+ gl_->ReleaseTexImage2DCHROMIUM(texture_target_, surface->image);
+ gl_->DeleteTextures(1, &surface->texture);
+ gl_->DestroyImageCHROMIUM(surface->image);
+ if (surface->stencil)
+ gl_->DeleteRenderbuffers(1, &surface->stencil);
+ surface->buffer.reset();
+ allocated_count_--;
+}
+
+std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface() {
+ if (!available_surfaces_.empty()) {
+ std::unique_ptr<AllocatedSurface> surface =
+ std::move(available_surfaces_.back());
+ available_surfaces_.pop_back();
+ return surface;
+ }
+
+ GLuint texture;
+ gl_->GenTextures(1, &texture);
+
+ GLuint stencil = 0;
+ if (use_stencil_) {
+ gl_->GenRenderbuffers(1, &stencil);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, stencil);
+ gl_->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size_.width(),
+ size_.height());
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, 0);
+ }
+
+ // We don't want to allow anything more than triple buffering.
+ DCHECK_LT(allocated_count_, 4U);
+ std::unique_ptr<gfx::GpuMemoryBuffer> buffer(
+ gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
+ size_, format_, gfx::BufferUsage::SCANOUT, surface_handle_));
+ if (!buffer.get()) {
+ gl_->DeleteTextures(1, &texture);
+ DLOG(ERROR) << "Failed to allocate GPU memory buffer";
+ return nullptr;
+ }
+ buffer->SetColorSpaceForScanout(color_space_);
+
+ uint32_t id =
+ gl_->CreateImageCHROMIUM(buffer->AsClientBuffer(), size_.width(),
+ size_.height(), internal_format_);
+ if (!id) {
+ LOG(ERROR) << "Failed to allocate backing image surface";
+ gl_->DeleteTextures(1, &texture);
+ return nullptr;
+ }
+
+ allocated_count_++;
+ gl_->BindTexture(texture_target_, texture);
+ gl_->BindTexImage2DCHROMIUM(texture_target_, id);
+ return base::MakeUnique<AllocatedSurface>(this, std::move(buffer), texture,
+ id, stencil, gfx::Rect(size_));
+}
+
+BufferQueue::AllocatedSurface::AllocatedSurface(
+ BufferQueue* buffer_queue,
+ std::unique_ptr<gfx::GpuMemoryBuffer> buffer,
+ uint32_t texture,
+ uint32_t image,
+ uint32_t stencil,
+ const gfx::Rect& rect)
+ : buffer_queue(buffer_queue),
+ buffer(buffer.release()),
+ texture(texture),
+ image(image),
+ stencil(stencil),
+ damage(rect) {}
+
+BufferQueue::AllocatedSurface::~AllocatedSurface() {
+ buffer_queue->FreeSurfaceResources(this);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/buffer_queue.h b/chromium/components/viz/service/display_embedder/buffer_queue.h
new file mode 100644
index 00000000000..2ab4915857f
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/buffer_queue.h
@@ -0,0 +1,137 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_BUFFER_QUEUE_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_BUFFER_QUEUE_H_
+
+#include <stddef.h>
+
+#include <deque>
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/viz/service/viz_service_export.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+class GpuMemoryBuffer;
+}
+
+namespace gpu {
+class GpuMemoryBufferManager;
+
+namespace gles2 {
+class GLES2Interface;
+}
+} // namespace gpu
+
+namespace viz {
+
+class GLHelper;
+
+// Provides a surface that manages its own buffers, backed by GpuMemoryBuffers
+// created using CHROMIUM_image. Double/triple buffering is implemented
+// internally. Doublebuffering occurs if PageFlipComplete is called before the
+// next BindFramebuffer call, otherwise it creates extra buffers.
+class VIZ_SERVICE_EXPORT BufferQueue {
+ public:
+ BufferQueue(gpu::gles2::GLES2Interface* gl,
+ uint32_t texture_target,
+ uint32_t internal_format,
+ gfx::BufferFormat format,
+ GLHelper* gl_helper,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gpu::SurfaceHandle surface_handle);
+ virtual ~BufferQueue();
+
+ void Initialize();
+
+ void BindFramebuffer();
+ void SwapBuffers(const gfx::Rect& damage);
+ void PageFlipComplete();
+ void Reshape(const gfx::Size& size,
+ float scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool use_stencil);
+ void RecreateBuffers();
+ uint32_t GetCurrentTextureId() const;
+
+ uint32_t fbo() const { return fbo_; }
+ uint32_t internal_format() const { return internal_format_; }
+ gfx::BufferFormat buffer_format() const { return format_; }
+
+ private:
+ friend class BufferQueueTest;
+ friend class AllocatedSurface;
+
+ struct VIZ_SERVICE_EXPORT AllocatedSurface {
+ AllocatedSurface(BufferQueue* buffer_queue,
+ std::unique_ptr<gfx::GpuMemoryBuffer> buffer,
+ uint32_t texture,
+ uint32_t image,
+ uint32_t stencil,
+ const gfx::Rect& rect);
+ ~AllocatedSurface();
+ BufferQueue* const buffer_queue;
+ std::unique_ptr<gfx::GpuMemoryBuffer> buffer;
+ const uint32_t texture;
+ const uint32_t image;
+ const uint32_t stencil;
+ gfx::Rect damage; // This is the damage for this frame from the previous.
+ };
+
+ void FreeAllSurfaces();
+
+ void FreeSurfaceResources(AllocatedSurface* surface);
+
+ // Copy everything that is in |copy_rect|, except for what is in
+ // |exclude_rect| from |source_texture| to |texture|.
+ virtual void CopyBufferDamage(int texture,
+ int source_texture,
+ const gfx::Rect& new_damage,
+ const gfx::Rect& old_damage);
+
+ void UpdateBufferDamage(const gfx::Rect& damage);
+
+ // Return a surface, available to be drawn into.
+ std::unique_ptr<AllocatedSurface> GetNextSurface();
+
+ std::unique_ptr<AllocatedSurface> RecreateBuffer(
+ std::unique_ptr<AllocatedSurface> surface);
+
+ gpu::gles2::GLES2Interface* const gl_;
+ gfx::Size size_;
+ gfx::ColorSpace color_space_;
+ bool use_stencil_ = false;
+ uint32_t fbo_;
+ size_t allocated_count_;
+ uint32_t texture_target_;
+ uint32_t internal_format_;
+ gfx::BufferFormat format_;
+ // This surface is currently bound. This may be nullptr if no surface has
+ // been bound, or if allocation failed at bind.
+ std::unique_ptr<AllocatedSurface> current_surface_;
+ // The surface currently on the screen, if any.
+ std::unique_ptr<AllocatedSurface> displayed_surface_;
+ // These are free for use, and are not nullptr.
+ std::vector<std::unique_ptr<AllocatedSurface>> available_surfaces_;
+ // These have been swapped but are not displayed yet. Entries of this deque
+ // may be nullptr, if they represent frames that have been destroyed.
+ std::deque<std::unique_ptr<AllocatedSurface>> in_flight_surfaces_;
+ GLHelper* gl_helper_;
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
+ gpu::SurfaceHandle surface_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferQueue);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_BUFFER_QUEUE_H_
diff --git a/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc b/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc
new file mode 100644
index 00000000000..5f2179029fb
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc
@@ -0,0 +1,704 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/buffer_queue.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <set>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "cc/test/test_context_provider.h"
+#include "cc/test/test_gpu_memory_buffer_manager.h"
+#include "cc/test/test_web_graphics_context_3d.h"
+#include "components/viz/common/gl_helper.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "ui/display/types/display_snapshot.h"
+
+using ::testing::_;
+using ::testing::Expectation;
+using ::testing::Ne;
+using ::testing::Return;
+
+namespace viz {
+
+class StubGpuMemoryBufferImpl : public gfx::GpuMemoryBuffer {
+ public:
+ explicit StubGpuMemoryBufferImpl(size_t* set_color_space_count)
+ : set_color_space_count_(set_color_space_count) {}
+
+ // Overridden from gfx::GpuMemoryBuffer:
+ bool Map() override { return false; }
+ void* memory(size_t plane) override { return nullptr; }
+ void Unmap() override {}
+ gfx::Size GetSize() const override { return gfx::Size(); }
+ gfx::BufferFormat GetFormat() const override {
+ return gfx::BufferFormat::BGRX_8888;
+ }
+ int stride(size_t plane) const override { return 0; }
+ gfx::GpuMemoryBufferId GetId() const override {
+ return gfx::GpuMemoryBufferId(0);
+ }
+ void SetColorSpaceForScanout(const gfx::ColorSpace& color_space) override {
+ *set_color_space_count_ += 1;
+ }
+ gfx::GpuMemoryBufferHandle GetHandle() const override {
+ return gfx::GpuMemoryBufferHandle();
+ }
+ ClientBuffer AsClientBuffer() override {
+ return reinterpret_cast<ClientBuffer>(this);
+ }
+
+ size_t* set_color_space_count_;
+};
+
+class StubGpuMemoryBufferManager : public cc::TestGpuMemoryBufferManager {
+ public:
+ StubGpuMemoryBufferManager() : allocate_succeeds_(true) {}
+
+ size_t set_color_space_count() const { return set_color_space_count_; }
+
+ void set_allocate_succeeds(bool value) { allocate_succeeds_ = value; }
+
+ std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) override {
+ if (!surface_handle) {
+ return TestGpuMemoryBufferManager::CreateGpuMemoryBuffer(
+ size, format, usage, surface_handle);
+ }
+ if (allocate_succeeds_)
+ return base::WrapUnique<gfx::GpuMemoryBuffer>(
+ new StubGpuMemoryBufferImpl(&set_color_space_count_));
+ return nullptr;
+ }
+
+ private:
+ bool allocate_succeeds_;
+ size_t set_color_space_count_ = 0;
+};
+
+#if defined(OS_WIN)
+const gpu::SurfaceHandle kFakeSurfaceHandle =
+ reinterpret_cast<gpu::SurfaceHandle>(1);
+#else
+const gpu::SurfaceHandle kFakeSurfaceHandle = 1;
+#endif
+
+class MockBufferQueue : public BufferQueue {
+ public:
+ MockBufferQueue(gpu::gles2::GLES2Interface* gl,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ unsigned int target,
+ unsigned int internalformat)
+ : BufferQueue(gl,
+ target,
+ internalformat,
+ display::DisplaySnapshot::PrimaryFormat(),
+ nullptr,
+ gpu_memory_buffer_manager,
+ kFakeSurfaceHandle) {}
+ MOCK_METHOD4(CopyBufferDamage,
+ void(int, int, const gfx::Rect&, const gfx::Rect&));
+};
+
+class BufferQueueTest : public ::testing::Test {
+ public:
+ BufferQueueTest() : doublebuffering_(true), first_frame_(true) {}
+
+ void SetUp() override {
+ InitWithContext(cc::TestWebGraphicsContext3D::Create());
+ }
+
+ void InitWithContext(std::unique_ptr<cc::TestWebGraphicsContext3D> context) {
+ context_provider_ = cc::TestContextProvider::Create(std::move(context));
+ context_provider_->BindToCurrentThread();
+ gpu_memory_buffer_manager_.reset(new StubGpuMemoryBufferManager);
+ mock_output_surface_ = new MockBufferQueue(context_provider_->ContextGL(),
+ gpu_memory_buffer_manager_.get(),
+ GL_TEXTURE_2D, GL_RGB);
+ output_surface_.reset(mock_output_surface_);
+ output_surface_->Initialize();
+ }
+
+ unsigned current_surface() {
+ return output_surface_->current_surface_
+ ? output_surface_->current_surface_->image
+ : 0;
+ }
+ const std::vector<std::unique_ptr<BufferQueue::AllocatedSurface>>&
+ available_surfaces() {
+ return output_surface_->available_surfaces_;
+ }
+ std::deque<std::unique_ptr<BufferQueue::AllocatedSurface>>&
+ in_flight_surfaces() {
+ return output_surface_->in_flight_surfaces_;
+ }
+
+ const BufferQueue::AllocatedSurface* displayed_frame() {
+ return output_surface_->displayed_surface_.get();
+ }
+ const BufferQueue::AllocatedSurface* current_frame() {
+ return output_surface_->current_surface_.get();
+ }
+ const BufferQueue::AllocatedSurface* next_frame() {
+ return output_surface_->available_surfaces_.back().get();
+ }
+ const gfx::Size size() { return output_surface_->size_; }
+
+ int CountBuffers() {
+ int n = available_surfaces().size() + in_flight_surfaces().size() +
+ (displayed_frame() ? 1 : 0);
+ if (current_surface())
+ n++;
+ return n;
+ }
+
+ // Check that each buffer is unique if present.
+ void CheckUnique() {
+ std::set<unsigned> buffers;
+ EXPECT_TRUE(InsertUnique(&buffers, current_surface()));
+ if (displayed_frame())
+ EXPECT_TRUE(InsertUnique(&buffers, displayed_frame()->image));
+ for (auto& surface : available_surfaces())
+ EXPECT_TRUE(InsertUnique(&buffers, surface->image));
+ for (auto& surface : in_flight_surfaces()) {
+ if (surface)
+ EXPECT_TRUE(InsertUnique(&buffers, surface->image));
+ }
+ }
+
+ void SwapBuffers() {
+ output_surface_->SwapBuffers(gfx::Rect(output_surface_->size_));
+ }
+
+ void SendDamagedFrame(const gfx::Rect& damage) {
+ // We don't care about the GL-level implementation here, just how it uses
+ // damage rects.
+ output_surface_->BindFramebuffer();
+ output_surface_->SwapBuffers(damage);
+ if (doublebuffering_ || !first_frame_)
+ output_surface_->PageFlipComplete();
+ first_frame_ = false;
+ }
+
+ void SendFullFrame() { SendDamagedFrame(gfx::Rect(output_surface_->size_)); }
+
+ protected:
+ bool InsertUnique(std::set<unsigned>* set, unsigned value) {
+ if (!value)
+ return true;
+ if (set->find(value) != set->end())
+ return false;
+ set->insert(value);
+ return true;
+ }
+
+ scoped_refptr<cc::TestContextProvider> context_provider_;
+ std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager_;
+ std::unique_ptr<BufferQueue> output_surface_;
+ MockBufferQueue* mock_output_surface_;
+ bool doublebuffering_;
+ bool first_frame_;
+};
+
+namespace {
+const gfx::Size screen_size = gfx::Size(30, 30);
+const gfx::Rect screen_rect = gfx::Rect(screen_size);
+const gfx::Rect small_damage = gfx::Rect(gfx::Size(10, 10));
+const gfx::Rect large_damage = gfx::Rect(gfx::Size(20, 20));
+const gfx::Rect overlapping_damage = gfx::Rect(gfx::Size(5, 20));
+
+GLuint CreateImageDefault() {
+ static GLuint id = 0;
+ return ++id;
+}
+
+class MockedContext : public cc::TestWebGraphicsContext3D {
+ public:
+ MockedContext() {
+ 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,
+ GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
+ MOCK_METHOD1(destroyImageCHROMIUM, void(GLuint));
+ MOCK_METHOD5(framebufferTexture2D,
+ void(GLenum, GLenum, GLenum, GLuint, GLint));
+};
+
+class BufferQueueMockedContextTest : public BufferQueueTest {
+ public:
+ void SetUp() override {
+ context_ = new MockedContext();
+ InitWithContext(std::unique_ptr<cc::TestWebGraphicsContext3D>(context_));
+ }
+
+ protected:
+ MockedContext* context_;
+};
+
+scoped_refptr<cc::TestContextProvider> CreateMockedContextProvider(
+ MockedContext** context) {
+ std::unique_ptr<MockedContext> owned_context(new MockedContext);
+ *context = owned_context.get();
+ scoped_refptr<cc::TestContextProvider> context_provider =
+ cc::TestContextProvider::Create(std::move(owned_context));
+ context_provider->BindToCurrentThread();
+ return context_provider;
+}
+
+std::unique_ptr<BufferQueue> CreateBufferQueue(
+ unsigned int target,
+ gpu::gles2::GLES2Interface* gl,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) {
+ std::unique_ptr<BufferQueue> buffer_queue(new BufferQueue(
+ gl, target, GL_RGB, display::DisplaySnapshot::PrimaryFormat(), nullptr,
+ gpu_memory_buffer_manager, kFakeSurfaceHandle));
+ buffer_queue->Initialize();
+ return buffer_queue;
+}
+
+TEST(BufferQueueStandaloneTest, FboInitialization) {
+ MockedContext* context;
+ scoped_refptr<cc::TestContextProvider> context_provider =
+ CreateMockedContextProvider(&context);
+ std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager(
+ new StubGpuMemoryBufferManager);
+ std::unique_ptr<BufferQueue> output_surface =
+ CreateBufferQueue(GL_TEXTURE_2D, context_provider->ContextGL(),
+ gpu_memory_buffer_manager.get());
+
+ 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);
+}
+
+TEST(BufferQueueStandaloneTest, FboBinding) {
+ GLenum targets[] = {GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB};
+ for (size_t i = 0; i < 2; ++i) {
+ GLenum target = targets[i];
+
+ MockedContext* context;
+ scoped_refptr<cc::TestContextProvider> context_provider =
+ CreateMockedContextProvider(&context);
+ std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager(
+ new StubGpuMemoryBufferManager);
+ 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));
+ Expectation image =
+ EXPECT_CALL(*context, createImageCHROMIUM(_, 0, 0, GL_RGB))
+ .WillOnce(Return(1));
+ Expectation fb =
+ 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))
+ .After(tex, image);
+ EXPECT_CALL(*context,
+ framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ target, Ne(0U), _))
+ .After(fb, bind_tex);
+
+ output_surface->BindFramebuffer();
+ }
+}
+
+TEST(BufferQueueStandaloneTest, CheckBoundFramebuffer) {
+ scoped_refptr<cc::TestContextProvider> context_provider =
+ cc::TestContextProvider::Create();
+ context_provider->BindToCurrentThread();
+ std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager;
+ std::unique_ptr<BufferQueue> output_surface;
+ gpu_memory_buffer_manager.reset(new StubGpuMemoryBufferManager);
+
+ std::unique_ptr<GLHelper> gl_helper;
+ gl_helper.reset(new GLHelper(context_provider->ContextGL(),
+ context_provider->ContextSupport()));
+
+ output_surface.reset(new BufferQueue(
+ context_provider->ContextGL(), GL_TEXTURE_2D, GL_RGB,
+ display::DisplaySnapshot::PrimaryFormat(), gl_helper.get(),
+ gpu_memory_buffer_manager.get(), kFakeSurfaceHandle));
+ output_surface->Initialize();
+ output_surface->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ // Trigger a sub-buffer copy to exercise all paths.
+ output_surface->BindFramebuffer();
+ output_surface->SwapBuffers(screen_rect);
+ output_surface->PageFlipComplete();
+ output_surface->BindFramebuffer();
+ output_surface->SwapBuffers(small_damage);
+
+ int current_fbo = 0;
+ context_provider->ContextGL()->GetIntegerv(GL_FRAMEBUFFER_BINDING,
+ &current_fbo);
+ EXPECT_EQ(static_cast<int>(output_surface->fbo()), current_fbo);
+}
+
+TEST_F(BufferQueueTest, PartialSwapReuse) {
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ ASSERT_TRUE(doublebuffering_);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, _, small_damage, screen_rect))
+ .Times(1);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, _, small_damage, small_damage))
+ .Times(1);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, _, large_damage, small_damage))
+ .Times(1);
+ SendFullFrame();
+ SendDamagedFrame(small_damage);
+ SendDamagedFrame(small_damage);
+ SendDamagedFrame(large_damage);
+ // Verify that the damage has propagated.
+ EXPECT_EQ(next_frame()->damage, large_damage);
+}
+
+TEST_F(BufferQueueTest, PartialSwapFullFrame) {
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ ASSERT_TRUE(doublebuffering_);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, _, small_damage, screen_rect))
+ .Times(1);
+ SendFullFrame();
+ SendDamagedFrame(small_damage);
+ SendFullFrame();
+ SendFullFrame();
+ EXPECT_EQ(next_frame()->damage, screen_rect);
+}
+
+TEST_F(BufferQueueTest, PartialSwapOverlapping) {
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ ASSERT_TRUE(doublebuffering_);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, _, small_damage, screen_rect))
+ .Times(1);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, _, overlapping_damage, small_damage))
+ .Times(1);
+
+ SendFullFrame();
+ SendDamagedFrame(small_damage);
+ SendDamagedFrame(overlapping_damage);
+ EXPECT_EQ(next_frame()->damage, overlapping_damage);
+}
+
+TEST_F(BufferQueueTest, MultipleBindCalls) {
+ // Check that multiple bind calls do not create or change surfaces.
+ output_surface_->BindFramebuffer();
+ EXPECT_EQ(1, CountBuffers());
+ unsigned int fb = current_surface();
+ output_surface_->BindFramebuffer();
+ EXPECT_EQ(1, CountBuffers());
+ EXPECT_EQ(fb, current_surface());
+}
+
+TEST_F(BufferQueueTest, CheckDoubleBuffering) {
+ // Check buffer flow through double buffering path.
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ EXPECT_EQ(0, CountBuffers());
+ output_surface_->BindFramebuffer();
+ EXPECT_EQ(1, CountBuffers());
+ EXPECT_NE(0U, current_surface());
+ EXPECT_FALSE(displayed_frame());
+ SwapBuffers();
+ EXPECT_EQ(1U, in_flight_surfaces().size());
+ output_surface_->PageFlipComplete();
+ EXPECT_EQ(0U, in_flight_surfaces().size());
+ EXPECT_TRUE(displayed_frame()->texture);
+ output_surface_->BindFramebuffer();
+ EXPECT_EQ(2, CountBuffers());
+ CheckUnique();
+ EXPECT_NE(0U, current_surface());
+ EXPECT_EQ(0U, in_flight_surfaces().size());
+ EXPECT_TRUE(displayed_frame()->texture);
+ SwapBuffers();
+ CheckUnique();
+ EXPECT_EQ(1U, in_flight_surfaces().size());
+ EXPECT_TRUE(displayed_frame()->texture);
+ output_surface_->PageFlipComplete();
+ CheckUnique();
+ EXPECT_EQ(0U, in_flight_surfaces().size());
+ EXPECT_EQ(1U, available_surfaces().size());
+ EXPECT_TRUE(displayed_frame()->texture);
+ output_surface_->BindFramebuffer();
+ EXPECT_EQ(2, CountBuffers());
+ CheckUnique();
+ EXPECT_TRUE(available_surfaces().empty());
+}
+
+TEST_F(BufferQueueTest, CheckTripleBuffering) {
+ // Check buffer flow through triple buffering path.
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+
+ // This bit is the same sequence tested in the doublebuffering case.
+ output_surface_->BindFramebuffer();
+ EXPECT_FALSE(displayed_frame());
+ SwapBuffers();
+ output_surface_->PageFlipComplete();
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+
+ EXPECT_EQ(2, CountBuffers());
+ CheckUnique();
+ EXPECT_EQ(1U, in_flight_surfaces().size());
+ EXPECT_TRUE(displayed_frame()->texture);
+ output_surface_->BindFramebuffer();
+ EXPECT_EQ(3, CountBuffers());
+ CheckUnique();
+ EXPECT_NE(0U, current_surface());
+ EXPECT_EQ(1U, in_flight_surfaces().size());
+ EXPECT_TRUE(displayed_frame()->texture);
+ output_surface_->PageFlipComplete();
+ EXPECT_EQ(3, CountBuffers());
+ CheckUnique();
+ EXPECT_NE(0U, current_surface());
+ EXPECT_EQ(0U, in_flight_surfaces().size());
+ EXPECT_TRUE(displayed_frame()->texture);
+ EXPECT_EQ(1U, available_surfaces().size());
+}
+
+TEST_F(BufferQueueTest, CheckEmptySwap) {
+ // Check empty swap flow, in which the damage is empty.
+ EXPECT_EQ(0, CountBuffers());
+ output_surface_->BindFramebuffer();
+ EXPECT_EQ(1, CountBuffers());
+ EXPECT_NE(0U, current_surface());
+ EXPECT_FALSE(displayed_frame());
+ SwapBuffers();
+ EXPECT_EQ(1U, in_flight_surfaces().size());
+ EXPECT_EQ(nullptr, in_flight_surfaces().front());
+ output_surface_->PageFlipComplete();
+ EXPECT_EQ(0U, in_flight_surfaces().size());
+ EXPECT_EQ(nullptr, displayed_frame());
+}
+
+TEST_F(BufferQueueTest, CheckCorrectBufferOrdering) {
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ const size_t kSwapCount = 3;
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ }
+
+ EXPECT_EQ(kSwapCount, in_flight_surfaces().size());
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ unsigned int next_texture_id = in_flight_surfaces().front()->texture;
+ output_surface_->PageFlipComplete();
+ EXPECT_EQ(displayed_frame()->texture, next_texture_id);
+ }
+}
+
+TEST_F(BufferQueueTest, ReshapeWithInFlightSurfaces) {
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ const size_t kSwapCount = 3;
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ }
+
+ output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace(), false);
+ EXPECT_EQ(3u, in_flight_surfaces().size());
+
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ output_surface_->PageFlipComplete();
+ EXPECT_FALSE(displayed_frame());
+ }
+
+ // The dummy surfacess left should be discarded.
+ EXPECT_EQ(0u, available_surfaces().size());
+}
+
+TEST_F(BufferQueueTest, SwapAfterReshape) {
+ DCHECK_EQ(0u, gpu_memory_buffer_manager_->set_color_space_count());
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ const size_t kSwapCount = 3;
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ }
+ DCHECK_EQ(kSwapCount, gpu_memory_buffer_manager_->set_color_space_count());
+
+ output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace(), false);
+ DCHECK_EQ(kSwapCount, gpu_memory_buffer_manager_->set_color_space_count());
+
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ }
+ DCHECK_EQ(2 * kSwapCount,
+ gpu_memory_buffer_manager_->set_color_space_count());
+ EXPECT_EQ(2 * kSwapCount, in_flight_surfaces().size());
+
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ output_surface_->PageFlipComplete();
+ EXPECT_FALSE(displayed_frame());
+ }
+
+ CheckUnique();
+
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ unsigned int next_texture_id = in_flight_surfaces().front()->texture;
+ output_surface_->PageFlipComplete();
+ EXPECT_EQ(displayed_frame()->texture, next_texture_id);
+ EXPECT_TRUE(displayed_frame());
+ }
+
+ DCHECK_EQ(2 * kSwapCount,
+ gpu_memory_buffer_manager_->set_color_space_count());
+ for (size_t i = 0; i < kSwapCount; ++i) {
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ output_surface_->PageFlipComplete();
+ }
+ DCHECK_EQ(2 * kSwapCount,
+ gpu_memory_buffer_manager_->set_color_space_count());
+}
+
+TEST_F(BufferQueueMockedContextTest, RecreateBuffers) {
+ // This setup is to easily get one frame in each of:
+ // - currently bound for drawing.
+ // - in flight to GPU.
+ // - currently displayed.
+ // - free frame.
+ // This tests buffers in all states.
+ // Bind/swap pushes frames into the in flight list, then the PageFlipComplete
+ // calls pull one frame into displayed and another into the free list.
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ output_surface_->BindFramebuffer();
+ SwapBuffers();
+ output_surface_->BindFramebuffer();
+ output_surface_->PageFlipComplete();
+ output_surface_->PageFlipComplete();
+ // We should have one buffer in each possible state right now, including one
+ // being drawn to.
+ ASSERT_EQ(1U, in_flight_surfaces().size());
+ ASSERT_EQ(1U, available_surfaces().size());
+ EXPECT_TRUE(displayed_frame());
+ EXPECT_TRUE(current_frame());
+
+ auto* current = current_frame();
+ auto* displayed = displayed_frame();
+ auto* in_flight = in_flight_surfaces().front().get();
+ auto* available = available_surfaces().front().get();
+
+ // 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(), GL_RGB))
+ .Times(3);
+ Expectation copy1 = EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, displayed->texture, _, _))
+ .Times(1);
+ Expectation copy2 = EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, current->texture, _, _))
+ .Times(1);
+ Expectation copy3 = EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(_, in_flight->texture, _, _))
+ .Times(1);
+
+ EXPECT_CALL(*context_, destroyImageCHROMIUM(displayed->image))
+ .Times(1)
+ .After(copy1);
+ EXPECT_CALL(*context_, destroyImageCHROMIUM(current->image))
+ .Times(1)
+ .After(copy2);
+ EXPECT_CALL(*context_, destroyImageCHROMIUM(in_flight->image))
+ .Times(1)
+ .After(copy3);
+ EXPECT_CALL(*context_, destroyImageCHROMIUM(available->image)).Times(1);
+ // After copying, we expect the framebuffer binding to be updated.
+ EXPECT_CALL(*context_, bindFramebuffer(_, _))
+ .After(copy1)
+ .After(copy2)
+ .After(copy3);
+ EXPECT_CALL(*context_, framebufferTexture2D(_, _, _, _, _))
+ .After(copy1)
+ .After(copy2)
+ .After(copy3);
+
+ output_surface_->RecreateBuffers();
+ testing::Mock::VerifyAndClearExpectations(context_);
+ testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
+
+ // All free buffers should be destroyed, the remaining buffers should all
+ // be replaced but still valid.
+ EXPECT_EQ(1U, in_flight_surfaces().size());
+ EXPECT_EQ(0U, available_surfaces().size());
+ EXPECT_TRUE(displayed_frame());
+ EXPECT_TRUE(current_frame());
+}
+
+TEST_F(BufferQueueTest, AllocateFails) {
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
+
+ // Succeed in the two swaps.
+ output_surface_->BindFramebuffer();
+ EXPECT_TRUE(current_frame());
+ output_surface_->SwapBuffers(screen_rect);
+
+ // Fail the next surface allocation.
+ gpu_memory_buffer_manager_->set_allocate_succeeds(false);
+ output_surface_->BindFramebuffer();
+ EXPECT_FALSE(current_frame());
+ output_surface_->SwapBuffers(screen_rect);
+ EXPECT_FALSE(current_frame());
+
+ // Try another swap. It should copy the buffer damage from the back
+ // surface.
+ gpu_memory_buffer_manager_->set_allocate_succeeds(true);
+ output_surface_->BindFramebuffer();
+ unsigned int source_texture = in_flight_surfaces().front()->texture;
+ unsigned int target_texture = current_frame()->texture;
+ testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(target_texture, source_texture, small_damage, _))
+ .Times(1);
+ output_surface_->SwapBuffers(small_damage);
+ testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
+
+ // Destroy the just-created buffer, and try another swap. The copy should
+ // come from the displayed surface (because both in-flight surfaces are
+ // gone now).
+ output_surface_->PageFlipComplete();
+ in_flight_surfaces().back().reset();
+ EXPECT_EQ(2u, in_flight_surfaces().size());
+ for (auto& surface : in_flight_surfaces())
+ EXPECT_FALSE(surface);
+ output_surface_->BindFramebuffer();
+ source_texture = displayed_frame()->texture;
+ EXPECT_TRUE(current_frame());
+ EXPECT_TRUE(displayed_frame());
+ target_texture = current_frame()->texture;
+ testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
+ EXPECT_CALL(*mock_output_surface_,
+ CopyBufferDamage(target_texture, source_texture, small_damage, _))
+ .Times(1);
+ output_surface_->SwapBuffers(small_damage);
+ testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator.h b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator.h
new file mode 100644
index 00000000000..cbed76ce606
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_H_
+
+#include "base/macros.h"
+#include "cc/output/overlay_candidate_validator.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT CompositorOverlayCandidateValidator
+ : public cc::OverlayCandidateValidator {
+ public:
+ CompositorOverlayCandidateValidator() {}
+ ~CompositorOverlayCandidateValidator() override {}
+
+ virtual void SetSoftwareMirrorMode(bool enabled) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidator);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_H_
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.cc b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.cc
new file mode 100644
index 00000000000..6c3a73a2641
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.cc
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "cc/output/overlay_processor.h"
+#include "cc/output/overlay_strategy_underlay.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+
+namespace viz {
+
+CompositorOverlayCandidateValidatorAndroid::
+ CompositorOverlayCandidateValidatorAndroid() {}
+
+CompositorOverlayCandidateValidatorAndroid::
+ ~CompositorOverlayCandidateValidatorAndroid() {}
+
+void CompositorOverlayCandidateValidatorAndroid::GetStrategies(
+ cc::OverlayProcessor::StrategyList* strategies) {
+ strategies->push_back(base::MakeUnique<cc::OverlayStrategyUnderlay>(this));
+}
+
+void CompositorOverlayCandidateValidatorAndroid::CheckOverlaySupport(
+ cc::OverlayCandidateList* candidates) {
+ // There should only be at most a single overlay candidate: the video quad.
+ // There's no check that the presented candidate is really a video frame for
+ // a fullscreen video. Instead it's assumed that if a quad is marked as
+ // overlayable, it's a fullscreen video quad.
+ DCHECK_LE(candidates->size(), 1u);
+
+ if (!candidates->empty()) {
+ cc::OverlayCandidate& candidate = candidates->front();
+
+ // This quad either will be promoted, or would be if it were backed by a
+ // SurfaceView. Record that it should get a promotion hint.
+ candidates->AddPromotionHint(candidate);
+
+ if (candidate.is_backed_by_surface_texture) {
+ // This quad would be promoted if it were backed by a SurfaceView. Since
+ // it isn't, we can't promote it.
+ return;
+ }
+
+ candidate.display_rect =
+ gfx::RectF(gfx::ToEnclosingRect(candidate.display_rect));
+ candidate.overlay_handled = true;
+ candidate.plane_z_order = -1;
+ }
+}
+
+bool CompositorOverlayCandidateValidatorAndroid::AllowCALayerOverlays() {
+ return false;
+}
+
+bool CompositorOverlayCandidateValidatorAndroid::AllowDCLayerOverlays() {
+ return false;
+}
+
+// Overlays will still be allowed when software mirroring is enabled, even
+// though they won't appear in the mirror.
+void CompositorOverlayCandidateValidatorAndroid::SetSoftwareMirrorMode(
+ bool enabled) {}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h
new file mode 100644
index 00000000000..e9d3a30985e
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_ANDROID_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_ANDROID_H_
+
+#include "base/macros.h"
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// An overlay validator for supporting fullscreen video underlays on Android.
+// Things are a bit different on Android compared with other platforms. By the
+// time a video frame is marked as overlayable it means the video decoder was
+// outputting to a Surface that we can't read back from. As a result, the
+// overlay must always succeed, or the video won't be visible. This is one of of
+// the reasons that only fullscreen is supported: we have to be sure that
+// nothing will cause the overlay to be rejected, because there's no fallback to
+// gl compositing.
+class VIZ_SERVICE_EXPORT CompositorOverlayCandidateValidatorAndroid
+ : public CompositorOverlayCandidateValidator {
+ public:
+ CompositorOverlayCandidateValidatorAndroid();
+ ~CompositorOverlayCandidateValidatorAndroid() override;
+
+ void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
+ void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
+ bool AllowCALayerOverlays() override;
+ bool AllowDCLayerOverlays() override;
+
+ void SetSoftwareMirrorMode(bool enabled) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorAndroid);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_ANDROID_H_
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.h b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.h
new file mode 100644
index 00000000000..aade5d1a71f
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_MAC_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_MAC_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT CompositorOverlayCandidateValidatorMac
+ : public CompositorOverlayCandidateValidator {
+ public:
+ explicit CompositorOverlayCandidateValidatorMac(bool ca_layer_disabled);
+ ~CompositorOverlayCandidateValidatorMac() override;
+
+ // cc::OverlayCandidateValidator implementation.
+ void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
+ bool AllowCALayerOverlays() override;
+ bool AllowDCLayerOverlays() override;
+ void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
+
+ // CompositorOverlayCandidateValidator implementation.
+ void SetSoftwareMirrorMode(bool enabled) override;
+
+ private:
+ bool software_mirror_active_;
+ const bool ca_layer_disabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorMac);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_MAC_H_
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.mm b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.mm
new file mode 100644
index 00000000000..fa2f5c71b61
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.mm
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.h"
+
+#include <stddef.h>
+
+namespace viz {
+
+CompositorOverlayCandidateValidatorMac::CompositorOverlayCandidateValidatorMac(
+ bool ca_layer_disabled)
+ : software_mirror_active_(false), ca_layer_disabled_(ca_layer_disabled) {}
+
+CompositorOverlayCandidateValidatorMac::
+ ~CompositorOverlayCandidateValidatorMac() {}
+
+void CompositorOverlayCandidateValidatorMac::GetStrategies(
+ cc::OverlayProcessor::StrategyList* strategies) {}
+
+bool CompositorOverlayCandidateValidatorMac::AllowCALayerOverlays() {
+ return !ca_layer_disabled_ && !software_mirror_active_;
+}
+
+bool CompositorOverlayCandidateValidatorMac::AllowDCLayerOverlays() {
+ return false;
+}
+
+void CompositorOverlayCandidateValidatorMac::CheckOverlaySupport(
+ cc::OverlayCandidateList* surfaces) {}
+
+void CompositorOverlayCandidateValidatorMac::SetSoftwareMirrorMode(
+ bool enabled) {
+ software_mirror_active_ = enabled;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.cc b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.cc
new file mode 100644
index 00000000000..84861074dad
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.cc
@@ -0,0 +1,126 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_split.h"
+#include "cc/output/overlay_strategy_fullscreen.h"
+#include "cc/output/overlay_strategy_single_on_top.h"
+#include "cc/output/overlay_strategy_underlay.h"
+#include "cc/output/overlay_strategy_underlay_cast.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+
+namespace viz {
+namespace {
+// Templated function used to create an OverlayProcessor::Strategy
+// of type |S|.
+template <typename S>
+std::unique_ptr<cc::OverlayProcessor::Strategy> MakeOverlayStrategy(
+ CompositorOverlayCandidateValidatorOzone* capability_checker) {
+ return base::MakeUnique<S>(capability_checker);
+}
+
+} // namespace
+
+// |overlay_candidates| is an object used to answer questions about possible
+// overlays configuarations.
+// |strategies_string| is a comma-separated string containing all the overaly
+// strategies that should be returned by GetStrategies.
+// If |strategies_string| is empty "single-on-top,underlay" will be used as
+// default.
+CompositorOverlayCandidateValidatorOzone::
+ CompositorOverlayCandidateValidatorOzone(
+ std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates,
+ std::string strategies_string)
+ : overlay_candidates_(std::move(overlay_candidates)),
+ software_mirror_active_(false) {
+ if (!strategies_string.length())
+ strategies_string = "single-on-top,underlay";
+
+ for (const auto& strategy_name :
+ base::SplitStringPiece(strategies_string, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ if (strategy_name == "single-fullscreen") {
+ strategies_instantiators_.push_back(
+ base::Bind(MakeOverlayStrategy<cc::OverlayStrategyFullscreen>));
+ } else if (strategy_name == "single-on-top") {
+ strategies_instantiators_.push_back(
+ base::Bind(MakeOverlayStrategy<cc::OverlayStrategySingleOnTop>));
+ } else if (strategy_name == "underlay") {
+ strategies_instantiators_.push_back(
+ base::Bind(MakeOverlayStrategy<cc::OverlayStrategyUnderlay>));
+ } else if (strategy_name == "cast") {
+ strategies_instantiators_.push_back(
+ base::Bind(MakeOverlayStrategy<cc::OverlayStrategyUnderlayCast>));
+ } else {
+ LOG(WARNING) << "Unrecognized overlay strategy " << strategy_name;
+ }
+ }
+}
+
+CompositorOverlayCandidateValidatorOzone::
+ ~CompositorOverlayCandidateValidatorOzone() {}
+
+void CompositorOverlayCandidateValidatorOzone::GetStrategies(
+ cc::OverlayProcessor::StrategyList* strategies) {
+ for (auto& instantiator : strategies_instantiators_)
+ strategies->push_back(instantiator.Run(this));
+}
+
+bool CompositorOverlayCandidateValidatorOzone::AllowCALayerOverlays() {
+ return false;
+}
+
+bool CompositorOverlayCandidateValidatorOzone::AllowDCLayerOverlays() {
+ return false;
+}
+
+void CompositorOverlayCandidateValidatorOzone::CheckOverlaySupport(
+ cc::OverlayCandidateList* surfaces) {
+ // SW mirroring copies out of the framebuffer, so we can't remove any
+ // quads for overlaying, otherwise the output is incorrect.
+ if (software_mirror_active_) {
+ for (size_t i = 0; i < surfaces->size(); i++) {
+ surfaces->at(i).overlay_handled = false;
+ }
+ return;
+ }
+
+ DCHECK_GE(2U, surfaces->size());
+ ui::OverlayCandidatesOzone::OverlaySurfaceCandidateList ozone_surface_list;
+ ozone_surface_list.resize(surfaces->size());
+
+ for (size_t i = 0; i < surfaces->size(); i++) {
+ ozone_surface_list.at(i).transform = surfaces->at(i).transform;
+ ozone_surface_list.at(i).format = surfaces->at(i).format;
+ ozone_surface_list.at(i).display_rect = surfaces->at(i).display_rect;
+ ozone_surface_list.at(i).crop_rect = surfaces->at(i).uv_rect;
+ ozone_surface_list.at(i).clip_rect = surfaces->at(i).clip_rect;
+ ozone_surface_list.at(i).is_clipped = surfaces->at(i).is_clipped;
+ ozone_surface_list.at(i).plane_z_order = surfaces->at(i).plane_z_order;
+ ozone_surface_list.at(i).buffer_size =
+ surfaces->at(i).resource_size_in_pixels;
+ }
+
+ overlay_candidates_->CheckOverlaySupport(&ozone_surface_list);
+ DCHECK_EQ(surfaces->size(), ozone_surface_list.size());
+
+ for (size_t i = 0; i < surfaces->size(); i++) {
+ surfaces->at(i).overlay_handled = ozone_surface_list.at(i).overlay_handled;
+ surfaces->at(i).display_rect = ozone_surface_list.at(i).display_rect;
+ }
+}
+
+void CompositorOverlayCandidateValidatorOzone::SetSoftwareMirrorMode(
+ bool enabled) {
+ software_mirror_active_ = enabled;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.h b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.h
new file mode 100644
index 00000000000..4ba9429d14a
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h"
+#include "components/viz/service/viz_service_export.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+class OverlayCandidatesOzone;
+}
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT CompositorOverlayCandidateValidatorOzone
+ : public CompositorOverlayCandidateValidator {
+ public:
+ CompositorOverlayCandidateValidatorOzone(
+ std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates,
+ std::string strategies_string);
+ ~CompositorOverlayCandidateValidatorOzone() override;
+
+ // cc::OverlayCandidateValidator implementation.
+ void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
+ bool AllowCALayerOverlays() override;
+ bool AllowDCLayerOverlays() override;
+ void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
+
+ // CompositorOverlayCandidateValidator implementation.
+ void SetSoftwareMirrorMode(bool enabled) override;
+
+ private:
+ std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates_;
+ // Callback declaration to allocate a new OverlayProcessor::Strategy.
+ using StrategyInstantiator =
+ base::Callback<std::unique_ptr<cc::OverlayProcessor::Strategy>(
+ CompositorOverlayCandidateValidatorOzone*)>;
+ // List callbacks used to instantiate OverlayProcessor::Strategy
+ // as defined by |strategies_string| paramter in the constructor.
+ std::vector<StrategyInstantiator> strategies_instantiators_;
+ bool software_mirror_active_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorOzone);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.cc b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.cc
new file mode 100644
index 00000000000..bb22c575671
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.h"
+
+#include "cc/output/overlay_processor.h"
+
+namespace viz {
+
+CompositorOverlayCandidateValidatorWin::
+ CompositorOverlayCandidateValidatorWin() {}
+
+CompositorOverlayCandidateValidatorWin::
+ ~CompositorOverlayCandidateValidatorWin() {}
+
+void CompositorOverlayCandidateValidatorWin::GetStrategies(
+ cc::OverlayProcessor::StrategyList* strategies) {}
+
+void CompositorOverlayCandidateValidatorWin::CheckOverlaySupport(
+ cc::OverlayCandidateList* candidates) {
+ NOTIMPLEMENTED();
+}
+
+bool CompositorOverlayCandidateValidatorWin::AllowCALayerOverlays() {
+ return false;
+}
+
+bool CompositorOverlayCandidateValidatorWin::AllowDCLayerOverlays() {
+ return true;
+}
+
+void CompositorOverlayCandidateValidatorWin::SetSoftwareMirrorMode(
+ bool enabled) {
+ // Software mirroring isn't supported on Windows.
+ NOTIMPLEMENTED();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.h b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.h
new file mode 100644
index 00000000000..c5a20c6d5c5
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.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_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_WIN_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_WIN_H_
+
+#include "base/macros.h"
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// This is a simple overlay candidate validator that promotes everything
+// possible to an overlay.
+class VIZ_SERVICE_EXPORT CompositorOverlayCandidateValidatorWin
+ : public CompositorOverlayCandidateValidator {
+ public:
+ CompositorOverlayCandidateValidatorWin();
+ ~CompositorOverlayCandidateValidatorWin() override;
+
+ void GetStrategies(cc::OverlayProcessor::StrategyList* strategies) override;
+ void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) override;
+ bool AllowCALayerOverlays() override;
+ bool AllowDCLayerOverlays() override;
+
+ void SetSoftwareMirrorMode(bool enabled) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompositorOverlayCandidateValidatorWin);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_WIN_H_
diff --git a/chromium/components/viz/service/display_embedder/display_output_surface.cc b/chromium/components/viz/service/display_embedder/display_output_surface.cc
new file mode 100644
index 00000000000..b293f8b82ce
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/display_output_surface.cc
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/display_output_surface.h"
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "cc/output/output_surface_client.h"
+#include "cc/output/output_surface_frame.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "gpu/command_buffer/client/context_support.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+
+namespace viz {
+
+DisplayOutputSurface::DisplayOutputSurface(
+ scoped_refptr<InProcessContextProvider> context_provider,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source)
+ : cc::OutputSurface(context_provider),
+ synthetic_begin_frame_source_(synthetic_begin_frame_source),
+ weak_ptr_factory_(this) {
+ capabilities_.flipped_output_surface =
+ context_provider->ContextCapabilities().flips_vertically;
+ capabilities_.supports_stencil =
+ context_provider->ContextCapabilities().num_stencil_bits > 0;
+ context_provider->SetSwapBuffersCompletionCallback(
+ base::Bind(&DisplayOutputSurface::OnGpuSwapBuffersCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
+ context_provider->SetUpdateVSyncParametersCallback(
+ base::Bind(&DisplayOutputSurface::OnVSyncParametersUpdated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+DisplayOutputSurface::~DisplayOutputSurface() {}
+
+void DisplayOutputSurface::BindToClient(cc::OutputSurfaceClient* client) {
+ DCHECK(client);
+ DCHECK(!client_);
+ client_ = client;
+}
+
+void DisplayOutputSurface::EnsureBackbuffer() {}
+
+void DisplayOutputSurface::DiscardBackbuffer() {
+ context_provider()->ContextGL()->DiscardBackbufferCHROMIUM();
+}
+
+void DisplayOutputSurface::BindFramebuffer() {
+ context_provider()->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void DisplayOutputSurface::SetDrawRectangle(const gfx::Rect& rect) {
+ if (set_draw_rectangle_for_frame_)
+ return;
+ DCHECK(gfx::Rect(size_).Contains(rect));
+ DCHECK(has_set_draw_rectangle_since_last_resize_ ||
+ (gfx::Rect(size_) == rect));
+ set_draw_rectangle_for_frame_ = true;
+ has_set_draw_rectangle_since_last_resize_ = true;
+ context_provider()->ContextGL()->SetDrawRectangleCHROMIUM(
+ rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void DisplayOutputSurface::Reshape(const gfx::Size& size,
+ float device_scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool has_alpha,
+ bool use_stencil) {
+ size_ = size;
+ has_set_draw_rectangle_since_last_resize_ = false;
+ context_provider()->ContextGL()->ResizeCHROMIUM(
+ size.width(), size.height(), device_scale_factor, has_alpha);
+}
+
+void DisplayOutputSurface::SwapBuffers(cc::OutputSurfaceFrame frame) {
+ DCHECK(context_provider_);
+
+ if (frame.latency_info.size() > 0)
+ context_provider_->ContextSupport()->AddLatencyInfo(frame.latency_info);
+
+ set_draw_rectangle_for_frame_ = false;
+ if (frame.sub_buffer_rect) {
+ context_provider_->ContextSupport()->PartialSwapBuffers(
+ *frame.sub_buffer_rect);
+ } else {
+ context_provider_->ContextSupport()->Swap();
+ }
+}
+
+uint32_t DisplayOutputSurface::GetFramebufferCopyTextureFormat() {
+ // TODO(danakj): What attributes are used for the default framebuffer here?
+ // Can it have alpha? InProcessContextProvider doesn't take any
+ // attributes.
+ return GL_RGB;
+}
+
+cc::OverlayCandidateValidator*
+DisplayOutputSurface::GetOverlayCandidateValidator() const {
+ return nullptr;
+}
+
+bool DisplayOutputSurface::IsDisplayedAsOverlayPlane() const {
+ return false;
+}
+
+unsigned DisplayOutputSurface::GetOverlayTextureId() const {
+ return 0;
+}
+
+gfx::BufferFormat DisplayOutputSurface::GetOverlayBufferFormat() const {
+ return gfx::BufferFormat::RGBX_8888;
+}
+
+bool DisplayOutputSurface::SurfaceIsSuspendForRecycle() const {
+ return false;
+}
+
+bool DisplayOutputSurface::HasExternalStencilTest() const {
+ return false;
+}
+
+void DisplayOutputSurface::ApplyExternalStencil() {}
+
+void DisplayOutputSurface::DidReceiveSwapBuffersAck(gfx::SwapResult result) {
+ client_->DidReceiveSwapBuffersAck();
+}
+
+void DisplayOutputSurface::OnGpuSwapBuffersCompleted(
+ const std::vector<ui::LatencyInfo>& latency_info,
+ gfx::SwapResult result,
+ const gpu::GpuProcessHostedCALayerTreeParamsMac* params_mac) {
+ for (const auto& latency : latency_info) {
+ if (latency.latency_components().size() > 0)
+ latency_tracker_.OnGpuSwapBuffersCompleted(latency);
+ }
+ DidReceiveSwapBuffersAck(result);
+}
+
+void DisplayOutputSurface::OnVSyncParametersUpdated(base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ // TODO(brianderson): We should not be receiving 0 intervals.
+ synthetic_begin_frame_source_->OnUpdateVSyncParameters(
+ timebase,
+ interval.is_zero() ? cc::BeginFrameArgs::DefaultInterval() : interval);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/display_output_surface.h b/chromium/components/viz/service/display_embedder/display_output_surface.h
new file mode 100644
index 00000000000..7e59c6a160b
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/display_output_surface.h
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_OUTPUT_SURFACE_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_OUTPUT_SURFACE_H_
+
+#include <memory>
+
+#include "cc/output/output_surface.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
+#include "ui/latency/latency_tracker.h"
+
+namespace cc {
+class SyntheticBeginFrameSource;
+}
+
+namespace viz {
+
+// An OutputSurface implementation that directly draws and
+// swaps to an actual GL surface.
+class DisplayOutputSurface : public cc::OutputSurface {
+ public:
+ DisplayOutputSurface(
+ scoped_refptr<InProcessContextProvider> context_provider,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source);
+ ~DisplayOutputSurface() override;
+
+ // cc::OutputSurface implementation
+ void BindToClient(cc::OutputSurfaceClient* client) override;
+ void EnsureBackbuffer() override;
+ void DiscardBackbuffer() override;
+ void BindFramebuffer() override;
+ void SetDrawRectangle(const gfx::Rect& draw_rectangle) override;
+ void Reshape(const gfx::Size& size,
+ float device_scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool has_alpha,
+ bool use_stencil) override;
+ void SwapBuffers(cc::OutputSurfaceFrame frame) override;
+ uint32_t GetFramebufferCopyTextureFormat() override;
+ cc::OverlayCandidateValidator* GetOverlayCandidateValidator() const override;
+ bool IsDisplayedAsOverlayPlane() const override;
+ unsigned GetOverlayTextureId() const override;
+ gfx::BufferFormat GetOverlayBufferFormat() const override;
+ bool SurfaceIsSuspendForRecycle() const override;
+ bool HasExternalStencilTest() const override;
+ void ApplyExternalStencil() override;
+
+ protected:
+ cc::OutputSurfaceClient* client() const { return client_; }
+
+ // Called when a swap completion is signaled from ImageTransportSurface.
+ virtual void DidReceiveSwapBuffersAck(gfx::SwapResult result);
+
+ private:
+ // Called when a swap completion is signaled from ImageTransportSurface.
+ void OnGpuSwapBuffersCompleted(
+ const std::vector<ui::LatencyInfo>& latency_info,
+ gfx::SwapResult result,
+ const gpu::GpuProcessHostedCALayerTreeParamsMac* params_mac);
+ void OnVSyncParametersUpdated(base::TimeTicks timebase,
+ base::TimeDelta interval);
+
+ cc::OutputSurfaceClient* client_ = nullptr;
+ cc::SyntheticBeginFrameSource* const synthetic_begin_frame_source_;
+ ui::LatencyTracker latency_tracker_;
+
+ bool set_draw_rectangle_for_frame_ = false;
+ // True if the draw rectangle has been set at all since the last resize.
+ bool has_set_draw_rectangle_since_last_resize_ = false;
+ gfx::Size size_;
+
+ base::WeakPtrFactory<DisplayOutputSurface> weak_ptr_factory_;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_OUTPUT_SURFACE_H_
diff --git a/chromium/components/viz/service/display_embedder/display_output_surface_ozone.cc b/chromium/components/viz/service/display_embedder/display_output_surface_ozone.cc
new file mode 100644
index 00000000000..42df4118c21
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/display_output_surface_ozone.cc
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/display_output_surface_ozone.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "cc/output/output_surface_client.h"
+#include "cc/output/output_surface_frame.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/service/display_embedder/buffer_queue.h"
+#include "gpu/command_buffer/client/context_support.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "ui/display/types/display_snapshot.h"
+
+namespace viz {
+
+DisplayOutputSurfaceOzone::DisplayOutputSurfaceOzone(
+ scoped_refptr<InProcessContextProvider> context_provider,
+ gfx::AcceleratedWidget widget,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ uint32_t target,
+ uint32_t internalformat)
+ : DisplayOutputSurface(context_provider, synthetic_begin_frame_source),
+ gl_helper_(context_provider->ContextGL(),
+ context_provider->ContextSupport()) {
+ capabilities_.uses_default_gl_framebuffer = false;
+ capabilities_.flipped_output_surface = true;
+ // Set |max_frames_pending| to 2 for surfaceless, which aligns scheduling
+ // more closely with the previous surfaced behavior.
+ // With a surface, swap buffer ack used to return early, before actually
+ // presenting the back buffer, enabling the browser compositor to run ahead.
+ // Surfaceless implementation acks at the time of actual buffer swap, which
+ // shifts the start of the new frame forward relative to the old
+ // implementation.
+ capabilities_.max_frames_pending = 2;
+
+ buffer_queue_.reset(
+ new BufferQueue(context_provider->ContextGL(), target, internalformat,
+ display::DisplaySnapshot::PrimaryFormat(), &gl_helper_,
+ gpu_memory_buffer_manager, widget));
+ buffer_queue_->Initialize();
+}
+
+DisplayOutputSurfaceOzone::~DisplayOutputSurfaceOzone() {
+ // TODO(rjkroege): Support cleanup.
+}
+
+void DisplayOutputSurfaceOzone::BindFramebuffer() {
+ DCHECK(buffer_queue_);
+ buffer_queue_->BindFramebuffer();
+}
+
+// We call this on every frame that a value changes, but changing the size once
+// we've allocated backing NativePixmapOzone instances will cause a DCHECK
+// because Chrome never Reshape(s) after the first one from (0,0). NB: this
+// implies that screen size changes need to be plumbed differently. In
+// particular, we must create the native window in the size that the hardware
+// reports.
+void DisplayOutputSurfaceOzone::Reshape(const gfx::Size& size,
+ float device_scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool has_alpha,
+ bool use_stencil) {
+ reshape_size_ = size;
+ DisplayOutputSurface::Reshape(size, device_scale_factor, color_space,
+ has_alpha, use_stencil);
+ buffer_queue_->Reshape(size, device_scale_factor, color_space, use_stencil);
+}
+
+void DisplayOutputSurfaceOzone::SwapBuffers(cc::OutputSurfaceFrame frame) {
+ DCHECK(buffer_queue_);
+
+ // TODO(rjkroege): What if swap happens again before DidReceiveSwapBuffersAck
+ // then it would see the wrong size?
+ DCHECK(reshape_size_ == frame.size);
+ swap_size_ = reshape_size_;
+
+ gfx::Rect damage_rect =
+ frame.sub_buffer_rect ? *frame.sub_buffer_rect : gfx::Rect(swap_size_);
+ buffer_queue_->SwapBuffers(damage_rect);
+
+ DisplayOutputSurface::SwapBuffers(std::move(frame));
+}
+
+uint32_t DisplayOutputSurfaceOzone::GetFramebufferCopyTextureFormat() {
+ return buffer_queue_->internal_format();
+}
+
+bool DisplayOutputSurfaceOzone::IsDisplayedAsOverlayPlane() const {
+ // TODO(rjkroege): implement remaining overlay functionality.
+ return true;
+}
+
+unsigned DisplayOutputSurfaceOzone::GetOverlayTextureId() const {
+ return buffer_queue_->GetCurrentTextureId();
+}
+
+gfx::BufferFormat DisplayOutputSurfaceOzone::GetOverlayBufferFormat() const {
+ DCHECK(buffer_queue_);
+ return buffer_queue_->buffer_format();
+}
+
+void DisplayOutputSurfaceOzone::DidReceiveSwapBuffersAck(
+ gfx::SwapResult result) {
+ bool force_swap = false;
+ if (result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) {
+ // Even through the swap failed, this is a fixable error so we can pretend
+ // it succeeded to the rest of the system.
+ result = gfx::SwapResult::SWAP_ACK;
+ buffer_queue_->RecreateBuffers();
+ force_swap = true;
+ }
+
+ buffer_queue_->PageFlipComplete();
+ client()->DidReceiveSwapBuffersAck();
+
+ if (force_swap)
+ client()->SetNeedsRedrawRect(gfx::Rect(swap_size_));
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/display_output_surface_ozone.h b/chromium/components/viz/service/display_embedder/display_output_surface_ozone.h
new file mode 100644
index 00000000000..f49180cf708
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/display_output_surface_ozone.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_OUTPUT_SURFACE_OZONE_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_OUTPUT_SURFACE_OZONE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "cc/output/output_surface.h"
+#include "components/viz/common/gl_helper.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
+#include "components/viz/service/display_embedder/display_output_surface.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/swap_result.h"
+#include "ui/gl/gl_surface.h"
+
+namespace cc {
+class SyntheticBeginFrameSource;
+}
+
+namespace gpu {
+class GpuMemoryBufferManager;
+}
+
+namespace viz {
+
+class BufferQueue;
+
+// An OutputSurface implementation that directly draws and swap to a GL
+// "surfaceless" surface (aka one backed by a buffer managed explicitly in
+// mus/ozone. This class is adapted from
+// GpuSurfacelessBrowserCompositorOutputSurface.
+class DisplayOutputSurfaceOzone : public DisplayOutputSurface {
+ public:
+ DisplayOutputSurfaceOzone(
+ scoped_refptr<InProcessContextProvider> context_provider,
+ gfx::AcceleratedWidget widget,
+ cc::SyntheticBeginFrameSource* synthetic_begin_frame_source,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ uint32_t target,
+ uint32_t internalformat);
+
+ ~DisplayOutputSurfaceOzone() override;
+
+ // TODO(rjkroege): Implement the equivalent of Reflector.
+
+ private:
+ // cc::OutputSurface implementation.
+ void BindFramebuffer() override;
+ void Reshape(const gfx::Size& size,
+ float device_scale_factor,
+ const gfx::ColorSpace& color_space,
+ bool has_alpha,
+ bool use_stencil) override;
+ void SwapBuffers(cc::OutputSurfaceFrame frame) override;
+ uint32_t GetFramebufferCopyTextureFormat() override;
+ bool IsDisplayedAsOverlayPlane() const override;
+ unsigned GetOverlayTextureId() const override;
+ gfx::BufferFormat GetOverlayBufferFormat() const override;
+
+ // DisplayOutputSurface:
+ void DidReceiveSwapBuffersAck(gfx::SwapResult result) override;
+
+ GLHelper gl_helper_;
+ std::unique_ptr<BufferQueue> buffer_queue_;
+
+ gfx::Size reshape_size_;
+ gfx::Size swap_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayOutputSurfaceOzone);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_OUTPUT_SURFACE_OZONE_H_
diff --git a/chromium/components/viz/service/display_embedder/display_provider.h b/chromium/components/viz/service/display_embedder/display_provider.h
new file mode 100644
index 00000000000..a6cba01d045
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/display_provider.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_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_PROVIDER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_PROVIDER_H_
+
+#include <memory>
+
+#include "gpu/ipc/common/surface_handle.h"
+
+namespace cc {
+class BeginFrameSource;
+class FrameSinkId;
+} // namespace cc
+
+namespace viz {
+class Display;
+
+// Handles creating Display and related classes for FrameSinkManagerImpl.
+class DisplayProvider {
+ public:
+ virtual ~DisplayProvider() {}
+
+ // Creates a new Display for |surface_handle| with |frame_sink_id|. Will
+ // also create cc::BeginFrameSource and return it in |begin_frame_source|.
+ virtual std::unique_ptr<Display> CreateDisplay(
+ const FrameSinkId& frame_sink_id,
+ gpu::SurfaceHandle surface_handle,
+ std::unique_ptr<cc::BeginFrameSource>* begin_frame_source) = 0;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_PROVIDER_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
new file mode 100644
index 00000000000..57f6f16e8bb
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -0,0 +1,109 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/gpu_display_provider.h"
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "cc/base/switches.h"
+#include "cc/output/texture_mailbox_deleter.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/gpu/in_process_context_provider.h"
+#include "components/viz/service/display/display.h"
+#include "components/viz/service/display/display_scheduler.h"
+#include "components/viz/service/display_embedder/display_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 "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/service/image_factory.h"
+#include "gpu/ipc/service/gpu_channel_manager.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+
+#if defined(USE_OZONE)
+#include "components/viz/service/display_embedder/display_output_surface_ozone.h"
+#include "gpu/command_buffer/client/gles2_interface.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(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_service,
+ gpu::GpuChannelManager* gpu_channel_manager)
+ : gpu_service_(std::move(gpu_service)),
+ gpu_memory_buffer_manager_(
+ base::MakeUnique<InProcessGpuMemoryBufferManager>(
+ gpu_channel_manager)),
+ image_factory_(GetImageFactory(gpu_channel_manager)),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+
+GpuDisplayProvider::~GpuDisplayProvider() = default;
+
+std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
+ const FrameSinkId& frame_sink_id,
+ gpu::SurfaceHandle surface_handle,
+ std::unique_ptr<cc::BeginFrameSource>* begin_frame_source) {
+ auto synthetic_begin_frame_source =
+ base::MakeUnique<cc::DelayBasedBeginFrameSource>(
+ base::MakeUnique<cc::DelayBasedTimeSource>(task_runner_.get()));
+
+ scoped_refptr<InProcessContextProvider> context_provider =
+ new InProcessContextProvider(gpu_service_, surface_handle,
+ gpu_memory_buffer_manager_.get(),
+ image_factory_, gpu::SharedMemoryLimits(),
+ nullptr /* shared_context */);
+
+ // TODO(rjkroege): If there is something better to do than CHECK, add it.
+ CHECK(context_provider->BindToCurrentThread());
+
+ std::unique_ptr<cc::OutputSurface> display_output_surface;
+ if (context_provider->ContextCapabilities().surfaceless) {
+#if defined(USE_OZONE)
+ display_output_surface = base::MakeUnique<DisplayOutputSurfaceOzone>(
+ std::move(context_provider), surface_handle,
+ synthetic_begin_frame_source.get(), gpu_memory_buffer_manager_.get(),
+ GL_TEXTURE_2D, GL_RGB);
+#else
+ NOTREACHED();
+#endif
+ } else {
+ display_output_surface = base::MakeUnique<DisplayOutputSurface>(
+ std::move(context_provider), synthetic_begin_frame_source.get());
+ }
+
+ int max_frames_pending =
+ display_output_surface->capabilities().max_frames_pending;
+ DCHECK_GT(max_frames_pending, 0);
+
+ auto scheduler = base::MakeUnique<DisplayScheduler>(
+ synthetic_begin_frame_source.get(), task_runner_.get(),
+ max_frames_pending);
+
+ RendererSettings settings;
+ settings.show_overdraw_feedback =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ cc::switches::kShowOverdrawFeedback);
+
+ // The ownership of the BeginFrameSource is transfered to the caller.
+ *begin_frame_source = std::move(synthetic_begin_frame_source);
+
+ return base::MakeUnique<Display>(
+ ServerSharedBitmapManager::current(), gpu_memory_buffer_manager_.get(),
+ settings, frame_sink_id, std::move(display_output_surface),
+ std::move(scheduler),
+ base::MakeUnique<cc::TextureMailboxDeleter>(task_runner_.get()));
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gpu_display_provider.h b/chromium/components/viz/service/display_embedder/gpu_display_provider.h
new file mode 100644
index 00000000000..f9c320ccb18
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/gpu_display_provider.h
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GPU_DISPLAY_PROVIDER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GPU_DISPLAY_PROVIDER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/service/display_embedder/display_provider.h"
+#include "components/viz/service/viz_service_export.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "gpu/ipc/in_process_command_buffer.h"
+
+namespace gpu {
+class GpuChannelManager;
+class ImageFactory;
+} // namespace gpu
+
+namespace viz {
+class Display;
+
+// In-process implementation of DisplayProvider.
+class VIZ_SERVICE_EXPORT GpuDisplayProvider
+ : public NON_EXPORTED_BASE(DisplayProvider) {
+ public:
+ GpuDisplayProvider(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_service,
+ gpu::GpuChannelManager* gpu_channel_manager);
+ ~GpuDisplayProvider() override;
+
+ // DisplayProvider:
+ std::unique_ptr<Display> CreateDisplay(
+ const FrameSinkId& frame_sink_id,
+ gpu::SurfaceHandle surface_handle,
+ std::unique_ptr<cc::BeginFrameSource>* begin_frame_source) override;
+
+ private:
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_service_;
+ std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
+ gpu::ImageFactory* image_factory_;
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuDisplayProvider);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GPU_DISPLAY_PROVIDER_H_
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
new file mode 100644
index 00000000000..fd4d44d5266
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h"
+
+#include "gpu/ipc/client/gpu_memory_buffer_impl.h"
+#include "gpu/ipc/service/gpu_channel_manager.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+
+namespace viz {
+
+InProcessGpuMemoryBufferManager::InProcessGpuMemoryBufferManager(
+ gpu::GpuChannelManager* channel_manager)
+ : client_id_(1), channel_manager_(channel_manager), weak_factory_(this) {
+ weak_ptr_ = weak_factory_.GetWeakPtr();
+}
+
+InProcessGpuMemoryBufferManager::~InProcessGpuMemoryBufferManager() {}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+InProcessGpuMemoryBufferManager::CreateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) {
+ gfx::GpuMemoryBufferId id(next_gpu_memory_id_++);
+ gfx::GpuMemoryBufferHandle buffer_handle =
+ channel_manager_->gpu_memory_buffer_factory()->CreateGpuMemoryBuffer(
+ id, size, format, usage, client_id_, surface_handle);
+ return gpu::GpuMemoryBufferImpl::CreateFromHandle(
+ buffer_handle, size, format, usage,
+ base::Bind(&InProcessGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
+ weak_ptr_, id, client_id_));
+}
+
+void InProcessGpuMemoryBufferManager::SetDestructionSyncToken(
+ gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) {
+ static_cast<gpu::GpuMemoryBufferImpl*>(buffer)->set_destruction_sync_token(
+ sync_token);
+}
+
+void InProcessGpuMemoryBufferManager::DestroyGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gpu::SyncToken& sync_token) {
+ channel_manager_->DestroyGpuMemoryBuffer(id, client_id, sync_token);
+}
+
+} // namespace viz
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
new file mode 100644
index 00000000000..4074531d145
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_IN_PROCESS_GPU_MEMORY_BUFFER_MANAGER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_IN_PROCESS_GPU_MEMORY_BUFFER_MANAGER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+
+namespace gpu {
+class GpuChannelManager;
+}
+
+namespace viz {
+
+class InProcessGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
+ public:
+ explicit InProcessGpuMemoryBufferManager(
+ gpu::GpuChannelManager* channel_manager);
+
+ ~InProcessGpuMemoryBufferManager() override;
+
+ // 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;
+
+ private:
+ void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gpu::SyncToken& sync_token);
+ const int client_id_;
+ int next_gpu_memory_id_ = 1;
+ gpu::GpuChannelManager* channel_manager_;
+ base::WeakPtr<InProcessGpuMemoryBufferManager> weak_ptr_;
+ base::WeakPtrFactory<InProcessGpuMemoryBufferManager> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(InProcessGpuMemoryBufferManager);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_IN_PROCESS_GPU_MEMORY_BUFFER_MANAGER_H_
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
new file mode 100644
index 00000000000..07a45c9b72b
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
@@ -0,0 +1,193 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace viz {
+
+class BitmapData : public base::RefCountedThreadSafe<BitmapData> {
+ public:
+ explicit BitmapData(size_t buffer_size) : buffer_size(buffer_size) {}
+ std::unique_ptr<base::SharedMemory> memory;
+ std::unique_ptr<uint8_t[]> pixels;
+ size_t buffer_size;
+
+ private:
+ friend class base::RefCountedThreadSafe<BitmapData>;
+ ~BitmapData() {}
+ DISALLOW_COPY_AND_ASSIGN(BitmapData);
+};
+
+namespace {
+
+class ServerSharedBitmap : public SharedBitmap {
+ public:
+ ServerSharedBitmap(uint8_t* pixels,
+ scoped_refptr<BitmapData> bitmap_data,
+ const SharedBitmapId& id,
+ ServerSharedBitmapManager* manager)
+ : SharedBitmap(pixels, id, 0 /* sequence_number */),
+ bitmap_data_(bitmap_data),
+ manager_(manager) {}
+
+ ~ServerSharedBitmap() override {
+ if (manager_)
+ manager_->FreeSharedMemoryFromMap(id());
+ }
+
+ // SharedBitmap:
+ base::SharedMemoryHandle GetSharedMemoryHandle() const override {
+ if (!bitmap_data_->memory)
+ return base::SharedMemoryHandle();
+ return bitmap_data_->memory->handle();
+ }
+
+ private:
+ scoped_refptr<BitmapData> bitmap_data_;
+ ServerSharedBitmapManager* manager_;
+};
+
+} // namespace
+
+base::LazyInstance<ServerSharedBitmapManager>::DestructorAtExit
+ g_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
+
+ServerSharedBitmapManager::ServerSharedBitmapManager() = default;
+
+ServerSharedBitmapManager::~ServerSharedBitmapManager() {
+ DCHECK(handle_map_.empty());
+}
+
+ServerSharedBitmapManager* ServerSharedBitmapManager::current() {
+ return g_shared_memory_manager.Pointer();
+}
+
+std::unique_ptr<SharedBitmap> ServerSharedBitmapManager::AllocateSharedBitmap(
+ const gfx::Size& size) {
+ base::AutoLock lock(lock_);
+ size_t bitmap_size;
+ if (!SharedBitmap::SizeInBytes(size, &bitmap_size))
+ return nullptr;
+
+ scoped_refptr<BitmapData> data(new BitmapData(bitmap_size));
+ // Bitmaps allocated in server don't need to be shared to other processes, so
+ // allocate them with new instead.
+ data->pixels = std::unique_ptr<uint8_t[]>(new uint8_t[bitmap_size]);
+
+ SharedBitmapId id = SharedBitmap::GenerateId();
+ handle_map_[id] = data;
+ return base::MakeUnique<ServerSharedBitmap>(data->pixels.get(), data, id,
+ this);
+}
+
+std::unique_ptr<SharedBitmap> ServerSharedBitmapManager::GetSharedBitmapFromId(
+ const gfx::Size& size,
+ const SharedBitmapId& id) {
+ base::AutoLock lock(lock_);
+ auto it = handle_map_.find(id);
+ if (it == handle_map_.end())
+ return nullptr;
+
+ BitmapData* data = it->second.get();
+
+ size_t bitmap_size;
+ if (!SharedBitmap::SizeInBytes(size, &bitmap_size) ||
+ bitmap_size > data->buffer_size)
+ return nullptr;
+
+ if (data->pixels) {
+ return base::MakeUnique<ServerSharedBitmap>(data->pixels.get(), data, id,
+ nullptr);
+ }
+ if (!data->memory->memory()) {
+ return nullptr;
+ }
+
+ return base::MakeUnique<ServerSharedBitmap>(
+ static_cast<uint8_t*>(data->memory->memory()), data, id, nullptr);
+}
+
+bool ServerSharedBitmapManager::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) {
+ base::AutoLock lock(lock_);
+
+ for (const auto& bitmap : handle_map_) {
+ base::trace_event::MemoryAllocatorDump* dump =
+ pmd->CreateAllocatorDump(base::StringPrintf(
+ "sharedbitmap/%s",
+ base::HexEncode(bitmap.first.name, sizeof(bitmap.first.name))
+ .c_str()));
+ if (!dump)
+ return false;
+
+ dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+ base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+ bitmap.second->buffer_size);
+
+ // Generate a global GUID used to share this allocation with renderer
+ // processes.
+ auto guid = GetSharedBitmapGUIDForTracing(bitmap.first);
+ base::UnguessableToken shared_memory_guid;
+ if (bitmap.second->memory) {
+ shared_memory_guid = bitmap.second->memory->mapped_id();
+ if (!shared_memory_guid.is_empty()) {
+ pmd->CreateSharedMemoryOwnershipEdge(
+ dump->guid(), guid, shared_memory_guid, 0 /* importance*/);
+ }
+ } else {
+ pmd->CreateSharedGlobalAllocatorDump(guid);
+ pmd->AddOwnershipEdge(dump->guid(), guid);
+ }
+ }
+
+ return true;
+}
+
+bool ServerSharedBitmapManager::ChildAllocatedSharedBitmap(
+ size_t buffer_size,
+ const base::SharedMemoryHandle& handle,
+ const SharedBitmapId& id) {
+ base::AutoLock lock(lock_);
+ if (handle_map_.find(id) != handle_map_.end())
+ return false;
+ auto data = base::MakeRefCounted<BitmapData>(buffer_size);
+ data->memory = base::MakeUnique<base::SharedMemory>(handle, false);
+ data->memory->Map(data->buffer_size);
+ data->memory->Close();
+ handle_map_[id] = std::move(data);
+ return true;
+}
+
+void ServerSharedBitmapManager::ChildDeletedSharedBitmap(
+ const SharedBitmapId& id) {
+ base::AutoLock lock(lock_);
+ handle_map_.erase(id);
+}
+
+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
new file mode 100644
index 00000000000..cd801a181e6
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SERVER_SHARED_BITMAP_MANAGER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SERVER_SHARED_BITMAP_MANAGER_H_
+
+#include <memory>
+#include <unordered_map>
+
+#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/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.
+class VIZ_SERVICE_EXPORT ServerSharedBitmapManager
+ : public NON_EXPORTED_BASE(SharedBitmapManager),
+ public base::trace_event::MemoryDumpProvider {
+ public:
+ ServerSharedBitmapManager();
+ ~ServerSharedBitmapManager() override;
+
+ static ServerSharedBitmapManager* current();
+
+ // SharedBitmapManager implementation.
+ std::unique_ptr<SharedBitmap> AllocateSharedBitmap(
+ const gfx::Size& size) override;
+ std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
+ const gfx::Size& size,
+ const SharedBitmapId&) override;
+
+ // base::trace_event::MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) override;
+
+ size_t AllocatedBitmapCount() const;
+
+ void FreeSharedMemoryFromMap(const SharedBitmapId& id);
+
+ private:
+ friend class SharedBitmapAllocationNotifierImpl;
+
+ bool ChildAllocatedSharedBitmap(size_t buffer_size,
+ const base::SharedMemoryHandle& handle,
+ const SharedBitmapId& id);
+ void ChildDeletedSharedBitmap(const SharedBitmapId& id);
+
+ mutable base::Lock lock_;
+
+ std::unordered_map<SharedBitmapId,
+ scoped_refptr<BitmapData>,
+ SharedBitmapIdHash>
+ handle_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerSharedBitmapManager);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SERVER_SHARED_BITMAP_MANAGER_H_
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
new file mode 100644
index 00000000000..0cf729b3d55
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+namespace {
+
+class ServerSharedBitmapManagerTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ manager_ = base::MakeUnique<ServerSharedBitmapManager>();
+ }
+
+ void TearDown() override { manager_.reset(); }
+
+ ServerSharedBitmapManager* manager() const { return manager_.get(); }
+
+ private:
+ std::unique_ptr<ServerSharedBitmapManager> manager_;
+};
+
+TEST_F(ServerSharedBitmapManagerTest, TestCreate) {
+ gfx::Size bitmap_size(1, 1);
+ size_t size_in_bytes;
+ EXPECT_TRUE(SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
+ std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
+ bitmap->CreateAndMapAnonymous(size_in_bytes);
+ memset(bitmap->memory(), 0xff, size_in_bytes);
+ SharedBitmapId id = SharedBitmap::GenerateId();
+
+ SharedBitmapAllocationNotifierImpl notifier(manager());
+ base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
+ notifier.ChildAllocatedSharedBitmap(size_in_bytes, handle, id);
+
+ std::unique_ptr<SharedBitmap> large_bitmap;
+ large_bitmap = manager()->GetSharedBitmapFromId(gfx::Size(1024, 1024), id);
+ EXPECT_TRUE(large_bitmap.get() == NULL);
+
+ std::unique_ptr<SharedBitmap> very_large_bitmap;
+ very_large_bitmap =
+ manager()->GetSharedBitmapFromId(gfx::Size(1, (1 << 30) | 1), id);
+ EXPECT_TRUE(very_large_bitmap.get() == NULL);
+
+ std::unique_ptr<SharedBitmap> negative_size_bitmap;
+ negative_size_bitmap =
+ manager()->GetSharedBitmapFromId(gfx::Size(-1, 1024), id);
+ EXPECT_TRUE(negative_size_bitmap.get() == NULL);
+
+ SharedBitmapId id2 = SharedBitmap::GenerateId();
+ std::unique_ptr<SharedBitmap> invalid_bitmap;
+ invalid_bitmap = manager()->GetSharedBitmapFromId(bitmap_size, id2);
+ EXPECT_TRUE(invalid_bitmap.get() == NULL);
+
+ std::unique_ptr<SharedBitmap> shared_bitmap;
+ shared_bitmap = manager()->GetSharedBitmapFromId(bitmap_size, id);
+ ASSERT_TRUE(shared_bitmap.get() != NULL);
+ EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), 4), 0);
+
+ std::unique_ptr<SharedBitmap> large_bitmap2;
+ large_bitmap2 = manager()->GetSharedBitmapFromId(gfx::Size(1024, 1024), id);
+ EXPECT_TRUE(large_bitmap2.get() == NULL);
+
+ std::unique_ptr<SharedBitmap> shared_bitmap2;
+ shared_bitmap2 = manager()->GetSharedBitmapFromId(bitmap_size, id);
+ EXPECT_TRUE(shared_bitmap2->pixels() == shared_bitmap->pixels());
+ shared_bitmap2.reset();
+ EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
+ 0);
+
+ notifier.DidDeleteSharedBitmap(id);
+
+ memset(bitmap->memory(), 0, size_in_bytes);
+
+ EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
+ 0);
+ bitmap.reset();
+ shared_bitmap.reset();
+}
+
+TEST_F(ServerSharedBitmapManagerTest, ServiceDestroyed) {
+ gfx::Size bitmap_size(1, 1);
+ size_t size_in_bytes;
+ EXPECT_TRUE(SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
+ std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
+ bitmap->CreateAndMapAnonymous(size_in_bytes);
+ memset(bitmap->memory(), 0xff, size_in_bytes);
+ SharedBitmapId id = SharedBitmap::GenerateId();
+
+ // This outlives the SharedBitmapAllocationNotifier.
+ std::unique_ptr<SharedBitmap> shared_bitmap;
+
+ {
+ SharedBitmapAllocationNotifierImpl notifier(manager());
+ base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
+ notifier.ChildAllocatedSharedBitmap(size_in_bytes, handle, id);
+
+ shared_bitmap = manager()->GetSharedBitmapFromId(bitmap_size, id);
+ ASSERT_TRUE(shared_bitmap.get() != NULL);
+
+ EXPECT_EQ(1u, manager()->AllocatedBitmapCount());
+ }
+ EXPECT_EQ(0u, manager()->AllocatedBitmapCount());
+
+ std::unique_ptr<SharedBitmap> shared_bitmap2;
+ shared_bitmap2 = manager()->GetSharedBitmapFromId(bitmap_size, id);
+ EXPECT_FALSE(!!shared_bitmap2);
+ EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
+ 0);
+}
+
+TEST_F(ServerSharedBitmapManagerTest, AddDuplicate) {
+ gfx::Size bitmap_size(1, 1);
+ size_t size_in_bytes;
+ EXPECT_TRUE(SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
+ std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
+ bitmap->CreateAndMapAnonymous(size_in_bytes);
+ memset(bitmap->memory(), 0xff, size_in_bytes);
+ SharedBitmapId id = SharedBitmap::GenerateId();
+
+ SharedBitmapAllocationNotifierImpl notifier(manager());
+
+ base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
+ notifier.ChildAllocatedSharedBitmap(size_in_bytes, handle, id);
+
+ std::unique_ptr<base::SharedMemory> bitmap2(new base::SharedMemory());
+ bitmap2->CreateAndMapAnonymous(size_in_bytes);
+ memset(bitmap2->memory(), 0x00, size_in_bytes);
+
+ notifier.ChildAllocatedSharedBitmap(size_in_bytes, bitmap2->handle(), id);
+
+ std::unique_ptr<SharedBitmap> shared_bitmap;
+ shared_bitmap = manager()->GetSharedBitmapFromId(bitmap_size, id);
+ ASSERT_TRUE(shared_bitmap.get() != NULL);
+ EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
+ 0);
+ notifier.DidDeleteSharedBitmap(id);
+}
+
+TEST_F(ServerSharedBitmapManagerTest, SharedMemoryHandle) {
+ gfx::Size bitmap_size(1, 1);
+ size_t size_in_bytes;
+ EXPECT_TRUE(SharedBitmap::SizeInBytes(bitmap_size, &size_in_bytes));
+ std::unique_ptr<base::SharedMemory> bitmap(new base::SharedMemory());
+ auto shared_memory_guid = bitmap->handle().GetGUID();
+ bitmap->CreateAndMapAnonymous(size_in_bytes);
+ memset(bitmap->memory(), 0xff, size_in_bytes);
+ SharedBitmapId id = SharedBitmap::GenerateId();
+
+ SharedBitmapAllocationNotifierImpl notifier(manager());
+
+ base::SharedMemoryHandle handle = bitmap->handle().Duplicate();
+ notifier.ChildAllocatedSharedBitmap(size_in_bytes, handle, id);
+
+ std::unique_ptr<SharedBitmap> shared_bitmap;
+ shared_bitmap = manager()->GetSharedBitmapFromId(gfx::Size(1, 1), id);
+ EXPECT_EQ(shared_bitmap->GetSharedMemoryHandle().GetGUID(),
+ shared_memory_guid);
+
+ notifier.DidDeleteSharedBitmap(id);
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc b/chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc
new file mode 100644
index 00000000000..d320a3dee75
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc
@@ -0,0 +1,85 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h"
+
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace viz {
+
+SharedBitmapAllocationNotifierImpl::SharedBitmapAllocationNotifierImpl(
+ ServerSharedBitmapManager* manager)
+ : manager_(manager), binding_(this) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+SharedBitmapAllocationNotifierImpl::~SharedBitmapAllocationNotifierImpl() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ ChildDied();
+}
+
+void SharedBitmapAllocationNotifierImpl::Bind(
+ cc::mojom::SharedBitmapAllocationNotifierRequest request) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (binding_.is_bound()) {
+ DLOG(ERROR) << "Only one SharedBitmapAllocationNotifierRequest is "
+ << "expected from the renderer.";
+ return;
+ }
+ binding_.Bind(std::move(request));
+}
+
+void SharedBitmapAllocationNotifierImpl::DidAllocateSharedBitmap(
+ mojo::ScopedSharedBufferHandle buffer,
+ const SharedBitmapId& id) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ base::SharedMemoryHandle memory_handle;
+ size_t size;
+ MojoResult result = mojo::UnwrapSharedMemoryHandle(
+ std::move(buffer), &memory_handle, &size, NULL);
+ DCHECK_EQ(result, MOJO_RESULT_OK);
+ this->ChildAllocatedSharedBitmap(size, memory_handle, id);
+ last_sequence_number_++;
+ for (SharedBitmapAllocationObserver& observer : observers_)
+ observer.DidAllocateSharedBitmap(last_sequence_number_);
+}
+
+void SharedBitmapAllocationNotifierImpl::DidDeleteSharedBitmap(
+ const SharedBitmapId& id) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ manager_->ChildDeletedSharedBitmap(id);
+ owned_bitmaps_.erase(id);
+}
+
+void SharedBitmapAllocationNotifierImpl::ChildAllocatedSharedBitmap(
+ size_t buffer_size,
+ const base::SharedMemoryHandle& handle,
+ const SharedBitmapId& id) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (manager_->ChildAllocatedSharedBitmap(buffer_size, handle, id))
+ owned_bitmaps_.insert(id);
+}
+
+void SharedBitmapAllocationNotifierImpl::ChildDied() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ for (const auto& id : owned_bitmaps_)
+ manager_->ChildDeletedSharedBitmap(id);
+ owned_bitmaps_.clear();
+ binding_.Close();
+}
+
+void SharedBitmapAllocationNotifierImpl::AddObserver(
+ SharedBitmapAllocationObserver* observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ observers_.AddObserver(observer);
+}
+
+void SharedBitmapAllocationNotifierImpl::RemoveObserver(
+ SharedBitmapAllocationObserver* observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ observers_.RemoveObserver(observer);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h b/chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h
new file mode 100644
index 00000000000..4e9330a5759
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SHARED_BITMAP_ALLOCATION_NOTIFIER_IMPL_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SHARED_BITMAP_ALLOCATION_NOTIFIER_IMPL_H_
+
+#include <unordered_set>
+
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "cc/ipc/shared_bitmap_allocation_notifier.mojom.h"
+#include "components/viz/common/quads/shared_bitmap.h"
+#include "components/viz/service/viz_service_export.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace viz {
+class ServerSharedBitmapManager;
+
+class SharedBitmapAllocationObserver {
+ public:
+ virtual void DidAllocateSharedBitmap(uint32_t sequence_number) = 0;
+};
+
+class VIZ_SERVICE_EXPORT SharedBitmapAllocationNotifierImpl
+ : NON_EXPORTED_BASE(public cc::mojom::SharedBitmapAllocationNotifier) {
+ public:
+ explicit SharedBitmapAllocationNotifierImpl(
+ ServerSharedBitmapManager* manager);
+
+ ~SharedBitmapAllocationNotifierImpl() override;
+
+ void AddObserver(SharedBitmapAllocationObserver* observer);
+ void RemoveObserver(SharedBitmapAllocationObserver* observer);
+
+ void Bind(cc::mojom::SharedBitmapAllocationNotifierRequest request);
+
+ // cc::mojom::SharedBitmapAllocationNotifier overrides:
+ void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
+ const SharedBitmapId& id) override;
+ void DidDeleteSharedBitmap(const SharedBitmapId& id) override;
+
+ void ChildAllocatedSharedBitmap(size_t buffer_size,
+ const base::SharedMemoryHandle& handle,
+ const SharedBitmapId& id);
+
+ void ChildDied();
+
+ uint32_t last_sequence_number() const { return last_sequence_number_; }
+
+ private:
+ THREAD_CHECKER(thread_checker_);
+ ServerSharedBitmapManager* const manager_;
+ mojo::Binding<cc::mojom::SharedBitmapAllocationNotifier> binding_;
+ std::unordered_set<SharedBitmapId, SharedBitmapIdHash> owned_bitmaps_;
+ base::ObserverList<SharedBitmapAllocationObserver> observers_;
+ uint32_t last_sequence_number_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedBitmapAllocationNotifierImpl);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SHARED_BITMAP_ALLOCATION_NOTIFIER_IMPL_H_
diff --git a/chromium/components/viz/service/frame_sinks/DEPS b/chromium/components/viz/service/frame_sinks/DEPS
new file mode 100644
index 00000000000..af562e205c4
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+cc/base",
+ "+cc/ipc",
+ "+cc/surfaces",
+ "+cc/scheduler",
+ "+gpu/ipc/common",
+ "+mojo/public/cpp/bindings",
+]
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
new file mode 100644
index 00000000000..c13c567fc5b
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -0,0 +1,385 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "cc/output/compositor_frame.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_reference.h"
+#include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/service/display/display.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_client.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+
+namespace viz {
+
+// static
+std::unique_ptr<CompositorFrameSinkSupport> CompositorFrameSinkSupport::Create(
+ CompositorFrameSinkSupportClient* client,
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_tokens) {
+ std::unique_ptr<CompositorFrameSinkSupport> support =
+ base::WrapUnique(new CompositorFrameSinkSupport(
+ client, frame_sink_id, is_root, handles_frame_sink_id_invalidation,
+ needs_sync_tokens));
+ support->Init(frame_sink_manager);
+ return support;
+}
+
+CompositorFrameSinkSupport::~CompositorFrameSinkSupport() {
+ if (!destruction_callback_.is_null())
+ std::move(destruction_callback_).Run();
+
+ // Unregister |this| as a cc::BeginFrameObserver so that the
+ // cc::BeginFrameSource does not call into |this| after it's deleted.
+ SetNeedsBeginFrame(false);
+
+ // For display root surfaces the surface is no longer going to be visible.
+ // Make it unreachable from the top-level root.
+ if (referenced_local_surface_id_.has_value()) {
+ auto reference = MakeTopLevelRootReference(
+ SurfaceId(frame_sink_id_, referenced_local_surface_id_.value()));
+ surface_manager_->RemoveSurfaceReferences({reference});
+ }
+
+ EvictCurrentSurface();
+ frame_sink_manager_->UnregisterFrameSinkManagerClient(frame_sink_id_);
+ if (handles_frame_sink_id_invalidation_)
+ surface_manager_->InvalidateFrameSinkId(frame_sink_id_);
+}
+
+void CompositorFrameSinkSupport::SetDestructionCallback(
+ base::OnceCallback<void()> callback) {
+ destruction_callback_ = std::move(callback);
+}
+
+void CompositorFrameSinkSupport::OnSurfaceActivated(cc::Surface* surface) {
+ DCHECK(surface->HasActiveFrame());
+ const cc::CompositorFrame& frame = surface->GetActiveFrame();
+ if (!seen_first_frame_activation_) {
+ // cc::SurfaceCreated only applies for the first cc::Surface activation.
+ seen_first_frame_activation_ = true;
+
+ gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size();
+ surface_manager_->SurfaceCreated(SurfaceInfo(
+ surface->surface_id(), frame.metadata.device_scale_factor, frame_size));
+ }
+ // Fire cc::SurfaceCreated first so that a temporary reference is added before
+ // it is potentially transformed into a real reference by the client.
+ DCHECK(surface->active_referenced_surfaces());
+ UpdateSurfaceReferences(surface->surface_id().local_surface_id(),
+ *surface->active_referenced_surfaces());
+ if (!surface_manager_->SurfaceModified(surface->surface_id(),
+ frame.metadata.begin_frame_ack)) {
+ TRACE_EVENT_INSTANT0("cc", "Damage not visible.", TRACE_EVENT_SCOPE_THREAD);
+ surface->RunDrawCallback();
+ }
+ surface_manager_->SurfaceActivated(surface);
+}
+
+void CompositorFrameSinkSupport::RefResources(
+ const std::vector<cc::TransferableResource>& resources) {
+ surface_resource_holder_.RefResources(resources);
+}
+
+void CompositorFrameSinkSupport::UnrefResources(
+ const std::vector<cc::ReturnedResource>& resources) {
+ surface_resource_holder_.UnrefResources(resources);
+}
+
+void CompositorFrameSinkSupport::ReturnResources(
+ const std::vector<cc::ReturnedResource>& resources) {
+ if (resources.empty())
+ return;
+ if (!ack_pending_count_ && client_) {
+ client_->ReclaimResources(resources);
+ return;
+ }
+
+ std::copy(resources.begin(), resources.end(),
+ std::back_inserter(surface_returned_resources_));
+}
+
+void CompositorFrameSinkSupport::ReceiveFromChild(
+ const std::vector<cc::TransferableResource>& resources) {
+ surface_resource_holder_.ReceiveFromChild(resources);
+}
+
+void CompositorFrameSinkSupport::SetBeginFrameSource(
+ cc::BeginFrameSource* begin_frame_source) {
+ if (begin_frame_source_ && added_frame_observer_) {
+ begin_frame_source_->RemoveObserver(this);
+ added_frame_observer_ = false;
+ }
+ begin_frame_source_ = begin_frame_source;
+ UpdateNeedsBeginFramesInternal();
+}
+
+void CompositorFrameSinkSupport::EvictCurrentSurface() {
+ if (!current_surface_id_.is_valid())
+ return;
+ SurfaceId to_destroy_surface_id = current_surface_id_;
+ current_surface_id_ = SurfaceId();
+ surface_manager_->DestroySurface(to_destroy_surface_id);
+}
+
+void CompositorFrameSinkSupport::SetNeedsBeginFrame(bool needs_begin_frame) {
+ needs_begin_frame_ = needs_begin_frame;
+ UpdateNeedsBeginFramesInternal();
+}
+
+void CompositorFrameSinkSupport::DidNotProduceFrame(
+ const cc::BeginFrameAck& ack) {
+ TRACE_EVENT2("cc", "CompositorFrameSinkSupport::DidNotProduceFrame",
+ "ack.source_id", ack.source_id, "ack.sequence_number",
+ ack.sequence_number);
+ DCHECK_GE(ack.sequence_number, cc::BeginFrameArgs::kStartingFrameNumber);
+
+ // |has_damage| is not transmitted, but false by default.
+ DCHECK(!ack.has_damage);
+
+ if (current_surface_id_.is_valid())
+ surface_manager_->SurfaceModified(current_surface_id_, ack);
+
+ if (begin_frame_source_)
+ begin_frame_source_->DidFinishFrame(this);
+}
+
+bool CompositorFrameSinkSupport::SubmitCompositorFrame(
+ const LocalSurfaceId& local_surface_id,
+ cc::CompositorFrame frame) {
+ TRACE_EVENT0("cc", "CompositorFrameSinkSupport::SubmitCompositorFrame");
+ DCHECK(local_surface_id.is_valid());
+ DCHECK(!frame.render_pass_list.empty());
+
+ ++ack_pending_count_;
+
+ // |has_damage| is not transmitted.
+ frame.metadata.begin_frame_ack.has_damage = true;
+ cc::BeginFrameAck ack = frame.metadata.begin_frame_ack;
+ DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber, ack.sequence_number);
+
+ if (!ui::LatencyInfo::Verify(frame.metadata.latency_info,
+ "RenderWidgetHostImpl::OnSwapCompositorFrame")) {
+ std::vector<ui::LatencyInfo>().swap(frame.metadata.latency_info);
+ }
+ for (ui::LatencyInfo& latency : frame.metadata.latency_info) {
+ if (latency.latency_components().size() > 0) {
+ latency.AddLatencyNumber(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT,
+ 0, 0);
+ }
+ }
+
+ cc::Surface* prev_surface =
+ surface_manager_->GetSurfaceForId(current_surface_id_);
+ cc::Surface* current_surface = nullptr;
+ if (prev_surface &&
+ local_surface_id == current_surface_id_.local_surface_id()) {
+ current_surface = prev_surface;
+ } else {
+ SurfaceId surface_id(frame_sink_id_, local_surface_id);
+ gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size();
+ float device_scale_factor = frame.metadata.device_scale_factor;
+ SurfaceInfo surface_info(surface_id, device_scale_factor, frame_size);
+
+ if (!surface_info.is_valid()) {
+ TRACE_EVENT_INSTANT0("cc", "Invalid SurfaceInfo",
+ TRACE_EVENT_SCOPE_THREAD);
+ EvictCurrentSurface();
+ std::vector<cc::ReturnedResource> resources =
+ cc::TransferableResource::ReturnResources(frame.resource_list);
+ ReturnResources(resources);
+ DidReceiveCompositorFrameAck();
+ return true;
+ }
+
+ current_surface = CreateSurface(surface_info);
+ current_surface_id_ = SurfaceId(frame_sink_id_, local_surface_id);
+ surface_manager_->SurfaceDamageExpected(current_surface->surface_id(),
+ last_begin_frame_args_);
+ }
+
+ bool result = current_surface->QueueFrame(
+ std::move(frame),
+ base::Bind(&CompositorFrameSinkSupport::DidReceiveCompositorFrameAck,
+ weak_factory_.GetWeakPtr()),
+ base::BindRepeating(&CompositorFrameSinkSupport::WillDrawSurface,
+ weak_factory_.GetWeakPtr()));
+
+ if (!result) {
+ EvictCurrentSurface();
+ return false;
+ }
+
+ if (prev_surface && prev_surface != current_surface) {
+ current_surface->SetPreviousFrameSurface(prev_surface);
+ surface_manager_->DestroySurface(prev_surface->surface_id());
+ }
+
+ if (begin_frame_source_)
+ begin_frame_source_->DidFinishFrame(this);
+
+ return true;
+}
+
+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<cc::SurfaceReference> references_to_add;
+ std::vector<cc::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 cc::SurfaceManager.
+ if (!references_to_add.empty())
+ surface_manager_->AddSurfaceReferences(references_to_add);
+ if (!references_to_remove.empty())
+ surface_manager_->RemoveSurfaceReferences(references_to_remove);
+}
+
+cc::SurfaceReference CompositorFrameSinkSupport::MakeTopLevelRootReference(
+ const SurfaceId& surface_id) {
+ return cc::SurfaceReference(surface_manager_->GetRootSurfaceId(), surface_id);
+}
+
+void CompositorFrameSinkSupport::DidReceiveCompositorFrameAck() {
+ DCHECK_GT(ack_pending_count_, 0);
+ ack_pending_count_--;
+ if (!client_)
+ return;
+
+ client_->DidReceiveCompositorFrameAck(surface_returned_resources_);
+ surface_returned_resources_.clear();
+}
+
+void CompositorFrameSinkSupport::WillDrawSurface(
+ const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) {
+ if (client_)
+ client_->WillDrawSurface(local_surface_id, damage_rect);
+}
+
+void CompositorFrameSinkSupport::ClaimTemporaryReference(
+ const SurfaceId& surface_id) {
+ surface_manager_->AssignTemporaryReference(surface_id, frame_sink_id_);
+}
+
+CompositorFrameSinkSupport::CompositorFrameSinkSupport(
+ CompositorFrameSinkSupportClient* client,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_tokens)
+ : client_(client),
+ frame_sink_id_(frame_sink_id),
+ surface_resource_holder_(this),
+ is_root_(is_root),
+ needs_sync_tokens_(needs_sync_tokens),
+ handles_frame_sink_id_invalidation_(handles_frame_sink_id_invalidation),
+ weak_factory_(this) {}
+
+void CompositorFrameSinkSupport::Init(
+ FrameSinkManagerImpl* frame_sink_manager) {
+ frame_sink_manager_ = frame_sink_manager;
+ surface_manager_ = frame_sink_manager->surface_manager();
+ if (handles_frame_sink_id_invalidation_)
+ surface_manager_->RegisterFrameSinkId(frame_sink_id_);
+ frame_sink_manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this);
+}
+
+void CompositorFrameSinkSupport::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ UpdateNeedsBeginFramesInternal();
+ if (current_surface_id_.is_valid()) {
+ surface_manager_->SurfaceDamageExpected(current_surface_id_, args);
+ }
+ last_begin_frame_args_ = args;
+ if (client_)
+ client_->OnBeginFrame(args);
+}
+
+const cc::BeginFrameArgs& CompositorFrameSinkSupport::LastUsedBeginFrameArgs()
+ const {
+ return last_begin_frame_args_;
+}
+
+void CompositorFrameSinkSupport::OnBeginFrameSourcePausedChanged(bool paused) {
+ if (client_)
+ client_->OnBeginFramePausedChanged(paused);
+}
+
+void CompositorFrameSinkSupport::UpdateNeedsBeginFramesInternal() {
+ if (!begin_frame_source_)
+ return;
+
+ if (needs_begin_frame_ == added_frame_observer_)
+ return;
+
+ added_frame_observer_ = needs_begin_frame_;
+ if (needs_begin_frame_)
+ begin_frame_source_->AddObserver(this);
+ else
+ begin_frame_source_->RemoveObserver(this);
+}
+
+cc::Surface* CompositorFrameSinkSupport::CreateSurface(
+ const SurfaceInfo& surface_info) {
+ seen_first_frame_activation_ = false;
+ return surface_manager_->CreateSurface(
+ weak_factory_.GetWeakPtr(), surface_info,
+ frame_sink_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_);
+}
+
+void CompositorFrameSinkSupport::RequestCopyOfSurface(
+ std::unique_ptr<cc::CopyOutputRequest> copy_request) {
+ if (!current_surface_id_.is_valid())
+ return;
+ cc::Surface* current_surface =
+ surface_manager_->GetSurfaceForId(current_surface_id_);
+ current_surface->RequestCopyOfOutput(std::move(copy_request));
+ cc::BeginFrameAck ack;
+ ack.has_damage = true;
+ if (current_surface->HasActiveFrame())
+ surface_manager_->SurfaceModified(current_surface->surface_id(), ack);
+}
+
+cc::Surface* CompositorFrameSinkSupport::GetCurrentSurfaceForTesting() {
+ return surface_manager_->GetSurfaceForId(current_surface_id_);
+}
+
+} // 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
new file mode 100644
index 00000000000..ccf7f398c64
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -0,0 +1,173 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_H_
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/surface_client.h"
+#include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_client.h"
+#include "components/viz/service/frame_sinks/referenced_surface_tracker.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/viz_service_export.h"
+
+namespace cc {
+class Surface;
+class SurfaceManager;
+} // namespace cc
+
+namespace viz {
+
+class FrameSinkManagerImpl;
+class CompositorFrameSinkSupportClient;
+
+class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
+ : public cc::BeginFrameObserver,
+ public SurfaceResourceHolderClient,
+ public FrameSinkManagerClient,
+ public cc::SurfaceClient {
+ public:
+ static std::unique_ptr<CompositorFrameSinkSupport> Create(
+ CompositorFrameSinkSupportClient* client,
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_tokens);
+
+ ~CompositorFrameSinkSupport() override;
+
+ const FrameSinkId& frame_sink_id() const { return frame_sink_id_; }
+
+ FrameSinkManagerImpl* frame_sink_manager() { return frame_sink_manager_; }
+ cc::SurfaceManager* surface_manager() { return surface_manager_; }
+
+ void SetDestructionCallback(base::OnceCallback<void()> callback);
+
+ // SurfaceClient implementation.
+ void OnSurfaceActivated(cc::Surface* surface) override;
+ void RefResources(
+ const std::vector<cc::TransferableResource>& resources) override;
+ void UnrefResources(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void ReturnResources(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void ReceiveFromChild(
+ const std::vector<cc::TransferableResource>& resources) override;
+
+ // FrameSinkManagerClient implementation.
+ void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override;
+
+ void EvictCurrentSurface();
+ void SetNeedsBeginFrame(bool needs_begin_frame);
+ void DidNotProduceFrame(const cc::BeginFrameAck& ack);
+ bool SubmitCompositorFrame(const LocalSurfaceId& local_surface_id,
+ cc::CompositorFrame frame);
+ void RequestCopyOfSurface(std::unique_ptr<cc::CopyOutputRequest> request);
+ void ClaimTemporaryReference(const SurfaceId& surface_id);
+
+ cc::Surface* GetCurrentSurfaceForTesting();
+
+ protected:
+ CompositorFrameSinkSupport(CompositorFrameSinkSupportClient* client,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_tokens);
+
+ void Init(FrameSinkManagerImpl* frame_sink_manager);
+
+ private:
+ // 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|.
+ cc::SurfaceReference MakeTopLevelRootReference(const SurfaceId& surface_id);
+
+ void DidReceiveCompositorFrameAck();
+ void WillDrawSurface(const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect);
+
+ // BeginFrameObserver implementation.
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
+
+ void UpdateNeedsBeginFramesInternal();
+ cc::Surface* CreateSurface(const SurfaceInfo& surface_info);
+
+ CompositorFrameSinkSupportClient* const client_;
+
+ FrameSinkManagerImpl* frame_sink_manager_ = nullptr;
+ cc::SurfaceManager* surface_manager_ = nullptr;
+
+ const FrameSinkId frame_sink_id_;
+ SurfaceId current_surface_id_;
+
+ // If this contains a value then a surface reference from the top-level root
+ // to SurfaceId(frame_sink_id_, referenced_local_surface_id_.value()) was
+ // added. This will not contain a value if |is_root_| is false.
+ base::Optional<LocalSurfaceId> referenced_local_surface_id_;
+
+ SurfaceResourceHolder surface_resource_holder_;
+
+ // Counts the number of CompositorFrames that have been submitted and have not
+ // yet received an ACK.
+ int ack_pending_count_ = 0;
+ std::vector<cc::ReturnedResource> surface_returned_resources_;
+
+ // The begin frame source being observered. Null if none.
+ cc::BeginFrameSource* begin_frame_source_ = nullptr;
+
+ // The last begin frame args generated by the begin frame source.
+ cc::BeginFrameArgs last_begin_frame_args_;
+
+ // Whether a request for begin frames has been issued.
+ bool needs_begin_frame_ = false;
+
+ // Whether or not a frame observer has been added.
+ bool added_frame_observer_ = false;
+
+ const bool is_root_;
+ const bool needs_sync_tokens_;
+ bool seen_first_frame_activation_ = false;
+
+ // TODO(staraz): Remove this flag once ui::Compositor no longer needs to call
+ // RegisterFrameSinkId().
+ // A surfaceSequence's validity is bound to the lifetime of the parent
+ // FrameSink that created it. We track the lifetime of FrameSinks through
+ // RegisterFrameSinkId and InvalidateFrameSinkId. During startup and GPU
+ // restart, a SurfaceSequence created by the top most layer compositor may be
+ // used prior to the creation of the associated CompositorFrameSinkSupport.
+ // CompositorFrameSinkSupport is created asynchronously when a new GPU channel
+ // is established. Once we switch to SurfaceReferences, this ordering concern
+ // goes away and we can remove this bool.
+ const bool handles_frame_sink_id_invalidation_;
+
+ // A callback that will be run at the start of the destructor if set.
+ base::OnceCallback<void()> destruction_callback_;
+
+ base::WeakPtrFactory<CompositorFrameSinkSupport> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkSupport);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_H_
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_client.h b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_client.h
new file mode 100644
index 00000000000..792bd9dcb1d
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_client.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_CLIENT_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_CLIENT_H_
+
+#include "cc/resources/returned_resource.h"
+
+namespace cc {
+struct BeginFrameArgs;
+} // namespace cc
+
+namespace gfx {
+class Rect;
+}
+
+namespace viz {
+class LocalSurfaceId;
+
+class CompositorFrameSinkSupportClient {
+ public:
+ // Notification that the previous CompositorFrame given to
+ // SubmitCompositorFrame() has been processed and that another frame
+ // can be submitted. This provides backpressure from the display compositor
+ // so that frames are submitted only at the rate it can handle them.
+ // TODO(fsamuel): This method ought not be necessary with unified BeginFrame.
+ // However, there's a fair amount of cleanup and refactoring necessary to get
+ // rid of it.
+ virtual void DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) = 0;
+
+ // Notification for the client to generate a CompositorFrame.
+ virtual void OnBeginFrame(const cc::BeginFrameArgs& args) = 0;
+
+ // Returns resources sent to SubmitCompositorFrame to be reused or freed.
+ virtual void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) = 0;
+
+ // Called when surface is being scheduled for a draw.
+ virtual void WillDrawSurface(const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) = 0;
+
+ // Notification that there may not be OnBeginFrame calls for some time.
+ virtual void OnBeginFramePausedChanged(bool paused) = 0;
+
+ protected:
+ virtual ~CompositorFrameSinkSupportClient() {}
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_CLIENT_H_
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h
new file mode 100644
index 00000000000..91a4e805709
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_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_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_MANAGER_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_MANAGER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+
+namespace viz {
+
+class CompositorFrameSinkSupport;
+class CompositorFrameSinkSupportClient;
+class FrameSinkId;
+
+// This inteface provides a way for DirectLayerTreeFrameSink to create a
+// CompositorFrameSinkSupport.
+class CompositorFrameSinkSupportManager {
+ public:
+ virtual std::unique_ptr<CompositorFrameSinkSupport>
+ CreateCompositorFrameSinkSupport(CompositorFrameSinkSupportClient* client,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_points) = 0;
+
+ protected:
+ virtual ~CompositorFrameSinkSupportManager() {}
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_MANAGER_H_
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
new file mode 100644
index 00000000000..e876d654a54
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -0,0 +1,894 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+
+#include "base/macros.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/copy_output_request.h"
+#include "cc/output/copy_output_result.h"
+#include "cc/resources/resource_provider.h"
+#include "cc/test/begin_frame_args_test.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "cc/test/fake_external_begin_frame_source.h"
+#include "cc/test/fake_surface_observer.h"
+#include "cc/test/mock_compositor_frame_sink_support_client.h"
+#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/frame_sinks/compositor_frame_sink_support_client.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::UnorderedElementsAre;
+using testing::IsEmpty;
+using testing::SizeIs;
+using testing::Invoke;
+using testing::_;
+using testing::Eq;
+
+namespace viz {
+namespace {
+
+constexpr bool kIsRoot = true;
+constexpr bool kIsChildRoot = false;
+constexpr bool kHandlesFrameSinkIdInvalidation = true;
+constexpr bool kNeedsSyncPoints = true;
+
+constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
+constexpr FrameSinkId kAnotherArbitraryFrameSinkId(2, 2);
+constexpr FrameSinkId kYetAnotherArbitraryFrameSinkId(3, 3);
+
+const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create();
+const base::UnguessableToken kArbitrarySourceId1 =
+ base::UnguessableToken::Deserialize(0xdead, 0xbeef);
+const base::UnguessableToken kArbitrarySourceId2 =
+ base::UnguessableToken::Deserialize(0xdead, 0xbee0);
+
+gpu::SyncToken GenTestSyncToken(int id) {
+ gpu::SyncToken token;
+ token.Set(gpu::CommandBufferNamespace::GPU_IO, 0,
+ gpu::CommandBufferId::FromUnsafeValue(id), 1);
+ return token;
+}
+
+class FakeCompositorFrameSinkSupportClient
+ : public CompositorFrameSinkSupportClient {
+ public:
+ FakeCompositorFrameSinkSupportClient() = default;
+ ~FakeCompositorFrameSinkSupportClient() override = default;
+
+ void DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) override {
+ InsertResources(resources);
+ }
+
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override {}
+
+ void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) override {
+ InsertResources(resources);
+ }
+
+ void WillDrawSurface(const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) override {}
+
+ void OnBeginFramePausedChanged(bool paused) override {}
+
+ void clear_returned_resources() { returned_resources_.clear(); }
+ const std::vector<cc::ReturnedResource>& returned_resources() {
+ return returned_resources_;
+ }
+
+ private:
+ void InsertResources(const std::vector<cc::ReturnedResource>& resources) {
+ returned_resources_.insert(returned_resources_.end(), resources.begin(),
+ resources.end());
+ }
+
+ std::vector<cc::ReturnedResource> returned_resources_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeCompositorFrameSinkSupportClient);
+};
+
+class CompositorFrameSinkSupportTest : public testing::Test {
+ public:
+ CompositorFrameSinkSupportTest()
+ : support_(
+ CompositorFrameSinkSupport::Create(&fake_support_client_,
+ &manager_,
+ kArbitraryFrameSinkId,
+ kIsRoot,
+ kHandlesFrameSinkIdInvalidation,
+ kNeedsSyncPoints)),
+ begin_frame_source_(0.f, false),
+ local_surface_id_(3, kArbitraryToken),
+ frame_sync_token_(GenTestSyncToken(4)),
+ consumer_sync_token_(GenTestSyncToken(5)) {
+ manager_.surface_manager()->AddObserver(&surface_observer_);
+ support_->SetBeginFrameSource(&begin_frame_source_);
+ }
+ ~CompositorFrameSinkSupportTest() override {
+ manager_.surface_manager()->RemoveObserver(&surface_observer_);
+ support_->EvictCurrentSurface();
+ }
+
+ void SubmitCompositorFrameWithResources(cc::ResourceId* resource_ids,
+ size_t num_resource_ids) {
+ auto frame = cc::test::MakeCompositorFrame();
+ for (size_t i = 0u; i < num_resource_ids; ++i) {
+ cc::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);
+ }
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
+ local_surface_id_);
+ }
+
+ void UnrefResources(cc::ResourceId* ids_to_unref,
+ int* counts_to_unref,
+ size_t num_ids_to_unref) {
+ std::vector<cc::ReturnedResource> unref_array;
+ for (size_t i = 0; i < num_ids_to_unref; ++i) {
+ cc::ReturnedResource resource;
+ resource.sync_token = consumer_sync_token_;
+ resource.id = ids_to_unref[i];
+ resource.count = counts_to_unref[i];
+ unref_array.push_back(resource);
+ }
+ support_->UnrefResources(unref_array);
+ }
+
+ void CheckReturnedResourcesMatchExpected(
+ cc::ResourceId* expected_returned_ids,
+ int* expected_returned_counts,
+ size_t expected_resources,
+ gpu::SyncToken expected_sync_token) {
+ const std::vector<cc::ReturnedResource>& actual_resources =
+ fake_support_client_.returned_resources();
+ ASSERT_EQ(expected_resources, actual_resources.size());
+ for (size_t i = 0; i < expected_resources; ++i) {
+ cc::ReturnedResource resource = actual_resources[i];
+ EXPECT_EQ(expected_sync_token, resource.sync_token);
+ EXPECT_EQ(expected_returned_ids[i], resource.id);
+ EXPECT_EQ(expected_returned_counts[i], resource.count);
+ }
+ fake_support_client_.clear_returned_resources();
+ }
+
+ cc::Surface* GetSurfaceForId(const SurfaceId& id) {
+ return manager_.surface_manager()->GetSurfaceForId(id);
+ }
+
+ void RefCurrentFrameResources() {
+ cc::Surface* surface = GetSurfaceForId(
+ SurfaceId(support_->frame_sink_id(), local_surface_id_));
+ support_->RefResources(surface->GetActiveFrame().resource_list);
+ }
+
+ protected:
+ FrameSinkManagerImpl manager_;
+ FakeCompositorFrameSinkSupportClient fake_support_client_;
+ std::unique_ptr<CompositorFrameSinkSupport> support_;
+ cc::FakeExternalBeginFrameSource begin_frame_source_;
+ LocalSurfaceId local_surface_id_;
+ cc::FakeSurfaceObserver surface_observer_;
+
+ // This is the sync token submitted with the frame. It should never be
+ // returned to the client.
+ const gpu::SyncToken frame_sync_token_;
+
+ // This is the sync token returned by the consumer. It should always be
+ // returned to the client.
+ const gpu::SyncToken consumer_sync_token_;
+};
+
+// Tests submitting a frame with resources followed by one with no resources
+// with no resource provider action in between.
+TEST_F(CompositorFrameSinkSupportTest, ResourceLifetimeSimple) {
+ cc::ResourceId first_frame_ids[] = {1, 2, 3};
+ SubmitCompositorFrameWithResources(first_frame_ids,
+ arraysize(first_frame_ids));
+
+ // All of the resources submitted in the first frame are still in use at this
+ // time by virtue of being in the pending frame, so none can be returned to
+ // the client yet.
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+ fake_support_client_.clear_returned_resources();
+
+ // The second frame references no resources of first frame and thus should
+ // make all resources of first frame available to be returned.
+ SubmitCompositorFrameWithResources(NULL, 0);
+
+ cc::ResourceId expected_returned_ids[] = {1, 2, 3};
+ int expected_returned_counts[] = {1, 1, 1};
+ // Resources were never consumed so no sync token should be set.
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), gpu::SyncToken());
+
+ cc::ResourceId third_frame_ids[] = {4, 5, 6};
+ SubmitCompositorFrameWithResources(third_frame_ids,
+ arraysize(third_frame_ids));
+
+ // All of the resources submitted in the third frame are still in use at this
+ // time by virtue of being in the pending frame, so none can be returned to
+ // the client yet.
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+ fake_support_client_.clear_returned_resources();
+
+ // The forth frame references no resources of third frame and thus should
+ // make all resources of third frame available to be returned.
+ cc::ResourceId forth_frame_ids[] = {7, 8, 9};
+ SubmitCompositorFrameWithResources(forth_frame_ids,
+ arraysize(forth_frame_ids));
+
+ cc::ResourceId forth_expected_returned_ids[] = {4, 5, 6};
+ int forth_expected_returned_counts[] = {1, 1, 1};
+ // Resources were never consumed so no sync token should be set.
+ CheckReturnedResourcesMatchExpected(
+ forth_expected_returned_ids, forth_expected_returned_counts,
+ arraysize(forth_expected_returned_counts), gpu::SyncToken());
+}
+
+// Tests submitting a frame with resources followed by one with no resources
+// with the resource provider holding everything alive.
+TEST_F(CompositorFrameSinkSupportTest,
+ ResourceLifetimeSimpleWithProviderHoldingAlive) {
+ cc::ResourceId first_frame_ids[] = {1, 2, 3};
+ SubmitCompositorFrameWithResources(first_frame_ids,
+ arraysize(first_frame_ids));
+
+ // All of the resources submitted in the first frame are still in use at this
+ // time by virtue of being in the pending frame, so none can be returned to
+ // the client yet.
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+ fake_support_client_.clear_returned_resources();
+
+ // Hold on to everything.
+ RefCurrentFrameResources();
+
+ // The second frame references no resources and thus should make all resources
+ // available to be returned as soon as the resource provider releases them.
+ SubmitCompositorFrameWithResources(NULL, 0);
+
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+ fake_support_client_.clear_returned_resources();
+
+ int release_counts[] = {1, 1, 1};
+ UnrefResources(first_frame_ids, release_counts, arraysize(first_frame_ids));
+
+ // None is returned to the client since DidReceiveCompositorAck is not
+ // invoked.
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+
+ // Submitting an empty frame causes previous resources referenced by the
+ // previous frame to be returned to client.
+ SubmitCompositorFrameWithResources(nullptr, 0);
+ cc::ResourceId expected_returned_ids[] = {1, 2, 3};
+ int expected_returned_counts[] = {1, 1, 1};
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), consumer_sync_token_);
+}
+
+// Tests referencing a resource, unref'ing it to zero, then using it again
+// before returning it to the client.
+TEST_F(CompositorFrameSinkSupportTest, ResourceReusedBeforeReturn) {
+ cc::ResourceId first_frame_ids[] = {7};
+ SubmitCompositorFrameWithResources(first_frame_ids,
+ arraysize(first_frame_ids));
+
+ // This removes all references to resource id 7.
+ SubmitCompositorFrameWithResources(NULL, 0);
+
+ // This references id 7 again.
+ SubmitCompositorFrameWithResources(first_frame_ids,
+ arraysize(first_frame_ids));
+
+ // This removes it again.
+ SubmitCompositorFrameWithResources(NULL, 0);
+
+ // Now it should be returned.
+ // We don't care how many entries are in the returned array for 7, so long as
+ // the total returned count matches the submitted count.
+ const std::vector<cc::ReturnedResource>& returned =
+ fake_support_client_.returned_resources();
+ size_t return_count = 0;
+ for (size_t i = 0; i < returned.size(); ++i) {
+ EXPECT_EQ(7u, returned[i].id);
+ return_count += returned[i].count;
+ }
+ EXPECT_EQ(2u, return_count);
+}
+
+// Tests having resources referenced multiple times, as if referenced by
+// multiple providers.
+TEST_F(CompositorFrameSinkSupportTest, ResourceRefMultipleTimes) {
+ cc::ResourceId first_frame_ids[] = {3, 4};
+ SubmitCompositorFrameWithResources(first_frame_ids,
+ arraysize(first_frame_ids));
+
+ // Ref resources from the first frame twice.
+ RefCurrentFrameResources();
+ RefCurrentFrameResources();
+
+ cc::ResourceId second_frame_ids[] = {4, 5};
+ SubmitCompositorFrameWithResources(second_frame_ids,
+ arraysize(second_frame_ids));
+
+ // Ref resources from the second frame 3 times.
+ RefCurrentFrameResources();
+ RefCurrentFrameResources();
+ RefCurrentFrameResources();
+
+ // Submit a frame with no resources to remove all current frame refs from
+ // submitted resources.
+ SubmitCompositorFrameWithResources(NULL, 0);
+
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+ fake_support_client_.clear_returned_resources();
+
+ // Expected current refs:
+ // 3 -> 2
+ // 4 -> 2 + 3 = 5
+ // 5 -> 3
+ {
+ SCOPED_TRACE("unref all 3");
+ cc::ResourceId ids_to_unref[] = {3, 4, 5};
+ int counts[] = {1, 1, 1};
+ UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
+
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+ fake_support_client_.clear_returned_resources();
+
+ UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
+ SubmitCompositorFrameWithResources(nullptr, 0);
+ cc::ResourceId expected_returned_ids[] = {3};
+ int expected_returned_counts[] = {1};
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), consumer_sync_token_);
+ }
+
+ // Expected refs remaining:
+ // 4 -> 3
+ // 5 -> 1
+ {
+ SCOPED_TRACE("unref 4 and 5");
+ cc::ResourceId ids_to_unref[] = {4, 5};
+ int counts[] = {1, 1};
+ UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
+ SubmitCompositorFrameWithResources(nullptr, 0);
+
+ cc::ResourceId expected_returned_ids[] = {5};
+ int expected_returned_counts[] = {1};
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), consumer_sync_token_);
+ }
+
+ // Now, just 2 refs remaining on resource 4. Unref both at once and make sure
+ // the returned count is correct.
+ {
+ SCOPED_TRACE("unref only 4");
+ cc::ResourceId ids_to_unref[] = {4};
+ int counts[] = {2};
+ UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
+ SubmitCompositorFrameWithResources(nullptr, 0);
+
+ cc::ResourceId expected_returned_ids[] = {4};
+ int expected_returned_counts[] = {2};
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), consumer_sync_token_);
+ }
+}
+
+TEST_F(CompositorFrameSinkSupportTest, ResourceLifetime) {
+ cc::ResourceId first_frame_ids[] = {1, 2, 3};
+ SubmitCompositorFrameWithResources(first_frame_ids,
+ arraysize(first_frame_ids));
+
+ // All of the resources submitted in the first frame are still in use at this
+ // time by virtue of being in the pending frame, so none can be returned to
+ // the client yet.
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+ fake_support_client_.clear_returned_resources();
+
+ // The second frame references some of the same resources, but some different
+ // ones. We expect to receive back resource 1 with a count of 1 since it was
+ // only referenced by the first frame.
+ cc::ResourceId second_frame_ids[] = {2, 3, 4};
+ SubmitCompositorFrameWithResources(second_frame_ids,
+ arraysize(second_frame_ids));
+ {
+ SCOPED_TRACE("second frame");
+ cc::ResourceId expected_returned_ids[] = {1};
+ int expected_returned_counts[] = {1};
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), gpu::SyncToken());
+ }
+
+ // The third frame references a disjoint set of resources, so we expect to
+ // receive back all resources from the first and second frames. Resource IDs 2
+ // and 3 will have counts of 2, since they were used in both frames, and
+ // resource ID 4 will have a count of 1.
+ cc::ResourceId third_frame_ids[] = {10, 11, 12, 13};
+ SubmitCompositorFrameWithResources(third_frame_ids,
+ arraysize(third_frame_ids));
+
+ {
+ SCOPED_TRACE("third frame");
+ cc::ResourceId expected_returned_ids[] = {2, 3, 4};
+ int expected_returned_counts[] = {2, 2, 1};
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), gpu::SyncToken());
+ }
+
+ // Simulate a ResourceProvider taking a ref on all of the resources.
+ RefCurrentFrameResources();
+
+ cc::ResourceId fourth_frame_ids[] = {12, 13};
+ SubmitCompositorFrameWithResources(fourth_frame_ids,
+ arraysize(fourth_frame_ids));
+
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+
+ RefCurrentFrameResources();
+
+ // All resources are still being used by the external reference, so none can
+ // be returned to the client.
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+
+ // Release resources associated with the first RefCurrentFrameResources() call
+ // first.
+ {
+ cc::ResourceId ids_to_unref[] = {10, 11, 12, 13};
+ int counts[] = {1, 1, 1, 1};
+ UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
+ }
+
+ // Nothing is returned to the client yet since DidReceiveCompositorFrameAck
+ // is not invoked.
+ {
+ SCOPED_TRACE("fourth frame, first unref");
+ CheckReturnedResourcesMatchExpected(nullptr, nullptr, 0,
+ consumer_sync_token_);
+ }
+
+ {
+ cc::ResourceId ids_to_unref[] = {12, 13};
+ int counts[] = {1, 1};
+ UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
+ }
+
+ // Resources 12 and 13 are still in use by the current frame, so they
+ // shouldn't be available to be returned.
+ EXPECT_EQ(0u, fake_support_client_.returned_resources().size());
+
+ // If we submit an empty frame, however, they should become available.
+ // Resources that were previously unref'd also return at this point.
+ SubmitCompositorFrameWithResources(NULL, 0u);
+
+ {
+ SCOPED_TRACE("fourth frame, second unref");
+ cc::ResourceId expected_returned_ids[] = {10, 11, 12, 13};
+ int expected_returned_counts[] = {1, 1, 2, 2};
+ CheckReturnedResourcesMatchExpected(
+ expected_returned_ids, expected_returned_counts,
+ arraysize(expected_returned_counts), consumer_sync_token_);
+ }
+}
+
+TEST_F(CompositorFrameSinkSupportTest, AddDuringEviction) {
+ cc::test::MockCompositorFrameSinkSupportClient mock_client;
+ auto support = CompositorFrameSinkSupport::Create(
+ &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_surface_id(6, kArbitraryToken);
+ support->SubmitCompositorFrame(local_surface_id,
+ cc::test::MakeCompositorFrame());
+
+ EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(_))
+ .WillOnce(testing::InvokeWithoutArgs([&support, &mock_client]() {
+ LocalSurfaceId new_id(7, base::UnguessableToken::Create());
+ support->SubmitCompositorFrame(new_id, cc::test::MakeCompositorFrame());
+ }))
+ .WillRepeatedly(testing::Return());
+ support->EvictCurrentSurface();
+}
+
+// Tests doing an EvictCurrentSurface before shutting down the factory.
+TEST_F(CompositorFrameSinkSupportTest, EvictCurrentSurface) {
+ cc::test::MockCompositorFrameSinkSupportClient mock_client;
+ auto support = CompositorFrameSinkSupport::Create(
+ &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_surface_id(7, kArbitraryToken);
+ SurfaceId id(kAnotherArbitraryFrameSinkId, local_surface_id);
+
+ cc::TransferableResource resource;
+ resource.id = 1;
+ resource.mailbox_holder.texture_target = GL_TEXTURE_2D;
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.resource_list.push_back(resource);
+ support->SubmitCompositorFrame(local_surface_id, std::move(frame));
+ EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
+ local_surface_id);
+ local_surface_id_ = LocalSurfaceId();
+
+ std::vector<cc::ReturnedResource> returned_resources = {
+ resource.ToReturnedResource()};
+ EXPECT_TRUE(GetSurfaceForId(id));
+ EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources))
+ .Times(1);
+ support->EvictCurrentSurface();
+ EXPECT_FALSE(GetSurfaceForId(id));
+}
+
+// Tests doing an EvictCurrentSurface which has unregistered dependency.
+TEST_F(CompositorFrameSinkSupportTest,
+ EvictCurrentSurfaceDependencyUnRegistered) {
+ cc::test::MockCompositorFrameSinkSupportClient mock_client;
+ auto support = CompositorFrameSinkSupport::Create(
+ &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_surface_id(7, kArbitraryToken);
+
+ cc::TransferableResource resource;
+ resource.id = 1;
+ resource.mailbox_holder.texture_target = GL_TEXTURE_2D;
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.resource_list.push_back(resource);
+ support->SubmitCompositorFrame(local_surface_id, std::move(frame));
+ EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
+ local_surface_id);
+ local_surface_id_ = LocalSurfaceId();
+
+ SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id);
+ cc::Surface* surface = GetSurfaceForId(surface_id);
+ surface->AddDestructionDependency(
+ SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
+
+ std::vector<cc::ReturnedResource> returned_resource = {
+ resource.ToReturnedResource()};
+
+ EXPECT_TRUE(GetSurfaceForId(surface_id));
+ EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resource))
+ .Times(1);
+ support->EvictCurrentSurface();
+ EXPECT_FALSE(GetSurfaceForId(surface_id));
+}
+
+// Tests doing an EvictCurrentSurface which has registered dependency.
+TEST_F(CompositorFrameSinkSupportTest,
+ EvictCurrentSurfaceDependencyRegistered) {
+ cc::test::MockCompositorFrameSinkSupportClient mock_client;
+ auto support = CompositorFrameSinkSupport::Create(
+ &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ LocalSurfaceId local_surface_id(7, kArbitraryToken);
+
+ cc::TransferableResource resource;
+ resource.id = 1;
+ resource.mailbox_holder.texture_target = GL_TEXTURE_2D;
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.resource_list.push_back(resource);
+ uint32_t execute_count = 0;
+ support->SubmitCompositorFrame(local_surface_id, std::move(frame));
+ EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
+ local_surface_id);
+ local_surface_id_ = LocalSurfaceId();
+
+ manager_.surface_manager()->RegisterFrameSinkId(
+ kYetAnotherArbitraryFrameSinkId);
+
+ SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id);
+ cc::Surface* surface = GetSurfaceForId(surface_id);
+ surface->AddDestructionDependency(
+ SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
+
+ std::vector<cc::ReturnedResource> returned_resources;
+ EXPECT_TRUE(GetSurfaceForId(surface_id));
+ support->EvictCurrentSurface();
+ EXPECT_TRUE(GetSurfaceForId(surface_id));
+ EXPECT_EQ(0u, execute_count);
+
+ returned_resources.push_back(resource.ToReturnedResource());
+ EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources))
+ .Times(1);
+ manager_.surface_manager()->SatisfySequence(
+ SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
+ EXPECT_FALSE(GetSurfaceForId(surface_id));
+}
+
+TEST_F(CompositorFrameSinkSupportTest, DestroySequence) {
+ LocalSurfaceId local_surface_id2(5, kArbitraryToken);
+ auto support2 = CompositorFrameSinkSupport::Create(
+ &fake_support_client_, &manager_, kYetAnotherArbitraryFrameSinkId,
+ kIsChildRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ SurfaceId id2(kYetAnotherArbitraryFrameSinkId, local_surface_id2);
+ support2->SubmitCompositorFrame(local_surface_id2,
+ cc::test::MakeCompositorFrame());
+
+ // Check that waiting before the sequence is satisfied works.
+ GetSurfaceForId(id2)->AddDestructionDependency(
+ SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
+ support2->EvictCurrentSurface();
+
+ DCHECK(GetSurfaceForId(id2));
+ manager_.surface_manager()->SatisfySequence(
+ SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
+ manager_.surface_manager()->SatisfySequence(
+ SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 6));
+ DCHECK(!GetSurfaceForId(id2));
+
+ // Check that waiting after the sequence is satisfied works.
+ support2->SubmitCompositorFrame(local_surface_id2,
+ cc::test::MakeCompositorFrame());
+ DCHECK(GetSurfaceForId(id2));
+ GetSurfaceForId(id2)->AddDestructionDependency(
+ SurfaceSequence(kAnotherArbitraryFrameSinkId, 6));
+ support2->EvictCurrentSurface();
+ DCHECK(!GetSurfaceForId(id2));
+}
+
+// Tests that SurfaceId namespace invalidation correctly allows
+// Sequences to be ignored.
+TEST_F(CompositorFrameSinkSupportTest, InvalidFrameSinkId) {
+ FrameSinkId frame_sink_id(1234, 5678);
+
+ LocalSurfaceId local_surface_id(5, kArbitraryToken);
+ SurfaceId id(support_->frame_sink_id(), local_surface_id);
+ support_->SubmitCompositorFrame(local_surface_id,
+ cc::test::MakeCompositorFrame());
+
+ manager_.surface_manager()->RegisterFrameSinkId(frame_sink_id);
+ GetSurfaceForId(id)->AddDestructionDependency(
+ SurfaceSequence(frame_sink_id, 4));
+
+ support_->EvictCurrentSurface();
+
+ // Verify the dependency has prevented the surface from getting destroyed.
+ EXPECT_TRUE(GetSurfaceForId(id));
+
+ manager_.surface_manager()->InvalidateFrameSinkId(frame_sink_id);
+
+ // Verify that the invalidated namespace caused the unsatisfied sequence
+ // to be ignored.
+ EXPECT_FALSE(GetSurfaceForId(id));
+}
+
+TEST_F(CompositorFrameSinkSupportTest, DestroyCycle) {
+ LocalSurfaceId local_surface_id2(5, kArbitraryToken);
+ SurfaceId id2(kYetAnotherArbitraryFrameSinkId, local_surface_id2);
+ auto support2 = CompositorFrameSinkSupport::Create(
+ &fake_support_client_, &manager_, kYetAnotherArbitraryFrameSinkId,
+ kIsChildRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
+ manager_.surface_manager()->RegisterFrameSinkId(kAnotherArbitraryFrameSinkId);
+ // Give local_surface_id_ an initial frame so another client can refer to
+ // that surface.
+ {
+ auto frame = cc::test::MakeCompositorFrame();
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ }
+ // Give id2 a frame that references local_surface_id_.
+ {
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.metadata.referenced_surfaces.push_back(
+ SurfaceId(support_->frame_sink_id(), local_surface_id_));
+ support2->SubmitCompositorFrame(local_surface_id2, std::move(frame));
+ EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
+ local_surface_id2);
+ }
+ GetSurfaceForId(id2)->AddDestructionDependency(
+ SurfaceSequence(kAnotherArbitraryFrameSinkId, 4));
+ support2->EvictCurrentSurface();
+ // Give local_surface_id_ a frame that references id2.
+ {
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.metadata.referenced_surfaces.push_back(id2);
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ }
+ support_->EvictCurrentSurface();
+ EXPECT_TRUE(GetSurfaceForId(id2));
+ // local_surface_id_ should be retained by reference from id2.
+ EXPECT_TRUE(
+ GetSurfaceForId(SurfaceId(support_->frame_sink_id(), local_surface_id_)));
+
+ // Satisfy last destruction dependency for id2.
+ manager_.surface_manager()->SatisfySequence(
+ SurfaceSequence(kAnotherArbitraryFrameSinkId, 4));
+
+ // id2 and local_surface_id_ are in a reference cycle that has no surface
+ // sequences holding on to it, so they should be destroyed.
+ EXPECT_TRUE(!GetSurfaceForId(id2));
+ EXPECT_TRUE(!GetSurfaceForId(
+ SurfaceId(support_->frame_sink_id(), local_surface_id_)));
+
+ local_surface_id_ = LocalSurfaceId();
+}
+
+void CopyRequestTestCallback(bool* called,
+ std::unique_ptr<cc::CopyOutputResult> result) {
+ *called = true;
+}
+
+TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) {
+ {
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.metadata.referenced_surfaces.push_back(
+ SurfaceId(support_->frame_sink_id(), local_surface_id_));
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
+ local_surface_id_);
+ }
+
+ bool called1 = false;
+ auto request = cc::CopyOutputRequest::CreateRequest(
+ base::BindOnce(&CopyRequestTestCallback, &called1));
+ request->set_source(kArbitrarySourceId1);
+
+ support_->RequestCopyOfSurface(std::move(request));
+ EXPECT_FALSE(called1);
+
+ bool called2 = false;
+ request = cc::CopyOutputRequest::CreateRequest(
+ base::BindOnce(&CopyRequestTestCallback, &called2));
+ request->set_source(kArbitrarySourceId2);
+
+ support_->RequestCopyOfSurface(std::move(request));
+ // Callbacks have different sources so neither should be called.
+ EXPECT_FALSE(called1);
+ EXPECT_FALSE(called2);
+
+ bool called3 = false;
+ request = cc::CopyOutputRequest::CreateRequest(
+ base::BindOnce(&CopyRequestTestCallback, &called3));
+ request->set_source(kArbitrarySourceId1);
+
+ support_->RequestCopyOfSurface(std::move(request));
+ // Two callbacks are from source1, so the first should be called.
+ EXPECT_TRUE(called1);
+ EXPECT_FALSE(called2);
+ EXPECT_FALSE(called3);
+
+ support_->EvictCurrentSurface();
+ local_surface_id_ = LocalSurfaceId();
+ EXPECT_TRUE(called1);
+ EXPECT_TRUE(called2);
+ EXPECT_TRUE(called3);
+}
+
+// Check whether the SurfaceInfo object is created and populated correctly
+// after the frame submission.
+TEST_F(CompositorFrameSinkSupportTest, SurfaceInfo) {
+ auto frame = cc::test::MakeCompositorFrame();
+
+ auto render_pass = cc::RenderPass::Create();
+ render_pass->SetNew(1, gfx::Rect(5, 6), gfx::Rect(), gfx::Transform());
+ frame.render_pass_list.push_back(std::move(render_pass));
+
+ render_pass = cc::RenderPass::Create();
+ render_pass->SetNew(2, gfx::Rect(7, 8), gfx::Rect(), gfx::Transform());
+ frame.render_pass_list.push_back(std::move(render_pass));
+
+ frame.metadata.device_scale_factor = 2.5f;
+
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ SurfaceId expected_surface_id(support_->frame_sink_id(), local_surface_id_);
+ EXPECT_EQ(expected_surface_id, surface_observer_.last_surface_info().id());
+ EXPECT_EQ(2.5f, surface_observer_.last_surface_info().device_scale_factor());
+ EXPECT_EQ(gfx::Size(7, 8),
+ surface_observer_.last_surface_info().size_in_pixels());
+}
+
+// Check that if a CompositorFrame is received with size zero, we don't create
+// a Surface for it.
+TEST_F(CompositorFrameSinkSupportTest, ZeroFrameSize) {
+ SurfaceId id(support_->frame_sink_id(), local_surface_id_);
+ auto frame = cc::test::MakeEmptyCompositorFrame();
+ frame.render_pass_list.push_back(cc::RenderPass::Create());
+ EXPECT_TRUE(
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
+ EXPECT_FALSE(GetSurfaceForId(id));
+}
+
+// Check that if a CompositorFrame is received with device scale factor of 0, we
+// don't create a Surface for it.
+TEST_F(CompositorFrameSinkSupportTest, ZeroDeviceScaleFactor) {
+ SurfaceId id(support_->frame_sink_id(), local_surface_id_);
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.metadata.device_scale_factor = 0.f;
+ EXPECT_TRUE(
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
+ EXPECT_FALSE(GetSurfaceForId(id));
+}
+
+// Check that if the size of a CompositorFrame doesn't match the size of the
+// Surface it's being submitted to, we skip the frame.
+TEST_F(CompositorFrameSinkSupportTest, FrameSizeMismatch) {
+ SurfaceId id(support_->frame_sink_id(), local_surface_id_);
+
+ // Submit a frame with size (5,5).
+ auto frame = cc::test::MakeEmptyCompositorFrame();
+ auto pass = cc::RenderPass::Create();
+ pass->SetNew(1, gfx::Rect(5, 5), gfx::Rect(), gfx::Transform());
+ frame.render_pass_list.push_back(std::move(pass));
+ EXPECT_TRUE(
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
+ EXPECT_TRUE(GetSurfaceForId(id));
+
+ // Submit a frame with size (5,4). This frame should be rejected and the
+ // surface should be destroyed.
+ frame = cc::test::MakeEmptyCompositorFrame();
+ pass = cc::RenderPass::Create();
+ pass->SetNew(1, gfx::Rect(5, 4), gfx::Rect(), gfx::Transform());
+ frame.render_pass_list.push_back(std::move(pass));
+ EXPECT_FALSE(
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
+ EXPECT_FALSE(GetSurfaceForId(id));
+}
+
+// Check that if the device scale factor of a CompositorFrame doesn't match the
+// device scale factor of the Surface it's being submitted to, the frame is
+// rejected and the surface is destroyed.
+TEST_F(CompositorFrameSinkSupportTest, DeviceScaleFactorMismatch) {
+ SurfaceId id(support_->frame_sink_id(), local_surface_id_);
+
+ // Submit a frame with device scale factor of 0.5.
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.metadata.device_scale_factor = 0.5f;
+ EXPECT_TRUE(
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
+ EXPECT_TRUE(GetSurfaceForId(id));
+
+ // Submit a frame with device scale factor of 0.4. This frame should be
+ // rejected and the surface should be destroyed.
+ frame = cc::test::MakeCompositorFrame();
+ frame.metadata.device_scale_factor = 0.4f;
+ EXPECT_FALSE(
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
+ EXPECT_FALSE(GetSurfaceForId(id));
+}
+
+TEST_F(CompositorFrameSinkSupportTest, PassesOnBeginFrameAcks) {
+ // Request BeginFrames.
+ support_->SetNeedsBeginFrame(true);
+
+ // Issue a BeginFrame.
+ cc::BeginFrameArgs args =
+ cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
+ begin_frame_source_.TestOnBeginFrame(args);
+
+ // Check that the support and SurfaceManager forward the BeginFrameAck
+ // attached to a CompositorFrame to the SurfaceObserver.
+ cc::BeginFrameAck ack(0, 1, true);
+ auto frame = cc::test::MakeCompositorFrame();
+ frame.metadata.begin_frame_ack = ack;
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ EXPECT_EQ(ack, surface_observer_.last_ack());
+
+ // Issue another BeginFrame.
+ args = cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
+ begin_frame_source_.TestOnBeginFrame(args);
+
+ // Check that the support and SurfaceManager forward a DidNotProduceFrame ack
+ // to the SurfaceObserver.
+ cc::BeginFrameAck ack2(0, 2, false);
+ support_->DidNotProduceFrame(ack2);
+ EXPECT_EQ(ack2, surface_observer_.last_ack());
+
+ support_->SetNeedsBeginFrame(false);
+}
+
+} // 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
new file mode 100644
index 00000000000..d0e94605b3f
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
@@ -0,0 +1,173 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h"
+
+#include "base/bind.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/layer_tree_frame_sink_client.h"
+#include "cc/surfaces/surface.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
+#include "components/viz/service/display/display.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+
+namespace viz {
+
+DirectLayerTreeFrameSink::DirectLayerTreeFrameSink(
+ const FrameSinkId& frame_sink_id,
+ CompositorFrameSinkSupportManager* support_manager,
+ FrameSinkManagerImpl* frame_sink_manager,
+ Display* display,
+ scoped_refptr<ContextProvider> context_provider,
+ scoped_refptr<ContextProvider> worker_context_provider,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ SharedBitmapManager* shared_bitmap_manager)
+ : LayerTreeFrameSink(std::move(context_provider),
+ std::move(worker_context_provider),
+ gpu_memory_buffer_manager,
+ shared_bitmap_manager),
+ frame_sink_id_(frame_sink_id),
+ support_manager_(support_manager),
+ frame_sink_manager_(frame_sink_manager),
+ display_(display) {
+ 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(
+ const FrameSinkId& frame_sink_id,
+ CompositorFrameSinkSupportManager* support_manager,
+ FrameSinkManagerImpl* frame_sink_manager,
+ Display* display,
+ scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider)
+ : LayerTreeFrameSink(std::move(vulkan_context_provider)),
+ frame_sink_id_(frame_sink_id),
+ support_manager_(support_manager),
+ frame_sink_manager_(frame_sink_manager),
+ display_(display) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ capabilities_.must_always_swap = true;
+}
+
+DirectLayerTreeFrameSink::~DirectLayerTreeFrameSink() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+bool DirectLayerTreeFrameSink::BindToClient(
+ cc::LayerTreeFrameSinkClient* client) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!cc::LayerTreeFrameSink::BindToClient(client))
+ return false;
+
+ // We want the Display's output surface to hear about lost context, and since
+ // this shares a context with it, we should not be listening for lost context
+ // callbacks on the context here.
+ if (auto* cp = context_provider())
+ cp->SetLostContextCallback(base::Closure());
+
+ constexpr bool is_root = true;
+ constexpr bool handles_frame_sink_id_invalidation = false;
+ support_ = support_manager_->CreateCompositorFrameSinkSupport(
+ this, frame_sink_id_, is_root, handles_frame_sink_id_invalidation,
+ capabilities_.delegated_sync_points_required);
+ begin_frame_source_ = base::MakeUnique<cc::ExternalBeginFrameSource>(this);
+ client_->SetBeginFrameSource(begin_frame_source_.get());
+
+ // Avoid initializing GL context here, as this should be sharing the
+ // Display's context.
+ display_->Initialize(this, frame_sink_manager_->surface_manager());
+ return true;
+}
+
+void DirectLayerTreeFrameSink::DetachFromClient() {
+ client_->SetBeginFrameSource(nullptr);
+ begin_frame_source_.reset();
+
+ // Unregister the SurfaceFactoryClient here instead of the dtor so that only
+ // one client is alive for this namespace at any given time.
+ support_.reset();
+
+ cc::LayerTreeFrameSink::DetachFromClient();
+}
+
+void DirectLayerTreeFrameSink::SubmitCompositorFrame(
+ cc::CompositorFrame frame) {
+ DCHECK(frame.metadata.begin_frame_ack.has_damage);
+ DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber,
+ frame.metadata.begin_frame_ack.sequence_number);
+
+ gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size();
+ if (!local_surface_id_.is_valid() || frame_size != last_swap_frame_size_ ||
+ frame.metadata.device_scale_factor != device_scale_factor_) {
+ local_surface_id_ = local_surface_id_allocator_.GenerateId();
+ last_swap_frame_size_ = frame_size;
+ device_scale_factor_ = frame.metadata.device_scale_factor;
+ display_->SetLocalSurfaceId(local_surface_id_, device_scale_factor_);
+ }
+
+ bool result =
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ DCHECK(result);
+}
+
+void DirectLayerTreeFrameSink::DidNotProduceFrame(
+ const cc::BeginFrameAck& ack) {
+ DCHECK(!ack.has_damage);
+ DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber, ack.sequence_number);
+ support_->DidNotProduceFrame(ack);
+}
+
+void DirectLayerTreeFrameSink::DisplayOutputSurfaceLost() {
+ is_lost_ = true;
+ client_->DidLoseLayerTreeFrameSink();
+}
+
+void DirectLayerTreeFrameSink::DisplayWillDrawAndSwap(
+ bool will_draw_and_swap,
+ const cc::RenderPassList& render_passes) {
+ // This notification is not relevant to our client outside of tests.
+}
+
+void DirectLayerTreeFrameSink::DisplayDidDrawAndSwap() {
+ // This notification is not relevant to our client outside of tests. We
+ // unblock the client from DidDrawCallback() when the surface is going to
+ // be drawn.
+}
+
+void DirectLayerTreeFrameSink::DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) {
+ client_->ReclaimResources(resources);
+ client_->DidReceiveCompositorFrameAck();
+}
+
+void DirectLayerTreeFrameSink::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ begin_frame_source_->OnBeginFrame(args);
+}
+
+void DirectLayerTreeFrameSink::ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) {
+ client_->ReclaimResources(resources);
+}
+
+void DirectLayerTreeFrameSink::WillDrawSurface(
+ const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) {
+ // TODO(staraz): Implement this.
+}
+
+void DirectLayerTreeFrameSink::OnBeginFramePausedChanged(bool paused) {
+ begin_frame_source_->OnSetBeginFrameSourcePaused(paused);
+}
+
+void DirectLayerTreeFrameSink::OnNeedsBeginFrames(bool needs_begin_frame) {
+ support_->SetNeedsBeginFrame(needs_begin_frame);
+}
+
+} // namespace viz
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
new file mode 100644
index 00000000000..0a1724f2bf1
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_DIRECT_LAYER_TREE_FRAME_SINK_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_DIRECT_LAYER_TREE_FRAME_SINK_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "cc/output/layer_tree_frame_sink.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
+#include "components/viz/service/display/display_client.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_client.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace cc {
+class LocalSurfaceIdAllocator;
+class FrameSinkManagerImpl;
+} // namespace cc
+
+namespace viz {
+class CompositorFrameSinkSupportManager;
+class Display;
+
+// This class submits compositor frames to an in-process Display, with the
+// client's frame being the root surface of the Display.
+class VIZ_SERVICE_EXPORT DirectLayerTreeFrameSink
+ : public cc::LayerTreeFrameSink,
+ public NON_EXPORTED_BASE(CompositorFrameSinkSupportClient),
+ public cc::ExternalBeginFrameSourceClient,
+ public NON_EXPORTED_BASE(DisplayClient) {
+ public:
+ // The underlying Display, FrameSinkManagerImpl, and LocalSurfaceIdAllocator
+ // must outlive this class.
+ DirectLayerTreeFrameSink(
+ const FrameSinkId& frame_sink_id,
+ CompositorFrameSinkSupportManager* support_manager,
+ FrameSinkManagerImpl* frame_sink_manager,
+ Display* display,
+ scoped_refptr<ContextProvider> context_provider,
+ scoped_refptr<ContextProvider> worker_context_provider,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ SharedBitmapManager* shared_bitmap_manager);
+ DirectLayerTreeFrameSink(
+ const FrameSinkId& frame_sink_id,
+ CompositorFrameSinkSupportManager* support_manager,
+ FrameSinkManagerImpl* frame_sink_manager,
+ Display* display,
+ scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider);
+ ~DirectLayerTreeFrameSink() override;
+
+ // LayerTreeFrameSink implementation.
+ bool BindToClient(cc::LayerTreeFrameSinkClient* client) override;
+ void DetachFromClient() override;
+ void SubmitCompositorFrame(cc::CompositorFrame frame) override;
+ void DidNotProduceFrame(const cc::BeginFrameAck& ack) override;
+
+ // DisplayClient implementation.
+ void DisplayOutputSurfaceLost() override;
+ void DisplayWillDrawAndSwap(bool will_draw_and_swap,
+ const cc::RenderPassList& render_passes) override;
+ void DisplayDidDrawAndSwap() override;
+
+ protected:
+ std::unique_ptr<CompositorFrameSinkSupport> support_; // protected for test.
+
+ private:
+ // CompositorFrameSinkSupportClient implementation:
+ void DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void WillDrawSurface(const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) override;
+ void OnBeginFramePausedChanged(bool paused) override;
+
+ // ExternalBeginFrameSourceClient implementation:
+ void OnNeedsBeginFrames(bool needs_begin_frame) override;
+
+ // This class is only meant to be used on a single thread.
+ THREAD_CHECKER(thread_checker_);
+
+ const FrameSinkId frame_sink_id_;
+ LocalSurfaceId local_surface_id_;
+ CompositorFrameSinkSupportManager* const support_manager_;
+ FrameSinkManagerImpl* frame_sink_manager_;
+ LocalSurfaceIdAllocator local_surface_id_allocator_;
+ Display* display_;
+ gfx::Size last_swap_frame_size_;
+ float device_scale_factor_ = 1.f;
+ bool is_lost_ = false;
+ std::unique_ptr<cc::ExternalBeginFrameSource> begin_frame_source_;
+
+ DISALLOW_COPY_AND_ASSIGN(DirectLayerTreeFrameSink);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_DIRECT_LAYER_TREE_FRAME_SINK_H_
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
new file mode 100644
index 00000000000..3b050b4c65d
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
@@ -0,0 +1,177 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "cc/output/texture_mailbox_deleter.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/scheduler/delay_based_time_source.h"
+#include "cc/test/begin_frame_args_test.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "cc/test/fake_layer_tree_frame_sink_client.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/ordered_simple_task_runner.h"
+#include "cc/test/test_context_provider.h"
+#include "cc/test/test_gpu_memory_buffer_manager.h"
+#include "cc/test/test_shared_bitmap_manager.h"
+#include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/local_surface_id_allocator.h"
+#include "components/viz/service/display/display.h"
+#include "components/viz/service/display/display_scheduler.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+namespace {
+
+static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
+
+class TestDirectLayerTreeFrameSink : public DirectLayerTreeFrameSink {
+ public:
+ using DirectLayerTreeFrameSink::DirectLayerTreeFrameSink;
+
+ CompositorFrameSinkSupport* support() const { return support_.get(); }
+};
+
+class TestCompositorFrameSinkSupportManager
+ : public CompositorFrameSinkSupportManager {
+ public:
+ explicit TestCompositorFrameSinkSupportManager(
+ FrameSinkManagerImpl* frame_sink_manager)
+ : frame_sink_manager_(frame_sink_manager) {}
+ ~TestCompositorFrameSinkSupportManager() override = default;
+
+ std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
+ CompositorFrameSinkSupportClient* client,
+ const FrameSinkId& frame_sink_id,
+ bool is_root,
+ bool handles_frame_sink_id_invalidation,
+ bool needs_sync_points) override {
+ return CompositorFrameSinkSupport::Create(
+ client, frame_sink_manager_, frame_sink_id, is_root,
+ handles_frame_sink_id_invalidation, needs_sync_points);
+ }
+
+ private:
+ FrameSinkManagerImpl* const frame_sink_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestCompositorFrameSinkSupportManager);
+};
+
+class DirectLayerTreeFrameSinkTest : public testing::Test {
+ public:
+ DirectLayerTreeFrameSinkTest()
+ : now_src_(new base::SimpleTestTickClock()),
+ task_runner_(new cc::OrderedSimpleTaskRunner(now_src_.get(), true)),
+ display_size_(1920, 1080),
+ display_rect_(display_size_),
+ support_manager_(&frame_sink_manager_),
+ context_provider_(cc::TestContextProvider::Create()) {
+ auto display_output_surface = cc::FakeOutputSurface::Create3d();
+ display_output_surface_ = display_output_surface.get();
+
+ begin_frame_source_ = base::MakeUnique<cc::BackToBackBeginFrameSource>(
+ base::MakeUnique<cc::DelayBasedTimeSource>(task_runner_.get()));
+
+ int max_frames_pending = 2;
+ std::unique_ptr<DisplayScheduler> scheduler(new DisplayScheduler(
+ begin_frame_source_.get(), task_runner_.get(), max_frames_pending));
+
+ display_.reset(new Display(
+ &bitmap_manager_, &gpu_memory_buffer_manager_, RendererSettings(),
+ kArbitraryFrameSinkId, std::move(display_output_surface),
+ std::move(scheduler),
+ base::MakeUnique<cc::TextureMailboxDeleter>(task_runner_.get())));
+ layer_tree_frame_sink_ = base::MakeUnique<TestDirectLayerTreeFrameSink>(
+ kArbitraryFrameSinkId, &support_manager_, &frame_sink_manager_,
+ display_.get(), context_provider_, nullptr, &gpu_memory_buffer_manager_,
+ &bitmap_manager_);
+
+ layer_tree_frame_sink_->BindToClient(&layer_tree_frame_sink_client_);
+ display_->Resize(display_size_);
+ display_->SetVisible(true);
+
+ EXPECT_FALSE(
+ layer_tree_frame_sink_client_.did_lose_layer_tree_frame_sink_called());
+ }
+
+ ~DirectLayerTreeFrameSinkTest() override {
+ layer_tree_frame_sink_->DetachFromClient();
+ }
+
+ void SwapBuffersWithDamage(const gfx::Rect& damage_rect) {
+ auto render_pass = cc::RenderPass::Create();
+ render_pass->SetNew(1, display_rect_, damage_rect, gfx::Transform());
+
+ cc::CompositorFrame frame = cc::test::MakeEmptyCompositorFrame();
+ frame.metadata.begin_frame_ack = cc::BeginFrameAck(0, 1, true);
+ frame.render_pass_list.push_back(std::move(render_pass));
+
+ layer_tree_frame_sink_->SubmitCompositorFrame(std::move(frame));
+ }
+
+ void SetUp() override {
+ // Draw the first frame to start in an "unlocked" state.
+ SwapBuffersWithDamage(display_rect_);
+
+ EXPECT_EQ(0u, display_output_surface_->num_sent_frames());
+ task_runner_->RunUntilIdle();
+ EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
+ }
+
+ protected:
+ std::unique_ptr<base::SimpleTestTickClock> now_src_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner_;
+
+ const gfx::Size display_size_;
+ const gfx::Rect display_rect_;
+ FrameSinkManagerImpl frame_sink_manager_;
+ TestCompositorFrameSinkSupportManager support_manager_;
+ cc::TestSharedBitmapManager bitmap_manager_;
+ cc::TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
+
+ scoped_refptr<cc::TestContextProvider> context_provider_;
+ cc::FakeOutputSurface* display_output_surface_ = nullptr;
+ std::unique_ptr<cc::BackToBackBeginFrameSource> begin_frame_source_;
+ std::unique_ptr<Display> display_;
+ cc::FakeLayerTreeFrameSinkClient layer_tree_frame_sink_client_;
+ std::unique_ptr<TestDirectLayerTreeFrameSink> layer_tree_frame_sink_;
+};
+
+TEST_F(DirectLayerTreeFrameSinkTest, DamageTriggersSwapBuffers) {
+ SwapBuffersWithDamage(display_rect_);
+ EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
+ task_runner_->RunUntilIdle();
+ EXPECT_EQ(2u, display_output_surface_->num_sent_frames());
+}
+
+TEST_F(DirectLayerTreeFrameSinkTest, NoDamageDoesNotTriggerSwapBuffers) {
+ SwapBuffersWithDamage(gfx::Rect());
+ EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
+ task_runner_->RunUntilIdle();
+ EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
+}
+
+TEST_F(DirectLayerTreeFrameSinkTest, SuspendedDoesNotTriggerSwapBuffers) {
+ SwapBuffersWithDamage(display_rect_);
+ EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
+ display_output_surface_->set_suspended_for_recycle(true);
+ task_runner_->RunUntilIdle();
+ EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
+ SwapBuffersWithDamage(display_rect_);
+ task_runner_->RunUntilIdle();
+ EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
+ display_output_surface_->set_suspended_for_recycle(false);
+ SwapBuffersWithDamage(display_rect_);
+ task_runner_->RunUntilIdle();
+ EXPECT_EQ(2u, display_output_surface_->num_sent_frames());
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/frame_eviction_manager.cc b/chromium/components/viz/service/frame_sinks/frame_eviction_manager.cc
new file mode 100644
index 00000000000..d6ed3980042
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_eviction_manager.cc
@@ -0,0 +1,179 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/frame_eviction_manager.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/memory_coordinator_client_registry.h"
+#include "base/memory/memory_coordinator_proxy.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/memory/memory_pressure_monitor.h"
+#include "base/memory/shared_memory.h"
+#include "base/sys_info.h"
+#include "build/build_config.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
+
+namespace viz {
+namespace {
+
+const int kModeratePressurePercentage = 50;
+const int kCriticalPressurePercentage = 10;
+
+} // namespace
+
+FrameEvictionManager* FrameEvictionManager::GetInstance() {
+ return base::Singleton<FrameEvictionManager>::get();
+}
+
+void FrameEvictionManager::AddFrame(FrameEvictionManagerClient* frame,
+ bool locked) {
+ RemoveFrame(frame);
+ if (locked)
+ locked_frames_[frame] = 1;
+ else
+ unlocked_frames_.push_front(frame);
+ CullUnlockedFrames(GetMaxNumberOfSavedFrames());
+}
+
+void FrameEvictionManager::RemoveFrame(FrameEvictionManagerClient* frame) {
+ std::map<FrameEvictionManagerClient*, size_t>::iterator locked_iter =
+ locked_frames_.find(frame);
+ if (locked_iter != locked_frames_.end())
+ locked_frames_.erase(locked_iter);
+ unlocked_frames_.remove(frame);
+}
+
+void FrameEvictionManager::LockFrame(FrameEvictionManagerClient* frame) {
+ std::list<FrameEvictionManagerClient*>::iterator unlocked_iter =
+ std::find(unlocked_frames_.begin(), unlocked_frames_.end(), frame);
+ if (unlocked_iter != unlocked_frames_.end()) {
+ DCHECK(locked_frames_.find(frame) == locked_frames_.end());
+ unlocked_frames_.remove(frame);
+ locked_frames_[frame] = 1;
+ } else {
+ DCHECK(locked_frames_.find(frame) != locked_frames_.end());
+ locked_frames_[frame]++;
+ }
+}
+
+void FrameEvictionManager::UnlockFrame(FrameEvictionManagerClient* frame) {
+ DCHECK(locked_frames_.find(frame) != locked_frames_.end());
+ size_t locked_count = locked_frames_[frame];
+ DCHECK(locked_count);
+ if (locked_count > 1) {
+ locked_frames_[frame]--;
+ } else {
+ RemoveFrame(frame);
+ unlocked_frames_.push_front(frame);
+ CullUnlockedFrames(GetMaxNumberOfSavedFrames());
+ }
+}
+
+size_t FrameEvictionManager::GetMaxNumberOfSavedFrames() const {
+ int percentage = 100;
+ auto* memory_coordinator_proxy = base::MemoryCoordinatorProxy::GetInstance();
+ if (memory_coordinator_proxy) {
+ switch (memory_coordinator_proxy->GetCurrentMemoryState()) {
+ case base::MemoryState::NORMAL:
+ percentage = 100;
+ break;
+ case base::MemoryState::THROTTLED:
+ percentage = kCriticalPressurePercentage;
+ break;
+ case base::MemoryState::SUSPENDED:
+ case base::MemoryState::UNKNOWN:
+ NOTREACHED();
+ break;
+ }
+ } else {
+ base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
+
+ if (!monitor)
+ return max_number_of_saved_frames_;
+
+ // Until we have a global OnMemoryPressureChanged event we need to query the
+ // value from our specific pressure monitor.
+ switch (monitor->GetCurrentPressureLevel()) {
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+ percentage = 100;
+ break;
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+ percentage = kModeratePressurePercentage;
+ break;
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+ percentage = kCriticalPressurePercentage;
+ break;
+ }
+ }
+ size_t frames = (max_number_of_saved_frames_ * percentage) / 100;
+ return std::max(static_cast<size_t>(1), frames);
+}
+
+FrameEvictionManager::FrameEvictionManager()
+ : memory_pressure_listener_(new base::MemoryPressureListener(
+ base::Bind(&FrameEvictionManager::OnMemoryPressure,
+ base::Unretained(this)))) {
+ base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this);
+ max_number_of_saved_frames_ =
+#if defined(OS_ANDROID)
+ // If the amount of memory on the device is >= 3.5 GB, save up to 5
+ // frames.
+ base::SysInfo::AmountOfPhysicalMemoryMB() < 1024 * 3.5f ? 1 : 5;
+#else
+ std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
+#endif
+ max_handles_ = base::SharedMemory::GetHandleLimit() / 8.0f;
+}
+
+FrameEvictionManager::~FrameEvictionManager() {}
+
+void FrameEvictionManager::CullUnlockedFrames(size_t saved_frame_limit) {
+ if (unlocked_frames_.size() + locked_frames_.size() > 0) {
+ float handles_per_frame =
+ ServerSharedBitmapManager::current()->AllocatedBitmapCount() * 1.0f /
+ (unlocked_frames_.size() + locked_frames_.size());
+
+ saved_frame_limit = std::max(
+ 1, static_cast<int>(std::min(static_cast<float>(saved_frame_limit),
+ max_handles_ / handles_per_frame)));
+ }
+ while (!unlocked_frames_.empty() &&
+ unlocked_frames_.size() + locked_frames_.size() > saved_frame_limit) {
+ size_t old_size = unlocked_frames_.size();
+ // Should remove self from list.
+ unlocked_frames_.back()->EvictCurrentFrame();
+ DCHECK_EQ(unlocked_frames_.size() + 1, old_size);
+ }
+}
+
+void FrameEvictionManager::OnMemoryPressure(
+ base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+ switch (memory_pressure_level) {
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+ PurgeMemory(kModeratePressurePercentage);
+ break;
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+ PurgeMemory(kCriticalPressurePercentage);
+ break;
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+ // No need to change anything when there is no pressure.
+ return;
+ }
+}
+
+void FrameEvictionManager::OnPurgeMemory() {
+ PurgeMemory(kCriticalPressurePercentage);
+}
+
+void FrameEvictionManager::PurgeMemory(int percentage) {
+ int saved_frame_limit = max_number_of_saved_frames_;
+ if (saved_frame_limit <= 1)
+ return;
+ CullUnlockedFrames(std::max(1, (saved_frame_limit * percentage) / 100));
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/frame_eviction_manager.h b/chromium/components/viz/service/frame_sinks/frame_eviction_manager.h
new file mode 100644
index 00000000000..6e78277377b
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_eviction_manager.h
@@ -0,0 +1,83 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_EVICTION_MANAGER_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_EVICTION_MANAGER_H_
+
+#include <stddef.h>
+
+#include <list>
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/memory_coordinator_client.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/memory/singleton.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT FrameEvictionManagerClient {
+ public:
+ virtual ~FrameEvictionManagerClient() {}
+ virtual void EvictCurrentFrame() = 0;
+};
+
+// This class is responsible for globally managing which renderers keep their
+// compositor frame when offscreen. We actively discard compositor frames for
+// offscreen tabs, but keep a minimum amount, as an LRU cache, to make switching
+// between a small set of tabs faster. The limit is a soft limit, because
+// clients can lock their frame to prevent it from being discarded, e.g. if the
+// tab is visible, or while capturing a screenshot.
+class VIZ_SERVICE_EXPORT FrameEvictionManager
+ : public base::MemoryCoordinatorClient {
+ public:
+ static FrameEvictionManager* GetInstance();
+
+ void AddFrame(FrameEvictionManagerClient*, bool locked);
+ void RemoveFrame(FrameEvictionManagerClient*);
+ void LockFrame(FrameEvictionManagerClient*);
+ void UnlockFrame(FrameEvictionManagerClient*);
+
+ size_t GetMaxNumberOfSavedFrames() const;
+
+ // For testing only
+ void set_max_number_of_saved_frames(size_t max_number_of_saved_frames) {
+ max_number_of_saved_frames_ = max_number_of_saved_frames;
+ }
+ void set_max_handles(float max_handles) { max_handles_ = max_handles; }
+
+ // React on memory pressure events to adjust the number of cached frames.
+ // Please make this private when crbug.com/443824 has been fixed.
+ void OnMemoryPressure(
+ base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
+
+ private:
+ FrameEvictionManager();
+ ~FrameEvictionManager() override;
+
+ // base::MemoryCoordinatorClient implementation:
+ void OnPurgeMemory() override;
+
+ void CullUnlockedFrames(size_t saved_frame_limit);
+
+ void PurgeMemory(int percentage);
+
+ friend struct base::DefaultSingletonTraits<FrameEvictionManager>;
+
+ // Listens for system under pressure notifications and adjusts number of
+ // cached frames accordingly.
+ std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
+
+ std::map<FrameEvictionManagerClient*, size_t> locked_frames_;
+ std::list<FrameEvictionManagerClient*> unlocked_frames_;
+ size_t max_number_of_saved_frames_;
+ float max_handles_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameEvictionManager);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_EVICTION_MANAGER_H_
diff --git a/chromium/components/viz/service/frame_sinks/frame_evictor.cc b/chromium/components/viz/service/frame_sinks/frame_evictor.cc
new file mode 100644
index 00000000000..086f9fca51d
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_evictor.cc
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/frame_evictor.h"
+
+#include "base/logging.h"
+
+namespace viz {
+
+FrameEvictor::FrameEvictor(FrameEvictorClient* client)
+ : client_(client), has_frame_(false), visible_(false) {}
+
+FrameEvictor::~FrameEvictor() {
+ DiscardedFrame();
+}
+
+void FrameEvictor::SwappedFrame(bool visible) {
+ visible_ = visible;
+ has_frame_ = true;
+ FrameEvictionManager::GetInstance()->AddFrame(this, visible);
+}
+
+void FrameEvictor::DiscardedFrame() {
+ FrameEvictionManager::GetInstance()->RemoveFrame(this);
+ has_frame_ = false;
+}
+
+void FrameEvictor::SetVisible(bool visible) {
+ if (visible_ == visible)
+ return;
+ visible_ = visible;
+ if (has_frame_) {
+ if (visible) {
+ LockFrame();
+ } else {
+ UnlockFrame();
+ }
+ }
+}
+
+void FrameEvictor::LockFrame() {
+ DCHECK(has_frame_);
+ FrameEvictionManager::GetInstance()->LockFrame(this);
+}
+
+void FrameEvictor::UnlockFrame() {
+ DCHECK(has_frame_);
+ FrameEvictionManager::GetInstance()->UnlockFrame(this);
+}
+
+void FrameEvictor::EvictCurrentFrame() {
+ client_->EvictDelegatedFrame();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/frame_evictor.h b/chromium/components/viz/service/frame_sinks/frame_evictor.h
new file mode 100644
index 00000000000..be6ef5d5433
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_evictor.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_EVICTOR_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_EVICTOR_H_
+
+#include "base/macros.h"
+#include "components/viz/service/frame_sinks/frame_eviction_manager.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT FrameEvictorClient {
+ public:
+ virtual ~FrameEvictorClient() {}
+ virtual void EvictDelegatedFrame() = 0;
+};
+
+class VIZ_SERVICE_EXPORT FrameEvictor : public FrameEvictionManagerClient {
+ public:
+ // |client| must outlive |this|.
+ explicit FrameEvictor(FrameEvictorClient* client);
+ ~FrameEvictor() override;
+
+ void SwappedFrame(bool visible);
+ void DiscardedFrame();
+ void SetVisible(bool visible);
+ void LockFrame();
+ void UnlockFrame();
+ bool HasFrame() { return has_frame_; }
+
+ private:
+ // FrameEvictionManagerClient implementation.
+ void EvictCurrentFrame() override;
+
+ FrameEvictorClient* client_;
+ bool has_frame_;
+ bool visible_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameEvictor);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_EVICTOR_H_
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_client.h b/chromium/components/viz/service/frame_sinks/frame_sink_manager_client.h
new file mode 100644
index 00000000000..da079e95496
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_client.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_SINK_MANAGER_CLIENT_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_SINK_MANAGER_CLIENT_H_
+
+#include "components/viz/service/viz_service_export.h"
+
+namespace cc {
+class BeginFrameSource;
+}
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT FrameSinkManagerClient {
+ public:
+ virtual ~FrameSinkManagerClient() = default;
+
+ // This allows the FrameSinkManagerImpl to pass a BeginFrameSource to use.
+ virtual void SetBeginFrameSource(
+ cc::BeginFrameSource* begin_frame_source) = 0;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_SINK_MANAGER_CLIENT_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
new file mode 100644
index 00000000000..8fed8fb7c03
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -0,0 +1,353 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "components/viz/service/display/display.h"
+#include "components/viz/service/display_embedder/display_provider.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_client.h"
+#include "components/viz/service/frame_sinks/gpu_compositor_frame_sink.h"
+#include "components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h"
+#include "components/viz/service/frame_sinks/primary_begin_frame_source.h"
+
+#if DCHECK_IS_ON()
+#include <sstream>
+#endif
+
+namespace viz {
+
+FrameSinkManagerImpl::FrameSinkSourceMapping::FrameSinkSourceMapping() =
+ default;
+
+FrameSinkManagerImpl::FrameSinkSourceMapping::FrameSinkSourceMapping(
+ const FrameSinkSourceMapping& other) = default;
+
+FrameSinkManagerImpl::FrameSinkSourceMapping::~FrameSinkSourceMapping() =
+ default;
+
+FrameSinkManagerImpl::FrameSinkManagerImpl(
+ DisplayProvider* display_provider,
+ cc::SurfaceManager::LifetimeType lifetime_type)
+ : display_provider_(display_provider),
+ surface_manager_(lifetime_type),
+ binding_(this) {
+ surface_manager_.AddObserver(this);
+}
+
+FrameSinkManagerImpl::~FrameSinkManagerImpl() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // All FrameSinks should be unregistered prior to FrameSinkManager
+ // destruction.
+ compositor_frame_sinks_.clear();
+ DCHECK_EQ(clients_.size(), 0u);
+ DCHECK_EQ(registered_sources_.size(), 0u);
+ surface_manager_.RemoveObserver(this);
+}
+
+void FrameSinkManagerImpl::BindAndSetClient(
+ cc::mojom::FrameSinkManagerRequest request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ cc::mojom::FrameSinkManagerClientPtr client) {
+ DCHECK(!client_);
+ DCHECK(!binding_.is_bound());
+ binding_.Bind(std::move(request), std::move(task_runner));
+ client_ptr_ = std::move(client);
+
+ client_ = client_ptr_.get();
+}
+
+void FrameSinkManagerImpl::SetLocalClient(
+ cc::mojom::FrameSinkManagerClient* client) {
+ DCHECK(!client_ptr_);
+
+ client_ = client;
+}
+
+void FrameSinkManagerImpl::CreateRootCompositorFrameSink(
+ const FrameSinkId& frame_sink_id,
+ gpu::SurfaceHandle surface_handle,
+ cc::mojom::CompositorFrameSinkAssociatedRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client,
+ cc::mojom::DisplayPrivateAssociatedRequest display_private_request) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK_NE(surface_handle, gpu::kNullSurfaceHandle);
+ DCHECK_EQ(0u, compositor_frame_sinks_.count(frame_sink_id));
+ DCHECK(display_provider_);
+
+ std::unique_ptr<cc::BeginFrameSource> begin_frame_source;
+ auto display = display_provider_->CreateDisplay(frame_sink_id, surface_handle,
+ &begin_frame_source);
+
+ compositor_frame_sinks_[frame_sink_id] =
+ base::MakeUnique<GpuRootCompositorFrameSink>(
+ this, frame_sink_id, std::move(display),
+ std::move(begin_frame_source), std::move(request),
+ std::move(private_request), std::move(client),
+ std::move(display_private_request));
+}
+
+void FrameSinkManagerImpl::CreateCompositorFrameSink(
+ const FrameSinkId& frame_sink_id,
+ cc::mojom::CompositorFrameSinkRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK_EQ(0u, compositor_frame_sinks_.count(frame_sink_id));
+
+ compositor_frame_sinks_[frame_sink_id] =
+ base::MakeUnique<GpuCompositorFrameSink>(
+ this, frame_sink_id, std::move(request), std::move(private_request),
+ std::move(client));
+}
+
+void FrameSinkManagerImpl::RegisterFrameSinkHierarchy(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) {
+ // If it's possible to reach the parent through the child's descendant chain,
+ // then this will create an infinite loop. Might as well just crash here.
+ CHECK(!ChildContains(child_frame_sink_id, parent_frame_sink_id));
+
+ std::vector<FrameSinkId>& children =
+ frame_sink_source_map_[parent_frame_sink_id].children;
+ for (size_t i = 0; i < children.size(); ++i)
+ DCHECK(children[i] != child_frame_sink_id);
+ children.push_back(child_frame_sink_id);
+
+ // If the parent has no source, then attaching it to this child will
+ // not change any downstream sources.
+ cc::BeginFrameSource* parent_source =
+ frame_sink_source_map_[parent_frame_sink_id].source;
+ if (!parent_source)
+ return;
+
+ DCHECK_EQ(registered_sources_.count(parent_source), 1u);
+ RecursivelyAttachBeginFrameSource(child_frame_sink_id, parent_source);
+}
+
+void FrameSinkManagerImpl::UnregisterFrameSinkHierarchy(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) {
+ // Deliberately do not check validity of either parent or child
+ // FrameSinkId here. They were valid during the registration, so were
+ // valid at some point in time. This makes it possible to invalidate parent
+ // and child FrameSinkIds independently of each other and not have an ordering
+ // dependency of unregistering the hierarchy first before either of them.
+ DCHECK_EQ(frame_sink_source_map_.count(parent_frame_sink_id), 1u);
+
+ auto iter = frame_sink_source_map_.find(parent_frame_sink_id);
+
+ std::vector<FrameSinkId>& children = iter->second.children;
+ bool found_child = false;
+ for (size_t i = 0; i < children.size(); ++i) {
+ if (children[i] == child_frame_sink_id) {
+ found_child = true;
+ children[i] = children.back();
+ children.resize(children.size() - 1);
+ break;
+ }
+ }
+ DCHECK(found_child);
+
+ // The CompositorFrameSinkSupport and hierarchy can be registered/unregistered
+ // in either order, so empty frame_sink_source_map entries need to be
+ // checked when removing either clients or relationships.
+ if (!iter->second.has_children() && !clients_.count(parent_frame_sink_id) &&
+ !iter->second.source) {
+ frame_sink_source_map_.erase(iter);
+ return;
+ }
+
+ // If the parent does not have a begin frame source, then disconnecting it
+ // will not change any of its children.
+ cc::BeginFrameSource* parent_source = iter->second.source;
+ if (!parent_source)
+ return;
+
+ // TODO(enne): these walks could be done in one step.
+ RecursivelyDetachBeginFrameSource(child_frame_sink_id, parent_source);
+ for (auto source_iter : registered_sources_)
+ RecursivelyAttachBeginFrameSource(source_iter.second, source_iter.first);
+}
+
+void FrameSinkManagerImpl::DropTemporaryReference(const SurfaceId& surface_id) {
+ surface_manager_.DropTemporaryReference(surface_id);
+}
+
+void FrameSinkManagerImpl::RegisterFrameSinkManagerClient(
+ const FrameSinkId& frame_sink_id,
+ FrameSinkManagerClient* client) {
+ DCHECK(client);
+
+ clients_[frame_sink_id] = client;
+
+ auto it = frame_sink_source_map_.find(frame_sink_id);
+ if (it != frame_sink_source_map_.end()) {
+ if (it->second.source)
+ client->SetBeginFrameSource(it->second.source);
+ }
+}
+
+void FrameSinkManagerImpl::UnregisterFrameSinkManagerClient(
+ const FrameSinkId& frame_sink_id) {
+ auto client_iter = clients_.find(frame_sink_id);
+ DCHECK(client_iter != clients_.end());
+
+ auto source_iter = frame_sink_source_map_.find(frame_sink_id);
+ if (source_iter != frame_sink_source_map_.end()) {
+ if (source_iter->second.source)
+ client_iter->second->SetBeginFrameSource(nullptr);
+ }
+ clients_.erase(client_iter);
+}
+
+void FrameSinkManagerImpl::RegisterBeginFrameSource(
+ cc::BeginFrameSource* source,
+ const FrameSinkId& frame_sink_id) {
+ DCHECK(source);
+ DCHECK_EQ(registered_sources_.count(source), 0u);
+
+ registered_sources_[source] = frame_sink_id;
+ RecursivelyAttachBeginFrameSource(frame_sink_id, source);
+
+ primary_source_.OnBeginFrameSourceAdded(source);
+}
+
+void FrameSinkManagerImpl::UnregisterBeginFrameSource(
+ cc::BeginFrameSource* source) {
+ DCHECK(source);
+ DCHECK_EQ(registered_sources_.count(source), 1u);
+
+ FrameSinkId frame_sink_id = registered_sources_[source];
+ registered_sources_.erase(source);
+
+ primary_source_.OnBeginFrameSourceRemoved(source);
+
+ if (frame_sink_source_map_.count(frame_sink_id) == 0u)
+ return;
+
+ // TODO(enne): these walks could be done in one step.
+ // Remove this begin frame source from its subtree.
+ RecursivelyDetachBeginFrameSource(frame_sink_id, source);
+ // Then flush every remaining registered source to fix any sources that
+ // became null because of the previous step but that have an alternative.
+ for (auto source_iter : registered_sources_)
+ RecursivelyAttachBeginFrameSource(source_iter.second, source_iter.first);
+}
+
+cc::BeginFrameSource* FrameSinkManagerImpl::GetPrimaryBeginFrameSource() {
+ return &primary_source_;
+}
+
+void FrameSinkManagerImpl::RecursivelyAttachBeginFrameSource(
+ const FrameSinkId& frame_sink_id,
+ cc::BeginFrameSource* source) {
+ FrameSinkSourceMapping& mapping = frame_sink_source_map_[frame_sink_id];
+ if (!mapping.source) {
+ mapping.source = source;
+ auto client_iter = clients_.find(frame_sink_id);
+ if (client_iter != clients_.end())
+ client_iter->second->SetBeginFrameSource(source);
+ }
+ for (size_t i = 0; i < mapping.children.size(); ++i) {
+ // |frame_sink_source_map_| is a container that can allocate new memory and
+ // move data between buffers. Copy child's FrameSinkId before passing
+ // it to RecursivelyAttachBeginFrameSource so that we don't reference data
+ // inside |frame_sink_source_map_|.
+ FrameSinkId child_copy = mapping.children[i];
+ RecursivelyAttachBeginFrameSource(child_copy, source);
+ }
+}
+
+void FrameSinkManagerImpl::RecursivelyDetachBeginFrameSource(
+ const FrameSinkId& frame_sink_id,
+ cc::BeginFrameSource* source) {
+ auto iter = frame_sink_source_map_.find(frame_sink_id);
+ if (iter == frame_sink_source_map_.end())
+ return;
+ if (iter->second.source == source) {
+ iter->second.source = nullptr;
+ auto client_iter = clients_.find(frame_sink_id);
+ if (client_iter != clients_.end())
+ client_iter->second->SetBeginFrameSource(nullptr);
+ }
+
+ if (!iter->second.has_children() && !clients_.count(frame_sink_id)) {
+ frame_sink_source_map_.erase(iter);
+ return;
+ }
+
+ std::vector<FrameSinkId>& children = iter->second.children;
+ for (size_t i = 0; i < children.size(); ++i) {
+ RecursivelyDetachBeginFrameSource(children[i], source);
+ }
+}
+
+bool FrameSinkManagerImpl::ChildContains(
+ const FrameSinkId& child_frame_sink_id,
+ const FrameSinkId& search_frame_sink_id) const {
+ auto iter = frame_sink_source_map_.find(child_frame_sink_id);
+ if (iter == frame_sink_source_map_.end())
+ return false;
+
+ const std::vector<FrameSinkId>& children = iter->second.children;
+ for (size_t i = 0; i < children.size(); ++i) {
+ if (children[i] == search_frame_sink_id)
+ return true;
+ if (ChildContains(children[i], search_frame_sink_id))
+ return true;
+ }
+ return false;
+}
+
+void FrameSinkManagerImpl::OnSurfaceCreated(const SurfaceInfo& surface_info) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK_GT(surface_info.device_scale_factor(), 0.0f);
+
+ // TODO(kylechar): |client_| will try to find an owner for the temporary
+ // reference to the new surface. With surface synchronization this might not
+ // be necessary, because a surface reference might already exist and no
+ // temporary reference was created. It could be useful to let |client_| know
+ // if it should find an owner.
+ if (client_)
+ client_->OnSurfaceCreated(surface_info);
+}
+
+bool FrameSinkManagerImpl::OnSurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) {
+ return false;
+}
+
+void FrameSinkManagerImpl::OnSurfaceDiscarded(const SurfaceId& surface_id) {}
+
+void FrameSinkManagerImpl::OnSurfaceDestroyed(const SurfaceId& surface_id) {}
+
+void FrameSinkManagerImpl::OnSurfaceDamageExpected(
+ const SurfaceId& surface_id,
+ const cc::BeginFrameArgs& args) {}
+
+void FrameSinkManagerImpl::OnSurfaceWillDraw(const SurfaceId& surface_id) {}
+
+void FrameSinkManagerImpl::OnClientConnectionLost(
+ const FrameSinkId& frame_sink_id) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (client_)
+ client_->OnClientConnectionClosed(frame_sink_id);
+}
+
+void FrameSinkManagerImpl::OnPrivateConnectionLost(
+ const FrameSinkId& frame_sink_id) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DestroyCompositorFrameSink(frame_sink_id);
+}
+
+void FrameSinkManagerImpl::DestroyCompositorFrameSink(FrameSinkId sink_id) {
+ compositor_frame_sinks_.erase(sink_id);
+}
+
+} // namespace viz
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
new file mode 100644
index 00000000000..e042b874e87
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -0,0 +1,196 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_SINK_MANAGER_IMPL_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_SINK_MANAGER_IMPL_H_
+
+#include <stdint.h>
+
+#include <unordered_map>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "cc/ipc/frame_sink_manager.mojom.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/surfaces/surface_observer.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/service/frame_sinks/primary_begin_frame_source.h"
+#include "components/viz/service/viz_service_export.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace cc {
+
+class BeginFrameSource;
+
+namespace test {
+class SurfaceSynchronizationTest;
+}
+
+} // namespace cc
+
+namespace viz {
+
+class DisplayProvider;
+class FrameSinkManagerClient;
+
+// FrameSinkManagerImpl manages BeginFrame hierarchy. This is the implementation
+// detail for FrameSinkManagerImpl.
+class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
+ : public cc::SurfaceObserver,
+ public NON_EXPORTED_BASE(cc::mojom::FrameSinkManager) {
+ public:
+ FrameSinkManagerImpl(DisplayProvider* display_provider = nullptr,
+ cc::SurfaceManager::LifetimeType lifetime_type =
+ cc::SurfaceManager::LifetimeType::SEQUENCES);
+ ~FrameSinkManagerImpl() override;
+
+ // 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.
+ void BindAndSetClient(cc::mojom::FrameSinkManagerRequest request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ cc::mojom::FrameSinkManagerClientPtr client);
+
+ // Sets up a direction connection to |client| without using Mojo.
+ void SetLocalClient(cc::mojom::FrameSinkManagerClient* client);
+
+ // cc::mojom::FrameSinkManager implementation:
+ void CreateRootCompositorFrameSink(
+ const FrameSinkId& frame_sink_id,
+ gpu::SurfaceHandle surface_handle,
+ cc::mojom::CompositorFrameSinkAssociatedRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client,
+ cc::mojom::DisplayPrivateAssociatedRequest display_private_request)
+ override;
+ void CreateCompositorFrameSink(
+ const FrameSinkId& frame_sink_id,
+ cc::mojom::CompositorFrameSinkRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client) override;
+ void RegisterFrameSinkHierarchy(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) override;
+ void UnregisterFrameSinkHierarchy(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) override;
+ void DropTemporaryReference(const SurfaceId& surface_id) override;
+
+ // CompositorFrameSinkSupport, hierarchy, and BeginFrameSource can be
+ // registered and unregistered in any order with respect to each other.
+ //
+ // This happens in practice, e.g. the relationship to between ui::Compositor /
+ // DelegatedFrameHost is known before ui::Compositor has a surface/client).
+ // However, DelegatedFrameHost can register itself as a client before its
+ // relationship with the ui::Compositor is known.
+
+ // Associates a FrameSinkManagerClient with the frame_sink_id it uses.
+ // FrameSinkManagerClient and framesink allocators have a 1:1 mapping.
+ // Caller guarantees the client is alive between register/unregister.
+ void RegisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id,
+ FrameSinkManagerClient* client);
+ void UnregisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id);
+
+ // Associates a |source| with a particular framesink. That framesink and
+ // any children of that framesink with valid clients can potentially use
+ // that |source|.
+ void RegisterBeginFrameSource(cc::BeginFrameSource* source,
+ const FrameSinkId& frame_sink_id);
+ void UnregisterBeginFrameSource(cc::BeginFrameSource* source);
+
+ // Returns a stable BeginFrameSource that forwards BeginFrames from the first
+ // available BeginFrameSource.
+ cc::BeginFrameSource* GetPrimaryBeginFrameSource();
+
+ cc::SurfaceManager* surface_manager() { return &surface_manager_; }
+
+ // cc::SurfaceObserver implementation.
+ void OnSurfaceCreated(const SurfaceInfo& surface_info) override;
+ bool OnSurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) override;
+ void OnSurfaceDiscarded(const SurfaceId& surface_id) override;
+ void OnSurfaceDestroyed(const SurfaceId& surface_id) override;
+ void OnSurfaceDamageExpected(const SurfaceId& surface_id,
+ const cc::BeginFrameArgs& args) override;
+ void OnSurfaceWillDraw(const SurfaceId& surface_id) override;
+
+ void OnClientConnectionLost(const FrameSinkId& frame_sink_id);
+ void OnPrivateConnectionLost(const FrameSinkId& frame_sink_id);
+
+ // It is necessary to pass |frame_sink_id| by value because the id
+ // is owned by the GpuCompositorFrameSink in the map. When the sink is
+ // removed from the map, |frame_sink_id| would also be destroyed if it were a
+ // reference. But the map can continue to iterate and try to use it. Passing
+ // by value avoids this.
+ void DestroyCompositorFrameSink(FrameSinkId frame_sink_id);
+
+ private:
+ friend class cc::test::SurfaceSynchronizationTest;
+
+ void RecursivelyAttachBeginFrameSource(const FrameSinkId& frame_sink_id,
+ cc::BeginFrameSource* source);
+ void RecursivelyDetachBeginFrameSource(const FrameSinkId& frame_sink_id,
+ cc::BeginFrameSource* source);
+
+ // Returns true if |child framesink| is or has |search_frame_sink_id| as a
+ // child.
+ bool ChildContains(const FrameSinkId& child_frame_sink_id,
+ const FrameSinkId& search_frame_sink_id) const;
+
+ // Begin frame source routing. Both BeginFrameSource and
+ // CompositorFrameSinkSupport pointers guaranteed alive by callers until
+ // unregistered.
+ struct FrameSinkSourceMapping {
+ FrameSinkSourceMapping();
+ FrameSinkSourceMapping(const FrameSinkSourceMapping& other);
+ ~FrameSinkSourceMapping();
+ bool has_children() const { return !children.empty(); }
+ // The currently assigned begin frame source for this client.
+ cc::BeginFrameSource* source = nullptr;
+ // This represents a dag of parent -> children mapping.
+ std::vector<FrameSinkId> children;
+ };
+
+ // Provides a Display for CreateRootCompositorFrameSink().
+ DisplayProvider* const display_provider_;
+
+ base::flat_map<FrameSinkId, FrameSinkManagerClient*> clients_;
+ std::unordered_map<FrameSinkId, FrameSinkSourceMapping, FrameSinkIdHash>
+ frame_sink_source_map_;
+
+ // Set of BeginFrameSource along with associated FrameSinkIds. Any child
+ // that is implicitly using this framesink must be reachable by the
+ // parent in the dag.
+ std::unordered_map<cc::BeginFrameSource*, FrameSinkId> registered_sources_;
+
+ PrimaryBeginFrameSource primary_source_;
+
+ // |surface_manager_| should be placed under |primary_source_| so that all
+ // surfaces are destroyed before |primary_source_|.
+ cc::SurfaceManager surface_manager_;
+
+ std::unordered_map<FrameSinkId,
+ std::unique_ptr<cc::mojom::CompositorFrameSink>,
+ FrameSinkIdHash>
+ compositor_frame_sinks_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ // This will point to |client_ptr_| if using Mojo or a provided client if
+ // directly connected. Use this to make function calls.
+ cc::mojom::FrameSinkManagerClient* client_ = nullptr;
+
+ cc::mojom::FrameSinkManagerClientPtr client_ptr_;
+ mojo::Binding<cc::mojom::FrameSinkManager> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameSinkManagerImpl);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_FRAME_SINK_MANAGER_IMPL_H_
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
new file mode 100644
index 00000000000..8fd888d7ba1
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
@@ -0,0 +1,529 @@
+// 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 <stddef.h>
+
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/test/begin_frame_source_test.h"
+#include "cc/test/fake_external_begin_frame_source.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_client.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+
+namespace {
+
+constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
+}
+
+class FakeFrameSinkManagerClient : public FrameSinkManagerClient {
+ public:
+ explicit FakeFrameSinkManagerClient(const FrameSinkId& frame_sink_id)
+ : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) {}
+
+ FakeFrameSinkManagerClient(const FrameSinkId& frame_sink_id,
+ FrameSinkManagerImpl* manager)
+ : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) {
+ DCHECK(manager);
+ Register(manager);
+ }
+
+ ~FakeFrameSinkManagerClient() override {
+ if (manager_) {
+ Unregister();
+ }
+ EXPECT_EQ(nullptr, source_);
+ }
+
+ cc::BeginFrameSource* source() { return source_; }
+ const FrameSinkId& frame_sink_id() { return frame_sink_id_; }
+
+ void Register(FrameSinkManagerImpl* manager) {
+ EXPECT_EQ(nullptr, manager_);
+ manager_ = manager;
+ manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this);
+ }
+
+ void Unregister() {
+ EXPECT_NE(manager_, nullptr);
+ manager_->UnregisterFrameSinkManagerClient(frame_sink_id_);
+ manager_ = nullptr;
+ }
+
+ // FrameSinkManagerClient implementation.
+ void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override {
+ DCHECK(!source_ || !begin_frame_source);
+ source_ = begin_frame_source;
+ }
+
+ private:
+ cc::BeginFrameSource* source_;
+ FrameSinkManagerImpl* manager_;
+ FrameSinkId frame_sink_id_;
+};
+
+class FrameSinkManagerTest : public testing::Test {
+ public:
+ FrameSinkManagerTest() = default;
+
+ ~FrameSinkManagerTest() override = default;
+
+ protected:
+ FrameSinkManagerImpl manager_;
+};
+
+TEST_F(FrameSinkManagerTest, SingleClients) {
+ FakeFrameSinkManagerClient client(FrameSinkId(1, 1));
+ FakeFrameSinkManagerClient other_client(FrameSinkId(2, 2));
+ cc::StubBeginFrameSource source;
+
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+ client.Register(&manager_);
+ other_client.Register(&manager_);
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+
+ // Test setting unsetting BFS
+ manager_.RegisterBeginFrameSource(&source, client.frame_sink_id());
+ EXPECT_EQ(&source, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+
+ // Set BFS for other namespace
+ manager_.RegisterBeginFrameSource(&source, other_client.frame_sink_id());
+ EXPECT_EQ(&source, other_client.source());
+ EXPECT_EQ(nullptr, client.source());
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+
+ // Re-set BFS for original
+ manager_.RegisterBeginFrameSource(&source, client.frame_sink_id());
+ EXPECT_EQ(&source, client.source());
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+}
+
+// This test verifies that a client is still connected to the BeginFrameSource
+// after restart.
+TEST_F(FrameSinkManagerTest, ClientRestart) {
+ FakeFrameSinkManagerClient client(kArbitraryFrameSinkId);
+ cc::StubBeginFrameSource source;
+ client.Register(&manager_);
+
+ manager_.RegisterBeginFrameSource(&source, kArbitraryFrameSinkId);
+ EXPECT_EQ(&source, client.source());
+
+ // |client| is disconnect from |source| after Unregister.
+ client.Unregister();
+ EXPECT_EQ(nullptr, client.source());
+
+ // |client| is reconnected with |source| after re-Register.
+ client.Register(&manager_);
+ EXPECT_EQ(&source, client.source());
+
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+}
+
+// This test verifies that a PrimaryBeginFrameSource will receive BeginFrames
+// from the first BeginFrameSource registered. If that BeginFrameSource goes
+// away then it will receive BeginFrames from the second BeginFrameSource.
+TEST_F(FrameSinkManagerTest, PrimaryBeginFrameSource) {
+ // This PrimaryBeginFrameSource should track the first BeginFrameSource
+ // registered with the SurfaceManager.
+ testing::NiceMock<cc::MockBeginFrameObserver> obs;
+ cc::BeginFrameSource* begin_frame_source =
+ manager_.GetPrimaryBeginFrameSource();
+ begin_frame_source->AddObserver(&obs);
+
+ FakeFrameSinkManagerClient root1(FrameSinkId(1, 1), &manager_);
+ std::unique_ptr<cc::FakeExternalBeginFrameSource> external_source1 =
+ base::MakeUnique<cc::FakeExternalBeginFrameSource>(60.f, false);
+ manager_.RegisterBeginFrameSource(external_source1.get(),
+ root1.frame_sink_id());
+
+ FakeFrameSinkManagerClient root2(FrameSinkId(2, 2), &manager_);
+ std::unique_ptr<cc::FakeExternalBeginFrameSource> external_source2 =
+ base::MakeUnique<cc::FakeExternalBeginFrameSource>(60.f, false);
+ manager_.RegisterBeginFrameSource(external_source2.get(),
+ root2.frame_sink_id());
+
+ cc::BeginFrameArgs args =
+ cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
+
+ // Ticking |external_source2| does not propagate to |begin_frame_source|.
+ {
+ EXPECT_CALL(obs, OnBeginFrame(args)).Times(0);
+ external_source2->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+ }
+
+ // Ticking |external_source1| does propagate to |begin_frame_source| and
+ // |obs|.
+ {
+ EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(1);
+ external_source1->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+ }
+
+ // Getting rid of |external_source1| means those BeginFrames will not
+ // propagate. Instead, |external_source2|'s BeginFrames will propagate
+ // to |begin_frame_source|.
+ {
+ manager_.UnregisterBeginFrameSource(external_source1.get());
+ EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(0);
+ external_source1->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+
+ EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(1);
+ external_source2->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+ }
+
+ // Tear down
+ manager_.UnregisterBeginFrameSource(external_source2.get());
+ begin_frame_source->RemoveObserver(&obs);
+}
+
+TEST_F(FrameSinkManagerTest, MultipleDisplays) {
+ cc::StubBeginFrameSource root1_source;
+ cc::StubBeginFrameSource root2_source;
+
+ // root1 -> A -> B
+ // root2 -> C
+ FakeFrameSinkManagerClient root1(FrameSinkId(1, 1), &manager_);
+ FakeFrameSinkManagerClient root2(FrameSinkId(2, 2), &manager_);
+ FakeFrameSinkManagerClient client_a(FrameSinkId(3, 3), &manager_);
+ FakeFrameSinkManagerClient client_b(FrameSinkId(4, 4), &manager_);
+ FakeFrameSinkManagerClient client_c(FrameSinkId(5, 5), &manager_);
+
+ manager_.RegisterBeginFrameSource(&root1_source, root1.frame_sink_id());
+ manager_.RegisterBeginFrameSource(&root2_source, root2.frame_sink_id());
+ EXPECT_EQ(root1.source(), &root1_source);
+ EXPECT_EQ(root2.source(), &root2_source);
+
+ // Set up initial hierarchy.
+ manager_.RegisterFrameSinkHierarchy(root1.frame_sink_id(),
+ client_a.frame_sink_id());
+ EXPECT_EQ(client_a.source(), root1.source());
+ manager_.RegisterFrameSinkHierarchy(client_a.frame_sink_id(),
+ client_b.frame_sink_id());
+ EXPECT_EQ(client_b.source(), root1.source());
+ manager_.RegisterFrameSinkHierarchy(root2.frame_sink_id(),
+ client_c.frame_sink_id());
+ EXPECT_EQ(client_c.source(), root2.source());
+
+ // Attach A into root2's subtree, like a window moving across displays.
+ // root1 -> A -> B
+ // root2 -> C -> A -> B
+ manager_.RegisterFrameSinkHierarchy(client_c.frame_sink_id(),
+ client_a.frame_sink_id());
+ // With the heuristic of just keeping existing BFS in the face of multiple,
+ // no client sources should change.
+ EXPECT_EQ(client_a.source(), root1.source());
+ EXPECT_EQ(client_b.source(), root1.source());
+ EXPECT_EQ(client_c.source(), root2.source());
+
+ // Detach A from root1. A and B should now be updated to root2.
+ manager_.UnregisterFrameSinkHierarchy(root1.frame_sink_id(),
+ client_a.frame_sink_id());
+ EXPECT_EQ(client_a.source(), root2.source());
+ EXPECT_EQ(client_b.source(), root2.source());
+ EXPECT_EQ(client_c.source(), root2.source());
+
+ // Detach root1 from BFS. root1 should now have no source.
+ manager_.UnregisterBeginFrameSource(&root1_source);
+ EXPECT_EQ(nullptr, root1.source());
+ EXPECT_NE(nullptr, root2.source());
+
+ // Detatch root2 from BFS.
+ manager_.UnregisterBeginFrameSource(&root2_source);
+ EXPECT_EQ(nullptr, client_a.source());
+ EXPECT_EQ(nullptr, client_b.source());
+ EXPECT_EQ(nullptr, client_c.source());
+ EXPECT_EQ(nullptr, root2.source());
+
+ // Cleanup hierarchy.
+ manager_.UnregisterFrameSinkHierarchy(root2.frame_sink_id(),
+ client_c.frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_c.frame_sink_id(),
+ client_a.frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_a.frame_sink_id(),
+ client_b.frame_sink_id());
+}
+
+// This test verifies that a BeginFrameSource path to the root from a
+// FrameSinkId is preserved even if that FrameSinkId has no children
+// and does not have a corresponding FrameSinkManagerClient.
+TEST_F(FrameSinkManagerTest, ParentWithoutClientRetained) {
+ cc::StubBeginFrameSource root_source;
+
+ constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
+ constexpr FrameSinkId kFrameSinkIdA(2, 2);
+ constexpr FrameSinkId kFrameSinkIdB(3, 3);
+ constexpr FrameSinkId kFrameSinkIdC(4, 4);
+
+ FakeFrameSinkManagerClient root(kFrameSinkIdRoot, &manager_);
+ FakeFrameSinkManagerClient client_b(kFrameSinkIdB, &manager_);
+ FakeFrameSinkManagerClient client_c(kFrameSinkIdC, &manager_);
+
+ manager_.RegisterBeginFrameSource(&root_source, root.frame_sink_id());
+ EXPECT_EQ(&root_source, root.source());
+
+ // Set up initial hierarchy: root -> A -> B.
+ // Note that A does not have a FrameSinkManagerClient.
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ // The root's BeginFrameSource should propagate to B.
+ EXPECT_EQ(root.source(), client_b.source());
+
+ // Unregister B, and attach C to A: root -> A -> C
+ manager_.UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ EXPECT_EQ(nullptr, client_b.source());
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
+ // The root's BeginFrameSource should propagate to C.
+ EXPECT_EQ(root.source(), client_c.source());
+
+ manager_.UnregisterBeginFrameSource(&root_source);
+ EXPECT_EQ(nullptr, root.source());
+ EXPECT_EQ(nullptr, client_c.source());
+}
+
+// This test sets up the same hierarchy as ParentWithoutClientRetained.
+// However, this unit test registers the BeginFrameSource AFTER C
+// has been attached to A. This test verifies that the BeginFrameSource
+// propagates all the way to C.
+TEST_F(FrameSinkManagerTest,
+ ParentWithoutClientRetained_LateBeginFrameRegistration) {
+ cc::StubBeginFrameSource root_source;
+
+ constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
+ constexpr FrameSinkId kFrameSinkIdA(2, 2);
+ constexpr FrameSinkId kFrameSinkIdB(3, 3);
+ constexpr FrameSinkId kFrameSinkIdC(4, 4);
+
+ FakeFrameSinkManagerClient root(kFrameSinkIdRoot, &manager_);
+ FakeFrameSinkManagerClient client_b(kFrameSinkIdB, &manager_);
+ FakeFrameSinkManagerClient client_c(kFrameSinkIdC, &manager_);
+
+ // Set up initial hierarchy: root -> A -> B.
+ // Note that A does not have a FrameSinkManagerClient.
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ // The root does not yet have a BeginFrameSource so client B should not have
+ // one either.
+ EXPECT_EQ(nullptr, client_b.source());
+
+ // Unregister B, and attach C to A: root -> A -> C
+ manager_.UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
+
+ // Registering a BeginFrameSource at the root should propagate it to C.
+ manager_.RegisterBeginFrameSource(&root_source, root.frame_sink_id());
+ // The root's BeginFrameSource should propagate to C.
+ EXPECT_EQ(&root_source, root.source());
+ EXPECT_EQ(root.source(), client_c.source());
+
+ manager_.UnregisterBeginFrameSource(&root_source);
+ EXPECT_EQ(nullptr, root.source());
+ EXPECT_EQ(nullptr, client_c.source());
+}
+
+// In practice, registering and unregistering both parent/child relationships
+// and FrameSinkManagerClients can happen in any ordering with respect to
+// each other. These following tests verify that all the data structures
+// are properly set up and cleaned up under the four permutations of orderings
+// of this nesting.
+
+class SurfaceManagerOrderingTest : public FrameSinkManagerTest {
+ public:
+ SurfaceManagerOrderingTest()
+ : client_a_(FrameSinkId(1, 1)),
+ client_b_(FrameSinkId(2, 2)),
+ client_c_(FrameSinkId(3, 3)),
+ hierarchy_registered_(false),
+ clients_registered_(false),
+ bfs_registered_(false) {
+ AssertCorrectBFSState();
+ }
+
+ ~SurfaceManagerOrderingTest() override {
+ EXPECT_FALSE(hierarchy_registered_);
+ EXPECT_FALSE(clients_registered_);
+ EXPECT_FALSE(bfs_registered_);
+ AssertCorrectBFSState();
+ }
+
+ void RegisterHierarchy() {
+ DCHECK(!hierarchy_registered_);
+ hierarchy_registered_ = true;
+ manager_.RegisterFrameSinkHierarchy(client_a_.frame_sink_id(),
+ client_b_.frame_sink_id());
+ manager_.RegisterFrameSinkHierarchy(client_b_.frame_sink_id(),
+ client_c_.frame_sink_id());
+ AssertCorrectBFSState();
+ }
+ void UnregisterHierarchy() {
+ DCHECK(hierarchy_registered_);
+ hierarchy_registered_ = false;
+ manager_.UnregisterFrameSinkHierarchy(client_a_.frame_sink_id(),
+ client_b_.frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_b_.frame_sink_id(),
+ client_c_.frame_sink_id());
+ AssertCorrectBFSState();
+ }
+
+ void RegisterClients() {
+ DCHECK(!clients_registered_);
+ clients_registered_ = true;
+ client_a_.Register(&manager_);
+ client_b_.Register(&manager_);
+ client_c_.Register(&manager_);
+ AssertCorrectBFSState();
+ }
+
+ void UnregisterClients() {
+ DCHECK(clients_registered_);
+ clients_registered_ = false;
+ client_a_.Unregister();
+ client_b_.Unregister();
+ client_c_.Unregister();
+ AssertCorrectBFSState();
+ }
+
+ void RegisterBFS() {
+ DCHECK(!bfs_registered_);
+ bfs_registered_ = true;
+ manager_.RegisterBeginFrameSource(&source_, client_a_.frame_sink_id());
+ AssertCorrectBFSState();
+ }
+ void UnregisterBFS() {
+ DCHECK(bfs_registered_);
+ bfs_registered_ = false;
+ manager_.UnregisterBeginFrameSource(&source_);
+ AssertCorrectBFSState();
+ }
+
+ void AssertEmptyBFS() {
+ EXPECT_EQ(nullptr, client_a_.source());
+ EXPECT_EQ(nullptr, client_b_.source());
+ EXPECT_EQ(nullptr, client_c_.source());
+ }
+
+ void AssertAllValidBFS() {
+ EXPECT_EQ(&source_, client_a_.source());
+ EXPECT_EQ(&source_, client_b_.source());
+ EXPECT_EQ(&source_, client_c_.source());
+ }
+
+ protected:
+ void AssertCorrectBFSState() {
+ if (!clients_registered_ || !bfs_registered_) {
+ AssertEmptyBFS();
+ return;
+ }
+ if (!hierarchy_registered_) {
+ // A valid but not attached to anything.
+ EXPECT_EQ(&source_, client_a_.source());
+ EXPECT_EQ(nullptr, client_b_.source());
+ EXPECT_EQ(nullptr, client_c_.source());
+ return;
+ }
+
+ AssertAllValidBFS();
+ }
+
+ cc::StubBeginFrameSource source_;
+ // A -> B -> C hierarchy, with A always having the BFS.
+ FakeFrameSinkManagerClient client_a_;
+ FakeFrameSinkManagerClient client_b_;
+ FakeFrameSinkManagerClient client_c_;
+
+ bool hierarchy_registered_;
+ bool clients_registered_;
+ bool bfs_registered_;
+};
+
+enum RegisterOrder { REGISTER_HIERARCHY_FIRST, REGISTER_CLIENTS_FIRST };
+enum UnregisterOrder { UNREGISTER_HIERARCHY_FIRST, UNREGISTER_CLIENTS_FIRST };
+enum BFSOrder { BFS_FIRST, BFS_SECOND, BFS_THIRD };
+
+static const RegisterOrder kRegisterOrderList[] = {REGISTER_HIERARCHY_FIRST,
+ REGISTER_CLIENTS_FIRST};
+static const UnregisterOrder kUnregisterOrderList[] = {
+ UNREGISTER_HIERARCHY_FIRST, UNREGISTER_CLIENTS_FIRST};
+static const BFSOrder kBFSOrderList[] = {BFS_FIRST, BFS_SECOND, BFS_THIRD};
+
+class SurfaceManagerOrderingParamTest
+ : public SurfaceManagerOrderingTest,
+ public ::testing::WithParamInterface<
+ std::tr1::tuple<RegisterOrder, UnregisterOrder, BFSOrder>> {};
+
+TEST_P(SurfaceManagerOrderingParamTest, Ordering) {
+ // Test the four permutations of client/hierarchy setting/unsetting and test
+ // each place the BFS can be added and removed. The BFS and the
+ // client/hierarchy are less related, so BFS is tested independently instead
+ // of every permutation of BFS setting and unsetting.
+ // The register/unregister functions themselves test most of the state.
+ RegisterOrder register_order = std::tr1::get<0>(GetParam());
+ UnregisterOrder unregister_order = std::tr1::get<1>(GetParam());
+ BFSOrder bfs_order = std::tr1::get<2>(GetParam());
+
+ // Attach everything up in the specified order.
+ if (bfs_order == BFS_FIRST)
+ RegisterBFS();
+
+ if (register_order == REGISTER_HIERARCHY_FIRST)
+ RegisterHierarchy();
+ else
+ RegisterClients();
+
+ if (bfs_order == BFS_SECOND)
+ RegisterBFS();
+
+ if (register_order == REGISTER_HIERARCHY_FIRST)
+ RegisterClients();
+ else
+ RegisterHierarchy();
+
+ if (bfs_order == BFS_THIRD)
+ RegisterBFS();
+
+ // Everything hooked up, so should be valid.
+ AssertAllValidBFS();
+
+ // Detach everything in the specified order.
+ if (bfs_order == BFS_THIRD)
+ UnregisterBFS();
+
+ if (unregister_order == UNREGISTER_HIERARCHY_FIRST)
+ UnregisterHierarchy();
+ else
+ UnregisterClients();
+
+ if (bfs_order == BFS_SECOND)
+ UnregisterBFS();
+
+ if (unregister_order == UNREGISTER_HIERARCHY_FIRST)
+ UnregisterClients();
+ else
+ UnregisterHierarchy();
+
+ if (bfs_order == BFS_FIRST)
+ UnregisterBFS();
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SurfaceManagerOrderingParamTestInstantiation,
+ SurfaceManagerOrderingParamTest,
+ ::testing::Combine(::testing::ValuesIn(kRegisterOrderList),
+ ::testing::ValuesIn(kUnregisterOrderList),
+ ::testing::ValuesIn(kBFSOrderList)));
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc b/chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc
new file mode 100644
index 00000000000..12e0d5bc124
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc
@@ -0,0 +1,106 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/gpu_compositor_frame_sink.h"
+
+#include <utility>
+
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+
+namespace viz {
+
+GpuCompositorFrameSink::GpuCompositorFrameSink(
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ cc::mojom::CompositorFrameSinkRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest
+ compositor_frame_sink_private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client)
+ : support_(CompositorFrameSinkSupport::Create(
+ this,
+ frame_sink_manager,
+ frame_sink_id,
+ false /* is_root */,
+ true /* handles_frame_sink_id_invalidation */,
+ true /* needs_sync_points */)),
+ client_(std::move(client)),
+ compositor_frame_sink_binding_(this, std::move(request)),
+ compositor_frame_sink_private_binding_(
+ this,
+ std::move(compositor_frame_sink_private_request)) {
+ compositor_frame_sink_binding_.set_connection_error_handler(base::Bind(
+ &GpuCompositorFrameSink::OnClientConnectionLost, base::Unretained(this)));
+ compositor_frame_sink_private_binding_.set_connection_error_handler(
+ base::Bind(&GpuCompositorFrameSink::OnPrivateConnectionLost,
+ base::Unretained(this)));
+}
+
+GpuCompositorFrameSink::~GpuCompositorFrameSink() = default;
+
+void GpuCompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
+ support_->SetNeedsBeginFrame(needs_begin_frame);
+}
+
+void GpuCompositorFrameSink::SubmitCompositorFrame(
+ const LocalSurfaceId& local_surface_id,
+ cc::CompositorFrame frame) {
+ if (!support_->SubmitCompositorFrame(local_surface_id, std::move(frame))) {
+ compositor_frame_sink_binding_.CloseWithReason(
+ 1, "Surface invariants violation");
+ OnClientConnectionLost();
+ }
+}
+
+void GpuCompositorFrameSink::DidNotProduceFrame(
+ const cc::BeginFrameAck& begin_frame_ack) {
+ support_->DidNotProduceFrame(begin_frame_ack);
+}
+
+void GpuCompositorFrameSink::DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) {
+ if (client_)
+ client_->DidReceiveCompositorFrameAck(resources);
+}
+
+void GpuCompositorFrameSink::ClaimTemporaryReference(
+ const SurfaceId& surface_id) {
+ support_->ClaimTemporaryReference(surface_id);
+}
+
+void GpuCompositorFrameSink::RequestCopyOfSurface(
+ std::unique_ptr<cc::CopyOutputRequest> request) {
+ support_->RequestCopyOfSurface(std::move(request));
+}
+
+void GpuCompositorFrameSink::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ if (client_)
+ client_->OnBeginFrame(args);
+}
+
+void GpuCompositorFrameSink::OnBeginFramePausedChanged(bool paused) {
+ if (client_)
+ client_->OnBeginFramePausedChanged(paused);
+}
+
+void GpuCompositorFrameSink::ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) {
+ if (client_)
+ client_->ReclaimResources(resources);
+}
+
+void GpuCompositorFrameSink::WillDrawSurface(
+ const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) {}
+
+void GpuCompositorFrameSink::OnClientConnectionLost() {
+ support_->frame_sink_manager()->OnClientConnectionLost(
+ support_->frame_sink_id());
+}
+
+void GpuCompositorFrameSink::OnPrivateConnectionLost() {
+ support_->frame_sink_manager()->OnPrivateConnectionLost(
+ support_->frame_sink_id());
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h b/chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h
new file mode 100644
index 00000000000..54a40561929
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h
@@ -0,0 +1,74 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/ipc/compositor_frame_sink.mojom.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_client.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace viz {
+
+// Server side representation of a WindowSurface.
+class GpuCompositorFrameSink
+ : public NON_EXPORTED_BASE(CompositorFrameSinkSupportClient),
+ public NON_EXPORTED_BASE(cc::mojom::CompositorFrameSink),
+ public NON_EXPORTED_BASE(cc::mojom::CompositorFrameSinkPrivate) {
+ public:
+ GpuCompositorFrameSink(
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ cc::mojom::CompositorFrameSinkRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client);
+
+ ~GpuCompositorFrameSink() override;
+
+ // cc::mojom::CompositorFrameSink:
+ void SetNeedsBeginFrame(bool needs_begin_frame) override;
+ void SubmitCompositorFrame(const LocalSurfaceId& local_surface_id,
+ cc::CompositorFrame frame) override;
+ void DidNotProduceFrame(const cc::BeginFrameAck& begin_frame_ack) override;
+
+ // cc::mojom::CompositorFrameSinkPrivate:
+ void ClaimTemporaryReference(const SurfaceId& surface_id) override;
+ void RequestCopyOfSurface(
+ std::unique_ptr<cc::CopyOutputRequest> request) override;
+
+ private:
+ // CompositorFrameSinkSupportClient implementation:
+ void DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ void OnBeginFramePausedChanged(bool paused) override;
+ void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void WillDrawSurface(const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) override;
+
+ void OnClientConnectionLost();
+ void OnPrivateConnectionLost();
+
+ std::unique_ptr<CompositorFrameSinkSupport> support_;
+
+ cc::mojom::CompositorFrameSinkClientPtr client_;
+ mojo::Binding<cc::mojom::CompositorFrameSink> compositor_frame_sink_binding_;
+ mojo::Binding<cc::mojom::CompositorFrameSinkPrivate>
+ compositor_frame_sink_private_binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuCompositorFrameSink);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_COMPOSITOR_FRAME_SINK_H_
diff --git a/chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc b/chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc
new file mode 100644
index 00000000000..610d3f85c08
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc
@@ -0,0 +1,163 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h"
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "components/viz/service/display/display.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+
+namespace viz {
+
+GpuRootCompositorFrameSink::GpuRootCompositorFrameSink(
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ std::unique_ptr<Display> display,
+ std::unique_ptr<cc::BeginFrameSource> begin_frame_source,
+ cc::mojom::CompositorFrameSinkAssociatedRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest
+ compositor_frame_sink_private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client,
+ cc::mojom::DisplayPrivateAssociatedRequest display_private_request)
+ : support_(CompositorFrameSinkSupport::Create(
+ this,
+ frame_sink_manager,
+ frame_sink_id,
+ true /* is_root */,
+ true /* handles_frame_sink_id_invalidation */,
+ true /* needs_sync_points */)),
+ display_begin_frame_source_(std::move(begin_frame_source)),
+ display_(std::move(display)),
+ client_(std::move(client)),
+ compositor_frame_sink_binding_(this, std::move(request)),
+ compositor_frame_sink_private_binding_(
+ this,
+ std::move(compositor_frame_sink_private_request)),
+ display_private_binding_(this, std::move(display_private_request)) {
+ DCHECK(display_begin_frame_source_);
+ compositor_frame_sink_binding_.set_connection_error_handler(
+ base::Bind(&GpuRootCompositorFrameSink::OnClientConnectionLost,
+ base::Unretained(this)));
+ compositor_frame_sink_private_binding_.set_connection_error_handler(
+ base::Bind(&GpuRootCompositorFrameSink::OnPrivateConnectionLost,
+ base::Unretained(this)));
+ frame_sink_manager->RegisterBeginFrameSource(
+ display_begin_frame_source_.get(), frame_sink_id);
+ display_->Initialize(this, frame_sink_manager->surface_manager());
+}
+
+GpuRootCompositorFrameSink::~GpuRootCompositorFrameSink() {
+ support_->frame_sink_manager()->UnregisterBeginFrameSource(
+ display_begin_frame_source_.get());
+}
+
+void GpuRootCompositorFrameSink::SetDisplayVisible(bool visible) {
+ DCHECK(display_);
+ display_->SetVisible(visible);
+}
+
+void GpuRootCompositorFrameSink::ResizeDisplay(const gfx::Size& size) {
+ DCHECK(display_);
+ display_->Resize(size);
+}
+
+void GpuRootCompositorFrameSink::SetDisplayColorSpace(
+ const gfx::ColorSpace& color_space) {
+ DCHECK(display_);
+ display_->SetColorSpace(color_space, color_space);
+}
+
+void GpuRootCompositorFrameSink::SetOutputIsSecure(bool secure) {
+ DCHECK(display_);
+ display_->SetOutputIsSecure(secure);
+}
+
+void GpuRootCompositorFrameSink::SetLocalSurfaceId(
+ const LocalSurfaceId& local_surface_id,
+ float scale_factor) {
+ display_->SetLocalSurfaceId(local_surface_id, scale_factor);
+}
+
+void GpuRootCompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
+ support_->SetNeedsBeginFrame(needs_begin_frame);
+}
+
+void GpuRootCompositorFrameSink::SubmitCompositorFrame(
+ const LocalSurfaceId& local_surface_id,
+ cc::CompositorFrame frame) {
+ if (!support_->SubmitCompositorFrame(local_surface_id, std::move(frame))) {
+ compositor_frame_sink_binding_.Close();
+ OnClientConnectionLost();
+ }
+}
+
+void GpuRootCompositorFrameSink::DidNotProduceFrame(
+ const cc::BeginFrameAck& begin_frame_ack) {
+ support_->DidNotProduceFrame(begin_frame_ack);
+}
+
+void GpuRootCompositorFrameSink::ClaimTemporaryReference(
+ const SurfaceId& surface_id) {
+ support_->ClaimTemporaryReference(surface_id);
+}
+
+void GpuRootCompositorFrameSink::RequestCopyOfSurface(
+ std::unique_ptr<cc::CopyOutputRequest> request) {
+ support_->RequestCopyOfSurface(std::move(request));
+}
+
+void GpuRootCompositorFrameSink::DisplayOutputSurfaceLost() {
+ // TODO(staraz): Implement this. Client should hear about context/output
+ // surface lost.
+}
+
+void GpuRootCompositorFrameSink::DisplayWillDrawAndSwap(
+ bool will_draw_and_swap,
+ const cc::RenderPassList& render_pass) {
+ hit_test_aggregator_.PostTaskAggregate(display_->CurrentSurfaceId());
+}
+
+void GpuRootCompositorFrameSink::DisplayDidDrawAndSwap() {}
+
+void GpuRootCompositorFrameSink::DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) {
+ if (client_)
+ client_->DidReceiveCompositorFrameAck(resources);
+}
+
+void GpuRootCompositorFrameSink::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ hit_test_aggregator_.Swap();
+ if (client_)
+ client_->OnBeginFrame(args);
+}
+
+void GpuRootCompositorFrameSink::OnBeginFramePausedChanged(bool paused) {
+ if (client_)
+ client_->OnBeginFramePausedChanged(paused);
+}
+
+void GpuRootCompositorFrameSink::ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) {
+ if (client_)
+ client_->ReclaimResources(resources);
+}
+
+void GpuRootCompositorFrameSink::WillDrawSurface(
+ const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) {}
+
+void GpuRootCompositorFrameSink::OnClientConnectionLost() {
+ support_->frame_sink_manager()->OnClientConnectionLost(
+ support_->frame_sink_id());
+}
+
+void GpuRootCompositorFrameSink::OnPrivateConnectionLost() {
+ support_->frame_sink_manager()->OnPrivateConnectionLost(
+ support_->frame_sink_id());
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h b/chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h
new file mode 100644
index 00000000000..05ef8d6cec0
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h
@@ -0,0 +1,108 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
+
+#include <memory>
+
+#include "cc/ipc/compositor_frame_sink.mojom.h"
+#include "cc/ipc/frame_sink_manager.mojom.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/display/display_client.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support_client.h"
+#include "components/viz/service/hit_test/hit_test_aggregator.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace cc {
+class BeginFrameSource;
+}
+
+namespace viz {
+class CompositorFrameSinkSupport;
+class Display;
+class FrameSinkManagerImpl;
+
+class GpuRootCompositorFrameSink
+ : public NON_EXPORTED_BASE(CompositorFrameSinkSupportClient),
+ public NON_EXPORTED_BASE(cc::mojom::CompositorFrameSink),
+ public NON_EXPORTED_BASE(cc::mojom::CompositorFrameSinkPrivate),
+ public NON_EXPORTED_BASE(cc::mojom::DisplayPrivate),
+ public NON_EXPORTED_BASE(DisplayClient) {
+ public:
+ GpuRootCompositorFrameSink(
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ std::unique_ptr<Display> display,
+ std::unique_ptr<cc::BeginFrameSource> begin_frame_source,
+ cc::mojom::CompositorFrameSinkAssociatedRequest request,
+ cc::mojom::CompositorFrameSinkPrivateRequest private_request,
+ cc::mojom::CompositorFrameSinkClientPtr client,
+ cc::mojom::DisplayPrivateAssociatedRequest display_private_request);
+
+ ~GpuRootCompositorFrameSink() override;
+
+ // cc::mojom::DisplayPrivate:
+ void SetDisplayVisible(bool visible) override;
+ void ResizeDisplay(const gfx::Size& size) override;
+ void SetDisplayColorSpace(const gfx::ColorSpace& color_space) override;
+ void SetOutputIsSecure(bool secure) override;
+ void SetLocalSurfaceId(const LocalSurfaceId& local_surface_id,
+ float scale_factor) override;
+
+ // cc::mojom::CompositorFrameSink:
+ void SetNeedsBeginFrame(bool needs_begin_frame) override;
+ void SubmitCompositorFrame(const LocalSurfaceId& local_surface_id,
+ cc::CompositorFrame frame) override;
+ void DidNotProduceFrame(const cc::BeginFrameAck& begin_frame_ack) override;
+
+ // cc::mojom::CompositorFrameSinkPrivate:
+ void ClaimTemporaryReference(const SurfaceId& surface_id) override;
+ void RequestCopyOfSurface(
+ std::unique_ptr<cc::CopyOutputRequest> request) override;
+
+ private:
+ // DisplayClient:
+ void DisplayOutputSurfaceLost() override;
+ void DisplayWillDrawAndSwap(bool will_draw_and_swap,
+ const cc::RenderPassList& render_passes) override;
+ void DisplayDidDrawAndSwap() override;
+
+ // CompositorFrameSinkSupportClient:
+ void DidReceiveCompositorFrameAck(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ void OnBeginFramePausedChanged(bool paused) override;
+ void ReclaimResources(
+ const std::vector<cc::ReturnedResource>& resources) override;
+ void WillDrawSurface(const LocalSurfaceId& local_surface_id,
+ const gfx::Rect& damage_rect) override;
+
+ void OnClientConnectionLost();
+ void OnPrivateConnectionLost();
+
+ std::unique_ptr<CompositorFrameSinkSupport> support_;
+
+ // GpuRootCompositorFrameSink holds a Display and its BeginFrameSource if
+ // it was created with a non-null gpu::SurfaceHandle.
+ std::unique_ptr<cc::BeginFrameSource> display_begin_frame_source_;
+ std::unique_ptr<Display> display_;
+
+ cc::mojom::CompositorFrameSinkClientPtr client_;
+ mojo::AssociatedBinding<cc::mojom::CompositorFrameSink>
+ compositor_frame_sink_binding_;
+ mojo::Binding<cc::mojom::CompositorFrameSinkPrivate>
+ compositor_frame_sink_private_binding_;
+ mojo::AssociatedBinding<cc::mojom::DisplayPrivate> display_private_binding_;
+
+ HitTestAggregator hit_test_aggregator_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuRootCompositorFrameSink);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_GPU_ROOT_COMPOSITOR_FRAME_SINK_H_
diff --git a/chromium/components/viz/service/frame_sinks/primary_begin_frame_source.cc b/chromium/components/viz/service/frame_sinks/primary_begin_frame_source.cc
new file mode 100644
index 00000000000..58ec08404e0
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/primary_begin_frame_source.cc
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/primary_begin_frame_source.h"
+
+namespace viz {
+
+PrimaryBeginFrameSource::PrimaryBeginFrameSource()
+ : begin_frame_source_(this) {}
+
+PrimaryBeginFrameSource::~PrimaryBeginFrameSource() = default;
+
+void PrimaryBeginFrameSource::OnBeginFrameSourceAdded(
+ cc::BeginFrameSource* begin_frame_source) {
+ sources_.insert(begin_frame_source);
+
+ if (current_begin_frame_source_)
+ return;
+
+ current_begin_frame_source_ = begin_frame_source;
+ if (needs_begin_frames_ && current_begin_frame_source_)
+ current_begin_frame_source_->AddObserver(this);
+}
+
+void PrimaryBeginFrameSource::OnBeginFrameSourceRemoved(
+ cc::BeginFrameSource* begin_frame_source) {
+ sources_.erase(begin_frame_source);
+ if (current_begin_frame_source_ != begin_frame_source)
+ return;
+
+ if (needs_begin_frames_)
+ current_begin_frame_source_->RemoveObserver(this);
+
+ if (!sources_.empty())
+ current_begin_frame_source_ = *sources_.begin();
+ else
+ current_begin_frame_source_ = nullptr;
+
+ if (needs_begin_frames_ && current_begin_frame_source_)
+ current_begin_frame_source_->AddObserver(this);
+}
+
+void PrimaryBeginFrameSource::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ begin_frame_source_.OnBeginFrame(args);
+ last_begin_frame_args_ = args;
+}
+
+const cc::BeginFrameArgs& PrimaryBeginFrameSource::LastUsedBeginFrameArgs()
+ const {
+ return last_begin_frame_args_;
+}
+
+void PrimaryBeginFrameSource::OnBeginFrameSourcePausedChanged(bool paused) {}
+
+void PrimaryBeginFrameSource::DidFinishFrame(cc::BeginFrameObserver* obs) {
+ begin_frame_source_.DidFinishFrame(obs);
+}
+
+void PrimaryBeginFrameSource::AddObserver(cc::BeginFrameObserver* obs) {
+ begin_frame_source_.AddObserver(obs);
+}
+
+void PrimaryBeginFrameSource::RemoveObserver(cc::BeginFrameObserver* obs) {
+ begin_frame_source_.RemoveObserver(obs);
+}
+
+bool PrimaryBeginFrameSource::IsThrottled() const {
+ return current_begin_frame_source_
+ ? current_begin_frame_source_->IsThrottled()
+ : true;
+}
+
+void PrimaryBeginFrameSource::OnNeedsBeginFrames(bool needs_begin_frames) {
+ if (needs_begin_frames_ == needs_begin_frames)
+ return;
+
+ needs_begin_frames_ = needs_begin_frames;
+
+ if (!current_begin_frame_source_)
+ return;
+
+ if (needs_begin_frames_)
+ current_begin_frame_source_->AddObserver(this);
+ else
+ current_begin_frame_source_->RemoveObserver(this);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/primary_begin_frame_source.h b/chromium/components/viz/service/frame_sinks/primary_begin_frame_source.h
new file mode 100644
index 00000000000..51aec3dc8cb
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/primary_begin_frame_source.h
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_PRIMARY_BEGIN_FRAME_SOURCE_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_PRIMARY_BEGIN_FRAME_SOURCE_H_
+
+#include "base/containers/flat_set.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// PrimaryBeginFrameSource echos the first BeginFrameSource in the system.
+// If the first source goes away then it will echo the new first
+// BeginFrameSource.
+class VIZ_SERVICE_EXPORT PrimaryBeginFrameSource
+ : public cc::BeginFrameSource,
+ public cc::BeginFrameObserver,
+ public cc::ExternalBeginFrameSourceClient {
+ public:
+ PrimaryBeginFrameSource();
+ ~PrimaryBeginFrameSource() override;
+
+ void OnBeginFrameSourceAdded(cc::BeginFrameSource* begin_frame_source);
+ void OnBeginFrameSourceRemoved(cc::BeginFrameSource* begin_frame_source);
+
+ // cc::BeginFrameObserver implementation.
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
+
+ // cc::BeginFrameSource implementation.
+ void DidFinishFrame(cc::BeginFrameObserver* obs) override;
+ void AddObserver(cc::BeginFrameObserver* obs) override;
+ void RemoveObserver(cc::BeginFrameObserver* obs) override;
+ bool IsThrottled() const override;
+
+ // cc::ExternalBeginFrameSourceClient implementation.
+ void OnNeedsBeginFrames(bool needs_begin_frames) override;
+
+ private:
+ cc::ExternalBeginFrameSource begin_frame_source_;
+ cc::BeginFrameSource* current_begin_frame_source_ = nullptr;
+
+ // The last begin frame args generated by the begin frame source.
+ cc::BeginFrameArgs last_begin_frame_args_;
+
+ bool needs_begin_frames_ = false;
+
+ base::flat_set<cc::BeginFrameSource*> sources_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimaryBeginFrameSource);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_PRIMARY_BEGIN_FRAME_SOURCE_H_
diff --git a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.cc b/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.cc
new file mode 100644
index 00000000000..6464e324d1c
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.cc
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/referenced_surface_tracker.h"
+
+#include "base/logging.h"
+
+namespace viz {
+
+void GetSurfaceReferenceDifference(
+ const SurfaceId& parent_surface_id,
+ const base::flat_set<SurfaceId>& old_referenced_surfaces,
+ const base::flat_set<SurfaceId>& new_referenced_surfaces,
+ std::vector<cc::SurfaceReference>* references_to_add,
+ std::vector<cc::SurfaceReference>* references_to_remove) {
+ DCHECK(parent_surface_id.is_valid());
+
+ // Find SurfaceIds in |old_referenced_surfaces| that aren't referenced
+ // anymore.
+ for (const SurfaceId& surface_id : old_referenced_surfaces) {
+ if (new_referenced_surfaces.count(surface_id) == 0) {
+ references_to_remove->push_back(
+ cc::SurfaceReference(parent_surface_id, surface_id));
+ }
+ }
+
+ // Find SurfaceIds in |new_referenced_surfaces| that aren't already
+ // referenced.
+ for (const SurfaceId& surface_id : new_referenced_surfaces) {
+ if (old_referenced_surfaces.count(surface_id) == 0) {
+ references_to_add->push_back(
+ cc::SurfaceReference(parent_surface_id, surface_id));
+ }
+ }
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.h b/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.h
new file mode 100644
index 00000000000..4bed2beb346
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.h
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_REFERENCED_SURFACE_TRACKER_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_REFERENCED_SURFACE_TRACKER_H_
+
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "cc/surfaces/surface_reference.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// Finds the difference between |old_referenced_surfaces| and
+// |new_referenced_surfaces|. Populates |references_to_add| and
+// |references_to_remove| based on the difference using |parent_surface_id| as
+// the parent for references.
+void VIZ_SERVICE_EXPORT GetSurfaceReferenceDifference(
+ const SurfaceId& parent_surface_id,
+ const base::flat_set<SurfaceId>& old_referenced_surfaces,
+ const base::flat_set<SurfaceId>& new_referenced_surfaces,
+ std::vector<cc::SurfaceReference>* references_to_add,
+ std::vector<cc::SurfaceReference>* references_to_remove);
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_REFERENCED_SURFACE_TRACKER_H_
diff --git a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker_unittest.cc b/chromium/components/viz/service/frame_sinks/referenced_surface_tracker_unittest.cc
new file mode 100644
index 00000000000..7ff2d17d26a
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/referenced_surface_tracker_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/referenced_surface_tracker.h"
+
+#include <memory>
+
+#include "base/containers/flat_set.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "cc/surfaces/surface_reference.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::UnorderedElementsAre;
+using testing::IsEmpty;
+
+namespace viz {
+namespace test {
+namespace {
+
+constexpr FrameSinkId kParentFrameSink(2, 1);
+constexpr FrameSinkId kChildFrameSink1(65563, 1);
+constexpr FrameSinkId kChildFrameSink2(65564, 1);
+
+base::flat_set<SurfaceId> MakeReferenceSet(
+ std::initializer_list<SurfaceId> surface_ids) {
+ return base::flat_set<SurfaceId>(surface_ids, base::KEEP_FIRST_OF_DUPES);
+}
+
+SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) {
+ return SurfaceId(
+ frame_sink_id,
+ LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u)));
+}
+
+} // namespace
+
+class ReferencedSurfaceTrackerTest : public testing::Test {
+ public:
+ ReferencedSurfaceTrackerTest() {}
+ ~ReferencedSurfaceTrackerTest() override {}
+
+ const std::vector<cc::SurfaceReference>& references_to_remove() const {
+ return references_to_remove_;
+ }
+
+ const std::vector<cc::SurfaceReference>& references_to_add() const {
+ return references_to_add_;
+ }
+
+ void UpdateReferences(
+ const SurfaceId& surface_id,
+ const base::flat_set<SurfaceId>& old_referenced_surfaces,
+ const base::flat_set<SurfaceId>& new_referenced_surfaces) {
+ references_to_add_.clear();
+ references_to_remove_.clear();
+ GetSurfaceReferenceDifference(surface_id, old_referenced_surfaces,
+ new_referenced_surfaces, &references_to_add_,
+ &references_to_remove_);
+ }
+
+ private:
+ std::vector<cc::SurfaceReference> references_to_add_;
+ std::vector<cc::SurfaceReference> references_to_remove_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReferencedSurfaceTrackerTest);
+};
+
+TEST_F(ReferencedSurfaceTrackerTest, AddSurfaceReference) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const cc::SurfaceReference reference(parent_id, child_id1);
+
+ // Check that reference to |child_id1| is added.
+ UpdateReferences(parent_id, MakeReferenceSet({}),
+ MakeReferenceSet({child_id1}));
+ EXPECT_THAT(references_to_add(), UnorderedElementsAre(reference));
+ EXPECT_THAT(references_to_remove(), IsEmpty());
+}
+
+TEST_F(ReferencedSurfaceTrackerTest, NoChangeToReferences) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const cc::SurfaceReference reference(parent_id, child_id1);
+
+ // Check that no references are added or removed.
+ auto referenced_surfaces = MakeReferenceSet({child_id1});
+ UpdateReferences(parent_id, referenced_surfaces, referenced_surfaces);
+ EXPECT_THAT(references_to_remove(), IsEmpty());
+ EXPECT_THAT(references_to_add(), IsEmpty());
+}
+
+TEST_F(ReferencedSurfaceTrackerTest, RemoveSurfaceReference) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const cc::SurfaceReference reference(parent_id, child_id1);
+
+ // Check that reference to |child_id1| is removed.
+ UpdateReferences(parent_id, MakeReferenceSet({child_id1}),
+ MakeReferenceSet({}));
+ EXPECT_THAT(references_to_add(), IsEmpty());
+ EXPECT_THAT(references_to_remove(), UnorderedElementsAre(reference));
+}
+
+TEST_F(ReferencedSurfaceTrackerTest, RemoveOneOfTwoSurfaceReferences) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1_first = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id1_second = MakeSurfaceId(kChildFrameSink1, 2);
+ const cc::SurfaceReference reference_first(parent_id, child_id1_first);
+ const cc::SurfaceReference reference_second(parent_id, child_id1_second);
+
+ // Check that reference to |child_id1_first| is removed and reference to
+ // |child_id1_second| is added.
+ UpdateReferences(parent_id, MakeReferenceSet({child_id1_first}),
+ MakeReferenceSet({child_id1_second}));
+ EXPECT_THAT(references_to_remove(), UnorderedElementsAre(reference_first));
+ EXPECT_THAT(references_to_add(), UnorderedElementsAre(reference_second));
+}
+
+TEST_F(ReferencedSurfaceTrackerTest, AddTwoThenRemoveOneSurfaceReferences) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 2);
+ const cc::SurfaceReference reference1(parent_id, child_id1);
+ const cc::SurfaceReference reference2(parent_id, child_id2);
+
+ // Check that first frame adds both surface references.
+ const auto initial_referenced = MakeReferenceSet({child_id1, child_id2});
+ UpdateReferences(parent_id, MakeReferenceSet({}), initial_referenced);
+ EXPECT_THAT(references_to_remove(), IsEmpty());
+ EXPECT_THAT(references_to_add(),
+ UnorderedElementsAre(reference1, reference2));
+
+ // Check that reference to |child_id1| is removed but not to |child_id2|.
+ UpdateReferences(parent_id, initial_referenced,
+ MakeReferenceSet({child_id2}));
+ EXPECT_THAT(references_to_remove(), UnorderedElementsAre(reference1));
+ EXPECT_THAT(references_to_add(), IsEmpty());
+}
+
+} // namespace test
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc b/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
new file mode 100644
index 00000000000..d0269dcc90d
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
@@ -0,0 +1,527 @@
+// 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 <stddef.h>
+
+#include <unordered_map>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/memory/ptr_util.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+
+namespace viz {
+namespace test {
+namespace {
+
+constexpr FrameSinkId kFrameSink1(1, 0);
+constexpr FrameSinkId kFrameSink2(2, 0);
+constexpr FrameSinkId kFrameSink3(3, 0);
+
+} // namespace
+
+// Tests for reference tracking in CompositorFrameSinkSupport and
+// cc::SurfaceManager.
+class SurfaceReferencesTest : public testing::Test {
+ public:
+ cc::SurfaceManager& GetSurfaceManager() {
+ return *manager_->surface_manager();
+ }
+
+ // Creates a new Surface with the provided |frame_sink_id| and |local_id|.
+ // Will first create a Surfacesupport for |frame_sink_id| if necessary.
+ SurfaceId CreateSurface(const FrameSinkId& frame_sink_id, uint32_t local_id) {
+ LocalSurfaceId local_surface_id(local_id,
+ base::UnguessableToken::Deserialize(0, 1u));
+ GetCompositorFrameSinkSupport(frame_sink_id)
+ .SubmitCompositorFrame(local_surface_id,
+ cc::test::MakeCompositorFrame());
+ return SurfaceId(frame_sink_id, local_surface_id);
+ }
+
+ // Destroy Surface with |surface_id|.
+ void DestroySurface(const SurfaceId& surface_id) {
+ GetCompositorFrameSinkSupport(surface_id.frame_sink_id())
+ .EvictCurrentSurface();
+ }
+
+ CompositorFrameSinkSupport& GetCompositorFrameSinkSupport(
+ const FrameSinkId& frame_sink_id) {
+ auto& support_ptr = supports_[frame_sink_id];
+ if (!support_ptr) {
+ constexpr bool is_root = false;
+ constexpr bool handles_frame_sink_id_invalidation = true;
+ constexpr bool needs_sync_points = true;
+ support_ptr = CompositorFrameSinkSupport::Create(
+ nullptr, manager_.get(), frame_sink_id, is_root,
+ handles_frame_sink_id_invalidation, needs_sync_points);
+ }
+ return *support_ptr;
+ }
+
+ void DestroyCompositorFrameSinkSupport(const FrameSinkId& frame_sink_id) {
+ auto support_ptr = supports_.find(frame_sink_id);
+ ASSERT_NE(support_ptr, supports_.end());
+ supports_.erase(support_ptr);
+ }
+
+ void RemoveSurfaceReference(const SurfaceId& parent_id,
+ const SurfaceId& child_id) {
+ manager_->surface_manager()->RemoveSurfaceReferences(
+ {cc::SurfaceReference(parent_id, child_id)});
+ }
+
+ void AddSurfaceReference(const SurfaceId& parent_id,
+ const SurfaceId& child_id) {
+ manager_->surface_manager()->AddSurfaceReferences(
+ {cc::SurfaceReference(parent_id, child_id)});
+ }
+
+ // Returns all the references where |surface_id| is the parent.
+ const base::flat_set<SurfaceId>& GetReferencesFrom(
+ const SurfaceId& surface_id) {
+ return GetSurfaceManager().GetSurfacesReferencedByParent(surface_id);
+ }
+
+ // Returns all the references where |surface_id| is the child.
+ const base::flat_set<SurfaceId>& GetReferencesFor(
+ const SurfaceId& surface_id) {
+ return GetSurfaceManager().GetSurfacesThatReferenceChild(surface_id);
+ }
+
+ // Temporary references are stored as a map in cc::SurfaceManager. This method
+ // converts the map to a vector.
+ std::vector<SurfaceId> GetAllTempReferences() {
+ std::vector<SurfaceId> temp_references;
+ for (auto& map_entry : GetSurfaceManager().temporary_references_)
+ temp_references.push_back(map_entry.first);
+ return temp_references;
+ }
+
+ protected:
+ // testing::Test:
+ void SetUp() override {
+ // Start each test with a fresh cc::SurfaceManager instance.
+ manager_ = base::MakeUnique<FrameSinkManagerImpl>(
+ nullptr /* display_provider */,
+ cc::SurfaceManager::LifetimeType::REFERENCES);
+ }
+ void TearDown() override {
+ for (auto& support : supports_)
+ support.second->EvictCurrentSurface();
+ supports_.clear();
+ manager_.reset();
+ }
+
+ std::unordered_map<FrameSinkId,
+ std::unique_ptr<CompositorFrameSinkSupport>,
+ FrameSinkIdHash>
+ supports_;
+ std::unique_ptr<FrameSinkManagerImpl> manager_;
+};
+
+TEST_F(SurfaceReferencesTest, AddReference) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+
+ EXPECT_THAT(GetReferencesFor(id1),
+ UnorderedElementsAre(GetSurfaceManager().GetRootSurfaceId()));
+ EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
+}
+
+TEST_F(SurfaceReferencesTest, AddRemoveReference) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+ AddSurfaceReference(id1, id2);
+
+ EXPECT_THAT(GetReferencesFor(id1),
+ UnorderedElementsAre(GetSurfaceManager().GetRootSurfaceId()));
+ EXPECT_THAT(GetReferencesFor(id2), UnorderedElementsAre(id1));
+ EXPECT_THAT(GetReferencesFrom(id1), UnorderedElementsAre(id2));
+ EXPECT_THAT(GetReferencesFrom(id2), IsEmpty());
+
+ RemoveSurfaceReference(id1, id2);
+ EXPECT_THAT(GetReferencesFor(id1), SizeIs(1));
+ EXPECT_THAT(GetReferencesFor(id2), IsEmpty());
+ EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
+ EXPECT_THAT(GetReferencesFrom(id2), IsEmpty());
+}
+
+TEST_F(SurfaceReferencesTest, NewSurfaceFromFrameSink) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+ SurfaceId id3 = CreateSurface(kFrameSink3, 1);
+
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+ AddSurfaceReference(id1, id2);
+ AddSurfaceReference(id2, id3);
+
+ // |kFramesink2| received a CompositorFrame with a new size, so it destroys
+ // |id2| and creates |id2_next|. No reference have been removed yet.
+ SurfaceId id2_next = CreateSurface(kFrameSink2, 2);
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2_next));
+
+ // Add references to and from |id2_next|.
+ AddSurfaceReference(id1, id2_next);
+ AddSurfaceReference(id2_next, id3);
+ EXPECT_THAT(GetReferencesFor(id2), UnorderedElementsAre(id1));
+ EXPECT_THAT(GetReferencesFor(id2_next), UnorderedElementsAre(id1));
+ EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2, id2_next));
+
+ RemoveSurfaceReference(id1, id2);
+ EXPECT_THAT(GetReferencesFor(id2), IsEmpty());
+ EXPECT_THAT(GetReferencesFor(id2_next), UnorderedElementsAre(id1));
+ EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2_next));
+
+ // |id2| should be deleted during GC but other surfaces shouldn't.
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2_next));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
+}
+
+TEST_F(SurfaceReferencesTest, ReferenceCycleGetsDeleted) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+ SurfaceId id3 = CreateSurface(kFrameSink3, 1);
+
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+ AddSurfaceReference(id1, id2);
+ AddSurfaceReference(id2, id3);
+
+ // This reference forms a cycle between id2 and id3.
+ AddSurfaceReference(id3, id2);
+
+ DestroySurface(id3);
+ DestroySurface(id2);
+ DestroySurface(id1);
+
+ RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+
+ // Removing the reference from the root to id1 should allow all three surfaces
+ // to be deleted during GC even with a cycle between 2 and 3.
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
+}
+
+TEST_F(SurfaceReferencesTest, SurfacesAreDeletedDuringGarbageCollection) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+ AddSurfaceReference(id1, id2);
+
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+
+ // Destroying the surfaces shouldn't delete them yet, since there is still an
+ // active reference on all surfaces.
+ DestroySurface(id1);
+ DestroySurface(id2);
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+
+ // Should delete |id2| when the only reference to it is removed.
+ RemoveSurfaceReference(id1, id2);
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+
+ // Should delete |id1| when the only reference to it is removed.
+ RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+}
+
+TEST_F(SurfaceReferencesTest, GarbageCollectionWorksRecusively) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+ SurfaceId id3 = CreateSurface(kFrameSink3, 1);
+
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+ AddSurfaceReference(id1, id2);
+ AddSurfaceReference(id2, id3);
+
+ DestroySurface(id3);
+ DestroySurface(id2);
+ DestroySurface(id1);
+
+ // Destroying the surfaces shouldn't delete them yet, since there is still an
+ // active reference on all surfaces.
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+
+ RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+
+ // Removing the reference from the root to id1 should allow all three surfaces
+ // to be deleted during GC.
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
+}
+
+TEST_F(SurfaceReferencesTest, TryAddReferenceSameReferenceTwice) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+ AddSurfaceReference(id1, id2);
+ EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
+ EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
+
+ // The second request should be ignored without crashing.
+ AddSurfaceReference(id1, id2);
+ EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
+ EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
+}
+
+TEST_F(SurfaceReferencesTest, AddingSelfReferenceFails) {
+ SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+
+ // A temporary reference must exist to |id1|.
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(id1));
+ EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
+ EXPECT_THAT(GetReferencesFor(id1), IsEmpty());
+
+ // Try to add a self reference. This should fail.
+ AddSurfaceReference(id1, id1);
+
+ // Adding a self reference should be ignored without crashing. The temporary
+ // reference to |id1| must still exist.
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(id1));
+ EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
+ EXPECT_THAT(GetReferencesFor(id1), IsEmpty());
+}
+
+TEST_F(SurfaceReferencesTest, RemovingNonexistantReferenceFails) {
+ SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+
+ // Removing non-existent reference should be ignored.
+ AddSurfaceReference(id1, id2);
+ RemoveSurfaceReference(id2, id1);
+ EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
+ EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
+}
+
+TEST_F(SurfaceReferencesTest, AddSurfaceThenReference) {
+ // Create a new surface.
+ const SurfaceId surface_id = CreateSurface(kFrameSink2, 1);
+
+ // A temporary reference must be added to |surface_id|.
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));
+
+ // Create |parent_id| and add a real reference from it to |surface_id|.
+ const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(parent_id, surface_id);
+
+ // The temporary reference to |surface_id| should be gone.
+ // The only temporary reference should be to |parent_id|.
+ // There must be a real reference from |parent_id| to |child_id|.
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
+ EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id));
+}
+
+TEST_F(SurfaceReferencesTest, AddSurfaceThenRootReference) {
+ // Create a new surface.
+ const SurfaceId surface_id = CreateSurface(kFrameSink1, 1);
+
+ // Temporary reference should be added to |surface_id|.
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));
+
+ // Add a real reference from root to |surface_id|.
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), surface_id);
+
+ // The temporary reference should be gone and there should now be a surface
+ // reference from root to |surface_id|.
+ EXPECT_TRUE(GetAllTempReferences().empty());
+ EXPECT_THAT(GetReferencesFrom(GetSurfaceManager().GetRootSurfaceId()),
+ ElementsAre(surface_id));
+}
+
+TEST_F(SurfaceReferencesTest, AddTwoSurfacesThenOneReference) {
+ // Create two surfaces with different FrameSinkIds.
+ const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
+ const SurfaceId surface_id2 = CreateSurface(kFrameSink3, 1);
+
+ // Temporary reference should be added for both surfaces.
+ EXPECT_THAT(GetAllTempReferences(),
+ UnorderedElementsAre(surface_id1, surface_id2));
+
+ // Create |parent_id| and add a real reference from it to |surface_id1|.
+ const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(parent_id, surface_id1);
+
+ // Real reference must be added to |surface_id1| and the temporary reference
+ // to it must be gone.
+ // There should still be a temporary reference left to |surface_id2|.
+ // A temporary reference to |parent_id| must be created.
+ EXPECT_THAT(GetAllTempReferences(),
+ UnorderedElementsAre(parent_id, surface_id2));
+ EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
+}
+
+TEST_F(SurfaceReferencesTest, AddSurfacesSkipReference) {
+ // Add two surfaces that have the same FrameSinkId. This would happen
+ // when a client submits two CompositorFrames before parent submits a new
+ // CompositorFrame.
+ const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 2);
+ const SurfaceId surface_id2 = CreateSurface(kFrameSink2, 1);
+
+ // Temporary references should be added for both surfaces and they should be
+ // stored in the order of creation.
+ EXPECT_THAT(GetAllTempReferences(),
+ UnorderedElementsAre(surface_id1, surface_id2));
+
+ // Create |parent_id| and add a reference from it to |surface_id2| which was
+ // created later.
+ const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(parent_id, surface_id2);
+
+ // The real reference should be added for |surface_id2| and the temporary
+ // references to both |surface_id1| and |surface_id2| should be gone.
+ // There should be a temporary reference to |parent_id|.
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
+ EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id2));
+}
+
+TEST_F(SurfaceReferencesTest, RemoveFirstTempReferenceOnly) {
+ // Add two surfaces that have the same FrameSinkId. This would happen
+ // when a client submits two CFs before parent submits a new CF.
+ const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
+ const SurfaceId surface_id2 = CreateSurface(kFrameSink2, 2);
+
+ // Temporary references should be added for both surfaces and they should be
+ // stored in the order of creation.
+ EXPECT_THAT(GetAllTempReferences(),
+ UnorderedElementsAre(surface_id1, surface_id2));
+
+ // Create |parent_id| and add a reference from it to |surface_id1| which was
+ // created earlier.
+ const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(parent_id, surface_id1);
+
+ // The real reference should be added for |surface_id1| and its temporary
+ // reference should be removed. The temporary reference for |surface_id2|
+ // should remain. A temporary reference must be added for |parent_id|.
+ EXPECT_THAT(GetAllTempReferences(),
+ UnorderedElementsAre(parent_id, surface_id2));
+ EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
+}
+
+TEST_F(SurfaceReferencesTest, SurfaceWithTemporaryReferenceIsNotDeleted) {
+ const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+
+ // We create |id2| and never add a real reference to it. This leaves the
+ // temporary reference.
+ const SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id2));
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+
+ // Destroy both surfaces so they can be garbage collected. We remove the
+ // surface reference to |id1| which will run GarbageCollectSurfaces().
+ DestroySurface(id1);
+ DestroySurface(id2);
+ RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+
+ // |id1| is destroyed and has no references, so it's deleted.
+ EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+
+ // |id2| is destroyed but has a temporary reference, it's not deleted.
+ EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+}
+
+// Checks that when a temporary reference is assigned an owner, if the owner is
+// invalidated then the temporary reference is also removed.
+TEST_F(SurfaceReferencesTest, InvalidateTempReferenceOwnerRemovesReference) {
+ // Surface |id1| should have a temporary reference on creation.
+ const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+ // |id1| should have a temporary reference after an owner is assigned.
+ GetSurfaceManager().AssignTemporaryReference(id1, kFrameSink1);
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+ // When |kFrameSink1| is invalidated the temporary reference will be removed.
+ GetSurfaceManager().InvalidateFrameSinkId(kFrameSink1);
+ ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+}
+
+// Checks that adding a surface reference clears the temporary reference and
+// ownership. Invalidating the old owner shouldn't do anything.
+TEST_F(SurfaceReferencesTest, InvalidateHasNoEffectOnSurfaceReferences) {
+ const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
+
+ const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+ GetSurfaceManager().AssignTemporaryReference(id1, kFrameSink1);
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+ // Adding a real surface reference will remove the temporary reference.
+ AddSurfaceReference(parent_id, id1);
+ ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+ ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
+
+ // When |kFrameSink1| is invalidated it shouldn't change the surface
+ // references.
+ DestroyCompositorFrameSinkSupport(kFrameSink1);
+ ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
+}
+
+TEST_F(SurfaceReferencesTest, CheckDropTemporaryReferenceWorks) {
+ const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+ // An example of why this could happen is the window server doesn't know the
+ // owner, maybe it has crashed and been cleanup already, and asks to drop the
+ // temporary reference.
+ GetSurfaceManager().DropTemporaryReference(id1);
+ ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+}
+
+// Checks that we handle ownership and temporary references correctly when there
+// are multiple temporary references. This tests something like the parent
+// client crashing, so it's
+TEST_F(SurfaceReferencesTest, TempReferencesWithClientCrash) {
+ const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+ AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
+
+ const SurfaceId id1a = CreateSurface(kFrameSink2, 1);
+ const SurfaceId id1b = CreateSurface(kFrameSink2, 2);
+
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
+
+ // Assign |id1a| to |kFrameSink1|. This doesn't change the temporary
+ // reference, it just assigns as owner to it.
+ GetSurfaceManager().AssignTemporaryReference(id1a, kFrameSink1);
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
+
+ // If the parent client crashes then the FrameSink connection will be closed
+ // and the FrameSinkId invalidated. The temporary reference |kFrameSink1|
+ // owns to |id2a| will be removed.
+ DestroyCompositorFrameSinkSupport(kFrameSink1);
+ ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1b));
+
+ // If the parent has crashed then the window server will have already removed
+ // it from the ServerWindow hierarchy and won't have an owner for |id2b|. The
+ // window server will ask to drop the reference instead.
+ GetSurfaceManager().DropTemporaryReference(id1b);
+ ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+}
+
+} // namespace test
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/surface_resource_holder.cc b/chromium/components/viz/service/frame_sinks/surface_resource_holder.cc
new file mode 100644
index 00000000000..63d5d4e0e8a
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/surface_resource_holder.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/frame_sinks/surface_resource_holder.h"
+
+#include "components/viz/service/frame_sinks/surface_resource_holder_client.h"
+
+namespace viz {
+
+SurfaceResourceHolder::SurfaceResourceHolder(
+ SurfaceResourceHolderClient* client)
+ : client_(client) {}
+
+SurfaceResourceHolder::~SurfaceResourceHolder() = default;
+
+SurfaceResourceHolder::ResourceRefs::ResourceRefs()
+ : refs_received_from_child(0), refs_holding_resource_alive(0) {}
+
+void SurfaceResourceHolder::Reset() {
+ resource_id_info_map_.clear();
+}
+
+void SurfaceResourceHolder::ReceiveFromChild(
+ const std::vector<cc::TransferableResource>& resources) {
+ for (const auto& resource : resources) {
+ ResourceRefs& ref = resource_id_info_map_[resource.id];
+ ref.refs_holding_resource_alive++;
+ ref.refs_received_from_child++;
+ }
+}
+
+void SurfaceResourceHolder::RefResources(
+ const std::vector<cc::TransferableResource>& resources) {
+ for (const auto& resource : resources) {
+ ResourceIdInfoMap::iterator count_it =
+ resource_id_info_map_.find(resource.id);
+ DCHECK(count_it != resource_id_info_map_.end());
+ count_it->second.refs_holding_resource_alive++;
+ }
+}
+
+void SurfaceResourceHolder::UnrefResources(
+ const std::vector<cc::ReturnedResource>& resources) {
+ std::vector<cc::ReturnedResource> resources_available_to_return;
+
+ for (const auto& resource : resources) {
+ ResourceIdInfoMap::iterator count_it =
+ resource_id_info_map_.find(resource.id);
+ if (count_it == resource_id_info_map_.end())
+ continue;
+ ResourceRefs& ref = count_it->second;
+ ref.refs_holding_resource_alive -= resource.count;
+ // Keep the newest return sync token that has data.
+ // TODO(jbauman): Handle the case with two valid sync tokens.
+ if (resource.sync_token.HasData())
+ ref.sync_token = resource.sync_token;
+ if (ref.refs_holding_resource_alive == 0) {
+ cc::ReturnedResource returned = resource;
+ returned.sync_token = ref.sync_token;
+ returned.count = ref.refs_received_from_child;
+ resources_available_to_return.push_back(returned);
+ resource_id_info_map_.erase(count_it);
+ }
+ }
+
+ client_->ReturnResources(resources_available_to_return);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/surface_resource_holder.h b/chromium/components/viz/service/frame_sinks/surface_resource_holder.h
new file mode 100644
index 00000000000..33aed9a322e
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/surface_resource_holder.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_SURFACE_RESOURCE_HOLDER_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_SURFACE_RESOURCE_HOLDER_H_
+
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "cc/base/resource_id.h"
+#include "cc/resources/returned_resource.h"
+#include "cc/resources/transferable_resource.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+class SurfaceResourceHolderClient;
+
+// A SurfaceResourceHolder manages the lifetime of resources submitted by a
+// particular SurfaceFactory. Each resource is held by the service until
+// it is no longer referenced by any pending frames or by any
+// resource providers.
+class VIZ_SERVICE_EXPORT SurfaceResourceHolder {
+ public:
+ explicit SurfaceResourceHolder(SurfaceResourceHolderClient* client);
+ ~SurfaceResourceHolder();
+
+ void Reset();
+ void ReceiveFromChild(const std::vector<cc::TransferableResource>& resources);
+ void RefResources(const std::vector<cc::TransferableResource>& resources);
+ void UnrefResources(const std::vector<cc::ReturnedResource>& resources);
+
+ private:
+ SurfaceResourceHolderClient* client_;
+
+ struct ResourceRefs {
+ ResourceRefs();
+
+ int refs_received_from_child;
+ int refs_holding_resource_alive;
+ gpu::SyncToken sync_token;
+ };
+ // Keeps track of the number of users currently in flight for each resource
+ // ID we've received from the client. When this counter hits zero for a
+ // particular resource, that ID is available to return to the client with
+ // the most recently given non-empty sync token.
+ using ResourceIdInfoMap = std::unordered_map<cc::ResourceId, ResourceRefs>;
+ ResourceIdInfoMap resource_id_info_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceResourceHolder);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_SURFACE_RESOURCE_HOLDER_H_
diff --git a/chromium/components/viz/service/frame_sinks/surface_resource_holder_client.h b/chromium/components/viz/service/frame_sinks/surface_resource_holder_client.h
new file mode 100644
index 00000000000..31b56e17d93
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/surface_resource_holder_client.h
@@ -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.
+
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_SURFACE_RESOURCE_HOLDER_CLIENT_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_SURFACE_RESOURCE_HOLDER_CLIENT_H_
+
+#include "cc/resources/returned_resource.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT SurfaceResourceHolderClient {
+ public:
+ virtual ~SurfaceResourceHolderClient() = default;
+
+ // ReturnResources gets called when the display compositor is done using the
+ // resources so that the client can use them.
+ virtual void ReturnResources(
+ const std::vector<cc::ReturnedResource>& resources) = 0;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_SURFACE_RESOURCE_HOLDER_CLIENT_H_
diff --git a/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
new file mode 100644
index 00000000000..245b52fcbbb
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -0,0 +1,1605 @@
+// 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 "base/containers/flat_set.h"
+#include "cc/test/begin_frame_args_test.h"
+#include "cc/test/compositor_frame_helpers.h"
+#include "cc/test/fake_external_begin_frame_source.h"
+#include "cc/test/fake_surface_observer.h"
+#include "cc/test/mock_compositor_frame_sink_support_client.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using cc::test::MockCompositorFrameSinkSupportClient;
+using cc::test::MakeCompositorFrame;
+using testing::_;
+using testing::Eq;
+using testing::IsEmpty;
+using testing::UnorderedElementsAre;
+
+namespace viz {
+namespace test {
+namespace {
+
+constexpr bool kIsRoot = true;
+constexpr bool kIsChildRoot = false;
+constexpr bool kHandlesFrameSinkIdInvalidation = true;
+constexpr bool kNeedsSyncPoints = true;
+constexpr FrameSinkId kDisplayFrameSink(2, 0);
+constexpr FrameSinkId kParentFrameSink(3, 0);
+constexpr FrameSinkId kChildFrameSink1(65563, 0);
+constexpr FrameSinkId kChildFrameSink2(65564, 0);
+constexpr FrameSinkId kArbitraryFrameSink(1337, 7331);
+
+std::vector<SurfaceId> empty_surface_ids() {
+ return std::vector<SurfaceId>();
+}
+
+SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) {
+ return SurfaceId(
+ frame_sink_id,
+ LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u)));
+}
+
+} // namespace
+
+class FakeExternalBeginFrameSourceClient
+ : public cc::FakeExternalBeginFrameSource::Client {
+ public:
+ FakeExternalBeginFrameSourceClient() = default;
+ ~FakeExternalBeginFrameSourceClient() = default;
+
+ bool has_observers() const { return observer_count_ > 0; }
+
+ // cc::FakeExternalBeginFrameSource::Client implementation:
+ void OnAddObserver(cc::BeginFrameObserver* obs) override {
+ ++observer_count_;
+ }
+
+ void OnRemoveObserver(cc::BeginFrameObserver* obs) override {
+ DCHECK_GT(observer_count_, 0);
+ --observer_count_;
+ }
+
+ private:
+ int observer_count_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeExternalBeginFrameSourceClient);
+};
+
+class SurfaceSynchronizationTest : public testing::Test {
+ public:
+ SurfaceSynchronizationTest()
+ : frame_sink_manager_(nullptr /* display_provider */,
+ cc::SurfaceManager::LifetimeType::REFERENCES),
+ surface_observer_(false) {}
+ ~SurfaceSynchronizationTest() override {}
+
+ CompositorFrameSinkSupport& display_support() { return *supports_[0]; }
+ cc::Surface* display_surface() {
+ return display_support().GetCurrentSurfaceForTesting();
+ }
+
+ CompositorFrameSinkSupport& parent_support() { return *supports_[1]; }
+ cc::Surface* parent_surface() {
+ return parent_support().GetCurrentSurfaceForTesting();
+ }
+
+ CompositorFrameSinkSupport& child_support1() { return *supports_[2]; }
+ cc::Surface* child_surface1() {
+ return child_support1().GetCurrentSurfaceForTesting();
+ }
+
+ CompositorFrameSinkSupport& child_support2() { return *supports_[3]; }
+ cc::Surface* child_surface2() {
+ return child_support2().GetCurrentSurfaceForTesting();
+ }
+
+ CompositorFrameSinkSupport& support(int index) { return *supports_[index]; }
+ cc::Surface* surface(int index) {
+ return support(index).GetCurrentSurfaceForTesting();
+ }
+
+ FrameSinkManagerImpl& frame_sink_manager() { return frame_sink_manager_; }
+
+ // Returns all the references where |surface_id| is the parent.
+ const base::flat_set<SurfaceId>& GetChildReferences(
+ const SurfaceId& surface_id) {
+ return frame_sink_manager()
+ .surface_manager()
+ ->GetSurfacesReferencedByParent(surface_id);
+ }
+
+ // Returns true if there is a temporary reference for |surface_id|.
+ bool HasTemporaryReference(const SurfaceId& surface_id) {
+ return frame_sink_manager().surface_manager()->HasTemporaryReference(
+ surface_id);
+ }
+
+ cc::FakeExternalBeginFrameSource* begin_frame_source() {
+ return begin_frame_source_.get();
+ }
+
+ void SendNextBeginFrame() {
+ // Creep the time forward so that any cc::BeginFrameArgs is not equal to the
+ // last one otherwise we violate the BeginFrameSource contract.
+ now_src_->Advance(cc::BeginFrameArgs::DefaultInterval());
+ cc::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, now_src_.get());
+ begin_frame_source_->TestOnBeginFrame(args);
+ }
+
+ cc::FakeSurfaceObserver& surface_observer() { return surface_observer_; }
+
+ // testing::Test:
+ void SetUp() override {
+ testing::Test::SetUp();
+
+ begin_frame_source_ =
+ base::MakeUnique<cc::FakeExternalBeginFrameSource>(0.f, false);
+ begin_frame_source_->SetClient(&begin_frame_source_client_);
+ now_src_ = base::MakeUnique<base::SimpleTestTickClock>();
+ frame_sink_manager_.surface_manager()->AddObserver(&surface_observer_);
+ supports_.push_back(CompositorFrameSinkSupport::Create(
+ &support_client_, &frame_sink_manager_, kDisplayFrameSink, kIsRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
+ supports_.push_back(CompositorFrameSinkSupport::Create(
+ &support_client_, &frame_sink_manager_, kParentFrameSink, kIsChildRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
+ supports_.push_back(CompositorFrameSinkSupport::Create(
+ &support_client_, &frame_sink_manager_, kChildFrameSink1, kIsChildRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
+ supports_.push_back(CompositorFrameSinkSupport::Create(
+ &support_client_, &frame_sink_manager_, kChildFrameSink2, kIsChildRoot,
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
+
+ // Normally, the BeginFrameSource would be registered by the Display. We
+ // register it here so that BeginFrames are received by the display support,
+ // for use in the PassesOnBeginFrameAcks test. Other supports do not receive
+ // BeginFrames, since the frame sink hierarchy is not set up in this test.
+ frame_sink_manager_.RegisterBeginFrameSource(begin_frame_source_.get(),
+ kDisplayFrameSink);
+ }
+
+ void TearDown() override {
+ frame_sink_manager_.surface_manager()->RemoveObserver(&surface_observer_);
+ frame_sink_manager_.UnregisterBeginFrameSource(begin_frame_source_.get());
+
+ begin_frame_source_->SetClient(nullptr);
+ begin_frame_source_.reset();
+
+ supports_.clear();
+
+ surface_observer_.Reset();
+ }
+
+ bool IsMarkedForDestruction(const SurfaceId& surface_id) {
+ return frame_sink_manager_.surface_manager()->IsMarkedForDestruction(
+ surface_id);
+ }
+
+ cc::Surface* GetSurfaceForId(const SurfaceId& surface_id) {
+ return frame_sink_manager_.surface_manager()->GetSurfaceForId(surface_id);
+ }
+
+ protected:
+ testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_;
+
+ private:
+ FrameSinkManagerImpl frame_sink_manager_;
+ cc::FakeSurfaceObserver surface_observer_;
+ FakeExternalBeginFrameSourceClient begin_frame_source_client_;
+ std::unique_ptr<cc::FakeExternalBeginFrameSource> begin_frame_source_;
+ std::unique_ptr<base::SimpleTestTickClock> now_src_;
+ std::vector<std::unique_ptr<CompositorFrameSinkSupport>> supports_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceSynchronizationTest);
+};
+
+// The display root surface should have a surface reference from the top-level
+// root added/removed when a cc::CompositorFrame is submitted with a new
+// SurfaceId.
+TEST_F(SurfaceSynchronizationTest, RootSurfaceReceivesReferences) {
+ const SurfaceId display_id_first = MakeSurfaceId(kDisplayFrameSink, 1);
+ const SurfaceId display_id_second = MakeSurfaceId(kDisplayFrameSink, 2);
+
+ // Submit a cc::CompositorFrame for the first display root surface.
+ display_support().SubmitCompositorFrame(display_id_first.local_surface_id(),
+ MakeCompositorFrame());
+
+ // A surface reference from the top-level root is added and there shouldn't be
+ // a temporary reference.
+ EXPECT_FALSE(HasTemporaryReference(display_id_first));
+ EXPECT_THAT(GetChildReferences(
+ frame_sink_manager().surface_manager()->GetRootSurfaceId()),
+ UnorderedElementsAre(display_id_first));
+
+ // Submit a cc::CompositorFrame for the second display root surface.
+ display_support().SubmitCompositorFrame(display_id_second.local_surface_id(),
+ MakeCompositorFrame());
+
+ // A surface reference from the top-level root to |display_id_second| should
+ // be added and the reference to |display_root_first| removed.
+ EXPECT_FALSE(HasTemporaryReference(display_id_second));
+ EXPECT_THAT(GetChildReferences(
+ frame_sink_manager().surface_manager()->GetRootSurfaceId()),
+ UnorderedElementsAre(display_id_second));
+
+ // cc::Surface |display_id_first| is unreachable and should get deleted.
+ EXPECT_EQ(nullptr, GetSurfaceForId(display_id_first));
+}
+
+// The parent cc::Surface is blocked on |child_id1| and |child_id2|.
+TEST_F(SurfaceSynchronizationTest, BlockedOnTwo) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // parent_support is blocked on |child_id1| and |child_id2|.
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1, child_id2));
+
+ // Submit a cc::CompositorFrame without any dependencies to |child_id1|.
+ // parent_support should now only be blocked on |child_id2|.
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeCompositorFrame());
+
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+
+ // Submit a cc::CompositorFrame without any dependencies to |child_id2|.
+ // parent_support should be activated.
+ child_support2().SubmitCompositorFrame(child_id2.local_surface_id(),
+ MakeCompositorFrame());
+
+ EXPECT_FALSE(child_surface2()->has_deadline());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+}
+
+// The parent cc::Surface is blocked on |child_id2| which is blocked on
+// |child_id3|.
+TEST_F(SurfaceSynchronizationTest, BlockedChain) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // parent_support is blocked on |child_id1|.
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1));
+ // The parent should not report damage until it activates.
+ EXPECT_FALSE(surface_observer().IsSurfaceDamaged(parent_id));
+
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // child_support1 should now be blocked on |child_id2|.
+ EXPECT_TRUE(child_surface1()->has_deadline());
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+ // The parent and child should not report damage until they activate.
+ EXPECT_FALSE(surface_observer().IsSurfaceDamaged(parent_id));
+ EXPECT_FALSE(surface_observer().IsSurfaceDamaged(child_id1));
+
+ // The parent should still be blocked on |child_id1| because it's pending.
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1));
+
+ // Submit a cc::CompositorFrame without any dependencies to |child_id2|.
+ // parent_support should be activated.
+ child_support2().SubmitCompositorFrame(
+ child_id2.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_FALSE(child_surface2()->has_deadline());
+
+ // child_surface1 should now be active.
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty());
+
+ // parent_surface should now be active.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+
+ // All three surfaces |parent_id|, |child_id1|, and |child_id2| should
+ // now report damage. This would trigger a new display frame.
+ EXPECT_TRUE(surface_observer().IsSurfaceDamaged(parent_id));
+ EXPECT_TRUE(surface_observer().IsSurfaceDamaged(child_id1));
+ EXPECT_TRUE(surface_observer().IsSurfaceDamaged(child_id2));
+}
+
+// parent_surface and child_surface1 are blocked on |child_id2|.
+TEST_F(SurfaceSynchronizationTest, TwoBlockedOnOne) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // parent_support is blocked on |child_id2|.
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+
+ // child_support1 should now be blocked on |child_id2|.
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_TRUE(child_surface1()->has_deadline());
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+
+ // The parent should still be blocked on |child_id2|.
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+
+ // Submit a cc::CompositorFrame without any dependencies to |child_id2|.
+ // parent_support should be activated.
+ child_support2().SubmitCompositorFrame(child_id2.local_surface_id(),
+ MakeCompositorFrame());
+
+ EXPECT_FALSE(child_surface2()->has_deadline());
+
+ // child_surface1 should now be active.
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty());
+
+ // parent_surface should now be active.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+}
+
+// parent_surface is blocked on |child_id1|, and child_surface2 is blocked on
+// |child_id2| until the deadline hits.
+TEST_F(SurfaceSynchronizationTest, DeadlineHits) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // parent_support is blocked on |child_id1|.
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1));
+
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // child_support1 should now be blocked on |child_id2|.
+ EXPECT_TRUE(child_surface1()->has_deadline());
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+
+ // The parent should still be blocked on |child_id1| because it's pending.
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1));
+
+ for (int i = 0; i < 3; ++i) {
+ SendNextBeginFrame();
+ // There is still a looming deadline! Eeek!
+ EXPECT_TRUE(parent_surface()->has_deadline());
+
+ // parent_support is still blocked on |child_id1|.
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1));
+
+ // child_support1 is still blocked on |child_id2|.
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+ }
+
+ SendNextBeginFrame();
+
+ // The deadline has passed.
+ EXPECT_FALSE(parent_surface()->has_deadline());
+
+ // parent_surface has been activated.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+
+ // child_surface1 has been activated.
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty());
+}
+
+// This test verifies at the cc::Surface activates once a cc::CompositorFrame is
+// submitted that has no unresolved dependencies.
+TEST_F(SurfaceSynchronizationTest, NewFrameOverridesOldDependencies) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1);
+
+ // Submit a cc::CompositorFrame that depends on |arbitrary_id|.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // Verify that the cc::CompositorFrame is blocked on |arbitrary_id|.
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(arbitrary_id));
+
+ // Submit a cc::CompositorFrame that has no dependencies.
+ parent_support().SubmitCompositorFrame(parent_id.local_surface_id(),
+ MakeCompositorFrame());
+
+ // Verify that the cc::CompositorFrame has been activated.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+}
+
+// This test verifies that a pending cc::CompositorFrame does not affect surface
+// references. A new surface from a child will continue to exist as a temporary
+// reference until the parent's frame activates.
+TEST_F(SurfaceSynchronizationTest, OnlyActiveFramesAffectSurfaceReferences) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ // child_support1 submits a cc::CompositorFrame without any dependencies.
+ // DidReceiveCompositorFrameAck should call on immediate activation.
+ EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(1);
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeCompositorFrame());
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // Verify that the child surface is not blocked.
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty());
+
+ // Verify that there's a temporary reference for |child_id1|.
+ EXPECT_TRUE(HasTemporaryReference(child_id1));
+
+ // parent_support submits a cc::CompositorFrame that depends on |child_id1|
+ // (which is already active) and |child_id2|. Thus, the parent should not
+ // activate immediately. DidReceiveCompositorFrameAck should not be called
+ // immediately because the parent cc::CompositorFrame is also blocked on
+ // |child_id2|.
+ EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0);
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id2}, {child_id1},
+ std::vector<cc::TransferableResource>()));
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+ EXPECT_THAT(GetChildReferences(parent_id), IsEmpty());
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // Verify that there's a temporary reference for |child_id1| that still
+ // exists.
+ EXPECT_TRUE(HasTemporaryReference(child_id1));
+
+ // child_support2 submits a cc::CompositorFrame without any dependencies.
+ // Both the child and the parent should immediately ACK CompositorFrames
+ // on activation.
+ EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2);
+ child_support2().SubmitCompositorFrame(child_id2.local_surface_id(),
+ MakeCompositorFrame());
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // Verify that the child surface is not blocked.
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty());
+
+ // Verify that the parent surface's cc::CompositorFrame has activated and that
+ // the temporary reference has been replaced by a permanent one.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+ EXPECT_FALSE(HasTemporaryReference(child_id1));
+ EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
+}
+
+// This test verifies that we do not double count returned resources when a
+// cc::CompositorFrame starts out as pending, then becomes active, and then is
+// replaced with another active cc::CompositorFrame.
+TEST_F(SurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1);
+
+ // The parent submits a cc::CompositorFrame that depends on |child_id| before
+ // the child submits a cc::CompositorFrame. The cc::CompositorFrame also has
+ // resources in its resource list.
+ cc::TransferableResource resource;
+ resource.id = 1337;
+ resource.format = ALPHA_8;
+ resource.filter = 1234;
+ resource.size = gfx::Size(1234, 5678);
+ std::vector<cc::TransferableResource> resource_list = {resource};
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id}, empty_surface_ids(), resource_list));
+
+ // Verify that the cc::CompositorFrame is blocked on |child_id|.
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id));
+
+ child_support1().SubmitCompositorFrame(
+ child_id.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // Verify that the child cc::CompositorFrame activates immediately.
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty());
+
+ // Verify that the parent has activated.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+
+ std::vector<cc::ReturnedResource> returned_resources = {
+ resource.ToReturnedResource()};
+ EXPECT_CALL(support_client_,
+ DidReceiveCompositorFrameAck(returned_resources));
+
+ // The parent submits a cc::CompositorFrame without any dependencies. That
+ // frame should activate immediately, replacing the earlier frame. The
+ // 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()},
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+}
+
+// The parent cc::Surface is blocked on |child_id2| which is blocked on
+// |child_id3|. child_support1 evicts its blocked cc::Surface. The parent
+// surface should activate.
+TEST_F(SurfaceSynchronizationTest, EvictSurfaceWithPendingFrame) {
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ // Submit a cc::CompositorFrame that depends on |child_id1|.
+ parent_support().SubmitCompositorFrame(
+ parent_id1.local_surface_id(),
+ MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // Verify that the cc::CompositorFrame is blocked on |child_id1|.
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1));
+
+ // Submit a cc::CompositorFrame that depends on |child_id2|.
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // Verify that the cc::CompositorFrame is blocked on |child_id2|.
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+
+ // Evict child_support1's current cc::Surface.
+ // TODO(fsamuel): EvictCurrentSurface => EvictCurrentSurface.
+ child_support1().EvictCurrentSurface();
+
+ // The parent cc::Surface should immediately activate.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+ EXPECT_FALSE(parent_surface()->has_deadline());
+}
+
+// This test verifies that if a surface has both a pending and active
+// cc::CompositorFrame and the pending cc::CompositorFrame activates, replacing
+// the existing active cc::CompositorFrame, then the surface reference hierarchy
+// will be updated allowing garbage collection of surfaces that are no longer
+// referenced.
+TEST_F(SurfaceSynchronizationTest, DropStaleReferencesAfterActivation) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ // The parent submits a cc::CompositorFrame that depends on |child_id1| before
+ // the child submits a cc::CompositorFrame.
+ EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0);
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // Verify that the cc::CompositorFrame is blocked on |child_id|.
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id1));
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // Verify that no references are added while the cc::CompositorFrame is
+ // pending.
+ EXPECT_THAT(GetChildReferences(parent_id), IsEmpty());
+
+ // DidReceiveCompositorFrameAck should get called twice: once for the child
+ // and once for the now active parent cc::CompositorFrame.
+ EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2);
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeCompositorFrame());
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // Verify that the child cc::CompositorFrame activates immediately.
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty());
+
+ // Verify that the parent cc::Surface has activated.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+
+ // Submit a new parent cc::CompositorFrame to add a reference.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), {child_id1},
+ std::vector<cc::TransferableResource>()));
+
+ // Verify that the parent cc::Surface has activated.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+
+ // Verify that there is no temporary reference for the child and that
+ // the reference from the parent to the child still exists.
+ EXPECT_FALSE(HasTemporaryReference(child_id1));
+ EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
+
+ // The parent submits another cc::CompositorFrame that depends on |child_id2|.
+ // Submitting a pending cc::CompositorFrame will not trigger a
+ // CompositorFrameAck.
+ EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0);
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // The parent surface should now have both a pending and activate
+ // cc::CompositorFrame. Verify that the set of child references from
+ // |parent_id| are only from the active cc::CompositorFrame.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(),
+ UnorderedElementsAre(child_id2));
+ EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
+
+ child_support2().SubmitCompositorFrame(child_id2.local_surface_id(),
+ MakeCompositorFrame());
+
+ // Verify that the parent cc::Surface has activated and no longer has a
+ // pending cc::CompositorFrame. Also verify that |child_id1| is no longer a
+ // child reference of |parent_id|.
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty());
+ // The parent will not immediately refer to the child until it submits a new
+ // cc::CompositorFrame with the reference.
+ EXPECT_THAT(GetChildReferences(parent_id), IsEmpty());
+}
+
+// Checks whether the latency info are moved to the new surface from the old
+// one when LocalSurfaceId changes. No frame has unresolved dependencies.
+TEST_F(SurfaceSynchronizationTest,
+ LatencyInfoCarriedOverOnResize_NoUnresolvedDependencies) {
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
+ const ui::LatencyComponentType latency_type1 =
+ ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT;
+ const int64_t latency_id1 = 234;
+ const int64_t latency_sequence_number1 = 5645432;
+ const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT;
+ const int64_t latency_id2 = 31434351;
+ const int64_t latency_sequence_number2 = 663788;
+
+ // Submit a frame with latency info
+ ui::LatencyInfo info;
+ info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1);
+
+ cc::CompositorFrame frame = MakeCompositorFrame();
+ frame.metadata.latency_info.push_back(info);
+
+ parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(),
+ std::move(frame));
+
+ // Verify that the old surface has an active frame and no pending frame.
+ cc::Surface* old_surface = GetSurfaceForId(parent_id1);
+ ASSERT_NE(nullptr, old_surface);
+ EXPECT_TRUE(old_surface->HasActiveFrame());
+ EXPECT_FALSE(old_surface->HasPendingFrame());
+
+ // Submit another frame with some other latency info and a different
+ // LocalSurfaceId.
+ ui::LatencyInfo info2;
+ info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2);
+
+ cc::CompositorFrame frame2 = MakeCompositorFrame();
+ frame2.metadata.latency_info.push_back(info2);
+
+ parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(),
+ std::move(frame2));
+
+ // Verify that the new surface has an active frame and no pending frames.
+ cc::Surface* surface = GetSurfaceForId(parent_id2);
+ ASSERT_NE(nullptr, surface);
+ EXPECT_TRUE(surface->HasActiveFrame());
+ EXPECT_FALSE(surface->HasPendingFrame());
+
+ // Verify that the new surface has both latency info elements.
+ std::vector<ui::LatencyInfo> info_list;
+ surface->TakeLatencyInfo(&info_list);
+ EXPECT_EQ(2u, info_list.size());
+
+ ui::LatencyInfo aggregated_latency_info = info_list[0];
+ aggregated_latency_info.AddNewLatencyFrom(info_list[1]);
+
+ // Two components are the original ones, and the third one is
+ // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame
+ // 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_EQ(latency_sequence_number1, comp1.sequence_number);
+ EXPECT_TRUE(
+ aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(
+ ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr));
+}
+
+// Checks whether the latency info are moved to the new surface from the old
+// one when LocalSurfaceId changes. Old surface has unresolved
+// dependencies.
+TEST_F(SurfaceSynchronizationTest,
+ LatencyInfoCarriedOverOnResize_OldSurfaceHasPendingAndActiveFrame) {
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1);
+
+ const ui::LatencyComponentType latency_type1 =
+ ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT;
+ const int64_t latency_id1 = 234;
+ const int64_t latency_sequence_number1 = 5645432;
+ const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT;
+ const int64_t latency_id2 = 31434351;
+ const int64_t latency_sequence_number2 = 663788;
+
+ // Submit a frame with no unresolved dependecy.
+ ui::LatencyInfo info;
+ info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1);
+
+ cc::CompositorFrame frame = MakeCompositorFrame();
+ frame.metadata.latency_info.push_back(info);
+
+ parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(),
+ std::move(frame));
+
+ // Submit a frame with unresolved dependencies.
+ ui::LatencyInfo info2;
+ info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2);
+
+ cc::CompositorFrame frame2 = MakeCompositorFrame(
+ {child_id}, empty_surface_ids(), std::vector<cc::TransferableResource>());
+ frame2.metadata.latency_info.push_back(info2);
+
+ parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(),
+ std::move(frame2));
+
+ // Verify that the old surface has both an active and a pending frame.
+ cc::Surface* old_surface = GetSurfaceForId(parent_id1);
+ ASSERT_NE(nullptr, old_surface);
+ EXPECT_TRUE(old_surface->HasActiveFrame());
+ EXPECT_TRUE(old_surface->HasPendingFrame());
+
+ // Submit a frame with a new local surface id.
+ parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(),
+ MakeCompositorFrame());
+
+ // Verify that the new surface has an active frame only.
+ cc::Surface* surface = GetSurfaceForId(parent_id2);
+ ASSERT_NE(nullptr, surface);
+ EXPECT_TRUE(surface->HasActiveFrame());
+ EXPECT_FALSE(surface->HasPendingFrame());
+
+ // Verify that the new surface has latency info from both active and pending
+ // frame of the old surface.
+ std::vector<ui::LatencyInfo> info_list;
+ surface->TakeLatencyInfo(&info_list);
+ EXPECT_EQ(2u, info_list.size());
+
+ ui::LatencyInfo aggregated_latency_info = info_list[0];
+ aggregated_latency_info.AddNewLatencyFrom(info_list[1]);
+
+ // Two components are the original ones, and the third one is
+ // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame
+ // 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_EQ(latency_sequence_number1, comp1.sequence_number);
+ EXPECT_TRUE(
+ aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(
+ ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr));
+}
+
+// Checks whether the latency info are moved to the new surface from the old
+// one when LocalSurfaceId changes. The new surface has unresolved
+// dependencies.
+TEST_F(SurfaceSynchronizationTest,
+ LatencyInfoCarriedOverOnResize_NewSurfaceHasPendingFrame) {
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1);
+
+ const ui::LatencyComponentType latency_type1 =
+ ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT;
+ const int64_t latency_id1 = 234;
+ const int64_t latency_sequence_number1 = 5645432;
+ const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT;
+ const int64_t latency_id2 = 31434351;
+ const int64_t latency_sequence_number2 = 663788;
+
+ // Submit a frame with no unresolved dependencies.
+ ui::LatencyInfo info;
+ info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1);
+
+ cc::CompositorFrame frame = MakeCompositorFrame();
+ frame.metadata.latency_info.push_back(info);
+
+ parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(),
+ std::move(frame));
+
+ // Verify that the old surface has an active frame only.
+ cc::Surface* old_surface = GetSurfaceForId(parent_id1);
+ ASSERT_NE(nullptr, old_surface);
+ EXPECT_TRUE(old_surface->HasActiveFrame());
+ EXPECT_FALSE(old_surface->HasPendingFrame());
+
+ // Submit a frame with a new local surface id and with unresolved
+ // dependencies.
+ ui::LatencyInfo info2;
+ info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2);
+
+ cc::CompositorFrame frame2 = MakeCompositorFrame(
+ {child_id}, empty_surface_ids(), std::vector<cc::TransferableResource>());
+ frame2.metadata.latency_info.push_back(info2);
+
+ parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(),
+ std::move(frame2));
+
+ // Verify that the new surface has a pending frame and no active frame.
+ cc::Surface* surface = GetSurfaceForId(parent_id2);
+ ASSERT_NE(nullptr, surface);
+ EXPECT_TRUE(surface->HasPendingFrame());
+ EXPECT_FALSE(surface->HasActiveFrame());
+
+ // Resolve the dependencies. The frame in parent's surface must become active.
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+ MakeCompositorFrame());
+ EXPECT_FALSE(surface->HasPendingFrame());
+ EXPECT_TRUE(surface->HasActiveFrame());
+
+ // Both latency info elements must exist in the now-activated frame of the
+ // new surface.
+ std::vector<ui::LatencyInfo> info_list;
+ surface->TakeLatencyInfo(&info_list);
+ EXPECT_EQ(2u, info_list.size());
+
+ ui::LatencyInfo aggregated_latency_info = info_list[0];
+ aggregated_latency_info.AddNewLatencyFrom(info_list[1]);
+
+ // Two components are the original ones, and the third one is
+ // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame
+ // 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_EQ(latency_sequence_number1, comp1.sequence_number);
+ EXPECT_TRUE(
+ aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(
+ ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr));
+}
+
+// Checks that resources and ack are sent together if possible.
+TEST_F(SurfaceSynchronizationTest, ReturnResourcesWithAck) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ cc::TransferableResource resource;
+ resource.id = 1234;
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ {resource}));
+ std::vector<cc::ReturnedResource> returned_resources =
+ cc::TransferableResource::ReturnResources({resource});
+ EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0);
+ EXPECT_CALL(support_client_,
+ DidReceiveCompositorFrameAck(Eq(returned_resources)));
+ parent_support().SubmitCompositorFrame(parent_id.local_surface_id(),
+ MakeCompositorFrame());
+}
+
+// Verifies that if a surface is marked destroyed and a new frame arrives for
+// it, it will be recovered.
+TEST_F(SurfaceSynchronizationTest, SurfaceResurrection) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3);
+
+ // Create the child surface by submitting a frame to it.
+ EXPECT_EQ(nullptr, GetSurfaceForId(child_id));
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+ MakeCompositorFrame());
+
+ // Verify that the child surface is created.
+ cc::Surface* surface = GetSurfaceForId(child_id);
+ EXPECT_NE(nullptr, surface);
+
+ // Add a reference from the parent to the child.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id}, {child_id},
+ std::vector<cc::TransferableResource>()));
+
+ // Attempt to destroy the child surface. The surface must still exist since
+ // the parent needs it but it will be marked as destroyed.
+ child_support1().EvictCurrentSurface();
+ surface = GetSurfaceForId(child_id);
+ EXPECT_NE(nullptr, surface);
+ EXPECT_TRUE(IsMarkedForDestruction(child_id));
+
+ // Child submits another frame to the same local surface id that is marked
+ // destroyed.
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+ MakeCompositorFrame());
+
+ // Verify that the surface that was marked destroyed is recovered and is being
+ // used again.
+ cc::Surface* surface2 = GetSurfaceForId(child_id);
+ EXPECT_EQ(surface, surface2);
+ EXPECT_FALSE(IsMarkedForDestruction(child_id));
+}
+
+// Verifies that if a LocalSurfaceId belonged to a surface that doesn't
+// exist anymore, it can still be reused for new surfaces.
+TEST_F(SurfaceSynchronizationTest, LocalSurfaceIdIsReusable) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3);
+
+ // Submit the first frame. Creates the surface.
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+ MakeCompositorFrame());
+ EXPECT_NE(nullptr, GetSurfaceForId(child_id));
+
+ // Add a reference from parent.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id}, {child_id},
+ std::vector<cc::TransferableResource>()));
+
+ // Remove the reference from parant. This allows us to destroy the surface.
+ parent_support().SubmitCompositorFrame(parent_id.local_surface_id(),
+ MakeCompositorFrame());
+
+ // Destroy the surface.
+ child_support1().EvictCurrentSurface();
+ EXPECT_EQ(nullptr, GetSurfaceForId(child_id));
+
+ // Submit another frame with the same local surface id. This should work fine
+ // and a new surface must be created.
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+ MakeCompositorFrame());
+ EXPECT_NE(nullptr, GetSurfaceForId(child_id));
+}
+
+// This test verifies that a crash does not occur if garbage collection is
+// triggered during surface dependency resolution. This test triggers garbage
+// collection during surface resolution, by causing an activation to remove
+// a surface subtree from the root. Both the old subtree and the new
+// activated subtree refer to the same dependency. The old subtree was activated
+// by deadline, and the new subtree was activated by a dependency finally
+// resolving.
+TEST_F(SurfaceSynchronizationTest, DependencyTrackingGarbageCollection) {
+ const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1);
+
+ parent_support().SubmitCompositorFrame(
+ parent_id1.local_surface_id(),
+ MakeCompositorFrame({child_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_TRUE(parent_surface()->has_deadline());
+
+ // Advance BeginFrames to trigger a deadline.
+ for (int i = 0; i < 3; ++i) {
+ SendNextBeginFrame();
+ EXPECT_TRUE(display_surface()->has_deadline());
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ }
+ SendNextBeginFrame();
+
+ EXPECT_TRUE(display_surface()->HasActiveFrame());
+ EXPECT_FALSE(display_surface()->HasPendingFrame());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+
+ parent_support().SubmitCompositorFrame(
+ parent_id2.local_surface_id(),
+ MakeCompositorFrame({child_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ // The display surface now has two CompositorFrames. One that is pending,
+ // indirectly blocked on child_id and one that is active, also indirectly
+ // referring to child_id, but activated due to the deadline above.
+ EXPECT_TRUE(display_surface()->HasActiveFrame());
+ EXPECT_TRUE(display_surface()->HasPendingFrame());
+
+ // Submitting a cc::CompositorFrame will trigger garbage collection of the
+ // |parent_id1| subtree. This should not crash.
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+ MakeCompositorFrame());
+}
+
+// This test verifies that a crash does not occur if garbage collection is
+// triggered when a deadline forces frame activation. This test triggers garbage
+// collection during deadline activation by causing the activation of a display
+// frame to replace a previously activated display frame that was referring to
+// a now-unreachable surface subtree. That subtree gets garbage collected during
+// deadline activation. SurfaceDependencyTracker is also tracking a surface
+// from that subtree due to an unresolved dependency. This test verifies that
+// this dependency resolution does not crash.
+TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) {
+ const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1);
+
+ // |parent_id1| is blocked on |child_id|.
+ parent_support().SubmitCompositorFrame(
+ parent_id1.local_surface_id(),
+ MakeCompositorFrame({child_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id1}, {parent_id1},
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_TRUE(display_surface()->has_deadline());
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_TRUE(display_surface()->HasPendingFrame());
+ EXPECT_FALSE(display_surface()->HasActiveFrame());
+
+ // Advance BeginFrames to trigger a deadline. This activates the
+ // cc::CompositorFrame submitted above.
+ for (int i = 0; i < 3; ++i) {
+ SendNextBeginFrame();
+ EXPECT_TRUE(display_surface()->has_deadline());
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ }
+ SendNextBeginFrame();
+ EXPECT_FALSE(display_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->has_deadline());
+ EXPECT_FALSE(display_surface()->HasPendingFrame());
+ EXPECT_TRUE(display_surface()->HasActiveFrame());
+
+ // By submitting a display cc::CompositorFrame, and replacing the parent's
+ // cc::CompositorFrame with another surface ID, parent_id1 becomes unreachable
+ // and a candidate for garbage collection.
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(display_surface()->has_deadline());
+
+ // 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(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(display_surface()->has_deadline());
+ EXPECT_TRUE(parent_surface()->has_deadline());
+
+ // SurfaceDependencyTracker should now be tracking |display_id|, |parent_id1|
+ // and |parent_id2|. By activating the pending |display_id| frame by deadline,
+ // |parent_id1| becomes unreachable and is garbage collected while
+ // SurfaceDependencyTracker is in the process of activating surfaces. This
+ // should not cause a crash or use-after-free.
+ for (int i = 0; i < 3; ++i) {
+ SendNextBeginFrame();
+ EXPECT_TRUE(display_surface()->has_deadline());
+ }
+ SendNextBeginFrame();
+ EXPECT_FALSE(display_surface()->has_deadline());
+}
+
+// This test verifies that a cc::CompositorFrame will only blocked on embedded
+// surfaces but not on other retained surface IDs in the cc::CompositorFrame.
+TEST_F(SurfaceSynchronizationTest, OnlyBlockOnEmbeddedSurfaces) {
+ const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
+
+ // Submitting a cc::CompositorFrame with |parent_id2| so that the display
+ // cc::CompositorFrame can hold a reference to it.
+ parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(),
+ MakeCompositorFrame());
+
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id1}, {parent_id2},
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_TRUE(display_surface()->HasPendingFrame());
+ EXPECT_FALSE(display_surface()->HasActiveFrame());
+ EXPECT_TRUE(display_surface()->has_deadline());
+
+ // Verify that the display cc::CompositorFrame will only block on |parent_id1|
+ // but not |parent_id2|.
+ EXPECT_THAT(display_surface()->activation_dependencies(),
+ UnorderedElementsAre(parent_id1));
+ // Verify that the display surface holds no references while its
+ // cc::CompositorFrame is pending.
+ EXPECT_THAT(GetChildReferences(display_id), IsEmpty());
+
+ // Submitting a cc::CompositorFrame with |parent_id1| should unblock the
+ // display cc::CompositorFrame.
+ parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(),
+ MakeCompositorFrame());
+
+ EXPECT_FALSE(display_surface()->has_deadline());
+ EXPECT_FALSE(display_surface()->HasPendingFrame());
+ EXPECT_TRUE(display_surface()->HasActiveFrame());
+ EXPECT_THAT(display_surface()->activation_dependencies(), IsEmpty());
+}
+
+// This test verifies that a late arriving cc::CompositorFrame activates
+// immediately and does not trigger a new deadline.
+TEST_F(SurfaceSynchronizationTest, LateArrivingDependency) {
+ const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_TRUE(display_surface()->HasPendingFrame());
+ EXPECT_FALSE(display_surface()->HasActiveFrame());
+ EXPECT_TRUE(display_surface()->has_deadline());
+
+ // Advance BeginFrames to trigger a deadline. This activates the
+ // cc::CompositorFrame submitted above.
+ for (int i = 0; i < 3; ++i) {
+ SendNextBeginFrame();
+ EXPECT_TRUE(display_surface()->has_deadline());
+ }
+ SendNextBeginFrame();
+ EXPECT_FALSE(display_surface()->has_deadline());
+ EXPECT_FALSE(display_surface()->HasPendingFrame());
+ EXPECT_TRUE(display_surface()->HasActiveFrame());
+
+ // A late arriving cc::CompositorFrame should activate immediately without
+ // scheduling a deadline and without waiting for dependencies to resolve.
+ parent_support().SubmitCompositorFrame(
+ parent_id1.local_surface_id(),
+ MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_FALSE(parent_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+}
+
+// This test verifies that a late arriving cc::CompositorFrame activates
+// immediately along with its subtree and does not trigger a new deadline.
+TEST_F(SurfaceSynchronizationTest, MultiLevelLateArrivingDependency) {
+ const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1);
+
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(display_surface()->HasPendingFrame());
+ EXPECT_FALSE(display_surface()->HasActiveFrame());
+ EXPECT_TRUE(display_surface()->has_deadline());
+
+ // Issue some BeginFrames to trigger the deadline and activate the display's
+ // surface. |parent_id| is now late. Advance BeginFrames to trigger a
+ // deadline.
+ for (int i = 0; i < 4; ++i) {
+ EXPECT_TRUE(display_surface()->has_deadline());
+ SendNextBeginFrame();
+ }
+ EXPECT_FALSE(display_surface()->HasPendingFrame());
+ EXPECT_TRUE(display_surface()->HasActiveFrame());
+ EXPECT_FALSE(display_surface()->has_deadline());
+
+ // The child surface is not currently causally linked to the display's
+ // surface and so it gets a separate deadline.
+ child_support1().SubmitCompositorFrame(
+ child_id.local_surface_id(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->has_deadline());
+
+ // Submitting a cc::CompositorFrame to the parent surface creates a dependency
+ // chain from the display to the parent to the child, allowing them all to
+ // assume the same deadline. Both the parent and the child are determined to
+ // be late and activate immediately.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->has_deadline());
+
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->has_deadline());
+}
+
+// This test verifies that CompositorFrames submitted to a surface referenced
+// by a parent cc::CompositorFrame as a fallback will be rejected and ACK'ed
+// immediately.
+TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) {
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ // This is the fallback child surface that the parent holds a reference to.
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ // This is the primary child surface that the parent wants to block on.
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
+
+ // child_support1 submits a cc::CompositorFrame without any dependencies.
+ // DidReceiveCompositorFrameAck should call on immediate activation.
+ // However, resources will not be returned because this frame is a candidate
+ // for display.
+ cc::TransferableResource resource;
+ resource.id = 1337;
+ resource.format = ALPHA_8;
+ resource.filter = 1234;
+ resource.size = gfx::Size(1234, 5678);
+ std::vector<cc::ReturnedResource> returned_resources =
+ cc::TransferableResource::ReturnResources({resource});
+
+ EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(
+ Eq(std::vector<cc::ReturnedResource>())));
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ {resource}));
+ EXPECT_FALSE(child_surface1()->has_deadline());
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // The parent is blocked on |child_id2| and references |child_id1|. The
+ // surface corresponding to |child_id1| will not accept new CompositorFrames
+ // while the parent cc::CompositorFrame is blocked.
+ parent_support().SubmitCompositorFrame(
+ parent_id1.local_surface_id(),
+ MakeCompositorFrame({child_id2}, {child_id1},
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+
+ // Resources will be returned immediately because |child_id1|'s surface is
+ // closed.
+ cc::TransferableResource resource2;
+ resource2.id = 1246;
+ resource2.format = ALPHA_8;
+ resource2.filter = 1357;
+ resource2.size = gfx::Size(8765, 4321);
+ std::vector<cc::ReturnedResource> returned_resources2 =
+ cc::TransferableResource::ReturnResources({resource2});
+ EXPECT_CALL(support_client_,
+ DidReceiveCompositorFrameAck(Eq(returned_resources2)));
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ {resource2}));
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+
+ // Advance BeginFrames to trigger a deadline. This activates the
+ // cc::CompositorFrame submitted to the parent.
+ for (int i = 0; i < 3; ++i) {
+ SendNextBeginFrame();
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ }
+ SendNextBeginFrame();
+ EXPECT_FALSE(parent_surface()->has_deadline());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+
+ // Resources will be returned immediately because |child_id1|'s surface is
+ // closed forever.
+ EXPECT_CALL(support_client_,
+ DidReceiveCompositorFrameAck(Eq(returned_resources2)));
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ {resource2}));
+ testing::Mock::VerifyAndClearExpectations(&support_client_);
+}
+
+// This test verifies that two surface subtrees have independent deadlines.
+TEST_F(SurfaceSynchronizationTest, IndependentDeadlines) {
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
+ const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1);
+
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeCompositorFrame());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+
+ child_support2().SubmitCompositorFrame(child_id2.local_surface_id(),
+ MakeCompositorFrame());
+ EXPECT_FALSE(child_surface2()->HasPendingFrame());
+ EXPECT_TRUE(child_surface2()->HasActiveFrame());
+
+ parent_support().SubmitCompositorFrame(
+ parent_id1.local_surface_id(),
+ MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->has_deadline());
+
+ // Submit another cc::CompositorFrame to |child_id1| that blocks on
+ // |arbitrary_id|.
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->has_deadline());
+
+ // Advance to the next BeginFrame. |child_id1|'s pending Frame should activate
+ // after 3 frames.
+ SendNextBeginFrame();
+
+ // Submit another cc::CompositorFrame to |child_id2| that blocks on
+ // |arbitrary_id|.
+ child_support2().SubmitCompositorFrame(
+ child_id2.local_surface_id(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(child_surface2()->HasPendingFrame());
+ EXPECT_TRUE(child_surface2()->HasActiveFrame());
+ EXPECT_TRUE(child_surface2()->has_deadline());
+
+ // If we issue another two BeginFrames both children should remain blocked.
+ for (int i = 0; i < 2; ++i) {
+ SendNextBeginFrame();
+ EXPECT_TRUE(child_surface1()->has_deadline());
+ EXPECT_TRUE(child_surface2()->has_deadline());
+ }
+
+ // Issuing another BeginFrame should activate the frame in |child_id1| but not
+ // |child_id2|. This verifies that |child_id1| and |child_id2| have different
+ // deadlines.
+ SendNextBeginFrame();
+
+ EXPECT_FALSE(child_surface1()->has_deadline());
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+
+ EXPECT_TRUE(child_surface2()->has_deadline());
+ EXPECT_TRUE(child_surface2()->HasPendingFrame());
+ EXPECT_TRUE(child_surface2()->HasActiveFrame());
+
+ // Issuing another BeginFrame should activate the frame in |child_id2|.
+ SendNextBeginFrame();
+
+ EXPECT_FALSE(child_surface2()->has_deadline());
+ EXPECT_FALSE(child_surface2()->HasPendingFrame());
+ EXPECT_TRUE(child_surface2()->HasActiveFrame());
+}
+
+// This test verifies that a child inherits its deadline from its dependent
+// parent (embedder) surface.
+TEST_F(SurfaceSynchronizationTest, DeadlineInheritance) {
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1);
+
+ parent_support().SubmitCompositorFrame(
+ parent_id1.local_surface_id(),
+ MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->has_deadline());
+
+ // Advance to the next BeginFrame. The parent surface will activate in 3
+ // frames.
+ SendNextBeginFrame();
+
+ child_support1().SubmitCompositorFrame(
+ child_id1.local_surface_id(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->has_deadline());
+
+ // If we issue another three BeginFrames then both the parent and the child
+ // should activate, verifying that the child's deadline is inherited from the
+ // parent.
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_TRUE(child_surface1()->has_deadline());
+ SendNextBeginFrame();
+ }
+
+ // Verify that both the parent and child have activated.
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->has_deadline());
+
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->has_deadline());
+}
+
+// This test verifies that all surfaces within a dependency chain will
+// ultimately inherit the same deadline even if the grandchild is available
+// before the child.
+TEST_F(SurfaceSynchronizationTest, MultiLevelDeadlineInheritance) {
+ const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1);
+ const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1);
+
+ display_support().SubmitCompositorFrame(
+ display_id.local_surface_id(),
+ MakeCompositorFrame({parent_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(display_surface()->HasPendingFrame());
+ EXPECT_FALSE(display_surface()->HasActiveFrame());
+ EXPECT_TRUE(display_surface()->has_deadline());
+
+ // Issue a BeginFrame to move closer to the display's deadline.
+ SendNextBeginFrame();
+
+ // The child surface is not currently causally linked to the display's
+ // surface and so it gets a separate deadline.
+ child_support1().SubmitCompositorFrame(
+ child_id.local_surface_id(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(child_surface1()->HasPendingFrame());
+ EXPECT_FALSE(child_surface1()->HasActiveFrame());
+ EXPECT_TRUE(child_surface1()->has_deadline());
+
+ // Submitting a cc::CompositorFrame to the parent frame creates a dependency
+ // chain from the display to the parent to the child, allowing them all to
+ // assume the same deadline.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id}, empty_surface_ids(),
+ std::vector<cc::TransferableResource>()));
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->has_deadline());
+
+ // Advancing the time by three BeginFrames should activate all the surfaces.
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_TRUE(display_surface()->has_deadline());
+ EXPECT_TRUE(parent_surface()->has_deadline());
+ EXPECT_TRUE(child_surface1()->has_deadline());
+ SendNextBeginFrame();
+ }
+
+ // Verify that all the CompositorFrames have activated.
+ EXPECT_FALSE(display_surface()->HasPendingFrame());
+ EXPECT_TRUE(display_surface()->HasActiveFrame());
+ EXPECT_FALSE(display_surface()->has_deadline());
+
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->has_deadline());
+
+ EXPECT_FALSE(child_surface1()->HasPendingFrame());
+ EXPECT_TRUE(child_surface1()->HasActiveFrame());
+ EXPECT_FALSE(child_surface1()->has_deadline());
+}
+
+} // namespace test
+} // namespace viz
diff --git a/chromium/components/viz/service/hit_test/DEPS b/chromium/components/viz/service/hit_test/DEPS
new file mode 100644
index 00000000000..e207341147d
--- /dev/null
+++ b/chromium/components/viz/service/hit_test/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+components/viz/common",
+ "+cc/surfaces/surface_observer.h",
+ "+services/viz/hit_test/public/interfaces",
+]
diff --git a/chromium/components/viz/service/hit_test/OWNERS b/chromium/components/viz/service/hit_test/OWNERS
new file mode 100644
index 00000000000..3940e4c511d
--- /dev/null
+++ b/chromium/components/viz/service/hit_test/OWNERS
@@ -0,0 +1 @@
+rjkroege@chromium.org \ No newline at end of file
diff --git a/chromium/components/viz/service/hit_test/hit_test_aggregator.cc b/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
new file mode 100644
index 00000000000..72845f5de51
--- /dev/null
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
@@ -0,0 +1,213 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/hit_test/hit_test_aggregator.h"
+#include "components/viz/common/hit_test/aggregated_hit_test_region.h"
+
+namespace viz {
+
+namespace {
+// TODO(gklassen): Review and select appropriate sizes based on
+// telemetry / UMA.
+constexpr int kInitialSize = 1024;
+constexpr int kIncrementalSize = 1024;
+constexpr int kMaxRegionsPerSurface = 1024;
+constexpr int kMaxSize = 100 * 1024;
+
+bool ValidateHitTestRegion(const mojom::HitTestRegionPtr& hit_test_region) {
+ if (hit_test_region->flags == mojom::kHitTestChildSurface) {
+ if (!hit_test_region->surface_id.is_valid())
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateHitTestRegionList(
+ const mojom::HitTestRegionListPtr& hit_test_region_list) {
+ if (hit_test_region_list->regions.size() > kMaxRegionsPerSurface)
+ return false;
+ for (auto& region : hit_test_region_list->regions) {
+ if (!ValidateHitTestRegion(region))
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+HitTestAggregator::HitTestAggregator() : weak_ptr_factory_(this) {
+ AllocateHitTestRegionArray();
+}
+
+HitTestAggregator::~HitTestAggregator() = default;
+
+void HitTestAggregator::SubmitHitTestRegionList(
+ mojom::HitTestRegionListPtr hit_test_region_list) {
+ DCHECK(ValidateHitTestRegionList(hit_test_region_list));
+ // TODO(gklassen): Runtime validation that hit_test_region_list is valid.
+ // TODO(gklassen): Inform FrameSink that the hit_test_region_list is invalid.
+ // TODO(gklassen): FrameSink needs to inform the host of a difficult renderer.
+ pending_[hit_test_region_list->surface_id] = std::move(hit_test_region_list);
+}
+
+bool HitTestAggregator::OnSurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) {
+ return false;
+}
+
+void HitTestAggregator::OnSurfaceDiscarded(const SurfaceId& surface_id) {
+ // Update the region count.
+ auto active_search = active_.find(surface_id);
+ if (active_search != active_.end()) {
+ mojom::HitTestRegionList* old_hit_test_data = active_search->second.get();
+ active_region_count_ -= old_hit_test_data->regions.size();
+ }
+ DCHECK_GE(active_region_count_, 0);
+
+ pending_.erase(surface_id);
+ active_.erase(surface_id);
+}
+
+void HitTestAggregator::OnSurfaceWillDraw(const SurfaceId& surface_id) {
+ auto pending_search = pending_.find(surface_id);
+ if (pending_search == pending_.end()) {
+ // Have already activated pending hit_test_region_list objects for this
+ // surface.
+ return;
+ }
+ mojom::HitTestRegionList* hit_test_region_list = pending_search->second.get();
+
+ // Update the region count.
+ auto active_search = active_.find(surface_id);
+ if (active_search != active_.end()) {
+ mojom::HitTestRegionList* old_hit_test_data = active_search->second.get();
+ active_region_count_ -= old_hit_test_data->regions.size();
+ }
+ active_region_count_ += hit_test_region_list->regions.size();
+ DCHECK_GE(active_region_count_, 0);
+
+ active_[surface_id] = std::move(pending_[surface_id]);
+ pending_.erase(surface_id);
+}
+
+void HitTestAggregator::AllocateHitTestRegionArray() {
+ AllocateHitTestRegionArray(kInitialSize);
+ Swap();
+ AllocateHitTestRegionArray(kInitialSize);
+}
+
+void HitTestAggregator::AllocateHitTestRegionArray(int size) {
+ size_t num_bytes = size * sizeof(AggregatedHitTestRegion);
+ write_handle_ = mojo::SharedBufferHandle::Create(num_bytes);
+ write_size_ = size;
+ write_buffer_ = write_handle_->Map(num_bytes);
+
+ AggregatedHitTestRegion* region =
+ (AggregatedHitTestRegion*)write_buffer_.get();
+ region[0].child_count = kEndOfList;
+}
+
+void HitTestAggregator::PostTaskAggregate(SurfaceId display_surface_id) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&HitTestAggregator::Aggregate,
+ weak_ptr_factory_.GetWeakPtr(), display_surface_id));
+}
+
+void HitTestAggregator::Aggregate(const SurfaceId& display_surface_id) {
+ // Check to ensure that enough memory has been allocated.
+ int size = write_size_;
+ int max_size = active_region_count_ + active_.size() + 1;
+ if (max_size > kMaxSize)
+ max_size = kMaxSize;
+
+ if (max_size > size) {
+ size = (1 + max_size / kIncrementalSize) * kIncrementalSize;
+ AllocateHitTestRegionArray(size);
+ }
+
+ AppendRoot(display_surface_id);
+}
+
+void HitTestAggregator::AppendRoot(const SurfaceId& surface_id) {
+ auto search = active_.find(surface_id);
+ if (search == active_.end())
+ return;
+
+ mojom::HitTestRegionList* hit_test_region_list = search->second.get();
+
+ AggregatedHitTestRegion* regions =
+ static_cast<AggregatedHitTestRegion*>(write_buffer_.get());
+
+ regions[0].frame_sink_id = hit_test_region_list->surface_id.frame_sink_id();
+ regions[0].flags = hit_test_region_list->flags;
+ regions[0].rect = hit_test_region_list->bounds;
+ regions[0].transform = hit_test_region_list->transform;
+
+ int region_index = 1;
+ for (const auto& region : hit_test_region_list->regions) {
+ if (region_index >= write_size_ - 1)
+ break;
+ region_index = AppendRegion(regions, region_index, region);
+ }
+
+ DCHECK_GE(region_index, 1);
+ regions[0].child_count = region_index - 1;
+ regions[region_index].child_count = kEndOfList;
+}
+
+int HitTestAggregator::AppendRegion(AggregatedHitTestRegion* regions,
+ int region_index,
+ const mojom::HitTestRegionPtr& region) {
+ AggregatedHitTestRegion* element = &regions[region_index];
+
+ element->frame_sink_id = region->surface_id.frame_sink_id();
+ element->flags = region->flags;
+ element->rect = region->rect;
+ element->transform = region->transform;
+
+ int parent_index = region_index++;
+ if (region_index >= write_size_ - 1) {
+ element->child_count = 0;
+ return region_index;
+ }
+
+ if (region->flags == mojom::kHitTestChildSurface) {
+ auto search = active_.find(region->surface_id);
+ if (search == active_.end()) {
+ // Surface HitTestRegionList not found - it may be late.
+ // Don't include this region so that it doesn't receive events.
+ return parent_index;
+ }
+
+ // Rather than add a node in the tree for this hit_test_region_list element
+ // we can simplify the tree by merging the flags and transform into
+ // the kHitTestChildSurface element.
+ mojom::HitTestRegionList* hit_test_region_list = search->second.get();
+ if (!hit_test_region_list->transform.IsIdentity())
+ element->transform.PreconcatTransform(hit_test_region_list->transform);
+
+ element->flags |= hit_test_region_list->flags;
+
+ for (const auto& child_region : hit_test_region_list->regions) {
+ region_index = AppendRegion(regions, region_index, child_region);
+ if (region_index >= write_size_ - 1)
+ break;
+ }
+ }
+ DCHECK_GE(region_index - parent_index - 1, 0);
+ element->child_count = region_index - parent_index - 1;
+ return region_index;
+}
+
+void HitTestAggregator::Swap() {
+ using std::swap;
+
+ swap(read_handle_, write_handle_);
+ swap(read_size_, write_size_);
+ swap(read_buffer_, write_buffer_);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/hit_test/hit_test_aggregator.h b/chromium/components/viz/service/hit_test/hit_test_aggregator.h
new file mode 100644
index 00000000000..0b49a18201f
--- /dev/null
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator.h
@@ -0,0 +1,107 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_HIT_TEST_HIT_TEST_AGGREGATOR_H_
+#define COMPONENTS_VIZ_SERVICE_HIT_TEST_HIT_TEST_AGGREGATOR_H_
+
+#include "cc/surfaces/surface_observer.h"
+#include "components/viz/common/hit_test/aggregated_hit_test_region.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/viz_service_export.h"
+#include "services/viz/hit_test/public/interfaces/hit_test_region_list.mojom.h"
+
+namespace viz {
+
+// HitTestAggregator collects HitTestRegionList objects from surfaces and
+// aggregates them into a DisplayHitTesData structue made available in
+// shared memory to enable efficient hit testing across processes.
+//
+// This is intended to be created in the viz or GPU process. For mus+ash this
+// will be true after the mus process split.
+class VIZ_SERVICE_EXPORT HitTestAggregator : public cc::SurfaceObserver {
+ public:
+ HitTestAggregator();
+ ~HitTestAggregator();
+
+ // Called when HitTestRegionList is submitted along with every call
+ // to SubmitCompositorFrame. This is collected in pending_ until
+ // surfaces are aggregated and put on the display.
+ void SubmitHitTestRegionList(
+ mojom::HitTestRegionListPtr hit_test_region_list);
+
+ // Called after surfaces have been aggregated into the DisplayFrame.
+ // In this call HitTestRegionList structures received from active surfaces
+ // are aggregated into the HitTestRegionList structure in
+ // shared memory used for event targetting.
+ void Aggregate(const SurfaceId& display_surface_id);
+
+ // Performs the work of Aggregate by creating a PostTask so that
+ // the work is not directly on the call.
+ void PostTaskAggregate(SurfaceId display_surface_id);
+
+ // Called at BeginFrame. Swaps buffers in shared memory.
+ void Swap();
+
+ protected:
+ // cc::SurfaceObserver:
+ void OnSurfaceCreated(const SurfaceInfo& surface_info) override {}
+ void OnSurfaceDestroyed(const SurfaceId& surface_id) override {}
+ bool OnSurfaceDamaged(const SurfaceId& surface_id,
+ const cc::BeginFrameAck& ack) override;
+ void OnSurfaceDiscarded(const SurfaceId& surface_id) override;
+ void OnSurfaceDamageExpected(const SurfaceId& surface_id,
+ const cc::BeginFrameArgs& args) override {}
+
+ // Called when a surface has been aggregated and added to the
+ // display frame. HitTestRegionList objects are held but ignored until
+ // this happens. HitTestRegionList for the surface is copied from |pending_|
+ // to |active_| in this method.
+ void OnSurfaceWillDraw(const SurfaceId& surface_id) override;
+
+ // The collection of received HitTestRegionList objects that have not yet
+ // been added to the DisplayFrame (OnSurfaceWillDraw has not been called).
+ std::map<SurfaceId, mojom::HitTestRegionListPtr> pending_;
+
+ // The collection of HitTestRegionList objects that have been added to the
+ // DisplayFrame (OnSurfaceWillDraw has been called).
+ std::map<SurfaceId, mojom::HitTestRegionListPtr> active_;
+
+ // Keeps track of the number of regions in the active list
+ // so that we know when we exceed the available length.
+ int active_region_count_ = 0;
+
+ mojo::ScopedSharedBufferHandle read_handle_;
+ mojo::ScopedSharedBufferHandle write_handle_;
+
+ // The number of elements allocated.
+ int read_size_ = 0;
+ int write_size_ = 0;
+
+ mojo::ScopedSharedBufferMapping read_buffer_;
+ mojo::ScopedSharedBufferMapping write_buffer_;
+
+ private:
+ // Allocates memory for the AggregatedHitTestRegion array.
+ void AllocateHitTestRegionArray();
+ void AllocateHitTestRegionArray(int length);
+
+ // Appends the root element to the AggregatedHitTestRegion array.
+ void AppendRoot(const SurfaceId& surface_id);
+
+ // Appends a region to the HitTestRegionList structure to recursively
+ // build the tree.
+ int AppendRegion(AggregatedHitTestRegion* regions,
+ int region_index,
+ const mojom::HitTestRegionPtr& region);
+
+ // Handles the case when this object is deleted after
+ // the PostTaskAggregation call is scheduled but before invocation.
+ base::WeakPtrFactory<HitTestAggregator> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HitTestAggregator);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_HIT_TEST_HIT_TEST_AGGREGATOR_H_
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
new file mode 100644
index 00000000000..4a23b4dca4a
--- /dev/null
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
@@ -0,0 +1,1017 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/hit_test/hit_test_aggregator.h"
+
+#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+
+namespace {
+
+constexpr FrameSinkId kDisplayFrameSink(2, 0);
+
+SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) {
+ return SurfaceId(
+ frame_sink_id,
+ LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u)));
+}
+
+} // namespace
+
+class TestHitTestAggregator : public HitTestAggregator {
+ public:
+ TestHitTestAggregator() = default;
+ ~TestHitTestAggregator() = default;
+
+ void CallOnSurfaceWillDraw(SurfaceId surface_id) {
+ OnSurfaceWillDraw(surface_id);
+ }
+ void CallOnSurfaceDiscarded(SurfaceId surface_id) {
+ OnSurfaceDiscarded(surface_id);
+ }
+
+ int Count() {
+ AggregatedHitTestRegion* start =
+ static_cast<AggregatedHitTestRegion*>(read_buffer_.get());
+ AggregatedHitTestRegion* end = start;
+ while (end->child_count != kEndOfList)
+ end++;
+
+ int count = end - start;
+ return count;
+ }
+ int GetPendingCount() { return pending_.size(); }
+ int GetActiveCount() { return active_.size(); }
+ int GetActiveRegionCount() { return active_region_count_; }
+ int GetHitTestRegionListSize() { return read_size_; }
+ AggregatedHitTestRegion* GetRegions() {
+ return static_cast<AggregatedHitTestRegion*>(read_buffer_.get());
+ }
+
+ void Reset() {
+ AggregatedHitTestRegion* regions =
+ static_cast<AggregatedHitTestRegion*>(write_buffer_.get());
+ regions[0].child_count = kEndOfList;
+
+ regions = static_cast<AggregatedHitTestRegion*>(read_buffer_.get());
+ regions[0].child_count = kEndOfList;
+
+ pending_.clear();
+ active_.clear();
+ }
+};
+
+class HitTestAggregatorTest : public testing::Test {
+ public:
+ HitTestAggregatorTest() = default;
+ ~HitTestAggregatorTest() override = default;
+
+ // testing::Test:
+ void SetUp() override {}
+ void TearDown() override { aggregator_.Reset(); }
+
+ TestHitTestAggregator aggregator_;
+
+ // Creates a hit test data element with 8 children recursively to
+ // the specified depth. SurfaceIds are generated in sequential order and
+ // the method returns the next unused id.
+ int CreateAndSubmitHitTestRegionListWith8Children(int id, int depth) {
+ SurfaceId surface_id = MakeSurfaceId(kDisplayFrameSink, id);
+ id++;
+
+ auto hit_test_region_list = mojom::HitTestRegionList::New();
+ hit_test_region_list->surface_id = surface_id;
+ hit_test_region_list->flags = mojom::kHitTestMine;
+ hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
+
+ for (int i = 0; i < 8; i++) {
+ auto hit_test_region = mojom::HitTestRegion::New();
+ hit_test_region->rect.SetRect(100, 100, 100, 100);
+
+ if (depth > 0) {
+ hit_test_region->flags = mojom::kHitTestChildSurface;
+ hit_test_region->surface_id = MakeSurfaceId(kDisplayFrameSink, id);
+ id = CreateAndSubmitHitTestRegionListWith8Children(id, depth - 1);
+ } else {
+ hit_test_region->flags = mojom::kHitTestMine;
+ }
+ hit_test_region_list->regions.push_back(std::move(hit_test_region));
+ }
+
+ aggregator_.SubmitHitTestRegionList(std::move(hit_test_region_list));
+ return id;
+ }
+};
+
+// TODO(gklassen): Add tests for 3D use cases as suggested by and with
+// input from rjkroege.
+
+// One surface.
+//
+// +----------+
+// | |
+// | |
+// | |
+// +----------+
+//
+TEST_F(HitTestAggregatorTest, OneSurface) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId display_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+
+ auto hit_test_region_list = mojom::HitTestRegionList::New();
+ hit_test_region_list->surface_id = display_surface_id;
+ hit_test_region_list->flags = mojom::kHitTestMine;
+ hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
+
+ aggregator_.SubmitHitTestRegionList(std::move(hit_test_region_list));
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(display_surface_id);
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ aggregator_.Aggregate(display_surface_id);
+ aggregator_.Swap();
+
+ // Expect 1 entry routing all events to the one surface (display root).
+ EXPECT_EQ(1, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(display_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// One opaque embedder with two regions.
+//
+// +e-------------+
+// | +r1-+ +r2--+ |
+// | | | | | |
+// | | | | | |
+// | +---+ +----+ |
+// +--------------+
+//
+TEST_F(HitTestAggregatorTest, OneEmbedderTwoRegions) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_r1 = mojom::HitTestRegion::New();
+ e_hit_test_region_r1->flags = mojom::kHitTestMine;
+ e_hit_test_region_r1->rect.SetRect(100, 100, 200, 400);
+
+ auto e_hit_test_region_r2 = mojom::HitTestRegion::New();
+ e_hit_test_region_r2->flags = mojom::kHitTestMine;
+ e_hit_test_region_r2->rect.SetRect(400, 100, 300, 400);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_r1));
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_r2));
+
+ // Submit mojom::HitTestRegionList.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ // Add Surfaces to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+ EXPECT_EQ(3, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(2, region->child_count);
+
+ region = &regions[1];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(gfx::Rect(100, 100, 200, 400), region->rect);
+ EXPECT_EQ(0, region->child_count);
+
+ region = &regions[2];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(gfx::Rect(400, 100, 300, 400), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// One embedder with two children.
+//
+// +e-------------+
+// | +c1-+ +c2--+ |
+// | | | | | |
+// | | | | | |
+// | +---+ +----+ |
+// +--------------+
+//
+
+TEST_F(HitTestAggregatorTest, OneEmbedderTwoChildren) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ SurfaceId c1_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
+ SurfaceId c2_surface_id = MakeSurfaceId(kDisplayFrameSink, 3);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_c1 = mojom::HitTestRegion::New();
+ e_hit_test_region_c1->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c1->surface_id = c1_surface_id;
+ e_hit_test_region_c1->rect.SetRect(100, 100, 200, 300);
+
+ auto e_hit_test_region_c2 = mojom::HitTestRegion::New();
+ e_hit_test_region_c2->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c2->surface_id = c2_surface_id;
+ e_hit_test_region_c2->rect.SetRect(400, 100, 400, 300);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c1));
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c2));
+
+ auto c1_hit_test_data = mojom::HitTestRegionList::New();
+ c1_hit_test_data->surface_id = c1_surface_id;
+
+ auto c2_hit_test_data = mojom::HitTestRegionList::New();
+ c2_hit_test_data->surface_id = c2_surface_id;
+
+ // Submit in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c1_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(2, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c2_hit_test_data));
+ EXPECT_EQ(3, aggregator_.GetPendingCount());
+
+ // Surfaces added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c2_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c1_surface_id);
+ EXPECT_EQ(2, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(3, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+
+ EXPECT_EQ(3, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(2, region->child_count);
+
+ region = &regions[1];
+ EXPECT_EQ(mojom::kHitTestChildSurface, region->flags);
+ EXPECT_EQ(c1_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(100, 100, 200, 300), region->rect);
+ EXPECT_EQ(0, region->child_count);
+
+ region = &regions[2];
+ EXPECT_EQ(mojom::kHitTestChildSurface, region->flags);
+ EXPECT_EQ(c2_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(400, 100, 400, 300), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// Occluded child frame (OOPIF).
+//
+// +e-----------+
+// | +c--+ |
+// | | +div-+ |
+// | | | | |
+// | | +----+ |
+// | +---+ |
+// +------------+
+//
+
+TEST_F(HitTestAggregatorTest, OccludedChildFrame) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_div = mojom::HitTestRegion::New();
+ e_hit_test_region_div->flags = mojom::kHitTestMine;
+ e_hit_test_region_div->surface_id = e_surface_id;
+ e_hit_test_region_div->rect.SetRect(200, 200, 300, 200);
+
+ auto e_hit_test_region_c = mojom::HitTestRegion::New();
+ e_hit_test_region_c->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c->surface_id = c_surface_id;
+ e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
+
+ auto c_hit_test_data = mojom::HitTestRegionList::New();
+ c_hit_test_data->surface_id = c_surface_id;
+ c_hit_test_data->flags = mojom::kHitTestMine;
+ c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+
+ // Submit in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(2, aggregator_.GetPendingCount());
+
+ // Surfaces added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c_surface_id);
+ EXPECT_EQ(2, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+
+ EXPECT_EQ(3, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(2, region->child_count);
+
+ region = &regions[1];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(200, 200, 300, 200), region->rect);
+ EXPECT_EQ(0, region->child_count);
+
+ region = &regions[2];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestMine);
+ EXPECT_EQ(c_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(100, 100, 200, 500), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// Foreground child frame (OOPIF).
+// Same as the previous test except the child is foreground.
+//
+// +e-----------+
+// | +c--+ |
+// | | |div-+ |
+// | | | | |
+// | | |----+ |
+// | +---+ |
+// +------------+
+//
+
+TEST_F(HitTestAggregatorTest, ForegroundChildFrame) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_div = mojom::HitTestRegion::New();
+ e_hit_test_region_div->flags = mojom::kHitTestMine;
+ e_hit_test_region_div->surface_id = e_surface_id;
+ e_hit_test_region_div->rect.SetRect(200, 200, 300, 200);
+
+ auto e_hit_test_region_c = mojom::HitTestRegion::New();
+ e_hit_test_region_c->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c->surface_id = c_surface_id;
+ e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
+
+ auto c_hit_test_data = mojom::HitTestRegionList::New();
+ c_hit_test_data->surface_id = c_surface_id;
+ c_hit_test_data->flags = mojom::kHitTestMine;
+ c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+
+ // Submit in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(2, aggregator_.GetPendingCount());
+
+ // Surfaces added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c_surface_id);
+ EXPECT_EQ(2, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+
+ EXPECT_EQ(3, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(2, region->child_count);
+
+ region = &regions[1];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestMine);
+ EXPECT_EQ(c_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(100, 100, 200, 500), region->rect);
+ EXPECT_EQ(0, region->child_count);
+
+ region = &regions[2];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(200, 200, 300, 200), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// One embedder with a clipped child with a tab and transparent background.
+//
+// +e-------------+
+// | +c---------| Point maps to
+// | 1 |+a--+ | ----- -------
+// | || 2 | 3 | 1 e
+// | |+b--------| 2 a
+// | || | 3 e (transparent area in c)
+// | || 4 | 4 b
+// +--------------+
+//
+
+TEST_F(HitTestAggregatorTest, ClippedChildWithTabAndTransparentBackground) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
+ SurfaceId a_surface_id = MakeSurfaceId(kDisplayFrameSink, 3);
+ SurfaceId b_surface_id = MakeSurfaceId(kDisplayFrameSink, 4);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_c = mojom::HitTestRegion::New();
+ e_hit_test_region_c->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c->surface_id = c_surface_id;
+ e_hit_test_region_c->rect.SetRect(200, 100, 1600, 800);
+ e_hit_test_region_c->transform.Translate(200, 100);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
+
+ auto c_hit_test_data = mojom::HitTestRegionList::New();
+ c_hit_test_data->surface_id = c_surface_id;
+ c_hit_test_data->flags = mojom::kHitTestIgnore;
+ c_hit_test_data->bounds.SetRect(0, 0, 1600, 800);
+
+ auto c_hit_test_region_a = mojom::HitTestRegion::New();
+ c_hit_test_region_a->flags = mojom::kHitTestChildSurface;
+ c_hit_test_region_a->surface_id = a_surface_id;
+ c_hit_test_region_a->rect.SetRect(0, 0, 200, 100);
+
+ auto c_hit_test_region_b = mojom::HitTestRegion::New();
+ c_hit_test_region_b->flags = mojom::kHitTestChildSurface;
+ c_hit_test_region_b->surface_id = b_surface_id;
+ c_hit_test_region_b->rect.SetRect(0, 100, 800, 600);
+
+ c_hit_test_data->regions.push_back(std::move(c_hit_test_region_a));
+ c_hit_test_data->regions.push_back(std::move(c_hit_test_region_b));
+
+ auto a_hit_test_data = mojom::HitTestRegionList::New();
+ a_hit_test_data->surface_id = a_surface_id;
+ a_hit_test_data->flags = mojom::kHitTestMine;
+ a_hit_test_data->bounds.SetRect(0, 0, 200, 100);
+
+ auto b_hit_test_data = mojom::HitTestRegionList::New();
+ b_hit_test_data->surface_id = b_surface_id;
+ b_hit_test_data->flags = mojom::kHitTestMine;
+ b_hit_test_data->bounds.SetRect(0, 100, 800, 600);
+
+ // Submit in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(a_hit_test_data));
+ EXPECT_EQ(2, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(b_hit_test_data));
+ EXPECT_EQ(3, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(4, aggregator_.GetPendingCount());
+
+ // Surfaces added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(2, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(b_surface_id);
+ EXPECT_EQ(3, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(a_surface_id);
+ EXPECT_EQ(4, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+
+ EXPECT_EQ(4, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(3, region->child_count);
+
+ region = &regions[1];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestIgnore);
+ EXPECT_EQ(c_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(200, 100, 1600, 800), region->rect);
+ EXPECT_EQ(2, region->child_count);
+
+ gfx::Point point(300, 300);
+ region->transform.TransformPointReverse(&point);
+ EXPECT_TRUE(point == gfx::Point(100, 200));
+
+ region = &regions[2];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestMine);
+ EXPECT_EQ(a_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 200, 100), region->rect);
+ EXPECT_EQ(0, region->child_count);
+
+ region = &regions[3];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestMine);
+ EXPECT_EQ(b_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 100, 800, 600), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// Three children deep.
+//
+// +e------------+
+// | +c1-------+ |
+// | | +c2---+ | |
+// | | | +c3-| | |
+// | | | | | | |
+// | | | +---| | |
+// | | +-----+ | |
+// | +---------+ |
+// +-------------+
+//
+
+TEST_F(HitTestAggregatorTest, ThreeChildrenDeep) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ SurfaceId c1_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
+ SurfaceId c2_surface_id = MakeSurfaceId(kDisplayFrameSink, 3);
+ SurfaceId c3_surface_id = MakeSurfaceId(kDisplayFrameSink, 4);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_c1 = mojom::HitTestRegion::New();
+ e_hit_test_region_c1->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c1->surface_id = c1_surface_id;
+ e_hit_test_region_c1->rect.SetRect(100, 100, 700, 700);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c1));
+
+ auto c1_hit_test_data = mojom::HitTestRegionList::New();
+ c1_hit_test_data->surface_id = c1_surface_id;
+ c1_hit_test_data->flags = mojom::kHitTestMine;
+ c1_hit_test_data->bounds.SetRect(0, 0, 600, 600);
+
+ auto c1_hit_test_region_c2 = mojom::HitTestRegion::New();
+ c1_hit_test_region_c2->flags = mojom::kHitTestChildSurface;
+ c1_hit_test_region_c2->surface_id = c2_surface_id;
+ c1_hit_test_region_c2->rect.SetRect(100, 100, 500, 500);
+
+ c1_hit_test_data->regions.push_back(std::move(c1_hit_test_region_c2));
+
+ auto c2_hit_test_data = mojom::HitTestRegionList::New();
+ c2_hit_test_data->surface_id = c2_surface_id;
+ c2_hit_test_data->flags = mojom::kHitTestMine;
+ c2_hit_test_data->bounds.SetRect(0, 0, 400, 400);
+
+ auto c2_hit_test_region_c3 = mojom::HitTestRegion::New();
+ c2_hit_test_region_c3->flags = mojom::kHitTestChildSurface;
+ c2_hit_test_region_c3->surface_id = c3_surface_id;
+ c2_hit_test_region_c3->rect.SetRect(100, 100, 300, 300);
+
+ c2_hit_test_data->regions.push_back(std::move(c2_hit_test_region_c3));
+
+ auto c3_hit_test_data = mojom::HitTestRegionList::New();
+ c3_hit_test_data->surface_id = c3_surface_id;
+ c3_hit_test_data->flags = mojom::kHitTestMine;
+ c3_hit_test_data->bounds.SetRect(0, 0, 200, 200);
+
+ // Submit in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c1_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c3_hit_test_data));
+ EXPECT_EQ(2, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(3, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c2_hit_test_data));
+ EXPECT_EQ(4, aggregator_.GetPendingCount());
+
+ // Surfaces added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c2_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c1_surface_id);
+ EXPECT_EQ(2, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(3, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c3_surface_id);
+ EXPECT_EQ(4, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+
+ EXPECT_EQ(4, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(3, region->child_count);
+
+ region = &regions[1];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestMine);
+ EXPECT_EQ(c1_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(100, 100, 700, 700), region->rect);
+ EXPECT_EQ(2, region->child_count);
+
+ region = &regions[2];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestMine);
+ EXPECT_EQ(c2_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(100, 100, 500, 500), region->rect);
+ EXPECT_EQ(1, region->child_count);
+
+ region = &regions[3];
+ EXPECT_EQ(region->flags, mojom::kHitTestChildSurface | mojom::kHitTestMine);
+ EXPECT_EQ(c3_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(100, 100, 300, 300), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// Missing / late child.
+//
+// +e-----------+
+// | +c--+ |
+// | | |div-+ |
+// | | | | |
+// | | |----+ |
+// | +---+ |
+// +------------+
+//
+
+TEST_F(HitTestAggregatorTest, MissingChildFrame) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_div = mojom::HitTestRegion::New();
+ e_hit_test_region_div->flags = mojom::kHitTestMine;
+ e_hit_test_region_div->surface_id = e_surface_id;
+ e_hit_test_region_div->rect.SetRect(200, 200, 300, 200);
+
+ auto e_hit_test_region_c = mojom::HitTestRegion::New();
+ e_hit_test_region_c->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c->surface_id = c_surface_id;
+ e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
+
+ auto c_hit_test_data = mojom::HitTestRegionList::New();
+ c_hit_test_data->surface_id = c_surface_id;
+ c_hit_test_data->flags = mojom::kHitTestMine;
+ c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+
+ // Submit in unexpected order, but not the child.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ // Surfaces added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+
+ EXPECT_EQ(2, aggregator_.Count());
+
+ AggregatedHitTestRegion* regions = aggregator_.GetRegions();
+
+ AggregatedHitTestRegion* region = nullptr;
+
+ region = &regions[0];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(0, 0, 1024, 768), region->rect);
+ EXPECT_EQ(1, region->child_count);
+
+ // Child would exist here but it was not included in the Display Frame.
+
+ region = &regions[1];
+ EXPECT_EQ(mojom::kHitTestMine, region->flags);
+ EXPECT_EQ(e_surface_id.frame_sink_id(), region->frame_sink_id);
+ EXPECT_EQ(gfx::Rect(200, 200, 300, 200), region->rect);
+ EXPECT_EQ(0, region->child_count);
+}
+
+// Exceed limits to ensure that bounds and resize work.
+//
+// A tree of embedders each with 8 children and 4 levels deep = 4096 regions.
+// This will exceed initial allocation and force a resize.
+//
+// +e--------------------------------------------------------+
+// | +c1----------++c2----------++c3----------++c4----------+|
+// | | +c1--------|| +c1--------|| +c1--------|| +c1--------||
+// | | | +c1-++c2-|| | +c1-++c2-|| | +c1-++c2-|| | +c1-++c2-||
+// | | | | || || | | || || | | || || | | || ||
+// | | | +---++---|| | +---++---|| | +---++---|| | +---++---||
+// | +------------++------------++------------++------------+|
+// | +c5----------++c6----------++c7----------++c8----------+|
+// | | +c1--------|| +c1--------|| +c1--------|| +c1--------||
+// | | | +c1-++c2-|| | +c1-++c2-|| | +c1-++c2-|| | +c1-++c2-||
+// | | | | || || | | || || | | || || | | || ||
+// | | | +---++---|| | +---++---|| | +---++---|| | +---++---||
+// | +------------++------------++------------++------------+|
+// +---------------------------------------------------------+
+//
+
+TEST_F(HitTestAggregatorTest, ExceedLimits) {
+ EXPECT_EQ(0, aggregator_.Count());
+
+ EXPECT_LT(aggregator_.GetHitTestRegionListSize(), 4096);
+
+ SurfaceId display_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+
+ int next_surface_id = CreateAndSubmitHitTestRegionListWith8Children(1, 3);
+ int surface_count = next_surface_id - 1;
+
+ EXPECT_EQ(surface_count, aggregator_.GetPendingCount());
+
+ // Mark Surfaces as added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ for (int i = 1; i <= surface_count; i++) {
+ SurfaceId surface_id = MakeSurfaceId(kDisplayFrameSink, i);
+ aggregator_.CallOnSurfaceWillDraw(surface_id);
+ }
+
+ EXPECT_EQ(surface_count, aggregator_.GetActiveCount());
+
+ // Aggregate and swap.
+ aggregator_.Aggregate(display_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+
+ aggregator_.Swap();
+
+ // Expect 4680 regions:
+ // 8 children 4 levels deep 8*8*8*8 is 4096
+ // 1 region for each embedder/surface + 584
+ // 1 root + 1
+ // -----
+ // 4681.
+ EXPECT_EQ(4681, aggregator_.Count());
+
+ EXPECT_GE(aggregator_.GetHitTestRegionListSize(), 4681);
+}
+
+TEST_F(HitTestAggregatorTest, ActiveRegionCount) {
+ EXPECT_EQ(0, aggregator_.GetActiveRegionCount());
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
+ SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
+
+ auto e_hit_test_data = mojom::HitTestRegionList::New();
+ e_hit_test_data->surface_id = e_surface_id;
+ e_hit_test_data->flags = mojom::kHitTestMine;
+ e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+
+ auto e_hit_test_region_div = mojom::HitTestRegion::New();
+ e_hit_test_region_div->flags = mojom::kHitTestMine;
+ e_hit_test_region_div->surface_id = e_surface_id;
+ e_hit_test_region_div->rect.SetRect(200, 200, 300, 200);
+
+ auto e_hit_test_region_c = mojom::HitTestRegion::New();
+ e_hit_test_region_c->flags = mojom::kHitTestChildSurface;
+ e_hit_test_region_c->surface_id = c_surface_id;
+ e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
+
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
+ e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
+
+ auto c_hit_test_data = mojom::HitTestRegionList::New();
+ c_hit_test_data->surface_id = c_surface_id;
+ c_hit_test_data->flags = mojom::kHitTestMine;
+ c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+
+ EXPECT_EQ(0, aggregator_.GetActiveRegionCount());
+
+ // Submit in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(c_hit_test_data));
+ EXPECT_EQ(1, aggregator_.GetPendingCount());
+
+ aggregator_.SubmitHitTestRegionList(std::move(e_hit_test_data));
+ EXPECT_EQ(2, aggregator_.GetPendingCount());
+
+ EXPECT_EQ(0, aggregator_.GetActiveRegionCount());
+
+ // Surfaces added to DisplayFrame in unexpected order.
+
+ EXPECT_EQ(0, aggregator_.Count());
+ EXPECT_EQ(0, aggregator_.GetActiveCount());
+
+ aggregator_.CallOnSurfaceWillDraw(e_surface_id);
+ EXPECT_EQ(1, aggregator_.GetActiveCount());
+ EXPECT_EQ(2, aggregator_.GetActiveRegionCount());
+
+ aggregator_.CallOnSurfaceWillDraw(c_surface_id);
+ EXPECT_EQ(2, aggregator_.GetActiveCount());
+ EXPECT_EQ(2, aggregator_.GetActiveRegionCount());
+
+ // Aggregate and swap.
+
+ aggregator_.Aggregate(e_surface_id);
+ EXPECT_EQ(0, aggregator_.Count());
+ EXPECT_EQ(2, aggregator_.GetActiveRegionCount());
+
+ aggregator_.Swap();
+
+ EXPECT_EQ(3, aggregator_.Count());
+ EXPECT_EQ(2, aggregator_.GetActiveRegionCount());
+
+ // Discard Surface and ensure active count goes down.
+
+ aggregator_.CallOnSurfaceDiscarded(c_surface_id);
+ EXPECT_EQ(2, aggregator_.GetActiveRegionCount());
+
+ aggregator_.CallOnSurfaceDiscarded(e_surface_id);
+ EXPECT_EQ(0, aggregator_.GetActiveRegionCount());
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/viz_service_export.h b/chromium/components/viz/service/viz_service_export.h
new file mode 100644
index 00000000000..e2d6a9e9688
--- /dev/null
+++ b/chromium/components/viz/service/viz_service_export.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_VIZ_SERVICE_EXPORT_H_
+#define COMPONENTS_VIZ_SERVICE_VIZ_SERVICE_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(VIZ_SERVICE_IMPLEMENTATION)
+#define VIZ_SERVICE_EXPORT __declspec(dllexport)
+#else
+#define VIZ_SERVICE_EXPORT __declspec(dllimport)
+#endif // defined(VIZ_SERVICE_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(VIZ_SERVICE_IMPLEMENTATION)
+#define VIZ_SERVICE_EXPORT __attribute__((visibility("default")))
+#else
+#define VIZ_SERVICE_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define VIZ_SERVICE_EXPORT
+#endif
+
+#endif // COMPONENTS_VIZ_SERVICE_VIZ_SERVICE_EXPORT_H_
diff --git a/chromium/components/viz/test/BUILD.gn b/chromium/components/viz/test/BUILD.gn
new file mode 100644
index 00000000000..91e0425cddb
--- /dev/null
+++ b/chromium/components/viz/test/BUILD.gn
@@ -0,0 +1,36 @@
+# 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.
+
+import("//components/viz/viz.gni")
+
+viz_static_library("test_support") {
+ testonly = true
+ sources = [
+ "paths.cc",
+ "paths.h",
+ "test_layer_tree_frame_sink.cc",
+ "test_layer_tree_frame_sink.h",
+ ]
+ deps = [
+ "//base",
+ "//cc",
+ "//cc/surfaces",
+ "//components/viz/service",
+ "//ui/gfx/geometry",
+ ]
+}
+
+viz_source_set("test_suite") {
+ testonly = true
+ sources = [
+ "viz_test_suite.cc",
+ "viz_test_suite.h",
+ ]
+ deps = [
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//ui/gl:test_support",
+ ]
+}
diff --git a/chromium/components/viz/viz.gni b/chromium/components/viz/viz.gni
new file mode 100644
index 00000000000..5df8b1aba69
--- /dev/null
+++ b/chromium/components/viz/viz.gni
@@ -0,0 +1,57 @@
+# 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.
+
+import("//testing/test.gni")
+
+viz_remove_configs = []
+viz_add_configs = [ "//build/config:precompiled_headers" ]
+
+if (!is_debug && (is_win || is_android)) {
+ viz_remove_configs += [ "//build/config/compiler:default_optimization" ]
+ viz_add_configs += [ "//build/config/compiler:optimize_max" ]
+}
+
+template("viz_source_set") {
+ source_set(target_name) {
+ forward_variables_from(invoker, "*", [ "configs" ])
+ if (defined(invoker.configs)) {
+ configs += invoker.configs
+ }
+ configs -= viz_remove_configs
+ configs += viz_add_configs
+ }
+}
+
+template("viz_component") {
+ component(target_name) {
+ forward_variables_from(invoker, "*", [ "configs" ])
+ if (defined(invoker.configs)) {
+ configs += invoker.configs
+ }
+ configs -= viz_remove_configs
+ configs += viz_add_configs
+ }
+}
+
+template("viz_static_library") {
+ static_library(target_name) {
+ forward_variables_from(invoker, "*", [ "configs" ])
+ if (defined(invoker.configs)) {
+ configs += invoker.configs
+ }
+ configs -= viz_remove_configs
+ configs += viz_add_configs
+ }
+}
+
+template("viz_test") {
+ test(target_name) {
+ forward_variables_from(invoker, "*", [ "configs" ])
+ if (defined(invoker.configs)) {
+ configs += invoker.configs
+ }
+ configs -= viz_remove_configs
+ configs += viz_add_configs
+ }
+}
diff --git a/chromium/components/viz/viz_export.h b/chromium/components/viz/viz_export.h
deleted file mode 100644
index 4948f15de93..00000000000
--- a/chromium/components/viz/viz_export.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 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_VIZ_EXPORT_H_
-#define COMPONENTS_VIZ_VIZ_EXPORT_H_
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined(VIZ_IMPLEMENTATION)
-#define VIZ_EXPORT __declspec(dllexport)
-#else
-#define VIZ_EXPORT __declspec(dllimport)
-#endif // defined(VIZ_IMPLEMENTATION)
-
-#else // defined(WIN32)
-#if defined(VIZ_IMPLEMENTATION)
-#define VIZ_EXPORT __attribute__((visibility("default")))
-#else
-#define VIZ_EXPORT
-#endif
-#endif
-
-#else // defined(COMPONENT_BUILD)
-#define VIZ_EXPORT
-#endif
-
-#endif // COMPONENTS_VIZ_VIZ_EXPORT_H_
diff --git a/chromium/components/wallpaper/BUILD.gn b/chromium/components/wallpaper/BUILD.gn
index 84f4d149ea4..6bdf12acc7e 100644
--- a/chromium/components/wallpaper/BUILD.gn
+++ b/chromium/components/wallpaper/BUILD.gn
@@ -7,9 +7,11 @@ component("wallpaper") {
"wallpaper_color_calculator.cc",
"wallpaper_color_calculator.h",
"wallpaper_color_calculator_observer.h",
+ "wallpaper_color_extraction_result.h",
+ "wallpaper_color_profile.h",
"wallpaper_files_id.cc",
"wallpaper_files_id.h",
- "wallpaper_layout.h",
+ "wallpaper_info.h",
"wallpaper_resizer.cc",
"wallpaper_resizer.h",
"wallpaper_resizer_observer.h",
@@ -49,6 +51,7 @@ source_set("unit_tests") {
deps = [
":wallpaper",
"//base/test:test_support",
+ "//testing/gmock",
"//testing/gtest",
"//ui/gfx",
]
diff --git a/chromium/components/wallpaper/wallpaper_color_calculator.cc b/chromium/components/wallpaper/wallpaper_color_calculator.cc
index 499fdd544da..39f6f7aa111 100644
--- a/chromium/components/wallpaper/wallpaper_color_calculator.cc
+++ b/chromium/components/wallpaper/wallpaper_color_calculator.cc
@@ -12,6 +12,12 @@
#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "components/wallpaper/wallpaper_color_calculator_observer.h"
+#include "components/wallpaper/wallpaper_color_extraction_result.h"
+#include "ui/gfx/color_analysis.h"
+#include "ui/gfx/image/image_skia.h"
+
+using LumaRange = color_utils::LumaRange;
+using SaturationRange = color_utils::SaturationRange;
namespace wallpaper {
@@ -23,24 +29,61 @@ namespace {
// thread would actually take longer.
const int kMaxPixelsForSynchronousCalculation = 100;
-// Wrapper for color_utils::CalculateProminentColorOfBitmap() that records
+// Wrapper for color_utils::CalculateProminentColorsOfBitmap() that records
// wallpaper specific metrics.
//
// NOTE: |image| is intentionally a copy to ensure it exists for the duration of
// the calculation.
-SkColor CalculateWallpaperColor(const gfx::ImageSkia image,
- color_utils::LumaRange luma,
- color_utils::SaturationRange saturation) {
+std::vector<SkColor> CalculateWallpaperColor(
+ const gfx::ImageSkia image,
+ const std::vector<color_utils::ColorProfile> color_profiles) {
base::TimeTicks start_time = base::TimeTicks::Now();
- const SkColor prominent_color = color_utils::CalculateProminentColorOfBitmap(
- *image.bitmap(), luma, saturation);
+ const std::vector<SkColor> prominent_colors =
+ color_utils::CalculateProminentColorsOfBitmap(*image.bitmap(),
+ color_profiles);
UMA_HISTOGRAM_TIMES("Ash.Wallpaper.ColorExtraction.Durations",
base::TimeTicks::Now() - start_time);
- UMA_HISTOGRAM_BOOLEAN("Ash.Wallpaper.ColorExtractionResult",
- prominent_color != SK_ColorTRANSPARENT);
+ WallpaperColorExtractionResult result = NUM_COLOR_EXTRACTION_RESULTS;
+ for (size_t i = 0; i < color_profiles.size(); ++i) {
+ bool is_result_transparent = prominent_colors[i] == SK_ColorTRANSPARENT;
+ if (color_profiles[i].saturation == SaturationRange::VIBRANT) {
+ switch (color_profiles[i].luma) {
+ case LumaRange::DARK:
+ result = is_result_transparent ? RESULT_DARK_VIBRANT_TRANSPARENT
+ : RESULT_DARK_VIBRANT_OPAQUE;
+ break;
+ case LumaRange::NORMAL:
+ result = is_result_transparent ? RESULT_NORMAL_VIBRANT_TRANSPARENT
+ : RESULT_NORMAL_VIBRANT_OPAQUE;
+ break;
+ case LumaRange::LIGHT:
+ result = is_result_transparent ? RESULT_LIGHT_VIBRANT_TRANSPARENT
+ : RESULT_LIGHT_VIBRANT_OPAQUE;
+ break;
+ }
+ } else {
+ switch (color_profiles[i].luma) {
+ case LumaRange::DARK:
+ result = is_result_transparent ? RESULT_DARK_MUTED_TRANSPARENT
+ : RESULT_DARK_MUTED_OPAQUE;
+ break;
+ case LumaRange::NORMAL:
+ result = is_result_transparent ? RESULT_NORMAL_MUTED_TRANSPARENT
+ : RESULT_NORMAL_MUTED_OPAQUE;
+ break;
+ case LumaRange::LIGHT:
+ result = is_result_transparent ? RESULT_LIGHT_MUTED_TRANSPARENT
+ : RESULT_LIGHT_MUTED_OPAQUE;
+ break;
+ }
+ }
+ }
+ DCHECK_NE(NUM_COLOR_EXTRACTION_RESULTS, result);
+ UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.ColorExtractionResult2", result,
+ NUM_COLOR_EXTRACTION_RESULTS);
- return prominent_color;
+ return prominent_colors;
}
bool ShouldCalculateSync(const gfx::ImageSkia& image) {
@@ -51,16 +94,17 @@ bool ShouldCalculateSync(const gfx::ImageSkia& image) {
WallpaperColorCalculator::WallpaperColorCalculator(
const gfx::ImageSkia& image,
- color_utils::LumaRange luma,
- color_utils::SaturationRange saturation,
+ const std::vector<color_utils::ColorProfile>& color_profiles,
scoped_refptr<base::TaskRunner> task_runner)
: image_(image),
- luma_(luma),
- saturation_(saturation),
+ color_profiles_(color_profiles),
task_runner_(std::move(task_runner)),
- weak_ptr_factory_(this) {}
+ weak_ptr_factory_(this) {
+ prominent_colors_ =
+ std::vector<SkColor>(color_profiles_.size(), SK_ColorTRANSPARENT);
+}
-WallpaperColorCalculator::~WallpaperColorCalculator() {}
+WallpaperColorCalculator::~WallpaperColorCalculator() = default;
void WallpaperColorCalculator::AddObserver(
WallpaperColorCalculatorObserver* observer) {
@@ -74,25 +118,26 @@ void WallpaperColorCalculator::RemoveObserver(
bool WallpaperColorCalculator::StartCalculation() {
if (ShouldCalculateSync(image_)) {
- const SkColor prominent_color =
- CalculateWallpaperColor(image_, luma_, saturation_);
- NotifyCalculationComplete(prominent_color);
+ const std::vector<SkColor> prominent_colors =
+ CalculateWallpaperColor(image_, color_profiles_);
+ NotifyCalculationComplete(prominent_colors);
return true;
}
image_.MakeThreadSafe();
if (base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
- base::Bind(&CalculateWallpaperColor, image_, luma_, saturation_),
+ base::Bind(&CalculateWallpaperColor, image_, color_profiles_),
base::Bind(&WallpaperColorCalculator::OnAsyncCalculationComplete,
weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now()))) {
return true;
}
LOG(WARNING) << "PostSequencedWorkerTask failed. "
- << "Wallpaper promiment color may not be calculated.";
+ << "Wallpaper prominent colors may not be calculated.";
- prominent_color_ = SK_ColorTRANSPARENT;
+ prominent_colors_ =
+ std::vector<SkColor>(color_profiles_.size(), SK_ColorTRANSPARENT);
return false;
}
@@ -103,15 +148,15 @@ void WallpaperColorCalculator::SetTaskRunnerForTest(
void WallpaperColorCalculator::OnAsyncCalculationComplete(
base::TimeTicks async_start_time,
- SkColor prominent_color) {
+ const std::vector<SkColor>& prominent_colors) {
UMA_HISTOGRAM_TIMES("Ash.Wallpaper.ColorExtraction.UserDelay",
base::TimeTicks::Now() - async_start_time);
- NotifyCalculationComplete(prominent_color);
+ NotifyCalculationComplete(prominent_colors);
}
void WallpaperColorCalculator::NotifyCalculationComplete(
- SkColor prominent_color) {
- prominent_color_ = prominent_color;
+ const std::vector<SkColor>& prominent_colors) {
+ prominent_colors_ = prominent_colors;
for (auto& observer : observers_)
observer.OnColorCalculationComplete();
diff --git a/chromium/components/wallpaper/wallpaper_color_calculator.h b/chromium/components/wallpaper/wallpaper_color_calculator.h
index c59c12e7139..6862dee1e6d 100644
--- a/chromium/components/wallpaper/wallpaper_color_calculator.h
+++ b/chromium/components/wallpaper/wallpaper_color_calculator.h
@@ -12,25 +12,28 @@
#include "base/time/time.h"
#include "components/wallpaper/wallpaper_export.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/color_analysis.h"
#include "ui/gfx/image/image_skia.h"
namespace base {
class TaskRunner;
}
+namespace color_utils {
+struct ColorProfile;
+}
+
namespace wallpaper {
class WallpaperColorCalculatorObserver;
// Calculates colors based on a wallpaper image.
class WALLPAPER_EXPORT WallpaperColorCalculator {
public:
- // |image|, |luma| and |saturation| are the input parameters to the color
- // calculation that is executed on the |task_runner|.
- WallpaperColorCalculator(const gfx::ImageSkia& image,
- color_utils::LumaRange luma,
- color_utils::SaturationRange saturation,
- scoped_refptr<base::TaskRunner> task_runner);
+ // |image|, |color_profiles| are the input parameters to the color calculation
+ // that is executed on the |task_runner|.
+ WallpaperColorCalculator(
+ const gfx::ImageSkia& image,
+ const std::vector<color_utils::ColorProfile>& color_profiles,
+ scoped_refptr<base::TaskRunner> task_runner);
~WallpaperColorCalculator();
void AddObserver(WallpaperColorCalculatorObserver* observer);
@@ -42,10 +45,11 @@ class WALLPAPER_EXPORT WallpaperColorCalculator {
// Callers should be aware that this will make |image_| read-only.
bool StartCalculation() WARN_UNUSED_RESULT;
- SkColor prominent_color() const { return prominent_color_; }
+ std::vector<SkColor> prominent_colors() const { return prominent_colors_; }
- void set_prominent_color_for_test(SkColor prominent_color) {
- prominent_color_ = prominent_color;
+ void set_prominent_colors_for_test(
+ const std::vector<SkColor>& prominent_colors) {
+ prominent_colors_ = prominent_colors;
}
// Explicitly sets the |task_runner_| for testing.
@@ -55,23 +59,20 @@ class WALLPAPER_EXPORT WallpaperColorCalculator {
// Handles asynchronous calculation results. |async_start_time| is used to
// record duration metrics.
void OnAsyncCalculationComplete(base::TimeTicks async_start_time,
- SkColor prominent_color);
+ const std::vector<SkColor>& prominent_colors);
// Notifies observers that a color calulation has completed. Called on the
// same thread that constructed |this|.
- void NotifyCalculationComplete(SkColor prominent_color);
+ void NotifyCalculationComplete(const std::vector<SkColor>& prominent_colors);
// The result of the color calculation.
- SkColor prominent_color_ = SK_ColorTRANSPARENT;
+ std::vector<SkColor> prominent_colors_;
// The image to calculate colors from.
gfx::ImageSkia image_;
- // Input for the color calculation.
- color_utils::LumaRange luma_;
-
- // Input for the color calculation.
- color_utils::SaturationRange saturation_;
+ // The color profiles used to calculate colors.
+ std::vector<color_utils::ColorProfile> color_profiles_;
// The task runner to run the calculation on.
scoped_refptr<base::TaskRunner> task_runner_;
diff --git a/chromium/components/wallpaper/wallpaper_color_calculator_unittest.cc b/chromium/components/wallpaper/wallpaper_color_calculator_unittest.cc
index 595f5c06b57..fa7007d9731 100644
--- a/chromium/components/wallpaper/wallpaper_color_calculator_unittest.cc
+++ b/chromium/components/wallpaper/wallpaper_color_calculator_unittest.cc
@@ -14,7 +14,9 @@
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/wallpaper/wallpaper_color_calculator_observer.h"
+#include "components/wallpaper/wallpaper_color_extraction_result.h"
#include "skia/ext/platform_canvas.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"
@@ -24,6 +26,9 @@
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+
namespace wallpaper {
namespace {
@@ -59,9 +64,8 @@ class TestWallpaperColorCalculatorObserver
DISALLOW_COPY_AND_ASSIGN(TestWallpaperColorCalculatorObserver);
};
-// Returns an image that will yield a color when processing it with
-// color_utils::CalculateProminentColorOfBitmap() using the LumaRange::NORMAL
-// and SaturationRange::VIBRANT values.
+// Returns an image that will yield a color using the LumaRange::NORMAL and
+// SaturationRange::VIBRANT values.
gfx::ImageSkia CreateColorProducingImage(const gfx::Size& size) {
gfx::Canvas canvas(size, 1.0f, true);
canvas.DrawColor(kGray);
@@ -69,8 +73,8 @@ gfx::ImageSkia CreateColorProducingImage(const gfx::Size& size) {
return gfx::ImageSkia::CreateFrom1xBitmap(canvas.GetBitmap());
}
-// Returns an image that will not yield a color when processing it with
-// color_utils::CalculateProminentColorOfBitmap().
+// Returns an image that will not yield a color using the LumaRange::NORMAL and
+// SaturationRange::VIBRANT values.
gfx::ImageSkia CreateNonColorProducingImage(const gfx::Size& size) {
gfx::Canvas canvas(size, 1.0f, true);
canvas.DrawColor(kGray);
@@ -129,45 +133,50 @@ void WallPaperColorCalculatorTest::InstallTaskRunner(
void WallPaperColorCalculatorTest::CreateCalculator(
const gfx::ImageSkia& image) {
+ std::vector<color_utils::ColorProfile> color_profiles;
+ color_profiles.emplace_back(color_utils::LumaRange::NORMAL,
+ color_utils::SaturationRange::VIBRANT);
calculator_ = base::MakeUnique<WallpaperColorCalculator>(
- image, color_utils::LumaRange::NORMAL,
- color_utils::SaturationRange::VIBRANT, task_runner_);
+ image, color_profiles, task_runner_);
calculator_->AddObserver(&observer_);
}
// Used to group the asynchronous calculation tests.
-typedef WallPaperColorCalculatorTest WallPaperColorCalculatorAsyncTest;
+using WallPaperColorCalculatorAsyncTest = WallPaperColorCalculatorTest;
TEST_F(WallPaperColorCalculatorAsyncTest, MetricsForSuccessfulExtraction) {
- histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtractionResult", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 0);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ IsEmpty());
EXPECT_TRUE(calculator_->StartCalculation());
task_runner_->RunUntilIdle();
- histograms_.ExpectUniqueSample("Ash.Wallpaper.ColorExtractionResult", true,
- 1);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 1);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 1);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ ElementsAre(base::Bucket(RESULT_NORMAL_VIBRANT_OPAQUE, 1)));
}
TEST_F(WallPaperColorCalculatorAsyncTest, MetricsWhenPostingTaskFails) {
scoped_refptr<base::NullTaskRunner> task_runner = new base::NullTaskRunner();
InstallTaskRunner(task_runner);
- histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtractionResult", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 0);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ IsEmpty());
EXPECT_FALSE(calculator_->StartCalculation());
task_runner_->RunUntilIdle();
- histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtractionResult", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 0);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ IsEmpty());
- EXPECT_EQ(kDefaultColor, calculator_->prominent_color());
+ EXPECT_EQ(kDefaultColor, calculator_->prominent_colors()[0]);
}
TEST_F(WallPaperColorCalculatorAsyncTest,
@@ -182,13 +191,14 @@ TEST_F(WallPaperColorCalculatorAsyncTest,
}
TEST_F(WallPaperColorCalculatorAsyncTest, ColorUpdatedOnSuccessfulCalculation) {
- calculator_->set_prominent_color_for_test(kDefaultColor);
+ std::vector<SkColor> colors = {kDefaultColor};
+ calculator_->set_prominent_colors_for_test(colors);
EXPECT_TRUE(calculator_->StartCalculation());
- EXPECT_EQ(kDefaultColor, calculator_->prominent_color());
+ EXPECT_EQ(kDefaultColor, calculator_->prominent_colors()[0]);
task_runner_->RunUntilIdle();
- EXPECT_NE(kDefaultColor, calculator_->prominent_color());
+ EXPECT_NE(kDefaultColor, calculator_->prominent_colors()[0]);
}
TEST_F(WallPaperColorCalculatorAsyncTest,
@@ -204,37 +214,39 @@ TEST_F(WallPaperColorCalculatorAsyncTest,
}
// Used to group the synchronous calculation tests.
-typedef WallPaperColorCalculatorTest WallPaperColorCalculatorSyncTest;
+using WallPaperColorCalculatorSyncTest = WallPaperColorCalculatorTest;
TEST_F(WallPaperColorCalculatorSyncTest, MetricsForSuccessfulExtraction) {
CreateCalculator(CreateColorProducingImage(kSyncImageSize));
calculator_->SetTaskRunnerForTest(nullptr);
- histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtractionResult", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 0);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ IsEmpty());
EXPECT_TRUE(calculator_->StartCalculation());
- histograms_.ExpectUniqueSample("Ash.Wallpaper.ColorExtractionResult", true,
- 1);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 1);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 0);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ ElementsAre(base::Bucket(RESULT_NORMAL_VIBRANT_OPAQUE, 1)));
}
TEST_F(WallPaperColorCalculatorSyncTest, MetricsForFailedExctraction) {
CreateCalculator(CreateNonColorProducingImage(kSyncImageSize));
- histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtractionResult", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 0);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 0);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ IsEmpty());
EXPECT_TRUE(calculator_->StartCalculation());
- histograms_.ExpectUniqueSample("Ash.Wallpaper.ColorExtractionResult", false,
- 1);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.Durations", 1);
histograms_.ExpectTotalCount("Ash.Wallpaper.ColorExtraction.UserDelay", 0);
+ EXPECT_THAT(histograms_.GetAllSamples("Ash.Wallpaper.ColorExtractionResult2"),
+ ElementsAre(base::Bucket(RESULT_NORMAL_VIBRANT_TRANSPARENT, 1)));
}
} // namespace wallpaper
diff --git a/chromium/components/wallpaper/wallpaper_color_extraction_result.h b/chromium/components/wallpaper/wallpaper_color_extraction_result.h
new file mode 100644
index 00000000000..ebc77794f66
--- /dev/null
+++ b/chromium/components/wallpaper/wallpaper_color_extraction_result.h
@@ -0,0 +1,45 @@
+// 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_WALLPAPER_WALLPAPER_COLOR_EXTRACTION_RESULT_H_
+#define COMPONENTS_WALLPAPER_WALLPAPER_COLOR_EXTRACTION_RESULT_H_
+
+namespace wallpaper {
+
+// This enum is used to back a histogram, and should therefore be treated as
+// append-only.
+// For result, transparent indicates extraction failure and opaque indicates
+// extraction success.
+enum WallpaperColorExtractionResult {
+ // Transparent result on (dark, vibrant) color profile.
+ RESULT_DARK_VIBRANT_TRANSPARENT = 0,
+ // Opaque result on (dark, vibrant) color profile.
+ RESULT_DARK_VIBRANT_OPAQUE,
+ // Transparent result on (normal, vibrant) color profile.
+ RESULT_NORMAL_VIBRANT_TRANSPARENT,
+ // Opaque result on (normal, vibrant) color profile.
+ RESULT_NORMAL_VIBRANT_OPAQUE,
+ // Transparent result on (light, vibrant) color profile.
+ RESULT_LIGHT_VIBRANT_TRANSPARENT,
+ // Opaque result on (light, vibrant) color profile.
+ RESULT_LIGHT_VIBRANT_OPAQUE,
+ // Transparent result on (dark, muted) color profile.
+ RESULT_DARK_MUTED_TRANSPARENT,
+ // Opaque result on (dark, muted) color profile.
+ RESULT_DARK_MUTED_OPAQUE,
+ // Transparent result on (normal, muted) color profile.
+ RESULT_NORMAL_MUTED_TRANSPARENT,
+ // Opaque result on (normal, muted) color profile.
+ RESULT_NORMAL_MUTED_OPAQUE,
+ // Transparent result on (light, muted) color profile.
+ RESULT_LIGHT_MUTED_TRANSPARENT,
+ // Opaque result on (light, muted) color profile.
+ RESULT_LIGHT_MUTED_OPAQUE,
+
+ NUM_COLOR_EXTRACTION_RESULTS,
+};
+
+} // namespace wallpaper
+
+#endif // COMPONENTS_WALLPAPER_WALLPAPER_COLOR_EXTRACTION_RESULT_H_
diff --git a/chromium/components/wallpaper/wallpaper_color_profile.h b/chromium/components/wallpaper/wallpaper_color_profile.h
new file mode 100644
index 00000000000..3112eecc6ba
--- /dev/null
+++ b/chromium/components/wallpaper/wallpaper_color_profile.h
@@ -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.
+
+#ifndef COMPONENTS_WALLPAPER_WALLPAPER_COLOR_PROFILE_H_
+#define COMPONENTS_WALLPAPER_WALLPAPER_COLOR_PROFILE_H_
+
+namespace wallpaper {
+
+// The color profile type, ordered as the color profiles applied in
+// ash::WallpaperController.
+enum class ColorProfileType {
+ DARK_VIBRANT = 0,
+ NORMAL_VIBRANT,
+ LIGHT_VIBRANT,
+ DARK_MUTED,
+ NORMAL_MUTED,
+ LIGHT_MUTED,
+
+ NUM_OF_COLOR_PROFILES,
+};
+
+} // namespace wallpaper
+
+#endif // COMPONENTS_WALLPAPER_WALLPAPER_COLOR_PROFILE_H_
diff --git a/chromium/components/wallpaper/wallpaper_info.h b/chromium/components/wallpaper/wallpaper_info.h
new file mode 100644
index 00000000000..d3238823af5
--- /dev/null
+++ b/chromium/components/wallpaper/wallpaper_info.h
@@ -0,0 +1,81 @@
+// 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_WALLPAPER_WALLPAPER_INFO_H_
+#define COMPONENTS_WALLPAPER_WALLPAPER_INFO_H_
+
+#include "base/time/time.h"
+#include "components/wallpaper/wallpaper_export.h"
+
+namespace wallpaper {
+
+// This enum is used to define the buckets for an enumerated UMA histogram.
+// Hence,
+// (a) existing enumerated constants should never be deleted or reordered,
+// (b) new constants should only be appended at the end of the enumeration.
+enum WallpaperLayout {
+ // Center the wallpaper on the desktop without scaling it. The wallpaper
+ // may be cropped.
+ WALLPAPER_LAYOUT_CENTER,
+ // Scale the wallpaper (while preserving its aspect ratio) to cover the
+ // desktop; the wallpaper may be cropped.
+ WALLPAPER_LAYOUT_CENTER_CROPPED,
+ // Scale the wallpaper (without preserving its aspect ratio) to match the
+ // desktop's size.
+ WALLPAPER_LAYOUT_STRETCH,
+ // Tile the wallpaper over the background without scaling it.
+ WALLPAPER_LAYOUT_TILE,
+ // This must remain last.
+ NUM_WALLPAPER_LAYOUT,
+};
+
+// This enum is used to define the buckets for an enumerated UMA histogram.
+// Hence,
+// (a) existing enumerated constants should never be deleted or reordered,
+// (b) new constants should only be appended at the end of the enumeration.
+enum WallpaperType {
+ DAILY = 0, // Surprise wallpaper. Changes once a day if enabled.
+ CUSTOMIZED = 1, // Selected by user.
+ DEFAULT = 2, // Default.
+ /* UNKNOWN = 3 */ // Removed.
+ ONLINE = 4, // WallpaperInfo.location denotes an URL.
+ POLICY = 5, // Controlled by policy, can't be changed by the user.
+ THIRDPARTY = 6, // Current wallpaper is set by a third party app.
+ DEVICE = 7, // Current wallpaper is the device policy controlled
+ // wallpaper. It shows on the login screen if the device
+ // is an enterprise managed device.
+ WALLPAPER_TYPE_COUNT = 8
+};
+
+struct WALLPAPER_EXPORT WallpaperInfo {
+ WallpaperInfo()
+ : layout(WALLPAPER_LAYOUT_CENTER), type(WALLPAPER_TYPE_COUNT) {}
+
+ WallpaperInfo(const std::string& in_location,
+ WallpaperLayout in_layout,
+ WallpaperType in_type,
+ const base::Time& in_date)
+ : location(in_location),
+ layout(in_layout),
+ type(in_type),
+ date(in_date) {}
+
+ ~WallpaperInfo() {}
+
+ bool operator==(const WallpaperInfo& other) {
+ return (location == other.location) && (layout == other.layout) &&
+ (type == other.type);
+ }
+
+ // Either file name of migrated wallpaper including first directory level
+ // (corresponding to user wallpaper_files_id) or online wallpaper URL.
+ std::string location;
+ WallpaperLayout layout;
+ WallpaperType type;
+ base::Time date;
+};
+
+} // namespace wallpaper
+
+#endif // COMPONENTS_WALLPAPER_WALLPAPER_INFO_H_
diff --git a/chromium/components/wallpaper/wallpaper_layout.h b/chromium/components/wallpaper/wallpaper_layout.h
deleted file mode 100644
index edcee323f5b..00000000000
--- a/chromium/components/wallpaper/wallpaper_layout.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_WALLPAPER_WALLPAPER_LAYOUT_H_
-#define COMPONENTS_WALLPAPER_WALLPAPER_LAYOUT_H_
-
-namespace wallpaper {
-
-// This enum is used to back a histogram, and should therefore be treated as
-// append-only.
-enum WallpaperLayout {
- // Center the wallpaper on the desktop without scaling it. The wallpaper
- // may be cropped.
- WALLPAPER_LAYOUT_CENTER,
- // Scale the wallpaper (while preserving its aspect ratio) to cover the
- // desktop; the wallpaper may be cropped.
- WALLPAPER_LAYOUT_CENTER_CROPPED,
- // Scale the wallpaper (without preserving its aspect ratio) to match the
- // desktop's size.
- WALLPAPER_LAYOUT_STRETCH,
- // Tile the wallpaper over the background without scaling it.
- WALLPAPER_LAYOUT_TILE,
- NUM_WALLPAPER_LAYOUT,
-};
-
-} // namespace wallpaper
-#endif // COMPONENTS_WALLPAPER_WALLPAPER_LAYOUT_H_
diff --git a/chromium/components/wallpaper/wallpaper_manager_base.cc b/chromium/components/wallpaper/wallpaper_manager_base.cc
index e72039f4af3..f048b0ca984 100644
--- a/chromium/components/wallpaper/wallpaper_manager_base.cc
+++ b/chromium/components/wallpaper/wallpaper_manager_base.cc
@@ -19,19 +19,13 @@
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
#include "base/task_scheduler/post_task.h"
-#include "base/time/time.h"
#include "base/values.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
-#include "components/signin/core/account_id/account_id.h"
-#include "components/user_manager/user.h"
-#include "components/user_manager/user_image/user_image.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.h"
#include "components/wallpaper/wallpaper_files_id.h"
-#include "components/wallpaper/wallpaper_layout.h"
-#include "components/wallpaper/wallpaper_manager_base.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/geometry/safe_integer_conversions.h"
@@ -92,12 +86,8 @@ void DeleteWallpaperInList(const std::vector<base::FilePath>& file_list) {
for (std::vector<base::FilePath>::const_iterator it = file_list.begin();
it != file_list.end(); ++it) {
base::FilePath path = *it;
- // Some users may still have legacy wallpapers with png extension. We need
- // to delete these wallpapers too.
- if (!base::DeleteFile(path, true) &&
- !base::DeleteFile(path.AddExtension(".png"), false)) {
+ if (!base::DeleteFile(path, true))
LOG(ERROR) << "Failed to remove user wallpaper at " << path.value();
- }
}
}
@@ -146,24 +136,6 @@ MovableOnDestroyCallback::~MovableOnDestroyCallback() {
callback_.Run();
}
-WallpaperInfo::WallpaperInfo()
- : layout(WALLPAPER_LAYOUT_CENTER),
- type(user_manager::User::WALLPAPER_TYPE_COUNT) {
-}
-
-WallpaperInfo::WallpaperInfo(const std::string& in_location,
- WallpaperLayout in_layout,
- user_manager::User::WallpaperType in_type,
- const base::Time& in_date)
- : location(in_location),
- layout(in_layout),
- type(in_type),
- date(in_date) {
-}
-
-WallpaperInfo::~WallpaperInfo() {
-}
-
void AssertCalledOnWallpaperSequence() {
#if DCHECK_IS_ON()
DCHECK(g_wallpaper_sequence_checker.Get().CalledOnValidSequence());
@@ -189,9 +161,6 @@ const int kWallpaperThumbnailHeight = 68;
const char kUsersWallpaperInfo[] = "user_wallpaper_info";
-const char kUserWallpapers[] = "UserWallpapers";
-const char kUserWallpapersProperties[] = "UserWallpapersProperties";
-
const base::FilePath&
WallpaperManagerBase::CustomizedWallpaperRescaledFiles::path_downloaded()
const {
@@ -314,15 +283,13 @@ base::FilePath WallpaperManagerBase::GetCustomWallpaperDir(
// static
void WallpaperManagerBase::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kUsersWallpaperInfo);
- registry->RegisterDictionaryPref(kUserWallpapers);
- registry->RegisterDictionaryPref(kUserWallpapersProperties);
}
void WallpaperManagerBase::EnsureLoggedInUserWallpaperLoaded() {
WallpaperInfo info;
if (GetLoggedInUserWallpaperInfo(&info)) {
UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type,
- user_manager::User::WALLPAPER_TYPE_COUNT);
+ WALLPAPER_TYPE_COUNT);
RecordWallpaperAppType();
if (info == current_user_wallpaper_info_)
return;
@@ -361,8 +328,7 @@ bool WallpaperManagerBase::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
info->location = current_user_wallpaper_info_.location = "";
info->layout = current_user_wallpaper_info_.layout =
WALLPAPER_LAYOUT_CENTER_CROPPED;
- info->type = current_user_wallpaper_info_.type =
- user_manager::User::DEFAULT;
+ info->type = current_user_wallpaper_info_.type = DEFAULT;
info->date = current_user_wallpaper_info_.date =
base::Time::Now().LocalMidnight();
return true;
@@ -377,8 +343,8 @@ void WallpaperManagerBase::SetDefaultWallpaper(const AccountId& account_id,
RemoveUserWallpaperInfo(account_id);
const wallpaper::WallpaperInfo info = {
- std::string(), wallpaper::WALLPAPER_LAYOUT_CENTER,
- user_manager::User::DEFAULT, base::Time::Now().LocalMidnight()};
+ std::string(), wallpaper::WALLPAPER_LAYOUT_CENTER, DEFAULT,
+ base::Time::Now().LocalMidnight()};
const bool is_persistent =
!user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
account_id);
@@ -431,11 +397,7 @@ bool WallpaperManagerBase::ResizeImage(
gfx::Size(resized_width, resized_height));
SkBitmap bitmap = *(resized_image.bitmap());
- gfx::JPEGCodec::Encode(
- reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
- gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(),
- bitmap.width() * bitmap.bytesPerPixel(), kDefaultEncodingQuality,
- &(*output)->data());
+ gfx::JPEGCodec::Encode(bitmap, kDefaultEncodingQuality, &(*output)->data());
if (output_skia) {
resized_image.MakeThreadSafe();
@@ -472,14 +434,14 @@ bool WallpaperManagerBase::IsPolicyControlled(
WallpaperInfo info;
if (!GetUserWallpaperInfo(account_id, &info))
return false;
- return info.type == user_manager::User::POLICY;
+ return info.type == POLICY;
}
void WallpaperManagerBase::OnPolicySet(const std::string& policy,
const AccountId& account_id) {
WallpaperInfo info;
GetUserWallpaperInfo(account_id, &info);
- info.type = user_manager::User::POLICY;
+ info.type = POLICY;
SetUserWallpaperInfo(account_id, info, true /* is_persistent */);
}
@@ -487,9 +449,13 @@ void WallpaperManagerBase::OnPolicyCleared(const std::string& policy,
const AccountId& account_id) {
WallpaperInfo info;
GetUserWallpaperInfo(account_id, &info);
- info.type = user_manager::User::DEFAULT;
+ info.type = DEFAULT;
SetUserWallpaperInfo(account_id, info, true /* is_persistent */);
- SetDefaultWallpaperNow(account_id);
+
+ // If we're at the login screen, do not change the wallpaper but defer it
+ // until the user logs in to the system.
+ if (user_manager::UserManager::Get()->IsUserLoggedIn())
+ SetDefaultWallpaperNow(account_id);
}
// static
@@ -600,8 +566,6 @@ void WallpaperManagerBase::GetCustomWallpaperInternal(
// Falls back to custom wallpaper that uses AccountId as part of its file
// path.
// Note that account id is used instead of wallpaper_files_id here.
- LOG(ERROR) << "Failed to load custom wallpaper from its original fallback "
- "file path: " << valid_path.value();
const std::string& old_path = account_id.GetUserEmail(); // Migrated
valid_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir,
WallpaperFilesId::FromString(old_path),
@@ -609,13 +573,11 @@ void WallpaperManagerBase::GetCustomWallpaperInternal(
}
if (!base::PathExists(valid_path)) {
- LOG(ERROR) << "Failed to load previously selected custom wallpaper. "
- << "Fallback to default wallpaper. Expected wallpaper path: "
- << wallpaper_path.value();
reply_task_runner->PostTask(
FROM_HERE,
- base::Bind(&WallpaperManagerBase::DoSetDefaultWallpaper, weak_ptr,
- account_id, base::Passed(std::move(on_finish))));
+ base::Bind(&WallpaperManagerBase::OnCustomWallpaperFileNotFound,
+ weak_ptr, account_id, wallpaper_path, update_wallpaper,
+ base::Passed(std::move(on_finish))));
} else {
reply_task_runner->PostTask(
FROM_HERE, base::Bind(&WallpaperManagerBase::StartLoad, weak_ptr,
@@ -628,7 +590,7 @@ void WallpaperManagerBase::InitInitialUserWallpaper(const AccountId& account_id,
bool is_persistent) {
current_user_wallpaper_info_.location = "";
current_user_wallpaper_info_.layout = WALLPAPER_LAYOUT_CENTER_CROPPED;
- current_user_wallpaper_info_.type = user_manager::User::DEFAULT;
+ current_user_wallpaper_info_.type = DEFAULT;
current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
std::string device_wallpaper_url;
@@ -637,7 +599,7 @@ void WallpaperManagerBase::InitInitialUserWallpaper(const AccountId& account_id,
&device_wallpaper_hash)) {
current_user_wallpaper_info_.location =
GetDeviceWallpaperFilePath().value();
- current_user_wallpaper_info_.type = user_manager::User::DEVICE;
+ current_user_wallpaper_info_.type = DEVICE;
}
WallpaperInfo info = current_user_wallpaper_info_;
@@ -732,11 +694,9 @@ void WallpaperManagerBase::CacheUserWallpaper(const AccountId& account_id) {
base::FilePath wallpaper_dir;
base::FilePath wallpaper_path;
- if (info.type == user_manager::User::CUSTOMIZED ||
- info.type == user_manager::User::POLICY ||
- info.type == user_manager::User::DEVICE) {
+ if (info.type == CUSTOMIZED || info.type == POLICY || info.type == DEVICE) {
base::FilePath wallpaper_path;
- if (info.type == user_manager::User::DEVICE) {
+ if (info.type == DEVICE) {
wallpaper_path = GetDeviceWallpaperFilePath();
} else {
const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
@@ -760,34 +720,30 @@ void WallpaperManagerBase::CacheUserWallpaper(const AccountId& account_id) {
}
}
-void WallpaperManagerBase::DeleteUserWallpapers(
- const AccountId& account_id,
- const std::string& path_to_file) {
+void WallpaperManagerBase::DeleteUserWallpapers(const AccountId& account_id) {
+ // System salt might not be ready in tests. Thus we don't have a valid
+ // wallpaper files id here.
+ if (!CanGetWallpaperFilesId())
+ return;
+
std::vector<base::FilePath> file_to_remove;
- // Remove small user wallpaper.
+ wallpaper::WallpaperFilesId wallpaper_files_id = GetFilesId(account_id);
+
+ // Remove small user wallpapers.
base::FilePath wallpaper_path = GetCustomWallpaperDir(kSmallWallpaperSubDir);
- // Remove old directory if exists
- file_to_remove.push_back(wallpaper_path.Append(account_id.GetUserEmail()));
- wallpaper_path = wallpaper_path.Append(path_to_file).DirName();
- file_to_remove.push_back(wallpaper_path);
+ file_to_remove.push_back(wallpaper_path.Append(wallpaper_files_id.id()));
- // Remove large user wallpaper.
+ // Remove large user wallpapers.
wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir);
- file_to_remove.push_back(wallpaper_path.Append(account_id.GetUserEmail()));
- wallpaper_path = wallpaper_path.Append(path_to_file);
- file_to_remove.push_back(wallpaper_path);
+ file_to_remove.push_back(wallpaper_path.Append(wallpaper_files_id.id()));
- // Remove user wallpaper thumbnail.
+ // Remove user wallpaper thumbnails.
wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
- file_to_remove.push_back(wallpaper_path.Append(account_id.GetUserEmail()));
- wallpaper_path = wallpaper_path.Append(path_to_file);
- file_to_remove.push_back(wallpaper_path);
+ file_to_remove.push_back(wallpaper_path.Append(wallpaper_files_id.id()));
- // Remove original user wallpaper.
+ // Remove original user wallpapers.
wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
- file_to_remove.push_back(wallpaper_path.Append(account_id.GetUserEmail()));
- wallpaper_path = wallpaper_path.Append(path_to_file);
- file_to_remove.push_back(wallpaper_path);
+ file_to_remove.push_back(wallpaper_path.Append(wallpaper_files_id.id()));
base::PostTaskWithTraits(FROM_HERE,
{base::MayBlock(), base::TaskPriority::BACKGROUND,
@@ -817,8 +773,7 @@ void WallpaperManagerBase::LoadWallpaper(
base::FilePath wallpaper_path;
// Do a sanity check that file path information is not empty.
- if (info.type == user_manager::User::ONLINE ||
- info.type == user_manager::User::DEFAULT) {
+ if (info.type == ONLINE || info.type == DEFAULT) {
if (info.location.empty()) {
if (base::SysInfo::IsRunningOnChromeOS()) {
NOTREACHED() << "User wallpaper info appears to be broken: "
@@ -836,7 +791,7 @@ void WallpaperManagerBase::LoadWallpaper(
}
}
- if (info.type == user_manager::User::ONLINE) {
+ if (info.type == ONLINE) {
std::string file_name = GURL(info.location).ExtractFileName();
WallpaperResolution resolution = GetAppropriateResolution();
// Only solid color wallpapers have stretch layout and they have only one
@@ -863,7 +818,7 @@ void WallpaperManagerBase::LoadWallpaper(
loaded_wallpapers_for_test_++;
StartLoad(account_id, info, update_wallpaper, wallpaper_path,
std::move(on_finish));
- } else if (info.type == user_manager::User::DEFAULT) {
+ } else if (info.type == DEFAULT) {
// Default wallpapers are migrated from M21 user profiles. A code refactor
// overlooked that case and caused these wallpapers not being loaded at all.
// On some slow devices, it caused login webui not visible after upgrade to
@@ -878,7 +833,7 @@ void WallpaperManagerBase::LoadWallpaper(
// In unexpected cases, revert to default wallpaper to fail safely. See
// crosbug.com/38429.
LOG(ERROR) << "Wallpaper reverts to default unexpected.";
- DoSetDefaultWallpaper(account_id, std::move(on_finish));
+ DoSetDefaultWallpaper(account_id, update_wallpaper, std::move(on_finish));
}
}
@@ -887,7 +842,7 @@ void WallpaperManagerBase::MoveCustomWallpapersSuccess(
const wallpaper::WallpaperFilesId& wallpaper_files_id) {
WallpaperInfo info;
GetUserWallpaperInfo(account_id, &info);
- if (info.type == user_manager::User::CUSTOMIZED) {
+ if (info.type == CUSTOMIZED) {
// New file field should include user wallpaper_files_id in addition to
// file name. This is needed because at login screen, wallpaper_files_id
// is not available.
@@ -1057,4 +1012,25 @@ void WallpaperManagerBase::CreateSolidDefaultWallpaper() {
default_wallpaper_image_.reset(new user_manager::UserImage(image));
}
+void WallpaperManagerBase::OnCustomWallpaperFileNotFound(
+ const AccountId& account_id,
+ const base::FilePath& expected_path,
+ bool update_wallpaper,
+ MovableOnDestroyCallbackHolder on_finish) {
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+ const user_manager::User* user = user_manager->FindUser(account_id);
+ LOG(ERROR) << "Failed to load previously selected custom wallpaper. "
+ << "Fallback to default wallpaper. Expected wallpaper path: "
+ << expected_path.value() << ". Number of users on the device: "
+ << user_manager->GetUsers().size()
+ << ", Number of logged in users on the device: "
+ << user_manager->GetLoggedInUsers().size()
+ << ". Current user type: " << user->GetType()
+ << ", IsActiveUser=" << (user_manager->GetActiveUser() == user)
+ << ", IsPrimaryUser=" << (user_manager->GetPrimaryUser() == user)
+ << ".";
+
+ DoSetDefaultWallpaper(account_id, update_wallpaper, std::move(on_finish));
+}
+
} // namespace wallpaper
diff --git a/chromium/components/wallpaper/wallpaper_manager_base.h b/chromium/components/wallpaper/wallpaper_manager_base.h
index 88601cc801e..7fae834761a 100644
--- a/chromium/components/wallpaper/wallpaper_manager_base.h
+++ b/chromium/components/wallpaper/wallpaper_manager_base.h
@@ -21,13 +21,11 @@
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/signin/core/account_id/account_id.h"
-#include "components/user_manager/user.h"
#include "components/user_manager/user_image/user_image.h"
#include "components/wallpaper/wallpaper_export.h"
-#include "components/wallpaper/wallpaper_layout.h"
+#include "components/wallpaper/wallpaper_info.h"
#include "ui/gfx/image/image_skia.h"
class PrefRegistrySimple;
@@ -38,7 +36,6 @@ class SequencedTaskRunner;
}
namespace user_manager {
-class User;
class UserImage;
}
@@ -60,26 +57,6 @@ class WALLPAPER_EXPORT MovableOnDestroyCallback {
using MovableOnDestroyCallbackHolder =
std::unique_ptr<MovableOnDestroyCallback>;
-struct WALLPAPER_EXPORT WallpaperInfo {
- WallpaperInfo();
- WallpaperInfo(const std::string& in_location,
- WallpaperLayout in_layout,
- user_manager::User::WallpaperType in_type,
- const base::Time& in_date);
- ~WallpaperInfo();
-
- // Either file name of migrated wallpaper including first directory level
- // (corresponding to user wallpaper_files_id) or online wallpaper URL.
- std::string location;
- WallpaperLayout layout;
- user_manager::User::WallpaperType type;
- base::Time date;
- bool operator==(const WallpaperInfo& other) {
- return (location == other.location) && (layout == other.layout) &&
- (type == other.type);
- }
-};
-
// Asserts that the current task is sequenced with any other task that calls
// this.
void WALLPAPER_EXPORT AssertCalledOnWallpaperSequence();
@@ -114,16 +91,9 @@ WALLPAPER_EXPORT extern const int kLargeWallpaperMaxHeight;
WALLPAPER_EXPORT extern const int kWallpaperThumbnailWidth;
WALLPAPER_EXPORT extern const int kWallpaperThumbnailHeight;
-// A dictionary that maps usernames to wallpaper properties.
+// A dictionary pref that maps usernames to wallpaper info.
WALLPAPER_EXPORT extern const char kUsersWallpaperInfo[];
-// A dictionary pref that maps usernames to file paths to their wallpapers.
-// Deprecated. Will remove this const char after done migration.
-WALLPAPER_EXPORT extern const char kUserWallpapers[];
-
-// A dictionary pref that maps usernames to wallpaper properties.
-WALLPAPER_EXPORT extern const char kUserWallpapersProperties[];
-
class WallpaperFilesId;
// This singleton class maintains wallpapers for users.
@@ -194,8 +164,11 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
class Observer {
public:
virtual ~Observer() {}
- virtual void OnWallpaperAnimationFinished(const AccountId& account_id) = 0;
+ // Notified when the wallpaper animation finishes.
+ virtual void OnWallpaperAnimationFinished(const AccountId& account_id) {}
+ // Notified when the wallpaper is updated.
virtual void OnUpdateWallpaperForTesting() {}
+ // Notified when the wallpaper pending list is empty.
virtual void OnPendingListEmptyForTesting() {}
};
@@ -272,7 +245,7 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
const WallpaperFilesId& wallpaper_files_id,
const std::string& file,
WallpaperLayout layout,
- user_manager::User::WallpaperType type,
+ WallpaperType type,
const gfx::ImageSkia& image,
bool update_wallpaper) = 0;
@@ -447,8 +420,7 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
virtual void ClearDisposableWallpaperCache();
// Deletes all |account_id| related custom wallpapers and directories.
- virtual void DeleteUserWallpapers(const AccountId& account_id,
- const std::string& path_to_file);
+ virtual void DeleteUserWallpapers(const AccountId& account_id);
// Gets the CommandLine representing the current process's command line.
virtual base::CommandLine* GetCommandLine();
@@ -498,7 +470,7 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
// because that's the callback interface provided by UserImageLoader.)
virtual void OnWallpaperDecoded(
const AccountId& account_id,
- WallpaperLayout layout,
+ const WallpaperInfo& info,
bool update_wallpaper,
MovableOnDestroyCallbackHolder on_finish,
std::unique_ptr<user_manager::UserImage> user_image) = 0;
@@ -507,9 +479,11 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
virtual void ScheduleSetUserWallpaper(const AccountId& account_id,
bool delayed) = 0;
- // Sets wallpaper to default.
+ // Sets wallpaper to default if |update_wallpaper| is true. Otherwise just
+ // load defaut wallpaper to cache.
virtual void DoSetDefaultWallpaper(
const AccountId& account_id,
+ bool update_wallpaper,
MovableOnDestroyCallbackHolder on_finish) = 0;
// Starts to load wallpaper at |wallpaper_path|. If |wallpaper_path| is
@@ -559,38 +533,47 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
virtual void SetDefaultWallpaperPathsFromCommandLine(
base::CommandLine* command_line) = 0;
- // Sets wallpaper to decoded default.
+ // Sets wallpaper to decoded default if |update_wallpaper| is true.
virtual void OnDefaultWallpaperDecoded(
const base::FilePath& path,
const WallpaperLayout layout,
+ bool update_wallpaper,
std::unique_ptr<user_manager::UserImage>* result,
MovableOnDestroyCallbackHolder on_finish,
std::unique_ptr<user_manager::UserImage> user_image) = 0;
- // Start decoding given default wallpaper.
+ // Start decoding given default wallpaper and set it as wallpaper if
+ // |update_wallpaper| is true.
virtual void StartLoadAndSetDefaultWallpaper(
const base::FilePath& path,
const WallpaperLayout layout,
+ bool update_wallpaper,
MovableOnDestroyCallbackHolder on_finish,
std::unique_ptr<user_manager::UserImage>* result_out) = 0;
// Record the Wallpaper App that the user is using right now on Chrome OS.
virtual void RecordWallpaperAppType() = 0;
+ // Returns true if wallpaper files id can be returned successfully.
+ virtual bool CanGetWallpaperFilesId() const = 0;
+
// Returns wallpaper subdirectory name for current resolution.
virtual const char* GetCustomWallpaperSubdirForCurrentResolution();
// Init default_wallpaper_image_ with 1x1 image of default color.
virtual void CreateSolidDefaultWallpaper();
+ // Callback function for WallpaperManagerBase::GetCustomWallpaperInternal().
+ void OnCustomWallpaperFileNotFound(const AccountId& account_id,
+ const base::FilePath& expected_path,
+ bool update_wallpaper,
+ MovableOnDestroyCallbackHolder on_finish);
+
// The number of loaded wallpapers.
int loaded_wallpapers_for_test_;
base::ThreadChecker thread_checker_;
- // Sequence token associated with wallpaper operations.
- base::SequencedWorkerPool::SequenceToken sequence_token_;
-
// Wallpaper sequenced task runner.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chromium/components/wallpaper/wallpaper_resizer.cc b/chromium/components/wallpaper/wallpaper_resizer.cc
index 0bfa3d4648c..bd215df5234 100644
--- a/chromium/components/wallpaper/wallpaper_resizer.cc
+++ b/chromium/components/wallpaper/wallpaper_resizer.cc
@@ -34,7 +34,7 @@ void Resize(const gfx::ImageSkia image,
WallpaperLayout layout,
SkBitmap* resized_bitmap_out,
base::TaskRunner* task_runner) {
- DCHECK(task_runner->RunsTasksOnCurrentThread());
+ DCHECK(task_runner->RunsTasksInCurrentSequence());
SkBitmap orig_bitmap = *image.bitmap();
SkBitmap new_bitmap = orig_bitmap;
diff --git a/chromium/components/wallpaper/wallpaper_resizer.h b/chromium/components/wallpaper/wallpaper_resizer.h
index ee96fc403ad..220ed18b708 100644
--- a/chromium/components/wallpaper/wallpaper_resizer.h
+++ b/chromium/components/wallpaper/wallpaper_resizer.h
@@ -12,7 +12,7 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
-#include "components/wallpaper/wallpaper_layout.h"
+#include "components/wallpaper/wallpaper_info.h"
#include "components/wallpaper/wallpaper_resizer_observer.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
diff --git a/chromium/components/web_cache/renderer/web_cache_impl.cc b/chromium/components/web_cache/renderer/web_cache_impl.cc
index e955bba45fb..23a815466b2 100644
--- a/chromium/components/web_cache/renderer/web_cache_impl.cc
+++ b/chromium/components/web_cache/renderer/web_cache_impl.cc
@@ -32,9 +32,7 @@ WebCacheImpl::WebCacheImpl() : clear_cache_state_(kInit) {
WebCacheImpl::~WebCacheImpl() {}
-void WebCacheImpl::BindRequest(
- const service_manager::BindSourceInfo& source_info,
- mojom::WebCacheRequest web_cache_request) {
+void WebCacheImpl::BindRequest(mojom::WebCacheRequest web_cache_request) {
bindings_.AddBinding(this, std::move(web_cache_request));
}
diff --git a/chromium/components/web_cache/renderer/web_cache_impl.h b/chromium/components/web_cache/renderer/web_cache_impl.h
index 5697cebe913..4a8a44f08c2 100644
--- a/chromium/components/web_cache/renderer/web_cache_impl.h
+++ b/chromium/components/web_cache/renderer/web_cache_impl.h
@@ -13,10 +13,6 @@
#include "components/web_cache/public/interfaces/web_cache.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
-namespace service_manager {
-struct BindSourceInfo;
-}
-
namespace web_cache {
// This class implements the Mojo interface mojom::WebCache.
@@ -25,8 +21,7 @@ class WebCacheImpl : public mojom::WebCache {
WebCacheImpl();
~WebCacheImpl() override;
- void BindRequest(const service_manager::BindSourceInfo& source_info,
- mojom::WebCacheRequest web_cache_request);
+ void BindRequest(mojom::WebCacheRequest web_cache_request);
// Needs to be called by RenderViews in case of navigations to execute
// any 'clear cache' commands that were delayed until the next navigation.
diff --git a/chromium/components/web_contents_delegate_android/BUILD.gn b/chromium/components/web_contents_delegate_android/BUILD.gn
index d215023c7a8..988af1004e9 100644
--- a/chromium/components/web_contents_delegate_android/BUILD.gn
+++ b/chromium/components/web_contents_delegate_android/BUILD.gn
@@ -8,8 +8,6 @@ static_library("web_contents_delegate_android") {
sources = [
"color_chooser_android.cc",
"color_chooser_android.h",
- "component_jni_registrar.cc",
- "component_jni_registrar.h",
"validation_message_bubble_android.cc",
"validation_message_bubble_android.h",
"web_contents_delegate_android.cc",
diff --git a/chromium/components/web_contents_delegate_android/DEPS b/chromium/components/web_contents_delegate_android/DEPS
index 0cbb2748a7c..560840d67c2 100644
--- a/chromium/components/web_contents_delegate_android/DEPS
+++ b/chromium/components/web_contents_delegate_android/DEPS
@@ -2,7 +2,7 @@ include_rules = [
"+content/public/browser",
"+content/public/common",
"+jni",
- "+ui/android/window_android.h",
+ "+ui/android",
"+ui/base",
"+ui/gfx",
]
diff --git a/chromium/components/web_contents_delegate_android/color_chooser_android.cc b/chromium/components/web_contents_delegate_android/color_chooser_android.cc
index 5aeb8ee8aca..4c31f395c2a 100644
--- a/chromium/components/web_contents_delegate_android/color_chooser_android.cc
+++ b/chromium/components/web_contents_delegate_android/color_chooser_android.cc
@@ -8,7 +8,6 @@
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
-#include "content/public/browser/android/content_view_core.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/color_suggestion.h"
#include "jni/ColorChooserAndroid_jni.h"
@@ -16,7 +15,6 @@
using base::android::ConvertUTF16ToJavaString;
using base::android::JavaRef;
-using content::ContentViewCore;
namespace web_contents_delegate_android {
@@ -41,17 +39,14 @@ ColorChooserAndroid::ColorChooserAndroid(
}
}
- ContentViewCore* content_view_core =
- ContentViewCore::FromWebContents(web_contents);
- if (content_view_core) {
- base::android::ScopedJavaLocalRef<jobject> java_content_view_core =
- content_view_core->GetJavaObject();
- if (!java_content_view_core.is_null()) {
- j_color_chooser_.Reset(Java_ColorChooserAndroid_createColorChooserAndroid(
- env, reinterpret_cast<intptr_t>(this), java_content_view_core,
- initial_color, suggestions_array));
- }
+ auto* window_android = web_contents->GetNativeView()->GetWindowAndroid();
+ if (window_android) {
+ j_color_chooser_.Reset(Java_ColorChooserAndroid_createColorChooserAndroid(
+ env, reinterpret_cast<intptr_t>(this), window_android->GetJavaObject(),
+ initial_color, suggestions_array));
}
+
+ // Ends with the initial color if color chooser dialog failed.
if (j_color_chooser_.is_null())
OnColorChosen(env, j_color_chooser_, initial_color);
}
@@ -79,11 +74,4 @@ void ColorChooserAndroid::OnColorChosen(JNIEnv* env,
web_contents_->DidEndColorChooser();
}
-// ----------------------------------------------------------------------------
-// Native JNI methods
-// ----------------------------------------------------------------------------
-bool RegisterColorChooserAndroid(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
} // namespace web_contents_delegate_android
diff --git a/chromium/components/web_contents_delegate_android/color_chooser_android.h b/chromium/components/web_contents_delegate_android/color_chooser_android.h
index ebc4c7c9861..f3a6e99d3ba 100644
--- a/chromium/components/web_contents_delegate_android/color_chooser_android.h
+++ b/chromium/components/web_contents_delegate_android/color_chooser_android.h
@@ -49,9 +49,6 @@ class ColorChooserAndroid : public content::ColorChooser {
DISALLOW_COPY_AND_ASSIGN(ColorChooserAndroid);
};
-// Native JNI methods
-bool RegisterColorChooserAndroid(JNIEnv* env);
-
} // namespace web_contents_delegate_android
#endif // COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_COLOR_CHOOSER_ANDROID_H_
diff --git a/chromium/components/web_contents_delegate_android/validation_message_bubble_android.cc b/chromium/components/web_contents_delegate_android/validation_message_bubble_android.cc
index e2ee3a1fd69..2f15a5e5c68 100644
--- a/chromium/components/web_contents_delegate_android/validation_message_bubble_android.cc
+++ b/chromium/components/web_contents_delegate_android/validation_message_bubble_android.cc
@@ -5,47 +5,37 @@
#include "components/web_contents_delegate_android/validation_message_bubble_android.h"
#include "base/android/jni_string.h"
-#include "content/public/browser/android/content_view_core.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
#include "jni/ValidationMessageBubble_jni.h"
-#include "ui/gfx/geometry/rect.h"
+#include "ui/android/view_android.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size_conversions.h"
using base::android::ConvertUTF16ToJavaString;
-using content::ContentViewCore;
-using content::RenderWidgetHost;
namespace {
-base::android::ScopedJavaLocalRef<jobject> GetJavaContentViewCoreFrom(
- RenderWidgetHost* widget_host) {
- ContentViewCore* content_view_core =
- ContentViewCore::FromWebContents(content::WebContents::FromRenderViewHost(
- content::RenderViewHost::From(widget_host)));
- if (!content_view_core)
- return base::android::ScopedJavaLocalRef<jobject>();
- return content_view_core->GetJavaObject();
+gfx::Rect ScaleToRoundedRect(const gfx::Rect& rect, float scale) {
+ gfx::RectF scaledRect(rect);
+ scaledRect.Scale(scale);
+ return ToNearestRect(scaledRect);
}
+
+gfx::Size ScaleToRoundedSize(const gfx::SizeF& size, float scale) {
+ return gfx::ToRoundedSize(gfx::ScaleSize(size, scale));
}
+} // namespace
namespace web_contents_delegate_android {
ValidationMessageBubbleAndroid::ValidationMessageBubbleAndroid(
- RenderWidgetHost* widget_host,
- const gfx::Rect& anchor_in_root_view,
+ gfx::NativeView view,
const base::string16& main_text,
const base::string16& sub_text) {
- base::android::ScopedJavaLocalRef<jobject> java_content_view_core =
- GetJavaContentViewCoreFrom(widget_host);
- if (java_content_view_core.is_null())
- return;
-
JNIEnv* env = base::android::AttachCurrentThread();
java_validation_message_bubble_.Reset(
- Java_ValidationMessageBubble_createAndShowIfApplicable(
- env, java_content_view_core, anchor_in_root_view.x(),
- anchor_in_root_view.y(), anchor_in_root_view.width(),
- anchor_in_root_view.height(),
+ Java_ValidationMessageBubble_createIfApplicable(
+ env, view->GetContainerView(),
ConvertUTF16ToJavaString(env, main_text),
ConvertUTF16ToJavaString(env, sub_text)));
}
@@ -57,19 +47,22 @@ ValidationMessageBubbleAndroid::~ValidationMessageBubbleAndroid() {
}
}
-void ValidationMessageBubbleAndroid::SetPositionRelativeToAnchor(
- RenderWidgetHost* widget_host, const gfx::Rect& anchor_in_root_view) {
- base::android::ScopedJavaLocalRef<jobject> java_content_view_core =
- GetJavaContentViewCoreFrom(widget_host);
- if (java_content_view_core.is_null() ||
- java_validation_message_bubble_.is_null()) {
+void ValidationMessageBubbleAndroid::ShowAtPositionRelativeToAnchor(
+ gfx::NativeView view,
+ const gfx::Rect& anchor_in_screen) {
+ if (java_validation_message_bubble_.is_null())
return;
- }
- Java_ValidationMessageBubble_setPositionRelativeToAnchor(
- base::android::AttachCurrentThread(), java_validation_message_bubble_,
- java_content_view_core, anchor_in_root_view.x(), anchor_in_root_view.y(),
- anchor_in_root_view.width(), anchor_in_root_view.height());
+ // Convert to physical unit before passing to Java.
+ float scale = view->GetDipScale() * view->page_scale();
+ gfx::Rect anchor = ScaleToRoundedRect(anchor_in_screen, scale);
+ gfx::Size viewport = ScaleToRoundedSize(view->viewport_size(), scale);
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_ValidationMessageBubble_showAtPositionRelativeToAnchor(
+ env, java_validation_message_bubble_, viewport.width(), viewport.height(),
+ view->content_offset() * scale, anchor.x(), anchor.y(), anchor.width(),
+ anchor.height());
}
} // namespace web_contents_delegate_android
diff --git a/chromium/components/web_contents_delegate_android/validation_message_bubble_android.h b/chromium/components/web_contents_delegate_android/validation_message_bubble_android.h
index 1fe027b1850..08fa95de7b8 100644
--- a/chromium/components/web_contents_delegate_android/validation_message_bubble_android.h
+++ b/chromium/components/web_contents_delegate_android/validation_message_bubble_android.h
@@ -9,28 +9,24 @@
#include "base/android/scoped_java_ref.h"
#include "base/strings/string16.h"
+#include "ui/gfx/native_widget_types.h"
namespace gfx {
class Rect;
}
-namespace content {
-class RenderWidgetHost;
-}
-
namespace web_contents_delegate_android {
// An implementation of ValidationMessageBubble for Android. This class is a
// bridge to a Java implementation.
class ValidationMessageBubbleAndroid {
public:
- ValidationMessageBubbleAndroid(content::RenderWidgetHost* widget_host,
- const gfx::Rect& anchor_in_screen,
+ ValidationMessageBubbleAndroid(gfx::NativeView view,
const base::string16& main_text,
const base::string16& sub_text);
virtual ~ValidationMessageBubbleAndroid();
- virtual void SetPositionRelativeToAnchor(
- content::RenderWidgetHost* widget_host,
+ virtual void ShowAtPositionRelativeToAnchor(
+ gfx::NativeView view,
const gfx::Rect& anchor_in_screen);
private:
diff --git a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
index 731e90068e1..df91a2aecc4 100644
--- a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
+++ b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
@@ -11,7 +11,6 @@
#include "base/android/jni_string.h"
#include "components/web_contents_delegate_android/color_chooser_android.h"
#include "components/web_contents_delegate_android/validation_message_bubble_android.h"
-#include "content/public/browser/android/content_view_core.h"
#include "content/public/browser/color_chooser.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/invalidate_type.h"
@@ -110,6 +109,7 @@ WebContents* WebContentsDelegateAndroid::OpenURLFromTab(
load_params.should_replace_current_entry =
params.should_replace_current_entry;
load_params.is_renderer_initiated = params.is_renderer_initiated;
+ load_params.has_user_gesture = params.user_gesture;
if (params.uses_post) {
load_params.load_type = content::NavigationController::LOAD_TYPE_HTTP_POST;
@@ -185,6 +185,7 @@ void WebContentsDelegateAndroid::RendererResponsive(WebContents* source) {
bool WebContentsDelegateAndroid::ShouldCreateWebContents(
content::WebContents* web_contents,
+ content::RenderFrameHost* opener,
content::SiteInstance* source_site_instance,
int32_t route_id,
int32_t main_frame_route_id,
@@ -205,22 +206,13 @@ bool WebContentsDelegateAndroid::ShouldCreateWebContents(
java_url);
}
-bool WebContentsDelegateAndroid::OnGoToEntryOffset(int offset) {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
- if (obj.is_null())
- return true;
- return Java_WebContentsDelegateAndroid_onGoToEntryOffset(env, obj, offset);
-}
-
void WebContentsDelegateAndroid::WebContentsCreated(
WebContents* source_contents,
int opener_render_process_id,
int opener_render_frame_id,
const std::string& frame_name,
const GURL& target_url,
- WebContents* new_contents,
- const base::Optional<content::WebContents::CreateParams>& create_params) {
+ WebContents* new_contents) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
if (obj.is_null())
@@ -389,14 +381,9 @@ void WebContentsDelegateAndroid::ShowValidationMessage(
const gfx::Rect& anchor_in_root_view,
const base::string16& main_text,
const base::string16& sub_text) {
- RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
- if (rwhv) {
- validation_message_bubble_.reset(
- new ValidationMessageBubbleAndroid(rwhv->GetRenderWidgetHost(),
- anchor_in_root_view,
- main_text,
- sub_text));
- }
+ validation_message_bubble_.reset(new ValidationMessageBubbleAndroid(
+ web_contents->GetNativeView(), main_text, sub_text));
+ MoveValidationMessage(web_contents, anchor_in_root_view);
}
void WebContentsDelegateAndroid::HideValidationMessage(
@@ -409,11 +396,8 @@ void WebContentsDelegateAndroid::MoveValidationMessage(
const gfx::Rect& anchor_in_root_view) {
if (!validation_message_bubble_)
return;
- RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
- if (rwhv) {
- validation_message_bubble_->SetPositionRelativeToAnchor(
- rwhv->GetRenderWidgetHost(), anchor_in_root_view);
- }
+ validation_message_bubble_->ShowAtPositionRelativeToAnchor(
+ web_contents->GetNativeView(), anchor_in_root_view);
}
void WebContentsDelegateAndroid::RequestAppBannerFromDevTools(
diff --git a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
index 4b179ac46f6..e2b2c1ec352 100644
--- a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
+++ b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
@@ -12,12 +12,12 @@
#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/compiler_specific.h"
-#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
class GURL;
namespace content {
+class WebContents;
class WebContentsDelegate;
struct NativeWebKeyboardEvent;
struct OpenURLParams;
@@ -73,17 +73,15 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
content::WebContents* source,
const content::WebContentsUnresponsiveState& unresponsive_state) override;
void RendererResponsive(content::WebContents* source) override;
- void WebContentsCreated(
- content::WebContents* source_contents,
- int opener_render_process_id,
- int opener_render_frame_id,
- const std::string& frame_name,
- const GURL& target_url,
- content::WebContents* new_contents,
- const base::Optional<content::WebContents::CreateParams>& create_params)
- override;
+ void WebContentsCreated(content::WebContents* source_contents,
+ int opener_render_process_id,
+ int opener_render_frame_id,
+ const std::string& frame_name,
+ const GURL& target_url,
+ content::WebContents* new_contents) override;
bool ShouldCreateWebContents(
content::WebContents* web_contents,
+ content::RenderFrameHost* opener,
content::SiteInstance* source_site_instance,
int32_t route_id,
int32_t main_frame_route_id,
@@ -94,7 +92,6 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
const GURL& target_url,
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) override;
- bool OnGoToEntryOffset(int offset) override;
void CloseContents(content::WebContents* source) override;
void MoveContents(content::WebContents* source,
const gfx::Rect& pos) override;
diff --git a/chromium/components/web_resource/OWNERS b/chromium/components/web_resource/OWNERS
index ed078381545..0173e252934 100644
--- a/chromium/components/web_resource/OWNERS
+++ b/chromium/components/web_resource/OWNERS
@@ -1,5 +1,4 @@
achuith@chromium.org
-dbeam@chromium.org
rsesek@chromium.org
# For ResourceRequestAllowedNotifier and EulaAcceptedNotifier:
diff --git a/chromium/components/web_resource/web_resource_service_unittest.cc b/chromium/components/web_resource/web_resource_service_unittest.cc
index b8758105d3f..79d08c226e8 100644
--- a/chromium/components/web_resource/web_resource_service_unittest.cc
+++ b/chromium/components/web_resource/web_resource_service_unittest.cc
@@ -73,7 +73,7 @@ class TestWebResourceService : public WebResourceService {
parse_json_callback,
TRAFFIC_ANNOTATION_FOR_TESTS){};
- void Unpack(const base::DictionaryValue& parsed_json) override{};
+ void Unpack(const base::DictionaryValue& parsed_json) override {}
};
class WebResourceServiceTest : public testing::Test {
diff --git a/chromium/components/web_restrictions/BUILD.gn b/chromium/components/web_restrictions/BUILD.gn
index 89c8b33d7de..80ef8d189da 100644
--- a/chromium/components/web_restrictions/BUILD.gn
+++ b/chromium/components/web_restrictions/BUILD.gn
@@ -7,9 +7,12 @@ import("//mojo/public/tools/bindings/mojom.gni")
import("//testing/test.gni")
if (is_android) {
- android_library("web_restrictions_java") {
+ android_library("provider_java") {
+ java_files = [ "browser/java/src/org/chromium/components/webrestrictions/browser/WebRestrictionsContentProvider.java" ]
+ }
+
+ android_library("client_java") {
java_files = [
- "browser/java/src/org/chromium/components/webrestrictions/browser/WebRestrictionsContentProvider.java",
"browser/java/src/org/chromium/components/webrestrictions/browser/WebRestrictionsClient.java",
"browser/java/src/org/chromium/components/webrestrictions/browser/WebRestrictionsClientResult.java",
]
@@ -24,7 +27,8 @@ if (is_android) {
"browser/junit/src/org/chromium/components/webrestrictions/browser/WebRestrictionsClientTest.java",
]
deps = [
- ":web_restrictions_java",
+ ":client_java",
+ ":provider_java",
"//base:base_java",
"//third_party/hamcrest:hamcrest_java",
]
@@ -71,14 +75,14 @@ if (is_android) {
]
jni_package = "web_restrictions"
deps = [
- ":web_restrictions_java",
+ ":client_java",
]
}
android_library("test_support_java") {
java_files = [ "browser/javatest/src/org/chromium/components/webrestrictions/browser/MockWebRestrictionsClient.java" ]
deps = [
- ":web_restrictions_java",
+ ":client_java",
"//base:base_java",
]
}
diff --git a/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc b/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc
index 349b3d1d3d2..a30f427305a 100644
--- a/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc
+++ b/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc
@@ -31,7 +31,6 @@ WebRestrictionsMojoImplementation::~WebRestrictionsMojoImplementation() {}
void WebRestrictionsMojoImplementation::Create(
WebRestrictionsClient* client,
- const service_manager::BindSourceInfo& source_info,
mojom::WebRestrictionsRequest request) {
mojo::MakeStrongBinding(
base::MakeUnique<WebRestrictionsMojoImplementation>(client),
diff --git a/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.h b/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.h
index a5dbc3f823d..72d5bd7a684 100644
--- a/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.h
+++ b/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.h
@@ -10,10 +10,6 @@
#include "base/macros.h"
#include "components/web_restrictions/interfaces/web_restrictions.mojom.h"
-namespace service_manager {
-struct BindSourceInfo;
-}
-
namespace web_restrictions {
class WebRestrictionsClient;
@@ -24,7 +20,6 @@ class WebRestrictionsMojoImplementation : public mojom::WebRestrictions {
~WebRestrictionsMojoImplementation() override;
static void Create(WebRestrictionsClient* client,
- const service_manager::BindSourceInfo& source_info,
mojom::WebRestrictionsRequest request);
private:
diff --git a/chromium/components/webauth/BUILD.gn b/chromium/components/webauth/BUILD.gn
deleted file mode 100644
index 80da9afb1b3..00000000000
--- a/chromium/components/webauth/BUILD.gn
+++ /dev/null
@@ -1,15 +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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("authenticator") {
- sources = [
- "authenticator.mojom",
- ]
-
- public_deps = [
- "//mojo/common:common_custom_types",
- ]
-}
diff --git a/chromium/components/webauth/OWNERS b/chromium/components/webauth/OWNERS
deleted file mode 100644
index 08850f42120..00000000000
--- a/chromium/components/webauth/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/webauth/authenticator.mojom b/chromium/components/webauth/authenticator.mojom
deleted file mode 100644
index 1bf69a9796d..00000000000
--- a/chromium/components/webauth/authenticator.mojom
+++ /dev/null
@@ -1,89 +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.
-
-[JavaPackage="org.chromium.webauth.mojom"]
-module webauth.mojom;
-
-// This file describes the communication between the WebAuthentication renderer
-// implementation and browser-side implementations to create scoped credentials
-// and use already-created credentials to get assertions.
-// See https://w3c.github.io/webauthn/.
-
-// The public key and attestation that is returned by an authenticator's
-// call to makeCredential.
-struct ScopedCredentialInfo {
- // A blob of data containing the JSON serialization of client data passed
- // to the authenticator.
- array<uint8> client_data;
- // A blob of data returned from the authenticator.
- array<uint8> attestation;
-};
-
-// Information about the relying party and the user account held by that
-// relying party. This information is used by the authenticator to create
-// or retrieve an appropriate scoped credential for this account.
-// These fields take arbitrary input.
-
-struct RelyingPartyAccount {
- // Friendly name of the Relying Party, e.g. "Acme Corporation"
- string relying_party_display_name;
- // Friendly name associated with the user account, e.g. "John P. Smith"
- string display_name;
- // Identifier for the account, corresponding to no more than one credential
- // per authenticator and Relying Party.
- string id;
- // Detailed name for the account, e.g. john.p.smith@example.com
- string name;
- // User image, if any.
- // Todo make this url.mojom.Url in a followup CL
- string image_url;
-};
-
-// Parameters that are used to generate an appropriate scoped credential.
-struct ScopedCredentialParameters {
- ScopedCredentialType type;
- // TODO(kpaulhamus): add AlgorithmIdentifier algorithm;
-};
-
-// Optional parameters that are used during makeCredential.
-struct ScopedCredentialOptions {
- //TODO(kpaulhamus): Make this mojo.common.mojom.TimeDelta in followup CL
- int32 timeout_seconds;
- string relying_party_id;
- array<ScopedCredentialDescriptor> exclude_list;
- // TODO(kpaulhamus): add Extensions
-};
-
-enum ScopedCredentialType {
- SCOPEDCRED,
-};
-
-// Describes the credentials that the relying party already knows about for
-// the given account. If any of these are known to the authenticator,
-// it should not create a new credential.
-struct ScopedCredentialDescriptor {
- ScopedCredentialType type;
- // Blob representing a credential key handle. Up to 255 bytes for
- // U2F authenticators.
- array<uint8> id;
- array<Transport> transports;
-};
-
-enum Transport {
- USB,
- NFC,
- BLE,
-};
-
-// Interface to direct authenticators to create or use a scoped credential.
-interface Authenticator {
- // Gets the credential info for a new credential created by an authenticator
- // for the given relying party and account.
- // |attestation_challenge| is a blob passed from the relying party server.
- MakeCredential(RelyingPartyAccount account_information,
- array<ScopedCredentialParameters> crypto_parameters,
- array<uint8> attestation_challenge,
- ScopedCredentialOptions? options)
- => (array<ScopedCredentialInfo> scoped_credentials);
-};
diff --git a/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc b/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc
index 86c67db9842..88204984a12 100644
--- a/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc
@@ -6,7 +6,10 @@
#include <stddef.h>
#include <stdint.h>
+#include <utility>
+
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
@@ -219,7 +222,7 @@ TEST_F(WebCryptoAesCbcTest, ImportKeyJwkEmptyKeyOps) {
dict.SetString("kty", "oct");
dict.SetBoolean("ext", false);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- dict.Set("key_ops", new base::ListValue); // Takes ownership.
+ dict.Set("key_ops", base::MakeUnique<base::ListValue>());
// The JWK does not contain encrypt usages.
EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
@@ -260,8 +263,8 @@ TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsEncryptDecrypt) {
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- base::ListValue* key_ops = new base::ListValue;
- dict.Set("key_ops", key_ops); // Takes ownership.
+ base::ListValue* key_ops =
+ dict.SetList("key_ops", base::MakeUnique<base::ListValue>());
key_ops->AppendString("encrypt");
@@ -298,10 +301,9 @@ TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsNotSuperset) {
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- base::ListValue* key_ops = new base::ListValue;
- dict.Set("key_ops", key_ops); // Takes ownership.
-
+ auto key_ops = base::MakeUnique<base::ListValue>();
key_ops->AppendString("encrypt");
+ dict.Set("key_ops", std::move(key_ops));
EXPECT_EQ(
Status::ErrorJwkKeyopsInconsistent(),
@@ -364,10 +366,9 @@ TEST_F(WebCryptoAesCbcTest, ImportJwkKeyOpsLacksUsages) {
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- base::ListValue* key_ops = new base::ListValue;
- // Note: the following call makes dict assume ownership of key_ops.
- dict.Set("key_ops", key_ops);
+ auto key_ops = base::MakeUnique<base::ListValue>();
key_ops->AppendString("foo");
+ dict.Set("key_ops", std::move(key_ops));
EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
diff --git a/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc b/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc
index 293510021f2..12dea24036a 100644
--- a/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc
@@ -6,6 +6,7 @@
#include <stdint.h>
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
@@ -58,8 +59,8 @@ TEST_F(WebCryptoAesKwTest, ImportKeyJwkKeyOpsWrapUnwrap) {
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- base::ListValue* key_ops = new base::ListValue;
- dict.Set("key_ops", key_ops); // Takes ownership.
+ base::ListValue* key_ops =
+ dict.SetList("key_ops", base::MakeUnique<base::ListValue>());
key_ops->AppendString("wrapKey");
diff --git a/chromium/components/webcrypto/algorithms/hmac_unittest.cc b/chromium/components/webcrypto/algorithms/hmac_unittest.cc
index fe5b331323e..a57cdd825be 100644
--- a/chromium/components/webcrypto/algorithms/hmac_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/hmac_unittest.cc
@@ -6,7 +6,10 @@
#include <stddef.h>
#include <stdint.h>
+#include <utility>
+
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
@@ -228,8 +231,8 @@ TEST_F(WebCryptoHmacTest, ImportKeyJwkKeyOpsSignVerify) {
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- base::ListValue* key_ops = new base::ListValue;
- dict.Set("key_ops", key_ops); // Takes ownership.
+ base::ListValue* key_ops =
+ dict.SetList("key_ops", base::MakeUnique<base::ListValue>());
key_ops->AppendString("sign");
@@ -258,14 +261,14 @@ TEST_F(WebCryptoHmacTest, ImportKeyJwkUseInconsisteWithKeyOps) {
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- base::ListValue* key_ops = new base::ListValue;
- dict.Set("key_ops", key_ops); // Takes ownership.
-
dict.SetString("alg", "HS256");
dict.SetString("use", "sig");
+
+ auto key_ops = base::MakeUnique<base::ListValue>();
key_ops->AppendString("sign");
key_ops->AppendString("verify");
key_ops->AppendString("encrypt");
+ dict.Set("key_ops", std::move(key_ops));
EXPECT_EQ(
Status::ErrorJwkUseAndKeyopsInconsistent(),
ImportKeyJwkFromDict(
diff --git a/chromium/components/webcrypto/jwk.cc b/chromium/components/webcrypto/jwk.cc
index 326ce2b8862..50f81bb75d8 100644
--- a/chromium/components/webcrypto/jwk.cc
+++ b/chromium/components/webcrypto/jwk.cc
@@ -353,7 +353,7 @@ JwkWriter::JwkWriter(const std::string& algorithm,
const std::string& kty) {
if (!algorithm.empty())
dict_.SetString("alg", algorithm);
- dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages).release());
+ dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages));
dict_.SetBoolean("ext", extractable);
dict_.SetString("kty", kty);
}
diff --git a/chromium/components/webdata/common/BUILD.gn b/chromium/components/webdata/common/BUILD.gn
index 1fe078139fd..921d4443f67 100644
--- a/chromium/components/webdata/common/BUILD.gn
+++ b/chromium/components/webdata/common/BUILD.gn
@@ -58,6 +58,9 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/web_database/version_69.sql",
"//components/test/data/web_database/version_70.sql",
"//components/test/data/web_database/version_71.sql",
+ "//components/test/data/web_database/version_72.sql",
+ "//components/test/data/web_database/version_73.sql",
+ "//components/test/data/web_database/version_73_with_type_column.sql",
]
outputs = [
"{{bundle_resources_dir}}/" +
diff --git a/chromium/components/webdata/common/web_data_request_manager.cc b/chromium/components/webdata/common/web_data_request_manager.cc
index c52cfd82c53..f48d7abdc29 100644
--- a/chromium/components/webdata/common/web_data_request_manager.cc
+++ b/chromium/components/webdata/common/web_data_request_manager.cc
@@ -11,6 +11,8 @@
#include "base/memory/ptr_util.h"
#include "base/profiler/scoped_tracker.h"
#include "base/stl_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
////////////////////////////////////////////////////////////////////////////////
@@ -36,11 +38,12 @@ bool WebDataRequest::IsActive() {
WebDataRequest::WebDataRequest(WebDataRequestManager* manager,
WebDataServiceConsumer* consumer,
WebDataServiceBase::Handle handle)
- : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ : task_runner_(base::SequencedTaskRunnerHandle::IsSet()
+ ? base::SequencedTaskRunnerHandle::Get()
+ : nullptr),
atomic_manager_(reinterpret_cast<base::subtle::AtomicWord>(manager)),
consumer_(consumer),
handle_(handle) {
- DCHECK(task_runner_);
DCHECK(IsActive());
static_assert(sizeof(atomic_manager_) == sizeof(manager), "size mismatch");
}
@@ -54,7 +57,7 @@ WebDataServiceConsumer* WebDataRequest::GetConsumer() {
return consumer_;
}
-scoped_refptr<base::SingleThreadTaskRunner> WebDataRequest::GetTaskRunner() {
+scoped_refptr<base::SequencedTaskRunner> WebDataRequest::GetTaskRunner() {
return task_runner_;
}
@@ -99,12 +102,17 @@ void WebDataRequestManager::CancelRequest(WebDataServiceBase::Handle h) {
void WebDataRequestManager::RequestCompleted(
std::unique_ptr<WebDataRequest> request,
std::unique_ptr<WDTypedResult> result) {
- scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ // Careful: Don't swap this below the BindOnce() call below, since that
+ // effectively does a std::move() on |request|!
+ scoped_refptr<base::SequencedTaskRunner> task_runner =
request->GetTaskRunner();
- task_runner->PostTask(
- FROM_HERE,
+ auto task =
base::BindOnce(&WebDataRequestManager::RequestCompletedOnThread, this,
- base::Passed(&request), base::Passed(&result)));
+ base::Passed(&request), base::Passed(&result));
+ if (task_runner)
+ task_runner->PostTask(FROM_HERE, std::move(task));
+ else
+ base::PostTask(FROM_HERE, std::move(task));
}
WebDataRequestManager::~WebDataRequestManager() {
diff --git a/chromium/components/webdata/common/web_data_request_manager.h b/chromium/components/webdata/common/web_data_request_manager.h
index eb621a2cff1..2b96076ddfe 100644
--- a/chromium/components/webdata/common/web_data_request_manager.h
+++ b/chromium/components/webdata/common/web_data_request_manager.h
@@ -15,7 +15,6 @@
#include "base/atomicops.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "components/webdata/common/web_data_results.h"
#include "components/webdata/common/web_data_service_base.h"
@@ -25,6 +24,10 @@
class WebDataServiceConsumer;
class WebDataRequestManager;
+namespace base {
+class SequencedTaskRunner;
+}
+
//////////////////////////////////////////////////////////////////////////////
//
// WebData requests
@@ -60,15 +63,16 @@ class WebDataRequest {
// Retrieves the |consumer_| set in the constructor.
WebDataServiceConsumer* GetConsumer();
- // Retrieves the original task runner of the request.
- scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner();
+ // Retrieves the original task runner of the request. This may be null if the
+ // original task was not posted as a sequenced task.
+ scoped_refptr<base::SequencedTaskRunner> GetTaskRunner();
// Marks the current request as inactive, either due to cancellation or
// completion.
void MarkAsInactive();
// Tracks task runner that the request originated on.
- const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ const scoped_refptr<base::SequencedTaskRunner> task_runner_;
// The manager associated with this request. This is stored as a raw (untyped)
// pointer value because it does double duty as the flag indicating whether or
diff --git a/chromium/components/webdata/common/web_database.cc b/chromium/components/webdata/common/web_database.cc
index 072f025e4d6..9e64be9aa02 100644
--- a/chromium/components/webdata/common/web_database.cc
+++ b/chromium/components/webdata/common/web_database.cc
@@ -13,7 +13,7 @@
// corresponding changes must happen in the unit tests, and new migration test
// added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
// static
-const int WebDatabase::kCurrentVersionNumber = 72;
+const int WebDatabase::kCurrentVersionNumber = 74;
const int WebDatabase::kDeprecatedVersionNumber = 51;
@@ -29,15 +29,15 @@ void ChangeVersion(sql::MetaTable* meta_table,
meta_table->SetVersionNumber(version_num);
if (update_compatible_version_num) {
meta_table->SetCompatibleVersionNumber(
- std::min(version_num, kCompatibleVersionNumber));
+ std::min(version_num, kCompatibleVersionNumber));
}
}
// Outputs the failed version number as a warning and always returns
// |sql::INIT_FAILURE|.
sql::InitStatus FailedMigrationTo(int version_num) {
- LOG(WARNING) << "Unable to update web database to version "
- << version_num << ".";
+ LOG(WARNING) << "Unable to update web database to version " << version_num
+ << ".";
NOTREACHED();
return sql::INIT_FAILURE;
}
@@ -46,8 +46,7 @@ sql::InitStatus FailedMigrationTo(int version_num) {
WebDatabase::WebDatabase() {}
-WebDatabase::~WebDatabase() {
-}
+WebDatabase::~WebDatabase() {}
void WebDatabase::AddTable(WebDatabaseTable* table) {
tables_[table->GetTypeKey()] = table;
@@ -150,8 +149,7 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded() {
DCHECK_GT(current_version, kDeprecatedVersionNumber);
for (int next_version = current_version + 1;
- next_version <= kCurrentVersionNumber;
- ++next_version) {
+ next_version <= kCurrentVersionNumber; ++next_version) {
// Do any database-wide migrations.
bool update_compatible_version = false;
if (!MigrateToVersion(next_version, &update_compatible_version))
@@ -175,7 +173,7 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded() {
}
bool WebDatabase::MigrateToVersion(int version,
- bool* update_compatible_version) {
+ bool* update_compatible_version) {
// Migrate if necessary.
switch (version) {
case 58:
@@ -188,10 +186,9 @@ bool WebDatabase::MigrateToVersion(int version,
bool WebDatabase::MigrateToVersion58DropWebAppsAndIntents() {
sql::Transaction transaction(&db_);
- return transaction.Begin() &&
- db_.Execute("DROP TABLE IF EXISTS web_apps") &&
- db_.Execute("DROP TABLE IF EXISTS web_app_icons") &&
- db_.Execute("DROP TABLE IF EXISTS web_intents") &&
- db_.Execute("DROP TABLE IF EXISTS web_intents_defaults") &&
- transaction.Commit();
+ return transaction.Begin() && db_.Execute("DROP TABLE IF EXISTS web_apps") &&
+ db_.Execute("DROP TABLE IF EXISTS web_app_icons") &&
+ db_.Execute("DROP TABLE IF EXISTS web_intents") &&
+ db_.Execute("DROP TABLE IF EXISTS web_intents_defaults") &&
+ transaction.Commit();
}
diff --git a/chromium/components/webdata/common/web_database_migration_unittest.cc b/chromium/components/webdata/common/web_database_migration_unittest.cc
index 4417725203b..a119b444df2 100644
--- a/chromium/components/webdata/common/web_database_migration_unittest.cc
+++ b/chromium/components/webdata/common/web_database_migration_unittest.cc
@@ -103,7 +103,7 @@ class WebDatabaseMigrationTest : public testing::Test {
source_path = source_path.AppendASCII("web_database");
source_path = source_path.Append(file);
return base::PathExists(source_path) &&
- base::ReadFileToString(source_path, contents);
+ base::ReadFileToString(source_path, contents);
}
static int VersionFromConnection(sql::Connection* connection) {
@@ -130,7 +130,7 @@ class WebDatabaseMigrationTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
};
-const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 72;
+const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 74;
void WebDatabaseMigrationTest::LoadDatabase(
const base::FilePath::StringType& file) {
@@ -207,8 +207,7 @@ TEST_F(WebDatabaseMigrationTest, MigrateEmptyToCurrent) {
// Versions below 52 are deprecated. This verifies that old databases are razed.
TEST_F(WebDatabaseMigrationTest, RazeDeprecatedVersionAndReinit) {
- ASSERT_NO_FATAL_FAILURE(
- LoadDatabase(FILE_PATH_LITERAL("version_50.sql")));
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_50.sql")));
// Verify pre-conditions. These are expectations for version 50 of the
// database.
@@ -221,14 +220,14 @@ TEST_F(WebDatabaseMigrationTest, RazeDeprecatedVersionAndReinit) {
ASSERT_TRUE(meta_table.Init(&connection, 50, 50));
ASSERT_FALSE(connection.DoesColumnExist("keywords", "image_url"));
- ASSERT_FALSE(connection.DoesColumnExist("keywords",
- "search_url_post_params"));
- ASSERT_FALSE(connection.DoesColumnExist("keywords",
- "suggest_url_post_params"));
- ASSERT_FALSE(connection.DoesColumnExist("keywords",
- "instant_url_post_params"));
- ASSERT_FALSE(connection.DoesColumnExist("keywords",
- "image_url_post_params"));
+ ASSERT_FALSE(
+ connection.DoesColumnExist("keywords", "search_url_post_params"));
+ ASSERT_FALSE(
+ connection.DoesColumnExist("keywords", "suggest_url_post_params"));
+ ASSERT_FALSE(
+ connection.DoesColumnExist("keywords", "instant_url_post_params"));
+ ASSERT_FALSE(
+ connection.DoesColumnExist("keywords", "image_url_post_params"));
}
DoMigration();
@@ -245,22 +244,21 @@ TEST_F(WebDatabaseMigrationTest, RazeDeprecatedVersionAndReinit) {
// New columns should have been created.
EXPECT_TRUE(connection.DoesColumnExist("keywords", "image_url"));
- EXPECT_TRUE(connection.DoesColumnExist("keywords",
- "search_url_post_params"));
- EXPECT_TRUE(connection.DoesColumnExist("keywords",
- "suggest_url_post_params"));
- EXPECT_TRUE(connection.DoesColumnExist("keywords",
- "instant_url_post_params"));
- EXPECT_TRUE(connection.DoesColumnExist("keywords",
- "image_url_post_params"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("keywords", "search_url_post_params"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("keywords", "suggest_url_post_params"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("keywords", "instant_url_post_params"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("keywords", "image_url_post_params"));
}
}
// Tests that the column |new_tab_url| is added to the keyword table schema for
// a version 52 database.
TEST_F(WebDatabaseMigrationTest, MigrateVersion52ToCurrent) {
- ASSERT_NO_FATAL_FAILURE(
- LoadDatabase(FILE_PATH_LITERAL("version_52.sql")));
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_52.sql")));
// Verify pre-conditions. These are expectations for version 52 of the
// database.
@@ -348,12 +346,11 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion53ToCurrent) {
EXPECT_FALSE(connection.DoesColumnExist("autofill_profile_phones", "type"));
// Data should have been preserved.
- sql::Statement s_profiles(
- connection.GetUniqueStatement(
- "SELECT guid, company_name, street_address, dependent_locality,"
- " city, state, zipcode, sorting_code, country_code, date_modified,"
- " origin "
- "FROM autofill_profiles"));
+ sql::Statement s_profiles(connection.GetUniqueStatement(
+ "SELECT guid, company_name, street_address, dependent_locality,"
+ " city, state, zipcode, sorting_code, country_code, date_modified,"
+ " origin "
+ "FROM autofill_profiles"));
// Address lines 1 and 2.
ASSERT_TRUE(s_profiles.Step());
@@ -426,9 +423,8 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion53ToCurrent) {
EXPECT_FALSE(s_profiles.Step());
// Verify the phone number data as well.
- sql::Statement s_phones(
- connection.GetUniqueStatement(
- "SELECT guid, number FROM autofill_profile_phones"));
+ sql::Statement s_phones(connection.GetUniqueStatement(
+ "SELECT guid, number FROM autofill_profile_phones"));
ASSERT_TRUE(s_phones.Step());
EXPECT_EQ("00000000-0000-0000-0000-000000000001", s_phones.ColumnString(0));
@@ -559,12 +555,11 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion54ToCurrent) {
// Data should have been preserved. Note that it appears out of order
// relative to the previous table, as it's been alphabetized. That's ok.
- sql::Statement s(
- connection.GetUniqueStatement(
- "SELECT name, value, value_lower, date_created, date_last_used,"
- " count "
- "FROM autofill "
- "ORDER BY name, value ASC"));
+ sql::Statement s(connection.GetUniqueStatement(
+ "SELECT name, value, value_lower, date_created, date_last_used,"
+ " count "
+ "FROM autofill "
+ "ORDER BY name, value ASC"));
// "jane.doe@example.org": Timestamps should be parsed correctly, and only
// the first and last should be kept.
@@ -644,19 +639,17 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion55ToCurrent) {
// Data should have been preserved. Language code should have been set to
// empty string.
- sql::Statement s_profiles(
- connection.GetUniqueStatement(
- "SELECT guid, company_name, street_address, dependent_locality,"
- " city, state, zipcode, sorting_code, country_code, date_modified,"
- " origin, language_code "
- "FROM autofill_profiles"));
+ sql::Statement s_profiles(connection.GetUniqueStatement(
+ "SELECT guid, company_name, street_address, dependent_locality,"
+ " city, state, zipcode, sorting_code, country_code, date_modified,"
+ " origin, language_code "
+ "FROM autofill_profiles"));
ASSERT_TRUE(s_profiles.Step());
EXPECT_EQ("00000000-0000-0000-0000-000000000001",
s_profiles.ColumnString(0));
EXPECT_EQ(ASCIIToUTF16("Google Inc"), s_profiles.ColumnString16(1));
- EXPECT_EQ(ASCIIToUTF16("340 Main St"),
- s_profiles.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("340 Main St"), s_profiles.ColumnString16(2));
EXPECT_EQ(base::string16(), s_profiles.ColumnString16(3));
EXPECT_EQ(ASCIIToUTF16("Los Angeles"), s_profiles.ColumnString16(4));
EXPECT_EQ(ASCIIToUTF16("CA"), s_profiles.ColumnString16(5));
@@ -689,10 +682,9 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion56ToCurrent) {
connection.DoesColumnExist("autofill_profile_names", "full_name"));
// Verify the starting data.
- sql::Statement s_names(
- connection.GetUniqueStatement(
- "SELECT guid, first_name, middle_name, last_name "
- "FROM autofill_profile_names"));
+ sql::Statement s_names(connection.GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name "
+ "FROM autofill_profile_names"));
ASSERT_TRUE(s_names.Step());
EXPECT_EQ("B41FE6E0-B13E-2A2A-BF0B-29FCE2C3ADBD", s_names.ColumnString(0));
EXPECT_EQ(ASCIIToUTF16("Jon"), s_names.ColumnString16(1));
@@ -719,10 +711,9 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion56ToCurrent) {
// Data should have been preserved. Full name should have been set to the
// empty string.
- sql::Statement s_names(
- connection.GetUniqueStatement(
- "SELECT guid, first_name, middle_name, last_name, full_name "
- "FROM autofill_profile_names"));
+ sql::Statement s_names(connection.GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name, full_name "
+ "FROM autofill_profile_names"));
ASSERT_TRUE(s_names.Step());
EXPECT_EQ("B41FE6E0-B13E-2A2A-BF0B-29FCE2C3ADBD", s_names.ColumnString(0));
@@ -778,7 +769,8 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion57ToCurrent) {
TEST_F(WebDatabaseMigrationTest, MigrateVersion58ToCurrent) {
ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_58.sql")));
- const char query_extensions[] = "SELECT * FROM keywords "
+ const char query_extensions[] =
+ "SELECT * FROM keywords "
"WHERE url='chrome-extension://iphchnegaodmijmkdlbhbanjhfphhikp/"
"?q={searchTerms}'";
// Verify pre-conditions.
@@ -818,8 +810,9 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion58ToCurrent) {
}
EXPECT_EQ(0, count);
- s.Assign(connection.GetUniqueStatement("SELECT * FROM keywords "
- "WHERE short_name='Google'"));
+ s.Assign(
+ connection.GetUniqueStatement("SELECT * FROM keywords "
+ "WHERE short_name='Google'"));
ASSERT_TRUE(s.is_valid());
count = 0;
while (s.Step()) {
@@ -915,10 +908,10 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion61ToCurrent) {
sql::MetaTable meta_table;
ASSERT_TRUE(meta_table.Init(&connection, 61, 61));
- EXPECT_FALSE(connection.DoesColumnExist("unmasked_credit_cards",
- "use_count"));
- EXPECT_FALSE(connection.DoesColumnExist("unmasked_credit_cards",
- "use_date"));
+ EXPECT_FALSE(
+ connection.DoesColumnExist("unmasked_credit_cards", "use_count"));
+ EXPECT_FALSE(
+ connection.DoesColumnExist("unmasked_credit_cards", "use_date"));
}
DoMigration();
@@ -932,10 +925,10 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion61ToCurrent) {
// Check version.
EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
- EXPECT_TRUE(connection.DoesColumnExist("unmasked_credit_cards",
- "use_count"));
- EXPECT_TRUE(connection.DoesColumnExist("unmasked_credit_cards",
- "use_date"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("unmasked_credit_cards", "use_count"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("unmasked_credit_cards", "use_date"));
}
}
@@ -956,10 +949,9 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion64ToCurrent) {
EXPECT_FALSE(connection.DoesTableExist("server_address_metadata"));
// Add a server address --- make sure it gets an ID.
- sql::Statement insert_profiles(
- connection.GetUniqueStatement(
- "INSERT INTO server_addresses(id, postal_code) "
- "VALUES ('', 90210)"));
+ sql::Statement insert_profiles(connection.GetUniqueStatement(
+ "INSERT INTO server_addresses(id, postal_code) "
+ "VALUES ('', 90210)"));
insert_profiles.Run();
}
@@ -977,9 +969,8 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion64ToCurrent) {
EXPECT_TRUE(connection.DoesTableExist("server_card_metadata"));
EXPECT_TRUE(connection.DoesTableExist("server_address_metadata"));
- sql::Statement read_profiles(
- connection.GetUniqueStatement(
- "SELECT id, postal_code FROM server_addresses"));
+ sql::Statement read_profiles(connection.GetUniqueStatement(
+ "SELECT id, postal_code FROM server_addresses"));
ASSERT_TRUE(read_profiles.Step());
EXPECT_FALSE(read_profiles.ColumnString(0).empty());
EXPECT_EQ("90210", read_profiles.ColumnString(1));
@@ -999,8 +990,8 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion65ToCurrent) {
sql::MetaTable meta_table;
ASSERT_TRUE(meta_table.Init(&connection, 65, 65));
- EXPECT_FALSE(connection.DoesColumnExist("credit_cards",
- "billing_address_id"));
+ EXPECT_FALSE(
+ connection.DoesColumnExist("credit_cards", "billing_address_id"));
EXPECT_TRUE(connection.Execute(
"INSERT INTO credit_cards(guid, name_on_card) VALUES ('', 'Alice')"));
@@ -1017,8 +1008,8 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion65ToCurrent) {
// Check version.
EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
- EXPECT_TRUE(connection.DoesColumnExist("credit_cards",
- "billing_address_id"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("credit_cards", "billing_address_id"));
sql::Statement read_credit_cards(connection.GetUniqueStatement(
"SELECT name_on_card, billing_address_id FROM credit_cards"));
@@ -1045,9 +1036,9 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion66ToCurrent) {
EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
"billing_address_id"));
- EXPECT_TRUE(connection.Execute(
- "INSERT INTO masked_credit_cards(id, name_on_card) "
- "VALUES ('id', 'Alice')"));
+ EXPECT_TRUE(
+ connection.Execute("INSERT INTO masked_credit_cards(id, name_on_card) "
+ "VALUES ('id', 'Alice')"));
}
DoMigration();
@@ -1128,8 +1119,7 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion68ToCurrent) {
// Check version.
EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
- EXPECT_TRUE(
- connection.DoesColumnExist("keywords", "last_visited"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "last_visited"));
}
}
@@ -1277,7 +1267,8 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion71ToCurrent) {
// Check version.
EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
- EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards", "type"));
+ // Don't check for absence of "type", because that's added in version 73
+ // with a different meaning.
EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "network"));
sql::Statement s_cards_metadata(connection.GetUniqueStatement(
@@ -1287,3 +1278,133 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion71ToCurrent) {
EXPECT_EQ("VISA", s_cards_metadata.ColumnString(1));
}
}
+
+// Tests addition of bank_name to masked_credit_cards
+TEST_F(WebDatabaseMigrationTest, MigrateVersion72ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_72.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, 72, 72));
+
+ EXPECT_FALSE(
+ connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+ }
+
+ 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));
+
+ // The bank_name column should exist.
+ EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+
+ // Make sure that the default bank name value is empty.
+ sql::Statement s_masked_cards(connection.GetUniqueStatement(
+ "SELECT bank_name FROM masked_credit_cards"));
+ ASSERT_TRUE(s_masked_cards.Step());
+ EXPECT_EQ("", s_masked_cards.ColumnString(0));
+ }
+}
+
+// Tests adding "type" column for the "masked_credit_cards" table.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion73ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_73.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, 73, 73));
+
+ EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards", "type"));
+
+ EXPECT_TRUE(
+ connection.Execute("INSERT INTO masked_credit_cards(id, network) "
+ "VALUES ('id', 'VISA')"));
+ }
+
+ 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));
+
+ EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "type"));
+
+ sql::Statement cards(connection.GetUniqueStatement(
+ "SELECT id, network, type FROM masked_credit_cards"));
+ ASSERT_TRUE(cards.Step());
+ EXPECT_EQ("id", cards.ColumnString(0));
+ EXPECT_EQ("VISA", cards.ColumnString(1));
+ EXPECT_EQ(CreditCard::CARD_TYPE_UNKNOWN, cards.ColumnInt(2));
+ }
+}
+
+// Tests that version 73 with "type" column instead of "bank_name" column can be
+// migrated to version 74 with both of these columns. This is necessary to
+// verify that the version 73 collision resolves itself.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion73WithTypeColumnToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_73_with_type_column.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, 73, 73));
+
+ EXPECT_FALSE(
+ connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "type"));
+
+ EXPECT_TRUE(connection.Execute(
+ "INSERT INTO masked_credit_cards (type) VALUES (2)"));
+ }
+
+ 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));
+
+ // The bank_name column should exist.
+ EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+
+ // The type column should exist.
+ EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "type"));
+
+ // Make sure that the existing value of the type column is preserved.
+ sql::Statement s_masked_cards(
+ connection.GetUniqueStatement("SELECT type FROM masked_credit_cards"));
+ ASSERT_TRUE(s_masked_cards.Step());
+ EXPECT_EQ(2, s_masked_cards.ColumnInt(0));
+ }
+}
diff --git a/chromium/components/wifi/network_properties.cc b/chromium/components/wifi/network_properties.cc
index e8991259e3a..9d7a23181e3 100644
--- a/chromium/components/wifi/network_properties.cc
+++ b/chromium/components/wifi/network_properties.cc
@@ -4,6 +4,8 @@
#include "components/wifi/network_properties.h"
+#include <utility>
+
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
@@ -52,14 +54,14 @@ std::unique_ptr<base::DictionaryValue> NetworkProperties::ToValue(
frequency_list->AppendInteger(*it);
}
if (!frequency_list->empty())
- wifi->Set(onc::wifi::kFrequencyList, frequency_list.release());
+ wifi->Set(onc::wifi::kFrequencyList, std::move(frequency_list));
if (!bssid.empty())
wifi->SetString(onc::wifi::kBSSID, bssid);
wifi->SetString(onc::wifi::kSSID, ssid);
wifi->SetString(onc::wifi::kHexSSID,
base::HexEncode(ssid.c_str(), ssid.size()));
}
- value->Set(onc::network_type::kWiFi, wifi.release());
+ value->Set(onc::network_type::kWiFi, std::move(wifi));
return value;
}
diff --git a/chromium/components/zoom/OWNERS b/chromium/components/zoom/OWNERS
index 2cd925c2d95..fd016d73c0a 100644
--- a/chromium/components/zoom/OWNERS
+++ b/chromium/components/zoom/OWNERS
@@ -1,4 +1,3 @@
-dbeam@chromium.org
wjmaclean@chromium.org
# COMPONENT: UI>Browser>Zoom